Skip to content

[kernel] Dyamically adjust ATA driver for XT-IDE port 0x300 on PC/XTs#2351

Merged
ghaerr merged 2 commits intomasterfrom
atacf2
Jul 7, 2025
Merged

[kernel] Dyamically adjust ATA driver for XT-IDE port 0x300 on PC/XTs#2351
ghaerr merged 2 commits intomasterfrom
atacf2

Conversation

@ghaerr
Copy link
Copy Markdown
Owner

@ghaerr ghaerr commented Jul 7, 2025

For ease of testing on QEMU, as well as requested by @toncho11, the /dev/cf0 driver now automatically adjusts its port address from the standard ATA port 0x1F0 to the XT-IDE port 0x300 when the kernel is running on an 8086/80186, which almost always means a PC/XT.

This only occurs for CONFIG_ARCH_IBMPC. The port number is displayed so there is no confusion.
Screenshot 2025-07-07 at 5 14 06 PM

For now, the results of the sanity checks are displayed but ignored, allowing for easy testing on XT-IDE.

@ghaerr ghaerr mentioned this pull request Jul 7, 2025
@ghaerr
Copy link
Copy Markdown
Owner Author

ghaerr commented Jul 7, 2025

@fhendrikx: this should work without modification on Solo/86, since the ATA base port remains constant for Solo/86.

@ghaerr ghaerr merged commit 0606716 into master Jul 7, 2025
1 check passed
@ghaerr ghaerr deleted the atacf2 branch July 7, 2025 23:39
@ghaerr
Copy link
Copy Markdown
Owner Author

ghaerr commented Jul 7, 2025

Next step is to get ATA CF driver working when XMS is configured; this will allow the driver to be included in the standard desktop build, for which XMS is now always compiled in.

@fhendrikx
Copy link
Copy Markdown
Contributor

@fhendrikx: this should work without modification on Solo/86, since the ATA base port remains constant for Solo/86.

Great, thank you.

@toncho11
Copy link
Copy Markdown
Contributor

toncho11 commented Jul 9, 2025

And is there a tool on ELKS to test IO performance?

@fhendrikx
Copy link
Copy Markdown
Contributor

And is there a tool on ELKS to test IO performance?

@ghaerr might know, I used simple timing:

date; mkfat -fat32 /dev/hda 3903480; date

then subtract times to work out the elapsed time.

@toncho11
Copy link
Copy Markdown
Contributor

People use dd and hdparm on Linux. I suppose we can also write one. Here is an example from chatgpt:

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>

#define FILE_NAME "testfile.dat"
#define FILE_SIZE_KB 512   // 512 KB test
#define BLOCK_SIZE 256     // 256-byte block size (small for ELKS)
#define TOTAL_BLOCKS (FILE_SIZE_KB * 1024 / BLOCK_SIZE)

void write_test() {
    char buffer[BLOCK_SIZE];
    int i, fd;
    time_t start, end;

    // Fill buffer with pattern
    for (i = 0; i < BLOCK_SIZE; i++) {
        buffer[i] = (char)(i & 0xFF);
    }

    fd = open(FILE_NAME, O_WRONLY | O_CREAT | O_TRUNC, 0666);
    if (fd < 0) {
        perror("open (write)");
        return;
    }

    time(&start);
    for (i = 0; i < TOTAL_BLOCKS; i++) {
        if (write(fd, buffer, BLOCK_SIZE) != BLOCK_SIZE) {
            perror("write");
            break;
        }
    }
    time(&end);

    close(fd);

    printf("Write: %lu seconds for %d KB\n", (unsigned long)(end - start), FILE_SIZE_KB);
}

void read_test() {
    char buffer[BLOCK_SIZE];
    int fd, n;
    time_t start, end;
    int total_read = 0;

    fd = open(FILE_NAME, O_RDONLY);
    if (fd < 0) {
        perror("open (read)");
        return;
    }

    time(&start);
    while ((n = read(fd, buffer, BLOCK_SIZE)) > 0) {
        total_read += n;
    }
    time(&end);

    close(fd);

    printf("Read: %lu seconds for %d KB\n", (unsigned long)(end - start), FILE_SIZE_KB);
}

