Saturday, November 14, 2026

Using an Emulator to Fix Stuff - Torch Triple X (Emulator?) - Part something something...

With the Torch emulator working like a trooper I started to wonder if I could use it to help out in some way in the repair of the real thing. As a reminder, the Torch hardware frequently appeared to have problems with the Limpet RAM expansion board. On one occasion I got a bit cross and some force was applied to the Limpet and, bizarrely, that seemed to fix it. This led me to think it was the connectors it used into the motherboard. It, apparently, was not and after a while it became worse and worse until one day, the Torch would not boot.

I wasn't going to take this lying down, so on the somewhat presumptive assumption the Limpet was at fault, I went and created a new Limpet board which, on it's first couple of tests, actually freakin' worked. 

 

Shiny new Limpet board. Yay!

 

Happy days. Working Limpet.. 


But sadly, not for long. Now, the Torch will not get past the initial pale blue boot screen.

 

Sad face. No boot. :(

The LED on the Limpet only flashes once, and frequently, it displays garbage on screen which is similar to that seen waaaaay back when I was first trying to get the Torch booting. 

A few other things I noted before it went into storage while we moved from Crashed Towers to Crashed Tiny Abode:

  • The LED on the Limpet should normally flash four times during a proper boot. 
  • The voltages across the board were very, very low (down to 4.3v in most places).
  • The Torch did boot a few times with a very upsetting bend in the motherboard.

 

The horror - arrrgh!

 

So, my assumption now is that the Limpet is OK and that there is some mechanical issue with the board, or there is a failed IC hiding somewhere either on the motherboard or (potentially worse) on the Limpet. There are several PAL chips that are basically irreplaceable across both the motherboard and Limpet. This is the worst case scenario that I don't want to think about...

Anyway, on Torch startup it appears that there is some sort of RAM test, and I assume this is signified by the four flashes. Then the screen goes dark blue and shows the Caretaker version (this is basically the BIOS) before starting to boot proper. I wondered if we could nail down exactly what the thing was doing whether that might help in the quest to fix it. 

Fortunately, we have pretty much everything we need. We have the ROM, excellent schematics (redrawn by yours truly), a technical manual, and (thanks to the most excellent chap on the VCF forums) a working emulator. 

Torch Emulator - Talking to the outside word!

First things first, let's disassemble the Caretaker ROM.

But to do that, I needed to get a disassembler installed. And for this there is the most excellent (if rather powerful i.e. complex) Ghidra tool, as recommended by such luminaries as Ken Shirrif (a regular contibutor to Curious Marc's youtube channel). Installing this was a little bit of a chore but not too hard considering I was installing it on Pop OS on my new laptop (did I mention I have a new laptop?) which is a variation of Linux.

Once it was installed I threw the ROM in and it just told me everything... Just kidding. I had to install a specific extension to support the 6303 CPU. Even then, Ghidra still needs to be told what to do. And that includes where to load it... For the Caretaker ROM this needed to be at address $C000. The 6303 documentation identified that the reset vector was at $FFFE which then points back to $C2DC and that was the starting point.

 

On your marks! Reset! Go!
 

But that's not all that lurks in the Caretaker. I know that there is a section of bootstrap code for the 68010 main CPU. And to get this I ran the command:

 

Gimme the code.

So now I had another file that could help enormously. At this point I'm fairly certain that the service processor would copy Caretaker into VRAM before doing stuff, including I assume a RAM check, and then handing off control to the 68010. The questions I had were, is the RAM check carried out by the 6303 or the 68010? Is the RAM test just the VRAM or does it include the main system RAM? If the system RAM, how does it know if there's a Limpet installed? 

The first major discovery was around the 6303 service processor's diagnostic error-reporting routine. At address $C087, Caretaker enters an error handler that initialises several timing parameters before repeatedly calling a routine at $C277. That routine read the diagnostic byte store at $00B9.

But how it interprets that byte is really interesting.

Each diagnostic message consists of four beeps, with two bits controlling each beep:
- The first bit determines whether the beep is short or long.
- The second bit determines whether the pause following it is short or long.

For example, error $21 is binary 00100001. This produces short–long–short–short, with a longer pause after the fourth beep to separate each four beep sequence. That tied up with what I was seeing when the Torch wouldn't boot either with the Limpet (long-short-short-short) or without the Limpet (short-long-long-long).

The HD6303 generates these sounds using its internal timer and output-compare circuitry. The original Caretaker manual confirmed the codes identified in the disassembly and meant that Ghidra was on the right track. :)


Code    Beeps   Description
$01     SSSS    ROM checksum error
$03     SSSL    Video controller faulty
$09     SSLS    RTC stopped
$0B     SSLL    HD6303 internal RAM error
$21     SLSS    Video RAM fault
$23     SLSL    HD6303 serial port timer failure
$29     SLLS    VRAM inconsistent with copied ROM data
$2B     SLLL    Main CPU timeout


This didn't solve the code that I was seeing with the Limpet attached (long-short-short-short) but it might appear somewhere else in the code maybe (foreshadowing...).

Next thing, how the first part of RAM is tested. In actual fact, the 6303 tests the video RAM through a 32KB window located at $4000 to $BFFF and uses bit 2 of its port 1 register to determine which bank is being checked. The firmware writes a test pattern into both banks before reading the data back and comparing it.

The pattern is generated using:
         

        Next byte = (225 x Current byte + 205) mod 256

 

If any comparison fails then Caretaker stores $21 at $00B9 and runs the diagnostic routine i.e. it starts the four beep message, in this case for Video RAM fault.

The next big breakthrough was working out how error $2B gets initiated. This is one I'd seen many, many times (short-long-long-long) almost from day 1 of having the Torch. What happens is that Caretaker does the following:

 

Release the hounds, err, 68010 actually.

 

This code snippet selects VRAM bank 1, writes $FF to $BFFF, and releases the main processor by clearing Port 1 bit 3.

The service processor then repeatedly reads $BFFF, waiting for the value to change, which indicates the main CPU has started doing stuff (or not).

There are three outcomes:

Value              Meaning
$FF                Main processor has not yet responded
$00                Main processor has successfully acknowledged startup
Other value        Main processor has returned a diagnostic code


If $FF remains unchanged until the timeout expires then Caretaker generates error $2B and triggers the diagnostic routine. But if it does change to another non-zero value, then could this be the undocumented 'long-short-short-short' beep error message seen when the Limpet is connected?

Based on what we know about the beeps it's easy to see that 'long-short-short-short' should work out as '10000001' or $81. So, where does this come from? Is it a random number the just so happens to end up in $BFFF? Or is it a specific value that is inserted by some part of the system?

The answer was relatively straightforward.  

Although the HD6303 code we'd examined didn't explicitly generate it, I suspected that the 68010 might be returning the value through the video RAM or main RAM.

To test this, some logging was added logging to the emulator around the $BFFF handshake.

That produced:

[BFFF] SP WRITE PC=C54F bank=1 host=0FFFF old=81 new=FF
[BFFF] SP P1.3 PC=C552 old=1E new=16 host_release=YES
[BFFF] HOST WRITE PC=001011FA logical=0000FFFF physical=0FFFF old=FF new=00
[BFFF] SP READ PC=C55A addr=BFFF bank=1 host=0FFFF actual=00 returned=00

 

This is basically the processors (virtual in the emulator) communicating with each other. The HD6303 wrote $FF, released the 68010, and subsequently received $00.  

Interestingly, the initial behaviour in the emulator was a lie. The emulator had a bit of a shortcut that basically checked the value three times and then just returned '00'. Cheeky. There wasn't actually a need to do this as the code was written to process the communication properly and a quick modification to remove the shortcut was made, and the emulator still booted. Nice.

And now the smoking gun. Disassembling the 68010 bootstrap code revealed:

        0000004A  move.b  #$81,$0000FFFF
        00000052  bra     $00000052 

 

Bingo! The 68010 explicitly writes diagnostic $81 to the memory location monitored by Caretaker, then enters an endless loop. Tracing backwards revealed the reason for this. Immediately before reporting the error, the processor compares a value read from main RAM against an expected test pattern: 

        00000042  cmp.l   (A0)+,D1
        00000044  dbne    D0,$00000042
        00000048  beq     $0000003E
        0000004A  move.b  #$81,$0000FFFF

 

If the comparison fails then, boom!, $81 is written to $FFFF and the 68010 checks out into an infinite loop.

Weirdly, there is also another diagnostic value of $83 which converts to long-short-short-long. I haven't figured out what could trigger that yet...

Anyway, digging further into the 68010 code shows that the main RAM is tested by two passes, the first writing a pattern of 5A5AA5A5 and the second a pattern of A5A55A5A. Rather strange though, the code doesn't know how much RAM is present. It starts the test at $100000 and then just keeps going up until there is a bus error which indicates the end of available RAM. Then it repeats with the second pattern. Very odd but very clever as it means the system can use any amount of additional memory (although I'm not sure what any limit might be..).

Once the two sweeps are complete it writes $00 into $0000FFFF to tell Caretaker the RAM check is complete and all OK.

To check that all this was as expected, the emulator got another modification to deliberately put an error into the RAM test. At address $100000 instead of '5A' the value '5B' was inserted. The results were:

        [RAM-FAULT] First verification pass:
        physical $00100000 5A -> 5B

        [BFFF] HOST WRITE PC=00000052
        logical=0000FFFF physical=0FFFF old=FF new=81

        [BFFF] SP READ PC=C55A
        addr=BFFF bank=1 actual=81 returned=81

 

In other words, it's gone pear shaped! Stick $81 in $0000FFFF!

And the emulator also made the same beep pattern (but a little bit quicker) of long-short-short-short but also showed garbage on screen.

 

Beep, beep, beep, beep (I'm a sheep)

And this is very, very similar to the garbage that the real hardware presents when it's switched on:

 

It's the real thing...

 

So, where does that leave me? Well, I now have a much better understanding of the 'long-short-short-short' beep diagnostic. It indicates that the main 68010 has encountered a RAM readback mismatch during the main RAM tests. That could result from faulty memory chips, but also from problems elsewhere in the memory-access circuitry (address decoding, data buffers, RAM multiplexers or timing/control signals etc).

The Limpet expansion changes the physical configuration of the system, and the bootstrap tests whatever contiguous RAM the hardware makes available, but I can't run the system without it due to the missing U169 chip - which the Limpet effectively replaces. 

So I still can't say whether the failure is in mainboard RAM, Limpet expansion RAM or shared circuitry affected by fitting the Limpet.

But it does mean that my hardware tests can be much more targeted now that I have some idea on how the Torch actually starts up. I'll be using the oscilloscope and logic analyser to have a closer look at:

  • The 68010's /AS, R/W, /UDS, /LDS and /DTACK signals.
  • The DRAM /RAS and /CAS0–/CAS3 signals.
  • Memory address activity during writing and verification.
  • How the Limpet changes memory selection or bus acknowledgement (maybe allowing me to put something in to replace the missing U169)

 

Now, if the weather would just get a bit warmer and I could get over COVID (again), that would be great. 

 

 

Monday, November 09, 2026

Schroedinger's Floppy - Torch Emulator Part 5

One of the cool things in the Torch Triple X emulator is having the floppy disk support working properly. The emulator can read and write floppy images (thanks to the Torch architecture using the SCSI interface for floppies so it’s the same code as for hard disks – yay, Torch!), and now I've even added a separate graphical disk manager to insert and eject floppy disks while the emulator is running. Ohh, get me.

 

Look at that there fancy disk manager window


Everything appeared to be working rather well. And then I hit a snag.

The Torch Triple X runs its own version of System V Unix, and the emulator supports floppy disk images that use the correct format. These images can also be mounted directly under Linux, provided the Linux kernel supports the correct format which just so happens to be the old System V (sysv) filesystem. Most modern Linux kernels have dropped support for SysV, which is why I've had to install an older 6.14 kernel specifically for working with Torch Unix filesystems on my new laptop. (Did I mention I have a new laptop?)

This has been extremely useful as it allows me to copy files directly between my (new) laptop and the emulated Torch

At least, that was the theory.

While working with one of the floppy images, I discovered that attempting to copy another file onto it resulted in a rather unhelpful error: the disk was full. This was weird because, as far as I could see, the disk only a handful of very small files on it. 

Running df under Linux produced something like this: 

 

'df' says that /mnt/flop has 5 blocks free??
And 'ls' shows about 6KB worth of file. WTF?

But the directory listing only had three files listed occupying a grand total of about 6 KB. Where the fudge has the other 700 KB gone?

My initial suspicion was that something had gone wrong with the filesystem's allocation information. System V Unix uses a relatively simple filesystem structure compared with modern filesystems such as ext4. One of the main things it does it to keep track of which disk blocks are available for storing data. So, when a file is created, blocks are allocated to it and then when the file is deleted, those blocks are supposed to become available again.

If that process goes wrong then a disk can gradually fill up with blocks that are no longer associated with any existing file but that aren’t available to use for other files. Since the floppy image had been used several times for copying and deleting files, this seemed like it could be a possibility.

 

i-node, right?
 

Looking at the inode information on the disk image in Torch unix was also confusing. The filesystem supported 174 inodes, but only five were in use. For anyone unfamiliar with Unix filesystems, an inode is essentially the record containing information about a file, including where its data is stored. So, from this, I think I could be fairly sure that there weren't hundreds of forgotten files hiding somewhere.

Something else must be happening.

Rather than continue to dig at an image that might have become corrupted through repeated use, I decided to start again to try an work out what the heck was causing the problem.

I created a brand-new 720 KB floppy image under Linux:

        dd if=/dev/zero of=floppyblank.img bs=1024 count=720

This produces a file containing 737,280 bytes, all initially set to zero.

I then inserted the image into the Torch emulator and used my shiny ‘sh’ front end (pty) in Torch Unix to prepare it:

        format /dev/fdsk/0

This formats the raw device and is basically a SCSI low level format.

        mkfs /dev/fdsk/0 1440

This creates the filesystem as SysV with 1440 blocks of 512KB, giving the expected 720KB size.

        mount /dev/fdsk/0 /mnt/flop

Finally, we mount the blank image so we can write to or read from it.

Running the equivalent ‘df’ command on the Torch showed 1412 blocks free. That means 512KB x 1412 giving 722,944 bytes or 706KB. So far so good and everything looked perfectly normal.

So then I unmounted the floppy, shut down the emulator and mounted the image under Linux (I’m using Pop OS).

        sudo mount -t sysv -o loop,ro testimage.img /mnt/flop

This mounted the image at /mnt/flop. A quick df and, would you believe it, there it was again. Most of the free space was used! And I haven’t done a **** thing with the image at ALL! GAH!

 

WHERE IS MY FREE SPACE?

At this point, I was thinking of two possibilities. Either the emulator was doing something particularly unpleasant to its floppy images when it was formatted so Linux was having difficulty interpreting them, or the emulator was fine but Linux was being a bit crap and was just having difficulty reading them.

So I went back to the Torch emulator to ask again how much free space was available. Even this turned into another minor adventure when the Torch df command initially produced absolutely no output whatsoever. Not an error. Not a warning. Nothing.

GAH!!!

After restarting the emulator and repeating the operation, however, it decided to work. (Isn’t that the definition of madness?)

The Torch reported:

        /mnt/flop /dev/fdsk/0 1412 blocks 174 i-nodes

That meant (you can probably guess):

        1412 × 512 = 722,944 bytes

Or approximately 706 KB free.

So now there were two operating systems looking at the same floppy image and seeing two totally different things. Torch says it’s empty, Linux says it’s full. Brilliant.

But both could read the directory with no issues.

This was beginning to look less like an emulator problem and more like a disagreement over the filesystem's internal structures. There’s only one way to find out. FIGHT! Ahem.

Anyway, after a lot of googling (it’s amazing how interesting filesystem formats can be) I thought I should take a closer look at the superblock. Apparently, every System V filesystem has one of these, and it contains lots of important information about the filesystem including its size, the inode information and (ta-daa) the number of free disk blocks.

So to have a look at the superblock in these images I wrote a python script to read it directly. Yay python!

The script looks at the possible byte orders and filesystem layouts, and it produces the following result:

 

        SysV2 (Big endian)

        First data block : 13

        Total blocks : 720

        Free cache count : 7

        Total free blocks: 706

 

That looked promising. The filesystem contained 720 logical blocks, and its superblock said there were 706 free blocks. In other words, Torch's filesystem ‘metadata’ said exactly what would be expected for a regular blank floppy.

But there was another number that stuck out.

        Free cache count: 7

This is suspiciously close to the seven kilobytes Linux claimed were available. Hmmm.

After a bit more reading it seems that System V filesystems use a linked free-block list to manage available disk space. In other words, rather than storing every free-block address in the superblock, they keep a small collection of addresses there. When those addresses are all used up, another disk block contains the next collection. This continues until the complete list has been followed all the way to the end.

Think of it as a series of pages in a notebook, each containing a list of free locations and a reference to the next page. Even my small brain managed to deal with that analogy..

The Torch superblock contained seven free-block entries:

    35

    32

    29

    26

    23

    20

    17

The first entry, block 35, was especially important because it should lead to the next group of free blocks. So I extended the Python script to examine the contents of block 35.

The first 32 bytes looked like this:

    00 00 00 32 00 00 00 b9

    00 00 00 b6 00 00 00 b3

    00 00 00 b0 00 00 00 ad

    00 00 00 aa 00 00 00 a7

Initially, the script interpreted the first two bytes as the number of entries:

    00 00 = 0

That would indicate an empty list. But this just wasn't right.

But then, if we interpreted the first four bytes as a 32-bit ‘big-endian’ number instead, we get:

    00 00 00 32 = 50

And then all subsequent four-byte values looked like very plausible disk block numbers:

    00 00 00 b9 = 185

    00 00 00 b6 = 182

    00 00 00 b3 = 179

    00 00 00 b0 = 176

So, could it be that the Torch filesystem is storing the number of entries as a 32-bit value, but Linux was interpreting it as a 16-bit value? Hmmm.

To test the theory, I updated the Python script again. This time it treated the count at the beginning of each chained free-list block as a 32-bit value and followed the entire chain.

The output turned out to be the smoking gun:

    Block 35: 50 entries

    Block 185: 50 entries

    Block 150: 50 entries

    Block 112: 50 entries

    Block 262: 50 entries

    Block 212: 50 entries

    Block 362: 50 entries

    Block 312: 50 entries

    Block 462: 50 entries

    Block 412: 50 entries

    Block 562: 50 entries

    Block 512: 50 entries

    Block 662: 50 entries

    Block 690: 50 entries

Fourteen linked blocks with each containing 50 entries.

When combined with the seven entries in the superblock, that gave:

    7 + (14 × 50) = 707

That was frustratingly close to the superblock's reported total of 706 free blocks. Looking at the last free-list block though, block 690, its first entry was zero. So this was the terminating marker at the end of the list, not actually a free block.

So in reality the count was:

    7 + (14 × 50) - 1 = 706

Exactly 706 free blocks. Not approximately 706. Not somewhere in the region of 706. Exactly the number recorded in the superblock. GET IN!

Every supposedly missing block accounted for. Nice.

And then Linux confessed to something that I should have spotted at the start of this impromptu investigation. I had a look at ‘dmesg’ i.e. the Linux kernel messages with and guess what I found:

    sysv_count_free_blocks:

    free block count was 706, correcting to 7

That message explained exactly what the heck Linux was doing. It read the superblock and found the recorded count of 706 free blocks. And then it then attempted to traverse the free-block list itself. But because it expected a 16-bit count where the Torch had written a 32-bit count, it interpreted the first chained block as containing zero entries. And so it stopped.

Linux then decided that the superblock's count must be wrong and used its own calculated value of seven. This explained why df had been insisting that the floppy was almost full even though nothing had been written to it.

So it wasn't a problem with the emulator losing data, it wasn't caused by deleted files failing to release their blocks, and it wasn't a completely corrupt filesystem. It was an obscure difference in how the two Unix implementations represented the linked free-block list.

Now I know what is happening, the next question was what to do about it. What I really wanted was the ability to copy files to an image under Linux and then insert that same image into the Torch emulator.

And so the solution was another trip into Python.

Rather than modify the emulator or rebuild the Linux kernel (ain’t nobody got time for that), I created another python script that converts the relevant free-block structures between the two layouts.

For Torch-formatted images, it changes the linked free-list blocks from a 32-bit count to the 16-bit count expected by Linux. And for images being returned to Torch, it performs the reverse conversion. Everything else, the actual files, directories and inodes are all left untouched.

And to make sure there’s no unfortunate data loss (due to almost certain bad programming or operator error), the utility creates a separate output file rather than modifying the original image.

To convert from Torch to Linux:

        python3 torch_sysv_convert.py --from torch testdisk.img testdisk_linux.img

 

The resulting converted image can then be mounted under Linux:

        sudo mount -t sysv -o loop testdisk_linux.img /mnt/flop

 

Files can be then be copied on to it normally.

Once finished, the image is unmounted:

        sync

        sudo umount /mnt/flop

 

And converted back:

        python3 torch_sysv_convert.py --from linux testdisk_linux.img testdisk_torch.img

 

The final image can then be inserted directly back into the emulator.

The python script also validates the free-block chain, checks for invalid or duplicate block addresses, and verifies the resulting image. I love python. :)

To make sure everything was working correctly I took a freshly formatted, empty Torch floppy image, converted it to Linux format and mounted it.

This time, Linux reported the expected free space.

Success!

But reading a blank disk is one thing, successfully creating files and then using them in the original operating system is another. So a more comprehensive test was required...

Starting with the same blank Torch image, I copied the Torch Unix kernel onto it. Then I converted the image to Linux format and mounted it on the laptop. Next, I created two additional files, each 100 KB in size. That forced Linux to allocate a decent number of blocks and update the filesystem's free-space attributes.

After un-mounting the image, I converted it back into Torch format and inserted it into the emulator.

And everything worked. Perfectly. The files were present, the filesystem mounted correctly, and the space allocation was as expected.

It does add an extra step in the conversion of disk images but at least I know what the heck was going on. I don’t like not knowing why something is the way it is.

What did we learn from all this?

Firstly, the emulator's floppy disk implementation appears to be doing its job correctly. The disk image contained the expected filesystem information, and the free-block chain was coherent and intact.

Secondly, just because two operating systems recognise the same filesystem doesn't necessarily mean they interpret every internal structure identically. This is apparently important when dealing with historical Unix implementations. Presumably, in the early days different vendors introduced their own variations, just to annoy vintage computer enthusiasts 40 years later..

