Jump to content
IGNORED

Indexed read, page crossing and SC-RAM


Al_Nafuur

Recommended Posts

The last few days I have been dealing with a strange behavior of the VCS 6502 hardware (Maybe this behavior is commonly known, but it took me a few days).

 

I want to add @cd-w's 24 char textkernel to display the online HSC table in 1942 bB.

 

One part of the HSC table screen data is in ROM (the title rows) and the other part is in SC-RAM (the table). The base address for the indexed read is in SC-RAM and if I want to display a row from ROM the index is greater 127 and the page boundary from SC-RAM to the first ROM page is crossed.

 

This takes only an additional cpu cycle and works fine on the emulators (except for a wrong scanline count). But on the real hardware it looks like the low byte of the new address is set in the regular cycle and the high byte in the additional. Unfortunately the first of these two cycles looks like an access to the low byte of the same page for the cartridge, which is in our case (even more unfortunately) the write port of the SC-RAM, so the cartridge takes whatever is on the data-bus at then end of this cycle and writes it to the SC-RAM!

 

 

textkernel_HSC.asm

textkernel_HSC.asm.bin

 

Link to comment
Share on other sites

Due to the way the 6502 is designed, page-crossing will cause a bad address to appear on the address bus for a single CPU cycle before the proper address appears.  (the 8-bit ALU is in the middle of calculating the 16-bit address that the code wants to access).  This usually doesn't matter.  BUT, for the Atari 2600, the cartridge doesn't have access to the R/W line.  So you'll end up with these sorts of corruption issues.

 

It's best to try and avoid page crossing when reading/writing data to SC-RAM.

  • Like 5
Link to comment
Share on other sites

What you are experiencing is a so-called "ghost read". The CPU does a dummy-read on an address where the "summed up" hi-byte of the address was not taken into account.I ran into a similar problem when writing a UDP stack for a C64 network card. I noticed it in short here rather at the end:

 

Link to comment
Share on other sites

6 minutes ago, Thomas Jentzsch said:

For development you should enable "Trap on 'ghost' reads" (Options/Developer/Debugger). Then you would notice this.

 

 

it was/is enabled, but I still don't see the same effect than on hardware:

grafik.thumb.png.a694821bdc8c43b4fc74cb8ef8b3c673.png

 

@JetSetIlly added the "feature" to gopher2600 and this is what it looks like (and on the PlusCart too) :

 

crt_textkernel_HSC.asm_20220117_233557_double_1.thumb.jpg.a49eb3ba7f1ad981680e80ee77730631.jpg.d7c111e6057f8494b7cd7926cd7db34e.jpg

 

 

we wern't able to test on the Harmony cart, because the test ROM is 4kSC but the Harmony seems to detect it as 4k. And we are also not sure if the Harmony cart supports 4kSC at all.

 

 

 

 

Link to comment
Share on other sites

3 hours ago, Thomas Jentzsch said:

Yea, I had already edited my post. There is a bug in Stella (new issue here).

 

Fixed!

As far as I can see from your code you are storing random values in the SC-RAM when a read (or ghost-read) occurs to the SC-RAM write port.

 

I think it would be a better emulation to store the last byte on the data-bus to the SC-RAM.

 

Technically the data-bus is not driven in that cycle. It has been driven by the cartridge with the last value from the ROM ($f0 in our example here) until the beginning of that cycle (indicated to the cartridge by the address-bus change).

b9 80 f0 	      lda	RAM_Messages+0,Y

 

so the situation is (nearly) the same than reading a value from an ZP write address. Only that in this case the cart and not the 6502 is reading the (fading) value from the data-bus.

 

If there are some pull-ups or downs involved in the data-bus circuitry the value might fade faster on different setups and the read will be not be the last value on the data-bus.

 

So using random values or the last value on the bus is both not an exact emulation. But I consider using the last value the better solution.

 

I am not sure, but even if some disturbance is involved on the data-bus the resulting value will not be last byte, but it should be always the same value for the same "start" byte.

 

 

  • Like 1
Link to comment
Share on other sites

Real superchip games use standard mask roms, (eg. there's an 8k rom for an f8SC game, even if two pages of it aren't actually visible by the 2600, as the ram is mapped there), with the Sara chip handling the chip select signal to the rom.

 

My pet theory is that when addressing the first page on a bank, the rom can be briefly selected while the address bus is stabilizing, thus driving the data bus with whatever is on the mask rom on that address (which is not what you find on the dump unless the chip has been removed from the cart to obtain the binary).

 

This doesn't happen on an harmony/melody/unocart/pluscart, where the arm is emulating the cartridge hardware and only reacts after the address is stable, thus resulting in the last value on the bus to be written to ram (edit: after rereading the old thread, the harmony seems to drive one of the bits high).

 

 

Edited by alex_79
  • Like 3
Link to comment
Share on other sites

Huh. I guess I had better compile a version of Stella with this fix applied to make sure I'm not doing this in Penult anywhere. The idea that it may have been missed both on Stella and even playing on real hardware through a multicart is concerning.

