├── .gitignore ├── LICENSE ├── Makefile ├── README ├── counter.asm ├── counter.img ├── counter.lst ├── e.bat ├── os.asm ├── os.img ├── os.lst ├── osall.img └── patch ├── mine.asm ├── mine.img ├── snake.asm ├── snake.img ├── sokoban.asm └── sokoban.fdd /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | nasm 3 | 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019 Oscar Toledo G. http://nanochess.org/ 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 2. Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 17 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Makefile contributed by jtsiomb 2 | 3 | src = os.asm 4 | 5 | .PHONY: all 6 | all: os.img 7 | 8 | os.img: $(src) 9 | nasm -f bin -l os.lst -o $@ $(src) 10 | 11 | .PHONY: clean 12 | clean: 13 | $(RM) os.img 14 | 15 | .PHONY: runqemu 16 | runqemu: os.img 17 | qemu-system-i386 -fda os.img 18 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | _ _ ____ _____ 2 | | | | | / __ \ / ____| 3 | | |__ ___ ___ | |_| | | | (___ 4 | | '_ \ / _ \ / _ \| __| | | |\___ \ 5 | | |_) | (_) | (_) | |_| |__| |____) | 6 | |_.__/ \___/ \___/ \__|\____/|_____/ 7 | 8 | bootOS operating system in 512 bytes (boot sector) 9 | by Oscar Toledo G. Jul/22/2019 10 | 11 | http://nanochess.org 12 | https://github.com/nanochess/bootOS 13 | 14 | It's compatible with 8088 (the original IBM PC). 15 | 16 | If you want to assemble it, you must download the Netwide Assembler 17 | (nasm) from www.nasm.us 18 | 19 | Use this command line: 20 | 21 | nasm -f bin os.asm -l os.lst -o os.img 22 | 23 | 24 | What is bootOS: 25 | 26 | bootOS is a monolithic operating system that fits in 27 | one boot sector. It's able to load, execute, and save 28 | programs. Also keeps a filesystem. It can work with 29 | any floppy disk size starting at 180K. 30 | 31 | It relocates itself at 0000:7a00 and requires further 32 | 768 bytes of memory starting at 0000:7700. 33 | 34 | This operating system runs programs as boot sectors 35 | at 0000:7c00. 36 | 37 | It provides the following services: 38 | 39 | int 0x20 Exit to operating system. 40 | int 0x21 Input key and show in screen. 41 | Entry: none 42 | Output: AL = ASCII key pressed. 43 | Affects: AH/BX/BP. 44 | int 0x22 Output character to screen. 45 | Entry: AL = Character. 46 | Output: none. 47 | Affects: AH/BX/BP. 48 | int 0x23 Load file. 49 | Entry: DS:BX = Filename terminated with zero. 50 | ES:DI = Point to source data (512 bytes) 51 | Output: Carry flag = 0 = Found, 1 = Not found. 52 | Affects: All registers (including ES). 53 | int 0x24 Save file. 54 | Entry: DS:BX = Filename terminated with zero. 55 | ES:DI = Point to data target (512 bytes) 56 | Output: Carry flag = 0 = Successful. 1 = Error. 57 | Affects: All registers (including ES). 58 | int 0x25 Delete file. 59 | Entry: DS:BX = Filename terminated with zero. 60 | Affects: All registers (including ES). 61 | 62 | 63 | Filesystem organization: 64 | 65 | bootOS uses tracks from 0 to 32, side 0, sector 1. 66 | 67 | The directory is contained in track 0, side 0, sector 2. 68 | 69 | Each entry in the directory is 16 bytes wide, and 70 | contains the ASCII name of the file finished with a 71 | zero byte. A sector has a capacity of 512 bytes, it 72 | means only 32 files can be kept on a floppy disk. 73 | 74 | Deleting a file is a matter of zeroing a whole entry. 75 | 76 | Each file is one sector long. Its location in the 77 | disk is derived from its position in the directory. 78 | 79 | The 1st file is located at track 1, side 0, sector 1. 80 | The 2nd file is located at track 2, side 0, sector 1. 81 | The 32nd file is located at track 32, side 0, sector 1. 82 | 83 | 84 | Starting bootOS: 85 | 86 | Just make sure to write it at the boot sector of a 87 | floppy disk. It can work with any floppy disk size 88 | (360K, 720K, 1.2MB and 1.44MB) and it will waste the 89 | disk space as only uses the first two sectors of the 90 | disk and then the first sector of each following 91 | track. 92 | 93 | For emulation make sure to deposit it at the start 94 | of a .img file of 360K, 720K or 1440K. (at least 95 | VirtualBox detects the type of disk by the length 96 | of the image file) 97 | 98 | For Mac OS X and Linux you can create a 360K image 99 | in this way: 100 | 101 | dd if=/dev/zero of=oszero.img count=719 bs=512 102 | cat os.img oszero.img >osbase.img 103 | 104 | Replace 719 with 1439 for 720K, or 2879 for 1.44M. 105 | 106 | Tested with VirtualBox for Mac OS X running Windows XP 107 | running it, it also works with qemu: 108 | 109 | qemu-system-x86_64 -fda os.img 110 | 111 | Running bootOS: 112 | 113 | The first time you should enter the 'format' command, 114 | so it initializes the directory. It also copies itself 115 | again to the boot sector, this is useful to init new 116 | disks. 117 | 118 | bootOS commands: 119 | 120 | ver Shows the version (none at the moment) 121 | dir Shows the directory's content. 122 | del filename Deletes the "filename" file. 123 | format As explained before. 124 | enter Allows to enter up to 512 hexadecimal 125 | bytes to create another file. 126 | 127 | Notice the line size is 128 characters so 128 | you must break the input into chunks of 129 | 4, 8 or 16 bytes. 130 | 131 | It also allows to copy the last executed 132 | program just press Enter when the 'h' prompt 133 | appears and type the new name. 134 | 135 | For example: (Character + is Enter key) 136 | 137 | $enter+ 138 | hbb 17 7c 8a 07 84 c0 74 0c 53 b4 0e bb 0f 00 cd+ 139 | h10 5b 43 eb ee cd 20 48 65 6c 6c 6f 2c 20 77 6f+ 140 | h72 6c 64 0d 0a 00+ 141 | h+ 142 | *hello+ 143 | $dir+ 144 | hello 145 | $hello+ 146 | Hello, world 147 | $ 148 | 149 | bootOS programs: (Oh yes! we have software support) 150 | 151 | cubicDoom https://github.com/nanochess/cubicDoom 152 | bricks https://github.com/nanochess/bricks 153 | fbird https://github.com/nanochess/fbird 154 | Pillman https://github.com/nanochess/pillman 155 | invaders https://github.com/nanochess/invaders 156 | bootBASIC https://github.com/nanochess/bootBASIC 157 | bootRogue https://github.com/nanochess/bootRogue 158 | Atomchess https://github.com/nanochess/Toledo-Atomchess 159 | (requires minimum 286 processor) 160 | heart https://github.com/nanochess/heart 161 | pi https://github.com/nanochess/pi 162 | bootle https://github.com/nanochess/bootle 163 | 164 | Also our first 3rd party programs!!! 165 | 166 | bootSlide https://github.com/XlogicX/BootSlide 167 | (requires minimum 286 processor) 168 | tetranglix https://github.com/XlogicX/tetranglix 169 | (requires minimum 286 processor) 170 | snake https://gitlab.com/pmikkelsen/asm_snake 171 | (requires minimum 286 processor) 172 | bootMine https://github.com/io12/BootMine 173 | (requires minimum Pentium II processor because rdtsc instruction) 174 | sokoban https://ish.works/bootsector/bootsector.html 175 | (requires minimum 286 processor) 176 | 177 | These programs provide a boot sector version and a COM 178 | file version. You need the boot sector version as the 179 | programs are loaded at address 0000:7c00. 180 | 181 | You can copy the machine code directly using the 'enter' 182 | command, or you can create a file with signature bytes 183 | with the same command and later copy the binary into the 184 | .img file using the signature bytes as a clue to locate 185 | the right position in the image file. 186 | 187 | Or you can find a pre-designed disk image along this Git 188 | with the name osall.img 189 | 190 | Enjoy it! 191 | 192 | 193 | Special thanks to Maja Kądziołka (meithecatte) for finding 194 | some bugs and suggesting enhancements. 195 | 196 | There's a bootOS fork by jakiki6 capable of running from 197 | an USB stick, and including many other changes. 198 | Download from: https://github.com/jakiki6/bootOS 199 | 200 | 201 | >> ATTENTION << 202 | 203 | Would you like to learn 8086/8088 programming? Then you 204 | must get my new book Programming Boot Sector Games including 205 | a 8086/8088 crash course! 206 | 207 | Now available from Lulu: 208 | 209 | Soft-cover http://www.lulu.com/shop/oscar-toledo-gutierrez/programming-boot-sector-games/paperback/product-24188564.html 210 | 211 | Hard-cover http://www.lulu.com/shop/oscar-toledo-gutierrez/programming-boot-sector-games/hardcover/product-24188530.html 212 | 213 | eBook https://nanochess.org/store.html 214 | 215 | These are some of the example programs documented profusely 216 | in the book: 217 | 218 | * Guess the number. 219 | * Tic-Tac-Toe game. 220 | * Text graphics. 221 | * Mandelbrot set. 222 | * F-Bird game. 223 | * Invaders game. 224 | * Pillman game. 225 | * Toledo Atomchess. 226 | * bootBASIC language. 227 | 228 | After the success of my first book, if you need even 229 | More Boot Sector Games then you must get this book! 230 | 231 | Soft-cover http://www.lulu.com/shop/oscar-toledo-gutierrez/more-boot-sector-games/paperback/product-24462035.html 232 | 233 | Hard-cover http://www.lulu.com/shop/oscar-toledo-gutierrez/more-boot-sector-games/hardcover/product-24462029.html 234 | 235 | * Follow the Lights 236 | * bootRogue 237 | * bricks 238 | * cubicDoom 239 | * bootOS 240 | -------------------------------------------------------------------------------- /counter.asm: -------------------------------------------------------------------------------- 1 | ; 2 | ; Shows how to use bootOS services 3 | ; 4 | ; by Oscar Toledo G. 5 | ; http://nanochess.org/ 6 | ; 7 | ; Creation date: Jul/31/2019. 8 | ; 9 | 10 | org 0x7c00 11 | 12 | ; 13 | ; These segment values and addresses are for 14 | ; testing the correct bootOS behavior. 15 | ; 16 | name_segment: equ 0x1000 17 | name_address: equ 0x0100 18 | 19 | data_segment: equ 0x1100 20 | data_address: equ 0x0200 21 | 22 | start: 23 | mov ax,name_segment 24 | mov es,ax 25 | 26 | mov si,name 27 | mov di,name_address 28 | mov bx,di 29 | mov cx,9 30 | rep movsb 31 | 32 | push es 33 | pop ds ; ds:bx ready pointing to filename 34 | 35 | mov ax,data_segment 36 | mov es,ax 37 | mov di,data_address ; es:di ready pointing to data 38 | 39 | push bx 40 | push ds 41 | push di 42 | push es 43 | int 0x23 ; Load file. 44 | pop ds 45 | pop di 46 | push di 47 | push ds 48 | mov al,'*' ; Exists. 49 | jnc .1 50 | mov al,'?' ; Doesn't exist. 51 | mov word [di],0x0000 ; Setup counter to zero. 52 | .1: 53 | int 0x22 ; Output character. 54 | 55 | mov ax,[di] ; Read data. 56 | 57 | inc al ; Increase right digit. 58 | cmp al,10 ; Is it 10? 59 | jne .2 ; No, jump. 60 | mov al,0 ; Reset to zero. 61 | 62 | inc ah ; Increase left digit. 63 | cmp ah,10 ; Is it 10? 64 | jne .2 ; No, jump. 65 | mov ah,0 ; Reset to zero. 66 | 67 | .2: mov [di],ax ; Save data. 68 | 69 | push ax 70 | mov al,ah 71 | add al,'0' ; Convert to ASCII. 72 | int 0x22 ; Output character. 73 | pop ax 74 | 75 | add al,'0' ; Convert to ASCII. 76 | int 0x22 ; Output character. 77 | 78 | mov al,0x0d ; Go to next row on screen. 79 | int 0x22 ; Output character. 80 | 81 | pop es 82 | pop di 83 | pop ds 84 | pop bx 85 | int 0x24 ; Save file. 86 | 87 | int 0x20 ; Return to bootOS. 88 | 89 | name: db "data.bin",0 ; Filename. 90 | 91 | -------------------------------------------------------------------------------- /counter.img: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nanochess/bootOS/b740e059d46e7072af836189370f0dac48c8f55c/counter.img -------------------------------------------------------------------------------- /counter.lst: -------------------------------------------------------------------------------- 1 | 1 ; 2 | 2 ; Shows how to use bootOS services 3 | 3 ; 4 | 4 ; by Oscar Toledo G. 5 | 5 ; http://nanochess.org/ 6 | 6 ; 7 | 7 ; Creation date: Jul/31/2019. 8 | 8 ; 9 | 9 10 | 10 org 0x7c00 11 | 11 12 | 12 name_segment: equ 0x1000 13 | 13 name_address: equ 0x0100 14 | 14 15 | 15 data_segment: equ 0x1100 16 | 16 data_address: equ 0x0200 17 | 17 18 | 18 start: 19 | 19 00000000 B80010 mov ax,name_segment 20 | 20 00000003 8EC0 mov es,ax 21 | 21 22 | 22 00000005 BE[5F00] mov si,name 23 | 23 00000008 BF0001 mov di,name_address 24 | 24 0000000B 89FB mov bx,di 25 | 25 0000000D B90900 mov cx,9 26 | 26 00000010 F3A4 rep movsb 27 | 27 28 | 28 00000012 06 push es 29 | 29 00000013 1F pop ds 30 | 30 00000014 B80011 mov ax,data_segment 31 | 31 00000017 8EC0 mov es,ax 32 | 32 00000019 BF0002 mov di,data_address 33 | 33 34 | 34 0000001C 53 push bx 35 | 35 0000001D 1E push ds 36 | 36 0000001E 57 push di 37 | 37 0000001F 06 push es 38 | 38 00000020 CD23 int 0x23 ; Load file. 39 | 39 00000022 1F pop ds 40 | 40 00000023 5F pop di 41 | 41 00000024 57 push di 42 | 42 00000025 1E push ds 43 | 43 00000026 B02A mov al,'*' ; Exists. 44 | 44 00000028 7306 jnc .1 45 | 45 0000002A B03F mov al,'?' ; Doesn't exist. 46 | 46 0000002C C7050000 mov word [di],0x0000 ; Setup counter to zero. 47 | 47 .1: 48 | 48 00000030 CD22 int 0x22 ; Output character 49 | 49 50 | 50 00000032 8B05 mov ax,[di] ; Read data 51 | 51 52 | 52 00000034 FEC0 inc al ; Increase right digit. 53 | 53 00000036 3C0A cmp al,10 ; Is it 10? 54 | 54 00000038 750B jne .2 ; No, jump. 55 | 55 0000003A B000 mov al,0 ; Reset to zero. 56 | 56 57 | 57 0000003C FEC4 inc ah ; Increase left digit. 58 | 58 0000003E 80FC0A cmp ah,10 ; Is it 10? 59 | 59 00000041 7502 jne .2 ; No, jump. 60 | 60 00000043 B400 mov ah,0 ; Reset to zero. 61 | 61 62 | 62 00000045 8905 .2: mov [di],ax ; Save data 63 | 63 64 | 64 00000047 50 push ax 65 | 65 00000048 88E0 mov al,ah 66 | 66 0000004A 0430 add al,'0' ; Convert to ASCII 67 | 67 0000004C CD22 int 0x22 68 | 68 0000004E 58 pop ax 69 | 69 70 | 70 0000004F 0430 add al,'0' ; Convert to ASCII 71 | 71 00000051 CD22 int 0x22 72 | 72 73 | 73 00000053 B00D mov al,0x0d ; Go to next row on screen 74 | 74 00000055 CD22 int 0x22 75 | 75 76 | 76 00000057 07 pop es 77 | 77 00000058 5F pop di 78 | 78 00000059 1F pop ds 79 | 79 0000005A 5B pop bx 80 | 80 0000005B CD24 int 0x24 ; Save file 81 | 81 82 | 82 0000005D CD20 int 0x20 ; Return to bootOS 83 | 83 84 | 84 0000005F 646174612E62696E00 name: db "data.bin",0 85 | 85 86 | -------------------------------------------------------------------------------- /e.bat: -------------------------------------------------------------------------------- 1 | nasm -f bin basic.asm -Dcom_file=1 -o basic.com 2 | nasm -f bin basic.asm -l basic.lst -o basic.img 3 | rem basic 4 | 5 | -------------------------------------------------------------------------------- /os.asm: -------------------------------------------------------------------------------- 1 | ; 2 | ; bootOS, an operating system in 512 bytes 3 | ; 4 | ; by Oscar Toledo G. 5 | ; http://nanochess.org/ 6 | ; 7 | ; Creation date: Jul/21/2019. 6pm 10pm 8 | ; Revision date: Jul/22/2019. Optimization, corrections and comments. 9 | ; Revision date: Jul/31/2019. Added a service table and allows 10 | ; filenames/sources/targets from any segment. 11 | ; 'del' command now shows errors. 12 | ; 13 | 14 | cpu 8086 15 | 16 | ; 17 | ; What is bootOS: 18 | ; 19 | ; bootOS is a monolithic operating system that fits in 20 | ; one boot sector. It's able to load, execute, and save 21 | ; programs. Also keeps a filesystem. It can work with 22 | ; any floppy disk size starting at 180K. 23 | ; 24 | ; It relocates itself at 0000:7a00 and requires further 25 | ; 768 bytes of memory starting at 0000:7700. 26 | ; 27 | ; This operating system runs programs as boot sectors 28 | ; at 0000:7c00. 29 | ; 30 | ; It provides the following services: 31 | ; int 0x20 Exit to operating system. 32 | ; int 0x21 Input key and show in screen. 33 | ; Entry: none 34 | ; Output: AL = ASCII key pressed. 35 | ; Affects: AH/BX/BP. 36 | ; int 0x22 Output character to screen. 37 | ; Entry: AL = Character. 38 | ; Output: none. 39 | ; Affects: AH/BX/BP. 40 | ; int 0x23 Load file. 41 | ; Entry: DS:BX = Filename terminated with zero. 42 | ; ES:DI = Point to source data (512 bytes) 43 | ; Output: Carry flag = 0 = Found, 1 = Not found. 44 | ; Affects: All registers (including ES). 45 | ; int 0x24 Save file. 46 | ; Entry: DS:BX = Filename terminated with zero. 47 | ; ES:DI = Point to data target (512 bytes) 48 | ; Output: Carry flag = 0 = Successful. 1 = Error. 49 | ; Affects: All registers (including ES). 50 | ; int 0x25 Delete file. 51 | ; Entry: DS:BX = Filename terminated with zero. 52 | ; Affects: All registers (including ES). 53 | ; 54 | ; 55 | ; Filesystem organization: 56 | ; 57 | ; bootOS uses tracks from 0 to 32, side 0, sector 1. 58 | ; 59 | ; The directory is contained in track 0, side 0, sector 2. 60 | ; 61 | ; Each entry in the directory is 16 bytes wide, and 62 | ; contains the ASCII name of the file finished with a 63 | ; zero byte. A sector has a capacity of 512 bytes, it 64 | ; means only 32 files can be kept on a floppy disk. 65 | ; 66 | ; Deleting a file is a matter of zeroing a whole entry. 67 | ; 68 | ; Each file is one sector long. Its location in the 69 | ; disk is derived from its position in the directory. 70 | ; 71 | ; The 1st file is located at track 1, side 0, sector 1. 72 | ; The 2nd file is located at track 2, side 0, sector 1. 73 | ; The 32nd file is located at track 32, side 0, sector 1. 74 | ; 75 | ; 76 | ; Starting bootOS: 77 | ; 78 | ; Just make sure to write it at the boot sector of a 79 | ; floppy disk. It can work with any floppy disk size 80 | ; (360K, 720K, 1.2MB and 1.44MB) and it will waste the 81 | ; disk space as only uses the first two sectors of the 82 | ; disk and then the first sector of each following 83 | ; track. 84 | ; 85 | ; For emulation make sure to deposit it at the start 86 | ; of a .img file of 360K, 720K or 1440K. (at least 87 | ; VirtualBox detects the type of disk by the length 88 | ; of the image file) 89 | ; 90 | ; For Mac OS X and Linux you can create a 360K image 91 | ; in this way: 92 | ; 93 | ; dd if=/dev/zero of=oszero.img count=719 bs=512 94 | ; cat os.img oszero.img >osbase.img 95 | ; 96 | ; Replace 719 with 1439 for 720K, or 2879 for 1.44M. 97 | ; 98 | ; Tested with VirtualBox for Mac OS X running Windows XP 99 | ; running it, it also works with qemu: 100 | ; 101 | ; qemu-system-x86_64 -fda os.img 102 | ; 103 | ; Running bootOS: 104 | ; 105 | ; The first time you should enter the 'format' command, 106 | ; so it initializes the directory. It also copies itself 107 | ; again to the boot sector, this is useful to init new 108 | ; disks. 109 | ; 110 | ; bootOS commands: 111 | ; 112 | ; ver Shows the version (none at the moment) 113 | ; dir Shows the directory's content. 114 | ; del filename Deletes the "filename" file. 115 | ; format As explained before. 116 | ; enter Allows to enter up to 512 hexadecimal 117 | ; bytes to create another file. 118 | ; 119 | ; Notice the line size is 128 characters so 120 | ; you must break the input into chunks of 121 | ; 4, 8 or 16 bytes. 122 | ; 123 | ; It also allows to copy the last executed 124 | ; program just press Enter when the 'h' prompt 125 | ; appears and type the new name. 126 | ; 127 | ; For example: (Character + is Enter key) 128 | ; 129 | ; $enter+ 130 | ; hbb 17 7c 8a 07 84 c0 74 0c 53 b4 0e bb 0f 00 cd+ 131 | ; h10 5b 43 eb ee cd 20 48 65 6c 6c 6f 2c 20 77 6f+ 132 | ; h72 6c 64 0d 0a 00+ 133 | ; h+ 134 | ; *hello+ 135 | ; $dir+ 136 | ; hello 137 | ; $hello+ 138 | ; Hello, world 139 | ; $ 140 | ; 141 | ; bootOS programs: (Oh yes! we have software support) 142 | ; 143 | ; fbird https://github.com/nanochess/fbird 144 | ; Pillman https://github.com/nanochess/pillman 145 | ; invaders https://github.com/nanochess/invaders 146 | ; bootBASIC https://github.com/nanochess/bootBASIC 147 | ; 148 | ; You can copy the machine code directly using the 'enter' 149 | ; command, or you can create a file with signature bytes 150 | ; with the same command and later copy the binary into the 151 | ; .img file using the signature bytes as a clue to locate 152 | ; the right position in the image file. 153 | ; 154 | ; Or you can find a pre-designed disk image along this Git 155 | ; with the name osall.img 156 | ; 157 | 158 | stack: equ 0x7700 ; Stack pointer (grows to lower addresses) 159 | line: equ 0x7780 ; Buffer for line input 160 | sector: equ 0x7800 ; Sector data for directory 161 | osbase: equ 0x7a00 ; bootOS location 162 | boot: equ 0x7c00 ; Boot sector location 163 | 164 | entry_size: equ 16 ; Directory entry size 165 | sector_size: equ 512 ; Sector size 166 | max_entries: equ sector_size/entry_size 167 | 168 | ; 169 | ; Cold start of bootOS 170 | ; 171 | ; Notice it is loaded at 0x7c00 (boot) and needs to 172 | ; relocate itself to 0x7a00 (osbase). The instructions 173 | ; between 'start' and 'ver_command' shouldn't depend 174 | ; on the assembly location (osbase) because these 175 | ; are running at boot location (boot). 176 | ; 177 | org osbase 178 | start: 179 | xor ax,ax ; Set all segments to zero 180 | mov ds,ax 181 | mov es,ax 182 | mov ss,ax 183 | mov sp,stack ; Set stack to guarantee data safety 184 | 185 | cld ; Clear D flag. 186 | mov si,boot ; Copy bootOS boot sector... 187 | mov di,osbase ; ...into osbase 188 | mov cx,sector_size 189 | rep movsb 190 | 191 | ; NOTE: we could avoid the need for setting SI here 192 | ; by adjusting the length of the copy above, but that 193 | ; would break the 'format' command. 194 | mov si,int_0x20 ; SI now points to int_0x20 195 | mov di,0x0020*4 ; Address of service for int 0x20 196 | mov cl,6 197 | .load_vec: 198 | movsw ; Copy IP address 199 | stosw ; Copy CS address 200 | loop .load_vec 201 | 202 | ; 203 | ; 'ver' command 204 | ; 205 | ver_command: 206 | mov si,intro 207 | print_then_restart: 208 | call output_string 209 | int int_restart ; Restart bootOS 210 | 211 | ; 212 | ; Warm start of bootOS 213 | ; 214 | restart: 215 | cld ; Clear D flag. 216 | push cs ; Reinit all segment registers 217 | push cs 218 | push cs 219 | pop ds 220 | pop es 221 | pop ss 222 | mov sp,stack ; Restart stack 223 | 224 | mov al,'$' ; Command prompt 225 | call input_line ; Input line 226 | 227 | cmp byte [si],0x00 ; Empty line? 228 | je restart ; Yes, get another line 229 | 230 | mov di,commands ; Point to commands list 231 | 232 | ; Notice that filenames starting with same characters 233 | ; won't be recognized as such (so file dirab cannot be 234 | ; executed). 235 | os11: 236 | mov cl,[di] ; Read length of command in chars 237 | inc di 238 | xor ch, ch ; Also makes sure that ZF = 1 239 | push si ; Save current position 240 | rep cmpsb ; Compare statement 241 | jne os14 ; Equal? No, jump 242 | call word [di] ; Call command process 243 | jmp restart ; Go to expect another command 244 | 245 | os14: add di,cx ; Advance the list pointer 246 | scasw ; Avoid the address 247 | pop si 248 | jmp os11 ; Compare another statement 249 | 250 | exec_from_disk: 251 | pop bx 252 | pop bx 253 | mov di,boot ; Location to read data 254 | int int_load_file ; Load file 255 | jc os7 ; Jump if error 256 | jmp bx 257 | 258 | ; 259 | ; File not found error 260 | ; 261 | os7: 262 | mov si,error_message 263 | jmp print_then_restart 264 | 265 | ; 266 | ; >> COMMAND << 267 | ; del filename 268 | ; 269 | del_command: 270 | os22: 271 | mov bx,si ; Copy SI (buffer pointer) to BX 272 | lodsb 273 | cmp al,0x20 ; Avoid spaces 274 | je os22 275 | int int_delete_file 276 | jc os7 277 | ret 278 | 279 | ; 280 | ; 'dir' command 281 | ; 282 | dir_command: 283 | call read_dir ; Read the directory 284 | mov di,bx 285 | os18: 286 | cmp byte [di],0 ; Empty entry? 287 | je os17 ; Yes, jump 288 | mov si,di ; Point to data 289 | call output_string ; Show name 290 | os17: call next_entry 291 | jne os18 ; No, jump 292 | ret ; Return 293 | 294 | ; 295 | ; Get filename length and prepare for directory lookup 296 | ; Entry: 297 | ; si = pointer to string 298 | ; Output: 299 | ; si = unaffected 300 | ; di = pointer to start of directory 301 | ; cx = length of filename including zero terminator 302 | ; 303 | filename_length: 304 | push si 305 | xor cx,cx ; cx = 0 306 | .loop: 307 | lodsb ; Read character. 308 | inc cx ; Count character. 309 | cmp al,0 ; Is it zero (end character)? 310 | jne .loop ; No, jump. 311 | 312 | pop si 313 | mov di,sector ; Point to start of directory. 314 | ret 315 | 316 | ; 317 | ; >> SERVICE << 318 | ; Load file 319 | ; 320 | ; Entry: 321 | ; ds:bx = Pointer to filename ended with zero byte. 322 | ; es:di = Destination. 323 | ; Output: 324 | ; Carry flag = Set = not found, clear = successful. 325 | ; 326 | load_file: 327 | push di ; Save destination 328 | push es 329 | call find_file ; Find the file (sanitizes ES) 330 | mov ah,0x02 ; Read sector 331 | shared_file: 332 | pop es 333 | pop bx ; Restore destination on BX 334 | jc ret_cf ; Jump if error 335 | call disk ; Do operation with disk 336 | ; Carry guaranteed to be clear. 337 | ret_cf: 338 | mov bp,sp 339 | rcl byte [bp+4],1 ; Insert Carry flag in Flags (automatic usage of SS) 340 | iret 341 | 342 | ; 343 | ; >> SERVICE << 344 | ; Save file 345 | ; 346 | ; Entry: 347 | ; ds:bx = Pointer to filename ended with zero byte. 348 | ; es:di = Source. 349 | ; Output: 350 | ; Carry flag = Set = error, clear = good. 351 | ; 352 | save_file: 353 | push di ; Save origin 354 | push es 355 | push bx ; Save filename pointer 356 | int int_delete_file ; Delete previous file (sanitizes ES) 357 | pop bx ; Restore filename pointer 358 | call filename_length ; Prepare for lookup 359 | 360 | .find: es cmp byte [di],0 ; Found empty directory entry? 361 | je .empty ; Yes, jump and fill it. 362 | call next_entry 363 | jne .find 364 | jmp shared_file 365 | 366 | .empty: push di 367 | rep movsb ; Copy full name into directory 368 | call write_dir ; Save directory 369 | pop di 370 | call get_location ; Get location of file 371 | mov ah,0x03 ; Write sector 372 | jmp shared_file 373 | 374 | ; 375 | ; >> SERVICE << 376 | ; Delete file 377 | ; 378 | ; Entry: 379 | ; ds:bx = Pointer to filename ended with zero byte. 380 | ; Output: 381 | ; Carry flag = Set = not found, clear = deleted. 382 | ; 383 | delete_file: 384 | call find_file ; Find file (sanitizes ES) 385 | jc ret_cf ; If carry set then not found, jump. 386 | mov cx,entry_size 387 | call write_zero_dir ; Fill whole entry with zero. Write directory. 388 | jmp ret_cf 389 | 390 | ; 391 | ; Find file 392 | ; 393 | ; Entry: 394 | ; ds:bx = Pointer to filename ended with zero byte. 395 | ; Output: 396 | ; es:di = Pointer to directory entry 397 | ; Carry flag = Clear if found, set if not found. 398 | find_file: 399 | push bx 400 | call read_dir ; Read directory (sanitizes ES) 401 | pop si 402 | call filename_length ; Get filename length and setup DI 403 | os6: 404 | push si 405 | push di 406 | push cx 407 | repe cmpsb ; Compare name with entry 408 | pop cx 409 | pop di 410 | pop si 411 | je get_location ; Jump if equal. 412 | call next_entry 413 | jne os6 ; No, jump 414 | ret ; Return 415 | 416 | next_entry: 417 | add di,byte entry_size ; Go to next entry. 418 | cmp di,sector+sector_size ; Complete directory? 419 | stc ; Error, not found. 420 | ret 421 | 422 | ; 423 | ; Get location of file on disk 424 | ; 425 | ; Entry: 426 | ; DI = Pointer to entry in directory. 427 | ; 428 | ; Output: 429 | ; CH = Track number in disk. 430 | ; CL = Sector (always 0x01). 431 | ; 432 | ; The position of a file inside the disk depends on its 433 | ; position in the directory. The first entry goes to 434 | ; track 1, the second entry to track 2 and so on. 435 | ; 436 | get_location: 437 | lea ax,[di-(sector-entry_size)] ; Get entry pointer into directory 438 | ; Plus one entry (files start on track 1) 439 | mov cl,4 ; 2^(8-4) = entry_size 440 | shl ax,cl ; Shift left and clear Carry flag 441 | inc ax ; AL = Sector 1 442 | xchg ax,cx ; CH = Track, CL = Sector 443 | ret 444 | 445 | ; 446 | ; >> COMMAND << 447 | ; format 448 | ; 449 | format_command: 450 | mov di,sector ; Fill whole sector to zero 451 | mov cx,sector_size 452 | call write_zero_dir 453 | mov bx,osbase ; Copy bootOS onto first sector 454 | dec cx 455 | jmp short disk 456 | 457 | ; 458 | ; Read the directory from disk 459 | ; 460 | read_dir: 461 | push cs ; bootOS code segment... 462 | pop es ; ...to sanitize ES register 463 | mov ah,0x02 464 | jmp short disk_dir 465 | 466 | write_zero_dir: 467 | mov al,0 468 | rep stosb 469 | 470 | ; 471 | ; Write the directory to disk 472 | ; 473 | write_dir: 474 | mov ah,0x03 475 | disk_dir: 476 | mov bx,sector 477 | mov cx,0x0002 478 | ; 479 | ; Do disk operation. 480 | ; 481 | ; Input: 482 | ; AH = 0x02 read disk, 0x03 write disk 483 | ; ES:BX = data source/target 484 | ; CH = Track number 485 | ; CL = Sector number 486 | ; 487 | disk: 488 | push ax 489 | push bx 490 | push cx 491 | push es 492 | mov al,0x01 ; AL = 1 sector 493 | xor dx,dx ; DH = Drive A. DL = Head 0. 494 | int 0x13 495 | pop es 496 | pop cx 497 | pop bx 498 | pop ax 499 | jc disk ; Retry 500 | ret 501 | 502 | ; 503 | ; Input line from keyboard 504 | ; Entry: 505 | ; al = prompt character 506 | ; Output: 507 | ; buffer 'line' contains line, finished with CR 508 | ; SI points to 'line'. 509 | ; 510 | input_line: 511 | int int_output_char ; Output prompt character 512 | mov si,line ; Setup SI and DI to start of line buffer 513 | mov di,si ; Target for writing line 514 | os1: cmp al,0x08 ; Backspace? 515 | jne os2 516 | dec di ; Undo the backspace write 517 | dec di ; Erase a character 518 | os2: int int_input_key ; Read keyboard 519 | cmp al,0x0d ; CR pressed? 520 | jne os10 521 | mov al,0x00 522 | os10: stosb ; Save key in buffer 523 | jne os1 ; No, wait another key 524 | ret ; Yes, return 525 | 526 | ; 527 | ; Read a key into al 528 | ; Also outputs it to screen 529 | ; 530 | input_key: 531 | mov ah,0x00 532 | int 0x16 533 | ; 534 | ; Screen output of character contained in al 535 | ; Expands 0x0d (CR) into 0x0a 0x0d (LF CR) 536 | ; 537 | output_char: 538 | cmp al,0x0d 539 | jne os3 540 | mov al,0x0a 541 | int int_output_char 542 | mov al,0x0d 543 | os3: 544 | mov ah,0x0e ; Output character to TTY 545 | mov bx,0x0007 ; Gray. Required for graphic modes 546 | int 0x10 ; BIOS int 0x10 = Video 547 | iret 548 | 549 | ; 550 | ; Output string 551 | ; 552 | ; Entry: 553 | ; SI = address 554 | ; 555 | ; Implementation: 556 | ; It assumes that SI never points to a zero length string. 557 | ; 558 | output_string: 559 | lodsb ; Read character 560 | int int_output_char ; Output to screen 561 | cmp al,0x00 ; Is it 0x00 (terminator)? 562 | jne output_string ; No, the loop continues 563 | mov al,0x0d 564 | int int_output_char 565 | ret 566 | 567 | ; 568 | ; 'enter' command 569 | ; 570 | enter_command: 571 | mov di,boot ; Point to boot sector 572 | os23: push di 573 | mov al,'h' ; Prompt character 574 | call input_line ; Input line 575 | pop di 576 | cmp byte [si],0 ; Empty line? 577 | je os20 ; Yes, jump 578 | os19: call xdigit ; Get a hexadecimal digit 579 | jnc os23 580 | mov cl,4 581 | shl al,cl 582 | xchg ax,cx 583 | call xdigit ; Get a hexadecimal digit 584 | or al,cl 585 | stosb ; Write one byte 586 | jmp os19 ; Repeat loop to complete line 587 | os20: 588 | mov al,'*' ; Prompt character 589 | call input_line ; Input line with filename 590 | push si 591 | pop bx 592 | mov di,boot ; Point to data entered 593 | int int_save_file ; Save new file 594 | ret 595 | 596 | ; 597 | ; Convert ASCII letter to hexadecimal digit 598 | ; 599 | xdigit: 600 | lodsb 601 | cmp al,0x00 ; Zero character marks end of line 602 | je os15 603 | sub al,0x30 ; Avoid spaces (anything below ASCII 0x30) 604 | jc xdigit 605 | cmp al,0x0a 606 | jc os15 607 | sub al,0x07 608 | and al,0x0f 609 | stc 610 | os15: 611 | ret 612 | 613 | ; 614 | ; Our amazing presentation line 615 | ; 616 | intro: 617 | db "bootOS",0 618 | 619 | error_message: 620 | db "Oops",0 621 | 622 | ; 623 | ; Commands supported by bootOS 624 | ; 625 | commands: 626 | db 3,"dir" 627 | dw dir_command 628 | db 6,"format" 629 | dw format_command 630 | db 5,"enter" 631 | dw enter_command 632 | db 3,"del" 633 | dw del_command 634 | db 3,"ver" 635 | dw ver_command 636 | db 0 637 | dw exec_from_disk 638 | 639 | int_restart: equ 0x20 640 | int_input_key: equ 0x21 641 | int_output_char: equ 0x22 642 | int_load_file: equ 0x23 643 | int_save_file: equ 0x24 644 | int_delete_file: equ 0x25 645 | 646 | int_0x20: 647 | dw restart ; int 0x20 648 | dw input_key ; int 0x21 649 | dw output_char ; int 0x22 650 | dw load_file ; int 0x23 651 | dw save_file ; int 0x24 652 | dw delete_file ; int 0x25 653 | 654 | times 510-($-$$) db 0x4f 655 | db 0x55,0xaa ; Make it a bootable sector 656 | -------------------------------------------------------------------------------- /os.img: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nanochess/bootOS/b740e059d46e7072af836189370f0dac48c8f55c/os.img -------------------------------------------------------------------------------- /os.lst: -------------------------------------------------------------------------------- 1 | 1 ; 2 | 2 ; bootOS, an operating system in 512 bytes 3 | 3 ; 4 | 4 ; by Oscar Toledo G. 5 | 5 ; http://nanochess.org/ 6 | 6 ; 7 | 7 ; Creation date: Jul/21/2019. 6pm 10pm 8 | 8 ; Revision date: Jul/22/2019. Optimization, corrections and comments. 9 | 9 ; Revision date: Jul/31/2019. Added a service table and allows 10 | 10 ; filenames/sources/targets from any segment. 11 | 11 ; 'del' command now shows errors. 12 | 12 ; 13 | 13 14 | 14 cpu 8086 15 | 15 16 | 16 ; 17 | 17 ; What is bootOS: 18 | 18 ; 19 | 19 ; bootOS is a monolithic operating system that fits in 20 | 20 ; one boot sector. It's able to load, execute, and save 21 | 21 ; programs. Also keeps a filesystem. It can work with 22 | 22 ; any floppy disk size starting at 180K. 23 | 23 ; 24 | 24 ; It relocates itself at 0000:7a00 and requires further 25 | 25 ; 768 bytes of memory starting at 0000:7700. 26 | 26 ; 27 | 27 ; This operating system runs programs as boot sectors 28 | 28 ; at 0000:7c00. 29 | 29 ; 30 | 30 ; It provides the following services: 31 | 31 ; int 0x20 Exit to operating system. 32 | 32 ; int 0x21 Input key and show in screen. 33 | 33 ; Entry: none 34 | 34 ; Output: AL = ASCII key pressed. 35 | 35 ; Affects: AH/BX/BP. 36 | 36 ; int 0x22 Output character to screen. 37 | 37 ; Entry: AL = Character. 38 | 38 ; Output: none. 39 | 39 ; Affects: AH/BX/BP. 40 | 40 ; int 0x23 Load file. 41 | 41 ; Entry: DS:BX = Filename terminated with zero. 42 | 42 ; ES:DI = Point to source data (512 bytes) 43 | 43 ; Output: Carry flag = 0 = Found, 1 = Not found. 44 | 44 ; Affects: All registers (including ES). 45 | 45 ; int 0x24 Save file. 46 | 46 ; Entry: DS:BX = Filename terminated with zero. 47 | 47 ; ES:DI = Point to data target (512 bytes) 48 | 48 ; Output: Carry flag = 0 = Successful. 1 = Error. 49 | 49 ; Affects: All registers (including ES). 50 | 50 ; int 0x25 Delete file. 51 | 51 ; Entry: DS:BX = Filename terminated with zero. 52 | 52 ; Affects: All registers (including ES). 53 | 53 ; 54 | 54 ; 55 | 55 ; Filesystem organization: 56 | 56 ; 57 | 57 ; bootOS uses tracks from 0 to 32, side 0, sector 1. 58 | 58 ; 59 | 59 ; The directory is contained in track 0, side 0, sector 2. 60 | 60 ; 61 | 61 ; Each entry in the directory is 16 bytes wide, and 62 | 62 ; contains the ASCII name of the file finished with a 63 | 63 ; zero byte. A sector has a capacity of 512 bytes, it 64 | 64 ; means only 32 files can be kept on a floppy disk. 65 | 65 ; 66 | 66 ; Deleting a file is a matter of zeroing a whole entry. 67 | 67 ; 68 | 68 ; Each file is one sector long. Its location in the 69 | 69 ; disk is derived from its position in the directory. 70 | 70 ; 71 | 71 ; The 1st file is located at track 1, side 0, sector 1. 72 | 72 ; The 2nd file is located at track 2, side 0, sector 1. 73 | 73 ; The 32nd file is located at track 32, side 0, sector 1. 74 | 74 ; 75 | 75 ; 76 | 76 ; Starting bootOS: 77 | 77 ; 78 | 78 ; Just make sure to write it at the boot sector of a 79 | 79 ; floppy disk. It can work with any floppy disk size 80 | 80 ; (360K, 720K, 1.2MB and 1.44MB) and it will waste the 81 | 81 ; disk space as only uses the first two sectors of the 82 | 82 ; disk and then the first sector of each following 83 | 83 ; track. 84 | 84 ; 85 | 85 ; For emulation make sure to deposit it at the start 86 | 86 ; of a .img file of 360K, 720K or 1440K. (at least 87 | 87 ; VirtualBox detects the type of disk by the length 88 | 88 ; of the image file) 89 | 89 ; 90 | 90 ; For Mac OS X and Linux you can create a 360K image 91 | 91 ; in this way: 92 | 92 ; 93 | 93 ; dd if=/dev/zero of=oszero.img count=719 bs=512 94 | 94 ; cat os.img oszero.img >osbase.img 95 | 95 ; 96 | 96 ; Replace 719 with 1439 for 720K, or 2879 for 1.44M. 97 | 97 ; 98 | 98 ; Tested with VirtualBox for Mac OS X running Windows XP 99 | 99 ; running it, it also works with qemu: 100 | 100 ; 101 | 101 ; qemu-system-x86_64 -fda os.img 102 | 102 ; 103 | 103 ; Running bootOS: 104 | 104 ; 105 | 105 ; The first time you should enter the 'format' command, 106 | 106 ; so it initializes the directory. It also copies itself 107 | 107 ; again to the boot sector, this is useful to init new 108 | 108 ; disks. 109 | 109 ; 110 | 110 ; bootOS commands: 111 | 111 ; 112 | 112 ; ver Shows the version (none at the moment) 113 | 113 ; dir Shows the directory's content. 114 | 114 ; del filename Deletes the "filename" file. 115 | 115 ; format As explained before. 116 | 116 ; enter Allows to enter up to 512 hexadecimal 117 | 117 ; bytes to create another file. 118 | 118 ; 119 | 119 ; Notice the line size is 128 characters so 120 | 120 ; you must break the input into chunks of 121 | 121 ; 4, 8 or 16 bytes. 122 | 122 ; 123 | 123 ; It also allows to copy the last executed 124 | 124 ; program just press Enter when the 'h' prompt 125 | 125 ; appears and type the new name. 126 | 126 ; 127 | 127 ; For example: (Character + is Enter key) 128 | 128 ; 129 | 129 ; $enter+ 130 | 130 ; hbb 17 7c 8a 07 84 c0 74 0c 53 b4 0e bb 0f 00 cd+ 131 | 131 ; h10 5b 43 eb ee cd 20 48 65 6c 6c 6f 2c 20 77 6f+ 132 | 132 ; h72 6c 64 0d 0a 00+ 133 | 133 ; h+ 134 | 134 ; *hello+ 135 | 135 ; $dir+ 136 | 136 ; hello 137 | 137 ; $hello+ 138 | 138 ; Hello, world 139 | 139 ; $ 140 | 140 ; 141 | 141 ; bootOS programs: (Oh yes! we have software support) 142 | 142 ; 143 | 143 ; fbird https://github.com/nanochess/fbird 144 | 144 ; Pillman https://github.com/nanochess/pillman 145 | 145 ; invaders https://github.com/nanochess/invaders 146 | 146 ; bootBASIC https://github.com/nanochess/bootBASIC 147 | 147 ; 148 | 148 ; You can copy the machine code directly using the 'enter' 149 | 149 ; command, or you can create a file with signature bytes 150 | 150 ; with the same command and later copy the binary into the 151 | 151 ; .img file using the signature bytes as a clue to locate 152 | 152 ; the right position in the image file. 153 | 153 ; 154 | 154 ; Or you can find a pre-designed disk image along this Git 155 | 155 ; with the name osall.img 156 | 156 ; 157 | 157 158 | 158 stack: equ 0x7700 ; Stack pointer (grows to lower addresses) 159 | 159 line: equ 0x7780 ; Buffer for line input 160 | 160 sector: equ 0x7800 ; Sector data for directory 161 | 161 osbase: equ 0x7a00 ; bootOS location 162 | 162 boot: equ 0x7c00 ; Boot sector location 163 | 163 164 | 164 entry_size: equ 16 ; Directory entry size 165 | 165 sector_size: equ 512 ; Sector size 166 | 166 max_entries: equ sector_size/entry_size 167 | 167 168 | 168 ; 169 | 169 ; Cold start of bootOS 170 | 170 ; 171 | 171 ; Notice it is loaded at 0x7c00 (boot) and needs to 172 | 172 ; relocate itself to 0x7a00 (osbase). The instructions 173 | 173 ; between 'start' and 'ver_command' shouldn't depend 174 | 174 ; on the assembly location (osbase) because these 175 | 175 ; are running at boot location (boot). 176 | 176 ; 177 | 177 org osbase 178 | 178 start: 179 | 179 00000000 31C0 xor ax,ax ; Set all segments to zero 180 | 180 00000002 8ED8 mov ds,ax 181 | 181 00000004 8EC0 mov es,ax 182 | 182 00000006 8ED0 mov ss,ax 183 | 183 00000008 BC0077 mov sp,stack ; Set stack to guarantee data safety 184 | 184 185 | 185 0000000B FC cld ; Clear D flag. 186 | 186 0000000C BE007C mov si,boot ; Copy bootOS boot sector... 187 | 187 0000000F BF007A mov di,osbase ; ...into osbase 188 | 188 00000012 B90002 mov cx,sector_size 189 | 189 00000015 F3A4 rep movsb 190 | 190 191 | 191 ; NOTE: we could avoid the need for setting SI here 192 | 192 ; by adjusting the length of the copy above, but that 193 | 193 ; would break the 'format' command. 194 | 194 00000017 BE[E601] mov si,int_0x20 ; SI now points to int_0x20 195 | 195 0000001A BF8000 mov di,0x0020*4 ; Address of service for int 0x20 196 | 196 0000001D B106 mov cl,6 197 | 197 .load_vec: 198 | 198 0000001F A5 movsw ; Copy IP address 199 | 199 00000020 AB stosw ; Copy CS address 200 | 200 00000021 E2FC loop .load_vec 201 | 201 202 | 202 ; 203 | 203 ; 'ver' command 204 | 204 ; 205 | 205 ver_command: 206 | 206 00000023 BE[B401] mov si,intro 207 | 207 print_then_restart: 208 | 208 00000026 E83E01 call output_string 209 | 209 00000029 CD20 int int_restart ; Restart bootOS 210 | 210 211 | 211 ; 212 | 212 ; Warm start of bootOS 213 | 213 ; 214 | 214 restart: 215 | 215 0000002B FC cld ; Clear D flag. 216 | 216 0000002C 0E push cs ; Reinit all segment registers 217 | 217 0000002D 0E push cs 218 | 218 0000002E 0E push cs 219 | 219 0000002F 1F pop ds 220 | 220 00000030 07 pop es 221 | 221 00000031 17 pop ss 222 | 222 00000032 BC0077 mov sp,stack ; Restart stack 223 | 223 224 | 224 00000035 B024 mov al,'$' ; Command prompt 225 | 225 00000037 E8FE00 call input_line ; Input line 226 | 226 227 | 227 0000003A 803C00 cmp byte [si],0x00 ; Empty line? 228 | 228 0000003D 74EC je restart ; Yes, get another line 229 | 229 230 | 230 0000003F BF[C001] mov di,commands ; Point to commands list 231 | 231 232 | 232 ; Notice that filenames starting with same characters 233 | 233 ; won't be recognized as such (so file dirab cannot be 234 | 234 ; executed). 235 | 235 os11: 236 | 236 00000042 8A0D mov cl,[di] ; Read length of command in chars 237 | 237 00000044 47 inc di 238 | 238 00000045 30ED xor ch, ch ; Also makes sure that ZF = 1 239 | 239 00000047 56 push si ; Save current position 240 | 240 00000048 F3A6 rep cmpsb ; Compare statement 241 | 241 0000004A 7504 jne os14 ; Equal? No, jump 242 | 242 0000004C FF15 call word [di] ; Call command process 243 | 243 0000004E EBDB jmp restart ; Go to expect another command 244 | 244 245 | 245 00000050 01CF os14: add di,cx ; Advance the list pointer 246 | 246 00000052 AF scasw ; Avoid the address 247 | 247 00000053 5E pop si 248 | 248 00000054 EBEC jmp os11 ; Compare another statement 249 | 249 250 | 250 exec_from_disk: 251 | 251 00000056 5B pop bx 252 | 252 00000057 5B pop bx 253 | 253 00000058 BF007C mov di,boot ; Location to read data 254 | 254 0000005B CD23 int int_load_file ; Load file 255 | 255 0000005D 7202 jc os7 ; Jump if error 256 | 256 0000005F FFE3 jmp bx 257 | 257 258 | 258 ; 259 | 259 ; File not found error 260 | 260 ; 261 | 261 os7: 262 | 262 00000061 BE[BB01] mov si,error_message 263 | 263 00000064 EBC0 jmp print_then_restart 264 | 264 265 | 265 ; 266 | 266 ; >> COMMAND << 267 | 267 ; del filename 268 | 268 ; 269 | 269 del_command: 270 | 270 os22: 271 | 271 00000066 89F3 mov bx,si ; Copy SI (buffer pointer) to BX 272 | 272 00000068 AC lodsb 273 | 273 00000069 3C20 cmp al,0x20 ; Avoid spaces 274 | 274 0000006B 74F9 je os22 275 | 275 0000006D CD25 int int_delete_file 276 | 276 0000006F 72F0 jc os7 277 | 277 00000071 C3 ret 278 | 278 279 | 279 ; 280 | 280 ; 'dir' command 281 | 281 ; 282 | 282 dir_command: 283 | 283 00000072 E8A000 call read_dir ; Read the directory 284 | 284 00000075 89DF mov di,bx 285 | 285 os18: 286 | 286 00000077 803D00 cmp byte [di],0 ; Empty entry? 287 | 287 0000007A 7405 je os17 ; Yes, jump 288 | 288 0000007C 89FE mov si,di ; Point to data 289 | 289 0000007E E8E600 call output_string ; Show name 290 | 290 00000081 E86E00 os17: call next_entry 291 | 291 00000084 75F1 jne os18 ; No, jump 292 | 292 00000086 C3 ret ; Return 293 | 293 294 | 294 ; 295 | 295 ; Get filename length and prepare for directory lookup 296 | 296 ; Entry: 297 | 297 ; si = pointer to string 298 | 298 ; Output: 299 | 299 ; si = unaffected 300 | 300 ; di = pointer to start of directory 301 | 301 ; cx = length of filename including zero terminator 302 | 302 ; 303 | 303 filename_length: 304 | 304 00000087 56 push si 305 | 305 00000088 31C9 xor cx,cx ; cx = 0 306 | 306 .loop: 307 | 307 0000008A AC lodsb ; Read character. 308 | 308 0000008B 41 inc cx ; Count character. 309 | 309 0000008C 3C00 cmp al,0 ; Is it zero (end character)? 310 | 310 0000008E 75FA jne .loop ; No, jump. 311 | 311 312 | 312 00000090 5E pop si 313 | 313 00000091 BF0078 mov di,sector ; Point to start of directory. 314 | 314 00000094 C3 ret 315 | 315 316 | 316 ; 317 | 317 ; >> SERVICE << 318 | 318 ; Load file 319 | 319 ; 320 | 320 ; Entry: 321 | 321 ; ds:bx = Pointer to filename ended with zero byte. 322 | 322 ; es:di = Destination. 323 | 323 ; Output: 324 | 324 ; Carry flag = Set = not found, clear = successful. 325 | 325 ; 326 | 326 load_file: 327 | 327 00000095 57 push di ; Save destination 328 | 328 00000096 06 push es 329 | 329 00000097 E84000 call find_file ; Find the file (sanitizes ES) 330 | 330 0000009A B402 mov ah,0x02 ; Read sector 331 | 331 shared_file: 332 | 332 0000009C 07 pop es 333 | 333 0000009D 5B pop bx ; Restore destination on BX 334 | 334 0000009E 7203 jc ret_cf ; Jump if error 335 | 335 000000A0 E88400 call disk ; Do operation with disk 336 | 336 ; Carry guaranteed to be clear. 337 | 337 ret_cf: 338 | 338 000000A3 89E5 mov bp,sp 339 | 339 000000A5 D05604 rcl byte [bp+4],1 ; Insert Carry flag in Flags (automatic usage of SS) 340 | 340 000000A8 CF iret 341 | 341 342 | 342 ; 343 | 343 ; >> SERVICE << 344 | 344 ; Save file 345 | 345 ; 346 | 346 ; Entry: 347 | 347 ; ds:bx = Pointer to filename ended with zero byte. 348 | 348 ; es:di = Source. 349 | 349 ; Output: 350 | 350 ; Carry flag = Set = error, clear = good. 351 | 351 ; 352 | 352 save_file: 353 | 353 000000A9 57 push di ; Save origin 354 | 354 000000AA 06 push es 355 | 355 000000AB 53 push bx ; Save filename pointer 356 | 356 000000AC CD25 int int_delete_file ; Delete previous file (sanitizes ES) 357 | 357 000000AE 5B pop bx ; Restore filename pointer 358 | 358 000000AF E8D5FF call filename_length ; Prepare for lookup 359 | 359 360 | 360 000000B2 26803D00 .find: es cmp byte [di],0 ; Found empty directory entry? 361 | 361 000000B6 7407 je .empty ; Yes, jump and fill it. 362 | 362 000000B8 E83700 call next_entry 363 | 363 000000BB 75F5 jne .find 364 | 364 000000BD EBDD jmp shared_file 365 | 365 366 | 366 000000BF 57 .empty: push di 367 | 367 000000C0 F3A4 rep movsb ; Copy full name into directory 368 | 368 000000C2 E85A00 call write_dir ; Save directory 369 | 369 000000C5 5F pop di 370 | 370 000000C6 E83200 call get_location ; Get location of file 371 | 371 000000C9 B403 mov ah,0x03 ; Write sector 372 | 372 000000CB EBCF jmp shared_file 373 | 373 374 | 374 ; 375 | 375 ; >> SERVICE << 376 | 376 ; Delete file 377 | 377 ; 378 | 378 ; Entry: 379 | 379 ; ds:bx = Pointer to filename ended with zero byte. 380 | 380 ; Output: 381 | 381 ; Carry flag = Set = not found, clear = deleted. 382 | 382 ; 383 | 383 delete_file: 384 | 384 000000CD E80A00 call find_file ; Find file (sanitizes ES) 385 | 385 000000D0 72D1 jc ret_cf ; If carry set then not found, jump. 386 | 386 000000D2 B91000 mov cx,entry_size 387 | 387 000000D5 E84300 call write_zero_dir ; Fill whole entry with zero. Write directory. 388 | 388 000000D8 EBC9 jmp ret_cf 389 | 389 390 | 390 ; 391 | 391 ; Find file 392 | 392 ; 393 | 393 ; Entry: 394 | 394 ; ds:bx = Pointer to filename ended with zero byte. 395 | 395 ; Output: 396 | 396 ; es:di = Pointer to directory entry 397 | 397 ; Carry flag = Clear if found, set if not found. 398 | 398 find_file: 399 | 399 000000DA 53 push bx 400 | 400 000000DB E83700 call read_dir ; Read directory (sanitizes ES) 401 | 401 000000DE 5E pop si 402 | 402 000000DF E8A5FF call filename_length ; Get filename length and setup DI 403 | 403 os6: 404 | 404 000000E2 56 push si 405 | 405 000000E3 57 push di 406 | 406 000000E4 51 push cx 407 | 407 000000E5 F3A6 repe cmpsb ; Compare name with entry 408 | 408 000000E7 59 pop cx 409 | 409 000000E8 5F pop di 410 | 410 000000E9 5E pop si 411 | 411 000000EA 740F je get_location ; Jump if equal. 412 | 412 000000EC E80300 call next_entry 413 | 413 000000EF 75F1 jne os6 ; No, jump 414 | 414 000000F1 C3 ret ; Return 415 | 415 416 | 416 next_entry: 417 | 417 000000F2 83C710 add di,byte entry_size ; Go to next entry. 418 | 418 000000F5 81FF007A cmp di,sector+sector_size ; Complete directory? 419 | 419 000000F9 F9 stc ; Error, not found. 420 | 420 000000FA C3 ret 421 | 421 422 | 422 ; 423 | 423 ; Get location of file on disk 424 | 424 ; 425 | 425 ; Entry: 426 | 426 ; DI = Pointer to entry in directory. 427 | 427 ; 428 | 428 ; Output: 429 | 429 ; CH = Track number in disk. 430 | 430 ; CL = Sector (always 0x01). 431 | 431 ; 432 | 432 ; The position of a file inside the disk depends on its 433 | 433 ; position in the directory. The first entry goes to 434 | 434 ; track 1, the second entry to track 2 and so on. 435 | 435 ; 436 | 436 get_location: 437 | 437 000000FB 8D851088 lea ax,[di-(sector-entry_size)] ; Get entry pointer into directory 438 | 438 ; Plus one entry (files start on track 1) 439 | 439 000000FF B104 mov cl,4 ; 2^(8-4) = entry_size 440 | 440 00000101 D3E0 shl ax,cl ; Shift left and clear Carry flag 441 | 441 00000103 40 inc ax ; AL = Sector 1 442 | 442 00000104 91 xchg ax,cx ; CH = Track, CL = Sector 443 | 443 00000105 C3 ret 444 | 444 445 | 445 ; 446 | 446 ; >> COMMAND << 447 | 447 ; format 448 | 448 ; 449 | 449 format_command: 450 | 450 00000106 BF0078 mov di,sector ; Fill whole sector to zero 451 | 451 00000109 B90002 mov cx,sector_size 452 | 452 0000010C E80C00 call write_zero_dir 453 | 453 0000010F BB007A mov bx,osbase ; Copy bootOS onto first sector 454 | 454 00000112 49 dec cx 455 | 455 00000113 EB12 jmp short disk 456 | 456 457 | 457 ; 458 | 458 ; Read the directory from disk 459 | 459 ; 460 | 460 read_dir: 461 | 461 00000115 0E push cs ; bootOS code segment... 462 | 462 00000116 07 pop es ; ...to sanitize ES register 463 | 463 00000117 B402 mov ah,0x02 464 | 464 00000119 EB06 jmp short disk_dir 465 | 465 466 | 466 write_zero_dir: 467 | 467 0000011B B000 mov al,0 468 | 468 0000011D F3AA rep stosb 469 | 469 470 | 470 ; 471 | 471 ; Write the directory to disk 472 | 472 ; 473 | 473 write_dir: 474 | 474 0000011F B403 mov ah,0x03 475 | 475 disk_dir: 476 | 476 00000121 BB0078 mov bx,sector 477 | 477 00000124 B90200 mov cx,0x0002 478 | 478 ; 479 | 479 ; Do disk operation. 480 | 480 ; 481 | 481 ; Input: 482 | 482 ; AH = 0x02 read disk, 0x03 write disk 483 | 483 ; ES:BX = data source/target 484 | 484 ; CH = Track number 485 | 485 ; CL = Sector number 486 | 486 ; 487 | 487 disk: 488 | 488 00000127 50 push ax 489 | 489 00000128 53 push bx 490 | 490 00000129 51 push cx 491 | 491 0000012A 06 push es 492 | 492 0000012B B001 mov al,0x01 ; AL = 1 sector 493 | 493 0000012D 31D2 xor dx,dx ; DH = Drive A. DL = Head 0. 494 | 494 0000012F CD13 int 0x13 495 | 495 00000131 07 pop es 496 | 496 00000132 59 pop cx 497 | 497 00000133 5B pop bx 498 | 498 00000134 58 pop ax 499 | 499 00000135 72F0 jc disk ; Retry 500 | 500 00000137 C3 ret 501 | 501 502 | 502 ; 503 | 503 ; Input line from keyboard 504 | 504 ; Entry: 505 | 505 ; al = prompt character 506 | 506 ; Output: 507 | 507 ; buffer 'line' contains line, finished with CR 508 | 508 ; SI points to 'line'. 509 | 509 ; 510 | 510 input_line: 511 | 511 00000138 CD22 int int_output_char ; Output prompt character 512 | 512 0000013A BE8077 mov si,line ; Setup SI and DI to start of line buffer 513 | 513 0000013D 89F7 mov di,si ; Target for writing line 514 | 514 0000013F 3C08 os1: cmp al,0x08 ; Backspace? 515 | 515 00000141 7502 jne os2 516 | 516 00000143 4F dec di ; Undo the backspace write 517 | 517 00000144 4F dec di ; Erase a character 518 | 518 00000145 CD21 os2: int int_input_key ; Read keyboard 519 | 519 00000147 3C0D cmp al,0x0d ; CR pressed? 520 | 520 00000149 7502 jne os10 521 | 521 0000014B B000 mov al,0x00 522 | 522 0000014D AA os10: stosb ; Save key in buffer 523 | 523 0000014E 75EF jne os1 ; No, wait another key 524 | 524 00000150 C3 ret ; Yes, return 525 | 525 526 | 526 ; 527 | 527 ; Read a key into al 528 | 528 ; Also outputs it to screen 529 | 529 ; 530 | 530 input_key: 531 | 531 00000151 B400 mov ah,0x00 532 | 532 00000153 CD16 int 0x16 533 | 533 ; 534 | 534 ; Screen output of character contained in al 535 | 535 ; Expands 0x0d (CR) into 0x0a 0x0d (LF CR) 536 | 536 ; 537 | 537 output_char: 538 | 538 00000155 3C0D cmp al,0x0d 539 | 539 00000157 7506 jne os3 540 | 540 00000159 B00A mov al,0x0a 541 | 541 0000015B CD22 int int_output_char 542 | 542 0000015D B00D mov al,0x0d 543 | 543 os3: 544 | 544 0000015F B40E mov ah,0x0e ; Output character to TTY 545 | 545 00000161 BB0700 mov bx,0x0007 ; Gray. Required for graphic modes 546 | 546 00000164 CD10 int 0x10 ; BIOS int 0x10 = Video 547 | 547 00000166 CF iret 548 | 548 549 | 549 ; 550 | 550 ; Output string 551 | 551 ; 552 | 552 ; Entry: 553 | 553 ; SI = address 554 | 554 ; 555 | 555 ; Implementation: 556 | 556 ; It assumes that SI never points to a zero length string. 557 | 557 ; 558 | 558 output_string: 559 | 559 00000167 AC lodsb ; Read character 560 | 560 00000168 CD22 int int_output_char ; Output to screen 561 | 561 0000016A 3C00 cmp al,0x00 ; Is it 0x00 (terminator)? 562 | 562 0000016C 75F9 jne output_string ; No, the loop continues 563 | 563 0000016E B00D mov al,0x0d 564 | 564 00000170 CD22 int int_output_char 565 | 565 00000172 C3 ret 566 | 566 567 | 567 ; 568 | 568 ; 'enter' command 569 | 569 ; 570 | 570 enter_command: 571 | 571 00000173 BF007C mov di,boot ; Point to boot sector 572 | 572 00000176 57 os23: push di 573 | 573 00000177 B068 mov al,'h' ; Prompt character 574 | 574 00000179 E8BCFF call input_line ; Input line 575 | 575 0000017C 5F pop di 576 | 576 0000017D 803C00 cmp byte [si],0 ; Empty line? 577 | 577 00000180 7412 je os20 ; Yes, jump 578 | 578 00000182 E81C00 os19: call xdigit ; Get a hexadecimal digit 579 | 579 00000185 73EF jnc os23 580 | 580 00000187 B104 mov cl,4 581 | 581 00000189 D2E0 shl al,cl 582 | 582 0000018B 91 xchg ax,cx 583 | 583 0000018C E81200 call xdigit ; Get a hexadecimal digit 584 | 584 0000018F 08C8 or al,cl 585 | 585 00000191 AA stosb ; Write one byte 586 | 586 00000192 EBEE jmp os19 ; Repeat loop to complete line 587 | 587 os20: 588 | 588 00000194 B02A mov al,'*' ; Prompt character 589 | 589 00000196 E89FFF call input_line ; Input line with filename 590 | 590 00000199 56 push si 591 | 591 0000019A 5B pop bx 592 | 592 0000019B BF007C mov di,boot ; Point to data entered 593 | 593 0000019E CD24 int int_save_file ; Save new file 594 | 594 000001A0 C3 ret 595 | 595 596 | 596 ; 597 | 597 ; Convert ASCII letter to hexadecimal digit 598 | 598 ; 599 | 599 xdigit: 600 | 600 000001A1 AC lodsb 601 | 601 000001A2 3C00 cmp al,0x00 ; Zero character marks end of line 602 | 602 000001A4 740D je os15 603 | 603 000001A6 2C30 sub al,0x30 ; Avoid spaces (anything below ASCII 0x30) 604 | 604 000001A8 72F7 jc xdigit 605 | 605 000001AA 3C0A cmp al,0x0a 606 | 606 000001AC 7205 jc os15 607 | 607 000001AE 2C07 sub al,0x07 608 | 608 000001B0 240F and al,0x0f 609 | 609 000001B2 F9 stc 610 | 610 os15: 611 | 611 000001B3 C3 ret 612 | 612 613 | 613 ; 614 | 614 ; Our amazing presentation line 615 | 615 ; 616 | 616 intro: 617 | 617 000001B4 626F6F744F5300 db "bootOS",0 618 | 618 619 | 619 error_message: 620 | 620 000001BB 4F6F707300 db "Oops",0 621 | 621 622 | 622 ; 623 | 623 ; Commands supported by bootOS 624 | 624 ; 625 | 625 commands: 626 | 626 000001C0 03646972 db 3,"dir" 627 | 627 000001C4 [7200] dw dir_command 628 | 628 000001C6 06666F726D6174 db 6,"format" 629 | 629 000001CD [0601] dw format_command 630 | 630 000001CF 05656E746572 db 5,"enter" 631 | 631 000001D5 [7301] dw enter_command 632 | 632 000001D7 0364656C db 3,"del" 633 | 633 000001DB [6600] dw del_command 634 | 634 000001DD 03766572 db 3,"ver" 635 | 635 000001E1 [2300] dw ver_command 636 | 636 000001E3 00 db 0 637 | 637 000001E4 [5600] dw exec_from_disk 638 | 638 639 | 639 int_restart: equ 0x20 640 | 640 int_input_key: equ 0x21 641 | 641 int_output_char: equ 0x22 642 | 642 int_load_file: equ 0x23 643 | 643 int_save_file: equ 0x24 644 | 644 int_delete_file: equ 0x25 645 | 645 646 | 646 int_0x20: 647 | 647 000001E6 [2B00] dw restart ; int 0x20 648 | 648 000001E8 [5101] dw input_key ; int 0x21 649 | 649 000001EA [5501] dw output_char ; int 0x22 650 | 650 000001EC [9500] dw load_file ; int 0x23 651 | 651 000001EE [A900] dw save_file ; int 0x24 652 | 652 000001F0 [CD00] dw delete_file ; int 0x25 653 | 653 654 | 654 000001F2 4F times 510-($-$$) db 0x4f 655 | 655 000001FE 55AA db 0x55,0xaa ; Make it a bootable sector 656 | -------------------------------------------------------------------------------- /osall.img: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nanochess/bootOS/b740e059d46e7072af836189370f0dac48c8f55c/osall.img -------------------------------------------------------------------------------- /patch/mine.asm: -------------------------------------------------------------------------------- 1 | ; From https://gitlab.com/blevy/boot-sector-minesweeper 2 | ; MIT license. 3 | ; Modified to exit to bootOS by nanochess (Sep/16/2019) 4 | 5 | BITS 16 6 | CPU 686 7 | 8 | ;; CONSTANTS 9 | 10 | ;; Boot sector load address 11 | %assign BootSector.Begin 0x7c00 12 | %assign BootSector.Size 512 13 | %assign BootSector.End BootSector.Begin + BootSector.Size 14 | 15 | %assign WordSize 2 16 | 17 | ;; Address and dimensions of text buffer 18 | %assign TextBuf.Seg 0xb800 19 | %assign TextBuf.Width 40 20 | %assign TextBuf.Height 25 21 | 22 | ;; Minefield dimensions 23 | %assign Map.Width TextBuf.Width 24 | %assign Map.Height TextBuf.Height 25 | %assign Map.Size Map.Width * Map.Height 26 | 27 | ;; Keyboard scan codes 28 | ;; http://www.ctyme.com/intr/rb-0045.htm#Table6 29 | %assign Key.Space 0x39 30 | %assign Key.Up 0x48 31 | %assign Key.Down 0x50 32 | %assign Key.Left 0x4b 33 | %assign Key.Right 0x4d 34 | 35 | ;; GLOBAL VARIABLES 36 | 37 | ;; TODO: Change global vars to a state struct 38 | 39 | ;; Global variables are stored after the boot sector at runtime. After the boot 40 | ;; sector, there is 480.5K of memory safe to use. 41 | ;; https://wiki.osdev.org/Memory_Map_(x86)#Overview 42 | 43 | %assign Vars.Begin BootSector.End 44 | 45 | ;; TODO: Document these 46 | %assign Map.Mines Vars.Begin 47 | %assign Map.Unveiled Map.Mines + Map.Size 48 | 49 | ;; Distance between Map.Mines and Map.Unveiled 50 | %assign Map.Mines.ToUnveiled (Map.Unveiled - Map.Mines) 51 | 52 | ;; Seed used for random number generation 53 | %assign RandomSeed Map.Unveiled + Map.Size 54 | 55 | %assign Vars.End RandomSeed + WordSize 56 | 57 | %assign Vars.Size Vars.End - Vars.Begin 58 | 59 | org BootSector.Begin 60 | 61 | BootMine: 62 | ; VGA text mode 0x00 63 | ; 320x200 pixel resolution 64 | ; 40x25 text resolution 65 | ; 16 colors 66 | ; http://www.ctyme.com/intr/rb-0069.htm 67 | xor ax, ax 68 | int 0x10 69 | 70 | ;; Populate Map.Mines with mines 71 | PopulateMines: 72 | mov di, Map.Mines 73 | mov cx, Map.Size 74 | .Loop: 75 | ; ax = rdtsc() & 0b111 ? 0 : 1 76 | rdtsc 77 | test al, 0x07 78 | jz .Mine 79 | .Empty: 80 | xor ax, ax 81 | jmp .WriteCell 82 | .Mine: 83 | mov ax, 1 84 | .WriteCell: 85 | stosb 86 | loop .Loop 87 | 88 | ;; Number empty cells with amount of neighboring mines 89 | NumCells: 90 | mov di, Map.Unveiled 91 | mov cx, Map.Size 92 | .Loop: 93 | ; Get digit for the cell at DI 94 | mov al, [di - Map.Mines.ToUnveiled] 95 | test ax, ax 96 | jz .Empty 97 | .Mine: 98 | mov ax, '*' 99 | jmp .WriteCell 100 | .Empty: 101 | mov ax, '0' 102 | 103 | ; Straight 104 | lea bx, [di - 1 - Map.Mines.ToUnveiled] 105 | call LeftIncIfMineAtCell 106 | lea bx, [di + 1 - Map.Mines.ToUnveiled] 107 | call RightIncIfMineAtCell 108 | lea bx, [di - Map.Width - Map.Mines.ToUnveiled] 109 | call IncIfMineAtCell 110 | lea bx, [di + Map.Width - Map.Mines.ToUnveiled] 111 | call IncIfMineAtCell 112 | 113 | ; Diagonal 114 | lea bx, [di - 1 - Map.Width - Map.Mines.ToUnveiled] 115 | call LeftIncIfMineAtCell 116 | lea bx, [di - 1 + Map.Width - Map.Mines.ToUnveiled] 117 | call LeftIncIfMineAtCell 118 | lea bx, [di + 1 - Map.Width - Map.Mines.ToUnveiled] 119 | call RightIncIfMineAtCell 120 | lea bx, [di + 1 + Map.Width - Map.Mines.ToUnveiled] 121 | call RightIncIfMineAtCell 122 | 123 | cmp ax, '0' 124 | jne .WriteCell 125 | .Zero: 126 | mov ax, ' ' 127 | .WriteCell: 128 | stosb 129 | loop .Loop 130 | 131 | ClearScreen: 132 | mov cx, Map.Size 133 | xor di, di 134 | mov ax, 0xa0 << 8 | '.' 135 | mov dx, TextBuf.Seg 136 | mov es, dx 137 | .Loop: 138 | stosw 139 | loop .Loop 140 | 141 | xor dx, dx 142 | mov es, dx 143 | xor bp, bp 144 | 145 | GameLoop: 146 | ; Get keystroke 147 | ; AH = BIOS scan code 148 | ; AL = ASCII character 149 | ; http://www.ctyme.com/intr/rb-1754.htm 150 | xor ax, ax 151 | int 0x16 152 | 153 | .CmpUp: 154 | cmp ah, Key.Up 155 | jne .CmpDown 156 | sub bp, Map.Width 157 | jmp WrapCursor 158 | .CmpDown: 159 | cmp ah, Key.Down 160 | jne .CmpLeft 161 | add bp, Map.Width 162 | jmp WrapCursor 163 | .CmpLeft: 164 | cmp ah, Key.Left 165 | jne .CmpRight 166 | dec bp 167 | jmp WrapCursor 168 | .CmpRight: 169 | cmp ah, Key.Right 170 | jne .CmpSpace 171 | inc bp 172 | jmp WrapCursor 173 | .CmpSpace: 174 | cmp ah, Key.Space 175 | jne GameLoop 176 | 177 | ClearCell: 178 | mov al, [bp + Map.Unveiled] 179 | .CmpEmpty: 180 | cmp al, ' ' 181 | jne .CmpMine 182 | call Flood 183 | jmp GameLoop 184 | .CmpMine: 185 | cmp al, '*' 186 | jne .Digit 187 | jmp GameOver 188 | .Digit: 189 | call TextBufSetCharAt 190 | 191 | WrapCursor: 192 | cmp bp, Map.Size 193 | jb SetCursorPos 194 | xor bp, bp 195 | 196 | SetCursorPos: 197 | xor bx, bx 198 | call GetCursorPos 199 | mov dh, al 200 | mov dl, ah 201 | ; Set cursor position 202 | ; DH = Row 203 | ; DL = Column 204 | ; http://www.ctyme.com/intr/rb-0087.htm 205 | mov ah, 0x02 206 | int 0x10 207 | 208 | jmp GameLoop 209 | 210 | ;; Split the linear cursor position in BP as COL:ROW in AH:AL 211 | ;; 212 | ;; Clobbered registers: 213 | ;; * CL 214 | GetCursorPos: 215 | mov ax, bp 216 | mov cl, Map.Width 217 | div cl 218 | ret 219 | 220 | ;; TODO: Use this method in more places 221 | ;; 222 | ;; Get the character at position BP in the text buffer in AL 223 | ;; 224 | ;; Clobbered registers: 225 | ;; * DX 226 | TextBufGetCharAt: 227 | push bp 228 | mov dx, TextBuf.Seg 229 | mov ds, dx 230 | add bp, bp 231 | mov al, [ds:bp] 232 | xor dx, dx 233 | mov ds, dx 234 | pop bp 235 | ret 236 | 237 | ;; TODO: Use this method in more places 238 | ;; 239 | ;; Put the character AL in the text buffer at position BP 240 | ;; 241 | ;; Clobbered registers: 242 | ;; * DX 243 | TextBufSetCharAt: 244 | push bp 245 | mov dx, TextBuf.Seg 246 | mov ds, dx 247 | add bp, bp 248 | mov [ds:bp], al 249 | xor dx, dx 250 | mov ds, dx 251 | pop bp 252 | ret 253 | 254 | RightIncIfMineAtCell: 255 | push bx 256 | push ax 257 | push dx 258 | sub bx, Map.Mines 259 | mov ax, bx 260 | cwd 261 | mov bx, Map.Width 262 | idiv bx 263 | test dx, dx 264 | pop dx 265 | pop ax 266 | pop bx 267 | jz IncIfMineAtCell.RetZero 268 | jmp IncIfMineAtCell 269 | 270 | LeftIncIfMineAtCell: 271 | push bx 272 | push ax 273 | push dx 274 | sub bx, Map.Mines 275 | mov ax, bx 276 | cwd 277 | mov bx, Map.Width 278 | idiv bx 279 | cmp dx, Map.Width - 1 280 | pop dx 281 | pop ax 282 | pop bx 283 | je IncIfMineAtCell.RetZero 284 | ;; TODO: Update comment 285 | ;; 286 | ;; Increment AX if there is a mine in Map.Mines at index BX, where BX is a 287 | ;; pointer inside Map.Mines. In the case where BX is outside Map.Mines, AX is 288 | ;; NOT incremented. 289 | ;; 290 | ;; Parameters 291 | ;; * BX - Pointer inside Map.Mines 292 | ;; Clobbered registers 293 | ;; * AX - either incremented or unchanged, depending on whether there is or 294 | ;; isn't a mine at BX, respectively 295 | IncIfMineAtCell: 296 | ; Bounds check 297 | cmp bx, Map.Mines 298 | jb .RetZero 299 | cmp bx, Map.Mines + Map.Size 300 | jae .RetZero 301 | ; Within map bounds. Dereference and add map pointer. 302 | add al, [bx] 303 | ret 304 | .RetZero: 305 | ; Outside map bounds. Do not increment. 306 | ret 307 | 308 | ;; Flood fill empty cells 309 | ;; 310 | ;; Parameters: 311 | ;; * BP - Cell index 312 | ;; Clobbered registers: 313 | ;; * Yes [TODO] 314 | Flood: 315 | push bp 316 | 317 | ; Base case: bounds check 318 | cmp bp, Map.Size 319 | jae .Ret 320 | 321 | ; Base case: visited cell 322 | call TextBufGetCharAt 323 | cmp al, '.' 324 | jne .Ret 325 | 326 | ; Body: unveil cell 327 | mov al, [bp + Map.Unveiled] 328 | call TextBufSetCharAt 329 | 330 | ; Base case: nonempty cell 331 | cmp al, ' ' 332 | jne .Ret 333 | 334 | ; Recursive case: flood adjacent cells 335 | 336 | ; Flood up 337 | push bp 338 | sub bp, Map.Width 339 | call Flood 340 | pop bp 341 | 342 | ; Flood down 343 | push bp 344 | add bp, Map.Width 345 | call Flood 346 | pop bp 347 | 348 | ; Flood left 349 | call GetCursorPos 350 | test ah, ah 351 | jz .Right 352 | push bp 353 | dec bp 354 | call Flood 355 | pop bp 356 | 357 | .Right: 358 | ; Flood right 359 | inc bp 360 | call GetCursorPos 361 | test ah, ah 362 | jz .Ret 363 | call Flood 364 | 365 | .Ret: 366 | pop bp 367 | ret 368 | 369 | GameOverStr: 370 | db 'GAME OVER' 371 | %assign GameOverStr.Len $ - GameOverStr 372 | 373 | ;; Unveil all the mines, print "GAME OVER" text, and allow restarting 374 | ;; TODO: Finish this 375 | GameOver: 376 | ; Print "GAME OVER" in center of screen 377 | mov ax, 0x1300 378 | mov bx, 0x00c0 379 | mov cx, GameOverStr.Len 380 | mov dx, ((TextBuf.Height / 2) << 8) | (TextBuf.Width / 2 - GameOverStr.Len / 2) 381 | mov bp, GameOverStr 382 | int 0x10 383 | 384 | ; Halt forever 385 | mov ah, 0x00 386 | int 0x16 387 | mov ax, 0x0002 388 | int 0x10 389 | int 0x20 390 | 391 | ;; Print program size at build time 392 | %assign CodeSize $ - $$ 393 | ;%warning Code is CodeSize bytes 394 | 395 | CodeEnd: 396 | ; Pad to size of boot sector, minus the size of a word for the boot sector 397 | ; magic value. If the code is too big to fit in a boot sector, the `times` 398 | ; directive uses a negative value, causing a build error. 399 | times (BootSector.Size - WordSize) - CodeSize db 0 400 | 401 | ; Boot sector magic 402 | dw 0xaa55 403 | 404 | ;; Boot sector ends here 405 | -------------------------------------------------------------------------------- /patch/mine.img: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nanochess/bootOS/b740e059d46e7072af836189370f0dac48c8f55c/patch/mine.img -------------------------------------------------------------------------------- /patch/snake.asm: -------------------------------------------------------------------------------- 1 | ; From https://gitlab.com/pmikkelsen/asm_snake 2 | ; MIT License 3 | ; Modified to exit with Esc key to bootOS (nanochess, Sep/16/2019) 4 | 5 | mov ax, 0x07C0 6 | mov ds, ax ; set DS to the point where code is loaded 7 | mov ah, 0x01 8 | mov cx, 0x2000 9 | int 0x10 ; clear cursor blinking 10 | mov ax, 0x0305 11 | mov bx, 0x031F 12 | int 0x16 ; increase delay before keybort repeat 13 | 14 | game_loop: 15 | call clear_screen ; clear the screen 16 | push word [snake_pos] ; save snake head position for later 17 | mov ah, 0x01 ; check if key available 18 | int 0x16 19 | jz done_clear ; if not, move on 20 | mov ah, 0x00 ; if the was a key, remove it from buffer 21 | int 0x16 22 | jmp update_snakepos 23 | done_clear: 24 | mov al, [last_move] ; no keys, so we use the last one 25 | update_snakepos: 26 | cmp al, 0x1b ; Esc key 27 | jne no_exit 28 | int 0x20 29 | no_exit: 30 | cmp al, 'a' 31 | je left 32 | cmp al, 's' 33 | je down 34 | cmp al, 'd' 35 | je right 36 | cmp al, 'w' 37 | jne done_clear 38 | up: 39 | dec byte [snake_y_pos] 40 | jmp move_done ; jump away 41 | left: 42 | dec byte [snake_x_pos] 43 | jmp move_done ; jump away 44 | right: 45 | inc byte [snake_x_pos] 46 | jmp move_done ; jump away 47 | down: 48 | inc word [snake_y_pos] 49 | move_done: 50 | mov [last_move], al ; save the direction 51 | mov si, snake_body_pos ; prepare body shift 52 | pop ax ; restore read position into ax for body shift 53 | update_body: 54 | mov bx, [si] ; get element of body into bx 55 | test bx, bx ; check if zero (not a part of the body) 56 | jz done_update ; if zero, done. Otherwise 57 | mov [si], ax ; move the data from ax, into current position 58 | add si, 2 ; increment pointer by two bytes 59 | mov ax, bx ; save bx into ax for next loop 60 | jmp update_body ; loop 61 | done_update: 62 | cmp byte [grow_snake_flag], 1 ; snake should grow? 63 | jne add_zero_snake ; if not: jump to add_zero_snake 64 | mov word [si], ax ; save the last element at the next position 65 | mov byte [grow_snake_flag], 0 ; disable grow_snake_flag 66 | add si, 2 ; increment si by 2 67 | add_zero_snake: 68 | mov word [si], 0x0000 69 | print_stuff: 70 | xor dx, dx ; set pos to 0x0000 71 | call move_cursor ; move cursor 72 | mov si, score_msg ; prepare to print score string 73 | call print_string ; print it 74 | mov ax, [score] ; move the score into ax 75 | call print_int ; print it 76 | mov dx, [food_pos] ; set dx to the food position 77 | call move_cursor ; move cursor there 78 | mov al, '*' ; use '*' as food symbol 79 | call print_char ; print food 80 | mov dx, [snake_pos] ; set dx to the snake head position 81 | call move_cursor ; move there 82 | mov al, '@' ; use '@' as snake head symbol 83 | call print_char ; print it 84 | mov si, snake_body_pos ; prepare to print snake body 85 | snake_body_print_loop: 86 | lodsw ; load position from the body, and increment si 87 | test ax, ax ; check if position is zero 88 | jz check_collisions ; if it was zero, move out of here 89 | mov dx, ax ; if not, move the position into dx 90 | call move_cursor ; move the cursor there 91 | mov al, 'o' ; use 'o' as the snake body symbol 92 | call print_char ; print it 93 | jmp snake_body_print_loop ; loop 94 | 95 | check_collisions: 96 | mov bx, [snake_pos] ; move the snake head position into bx 97 | cmp bh, 25 ; check if we are too far down 98 | jge game_over_hit_wall ; if yes, jump 99 | cmp bh, 0 ; check if we are too far up 100 | jl game_over_hit_wall ; if yes, jump 101 | cmp bl, 80 ; check if we are too far to the right 102 | jge game_over_hit_wall ; if yes, jump 103 | cmp bl, 0 ; check if we are too far to the left 104 | jl game_over_hit_wall ; if yes, jump 105 | mov si, snake_body_pos ; prepare to check for self-collision 106 | check_collisions_self: 107 | lodsw ; load position of snake body, and increment si 108 | cmp ax, bx ; check if head position = body position 109 | je game_over_hit_self ; if it is, jump 110 | or ax, ax ; check if position is 0x0000 (we are done searching) 111 | jne check_collisions_self ; if not, loop 112 | 113 | no_collision: 114 | mov ax, [snake_pos] ; load snake head position into ax 115 | cmp ax, [food_pos] ; check if we are on the food 116 | jne game_loop_continued ; jump if snake didn't hit food 117 | inc word [score] ; if we were on food, increment score 118 | mov bx, 24 ; set max value for random call (y-val - 1) 119 | call rand ; generate random value 120 | push dx ; save it on the stack 121 | mov bx, 78 ; set max value for random call 122 | call rand ; generate random value 123 | pop cx ; restore old random into cx 124 | mov dh, cl ; move old value into high bits of new 125 | mov [food_pos], dx ; save the position of the new random food 126 | mov byte [grow_snake_flag], 1 ; make sure snake grows 127 | game_loop_continued: 128 | mov cx, 0x0002 ; Sleep for 0,15 seconds (cx:dx) 129 | mov dx, 0x49F0 ; 0x000249F0 = 150000 130 | mov ah, 0x86 131 | int 0x15 ; Sleep 132 | jmp game_loop ; loop 133 | 134 | game_over_hit_self: 135 | push self_msg 136 | jmp game_over 137 | 138 | game_over_hit_wall: 139 | push wall_msg 140 | 141 | game_over: 142 | call clear_screen 143 | mov si, hit_msg 144 | call print_string 145 | pop si 146 | call print_string 147 | mov si, retry_msg 148 | call print_string 149 | wait_for_r: 150 | mov ah, 0x00 151 | int 0x16 152 | cmp al, 'r' 153 | jne wait_for_r 154 | mov word [snake_pos], 0x0F0F 155 | and word [snake_body_pos], 0 156 | and word [score], 0 157 | mov byte [last_move], 'd' 158 | jmp game_loop 159 | 160 | ; SCREEN FUNCTIONS ------------------------------------------------------------ 161 | clear_screen: 162 | mov ax, 0x0700 ; clear entire window (ah 0x07, al 0x00) 163 | mov bh, 0x0C ; light red on black 164 | xor cx, cx ; top left = (0,0) 165 | mov dx, 0x1950 ; bottom right = (25, 80) 166 | int 0x10 167 | xor dx, dx ; set dx to 0x0000 168 | call move_cursor ; move cursor 169 | ret 170 | 171 | move_cursor: 172 | mov ah, 0x02 ; move to (dl, dh) 173 | xor bh, bh ; page 0 174 | int 0x10 175 | ret 176 | 177 | print_string_loop: 178 | call print_char 179 | print_string: ; print the string pointed to in si 180 | lodsb ; load next byte from si 181 | test al, al ; check if high bit is set (end of string) 182 | jns print_string_loop ; loop if high bit was not set 183 | 184 | print_char: ; print the char at al 185 | and al, 0x7F ; unset the high bit 186 | mov ah, 0x0E 187 | int 0x10 188 | ret 189 | 190 | print_int: ; print the int in ax 191 | push bp ; save bp on the stack 192 | mov bp, sp ; set bp = stack pointer 193 | 194 | push_digits: 195 | xor dx, dx ; clear dx for division 196 | mov bx, 10 ; set bx to 10 197 | div bx ; divide by 10 198 | push dx ; store remainder on stack 199 | test ax, ax ; check if quotient is 0 200 | jnz push_digits ; if not, loop 201 | 202 | pop_and_print_digits: 203 | pop ax ; get first digit from stack 204 | add al, '0' ; turn it into ascii digits 205 | call print_char ; print it 206 | cmp sp, bp ; is the stack pointer is at where we began? 207 | jne pop_and_print_digits ; if not, loop 208 | pop bp ; if yes, restore bp 209 | ret 210 | ; UTILITY FUNCTIONS ----------------------------------------------------------- 211 | rand: ; random number between 1 and bx. result in dx 212 | mov ah, 0x00 213 | int 0x1A ; get clock ticks since midnight 214 | mov ax, dx ; move lower bits into ax for division 215 | xor dx, dx ; clear dx 216 | div bx ; divide ax by bx to get remainder in dx 217 | inc dx 218 | ret 219 | 220 | ; MESSAGES (Encoded as 7-bit strings. Last byte is an ascii value with its 221 | ; high bit set ---------------------------------------------------------------- 222 | retry_msg db '! press r to retr', 0xF9 ; y 223 | hit_msg db 'You hit', 0xA0 ; space 224 | self_msg db 'yoursel', 0xE6 ; f 225 | wall_msg db 'the wal', 0xEC ; l 226 | score_msg db 'Score:', 0xA0 ; space 227 | 228 | ; VARIABLES ------------------------------------------------------------------- 229 | grow_snake_flag db 0 230 | food_pos dw 0x0D0D 231 | score dw 1 232 | last_move db 'd' 233 | snake_pos: 234 | snake_x_pos db 0x0F 235 | snake_y_pos db 0x0F 236 | snake_body_pos dw 0x0000 237 | 238 | ; PADDING AND BOOT SIGNATURE -------------------------------------------------- 239 | times 510-($-$$) db 0 240 | db 0x55 241 | db 0xAA 242 | -------------------------------------------------------------------------------- /patch/snake.img: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nanochess/bootOS/b740e059d46e7072af836189370f0dac48c8f55c/patch/snake.img -------------------------------------------------------------------------------- /patch/sokoban.asm: -------------------------------------------------------------------------------- 1 | ; by Ish. 2 | ; Public domain from https://ish.works/bootsector/bootsector.html 3 | 4 | bits 16 ; tell NASM this is 16 bit code 5 | org 0x7c00 ; tell NASM that our code will be loaded at offset 0x7c00 6 | 7 | %define CURRENT_LEVEL 0x1000 8 | %define CURRENT_LEVEL_4 0x1004 9 | %define SCREEN_DS 0xb800 10 | 11 | boot: 12 | ; clear screen (re-set text mode) 13 | xor ah, ah 14 | mov al, 0x03 ; text mode 80x25 16 colours 15 | int 0x10 16 | 17 | ; disable cursor 18 | mov ah, 0x01 19 | mov ch, 0x3f 20 | int 0x10 21 | 22 | ; set up stack 23 | mov ax, 0x9000 24 | mov ss, ax 25 | mov sp, 0x400 26 | 27 | ; set current level to test level by copying 28 | mov si, test_level 29 | 30 | ; get width and height and multiply by each other 31 | mov ax, [si] 32 | mul ah 33 | 34 | ; set multiplied width and height + 4 as counter 35 | mov cx, ax 36 | ;add cx, 4 37 | 38 | mov di, CURRENT_LEVEL ; next address to copy to 39 | xor ax, ax 40 | mov es, ax 41 | 42 | ; copy map size and player position ("uncompressed") 43 | lodsw 44 | stosw 45 | lodsw 46 | stosw 47 | 48 | .copy_level_loop: 49 | ; load "compressed" byte: e.g. 0x28 or 0x44 into AL 50 | lodsb 51 | 52 | mov ah, al ; AX = 0x2828 53 | and ax, 0x0FF0 ; AX = 0x0820 (little endian: 20 08) 54 | shr al, 4 ; AX = 0x0802 (little endian: 02 08) 55 | 56 | ; save "uncompressed" word: e.g. 02 08 or 04 04 from AX 57 | stosw 58 | 59 | loop .copy_level_loop 60 | 61 | call draw_current_level 62 | 63 | .mainloop: 64 | ; read key 65 | xor ax, ax 66 | int 0x16 67 | 68 | cmp ah, 0x01 ; esc 69 | je boot 70 | 71 | cmp ah, 0x50 ; down arrow 72 | je .try_move_down 73 | 74 | cmp ah, 0x48 ; up arrow 75 | je .try_move_up 76 | 77 | cmp ah, 0x4b ; left arrow 78 | je .try_move_left 79 | 80 | cmp ah, 0x4d ; right arrow 81 | je .try_move_right 82 | 83 | .redraw: 84 | call draw_current_level 85 | 86 | .check_win: 87 | 88 | ; get width and height 89 | mov ax, [CURRENT_LEVEL] ; al = width; ah = height 90 | mul ah 91 | mov cx, ax ; cx = size of map 92 | 93 | xor bx, bx ; bx = number of bricks-NOT-on-a-spot 94 | 95 | mov si, CURRENT_LEVEL_4 96 | .check_win_loop: 97 | lodsb 98 | cmp al, 2 99 | jne .not_a_brick 100 | inc bx 101 | .not_a_brick: 102 | loop .check_win_loop 103 | 104 | ; so, did we win? is the number of spotless bricks == 0?? 105 | cmp bx, 0 106 | je win 107 | jmp .mainloop 108 | 109 | 110 | .try_move_down: 111 | mov al, byte [CURRENT_LEVEL] ; (width of current level) to the right = 1 down 112 | call try_move 113 | jmp .redraw 114 | 115 | .try_move_up: 116 | mov al, byte [CURRENT_LEVEL] 117 | neg al ; (width of current level) to the left = 1 up 118 | call try_move 119 | jmp .redraw 120 | 121 | .try_move_left: 122 | mov al, -1 ; one to the left 123 | call try_move 124 | jmp .redraw 125 | 126 | .try_move_right: 127 | mov al, 1 ; one to the right 128 | call try_move 129 | jmp .redraw 130 | 131 | win: 132 | ; print a nice win message to the middle of the screen 133 | mov si, str_you_win 134 | 135 | ; destination position on screen 136 | mov ax, SCREEN_DS 137 | mov es, ax 138 | mov di, (80 * 12 + 40 - 6) * 2 139 | 140 | mov ah, 0x0F 141 | .loop: 142 | lodsb 143 | 144 | cmp al, 0 145 | je wait_for_esc 146 | 147 | stosw 148 | jmp .loop 149 | 150 | wait_for_esc: 151 | ; read key 152 | xor ax, ax 153 | int 0x16 154 | 155 | cmp ah, 0x01 ; esc 156 | je boot 157 | jmp wait_for_esc 158 | ; halt: 159 | ; cli ; clear interrupt flag 160 | ; hlt ; halt execution 161 | 162 | 163 | ;; functions: 164 | 165 | draw_current_level: 166 | ; get width and height 167 | mov cx, [CURRENT_LEVEL] ; cl = width; ch = height 168 | push cx ; put it in the stack for later reuse 169 | 170 | ; print in the middle and not in the corner 171 | mov di, 2000; middle of screen 172 | 173 | ; offset by half of width 174 | mov bl, cl 175 | and bx, 0x00FE 176 | sub di, bx 177 | 178 | ; offset by half of height 179 | mov cl, ch 180 | and cx, 0x00FE 181 | mov ax, 80 182 | mul cx 183 | sub di, ax 184 | 185 | 186 | mov si, CURRENT_LEVEL_4 ; source byte 187 | 188 | ; screen memory in text mode 189 | mov ax, SCREEN_DS 190 | mov es, ax 191 | 192 | .loop: 193 | mov bl, [si] 194 | xor bh, bh 195 | add bx, bx 196 | add bx, display_chars 197 | mov dx, [bx] 198 | mov [es:di], dx 199 | 200 | inc si 201 | add di, 2 202 | pop cx ; get counters 203 | dec cl ; subtract 1 from X axis counter 204 | jz .nextrow 205 | push cx 206 | jmp .loop 207 | 208 | .nextrow: 209 | dec ch ; subtract 1 from Y axis counter 210 | jz .finished 211 | mov cl, [CURRENT_LEVEL] 212 | push cx 213 | 214 | ; jump to next row down 215 | xor ch, ch 216 | neg cx 217 | add cx, 80 218 | add cx, cx 219 | add di, cx 220 | 221 | jmp .loop 222 | 223 | .finished: 224 | ret 225 | 226 | try_move: 227 | ; try to move the player 228 | ; al = offset of how much to move by 229 | pusha 230 | 231 | ; extend al into ax (signed) 232 | test al, al ; check if negative 233 | js .negative_al 234 | xor ah, ah 235 | jmp .after_al 236 | .negative_al: 237 | mov ah, 0xFF 238 | .after_al: 239 | push ax 240 | 241 | ; calculate total level size 242 | mov ax, [CURRENT_LEVEL] 243 | mul ah 244 | 245 | ; calculate requested destination position 246 | pop bx 247 | push bx 248 | mov dx, [CURRENT_LEVEL + 2] 249 | add bx, dx 250 | 251 | ; check if in bounds 252 | cmp bx, 0 253 | jl .finished 254 | cmp bx, ax 255 | jg .finished 256 | 257 | ; get value at destination position 258 | mov cl, [CURRENT_LEVEL_4 + bx] 259 | cmp cl, 4 260 | je .cant_push ; it's a wall 261 | test cl, 0x02 262 | jz .dont_push ; it's not a brick (on spot, or not), so don't try pushing 263 | 264 | ; try pushing brick 265 | pop cx ; get move offset 266 | push bx ; store player's destination position (brick's current position) 267 | 268 | mov dx, bx ; dx = current brick position 269 | add bx, cx ; bx = next brick position 270 | 271 | ; check bounds 272 | cmp bx, 0 273 | jl .cant_push 274 | cmp bx, ax 275 | jg .cant_push 276 | 277 | ; get value at destination position 278 | mov ch, [CURRENT_LEVEL_4 + bx] 279 | test ch, 0x0E ; test if the destination is occupied at all by ANDing with 0000 1110 280 | jnz .cant_push 281 | 282 | ; all checks passed! push the brick 283 | 284 | ; add new brick to screen 285 | or ch, 0x02 ; add brick bit, by ORing with 0000 0010 286 | mov [CURRENT_LEVEL_4 + bx], ch 287 | 288 | ; remove old brick from screen 289 | mov si, dx 290 | mov cl, [CURRENT_LEVEL_4 + si] 291 | and cl, 0xFD ; remove brick bit, by ANDing with 1111 1101 292 | mov [CURRENT_LEVEL_4 + si], cl 293 | 294 | mov dx, [CURRENT_LEVEL + 2] ; dx = current player position 295 | pop bx ; bx = next player position 296 | jmp .redraw_player 297 | 298 | .cant_push: 299 | pop bx 300 | jmp .finished 301 | 302 | .dont_push: 303 | pop cx ; don't need to have this offset in the stack anymore 304 | 305 | .redraw_player: 306 | ; remove old player from screen 307 | mov si, dx 308 | mov cl, [CURRENT_LEVEL_4 + si] 309 | and cl, 0xF7 ; remove player bit, by ANDing with 1111 0111 310 | mov [CURRENT_LEVEL_4 + si], cl 311 | 312 | ; add new player to screen 313 | mov ch, [CURRENT_LEVEL_4 + bx] 314 | or ch, 0x08 ; add player bit, by ORing with 0000 1000 315 | mov [CURRENT_LEVEL_4 + bx], ch 316 | 317 | ; update player position in memory 318 | mov [CURRENT_LEVEL + 2], bx 319 | 320 | .finished: 321 | 322 | popa 323 | ret 324 | 325 | 326 | ; data section: 327 | 328 | ; 0000 0000 EMPTY 329 | ; 0000 0001 SPOT 330 | ; 0000 0010 BRICK 331 | ; 0000 0011 BRICK ON SPOT 332 | ; 0000 0100 WALL 333 | ; 0000 1000 PLAYER 334 | ; 0000 1001 PLAYER ON SPOT 335 | test_level: 336 | ; this was the original level format, which was quite big: 337 | 338 | ; db 9, 7 ; width, height 339 | ; dw 32 ; playerxy 340 | ; db 4,4,4,4,4,4,0,0,0 341 | ; db 4,0,0,0,0,4,0,0,0 342 | ; db 4,0,0,2,0,4,4,0,0 343 | ; db 4,0,2,4,1,9,4,4,4 344 | ; db 4,4,0,0,3,1,2,0,4 345 | ; db 0,4,0,0,0,0,0,0,4 346 | ; db 0,4,4,4,4,4,4,4,4 347 | ; db 14, 10 ;width, height 348 | ; dw 63 ;playerxy 349 | 350 | ; when i tried to put in THIS level (from https://www.youtube.com/watch?v=fg8QImlvB-k) 351 | ; i passed the 512 byte limit... 352 | 353 | ; db 4,4,4,4,4,4,4,4,4,4,4,4,0,0 354 | ; db 4,1,1,0,0,4,0,0,0,0,0,4,4,4 355 | ; db 4,1,1,0,0,4,0,2,0,0,2,0,0,4 356 | ; db 4,1,1,0,0,4,2,4,4,4,4,0,0,4 357 | ; db 4,1,1,0,0,0,0,8,0,4,4,0,0,4 358 | ; db 4,1,1,0,0,4,0,4,0,0,2,0,4,4 359 | ; db 4,4,4,4,4,4,0,4,4,2,0,2,0,4 360 | ; db 0,0,4,0,2,0,0,2,0,2,0,2,0,4 361 | ; db 0,0,4,0,0,0,0,4,0,0,0,0,0,4 362 | ; db 0,0,4,4,4,4,4,4,4,4,4,4,4,4 363 | 364 | ; so i compressed it! high nybble first, low nybble second 365 | db 14, 10 ;width, height 366 | dw 63 ;playerxy 367 | db 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x00 368 | db 0x41, 0x10, 0x04, 0x00, 0x00, 0x04, 0x44 369 | db 0x41, 0x10, 0x04, 0x02, 0x00, 0x20, 0x04 370 | db 0x41, 0x10, 0x04, 0x24, 0x44, 0x40, 0x04 371 | db 0x41, 0x10, 0x00, 0x08, 0x04, 0x40, 0x04 372 | db 0x41, 0x10, 0x04, 0x04, 0x00, 0x20, 0x44 373 | db 0x44, 0x44, 0x44, 0x04, 0x42, 0x02, 0x04 374 | db 0x00, 0x40, 0x20, 0x02, 0x02, 0x02, 0x04 375 | db 0x00, 0x40, 0x00, 0x04, 0x00, 0x00, 0x04 376 | db 0x00, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44 377 | 378 | 379 | 380 | display_chars: db 0, 0x07 ; blank 381 | db 249, 0x07 ; spot 382 | db 4, 0x0C ; brick 383 | db 4, 0x0A ; brick on spot 384 | db 178, 0x71 ; wall 385 | db "5", 0x07 ; (no 5) 386 | db "6", 0x07 ; (no 6) 387 | db "7", 0x07 ; (no 7) 388 | db 1, 0x0F ; player 389 | db 1, 0x0F ; player on spot 390 | 391 | str_you_win: db 'YOU WIN! ',1,1,1,0 392 | 393 | times 510 - ($-$$) db 0 ; pad remaining 510 bytes with zeroes 394 | dw 0xaa55 ; magic bootloader magic - marks this 512 byte sector bootable! 395 | 396 | ; if you don't want your resulting file to be as big as a floppy, then comment the following line: 397 | 398 | times (1440 * 1024) - ($-$$) db 0 ; pad with zeroes to make a floppy-sized image -------------------------------------------------------------------------------- /patch/sokoban.fdd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nanochess/bootOS/b740e059d46e7072af836189370f0dac48c8f55c/patch/sokoban.fdd --------------------------------------------------------------------------------