Two bidirectional Software UARTs with AT89S52 microcontroller I have learned one way to read from a serial port via software UART is via timer interrupts. I have made an implementation of receiving data from one serial port below. I haven't tested it yet because I now also need a way to efficiently transmit data as well without causing bad timings since UART is a timer sensitive protocol. If possible, I want to use one timer to manage both UARTs.

The speed I'm aiming for is 9600bps, and my microcontroller has a crystal speed of 22.1184Mhz. Also, is my code optimized or could I optimize it further so that fewer clock cycles are required for serial processing?

  RX equ P3.0 ;receive bit
  TX equ P3.1 ;xmit bit
  sermode equ 23h ;serial mode.
  SWBUF equ 22h     ;stored data
  SWTBUF equ 24h    ;stored data temporarily
  SWI bit 0h    ;1=received valid data

  org 0000h ;machine start
  ljmp startapp

  org 000Bh ;Timer interrupt
  ljmp timerint

  org 0030h

  timerint:
push PSW
mov C,RX ;get bit asap
push ACC
mov TH0,#0FFh ;reset high timer byte
mov A,sermode
jnz serhalfbit
  ;entered every half bit time if sermode=0
  ;do nothing till bit is zero
  jc nostart
    ;set mode to 1
    inc sermode
    ;set access speed to slow
    mov TL0,#040h
    ;prepare to exit
    sjmp extimerint
  nostart:
serhalfbit:
jz serfullbit
  ;this is entered every full bit time if sermode &gt; 0
  jnb ACC.0,noend
    jnb ACC.3,noend
      ;if sermode=9, reset it...
      mov sermode,#0h
      ;set usable buffer to temporary buffer value
      mov SWBUF,SWTBUF
      ;set SWI to stop bit value. 1=valid data...
      mov SWI,C
      ;set access speed to fast
      mov TL0,#0A0h
    sjmp extimerint
  noend:
  ;if sermode=1 to 8, get bit into a buffer and increment mode
  mov A,SWTBUF
  RRC A
  mov SWTBUF,A
  inc sermode
serfullbit:
extimerint:
;common exit from interrupt
pop ACC
pop PSW
  reti

  startapp:
mov P1,#0FFh        ;reset GPIO
mov P3,#0FFh
mov sermode,#0h ;start at beginning
mov TH0,#0FFh
mov TL0,#0A0h   ;9.6kbps half bit time
mov TMOD,#11h   ;16-bit timers
setb PT0        ;timer 0=high priority
setb TR0        ;timer 0 on
mov IE,#81h         ;en Timer 0 int

bigloop:
; --async function here--
jnb SWI,novalid
  clr SWI
  mov A,SWBUF
  ; A=received serial data
novalid:
sjmp bigloop
You can spare a lot of trouble and cycles by configuring P3.0 as an interrupt source. This way start bit detection would not require constant polling.
I don't see why do you test ACC.0.
sermode is really a baud counter. Consider renaming.
I strongly recommend to sample each baud 3 or 5 times, and signal an error if samples disagree.
For testing the receiver, use a real RS232 part to transmit data. You may also want to set aside a spare pin, let the timer interrupt to toggle it, and observe the waveform on a scope.