-
-
Save berkus/5181359 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
; | |
; Selfer. | |
; | |
; 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/ | |
; | |
BITS 16 | |
ORG 0x7C00 | |
; Some defines. | |
%define SECTOR_SIZE 512 | |
%define SECTORS_PER_TRACK 18 | |
%define HEADS 2 | |
%define TRACKS 80 | |
%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 | |
CPU 8086 | |
; 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. | |
CPU 386 | |
; I was lazy, so just stuffed this here. | |
; EBP + SI -> somewhere. | |
; | |
; Returns: | |
; GS:BX -> same somewhere. | |
.GetSegment: | |
add ebp, esi | |
mov ebx, ebp | |
shr ebx, 4 | |
and bx, 0xF000 | |
mov gs, bx | |
mov bx, bp | |
sub ebp, esi | |
ret | |
CPU 8086 | |
.Error386: | |
mov al, '$' | |
.Error: | |
; Print AL. | |
xor bx, bx | |
mov ah, 0x0E | |
int 0x10 | |
jmp $ | |
CPU 386 | |
.GetLBA: | |
mov ebx, ebp | |
shr ebx, 9 | |
sub bx, 0x3E | |
; Since a write is going to follow, do that. | |
xor di, di | |
inc di | |
ret | |
CPU 8086 | |
.FlushCS: | |
; Set up segments. | |
xor bx, bx | |
; Stack. | |
mov ss, bx | |
mov sp, Main | |
mov ds, bx | |
; Check if CPU is 80386 or not. | |
; Invert the IOPL bits, since on 8086/80186 they are hardwired to 1. | |
; In real mode on the 286, they are always 0, though. | |
pushf | |
pop ax | |
xor ah, 0x30 | |
; Get them back. | |
push ax | |
popf | |
; Get flags to check if anything changed. | |
pushf | |
; Get new flags in CX. | |
pop cx | |
; Test if bits changed, or not. | |
xor ah, ch | |
test ah, ah | |
jnz .Error386 | |
CPU 386 | |
cld | |
mov [FREE_SPACE], dl | |
; Set to mode 0x03, or 80x25 text mode. | |
; AH should be zero as used above. | |
mov al, 0x03 | |
int 0x10 | |
; Set EBP (base address of sector/512b segment) | |
.ReturnJmp: | |
movzx ebp, sp | |
xor esi, esi | |
; Events. | |
EventLoop: | |
call OutputSector | |
; GS:BX now points to where we are. | |
call Main.GetSegment | |
xor ah, ah | |
; Get input. | |
int 0x16 | |
; Left key (previous byte) | |
.Left: | |
cmp ah, LEFT_SCANCODE | |
jne .Right | |
dec si | |
jmp .Next | |
; Right key (next byte) | |
.Right: | |
cmp ah, RIGHT_SCANCODE | |
jne .Retain | |
inc si | |
jmp .Next | |
; Retain. | |
.Retain: | |
cmp al, 'r' | |
jne .Paste | |
; Save the current byte pointing too. | |
mov [FREE_SPACE + 2], gs | |
mov [FREE_SPACE + 4], bx | |
jmp .Next | |
; Paste. | |
.Paste: | |
cmp al, 'p' | |
jne .Jump | |
; Get the pair in ES:DI. | |
mov edi, [FREE_SPACE + 2] | |
mov es, di | |
shr edi, 16 | |
; Paste the byte. | |
mov cx, [es:di] | |
mov [gs:bx], cx | |
; Move one point below, and decrement the byte pointing to at. | |
dec si | |
dec word [FREE_SPACE + 4] | |
; We wrote something. | |
jmp .WroteNext | |
; Jumps to the current cell. | |
.Jump: | |
cmp al, 'j' | |
jne .Write | |
; So that you can far ret from the code. | |
push word 0x0000 | |
push Main.ReturnJmp | |
; Get where to jump to. | |
push gs | |
push bx | |
retf | |
; Write's the current sector. | |
.Write: | |
cmp al, 'w' | |
jne .DownUp | |
; Get the LBA, and write. | |
call Main.GetLBA | |
call RWSector | |
jmp .Next | |
; Down/up key (next sector/previous sector) | |
.DownUp: | |
cmp ah, DOWN_SCANCODE | |
jne .Up | |
; Not fit? | |
cmp ebp, 0x80000 - SECTOR_SIZE | |
je .Next | |
jmp .Cont | |
.Up: | |
cmp ah, UP_SCANCODE | |
jne .Input | |
; Not fit? | |
cmp ebp, 0x7C00 | |
je .Next | |
.Cont: | |
call Main.GetLBA | |
cmp byte [FREE_SPACE + 1], 1 | |
je .SkipWrite | |
call RWSector | |
.SkipWrite: | |
cmp ah, DOWN_SCANCODE | |
jne .Up2 | |
; Down. | |
add ebp, SECTOR_SIZE | |
inc bx | |
jmp .ReadSector | |
.Up2: | |
sub ebp, SECTOR_SIZE | |
dec bx | |
.ReadSector: | |
dec di | |
call RWSector | |
mov byte [FREE_SPACE + 1], 1 | |
jmp .Next | |
.Input: | |
; Get another keystroke. | |
xor ah, ah | |
shl eax, 16 | |
int 0x16 | |
xchg ah, al | |
shr eax, 8 | |
mov cx, 2 | |
.GetHexLoop: | |
; Get '0' to '9'. | |
sub al, 48 | |
; If larger, subtract 7 to get hex. | |
cmp al, 9 | |
jbe .NextChar | |
; If larger than 0xF still, then perhaps lowercase. | |
sub al, 7 | |
cmp al, 0xF | |
jbe .NextChar | |
; Get normal digit. | |
sub al, 32 | |
.NextChar: | |
xchg al, ah | |
loop .GetHexLoop | |
.Display: | |
shl al, 4 | |
shr ax, 4 | |
mov [gs:bx], al | |
inc si | |
.WroteNext: | |
; Zero out FREE_SPACE + 1. | |
xor al, al | |
mov [FREE_SPACE + 1], al | |
.Next: | |
and si, 0x1FF | |
jmp EventLoop | |
; Read/write a sector. | |
; BX -> logical block address. | |
; EBP -> where to read/write to/from. | |
; EDI -> 0x00 for read; 0x01 for write. | |
RWSector: | |
pushad | |
xchg ax, bx | |
xor si, si | |
call Main.GetSegment | |
mov cx, gs | |
mov es, cx | |
; Three tries. | |
mov si, 3 | |
; Get CHS. | |
; CH -> cylinder number. | |
; CL -> sector number. | |
; DH -> head number. | |
xor dx, dx | |
mov cx, SECTORS_PER_TRACK | |
div cx | |
; Get sector. | |
mov cl, dl | |
inc cl | |
; Get head number. | |
mov dh, al | |
and dh, 0x1 | |
; Get track number. | |
shr ax, 1 | |
mov ch, al | |
mov dl, [FREE_SPACE] | |
shl di, 8 | |
.Loop: | |
clc | |
; Prepare for interrupt. | |
mov ax, 0x0201 | |
add ax, di | |
int 0x13 | |
jnc .Return | |
xor ah, ah | |
int 0x13 | |
dec si | |
jnz .Loop | |
; Get in the character. | |
mov al, '@' | |
jmp Main.Error | |
.Return: | |
popad | |
ret | |
; Converts a byte to a ASCII hexadecimal value. | |
; DL -> the byte to convert. | |
; | |
; Returns: | |
; AX -> the output. | |
HexToASCII: | |
movzx ax, dl | |
shl ax, 4 | |
shr al, 4 | |
mov dl, 2 | |
.Loop: | |
cmp al, 9 | |
jg .Char | |
add al, 48 | |
jmp .Next | |
.Char: | |
add al, 55 | |
.Next: | |
xchg ah, al | |
dec dl | |
jnz .Loop | |
ret | |
; Output an entire sector. | |
; EBP -> contains the base address of the sector to output. | |
; ESI -> current index. | |
OutputSector: | |
pushad | |
; ES for screen output. | |
mov ax, 0xB800 | |
mov es, ax | |
xor di, di | |
mov cx, 1024 | |
mov ax, 0x0F30 | |
rep stosw | |
mov ecx, ebp | |
add cx, si | |
shl si, 2 | |
and dword [es:si], 0x02000200 | |
xor si, si | |
call Main.GetSegment | |
.Loop: | |
mov dl, [gs:bx] | |
call HexToASCII | |
mov [es:si], ah | |
mov [es:si + 2], al | |
add si, 4 | |
inc bx | |
cmp si, 2048 | |
jb .Loop | |
mov edx, ecx | |
mov si, 0xF0E | |
mov cx, 4 | |
.L2: | |
call HexToASCII | |
mov [es:si], al | |
mov [es:si - 2], ah | |
.Next: | |
sub si, 4 | |
shr edx, 8 | |
loop .L2 | |
popad | |
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