$mod51
$debug

	mcon    equ     0c6h
	ta      equ     0c7h

	org     0                  ; reset interrupt vector
	sjmp    start

	org     0bh                ; timer 0 overflow interrupt vector
	ajmp    timerint

	org     2bh                ; power fail interrupt vector
	ajmp    powerfail

	org     30h                ; message strings
crlf:   db      0dh,0ah,'$'

timer:  db      17, 50, 08, 15     ; January
	db      18, 35, 07, 40     ; February
	db      19, 15, 06, 50     ; March
	db      19, 55, 05, 50     ; April
	db      20, 35, 05, 05     ; May
	db      21, 00, 04, 45     ; June
	db      20, 55, 05, 00     ; July
	db      20, 15, 05, 30     ; August
	db      19, 15, 06, 15     ; September
	db      18, 20, 06, 55     ; October
	db      17, 35, 07, 40     ; November
	db      17, 25, 08, 10     ; December


	org     78h                ; start of main program code
start:  mov     sp,#17h            ; allow use of Rn banks 00, 01, and 10
	acall   inithw             ; initialize hardware

	setb    rs1                ; switch to controller register space
	mov     r5,#3              ; R5:R4 = controller set point
	mov     r4,#232
	mov     r3,#0fh            ; R3:R2 = current HV control value
	mov     r2,#0ffh
	setb    psw.5              ; start with control/transmit enabled
	clr     rs1                ; return to default register space
	mov     r7,#6              ; initialize counter variables used in
	mov     r6,#25             ;   the timer interrupt handler
	
main:
	sjmp    main               ; continue main loop




; ReadADC.  Get 12 bit value from the MAX186 on port 1.  R1 selects the
;    channel to read (0 to 8), or more specifically, SEL2-SEL0.  The most
;    significant nybble of conversion is returned in R1 bits 3-0, and the
;    least significant byte is returned in R0.  Pin assignments:
;    P1.0 <-- Dout, P1.1 --> Din, P1.2 --> SCLK, P1.3 --> CS.
;    (Operates in currently selected register space).

readadc:
	clr     p1.3               ; take CS low to wake up the MAX186
	mov     a,r1               ; prepare a control byte
	rl      a                  ; channel select bits need to end up
	rl      a                  ;   in a.6 through a.4
	rl      a
	rl      a
	orl     a,#10001110b       ; complete the control byte

	mov     r0,#8              ; initialize bit counter
nbit1:  rlc     a                  ; send the control byte, MSB first
	mov     p1.1,c
	setb    p1.2               ; pulse SCLK between bits
	clr     p1.2
	djnz    r0,nbit1           ; repeat for all eight control bits

	nop                        ; wait 10us to insure complete conversion
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop

	clr     a                  
	mov     r0,#4              ; initialize bit counter
nbit2:  setb    p1.2               ; shift high nybble of result into acc,
	clr     p1.2               ;   pulsing SCLK between bits
	mov     c,p1.0
	rlc     a
	djnz    r0,nbit2           ; repeat to get four bits
	mov     r1,a               ; store the high nybble in R1

	mov     r0,#8              ; initialize bit counter
nbit3:  setb    p1.2               ; shift lower two nybbles of result into
	clr     p1.2               ;   accumulator, pulsing SCLK between bits
	mov     c,p1.0
	rlc     a
	djnz    r0,nbit3           ; repeat to get eight bits
	mov     r0,a               ; store the low byte in R0

	setb    p1.2               ; shift out the remaining four bits
	clr     p1.2               ;   (zeros) required by the MAX186
	setb    p1.2
	clr     p1.2
	setb    p1.2
	clr     p1.2
	setb    p1.2
	clr     p1.2
	setb    p1.3               ; take CS high

	ret

		
		 
; WriteDAC.  Send 12 bit value (most significant nybble in R3 bits 3-0,
;    least significant byte in R2) to the DAC-8043 on port 1.  Pin
;    assignments:  P1.4 --> SRI, P1.5 --> CLK, P1.6 --> LD.
;    (Operates in currently selected register space).

writedac:
	mov     b,r3               ; save R3 register (caller needs it)
	mov     a,r3               ; put upper nybble in accumulator
	rlc     a                  ; prepare to shift out the MSB
	rlc     a
	rlc     a
	rlc     a

	mov     r3,#4              ; initialize bit counter
