Skip to content

FDC, now with writing support #14

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 52 commits into
base: main
Choose a base branch
from

Conversation

giuliof
Copy link
Contributor

@giuliof giuliof commented Jan 2, 2025

No description provided.

@giuliof giuliof force-pushed the giuliof/fdc-now-with-more-writing branch 4 times, most recently from b65862d to 9ea2dbc Compare January 8, 2025 21:08
giuliof added 10 commits January 8, 2025 22:40
INT is used to notify asynchronous events to the MCU (i.e. seek
command is sent, INT rises when the head has moved onto the requested
track). This is used too to notify if new data burst is available during
execution phase.

If no disk is present and read command is requested, INT=0.
In this implementation, a disk inserted after the read command, will
trigger INT and it should unlock the reading process.

Now we can handle at least one error from floppy.c. Other errors will
raise an assertion and will be implemented later.
Floppy.h now exposes only frontend methods and fdc doesn't have to include
floppy.h. Access to the floppy disk image is provided through callbacks.
This should help with fdc module testability.
@giuliof giuliof force-pushed the giuliof/fdc-now-with-more-writing branch from 9ea2dbc to 383d305 Compare January 8, 2025 21:43
@giomba giomba self-requested a review January 9, 2025 11:06
Copy link
Collaborator

@giomba giomba left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As requested, here is my early review.
I see there are still a lot of TODOs, but overall it looks it is taking the right path.
I look forward seeing how you will manage all the errors.
A lot of comments regard naming and style, which I wrote to make the code more consistent with the emulator's style.
As a general rule, even if you don't feel like implementing the error handling everywhere, please at least add some explicit log, so that an user can clearly see that an unhandled error has been detected. In this way, there won't be a totally silent fail, and future problem hunting would be easier. (= problem hunting: I mainly expect to encounter errors which are not implemented yet, but at least the emulator will make it clear that warning: you should implement something here, so that we know where to look)
I am not very familiar with the way the FDC works, and I thank you for your contribution.
I'll also look forward seeing the test stuff with the CMake refactoring you have shown me during last call.

src/floppy.c Outdated
Comment on lines 129 to 132
if (phy_head != head)
return -1;
if (phy_track != track)
return -1;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that this could be interesting to exploit for some "copy protection" mechanism, even if I doubt it was something commonly used when this computer was used originally.
👍 for returning -1, but I would also add a LOG_WARN line, just to make it more clear, in case someone stumbles upon such a situation while using the emulator.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will be done


FILE *fd = floppy_units[unit_number].fd;
size_t len = 256;
long offset;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Coming from an embedded background, long looks ambiguous to me, and makes me costantly wonder "how long is this long? Should I worry?" (especially in the next lines where there are a lot of casts to long).

To make the code more readable and hide the casts, I suggest to replace magic numbers (and casts) with macros, like this:

