diff --git a/.gitignore b/.gitignore index a8a0dce..7cf5001 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ *.bin +*.img diff --git a/Makefile b/Makefile index 342f23b..ce40e01 100644 --- a/Makefile +++ b/Makefile @@ -1,20 +1,28 @@ NASM = nasm +PYTHON = python3 .SUFFIXES: -.SUFFIXES: .bin .asm +.SUFFIXES: .bin .asm .ansi -all: ponydos.bin +all: ponydos.img -run: ponydos.bin - qemu-system-i386 -fda ponydos.bin +ponydos.img: ponydos.bin wallpaper.bin + rw -i /dev/zero -o $@ -c 1440K + rw -i ponydos.bin -o $@ + rw -i wallpaper.bin -o $@ -O 512 .asm.bin: $(NASM) -fbin -o $@ $< +.ansi.bin: + $(PYTHON) process_wallpaper.py $< $@ 7 0 0 0 + +run: ponydos.img + qemu-system-i386 -fda $< + clean: - rm -f *.bin + rm -f *.bin *.img distclean: clean - qemu-system .PHONY: all run clean distclean diff --git a/ponydos.asm b/ponydos.asm index 6d9ff5a..27cd574 100644 --- a/ponydos.asm +++ b/ponydos.asm @@ -6,6 +6,8 @@ org 0x7c00 COLUMNS equ 80 ROWS equ 25 +WALLPAPER equ 0x500 + jmp 0:start start: cld @@ -27,6 +29,8 @@ start: xor al, al rep stosb + mov [boot_disk], dl + initialize_mouse: ; Initialize mouse ; https://www.ctyme.com/intr/rb-1601.htm @@ -60,21 +64,26 @@ initialize_mouse: .done: -initialize_screen: - mov ax, 0xb800 ; VGA text video memory - mov es, ax - +load_wallpaper: ; Clear screen with asterisks and 15-0 background-foreground mov ax, 0xf000 + '*' mov cx, 25*80 - xor di, di + mov di, WALLPAPER rep stosw + mov ax, 1 + mov bl, [boot_disk] + mov cx, 8 + mov di, WALLPAPER + call read_sectors + +initialize_screen: ; Disable text cursor mov ah, 0x01 mov ch, 0x20 int 0x10 + call draw_wallpaper call flip_mouse_cursor mainloop: @@ -98,6 +107,30 @@ mainloop: call flip_mouse_cursor jmp mainloop +; ------------------------------------------------------------------ +; Drawing subroutines +; ------------------------------------------------------------------ + +draw_wallpaper: + push ax + push bx + push cx + push es + + mov bx, 0xb800 + mov es, bx + + mov si, WALLPAPER + xor di, di + mov cx, 80*25 + rep movsw + + pop es + pop cx + pop bx + pop ax + ret + flip_mouse_cursor: push ax push bx @@ -131,6 +164,75 @@ flip_mouse_cursor: pop ax ret +; ------------------------------------------------------------------ +; Disk subroutines +; ------------------------------------------------------------------ + +; in: +; ax = LBA of first sector +; bl = drive number +; cx = number of sectors to read (must be at least 1) +; es:di = output buffer +read_sectors: + push ax + push cx + push di + + .loop: + call read_sector + inc ax + add di, 512 + loop .loop + + pop di + pop cx + pop ax + ret + +; in: +; ax = LBA of first sector +; bl = drive number +; es:di = output buffer +read_sector: + push ax + push bx + push cx + push dx + + mov cx, 18 + div cx + + ; cl = sector (1…18) + mov cl, dl + inc cl + + ; dh = head (0…1) + mov dh, 1 + and dh, al + + ; ch = cylinder + shr ax, 1 + mov ch, al + + ; dl = drive number + mov dl, bl + + ; es:bx = output buffer + mov bx, di + + mov ax, 0x0201 ; read one sector + int 0x13 + + pop dx + pop cx + pop bx + pop ax + ret + +; ------------------------------------------------------------------ +; Mouse callback +; ------------------------------------------------------------------ + Y_OVERFLOW equ 0x80 X_OVERFLOW equ 0x40 Y_NEGATIVE equ 0x20 @@ -205,6 +307,10 @@ mouse_handler: pop ax retf +; ------------------------------------------------------------------ +; Debug routines +; ------------------------------------------------------------------ + hexprint16: xchg ah, al call hexprint8 @@ -234,10 +340,22 @@ hexprint4: pop ax ret +hang: + hlt + jmp hang + +; ------------------------------------------------------------------ +; Padding and boot sector signature +; ------------------------------------------------------------------ + times 510-($-$$) db 0 db 0x55 db 0xaa +; ------------------------------------------------------------------ +; Zero-initialized variables +; ------------------------------------------------------------------ + section .bss _bss_start: @@ -248,4 +366,6 @@ mouse_buttons resb 1 mouse_column resb 1 mouse_row resw 1 +boot_disk resb 1 + _bss_end: diff --git a/process_wallpaper.py b/process_wallpaper.py new file mode 100644 index 0000000..45f11cf --- /dev/null +++ b/process_wallpaper.py @@ -0,0 +1,82 @@ +import sys + +WIDTH = 80 +HEIGHT = 25 +# ANSI orders the colours black, red, green, yellow, blue, magenta, cyan, white +# VGA orders them black, blue, green, cyan, red, magenta, yellow, white +color_map = [0, 4, 2, 6, 1, 5, 3, 7] + +if len(sys.argv) != 7: + print("Usage: {sys.argv[0]} infile outfile default_fgcolor default_bgcolor origin_x origin_y", file=sys.stderr) + sys.exit(1) + +infile = sys.argv[1] +outfile = sys.argv[2] + +default_fgcolor = int(sys.argv[3]) +assert 0 <= default_fgcolor <= 15 +default_bgcolor = int(sys.argv[4]) +assert 0 <= default_bgcolor <= 15 + +origin_x = int(sys.argv[5]) +assert 0 <= origin_x < WIDTH +origin_y = int(sys.argv[6]) +assert 0 <= origin_y < HEIGHT + +chars = [bytearray([0]*HEIGHT) for _ in range(WIDTH)] +attributes = [[(default_fgcolor, default_bgcolor)]*HEIGHT for _ in range(WIDTH)] + +with open(infile, 'rb') as f: + ansitext = f.read() + +x = origin_x +y = origin_y +fgcolor = default_fgcolor +bgcolor = default_bgcolor + +line = 1 +line_start = 0 +index = 0 +while index < len(ansitext): + if ansitext[index:].startswith(b'\x1b['): + index += len(b'\x1b[') + escape_length = ansitext[index:].index(b'm') + escape = ansitext[index:index+escape_length] + for color_parameter in escape.split(b';'): + color_parameter = int(color_parameter) + if color_parameter == 39: + fgcolor = default_fgcolor + elif color_parameter == 49: + bgcolor = default_bgcolor + elif 30 <= color_parameter <= 37: + fgcolor = color_map[color_parameter - 30] + elif 40 <= color_parameter <= 47: + bgcolor = color_map[color_parameter - 40] + elif 90 <= color_parameter <= 97: + fgcolor = color_map[color_parameter - 90] + 8 + elif 100 <= color_parameter <= 107: + bgcolor = color_map[color_parameter - 100] + 8 + else: + print(f'{line},{index-line_start+1}: Unknown colour escape {color_parameter}') + index += escape_length + len(b'm') + elif ansitext[index] == 13: + x = origin_x + index += 1 + elif ansitext[index] == 10: + x = origin_x + y += 1 + index += 1 + line += 1 + line_start = index + else: + chars[x][y] = ansitext[index] + attributes[x][y] = (fgcolor, bgcolor) + index += 1 + x += 1 + +with open(outfile, 'wb') as f: + for y in range(HEIGHT): + for x in range(WIDTH): + fgcolor, bgcolor = attributes[x][y] + char = chars[x][y] + f.write(bytes([char, (bgcolor<<4) | fgcolor])) diff --git a/wallpaper.ansi b/wallpaper.ansi new file mode 100644 index 0000000..2568804 --- /dev/null +++ b/wallpaper.ansi @@ -0,0 +1,25 @@ +ANSI art is my +passion +line 3 +line 4 +line 5 +line 6 +line 7 +line 8 +line 9 +line 10 +line 11 +line 12 +line 13 +line 14 +line 15 +line 16 +line 17 +line 18 +line 19 +line 20 +line 21 +line 22 +line 23 +line 24 +line 25