// license:BSD-3-Clause// copyright-holders:Olivier Galibert#include"emu.h"#include"upd765.h"#include"imagedev/floppy.h"#include"debugger.h"#define LOG_WARN (1U << 1) // Show warnings#define LOG_SHIFT (1U << 2) // Shows shift register contents#define LOG_HEADER (1U << 3) // Shows ID fields#define LOG_FORMAT (1U << 4) // Sectors being formatted#define LOG_TCIRQ (1U << 5) // Termination code line / interrupt#define LOG_REGS (1U << 6) // Digital input/output register and data rate select#define LOG_FIFO (1U << 7) // FIFO operations#define LOG_COMMAND (1U << 8) // Commands#define LOG_RW (1U << 9) // Read/write sector or track#define LOG_MATCH (1U << 10) // Sector matching#define LOG_STATE (1U << 11) // State machine#define LOG_LIVE (1U << 12) // Live states#define LOG_DONE (1U << 13) // Command done#define VERBOSE (LOG_GENERAL | LOG_WARN )#include"logmacro.h"#include<iomanip>#include<sstream>#define LOGWARN(...) LOGMASKED(LOG_WARN, __VA_ARGS__)#define LOGSHIFT(...) LOGMASKED(LOG_SHIFT, __VA_ARGS__)#define LOGHEADER(...) LOGMASKED(LOG_HEADER, __VA_ARGS__)#define LOGFORMAT(...) LOGMASKED(LOG_FORMAT, __VA_ARGS__)#define LOGTCIRQ(...) LOGMASKED(LOG_TCIRQ, __VA_ARGS__)#define LOGREGS(...) LOGMASKED(LOG_REGS, __VA_ARGS__)#define LOGFIFO(...) LOGMASKED(LOG_FIFO, __VA_ARGS__)#define LOGCOMMAND(...) LOGMASKED(LOG_COMMAND, __VA_ARGS__)#define LOGRW(...) LOGMASKED(LOG_RW, __VA_ARGS__)#define LOGMATCH(...) LOGMASKED(LOG_MATCH, __VA_ARGS__)#define LOGSTATE(...) LOGMASKED(LOG_STATE, __VA_ARGS__)#define LOGLIVE(...) LOGMASKED(LOG_LIVE, __VA_ARGS__)#define LOGDONE(...) LOGMASKED(LOG_DONE, __VA_ARGS__)DEFINE_DEVICE_TYPE(UPD765A,upd765a_device,"upd765a","NEC uPD765A FDC")DEFINE_DEVICE_TYPE(UPD765B,upd765b_device,"upd765b","NEC uPD765B FDC")DEFINE_DEVICE_TYPE(I8272A,i8272a_device,"i8272a","Intel 8272A FDC")DEFINE_DEVICE_TYPE(UPD72065,upd72065_device,"upd72065","NEC uPD72065 FDC")DEFINE_DEVICE_TYPE(UPD72067,upd72067_device,"upd72067","NEC uPD72067 FDC")DEFINE_DEVICE_TYPE(UPD72069,upd72069_device,"upd72069","NEC uPD72069 FDC")DEFINE_DEVICE_TYPE(I82072,i82072_device,"i82072","Intel 82072 FDC")DEFINE_DEVICE_TYPE(SMC37C78,smc37c78_device,"smc37c78","SMC FDC37C78 FDC")DEFINE_DEVICE_TYPE(N82077AA,n82077aa_device,"n82077aa","Intel N82077AA FDC")DEFINE_DEVICE_TYPE(PC_FDC_SUPERIO,pc_fdc_superio_device,"pc_fdc_superio","Winbond PC FDC Super I/O")DEFINE_DEVICE_TYPE(DP8473,dp8473_device,"dp8473","National Semiconductor DP8473 FDC")DEFINE_DEVICE_TYPE(PC8477A,pc8477a_device,"pc8477a","National Semiconductor PC8477A FDC")DEFINE_DEVICE_TYPE(WD37C65C,wd37c65c_device,"wd37c65c","Western Digital WD37C65C FDC")DEFINE_DEVICE_TYPE(MCS3201,mcs3201_device,"mcs3201","Motorola MCS3201 FDC")DEFINE_DEVICE_TYPE(TC8566AF,tc8566af_device,"tc8566af","Toshiba TC8566AF FDC")voidupd765a_device::map(address_map&map){map(0x0,0x0).r(FUNC(upd765a_device::msr_r));map(0x1,0x1).rw(FUNC(upd765a_device::fifo_r),FUNC(upd765a_device::fifo_w));}voidupd765b_device::map(address_map&map){map(0x0,0x0).r(FUNC(upd765b_device::msr_r));map(0x1,0x1).rw(FUNC(upd765b_device::fifo_r),FUNC(upd765b_device::fifo_w));}voidi8272a_device::map(address_map&map){map(0x0,0x0).r(FUNC(i8272a_device::msr_r));map(0x1,0x1).rw(FUNC(i8272a_device::fifo_r),FUNC(i8272a_device::fifo_w));}voidupd72065_device::map(address_map&map){map(0x0,0x0).rw(FUNC(upd72065_device::msr_r),FUNC(upd72065_device::auxcmd_w));map(0x1,0x1).rw(FUNC(upd72065_device::fifo_r),FUNC(upd72065_device::fifo_w));}voidi82072_device::map(address_map&map){map(0x0,0x0).rw(FUNC(i82072_device::msr_r),FUNC(i82072_device::dsr_w));map(0x1,0x1).rw(FUNC(i82072_device::fifo_r),FUNC(i82072_device::fifo_w));}voidsmc37c78_device::map(address_map&map){map(0x2,0x2).rw(FUNC(smc37c78_device::dor_r),FUNC(smc37c78_device::dor_w));map(0x3,0x3).rw(FUNC(smc37c78_device::tdr_r),FUNC(smc37c78_device::tdr_w));map(0x4,0x4).rw(FUNC(smc37c78_device::msr_r),FUNC(smc37c78_device::dsr_w));map(0x5,0x5).rw(FUNC(smc37c78_device::fifo_r),FUNC(smc37c78_device::fifo_w));map(0x7,0x7).rw(FUNC(smc37c78_device::dir_r),FUNC(smc37c78_device::ccr_w));}voidn82077aa_device::map(address_map&map){if(mode!=mode_t::AT){map(0x0,0x0).r(FUNC(n82077aa_device::sra_r));map(0x1,0x1).r(FUNC(n82077aa_device::srb_r));}map(0x2,0x2).rw(FUNC(n82077aa_device::dor_r),FUNC(n82077aa_device::dor_w));map(0x3,0x3).rw(FUNC(n82077aa_device::tdr_r),FUNC(n82077aa_device::tdr_w));map(0x4,0x4).rw(FUNC(n82077aa_device::msr_r),FUNC(n82077aa_device::dsr_w));map(0x5,0x5).rw(FUNC(n82077aa_device::fifo_r),FUNC(n82077aa_device::fifo_w));map(0x7,0x7).rw(FUNC(n82077aa_device::dir_r),FUNC(n82077aa_device::ccr_w));}voidpc_fdc_superio_device::map(address_map&map){map(0x2,0x2).w(FUNC(pc_fdc_superio_device::dor_w));map(0x3,0x3).w(FUNC(pc_fdc_superio_device::tdr_w));map(0x4,0x4).r(FUNC(pc_fdc_superio_device::msr_r));map(0x5,0x5).rw(FUNC(pc_fdc_superio_device::fifo_r),FUNC(pc_fdc_superio_device::fifo_w));map(0x7,0x7).rw(FUNC(pc_fdc_superio_device::dir_r),FUNC(pc_fdc_superio_device::ccr_w));}voiddp8473_device::map(address_map&map){map(0x2,0x2).w(FUNC(dp8473_device::dor_w));map(0x4,0x4).r(FUNC(dp8473_device::msr_r));map(0x5,0x5).rw(FUNC(dp8473_device::fifo_r),FUNC(dp8473_device::fifo_w));map(0x7,0x7).rw(FUNC(dp8473_device::dir_r),FUNC(dp8473_device::ccr_w));}voidpc8477a_device::map(address_map&map){if(mode!=mode_t::AT){map(0x0,0x0).r(FUNC(pc8477a_device::sra_r));map(0x1,0x1).r(FUNC(pc8477a_device::srb_r));}map(0x2,0x2).rw(FUNC(pc8477a_device::dor_r),FUNC(pc8477a_device::dor_w));map(0x3,0x3).rw(FUNC(pc8477a_device::tdr_r),FUNC(pc8477a_device::tdr_w));map(0x4,0x4).rw(FUNC(pc8477a_device::msr_r),FUNC(pc8477a_device::dsr_w));map(0x5,0x5).rw(FUNC(pc8477a_device::fifo_r),FUNC(pc8477a_device::fifo_w));map(0x7,0x7).rw(FUNC(pc8477a_device::dir_r),FUNC(pc8477a_device::ccr_w));}voidwd37c65c_device::map(address_map&map){// NOTE: this map only covers registers defined by CS.// LDOR and LDCR must be mapped separately, since their addresses are// defined only by external decoding circuits. LDIR (optional) is also separate.map(0x0,0x0).r(FUNC(wd37c65c_device::msr_r));map(0x1,0x1).rw(FUNC(wd37c65c_device::fifo_r),FUNC(wd37c65c_device::fifo_w));}voidmcs3201_device::map(address_map&map){map(0x0,0x0).r(FUNC(mcs3201_device::input_r));map(0x2,0x2).w(FUNC(mcs3201_device::dor_w));map(0x4,0x4).r(FUNC(mcs3201_device::msr_r));map(0x5,0x5).rw(FUNC(mcs3201_device::fifo_r),FUNC(mcs3201_device::fifo_w));map(0x7,0x7).rw(FUNC(mcs3201_device::dir_r),FUNC(mcs3201_device::ccr_w));}voidtc8566af_device::map(address_map&map){map(0x2,0x2).w(FUNC(tc8566af_device::dor_w));map(0x3,0x3).w(FUNC(tc8566af_device::cr1_w));map(0x4,0x4).r(FUNC(tc8566af_device::msr_r));map(0x5,0x5).rw(FUNC(tc8566af_device::fifo_r),FUNC(tc8566af_device::fifo_w));}constexprintupd765_family_device::rates[4];upd765_family_device::upd765_family_device(constmachine_config&mconfig,device_typetype,constchar*tag,device_t*owner,uint32_tclock):device_t(mconfig,type,tag,owner,clock),ready_connected(true),ready_polled(true),select_connected(true),select_multiplexed(true),has_dor(true),external_ready(false),recalibrate_steps(77),mode(mode_t::AT),intrq_cb(*this),drq_cb(*this),hdl_cb(*this),idx_cb(*this),us_cb(*this){}voidupd765_family_device::set_ready_line_connected(bool_ready){ready_connected=_ready;}voidupd765_family_device::set_select_lines_connected(bool_select){select_connected=_select;}voidps2_fdc_device::set_mode(mode_t_mode){mode=_mode;}voidupd765_family_device::device_resolve_objects(){intrq_cb.resolve_safe();drq_cb.resolve_safe();hdl_cb.resolve_safe();idx_cb.resolve_safe();us_cb.resolve_safe();}voidupd765_family_device::device_start(){save_item(NAME(selected_drive));for(inti=0;i!=4;i++){charname[2];flopi[i].tm=timer_alloc(i);flopi[i].id=i;if(select_connected){name[0]='0'+i;name[1]=0;floppy_connector*con=subdevice<floppy_connector>(name);if(con){flopi[i].dev=con->get_device();if(flopi[i].dev!=nullptr)flopi[i].dev->setup_index_pulse_cb(floppy_image_device::index_pulse_cb(&upd765_family_device::index_callback,this));}elseflopi[i].dev=nullptr;}elseflopi[i].dev=nullptr;flopi[i].main_state=IDLE;flopi[i].sub_state=IDLE;flopi[i].dir=0;flopi[i].counter=0;flopi[i].pcn=0;flopi[i].st0=0;flopi[i].st0_filled=false;flopi[i].live=false;flopi[i].index=false;flopi[i].ready=false;}cur_rate=250000;tc=false;selected_drive=-1;dor=0x0c;// reset at upper levels may cause a write to tc ending up with// live_sync, which will crash if the live structure isn't// initialized enoughcur_live.tm=attotime::never;cur_live.state=IDLE;cur_live.next_state=-1;cur_live.fi=nullptr;if(ready_polled){poll_timer=timer_alloc(TIMER_DRIVE_READY_POLLING);poll_timer->adjust(attotime::from_usec(100),0,attotime::from_usec(1024));}elsepoll_timer=nullptr;cur_irq=false;locked=false;}voidupd765_family_device::device_reset(){if(has_dor)dor=0x00;locked=false;soft_reset();}voidupd765_family_device::soft_reset(){main_phase=PHASE_CMD;for(inti=0;i<4;i++){flopi[i].main_state=IDLE;flopi[i].sub_state=IDLE;flopi[i].live=false;flopi[i].ready=!ready_polled;flopi[i].st0=i;flopi[i].st0_filled=false;}clr_drive_busy();data_irq=false;other_irq=false;internal_drq=false;fifo_pos=0;command_pos=0;result_pos=0;if(!locked)fifocfg=FIF_DIS;cur_live.fi=nullptr;drq=false;cur_live.tm=attotime::never;cur_live.state=IDLE;cur_live.next_state=-1;cur_live.fi=nullptr;tc_done=false;st1=st2=st3=0x00;set_ds(select_multiplexed?0:-1);check_irq();if(BIT(dor,2))end_reset();elseif(ready_polled)poll_timer->adjust(attotime::never);}voidupd765_family_device::end_reset(){if(ready_polled)poll_timer->adjust(attotime::from_usec(100),0,attotime::from_usec(1024));}voidupd765_family_device::tc_w(bool_tc){LOGTCIRQ("tc=%d\n",_tc);if(tc!=_tc&&_tc){live_sync();tc_done=true;tc=_tc;if(cur_live.fi)general_continue(*cur_live.fi);}elsetc=_tc;}voidupd765_family_device::ready_w(bool_ready){external_ready=_ready;}boolupd765_family_device::get_ready(intfid){if(ready_connected)returnflopi[fid].dev?!flopi[fid].dev->ready_r():false;return!external_ready;}WRITE_LINE_MEMBER(upd765_family_device::reset_w){// This implementation is not valid for devices with DOR and possibly other extra registers.// The working assumption is that no need to manipulate the RESET line directly when software can use DOR instead.assert(!has_dor);if(bool(state)==!BIT(dor,2))return;LOGREGS("reset = %d\n",state);if(state){dor&=0xfb;soft_reset();}else{dor|=0x04;end_reset();}}voidupd765_family_device::set_ds(intfid){if(selected_drive==fid)return;// pass drive select to connected drivesfor(floppy_info&fi:flopi)if(fi.dev)fi.dev->ds_w(fid);us_cb(fid);// record selected driveselected_drive=fid;}voidupd765_family_device::set_floppy(floppy_image_device*flop){for(floppy_info&elem:flopi){if(elem.dev){elem.dev->setup_index_pulse_cb(floppy_image_device::index_pulse_cb());if(elem.dev!=flop)elem.dev->ds_w(-1);}elem.dev=flop;}if(flop)flop->setup_index_pulse_cb(floppy_image_device::index_pulse_cb(&upd765_family_device::index_callback,this));elseidx_cb(0);}uint8_tps2_fdc_device::sra_r(){uint8_tsra=0;intfid=dor&3;floppy_info&fi=flopi[fid];if(fi.dir)sra|=0x01;if(fi.index)sra|=0x04;if(cur_rate>=500000)sra|=0x08;if(fi.dev&&fi.dev->trk00_r())sra|=0x10;if(fi.main_state==SEEK_WAIT_STEP_SIGNAL_TIME)sra|=0x20;sra|=0x40;if(cur_irq)sra|=0x80;if(mode==mode_t::M30)sra^=0x1f;returnsra;}uint8_tps2_fdc_device::srb_r(){return0;}uint8_tupd765_family_device::dor_r(){returndor;}voidupd765_family_device::dor_w(uint8_tdata){assert(has_dor);LOGREGS("dor = %02x\n",data);uint8_tdiff=dor^data;dor=data;if(BIT(diff,2)){if(BIT(data,2))end_reset();elsesoft_reset();}for(inti=0;i<4;i++){floppy_info&fi=flopi[i];if(fi.dev)fi.dev->mon_w(!(dor&(0x10<<i)));}check_irq();}uint8_tupd765_family_device::tdr_r(){return0;}voidupd765_family_device::tdr_w(uint8_tdata){}uint8_tupd765_family_device::msr_r(){uint32_tmsr=0;switch(main_phase){casePHASE_CMD:msr|=MSR_RQM;if(command_pos)msr|=MSR_CB;break;casePHASE_EXEC:msr|=MSR_CB;if(spec&SPEC_ND)msr|=MSR_EXM;if(internal_drq){msr|=MSR_RQM;if(!fifo_write)msr|=MSR_DIO;}break;casePHASE_RESULT:msr|=MSR_RQM|MSR_DIO|MSR_CB;break;}for(inti=0;i<4;i++)if(flopi[i].main_state==RECALIBRATE||flopi[i].main_state==SEEK){msr|=1<<i;//msr |= MSR_CB;}msr|=get_drive_busy();if(data_irq&&!machine().side_effects_disabled()){data_irq=false;check_irq();}returnmsr;}voidupd765_family_device::dsr_w(uint8_tdata){LOGREGS("dsr_w %02x (%s)\n",data,machine().describe_context());if(data&0x80)soft_reset();dsr=data&0x7f;cur_rate=rates[dsr&3];}voidupd765_family_device::set_rate(intrate){cur_rate=rate;}uint8_tupd765_family_device::fifo_r(){uint8_tr=0xff;switch(main_phase){casePHASE_EXEC:if(machine().side_effects_disabled())returnfifo[0];if(internal_drq)returnfifo_pop(false);LOGFIFO("fifo_r in phase %d\n",main_phase);break;casePHASE_RESULT:r=result[0];if(!machine().side_effects_disabled()){result_pos--;memmove(result,result+1,result_pos);if(!result_pos)main_phase=PHASE_CMD;elseif(result_pos==1){// clear drive busy bit after the first sense interrupt status result byte is readfor(floppy_info&fi:flopi)if((fi.main_state==RECALIBRATE||fi.main_state==SEEK)&&fi.sub_state==IDLE&&fi.st0_filled==false)fi.main_state=IDLE;clr_drive_busy();}}break;default:LOGFIFO("fifo_r in phase %d\n",main_phase);break;}returnr;}voidupd765_family_device::fifo_w(uint8_tdata){if(!BIT(dor,2))LOGWARN("%s: fifo_w(%02x) in reset\n",machine().describe_context(),data);switch(main_phase){casePHASE_CMD:{command[command_pos++]=data;other_irq=false;check_irq();intcmd=check_command();if(cmd==C_INCOMPLETE)break;if(cmd==C_INVALID){LOGFIFO("Invalid on %02x\n",command[0]);main_phase=PHASE_RESULT;result[0]=ST0_UNK;result_pos=1;command_pos=0;return;}start_command(cmd);break;}casePHASE_EXEC:if(internal_drq){fifo_push(data,false);return;}LOGFIFO("fifo_w in phase %d\n",main_phase);break;default:LOGFIFO("fifo_w in phase %d\n",main_phase);break;}}uint8_tupd765_family_device::do_dir_r(){floppy_info&fi=flopi[dor&3];if(fi.dev)returnfi.dev->dskchg_r()?0x00:0x80;return0x00;}voidupd765_family_device::ccr_w(uint8_tdata){dsr=(dsr&0xfc)|(data&3);cur_rate=rates[data&3];}voidupd765_family_device::set_drq(boolstate){if(state!=drq){drq=state;drq_cb(drq);}}boolupd765_family_device::get_drq()const{returndrq;}voidupd765_family_device::enable_transfer(){if(spec&SPEC_ND){// PIOif(!internal_drq){internal_drq=true;check_irq();}}else{// DMAif(!drq)set_drq(true);}}voidupd765_family_device::disable_transfer(){if(spec&SPEC_ND){internal_drq=false;check_irq();}elseset_drq(false);}voidupd765_family_device::fifo_push(uint8_tdata,boolinternal){// MZ: A bit speculative. These lines help to avoid some FIFO mess-up// with the HX5102 that happens when WRITE DATA fails to find the sector// but the host already starts pushing the sector data. Should not hurt.if(fifo_expected==0){LOGFIFO("Fifo not expecting data, discarding\n");return;}if(fifo_pos==16){if(internal){if(!(st1&ST1_OR))LOGFIFO("Fifo overrun\n");st1|=ST1_OR;}return;}fifo[fifo_pos++]=data;fifo_expected--;intthr=(fifocfg&FIF_THR)+1;if(!fifo_write&&(!fifo_expected||fifo_pos>=thr||(fifocfg&FIF_DIS)))enable_transfer();if(fifo_write&&(fifo_pos==16||!fifo_expected))disable_transfer();}uint8_tupd765_family_device::fifo_pop(boolinternal){if(!fifo_pos){if(internal){if(!(st1&ST1_OR))LOGFIFO("Fifo underrun\n");st1|=ST1_OR;}return0;}uint8_tr=fifo[0];fifo_pos--;memmove(fifo,fifo+1,fifo_pos);if(!fifo_write&&!fifo_pos)disable_transfer();intthr=fifocfg&FIF_THR;if(fifo_write&&fifo_expected&&(fifo_pos<=thr||(fifocfg&FIF_DIS)))enable_transfer();returnr;}voidupd765_family_device::fifo_expect(intsize,boolwrite){fifo_expected=size;fifo_write=write;if(fifo_write)enable_transfer();}uint8_tupd765_family_device::dma_r(){if(machine().side_effects_disabled())returnfifo[0];returnfifo_pop(false);}voidupd765_family_device::dma_w(uint8_tdata){fifo_push(data,false);}voidupd765_family_device::live_start(floppy_info&fi,intstate){cur_live.tm=machine().time();cur_live.state=state;cur_live.next_state=-1;cur_live.fi=&fi;cur_live.shift_reg=0;cur_live.crc=0xffff;cur_live.bit_counter=0;cur_live.data_separator_phase=false;cur_live.data_reg=0;cur_live.previous_type=live_info::PT_NONE;cur_live.data_bit_context=false;cur_live.byte_counter=0;cur_live.pll.reset(cur_live.tm);cur_live.pll.set_clock(attotime::from_hz(mfm?2*cur_rate:cur_rate));checkpoint_live=cur_live;fi.live=true;live_run();}voidupd765_family_device::checkpoint(){if(cur_live.fi)cur_live.pll.commit(cur_live.fi->dev,cur_live.tm);checkpoint_live=cur_live;}voidupd765_family_device::rollback(){cur_live=checkpoint_live;}voidupd765_family_device::live_delay(intstate){cur_live.next_state=state;if(cur_live.tm!=machine().time())cur_live.fi->tm->adjust(cur_live.tm-machine().time());elselive_sync();}voidupd765_family_device::live_sync(){if(!cur_live.tm.is_never()){if(cur_live.tm>machine().time()){rollback();live_run(machine().time());cur_live.pll.commit(cur_live.fi->dev,cur_live.tm);}else{cur_live.pll.commit(cur_live.fi->dev,cur_live.tm);if(cur_live.next_state!=-1){cur_live.state=cur_live.next_state;cur_live.next_state=-1;}if(cur_live.state==IDLE){cur_live.pll.stop_writing(cur_live.fi->dev,cur_live.tm);cur_live.tm=attotime::never;cur_live.fi->live=false;cur_live.fi=nullptr;}}cur_live.next_state=-1;checkpoint();}}voidupd765_family_device::live_abort(){if(!cur_live.tm.is_never()&&cur_live.tm>machine().time()){rollback();live_run(machine().time());}if(cur_live.fi){cur_live.pll.stop_writing(cur_live.fi->dev,cur_live.tm);cur_live.fi->live=false;cur_live.fi=nullptr;}cur_live.tm=attotime::never;cur_live.state=IDLE;cur_live.next_state=-1;}voidupd765_family_device::live_run(attotimelimit){if(cur_live.state==IDLE||cur_live.next_state!=-1)return;if(limit==attotime::never){if(cur_live.fi->dev)limit=cur_live.fi->dev->time_next_index();if(limit==attotime::never){// Happens when there's no disk or if the fdc is not// connected to a drive, hence no index pulse. Force a// sync from time to time in that case, so that the main// cpu timeout isn't too painful. Avoids looping into// infinity looking for data too.limit=machine().time()+attotime::from_msec(1);cur_live.fi->tm->adjust(attotime::from_msec(1));}}for(;;){switch(cur_live.state){caseSEARCH_ADDRESS_MARK_HEADER:if(read_one_bit(limit))return;LOGSHIFT("%s: shift = %04x data=%02x c=%d\n",cur_live.tm.to_string(),cur_live.shift_reg,(cur_live.shift_reg&0x4000?0x80:0x00)|(cur_live.shift_reg&0x1000?0x40:0x00)|(cur_live.shift_reg&0x0400?0x20:0x00)|(cur_live.shift_reg&0x0100?0x10:0x00)|(cur_live.shift_reg&0x0040?0x08:0x00)|(cur_live.shift_reg&0x0010?0x04:0x00)|(cur_live.shift_reg&0x0004?0x02:0x00)|(cur_live.shift_reg&0x0001?0x01:0x00),cur_live.bit_counter);if(mfm&&cur_live.shift_reg==0x4489){cur_live.crc=0x443b;cur_live.data_separator_phase=false;cur_live.bit_counter=0;cur_live.state=READ_HEADER_BLOCK_HEADER;LOGLIVE("%s: Found A1\n",cur_live.tm.to_string());}if(!mfm&&cur_live.shift_reg==0xf57e){cur_live.crc=0xef21;cur_live.data_separator_phase=false;cur_live.bit_counter=0;cur_live.state=READ_ID_BLOCK;LOGLIVE("%s: Found IDAM\n",cur_live.tm.to_string());}break;caseREAD_HEADER_BLOCK_HEADER:{if(read_one_bit(limit))return;LOGSHIFT("%s: shift = %04x data=%02x counter=%d\n",cur_live.tm.to_string(),cur_live.shift_reg,(cur_live.shift_reg&0x4000?0x80:0x00)|(cur_live.shift_reg&0x1000?0x40:0x00)|(cur_live.shift_reg&0x0400?0x20:0x00)|(cur_live.shift_reg&0x0100?0x10:0x00)|(cur_live.shift_reg&0x0040?0x08:0x00)|(cur_live.shift_reg&0x0010?0x04:0x00)|(cur_live.shift_reg&0x0004?0x02:0x00)|(cur_live.shift_reg&0x0001?0x01:0x00),cur_live.bit_counter);if(cur_live.bit_counter&15)break;intslot=cur_live.bit_counter>>4;if(slot<3){if(cur_live.shift_reg!=0x4489)cur_live.state=SEARCH_ADDRESS_MARK_HEADER;elseLOGLIVE("%s: Found A1\n",cur_live.tm.to_string());break;}if(cur_live.data_reg!=0xfe){LOGLIVE("%s: No ident byte found after triple-A1, continue search\n",cur_live.tm.to_string());cur_live.state=SEARCH_ADDRESS_MARK_HEADER;break;}cur_live.bit_counter=0;cur_live.state=READ_ID_BLOCK;break;}caseREAD_ID_BLOCK:{if(read_one_bit(limit))return;if(cur_live.bit_counter&15)break;intslot=(cur_live.bit_counter>>4)-1;LOGLIVE("%s: slot=%d data=%02x crc=%04x\n",cur_live.tm.to_string(),slot,cur_live.data_reg,cur_live.crc);cur_live.idbuf[slot]=cur_live.data_reg;if(slot==5){live_delay(IDLE);return;}break;}caseSEARCH_ADDRESS_MARK_DATA:if(read_one_bit(limit))return;LOGSHIFT("%s: shift = %04x data=%02x c=%d.%x\n",cur_live.tm.to_string(),cur_live.shift_reg,(cur_live.shift_reg&0x4000?0x80:0x00)|(cur_live.shift_reg&0x1000?0x40:0x00)|(cur_live.shift_reg&0x0400?0x20:0x00)|(cur_live.shift_reg&0x0100?0x10:0x00)|(cur_live.shift_reg&0x0040?0x08:0x00)|(cur_live.shift_reg&0x0010?0x04:0x00)|(cur_live.shift_reg&0x0004?0x02:0x00)|(cur_live.shift_reg&0x0001?0x01:0x00),cur_live.bit_counter>>4,cur_live.bit_counter&15);if(mfm){// Large tolerance due to perpendicular recording at extended densityif(cur_live.bit_counter>62*16){live_delay(SEARCH_ADDRESS_MARK_DATA_FAILED);return;}if(cur_live.bit_counter>=28*16&&cur_live.shift_reg==0x4489){cur_live.crc=0x443b;cur_live.data_separator_phase=false;cur_live.bit_counter=0;cur_live.state=READ_DATA_BLOCK_HEADER;}}else{if(cur_live.bit_counter>23*16){live_delay(SEARCH_ADDRESS_MARK_DATA_FAILED);return;}if(cur_live.bit_counter>=11*16&&(cur_live.shift_reg==0xf56a||cur_live.shift_reg==0xf56f)){cur_live.crc=cur_live.shift_reg==0xf56a?0x8fe7:0xbf84;cur_live.data_separator_phase=false;cur_live.bit_counter=0;cur_live.state=READ_SECTOR_DATA;}}break;caseREAD_DATA_BLOCK_HEADER:{if(read_one_bit(limit))return;LOGSHIFT("%s: shift = %04x data=%02x counter=%d\n",cur_live.tm.to_string(),cur_live.shift_reg,(cur_live.shift_reg&0x4000?0x80:0x00)|(cur_live.shift_reg&0x1000?0x40:0x00)|(cur_live.shift_reg&0x0400?0x20:0x00)|(cur_live.shift_reg&0x0100?0x10:0x00)|(cur_live.shift_reg&0x0040?0x08:0x00)|(cur_live.shift_reg&0x0010?0x04:0x00)|(cur_live.shift_reg&0x0004?0x02:0x00)|(cur_live.shift_reg&0x0001?0x01:0x00),cur_live.bit_counter);if(cur_live.bit_counter&15)break;intslot=cur_live.bit_counter>>4;if(slot<3){if(cur_live.shift_reg!=0x4489){live_delay(SEARCH_ADDRESS_MARK_DATA_FAILED);return;}break;}if(cur_live.data_reg!=0xfb&&cur_live.data_reg!=0xf8){live_delay(SEARCH_ADDRESS_MARK_DATA_FAILED);return;}if(((command[0]&0x08)==0&&cur_live.data_reg==0xf8)// Encountered deleted sector during read data||((command[0]&0x08)!=0&&cur_live.data_reg==0xfb)// Encountered normal sector during read deleted data){st2|=ST2_CM;}cur_live.bit_counter=0;cur_live.state=READ_SECTOR_DATA;break;}caseSEARCH_ADDRESS_MARK_DATA_FAILED:st1|=ST1_MA;st2|=ST2_MD;cur_live.state=IDLE;return;caseREAD_SECTOR_DATA:{if(read_one_bit(limit))return;if(cur_live.bit_counter&15)break;intslot=(cur_live.bit_counter>>4)-1;if(slot<sector_size){// Sector dataif(cur_live.fi->main_state==SCAN_DATA)live_delay(SCAN_SECTOR_DATA_BYTE);elselive_delay(READ_SECTOR_DATA_BYTE);return;}elseif(slot<sector_size+2){// CRCif(slot==sector_size+1){live_delay(IDLE);return;}}break;}caseREAD_SECTOR_DATA_BYTE:if(!tc_done)fifo_push(cur_live.data_reg,true);cur_live.state=READ_SECTOR_DATA;checkpoint();break;caseSCAN_SECTOR_DATA_BYTE:if(!scan_done){// TODO: handle stp, x68000 sets it to 0xff (as it would dtl)?intslot=(cur_live.bit_counter>>4)-1;uint8_tdata=fifo_pop(true);if(!slot)st2=(st2&~(ST2_SN))|ST2_SH;if(data!=cur_live.data_reg){st2=(st2&~(ST2_SH))|ST2_SN;if((data<cur_live.data_reg)&&((command[0]&0x1f)==0x19))// lowst2&=~ST2_SN;if((data>cur_live.data_reg)&&((command[0]&0x1f)==0x1d))// highst2&=~ST2_SN;}if((slot==sector_size)&&!(st2&ST2_SN)){scan_done=true;tc_done=true;}}else{if(fifo_pos)fifo_pop(true);}cur_live.state=READ_SECTOR_DATA;checkpoint();break;caseWRITE_SECTOR_SKIP_GAP2:cur_live.bit_counter=0;cur_live.byte_counter=0;cur_live.state=WRITE_SECTOR_SKIP_GAP2_BYTE;checkpoint();break;caseWRITE_SECTOR_SKIP_GAP2_BYTE:if(read_one_bit(limit))return;if(mfm&&cur_live.bit_counter!=22*16)break;if(!mfm&&cur_live.bit_counter!=11*16)break;cur_live.bit_counter=0;cur_live.byte_counter=0;live_delay(WRITE_SECTOR_DATA);return;caseWRITE_SECTOR_DATA:if(mfm){if(cur_live.byte_counter<12)live_write_mfm(0x00);elseif(cur_live.byte_counter<15)live_write_raw(0x4489);elseif(cur_live.byte_counter<16){cur_live.crc=0xcdb4;live_write_mfm(command[0]&0x08?0xf8:0xfb);}elseif(cur_live.byte_counter<16+sector_size)live_write_mfm(tc_done&&!fifo_pos?0x00:fifo_pop(true));elseif(cur_live.byte_counter<16+sector_size+2)live_write_mfm(cur_live.crc>>8);elseif(cur_live.byte_counter<16+sector_size+2+command[7])live_write_mfm(0x4e);else{cur_live.pll.stop_writing(cur_live.fi->dev,cur_live.tm);cur_live.state=IDLE;return;}}else{if(cur_live.byte_counter<6)live_write_fm(0x00);elseif(cur_live.byte_counter<7){cur_live.crc=0xffff;live_write_raw(command[0]&0x08?0xf56a:0xf56f);}elseif(cur_live.byte_counter<7+sector_size)live_write_fm(tc_done&&!fifo_pos?0x00:fifo_pop(true));elseif(cur_live.byte_counter<7+sector_size+2)live_write_fm(cur_live.crc>>8);elseif(cur_live.byte_counter<7+sector_size+2+command[7])live_write_fm(0xff);else{cur_live.pll.stop_writing(cur_live.fi->dev,cur_live.tm);cur_live.state=IDLE;return;}}cur_live.state=WRITE_SECTOR_DATA_BYTE;cur_live.bit_counter=16;checkpoint();break;caseWRITE_TRACK_PRE_SECTORS:if(!cur_live.byte_counter&&command[3])fifo_expect(4,true);if(mfm){if(cur_live.byte_counter<80)live_write_mfm(0x4e);elseif(cur_live.byte_counter<92)live_write_mfm(0x00);elseif(cur_live.byte_counter<95)live_write_raw(0x5224);elseif(cur_live.byte_counter<96)live_write_mfm(0xfc);elseif(cur_live.byte_counter<146)live_write_mfm(0x4e);else{cur_live.state=WRITE_TRACK_SECTOR;cur_live.byte_counter=0;break;}}else{if(cur_live.byte_counter<40)live_write_fm(0xff);elseif(cur_live.byte_counter<46)live_write_fm(0x00);elseif(cur_live.byte_counter<47)live_write_raw(0xf77a);elseif(cur_live.byte_counter<73)live_write_fm(0xff);else{cur_live.state=WRITE_TRACK_SECTOR;cur_live.byte_counter=0;break;}}cur_live.state=WRITE_TRACK_PRE_SECTORS_BYTE;cur_live.bit_counter=16;checkpoint();break;caseWRITE_TRACK_SECTOR:if(!cur_live.byte_counter){command[3]--;if(command[3])fifo_expect(4,true);}if(mfm){if(cur_live.byte_counter<12)live_write_mfm(0x00);elseif(cur_live.byte_counter<15)live_write_raw(0x4489);elseif(cur_live.byte_counter<16){cur_live.crc=0xcdb4;live_write_mfm(0xfe);}elseif(cur_live.byte_counter<20){uint8_tbyte=fifo_pop(true);command[12+cur_live.byte_counter-16]=byte;live_write_mfm(byte);if(cur_live.byte_counter==19)LOGFORMAT("formatting sector %02x %02x %02x %02x\n",command[12],command[13],command[14],command[15]);}elseif(cur_live.byte_counter<22)live_write_mfm(cur_live.crc>>8);elseif(cur_live.byte_counter<44)live_write_mfm(0x4e);elseif(cur_live.byte_counter<56)live_write_mfm(0x00);elseif(cur_live.byte_counter<59)live_write_raw(0x4489);elseif(cur_live.byte_counter<60){cur_live.crc=0xcdb4;live_write_mfm(0xfb);}elseif(cur_live.byte_counter<60+sector_size)live_write_mfm(command[5]);elseif(cur_live.byte_counter<62+sector_size)live_write_mfm(cur_live.crc>>8);elseif(cur_live.byte_counter<62+sector_size+command[4])live_write_mfm(0x4e);else{cur_live.byte_counter=0;cur_live.state=command[3]?WRITE_TRACK_SECTOR:WRITE_TRACK_POST_SECTORS;break;}}else{if(cur_live.byte_counter<6)live_write_fm(0x00);elseif(cur_live.byte_counter<7){cur_live.crc=0xffff;live_write_raw(0xf57e);}elseif(cur_live.byte_counter<11){uint8_tbyte=fifo_pop(true);command[12+cur_live.byte_counter-7]=byte;live_write_fm(byte);if(cur_live.byte_counter==10)LOGFORMAT("formatting sector %02x %02x %02x %02x\n",command[12],command[13],command[14],command[15]);}elseif(cur_live.byte_counter<13)live_write_fm(cur_live.crc>>8);elseif(cur_live.byte_counter<24)live_write_fm(0xff);elseif(cur_live.byte_counter<30)live_write_fm(0x00);elseif(cur_live.byte_counter<31){cur_live.crc=0xffff;live_write_raw(0xf56f);}elseif(cur_live.byte_counter<31+sector_size)live_write_fm(command[5]);elseif(cur_live.byte_counter<33+sector_size)live_write_fm(cur_live.crc>>8);elseif(cur_live.byte_counter<33+sector_size+command[4])live_write_fm(0xff);else{cur_live.byte_counter=0;cur_live.state=command[3]?WRITE_TRACK_SECTOR:WRITE_TRACK_POST_SECTORS;break;}}cur_live.state=WRITE_TRACK_SECTOR_BYTE;cur_live.bit_counter=16;checkpoint();break;caseWRITE_TRACK_POST_SECTORS:if(mfm)live_write_mfm(0x4e);elselive_write_fm(0xff);cur_live.state=WRITE_TRACK_POST_SECTORS_BYTE;cur_live.bit_counter=16;checkpoint();break;caseWRITE_TRACK_PRE_SECTORS_BYTE:caseWRITE_TRACK_SECTOR_BYTE:caseWRITE_TRACK_POST_SECTORS_BYTE:caseWRITE_SECTOR_DATA_BYTE:if(write_one_bit(limit))return;if(cur_live.bit_counter==0){cur_live.byte_counter++;live_delay(cur_live.state-1);return;}break;default:LOGWARN("%s: Unknown live state %d\n",cur_live.tm.to_string(),cur_live.state);return;}}}intupd765_family_device::check_command(){// 0.000010 read track// 00000011 specify// 00000100 sense drive status// ..000101 write data// ...00110 read data// 00000111 recalibrate// 00001000 sense interrupt status// ..001001 write deleted data// 0.001010 read id// ...01100 read deleted data// 0.001101 format track// 00001110 dumpreg// 00101110 save// 01001110 restore// 10001110 drive specification command// 00001111 seek// 1.001111 relative seek// 00010000 version// ...10001 scan equal// 00010010 perpendicular mode// 00010011 configure// 00110011 option// .0010100 lock// ...10110 verify// 00010111 powerdown mode// 00011000 part id// ...11001 scan low or equal// ...11101 scan high or equal// MSDOS 6.22 format uses 0xcd to format a track, which makes one// think only the bottom 5 bits are decoded.switch(command[0]&0x1f){case0x02:returncommand_pos==9?C_READ_TRACK:C_INCOMPLETE;case0x03:returncommand_pos==3?C_SPECIFY:C_INCOMPLETE;case0x04:returncommand_pos==2?C_SENSE_DRIVE_STATUS:C_INCOMPLETE;case0x05:case0x09:returncommand_pos==9?C_WRITE_DATA:C_INCOMPLETE;case0x06:case0x0c:returncommand_pos==9?C_READ_DATA:C_INCOMPLETE;case0x07:returncommand_pos==2?C_RECALIBRATE:C_INCOMPLETE;case0x08:returnC_SENSE_INTERRUPT_STATUS;case0x0a:returncommand_pos==2?C_READ_ID:C_INCOMPLETE;case0x0d:returncommand_pos==6?C_FORMAT_TRACK:C_INCOMPLETE;case0x0f:returncommand_pos==3?C_SEEK:C_INCOMPLETE;case0x11:returncommand_pos==9?C_SCAN_EQUAL:C_INCOMPLETE;case0x19:returncommand_pos==9?C_SCAN_LOW:C_INCOMPLETE;case0x1d:returncommand_pos==9?C_SCAN_HIGH:C_INCOMPLETE;default:returnC_INVALID;}}intps2_fdc_device::check_command(){switch(command[0]&0x1f){case0x0e:returnC_DUMP_REG;case0x10:returnC_VERSION;case0x12:returncommand_pos==2?C_PERPENDICULAR:C_INCOMPLETE;case0x13:returncommand_pos==4?C_CONFIGURE:C_INCOMPLETE;case0x14:returnC_LOCK;default:returnupd765_family_device::check_command();}}voidupd765_family_device::start_command(intcmd){command_pos=0;result_pos=0;main_phase=PHASE_EXEC;tc_done=false;execute_command(cmd);}voidupd765_family_device::execute_command(intcmd){switch(cmd){caseC_FORMAT_TRACK:format_track_start(flopi[command[1]&3]);break;caseC_READ_DATA:read_data_start(flopi[command[1]&3]);break;caseC_READ_ID:read_id_start(flopi[command[1]&3]);break;caseC_READ_TRACK:read_track_start(flopi[command[1]&3]);break;caseC_SCAN_EQUAL:caseC_SCAN_LOW:caseC_SCAN_HIGH:scan_start(flopi[command[1]&3]);break;caseC_RECALIBRATE:recalibrate_start(flopi[command[1]&3]);main_phase=PHASE_CMD;break;caseC_SEEK:seek_start(flopi[command[1]&3]);main_phase=PHASE_CMD;break;caseC_SENSE_DRIVE_STATUS:{floppy_info&fi=flopi[command[1]&3];main_phase=PHASE_RESULT;result[0]=get_st3(fi);LOGCOMMAND("command sense drive status %d (%02x)\n",fi.id,result[0]);result_pos=1;break;}caseC_SENSE_INTERRUPT_STATUS:{// Documentation is somewhat contradictory w.r.t polling// and irq. PC bios, especially 5150, requires that only// one irq happens. That's also what the ns82077a doc// says it does. OTOH, a number of docs says you need to// call SIS 4 times, once per drive...//// There's also the interaction with the seek irq. The// somewhat borderline tf20 code seems to think that// essentially ignoring the polling irq should work.//// And the pc98 expects to be able to accumulate irq reasons// for different drives and things to work.//// Current hypothesis:// - each drive has its own st0 and irq trigger// - SIS drops the irq always, but also returns the first full st0 it findsmain_phase=PHASE_RESULT;intfid;for(fid=0;fid<4&&!flopi[fid].st0_filled;fid++){};if(fid==4){result[0]=ST0_UNK;result_pos=1;LOGCOMMAND("command sense interrupt status (%02x) (%s)\n",result[0],machine().describe_context());break;}floppy_info&fi=flopi[fid];fi.st0_filled=false;result[0]=fi.st0;result[1]=fi.pcn;LOGCOMMAND("command sense interrupt status (fid=%d %02x %02x) (%s)\n",fid,result[0],result[1],machine().describe_context());result_pos=2;other_irq=false;check_irq();break;}caseC_SPECIFY:spec=(command[1]<<8)|command[2];LOGCOMMAND("command specify %02x %02x: step_rate=%d ms, head_unload=%d ms, head_load=%d ms, non_dma=%s\n",command[1],command[2],16-(command[1]>>4),(command[1]&0x0f)<<4,command[2]&0xfe,((command[2]&1)==1)?"true":"false");main_phase=PHASE_CMD;break;caseC_WRITE_DATA:write_data_start(flopi[command[1]&3]);break;default:LOGWARN("Unknown command %02x\n",cmd);// exit(1);}}voidps2_fdc_device::execute_command(intcmd){switch(cmd){caseC_CONFIGURE:LOGCOMMAND("command configure %02x %02x %02x\n",command[1],command[2],command[3]);// byte 1 is ignored, byte 3 is precompensation-relatedfifocfg=command[2];precomp=command[3];main_phase=PHASE_CMD;break;caseC_DUMP_REG:LOGCOMMAND("command dump regs\n");main_phase=PHASE_RESULT;result[0]=flopi[0].pcn;result[1]=flopi[1].pcn;result[2]=flopi[2].pcn;result[3]=flopi[3].pcn;result[4]=(spec&0xff00)>>8;result[5]=(spec&0x00ff);result[6]=sector_size;result[7]=locked?0x80:0x00;result[7]|=(perpmode&0x3f);result[8]=fifocfg;result[9]=precomp;result_pos=10;break;caseC_VERSION:LOGCOMMAND("command version\n");main_phase=PHASE_RESULT;result[0]=0x90;result_pos=1;break;caseC_LOCK:locked=command[0]&0x80;main_phase=PHASE_RESULT;result[0]=locked?0x10:0x00;result_pos=1;LOGCOMMAND("command lock (%s)\n",locked?"on":"off");break;caseC_PERPENDICULAR:LOGCOMMAND("command perpendicular\n");if(BIT(command[1],7))perpmode=command[1]&0x3f;elseperpmode=(perpmode&0x3c)|(command[1]&3);main_phase=PHASE_CMD;break;default:upd765_family_device::execute_command(cmd);break;}}voidupd765_family_device::command_end(floppy_info&fi,booldata_completion){LOGDONE("command done (%s) - %s\n",data_completion?"data":"seek",results());fi.main_state=fi.sub_state=IDLE;if(data_completion)data_irq=true;else{other_irq=true;fi.st0_filled=true;}check_irq();}uint8_tupd765_family_device::get_st3(floppy_info&fi){uint8_tst3=command[1]&7;if(fi.ready)st3|=ST3_RY;if(fi.dev)st3|=(fi.dev->wpt_r()?ST3_WP:0x00)|(fi.dev->trk00_r()?0x00:ST3_T0)|(fi.dev->twosid_r()?0x00:ST3_TS);returnst3;}voidupd765_family_device::recalibrate_start(floppy_info&fi){LOGCOMMAND("command recalibrate %d\n",command[1]&3);fi.main_state=RECALIBRATE;fi.sub_state=SEEK_WAIT_STEP_TIME_DONE;fi.dir=1;fi.counter=recalibrate_steps;fi.ready=get_ready(command[1]&3);fi.st0=(fi.ready?0:ST0_NR);seek_continue(fi);}voidupd765_family_device::seek_start(floppy_info&fi){LOGCOMMAND("command %sseek %d\n",command[0]&0x80?"relative ":"",command[2]);fi.main_state=SEEK;fi.sub_state=SEEK_WAIT_STEP_TIME_DONE;fi.dir=fi.pcn>command[2]?1:0;fi.ready=get_ready(command[1]&3);fi.st0=(fi.ready?0:ST0_NR);seek_continue(fi);}voidupd765_family_device::delay_cycles(emu_timer*tm,intcycles){tm->adjust(attotime::from_double(double(cycles)/cur_rate));}voidupd765_family_device::seek_continue(floppy_info&fi){for(;;){switch(fi.sub_state){caseSEEK_MOVE:LOGSTATE("SEEK_MOVE\n");if(fi.dev){fi.dev->dir_w(fi.dir);fi.dev->stp_w(0);}fi.sub_state=SEEK_WAIT_STEP_SIGNAL_TIME;fi.tm->adjust(attotime::from_nsec(2500));return;caseSEEK_WAIT_STEP_SIGNAL_TIME:LOGSTATE("SEEK_WAIT_STEP_SIGNAL_TIME\n");return;caseSEEK_WAIT_STEP_SIGNAL_TIME_DONE:LOGSTATE("SEEK_WAIT_STEP_SIGNAL_TIME_DONE\n");if(fi.dev)fi.dev->stp_w(1);if(fi.main_state==SEEK){if(fi.pcn>command[2])fi.pcn--;elsefi.pcn++;}fi.sub_state=SEEK_WAIT_STEP_TIME;delay_cycles(fi.tm,500*(16-(spec>>12)));return;caseSEEK_WAIT_STEP_TIME:LOGSTATE("SEEK_WAIT_STEP_TIME\n");return;caseSEEK_WAIT_STEP_TIME_DONE:{LOGSTATE("SEEK_WAIT_STEP_TIME_DONE\n");booldone=false;switch(fi.main_state){caseRECALIBRATE:LOGSTATE("RECALIBRATE\n");fi.counter--;done=fi.dev&&!fi.dev->trk00_r();if(done)fi.pcn=0;elseif(!fi.counter){fi.st0|=ST0_FAIL|ST0_SE|ST0_EC|fi.id;command_end(fi,false);return;}break;caseSEEK:LOGSTATE("SEEK\n");done=fi.pcn==command[2];break;}if(done){fi.sub_state=SEEK_WAIT_DONE;// recalibrate and seek takes some time, even if we don't movefi.tm->adjust(attotime::from_nsec((fi.main_state==RECALIBRATE)?20000:10000));return;}fi.sub_state=SEEK_MOVE;break;}caseSEEK_WAIT_DONE:LOGSTATE("SEEK_WAIT_DONE\n");fi.st0|=ST0_SE|fi.id;command_end(fi,false);return;}}}voidupd765_family_device::read_data_start(floppy_info&fi){fi.main_state=READ_DATA;fi.sub_state=HEAD_LOAD;mfm=command[0]&0x40;LOGCOMMAND("command read%s data%s%s%s%s cmd=%02x sel=%x chrn=(%d, %d, %d, %d) eot=%02x gpl=%02x dtl=%02x rate=%d\n",command[0]&0x08?" deleted":"",command[0]&0x80?" mt":"",command[0]&0x40?" mfm":"",command[0]&0x20?" sk":"",fifocfg&0x40?" seek":"",command[0],command[1],command[2],command[3],command[4],128<<(command[5]&7),command[6],command[7],command[8],cur_rate);fi.st0=command[1]&7;st1=ST1_MA;st2=0x00;hdl_cb(1);set_ds(command[1]&3);fi.ready=get_ready(command[1]&3);if(!fi.ready){fi.st0|=ST0_NR|ST0_FAIL;fi.sub_state=COMMAND_DONE;st1=0;st2=0;read_data_continue(fi);return;}if(fi.dev)fi.dev->ss_w(command[1]&4?1:0);read_data_continue(fi);}voidupd765_family_device::scan_start(floppy_info&fi){fi.main_state=SCAN_DATA;fi.sub_state=HEAD_LOAD;mfm=command[0]&0x40;LOGCOMMAND("command scan%s data%s%s%s%s cmd=%02x sel=%x chrn=(%d, %d, %d, %d) eot=%02x gpl=%02x stp=%02x rate=%d\n",command[0]&0x08?" deleted":"",command[0]&0x80?" mt":"",command[0]&0x40?" mfm":"",command[0]&0x20?" sk":"",fifocfg&0x40?" seek":"",command[0],command[1],command[2],command[3],command[4],128<<(command[5]&7),command[6],command[7],command[8],cur_rate);fi.st0=command[1]&7;st1=ST1_MA;st2=0x00;scan_done=false;hdl_cb(1);set_ds(command[1]&3);fi.ready=get_ready(command[1]&3);if(!fi.ready){fi.st0|=ST0_NR|ST0_FAIL;fi.sub_state=COMMAND_DONE;st1=0;st2=0;read_data_continue(fi);return;}if(fi.dev)fi.dev->ss_w(command[1]&4?1:0);read_data_continue(fi);}voidupd765_family_device::read_data_continue(floppy_info&fi){for(;;){switch(fi.sub_state){caseHEAD_LOAD:LOGSTATE("HEAD_LOAD\n");delay_cycles(fi.tm,500*(spec&0x00fe));fi.sub_state=HEAD_LOAD_DONE;break;caseHEAD_LOAD_DONE:LOGSTATE("HEAD_LOAD_DONE\n");if(fi.pcn==command[2]||!(fifocfg&0x40)){fi.sub_state=SEEK_DONE;break;}fi.st0|=ST0_SE;if(fi.dev){fi.dev->dir_w(fi.pcn>command[2]?1:0);fi.dev->stp_w(0);}fi.sub_state=SEEK_WAIT_STEP_SIGNAL_TIME;fi.tm->adjust(attotime::from_nsec(2500));return;caseSEEK_WAIT_STEP_SIGNAL_TIME:LOGSTATE("SEEK_WAIT_STEP_SIGNAL_TIME\n");return;caseSEEK_WAIT_STEP_SIGNAL_TIME_DONE:LOGSTATE("SEEK_WAIT_STEP_SIGNAL_TIME_DONE\n");if(fi.dev)fi.dev->stp_w(1);fi.sub_state=SEEK_WAIT_STEP_TIME;delay_cycles(fi.tm,500*(16-(spec>>12)));return;caseSEEK_WAIT_STEP_TIME:LOGSTATE("SEEK_WAIT_STEP_TIME\n");return;caseSEEK_WAIT_STEP_TIME_DONE:LOGSTATE("SEEK_WAIT_STEP_TIME_DONE\n");if(fi.pcn>command[2])fi.pcn--;elsefi.pcn++;fi.sub_state=HEAD_LOAD_DONE;break;caseSEEK_DONE:LOGSTATE("SEEK_DONE\n");fi.counter=0;fi.sub_state=SCAN_ID;LOGSTATE("SEARCH_ADDRESS_MARK_HEADER\n");live_start(fi,SEARCH_ADDRESS_MARK_HEADER);return;caseSCAN_ID:LOGSTATE("SCAN_ID\n");if(cur_live.crc){fi.st0|=ST0_FAIL;st1|=ST1_DE|ST1_ND;fi.sub_state=COMMAND_DONE;break;}// MZ: This st1 handling ensures that both HX5102 floppy and the// Speedlock protection scheme are properly working.// a) HX5102 requires that the ND flag not be set when no address// marks could be found on the track at all (particularly due to// wrong density)// b) Speedlock requires the ND flag be set when there are valid// sectors on the track, but the desired sector is missing, also// when it has no valid address marksst1&=~ST1_MA;st1|=ST1_ND;if(!sector_matches()){if(cur_live.idbuf[0]!=command[2]){if(cur_live.idbuf[0]==0xff)st2|=ST2_WC|ST2_BC;elsest2|=ST2_WC;}LOGSTATE("SEARCH_ADDRESS_MARK_HEADER\n");live_start(fi,SEARCH_ADDRESS_MARK_HEADER);return;}st1&=~ST1_ND;LOGRW("reading sector %02x %02x %02x %02x\n",cur_live.idbuf[0],cur_live.idbuf[1],cur_live.idbuf[2],cur_live.idbuf[3]);sector_size=calc_sector_size(cur_live.idbuf[3]);if(fi.main_state==SCAN_DATA)fifo_expect(sector_size,true);elsefifo_expect(sector_size,false);fi.sub_state=SECTOR_READ;LOGSTATE("SEARCH_ADDRESS_MARK_DATA\n");live_start(fi,SEARCH_ADDRESS_MARK_DATA);return;caseSCAN_ID_FAILED:LOGSTATE("SCAN_ID_FAILED\n");fi.st0|=ST0_FAIL;fi.sub_state=COMMAND_DONE;break;caseSECTOR_READ:{LOGSTATE("SECTOR_READ\n");if(st2&ST2_MD){fi.st0|=ST0_FAIL;fi.sub_state=COMMAND_DONE;break;}if(cur_live.crc){fi.st0|=ST0_FAIL;st1|=ST1_DE;st2|=ST2_CM;fi.sub_state=COMMAND_DONE;break;}if((st2&ST2_CM)&&!(command[0]&0x20)){// Encountered terminating sector while in non-skip mode.// This will stop reading when a normal data sector is encountered during read deleted data,// or when a deleted sector is encountered during a read data command.fi.sub_state=COMMAND_DONE;break;}booldone=tc_done;if(command[4]==command[6]){if(command[0]&0x80){command[3]=command[3]^1;command[4]=1;if(fi.dev)fi.dev->ss_w(command[3]&1);}if(!(command[0]&0x80)||!(command[3]&1)){if(!tc_done){fi.st0|=ST0_FAIL;st1|=ST1_EN;}else{command[2]++;command[4]=1;}done=true;}}elsecommand[4]++;if(!done){fi.sub_state=SEEK_DONE;break;}fi.sub_state=COMMAND_DONE;break;}caseCOMMAND_DONE:LOGSTATE("COMMAND_DONE\n");main_phase=PHASE_RESULT;result[0]=fi.st0;result[1]=st1;result[2]=st2;result[3]=command[2];result[4]=command[3];result[5]=command[4];result[6]=command[5];result_pos=7;command_end(fi,true);return;default:LOGWARN("%s: read sector unknown sub-state %d\n",ttsn(),fi.sub_state);return;}}}voidupd765_family_device::write_data_start(floppy_info&fi){fi.main_state=WRITE_DATA;fi.sub_state=HEAD_LOAD;mfm=command[0]&0x40;LOGCOMMAND("command write%s data%s%s cmd=%02x sel=%x chrn=(%d, %d, %d, %d) eot=%02x gpl=%02x dtl=%02x rate=%d\n",command[0]&0x08?" deleted":"",command[0]&0x80?" mt":"",command[0]&0x40?" mfm":"",command[0],command[1],command[2],command[3],command[4],128<<(command[5]&7),command[6],command[7],command[8],cur_rate);if(fi.dev)fi.dev->ss_w(command[1]&4?1:0);fi.st0=command[1]&7;st1=ST1_MA;st2=0x00;hdl_cb(1);set_ds(command[1]&3);fi.ready=get_ready(command[1]&3);if(!fi.ready){fi.st0|=ST0_NR|ST0_FAIL;fi.sub_state=COMMAND_DONE;st1=0;st2=0;write_data_continue(fi);return;}write_data_continue(fi);}voidupd765_family_device::write_data_continue(floppy_info&fi){for(;;){switch(fi.sub_state){caseHEAD_LOAD:LOGSTATE("HEAD_LOAD\n");delay_cycles(fi.tm,500*(spec&0x00fe));fi.sub_state=HEAD_LOAD_DONE;break;caseHEAD_LOAD_DONE:LOGSTATE("HEAD_LOAD_DONE\n");fi.counter=0;fi.sub_state=SCAN_ID;LOGSTATE("SEARCH_ADDRESS_MARK_HEADER\n");live_start(fi,SEARCH_ADDRESS_MARK_HEADER);return;caseSCAN_ID:LOGSTATE("SCAN_ID\n");if(!sector_matches()){LOGSTATE("SEARCH_ADDRESS_MARK_HEADER\n");live_start(fi,SEARCH_ADDRESS_MARK_HEADER);return;}if(cur_live.crc){fi.st0|=ST0_FAIL;st1|=ST1_DE|ST1_ND;fi.sub_state=COMMAND_DONE;break;}st1&=~ST1_MA;LOGRW("writing sector %02x %02x %02x %02x\n",cur_live.idbuf[0],cur_live.idbuf[1],cur_live.idbuf[2],cur_live.idbuf[3]);sector_size=calc_sector_size(cur_live.idbuf[3]);fifo_expect(sector_size,true);fi.sub_state=SECTOR_WRITTEN;LOGSTATE("WRITE_SECTOR_SKIP_GAP2\n");live_start(fi,WRITE_SECTOR_SKIP_GAP2);return;caseSCAN_ID_FAILED:LOGSTATE("SCAN_ID_FAILED\n");fi.st0|=ST0_FAIL;// st1 |= ST1_ND;fi.sub_state=COMMAND_DONE;break;caseSECTOR_WRITTEN:{LOGSTATE("SECTOR_WRITTEN\n");booldone=tc_done;if(command[4]==command[6]){if(command[0]&0x80){command[3]=command[3]^1;command[4]=1;if(fi.dev)fi.dev->ss_w(command[3]&1);}if(!(command[0]&0x80)||!(command[3]&1)){if(!tc_done){fi.st0|=ST0_FAIL;st1|=ST1_EN;}else{command[2]++;command[4]=1;}done=true;}}elsecommand[4]++;if(!done){fi.sub_state=HEAD_LOAD_DONE;break;}fi.sub_state=COMMAND_DONE;break;}caseCOMMAND_DONE:LOGSTATE("COMMAND_DONE\n");main_phase=PHASE_RESULT;result[0]=fi.st0;result[1]=st1;result[2]=st2;result[3]=command[2];result[4]=command[3];result[5]=command[4];result[6]=command[5];result_pos=7;command_end(fi,true);return;default:LOGWARN("%s: write sector unknown sub-state %d\n",ttsn(),fi.sub_state);return;}}}voidupd765_family_device::read_track_start(floppy_info&fi){fi.main_state=READ_TRACK;fi.sub_state=HEAD_LOAD;mfm=command[0]&0x40;sectors_read=0;LOGCOMMAND("command read track%s cmd=%02x sel=%x chrn=(%d, %d, %d, %d) eot=%02x gpl=%02x dtl=%02x rate=%d\n",command[0]&0x40?" mfm":"",command[0],command[1],command[2],command[3],command[4],128<<(command[5]&7),command[6],command[7],command[8],cur_rate);fi.st0=command[1]&7;st1=ST1_MA;st2=0x00;hdl_cb(1);set_ds(command[1]&3);fi.ready=get_ready(command[1]&3);if(!fi.ready){fi.st0|=ST0_NR|ST0_FAIL;fi.sub_state=COMMAND_DONE;st1=0;st2=0;read_track_continue(fi);return;}if(fi.dev)fi.dev->ss_w(command[1]&4?1:0);read_track_continue(fi);}voidupd765_family_device::read_track_continue(floppy_info&fi){for(;;){switch(fi.sub_state){caseHEAD_LOAD:LOGSTATE("HEAD_LOAD\n");delay_cycles(fi.tm,500*(spec&0x00fe));fi.sub_state=HEAD_LOAD_DONE;break;caseHEAD_LOAD_DONE:LOGSTATE("HEAD_LOAD_DONE\n");if(fi.pcn==command[2]||!(fifocfg&0x40)){fi.sub_state=SEEK_DONE;break;}fi.st0|=ST0_SE;if(fi.dev){fi.dev->dir_w(fi.pcn>command[2]?1:0);fi.dev->stp_w(0);}fi.sub_state=SEEK_WAIT_STEP_SIGNAL_TIME;fi.tm->adjust(attotime::from_nsec(2500));return;caseSEEK_WAIT_STEP_SIGNAL_TIME:LOGSTATE("SEEK_WAIT_STEP_SIGNAL_TIME\n");return;caseSEEK_WAIT_STEP_SIGNAL_TIME_DONE:LOGSTATE("SEEK_WAIT_STEP_SIGNAL_TIME_DONE\n");if(fi.dev)fi.dev->stp_w(1);fi.sub_state=SEEK_WAIT_STEP_TIME;delay_cycles(fi.tm,500*(16-(spec>>12)));return;caseSEEK_WAIT_STEP_TIME:LOGSTATE("SEEK_WAIT_STEP_TIME\n");return;caseSEEK_WAIT_STEP_TIME_DONE:LOGSTATE("SEEK_WAIT_STEP_TIME_DONE\n");if(fi.pcn>command[2])fi.pcn--;elsefi.pcn++;fi.sub_state=HEAD_LOAD_DONE;break;caseSEEK_DONE:LOGSTATE("SEEK_DONE\n");fi.counter=0;fi.sub_state=WAIT_INDEX;return;caseWAIT_INDEX:LOGSTATE("WAIT_INDEX\n");return;caseWAIT_INDEX_DONE:LOGSTATE("WAIT_INDEX_DONE\n");fi.sub_state=SCAN_ID;LOGSTATE("SEARCH_ADDRESS_MARK_HEADER\n");live_start(fi,SEARCH_ADDRESS_MARK_HEADER);return;caseSCAN_ID:LOGSTATE("SCAN_ID\n");if(cur_live.crc){st1|=ST1_DE;}st1&=~ST1_MA;LOGRW("reading sector %02x %02x %02x %02x\n",cur_live.idbuf[0],cur_live.idbuf[1],cur_live.idbuf[2],cur_live.idbuf[3]);if(!sector_matches())st1|=ST1_ND;elsest1&=~ST1_ND;sector_size=calc_sector_size(cur_live.idbuf[3]);fifo_expect(sector_size,false);fi.sub_state=SECTOR_READ;LOGSTATE("SEARCH_ADDRESS_MARK_DATA\n");live_start(fi,SEARCH_ADDRESS_MARK_DATA);return;caseSCAN_ID_FAILED:LOGSTATE("SCAN_ID_FAILED\n");fi.st0|=ST0_FAIL;// st1 |= ST1_ND;fi.sub_state=COMMAND_DONE;break;caseSECTOR_READ:{LOGSTATE("SECTOR_READ\n");if(st2&ST2_MD){fi.st0|=ST0_FAIL;fi.sub_state=COMMAND_DONE;break;}if(cur_live.crc){st1|=ST1_DE;st2|=ST2_CM;}booldone=tc_done;sectors_read++;if(sectors_read==command[6]){if(!tc_done){fi.st0|=ST0_FAIL;st1|=ST1_EN;}done=true;}if(!done){fi.sub_state=WAIT_INDEX_DONE;break;}fi.sub_state=COMMAND_DONE;break;}caseCOMMAND_DONE:LOGSTATE("COMMAND_DONE\n");main_phase=PHASE_RESULT;result[0]=fi.st0;result[1]=st1;result[2]=st2;result[3]=command[2];result[4]=command[3];result[5]=command[4];result[6]=command[5];result_pos=7;command_end(fi,true);return;default:LOGWARN("%s: read track unknown sub-state %d\n",ttsn(),fi.sub_state);return;}}}intupd765_family_device::calc_sector_size(uint8_tsize){returnsize>7?16384:128<<size;}voidupd765_family_device::format_track_start(floppy_info&fi){fi.main_state=FORMAT_TRACK;fi.sub_state=HEAD_LOAD;mfm=command[0]&0x40;LOGCOMMAND("command format track %s h=%02x n=%02x sc=%02x gpl=%02x d=%02x\n",command[0]&0x40?"mfm":"fm",command[1],command[2],command[3],command[4],command[5]);hdl_cb(1);set_ds(command[1]&3);fi.ready=get_ready(command[1]&3);if(!fi.ready){fi.st0=(command[1]&7)|ST0_NR|ST0_FAIL;fi.sub_state=TRACK_DONE;format_track_continue(fi);return;}fi.st0=command[1]&7;if(fi.dev)fi.dev->ss_w(command[1]&4?1:0);sector_size=calc_sector_size(command[2]);format_track_continue(fi);}voidupd765_family_device::format_track_continue(floppy_info&fi){for(;;){switch(fi.sub_state){caseHEAD_LOAD:LOGSTATE("HEAD_LOAD\n");delay_cycles(fi.tm,500*(spec&0x00fe));fi.sub_state=HEAD_LOAD_DONE;break;caseHEAD_LOAD_DONE:LOGSTATE("HEAD_LOAD_DONE\n");fi.sub_state=WAIT_INDEX;break;caseWAIT_INDEX:LOGSTATE("WAIT_INDEX\n");return;caseWAIT_INDEX_DONE:LOGSTATE("WAIT_INDEX_DONE\n");fi.sub_state=TRACK_DONE;cur_live.pll.start_writing(machine().time());LOGSTATE("WRITE_TRACK_PRE_SECTORS\n");live_start(fi,WRITE_TRACK_PRE_SECTORS);return;caseTRACK_DONE:LOGSTATE("TRACK_DONE\n");main_phase=PHASE_RESULT;result[0]=fi.st0;result[1]=0;result[2]=0;result[3]=0;result[4]=0;result[5]=0;result[6]=command[2];result_pos=7;command_end(fi,true);return;default:LOGWARN("%s: format track unknown sub-state %d\n",ttsn(),fi.sub_state);return;}}}voidupd765_family_device::read_id_start(floppy_info&fi){fi.main_state=READ_ID;fi.sub_state=HEAD_LOAD;mfm=command[0]&0x40;LOGCOMMAND("command read id%s %d, rate=%d\n",command[0]&0x40?" mfm":"",command[1]&3,cur_rate);if(fi.dev)fi.dev->ss_w(command[1]&4?1:0);fi.st0=command[1]&7;st1=0x00;st2=0x00;for(inti=0;i<4;i++)cur_live.idbuf[i]=0x00;hdl_cb(1);set_ds(command[1]&3);fi.ready=get_ready(command[1]&3);if(!fi.ready){fi.st0|=ST0_NR|ST0_FAIL;fi.sub_state=COMMAND_DONE;read_id_continue(fi);return;}for(inti=0;i<4;i++)cur_live.idbuf[i]=command[i+2];read_id_continue(fi);}voidupd765_family_device::read_id_continue(floppy_info&fi){for(;;){switch(fi.sub_state){caseHEAD_LOAD:LOGSTATE("HEAD_LOAD\n");delay_cycles(fi.tm,500*(spec&0x00fe));fi.sub_state=HEAD_LOAD_DONE;break;caseHEAD_LOAD_DONE:LOGSTATE("HEAD_LOAD_DONE\n");fi.counter=0;fi.sub_state=SCAN_ID;LOGSTATE("SEARCH_ADDRESS_MARK_HEADER\n");live_start(fi,SEARCH_ADDRESS_MARK_HEADER);return;caseSCAN_ID:LOGSTATE("SCAN_ID\n");if(cur_live.crc){fi.st0|=ST0_FAIL;st1|=ST1_MA|ST1_DE|ST1_ND;}fi.sub_state=COMMAND_DONE;break;caseSCAN_ID_FAILED:LOGSTATE("SCAN_ID_FAILED\n");fi.st0|=ST0_FAIL;st1|=ST1_ND|ST1_MA;fi.sub_state=COMMAND_DONE;break;caseCOMMAND_DONE:LOGSTATE("COMMAND_DONE\n");main_phase=PHASE_RESULT;result[0]=fi.st0;result[1]=st1;result[2]=st2;result[3]=cur_live.idbuf[0];result[4]=cur_live.idbuf[1];result[5]=cur_live.idbuf[2];result[6]=cur_live.idbuf[3];result_pos=7;command_end(fi,true);return;default:LOGWARN("%s: read id unknown sub-state %d\n",ttsn(),fi.sub_state);return;}}}voidupd765_family_device::check_irq(){boolold_irq=cur_irq;cur_irq=data_irq||other_irq||internal_drq;cur_irq=cur_irq&&(dor&4)&&(mode!=mode_t::AT||(dor&8));if(cur_irq!=old_irq){LOGTCIRQ("irq = %d\n",cur_irq);intrq_cb(cur_irq);}}boolupd765_family_device::get_irq()const{returncur_irq;}std::stringupd765_family_device::results()const{std::ostringstreamstream;stream<<"results=(";if(!result_pos)stream<<"none";else{stream<<std::hex<<std::setfill('0')<<std::setw(2)<<unsigned(result[0]);for(inti=1;i<result_pos;i++)stream<<','<<std::setw(2)<<unsigned(result[i]);}stream<<')';returnstream.str();}std::stringupd765_family_device::ttsn()const{returnmachine().time().to_string();}voidupd765_family_device::device_timer(emu_timer&timer,device_timer_idid,intparam,void*ptr){if(id==TIMER_DRIVE_READY_POLLING){run_drive_ready_polling();return;}live_sync();floppy_info&fi=flopi[id];switch(fi.sub_state){caseSEEK_WAIT_STEP_SIGNAL_TIME:fi.sub_state=SEEK_WAIT_STEP_SIGNAL_TIME_DONE;break;caseSEEK_WAIT_STEP_TIME:fi.sub_state=SEEK_WAIT_STEP_TIME_DONE;break;}general_continue(fi);}voidupd765_family_device::run_drive_ready_polling(){if(main_phase!=PHASE_CMD||(fifocfg&FIF_POLL)||command_pos)return;for(intfid=0;fid<4;fid++){boolready=get_ready(fid);if(ready!=flopi[fid].ready){LOGCOMMAND("polled %d : %d -> %d\n",fid,flopi[fid].ready,ready);flopi[fid].ready=ready;if(!flopi[fid].st0_filled){flopi[fid].st0=ST0_ABRT|fid;flopi[fid].st0_filled=true;other_irq=true;}}}check_irq();}voidupd765_family_device::index_callback(floppy_image_device*floppy,intstate){LOGSTATE("Index pulse %d\n",state);LOGLIVE("%s: Pulse %d\n",ttsn(),state);for(floppy_info&fi:flopi){if(fi.dev!=floppy)continue;if(fi.live)live_sync();fi.index=state;idx_cb(state);if(!state){general_continue(fi);continue;}switch(fi.sub_state){caseIDLE:caseSEEK_MOVE:caseSEEK_WAIT_STEP_SIGNAL_TIME:caseSEEK_WAIT_STEP_SIGNAL_TIME_DONE:caseSEEK_WAIT_STEP_TIME:caseSEEK_WAIT_STEP_TIME_DONE:caseHEAD_LOAD:caseHEAD_LOAD_DONE:caseSCAN_ID_FAILED:caseSECTOR_READ:break;caseWAIT_INDEX:fi.sub_state=WAIT_INDEX_DONE;break;caseSCAN_ID:fi.counter++;if(fi.counter==2){fi.sub_state=SCAN_ID_FAILED;live_abort();}break;caseTRACK_DONE:live_abort();break;default:LOGWARN("%s: Index pulse on unknown sub-state %d\n",ttsn(),fi.sub_state);break;}general_continue(fi);}}voidupd765_family_device::general_continue(floppy_info&fi){if(fi.live&&cur_live.state!=IDLE){live_run();if(cur_live.state!=IDLE)return;}switch(fi.main_state){caseIDLE:break;caseRECALIBRATE:caseSEEK:seek_continue(fi);break;caseREAD_DATA:caseSCAN_DATA:read_data_continue(fi);break;caseWRITE_DATA:write_data_continue(fi);break;caseREAD_TRACK:read_track_continue(fi);break;caseFORMAT_TRACK:format_track_continue(fi);break;caseREAD_ID:read_id_continue(fi);break;default:LOGWARN("%s: general_continue on unknown main-state %d\n",ttsn(),fi.main_state);break;}}boolupd765_family_device::read_one_bit(constattotime&limit){intbit=cur_live.pll.get_next_bit(cur_live.tm,cur_live.fi->dev,limit);if(bit<0)returntrue;cur_live.shift_reg=(cur_live.shift_reg<<1)|bit;cur_live.bit_counter++;if(cur_live.data_separator_phase){cur_live.data_reg=(cur_live.data_reg<<1)|bit;if((cur_live.crc^(bit?0x8000:0x0000))&0x8000)cur_live.crc=(cur_live.crc<<1)^0x1021;elsecur_live.crc=cur_live.crc<<1;}cur_live.data_separator_phase=!cur_live.data_separator_phase;returnfalse;}boolupd765_family_device::write_one_bit(constattotime&limit){boolbit=cur_live.shift_reg&0x8000;if(cur_live.pll.write_next_bit(bit,cur_live.tm,cur_live.fi->dev,limit))returntrue;if(cur_live.bit_counter&1){if((cur_live.crc^(bit?0x8000:0x0000))&0x8000)cur_live.crc=(cur_live.crc<<1)^0x1021;elsecur_live.crc=cur_live.crc<<1;}cur_live.shift_reg=cur_live.shift_reg<<1;cur_live.bit_counter--;returnfalse;}voidupd765_family_device::live_write_raw(uint16_traw){LOGLIVE("%s: write %04x %04x\n",cur_live.tm.to_string(),raw,cur_live.crc);cur_live.shift_reg=raw;cur_live.data_bit_context=raw&1;}voidupd765_family_device::live_write_mfm(uint8_tmfm){boolcontext=cur_live.data_bit_context;uint16_traw=0;for(inti=0;i<8;i++){boolbit=mfm&(0x80>>i);if(!(bit||context))raw|=0x8000>>(2*i);if(bit)raw|=0x4000>>(2*i);context=bit;}cur_live.data_reg=mfm;cur_live.shift_reg=raw;cur_live.data_bit_context=context;LOGLIVE("%s: write %02x %04x %04x\n",cur_live.tm.to_string(),mfm,cur_live.crc,raw);}voidupd765_family_device::live_write_fm(uint8_tfm){uint16_traw=0xaaaa;for(inti=0;i<8;i++)if(fm&(0x80>>i))raw|=0x4000>>(2*i);cur_live.data_reg=fm;cur_live.shift_reg=raw;cur_live.data_bit_context=fm&1;LOGLIVE("%s: write %02x %04x %04x\n",cur_live.tm.to_string(),fm,cur_live.crc,raw);}boolupd765_family_device::sector_matches()const{LOGMATCH("matching %02x %02x %02x %02x - %02x %02x %02x %02x\n",cur_live.idbuf[0],cur_live.idbuf[1],cur_live.idbuf[2],cur_live.idbuf[3],command[2],command[3],command[4],command[5]);returncur_live.idbuf[0]==command[2]&&cur_live.idbuf[1]==command[3]&&cur_live.idbuf[2]==command[4]&&cur_live.idbuf[3]==command[5];}upd765a_device::upd765a_device(constmachine_config&mconfig,constchar*tag,device_t*owner,uint32_tclock):upd765_family_device(mconfig,UPD765A,tag,owner,clock){has_dor=false;}upd765b_device::upd765b_device(constmachine_config&mconfig,constchar*tag,device_t*owner,uint32_tclock):upd765_family_device(mconfig,UPD765B,tag,owner,clock){has_dor=false;}i8272a_device::i8272a_device(constmachine_config&mconfig,constchar*tag,device_t*owner,uint32_tclock):upd765_family_device(mconfig,I8272A,tag,owner,clock){has_dor=false;}upd72065_device::upd72065_device(constmachine_config&mconfig,constchar*tag,device_t*owner,uint32_tclock):upd72065_device(mconfig,UPD72065,tag,owner,clock){}upd72065_device::upd72065_device(constmachine_config&mconfig,device_typetype,constchar*tag,device_t*owner,uint32_tclock):upd765_family_device(mconfig,type,tag,owner,clock){has_dor=false;recalibrate_steps=255;}upd72067_device::upd72067_device(constmachine_config&mconfig,constchar*tag,device_t*owner,uint32_tclock):upd72065_device(mconfig,UPD72067,tag,owner,clock){ready_polled=true;ready_connected=true;select_connected=true;select_multiplexed=false;}upd72069_device::upd72069_device(constmachine_config&mconfig,constchar*tag,device_t*owner,uint32_tclock):upd72065_device(mconfig,UPD72069,tag,owner,clock){ready_polled=true;ready_connected=true;select_connected=true;select_multiplexed=false;}i82072_device::i82072_device(constmachine_config&mconfig,constchar*tag,device_t*owner,uint32_tclock):upd765_family_device(mconfig,I82072,tag,owner,clock){has_dor=false;recalibrate_steps=255;}voidi82072_device::device_start(){upd765_family_device::device_start();save_item(NAME(motorcfg));save_item(NAME(motor_off_counter));save_item(NAME(motor_on_counter));save_item(NAME(drive_busy));save_item(NAME(delayed_command));}voidi82072_device::soft_reset(){motorcfg=0x60;upd765_family_device::soft_reset();}inti82072_device::check_command(){// ...00110 read data// ...01100 read deleted data// ..000101 write data// ..001001 write deleted data// 0.000010 read track// 0.001010 read id// 0.001101 format track// 00000111 recalibrate// 00001000 sense interrupt status// 00000011 specify// 00000100 sense drive status// 00001111 seek// 00010011 configure// ...01011 motor on/off// 1.001111 relative seek// 00001110 dumpregswitch(command[0]&0x1f){case0x02:returncommand_pos==9?C_READ_TRACK:C_INCOMPLETE;case0x03:returncommand_pos==3?C_SPECIFY:C_INCOMPLETE;case0x04:returncommand_pos==2?C_SENSE_DRIVE_STATUS:C_INCOMPLETE;case0x05:case0x09:returncommand_pos==9?C_WRITE_DATA:C_INCOMPLETE;case0x06:case0x0c:returncommand_pos==9?C_READ_DATA:C_INCOMPLETE;case0x07:returncommand_pos==2?C_RECALIBRATE:C_INCOMPLETE;case0x08:returnC_SENSE_INTERRUPT_STATUS;case0x0a:returncommand_pos==2?C_READ_ID:C_INCOMPLETE;case0x0b:returnC_MOTOR_ONOFF;case0x0d:returncommand_pos==6?C_FORMAT_TRACK:C_INCOMPLETE;case0x0e:returnC_DUMP_REG;case0x0f:returncommand_pos==3?C_SEEK:C_INCOMPLETE;case0x13:returncommand_pos==4?C_CONFIGURE:C_INCOMPLETE;default:returnC_INVALID;}}voidi82072_device::start_command(intcmd){// check if the command specifies a target driveswitch(cmd){caseC_READ_TRACK:caseC_SENSE_DRIVE_STATUS:caseC_WRITE_DATA:caseC_READ_DATA:caseC_RECALIBRATE://case C_WRITE_DELETED_DATA:caseC_READ_ID://case C_READ_DELETED_DATA:caseC_FORMAT_TRACK:caseC_SEEK:// start the motormotor_control(command[1]&0x3,true);break;}// execute the command immediately if there's no motor on delayif(motor_on_counter==0){upd765_family_device::start_command(cmd);// set motor off counter if command execution has completedif(main_phase!=PHASE_EXEC&&motorcfg)motor_off_counter=(2+((motorcfg&MOFF)>>4))<<(motorcfg&HSDA?1:0);}elsedelayed_command=cmd;}voidi82072_device::execute_command(intcmd){switch(cmd){caseC_CONFIGURE:LOGCOMMAND("command configure %02x %02x %02x\n",command[1],command[2],command[3]);motorcfg=command[1];fifocfg=command[2];precomp=command[3];main_phase=PHASE_CMD;break;caseC_DUMP_REG:LOGCOMMAND("command dump regs\n");main_phase=PHASE_RESULT;result[0]=flopi[0].pcn;result[1]=flopi[1].pcn;result[2]=flopi[2].pcn;result[3]=flopi[3].pcn;result[4]=(spec&0xff00)>>8;result[5]=(spec&0x00ff);result[6]=sector_size;// i82072 dumps motor configuration at offset 7result[7]=motorcfg;result[8]=fifocfg;result[9]=precomp;result_pos=10;break;caseC_MOTOR_ONOFF:{boolmotor_on=command[0]&0x80;floppy_info&fi=flopi[(command[0]>>5)&0x3];LOGCOMMAND("command motor %s drive %d\n",motor_on?"on":"off",fi.id);// if we are selecting a different drive, stop the motor on the previously selected driveif(selected_drive!=fi.id&&flopi[selected_drive].dev&&flopi[selected_drive].dev->mon_r()==0)flopi[selected_drive].dev->mon_w(1);// select the driveif(motor_on)set_ds(fi.id);// start the motorif(fi.dev)fi.dev->mon_w(motor_on?0:1);main_phase=PHASE_CMD;break;}default:upd765_family_device::execute_command(cmd);break;}}/* * The Intel datasheet says that the drive busy bits in the MSR are supposed to remain * set after a seek or recalibrate until a sense interrupt status status command is * executed. The InterPro 2000 diagnostic routine goes further, and tests the drive * status bits before and after the first sense interrupt status result byte is read, * and expects the drive busy bit to clear only after. * * The Amstrad CPC6128 uses a upd765a and seems to expect the busy bits to be cleared * immediately after the seek/recalibrate interrupt is generated. * * Special casing the i82072 here seems the only way to reconcile this apparently * different behaviour for now. */voidi82072_device::command_end(floppy_info&fi,booldata_completion){if(!data_completion)drive_busy|=(1<<fi.id);// set motor off counterif(motorcfg)motor_off_counter=(2+((motorcfg&MOFF)>>4))<<(motorcfg&HSDA?1:0);// clear existing interrupt sense datafor(floppy_info&fi:flopi)fi.st0_filled=false;upd765_family_device::command_end(fi,data_completion);}voidi82072_device::motor_control(intfid,boolstart_motor){// check if motor control is enabledif(motorcfg==0)return;floppy_info&fi=flopi[fid];if(start_motor){// if we are selecting a different drive, stop the motor on the previously selected driveif(selected_drive!=fid&&flopi[selected_drive].dev&&flopi[selected_drive].dev->mon_r()==0)flopi[selected_drive].dev->mon_w(1);// start the motor on the selected driveif(fi.dev&&fi.dev->mon_r()==1){LOGCOMMAND("motor_control: switching on motor for drive %d\n",fid);// select the drive and enable the motorset_ds(fid);fi.dev->mon_w(0);// set motor on countermotor_on_counter=(motorcfg&MON)<<(motorcfg&HSDA?1:0);}}else{// motor off timer only applies to the selected driveif(selected_drive!=fid)return;// decrement motor on counterif(motor_on_counter)motor_on_counter--;// execute the command if the motor on counter has expiredif(motor_on_counter==0&&main_phase==PHASE_CMD&&delayed_command){upd765_family_device::start_command(delayed_command);// set motor off counter if command execution has completedif(main_phase!=PHASE_EXEC&&motorcfg)motor_off_counter=(2+((motorcfg&MOFF)>>4))<<(motorcfg&HSDA?1:0);delayed_command=0;return;}// ignore motor off timer while drive is busyif(fi.main_state==SEEK||fi.main_state==RECALIBRATE)return;// check if the motor is already offif(motor_off_counter==0||(fi.dev&&fi.dev->mon_r()==1))return;// decrement the countermotor_off_counter--;// if the motor off timer has expired, stop the motorif(motor_off_counter==0&&fi.dev){LOGCOMMAND("motor_control: switching off motor for drive %d\n",fid);fi.dev->mon_w(1);}}}voidi82072_device::index_callback(floppy_image_device*floppy,intstate){if(state)for(floppy_info&fi:flopi){if(fi.dev!=floppy)continue;// update motor on/off counters and stop motor if necessarymotor_control(fi.id,false);}upd765_family_device::index_callback(floppy,state);}ps2_fdc_device::ps2_fdc_device(constmachine_config&mconfig,device_typetype,constchar*tag,device_t*owner,uint32_tclock):upd765_family_device(mconfig,type,tag,owner,clock){}voidps2_fdc_device::device_start(){upd765_family_device::device_start();save_item(NAME(perpmode));}voidps2_fdc_device::device_reset(){upd765_family_device::device_reset();perpmode=0;}voidps2_fdc_device::soft_reset(){upd765_family_device::soft_reset();perpmode&=0x3c;}smc37c78_device::smc37c78_device(constmachine_config&mconfig,constchar*tag,device_t*owner,uint32_tclock):ps2_fdc_device(mconfig,SMC37C78,tag,owner,clock){ready_connected=false;select_connected=true;select_multiplexed=false;recalibrate_steps=80;}n82077aa_device::n82077aa_device(constmachine_config&mconfig,constchar*tag,device_t*owner,uint32_tclock):ps2_fdc_device(mconfig,N82077AA,tag,owner,clock){ready_connected=false;select_connected=true;select_multiplexed=false;}pc_fdc_superio_device::pc_fdc_superio_device(constmachine_config&mconfig,constchar*tag,device_t*owner,uint32_tclock):upd765_family_device(mconfig,PC_FDC_SUPERIO,tag,owner,clock){ready_polled=false;ready_connected=false;select_connected=true;select_multiplexed=false;}dp8473_device::dp8473_device(constmachine_config&mconfig,constchar*tag,device_t*owner,uint32_tclock):upd765_family_device(mconfig,DP8473,tag,owner,clock){ready_polled=false;ready_connected=false;select_connected=true;select_multiplexed=false;recalibrate_steps=77;// TODO: 3917 in extended track range mode}pc8477a_device::pc8477a_device(constmachine_config&mconfig,constchar*tag,device_t*owner,uint32_tclock):ps2_fdc_device(mconfig,PC8477A,tag,owner,clock){ready_polled=true;ready_connected=false;select_connected=true;select_multiplexed=false;recalibrate_steps=85;// TODO: may also be programmed as 255, 3925 or 4095 by (unemulated) mode command}wd37c65c_device::wd37c65c_device(constmachine_config&mconfig,constchar*tag,device_t*owner,uint32_tclock):upd765_family_device(mconfig,WD37C65C,tag,owner,clock),m_clock2(0){ready_polled=true;ready_connected=false;select_connected=true;select_multiplexed=false;(void)m_clock2;// TODO}uint8_twd37c65c_device::get_st3(floppy_info&fi){uint8_tst3=command[1]&7;st3|=0x20;if(fi.dev)st3|=(fi.dev->wpt_r()?0x48:0x00)|(fi.dev->trk00_r()?0x00:0x10);returnst3;}mcs3201_device::mcs3201_device(constmachine_config&mconfig,constchar*tag,device_t*owner,uint32_tclock):upd765_family_device(mconfig,MCS3201,tag,owner,clock),m_input_handler(*this){has_dor=true;ready_polled=false;ready_connected=false;select_connected=true;select_multiplexed=false;}voidmcs3201_device::device_start(){upd765_family_device::device_start();m_input_handler.resolve_safe(0);}uint8_tmcs3201_device::input_r(){returnm_input_handler();}tc8566af_device::tc8566af_device(constmachine_config&mconfig,constchar*tag,device_t*owner,uint32_tclock):upd765_family_device(mconfig,TC8566AF,tag,owner,clock),m_cr1(0){ready_polled=true;ready_connected=true;select_connected=true;select_multiplexed=false;recalibrate_steps=255;}voidtc8566af_device::device_start(){upd765_family_device::device_start();save_item(NAME(m_cr1));}voidtc8566af_device::cr1_w(uint8_tdata){m_cr1=data;if(m_cr1&0x02){// Not sure if this inverted or nottc_w((m_cr1&0x01)?true:false);}}voidupd72065_device::auxcmd_w(uint8_tdata){switch(data){case0x36:// resetsoft_reset();break;case0x35:// set standbybreak;case0x34:// reset standbybreak;}}voidupd72067_device::auxcmd_w(uint8_tdata){/* * Auxiliary commands: * * ???? ???? start clock * 0011 0011 enable external mode * 0x00 1011 control internal mode * xxxx 1110 enable motors * 010x 1111 select IBM or ECMA/ISO format * * The bare minimum needed to satisfy the news_r3k diagnostic has been * implemented here. */switch(data&0x0f){case0x0e:// motor on/off - no idea about timeoutfor(unsignedi=0;i<4;i++)if(flopi[i].dev)flopi[i].dev->mon_w(!BIT(data,i+4));[[fallthrough]];case0x03:// enable external modecase0x0b:// control internal modecase0x0f:// select format// report a successful statusmain_phase=PHASE_RESULT;result[0]=ST0_UNK;result_pos=1;break;default:upd72065_device::auxcmd_w(data);break;}}