Thirdly, filesystem corruption isn't always what it first appears to be. In this case, the symptoms lead me down the path of a damaged allocation structure, but the information was actually still present and consistent and it was just a misunderstanding of the block list values.

And finally, a relatively small python script can solve a compatibility problem that would otherwise have involved me trying to modify the Linux kernel (bleurgh) or changing the emulator (not necessary as it turned out).

Sometimes I wonder whether retro computing is a hobby or a particularly elaborate form of self-inflicted punishment.

And the floppy image that started all this? It was both full and empty. It just depended on which operating system was observing it…

(Insert Schroedinger joke here...)


 

 

 

Sunday, October 18, 2026

If a Torch beeps in a forest, does it make a sound? - Torch Emulator Part 4

So, the Torch emulator is ticking along nicely. But silently. It's fair to say that the Torch is not exactly endowed with an extensive sound system such as was had by the Amiga or even the C64. As per most 'serious' computers of the time, it basically had a beeper. It was a little more advanced than the IBM PC since it does not have a discrete speaker, rather the beeps are piped through the video output connector.

Content warning again. Because this is about audio, this blog doesn't have many screenshots. It has a youtube video at the end, though.

At startup, the system beeps a la PC but then once the initial memory is tested OK, it makes a rising 'whoop' sound before loading in the main Caretaker software to continue starting up. Except that the emulator doesn't. It's currently completely silent. So what to do. 

