SCC2691 Programming - BIOS

Programming the 6502 microprocessor and its relatives in assembly and other languages.
User avatar
floobydust
Posts: 1394
Joined: 05 Mar 2013

SCC2691 Programming - BIOS

Post by floobydust »

I've been working on the initial BIOS version to get my new Pocket SBC running for a bit now and I do have it functioning quite well. Here's a link to the hardware project:

viewtopic.php?f=4&t=4988

I've found some odd operation of the SCC2691 for initialization and operation. For initializing the chip, it's a more complicated set of instructions versus something as trivial as the 65C51. I initially got it to work by simply doing load and store operations, i.e., LDA #$value, STA UART_REGISTER. After getting the configuration and sequence sorted, I decided to put it into an indexed load/store with two tables; one that holds the register offset and the other that holds the register data. As the NXP UARTS use a set of Mode Registers at the same physical address, you issue a command to reset the Mode register pointer, then store the desired value for MR1 followed by MR2. For some odd reason, this method doesn't work. The first Mode register gets loaded with all bit active ($FF) while the second Mode register gets loaded with the correct value. I've tried this multiple ways and in every case, if I used a load/store that is indexed addressing (W65C02) the first Mode register never gets loaded correctly.

I'm currently using two routines to get the UART functioning; The first is a simple set of commands that set power down mode as inactive (ensure the chip is in power up mode) followed by a sequence of commands that clear the Break Change Interrupt, Disable the Receiver, Disable the Transmitter, Clear all errors. The second routine uses an indexed set of tables to set up the various registers for operating mode, i.e., baud rate, interrupt masks, counter/timer preload values, etc. The last part of this routine does the load/store of the Mode registers. I also use the same table of reset commands for handling the Receive Break function of the UART with one extra command to enable the receiver. The odd part is I have to issues a Power On mode command again with the indexed list (this was done in the first Reset routine) otherwise the UART never works. It's not clear why I need to issue the Power Up command again with the second routine, but it won't work without doing it.

Once the chip is working, it's basically flawless in receive and transmit along with handling the received break function. I use this in the Monitor/BIOS to break out from the Macro loop of the Monitor (allows a sequence of instructions to be executed in an endless loop). In fact, I'm quit happy with the results of both the new board and the new UART as a console. However, the NXP UARTs also have some caveats that need to be managed so you don't hose them up. There are a couple test modes that are invoked by simply doing a read to a register. One is for the baud rate and a second is a 1X/16X clock test. As one of the monitor functions displays memory addresses in hex and ASCII, if the routine is executed against the UART, it locks up the system. I've done some initial debug around this and it turns out there is no way to reset the chip via software, a physical hardware reset is required. I have a Panic routine invoked via a NMI trigger which save registers, pertinent pages and restores the vectors and I/O initialization (which runs to completion) but the UART won't start working again until a physical reset is done.

An earlier bit of code was not locking up, albeit the console display of the I/O page (UART addresses) was garbled up a bit. Unfortunately I can't seem to find the cause for the recent locking up on reading the registers. Also, reading the receive register will also increment the FIFO buffer which adds yet another issues to be dealt with. I'm not currently decided on how to manage the memory display in the Monitor code just yet, but I'm thinking of having an excluded address list that will return a bogus value for any accesses to those addresses.

In any case, using the SCC2691 is a bit of a challenge versus the 65C51 for something as simple as a console. Overall, I'm still glad that I made the switch (of UARTs) and the new SBC. It's been a good project so far and I'll likely have the initial BIOS and Monitor versions done shortly. It also gives me the base code and knowledge to move to the newer NXP UARTs with minor code changes. If anyone else has done some (65C02) coding around the SCC2691, I'd be interested to hear any experiences to compare with mine so far.
User avatar
Dr Jefyll
Posts: 3526
Joined: 11 Dec 2009
Location: Ontario, Canada
Contact:

Re: SCC2691 Programming - BIOS

Post by Dr Jefyll »

floobydust wrote:
I decided to put it into an indexed load/store with two tables; one that holds the register offset and the other that holds the register data. [...] For some odd reason, this method doesn't work.
You haven't posted the code, or mentioned where IO is located. But let's say IO is at $C000. In your code it sounds as if the actual STA to the UART is indexed by X or Y, like this:

