VOGONS


First post, by superfury

User metadata
Rank l33t++
Rank
l33t++

From what I can understand in the documentation, the IRQs on the parallel port trigger in a bit weird way(combining the manual of the parallel port and various (Dosbox emulation of the DSS, DSS programmer's guide(https://archive.org/details/dss-programmers-g … age/n1/mode/2up) various other documentations(http://www.emblogic.com/blog/07/interrupts-in-parallel-port/)))?

As far as I can see, bit 7 of the data register is wired to the BSY bit on the port, causing it to be read back inverted on said port? That might be a crude detection method.

But what especially is strange, is that when the DSS buffer is full(nothing can be written anymore), ACK becomes high(bit 6 in the status becomes set). So that's a detection of a buffer that's filled.
But the documentation also says that when ACK toggles from low to high(it's raised), an IRQ is triggered?

But wouldn't you normally want an IRQ when the buffer is empty instead of full(so you can write most data, instead of nothing)? Or is an IRQ supposed to fire when ACK becomes low(which would mean that all documentation on the parallel port is flipped)?

Edit: Hmmm.... Reading https://allpinouts.org/pinouts/connectors/par … l/spp-parallel/ , it says that the ACK line is actually a NACK line? So the IRQ might be raised when ACK is raised, but the register reports it inverted? So that would mean that whenever the read out value turns from set(no ACK signal) to clear(meaning the ACK is raised), more data can be received(buffer isn't full)? And said bit is set (buffer full as the documentation on the Sound Source says it is), the ACK bit is actually cleared(lowered)? Then once the buffer has space again, the ACK signal from the device is raised again, causing the nACK to clear(buffer isn't full anymore) and an IRQ to fire?

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 1 of 7, by superfury

User metadata
Rank l33t++
Rank
l33t++

I've just been thinking: the 'buffer full' bit that's documented in the DSS documentation(bit 6 of the LPT status register)... Could it be that it's sticky? With that I mean that it changes from 0 to 1 when the buffer becomes full, but doesn't become 0 again(triggering an IRQ when it's enabled on the LPT) until the buffer is either fully emptied by rendering or a new value is added to the buffer(which re-checks said bit and updates it for compatiblity)?
So in that way, both the detection scheme(becoming 1 when full) and the entire 16-byte buffer can be used during the rendering of sound(instead of throwing an IRQ for every sample that's sent to the DAC(since the ACK line becomes 0 after the first byte is emptied from the buffer))?

Edit: I've tried to implement it this way:
- When a byte is written, the full flag is cleared, making it instantly become the current full status(see below).
- When the status(ACK in this case) is read, if the buffer is full, the flag is set(giving full status). Else, if the buffer is empty, it's cleared(giving empty status).

So when software tries to do single-sample transfers each time, the full status will update every time a byte is written, setting the flag(because the buffer became full) when data is added, as is documented(also allowing detection schemes to work).
But when the program wants to use the full capabilities of the FIFO, it just writes until the flag becomes set(since the buffer was full), which won't become empty until all samples are rendered(buffer is empty), at which point the flag clears and the IRQ is triggered. It should also work with the code written in the documentation of the DSS.

Would that be correct behaviour?

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 2 of 7, by superfury

User metadata
Rank l33t++
Rank
l33t++

OK. Testing the parallel port emulation with the Sound Source, I noticed something: it seems like Wolfenstein 3D doesn't turn off the Sound Source connected on the parallel port(upon exiting, it leaves bit 2 of the control register set, thus leaving the Sound Source powered on? I also see MS-DOS enabling it somehow when booting?

Or is that perhaps an issue with the Compaq Deskpro 386 BIOS?
Edit: The same thing happens with Windows 95 booting. Just before booting, it's turned on?

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 3 of 7, by superfury

User metadata
Rank l33t++
Rank
l33t++

Hmmm... Those weird values writing into the parallel port's control register seems to be due to the INT 17h or some part of the BIOS handling of interrrupt 17h? That's not supposed to happen? So there's some CPU bug there?

Edit: Just confirmed. It's the initialization call of the BIOS, INT 17h function 01h DX=0 (http://www.ctyme.com/intr/rb-2099.htm) that's causing the DSS to turn on on boot.

So, somewhere in there, there's a CPU or hardware(most likely CPU) error?

Edit: Hmmm... So this seems to be the entirety of the code that enables the DSS speaker while booting:

Details

x88ea: sti ; 000088EA FB '.'
push bx ; 000088EB 53 'S'
push cx ; 000088EC 51 'Q'
push dx ; 000088ED 52 'R'
push si ; 000088EE 56 'V'
push ds ; 000088EF 1E '.'
mov bx,0x40 ; 000088F0 BB4000 '.@.'
mov ds,bx ; 000088F3 8EDB '..'
and dx,0x3 ; 000088F5 81E20300 '....'
mov si,dx ; 000088F9 8BF2 '..'
mov bl,[si+0x78] ; 000088FB 8A9C7800 '..x.'
shl si,1 ; 000088FF D1E6 '..'
mov dx,[si+0x8] ; 00008901 8B940800 '....'
or dx,dx ; 00008905 0BD2 '..'
jz x8980 ; 00008907 7477 'tw'
push ax ; 00008909 50 'P'
test ah,ah ; 0000890A 84E4 '..'
jz x8919 ; 0000890C 740B 't.'
dec ah ; 0000890E FECC '..'
jz x895b ; 00008910 7449 'tI'
dec ah ; 00008912 FECC '..'
jz x8971 ; 00008914 745B 't['
pop ax ; 00008916 58 'X'
jmp short x8980 ; 00008917 EB67 '.g'

x8919: out dx,al ; 00008919 EE '.'
inc dx ; 0000891A 42 'B'
jmp short x891d ; 0000891B EB00 '..'

x891d: jmp short x891f ; 0000891D EB00 '..'

x891f: in al,dx ; 0000891F EC '.'
and al,0xf8 ; 00008920 24F8 '$.'
js x894c ; 00008922 7828 'x('
push ax ; 00008924 50 'P'
mov ax,0x90fe ; 00008925 B8FE90 '...'
clc ; 00008928 F8 '.'
int 0x15 ; 00008929 CD15 '..'
pop ax ; 0000892B 58 'X'
jc x893f ; 0000892C 7211 'r.'
x892e: mov cx,0xf424 ; 0000892E B924F4 '.$.'
x8931: call x919a ; 00008931 E86608 '.f.'
in al,dx ; 00008934 EC '.'
and al,0xf8 ; 00008935 24F8 '$.'
js x894c ; 00008937 7813 'x.'
loop x8931 ; 00008939 E2F6 '..'
dec bl ; 0000893B FECB '..'
jnz x892e ; 0000893D 75EF 'u.'
x893f: jmp short x8941 ; 0000893F EB00 '..'

x8941: jmp short x8943 ; 00008941 EB00 '..'

x8943: in al,dx ; 00008943 EC '.'
and al,0xf8 ; 00008944 24F8 '$.'
js x894c ; 00008946 7804 'x.'
or al,0x1 ; 00008948 0C01 '..'
jmp short x8979 ; 0000894A EB2D '.-'

x894c: inc dx ; 0000894C 42 'B'
in al,dx ; 0000894D EC '.'
and al,0x1c ; 0000894E 241C '$.'
or al,0x1 ; 00008950 0C01 '..'
jmp short x8954 ; 00008952 EB00 '..'

x8954: jmp short x8956 ; 00008954 EB00 '..'

x8956: out dx,al ; 00008956 EE '.'
and al,0x1c ; 00008957 241C '$.'
jmp short x896a ; 00008959 EB0F '..'

x895b: inc dx ; 0000895B 42 'B'
inc dx ; 0000895C 42 'B'
mov al,0x8 ; 0000895D B008 '..'
jmp short x8961 ; 0000895F EB00 '..'

x8961: out dx,al ; 00008961 EE '.'
mov bx,0x5 ; 00008962 BB0500 '...'
call xc638 ; 00008965 E8D03C '..<'
or al,0x4 ; 00008968 0C04 '..'
x896a: jmp short x896c ; 0000896A EB00 '..'

x896c: jmp short x896e ; 0000896C EB00 '..'

x896e: out dx,al ; 0000896E EE '.'
dec dx ; 0000896F 4A 'J'
dec dx ; 00008970 4A 'J'
x8971: inc dx ; 00008971 42 'B'
jmp short x8974 ; 00008972 EB00 '..'

x8974: jmp short x8976 ; 00008974 EB00 '..'

x8976: in al,dx ; 00008976 EC '.'
and al,0xf8 ; 00008977 24F8 '$.'
x8979: xor al,0x48 ; 00008979 3448 '4H'
mov bh,al ; 0000897B 8AF8 '..'
pop ax ; 0000897D 58 'X'
mov ah,bh ; 0000897E 8AE7 '..'
x8980: pop ds ; 00008980 1F '.'
pop si ; 00008981 5E '^'
pop dx ; 00008982 5A 'Z'
pop cx ; 00008983 59 'Y'
pop bx ; 00008984 5B '['
iret ; 00008985 CF '.'

Somewhere in there, there might be something wrong when running in UniPCemu. Or perhaps it's a basic issue with said BIOS itself?

This is what runs:

debugger_UniPCemu_initializingParallelPort.7z

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 4 of 7, by superfury

User metadata
Rank l33t++
Rank
l33t++

This is what I can conclude so far:

Details

entry point: f000:efd2 jmp 88ea(opcode e9h)
40:78 byte: time-out value for LPT1
40:08 word: LPT1 port address
BL=timeout
DX=LPT1(378h).

Bochs ioports.lst:
port 378: data port(w)
port 379: status port(r/w)
port 37a: control port(r/w)

f000:00008909: reached valid port(non-zero at 40:08). DX=port base, BL=timeout.
f000:0000895b: function 00h handler starting.
f000:00008961: write 8 to port 37a(raise select line, enabling the DSS)
f000:0000c638: delay function(based on BX).
f000:00008976: read status after raising bit 4 of 37a. Reads 07h.
f000:00008979: flip status bits 6(ACK, which is 0, becoming 1) and 3(error, which is 0, becoming 1). SELECT_IN is 0.
upon return(0070:000022a4 C3 ret), AH=48h.
AX=4800h, BX=e6f5h, CX=f802h, DX=0
SP=6FAh, BP=7BEh, SI=119h, DI=a45ah.

input was:
ah=0102h, bx=e6f5h, dx=2
SP=6F4h(start of interrupt), BP=7BEh, SI=119h, DI=a45ah.

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 5 of 7, by superfury

User metadata
Rank l33t++
Rank
l33t++

Just took another look at the DSS schematic (https://archive.org/details/dss-programmers-g … age/n3/mode/2up page 5)... It looks like the output signal of the buffer is actually NOT FULL, which drives a transistor to ground pin 10 when set.
So when the device is turned on and the buffer is not full, said signal is set and pin 10 is grounded( thus the input becomes 0 ).
If the device is turned off (pin 17=low because the chip can't drive it) or the buffer is full(chip is driving it low), said signal is effectively 0, causing the transistor to not ground pin 10(ACK). Then, the value becomes the input of line 16 instead(=line 16, the INIT line). So in that case, ACK=INIT.

So only in the off state and in the on state with full buffer, pin 10(ACK) becomes INIT because it isn't grounded. But pin 10(ACK) is flipped on the PC side, causing it to be inverted.

So, from the output pins(raw output signals on SELECT/INIT lines) until the input pins:
- Off(SELECT(pin 17)=0), pin 16(INIT not grounded)=>10(ACK), ACK is reversed so: ACK=!INIT
- On(SELECT(pin 17)=1) with full buffer(INIT not grounded): same as the off state, pin 16(INIT)=>10(ACK), ACK is reversed so: ACK=!INIT. So when INIT=1, ACK=0 and when INIT=0, ACK=1.
- On(SELECT(pin 17)=1) with not full buffer: INIT is grounded(becomes 0 always), then reversed on read, so ACK=1 always.

So in all cases, ACK=!INIT, except when SELECT is set with a not full buffer(which according to me is actually more like a "was not last full" flag instead of a is isn't full flag, meaning the definition of full/empty is simply the last full/empty state being reached) where INIT is grounded and ACK is 1 always.

Edit: So the INIT line is active low, so the value 0 being in the INIT bit becomes 1 to the ground, while the value 1 being in the init bit becomes 0 to the ground. So only when INIT isn't set, the buffer status can become 1 or a 0 when grounded.

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 6 of 7, by digger

User metadata
Rank Oldbie
Rank
Oldbie

There is just something fascinating about such a relatively simple device as the Disney Sound Source, wouldn't you agree? 🙂

Any way, I remember perusing through the official programming guide (which you can find at archive.org) and I believe the DSS never triggers an IRQ. The designers could have chosen to use the IRQ on the parallel port as a means to signal the host PC that the FIFO was full, but for some reason they chose not to, and decided to go with a status pin instead.

Maybe because not all parallel port implementations included an IRQ? Or maybe because it wasn't necessary, since the programmers were expected to continously monitor the status pin and keep the FIFO buffer fed in a backround routine piggy-backing on the system timer interrupt anyway, so triggering a hardware interrupt in addition to that would have been redundant?

Reply 7 of 7, by superfury

User metadata
Rank l33t++
Rank
l33t++

I think it's probably just meant to keep the FIFO full using a background process? And the only reason there's said full/not full bit instead of an completely full/completely emptied bit is to supply data into the FIFO in an OS-style? So use the FIFO full bit only to keep the cache as full as possible, much in the same way the Prefetch Input Queue(PIQ) of the x86 works best when continuously being completely filled? That's the only reason I can think of. Otherwise, couldn't they just implement the completely full(1)/completely empty(0) bit on the ACK line for parallel port IRQs and use the SelectIn pin(which is grounded with the DSS) as the FIFO full/not full bit instead? That would allow both methods to work?

Afaik all pins other than ACK in the status register are grounded or passthrough anyways?
Thay's error(pin 17, grounded externally and used to drive chip select), select_in(pin 15, passed through), paper_out(pin 12, passed through), busy(pin 11, passed through)?

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io