-
Notifications
You must be signed in to change notification settings - Fork 0
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
base: main
Are you sure you want to change the base?
Conversation
b65862d
to
9ea2dbc
Compare
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.
…interface for tests
9ea2dbc
to
383d305
Compare
There was a problem hiding this 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 TODO
s, 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
if (phy_head != head) | ||
return -1; | ||
if (phy_track != track) | ||
return -1; |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
if (fseek(fd, offset, SEEK_SET) < 0) | ||
return -1; | ||
|
||
len = fwrite(buffer, sizeof(uint8_t), len, fd); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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
/* 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; | ||
|
There was a problem hiding this comment.
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
.
There was a problem hiding this comment.
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_ |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
src/fdc_registers.h
Outdated
#define ADDR_STATUS_REGISTER (0x00) | ||
#define ADDR_DATA_REGISTER (0x01) |
There was a problem hiding this comment.
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 #define
s and enum
s, 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)
9989d7f
to
1385fc2
Compare
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
1385fc2
to
9644118
Compare
9644118
to
0c6728b
Compare
TODO: remove bitfields and use these constants
4be7443
to
79f7ecf
Compare
9982556
to
4ad3980
Compare
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
2c627bb
to
89db320
Compare
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
89db320
to
dadc697
Compare
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.
43772b7
to
434a1a1
Compare
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.
434a1a1
to
5d0f62d
Compare
No description provided.