Code: Select all

             ;$C000 is the beginning of the IO page. The lobyte of address is in X

STA $C000,X  ;
As an experiment you can try changing that snippet to what's shown below. The altered code still addresses the same location but in the process it'll force a page crossing to occur because the base address now has $FF in the low byte. I might be barking up the wrong tree but I suspect the page crossing will fix your problem. If you get a sec to try this pls report back the result. :)

Code: Select all

               ;$C000 is the beginning of the IO page. The lobyte of address is in X

INX            ;index is increased by one
STA $C000-1,X  ;base address is reduced by one (to $BFFF). Any non-zero index will cross to the next page.
cheers
Jeff
In 1988 my 65C02 got six new registers and 44 new full-speed instructions!
https://laughtonelectronics.com/Arcana/ ... mmary.html
User avatar
BigDumbDinosaur
Posts: 9428
Joined: 28 May 2009
Location: Midwestern USA (JB Pritzker’s dystopia)
Contact:

Re: SCC2691 Programming - BIOS

Post by BigDumbDinosaur »

Dr Jefyll wrote:
You haven't posted the code, or mentioned where IO is located. But let's say IO is at $C000. In your code it sounds as if the actual STA to the UART is indexed by X or Y, like this...
My feeble dinosaur brain seems to recall another discussion we had going about this, which, if I recall, centered on the 65C02 doing an undocumented "false" read of an address when an indexed write instruction is being executed. As what you are doing is programming the 2691 via a series of indexed stores, I think what is happening is the false read is resulting in the 65C02 touching MR twice, once during the false read and again during the actual write. False reading MR when it has been set to MR1 will cause it to autoincrement to MR2, which means you would not be able to write to MR1 using a "straight" indexed access. Therefore, I think you should try out Jeff's address offset suggestion to see if it magically fixes itself. If not, another route to consider would be using indirect addressing, which should not result in the false read.

Incidentally, I would consider this behavior of the 65C02 to be chip errata, as it is not documented in the data sheet and potentially precludes the use of indexing on I/O hardware, including 65xx family devices. This problem does not arise with the 65C816 when the VDA and VPA outputs are used to qualify accesses, as the machine cycle in which the false read would occur is signaled by VDA and VPA both being low.
x86?  We ain't got no x86.  We don't NEED no stinking x86!
User avatar
floobydust
Posts: 1394
Joined: 05 Mar 2013

Re: SCC2691 Programming - BIOS

Post by floobydust »

Thanks Jeff/BDD,

I'm posting the two setup routines here. This takes the guessing out of what I'm actually doing. The UART is addressed at $FE80, so the initial recommendation (which is a really good one) won't work unless I reprogram the 22V10 and change it (which I can do). Also note that the instructions are sent in reverse as listed due to the indexed count being decremented.

Code: Select all

;
; Initializing the SCC2691 UART as a Console
;
; There is an odd problem with initialization of the 2691.
; Regardless of the data structure or sequence,
; Mode Register 1 always shows as $FF after being initialized.
; Mode Register 2 always shows the correct data after being initialized.
; The same init routine can executed multiple times with the same result,
; so the Mode register pointer is being reset.
; I suspect that using an indexed addressing mode routine creates an issue
; with the registers of the SCC2691 for whatever reason.
;
; To work around this, there are two basic routines to setup the 2691 UART:
; - the first routine is a basic RESET of the UART. It issues a sequence of
; commands to do the following:
; 1- Send a Power On command to the ACR
; 2- Reset Break Change Interrupt
; 3- Reset Receiver
; 4- Reset Transmitter
; 5- Reset All errors
;
; The second routine initializes tha 2691 UART for operation. It uses two tables
; of data; one for the register offset and the other for the register data.
; The table for register offsets is maintained in ROM.
; The table for register data is copied to page $03, making it soft data.
; If needed, operating parameters can be altered and the UART re-initialized.
;
INIT_IO		JSR	RESET_2691	;Power-Up Reset of SCC2691 UART
					JSR	INIT_2691	;Initialize the SCC2691 UART
					LDA	#DF_TICKS	;Get divider for jiffy clock for 1-second
					STA	TICKS	;Preload TICK count
					RTS	;Return to caller
