+------------------------------------------------------------------------+
|                    06 / Timers, UART and interrupts                    |
+------------------------------------------------------------------------+

BASELINE SFR DESK REFERENCE (BYTE ADDRESSES)
80 P0    81 SP    82 DPL   83 DPH   87 PCON
88 TCON  89 TMOD  8A TL0   8B TL1   8C TH0   8D TH1
90 P1    98 SCON  99 SBUF  A0 P2    A8 IE    B0 P3
B8 IP    D0 PSW   E0 ACC   F0 B
Bit-addressable: P0, TCON, P1, SCON, P2, IE, P3, IP, PSW, ACC, B. SP
resets to 07 and port latches to FF. Reset is not an instruction to clear
all RAM.

TIMER 0 AND TIMER 1
TMOD has one four-bit field per timer: Timer 1 in bits 7-4, Timer 0 in
bits 3-0. Each field is GATE, C/T, M1, M0. GATE=1 requires the
corresponding INT pin high AND TRn set. C/T=0 counts internal machine
cycles; C/T=1 counts sampled falling edges on T0/T1. Mode 0 is a 13-bit
counter (THn plus the low five bits of TLn); mode 1 is 16-bit; mode 2 is
8-bit auto-reload from THn into TLn. Mode 3 splits Timer 0 into two 8-bit
timers: TL0 keeps Timer 0 controls, TH0 borrows TR1/TF1. Timer 1's normal
interrupt/control use is consequently restricted; do not treat mode 3 as
two independent ordinary 16-bit timers. Timer 1 mode 3 stops its counting.

TCON bits: TF1 TR1 TF0 TR0 IE1 IT1 IE0 IT0. TRn starts a timer; TFn
records overflow. Timer overflow flags are cleared on hardware interrupt
vectoring; polling code must clear them itself. Stop the timer before
making a coherent multi-byte configuration change.

BARE-METAL POLLED TIMER (NOT DOS/51 INITIALIZATION)
    anl 0x89,#0xF0   ; preserve Timer 1 mode
    orl 0x89,#0x01   ; Timer 0 mode 1, internal clock
    clr 0x8C         ; TR0 (bit address)
    mov 0x8C,#0xFC   ; TH0 (byte address)
    mov 0x8A,#0x66   ; TL0: 65536 - 922
    clr 0x8D         ; TF0 (bit address)
    setb 0x8C
wait0:
    jnb 0x8D,wait0
    clr 0x8C
    clr 0x8D

At 11.0592 MHz, 922 ticks are approximately 1 ms. Polling/setup overhead
makes this NOT an exact 1-ms software delay. For periodic precision use a
suitable reload strategy and account for interrupt latency. External event
counting is sampled, so input edge rate and pulse widths must meet the
chip timing requirements.

UART: SCON = SM0 SM1 SM2 REN TB8 RB8 TI RI
Mode 0: synchronous shift register, normally fosc/12.
Mode 1: 8-bit UART with start/stop bits, variable baud.
Mode 2: 9-bit UART, fixed fosc/64 or fosc/32 via SMOD.
Mode 3: 9-bit UART, variable baud.
REN enables reception. Write SBUF to transmit; read SBUF to retrieve the
separately buffered received byte. RI and TI share one interrupt vector
and are cleared by SOFTWARE, not by reading SBUF or entering the ISR. Mode
1's RB8 reflects the received stop bit when appropriate; modes 2/3 use
TB8/RB8 for the ninth data bit and SM2 for multiprocessor filtering.

With Timer 1 mode 2, UART modes 1/3 use:
baud = (2^SMOD * fosc) / (384 * (256 - TH1)).
At 11.0592 MHz: TH1=FD and SMOD=0 gives 9600; TH1=FD and SMOD=1 gives
19200; TH1=FF and SMOD=1 gives 57600. Arbitrary baud rates are not all
representable.

BARE-METAL 9600-BAUD SETUP (11.0592 MHz)
    anl 0x87,#0x7F   ; clear SMOD, preserve other PCON bits
    anl 0x89,#0x0F
    orl 0x89,#0x20   ; Timer 1 mode 2
    mov 0x8D,#0xFD   ; TH1 reload
    mov 0x8B,#0xFD   ; TL1 initial count
    mov 0x98,#0x50   ; mode 1, REN=1, RI/TI=0
    setb 0x8E        ; TR1

Do not run that setup inside DOS/51: its console is already 57600 baud,
and Timer 1 belongs to it. To poll reception, wait for RI, read SBUF and
clear RI. A polled transmitter writes SBUF, waits for TI and clears TI;
ensure TI was not left set by an earlier transmission. At higher
throughput handle overrun in software: there is no general-purpose deep
hardware FIFO.

INTERRUPTS
Reset jumps to 0000. Baseline vectors: INT0=0003, Timer0=000B, INT1=0013,
Timer1=001B, serial=0023. Each vector is normally a short LJMP stub
because adjacent entries are only eight bytes apart. IE.7 (EA) globally
enables requests; IE bits 0-4 enable EX0, ET0, EX1, ET1 and ES. IP bits
select low/high priority in the baseline core. A high-priority handler can
preempt a low-priority handler, not vice versa; equal-priority service is
not reentrant. Fixed polling order resolves simultaneous equal-priority
requests in the vector order above.

IT0/IT1=1 selects falling-edge external interrupts; zero selects
active-low level requests. Release a level-triggered request before
returning or it can immediately retrigger. Switch bounce requires
debouncing. Hardware pushes PC only. Save every clobbered register
including PSW as necessary; restore in reverse order and finish with RETI
so the interrupt controller releases its in-service state. RET alone is
not an interrupt return. Serial ISRs test BOTH RI and TI. A register-bank
switch can reduce saves, but each nested priority level needs a
nonconflicting bank and RAM allocation.

On DOS/51 the lower CODE vectors are in ROM. An application at 8000 cannot
patch them just by writing that numerical XDATA address. Use documented
firmware facilities or a different ROM build rather than assuming vector
ownership.

[ DOS/51 FIELD MANUAL | IMANIA.DK | 2026-11-14 ]