Link to comment
Share on other sites

3 minutes ago, Karl G said:

Huh. I guess I had better compile a version of Stella with this fix applied to make sure I'm not doing this in Penult anywhere. The idea that it may have been missed both on Stella and even playing on real hardware through a multicart is concerning.

It just fails differently on a multicart and real SC-RAM/masked ROM hardware, but I don't see any chance that your code will work as expected on a multicart when it uses this "feature".

 

 

  • Thanks 1
Link to comment
Share on other sites

6 hours ago, Al_Nafuur said:

we wern't able to test on the Harmony cart, because the test ROM is 4kSC but the Harmony seems to detect it as 4k. And we are also not sure if the Harmony cart supports 4kSC at all.

Simple trick: just copy the 4kSC ROM two times to get an F8SC. When both banks contain the same, it does not matter when a bank-switch is triggered.

 

Also when accessing original SARA, you have to take into account that the RAM is too slow for handling the "full" clockspeed of the 2600. This is why you can't have code running from $F0xx.

Link to comment
Share on other sites

Semi-related question: why don't Tigervision cartridges (method 3F) bank switch on phantom reads?

 

Take Miner2049er for example. In bank 2 at address $3611, the instruction STA VSYNC,X will cause a phantom read of address 0.

 

Absent some other condition, this phantom read will cause a switch to bank 0.

 

What's the condition in the cartridge that prevents this from happening?

 

 

If we say that 3F only bank switches on access of address $003F then it solves the problem. However, it is my understanding that any primary TIA address will work, thereby causing the issue of phantom access.

 

 

 

Edited by JetSetIlly
Link to comment
Share on other sites

11 minutes ago, JetSetIlly said:

Semi-related question: why don't Tigervision cartridges (method 3F) bank switch on phantom reads?

 

Take Miner2049er for example. In bank 2 at address $3611, the instruction STA VSYNC,X will cause a phantom read of address 0.

 

Absent some other condition, this phantom read will cause a switch to bank 0.

 

What's the condition in the cartridge that prevents this from happening?

 

 

If we say that 3F only bank switches on access of address $003F then it solves the problem. However, it is my understanding that any primary TIA address will work, thereby causing the issue of phantom access.

 

 

 

IMHO because VSYNC is $00 a bank switching will only occur if X is $3F.

 

Also a ghost write (with sta VSYNC,X) or a ghost read with (lda VSYNC,x) should never appear, because no matter what value X has there will never be a page crossed if the base address is the first byte of a page.

Link to comment
Share on other sites

8 hours ago, Al_Nafuur said:

we wern't able to test on the Harmony cart, because the test ROM is 4kSC but the Harmony seems to detect it as 4k. And we are also not sure if the Harmony cart supports 4kSC at all.

How hard would it be to expand the test ROM to F8SC?

Link to comment
Share on other sites

5 minutes ago, Pat Brady said:

How hard would it be to expand the test ROM to F8SC?

according to @SvOlli its not hard:

2 hours ago, SvOlli said:

Simple trick: just copy the 4kSC ROM two times to get an F8SC. When both banks contain the same, it does not matter when a bank-switch is triggered.

 

so here you go:

textkernel_HSC_8kSC.asm.bin

  • Like 1
Link to comment
Share on other sites

1 hour ago, Al_Nafuur said:

IMHO because VSYNC is $00 a bank switching will only occur if X is $3F.

 

Yes. If we say that the switching only ever occurs on access of $003f then that works. But I'm not sure it is limited to that single address.

 

According to this old document by Kevin Horton (v6.0 of Cartridge Information) then scheme 3F will switch on an access of an address between 00 and 3f.

 

http://kevtris.org/files/sizes.txt

 

Maybe the document is wrong and it strictly works with address 3f

 

1 hour ago, Al_Nafuur said:

Also a ghost write (with sta VSYNC,X) or a ghost read with (lda VSYNC,x) should never appear, because no matter what value X has there will never be a page crossed if the base address is the first byte of a page.

 

It will. There's always a ghost read with indexed addressing. The base address is put on the bus while the index addition is taking place.

 

Link to comment
Share on other sites

The bankswitch happens if any address with both A6 and A7 low is accessed, and if A12 goes from low to high right after that access.

 

That second part is the reason why that write (sta VSYNC,X, with X=$40) does not cause a bankswitch.

 

This very old thread explains how the 3f banking was implemented.
I quote it here with fixed formatting of the ASCII schematic.

I also changed the labeling to avoid confusion about what A11 and A12 represent in the schematic:
"RA11" and "RA12" are the ROM address pins, while "A11" and "A12" refers to the ones from the CPU.

 

Quote

Here's some text I got from Kevin Horton and Chad Schell plus an ascii
diagram I did up. Not 100% sure of the inductor values or the caps to
ground.

 