;
RESET_2691	;This routine does a basic Reset of the SCC2691
					LDA	#%00001000	;Get Power On mask
					STA	UART_AUXCR	;Send to 2691 (ensure it's on)
;
					LDX	#UART_RDATAE-UART_RDATA1	;Get index count
UART_RES1	LDA	UART_RDATA1-1,X	;Get Reset commands
					STA	UART_COMMAND	;Send to UART CR
					DEX	;Decrement the command list
					BNE	UART_RES1	;Loop back until all are sent
					RTS	;Return to caller
;
INIT_2691	;This routine sets the initial operating mode of the UART
					LDX	#INIT_DATAE-INIT_DATA	;Load X register with number of init bytes for 2691
2691_INT	LDA	LOAD_2691-1,X	;Get Data for 2691 register
					LDY	INIT_OFFSET-1,X	;Get Offset for 2691 register
					STA	SCC2691_BASE,Y	;Store to selected register
					DEX	;Decrement count
					BNE	2691_INT	;Loop back until all registers are loaded
;
					LDA	#%00010000	;Get mask for Reset MR pointer
					STA	UART_COMMAND	;Send to 2691
					LDA	MR1_DAT	;Get Mode Register 1 Data
					STA	UART_MODEREG	;Send to 2691
					LDA	MR2_DAT	;Get Mode Register 2 Data
					STA	UART_MODEREG	;Send to 2691
					RTS	;Return to caller
;
...and the data used: Basic configuration is for 38.4K baud, 8 data bits, 1 stop bit, no parity. MPO/MPI are configured for RTS/CTS handshaking. Timer/Counter is loaded to generate an interrupt every 10ms. Interrupts are enable for Receive/Transmit, Timer/Counter and Received Break.

Code: Select all

; Reset UART Data is listed here. The sequence and commands
; do not require changes for any reason. As a result, these
; are maintained in ROM only.
;
; Data commands are sent in reverse order from list
; There is a shorter list which is used to Reset the UART
; and the full list which resets and re-enables the Receiver
; via the Receive Break routine.
;
UART_RDATA	;UART Reset Data for Received Break (ExtraPutty)
					.DB	%00000001	;Enable Receiver
UART_RDATA1	;Smaller list for entry level Reset
					.DB	%01000000	;Reset All Errors
					.DB	%00110000	;Reset Transmitter
					.DB	%00100000	;Reset Receiver
					.DB	%01010000	;Reset Break Change Interrupt
UART_RDATAE	;End of UART Reset Data 
;
INIT_OFFSET	;Start of UART Initialization Register Offsets
					.DB	$02	;Command Register
					.DB	$02	;Command Register
					.DB	$05	;Interrupt Mask Register
					.DB	$04	;Aux Command Register
					.DB	$06	;Counter Preset Upper
					.DB	$07	;Counter Preset Lower
					.DB	$01	;Baud Clock Register
					.DB	$02	;Command Register
					.DB	$02	;Command Register
					.DB	$02	;Command Register
					.DB	$05	;Interrupt Mask Register
					.DB	$04	;Aux Command Register
INIT_OFFSETE	;End of UART Initialization Register Offsets
;
CFG_TABLE	;Configuration table for hardware devices
;
; Data commands are sent in reverse order from list
; This list is the default initialization for the UART 
; as configured for use as a Console connected to ExtraPutty
; the data here is copied to page $03 and is used to configure
; the UART during boot up. The soft data can be changed and
; the the core INIT_2691 can be called to reconfigure the UART.
; NOTE: the Register offset data is not kept in soft config memory
; as the initialization sequence should not be changed!
;
INIT_DATA		;Start of UART Initialization Data
					.DB	%10100000	;Enable RTS (Receiver)
					.DB	%00001001	;Enable Receiver/Disable Transmitter
					.DB	%00011101	;Interrupt Mask Register setup
					.DB	%01101000	;Aux Register setup for Counter/Timer
					.DB	%01001000	;Counter/Timer Upper Preset
					.DB	%00000000	;Counter/Timer Lower Preset
					.DB	%11001100	;Baud Rate clock for Rcv/Xmt
					.DB	%10010000	;Disable Counter/Timer
					.DB	%00001010	;Disable Receiver/Transmitter
					.DB	%10110000	;Disable RTS (Receiver)
					.DB	%00000000	;Interrupt Mask Register setup
					.DB	%00001000	;Aux Register setup for Power On
INIT_DATAE	;End of UART Initialization Data
;
; Mode Register Data is held separately. Using a loop routine to
; send this data to the UART does not work properly. MR1 always shows
; as having all bits on ($FF) after the routine. Debugging the UART has
; confirmed this odd situation. So the main INIT_2691 send these data
; config bytes as separate load/store instructions.
; This data is also kept in soft config memory in page $03
;
MR1_DAT		.DB	%10010011	;Mode Register 1 Data
MR2_DAT		.DB	%00010111	;Mode Register 2 data
;
Fortunately, the above code is working properly for setup of the 2691. I'm thinking about moving the UART I/O address to page $FF and simply clipping off the addresses for memory display and search routines within the Monitor code. Then again, this does make things a bit ugly overall... as the table-driven disassembler could also end up clobbering the UART registers on reads if allowed to go that far. Still, this is a fun project and a good exercise in thinking as well as coding :shock:
User avatar
BigEd
Posts: 11464
Joined: 11 Dec 2008
Location: England
Contact:

Re: SCC2691 Programming - BIOS

Post by BigEd »

The idea of the workaround is to force a page crossing. So, instead of accessing relative to $FE80, you would need to adjust X so you access relative to $FDFF. If X comes from a table, that's easy, otherwise you need to add $81 to X which would take a few instructions.

It looks like X does come from a table in your case.
User avatar
BigDumbDinosaur
Posts: 9428
Joined: 28 May 2009
Location: Midwestern USA (JB Pritzker’s dystopia)
Contact:

Re: SCC2691 Programming - BIOS

Post by BigDumbDinosaur »

BigEd wrote:
The idea of the workaround is to force a page crossing. So, instead of accessing relative to $FE80, you would need to adjust X so you access relative to $FDFF. If X comes from a table, that's easy, otherwise you need to add $81 to X which would take a few instructions.

It looks like X does come from a table in your case.
Actually, tinkering with .X won't help, as that is his table index. The device index is in .Y, which is loaded from the register table with LDY INIT_OFFSET-1,X. So what could be done would be to change STA SCC2691_BASE,Y to INY followed by STA SCC2691_BASE-1,Y. MR is at offset $00 in the 2691, so the above would make it seem to the MPU that MR is really at offset $01. I would think this would result in correct operation, assuming my theory about what the 65C02 is doing is correct.
x86?  We ain't got no x86.  We don't NEED no stinking x86!
User avatar
BigEd
Posts: 11464
Joined: 11 Dec 2008
Location: England
Contact:

Re: SCC2691 Programming - BIOS

Post by BigEd »

Oops, X ≠ Y. But still, I believe the idea is to force a page crossing, so the unwanted access affects the previous page and not the I/O page. So I think the table contents need to change, and the base needs to change too. These two lines

Code: Select all

               LDY   INIT_OFFSET-1,X   ;Get Offset for 2691 register
               STA   SCC2691_BASE,Y   ;Store to selected register
become

Code: Select all

               LDY   INIT_OFFSET-1,X   ;Get adjusted Offset for 2691 register
               STA   (SCC2691_BASE & $FF00)-1,Y   ;Store to selected register (force page crossing)
User avatar
BigDumbDinosaur
Posts: 9428
Joined: 28 May 2009
Location: Midwestern USA (JB Pritzker’s dystopia)
Contact:

Re: SCC2691 Programming - BIOS

Post by BigDumbDinosaur »

BigEd wrote:
Oops, X ≠ Y. But still, I believe the idea is to force a page crossing, so the unwanted access affects the previous page and not the I/O page. So I think the table contents need to change, and the base needs to change too. These two lines

Code: Select all

               LDY   INIT_OFFSET-1,X   ;Get Offset for 2691 register
               STA   SCC2691_BASE,Y   ;Store to selected register
become

Code: Select all

               LDY   INIT_OFFSET-1,X   ;Get adjusted Offset for 2691 register
               STA   (SCC2691_BASE & $FF00)-1,Y   ;Store to selected register (force page crossing)
Yes, forcing a page crossing should make the 'C02 stop doing the false read, if one can believe the data sheet. :D My thinking is based upon the notion that the false read is occurring at the unindexed base address, with the following write occurring at the indexed address. In the case of the 2691, if the index is $00, then MR (mode register) is touched twice, causing it to autoincrement to MR2 and setting the stage for the problem being seen. Different ways of fixing the same issue, as it were.
x86?  We ain't got no x86.  We don't NEED no stinking x86!
User avatar
Dr Jefyll
Posts: 3526
Joined: 11 Dec 2009
Location: Ontario, Canada
Contact:

Re: SCC2691 Programming - BIOS

Post by Dr Jefyll »

floobydust wrote:
I'm posting the two setup routines here. This takes the guessing out of what I'm actually doing.
Okay, good. :) And I don't think it'll be necessary to reprogram the 22V10. Instead the test I suggested can be altered.

