@@ -46,13 +46,14 @@ Swim3Ctrl::Swim3Ctrl()
46
46
47
47
// Attach virtual Superdrive to the internal drive connector
48
48
// TODO: make SWIM3/drive wiring user selectable
49
- this ->int_drive = std::unique_ptr<MacSuperdrive::MacSuperDrive>
49
+ this ->drive_1 = std::unique_ptr<MacSuperdrive::MacSuperDrive>
50
50
(new MacSuperdrive::MacSuperDrive (" Superdrive1" ));
51
51
}
52
52
53
53
void Swim3Ctrl::reset ()
54
54
{
55
55
this ->setup_reg = 0 ;
56
+ this ->selected_drive = nullptr ;
56
57
this ->mode_reg = 0 ;
57
58
this ->int_reg = 0 ;
58
59
this ->int_flags = 0 ;
@@ -94,7 +95,7 @@ int Swim3Ctrl::device_postinit()
94
95
std::string fd_image_path = GET_STR_PROP (" fdd_img" );
95
96
int fd_write_prot = GET_BIN_PROP (" fdd_wr_prot" );
96
97
if (!fd_image_path.empty ()) {
97
- this ->int_drive ->insert_disk (fd_image_path, fd_write_prot);
98
+ this ->drive_1 ->insert_disk (fd_image_path, fd_write_prot);
98
99
}
99
100
100
101
return 0 ;
@@ -116,14 +117,17 @@ uint8_t Swim3Ctrl::read(uint8_t reg_offset)
116
117
case Swim3Reg::Setup:
117
118
return this ->setup_reg ;
118
119
case Swim3Reg::Handshake_Mode1:
119
- if (this ->mode_reg & SWIM3_DRIVE_1 ) { // internal drive?
120
+ if (this ->selected_drive ) {
120
121
status_addr = ((this ->mode_reg & SWIM3_HEAD_SELECT) >> 2 ) | (this ->phase_lines & 7 );
121
- rddata_val = this ->int_drive ->status (status_addr) & 1 ;
122
+ rddata_val = this ->selected_drive ->status (status_addr) & 1 ;
122
123
123
124
// transfer rddata_val to both bit 2 (RDDATA) and bit 3 (SENSE)
124
125
// because those signals seem to be historically wired together
125
126
return (rddata_val << 2 ) | (rddata_val << 3 );
126
127
}
128
+ else {
129
+ LOG_F (ERROR, " SWIM3: read Handshake_Mode1; no drive selected yet" );
130
+ }
127
131
return 0xC ; // report both RdData & Sense high
128
132
case Swim3Reg::Interrupt_Flags:
129
133
old_int_flags = this ->int_flags ;
@@ -164,41 +168,31 @@ void Swim3Ctrl::write(uint8_t reg_offset, uint8_t value)
164
168
case Swim3Reg::Phase:
165
169
this ->phase_lines = value & 0xF ;
166
170
if (this ->phase_lines & 8 ) { // CA3 aka LSTRB high -> sending a command to the drive
167
- if (this ->mode_reg & SWIM3_DRIVE_1) { // if internal drive is selected
168
- this ->int_drive ->command (
169
- ((this ->mode_reg & SWIM3_HEAD_SELECT) >> 3 ) | (this ->phase_lines & 3 ),
170
- (value >> 2 ) & 1
171
- );
172
- }
173
- } else if (this ->phase_lines == 4 && (this ->mode_reg & SWIM3_DRIVE_1)) {
171
+ uint8_t command_addr = ((this ->mode_reg & SWIM3_HEAD_SELECT) >> 3 ) | (this ->phase_lines & 3 );
172
+ uint8_t val = (value >> 2 ) & 1 ;
173
+ if (this ->selected_drive ) {
174
+ this ->selected_drive ->command (command_addr, val);
175
+ } else
176
+ LOG_F (ERROR, " SWIM3: command %-17s addr=0x%X, value=%d; no drive selected yet" ,
177
+ MacSuperdrive::get_command_name (command_addr).c_str (), command_addr, val);
178
+ } else if (this ->phase_lines == 4 ) {
179
+ // Select_Head_0 or Select_Head_1
174
180
status_addr = ((this ->mode_reg & SWIM3_HEAD_SELECT) >> 2 ) | (this ->phase_lines & 7 );
175
- this ->rd_line = this ->int_drive ->status (status_addr) & 1 ;
181
+ if (this ->selected_drive )
182
+ this ->rd_line = this ->selected_drive ->status (status_addr) & 1 ;
183
+ else
184
+ LOG_F (ERROR, " SWIM3: status %-13s 0x%X; no drive selected yet" ,
185
+ MacSuperdrive::get_status_name (status_addr).c_str (), status_addr);
176
186
}
177
187
break ;
178
188
case Swim3Reg::Setup:
179
189
this ->setup_reg = value;
180
190
break ;
181
191
case Swim3Reg::Status_Mode0:
182
- // ones in value clear the corresponding bits in the mode register
183
- if ((this ->mode_reg & value) & (SWIM3_GO | SWIM3_GO_STEP)) {
184
- if (value & SWIM3_GO_STEP) {
185
- stop_stepping ();
186
- } else {
187
- stop_disk_access ();
188
- }
189
- }
190
- this ->mode_reg &= ~value;
192
+ this ->mode_change (this ->mode_reg & ~value);
191
193
break ;
192
194
case Swim3Reg::Handshake_Mode1:
193
- // ones in value set the corresponding bits in the mode register
194
- if ((this ->mode_reg ^ value) & (SWIM3_GO | SWIM3_GO_STEP)) {
195
- if (value & SWIM3_GO_STEP) {
196
- start_stepping ();
197
- } else {
198
- start_disk_access ();
199
- }
200
- }
201
- this ->mode_reg |= value;
195
+ this ->mode_change (this ->mode_reg | value);
202
196
break ;
203
197
case Swim3Reg::Step:
204
198
this ->step_count = value;
@@ -235,7 +229,10 @@ void Swim3Ctrl::do_step()
235
229
{
236
230
if (this ->mode_reg & SWIM3_GO_STEP && this ->step_count ) { // are we still stepping?
237
231
// instruct the drive to perform single step in current direction
238
- this ->int_drive ->command (MacSuperdrive::CommandAddr::Do_Step, 0 );
232
+ if (this ->selected_drive )
233
+ this ->selected_drive ->command (MacSuperdrive::CommandAddr::Do_Step, 0 );
234
+ else
235
+ LOG_F (ERROR, " SWIM3: do_step; no drive selected yet" );
239
236
if (--this ->step_count == 0 ) {
240
237
if (this ->step_timer_id ) {
241
238
this ->stop_stepping ();
@@ -317,8 +314,13 @@ void Swim3Ctrl::start_disk_access()
317
314
318
315
this ->target_sect = this ->first_sec ;
319
316
317
+ if (!this ->selected_drive ) {
318
+ LOG_F (ERROR, " SWIM3: start_disk_access; no drive selected yet" );
319
+ return ;
320
+ }
321
+
320
322
this ->access_timer_id = TimerManager::get_instance ()->add_oneshot_timer (
321
- this ->int_drive ->sync_to_disk (),
323
+ this ->selected_drive ->sync_to_disk (),
322
324
[this ]() {
323
325
this ->cur_state = SWIM3_ADDR_MARK_SEARCH;
324
326
this ->disk_access ();
@@ -331,9 +333,14 @@ void Swim3Ctrl::disk_access()
331
333
MacSuperdrive::SectorHdr hdr;
332
334
uint64_t delay;
333
335
336
+ if (!this ->selected_drive ) {
337
+ LOG_F (ERROR, " SWIM3: disk access; no drive selected yet" );
338
+ return ;
339
+ }
340
+
334
341
switch (this ->cur_state ) {
335
342
case SWIM3_ADDR_MARK_SEARCH:
336
- hdr = this ->int_drive ->current_sector_header ();
343
+ hdr = this ->selected_drive ->current_sector_header ();
337
344
// update the corresponding SWIM3 registers
338
345
this ->cur_track = ((hdr.side & 1 ) << 7 ) | (hdr.track & 0x7F );
339
346
this ->cur_sector = 0x80 /* CRC/checksum valid */ | (hdr.sector & 0x7F );
@@ -344,16 +351,16 @@ void Swim3Ctrl::disk_access()
344
351
if ((this ->cur_sector & 0x7F ) == this ->target_sect ) {
345
352
// sector matches -> transfer its data
346
353
this ->cur_state = SWIM3_DATA_XFER;
347
- delay = this ->int_drive ->sector_data_delay ();
354
+ delay = this ->selected_drive ->sector_data_delay ();
348
355
} else {
349
356
// move to next address mark
350
357
this ->cur_state = SWIM3_ADDR_MARK_SEARCH;
351
- delay = this ->int_drive ->next_sector_delay ();
358
+ delay = this ->selected_drive ->next_sector_delay ();
352
359
}
353
360
break ;
354
361
case SWIM3_DATA_XFER:
355
362
// transfer sector data over DMA
356
- this ->dma_ch ->push_data (this ->int_drive ->get_sector_data_ptr (this ->cur_sector & 0x7F ), 512 );
363
+ this ->dma_ch ->push_data (this ->selected_drive ->get_sector_data_ptr (this ->cur_sector & 0x7F ), 512 );
357
364
if (--this ->xfer_cnt == 0 ) {
358
365
this ->stop_disk_access ();
359
366
// generate sector_done interrupt
@@ -362,7 +369,7 @@ void Swim3Ctrl::disk_access()
362
369
return ;
363
370
}
364
371
this ->cur_state = SWIM3_ADDR_MARK_SEARCH;
365
- delay = this ->int_drive ->next_addr_mark_delay (&this ->target_sect );
372
+ delay = this ->selected_drive ->next_addr_mark_delay (&this ->target_sect );
366
373
break ;
367
374
default :
368
375
LOG_F (ERROR, " SWIM3: unknown disk access phase 0x%X" , this ->cur_state );
@@ -424,6 +431,57 @@ uint8_t Swim3Ctrl::calc_timer_val()
424
431
}
425
432
}
426
433
434
+ void Swim3Ctrl::mode_change (uint8_t new_mode)
435
+ {
436
+ uint8_t changed_bits = this ->mode_reg ^ new_mode;
437
+
438
+ if (changed_bits & (SWIM3_DRIVE_1 | SWIM3_DRIVE_2)) {
439
+ this ->selected_drive = nullptr ;
440
+ this ->cur_track = 0xFF ;
441
+ this ->cur_sector = 0x7F ;
442
+
443
+ switch (new_mode & (SWIM3_DRIVE_1 | SWIM3_DRIVE_2)) {
444
+ case 0 :
445
+ break ;
446
+ case SWIM3_DRIVE_1:
447
+ if (this ->drive_1 )
448
+ this ->selected_drive = this ->drive_1 .get ();
449
+ break ;
450
+ case SWIM3_DRIVE_2:
451
+ break ;
452
+ case SWIM3_DRIVE_1 | SWIM3_DRIVE_2:
453
+ LOG_F (ERROR, " SWIM3: both drives selected, selecting drive 1" );
454
+ if (this ->drive_1 )
455
+ this ->selected_drive = this ->drive_1 .get ();
456
+ break ;
457
+ }
458
+ if (this ->xfer_cnt ) {
459
+ LOG_F (ERROR, " SWIM3: selecting drive while xfer still in progress" );
460
+ }
461
+ }
462
+
463
+ if (changed_bits & SWIM3_GO_STEP) {
464
+ if (new_mode & SWIM3_GO_STEP)
465
+ start_stepping ();
466
+ else
467
+ stop_stepping ();
468
+ if (changed_bits & SWIM3_GO) {
469
+ LOG_F (ERROR, " SWIM3: attempt to change GO and GO_STEP, ignoring GO" );
470
+ }
471
+ }
472
+ else
473
+ if (changed_bits & SWIM3_GO) {
474
+ if (new_mode & SWIM3_GO)
475
+ start_disk_access ();
476
+ else {
477
+ stop_disk_access ();
478
+ this ->cur_sector &= ~0x80 ;
479
+ }
480
+ }
481
+
482
+ this ->mode_reg = new_mode;
483
+ }
484
+
427
485
// floppy disk formats properties for the cases
428
486
// where disk format needs to be specified manually
429
487
static const std::vector<std::string> FloppyFormats = {
0 commit comments