1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092 |
- "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;
- this.push_irq();
- }
- 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]);
- };
|