6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Wed May 08, 2024 10:32 am

All times are UTC




Post new topic Reply to topic  [ 18 posts ]  Go to page 1, 2  Next
Author Message
PostPosted: Fri May 15, 2020 5:45 pm 
Offline
User avatar

Joined: Tue Dec 31, 2019 11:13 pm
Posts: 5
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.

Attachment:
File comment: Early work base on Ben Eater's videos.
IMG_20191122_234335.jpg
IMG_20191122_234335.jpg [ 4.01 MiB | Viewed 1491 times ]

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.
Attachment:
File comment: Using a C64 as a serial terminal to connect to my stack computer.
IMG_20200108_165500.jpg
IMG_20200108_165500.jpg [ 3.97 MiB | Viewed 1491 times ]

Attachment:
File comment: First custom pcb build.
IMG_20200115_210505.jpg
IMG_20200115_210505.jpg [ 4.35 MiB | Viewed 1491 times ]

I'm currently on my second PCB version.
Attachment:
File comment: Newest PCB.
IMG_20200515_132632.jpg
IMG_20200515_132632.jpg [ 2.82 MiB | Viewed 1491 times ]

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 (http://forum.6502.org/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.


Top
 Profile  
Reply with quote  
PostPosted: Fri May 15, 2020 6:13 pm 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10800
Location: England
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.


Top
 Profile  
Reply with quote  
PostPosted: Fri May 15, 2020 7:04 pm 
Offline

Joined: Mon May 21, 2018 8:09 pm
Posts: 1462
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.


Top
 Profile  
Reply with quote  
PostPosted: Fri May 15, 2020 10:10 pm 
Offline
User avatar

Joined: Tue Dec 31, 2019 11:13 pm
Posts: 5
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.


Yeah, I have some code written that is based on that. I have been trying to put my own table together, and I've had some limited success. I am trying to repeatedly send a single character. I am using the sioput routine as detailed in that doc, and an interrupt is being created and handled, which is causing the RX light to flash on my serial to usb board, so that's a good sign, but still nothing being picked up on my terminal. Next, maybe I'll knock down the speed from 115200 to 9600 and mess around with the other connection settings to see if anything helps..


Top
 Profile  
Reply with quote  
PostPosted: Fri May 15, 2020 10:22 pm 
Offline

Joined: Tue Oct 02, 2018 4:22 am
Posts: 48
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.


Top
 Profile  
Reply with quote  
PostPosted: Fri May 15, 2020 10:34 pm 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8176
Location: Midwestern USA
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.

_________________
x86?  We ain't got no x86.  We don't NEED no stinking x86!


Top
 Profile  
Reply with quote  
PostPosted: Fri May 15, 2020 11:44 pm 
Offline

Joined: Mon May 21, 2018 8:09 pm
Posts: 1462
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:
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


Top
 Profile  
Reply with quote  
PostPosted: Sat May 16, 2020 12:35 am 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8176
Location: Midwestern USA
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:
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

The 65C02 data sheet says any indexed operation will cause a dummy read. In the NMOS 6502, that dummy read touched an undefined address. In the 65C02, supposedly the dummy read will be of the instruction opcode. However, we know that the dummy read will occur on the base address if the effective address is in the same page. The data sheet makes no distinction between <addr>,X and (<zp>),Y addressing—it merely says "Indexed addressing across page boundary", so some experimentation may be necessary to prove your method will work.

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!


Top
 Profile  
Reply with quote  
PostPosted: Sat May 16, 2020 12:38 am 
Offline
User avatar

Joined: Fri Dec 11, 2009 3:50 pm
Posts: 3354
Location: Ontario, Canada
Chromatix wrote:
Code:
  STA (ptr),Y
It's a good suggestion -- using a different address mode will avoid the problem. But, just for the record, STA using (zp),Y addressing mode is always 6 cycles... in contrast to read instructions using (zp),Y, which only add the extra cycle when a page crossing occurs. I sometimes have trouble remembering these details, but Drass published a thorough list and description of 6502 and 'C02 dead cyles here.

BigDumbDinosaur wrote:
The above kludge is not necessary with the 65C816, as long as its glue logic correctly handles VDA and VPA.
Again an effective solution. It makes sense to prevent any IO access if VDA is low. But is it important to involve VPA as well? Simpler just to ignore 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

_________________
In 1988 my 65C02 got six new registers and 44 new full-speed instructions!
https://laughtonelectronics.com/Arcana/ ... mmary.html


Last edited by Dr Jefyll on Sat May 16, 2020 12:46 am, edited 1 time in total.

Top
 Profile  
Reply with quote  
PostPosted: Sat May 16, 2020 12:43 am 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8176
Location: Midwestern USA
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.
Again an effective solution. It makes sense to prevent any IO access if VDA is low. But is it important to involve VPA as well? Simpler just to ignore 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?

I just throw VPA in there so it doesn't feel left out. :D

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!


Top
 Profile  
Reply with quote  
PostPosted: Sat May 16, 2020 12:55 am 
Offline
User avatar

Joined: Fri Dec 11, 2009 3:50 pm
Posts: 3354
Location: Ontario, Canada
BigDumbDinosaur wrote:
It depends on how the glue logic works.
Fair enough, I suppose, but why would you want it to work with any more complexity than necessary?

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


Top
 Profile  
Reply with quote  
PostPosted: Sat May 16, 2020 1:11 am 
Offline
User avatar

Joined: Tue Dec 31, 2019 11:13 pm
Posts: 5
BigDumbDinosaur wrote:
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.
...

Thanks for all of that! I've definitely made a lot of progress in the past few hours. That fix for the dummy read was very helpful. I know I saw something about that in the doc you had written about driving the SC28L91, but I had completely forgotten about it.

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.


Top
 Profile  
Reply with quote  
PostPosted: Sat May 16, 2020 1:18 am 
Offline

Joined: Mon May 21, 2018 8:09 pm
Posts: 1462
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.


Top
 Profile  
Reply with quote  
PostPosted: Sat May 16, 2020 4:04 am 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8176
Location: Midwestern USA
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.

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.

_________________
x86?  We ain't got no x86.  We don't NEED no stinking x86!


Top
 Profile  
Reply with quote  
PostPosted: Sat May 16, 2020 6:44 pm 
Offline
User avatar

Joined: Tue Dec 31, 2019 11:13 pm
Posts: 5
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.


Still haven't quite got it yet. I'm pretty sure it is some sort of data rate issue. I have disabled RTS/CTS to simplify things. I think this should still work without that, right? I've set up a driver similar to the one you outline in your doc, so it's possible I've messed something up in there too, but that part seems to be working as far as I can tell. I've set up a program to display each incoming byte on the LEDs while echoing back, then advance to the next with a press of the NMI button. I've tried a few different terminal programs to connect to it and a few different usb serial adapters and while the exact characters that show up are different, the results are pretty much the same. A single key press will be picked up as multiple incoming bytes. For instance, if I press 'q' in a terminal program it recieves $06 followed by $FE.


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:
  ;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


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 18 posts ]  Go to page 1, 2  Next

All times are UTC


Who is online

Users browsing this forum: No registered users and 6 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to: