From a86c9533d817323ac992cf89efe5c2e4c1aa59d3 Mon Sep 17 00:00:00 2001 From: Plaza521 <89989298+Plaza521@users.noreply.github.com> Date: Wed, 15 Mar 2023 00:59:50 +0300 Subject: [PATCH] add code No more commits because I don't work on it anymore --- bootloader.asm | 160 +++++++ constants.asm | 11 + data.asm | 117 +++++ fasm | Bin 0 -> 107111 bytes filetable.asm | 8 + funcs.asm | 846 +++++++++++++++++++++++++++++++++++++ functable.asm | 74 ++++ help.txt | 152 +++++++ iso/boot.img | Bin 0 -> 1474560 bytes iso/calc.bin | Bin 0 -> 295 bytes iso/just_text.t | 10 + iso/notepad.bin | Bin 0 -> 47 bytes iso/os.bin | Bin 0 -> 8192 bytes iso/snake.bin | Bin 0 -> 512 bytes iso/viewer.bin | Bin 0 -> 3024 bytes kernel.asm | 480 +++++++++++++++++++++ macros.asm | 71 ++++ programs/calculator.asm | 232 ++++++++++ programs/dskview.asm | 58 +++ programs/hw.asm | 6 + programs/include.asm | 52 +++ programs/notepad.asm | 44 ++ programs/programmacros.asm | 71 ++++ programs/snake.asm | 274 ++++++++++++ programs/viewer.asm | 72 ++++ run.sh | 35 ++ secstage.asm | 36 ++ 27 files changed, 2809 insertions(+) create mode 100644 bootloader.asm create mode 100644 constants.asm create mode 100644 data.asm create mode 100644 fasm create mode 100644 filetable.asm create mode 100644 funcs.asm create mode 100644 functable.asm create mode 100644 help.txt create mode 100644 iso/boot.img create mode 100644 iso/calc.bin create mode 100644 iso/just_text.t create mode 100644 iso/notepad.bin create mode 100644 iso/os.bin create mode 100644 iso/snake.bin create mode 100644 iso/viewer.bin create mode 100644 kernel.asm create mode 100644 macros.asm create mode 100644 programs/calculator.asm create mode 100644 programs/dskview.asm create mode 100644 programs/hw.asm create mode 100644 programs/include.asm create mode 100644 programs/notepad.asm create mode 100644 programs/programmacros.asm create mode 100644 programs/snake.asm create mode 100644 programs/viewer.asm create mode 100644 run.sh create mode 100644 secstage.asm diff --git a/bootloader.asm b/bootloader.asm new file mode 100644 index 0000000..3d44d20 --- /dev/null +++ b/bootloader.asm @@ -0,0 +1,160 @@ +USE16 +format binary as 'bin' + +include 'macros.asm' +include 'constants.asm' + +org 7C00h + +start: + mov byte [BOOT_DRIVE], dl + mov ax, [0410h] + mov word [DETECTED_HARDWARE], ax + + cli + xor ax, ax + mov ds, ax + mov es, ax + mov ss, ax + mov sp, 1000h + sti + + mov ax, 3 + int 10h + + mov ax, 1301h + mov cx, 10 + mov bp, loadingdb + xor dx, dx + mov bx, 7 + int 10h + + mov ah, 02h + ; Number of sectors to load + ; mov al, PROGRAM_LENGTH + mov al, 4 + mov dl, [BOOT_DRIVE] + ; Cylinder + mov ch, 0 + ; Head + mov dh, 0 + ; Start sector number + mov cl, 2 + ; Where load + mov bx, functable + int 13h + + jc booterror + + ; call main + jmp secstage + + ; call dbg_halt_cpu + +loadingdb db "Loading..." + +; Print unsigned int +; +; AX - Num to convert +; SI - Where save string +; --- +; SI - Pointer to string with converted num +its: + push cx + push bx + push di + push ax + + mov cx, 0 + mov bx, 10 + mov di, si + +its.push: + mov dx, 0 + div bx + inc cx + push dx + test ax, ax + jnz its.push +its.pop: + pop dx + add dl, '0' + push ax + mov ah, 0Eh + mov al, dl + int 10h + pop ax + inc di + dec cx + jnz its.pop + + pop ax + pop di + pop bx + pop cx + ret + +errcode db 0 +booterror: + mov [errcode], ah + mov ax, 3 + int 10h + mov ax, 1301h + mov bx, 7 + xor dx, dx + mov cx, 13 + mov bp, booterror.message + int 10h + xor ax, ax + mov al, [errcode] + call its + cli + hlt + +booterror.message db "Error! Code: " + +; times 507-($-$$) db 0 +; MBR partition table starting at byte 446 +times 443-($-$$) db 0 +DETECTED_HARDWARE dw 0 +BOOT_DRIVE db 0 + +; partition 1 (contains the kernel code) +boot_indicator: db 0x80 ; mark as bootable +starting_head: db 0x0 +starting_sector: db 0x1 ; bits 5-0 for sector and bits 7-6 are upper bits of cylinder +starting_cylinder: db 0x0 +system_id: db 0x7f ; just some ID that has not been used for anything else by standard +; the last sector of the partition should be 2880 +ending_head: db 1 ; here I assume the maximum number of heads as 255 +ending_sector: db 4 +ending_cylinder: db 0x0 +first_sector_lba: dd 0x1 ; first sector after the bootsector +total_sectors_in_partition: dd 21 ; 2880-1 because first sector is bootsector + +; partitions 2-4 are unused and therefore set to 0 +times 16 db 0 +times 16 db 0 +times 16 db 0 + +db 0x55, 0xAA + + +include 'functable.asm' + +include 'filetable.asm' + +include 'secstage.asm' + +include 'kernel.asm' + +include 'funcs.asm' + +include 'data.asm' + +VIDEO_MODE db 0 + +times 8192-($-$$) db 0 +; times 8400h-($-$$) db 0 + +; include 'programs/calculator.asm' diff --git a/constants.asm b/constants.asm new file mode 100644 index 0000000..e59be64 --- /dev/null +++ b/constants.asm @@ -0,0 +1,11 @@ +INNUM_BUFFER_SIZE equ 5 +SHNUM_BUFFER_SIZE equ 3 +ACTION_BUFFER_SIZE equ 3 +RESULT_BUFFER_SIZE equ 7 +USERINPUT_BUFFER_SIZE equ 64 +ARGUMENT_BUFFER_SIZE equ 16 +FILENAME_BUFFER_SIZE equ 11 + +PROGRAM_ADDRESS equ 1000h +PROGRAM_LENGTH equ 65 ; In sectors, without boot sector +TABLE_SIZE equ 64 diff --git a/data.asm b/data.asm new file mode 100644 index 0000000..dc4a465 --- /dev/null +++ b/data.asm @@ -0,0 +1,117 @@ +greetings db "Welcome to the Command Line (c)Plaza 2022-2023.", 0Dh, 0Ah, \ + "Write help to see list of commands.", 0 + +goodbye db "Uhadi!", 0 + +zx db "0x", 0 +ln db 0Dh, 0Ah, 0 +space db " ", 0 + +print_registers.axd db "AX: 0x", 0 +print_registers.bxd db "BX: 0x", 0 +print_registers.cxd db "CX: 0x", 0 +print_registers.dxd db "DX: 0x", 0 +print_registers.sid db "SI: 0x", 0 +print_registers.did db "DI: 0x", 0 +print_registers.esd db "ES: 0x", 0 +print_registers.dsd db "DS: 0x", 0 +print_registers.ssd db "SS: 0x", 0 +print_registers.fd db "FLAGS: 0x", 0 +print_registers.h db " ", 0 + +cmd.prefix db ">", 0 +cmd.inc_com db "Invalid command or filename. Type 'help' to see more.", 0Dh, 0Ah, 0 +cmd.file_notfound_err db "There are no files with this filename.", 0 + +cmd.cmd_help db "help", 0 +cmd.cmd_help_desc db "Commands:", 0Dh, 0Ah, \ + "mrun - Run program on user offset", 0Dh, 0Ah, \ + "stdfuncs - show standart functions", 0Dh, 0Ah, \ + "showmem - show memory on offset", 0Dh, 0Ah, \ + "writemem - write byte to memory", 0Dh, 0Ah, \ + "loadmem - load from disk to memory with offset", 0Dh, 0Ah, \ + "shutdown - turn off the computer", 0Dh, 0Ah, \ + "rfd - run program from disk", 0Dh, 0Ah, \ + "dir - show files on disk", 0Dh, 0Ah, \ + "rm - remove file", 0Dh, 0Ah, \ + "help - Show this menu", 0 + +cmd.cmd_mrun db "mrun", 0 +cmd.cmd_mrun_desc db "Enter offset>", 0 + +cmd.cmd_stdfuncs db "stdfuncs", 0 +cmd.cmd_stdfuncs_desc db "System functions:", 0 + +cmd.cmd_showmem db "showmem", 0 +cmd.cmd_showmem_usage db "Usage: showmem ", 0 +; cmd.cmd_showmem_desc db "Enter offset>", 0 +; cmd.cmd_showmem_desc1 db "Enter number of lines(16 bytes) to read>", 0 + +cmd.cmd_writemem db "writemem", 0 +cmd.cmd_writemem_desc db "Enter offset>", 0 +cmd.cmd_writemem_desc1 db "Enter value>", 0 + +cmd.cmd_addlink db "addlink", 0 +cmd.cmd_addlink_desc db "Enter size>", 0 +cmd.cmd_addlink_desc1 db "Enter filename>", 0 +cmd.cmd_addlink_usage db "Usage: addlink ", 0 +cmd.cmd_addlink_size db 0 + +cmd.cmd_loadmem db "loadmem", 0 +cmd.cmd_loadmem_desc db "Enter numbers of sectors to read>", 0 +cmd.cmd_loadmem_desc1 db "Enter drive to read>", 0 +cmd.cmd_loadmem_desc2 db "Enter cylinder>", 0 +cmd.cmd_loadmem_desc3 db "Enter head>", 0 +cmd.cmd_loadmem_desc4 db "Enter number from which sector read>", 0 +cmd.cmd_loadmem_desc5 db "Enter offset to load>", 0 +cmd.cmd_loadmem_sectors db 0 +cmd.cmd_loadmem_drive db 0 +cmd.cmd_loadmem_cylinder db 0 +cmd.cmd_loadmem_head db 0 +cmd.cmd_loadmem_loadoffset db 0 +cmd.cmd_loadmem_saveoffset dw 0 +cmd.cmd_loadmem_loaderr db "Error while reading. See error code in AL", 0 + +cmd.cmd_run_nonex db "File is not executable or link is broken", 0 + +cmd.cmd_rfd db "rfd", 0 +cmd.cmd_rfd_desc db "Enter len of program>", 0 +cmd.cmd_rfd_desc1 db "Enter drive>", 0 +cmd.cmd_rfd_desc4 db "Enter first sector>", 0 +cmd.cmd_rfd_locateerr db "There are no any programs at your address", 0 + +cmd.cmd_dir db "dir", 0 + +cmd.cmd_rm db "rm", 0 +cmd.cmd_rm_usage db "Usage: rm ", 0 + +; cmd.cmd_cf db "cf", 0 + +cmd.cmd_exit db "shutdown", 0 + +cmd.cmd_reboot db "reboot", 0 + +cmd.userinput_buf: + times USERINPUT_BUFFER_SIZE db 0 +db 0 + +cmd.userinput_com: + times ARGUMENT_BUFFER_SIZE db 0 +db 0 + +cmd.userinput_arg1: + times ARGUMENT_BUFFER_SIZE db 0 +db 0 + +cmd.userinput_arg2: + times ARGUMENT_BUFFER_SIZE db 0 +db 0 + +cmd.userinput_arg3: + times ARGUMENT_BUFFER_SIZE db 0 +db 0 + +cmd.cmd_mrun_buf: + times INNUM_BUFFER_SIZE db 0 +db 0 +cmd.cmd_mrun_offset dw 0 diff --git a/fasm b/fasm new file mode 100644 index 0000000000000000000000000000000000000000..bbe35c644b6d74bcca8ae3a9f31bf551e20efee0 GIT binary patch literal 107111 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�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 literal 0 HcmV?d00001 diff --git a/filetable.asm b/filetable.asm new file mode 100644 index 0000000..7f7955f --- /dev/null +++ b/filetable.asm @@ -0,0 +1,8 @@ +filetable: + filetable_ff db 0, 0, 6, 11, "kernel.bin", 0, 0 ; Cylinder, Head, Sector, Size, Name + db 0, 0, 17, 1, "calc.bin", 0, 0, 0, 0 + db 0, 0, 18, 2, "viewer.bin", 0, 0 + db 0, 1, 2, 1, 'null.bin', 0, 0, 0, 0 + db 0, 1, 3, 1, "snake.bin", 0, 0, 0 + +times 2048-($-$$) db 0 ; 1024 bytes \ No newline at end of file diff --git a/funcs.asm b/funcs.asm new file mode 100644 index 0000000..caf8f9e --- /dev/null +++ b/funcs.asm @@ -0,0 +1,846 @@ +; Clear the screen and set cursor position to (0, 0) +clear: + push ax + mov ax, 03h + int 10h + mov ax, 200h + xor dx, dx + int 10h + pop ax + ret + +; Detect display. Video mode 0 if no display, +; 1 if monochrome display, 2 if colour display +detect_video: + push ax + + mov ax, 30h + and ax, DETECTED_HARDWARE + + cmp ax, 20h + mov [VIDEO_MODE], 2 + + cmp ax, 30h + mov [VIDEO_MODE], 1 + + pop ax + ret + +; Print terminated string +; +; DS:SI - Pointer on string +print_string: + pusha + mov cx, 1 + mov bh, 0Fh +print_string.print: + lodsb + + cmp al, 0 + je print_string.finish + + call print_symbol + jmp print_string.print +print_string.finish: + popa + ret + +; Split user input on 4 parts by space +split_str_4_space: + push ax + push bx + push cx + push dx + push si + reset_buf cmd.userinput_com, ARGUMENT_BUFFER_SIZE + reset_buf cmd.userinput_arg1, ARGUMENT_BUFFER_SIZE + reset_buf cmd.userinput_arg2, ARGUMENT_BUFFER_SIZE + reset_buf cmd.userinput_arg3, ARGUMENT_BUFFER_SIZE + mov bx, cmd.userinput_com + mov si, cmd.userinput_buf + +split_str_4_space.lp: + mov al, [si] + mov [bx], al + + inc bx + inc si + + cmp byte [si], ' ' + je split_str_4_space.change_part + cmp byte [si], 0 + je split_str_4_space.rtt + + jmp split_str_4_space.lp +split_str_4_space.rtt: + pop si + pop dx + pop cx + pop bx + pop ax + mov byte [split_str_4_space.part], 1 + ret +split_str_4_space.change_part: + cmp byte [split_str_4_space.part], 1 + je split_str_4_space.change_part.part1 + cmp byte [split_str_4_space.part], 2 + je split_str_4_space.change_part.part2 + cmp byte [split_str_4_space.part], 3 + je split_str_4_space.change_part.part3 + cmp byte [split_str_4_space.part], 4 + je split_str_4_space.change_part.part4 + + split_str_4_space.part db 1 + +split_str_4_space.change_part.part1: + inc [split_str_4_space.part] + mov bx, cmd.userinput_arg1 + inc si + jmp split_str_4_space.lp +split_str_4_space.change_part.part2: + inc [split_str_4_space.part] + mov bx, cmd.userinput_arg2 + inc si + jmp split_str_4_space.lp +split_str_4_space.change_part.part3: + inc [split_str_4_space.part] + mov bx, cmd.userinput_arg3 + inc si + jmp split_str_4_space.lp +split_str_4_space.change_part.part4: + jmp split_str_4_space.rtt + +; Print symbol +; +; AL - symbol +print_symbol: + pusha + + xor bx, bx + mov cx, 1 + mov ah, 0Eh + int 10h + + popa + ret + + +; Reset buffer +; DS:SI - buffer +; CX - Length +reset_buffer: + push si + push cx +reset_buffer.lp: + mov byte [ds:si], 0 + inc si + + dec cx + cmp cx, 0 + jne reset_buffer.lp +reset_buffer.nd: + pop cx + pop si + ret + +; Input string +; +; DS:SI - Pointer on buffer +; CX - Buffer length +; --- +; AX - Last pressed key +input_string: + push bx + mov bx, 0 + + input_string.reset_buffer: + push si + push cx + input_string.reset_buffer.lp: + mov byte [ds:si], 0 + inc si + + dec cx + cmp cx, 0 + jne input_string.reset_buffer.lp + input_string.reset_buffer.nd: + pop cx + pop si + +input_string.processing: + xor ah, ah + int 16h + + cmp al, 0Dh + je input_string.ent + + cmp al, 08h + je input_string.backspace + + cmp al, 03h + je input_string.ctrlc + + call print_symbol + + mov [ds:si+bx], al + inc bx + + cmp bx, cx + je input_string.ent + + jmp input_string.processing +input_string.backspace: + cmp bx, 0 + je input_string.processing + + mov ah, 0Eh + int 10h + + mov al, ' ' + int 10h + + mov al, 08h + int 10h + + dec bx + mov byte [ds:si+bx], 0 + + jmp input_string.processing +input_string.ent: + pop bx + xor ah, ah + print ln + ret +input_string.ctrlc: + jmp input_string.ent + +; Compare strings +; +; DS:SI - Pointer on first string +; DS:BX - Pointer on second string +; --- +; Carry flag - 1 if strings are not equal +compare_strings: + push ax + push bx + push si +compare_strings.comp: + lodsb + + cmp [bx], al + jne compare_strings.not_equal + cmp al, 0 + je compare_strings.equal + + inc bx + + jmp compare_strings.comp +compare_strings.equal: + clc + jmp compare_strings.return +compare_strings.not_equal: + stc + jmp compare_strings.return +compare_strings.return: + pop si + pop bx + pop ax + ret + +; Copies string +; +; DS:SI - Pointer on memory from where copy +; DS:BX - Pointer on memory where copy +copy_string: + push si + push bx + push ax +copy_string.lp: + lodsb + + cmp al, 0 + je copy_string.ed + + mov byte [bx], al + + inc bx + + jmp copy_string.lp +copy_string.ed: + pop ax + pop bx + pop si + ret + +; Calculate len of string +; +; DS:SI - Pointer on string +; --- +; AX - Len of string +calculate_string_len: + push si + xor ax, ax + push ax +calculate_string_len.lp: + lodsb + cmp al, 0 + je calculate_string_len.ed + + pop ax + inc ax + push ax + + jmp calculate_string_len.lp +calculate_string_len.ed: + pop ax + pop si + ret + +; ------------------------------------------------------------------ +; os_print_digit -- Displays contents of AX as a single digit +; Works up to base 37, ie digits 0-Z +; IN: AX = "digit" to format and print + +os_print_digit: + pusha + + cmp ax, 9 ; There is a break in ASCII table between 9 and A + jle .digit_format + + add ax, 'A'-'9'-1 ; Correct for the skipped punctuation + +.digit_format: + add ax, "0" ; 0 will display as '0', etc. + + mov ah, 0Eh ; May modify other registers + int 10h + + popa + ret + + +; ------------------------------------------------------------------ +; os_print_1hex -- Displays low nibble of AL in hex format +; IN: AL = number to format and print + +os_print_1hex: + pusha + + and ax, 0Fh ; Mask off data to display + call os_print_digit + + popa + ret + +; Print AL in hex +; +; AL - Number to print +os_print_2hex: + pusha + + push ax ; Output high nibble + shr ax, 4 + call os_print_1hex + + pop ax ; Output low nibble + call os_print_1hex + + popa + ret + + +; Print AX in hex +; +; AX - Number to print +print_hex: + pusha + + push ax ; Output high byte + mov al, ah + call os_print_2hex + + pop ax ; Output low byte + call os_print_2hex + + popa + ret + +; Print values of registers to screen +; +; AX, BX, CX, DX, SI, DI, ES, DS, SS, FLAGS - Registers to print +print_registers: + pushf + push ss + push ds + push es + push di + push si + push dx + push cx + push bx + + print print_registers.axd + call print_hex + print print_registers.h + + pop ax + print print_registers.bxd + call print_hex + print print_registers.h, ln + + pop ax + print print_registers.cxd + call print_hex + print print_registers.h + + pop ax + print print_registers.dxd + call print_hex + print print_registers.h, ln + + pop ax + print print_registers.sid + call print_hex + print print_registers.h + + pop ax + print print_registers.did + call print_hex + print print_registers.h, ln + + pop ax + print print_registers.esd + call print_hex + print print_registers.h + + pop ax + print print_registers.dsd + call print_hex + print print_registers.h, ln + + pop ax + print print_registers.ssd + call print_hex + print print_registers.h + + pop ax + print print_registers.fd + call print_hex + print print_registers.h, ln + + ret + +; ------------------------------------------------------------------ +; os_string_to_int -- Convert decimal string to integer value +; IN: SI = string location (max 5 chars, up to '65536') +; OUT: AX = number +string_to_int: + pusha + + mov ax, si ; First, get length of string + call os_string_length + + add si, ax ; Work from rightmost char in string + dec si + + mov cx, ax ; Use string length as counter + + mov bx, 0 ; BX will be the final number + mov ax, 0 + + + ; As we move left in the string, each char is a bigger multiple. The + ; right-most character is a multiple of 1, then next (a char to the + ; left) a multiple of 10, then 100, then 1,000, and the final (and + ; leftmost char) in a five-char number would be a multiple of 10,000 + + mov word [.multiplier], 1 ; Start with multiples of 1 + +.loop: + mov ax, 0 + mov byte al, [si] ; Get character + sub al, 48 ; Convert from ASCII to real number + + mul word [.multiplier] ; Multiply by our multiplier + + add bx, ax ; Add it to BX + + push ax ; Multiply our multiplier by 10 for next char + mov word ax, [.multiplier] + mov dx, 10 + mul dx + mov word [.multiplier], ax + pop ax + + dec cx ; Any more chars? + cmp cx, 0 + je .finish + dec si ; Move back a char in the string + jmp .loop + +.finish: + mov word [.tmp], bx + popa + mov word ax, [.tmp] + + ret + + + .multiplier dw 0 + .tmp dw 0 + + os_string_length: + pusha + + mov bx, ax ; Move location of string to BX + + mov cx, 0 ; Counter + + .more: + cmp byte [bx], 0 ; Zero (end of string) yet? + je .done + inc bx ; If not, keep adding + inc cx + jmp .more + + + .done: + mov word [.tmp_counter], cx ; Store count before restoring other registers + popa + + mov ax, [.tmp_counter] ; Put count back into AX before returning + ret + + .tmp_counter dw 0 + +; Convert unsigned int to string +; +; AX - Num to convert +; SI - Where save string +; --- +; SI - Pointer to string with converted num +int_to_string: + push cx + push bx + push di + push ax + + mov cx, 0 + mov bx, 10 ; Set BX 10, for division and mod + mov di, si ; Get our pointer ready + +int_to_string.push: + mov dx, 0 + div bx ; Remainder in DX, quotient in AX + inc cx ; Increase pop loop counter + push dx ; Push remainder, so as to reverse order when popping + test ax, ax ; Is quotient zero? + jnz int_to_string.push ; If not, loop again +int_to_string.pop: + pop dx ; Pop off values in reverse order, and add 48 to make them digits + add dl, '0' ; And save them in the string, increasing the pointer each time + mov [di], dl + inc di + dec cx + jnz int_to_string.pop + + mov byte [di], 0 ; Zero-terminate string + + mov si, di + pop ax + pop di + pop bx + pop cx + ret + +; Find file on disk from filename +; BX - filename +; --- +; SI - filename position in table +; CF - 1 if file not exist +find_file: + push bx + push ax + push cx + mov cx, TABLE_SIZE + mov si, filetable + add si, 4 + mov [find_file.fn_offset], bx +find_file.lp: + + mov bx, [find_file.fn_offset] + call compare_strings + jnc find_file.found + + dec cx + cmp cx, 0 + je find_fileret + + mov bx, 12 + ; len si + xor ax, ax + sub bx, ax + xchg ax, bx + add si, ax + add si, 4 + ; mov al, [si] + ; cmp al, 0 + je find_fileret + jmp find_file.lp +find_file.found: + sub si, 4 + pop cx + pop ax + pop bx + clc + ret +find_fileret: + xor si, si + pop cx + pop ax + pop bx + stc + ret + + find_file.fn_offset dw 0 + +; Remove file +; BX - filename +; --- +; CF - 1 if file not exist +remove_file: + push ax + push si + push bx + + call find_file + jc remove_file.rtt + + mov dword [si], 0 + add si, 2 + mov dword [si], 0 + add si, 2 + mov dword [si], 0 + add si, 2 + mov dword [si], 0 + add si, 2 + mov dword [si], 0 + add si, 2 + mov dword [si], 0 + add si, 2 + mov dword [si], 0 + add si, 2 + mov dword [si], 0 + + pop bx + pop si + pop ax + ret +remove_file.rtt: + pop bx + pop si + pop ax + ret + +; Append file to FS table +; AL - Cylinder +; AH - Head +; BL - sector +; BH - Size +; SI - Pointer on name (Max 11 chars) +append_file: + pusha + mov [append_file.cylinder], al + mov [append_file.head], ah + mov [append_file.sector], bl + mov [append_file.size], bh + mov word [append_file.name_os], si + mov cx, TABLE_SIZE + mov si, filetable+4 +append_file.lp: + cmp byte [si], 0 + je append_file.write + add si, 16 + dec cx + cmp cx, 0 + je append_file.cannot + jmp append_file.lp +append_file.nd: + popa + ret +append_file.write: + sub si, 4 + mov al, [append_file.cylinder] + mov ah, [append_file.head] + mov bl, [append_file.sector] + mov bh, [append_file.size] + mov byte [si], al + mov byte [si+1], ah + mov byte [si+2], bl + mov byte [si+3], bh + add si, 4 + mov bx, si + mov si, word [append_file.name_os] + call copy_string + clc + call write_table + jmp append_file.nd +append_file.cannot: + stc + jmp append_file.nd + + append_file.cylinder db 0 + append_file.head db 0 + append_file.sector db 0 + append_file.size db 0 + append_file.name_os dw 0 + +; Rename file in FS table +; SI - Filename +; BX - To what filename change +rename_file: + pusha + mov word [rename_file.fst], si + mov word [rename_file.snd], bx + mov bx, si + call find_file + jc rename_file.notfound + add si, 4 + push si + reset_buf si, 12 + pop si + mov bx, si + mov si, word [rename_file.snd] + call copy_string + rename_file.notfound: + popa + ret + + rename_file.fst dw 0 + rename_file.snd dw 0 + +; Read file and load to memory +; SI - File in FS table +; BX - Where load +load_file: + pusha + mov ah, 02h + mov al, [si+3] + mov dl, [BOOT_DRIVE] + mov ch, [si] + mov dh, [si+1] + mov cl, [si+2] + int 13h + popa + ret + + ; loadoffset dw 0 + +; Write file and load to memory +; SI - File in FS table +; CX - Where +save_file: + pusha + popa + ret + +; Read FS table from disk +read_table: + pusha + mov ah, 02h + mov al, 2 + mov dl, [BOOT_DRIVE] + mov ch, 0 + mov dh, 0 + mov cl, 3 + mov bx, filetable + int 13h + popa + ret + +; Write FS table to disk +write_table: + pusha + mov ah, 03h + mov al, 2 + mov dl, [BOOT_DRIVE] + mov ch, 0 + mov dh, 0 + mov cl, 3 + mov bx, filetable + int 13h + popa + ret + + +wait_key: + mov ah, 11h + int 16h + + jnz wait_key.key_pressed + + hlt + jmp wait_key + +wait_key.key_pressed: + mov ah, 10h + int 16h + ret + +; Copy memory from A to B +; SI - A +; BX - B +; CX - Number of bytes to copy +copy_memory: + pusha +copy_memory.lp: + cmp cx, 0 + je copy_memory.nd + lodsb + mov [bx], al + inc bx + dec cx + jmp copy_memory.lp +copy_memory.nd: + popa + ret + +; Get cursor position +; --- +; DL - X +; DH - Y +get_cursor_pos: + push ax + push cx + push bx + mov ah, 03h + xor bx, bx + int 10h + pop bx + pop cx + pop ax + ret + +; Set cursor position +; DL - X +; DH - Y +set_cursor_pos: + push ax + push bx + mov ah, 02h + xor bx, bx + int 10h + pop bx + pop ax + ret + +; Print values of registers and halt cpu +dbg_halt_cpu: + call print_registers + call halt_cpu + +; Shows goodbye message and stops the CPU +halt_cpu: + print ln, goodbye + cli + hlt diff --git a/functable.asm b/functable.asm new file mode 100644 index 0000000..f64aa1e --- /dev/null +++ b/functable.asm @@ -0,0 +1,74 @@ +functable: + + dw clear + dw detect_video + dw print_string + dw print_symbol + dw input_string + dw compare_strings + dw copy_string + dw calculate_string_len + dw os_print_digit + dw os_print_1hex + dw os_print_2hex + dw print_hex + dw print_registers + dw string_to_int + dw int_to_string + dw dbg_halt_cpu + dw halt_cpu + dw its + dw find_file + dw remove_file + dw write_table + dw cmd.userinput_arg1 + dw cmd.userinput_arg2 + dw cmd.userinput_arg3 + dw reset_buffer + dw append_file + dw rename_file + dw load_file + dw save_file + dw wait_key + dw copy_memory + dw get_cursor_pos + dw set_cursor_pos + dw read_table +dw 0 + +times 1024 - ($-$$) db 0 + +; FUNCTABLE EQU 7E00h +; clear EQU word [7E00h] +; detect_video EQU word [7E02h] +; print_string EQU word [7E04h] +; print_symbol EQU word [7E06h] +; input_string EQU word [7E08h] +; compare_strings EQU word [7E0Ah] +; copy_string EQU word [7E0Ch] +; calculate_string_len EQU word [7E0Eh] +; os_print_digit EQU word [7E10h] +; os_print_1hex EQU word [7E12h] +; os_print_2hex EQU word [7E14h] +; print_hex EQU word [7E16h] +; print_registers EQU word [7E18h] +; string_to_int EQU word [7E1Ah] +; int_to_string EQU word [7E1Ch] +; dbg_halt_cpu EQU word [7E1Eh] +; halt_cpu EQU word [7E20h] +; its EQU word [7E22h] +; find_file EQU word [7E24h] +; remove_file EQU word [7E26h] +; write_table EQU word [7E28h] +; arg1 EQU word [7E2Ah] +; arg2 EQU word [7E2Ch] +; arg3 EQU word [7E2Eh] +; reset_buffer EQU word [7E30h] +; append_file EQU word [7E32h] +; rename_file EQU word [7E34h] +; load_file EQU word [7E36h] +; save_file EQU word [7E38h] +; wait_key EQU word [7E3Ah] +; copy_memory EQU word [7E3Ch] +; get_cursor_pos EQU word [7E3Eh] +; set_cursor_pos EQU word [7E40h] \ No newline at end of file diff --git a/help.txt b/help.txt new file mode 100644 index 0000000..d574e5c --- /dev/null +++ b/help.txt @@ -0,0 +1,152 @@ +; Clear the screen and set cursor position to (0, 0) +clear: + +; Detect display. Video mode 0 if no display, +; 1 if monochrome display, 2 if colour display +detect_video: + +; Print terminated string +; +; DS:SI - Pointer on string +print_string: + +; Print symbol +; +; AL - symbol +; print_symbol: + +; Reset buffer +; DS:SI - buffer +; CX - Length +reset_buffer: + +; Input string +; +; DS:SI - Pointer on buffer +; CX - Buffer length +; --- +; AX - Last pressed key +input_string: + +; Compare strings +; +; DS:SI - Pointer on first string +; DS:BX - Pointer on second string +; --- +; Carry flag - 1 if strings are not equal +compare_strings: + +; Copies string +; +; DS:SI - Pointer on memory from where copy +; DS:BX - Pointer on memory where copy +copy_string: + +; Calculate len of string +; +; DS:SI - Pointer on string +; --- +; AX - Len of string +calculate_string_len: + +; ------------------------------------------------------------------ +; os_print_digit -- Displays contents of AX as a single digit +; Works up to base 37, ie digits 0-Z +; IN: AX = "digit" to format and print +os_print_digit: + +; ------------------------------------------------------------------ +; os_print_1hex -- Displays low nibble of AL in hex format +; IN: AL = number to format and print +os_print_1hex: + +; Print AL in hex +; +; AL - Number to print +os_print_2hex: + +; Print AX in hex +; +; AX - Number to print +print_hex: + +; Print values of registers to screen +; +; AX, BX, CX, DX, SI, DI, ES, DS, SS, FLAGS - Registers to print +print_registers: + +; ------------------------------------------------------------------ +; os_string_to_int -- Convert decimal string to integer value +; IN: SI = string location (max 5 chars, up to '65536') +; OUT: AX = number +string_to_int: + +; Convert unsigned int to string +; +; AX - Num to convert +; SI - Where save string +; --- +; SI - Pointer to string with converted num +int_to_string: + +; Find file on disk from filename +; BX - filename +; --- +; SI - filename position in table +; CF - 1 if file not exist +find_file: + +; Remove file +; BX - filename +; --- +; CF - 1 if file not exist +remove_file: + +; Append file to FS table +; AL - Cylinder +; AH - Head +; BL - sector +; BH - Size +; SI - Pointer on name (Max 11 chars) +append_file: + +; Rename file in FS table +; SI - Filename +; BX - To what filename change +rename_file: + +; Read file and load to memory +; SI - File in FS table +; CX - Where load +load_file: + +; Write file and load to memory +; SI - File in FS table +; CX - Where +save_file: + +; Write FS table to disk +write_table: + +; Copy memory from A to B +; SI - A +; BX - B +; CX - Number of bytes to copy +copy_memory: + +; Get cursor position +; --- +; DL - X +; DH - Y +get_cursor_pos: + +; Set cursor position +; DL - X +; DH - Y +set_cursor_pos: + +; Print values of registers and halt cpu +dbg_halt_cpu: + +; Shows goodbye message and stops the CPU +halt_cpu: \ No newline at end of file diff --git a/iso/boot.img b/iso/boot.img new file mode 100644 index 0000000000000000000000000000000000000000..8a9e14b757453fd80eab97d32ea4be120b6da0a9 GIT binary patch literal 1474560 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 literal 0 HcmV?d00001 diff --git a/iso/calc.bin b/iso/calc.bin new file mode 100644 index 0000000000000000000000000000000000000000..86118b4b5272a81c51572996a010167e66f9b8a9 GIT binary patch literal 295 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", 0 + prefix_2 db "Second number>", 0 + prefix_act db "Action>", 0 + inc_act db "Incorrect action!", 0 + ln db 0Dh, 0Ah, 0 + act_add db "+", 0 + act_sub db "-", 0 + act_div db "/", 0 + act_mul db "*", 0 + + innum_buffer: + times INNUM_BUFFER_SIZE db 0 + db 0 diff --git a/programs/dskview.asm b/programs/dskview.asm new file mode 100644 index 0000000..6c512ce --- /dev/null +++ b/programs/dskview.asm @@ -0,0 +1,58 @@ +USE16 +include 'programmacros.asm' +format binary as 'bin' + +FUNCTABLE EQU 7E00h +clear EQU word [7E00h] +detect_video EQU word [7E02h] +print_string EQU word [7E04h] +print_symbol EQU word [7E06h] +input_string EQU word [7E08h] +compare_strings EQU word [7E0Ah] +copy_string EQU word [7E0Ch] +calculate_string_len EQU word [7E0Eh] +os_print_digit EQU word [7E10h] +os_print_1hex EQU word [7E12h] +os_print_2hex EQU word [7E14h] +print_hex EQU word [7E16h] +print_registers EQU word [7E18h] +string_to_int EQU word [7E1Ah] +int_to_string EQU word [7E1Ch] +dbg_halt_cpu EQU word [7E1Eh] +halt_cpu EQU word [7E20h] +its EQU word [7E22h] +find_file EQU word [7E24h] +remove_file EQU word [7E26h] +write_table EQU word [7E28h] +arg1 EQU word [7E2Ah] +arg2 EQU word [7E2Ch] +arg3 EQU word [7E2Eh] +reset_buffer EQU word [7E30h] +append_file EQU word [7E32h] +rename_file EQU word [7E34h] +load_file EQU word [7E36h] +save_file EQU word [7E38h] +wait_key EQU word [7E3Ah] +copy_memory EQU word [7E3Ch] + +org 1000h + +start: +mainloop: + print userinput_prefix + input userinput_buf, 30 + cmp al, 03h + je progret + + cmp_strs userinput_buf, loadsector + je loadsector_com + + jmp mainloop +progret: + ret +loadsector_com: + jmp mainloop + + userinput_prefix db '>', 0 + loadsector db "load", 0 + userinput_buf: times 31 db 0 \ No newline at end of file diff --git a/programs/hw.asm b/programs/hw.asm new file mode 100644 index 0000000..e404a6a --- /dev/null +++ b/programs/hw.asm @@ -0,0 +1,6 @@ +include 'include.asm' + +print hw +ret + +hw db "Hello, world", 0Dh, 0Ah, 0 \ No newline at end of file diff --git a/programs/include.asm b/programs/include.asm new file mode 100644 index 0000000..15b424d --- /dev/null +++ b/programs/include.asm @@ -0,0 +1,52 @@ +USE16 +format binary as 'bin' + +include 'programmacros.asm' + +INNUM_BUFFER_SIZE equ 5 +SHNUM_BUFFER_SIZE equ 3 +ACTION_BUFFER_SIZE equ 3 +RESULT_BUFFER_SIZE equ 7 +USERINPUT_BUFFER_SIZE equ 64 +ARGUMENT_BUFFER_SIZE equ 16 +FILENAME_BUFFER_SIZE equ 11 + +FUNCTABLE EQU 7E00h +clear EQU word [7E00h] +detect_video EQU word [7E02h] +print_string EQU word [7E04h] +print_symbol EQU word [7E06h] +input_string EQU word [7E08h] +compare_strings EQU word [7E0Ah] +copy_string EQU word [7E0Ch] +calculate_string_len EQU word [7E0Eh] +os_print_digit EQU word [7E10h] +os_print_1hex EQU word [7E12h] +os_print_2hex EQU word [7E14h] +print_hex EQU word [7E16h] +print_registers EQU word [7E18h] +string_to_int EQU word [7E1Ah] +int_to_string EQU word [7E1Ch] +dbg_halt_cpu EQU word [7E1Eh] +halt_cpu EQU word [7E20h] +its EQU word [7E22h] +find_file EQU word [7E24h] +remove_file EQU word [7E26h] +write_table EQU word [7E28h] +arg1 EQU word [7E2Ah] +arg2 EQU word [7E2Ch] +arg3 EQU word [7E2Eh] +reset_buffer EQU word [7E30h] +append_file EQU word [7E32h] +rename_file EQU word [7E34h] +load_file EQU word [7E36h] +save_file EQU word [7E38h] +wait_key EQU word [7E3Ah] +copy_memory EQU word [7E3Ch] +get_cursor_pos EQU word [7E3Eh] +set_cursor_pos EQU word [7E40h] +read_table EQU word [7E42h] + +org 1000h + +db 0x7A diff --git a/programs/notepad.asm b/programs/notepad.asm new file mode 100644 index 0000000..7550cf0 --- /dev/null +++ b/programs/notepad.asm @@ -0,0 +1,44 @@ +include 'programmacros.asm' + +FUNCTABLE EQU 7E00h +clear EQU word [7E00h] +detect_video EQU word [7E02h] +print_string EQU word [7E04h] +print_symbol EQU word [7E06h] +input_string EQU word [7E08h] +compare_strings EQU word [7E0Ah] +copy_string EQU word [7E0Ch] +calculate_string_len EQU word [7E0Eh] +os_print_digit EQU word [7E10h] +os_print_1hex EQU word [7E12h] +os_print_2hex EQU word [7E14h] +print_hex EQU word [7E16h] +print_registers EQU word [7E18h] +string_to_int EQU word [7E1Ah] +int_to_string EQU word [7E1Ch] +dbg_halt_cpu EQU word [7E1Eh] +halt_cpu EQU word [7E20h] +its EQU word [7E22h] +find_file EQU word [7E24h] +remove_file EQU word [7E26h] +write_table EQU word [7E28h] +arg1 EQU word [7E2Ah] +arg2 EQU word [7E2Ch] +arg3 EQU word [7E2Eh] +reset_buffer EQU word [7E30h] +append_file EQU word [7E32h] +rename_file EQU word [7E34h] + +org 1000h + +start: + print greetings, ln + ret + +2d_input + + + greetings db "Welcome to the notepad!", 0 + zx db "0x", 0 + ln db 0Dh, 0Ah, 0 + space db " ", 0 \ No newline at end of file diff --git a/programs/programmacros.asm b/programs/programmacros.asm new file mode 100644 index 0000000..2d6d859 --- /dev/null +++ b/programs/programmacros.asm @@ -0,0 +1,71 @@ +macro print [pointer] +{ + mov si, pointer + call print_string +} + +macro printh [num] +{ + mov ax, num + call print_hex +} + +macro printi [num] +{ + mov ax, num + call its +} + +macro cmp_strs ptr1, ptr2 +{ + mov si, ptr1 + mov bx, ptr2 + call compare_strings +} + +macro cp_str ptr1, ptr2 +{ + mov si, ptr1 + mov bx, ptr2 + call copy_string +} + +macro input pointer, length +{ + mov si, pointer + mov cx, length + call input_string +} + +macro len pointer +{ + mov si, pointer + call calculate_string_len +} + +macro muln num1, num2 +{ + mov ax, num1 + mov bx, num2 + mul bx +} + +macro str_to_i pointer +{ + mov si, pointer + call string_to_int +} + +macro i_to_str pointer, number +{ + mov si, pointer + mov ax, number + call int_to_string +} + +macro reset_buf pointer, length +{ + mov si, pointer + mov cx, length + call reset_buffer +} diff --git a/programs/snake.asm b/programs/snake.asm new file mode 100644 index 0000000..a3a5a58 --- /dev/null +++ b/programs/snake.asm @@ -0,0 +1,274 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; ;;;;;; ;;;;;; ;; +;; ;; ;; ;; ;; +;; ;; ;; ;;;;;; ;;; ;; ;; ;; ;; ;;; ;; ;; ;;;;;; ;; +;; ;;;;;; ;; ;; ;; ;; ;;;;;;; ;;; ;; ;; ;; ;; ;; ;; ;; +;; ;; ;; ;;;;; ;; ;; ;; ;; ;; ; ;; ;; ;; ;;;; ;;;;;; ;; +;; ;; ;; ;; ;;;;;;; ;; ;; ;; ;;; ;;;;;;; ;; ;; ;; ;; +;; ;; ;; ;;;;;; ;; ;; ;;;;;; ;;;;;; ;; ;; ;; ;; ;; ;; ;;;;;; ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Snake like game writen in x86 real mode assembly. ;; +;; Copyright (C) 2014 Piotr Majkrzak ;; +;; ;; +;; This program is free software: you can redistribute it and/or modify ;; +;; it under the terms of the GNU General Public License as published by ;; +;; the Free Software Foundation, either version 3 of the License, or ;; +;; (at your option) any later version. ;; +;; ;; +;; This program is distributed in the hope that it will be useful, ;; +;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; +;; GNU General Public License for more details. ;; +;; ;; +;; You should have received a copy of the GNU General Public License ;; +;; along with this program. If not, see . ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; ;; +;; About ;; +;;------- ;; +;; ;; +;; This game is written in Intel's x86 real mode assembly. It is designed ;; +;; to fit in the boot sector, so it must have at most 510 bytes. Therefore, ;; +;; it has very limited functionalities and spaghetti code. You are able to ;; +;; change snake's head direction, eat fruits and restart the game. ;; +;; ;; +;; ;; +;; Assembly ;; +;;---------- ;; +;; ;; +;; To build this code you need to have NASM installed, you can get it from ;; +;; this website: (http://www.nasm.us/). ;; +;; Execute following command: ;; +;; ;; +;; nasm -fbin snake.asm -o snake.bin ;; +;; ;; +;; ;; +;; Launch ;; +;;-------- ;; +;; ;; +;; Now you have `snake.bin` file, which can be installed or loaded into ;; +;; virtual machine like QEMU, which you can get from (www.qemu.org/). ;; +;; ;; +;; qemu-system-i386 snake.bin ;; +;; ;; +;; ;; +;; Gameplay ;; +;;---------- ;; +;; ;; +;; When the game will be launched, you will see huge green rectangle in the ;; +;; middle of the screen. Snake is hidden now in top left corner. To move it ;; +;; you should set its head direction, using arrow keys. If you are stuck or ;; +;; you just want to restart the game, you should press space bar. ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +[org 0x1000] + +%DEFINE EMPTY 0b0000_0000 +%DEFINE SNAKE 0b0000_0001 +%DEFINE FRUIT 0b0000_0010 +%DEFINE EATEN 0b0000_0100 +%DEFINE WRECK 0b0000_1000 +%DEFINE DIRUP 0b0001_0000 +%DEFINE DIRDO 0b0010_0000 +%DEFINE DIRLE 0b0100_0000 +%DEFINE DIRRI 0b1000_0000 +%define map(i) byte [es:i] +%define head word [es:1024] +%define tail word [es:1026] +%define fpos word [es:1028] +%define ftim word [es:1030] +%define rand word [es:1032] + +db 0x7A + +init: + .random: + mov ah, 0 + int 0x1A + mov rand, dx + .display: + mov ah, 0x00 + mov al, 0x13 + int 0x10 + .interrupt: + mov [fs:0x08*4], word timer + mov [fs:0x08*4+2], ds + mov [fs:0x09*4], word keyboard + mov [fs:0x09*4+2], ds + + +main: + hlt + jmp main + + +;; () -> (ax); ax,cx,dx +random: + mov ax, rand + mov dx, 7993 + mov cx, 9781 + mul dx + add ax, cx + mov rand, ax + ret + +;; (si) -> (di,ah:al) ; cx,dx +movement: + mov cl, map(si) + mov ax, si + mov dl, 32 + div dl + test cl, DIRUP + jz $+4 + dec al + test cl, DIRDO + jz $+4 + inc al + test cl, DIRLE + jz $+4 + dec ah + test cl, DIRRI + jz $+4 + inc ah + and al, 31 + and ah, 31 + movzx di, al + rol di, 5 + movzx cx, ah + add di,cx + ret + + +keyboard: + in al, 0x60 + mov bx, head + mov ah, map(bx) + cmp al, 0x39 + jne $+12 + mov cx, 1032 + mov al, 0 + mov di, 0 + rep stosb + and ah, 0x0F + cmp al, 0x48 + jne $+5 + or ah, DIRUP + cmp al, 0x50 + jne $+5 + or ah, DIRDO + cmp al, 0x4b + jne $+5 + or ah, DIRLE + cmp al, 0x4d + jne $+5 + or ah, DIRRI + test ah, 0xF0 + jz $+4 + mov map(bx), ah + mov al, 0x61 + out 0x20, al + iret + +timer: + .tick_rtc: + int 0x70 + .move_head: + mov si, head + call movement + mov ah, map(di) + mov al, map(si) + test al, WRECK + jz $+3 + iret + test ah, SNAKE|EATEN + jz $+7 + mov map(si), WRECK + iret + test ah, FRUIT + jz $+20 + mov ftim, 0 + mov fpos, -1 + mov bl, EATEN + jmp $+4 + mov bl, SNAKE + and al, 0xF0 + or bl, al + mov map(di), bl + mov head, di + .move_tail: + mov si, tail + call movement + mov al, map(si) + test al, SNAKE + jz $+11 + mov map(si), EMPTY + mov tail, di + jnz $+9 + and al, 0xF0 + or al, SNAKE + mov map(si), al + .move_fruit: + cmp ftim, 0 + jne $+42 + mov bx, fpos + mov map(bx), EMPTY + call random + mov bx, ax + and bx, 1023 + cmp map(bx), EMPTY + jne $-13 + mov map(bx), FRUIT + mov fpos, bx + mov ftim, 64 + dec ftim + .redraw: + mov cx, 0 + mov ax, cx + mov dl, 32 + div dl + mov bx, ax + movzx ax, bl + add ax, 9 + mov dx, 320 + mul dx + movzx dx, bh + add ax, dx + add ax, 24 + mov dx, 4 + mul dx + mov di, cx + mov dl, map(di) + and dl, 0x0F + cmp dl, EMPTY + jne $+8 + mov ebx, 0x02020202 + cmp dl, SNAKE + jne $+8 + mov ebx, 0x01010101 + cmp dl, FRUIT + jne $+8 + mov ebx, 0x04040404 + cmp dl, EATEN + jne $+8 + mov ebx, 0x05050505 + mov di, ax + mov [gs:di],ebx + add di, 320 + mov [gs:di],ebx + add di, 320 + mov [gs:di],ebx + add di, 320 + mov [gs:di],ebx + inc cx + cmp cx, 1024 + jne .redraw+3 + iret + +times 510-($-$$) db 0 +dw 0AA55h \ No newline at end of file diff --git a/programs/viewer.asm b/programs/viewer.asm new file mode 100644 index 0000000..95c6489 --- /dev/null +++ b/programs/viewer.asm @@ -0,0 +1,72 @@ +include 'include.asm' + +start: + len arg1 + cmp ax, 0 + je progret.usage + mov bx, arg1 + call find_file + jc progret.fnf + mov word [file_FS_offset], si + mov si, file_offset + jmp view_text.prt +progret.usage: + print usage, ln + jmp progret +progret.fnf: + print fnf, ln + jmp progret +progret: + ret + +view_text.prt: + mov word [xpos], 0 + jmp print_symb + view_text.prt.continue: + inc si + jmp view_text.prt +view_text.waiting: + call wait_key + cmp al, 'q' + je progret + jmp view_text.prt + +print_symb: + mov dl, [xpos] + mov dh, [ypos] + cmp dl, 0 + je print_symb.mb_end + print_symb.continue: + mov al, [si] + call print_symbol + pusha + cmp [xpos], 24 + je print_symb.mov_ln + mov al, [xpos] + inc al + mov [xpos], al + print_symb.continue2: + popa + printh word [xpos] + jmp view_text.prt.continue +print_symb.mov_ln: + mov al, [ypos] + inc al + mov [ypos], al + mov byte [xpos], 0 + jmp print_symb.continue2 +print_symb.mb_end: + cmp dh, 25 + je view_text.waiting + jmp print_symb.continue + + usage db "Usage: viewer.bin ", 0 + fnf db "File not found.", 0 + ln db 0Dh, 0Ah, 0 + file_FS_offset dw 0 + xpos db 0 + ypos db 0 + +times 1024-($-$$) db 0 +file_offset: +times 2000 db "A" \ No newline at end of file diff --git a/run.sh b/run.sh new file mode 100644 index 0000000..3ea00fb --- /dev/null +++ b/run.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +rm iso/os.bin + +echo +echo +./fasm bootloader.asm iso/os.bin +echo +./fasm programs/calculator.asm iso/calc.bin +echo +# ./fasm programs/notepad.asm iso/notepad.bin +# echo +./fasm programs/viewer.asm iso/viewer.bin +echo +nasm -fbin programs/snake.asm -o iso/snake.bin +echo +echo + +cd iso + +rm boot.img +dd if=/dev/zero of=boot.img bs=1024 count=1440 +dd if=os.bin of=boot.img conv=notrunc +dd if=calc.bin of=boot.img conv=notrunc bs=512 seek=16 +# dd if=notepad.bin of=boot.img conv=notrunc bs=512 seek=17 +dd if=viewer.bin of=boot.img conv=notrunc bs=512 seek=17 +dd if=calc.bin of=boot.img bs=512 seek=19 conv=notrunc +dd if=snake.bin of=boot.img bs=512 seek=20 conv=notrunc +# dd if=just_text.t of=boot.img conv=notrunc bs=512 seek=23 + + +cd .. + +# qemu-system-i386 iso/os.bin +qemu-system-i386 -fda iso/boot.img diff --git a/secstage.asm b/secstage.asm new file mode 100644 index 0000000..3a2f65e --- /dev/null +++ b/secstage.asm @@ -0,0 +1,36 @@ +secstage: + mov ah, 02h + mov al, [filetable_ff+3] + mov dl, [BOOT_DRIVE] + mov ch, [filetable_ff] + mov dh, [filetable_ff+1] + mov cl, [filetable_ff+2] + mov bx, main + int 13h + + mov al, [BOOT_DRIVE] + jc .error + + call main + + call dbg_halt_cpu +.error: + cmp cl, 0x0 + je .notfound + cmp cl, 0x20 + je .notfound + jmp booterror + +.notfound: + mov ax, 1301h + mov bx, 7 + xor dx, dx + mov bp, .notfoundmsg + mov cx, 22 + int 10h + cli + hlt + + .notfoundmsg db '"kernel.bin" not found' + +times 2560-($-$$) db 0