apple2ts
apple2ts copied to clipboard
Apple 2TS does not work on the sample WOZ images provided in applesauce
Hi, some of the woz images from Applesauce (http://evolutioninteractive.com/applesauce/woz_images.zip) are not working with Apple2TS.
Some of non-working titles
- Blazing Paddles (incorrect cross track synchronization)
- Miner 2049er II
- Wings of Fury - Disk 1
Balance of Power - fails cross-track sync check on track 35, see https://github.com/AppleWin/AppleWin/issues/1022
Blazing Paddles - stuck at track 0/0.5 - this might be an unenhanced ROM issue?
Miner 2049er - oscillates between tracks 0-3
Sammy Lightfoot - boots to title screen, press
Bilestoad - works Bouncing Kamungas - works DOS 3.3 - works Hard Hat Mack - works Print Shop Companion - works Rescue Raiders - works Mr. Do - works
Balance of Power - works Blazing Paddles - works Miner 2049er - works due to same commit that fixed Balance of Power: https://github.com/ct6502/apple2ts/commit/743fcb78721111996fa9b843af47f323e05232c6 Sammy Lightfoot - works Wings of Fury - still just boots to Applesoft
For the wings of fury, it does not works probably is due to incorrect handling of softswitch $C08D as mentioned in the readme that is shown below.
READING OFFSET DATA STREAMS
Some programs will reset the data latch mid way through a nibble (via LDA $C08D,x). This will cause the incoming data stream to read offset from the normal nibble starts. These streams will usually incorporate timing bits between nibbles that will not become data bits instead. Some good test candidates for this are "Wings of Fury" and "Stickybear Town Builder". If you can get to the title screens, then you have passed.
Where was that README from?
You can find the readme.txt under the folder woz test images in the woz_images.zip. It is also mentioned in WOZ 2.1 reference under the section "Abusing Disk Controller Soft Switches for Fun and Profit". The Wings of Fury has apparently implemented E7 protection.
For convenience, here is the paragraph from the WOZ 2.1 reference site,
Another nuance that needs to be implemented is that reading $C08D,X will reset the sequencer and clear the contents of the data latch. This is used to great effect in the E7 protection scheme to resynchronize the nibble stream to make timing bits become valid data bits.
Your implementation on LATCH_ON do fixes some games with E7 protection (for e.g. G.I JOE and Ikari Warriors).
For Wings of Fury, it does pass the initial E7 protection. I got stuck at track 2.
- It passes the detection of initial EE
- It then read 7 bytes and put into data memory 0xf0. The values tallies with values obtained from MAME / AppleWin.
I had to uncomment some code in the $C08D,X data latch. Now Wings of Fury gets past track 0, and gets stuck on track 2. Putting a breakpoint at $6E21 shows that it reads the first few chunks of data and then goes off the rails. Need to compare with Virtual ][ to see where my emulator deviates...
The implementation of resetting of the data register at $C08D,X sometimes do not work if the value is greater than 0. This is observed in one of the WOZ test images called Stickybear Town Builder. Because the data register is non zero, this software will hang after clicking Build Town option.
One possible fix is change the current code
if (value >= 0) {
dataRegister = value
}
to the following
if (ds.writeMode && value >= 0) {
dataRegister = value
}
The problem code is at hex $0359, where it does a STA $C08D,X. A=$AD
No idea why it is trying to load the write data latch during a read, but you are correct, this is definitely a problem.
Current status as of 4/19/2025:
All these work, added unit tests: Balance of Power Blazing Paddles King's Quest Miner 2049er (briefly broken as part of fix for #134) Sammy Lightfoot
Stickybear Town Builder also works fine now, but no unit test because it requires user interaction to get to where it was hanging.
Wings of Fury - gets to track 2, then infinite loop around address $6Cxx.
I had to uncomment some code in the $C08D,X data latch. Now Wings of Fury gets past track 0, and gets stuck on track 2. Putting a breakpoint at $6E21 shows that it reads the first few chunks of data and then goes off the rails. Need to compare with Virtual ][ to see where my emulator deviates...
Hi, i have taken a look at why Wings of Fury is hung at track 2. The code failed at 0x6c10 where the expected value is 0x96 but return 0xe5. I have tabulated it for Apple2TS (left side) vs my emulator (right side). Apple2TS should read 0x96 at bit location 943
| Cycle | C0EC Value | Bit Location | Diff Cycle | Cycle | C0EC Value | Bit Location | Diff Cycle |
|---|---|---|---|---|---|---|---|
| 3045844 | 0xff | 831 | 3160510 | 0xff | 832 | ||
| 3045883 | 0xff | 841 | 39 | 3160549 | 0xff | 842 | 39 |
| 3045922 | 0xff | 851 | 39 | 3160588 | 0xff | 852 | 39 |
| 3045961 | 0xff | 861 | 39 | 3160627 | 0xff | 862 | 39 |
| 3046007 | 0xff | 871 | 46 | 3160666 | 0xff | 871 | 39 |
| 3046039 | 0xd5 | 879 | 32 | 3160698 | 0xd5 | 879 | 32 |
| 3046065 | 0x9d | 887 | 26 | 3160731 | 0x9d | 888 | 33 |
| 3046098 | 0x9a | 895 | 33 | 3160764 | 0x9a | 896 | 33 |
| 3046130 | 0x97 | 903 | 32 | 3160796 | 0x97 | 904 | 32 |
| 3046162 | 0x9b | 911 | 32 | 3160828 | 0x9b | 912 | 32 |
| 3046199 | 0xaa | 919 | 37 | 3160858 | 0xaa | 919 | 30 |
| 3046287 | 0xe5 | 941 | 88 | 3160953 | 0x96 | 943 | 95 |
On further inspection of Apple2TS code, i noticed that in diskdata.ts line 178
if (dataRegister & 128 && cycleRemainder <= 6) break
If this line of code is changed to the line below, it will work. Setting the cutoff to 70 also works. In both cases, this modified setting will break some other images
if (dataRegister & 128) break
Hi, finally manage to solve Wings of Fury, just changing the getNextByte function from
if (dataRegister > 0 || bit) {
dataRegister = (dataRegister << 1) | bit
}
to
if (dataRegister > 0 || bit) {
if (dataRegister & 128) {
dataRegister = 0
}
dataRegister = (dataRegister << 1) | bit
}
The extra condition is required so that the data register is cleared when the data register high bit is set.
Hi, also tested one round with all the WOZ 2.0 images. All are working except for the following
- DOS 3.2 System Master.woz (Apple2ts is using P6 ROM)
- First Math Adventures - Understanding Word Problems (Apple2ts is clearing the data register to 0. Refer to the WOZ test image readme)
- PlanetFall (cycleRemainder <= 6 fails, working is cycleRemainder <= 8)
- Border Zone (Works but with bit timing of 4us, Loading time is around 50s. This image is with bit timing 3.5us. The expected loading time is 18s)
- The Print Shop Companion - Disk 1, Side A (Works but very slow in firefox)
The code below should be able to address point 2. and 3.
const getNextByte = (ds: DriveState, dd: Uint8Array, cycles: number) => {
// const tracklocSave = ds.trackLocation
// If no disk then return random noise from the drive.
// Some programs (like anti-m) use this to check if no disk is inserted.
if (dd.length === 0) return randByte()
let result = 0
// Read individual bits and combine them.
cycleRemainder += cycles
while (cycleRemainder >= 4) {
const bit = getNextBit(ds, dd)
if (!(dataRegister & 0x80 && !bit)) {
if (dataRegister & 0x80) {
dataRegister = 0
}
dataRegister = (dataRegister << 1) | bit
}
cycleRemainder -= 4
// Changing this cycleRemainder cutoff to less than 6 will break
// loading certain disks like Balance of Power.
// 6 - will break Planetfall and Border Zone
// 8 - will work for all the test images in driverstate.test
if (dataRegister & 128 && cycleRemainder <= 8) break
}
if (cycleRemainder < 0) {
cycleRemainder = 0
}
dataRegister &= 0xFF
// if (ds.halftrack === 70 && dataRegister > 127) {
// console.log(toHex(dataRegister))
// }
// if (s6502.PC >= 0x9E45 && s6502.PC <= 0x9E5F) {
// console.log(` getNextByte: ${cycles} cycles PC=${toHex(s6502.PC, 4)} loc=${tracklocSave} dataRegister=${toHex(dataRegister)}`)
// }
result = dataRegister
return result
}
I'm really busy for the next week or so, so I won't be able to look at this until after that, but overall this looks great. I don't see a slowdown with Print Shop Companion, at least not using Chrome. I noticed that you deleted the "fast path" for non-synchronized disks. Does it actually cause any problems, or is it just not that much faster?
The fast path is causing problem, that is why i have deleted it. No worries take your time to make the change. I have also tried updating the getNextByte to support WOZ 2.0 optimalTiming parameter to fix the speed issue of Border Zone. It also fixes on images that rely on non standard bit timing (for e.g. Paul Whitehead Teaches Chess at https://github.com/a2-4am/passport-test-suite/blob/master/Paul%20Whitehead%20Teaches%20Chess.woz
The changes are
-- In diskdata.ts
const getNextByte = (ds: DriveState, dd: Uint8Array, cycles: number) => {
// const tracklocSave = ds.trackLocation
// If no disk then return random noise from the drive.
// Some programs (like anti-m) use this to check if no disk is inserted.
if (dd.length === 0) return randByte()
let result = 0
// Read individual bits and combine them.
cycleRemainder += cycles
while (cycleRemainder >= ds.optimalTiming / 8) {
const bit = getNextBit(ds, dd)
if (!(dataRegister & 0x80 && !bit)) {
if (dataRegister & 0x80) {
dataRegister = 0
}
dataRegister = (dataRegister << 1) | bit
}
cycleRemainder -= (ds.optimalTiming / 8)
// Changing this cycleRemainder cutoff to less than 6 will break
// loading certain disks like Balance of Power.
if (dataRegister & 128 && cycleRemainder <= (ds.optimalTiming / 4)) break
}
if (cycleRemainder < 0) {
cycleRemainder = 0
}
dataRegister &= 0xFF
// if (ds.halftrack === 70 && dataRegister > 127) {
// console.log(toHex(dataRegister))
// }
// if (s6502.PC >= 0x9E45 && s6502.PC <= 0x9E5F) {
// console.log(` getNextByte: ${cycles} cycles PC=${toHex(s6502.PC, 4)} loc=${tracklocSave} dataRegister=${toHex(dataRegister)}`)
// }
result = dataRegister
return result
}
-- drivestate.ts
const initDriveState = (index: number, drive: number, hardDrive: boolean): DriveState => {
return {
index: index,
hardDrive: hardDrive,
drive: drive,
status: "",
filename: "",
diskHasChanges: false,
motorRunning: false,
isWriteProtected: false,
isSynchronized: false,
halftrack: 0,
prevHalfTrack: 0,
writeMode: false,
currentPhase: 0,
trackStart: hardDrive ? Array<number>() : Array<number>(80).fill(0),
trackNbits: hardDrive ? Array<number>() : Array<number>(80).fill(51024),
trackLocation: 0,
maxHalftrack: 0,
lastLocalWriteTime: -1,
cloudData: null,
writableFileHandle: null,
lastWriteTime: -1,
optimalTiming: 32,
}
}
-- decodedisk.ts
const decodeWoz2 = (driveState: DriveState, diskData: Uint8Array): boolean => {
const woz2 = [0x57, 0x4F, 0x5A, 0x32, 0xFF, 0x0A, 0x0D, 0x0A]
const isWoz2 = woz2.find((value, i) => value !== diskData[i]) === undefined
if (!isWoz2) return false
driveState.isWriteProtected = diskData[22] === 1
driveState.isSynchronized = diskData[23] === 1
driveState.optimalTiming = diskData[59]
Thanks @univta0001 for all the help and the code. That final code block seemed to fix a lot more issues, and now even Wings of Fury boots! I'm going to close this bug. If we find other disk images that don't work, let's open a new issue.