| 1 | #include "sbsound.h"
|
|---|
| 2 |
|
|---|
| 3 | /*
|
|---|
| 4 | Subj: Re:AdamS60982
|
|---|
| 5 | Date: 94-04-17 20:38:07 EDT
|
|---|
| 6 | From: AdamS60982
|
|---|
| 7 |
|
|---|
| 8 | I couldn//t really recommend any SB books since I don//t have any. :)
|
|---|
| 9 | About the FM, the following example is the best I could find that uses the
|
|---|
| 10 | information found in the SB
|
|---|
| 11 | SBSOUND.BAS by Brett Levin 1992
|
|---|
| 12 |
|
|---|
| 13 | These routines were made entirely from a pretty detailed(techie, but
|
|---|
| 14 |
|
|---|
| 15 | not that I mind <G>) text file on programming the FM ports on the AdLib/SB.
|
|---|
| 16 |
|
|---|
| 17 | You are free to use this in any program what so ever, as long as you
|
|---|
| 18 |
|
|---|
| 19 | give credit where credit is due.. (stole that line from Rich!) :)
|
|---|
| 20 | */
|
|---|
| 21 |
|
|---|
| 22 | //DEFINT A-Z
|
|---|
| 23 | int DetectCard(void); //DECLARE FUNCTION DetectCard% ()
|
|---|
| 24 | void SBInit(void); //DECLARE SUB SBInit ()
|
|---|
| 25 | void WriteReg(short Reg, short Value); //DECLARE SUB WriteReg (Reg%, Value%)
|
|---|
| 26 | void SBPlay(short freq); //DECLARE SUB SBPlay (freq%)
|
|---|
| 27 | uint8_t inb(uint16_t port);
|
|---|
| 28 | void outb(uint16_t port, uint8_t val);
|
|---|
| 29 | extern void puts(char * str);
|
|---|
| 30 |
|
|---|
| 31 | //CONST false = 0, true = NOT false
|
|---|
| 32 |
|
|---|
| 33 | //SCREEN 0: CLS
|
|---|
| 34 |
|
|---|
| 35 | void SoundTest(void){
|
|---|
| 36 | /*
|
|---|
| 37 | IF DetectCard = true THEN
|
|---|
| 38 | PRINT "AdLib-compatible sound card detected."
|
|---|
| 39 | ELSE
|
|---|
| 40 | PRINT "Unable to find/detect sound card."
|
|---|
| 41 | BEEP
|
|---|
| 42 | SYSTEM
|
|---|
| 43 | END IF
|
|---|
| 44 | PRINT " Initalizing..."
|
|---|
| 45 | */
|
|---|
| 46 |
|
|---|
| 47 | /*
|
|---|
| 48 | if (DetectCard()){
|
|---|
| 49 | puts("AdLib-compatible sound card detected.\r\n\0");
|
|---|
| 50 | }
|
|---|
| 51 | else
|
|---|
| 52 | {
|
|---|
| 53 | puts("Unable to find/detect sound card.\r\n\0");
|
|---|
| 54 | //beep();
|
|---|
| 55 | return;
|
|---|
| 56 | }
|
|---|
| 57 |
|
|---|
| 58 |
|
|---|
| 59 | puts(" Initalizing...\r\n\0");
|
|---|
| 60 | SBInit();
|
|---|
| 61 | puts(" Done.\r\n\0");
|
|---|
| 62 | */
|
|---|
| 63 |
|
|---|
| 64 | for(int nt = 0; nt<256; ++nt){ //FOR nt = 0 TO 255
|
|---|
| 65 | SBPlay(nt);
|
|---|
| 66 | } //NEXT nt
|
|---|
| 67 |
|
|---|
| 68 | puts("\r\nThese routines only support one channel/voice of the FM chip, but\r\n\0");
|
|---|
| 69 | puts("eventually I may fix them so you can have a bunch o'instruments on\r\n\0");
|
|---|
| 70 | puts("at once. I'd also like to write a replacement for SBFMDRV.COM, but\r\n\0");
|
|---|
| 71 | puts("that's far off, and probably not in QB anyway. This is too fast\r\n\0");
|
|---|
| 72 | puts("compiled, so if you are going to use it in anything, add a delay.\r\n\0");
|
|---|
| 73 | puts(" Enjoy! -Brett 11 / 12 / 92\r\n\0");
|
|---|
| 74 |
|
|---|
| 75 |
|
|---|
| 76 | for(int nt = 255; nt>-1; --nt){ //FOR nt = 255 TO 0 STEP -1
|
|---|
| 77 | SBPlay(nt);
|
|---|
| 78 | } //NEXT nt
|
|---|
| 79 |
|
|---|
| 80 | // puts("[Press any key to end]\r\n\0");
|
|---|
| 81 | //SLEEP
|
|---|
| 82 |
|
|---|
| 83 | WriteReg(0xB0, 0x0); //Makes sure no extra sound is left playing
|
|---|
| 84 |
|
|---|
| 85 | return;
|
|---|
| 86 | }
|
|---|
| 87 |
|
|---|
| 88 |
|
|---|
| 89 | int DetectCard(void){
|
|---|
| 90 |
|
|---|
| 91 | // Purpose: Detects an AdLib-compatible card.
|
|---|
| 92 | // Returns -1 (true) if detected and 0 (false) if not.
|
|---|
| 93 | // Variables: Nope
|
|---|
| 94 |
|
|---|
| 95 | WriteReg(0x4, 0x60);
|
|---|
| 96 | WriteReg(0x4, 0x80);
|
|---|
| 97 |
|
|---|
| 98 | int B = inb(0x388);
|
|---|
| 99 |
|
|---|
| 100 | WriteReg(0x2, 0xFF);
|
|---|
| 101 | WriteReg(0x4, 0x21);
|
|---|
| 102 |
|
|---|
| 103 | for (int x = 0; x <= 130; ++x){ // FOR x = 0 TO 130
|
|---|
| 104 | int a = inb(0x388);
|
|---|
| 105 | } // NEXT x
|
|---|
| 106 |
|
|---|
| 107 | int C = inb(0x388);
|
|---|
| 108 |
|
|---|
| 109 | WriteReg(0x4, 0x60);
|
|---|
| 110 | WriteReg(0x4, 0x80);
|
|---|
| 111 |
|
|---|
| 112 | if ((B & 0xE0) == 0x00) {
|
|---|
| 113 | if ((C & 0xE0) == 0xC0) {
|
|---|
| 114 | return -1;
|
|---|
| 115 | }
|
|---|
| 116 | }
|
|---|
| 117 |
|
|---|
| 118 | return 0;
|
|---|
| 119 | }
|
|---|
| 120 |
|
|---|
| 121 | void SBInit(void){
|
|---|
| 122 | // Initialize the sound card
|
|---|
| 123 | //(This is the "quick-and-dirty" method; what it//s doing is zeroing out
|
|---|
| 124 | // all of the card//s registers. I haven//t had any problems with this.)
|
|---|
| 125 |
|
|---|
| 126 | for(int q = 0; q < 0xF6; ++q){ //FOR q = 1 TO &HF5
|
|---|
| 127 | WriteReg(q, 0);
|
|---|
| 128 | } //NEXT q
|
|---|
| 129 |
|
|---|
| 130 | return;
|
|---|
| 131 | }
|
|---|
| 132 |
|
|---|
| 133 | void WriteReg(short Reg, short Value){
|
|---|
| 134 | // Purpose: Writes to any of the SB/AdLib//s registers
|
|---|
| 135 | // Variables: Reg%: Register number,
|
|---|
| 136 | // Value%: Value to insert in register
|
|---|
| 137 | // (Note: The registers are from 00-F5 (hex))
|
|---|
| 138 |
|
|---|
| 139 | outb(0x388, Reg); //388h = address/status port, 389h = dataport
|
|---|
| 140 | for(int x = 0; x <= 5; ++x){ // This tells the SB what register we want to write to
|
|---|
| 141 | inb(0x388); // After we write to the address port we must wait 3.3ms
|
|---|
| 142 | }
|
|---|
| 143 |
|
|---|
| 144 | outb(0x389, Value); // Send the value for the register to 389h
|
|---|
| 145 | for(int x = 0; x <= 34; ++x){ // Here we must also wait, this time 23ms
|
|---|
| 146 | inb(0x388);
|
|---|
| 147 | }
|
|---|
| 148 |
|
|---|
| 149 | return;
|
|---|
| 150 | }
|
|---|
| 151 |
|
|---|
| 152 | void SBPlay(short freq){
|
|---|
| 153 | // Purpose: Plays a note
|
|---|
| 154 | // Variables: freq% - Frequency (00-FF hex)
|
|---|
| 155 | // duration% - Duration (n seconds) (not used)
|
|---|
| 156 | // I//m still working on this part, it may be ugly, but it works <g>.
|
|---|
| 157 |
|
|---|
| 158 | // The first group of WriteRegs is the modulator, the second is the
|
|---|
| 159 |
|
|---|
| 160 | // carrier.
|
|---|
| 161 | // If you just want to know how to create your own instrument, play around
|
|---|
| 162 |
|
|---|
| 163 | // with the second values in the first four calls to WriteReg in each group.
|
|---|
| 164 |
|
|---|
| 165 | // :-) Have fun! - Brett
|
|---|
| 166 |
|
|---|
| 167 | WriteReg(0x20, 0x7); // Set modulator's multiple to F
|
|---|
| 168 | WriteReg(0x40, 0xF); // Set modulator's level to 40 dB
|
|---|
| 169 | WriteReg(0x60, 0xF0); // Modulator attack: quick, decay: long
|
|---|
| 170 |
|
|---|
| 171 | WriteReg(0x80, 0xF0); // Modulator sustain: medium, release: medium
|
|---|
| 172 | WriteReg(0xA0, freq);
|
|---|
| 173 |
|
|---|
| 174 |
|
|---|
| 175 | WriteReg(0x23, 0xF); // Set carrier's multiple to 0
|
|---|
| 176 | WriteReg(0x43, 0x0); // Set carrier's level to 0 dB
|
|---|
| 177 | WriteReg(0x63, 0xF0); // Carrier attack: quick, decay: long
|
|---|
| 178 |
|
|---|
| 179 | WriteReg(0x83, 0xFF); // Carrier sustain: quick, release: quick
|
|---|
| 180 | WriteReg(0xB0, 0x20); // Octave
|
|---|
| 181 |
|
|---|
| 182 | WriteReg(0xE0, 0x0); // Waveform argument for Tom..
|
|---|
| 183 | // &H00 is the default, but I felt like
|
|---|
| 184 | // dropping it in for you.. :)
|
|---|
| 185 |
|
|---|
| 186 | // I originally had an extra argument, duration!, but for some reason
|
|---|
| 187 | // I wanted to do the timing outside of this sub.. You can change it back
|
|---|
| 188 |
|
|---|
| 189 | // if needs require..
|
|---|
| 190 |
|
|---|
| 191 | //TimeUp! = TIMER + duation!
|
|---|
| 192 | //WHILE TimeUp! > TIMER: WEND // Worst you can be off is .182 of a second
|
|---|
| 193 |
|
|---|
| 194 | return;
|
|---|
| 195 |
|
|---|
| 196 | }
|
|---|
| 197 |
|
|---|
| 198 | uint8_t inb(uint16_t port)
|
|---|
| 199 | {
|
|---|
| 200 | uint8_t ret;
|
|---|
| 201 | __asm__ volatile ( "inb %1, %0"
|
|---|
| 202 | : "=a"(ret)
|
|---|
| 203 | : "Nd"(port) );
|
|---|
| 204 | return ret;
|
|---|
| 205 | }
|
|---|
| 206 |
|
|---|
| 207 | void outb(uint16_t port, uint8_t val)
|
|---|
| 208 | {
|
|---|
| 209 | __asm__ volatile ( "outb %0, %1" : : "a"(val), "Nd"(port) );
|
|---|
| 210 | /* There's an outb %al, $imm8 encoding, for compile-time constant port numbers that fit in 8b. (N constraint).
|
|---|
| 211 | * Wider immediate constants would be truncated at assemble-time (e.g. "i" constraint).
|
|---|
| 212 | * The outb %al, %dx encoding is the only option for all other cases.
|
|---|
| 213 | * %1 expands to %dx because port is a uint16_t. %w1 could be used if we had the port number a wider C type */
|
|---|
| 214 | }
|
|---|