First let me doublecheck something. (I don't want to add another hiccup to the discussion.) Although you didn't post the part of your code where SCC2691_BASE is declared, you did say, "The UART is addressed at $FE80." I take that to mean the constant SCC2691_BASE has the value $FE80, and I will proceed on that premise.

So...

Code: Select all

STA   SCC2691_BASE,Y
is the same as

Code: Select all

STA   $FE80,Y
In order to alter this in a way that forces a page crossing we do as Ed said: STA (SCC2691_BASE & $FF00)-1,Y or, in other words,

Code: Select all

STA   $FDFF,Y
Notice the base address has been reduced from $FE80 to $FDFF -- a difference of $81. To compensate you need to increase the index by $81, which can be most efficiently done by adding $81 to every entry in the table from which Y got loaded, more or less as follows. (Yes there are prettier ways of doing this. :oops: )

Code: Select all

INIT_OFFSET   ;Start of UART Initialization Register Offsets
               .DB   $02 +$81  ;Command Register
               .DB   $02 +$81  ;Command Register
               .DB   $05 +$81 ;Interrupt Mask Register (and so on)
I hope it's clear how this will force a page crossing. Please give this a try and report back.
BigDumbDinosaur wrote:
Yes, forcing a page crossing should make the 'C02 stop doing the false read, if one can believe the data sheet. :D
I agree there's a problem with the data sheet, but the exact explanation involves some hair-splitting. I think it'll be best if we discuss that after the problem has been resolved.

J :)
Last edited by Dr Jefyll on Fri Nov 24, 2017 3:30 pm, 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
User avatar
floobydust
Posts: 1394
Joined: 05 Mar 2013

