The TCBM port handling is broken in xplus4 in Vice 3.9.
Games triggering the bug:
Bard's Tale
https://plus4world.powweb.com/software/Bards_Tale
Bard's Tale II
https://plus4world.powweb.com/software/Bards_Tale_II
Dragon Wars
https://plus4world.powweb.com/software/Dragon_Wars
Essentially each gets stuck at loading process when the custom loader kicks in. Each of them loads in Yape correctly.
I've debugged BTI. Here the loader uses ASL $FEF2, to clear handshake bit, which doesn't happen because the port read functions in the TCBM layers handle the unconnected bits as being 1 when they are 0.
It is simple to verify, in Yape the default states of $FEF0-$FEF5 at idle are as follows:
00 00 C0 FF 00 40
In the case of Vice this reads
00 FC FF FF 00 40
The correct port read return functions in plus4tcbm.c should be the following
datab_read function should return:
return (
(tiatcbm[dnr].datab & tiatcbm[dnr].ddrb) |
(tpid_outputc[dnr] & (~tiatcbm[dnr].ddrb & 0x03))
);
datac_read function should return:
return (
(tiatcbm[dnr].datac & tiatcbm[dnr].ddrc) |
((tpid_outputc[dnr] << 4) & (~tiatcbm[dnr].ddrc) & 0x80) |
((tpid_outputc[dnr] >> 1) & (~tiatcbm[dnr].ddrc) & 0x40)
);
I haven't checked port A behaviour yet, for extreme cases, like if the port is set on both ends for both output or both input. Or if port A is split between read and write lines, dataa_read might need to be adjusted as well.
Applying these fixes make both BTI and BT II load, Dragon Wars doesn't get stuck, it continues but then resets the machine, probably due to an unrelated bug. Strangely Dragon Wars works if loaded with Vice 3.6.1 so there must some regression bug lying around.
Would be really great to have some dedicated test programs for this (which can be used for the testbench)
Oh and... it would help if you could try newer Versions and perhaps tell where it broke - it could be another fix that did it
Attached a rudimentary test app, loads from $2000-$2060, start with G2000 runs the test, writes the result into $2060 and exits to monitor. #$01 in $f2060 means pass, #$02 means fail. I'm not familiar with the testbench framework, so the test needs to be adapted for it.
Rechecked all apps with 3.61., 3.7, 3.8, 3.9 and 3.9 r45735. Each version shows the same issues
Unconnected bits show as 1 when data direction is set to input
BT I stucks at track 1
BT II stucks at track 3
Dragon Wars sticks at track 18
I must have run Dragon Wars in Yape instead of 3.61, my mistake, that was a false positive.
From 3.8, smart attach/autostart requires emulator power cycle, to work consecutively, which is fine.
smart attach/autostart in 3.9 r45735 does require multiple power cycles to work, otherwise it breaks multiple ways. seen drive CPU jams, and device not found errors.
Cool! Please attach source too, i'll adapt it for the testbench then
Dragonwars indeed looks like a loader related problem as well (watch fffc, then chis) but i am not familiar enough with plus4 to debug this properly :)
Last edit: gpz 2025-08-12
Run a disam tool on the test program, attached the asm file. It's very simple, just testing the unconnected lines on port b and on port c. As a side effect it changes direction and port values on the connected lines as well, but those are not tested.
I'll come back to Dragon Wars later.
Looking at the chis log of Dragon Wars, it did transfer a complete sector to the machine, plus 1 byte which is the error code, that is set to #$27, and it expects #$01, that's why it resets.
Drive code look like:
Command loop is at $43e. reads 3 byte from computer first byte is command goes to $d1, then track number goes into $e, then sector number goes into $f
Command 3 looks like format located at $4be
Command 2 is write sector located at $48c
Command 1 is read sector at $459
Load sector subroutine is at $518. Error code will go into $d2, retry count is in $c4 set at #$03, If $c4 gets to zero, then the error code is set to #$27. This is what actually gets triggered/ It tries to read Track #1 Sector #4.
The sector load code calls to functions $63d and $678. $678 is just waiting for SYNC with timeout loop. Timeout is treated as error. $63d reads 8 bytes for header. Then reads the sector. Once the sector is read it calls a ROM function $F618 at $549 and it expects a retun value of #$0f into $2f, this doesn't match and the 4 tries eventually runs out, and the whole thing bugs out. There is an other 3 retries on the outer loop, and I've tried set the retry count to #$ff but that's no help, so the issue is not that the disk doesn't spin into place, but rather somewhere in the reading and decoding of the sector.
applied your patch in r45736 - i am leaving this open for the time being because of that other problem
added your test in r45742 - have a look at that commit if you are interested in how exactly the testbench works :)
added your test in r45742 - have a look at that commit if you are interested in how exactly the testbench works :)