Compare commits
	
		
			37 Commits
		
	
	
		
			feature/st
			...
			feature/vi
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 8be364c990 | |||
| 0e2c6b2936 | |||
| 632c3fa66b | |||
| 875e3e4765 | |||
| e28d68128d | |||
| a9969d58f8 | |||
| d7a54d858d | |||
| 4e991c3e6f | |||
| a3217de71a | |||
| 19b433a19f | |||
| 69d1d22a18 | |||
| 5c15343ed0 | |||
| 3ae349fbc8 | |||
| 78c060c114 | |||
| cd925e96c2 | |||
| 142dd486dd | |||
| beeeabd441 | |||
| b73c66f6d6 | |||
| 189526cab8 | |||
| 595dd4fbac | |||
| ba6d8037d2 | |||
| 208bda92c8 | |||
| a7111d3cac | |||
| d182fd7f46 | |||
| 4c5038b001 | |||
| 98481ea1ce | |||
| b5582b1284 | |||
| 161cea4920 | |||
| 6b81c4ece0 | |||
| ee17f69115 | |||
| 81c19bff16 | |||
| e7cda58113 | |||
| 44f18554e1 | |||
| 52b42ec975 | |||
| 2815dd9e4d | |||
| d057bfc3ff | |||
| e4621c8aaa | 
							
								
								
									
										52
									
								
								.drone.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								.drone.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,52 @@ | ||||
