From 726247a4f28e7d68a357eee48137ce64f6904c8d Mon Sep 17 00:00:00 2001 From: Tom-on64 Date: Tue, 2 Jan 2024 14:40:32 +0100 Subject: [PATCH] Began moving to C. (No C yet) If you want to build the assembly version, run the buildAsm.sh script Currently just setting up the bootloader. --- README.md | 2 +- asmSrc/boot.asm | 98 +++++++++++++++++++++++++++++++++ asmSrc/filetable.asm | 22 ++++++++ {src => asmSrc}/fs/calc.asm | 9 +++ {src => asmSrc}/fs/edit.asm | 91 +++++++++++++++++++----------- {src => asmSrc}/kernel.asm | 43 ++++++++++++++- {src => asmSrc}/lib/disk.asm | 51 +++++++---------- {src => asmSrc}/lib/print.asm | 0 {src => asmSrc}/lib/screen.asm | 0 asmSrc/testing/hello | Bin 0 -> 44 bytes asmSrc/testing/hello.asm | 25 +++++++++ build.sh | 10 ++-- buildAsm.sh | 32 +++++++++++ other/editor.png | Bin 34701 -> 31473 bytes src/boot.asm | 56 ++++++++++++++++++- 15 files changed, 365 insertions(+), 74 deletions(-) create mode 100644 asmSrc/boot.asm create mode 100644 asmSrc/filetable.asm rename {src => asmSrc}/fs/calc.asm (74%) rename {src => asmSrc}/fs/edit.asm (80%) rename {src => asmSrc}/kernel.asm (91%) rename {src => asmSrc}/lib/disk.asm (90%) rename {src => asmSrc}/lib/print.asm (100%) rename {src => asmSrc}/lib/screen.asm (100%) create mode 100644 asmSrc/testing/hello create mode 100644 asmSrc/testing/hello.asm create mode 100644 buildAsm.sh diff --git a/README.md b/README.md index 97dc021..cf1071d 100644 --- a/README.md +++ b/README.md @@ -38,4 +38,4 @@ _These images may be outdated, Idk_ ## Known issues - If you run the kernel.bin, loading files sorta breaks, because the program is running at 0x8000, but it thinks that it's at 0x2000 (i think) - If you run anything in the code editor and return to the kernel, you won't be able to load any files -- If you type only the starting letters of a command, it will still run (ie. 'he' will run help) +- If you type only the starting letters of a command, it will still run (i.e. 'he' will run help) diff --git a/asmSrc/boot.asm b/asmSrc/boot.asm new file mode 100644 index 0000000..efeec90 --- /dev/null +++ b/asmSrc/boot.asm @@ -0,0 +1,98 @@ +[org 0x7c00] +[bits 16] +[global start] + +start: + ; Setup + xor ax, ax + mov es, ax + mov ds, ax + mov bp, 0x7c00 + mov sp, bp + + ; Save drive number + mov byte [driveNumber], dl + + ; Print boot message + mov si, bootMsg + call print + mov dl, byte [driveNumber] + call printHex + mov si, newline + call print + + ; Read filetable from disk + mov bx, FILETAB_LOC ; Filetable location in memory + mov es, bx + xor bx, bx + + mov ah, 2 ; Read from disk + mov al, 1 ; Read 1 sector (512B) + + mov dl, byte [driveNumber] ; Drive number + mov ch, 0 ; Cylinder + mov dh, 0 ; Head + mov cl, 2 ; Sector + int 0x13 ; INT 0x13 - Disk operations + + jc readFail ; CF will be set if the read fails + + ; Read filetable from disk + mov bx, KERNEL_LOC ; Filetable location in memory + mov es, bx + xor bx, bx + + mov ah, 2 ; Read from disk + mov al, 4 ; Read 4 sectors (2kB) + + mov dl, byte [driveNumber] ; Drive number + mov ch, 0 ; Cylinder + mov dh, 0 ; Head + mov cl, 3 ; Sector + int 0x13 ; INT 0x13 - Disk operations + + jc readFail ; CF will be set if the read fails + + ; Setup segmentation + mov ax, KERNEL_LOC + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + + ; Setup stack + mov bp, 0xffff + mov sp, bp + mov ax, 0x9000 + mov ss, ax + + mov bl, byte [driveNumber] + + ; Setup video mode + mov ax, 0x0003 + int 0x10 + mov ah, 0x0b + mov bx, 0x0001 + int 0x10 + + ; Far jump to the kernel (never returns) + jmp KERNEL_LOC:0x0000 + +readFail: + mov si, diskErrorMsg + call print + cli + hlt + +%include "./src/lib/print.asm" + +driveNumber: db 0 + +bootMsg: db ENDL, "Booting kern.", ENDL, "Booting from drive ", 0 +diskErrorMsg: db "Disk Error!", ENDL, 0 + +FILETAB_LOC equ 0x1000 +KERNEL_LOC equ 0x2000 + +times 510-($-$$) db 0 +dw 0xaa55 diff --git a/asmSrc/filetable.asm b/asmSrc/filetable.asm new file mode 100644 index 0000000..800cd3a --- /dev/null +++ b/asmSrc/filetable.asm @@ -0,0 +1,22 @@ +; ------------------ +; Simple Filetable +; ------------------ +; +; 16 Byte entries +; 0-9 - Filename +; 10-12 - File extension (type) +; 13 - Number of filetable entries +; 14 - Starting Sector +; 15 - Filesize in sectors (n * 512) +; +; ------------------ + +filetable: + ; db 'all',0,0,0,0,0,0,0,'txt',0,1,7 ; all - start: 1, size: 7 + db 'boot',0,0,0,0,0,0, 'bin',0,1,1 ; boot.bin - start: 1, size: 1 + db 'filetable',0, 'txt',0,2,1 ; filetable.txt - start: 2, size: 1 + db 'kernel',0,0,0,0, 'bin',0,3,4 ; kernel.bin - start: 3, size: 4 + db 'calc',0,0,0,0,0,0, 'bin',0,7,1 ; calc.bin - start: 7, size: 1 + db 'edit',0,0,0,0,0,0, 'bin',0,8,4 ; edit.bin - start: 8, size: 2 + +times 512-($-$$) db 0 diff --git a/src/fs/calc.asm b/asmSrc/fs/calc.asm similarity index 74% rename from src/fs/calc.asm rename to asmSrc/fs/calc.asm index ca71d2b..9713e03 100644 --- a/src/fs/calc.asm +++ b/asmSrc/fs/calc.asm @@ -1,12 +1,21 @@ [bits 16] start: + pusha + mov si, msg call print xor ax, ax int 0x16 + mov dl, ah + call printHexByte + + xor ax, ax + int 0x16 + + popa .return: mov ax, 0x2000 mov ds, ax diff --git a/src/fs/edit.asm b/asmSrc/fs/edit.asm similarity index 80% rename from src/fs/edit.asm rename to asmSrc/fs/edit.asm index 9f43914..0e86338 100644 --- a/src/fs/edit.asm +++ b/asmSrc/fs/edit.asm @@ -1,10 +1,10 @@ [bits 16] start: + pusha mov byte [driveNum], dl hexEditor: - pusha call clearText mov di, 0x0e60 ; Offset in vidmem (2nd to last line) @@ -16,6 +16,29 @@ hexEditor: mov di, hexCode xor cx, cx ; Clear counter + + mov dx, [codeLen] + call printHexByte + mov si, bytesMsg + call print + mov si, newline + call print + + cmp word [codeLen], 0 + je getHexCh + + mov cx, [codeLen] +.printCode: + mov dl, [di] + call printHexByte + mov ah, 0x0e + mov al, ' ' + int 0x10 + inc di + dec cx + jnz .printCode + + mov cx, [codeLen] getHexCh: xor ax, ax int 0x16 @@ -42,7 +65,7 @@ getHexCh: mov [hexByte], al jmp getHexCh .putByte: - cmp di, hexCode+256 + cmp di, hexCode+510 jge getHexCh ; Max length reached rol byte [hexByte], 4 or byte [hexByte], al @@ -50,18 +73,18 @@ getHexCh: stosb mov dx, [hexByte] call printHexByte - inc byte [codeLen] + inc word [codeLen] xor cx, cx mov al, ' ' mov ah, 0x0e int 0x10 jmp getHexCh .backspace: - mov byte [hexByte], 0 - dec byte [codeLen] - xor cx, cx - cmp di, hexByte ; Check if there's anything to delete + cmp word [codeLen], 0 ; Check if there's anything to delete je getHexCh ; If not, ignore + mov byte [hexByte], 0 + dec word [codeLen] + xor cx, cx mov byte [di], 0 dec di mov si, hexBackspace @@ -72,7 +95,9 @@ getHexCh: call print pusha - call hexCode ; Call the code + push es + call hexCode ;! Runs arbitrary code (may break everything) + pop es popa mov si, ranMsg @@ -80,16 +105,7 @@ getHexCh: xor ax, ax int 0x16 jmp hexEditor - return: - mov cx, 256 - mov di, hexCode -.loop: - mov byte [di], 0 - inc di - dec cx - jnz .loop - popa mov ax, 0x2000 @@ -105,7 +121,7 @@ return: save: mov dl, 9 mov si, saveFileMsg - call getFilename + call getInput mov byte al, [filename] cmp byte al, 0 je getHexCh @@ -113,14 +129,17 @@ save: ; Call saveFile mov byte dl, [driveNum] push word filename - ; TODO: More filetypes other than .bin - push word binFile ; Filetype + push word binFile ; Filetype, TODO: More filetypes other than .bin push word 1 ; TODO: Dynamic filesize push word 0x8000 ; Program location (this segment) - push word hexCode ; Offset (hex code location) + push word savedData ; Offset (hex code location) call saveFile ; Returns exit code in al add sp, 10 ; Stack bs + mov ax, 0x8000 + mov es, ax + mov fs, ax + cmp al, 0 ; Won't be 1 (File not found) so must be disk error jne .error @@ -129,13 +148,14 @@ save: mov si, diskErrMsg mov di, 0x0f00 ; Last line call drawString + jmp getHexCh ; Opens a file open: mov dl, 11 mov si, openFileMsg - call getFilename + call getInput mov byte al, [filename] cmp byte al, 0 je getHexCh @@ -143,17 +163,21 @@ open: ; Call loadFile mov byte dl, [driveNum] push word filename - push 0x8000 - push hexCode + push word 0x8000 + push word hexCode call loadFile - add sp, 8 + + mov ax, 0x8000 + mov es, ax + mov fs, ax cmp al, 1 je .notFound cmp al, 2 je .diskErr - jmp getHexCh + ; Success + jmp hexEditor .notFound: mov si, notFoundMsg jmp .err @@ -162,7 +186,9 @@ open: .err: mov di, 0x0f00 call drawString - jmp getHexCh + xor ax, ax + int 0x16 + jmp hexEditor ; ; Gets a filename (10 chars, If less, padded with Nulls) @@ -170,7 +196,7 @@ open: ; si - Prompt string (Null terminated) ; dl - String offset ; -getFilename: +getInput: pusha push es @@ -247,6 +273,9 @@ textModeMsg: db " -- Text -- ", 0 controlsMsg: db "'$' Execute; ^Q Quit; ^S Save; ^O Open", 0 saveFileMsg: db "Save as: ", 0 ; Make sure to erase the controls openFileMsg: db "Open file: ", 0 +bytesMsg: db " Bytes", 0 +; savedMsg: db "Saved file as '", 0 +; oepenedMsg: db "Editing file '", 0 runMsg: db ENDL, ENDL, 0 ranMsg: db ENDL, ENDL, " - Finished Execution - ", ENDL, 0 notFoundMsg: db "File not found! ", 0 ; Erase the user input @@ -264,7 +293,6 @@ hexByte: db 0 filenameLen: db 0 filename: times 10 db 0 -codeLen: db 0 ;; Constants RUN_CODE equ '$' @@ -275,5 +303,6 @@ OPEN_FILE equ 'o' times 3*512-($-$$) db 0 -hexCode: times 511 db 0 ; Code buffer - ret +savedData: + hexCode: times 510 db 0 ; Code buffer + codeLen: dw 0 ; Hopefully we don't overflow :) diff --git a/src/kernel.asm b/asmSrc/kernel.asm similarity index 91% rename from src/kernel.asm rename to asmSrc/kernel.asm index 29b7d5a..62e8118 100644 --- a/src/kernel.asm +++ b/asmSrc/kernel.asm @@ -65,7 +65,27 @@ input: cmp byte [inputLen], 0 ; Check if user input something je input +tokenize: ; Tokenize + mov byte cl, [inputLen] + mov di, tokens +.tokenLoop: + lodsb + cmp al, 0 ; Check for end of input + je runCommand +.wsLoop: + cmp al, ' ' ; Compare with whitespace + jne .stringLoop + lodsb + jmp .wsLoop +.stringLoop: + cmp al, ' ' + je .next + +.next: + + ; Runs a command in userInput +; Expects tokenized input runCommand: mov di, lsCmd call .cmp @@ -91,6 +111,10 @@ runCommand: call .cmp je shutdown + mov di, rmCmd + call .cmp + je rmFile + jmp .end .cmp: push cx @@ -255,6 +279,13 @@ shutdown: mov dx, 0x0604 out dx, ax +; +; Remove file (rm) +; +rmFile: + call removeFile + jmp input + ; ; File list (ls) ; @@ -419,6 +450,7 @@ reboot: jmp 0xffff:0x0000 ;; Includes %include "./src/lib/print.asm" %include "./src/lib/screen.asm" +%include "./src/lib/disk.asm" ;; Variables driveNum: db 0 @@ -441,24 +473,29 @@ helpMsg: db "Available Commands:", ENDL, \ " gfx | Graphics mode test", ENDL, \ " help | Prints this message", ENDL, \ " ls | List all files", ENDL, \ - " reboot | Reboots the system", ENDL, ENDL, 0 + " reboot | Reboots the system", ENDL, \ + " rm | Removes a file", ENDL, ENDL, 0 ; Commands ; TODO: Put these into a file or something clearCmd: db "clear", 0 +exitCmd: db "exit" gfxCmd: db "gfx", 0 helpCmd: db "help", 0 lsCmd: db "ls", 0 -exitCmd: db "exit" rebootCmd: db "reboot", 0 +rmCmd: db "rm", 0 ; Files fileExt: db " ", 0 binExt: db "bin", 0 txtExt: db "txt", 0 +tokens: times 50 db 0 ; char[10][5] (List of 5 strings that are 10 chars long each or null terminated) +tokenLen: times 5 db 0 ; 5 lengths for each token +tokenCount: db 0 inputLen: db 0 -userInput: db 0 +userInput: times 80 db 0 ;; Constants FILETAB_LOC equ 0x1000 diff --git a/src/lib/disk.asm b/asmSrc/lib/disk.asm similarity index 90% rename from src/lib/disk.asm rename to asmSrc/lib/disk.asm index ade50d9..004e89f 100644 --- a/src/lib/disk.asm +++ b/asmSrc/lib/disk.asm @@ -21,9 +21,11 @@ loadFile: push es mov byte [diskNum], dl - mov bx, [bp+4] ; dest Offset - mov fs, [bp+6] ; dest Segment - mov si, [bp+8] ; Filename + mov bx, [bp+4] ; src Offset + mov fs, [bp+6] ; src Segment + mov si, [bp+8] ; Filename + + push bx mov cx, 10 mov di, fileName @@ -47,7 +49,7 @@ loadFile: .fileNameCheck: mov al, [es:di] cmp al, 0 ; End of filetable - je .notFound ; File doesn't exist, create new one + je .notFound cmp al, [si] je .beginCmp @@ -76,11 +78,6 @@ loadFile: mov byte [exitCode], NOT_FOUND jmp .done .found: - mov al, [es:di] - inc di - cmp al, 0 ; Skip NULLs untill extension - je .found - add di, 3 ; Skip extension inc di ; Skip Number of entries @@ -88,13 +85,16 @@ loadFile: mov dl, [diskNum] int 0x13 - mov ax, fs - mov es, ax mov ch, 0 ; Cylinder mov dh, 0 ; Head mov cl, [es:di] ; Sector inc di mov al, [es:di] ; Length in sectors + mov bx, fs + mov es, bx + pop bx + mov dx, bx + call printHexByte mov ah, 2 ; Read from disk int 0x13 @@ -210,11 +210,6 @@ saveFile: jmp .fileNameCheck .found: ; File already exists (override) ; TODO: Ask user if they want to override file - mov al, [es:di] - inc di - cmp al, 0 ; Skip NULLs untill extension - je .found - add di, 3 ; Skip extension inc di ; Skip Number of entries @@ -236,7 +231,7 @@ saveFile: ; jnc returnToCaller mov byte [exitCode], DISK_ERROR - jmp return ; TODO: Fix this + jmp returnToCaller ; TODO: Fix this createFile: ; Appends a file to the filetable. mov di, FILETAB_LOC @@ -309,20 +304,14 @@ returnToCaller: mov byte al, [exitCode] ret -debug: - pusha - mov ah, 0x0e - mov bl, 0x1f - mov bh, 0 -.loop: - lodsb ; Load [si] into al; Increment si - or al, al - jz .done - int 0x10 - jmp .loop -.done: - popa - ret +; +; Removes a file from the disk/filetable +; +; Params: +; Filename +; +removeFile: + ;; Variables availableSector: db 0 diff --git a/src/lib/print.asm b/asmSrc/lib/print.asm similarity index 100% rename from src/lib/print.asm rename to asmSrc/lib/print.asm diff --git a/src/lib/screen.asm b/asmSrc/lib/screen.asm similarity index 100% rename from src/lib/screen.asm rename to asmSrc/lib/screen.asm diff --git a/asmSrc/testing/hello b/asmSrc/testing/hello new file mode 100644 index 0000000000000000000000000000000000000000..a710842ec906930dd23fb4ade3952dce1dca254e GIT binary patch literal 44 wcmdnOx54AAz=l*H&N(XpB=dn-@vMO1fwN*e7!>+$^d0DX(D&jM0|SEs0MfP;SO5S3 literal 0 HcmV?d00001 diff --git a/asmSrc/testing/hello.asm b/asmSrc/testing/hello.asm new file mode 100644 index 0000000..7c638ea --- /dev/null +++ b/asmSrc/testing/hello.asm @@ -0,0 +1,25 @@ +[bits 16] + +start: + mov ah, 0x0e + mov al, 'H' + int 0x10 + mov al, 'e' + int 0x10 + mov al, 'l' + int 0x10 + int 0x10 + mov al, 'o' + int 0x10 + mov al, '!' + int 0x10 + + xor ax, ax + int 0x16 + + mov ax, 0x2000 + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + jmp 0x2000:0x0000 diff --git a/build.sh b/build.sh index a63ba66..392c47f 100644 --- a/build.sh +++ b/build.sh @@ -11,16 +11,16 @@ nasm -fbin src/boot.asm -o ./build/boot.bin nasm -fbin src/filetable.asm -o ./build/filetable.bin # Kernel -nasm -fbin src/kernel.asm -o ./build/kernel.bin +# nasm -fbin src/kernel.asm -o ./build/kernel.bin # Programs -nasm -fbin src/fs/calc.asm -o ./build/calc.bin -nasm -fbin src/fs/edit.asm -o ./build/edit.bin +# nasm -fbin src/fs/calc.asm -o ./build/calc.bin +# nasm -fbin src/fs/edit.asm -o ./build/edit.bin -cat ./build/calc.bin ./build/edit.bin > ./build/files.bin +# cat ./build/calc.bin ./build/edit.bin > ./build/files.bin # Final binary -cat ./build/boot.bin ./build/filetable.bin ./build/kernel.bin ./build/files.bin > ./build/temp.bin +cat ./build/boot.bin ./build/filetable.bin > ./build/temp.bin dd if=/dev/zero of=./build/kern.iso bs=512 count=2880 dd if=./build/temp.bin of=./build/kern.iso conv=notrunc rm -rf ./build/**.bin diff --git a/buildAsm.sh b/buildAsm.sh new file mode 100644 index 0000000..a63ba66 --- /dev/null +++ b/buildAsm.sh @@ -0,0 +1,32 @@ +#!/bin/bash + +# Create ./build dir if it doesn't exist +if !(test -d ././build); then + mkdir ././build + echo "Created ./build/ directory!" +fi + +# Bootloader +nasm -fbin src/boot.asm -o ./build/boot.bin +nasm -fbin src/filetable.asm -o ./build/filetable.bin + +# Kernel +nasm -fbin src/kernel.asm -o ./build/kernel.bin + +# Programs +nasm -fbin src/fs/calc.asm -o ./build/calc.bin +nasm -fbin src/fs/edit.asm -o ./build/edit.bin + +cat ./build/calc.bin ./build/edit.bin > ./build/files.bin + +# Final binary +cat ./build/boot.bin ./build/filetable.bin ./build/kernel.bin ./build/files.bin > ./build/temp.bin +dd if=/dev/zero of=./build/kern.iso bs=512 count=2880 +dd if=./build/temp.bin of=./build/kern.iso conv=notrunc +rm -rf ./build/**.bin + +echo "Built kern.iso!" + +# Run QEMU +echo "Running QEMU..." +qemu-system-x86_64 -drive format=raw,file="./build/kern.iso",index=0,media=disk -m 256M diff --git a/other/editor.png b/other/editor.png index c32203094c86cba5ff03fb55aabfd75c399fd24a..d09291591a91b00b10586249602ac5291a177333 100644 GIT binary patch literal 31473 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 AHUIzs literal 34701 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