int main() {
    printf("ELKS HDD Performance Test\n");
    printf("Testing %d KB with %d-byte blocks\n\n", FILE_SIZE_KB, BLOCK_SIZE);

    write_test();
    read_test();

    unlink(FILE_NAME);

    return 0;
}

@ghaerr
Copy link
Copy Markdown
Owner Author

ghaerr commented Jul 10, 2025

is there a tool on ELKS to test IO performance?

date; mkfat -fat32 /dev/hda 3903480; date

Even easier is just using "time":

$ time mkfat -fat32 /dev/hda 3903480

We do have the fdtest program that could be enhanced for specific I/O testing, but in general, all we were looking for is to see whether there was much performance degradation when using 8-bit rather than 16-bit access. As you've seen, strangely enough, 8-bits actually tested faster - 7 secs vs 8! So no degradation.

People use dd and hdparm on Linux.

ELKS has dd, that can be used with time above as well quite easily.

@toncho11
Copy link
Copy Markdown
Contributor

mkfat is testing only writing.
Simply copying the bin folder will test both.
I am more interested in comparing the BIOS vs direct driver. I wonder if the improvement will be better than expected.

@toncho11
Copy link
Copy Markdown
Contributor

toncho11 commented Jul 13, 2025

I removed my network card and tried to mount my current CF card with partitions. I was hoping I could mount it as raw ignoring the partitions, but it did not work. Also there are absolutely no debug messages.

These are early tests before my new CF card arrives.

IMG_20250713_123843.jpg

@ghaerr @fhendrikx

There is absolutely no indication if the ATA and CF card are actually accessible.

@toncho11
Copy link
Copy Markdown
Contributor

toncho11 commented Jul 13, 2025

I had a look at the code and I have doubts that the ATA_BASE_PORT is never set to 0x300. It is a bit complicated the whole procedure. The ata_reset() must change it](

if (arch_cpu < 6)
), but I am not sure it is executed on time. I think a #define sets the address early before it is changed and then the change is never applied.

Here are the details:

I think that ATA_BASE_PORT is set at compile time and then only the variable ata_base_port is updated, but not the define

#define ATA_BASE_PORT ata_base_port

that is used in ata.h to set the real ATA addresses:
#define ATA_PORT_DATA (ATA_BASE_PORT + 0x0)

@ghaerr
Copy link
Copy Markdown
Owner Author

ghaerr commented Jul 13, 2025

Hi @toncho11,

Thanks for your prelim testing and screenshot. Which actual CI build are you testing, my latest PR #2352 or something else?

I had a look at the code and I have doubts that the ATA_BASE_PORT is never set to 0x300. I

I checked and I think it's ok. However, I've added debug code to PR #2352 so that if the ATA identify fails, it will show the port number used. This way, at least for now, you'll always get some indication of the ATA CF driver trying to do something. Can you please try this updated version?

I suspect the updated version will still fail, but will at least show the internal kernel error code, to give us more ideas as to what needs to be done. Thanks.

@fhendrikx: I finally found the reason the previous code was working in QEMU for two ATA drives, even when only one was connected. Unbelievably, it turns out that the heap_alloc call for the ATA identify buffer returns the very same address of a just-freed buffer of the same size that the BIOS IDE ideprobe drive used to "probe" the IDE drive getting the very same data!!! So the returned buffer's contents were exactly ATA-compatible. How's that for coincidence!!! I added a HEAP_TAG_CLEAR to heap_alloc to clear memory before returning and that stopped it from happening. It doesn't happen anymore anyways since the function error returns were cleaned up, but I thought you'd like to know about it!

@toncho11
Copy link
Copy Markdown
Contributor

toncho11 commented Jul 13, 2025

I used your latest PR that is not merged. Yes #2352.
Can you please explain why do you think it is OK?
Are you sure ATA_BASE_PORT is updated after ata_reset(). Is ata_reset() executed early enough to update this value? Currently I am convinced that it is not OK.

I did not see any port used as you can see form my screenshot.
Please check what I explain above.

@ghaerr
Copy link
Copy Markdown
Owner Author

ghaerr commented Jul 13, 2025

Can you please explain why do you think it is OK?
Are you sure ATA_BASE_PORT is updated after ata_reset(). Is ata_reset() executed early enough to update this value?

Sure. I checked first to see that arch_cpu is set, which it is in setup_arch() very early in kernel init.

