jjbliss wrote:
The DUART is the last thing I need to figure out how to program for...
You will need to write some setup values to CR for each channel, as well as MR1 and MR2. The power-on value in MR0 is good enough to get you started. You can later on tinker with MR0 to enable some additional features.
Don't forget about the interrupt mask register (IMR). While it is possible to run the 28L92 in PIO mode, it's pretty inefficient, especially if the serial port is running at high data rates. See below for the data table I use to set up the DUART, as well as the values to be written into the registers.
Code:
;================================================================================
;
;PHILIPS/NXP MULTIPLE CHANNEL UART CONFIGURATION & CONTROL CONSTANTS
;
nxx1freq =3686400 ;X1 clock frequency in Hz
nxctscal =nxx1freq/2 ;C/T scaled clock
;
;
; ACR — auxiliary control register...
;
nxparbrt =@01100000 ;...
;
; a) Use baud rate table #1 —— see data sheet.
; b) C/T acts as a timer.
; c) C/T clock source is scaled to X1 ÷ 1.
;
;
; CR — command register...
;
nxpcrrxe =@00000001 ;enable receiver
nxpcrrxd =@00000010 ;disable receiver
nxpcrtxe =@00000100 ;enable transmitter
nxpcrtxd =@00001000 ;disable transmitter
nxpcrmr1 =@00010000 ;select MR1
nxpcrrxr =@00100000 ;reset receiver
nxpcrtxr =@00110000 ;reset transmitter
nxpcresr =@01000000 ;reset error status
nxpcrbir =@01010000 ;reset received break change IRQ
nxpcrbks =@01100000 ;start break
nxpcrbke =@01110000 ;stop break
nxpcrrsa =@10000000 ;assert request to send
nxpcrrsd =@10010000 ;deassert request to send
nxpcrtme =@10100000 ;select C/T timer mode
nxpcrmr0 =@10110000 ;select MR0
nxpcrtmd =@11000000 ;select C/T counter mode
;
;
; CSR — clock select register...
;
nxpcsdef =@01100110 ;RxD & TxD at 115.2 Kbps...
;
; ————————————————————————————————————————————————————————————————
; The above data rate is based upon the value of NXPARBRT (above).
; ————————————————————————————————————————————————————————————————
;
;
; CT — counter/timer...
;
nxpctdlo =<[nxctscal/hz] ;underflows/sec LSB
nxpctdhi =>[nxctscal/hz] ;underflows/sec MSB
;
;
; IMR — interrupt mask register...
;
nxpairq =nxpatirq|nxparirq ;enable ch A RxD & TxD IRQs
nxpbirq =nxpbtirq|nxpbrirq ;enable ch B RxD & TxD IRQs
nxpiqmsk =nxpairq|nxpbirq|nxpctirq ;set IRQ sources...
;
;
; MR0 — mode 0 register...
;
nxpm0def =@11001100 ;...
;
; 11001100
; ||||||||
; |||||+++———> extended baud rate #2
; ||||+——————> 16-deep FIFO
; ||++———————> TxD interrupts only when FIFO is empty
; |+—————————> RxD interrupts only when FIFO is full (see also MR1:6)
; +——————————> enable RxD watchdog timer
;
;
; MR1 — mode 1 register...
;
nxpm1def =@11010011 ;...
;
; 11010011
; ||||||||
; ||||||++———> 8 bit char size
; |||||+—————> parity type (ignored)
; |||++——————> no parity generated or checked
; ||+————————> character error mode
; |+—————————> RxD interrupts only when FIFO is full (see also MR0:6)
; +——————————> RxD controls RTS
;
;
; MR2 — mode 2 register...
;
nxpm2def =@00010111 ;normal mode, auto RTS
;
;
; OPCR — output port configuration register...
;
nxpopdef =@11110000 ;OP4-7 as IRQ outputs
;
;
; combined setup commands...
;
nxpcrrtd =nxpcrtxd|nxpcrrxd ;disable transmitter & receiver
nxpcrrte =nxpcrtxe|nxpcrrxe ;enable transmitter & receiver
Code:
;================================================================================
;
;PHILIPS/NXP DUAL UART INITIALIZATION DATA TABLE
;
nxpsutab .byte nx_imr, nxpiqmsk ;IMR (enables IRQs)
.byte nx_ctu, nxpctdhi ;CTU
.byte nx_ctl, nxpctdlo ;CTL
.byte nx_crb, nxpcrtxe ;CRB
.byte nx_csrb,nxpcsdef ;CSRB
.byte nx_mrb, nxpm2def ;MR2B
.byte nx_mrb, nxpm1def ;MR1B
.byte nx_crb, nxpcrmr1 ;CRB
.byte nx_mrb, nxpm0def ;MR0B
.byte nx_crb, nxpcrmr0 ;CRB
.byte nx_cra, nxpcrrsa ;CRA
.byte nx_cra, nxpcrrte ;CRA
.byte nx_csra,nxpcsdef ;CSRA
.byte nx_mra, nxpm2def ;MR2A
.byte nx_mra, nxpm1def ;MR1A
.byte nx_cra, nxpcrmr1 ;CRA
.byte nx_mra, nxpm0def ;MR0A
.byte nx_cra, nxpcrmr0 ;CRA
.byte nx_acr, nxparbrt ;ACR
.byte nx_opcr,nxpopdef ;OPCR
.byte nx_crb, nxpcrtmd ;CRB
.byte nx_crb, nxpcresr ;CRB
.byte nx_crb, nxpcrbir ;CRB
.byte nx_crb, nxpcrtxr ;CRB
.byte nx_crb, nxpcrrxr ;CRB
.byte nx_crb, nxpcrrsd ;CRB
.byte nx_cra, nxpcrtmd ;CRA
.byte nx_cra, nxpcresr ;CRA
.byte nx_cra, nxpcrbir ;CRA
.byte nx_cra, nxpcrtxr ;CRA
.byte nx_cra, nxpcrrxr ;CRA
.byte nx_cra, nxpcrrsd ;CRA
.byte nx_imr, 0 ;IMR (disable IRQs)
s_nxptab =*-nxpsutab
The above data table refers to the constants defined in the first code block. The table is read backwards, with the first value in each pair being the register offset and the second value being the parameter to be written into the register.
Unfortunately, the 65C02 has a hardware bug that can give you some grief during setup. A code example will illustrate it:
Code:
; configure DUART, works fine with 65C816, fails with 65C02...
;
ldy #s_nxptab-2 ;DUART setup table index
;
loop ldx nxpsutab,y ;get register offset
lda nxpsutab+s_byte,y ;get register parameter
sta io_acia,x ;write to register <—— bug in 65C02
dey
dey
bpl loop ;next register
Assuming
IO_ACIA, the DUART's base address, is such that all registers are visible in the same page—which is usually the case, the
STA IO_ACIA,X instruction will behave in an insidious way.
When the 65C02 does an indexed write operation that does not cross a page boundary it will perform an undocumented dummy read on the base address, followed by a write on the base address plus whatever is in the (in this case) X-register. What makes it insidious in this case is the dummy read will be to
IO_ACIA+$00, which is the
MR register. A read to
MR autoincrements the register. So, if
MR is set to
MR1 in preparation for a write operation and a write occurs to some other register prior to the
MR1 access,
MR will be "touched" by the dummy read and will increment to
MR2. Later when the write to
MR1 is made, it will be
MR2 that will be written. Adding insult to injury, this bug makes it impossible to configure
MR0, since the act of writing to
MR will result in the dummy read incrementing it to
MR1.
Fortunately, there is a workaround, and that is to modify the setup code to force a page crossing on each access to the DUART. The result is while a dummy read will still occur, it will be a read of the
STA opcode, not a register. Here's an example:
Code:
; configure DUART, kludged to work with a 65C02...
;
ldy #s_nxptab-2 ;DUART setup table index
;
loop ldx nxpsutab,y ;get register offset
inx ;adjust for page crossing kludge <——
lda nxpsutab+s_byte,y ;get register parameter
sta io_acia-1,x ;write to register, forces page crossing <——
dey
dey
bpl loop ;next register
The above kludge is not necessary with the 65C816, as long as its glue logic correctly handles
VDA and
VPA.
Anyhow, the above should be enough information to get the DUART work.