123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091 |
- "use strict";
- /** @const */
- var CDROM_SECTOR_SIZE = 2048;
- /** @const */
- var HD_SECTOR_SIZE = 512;
- /**
- * @constructor
- * @param {CPU} cpu
- * @param {boolean} is_cd
- * @param {number} nr
- * @param {BusConnector} bus
- * */
- function IDEDevice(cpu, master_buffer, slave_buffer, is_cd, nr, bus)
- {
- this.master = new IDEInterface(this, cpu, master_buffer, is_cd, nr, 0, bus);
- this.slave = new IDEInterface(this, cpu, slave_buffer, false, nr, 1, bus);
- this.current_interface = this.master;
- this.cpu = cpu;
- // gets set via PCI in seabios, likely doesn't matter
- if(nr === 0)
- {
- this.ata_port = 0x1F0;
- this.irq = 14;
- this.pci_id = 0x1E << 3;
- }
- else if(nr === 1)
- {
- this.ata_port = 0x170;
- this.irq = 15;
- this.pci_id = 0x1F << 3;
- }
- else
- {
- dbg_assert(false, "IDE device with nr " + nr + " ignored", LOG_DISK);
- }
- // alternate status, starting at 3f4/374
- /** @type {number} */
- this.ata_port_high = this.ata_port | 0x204;
- /** @type {number} */
- this.master_port = 0xB400;
- this.pci_space = [
- 0x86, 0x80, 0x10, 0x70, 0x05, 0x00, 0xA0, 0x02,
- 0x00, 0x80, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00,
- this.ata_port & 0xFF | 1, this.ata_port >> 8, 0x00, 0x00,
- this.ata_port_high & 0xFF | 1, this.ata_port_high >> 8, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, // second device
- 0x00, 0x00, 0x00, 0x00, // second device
- this.master_port & 0xFF | 1, this.master_port >> 8, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x43, 0x10, 0xD4, 0x82,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, this.irq, 0x01, 0x00, 0x00,
- // 0x40
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- // 0x80
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- ];
- this.pci_bars = [
- {
- size: 8,
- },
- {
- size: 4,
- },
- undefined,
- undefined,
- {
- size: 0x10,
- },
- ];
- this.name = "ide" + nr;
- /** @type {number} */
- this.device_control = 2;
- // status
- cpu.io.register_read(this.ata_port | 7, this, function() {
- dbg_log("lower irq", LOG_DISK);
- this.cpu.device_lower_irq(this.irq);
- return this.read_status();
- });
- cpu.io.register_read(this.ata_port_high | 2, this, this.read_status);
- cpu.io.register_write(this.ata_port_high | 2, this, this.write_control);
- cpu.io.register_read(this.ata_port | 0, this, function()
- {
- return this.current_interface.read_data(1);
- }, function()
- {
- return this.current_interface.read_data(2);
- }, function()
- {
- return this.current_interface.read_data(4);
- });
- cpu.io.register_read(this.ata_port | 1, this, function()
- {
- dbg_log("Read error: " + h(this.current_interface.error & 0xFF) +
- " slave=" + (this.current_interface === this.slave), LOG_DISK);
- return this.current_interface.error & 0xFF;
- });
- cpu.io.register_read(this.ata_port | 2, this, function()
- {
- dbg_log("Read bytecount: " + h(this.current_interface.bytecount & 0xFF), LOG_DISK);
- return this.current_interface.bytecount & 0xFF;
- });
- cpu.io.register_read(this.ata_port | 3, this, function()
- {
- dbg_log("Read sector: " + h(this.current_interface.sector & 0xFF), LOG_DISK);
- return this.current_interface.sector & 0xFF;
- });
- cpu.io.register_read(this.ata_port | 4, this, function()
- {
- dbg_log("Read 1F4: " + h(this.current_interface.cylinder_low & 0xFF), LOG_DISK);
- return this.current_interface.cylinder_low & 0xFF;
- });
- cpu.io.register_read(this.ata_port | 5, this, function()
- {
- dbg_log("Read 1F5: " + h(this.current_interface.cylinder_high & 0xFF), LOG_DISK);
- return this.current_interface.cylinder_high & 0xFF;
- });
- cpu.io.register_read(this.ata_port | 6, this, function()
- {
- dbg_log("Read 1F6", LOG_DISK);
- return this.current_interface.drive_head & 0xFF;
- });
- cpu.io.register_write(this.ata_port | 0, this, function(data)
- {
- this.current_interface.write_data_port8(data);
- }, function(data)
- {
- this.current_interface.write_data_port16(data);
- }, function(data)
- {
- this.current_interface.write_data_port32(data);
- });
- cpu.io.register_write(this.ata_port | 1, this, function(data)
- {
- dbg_log("1F1/lba_count: " + h(data), LOG_DISK);
- this.master.lba_count = (this.master.lba_count << 8 | data) & 0xFFFF;
- this.slave.lba_count = (this.slave.lba_count << 8 | data) & 0xFFFF;
- });
- cpu.io.register_write(this.ata_port | 2, this, function(data)
- {
- dbg_log("1F2/bytecount: " + h(data), LOG_DISK);
- this.master.bytecount = (this.master.bytecount << 8 | data) & 0xFFFF;
- this.slave.bytecount = (this.slave.bytecount << 8 | data) & 0xFFFF;
- });
- cpu.io.register_write(this.ata_port | 3, this, function(data)
- {
- dbg_log("1F3/sector: " + h(data), LOG_DISK);
- this.master.sector = (this.master.sector << 8 | data) & 0xFFFF;
- this.slave.sector = (this.slave.sector << 8 | data) & 0xFFFF;
- });
- cpu.io.register_write(this.ata_port | 4, this, function(data)
- {
- dbg_log("1F4/sector low: " + h(data), LOG_DISK);
- this.master.cylinder_low = (this.master.cylinder_low << 8 | data) & 0xFFFF;
- this.slave.cylinder_low = (this.slave.cylinder_low << 8 | data) & 0xFFFF;
- });
- cpu.io.register_write(this.ata_port | 5, this, function(data)
- {
- dbg_log("1F5/sector high: " + h(data), LOG_DISK);
- this.master.cylinder_high = (this.master.cylinder_high << 8 | data) & 0xFFFF;
- this.slave.cylinder_high = (this.slave.cylinder_high << 8 | data) & 0xFFFF;
- });
- cpu.io.register_write(this.ata_port | 6, this, function(data)
- {
- var slave = data & 0x10;
- var mode = data & 0xE0;
- dbg_log("1F6/drive: " + h(data, 2), LOG_DISK);
- if(slave)
- {
- dbg_log("Slave", LOG_DISK);
- this.current_interface = this.slave;
- }
- else
- {
- this.current_interface = this.master;
- }
- this.master.drive_head = data;
- this.slave.drive_head = data;
- this.master.is_lba = this.slave.is_lba = data >> 6 & 1;
- this.master.head = this.slave.head = data & 0xF;
- });
- /** @type {number} */
- this.prdt_addr = 0;
- /** @type {number} */
- this.dma_status = 0;
- /** @type {number} */
- this.dma_command = 0;
- cpu.io.register_write(this.ata_port | 7, this, function(data)
- {
- dbg_log("lower irq", LOG_DISK);
- this.cpu.device_lower_irq(this.irq);
- this.current_interface.ata_command(data);
- });
- cpu.io.register_read(this.master_port | 4, this, undefined, undefined, this.dma_read_addr);
- cpu.io.register_write(this.master_port | 4, this, undefined, undefined, this.dma_set_addr);
- cpu.io.register_read(this.master_port, this,
- this.dma_read_command8, undefined, this.dma_read_command);
- cpu.io.register_write(this.master_port, this,
- this.dma_write_command8, undefined, this.dma_write_command);
- cpu.io.register_read(this.master_port | 2, this, this.dma_read_status);
- cpu.io.register_write(this.master_port | 2, this, this.dma_write_status);
- cpu.io.register_read(this.master_port | 0x8, this, function() {
- dbg_log("DMA read 0x8", LOG_DISK); return 0;
- });
- cpu.io.register_read(this.master_port | 0xA, this, function() {
- dbg_log("DMA read 0xA", LOG_DISK); return 0;
- });
- cpu.devices.pci.register_device(this);
- DEBUG && Object.seal(this);
- }
- IDEDevice.prototype.read_status = function()
- {
- if(this.current_interface.buffer)
- {
- var ret = this.current_interface.status;
- dbg_log("ATA read status: " + h(ret, 2), LOG_DISK);
- return ret;
- }
- else
- {
- return 0;
- }
- };
- IDEDevice.prototype.write_control = function(data)
- {
- dbg_log("set device control: " + h(data, 2) + " interrupts " +
- ((data & 2) ? "disabled" : "enabled"), LOG_DISK);
- if(data & 4)
- {
- dbg_log("Reset via control port", LOG_DISK);
- this.cpu.device_lower_irq(this.irq);
- this.master.device_reset();
- this.slave.device_reset();
- }
- this.device_control = data;
- };
- IDEDevice.prototype.dma_read_addr = function()
- {
- dbg_log("dma get address: " + h(this.prdt_addr, 8), LOG_DISK);
- return this.prdt_addr;
- };
- IDEDevice.prototype.dma_set_addr = function(data)
- {
- dbg_log("dma set address: " + h(data, 8), LOG_DISK);
- this.prdt_addr = data;
- };
- IDEDevice.prototype.dma_read_status = function()
- {
- dbg_log("DMA read status: " + h(this.dma_status), LOG_DISK);
- return this.dma_status;
- };
- IDEDevice.prototype.dma_write_status = function(value)
- {
- dbg_log("DMA set status: " + h(value), LOG_DISK);
- this.dma_status &= ~(value & 6);
- };
- IDEDevice.prototype.dma_read_command = function()
- {
- return this.dma_read_command8() | this.dma_read_status() << 16;
- };
- IDEDevice.prototype.dma_read_command8 = function()
- {
- dbg_log("DMA read command: " + h(this.dma_command), LOG_DISK);
- return this.dma_command;
- };
- IDEDevice.prototype.dma_write_command = function(value)
- {
- dbg_log("DMA write command: " + h(value), LOG_DISK);
- this.dma_write_command8(value & 0xFF);
- this.dma_write_status(value >> 16 & 0xFF);
- };
- IDEDevice.prototype.dma_write_command8 = function(value)
- {
- dbg_log("DMA write command8: " + h(value), LOG_DISK);
- let old_command = this.dma_command;
- this.dma_command = value & 0x9;
- if((old_command & 1) === (value & 1))
- {
- return;
- }
- if((value & 1) === 0)
- {
- this.dma_status &= ~1;
- return;
- }
- this.dma_status |= 1;
- switch(this.current_interface.current_command)
- {
- case 0x25:
- case 0xC8:
- this.current_interface.do_ata_read_sectors_dma();
- break;
- case 0xCA:
- case 0x35:
- this.current_interface.do_ata_write_sectors_dma();
- break;
- case 0xA0:
- this.current_interface.do_atapi_dma();
- break;
- default:
- dbg_log("Spurious dma command write, current command: " +
- h(this.current_interface.current_command), LOG_DISK);
- dbg_assert(false);
- }
- };
- IDEDevice.prototype.push_irq = function()
- {
- if((this.device_control & 2) === 0)
- {
- dbg_log("push irq", LOG_DISK);
- this.dma_status |= 4;
- this.cpu.device_raise_irq(this.irq);
- }
- };
- IDEDevice.prototype.get_state = function()
- {
- var state = [];
- state[0] = this.master;
- state[1] = this.slave;
- state[2] = this.ata_port;
- state[3] = this.irq;
- state[4] = this.pci_id;
- state[5] = this.ata_port_high;
- state[6] = this.master_port;
- state[7] = this.name;
- state[8] = this.device_control;
- state[9] = this.prdt_addr;
- state[10] = this.dma_status;
- state[11] = this.current_interface === this.master;
- state[12] = this.dma_command;
- return state;
- };
- IDEDevice.prototype.set_state = function(state)
- {
- this.master.set_state(state[0]);
- this.slave.set_state(state[1]);
- this.ata_port = state[2];
- this.irq = state[3];
- this.pci_id = state[4];
- this.ata_port_high = state[5];
- this.master_port = state[6];
- this.name = state[7];
- this.device_control = state[8];
- this.prdt_addr = state[9];
- this.dma_status = state[10];
- this.current_interface = state[11] ? this.master : this.slave;
- this.dma_command = state[12];
- };
- /**
- * @constructor
- */
- function IDEInterface(device, cpu, buffer, is_cd, device_nr, interface_nr, bus)
- {
- this.device = device;
- /** @const @type {BusConnector} */
- this.bus = bus;
- /**
- * @const
- * @type {number}
- */
- this.nr = device_nr;
- /** @const @type {CPU} */
- this.cpu = cpu;
- this.buffer = buffer;
- /** @type {number} */
- this.sector_size = is_cd ? CDROM_SECTOR_SIZE : HD_SECTOR_SIZE;
- /** @type {boolean} */
- this.is_atapi = is_cd;
- /** @type {number} */
- this.sector_count = 0;
- /** @type {number} */
- this.head_count = 0;
- /** @type {number} */
- this.sectors_per_track = 0;
- /** @type {number} */
- this.cylinder_count = 0;
- if(this.buffer)
- {
- this.sector_count = this.buffer.byteLength / this.sector_size;
- if(this.sector_count !== (this.sector_count | 0))
- {
- dbg_log("Warning: Disk size not aligned with sector size", LOG_DISK);
- this.sector_count = Math.ceil(this.sector_count);
- }
- if(is_cd)
- {
- this.head_count = 1;
- this.sectors_per_track = 0;
- }
- else
- {
- // "default" values: 16/63
- // common: 255, 63
- this.head_count = 16;
- this.sectors_per_track = 63;
- }
- this.cylinder_count = this.sector_count / this.head_count / this.sectors_per_track;
- if(this.cylinder_count !== (this.cylinder_count | 0))
- {
- dbg_log("Warning: Rounding up cylinder count. Choose different head number", LOG_DISK);
- this.cylinder_count = Math.floor(this.cylinder_count);
- //this.sector_count = this.cylinder_count * this.head_count *
- // this.sectors_per_track * this.sector_size;
- }
- //if(this.cylinder_count > 16383)
- //{
- // this.cylinder_count = 16383;
- //}
- // disk translation: lba
- var rtc = cpu.devices.rtc;
- // master
- rtc.cmos_write(CMOS_BIOS_DISKTRANSFLAG,
- rtc.cmos_read(CMOS_BIOS_DISKTRANSFLAG) | 1 << this.nr * 4);
- rtc.cmos_write(CMOS_DISK_DATA, rtc.cmos_read(CMOS_DISK_DATA) & 0x0F | 0xF0);
- var reg = CMOS_DISK_DRIVE1_CYL;
- rtc.cmos_write(reg + 0, this.cylinder_count & 0xFF);
- rtc.cmos_write(reg + 1, this.cylinder_count >> 8 & 0xFF);
- rtc.cmos_write(reg + 2, this.head_count & 0xFF);
- rtc.cmos_write(reg + 3, 0xFF);
- rtc.cmos_write(reg + 4, 0xFF);
- rtc.cmos_write(reg + 5, 0xC8);
- rtc.cmos_write(reg + 6, this.cylinder_count & 0xFF);
- rtc.cmos_write(reg + 7, this.cylinder_count >> 8 & 0xFF);
- rtc.cmos_write(reg + 8, this.sectors_per_track & 0xFF);
- //rtc.cmos_write(CMOS_BIOS_DISKTRANSFLAG,
- // rtc.cmos_read(CMOS_BIOS_DISKTRANSFLAG) | 1 << (nr * 4 + 2)); // slave
- }
- /** @const */
- this.stats = {
- sectors_read: 0,
- sectors_written: 0,
- bytes_read: 0,
- bytes_written: 0,
- loading: false,
- };
- this.buffer = buffer;
- /** @type {number} */
- this.is_lba = 0;
- /** @type {number} */
- this.bytecount = 0;
- /** @type {number} */
- this.sector = 0;
- /** @type {number} */
- this.lba_count = 0;
- /** @type {number} */
- this.cylinder_low = 0;
- /** @type {number} */
- this.cylinder_high = 0;
- /** @type {number} */
- this.head = 0;
- /** @type {number} */
- this.drive_head = 0;
- /** @type {number} */
- this.status = 0x50;
- /** @type {number} */
- this.sectors_per_drq = 0x80;
- /** @type {number} */
- this.error = 0;
- /** @type {number} */
- this.data_pointer = 0;
- this.data = new Uint8Array(64 * 1024);
- this.data16 = new Uint16Array(this.data.buffer);
- this.data32 = new Int32Array(this.data.buffer);
- /** @type {number} */
- this.data_length = 0;
- /** @type {number} */
- this.data_end = 0;
- /** @type {number} */
- this.current_command = -1;
- /** @type {number} */
- this.current_atapi_command = -1;
- /** @type {number} */
- this.write_dest = 0;
- // cancellation support
- this.last_io_id = 0;
- this.in_progress_io_ids = new Set();
- this.cancelled_io_ids = new Set();
- Object.seal(this);
- }
- IDEInterface.prototype.device_reset = function()
- {
- if(this.is_atapi)
- {
- this.status = 0;
- this.bytecount = 1;
- this.error = 1;
- this.sector = 1; // lba_low
- this.cylinder_low = 0x14; // lba_mid
- this.cylinder_high = 0xEB; // lba_high
- }
- else
- {
- this.status = 0x50 | 1;
- this.bytecount = 1;
- this.error = 1;
- this.sector = 1; // lba_low
- // 0, 0 needed by bochs bios
- this.cylinder_low = 0; // lba_mid
- this.cylinder_high = 0; // lba_high
- }
- this.cancel_io_operations();
- };
- IDEInterface.prototype.push_irq = function()
- {
- this.device.push_irq();
- };
- IDEInterface.prototype.ata_command = function(cmd)
- {
- dbg_log("ATA Command: " + h(cmd) + " slave=" + (this.drive_head >> 4 & 1), LOG_DISK);
- if(!this.buffer)
- {
- dbg_log("abort: No buffer", LOG_DISK);
- this.error = 4;
- this.status = 0x41;
- this.push_irq();
- return;
- }
- this.current_command = cmd;
- this.error = 0;
- switch(cmd)
- {
- case 0x08:
- dbg_log("ATA device reset", LOG_DISK);
- this.data_pointer = 0;
- this.data_end = 0;
- this.data_length = 0;
- this.device_reset();
- this.push_irq();
- break;
- case 0x10:
- // calibrate drive
- this.status = 0x50;
- this.cylinder_low = 0;
- this.push_irq();
- break;
- case 0xF8:
- // read native max address
- this.status = 0x50;
- var last_sector = this.sector_count - 1;
- this.sector = last_sector & 0xFF;
- this.cylinder_low = last_sector >> 8 & 0xFF;
- this.cylinder_high = last_sector >> 16 & 0xFF;
- this.drive_head = this.drive_head & 0xF0 | last_sector >> 24 & 0x0F;
- this.push_irq();
- break;
- case 0x27:
- // read native max address ext
- this.status = 0x50;
- var last_sector = this.sector_count - 1;
- this.sector = last_sector & 0xFF;
- this.cylinder_low = last_sector >> 8 & 0xFF;
- this.cylinder_high = last_sector >> 16 & 0xFF;
- this.sector |= last_sector >> 24 << 8 & 0xFF00;
- this.push_irq();
- break;
- case 0x20:
- case 0x24:
- case 0x29:
- case 0xC4:
- // 0x20 read sectors
- // 0x24 read sectors ext
- // 0xC4 read multiple
- // 0x29 read multiple ext
- this.ata_read_sectors(cmd);
- break;
- case 0x30:
- case 0x34:
- case 0x39:
- case 0xC5:
- // 0x30 write sectors
- // 0x34 write sectors ext
- // 0xC5 write multiple
- // 0x39 write multiple ext
- this.ata_write_sectors(cmd);
- break;
- case 0x90:
- // execute device diagnostic
- this.push_irq();
- this.error = 0x101;
- this.status = 0x50;
- break;
- case 0x91:
- // initialize device parameters
- this.status = 0x50;
- this.push_irq();
- break;
- case 0xA0:
- // ATA packet
- if(this.is_atapi)
- {
- this.status = 0x58;
- this.data_allocate(12);
- this.data_end = 12;
- this.bytecount = 1;
- this.push_irq();
- }
- break;
- case 0xA1:
- dbg_log("ATA identify packet device", LOG_DISK);
- if(this.is_atapi)
- {
- this.create_identify_packet();
- this.status = 0x58;
- this.cylinder_low = 0x14;
- this.cylinder_high = 0xEB;
- this.push_irq();
- }
- else
- {
- this.status = 0x41;
- this.push_irq();
- }
- break;
- case 0xC6:
- // set multiple mode
- // Logical sectors per DRQ Block in word 1
- dbg_log("Logical sectors per DRQ Block: " + h(this.bytecount & 0xFF), LOG_DISK);
- this.sectors_per_drq = this.bytecount & 0xFF;
- this.status = 0x50;
- this.push_irq();
- break;
- case 0x25: // read dma ext
- case 0xC8: // read dma
- this.ata_read_sectors_dma(cmd);
- break;
- case 0x35: // write dma ext
- case 0xCA: // write dma
- this.ata_write_sectors_dma(cmd);
- break;
- case 0x40:
- dbg_log("read verify sectors", LOG_DISK);
- this.status = 0x50;
- this.push_irq();
- break;
- case 0xDA:
- dbg_log("Unimplemented: get media status", LOG_DISK);
- this.status = 0x41;
- this.error = 4;
- this.push_irq();
- break;
- case 0xE0:
- dbg_log("ATA standby immediate", LOG_DISK);
- this.status = 0x50;
- this.push_irq();
- break;
- case 0xE1:
- dbg_log("ATA idle immediate", LOG_DISK);
- this.status = 0x50;
- this.push_irq();
- break;
- case 0xE7:
- dbg_log("ATA flush cache", LOG_DISK);
- this.status = 0x50;
- this.push_irq();
- break;
- case 0xEC:
- dbg_log("ATA identify device", LOG_DISK);
- if(this.is_atapi)
- {
- this.status = 0x41;
- this.error = 4;
- this.push_irq();
- return;
- }
- this.create_identify_packet();
- this.status = 0x58;
- this.push_irq();
- break;
- case 0xEA:
- dbg_log("flush cache ext", LOG_DISK);
- this.status = 0x50;
- this.push_irq();
- break;
- case 0xEF:
- dbg_log("set features: " + h(this.bytecount & 0xFF), LOG_DISK);
- this.status = 0x50;
- this.push_irq();
- break;
- case 0xDE:
- // obsolete
- this.status = 0x50;
- this.push_irq();
- break;
- case 0xF5:
- dbg_log("security freeze lock", LOG_DISK);
- this.status = 0x50;
- this.push_irq();
- break;
- case 0xF9:
- dbg_log("Unimplemented: set max address", LOG_DISK);
- this.status = 0x41;
- this.error = 4;
- break;
- default:
- dbg_assert(false, "New ATA cmd on 1F7: " + h(cmd), LOG_DISK);
- this.status = 0x41;
- // abort bit set
- this.error = 4;
- }
- };
- IDEInterface.prototype.atapi_handle = function()
- {
- dbg_log("ATAPI Command: " + h(this.data[0]) +
- " slave=" + (this.drive_head >> 4 & 1), LOG_DISK);
- this.data_pointer = 0;
- this.current_atapi_command = this.data[0];
- switch(this.current_atapi_command)
- {
- case 0x00:
- dbg_log("test unit ready", LOG_DISK);
- // test unit ready
- this.data_allocate(0);
- this.data_end = this.data_length;
- this.status = 0x50;
- break;
- case 0x03:
- // request sense
- this.data_allocate(this.data[4]);
- this.data_end = this.data_length;
- this.status = 0x58;
- this.data[0] = 0x80 | 0x70;
- this.data[2] = 5; // illegal request
- this.data[7] = 8;
- break;
- case 0x12:
- // inquiry
- var length = this.data[4];
- this.status = 0x58;
- dbg_log("inquiry: " + h(this.data[1], 2) + " length=" + length, LOG_DISK);
- // http://www.t10.org/ftp/x3t9.2/document.87/87-106r0.txt
- //this.data_allocate(36);
- this.data.set([
- 0x05, 0x80, 0x01, 0x31,
- // additional length
- 31,
- 0, 0, 0,
- // 8
- 0x53, 0x4F, 0x4E, 0x59,
- 0x20, 0x20, 0x20, 0x20,
- // 16
- 0x43, 0x44, 0x2D, 0x52,
- 0x4F, 0x4D, 0x20, 0x43,
- 0x44, 0x55, 0x2D, 0x31,
- 0x30, 0x30, 0x30, 0x20,
- // 32
- 0x31, 0x2E, 0x31, 0x61,
- ]);
- this.data_end = this.data_length = Math.min(36, length);
- break;
- case 0x1A:
- // mode sense (6)
- this.data_allocate(this.data[4]);
- this.data_end = this.data_length;
- this.status = 0x58;
- break;
- case 0x1E:
- // prevent/allow medium removal
- this.data_allocate(0);
- this.data_end = this.data_length;
- this.status = 0x50;
- break;
- case 0x25:
- // read capacity
- var count = this.sector_count - 1;
- this.data_set(new Uint8Array([
- count >> 24 & 0xFF,
- count >> 16 & 0xFF,
- count >> 8 & 0xFF,
- count & 0xFF,
- 0,
- 0,
- this.sector_size >> 8 & 0xFF,
- this.sector_size & 0xFF,
- ]));
- this.data_end = this.data_length;
- this.status = 0x58;
- break;
- case 0x28:
- // read
- if(this.lba_count & 1)
- {
- this.atapi_read_dma(this.data);
- }
- else
- {
- this.atapi_read(this.data);
- }
- break;
- case 0x42:
- var length = this.data[8];
- this.data_allocate(Math.min(8, length));
- this.data_end = this.data_length;
- dbg_log("read q subcode: length=" + length, LOG_DISK);
- this.status = 0x58;
- break;
- case 0x43:
- // read toc
- var length = this.data[8] | this.data[7] << 8;
- var format = this.data[9] >> 6;
- this.data_allocate(length);
- this.data_end = this.data_length;
- dbg_log("read toc: " + h(format, 2) +
- " length=" + length +
- " " + (this.data[1] & 2) +
- " " + h(this.data[6]), LOG_DISK);
- if(format === 0)
- {
- var sector_count = this.sector_count;
- this.data.set(new Uint8Array([
- 0, 18, // length
- 1, 1, // first and last session
- 0,
- 0x14,
- 1, // track number
- 0,
- 0, 0, 0, 0,
- 0,
- 0x16,
- 0xAA, // track number
- 0,
- sector_count >> 24,
- sector_count >> 16 & 0xFF,
- sector_count >> 8 & 0xFF,
- sector_count & 0xFF,
- ]));
- }
- else if(format === 1)
- {
- this.data.set(new Uint8Array([
- 0, 10, // length
- 1, 1, // first and last session
- 0, 0,
- 0, 0,
- 0, 0,
- 0, 0,
- ]));
- }
- else
- {
- dbg_assert(false, "Unimplemented format: " + format);
- }
- this.status = 0x58;
- break;
- case 0x46:
- // get configuration
- var length = this.data[8] | this.data[7] << 8;
- length = Math.min(length, 32);
- this.data_allocate(length);
- this.data_end = this.data_length;
- this.data[0] = length - 4 >> 24 & 0xFF;
- this.data[1] = length - 4 >> 16 & 0xFF;
- this.data[2] = length - 4 >> 8 & 0xFF;
- this.data[3] = length - 4 & 0xFF;
- this.data[6] = 0x08;
- this.data[10] = 3;
- this.status = 0x58;
- break;
- case 0x51:
- // read disk information
- this.data_allocate(0);
- this.data_end = this.data_length;
- this.status = 0x50;
- break;
- case 0x52:
- dbg_log("Unimplemented ATAPI command: " + h(this.data[0]), LOG_DISK);
- this.status = 0x51;
- this.data_length = 0;
- this.error = 5 << 4;
- break;
- case 0x5A:
- // mode sense
- var length = this.data[8] | this.data[7] << 8;
- var page_code = this.data[2];
- dbg_log("mode sense: " + h(page_code) + " length=" + length, LOG_DISK);
- if(page_code === 0x2A)
- {
- this.data_allocate(Math.min(30, length));
- }
- this.data_end = this.data_length;
- this.status = 0x58;
- break;
- case 0xBD:
- // mechanism status
- this.data_allocate(this.data[9] | this.data[8] << 8);
- this.data_end = this.data_length;
- this.data[5] = 1;
- this.status = 0x58;
- break;
- case 0x4A:
- this.status = 0x51;
- this.data_length = 0;
- this.error = 5 << 4;
- dbg_log("Unimplemented ATAPI command: " + h(this.data[0]), LOG_DISK);
- break;
- case 0xBE:
- // Hiren's boot CD
- dbg_log("Unimplemented ATAPI command: " + h(this.data[0]), LOG_DISK);
- this.data_allocate(0);
- this.data_end = this.data_length;
- this.status = 0x50;
- break;
- default:
- this.status = 0x51;
- this.data_length = 0;
- this.error = 5 << 4;
- dbg_log("Unimplemented ATAPI command: " + h(this.data[0]), LOG_DISK);
- dbg_assert(false);
- }
- this.bytecount = this.bytecount & ~7 | 2;
- if((this.status & 0x80) === 0)
- {
- this.push_irq();
- }
- if((this.status & 0x80) === 0 && this.data_length === 0)
- {
- this.bytecount |= 1;
- this.status &= ~8;
- }
- };
- IDEInterface.prototype.do_write = function()
- {
- this.status = 0x50;
- dbg_assert(this.data_length <= this.data.length);
- var data = this.data.subarray(0, this.data_length);
- //dbg_log(hex_dump(data), LOG_DISK);
- dbg_assert(this.data_length % 512 === 0);
- this.ata_advance(this.current_command, this.data_length / 512);
- this.push_irq();
- this.buffer.set(this.write_dest, data, function()
- {
- });
- this.report_write(this.data_length);
- };
- IDEInterface.prototype.atapi_read = function(cmd)
- {
- // Note: Big Endian
- var lba = cmd[2] << 24 | cmd[3] << 16 | cmd[4] << 8 | cmd[5];
- var count = cmd[7] << 8 | cmd[8];
- var flags = cmd[1];
- var byte_count = count * this.sector_size;
- var start = lba * this.sector_size;
- dbg_log("CD read lba=" + h(lba) +
- " lbacount=" + h(count) +
- " bytecount=" + h(byte_count) +
- " flags=" + h(flags), LOG_DISK);
- this.data_length = 0;
- var req_length = this.cylinder_high << 8 & 0xFF00 | this.cylinder_low & 0xFF;
- dbg_log(h(this.cylinder_high, 2) + " " + h(this.cylinder_low, 2), LOG_DISK);
- this.cylinder_low = this.cylinder_high = 0; // oak technology driver (windows 3.0)
- if(req_length === 0xFFFF)
- req_length--;
- if(req_length > byte_count)
- {
- req_length = byte_count;
- }
- if(start >= this.buffer.byteLength)
- {
- dbg_assert(false, "CD read: Outside of disk end=" + h(start + byte_count) +
- " size=" + h(this.buffer.byteLength), LOG_DISK);
- this.status = 0xFF;
- this.push_irq();
- }
- else if(byte_count === 0)
- {
- this.status = 0x50;
- this.data_pointer = 0;
- //this.push_irq();
- }
- else
- {
- byte_count = Math.min(byte_count, this.buffer.byteLength - start);
- this.status = 0x50 | 0x80;
- this.report_read_start();
- this.read_buffer(start, byte_count, (data) =>
- {
- //setTimeout(() => {
- dbg_log("cd read: data arrived", LOG_DISK);
- this.data_set(data);
- this.status = 0x58;
- this.bytecount = this.bytecount & ~7 | 2;
- this.push_irq();
- req_length &= ~3;
- this.data_end = req_length;
- if(this.data_end > this.data_length)
- {
- this.data_end = this.data_length;
- }
- this.cylinder_low = this.data_end & 0xFF;
- this.cylinder_high = this.data_end >> 8 & 0xFF;
- this.report_read_end(byte_count);
- //}, 10);
- });
- }
- };
- IDEInterface.prototype.atapi_read_dma = function(cmd)
- {
- // Note: Big Endian
- var lba = cmd[2] << 24 | cmd[3] << 16 | cmd[4] << 8 | cmd[5];
- var count = cmd[7] << 8 | cmd[8];
- var flags = cmd[1];
- var byte_count = count * this.sector_size;
- var start = lba * this.sector_size;
- dbg_log("CD read DMA lba=" + h(lba) +
- " lbacount=" + h(count) +
- " bytecount=" + h(byte_count) +
- " flags=" + h(flags), LOG_DISK);
- if(start >= this.buffer.byteLength)
- {
- dbg_assert(false, "CD read: Outside of disk end=" + h(start + byte_count) +
- " size=" + h(this.buffer.byteLength), LOG_DISK);
- this.status = 0xFF;
- this.push_irq();
- }
- else
- {
- this.status = 0x50 | 0x80;
- this.report_read_start();
- this.read_buffer(start, byte_count, (data) =>
- {
- dbg_log("atapi_read_dma: Data arrived");
- this.report_read_end(byte_count);
- this.status = 0x58;
- this.bytecount = this.bytecount & ~7 | 2;
- this.data_set(data);
- this.do_atapi_dma();
- });
- }
- };
- IDEInterface.prototype.do_atapi_dma = function()
- {
- if((this.device.dma_status & 1) === 0)
- {
- dbg_log("do_atapi_dma: Status not set", LOG_DISK);
- return;
- }
- if((this.status & 0x8) === 0)
- {
- dbg_log("do_atapi_dma: DRQ not set", LOG_DISK);
- return;
- }
- dbg_log("atapi dma transfer len=" + this.data_length, LOG_DISK);
- var prdt_start = this.device.prdt_addr;
- var offset = 0;
- var data = this.data;
- do {
- var addr = this.cpu.read32s(prdt_start);
- var count = this.cpu.read16(prdt_start + 4);
- var end = this.cpu.read8(prdt_start + 7) & 0x80;
- if(!count)
- {
- count = 0x10000;
- }
- dbg_log("dma read dest=" + h(addr) + " count=" + h(count) + " datalen=" + h(this.data_length), LOG_DISK);
- this.cpu.write_blob(data.subarray(offset, Math.min(offset + count, this.data_length)), addr);
- offset += count;
- prdt_start += 8;
- if(offset >= this.data_length && !end)
- {
- dbg_log("leave early end=" + (+end) +
- " offset=" + h(offset) +
- " data_length=" + h(this.data_length) +
- " cmd=" + h(this.current_command), LOG_DISK);
- break;
- }
- }
- while(!end);
- dbg_log("end offset=" + offset, LOG_DISK);
- this.status = 0x50;
- this.device.dma_status &= ~1;
- this.bytecount = this.bytecount & ~7 | 3;
- this.push_irq();
- };
- IDEInterface.prototype.read_data = function(length)
- {
- if(this.data_pointer < this.data_end)
- {
- dbg_assert(this.data_pointer + length - 1 < this.data_end);
- dbg_assert(this.data_pointer % length === 0, h(this.data_pointer) + " " + length);
- if(length === 1)
- {
- var result = this.data[this.data_pointer];
- }
- else if(length === 2)
- {
- var result = this.data16[this.data_pointer >>> 1];
- }
- else
- {
- var result = this.data32[this.data_pointer >>> 2];
- }
- this.data_pointer += length;
- var align = (this.data_end & 0xFFF) === 0 ? 0xFFF : 0xFF;
- if((this.data_pointer & align) === 0)
- {
- dbg_log("Read 1F0: " + h(this.data[this.data_pointer], 2) +
- " cur=" + h(this.data_pointer) +
- " cnt=" + h(this.data_length), LOG_DISK);
- }
- if(this.data_pointer >= this.data_end)
- {
- this.read_end();
- }
- return result;
- }
- else
- {
- dbg_log("Read 1F0: empty", LOG_DISK);
- this.data_pointer += length;
- return 0;
- }
- };
- IDEInterface.prototype.read_end = function()
- {
- dbg_log("read_end cmd=" + h(this.current_command) + " data_pointer=" + h(this.data_pointer) +
- " end=" + h(this.data_end) + " length=" + h(this.data_length), LOG_DISK);
- if(this.current_command === 0xA0)
- {
- if(this.data_end === this.data_length)
- {
- this.status = 0x50;
- this.bytecount = this.bytecount & ~7 | 3;
- this.push_irq();
- }
- else
- {
- this.status = 0x58;
- this.bytecount = this.bytecount & ~7 | 2;
- this.push_irq();
- var byte_count = this.cylinder_high << 8 & 0xFF00 | this.cylinder_low & 0xFF;
- if(this.data_end + byte_count > this.data_length)
- {
- this.cylinder_low = (this.data_length - this.data_end) & 0xFF;
- this.cylinder_high = (this.data_length - this.data_end) >> 8 & 0xFF;
- this.data_end = this.data_length;
- }
- else
- {
- this.data_end += byte_count;
- }
- dbg_log("data_end=" + h(this.data_end), LOG_DISK);
- }
- }
- else
- {
- this.error = 0;
- if(this.data_pointer >= this.data_length)
- {
- this.status = 0x50;
- }
- else
- {
- if(this.current_command === 0xC4 || this.current_command === 0x29)
- {
- var sector_count = Math.min(this.sectors_per_drq,
- (this.data_length - this.data_end) / 512);
- dbg_assert(sector_count % 1 === 0);
- }
- else
- {
- dbg_assert(this.current_command === 0x20 || this.current_command === 0x24);
- var sector_count = 1;
- }
- this.ata_advance(this.current_command, sector_count);
- this.data_end += 512 * sector_count;
- this.status = 0x58;
- this.push_irq();
- }
- }
- };
- IDEInterface.prototype.write_data_port = function(data, length)
- {
- dbg_assert(this.data_pointer % length === 0);
- if(this.data_pointer >= this.data_end)
- {
- dbg_log("Redundant write to data port: " + h(data) + " count=" + h(this.data_end) +
- " cur=" + h(this.data_pointer), LOG_DISK);
- }
- else
- {
- var align = (this.data_end & 0xFFF) === 0 ? 0xFFF : 0xFF;
- if((this.data_pointer + length & align) === 0 || this.data_end < 20)
- {
- dbg_log("Data port: " + h(data >>> 0) + " count=" + h(this.data_end) +
- " cur=" + h(this.data_pointer), LOG_DISK);
- }
- if(length === 1)
- {
- this.data[this.data_pointer++] = data;
- }
- else if(length === 2)
- {
- this.data16[this.data_pointer >>> 1] = data;
- this.data_pointer += 2;
- }
- else
- {
- this.data32[this.data_pointer >>> 2] = data;
- this.data_pointer += 4;
- }
- dbg_assert(this.data_pointer <= this.data_end);
- if(this.data_pointer === this.data_end)
- {
- this.write_end();
- }
- }
- };
- IDEInterface.prototype.write_data_port8 = function(data)
- {
- this.write_data_port(data, 1);
- };
- IDEInterface.prototype.write_data_port16 = function(data)
- {
- this.write_data_port(data, 2);
- };
- IDEInterface.prototype.write_data_port32 = function(data)
- {
- this.write_data_port(data, 4);
- };
- IDEInterface.prototype.write_end = function()
- {
- if(this.current_command === 0xA0)
- {
- this.atapi_handle();
- }
- else
- {
- dbg_log("write_end data_pointer=" + h(this.data_pointer) +
- " data_length=" + h(this.data_length), LOG_DISK);
- if(this.data_pointer >= this.data_length)
- {
- this.do_write();
- }
- else
- {
- dbg_assert(this.current_command === 0x30 ||
- this.current_command === 0x34 ||
- this.current_command === 0xC5,
- "Unexpected command: " + h(this.current_command));
- // XXX: Should advance here, but do_write does all the advancing
- //this.ata_advance(this.current_command, 1);
- this.status = 0x58;
- this.data_end += 512;
- this.push_irq();
- }
- }
- };
- IDEInterface.prototype.ata_advance = function(cmd, sectors)
- {
- dbg_log("Advance sectors=" + sectors + " old_bytecount=" + this.bytecount, LOG_DISK);
- this.bytecount -= sectors;
- if(cmd === 0x24 || cmd === 0x29 || cmd === 0x34 || cmd === 0x39 ||
- cmd === 0x25 || cmd === 0x35)
- {
- var new_sector = sectors + this.get_lba48();
- this.sector = new_sector & 0xFF | new_sector >> 16 & 0xFF00;
- this.cylinder_low = new_sector >> 8 & 0xFF;
- this.cylinder_high = new_sector >> 16 & 0xFF;
- }
- else if(this.is_lba)
- {
- var new_sector = sectors + this.get_lba28();
- this.sector = new_sector & 0xFF;
- this.cylinder_low = new_sector >> 8 & 0xFF;
- this.cylinder_high = new_sector >> 16 & 0xFF;
- this.head = this.head & ~0xF | new_sector & 0xF;
- }
- else // chs
- {
- var new_sector = sectors + this.get_chs();
- var c = new_sector / (this.head_count * this.sectors_per_track) | 0;
- this.cylinder_low = c & 0xFF;
- this.cylinder_high = c >> 8 & 0xFF;
- this.head = (new_sector / this.sectors_per_track | 0) % this.head_count & 0xF;
- this.sector = (new_sector % this.sectors_per_track + 1) & 0xFF;
- dbg_assert(new_sector === this.get_chs());
- }
- };
- IDEInterface.prototype.ata_read_sectors = function(cmd)
- {
- var is_lba48 = cmd === 0x24 || cmd === 0x29;
- var count = this.get_count(is_lba48);
- var lba = this.get_lba(is_lba48);
- var is_single = cmd === 0x20 || cmd === 0x24;
- var byte_count = count * this.sector_size;
- var start = lba * this.sector_size;
- dbg_log("ATA read cmd=" + h(cmd) +
- " mode=" + (this.is_lba ? "lba" : "chs") +
- " lba=" + h(lba) +
- " lbacount=" + h(count) +
- " bytecount=" + h(byte_count), LOG_DISK);
- if(start + byte_count > this.buffer.byteLength)
- {
- dbg_assert(false, "ATA read: Outside of disk", LOG_DISK);
- this.status = 0xFF;
- this.push_irq();
- }
- else
- {
- this.status = 0x80 | 0x40;
- this.report_read_start();
- this.read_buffer(start, byte_count, (data) =>
- {
- //setTimeout(() => {
- dbg_log("ata_read: Data arrived", LOG_DISK);
- this.data_set(data);
- this.status = 0x58;
- this.data_end = is_single ? 512 : Math.min(byte_count, this.sectors_per_drq * 512);
- this.ata_advance(cmd, is_single ? 1 : Math.min(count, this.sectors_per_track));
- this.push_irq();
- this.report_read_end(byte_count);
- //}, 10);
- });
- }
- };
- IDEInterface.prototype.ata_read_sectors_dma = function(cmd)
- {
- var is_lba48 = cmd === 0x25;
- var count = this.get_count(is_lba48);
- var lba = this.get_lba(is_lba48);
- var byte_count = count * this.sector_size;
- var start = lba * this.sector_size;
- dbg_log("ATA DMA read lba=" + h(lba) +
- " lbacount=" + h(count) +
- " bytecount=" + h(byte_count), LOG_DISK);
- if(start + byte_count > this.buffer.byteLength)
- {
- dbg_assert(false, "ATA read: Outside of disk", LOG_DISK);
- this.status = 0xFF;
- this.push_irq();
- return;
- }
- this.status = 0x58;
- this.device.dma_status |= 1;
- };
- IDEInterface.prototype.do_ata_read_sectors_dma = function()
- {
- var cmd = this.current_command;
- var is_lba48 = cmd === 0x25;
- var count = this.get_count(is_lba48);
- var lba = this.get_lba(is_lba48);
- var byte_count = count * this.sector_size;
- var start = lba * this.sector_size;
- dbg_assert(lba < this.buffer.byteLength);
- this.report_read_start();
- var orig_prdt_start = this.device.prdt_addr;
- this.read_buffer(start, byte_count, (data) =>
- {
- //setTimeout(function() {
- dbg_log("do_ata_read_sectors_dma: Data arrived", LOG_DISK);
- var prdt_start = this.device.prdt_addr;
- var offset = 0;
- dbg_assert(orig_prdt_start === prdt_start);
- do {
- var prd_addr = this.cpu.read32s(prdt_start);
- var prd_count = this.cpu.read16(prdt_start + 4);
- var end = this.cpu.read8(prdt_start + 7) & 0x80;
- if(!prd_count)
- {
- prd_count = 0x10000;
- dbg_log("dma: prd count was 0", LOG_DISK);
- }
- dbg_log("dma read transfer dest=" + h(prd_addr) +
- " prd_count=" + h(prd_count), LOG_DISK);
- this.cpu.write_blob(data.subarray(offset, offset + prd_count), prd_addr);
- offset += prd_count;
- prdt_start += 8;
- }
- while(!end);
- dbg_assert(offset === byte_count);
- this.ata_advance(this.current_command, count);
- this.status = 0x50;
- this.device.dma_status &= ~1;
- this.current_command = -1;
- this.push_irq();
- this.report_read_end(byte_count);
- //}.bind(this), 10);
- });
- };
- IDEInterface.prototype.ata_write_sectors = function(cmd)
- {
- var is_lba48 = cmd === 0x34 || cmd === 0x39;
- var count = this.get_count(is_lba48);
- var lba = this.get_lba(is_lba48);
- var is_single = cmd === 0x30 || cmd === 0x34;
- var byte_count = count * this.sector_size;
- var start = lba * this.sector_size;
- dbg_log("ATA write lba=" + h(lba) +
- " mode=" + (this.is_lba ? "lba" : "chs") +
- " lbacount=" + h(count) +
- " bytecount=" + h(byte_count), LOG_DISK);
- if(start + byte_count > this.buffer.byteLength)
- {
- dbg_assert(false, "ATA write: Outside of disk", LOG_DISK);
- this.status = 0xFF;
- this.push_irq();
- }
- else
- {
- this.status = 0x58;
- this.data_allocate_noclear(byte_count);
- this.data_end = is_single ? 512 : Math.min(byte_count, this.sectors_per_drq * 512);
- this.write_dest = start;
- }
- };
- IDEInterface.prototype.ata_write_sectors_dma = function(cmd)
- {
- var is_lba48 = cmd === 0x35;
- var count = this.get_count(is_lba48);
- var lba = this.get_lba(is_lba48);
- var byte_count = count * this.sector_size;
- var start = lba * this.sector_size;
- dbg_log("ATA DMA write lba=" + h(lba) +
- " lbacount=" + h(count) +
- " bytecount=" + h(byte_count), LOG_DISK);
- if(start + byte_count > this.buffer.byteLength)
- {
- dbg_assert(false, "ATA DMA write: Outside of disk", LOG_DISK);
- this.status = 0xFF;
- this.push_irq();
- return;
- }
- this.status = 0x58;
- this.device.dma_status |= 1;
- };
- IDEInterface.prototype.do_ata_write_sectors_dma = function()
- {
- var cmd = this.current_command;
- var is_lba48 = cmd === 0x35;
- var count = this.get_count(is_lba48);
- var lba = this.get_lba(is_lba48);
- var byte_count = count * this.sector_size;
- var start = lba * this.sector_size;
- var prdt_start = this.device.prdt_addr;
- var offset = 0;
- dbg_log("prdt addr: " + h(prdt_start, 8), LOG_DISK);
- const buffer = new Uint8Array(byte_count);
- do {
- var prd_addr = this.cpu.read32s(prdt_start);
- var prd_count = this.cpu.read16(prdt_start + 4);
- var end = this.cpu.read8(prdt_start + 7) & 0x80;
- if(!prd_count)
- {
- prd_count = 0x10000;
- dbg_log("dma: prd count was 0", LOG_DISK);
- }
- dbg_log("dma write transfer dest=" + h(prd_addr) + " prd_count=" + h(prd_count), LOG_DISK);
- var slice = this.cpu.mem8.subarray(prd_addr, prd_addr + prd_count);
- dbg_assert(slice.length === prd_count);
- buffer.set(slice, offset);
- //if(DEBUG)
- //{
- // dbg_log(hex_dump(slice), LOG_DISK);
- //}
- offset += prd_count;
- prdt_start += 8;
- }
- while(!end);
- dbg_assert(offset === buffer.length);
- this.buffer.set(start, buffer, () =>
- {
- dbg_log("dma write completed", LOG_DISK);
- this.ata_advance(this.current_command, count);
- this.status = 0x50;
- this.push_irq();
- this.device.dma_status &= ~1;
- this.current_command = -1;
- });
- this.report_write(byte_count);
- };
- IDEInterface.prototype.get_chs = function()
- {
- var c = this.cylinder_low & 0xFF | this.cylinder_high << 8 & 0xFF00;
- var h = this.head;
- var s = this.sector & 0xFF;
- dbg_log("get_chs: c=" + c + " h=" + h + " s=" + s, LOG_DISK);
- return (c * this.head_count + h) * this.sectors_per_track + s - 1;
- };
- IDEInterface.prototype.get_lba28 = function()
- {
- return this.sector & 0xFF |
- this.cylinder_low << 8 & 0xFF00 |
- this.cylinder_high << 16 & 0xFF0000 |
- (this.head & 0xF) << 24;
- };
- IDEInterface.prototype.get_lba48 = function()
- {
- // Note: Bits over 32 missing
- return (this.sector & 0xFF |
- this.cylinder_low << 8 & 0xFF00 |
- this.cylinder_high << 16 & 0xFF0000 |
- (this.sector >> 8) << 24 & 0xFF000000) >>> 0;
- };
- IDEInterface.prototype.get_lba = function(is_lba48)
- {
- if(is_lba48)
- {
- return this.get_lba48();
- }
- else if(this.is_lba)
- {
- return this.get_lba28();
- }
- else
- {
- return this.get_chs();
- }
- };
- IDEInterface.prototype.get_count = function(is_lba48)
- {
- if(is_lba48)
- {
- var count = this.bytecount;
- if(count === 0) count = 0x10000;
- return count;
- }
- else
- {
- var count = this.bytecount & 0xFF;
- if(count === 0) count = 0x100;
- return count;
- }
- };
- IDEInterface.prototype.create_identify_packet = function()
- {
- // http://bochs.sourceforge.net/cgi-bin/lxr/source/iodev/harddrv.cc#L2821
- if(this.drive_head & 0x10)
- {
- // slave
- this.data_allocate(0);
- return;
- }
- for(var i = 0; i < 512; i++)
- {
- this.data[i] = 0;
- }
- var cylinder_count = Math.min(16383, this.cylinder_count);
- this.data_set([
- 0x40, this.is_atapi ? 0x85 : 0,
- // 1 cylinders
- cylinder_count, cylinder_count >> 8,
- 0, 0,
- // 3 heads
- this.head_count, this.head_count >> 8,
- this.sectors_per_track / 512, this.sectors_per_track / 512 >> 8,
- // 5
- 0, 512 >> 8,
- // sectors per track
- this.sectors_per_track, this.sectors_per_track >> 8,
- 0, 0, 0, 0, 0, 0,
- // 10-19 serial number
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- // 15
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- // 20
- 3, 0,
- 0, 2,
- 4, 0,
- // 23-26 firmware revision
- 0, 0, 0, 0, 0, 0, 0, 0,
- // 27 model number
- 56, 118, 32, 54, 68, 72, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
- 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
- // 47 max value for set multiple mode
- 0x80, 0,
- 1, 0,
- //0, 3, // capabilities, 2: Only LBA / 3: LBA and DMA
- 0, 2, // capabilities, 2: Only LBA / 3: LBA and DMA
- // 50
- 0, 0,
- 0, 2,
- 0, 2,
- 7, 0,
- // 54 cylinders
- cylinder_count, cylinder_count >> 8,
- // 55 heads
- this.head_count, this.head_count >> 8,
- // 56 sectors per track
- this.sectors_per_track, 0,
- // capacity in sectors
- this.sector_count & 0xFF, this.sector_count >> 8 & 0xFF,
- this.sector_count >> 16 & 0xFF, this.sector_count >> 24 & 0xFF,
- 0, 0,
- // 60
- this.sector_count & 0xFF, this.sector_count >> 8 & 0xFF,
- this.sector_count >> 16 & 0xFF, this.sector_count >> 24 & 0xFF,
- 0, 0,
- // 63, dma supported mode, dma selected mode
- this.current_command === 0xA0 ? 0 : 7, this.current_command === 0xA0 ? 0 : 4,
- //0, 0, // no DMA
- 0, 0,
- // 65
- 30, 0, 30, 0, 30, 0, 30, 0, 0, 0,
- // 70
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- // 75
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- // 80
- 0x7E, 0, 0, 0, 0, 0, 0, 0x74, 0, 0x40,
- // 85
- 0, 0x40, 0, 0x74, 0, 0x40, 0, 0, 0, 0,
- // 90
- 0, 0, 0, 0, 0, 0, 1, 0x60, 0, 0,
- // 95
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- // 100
- this.sector_count & 0xFF, this.sector_count >> 8 & 0xFF,
- this.sector_count >> 16 & 0xFF, this.sector_count >> 24 & 0xFF,
- ]);
- this.data_length = 512;
- this.data_end = 512;
- };
- IDEInterface.prototype.data_allocate = function(len)
- {
- this.data_allocate_noclear(len);
- for(var i = 0; i < (len + 3 >> 2); i++)
- {
- this.data32[i] = 0;
- }
- };
- IDEInterface.prototype.data_allocate_noclear = function(len)
- {
- if(this.data.length < len)
- {
- this.data = new Uint8Array(len + 3 & ~3);
- this.data16 = new Uint16Array(this.data.buffer);
- this.data32 = new Int32Array(this.data.buffer);
- }
- this.data_length = len;
- this.data_pointer = 0;
- };
- IDEInterface.prototype.data_set = function(data)
- {
- this.data_allocate_noclear(data.length);
- this.data.set(data);
- };
- IDEInterface.prototype.report_read_start = function()
- {
- this.stats.loading = true;
- this.bus.send("ide-read-start");
- };
- IDEInterface.prototype.report_read_end = function(byte_count)
- {
- this.stats.loading = false;
- var sector_count = byte_count / this.sector_size | 0;
- this.stats.sectors_read += sector_count;
- this.stats.bytes_read += byte_count;
- this.bus.send("ide-read-end", [this.nr, byte_count, sector_count]);
- };
- IDEInterface.prototype.report_write = function(byte_count)
- {
- var sector_count = byte_count / this.sector_size | 0;
- this.stats.sectors_written += sector_count;
- this.stats.bytes_written += byte_count;
- this.bus.send("ide-write-end", [this.nr, byte_count, sector_count]);
- };
- IDEInterface.prototype.read_buffer = function(start, length, callback)
- {
- const id = this.last_io_id++;
- this.in_progress_io_ids.add(id);
- this.buffer.get(start, length, data =>
- {
- if(this.cancelled_io_ids.delete(id))
- {
- dbg_assert(!this.in_progress_io_ids.has(id));
- return;
- }
- const removed = this.in_progress_io_ids.delete(id);
- dbg_assert(removed);
- callback(data);
- });
- };
- IDEInterface.prototype.cancel_io_operations = function()
- {
- for(const id of this.in_progress_io_ids)
- {
- this.cancelled_io_ids.add(id);
- }
- this.in_progress_io_ids.clear();
- };
- IDEInterface.prototype.get_state = function()
- {
- var state = [];
- state[0] = this.bytecount;
- state[1] = this.cylinder_count;
- state[2] = this.cylinder_high;
- state[3] = this.cylinder_low;
- state[4] = this.data_pointer;
- state[5] = 0;
- state[6] = 0;
- state[7] = 0;
- state[8] = 0;
- state[9] = this.drive_head;
- state[10] = this.error;
- state[11] = this.head;
- state[12] = this.head_count;
- state[13] = this.is_atapi;
- state[14] = this.is_lba;
- state[15] = this.lba_count;
- state[16] = this.data;
- state[17] = this.data_length;
- state[18] = this.sector;
- state[19] = this.sector_count;
- state[20] = this.sector_size;
- state[21] = this.sectors_per_drq;
- state[22] = this.sectors_per_track;
- state[23] = this.status;
- state[24] = this.write_dest;
- state[25] = this.current_command;
- state[26] = this.data_end;
- state[27] = this.current_atapi_command;
- state[28] = this.buffer;
- return state;
- };
- IDEInterface.prototype.set_state = function(state)
- {
- this.bytecount = state[0];
- this.cylinder_count = state[1];
- this.cylinder_high = state[2];
- this.cylinder_low = state[3];
- this.data_pointer = state[4];
- this.drive_head = state[9];
- this.error = state[10];
- this.head = state[11];
- this.head_count = state[12];
- this.is_atapi = state[13];
- this.is_lba = state[14];
- this.lba_count = state[15];
- this.data = state[16];
- this.data_length = state[17];
- this.sector = state[18];
- this.sector_count = state[19];
- this.sector_size = state[20];
- this.sectors_per_drq = state[21];
- this.sectors_per_track = state[22];
- this.status = state[23];
- this.write_dest = state[24];
- this.current_command = state[25];
- this.data_end = state[26];
- this.current_atapi_command = state[27];
- this.data16 = new Uint16Array(this.data.buffer);
- this.data32 = new Int32Array(this.data.buffer);
- this.buffer && this.buffer.set_state(state[28]);
- };
|