New 65c02 build with DUART problems
New 65c02 build with DUART problems
Hi everyone.
I've been playing around with building some 65c02 systems since last fall. After seeing the work being done on the 8-bit guy's Commander X16 computer and playing around with programming a game in 6502 assembly for it (https://www.youtube.com/watch?v=-Gw56Zh5yP8), I was inspired to try my hand at designing my own computer.
I started with something based around Ben Eater's breadboard computer and eventually soldered together some stackable modules, but so many wires made it very finicky and I tried my hand at designing a pcb. I'm currently on my second PCB version. In my earlier versions, I was using an arduino and then an ATMEGA328P for serial communications, since I knew what I was doing with that.
My newest revision uses the SC28L92 DUART as recommended by BigDumbDinosaur (viewtopic.php?f=4&t=4587)
I had a few issues that were caused by the Programmable logic chip I'm using for address decoding, but that seems to be working fine now. The DUART is the last thing I need to figure out how to program for.
I've been looking through the docs from BigDumbDinosaur and I now mostly understand how to talk to it, and I can write to a register and read it back, so the chip is at least somewhat functional, but I haven't been able to establish a working serial connection. I've occasionally been able to get the RX light to light up on my Serial to USB board, but nothing is showing up in the terminal.
Does anyone know what the minimum number of commands would be to get the SC28L92 to output anything on serial?
Setting up auto echo would work just as well I think.
I definitely need to set up the m0, m1, and m2 registers, as well as the csr and asr.
What else is absolutely necessary?
Thanks everyone.
I've been playing around with building some 65c02 systems since last fall. After seeing the work being done on the 8-bit guy's Commander X16 computer and playing around with programming a game in 6502 assembly for it (https://www.youtube.com/watch?v=-Gw56Zh5yP8), I was inspired to try my hand at designing my own computer.
I started with something based around Ben Eater's breadboard computer and eventually soldered together some stackable modules, but so many wires made it very finicky and I tried my hand at designing a pcb. I'm currently on my second PCB version. In my earlier versions, I was using an arduino and then an ATMEGA328P for serial communications, since I knew what I was doing with that.
My newest revision uses the SC28L92 DUART as recommended by BigDumbDinosaur (viewtopic.php?f=4&t=4587)
I had a few issues that were caused by the Programmable logic chip I'm using for address decoding, but that seems to be working fine now. The DUART is the last thing I need to figure out how to program for.
I've been looking through the docs from BigDumbDinosaur and I now mostly understand how to talk to it, and I can write to a register and read it back, so the chip is at least somewhat functional, but I haven't been able to establish a working serial connection. I've occasionally been able to get the RX light to light up on my Serial to USB board, but nothing is showing up in the terminal.
Does anyone know what the minimum number of commands would be to get the SC28L92 to output anything on serial?
Setting up auto echo would work just as well I think.
I definitely need to set up the m0, m1, and m2 registers, as well as the csr and asr.
What else is absolutely necessary?
Thanks everyone.
Re: New 65c02 build with DUART problems
Welcome, and well done! (It's useful too, I think, to show that it might take two or three revisions of a project.)
Can't help with the DUART but I'm sure help will be along before too long.
Can't help with the DUART but I'm sure help will be along before too long.
Re: New 65c02 build with DUART problems
Have you read the "Driving the 28L91" document from this thread?
ETA: The initialisation routine is at the end, and appears to rely on a table that is definitely incomplete.
ETA: The initialisation routine is at the end, and appears to rely on a table that is definitely incomplete.
Re: New 65c02 build with DUART problems
Chromatix wrote:
Have you read the "Driving the 28L91" document from this thread?
ETA: The initialisation routine is at the end, and appears to rely on a table that is definitely incomplete.
ETA: The initialisation routine is at the end, and appears to rely on a table that is definitely incomplete.
Re: New 65c02 build with DUART problems
I have a similar problem at 115200 or 230400.
Everything works fine at 38400, but at 115200 communication becomes one way from my PC to my SBC.
Different uart, but I suspect the USB->uart bridge is the problem.
Other people have had issues with wires from the UART to the USB chip being too long as well.
Everything works fine at 38400, but at 115200 communication becomes one way from my PC to my SBC.
Different uart, but I suspect the USB->uart bridge is the problem.
Other people have had issues with wires from the UART to the USB chip being too long as well.
- BigDumbDinosaur
- Posts: 9427
- Joined: 28 May 2009
- Location: Midwestern USA (JB Pritzker’s dystopia)
- Contact:
Re: New 65c02 build with DUART problems
jjbliss wrote:
The DUART is the last thing I need to figure out how to program for...
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: Select all
;================================================================================
;
;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 & receiverCode: Select all
;================================================================================
;
;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 =*-nxpsutabUnfortunately, the 65C02 has a hardware bug that can give you some grief during setup. A code example will illustrate it:
Code: Select all
; 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 registerWhen 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: Select all
; 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 registerAnyhow, the above should be enough information to get the DUART work.
x86? We ain't got no x86. We don't NEED no stinking x86!
Re: New 65c02 build with DUART problems
Another approach to avoiding the dummy read cycle is to use the (zp) or (zp),Y addressing modes. As long as the indexing operation doesn't cross a page boundary, these will take 5 cycles, all of which are meaningful and addressed to the correct locations. So you could rewrite the initialisation loop as:
Code: Select all
InitUART:
LDA #>io_acia
STA ptr
LDA #<io_acia
STA ptr+1
LDX #s_nxptab-1
@loop:
LDA nxpsutab,X
DEX
LDY nxpsutab,X
STA (ptr),Y
DEX
BPL @loop
RTS
- BigDumbDinosaur
- Posts: 9427
- Joined: 28 May 2009
- Location: Midwestern USA (JB Pritzker’s dystopia)
- Contact:
Re: New 65c02 build with DUART problems
Chromatix wrote:
Another approach to avoiding the dummy read cycle is to use the (zp) or (zp),Y addressing modes. As long as the indexing operation doesn't cross a page boundary, these will take 5 cycles, all of which are meaningful and addressed to the correct locations. So you could rewrite the initialisation loop as:
Code: Select all
InitUART:
LDA #>io_acia
STA ptr
LDA #<io_acia
STA ptr+1
LDX #s_nxptab-1
@loop:
LDA nxpsutab,X
DEX
LDY nxpsutab,X
STA (ptr),Y
DEX
BPL @loop
RTS
BTW, I know from observation with the logic analyzer that the 65C816 behaves like the NMOS 6502 if the circuit fails to do VDA/VPA qualification. That was the cause of some early problems I had with POC V1.0 in getting the DUART to behave.
x86? We ain't got no x86. We don't NEED no stinking x86!
Re: New 65c02 build with DUART problems
Chromatix wrote:
Code: Select all
STA (ptr),YBigDumbDinosaur wrote:
The above kludge is not necessary with the 65C816, as long as its glue logic correctly handles VDA and VPA.
What's the worst that could happen if we ignore VPA and simply prevent any IO access if VDA is low? IMO IO would be adequately protected. But if I'm missing something then I'm happy to learn the details of how this could fail.
-- Jeff
Last edited by Dr Jefyll on Sat May 16, 2020 12:46 am, edited 1 time in total.
In 1988 my 65C02 got six new registers and 44 new full-speed instructions!
https://laughtonelectronics.com/Arcana/ ... mmary.html
https://laughtonelectronics.com/Arcana/ ... mmary.html
- BigDumbDinosaur
- Posts: 9427
- Joined: 28 May 2009
- Location: Midwestern USA (JB Pritzker’s dystopia)
- Contact:
Re: New 65c02 build with DUART problems
Dr Jefyll wrote:
BigDumbDinosaur wrote:
The above kludge is not necessary with the 65C816, as long as its glue logic correctly handles VDA and VPA.
Or am I missing something? What's the worst that could happen if we ignore VPA and simply prevent any IO access if VDA is low?
It depends on how the glue logic works. If only VDA is used to qualify accesses and the '816 is in the opcode fetch cycle it may come up empty if the addressed location is not selected when VDA is low.
x86? We ain't got no x86. We don't NEED no stinking x86!
Re: New 65c02 build with DUART problems
BigDumbDinosaur wrote:
It depends on how the glue logic works.
The simplest arrangement is as I described: ignore VPA and simply prevent any IO access if VDA is low. The only case where the CPU will come up empty is if you're trying to fetch code from an IO device.
ps-
Fun fact: the active-high Chip Select input on a VIA is ideal for connecting straight to VDA. It's a zero-chip solution to protecting the VIA from responding to a dead cycle.
In 1988 my 65C02 got six new registers and 44 new full-speed instructions!
https://laughtonelectronics.com/Arcana/ ... mmary.html
https://laughtonelectronics.com/Arcana/ ... mmary.html
Re: New 65c02 build with DUART problems
BigDumbDinosaur wrote:
jjbliss wrote:
The DUART is the last thing I need to figure out how to program for...
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.
...
When slowing the speed to 9600, I am now able to send a value from a terminal and receive a value back, though it is currently the incorrect value. I assume there is some value in the configuration I have set wrong, My code is quite a mess with all the things I've been trying and there are bound to be problems in it. Hopefully I'll figure out the problem tomorrow.
Re: New 65c02 build with DUART problems
Ah yes, I looked up only the izp mode (which *is* 5 cycles) without double-checking STA izy.
But since the 65C02 only re-reads the previous address accessed in the indirect indexed mode, that address is now in zero-page, well away from the UART. I'd say it's a better solution (and still a cycle faster) than offsetting the base address of the index purely to ensure a page-crossing. It's also more general, since it now works even if the device is not at the bottom of a page.
But since the 65C02 only re-reads the previous address accessed in the indirect indexed mode, that address is now in zero-page, well away from the UART. I'd say it's a better solution (and still a cycle faster) than offsetting the base address of the index purely to ensure a page-crossing. It's also more general, since it now works even if the device is not at the bottom of a page.
- BigDumbDinosaur
- Posts: 9427
- Joined: 28 May 2009
- Location: Midwestern USA (JB Pritzker’s dystopia)
- Contact:
Re: New 65c02 build with DUART problems
jjbliss wrote:
I've definitely made a lot of progress in the past few hours...When slowing the speed to 9600, I am now able to send a value from a terminal and receive a value back, though it is currently the incorrect value. I assume there is some value in the configuration I have set wrong, My code is quite a mess with all the things I've been trying and there are bound to be problems in it. Hopefully I'll figure out the problem tomorrow.
As an aside, the setup table I earlier presented configures the channel for 115.2Kbps and 8N1. You probably just need to tinker with the data rate.
x86? We ain't got no x86. We don't NEED no stinking x86!
Re: New 65c02 build with DUART problems
BigDumbDinosaur wrote:
At this point, it's likely a data rate or format error in your setup. Are you sure the terminal is running at 9600? If you're positive it is, then you need to verify that you are using the right number of data and stop bits, and that parity is off, unless the terminal is expecting parity (a rare expectation, in my experience).
As an aside, the setup table I earlier presented configures the channel for 115.2Kbps and 8N1. You probably just need to tinker with the data rate.
As an aside, the setup table I earlier presented configures the channel for 115.2Kbps and 8N1. You probably just need to tinker with the data rate.
To make sure that it wasn't caused by my code reading the table, I've temporarily switched my setup code into individual writes.
I think this should set up 9600 with 8 data bits, no parity, 1 stop bit, and no RTS/CTS.
Code: Select all
;IMR (disable IRQs)
lda #0
sta nx_ubase+nx_imr
lda #nxpcrrsd ;deassert request to send
sta nx_ubase+nx_cra
lda #nxpcrrxr ; reset receiver
sta nx_ubase+nx_cra
lda #nxpcrtxr ;reset transmitter
sta nx_ubase+nx_cra
lda #nxpcrbir ;reset received break change IRQ
sta nx_ubase+nx_cra
lda #nxpcresr ;reset error status
sta nx_ubase+nx_cra
lda #nxpcrtmd ;select C/T counter mode %11000000
sta nx_ubase+nx_cra
lda #nx_arbrt ; %01100000
sta nx_ubase+nx_acr
lda #nxpcrrtd ;disable transmitter & receiver
sta nx_ubase+nx_cra
;Set up mode registers
lda #nxpcrmr0
sta nx_ubase+nx_cra
lda #nxpm0def ;%11001100
sta nx_ubase+nx_mra
lda #nxpcrmr1
sta nx_ubase+nx_cra
lda #nxpm1def ;%01010011
sta nx_ubase+nx_mra
lda #nxpm2def ;%00000111
sta nx_ubase+nx_mra
lda #nxpcsdef ;%10111011 Should be 9600
sta nx_ubase+nx_csra
lda #nxpcrrte ;enable transmitter & receiver
sta nx_ubase+nx_cra
lda #nxpiqmsk
sta nx_ubase+nx_imr