mirror of
https://github.com/wavemotion-dave/GimliDS.git
synced 2025-06-18 13:55:32 -04:00
First pass at improving the 1541 emulation and GCR support by backporting some GCR handling from Frodo 4.3
This commit is contained in:
parent
70b4450f1b
commit
3f27952ff7
@ -1,5 +1,22 @@
|
||||
// =====================================================================================
|
||||
// GimliDS Copyright (c) 2025 Dave Bernazzani (wavemotion-dave)
|
||||
//
|
||||
// As GimliDS is a port of the Frodo emulator for the DS/DSi/XL/LL handhelds,
|
||||
// any copying or distribution of this emulator, its source code and associated
|
||||
// readme files, with or without modification, are permitted per the original
|
||||
// Frodo emulator license shown below. Hugest thanks to Christian Bauer for his
|
||||
// efforts to provide a clean open-source emulation base for the C64.
|
||||
//
|
||||
// Numerous hacks and 'unsafe' optimizations have been performed on the original
|
||||
// Frodo emulator codebase to get it running on the small handheld system. You
|
||||
// are strongly encouraged to seek out the official Frodo sources if you're at
|
||||
// all interested in this emulator code.
|
||||
//
|
||||
// The GimliDS emulator is offered as-is, without any warranty. Please see readme.md
|
||||
// =====================================================================================
|
||||
|
||||
/*
|
||||
* 1541job.cpp - Emulation of 1541 GCR disk reading/writing
|
||||
* 1541gcr.cpp - Emulation of 1541 GCR disk reading/writing
|
||||
*
|
||||
* Frodo Copyright (C) Christian Bauer
|
||||
*
|
||||
@ -32,31 +49,29 @@
|
||||
* ------------------
|
||||
*
|
||||
* - No GCR writing possible (WriteSector is a ROM patch)
|
||||
* - Programs depending on the exact timing of head movement/disk
|
||||
* rotation don't work
|
||||
* - Programs depending on the exact timing of head movement don't work
|
||||
* - The .d64 error info is unused
|
||||
*/
|
||||
#include <nds.h>
|
||||
|
||||
#include "sysdeps.h"
|
||||
|
||||
#include "1541gcr.h"
|
||||
#include "CPU1541.h"
|
||||
#include "Prefs.h"
|
||||
|
||||
|
||||
// Number of tracks/sectors
|
||||
const int NUM_TRACKS = 35;
|
||||
const int NUM_SECTORS = 683;
|
||||
constexpr unsigned NUM_TRACKS = 35;
|
||||
constexpr unsigned NUM_SECTORS = 683;
|
||||
|
||||
// Size of GCR encoded data
|
||||
const int GCR_SECTOR_SIZE = 5 + 10 + 9 + 5 + 325 + 12; // SYNC + Header + Gap + SYNC + Data + Gap
|
||||
constexpr unsigned GCR_SECTOR_SIZE = 5 + 10 + 9 + 5 + 325 + 12; // SYNC + Header + Gap + SYNC + Data + Gap
|
||||
constexpr unsigned GCR_TRACK_SIZE = GCR_SECTOR_SIZE * 21; // Each track in gcr_data has room for 21 sectors
|
||||
constexpr unsigned GCR_DISK_SIZE = GCR_TRACK_SIZE * NUM_TRACKS;
|
||||
|
||||
const int GCR_TRACK_SIZE = GCR_SECTOR_SIZE * 21; // Each track in gcr_data has 21 sectors
|
||||
const int GCR_DISK_SIZE = GCR_TRACK_SIZE * NUM_TRACKS;
|
||||
|
||||
// Job return codes
|
||||
const int RET_OK = 1; // No error
|
||||
const int RET_NOT_FOUND = 2; // Block not found
|
||||
const int RET_NOT_READY = 15; // Drive not ready
|
||||
// Clock cycles per GCR byte
|
||||
// TODO: handle speed selection
|
||||
constexpr unsigned CYCLES_PER_BYTE = 30;
|
||||
|
||||
|
||||
// Number of sectors of each track
|
||||
@ -83,19 +98,27 @@ const int sector_offset[36] = {
|
||||
* emulation is enabled
|
||||
*/
|
||||
|
||||
Job1541::Job1541(uint8 *ram1541) : ram(ram1541)
|
||||
Job1541::Job1541(uint8_t *ram1541) : ram(ram1541), the_file(nullptr)
|
||||
{
|
||||
the_file = NULL;
|
||||
current_halftrack = 2; // Track 1
|
||||
|
||||
gcr_data = gcr_ptr = gcr_track_start = new uint8[GCR_DISK_SIZE];
|
||||
gcr_track_end = gcr_track_start + GCR_TRACK_SIZE;
|
||||
current_halftrack = 2;
|
||||
gcr_data = new uint8_t[GCR_DISK_SIZE];
|
||||
memset(gcr_data, 0x55, GCR_DISK_SIZE);
|
||||
|
||||
set_gcr_ptr();
|
||||
gcr_offset = 1;
|
||||
|
||||
last_byte_cycle = 0;
|
||||
byte_latch = 0;
|
||||
|
||||
disk_changed = true;
|
||||
motor_on = false;
|
||||
write_protected = true;
|
||||
disk_changed = true;
|
||||
byte_ready = false;
|
||||
|
||||
if (ThePrefs.TrueDrive)
|
||||
if (ThePrefs.TrueDrive) {
|
||||
open_d64_file(ThePrefs.DrivePath[0]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -106,6 +129,7 @@ Job1541::Job1541(uint8 *ram1541) : ram(ram1541)
|
||||
Job1541::~Job1541()
|
||||
{
|
||||
close_d64_file();
|
||||
|
||||
delete[] gcr_data;
|
||||
}
|
||||
|
||||
@ -114,25 +138,23 @@ Job1541::~Job1541()
|
||||
* Preferences may have changed
|
||||
*/
|
||||
|
||||
void Job1541::NewPrefs(Prefs *prefs)
|
||||
void Job1541::NewPrefs(const Prefs *prefs)
|
||||
{
|
||||
// 1541 emulation turned off?
|
||||
if (!prefs->TrueDrive)
|
||||
if (!prefs->TrueDrive) {
|
||||
close_d64_file();
|
||||
|
||||
// 1541 emulation turned on?
|
||||
else if (!ThePrefs.TrueDrive && prefs->TrueDrive)
|
||||
} else if (!ThePrefs.TrueDrive && prefs->TrueDrive) {
|
||||
open_d64_file(prefs->DrivePath[0]);
|
||||
|
||||
// .d64 file name changed?
|
||||
else
|
||||
}
|
||||
else if (strcmp(ThePrefs.DrivePath[0], prefs->DrivePath[0]))
|
||||
{
|
||||
if (strcmp(ThePrefs.DrivePath[0], prefs->DrivePath[0]))
|
||||
{
|
||||
close_d64_file();
|
||||
open_d64_file(prefs->DrivePath[0]);
|
||||
disk_changed = true;
|
||||
}
|
||||
close_d64_file();
|
||||
open_d64_file(prefs->DrivePath[0]);
|
||||
disk_changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -141,39 +163,40 @@ void Job1541::NewPrefs(Prefs *prefs)
|
||||
* Open .d64 file
|
||||
*/
|
||||
|
||||
void Job1541::open_d64_file(char *filepath)
|
||||
void Job1541::open_d64_file(const std::string & filepath)
|
||||
{
|
||||
long size;
|
||||
uint8 magic[4];
|
||||
static uint8 bam[256];
|
||||
//printdbg(filepath);
|
||||
uint8_t magic[4];
|
||||
uint8_t bam[256];
|
||||
|
||||
// Clear GCR buffer
|
||||
memset(gcr_data, 0x55, GCR_DISK_SIZE);
|
||||
|
||||
// Try opening the file for reading/writing first, then for reading only
|
||||
write_protected = false;
|
||||
the_file = fopen(filepath, "rb+");
|
||||
if (the_file == NULL) {
|
||||
the_file = fopen(filepath.c_str(), "rb+");
|
||||
if (the_file == nullptr) {
|
||||
write_protected = true;
|
||||
the_file = fopen(filepath, "rb");
|
||||
the_file = fopen(filepath.c_str(), "rb");
|
||||
}
|
||||
if (the_file != NULL) {
|
||||
if (the_file != nullptr) {
|
||||
|
||||
// Check length
|
||||
fseek(the_file, 0, SEEK_END);
|
||||
if ((size = ftell(the_file)) < NUM_SECTORS * 256) {
|
||||
if (((size = ftell(the_file))) < (int)(NUM_SECTORS * 256)) {
|
||||
fclose(the_file);
|
||||
the_file = NULL;
|
||||
the_file = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
// x64 image?
|
||||
fseek(the_file, 0, SEEK_SET);
|
||||
fread(&magic, 4, 1, the_file);
|
||||
if (magic[0] == 0x43 && magic[1] == 0x15 && magic[2] == 0x41 && magic[3] == 0x64)
|
||||
if (magic[0] == 0x43 && magic[1] == 0x15 && magic[2] == 0x41 && magic[3] == 0x64) {
|
||||
image_header = 64;
|
||||
else
|
||||
} else {
|
||||
image_header = 0;
|
||||
}
|
||||
|
||||
// Preset error info (all sectors no error)
|
||||
memset(error_info, 1, NUM_SECTORS);
|
||||
@ -199,35 +222,33 @@ void Job1541::open_d64_file(char *filepath)
|
||||
* Close .d64 file
|
||||
*/
|
||||
|
||||
void Job1541::close_d64_file(void)
|
||||
void Job1541::close_d64_file()
|
||||
{
|
||||
if (the_file != NULL) {
|
||||
// Blank out GCR data
|
||||
memset(gcr_data, 0x55, GCR_DISK_SIZE);
|
||||
|
||||
if (the_file != nullptr) {
|
||||
fclose(the_file);
|
||||
the_file = NULL;
|
||||
the_file = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void Job1541::Reset(void)
|
||||
{
|
||||
current_halftrack = 2;
|
||||
disk_changed = true;
|
||||
motor_on = false;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Write sector to disk (1541 ROM patch)
|
||||
*/
|
||||
|
||||
void Job1541::WriteSector(void)
|
||||
void Job1541::WriteSector()
|
||||
{
|
||||
int track = ram[0x18];
|
||||
int track = ram[0x18];
|
||||
int sector = ram[0x19];
|
||||
uint16 buf = ram[0x30] | (ram[0x31] << 8);
|
||||
uint16_t buf = ram[0x30] | (ram[0x31] << 8);
|
||||
|
||||
if (buf <= 0x0700)
|
||||
if (write_sector(track, sector, ram + buf))
|
||||
if (buf <= 0x0700) {
|
||||
if (write_sector(track, sector, ram + buf)) {
|
||||
sector2gcr(track, sector);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -235,30 +256,31 @@ void Job1541::WriteSector(void)
|
||||
* Format one track (1541 ROM patch)
|
||||
*/
|
||||
|
||||
void Job1541::FormatTrack(void)
|
||||
void Job1541::FormatTrack()
|
||||
{
|
||||
int track = ram[0x51];
|
||||
|
||||
// Get new ID
|
||||
uint8 bufnum = ram[0x3d];
|
||||
uint8_t bufnum = ram[0x3d];
|
||||
id1 = ram[0x12 + bufnum];
|
||||
id2 = ram[0x13 + bufnum];
|
||||
|
||||
// Create empty block
|
||||
uint8 buf[256];
|
||||
uint8_t buf[256];
|
||||
memset(buf, 1, 256);
|
||||
buf[0] = 0x4b;
|
||||
|
||||
// Write block to all sectors on track
|
||||
for(int sector=0; sector<num_sectors[track]; sector++) {
|
||||
for (int sector=0; sector<num_sectors[track]; sector++) {
|
||||
write_sector(track, sector, buf);
|
||||
sector2gcr(track, sector);
|
||||
}
|
||||
|
||||
// Clear error info (all sectors no error)
|
||||
if (track == 35)
|
||||
if (track == 35) {
|
||||
memset(error_info, 1, NUM_SECTORS);
|
||||
// Write error_info to disk?
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -267,12 +289,12 @@ void Job1541::FormatTrack(void)
|
||||
* true: success, false: error
|
||||
*/
|
||||
|
||||
bool Job1541::read_sector(int track, int sector, uint8 *buffer)
|
||||
bool Job1541::read_sector(unsigned track, unsigned sector, uint8_t *buffer)
|
||||
{
|
||||
int offset;
|
||||
|
||||
floppy_soundfx(0); // Play floppy SFX if needed
|
||||
|
||||
|
||||
// Convert track/sector to byte offset in file
|
||||
if ((offset = offset_from_ts(track, sector)) < 0)
|
||||
return false;
|
||||
@ -288,10 +310,10 @@ bool Job1541::read_sector(int track, int sector, uint8 *buffer)
|
||||
* true: success, false: error
|
||||
*/
|
||||
|
||||
bool Job1541::write_sector(int track, int sector, uint8 *buffer)
|
||||
bool Job1541::write_sector(unsigned track, unsigned sector, uint8_t *buffer)
|
||||
{
|
||||
int offset;
|
||||
|
||||
|
||||
floppy_soundfx(1); // Play floppy SFX if needed
|
||||
|
||||
// Convert track/sector to byte offset in file
|
||||
@ -308,15 +330,15 @@ bool Job1541::write_sector(int track, int sector, uint8 *buffer)
|
||||
* Convert track/sector to offset
|
||||
*/
|
||||
|
||||
int Job1541::secnum_from_ts(int track, int sector)
|
||||
unsigned Job1541::secnum_from_ts(unsigned track, unsigned sector)
|
||||
{
|
||||
return sector_offset[track] + sector;
|
||||
}
|
||||
|
||||
int Job1541::offset_from_ts(int track, int sector)
|
||||
int Job1541::offset_from_ts(unsigned track, unsigned sector)
|
||||
{
|
||||
if ((track < 1) || (track > NUM_TRACKS) ||
|
||||
(sector < 0) || (sector >= num_sectors[track]))
|
||||
(sector < 0) || (sector >= (unsigned)num_sectors[track]))
|
||||
return -1;
|
||||
|
||||
return (sector_offset[track] + sector) << 8;
|
||||
@ -327,14 +349,14 @@ int Job1541::offset_from_ts(int track, int sector)
|
||||
* Convert 4 bytes to 5 GCR encoded bytes
|
||||
*/
|
||||
|
||||
const uint16 gcr_table[16] = {
|
||||
const uint16_t gcr_table[16] = {
|
||||
0x0a, 0x0b, 0x12, 0x13, 0x0e, 0x0f, 0x16, 0x17,
|
||||
0x09, 0x19, 0x1a, 0x1b, 0x0d, 0x1d, 0x1e, 0x15
|
||||
};
|
||||
|
||||
void Job1541::gcr_conv4(uint8 *from, uint8 *to)
|
||||
void Job1541::gcr_conv4(const uint8_t *from, uint8_t *to)
|
||||
{
|
||||
uint16 g;
|
||||
uint16_t g;
|
||||
|
||||
g = (gcr_table[*from >> 4] << 5) | gcr_table[*from & 15];
|
||||
*to++ = g >> 2;
|
||||
@ -361,40 +383,44 @@ void Job1541::gcr_conv4(uint8 *from, uint8 *to)
|
||||
* Create GCR encoded disk data from image
|
||||
*/
|
||||
|
||||
void Job1541::sector2gcr(int track, int sector)
|
||||
void Job1541::sector2gcr(unsigned track, unsigned sector)
|
||||
{
|
||||
uint8 block[256];
|
||||
uint8 buf[4];
|
||||
uint8 *p = gcr_data + (track-1) * GCR_TRACK_SIZE + sector * GCR_SECTOR_SIZE;
|
||||
uint8_t block[256];
|
||||
uint8_t buf[4];
|
||||
uint8_t *p = gcr_data + (track-1) * GCR_TRACK_SIZE + sector * GCR_SECTOR_SIZE;
|
||||
|
||||
read_sector(track, sector, block);
|
||||
|
||||
// Create GCR header
|
||||
*p++ = 0xff;*p++ = 0xff;*p++ = 0xff;*p++ = 0xff;*p++ = 0xff; // SYNC
|
||||
memset(p, 0xff, 5); // SYNC
|
||||
p += 5;
|
||||
buf[0] = 0x08; // Header mark
|
||||
buf[1] = sector ^ track ^ id2 ^ id1; // Checksum
|
||||
buf[2] = sector;
|
||||
buf[3] = track;
|
||||
gcr_conv4(buf, p);
|
||||
p += 5;
|
||||
buf[0] = id2;
|
||||
buf[1] = id1;
|
||||
buf[2] = 0x0f;
|
||||
buf[3] = 0x0f;
|
||||
gcr_conv4(buf, p+5);
|
||||
p += 10;
|
||||
gcr_conv4(buf, p);
|
||||
p += 5;
|
||||
|
||||
memset(p, 0x55, 9); // Gap
|
||||
p += 9;
|
||||
|
||||
// Create GCR data
|
||||
uint8 sum;
|
||||
*p++ = 0xff;*p++ = 0xff;*p++ = 0xff;*p++ = 0xff;*p++ = 0xff; // SYNC
|
||||
memset(p, 0xff, 5); // SYNC
|
||||
p += 5;
|
||||
uint8_t sum;
|
||||
buf[0] = 0x07; // Data mark
|
||||
sum = buf[1] = block[0];
|
||||
sum = buf[1] = block[0];
|
||||
sum ^= buf[2] = block[1];
|
||||
sum ^= buf[3] = block[2];
|
||||
gcr_conv4(buf, p);
|
||||
p += 5;
|
||||
for (int i=3; i<255; i+=4) {
|
||||
for (unsigned i = 3; i < 255; i += 4) {
|
||||
sum ^= buf[0] = block[i];
|
||||
sum ^= buf[1] = block[i+1];
|
||||
sum ^= buf[2] = block[i+2];
|
||||
@ -408,15 +434,30 @@ void Job1541::sector2gcr(int track, int sector)
|
||||
buf[3] = 0;
|
||||
gcr_conv4(buf, p);
|
||||
p += 5;
|
||||
memset(p, 0x55, 12); // Gap
|
||||
|
||||
memset(p, 0x55, 12); // Gap
|
||||
}
|
||||
|
||||
void Job1541::disk2gcr(void)
|
||||
void Job1541::disk2gcr()
|
||||
{
|
||||
// Convert all tracks and sectors
|
||||
for (int track=1; track<=NUM_TRACKS; track++)
|
||||
for(int sector=0; sector<num_sectors[track]; sector++)
|
||||
for (unsigned track = 1; track <= NUM_TRACKS; ++track) {
|
||||
for(unsigned sector = 0; sector < (unsigned)num_sectors[track]; ++sector) {
|
||||
sector2gcr(track, sector);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Reset GCR pointers for current halftrack
|
||||
*/
|
||||
|
||||
void Job1541::set_gcr_ptr()
|
||||
{
|
||||
gcr_track_start = gcr_data + ((current_halftrack >> 1) - 1) * GCR_TRACK_SIZE;
|
||||
gcr_track_end = gcr_track_start + num_sectors[current_halftrack >> 1] * GCR_SECTOR_SIZE;
|
||||
gcr_track_length = gcr_track_end - gcr_track_start;
|
||||
}
|
||||
|
||||
|
||||
@ -424,29 +465,18 @@ void Job1541::disk2gcr(void)
|
||||
* Move R/W head out (lower track numbers)
|
||||
*/
|
||||
|
||||
void Job1541::MoveHeadOut(void)
|
||||
void Job1541::MoveHeadOut(uint32_t cycle_counter)
|
||||
{
|
||||
if (current_halftrack == 2)
|
||||
return;
|
||||
|
||||
current_halftrack--;
|
||||
|
||||
gcr_ptr = gcr_track_start = gcr_data + ((current_halftrack >> 1) - 1) * GCR_TRACK_SIZE;
|
||||
gcr_track_end = gcr_track_start + num_sectors[current_halftrack >> 1] * GCR_SECTOR_SIZE;
|
||||
}
|
||||
last_byte_cycle = cycle_counter;
|
||||
byte_ready = false;
|
||||
|
||||
/*
|
||||
* Control spindle motor
|
||||
*/
|
||||
|
||||
void Job1541::SetMotor(bool on)
|
||||
{
|
||||
motor_on = on;
|
||||
}
|
||||
|
||||
|
||||
bool Job1541::ByteReady(void)
|
||||
{
|
||||
return motor_on;
|
||||
set_gcr_ptr();
|
||||
gcr_offset = 1; // TODO: handle track-to-track skew
|
||||
}
|
||||
|
||||
|
||||
@ -454,14 +484,18 @@ bool Job1541::ByteReady(void)
|
||||
* Move R/W head in (higher track numbers)
|
||||
*/
|
||||
|
||||
void Job1541::MoveHeadIn(void)
|
||||
void Job1541::MoveHeadIn(uint32_t cycle_counter)
|
||||
{
|
||||
if (current_halftrack == NUM_TRACKS*2)
|
||||
return;
|
||||
|
||||
current_halftrack++;
|
||||
|
||||
gcr_ptr = gcr_track_start = gcr_data + ((current_halftrack >> 1) - 1) * GCR_TRACK_SIZE;
|
||||
gcr_track_end = gcr_track_start + num_sectors[current_halftrack >> 1] * GCR_SECTOR_SIZE;
|
||||
last_byte_cycle = cycle_counter;
|
||||
byte_ready = false;
|
||||
|
||||
set_gcr_ptr();
|
||||
gcr_offset = 1; // TODO: handle track-to-track skew
|
||||
}
|
||||
|
||||
|
||||
@ -469,12 +503,17 @@ void Job1541::MoveHeadIn(void)
|
||||
* Get state
|
||||
*/
|
||||
|
||||
void Job1541::GetState(Job1541State *state)
|
||||
void Job1541::GetState(Job1541State *state) const
|
||||
{
|
||||
state->current_halftrack = current_halftrack;
|
||||
state->gcr_ptr = gcr_ptr - gcr_data;
|
||||
state->gcr_offset = gcr_offset;
|
||||
state->last_byte_cycle = last_byte_cycle;
|
||||
state->byte_latch = byte_latch;
|
||||
|
||||
state->motor_on = motor_on;
|
||||
state->write_protected = write_protected;
|
||||
state->disk_changed = disk_changed;
|
||||
state->byte_ready = byte_ready;
|
||||
}
|
||||
|
||||
|
||||
@ -482,12 +521,106 @@ void Job1541::GetState(Job1541State *state)
|
||||
* Set state
|
||||
*/
|
||||
|
||||
void Job1541::SetState(Job1541State *state)
|
||||
void Job1541::SetState(const Job1541State *state)
|
||||
{
|
||||
current_halftrack = state->current_halftrack;
|
||||
gcr_ptr = gcr_data + state->gcr_ptr;
|
||||
gcr_track_start = gcr_data + ((current_halftrack >> 1) - 1) * GCR_TRACK_SIZE;
|
||||
gcr_track_end = gcr_track_start + num_sectors[current_halftrack >> 1] * GCR_SECTOR_SIZE;
|
||||
set_gcr_ptr();
|
||||
gcr_offset = state->gcr_offset;
|
||||
last_byte_cycle = state->last_byte_cycle;
|
||||
byte_latch = state->byte_latch;
|
||||
|
||||
motor_on = state->motor_on;
|
||||
write_protected = state->write_protected;
|
||||
disk_changed = state->disk_changed;
|
||||
byte_ready = state->byte_ready;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Rotate disk (virtually)
|
||||
*/
|
||||
|
||||
void Job1541::rotate_disk(uint32_t cycle_counter)
|
||||
{
|
||||
if (motor_on) {
|
||||
|
||||
uint32_t elapsed = cycle_counter - last_byte_cycle;
|
||||
uint32_t advance = elapsed / CYCLES_PER_BYTE;
|
||||
|
||||
if (advance > 0) {
|
||||
gcr_offset += advance;
|
||||
while (gcr_offset >= gcr_track_length) {
|
||||
gcr_offset -= gcr_track_length;
|
||||
}
|
||||
if (gcr_offset == 0) { // Always keep >0 so we can access gcr_track_start[gcr_offset - 1]
|
||||
++gcr_offset;
|
||||
}
|
||||
|
||||
// Byte is ready if not on sync
|
||||
byte_ready = !(((gcr_track_start[gcr_offset - 1] & 0x03) == 0x03)
|
||||
&& (gcr_track_start[gcr_offset] == 0xff));
|
||||
|
||||
if (byte_ready)
|
||||
{
|
||||
byte_latch = gcr_track_start[gcr_offset];
|
||||
}
|
||||
|
||||
last_byte_cycle += advance * CYCLES_PER_BYTE;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
last_byte_cycle = cycle_counter;
|
||||
byte_ready = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Check if R/W head is over SYNC
|
||||
*/
|
||||
|
||||
bool Job1541::SyncFound(uint32_t cycle_counter)
|
||||
{
|
||||
rotate_disk(cycle_counter);
|
||||
|
||||
// Sync = ten "1" bits
|
||||
return motor_on && ((gcr_track_start[gcr_offset - 1] & 0x03) == 0x03)
|
||||
&& (gcr_track_start[gcr_offset] == 0xff);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Check if GCR byte is available for reading
|
||||
*/
|
||||
|
||||
bool Job1541::ByteReady(uint32_t cycle_counter)
|
||||
{
|
||||
rotate_disk(cycle_counter);
|
||||
|
||||
return byte_ready;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Read one GCR byte from disk
|
||||
*/
|
||||
|
||||
uint8_t Job1541::ReadGCRByte(uint32_t cycle_counter)
|
||||
{
|
||||
floppy_soundfx(0); // Play floppy SFX if needed
|
||||
|
||||
rotate_disk(cycle_counter);
|
||||
|
||||
byte_ready = false;
|
||||
return byte_latch;
|
||||
}
|
||||
|
||||
|
||||
void Job1541::Reset(void)
|
||||
{
|
||||
current_halftrack = 2; // Track 1
|
||||
disk_changed = true;
|
||||
motor_on = false;
|
||||
byte_ready = false;
|
||||
}
|
||||
|
@ -16,7 +16,7 @@
|
||||
// =====================================================================================
|
||||
|
||||
/*
|
||||
* 1541job.h - Emulation of 1541 GCR disk reading/writing
|
||||
* 1541gcr.h - Emulation of 1541 GCR disk reading/writing
|
||||
*
|
||||
* Frodo Copyright (C) Christian Bauer
|
||||
*
|
||||
@ -37,8 +37,9 @@
|
||||
|
||||
#ifndef _1541GCR_H
|
||||
#define _1541GCR_H
|
||||
#include <nds.h>
|
||||
#include "sysdeps.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
|
||||
class MOS6502_1541;
|
||||
class Prefs;
|
||||
@ -46,106 +47,100 @@ struct Job1541State;
|
||||
|
||||
class Job1541 {
|
||||
public:
|
||||
Job1541(uint8 *ram1541);
|
||||
Job1541(uint8_t *ram1541);
|
||||
~Job1541();
|
||||
|
||||
void GetState(Job1541State *state);
|
||||
void SetState(Job1541State *state);
|
||||
void NewPrefs(Prefs *prefs);
|
||||
void MoveHeadOut(void);
|
||||
void MoveHeadIn(void);
|
||||
bool SyncFound(void);
|
||||
bool ByteReady(void);
|
||||
uint8 ReadGCRByte(void);
|
||||
uint8 WPState(void);
|
||||
void WriteSector(void);
|
||||
void FormatTrack(void);
|
||||
void GetState(Job1541State *state) const;
|
||||
void SetState(const Job1541State *state);
|
||||
void NewPrefs(const Prefs *prefs);
|
||||
void Reset(void);
|
||||
|
||||
void SetMotor(bool on);
|
||||
void MoveHeadOut(uint32_t cycle_counter);
|
||||
void MoveHeadIn(uint32_t cycle_counter);
|
||||
|
||||
bool SyncFound(uint32_t cycle_counter);
|
||||
bool ByteReady(uint32_t cycle_counter);
|
||||
uint8_t ReadGCRByte(uint32_t cycle_counter);
|
||||
uint8_t WPState();
|
||||
|
||||
void WriteSector();
|
||||
void FormatTrack();
|
||||
|
||||
private:
|
||||
void open_d64_file(char *filepath);
|
||||
void close_d64_file(void);
|
||||
bool read_sector(int track, int sector, uint8 *buffer);
|
||||
bool write_sector(int track, int sector, uint8 *buffer);
|
||||
void format_disk(void);
|
||||
int secnum_from_ts(int track, int sector);
|
||||
int offset_from_ts(int track, int sector);
|
||||
void gcr_conv4(uint8 *from, uint8 *to);
|
||||
void sector2gcr(int track, int sector);
|
||||
void disk2gcr(void);
|
||||
void open_d64_file(const std::string & filepath);
|
||||
void close_d64_file();
|
||||
bool read_sector(unsigned track, unsigned sector, uint8_t *buffer);
|
||||
bool write_sector(unsigned track, unsigned sector, uint8_t *buffer);
|
||||
void format_disk();
|
||||
unsigned secnum_from_ts(unsigned track, unsigned sector);
|
||||
int offset_from_ts(unsigned track, unsigned sector);
|
||||
void gcr_conv4(const uint8_t *from, uint8_t *to);
|
||||
void sector2gcr(unsigned track, unsigned sector);
|
||||
void disk2gcr();
|
||||
void set_gcr_ptr();
|
||||
void rotate_disk(uint32_t cycle_counter);
|
||||
|
||||
uint8 *ram; // Pointer to 1541 RAM
|
||||
FILE *the_file; // File pointer for .d64 file
|
||||
int image_header; // Length of .d64/.x64 file header
|
||||
uint8_t *ram; // Pointer to 1541 RAM
|
||||
FILE *the_file; // File pointer for .d64 file
|
||||
unsigned image_header; // Length of .d64/.x64 file header
|
||||
|
||||
uint8 id1, id2; // ID of disk
|
||||
uint8 error_info[683]; // Sector error information (1 byte/sector)
|
||||
uint8_t id1, id2; // ID of disk
|
||||
uint8_t error_info[683]; // Sector error information (1 byte/sector)
|
||||
|
||||
uint8 *gcr_data; // Pointer to GCR encoded disk data
|
||||
uint8 *gcr_ptr; // Pointer to GCR data under R/W head
|
||||
uint8 *gcr_track_start; // Pointer to start of GCR data of current track
|
||||
uint8 *gcr_track_end; // Pointer to end of GCR data of current track
|
||||
int current_halftrack; // Current halftrack number (2..70)
|
||||
unsigned current_halftrack; // Current halftrack number (2..70)
|
||||
|
||||
bool write_protected; // Flag: Disk write-protected
|
||||
bool disk_changed; // Flag: Disk changed (WP sensor strobe control)
|
||||
bool motor_on;
|
||||
uint8_t *gcr_data; // Pointer to GCR encoded disk data
|
||||
uint8_t *gcr_track_start; // Pointer to start of GCR data of current track
|
||||
uint8_t *gcr_track_end; // Pointer to end of GCR data of current track
|
||||
size_t gcr_track_length; // Number of GCR bytes in current track
|
||||
size_t gcr_offset; // Offset of GCR data byte under R/W head, relative to gcr_track_start
|
||||
// Note: This is never 0, so we can access the previous GCR byte for sync detection
|
||||
|
||||
uint32_t last_byte_cycle; // Cycle when last byte was available
|
||||
uint8_t byte_latch; // Latch for read GCR byte
|
||||
|
||||
bool motor_on; // Flag: Spindle motor on
|
||||
bool write_protected; // Flag: Disk write-protected
|
||||
bool disk_changed; // Flag: Disk changed (WP sensor strobe control)
|
||||
bool byte_ready; // Flag: GCR byte ready for reading
|
||||
};
|
||||
|
||||
// 1541 GCR state
|
||||
struct Job1541State {
|
||||
int current_halftrack;
|
||||
uint32 gcr_ptr;
|
||||
uint32_t current_halftrack;
|
||||
uint32_t gcr_offset;
|
||||
uint32_t last_byte_cycle;
|
||||
uint8_t byte_latch;
|
||||
bool motor_on;
|
||||
bool write_protected;
|
||||
bool disk_changed;
|
||||
bool byte_ready;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Check if R/W head is over SYNC
|
||||
* Control spindle motor
|
||||
*/
|
||||
|
||||
inline bool Job1541::SyncFound(void)
|
||||
inline void Job1541::SetMotor(bool on)
|
||||
{
|
||||
if (*gcr_ptr == 0xff && gcr_ptr[1] != 0xff)
|
||||
return true;
|
||||
else {
|
||||
gcr_ptr++; // Rotate disk
|
||||
if (gcr_ptr == gcr_track_end)
|
||||
gcr_ptr = gcr_track_start;
|
||||
return false;
|
||||
}
|
||||
motor_on = on;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Read one GCR byte from disk
|
||||
* Return state of write protect sensor as VIA port value (PB4)
|
||||
*/
|
||||
|
||||
inline uint8 Job1541::ReadGCRByte(void)
|
||||
{
|
||||
extern void floppy_soundfx(u8 type);
|
||||
floppy_soundfx(0); // Play floppy SFX if needed
|
||||
|
||||
uint8 byte = *gcr_ptr++; // Rotate disk
|
||||
if (gcr_ptr == gcr_track_end)
|
||||
gcr_ptr = gcr_track_start;
|
||||
return byte;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Return state of write protect sensor
|
||||
*/
|
||||
|
||||
inline uint8 Job1541::WPState(void)
|
||||
inline uint8_t Job1541::WPState()
|
||||
{
|
||||
if (disk_changed) { // Disk change -> WP sensor strobe
|
||||
disk_changed = false;
|
||||
return write_protected ? 0x10 : 0;
|
||||
} else
|
||||
} else {
|
||||
return write_protected ? 0 : 0x10;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -188,6 +188,7 @@ void C64::Reset(void)
|
||||
InitMemory();
|
||||
TheCPU->AsyncReset();
|
||||
TheCPU1541->AsyncReset();
|
||||
TheJob1541->Reset();
|
||||
TheSID->Reset();
|
||||
TheCIA1->Reset();
|
||||
TheCIA2->Reset();
|
||||
|
@ -114,6 +114,7 @@ MOS6502_1541::MOS6502_1541(C64 *c64, Job1541 *job, C64Display *disp, uint8 *Ram,
|
||||
v_flag = d_flag = c_flag = false;
|
||||
i_flag = true;
|
||||
|
||||
cycle_counter = 0;
|
||||
borrowed_cycles = 0;
|
||||
|
||||
rom = Rom - 0xC000; // So we don't have to mask the ROM when reading
|
||||
@ -192,13 +193,13 @@ inline uint8 MOS6502_1541::read_byte_io(uint16 adr)
|
||||
else if ((adr & 0xfc00) == 0x1c00) // VIA 2
|
||||
switch (adr & 0xf) {
|
||||
case 0:
|
||||
if (the_job->SyncFound())
|
||||
if (the_job->SyncFound(cycle_counter))
|
||||
return (via2_prb & 0x7f) | the_job->WPState();
|
||||
else
|
||||
return (via2_prb | 0x80) | the_job->WPState();
|
||||
case 1:
|
||||
case 15:
|
||||
return the_job->ReadGCRByte();
|
||||
return the_job->ReadGCRByte(cycle_counter);
|
||||
case 2:
|
||||
return via2_ddrb;
|
||||
case 3:
|
||||
@ -348,9 +349,9 @@ void MOS6502_1541::write_byte_io(uint16 adr, uint8 byte)
|
||||
if ((via2_prb ^ byte) & 3) // Bits 0/1: Stepper motor
|
||||
{
|
||||
if ((via2_prb & 3) == ((byte+1) & 3))
|
||||
the_job->MoveHeadOut();
|
||||
the_job->MoveHeadOut(cycle_counter);
|
||||
else if ((via2_prb & 3) == ((byte-1) & 3))
|
||||
the_job->MoveHeadIn();
|
||||
the_job->MoveHeadIn(cycle_counter);
|
||||
}
|
||||
via2_prb = byte;
|
||||
break;
|
||||
@ -571,6 +572,8 @@ void MOS6502_1541::GetState(MOS6502State *s)
|
||||
s->via2_sr = via2_sr;
|
||||
s->via2_acr = via2_acr; s->via2_pcr = via2_pcr;
|
||||
s->via2_ifr = via2_ifr; s->via2_ier = via2_ier;
|
||||
|
||||
s->cycle_counter = cycle_counter;
|
||||
}
|
||||
|
||||
|
||||
@ -614,6 +617,8 @@ void MOS6502_1541::SetState(MOS6502State *s)
|
||||
via2_sr = s->via2_sr;
|
||||
via2_acr = s->via2_acr; via2_pcr = s->via2_pcr;
|
||||
via2_ifr = s->via2_ifr; via2_ier = s->via2_ier;
|
||||
|
||||
cycle_counter = s->cycle_counter;
|
||||
}
|
||||
|
||||
|
||||
@ -637,6 +642,9 @@ void MOS6502_1541::Reset(void)
|
||||
|
||||
// Clear all interrupt lines
|
||||
interrupt.intr_any = 0;
|
||||
|
||||
cycle_counter = 0;
|
||||
borrowed_cycles = 0;
|
||||
|
||||
// Read reset vector
|
||||
jump(read_word(0xfffc));
|
||||
@ -683,7 +691,7 @@ void MOS6502_1541::illegal_op(uint8 op, uint16 at)
|
||||
// Push processor flags onto the stack
|
||||
#define push_flags(b_flag) \
|
||||
tmp = 0x20 | (n_flag & 0x80); \
|
||||
if (((via2_pcr & 0x0e) == 0x0e) && the_job->ByteReady()) v_flag = true; \
|
||||
if (((via2_pcr & 0x0e) == 0x0e) && the_job->ByteReady(cycle_counter)) v_flag = true; \
|
||||
if (v_flag) tmp |= 0x40; \
|
||||
if (b_flag) tmp |= 0x10; \
|
||||
if (d_flag) tmp |= 0x08; \
|
||||
|
@ -79,6 +79,7 @@ public:
|
||||
void trigger_via1_irq(void);
|
||||
void trigger_via2_irq(void);
|
||||
bool InterruptEnabled(void);
|
||||
uint32_t CycleCounter() const { return cycle_counter; }
|
||||
|
||||
MOS6526_2 *TheCIA2; // Pointer to C64 CIA 2
|
||||
|
||||
@ -120,6 +121,7 @@ private:
|
||||
uint8 a, x, y, sp;
|
||||
uint16_t pc;
|
||||
|
||||
uint32 cycle_counter;
|
||||
int borrowed_cycles; // Borrowed cycles from next line
|
||||
|
||||
uint8 via1_pra; // PRA of VIA 1
|
||||
@ -188,6 +190,8 @@ struct MOS6502State {
|
||||
uint8 via2_pcr;
|
||||
uint8 via2_ifr;
|
||||
uint8 via2_ier;
|
||||
|
||||
uint32 cycle_counter;
|
||||
};
|
||||
|
||||
|
||||
|
@ -155,6 +155,7 @@
|
||||
|
||||
while ((cycles_left -= last_cycles) >= 0)
|
||||
{
|
||||
cycle_counter += last_cycles; // Needed for GCR timing
|
||||
// If we are 1541CPU, we want to alternate running instructions with the main CPU ...
|
||||
while (cpu_cycles > cycles_left) cpu_cycles -= localCPU->EmulateLine(0);
|
||||
#endif
|
||||
@ -844,7 +845,7 @@
|
||||
Branch(v_flag);
|
||||
#else
|
||||
Branch((via2_pcr & 0x0e) == 0x0e ? 1 : v_flag); // GCR byte ready flag
|
||||
if ((via2_pcr & 0x0e) == 0x0e && the_job->ByteReady()) { // CA2 high output and byte ready
|
||||
if ((via2_pcr & 0x0e) == 0x0e && the_job->ByteReady(cycle_counter)) { // CA2 high output and byte ready
|
||||
v_flag = true;
|
||||
}
|
||||
Branch(v_flag);
|
||||
@ -854,7 +855,7 @@
|
||||
#ifndef IS_CPU_1541
|
||||
Branch(!v_flag);
|
||||
#else
|
||||
if ((via2_pcr & 0x0e) == 0x0e && the_job->ByteReady()) { // CA2 high output and byte ready
|
||||
if ((via2_pcr & 0x0e) == 0x0e && the_job->ByteReady(cycle_counter)) { // CA2 high output and byte ready
|
||||
v_flag = true;
|
||||
}
|
||||
Branch(!v_flag);
|
||||
|
@ -120,6 +120,7 @@
|
||||
/* Define if your <sys/time.h> declares `struct tm'. */
|
||||
#undef TM_IN_SYS_TIME
|
||||
|
||||
#if 0
|
||||
// Use iprintf/iscanf on NDS to save ~50 KB
|
||||
#define sscanf siscanf
|
||||
#define printf iprintf
|
||||
@ -127,3 +128,4 @@
|
||||
#define sprintf siprintf
|
||||
#define snprintf sniprintf
|
||||
#define vsnprintf vsniprintf
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user