nbit4:  rlc     a                  ; shift four bits out to SRI,
	mov     p1.4,c             ;   providing a clock pulse on CLK
	setb    p1.5               ;   between each one
	clr     p1.5
	djnz    r3,nbit4           ; repeat four times

	mov     a,r2               ; put lower two nybbles in accumulator

	mov     r3,#8              ; initialize bit counter
nbit5:  rlc     a                  ; shift eight bits out to SRI,
	mov     p1.4,c             ;   providing a clock pulse on CLK
	setb    p1.5               ;   between each one
	clr     p1.5
	djnz    r3,nbit5           ; repeat eight times

	clr     p1.6               ; pulse LD low to load the DAC register
	setb    p1.6

	mov     r3,b               ; restore R3 register

	ret



; BinToFloat.  Converts a 12 bit value (most significant nybble in R1 bits
;    3-0, least significant byte in R0) into an ASCII floating point string
;    of the form wxyz.  The ASCII bytes for each digit are returned in R3,
;    R2, R1, and R0 (for digits w, x, y, and z, respectively) of the RS=01
;    register space.  May only be called from the RS=10 register space.

bintofloat:
	mov     a,r0               ; transfer parameters to new Rn space
	clr     rs1                ;   and remain there for this subroutine
	setb    rs0
	mov     r0,a
	setb    rs1
	clr     rs0
	mov     a,r1
	clr     rs1
	setb    rs0
	mov     r1,a

	mov     r3,#0ffh
	mov     a,r0
	clr     c
subtra: inc     r3
	mov     b,a
	subb    a,#100
	jnc     subtra
	clr     c
	cjne    r1,#0,next
	sjmp    skip

next:   djnz    r1,subtra
subtra2:mov     b,a
	subb    a,#100
	inc     r3
	jnc     subtra2

skip:   mov     a,b
	mov     b,#10
	div     ab
	add     a,#48              ; convert to ASCII
	mov     r1,a               ; R1 now has the 10's place
	mov     a,b
	add     a,#48              ; convert to ASCII
	mov     r0,a               ; R0 now has the 1's place

	mov     a,r3
	mov     b,#10
	div     ab
	add     a,#48              ; convert to ASCII
	mov     r3,a               ; R3 now has the 1000's place
	mov     a,b
	add     a,#48              ; convert to ASCII
	mov     r2,a               ; R2 now has the 100's place

	setb    rs1                ; restore controller Rn space
	clr     rs0
	ret



; SendFloat.  Takes the four ASCII numeric characters returned by BinToFloat
;    (R3 . R2 R1 R0 in the RS=01 register space) and sends them to the serial
;    port after a leading space character.  May only be called from the RS=10 
;    register space.

sendfloat:
	clr     rs1                ; values are in separate Rn space
	setb    rs0

	jnb     ti,$               ; send a space character
	clr     ti
	mov     sbuf,#' '

	jnb     ti,$               ; send ones place
	clr     ti
	mov     sbuf,r3

	jnb     ti,$               ; send decimal point
	clr     ti
	mov     sbuf,#'.'

	jnb     ti,$               ; send tenths place
	clr     ti
	mov     sbuf,r2

	jnb     ti,$               ; send hundredths place
	clr     ti
	mov     sbuf,r1

	jnb     ti,$               ; send thousandths place
	clr     ti
	mov     sbuf,r0

	setb    rs1                ; restore controller Rn space
	clr     rs0
	ret


	
; SendStr.  Sends characters pointed to by DPTR out the serial port until
;    a '$' character is encountered.  Uses currently selected register space.

sendstr:
	mov     a,#0
	movc    a,@a+dptr
	cjne    a,#'$',sendnext
	ret
sendnext:
	jnb     ti,$
	clr     ti
	mov     sbuf,a
	inc     dptr
	sjmp    sendstr



; Add16.  Adds the 16-bit number in R1:R0 to the 16-bit number in R3:R2, and
;    stores the result in R3:R2.  Uses the currently selected register space.

add16:  mov     a,r2
	add     a,r0
	mov     r2,a
	mov     a,r3
	addc    a,r1
	mov     r3,a

	ret



; Sub16.  Subtracts the 16-bit number in R5:R4 from the 16-bit number in
;    R1:R0, and stores the result in R1:R0.  Uses the currently selected
;    register space.

