Skip to content

Commit f9d78ad

Browse files
committed
imagedev/floppy: replace write_flux with streaming write interface
Replace the batched write_flux(start, end, count, transitions) interface with an event-driven streaming interface: write_start, write_flux_change, write_end and write_flush. This makes the floppy device own the write-gate lifecycle and the transition buffer rather than requiring every controller to maintain its own. write_start activates the write gate and snapshots the current track identity. write_flux_change appends a single transition timestamp to an internal buffer. write_end deactivates the write gate and flushes buffered transitions to the track. write_flush commits transitions earlier than a given time without ending the write, retaining later transitions for a subsequent flush or end. All controllers now call write_flux_change directly as transitions are generated rather than maintaining local buffers. Controllers that use speculative execution with checkpoint/rollback (wd_fdc, 64h156, c2040fdc, paulafdc) previously needed local 32-entry write buffers to avoid duplicating transitions after rollback. The floppy device now handles this transparently: write_flux_change detects out-of-sequence timestamps and discards stale entries, so replayed transitions replace their predecessors cleanly. The hp9895 controller supports simultaneous read and write for write-with- verify loopback. It previously read back transitions from the fdc_pll write buffer. Since that buffer no longer exists, hp9895 now maintains its own loopback buffer that captures transition times as they are generated and serves them back through get_next_transition when the write gate is active. Add a floppy parameter to fdc_pll_t::start_writing so that write_start is called on the floppy device at the correct point in the PLL lifecycle. All controllers that use fdc_pll_t (wd_fdc, swim3, mc6843, upd765, i8271, hdc92x4, hp9885, isbc202, c8050fdc, victor9k_fdc, hp9895) now pass their floppy pointer through start_writing. The same change applies to the wd_fdc digital PLL and the paulafdc PLL. The old fdc_pll_t::write_next_bit had a lazy-init path that implicitly started writing on the first bit if start_writing had not been called. Three controllers (upd765, i8271, hdc92x4) relied on this for sector writes -- only their format-track paths called start_writing explicitly. That lazy init is removed; the sector-write paths now call start_writing at the point where they transition from reading gap data to writing sector data. The batching performance benefit from the earlier write_flux optimization is preserved: transitions accumulate in the floppy device and are written to the track representation only at flush points, avoiding repeated track vector edits during sequential writes such as formatting.
1 parent 248fffb commit f9d78ad

38 files changed

Lines changed: 326 additions & 504 deletions

docs/source/techspecs/floppy.rst

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,29 @@ A number of methods are provided to simplify writing the converter classes.
409409

410410
The read/write interface is designed to work asynchronously, e.g. somewhat independently of the current time.
411411

412+
**get_next_transition(from_when)**
413+
414+
Returns the attotime of the next flux transition after *from_when*. Returns ``attotime::never`` when there is no upcoming transition.
415+
416+
**write_start(when)**
417+
418+
Activates the write gate at time *when*. No effect when no disk is loaded, the motor is off or the media is write-protected.
419+
420+
**write_flux_change(when)**
421+
422+
Records a single flux transition at time *when*. Buffered until a flush or end. If *when* is not after the most recently buffered transition, later entries are discarded first; this handles speculative re-execution transparently.
423+
424+
**write_end(when)**
425+
426+
Deactivates the write gate. Buffered transitions earlier than *when* are flushed to the track with the write span ending at *when*.
427+
428+
**write_flush(when)**
429+
430+
Commits buffered transitions earlier than *when* without deactivating the write gate. Transitions at or after *when* are retained for a subsequent flush or end.
431+
432+
**set_write_splice(when)**
433+
434+
Sets the track write-splice position to the angular position corresponding to *when*.
412435

413436

414437
.. [1] Cylinder is a hard-drive term somewhat improperly used for floppies. It comes from the fact that hard-drives are similar to floppies but include a series of stacked disks with a read/write head on each. The heads are physically linked and all point to the same circle on every disk at a given time, making the accessed area look like a cylinder. Hence the name.

src/devices/bus/a2bus/agat_fdc.cpp

Lines changed: 20 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -138,9 +138,6 @@ class a2bus_agat_fdc_device:
138138

139139
uint8_t last_6502_write;
140140
bool mode_write, write_desync;
141-
attotime write_start_time;
142-
attotime write_buffer[32];
143-
int write_position;
144141

145142
int m_seektime;
146143
int m_waittime;
@@ -238,8 +235,6 @@ void a2bus_agat_fdc_device::device_start()
238235
save_item(NAME(last_6502_write));
239236
save_item(NAME(mode_write));
240237
save_item(NAME(write_desync));
241-
save_item(NAME(write_start_time));
242-
save_item(NAME(write_position));
243238
}
244239

