home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Shareware Supreme Volume 6 #1
/
swsii.zip
/
swsii
/
355
/
APDEVKIT.ZIP
/
AUDIOP.DOC
Wrap
Text File
|
1992-05-12
|
62KB
|
1,602 lines
----==================================----
----====< Audio Port Programming >====----
----==================================----
TABLE OF CONTENTS
1. Audio Port overview
2. Programming Considerations
A. Hardware I/O Addresses
B. Audio Port Interface Controller
C. Command/Data Handshaking
3. Digital Audio programming
A. Sending Micro Commands/Data
B. Reading Micro Data
C. Reading FIFO data
D. Writing FIFO data
E. Micro Commands
4. FM programming.
5. Interrupt processing.
Appendix A - Hardware Ports & States
Appendix B - Example routines
AUDIO PORT OVERVIEW
The Audio Port is a portable audio device designed for
use with laptop or desktop computers. This device, which
attaches to the standard IBM PC parallel port, can record
and playback 22khz mono digital audio. Included with the
Audio Port is a built-in FM synthesis chip, the Yamaha 3812.
The Audio Port supports ADPCM hardware compression, and
decompression, in real-time, at rates up to 22khz. Speci-
fically, the device support 2-to-1 and 4-to-1 compression
ratios.
The programming interface is based upon the standard
parallel port I/O scheme, with a few bits redefined. The
parallel port on the IBM PC uses 3 I/O addresses; one output
data, one input status, and one output control. On certain
IBM AT compatible machines, the data port has been made into
a bi-directional data path for use with network adapters,
and other peripherals. The Audio Port supports this
bi-directional 8 bit data path.
The Audio Port interfaces the FM and microcontroller
chip to the PC via the Audio Port Interface Controller; aka,
the APIC. The following diagram illustrates the data/control
path.
┌──────┐ ┌─┐/│
│ │ ┌────────────────────────┐ │ │ │
│ ├─┐ ┴┌───────┐ ┌─────┐ │ ┌───┤ │ │
│ │p├─────┤ ╞════╡ FM ├──┐ │ │ │ │ │
│ IBM │a├─────┤ │ └─────┘ │ │ │ └─┘\│
│ │r├─────┤Audio │ ┌───────┐│ ┴ │
│ PC/ │a├─────┤Port ╞════╡DAC/ ├┴───┘
│ │l├─────┤Interfc│ │ADC ├────┐
│ AT │l├─────┤Ctlr │ └──┬────┘ ┬ │ ┌─┐
│ │e├─────┤ │ ┌──┴──┐ │ │┌────┤░│
│ │l├─────┤ ╞════╡Micro│ │ └┤ │░│
│ ├─┘ ┬└───────┘ └─────┘ │ └────┤░│
│ │ └────────────────────────┘ └─┘
└──────┘
The APIC handles the actual clocking of data, and
determines the target device, either FM or the micro-
controller. In certain DAC/ADC modes, the APIC will drive
the DAC/ADC without intervention from the microcontroller
through the use of an internal 1k FIFO.
Data sent to the different devices require some
handshaking to guarantee that commands and data do not
overrun. This control is a single bit that is multiplexed
between the FM and Micro.
----====< PROGRAMMING CONSIDERATIONS >====----
H A R D W A R E I / O A D D R E S S E S
On the IBM PC/AT, three parallel ports can be found at
several addresess. For each of the parallel port there are
three I/O ports for communications to the external parallel
devices. The following map defines the three I/O ports,
across the three ranges of addresses.
NAME LPT1 LPT2 MONO DESCRIPTION
┌───────────┬───┬─────┬─────┬─────┬──────────────────┐
│Data Output│R/W│ 378 │ 278 │ 3BC │ write output data│
├───────────┼───┼─────┼─────┼─────┼──────────────────┤
│Status │ R │ 379 │ 279 │ 3BD │ read status/data │
├───────────┼───┼─────┼─────┼─────┼──────────────────┤
│Control │ W │ 37A │ 27A │ 3BE │ control lines │
└───────────┴───┴─────┴─────┴─────┴──────────────────┘
The following illustrations show the actual bit definitions
of the three parallel port I/O addresses:
Data Register Definitions:
D7 D6 D5 D4 D3 D2 D1 D0
┌───────┬────┬────┬────┬────┬────┬────┬────┬────┐
│Data │bit7│bit6│bit5│bit4│bit3│bit2│bit1│bit0│
└───────┴────┴────┴────┴────┴────┴────┴────┴────┘
| | | | | | | |
+----+----+----+----+----+----+----+-- Data lines
Status Register Definitions:
D7 D6 D5 D4 D3 D2 D1 D0
┌───────┬────┬────┬────┬────┬────┬────┬────┬────┐
│Status │ W │IRQ │PAR │SRQ │ DA │ X │ X │ X │
└───────┴────┴────┴────┴────┴────┴────┴────┴────┘
| | | | | | | |
| | | | | +----+----+-- unused
| | | | +----------------- Data Available
| | | +---------------------- SRQ/D4/D0
| | +--------------------------- PAR/D5/D1
| +-------------------------------- IRQ/D6/D2
+------------------------------------- WAIT/D7/D3
D7 - Wait status bit. It presents the
command/data register full for either
the FM or Micro registers, depending
on the control register state.
D6 - IRQ is pending bit. Same as the
parallel port definition.
D5 - Input Data Enabled.
D4 - Service ReQuest. This bit is used
by the digital audio FIFO to indicate
if the FIFO can take more data.
D3 - Data is available from the Micro or
FIFO. FM never sends data to the PC.
When the control register is in a read
state, D4-D7 become input lines presenting
each nibble of the 8 bit byte. During the
control port read idle state, the low nibble
is presented. During a read data state, the
high nibble is presented.
Control Register Definitions:
D7 D6 D5 D4 D3 D2 D1 D0
┌───────┬────┬────┬────┬────┬────┬────┬────┬────┐
│Control│PAR │PAR │PAR │IRQ │ AS │MFM │ DS │ RW │
└───────┴────┴────┴────┴────┴────┴────┴────┴────┘
| | | | | | | |
| | | | | | | +-- Read(0)/Write(1)
| | | | | | +------- Data Strobe
| | | | | +------------ Micro/FM select
| | | | +----------------- Address Strobe
| | | +---------------------- IRQ Enable
+----+----+--------------------------- Parallel Input
D0-D3. These lines control the clocking to
the API . It is best to view these
four lines as control words with 16
possible states. A list of the control
states follows.
D4 When this bit is set to 1, interrupts
are allowed to be pass to the 8259
interrupt controller on the mother-
board. Using interrupts from the
parallel port is never advised. It is
best to a faster system timer to
perform interrupt duties.
D5-D7 With these bits high, some bi-
directional parallel ports become
active, that is, allow for 8 bit data
to be read from the data port.
The DATA port is used for sending data/commands to the
Audio Port. In cases where the computer supports bi-
directional data transfers, data can be read from the Audio
Port via this register. IBM did not design the parallel port
to be bi-directional, so in most cases, this feature will
not be used.
A U D I O P O R T I N T E R F A C E C O N T R O L L E R
It is important to understand the concepts of talking to
the APIC. This device is the front-end to the separate
pieces of the Audio Port. The PC software is responsible for
creating the proper handshaking with the APIC. As implied by
it's name, there are specific hardware states for driving
either FM, or digital audio. The following is a list of all
the possible states for the Audio Port:
Value State Name Function
00h RdFMIdle Read FM Idle
01h WrFMIdle Write FM Idle
02h invalid invalid state
03h WrFMData Write FM Data
04h RdIdle Read Sound Idle
05h WrIdle Write Sound Idle
06h RdSndData Read Sound Data
07h WrSndData Write Sound Data
08h RdFMStat Get FM Status
09h WrFMAddr Write FM Address
0Ah invalid invalid state
0Bh invalid And another Reset
0Ch RdSndStat Read Sound Status
0Dh WrSndCmd Write Sound Command
0Eh invalid invalid state
0Fh WrReset2 Write Reset
The four low order bits in the Control register
determine the state of the interface. Notice that some of
the states are invalid. Never assume that setting an invalid
state is harmless! An invalid state may present certain
clocking signals that may, for instance, load PCM data into
the FM index register. Other transitions from or to an
invalid state may have undetermined effects on the Audio
Port.
The actual APIC logic requires the PC to program the
Audio Port in a concise manner. Transitions from idle to
active, then back to idle states, must be strictly adhered
to, or unpredictable results may occur. The basic programm-
ing convention is to leave the control port in Write Idle
mode when idle. This way, another state can be transistioned
in and out. Another benefit of leaving the control port in
Write Idle is that the status register is switched to status
mode, not read nibble mode; therefore, all status bits are
available for reading. For more info, see the above
description of the Status Port.
The following are simple state flow diagrams. Reading
from left to right, these diagrams show the various legal
states for writing commands or data, or just reading data.
The I/O to the data port and status ports are not shown.
TO RESET THE AUDIO PORT:
WrIdle__>___ WrReset2 __>________________________ WrIdle
TO WRITE COMMANDS/DATA TO THE MICRO:
_ WrSndCmd __>_____________________
WrIdle__>_/ \__ WrIdle
\_ WrSndData __>_____________________/
TO READ MICRO DATA:
WrIdle__>_ RdIdle__>_ RdSndData__>_ RdIdle__>_____ WrIdle
TO WRITE INDEX/DATA TO THE FM CHIP:
_ WrFMAddr _
WrIdle__>_ WrFMIdle__/ \__ WrFMIdle__>_ WrIdle
\_ WrFMData _/
TO READ FM STATUS:
WrIdle__>_ RdFMIdle__>_ RdFMStat__>_ RdFMIdle__>__ WrIdle
A typical sequence to send a PCM data byte would be:
mov dx,[PortAddress] ; get the base address.
add dl,_CONTROL ; move to the control port.
mov al,WrIdle ; set to Write Idle mode.
out dx,al
Idledly
sub dl,_CONTROL ; move to the data port.
mov al,[DataByte] ; port, then send the
out dx,al ; data byte.
add dl,_CONTROL ; move to the control port.
mov al,WrSndData ; strobe the data out.
out dx,al
mov al,WrIdle ; complete the strobe by
out dx,al ; returning to write idle.
Idledly
Two things to note from the above example; to strobe
data out to the Audio Port, the Audio port must make state
transitions from Idle, to an active send, then back to idle.
The second thing to note, is the required I/O delay when
returning to idle mode. By IBM's design, the parallel port
hardware signal timings make transitions to active states
very quickly. When transitioning into idle modes, the
signals take almost twice as long. The above example shows
the use of a simple text macro called "Idledly". This macro
It could be defined as:
Idledly equ <out dx,al>
All it does, is perform an addition I/O write instruction,
which on the IBM ISA bus, is guaranteed to take a minimum of
850ns of time.
The other equates used in the above code segment have
the following values:
_DATA equ 0
_STATUS equ 1
_CONTROL equ 2
C O M M A N D / D A T A H A N D S H A K I N G
When the PC clocks data to the Micro or FM, the APIC
latches the data, watches the signals, then sends the data
to the appropriate device. In order to help the PC know when
to send the index, commands, or data, a status bit in the
Status register is presented for the PC to poll. This status
bit, bit D7, is used for both the FM and Micro WAIT
status's. The Control register state, WrIdle or WrFMIdle,
determine which WAIT state is being presented for either
the FM or Micro.
For sending index/data to the 3812 FM chip, Yamaha has
defined certain timing delays between the writes. Index
writes take a minimum of 5 micro seconds to complete. Data
writes take a minimum of 35 micro seconds to complete. The
APIC starts clocking the index/data out to the FM chip once
the PC finishes clocking out the data byte to the APIC.
During the time the APIC is clocking the index/data out to
the 3812, the WAIT bit will be held high. The APIC holds the
wait line high for the entire duration of both index and
data write times. This means the PC has to poll the wait
line before writing the index, but can write both the index,
then data byte with no intervening waits.
For Micro reads and writes, the WAIT line is derived
from the micro. The APIC just passes this signal through
unaltered. During Write idle, the WAIT line indicates
whether the Micro can receive more commands or data. The
signal goes high (1) when WAIT is asserted.
For queued DAC output, the WAIT line goes active (1)
when the queue cannot receive more data. For ADC input, the
WAIT line goes active (1) when the queue is emptied.
DIGITAL AUDIO PROGRAMMING
The Digial Audio section is controlled by both the APIC
and Microcontroller. All commands issued are processed by
the microcontroller. The APIC provides the clocking control,
and FIFO management. To issue commands to the micro, the
status port must be polled to check the WAIT bit. The Micro
WAIT is presented during WrIdle mode. When the bit is low
(0), commands or data may be written.
Micro Command Set:
┌────────────────┬──────────┬─────────┬─────────┬─────────┬─────────┐
│ Command │D7-D4 Code│ D3 │ D2 │ D1 │ D0 │
├────────────────┼──────────┼─────────┼─────────┼─────────┼─────────┤
│ reserved │ 0 │ . │ . │ . │ . │
├────────────────┼──────────┼─────────┼─────────┼─────────┼─────────┤
│System Control │ 1 │ X │ X │ X │ X │
├────────────────┼──────────┼─────────┼─────────┼─────────┼─────────┤
│8 Bit PCM Direct│ 2 │Play/Rcd │AGC Kill │ Timer │ 0 │
├────────────────┼──────────┼─────────┼─────────┼─────────┼─────────┤
│ reserved │ 3 │ . │ . │ . │ . │
├────────────────┼──────────┼─────────┼─────────┼─────────┼─────────┤
│Play PCM Queue │ 4 │Compress │ 4/2 Bit │ Timer │ Ref │
├────────────────┼──────────┼─────────┼─────────┼─────────┼─────────┤
│Rcd 8 Bit Queue │ 5 │ VOX │AGC Kill │ Timer │ 0 │
├────────────────┼──────────┼─────────┼─────────┼─────────┼─────────┤
│Rcd 4 Bit Queue │ 6 │ VOX │AGC Kill │ Timer │ 1 │
├────────────────┼──────────┼─────────┼─────────┼─────────┼─────────┤
│Rcd 2 Bit Queue │ 7 │ VOX │AGC Kill │ Timer │ 1 │
├────────────────┼──────────┼─────────┼─────────┼─────────┼─────────┤
│ reserved │ 8 │ . │ . │ . │ . │
├────────────────┼──────────┼─────────┼─────────┼─────────┼─────────┤
│ reserved │ 9 │ . │ . │ . │ . │
├────────────────┼──────────┼─────────┼─────────┼─────────┼─────────┤
│ reserved │ A │ . │ . │ . │ . │
├────────────────┼──────────┼─────────┼─────────┼─────────┼─────────┤
│ reserved │ B │ . │ . │ . │ . │
├────────────────┼──────────┼─────────┼─────────┼─────────┼─────────┤
│ reserved │ C │ . │ . │ . │ . │
├────────────────┼──────────┼─────────┼─────────┼─────────┼─────────┤
│ reserved │ D │ . │ . │ . │ . │
├────────────────┼──────────┼─────────┼─────────┼─────────┼─────────┤
│ reserved │ E │ . │ . │ . │ . │
├────────────────┼──────────┼─────────┼─────────┼─────────┼─────────┤
│ reserved │ F │ . │ . │ . │ . │
└────────────────┴──────────┴─────────┴─────────┴─────────┴─────────┘
System Control Commands - 0001xxxx
10h SYSCMDIDLE - Terminate DAC/ADC modes, return to Idle.
This is guaranteed to return the Audio
Port Micro to an Idle state
Parameters: No Parameters
Returns: No Data Returned
11h SYSCMDGETID - Get ESP version
Parameters: No parameters
Returns: Returns 2 bytes
13h SYSCMDENAPARR - Enable Parallel Input
Parameters: No Parameters
Returns: No Data Returned
14h SYSCMDDISPARR - Disable Parallel Input
Parameters: No Parameters
Returns: No Data Returned
15h SYSCMDTSTPARR - Test Parallel Input
Parameters: One data byte following (any value)
Returns: Returns the same data byte
in parallel mode.
Direct DAC/ADC Control Commands - 0010xxxx
NOTE: The AGC kill bit disables the internal Auto Gain
Control. This bit is normally set to 0, thus allowing
the AGC to function. Setting this bit to a 1 turns
off the AGC for the duration of the recording.
20h SYSDIRPLAY - 8 bit direct play.
This places the Audio Port Micro into DAC
output mode. The queue is not enabled.
Parameters: Sound Data follows.
Returns: No Data Returned
22h SYSDIRNA2 - 8 bit direct play w/time constant following.
This places the Audio Port Micro into
DAC output mode. The queue is not enabled.
Parameters: One byte parameter follows. This is the
sample rate speed. The following function
calculates the sample rate:
TimeConst = 256-(1000000/SampleRate)
Sound Data follows.
Returns: No Data Returned
28h SYSDIRRECD - 8 bit direct record.
Sampled data will be made available until
command 10H is issued.
Parameters: No Parameters
Returns: Sampled data will be made available until
command 10H is issued.
2Ah SYSDIRNAA - 8 bit direct record w/time constant following.
Parameters: One byte parameter follows. This is the
sample rate speed. The following function
calculates the sample rate:
TimeConst = 256-(1000000/SampleRate)
Returns: Sampled data will be made available until
command 10H is issued.
FIFO DAC/ADC Commands - 0100xxxx
40h SYSDIRQPLAY - 8 bit FIFO DAC out.
This function enables the 1k FIFO output
to the DAC. Watch the SRQ line to send out
256 byte blocks.
Parameters: Sound Data follows.
Returns: No Data Returned
42h SYSDIRQNA2 - 8 bit FIFO DAC out w/time constant following.
This function enables the 1k FIFO output
to the DAC. Watch the SRQ line to send out
256 byte blocks.
One byte parameter follows. This is the
sample rate speed. The following function
calculates the sample rate:
TimeConst = 256-(1000000/SampleRate)
Parameters: Sound Data follows.
Returns: No Data Returned
48h SYSDIRQRECD - 4 bit FIFO DAC out decompression
This function enables the 1k FIFO output
to the DAC. Watch the SRQ line to send out
256 byte blocks.
Parameters: Sound Data follows.
Returns: No Data Returned
49h SYSDIRQRECD - 4 bit FIFO DAC out decompression with
reference byte.
This function enables the 1k FIFO output
to the DAC. Watch the SRQ line to send out
256 byte blocks.
Parameters: One 8 bit sample follows. This is the seed
value for decompression.
Sound Data follows.
Returns: No Data Returned
4Ah SYSDIRQRECD - 4 bit FIFO DAC out decompression with
time constant following.
This function enables the 1k FIFO output
to the DAC. Watch the SRQ line to send out
256 byte blocks.
Parameters: One byte parameter follows. This is the
sample rate speed. The following function
calculates the sample rate:
TimeConst = 256-(1000000/SampleRate)
Sound Data follows.
Returns: No Data Returned
4Bh SYSDIRQRECD - 4 bit FIFO DAC out decompression with
time constant following.
This function enables the 1k FIFO output
to the DAC. Watch the SRQ line to send out
256 byte blocks.
Parameters: Two parameter bytes follows. The first is the
sample rate speed. The following function
calculates the sample rate:
TimeConst = 256-(1000000/SampleRate)
The second byte is the seed value for
decompression.
Sound Data follows.
Returns: No Data Returned
4Ch SYSDIRQRECD - 2 bit FIFO DAC out decompression.
This function enables the 1k FIFO output
to the DAC. Watch the SRQ line to send out
256 byte blocks.
Parameters: Sound Data follows.
Returns: No Data Returned
4Dh SYSDIRQRECD - 2 bit FIFO DAC out decompression with
reference byte.
This function enables the 1k FIFO output
to the DAC. Watch the SRQ line to send out
256 byte blocks.
Parameters: One 8 bit sample follows. This is the seed
value for decompression.
Sound Data follows.
Returns: No Data Returned
4Eh SYSDIRQRECD - 2 bit FIFO DAC out decompression with
time constant following.
This function enables the 1k FIFO output
to the DAC. Watch the SRQ line to send out
256 byte blocks.
Parameters: One byte parameter follows. This is the
sample rate speed. The following function
calculates the sample rate:
TimeConst = 256-(1000000/SampleRate)
Sound Data follows.
Returns: No Data Returned
4Fh SYSDIRQRECD - 2 bit FIFO DAC out w/time constant following.
This function enables the 1k FIFO output
to the DAC. Watch the SRQ line to send out
256 byte blocks.
Parameters: Two parameter bytes follows. The first is the
sample rate speed. The following function
calculates the sample rate:
TimeConst = 256-(1000000/SampleRate)
The second byte is the seed value for
decompression.
Sound Data follows.
Returns: No Data Returned
FIFO 8 Bit Recording Commands - 0101xxxx
NOTE: The AGC kill bit disables the internal Auto Gain
Control. This bit is normally set to 0, thus allowing
the AGC to function. Setting this bit to a 1 turns
off the AGC for the duration of the recording.
The VOX bit allows voice activated recording. When
this bit is set (1), the Audio Port will only begin
recording when the mic signal reaches certain volumes.
When in VOX mode, the Audio Port will stop recording
1 to 2 seconds after the mic signal becomes quiet.
50h SYSDIRPLAY - 8 bit FIFO record.
Sampled data will be made available until
command 10H is issued.
Parameters: No Parameters
Returns: Sampled data will be made available until
command 10H is issued.
52h SYSDIRNA2 - 8 bit FIFO w/time constant following.
Parameters: One byte parameter follows. This is the
sample rate speed. The following function
calculates the sample rate:
TimeConst = 256-(1000000/SampleRate)
Returns: Sampled data will be made available until
command 10H is issued.
FIFO 4 Bit Recording Commands - 0110xxxx
NOTE: The AGC kill bit disables the internal Auto Gain
Control. This bit is normally set to 0, thus allowing
the AGC to function. Setting this bit to a 1 turns
off the AGC for the duration of the recording.
The VOX bit allows voice activated recording. When
this bit is set (1), the Audio Port will only begin
recording when the mic signal reaches certain volumes.
When in VOX mode, the Audio Port will stop recording
1 to 2 seconds after the mic signal becomes quiet.
60h SYSDIRPLAY - 4 bit FIFO record compression.
Sampled data will be made available until
command 10H is issued.
Parameters: No Parameters
Returns: Sampled data will be made available until
command 10H is issued.
63h SYSDIRNA2 - 4 bit FIFO w/time constant byte parameter.
Parameters: One byte parameter follows. This is the
sample rate speed. The following function
calculates the sample rate:
TimeConst = 256-(1000000/SampleRate)
Returns: Sampled data will be made available until
command 10H is issued. The first sample
returned is the full 8 bit sample used
as the reference byte.
FIFO 2 Bit Recording Commands - 0110xxxx
NOTE: The AGC kill bit disables the internal Auto Gain
Control. This bit is normally set to 0, thus allowing
the AGC to function. Setting this bit to a 1 turns
off the AGC for the duration of the recording.
The VOX bit allows voice activated recording. When
this bit is set (1), the Audio Port will only begin
recording when the mic signal reaches certain volumes.
When in VOX mode, the Audio Port will stop recording
1 to 2 seconds after the mic signal becomes quiet.
70h SYSDIRPLAY - 2 bit FIFO record.
Sampled data will be made available until
command 10H is issued.
Parameters: No Parameters
Returns: Sampled data will be made available until
command 10H is issued.
73h SYSDIRNA2 - 2 bit FIFO w/time constant following.
Parameters: One byte parameter follows. This is the
sample rate speed. The following function
calculates the sample rate:
TimeConst = 256-(1000000/SampleRate)
Returns: Sampled data will be made available until
command 10H is issued. The first sample
returned is the full 8 bit sample used
as the reference byte.
FM PROGRAMMING.
For sending index/data to the 3812 FM chip, Yamaha has
defined certain timing delays between the writes. Index
writes take a minimum of 5 micro seconds to complete. Data
writes take a minimum of 35 micro seconds to complete. The
APIC starts clocking the index/data out to the FM chip once
the PC finishes clocking out the data byte to the APIC.
During the time the APIC is clocking the index/data out to
the 3812, the WAIT bit will be held high. The APIC holds the
wait line high for the entire duration of both index and
data write times plus delay times. This means the PC has to
poll the wait line before writing the index, but can write
both the index, then data byte with no intervening waits.
INTERRUPT PROCESSING.
The recommended approach for interrupt processing is to use
a fast system timer interrrupt. The system timer should be re-
programmed to generate an interrupt every 30 milliseconds to
check the SRQ line. If the SRQ is high, another 256 byte block
of PCM data can be read or written. At interrupt time, upon
completing the first 256 byte block, check SRQ again, and
repeat the process until the SRQ goes low. Once all blocks are
finished, the system timer can be reprogrammed to interrupt at
it's normal frequency of 18.2 times per second.
On DAC output, The SRQ line goes high when the 1k buffer
has 256 bytes, or less remaining. It goes low when it has
768 bytes or more stored.
On ADC input, the SRQ line goes high if the FIFO has
768 or more bytes. It goes low when it has 256 or fewer
bytes stored.
APPENDIX A - HARDWARE PORTS & STATES
NAME LPT1 LPT2 MONO DESCRIPTION
┌───────────┬───┬─────┬─────┬─────┬─────────────────┐
│Data Output│ R │ 378 │ 278 │ 3BC │write to TP │
├───────────┼───┼─────┼─────┼─────┼─────────────────┤
│Status │ R │ 379 │ 279 │ 3BD │read status/data │
├───────────┼───┼─────┼─────┼─────┼─────────────────┤
│Control │ W │ 37A │ 27A │ 3BE │set I/O control │
└───────────┴───┴─────┴─────┴─────┴─────────────────┘
Base Address + 0 offset - Data Register Definitions:
D7 D6 D5 D4 D3 D2 D1 D0
┌───────┬────┬────┬────┬────┬────┬────┬────┬────┐
│Data │bit7│bit6│bit5│bit4│bit3│bit2│bit1│bit0│
└───────┴────┴────┴────┴────┴────┴────┴────┴────┘
| | | | | | | |
+----+----+----+----+----+----+----+-- Data lines
Base Address + 1 offset - Status Register Definitions
D7 D6 D5 D4 D3 D2 D1 D0
┌─────────┬────┬────┬────┬────┬────┬────┬────┬────┐
│Status │ W │IRQ │PAR │SRQ │ DA │ X │ X │ X │
└─────────┴────┴────┴────┴────┴────┴────┴────┴────┘
| | | | | | | |
| | | | | +----+----+- unused
| | | | +---------------- Data Available
| | | +--------------------- SRQ/D4/D0
| | +-------------------------- PAR/D5/D1
| +------------------------------- IRQ/D6/D2
+------------------------------------ WAIT/D7/D3
Base Address + 2 offset - Control Register Definitions
D7 D6 D5 D4 D3 D2 D1 D0
┌─────────┬────┬────┬────┬────┬────┬────┬────┬────┐
│Control │PAR │PAR │PAR │IRQ │ AS │MFM │ DS │ RW │
└─────────┴────┴────┴────┴────┴────┴────┴────┴────┘
| | | | | | | |
| | | | | | | +- Read(0)/Write(1)
| | | | | | +------ Data Strobe
| | | | | +----------- Micro/FM select
| | | | +---------------- Address Strobe
| | | +--------------------- IRQ Enable
+----+----+-------------------------- Par. input select
Control Register Clock State:
00h RdFMIdle Read FM Idle
01h WrFMIdle Write FM Idle
02h invalid invalid state
03h WrFMData Write FM Data
04h RdIdle Read Sound Idle
05h WrIdle Write Sound Idle
06h RdSndData Read Sound Data
07h WrSndData Write Sound Data
08h RdFMStat Get FM Status
09h WrFMAddr Write FM Address
0Ah invalid invalid state
0Bh invalid And another Reset
0Ch RdSndStat Read Sound Status
0Dh WrSndCmd Write Sound Command
0Eh invalid invalid state
0Fh WrReset2 Write Reset
Control Register Additional bit definitions
10h Interrupt Enable interrupt
E0h EnableInput Enable Parallel Input
APPENDIX B - EXAMPLE ROUTINES
The following code assumes the parallel port base I/O
address for the Audio Port is stored in the variable,
"PortAddress".
The following macros and equates are used in the code
examples:
Idledly equ <out dx,al>
_DATA equ 0 ; Parallel port data port offset
_STATUS equ 1 ; Parallel port status port offset
_CONTROL equ 2 ; Parallel port control port offset
RdFMIdle equ 00h ; Read FM Idle
WrFMIdle equ 01h ; Write FM Idle
invalid equ 02h ; invalid state
WrFMData equ 03h ; Write FM Data
RdIdle equ 04h ; Read Sound Idle
WrIdle equ 05h ; Write Sound Idle
RdSndData equ 06h ; Read Sound Data
WrSndData equ 07h ; Write Sound Data
RdFMStat equ 08h ; Get FM Status
WrFMAddr equ 09h ; Write FM Address
invalid equ 0Ah ; invalid state
invalid equ 0Bh ; And another Reset
RdSndStat equ 0Ch ; Read Sound Status
WrSndCmd equ 0Dh ; Write Sound Command
invalid equ 0Eh ; invalid state
WrReset2 equ 0Fh ; Write Reset
;
; /*\
;---|*|----====< AP_ADC_input >====----
;---|*|
;---|*| Input 256 bytes from the AP FIFO. This routine
;---|*| assumes that the AP is already in ADC input mode.
;---|*|
;---|*| Entry Conditions:
;---|*| ES:DI point to the buffer.
;---|*| CX holds a count of 1 to 256
;---|*|
;---|*| Exit Conditions:
;---|*| ES:DI is updated. CX is zero
;---|*| AX,BX,CX,DX,ES:DI modified
;---|*|
; \*/
;
public AP_ADC_input
AP_ADC_input proc near
;
; load the FIFO quarter size for use with the SRQ handshaking.
;
mov dx,[PortAddress] ; get the port address
add dl,_CONTROL
cld ; STOSBs are used
;
apadc05:
mov al,RdIdle ; go into Read Idle to pick up the nibble
out dx,al ; Idle w/o interrupts
Idledly
dec dx ; move to STATUS
in al,dx
mov ah,al ; save the high nibble
inc dx
mov al,RdSndData ; ah = RdIdle, al = RdSndData
out dx,al ; this delivers the second nibble
Idledly
dec dx
in al,dx ; fetch it
shr ah,4 ; merge the nibbles
shr ax,4
stosb ; save the byte
or di,di ; segment wrap?
jz apadcsegwrap ; yes, go adjust...
;
apadc10:
inc dx ; place the board into idle mode again
loop apadc05 ; do all 256 or less bytes
mov al,WrIdle ; return back to write idle
out dx,al
Idledly
ret ; all done recording this block!
;
apadcsegwrap:
mov di,es ; wrap the segment for huge model
add di,1000h ; support.
mov es,di
sub di,di
jmp short apadc10
AP_ADC_input endp
;
; /*\
;---|*|----====< AP_DAC_output >====----
;---|*|
;---|*| Output 256 bytes to the AP FIFO. This routine
;---|*| assumes that the AP is already in DAC output mode.
;---|*|
;---|*| Entry Conditions:
;---|*| ES:DI point to the buffer.
;---|*| CX holds a count of 1 to 256
;---|*|
;---|*| Exit Conditions:
;---|*| ES:DI is updated. CX is zero
;---|*| AX,CX,DX,ES:DI modified
;---|*|
; \*/
;
public AP_DAC_output
AP_DAC_output proc near
;
; make sure the port is in write idle mode...
;
mov dx,[PortAddress] ; get the port address
add dl,_CONTROL
mov al,WrIdle ; make sure its idle
out dx,al
sub dl,_CONTROL ; move back to the data port
;
apdac05:
;
; send 256 bytes of data out to the FIFO
;
mov al,es:[di] ; fetch the byte
inc di
jz apdacsegwrap ; odd case jumps out...
;
apdac10:
out dx,al ; write it to the audio port
add dl,_CONTROL
mov al,WrSndData ; start the strobe...
out dx,al
mov al,WrIdle ; end the strobe....
out dx,al
Idledly
sub dl,_CONTROL
loop apdac05 ; do all 256 or less bytes
ret
;
apdacsegwrap:
mov di,es ; move to the next segment
add di,1000h
mov es,di
sub di,di
jmp short apdac10 ; then get back into the playback
AP_DAC_output endp
/*\
|*|----====< AP_Reset >====----
|*|
|*| Reset the Audio Port
|*|
|*| Entry Conditions:
|*| None.
|*|
|*| Exit Conditions:
|*| AL holds the reset byte - 05A if successful
|*| AX,BX,CX,DX modified
|*|
\*/
public AP_Reset
AP_Reset proc
mov dx,[PortAddress]
mov al,-1 ; load FF into the data port to drive the
out dx,al ; data lines.
add dl,_CONTROL
mov cx,16
mov al,WrReset2 ; output reset mode
;
@@:
out dx,al
loop @B
mov cx,16
;
@@:
mov al,WrIdle ; clear reset
out dx,al
loop @B
mov al,RdIdle ; get sound data read status
out dx,al
Idledly
dec dx ; move back to the status port
sub cx,cx
;
aprst05:
in al,dx
test al,stIDav ; wait for data to become available
loopz aprst05
jz aprstbad
mov ah,al ; save the result (should not be 0xFF)
inc dx ; move to the control port
mov al,RdSndData ; Clock in the data
out dx,al
dec dx
in al,dx
mov bx,ax
inc dx
mov al,RdIdle ; go back to idle
out dx,al
Idledly
mov al,WrIdle ; go back to idle
out dx,al
Idledly
mov cl,4 ; build the reset byte.
shr bh,cl
shr bx,cl
mov al,bl
ret
;
aprstbad:
mov al,-1
ret
AP_reset endp
/*\
|*|----====< AP_MDR_stat >====----
|*|
|*| Returns the status of data available
|*|
|*| Entry Conditions:
|*| None.
|*|
|*| Exit Conditions:
|*| Carry clear & AX = 0, no data available
|*| Carry set & AX = -1, data available
|*| DX,AX modified
|*|
\*/
public AP_MDR_stat
AP_MDR_stat proc
mov dx,[PortAddress]
add dl,_CONTROL
mov al,RdIdle ; Micro read idle
out dx,al
Idledly
dec dx ; mov to the status port
in al,dx ; data rdy is high for data available
mov ah,stIDav
and ah,al ; save the status bit
mov al,WrIdle ; send the port back to
out dx,al ; idle mode
Idledly
neg ah ; set carry if non-zero
sbb ax,ax ; if data is available, return -1 & Cy
ret ; else return 0 & NC
AP_MDR_stat endp
/*\
|*|----====< AP_MDR_wait >====----
|*|
|*| Wait for Micro Data Read to be ready.
|*|
|*| Entry Conditions:
|*| None.
|*|
|*| Exit Conditions:
|*| Carry clear & AX = 0, no data available
|*| Carry set & AX = -1, data available
|*| DX,AX modified
|*|
\*/
public AP_MDR_wait
AP_MDR_wait proc
push cx
sub cx,cx
;
@@:
call AP_MDR_stat
or ax,ax
loopz @B
pop cx
ret
AP_MDR_wait endp
/*\
|*|----====< AP_MCW >====----
|*|
|*| Send a command byte out to the Micro.
|*|
|*| Entry Conditions:
|*| AL holds the command
|*|
|*| Exit Conditions:
|*| AX = -1 if data is available, or 0
|*| DX,AX modified
|*|
\*/
public AP_MCW
AP_MCW proc
push cx
mov ah,al ; save the command/data
;
apmcw_00:
mov dx,[PortAddress]
add dl,_CONTROL
mov al,WrIdle
out dx,al
Idledly
mov al,ah ; get the byte and write it out
sub dl,_CONTROL ; move to the data port
out dx,al ; load the data
inc dx ; move to the status port
sub cx,cx
;
apmcw_05:
in al,dx ; wait=1 when cmd/data cannot be sent
test al,stWait
loopnz apmcw_05
jz apmcw_10 ; its available
push ax ; The device is probably powered off, so
call AP_reset ; we just have to kick it...
pop ax ; AH holds the command/data byte
jmp short apmcw_00
;
apmcw_10:
inc dx ; move to the control port
mov al,WrSndCmd ; start the strobe
out dx,al
mov al,WrIdle ; end the strobe
out dx,al
Idledly
pop cx
ret
AP_MCW endp
/*\
|*|----====< AP_MDR >====----
|*|
|*| Read a data byte from the Micro of FIFO
|*|
|*| Entry Conditions:
|*| Data Available must have been checked before calling
|*| this routine. It is assumed to be high.
|*|
|*| Exit Conditions:
|*| AL holds something...
|*| DX,AX modified
|*|
\*/
AP_MDR proc
push cx
mov dx,[PortAddress]
add dl,_CONTROL
mov al,RdIdle ; go to read IDLE
out dx,al
Idledly
mov al,RdSndData ; read the high nibble
out dx,al ; start the strobe
dec dx ; grab one nibble
in al,dx ; from the status port
mov ah,al ; save in AH!
inc dx
mov al,RdIdle ; swap nibbles
out dx,al ; end the strobe
Idledly
dec dx
in al,dx ; read the next nibble
mov cl,WrIdle
xchg ax,cx ; save the bytes, load WrIdle
inc dx ; send H/W into write idle mode
out dx,al
xchg ax,cx ; get the bytes
mov cl,4
shr ah,cl ; this order makes for a
shr ax,cl ; fast conversion
pop cx
ret
AP_MDR endp
/*\
|*|----====< AP_MDW >====----
|*|
|*| Send a data byte out to the FIFO.
|*|
|*| Entry Conditions:
|*| AL holds the data
|*|
|*| Exit Conditions:
|*| AX = -1 if data is available, or 0
|*| DX,AX modified
|*|
\*/
public AP_MDW
AP_MDW proc
push cx
mov ah,al ; save the command/data
;
apmdw_00:
mov dx,[PortAddress]
add dl,_CONTROL
mov al,WrIdle ; make sure its' idle
out dx,al
Idledly
mov al,ah ; get the byte and write it out
sub dl,_CONTROL ; move to the data port
out dx,al ; load the data
inc dx ; move to the status port
sub cx,cx
;
apmdw_05:
in al,dx ; wait=1 when cmd/data cannot be sent
test al,stWait
loopnz apmdw_05
jz apmdw_10 ; its available
push ax ; The device is probably powered off, so
call AP_reset ; we just have to kick it...
pop ax ; AH holds the command/data byte
jmp short apmdw_00
;
apmdw_10:
inc dx ; move to the control port
mov al,WrSndData ; start the strobe
out dx,al
mov al,WrIdle ; end the strobe
out dx,al
Idledly
pop cx
ret
AP_MDW endp
/*\
|*|----====< AP_SRQ_stat >====----
|*|
|*| Returns the status of Service Request
|*|
|*| Entry Conditions:
|*| None.
|*|
|*| Exit Conditions:
|*| Carry clear & AX = 0, wait
|*| Carry set & AX = -1, service request is posted
|*| DX,AX modified
\*/
public AP_SRQ_stat
AP_SRQ_stat proc
mov dx,[PortAddress]
add dl,_CONTROL
mov al,WrIdle ; Write Idle mode...
out dx,al
Idledly
dec dx ; mov to the status port
in al,dx ; data rdy is high for data available
and al,stSRQ
neg al ; set carry if non-zero
sbb ax,ax ; if data is available, return -1 & Cy
ret ; else return 0 & NC
AP_SRQ_stat endp
/*\
|*|----====< AP_FM_out >====----
|*|
|*| Send index/data out to the parallel FM chip
|*|
|*| Entry Conditions:
|*| AL holds the index
|*| AH holds the data
|*|
|*| Exit Conditions:
|*| None
|*| DX,AX modified
\*/
public AP_FM_out
AP_FM_out proc
push cx
push ax ; save the index/data
;
apfmo00:
mov dx,[PortAddress]
add dl,_CONTROL ; move to the status port
mov al,WrFMIdle ; go into FM Mode
out dx,al
Idledly
dec dx ; move back to status
sub cx,cx ; loop control
;
apfmo05:
in al,dx
test al,stWait ; FM wait high?
loopnz apfmo05 ; yes, dont write it
jz apfmo10 ; its available
call AP_reset ; The device is probably powered off, so
jmp short apfmo00 ; we just have to kick it...
;
apfmo10:
pop ax ; get the index and data
jnz apfmobad ; bomb out if we cant do it
dec dx ; move to the data port
out dx,al ; load the data port
inc dx ; move to the control register
inc dx
mov al,WrFMAddr
out dx,al ; start the strobe
mov al,WrFMIdle
out dx,al ; end the strobe
Idledly
mov al,ah
sub dl,_CONTROL ; move to the data port
out dx,al ; load the data port
add dl,_CONTROL
mov al,WrFMData
out dx,al ; start the strobe
mov al,WrFMIdle
out dx,al ; end the strobe
Idledly
mov al,WrIdle
out dx,al ; back to write idle...
;
apfmobad:
pop cx
ret
AP_FM_out endp
/*\
|*|----====< AP_FM_in >====----
|*|
|*| Read the FM status register
|*|
|*| Entry Conditions:
|*| None
|*|
|*| Exit Conditions:
|*| AL holds the status byte
|*| DX,AX modified
\*/
AP_FM_in proc
mov dx,[PortAddress]
add dl,_CONTROL ; point to control port
mov al,WrIdle ; make sure we're idle
out dx,al
Idledly
mov al,RdFMIdle ; go into FM Mode
out dx,al
Idledly
mov al,RdFMStat ; clock in the data
out dx,al
dec dx ; move to the status port
in al,dx
mov bl,al ; save the high nibble
inc dx
mov al,RdFMIdle ; all set
out dx,al ; strobe address
Idledly
dec dx
in al,dx ; clock in the data
mov bh,al
inc dx
mov al,WrIdle ; go back to idle mode
out dx,al
Idledly
mov cl,4
shr bh,cl
shr bx,cl
mov al,bl
ret
AP_FM_in endp