Then, after the ATA-CF driver registers the block device, it calls ata_reset(). The first thing that routine does is set ata_base_port to 0x300. Since ATA_BASE_PORT is defined to be ata_base_port and no I/O is done in the driver before this, I think it is set OK.

I did not see any port used as you can see form my screenshot.

Agreed. That's because if the ata_identify function fails, which it could do for the following reasons, in the PR nothing will be displayed at all. However, I just pushed a change that will display the port number being used and the reason (error number) for failure in ata_identify, that you should see when you test again.

The reasons for ATA identify failure are (in order):

  • Wait for ATA device not busy times out (error -ENXIO)
  • ATA identify command fails (-EIO for on DFE or ERR bit, -EINVAL on invalid drive number)
  • Wait for ATA device to become not busy fails (-ENXIO)

Hopefully we will get enough information to fix the problem from your next test. Sorry for the trouble.

@toncho11
Copy link
Copy Markdown
Contributor

OK but his code will be executed before the reset I think:

static unsigned int ata_base_port = 0x1F0;

#define ATA_BASE_PORT   ata_base_port
#endif

Once we defined ATA_BASE_PORT as 0x1F0, there is no going back. I think a static and #define are executed before any functions????

@ghaerr
Copy link
Copy Markdown
Owner Author

ghaerr commented Jul 13, 2025

I think a static and #define are executed before any functions????

A define is not executed, but serves as a macro definition at compile time: ATA_BASE_PORT is seen by the code generator as ata_base_port. The static definition of ata_base_port sets the variable to 0x1F0 before the kernel starts.

Later, as described above, the variable's value is changed to 0x300. I have tested this and it works.

Once we defined ATA_BASE_PORT as 0x1F0, there is no going back

No, you're probably confusing static and const. That's not the way it works.

You'll need to test again and see the informational message to learn what is happening on your system. The port will be displayed as 300, but I'm guessing that the XT-IDE interface may be different than ATA-CF. We will see.

@toncho11
Copy link
Copy Markdown
Contributor

I see ATA_BASE_PORT and ata_base_port are the same thing, the same memory location.

@ghaerr
Copy link
Copy Markdown
Owner Author

ghaerr commented Jul 13, 2025

I see ATA_BASE_PORT and ata_base_port are the same thing, the same memory location.

Yes - you should now see that's used as a nice trick in include/arch/ata.h, where the value is constant for Solo/86 but a variable, switching between 0x1F0 and 0x300 for IBM PC in ata.c.

@fhendrikx
Copy link
Copy Markdown
Contributor

@fhendrikx: I finally found the reason the previous code was working in QEMU for two ATA drives, even when only one was connected. Unbelievably, it turns out that the heap_alloc call for the ATA identify buffer returns the very same address of a just-freed buffer of the same size that the BIOS IDE ideprobe drive used to "probe" the IDE drive getting the very same data!!! So the returned buffer's contents were exactly ATA-compatible. How's that for coincidence!!! I added a HEAP_TAG_CLEAR to heap_alloc to clear memory before returning and that stopped it from happening. It doesn't happen anymore anyways since the function error returns were cleaned up, but I thought you'd like to know about it!

That's pretty amazing. Nice change :)

@ghaerr
Copy link
Copy Markdown
Owner Author

ghaerr commented Jul 13, 2025

@fhendrikx,

While looking hard at the ATA driver, I'm noticing basically no difference between the ata_wait() and ata_wait_busy() routines, except that the latter calls ata_wait up to ATA_RETRY times (I aggregated all the separately-coded ATA_RETRY code into the new routine ata_wait_busy without changing any code).

I'm basically thinking that the driver doesn't really need two separate wait routines - one with a 3x wait while the other 1x - does it really matter? That is, in all the normal cases, always waiting for 3x (i.e. 1500 loops of checking ATA_PORT_STATUS) will always work, at the possible penalty that an read/write/init error takes a few milliseconds longer to return, if/when the drive never signals ready.

What do you think if I combine the two routines? I'm trying to develop better code for @toncho11's testing, which will surely show some element of our driver that isn't working with XTIDE for reasons yet unknown. Having a single -ETIMEOUT or -ENXIO return for a non-busy state never achieved would then allow for simpler debugging of the driver, I think.

