forked from crazyettin/EttinOS

10 changed files with 1024 additions and 1024 deletions
@ -1,23 +1,23 @@
|
||||
MIT License |
||||
=========== |
||||
|
||||
Copyright (c) 2021 CrazyEttin |
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a |
||||
copy of this software and associated documentation files (the |
||||
"Software"), to deal in the Software without restriction, including |
||||
without limitation the rights to use, copy, modify, merge, publish, |
||||
distribute, sublicense, and/or sell copies of the Software, and to |
||||
permit persons to whom the Software is furnished to do so, subject to |
||||
the following conditions: |
||||
|
||||
The above copyright notice and this permission notice shall be included |
||||
in all copies or substantial portions of the Software. |
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
||||
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. |
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY |
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, |
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE |
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
||||
MIT License |
||||
=========== |
||||
|
||||
Copyright (c) 2021 CrazyEttin |
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a |
||||
copy of this software and associated documentation files (the |
||||
"Software"), to deal in the Software without restriction, including |
||||
without limitation the rights to use, copy, modify, merge, publish, |
||||
distribute, sublicense, and/or sell copies of the Software, and to |
||||
permit persons to whom the Software is furnished to do so, subject to |
||||
the following conditions: |
||||
|
||||
The above copyright notice and this permission notice shall be included |
||||
in all copies or substantial portions of the Software. |
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
||||
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. |
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY |
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, |
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE |
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
||||
|
@ -1,63 +1,63 @@
|
||||
EttinOS |
||||
======= |
||||
|
||||
EttinOS is a minimalist 16-bit DOS-like hobbyist operating system for |
||||
the IBM PC and compatible computers. Its git repository can be found at |
||||
https://ahti.space/git/crazyettin/EttinOS. |
||||
|
||||
System requirements |
||||
------------------- |
||||
|
||||
* An Intel 8086 or compatible CPU |
||||
* BIOS, or UEFI in legacy mode |
||||
* 64 KiB of RAM |
||||
* A floppy disk drive |
||||
|
||||
Building |
||||
-------- |
||||
|
||||
Build dependencies: |
||||
* A Unix-like operating system |
||||
* bash |
||||
* coreutils |
||||
* dosfstools |
||||
* mtools |
||||
* nasm |
||||
|
||||
Running make.sh will build EttinOS and create a bootable 360 KiB 5.25" |
||||
floppy disk image named EttinOS.img. To get a 1.44 MB 3.5" one instead |
||||
use the argument -1440. If you want to use another floppy disk format |
||||
you will have to adjust the bootloader disk description tables and |
||||
install the system manually. Hard disk drives are not supported. |
||||
|
||||
Input |
||||
----- |
||||
|
||||
The EttinOS input system is inspired by typewriters. Typing a character |
||||
overwrites the cursor location and the erase (=tab) key erases it. The |
||||
space and backspace keys move the cursor. |
||||
|
||||
Programming |
||||
----------- |
||||
|
||||
EttinOS has a flat address space of 64 KiB. The data, stack, and |
||||
extra segments are set at the beginning of the RAM and the system stack |
||||
at the end of the address space. Programs are loaded at address 0x2000. |
||||
The stack is reset back to the end of the address space after a program |
||||
has finished running. |
||||
|
||||
System calls: |
||||
* Interrupt 0x20: Return to the shell. |
||||
* Interrupt 0x21: Input and output: |
||||
* AH = 0x0: Print a string ending in a null from SI. |
||||
* AH = 0x1: Read a string ending in a null of at most AL |
||||
characters to DI until a return. |
||||
* AH = 0x2: Print a string ending in a null from SI followed by a |
||||
CRLF. |
||||
* AH = 0x3: Read a string ending in a null of at most AL |
||||
characters to DI until a return and print a CRLF. |
||||
* Interrupt 0x22: Disk operations: |
||||
* AH = 0x0: Load a file named in SI as a string ending in a null |
||||
to the offset BX and set AL to 0x0 if the load was |
||||
succesfull and 0x1 if there was an error. |
||||
* AH = 0x1: Save a file (under construction). |
||||
EttinOS |
||||
======= |
||||
|
||||
EttinOS is a minimalist 16-bit DOS-like hobbyist operating system for |
||||
the IBM PC and compatible computers. Its git repository can be found at |
||||
https://ahti.space/git/crazyettin/EttinOS. |
||||
|
||||
System requirements |
||||
------------------- |
||||
|
||||
* An Intel 8086 or compatible CPU |
||||
* BIOS, or UEFI in legacy mode |
||||
* 64 KiB of RAM |
||||
* A floppy disk drive |
||||
|
||||
Building |
||||
-------- |
||||
|
||||
Build dependencies: |
||||
* A Unix-like operating system |
||||
* bash |
||||
* coreutils |
||||
* dosfstools |
||||
* mtools |
||||
* nasm |
||||
|
||||
Running make.sh will build EttinOS and create a bootable 360 KiB 5.25" |
||||
floppy disk image named EttinOS.img. To get a 1.44 MB 3.5" one instead |
||||
use the argument -1440. If you want to use another floppy disk format |
||||
you will have to adjust the bootloader disk description tables and |
||||
install the system manually. Hard disk drives are not supported. |
||||
|
||||
Input |
||||
----- |
||||
|
||||
The EttinOS input system is inspired by typewriters. Typing a character |
||||
overwrites the cursor location and the erase (=tab) key erases it. The |
||||
space and backspace keys move the cursor. |
||||
|
||||
Programming |
||||
----------- |
||||
|
||||
EttinOS has a flat address space of 64 KiB. The data, stack, and |
||||
extra segments are set at the beginning of the RAM and the system stack |
||||
at the end of the address space. Programs are loaded at address 0x2000. |
||||
The stack is reset back to the end of the address space after a program |
||||
has finished running. |
||||
|
||||
System calls: |
||||
* Interrupt 0x20: Return to the shell. |
||||
* Interrupt 0x21: Input and output: |
||||
* AH = 0x0: Print a string ending in a null from SI. |
||||
* AH = 0x1: Read a string ending in a null of at most AL |
||||
characters to DI until a return. |
||||
* AH = 0x2: Print a string ending in a null from SI followed by a |
||||
CRLF. |
||||
* AH = 0x3: Read a string ending in a null of at most AL |
||||
characters to DI until a return and print a CRLF. |
||||
* Interrupt 0x22: Disk operations: |
||||
* AH = 0x0: Load a file named in SI as a string ending in a null |
||||
to the offset BX and set AL to 0x0 if the load was |
||||
succesfull and 0x1 if there was an error. |
||||
* AH = 0x1: Save a file (under construction). |
||||
|
@ -1,243 +1,243 @@
|
||||
CPU 8086 |
||||
ORG 0x7c00 |
||||
|
||||
jmp start |
||||
nop |
||||
|
||||
;Disk description tables |
||||
|
||||
%ifdef F1440 |
||||
;1.44 MB 3.5" floppy disk (enable with the argument -d F1440 when building) |
||||
oemlabel db "ETTINOS " |
||||
sectorsize dw 0x200 ;bytes |
||||
clustersize db 0x1 ;sectors |
||||
bootsectors dw 0x1 |
||||
fats db 0x2 |
||||
rootentries dw 0xe0 |
||||
logicalsectors dw 0xb40 |
||||
mediadescriptor db 0xf0 |
||||
sectorsperfat dw 0x9 |
||||
sectorspertrack dw 0x12 |
||||
sides dw 0x2 |
||||
hiddensectors dd 0x0 |
||||
largesectors dd 0x0 |
||||
driveid dw 0x0 |
||||
drivesignature db 0x29 |
||||
volumeid dd 0x0 |
||||
volumelabel db "ETTINOS " |
||||
filesystem db "FAT12 " |
||||
|
||||
%else |
||||
;360 KiB 5.25" floppy disk (default) |
||||
oemlabel db "ETTINOS " |
||||
sectorsize dw 0x200 ;bytes |
||||
clustersize db 0x2 ;sectors |
||||
bootsectors dw 0x1 |
||||
fats db 0x2 |
||||
rootentries dw 0x70 |
||||
logicalsectors dw 0x2d0 |
||||
mediadescriptor db 0xfd |
||||
sectorsperfat dw 0x2 |
||||
sectorspertrack dw 0x9 |
||||
sides dw 0x2 |
||||
hiddensectors dd 0x0 |
||||
largesectors dd 0x0 |
||||
driveid dw 0x0 |
||||
drivesignature db 0x29 |
||||
volumeid dd 0x0 |
||||
volumelabel db "ETTINOS " |
||||
filesystem db "FAT12 " |
||||
|
||||
%endif |
||||
|
||||
start: |
||||
|
||||
;Setup |
||||
;Set up the data, stack, and extra segments |
||||
mov ax, 0x0 |
||||
mov ds, ax |
||||
mov ss, ax |
||||
mov es, ax |
||||
;Set up the stack |
||||
cli |
||||
mov sp, 0x0 |
||||
sti |
||||
;Store the boot drive number |
||||
mov [bootdrive], dl |
||||
|
||||
;Load the root |
||||
;Set the source |
||||
mov ah, 0x0 |
||||
mov al, [fats] |
||||
mul word [sectorsperfat] |
||||
add ax, [bootsectors] |
||||
push ax |
||||
call calcsource |
||||
;Set the destination |
||||
mov si, buffer |
||||
mov bx, si |
||||
;Set the size |
||||
push dx |
||||
mov ax, [rootentries] |
||||
mov dx, 0x20 |
||||
mul dx |
||||
mov dx, 0x0 |
||||
div word [sectorsize] |
||||
pop dx |
||||
push ax |
||||
;Load |
||||
mov ah, 0x2 |
||||
int 0x13 |
||||
|
||||
;Search the root for the system entry |
||||
;Set DI to the root |
||||
mov di, buffer |
||||
;Initialise the search loop |
||||
mov cx, word [rootentries] |
||||
mov ax, 0x0 |
||||
search: |
||||
;Store CX in the stack |
||||
push cx |
||||
;Check for the system entry |
||||
mov si, sysfile |
||||
mov cx, 0xb |
||||
rep cmpsb |
||||
je loadentry |
||||
;Set DI to the next entry |
||||
add ax, 0x20 |
||||
mov di, buffer |
||||
add di, ax |
||||
;Load CX from the stack |
||||
pop cx |
||||
loop search |
||||
|
||||
;Print an error message if the system is not found |
||||
mov si, errormsg |
||||
printerror: |
||||
;Load a character |
||||
lodsb |
||||
;Check for the string end |
||||
cmp al, 0x0 |
||||
je $ |
||||
;Print the character |
||||
mov ah, 0xe |
||||
int 0x10 |
||||
;Repeat |
||||
jmp printerror |
||||
|
||||
;Load the system entry |
||||
loadentry: |
||||
;Load CX from the stack |
||||
pop cx |
||||
;Store the first cluster |
||||
mov ax, word [es:di+0xf] |
||||
mov word [cluster], ax |
||||
;Set the source |
||||
mov ax, 0x1 |
||||
call calcsource |
||||
;Set the destination |
||||
mov di, buffer |
||||
mov bx, di |
||||
;Set the size |
||||
mov ax, [sectorsperfat] |
||||
;Load |
||||
mov ah, 0x2 |
||||
int 0x13 |
||||
|
||||
;Load the system file |
||||
|
||||
;Load a cluster |
||||
loadcluster: |
||||
;Set the source |
||||
pop cx |
||||
pop bx |
||||
mov ax, word [cluster] |
||||
sub ax, 0x2 |
||||
mul byte [clustersize] |
||||
add ax, bx |
||||
add ax, cx |
||||
push bx |
||||
push cx |
||||
call calcsource |
||||
;Set the destination |
||||
mov bx, word [pointer] |
||||
;Set the size |
||||
;mov al, 0x1 |
||||
mov al, [clustersize] |
||||
;Load |
||||
mov ah, 0x2 |
||||
int 0x13 |
||||
|
||||
;Calculate the next cluster |
||||
mov ax, [cluster] |
||||
mov dx, 0x0 |
||||
mov bx, 0x3 |
||||
mul bx |
||||
mov bx, 0x2 |
||||
div bx |
||||
mov si, buffer |
||||
add si, ax |
||||
mov ax, word [ds:si] |
||||
or dx, dx |
||||
jz even |
||||
odd: |
||||
shr ax, 1 |
||||
shr ax, 1 |
||||
shr ax, 1 |
||||
shr ax, 1 |
||||
jmp contcalc |
||||
even: |
||||
and ax, 0xfff |
||||
contcalc: |
||||
mov word [cluster], ax |
||||
cmp ax, 0xff8 |
||||
jge boot |
||||
mov ax, [sectorsize] |
||||
mul word [clustersize] |
||||
add word [pointer], ax |
||||
jmp loadcluster |
||||
|
||||
;Clear the stack and boot the system |
||||
boot: |
||||
;Clear |
||||
pop cx |
||||
pop bx |
||||
;Boot |
||||
jmp 0x0:0x500 |
||||
|
||||
;Data |
||||
bootdrive db 0x0 |
||||
sysfile db "SYSTEM BIN" |
||||
errormsg db "System not found", 0xd, 0xa, 0x0 |
||||
cluster dw 0x0 |
||||
pointer dw 0x500 |
||||
|
||||
;Calculate the source arguments for loading data from the disk |
||||
calcsource: |
||||
push ax |
||||
push bx |
||||
mov bx, ax |
||||
mov dx, 0x0 |
||||
div word [sectorspertrack] |
||||
add dl, 0x1 |
||||
mov cl, dl |
||||
mov ax, bx |
||||
mov dx, 0x0 |
||||
div word [sectorspertrack] |
||||
mov dx, 0x0 |
||||
div word [sides] |
||||
mov dh, dl |
||||
mov ch, al |
||||
pop bx |
||||
pop ax |
||||
mov dl, byte [bootdrive] |
||||
ret |
||||
|
||||
;Pad the binary to a full sector and make the disk bootable |
||||
;Padding |
||||
times 0x1fe-($-$$) db 0x0 |
||||
;Boot signature |
||||
dw 0xaa55 |
||||
|
||||
;File system buffer |
||||
buffer: |
||||
CPU 8086 |
||||
ORG 0x7c00 |
||||
|
||||
jmp start |
||||
nop |
||||
|
||||
;Disk description tables |
||||
|
||||
%ifdef F1440 |
||||
;1.44 MB 3.5" floppy disk (enable with the argument -d F1440 when building) |
||||
oemlabel db "ETTINOS " |
||||
sectorsize dw 0x200 ;bytes |
||||
clustersize db 0x1 ;sectors |
||||
bootsectors dw 0x1 |
||||
fats db 0x2 |
||||
rootentries dw 0xe0 |
||||
logicalsectors dw 0xb40 |
||||
mediadescriptor db 0xf0 |
||||
sectorsperfat dw 0x9 |
||||
sectorspertrack dw 0x12 |
||||
sides dw 0x2 |
||||
hiddensectors dd 0x0 |
||||
largesectors dd 0x0 |
||||
driveid dw 0x0 |
||||
drivesignature db 0x29 |
||||
volumeid dd 0x0 |
||||
volumelabel db "ETTINOS " |
||||
filesystem db "FAT12 " |
||||
|
||||
%else |
||||
;360 KiB 5.25" floppy disk (default) |
||||
oemlabel db "ETTINOS " |
||||
sectorsize dw 0x200 ;bytes |
||||
clustersize db 0x2 ;sectors |
||||
bootsectors dw 0x1 |
||||
fats db 0x2 |
||||
rootentries dw 0x70 |
||||
logicalsectors dw 0x2d0 |
||||
mediadescriptor db 0xfd |
||||
sectorsperfat dw 0x2 |
||||
sectorspertrack dw 0x9 |
||||
sides dw 0x2 |
||||
hiddensectors dd 0x0 |
||||
largesectors dd 0x0 |
||||
driveid dw 0x0 |
||||
drivesignature db 0x29 |
||||
volumeid dd 0x0 |
||||
volumelabel db "ETTINOS " |
||||
filesystem db "FAT12 " |
||||
|
||||
%endif |
||||
|
||||
start: |
||||
|
||||
;Setup |
||||
;Set up the data, stack, and extra segments |
||||
mov ax, 0x0 |
||||
mov ds, ax |
||||
mov ss, ax |
||||
mov es, ax |
||||
;Set up the stack |
||||
cli |
||||
mov sp, 0x0 |
||||
sti |
||||
;Store the boot drive number |
||||
mov [bootdrive], dl |
||||
|
||||
;Load the root |
||||
;Set the source |
||||
mov ah, 0x0 |
||||
mov al, [fats] |
||||
mul word [sectorsperfat] |
||||
add ax, [bootsectors] |
||||
push ax |
||||
call calcsource |
||||
;Set the destination |
||||
mov si, buffer |
||||
mov bx, si |
||||
;Set the size |
||||
push dx |
||||
mov ax, [rootentries] |
||||
mov dx, 0x20 |
||||
mul dx |
||||
mov dx, 0x0 |
||||
div word [sectorsize] |
||||
pop dx |
||||
push ax |
||||
;Load |
||||
mov ah, 0x2 |
||||
int 0x13 |
||||
|
||||
;Search the root for the system entry |
||||
;Set DI to the root |
||||
mov di, buffer |
||||
;Initialise the search loop |
||||
mov cx, word [rootentries] |
||||
mov ax, 0x0 |
||||
search: |
||||
;Store CX in the stack |
||||
push cx |
||||
;Check for the system entry |
||||
mov si, sysfile |
||||
mov cx, 0xb |
||||
rep cmpsb |
||||
je loadentry |
||||
;Set DI to the next entry |
||||
add ax, 0x20 |
||||
mov di, buffer |
||||
add di, ax |
||||
;Load CX from the stack |
||||
pop cx |
||||
loop search |
||||
|
||||
;Print an error message if the system is not found |
||||
mov si, errormsg |
||||
printerror: |
||||
;Load a character |
||||
lodsb |
||||
;Check for the string end |
||||
cmp al, 0x0 |
||||
je $ |
||||
;Print the character |
||||
mov ah, 0xe |
||||
int 0x10 |
||||
;Repeat |
||||
jmp printerror |
||||
|
||||
;Load the system entry |
||||
loadentry: |
||||
;Load CX from the stack |
||||
pop cx |
||||
;Store the first cluster |
||||
mov ax, word [es:di+0xf] |
||||
mov word [cluster], ax |
||||
;Set the source |
||||
mov ax, 0x1 |
||||
call calcsource |
||||
;Set the destination |
||||
mov di, buffer |
||||
mov bx, di |
||||
;Set the size |
||||
mov ax, [sectorsperfat] |
||||
;Load |
||||
mov ah, 0x2 |
||||
int 0x13 |
||||
|
||||
;Load the system file |
||||
|
||||
;Load a cluster |
||||
loadcluster: |
||||
;Set the source |
||||
pop cx |
||||
pop bx |
||||
mov ax, word [cluster] |
||||
sub ax, 0x2 |
||||
mul byte [clustersize] |
||||
add ax, bx |
||||
add ax, cx |
||||
push bx |
||||
push cx |
||||
call calcsource |
||||
;Set the destination |
||||
mov bx, word [pointer] |
||||
;Set the size |
||||
;mov al, 0x1 |
||||
mov al, [clustersize] |
||||
;Load |
||||
mov ah, 0x2 |
||||
int 0x13 |
||||
|
||||
;Calculate the next cluster |
||||
mov ax, [cluster] |
||||
mov dx, 0x0 |
||||
mov bx, 0x3 |
||||
mul bx |
||||
mov bx, 0x2 |
||||
div bx |
||||
mov si, buffer |
||||
add si, ax |
||||
mov ax, word [ds:si] |
||||
or dx, dx |
||||
jz even |
||||
odd: |
||||
shr ax, 1 |
||||
shr ax, 1 |
||||
shr ax, 1 |
||||
shr ax, 1 |
||||
jmp contcalc |
||||
even: |
||||
and ax, 0xfff |
||||
contcalc: |
||||
mov word [cluster], ax |
||||
cmp ax, 0xff8 |
||||
jge boot |
||||
mov ax, [sectorsize] |
||||
mul word [clustersize] |
||||
add word [pointer], ax |
||||
jmp loadcluster |
||||
|
||||
;Clear the stack and boot the system |
||||
boot: |
||||
;Clear |
||||
pop cx |
||||
pop bx |
||||
;Boot |
||||
jmp 0x0:0x500 |
||||
|
||||
;Data |
||||
bootdrive db 0x0 |
||||
sysfile db "SYSTEM BIN" |
||||
errormsg db "System not found", 0xd, 0xa, 0x0 |
||||
cluster dw 0x0 |
||||
pointer dw 0x500 |
||||
|
||||
;Calculate the source arguments for loading data from the disk |
||||
calcsource: |
||||
push ax |
||||
push bx |
||||
mov bx, ax |
||||
mov dx, 0x0 |
||||
div word [sectorspertrack] |
||||
add dl, 0x1 |
||||
mov cl, dl |
||||
mov ax, bx |
||||
mov dx, 0x0 |
||||
div word [sectorspertrack] |
||||
mov dx, 0x0 |
||||
div word [sides] |
||||
mov dh, dl |
||||
mov ch, al |
||||
pop bx |
||||
pop ax |
||||
mov dl, byte [bootdrive] |
||||
ret |
||||
|
||||
;Pad the binary to a full sector and make the disk bootable |
||||
;Padding |
||||
times 0x1fe-($-$$) db 0x0 |
||||
;Boot signature |
||||
dw 0xaa55 |
||||
|
||||
;File system buffer |
||||
buffer: |
||||
|
@ -1,11 +1,11 @@
|
||||
CPU 8086 |
||||
ORG 0x2000 |
||||
|
||||
;Prints a hello world. |
||||
mov si, .hello |
||||
mov ah, 0x2 |
||||
int 0x21 |
||||
int 0x20 |
||||
|
||||
;Data |
||||
.hello db "Hello world!", 0x0 |
||||
CPU 8086 |
||||
ORG 0x2000 |
||||
|
||||
;Prints a hello world. |
||||
mov si, .hello |
||||
mov ah, 0x2 |
||||
int 0x21 |
||||
int 0x20 |
||||
|
||||
;Data |
||||
.hello db "Hello world!", 0x0 |
||||
|
@ -1,304 +1,304 @@
|
||||
;Load a file named in SI as a string ending in a null to the offset BX and set AL to 0x0 if the load was succesful and 0x1 if there was an error. |
||||
|
||||
loadf: |
||||
|
||||
;Store the initial registers in the stack |
||||
push ax |
||||
push bx |
||||
push cx |
||||
push dx |
||||
push si |
||||
push di |
||||
|
||||
;Store the offset |
||||
mov word [.pointer], bx |
||||
|
||||
;Set DI at .file and initialise it with spaces |
||||
mov di, .file |
||||
mov cx, 0xb |
||||
mov al, 0x20 |
||||
rep stosb |
||||
sub di, 0xb |
||||
|
||||
;Convert .file into FAT formatting |
||||
|
||||
;Initialise the length counter for the main part of the name |
||||
mov bl, 0x8 |
||||
|
||||
;Convert the main part of the file name |
||||
.nameloop: |
||||
;Load a character |
||||
lodsb |
||||
;Check for a period |
||||
cmp al, 0x2e |
||||
je .initext |
||||
;Check for everything else and convert to upper case |
||||
call .checkconv |
||||
jmp .nameloop |
||||
|
||||
;Convert the extension |
||||
.initext: |
||||
;Set DI and initialise the length counter for the extension |
||||
mov bl, 0x3 |
||||
mov di, .file+0x8 |
||||
.extloop: |
||||
;Load a character |
||||
lodsb |
||||
;Check for a period |
||||
push ax |
||||
cmp al, 0x2e |
||||
je .error |
||||
pop ax |
||||
;Check for everything else and convert to upper case |
||||
call .checkconv |
||||
jmp .extloop |
||||
|
||||
;Set the carry flag and print an error message if the file name is invalid |
||||
.error: |
||||
pop ax |
||||
stc |
||||
mov si, .errormsg |
||||
mov ah, 0x2 |
||||
int 0x21 |
||||
jmp .done |
||||
|
||||
;Find and load the file |
||||
|
||||
.load: |
||||
pop ax |
||||
|
||||
;Load the root |
||||
;Set the source |
||||
mov ah, 0x0 |
||||
mov al, [.fats] |
||||
mul word [.sectorsperfat] |
||||
add ax, [.bootsectors] |
||||
push ax |
||||
call .calcsource |
||||
;Set the destination |
||||
mov si, buffer |
||||
mov bx, si |
||||
;Set the size |
||||
push dx |
||||
mov ax, [.rootentries] |
||||
mov dx, 0x20 |
||||
mul dx |
||||
mov dx, 0x0 |
||||
div word [.sectorsize] |
||||
pop dx |
||||
push ax |
||||
;Load |
||||
mov ah, 0x2 |
||||
int 0x13 |
||||
|
||||
;Search the root for the file entry |
||||
;Set DI to the root |
||||
mov di, buffer |
||||
;Initialise the search loop |
||||
mov cx, word [.rootentries] |
||||
mov ax, 0x0 |
||||
.search: |
||||
;Store CX in the stack |
||||
push cx |
||||
;Check for the file entry |
||||
mov si, .file |
||||
mov cx, 0xb |
||||
rep cmpsb |
||||
je .loadentry |
||||
;Set DI to the next entry |
||||
add ax, 0x20 |
||||
mov di, buffer |
||||
add di, ax |
||||
;Load CX from the stack |
||||
pop cx |
||||
loop .search |
||||
|
||||
;Set the carry flag and print an error message if the file is not found |
||||
stc |
||||
mov si, .errormsg |
||||
mov ah, 0x2 |
||||
int 0x21 |
||||
jmp .clearstack |
||||
|
||||
;Load the file entry |
||||
.loadentry: |
||||
;Load CX from the stack |
||||
pop cx |
||||
;Store the first cluster |
||||
mov ax, word [es:di+0xf] |
||||
mov word [.cluster], ax |
||||
;Set the source |
||||
mov ax, 0x1 |
||||
call .calcsource |
||||
;Set the destination |
||||
mov di, buffer |
||||
mov bx, di |
||||
;Set the size |
||||
mov ax, [.sectorsperfat] |
||||
;Load |
||||
mov ah, 0x2 |
||||
int 0x13 |
||||
|
||||
;Load the file |
||||
|
||||
;Load a cluster |
||||
.loadcluster: |
||||
;Set the source |
||||
pop cx |
||||
pop bx |
||||
mov ax, word [.cluster] |
||||
sub ax, 0x2 |
||||
mul byte [.clustersize] |
||||
add ax, bx |
||||
add ax, cx |
||||
push bx |
||||
push cx |
||||
call .calcsource |
||||
;Set the destination |
||||
mov bx, word [.pointer] |
||||
;Set the size |
||||
;mov al, 0x1 |
||||
mov al, [.clustersize] |
||||
;Load |
||||
mov ah, 0x2 |
||||
int 0x13 |
||||
|
||||
;Calculate the next cluster |
||||
mov ax, [.cluster] |
||||
mov dx, 0x0 |
||||
mov bx, 0x3 |
||||
mul bx |
||||
mov bx, 0x2 |
||||
div bx |
||||
mov si, buffer |
||||
add si, ax |
||||
mov ax, word [ds:si] |
||||
or dx, dx |
||||
jz .even |
||||
shr ax, 1 |
||||
shr ax, 1 |
||||
shr ax, 1 |
||||
shr ax, 1 |
||||
jmp .contcalc |
||||
.even: |
||||
and ax, 0xfff |
||||
.contcalc: |
||||
mov word [.cluster], ax |
||||
cmp ax, 0xff8 |
||||
jge .clearcarry |
||||
mov ax, [.sectorsize] |
||||
mul word [.clustersize] |
||||
add word [.pointer], ax |
||||
jmp .loadcluster |
||||
|
||||
;Clear the carry flag if the load was succesful |
||||
.clearcarry: |
||||
clc |
||||
|
||||
;Clear the stack |
||||
.clearstack: |
||||
pop cx |
||||
pop bx |
||||
|
||||
.done: |
||||
|
||||
;Load the initial registers from the stack |
||||
pop di |
||||
pop si |
||||
pop dx |
||||
pop cx |
||||
pop bx |
||||
pop ax |
||||
|
||||
;Set AL to 0x1 if there was an error and to 0x0 otherwise and return |
||||
jc .setal |
||||
mov al, 0x0 |
||||
iret |
||||
.setal: |
||||
mov al, 0x1 |
||||
iret |
||||
|
||||
;Data |
||||
.file times 0xb db 0x20 |
||||
.errormsg db "File not found", 0x0 |
||||
.cluster dw 0x0 |
||||
.pointer dw 0x0 |
||||
|
||||
;These are temporary until i write something to load them from the disk itself |
||||
.bootdrive db 0x0 |
||||
.sectorsize dw 0x200 ;bytes |
||||
.clustersize db 0x2 ;sectors |
||||
.bootsectors dw 0x1 |
||||
.fats db 0x2 |
||||
.rootentries dw 0x70 |
||||
.sectorsperfat dw 0x2 |
||||
.sectorspertrack dw 0x9 |
||||
.sides dw 0x2 |
||||
|
||||
;Check the file name and convert to upper case |
||||
.checkconv: |
||||
;Check for the string end |
||||
cmp al, 0x0 |
||||
je .load |
||||
;Check for the length limit |
||||
cmp bl, 0x0 |
||||
je .error |
||||
;Check for invalid characters |
||||
cmp al, 0x22 |
||||
je .error |
||||
cmp al, 0x2a |
||||
jl .contcheck1 |
||||
cmp al, 0x2c |
||||
jg .contcheck1 |
||||
jmp .error |
||||
.contcheck1: |
||||
cmp al, 0x2f |
||||
je .error |
||||
cmp al, 0x3a |
||||
jl .contcheck2 |
||||
cmp al, 0x3f |
||||
jg .contcheck2 |
||||
jmp .error |
||||
.contcheck2: |
||||
cmp al, 0x5b |
||||
jl .contcheck3 |
||||
cmp al, 0x5d |
||||
jg .contcheck3 |
||||
jmp .error |
||||
.contcheck3: |
||||
cmp al, 0x7c |
||||
je .error |
||||
;Check for lower case |
||||
cmp al, 0x61 |
||||
jl .storech |
||||
cmp al, 0x7a |
||||
jg .storech |
||||
;Convert lower to upper case |
||||
sub al, 0x20 |
||||
.storech: |
||||
;Store the character |
||||
stosb |
||||
;Increase the counter |
||||
dec bl |
||||
ret |
||||
|
||||
;Calculate the source arguments for loading data from the disk |
||||
.calcsource: |
||||
push ax |
||||
push bx |
||||
mov bx, ax |
||||
mov dx, 0x0 |
||||
div word [.sectorspertrack] |
||||
add dl, 0x1 |
||||
mov cl, dl |
||||
mov ax, bx |
||||
mov dx, 0x0 |
||||
div word [.sectorspertrack] |
||||
mov dx, 0x0 |
||||
div word [.sides] |
||||
mov dh, dl |
||||
mov ch, al |
||||
pop bx |
||||
pop ax |
||||
mov dl, byte [.bootdrive] |
||||
ret |
||||
;Load a file named in SI as a string ending in a null to the offset BX and set AL to 0x0 if the load was succesful and 0x1 if there was an error. |
||||
|
||||
loadf: |
||||
|
||||
;Store the initial registers in the stack |
||||
push ax |
||||
push bx |
||||
push cx |
||||
push dx |
||||
push si |
||||
push di |
||||
|
||||
;Store the offset |
||||
mov word [.pointer], bx |
||||
|
||||
;Set DI at .file and initialise it with spaces |
||||
mov di, .file |
||||
mov cx, 0xb |
||||
mov al, 0x20 |
||||
rep stosb |
||||
sub di, 0xb |
||||
|
||||
;Convert .file into FAT formatting |
||||
|
||||
;Initialise the length counter for the main part of the name |
||||
mov bl, 0x8 |
||||
|
||||
;Convert the main part of the file name |
||||
.nameloop: |
||||
;Load a character |
||||
lodsb |
||||
;Check for a period |
||||
cmp al, 0x2e |
||||
je .initext |
||||
;Check for everything else and convert to upper case |
||||
call .checkconv |
||||
jmp .nameloop |
||||
|
||||
;Convert the extension |
||||
.initext: |
||||
;Set DI and initialise the length counter for the extension |
||||
mov bl, 0x3 |
||||
mov di, .file+0x8 |
||||
.extloop: |
||||
;Load a character |
||||
lodsb |
||||
;Check for a period |
||||
push ax |
||||
cmp al, 0x2e |
||||
je .error |
||||
pop ax |
||||
;Check for everything else and convert to upper case |
||||
call .checkconv |
||||
jmp .extloop |
||||
|
||||
;Set the carry flag and print an error message if the file name is invalid |
||||
.error: |
||||
pop ax |
||||
stc |
||||
mov si, .errormsg |
||||
mov ah, 0x2 |
||||
int 0x21 |
||||
jmp .done |
||||
|
||||
;Find and load the file |
||||
|
||||
.load: |
||||
pop ax |
||||
|
||||
;Load the root |
||||
;Set the source |
||||
mov ah, 0x0 |
||||
mov al, [.fats] |
||||
mul word [.sectorsperfat] |
||||
add ax, [.bootsectors] |
||||
push ax |
||||
call .calcsource |
||||
;Set the destination |
||||
mov si, buffer |
||||
mov bx, si |
||||
;Set the size |
||||
push dx |
||||
mov ax, [.rootentries] |
||||
mov dx, 0x20 |
||||
mul dx |
||||
mov dx, 0x0 |
||||
div word [.sectorsize] |
||||
pop dx |
||||
push ax |
||||
;Load |
||||
mov ah, 0x2 |
||||
int 0x13 |
||||
|
||||
;Search the root for the file entry |
||||
;Set DI to the root |
||||
mov di, buffer |
||||
;Initialise the search loop |
||||
mov cx, word [.rootentries] |
||||
mov ax, 0x0 |
||||
.search: |
||||
;Store CX in the stack |
||||
push cx |
||||
;Check for the file entry |
||||
mov si, .file |
||||
mov cx, 0xb |
||||
rep cmpsb |
||||
je .loadentry |
||||
;Set DI to the next entry |
||||
add ax, 0x20 |
||||
mov di, buffer |
||||
add di, ax |
||||
;Load CX from the stack |
||||
pop cx |
||||
loop .search |
||||
|
||||
;Set the carry flag and print an error message if the file is not found |
||||
stc |
||||
mov si, .errormsg |
||||
mov ah, 0x2 |
||||
int 0x21 |
||||
jmp .clearstack |
||||
|
||||
;Load the file entry |
||||
.loadentry: |
||||
;Load CX from the stack |
||||
pop cx |
||||
;Store the first cluster |
||||
mov ax, word [es:di+0xf] |
||||
mov word [.cluster], ax |
||||
;Set the source |
||||
mov ax, 0x1 |
||||
call .calcsource |
||||
;Set the destination |
||||
mov di, buffer |
||||
mov bx, di |
||||
;Set the size |
||||
mov ax, [.sectorsperfat] |
||||
;Load |
||||
mov ah, 0x2 |
||||
int 0x13 |
||||
|
||||
;Load the file |
||||
|
||||
;Load a cluster |
||||
.loadcluster: |
||||
;Set the source |
||||
pop cx |
||||
pop bx |
||||
mov ax, word [.cluster] |
||||
sub ax, 0x2 |
||||
mul byte [.clustersize] |
||||
add ax, bx |
||||
add ax, cx |
||||
push bx |
||||
push cx |
||||
call .calcsource |
||||
;Set the destination |
||||
mov bx, word [.pointer] |
||||
;Set the size |
||||
;mov al, 0x1 |
||||
mov al, [.clustersize] |
||||
;Load |
||||
mov ah, 0x2 |
||||
int 0x13 |
||||
|
||||
;Calculate the next cluster |
||||
mov ax, [.cluster] |
||||
mov dx, 0x0 |
||||
mov bx, 0x3 |
||||
mul bx |
||||
mov bx, 0x2 |
||||
div bx |
||||
mov si, buffer |
||||
add si, ax |
||||
mov ax, word [ds:si] |
||||
or dx, dx |
||||
jz .even |
||||
shr ax, 1 |
||||
shr ax, 1 |
||||
shr ax, 1 |
||||
shr ax, 1 |
||||
jmp .contcalc |
||||
.even: |
||||
and ax, 0xfff |
||||
.contcalc: |
||||
mov word [.cluster], ax |
||||
cmp ax, 0xff8 |
||||
jge .clearcarry |
||||
mov ax, [.sectorsize] |
||||
mul word [.clustersize] |
||||
add word [.pointer], ax |
||||
jmp .loadcluster |
||||
|
||||
;Clear the carry flag if the load was succesful |
||||
.clearcarry: |
||||
clc |
||||
|
||||
;Clear the stack |
||||
.clearstack: |
||||
pop cx |
||||
pop bx |
||||
|
||||
.done: |
||||
|
||||
;Load the initial registers from the stack |
||||
pop di |
||||
pop si |
||||
pop dx |
||||
pop cx |
||||
pop bx |
||||
pop ax |
||||
|
||||
;Set AL to 0x1 if there was an error and to 0x0 otherwise and return |
||||
jc .setal |
||||
mov al, 0x0 |
||||
iret |
||||
.setal: |
||||
mov al, 0x1 |
||||
iret |
||||
|
||||
;Data |
||||
.file times 0xb db 0x20 |
||||
.errormsg db "File not found", 0x0 |
||||
.cluster dw 0x0 |
||||
.pointer dw 0x0 |
||||
|
||||
;These are temporary until i write something to load them from the disk itself |
||||
.bootdrive db 0x0 |
||||
.sectorsize dw 0x200 ;bytes |
||||
.clustersize db 0x2 ;sectors |
||||
.bootsectors dw 0x1 |
||||
.fats db 0x2 |
||||
.rootentries dw 0x70 |
||||
.sectorsperfat dw 0x2 |
||||
.sectorspertrack dw 0x9 |
||||
.sides dw 0x2 |
||||
|
||||
;Check the file name and convert to upper case |
||||
.checkconv: |
||||
;Check for the string end |
||||
cmp al, 0x0 |
||||
je .load |
||||
;Check for the length limit |
||||
cmp bl, 0x0 |
||||
je .error |
||||
;Check for invalid characters |
||||
cmp al, 0x22 |
||||
je .error |
||||
cmp al, 0x2a |
||||
jl .contcheck1 |
||||
cmp al, 0x2c |
||||
jg .contcheck1 |
||||
jmp .error |
||||
.contcheck1: |
||||
cmp al, 0x2f |
||||
je .error |
||||
cmp al, 0x3a |
||||
jl .contcheck2 |
||||
cmp al, 0x3f |
||||
jg .contcheck2 |
||||
jmp .error |
||||
.contcheck2: |
||||
cmp al, 0x5b |
||||
jl .contcheck3 |
||||
cmp al, 0x5d |
||||
jg .contcheck3 |
||||
jmp .error |
||||
.contcheck3: |
||||
cmp al, 0x7c |
||||
je .error |
||||
;Check for lower case |
||||
cmp al, 0x61 |
||||
jl .storech |
||||
cmp al, 0x7a |
||||
jg .storech |
||||
;Convert lower to upper case |
||||
sub al, 0x20 |
||||
.storech: |
||||
;Store the character |
||||
stosb |
||||
;Increase the counter |
||||
dec bl |
||||
ret |
||||
|
||||
;Calculate the source arguments for loading data from the disk |
||||
.calcsource: |
||||
push ax |
||||
push bx |
||||
mov bx, ax |
||||
mov dx, 0x0 |
||||
div word [.sectorspertrack] |
||||
add dl, 0x1 |
||||
mov cl, dl |
||||
mov ax, bx |
||||
mov dx, 0x0 |
||||
div word [.sectorspertrack] |
||||
mov dx, 0x0 |
||||
div word [.sides] |
||||
mov dh, dl |
||||
mov ch, al |
||||
pop bx |
||||
pop ax |
||||
mov dl, byte [.bootdrive] |
||||
ret |
||||
|
@ -1,12 +1,12 @@
|
||||
;Print a string ending in a null from SI followed by a CRLF. |
||||
|
||||
println: |
||||
|
||||
;Print the string |
||||
mov ah, 0x0 |
||||
int 0x21 |
||||
|
||||
;Print a CRLF |
||||
call printcrlf |
||||
|
||||
iret |
||||
;Print a string ending in a null from SI followed by a CRLF. |
||||
|
||||
println: |
||||
|
||||
;Print the string |
||||
mov ah, 0x0 |
||||
int 0x21 |
||||
|
||||
;Print a CRLF |
||||
call printcrlf |
||||
|
||||
iret |
||||
|
@ -1,28 +1,28 @@
|
||||
;Print a string ending in a null from SI. |
||||
|
||||
printstr: |
||||
|
||||
;Store the initial registers in the stack |
||||
push ax |
||||
push si |
||||
|
||||
;Print the string |
||||
.loop: |
||||
;Load the current character |
||||
lodsb |
||||
;Check for the string end |
||||
cmp al, 0x0 |
||||
je .done |
||||
;Print the character |
||||
mov ah, 0xe |
||||
int 0x10 |
||||
;Repeat for the next character |
||||
jmp .loop |
||||
|
||||
.done: |
||||
|
||||
;Load the initial registers from the stack |
||||
pop si |
||||
pop ax |
||||
|
||||
iret |
||||
;Print a string ending in a null from SI. |
||||
|
||||
printstr: |
||||
|
||||
;Store the initial registers in the stack |
||||
push ax |
||||
push si |
||||
|
||||
;Print the string |
||||
.loop: |
||||
;Load the current character |
||||
lodsb |
||||
;Check for the string end |
||||
cmp al, 0x0 |
||||
je .done |
||||
;Print the character |
||||
mov ah, 0xe |
||||
int 0x10 |
||||
;Repeat for the next character |
||||
jmp .loop |
||||
|
||||
.done: |
||||
|
||||
;Load the initial registers from the stack |
||||
pop si |
||||
pop ax |
||||
|
||||
iret |
||||
|
@ -1,12 +1,12 @@
|
||||
;Read a string ending in null of at most AL characters to DI until a return and print a CRLF. |
||||
|
||||
readln: |
||||
|
||||
;Read the string |
||||
mov ah, 0x1 |
||||
int 0x21 |
||||
|
||||
;Print a CRLF |
||||
call printcrlf |
||||
|
||||
iret |
||||
;Read a string ending in null of at most AL characters to DI until a return and print a CRLF. |
||||
|
||||
readln: |
||||
|
||||
;Read the string |
||||
mov ah, 0x1 |
||||
int 0x21 |
||||
|
||||
;Print a CRLF |
||||
call printcrlf |
||||
|
||||
iret |
||||
|
@ -1,204 +1,204 @@
|
||||
;Read a string ending in a null of at most AL characters to DI until a return. |
||||
|
||||
readstr: |
||||
|
||||
;Store the initial registers in the stack |
||||
push ax |
||||
push bx |
||||
push cx |
||||
push dx |
||||
push di |
||||
|
||||
;Setup |
||||
;Store the input length in the stack |
||||
mov ah, 0 |
||||
push ax |
||||
;Initialise the destination with spaces |
||||
mov cx, ax |
||||
mov al, 0x20 |
||||
rep stosb |
||||
pop ax |
||||
push ax |
||||
sub di, ax |
||||
;Initialise the cursor pointer in BL and clear BH |
||||
mov bx, 0x1 |
||||
|
||||
.loop: |
||||
|
||||
;Read a keypress |
||||
mov ah, 0x0 |
||||
int 0x16 |
||||
|
||||
;Check for special keys and non-printing characters |
||||
;Check for return |
||||
cmp al, 0xd |
||||
je .return |
||||
;Check for backspace |
||||
cmp al, 0x8 |
||||
je .backspace |
||||
;Check for input end |
||||
pop dx |
||||
push dx |
||||
cmp bl, dl |
||||
je .loop |
||||
;Check for space |
||||
cmp al, 0x20 |
||||
je .space |
||||
;Check for erase |
||||
cmp al, 0x9 |
||||
je .erase |
||||
;Check for non-printing characters |
||||
cmp al, 0x1f |
||||
jle .loop |
||||
cmp al, 0x7f |
||||
je .loop |
||||
|
||||
;Store and print a character |
||||
.character: |
||||
;Store the character |
||||
stosb |
||||
;Print the character |
||||
mov ah, 0xe |
||||
int 0x10 |
||||
;Move the cursor pointer |
||||
inc bl |
||||
jmp .loop |
||||
|
||||
;Replace the cursor position with a space |
||||
.erase: |
||||
mov al, 0x20 |
||||
jmp .character |
||||
|
||||
;Move the cursor forward |
||||
.space: |
||||
call .nextchar |
||||
inc di |
||||
inc bl |
||||
jmp .loop |
||||
|
||||
;Move the cursor backward |
||||
.backspace: |
||||
;Check for the input beginning |
||||
cmp bl, 0x1 |
||||
je .loop |
||||
;Move the cursor |
||||
call .prevchar |
||||
dec di |
||||
dec bl |
||||
jmp .loop |
||||
|
||||
;Finish reading the string |
||||
.return: |
||||
|
||||
;Find and remove trailing spaces |
||||
;Go to the end of the input |
||||
pop ax |
||||
mov bh, 0x0 |
||||
sub ax, bx |
||||
push di |
||||
add di, ax |
||||
.findtrailing: |
||||
;Check for a trailing space |
||||
cmp byte [di], 0x20 |
||||
je .deltrailing |
||||
jmp .end |
||||
.deltrailing: |
||||
;Delete a trailing space |
||||
mov al, 0x0 |
||||
stosb |
||||
sub di, 0x2 |
||||
jmp .findtrailing |
||||
|
||||
;Move the cursor to the end of the input string |
||||
.end: |
||||
pop di |
||||
.findend: |
||||
cmp byte [di], 0x0 |
||||
jne .notend |
||||
jmp .done |
||||
.notend: |
||||
call .nextchar |
||||
inc di |
||||
inc bl |
||||
jmp .findend |
||||
|
||||
.done: |
||||
|
||||
;Load the initial registers from the stack |
||||
pop di |
||||
pop dx |
||||
pop cx |
||||
pop bx |
||||
pop ax |
||||
|
||||
iret |
||||
|
||||
;Move the cursor forward |
||||
|
||||
;Move forward within a line |
||||
.nextchar: |
||||
;Get the cursor position |
||||
mov ah, 0x3 |
||||
int 0x10 |
||||
;Check for the end of the line |
||||
cmp dl, 0x4f |
||||
je .nextln |
||||
;Move |
||||
inc dl |
||||
mov ah, 0x2 |
||||
int 0x10 |
||||
ret |
||||
|
||||
;Move to the beginning of the next line |
||||
.nextln: |
||||
;Check if the current line is the last on screen |
||||
cmp dh, 0x18 |
||||
je .scroll |
||||
;Move |
||||
mov ah, 0x2 |
||||
inc dh |
||||
mov dl, 0x0 |
||||
int 0x10 |
||||
ret |
||||
|
||||
;Scroll the screen up by one line |
||||
.scroll: |
||||
;Scroll |
||||
mov ah, 0x6 |
||||
mov al, 0x1 |
||||
mov bh, 0x7 |
||||
mov ch, 0x0 |
||||
mov cl, 0x0 |
||||
mov dh, 0x18 |
||||
mov dl, 0x4f |
||||
int 0x10 |
||||
;Move to the beginning of the new line |
||||
mov ah, 0x2 |
||||
mov bh, 0x0 |
||||
mov dl, 0x0 |
||||
int 0x10 |
||||
ret |
||||
|
||||
;Move the cursor backward |
||||
|
||||
;Move backward within a line |
||||
.prevchar: |
||||
;Get the cursor position |
||||
mov ah, 0x3 |
||||
int 0x10 |
||||
;Check for the beginning of the line |
||||
cmp dl, 0x0 |
||||
je .prevln |
||||
;Move |
||||
dec dl |
||||
mov ah, 0x2 |
||||
int 0x10 |
||||
ret |
||||
|
||||
;Move to the end of the previous line |
||||
.prevln: |
||||
mov ah, 0x2 |
||||
dec dh |
||||
mov dl, 0x4f |
||||
int 0x10 |
||||
ret |
||||
;Read a string ending in a null of at most AL characters to DI until a return. |
||||
|
||||
readstr: |
||||
|
||||
;Store the initial registers in the stack |
||||
push ax |
||||
push bx |
||||
push cx |
||||
push dx |
||||
push di |
||||
|
||||
;Setup |
||||
;Store the input length in the stack |
||||
mov ah, 0 |
||||
push ax |
||||
;Initialise the destination with spaces |
||||
mov cx, ax |
||||
mov al, 0x20 |
||||
rep stosb |
||||
pop ax |
||||
push ax |
||||
sub di, ax |
||||
;Initialise the cursor pointer in BL and clear BH |
||||
mov bx, 0x1 |
||||
|
||||
.loop: |
||||
|
||||
;Read a keypress |
||||
mov ah, 0x0 |
||||
int 0x16 |
||||
|
||||
;Check for special keys and non-printing characters |
||||
;Check for return |
||||
cmp al, 0xd |
||||
je .return |
||||
;Check for backspace |
||||
cmp al, 0x8 |
||||
je .backspace |
||||
;Check for input end |
||||
pop dx |
||||
push dx |
||||
cmp bl, dl |
||||
je .loop |
||||
;Check for space |
||||
cmp al, 0x20 |
||||
je .space |
||||
;Check for erase |
||||
cmp al, 0x9 |
||||
je .erase |
||||
;Check for non-printing characters |
||||
cmp al, 0x1f |
||||
jle .loop |
||||
cmp al, 0x7f |
||||
je .loop |
||||
|
||||
;Store and print a character |
||||
.character: |
||||
;Store the character |
||||
stosb |
||||
;Print the character |
||||
mov ah, 0xe |
||||
int 0x10 |
||||
;Move the cursor pointer |
||||
inc bl |
||||
jmp .loop |
||||