Re: SCC2691 Programming - BIOS

Post by floobydust »

Well, I did a much simpler test to prove all of this. As the BRG Test register is the Register Read of $02 (register offset from base), writing to that address is the Command Register (which is done a lot!). So, examining the BRG Test baud rates, if programmed for either 38.4K or 19.2K, they stay the same. So I changed the soft config byte from $CC to $AA (7200 baud) and called the INIT routine. I then had to change the baud rate to match the 7200 and was able to communicate to the UART. I then ran the INIT routine again and then had to change the baud rate to 57.6K to talk to the UART. Run the INIT routine again and I have to switch the BAUD rate back to 7200. It just toggles back and forth!

So, no doubt... this is the problem, i.e., the 65C02 "is" doing a false read when using indexed addressing. This certainly puts some inhibitors on how you use the NXP UARTS with the 65C02.
User avatar
Dr Jefyll
Posts: 3526
Joined: 11 Dec 2009
Location: Ontario, Canada
Contact:

Re: SCC2691 Programming - BIOS

Post by Dr Jefyll »

floobydust wrote:
This certainly puts some inhibitors on how you use the NXP UARTS with the 65C02.
The issue isn't that hard to work around. And the ugly "test" code I suggested can be prettified; I only wrote it coarsely so it'd be clear. Or you can use BDD's suggestion of using the C02's indirect address mode. Are you willing to try the (ugly) test? :)
In 1988 my 65C02 got six new registers and 44 new full-speed instructions!
https://laughtonelectronics.com/Arcana/ ... mmary.html
User avatar
BigDumbDinosaur
Posts: 9428
Joined: 28 May 2009
Location: Midwestern USA (JB Pritzker’s dystopia)
Contact:

