Skip to content

Instantly share code, notes, and snippets.

@shikhin shikhin/Selfer (ext).asm
Last active Dec 15, 2015

Embed
What would you like to do?
Following is Selfer, which is an attempt at a stand-alone system to create, well, new systems! (this is the hard disk version, expecting to be passed control from a MBR)
;
; Selfer (extended).
;
; Shikhin Sethi, the author of selfer, has dedicated the work to the public domain
; by waiving all of his or her rights to the work worldwide under copyright law,
; including all related and neighboring rights, to the extent allowed by law.
;
; You can copy, modify, distribute and perform the work, even for commercial purposes,
; all without asking permission.
;
; https://creativecommons.org/publicdomain/zero/1.0/
;
; 16 bits, starting at 0x7C00.
BITS 16
ORG 0x7C00
; DEFINES.
%define SECTOR_SIZE 512
%define READ_SECTOR 0x00
%define WRITE_SECTOR 0x01
%define LEFT_SCANCODE 75
%define RIGHT_SCANCODE 77
%define UP_SCANCODE 72
%define DOWN_SCANCODE 80
%define FREE_SPACE 0x500
%define MEANING_OF_LIFE 0x42
%define NOP_OPCODE 0x90
%define F_OPCODE(no) 0x3A + no
%define LINE_LENGTH 40
; Output an entire sector.
; EBP -> contains the base address of the sector to output.
; ESI -> current index.
%macro OUTPUT_SECTOR 0
pushad
; Set ES for screen output.
mov ax, 0xB800
mov es, ax
; Memset the entire display memory (till the point we display) to 0x0F300B30,
; such that colors are: white & light cyan.
xor di, di
mov cx, 512
mov eax, 0x0F300B30
rep stosd
; Store BP in BX, and get exact index in AX.
mov bx, bp
add bp, si
xchg bp, ax
; At si * 4 (since every si represents two bytes, i.e. two characters,
; and there are two bytes for each character in display memory) get red color.
shl si, 2
mov dword [es:si], 0x0C300C30
; Display the address.
; We start outputting from 0xF06 into display memory, and move down.
mov si, 0xF06
; Two bytes (a word address).
mov cx, 2
.LoopAddress:
; Get the ASCII values and store it.
call HexToASCII
mov [es:si], dl
mov [es:si - 2], dh
; Move 4 down (two bytes displayed).
sub si, 4
; Get the next byte in address.
shr ax, 8
loop .LoopAddress
; Start from index 0, and display the entire sector.
xor si, si
.LoopIndex:
; Get the byte in DL, and call HexToASCII.
mov al, [bx]
call HexToASCII
; Store DH & DL in display memory.
mov [es:si], dh
mov [es:si + 2], dl
; Increment BX, to get to the next byte.
inc bx
; Add 4 to si (for reasons described above), and if we've done 2048,
; that is, one sector, then we're done.
add si, 4
; If below 2048, then loop.
cmp si, 2048
jb .LoopIndex
popad
%endmacro
CPU 386
; Main entry point where BIOS leaves us.
; DL -> Expects the drive number to be present in dl.
; CS:IP -> Expects CS:IP to point to the linear address 0x7C00.
Main:
jmp 0x0000:.FlushCS ; Some BIOS' may load us at 0x0000:0x7C00, while others at 0x07C0:0x0000. Let's just make this uniform.
; If the BIOS doesn't support int 0x13 extensions, display '$'.
.ErrorInt13Ext:
mov al, '$'
; General error function.
.Error:
; Print whatever is in AL via the BIOS function.
xor bx, bx
mov ah, 0x0E
int 0x10
; Halt!
jmp $
; Flush segments.
.FlushCS:
; Check for int 0x13 extensions.
mov ah, 0x41
mov bx, 0x55AA
int 0x13
; Error if carry set.
jc .ErrorInt13Ext
; Set up segments.
xor ax, ax
; Stack.
mov ss, ax
mov sp, Main
; Clear direction flag.
cld
; Get LBA into ECX.
mov ecx, [si + 8]
; DS segment.
mov ds, ax
; Store the boot drive number.
mov [FREE_SPACE], dl
; Store the LBA.
mov [FREE_SPACE + 3], ecx
; Set to mode 0x03, or 80x25 text mode.
; AH should be zero as used above.
mov al, 0x03
int 0x10
; Hide the hardware cursor.
mov ch, 0x26
mov ah, 0x1
int 0x10
; Index 0.
xor si, si
; Read everything from 0x7C00 to 0x10000.
; Start from the end, i.e. 0x10000 - 0x200, and
; LBA 0x41.
mov bp, 0x10000 - 0x200
xor di, di
mov bx, MEANING_OF_LIFE - 1
.NextSector:
call RWSector
sub bp, SECTOR_SIZE
; Read till all not read.
dec bx
jnz .NextSector
; If there is a NOP, we'd need the address to jump to in BX.
mov bx, 0x7E00
; If the value at 0x7E00 is a NOP, then simply jump to it.
cmp byte [0x7E00], NOP_OPCODE
je EventLoop.CallCode
; Events.
EventLoop:
; Display the current sector.
OUTPUT_SECTOR
xor ah, ah
; Get input.
int 0x16
; BX points to the exact index.
mov bx, bp
add bx, si
; Left key (previous byte)
.Left:
cmp ah, LEFT_SCANCODE
jne .Right
; Go to previous byte.
dec si
jmp .Next
; Right key (next byte)
.Right:
cmp ah, RIGHT_SCANCODE
jne .NextLine
; Go the next byte.
inc si
jmp .Next
; Go to the next line.
.NextLine:
cmp ah, DOWN_SCANCODE
jne .PreviousLine
add si, LINE_LENGTH
jmp .Next
; Go to the previous line.
.PreviousLine:
cmp ah, UP_SCANCODE
jne .Retain
sub si, LINE_LENGTH
jmp .Next
; Retain.
.Retain:
cmp al, 'K'
jne .Move
; Save the current byte pointing too.
mov [FREE_SPACE + 1], bx
jmp .Next
; Sort-of like memmove.
.Move:
cmp al, 'M'
jne .Paste
jmp .CommonPaste
; Paste.
.Paste:
cmp al, 'P'
jne .Run
.CommonPaste:
; Get the address in DI.
mov di, [FREE_SPACE + 1]
; Get the byte into CL, and then into current index.
mov cl, [di]
mov [bx], cl
; If it was memmove, then we decrement pointer.
cmp al, 'M'
je .DecrementPointer
; Or increment pointer, for paste.
.IncrementPointer:
inc si
inc word [FREE_SPACE + 1]
jmp .Next
.DecrementPointer:
; Move one point below, and decrement the byte pointing to at.
dec si
dec word [FREE_SPACE + 1]
jmp .Next
; Run from the current cell.
.Run:
cmp al, 'R'
jne .Save
.CallCode:
; Save everything important.
push bp
push si
push ds
push es
call bx
.RetRun:
pop es
pop ds
pop si
pop bp
jmp .Next
; Save the current sector.
.Save:
cmp al, 'S'
jne .Write
; Write.
xor di, di
inc di
; Get the LBA into BX.
shr bx, 9
sub bx, 0x3E
call RWSector
jmp .Next
; Write's all the sectors.
.Write:
cmp al, 'W'
jne .NextSector
push bp
; Write everything from 0x7C00 to 0x10000.
; Again, we do it in reverse order here.
mov bp, 0x10000 - 0x200
xor di, di
inc di
mov bx, MEANING_OF_LIFE - 1
.LoopSector:
call RWSector
sub bp, SECTOR_SIZE
; Loop till all sectors not done.
dec bx
jnz .LoopSector
pop bp
jmp .Next
; Next sector.
.NextSector:
cmp al, 'X'
jne .PreviousSector
; Are we going beyond our region?
cmp bp, 0x10000 - SECTOR_SIZE
je .Next
; If not, move one sector above.
add bp, SECTOR_SIZE
jmp .Next
; Previous sector.
.PreviousSector:
cmp al, 'Z'
jne .Input
nop
nop
; Are we going beyond our region?
cmp bp, 0x7C00
je .Next
; If not, move one sector below.
sub bp, SECTOR_SIZE
.Next:
; Mask of SI.
and si, 0x1FF
jmp EventLoop
.Input:
; Get another keystroke.
shl eax, 16
int 0x16
; Arrange both into AX.
xchg ah, al
shr eax, 8
; If second key was ESC, then throw away input.
cmp al, 0x1B
je .Next
mov cx, 2
.GetHexLoop:
; Get '0' to '9'.
sub al, 48
; If larger, subtract 7 to get hex.
cmp al, 9
jbe .NextChar
sub al, 7
.NextChar:
; Do next character, now.
xchg al, ah
loop .GetHexLoop
; Display whatever was outputted.
.Display:
shl al, 4
shr ax, 4
mov [bx], al
; Go to next byte.
inc si
jmp .Next
; Read/write a sector.
; EBX -> logical block address.
; EBP -> where to read/write to/from.
; EDI -> 0x00 for read; 0x01 for write.
RWSector:
pushad
; DS:SI now points to the buffer.
mov si, FREE_SPACE + 7
xor ecx, ecx
; Zero it up.
mov [si + 4], ecx
mov [si + 12], ecx
; 0x0001 -> one block.
; 0x00 -> reserved.
; 0x10 -> size of packet.
mov dword [si], 0x00010010
add ebx, [si - 4]
; Get the LBA into EBX.
mov [si + 4], bp
mov [si + 8], ebx
; 0x42 for read; 0x43 for write.
shl di, 8
mov ah, 0x42
add ax, di
; Boot drive number into DL.
mov dl, [FREE_SPACE]
int 0x13
jc .Error
.Return:
popad
ret
.Error:
; Get in the character.
mov al, '@'
jmp Main.Error
; Converts a byte to a ASCII hexadecimal value.
; AL -> the byte to convert.
;
; Returns:
; DX -> the output.
HexToASCII:
movzx dx, al
shl dx, 4
shr dl, 4
mov al, 2
.Loop:
cmp dl, 9
jg .Char
add dl, 48
jmp .Next
.Char:
add dl, 55
.Next:
xchg dh, dl
dec al
jnz .Loop
ret
; Padding.
times 510 - ($ - $$) db 0
BIOSSignature:
dw 0xAA55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.