sub16:  clr     c
	mov     a,r0
	subb    a,r4
	mov     r0,a
	mov     a,r1
	subb    a,r5
	mov     r1,a

	ret



; DivBy2.  Divides the 16-bit number in R1:R0 by two, preserving the sign.
;    Uses the currently selected register space.

divby2: clr     c
	mov     a,r1
	anl     a,#80h
	jz      positive
	setb    c
positive:
	mov     a,r1
	rrc     a
	mov     r1,a

	mov     a,r0
	rrc     a
	mov     r0,a

	ret



; Trunc12.  Examines the 16-bit signed integer in R3:R2 and truncates it
;    to a 12-bit unsigned integer.  Values < 0 are truncated to 0 and
;    values > 0xfff are truncated to 0xfff.  Uses the currently selected
;    register space.

trunc12:
	mov     a,r3
	rlc     a
	jc      isneg              ; test sign bit
	anl     a,#0e0h            ; check other three bits past 12-bit limit
	jz      isok
	mov     r3,#0fh            ; R3:R2 > 0xfff, so truncate to 0xfff
	mov     r2,#0ffh
	ret
isneg:  mov     r3,#0              ; R3:R2 < 0, so truncate to 0
	mov     r2,#0

isok:   ret                        ; 0 < R3:R2 < 0xfff, so leave as-is 



; InitHW.  Initializes the serial port, ADC, DAC, and processor safety
;    features at beginning of program execution.

inithw: mov     r3,#0fh            ; send 0xfff to the DAC so we will
	mov     r2,#0ffh           ;   start with the PMT high voltage
	acall   writedac           ;   turned off

	mov     ta,#0aah           ; timed access
	mov     ta,#55h
	anl     pcon,#0fbh         ; disable watchdog timer temporarily     

	mov     ie,#82h            ; enable only the timer 0 interrupt
	mov     tmod,#21h          ; select timer 1 mode 2 (auto reload)
				   ;   and timer 0 mode 1 (16 bit)
	mov     th1,#0f0h          ; this is correct for 1200 bps with a
	mov     tl1,#0f0h          ;   7.3728 MHz crystal
	mov     th0,#10h           ; this will generate overflow interrupts
	mov     tl0,#08h           ;   every 0.1s (10 Hz)
	mov     scon,#52h          ; select serial mode 1
	mov     tcon,#40h          ; start up timer 1 only (for serial port)

	mov     r3,#0eh            ; allow roughly 3 seconds for the TNC
loop2:  mov     r2,#0ffh           ;   to power up and arrive at the command
loop1:  mov     r1,#0ffh           ;   prompt
	djnz    r1,$
	djnz    r2,loop1
	djnz    r3,loop2

	jnb     ti,$               ; put the TNC into transparent mode by
	clr     ti                 ;   sending "tr"
	mov     sbuf,#'t'
	jnb     ti,$
	clr     ti
	mov     sbuf,#'r'
	jnb     ti,$
	clr     ti
	mov     sbuf,#0dh

	mov     ta,#0aah           ; timed access
	mov     ta,#55h
	setb    ip.7               ; set RWT (reset watchdog timer)
	mov     ta,#0aah           ; timed access
	mov     ta,#55h
	orl     pcon,#4ch          ; set POR, EWT, and EPFW     

	setb    tr0                ; start timer 0 for 10 Hz interrupts

	acall   readadc            ; "prime" the analog input channels
	acall   readadc
	
	ret



; PowerFail.  ISR for the power fail interrupt; sets high voltage to zero
;    and takes any other action necessary to prepare for program termination,
;    then waits in a loop until Vcc again reaches a safe level or program
;    execution is terminated.

powerfail:
	push    acc
	mov     r3,#0fh            ; turn off high voltage to the PMT
	mov     r2,#0ffh
	acall   writedac

danger: acall   resetwt            ; wait until the danger condition passes
	mov     a,pcon             ;   or execution is terminated
	mov     a,pcon
	anl     a,#20h
	jnz     danger
	pop     acc
	reti



; TimerInt.  ISR for a periodic 10 Hz interrupt generated by timer 0.
;    This code utilizes the RS=10 register space (the "controller space")
;    as well as the standard RS=00 space.