245240
void a2bus_agat_fdc_device::device_reset()
@@ -249,8 +244,6 @@ void a2bus_agat_fdc_device::device_reset()
249244
mode_write = false;
250245
write_desync = false;
251246
last_6502_write = 0x00;
252-
write_start_time = attotime::never;
253-
write_position = 0;
254247
}
255248

256249
void a2bus_agat_fdc_device::reset_from_bus()
@@ -400,15 +393,6 @@ TIMER_CALLBACK_MEMBER(a2bus_agat_fdc_device::lss_sync)
400393
m_d15->pc2_w(0);
401394
if (mode_write)
402395
{
403-
if(write_position) {
404-
LOGWRITE("writing %d transitions\n", write_position);
405-
attotime now = cycles_to_time(cycles);
406-
if(floppy)
407-
floppy->write_flux(write_start_time, now, write_position, write_buffer);
408-
write_start_time = now;
409-
write_position = 0;
410-
}
411-
412396
if (write_desync)
413397
{
414398
live_write_raw(0x2d55);
@@ -430,8 +414,10 @@ TIMER_CALLBACK_MEMBER(a2bus_agat_fdc_device::lss_sync)
430414
cur_live.shift_reg <<= 2;
431415
if (b)
432416
{
433-
if (b & 2) write_buffer[write_position++] = cycles_to_time(cycles);
434-
if (b & 1) write_buffer[write_position++] = cycles_to_time(cycles + 8);
417+
if (floppy && (b & 2))
418+
floppy->write_flux_change(cycles_to_time(cycles));
419+
if (floppy && (b & 1))
420+
floppy->write_flux_change(cycles_to_time(cycles + 8));
435421
}
436422
}
437423
}
@@ -588,6 +574,8 @@ uint8_t a2bus_agat_fdc_device::d14_i_b()
588574
void a2bus_agat_fdc_device::d14_o_c(uint8_t data)
589575
{
590576
const bool new_write = BIT(data, 6) & BIT(data, 7);
577+
const attotime now = machine().time();
578+
floppy_image_device *const old_floppy = floppy;
591579
m_unit = BIT(data, 3);
592580

593581
switch (m_unit)
@@ -600,6 +588,13 @@ void a2bus_agat_fdc_device::d14_o_c(uint8_t data)
600588
break;
601589
}
602590

591+
if (mode_write && old_floppy != floppy)
592+
{
593+
if (old_floppy)
594+
old_floppy->write_end(now);
595+
mode_write = false;
596+
}
597+
603598
if (floppy)
604599
{
605600
floppy->dir_w(!BIT(data, 2));
@@ -617,20 +612,20 @@ void a2bus_agat_fdc_device::d14_o_c(uint8_t data)
617612
LOGWRITE("n_w %d (%d)\n", new_write, mode_write);
618613
address |= 0x100;
619614
if(!mode_write) {
620-
write_start_time = machine().time();
621-
write_position = 0;
622-
if(floppy)
623-
floppy->set_write_splice(write_start_time);
615+
if(floppy) {
616+
floppy->set_write_splice(now);
617+
floppy->write_start(now);
618+
}
624619
mode_write = true;
625620
}
626621
}
627622
else
628623
{
629624
address &= 0xff;
630625
if(mode_write) {
631-
LOGWRITE("write->read: writing %d transitions\n", write_position);
632-
if(floppy && write_position)
633-
floppy->write_flux(write_start_time, machine().time(), write_position, write_buffer);
626+
LOGWRITE("write->read\n");
627+
if(floppy)
628+
floppy->write_end(now);
634629
mode_write = false;
635630
}
636631
}

