Vice version: 3.9 GTK3, compiled from source with options "--enable-gtk3ui"
OS: Linux Mint 22 (kernal 6.12.6, GCC 13.3.0)
A routine I've used to detect presence of extra drive RAM in 1541/1571 no longer works properly (didn't work in 3.8 either, not sure when it broke). In the code below, when no drive RAM is present at $6000, the emulator takes the branch $20f when it shouldn't. Looking at $6800 with Vice monitor shows that the byte contains $00 both at the LDA instruction and after the INC.
On a 1571, it works, but only because the memory configuration has an $ff at $6800 instead. If I change it to use $6802, which defaults to a $00 byte, this fails as well.
Also, if I change the CMP $6800 to CMP $0000 (where this address also contains a $00 value), the expected result is returned. The zero flag is set as the value is the same, and the branch is not taken. It seems the problem is comparing to a zero value only in address space that doesn't contain RAM.
Also, the Vice monitor returns a different value from this memory space than a software-based drive monitor does (such as the Warp Speed cartridge's). Using "m $6800" with cartridge monitor returns a value of $68 at $6800 instead of the $00 that Vice monitor shows with sidefx off or $ff with sidefx on. This is somewhat confusing. Is there a way to have the vicemon show what software reads would actually be seeing?
.8:0205 78 SEI
.8:0206 AD 00 68 LDA $6800 ; A=$00
.8:0209 EE 00 68 INC $6800 ; $6800 still contains $00 after this
.8:020c CD 00 68 CMP $6800 ; Z=0 after this, but should be Z=1
.8:020f D0 0E BNE $021F ; jump is taken, RAM at $6000 detected
.8:0211 AD 00 88 LDA $8800
.8:0214 EE 00 88 INC $8800
.8:0217 CD 00 88 CMP $8800
.8:021a F0 06 BEQ $0222
.8:021c A9 08 LDA #$08
.8:021e 2C A9 06 BIT $06A9
.8:0221 2C A9 00 BIT $00A9
.8:0224 85 C7 STA $C7
.8:0226 60 RTS
https://sourceforge.net/p/vice-emu/code/43662/ might be releated (apparently this is what broke it)
That said, we DO have a test program to back up this change: https://sourceforge.net/p/vice-emu/code/HEAD/tree/testprogs/drive/openbus/
In any case, this code actually looks broken to me - there is a 1 in 256 chance it will fail, depending on what is contained in the RAM address being tested before the test - and even regardless of what our "what is read from open bus" theory looks like
"On a 1571, it works, but only because the memory configuration has an $ff at $6800 instead. If I change it to use $6802, which defaults to a $00 byte, this fails as well."
This btw sounds very wrong. Bytes in RAM don't default to anything - there is just a certain chance for a certain value to manifest at a certain address at powerup. So anything relying on the powerup state is broken by design.
Whatever, we need a test case to check a) uninitialized RAM content and b) verify the open bus reads some more. I am not convinced this is an actual bug in VICE for that matter - it seems to be one of those cases were software that only works by chance stopped working, because the chance flipped.
Forgot:
No, this is not possible. Because "what software reads would actually be seeing" depends on the actual software that is running (and there is no such thing when using VICE monitor). Like in this case, the value is the last value that was on the bus - but there is no such value in the vice monitor :)
I use this on real hardware (1541-II mostly) and I have not seen it fail there yet. The value read by the drive with no memory mapped at the address always indeed comes back as the high byte that was part of the read (at least this seems to be true with LDA and CMP instructions). Therefore, an INC to this address and then reading it back again should result in the same value instead of the incremented value.
you are right (brainfart on my end).
So first thing i did: i ran this openbus.prg on my c64+1541 ... and it works (green border). Same in VICE.
A quick test in the monitor seems to indicate it is always reading 0 though. Now i am confused - why does that testprogram work?
OK some more checking. Unfortunately the mentioned fix is indeed the problem. It fixes the case when open io is read by an indexed instruction - which is also what the test program checks.
it does however break the simple case, without indexing... args
waiting for @ikorb to comment (he made that test). can't think of a simple fix right now. Its tricky, because right now the "floating value" is set by the dummy reads (which makes lda abs,x work) - but with a regular "lda abs" there is no such thing. we must somehow differentiate between the two things - outside of the cpu core
Last edit: gpz 2025-03-30
PS: once we fixed this, we need to check the same thing on vic20 and pet as well
yes the PET also reads the high byte of the address from empty space. I never researched deep enough to know why. And I never heard that it would behave differently for indexed addressing modes...
I even found a test program from Commodore that depends on reading $E8 from $E800:
r45467 | rhialto | 2025-01-28 21:14:43 +0100 (Tue, 28 Jan 2025) | 15 lines
Also return empty space on E80x.
I found a test program that cares about this:
8032.mem.prg from https://www.zimmers.net/anonftp/pub/cbm/pet/utilities/
It tests the 64K memory expansion and wants to see empty space at E800 if the
"I/O peek through" is enabled:
.C:096d A9 CC LDA #$CC ; enabled; i/o peek though; bank 3 and 1; no w/p
.C:096f 8D F0 FF STA $FFF0
.C:0972 85 00 STA $00
.C:0974 AD 00 E8 LDA $E800 ; I/O area but no chip selected
.C:0977 C9 E8 CMP #$E8
.C:0979 F0 07 BEQ $0982
Last edit: Olaf Seibert 2025-03-31
A few tests performed on a real 1541-II result in reading the high byte every time. Tried with load and compare instructions in all valid addressing modes.
However, crossing the page boundary with an index mode results in different behaviour.
LDX #$01
LDA $67FF,X (A = contents of $6700,X, which is $700,X when no RAM connected)
LDA $68FF,X (A = contents of $6800,X or $68 when no RAM connected)
Last edit: Lord Crass 2025-03-31
Have you tried the test program linked above? it shows the case where it is NOT the high byte of the address. (You'll have to craft some specific cases with page boundary crossing to make this happen)
With abs,x the value "read" should be the one resulting from the dummy read, ie from the address before the high byte was corrected after adding the indeed.
I'm making some tables atm to check if this is really the whole truth. I'd expect at least one more weird case :)
why it happens is clear now - when trying to read unconnected bus space, the cpu will "see" the last value whatever else left on the bus.
now why it is different between instructions is a direct consequence out of this, eg for "lda abs" this will be the high byte of the address. For something like "lda abs,x" it will be the value resulting from the dummy read that happens before the actual read.
Now the problem with fixing this in the emulation is that in the first case, the value is not actually read or written (so the fix in #43662 does not work for it)
Now what puzzles me - we have a test program for vic20:
https://sourceforge.net/p/vice-emu/code/HEAD/tree/testprogs/VIC20/unconnected/ (although it is a bit hard to read, it seems it does actually check "lda abs" and "lda abs,x")
However, i can not find the place in xvic where it handles the case when it should return address highbyte. Somehow it just works. shrug
I edited my last comment as I forgot to test the case of crossing page boundary.
Indirect indexed also behaves like absolute indexed. Returns what's in the prior page.
Ran the openbus.prg with stock ROMs in NTSC C64C and 1541-II drive and it failed the majority of the time, highlighting a seemingly random byte as being wrong on each failure. Rough estimate is that it returned a green border maybe 20% of the time.
Thats because the transfer routines are PAL only - i didn't manage to fix them :/ Any help with that appreciated, of course :)
So, i found something interesting - i did not expect this. (This behaviour may be the reason for other strange effects as well - anything that relies on being triggered by reads in codespace)
in memiec.c put a printf
and same in drivemem.c
now in vice mon:
you get this output (blank lines after Z in monitor inserted by me)
so apparently the cpu core always fetches 5 bytes in advance for each instruction - and thats why the current fix does not work for lda abs (and only works by chance for lda abs,x)
So the question is why it does this - and if it is even possible to fix in the current architecture
Theory is wrong - this is the disassembler that is calling it 5 times, AND then triggers another bug.
I have a temorary fix:
I comitted in r45617. Please test - also please try a bunch of random "problem cases" :)
Performance might have suffered, please tell if its a noticable difference.
If there are still weird problems: you can get debug info when starting with -debug, please attach the relevant part
Just for the records, so i don't forget:
1) the (drive) test program needs to be updated to check the lda abs case too
2) Star_Rank_Boxing_Green_Label_GameStar_s0.g64 works now :)
Confirmed this fixes the problem with check I used in WS V3 cart. It also works as expected with Star Rank Boxing. Crashes if drive RAM enabled, works if disabled. I think one of the 21 Second backup copiers or the VG Datashack file copier also had some roundabout check for drive RAM, I'll see if I can dig that up and test as well.
Confirmed working with 21 Second Backup v4.1, which does this:
The .g64? Doesn't work for me... do i NEED drive RAM for this? :)
No, it's a parallel-only copier. The mirrored access it does at $6000 will cause it to crash if you have memory mapped there though. Memory at $8000 is no problem.
Other parts the protection code are very specific to the drive ROM. Be sure you're using a 1541 and not a 1541-II. You also have to write-protect the disk or it won't load.
AAAH old drive
x64sc -default -ntsc -drive8type "1541" -userportdevice "21" -parallel8 "4" -attach8ro "20-sec-backup/VG Datashack 21 Second Backup v4.1.g64"
working indeed, cool!
I think I finally understand where that high address byte is coming from!
This is because with a LOAD instruction, the last successful read on the bus was the high byte of the address.
Example: LDA $9000 reads AD 00 90 as instruction bytes and then from $9000.
Even LDA ($12),Y reads B1 12 as instruction bytes, but then 00 90 from the zero page, then from $9000.
The only exception is when there is indexing with a page crossing. Then there is a dummy read from $90xx before the real read of $91xx. In that case you get $90, not $91.
Of course that assumes that the value remains on the unloaded bus for not only one but for 2 cycles.
Another exceptional case would be if you had empty space in the zero page. Then you would get different results when you use LDA $0012 or LDA $12.
You're not limited to the high byte of the (in-incremented or current) address, if the dummy read hits a non-open memory address its content will stay on the bus.
Last edit: Ingo Korb 2025-04-01
And the last time I made any changes for unmapped addresses in the PET, I must have been tricked by the Vice monitor which doesn't show it like the CPU sees...