| kind: pipeline | ||||
| type: docker | ||||
| name: build | ||||
|  | ||||
| steps: | ||||
|   - name: build-image | ||||
|     image: plugins/docker | ||||
|     settings: | ||||
|       repo: t3hn3rd/asuro-build | ||||
|       tags: latest | ||||
|       dockerfile: Dockerfile | ||||
|       registry: docker.io | ||||
|       username: | ||||
|         from_secret: docker_username | ||||
|       password: | ||||
|         from_secret: docker_password | ||||
|  | ||||
|   - name: compile | ||||
|     image: t3hn3rd/asuro-build:latest | ||||
|     depends_on: | ||||
|       - build-image | ||||
|     commands: | ||||
|       - git fetch --tags | ||||
|       - find . -type f -print0 | xargs -0 dos2unix | ||||
|       - chmod +x /drone/src/*.sh | ||||
|       - /drone/src/compile.sh | ||||
|  | ||||
|   - name: upload-iso-artifact | ||||
|     image: alpine/git | ||||
|     depends_on: | ||||
|       - compile | ||||
|     when: | ||||
|       branch: | ||||
|         - master | ||||
|     environment: | ||||
|       GITEA_TOKEN: | ||||
|         from_secret: gitea_token | ||||
|     commands: | ||||
|       - apk add --no-cache curl | ||||
|       - chmod +w Asuro.iso | ||||
|       - cp Asuro.iso "Asuro-$(git rev-parse --short HEAD).iso" | ||||
|       - echo "Uploading Asuro-$(git rev-parse --short HEAD).iso to Gitea Packages..." | ||||
|       - | | ||||
|          curl -X PUT "https://gitea.spexeah.com/api/packages/Spexeah/generic/asuro-iso/$(git rev-parse --short HEAD)/Asuro.iso" \ | ||||
|          -H "Authorization: token $GITEA_TOKEN" --upload-file "Asuro-$(git rev-parse --short HEAD).iso" | ||||
|       - echo "Updating latest ISO reference..." | ||||
|       - | | ||||
|         curl -X DELETE https://gitea.spexeah.com/api/packages/Spexeah/generic/asuro-iso/latest/Asuro.iso \ | ||||
|         -H "Authorization: token $GITEA_TOKEN" || echo "No previous latest version found." | ||||
|       - | | ||||
|         curl -X PUT https://gitea.spexeah.com/api/packages/Spexeah/generic/asuro-iso/latest/Asuro.iso \ | ||||
|         -H "Authorization: token $GITEA_TOKEN" --upload-file "Asuro-$(git rev-parse --short HEAD).iso" | ||||
| @@ -1,73 +0,0 @@ | ||||
| stages: | ||||
|   - Compile Versions | ||||
|   - Compile Sources | ||||
|   - Link | ||||
|   - Generate Documentation | ||||
|   - Deploy | ||||
|  | ||||
| cache: | ||||
|   - key: ${CI_COMMIT_REF_SLUG} | ||||
|     paths: | ||||
|       - lib/*.o | ||||
|       - bin/kernel.bin | ||||
|       - doc | ||||
|  | ||||
| before_script: | ||||
|   - chmod +x *.sh | ||||
|  | ||||
| versions: | ||||
|   stage: Compile Versions | ||||
|   script: | ||||
|     - ./compile_vergen.sh | ||||
|   artifacts: | ||||
|     paths: | ||||
|       - release/*.svg | ||||
|       - src/include/asuro.pas | ||||
|  | ||||
| compile_sources: | ||||
|   stage: Compile Sources | ||||
|   script: | ||||
|     - rm -f lib/*.so | ||||
|     - ./compile_sources.sh | ||||
|   needs: | ||||
|     - versions | ||||
|  | ||||
| link: | ||||
|   stage: Link | ||||
|   script: | ||||
|     - ./compile_stub.sh | ||||
|     - ./compile_link.sh | ||||
|   needs: | ||||
|     - versions | ||||
|     - compile_sources | ||||
|  | ||||
| isogen: | ||||
|   stage: Deploy | ||||
|   script: | ||||
|     - ./compile_isogen.sh | ||||
|     - ./compile_sumgen.sh | ||||
|   artifacts: | ||||
|     paths: | ||||
|       - ./Asuro.iso | ||||
|       - ./release/checksum.svg | ||||
|   needs: | ||||
|     - link | ||||
|  | ||||
| docgen: | ||||
|   stage: Generate Documentation | ||||
|   only: | ||||
|     - master | ||||
|     - develop | ||||
|   script: | ||||
|     - ./compile_sourcelist.sh | ||||
|     - ./compile_docs.sh | ||||
|     # Remove comments when we want to use gitlab pages. | ||||
|     #- cp doc public | ||||
|   allow_failure: true | ||||
|   artifacts: | ||||
|     paths: | ||||
|       - doc | ||||
|       #- public/* | ||||
|       #- ./sources.list | ||||
|   needs: | ||||
|     - versions | ||||
| @@ -16,7 +16,7 @@ RUN curl -sL https://sourceforge.net/projects/freepascal/files/Linux/$FPC_VERSIO | ||||
|  | ||||
| COPY compile.sh /compile.sh | ||||
| ADD https://raw.githubusercontent.com/fsaintjacques/semver-tool/master/src/semver /usr/bin/semver | ||||
| RUN mkdir /code && chmod +x /usr/bin/semver | ||||
| RUN chmod +x /usr/bin/semver | ||||
| WORKDIR /code | ||||
| RUN find . -type f -print0 | xargs -0 dos2unix | ||||
| ENTRYPOINT ["/bin/bash", "-c"] | ||||
|   | ||||
							
								
								
									
										32
									
								
								compile.sh
									
									
									
									
									
								
							
							
						
						
									
										32
									
								
								compile.sh
									
									
									
									
									
								
							| @@ -20,26 +20,30 @@ runOrFail() { | ||||
| 	fi | ||||
| } | ||||
|  | ||||
| runOrFail $(pwd)/compile_stub.sh "Failed to compile stub!" | ||||
| declare -a run_steps=( | ||||
| 	"compile_stub.sh" "Failed to compile stub!" | ||||
| 	"compile_vergen.sh" "Versions failed to compile" | ||||
| 	"compile_sources.sh" "Failed to compile FPC Sources!" | ||||
| 	"compile_link.sh" "Failed linking!" | ||||
| 	"compile_isogen.sh" "Failed to create ISO!" | ||||
| ) | ||||
|  | ||||
| #Generate .pas with versioning headers. | ||||
| runOrFail $(pwd)/compile_vergen.sh "Versions failed to compile" | ||||
|  | ||||
| #Compile all .pas sources | ||||
| runOrFail $(pwd)/compile_sources.sh "Failed to compile FPC Sources!" | ||||
|  | ||||
| #Link into a binary. | ||||
| runOrFail $(pwd)/compile_link.sh "Failed linking!" | ||||
|  | ||||
| #Generate an ISO with GRUB as the Bootloader. | ||||
| runOrFail ./compile_isogen.sh "Failed to create ISO!" | ||||
| for ((i=0; i<${#run_steps[@]}; i+=2)) | ||||
| do | ||||
| 	if [ "$ERRCOUNT" -eq "0" ] | ||||
| 	then | ||||
| 			script=$(pwd)/"${run_steps[$i]}" | ||||
| 			message="${run_steps[$i+1]}" | ||||
| 			runOrFail "$script" "$message" | ||||
| 	fi | ||||
| done | ||||
|  | ||||
| #Call generate final artifacts based on failure or success of the above. | ||||
| if [ "$ERRCOUNT" -ne "0" ] | ||||
| then | ||||
| 	./compile_finish.sh "failed" | ||||
| 	. ./compile_finish.sh "failed" | ||||
| else | ||||
| 	./compile_finish.sh "success" | ||||
| 	. ./compile_finish.sh "success" | ||||
| fi | ||||
|  | ||||
| cd .. | ||||
| @@ -4,4 +4,4 @@ echo "=======================" | ||||
| echo " " | ||||
| echo "Compiling FPC Sources..." | ||||
| echo " " | ||||
| fpc -Aelf -gw -n -O3 -Op3 -Si -Sc -Sg -Xd -CX -XXs -CfSSE -CfSSE2 -Rintel -Pi386 -Tlinux -FElib/ -Fusrc/* -Fusrc/driver/storage/* -Fusrc/driver/storage/AHCI/* -Fusrc/driver/* -Fusrc/driver/net/*  src/kernel.pas | ||||
| fpc -Aelf -gw -g -gl -n -vlewn -O3 -Op3 -Si -Sc -Sg -Xd -CX -XXs -CfSSE -CfSSE2 -Rintel -Pi386 -Tlinux -FElib/ -Fusrc/* -Fusrc/driver/* -Fusrc/driver/net/* src/kernel.pas | ||||
| @@ -1,32 +0,0 @@ | ||||
| #Flat filesystem | ||||
|  | ||||
| A super simple filesystem for asuro. Folders are emulated in filenames. | ||||
|  | ||||
| Starts with disk info sector, sector 0 of volume | ||||
|  | ||||
| --- | ||||
| #### disk info | ||||
|  | ||||
| jmp2boot        : ubit24; | ||||
| OEMName         : array[0..7] of char; | ||||
| version         : uint16 // numerical version of filesystem | ||||
| sectorCount      : uint16; | ||||
| fileCount       : uint16 | ||||
| signature       : uint32 = 0x0B00B1E5 | ||||
|  | ||||
|  | ||||
| the Rest of the sector is reserved | ||||
|  | ||||
| --- | ||||
|  | ||||
| Starting from sector 1 is the file table. Table size is determined by entry size (64) * fileCount | ||||
|  | ||||
| --- | ||||
| ####File entry | ||||
|  | ||||
| name : array[0..59] of char //file name max 60 chars | ||||
| fileStart : 16bit // start sector of data | ||||
| fileSize : 16bit // data size in sectors | ||||
|  | ||||
| --- | ||||
|  | ||||
| @@ -2,7 +2,7 @@ ENTRY(loader) | ||||
| SECTIONS | ||||
| { | ||||
|   . = 0xC0100000; | ||||
|  | ||||
|   kernel_start = .; | ||||
|   .text : AT(ADDR(.text) - 0xC0000000) | ||||
|   { | ||||
|     text = .; _text = .; __text = .; | ||||
| @@ -33,4 +33,5 @@ SECTIONS | ||||
|     . = ALIGN(4096); | ||||
|   } | ||||
|   end = .; _end = .; __end = .; | ||||
|   kernel_end = .; | ||||
| } | ||||
|   | ||||
| @@ -1776,14 +1776,15 @@ var | ||||
|    fb: puint32; | ||||
|  | ||||
| Begin | ||||
|      Ready:= False; | ||||
|      fb:= puint32(uint32(multibootinfo^.framebuffer_addr)); | ||||
|      Console_Properties.Width:= multibootinfo^.framebuffer_width; | ||||
|      Console_Properties.Height:= multibootinfo^.framebuffer_height; | ||||
|      Console_Properties.BitsPerPixel:= multibootinfo^.framebuffer_bpp; | ||||
|      Console_Properties.MAX_CELL_X:= (Console_Properties.Width div 8) - 1; | ||||
|      Console_Properties.MAX_CELL_Y:= (Console_Properties.Height div 16) - 1; | ||||
|      If Console_Properties.BitsPerPixel <> 16 then while true do begin | ||||
|  | ||||
|      If Console_Properties.BitsPerPixel <> 16 then begin | ||||
|         exit; | ||||
|      end; | ||||
|      kpalloc(uint32(fb)); | ||||
|      keyboard.hook(@keyhook); | ||||
|   | ||||
							
								
								
									
										23
									
								
								src/cpu.pas
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								src/cpu.pas
									
									
									
									
									
								
							| @@ -291,6 +291,28 @@ begin | ||||
|     end; | ||||
| end; | ||||
|  | ||||
| procedure enableAVX(); | ||||
| begin | ||||
|     if CPUID.Capabilities1^.AVX then begin | ||||
|         asm | ||||
|             PUSH EAX | ||||
|             PUSH ECX | ||||
|             PUSH EDX | ||||
|             XOR ECX, ECX | ||||
|             db $0F | ||||
|             db $01 | ||||
|             db $D0 | ||||
|             OR EAX, 7 | ||||
|             db $0F | ||||
|             db $01 | ||||
|             db $D1 | ||||
|             POP EDX | ||||
|             POP ECX | ||||
|             POP EAX | ||||
|         end; | ||||
|     end; | ||||
| end; | ||||
|  | ||||
| procedure init(); | ||||
| begin | ||||
|     terminal.registerCommand('CPU', @Terminal_Command_CPU, 'CPU Info.'); | ||||
| @@ -300,6 +322,7 @@ begin | ||||
|     getCPUCapabilities; | ||||
|     getCPUClockSpeed; | ||||
|     enableSSE; | ||||
|     enableAVX; | ||||
| end; | ||||
|  | ||||
| end. | ||||
| @@ -91,7 +91,6 @@ function getDeviceInfo(class_code : uint8; subclass_code : uint8; prog_if : uint | ||||
| procedure requestConfig(bus : uint8; slot : uint8; func : uint8; row : uint8); | ||||
| procedure writeConfig(bus: uint8; slot : uint8; func : uint8; row : uint8; val : uint32); | ||||
| procedure setBusMaster(bus : uint8; slot : uint8; func : uint8; master : boolean); | ||||
| procedure enableDevice(bus : uint8; slot : uint8; func : uint8); | ||||
|  | ||||
| implementation  | ||||
|  | ||||
| @@ -443,34 +442,4 @@ begin | ||||
|     pop_trace; | ||||
| end; | ||||
|  | ||||
| //Enable device inturrupts and set bus master | ||||
| procedure enableDevice(bus : uint8; slot : uint8; func : uint8); | ||||
| var | ||||
|     addr : uint32; | ||||
|     cmd : uint32; | ||||
| begin | ||||
|     push_trace('PCI.enableDevice'); | ||||
|  | ||||
|     addr := ($1 shl 31); | ||||
|     addr := addr or (bus shl 16); | ||||
|     addr := addr or ((slot) shl 11); | ||||
|     addr := addr or ((func) shl 8); | ||||
|     addr := addr or ($04 and $FC); | ||||
|  | ||||
|     outl(PCI_CONFIG_ADDRESS_PORT, addr); | ||||
|     cmd := inl(PCI_CONFIG_DATA_PORT); | ||||
|     cmd := cmd or PCI_COMMAND_MEM_SPACE; | ||||
|     cmd := cmd or PCI_COMMAND_BUS_MASTER; | ||||
|     cmd := cmd or (3 shl 1); | ||||
|  | ||||
|     //enable interrupts, remove disable interrupt bit maybe | ||||
|     cmd := cmd and not PCI_COMMAND_INT_DISABLE; | ||||
|     // cmd := cmd and not (1 shl 9); | ||||
|  | ||||
|     outl(PCI_CONFIG_ADDRESS_PORT, addr); | ||||
|     outl(PCI_CONFIG_DATA_PORT, cmd); | ||||
|  | ||||
|     pop_trace; | ||||
| end; | ||||
|  | ||||
| end. | ||||
| @@ -21,29 +21,6 @@ unit drivertypes; | ||||
|  | ||||
| interface | ||||
|  | ||||
| const | ||||
|     PCI_CONFIG_ADDRESS_PORT = $0CF8; | ||||
|     PCI_CONFIG_DATA_PORT    = $0CFC; | ||||
|  | ||||
|     PCI_COMMAND_IO_SPACE    = $0001; | ||||
|     PCI_COMMAND_MEM_SPACE   = $0002; | ||||
|     PCI_COMMAND_BUS_MASTER  = $0004; | ||||
|     PCI_COMMAND_SPECIAL_CYC = $0008; | ||||
|     PCI_COMMAND_MEM_WRITE   = $0010; | ||||
|     PCI_COMMAND_VGA_PALETTE = $0020; | ||||
|     PCI_COMMAND_PARITY      = $0040; | ||||
|     PCI_COMMAND_WAIT        = $0080; | ||||
|     PCI_COMMAND_SERR        = $0100; | ||||
|     PCI_COMMAND_FAST_BACK   = $0200; | ||||
|     PCI_COMMAND_INT_DISABLE = $0400; | ||||
|     PCI_COMMAND_SERR_ENABLE = $8000; | ||||
|  | ||||
|     PCI_CAP_ID_MSI           = $05; | ||||
|  | ||||
|     // Bits in the MSI Control register (16-bit): | ||||
|     MSI_CONTROL_ENABLE       = 1 shl 0;  // Bit 0 | ||||
|     MSI_CONTROL_64BIT        = 1 shl 7;  // Bit 7 | ||||
|     MSI_CONTROL_PVMASK       = 1 shl 8;  // Bit 8 (optional: per-vector masking) | ||||
| type | ||||
|  | ||||
|     PPCI_Device = ^TPCI_Device; | ||||
| @@ -67,7 +44,6 @@ type | ||||
|         address1       : uint32; | ||||
|         address2       : uint32; | ||||
|         address3       : uint32; | ||||
|  | ||||
|         address4       : uint32; | ||||
|         address5       : uint32; | ||||
|         CIS_pointer    : uint32; | ||||
|   | ||||
							
								
								
									
										296
									
								
								src/driver/storage/AHCI.pas
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										296
									
								
								src/driver/storage/AHCI.pas
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,296 @@ | ||||
| //  Copyright 2021 Aaron Hance | ||||
| // | ||||
| //  Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| //  you may not use this file except in compliance with the License. | ||||
| //  You may obtain a copy of the License at | ||||
| // | ||||
| //  http://www.apache.org/licenses/LICENSE-2.0 | ||||
| // | ||||
| //  Unless required by applicable law or agreed to in writing, software | ||||
| //  distributed under the License is distributed on an "AS IS" BASIS, | ||||
| //  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| //  See the License for the specific language governing permissions and | ||||
| //  limitations under the License. | ||||
|  | ||||
| {  | ||||
| 	Drivers->Storage->AHCI - AHCI SATA Driver. | ||||
| 	 | ||||
| 	@author(Aaron Hance <ah@aaronhance.me>) | ||||
| } | ||||
| unit AHCI; | ||||
|  | ||||
| interface | ||||
|  | ||||
| uses  | ||||
|     util, | ||||
|     PCI, | ||||
|     drivertypes, | ||||
|     drivermanagement, | ||||
|     lmemorymanager, | ||||
|     console, | ||||
|     vmemorymanager; | ||||
|  | ||||
| type | ||||
|  | ||||
| //Struct hell | ||||
|  | ||||
|     TFIS_Type = ( | ||||
|         REG_H2D = $27, | ||||
|         REG_D2H = $34, | ||||
|         DMA_ACT = $39, | ||||
|         DMA_SETUP = $41, | ||||
|         DATA = $46, | ||||
|         BIST = $58, | ||||
|         PIO_SETUP = $5F, | ||||
|         DEV_BITS = $A0 | ||||
|     ); | ||||
|  | ||||
|     PFIS_REG_H2D = ^TFIS_REG_H2D; | ||||
|     TFIS_REG_H2D = bitpacked record | ||||
|         fis_type     : uint8;  | ||||
|         port_mult    : UBit4; | ||||
|         rsv0         : UBit3; | ||||
|         coc          : boolean; | ||||
|         command      : uint8; | ||||
|         feature_low  : uint8; | ||||
|         lba0         : uint8; | ||||
|         lba1         : uint8; | ||||
|         lba2         : uint8; | ||||
|         device       : uint8; | ||||
|         lba3         : uint8; | ||||
|         lba4         : uint8; | ||||
|         lba5         : uint8; | ||||
|         feature_high : uint8; | ||||
|         count_low    : uint8; | ||||
|         count_high   : uint8; | ||||
|         icc          : uint8; | ||||
|         control      : uint8; | ||||
|         rsvl         : uint32; | ||||
|     end;          | ||||
|      | ||||
|     TFIS_REG_D2H = bitpacked record | ||||
|         fis_type     : uint8;  | ||||
|         port_mult    : UBit4; | ||||
|         rsv0         : UBit2; | ||||
|         i            : boolean; | ||||
|         rsvl         : boolean; | ||||
|         status       : uint8; | ||||
|         error        : uint8; | ||||
|         lba0         : uint8; | ||||
|         lba1         : uint8; | ||||
|         lba2         : uint8; | ||||
|         device       : uint8; | ||||
|         lba3         : uint8; | ||||
|         lba4         : uint8; | ||||
|         lba5         : uint8; | ||||
|         rsv2         : uint8; | ||||
|         count_low    : uint8; | ||||
|         count_high   : uint8; | ||||
|         rsv3         : uint16; | ||||
|         rsv4         : uint32; | ||||
|     end; | ||||
|  | ||||
|     TFIS_Data = bitpacked record | ||||
|         fis_type  : uint8; | ||||
|         port_mult : UBit4; | ||||
|         rsv0      : UBit4; | ||||
|         rsv1      : uint16; | ||||
|         data      : ^uint32; | ||||
|     end; | ||||
|  | ||||
|     TFIS_PIO_Setup = bitpacked record | ||||
|         fis_type : uint8; | ||||
|         pmport   : UBit4; | ||||
|         rsv0     : boolean; | ||||
|         d        : boolean; | ||||
|         i        : boolean; | ||||
|         rsv1     : boolean; | ||||
|         status   : uint8; | ||||
|         error    : uint8; | ||||
|         lba0     : uint8; | ||||
|         lba1     : uint8; | ||||
|         lba2     : uint8; | ||||
|         device   : uint8; | ||||
|         lba3     : uint8; | ||||
|         lba4     : uint8; | ||||
|         lba5     : uint8; | ||||
|         rsv2     : uint8; | ||||
|         countl   : uint8; | ||||
|         counth   : uint8; | ||||
|         rsv3     : uint8; | ||||
|         e_status : uint8; | ||||
|         tc       : uint16; | ||||
|         rsv4     : uint16; | ||||
|     end; | ||||
|  | ||||
|     // TFIS_DMA_Setup = bitpacked record | ||||
|     // end; | ||||
|  | ||||
|     // THBA_Memory = bitpacked record | ||||
|     // end; | ||||
|  | ||||
|     // THBA_Port = bitpacked record | ||||
|     // end; | ||||
|  | ||||
|     // THBA_FIS = bitpacked record | ||||
|     // end; | ||||
|  | ||||
|     PHBA_PORT = ^THBA_PORT; | ||||
|     THBA_PORT = bitpacked record | ||||
|         clb : uint32; | ||||
|         clbu : uint32; | ||||
|         fb : uint32; | ||||
|         fbu : uint32; | ||||
|         istat : uint32; | ||||
|         ie : uint32; | ||||
|         cmd : uint32; | ||||
|         rsv0 : uint32; | ||||
|         tfd : uint32; | ||||
|         sig : uint32; | ||||
|         ssts : uint32; | ||||
|         sctl : uint32; | ||||
|         serr : uint32; | ||||
|         sact : uint32; | ||||
|         ci : uint32; | ||||
|         sntf : uint32; | ||||
|         fbs : uint32; | ||||
|         rsv1 : array[0..11] of uint32; | ||||
|         vendor : array[0..4] of uint32; | ||||
|     end;  | ||||
|  | ||||
|     THBA_MEM = bitpacked record  | ||||
|         cap                 : uint32; //0 | ||||
|         global_host_control : uint32; //4 | ||||
|         interrupt_status    : uint32; //8 | ||||
|         port_implemented    : uint32; //c | ||||
|         version             : uint32; //10 | ||||
|         ccc_control         : uint32; //14 | ||||
|         ccc_ports           : uint32; //18 | ||||
|         em_location         : uint32; //1c | ||||
|         em_Control          : uint32; //20 | ||||
|         hcap2               : uint32; //24 | ||||
|         bohc                : uint32; //28 | ||||
|         rsv0                : array[0..210] of boolean; | ||||
|         ports               : array[0..31] of THBA_Port; | ||||
|     end; | ||||
|  | ||||
|     PHBA = ^THBA_MEM; | ||||
|  | ||||
|     PCMDHeader = ^ TCommand_Header; | ||||
|     TCommand_Header = bitpacked record | ||||
|         cfl    : ubit5; | ||||
|         a      : boolean; | ||||
|         w      : boolean; | ||||
|         p      : boolean; | ||||
|         r      : boolean; | ||||
|         b      : boolean; | ||||
|         c      : boolean; | ||||
|         rsv0   : boolean; | ||||
|         pmp    : ubit4; | ||||
|         PRDTL  : uint16; | ||||
|         PRDTBC : uint32; | ||||
|         CTBA   : uint32; | ||||
|         CTBAU  : uint32; | ||||
|         rsv1   : array[0..3] of uint32; | ||||
|     end; | ||||
|  | ||||
|     TPRD_Entry = bitpacked record | ||||
|         data_base_address   : uint32; | ||||
|         data_bade_address_U : uint32; | ||||
|         rsv0                : uint32; | ||||
|         data_byte_count     : ubit22; | ||||
|         rsv1                : ubit9; | ||||
|         interrupt_oc        : boolean; | ||||
|     end; | ||||
|  | ||||
|     PCommand_Table = ^TCommand_Table; | ||||
|     TCommand_Table = bitpacked record | ||||
|         cfis : array[0..64] of uint8; | ||||
|         acmd : array[0..16] of uint8; | ||||
|         rsv  : array[0..48] of uint8; | ||||
|         prdt : array[0..7] of TPRD_Entry; | ||||
|     end; | ||||
|  | ||||
|     TSataDevice = record | ||||
|         controller : uint8; | ||||
|         port : uint8; | ||||
|     end; | ||||
|  | ||||
| var | ||||
|     //constants | ||||
|     //SATA_SIG_ATA   := $101; | ||||
|     //SATA_SIG_ATAPI := $EB140101; | ||||
|     //STA_SIG_SEMB  := $C33C0101; | ||||
|     //STAT_SIG_PM    := $96690101; | ||||
|     AHCI_BASE: uint32 = $400000; //irrelivent | ||||
|  | ||||
|     //other  | ||||
|     ahciControllers     : array[0.16] of PuInt32; | ||||
|     ahciControllerCount : uint8 = 0; | ||||
|     hba                 : array[0..16] of PHBA; | ||||
|  | ||||
|     sataDevices     : array[0..127] of TSataDevice; | ||||
|     sataDeviceCount : uint8; | ||||
|  | ||||
|      | ||||
|  | ||||
| procedure init(); | ||||
| procedure check_ports(controller : uint8); | ||||
| procedure enable_cmd(port : uint8); | ||||
| procedure disable_cmd(port : uint8); | ||||
| procedure port_rebase(port : uint8); | ||||
| function load(ptr:void): boolean; | ||||
| function read(port : uint8; startl : uint32; starth : uint32; count : uint32; buf : PuInt32) : boolean; | ||||
| function write(port : uint8; startl : uint32; starth : uint32; count : uint32; buf : PuInt32) : boolean; | ||||
| function find_cmd_slot(port : uint8) : uint32; | ||||
|  | ||||
| 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('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; | ||||
|  | ||||
| procedure check_ports(controller : uint8); | ||||
| var | ||||
|     d : uint32 = 1; | ||||
|     i : uint32; | ||||
| begin | ||||
|     for i:=0 to 31 do begin | ||||
|         if (d > 0) and (hba[controller]^.port_implemented shr i) = 1 then begin | ||||
|             if (hba[controller]^.ports[i].ssts shr 8) <> 1 and (hba[controller]^.ports[i].ssts and $0F) <> 3 then begin | ||||
|                 if hba[controller]^.ports[i].sig = 1 then begin | ||||
|                     //device is sata | ||||
|                     sataDevices[sataDeviceCount].controller := controller; | ||||
|                     sataDevices[sataDeviceCount].port := i; | ||||
|                     sataDeviceCount += 1; | ||||
|                 end; | ||||
|                 //TODO implement ATAPI | ||||
|             end; | ||||
|         end; | ||||
|     end; | ||||
| end; | ||||
| @@ -1,899 +0,0 @@ | ||||
|  | ||||
| //  Copyright 2021 Aaron Hance | ||||
| // | ||||
| //  Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| //  you may not use this file except in compliance with the License. | ||||
| //  You may obtain a copy of the License at | ||||
| // | ||||
| //  http://www.apache.org/licenses/LICENSE-2.0 | ||||
| // | ||||
| //  Unless required by applicable law or agreed to in writing, software | ||||
| //  distributed under the License is distributed on an "AS IS" BASIS, | ||||
| //  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| //  See the License for the specific language governing permissions and | ||||
| //  limitations under the License. | ||||
|  | ||||
| {  | ||||
| 	Drivers->Storage->AHCI->AHCI - AHCI Driver. | ||||
| 	 | ||||
| 	@author(Aaron Hance <ah@aaronhance.me>) | ||||
| } | ||||
| unit AHCI; | ||||
|  | ||||
| interface | ||||
|  | ||||
| uses | ||||
|     AHCITypes, | ||||
|     console, | ||||
|     drivermanagement, | ||||
|     drivertypes, | ||||
|     idetypes, | ||||
|     isrmanager, | ||||
|     lists, | ||||
|     lmemorymanager, | ||||
|     PCI, | ||||
|     util, | ||||
|     vmemorymanager; | ||||
|  | ||||
| var | ||||
|     ahciControllers : PDList; | ||||
|     page_base : puint32; | ||||
|  | ||||
| procedure init(); | ||||
| function load(ptr : void) : boolean; | ||||
| procedure check_ports(controller : PAHCI_Controller); | ||||
| procedure identify_device(controller : PAHCI_Controller; portIndex : uint32; isATAPI : boolean); | ||||
| procedure ahci_isr(); | ||||
| function find_cmd_slot(device : PAHCI_Device) : uint32; | ||||
| function send_read_dma(device : PAHCI_Device; lba : uint64; count : uint32; buffer : puint32) : boolean; | ||||
| // function send_write_dma(device : PAHCI_Device; lba : uint64; count : uint32; buffer : puint32) : boolean; | ||||
| function send_write_dma(device : PAHCI_Device; lba : uint64; count : uint32; buffer : puint32) : boolean; | ||||
| function read_atapi(device : PAHCI_Device; lba : uint64; count : uint32; buffer : puint32) : boolean; | ||||
|  | ||||
|  | ||||
| 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); | ||||
|     //TODO check IDE devices in ide for sata devices | ||||
| end; | ||||
|  | ||||
| procedure stop_port(port : PHBA_Port); | ||||
| begin | ||||
|     port^.cmd := port^.cmd and not $1; | ||||
|     port^.cmd := port^.cmd and not $100; | ||||
|     while ((port^.cmd and $4000) or (port^.cmd and $8000)) <> 0 do begin | ||||
|         //wait for the port to stop | ||||
|     end; | ||||
| end; | ||||
|  | ||||
| procedure start_port(port : PHBA_Port); | ||||
| begin | ||||
|  | ||||
|     while (port^.cmd and $8000) <> 0 do begin | ||||
|         //wait for port to not be busy | ||||
|     end; | ||||
|     port^.cmd := port^.cmd or $100; | ||||
|     port^.cmd := port^.cmd or $1; | ||||
| end; | ||||
|  | ||||
| procedure reset_port(port : PHBA_Port); | ||||
| var | ||||
|   ssts, serr, timeout : uint32; | ||||
| begin | ||||
|   console.writestringln('AHCI: Performing a full port reset.'); | ||||
|  | ||||
|     port^.cmd := port^.cmd and not $1; | ||||
|  | ||||
|     // Wait until CR (bit 15) is cleared. | ||||
|     timeout := 0; | ||||
|     while (port^.cmd and $8000) <> 0 do begin | ||||
|         timeout := timeout + 1; | ||||
|         if timeout > 1000000 then begin | ||||
|             console.writestringln('AHCI: Port reset timeout.'); | ||||
|             break; | ||||
|         end; | ||||
|     end; | ||||
|  | ||||
|     port^.sata_ctrl := port^.sata_ctrl or $FFFF0000; | ||||
|  | ||||
|     while (port^.sata_status and $F) <> 3 do begin | ||||
|         //wait for the port to be ready | ||||
|     end; | ||||
|  | ||||
|     // Clear the SERR register by writing 1s to it. | ||||
|     port^.sata_error := $FFFFFFFF; | ||||
|  | ||||
|     start_port(port); | ||||
| end; | ||||
|  | ||||
| // procedure reset_port(port : PHBA_Port); | ||||
| // var | ||||
| //   ssts, serr, timeout : uint32; | ||||
| // begin | ||||
| //   console.writestringln('AHCI: Performing a full port reset.'); | ||||
|  | ||||
| //     port^.cmd := port^.cmd and not $1; | ||||
|  | ||||
| //     // Wait until CR (bit 15) is cleared. | ||||
| //     timeout := 0; | ||||
| //     while (port^.cmd and $8000) <> 0 do begin | ||||
| //         timeout := timeout + 1; | ||||
| //         if timeout > 1000000 then begin | ||||
| //             console.writestringln('AHCI: Port reset timeout.'); | ||||
| //             break; | ||||
| //         end; | ||||
| //     end; | ||||
|  | ||||
| //     port^.sata_ctrl := port^.sata_ctrl and $FFFF0000; | ||||
| //     port^.sata_ctrl := port^.sata_ctrl or $1; | ||||
| //     psleep(10); | ||||
| //     port^.sata_ctrl := port^.sata_ctrl and $FFFF0000; | ||||
|  | ||||
| //     while (port^.sata_status and $F) <> $3 do begin | ||||
| //         //wait for the port to be ready | ||||
| //     end; | ||||
|  | ||||
| //     // Clear the SERR register by writing 1s to it. | ||||
| //     port^.sata_error := $FFFFFFFF; | ||||
|  | ||||
| //     // start_port(port); | ||||
| // end; | ||||
|  | ||||
|  | ||||
| { | ||||
|     Check the ports on the controller and setup the command list, FIS, and command table | ||||
| } | ||||
| procedure check_ports(controller : PAHCI_Controller); | ||||
| var | ||||
|     i : uint32; | ||||
|     ii : uint32; | ||||
|     port : PHBA_Port; | ||||
|     device : PAHCI_Device; | ||||
|     cmd_list_base : puint32; | ||||
|     fis_base : puint32; | ||||
|     cmd_table_base : puint32; | ||||
|     command_header : PHBA_CMD_HEADER; | ||||
| begin | ||||
|  | ||||
|     console.writestring('AHCI: active ports: '); | ||||
|     console.writehexln(controller^.mio^.ports_implimented); | ||||
|  | ||||
|     for i:=0 to 31 do begin | ||||
|         port := @controller^.mio^.ports[i]; | ||||
|         console.writehex(port^.signature); | ||||
|         console.writestring(' '); | ||||
|     end; | ||||
|  | ||||
|     for i:=0 to 31 do begin | ||||
|         if ((controller^.mio^.ports_implimented shr i) and 1) = 1 then begin | ||||
|             port := @controller^.mio^.ports[i]; | ||||
|             console.writestring('AHCI: Port '); | ||||
|             console.writeint(i); | ||||
|             console.writestring(' implemented on controller '); | ||||
|             console.writehexln(uint32(controller^.pci_device^.address5)); | ||||
|  | ||||
|             device := PAHCI_Device(@controller^.devices[i]); | ||||
|             device^.port := port; | ||||
|  | ||||
|             //check if the port is active TODO | ||||
|  | ||||
|             console.writestring('AHCI: signature: '); | ||||
|             console.writehexln(port^.signature); | ||||
|  | ||||
|             //check device type | ||||
|             case port^.signature of | ||||
|                 SATA_SIG_ATA: begin | ||||
|                     console.writestringln('AHCI: Device is SATA'); | ||||
|                     device^.device_type := SATA; | ||||
|                 end; | ||||
|                 SATA_SIG_ATAPI: begin | ||||
|                     console.writestringln('AHCI: Device is ATAPI'); | ||||
|                     device^.device_type := ATAPI; | ||||
|                 end; | ||||
|                 SATA_SIG_SEMB: begin | ||||
|                     console.writestringln('AHCI: Device is SEMB'); | ||||
|                     device^.device_type := SEMB; | ||||
|                 end; | ||||
|                 SATA_SIG_PM: begin | ||||
|                     console.writestringln('AHCI: Device is PM'); | ||||
|                     device^.device_type := PM; | ||||
|                 end; | ||||
|             end; | ||||
|  | ||||
|             //NEEED TO STOP the port before doing anything | ||||
|             stop_port(port); | ||||
|             console.writestringln('AHCI: Port stopped'); | ||||
|  | ||||
|             //allocate memory for the command list and ensure it is aligned to 1024 bytes | ||||
|             cmd_list_base := kalloc(sizeof(THBA_CMD_HEADER) * 64); | ||||
|             cmd_list_base := puint32((uint32(cmd_list_base) + 1023) and $FFFFFC00); | ||||
|             memset(uint32(cmd_list_base), 0, sizeof(THBA_CMD_HEADER) * 32); | ||||
|  | ||||
|             //set the command list base address | ||||
|             port^.cmdl_basel := vtop(uint32(cmd_list_base)); //todo set virtual address in device | ||||
|             port^.cmdl_baseu := 0; | ||||
|  | ||||
|             device^.command_list := PHBA_CMD_HEADER(cmd_list_base); | ||||
|  | ||||
|  | ||||
|             //print the command list base address | ||||
|             console.writestring('AHCI: Command list base address: '); | ||||
|             console.writehexln(uint32(cmd_list_base)); | ||||
|  | ||||
|             //allocate memory for the FIS and ensure it is aligned to 256 bytes | ||||
|             fis_base := kalloc(sizeof(THBA_FIS) * 8); | ||||
|             fis_base := puint32((uint32(fis_base) + 255) and $FFFFFF00); | ||||
|  | ||||
|             //set the FIS base address | ||||
|             port^.fis_basel := vtop(uint32(fis_base)); | ||||
|             port^.fis_baseu := 0; | ||||
|  | ||||
|             memset(uint32(fis_base), 0, sizeof(THBA_FIS)); | ||||
|  | ||||
|             device^.fis := PHBA_FIS(fis_base); | ||||
|             //todo check how many simultaneous commands are supported | ||||
|  | ||||
|             //allocate memory for the command table and ensure it is aligned to 128 bytes | ||||
|             cmd_table_base := kalloc(sizeof(THBA_CMD_TABLE) * 64); | ||||
|             cmd_table_base := puint32((uint32(cmd_table_base) + 127) and $FFFFFF80); | ||||
|             memset(uint32(cmd_table_base), 0, sizeof(THBA_CMD_TABLE) * 32); | ||||
|  | ||||
|             device^.command_table := PHBA_CMD_TABLE(cmd_table_base); | ||||
|  | ||||
|             //set the command table base address and setup command table | ||||
|             for ii:=0 to 31 do begin | ||||
|                 //set command header locations | ||||
|                 command_header := PHBA_CMD_HEADER(uint32(cmd_list_base) + (ii * sizeof(THBA_CMD_HEADER))); | ||||
|                 command_header^.prdtl := 32; | ||||
|                 command_header^.cmd_table_base := vtop(uint32(cmd_table_base)) + (ii * sizeof(THBA_CMD_TABLE)); | ||||
|                 command_header^.cmd_table_baseu := 0; | ||||
|             end; | ||||
|  | ||||
|             //reset the port | ||||
|             reset_port(port); | ||||
|  | ||||
|             //print  sata status | ||||
|             console.writestring('AHCI: sata status: '); | ||||
|             console.writehexln(port^.sata_status); | ||||
|  | ||||
|             // //pass devices count as second param | ||||
|             if (device^.device_type = SATA) then begin | ||||
|                 identify_device(controller, i, false); | ||||
|             end else if (device^.device_type = ATAPI) then begin | ||||
|                 identify_device(controller, i, true); | ||||
|             end else begin | ||||
|                 console.writestringln('AHCI: Unsupported device type'); | ||||
|             end; | ||||
|  | ||||
|             controller^.mio^.int_status := $FFFFFFFF; | ||||
|         end; | ||||
|     end; | ||||
| end; | ||||
|  | ||||
| procedure identify_device(controller : PAHCI_Controller; portIndex : uint32; isATAPI : boolean); | ||||
| var | ||||
|     fis         : PHBA_FIS_REG_H2D; | ||||
|     cmd_header  : PHBA_CMD_HEADER; | ||||
|     cmd_table   : PHBA_CMD_TABLE; | ||||
|     cmd         : uint32; | ||||
|     i, timeout  : uint32; | ||||
|     buffer      : puint32; | ||||
|     device      : PAHCI_Device; | ||||
|     tfd         : uint32; | ||||
|     sec_count   : uint32; | ||||
|     b8          : puint16; | ||||
| begin | ||||
|     console.writestring('AHCI: Identifying device '); | ||||
|     console.writeintln(portIndex); | ||||
|     device := PAHCI_Device(@controller^.devices[portIndex]); | ||||
|      | ||||
|     // (Optional) Print the device type if you store it: | ||||
|     console.writestring('AHCI: Device type: '); | ||||
|     // For example: if device^.device_type = SATA then ... | ||||
|     console.writestring('AHCI: sata status: '); | ||||
|     console.writehexln(device^.port^.sata_status); | ||||
|     console.writestring('AHCI: signature: '); | ||||
|     console.writehexln(device^.port^.signature); | ||||
|     console.writestring('AHCI: device type: '); | ||||
|     console.writeintln(uint32(device^.device_type)); | ||||
|  | ||||
|     //clear any pending interrupts | ||||
|     device^.port^.int_status := $FFFFFFFF; | ||||
|     device^.port^.int_enable := $0; | ||||
|      | ||||
|     // Allocate a 512-byte DMA buffer for the IDENTIFY data | ||||
|     buffer := kalloc(512); | ||||
|     memset(uint32(buffer), 0, 512); | ||||
|  | ||||
|     // Use command slot 0 for the IDENTIFY command. | ||||
|     cmd_header := device^.command_list;  // Assuming slot 0 is at the beginning. | ||||
|     cmd_header^.cmd_fis_length := sizeof(THBA_FIS_REG_H2D) div sizeof(uint32); | ||||
|     cmd_header^.wrt := 0; | ||||
|     cmd_header^.prdtl := 1; | ||||
|     cmd_header^.clear_busy := 1; | ||||
|     if isATAPI then begin | ||||
|         cmd_header^.atapi := 1; | ||||
|     end; | ||||
|  | ||||
|  | ||||
|     // Setup the command table (using slot 0) | ||||
|     cmd_table := device^.command_table;   | ||||
|     cmd_table^.prdt[0].dba := vtop(uint32(buffer)); | ||||
|     cmd_table^.prdt[0].dbc := 511;   // 512 bytes (0-based count) | ||||
|     cmd_table^.prdt[0].int := 1;     // Interrupt on completion | ||||
|  | ||||
|     // if isATAPI then begin | ||||
|     // // ATAPI Identify Device command | ||||
|     // buffer[0] := $A1;  // ATAPI Identify Device | ||||
|     // end; | ||||
|  | ||||
|     // Construct the Command FIS in the command table's CFIS area | ||||
|     fis := PHBA_FIS_REG_H2D(@device^.command_table^.cmd_fis); | ||||
|     memset(uint32(fis), 0, sizeof(THBA_FIS_REG_H2D)); | ||||
|     fis^.fis_type := uint8(FIS_TYPE_REG_H2D); | ||||
|     fis^.c   := $1; | ||||
|     if isATAPI then begin | ||||
|         fis^.command  := ATA_CMD_IDENTIFY_PACKET; | ||||
|     end else begin | ||||
|         fis^.command  := ATA_CMD_IDENTIFY; | ||||
|     end; | ||||
|     fis^.device   := $A0;  // LBA mode 40? | ||||
|  | ||||
|     //waut for the port to be ready, bit 7 in the TFD register and bit 3 tfd | ||||
|     while (device^.port^.tfd and $88) <> 0 do begin | ||||
|         // console.writestring('AHCI: tfd: '); | ||||
|         // console.writehexln(device^.port^.tfd); | ||||
|     end; | ||||
|  | ||||
|     // Issue the command by setting bit 0 in the port's Command Issue register. | ||||
|     cmd := device^.port^.cmd_issue; | ||||
|     cmd := cmd or $1; | ||||
|     device^.port^.cmd_issue := cmd; | ||||
|  | ||||
|     console.writestringln('AHCI: Sent identify command'); | ||||
|  | ||||
|  | ||||
|     // Wait for command completion with a timeout. | ||||
|     timeout := 0; | ||||
|     repeat | ||||
|       cmd := device^.port^.cmd_issue;  // Command Issue register | ||||
|       tfd := device^.port^.tfd;  // Task File Data often contains error/status info. | ||||
|       device^.port^.int_status := $FFFFFFFF;  // Clear any pending interrupts | ||||
|       timeout := timeout + 10; | ||||
|       if timeout > 100000 then begin | ||||
|           console.writestringln('AHCI: IDENTIFY command timeout'); | ||||
|           break; | ||||
|       end; | ||||
|         // check for bit 30 in the tfd register | ||||
|         if (tfd and $40000000) <> 0 then begin | ||||
|             console.writestringln('AHCI: error'); | ||||
|             break; | ||||
|         end; | ||||
|  | ||||
|     until ((device^.port^.cmd_issue and $1) = 0);  // Wait until slot 0 is cleared | ||||
|  | ||||
|     console.writestringln('AHCI: Command complete'); | ||||
|  | ||||
|     // Check for an error flag in the command register (bit 1) | ||||
|     if (cmd and $2) <> 0 then begin | ||||
|         console.writestringln('AHCI: Error sending identify command'); | ||||
|  | ||||
|         // Check the error register for more information | ||||
|         console.writestring('AHCI: Error register: '); | ||||
|         console.writehexln(device^.port^.sata_error); | ||||
|     end; | ||||
|  | ||||
|     // Dump the 512-byte IDENTIFY data for debugging (display 64 DWORDs) | ||||
|     for i := 0 to 63 do begin | ||||
|         console.writehex(uint32(buffer[i])); | ||||
|         console.writestring(' '); | ||||
|     end; | ||||
|  | ||||
|     console.writestringln(''); | ||||
|  | ||||
|     b8 := puint16(buffer); | ||||
|     sec_count := (b8[61] shl 16) or b8[60]; | ||||
|     console.writestring('AHCI: Sector count: '); | ||||
|     console.writeintln(sec_count); | ||||
|     console.writestring('AHCI: Device size: '); | ||||
|     console.writeint(sec_count * 512 div 1024 div 1024); | ||||
|     console.writestringln(' MB'); | ||||
|  | ||||
|  | ||||
|     console.writestringln(''); | ||||
|  | ||||
|     buffer := puint32(kalloc(2048)); | ||||
|     memset(uint32(buffer), 0, 2048); | ||||
|  | ||||
|     //send a read DMA command to the device | ||||
|     if isATAPI then begin | ||||
|         read_atapi(device, 0, 2048, buffer); | ||||
|     end else begin | ||||
|         memset(uint32(buffer), $77, 2048); | ||||
|         send_write_dma(device, 22, 512, buffer); | ||||
|         memset(uint32(buffer), 0, 2048); | ||||
|  | ||||
|         send_read_dma(device, 22, 512, buffer); | ||||
|     end; | ||||
|  | ||||
|     //print the buffer | ||||
|     for i:=0 to 63 do begin | ||||
|         console.writehex(puInt8(buffer)[i]); | ||||
|         console.writestring(' '); | ||||
|     end; | ||||
|     console.writestringln(''); | ||||
|  | ||||
|  | ||||
| end; | ||||
|  | ||||
|  | ||||
| function load(ptr : void) : boolean; | ||||
| var  | ||||
|     device : PPCI_Device; | ||||
|     controller : PAHCI_Controller; | ||||
|     i : uint32; | ||||
|     base : PHBA_Memory; | ||||
|     cmd_list_base : puint32; | ||||
|     fis_base : puint32; | ||||
|     cmd_table_base : puint32; | ||||
|     int_no : uInt8; | ||||
|     timeout : uint32; | ||||
|     bohc : uint32; | ||||
| begin | ||||
|     console.writestringln('AHCI: initilizing a new controller'); | ||||
|  | ||||
|     if ahciControllers = nil then begin | ||||
|         console.writestringln('AHCI: Initializing controller list'); | ||||
|         ahciControllers := DL_New(SizeOf(TAHCI_Controller)); | ||||
|     end; | ||||
|  | ||||
|     device := PPCI_Device(ptr); | ||||
|  | ||||
|     pci.enableDevice(device^.bus, device^.slot, device^.func); | ||||
|     psleep(5); | ||||
|     int_no := device^.interrupt_line + 32; | ||||
|  | ||||
|  | ||||
|     //print interrupt number | ||||
|     console.writestring('AHCI: Interrupt number: '); | ||||
|     console.writehexln(int_no); | ||||
|  | ||||
|     controller := PAHCI_Controller(DL_Add(ahciControllers)); | ||||
|  | ||||
|     controller^.pci_device := PPCI_Device(device); | ||||
|      | ||||
| //     Perform BIOS/OS handoff (if the bit in the extended capabilities is set) | ||||
| //     Reset controller | ||||
|  | ||||
|     //get the base address of the controller | ||||
|     page_base := kpalloc(device^.address5); // TODO MEM memory manager need to be improved | ||||
|     base := PHBA_Memory(device^.address5); | ||||
|     controller^.mio := base; | ||||
|  | ||||
|     if (controller^.mio^.capabilites2 and $28) <> 0 then begin | ||||
|         console.writestringln('AHCI: Controller needs to be handed off to the OS'); | ||||
|     end; | ||||
|  | ||||
|     console.writestring('AHCI: Controller at: '); | ||||
|     console.writehexln(device^.address5); | ||||
|     console.writehexln(uint32(page_base)); | ||||
|  | ||||
|     bohc := controller^.mio^.bohc; | ||||
|  | ||||
|     if (bohc and $1) <> 0 or (bohc and not (1 shl 1)) then begin | ||||
|         console.writestringln('AHCI: BIOS Owned Semaphore is set, initiating handoff.'); | ||||
|  | ||||
|         // Set the OS Owned Semaphore (typically bit 1). | ||||
|         bohc := bohc or (1 shl 1); | ||||
|         controller^.mio^.bohc := bohc; | ||||
|  | ||||
|         // Wait until the BIOS Owned Semaphore (bit 0) is cleared. | ||||
|         timeout := 0; | ||||
|         while ((controller^.mio^.bohc and $1) <> 0) and (timeout < 1000000) do begin | ||||
|         timeout := timeout + 1; | ||||
|         end; | ||||
|  | ||||
|         if timeout = 1000000 then begin | ||||
|         console.writestringln('AHCI: BIOS/OS handoff timed out.'); | ||||
|         end else begin | ||||
|         console.writestringln('AHCI: BIOS/OS handoff successful.'); | ||||
|         end; | ||||
|     end else begin | ||||
|         console.writestringln('AHCI: BIOS not holding controller or handoff already complete.'); | ||||
|     end; | ||||
|  | ||||
|     base^.global_ctrl := base^.global_ctrl or 1; | ||||
|  | ||||
|     while (base^.global_ctrl and 1) <> 0 do begin | ||||
|     end; | ||||
|      | ||||
|  | ||||
|     registerISR(int_no, @ahci_isr); | ||||
|  | ||||
|     //enable AHCI mode, TODO check if is not already in AHCI mode and enable it, also perhaps remove if loaded as IDE | ||||
|     base^.global_ctrl := base^.global_ctrl or AHCI_CONTROLLER_MODE; | ||||
|     // base^.global_ctrl := base^.global_ctrl or $2; //enable interrupts | ||||
|         | ||||
|     //disable interrupts | ||||
|     base^.global_ctrl := base^.global_ctrl or $2; | ||||
|  | ||||
|     //clear any pending interrupts | ||||
|     base^.int_status := $FFFFFFFF; | ||||
|  | ||||
|     check_ports(controller); | ||||
| end; | ||||
|  | ||||
| procedure ahci_isr(); | ||||
| begin | ||||
|     console.writestringln('AHCI: ISR'); | ||||
|     console.redrawWindows(); | ||||
|     //welp there is no way to know what port caused the interrupt or even if it was the controller | ||||
|     //so we will just have to check all ports, and figure the operation that caused the interrupt | ||||
| end; | ||||
|  | ||||
| function find_cmd_slot(device : PAHCI_Device) : uint32; | ||||
| var | ||||
|     i : uint32; | ||||
| begin | ||||
|     // for i:=0 to 31 do begin | ||||
|     //     if (device^.port^.cmd_issue[i] and $80000000) = 0 then begin | ||||
|     //         exit(i); | ||||
|     //     end; | ||||
|     // end; | ||||
|     exit(0); | ||||
| end; | ||||
|  | ||||
| function send_read_dma(device : PAHCI_Device; lba : uint64; count : uint32; buffer : puint32) : boolean; | ||||
| var | ||||
|     fis         : PHBA_FIS_REG_H2D; | ||||
|     cmd_header  : PHBA_CMD_HEADER; | ||||
|     cmd_table   : PHBA_CMD_TABLE; | ||||
|     cmd         : uint32; | ||||
|     i, timeout  : uint32; | ||||
|     tfd         : uint32; | ||||
|     sec_count   : uint32; | ||||
|     prdt_count  : uint32; | ||||
|     b8          : puint16; | ||||
|     slot        : uint32; | ||||
| begin | ||||
|     console.writestringln('AHCI: Sending read DMA command'); | ||||
|  | ||||
|     reset_port(device^.port); | ||||
|  | ||||
|     //prdt count is 22 bits | ||||
|     prdt_count := (count div 4194304) + 1; | ||||
|  | ||||
|     if prdt_count > 32 then begin | ||||
|         send_read_dma := false; | ||||
|         exit; | ||||
|     end; | ||||
|      | ||||
|     // Use command slot 0 for the IDENTIFY command. | ||||
|     slot := find_cmd_slot(device); | ||||
|     cmd_header := PHBA_CMD_HEADER(uint32(device^.command_list) + (slot * sizeof(THBA_CMD_HEADER))); | ||||
|     cmd_header^.cmd_fis_length := sizeof(THBA_FIS_REG_H2D) div sizeof(uint32); | ||||
|     cmd_header^.wrt := 0; | ||||
|     cmd_header^.prdtl := prdt_count; | ||||
|     cmd_header^.clear_busy := 1; | ||||
|  | ||||
|     // Setup the command table (using slot 0) | ||||
|     cmd_table := PHBA_CMD_TABLE(uint32(device^.command_table) + (slot * sizeof(THBA_CMD_TABLE))); | ||||
|  | ||||
|     for i:=0 to prdt_count - 1 do begin | ||||
|         cmd_table^.prdt[i].dba := vtop(uint32(buffer) + (i * 4194304)); | ||||
|  | ||||
|         if i = prdt_count - 1 then begin | ||||
|             cmd_table^.prdt[i].dbc := count - (i * 4194304) - 1;   // 512 bytes (0-based count) | ||||
|         end else begin | ||||
|             cmd_table^.prdt[i].dbc := 4194304 - 1;   // 512 bytes (0-based count) | ||||
|         end; | ||||
|  | ||||
|         cmd_table^.prdt[i].int := 1;     // Interrupt on completion | ||||
|     end; | ||||
|  | ||||
|     // Construct the Command FIS in the command table's CFIS area | ||||
|     fis := PHBA_FIS_REG_H2D(@cmd_table^.cmd_fis); | ||||
|     memset(uint32(fis), 0, sizeof(THBA_FIS_REG_H2D)); | ||||
|     fis^.fis_type := uint8(FIS_TYPE_REG_H2D); | ||||
|     fis^.c   := $1; | ||||
|     fis^.command  := ATA_CMD_READ_DMA_EXT; | ||||
|     fis^.device   := $40;  // LBA mode 48? | ||||
|  | ||||
|     fis^.lba0 := lba and $FF; | ||||
|     fis^.lba1 := (lba shr 8) and $FF; | ||||
|     fis^.lba2 := (lba shr 16) and $FF; | ||||
|     fis^.lba3 := (lba shr 24) and $FF; | ||||
|     fis^.lba4 := (lba shr 32) and $FF; | ||||
|  | ||||
|     fis^.countl := count and $FF; | ||||
|     fis^.counth := (count shr 8) and $FF; | ||||
|  | ||||
|     //waut for the port to be ready, bit 7 in the TFD register and bit 3 tfd | ||||
|     while (device^.port^.tfd and $88) <> 0 do begin | ||||
|         // console.writestring('AHCI: tfd: '); | ||||
|         // console.writehexln(device^.port^.tfd); | ||||
|     end; | ||||
|  | ||||
|     // Issue the command by setting bit 0 in the port's Command Issue register. | ||||
|     cmd := device^.port^.cmd_issue; | ||||
|     cmd := cmd or (1 shl slot); | ||||
|     device^.port^.cmd_issue := cmd; | ||||
|  | ||||
|     console.writestringln('AHCI: Sent read DMA command'); | ||||
|  | ||||
|     // Wait for command completion with a timeout. | ||||
|     timeout := 0; | ||||
|     repeat | ||||
|       cmd := device^.port^.cmd_issue;  // Command Issue register | ||||
|       tfd := device^.port^.tfd;  // Task File Data often contains error/status info. | ||||
|       timeout := timeout + 10; | ||||
|       if timeout > 100000 then begin | ||||
|           console.writestringln('AHCI: Read DMA command timeout'); | ||||
|           break; | ||||
|       end; | ||||
|         // check for bit 30 in the tfd register | ||||
|         if (tfd and $40000000) <> 0 then begin | ||||
|             console.writestringln('AHCI: error'); | ||||
|             break; | ||||
|         end; | ||||
|  | ||||
|     until ((device^.port^.cmd_issue and (1 shl slot)) = 0);  // Wait until slot 0 is cleared | ||||
|  | ||||
|     console.writestringln('AHCI: Command complete'); | ||||
|  | ||||
|     // Check for an error flag in the command register (bit 1) | ||||
|     if (cmd and (1 shl slot)) <> 0 then begin | ||||
|         console.writestringln('AHCI: Error sending read DMA command'); | ||||
|  | ||||
|         // Check the error register for more information | ||||
|         console.writestring('AHCI: Error register: '); | ||||
|         console.writehexln(device^.port^.sata_error); | ||||
|     end; | ||||
|  | ||||
|     send_read_dma := true; | ||||
| end; | ||||
|  | ||||
| function send_write_dma(device : PAHCI_Device; lba : uint64; count : uint32; buffer : puint32) : boolean; | ||||
| var | ||||
|     fis         : PHBA_FIS_REG_H2D; | ||||
|     cmd_header  : PHBA_CMD_HEADER; | ||||
|     cmd_table   : PHBA_CMD_TABLE; | ||||
|     cmd         : uint32; | ||||
|     i, timeout  : uint32; | ||||
|     tfd         : uint32; | ||||
|     sec_count   : uint32; | ||||
|     prdt_count  : uint32; | ||||
|     b8          : puint16; | ||||
|     slot        : uint32; | ||||
| begin | ||||
|     console.writestringln('AHCI: Sending write DMA command'); | ||||
|  | ||||
|     reset_port(device^.port); | ||||
|  | ||||
|     //prdt count is 22 bits | ||||
|     prdt_count := (count div 4194304) + 1; | ||||
|  | ||||
|     if prdt_count > 32 then begin | ||||
|         send_write_dma := false; | ||||
|         exit; | ||||
|     end; | ||||
|      | ||||
|     // Use command slot 0 for the IDENTIFY command. | ||||
|     slot := find_cmd_slot(device); | ||||
|     cmd_header := PHBA_CMD_HEADER(uint32(device^.command_list) + (slot * sizeof(THBA_CMD_HEADER))); | ||||
|     cmd_header^.cmd_fis_length := sizeof(THBA_FIS_REG_H2D) div sizeof(uint32); | ||||
|     cmd_header^.wrt := 1; | ||||
|     cmd_header^.prdtl := prdt_count; | ||||
|     cmd_header^.clear_busy := 1; | ||||
|  | ||||
|     // Setup the command table (using slot 0) | ||||
|     cmd_table := PHBA_CMD_TABLE(uint32(device^.command_table) + (slot * sizeof(THBA_CMD_TABLE))); | ||||
|  | ||||
|     for i:=0 to prdt_count - 1 do begin | ||||
|         cmd_table^.prdt[i].dba := vtop(uint32(buffer) + (i * 4194304)); | ||||
|  | ||||
|         if i = prdt_count - 1 then begin | ||||
|             cmd_table^.prdt[i].dbc := count - (i * 4194304) - 1;   // 512 bytes (0-based count) | ||||
|         end else begin | ||||
|             cmd_table^.prdt[i].dbc := 4194304 - 1;   // 512 bytes (0-based count) | ||||
|         end; | ||||
|  | ||||
|         cmd_table^.prdt[i].int := 1;     // Interrupt on completion | ||||
|     end; | ||||
|  | ||||
|     // Construct the Command FIS in the command table's CFIS area | ||||
|     fis := PHBA_FIS_REG_H2D(@cmd_table^.cmd_fis); | ||||
|     memset(uint32(fis), 0, sizeof(THBA_FIS_REG_H2D)); | ||||
|     fis^.fis_type := uint8(FIS_TYPE_REG_H2D); | ||||
|     fis^.c   := $1; | ||||
|     fis^.command  := ATA_CMD_WRITE_DMA_EXT; | ||||
|     fis^.device   := $40;  // LBA mode 48? | ||||
|  | ||||
|     count:= count div 512; | ||||
|  | ||||
|     fis^.lba0 := lba and $FF; | ||||
|     fis^.lba1 := ( | ||||
|         lba shr 8) and $FF; | ||||
|     fis^.lba2 := ( | ||||
|         lba shr 16) and $FF; | ||||
|     fis^.lba3 := ( | ||||
|         lba shr 24) and $FF; | ||||
|     fis^.lba4 := ( | ||||
|         lba shr 32) and $FF; | ||||
|  | ||||
|     fis^.countl := count and $FF; | ||||
|     fis^.counth := ( | ||||
|         count shr 8) and $FF;    | ||||
|  | ||||
|     //waut for the port to be ready, bit 7 in the TFD register and bit 3 tfd | ||||
|     while (device^.port^.tfd and $88) <> 0 do begin | ||||
|         // console.writestring('AHCI: tfd: '); | ||||
|         // console.writehexln(device^.port^.tfd); | ||||
|     end; | ||||
|  | ||||
|     // Issue the command by setting bit 0 in the port's Command Issue register. | ||||
|     cmd := device^.port^.cmd_issue; | ||||
|     cmd := cmd or (1 shl slot); | ||||
|     device^.port^.cmd_issue := cmd; | ||||
|  | ||||
|     console.writestringln('AHCI: Sent write DMA command'); | ||||
|  | ||||
|     // Wait for command completion with a timeout. | ||||
|     timeout := 0;    | ||||
|     repeat | ||||
|       cmd := device^.port^.cmd_issue;  // Command Issue register | ||||
|       tfd := device^.port^.tfd;  // Task File Data often contains error/status info. | ||||
|       timeout := timeout + 10; | ||||
|       if timeout > 100000 then begin | ||||
|           console.writestringln('AHCI: Write DMA command timeout'); | ||||
|           break; | ||||
|       end; | ||||
|         // check for bit 30 in the tfd register | ||||
|         if (tfd and $40000000) <> 0 then begin | ||||
|             console.writestringln('AHCI: error'); | ||||
|             break; | ||||
|         end; | ||||
|  | ||||
|     until ((device^.port^.cmd_issue and (1 shl slot)) = 0);  // Wait until slot 0 is cleared | ||||
|  | ||||
|     console.writestringln('AHCI: Command complete'); | ||||
|  | ||||
|     // Check for an error flag in the command register (bit 1) | ||||
|     if (cmd and (1 shl slot)) <> 0 then begin | ||||
|         console.writestringln('AHCI: Error sending write DMA command'); | ||||
|  | ||||
|         // Check the error register for more information | ||||
|         console.writestring('AHCI: Error register: '); | ||||
|         console.writehexln(device^.port^.sata_error); | ||||
|     end; | ||||
|  | ||||
|     send_write_dma := true; | ||||
| end; | ||||
|  | ||||
| //read atapi  | ||||
| function read_atapi(device : PAHCI_Device; lba : uint64; count : uint32; buffer : puint32) : boolean; | ||||
| var | ||||
|     fis         : PHBA_FIS_REG_H2D; | ||||
|     cmd_header  : PHBA_CMD_HEADER; | ||||
|     cmd_table   : PHBA_CMD_TABLE; | ||||
|     cmd         : uint32; | ||||
|     i, timeout  : uint32; | ||||
|     tfd         : uint32; | ||||
|     sec_count   : uint32; | ||||
|     prdt_count  : uint32; | ||||
|     b8          : puint16; | ||||
|     slot        : uint32; | ||||
| begin | ||||
|     console.writestringln('AHCI: Sending read ATAPI command'); | ||||
|  | ||||
|     reset_port(device^.port); | ||||
|  | ||||
|     //prdt count is 22 bits | ||||
|     prdt_count := (count div 4194304) + 1; | ||||
|  | ||||
|     if prdt_count > 32 then begin | ||||
|         read_atapi := false; | ||||
|         exit; | ||||
|     end; | ||||
|      | ||||
|     // Use command slot 0 for the IDENTIFY command. | ||||
|     slot := find_cmd_slot(device); | ||||
|     cmd_header := PHBA_CMD_HEADER(uint32(device^.command_list) + (slot * sizeof(THBA_CMD_HEADER))); | ||||
|     cmd_header^.cmd_fis_length := sizeof(THBA_FIS_REG_H2D) div sizeof(uint32); | ||||
|     cmd_header^.wrt := 0; | ||||
|     cmd_header^.prdtl := 1; | ||||
|     cmd_header^.atapi := 1; | ||||
|     cmd_header^.clear_busy := 1; | ||||
|  | ||||
|     // Setup the command table (using slot 0) | ||||
|     cmd_table := PHBA_CMD_TABLE(uint32(device^.command_table) + (slot * sizeof(THBA_CMD_TABLE))); | ||||
|  | ||||
|     cmd_table^.prdt[0].dba := vtop(uint32(buffer)); | ||||
|     cmd_table^.prdt[0].dbc := 2048 - 1;   // 512 bytes (0-based count | ||||
|     cmd_table^.prdt[0].int := 1;     // Interrupt on completion | ||||
|  | ||||
|     // Construct the Command FIS in the command table's CFIS area | ||||
|     fis := PHBA_FIS_REG_H2D(@cmd_table^.cmd_fis); | ||||
|     memset(uint32(fis), 0, sizeof(THBA_FIS_REG_H2D)); | ||||
|     fis^.fis_type := uint8(FIS_TYPE_REG_H2D); | ||||
|     fis^.c   := $1; | ||||
|     fis^.command  := ATA_CMD_PACKET; | ||||
|     fis^.device   := $A0;  | ||||
|  | ||||
|     fis^.featurel := fis^.featurel or 1; //setting both of these might not work on real hardware | ||||
|     fis^.featurel := fis^.featurel or (1 shl 2); | ||||
|  | ||||
|     cmd_table^.acmd[0] := $A8; //ATAPI command | ||||
|     cmd_table^.acmd[1] := $0; //ATAPI command | ||||
|  | ||||
|     cmd_table^.acmd[2] := uInt8((lba shr 24) and $FF); | ||||
|     cmd_table^.acmd[3] := uInt8((lba shr 16) and $FF); | ||||
|     cmd_table^.acmd[4] := uInt8((lba shr 8) and $FF); | ||||
|     cmd_table^.acmd[5] := uInt8(lba and $FF); | ||||
|  | ||||
|     cmd_table^.acmd[9] := uInt8((1) and $FF); | ||||
|  | ||||
|  | ||||
|     //waut for the port to be ready, bit 7 in the TFD register and bit 3 tfd | ||||
|     while (device^.port^.tfd and $88) <> 0 do begin | ||||
|         // console.writestring('AHCI: tfd: '); | ||||
|         // console.writehexln(device^.port^.tfd); | ||||
|     end; | ||||
|  | ||||
|     // Issue the command by setting bit 0 in the port's Command Issue register. | ||||
|     cmd := device^.port^.cmd_issue; | ||||
|     cmd := cmd or (1 shl slot); | ||||
|     device^.port^.cmd_issue := cmd; | ||||
|  | ||||
|     console.writestringln('AHCI: Sent read ATAPI command'); | ||||
|  | ||||
|     // Wait for command completion with a timeout. | ||||
|     timeout := 0; | ||||
|     repeat | ||||
|       cmd := device^.port^.cmd_issue;  // Command Issue register | ||||
|       tfd := device^.port^.tfd;  // Task File Data often contains error/status info. | ||||
|       timeout := timeout + 10; | ||||
|       if timeout > 100000 then begin | ||||
|           console.writestringln('AHCI: Read ATAPI command timeout'); | ||||
|           break; | ||||
|       end; | ||||
|         // check for bit 30 in the tfd register | ||||
|         if (tfd and $40000000) <> 0 then begin | ||||
|             console.writestringln('AHCI: error'); | ||||
|             break; | ||||
|         end; | ||||
|  | ||||
|     until ((device^.port^.cmd_issue and (1 shl slot)) = 0);  // Wait until slot 0 is cleared | ||||
|  | ||||
|     console.writestringln('AHCI: Command complete'); | ||||
|  | ||||
|     // Check for an error flag in the command register (bit 1) | ||||
|     if (cmd and (1 shl slot)) <> 0 then begin | ||||
|         console.writestringln('AHCI: Error sending read ATAPI command'); | ||||
|  | ||||
|         // Check the error register for more information | ||||
|         console.writestring('AHCI: Error register: '); | ||||
|         console.writehexln(device^.port^.sata_error); | ||||
|     end; | ||||
|  | ||||
|     read_atapi := true; | ||||
|  | ||||
| end; | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| end. | ||||
| @@ -1,320 +0,0 @@ | ||||
| //  Copyright 2021 Aaron Hance | ||||
| // | ||||
| //  Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| //  you may not use this file except in compliance with the License. | ||||
| //  You may obtain a copy of the License at | ||||
| // | ||||
| //  http://www.apache.org/licenses/LICENSE-2.0 | ||||
| // | ||||
| //  Unless required by applicable law or agreed to in writing, software | ||||
| //  distributed under the License is distributed on an "AS IS" BASIS, | ||||
| //  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| //  See the License for the specific language governing permissions and | ||||
| //  limitations under the License. | ||||
|  | ||||
| {  | ||||
| 	Drivers->Storage->AHCI->AHCITypes - AHCI Driver Types. | ||||
| 	 | ||||
| 	@author(Aaron Hance <ah@aaronhance.me>) | ||||
| } | ||||
| unit AHCITypes; | ||||
| interface | ||||
| uses  | ||||
|     util, | ||||
|     PCI, | ||||
|     drivertypes, | ||||
|     drivermanagement, | ||||
|     lmemorymanager, | ||||
|     console, | ||||
|     vmemorymanager, | ||||
|     idetypes, | ||||
|     lists; | ||||
|  | ||||
| const | ||||
|     AHCI_CONTROLLER_MODE = $80000000; | ||||
|  | ||||
|     CMD_LIST_ALIGN = 1024; | ||||
|     CMD_TBL_ALIGN = 128; | ||||
|  | ||||
|     NUM_CMD_ENTRIES = 32; | ||||
|  | ||||
|     //device type signatures | ||||
|     SATA_SIG_ATA = $00000101; | ||||
|     SATA_SIG_ATAPI = $EB140101; | ||||
|     SATA_SIG_SEMB = $C33C0101; | ||||
|     SATA_SIG_PM = $96690101; | ||||
|  | ||||
|  | ||||
|  | ||||
| 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_status: 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; | ||||
|         vendor: array[0..3] of uint32; | ||||
|     end; | ||||
|  | ||||
|     PHBA_Port = ^THBA_Port; | ||||
|  | ||||
|     {  | ||||
|         AHCI Host Bus Adapter (HBA) Memory-Mapped Register Interface | ||||
|         This structure is used to access the AHCI's memory-mapped registers. | ||||
|         The AHCI'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_implimented: 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 | ||||
|  | ||||
|         //0x2c to 0x9f reserved | ||||
|         rsv0: array[0..115] of uint8; | ||||
|  | ||||
|         //vendor specific | ||||
|         vendor: array[0..95] of uint8; | ||||
|  | ||||
|         //port registers | ||||
|         ports: array[0..31] of THBA_Port; | ||||
|        | ||||
|     end; | ||||
|  | ||||
|     PHBA_Memory = ^THBA_Memory; | ||||
|  | ||||
|     {  | ||||
|         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. RX | ||||
|     } | ||||
|     THBA_FIS = bitpacked record  | ||||
|         dsfis: array[0..$1F] of uint32; // DMA Setup FIS | ||||
|         rsv0: array[0..$1F] of uint32; | ||||
|         psfis: array[0..$1F] of uint32; // PIO Setup FIS | ||||
|         rsv1: array[0..$1F] of uint32; | ||||
|         rfis: array[0..$1F] of uint32;  // D2H Register FIS | ||||
|         rsv2: array[0..$1F] of uint32; | ||||
|         sdbfis: array[0..$F] of uint32; // Set Device Bits FIS | ||||
|         ufis: array[0..$1F] of uint32;  // Unknown FIS | ||||
|         rsv3: array[0..$67] of uint32; | ||||
|     end; | ||||
|  | ||||
|     PHBA_FIS = ^THBA_FIS; | ||||
|  | ||||
|     //enum fis type | ||||
|     TFISType = ( | ||||
|         FIS_TYPE_REG_H2D = $27, // Register FIS - Host to Device | ||||
|         FIS_TYPE_REG_D2H = $34, // Register FIS - Device to Host | ||||
|         FIS_TYPE_DMA_ACT = $39, // DMA Activate FIS - Device to Host | ||||
|         FIS_TYPE_DMA_SETUP = $41, // DMA Setup FIS - Bidirectional | ||||
|         FIS_TYPE_DATA = $46, // Data FIS - Bidirectional | ||||
|         FIS_TYPE_BIST = $58, // BIST Activate FIS - Bidirectional | ||||
|         FIS_TYPE_PIO_SETUP = $5F, // PIO Setup FIS - Device to Host | ||||
|         FIS_TYPE_DEV_BITS = $A1 // Set Device Bits FIS - Device to Host | ||||
|     ); | ||||
|  | ||||
|     {  | ||||
|         AHCI Host Bus Adapter (HBA) FIS (Frame Information Structure) Interface | ||||
|         This structure is used to access the AHCI HBA's FIS (Frame Information Structure) | ||||
|     } | ||||
|     THBA_FIS_REG_H2D = bitpacked record  | ||||
|         fis_type: uint8;         // FIS Type | ||||
|         // pmport: uint8;           // Port Multiplier Port is pmport:4 and rsv0:3 and i:1 | ||||
|         pmport: ubit4;           // Port Multiplier Port | ||||
|         rsv: ubit3;             // Reserved | ||||
|         c: ubit1;                // command or control | ||||
|         command: uint8;          // Command | ||||
|         featurel: uint8;         // Feature Lower 8-bits | ||||
|         lba0: uint8;             // LBA0 | ||||
|         lba1: uint8;             // LBA1 | ||||
|         lba2: uint8;             // LBA2 | ||||
|         device: uint8;           // Device | ||||
|         lba3: uint8;             // LBA3 | ||||
|         lba4: uint8;             // LBA4 | ||||
|         lba5: uint8;             // LBA5 | ||||
|         featureh: uint8;         // Feature Upper 8-bits | ||||
|         countl: uint8;           // Count Lower 8-bits | ||||
|         counth: uint8;           // Count Upper 8-bits | ||||
|         icc: uint8;              // Isochronous Command Completion | ||||
|         control: uint8;          // Control | ||||
|         rsv0: array[0..3] of uint8; | ||||
|     end; | ||||
|  | ||||
|     PHBA_FIS_REG_H2D = ^THBA_FIS_REG_H2D; | ||||
|  | ||||
|  | ||||
|  | ||||
|     {  | ||||
|         AHCI Host Bus Adapter (HBA) Command Header Interface | ||||
|     } | ||||
|     THBA_CMD_HEADER = bitpacked record  | ||||
|         cmd_fis_length: UBit5;   // Command FIS Length | ||||
|         atapi: UBit1;            // ATAPI | ||||
|         wrt: UBit1;            // Write | ||||
|         prefetchable: UBit1;     // Prefetchable | ||||
|  | ||||
|         reset: UBit1;            // Reset | ||||
|         bist: UBit1;             // BIST | ||||
|         clear_busy: UBit1;       // Clear Busy | ||||
|         reserved0: UBit1;        // Reserved | ||||
|         port_multiplier: UBit4;  // Port Multiplier Port | ||||
|  | ||||
|         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..3] of uint32; | ||||
|     end; | ||||
|  | ||||
|     PHBA_CMD_HEADER = ^THBA_CMD_HEADER; | ||||
|  | ||||
|     {  | ||||
|         AHCI Host Bus Adapter (HBA) Command Table Interface | ||||
|     } | ||||
|     THBA_PRD = bitpacked record  | ||||
|         dba: uint32;             // Data Base Address | ||||
|         dbau: uint32;            // Data Base Address Upper 32-bits | ||||
|         rsv0: uint32;            // Reserved | ||||
|         dbc: ubit22;             // Data Byte Count | ||||
|         reserved: ubit9;         // Reserved | ||||
|         int: ubit1;        // Interrupt | ||||
|         // dbc: uint32;             // Data Byte Count, bit 1 is Interrupt, then 22 bits of Byte Count, then 9 bits of Reserved | ||||
|     end; | ||||
|  | ||||
|     TPRDT = array[0..31] of THBA_PRD; | ||||
|     PPRDT = ^TPRDT; | ||||
|  | ||||
|     {  | ||||
|         AHCI Host Bus Adapter (HBA) Command Table Interface | ||||
|     } | ||||
|     THBA_CMD_TABLE = bitpacked record  | ||||
|         cmd_fis: array[0..63] of uint8; // Command FIS | ||||
|         acmd: array[0..15] of uint8;    // ATAPI Command | ||||
|         rsv0: array[0..47] of uint8; | ||||
|         prdt: array[0..31] of THBA_PRD;                    // Physical Region Descriptor Table | ||||
|     end; | ||||
|  | ||||
|     PHBA_CMD_TABLE = ^THBA_CMD_TABLE; | ||||
|  | ||||
|     TCMD_LIST = array[0..255] of THBA_CMD_HEADER; | ||||
|     PCMD_LIST = ^TCMD_LIST; | ||||
|  | ||||
|     ////////////////////////////////////////// | ||||
|     //////////// Asuro AHCI types //////////// | ||||
|     ////////////////////////////////////////// | ||||
|  | ||||
|     { | ||||
|         Device type enum | ||||
|     } | ||||
|     TDeviceType = ( | ||||
|         SATA = 1, | ||||
|         ATAPI = 2, | ||||
|         SEMB, | ||||
|         PM | ||||
|     ); | ||||
|  | ||||
|     { | ||||
|         AHCI device reference | ||||
|     } | ||||
|     TAHCI_Device = bitpacked record | ||||
|         port : PHBA_Port; | ||||
|         device_type : TDeviceType; | ||||
|         ata_info : TIdentResponse; | ||||
|         command_list : PHBA_CMD_HEADER; | ||||
|         fis : PHBA_FIS; | ||||
|         command_table : PHBA_CMD_TABLE; | ||||
|     end; | ||||
|  | ||||
|     PAHCI_Device = ^TAHCI_Device; | ||||
|  | ||||
|     { | ||||
|         controller reference | ||||
|     } | ||||
|     TAHCI_Controller = bitpacked record | ||||
|         pci_device : PPCI_Device; | ||||
|         mio: PHBA_Memory; | ||||
|         ata_info : TIdentResponse; | ||||
|         devices : array[0..31] of TAHCI_Device; | ||||
|     end; | ||||
|  | ||||
|     PAHCI_Controller = ^TAHCI_Controller; | ||||
|  | ||||
|       | ||||
|  function get_device_type(sig : uint32) : TDeviceType; | ||||
| //  function get_device_type_string(deive_type : TDeviceType) : string; | ||||
|  | ||||
| implementation | ||||
|  | ||||
| function get_device_type(sig : uint32) : TDeviceType; | ||||
| begin | ||||
|     case sig of | ||||
|         SATA_SIG_ATA: begin | ||||
|             get_device_type := SATA; | ||||
|         end; | ||||
|         SATA_SIG_ATAPI: begin | ||||
|             get_device_type := ATAPI; | ||||
|         end; | ||||
|         SATA_SIG_SEMB: begin | ||||
|             get_device_type := SEMB; | ||||
|         end; | ||||
|         SATA_SIG_PM: begin | ||||
|             get_device_type := PM; | ||||
|         end; | ||||
|     end; | ||||
| end; | ||||
|  | ||||
| // function get_device_type_string(deive_type : TDeviceType) : string; | ||||
| // begin | ||||
| //     case deive_type of | ||||
| //         SATA: begin | ||||
| //             get_device_type_string := 'SATA'; | ||||
| //         end; | ||||
| //         ATAPI: begin | ||||
| //             get_device_type_string := 'ATAPI'; | ||||
| //         end; | ||||
| //         SEMB: begin | ||||
| //             get_device_type_string := 'SEMB'; | ||||
| //         end; | ||||
| //         PM: begin | ||||
| //             get_device_type_string := 'PM'; | ||||
| //         end; | ||||
| //     end; | ||||
| // end; | ||||
|  | ||||
| end. | ||||
|  | ||||
							
								
								
									
										85
									
								
								src/driver/storage/ATA_ISR.pas
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								src/driver/storage/ATA_ISR.pas
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,85 @@ | ||||
| //  Copyright 2021 Aaron Hance | ||||
| // | ||||
| //  Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| //  you may not use this file except in compliance with the License. | ||||
| //  You may obtain a copy of the License at | ||||
| // | ||||
| //  http://www.apache.org/licenses/LICENSE-2.0 | ||||
| // | ||||
| //  Unless required by applicable law or agreed to in writing, software | ||||
| //  distributed under the License is distributed on an "AS IS" BASIS, | ||||
| //  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| //  See the License for the specific language governing permissions and | ||||
| //  limitations under the License. | ||||
|  | ||||
| {  | ||||
| 	Drivers->Storage->ATA_ISR - Primary ATA IRQ. | ||||
| 	 | ||||
| 	@author(Aaron Hance <ah@aaronhance.me>) | ||||
| } | ||||
| unit ATA_ISR; | ||||
|  | ||||
| interface | ||||
|  | ||||
| uses | ||||
|     util, | ||||
|     console, | ||||
|     isr_types, | ||||
|     isrmanager, | ||||
|     IDT; | ||||
|  | ||||
| procedure register(); | ||||
| procedure hook(hook_method : uint32); | ||||
| procedure unhook(hook_method : uint32); | ||||
|  | ||||
| implementation | ||||
|  | ||||
| var | ||||
|     Hooks : Array[1..MAX_HOOKS] of pp_hook_method; | ||||
|  | ||||
| procedure Main(); interrupt; | ||||
| var | ||||
|     i : integer; | ||||
|      | ||||
| begin | ||||
|     CLI; | ||||
|     for i:=0 to MAX_HOOKS-1 do begin | ||||
|         if uint32(Hooks[i]) <> 0 then Hooks[i](void($00000000)); | ||||
|     end; | ||||
|     console.writestringln('Disk Operation Complete'); | ||||
| end; | ||||
|  | ||||
| procedure register(); | ||||
| begin | ||||
|     memset(uint32(@Hooks[0]), 0, sizeof(pp_hook_method)*MAX_HOOKS); | ||||
|     //isrmanager.registerISR(76, @Main); | ||||
|     //IDT.set_gate(76, uint32(@Main), $08, ISR_RING_0); | ||||
| end; | ||||
|  | ||||
| procedure hook(hook_method : uint32); | ||||
| var | ||||
|     i : uint32; | ||||
|  | ||||
| begin | ||||
|     for i:=0 to MAX_HOOKS-1 do begin | ||||
|         if uint32(Hooks[i]) = hook_method then exit; | ||||
|     end; | ||||
|     for i:=0 to MAX_HOOKS-1 do begin | ||||
|         if uint32(Hooks[i]) = 0 then begin | ||||
|             Hooks[i]:= pp_hook_method(hook_method); | ||||
|             exit; | ||||
|         end; | ||||
|     end; | ||||
| end; | ||||
|  | ||||
| procedure unhook(hook_method : uint32); | ||||
| var | ||||
|     i : uint32; | ||||
| begin | ||||
|     for i:=0 to MAX_HOOKS-1 do begin | ||||
|         If uint32(Hooks[i]) = hook_method then Hooks[i]:= nil; | ||||
|         exit; | ||||
|     end; | ||||
| end; | ||||
|  | ||||
| end. | ||||
| @@ -1,506 +1,246 @@ | ||||
| //  Copyright 2021 Aaron Hance | ||||
| // | ||||
| //  Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| //  you may not use this file except in compliance with the License. | ||||
| //  You may obtain a copy of the License at | ||||
| // | ||||
| //  http://www.apache.org/licenses/LICENSE-2.0 | ||||
| // | ||||
| //  Unless required by applicable law or agreed to in writing, software | ||||
| //  distributed under the License is distributed on an "AS IS" BASIS, | ||||
| //  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| //  See the License for the specific language governing permissions and | ||||
| //  limitations under the License. | ||||
|  | ||||
| {  | ||||
| 	Drivers->Storage->ide - IDE Driver. | ||||
|  | ||||
|     NOT FINSIHED NEEDS WORK | ||||
| 	 | ||||
| 	@author(Aaron Hance <ah@aaronhance.me>) | ||||
| } | ||||
| unit ide; | ||||
| { ************************************************ | ||||
|   * Asuro | ||||
|   * Unit: Drivers/storage/IDE | ||||
|   * Description: IDE ATA Driver | ||||
|   *  | ||||
|   ************************************************ | ||||
|   * Author: Aaron Hance | ||||
|   * Contributors:  | ||||
|   ************************************************ } | ||||
| unit IDE; | ||||
|  | ||||
| interface | ||||
|  | ||||
| uses | ||||
|     ata, | ||||
|     atapi, | ||||
|     console, | ||||
|     drivermanagement, | ||||
|     drivertypes, | ||||
|     idetypes, | ||||
|     isrmanager, | ||||
|     lmemorymanager, | ||||
|     storagetypes, | ||||
|     strings, | ||||
|     terminal, | ||||
|     tracer, | ||||
|     util, | ||||
|     vmemorymanager; | ||||
|     drivertypes, | ||||
|     console, | ||||
|     terminal, | ||||
|     drivermanagement, | ||||
|     vmemorymanager, | ||||
|     lmemorymanager, | ||||
|     storagemanagement, | ||||
|     strings, | ||||
|     tracer; | ||||
|  | ||||
| type  | ||||
|     TPortMode = (P_READ, P_WRITE); | ||||
|  | ||||
|     TIdentResponse = array[0..255] of uint16; | ||||
|  | ||||
|     TIDE_Channel_Registers = record | ||||
|         base    : uint16; | ||||
|         ctrl    : uint16; | ||||
|         bmide   : uint16; | ||||
|         noInter : uint8 | ||||
|     end; | ||||
|  | ||||
|     TIDE_Device = record | ||||
|         exists       : boolean; | ||||
|         isPrimary    : boolean; | ||||
|         isMaster     : boolean; | ||||
|         isATAPI      : boolean; | ||||
|         info         : TIdentResponse; | ||||
|     end; | ||||
|  | ||||
|     TIDE_Status = bitpacked record  | ||||
|         Busy  : Boolean; | ||||
|         Ready : Boolean; | ||||
|         Fault : Boolean; | ||||
|         Seek  : Boolean; | ||||
|         DRQ   : Boolean; | ||||
|         CORR  : Boolean; | ||||
|         IDDEX : Boolean; | ||||
|         ERROR : Boolean; | ||||
|     end; | ||||
|     PIDE_Status = ^TIDE_Status; | ||||
|  | ||||
|  | ||||
| const | ||||
|     ATA_SR_BUSY = $80; //BUSY | ||||
|     ATA_SR_DRDY = $40; //DRIVE READY | ||||
|     ATA_SR_DF   = $20; //DRIVE WRITE FAULT | ||||
|     ATA_SR_DSC  = $10; //DRIVE SEEK COMPLETE | ||||
|     ATA_SR_DRQ  = $08; //DATA REQUEST READY | ||||
|     ATA_SR_CORR = $04; //CORRECTED DATA | ||||
|     ATA_SR_IDX  = $02; //INLEX | ||||
|     ATA_SR_ERR  = $01; //ERROR | ||||
|  | ||||
|     ATA_ER_BBK   = $80; //BAD SECTOR | ||||
|     ATA_ER_UNC   = $40; //UNCORRECTABLE DATA | ||||
|     ATA_ER_MC    = $20; //NO MEDIA | ||||
|     ATA_ER_IDNF  = $10; //ID MARK NOT FOUND | ||||
|     ATA_ER_MCR   = $08; //NO MEDIA | ||||
|     ATA_ER_ABRT  = $04; //COMMAND ABORTED | ||||
|     ATA_ER_TK0NF = $02; //TRACK 0 NOT FOUND | ||||
|     ATA_ER_AMNF  = $01; //NO ADDRESS MARK | ||||
|  | ||||
|     ATA_CMD_READ_PIO        = $20;  | ||||
|     ATA_CMD_READ_PIO_EXT    = $24;  | ||||
|     ATA_CMD_READ_DMA        = $C8;  | ||||
|     ATA_CMD_READ_DMA_EXT    = $25;  | ||||
|     ATA_CMD_WRITE_PIO       = $30;  | ||||
|     ATA_CMD_WRITE_PIO_EXT   = $34;  | ||||
|     ATA_CMD_WRITE_DMA       = $CA;  | ||||
|     ATA_CMD_WRITE_DMA_EXT   = $35;  | ||||
|     ATA_CMD_CACHE_FLUSH     = $E7;  | ||||
|     ATA_CMD_CACHE_FLUSH_EXT = $EA;  | ||||
|     ATA_CMD_PACKET          = $A0;  | ||||
|     ATA_CMD_IDENTIFY_PACKET = $A1;  | ||||
|     ATA_CMD_IDENTIFY        = $EC;  | ||||
|  | ||||
|     ATAPI_CMD_READ  = $A8; | ||||
|     ATAPI_CMD_EJECT = $1B; | ||||
|  | ||||
|     ATA_IDENT_DEVICETYPE   = $0;     | ||||
|     ATA_IDENT_CYLINDERS    = $2;     | ||||
|     ATA_IDENT_HEADS        = $6;     | ||||
|     ATA_IDENT_SECOTRS      = $12;     | ||||
|     ATA_IDENT_SERIAL       = $20;     | ||||
|     ATA_IDENT_MODEL        = $54;     | ||||
|     ATA_IDENT_CAPABILITIES = $98;     | ||||
|     ATA_IDENT_FIELDVALID   = $106;    | ||||
|     ATA_IDENT_MAX_LBA      = $120;    | ||||
|     ATA_IDENT_COMMANDSETS  = $164;        | ||||
|     ATA_IDENT_MAX_LBA_EXT  = $200; | ||||
|  | ||||
|     ATA_REG_DATA       = $00; | ||||
|     ATA_REG_ERROR      = $01; | ||||
|     ATA_REG_FEATURES   = $01; | ||||
|     ATA_REG_SECCOUNT  = $02; | ||||
|     ATA_REG_LBA0       = $03; | ||||
|     ATA_REG_LBA1       = $04; | ||||
|     ATA_REG_LBA2       = $05; | ||||
|     ATA_REG_HDDEVSEL   = $06; | ||||
|     ATA_REG_COMMAND    = $07; | ||||
|     ATA_REG_STATUS     = $07; | ||||
|     ATA_REG_SECCOUNT1  = $08; | ||||
|     ATA_REG_LBA3       = $09; | ||||
|     ATA_REG_LBA4       = $0A; | ||||
|     ATA_REG_LBA5       = $0B; | ||||
|     ATA_REG_CONTROL    = $0C; | ||||
|     ATA_REG_ALTSTATUS  = $0C; | ||||
|     ATA_REG_DEVADDRESS = $0D;  | ||||
|  | ||||
|     ATA_DEVICE_MASTER = $A0; | ||||
|     ATA_DEVICE_SLAVE = $B0; | ||||
|  | ||||
|     ATA_PRIMARY_BASE = $1F0; | ||||
|  | ||||
| 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; blockSize: 0; info: nil), | ||||
|     controller : PPCI_Device; | ||||
|  | ||||
|         (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; blockSize: 0; info: nil) | ||||
|     ); | ||||
|     bar0 : uint32; | ||||
|     bar1 : uint32; | ||||
|     bar4 : uint32; | ||||
|  | ||||
|     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; blockSize: 0; info: nil), | ||||
|     IDEDevices : array[0..3] of TIDE_Device; | ||||
|  | ||||
|         (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; blockSize: 0; info: nil) | ||||
|     ); | ||||
|     buffer : Puint32; | ||||
|  | ||||
| procedure init(); | ||||
| function load(ptr : void) : boolean; | ||||
| function identify_device(bus : uint8; device : uint8) : TIdentResponse; | ||||
|  | ||||
|     function load(ptr: void) : boolean; | ||||
|     procedure init(); | ||||
|     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); | ||||
| // procedure flush(); | ||||
| procedure readPIO28(drive : uint8; LBA : uint32; buffer : puint8); | ||||
| procedure writePIO28(drive : uint8; LBA : uint32; buffer : puint8); | ||||
| //read/write must be capable of reading/writting any amknt of data upto disk size | ||||
|  | ||||
| procedure dread(device : PStorage_device; LBA : uint32; sectorCount : uint32; buffer : puint32); | ||||
| procedure dwrite(device : PStorage_device; LBA : uint32; sectorCount : uint32; buffer : puint32); | ||||
|  | ||||
| implementation  | ||||
|  | ||||
| { | ||||
|     Disable interrupts on the IDE bus | ||||
|     @param(isPrimary The bus to disable interrupts on) | ||||
| } | ||||
| procedure no_interrupt(isPrimary : boolean); | ||||
| function port_read(register : uint8) : uint8; | ||||
| begin | ||||
|     if isPrimary then begin | ||||
|         outb(ATA_INTERRUPT_PRIMARY, inb(ATA_INTERRUPT_PRIMARY) or (1 shl 1)); | ||||
|     end else begin | ||||
|         outb(ATA_INTERRUPT_SECONDARY, inb(ATA_INTERRUPT_SECONDARY) or (1 shl 1)); | ||||
|     port_read:= inb(ATA_PRIMARY_BASE + register); | ||||
| end; | ||||
|  | ||||
| procedure port_write(register : uint8; data : uint8); | ||||
| var | ||||
|     i : uint8; | ||||
| begin | ||||
|     outb(ATA_PRIMARY_BASE + register, data); | ||||
|     util.psleep(1); | ||||
|     if register = ATA_REG_COMMAND then begin | ||||
|         for i:= 0 to 5 do begin  | ||||
|             port_read(ATA_REG_STATUS); | ||||
|         end; | ||||
|     end; | ||||
| end; | ||||
|  | ||||
| procedure enable_interrupt(isPrimary : boolean); | ||||
| var | ||||
|     reg : uint8; | ||||
| procedure no_interrupt(device : 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; | ||||
|     outb($3F6, inb($3f6) or (1 shl 1)); | ||||
| end; | ||||
|  | ||||
| //soft reset | ||||
| procedure reset_device(device : TIDE_Device); | ||||
| var | ||||
|     reg : uint8; | ||||
| procedure device_select(device : 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; | ||||
|  | ||||
|      | ||||
|     outb($1F6, device); //TODO clean | ||||
| end; | ||||
|  | ||||
| {  | ||||
|     Wait for the device to be ready on the IDE bus | ||||
|  | ||||
|     @param(device The device to wait for) | ||||
|     @returns(@True if the device is ready, @False otherwise) | ||||
| } | ||||
| function wait_for_device(device : TIDE_Device; ioop : boolean) : boolean; | ||||
| function is_ready() : boolean; | ||||
| var  | ||||
|     status : TIDE_Status; | ||||
|     status : uint8; | ||||
|     i : uint32; | ||||
| begin | ||||
|     push_trace('ide.wait_for_device()'); | ||||
|  | ||||
|     i := 0; | ||||
|  | ||||
|     while (i < 50000) do begin | ||||
|         status := get_status(device); | ||||
|  | ||||
|         if (status.BUSY = false) and (status.DRQ or (not ioop)) then begin //todo test | ||||
|             wait_for_device := true; | ||||
|             pop_trace(); | ||||
|             exit; | ||||
|         end; | ||||
|  | ||||
|         i := i + 1; | ||||
|     end; | ||||
|  | ||||
|     wait_for_device := false; | ||||
|     pop_trace(); | ||||
| end; | ||||
|  | ||||
| {  | ||||
|     Select the active device on the IDE bus  | ||||
|     @param(device The device to select) | ||||
|  | ||||
|     Function can set the device to either master or slave, | ||||
|     one device per bus/channel can be active at a time. | ||||
| } | ||||
| procedure select_device(device : TIDE_Device); | ||||
| var | ||||
|     dev : uint8; | ||||
|     reg : uint8; | ||||
| begin | ||||
|     push_trace('ide.select_device()'); | ||||
|  | ||||
|     dev := ATA_DEVICE_SLAVE; | ||||
|  | ||||
|     if device.isMaster then begin | ||||
|         dev := ATA_DEVICE_MASTER; | ||||
|     end; | ||||
|  | ||||
|     reg := inb(device.base + ATA_REG_HDDEVSEL); | ||||
|     reg := (reg and $F0) or dev; | ||||
|  | ||||
|     outb(device.base + ATA_REG_HDDEVSEL, reg); | ||||
|  | ||||
|     pop_trace(); | ||||
| end; | ||||
|  | ||||
| {  | ||||
|     Get the status of the device on the IDE bus | ||||
|  | ||||
|     @param(device The device to get the status of) | ||||
|     @returns(@TIDE_Device The status of the device on the IDE bus) | ||||
| } | ||||
| function get_status(var device : TIDE_Device) : TIDE_Status; | ||||
| var | ||||
|     status : TIDE_Status; | ||||
|     errorReg : uint8; | ||||
| begin | ||||
|     push_trace('ide.get_status()'); | ||||
|  | ||||
|     // select_device(device); | ||||
|  | ||||
|     status := TIDE_Status(inb(device.base + ATA_REG_STATUS)); | ||||
|  | ||||
|     device.status := status; | ||||
|  | ||||
|     if status.ERROR then begin | ||||
|         errorReg := inb(device.base + ATA_REG_ERROR); | ||||
|          | ||||
|         console.writestringln('[IDE] ERROR detected!'); | ||||
|         console.writestring('[IDE] ERROR REGISTER: '); | ||||
|         console.writebin8ln(errorReg); | ||||
|  | ||||
|         if (errorReg and $04) <> 0 then console.writestringln('[IDE] ERROR: Aborted Command'); | ||||
|         if (errorReg and $10) <> 0 then console.writestringln('[IDE] ERROR: ID Not Found'); | ||||
|         if (errorReg and $40) <> 0 then console.writestringln('[IDE] ERROR: Uncorrectable Data'); | ||||
|         if (errorReg and $80) <> 0 then console.writestringln('[IDE] ERROR: Bad Block'); | ||||
|     //wait for drive to be ready | ||||
|     while true do begin  | ||||
|         status := port_read(ATA_REG_COMMAND); | ||||
|  | ||||
|         if(status and ATA_SR_ERR) = ATA_SR_ERR then begin | ||||
|             console.writestringln('[IDE] (IDENTIFY_DEVICE) DRIVE ERROR!'); | ||||
|             console.redrawWindows(); | ||||
|             is_ready := false; | ||||
|             break; | ||||
|         end; | ||||
|  | ||||
|         if (status and ATA_SR_BUSY) <> ATA_SR_BUSY then begin | ||||
|             is_ready := true; | ||||
|             break; | ||||
|         end; | ||||
|     end; | ||||
| end; | ||||
|  | ||||
| {  | ||||
|     Check if the device is present on the IDE bus | ||||
|  | ||||
|     @param(device The device to check) | ||||
|     @returns(@True if the device is present, @False otherwise) | ||||
| } | ||||
| function is_device_present(var device : TIDE_Device) : boolean; | ||||
| function validate_28bit_address(addr : uint32) : boolean; | ||||
| begin | ||||
|     push_trace('ide.is_device_present()'); | ||||
|  | ||||
|     get_status(device); | ||||
|  | ||||
|     if (uInt8(device.status) = $FF) then begin //TODO make this more robust | ||||
|         is_device_present := false; | ||||
|     end else begin | ||||
|         is_device_present := true; | ||||
|     end; | ||||
|  | ||||
|     validate_28bit_address := (addr and $F0000000) = 0; | ||||
| end; | ||||
|  | ||||
| {  | ||||
|     Check if the device is an ATAPI device | ||||
|  | ||||
|     @param(device The device to check) | ||||
|     @returns(@True if the device is an ATAPI device, @False otherwise) | ||||
| } | ||||
| function check_device_type(var device : TIDE_Device) : boolean; | ||||
| function identify_device(bus : uint8; device : uint8) : TIdentResponse; | ||||
| var | ||||
|     sec, lba0, lba1, lba2 : uint8; | ||||
| begin | ||||
|     push_trace('ide.check_device_type()'); | ||||
|  | ||||
|     select_device(device); | ||||
|  | ||||
|     //TODO make sure dvice signture is set at this time, else reset it | ||||
|     //reset any device signature | ||||
| { | ||||
| Write 0x04 to the Control Register (0x3F6 for primary bus, 0x376 for secondary). | ||||
| Wait ~5 microseconds. | ||||
| Write 0x00 back to Control Register to complete reset. | ||||
| // } | ||||
|     outb(ATA_PRIMARY_BASE1, $04); | ||||
|     outb(ATA_SECONDARY_BASE1, $04); | ||||
|     sleep(1); | ||||
|     // outb(ATA_PRIMARY_BASE1, $00); | ||||
|     // outb(ATA_SECONDARY_BASE1, $00); | ||||
|     select_device(device); | ||||
|  | ||||
|     sleep(1); | ||||
|  | ||||
|     //read all bytes of LBA address | ||||
|     sec := inb(device.base + ATA_REG_SECCOUNT); | ||||
|     lba0 := inb(device.base + ATA_REG_LBA0); | ||||
|     lba1 := inb(device.base + ATA_REG_LBA1); | ||||
|     lba2 := inb(device.base + ATA_REG_LBA2); | ||||
|  | ||||
|     console.writestring('[IDE] (check_device_type) SEC: '); | ||||
|     console.writehexln(sec); | ||||
|     console.writestring('[IDE] (check_device_type) LBA0: '); | ||||
|     console.writehexln(lba0); | ||||
|     console.writestring('[IDE] (check_device_type) LBA1: '); | ||||
|     console.writehexln(lba1); | ||||
|     console.writestring('[IDE] (check_device_type) LBA2: '); | ||||
|     console.writehexln(lba2); | ||||
|  | ||||
|  | ||||
|     //check if the device is an ATAPI device | ||||
|     if ((sec = 3) or (lba2 = $EB)) then begin | ||||
|         check_device_type := true; | ||||
|         device.isATAPI := true; | ||||
|     end else if sec = 1 then begin | ||||
|         check_device_type := true; | ||||
|         device.isATAPI := false; | ||||
|     end else begin | ||||
|         check_device_type := false; | ||||
|     end; | ||||
| end; | ||||
|  | ||||
| {  | ||||
|     Load the device on the IDE bus | ||||
|  | ||||
|     @param(device The device to load) | ||||
|     @returns(@True if the device is loaded, @False otherwise) | ||||
| } | ||||
| procedure load_device(var device : TIDE_Device); | ||||
| var | ||||
|     i : uint8; | ||||
|     buffer : puint8; | ||||
|     storageDevice : PStorage_device; | ||||
|     success : boolean; | ||||
|     size : uint32; | ||||
| begin | ||||
|     push_trace('ide.load_device()'); | ||||
|     success := check_device_type(device); | ||||
|  | ||||
|     if (is_device_present(device) and success) then begin | ||||
|         console.writestringln('[IDE] (load_device) Device is present'); | ||||
|  | ||||
|         if (device.isATAPI) then begin | ||||
|             console.writestringln('[IDE] (load_device) Device is ATAPI'); | ||||
|             //todo load device info | ||||
|             success := atapi.identify_device(device); | ||||
|  | ||||
|             device.exists := true; | ||||
|             device.isATAPI := true; | ||||
|  | ||||
|         end else begin | ||||
|             console.writestringln('[IDE] (load_device) Device is ATA'); | ||||
|             success:= ata.identify_device(device); | ||||
|  | ||||
|             //check if atapi device using info from identify_device | ||||
|             console.writestringln('[IDE] (load_device) Device info: '); | ||||
|             console.writecharln(char(puint8(device.info)[252])); | ||||
|             console.writecharln(char(puint8(device.info)[253])); | ||||
|  | ||||
|             if (puint8(device.info)[253] = $FF) then begin | ||||
|                 console.writestringln('[IDE] (load_device) Device is ATAPI'); | ||||
|                 device.isATAPI := true; | ||||
|                 device.exists := true; | ||||
|  | ||||
|                 success := atapi.identify_device(device); | ||||
|             end; | ||||
|  | ||||
|         end; | ||||
|  | ||||
|         if not success then begin | ||||
|             console.writestringln('[IDE] (load_device) Error identifying device'); //todo  | ||||
|             device.exists := false; | ||||
|             exit; | ||||
|         end; | ||||
|  | ||||
|         device.exists := true; | ||||
|  | ||||
|         storageDevice := PStorage_Device(kalloc(sizeof(TStorage_Device))); | ||||
|         memset(uint32(storageDevice), 0, sizeof(TStorage_Device)); | ||||
|  | ||||
|         if (device.isATAPI) then begin | ||||
|             storageDevice^.controller := TControllerType.ControllerATAPI; | ||||
|             storageDevice^.writable := false; //TODO atapi | ||||
|             size := atapi.get_device_size(device); | ||||
|             storageDevice^.maxSectorCount := size; | ||||
|             storageDevice^.sectorSize := device.blockSize; | ||||
|  | ||||
|             if (device.isMaster) then begin | ||||
|                 storageDevice^.controllerId0 := 0; | ||||
|             end else begin | ||||
|                 storageDevice^.controllerId0 := 1; | ||||
|             end; | ||||
|  | ||||
|             //read and print first sector | ||||
|             buffer := puint8(kalloc(2048)); | ||||
|             memset(uint32(buffer), 11, 2048); | ||||
|             atapi.read_pio28(device, 11, 1, puint16(buffer)); | ||||
|  | ||||
|             console.writestringln('[IDE] (load_device) First sector of ATAPI device: '); | ||||
|             for i:=0 to 16 do begin | ||||
|                 console.writehexln(buffer[i]); | ||||
|             end; | ||||
|  | ||||
|             memset(uint32(buffer), 0, 2048); | ||||
|  | ||||
|             atapi.read_pio28(device, 5200, 1, puint16(buffer)); | ||||
|  | ||||
|             console.writestringln('[IDE] (load_device) First sector of ATAPI device: '); | ||||
|             for i:=0 to 16 do begin | ||||
|                 console.writehexln(buffer[i]); | ||||
|             end; | ||||
|  | ||||
|  | ||||
|         end else begin | ||||
|             storageDevice^.controller := TControllerType.ControllerATA; | ||||
|             storageDevice^.writable := true; | ||||
|  | ||||
|             storageDevice^.maxSectorCount := (device.info^[61] shl 16) or device.info^[60]; | ||||
|             storageDevice^.sectorSize := 512; //todo | ||||
|              | ||||
|             if (device.isMaster) then begin | ||||
|                 storageDevice^.controllerId0 := 0; | ||||
|             end else begin | ||||
|                 storageDevice^.controllerId0 := 1; | ||||
|             end; | ||||
|  | ||||
|             //if secotorcount is 0, then the device is not present | ||||
|             if (storageDevice^.maxSectorCount = 0) then begin | ||||
|                 device.exists := false; | ||||
|                 exit; | ||||
|             end; | ||||
|  | ||||
|             // //wrtie test, 1F2F1F2F repeated | ||||
|             // buffer := puint8(kalloc(1024)); | ||||
|             // memset(uint32(buffer), $02, 1024); | ||||
|  | ||||
|             // uint8(buffer[0]) := $1F; | ||||
|             // uint8(buffer[1]) := $2F; | ||||
|             // uint8(buffer[2]) := $3F; | ||||
|             // uint8(buffer[3]) := $4F; | ||||
|             // uint8(buffer[4]) := $5F; | ||||
|             // uint8(buffer[5]) := $6F; | ||||
|             // uint8(buffer[6]) := $7F; | ||||
|             // uint8(buffer[7]) := $8F; | ||||
|    | ||||
|             // //write to the first sector | ||||
|             // ata.write_pio28(device, 10, 2, puint16(buffer)); | ||||
|  | ||||
|             // //read the first sector | ||||
|             // memset(uint32(buffer), 0, 512); | ||||
|             // ata.read_pio28(device, 10, 1, puint16(buffer)); | ||||
|  | ||||
|             // //check if the data is the same | ||||
|             // for i:=0 to 10 do begin | ||||
|             //    console.writehexln(buffer[i]); | ||||
|             // end; | ||||
|         end; | ||||
|  | ||||
|         //register the device TODO | ||||
|         // drivemanager.register_device(storageDevice); | ||||
|  | ||||
|     end else begin | ||||
|         console.writestringln('[IDE] (load_device) Device is not present'); | ||||
|         device.exists := false; | ||||
|     end; | ||||
|  | ||||
|     pop_trace(); | ||||
| end; | ||||
|  | ||||
|  | ||||
| function load(ptr: void) : boolean; | ||||
| var  | ||||
|     pciDevice : PPCI_Device; | ||||
|     buffer : puint8; | ||||
|     status : uint8; | ||||
|     identResponse : TIdentResponse; | ||||
|     i : uint8; | ||||
| 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); | ||||
|     push_trace('IDE.Identify_Device'); | ||||
|     device_select(device); | ||||
|     no_interrupt(device); | ||||
|     port_write(ATA_REG_CONTROL, 0); | ||||
|  | ||||
|     load_device(primaryDevices[0]); | ||||
|     load_device(primaryDevices[1]); | ||||
|     load_device(secondaryDevices[0]); | ||||
|     load_device(secondaryDevices[1]); | ||||
|     //check if bus is floating | ||||
|     status := port_read(ATA_REG_COMMAND); | ||||
|     if status = $FF then exit; | ||||
|  | ||||
|     pop_trace(); | ||||
|     load := true; | ||||
|     port_write(ATA_REG_SECCOUNT, 0); | ||||
|     port_write(ATA_REG_LBA0, 0); | ||||
|     port_write(ATA_REG_LBA1, 0); | ||||
|     port_write(ATA_REG_LBA2, 0); | ||||
|  | ||||
|     console.writestringln('[IDE] (load) IDE Device Loading Finished'); | ||||
|     port_write(ATA_REG_COMMAND, ATA_CMD_IDENTIFY); | ||||
|  | ||||
|     i := 0; | ||||
|     //check if drive is present | ||||
|     status := port_read(ATA_REG_COMMAND); | ||||
|     if status = $00 then exit; | ||||
|  | ||||
|     if (primaryDevices[0].exists) then begin | ||||
|         console.writestringln('[IDE] (load) FOUND Primary Master Device 0'); | ||||
|         i := i + 1; | ||||
|     if not is_ready() then exit; | ||||
|  | ||||
|     for i:=0 to 255 do begin | ||||
|         identResponse[i] := inw(ATA_PRIMARY_BASE + ATA_REG_DATA); | ||||
|     end; | ||||
|  | ||||
|     if (primaryDevices[1].exists) then begin | ||||
|         console.writestringln('[IDE] (load) FOUND Primary Slave Device 1'); | ||||
|         i := i + 1; | ||||
|     end; | ||||
|  | ||||
|     if (secondaryDevices[0].exists) then begin | ||||
|         console.writestringln('[IDE] (load) FOUND Secondary Master Device 2'); | ||||
|         i := i + 1; | ||||
|     end; | ||||
|  | ||||
|     if (secondaryDevices[1].exists) then begin | ||||
|         console.writestringln('[IDE] (load) FOUND Secondary Slave Device 3'); | ||||
|         i := i + 1; | ||||
|     end; | ||||
|  | ||||
|     // console.writestringln('[IDE] (load) Found ' + i + ' IDE Devices'); | ||||
|  | ||||
|     identify_device := identResponse;  | ||||
| end; | ||||
|  | ||||
| procedure init();   | ||||
| @@ -516,8 +256,193 @@ begin | ||||
|     devID.id3:= idANY; | ||||
|     devID.id4:= idANY; | ||||
|     devID.ex:= nil; | ||||
|     drivermanagement.register_driver('IDE ATA/ATAPI Driver', @devID, @load); | ||||
|     drivermanagement.register_driver('IDE ATA Driver', @devID, @load); | ||||
|     console.writestringln('[IDE] (INIT) END'); | ||||
| end; | ||||
|  | ||||
| function load(ptr : void) : boolean; | ||||
| var | ||||
|     controller : PPCI_Device; | ||||
|     masterDevice : TStorage_Device; | ||||
|     slaveDevice : TStorage_Device; | ||||
|     buffer : puint8; | ||||
|     i : uint8; | ||||
| begin | ||||
|     push_trace('ide.load'); | ||||
|     console.writestringln('[IDE] (LOAD) BEGIN'); | ||||
|     controller := PPCI_Device(ptr); | ||||
|  | ||||
|     console.writestringln('[IDE] (INIT) CHECK FLOATING BUS'); | ||||
|     //check if bus is floating and identify device | ||||
|     if inb($1F7) <> $FF then begin | ||||
|         //outb($3F6, inb($3f6) or (1 shl 1)); // disable interrupts | ||||
|         IDEDevices[0].isMaster:= true; | ||||
|         IDEDevices[0].info := identify_device(0, ATA_DEVICE_MASTER); | ||||
|  | ||||
|         masterDevice.controller := ControllerIDE; | ||||
|         masterDevice.controllerId0:= 0; | ||||
|         masterDevice.maxSectorCount:= (IDEDevices[0].info[60] or (IDEDevices[0].info[61] shl 16) ); //LBA28 SATA | ||||
|          | ||||
|         if IDEDevices[0].info[1] = 0 then begin | ||||
|             console.writestringln('[IDE] (INIT) ERROR: DEVICE IDENT FAILED!'); | ||||
|             exit; | ||||
|         end; | ||||
|  | ||||
|         masterDevice.hpc:= uint32(IDEDevices[0].info[3] DIV IDEDevices[0].info[1]); | ||||
|  | ||||
|         masterDevice.sectorSize:= 512; | ||||
|         if masterDevice.maxSectorCount <> 0 then begin | ||||
|             IDEDevices[0].exists:= true; | ||||
|             masterDevice.readCallback:= @dread; | ||||
|             masterDevice.writeCallback:= @dwrite; | ||||
|             storagemanagement.register_device(@masterDevice); | ||||
|         end; | ||||
|  | ||||
|     end; | ||||
|  | ||||
|     // buffer:= puint8(kalloc(512)); | ||||
|     // buffer[0] := 1; | ||||
|     // buffer[1] := 2; | ||||
|     // buffer[2] := 3; | ||||
|     // buffer[3] := 4; | ||||
|     // buffer[4] := 5; | ||||
|     // buffer[5] := 6; | ||||
|     // writePIO28(0, 3, buffer); | ||||
|     // writePIO28(0, 3, buffer); | ||||
|     // writePIO28(0, 3, buffer); | ||||
|     // writePIO28(0, 4, buffer); | ||||
|     // writePIO28(0, 5, buffer); | ||||
|     // writePIO28(0, 5, buffer); | ||||
|     // writePIO28(0, 5, buffer); | ||||
|     // psleep(1000); | ||||
|     // writePIO28(0, 5, buffer); | ||||
|     // writePIO28(0, 5, buffer); | ||||
|     // writePIO28(0, 5, buffer); | ||||
|     // kfree(puint32(buffer)); | ||||
|  | ||||
|     console.writestringln('[IDE] (LOAD) END'); | ||||
| end; | ||||
|  | ||||
| procedure readPIO28(drive : uint8; LBA : uint32; buffer : puint8); | ||||
| var | ||||
|     status : uint8; | ||||
|     i: uint16; | ||||
|     device: uint8; | ||||
|     data: uint16; | ||||
| begin  | ||||
|        if not validate_28bit_address(LBA) then begin | ||||
|         console.writestringln('IDE (writePIO28) ERROR: Invalid LBA!'); | ||||
|     end; | ||||
|  | ||||
|     //Add last 4 bits of LBA to device port | ||||
|     if IDEDevices[drive].isMaster then begin | ||||
|         device:= ATA_DEVICE_MASTER; | ||||
|         device_select($E0 or ((LBA and $0F000000) shr 24)); //LBA primary master | ||||
|     end | ||||
|     else begin | ||||
|         device:= ATA_DEVICE_SLAVE; | ||||
|         device_select($F0 or ((LBA and $0F000000) shr 24)); //LBA primary slave | ||||
|     end; | ||||
|  | ||||
|     no_interrupt(device); | ||||
|     port_write(ATA_REG_ERROR, 0); | ||||
|  | ||||
|     //Write sector count and LBA | ||||
|     port_write(ATA_REG_SECCOUNT, 1); | ||||
|     port_write(ATA_REG_LBA0, (LBA and $000000FF)); | ||||
|     port_write(ATA_REG_LBA1, (LBA and $0000FF00) shr 8); | ||||
|     port_write(ATA_REG_LBA2, (LBA and $00FF0000) shr 16); | ||||
|  | ||||
|     //send read command | ||||
|     port_write(ATA_REG_COMMAND, ATA_CMD_READ_PIO);  | ||||
|     if not is_ready() then exit; | ||||
|  | ||||
|     i:=0; | ||||
|     while i < 512 do begin | ||||
|         // if not is_ready() then exit; | ||||
|  | ||||
|         data:= inw(ATA_PRIMARY_BASE + ATA_REG_DATA); | ||||
|  | ||||
|         buffer[i+1] := uint8($00ff and (data shr 8)); | ||||
|         buffer[i] := uint8($00ff and data); | ||||
|  | ||||
|         i:= i + 2; | ||||
|     end; | ||||
| end; | ||||
|  | ||||
| procedure writePIO28(drive : uint8; LBA : uint32; buffer : puint8); | ||||
| var | ||||
|     status : uint8; | ||||
|     i: uint16; | ||||
|     device: uint8; | ||||
| begin  | ||||
|     push_trace('IDE.WritePIO28'); | ||||
|     if not validate_28bit_address(LBA) then begin | ||||
|         console.writestringln('IDE (writePIO28) ERROR: Invalid LBA!'); | ||||
|     end; | ||||
|  | ||||
|     console.writeintln(uint32(drive)); | ||||
|     console.writeintln(LBA); | ||||
|  | ||||
|     //Add last 4 bits of LBA to device port | ||||
|     if IDEDevices[drive].isMaster then begin | ||||
|         device:= ATA_DEVICE_MASTER; | ||||
|         device_select($E0 or ((LBA and $0F000000) shr 24)); //LBA primary master | ||||
|     end | ||||
|     else begin | ||||
|         device:= ATA_DEVICE_SLAVE; | ||||
|         device_select($F0 or ((LBA and $0F000000) shr 24)); //LBA primary slave | ||||
|     end; | ||||
|  | ||||
|     // no_interrupt(device); | ||||
|  | ||||
|     port_write(ATA_REG_ERROR, 0); | ||||
|     port_write(ATA_REG_CONTROL, 0); | ||||
|  | ||||
|     // check if bus is floating | ||||
|     status := port_read(ATA_REG_COMMAND); | ||||
|     if status = $FF then exit; | ||||
|  | ||||
|     //Write sector count and LBA | ||||
|     port_write(ATA_REG_SECCOUNT, 1); | ||||
|     port_write(ATA_REG_LBA0, (LBA and $000000FF)); | ||||
|     port_write(ATA_REG_LBA1, (LBA and $0000FF00) shr 8); | ||||
|     port_write(ATA_REG_LBA2, (LBA and $00FF0000) shr 16); | ||||
|  | ||||
|     //send write command | ||||
|     port_write(ATA_REG_COMMAND, ATA_CMD_WRITE_PIO);  | ||||
|  | ||||
|     //write data | ||||
|     i:=0; | ||||
|     while i < 512 do begin | ||||
|         outw(ATA_PRIMARY_BASE + ATA_REG_DATA, uint16(buffer[i] or (buffer[i+1] shl 8))); | ||||
|         i:= i + 2; | ||||
|     end; | ||||
|  | ||||
|     //flush drive cache | ||||
|     psleep(1); | ||||
|     port_write(ATA_REG_COMMAND, ATA_CMD_CACHE_FLUSH); | ||||
|     psleep(1); | ||||
|     if not is_ready() then exit; | ||||
| end; | ||||
|  | ||||
| procedure dread(device : PStorage_device; LBA : uint32; sectorCount : uint32; buffer : Puint32); | ||||
| var | ||||
|     i : uint16; | ||||
| begin | ||||
|     for i:=0 to sectorCount-1 do begin | ||||
|         readPIO28(device^.controllerId0, LBA, puint8(@buffer[512*i]));  | ||||
|     end; | ||||
| end; | ||||
|  | ||||
| procedure dwrite(device : PStorage_device; LBA : uint32; sectorCount : uint32; buffer : Puint32); | ||||
| var | ||||
|     i : uint16; | ||||
| begin | ||||
|     for i:=0 to sectorCount-1 do begin | ||||
|         writePIO28(device^.controllerId0, LBA, puint8(@buffer[512*i])); | ||||
|     end; | ||||
|     // writePIO28(device^.controllerId0, LBA, puint8(buffer)); | ||||
| end; | ||||
|  | ||||
| end. | ||||
| @@ -1,81 +0,0 @@ | ||||
| { | ||||
|     Driver->Storage->MBR Master boot record  | ||||
|  | ||||
|     @author(Aaron Hance ah@aaronhance.me) | ||||
| } | ||||
| unit mbr; | ||||
|  | ||||
| interface | ||||
|  | ||||
| uses | ||||
|     tracer; | ||||
|  | ||||
| type | ||||
|  | ||||
|     PMaster_Boot_Record = ^TMaster_Boot_Record; | ||||
|     PPartition_table = ^TPartition_table; | ||||
|  | ||||
|     TPartition_table = bitpacked record  | ||||
|         attributes     : uint8; | ||||
|         CHS_start      : array[0..2] of uint8; | ||||
|         system_id      : uint8; | ||||
|         CHS_end        : array[0..2] of uint8; | ||||
|         LBA_start      : uInt32; | ||||
|         sector_count   : uInt32; | ||||
|     end; | ||||
|  | ||||
|     TMaster_Boot_Record = bitpacked record  | ||||
|         bootstrap   : array[0..439] of uint8; | ||||
|         signature   : uint32; | ||||
|         rsv         : uint16; | ||||
|         partition   : array[0..3] of TPartition_table; | ||||
|         boot_sector : uint16; | ||||
|     end; | ||||
|  | ||||
|     T24bit = array[0..2] of uint8; | ||||
|  | ||||
|     function get_bootable(partition_table : PPartition_table) : boolean; | ||||
|     procedure set_bootable(partition_table : PPartition_table); | ||||
|     procedure setup_partition(partition_table : PPartition_table; address : uint32; sectorSize : uint32); | ||||
|  | ||||
| implementation | ||||
|  | ||||
| { convert LBA address to CHS address} | ||||
| function LBA_2_CHS(lba : uint32) : T24bit; | ||||
| var | ||||
|     dat : T24bit; | ||||
| begin | ||||
|   //TODO impliment procedure | ||||
|   LBA_2_CHS := dat; | ||||
| end; | ||||
|  | ||||
| { Set a partition struct to be bootable} | ||||
| procedure set_bootable(partition_table : PPartition_table); | ||||
| begin | ||||
|     push_trace('MBR.set_bootable'); | ||||
|     //set the bootble bit in attributes | ||||
|     partition_table^.attributes := (partition_table^.attributes and $80); | ||||
| end; | ||||
|  | ||||
| { Check a partitions bootable bit } | ||||
| function get_bootable(partition_table : PPartition_table) : boolean; | ||||
| begin | ||||
|     push_trace('MBR.get_bootable'); | ||||
|     //get the bootble bit in attributes | ||||
|     get_bootable := (partition_table^.attributes and $80) = $80; | ||||
| end; | ||||
|  | ||||
| { Setup a partition table struct } | ||||
| procedure setup_partition(partition_table : PPartition_table; address : uint32; sectorSize : uint32); | ||||
| begin | ||||
|     push_trace('MBR.setup_partition'); | ||||
|     //set values in both LBA and CHS addressing schemes | ||||
|     partition_table^.LBA_start := address; | ||||
|     partition_table^.sector_count := sectorSize; | ||||
|     partition_table^.CHS_start := LBA_2_CHS(address); | ||||
|     partition_table^.CHS_start := LBA_2_CHS(address + sectorSize);   | ||||
|     push_trace('MBR.setup_partition.end'); | ||||
| end; | ||||
|  | ||||
|  | ||||
| end. | ||||
| @@ -1,306 +0,0 @@ | ||||
| //  Copyright 2021 Aaron Hance | ||||
| // | ||||
| //  Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| //  you may not use this file except in compliance with the License. | ||||
| //  You may obtain a copy of the License at | ||||
| // | ||||
| //  http://www.apache.org/licenses/LICENSE-2.0 | ||||
| // | ||||
| //  Unless required by applicable law or agreed to in writing, software | ||||
| //  distributed under the License is distributed on an "AS IS" BASIS, | ||||
| //  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| //  See the License for the specific language governing permissions and | ||||
| //  limitations under the License. | ||||
|  | ||||
| { | ||||
|     Driver->Storage->DriveManager Physical storage device manager | ||||
|  | ||||
|     @author(Aaron Hance ah@aaronhance.me) | ||||
| } | ||||
| unit drivemanager; | ||||
|  | ||||
| interface | ||||
|  | ||||
| uses | ||||
|     util, | ||||
|     drivertypes, | ||||
|     console, | ||||
|     terminal, | ||||
|     drivermanagement, | ||||
|     lmemorymanager, | ||||
|     strings, | ||||
|     lists, | ||||
|     tracer, | ||||
|     rtc, | ||||
|     MBR, | ||||
|     serial, | ||||
|     volumemanager, | ||||
|     storagetypes; | ||||
|  | ||||
| var  | ||||
|     storageDevices : PLinkedListBase; //index in this array is global drive id | ||||
|  | ||||
|     procedure init(); | ||||
|  | ||||
|     procedure register_device(device : PStorage_Device); | ||||
|     function get_drive_list() : PLinkedListBase; | ||||
|     function controller_type_2_string(controllerType : TControllerType) : pchar;  | ||||
|  | ||||
| implementation  | ||||
|  | ||||
| procedure list_drive_command(); | ||||
| var  | ||||
|     i : uint32 = 0; | ||||
| begin | ||||
|     push_trace('DriverManager.list_drive_command'); | ||||
|     //if no storage device print none found | ||||
|     if LL_Size(storageDevices) < 1 then begin | ||||
|         console.writestringlnWnd('No storage devices found.', getTerminalHWND()); | ||||
|         exit; | ||||
|     end; | ||||
|  | ||||
|     //print number of storage devices | ||||
|     console.writeintwnd(LL_Size(storageDevices), getTerminalHWND()); | ||||
|     console.writestringlnWnd(' devices found', getTerminalHWND()); | ||||
|  | ||||
|     for i:=0 to LL_Size(storageDevices)-1 do begin | ||||
|  | ||||
|         //print device id and type | ||||
|         console.writeintwnd(i, getTerminalHWND()); | ||||
|         console.writestringwnd(' - Device type: ', getTerminalHWND()); | ||||
|         console.writestringwnd(controller_type_2_string(PStorage_Device(LL_Get(storageDevices, i))^.controller), getTerminalHWND()); | ||||
|         console.writestringwnd(', ', getTerminalHWND()); | ||||
|  | ||||
|         //print device capcity | ||||
|         console.writestringwnd(' Capacity: ', getTerminalHWND()); | ||||
|         console.writeintwnd((( PStorage_Device(LL_Get(storageDevices, i))^.maxSectorCount * PStorage_Device(LL_Get(storageDevices, i))^.sectorSize) DIV 1024) DIV 1024, getTerminalHWND()); | ||||
|         console.writestringlnWnd('MB', getTerminalHWND()); | ||||
|     end; | ||||
|  | ||||
| end; | ||||
|  | ||||
| { Disk subcommand for formatting drives } | ||||
| procedure format_command(params : PParamList); | ||||
| var | ||||
|     driveIndex : uint16; | ||||
|     drive : PStorage_Device; | ||||
|  | ||||
|     filesystemString : pchar; | ||||
|     spc : puint32; | ||||
|  | ||||
|     sectorCount : uint32; | ||||
|     volumeId : uint8; | ||||
|  | ||||
|     mb : PMaster_Boot_Record; | ||||
|     i : uint32 = 0; | ||||
|  | ||||
| begin | ||||
|     push_trace('DriverManager.format_command'); | ||||
|  | ||||
|     //format <DRIVE_ID> <FILESYSTEM> <size> | ||||
|  | ||||
|     spc:= puint32(kalloc(4)); | ||||
|     spc^:= 1; | ||||
|  | ||||
|     push_trace('DriverManager.format_command.0'); | ||||
|  | ||||
|     //get drive from index/id | ||||
|     driveIndex:= stringToInt( getParam(1, params) ); | ||||
|     drive:= PStorage_Device(LL_Get(storageDevices, driveIndex)); | ||||
|  | ||||
|     filesystemString := getParam(2, params); | ||||
|     sectorCount := stringToInt( getParam(3, params) ); | ||||
|  | ||||
|     // if sector count is 0, use full drive | ||||
|     if sectorCount = 0 then begin | ||||
|         sectorCount := drive^.maxSectorCount - 10; | ||||
|     end; | ||||
|  | ||||
|     //create MBR if none, and partition table | ||||
|     mb := PMaster_Boot_Record(kalloc(sizeof(TMaster_Boot_Record)*2)); | ||||
|     memset(uint32(mb), 0, sizeof(TMaster_Boot_Record)); | ||||
|     drive^.readCallback(drive, 0, 1, PuInt32(mb)); | ||||
|  | ||||
|     //check if MBR exists | ||||
|     if  (mb^.boot_sector <> $55AA)then begin | ||||
|  | ||||
|         writestringlnWND('Creating MBR', getTerminalHWND()); | ||||
|  | ||||
|         //create MBR | ||||
|         mb^.signature := $A570 + drive^.id; | ||||
|         mb^.boot_sector := $55AA; | ||||
|          | ||||
|         //create partition table | ||||
|         mbr.setup_partition(@mb^.partition[0], 2, sectorCount); | ||||
|  | ||||
|         //write MBR | ||||
|         drive^.writeCallback(drive, 0, 1, puint32(mb)); | ||||
|          | ||||
|     end else begin | ||||
|  | ||||
|         writestringlnWND('MBR already exists', getTerminalHWND()); | ||||
|      | ||||
|         //write first partiton sectorStart and sectorCount | ||||
|         writeintlnWND(mb^.partition[0].LBA_start, getTerminalHWND()); | ||||
|         writeintlnWND(mb^.partition[0].sector_count, getTerminalHWND()); | ||||
|  | ||||
|         //create partition table | ||||
|         mbr.setup_partition(@mb^.partition[0], 2, sectorCount); | ||||
|  | ||||
|         //write MBR | ||||
|         drive^.writeCallback(drive, 0, 1, puint32(mb)); | ||||
|  | ||||
|     end; | ||||
|  | ||||
|     kfree(puint32(mb)); | ||||
|  | ||||
|     //setup volume | ||||
|     volumemanager.create_volume(drive, filesystemString, 2, sectorCount); | ||||
|  | ||||
|     writestringWnd('Drive ', getTerminalHWND); | ||||
|     writeintWnd(driveIndex, getTerminalHWND); | ||||
|     writestringlnWnd(' formatted.', getTerminalHWND); | ||||
|      | ||||
| end; | ||||
|  | ||||
| { initalise a drive with a MBR - TODO needs to be expanded for partitioning } | ||||
| procedure init_drive_command(driveIndex : uint32); | ||||
| var | ||||
|     bootrecord : PMaster_Boot_Record; | ||||
|     partition : PPartition_table; | ||||
|     drive : PStorage_device; | ||||
| begin | ||||
|     push_trace('DriverManager.init_drive_command'); | ||||
|     //setup MBR and partition table | ||||
|     bootrecord := PMaster_Boot_record(kalloc(SizeOf(TMaster_boot_record))); | ||||
|     partition := PPartition_table(kalloc(SizeOf(TPartition_table))); | ||||
|  | ||||
|     drive := PStorage_device(LL_Get(storageDevices, driveIndex)); | ||||
|  | ||||
|     MBR.setup_partition(partition, 100, drive^.maxSectorCount-300); | ||||
|     bootrecord^.partition[0] := partition^; | ||||
|  | ||||
|     //write MBR and partition table to disk | ||||
|     drive^.writeCallback(drive, 0, 1, puint32(bootrecord)); | ||||
|  | ||||
|     //volume_manager reload partition | ||||
|     //TODO | ||||
|  | ||||
| end; | ||||
|  | ||||
| procedure drive_command(params : PParamList); | ||||
| var | ||||
|     i : uint16; | ||||
|     drive : uint16; | ||||
|     subcmd : pchar; | ||||
| begin | ||||
|     push_trace('DriverManager.drive_command'); | ||||
|     subcmd:= getParam(0, params); | ||||
|     push_trace('DriverManager.drive_command.0'); | ||||
|  | ||||
|     if ((paramCount(params) = 0)) then begin | ||||
|         console.writestringlnWnd('Please provide valid arguments.', getTerminalHWND()); | ||||
|         console.writestringlnWnd('  ls - for listing all drives', getTerminalHWND()); | ||||
|         console.writestringlnWnd('  info [drive] - display formation for specified drive', getTerminalHWND()); | ||||
|         console.writestringlnWnd('  init [drive] - destructive, formats a drive with a blank partition', getTerminalHWND()); | ||||
|         console.writestringlnWnd('  format [drive] [filesystem] [size (0 for max)] - destructive, formats a drive with MBR and volume', getTerminalHWND()); | ||||
|         exit; | ||||
|     end; | ||||
|     push_trace('DriverManager.drive_command.1'); | ||||
|  | ||||
|     if( stringEquals(subcmd, 'ls') ) then begin | ||||
|       list_drive_command(); | ||||
|       exit; | ||||
|     end; | ||||
|     push_trace('DriverManager.drive_command.2'); | ||||
|  | ||||
|     if( stringEquals(subcmd, 'info') ) then begin | ||||
|         console.writestringWnd('Disk: ', getTerminalHWND()); | ||||
|         console.writeintlnWND(1, getTerminalHWND()); | ||||
|  | ||||
|         console.writestringWnd('Capacity: ', getTerminalHWND()); | ||||
|         console.writeintlnWND(1, getTerminalHWND()); | ||||
|         //TODO impliment | ||||
|       exit; | ||||
|     end; | ||||
|  | ||||
|     if( stringEquals(subcmd, 'init') ) then begin | ||||
|       init_drive_command(stringToInt(getParam(1, params) ) ); | ||||
|       exit; | ||||
|     end; | ||||
|     push_trace('DriverManager.drive_command.3'); | ||||
|  | ||||
|     if( stringEquals(subcmd, 'format') ) then begin | ||||
|       format_command(params); | ||||
|       exit; | ||||
|     end; | ||||
|     push_trace('DriverManager.drive_command.4'); | ||||
|  | ||||
| end; | ||||
|  | ||||
| { Initialise the drive manager } | ||||
| procedure init(); | ||||
| begin | ||||
|     push_trace('DriveManager.init'); | ||||
|      | ||||
|     //setup drive manager | ||||
|     setworkingdirectory('.'); | ||||
|     storageDevices:= ll_new(sizeof(TStorage_Device)); | ||||
|  | ||||
|     //register DISK command  | ||||
|     terminal.registerCommand('DISK', @drive_command, 'Disk utility'); | ||||
| end; | ||||
|  | ||||
| { Register a new drive with the drive manager } | ||||
| procedure register_device(device : PStorage_device); | ||||
| var  | ||||
|     i   : uint8; | ||||
|     elm : void; | ||||
| begin | ||||
|     push_trace('DriveManager.register_device'); | ||||
|  | ||||
|     //add the drive to the list of storage devices. | ||||
|     elm:= LL_Add(storageDevices);  | ||||
|     //PStorage_device(elm)^ := device^; //TODO memcopy | ||||
|     memcpy(uint32(device), uint32(elm), SizeOf(TStorage_Device)); | ||||
|  | ||||
|     //set drive id in struct | ||||
|     PStorage_device(elm)^.id := LL_Size(storageDevices) - 1; | ||||
|  | ||||
|     //create empty volume list for drive | ||||
|     PStorage_device(elm)^.volumes := LL_New(sizeof(TStorage_Volume)); | ||||
|      | ||||
|     //TODO register with volume manager | ||||
|     //volumemanager.check_for_volumes(PStorage_Device(elm)); | ||||
| end; | ||||
|  | ||||
| { Get the drive list } | ||||
| function get_drive_list() : PLinkedListBase; | ||||
| begin | ||||
|     push_trace('DriverManager.get_drive_list'); | ||||
|  | ||||
|     //return the device list | ||||
|     get_drive_list:= storageDevices; | ||||
| end; | ||||
|  | ||||
| { Get string name for controller type } | ||||
| function controller_type_2_string(controllerType : TControllerType) : pchar;  | ||||
| begin | ||||
|     push_trace('DriverManager.controller_type_2_string'); | ||||
|  | ||||
|     //return string of controller type | ||||
|     case controllerType of | ||||
|         ControllerATA : controller_type_2_string:= 'ATA'; | ||||
|         ControllerATAPI : controller_type_2_string:= 'ATAPI'; | ||||
|         ControllerUSB :  controller_type_2_string:= 'USB'; | ||||
|         ControllerAHCI : controller_type_2_string:= 'ACHI (SATA)'; | ||||
|         ControllerNET : controller_type_2_string:= 'Net'; | ||||
|     end; | ||||
| end; | ||||
|  | ||||
| function write_2_drive(drive : uint32; address : uint32; byteSize : uint32; buffer : puint32) : PDrive_Error; | ||||
| begin | ||||
|    | ||||
| end; | ||||
|  | ||||
| end. | ||||
| @@ -1,429 +0,0 @@ | ||||
| //  Copyright 2021 Aaron Hance | ||||
| // | ||||
| //  Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| //  you may not use this file except in compliance with the License. | ||||
| //  You may obtain a copy of the License at | ||||
| // | ||||
| //  http://www.apache.org/licenses/LICENSE-2.0 | ||||
| // | ||||
| //  Unless required by applicable law or agreed to in writing, software | ||||
| //  distributed under the License is distributed on an "AS IS" BASIS, | ||||
| //  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| //  See the License for the specific language governing permissions and | ||||
| //  limitations under the License. | ||||
|  | ||||
| { | ||||
|     Driver->Storage->VolumeManager Drive volume manager | ||||
|  | ||||
|     @author(Aaron Hance ah@aaronhance.me) | ||||
| } | ||||
| unit volumemanager; | ||||
|  | ||||
| interface | ||||
|  | ||||
| uses | ||||
|     util,  | ||||
|     console, | ||||
|     terminal, | ||||
|     strings, | ||||
|     lists, | ||||
|     tracer, | ||||
|     lmemorymanager, | ||||
|     vfs, | ||||
|     MBR, | ||||
|     storagetypes; | ||||
|  | ||||
| type | ||||
|      | ||||
|     TDirectory_Entry_Type = (directoryEntry, fileEntry, mountEntry); | ||||
|  | ||||
|     PDirectory_Entry = ^TDirectory_Entry; | ||||
|     PFilesystem = ^TFilesystem; | ||||
|     PStorage_Volume = ^TStorage_Volume; | ||||
|  | ||||
|     PPWriteHook = procedure(volume : PStorage_volume; directory : pchar; entry : PDirectory_Entry; byteCount : uint32; buffer : puint32; statusOut : puint32); | ||||
|     PPReadHook = function(volume : PStorage_Volume; directory : pchar; fileName : pchar; fileExtension : pchar; buffer : puint32; bytecount : puint32) : uint32; | ||||
|     PPHIOHook = procedure(volume : PStorage_device; addr : uint32; sectors : uint32; buffer : puint32); | ||||
|     PPCreateHook = procedure(volume : PStorage_volume; start : uint32; size : uint32; config : puint32); | ||||
|     PPDetectHook = procedure(disk : PStorage_Device); | ||||
|     PPCreateDirHook = procedure(volume : PStorage_volume; directory : pchar; dirname : pchar; attributes : uint32; status : puint32); | ||||
|     PPReadDirHook = function(volume : PStorage_volume; directory : pchar; status : puint32) : PLinkedListBase; //returns: 0 = success, 1 = dir not exsist, 2 = not directory, 3 = error //returns: 0 = success, 1 = dir not exsist, 2 = not directory, 3 = error | ||||
|  | ||||
|     PPHIOHook_ = procedure; | ||||
|  | ||||
|     TFilesystem = record | ||||
|         sName : pchar; | ||||
|         system_id : uint8; | ||||
|         writeCallback     : PPWriteHook; | ||||
|         readCallback      : PPReadHook; | ||||
|         createCallback    : PPCreateHook; //create volume | ||||
|         detectCallback    : PPDetectHook; //detect volume | ||||
|         createDirCallback : PPCreateDirHook; | ||||
|         readDirCallback   : PPReadDirHook; | ||||
|     end; | ||||
|  | ||||
|     TStorage_Volume = record | ||||
|         volumeHandle    : uint32; | ||||
|         device          : PStorage_device; | ||||
|         sectorStart     : uint32; | ||||
|         sectorSize      : uint32; | ||||
|         sectorCount     : uint32; | ||||
|         freeSectors     : uint32; | ||||
|         filesystem      : PFilesystem;  | ||||
|         { True if this drive contains the loaded OS } | ||||
|         isBootDrive     : boolean; | ||||
|     end; | ||||
|  | ||||
|     { Generic directory entry } | ||||
|     TDirectory_Entry = record | ||||
|         { Contains filename and optionally file extension, seperated by the last period.} | ||||
|         fileName  : pchar;  | ||||
|         entryType : TDirectory_Entry_Type; | ||||
|         // creationT : TDateTime; | ||||
|         // modifiedT : TDateTime; | ||||
|     end; | ||||
|  | ||||
|     TFilehandleEntry = record | ||||
|         volume : PStorage_Volume; | ||||
|         fileName : pchar; | ||||
|         fileHandle : uint32; | ||||
|         openMode : TOpenMode; | ||||
|         writeMode : TWriteMode; | ||||
|         lock : Boolean; | ||||
|         closed : Boolean; | ||||
|  | ||||
|     end; | ||||
|  | ||||
|     PFilehandleEntry = ^TFilehandleEntry; | ||||
|    | ||||
| var | ||||
|  | ||||
|     storageVolumes : PLinkedListBase; | ||||
|     filesystems : PlinkedListBase; | ||||
|  | ||||
|     fileHandles : PLinkedListBase; | ||||
|  | ||||
|     { Returns the number of volumes } | ||||
|  | ||||
|     procedure init(); | ||||
|      | ||||
|     procedure register_filesystem(filesystem : PFilesystem); | ||||
|     procedure check_for_volumes(drive : PStorage_device); | ||||
|     procedure create_volume(disk : PStorage_device; filesystem : PChar; sectorStart : uint32; sectorCount : uint32); | ||||
|  | ||||
|     //IO functions | ||||
|  | ||||
| implementation | ||||
|  | ||||
| procedure format_volume_command(params : PParamList); | ||||
| var | ||||
|     volume : PStorage_Volume; | ||||
|     filesystem : PFilesystem; | ||||
|     config : puint32; | ||||
| begin | ||||
|     push_trace('VolumeManager.format_volume_command'); | ||||
|     console.writestringlnWND('Not implimented, use disk format', getTerminalHWND()); | ||||
|     exit; | ||||
|  | ||||
|     if paramCount(params) < 3 then begin | ||||
|       console.writestringlnWND('Invalid arguments', getTerminalHWND()); | ||||
|     end; | ||||
|         push_trace('VolumeManager.format_volume_command.1'); | ||||
|  | ||||
|     volume := PStorage_volume(LL_get(storageVolumes, stringToInt(getParam(1, params)))); | ||||
|     filesystem := PFilesystem(LL_get(filesystems, stringToInt(getParam(2, params)))); | ||||
|             push_trace('VolumeManager.format_volume_command.2'); | ||||
|  | ||||
|     config := PuInt32(kalloc(sizeof(uInt32))); | ||||
|     config^ := 4; | ||||
|         push_trace('VolumeManager.format_volume_command.3'); | ||||
|  | ||||
|     //TODO check things exsist | ||||
|  | ||||
|     console.writestringlnWND('Staring volume formatting...', getTerminalHWND()); | ||||
|     // filesystem^.formatVolumeCallback(volume^.device, volume^.sectorSize, volume^.sectorStart, config); | ||||
|     console.writestringlnWND('Volume format finished.', getTerminalHWND()); | ||||
| end; | ||||
|  | ||||
| procedure list_filesystems_command(); | ||||
| var | ||||
|   i : uint32; | ||||
|   filesystem : PFilesystem; | ||||
| begin | ||||
|    push_trace('VolumeManager.list_filesystems_command'); | ||||
|  | ||||
|   if LL_Size(filesystems) < 1 then begin | ||||
|       console.writestringlnWnd('ERROR: no filesystems found!', getTerminalHWND()); | ||||
|       exit; | ||||
|   end; | ||||
|  | ||||
|   console.writestringlnWnd('Filesystems: ', getTerminalHWND()); | ||||
|  | ||||
|   for i:=0 to LL_Size(filesystems) - 1 do begin | ||||
|       filesystem := PFilesystem(LL_Get(filesystems, i)); | ||||
|  | ||||
|       console.writestringWnd('  ', getTerminalHWND()); | ||||
|       console.writestringWnd(filesystem^.sName, getTerminalHWND()); | ||||
|       console.writestringWnd(' - ', getTerminalHWND()); | ||||
|       console.writehexlnWND(filesystem^.system_id, getTerminalHWND()); | ||||
|   end; | ||||
| end; | ||||
|  | ||||
| procedure list_volume_command(); | ||||
| var  | ||||
|     i : uint32;  | ||||
|     volume : PStorage_Volume; | ||||
| begin | ||||
|   push_trace('VolumeManager.list_volume_command'); | ||||
|  | ||||
|   if LL_Size(storageVolumes) < 1 then begin | ||||
|       console.writestringlnWnd('No volumes found.', getTerminalHWND()); | ||||
|       exit; | ||||
|   end; | ||||
|  | ||||
|   console.writestringlnWnd('Volumes: ', getTerminalHWND()); | ||||
|  | ||||
|   for i:=0 to LL_Size(storageVolumes) - 1 do begin | ||||
|       volume := PStorage_Volume(LL_Get(storageVolumes, i)); | ||||
|  | ||||
|       console.writestringWnd('  ', getTerminalHWND()); | ||||
|       console.writeintWnd(i, getTerminalHWND()); | ||||
|       console.writestringWnd(' - Capacity: ', getTerminalHWND()); | ||||
|       console.writeintWnd(uint32((volume^.sectorSize * volume^.sectorCount) DIV 1024 DIV 1024), getTerminalHWND()); | ||||
|       console.writestringWnd('MB,  Filesystem: ', getTerminalHWND()); | ||||
|  | ||||
|       if volume^.filesystem <> nil then begin | ||||
|         console.writestringlnWnd(volume^.filesystem^.sName, getTerminalHWND()); | ||||
|       end else begin | ||||
|         console.writestringlnWnd('None', getTerminalHWND()); | ||||
|       end; | ||||
|  | ||||
|   end; | ||||
| end; | ||||
|  | ||||
| procedure volume_command(params : PParamList); | ||||
| var | ||||
|     i : uint16; | ||||
|     subcmd : pchar; | ||||
| begin | ||||
|     push_trace('VolumeManager.volume_command'); | ||||
|     subcmd:= getParam(0, params); | ||||
|  | ||||
|     if ((paramCount(params) = 0)) then begin | ||||
|         console.writestringlnWnd('Please provide valid arguments.', getTerminalHWND()); | ||||
|         console.writestringlnWnd('  ls - for listing all volumes', getTerminalHWND()); | ||||
|         console.writestringlnWnd('  lsfs - for listing all filesystems', getTerminalHWND()); | ||||
|         console.writestringlnWnd('  info [volume] - display formation for specified volume', getTerminalHWND()); | ||||
|         console.writestringlnWnd('  format [volume] [filesystem] - destructive, formats a volume with a specified filesystem', getTerminalHWND()); | ||||
|         exit; | ||||
|     end; | ||||
|  | ||||
|     if( stringEquals(subcmd, 'ls') ) then begin | ||||
|       list_volume_command(); | ||||
|       exit; | ||||
|     end; | ||||
|  | ||||
|     if( stringEquals(subcmd, 'lsfs') ) then begin | ||||
|       list_filesystems_command(); | ||||
|       exit; | ||||
|     end; | ||||
|  | ||||
|     if( stringEquals(subcmd, 'format') ) then begin | ||||
|       format_volume_command(params); | ||||
|       exit; | ||||
|     end; | ||||
| end; | ||||
|  | ||||
| { Initialise volume manager } | ||||
| procedure init(); | ||||
| begin | ||||
|     push_trace('VolumeManager.init'); | ||||
|  | ||||
|     { setup lists } | ||||
|     storageVolumes:= ll_New(sizeof(TFilesystem)); | ||||
|     filesystems:= ll_New(sizeof(TFilesystem));    | ||||
|     fileHandles:= ll_New(sizeof(TFilehandleEntry)); | ||||
|  | ||||
|     { register commands } | ||||
|  | ||||
|     terminal.registerCommand('volume', @volume_command, 'Volume utility');    | ||||
| end; | ||||
|  | ||||
| { register a new type of filesystem } | ||||
| procedure register_filesystem(filesystem : PFilesystem); //TODO on init create null filesystem for empty partition | ||||
| var | ||||
|     elm : void; | ||||
| begin | ||||
|     //add filesystem | ||||
|     elm := LL_add(filesystems); | ||||
|     memcpy(uint32(filesystem), uInt32(elm), SizeOf(TFilesystem)); | ||||
|  | ||||
|     //check drives for volumes of new type | ||||
| end; | ||||
|  | ||||
| { Check for volumes on a physical device } | ||||
| procedure check_for_volumes(drive : PStorage_device); | ||||
| var  | ||||
|     bootrecord : PMaster_Boot_Record; | ||||
|     storageVolume : TStorage_Volume; | ||||
|     i : uint32 = 0; | ||||
|     elm : void; | ||||
| begin | ||||
|     push_trace('VolumeManager.check_for_volumes'); | ||||
|  | ||||
|     bootrecord := PMaster_Boot_Record(kalloc(SizeOf(TMaster_Boot_Record))); | ||||
|     drive^.readCallback(drive, 0, 1, puint32(bootrecord)); //TODO multiple drives | ||||
|  | ||||
|     //TODO multipe partition entries | ||||
|     if bootrecord^.partition[0].LBA_start <> 0 then | ||||
|     begin | ||||
|       //set volume information | ||||
|       storageVolume.device := drive; | ||||
|       storageVolume.sectorStart := bootrecord^.partition[0].LBA_start; | ||||
|       storageVolume.sectorCount := bootrecord^.partition[0].sector_count; | ||||
|       storageVolume.sectorSize  := drive^.sectorSize;  | ||||
|       storageVolume.freeSectors := 0; //TODO impliment | ||||
|       storageVolume.filesystem := nil; | ||||
|  | ||||
|       if LL_Size(filesystems) < 1 then begin | ||||
|         console.writestringln('Failed to initalise storage system, no filesystems found, stopping!'); | ||||
|         exit; | ||||
|       end; | ||||
|  | ||||
|       //check for filesystem type | ||||
|       for i:=0 to LL_Size(filesystems) - 1 do begin | ||||
|         if bootrecord^.partition[0].system_id = PFilesystem(LL_Get(filesystems, i))^.system_id then | ||||
|         begin | ||||
|           storageVolume.filesystem := PFilesystem(LL_Get(filesystems, i)); | ||||
|         end; | ||||
|       end; | ||||
|     push_trace('VolumeManager.init3'); | ||||
|  | ||||
|       //add volume to list | ||||
|       elm := LL_Add(storageVolumes); | ||||
|       memcpy(uint32(@storageVolume), uint32(elm), SizeOf(TStorage_Volume)); | ||||
|             push_trace('VolumeManager.init4'); | ||||
|  | ||||
|       elm := LL_Add(drive^.volumes); | ||||
|       memcpy(uint32(@storageVolume), uint32(elm), SizeOf(TStorage_Volume)); | ||||
|  | ||||
|     end; | ||||
|      | ||||
| end; | ||||
|  | ||||
| procedure remove_volume(list : PLinkedListBase; volume : PStorage_Volume); | ||||
| var | ||||
|     i : uint32; | ||||
|     elm : void; | ||||
| begin | ||||
|  | ||||
|     for i:=0 to LL_Size(list) - 1 do begin | ||||
|         elm := LL_Get(list, i); | ||||
|         if (PStorage_Volume(elm)^.device = volume^.device)  | ||||
|         and (PStorage_Volume(elm)^.sectorStart = volume^.sectorStart) then begin | ||||
|             LL_Delete(list, i); | ||||
|             break; | ||||
|         end; | ||||
|     end; | ||||
| end; | ||||
|  | ||||
| procedure create_volume(disk : PStorage_device; filesystem : PChar; sectorStart : uint32; sectorCount : uint32); | ||||
| var | ||||
|     volume : PStorage_Volume; | ||||
|     elm : void; | ||||
|     i : uint16; | ||||
|     config : PuInt32; | ||||
| begin | ||||
|  | ||||
|   push_trace('VolumeManager.create_volume'); | ||||
|  | ||||
|   volume := PStorage_Volume(kalloc(SizeOf(TStorage_Volume))); | ||||
|   volume^.device := disk; | ||||
|   volume^.sectorStart := sectorStart; | ||||
|   volume^.sectorCount := sectorCount - 10;  | ||||
|   volume^.sectorSize := disk^.sectorSize; | ||||
|   volume^.freeSectors := 0; //setup by filesystem | ||||
|  | ||||
|   //find filesystem | ||||
|   for i:=0 to LL_Size(filesystems) - 1 do begin | ||||
|     if stringEquals(filesystem, PFilesystem(LL_Get(filesystems, i))^.sName) then begin | ||||
|       volume^.filesystem := PFilesystem(LL_Get(filesystems, i)); | ||||
|     end; | ||||
|   end; | ||||
|  | ||||
|   //TODO CHECK FILESYSTEM EXSISTS | ||||
|  | ||||
|   //format volume | ||||
|   volume^.filesystem^.createCallback(volume, sectorCount, sectorStart, config); | ||||
|  | ||||
|   push_trace('VolumeManager.create_volume.2'); | ||||
|  | ||||
|   //remove volume from list of volumes | ||||
|   remove_volume(storageVolumes, volume); | ||||
|  | ||||
|   //add volume to list | ||||
|   elm := LL_Add(storageVolumes); | ||||
|   memcpy(uint32(volume), uint32(elm), SizeOf(TStorage_Volume)); | ||||
|  | ||||
|   push_trace('VolumeManager.create_volume.3'); | ||||
|  | ||||
|   //remove volume from list of volumes on device | ||||
|   remove_volume(disk^.volumes, volume); | ||||
|  | ||||
|   //add volume to device | ||||
|   elm := LL_Add(disk^.volumes); | ||||
|   memcpy(uint32(volume), uint32(elm), SizeOf(TStorage_Volume)); | ||||
|  | ||||
|   //free memory | ||||
|   kfree(puint32(volume)); | ||||
| end; | ||||
|  | ||||
| procedure register_volume(device : PStorage_Device; volume : PStorage_Volume); | ||||
| var | ||||
|     elm : void; | ||||
| begin | ||||
|     push_trace('storagemanagement.register_volume'); | ||||
|     elm := LL_Add(device^.volumes); | ||||
|     PStorage_volume(elm)^:= volume^; | ||||
|  | ||||
|     // if rootVolume = PStorage_Volume(0) then rootVolume:= volume; | ||||
| end; | ||||
|  | ||||
| procedure openFile(Handle : uint32; Filename : PChar; OpenMode : TOpenMode; WriteMode : TWriteMode; lock : boolean; Error : PError); | ||||
| var | ||||
|     fileHandleEntree : PFilehandleEntry; | ||||
|     fileHandle : uInt32; | ||||
|     i : uint32; | ||||
|     elm : void; | ||||
| begin | ||||
|     push_trace('VolumeManager.openFile'); | ||||
|  | ||||
|     //check if file is already open and if it is locked, if openmode is not read only | ||||
|     if OpenMode <> omReadOnly then begin | ||||
|         for i:=0 to LL_Size(fileHandles) - 1 do begin | ||||
|             elm := LL_Get(fileHandles, i); | ||||
|             if stringEquals(PFilehandleEntry(elm)^.filename, Filename) then begin | ||||
|                 if PFilehandleEntry(elm)^.lock and (not PFilehandleEntry(elm)^.closed) then begin | ||||
|                     Error^ := eFileInUse; | ||||
|                     exit; | ||||
|                 end; | ||||
|             end; | ||||
|         end; | ||||
|     end; | ||||
|  | ||||
|     //create filehandleentry | ||||
|     fileHandleEntree := PFilehandleEntry(kalloc(SizeOf(TFilehandleEntry))); | ||||
|     fileHandleEntree^.fileName := Filename; | ||||
|     fileHandleEntree^.openMode := OpenMode; | ||||
|     fileHandleEntree^.writeMode := WriteMode; | ||||
|     fileHandleEntree^.lock := lock; | ||||
|     fileHandleEntree^.closed := false; | ||||
|  | ||||
|     //create filehandle | ||||
|     fileHandle := LL_Size(fileHandles); | ||||
|  | ||||
|  | ||||
| end; | ||||
|  | ||||
|  | ||||
| end. | ||||
| @@ -1,251 +0,0 @@ | ||||
| //  Copyright 2021 Aaron Hance | ||||
| // | ||||
| //  Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| //  you may not use this file except in compliance with the License. | ||||
| //  You may obtain a copy of the License at | ||||
| // | ||||
| //  http://www.apache.org/licenses/LICENSE-2.0 | ||||
| // | ||||
| //  Unless required by applicable law or agreed to in writing, software | ||||
| //  distributed under the License is distributed on an "AS IS" BASIS, | ||||
| //  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| //  See the License for the specific language governing permissions and | ||||
| //  limitations under the License. | ||||
|  | ||||
| {  | ||||
| 	Drivers->Storage->ATA - ATA Driver. | ||||
| 	 | ||||
| 	@author(Aaron Hance <ah@aaronhance.me>) | ||||
| } | ||||
| unit ata; | ||||
|  | ||||
| interface | ||||
|  | ||||
| uses | ||||
|     console, | ||||
|     drivermanagement, | ||||
|     drivertypes, | ||||
|     idetypes, | ||||
|     lmemorymanager, | ||||
|     storagetypes, | ||||
|     strings, | ||||
|     terminal, | ||||
|     tracer, | ||||
|     util, | ||||
|     vmemorymanager; | ||||
|  | ||||
| var | ||||
|  | ||||
|     test : uint32; | ||||
|  | ||||
|     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; | ||||
|  | ||||
|  | ||||
| implementation | ||||
| uses | ||||
|     ide; | ||||
|  | ||||
|  | ||||
|     procedure outb(port : uint16; value : uint8);  | ||||
|     begin | ||||
|         util.outb(port, value); | ||||
|         psleep(1); | ||||
|         // get_status(primaryDevices[0]); | ||||
|     end; | ||||
|  | ||||
| {  | ||||
|     ensure the address is a valid 28 bit address | ||||
|  | ||||
|     @param(addr The address to validate) | ||||
|     @returns(@True if the address is valid, @False otherwise) | ||||
| } | ||||
| function validate_28bit_address(addr : uint32) : boolean; | ||||
| begin | ||||
|     validate_28bit_address := (addr and $F0000000) = 0; | ||||
| end; | ||||
|  | ||||
| {  | ||||
|     Identify the device on the IDE bus | ||||
|  | ||||
|     @param(device The device to identify) | ||||
|     @returns(@True if the device is identified, @False otherwise) | ||||
| } | ||||
| function identify_device(var device : TIDE_Device) : Boolean; | ||||
| var | ||||
|     i : uint8; | ||||
|     status : TIDE_Status; | ||||
|     buffer : TIdentResponse; | ||||
|     ready : boolean; | ||||
| begin | ||||
|     select_device(device); | ||||
|     no_interrupt(device.isPrimary); | ||||
|  | ||||
|     status := get_status(device); | ||||
|  | ||||
|     outb(device.base + ATA_REG_SECCOUNT, 0); | ||||
|     outb(device.base + ATA_REG_LBA0, 0); | ||||
|     outb(device.base + ATA_REG_LBA1, 0); | ||||
|     outb(device.base + ATA_REG_LBA2, 0); | ||||
|  | ||||
|     outb(device.base + ATA_REG_COMMAND, ATA_CMD_IDENTIFY); | ||||
|  | ||||
|     status := get_status(device); | ||||
|  | ||||
|     if status.ERROR then begin | ||||
|         console.writestringln('Error identifying device, maybe'); //todo | ||||
|     end; | ||||
|  | ||||
|     ready := wait_for_device(device, false); | ||||
|  | ||||
|     if not ready then begin | ||||
|         console.writestringln('Device not ready in time!'); | ||||
|         //teapot time | ||||
|         // BSOD('Device not ready in time!', 'ATA DEVICE NOT READY IN TIME FOR IDENT'); | ||||
|         exit(false); | ||||
|     end; | ||||
|  | ||||
|     for i:=0 to 255 do begin | ||||
|         buffer[i] := inw(device.base + ATA_REG_DATA); | ||||
|     end; | ||||
|  | ||||
|     device.info := @buffer; | ||||
|  | ||||
|     identify_device := true; | ||||
| end; | ||||
|  | ||||
| procedure set_lba_mode(device : TIDE_Device; lba : uint32); | ||||
| begin | ||||
|     // if device.isPrimary then begin | ||||
|         if device.isMaster then begin | ||||
|             outb(device.base + ATA_REG_HDDEVSEL, $E0 or ((lba and $0F000000) shr 24)); | ||||
|         end else begin | ||||
|             outb(device.base + ATA_REG_HDDEVSEL, $F0 or ((lba and $0F000000) shr 24)); | ||||
|         end; | ||||
|     // end else begin //TODO  | ||||
|         // if device.isMaster then begin | ||||
|         //     outb(device.base + ATA_REG_HDDEVSEL, $A0); | ||||
|         // end else begin | ||||
|         //     outb(device.base + ATA_REG_HDDEVSEL, $B0); | ||||
|         // end; | ||||
|     // end; | ||||
| end; | ||||
|  | ||||
| function read_pio28(device : TIDE_Device; lba : uint32; count : uint8; buffer : puint16) : boolean; | ||||
| var | ||||
|     i : uint16; | ||||
|     ii : uint16; | ||||
|     status : TIDE_Status; | ||||
|     ready : boolean; | ||||
| begin | ||||
|     push_trace('ata.read_pio28()'); | ||||
|  | ||||
|     if not validate_28bit_address(lba) then begin | ||||
|         console.writestringln('Invalid address for 28 bit read'); | ||||
|         read_pio28 := false; | ||||
|         exit; | ||||
|     end; | ||||
|  | ||||
|     select_device(device); | ||||
|     psleep(50); | ||||
|     no_interrupt(device.isPrimary); | ||||
|     psleep(50); | ||||
|     set_lba_mode(device, lba); | ||||
|     psleep(50); | ||||
|  | ||||
|     outb(device.base + ATA_REG_SECCOUNT, count); | ||||
|     psleep(50); | ||||
|  | ||||
|     outb(device.base + ATA_REG_LBA0, (lba and $000000FF)); | ||||
|     psleep(50); | ||||
|     outb(device.base + ATA_REG_LBA1, (lba and $0000FF00) shr 8); | ||||
|     psleep(50); | ||||
|     outb(device.base + ATA_REG_LBA2, (lba and $00FF0000) shr 16); | ||||
|     psleep(50); | ||||
|  | ||||
|     outb(device.base + ATA_REG_COMMAND, ATA_CMD_READ_PIO); | ||||
|     psleep(50); | ||||
|  | ||||
|     ready := wait_for_device(device, false); | ||||
|  | ||||
|     if not ready then begin | ||||
|         console.writestringln('Device not ready in time!'); | ||||
|         BSOD('Device not ready in time!', 'ATA DEVICE NOT READY IN TIME FOR READ COMMAND'); | ||||
|     end; | ||||
|  | ||||
|     for i:=0 to count-1 do begin | ||||
|         ii:=0; | ||||
|         while ii < 256 do begin | ||||
|             buffer[ii+(i*256)] := inw(device.base + ATA_REG_DATA); | ||||
|             ii := ii + 1; | ||||
|             psleep(50); | ||||
|         end; | ||||
|     end; | ||||
|  | ||||
|     read_pio28 := true; | ||||
|     pop_trace(); | ||||
| end; | ||||
|  | ||||
| function write_pio28(device : TIDE_Device; lba : uint32; count : uint8; buffer : puint16) : boolean; | ||||
| var | ||||
|     i : uint16; | ||||
|     ii : uint16; | ||||
|     status : TIDE_Status; | ||||
|     ready : boolean; | ||||
| begin | ||||
|     push_trace('ata.write_pio28()'); | ||||
|  | ||||
|     if not validate_28bit_address(lba) then begin | ||||
|         console.writestringln('Invalid address for 28 bit write'); | ||||
|         write_pio28 := false; | ||||
|         exit; | ||||
|     end; | ||||
|  | ||||
|     select_device(device); | ||||
|     no_interrupt(device.isPrimary); //maybe not? | ||||
|     set_lba_mode(device, lba); | ||||
|  | ||||
|  | ||||
|     outb(device.base + ATA_REG_SECCOUNT, count); | ||||
|     outb(device.base + ATA_REG_LBA0, (lba and $000000FF)); | ||||
|     outb(device.base + ATA_REG_LBA1, (lba and $0000FF00) shr 8); | ||||
|     outb(device.base + ATA_REG_LBA2, (lba and $00FF0000) shr 16); | ||||
|     outb(device.base + ATA_REG_COMMAND, ATA_CMD_WRITE_PIO); | ||||
|  | ||||
|     ready := wait_for_device(device, false); | ||||
|  | ||||
|     if not ready then begin | ||||
|         console.writestringln('Device not ready in time!'); | ||||
|         BSOD('Device not ready in time!', 'ATA DEVICE NOT READY IN TIME FOR WRITE COMMAND'); | ||||
|     end; | ||||
|  | ||||
|     for i:=0 to count-1 do begin | ||||
|         ii:=0; | ||||
|         while ii < 256 do begin | ||||
|             outw(ATA_PRIMARY_BASE + ATA_REG_DATA, buffer[ii+(i*256)]); | ||||
|             ii := ii + 1; | ||||
|             psleep(50); | ||||
|         end; | ||||
|  | ||||
|             psleep(50); //todo check | ||||
|             outb(device.base + ATA_REG_COMMAND, ATA_CMD_CACHE_FLUSH); | ||||
|             ready := wait_for_device(device, false); | ||||
|  | ||||
|         if not ready then begin | ||||
|             console.writestringln('Device not ready in time!'); | ||||
|         BSOD('Device not ready in time!', 'ATA DEVICE NOT READY IN TIME FOR WRITE'); | ||||
|     end; | ||||
|     end; | ||||
|  | ||||
|  | ||||
|     // if not ready then begin | ||||
|     //     console.writestringln('Device not ready in time!'); | ||||
|     //     BSOD('Device not ready in time!', 'ATA DEVICE NOT READY IN TIME FOR CACHE FLUSH'); | ||||
|     // end; | ||||
|  | ||||
|     write_pio28 := true; | ||||
|     pop_trace(); | ||||
| end; | ||||
|  | ||||
| end. | ||||
| @@ -1,448 +0,0 @@ | ||||
| //  Copyright 2021 Aaron Hance | ||||
| // | ||||
| //  Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| //  you may not use this file except in compliance with the License. | ||||
| //  You may obtain a copy of the License at | ||||
| // | ||||
| //  http://www.apache.org/licenses/LICENSE-2.0 | ||||
| // | ||||
| //  Unless required by applicable law or agreed to in writing, software | ||||
| //  distributed under the License is distributed on an "AS IS" BASIS, | ||||
| //  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| //  See the License for the specific language governing permissions and | ||||
| //  limitations under the License. | ||||
|  | ||||
| {  | ||||
| 	Drivers->Storage->ATAPI - ATAPI Driver. | ||||
|  | ||||
|     !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! | ||||
|     !!!!!!!!!!!!!!!!!!!!!! CURRENTLY NOT FUNCTIONAL !!!!!!!!!!!!!!!!!!!!!! | ||||
|     !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! | ||||
| 	 | ||||
| 	@author(Aaron Hance <ah@aaronhance.me>) | ||||
| } | ||||
|  | ||||
| unit atapi; | ||||
|  | ||||
| interface | ||||
|  | ||||
| uses | ||||
|     console, | ||||
|     drivermanagement, | ||||
|     drivertypes, | ||||
|     idetypes, | ||||
|     lmemorymanager, | ||||
|     storagetypes, | ||||
|     strings, | ||||
|     terminal, | ||||
|     tracer, | ||||
|     util, | ||||
|     vmemorymanager; | ||||
|  | ||||
| var | ||||
|  | ||||
|     test : uint32; | ||||
|  | ||||
|     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(var device : TIDE_Device) : uint32; | ||||
|     procedure ide_irq(); | ||||
|  | ||||
|  | ||||
| implementation | ||||
| uses | ||||
|     ata, | ||||
|     ide; | ||||
|  | ||||
|  | ||||
| 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); | ||||
|     psleep(1); | ||||
|     // get_status(primaryDevices[0]); | ||||
| end; | ||||
|  | ||||
| {  | ||||
|     Identify the device on the IDE bus | ||||
|  | ||||
|     @param(device The device to identify) | ||||
|     @returns(@True if the device is identified, @False otherwise) | ||||
| } | ||||
| function identify_device(var device : TIDE_Device) : Boolean; | ||||
| var | ||||
|     i : uint8; | ||||
|     status : TIDE_Status; | ||||
|     buffer : TIdentResponse; | ||||
|     ready : boolean; | ||||
| begin | ||||
|     select_device(device); | ||||
|     no_interrupt(device.isPrimary); | ||||
|  | ||||
|     // outb(ATA_PRIMARY_BASE1, $04);  | ||||
|     // outb(ATA_SECONDARY_BASE1, $04); | ||||
|     // sleep(1); | ||||
|  | ||||
|     status := get_status(device); | ||||
|  | ||||
|     outb(device.base + ATA_REG_SECCOUNT, 0); | ||||
|     outb(device.base + ATA_REG_LBA0, 0); | ||||
|     outb(device.base + ATA_REG_LBA1, 0); | ||||
|     outb(device.base + ATA_REG_LBA2, 0); | ||||
|  | ||||
|     psleep(1); | ||||
|  | ||||
|     outb(device.base + ATA_REG_COMMAND, ATA_CMD_IDENTIFY_PACKET); | ||||
|  | ||||
|     status := get_status(device); | ||||
|  | ||||
|     if status.ERROR then begin | ||||
|         console.writestringln('Error identifying device, maybe'); //todo | ||||
|     end; | ||||
|  | ||||
|     ready := wait_for_device(device, false); | ||||
|  | ||||
|     if not ready then begin | ||||
|         console.writestringln('Device not ready in time!'); | ||||
|         //teapot time | ||||
|         // BSOD('Device not ready in time!', 'ATA DEVICE NOT READY IN TIME FOR IDENT'); | ||||
|         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; | ||||
| end; | ||||
|  | ||||
| function read_PIO28(device : TIDE_Device; lba : uint32; count : uint8; buffer : puint16) : boolean; | ||||
| var | ||||
|     i, j : uint16; | ||||
|     status : TIDE_Status; | ||||
|     ready : boolean; | ||||
|     packet : array[0..11] of uint8; | ||||
| begin | ||||
|     push_trace('read_PIO28()'); | ||||
|  | ||||
|     if not device.isATAPI then begin | ||||
|         console.writestringln('[ATAPI] Device is not an ATAPI device!'); | ||||
|         read_PIO28 := false; | ||||
|         exit; | ||||
|     end; | ||||
|  | ||||
|     select_device(device); | ||||
|     no_interrupt(device.isPrimary); | ||||
|  | ||||
|     // Step 1: Send PACKET command | ||||
|     outb(device.base + ATA_REG_FEATURES, 0);     // No special features | ||||
|     outb(device.base + ATA_REG_SECCOUNT, 0);     // ATAPI requires this to be 0 | ||||
|     outb(device.base + ATA_REG_LBA0, (2048 shr 0) and $FF);  // Byte size of transfer (2048 for CDs) | ||||
|     outb(device.base + ATA_REG_LBA1, (2048 shr 8) and $FF); | ||||
|     outb(device.base + ATA_REG_LBA2, 0);         // Reserved | ||||
|     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, false); | ||||
|     if not ready then begin | ||||
|         console.writestringln('[ATAPI] Timeout waiting for PACKET DRQ!'); | ||||
|         read_PIO28 := false; | ||||
|         exit; | ||||
|     end; | ||||
|  | ||||
|     // Step 3: Prepare 12-byte SCSI CDB (Read Sectors Command 0xA8) | ||||
|     packet[0] := $A8;  // ATAPI READ (10) command | ||||
|     packet[1] := 0;    // Reserved | ||||
|     packet[2] := (lba shr 24) and $FF; // LBA high byte | ||||
|     packet[3] := (lba shr 16) and $FF; | ||||
|     packet[4] := (lba shr 8) and $FF; | ||||
|     packet[5] := (lba shr 0) and $FF; | ||||
|     packet[6] := 0;    // Reserved | ||||
|     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 6: Read the data (count * 2048 bytes) | ||||
|     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; | ||||
|  | ||||
|     // Success | ||||
|     read_PIO28 := true; | ||||
|     pop_trace(); | ||||
| end; | ||||
|  | ||||
|  | ||||
| function write_pio28(device : TIDE_Device; lba : uint32; count : uint8; buffer : puint16) : boolean; | ||||
| var | ||||
|     i : uint16; | ||||
|     ii : uint16; | ||||
|     status : TIDE_Status; | ||||
|     ready : boolean; | ||||
| begin | ||||
|     write_pio28 := false; | ||||
| end; | ||||
|  | ||||
| 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); | ||||
|  | ||||
|   // 3) Indicate how many bytes we expect from the drive. | ||||
|   //    Usually we place "MaxByteCount" in LBA1:LBA2. | ||||
|   //    We need at least 8 bytes for READ CAPACITY(10). | ||||
|   //    We'll do 8 => low byte in LBA1, high byte in LBA2. | ||||
|   outb(device.base + ATA_REG_LBA1, $08); | ||||
|   outb(device.base + ATA_REG_LBA2, $00); | ||||
|  | ||||
|   // 4) Send the "PACKET" command (0xA0) to start an ATAPI packet transfer | ||||
|   outb(device.base + ATA_REG_COMMAND, ATA_CMD_PACKET); | ||||
|  | ||||
|   // 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(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 | ||||
|   // We'll store it as little-endian 16-bit words: | ||||
|   //   packet[0] = 0x0025 means low byte=0x25 (opcode), high byte=0x00 | ||||
|   packet[0] := $0025; // cdb[0..1] | ||||
|   packet[1] := 0;     // cdb[2..3] | ||||
|   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); | ||||
|   if not ready then | ||||
|   begin | ||||
|     console.writestringln('Device not ready to receive READ CAPACITY(10) CDB.'); | ||||
|     exit; | ||||
|   end; | ||||
|  | ||||
|   // 7) Write our 5 words (10 bytes) of CDB to the drive | ||||
|   for i := 0 to 5 do | ||||
|   begin | ||||
|     outw(device.base + ATA_REG_DATA, packet[i]); | ||||
|     psleep(1); // short delay | ||||
|   end; | ||||
|   // Free the CDB memory | ||||
| //   kfree(uint32(packet)); | ||||
|  | ||||
|   // 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.'); | ||||
|     exit; | ||||
|   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; | ||||
|  | ||||
|   // 10) Allocate space for the 8-byte result | ||||
|   response := puint16(kalloc(8)); | ||||
|   memset(uint32(response), 0, 8); | ||||
|  | ||||
|   // We'll read 4 words = 8 bytes from the data register | ||||
|   for i := 0 to 3 do | ||||
|   begin | ||||
|     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.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)[0]; | ||||
| end; | ||||
|  | ||||
|  | ||||
|  | ||||
| end. | ||||
| @@ -10,16 +10,14 @@ interface | ||||
|  | ||||
| uses | ||||
|     console, | ||||
|     util, | ||||
|     terminal, | ||||
|     storagemanagement, | ||||
|     util, terminal, | ||||
|     lmemorymanager, | ||||
|     strings, | ||||
|     lists, | ||||
|     tracer, | ||||
|     serial, | ||||
|     rtc, | ||||
|     volumemanager, | ||||
|     storagetypes; | ||||
|     rtc; | ||||
|  | ||||
| type  | ||||
|  | ||||
| @@ -91,7 +89,7 @@ var | ||||
|     filesystem : TFilesystem; | ||||
|  | ||||
| procedure init; | ||||
| procedure create_volume(volume : PStorage_Volume; sectors : uint32; start : uint32; config : puint32); | ||||
| procedure create_volume(disk : PStorage_Device; sectors : uint32; start : uint32; config : puint32); | ||||
| procedure detect_volumes(disk : PStorage_Device); | ||||
| //function writeDirectory(volume : PStorage_volume; directory : pchar; attributes : uint32) : uint8; // need to handle parent table cluster overflow, need to take attributes | ||||
| //function readDirectory(volume : PStorage_volume; directory : pchar; listPtr : PLinkedListBase) : uint8; //returns: 0 = success, 1 = dir not exsist, 2 = not directory, 3 = error | ||||
| @@ -753,7 +751,7 @@ end; | ||||
|  | ||||
| //TODO check directory commands for errors with a clean disk | ||||
|  | ||||
| procedure create_volume(volume : PStorage_Volume; sectors : uint32; start : uint32; config : puint32); | ||||
| procedure create_volume(disk : PStorage_Device; sectors : uint32; start : uint32; config : puint32); | ||||
| var | ||||
|     buffer     : puint32; | ||||
|     bt: puint32; | ||||
| @@ -774,18 +772,9 @@ var | ||||
|     programFileArray   : byteArray8 = ('P','R','O','G','R','A','M','S'); | ||||
|     rootCluster   : uint32 = 1; | ||||
|  | ||||
|     sysArray : byteArray8 = ('S','Y','S','T','E','M',' ',' '); | ||||
|     progArray : byteArray8 = ('P','R','O','G','R','A','M','S'); | ||||
|     userArray : byteArray8 = ('U','S','E','R',' ',' ',' ',' '); | ||||
|  | ||||
|     status : puint32; | ||||
|     disk : PStorage_device; | ||||
|  | ||||
| begin //maybe increase buffer size by one? | ||||
|     push_trace('fat32.create_volume()'); | ||||
|  | ||||
|     disk := volume^.device; | ||||
|  | ||||
|     // zeroBuffer:= puint32(kalloc( disk^.sectorSize * 4 )); | ||||
|     // memset(uint32(zeroBuffer), 0, disk^.sectorSize * 4); | ||||
|  | ||||
| @@ -822,7 +811,7 @@ begin //maybe increase buffer size by one? | ||||
|     bootRecord^.jmp2boot        := $0; //TODO impliment boot jump | ||||
|     bootRecord^.OEMName         := asuroArray; | ||||
|     bootRecord^.sectorSize      := disk^.sectorsize; | ||||
|     bootRecord^.spc             := 1; | ||||
|     bootRecord^.spc             := config^; | ||||
|     bootRecord^.rsvSectors      := 32; //32 is standard | ||||
|     bootRecord^.numFats         := 1; | ||||
|     bootRecord^.mediaDescp      := $F8; | ||||
| @@ -884,16 +873,8 @@ begin //maybe increase buffer size by one? | ||||
|      | ||||
|     disk^.writecallback(disk, dataStart + (config^ * rootCluster), 1, buffer); | ||||
|  | ||||
|     memset(uint32(buffer), 0, disk^.sectorsize); | ||||
|  | ||||
|     status := puint32(kalloc(sizeof(uint32))); | ||||
|     writeDirectory(volume, '', sysArray, $10, status, ''); | ||||
|  | ||||
|  | ||||
|     kfree(buffer); | ||||
|  | ||||
|  | ||||
|  | ||||
| end; | ||||
|  | ||||
| procedure detect_volumes(disk : PStorage_Device); | ||||
| @@ -922,11 +903,11 @@ begin | ||||
|     if (puint32(buffer)[127] = $55AA) and (PBootRecord(buffer)^.bsignature = $29) then begin //TODO partition table | ||||
|         console.writestringln('FAT32: volume found!'); | ||||
|         volume^.device:= disk; | ||||
|         volume^.sectorStart:= 1; //THIS MAKE SURE IF NOT FUCKY, cos im getting info from 2 | ||||
|         volume^.sectorStart:= 1; | ||||
|         volume^.sectorSize:= PBootRecord(buffer)^.sectorSize; | ||||
|         volume^.freeSectors:= 1000000; //TODO implement get free sectors need FSINFO implemented first | ||||
|         volume^.filesystem := @filesystem; | ||||
|         // storagemanagement.register_volume(disk, volume); TODO repalce with new thing | ||||
|         storagemanagement.register_volume(disk, volume); | ||||
|     end; | ||||
|  | ||||
|     kfree(buffer); | ||||
| @@ -936,7 +917,6 @@ procedure init(); | ||||
| begin | ||||
|     push_trace('fat32.init()'); | ||||
|     filesystem.sName:= 'FAT32';  | ||||
|     filesystem.system_id:= $01;  | ||||
|     filesystem.readDirCallback:= @readDirectoryGen; | ||||
|     filesystem.createDirCallback:= @writeDirectoryGen; | ||||
|     filesystem.createcallback:= @create_volume; | ||||
| @@ -944,7 +924,7 @@ begin | ||||
|     filesystem.writecallback:= @writeFile; | ||||
|     filesystem.readcallback := @readFile; | ||||
|  | ||||
|     volumemanager.register_filesystem(@filesystem); | ||||
|     storagemanagement.register_filesystem(@filesystem); | ||||
| end; | ||||
|  | ||||
| end. | ||||
| @@ -1,725 +0,0 @@ | ||||
| //  Copyright 2021 Aaron Hance | ||||
| // | ||||
| //  Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| //  you may not use this file except in compliance with the License. | ||||
| //  You may obtain a copy of the License at | ||||
| // | ||||
| //  http://www.apache.org/licenses/LICENSE-2.0 | ||||
| // | ||||
| //  Unless required by applicable law or agreed to in writing, software | ||||
| //  distributed under the License is distributed on an "AS IS" BASIS, | ||||
| //  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| //  See the License for the specific language governing permissions and | ||||
| //  limitations under the License. | ||||
|  | ||||
| { | ||||
|     Driver->Storage->flatfs super simple flat filesystem | ||||
|  | ||||
|     @author(Aaron Hance ah@aaronhance.me) | ||||
| } | ||||
|  | ||||
| unit flatfs; | ||||
|  | ||||
| interface | ||||
|  | ||||
| uses | ||||
|     tracer, | ||||
|     strings, | ||||
|     volumemanager, | ||||
|     lists, | ||||
|     console, | ||||
|     terminal, | ||||
|     storagetypes, | ||||
|     lmemorymanager, | ||||
|     util; | ||||
|  | ||||
| type | ||||
|  | ||||
|     TDisk_Info = bitpacked record | ||||
|         jmp2boot: ubit24; | ||||
|         OEMName: array[0..7] of char; | ||||
|         version: uint16; | ||||
|         sectorCount : uint16; | ||||
|         fileCount : uint16; | ||||
|         signature : uint32; | ||||
|     end; | ||||
|     PDisk_Info = ^TDisk_Info; | ||||
|  | ||||
|     TFile_Entry = bitpacked record | ||||
|         attribues : uint8; // 0x00 = does not exsist, 0x01 = file, 0x02 = hidden, 0x04 = system/read only, 0x10 = directory, 0x20 = archive | ||||
|         name: array[0..53] of char; //max file name length is 54 | ||||
|         rsv : uint8; | ||||
|         size: uint32; | ||||
|         start : uint32; | ||||
|     end; | ||||
|     PFile_Entry = ^TFile_Entry; | ||||
|  | ||||
| var | ||||
|     filesystem : TFilesystem; | ||||
|  | ||||
|  | ||||
| procedure init; | ||||
| procedure create_volume(volume : PStorage_Volume; sectors : uint32; start : uint32; config : puint32); | ||||
| procedure detect_volumes(disk : PStorage_Device); | ||||
| function read_directory(volume : PStorage_Volume; directory : pchar; status : PuInt32) : PLinkedListBase; | ||||
| procedure write_directory(volume : PStorage_Volume; directory : pchar; status : PuInt32); | ||||
| procedure write_file(volume : PStorage_Volume; fileName : pchar; data : PuInt32; size : uint32; status : PuInt32); | ||||
| procedure read_file(volume : PStorage_Volume; fileName : pchar; data : PuInt32; status : PuInt32); | ||||
|  | ||||
| implementation | ||||
|  | ||||
| procedure create_volume(volume : PStorage_Volume; sectors : uint32; start : uint32; config : puint32); | ||||
| var | ||||
|     info : PDisk_Info; | ||||
|     entryTable : PFile_Entry; | ||||
|     i : uint32; | ||||
|  | ||||
|     directories : PLinkedListBase; | ||||
|     status : PuInt32; | ||||
|  | ||||
|     data : pchar; | ||||
|  | ||||
| const | ||||
|     asuroArray : array[0..7] of char = 'ASURO'; | ||||
|     systemArray : array[0..6] of char = '/system'; | ||||
|     programsArray : array[0..8] of char = '/programs'; | ||||
|     userArray : array[0..4] of char = '/user'; | ||||
|  | ||||
|     testArray : array[0..23] of char = 'ASURO TEST DATA ARRAY'; | ||||
| begin | ||||
|  | ||||
|     info := PDisk_Info(Kalloc(512)); | ||||
|     memset(uint32(info), 0, 512); | ||||
|  | ||||
|     info^.jmp2boot := $0; | ||||
|     info^.OEMName := asuroArray; | ||||
|     info^.version := 1; | ||||
|     info^.sectorCount := sectors; | ||||
|     info^.fileCount := 1000; | ||||
|     info^.signature := $0B00B1E5; | ||||
|  | ||||
|     if config^ <> 0 then begin | ||||
|         info^.fileCount := config^; | ||||
|     end; | ||||
|  | ||||
|     volume^.device^.writeCallback(volume^.device, start, 1, PuInt32(info));// what happens if buffer is smaller than 512? | ||||
|      | ||||
|     // create file table | ||||
|     entryTable := PFile_Entry(Kalloc(126 * 512)); | ||||
|     memset(uint32(entryTable), 0, 126 * 512); | ||||
|  | ||||
|     //create system folders | ||||
|     entryTable[0].attribues := $10; | ||||
|     memcpy(uint32(@systemArray), uint32(@entryTable[0].name), 7); | ||||
|     entryTable[0].size := 0; | ||||
|     entryTable[0].start := 0; | ||||
|  | ||||
|     entryTable[1].attribues := $10; | ||||
|     memcpy(uint32(@programsArray), uint32(@entryTable[1].name), 9); | ||||
|     entryTable[1].size := 0; | ||||
|     entryTable[1].start := 0; | ||||
|  | ||||
|     entryTable[2].attribues := $10; | ||||
|     memcpy(uint32(@userArray), uint32(@entryTable[2].name), 5); | ||||
|     entryTable[2].size := 0; | ||||
|     entryTable[2].start := 0; | ||||
|  | ||||
|     //write file table | ||||
|     volume^.device^.writeCallback(volume^.device, start + 1, ((sizeof(TFile_Entry) * info^.fileCount) div 512), puint32(entryTable)); | ||||
|     //volume^.device^.writeCallback(volume^.device, start + 1, 1, puint32(entryTable)); | ||||
|  | ||||
|     //temp read | ||||
|     status := PuInt32(Kalloc(sizeof(uint32))); | ||||
|     // directories := read_directory(volume, '/', status); | ||||
|  | ||||
|     data := stringNew(1024); | ||||
|     memset(uint32(data), 0, 513); | ||||
|     memcpy(uint32(@testArray), uint32(data), 23); | ||||
|      | ||||
|     write_directory(volume, '/logs', status); | ||||
|     write_directory(volume, '/logs/test', status); | ||||
|     write_file(volume, '/logs/log.txt', PuInt32(data), 1, status); | ||||
|     write_file(volume, '/logs/log2.txt', PuInt32(data), 1, status); | ||||
|     write_file(volume, '/logs/log3.txt', PuInt32(data), 1, status); | ||||
|     read_directory(volume, '/logs', status); | ||||
|  | ||||
|     kfree(puint32(data)); | ||||
|  | ||||
|     read_file(volume, '/logs/log.txt', PuInt32(data), status); | ||||
|     writestringlnWND(pchar(data), getterminalhwnd()); | ||||
| end; | ||||
|  | ||||
| procedure write_directory(volume : PStorage_Volume; directory : pchar; status : PuInt32); | ||||
| var | ||||
|     directories : PLinkedListBase; | ||||
|     i : uint32; | ||||
|  | ||||
|     fileEntries : PFile_Entry; | ||||
|  | ||||
|     fileEntry : PFile_Entry; | ||||
|     buffer : PuInt32; | ||||
|  | ||||
|     position : uint32; | ||||
|     offset : uint32; | ||||
|  | ||||
| const | ||||
|     fileCount : uint32 = 1000; | ||||
| begin | ||||
|     status^ := 0; | ||||
|  | ||||
|     //directories := read_directory(volume, directory, status); | ||||
|  | ||||
|     fileEntries := PFile_Entry(Kalloc(sizeof(TFile_Entry) * fileCount + 512)); | ||||
|     memset(uint32(fileEntries), 0, sizeof(TFile_Entry) * fileCount + 512); | ||||
|  | ||||
|     volume^.device^.readCallback(volume^.device, volume^.sectorStart + 1, (sizeof(TFile_Entry) * fileCount) div 512, PuInt32(fileEntries)); | ||||
|  | ||||
|     if status^ = 0 then begin | ||||
|         for i := 0 to fileCount - 1 do begin | ||||
|  | ||||
|             push_trace('write_directory_2'); | ||||
|  | ||||
|             //fileEntry := PFile_Entry(STRLL_Get(directories, i)); | ||||
|             fileEntry := @fileEntries[i]; | ||||
|  | ||||
|             if fileEntry^.attribues = 0 then begin | ||||
|                 fileEntry^.attribues := $10; | ||||
|                 fileEntry^.size := 0; | ||||
|                 fileEntry^.start := 0; | ||||
|                 memcpy(uint32(directory), uint32(@fileEntry^.name), stringSize(directory)); | ||||
|  | ||||
|                 push_trace('write_directory_3'); | ||||
|  | ||||
|                 //write file table | ||||
|                 buffer := PuInt32(Kalloc(513)); | ||||
|                 memset(uint32(buffer), 0, 512); | ||||
|  | ||||
|                 push_trace('write_directory.b4_read'); | ||||
|  | ||||
|                 //read file table cointaining entry of interest | ||||
|                 position := (i * sizeof(TFile_Entry)) div 512; | ||||
|                 volume^.device^.readCallback(volume^.device, volume^.sectorStart + 1 + position, 1, buffer); | ||||
|  | ||||
|                 push_trace('write_directory.after_read'); | ||||
|  | ||||
|                 //calculate entry offset into sector | ||||
|                 offset := (i * sizeof(TFile_Entry)) mod 512; | ||||
|                 offset := offset div sizeof(TFile_Entry); | ||||
|  | ||||
|                 //set entry in buffer | ||||
|                 PFile_Entry(buffer)[offset] := fileEntry^; | ||||
|  | ||||
|                 //write updated file table sector | ||||
|                 volume^.device^.writeCallback(volume^.device, volume^.sectorStart + 1 + position, 1, buffer); | ||||
|  | ||||
|                 Kfree(buffer); | ||||
|  | ||||
|                 push_trace('write_directory.after_write'); | ||||
|  | ||||
|                 break; | ||||
|  | ||||
|             end; | ||||
|         end; | ||||
|     end; | ||||
|  | ||||
|     if fileEntry^.attribues = 0 then begin | ||||
|         status^ := 1; | ||||
|     end; | ||||
|  | ||||
| end; | ||||
|  | ||||
| function read_directory(volume : PStorage_Volume; directory : pchar; status : PuInt32) : PLinkedListBase; | ||||
| var | ||||
|     directories : PLinkedListBase; | ||||
|     fileEntrys : PFile_Entry; | ||||
|     i : uint32; | ||||
|  | ||||
|     compString : PChar; | ||||
|     afterString : PChar; | ||||
|     slash : PChar; | ||||
|     compStringLength : uint32; | ||||
|  | ||||
|     fileCount : uint32 = 1000; | ||||
|  | ||||
|     slashArray : array[0..1] of char = '/'; | ||||
|  | ||||
| begin | ||||
|     compString := pchar(kalloc(60)); | ||||
|     memset(uint32(compString), 0, 60); | ||||
|  | ||||
|     afterString := pchar(kalloc(60)); | ||||
|     memset(uint32(afterString), 0, 60); | ||||
|  | ||||
|     slash := stringNew(2); | ||||
|     memcpy(uint32(@slashArray), uint32(slash), 1); | ||||
|  | ||||
|     directories := STRLL_New(); | ||||
|  | ||||
|     fileEntrys := PFile_Entry(Kalloc(sizeof(TFile_Entry) * fileCount + 512)); | ||||
|     volume^.device^.readCallback(volume^.device, volume^.sectorStart + 1, sizeof(TFile_Entry) * fileCount div 512, puint32(fileEntrys)); | ||||
|  | ||||
|     for i := 0 to fileCount - 1 do begin | ||||
|  | ||||
|             if uint32(fileEntrys[i].name[0]) = 0 then begin | ||||
|                 break; | ||||
|             end; | ||||
|  | ||||
|              | ||||
|  | ||||
|             compString := stringSub(fileEntrys[i].name, 0, stringSize(directory)); | ||||
|             afterString := stringSub(fileEntrys[i].name, stringSize(directory), stringSize(fileEntrys[i].name)-1); | ||||
|  | ||||
|             writestringlnWND(afterString, getterminalhwnd()); | ||||
|  | ||||
|             push_trace('read_directory_2'); | ||||
|  | ||||
|             if stringEquals(compString, directory) and  | ||||
|                 (not stringContains(afterString, slash)) then begin | ||||
|  | ||||
|                 writestringlnWND(fileEntrys[i].name, getterminalhwnd()); | ||||
|                 STRLL_Add(directories, afterString); | ||||
|             end; | ||||
|     end; | ||||
|  | ||||
|     push_trace('read_directory_3'); | ||||
|  | ||||
|     Kfree(puint32(fileEntrys)); | ||||
|  | ||||
|     read_directory := directories; | ||||
| end; | ||||
|  | ||||
| procedure quicksort_start(list : PFile_Entry; begining : uint32; stop : uint32); | ||||
| var | ||||
|     pivot : uint32; | ||||
|     i : uint32; | ||||
|     j : uint32; | ||||
|     temp : PLinkedListBase; | ||||
|  | ||||
|     tempFileEntry : PFile_Entry; | ||||
| begin | ||||
|  | ||||
|     tempFileEntry := PFile_Entry(Kalloc(sizeof(TFile_Entry))); | ||||
|     memset(uint32(tempFileEntry), 0, sizeof(TFile_Entry)); | ||||
|  | ||||
|     i:=begining; | ||||
|     j:=stop; | ||||
|  | ||||
|     pivot := list[(begining + stop) div 2].start; | ||||
|  | ||||
|     while i <= j do begin | ||||
|         while list[i].start < pivot do begin | ||||
|             i := i + 1; | ||||
|         end; | ||||
|  | ||||
|         while list[j].start > pivot do begin | ||||
|             j := j - 1; | ||||
|         end; | ||||
|  | ||||
|         if i <= j then begin | ||||
|             tempFileEntry^ := list[i]; | ||||
|             list[i] := list[j]; | ||||
|             list[j] := tempFileEntry^; | ||||
|  | ||||
|             i := i + 1; | ||||
|             j := j - 1; | ||||
|         end; | ||||
|     end; | ||||
|  | ||||
|     if begining < j then begin | ||||
|         quicksort_start(list, begining, j); | ||||
|     end; | ||||
|  | ||||
|     if i < stop then begin | ||||
|         quicksort_start(list, i, stop); | ||||
|     end; | ||||
|  | ||||
|     Kfree(puint32(tempFileEntry)); | ||||
|  | ||||
| end; | ||||
|  | ||||
| function find_free_space(volume : PStorage_Volume; size : uint32; status : PuInt32) : uint32; | ||||
| var | ||||
|     fileEntrys : PFile_Entry; | ||||
|     confirmedFileEntrys : PFile_Entry; | ||||
|     confirmedFileEntrysCount : uint32 = 0; | ||||
|     fileEntry : PFile_Entry; | ||||
|     i : uint32; | ||||
|     fileCount : uint32 = 1000; | ||||
|  | ||||
|     baseAddress : uint32; | ||||
| begin | ||||
|     fileEntrys := PFile_Entry(Kalloc(sizeof(TFile_Entry) * fileCount + 512)); | ||||
|     confirmedFileEntrys := PFile_Entry(Kalloc(sizeof(TFile_Entry) * fileCount + 512)); | ||||
|     memset(uint32(fileEntrys), 0, sizeof(TFile_Entry) * fileCount + 512); | ||||
|     memset(uint32(confirmedFileEntrys), 0, sizeof(TFile_Entry) * fileCount + 512); | ||||
|  | ||||
|     volume^.device^.readCallback(volume^.device, volume^.sectorStart + 1, sizeof(TFile_Entry) * fileCount div 512, puint32(fileEntrys)); | ||||
|  | ||||
|     baseAddress := volume^.sectorStart + 2 + ( (fileCount * sizeof(TFile_Entry)) div 512); | ||||
|  | ||||
|     for i := 0 to fileCount - 1 do begin | ||||
|         if fileEntrys[i].attribues <> 0 then begin | ||||
|             confirmedFileEntrys[confirmedFileEntrysCount] := fileEntrys[i]; | ||||
|             confirmedFileEntrysCount := confirmedFileEntrysCount + 1; | ||||
|         end; | ||||
|     end; | ||||
|  | ||||
|     //sort file table by start address using quicksort | ||||
|     quicksort_start(confirmedFileEntrys, 0, confirmedFileEntrysCount - 1); | ||||
|  | ||||
|     //find free space big enough to fit size //TODO: make this work | ||||
|     for i := 0 to confirmedFileEntrysCount - 1 do begin | ||||
|  | ||||
|         if confirmedFileEntrys[i+1].attribues = 0 then begin | ||||
|  | ||||
|             if confirmedFileEntrys[i].size = 0 then begin | ||||
|                 find_free_space := baseAddress; | ||||
|                 break; | ||||
|             end else begin | ||||
|                 find_free_space := confirmedFileEntrys[i].start + confirmedFileEntrys[i].size; | ||||
|                 break; | ||||
|             end; | ||||
|  | ||||
|             break; | ||||
|         end; | ||||
|  | ||||
|         if confirmedFileEntrys[i].size <> 0 then begin | ||||
|             if confirmedFileEntrys[i+1].start - (confirmedFileEntrys[i].start + confirmedFileEntrys[i].size) > size+1 then begin | ||||
|                 find_free_space := confirmedFileEntrys[i].start + confirmedFileEntrys[i].size; | ||||
|                 status^ := 0; | ||||
|                 break; | ||||
|             end; | ||||
|         end; | ||||
|     end; | ||||
|  | ||||
|     Kfree(puint32(fileEntrys)); | ||||
|     Kfree(puint32(confirmedFileEntrys)); | ||||
| end; | ||||
|  | ||||
| procedure write_file(volume : PStorage_Volume; fileName : pchar; data : PuInt32; size : uint32; status : PuInt32); | ||||
| var | ||||
|     directories : PLinkedListBase; | ||||
|     i : uint32; | ||||
|     p : uint32; | ||||
|  | ||||
|     fileEntries : PFile_Entry; | ||||
|     fileEntry : PFile_Entry; | ||||
|     buffer : PuInt32; | ||||
|  | ||||
|     position : uint32; | ||||
|     offset : uint32; | ||||
|  | ||||
|     fileCount : uint32 = 1000; | ||||
|  | ||||
|     exsists : boolean; | ||||
|  | ||||
|     elm : puint32; | ||||
| begin | ||||
|     push_trace('flatfs.write_file'); | ||||
|  | ||||
|     //load file table | ||||
|     fileEntries := PFile_Entry(Kalloc(sizeof(TFile_Entry) * fileCount + 512)); | ||||
|     memset(uint32(fileEntries), 0, sizeof(TFile_Entry) * fileCount); | ||||
|  | ||||
|     volume^.device^.readCallback(volume^.device, volume^.sectorStart + 1, sizeof(TFile_Entry) * fileCount div 512, puint32(fileEntries)); | ||||
|  | ||||
|     //check if file exists else find free entry | ||||
|     for i := 0 to fileCount - 1 do begin | ||||
|  | ||||
|         if stringEquals(fileEntries[i].name, fileName) then begin | ||||
|             exsists := true; | ||||
|             p:= i; | ||||
|             break; | ||||
|         end; | ||||
|  | ||||
|         if fileEntries[i].attribues = 0 then begin | ||||
|             exsists := false; | ||||
|             p:= i; | ||||
|             break; | ||||
|         end; | ||||
|     end; | ||||
|  | ||||
|     //if entry is directory then exit | ||||
|     if fileEntries[p].attribues and $10 = $10 then begin | ||||
|         status^ := 1; | ||||
|         exit; | ||||
|     end; | ||||
|  | ||||
|     // //read file table cointaining entry of interest | ||||
|     position := (p * sizeof(TFile_Entry)) div 512; | ||||
|  | ||||
|     //if file exists update entry else create new entry | ||||
|     if exsists then begin | ||||
|          //check if size is greater than current size | ||||
|         if size > fileEntries[p].size then begin | ||||
|             //find free space | ||||
|             fileEntries[p].start := find_free_space(volume, size, status); | ||||
|             fileEntries[p].size := size; | ||||
|         end else begin | ||||
|             //update size | ||||
|             fileEntries[p].size := size; | ||||
|         end; | ||||
|  | ||||
|     end else begin | ||||
|         //find free space | ||||
|         fileEntries[p].start := find_free_space(volume, size, status); | ||||
|         fileEntries[p].size := size; | ||||
|         fileEntries[p].attribues := $01; | ||||
|  | ||||
|         //add file name | ||||
|         memcpy(uint32(fileName), uint32(@fileEntries[p].name), 14); | ||||
|  | ||||
|     end; | ||||
|  | ||||
|     //write file table | ||||
|     volume^.device^.writeCallback(volume^.device, volume^.sectorStart + 1 , 1, puint32(@fileEntries[position])); | ||||
|  | ||||
|     //write file | ||||
|     volume^.device^.writeCallback(volume^.device, fileEntries[p].start, size, data); | ||||
|     | ||||
|     //free buffer | ||||
|     Kfree(puint32(fileEntries)); | ||||
| end; | ||||
|  | ||||
| procedure read_file(volume : PStorage_Volume; fileName : pchar; data : PuInt32; status : PuInt32); | ||||
| var | ||||
|     directories : PLinkedListBase; | ||||
|     i : uint32; | ||||
|     p : uint32; | ||||
|  | ||||
|     fileEntries : PFile_Entry; | ||||
|     fileEntry : PFile_Entry; | ||||
|  | ||||
|     fileCount : uint32 = 1000; | ||||
|     exsists : boolean = false; | ||||
|  | ||||
|     size : uint32; | ||||
|  | ||||
| begin | ||||
|    | ||||
|     //load file table | ||||
|     fileEntries := PFile_Entry(Kalloc(sizeof(TFile_Entry) * fileCount + 512)); | ||||
|     memset(uint32(fileEntries), 0, sizeof(TFile_Entry) * fileCount); | ||||
|  | ||||
|     volume^.device^.readCallback(volume^.device, volume^.sectorStart + 1, sizeof(TFile_Entry) * fileCount div 512, puint32(fileEntries)); | ||||
|  | ||||
|     //check if file exists else exit | ||||
|     for i := 0 to fileCount - 1 do begin | ||||
|  | ||||
|         if stringEquals(fileEntries[i].name, fileName) then begin | ||||
|             exsists := true; | ||||
|             p:= i; | ||||
|             break; | ||||
|         end; | ||||
|  | ||||
|     end; | ||||
|  | ||||
|     //if entry is directory then exit | ||||
|     if fileEntries[p].attribues and $10 = $10 then begin | ||||
|         status^ := 1; | ||||
|         exit; | ||||
|     end; | ||||
|  | ||||
|     //if entry is not found exit | ||||
|     if fileEntries[i].attribues = 0 then begin | ||||
|         exsists := false; | ||||
|         p:= i; | ||||
|         exit; | ||||
|     end; | ||||
|  | ||||
|     //file size rounded up to nearest 512 | ||||
|     size := (fileEntries[p].size + 511) and -512; | ||||
|  | ||||
|     //read file | ||||
|     data := Kalloc(size); | ||||
|     volume^.device^.readCallback(volume^.device, fileEntries[p].start, size, data); | ||||
|  | ||||
|     //free buffer | ||||
|     Kfree(puint32(fileEntries)); | ||||
|  | ||||
|     status^ := 0; | ||||
| end; | ||||
|  | ||||
| procedure detect_volumes(disk : PStorage_Device); | ||||
| var | ||||
|     buffer : puint32; | ||||
|     volume : PStorage_volume; | ||||
| begin | ||||
|     push_trace('flatfs.detectVolumes()'); | ||||
|  | ||||
|     volume:= PStorage_volume(kalloc(sizeof(TStorage_Volume))); | ||||
|     //check first address for MBR | ||||
|     //if found then add volume and use info to see if there is another volume | ||||
|     buffer := puint32(kalloc(512)); | ||||
|     memset(uint32(buffer), 0, 512); | ||||
|     disk^.readcallback(disk, 2, 1, buffer); | ||||
|  | ||||
|  | ||||
|     if (PDisk_Info(buffer)^.signature = $0B00B1E5) then begin | ||||
|         console.writestringln('FlatFS: volume found!'); | ||||
|         volume^.device:= disk; | ||||
|         volume^.sectorStart:= 2; | ||||
|         volume^.sectorSize:= 512; | ||||
|         volume^.sectorCount:= PDisk_Info(buffer)^.sectorCount; | ||||
|         volume^.freeSectors:= 1000000; //TODO implement get free sectors need FSINFO implemented first | ||||
|         volume^.filesystem := @filesystem; | ||||
|         volume^.isBootDrive := false; | ||||
|          | ||||
|         // storagemanagement.register_volume(disk, volume); TODO repalce with new thing | ||||
|          | ||||
|     end; | ||||
|  | ||||
|     kfree(buffer); | ||||
| end; | ||||
|  | ||||
| procedure delete_file(volume : PStorage_Volume; fileName : pchar; status : PuInt32); | ||||
| var | ||||
|     directories : PLinkedListBase; | ||||
|     i : uint32; | ||||
|     p : uint32; | ||||
|  | ||||
|     fileEntries : PFile_Entry; | ||||
|     fileEntry : PFile_Entry; | ||||
|     zeroBuffer : PuInt32; | ||||
|  | ||||
|     position : uint32; | ||||
|     offset : uint32; | ||||
|  | ||||
|     fileCount : uint32 = 1000; | ||||
|  | ||||
|     exsists : boolean; | ||||
| begin | ||||
|    | ||||
|     //load file table | ||||
|     fileEntries := PFile_Entry(Kalloc(sizeof(TFile_Entry) * fileCount + 512)); | ||||
|     memset(uint32(fileEntries), 0, sizeof(TFile_Entry) * fileCount); | ||||
|  | ||||
|     volume^.device^.readCallback(volume^.device, volume^.sectorStart + 1, sizeof(TFile_Entry) * fileCount div 512, puint32(fileEntries)); | ||||
|  | ||||
|     //check if file exists else exit | ||||
|     for i := 0 to fileCount - 1 do begin | ||||
|  | ||||
|         if stringEquals(fileEntries[i].name, fileName) then begin | ||||
|             exsists := true; | ||||
|             p:= i; | ||||
|             break; | ||||
|         end; | ||||
|  | ||||
|     end; | ||||
|  | ||||
|     //set position of entry | ||||
|     position := (p * sizeof(TFile_Entry)) div 512; | ||||
|  | ||||
|     //if entry is directory then exit | ||||
|     if fileEntries[p].attribues and $10 = $10 then begin | ||||
|         status^ := 1; | ||||
|         exit; | ||||
|     end; | ||||
|  | ||||
|     //if entry is not found exit | ||||
|     if fileEntries[i].attribues = 0 then begin | ||||
|         exsists := false; | ||||
|         p:= i; | ||||
|         exit; | ||||
|     end; | ||||
|  | ||||
|     //zero out file | ||||
|     zeroBuffer := Kalloc(fileEntries[p].size); | ||||
|     memset(uint32(zeroBuffer), 0, fileEntries[p].size); | ||||
|  | ||||
|     //write file | ||||
|     volume^.device^.writeCallback(volume^.device, fileEntries[p].start, fileEntries[p].size, zeroBuffer); | ||||
|  | ||||
|     //free buffer | ||||
|     Kfree(puint32(zeroBuffer)); | ||||
|  | ||||
|     //zero out file table | ||||
|     memset(uint32(@fileEntries[p]), 0, sizeof(TFile_Entry)); | ||||
|      | ||||
|     //write file table | ||||
|     volume^.device^.writeCallback(volume^.device, volume^.sectorStart + 1 , 1, puint32(@fileEntries[position])); | ||||
|  | ||||
|     //free buffer | ||||
|     Kfree(puint32(fileEntries)); | ||||
|  | ||||
|     status^ := 0; | ||||
|      | ||||
| end; | ||||
|  | ||||
| procedure delete_directory(volume : PStorage_Volume; directoryName : pchar; status : PuInt32); | ||||
| var | ||||
|     directories : PLinkedListBase; | ||||
|     i : uint32; | ||||
|     p : uint32; | ||||
|  | ||||
|     fileEntries : PFile_Entry; | ||||
|     fileEntry : PFile_Entry; | ||||
|     zeroBuffer : PuInt32; | ||||
|  | ||||
|     position : uint32; | ||||
|     offset : uint32; | ||||
|  | ||||
|     fileCount : uint32 = 1000; | ||||
|  | ||||
|     exsists : boolean; | ||||
| begin | ||||
|  | ||||
|     //load file table | ||||
|     fileEntries := PFile_Entry(Kalloc(sizeof(TFile_Entry) * fileCount + 512)); | ||||
|     memset(uint32(fileEntries), 0, sizeof(TFile_Entry) * fileCount); | ||||
|  | ||||
|     volume^.device^.readCallback(volume^.device, volume^.sectorStart + 1, sizeof(TFile_Entry) * fileCount div 512, puint32(fileEntries)); | ||||
|  | ||||
|     //check if file exists else exit | ||||
|  | ||||
|     for i := 0 to fileCount - 1 do begin | ||||
|  | ||||
|         if stringEquals(fileEntries[i].name, directoryName) then begin | ||||
|             exsists := true; | ||||
|             p:= i; | ||||
|             break; | ||||
|         end; | ||||
|  | ||||
|     end; | ||||
|  | ||||
|     //if entry is not directory then exit | ||||
|     if fileEntries[i].attribues and $10 <> $10 then begin | ||||
|         status^ := 1; | ||||
|         exit; | ||||
|     end; | ||||
|  | ||||
|     //calculate position of directory in file table | ||||
|     position := (p * sizeof(TFile_Entry)) div 512; | ||||
|  | ||||
|     //zero file table | ||||
|     memset(uint32(@fileEntries[p]), 0, sizeof(TFile_Entry)); | ||||
|  | ||||
|     //write file table | ||||
|     volume^.device^.writeCallback(volume^.device, volume^.sectorStart + 1 , 1, puint32(@fileEntries[position])); | ||||
|  | ||||
|     //free buffer | ||||
|     Kfree(puint32(fileEntries)); | ||||
|  | ||||
|     status^ := 0; | ||||
|  | ||||
| end; | ||||
|  | ||||
|  | ||||
| procedure init(); | ||||
| begin | ||||
|     push_trace('flatfs.init()'); | ||||
|     filesystem.sName:= 'flatfs';  | ||||
|     filesystem.system_id:= $02;  | ||||
|     filesystem.readDirCallback:= @read_directory; | ||||
|     // filesystem.createDirCallback:= @writeDirectoryGen; | ||||
|     filesystem.createcallback:= @create_volume; | ||||
|     filesystem.detectcallback:= @detect_volumes; | ||||
|     // filesystem.writecallback:= @writeFile; | ||||
|     // filesystem.readcallback := @readFile; | ||||
|     // filesystem.formatVolumeCallback := @create_volume; | ||||
|  | ||||
|     volumemanager.register_filesystem(@filesystem); | ||||
| end; | ||||
|  | ||||
|      | ||||
| end. | ||||
| @@ -1,148 +0,0 @@ | ||||
| //  Copyright 2021 Aaron Hance | ||||
| // | ||||
| //  Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| //  you may not use this file except in compliance with the License. | ||||
| //  You may obtain a copy of the License at | ||||
| // | ||||
| //  http://www.apache.org/licenses/LICENSE-2.0 | ||||
| // | ||||
| //  Unless required by applicable law or agreed to in writing, software | ||||
| //  distributed under the License is distributed on an "AS IS" BASIS, | ||||
| //  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| //  See the License for the specific language governing permissions and | ||||
| //  limitations under the License. | ||||
|  | ||||
| {  | ||||
| 	Driver->Storage->IDE->idetypes - IDE device types and constants. | ||||
| 	 | ||||
| 	@author(Aaron Hance <ah@aaronhance.me>) | ||||
| } | ||||
| unit idetypes; | ||||
|  | ||||
| interface | ||||
|  | ||||
| const | ||||
|  | ||||
|     ATA_SR_BUSY = $80; //BUSY | ||||
|     ATA_SR_DRDY = $40; //DRIVE READY | ||||
|     ATA_SR_DF   = $20; //DRIVE WRITE FAULT | ||||
|     ATA_SR_DSC  = $10; //DRIVE SEEK COMPLETE | ||||
|     ATA_SR_DRQ  = $08; //DATA REQUEST READY | ||||
|     ATA_SR_CORR = $04; //CORRECTED DATA | ||||
|     ATA_SR_IDX  = $02; //INLEX | ||||
|     ATA_SR_ERR  = $01; //ERROR | ||||
|  | ||||
|     ATA_ER_BBK   = $80; //BAD SECTOR | ||||
|     ATA_ER_UNC   = $40; //UNCORRECTABLE DATA | ||||
|     ATA_ER_MC    = $20; //NO MEDIA | ||||
|     ATA_ER_IDNF  = $10; //ID MARK NOT FOUND | ||||
|     ATA_ER_MCR   = $08; //NO MEDIA | ||||
|     ATA_ER_ABRT  = $04; //COMMAND ABORTED | ||||
|     ATA_ER_TK0NF = $02; //TRACK 0 NOT FOUND | ||||
|     ATA_ER_AMNF  = $01; //NO ADDRESS MARK | ||||
|  | ||||
|     ATA_CMD_READ_PIO        = $20;  | ||||
|     ATA_CMD_READ_PIO_EXT    = $24;  | ||||
|     ATA_CMD_READ_DMA        = $C8;  | ||||
|     ATA_CMD_READ_DMA_EXT    = $25;  | ||||
|     ATA_CMD_WRITE_PIO       = $30;  | ||||
|     ATA_CMD_WRITE_PIO_EXT   = $34;  | ||||
|     ATA_CMD_WRITE_DMA       = $CA;  | ||||
|     ATA_CMD_WRITE_DMA_EXT   = $35;  | ||||
|     ATA_CMD_CACHE_FLUSH     = $E7;  | ||||
|     ATA_CMD_CACHE_FLUSH_EXT = $EA;  | ||||
|     ATA_CMD_PACKET          = $A0;  | ||||
|     ATA_CMD_IDENTIFY_PACKET = $A1;  | ||||
|     ATA_CMD_IDENTIFY        = $EC;  | ||||
|  | ||||
|     ATAPI_CMD_READ  = $A8; | ||||
|     ATAPI_CMD_EJECT = $1B; | ||||
|  | ||||
|     ATA_IDENT_DEVICETYPE   = $0;     | ||||
|     ATA_IDENT_CYLINDERS    = $2;     | ||||
|     ATA_IDENT_HEADS        = $6;     | ||||
|     ATA_IDENT_SECOTRS      = $12;     | ||||
|     ATA_IDENT_SERIAL       = $20;     | ||||
|     ATA_IDENT_MODEL        = $54;     | ||||
|     ATA_IDENT_CAPABILITIES = $98;     | ||||
|     ATA_IDENT_FIELDVALID   = $106;    | ||||
|     ATA_IDENT_MAX_LBA      = $120;    | ||||
|     ATA_IDENT_COMMANDSETS  = $164;        | ||||
|     ATA_IDENT_MAX_LBA_EXT  = $200; | ||||
|  | ||||
|     ATA_REG_DATA       = $00; | ||||
|     ATA_REG_ERROR      = $01; | ||||
|     ATA_REG_FEATURES   = $01; | ||||
|     ATA_REG_SECCOUNT  = $02; | ||||
|     ATA_REG_LBA0       = $03; | ||||
|     ATA_REG_LBA1       = $04; | ||||
|     ATA_REG_LBA2       = $05; | ||||
|     ATA_REG_HDDEVSEL   = $06; | ||||
|     ATA_REG_COMMAND    = $07; | ||||
|     ATA_REG_STATUS     = $07; | ||||
|     ATA_REG_SECCOUNT1  = $08; | ||||
|     ATA_REG_LBA3       = $09; | ||||
|     ATA_REG_LBA4       = $0A; | ||||
|     ATA_REG_LBA5       = $0B; | ||||
|     ATA_REG_CONTROL    = $0C; //1FC on some systems, 3F6 on all | ||||
|     ATA_REG_ALTSTATUS  = $0C; | ||||
|     ATA_REG_DEVADDRESS = $0D;  | ||||
|  | ||||
|     ATA_DEVICE_MASTER = $A0; | ||||
|     ATA_DEVICE_SLAVE = $B0; | ||||
|  | ||||
|     ATA_PRIMARY_BASE = $1F0; | ||||
|     ATA_PRIMARY_BASE1 = $3F6; | ||||
|     ATA_SECONDARY_BASE = $170; //todo check this | ||||
|     ATA_SECONDARY_BASE1 = $376; //todo check this | ||||
|  | ||||
|     ATA_INTERRUPT_PRIMARY = $3F6; | ||||
|     ATA_INTERRUPT_SECONDARY = $376; | ||||
|  | ||||
| type | ||||
|     TPortMode = (P_READ, P_WRITE); | ||||
|  | ||||
|     TIdentResponse = array[0..255] of uint16; | ||||
|     PIdentResponse = ^TIdentResponse; | ||||
|  | ||||
|     TIDE_Channel_Registers = record | ||||
|         base    : uint16; | ||||
|         ctrl    : uint16; | ||||
|         bmide   : uint16; | ||||
|         noInter : uint8 | ||||
|     end; | ||||
|  | ||||
|     TIDE_Status = bitpacked record  | ||||
|         Busy  : Boolean; | ||||
|         Ready : Boolean; | ||||
|         Fault : Boolean; | ||||
|         Seek  : Boolean; | ||||
|         DRQ   : Boolean; | ||||
|         CORR  : Boolean; | ||||
|         IDDEX : Boolean; | ||||
|         ERROR : Boolean; | ||||
|     end; | ||||
|     PIDE_Status = ^TIDE_Status; | ||||
|  | ||||
|     TIDE_Device = record | ||||
|         exists       : boolean; | ||||
|         isPrimary    : boolean; | ||||
|         isMaster     : boolean; | ||||
|         isATAPI      : boolean; | ||||
|         status       : TIDE_Status; | ||||
|         base         : uint16; | ||||
|         blockSize    : uint32; | ||||
|         info         : PIdentResponse; | ||||
|     end; | ||||
|  | ||||
|     TIDE_PACKET = record | ||||
|         command : uint8; | ||||
|         lba     : uint32; | ||||
|         count   : uint8; | ||||
|         buffer  : puint16; | ||||
|     end; | ||||
|  | ||||
|  | ||||
| implementation | ||||
|    | ||||
| end. | ||||
| @@ -15,8 +15,6 @@ | ||||
| { | ||||
|     Driver->Storage->StorageManagment Storage Managment System | ||||
| 
 | ||||
|     DEPRECATED unit | ||||
| 
 | ||||
|     @author(Aaron Hance ah@aaronhance.me) | ||||
| } | ||||
| unit storagemanagement; | ||||
| @@ -33,14 +31,14 @@ uses | ||||
|     strings, | ||||
|     lists, | ||||
|     tracer, | ||||
|     rtc, | ||||
|     MBR, | ||||
|     volumemanager, | ||||
|     storagetypes; | ||||
|     rtc; | ||||
| 
 | ||||
| type  | ||||
| 
 | ||||
|     TControllerType = (ControllerIDE, ControllerUSB, ControllerAHCI, ControllerNET, ControllerRAM, rsvctr1, rsvctr2, rsvctr3); | ||||
|     TDirectory_Entry_Type = (directoryEntry, fileEntry, mountEntry); | ||||
|     PStorage_volume = ^TStorage_Volume; | ||||
|     PStorage_device = ^TStorage_Device; | ||||
|     APStorage_Volume = array[0..10] of PStorage_volume; | ||||
|     byteArray8 = array[0..7] of char; | ||||
|     PByteArray8 = ^byteArray8; | ||||
| @@ -56,6 +54,41 @@ type | ||||
| 
 | ||||
|     PPHIOHook_ = procedure; | ||||
| 
 | ||||
|     TFilesystem = record | ||||
|         sName : pchar; | ||||
|         writeCallback     : PPWriteHook; | ||||
|         readCallback      : PPReadHook; | ||||
|         createCallback    : PPCreateHook; | ||||
|         detectCallback    : PPDetectHook; | ||||
|         createDirCallback : PPCreateDirHook; | ||||
|         readDirCallback   : PPReadDirHook; | ||||
|     end; | ||||
|     PFileSystem = ^TFilesystem; | ||||
| 
 | ||||
|     TStorage_Volume = record | ||||
|         device          : PStorage_device; | ||||
|         sectorStart     : uint32; | ||||
|         sectorSize      : uint32; | ||||
|         freeSectors     : uint32; | ||||
|         filesystem      : PFilesystem;  | ||||
|         { True if this drive contains the loaded OS } | ||||
|         isBootDrive     : boolean; | ||||
|     end; | ||||
| 
 | ||||
|     { Generic storage device } | ||||
|     TStorage_Device = record | ||||
|         idx              : uint8; | ||||
|         controller       : TControllerType; | ||||
|         controllerId0    : uint32; | ||||
|         maxSectorCount   : uint32; | ||||
|         sectorSize       : uint32; | ||||
|         writable         : boolean; | ||||
|         volumes          : PLinkedListBase; | ||||
|         writeCallback    : PPHIOHook; | ||||
|         readCallback     : PPHIOHook; | ||||
|         hpc              : uint16; | ||||
|         spt              : uint16; | ||||
|     end; | ||||
|     APStorage_Device = array[0..255] of PStorage_device; | ||||
| 
 | ||||
|     { Generic directory entry } | ||||
| @@ -77,13 +110,20 @@ procedure init(); | ||||
| procedure register_device(device : PStorage_Device); | ||||
| function get_device_list() : PLinkedListBase; | ||||
| 
 | ||||
| procedure register_filesystem(filesystem : PFilesystem); | ||||
| 
 | ||||
| procedure register_volume(device : PStorage_Device; volume : PStorage_Volume); | ||||
| //function writeNewFile(fileName : pchar; extension : pchar; buffer : puint32; size : uint32) : uint32; | ||||
| //function readFile(fileName : pchar; extension : pchar; buffer : puint32; byteCount : puint32) : puint32; | ||||
| 
 | ||||
| //TODO write partition table | ||||
| 
 | ||||
| implementation  | ||||
| 
 | ||||
| function controller_type_2_string(controllerType : TControllerType) : pchar;  | ||||
| begin | ||||
|     case controllerType of | ||||
|         ControllerATA : controller_type_2_string:= 'ATA'; | ||||
|         ControllerATAPI : controller_type_2_string:= 'ATAPI'; | ||||
|         ControllerIDE : controller_type_2_string:= 'IDE'; | ||||
|         ControllerUSB :  controller_type_2_string:= 'USB'; | ||||
|         ControllerAHCI : controller_type_2_string:= 'ACHI (SATA)'; | ||||
|         ControllerNET : controller_type_2_string:= 'Net'; | ||||
| @@ -129,57 +169,18 @@ var | ||||
|     drive : PStorage_Device; | ||||
| 
 | ||||
|     filesystemString : pchar; | ||||
|     filesystem : PFileSystem; | ||||
|     spc : puint32; | ||||
| 
 | ||||
|     sectorCount : uint32; | ||||
|     volumeId : uint8; | ||||
| 
 | ||||
|     mb : PMaster_Boot_Record; | ||||
|     i : uint32 = 0; | ||||
| 
 | ||||
| begin | ||||
| 
 | ||||
|     //format <DRIVE_ID> <FILESYSTEM> <size> | ||||
| 
 | ||||
|     spc:= puint32(kalloc(4)); | ||||
|     spc^:= 1; | ||||
|     spc^:= 4; | ||||
| 
 | ||||
|     driveIndex:= stringToInt( getParam(1, params) ); | ||||
|     drive:= PStorage_Device(LL_Get(storageDevices, driveIndex)); | ||||
| 
 | ||||
|     filesystemString := getParam(2, params); | ||||
| 
 | ||||
|     sectorCount := stringToInt( getParam(4, params) ); | ||||
| 
 | ||||
|     // if sector count is 0, use full drive | ||||
|     if sectorCount = 0 then begin | ||||
|         sectorCount := drive^.maxSectorCount - 10; | ||||
|     end; | ||||
| 
 | ||||
|     //create MBR if none, and partition table | ||||
| 
 | ||||
|     mb := PMaster_Boot_Record(kalloc(sizeof(TMaster_Boot_Record))); | ||||
|     drive^.readCallback(drive, 0, 1, PuInt32(mb)); | ||||
| 
 | ||||
|     //check if MBR exists | ||||
|     if not mb^.boot_sector = $55AA then begin | ||||
| 
 | ||||
|         //create MBR | ||||
|         mb^.signature := $A570 + drive^.id; | ||||
|         mb^.boot_sector := $55AA; | ||||
|          | ||||
|         //create partition table | ||||
|         mbr.setup_partition(@mb^.partition[0], 2, sectorCount); | ||||
| 
 | ||||
|         //write MBR | ||||
|         drive^.writeCallback(drive, 0, 1, puint32(mb)); | ||||
|          | ||||
|     end; | ||||
| 
 | ||||
|     kfree(puint32(mb)); | ||||
| 
 | ||||
|     //setup volume | ||||
|     volumemanager.create_volume(drive, filesystemString, 2, sectorCount); | ||||
|     //todo change b4 adding in aniother filesytem | ||||
|     PFilesystem(LL_Get(filesystems, 0))^.createCallback(drive, drive^.maxSectorCount-1, 1, spc);  | ||||
| 
 | ||||
|     writestringWnd('Drive ', getTerminalHWND); | ||||
|     writeintWnd(driveIndex, getTerminalHWND); | ||||
| @@ -216,6 +217,10 @@ begin | ||||
|         format_command(params); | ||||
|     end; | ||||
| 
 | ||||
|     //lsfs command for listing filesystems | ||||
|     if stringEquals(subcmd, 'lsfs') then begin | ||||
|     end; | ||||
| 
 | ||||
|     pop_trace(); | ||||
| end; | ||||
| 
 | ||||
| @@ -226,8 +231,10 @@ begin | ||||
|      | ||||
|     setworkingdirectory('.'); | ||||
|     storageDevices:= ll_new(sizeof(TStorage_Device)); | ||||
|     fileSystems:= ll_New(sizeof(TFilesystem)); | ||||
|     terminal.registerCommand('DISK', @disk_command, 'Disk utility'); | ||||
| 
 | ||||
|     pop_trace(); | ||||
| end;  | ||||
| 
 | ||||
| { Register a new drive with the storage manager } | ||||
| @@ -246,7 +253,9 @@ begin | ||||
|     PStorage_Device(LL_Get(storageDevices, LL_Size(storageDevices) - 1))^.volumes := LL_New(sizeof(TStorage_Volume)); | ||||
|      | ||||
|     //check for volumes | ||||
| 
 | ||||
|     for i:=0 to ll_size(filesystems) - 1 do begin | ||||
|         PFileSystem(LL_Get(filesystems, i))^.detectCallback(PStorage_Device(LL_Get(storageDevices, LL_Size(storageDevices) - 1))); | ||||
|     end; | ||||
| 
 | ||||
|     pop_trace(); | ||||
| end; | ||||
| @@ -256,4 +265,23 @@ begin | ||||
|     get_device_list:= storageDevices; | ||||
| end; | ||||
| 
 | ||||
| procedure register_filesystem(filesystem : PFilesystem); | ||||
| var | ||||
|     elm : void;     | ||||
| begin | ||||
|     elm:= LL_Add(fileSystems); | ||||
|     PFileSystem(elm)^ := filesystem^; | ||||
| end; | ||||
| 
 | ||||
| procedure register_volume(device : PStorage_Device; volume : PStorage_Volume); | ||||
| var | ||||
|     elm : void; | ||||
| begin | ||||
|     push_trace('storagemanagement.register_volume'); | ||||
|     elm := LL_Add(device^.volumes); | ||||
|     PStorage_volume(elm)^:= volume^; | ||||
| 
 | ||||
|     if rootVolume = PStorage_Volume(0) then rootVolume:= volume; | ||||
| end; | ||||
| 
 | ||||
| end. | ||||
| @@ -1,95 +0,0 @@ | ||||
| //  Copyright 2021 Aaron Hance | ||||
| // | ||||
| //  Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| //  you may not use this file except in compliance with the License. | ||||
| //  You may obtain a copy of the License at | ||||
| // | ||||
| //  http://www.apache.org/licenses/LICENSE-2.0 | ||||
| // | ||||
| //  Unless required by applicable law or agreed to in writing, software | ||||
| //  distributed under the License is distributed on an "AS IS" BASIS, | ||||
| //  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| //  See the License for the specific language governing permissions and | ||||
| //  limitations under the License. | ||||
|  | ||||
| {  | ||||
| 	Driver->Storage->Include->storagetypes - Structs & Data Shared Across Storage Drivers. | ||||
| 	 | ||||
| 	@author(Aaron Hance <ah@aaronhance.me>) | ||||
| } | ||||
| unit storagetypes; | ||||
|  | ||||
| interface | ||||
|  | ||||
| uses | ||||
|     lists; | ||||
|  | ||||
| type | ||||
|  | ||||
|     TControllerType = ( | ||||
|         ControllerATA,  | ||||
|         ControllerATAPI, | ||||
|         ControllerUSB, | ||||
|         ControllerAHCI, | ||||
|         ControllerAHCI_ATAPI, | ||||
|         ControllerNVMe, | ||||
|         ControllerNET,  | ||||
|         ControllerRAM, | ||||
|         ControllerSCSI | ||||
|         ); | ||||
|  | ||||
|     TFileSystemType = ( | ||||
|         FileSystemFAT32, | ||||
|         FileSystemExFAT, | ||||
|         FileSystemEXT, | ||||
|         FileSystemEXT2, | ||||
|         FileSystemCDFS, | ||||
|         FileSystemOther | ||||
|         ); | ||||
|  | ||||
|     PStorage_device = ^TStorage_Device; | ||||
|     PDrive_Error    = ^TDrive_Error; | ||||
|  | ||||
|     PPHIOHook = procedure(drive : PStorage_device; addr : uint32; sectors : uint32; buffer : puint32); | ||||
|     PPHIOHook_ = procedure; | ||||
|  | ||||
|     byteArray8 = array[0..7] of char; | ||||
|     PByteArray8 = ^byteArray8; | ||||
|  | ||||
|     { Generic storage device } | ||||
|     TStorage_Device = record | ||||
|         id               : uint8; | ||||
|         controller       : TControllerType; | ||||
|         controllerId0    : uint32; | ||||
|         writable         : boolean; | ||||
|         volumes          : PDList; | ||||
|         writeCallback    : PPHIOHook; | ||||
|         readCallback     : PPHIOHook; | ||||
|         maxSectorCount   : uint32;  | ||||
|         sectorSize       : uint32; //in bytes | ||||
|         start            : uint32; //start of device in sectors | ||||
|     end; | ||||
|  | ||||
|     { Generic storage volume } | ||||
|     TStorage_Volume = record | ||||
|         id         : uint32; | ||||
|         device     : PStorage_device; | ||||
|         name       : pchar; | ||||
|         fileSystem : TFileSystemType; | ||||
|         size       : uint32; //size of volume in sectors | ||||
|         start      : uint32; //start of volume in sectors | ||||
|     end; | ||||
|  | ||||
|     PStorage_Volume = ^TStorage_Volume; | ||||
|  | ||||
|     TDrive_Error = record  | ||||
|         code : uint16; | ||||
|         description : pchar; | ||||
|         recoverable : boolean; | ||||
|     end; | ||||
|  | ||||
|  | ||||
| implementation | ||||
|  | ||||
|  | ||||
| end. | ||||
| @@ -1,108 +0,0 @@ | ||||
| //  Copyright 2021 Aaron Hance | ||||
| // | ||||
| //  Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| //  you may not use this file except in compliance with the License. | ||||
| //  You may obtain a copy of the License at | ||||
| // | ||||
| //  http://www.apache.org/licenses/LICENSE-2.0 | ||||
| // | ||||
| //  Unless required by applicable law or agreed to in writing, software | ||||
| //  distributed under the License is distributed on an "AS IS" BASIS, | ||||
| //  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| //  See the License for the specific language governing permissions and | ||||
| //  limitations under the License. | ||||
|  | ||||
| { | ||||
|     Driver->Storage->VolumeManager - Volume manager for storage devices. | ||||
|  | ||||
|     @author(Aaron Hance ah@aaronhance.me) | ||||
| } | ||||
|  | ||||
| unit volumemanager; | ||||
|  | ||||
| interface | ||||
|  | ||||
| uses | ||||
|     console, | ||||
|     drivermanagement, | ||||
|     drivertypes, | ||||
|     lists, | ||||
|     lmemorymanager, | ||||
|     MBR, | ||||
|     rtc, | ||||
|     serial, | ||||
|     storagetypes, | ||||
|     strings, | ||||
|     terminal, | ||||
|     tracer, | ||||
|     util; | ||||
|  | ||||
| var | ||||
|     storageDevices : PDList; //index in this array is global drive id | ||||
|     volumes : PDList; | ||||
|  | ||||
|     procedure init(); | ||||
|     procedure register_device(device : PStorage_Device); | ||||
|     function discover_volumes(device : PStorage_device) : PDList; | ||||
|  | ||||
|     function get_volume_list() : PDList; | ||||
|      | ||||
|  | ||||
| implementation | ||||
|  | ||||
| procedure init(); | ||||
| begin | ||||
|     push_trace('VolumeManager.init'); | ||||
|  | ||||
|     storageDevices := DL_New(sizeof(PStorage_Device)); | ||||
|     volumes := DL_New(sizeof(PStorage_Volume)); | ||||
|  | ||||
|     //TODO register commnads | ||||
|     pop_trace(); | ||||
| end; | ||||
|  | ||||
| procedure register_device(device : PStorage_Device); | ||||
| var  | ||||
|     diskVolumes : PDList; | ||||
| begin | ||||
|     push_trace('VolumeManager.register_device'); | ||||
|  | ||||
|     DL_Add(storageDevices); | ||||
|     DL_Set(storageDevices, DL_Size(storageDevices) - 1, puint32(device)); | ||||
|  | ||||
|     diskVolumes := discover_volumes(device); | ||||
|  | ||||
|     if volumes <> nil then begin | ||||
|         volumes := DL_Concat(volumes, diskVolumes); | ||||
|     end; | ||||
|  | ||||
|     pop_trace(); | ||||
| end; | ||||
|  | ||||
| function discover_volumes(device : PStorage_device) : PDList; | ||||
| begin | ||||
|     push_trace('VolumeManager.discover_volumes'); | ||||
|      | ||||
|     discover_volumes := nil; | ||||
|     //TODO implement | ||||
|  | ||||
|     pop_trace(); | ||||
| end; | ||||
|  | ||||
| function get_volume_list() : PDList; | ||||
| var | ||||
|     newList : PDList; | ||||
|     i : uint32; | ||||
| begin | ||||
|     push_trace('VolumeManager.get_volume_list'); | ||||
|  | ||||
|     //get copy of volume list | ||||
|     newList := DL_New(sizeof(PStorage_Volume)); //TODO need to add DL_Copy function | ||||
|  | ||||
|     for i := 0 to DL_Size(volumes) - 1 do begin | ||||
|         DL_Add(newList); | ||||
|         DL_Set(newList, i, DL_Get(volumes, i)); | ||||
|     end; | ||||
| end; | ||||
|  | ||||
| end. | ||||
							
								
								
									
										100
									
								
								src/driver/video/doublebuffer.pas
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								src/driver/video/doublebuffer.pas
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,100 @@ | ||||
| //  Copyright 2021 Kieron Morris | ||||
| // | ||||
| //  Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| //  you may not use this file except in compliance with the License. | ||||
| //  You may obtain a copy of the License at | ||||
| // | ||||
| //  http://www.apache.org/licenses/LICENSE-2.0 | ||||
| // | ||||
| //  Unless required by applicable law or agreed to in writing, software | ||||
| //  distributed under the License is distributed on an "AS IS" BASIS, | ||||
| //  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| //  See the License for the specific language governing permissions and | ||||
| //  limitations under the License. | ||||
|  | ||||
| {  | ||||
| 	Driver->Video->Doublebuffer - Implements a very basic double buffer, tested with VESA drivers. | ||||
| 	 | ||||
| 	@author(Kieron Morris <kjm@kieronmorris.me>) | ||||
| } | ||||
| unit doublebuffer; | ||||
|  | ||||
| interface | ||||
|  | ||||
| uses | ||||
|     lmemorymanager, tracer, videotypes, serial, util; | ||||
|  | ||||
| //Init the driver, and register with the video interface in a state ready for execution. | ||||
| procedure init(Register : FRegisterDriver); | ||||
|  | ||||
| implementation | ||||
|  | ||||
| function allocateBackBuffer(Width : uint32; Height : uint32; BitsPerPixel : uint8) : uint64; | ||||
| begin | ||||
|     tracer.push_trace('doublebuffer.allocateBackBuffer.enter'); | ||||
|     //This doesn't currently work... Needs a rework of lmemorymanager | ||||
|     allocateBackBuffer:= uint64(klalloc((Width * Height) * BitsPerPixel)); | ||||
|     tracer.push_trace('doublebuffer.allocateBackBuffer.exit'); | ||||
| end; | ||||
|  | ||||
| procedure initBackBuffer(VInterface : PVideoInterface; Width : uint32; Height : uint32; BitsPerPixel : uint8); | ||||
| begin | ||||
|     tracer.push_trace('doublebuffer.initBackBuffer.enter'); | ||||
|     if not(VInterface^.BackBuffer.Initialized) then begin | ||||
|         VInterface^.BackBuffer.Location:= allocateBackBuffer(Width, Height, BitsPerPixel); | ||||
|         if (VInterface^.BackBuffer.Location <> 0) then begin | ||||
|             VInterface^.BackBuffer.Width:= Width; | ||||
|             VInterface^.BackBuffer.Height:= Height; | ||||
|             VInterface^.BackBuffer.BitsPerPixel:= BitsPerPixel; | ||||
|             VInterface^.BackBuffer.Initialized:= True; | ||||
|         end; | ||||
|     end; | ||||
|     tracer.push_trace('doublebuffer.initBackBuffer.exit'); | ||||
| end; | ||||
|  | ||||
| procedure Flush(FrontBuffer : PVideoBuffer; BackBuffer : PVideoBuffer); | ||||
| var | ||||
|     idx : uint32; | ||||
|     Back,Front : uint32; | ||||
|     BufferSize : uint32; | ||||
|  | ||||
| const | ||||
|     //COPY_WIDTH = 64; //Use this for 64bit copies | ||||
|     COPY_WIDTH = 128; //Use this for SSE copies | ||||
|  | ||||
| begin | ||||
|     //tracer.push_trace('doublebuffer.Flush.enter'); | ||||
|     if not(BackBuffer^.Initialized) then exit; | ||||
|     if ((FrontBuffer^.Width > BackBuffer^.Width) or (FrontBuffer^.Height > BackBuffer^.Height)) then exit; | ||||
|     Back:= BackBuffer^.Location; | ||||
|     Front:= FrontBuffer^.Location; | ||||
|     BufferSize:= ( ( BackBuffer^.Width * BackBuffer^.Height * BackBuffer^.BitsPerPixel) div COPY_WIDTH ) - 1; | ||||
|     for idx:=0 to BufferSize do begin | ||||
|         //Front[idx]:= Back[idx]; | ||||
|         // -- TODO: Get SSE working here for 128bit copies -- | ||||
|         __SSE_128_memcpy(Back + (idx * 16), Front + (idx * 16));   | ||||
|     end; | ||||
|     //tracer.push_trace('doublebuffer.Flush.exit'); | ||||
| end; | ||||
|  | ||||
| function enable(VideoInterface : PVideoInterface) : boolean; | ||||
| begin | ||||
|     tracer.push_trace('doublebuffer.enable.enter'); | ||||
|     enable:= false; | ||||
|     initBackBuffer(VideoInterface, VideoInterface^.FrontBuffer.Width, VideoInterface^.FrontBuffer.Height, VideoInterface^.FrontBuffer.BitsPerPixel); | ||||
|     if VideoInterface^.BackBuffer.Initialized then begin | ||||
|         VideoInterface^.DefaultBuffer:= @VideoInterface^.BackBuffer; | ||||
|         VideoInterface^.DrawRoutines.Flush:= @Flush; | ||||
|         enable:= true; | ||||
|     end; | ||||
|     tracer.push_trace('doublebuffer.enable.exit'); | ||||
| end; | ||||
|  | ||||
| procedure init(Register : FRegisterDriver); | ||||
| begin | ||||
|     tracer.push_trace('doublebuffer.init.enter'); | ||||
|     Register('BASIC_DOUBLE_BUFFER', @enable); | ||||
|     tracer.push_trace('doublebuffer.init.exit'); | ||||
| end; | ||||
|  | ||||
| end. | ||||
							
								
								
									
										87
									
								
								src/driver/video/vesa.pas
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								src/driver/video/vesa.pas
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,87 @@ | ||||
| //  Copyright 2021 Kieron Morris | ||||
| // | ||||
| //  Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| //  you may not use this file except in compliance with the License. | ||||
| //  You may obtain a copy of the License at | ||||
| // | ||||
| //  http://www.apache.org/licenses/LICENSE-2.0 | ||||
| // | ||||
| //  Unless required by applicable law or agreed to in writing, software | ||||
| //  distributed under the License is distributed on an "AS IS" BASIS, | ||||
| //  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| //  See the License for the specific language governing permissions and | ||||
| //  limitations under the License. | ||||
|  | ||||
| {  | ||||
| 	Driver->Video->VESA - VESA Display Driver. | ||||
| 	 | ||||
| 	@author(Kieron Morris <kjm@kieronmorris.me>) | ||||
| } | ||||
| unit vesa; | ||||
|  | ||||
| interface | ||||
|  | ||||
| uses | ||||
|     videotypes, tracer, multiboot, lmemorymanager; | ||||
|  | ||||
| //Init the driver, and register with the video interface in a state ready for execution. | ||||
| procedure init(Register : FRegisterDriver); | ||||
|  | ||||
| implementation | ||||
|  | ||||
| uses | ||||
|     VESA8, VESA16, VESA24, VESA32; | ||||
|  | ||||
| procedure allocateVESAFrameBuffer(Address : uint32; Width : uint32; Height : uint32); | ||||
| var | ||||
|     LowerAddress, UpperAddress : uint32; | ||||
|     Block : uint32; | ||||
|  | ||||
| begin | ||||
|     tracer.push_trace('VESA.allocateFrameBuffer.enter'); | ||||
|     LowerAddress:= ((Address) SHR 22)-1; | ||||
|     UpperAddress:= ((Address + (Width * Height)) SHR 22)+1; | ||||
|     For Block:=LowerAddress to UpperAddress do begin | ||||
|         kpalloc(Block SHL 22); | ||||
|     end; | ||||
|     tracer.push_trace('VESA.allocateFrameBuffer.exit'); | ||||
| end; | ||||
|  | ||||
| procedure initVESAFrameBuffer(VideoBuffer : PVideoBuffer; Location : uint64; Width : uint32; Height : uint32; BitsPerPixel : uint8); | ||||
| begin | ||||
|     tracer.push_trace('VESA.initVESAFrameBuffer.enter'); | ||||
|     if not(VideoBuffer^.Initialized) then begin | ||||
|         VideoBuffer^.Location:= Location; | ||||
|         VideoBuffer^.BitsPerPixel:= BitsPerPixel; | ||||
|         VideoBuffer^.Width:= Width; | ||||
|         VideoBuffer^.Height:= Height; | ||||
|         allocateVESAFrameBuffer(VideoBuffer^.Location, VideoBuffer^.Width, VideoBuffer^.Height); | ||||
|         if VideoBuffer^.Location <> 0 then | ||||
|             VideoBuffer^.Initialized:= True; | ||||
|     end; | ||||
|     tracer.push_trace('VESA.initVESAFrameBuffer.exit'); | ||||
| end; | ||||
|  | ||||
| function enable(VideoInterface : PVideoInterface) : boolean; | ||||
| begin | ||||
|     tracer.push_trace('VESA.enable.enter'); | ||||
|     initVESAFrameBuffer(@VideoInterface^.FrontBuffer, multiboot.multibootinfo^.framebuffer_addr, multiboot.multibootinfo^.framebuffer_width, multiboot.multibootinfo^.framebuffer_height, multiboot.multibootinfo^.framebuffer_bpp); | ||||
|     case (VideoInterface^.FrontBuffer.BitsPerPixel) of | ||||
|         08:VESA8.init(@VideoInterface^.DrawRoutines); | ||||
|         16:VESA16.init(@VideoInterface^.DrawRoutines); | ||||
|         24:VESA24.init(@VideoInterface^.DrawRoutines); | ||||
|         32:VESA32.init(@VideoInterface^.DrawRoutines); | ||||
|     end; | ||||
|     enable:= VideoInterface^.FrontBuffer.Initialized;   | ||||
|     tracer.push_trace('VESA.enable.exit'); | ||||
| end; | ||||
|  | ||||
| procedure init(Register : FRegisterDriver); | ||||
| begin | ||||
|     tracer.push_trace('VESA.init.enter'); | ||||
|     if Register <> nil then | ||||
|         Register('VESA', @enable);    | ||||
|     tracer.push_trace('VESA.init.exit'); | ||||
| end; | ||||
|  | ||||
| end. | ||||
							
								
								
									
										43
									
								
								src/driver/video/vesa16.pas
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								src/driver/video/vesa16.pas
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,43 @@ | ||||
| //  Copyright 2021 Kieron Morris | ||||
| // | ||||
| //  Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| //  you may not use this file except in compliance with the License. | ||||
| //  You may obtain a copy of the License at | ||||
| // | ||||
| //  http://www.apache.org/licenses/LICENSE-2.0 | ||||
| // | ||||
| //  Unless required by applicable law or agreed to in writing, software | ||||
| //  distributed under the License is distributed on an "AS IS" BASIS, | ||||
| //  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| //  See the License for the specific language governing permissions and | ||||
| //  limitations under the License. | ||||
|  | ||||
| {  | ||||
| 	Driver->Video->VESA16 - Implementation of VESA 16bpp draw routines for the VESA Driver. | ||||
| 	 | ||||
| 	@author(Kieron Morris <kjm@kieronmorris.me>) | ||||
| } | ||||
| unit vesa16; | ||||
|  | ||||
| interface | ||||
|  | ||||
| uses | ||||
|     videotypes, vesa, tracer, color; | ||||
|  | ||||
| //Init the draw routines by providing what we support through the DrawRoutines struct. | ||||
| procedure init(DrawRoutines : PDrawRoutines); | ||||
|  | ||||
| implementation | ||||
|  | ||||
| procedure DrawPixel(Buffer : PVideoBuffer; X : uint32; Y : uint32; Pixel : TRGB32); | ||||
| begin | ||||
|  | ||||
| end; | ||||
|  | ||||
| procedure init(DrawRoutines : PDrawRoutines); | ||||
| begin | ||||
|     tracer.push_trace('vesa16.init.enter');    | ||||
|     tracer.push_trace('vesa16.init.exit'); | ||||
| end; | ||||
|  | ||||
| end. | ||||
							
								
								
									
										43
									
								
								src/driver/video/vesa24.pas
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								src/driver/video/vesa24.pas
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,43 @@ | ||||
| //  Copyright 2021 Kieron Morris | ||||
| // | ||||
| //  Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| //  you may not use this file except in compliance with the License. | ||||
| //  You may obtain a copy of the License at | ||||
| // | ||||
| //  http://www.apache.org/licenses/LICENSE-2.0 | ||||
| // | ||||
| //  Unless required by applicable law or agreed to in writing, software | ||||
| //  distributed under the License is distributed on an "AS IS" BASIS, | ||||
| //  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| //  See the License for the specific language governing permissions and | ||||
| //  limitations under the License. | ||||
|  | ||||
| {  | ||||
| 	Driver->Video->VESA24 - Implementation of VESA 24bpp draw routines for the VESA Driver. | ||||
| 	 | ||||
| 	@author(Kieron Morris <kjm@kieronmorris.me>) | ||||
| } | ||||
| unit vesa24; | ||||
|  | ||||
| interface | ||||
|  | ||||
| uses | ||||
|     videotypes, vesa, tracer, color; | ||||
|  | ||||
| //Init the draw routines by providing what we support through the DrawRoutines struct. | ||||
| procedure init(DrawRoutines : PDrawRoutines); | ||||
|  | ||||
| implementation | ||||
|  | ||||
| procedure DrawPixel(Buffer : PVideoBuffer; X : uint32; Y : uint32; Pixel : TRGB32); | ||||
| begin | ||||
|  | ||||
| end; | ||||
|  | ||||
| procedure init(DrawRoutines : PDrawRoutines); | ||||
| begin | ||||
| 	tracer.push_trace('vesa24.init.enter'); | ||||
| 	tracer.push_trace('vesa24.init.exit'); | ||||
| end; | ||||
|  | ||||
| end. | ||||
							
								
								
									
										52
									
								
								src/driver/video/vesa32.pas
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								src/driver/video/vesa32.pas
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,52 @@ | ||||
| //  Copyright 2021 Kieron Morris | ||||
| // | ||||
| //  Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| //  you may not use this file except in compliance with the License. | ||||
| //  You may obtain a copy of the License at | ||||
| // | ||||
| //  http://www.apache.org/licenses/LICENSE-2.0 | ||||
| // | ||||
| //  Unless required by applicable law or agreed to in writing, software | ||||
| //  distributed under the License is distributed on an "AS IS" BASIS, | ||||
| //  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| //  See the License for the specific language governing permissions and | ||||
| //  limitations under the License. | ||||
|  | ||||
| {  | ||||
| 	Driver->Video->VESA32 - Implementation of VESA 32bpp draw routines for the VESA Driver. | ||||
| 	 | ||||
| 	@author(Kieron Morris <kjm@kieronmorris.me>) | ||||
| } | ||||
| unit vesa32; | ||||
|  | ||||
| interface | ||||
|  | ||||
| uses | ||||
|     videotypes, vesa, tracer, color; | ||||
|  | ||||
| //Init the draw routines by providing what we support through the DrawRoutines struct. | ||||
| procedure init(DrawRoutines : PDrawRoutines); | ||||
|  | ||||
| implementation | ||||
|  | ||||
| procedure DrawPixel(Buffer : PVideoBuffer; X : uint32; Y : uint32; Pixel : TRGB32); | ||||
| var | ||||
|     Location : PuInt32; | ||||
|     LocationIndex : Uint32; | ||||
|  | ||||
| begin | ||||
|     //tracer.push_trace('vesa32.DrawPixel.enter'); | ||||
|     Location:= Puint32(Buffer^.Location); | ||||
|     LocationIndex:= (Y * Buffer^.Width) + X; | ||||
|     Location[LocationIndex]:= uint32(Pixel); | ||||
|     //tracer.push_trace('vesa32.DrawPixel.exit'); | ||||
| end; | ||||
|  | ||||
| procedure init(DrawRoutines : PDrawRoutines); | ||||
| begin | ||||
|     tracer.push_trace('vesa32.init.enter'); | ||||
|     DrawRoutines^.DrawPixel:= @DrawPixel; | ||||
|     tracer.push_trace('vesa32.init.exit'); | ||||
| end; | ||||
|  | ||||
| end. | ||||
							
								
								
									
										43
									
								
								src/driver/video/vesa8.pas
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								src/driver/video/vesa8.pas
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,43 @@ | ||||
| //  Copyright 2021 Kieron Morris | ||||
| // | ||||
| //  Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| //  you may not use this file except in compliance with the License. | ||||
| //  You may obtain a copy of the License at | ||||
| // | ||||
| //  http://www.apache.org/licenses/LICENSE-2.0 | ||||
| // | ||||
| //  Unless required by applicable law or agreed to in writing, software | ||||
| //  distributed under the License is distributed on an "AS IS" BASIS, | ||||
| //  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| //  See the License for the specific language governing permissions and | ||||
| //  limitations under the License. | ||||
|  | ||||
| {  | ||||
| 	Driver->Video->VESA8 - Implementation of VESA 8bpp draw routines for the VESA Driver. | ||||
| 	 | ||||
| 	@author(Kieron Morris <kjm@kieronmorris.me>) | ||||
| } | ||||
| unit vesa8; | ||||
|  | ||||
| interface | ||||
|  | ||||
| uses | ||||
|     videotypes, vesa, tracer, color; | ||||
|  | ||||
| //Init the draw routines by providing what we support through the DrawRoutines struct. | ||||
| procedure init(DrawRoutines : PDrawRoutines); | ||||
|  | ||||
| implementation | ||||
|  | ||||
| procedure DrawPixel(Buffer : PVideoBuffer; X : uint32; Y : uint32; Pixel : TRGB32); | ||||
| begin | ||||
|  | ||||
| end; | ||||
|  | ||||
| procedure init(DrawRoutines : PDrawRoutines); | ||||
| begin | ||||
|     tracer.push_trace('vesa8.init.enter');    | ||||
|     tracer.push_trace('vesa8.init.exit');   | ||||
| end; | ||||
|  | ||||
| end. | ||||
							
								
								
									
										318
									
								
								src/driver/video/video.pas
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										318
									
								
								src/driver/video/video.pas
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,318 @@ | ||||
| //  Copyright 2021 Kieron Morris | ||||
| // | ||||
| //  Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| //  you may not use this file except in compliance with the License. | ||||
| //  You may obtain a copy of the License at | ||||
| // | ||||
| //  http://www.apache.org/licenses/LICENSE-2.0 | ||||
| // | ||||
| //  Unless required by applicable law or agreed to in writing, software | ||||
| //  distributed under the License is distributed on an "AS IS" BASIS, | ||||
| //  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| //  See the License for the specific language governing permissions and | ||||
| //  limitations under the License. | ||||
|  | ||||
| {  | ||||
| 	Driver->Video->Video - Provides an abstract rasterization/drawing interface. | ||||
| 	 | ||||
| 	@author(Kieron Morris <kjm@kieronmorris.me>) | ||||
| } | ||||
| unit video; | ||||
|  | ||||
| interface | ||||
|  | ||||
| uses | ||||
|     lmemorymanager, tracer, color, videotypes, hashmap, util, texture; | ||||
|  | ||||
| procedure init(); | ||||
| procedure DrawPixel(X : uint32; Y : uint32; Pixel : TRGB32); | ||||
| procedure DrawLine(x1,y1,x2,y2 : uint32; thickness : uint32; Color : TRGB32); | ||||
| procedure DrawRect(x1,y1,x2,y2 : uint32; line_thickness : uint32; Color : TRGB32); | ||||
| procedure FillRect(x1,y1,x2,y2 : uint32; line_thickness : uint32; Line_Color : TRGB32; Fill_Color : TRGB32); | ||||
| procedure Flush(); | ||||
|  | ||||
| function register(DriverIdentifier : pchar; EnableCallback : FEnableDriver) : boolean; | ||||
| function enable(DriverIdentifier : pchar) : boolean; | ||||
|  | ||||
| function frontBufferWidth : uint32; | ||||
| function frontBufferHeight : uint32; | ||||
| function frontBufferBpp : uint8; | ||||
| function backBufferWidth : uint32; | ||||
| function backBufferHeight : uint32; | ||||
| function backBufferBpp : uint8; | ||||
|  | ||||
| Procedure basicFDrawTexture(Buffer : PVideoBuffer; X : uint32; Y : uint32; Texture : PTexture); | ||||
|  | ||||
| implementation | ||||
|  | ||||
| Procedure dummyFDrawPixel(Buffer : PVideoBuffer; X : uint32; Y : uint32; Pixel : TRGB32); | ||||
| begin | ||||
|     tracer.push_trace('video.dummyFDrawPixel.enter'); | ||||
|     //Do nothing, this is the most basic function that must be implemented by a driver. | ||||
| end; | ||||
|  | ||||
| Procedure basicFFlush(FrontBuffer : PVideoBuffer; BackBuffer : PVideoBuffer); | ||||
| var | ||||
|     idx : uint32; | ||||
|     Back,Front : puint32; | ||||
|     BufferSize : uint32; | ||||
|  | ||||
| const | ||||
|     COPY_WIDTH = 32; | ||||
|  | ||||
| begin | ||||
|     //tracer.push_trace('video.basicFFlush.enter'); | ||||
|     If not(FrontBuffer^.Initialized and BackBuffer^.Initialized) then exit; | ||||
|     if (BackBuffer^.Width > FrontBuffer^.Width) or (BackBuffer^.Height > FrontBuffer^.Height) then exit; | ||||
|     Back:= puint32(BackBuffer^.Location); | ||||
|     Front:= puint32(FrontBuffer^.Location); | ||||
|     BufferSize:= ( (BackBuffer^.Width * BackBuffer^.Height * BackBuffer^.BitsPerPixel ) div COPY_WIDTH ) - 1; | ||||
|     for idx:=0 to BufferSize do begin | ||||
|         Front[idx]:= Back[idx]; | ||||
|     end; | ||||
| end; | ||||
|  | ||||
| Procedure basicFDrawTexture(Buffer : PVideoBuffer; X : uint32; Y : uint32; Texture : PTexture); | ||||
| var | ||||
|     i, j : uint32; | ||||
|  | ||||
| begin | ||||
|     //Draw texture to Buffer at x and y | ||||
|     for i:=0 to Texture^.Height - 1 do begin | ||||
|         for j:=0 to Texture^.Width - 1 do begin | ||||
|             DrawPixel(X + j, Y + i, Texture^.Pixels[(i * Texture^.Width) + j]); | ||||
|         end; | ||||
|     end; | ||||
| end; | ||||
|  | ||||
| procedure basicFDrawLine(Buffer : PVideoBuffer; x1,y1,x2,y2 : uint32; thickness : uint32; Color : TRGB32); | ||||
| var | ||||
|     X, Y, DX, DY, DX1, DY1, PX, PY, XE, YE, I : sint32; | ||||
|  | ||||
| begin | ||||
|     tracer.push_trace('video.basicFDrawLine.enter'); | ||||
|  | ||||
|     if(x1 = x2) then begin | ||||
|         for Y:=Y1 to Y2 do begin | ||||
|             DrawPixel(X1,Y,Color); | ||||
|         end; | ||||
|     end else if (y1 = y2) then begin | ||||
|         for X:=X1 to X2 do begin | ||||
|             DrawPixel(X,Y1,Color); | ||||
|         end; | ||||
|     end else begin | ||||
|         DX:= X2 - X1; | ||||
|         DY:= Y2 - Y1; | ||||
|  | ||||
|         DX1:= util.abs(DX); | ||||
|         DY1:= util.abs(DY); | ||||
|  | ||||
|         PX:= 2 * DY1 - DX1; | ||||
|         PY:= 2 * DX1 - DY1; | ||||
|  | ||||
|         if (DY1 <= DX1) then begin | ||||
|             if (dx >= 0) then begin | ||||
|                 X:= X1; Y:= Y1; XE:= X2; | ||||
|             end else begin | ||||
|                 X:= X2; Y:= Y2; XE:= X1; | ||||
|             end; | ||||
|  | ||||
|             DrawPixel(X, Y, Color); | ||||
|  | ||||
|             I:=0; | ||||
|             while (x < xe) do begin | ||||
|                 X:= X + 1; | ||||
|                 if (PX < 0) then begin | ||||
|                     PX:= PX + 2 + DY1;  | ||||
|                 end else begin | ||||
|                     if(((dx < 0) and (dy < 0)) OR ((dx > 0) and (dy > 0))) then begin | ||||
|                         Y:= Y + 1;   | ||||
|                     end else begin | ||||
|                         Y:= Y - 1; | ||||
|                     end; | ||||
|                     PX:= PX + 2 * (DY1 - DX1); | ||||
|                 end; | ||||
|                 DrawPixel(X,Y,Color); | ||||
|                 I:= I + 1; | ||||
|             end; | ||||
|         end else begin | ||||
|             if (DY >= 0) then begin | ||||
|                 X:= X1; Y:= Y1; YE:= Y2;  | ||||
|             end else begin | ||||
|                 X:= X2; Y:= Y2; YE:= Y1; | ||||
|             end; | ||||
|  | ||||
|             DrawPixel(X, Y, Color); | ||||
|  | ||||
|             I:=0; | ||||
|             while (Y < YE) do begin | ||||
|                 Y:= Y + 1; | ||||
|                 if (PY <= 0) then begin | ||||
|                     PY:= PY + 2 * DX1; | ||||
|                 end else begin | ||||
|                     if (((dx < 0) and (dy < 0)) OR ((dx > 0) and (dy > 0))) then begin | ||||
|                         X:= X + 1;  | ||||
|                     end else begin | ||||
|                         X:= X - 1; | ||||
|                     end; | ||||
|                     PY:= PY + 2 * (dx1 - dy1); | ||||
|                 end; | ||||
|                 DrawPixel(X,Y,Color); | ||||
|                 I:= I + 1; | ||||
|             end; | ||||
|         end; | ||||
|     end; | ||||
| end; | ||||
|  | ||||
| procedure basicFDrawRect(Buffer : PVideoBuffer; x1,y1,x2,y2 : uint32; line_thickness : uint32; Color : TRGB32); | ||||
| begin | ||||
|     tracer.push_trace('video.basicFDrawRect.enter'); | ||||
|     DrawLine(x1,y1,x2,y1,line_thickness,Color); | ||||
|     DrawLine(x1,y2,x2,y2,line_thickness,Color); | ||||
|     DrawLine(x1,y1,x1,y2,line_thickness,Color); | ||||
|     DrawLine(x2,y1,x2,y2,line_thickness,Color); | ||||
| end; | ||||
|  | ||||
| procedure basicFFillRect(Buffer : PVideoBuffer; x1,y1,x2,y2 : uint32; line_thickness : uint32; Line_Color : TRGB32; Fill_Color : TRGB32); | ||||
| var | ||||
|     Y : uint32; | ||||
|  | ||||
| begin | ||||
|     tracer.push_trace('video.basicFFillRect.enter');    | ||||
|     for Y:=y1 to y2 do begin | ||||
|         DrawLine(x1,y,x2,y,line_thickness,Fill_Color); | ||||
|     end; | ||||
|     DrawRect(x1,y1,x2,y2,line_thickness,Line_Color); | ||||
| end; | ||||
|  | ||||
| var | ||||
|     VideoInterface : TVideoInterface; | ||||
|     DriverMap      : PHashMap; | ||||
|  | ||||
| procedure init(); | ||||
| var | ||||
|     RGB : TRGB32; | ||||
|     x,y : uint32; | ||||
|  | ||||
| begin | ||||
|     tracer.push_trace('video.init.enter'); | ||||
|     //Ensure the frontbuffer is empty & nil, ready for initialization by a driver. | ||||
|     VideoInterface.FrontBuffer.Initialized:= false; | ||||
|     VideoInterface.FrontBuffer.Width:= 0; | ||||
|     VideoInterface.FrontBuffer.Height:= 0; | ||||
|     VideoInterface.FrontBuffer.BitsPerPixel:= 0; | ||||
|     VideoInterface.FrontBuffer.Location:= 0; | ||||
|  | ||||
|     //Ensure the backbuffer is empty & nil, ready for initialization by a driver. | ||||
|     VideoInterface.BackBuffer.Initialized:= false; | ||||
|     VideoInterface.BackBuffer.Width:= 0; | ||||
|     VideoInterface.BackBuffer.Height:= 0; | ||||
|     VideoInterface.BackBuffer.BitsPerPixel:= 0; | ||||
|     VideoInterface.BackBuffer.Location:= 0; | ||||
|  | ||||
|     //Set the default buffer to be the FrontBuffer, assume no double buffering to begin with. | ||||
|     VideoInterface.DefaultBuffer:= @VideoInterface.FrontBuffer; | ||||
|  | ||||
|     { Set the draw routines to point to dummy/empty routines. | ||||
|       Calls will still succeed but do nothing until a driver has registered. } | ||||
|     VideoInterface.DrawRoutines.DrawPixel:= @dummyFDrawPixel; | ||||
|     VideoInterface.DrawRoutines.Flush:= @basicFFlush; | ||||
|     VideoInterface.DrawRoutines.DrawLine:= @basicFDrawLine; | ||||
|     VideoInterface.DrawRoutines.DrawRect:= @basicFDrawRect; | ||||
|     VideoInterface.DrawRoutines.FillRect:= @basicFFillRect; | ||||
|  | ||||
|     //Initialize our 'DriverMap', a hashmap of loadable display drivers. | ||||
|     DriverMap:= hashmap.new; | ||||
|  | ||||
|     tracer.push_trace('video.init.exit'); | ||||
| end; | ||||
|  | ||||
| function register(DriverIdentifier : pchar; EnableCallback : FEnableDriver) : boolean; | ||||
| var | ||||
|     Value : Void; | ||||
|  | ||||
| begin | ||||
|     tracer.push_trace('video.register.enter'); | ||||
|     register:= false; | ||||
|     Value:= Hashmap.get(DriverMap, DriverIdentifier); | ||||
|     if (Value = nil) then begin | ||||
|         Hashmap.add(DriverMap, DriverIdentifier, void(EnableCallback)); | ||||
|         register:= true; | ||||
|     end; | ||||
|     tracer.push_trace('video.register.exit'); | ||||
| end; | ||||
|  | ||||
| function enable(DriverIdentifier : pchar) : boolean; | ||||
| var | ||||
|     Value : Void; | ||||
|  | ||||
| begin | ||||
|     tracer.push_trace('video.enable.enter'); | ||||
|     enable:= false; | ||||
|     Value:= Hashmap.get(DriverMap, DriverIdentifier); | ||||
|     if (Value <> nil) then begin | ||||
|         enable:= FEnableDriver(Value)(@VideoInterface); | ||||
|     end; | ||||
|     tracer.push_trace('video.enable.exit'); | ||||
| end; | ||||
|  | ||||
| procedure DrawPixel(X : uint32; Y : uint32; Pixel : TRGB32); | ||||
| begin | ||||
|     //tracer.push_trace('video.DrawPixel.enter'); | ||||
|     VideoInterface.DrawRoutines.DrawPixel(VideoInterface.DefaultBuffer, X, Y, Pixel); | ||||
|     //tracer.push_trace('video.DrawPixel.exit'); | ||||
| end; | ||||
|  | ||||
| procedure Flush(); | ||||
| begin | ||||
|     //tracer.push_trace('video.Flush.enter'); | ||||
|     VideoInterface.DrawRoutines.Flush(@VideoInterface.FrontBuffer, @VideoInterface.BackBuffer); | ||||
|     //tracer.push_trace('video.Flush.exit'); | ||||
| end; | ||||
|  | ||||
| procedure DrawLine(x1,y1,x2,y2 : uint32; thickness : uint32; Color : TRGB32); | ||||
| begin | ||||
|     VideoInterface.DrawRoutines.DrawLine(VideoInterface.DefaultBuffer,x1,y1,x2,y2,thickness,Color); | ||||
| end; | ||||
|  | ||||
| procedure DrawRect(x1,y1,x2,y2 : uint32; line_thickness : uint32; Color : TRGB32); | ||||
| begin | ||||
|     VideoInterface.DrawRoutines.DrawRect(VideoInterface.DefaultBuffer,x1,y1,x2,y2,line_thickness,color); | ||||
| end; | ||||
|  | ||||
| procedure FillRect(x1,y1,x2,y2 : uint32; line_thickness : uint32; Line_Color : TRGB32; Fill_Color : TRGB32); | ||||
| begin | ||||
|     VideoInterface.DrawRoutines.FillRect(VideoInterface.DefaultBuffer,x1,y1,x2,y2,line_thickness,Line_Color,Fill_Color); | ||||
| end; | ||||
|  | ||||
| function frontBufferWidth : uint32; | ||||
| begin | ||||
|     frontBufferWidth:= VideoInterface.FrontBuffer.Width; | ||||
| end; | ||||
|  | ||||
| function frontBufferHeight : uint32; | ||||
| begin | ||||
|     frontBufferHeight:= VideoInterface.FrontBuffer.Height; | ||||
| end; | ||||
|  | ||||
| function frontBufferBpp : uint8; | ||||
| begin | ||||
|     frontBufferBpp:= VideoInterface.FrontBuffer.BitsPerPixel; | ||||
| end; | ||||
|  | ||||
| function backBufferWidth : uint32; | ||||
| begin | ||||
|     backBufferWidth:= VideoInterface.BackBuffer.Width; | ||||
| end; | ||||
|  | ||||
| function backBufferHeight : uint32; | ||||
| begin | ||||
|     backBufferHeight:= VideoInterface.BackBuffer.Height; | ||||
| end; | ||||
|  | ||||
| function backBufferBpp : uint8; | ||||
| begin | ||||
|     backBufferBpp:= VideoInterface.BackBuffer.BitsPerPixel; | ||||
| end; | ||||
|  | ||||
| end. | ||||
							
								
								
									
										92
									
								
								src/driver/video/videotypes.pas
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								src/driver/video/videotypes.pas
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,92 @@ | ||||
| //  Copyright 2021 Kieron Morris | ||||
| // | ||||
| //  Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| //  you may not use this file except in compliance with the License. | ||||
| //  You may obtain a copy of the License at | ||||
| // | ||||
| //  http://www.apache.org/licenses/LICENSE-2.0 | ||||
| // | ||||
| //  Unless required by applicable law or agreed to in writing, software | ||||
| //  distributed under the License is distributed on an "AS IS" BASIS, | ||||
| //  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| //  See the License for the specific language governing permissions and | ||||
| //  limitations under the License. | ||||
|  | ||||
| {  | ||||
| 	Driver->Video->Videotypes - Provides types relating to the video framework & driver interface. | ||||
| 	 | ||||
| 	@author(Kieron Morris <kjm@kieronmorris.me>) | ||||
| } | ||||
| unit videotypes; | ||||
|  | ||||
| interface | ||||
|  | ||||
| uses | ||||
|     color; | ||||
|  | ||||
| type | ||||
|     //Arbitrary pointer to a raw video buffer in memory | ||||
|     VideoBuffer = uint32; | ||||
|  | ||||
|     //Struct representing a Memory Mapped Video Buffer | ||||
|     TVideoBuffer = record | ||||
|         //Has this buffer been initialized? Has it been paged/created in memory? | ||||
|         Initialized     : Boolean; | ||||
|         //Location of the video buffer in memory as a QWORD. | ||||
|         Location        : VideoBuffer; | ||||
|         //How many bits per pixel? | ||||
|         BitsPerPixel    : uint8; | ||||
|         //Width of the buffer. | ||||
|         Width           : uint32; | ||||
|         //Height of the buffer. | ||||
|         Height          : uint32; | ||||
|     end; | ||||
|     //Pointer to a video buffer | ||||
|     PVideoBuffer = ^TVideoBuffer; | ||||
|  | ||||
|     //(Abstract) Draw a pixel to screenspace | ||||
|     FDrawPixel = procedure(Buffer : PVideoBuffer; X : uint32; Y : uint32; Pixel : TRGB32); | ||||
|     //(Abstract) Flush backbuffer to MMIO Buffer | ||||
|     FFlush     = procedure(FrontBuffer : PVideoBuffer; BackBuffer : PVideoBuffer); | ||||
|     //(Abstract) Draw a line to the screen | ||||
|     FDrawLine = procedure(Buffer : PVideoBuffer; x1,y1,x2,y2 : uint32; thickness : uint32; Color : TRGB32); | ||||
|     //(Abstract) Draw a rect to the screen | ||||
|     FDrawRect = procedure(Buffer : PVideoBuffer; x1,y1,x2,y2 : uint32; line_thickness : uint32; Color : TRGB32); | ||||
|     //(Abstract) Draw a filled rect to the screen | ||||
|     FFillRect = procedure(Buffer : PVideoBuffer; x1,y1,x2,y2 : uint32; line_thickness : uint32; Line_Color : TRGB32; Fill_Color : TRGB32); | ||||
|  | ||||
|     //Routines for drawing to the screen | ||||
|     TDrawRoutines = record | ||||
|         DrawPixel : FDrawPixel; | ||||
|         Flush     : FFlush; | ||||
|         DrawLine  : FDrawLine; | ||||
|         DrawRect  : FDrawRect; | ||||
|         FillRect  : FFillRect; | ||||
|     end; | ||||
|     //Pointer to drawing routines | ||||
|     PDrawRoutines = ^TDrawRoutines; | ||||
|  | ||||
|     //Struct representing the whole video driver. | ||||
|     TVideoInterface = record | ||||
|         //Default buffer to be used when rendering, front buffer for no double buffering, back buffer otherwise. | ||||
|         DefaultBuffer   : PVideoBuffer; | ||||
|         //Memory Mapped IO Buffer for raw rasterization. | ||||
|         FrontBuffer     : TVideoBuffer; | ||||
|         //Back buffer used for double buffering, this is flushed to FrontBuffer with flush(); | ||||
|         BackBuffer      : TVideoBuffer; | ||||
|         //Drawing Routines | ||||
|         DrawRoutines    : TDrawRoutines; | ||||
|     end; | ||||
|     //Pointer to a video interface struct. | ||||
|     PVideoInterface = ^TVideoInterface; | ||||
|  | ||||
|     //(Abstract) Enable method for a driver, called from video to enable driver. | ||||
|     FEnableDriver = function(VideoInterface : PVideoInterface) : boolean; | ||||
|     //(Abstract) Register driver, called from a driver to register with the video interface. | ||||
|     FRegisterDriver = function(DriverIdentifier : pchar; EnableCallback : FEnableDriver) : boolean; | ||||
|     //(Abstract) Init driver, called from _somewhere_ to start the driver and register it with the video interface. | ||||
|     FInitDriver = procedure(Register : FRegisterDriver); | ||||
|  | ||||
| implementation | ||||
|  | ||||
| end. | ||||
							
								
								
									
										63
									
								
								src/include/color.pas
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								src/include/color.pas
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,63 @@ | ||||
| //  Copyright 2021 Kieron Morris | ||||
| // | ||||
| //  Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| //  you may not use this file except in compliance with the License. | ||||
| //  You may obtain a copy of the License at | ||||
| // | ||||
| //  http://www.apache.org/licenses/LICENSE-2.0 | ||||
| // | ||||
| //  Unless required by applicable law or agreed to in writing, software | ||||
| //  distributed under the License is distributed on an "AS IS" BASIS, | ||||
| //  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| //  See the License for the specific language governing permissions and | ||||
| //  limitations under the License. | ||||
|  | ||||
| {  | ||||
| 	Include->Color - Provides types relating to color/graphics. | ||||
| 	 | ||||
| 	@author(Kieron Morris <kjm@kieronmorris.me>) | ||||
| } | ||||
| unit color; | ||||
|  | ||||
| interface | ||||
|  | ||||
| type | ||||
|     TRGB32 = bitpacked record | ||||
|         B : uint8; | ||||
|         G : uint8; | ||||
|         R : uint8; | ||||
|         A : uint8; | ||||
|     end; | ||||
|     PRGB32 = ^TRGB32; | ||||
|  | ||||
|     TRGB24 = bitpacked record | ||||
|         B : uint8; | ||||
|         G : uint8; | ||||
|         R : uint8; | ||||
|     end; | ||||
|     PRGB23 = ^TRGB24; | ||||
|  | ||||
|     TRGB16 = bitpacked record | ||||
|         B : UBit5; | ||||
|         G : UBit6; | ||||
|         R : UBit5; | ||||
|     end; | ||||
|     PRGB16 = ^TRGB16; | ||||
|  | ||||
|     TRGB8 = bitpacked record | ||||
|         B : UBit2; | ||||
|         G : UBit4; | ||||
|         R : UBit2; | ||||
|     end; | ||||
|     PRGB8 = ^TRGB8; | ||||
|  | ||||
| const | ||||
|     black : TRGB32 = (B: 000; G: 000; R: 000; A: 000); | ||||
|     white : TRGB32 = (B: 255; G: 255; R: 255; A: 000); | ||||
|     red   : TRGB32 = (B: 000; G: 000; R: 255; A: 000); | ||||
|     green : TRGB32 = (B: 000; G: 255; R: 000; A: 000); | ||||
|     blue  : TRGB32 = (B: 255; G: 000; R: 000; A: 000); | ||||
|  | ||||
| implementation | ||||
|  | ||||
| end. | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -143,6 +143,22 @@ type | ||||
|  | ||||
|     PText = ^Text; | ||||
|  | ||||
| var | ||||
|   AK_START : uint32; external name 'kernel_start'; | ||||
|   AK_END   : uint32; external name 'kernel_end'; | ||||
|   ASURO_KERNEL_START : uint32; | ||||
|   ASURO_KERNEL_END   : uint32; | ||||
|   ASURO_KERNEL_SIZE  : uint32; | ||||
|  | ||||
| procedure init(); | ||||
|  | ||||
| implementation | ||||
|  | ||||
| procedure init(); | ||||
| begin | ||||
|     ASURO_KERNEL_START := uint32(@AK_START); | ||||
|     ASURO_KERNEL_END := uint32(@AK_END); | ||||
|     ASURO_KERNEL_SIZE:= ASURO_KERNEL_END - ASURO_KERNEL_START; | ||||
| end; | ||||
|  | ||||
| end. | ||||
|   | ||||
							
								
								
									
										67
									
								
								src/include/targa.pas
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								src/include/targa.pas
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,67 @@ | ||||
| unit targa; | ||||
|  | ||||
| interface | ||||
|  | ||||
| uses | ||||
|     lmemorymanager, texture; | ||||
|  | ||||
| type | ||||
|     TTARGAColor = packed record | ||||
|       b, g, r, a: uint8; | ||||
|     end; | ||||
|     PTARGAColor = ^TTARGAColor; | ||||
|  | ||||
|     TTARGAHeader = packed record | ||||
|         Magic: uint8; | ||||
|         ColorMapType: uint8; | ||||
|         ImageType: uint8; | ||||
|         ColorMapOrigin: uint16; | ||||
|         ColorMapLength: uint16; | ||||
|         ColorMapDepth: uint8; | ||||
|         XOrigin: uint16; | ||||
|         YOrigin: uint16; | ||||
|         Width: uint16; | ||||
|         Height: uint16; | ||||
|         PixelDepth: uint8; | ||||
|         ImageDescriptor: uint8; | ||||
|         Data : PTARGAColor; | ||||
|     end; | ||||
|     PTARGAHeader = ^TTARGAHeader; | ||||
|  | ||||
| Function Parse(buffer : puint8; len : uint32) : PTexture; | ||||
|  | ||||
| implementation | ||||
|  | ||||
| Function Parse(buffer : puint8; len : uint32) : PTexture; | ||||
| var | ||||
|     header : PTARGAHeader; | ||||
|     i, j : uint32; | ||||
|     data : PTARGAColor; | ||||
|     tex : PTexture; | ||||
|  | ||||
| begin | ||||
|     if (len < sizeof(TTARGAHeader)) then exit; | ||||
|  | ||||
|     header := PTARGAHeader(buffer); | ||||
|     if (header^.Magic <> $0) or (header^.ImageType <> 2) or (header^.PixelDepth <> 32) then exit; | ||||
|      | ||||
|     //Create a new texture | ||||
|     tex:= texture.newTexture(header^.Width, header^.Height); | ||||
|     tex^.Width:= header^.Width; | ||||
|     tex^.Height:= header^.Height; | ||||
|      | ||||
|     //Copy the data | ||||
|     data := PTARGAColor(header^.Data); | ||||
|     for i := 0 to header^.Height - 1 do begin | ||||
|         for j := 0 to header^.Width - 1 do begin | ||||
|             tex^.Pixels[i * header^.Width + j].r := data^.r; | ||||
|             tex^.Pixels[i * header^.Width + j].g := data^.g; | ||||
|             tex^.Pixels[i * header^.Width + j].b := data^.b; | ||||
|             tex^.Pixels[i * header^.Width + j].a := data^.a; | ||||
|         end; | ||||
|     end; | ||||
|  | ||||
|     Parse := tex; | ||||
| end; | ||||
|  | ||||
| end. | ||||
							
								
								
									
										34
									
								
								src/include/texture.pas
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								src/include/texture.pas
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | ||||
| unit texture; | ||||
|  | ||||
| interface | ||||
|  | ||||
| uses | ||||
|     lmemorymanager, color; | ||||
|  | ||||
| type | ||||
|     TTexture = packed record | ||||
|         Width  : uint32;  | ||||
|         Height : uint32;  | ||||
|         Size   : uint32; | ||||
|         Pixels : PRGB32; | ||||
|     end; | ||||
|     PTexture = ^TTexture; | ||||
|  | ||||
| Function newTexture(width, height: uint32): PTexture; | ||||
|  | ||||
| implementation | ||||
|  | ||||
| Function newTexture(width, height: uint32): PTexture; | ||||
| var | ||||
|     texture: PTexture; | ||||
|  | ||||
| begin | ||||
|     texture:= PTexture(kalloc(sizeof(TTexture))); | ||||
|     texture^.Pixels:= PRGB32(kalloc(width * height * sizeof(TRGB32))); | ||||
|     texture^.Width:= width; | ||||
|     texture^.Height:= height; | ||||
|     texture^.Size:= width * height; | ||||
|     newTexture:= texture; | ||||
| end; | ||||
|  | ||||
| end. | ||||
| @@ -49,6 +49,7 @@ procedure io_wait; | ||||
|  | ||||
| procedure memset(location : uint32; value : uint8; size : uint32); | ||||
| procedure memcpy(source : uint32; dest : uint32; size : uint32); | ||||
| procedure __SSE_128_memcpy(source : uint32; dest : uint32); | ||||
|  | ||||
| procedure printmemory(source : uint32; length : uint32; col : uint32; delim : PChar; offset_row : boolean); | ||||
| procedure printmemoryWND(source : uint32; length : uint32; col : uint32; delim : PChar; offset_row : boolean; WND : HWND); | ||||
| @@ -80,6 +81,8 @@ function RorDWord(AValue : uint32; Dist : uint8) : uint32; | ||||
|  | ||||
| function MsSinceSystemBoot : uint64; | ||||
|  | ||||
| function abs(x : sint32) : uint32; | ||||
|  | ||||
| var | ||||
|     endptr : uint32; external name '__end'; | ||||
|     stack  : uint32; external name 'KERNEL_STACK'; | ||||
| @@ -89,6 +92,15 @@ implementation | ||||
| uses | ||||
|     console, RTC, cpu, serial, strings, isr_types; | ||||
|  | ||||
| function abs(x : sint32) : uint32; | ||||
| var | ||||
|     y : uint32; | ||||
|  | ||||
| begin | ||||
|     y:= x SHR 31; | ||||
|     abs:= (x XOR y) - y; | ||||
| end; | ||||
|  | ||||
| function MsSinceSystemBoot : uint64; | ||||
| begin | ||||
|     MsSinceSystemBoot:= div6432(getTSC, (CPUID.ClockSpeed.Hz div 1000)); | ||||
| @@ -116,6 +128,14 @@ begin | ||||
|     div6432:= (r0 SHL 32) OR r4; | ||||
| end; | ||||
|  | ||||
| procedure __SSE_128_memcpy(source : uint32; dest : uint32); assembler; | ||||
| asm | ||||
|     MOV EAX, Source | ||||
|     MOVAPS XMM1, [EAX] | ||||
|     MOV EAX, Dest | ||||
|     MOVAPS [EAX], XMM1 | ||||
| end; | ||||
|  | ||||
| function switchendian16(b : uint16) : uint16; | ||||
| begin | ||||
|     switchendian16:= ((b AND $FF00) SHR 8) OR ((b AND $00FF) SHL 8); | ||||
| @@ -335,7 +355,6 @@ begin | ||||
|      //serial.sendString('[outb]'); | ||||
|      //serial.sendHex(port); | ||||
|      //serial.sendHex(val); | ||||
|      psleep(1); | ||||
|      asm | ||||
|           PUSH EAX | ||||
|           PUSH EDX | ||||
|   | ||||
| @@ -33,8 +33,6 @@ procedure init; | ||||
| procedure registerISR(INT_N : uint8; callback : TISRHook); | ||||
|  | ||||
| implementation | ||||
| uses  | ||||
| console; | ||||
|  | ||||
| var | ||||
|     Hooks : TISRHookArray; | ||||
| @@ -58,13 +56,6 @@ 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; | ||||
|   | ||||
| @@ -40,13 +40,10 @@ uses | ||||
|      testdriver, | ||||
|      E1000, | ||||
|      IDE, | ||||
|     //  storagemanagement, | ||||
|     //  drivemanager, | ||||
|      volumemanager, | ||||
|      storagemanagement, | ||||
|      lists, | ||||
|      net, | ||||
|     //  fat32, | ||||
|     //  flatfs, | ||||
|      fat32, | ||||
|      isrmanager, | ||||
|      faults, | ||||
|      fonts, | ||||
| @@ -58,7 +55,7 @@ uses | ||||
|      rand, | ||||
|      terminal, | ||||
|      hashmap, vfs,  | ||||
|      AHCI; | ||||
|      video, vesa, doublebuffer, color; | ||||
|   | ||||
| procedure kmain(mbinfo: Pmultiboot_info_t; mbmagic: uint32); stdcall; | ||||
|   | ||||
| @@ -128,7 +125,15 @@ var | ||||
|  | ||||
|    HM              : PHashMap; | ||||
|  | ||||
|    colour          : TRGB32; | ||||
|  | ||||
|    array1          : Array[0..255] of char; | ||||
|    array2          : Array[0..255] of char; | ||||
|     | ||||
| begin | ||||
|      { Init the base system unit } | ||||
|      System.init(); | ||||
|  | ||||
|      { Serial Init } | ||||
|      serial.init(); | ||||
|  | ||||
| @@ -136,6 +141,8 @@ begin | ||||
|      multibootinfo:= mbinfo; | ||||
|      multibootmagic:= mbmagic; | ||||
|  | ||||
|     //video.init(); | ||||
|  | ||||
|      { Ensure tracer is frozen } | ||||
|      tracer.freeze(); | ||||
|  | ||||
| @@ -202,6 +209,53 @@ begin | ||||
|      { Call Tracer } | ||||
|      tracer.init(); | ||||
|  | ||||
|      video.init(); | ||||
|      vesa.init(@video.register); | ||||
|      doublebuffer.init(@video.register); | ||||
|      video.enable('VESA'); | ||||
|      video.enable('BASIC_DOUBLE_BUFFER'); | ||||
|      colour:= color.white; | ||||
|  | ||||
|     //  serial.sendHex(uint32(@array1[8])); | ||||
|     //  serial.sendHex(uint32(@array2[8])); | ||||
|     //  for i:=8 to 23 do begin | ||||
|     //     array2[i]:= '?'; | ||||
|     //  end; | ||||
|     //  array2[24]:= #0; | ||||
|  | ||||
|     //  array1[8]:= 'H'; | ||||
|     //  array1[9]:= 'e'; | ||||
|     //  array1[10]:= 'l'; | ||||
|     //  array1[11]:= 'l'; | ||||
|     //  array1[12]:= 'o'; | ||||
|     //  array1[13]:= 'w'; | ||||
|     //  array1[14]:= 'o'; | ||||
|     //  array1[15]:= 'r'; | ||||
|     //  array1[16]:= 'l'; | ||||
|     //  array1[17]:= 'd'; | ||||
|     //  array1[18]:= '1'; | ||||
|     //  array1[19]:= '2'; | ||||
|     //  array1[20]:= '3'; | ||||
|     //  array1[21]:= '4'; | ||||
|     //  array1[22]:= '5'; | ||||
|     //  array1[23]:= '!'; | ||||
|     //  __SSE_128_memcpy(uint32(@array1[8]), uint32(@array2[8])); | ||||
|     //  serial.sendString(pchar(@array2[8])); | ||||
|  | ||||
|      for i:=0 to video.frontBufferWidth-1 do begin | ||||
|         for z:=0 to video.frontBufferHeight-1 do begin | ||||
|             video.DrawPixel(i, z, colour); | ||||
|         end; | ||||
|      end; | ||||
|  | ||||
|      video.DrawLine(50,50,100,100,1,color.black); | ||||
|      video.DrawRect(50,150,100,200,1,color.black); | ||||
|      video.FillRect(50,250,100,300,1,color.black,color.red); | ||||
|      video.DrawLine(50,350,100,350,1,color.black); | ||||
|      video.Flush(); | ||||
|  | ||||
|      while true do begin end; | ||||
|  | ||||
|      { VFS Init } | ||||
|      vfs.init(); | ||||
|  | ||||
| @@ -209,9 +263,7 @@ begin | ||||
|      tracer.push_trace('kmain.DRVMGMT'); | ||||
|      drivermanagement.init(); | ||||
|      tracer.push_trace('kmain.STRMGMT'); | ||||
|      //  storagemanagement.init(); | ||||
|     //  drivemanager.init(); | ||||
|     //  volumemanager.init(); | ||||
|      storagemanagement.init(); | ||||
|  | ||||
|      { Hook Timer for Ticks } | ||||
|      tracer.push_trace('kmain.TMR'); | ||||
| @@ -219,8 +271,7 @@ begin | ||||
|      TMR_0_ISR.hook(uint32(@bios_data_area.tick_update)); | ||||
|  | ||||
|      { Filsystems } | ||||
|     //  fat32.init(); | ||||
|     //  flatfs.init(); | ||||
|      fat32.init(); | ||||
|  | ||||
|      { Device Drivers } | ||||
|      tracer.push_trace('kmain.DEVDRV'); | ||||
| @@ -229,8 +280,7 @@ begin | ||||
|      mouse.init(); | ||||
|      testdriver.init(); | ||||
|      E1000.init(); | ||||
|     //  IDE.init(); | ||||
|      AHCI.init(); | ||||
|      IDE.init(); | ||||
|      console.outputln('KERNEL', 'DEVICE DRIVERS: INIT END.'); | ||||
|  | ||||
|      { Bus Drivers } | ||||
|   | ||||
| @@ -57,7 +57,6 @@ var | ||||
|  | ||||
| procedure init; | ||||
| function kalloc(size : uint32) : void; | ||||
| function kalloc(size : uint32; isPersistent : boolean) : void; | ||||
| function klalloc(size : uint32) : void; | ||||
| procedure klfree(size : uint32); //Todo ??? Profit? | ||||
| function kpalloc(address : uint32) : void; | ||||
| @@ -218,11 +217,6 @@ begin | ||||
|     //pop_trace; | ||||
| end; | ||||
|  | ||||
| function kalloc(size : uint32; isPersistent : boolean) : void; | ||||
| begin | ||||
|     kalloc:= kalloc(size); | ||||
| end; | ||||
|  | ||||
| procedure kfree(area : void); | ||||
| var | ||||
|     hp : PHeapPage; | ||||
|   | ||||
| @@ -22,15 +22,7 @@ unit edit; | ||||
| interface | ||||
|  | ||||
| uses | ||||
|     console, | ||||
|     keyboard, | ||||
|     lmemorymanager, | ||||
|     shell, | ||||
|     storagemanagement, | ||||
|     strings, | ||||
|     terminal, | ||||
|     tracer, | ||||
|     util; | ||||
|     console, terminal, keyboard, shell, strings, tracer, storagemanagement, lmemorymanager, util; | ||||
|  | ||||
| procedure init(); | ||||
|  | ||||
|   | ||||
| @@ -25,7 +25,7 @@ uses | ||||
|     tracer, console, | ||||
|     //progs | ||||
|     base64_prog, md5sum, shell, terminal,  | ||||
|      netlog, themer, | ||||
|     edit, netlog, themer, | ||||
|     memview, udpcat, dhclient, vbeinfo; | ||||
|  | ||||
| { Initialize all baked-in programs } | ||||
| @@ -43,8 +43,8 @@ begin | ||||
|     themer.init(); | ||||
|     tracer.push_trace('progmanager.netlog.init'); | ||||
|     netlog.init(); | ||||
|     // tracer.push_trace('progmanager.edit.init'); | ||||
|     // edit.init(); | ||||
|     tracer.push_trace('progmanager.edit.init'); | ||||
|     edit.init(); | ||||
|     tracer.push_trace('progmanager.udpcat.init'); | ||||
|     udpcat.init(); | ||||
|     tracer.push_trace('progmanager.md5sum.init'); | ||||
|   | ||||
| @@ -77,9 +77,9 @@ dd 0 | ||||
| dd 0 | ||||
| dd 0 | ||||
| dd 0 | ||||
| dd 1280 | ||||
| dd 1024 | ||||
| dd 16 | ||||
| dd 1600 | ||||
| dd 1200 | ||||
| dd 32 | ||||
|  | ||||
| ; | ||||
| ; Entrypoint | ||||
|   | ||||
		Reference in New Issue
	
	Block a user