timerint:
	clr     tr0                ; halt timer
	mov     th0,#10h           ; reload for another 0.1s run
	mov     tl0,#08h
	setb    tr0                ; restart timer

	acall   gettime            ; fetch date/time information
	clr     rs1                ; go to the date/time register space
	setb    rs0
	
	mov     a,r4               ; convert BCD month into decimal month-1
	jnb     acc.4,notens
	anl     a,#11101111b
	add     a,#9
	sjmp    testtime
notens: clr     c
	subb    a,#1
testtime:                          
	mov     b,#4               ; multiply value by 4 to index into the
	mul     ab                 ;   times array
	jnb     psw.5,testtim2     ; add two if we want to look at a potential
	add     a,#2               ;   turnoff (to fetch sunrise times)
testtim2:        
	mov     r7,a               ; save this index value for later uses
	
	mov     a,r2               ; convert BCD hours into decimal hours
	anl     a,#0f0h
	rr      a
	rr      a
	rr      a
	rr      a
	mov     b,#10
	mul     ab
	mov     b,r2
	anl     b,#0fh
	add     a,b
	mov     b,a
	mov     a,r7
	mov     dptr,#timer        ; fetch the hours value from the array,
	movc    a,@a+dptr          ;   appropriate for the current month
	cjne    a,b,nomatch        ; test hour
	
	mov     a,r1               ; convert BCD minutes into decimal minutes
	anl     a,#0f0h
	rr      a
	rr      a
	rr      a
	rr      a
	mov     b,#10
	mul     ab
	mov     b,r1
	anl     b,#0fh
	add     a,b
	mov     b,a
	mov     a,r7
	inc     a                  ; fetch the minutes value from the array,
	movc    a,@a+dptr          ;   appropriate for the current month
	cjne    a,b,nomatch        ; test minute
	
	cpl     psw.5              ; change modes from nighttime (xmit) to
				   ;   daytime (standby), or vice-versa
	jb      psw.5,nomatch
	setb    rs1
	clr     rs0
	mov     r3,#0fh            ; turn off high voltage to the PMT
	mov     r2,#0ffh
	acall   writedac
	acall   resetwt
	clr     rs1
	clr     rs0
	reti

nomatch:
	clr     rs1                ; insure that we start out in default
	clr     rs0                ;   register space
	
	jnb     psw.5,short        ; do nothing if the "transmit bit" is zero

	djnz    r7,short1          ; return early if not yet time for a control
				   ;   action
	mov     r7,#6              ; schedule a control cycle every ~0.6s
	sjmp    ctrl

short1: ajmp    short

ctrl:   setb    rs1                ; switch to controller register space
	clr     rs0
	mov     r1,#0              ; read PMT signal
	acall   readadc
	mov     a,r1               ; save PMT value for possible later display
	mov     r7,a
	mov     a,r0
	mov     r6,a
	acall   sub16              ; subtract set point (R5:R4) from PMT
	acall   divby2             ;   level (R1:R0), and divide resultant
	acall   divby2             ;   (R1:R0) by eight
	acall   divby2
	acall   add16              ; add result to current HV control value
	acall   trunc12            ;   (R3:R2) and truncate to positive 12-bit
				   ;   range limits
	acall   writedac           ; take control action
	acall   resetwt            ; reset watchdog timer

	clr     rs1                ; switch to default register space
	clr     rs0
	djnz    r6,short           ; return without transmitting a status line
				   ;   if it is not yet time
	mov     r6,#25             ; schedule a status run every ~15s
	mov     r7,#3              ; schedule a control cycle sooner than
				   ;   usual, since the status transmission
				   ;   takes ~0.34s
	setb    rs1                ; switch to controller register space
	clr     rs0

	clr     tr0                ; this is gonna take >> 0.1 seconds...

	acall   sendtime           ; send current time/date to serial port
	acall   resetwt
	
	mov     a,r3               ; display HV value
	mov     r1,a 
	mov     a,r2
	mov     r0,a
	acall   bintofloat
	acall   sendfloat
	acall   resetwt

	mov     a,r7               ; retrieve and display last PMT value
	mov     r1,a
	mov     a,r6
	mov     r0,a
	acall   bintofloat
	acall   sendfloat
	acall   resetwt
	
	mov     r1,#4              ; display PD value
	acall   readadc
	acall   bintofloat
	acall   sendfloat
	acall   resetwt

	mov     r1,#1              ; display temperature value
	acall   readadc
	acall   bintofloat
	acall   sendfloat
	acall   resetwt
 
	mov     dptr,#crlf         ; add CR/LF at end of line
	acall   sendstr
	acall   resetwt

	clr     rs1                ; restore default register space
	clr     rs0

	mov     th0,#7ch           ; these should be tweaked to equalize delays
	mov     tl1,#00h           ;   depending on the time required for
	setb    tr0                ;   transmitting the status line