src/devices/bus/hp9845_io/hp9885.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -430,7 +430,7 @@ TIMER_CALLBACK_MEMBER(hp9885_device::bit_byte_tick)
430430
} else {
431431
m_word_cnt = 130;
432432
set_state(FSM_WR_DATA);
433-
m_pll.start_writing(m_pll.ctime);
433+
m_pll.start_writing(m_pll.ctime, m_drive);
434434
m_had_transition = false;
435435
wr_word(m_input);
436436
set_ibf(false);
@@ -624,7 +624,7 @@ void hp9885_device::floppy_index_cb(floppy_image_device *floppy , int state)
624624
// See bit_byte_tick function
625625
m_word_cnt = 167;
626626
m_pll.set_clock(attotime::from_usec(HALF_CELL_US));
627-
m_pll.start_writing(machine().time());
627+
m_pll.start_writing(machine().time(), m_drive);
628628
m_pll.read_reset(machine().time());
629629
m_had_transition = false;
630630
// Start by writing 1st sync word

src/devices/bus/ieee488/c2040fdc.cpp

Lines changed: 7 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,6 @@ c2040_fdc_device::c2040_fdc_device(const machine_config &mconfig, const char *ta
9393
cur_live.tm = attotime::never;
9494
cur_live.state = IDLE;
9595
cur_live.next_state = -1;
96-
cur_live.write_position = 0;
97-
cur_live.write_start_time = attotime::never;
9896
cur_live.drv_sel = m_drv_sel;
9997
}
10098

@@ -194,29 +192,24 @@ void c2040_fdc_device::rollback()
194192

195193
void c2040_fdc_device::start_writing(const attotime &tm)
196194
{
197-
cur_live.write_start_time = tm;
198-
cur_live.write_position = 0;
195+
if(get_floppy())
196+
get_floppy()->write_start(tm);
199197
}
200198

201199
void c2040_fdc_device::stop_writing(const attotime &tm)
202200
{
203-
commit(tm);
204-
cur_live.write_start_time = attotime::never;
201+
if(get_floppy())
202+
get_floppy()->write_end(tm);
205203
}
206204

207205
bool c2040_fdc_device::write_next_bit(bool bit, const attotime &limit)
208206
{
209-
if(cur_live.write_start_time.is_never()) {
210-
cur_live.write_start_time = cur_live.tm;
211-
cur_live.write_position = 0;
212-
}
213-
214207
attotime etime = cur_live.tm + m_period;
215208
if(etime > limit)
216209
return true;
217210

218-
if(bit && cur_live.write_position < std::size(cur_live.write_buffer))
219-
cur_live.write_buffer[cur_live.write_position++] = cur_live.tm - m_period;
211+
if(bit && get_floppy())
212+
get_floppy()->write_flux_change(cur_live.tm - m_period);
220213

221214
if (LOG) logerror("%s write bit %u (%u)\n", cur_live.tm.as_string(), cur_live.bit_counter, bit);
222215

@@ -225,16 +218,8 @@ bool c2040_fdc_device::write_next_bit(bool bit, const attotime &limit)
225218

226219
void c2040_fdc_device::commit(const attotime &tm)
227220
{
228-
if(cur_live.write_start_time.is_never() || tm == cur_live.write_start_time || !cur_live.write_position)
229-
return;
230-
231-
if (LOG) logerror("%s committing %u transitions since %s\n", tm.as_string(), cur_live.write_position, cur_live.write_start_time.as_string());
232-
233221
if(get_floppy())
234-
get_floppy()->write_flux(cur_live.write_start_time, tm, cur_live.write_position, cur_live.write_buffer);
235-
236-
cur_live.write_start_time = tm;
237-
cur_live.write_position = 0;
222+
get_floppy()->write_flush(tm);
238223
}
239224

240225
void c2040_fdc_device::live_delay(int state)
@@ -281,8 +266,6 @@ void c2040_fdc_device::live_abort()
281266
cur_live.tm = attotime::never;
282267
cur_live.state = IDLE;
283268
cur_live.next_state = -1;
284-
cur_live.write_position = 0;
285-
cur_live.write_start_time = attotime::never;
286269

287270
cur_live.ready = 1;
288271
cur_live.sync = 1;

src/devices/bus/ieee488/c2040fdc.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,9 +81,6 @@ class c2040_fdc_device : public device_t
8181

8282
uint8_t pi;
8383
uint16_t shift_reg_write;
84-
attotime write_start_time;
85-
attotime write_buffer[32];
86-
int write_position;
8784
};
8885

8986
// device_t implementation

src/devices/bus/ieee488/c8050fdc.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -252,9 +252,9 @@ void c8050_fdc_device::pll_reset(const attotime &when)
252252
cur_pll.set_clock(attotime::from_hz(clock() / (16 - m_ds)));
253253
}
254254

255-
void c8050_fdc_device::pll_start_writing(const attotime &tm)
255+
void c8050_fdc_device::pll_start_writing(const attotime &tm, floppy_image_device *floppy)
256256
{
257-
cur_pll.start_writing(tm);
257+
cur_pll.start_writing(tm, floppy);
258258
pll_reset(cur_live.tm);
259259
}
260260

@@ -558,7 +558,7 @@ void c8050_fdc_device::rw_sel_w(int state)
558558
if (m_rw_sel) {
559559
pll_stop_writing(get_floppy(), cur_live.tm);
560560
} else {
561-
pll_start_writing(cur_live.tm);
561+
pll_start_writing(cur_live.tm, get_floppy());
562562
}
563563
live_run();
564564
}

src/devices/bus/ieee488/c8050fdc.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ class c8050_fdc_device : public device_t
101101
void checkpoint();
102102
void rollback();
103103
void pll_reset(const attotime &when);
104-
void pll_start_writing(const attotime &tm);
104+
void pll_start_writing(const attotime &tm, floppy_image_device *floppy);
105105
void pll_commit(floppy_image_device *floppy, const attotime &tm);
106106
void pll_stop_writing(floppy_image_device *floppy, const attotime &tm);
107107
int pll_get_next_bit(attotime &tm, floppy_image_device *floppy, const attotime &limit);

