Hi.
As far as I can tell from testing, the BIOS INT 14H doesn't
work on most real hardware I have tried (computers made
maybe 10 years ago that still have a serial port).
So I am gearing up to replace that with 80386 PM32 code.
I have some comms routines for MSDOS (pdcomm) that I
wrote decades ago, but I want to do it a different way this time.
Less sophisticated, but more straightforward.
And designed for a single-tasking system with only one
CPU enabled (PDOS/386).
Most of the code can be done in C, but the last bit I want
to do in assembler.
Prior to hitting the assembler, I will have installed the
new interrupt address (which is assembler code, gotint,
below).
I'm just trying to confirm the sequence in the final assembler.
outb port1, transmit_byte
outb port2, tbemask ; enable transmit buffer empty (only)
xxx:
hlt ; this could get interrupted by timer interrupts and
; then processing continues so we need a jmp
jmp xxx
gotint: ; this is the interrupt address installed by C caller
outb port2, oldmask ; restore previous interrupt mask (everything disabled) add esp, 12 ; we don't return to the previous instruction, which was hlt
; instead we skip over the return address, segment and flags
ret ; return to C caller which will do the EOI or whatever
; via separate calls to individual simple assembler functions
; like outportb()
Note that this was inspired by something similar I wrote
for S/370.
It's basically quite minimal assembler and straightforward.
There is a loop in the assembler, which I didn't have in my
old routines, but it's not really processing logic.
After transmit is working I'll try a variation of the above for receive.
I especially don't know if these two need to be swapped:
outb port1, transmit_byte
outb port2, tbemask ; enable transmit buffer empty (only)
I don't want to miss an interrupt. I want the order to
guarantee that I will get the interrupt. ie if I have already
attempted to write the transmit byte, will the interrupt
pend until I enable it, or will it be skipped?
Or is the other way around? If I haven't enabled interrupts
will it just transmit the byte and not bother interrupting?
Assume that the transmit is very fast, or the CPU is very
slow, so that there is a gap between the two outb
instructions where a decision is made on whether to
interrupt or not.
Thanks. Paul.
After saving the received byte I optionally loop back until the input
buffer is empty (more modern PCs had a 16-byte buffer you could enable).
If you are interested I can see if I can locate my source code...
at this point I re-enable all other interrupts
/ enable interrupts and then halt until interrupt hit
_hltintgo:
sti
hloop:
/ I believe hlt will be interrupted by other interrupts, like
/ the timer interrupt, so we need to do it in a loop
hlt
jmp hloop
_hltinthit:
/ remove return address, segment and flags from the stack as we
/ do not intend to return to the jmp following the hlt instruction
/ that was likely interrupted
add %esp, 12
/ note that interrupts will be disabled again (I think) by virtue
/ of the fact that an interrupt occurred. The caller would have
/ disabled interrupts already, so we are returning to the same
/ disabled state.
ret
The first time through, you hlt with interrupts enabled, so will wake up
on the fist interrupt.
But when you loop, you'll hlt again, this time with interrupts disabled,
and will never ever wake up again (other than for an NMI).
I committed code that I expected to work:
https://sourceforge.net/p/pdos/gitcode/ci/3260d2aabe9f133f89bae3f9148070c188eb42b1/
But I am only getting the first character transmitted,
and then it never returns, thus never reboots.
So I need to begin debugging.
It was thus said that the Great Paul Edwards <muta...@nospicedham.gmail.com> once stated:
I committed code that I expected to work:
https://sourceforge.net/p/pdos/gitcode/ci/3260d2aabe9f133f89bae3f9148070c188eb42b1/
But I am only getting the first character transmitted,
and then it never returns, thus never reboots.
So I need to begin debugging.So if you are writing code that only runs one program at a time, why do
you even need to mess with interrupts in the first place? Assuming a C funcion inb() to read a byte from an IO port, and outb() to write a byte to an IO port, I would think this would work:
/* Assuming UART has been initialized, but its IRQ has been disabled */ static void writecomm(int c)
{
/* read line status register to detect if the xmit buffer */
/* is ready to send. */
while (inb(0x3f8+5) & 0x20 == 0)
{
/* do nothing but wait */
}
/* we can now write the data */
outb(0x3f8,c);
}
It seems simpler to me than trying to muck with interrupts and adjusting
the return stack and all that.
-spc
So if you are writing code that only runs one program at a time, why do
you even need to mess with interrupts in the first place? Assuming a C
funcion inb() to read a byte from an IO port, and outb() to write a byte to >> an IO port, I would think this would work:
/* Assuming UART has been initialized, but its IRQ has been disabled */
static void writecomm(int c)
{
/* read line status register to detect if the xmit buffer */
/* is ready to send. */
while (inb(0x3f8+5) & 0x20 == 0)
{
/* do nothing but wait */
}
/* we can now write the data */
outb(0x3f8,c);
}
It seems simpler to me than trying to muck with interrupts and adjusting
the return stack and all that.
My solution is in-between those two extremes.
I only support a single task but I don't run hot polling.
I committed code that I expected to work:
So I need to begin debugging.
add %esp, 12
uartEnableTBE(&uart);
uartTxCh(&uart, ch);
uartEnableTBE(&uart);
On Saturday, April 15, 2023 at 10:36:37 PM UTC+8, Paul Edwards wrote:
I finally realized that since I'm getting an interrupt from
the TBE enable (for unknown reasons), then if I moved
the disable (cli) before that, then by the time I had
outputted a byte, the interrupt would still be pending
and even if I didn't get one for the outputted byte, it
was enough to get one for the TBE call.
And now it is working, with the simple design.
Note that it is working (and previously failing) under
Bochs. I haven't tried real hardware yet.
And now I realize there may be a problem with the
current code.
Let's say the serial port is slow.
The sequence I am doing is enabling TBE and then outputting
a byte.
Enabling TBE generates an interrupt, but outputting the byte
only randomly does (could also be a Bochs bug).
Because I am now relying on the TBE enable interrupt to get
me out of the HLT loop, I am no longer have the desired
constraint on the OUT instruction completing.
Meaning the second time through the loop, the second OUT
could be executed before the first one has completed.
If the UART discards the TBE interrupt when it realizes that
it is no longer the case that the transmit buffer is empty,
because there has been an OUT instruction issued since
then, then my current design should work.
Does anyone know what is happening?
Thanks. Paul.
I finally realized that since I'm getting an interrupt from
the TBE enable (for unknown reasons), then if I moved
the disable (cli) before that, then by the time I had
outputted a byte, the interrupt would still be pending
and even if I didn't get one for the outputted byte, it
was enough to get one for the TBE call.
And now it is working, with the simple design.
And now it is working, with the simple design....
I will look into refinements now that the basics are working.
static void writecomm(int port, int ch)
{
old1 = G_intloc[(intno + 0xb0) * 2];
old2 = G_intloc[(intno + 0xb0) * 2 + 1];
intaddr = (unsigned long)hltinthit;
intdesc1 = (0x8 << 16) | (intaddr & 0xffff);
intdesc2 = (intaddr & 0xffff0000)
| (1 << 15)
| (0 << 13)
| (0x0e << 8);
G_intloc[(intno + 0xb0) * 2] = intdesc1;
G_intloc[(intno + 0xb0) * 2 + 1] = intdesc2;
G_intloc[(intno + 0xb0) * 2] = old1;
G_intloc[(intno + 0xb0) * 2 + 1] = old2;
}
How do you guarantee that the interrupt is directed to your thread
that's sitting in a HLT state?
I replied once with some code, but it seems you didn't see it, so I'm replying again.
Why are you installing, then uninstalling, the interrupt handler for each character?
And I think you are making this out to be more complicated that
it should be.
As I wrote before, the interrupt handler for the UART can be
as simple as:
pause:
hlt ; halt CPU
bne pause ; if not, keep waiting
You need to tell both the 8259 and the UART that the interrupt has been handled.
And now I realize there may be a problem with the
current code.
Sysop: | Keyop |
---|---|
Location: | Huddersfield, West Yorkshire, UK |
Users: | 546 |
Nodes: | 16 (2 / 14) |
Uptime: | 02:51:40 |
Calls: | 10,387 |
Calls today: | 2 |
Files: | 14,061 |
Messages: | 6,416,755 |