short:  acall   resetwt            ; reset watchdog timer
	reti



; ResetWT.  Resets the watchdog timer on-board the DS5000T processor module.

resetwt:
	mov     ta,#0aah           ; timed access
	mov     ta,#55h
	setb    ip.7               ; set RWT
	ret



; GetTime.  Reads the DS1215 Real-Time Clock onboard the DS5000T processor
;    module.  GetTime may only be called from the RS=00 (default) 
;    register space.

gettime:
	clr     rs1                ; switch to a spare set of Rn registers
	setb    rs0
	mov     mcon,#0f8h         ; turn off CE2 for memory access

	acall   open               ; set up to read date/time

	acall   rbyte              ; read hundredths/tenths of a second
	mov     r6,a

	acall   rbyte              ; read ones/tens of seconds
	mov     r0,a

	acall   rbyte              ; read minutes
	mov     r1,a

	acall   rbyte              ; read hours
	mov     r2,a

	acall   rbyte              ; discard day of week
	acall   rbyte              ; read date
	mov     r3,a

	acall   rbyte              ; read month
	mov     r4,a

	acall   rbyte              ; read year
	mov     r5,a

	acall   close              ; close clock register
	
	clr     rs1                ; go back to default register space
	clr     rs0
	ret



; SendTime.  Sends a concise time/date ASCII string out the serial port,
;    given the data in RS=01 from the GetTime routine.  SendTime may only
;    be called from the RS=10 (controller) register space.

sendtime:
	clr     rs1                ; switch to a spare set of Rn registers
	setb    rs0
     
	mov     a,r2               ; send first hour digit
	anl     a,#0f0h
	rr      a
	rr      a
	rr      a
	rr      a
	add     a,#30h
	jnb     ti,$
	clr     ti
	mov     sbuf,a

	mov     a,r2               ; send second hour digit
	anl     a,#0fh
	add     a,#30h
	jnb     ti,$
	clr     ti
	mov     sbuf,a

	mov     a,r1               ; send first minute digit
	anl     a,#0f0h
	rr      a
	rr      a
	rr      a
	rr      a
	add     a,#30h
	jnb     ti,$
	clr     ti
	mov     sbuf,a

	mov     a,r1               ; send second minute digit
	anl     a,#0fh
	add     a,#30h
	jnb     ti,$
	clr     ti
	mov     sbuf,a

	mov     a,r0               ; send first second digit
	anl     a,#0f0h
	rr      a
	rr      a
	rr      a
	rr      a
	add     a,#30h
	jnb     ti,$
	clr     ti
	mov     sbuf,a

	mov     a,r0               ; send second second digit
	anl     a,#0fh
	add     a,#30h
	jnb     ti,$
	clr     ti
	mov     sbuf,a

	jnb     ti,$               ; send "."
	clr     ti
	mov     sbuf,#'.'

	mov     a,r6               ; send tenths of a second digit
	anl     a,#0f0h
	rr      a
	rr      a
	rr      a
	rr      a
	add     a,#30h
	jnb     ti,$
	clr     ti
	mov     sbuf,a

	jnb     ti,$               ; send " "
	clr     ti
	mov     sbuf,#' '

	mov     a,r4               ; send first month digit
	anl     a,#0f0h
	rr      a
	rr      a
	rr      a
	rr      a
	add     a,#30h
	jnb     ti,$
	clr     ti
	mov     sbuf,a
	
	mov     a,r4               ; send second month digit
	anl     a,#0fh
	add     a,#30h
	jnb     ti,$
	clr     ti
	mov     sbuf,a

	mov     a,r3               ; send first date digit
	anl     a,#0f0h
	rr      a
	rr      a
	rr      a
	rr      a
	add     a,#30h
	jnb     ti,$
	clr     ti
	mov     sbuf,a

	mov     a,r3               ; send second date digit
	anl     a,#0fh
	add     a,#30h
	jnb     ti,$
	clr     ti
	mov     sbuf,a

	mov     a,r5               ; send first year digit
	anl     a,#0f0h
	rr      a
	rr      a
	rr      a
	rr      a
	add     a,#30h
	jnb     ti,$
	clr     ti
	mov     sbuf,a

	mov     a,r5               ; send second year digit
	anl     a,#0fh
	add     a,#30h
	jnb     ti,$
	clr     ti
	mov     sbuf,a

	setb    rs1                ; go back to controller register space
	clr     rs0
	ret



