home *** CD-ROM | disk | FTP | other *** search
/ Sound Sensations! / sound_sensations.iso / miscprog / play / play.asm next >
Assembly Source File  |  1987-04-01  |  22KB  |  466 lines

  1. ;               Play.asm
  2. ;      Emulates Basic's Play commands
  3. ; Syntax  PLAY [string[/K] | filespec/F[/K]]
  4. ;      /K denotes no keyboard polling
  5. ;
  6. CODE SEGMENT                           ;*************************
  7. ASSUME CS:CODE,DS:CODE                 ;*                       *
  8. ORG 100H                               ;*  REMEMBER TO EXE2BIN  *
  9.                                        ;*                       *
  10. START:         JMP    BEGINNING        ;*************************
  11.  
  12. COPYRIGHT      DB     'Copyright l987 Ziff-Davis Publishing Co.',1AH
  13. PROGRAMMER     DB     'Michael J. Mefford'
  14.  
  15. TIMER_TIC      DD     ?
  16.  
  17. OCTAVE         DB     4
  18. LEN            DW     64
  19. TEMPO          DW     136
  20. MUSIC          DB     1
  21. COUNT          DW     0
  22.  
  23. NOTE:
  24. ;      C     C#    D     D#    E     F
  25. DW   16744,17736,18792,19912,21096,22352
  26. ;      F#    G     G#    A     A#    B
  27. DW   23680,25088,26576,28160,29712,31616
  28.  
  29. FILE           DB     'PLAY.DAT',0
  30. PARA_FLAG      DB     0
  31. FILE_FLAG      DB     0
  32. POLL_KEY       DB     0
  33.  
  34. ;----------------------------------------------------------;
  35. ; Speed the clock up to four times normal speed.           ;
  36. ; Capitalize command line and check for switch characters. ;
  37. ;----------------------------------------------------------;
  38.  
  39. BEGINNING:     MOV    BX,16384               ;Divisor for 72.8/sec IRQ 0.
  40.                CALL   SET_CLOCK
  41.  
  42.                MOV    AX,3508H               ;Get INT 8 (timer tic).
  43.                INT    21H
  44.                MOV    WORD PTR TIMER_TIC,BX       ;Save old vector.
  45.                MOV    WORD PTR TIMER_TIC[2],ES
  46.                PUSH   DS
  47.                POP    ES                     ;Restore ES.
  48.  
  49.                MOV    DX,OFFSET INT_8        ;Point INT 8 to our routine.
  50.                MOV    AX,2508H
  51.                INT    21H
  52.  
  53.                CMP    BYTE PTR DS:[80H],0    ;Are there any parameters?
  54.                JZ     PARAMETERS             ;If no, open PLAY.DAT.
  55.  
  56. CAPITALIZE:    MOV    SI,81H                 ;Point to parameters.
  57. NEXT_CAP:      LODSB                         ;Get a byte.
  58.                CMP    AL,13                  ;Is it carriage return?
  59.                JZ     PARAMETERS             ;If yes, done here.
  60.                CMP    AL,'/'                 ;Is it switch character?
  61.                JZ     SWITCHES               ;If yes, check which one.
  62.                CMP    AL,'0'                 ;Is it a possible command?
  63.                JB     NEXT_CAP               ;If no, get next byte.
  64.                MOV    PARA_FLAG,1            ;Else flag that parameter exists.
  65.                CMP    AL,'a'                 ;Is it lower case?
  66.                JB     NEXT_CAP               ;If no, get next byte.
  67.                AND    BYTE PTR [SI-1],5FH    ;Else, capitalize.
  68.                JMP    SHORT NEXT_CAP         ;Next byte.
  69.  
  70. SWITCHES:      CMP    AL,'/'                 ;Is it switch character?
  71.                JNZ    GET_SWITCH             ;If no, get next byte.
  72.                MOV    BYTE PTR [SI-1],0      ;Else create ASCIIZ for DOS.
  73. GET_SWITCH:    LODSB                         ;Get a byte.
  74.                CMP    AL,13                  ;Is it carriage return?
  75.                JZ     PARAMETERS             ;If yes, done here.
  76.                AND    AL,5FH                 ;Else, capitalize.
  77.                CMP    AL,'K'                 ;Is it /K switch?
  78.                JNZ    CK_FILE                ;If no, check /F.
  79.                MOV    POLL_KEY,1             ;Else, flag as no keyboard poll.
  80. CK_FILE:       CMP    AL,'F'                 ;Is it /F?
  81.                JNZ    SWITCHES               ;If no, get next byte.
  82.                MOV    FILE_FLAG,1            ;Else, flag as filespec.
  83.                JMP    SHORT SWITCHES         ;Get next byte.
  84.  
  85. ;-----------------------------------------------------------------------;
  86. ; Exit is here so it can be reached by as many short jumps as possible. ;
  87. ; Before terminating, reset clock to 18.2/sec and restore INT 8 vector. ;
  88. ;-----------------------------------------------------------------------;
  89.  
  90. EXIT:          MOV    BX,0                   ;Divisor of 65536 or zero.
  91.                CALL   SET_CLOCK
  92.  
  93.                MOV    DX,WORD PTR TIMER_TIC       ;Restore old vector
  94.                MOV    AX,WORD PTR TIMER_TIC[2]
  95.                MOV    DS,AX
  96.                MOV    AX,2508H                    ; of INT 8.
  97.                INT    21H
  98.  
  99.                INT    20H                    ;Terminate.
  100.  
  101. ;-----------------------------------------------------------------;
  102. ; Check to see if parameters exist and if yes check if filespec.  ;
  103. ; If yes, read file into buffer and process by stripping Wordstar ;
  104. ; high bit, capitalizing, stripping comments and stripping space  ;
  105. ; characters and below.  Finish by tacking on a carriage return.  ;
  106. ;-----------------------------------------------------------------;
  107.  
  108. PARAMETERS:    CMP    PARA_FLAG,1            ;Are there parameter?
  109.                JZ     FILE_NAME              ;If yes, check if filespec.
  110.                MOV    DX,OFFSET FILE         ;Else, point to PLAY.DAT
  111.                JMP    SHORT OPEN_FILE        ; and open file.
  112.  
  113. FILE_NAME:     CMP    FILE_FLAG,1            ;Is it a filespec?
  114.                JNZ    READ_COMMAND           ;If no, read the command line.
  115.                MOV    DX,82H                 ;Else, point to filespec.
  116.  
  117. OPEN_FILE:     MOV    AX,3D00H               ;Open file for reading.
  118.                INT    21H
  119.                JC     EXIT                   ;If not found, exit.
  120.  
  121.                MOV    BX,AX                  ;Else, filehandle in BX.
  122.                MOV    DX,OFFSET BUFFER       ;Read file into buffer.
  123.                MOV    CX,0F000H
  124.                MOV    AH,3FH
  125.                INT    21H
  126.  
  127.                MOV    CX,AX                  ;File length in CX.
  128.                MOV    SI,OFFSET BUFFER       ;Initialized SI and DI
  129.                MOV    DI,OFFSET BUFFER       ; to head of buffer.
  130.  
  131. FORMAT:        LODSB                         ;Get a byte.
  132.                AND    AL,7FH                 ;Strip Wordstar high bit.
  133.                CMP    AL,':'                 ;Is it comment character?
  134.                JZ     STRIP                  ;If yes, strip comment.
  135.                CMP    AL,'a'                 ;Is it lower case?
  136.                JB     CK_CONTROL             ;If no, check space and below.
  137.                AND    AL,5FH                 ;Else, capitalize.
  138. CK_CONTROL:    CMP    AL,32                  ;Is it space or below?
  139.                JBE    NEXT_FORMAT            ;If yes, don't store.
  140.                STOSB                         ;Else store the character.
  141. NEXT_FORMAT:   LOOP   FORMAT                 ;Get next byte.
  142. END_FORMAT:    MOV    BYTE PTR [DI],13       ;Tack on carriage return as EOF.
  143.                MOV    SI,OFFSET BUFFER       ;Point to commands.
  144.                JMP    SHORT NEXT_COMMAND     ;Get the commands.
  145.  
  146. NEXT_STRIP:    LODSB                         ;Get a byte.
  147.                AND    AL,7FH                 ;Strip Wordstar high bit.
  148.                CMP    AL,10                  ;Is it linefeed?
  149.                JZ     NEXT_FORMAT            ;If yes get next command.
  150. STRIP:         LOOP   NEXT_STRIP             ;Else, skip and get next byte.
  151.                JMP    SHORT END_FORMAT
  152.  
  153. ;--------------------------------;
  154. ; This is the command processor. ;
  155. ;--------------------------------;
  156.  
  157. READ_COMMAND:  MOV    SI,82H                 ;Initialize pointer to commands.
  158.  
  159. NEXT_COMMAND:  CMP    POLL_KEY,1             ;Should we poll keyboard?
  160.                JZ     GET_COMMAND            ;If no, skip.
  161.                MOV    AH,1                   ;Else, check for keystroke
  162.                INT    16H                    ; via BIOS.
  163.                JNZ    EXIT                   ;If keystroke, exit.
  164. GET_COMMAND:   LODSB                         ;Else, get a byte.
  165.                CMP    AL,13                  ;Is it carriage return?
  166.                JA     CONTINUE               ;If no, continue.
  167.                JMP    EXIT                   ;Else, we are done.
  168. CONTINUE:      CMP    AL,32                  ;Is it space, comma or semicolon?
  169.                JZ     NEXT_COMMAND           ;If yes, skip.
  170.                CMP    AL,','
  171.                JZ     NEXT_COMMAND
  172.                CMP    AL,';'
  173.                JZ     NEXT_COMMAND
  174.  
  175.                CMP    AL,'O'                 ;Is it "O" ; Octave?
  176.                JNZ    CK_L                   ;If no, check "L".
  177.                CALL   CK_NUMBER              ;Else, get number.
  178.                CMP    AL,7                   ;Is it in range?
  179.                JA     NEXT_COMMAND           ;If no, skip.
  180.                MOV    OCTAVE,AL              ;Else store.
  181.                JMP    SHORT NEXT_COMMAND
  182.  
  183. CK_L:          CMP    AL,'L'                 ;Is it "L" ; Length?
  184.                JNZ    CK_TEMPO               ;If no, check tempo.
  185.                CALL   CK_NUMBER              ;Else, get number.
  186.                CMP    AL,0                   ;Is it between 1 and 64?
  187.                JZ     NEXT_COMMAND           ;If no, skip.
  188.                CMP    AL,64
  189.                JA     NEXT_COMMAND
  190.                XOR    DX,DX                  ;Else, zero in high half.
  191.                MOV    AX,256                 ;Dividend of 256.
  192.                DIV    BX                     ;Divide by length
  193.                MOV    LEN,AX              ; and store.
  194.                JMP    SHORT NEXT_COMMAND
  195.  
  196. CK_TEMPO:      CMP    AL,'T'                 ;Is it "T" ; tempo?
  197.                JNZ    CK_N                   ;If no, check number.
  198.                CALL   CK_NUMBER              ;Else, get number.
  199.                CMP    AL,32                  ;Is it between 32 and 255?
  200.                JB     NEXT_COMMAND           ;If no, skip.
  201.                MOV    AX,255
  202.                SUB    AL,BL                  ;Else, complement.
  203.                INC    AX                     ;Make non zero
  204.                MOV    TEMPO,AX               ; and store.
  205.                JMP    SHORT NEXT_COMMAND
  206.  
  207. CK_N:          CMP    AL,'N'                 ;Is it "N" ; number?
  208.                JNZ    CK_NOTE                ;If no, check note.
  209.                CALL   CK_NUMBER              ;Else, get number.
  210.                CMP    AL,84                  ;Is it between 0 and 84?
  211.                JA     NEXT_COMMAND           ;If no, skip.
  212.                CMP    AL,0
  213.                JZ     GOT_N
  214.                DEC    AL                     ;Adjust number.
  215. GOT_N:         XOR    AH,AH                  ;Zero in high half.
  216.                MOV    CL,12                  ;Divide by 12.
  217.                DIV    CL
  218.                MOV    CX,8                   ;Get octave.
  219.                SUB    CL,AL
  220.                MOV    BL,AH                  ;Remainder in BX pointer.
  221.                XOR    BH,BH
  222.                SHL    BX,1                   ;Adjusts as word pointer.
  223.                MOV    BX,[OFFSET NOTE+BX]    ;Retrieve note.
  224.  
  225.                CALL   PLAY_NUMBER            ;Play the note.
  226.                JMP    NEXT_COMMAND
  227.  
  228. CK_NOTE:       CMP    AL,'A'                 ;Is it letter note ; A-G?
  229.                JB     COMMAND_END
  230.                CMP    AL,'G'
  231.                JA     CK_P                   ;If no, check pause.
  232.                CMP    AL,'C'
  233.                JB     ABOVE_C
  234.                SUB    AL,7                   ;First subtract 7 for C and above.
  235. ABOVE_C:       SUB    AL,60                  ;Finish pointer adjustment.
  236.                XOR    AH,AH                  ;Zero in high half.
  237.                MOV    DI,AX                  ;Move into pointer.
  238.                MOV    CL,2                   ;Convert to word pointer
  239.                SHL    DI,CL                  ; by multiplying by 2.
  240.                CMP    DI,8                   ;Adjust if necessary.
  241.                JBE    GOT_NOTE
  242.                SUB    DI,2
  243.  
  244. GOT_NOTE:      CALL   BLACK_KEY              ;Check for sharps or flats.
  245.                CALL   PLAY                   ;Play the note.
  246.                JMP    NEXT_COMMAND
  247.  
  248. CK_P:          CMP    AL,'P'                 ;Is it "P" ; pause?
  249.                JNZ    CK_K                   ;If no, check key poll.
  250.                CALL   CK_NUMBER              ;Else, get number.
  251.                CMP    AL,0                   ;Is it between 1 and 64?
  252.                JZ     COMMAND_END            ;If no, skip.
  253.                CMP    AL,64
  254.                JA     COMMAND_END
  255.                XOR    DX,DX                  ;Else, zero in high half.
  256.                MOV    AX,256                 ;Dividend of 256.
  257.                DIV    BX                     ;Divide by length
  258.                MOV    BP,AX                  ;Pause in BP.
  259.                CALL   PAUSE_DELAY
  260.                JMP    NEXT_COMMAND
  261.  
  262. CK_K:          CMP    AL,'K'                 ;Is it "K" ; non keyboard poll?
  263.                JNZ    CK_M                   ;If no, check music.
  264.                MOV    POLL_KEY,1             ;Else, flag as no poll.
  265.                JMP    NEXT_COMMAND
  266.  
  267. CK_M:          CMP    AL,'M'                 ;Is it "M" ; music?
  268.                JNZ    COMMAND_END            ;If no, next command.
  269.                LODSB                         ;Else, get next byte.
  270.                CMP    AL,13
  271.                JNZ    CK_N2
  272.                JMP    EXIT
  273. CK_N2:         CMP    AL,'N'                 ;Is it "N" ; normal?
  274.                JNZ    CK_L2                  ;If no, check L.
  275.                MOV    MUSIC,1                ;Else, flag as music one.
  276. CK_L2:         CMP    AL,'L'                 ;Is it "L" ; legato?
  277.                JNZ    CK_S2                  ;If no, check S.
  278.                MOV    MUSIC,2                ;Else, flag as music two.
  279. CK_S2:         CMP    AL,'S'                 ;Is it "S" ; staccato?
  280.                JNZ    COMMAND_END            ;If no, skip.
  281.                MOV    MUSIC,3                ;Else, flag as music three.
  282. COMMAND_END:   JMP    NEXT_COMMAND
  283.  
  284.                ;*************;
  285.                ; Subroutines ;
  286.                ;*************;
  287.  
  288. ;---------------------------------------------------------;
  289. ; This subroutine converts decimal command number to hex. ;
  290. ;---------------------------------------------------------;
  291.  
  292. CK_NUMBER:     XOR    BX,BX                  ;Initialize to zero.
  293. NEXT_NUMBER:   CMP    BYTE PTR [SI],'0'      ;Is it a number?
  294.                JB     END_NUMBER             ;If no, we're done.
  295.                CMP    BYTE PTR [SI],'9'
  296.                JA     END_NUMBER
  297.                LODSB                         ;Get number.
  298.                SUB    AL,30H                 ;Convert to hex.
  299.                MOV    DL,AL
  300.                MOV    AX,10                  ;Shift decimal by ten.
  301.                MUL    BL
  302.                MOV    BL,AL                  ;Result in BL.
  303.                ADD    BL,DL                  ;Add new number.
  304.                JMP    SHORT NEXT_NUMBER      ;Get next number.
  305.  
  306. END_NUMBER:    MOV    AL,BL                  ;Return with number in AL.
  307.                XOR    AH,AH                  ;Zero in high half.
  308.                RET
  309.  
  310. ;---------------------------------------------;
  311. ; This subroutine checks for sharps or flats. ;
  312. ;---------------------------------------------;
  313.  
  314. BLACK_KEY:     CMP    BYTE PTR [SI],'+'      ;Is it sharp?
  315.                JNZ    CK_SHARP               ;If no, check #.
  316.                ADD    DI,2                   ;Else, point to next note.
  317.                INC    SI                     ;Adjust command pointer.
  318. CK_SHARP:      CMP    BYTE PTR [SI],'#'      ;Do same for #.
  319.                JNZ    CK_FLAT
  320.                ADD    DI,2
  321.                INC    SI
  322. CK_FLAT:       CMP    BYTE PTR [SI],'-'      ;Do same for flat except
  323.                JNZ    END_BLACK              ; decrement note pointer.
  324.                SUB    DI,2
  325.                INC    SI
  326. END_BLACK:     RET
  327.  
  328. ;---------------------------------;
  329. ; This subroutine plays the note. ;
  330. ;---------------------------------;
  331.  
  332. PLAY:          MOV    BX,[OFFSET NOTE+DI]    ;Retrieve the note.
  333.                MOV    CX,8                   ;Complement the octave.
  334.                SUB    CL,OCTAVE
  335. PLAY_NUMBER:   SHR    BX,CL                  ;Divide by octave base two.
  336.                MOV    DX,12H                 ;120000h dividend constant.
  337.                XOR    AX,AX
  338.                DIV    BX                     ;Divide by frequency.
  339.                MOV    BX,AX                  ;Store in BX.
  340.                MOV    AL,0B6H                ;Latch to Channel 2.
  341.                OUT    43H,AL
  342.                MOV    AX,BX                  ;Send LSB then MSB to 8253.
  343.                OUT    42H,AL
  344.                MOV    AL,AH
  345.                OUT    42H,AL
  346.                IN     AL,61H
  347.                OR     AL,3                   ;Turn bits 0 and 1 on 8255 chip
  348.                OUT    61H,AL                 ; to turn speaker on.
  349.  
  350.                CALL   DELAY                  ;Delay.
  351.  
  352. OFF:           IN     AL,61H                 ;Turn bits 0 and 1 back off.
  353.                AND    AL,11111100B
  354.                OUT    61H,AL
  355.  
  356.                MOV    CX,BX                  ;Delay for pause between notes.
  357.                CALL   STACCATO
  358.  
  359.                RET
  360.  
  361. ;------------------------------------------------;
  362. ; This subroutine calculates the length of play. ;
  363. ;------------------------------------------------;
  364.  
  365. DELAY:         MOV    BP,LEN                 ;Retrieve length.
  366. PAUSE_DELAY:   MOV    CX,BP                  ;Store in CX.
  367. NEXT_DOT:      CMP    BYTE PTR [SI],'.'      ;Is it a dotted note?
  368.                JNZ    GOT_DELAY              ;If no, got length.
  369.                INC    SI                     ;Else, adjust command pointer.
  370.                SHR    CX,1                   ;Divide length by two and
  371.                ADD    BP,CX                  ; add to total length.
  372.                JMP    SHORT NEXT_DOT         ;Check for another dot.
  373.  
  374. GOT_DELAY:     MOV    CX,BP                  ;Length in CX.
  375.                PUSH   CX                     ;Save.
  376.                CMP    MUSIC,2                ;Is it music legato?
  377.                JZ     WAIT                   ;If yes, play full length.
  378.  
  379.                MOV    AX,CX                  ;Else, length in AX.
  380.                XOR    DX,DX                  ;Zero in high half.
  381.                CMP    MUSIC,1                ;Is it music normal?
  382.                JNZ    MUSIC_3                ;If no, must be music stacatto.
  383.                MOV    CX,7                   ;Multiply by 7/8.
  384.                MUL    CX
  385.                MOV    CL,3
  386.                SHR    AX,CL
  387.                MOV    CX,AX
  388.                JMP    SHORT WAIT
  389.  
  390. MUSIC_3:       MOV    CX,3                   ;Multiply by 3/4.
  391.                MUL    CX
  392.                MOV    CL,2
  393.                SHR    AX,CL
  394.                MOV    CX,AX
  395.  
  396. WAIT:          POP    BX                     ;Recover total length.
  397.                SUB    BX,CX                  ;Subtract pause time.
  398.  
  399. STACCATO:      MOV    AX,TEMPO               ;Retrieve tempo.
  400.                MUL    CL                     ;Multiply by length of note.
  401.                XOR    DX,DX
  402.                MOV    CX,200                 ;Adjust by looping constant.
  403.                DIV    CX
  404.                INC    AX                     ;Prevent zero loop.
  405.  
  406.                MOV    CX,COUNT               ;Get present counter.
  407.                ADD    CX,AX                  ;Add length of note.
  408.                JB     END_DELAY              ;Skip if count wrapped.
  409. NEXT_DELAY:    MOV    AX,COUNT               ;Get counter again.
  410.                CMP    AX,CX                  ;Has delay expired?
  411.                JB     NEXT_DELAY             ;If no, get counter until it does.
  412. END_DELAY:     RET
  413.  
  414. ;------------------------------------------------------;
  415. ; This subroutine programs Channel 0 of the 8253 chip. ;
  416. ;------------------------------------------------------;
  417.  
  418. SET_CLOCK:     MOV    AL,00110110B           ;Latch to Channel 0.
  419.                OUT    43H,AL                 ;Send LSB then MSB.
  420.                MOV    AL,BL
  421.                OUT    40H,AL
  422.                MOV    AL,BH
  423.                OUT    40H,AL
  424.                RET
  425.  
  426. ;------------------------------------;
  427. ; This is the new INT 8 interceptor. ;
  428. ;------------------------------------;
  429.  
  430. ASSUME DS:NOTHING
  431.  
  432. INT_8:         STI                           ;Interrupts back on.
  433.                PUSHF                         ;Save registers.
  434.                PUSH   AX
  435.                PUSH   CX
  436.                PUSH   DX
  437.                INC    COUNT                  ;Increment our counter.
  438.                MOV    AX,COUNT               ;Check if divisible by 4.
  439.                XOR    DX,DX
  440.                MOV    CX,4
  441.                DIV    CX
  442.                CMP    DX,0
  443.                JNZ    TIMER_RET              ;If no, skip clock update.
  444.                POP    DX                     ;Else, restore registers.
  445.                POP    CX
  446.                POP    AX
  447.                POPF
  448.                JMP    TIMER_TIC              ;Pass control to old INT 8.
  449.  
  450. TIMER_RET:     POP    DX                     ;Restore DX and CX; need AX still.
  451.                POP    CX
  452.                CLI                           ;No interrupts.
  453.                MOV    AL,20H                 ;Send 8259 command port EOI
  454.                OUT    20H,AL                 ; end-of-interrupt.
  455.                POP    AX                     ;Restore other registers.
  456.                POPF
  457.                IRET                          ;Interrupt return.
  458.  
  459. BUFFER:
  460.  
  461. CODE ENDS
  462. END  START
  463.  
  464.  
  465.  
  466.