+#define STANDARD_SECTOR_SIZE (1024L)
+#define TRACK0_SECTOR_SIZE   (256L)
+
 static int floppy_write_buffer(uint8_t *buffer, uint8_t unit_number,
                                bool phy_head, uint8_t phy_track, bool head,
                                uint8_t track, uint8_t sector) {
@@ -173,19 +176,19 @@ static int floppy_write_buffer(uint8_t *buffer, uint8_t unit_number,
         if (sector > 15)
             return -1;
         // Compute byte offset to sector start
-        offset = (long)sector * 256;
+        offset = sector * TRACK0_SECTOR_SIZE;
     } else {
         if (sector > 5)
             return -1;
 
         // Compute byte offset to sector start
-        offset = (long)track * 1024 * 5 * 2;
-        offset += (long)head * 1024 * 5;
-        offset += (long)sector * 1024;
+        offset = track * STANDARD_SECTOR_SIZE * 5 * 2;
+        offset += head * STANDARD_SECTOR_SIZE * 5;
+        offset += sector * STANDARD_SECTOR_SIZE;
 
         // First track has a different format
-        offset -= (long)1024 * 5;
-        offset += (long)256 * 16;
+        offset -= STANDARD_SECTOR_SIZE * 5;
+        offset += TRACK0_SECTOR_SIZE * 16;
 
         len = 1024;
     }

Note: macro's names are just an hint: feel free to work on them if it feels right.

You could also use other defines for 5 and 16, so that you won't need additional comments because code is self explanatory.
Example:

     // Locate sector start
     if (track == 0 && head == 0) {
-        // First track has max 16 sectors
-        if (sector > 15)
+#define TRACK0_SECTORS_PER_TRACK 16
+        if (sector >= TRACK0_SECTORS_PER_TRACK)
             return -1;
         // Compute byte offset to sector start
         offset = (long)sector * 256;
@@ -184,7 +184,8 @@ static int floppy_write_buffer(uint8_t *buffer, uint8_t unit_number,
         offset += (long)sector * 1024;
 
         // First track has a different format
-        offset -= (long)1024 * 5;
+#define STANDARD_SECTORS_PER_TRACK 5
+        offset -= (long)1024 * STANDARD_SECTORS_PER_TRACK;
         offset += (long)256 * 16;
 
         len = 1024;

Again: names and location are just an hint.

Does this look reasonable to you?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will be done

src/floppy.c Outdated
Comment on lines 163 to 166
if (fseek(fd, offset, SEEK_SET) < 0)
return -1;

len = fwrite(buffer, sizeof(uint8_t), len, fd);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fseek and fwrite not working would be very... interesting.
I suggest to also LOG_ERR something, in case this happens, because this looks more like an issue of the underlying operating system, rather than of this emulator.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fseek, fread and fwrite should set errno and this can be converted to string, can it be used to give a more informative message?

src/fdc.c Outdated

exec_buffer[rwcount++] = value;

if (rwcount == rwcount_max) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here we can negate the condition and perform an early return to save indentation space.

src/fdc.c Outdated
@@ -181,6 +173,8 @@ static uint8_t args[8];
static uint8_t exec_buffer[1024];
// Result buffer. Each command has maximum 7 bytes as argument.
static uint8_t result[7];
static bool tc_status = false;
static bool isReady = false;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Every other variable uses lower_snake_case, I suggest to use it here too for consistency.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, will be pushed soon

@@ -122,7 +125,7 @@ int floppy_write_buffer(uint8_t *buffer, uint8_t unit_number, bool phy_head,

// No disk loaded, raise error
if (fd == NULL)
return -1;
return 0; // TODO(giuliof): no disk loaded error
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment may become obsolete in case the error is handled.
For now, shall we log something, just to raise the user's attention?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as before

src/fdc.c Outdated
return isReady;
}

void fdc_kickDiskImage(void) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know what this function does because you told me, but can you add some documentation?
Also, the buffer_update() and buffer_write_size() functions which are called here, aren't really very descriptive of what this function is doing.

src/fdc.c Outdated
Comment on lines 219 to 227
/* Callbacks to handle floppy read and write */
static int (*read_buffer_cb)(uint8_t *buffer, uint8_t unit_number,
bool phy_head, uint8_t phy_track, bool head,
uint8_t track, uint8_t sector) = NULL;

static int (*write_buffer_cb)(uint8_t *buffer, uint8_t unit_number,
bool phy_head, uint8_t phy_track, bool head,
uint8_t track, uint8_t sector) = NULL;

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In order to make this readable, would you consider using typedef to declare the callback type, and then define the function pointer?
Example:

-static int (*read_buffer_cb)(uint8_t *buffer, uint8_t unit_number,
-                             bool phy_head, uint8_t phy_track, bool head,
-                             uint8_t track, uint8_t sector) = NULL;
+typedef int (*floppy_read_buffer_t)(uint8_t *buffer, uint8_t unit_number,
+                                    bool phy_head, uint8_t phy_track, bool head,
+                                    uint8_t track, uint8_t sector);
+static floppy_read_buffer_t read_buffer_cb = NULL;

This suggestion is also valid for write_buffer_cb.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, will be pushed soon.

@@ -0,0 +1,36 @@
#ifndef CEDA_FDC_REGISTERS_H_
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need a separate fdc_registers.h file to manage some circular dependency? It looks like we can put all these definitions inside fdc.h

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Initially, all the content of fdc_registers.h was inside fdc.c since it doesn't need to be exported.
With the introduction of the tests, I needed to share register addresses between core implementation and tests only, so I created a separate file for this.

Comment on lines 5 to 6
#define ADDR_STATUS_REGISTER (0x00)
#define ADDR_DATA_REGISTER (0x01)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this needs to be "public" (ie. in an header file), please prefix it to keep the global namespace as clean as possible.
Example:

- #define ADDR_STATUS_REGISTER (0x00)
+ #define FDC_ADDR_STATUS_REGISTER (0x00)

The same suggestion applies to cmd_t (see below), the enum values and the CMD_* ones.

As a general rule, I would suggest to always use prefixed #defines and enums, even in "closed" compilation units (.c), so we won't have to think about this anymore. (unless adding a prefix makes the code much much harder to read)

@giuliof giuliof force-pushed the giuliof/fdc-now-with-more-writing branch from 9989d7f to 1385fc2 Compare January 16, 2025 22:00
Just check if read command can accept arguments and puts the FDC in the
right status, even if media is not loaded and then loaded at runtime
@giuliof giuliof force-pushed the giuliof/fdc-now-with-more-writing branch from 1385fc2 to 9644118 Compare January 16, 2025 22:17
@giuliof giuliof force-pushed the giuliof/fdc-now-with-more-writing branch from 9644118 to 0c6728b Compare January 18, 2025 18:19
TODO: remove bitfields and use these constants
@giuliof giuliof force-pushed the giuliof/fdc-now-with-more-writing branch 3 times, most recently from 4be7443 to 79f7ecf Compare January 23, 2025 19:58
@giuliof giuliof force-pushed the giuliof/fdc-now-with-more-writing branch from 9982556 to 4ad3980 Compare February 6, 2025 20:53
DTL is data transfer length and it is used with N=0 to specify the
number of bytes per sector transferred during read or write operations.
In tests, this is used to avoid very long transfers.
Added status registers to store error flags
@giuliof giuliof force-pushed the giuliof/fdc-now-with-more-writing branch from 2c627bb to 89db320 Compare February 12, 2025 21:57
See tests, interrupt must be deasserted in result phase or when
signalling an invalid command
- Removed one TODO with kinda working implementation
- Improved seek test by removing edge case
- Can be further improved, ad the moment sequential seek/recalibrate
  without interleaved sense interrupt are not supported
@giuliof giuliof force-pushed the giuliof/fdc-now-with-more-writing branch from 89db320 to dadc697 Compare February 12, 2025 22:02
This is the proper execution order expected by the FDC, else the INT
would be cleared by read operation
This temporarily breaks one test, but will be fixed soon
e.g. Sense Interrupt that is not following Seek
…itions

- terminal count is always required, read and write operation never stop
  by themselves (qui pro quo from the readq track command that seems to
  stop at the end of track? To be verified, not in tests).
- Maybe, the error codes are not be fully true, but the CHS values
  should be fine.
- Tests now give error since the implementation is not fully correct.
  See next commit.
… compliant

Introduced IDR (ID Register) and next IDR to handle the result phase in
both success and error condition.
Briefly, when one or more sectors are succesfully read, IDR is
incremented to the next one (handling head/track overflow).
If an error occur, the CHS in result are referred to the latest
correctly read sector.
Tested on the actual FDC device.
@giuliof giuliof force-pushed the giuliof/fdc-now-with-more-writing branch 2 times, most recently from 43772b7 to 434a1a1 Compare March 11, 2025 22:34
giuliof added 3 commits March 11, 2025 23:36
Note, the tests should now fail. This is legit, the previous
implementation was not fully correct (and probably neither this one, see
comments...).
Code fixup in the next commit.
@giuliof giuliof force-pushed the giuliof/fdc-now-with-more-writing branch from 434a1a1 to 5d0f62d Compare March 11, 2025 22:36
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.

2 participants