Using the SDL library for sound should be relatively straightforward but we still need to know how the 'audio' is actually generated. To the schematics, Batman!

Video Connector Pins


We can see from this where the sound comes out but we need to follow the lines back to the origin of the 'AUDIO' signal. Fortunately, as the system is simple (at least for sound) the AUDIO signal only appears on one other sheet, which shows that the 6303 service processor is the source of the sound. From pin 9. :)

Audio from pin 9 on the 6303


So, the audio is a simple square waveform (as previously seen from the hardware when testing the Torch motherboard) which is generated by the 6303. This then passes through a simple amplifier circuit and out to the speaker pins on the video output socket. Easy.


Error beeps from the real hardware


But now what? I know that the system does its 'whoop' before anything else appears to happen so I think it's safe to assume that the code for that is in the Caretaker ROM. After a bit of analysis it seemed that there was code specifically for the 6303 but also for the 68010. Given that the audio is generated by the 6303 I assumed that the code for the 68010 could be discounted - at least for now. But what the Caretaker ROM did include was a series of what looked like progressively shorted speaker pulses. This would be exactly what you would see to create a 'whoop' noise. 

The original code of the emulator (which if you recall, was not actually done by me) is running the timer needed but it just isn't turning the output into sound. 

The key thing about all this is that the emulator was missing the specific VSYNC signal required by the service processor. This is received by the 6303, marking the start of each video frame. That signal prompts it to run a regular housekeeping routine. The new code adds a simulated 50 times a second signal, along with handling the interrupts to allow the firmware (Caretaker) to receive and acknowledge it.  That housekeeping routine controls the sound duration (and another surprise for later). 

So, when I came to test the code (after eliminating the usual sausage fingers and low-talent related errors), I was quite surprised by the results. The sound works perfectly. The 'beep' and 'whoop' at startup worked great and the entering the command:

    echo -ne '\007'

resulted in the expected 'beep'. The ASCII 007 is actually 'Bell' which is a really ancient hangover from early computer systems.

But the BIG surprise was the palette. The addition of the routines to manage the missing VSYNC signal had an additional affect. The palette selection all now works exactly as it should. Previously, changing the colours on the screen did not actually make any difference. The only way to change the colours was to alter the 'fallback' colours in the source code. The control panel on Torch Unix just did not work. 

It does now. :)










So, here's a video of the reboot and startup of the Torch Triple X, which is exactly as it appears on the real hardware, sound and all.



Next time, I try to get online with the Torch Emulator. It may not go well..

Monday, October 12, 2026

Ticking away the moments that make up a dull day - Torch Emulator Part 3

"Time is the continuous passage of existence from the past through the present and into the future." Apparently. I prefer "Time is an illusion. Lunchtime doubly so."

Content warning. This post is mostly text.

Anyway, the Torch emulator has a bit of an issue. It now picks up the time from the local host (either Linux or - bleurgh - windows) and even uses days with dates that match the current year even though it's not y2k compliant. It just picks a previous year where the days match and for 2026 that year is 1998.


Years are so last year...


All well and good since we can manage without the absolute correct year on files. But what is more of an issue is that the time starts to accelerate as soon as the emulator picks up the host time. So after a few minutes the time has advanced about ten minutes. After an hour, the time is an hour and a half ahead and just getting further and further away from the actual time.


Are you sure it's nearly five to six?



It turns out that the original code basically says 'after 200,000 emulated cycles advance the RTC by one second'. And because the code doesn't advance anything other than the seconds, I have to assume that Torch Unix sees it and just maintains its own system clock from that. 

Looking through the comments in the code there's this one:

/*
 * The deliberately short interval also satisfies Caretaker's RTC
 * liveness tests.
 */

This suggested that it was designed to make the seconds register update often enough that Caretaker believes that the RTC works. Obviously, in the real hardware, the seconds register would update once per err... second.. (In actual fact, this check is only at the start..)

So, the next question, does the clock run fast because the 68010 implementation in musashi is not regulated? The Triple X hardware uses the 6845E CRTC VSYNC signal to drive IRQ6 so maybe Torch Unix is actually using that as the system clock 'tick'. As a bit of a test, I put in a fixed 50hz IRQ6 using one of the SDL functions (SDL_GetPerformanceCounter()) rather than the existing CRTC timing. And would you believe, with that hack running the time is spot on. So that's nice.

Looking over the original code, the function video_crtc_tick() is just toggling the VSYNC state every 4000 calls to that function. And because the emulator main loop runs at the host speed it means that this timing is also host speed dependent. There is a variable called g_crtc_cycles that doesn't really represent CRTC cycles but actually counts function calls... (This code is freakin' complicated.)


To get around that I changed the video code (since it was all CRTC based) to use the SDL library performance counter. This means that the CRTC now has an accumulator that generates one VSYNC event every 20ms. I also had to mod the video code so that it now has a 'pending' VSYNC count. One function adds the 'tick' event every 20ms and then another removes one event at a time. This means that VSYNC events aren't lost because the main loop doesn't see the actual event i.e. it's still in the queue. Without this change the clock was losing about five seconds a minute because of the 'missed' VSYNC pulses.

There is a hack in the code for Caretaker startup that I didn't touch (mentioned earlier). This hack is needed because Caretaker does a timing test during power up and it expects about 600 status reads between changes. If it ain't broke, don't fix it... This hack is only running at startup anyway and doesn't impact the (updated) RTC and VSYNC code.

At this point, the clock looks good and seemed to be keeping good time. Except it was an hour ahead. I hate GMT/BST nonsense. (It causes me issues at work too..) Anyway, I just updated the Linux RTC sync function to use gmtime() rather than localtime(). It looked like Linux was compensating for local time even though local time was already at BST.. Twisting my melon man. 

So far so good. But all of this messing about with the clock code messed up the Torch startup animation. It would get through the first 'bar' and then immediately move onto the next part of the boot phase. There's just about enough time to click 'STOP' if I wanted to but I wasn't happy with the situation. I wanted it to look like a proper Torch booting up and that requires a fully executed animation. So there.

So back to the code again. I had to fiddle with the 'host divisor' (which controls how slow Caretaker runs to give you time to see the animation and click 'STOP' if you want) and realised that if I increased it, it would compensate for the now correct VSYNC rate. I got the full animation back with a bit of experimenting but with a minor annoyance. Because the whole system is running slow during that initial startup, it puts the clock 30 seconds(ish) behind real time. By this point, I am willing to just go with that. I'd had enough of trying to understand the code and where it is now is fine. 

In summary, the RTC timezone is now correct (yay!). The CRTC now generates a 'real' 50Hz VSYNC. IRQ6 uses this to drive the Unix software clock (or rather the Unix software uses IRQ6 as the 'tick' for its clock). The Caretaker startup is intentionally slowed down to allow the boot animation to run (as well as give an opportunity to click 'STOP' for more startup options) but with a slightly modified 'divisor' value. This puts the Torch RTC about 32 seconds behind actual time. 

I can live with that.


I think I can cope with this time difference..


*I'm fully aware the time difference on the last screenshot is actually 28 seconds. It takes four seconds for me to get the screenshot up and clicked after running the 'date' command on the emulator!


 