src/devices/bus/ieee488/hp9895.cpp

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,10 @@ class hp9895_device : public device_t,
255255

256256
// PLL
257257
fdc_pll_t m_pll;
258+
259+
// Write loopback buffer for read-during-write verification
260+
attotime m_loopback_buf[32];
261+
int m_loopback_pos;
258262
};
259263

260264
hp9895_device::hp9895_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
@@ -315,6 +319,8 @@ void hp9895_device::device_start()
315319
save_item(NAME(m_sync_cnt));
316320
save_item(NAME(m_hiden));
317321
save_item(NAME(m_mgnena));
322+
save_item(NAME(m_loopback_buf));
323+
save_item(NAME(m_loopback_pos));
318324

319325
m_timeout_timer = timer_alloc(FUNC(hp9895_device::timeout_timer_tick), this);
320326
m_byte_timer = timer_alloc(FUNC(hp9895_device::byte_timer_tick), this);
@@ -349,6 +355,7 @@ void hp9895_device::device_reset()
349355
m_sync_cnt = 0;
350356
m_hiden = false;
351357
m_mgnena = false;
358+
m_loopback_pos = 0;
352359
m_timeout_timer->reset();
353360
m_byte_timer->reset();
354361
m_half_bit_timer->reset();
@@ -390,6 +397,7 @@ TIMER_CALLBACK_MEMBER(hp9895_device::byte_timer_tick)
390397
// Writing
391398
m_pll.commit(get_write_device() , sdok_time);
392399
m_pll.ctime = sdok_time;
400+
m_loopback_pos = 0;
393401

394402
// Check for AMDT when in loopback mode
395403
if (!m_lckup && !m_amdt && BIT(m_cntl_reg , REG_CNTL_READON_BIT)) {
@@ -687,7 +695,8 @@ void hp9895_device::cntl_w(uint8_t data)
687695
if (!old_writon && new_writon) {
688696
// Writing enabled
689697
LOGMASKED(LOG_LEVEL0, "Start writing\n");
690-
m_pll.start_writing(machine().time());
698+
m_pll.start_writing(machine().time(), get_write_device());
699+
m_loopback_pos = 0;
691700
m_wr_context = 0;
692701
m_had_transition = false;
693702
} else if (old_writon && !new_writon) {
@@ -896,10 +905,9 @@ void hp9895_device::get_next_transition(const attotime& from_when , attotime& ed
896905
edge = attotime::never;
897906

898907
if (BIT(m_cntl_reg , REG_CNTL_WRITON_BIT)) {
899-
// Loop back write transitions into reading data path
900-
for (int idx = 0; idx < m_pll.write_position; idx++) {
901-
if (m_pll.write_buffer[ idx ] >= from_when) {
902-
edge = m_pll.write_buffer[ idx ];
908+
for (int idx = 0; idx < m_loopback_pos; idx++) {
909+
if (m_loopback_buf[idx] >= from_when) {
910+
edge = m_loopback_buf[idx];
903911
break;
904912
}
905913
}
@@ -946,9 +954,14 @@ void hp9895_device::write_bit(bool data_bit , bool clock_bit)
946954
// else... IBM mode, nothing to do
947955

948956
attotime dummy;
949-
950-
m_pll.write_next_bit(clock_bit , dummy , nullptr , attotime::never);
951-
m_pll.write_next_bit(data_bit , dummy , nullptr , attotime::never);
957+
floppy_image_device *wd = get_write_device();
958+
959+
if(clock_bit && m_loopback_pos < int(std::size(m_loopback_buf)))
960+
m_loopback_buf[m_loopback_pos++] = m_pll.ctime + m_pll.period/2;
961+
m_pll.write_next_bit(clock_bit , dummy , wd , attotime::never);
962+
if(data_bit && m_loopback_pos < int(std::size(m_loopback_buf)))
963+
m_loopback_buf[m_loopback_pos++] = m_pll.ctime + m_pll.period/2;
964+
m_pll.write_next_bit(data_bit , dummy , wd , attotime::never);
952965
}
953966

954967
ROM_START(hp9895)

src/devices/bus/multibus/isbc202.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1074,7 +1074,7 @@ void isbc202_device::set_rd_wr(bool new_rd , bool new_wr)
10741074
// Start writing
10751075
LOG_WR("Start WR\n");
10761076
m_pll.set_clock(attotime::from_usec(HALF_BIT_CELL_US));
1077-
m_pll.start_writing(machine().time());
1077+
m_pll.start_writing(machine().time(), m_current_drive);
10781078
m_pll.ctime = machine().time();
10791079
m_last_data_bit = false;
10801080
m_byte_timer->adjust(attotime::from_usec(HALF_BIT_CELL_US * 14));

0 commit comments

Comments
 (0)