home *** CD-ROM | disk | FTP | other *** search
/ C!T ROM 2 / ctrom_ii_b.zip / ctrom_ii_b / C!T / C!T03_92 / COVOX / COVOX.LIS
File List  |  1992-03-30  |  19KB  |  324 lines

  1. door Thijs Schoonbrood
  2.  
  3. Hardware Project: Bouw zelf een Covox!
  4.  
  5. ┌─────────────────────────────────────────────────────────┐
  6. │ In dit bestand vindt u een uitgebreidere verhandeling   │
  7. │ over het programmeren met de Covox zelfbouw sound       │
  8. │ interface, zoals daarover is geschreven in Computer!    │
  9. │ Totaal nummer 3. Mocht u nog vragen hebben, zendt dan   │
  10. │ een briefje aan de redactie. Wij zorgen dan voor        │
  11. │ doorzending van uw brief aan de auteur.                 │
  12. └─────────────────────────────────────────────────────────┘
  13.  
  14. JAAaaaa... Programmeren !!
  15.  
  16. Voor er geprogrammeerd zal worden moet ik U vertellen dat het volgende geen makkelijke stof is. Programmeren is echt niet moeilijk, maar voor dit stuk van het artikel is wel gedegen kennis van assemblerprogrammering vereist. Info over assembler is te bekomen bij Methode Vanderaart.
  17.  
  18. Ik zal eerst uitleggen hoe geluidsgolven eruit 'zien'. Zoals U waarschijnlijk reeds weet is geluid niets anders dan trillende lucht. U kunt de klankkleur beinvloeden door de
  19. trillingen anders te laten verlopen. Zo kennen we b.v. de sinus-, de zaagtand-, de puls- en de driehoeksgolf. De frequentie is het omgekeerde van de tijd die nodig is om 1 volledige golf te 'doorlopen'. Als je dus een frequentie van 1000Hz hebt, is de tijd die nodig is om 1 volledige sinusgolf af te spelen 0.001 seconde.
  20.  
  21. Behalve frequentie heeft elke toon ook nog een volume. Het volume van een bepaald geluid is in feite niets anders dan het verschil tussen de maximale en de minimale waardes. Als U de interface steeds laat switchen tussen 0FFh en 000h, krijgt U de luidste toon die de interface kan genereren. (Het verschil is immers 0FFh, de maximale waarde voor 8 bitten.) Pendelen tussen 07F en 000h levert een toon met halve geluidssterkte op.
  22.  
  23. Hoe stelt een programma een bepaalde spanning in? Simpel, door gewoon de gewenste waarde met een OUT naar de poort te sturen. Regel: Hoe hoger de verzonden waarde, hoe hoger de spanning. Wat is de juiste poort ? Dat hangt er vanaf. Het onderstaande schema zou duidelijkheid moeten scheppen:
  24.  
  25. Printerpoort:            |    I/O Adres
  26. ----------------------------------------------
  27. Printerpoort op MDA kaart    |    003BCh
  28. Printerpoort #1            |    00378h
  29. Printerpoort #2            |    00278h
  30.  
  31. In feite zou U nu al wat geluid moeten kunnen produceren, alleen zit U dan met het probleem van de timing. Hoe krijg ik de computer zover dat hij bijvoorbeeld 1000 keer per seconde de waarde op de printerpoort verandert? Omdat er veel verschillende computers in omloop zijn met elk een aparte kloksnelheid kunnen we dus geen wachtlussen inbouwen.
  32.  
  33. Dat wordt anders opgelost, en wel met de timerchip.
  34. Eigenlijk is het niet helemaal volgens het boekje, omdat dat niet op alle computers werkt. Op 95% van de conputers werkt het echter wel, en het is de enige manier om goed met de interface om te gaan. Of het bij U werkt? Weet ik niet, hoogstwaarschijnlijk wel, maar U zult het even moeten proberen...
  35.  
  36. De timerchip valt zo te programmeren dat er 1000
  37. interrupts per seconde genereert worden. Als U bijvoorbeeld de interrupthandler de waarde op de poort laat afwisselen tussen 000h en 0FFh, dan krijgt U een pulsgolf met een frequentie van 500Hz en maximaal volume.
  38.  
  39. Omdat goede documentatie over de timerchip schaars is, zet ik het nog even op een rijtje voor de wat minder bedeelden wat documentatie betreft. 
  40.  
  41. De timerchip bezit 3 counters die aftellen naar nul. Bij iedere nul-doorgang veroorzaakt de chip een bepaalde actie. Counter 0 zorgt voor een IRQ 0. (De overige counters zijn hier niet van toepassing. Counter 1 dient voor de refresh en counter 2 is voor de interne speaker) De processor antwoordt op een IRQ 0 met een INT 8h. Het is zaak die INT 8h af te vangen en onze eigen routine te installeren. Let op! De oude INT 8h wordt niet na afloop van onze routine aangeroepen. Dat heeft als gevolg dat de klok stil blijft staan. Jammer voor de klok, want het gaat te diep om ook nog uit te leggen hoe die klok toch kan doorlopen. (Het gaat hier overigens niet om een middeltje tegen ouder worden, hoor!) Verder kan het ook zijn dat hardware intensieve TSR's niet meer naar behoren werken. De oude INT 8h met de juiste frequentie (18.2Hz) aanroepen kan soelaas bieden. Bedenk echter wel dat U niet weer hoelang de orginele INT 8h duurt. Als deze te lang is kan het zijn dat er 'tikken' te horen zijn. Mensen die toch een oplossing hebben gevonden nodig ik uit om eens een diskje met de source daarop op te sturen. Op het einde van de nieuwe INT 8h wordt de waarde 020h naar poort 020h gestuurd, zodat de interruptcontroller weet dat de interrupt afgehandeld is.
  42.  
  43. Normaal wordt INT 8h 18.2 keer per seconde aangeroepen door de timerchip. Dat is natuurlijk veel te langzaam voor geluid. Hoe de frequentie te veranderen? De timerchip verlaagt 1193180 keer per seconde de counters. Counter 0 wordt door de BIOS na de POST (=Power On Self Test) geinitialiseerd op 0FFFFh. 1193180/0FFFFh=18.2. U ziet dat 18.2Hz de laagste frequentie is die U kunt instellen, aangezien 0FFFFh de hoogste waarde is die in een 16-bits counter past. Nieuwe waardes voor de timer worden dus alsvolgt berekend: Waarde=1193180/Frequentie. Bij een
  44. frequentie van 1000 Ints/Sec levert dat dus 1193 op. Op een 4.77Mhz PC mag U maximaal 4 keer de frequentie aan kloktikken gebruiken per interrupthandler. Nu is dat dus 4772. Nooit alles uit de kan halen, want dan staat het proces op de voorgrond bijna stil! In principe kan U op een 8Mhz PC meer kloktikken gebruiken, maar dan werkt uw programma niet meer op een normale, slome PC. Probeer dat dus zoveel mogelijk te vermijden. Heeft uw programma een 12Mhz of snellere processor nodig? Dan kunt U er donder op zeggen dat het programma op een 286 of beter draait. Maak dan ook gebruik van de extra instructies die U tot uw beschikking hebt, dat scheelt ook weer een fikse slok op de borrel. (Of de
  45. interrupthandler, zo U wilt.)
  46.  
  47. Als U de waarde voor de timerchip berekend heeft moet die natuurlijk aan de timerchip worden doorgegeven. Moeilijk ? Welnee. U dient eerst de chip te vertellen dat er een nieuwe counter-waarde gestuurd gaat worden. Via IO-poort 043h voorziet U de chip van commando's. Een commando-byte ziet er alsvolgt uit:
  48.  
  49. 7    6    5    4    3    2    1    0
  50. SC1    SC0    RL1    RL0    M2    M1    M0    BCD
  51.  
  52. SC1 & SC0 bepalen welke counter U van een commando wilt voorzien. Beide bitjes worden op 0 gezet, omdat we alleen counter 0 willen wijzigen. RL1 & RL0 zet U op 1, zodat eerst de lage en daarna de hoge byte wordt gestuurd. (De andere mogelijkheden zijn nu niet van belang.) M2, M1 & M0 kiezen de mode uit waarin de timer werkt. Mode 3 moet U hebben. M2 wordt dus 0, M1 & M0 worden 1. BCD bepaalt of de teller in BCD-mode werkt. Hier moet dat niet. Ook BCD op 0 dus. Dat levert dan het volgende bit-patroon op: 00110110b=036h.
  53.  
  54. Nadat de commando-byte via poort 043h naar de timerchip is gestuurd zal deze ook van de nieuwe tellerwaarde kennis moeten nemen. Voor counter 0 moet dat op IO-poort 040h. Eerst het lage deel van het 16-bits getal, daarna het hoge.
  55.  
  56. Het wordt tijd dat we na deze droge theorie eens wat praktisch gaan doen. Stel dat we een 1000Hz pulsgolf te
  57. voorschijn willen toveren. Hoe te doen? Eerst de frequentie op 2000Hz instellen. Ja, U leest het goed, 2000Hz en geen 1000. De interrupt moet immers 2x per geluidsperiode worden aangeroepen. Een keer om het signaal 'hoog' (b.v. 0FFh voor maximaal volume) te maken, en daarna nog eens om het signaal weer op 0 te brengen. In het programma stelt SetFreq de frequentie van de interrupts in. (Zie voorbeeld #1)
  58.  
  59. Daarna moeten we een eigen interrupthandler gaan
  60. installeren. InstallInt doet dat in het voorbeeld. Assembleer het programma, link het en maak er een .COM-file van. Start die maar eens op. Nadat de nieuwe interrupt operationeel is zal er geluid uit de speakers van uw versterker klinken, mits U alles goed heeft aangesloten en het ding aanstaat. Dat geluid zal klinken totdat U op een toets drukt. U kunt het programma ook zo wijzigen dat de toon een bepaalde tijdsduur krijgt, maar dat laat ik aan de creatieve geesten onder de lezers over. Uiteraard zijn ook hier reacties welkom.
  61.  
  62. Natuurlijk is een pulsgolf niet echt wat we willen, want dat kan zelfs het interne speakertje van een PC. Stel dat we een sinusgolf willen hebben, dan wordt het al wat lastiger. Omdat een sinus moeilijk real-time te berekenen is maken we gewoon een tabel met daarin een periode van een sinusgolf. Door nu een programma te schrijven dat zo'n tabel kan 'afspelen' wordt het in principe mogelijk om elk soort waveform te gebruiken, U hoeft immers alleen de tabel te wijzigen.
  63.  
  64. Dat is gedaan in voorbeeld #2. Het programma voorziet in een puls-, sinus-, zaagtand en driehoeksgolven. Alle tabellen bestaan uit 64 bytes. Als U een toon van 200Hz wilt genereren, moet U de interruptfrequentie dus op 64*200=12800 instellen. Let bij trage computers op dat U de frequentie niet te hoog maakt. Als de processor een interrupt aan het afhandelen is en de timerchip staat alweer klaar staat met de volgende kunnen er problemen ontstaan. De interrupthandler heeft nu zo'n 300
  65. kloktikken nodig op 'n 8088. (Op AT's beduidend minder nodig.) De maximale frequentie van de interrupts is dus 4770000/300=15900. Altijd wat tijd voor de voorgrond overlaten, dus niet hoger gaan dan 15000 ints/sec. Dat geeft dan een maximale geluidsfrequentie van 15000/64=234. Dat is natuurlijk veel te laag voor een leuk melodietje. De frequentie is op verschillende manieren te
  66. verhogen. U kunt proberen de interrupthandler nog wat sneller te maken, en dat kan zekers! 
  67.  
  68. Probeer maar eens een 8-bits tellertje als pointer in de waveform. Ook zou U ervoor kunnen zorgen dat de registers die de handler gebruikt niet op de voorgrond gebruikt worden, zodat de handler ze niet steeds hoeft te pushen en poppen. Als U dat doet kunt U als pointer voor het waveform ook een register gebruiken. Om hogere tonen te krijgen kunt U natuurlijk ook gewoon van een 10Mhz AT uitgaan, maar mensen met een PCtje zullen dat niet waarderen, en het getuigt m.i. niet van vindingrijkheid en programmeerkunst! Beter is om een kleiner waveform te pakken. 16 bytes voor een zaagtand is eigenlijk ook genoeg bij hoge
  69. frequenties. Als de frequentie lager wordt dan 234 schakelt U gewoon weer terug naar 64 bytes. Verder zijn de waveforms
  70. 'Zaagtand' en 'Driehoek' makkelijk realtime te berekenen, want ook weer de nodige snelheidswinst oplevert.
  71.  
  72. Toontjes afspelen is natuurlijk wel leuk, maar
  73. gedigitaliseerde muziek afspelen is nog veel leuker. De hier besproken interface is niet geschikt om te digitaliseren, maar hij kan wel samples afspelen. Nu heb ik wel een interface die geschikt is om te digitaliseren, en laat ik nu de moeite hebben genoemen om een aantal drums te samplen voor U...
  74.  
  75. De samples zijn gedigitaliseerd met een frequentie van 10Khz. Dat is eigenlijk een vrij lage samplerate, maar alle samples moesten passen in 64K, vandaar. Ik heb alle 25 drums onder een mooi drumprogramma, dat onder de Organizer van John Vanderaart draait, gehangen. Onderaan dit artikel kunt U zien waar dat programma te downloaden is. 
  76.  
  77. Het gaat te ver om hier de hele listing af te drukken, maar ik kan wel uitleggen hoe dat programma ongeveer werkt. Eigenlijk is het bijna het zelfde programma als voorbeeld #2, maar dan met veel meer - en grotere - waveforms. Die waveforms worden met een vaste frequentie van 10000 bytes/sec naar de interface gestuurd. De samples worden aangewezen door een paar pointers in een tabel, zodat samples van verschillende lengten mogelijk zijn. Welke sample ten gehore wordt gebracht hangt af van de toets die U indrukte. Eerst wordt die toets omgezet naar een hoofdletter. Daarna wordt de tabel met pointers geindexeerd aan de hand van de ASCII waarde van de toets. De interrupthandler begint met afspelen, totdat het einde van de sample bereikt is. Leuk toch?
  78.  
  79. Dit programma is inclusief source en Organizer te
  80. downloaden is bij: "Roel's Praathoek", 077-547521. Alle
  81. baudrates, tot 14400. Als U voor de eerste keer inlogt, kunt U al downloaden. Vanuit het hoofdmenu gaat U naar het softwaremenu met de 'S'. Daarna drukt op op '*' om van filearea te wisselen. Area 10 moet U hebben. Met de 'F' van Files krijgt U de inhoudsopgave op uw scherm. Zoek de files uit die U wilt hebben, en druk daarna op de 'D' van Download. Pak het correcte protocol, en downloaden maar!
  82.  
  83. Als er nog mensen zijn die zelf wat hebben programmeerd voor de interface, dan hoor ik daar graag van. NetMail naar: 77:8310/509 voor TDN en 17:786/509 voor ()lympic. Natuurlijk kunt U ook een berichtje achter laten op het eerder genoemde BBS.
  84.  
  85. Succes, Thijs
  86.  
  87. ; ****
  88. ; **** Geprogrammeerd door Thijs Schoonbrood
  89. ; ****
  90.  
  91. Example1        SEGMENT PARA
  92.             ORG 0100h
  93.             ASSUME CS:Example1,DS:Example1
  94.  
  95. StartUp:        jmp SetUp
  96.  
  97. TimerFreq        EQU 1193180
  98. TimerCount0        EQU 040h
  99. TimerComReg        EQU 043h
  100. InterFreq        EQU TimerFreq / 2000
  101. Port            EQU 0378h
  102.  
  103. Txt            DB 10,13,'Een 1000Hz pulsgolf.'
  104.             DB 10,13,'$'
  105. Current            DB 0
  106. Old08            DW 0, 0
  107.  
  108. New08:            push ax
  109.             push dx
  110.             cmp CS:Current,0
  111.             jz New080
  112.             mov CS:Current,0
  113.             mov dx,Port
  114.             xor al,al
  115.             out dx,al
  116.             jmp SHORT New081
  117. New080:            mov CS:Current,1
  118.             mov dx,Port
  119.             mov al,0FFh
  120.             out dx,al
  121. New081:            mov al,020h        ; EOI to PIC
  122.             out 020h,al
  123.             pop dx
  124.             pop ax
  125.             iret
  126.  
  127. SetFreq:        cli
  128.             mov dx,TimerComReg
  129.             mov al,00110110b    ; Command Byte
  130.             out dx,al
  131.             mov dx,TimerCount0
  132.             mov al,cl        ; First LowByte
  133.             out dx,al
  134.             mov al,ch        ; Then HighByte
  135.             out dx,al
  136.             sti
  137.             ret
  138.  
  139. InstallInt:        mov ax,03508h        ; Get current handler
  140.             int 21h
  141.             mov Old08,bx
  142.             mov Old08+2,es
  143.             mov ax,02508h        ; Set our handler
  144.             mov dx,OFFSET New08
  145.             int 21h
  146.             ret
  147.  
  148. RestoreInt:        mov ax,02508h
  149.             mov dx,Old08        ; Restore former
  150.             mov ds,Old08+2        ; handler
  151.             int 21h
  152.             ret
  153.  
  154. SetUp:            mov ah,9        ; Display String
  155.             mov dx,OFFSET Txt
  156.             int 21h
  157.             call InstallInt
  158.             mov cx,InterFreq    ; Set frequency
  159.             call SetFreq
  160.             xor ah,ah        ; Wait for key
  161.             int 16h
  162.             mov cx,0FFFFh        ; Restore original
  163.             call SetFreq        ; frequency
  164.             call RestoreInt
  165.             int 20h            ; Return to DOS
  166.  
  167. Example1        ENDS
  168.             END StartUp
  169.  
  170.  
  171. ; ****
  172. ; **** Geprogrammeerd door Thijs Schoonbrood
  173. ; ****
  174.  
  175. ======================================================
  176.  
  177.  
  178. Example2        SEGMENT PARA
  179.             ORG 0100h
  180.             ASSUME CS:Example2,DS:Example2
  181.  
  182. StartUp:        jmp SetUp
  183.  
  184. TimerCount0        EQU 040h
  185. TimerComReg        EQU 043h
  186. InterFreq        EQU 1193180 / 12800
  187. Port            EQU 0378h
  188. Txt1            DB 10,13,'Voorbeeld #1. Een 200Hz driehoeksgolf.'
  189.             DB '    <TOETS>$'
  190. Txt2            DB 10,13,'Voorbeeld #2. Een 200Hz zaagtandgolf.'
  191.             DB '    <TOETS>$'
  192. Txt3            DB 10,13,'Voorbeeld #3. Een 200Hz sinusgolf.'
  193.             DB '    <TOETS>$'
  194. Txt4            DB 10,13,'Voorbeeld #4. Ruis.'
  195.             DB '    <TOETS>',10,13,'$'
  196. WaveForm        DW 0
  197. TabPtr            DW 0
  198. Old08            DW 0, 0
  199. Driehoek        DB 000, 008, 016, 024, 032, 040, 048, 056
  200.             DB 064, 072, 080, 088, 096, 104, 112, 120
  201.             DB 128, 136, 144, 152, 160, 168, 176, 184
  202.             DB 192, 200, 208, 216, 224, 232, 240, 248
  203.             DB 255, 248, 240, 232, 224, 216, 208, 200
  204.             DB 192, 184, 176, 168, 160, 152, 144, 136
  205.             DB 128, 120, 112, 104, 096, 088, 080, 072
  206.             DB 064, 056, 048, 040, 032, 024, 016, 008
  207. Zaagtand        DB 000, 002, 004, 006, 008, 010, 012, 014
  208.             DB 016, 018, 020, 022, 024, 026, 028, 030
  209.             DB 032, 034, 036, 038, 040, 042, 044, 046
  210.             DB 048, 050, 052, 054, 056, 058, 060, 062
  211.             DB 064, 066, 068, 070, 072, 074, 076, 078
  212.             DB 080, 082, 084, 086, 088, 090, 092, 094
  213.             DB 096, 098, 100, 102, 104, 106, 108, 110
  214.             DB 112, 114, 116, 118, 120, 122, 124, 126
  215. Sinus            DB 127, 139, 152, 164, 176, 187, 198, 208
  216.             DB 217, 225, 233, 239, 244, 249, 252, 253
  217.             DB 254, 253, 252, 249, 244, 239, 233, 225
  218.             DB 217, 208, 198, 187, 176, 164, 152, 139
  219.             DB 127, 115, 102, 090, 078, 067, 056, 046
  220.             DB 037, 029, 021, 015, 010, 005, 002, 001
  221.             DB 000, 001, 002, 005, 010, 015, 021, 029
  222.             DB 037, 046, 056, 067, 078, 090, 102, 115
  223. Noise            DB 247, 051, 132, 033, 223, 006, 121, 243
  224.             DB 081, 196, 176, 225, 076, 159, 211, 171
  225.             DB 119, 246, 056, 245, 141, 248, 033, 115
  226.             DB 254, 049, 175, 170, 095, 019, 208, 108
  227.             DB 118, 205, 145, 117, 024, 242, 162, 230
  228.             DB 035, 201, 250, 115, 045, 062, 229, 135
  229.             DB 213, 219, 232, 187, 196, 058, 068, 074
  230.             DB 192, 207, 014, 129, 102, 012, 027, 064
  231.  
  232. New08:                  push ax                 ; 15
  233.             push bx                 ; 15
  234.             push dx                 ; 15
  235.             mov dx,Port             ;  4
  236.             inc CS:TabPtr           ; 31
  237.             cmp CS:TabPtr,64        ; 21
  238.             jb New080               ;  4 / 16
  239.             mov CS:TabPtr,0         ; 22
  240. New080:                 mov bx,CS:TabPtr        ; 20
  241.             add bx,CS:WaveForm      ; 21
  242.             mov al,CS:[bx]          ; 10
  243.             out dx,al               ; 12
  244.             mov al,020h             ;  4
  245.             out 020h,al             ; 14
  246.             pop dx                  ; 12
  247.             pop bx                  ; 12
  248.             pop ax                  ; 12
  249.             iret                    ; 44
  250.  
  251. SetFreq:        cli
  252.             mov dx,TimerComReg
  253.             mov al,00110110b    ; Command Byte
  254.             out dx,al
  255.             mov dx,TimerCount0
  256.             mov al,cl        ; First LowByte
  257.             out dx,al
  258.             mov al,ch        ; Then HighByte
  259.             out dx,al
  260.             sti
  261.             ret
  262.  
  263. InstallInt:        mov ax,03508h        ; Get current handler
  264.             int 21h
  265.             mov Old08,bx
  266.             mov Old08+2,es
  267.             mov ax,02508h        ; Set my handler
  268.             mov dx,OFFSET New08
  269.             int 21h
  270.             ret
  271.  
  272. RestoreInt:        mov ax,02508h
  273.             mov dx,Old08        ; Restore former
  274.             mov ds,Old08+2        ; handler
  275.             int 21h
  276.             ret
  277.  
  278. SetUp:            call InstallInt
  279.             mov cx,InterFreq    ; Set frequency
  280.             call SetFreq
  281.             mov WaveForm,OFFSET Driehoek
  282.             mov ah,9        ; Display String
  283.             mov dx,OFFSET Txt1
  284.             int 21h
  285.             xor ah,ah        ; Wait for key
  286.             int 16h
  287.             mov WaveForm,OFFSET Zaagtand
  288.             mov ah,9
  289.             mov dx,OFFSET Txt2
  290.             int 21h
  291.             xor ah,ah
  292.             int 16h
  293.             mov WaveForm,OFFSET Sinus
  294.             mov ah,9
  295.             mov dx,OFFSET Txt3
  296.             int 21h
  297.             xor ah,ah
  298.             int 16h
  299.             mov WaveForm,OFFSET Noise
  300.             mov ah,9
  301.             mov dx,OFFSET Txt4
  302.             int 21h
  303.             xor ah,ah
  304.             int 16h
  305.             mov cx,0FFFFh        ; Restore original
  306.             call SetFreq        ; frequency
  307.             call RestoreInt
  308.             int 20h            ; Return to DOS
  309.  
  310. Example2        ENDS
  311.             END StartUp
  312.  
  313.  
  314. ╔════════════════════════════════════════════════╗
  315. ║  In het artikel in Computer! Totaal wordt      ║
  316. ║  melding gemaakt van een drum-programma,       ║
  317. ║  dat gratis beschikbaar wordt gesteld aan      ║
  318. ║  de lezers. Helaas hebben we vastgesteld       ║
  319. ║  dat het programma niet voor 100% vlekkeloos   ║
  320. ║  werkt. Het programma zal pas op bulletin-     ║
  321. ║  boards worden geplaatst als een goed wer-     ║
  322. ║  kende uitvoering beschikbaar is.              ║
  323. ║                                                ║
  324. ╚════════════════════════════════════════════════╝