;******************************************
;**** SUBROUTINE TO OPEN TIMEKEEPER *******
;******************************************
;
;  This subroutine executes the sequence of reads and writes which
;  is required in order to open communication with the timekeeper.
;  The subroutine returns with the timekeeper opened for data
;  access and with both the accumulator and B register modified.
;
OPEN:           ACALL           CLOSE           ; Make sure it is closed.
		MOV             B,#4            ; Set pattern period count.
		MOV             A,#0C5H         ; Load first byte of pattern.
OPENA:          ACALL           WBYTE           ; Send out the byte.
		XRL             A,#0FFH         ; Generate next pattern byte.
		ACALL           WBYTE           ; Send out the byte.
		SWAP            A               ; Generate next pattern byte.
		DJNZ            B,OPENA         ; Repeat until 8 bytes sent.
		RET                             ; Return.


;******************************************
;**** SUBROUTINE TO CLOSE TIMEKEEPER ******
;******************************************
;
;  This subroutine insures that the registers of the timekeeper
;  are closed by executing 9 successive reads of the date and time
;  registers.  The subroutine returns with both the accumulator
;  and the B register modified.
;
CLOSE:          MOV             B,#9            ; Set up to read 9 bytes.
CLOSEA:         ACALL           RBYTE           ; Read a byte;
		DJNZ            B,CLOSEA        ; Loop for 9 byte reads.
		RET                             ; Return.


;******************************************
;**** SUBROUTINE TO READ A DATA BYTE ******
;******************************************
;
;  This subroutine performs a "context switch" to the CE2 data
;  space and then reads one byte from the timekeeping device.
;  Then it switches back to the CE1 data space and returns
;  the byte read in the accumulator, with all other registers
;  unchanged.
;
RBYTE:          PUSH            DPL             ; Save the data
		PUSH            DPH             ;    pointer on stack.
		PUSH            MCON            ; Save MCON register.
		ORL             MCON,#4         ; Switch to CE2.
		PUSH            B               ; Save the B register.
		MOV             DPL ,#4         ; Set up for data input.
		MOV             DPH, #0         ; Set high address byte.
		MOV             B,   #8         ; Set the bit count.
LI:             PUSH            ACC             ; Save the accumulator.
		MOVX            A,   @DPTR      ; Input the data bit.
		RLC             A               ; Move it to carry.
		POP             ACC             ; Get the accumulator.
		RRC             A               ; Save the data bit.
		DJNZ            B,   LI         ; Loop for a whole byte.
		POP             B               ; Restore the B register.
		POP             MCON            ; Restore MCON register.
		POP             DPH             ; Restore the data
		POP             DPL             ;    pointer from stack.
		RET                             ; Return.


;******************************************
;**** SUBROUTINE TO WRITE A DATA BYTE *****
;******************************************
;
;  This subroutine performs a "context switch" to the CE2 data
;  space and then writes one byte from the accumulator to the
;  timekeeping device.  Then it switches back to the CE1 data
;  space and returns with all registers unchanged.
;
WBYTE:          PUSH            DPL             ; Save the data
		PUSH            DPH             ;    pointer on stack.
		PUSH            MCON            ; Save MCON register.
		ORL             MCON,#4         ; Switch to CE2.
		PUSH            B               ; Save the B register.
		MOV             DPH, #0         ; Set high address byte.
		MOV             B,   #8         ; Set the bit count.
LO:             PUSH            ACC             ; Save the accumulator.
		ANL             A,   #1         ; Set up bit for output.
		MOV             DPL, A          ; Set address to write bit.
		MOVX            A,   @DPTR      ; Output the data bit.
		POP             ACC             ; Restore the accumulator.
		RR              A               ; Position next bit.
		DJNZ            B,   LO         ; Loop for a whole byte.
		POP             B               ; Restore the B register.
		POP             MCON            ; Restore MCON register.
		POP             DPH             ; Restore the data
		POP             DPL             ;    pointer from stack.
		RET                             ; Return.



	end