-3F: Tigervision was the only user of this intresting method. This works
in a similar fashion to the above method; however, there are only 4 2K
segments instead of 4 1K ones, and the ROM image is broken up into 4 2K
slices. As before, the last 2K always points to the last 2K of the image.
You select the desired bank by performing an STA $3F instruction. The
accumulator holds the desired bank number (0-3; only the lower two bits
are used). Any STA in the $00-$3F range will change banks. This appears to
interfere with the TIA addresses, which it does; however you just use
$40 to $7F instead! :-) $3F does not have a corresponding TIA register, so
writing here has no effect other than switching banks. Very clever;
especially since you can implement this with only one chip! (a 74LS173)

 

 

They are the simplest things inside. normal 8K ROM and a 74xx chip.
It's a quad transparent latch. (can't remember the part # now exactly).

D0, D1 run to the inputs on the D inputs on the latch, and the 2 outputs
run to the upper address lines on the ROM.

/OE runs to A11, and there are pullups on the outputs so that when A11
is high, the outputs tristate but are pulled up. This selects the last
bank of ROM into the second bank in the 6502 address space.

when A11 is low, the outputs of the latches are in effect so it selects
the desired bank. The clock line is connected to A12, and the two enables
are tied to A6 and A7.

To get a valid latch, A12 has to go from LOW to HI, while A6 and A7 are
low.


----------------------------------------------------------------------------


74173


4-bit 3-state D flip-flop with reset, dual clock enables and dual output
enables.



      5v      5v
       |       |
       |       |
       /       /
      5.1k    5.1k               +---+--+---+
       /       /   gnd______/OE1 |1  +--+ 16| VCC________5v
       |       |   A11______/OE2 |2       15| RST________gnd
       | RA11__|______________Q0 |3       14| D0_________d0
RA12___|______________________Q1 |4   74  13| D1_________d1
                              Q2 |5  173  12| D2_________gnd    
                              Q3 |6       11| D3_________gnd
                   A12_______CLK |7       10| /CLKEN1________,
                   gnd_______GND |8        9| /CLKEN2___,    |
                                 +----------+           |    |
                                            _______     |    |
                              A7___///_____|       |____|    |
                                  750      |_______|    |    |
                                              10uh     ___   |
                                                       ___   |
                                                        |    |
                                                        |    |
                                                       gnd   |
                                            _______          |
                              A6___///_____|       |_________|
                                  750      |_______|    |
                                              10uh     ___
                                                       ___
                                                        |
                                                        |
                                                       gnd

 


when executing the code "sta VSYNC,x" , there's a ghost read from the base address $00, followed by the write to the final address $40.
So, while $00 is an hotspot, the bankswitch is not triggered, because there's not the clock pulse (A12 going from low to high) until after A6 goes high, which disables the latch.

 

  • Thanks 2
Link to comment
Share on other sites

3 minutes ago, alex_79 said:

The bankswitch happens if any address with both A6 and A7 low is accessed, and if A12 goes from low to high right after that access.

 

That second part is the reason why that write (sta VSYNC,X, with X=$40) does not cause a bankswitch.

 

This very old thread explains how the 3f banking was implemented.
I quote it here with fixed formatting of the ASCII schematic.

I also changed the labeling to avoid confusion about what A11 and A12 represent in the schematic:
"RA11" and "RA12" are the ROM address pins, while "A11" and "A12" refers to the ones from the CPU.

 


when executing the code "sta VSYNC,x" , there's a ghost read from the base address $00, followed by the write to the final address $40.
So, while $00 is an hotspot, the bankswitch is not triggered, because there's not the clock pulse (A12 going from low to high) until after A6 goes high, which disables the latch.

 

Fantastic information.

 

Link to comment
Share on other sites

5 hours ago, Al_Nafuur said:

So using random values or the last value on the bus is both not an exact emulation. But I consider using the last value the better solution.

I wouldn't call it a solution, really, as there is no way to emulate this behavior because it can vary between consoles and between different carts in the same console, or even by waving your hand near the console or cart, or by temperature or other environmental effects.

 

While I think Stella's behavior of using the last value by default is fine, the optional setting that randomly drives unused bits is very important in testing so that certain bugs may be found.

Link to comment
Share on other sites

19 minutes ago, alex_79 said:

The bankswitch happens if any address with both A6 and A7 low is accessed, and if A12 goes from low to high right after that access.

 

That second part is the reason why that write (sta VSYNC,X, with X=$40) does not cause a bankswitch.

 

This very old thread explains how the 3f banking was implemented.
I quote it here with fixed formatting of the ASCII schematic.

I also changed the labeling to avoid confusion about what A11 and A12 represent in the schematic:
"RA11" and "RA12" are the ROM address pins, while "A11" and "A12" refers to the ones from the CPU.

 


when executing the code "sta VSYNC,x" , there's a ghost read from the base address $00, followed by the write to the final address $40.
So, while $00 is an hotspot, the bankswitch is not triggered, because there's not the clock pulse (A12 going from low to high) until after A6 goes high, which disables the latch.

 

That's one of my favorite circuits for bankswitching. Particularly I like the pair of RLC circuits to delay the write pulse until the right time.

  • Like 1
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...