home *** CD-ROM | disk | FTP | other *** search
/ Chip: Special Sound & MIDI / Chip-Special_Sound-und-Midi-auf-dem-PC.bin / midiprog / mplay.asm < prev    next >
Assembly Source File  |  1993-11-21  |  46KB  |  1,961 lines

  1. title MPLAY - Standard MIDI to MPU && PLAY Utility
  2. page 64,132
  3. .186
  4.  
  5. IS_PCNO            equ  1
  6. IS_TEST            equ  2
  7. IS_VERBOSE         equ  4
  8. IS_NOTEON          equ  8
  9. IS_DISPLAY         equ  16
  10. IS_TEMPOCHANGE     equ  32
  11. IS_PLAY            equ  128
  12.  
  13. DATA_END           equ  0fch
  14. CHANGE_TEMPO       equ  0f2h
  15.  
  16. MAX_TRACKS         equ  64
  17. SCREEN_SAVE        equ  2000h
  18. STACK_ADDR         equ  4000h
  19. ADDR_TRKNAME       equ  STACK_ADDR
  20. ADDR_INSTRNAME     equ  ADDR_TRKNAME + 800h
  21. ADDR_FIRSTTRK      equ  ADDR_INSTRNAME + 800h
  22.  
  23.          code segment 'code'
  24.          assume cs:code,ds:code,es:code
  25.  
  26.          org 100h
  27.  
  28. start  : jmp   anfang
  29.  
  30. ; -----
  31. ; Texte
  32. msg_hello      db  'MPLAY Roland MPU 401 SMF Typ 0/1 Play Utility Version 1.04a',13,10
  33.                db  'Copyright (C) 1993 by Andreas Nieden'
  34.                db  13,10,10,0
  35. msg_help       db  'Aufruf: MPLAY [-tv] Datei(en)'
  36. msg_cr         db  13,10,0
  37. msg_nompu      db  'Kein ROLAND MPU401 Interface gefunden',13,10,0
  38. msg_mpuio      db  'ROLAND MPU401 gefunden auf IO-Adresse 0x',0
  39. msg_intr       db  13,10,'Interface installiert auf IRQ 0x',0
  40. msg_nointr     db  13,10,'Interruptvektor-Test fehlgeschlagen',13,10,0
  41. msg_fnf        db  'Datei(en) nicht gefunden',13,10,0
  42. msg_nosmf      db  'Kein Standard MIDI File Typ 0/1',13,10,0
  43. msg_notbase    db  'Ungültige Timebase',13,10,0
  44. msg_toomanytrk db  'Zuviele Tracks im SMF',13,10,0
  45. msg_trktoolong db  'Track leider zu lang',13,10,0
  46. msg_unexpeof   db  'Unerwartetes Dateiende',13,10,0
  47. msg_invdelta   db  'Ungültiges Time-Delta',13,10,0
  48. msg_runerror   db  'SMF-Laufzeitfehler',13,10,0
  49. msg_loop       db  'FATAL: LOOP in put_cmd()',13,10,0
  50. msg_load       db  'Lade Datei, ',0
  51. msg_convert    db  'konvertiere, ',0
  52. msg_ready      db  'fertig.',13,10,0
  53.  
  54. msg_already    db  'MPLAY ist bereits installiert!',13,10,0
  55. msg_shell      db  'SHELL-ESCAPE - mit "EXIT" gelangen Sie zurück zu MPLAY'
  56.                db  13,10,10,0
  57.  
  58. ; ---------
  59. ; Variablen
  60. ALIGN 4
  61. oldvec         dd  0                     ; Alter Interruptvektor
  62. mpuaddr        dd  0                     ; Zeiger auf MPU-Spur
  63. mmseconds      dd  0                     ; Microsekunden insgesamt
  64. smftime        dd  0                     ; Microsekunden / Tick
  65. semaphor       dw  0500h,0               ; Semaphor für MPLAY
  66. trackcnt       dd  MAX_TRACKS  dup (0)   ; Trackcounter
  67.  
  68. screen         dw  0b800h
  69. expected       dw  0                     ; Zeit des Stücks in Sekunden
  70. passed         dw  0                     ; Wird inkrementiert
  71. tunits         dw  0                     ; Exp. Sekunden * 32
  72.  
  73. mpuseg         dw  0                     ; Segment der MPU-Spur
  74. mpucnt         dw  0                     ; MPU Track Time Counter
  75. trackseg       dw  MAX_TRACKS  dup (0)   ; Segmente der Tracks
  76. trackofs       dw  MAX_TRACKS  dup (0)   ; Trackoffsets
  77. fileaddr       dw  0                     ; Adresse Dateiname
  78. shandle        dw  0                     ; Filehandle Datei
  79. tracks         dw  0                     ; Anzahl der Tracks im SMF
  80. timebase       dw  0                     ; Timebase
  81. trklen         dw  0                     ; Tracklänge
  82. akt_track      dw  0                     ; Enthält gerade aktuellen Track
  83. trackstogo     dw  0                     ; Zähler für große Schleife
  84. mpuio          dw  0                     ; Startadresse ROLAND MPU401
  85. starttime      dw  0                     ; Start Zeit
  86. lasttime       dw  0                     ; Letzte Messung
  87. oldsec         dw  0                     ; Letzte Sekundenzahl
  88. tbvalues       dw  48,72,96,120,144,168,192
  89.  
  90. ; ----
  91. ; Hier kommen die Werte der anderen Attribute
  92. attributes     dw  7*160 + 2*1
  93.                db  22,1fh
  94.                dw  7*160 + 2*24
  95.                db  32,2
  96.                dw  7*160 + 2*57
  97.                db  22,1fh
  98.  
  99.                dw  9*160 + 2*1
  100.                db  22,1fh
  101.                dw  9*160 + 2*24
  102.                db  32,4
  103.                dw  9*160 + 2*57
  104.                db  22,1fh
  105.  
  106.                dw  11*160 + 2*1
  107.                db  78,7
  108.                dw  12*160 + 2*1
  109.                db  78,7
  110.                dw  13*160 + 2*1
  111.                db  78,7
  112.                dw  14*160 + 2*1
  113.                db  78,7
  114.                dw  15*160 + 2*1
  115.                db  78,7
  116.                dw  16*160 + 2*1
  117.                db  78,7
  118.                dw  17*160 + 2*1
  119.                db  78,7
  120.                dw  0
  121.  
  122. ; -------------
  123. ; Bytevariablen
  124. ALIGN 16
  125. msg_keyboard label byte
  126. db '╒══════════════════════╤════════════════════════════════╤══════════════════════╕'
  127. db '│  TIME EXPECTED 00:00 │■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■│ TIME PASSED  00:00   │'
  128. db '╞══════════════════════╪════════════════════════════════╪══════════════════════╡'
  129. db '│  Current Tempo = 000 │■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■│ SMF Timebase = 000   │'
  130. db '╞══════════════════════╧════════════════════════════════╧══════════════════════╡'
  131. db '│  ┌─▄─▄─┬─▄─▄─▄─┬─▄─▄─┬─▄─▄─▄─┬─▄─▄─┬─▄─▄─▄─┬─▄─▄─┬─▄─▄─▄─┬─▄─▄─┬─▄─▄─▄─┬─┐   │'
  132. db '│  │ █ █ │ █ █ █ │ █ █ │ █ █ █ │ █ █ │ █ █ █ │ █ █ │ █ █ █ │ █ █ │ █ █ █ │ │   │'
  133. db '│  │ █ █ │ █ █ █ │ █ █ │ █ █ █ │ █ █ │ █ █ █ │ █ █ │ █ █ █ │ █ █ │ █ █ █ │ │   │'
  134. db '│  │ █ █ │ █ █ █ │ █ █ │ █ █ █ │ █ █ │ █ █ █ │ █ █ │ █ █ █ │ █ █ │ █ █ █ │ │   │'
  135. db '│  │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │   │'
  136. db '│  │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │   │'
  137. db '│  └─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┘   │'
  138. db '╘══════════════════════════════════════════════════════════════════════════════╛'
  139.  
  140. notentext      db  '▄█████▀'   ; Noten an
  141.                db  '─     ─'   ; Note off
  142.  
  143. trackflags     db  MAX_TRACKS  dup (0)   ; Trackflags
  144. keymaps        db   0,7 ; C
  145.                db   2,4 ; C#
  146.                db   4,7 ; D
  147.                db   6,4 ; D#
  148.                db   8,7 ; E
  149.                db  12,7 ; F
  150.                db  14,4 ; F#
  151.                db  16,7 ; G
  152.                db  18,4 ; G#
  153.                db  20,7 ; A
  154.                db  22,4 ; A#
  155.                db  24,7 ; B
  156.  
  157. command        db  'C:\COMMAND.COM',0
  158. param          db  0,13
  159.  
  160. ALIGN 4
  161. pcmd           dw  0
  162.                dw  offset param
  163.                dw  0
  164.                dw  4 dup (0)
  165.  
  166. mflag          db  0                     ; Flag
  167. tbmultiply     db  0                     ; Timebase Multiplyer
  168. tbtodrop       db  0                     ; Relativ TEMPO
  169. tbcmd          db  0                     ; Timebase Kommando
  170. tempo          db  0                     ; Tempo setzen
  171. wtempo         db  0
  172. mpuacks        db  0
  173. intcnt         db  0
  174. oldmask        db  0
  175. mpudata        db  0
  176. irq            db  0
  177. irqvec         db  0
  178. irqtab         db  2,   3, 4, 5, 7,-1
  179. vectab         db  10, 11,12,13,15
  180. options        db  'PTQ',0
  181. smf_ext        db  '.MID',0
  182. attr1          db  14
  183. attr2          db  4fh
  184. attr3          db  21h
  185. attr4          db  3ah
  186. aktnote        db  0
  187. ampm           db  0               ; AM oder PM
  188. smftempo       db  3 dup (0)
  189. lastevent      db  3 dup (0)       ; letzter Event für dispnote
  190. controllers    db  01h,00h,07h,7fh,40h,00h,0
  191.  
  192. ; -------
  193. ; MPU 401 Interruptservice Routine
  194. ALIGN 16
  195. mpuintr: pusha
  196.          mov   dx,cs:mpuio     ; Base I/O Adresse MPU401
  197.          inc   dx              ; +1 = STATPORT
  198.          in    al,dx           ; kömmt der Interrupt von der MPU?
  199.          test  al,80h
  200.          jz    mpui100         ; Ja, dann ok.
  201.  
  202. mpuiex   proc  far
  203.          popa
  204.          jmp   cs:oldvec       ; Wenn nicht, dann auf Originalvektor!
  205. mpuiex   endp
  206.  
  207. mpui100: push  ds
  208.          push  es
  209.          mov   ax,cs
  210.          mov   ds,ax
  211.          mov   es,ax
  212.          cld                   ; Ganz wichtig für String Befehle !
  213.          dec   dx
  214.  
  215.          inc   intcnt
  216.  
  217.          in    al,dx           ; Byte aus dem Datenregister lesen
  218.  
  219.          cmp   al,0feh         ; Acknowledge ?
  220.          jnz   mpui120
  221.  
  222.          inc   mpuacks
  223.          jmp   mpui900
  224.  
  225. mpui120: cmp   al,0fch         ; ALL END !
  226.          jnz   mpui140
  227.  
  228.          and   mflag,not IS_PLAY
  229.          jmp   mpui900         ; Flag zurücksetzen
  230.  
  231. mpui140: cmp   al,0f9h         ; Conductor Request ?
  232.          jnz   mpui200         ; nein, weiterschauen
  233.  
  234.          mov   al,tempo
  235.          or    al,al
  236.          jz    mpui160         ; Hat sich das Tempo überhaupt geändert ?
  237.  
  238.          or    mflag,IS_TEMPOCHANGE
  239.          mov   wtempo,al       ; Tempowert für Anzeige
  240.          mov   tempo,0         ; altes Tempo setzen
  241.          push  ax              ; Tempowert sichern
  242.          mov   al,0            ; Timedelta = 0
  243.          call  put_midi        ; Ausgeben
  244.          mov   al,0e0h         ; CMD Setze tempo
  245.          call  put_midi        ; ausgeben
  246.          pop   ax              ; Tempowert holen
  247.          call  put_midi        ; ausgeben
  248.  
  249.          jmp   mpui900         ; und zurück
  250.  
  251. mpui160: mov   al,4            ; Alle 4 MIDI Clocks
  252.          call  put_midi
  253.          mov   al,0f8h         ; No Operation
  254.          call  put_midi
  255.          jmp   mpui900
  256.  
  257. mpui200: cmp   al,0f0h         ; Alles andere als Track DATA Request 1
  258.          jz    mpui400
  259.  
  260.          jmp   mpui900         ; ist Mumpitz ...
  261.  
  262. ; ----
  263. ; Hier ist der MAIN Server für den aktuellen TRACK
  264. mpui400: ; mov   word ptr cs:lastevent,0
  265.          lds   si,mpuaddr      ; Adresse MPU-Spur
  266.          call  getmpu          ; Time Delta nach AL holen
  267.          call  put_midi        ; Ausgeben
  268.  
  269.          call  getmpu          ; MIDI-Event holen
  270.          cmp   al,0fch         ; ALL End ?
  271.          jnz   mpui410
  272.  
  273.          and   cs:mflag,not IS_PLAY
  274.          jmp   mpui600
  275.  
  276. mpui410: call  testlast            ; Letzter Event
  277.          cmp   al,0f8h             ; No Operation ?
  278.          jz    mpui600             ; Ausgeben und - ENDE -
  279.  
  280.          cmp   al,CHANGE_TEMPO     ; Change Tempo ?
  281.          jnz   mpui420
  282.  
  283.          call  getmpu              ; Wert holen
  284.          mov   cs:tempo,al         ; und ablegen
  285.          mov   al,0f8h             ; No Operation
  286.          jmp   short mpui600
  287.  
  288. mpui420: mov   cs:lastevent,al     ; erstes Byte sichern
  289.          test  al,80h              ; Runtime Event ?
  290.          jnz   mpui430             ; nein, dann weiterschauen !
  291.  
  292.          call  put_midi            ; Notennummer ausgeben
  293.          call  getmpu              ; Velocity nach AL holen
  294.          mov   cs:lastevent+1,al   ; zweites Byte sichern
  295.  
  296.          jmp   short mpui600       ; und ausgeben - ENDE -
  297.  
  298. mpui430: call  put_midi            ; EVENT ausgeben!
  299.          mov   ah,al
  300.          shr   ah,4
  301.  
  302.          cmp   ah,0bh              ; Alles drunter ist 3 Byte groß!
  303.          ja    mpui460
  304.  
  305. mpui440: call  getmpu              ; Zweites Byte holen
  306.          mov   cs:lastevent+1,al   ; zweites Byte sichern
  307.  
  308. mpui450: call  put_midi            ; Und ausgeben
  309.          jmp   short mpui480       ; Und letztes Byte bearbeiten ...
  310.  
  311. mpui460: cmp   ah,0eh              ; PitchBender
  312.          jz    mpui440             ; ist auch drei Bytes groß
  313.  
  314. mpui480: call  getmpu              ; ... letztes Byte holen und ausgeben
  315.          mov   cs:lastevent+2,al   ; drittes Byte sichern
  316.  
  317. mpui600: call  put_midi
  318.          mov   word ptr cs:mpuaddr,si
  319.  
  320. mpui900: pop   es
  321.          pop   ds
  322.          mov   al,20h
  323.          out   20h,al
  324.          popa
  325.          iret
  326.  
  327.  
  328. ; -----
  329. ; shell - Führt eine SHELL aus
  330. shell  : call  endscr
  331.  
  332.          lea   si,msg_shell
  333.          call  wstring
  334.  
  335.          mov   ax,cs
  336.          mov   pcmd + 4,ax
  337.          lea   dx,command
  338.          lea   bx,pcmd
  339.          mov   ax,4b00h
  340.          int   21h
  341.  
  342. shell100:mov   ax,cs
  343.          mov   ds,ax
  344.          mov   es,ax
  345.          call  initscr
  346.          ret
  347.  
  348. ; --------
  349. ; testlast - Testet letzten Event !
  350. testlast:push  ax
  351.          and   cs:mflag,not IS_NOTEON
  352.          mov   al,cs:lastevent
  353.          test  al,80h
  354.          jnz   testl100
  355.  
  356.          cmp   cs:lastevent+1,0
  357.          jz    testl140
  358.          or    cs:mflag,IS_NOTEON
  359.          jmp   short testl140
  360.  
  361. testl100:shr   al,4
  362.          cmp   al,9
  363.          ja    testl_ex
  364.  
  365.          jnz   testl120
  366.  
  367.          cmp   cs:lastevent+2,0
  368.          jz    testl120
  369.  
  370.          or    cs:mflag,IS_NOTEON
  371.  
  372. testl120:mov   al,cs:lastevent+1
  373. testl140:call  dispnote
  374.  
  375. testl_ex:pop   ax
  376.          ret
  377.  
  378. ; ------
  379. ; getmpu - Holt Aktuelles Byte aus der MPU-Spur
  380. getmpu : lodsb
  381.          or    si,si
  382.          jnz   getmpuex
  383.          add   word ptr cs:mpuaddr+2,1000h
  384. getmpuex:ret
  385.  
  386. ; --------
  387. ; dispnote - Note in AL
  388. dispnote:cmp   al,36
  389.          jb    dispn10
  390.          cmp   al,96
  391.          ja    dispn10
  392.  
  393.          mov   cs:aktnote,al
  394.          or    cs:mflag,IS_DISPLAY
  395. dispn10: ret
  396.  
  397. disploop:test  mflag,IS_DISPLAY
  398.          jz    dispnex
  399.  
  400.          push  es
  401.          mov   al,aktnote
  402.          mov   es,screen
  403.          mov   di,11*160 + 8   ; Startoffset
  404.  
  405.          sub   al,36
  406.          cbw
  407.          mov   bl,12
  408.          div   bl              ; Es gilt die Oktave zu ermitteln
  409.          push  ax              ; Rest sichern
  410.          mov   ah,28
  411.          mul   ah
  412.          add   di,ax           ; Auf den Offset addieren
  413.          pop   ax
  414.          shr   ax,8
  415.          mov   si,ax
  416.          shl   si,1
  417.          add   si,offset keymaps
  418.          lodsb
  419.          mov   ah,0
  420.          add   di,ax           ; Endgültiger Offset
  421.          lodsb
  422.          mov   cl,al
  423.          mov   ch,0
  424.  
  425.          lea   si,notentext
  426.          mov   ah,15
  427.          test  mflag,IS_NOTEON
  428.          jnz   dispn20
  429.          mov   ah,7
  430.          cmp   cl,4
  431.          jz    dispn20
  432.          add   si,7
  433.  
  434. dispn20: lodsb
  435.          stosw
  436.          add   di,158
  437.          loop  dispn20
  438.  
  439. dispn90: and   mflag,not IS_DISPLAY
  440.          pop   es
  441.  
  442. dispnex: ret
  443.  
  444. ; ------
  445. ; prtime - Gibt Zeit in AX = Sekunden aus
  446. ; Adresse SCREEN = DI
  447. prtime:  pusha
  448.          push  es
  449.          mov   es,screen
  450.          xor   dx,dx
  451.          mov   bx,60
  452.          div   bx
  453.          push  dx              ; Rest sichern
  454.          call  prtime10
  455.  
  456.          or    byte ptr es:[di+1],80h
  457.          add   di,2
  458.          pop   ax
  459.          call  prtime10
  460.          pop   es
  461.          popa
  462.          ret
  463. prtime10:aam
  464.          or    ax,3030h
  465.          xchg  al,ah
  466.          stosb
  467.          inc   di
  468.          mov   al,ah
  469.          stosb
  470.          inc   di
  471.          ret
  472.  
  473. ; ------
  474. ; bcd - Rechnet BCD AL in Binärzahl um
  475. bcd    : push  bx
  476.          mov   bl,al
  477.          shr   al,4
  478.          mov   ah,10
  479.          mul   ah
  480.          and   bl,15
  481.          add   al,bl
  482.          mov   ah,0
  483.          pop   bx
  484.          ret
  485.  
  486. ; -------
  487. ; calcsec - Rechnet REALTIME Clock Daten in Sekunden um!
  488. calcsec: mov   ampm,0
  489.          mov   al,ch
  490.          cmp   al,12
  491.          jbe   calcs10
  492.  
  493.          sub   al,12
  494.          inc   ampm
  495.  
  496. calcs10: call  bcd             ; Stunden
  497.          mov   bx,ax
  498.          imul  bx,3600
  499.          mov   al,cl
  500.          call  bcd
  501.          mov   ah,60
  502.          mul   ah
  503.          add   bx,ax
  504.          mov   al,dh
  505.          call  bcd
  506.          add   bx,ax
  507.          mov   ax,bx
  508.          ret
  509.  
  510. ; -------
  511. ; initscr
  512. initscr: pusha
  513.          push  ds
  514.          xor   ax,ax
  515.          mov   ds,ax
  516.          mov   si,463h
  517.          lodsw
  518.          pop   ds
  519.          cmp   ax,03d4h
  520.          jz    inits10
  521.          mov   screen,0b000h
  522.          mov   ax,7070h
  523.          mov   word ptr attr1,ax
  524.          mov   word ptr attr3,ax
  525. inits10: push  ds
  526.          mov   ds,screen
  527.          mov   si,6*160
  528.          mov   di,SCREEN_SAVE
  529.          mov   cx,13*80
  530.          rep   movsw
  531.          pop   ds
  532.          push  es
  533.          mov   es,screen
  534.          mov   di,6*160
  535.          mov   ah,attr1
  536.          lea   si,msg_keyboard
  537.          mov   cx,13*80
  538. inits20: lodsb
  539.          stosw
  540.          loop  inits20
  541.  
  542. ; ----
  543. ; Hier setzen wir Attribute !
  544.          lea   si,attributes
  545.  
  546. inits30: lodsw
  547.          or    ax,ax
  548.          jz    inits_ex
  549.  
  550.          mov   di,ax
  551.          lodsb
  552.          mov   ah,0
  553.          mov   cx,ax
  554.          lodsb
  555. inits40: inc   di
  556.          stosb
  557.          loop  inits40
  558.          jmp   short inits30
  559.  
  560. inits_ex:pop   es
  561.          popa
  562.          ret
  563.  
  564. ; --------
  565. ; inittime - Initialisiert die Zeitberechnung
  566. inittime:mov   ax,0200h
  567.          int   1ah       ; Get REAL TIME
  568.  
  569.          call  calcsec   ; Tageszeit in Sekunden umrechnen
  570.          mov   starttime,ax
  571.          mov   lasttime,ax
  572.  
  573.          mov   passed,0
  574.  
  575.          add   ax,expected
  576.          mov   expected,ax
  577.  
  578.  
  579.          mov   ax,expected
  580.          sub   ax,lasttime
  581.          mov   di,7*160 + 2*17
  582.          call  prtime
  583.  
  584.          mov   ax,lasttime
  585.          sub   ax,starttime
  586.          mov   di,7*160 + 2*71
  587.          call  prtime
  588.          ret
  589.  
  590. ; -----
  591. ; wtime - Gibt die aktuelle Zeit aus
  592. wtime  : test  mflag,IS_TEMPOCHANGE
  593.          jz    wtime00
  594.  
  595.          mov   di,9*160 + 2*19
  596.          mov   al,wtempo
  597.          mov   ah,0
  598.          cwd
  599.          call  wdz
  600.  
  601.          mov   di,9*160 + 2*73
  602.          mov   ax,timebase
  603.          cwd
  604.          call  wdz
  605.          and   mflag,not IS_TEMPOCHANGE
  606.  
  607. wtime00: mov   ax,0200h
  608.          int   1ah
  609.          call  calcsec
  610.          cmp   lasttime,ax
  611.          jz    wtime_ex
  612.  
  613.          mov   lasttime,ax
  614.  
  615.          mov   ax,expected
  616.          sub   ax,lasttime
  617.          jb    wtime_ex
  618.  
  619.          mov   di,7*160 + 2*17
  620.          call  prtime
  621.  
  622.          mov   ax,lasttime
  623.          sub   ax,starttime
  624.          mov   passed,ax
  625.  
  626.          mov   di,7*160 + 2*71
  627.          call  prtime
  628.  
  629.          mov   ax,32
  630.          mul   word ptr passed
  631.          div   word ptr tunits
  632.  
  633.          mov   cx,ax
  634.          jcxz  wtime_ex
  635.  
  636.          push  es
  637.          mov   es,screen
  638.          mov   di,7*160 + 2*24
  639.  
  640.          push  cx
  641.          mov   al,4            ; ROT
  642. wtime10: inc   di
  643.          stosb
  644.          loop  wtime10
  645.          pop   cx
  646.  
  647.          mov   di,9*160 + 2*24
  648.  
  649.          mov   al,2            ; GRÜN
  650. wtime20: inc   di
  651.          stosb
  652.          loop  wtime20
  653.  
  654.          pop   es
  655.  
  656. wtime_ex:ret
  657.  
  658. ; ------
  659. ; endscr - Bildschirm wiederherstellen
  660. endscr : pusha
  661.          push  es
  662.          mov   es,screen
  663.          mov   di,6*160
  664.          mov   si,SCREEN_SAVE
  665.          mov   cx,13*80
  666.          rep   movsw
  667.  
  668.          pop   es
  669.          popa
  670.          ret
  671.  
  672. ; ---------
  673. ; Utilities
  674. ; -------
  675. ; wstring
  676. wstring: push  ax
  677.          push  si
  678. wstr100: lodsb
  679.          or    al,al
  680.          jz    wstr200
  681.          call  wchar
  682.          jmp   short wstr100
  683. wstr200: pop   si
  684.          pop   ax
  685.          ret
  686.  
  687. wchar  : push  ax
  688.          push  dx
  689.          mov   dl,al
  690.          mov   ah,2
  691.          int   21h
  692.          pop   dx
  693.          pop   ax
  694.          ret
  695.  
  696. ; ------
  697. ; werror
  698. werror : push  bx
  699.          push  cx
  700.          push  si
  701.          xor   cx,cx
  702.          mov   dx,si
  703.  
  704. werr10 : lodsb
  705.          or    al,al
  706.          jz    werr20
  707.          inc   cx
  708.          jmp   short werr10
  709.  
  710. werr20 : mov   ax,4000h
  711.          mov   bx,2
  712.          int   21h
  713.          pop   si
  714.          pop   cx
  715.          pop   bx
  716.          ret
  717.  
  718. hexw   : xchg  al,ah
  719.          call  hexb
  720.          xchg  al,ah
  721.  
  722. hexb   : push  ax
  723.          push  cx
  724.          mov   ah,al
  725.          mov   cl,4
  726.          shr   al,cl
  727.          call  hexb500
  728.          mov   al,ah
  729.          and   al,15
  730.          call  hexb500
  731.          pop   cx
  732.          pop   ax
  733.          ret
  734.  
  735. hexb500: add   al,'0'
  736.          cmp   al,'9'
  737.          jbe   hexb600
  738.          add   al,7
  739. hexb600: call  wchar
  740.          ret
  741.  
  742. ; ------------
  743. ; DZ - Ausgabe einer Dezimalzahl in DX:AX
  744. dz     : pusha
  745.          xor   cx,cx
  746. de100  : call  dv
  747.          push  bx
  748.          inc   cx
  749.          or    ax,ax
  750.          jnz   de100
  751.          or    dx,dx
  752.          jnz   de100
  753.          mov   bx,8
  754. de200  : cmp   bx,cx
  755.          jz    de300
  756.          mov   al,' '
  757.          call  wchar
  758.          dec   bx
  759.          jmp   short de200
  760. de300  : pop   ax
  761.          or    al,'0'
  762.          call  wchar
  763.          loop  de300
  764.          popa
  765.          ret
  766.  
  767. ; ------------
  768. ; WDZ - Ausgabe einer Dezimalzahl in DX:AX WINDOW Routine
  769. wdz    : pusha
  770.          push  es
  771.          mov   es,screen
  772.          xor   cx,cx
  773. wdz100 : call  dv
  774.          push  bx
  775.          inc   cx
  776.          or    ax,ax
  777.          jnz   wdz100
  778.          or    dx,dx
  779.          jnz   wdz100
  780.          mov   bx,3
  781. wdz200 : cmp   bx,cx
  782.          jz    wdz300
  783.          mov   al,' '
  784.          stosb
  785.          inc   di
  786.          dec   bx
  787.          jmp   short wdz200
  788. wdz300 : pop   ax
  789.          or    al,'0'
  790.          stosb
  791.          inc   di
  792.          loop  wdz300
  793.          pop   es
  794.          popa
  795.          ret
  796.  
  797. dv     : push  cx
  798.          xor   bx,bx
  799.          mov   cx,20h
  800. dv10   : shl   ax,1
  801.          rcl   dx,1
  802.          rcl   bx,1
  803.          sub   bx,10
  804.          jnb   dv20
  805.          add   bx,10
  806.          add   ax,1
  807.          adc   dx,0
  808. dv20   : loop  dv10
  809.          not   ax
  810.          not   dx
  811.          pop   cx
  812.          ret
  813.  
  814. ; -----
  815. ; lumul Long Unsigned Multiplikation
  816. ; eingabe operand1 = ax:dx operand2 = cx:bx
  817. ; ausgabe ergebnis = ax:dx
  818.  
  819. lumul  : push  ax            ;save low word
  820.          push  dx            ;save high word
  821.          mul   bx
  822.          mov   bx,ax         ;keep partial product
  823.          pop   ax            ;get high word
  824.          mul   cx
  825.          add   bx,ax         ;add to partial product
  826.          pop   ax            ;get low word
  827.          mul   cx
  828.          add   dx,bx         ;add in partial products
  829.          ret
  830.  
  831. ; -----
  832. ; ludiv - Long Unsigned Division
  833. ; Eingabe: Dividend in AX:DX
  834. ;          Divisor  in CX:BX
  835. ; Ausgabe: Quotient in AX:DX
  836. ;          Rest     in CX:BX
  837.  
  838. ludiv  : test  bx,bx
  839.          jnz   lud200
  840.          cmp   cx,dx
  841.          ja    lud100
  842.          push  ax
  843.          mov   ax,dx
  844.          sub   dx,dx
  845.          div   cx
  846.          mov   bx,ax
  847.          pop   ax
  848.          div   cx
  849.          mov   cx,dx
  850.          mov   dx,bx
  851.          sub   bx,bx
  852.          ret
  853.  
  854. lud100 : div   cx
  855.          mov   cx,dx
  856.          mov   dx,bx
  857.          ret
  858.  
  859. lud200 : push  bp
  860.          push  di
  861.          push  si
  862.          mov   si,cx
  863.          mov   di,bx
  864.          sub   bx,bx
  865.          sub   bp,bp
  866.          mov   cx,32
  867.  
  868. lud220 : shl   ax,1
  869.          rcl   dx,1
  870.          rcl   bp,1
  871.          rcl   bx,1
  872.          sub   bp,si
  873.          sbb   bx,di
  874.          js    lud280
  875.  
  876. lud240 : inc   ax
  877.          loop  lud220
  878.          jmp   short lud300
  879.  
  880. lud260 : shl   ax,1
  881.          rcl   dx,1
  882.          rcl   bp,1
  883.          rcl   bx,1
  884.          add   bp,si
  885.          adc   bx,di
  886.          jns   lud240
  887.  
  888. lud280 : loop  lud260
  889.          add   bp,si
  890.          adc   bx,di
  891.  
  892. lud300 : mov   cx,bp
  893.          pop   si
  894.          pop   di
  895.          pop   bp
  896.          ret
  897.  
  898. toupper: cmp   al,'a'
  899.          jb    tou_ex
  900.          cmp   al,'z'
  901.          ja    tou_ex
  902.          and   al,not 32
  903. tou_ex : ret
  904.  
  905. ; -----
  906. ; taste - löscht den Tastaturpuffer und holt eine Tastenkombination nach AX
  907.  
  908. taste  : mov   ah,1
  909.          int   16h
  910.          jz    tast100
  911.          mov   ah,0
  912.          int   16h
  913.          jmp   short taste
  914. tast100: mov   ah,0
  915.          int   16h
  916.          ret
  917.  
  918. ; -----
  919. ; wtick - Wartet für einen Timertick ...
  920. wtick  : push  ax
  921.          push  bx
  922.          push  cx
  923.          push  dx
  924.          sti
  925.          xor   ax,ax
  926.          int   1ah
  927.          mov   bx,dx
  928. wtick10: xor   ax,ax
  929.          int   1ah
  930.          cmp   dx,bx
  931.          jz    wtick10
  932.          pop   dx
  933.          pop   cx
  934.          pop   bx
  935.          pop   ax
  936.          ret
  937.  
  938. ; ------
  939. ; alloff - ALL OFF über ACK !
  940.  
  941. alloff : mov   al,3fh
  942.          call  put_cmd
  943.          call  wtick
  944.          mov   al,0feh
  945.          call  put_midi
  946.  
  947.          mov   cx,9
  948. alloff10:call  wtick
  949.          loop  alloff10
  950.  
  951.          call  resetmpu
  952.          ret
  953.  
  954. ; -------
  955. ; waitrcv - Wait For MPU Receive Ready
  956.  
  957. waitrcv: push  ax
  958.          push  dx
  959.          mov   dx,cs:mpuio
  960.          inc   dx
  961.  
  962. waitr10: in    al,dx
  963.          test  al,40h
  964.          jnz   waitr10
  965.  
  966.          pop   dx
  967.          pop   ax
  968.          ret
  969.  
  970. ; --------
  971. ; put_midi - Ausgabe eines MidiBytes
  972.  
  973. put_midi:push  ax
  974.          push  dx
  975.          call  waitrcv
  976.  
  977.          mov   dx,cs:mpuio
  978.          pushf
  979.          cli
  980.          out   dx,al
  981.          popf
  982.  
  983.          pop   dx
  984.          pop   ax
  985.          ret
  986.  
  987. ; -------
  988. ; put_cmd - Ausgabe eines MPU-Kommandos
  989.  
  990. put_cmd: push  ax
  991.          push  cx
  992.          push  dx
  993.  
  994.          call  waitrcv
  995.  
  996.          pushf
  997.          cli
  998.          mov   dx,cs:mpuio
  999.          inc   dx
  1000.          out   dx,al
  1001.          mov   cx,4000h
  1002.  
  1003. put_c20: in    al,dx
  1004.          test  al,80h
  1005.          jz    put_c30
  1006.          loop  put_c20
  1007.  
  1008.          mov   al,20h
  1009.          out   20h,al
  1010.          sti
  1011.  
  1012.          mov   ax,cs
  1013.          mov   ds,ax
  1014.          call  idisable
  1015.          lea   si,msg_loop
  1016.          call  werror
  1017.          jmp   ende
  1018.  
  1019. put_c30: dec   dx
  1020.          in    al,dx
  1021.          inc   dx
  1022.          cmp   al,0feh
  1023.          jnz   put_c20
  1024.  
  1025. put_c90: popf
  1026.          pop   dx
  1027.          pop   cx
  1028.          pop   ax
  1029.          ret
  1030.  
  1031. ; -------
  1032. ; resetmpu - Reset MPU
  1033.  
  1034. resetmpu:push  ax
  1035.          push  dx
  1036.          call  waitrcv
  1037.  
  1038.          pushf
  1039.          cli
  1040.          mov   dx,cs:mpuio
  1041.          inc   dx
  1042.          mov   al,0ffh
  1043.          out   dx,al
  1044.  
  1045.          call  waitrcv
  1046.          dec   dx
  1047.          in    al,dx
  1048.          popf
  1049.  
  1050.          pop   dx
  1051.          pop   ax
  1052.          ret
  1053.  
  1054. ; -------
  1055. ; parscmd - Die Kommandozeile untersuchen
  1056.  
  1057. parscmd: mov   si,80h
  1058.          lodsw
  1059.          or    al,al
  1060.          jnz   pars100
  1061.  
  1062. helpexit:lea   si,msg_help
  1063.  
  1064. err_exit:call  werror
  1065.          mov   al,1
  1066.          jmp   ende
  1067.  
  1068. pars100: call  pars500
  1069.          jb    helpexit
  1070.  
  1071.          cmp   byte ptr [si],'-'
  1072.          jz    pars120
  1073.          cmp   byte ptr [si],'/'
  1074.          jnz   pars200
  1075.  
  1076. pars120: lodsw
  1077.          mov   al,ah
  1078. pars130: call  toupper
  1079.          lea   di,options
  1080.          mov   bl,1
  1081.  
  1082. pars140: cmp   byte ptr [di],0
  1083.          jz    helpexit
  1084.          scasb
  1085.          jz    pars160
  1086.          shl   bl,1
  1087.          jmp   short pars140
  1088.  
  1089. pars160: or    mflag,bl
  1090.          cmp   byte ptr [si],' '
  1091.          jz    pars100
  1092.          cmp   byte ptr [si],9
  1093.          jz    pars100
  1094.          cmp   byte ptr [si],13
  1095.          jz    helpexit
  1096.          lodsb
  1097.          jmp   short pars130
  1098.  
  1099. pars200: lea   di,filename
  1100.          mov   fileaddr,di
  1101.  
  1102.          mov   bl,0            ; BL kennzeichnet "."
  1103. pars220: lodsb
  1104.          cmp   al,13
  1105.          jz    pars300
  1106.  
  1107.          cmp   al,'.'
  1108.          jnz   pars240
  1109.          inc   bl
  1110.  
  1111. pars240: call  toupper
  1112.          stosb
  1113.  
  1114.          cmp   al,'\'
  1115.          jz    pars260
  1116.          cmp   al,':'
  1117.          jnz   pars220
  1118. pars260: mov   fileaddr,di
  1119.          jmp   short pars220
  1120.  
  1121. pars300: or    bl,bl
  1122.          jnz   pars320
  1123.  
  1124.          lea   si,smf_ext
  1125.          mov   cx,5
  1126.          rep   movsb
  1127.          ret
  1128.  
  1129. pars320: mov   al,0
  1130.          stosb
  1131.          ret
  1132.  
  1133. pars500: cmp   byte ptr [si],' '
  1134.          jz    pars540
  1135.          cmp   byte ptr [si],9
  1136.          jz    pars540
  1137.          cmp   byte ptr [si],13
  1138.          jnz   pars520
  1139.          stc
  1140.          ret
  1141.  
  1142. pars520: clc
  1143.          ret
  1144.  
  1145. pars540: inc   si
  1146.          jmp   short pars500
  1147.  
  1148. ; --------
  1149. ; fileloop - Durchsucht alle Dateien
  1150. fileloop:mov   ax,1a00h
  1151.          mov   dx,80h
  1152.          int   21h
  1153.  
  1154.          mov   ax,4e00h
  1155.          mov   cx,27h
  1156.          lea   dx,filename
  1157.          int   21h
  1158.          jnb   filel100
  1159.  
  1160. fnf_exit:lea   si,msg_fnf
  1161.          jmp   err_exit
  1162.  
  1163. filel100:mov   si,9eh
  1164.          mov   di,fileaddr
  1165.          mov   cx,6
  1166.          rep   movsw
  1167.          mov   al,0
  1168.          stosb
  1169.          lea   dx,filename
  1170.          mov   ax,3d00h
  1171.          int   21h
  1172.          jb    fnf_exit
  1173.  
  1174.          mov   shandle,ax
  1175.          call  loadfile
  1176.          mov   ax,3e00h
  1177.          mov   bx,shandle
  1178.          int   21h
  1179.          call  convert
  1180.  
  1181.          lea   si,msg_ready
  1182.          call  wstring
  1183.  
  1184.          test  mflag,IS_TEST
  1185.          jnz   filel120
  1186.  
  1187.          cld
  1188.          mov   ax,word ptr mmseconds
  1189.          mov   dx,word ptr mmseconds+2
  1190.          mov   cx,1000
  1191.          xor   bx,bx
  1192.          call  ludiv
  1193.          mov   cx,1000
  1194.          div   cx
  1195.          mov   expected,ax
  1196.          mov   passed,0
  1197.  
  1198.          mov   tunits,ax
  1199.  
  1200.          call  initscr
  1201.          call  inittime
  1202.          call  mplay
  1203.          call  endscr
  1204.  
  1205. filel120:mov   ax,4f00h
  1206.          int   21h
  1207.          jnb   filel100
  1208.          ret
  1209.  
  1210. ; --------
  1211. ; loadfile - Lädt die Datei
  1212. loadfile:lea   si,filename
  1213.          call  wstring
  1214.          lea   si,msg_cr
  1215.          call  wstring
  1216.          lea   si,msg_load
  1217.          call  wstring
  1218.  
  1219.          mov   ax,3f00h
  1220.          mov   bx,shandle
  1221.          mov   cx,14
  1222.          lea   dx,msg_hello
  1223.          int   21h
  1224.          mov   si,dx
  1225.          lodsw
  1226.          cmp   ax,'TM'         ; Magic Header SMF Teil 1
  1227.          jz    loadf100
  1228.  
  1229. nosmfex: lea   si,msg_nosmf
  1230.          jmp   err_exit
  1231.  
  1232. loadf100:lodsw
  1233.          cmp   ax,'dh'         ; Magic Header SMF Teil 2
  1234.          jnz   nosmfex
  1235.          lodsw
  1236.          or    ax,ax
  1237.          jnz   nosmfex
  1238.          lodsw
  1239.          xchg  al,ah
  1240.          cmp   ax,6            ; die obligatorische Anzahl der Bytes
  1241.          jnz   nosmfex         ; im Header des SMF
  1242.          lodsw
  1243.          xchg  al,ah
  1244.          cmp   ax,1            ; MIDI File Typ 0 oder 1
  1245.          ja    nosmfex         ; alles andere ist Mumpitz
  1246.          lodsw
  1247.          xchg  al,ah
  1248.          mov   tracks,ax       ; Anzahl der Tracks
  1249.  
  1250.          cmp   ax,MAX_TRACKS
  1251.          jb    loadf120
  1252.  
  1253.          lea   si,msg_toomanytrk
  1254.          jmp   err_exit
  1255.  
  1256. loadf120:lodsw
  1257.          xchg  al,ah
  1258.          mov   timebase,ax     ; Und zuletzt die Timebase laden
  1259.  
  1260.          call  settb
  1261.  
  1262.          ; Jetzt muß das Startsegment des ersten Tracks bestimmt werden
  1263.          mov   ax,cs           ; Codesegment nach AX
  1264.          mov   bx,ADDR_FIRSTTRK
  1265.          shr   bx,4            ; Zum Segment konvertieren
  1266.          add   ax,bx           ; Zum Codesegment hinzuaddieren
  1267.          mov   trackseg,ax     ; Erstes Tracksegment setzen
  1268.          mov   akt_track,0     ; Aktueller Track = 0
  1269.  
  1270. ; --------
  1271. ; loadf200 - Einsprung für jeden neuen Track
  1272. loadf200:mov   ax,3f00h
  1273.          mov   bx,shandle
  1274.          mov   cx,8
  1275.          lea   dx,msg_hello
  1276.          int   21h
  1277.          mov   si,dx
  1278.          lodsw
  1279.          cmp   ax,'TM'         ; Magic Header Track Teil 1
  1280.          jnz   nosmfex
  1281.  
  1282.          lodsw
  1283.          cmp   ax,'kr'         ; Magic Header Track Teil 2
  1284.          jnz   nosmfex
  1285.          lodsw
  1286.          or    ax,ax
  1287.          jz    loadf220
  1288.  
  1289.          lea   si,msg_trktoolong
  1290.          jmp   err_exit
  1291.  
  1292. loadf220:lodsw
  1293.          xchg  al,ah
  1294.          mov   trklen,ax       ; Tracklänge nur obligatorisch speichern
  1295.  
  1296.          mov   si,akt_track
  1297.          shl   si,1
  1298.          mov   word ptr trackofs[si],0
  1299.          ; Trackoffsets auf 0 setzen !
  1300.  
  1301.          mov   bx,trackseg[si] ; Aktuelles Tracksegment laden
  1302.          add   ax,15
  1303.          shr   ax,4            ; Tracklänge zum Segment konvertieren
  1304.          add   bx,ax           ; Endgültiges nächstes Segment
  1305.          mov   trackseg[si+2],bx
  1306.          mov   mpuseg,bx       ; Segment für MPU Spur setzen
  1307.  
  1308.          mov   ax,3f00h        ; Funktion LESEN
  1309.          mov   bx,shandle
  1310.          mov   cx,trklen
  1311.          xor   dx,dx
  1312.          push  ds
  1313.          mov   ds,trackseg[si]
  1314.          int   21h
  1315.          pop   ds
  1316.          cmp   ax,cx
  1317.          jz    loadf240
  1318.  
  1319.          lea   si,msg_unexpeof
  1320.          jmp   err_exit
  1321.  
  1322. loadf240:inc   akt_track
  1323.          mov   ax,akt_track
  1324.          cmp   ax,tracks
  1325.          jz    loadfex
  1326.          jmp   loadf200
  1327.  
  1328. loadfex: ret
  1329.  
  1330. ; ------
  1331. ; putmpu - Legt ein Byte in AL auf der MPU - Spur ab
  1332. putmpu : push  es
  1333.          push  di
  1334.          les   di,dword ptr mpuaddr
  1335.          stosb
  1336.          mov   word ptr mpuaddr,di
  1337.          or    di,di
  1338.          jnz   putmpuex
  1339.  
  1340.          add   word ptr mpuaddr+2,1000h
  1341.  
  1342. putmpuex:pop   di
  1343.          pop   es
  1344.          ret
  1345.  
  1346. ; --------
  1347. ; putdelta - Legt Timing Bytes in der MPU-Spur ab
  1348. putdelta:push  ax
  1349.          push  dx
  1350.          mov   ax,mpucnt
  1351.  
  1352.          mov   cl,tbmultiply
  1353.          shr   ax,cl
  1354.  
  1355. putdel02:cmp   ax,0f0h         ; Maximalwert Timing Byte
  1356.          jb    putdel20
  1357.  
  1358.          push  cx
  1359.          xor   dx,dx
  1360.          mov   cx,080h
  1361.          div   cx
  1362.          mov   cx,ax
  1363.  
  1364. putdel10:mov   al,080h
  1365.          call  putmpu
  1366.          mov   al,0f8h
  1367.          call  putmpu
  1368.          loop  putdel10
  1369.          pop   cx
  1370.  
  1371.          mov   al,dl
  1372.  
  1373. putdel20:call  putmpu
  1374.  
  1375.          mov   cx,mpucnt       ; Anzahl der Ticks
  1376.          xor   bx,bx
  1377.          mov   ax,word ptr smftime
  1378.          mov   dx,word ptr smftime+2
  1379.          call  lumul
  1380.          add   word ptr mmseconds,ax
  1381.          adc   word ptr mmseconds+2,dx
  1382.  
  1383.          mov   mpucnt,0
  1384.          pop   dx
  1385.          pop   ax
  1386.          ret
  1387.  
  1388. ; ------
  1389. ; getsmf - Holt ein Byte aus dem aktuellen Track nach AL
  1390. getsmf : push  bx
  1391.          push  si
  1392.          push  ds
  1393.          mov   bx,akt_track
  1394.          shl   bx,1
  1395.          mov   si,trackofs[bx]
  1396.          mov   ds,trackseg[bx]
  1397.          lodsb
  1398.          pop   ds
  1399.          mov   trackofs[bx],si
  1400.          pop   si
  1401.          pop   bx
  1402.          ret
  1403.  
  1404. ; --------
  1405. ; getdelta - Liest Timedelta nach AX:DX
  1406. getdelta:push  bx
  1407.          push  cx
  1408.          push  bp              ; Register sichern
  1409.          mov   bp,4            ; 4 Bytes = maximale Länge Timing Delta
  1410.  
  1411.          xor   ax,ax
  1412.          xor   dx,dx           ; AX:DX beherbergen Time Delta
  1413.  
  1414. getdel10:mov   cx,7
  1415. getdel20:shl   ax,1
  1416.          rcl   dx,1
  1417.          loop  getdel20        ; * 128
  1418.  
  1419.          push  ax
  1420.          call  getsmf          ; Byte aus dem Track holen!
  1421.          mov   cl,al
  1422.          pop   ax
  1423.  
  1424.          mov   bl,cl
  1425.          and   cx,07fh
  1426.          add   ax,cx
  1427.          adc   dx,0
  1428.          test  bl,80h
  1429.          jz    getdel30
  1430.  
  1431.          dec   bp
  1432.          jnz   getdel10
  1433.  
  1434.          lea   si,msg_invdelta
  1435.          jmp   err_exit
  1436.  
  1437. getdel30:pop   bp
  1438.          pop   cx
  1439.          pop   bx
  1440.          ret
  1441.  
  1442. ; -------
  1443. ; convert - Konvertiert SMF -> MPU
  1444. convert: lea   si,msg_convert
  1445.          call  wstring
  1446.  
  1447.          mov   word ptr mmseconds,0
  1448.          mov   word ptr mmseconds+2,0
  1449.  
  1450.          xor   ax,ax
  1451.          mov   di,ADDR_TRKNAME
  1452.          mov   cx,800h
  1453.          rep   stosw                     ; Track + Instrumentenname löschen
  1454.  
  1455.          lea   di,trackflags
  1456.          mov   cx,MAX_TRACKS
  1457.          rep   stosb                     ; Trackflags auf 0 setzen
  1458.  
  1459.          mov   word ptr mpuaddr,0        ; Offset MPU Pointer auf 0 setzen!
  1460.          mov   ax,mpuseg
  1461.          mov   word ptr mpuaddr+2,ax     ; Und Segment setzen
  1462.          mov   mpucnt,0                  ; MPU Time Counter = 0
  1463.  
  1464.          mov   cx,tracks                 ; CX = Anzahl der Tracks
  1465.          mov   trackstogo,cx             ; soviele Tracks sind abzuhandeln
  1466.          mov   akt_track,0               ; Aktueller Track = 0
  1467.  
  1468. ; ----
  1469. ; Hier setzen wir das erste Timingdelta
  1470. conv100: call  getdelta
  1471.          mov   bx,akt_track
  1472.          shl   bx,2
  1473.          mov   word ptr trackcnt[bx],ax
  1474.          mov   word ptr trackcnt[bx+2],dx
  1475.          inc   akt_track
  1476.          loop  conv100
  1477.  
  1478. ; -------
  1479. ; conv200 - äußerer Schleifenkopf
  1480. conv200: mov   akt_track,0               ; Aktueller Track = 0
  1481.  
  1482. conv220: mov   bx,akt_track
  1483.          cmp   byte ptr trackflags[bx],0
  1484.          jnz   conv500                   ; Wenn Track nicht aktiv weiter
  1485.  
  1486.          shl   bx,2
  1487.          cmp   word ptr trackcnt[bx],0
  1488.          jnz   conv240
  1489.          cmp   word ptr trackcnt[bx+2],0
  1490.          jz    conv300
  1491.  
  1492. conv240: sub   word ptr trackcnt[bx],1
  1493.          sbb   word ptr trackcnt[bx+2],0
  1494.          jmp   short conv500
  1495.  
  1496. ; ----
  1497. ; Hier ist der Trackzähler abgelaufen, der neue Wert muß geladen werden
  1498. conv300: call  putevent                  ; Event holen und ggfls. ablegen
  1499.  
  1500.          mov   bx,akt_track
  1501.          cmp   byte ptr trackflags[bx],0
  1502.          jnz   conv500                   ; Wenn Track nicht aktiv weiter
  1503.  
  1504.          call  getdelta                  ; nächstes Timedelta holen
  1505.  
  1506.          shl   bx,2
  1507.          mov   word ptr trackcnt[bx],ax
  1508.          mov   word ptr trackcnt[bx+2],dx
  1509.  
  1510.          or    ax,ax
  1511.          jnz   conv240
  1512.          or    dx,dx
  1513.          jz    conv300
  1514.          jmp   short conv240
  1515.  
  1516. conv500: inc   akt_track
  1517.          mov   ax,akt_track
  1518.          cmp   ax,tracks
  1519.          jb    conv220
  1520.  
  1521.          inc   mpucnt
  1522.  
  1523. conv520: cmp   trackstogo,0
  1524.          jnz   conv200
  1525.  
  1526.          mov   al,0
  1527.          call  putmpu
  1528.          mov   al,DATA_END
  1529.          call  putmpu
  1530.  
  1531.          mov   bx,word ptr mpuaddr
  1532.          add   bx,16
  1533.          shr   bx,4
  1534.          add   bx,word ptr mpuaddr+2
  1535.          mov   ax,cs
  1536.          sub   bx,ax
  1537.          mov   ax,4a00h
  1538.          int   21h
  1539.          ret
  1540.  
  1541. ; --------
  1542. ; putevent - bearbeitet einen EVENT
  1543. putevent:call  getsmf
  1544.          cmp   al,-1           ; META Event ?
  1545.          jz    putev020
  1546.  
  1547.          jmp   putev200        ; Nein, dann weiter
  1548.  
  1549. ; ----------
  1550. ; Abhandlung META Event
  1551. putev020:call  getsmf          ; Event lesen
  1552.          cmp   al,2fh          ; Event Trackende!
  1553.          jnz   putev100
  1554.  
  1555.          dec   trackstogo
  1556.          mov   bx,akt_track
  1557.          inc   byte ptr trackflags[bx]
  1558.          ret
  1559.  
  1560. ; --------
  1561. ; putev100 - Tempo Change ?
  1562. putev100:cmp   al,51h          ; Change Tempo wäre noch interessant (!)
  1563.          jnz   putev120
  1564.          call  getsmf          ; Länge ist immer = 3
  1565.  
  1566.          call  getsmf
  1567.          mov   smftempo,al
  1568.          push  ax
  1569.          call  getsmf
  1570.          mov   smftempo+1,al
  1571.          mov   ch,al
  1572.          call  getsmf
  1573.          mov   smftempo+2,al
  1574.          pop   bx
  1575.          mov   cl,al
  1576.          mov   bh,0
  1577.          mov   ax,8700h
  1578.          mov   dx,393h
  1579.          call  ludiv
  1580.  
  1581.          call  putdelta
  1582.          push  ax
  1583.          mov   al,CHANGE_TEMPO ; MPU Kommando - Change Tempo
  1584.          call  putmpu
  1585.          pop   ax
  1586.  
  1587.          call  putmpu
  1588.  
  1589.          mov   cx,timebase
  1590.          xor   bx,bx
  1591.          mov   dh,0
  1592.          mov   dl,smftempo
  1593.          mov   ah,smftempo+1
  1594.          mov   al,smftempo+2
  1595.          call  ludiv
  1596.          ; MICROSEC. / TIMEBASE = MICROSEC pro TICK
  1597.          mov   word ptr smftime,ax
  1598.          mov   word ptr smftime+2,dx
  1599.          ret
  1600.  
  1601. ; -----------
  1602. ; Ein für uns uninteressanter EVENT - überlesen und zurück!
  1603. putev120:call  getsmf          ; Länge nach AL
  1604.          mov   ah,0
  1605.          mov   cx,ax
  1606.          jcxz  putev160
  1607.  
  1608. putev140:call  getsmf
  1609.          loop  putev140        ; Und Event überlesen
  1610.  
  1611. putev160:ret
  1612.  
  1613. ; ----
  1614. ; Kein META-Event - also weitermachen
  1615. putev200:cmp   al,0f0h         ; SYSEX's
  1616.          jz    putev120        ; die überlesen wir einfach
  1617.  
  1618.          cmp   al,0f7h
  1619.          jz    putev120
  1620.  
  1621.          test  al,80h
  1622.          jnz   putev220
  1623.  
  1624.          ; Runtime EVENT
  1625.          call  putdelta
  1626.          call  putmpu
  1627.          call  getsmf
  1628.          test  al,80h
  1629.          jnz   runexit
  1630.          call  putmpu
  1631.          ret
  1632.  
  1633. putev220:mov   ah,al
  1634.          shr   ah,4
  1635.          cmp   ah,0bh
  1636.          jbe   putev240
  1637.  
  1638.          cmp   ah,0eh
  1639.          jz    putev240
  1640.  
  1641.          cmp   ah,0ch
  1642.          jnz   putev222
  1643.          test  mflag,IS_PCNO
  1644.          jz    putev222
  1645.          jmp   getsmf
  1646.  
  1647. putev222:call  putdelta
  1648.          call  putmpu
  1649.          call  getsmf
  1650.          test  al,80h
  1651.          jz    putev230
  1652.  
  1653. runexit: lea   si,msg_runerror
  1654.          jmp   err_exit
  1655.  
  1656. putev230:call  putmpu
  1657.          ret
  1658.  
  1659. putev240:call  putdelta
  1660.          call  putmpu
  1661.          call  getsmf
  1662.          test  al,80h
  1663.          jnz   runexit
  1664.  
  1665.          call  putmpu
  1666.          call  getsmf
  1667.          test  al,80h
  1668.          jnz   runexit
  1669.  
  1670.          call  putmpu
  1671.          ret
  1672.  
  1673. ; -----
  1674. ; settb - Set Internal Timebase
  1675. settb  : mov   ax,timebase
  1676.          mov   tbmultiply,0
  1677.  
  1678. settb10: lea   di,tbvalues
  1679.          mov   cx,7
  1680.          xor   bx,bx
  1681.  
  1682. settb20: scasw
  1683.          jz    settb30
  1684.          inc   bx
  1685.          loop  settb20
  1686.  
  1687.          inc   tbmultiply
  1688.          shr   ax,1
  1689.          or    ax,ax
  1690.          jnz   settb10
  1691.  
  1692.          lea   si,msg_notbase
  1693.          jmp   err_exit
  1694.  
  1695. settb30: add   bl,0c2h
  1696.          mov   tbcmd,bl
  1697.          ret
  1698.  
  1699. ; -----
  1700. ; mplay - Spielt die Datei
  1701. mplay  : call  ienable         ; Enable Interrupt Processing
  1702.  
  1703.          lea   si,controllers
  1704.  
  1705. mplay100:mov   cx,16
  1706.          mov   ah,0
  1707. mplay110:mov   al,0d0h
  1708.          or    al,ah
  1709.          call  put_cmd
  1710.          mov   al,0b0h
  1711.          or    al,ah
  1712.          call  put_midi
  1713.          mov   al,[si]
  1714.          call  put_midi
  1715.          mov   al,[si+1]
  1716.          call  put_midi
  1717.          inc   ah
  1718.          loop  mplay110
  1719.          add   si,2
  1720.          cmp   byte ptr [si],0
  1721.          jnz   mplay100
  1722.  
  1723.          call  wtick
  1724.          call  wtick
  1725.          call  wtick
  1726.          call  wtick
  1727.  
  1728.          mov   ax,mpuseg
  1729.          mov   word ptr mpuaddr,0
  1730.          mov   word ptr mpuaddr+2,ax
  1731.                                ; Adresse der MPU Spur setzen
  1732.  
  1733.          mov   al,0ech         ; Activate Tracks
  1734.          call  put_cmd
  1735.          mov   al,1
  1736.          call  put_midi        ; Und zwar nur 1 Track!
  1737.  
  1738.          mov   al,tbcmd
  1739.          call  put_cmd
  1740.  
  1741.          mov   al,8fh          ; Conductor ON
  1742.          call  put_cmd
  1743.  
  1744. mplay140:mov   al,0b8h         ; Clear Play Counter
  1745.          call  put_cmd
  1746.  
  1747.          mov   al,0ah          ; Start Play
  1748.          call  put_cmd
  1749.  
  1750.          or    mflag,IS_PLAY
  1751.  
  1752. mplay200:mov   ah,1
  1753.          int   16h
  1754.          jz    mplay220
  1755.  
  1756.          mov   ah,0
  1757.          int   16h
  1758.          cmp   al,27
  1759.          jz    mplay300
  1760.  
  1761.          call  toupper
  1762.          cmp   al,'S'
  1763.          jnz   mplay220
  1764.  
  1765.          call  shell
  1766.          jmp   short mplay200
  1767.  
  1768. mplay220:call  wtime
  1769.          call  disploop
  1770.          test  mflag,IS_PLAY
  1771.          jnz   mplay200
  1772.  
  1773.          mov   al,5
  1774.          call  put_cmd         ; Stop PLAY
  1775.          call  wtick
  1776.          call  idisable        ; Disable Interrupt Processing
  1777.          ret
  1778.  
  1779. mplay300:and   mflag,not IS_PLAY
  1780.          call  wtick
  1781.          call  wtick
  1782.          mov   al,5
  1783.          call  put_cmd         ; Stop PLAY
  1784.          call  wtick
  1785.          call  wtick
  1786.          call  idisable        ; Disable Interrupt Processing
  1787.          call  alloff          ; Alle Stimmen über ACK ausschalten
  1788.          ret
  1789.  
  1790. ; -------
  1791. ; ienable - Enable Interrupt Processing
  1792. ienable: push  es
  1793.          mov   ah,35h
  1794.          mov   al,irqvec
  1795.          int   21h
  1796.          mov   word ptr oldvec,bx
  1797.          mov   word ptr oldvec+2,es
  1798.          pop   es
  1799.  
  1800.          mov   ah,25h
  1801.          mov   al,irqvec
  1802.          lea   dx,mpuintr
  1803.          int   21h
  1804.  
  1805.          in    al,21h
  1806.          mov   oldmask,al
  1807.  
  1808.          mov   ah,1
  1809.          mov   cl,irq
  1810.          shl   ah,cl
  1811.          not   ah
  1812.          and   al,ah
  1813.          out   21h,al
  1814.          ret
  1815.  
  1816. ; --------
  1817. ; idisable - Disable Interrupt Processing
  1818. idisable:push  ds
  1819.          mov   ah,25h
  1820.          mov   al,irqvec
  1821.          lds   dx,oldvec
  1822.          int   21h
  1823.          pop   ds
  1824.          mov   al,oldmask
  1825.          out   21h,al
  1826.          ret
  1827.  
  1828. ; ------
  1829. ; chkmpu - Schauen, ob MPU401 vorhanden ist.
  1830. chkmpu : mov   dx,0330h
  1831.          mov   mpuio,dx
  1832.          in    al,dx
  1833.          cmp   al,-1
  1834.          jnz   chkmpu10
  1835.  
  1836.          mov   dx,0300h
  1837.          mov   mpuio,dx
  1838.          in    al,dx
  1839.          cmp   al,-1
  1840.          jnz   chkmpu10
  1841.  
  1842.          lea   si,msg_nompu
  1843.          jmp   err_exit
  1844.  
  1845. chkmpu10:lea   si,msg_mpuio
  1846.          call  wstring
  1847.          mov   ax,mpuio
  1848.          call  hexw
  1849.  
  1850.          call  resetmpu
  1851.  
  1852. ; -----
  1853. ; Jetzt kommt der Interruptvektoren Test
  1854.  
  1855.          ; ---
  1856.          ; wir testen alle möglichen IRQ's
  1857.  
  1858.          lea   si,irqtab
  1859.          lea   di,vectab
  1860.  
  1861. chkmpu20:lodsb
  1862.          cmp   al,-1
  1863.          jnz   chkmpu30
  1864.  
  1865.          lea   si,msg_nointr
  1866.          jmp   err_exit
  1867.  
  1868. chkmpu30:mov   irq,al
  1869.          mov   ah,[di]
  1870.          inc   di
  1871.          mov   irqvec,ah
  1872.  
  1873.          call  ienable
  1874.  
  1875.          mov   intcnt,0
  1876.  
  1877.          mov   al,89h          ; MIDI thru ON
  1878.          call  put_cmd
  1879.          mov   al,0b8h         ; Clear Play Counter
  1880.          call  put_cmd
  1881.  
  1882.          mov   al,0ach         ; Requests Version
  1883.          call  put_cmd
  1884.  
  1885.          xor   cx,cx
  1886. chkmpu50:xchg  al,ah
  1887.          xchg  al,ah
  1888.          loop  chkmpu50
  1889.  
  1890.          call  idisable
  1891.  
  1892. chkmpu60:cmp   intcnt,0
  1893.          jnz   chkmpuex
  1894.  
  1895.          jmp   chkmpu20
  1896.  
  1897. chkmpuex:lea   si,msg_intr
  1898.          call  wstring
  1899.          mov   al,irq
  1900.          call  hexb
  1901.          lea   si,msg_cr
  1902.          call  wstring
  1903.          ret
  1904.  
  1905. ; -----
  1906. ; testi - Testen, ob bereits installiert
  1907. testi  : push  es
  1908.          les   di,dword ptr semaphor
  1909.          mov   ax,-1
  1910.          scasw
  1911.          jnz   testi10
  1912.  
  1913.          lea   si,msg_already
  1914.          call  werror
  1915.          jmp   ende100
  1916.  
  1917. testi10: sub   di,2
  1918.          stosw
  1919.  
  1920. testi_ex:pop   es
  1921.          ret
  1922.  
  1923. ; -------
  1924. semi_ex: pusha
  1925.          push  es
  1926.          les   di,dword ptr semaphor
  1927.          xor   ax,ax
  1928.          stosw
  1929.          pop   es
  1930.          popa
  1931.          ret
  1932.  
  1933. ; ------
  1934. ; anfang
  1935. anfang : cld
  1936.          mov   sp,STACK_ADDR - 2
  1937.          mov   ax,0f00h
  1938.          int   10h
  1939.          mov   ah,0
  1940.          int   10h
  1941.          lea   si,msg_hello
  1942.          call  werror
  1943.  
  1944.          call  testi
  1945.  
  1946.          call  parscmd         ; Kommandozeile durchforsten
  1947.          call  chkmpu          ; Schauen, ob MPU 401 da ist.
  1948.          call  fileloop        ; Und alle Dateien abgrasen
  1949.  
  1950.  
  1951. ende   : call  semi_ex
  1952. ende100: mov   ah,4ch
  1953.          int   21h
  1954.  
  1955. ALIGN 16
  1956.  
  1957. filename       label byte
  1958.  
  1959. code     ends
  1960.          end   start
  1961.