Re: SCC2691 Programming - BIOS

Post by BigDumbDinosaur »

floobydust wrote:
So, no doubt... this is the problem, i.e., the 65C02 "is" doing a false read when using indexed addressing.
Which, of course, explains the MR contretemps.
Quote:
This certainly puts some inhibitors on how you use the NXP UARTS with the 65C02.
Well, you can try out Jeff's suggestion and see if that "fixes" it. Or perhaps use (<zp>),Y addressing to do the setup, where .Y would continue to be the offset of the register that is being configured, and <zp> would be loaded with the UART's base address.

There is no question in my mind that this behavior is chip errata on the part of the 65C02—not a defect of the 2691—and should be reported to WDC.
x86?  We ain't got no x86.  We don't NEED no stinking x86!
User avatar
Dr Jefyll
Posts: 3526
Joined: 11 Dec 2009
Location: Ontario, Canada
Contact:

Re: SCC2691 Programming - BIOS

Post by Dr Jefyll »

BigDumbDinosaur wrote:
Or perhaps use (<zp>),Y addressing to do the setup, where .Y would continue to be the offset of the register that is being configured, and <zp> would be loaded with the UART's base address.
Oops, nope. I misunderstood; thought you meant indirect address mode without indexing. The non-indexed indirect approach has no issues and is pretty easy to code; you just modify the lowbyte of the ZP pointer each time as required. But indirect mode with indexing (via Y) will give the same problem we've already seen -- namely, an unwanted read before the write. (And the same workaround -- a forced page crossing -- can be applied.)
BigDumbDinosaur wrote:
There is no question in my mind that this behavior is chip errata on the part of the 65C02—not a defect of the 2691
Right. The 'C02 is advertised as being "fixed," as compared to various issues affecting the NMOS '02 -- including unexpected bus behavior during indexing. The advertised fix is real and valuable, but less extensive than one might wish -- a few of the problem circumstances still give trouble.
In 1988 my 65C02 got six new registers and 44 new full-speed instructions!
https://laughtonelectronics.com/Arcana/ ... mmary.html
User avatar
floobydust
Posts: 1394
Joined: 05 Mar 2013

Re: SCC2691 Programming - BIOS

Post by floobydust »

Okay,

I did a quick code change based on Jeff's suggestion. I made a new indexed looped routine to reset the Mode Register Pointer and load the two Mode Registers. I coded the addresses normally first to ensure that the bug was present, i.e., no offset to force a page boundary change. This resulted in MR1 not being loaded. I then put in the offset of $81 to push the effective store address from $FE80 to $FDFF and the init routine now works properly.

I also noticed another caveat with the SCC2691 while initially debugging this problem early on. Once the MRs are loaded, you can turn the SBC off for several minutes... and the register contents still hold in the 2691. In other words, the test with bad code (indexed store) appears to work as MR1 never lost it's contents. So for debugging this I keep a short piece of wire handy and simply short out the +5V to ground at the EEPROM (pin 28 to in 14). This ensures the 2691 registers are cleared.

So once again... another interesting exercise and finding another bug with the 65C02. And of course, another set of thank-yous to Jeff and BDD ;-) Now that this is fully understood and documented, I'm going to use my existing code that loads the MRs separately and document why this is the case. If I decide to move the UART base address in the future (which is easy using the 22V10), I'll need to alter the routine addresses to ensure I get another page boundary crossing for it to work. I'll write this up and send it to WDC, along with another bug I found in the WDC Assembler while working on this project. Oh, the fun of it all!
resman
Posts: 154
Joined: 12 Dec 2015
Location: Lake Tahoe
Contact:

Re: SCC2691 Programming - BIOS

Post by resman »

FWIW, the Apple II Super Serial Card (using the 6551) has this exact bug fix in its firmware. The false read would look like an access to the data register which would clear the 'character available' status register bit.
Post Reply