Tuesday, September 29, 2026

You need a bigger what? Oh, a bigger DISK. - Torch Emulator Part 2

With the relative usefulness of the Torch Emulator (github page now available, more details below), I thought it would be a quick(!) diversion to have a go at compiling a program that would open a window in OpenTop. In the Torch documentation there is a programming manual which includes a simple c program that opens a window and shows the available ASCII character set. Not particularly flashy but it would at least prove how to open a window and link into it with other code. Nothing too earth shattering to be fair.

As there is no sign of this application on any disk I had to type it in myself which was (C)Not Fun. After correcting several typos that resulted in compile errors I reached a point I could not get past. The compiler was throwing an error that the file libw.a was of an unknown type. Hmm. This was a bit annoying.


Do you know the magic number?


It turns out that the 'magic number' is a Unix way of identifying the type of file, and it appears in the first few bytes of a file. Unfortunately, this file was saying it was type '000000' which the compiler/linker really did not like. This specific file, having the .a suffix, is actually an archive file. So I tried the Unix command 'ar -t libw.a' and it told me that the file was not a valid archive. Hmmm. 

Fortunately, I have access to several different disk images, including the original SysV floppy images. This gave me the chance to recover an earlier (or non-corrupted) version of the file. Or at least it would have if not for the support for the specific filesystems on the disks involved being removed from the kernel.

Tangent incoming.

I have a new laptop. It is the first new (personal) laptop I have had in twelve years. So I installed Pop OS on it for decent Linux Nvidia graphics support so I can play No Man's Sky. The latest version of Pop OS comes with v7 of the  kernel. Or 7.0.11 to be exact. The last version to support SysV filesystems was 6.14. Snap to me having to install a rather old kernel and then figure out how to get to the Pop OS boot menu. Fortunately, this wasn't too strenuous and didn't actually take too long given the power of my new laptop. Did I mention I have a new laptop? 😁

With my new (old) kernel installed, I could finally go and mount some disk images. I started with the Torch 'test system' disk image - available on bitsavers - but found that this did not actually contain the file. I assume because it's actually part of the development suite which includes the errr... includes which wouldn't be on a 'test' machine. So then I tried the other available image from bitsavers but this turned out to be exactly the same file I had in my image.

Finally, I turned to the SysV disk images I had. These are not formatted like normal floppy disks and this did cause some confusion to begin with. I had completely forgotten that these are archive disks (a lot of archive stuff in Unix isn't there?) and to access them required the use of the 'cpio' command. This command makes me shudder as I successfully destroyed my Linux Mint install on my old laptop a few months ago (not my new one - did I mention I have a new laptop?) by copying all the Unix files to their exact same locations in the Linux file structure. That was an unpleasant few hours sorting out that mess... So this time I made sure I was in a specific folder and used the command:

cpio -idmv --no-absolute-filenames < ../SysVDisk1.img

The important safety bit here was the '--no-absolute-filenames' which kept everything in the directory where we were located. Phew. And after about the third disk, I found the file I was looking for.

So with a new version of libw.a in hand would the code compile?

No.

I began to think that the version of libw.a on the Torch was actually not going to work at all. I had a look at the first few bytes of the libw.a file and a known good different file called libc.a. Very interesting was that the libc.a file had the following as the first few bytes:

21 3c 61 72 63 68 3e 0a
 !  <  a  r  c  h  > \n

But the newly copied libw.a file had:

0000 5757 726c 6173 2e6f 
W W r l a s . o
By now I was convinced that this file was not going to work. And it never would have. The file itself did contain the correct *.o files but they were referenced in a different way than the Torch was accepting. How this ended up like this I have no idea. But I needed that libw.a so I wrote a small python script to pull out the files to save them individually on a Linux machine (my new laptop - did I mention that?). Then I would copy them over to the Torch and re-insert them into a valid Torch archive file using the Torch terminal. Easy. 

(At this point I should say that this next section took me the best part of three days of spare time and a day of a weekend to sort out.)

Here's the handy python script I knocked up (or a screenshot of some of it anyway):

A professional developer I am not...

This successfully gave me the *.o files I needed which I duly copied over to the Torch. A quick 'ar r libw.a *.o' later and I would have a shiny new libw.a file. 

And did I have a nice shiny version of libw.a, ready to insert into my /lib directory?

No.

I got this instead:

No space? NO SPACE??


And so, finally, I get to the point of this blog entry. How do I make more room on the Torch emulators disks? 

The current layout of the disk is:

Partition 0    -    202Kb (that's KILOBYTES as in 1024 bytes multiplied by 202 - I'll die on this hill)
Partition 1    -    10Mb (/ i.e. boot partition)
Partition 2    -    5Mb (not really sure what this one is...)
Partition 3    -    26Mb (/usr)


Original disk image partitions

At the time (c.1985) this probably seemed to be an enormous amount of storage. But in 2026, this is far too small, especially since this disk image appears to not be a 'clean' install and with me adding loads of stuff to it. There is just no more room left. So, the question is, can I make the partitions bigger, say at least twice as big?

Possibly.

First things first. The emulator supports a single hard disk image. I need to update the emulator to support a second hard disk. This wasn't as hard as it might sound since it just required the addition of another copy of the existing hard disk code but tweaked to change the SCSI ID, along with a new command line option to mount it at startup.

Initially I went for SCSI ID 4 since this is way above the values of the original hardware. SCSI ID 0 LUN 0 is for the main hard disk with LUN 1 as the 'BlueSCSI' floppy drive and the Gotek is SCSI ID 1 LUN 1 so SCSI ID 4 seemed fair for a second hard disk image to keep it well away from any of the existing addresses.

Except, no. 

Caretaker actually seemed OK with SCSI ID 4. Unix, not so much. Because it's not a value expected by the Torch version of Unix I actually needed to create new nodes for SCSI ID 4. And this was a complete disaster. Whatever I did, any attempt to access the hard disk partitions through Unix resulted in a hard crash. After a few hours I gave up. So I went back and tried SCSI ID 1 with LUN 0 for the second image. 

And this just worked. More or less. I could run the emulator and enter the Caretaker disk manager program which appeared to give me to access SCSI ID 1. It also gave me a set of default values for the partitions on SCSI ID 1, except there was a problem. The 'size(K) left' value was -13000Kb. Ah. Well that's not right.  


Minus 13Mb? That's probably not correct...



After some head scratching I realised that if I turned off the 'partition alignment flag' for the partitions that had it set, then the disk space was shown correctly. So I left it switched off on those partitions.

Next problem. If I changed the size of the partitions that were not the last one then this would require the moving of some data (even though the image is technically empty). But this result in a crash 100% of the time when the partition information was written back to the drive. 


Crash! Ah-aaaa!


To get around this I had to set the partition sizes to zero. Then, as I added the new partitions with the increased sizes this meant that no data was 'moved' and the new partitions were just saved without issue. And these were the new sizes:


That's a bit more roomy...

So now the root partition has 20Mb and /usr now has 53Mb. But it's not done yet. This was just creating the partitions on a disk image attached to SCSI ID 1 LUN 0. I still have to create the filesystems and copy across the relevant system files.

I'm jumping ahead a little bit here, but for partition 0, this just requires the re-installation of Caretaker. This is simply an option in the main menu of the disk utility that automatically loads the required boot files onto partition 0. To make sure that there were no crashes, I reinstalled caretaker then immediately rebooted. This seemed to work and there were no error. Back to the regular bits of the disk...

In the Torch I needed to identify the correct device nodes in the /dev directory. Simply put, these nodes are the interface points for the hard disk. Fortunately, this was fairly straighforward. In /dev there is another directory called 'dsk'. Inside this are a series of existing nodes but the ones we're interested in are 1s0, 1s1, 1s2 and 1s3. The numbers correspond to the SCSI ID and the partition number. 

Interestingly, the 1s0 etc refer to SCSI ID 1 LUN 0 but the LUN is not included in the node, almost as if it is just implied. But it IS included for SCSI ID 1 LUN 1 as 11s0. Old computers are weird. Anyway, the final digit is definitely the partition number.

To make a filesystem, I executed the 'mkfs' command on each partition (substituting in the correct node and block amount):


    mkfs /dev/dsk/1s1 xxxxx (where xxxxx is the number of blocks required)


The upshot of all this is that the disk image should now be ready to receive files. To check I'd got this correct, I created directory part1, part2, part3 in the working Torch disk image. Then I mounted each part of the drive with:


    mount /dev/dsk/1s3 /mnt/part3 


And there were no complaints. This was a good sign as it suggested the filesystem had been mounted correctly. Even better was that I could write files to part1 and part3 and read them back, even after a reboot. I'm fairly sure that part2 is actually some sort of swap space (feel free to correct me in the comments).

With these all now in place, it was time to do what I'd been dreading. I needed to copy all the files over from each of the drives, starting with / and /usr. I had thought that this might be easier on Linux, but having mounted both image files, and after trying to copy the files across, it became very obvious that the modern Linux world does not enjoy trying to read and write ancient disk structures. So I went back to the Torch. 

And I should've done that first. It took less than ten minutes to copy the files across for both / and /usr and there were not complaints, no stumbles, no errors. So, the big question. Would the new image actually boot?


VICTORY!


And proof of the new sizes:


It's SO BIG!

That's 20Mb for / and 53Mb for /usr (I assume that partition 1 which is 8Mb is actually some sort of swap space).

And now I have the room, does my typed in program actually work now?

No.

I did get a new libw.a file that I had but despite it being so carefully crafted, it was missing its index. Whatever that means. It meant that the compile failed. Again. After all that work I was pretty despondent but then I did another 'ls' on the lib directory. Staring me in the face was a file called 'libW.a'. I began to wonder if this was actually what I needed so I went for broke and renamed it to 'libw.a' - Linux is case sensitive for filenames - and tried compiling again. 

Did it work?

Oh, yes. I had rather mixed emotions at this point. On the one hand I'd spent three days trying to get files out of an archive that the Torch apparently doesn't support (even though it's on the freakin' SysV disk) when I could have just renamed an existing file, but on the other hand, I've now got a big one. Disk image that is.


Ooo, he's a character.

Next time, time (you'll see).

If you'd like to follow - or even contribute to - this thing, see the github page at:





Disk images are available from bitsavers:



Caretaker may or may not be included somewhere or not in this thread (VCF forum membership required):