diff --git a/src/driver/storage/AHCI/AHCI.pas b/src/driver/storage/AHCI/AHCI.pas new file mode 100644 index 00000000..85948107 --- /dev/null +++ b/src/driver/storage/AHCI/AHCI.pas @@ -0,0 +1,56 @@ +unit AHCI; + +interface + +uses + util, + PCI, + drivertypes, + drivermanagement, + lmemorymanager, + console, + vmemorymanager, + AHCITypes; + +var + ahciControllers : array[0..255] of pointer; + ahciControllerCount : uint32; + + hba : array[0..255] of THBA_Memory; + +procedure init(); +procedure load(); + +implementation + +procedure init(); +var + devID : TDeviceIdentifier; +begin + console.writestringln('AHCI: Registering driver'); + devID.bus:= biPCI; + devID.id0:= idANY; + devID.id1:= $00000001; + devID.id2:= $00000006; + devID.id3:= $00000001; + devID.id4:= idANY; + devID.ex:= nil; + drivermanagement.register_driver('ATA/PI AHCI Driver', @devID, @load); +end; + +procedure load(ptr : void); +begin + // console.writestringln('AHCI: initilizing a new controller'); + // ahciControllers[ahciControllerCount] := ptr; + // hba[ahciControllerCount] := PPCI_Device(ptr)^.address5; + // new_page_at_address(hba[ahciControllerCount]); + + // //here would be any controller setup needed + + // check_ports(ahciControllerCount); + // ahciControllerCount += 1; + // exit(true); +end; + + +end. diff --git a/src/driver/storage/AHCI/AHCITypes.pas b/src/driver/storage/AHCI/AHCITypes.pas new file mode 100644 index 00000000..deb6ef2d --- /dev/null +++ b/src/driver/storage/AHCI/AHCITypes.pas @@ -0,0 +1,172 @@ + +unit AHCITypes; +interface +uses + util, + PCI, + drivertypes, + drivermanagement, + lmemorymanager, + console, + vmemorymanager; + +const + + + +type + + { + AHCI Host Bus Adapter (HBA) Memory-Mapped Register Interface + This structure is used to access the AHCI HBA's memory-mapped registers. + The AHCI HBA's memory-mapped registers are used to configure the HBA and + to issue commands to the SATA devices connected to the HBA. + + The AHCI HBA's memory-mapped registers are accessed by reading and writing + to the HBA's memory-mapped I/O space. The HBA's memory-mapped I/O space is + typically mapped to a region of physical memory by the system's BIOS or + UEFI firmware. The HBA's memory-mapped I/O space is typically mapped to a + region of physical memory that is accessible to the system's CPU(s) and + other devices. + } + THBA_Port = bitpacked record + cmdl_basel: uint32; // Command List Base Address Lower 32-bits + cmdl_baseu: uint32; // Command List Base Address Upper 32-bits + fis_basel: uint32; // FIS Base Address Lower 32-bits + fis_baseu: uint32; // FIS Base Address Upper 32-bits + int_status: uint32; // Interrupt Status + int_enable: uint32; // Interrupt Enable + cmd: uint32; // Command and Status + rsv0: uint32; // Reserved + tfd: uint32; // Task File Data + signature: uint32; // Signature + sata_stat: uint32; // SATA Status (SCR0:SStatus) + sata_ctrl: uint32; // SATA Control (SCR2:SControl) + sata_error: uint32; // SATA Error (SCR1:SError) + sata_active: uint32; // SATA Active + cmd_issue: uint32; // Command Issue + sata_noti: uint32; // SATA Notification + fis_switch_ctrl: uint32;// FIS-based Switch Control + rsv1: array[0..10] of uint32; + end; + + { + AHCI Host Bus Adapter (HBA) Memory-Mapped Register Interface + This structure is used to access the AHCI HBA's memory-mapped registers. + The AHCI HBA's memory-mapped registers are used to configure the HBA and + to issue commands to the SATA devices connected to the HBA. + } + THBA_Memory = bitpacked record + capabilites: uint32; // Host Capabilities + global_ctrl: uint32; // Global Host Control + int_status: uint32; // Interrupt Status + ports_implemented: uint32; // Ports Implemented + version: uint32; // Version + ccc_control: uint32; // Command Completion Coalescing Control + ccc_ports: uint32; // Command Completion Coalescing Ports + em_location: uint32; // Enclosure Management Location + em_control: uint32; // Enclosure Management Control + capabilites2: uint32; // Host Capabilities Extended + bohc: uint32; // BIOS/OS Handoff Control and Status + rsv0: array[0..0x210] of boolean; + ports: array[0..31] of THBA_Port; + end; + + { + AHCI Host Bus Adapter (HBA) FIS (Frame Information Structure) Interface + This structure is used to access the AHCI HBA's FIS (Frame Information Structure) + memory-mapped registers. The AHCI HBA's FIS memory-mapped registers are used to + configure the HBA and to issue commands to the SATA devices connected to the HBA. + } + THBA_FIS = bitpacked record + dsfis: array[0..0x1F] of uint32; // DMA Setup FIS + rsv0: array[0..0x1F] of uint32; + psfis: array[0..0x1F] of uint32; // PIO Setup FIS + rsv1: array[0..0x1F] of uint32; + rfis: array[0..0x1F] of uint32; // D2H Register FIS + rsv2: array[0..0x1F] of uint32; + sdbfis: array[0..0xF] of uint32; // Set Device Bits FIS + ufis: array[0..0x1F] of uint32; // Unknown FIS + rsv3: array[0..0x67] of uint32; + end; + + { + AHCI Host Bus Adapter (HBA) Command Header Interface + This structure is used to access the AHCI HBA's Command Header memory-mapped + registers. The AHCI HBA's Command Header memory-mapped registers are used to + configure the HBA and to issue commands to the SATA devices connected to the HBA. + } + THBA_CMD_HEADER = bitpacked record + cmd_fis_length: uint8; // Command FIS Length + atapi: uint8; // ATAPI + write: uint8; // Write + prefetchable: uint8; // Prefetchable + reset: uint8; // Reset + bist: uint8; // BIST + clear_busy: uint8; // Clear Busy + reserved0: uint8; // Reserved + prdtl: uint16; // Physical Region Descriptor Table Length + prdbc: uint32; // Physical Region Descriptor Byte Count + cmd_table_base: uint32; // Command Table Base Address + cmd_table_baseu: uint32; // Command Table Base Address Upper 32-bits + rsv0: array[0..4] of uint32; + end; + + { + AHCI Host Bus Adapter (HBA) Command Table Interface + This structure is used to access the AHCI HBA's Command Table memory-mapped + registers. The AHCI HBA's Command Table memory-mapped registers are used to + configure the HBA and to issue commands to the SATA devices connected to the HBA. + } + THBA_CMD_TABLE = bitpacked record + cmd_fis: THBA_FIS; // Command FIS + acmd: array[0..0x1F] of uint8; // ATAPI Command + rsv0: array[0..0x30] of uint8; + end; + + { + AHCI Host Bus Adapter (HBA) Command Table Interface + This structure is used to access the AHCI HBA's Command Table memory-mapped + registers. The AHCI HBA's Command Table memory-mapped registers are used to + configure the HBA and to issue commands to the SATA devices connected to the HBA. + } + THBA_PRD = bitpacked record + dba: uint32; // Data Base Address + dbau: uint32; // Data Base Address Upper 32-bits + rsv0: uint32; // Reserved + dbc: uint32; // Data Byte Count + rsv1: uint32; // Reserved + end; + + { + AHCI Host Bus Adapter (HBA) Command Table Interface + This structure is used to access the AHCI HBA's Command Table memory-mapped + registers. The AHCI HBA's Command Table memory-mapped registers are used to + configure the HBA and to issue commands to the SATA devices connected to the HBA. + } + THBA_CMD = bitpacked record + header: THBA_CMD_HEADER; // Command Header + table: THBA_CMD_TABLE; // Command Table + prd: array[0..0x7] of THBA_PRD; // Physical Region Descriptor Table + end; + + { + AHCI Host Bus Adapter (HBA) Command Table Interface + This structure is used to access the AHCI HBA's Command Table memory-mapped + registers. The AHCI HBA's Command Table memory-mapped registers are used to + configure the HBA and to issue commands to the SATA devices connected to the HBA. + } + THBA = bitpacked record + memory: THBA_Memory; // HBA Memory + cmd: array[0..0x7FF] of THBA_CMD; // Command List + end; + + PHBA = ^THBA; + + + +implementation + +end. + + diff --git a/src/driver/storage/IDE.pas b/src/driver/storage/IDE.pas index 7f3ba440..e3177b80 100644 --- a/src/driver/storage/IDE.pas +++ b/src/driver/storage/IDE.pas @@ -17,28 +17,29 @@ uses tracer, drivemanager, storagetypes, - idetypes; + idetypes, + isrmanager; var primaryDevices: array[0..1] of TIDE_Device = ( (exists: false; isPrimary: true; isMaster: true; isATAPI: false; status: (Busy: false; Ready: false; Fault: false; Seek: false; DRQ: false; CORR: false; IDDEX: false; ERROR: false); - base: ATA_PRIMARY_BASE; info: nil), + base: ATA_PRIMARY_BASE; blockSize: 0; info: nil), (exists: false; isPrimary: true; isMaster: false; isATAPI: false; status: (Busy: false; Ready: false; Fault: false; Seek: false; DRQ: false; CORR: false; IDDEX: false; ERROR: false); - base: ATA_PRIMARY_BASE; info: nil) + base: ATA_PRIMARY_BASE; blockSize: 0; info: nil) ); secondaryDevices: array[0..1] of TIDE_Device = ( (exists: false; isPrimary: false; isMaster: true; isATAPI: false; status: (Busy: false; Ready: false; Fault: false; Seek: false; DRQ: false; CORR: false; IDDEX: false; ERROR: false); - base: ATA_SECONDARY_BASE; info: nil), + base: ATA_SECONDARY_BASE; blockSize: 0; info: nil), (exists: false; isPrimary: false; isMaster: false; isATAPI: false; status: (Busy: false; Ready: false; Fault: false; Seek: false; DRQ: false; CORR: false; IDDEX: false; ERROR: false); - base: ATA_SECONDARY_BASE; info: nil) + base: ATA_SECONDARY_BASE; blockSize: 0; info: nil) ); @@ -47,6 +48,8 @@ var function get_status(var device : TIDE_Device) : TIDE_Status; function wait_for_device(device : TIDE_Device; ioop : boolean) : boolean; procedure no_interrupt(isPrimary : boolean); + procedure enable_interrupt(isPrimary : boolean); + procedure reset_device(device : TIDE_Device); procedure select_device(device : TIDE_Device); implementation @@ -64,6 +67,51 @@ begin end; end; +procedure enable_interrupt(isPrimary : boolean); +var + reg : uint8; +begin + if isPrimary then begin + reg := inb(ATA_INTERRUPT_PRIMARY); + reg := reg and not (1 shl 1); + outb(ATA_INTERRUPT_PRIMARY, reg); + end else begin + reg := inb(ATA_INTERRUPT_SECONDARY); + reg := reg and not (1 shl 1); + outb(ATA_INTERRUPT_SECONDARY, reg); + end; +end; + +//soft reset +procedure reset_device(device : TIDE_Device); +var + reg : uint8; +begin + if device.isPrimary then begin + reg := inb(ATA_INTERRUPT_PRIMARY); + reg := reg and (1 shl 2); + outb(ATA_INTERRUPT_PRIMARY, reg); + end else begin + reg := inb(ATA_INTERRUPT_SECONDARY); + reg := reg and (1 shl 2); + outb(ATA_INTERRUPT_SECONDARY, reg); + end; + + sleep(20); + + if device.isPrimary then begin + reg := inb(ATA_INTERRUPT_PRIMARY); + reg := reg and not (1 shl 2); + outb(ATA_INTERRUPT_PRIMARY, reg); + end else begin + reg := inb(ATA_INTERRUPT_SECONDARY); + reg := reg and not (1 shl 2); + outb(ATA_INTERRUPT_SECONDARY, reg); + end; + + +end; + { Wait for the device to be ready on the IDE bus @@ -105,6 +153,7 @@ end; procedure select_device(device : TIDE_Device); var dev : uint8; + reg : uint8; begin push_trace('ide.select_device()'); @@ -114,7 +163,10 @@ begin dev := ATA_DEVICE_MASTER; end; - outb(device.base + ATA_REG_HDDEVSEL, dev); + reg := inb(device.base + ATA_REG_HDDEVSEL); + reg := (reg and $F0) or dev; + + outb(device.base + ATA_REG_HDDEVSEL, reg); pop_trace(); end; @@ -201,6 +253,7 @@ Write 0x00 back to Control Register to complete reset. sleep(1); // outb(ATA_PRIMARY_BASE1, $00); // outb(ATA_SECONDARY_BASE1, $00); + select_device(device); sleep(1); @@ -246,10 +299,8 @@ var success : boolean; size : uint32; begin - push_trace('ide.load_device()'); success := check_device_type(device); - select_device(device); if (is_device_present(device) and success) then begin console.writestringln('[IDE] (load_device) Device is present'); @@ -297,7 +348,7 @@ begin storageDevice^.writable := false; //TODO atapi size := atapi.get_device_size(device); storageDevice^.maxSectorCount := size; - storageDevice^.sectorSize := 2048; //todo + storageDevice^.sectorSize := device.blockSize; if (device.isMaster) then begin storageDevice^.controllerId0 := 0; @@ -390,7 +441,11 @@ var begin push_trace('ide.load()'); console.writestringln('[IDE] (load) Loading IDE Devices'); - + console.redrawWindows(); + registerISR(14, @atapi.ide_irq); + registerISR(15, @atapi.ide_irq); + console.writestringln('[IDE] (load) Loading IDE Devices 2'); + console.redrawWindows(); pciDevice := PPCI_Device(ptr); load_device(primaryDevices[0]); diff --git a/src/driver/storage/atapi.pas b/src/driver/storage/atapi.pas index cf45e278..b294bd4f 100644 --- a/src/driver/storage/atapi.pas +++ b/src/driver/storage/atapi.pas @@ -24,13 +24,48 @@ var function identify_device(var device : TIDE_Device) : Boolean; function read_pio28(device : TIDE_Device; lba : uint32; count : uint8; buffer : puint16) : boolean; function write_pio28(device : TIDE_Device; lba : uint32; count : uint8; buffer : puint16) : boolean; - function get_device_size(device : TIDE_Device) : uint32; + function get_device_size(var device : TIDE_Device) : uint32; + procedure ide_irq(); implementation uses ide, ata; + +procedure ide_irq(); +begin + console.writestringln('IDE IRQ Handler'); + console.redrawWindows(); +end; + +//print status of the device, alt status register +procedure print_status(device : TIDE_Device); +begin + get_status(device); + + console.writestring('Status: '); + // console.writeintln(uint32(device.status)); + + console.writestring('Busy: '); + console.writeintln(uint32(device.status.Busy)); + console.writestring('Ready: '); + console.writeintln(uint32(device.status.Ready)); + console.writestring('Fault: '); + console.writeintln(uint32(device.status.Fault)); + console.writestring('Seek: '); + console.writeintln(uint32(device.status.Seek)); + console.writestring('DRQ: '); + console.writeintln(uint32(device.status.DRQ)); + console.writestring('CORR: '); + console.writeintln(uint32(device.status.CORR)); + console.writestring('IDDEX: '); + console.writeintln(uint32(device.status.IDDEX)); + console.writestring('ERROR: '); + console.writeintln(uint32(device.status.ERROR)); + +end; + procedure outb(port : uint16; value : uint8); begin util.outb(port, value); @@ -38,7 +73,6 @@ begin // get_status(primaryDevices[0]); end; - { Identify the device on the IDE bus @@ -55,9 +89,9 @@ begin select_device(device); no_interrupt(device.isPrimary); - outb(ATA_PRIMARY_BASE1, $04); - outb(ATA_SECONDARY_BASE1, $04); - sleep(1); + // outb(ATA_PRIMARY_BASE1, $04); + // outb(ATA_SECONDARY_BASE1, $04); + // sleep(1); status := get_status(device); @@ -85,10 +119,80 @@ begin exit(false); end; + console.writestringln('ATAPI Device identified: '); + for i:=0 to 255 do begin buffer[i] := inw(device.base + ATA_REG_DATA); + console.writestring('['); + console.writehexpair(buffer[i]); + console.writestring('] '); + if (i mod 4) = 0 then begin + console.writestringln(''); + end; end; + //bits 14 and 15 of word 0 + { + 00h + 01h + Direct-access device + Sequential-access device + 02h + 03h + Printer device + Processor device + 04h + 05h + Write-once device + CD-ROM device + 06h + 07h + Scanner device + Optical memory device + 08h + 09h + Medium changer device + Communications device + 0A-0Bh + 0Ch + Reserved for ACS IT8 (Graphic arts pre-press devices) + Array controller device + 0Dh + 0Eh + Enclosure services device + Reduced block command devices + 0Fh + 10-1Eh + Optical card reader/writer device + Reserved + 1Fh + Unknown or no device type} + + //print device type + console.writestring('Device Type: '); + case (buffer[0] shr 8) and $3 of + 0: console.writestringln('Direct-access device'); + 1: console.writestringln('Sequential-access device'); + 2: console.writestringln('Printer device'); + 3: console.writestringln('Processor device'); + 4: console.writestringln('Write-once device'); + 5: console.writestringln('CD-ROM device'); + 6: console.writestringln('Scanner device'); + 7: console.writestringln('Optical memory device'); + 8: console.writestringln('Medium changer device'); + 9: console.writestringln('Communications device'); + 10: console.writestringln('Reserved for ACS IT8 (Graphic arts pre-press devices)'); + 11: console.writestringln('Array controller device'); + 12: console.writestringln('Reserved'); + 13: console.writestringln('Enclosure services device'); + 14: console.writestringln('Reduced block command devices'); + 15: console.writestringln('Unknown or no device type'); + end; + + outb(ATA_PRIMARY_BASE1, $04); + outb(ATA_SECONDARY_BASE1, $04); + sleep(10); + device.info := @buffer; identify_device := true; @@ -121,7 +225,7 @@ begin outb(device.base + ATA_REG_COMMAND, ATA_CMD_PACKET); // Step 2: Wait for DRQ=1 (drive wants a command packet) - ready := wait_for_device(device, true); + ready := wait_for_device(device, false); if not ready then begin console.writestringln('[ATAPI] Timeout waiting for PACKET DRQ!'); read_PIO28 := false; @@ -139,22 +243,24 @@ begin packet[7] := count; // Number of sectors to read packet[8] := 0; // Reserved packet[9] := 0; // Control + packet[10] := 0; // Reserved + packet[11] := 0; // Reserved // Step 4: Send the command packet for i := 0 to 5 do outw(device.base + ATA_REG_DATA, (packet[i*2] or (packet[i*2+1] shl 8))); - // Step 5: Wait for DRQ=1 (data ready) - ready := wait_for_device(device, true); - if not ready then begin - console.writestringln('[ATAPI] Timeout waiting for data DRQ!'); - read_PIO28 := false; - exit; - end; - // Step 6: Read the data (count * 2048 bytes) - for i := 0 to (count * 1024) - 1 do begin - for j := 0 to 255 do begin + for i := 0 to (count) - 1 do begin + for j := 0 to 1023 do begin + + ready := wait_for_device(device, false); + if not ready then begin + console.writestringln('[ATAPI] Timeout waiting for data DRQ!'); + read_PIO28 := false; + exit; + end; + buffer[i * 256 + j] := inw(device.base + ATA_REG_DATA); end; end; @@ -175,18 +281,21 @@ begin write_pio28 := false; end; -function get_device_size(device: TIDE_Device): uint32; +function get_device_size(var device: TIDE_Device): uint32; var i: uint8; ready: boolean; packet: puint16; // Will hold our 10-byte CDB (as 5 words) response: puint16; // Will hold the 8-byte capacity response xferCount: uint16; + temp: uInt32; + data: uint16; begin get_device_size := 0; // 1) Select the device (Master/Slave) - presumably your function select_device(device); + enable_interrupt(device.isPrimary); // 2) We set "no special features" for ATAPI outb(device.base + ATA_REG_FEATURES, 0); @@ -203,8 +312,8 @@ begin // 5) Build our 10-byte SCSI "READ CAPACITY(10)" command descriptor block (CDB) // We'll store it in 5 words = 10 bytes - packet := puint16(kalloc(10)); // 5 * 2 = 10 bytes - memset(uint32(packet), 0, 10); + packet := puint16(kalloc(12)); // 5 * 2 = 10 bytes + memset(uint32(packet), 0, 12); // READ CAPACITY(10) = opcode 0x25 // The rest can be 0 for a basic capacity read @@ -215,6 +324,7 @@ begin packet[2] := 0; // cdb[4..5] packet[3] := 0; // cdb[6..7] packet[4] := 0; // cdb[8..9] + packet[5] := 0; // cdb[10..11] // 6) Wait for drive to be ready to receive the CDB ready := wait_for_device(device, false); @@ -225,7 +335,7 @@ begin end; // 7) Write our 5 words (10 bytes) of CDB to the drive - for i := 0 to 4 do + for i := 0 to 5 do begin outw(device.base + ATA_REG_DATA, packet[i]); psleep(1); // short delay @@ -236,6 +346,7 @@ begin // 8) Wait for next phase (the data phase). The device should assert DRQ // if it has data to send. We'll poll again. ready := wait_for_device(device, false); + psleep(1); if not ready then begin console.writestringln('Drive not ready after sending the CDB.'); @@ -243,12 +354,12 @@ begin end; // 9) The drive indicates how many bytes it’s about to send in LBA1:LBA2 - xferCount := (inb(device.base + ATA_REG_LBA2) shl 8) or inb(device.base + ATA_REG_LBA1); - if xferCount < 8 then - begin - console.writestringln('Drive is returning less than 8 bytes for capacity!'); - exit; - end; +// xferCount := (inb(device.base + ATA_REG_LBA2) shl 8) or inb(device.base + ATA_REG_LBA1); +// if xferCount < 8 then +// begin +// console.writestringln('Drive is returning less than 8 bytes for capacity!'); +// exit; +// end; // 10) Allocate space for the 8-byte result response := puint16(kalloc(8)); @@ -257,27 +368,58 @@ begin // We'll read 4 words = 8 bytes from the data register for i := 0 to 3 do begin - response[i] := inw(device.base + ATA_REG_DATA); - psleep(1); ready := wait_for_device(device, false); if not ready then begin console.writestringln('Device got stuck while reading capacity data!'); exit; end; + + psleep(3); + + data := inw(device.base + ATA_REG_DATA); + + response[i] := data; + + psleep(1); end; +// //fix the order of the bytes of each number + // endiness of device sector count + temp := puint32(response)[0]; + puint32(response)[0] := 0; + for i := 0 to 31 do begin + puint32(response)[0] := puint32(response)[0] or ((temp shr i) and 1) shl (31 - i); + end; + + //endiness of device sector size + temp := puint32(response)[1]; + puint32(response)[1] := 0; + for i := 0 to 31 do begin + puint32(response)[1] := puint32(response)[1] or ((temp shr i) and 1) shl (31 - i); + end; + // 11) Now parse the returned 8 bytes: // - first 4 bytes = last LBA // - next 4 bytes = block size console.writestring('Device sector count: '); console.writeintln(puint32(response)[0]); // last LBA + console.writebin32ln(puint32(response)[0]); + + console.writestring('Device sector size: '); - console.writeintln(puint32(response)[1]); // block size + console.writeintln(puint32(response)[1] ); // block size + console.writebin32ln(puint32(response)[1]); + + device.blockSize := puint32(response)[1]; + + console.writestringln('Device size: '); + console.writeintln(puint32(response)[0] * (puint32(response)[1]) ); // Return the LBA as "size" (some drivers do lastLBA+1). - get_device_size := puint32(response)[1]; + get_device_size := puint32(response)[0]; end; + end. \ No newline at end of file diff --git a/src/driver/storage/idetypes.pas b/src/driver/storage/idetypes.pas index 4a800907..ecccddc9 100644 --- a/src/driver/storage/idetypes.pas +++ b/src/driver/storage/idetypes.pas @@ -131,6 +131,7 @@ type isATAPI : boolean; status : TIDE_Status; base : uint16; + blockSize : uint32; info : PIdentResponse; end; diff --git a/src/isr/isrmanager.pas b/src/isr/isrmanager.pas index a2e7e41c..36ec748c 100644 --- a/src/isr/isrmanager.pas +++ b/src/isr/isrmanager.pas @@ -33,6 +33,8 @@ procedure init; procedure registerISR(INT_N : uint8; callback : TISRHook); implementation +uses +console; var Hooks : TISRHookArray; @@ -56,6 +58,13 @@ var i : uint8; begin + + // if int_n <> $20 then begin + // console.writestring('ISR: '); + // console.writehexpair(Int_N); + // console.writestringln(' called'); + // end; + for i:=0 to MAX_HOOKS do begin if Hooks[INT_N][i] <> nil then Hooks[INT_N][i](); end;