In the same vein, each driver routine waits for non-busy both at the start and end of ata_read and ata_identify. Why do that? The next call to any ata function will wait for busy. Agreed we want to wait for busy prior to returning from ata_write, to ensure it is fully synchronous.

I'm wondering if there's a special reason this has been done?

@toncho11
Copy link
Copy Markdown
Contributor

And in theory I should be able to mount a CF card with several partitions even I will not get the proper data?

@ghaerr
Copy link
Copy Markdown
Owner Author

ghaerr commented Jul 13, 2025

each driver routine waits for non-busy both at the start and end of ata_read and ata_identify. Why do that?s

Ok never mind - in read the OSDev Wiki it talks about the problem of requiring a device select command before each ATA command, and that one should not send a device select if the controller hasn't in fact updated BUSY or ERR. So now I understand why that's there.

However - the Wiki states that you one has to query device status busy after sending the device select command. Our driver doesn't do that - it asks for status immediately, which will return the status of the previously-selected drive. This could cause problems on rapid switching between drives 0 and 1. So this should probably be changed. I'm thinking that should be done in a separate PR.

[EDIT: Sorry, @fhendrikx, I'm confusing myself here. After yet more reading, it seems that the busy-wait prior to sending device select will in fact work and not cause problems when quickly switching between drives. We should probably regression test this on real hardware though copying back and forth between drives just to sure; you may have already done that.]

@ghaerr
Copy link
Copy Markdown
Owner Author

ghaerr commented Jul 13, 2025

I should be able to mount a CF card with several partitions even I will not get the proper data?

Not quite: the mount command will fail in that case. However, you can test by running hd /dev/cf0 | more which will hex dump the CF 0 card and paginate it so that a screenshot can be taken. The MBR bytes should then be visible if the CF driver is working. If it is not working, you'll get the same message you've been getting - "No such device".

@ghaerr
Copy link
Copy Markdown
Owner Author

ghaerr commented Jul 14, 2025

@toncho11:

After reading more about XTIDE I found that there are actually two I/O port addresses that XTIDE uses differently than IDE.

These two port start addresses are known as the base ports (sometimes called I/O ports) and the control port. On standard IDE systems, these addresses are 0x1F0 and 0x3F0. For XTIDE these are 0x300 and 0x308 (see below from XTIDE documentation):
XTIDE

There is only a single "control" port address ever used, which is actually set to the control port address + 6. In the previous code, the control port was always set to 0x3F6 (=0x3F0+6). I have modified the ATA CF driver to automatically switch to using the XTIDE default control port of 0x30E (=0x308+6) when running on an 8088 or 8086. The Solo/86 configuration has not changed. I am hoping this is the reason that your PC/XT didn't not work with the ATA driver. You might want to check with your XT/CF card to ensure that these two port address (0x300 and 0x308) are set by default, if in fact they are settable.

The latest commits in #2352 also combine the separate ATA wait code in an attempt at reducing the driver size. This shouldn't be a big deal, but it's only been tested on QEMU, not Solo/86.

I've also added code to display the base and control port addresses during boot time as well as on any error, along with the error number so we can try to more easily get this working.

Here's the QEMU boot screen with only one ATA drive attached:
Screenshot 2025-07-13 at 7 42 56 PM

Thank you!

@fhendrikx
Copy link
Copy Markdown
Contributor

@fhendrikx,

While looking hard at the ATA driver, I'm noticing basically no difference between the ata_wait() and ata_wait_busy() routines, except that the latter calls ata_wait up to ATA_RETRY times (I aggregated all the separately-coded ATA_RETRY code into the new routine ata_wait_busy without changing any code).

I'm basically thinking that the driver doesn't really need two separate wait routines - one with a 3x wait while the other 1x - does it really matter? That is, in all the normal cases, always waiting for 3x (i.e. 1500 loops of checking ATA_PORT_STATUS) will always work, at the possible penalty that an read/write/init error takes a few milliseconds longer to return, if/when the drive never signals ready.

Yes, it's fair enough.... some of that code grew organically, and once it worked, didn't want to go mucking with it too much.

What do you think if I combine the two routines? I'm trying to develop better code for @toncho11's testing, which will surely show some element of our driver that isn't working with XTIDE for reasons yet unknown. Having a single -ETIMEOUT or -ENXIO return for a non-busy state never achieved would then allow for simpler debugging of the driver, I think.

Yep, agreed.

In the same vein, each driver routine waits for non-busy both at the start and end of ata_read and ata_identify. Why do that? The next call to any ata function will wait for busy. Agreed we want to wait for busy prior to returning from ata_write, to ensure it is fully synchronous.

I do that so that we don't get calls running into each other.

I'm wondering if there's a special reason this has been done?

Without it, I seemed to be getting errors.

@fhendrikx
Copy link
Copy Markdown
Contributor

And in theory I should be able to mount a CF card with several partitions even I will not get the proper data?

No, the ATA CF driver doesn't support any partitions, so it won't work.

@toncho11
Copy link
Copy Markdown
Contributor

Can not make a screenshot with github.
It made a pause of 1 min and it said -6 for the cf0.
I have some doubt for the io address?

@toncho11
Copy link
Copy Markdown
Contributor

The IO port address is indeed 0x300 on the card.
And the kernel says after 1 minutes: "cf0: ATA port 300/30e, probe failed (-6)".
I did not try mounting it.

@ghaerr
Copy link
Copy Markdown
Owner Author

ghaerr commented Jul 14, 2025

It made a pause of 1 min and it said -6 for the cf0.

Ok, that helps. I assume the error is something like

cf0: ATA port 0x300/0x30e, probe failed (-6)

Is that correct?

-6 is -ENXIO. If the port numbers are as shown above, that means definitely that the status register being read at 0x300 never became not busy. This likely means that the card is not set to address 0x300 or there's another card at that address.

The reason it's taking 1 minute is because the code currently tries reading the port 5000 times. I'll lower that.

So the ATA driver is properly changing the port to 0x300. It is also possible that the "control" port on your card is not set to 0x308. What kind of card do you have?

@toncho11
Copy link
Copy Markdown
Contributor

The CF card is detected as hda and hda1.
hda: 244M CHS 979,16,32

@ghaerr
Copy link
Copy Markdown
Owner Author

ghaerr commented Jul 14, 2025

What brand of CF card do you have exactly? We need to read up on how its base and control ports can be switched. It will be quite hard for me to get this working without some documentation, I have no XTIDE experience.

@toncho11
Copy link
Copy Markdown
Contributor

I am using XT IDE universal bios @ CE00h
v2.0.0 (2013-04-03)

Interesting it says at boot before elks: Master at 300h: Sandisk 256
which is my CF Sandisk card.

@ghaerr
Copy link
Copy Markdown
Owner Author

ghaerr commented Jul 14, 2025

Well, that confirms the base port of 300 but says nothing about control port at 308.

There is hardware for XTIDE, right? Not just the BIOS ROM, I have source code for that. Isn't there a card that plugs into your XT?

@toncho11
Copy link
Copy Markdown
Contributor

Maybe code here will help:

https://github.com/JayesonLS/xtideuniversalbios/tree/master/XTIDE_Universal_BIOS/Src/Initialization

Some data from chatgpt:

it was expressly written to expose the bare ATA interface so that you can roll your own in either assembly or C:

Direct I/O access
The BIOS separates the IDE hardware from the BIOS code, placing the ATA registers at whatever I/O port you configure (default 0x300). All the low‑level ATA register read/write macros are collected in its “AssemblyLibrary” (AssemblyLibrary.asm), which you can simply port into your own code. From C you’d just use inb()/outb() (or inline assembly) against the same port addresses the BIOS uses.

BIOS‑level extensions (EBIOS)
XTIDE v2.0.0 implements the EBIOS extensions via INT 15h (not just legacy INT 13h). For example, AH=48h returns the EBIOS version and feature bitmap, AH=49h supports extended parameter blocks, and so on. You can invoke these services directly from C or assembly to get LBA‑capable disk I/O without re‑implementing the whole ATA protocol.

@toncho11
Copy link
Copy Markdown
Contributor

This is my card: https://www.ebay.fr/itm/395221610374

An ISA 8 bit card that has an integrated CF card reader.

@toncho11
Copy link
Copy Markdown
Contributor

There 2 switch blocks.
The first sets the port IO address.
The second configures the ROM BIOS address.

https://i.ebayimg.com/images/g/CU8AAOSwqa5jZCgg/s-l1600.webp

@ghaerr
Copy link
Copy Markdown
Owner Author

ghaerr commented Jul 14, 2025

Thanks. I'll have to study the XTIDE source code and/or find more information the XT CF Lite 4.1 card in order to understand why it's not working. Due to my rewrite and your testing, it is very clear that the driver is polling the status register at 0x300+7 and that address is signaling BUSY. However, the control port at 0x308+6 is set prior to that, and there is no code for testing the results of that operation.

I can add some more debug code to see exactly where we're failing but it would seem we're still missing part of the puzzle.

I'll probably commit #2352 to keep what we now have, then continue debugging after we've learned more.

Do you know where there might be other drivers or operating systems that support XTIDE other than DOS?

@ghaerr
Copy link
Copy Markdown
Owner Author

ghaerr commented Jul 14, 2025

@toncho11,

I think I've figured it out: according to this LoTech XT-CF-lite Technical Manual, the address mappings do start at 0x300 but the register array is NOT in the standard IDE order:
address

And interestingly enough, these offsets are exactly the same as those on @fhendrikx's Solo/86, but instead of starting at offset 0x40 in the Solo/86 case, they start at offset 0x300 in the XTIDE case for the card you have!

This means that we not only have to switch between starting offsets for the register array(s), but also have to change the offset itself for each of the register values. Let me think about this a bit and I'll post something that does that for your testing.

Ultimately, this probably means we need to support:

  • compile time option for Solo/86
  • dynamic option for standard ATA IDE (for QEMU or non-XTIDE systems)
  • dynamic option for XTIDE w/LoTech XT-CF-lite card (for @toncho11)
  • possible dynamic option for XTIDE using "standard" XTIDE register offsets (perhaps leave this out for now until we find a card that actually uses registers in that way)

Note that in the LoTech XT-CF-lite card, the "control" port follows the "base" port address plus a fixed offset of 0x1C, so a separate switch setting is not required. But it is nonetheless different than the "standard" XTIDE port 0x308+6.

Thoughts?

@toncho11
Copy link
Copy Markdown
Contributor

Sounds great! I was kind of disappointed before. Hardware testing takes a lot of effort each time.
The ATA registers are provided by the XT IDE indeed. It is not a direct mapping to the CF card ATA.

I have another XT IDE card from Monotech: https://monotech.fwscart.com/category/cards
It is a card that provides IDE connector and no CF card slot. On this card I have attached an IDE to SD card reader.
So it is more like a direct HD IDE driver through the XT-IDE in this case?
I mention this for possible testing.

@ghaerr
Copy link
Copy Markdown
Owner Author

ghaerr commented Jul 14, 2025

I was kind of disappointed before. Hardware testing takes a lot of effort each time.

Yes, I get disappointed too when something doesn't work at all. It's hard for me with no way to test! I think we're over the hump on this though, I just found out even more information:

In the XTIDE source code it mentions that the register offsets for XTIDE are the same as ATA IDE, except the offsets are shifted left by 1 (except for the control port). This corresponds exactly with the difference seen with the current ATA IDE driver, and the Solo/86 and now your XT-CF card, which will need to use a single variation for the ATA CF driver in XTIDE mode, with the exception of also having a different start I/O address for Solo/86 versus PC/XTIDE. In other words, a /bootopts port address and (probably) an option for whether to use XTIDE vs IDE register offsets.

Since we don't know which register offsets (IDE vs XTIDE) your Monotech card uses, I suggest we don't start debugging that card until we get the XT-CF-lite card working.

So it is more like a direct HD IDE driver through the XT-IDE in this case?

Hard to say - I suggest we add a /bootopts option so that it can work either way.

I'll put something together for another round of testing. Sorry for the trouble, I'll work hard to ensure it works this time, now that we're coming to actually understand the hardware!

@toncho11
Copy link
Copy Markdown
Contributor

Maybe a C program that tests different base IO ports and offsets will be good? A program that tests several strategies for the possible ATA addresses. Otherwise is too costly to test on real hardware. I am writing a 360 kb floppy disk each time, etc.

Thoughts?

@fhendrikx
Copy link
Copy Markdown
Contributor

I was kind of disappointed before. Hardware testing takes a lot of effort each time.

Yes, I get disappointed too when something doesn't work at all. It's hard for me with no way to test! I think we're over the hump on this though, I just found out even more information:

Testing is always somewhat painful... I did a ton of rounds on the Solo/86 hardware to get the ATA stuff working.

In other words, a /bootopts port address and (probably) an option for whether to use XTIDE vs IDE register offsets.

This sounds like a reasonable work-around... happy to use that for Solo/86 if you want to generalise it out.

@fhendrikx
Copy link
Copy Markdown
Contributor

Maybe a C program that tests different base IO ports and offsets will be good? A program that tests several strategies for the possible ATA addresses. Otherwise is too costly to test on real hardware. I am writing a 360 kb floppy disk each time, etc.

Thoughts?

Sounds like a nice idea... but I suspect we won't need it now that we've worked out your ports are shifted (like on Solo/86).

Let's see what @ghaerr comes back with... I will test that on Solo/86 too, to confirm it works in general.

@toncho11
Copy link
Copy Markdown
Contributor

Not sure if the ports are finally clear.

@ghaerr
Copy link
Copy Markdown
Owner Author

ghaerr commented Jul 14, 2025

Maybe a C program that tests different base IO ports and offsets will be good? A program that tests several strategies for the possible ATA addresses. Otherwise is too costly to test on real hardware. I am writing a 360 kb floppy disk each time, etc.

Well, I'm not sure how writing a C program would help, since I'd still have to put that program on an image and you'd still have to create a floppy... and then when all that is done, I still have to update the driver and you/we're still going to have to finally test on real hardware to know whether the XT-CF driver you're requesting actually works. I've got a pretty good idea about the ports now and am really getting into it my reading the XTIDE source.

I get it though, its a pain to write a physical floppy to test XT CF card, which you do have working using the BIOS hd driver on Amstradt, right?

So I have a new idea to save time: how about just writing an image to the CF card and booting directly from that? Have you tried it? I have two ideas for how that could work, both involve writing the CF card with a bootable ELKS image. From what I've been reading, there may be an issue depending on whether the CF card requires an MBR boot record on it or not.

If you can experiment to see whether you can get your Amstrad to boot from CF to any OS, I think we can make this work. The first idea would be to put the hd32mbr-minix.img on the CF (partitioned), the other would be the hd32-minix.img (not partitioned). The XTIDE boot code would load the boot sector and start the boot process, and the ELKS BIOSHD driver would then use its normal INT 13 to access the HD through the XTIDE ROM, if that can be made to work. What do you think about trying that?

If you can get a CF card to boot any ELKS, even perhaps a 360k floppy image dd onto it vs an HD image, the next step would be to supply you with a special ELKS kernel built to have the root filesystem be /dev/cf0, very much like the Solo/86 system does. In that way, the system comes up and actually runs on the ATA CF driver as the root filesystem. I can get more into details depending on what you're able to do by setting your Amstrad to boot from CF.

@ghaerr
Copy link
Copy Markdown
Owner Author

ghaerr commented Jul 14, 2025

Ultimately, this probably means we need to support:

After reading a GitHub version of the XTIDE sources, it has become apparent there are three different ways we'll need to program the ATA base and control registers in the ATA driver, if we want to use this driver for maximum compatibility across systems being discussed in this thread:

  • ATA (standard, non-XT IDE controller)
  • XTIDE (IDE controller on XT)
  • XTCF (CF controller on XT)
  • Solo/86 (same as XTCF but different BASE controller address)

The following table summarizes the somewhat complicated access methods and standard port numbers:

/*          ATA             XTIDE           XTCF                SOLO/86 */
BASE        0x1F0           0x300           0x300               0x40
BASE+reg    0x1F0+reg       0x300+reg       0x300+(reg<<1)      0x40+(reg<<1)

CTRL        BASE+0x200+reg  BASE+0x08+reg   BASE+0x10+(reg<<1)  BASE+0x10+(reg<<1)
CTRL+6      0x3F6           0x30E           0x31C               0x5C
DEVCTRL     BASE+0x206      BASE+0x0E       BASE+0x1C           BASE+0x1C

Each of ATA, XTIDE and XTCF uses different base ports (0x1F0, 0x300, 0x300 respectively), with Solo/86 using base port 0x40.

The separate "device control register", of which there's only one, can be calculated from the base port if we know whether we're running in ATA, XTIDE or XTCF mode (Solo/86 runs in XTCF mode).

I'm going to update the driver to support all four platforms. To keep things simple, the base ports will remained fixed for the time being, and the "mode" will be automatically set based on whether the system is PC/XT or not:

  • Set XTCF when running on PC/XT.
  • Set Solo/86 when configured for CONFIG_ARCH_SOLO86.
  • Ignore XTIDE more for now.
  • Otherwise, use ATA mode (QEMU).

After this is known working, and we additionally get a system bootable from a XTCF controller, we can worry about "XTIDE" card support.

@fhendrikx, is there a way you could test the current commits of PR #2352, before I add the above changes? It should work by pulling the PR and configuring for CONFIG_ARCH_SOLO86. If it doesn't work, I should really try to fix that before introducing these newer changes, so we don't get stuck with Solo/86 not working.

@ghaerr
Copy link
Copy Markdown
Owner Author

ghaerr commented Jul 14, 2025

@fhendrikx, is there a way you could test the current commits of PR #2352,

Or perhaps I'll just commit that PR now, so that it's easy to test. I can then start working on the updated support discussed above. I would like to get that work done before committing your #2353, since I'm right in the middle of it all, and the inb/outb functions are going to have to be wrapped in the updated version.

@fhendrikx
Copy link
Copy Markdown
Contributor

@fhendrikx, is there a way you could test the current commits of PR #2352,

Or perhaps I'll just commit that PR now, so that it's easy to test. I can then start working on the updated support discussed above. I would like to get that work done before committing your #2353, since I'm right in the middle of it all, and the inb/outb functions are going to have to be wrapped in the updated version.

might be simpler to merge and I'll just pull the latest, do a build and deploy and check everything is happy :)

@toncho11
Copy link
Copy Markdown
Contributor

toncho11 commented Jul 15, 2025

It might be possible to test without a real hardware using 86Box with emulated XT IDE.
I posted a question here: 86Box/86Box#5797
86Box runs on Windows, Linux and Apple.

I have already used it with an attached HD image of ELKS. Which means that it provides a standard IDE interface through XT BIOS. But we do not know if they emulate enough for a direct HD driver.

@toncho11
Copy link
Copy Markdown
Contributor

toncho11 commented Jul 15, 2025

So in 86Box version 5 beta development (not 4.2) you can choose between several XT IDE emulations and I checked in the [ISA] XT IDE version you can set the base IO port address.

@ghaerr
Copy link
Copy Markdown
Owner Author

ghaerr commented Jul 15, 2025

I'll check on 86Box, thanks. What do you think of the idea of trying to boot your Amstrad from CF? Will it do it? That would save lots of time, no?

@ghaerr
Copy link
Copy Markdown
Owner Author

ghaerr commented Jul 15, 2025

Also, regarding 86Box, you're aware that XTIDE is not the same as XTCF, right? The register array is at different port offsets. So testing XTIDE is not the same as testing XTCF. I am reading the question you filed over there, and learning about the 8-bit XTIDE mode they're talking about. This is different than the XTCF 8-bit mode that has been tested by @fhendrikx. In fact, the Solo/86 platform is using XTCF at a different port address, not XTIDE, so it's far closer to your Amstrad, except that the port offset base address is different. I fixed all this in the latest PR.

@toncho11
Copy link
Copy Markdown
Contributor

I'll check on 86Box, thanks. What do you think of the idea of trying to boot your Amstrad from CF? Will it do it? That would save lots of time, no?

We have https://github.com/ghaerr/elks/wiki/Installing-HD-image-on-physical-media and it is not so simple/fast either.

@ghaerr
Copy link
Copy Markdown
Owner Author

ghaerr commented Jul 15, 2025

it is not so simple/fast either.

I forgot you're trying to do this from Windows. Also, the new ATA driver doesn't use CHS, so I think we can create a much easier mechanism to building HD images that don't have to have preset CHS values. I am also currently evaluating getting partitions working on CF.

Since most modern hard drives don't actually require CHS settings, but always support LBA, I am thinking ELKS can potentially do away with requiring CHS, except for the BIOSHD driver, which will always work. This simplifies things considerably. Floppy disks will always have to use CHS.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants