├── .gitignore ├── LICENSE ├── README ├── TINYASM.EXE ├── e.bat ├── ins.c ├── test.bat ├── test ├── BASIC.IMG ├── BASIC.LST ├── BRICKS.IMG ├── FBIRD.IMG ├── INCLUDE.ASM ├── INCLUDE.COM ├── INCLUDE.LST ├── INCLUDE1.INC ├── INCLUDE2.INC ├── INCLUDE3.INC ├── INPUT0.IMG ├── INPUT1.IMG ├── INPUT2.IMG ├── INVADERS.IMG ├── OS.LST ├── PILLMAN.IMG ├── ROGUE.COM ├── ROGUE.IMG ├── ROGUE.LST ├── basic.asm ├── bricks.asm ├── doom.asm ├── fbird.asm ├── input0.asm ├── input1.asm ├── input2.asm ├── invaders.asm ├── os.asm ├── os.img ├── pillman.asm └── rogue.asm └── tinyasm.c /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | nasm 3 | nasm.exe 4 | tinyasm 5 | 6 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | ,--------.,--. 2 | '--. .--'`--',--,--, ,--. ,--.,--,--. ,---. ,--,--,--. 3 | | | ,--.| \ \ ' /' ,-. |( .-' | | 4 | | | | || || | \ ' \ '-' |.-' `)| | | | 5 | `--' `--'`--''--'.-' / `--`--'`----' `--`--`--' 6 | `---' 7 | Tinyasm 8086/8088 assembler 8 | by Oscar Toledo G. Oct/02/2019 9 | 10 | https://nanochess.org/ 11 | https://github.com/nanochess/tinyasm 12 | 13 | Tinyasm is a small assembler for 8086/8088 programs, 14 | and it can work over a real PC XT machine. It requires 15 | at least 128K of memory, maybe more. 16 | 17 | It came to my attention that several people wanted to 18 | assemble my boot sector games over real PC XT machines. 19 | 20 | Unfortunately, nasm doesn't run over 8086/8088 processors, 21 | and I couldn't find a compatible assembler! 22 | 23 | So what does a programmer when doesn't find the required 24 | tool? Start to code his own tool! 25 | 26 | It took me 3 days to start from zero and get a working 27 | assembler compatible with the nasm syntax, plus all the 28 | directives I've used in my boot sector programs. 29 | 30 | Using the same command line syntax as nasm: 31 | 32 | tinyasm -f bin rogue.asm -o rogue.img -l rogue.lst 33 | 34 | There is also the -d option for defining labels: 35 | 36 | -dCOM_FILE 37 | -dCOM_FILE=1 38 | 39 | It returns a non-zero error code when the assembled 40 | file generates errors. 41 | 42 | Thanks to tkchia for making it portable to ia16-elf-gcc, 43 | removing DeSmet C warnings, and making it to return 44 | error codes. 45 | 46 | Thanks to humbertocsjr for contributing the INCBIN 47 | command. 48 | 49 | 50 | >> DIFFERENCES BETWEEN TINYASM AND NASM >> 51 | 52 | The main difference is that Tinyasm won't insert long jumps 53 | for miscalculated conditional jumps. 54 | 55 | Labels are started with letter, underscore or period. 56 | Case is insensitive. 57 | 58 | Local labels are supported, and should start with period. 59 | The local labels final name is derived from concatenation 60 | of the last global label (not starting with period) and 61 | the local label. 62 | 63 | There is only support for 8086/8088 processors, and only 64 | are implemented the following directives: 65 | 66 | %ifdef 67 | %ifndef 68 | %if 69 | %else 70 | %endif 71 | %include 72 | incline 73 | times 74 | use16 75 | cpu 8086 76 | equ 77 | db 78 | dw 79 | 80 | The following operators are implemented: 81 | 82 | | Binary OR 83 | ^ Binary XOR 84 | & Binary AND 85 | << Shift to left 86 | >> Shift to right 87 | + Addition 88 | - Subtraction 89 | * Multiplication 90 | / Division (unsigned 16-bit) 91 | % Modulo operator 92 | (expr) Parenthesis 93 | - Unary negation 94 | 95 | The following numbers are implemented: 96 | 97 | 0b0000_0100 Binary, you can use underscore (it will be ignored) 98 | 0xabcd Hexadecimal. 99 | $0abcd Hexadecimal (after $ the first digit must be a number) 100 | 'a' Character constant. 101 | 10 Decimal. 102 | $$ Start address. 103 | $ Current address. 104 | 105 | This assembler won't win a speed test ;) because the 106 | internal implementation uses a linear search for the 107 | instruction set, and it is also implemented as a kind 108 | of regular expression subset for easier coding. 109 | 110 | 111 | >> DEBUG NOTES << 112 | 113 | If you're building boot sector games with Tinyasm, then you 114 | need the following info to load the game inside the boot 115 | sector of a floppy disk. 116 | 117 | You need to have DEBUG in your disk (included with the DOS 118 | disks). 119 | 120 | Do the following (replace filename as desired): 121 | 122 | DEBUG ROGUE.IMG 123 | Extract now your working disk and insert a blank one!!! 124 | A300 125 | MOV AX,0301 126 | MOV BX,0100 127 | MOV CX,1 128 | MOV DX,0 129 | INT 13 130 | JB 300 131 | INT 20 132 | RIP 133 | 300 134 | G 135 | Now the boot sector is replaced with the program!!! 136 | 137 | 138 | >> BUILDING THE ASSEMBLER >> 139 | 140 | You can build your own executable for Tinyasm using the 141 | C compiler Desmet C, version 3.1h available graciously at: 142 | 143 | http://www.desmet-c.com/ 144 | 145 | The compiler has many bugs and limitations, but it works for 146 | tinyasm purposes, and it's freely available. 147 | 148 | Supposedly it should support ANSI C, but I couldn't fit a 149 | standard ANSI C function definition, so I had to code again 150 | in C K&R for the first time in maybe 20 years! 151 | 152 | You can find the "e.bat" file to assemble the compiler. 153 | 154 | I provide an executable on the Git to save you some time. 155 | 156 | There are test cases in the 'test' subdirectory that are 157 | runnable with the "test.bat" file. 158 | 159 | There is a test for the full instruction set of 8086/8088 160 | (the same listing that appears in my book Programming Boot 161 | Sector Games). 162 | 163 | The test cases come from my own programs: 164 | 165 | https://github.com/nanochess/fbird 166 | https://github.com/nanochess/invaders 167 | https://github.com/nanochess/pillman 168 | https://github.com/nanochess/bootBASIC 169 | https://github.com/nanochess/bootOS 170 | https://github.com/nanochess/bootRogue 171 | 172 | 173 | >> ATTENTION << 174 | 175 | Would you like to learn 8086/8088 programming? Then you 176 | must get my new book Programming Boot Sector Games including 177 | a 8086/8088 crash course! 178 | 179 | Now available from Lulu: 180 | 181 | Soft-cover 182 | http://www.lulu.com/shop/oscar-toledo-gutierrez/programming-boot-sector-games/paperback/product-24188564.html 183 | 184 | Hard-cover 185 | http://www.lulu.com/shop/oscar-toledo-gutierrez/programming-boot-sector-games/hardcover/product-24188530.html 186 | 187 | eBook 188 | https://nanochess.org/store.html 189 | 190 | These are some of the example programs documented profusely 191 | in the book: 192 | 193 | * Guess the number. 194 | * Tic-Tac-Toe game. 195 | * Text graphics. 196 | * Mandelbrot set. 197 | * F-Bird game. 198 | * Invaders game. 199 | * Pillman game. 200 | * Toledo Atomchess. 201 | * bootBASIC language. 202 | 203 | After the success of my first book, if you need even 204 | More Boot Sector Games then you must get this book! 205 | 206 | Soft-cover 207 | http://www.lulu.com/shop/oscar-toledo-gutierrez/more-boot-sector-games/paperback/product-24462035.html 208 | 209 | Hard-cover 210 | http://www.lulu.com/shop/oscar-toledo-gutierrez/more-boot-sector-games/hardcover/product-24462029.html 211 | 212 | These are some of the example programs documented profusely 213 | in the book: 214 | 215 | * Follow the Lights 216 | * bootRogue 217 | * bricks 218 | * cubicDoom 219 | * bootOS 220 | -------------------------------------------------------------------------------- /TINYASM.EXE: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nanochess/tinyasm/84232d5845a677aa6924b51ee9123c16954cf7d4/TINYASM.EXE -------------------------------------------------------------------------------- /e.bat: -------------------------------------------------------------------------------- 1 | c88 tinyasm 2 | c88 ins 3 | bind tinyasm ins 4 | -------------------------------------------------------------------------------- /ins.c: -------------------------------------------------------------------------------- 1 | /* 2 | ** TinyASM - 8086/8088 assembler for DOS 3 | ** 4 | ** Instruction set. 5 | ** 6 | ** by Oscar Toledo G. 7 | ** 8 | ** Creation date: Oct/01/2019. 9 | */ 10 | 11 | #include 12 | 13 | /* 14 | ** This should have been 3 entries per line but DeSmet C only allows 500 strings per module. 15 | */ 16 | 17 | /* 18 | ** Notice some instructions are sorted by less byte usage first. 19 | */ 20 | char *instruction_set[] = { 21 | "ADD\0%d8,%r8\0x00 %d8%r8%d8", 22 | "ADD\0%d16,%r16\0x01 %d16%r16%d16", 23 | "ADD\0%r8,%d8\0x02 %d8%r8%d8", 24 | "ADD\0%r16,%d16\0x03 %d16%r16%d16", 25 | "ADD\0AL,%i8\0x04 %i8", 26 | "ADD\0AX,%i16\0x05 %i16", 27 | "PUSH\0ES\0x06", 28 | "POP\0ES\0x07", 29 | "OR\0%d8,%r8\0x08 %d8%r8%d8", 30 | "OR\0%d16,%r16\0x09 %d16%r16%d16", 31 | "OR\0%r8,%d8\0x0a %d8%r8%d8", 32 | "OR\0%r16,%d16\0x0b %d16%r16%d16", 33 | "OR\0AL,%i8\0x0c %i8", 34 | "OR\0AX,%i16\0x0d %i16", 35 | "PUSH\0CS\0x0e", 36 | "ADC\0%d8,%r8\0x10 %d8%r8%d8", 37 | "ADC\0%d16,%r16\0x11 %d16%r16%d16", 38 | "ADC\0%r8,%d8\0x12 %d8%r8%d8", 39 | "ADC\0%r16,%d16\0x13 %d16%r16%d16", 40 | "ADC\0AL,%i8\0x14 %i8", 41 | "ADC\0AX,%i16\0x15 %i16", 42 | "PUSH\0SS\0x16", 43 | "POP\0SS\0x17", 44 | "SBB\0%d8,%r8\0x18 %d8%r8%d8", 45 | "SBB\0%d16,%r16\0x19 %d16%r16%d16", 46 | "SBB\0%r8,%d8\0x1a %d8%r8%d8", 47 | "SBB\0%r16,%d16\0x1b %d16%r16%d16", 48 | "SBB\0AL,%i8\0x1c %i8", 49 | "SBB\0AX,%i16\0x1d %i16", 50 | "PUSH\0DS\0x1e", 51 | "POP\0DS\0x1f", 52 | "AND\0%d8,%r8\0x20 %d8%r8%d8", 53 | "AND\0%d16,%r16\0x21 %d16%r16%d16", 54 | "AND\0%r8,%d8\0x22 %d8%r8%d8", 55 | "AND\0%r16,%d16\0x23 %d16%r16%d16", 56 | "AND\0AL,%i8\0x24 %i8", 57 | "AND\0AX,%i16\0x25 %i16", 58 | "ES\0\0x26", 59 | "DAA\0\0x27", 60 | "SUB\0%d8,%r8\0x28 %d8%r8%d8", 61 | "SUB\0%d16,%r16\0x29 %d16%r16%d16", 62 | "SUB\0%r8,%d8\0x2a %d8%r8%d8", 63 | "SUB\0%r16,%d16\0x2b %d16%r16%d16", 64 | "SUB\0AL,%i8\0x2c %i8", 65 | "SUB\0AX,%i16\0x2d %i16", 66 | "CS\0\0x2e", 67 | "DAS\0\0x2f", 68 | "XOR\0%d8,%r8\0x30 %d8%r8%d8", 69 | "XOR\0%d16,%r16\0x31 %d16%r16%d16", 70 | "XOR\0%r8,%d8\0x32 %d8%r8%d8", 71 | "XOR\0%r16,%d16\0x33 %d16%r16%d16", 72 | "XOR\0AL,%i8\0x34 %i8", 73 | "XOR\0AX,%i16\0x35 %i16", 74 | "SS\0\0x36", 75 | "AAA\0\0x37", 76 | "CMP\0%d8,%r8\0x38 %d8%r8%d8", 77 | "CMP\0%d16,%r16\0x39 %d16%r16%d16", 78 | "CMP\0%r8,%d8\0x3a %d8%r8%d8", 79 | "CMP\0%r16,%d16\0x3b %d16%r16%d16", 80 | "CMP\0AL,%i8\0x3c %i8", 81 | "CMP\0AX,%i16\0x3d %i16", 82 | "DS\0\0x3e", 83 | "AAS\0\0x3f", 84 | "INC\0%r16\0b01000%r16", 85 | "DEC\0%r16\0b01001%r16", 86 | "PUSH\0%r16\0b01010%r16", 87 | "POP\0%r16\0b01011%r16", 88 | "JO\0%a8\0x70 %a8", 89 | "JNO\0%a8\0x71 %a8", 90 | "JB\0%a8\0x72 %a8", 91 | "JC\0%a8\0x72 %a8", 92 | "JNB\0%a8\0x73 %a8", 93 | "JNC\0%a8\0x73 %a8", 94 | "JZ\0%a8\0x74 %a8", 95 | "JNZ\0%a8\0x75 %a8", 96 | "JE\0%a8\0x74 %a8", 97 | "JNE\0%a8\0x75 %a8", 98 | "JBE\0%a8\0x76 %a8", 99 | "JA\0%a8\0x77 %a8", 100 | "JS\0%a8\0x78 %a8", 101 | "JNS\0%a8\0x79 %a8", 102 | "JPE\0%a8\0x7a %a8", 103 | "JPO\0%a8\0x7b %a8", 104 | "JL\0%a8\0x7C %a8", 105 | "JGE\0%a8\0x7D %a8", 106 | "JLE\0%a8\0x7E %a8", 107 | "JG\0%a8\0x7F %a8", 108 | "ADD\0%d16,%s8\0x83 %d16000%d16 %s8", 109 | "OR\0%d16,%s8\0x83 %d16001%d16 %s8", 110 | "ADC\0%d16,%s8\0x83 %d16010%d16 %s8", 111 | "SBB\0%d16,%s8\0x83 %d16011%d16 %s8", 112 | "AND\0%d16,%s8\0x83 %d16100%d16 %s8", 113 | "SUB\0%d16,%s8\0x83 %d16101%d16 %s8", 114 | "XOR\0%d16,%s8\0x83 %d16110%d16 %s8", 115 | "CMP\0%d16,%s8\0x83 %d16111%d16 %s8", 116 | "ADD\0%d8,%i8\0x80 %d8000%d8 %i8", 117 | "OR\0%d8,%i8\0x80 %d8001%d8 %i8", 118 | "ADC\0%d8,%i8\0x80 %d8010%d8 %i8", 119 | "SBB\0%d8,%i8\0x80 %d8011%d8 %i8", 120 | "AND\0%d8,%i8\0x80 %d8100%d8 %i8", 121 | "SUB\0%d8,%i8\0x80 %d8101%d8 %i8", 122 | "XOR\0%d8,%i8\0x80 %d8110%d8 %i8", 123 | "CMP\0%d8,%i8\0x80 %d8111%d8 %i8", 124 | "ADD\0%d16,%i16\0x81 %d16000%d16 %i16", 125 | "OR\0%d16,%i16\0x81 %d16001%d16 %i16", 126 | "ADC\0%d16,%i16\0x81 %d16010%d16 %i16", 127 | "SBB\0%d16,%i16\0x81 %d16011%d16 %i16", 128 | "AND\0%d16,%i16\0x81 %d16100%d16 %i16", 129 | "SUB\0%d16,%i16\0x81 %d16101%d16 %i16", 130 | "XOR\0%d16,%i16\0x81 %d16110%d16 %i16", 131 | "CMP\0%d16,%i16\0x81 %d16111%d16 %i16", 132 | "TEST\0%d8,%r8\0x84 %d8%r8%d8", 133 | "TEST\0%r8,%d8\0x84 %d8%r8%d8", 134 | "TEST\0%d16,%r16\0x85 %d16%r16%d16", 135 | "TEST\0%r16,%d16\0x85 %d16%r16%d16", 136 | 137 | "MOV\0AL,[%i16]\0xa0 %i16", 138 | "MOV\0AX,[%i16]\0xa1 %i16", 139 | "MOV\0[%i16],AL\0xa2 %i16", 140 | "MOV\0[%i16],AX\0xa3 %i16", 141 | "MOV\0%d8,%r8\0x88 %d8%r8%d8", 142 | "MOV\0%d16,%r16\0x89 %d16%r16%d16", 143 | "MOV\0%r8,%d8\0x8a %d8%r8%d8", 144 | "MOV\0%r16,%d16\0x8b %d16%r16%d16", 145 | 146 | "MOV\0%d16,ES\0x8c %d16000%d16", 147 | "MOV\0%d16,CS\0x8c %d16001%d16", 148 | "MOV\0%d16,SS\0x8c %d16010%d16", 149 | "MOV\0%d16,DS\0x8c %d16011%d16", 150 | "LEA\0%r16,%d16\0x8d %d16%r16%d16", 151 | "MOV\0ES,%d16\0x8e %d16000%d16", 152 | "MOV\0CS,%d16\0x8e %d16001%d16", 153 | "MOV\0SS,%d16\0x8e %d16010%d16", 154 | "MOV\0DS,%d16\0x8e %d16011%d16", 155 | "POP\0%d16\0x8f %d16000%d16", 156 | "NOP\0\0x90", 157 | "XCHG\0AX,%r16\0b10010%r16", 158 | "XCHG\0%r16,AX\0b10010%r16", 159 | "XCHG\0%d8,%r8\0x86 %d8%r8%d8", 160 | "XCHG\0%r8,%d8\0x86 %d8%r8%d8", 161 | "XCHG\0%d16,%r16\0x87 %d16%r16%d16", 162 | "XCHG\0%r16,%d16\0x87 %d16%r16%d16", 163 | "CBW\0\0x98", 164 | "CWD\0\0x99", 165 | "WAIT\0\0x9b", 166 | "PUSHF\0\0x9c", 167 | "POPF\0\0x9d", 168 | "SAHF\0\0x9e", 169 | "LAHF\0\0x9f", 170 | "MOVSB\0\0xa4", 171 | "MOVSW\0\0xa5", 172 | "CMPSB\0\0xa6", 173 | "CMPSW\0\0xa7", 174 | "TEST\0AL,%i8\0xa8 %i8", 175 | "TEST\0AX,%i16\0xa9 %i16", 176 | "STOSB\0\0xaa", 177 | "STOSW\0\0xab", 178 | "LODSB\0\0xac", 179 | "LODSW\0\0xad", 180 | "SCASB\0\0xae", 181 | "SCASW\0\0xaf", 182 | "MOV\0%r8,%i8\0b10110%r8 %i8", 183 | "MOV\0%r16,%i16\0b10111%r16 %i16", 184 | "RET\0%i16\0xc2 %i16", 185 | "RET\0\0xc3", 186 | "LES\0%r16,%d16\0b11000100 %d16%r16%d16", 187 | "LDS\0%r16,%d16\0b11000101 %d16%r16%d16", 188 | "MOV\0%db8,%i8\0b11000110 %d8000%d8 %i8", 189 | "MOV\0%dw16,%i16\0b11000111 %d16000%d16 %i16", 190 | "RETF\0%i16\0xca %i16", 191 | "RETF\0\0xcb", 192 | "INT3\0\0xcc", 193 | "INT\0%i8\0xcd %i8", 194 | "INTO\0\0xce", 195 | "IRET\0\0xcf", 196 | "ROL\0%d8,1\0xd0 %d8000%d8", 197 | "ROR\0%d8,1\0xd0 %d8001%d8", 198 | "RCL\0%d8,1\0xd0 %d8010%d8", 199 | "RCR\0%d8,1\0xd0 %d8011%d8", 200 | "SHL\0%d8,1\0xd0 %d8100%d8", 201 | "SHR\0%d8,1\0xd0 %d8101%d8", 202 | "SAR\0%d8,1\0xd0 %d8111%d8", 203 | "ROL\0%d16,1\0xd1 %d16000%d16", 204 | "ROR\0%d16,1\0xd1 %d16001%d16", 205 | "RCL\0%d16,1\0xd1 %d16010%d16", 206 | "RCR\0%d16,1\0xd1 %d16011%d16", 207 | "SHL\0%d16,1\0xd1 %d16100%d16", 208 | "SHR\0%d16,1\0xd1 %d16101%d16", 209 | "SAR\0%d16,1\0xd1 %d16111%d16", 210 | "ROL\0%d8,CL\0xd2 %d8000%d8", 211 | "ROR\0%d8,CL\0xd2 %d8001%d8", 212 | "RCL\0%d8,CL\0xd2 %d8010%d8", 213 | "RCR\0%d8,CL\0xd2 %d8011%d8", 214 | "SHL\0%d8,CL\0xd2 %d8100%d8", 215 | "SHR\0%d8,CL\0xd2 %d8101%d8", 216 | "SAR\0%d8,CL\0xd2 %d8111%d8", 217 | "ROL\0%d16,CL\0xd3 %d16000%d16", 218 | "ROR\0%d16,CL\0xd3 %d16001%d16", 219 | "RCL\0%d16,CL\0xd3 %d16010%d16", 220 | "RCR\0%d16,CL\0xd3 %d16011%d16", 221 | "SHL\0%d16,CL\0xd3 %d16100%d16", 222 | "SHR\0%d16,CL\0xd3 %d16101%d16", 223 | "SAR\0%d16,CL\0xd3 %d16111%d16", 224 | "AAM\0\0xd4 x0a", 225 | "AAD\0\0xd5 x0a", 226 | "XLAT\0\0xd7", 227 | "LOOPNZ\0%a8\0xe0 %a8", 228 | "LOOPNE\0%a8\0xe0 %a8", 229 | "LOOPZ\0%a8\0xe1 %a8", 230 | "LOOPE\0%a8\0xe1 %a8", 231 | "LOOP\0%a8\0xe2 %a8", 232 | "JCXZ\0%a8\0xe3 %a8", 233 | "IN\0AL,DX\0xec", 234 | "IN\0AX,DX\0xed", 235 | "OUT\0DX,AL\0xee", 236 | "OUT\0DX,AX\0xef", 237 | "IN\0AL,%i8\0xe4 %i8", 238 | "IN\0AX,%i8\0xe5 %i8", 239 | "OUT\0%i8,AL\0xe6 %i8", 240 | "OUT\0%i8,AX\0xe7 %i8", 241 | "CALL\0FAR %d16\0xff %d16011%d16", 242 | "JMP\0FAR %d16\0xff %d16101%d16", 243 | "CALL\0%f32\0x9a %f32", 244 | "JMP\0%f32\0xea %f32", 245 | "CALL\0%d16\0xff %d16010%d16", 246 | "JMP\0%d16\0xff %d16100%d16", 247 | "JMP\0%a8\0xeb %a8", 248 | "JMP\0%a16\0xe9 %a16", 249 | "CALL\0%a16\0xe8 %a16", 250 | "LOCK\0\0xf0", 251 | "REPNZ\0\0xf2", 252 | "REPNE\0\0xf2", 253 | "REPZ\0\0xf3", 254 | "REPE\0\0xf3", 255 | "REP\0\0xf3", 256 | "HLT\0\0xf4", 257 | "CMC\0\0xf5", 258 | "TEST\0%db8,%i8\0xf6 %d8000%d8 %i8", 259 | "NOT\0%db8\0xf6 %d8010%d8", 260 | "NEG\0%db8\0xf6 %d8011%d8", 261 | "MUL\0%db8\0xf6 %d8100%d8", 262 | "IMUL\0%db8\0xf6 %d8101%d8", 263 | "DIV\0%db8\0xf6 %d8110%d8", 264 | "IDIV\0%db8\0xf6 %d8111%d8", 265 | "TEST\0%dw16,%i16\0xf7 %d8000%d8 %i16", 266 | "NOT\0%dw16\0xf7 %d8010%d8", 267 | "NEG\0%dw16\0xf7 %d8011%d8", 268 | "MUL\0%dw16\0xf7 %d8100%d8", 269 | "IMUL\0%dw16\0xf7 %d8101%d8", 270 | "DIV\0%dw16\0xf7 %d8110%d8", 271 | "IDIV\0%dw16\0xf7 %d8111%d8", 272 | "CLC\0\0xf8", 273 | "STC\0\0xf9", 274 | "CLI\0\0xfa", 275 | "STI\0\0xfb", 276 | "CLD\0\0xfc", 277 | "STD\0\0xfd", 278 | "INC\0%db8\0xfe %d8000%d8", 279 | "DEC\0%db8\0xfe %d8001%d8", 280 | "INC\0%dw16\0xff %d16000%d16", 281 | "DEC\0%dw16\0xff %d16001%d16", 282 | "PUSH\0%d16\0xff %d16110%d16", 283 | NULL,NULL,NULL 284 | }; 285 | 286 | -------------------------------------------------------------------------------- /test.bat: -------------------------------------------------------------------------------- 1 | tinyasm -f bin test\input0.asm -o test\input0.img 2 | tinyasm -f bin test\input1.asm -o test\input1.img 3 | tinyasm -f bin test\input2.asm -o test\input2.img 4 | tinyasm -f bin test\fbird.asm -o test\fbird.img 5 | tinyasm -f bin test\invaders.asm -o test\invaders.img 6 | tinyasm -f bin test\pillman.asm -o test\pillman.img 7 | tinyasm -f bin test\rogue.asm -o test\rogue.img -l test\rogue.lst 8 | tinyasm -f bin test\rogue.asm -o test\rogue.com -l test\rogue.lst -dCOM_FILE=1 9 | tinyasm -f bin test\os.asm -o test\os.img -l test\os.lst 10 | tinyasm -f bin test\basic.asm -o test\basic.img -l test\basic.lst 11 | tinyasm -f bin test\include.asm -o test\include.com -l test\include.lst 12 | tinyasm -f bin test\bricks.asm -o test\bricks.img -l test\bricks.lst 13 | tinyasm -f bin test\doom.asm -o test\doom.img -l test\doom.lst 14 | -------------------------------------------------------------------------------- /test/BASIC.IMG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nanochess/tinyasm/84232d5845a677aa6924b51ee9123c16954cf7d4/test/BASIC.IMG -------------------------------------------------------------------------------- /test/BASIC.LST: -------------------------------------------------------------------------------- 1 | 00001 ; 2 | 00002 ; BOOTBASIC INTERPRETER IN 512 BYTES (BOOT SECTOR) 3 | 00003 ; 4 | 00004 ; BY OSCAR TOLEDO G. 5 | 00005 ; HTTP://NANOCHESS.ORG/ 6 | 00006 ; 7 | 00007 ; (C) COPYRIGHT 2019 OSCAR TOLEDO G. 8 | 00008 ; 9 | 00009 ; CREATION DATE: JUL/19/2019. 10PM TO 12AM. 10 | 00010 ; REVISION DATE: JUL/20/2019. 10AM TO 2PM. 11 | 00011 ; ADDED ASSIGNMENT STATEMENT. LIST NOW 12 | 00012 ; WORKS. RUN/GOTO NOW WORKS. ADDED 13 | 00013 ; SYSTEM AND NEW. 14 | 00014 ; REVISION DATE: JUL/22/2019. BOOT IMAGE NOW INCLUDES 'system' 15 | 00015 ; STATEMENT. 16 | 00016 ; 17 | 00017 18 | 00018 ; 19 | 00019 ; USER'S MANUAL: 20 | 21 | 00020 ; 22 | 00021 ; LINE ENTRY IS DONE WITH KEYBOARD, FINISH THE LINE WITH ENTER. 23 | 00022 ; ONLY 19 CHARACTERS PER LINE AS MAXIMUM. 24 | 00023 ; 25 | 00024 ; BACKSPACE CAN BE USED, DON't be fooled by the fact 26 | 27 | 00025 ; THAT SCREEN ISN't deleted (it'S ALL RIGHT IN THE BUFFER). 28 | 00026 ; 29 | 00027 ; ALL STATEMENTS MUST BE IN LOWERCASE. 30 | 00028 ; 31 | 00029 ; LINE NUMBERS CAN BE 1 TO 999. 32 | 00030 ; 33 | 00031 ; 26 VARIABLES ARE AVAILABLE (A-Z) 34 | 00032 ; 35 | 00033 ; NUMBERS (0-65535) CAN BE ENTERED AND DISPLAY AS UNSIGNED. 36 | 00034 ; 37 | 00035 ; TO ENTER NEW PROGRAM LINES: 38 | 00036 ; 10 PRINT "Hello, world!" 39 | 00037 ; 40 | 00038 ; TO ERASE PROGRAM LINES: 41 | 00039 ; 10 42 | 00040 ; 43 | 00041 ; TO TEST STATEMENTS DIRECTLY (INTERACTIVE SYNTAX): 44 | 00042 ; PRINT "Hello, world!" 45 | 00043 ; 46 | 00044 ; TO ERASE THE CURRENT PROGRAM: 47 | 00045 ; NEW 48 | 00046 ; 49 | 00047 ; TO RUN THE CURRENT PROGRAM: 50 | 00048 ; RUN 51 | 00049 ; 52 | 00050 ; TO LIST THE CURRENT PROGRAM: 53 | 00051 ; LIST 54 | 00052 ; 55 | 00053 ; TO EXIT TO COMMAND-LINE: 56 | 00054 ; SYSTEM 57 | 00055 ; 58 | 00056 ; STATEMENTS: 59 | 00057 ; VAR=EXPR ASSIGN EXPR VALUE TO VAR (A-Z) 60 | 00058 ; 61 | 00059 ; PRINT EXPR PRINT EXPRESSION VALUE, NEW LINE 62 | 00060 ; PRINT EXPR; PRINT EXPRESSION VALUE, CONTINUE 63 | 00061 ; PRINT "hello" PRINT STRING, NEW LINE 64 | 00062 ; PRINT "hello"; PRINT STRING, CONTINUE 65 | 00063 ; 66 | 00064 ; INPUT VAR INPUT VALUE INTO VARIABLE (A-Z) 67 | 00065 ; 68 | 00066 ; GOTO EXPR GOTO TO INDICATED LINE IN PROGRAM 69 | 00067 ; 70 | 00068 ; IF EXPR1 GOTO EXPR2 71 | 00069 ; IF EXPR1 IS NON-ZERO THEN GO TO LINE, 72 | 00070 ; ELSE GO TO FOLLOWING LINE. 73 | 00071 ; 74 | 00072 ; EXAMPLES OF IF: 75 | 00073 ; 76 | 00074 ; IF C-5 GOTO 20 IF C ISN't 5, go to line 20 77 | 78 | 00075 ; 79 | 00076 ; EXPRESSIONS: 80 | 00077 ; 81 | 00078 ; THE OPERATORS +, -, / AND * ARE AVAILABLE WITH 82 | 00079 ; COMMON PRECEDENCE RULES AND SIGNED OPERATION. 83 | 00080 ; 84 | 00081 ; YOU CAN ALSO USE PARENTHESES: 85 | 00082 ; 86 | 00083 ; 5+6*(10/2) 87 | 00084 ; 88 | 00085 ; VARIABLES AND NUMBERS CAN BE USED IN EXPRESSIONS. 89 | 00086 ; 90 | 00087 ; SAMPLE PROGRAM (COUNTING 1 TO 10): 91 | 00088 ; 92 | 00089 ; 10 A=1 93 | 00090 ; 20 PRINT A 94 | 00091 ; 30 A=A+1 95 | 00092 ; 40 IF A-11 GOTO 20 96 | 00093 ; 97 | 00094 ; SAMPLE PROGRAM (PASCAL's triangle, each number is the sum 98 | 99 | 00095 ; OF THE TWO OVER IT): 100 | 00096 ; 101 | 00097 ; 10 INPUT N 102 | 00098 ; 20 I=1 103 | 00099 ; 30 C=1 104 | 00100 ; 40 J=0 105 | 00101 ; 50 T=N-I 106 | 00102 ; 60 IF J-T GOTO 80 107 | 00103 ; 70 GOTO 110 108 | 00104 ; 80 PRINT " "; 109 | 00105 ; 90 J=J+1 110 | 00106 ; 100 GOTO 50 111 | 00107 ; 110 K=1 112 | 00108 ; 120 IF K-I-1 GOTO 140 113 | 00109 ; 130 GOTO 190 114 | 00110 ; 140 PRINT C; 115 | 00111 ; 150 C=C*(I-K)/K 116 | 00112 ; 160 PRINT " "; 117 | 00113 ; 170 K=K+1 118 | 00114 ; 180 GOTO 120 119 | 00115 ; 190 PRINT 120 | 00116 ; 200 I=I+1 121 | 00117 ; 210 IF I-N-1 GOTO 30 122 | 00118 ; 123 | 00119 124 | 00120 CPU 8086 125 | 00121 126 | 00122 %IFNDEF COM_FILE ; IF NOT DEFINED CREATE A BOOT SECTOR 127 | 00123 COM_FILE: EQU 0 128 | 00124 %ENDIF 129 | 00125 130 | 00126 %IF COM_FILE 131 | 00127 ORG 0X0100 132 | 00128 %ELSE 133 | 0006 00129 ORG 0X7C00 134 | 0006 00130 %ENDIF 135 | 0006 00131 136 | 0006 00132 VARS: EQU 0X7E00 ; VARIABLES (MULTIPLE OF 256) 137 | 0006 00133 RUNNING: EQU 0X7E7E ; RUNNING STATUS 138 | 0006 00134 LINE: EQU 0X7E80 ; LINE INPUT 139 | 0006 00135 PROGRAM: EQU 0X7F00 ; PROGRAM ADDRESS 140 | 0006 00136 STACK: EQU 0XFF00 ; STACK ADDRESS 141 | 0006 00137 MAX_LINE: EQU 1000 ; FIRST UNAVAILABLE LINE NUMBER 142 | 0006 00138 MAX_LENGTH: EQU 20 ; MAXIMUM LENGTH OF LINE 143 | 0006 00139 MAX_SIZE: EQU MAX_LINE*MAX_LENGTH ; MAX. PROGRAM SIZE 144 | 0006 00140 145 | 7C00 00141 START: 146 | 7C00 00142 %IF COM_FILE 147 | 7C00 00143 %ELSE 148 | 7C00 0E 00144 PUSH CS ; FOR BOOT SECTOR 149 | 7C01 0E 00145 PUSH CS ; IT NEEDS TO SETUP 150 | 7C02 0E 00146 PUSH CS ; DS, ES AND SS. 151 | 7C03 1F 00147 POP DS 152 | 7C04 07 00148 POP ES 153 | 7C05 17 00149 POP SS 154 | 7C05 00150 %ENDIF 155 | 7C06 FC 00151 CLD ; CLEAR DIRECTION FLAG 156 | 7C07 BF007F 00152 MOV DI,PROGRAM ; POINT TO PROGRAM 157 | 7C0A B00D 00153 MOV AL,0X0D ; FILL WITH CR 158 | 7C0C B9204E 00154 MOV CX,MAX_SIZE ; MAX. PROGRAM SIZE 159 | 7C0F F3AA 00155 REP STOSB ; INITIALIZE 160 | 7C0F 00156 161 | 7C0F 00157 ; 162 | 7C0F 00158 ; MAIN LOOP 163 | 7C0F 00159 ; 164 | 7C11 00160 MAIN_LOOP: 165 | 7C11 BC00FF 00161 MOV SP,STACK ; REINITIALIZE STACK POINTER 166 | 7C14 B8117C 00162 MOV AX,MAIN_LOOP 167 | 7C17 50 00163 PUSH AX 168 | 7C18 31C0 00164 XOR AX,AX ; MARK AS INTERACTIVE 169 | 7C1A A37E7E 00165 MOV [RUNNING],AX 170 | 7C1D B03E 00166 MOV AL,'>' ; SHOW PROMPT 171 | 7C1F E85001 00167 CALL INPUT_LINE ; ACCEPT LINE 172 | 7C22 E80401 00168 CALL INPUT_NUMBER ; GET NUMBER 173 | 7C25 09C0 00169 OR AX,AX ; NO NUMBER OR ZERO? 174 | 7C27 740E 00170 JE STATEMENT ; YES, JUMP 175 | 7C29 E83D01 00171 CALL FIND_LINE ; FIND THE LINE 176 | 7C2C 97 00172 XCHG AX,DI 177 | 7C2C 00173 ; MOV CX,MAX_LENGTH ; CX LOADED WITH THIS VALUE IN 'find_line' 178 | 7C2D F3A4 00174 REP MOVSB ; COPY ENTERED LINE INTO PROGRAM 179 | 7C2F C3 00175 RET 180 | 7C2F 00176 181 | 7C2F 00177 ; 182 | 7C2F 00178 ; HANDLE 'if' STATEMENT 183 | 7C2F 00179 ; 184 | 7C30 00180 IF_STATEMENT: 185 | 7C30 E86D00 00181 CALL EXPR ; PROCESS EXPRESSION 186 | 7C33 09C0 00182 OR AX,AX ; IS IT ZERO? 187 | 7C35 7459 00183 JE F6 ; YES, RETURN (IGNORE IF) 188 | 7C37 00184 STATEMENT: 189 | 7C37 E8D200 00185 CALL SPACES ; AVOID SPACES 190 | 7C3A 803C0D 00186 CMP BYTE [SI],0X0D ; EMPTY LINE? 191 | 7C3D 7451 00187 JE F6 ; YES, RETURN 192 | 7C3F BFC37D 00188 MOV DI,STATEMENTS ; POINT TO STATEMENTS LIST 193 | 7C42 8A05 00189 F5: MOV AL,[DI] ; READ LENGTH OF THE STRING 194 | 7C44 47 00190 INC DI ; AVOID LENGTH BYTE 195 | 7C45 25FF00 00191 AND AX,0X00FF ; IS IT ZERO? 196 | 7C48 7413 00192 JE F4 ; YES, JUMP 197 | 7C4A 91 00193 XCHG AX,CX 198 | 7C4B 56 00194 PUSH SI ; SAVE CURRENT POSITION 199 | 7C4C F3A6 00195 F16: REP CMPSB ; COMPARE STATEMENT 200 | 7C4E 7506 00196 JNE F3 ; EQUAL? NO, JUMP 201 | 7C50 58 00197 POP AX 202 | 7C51 E8B800 00198 CALL SPACES ; AVOID SPACES 203 | 7C54 FF25 00199 JMP WORD [DI] ; JUMP TO PROCESS STATEMENT 204 | 7C54 00200 205 | 7C56 01CF 00201 F3: ADD DI,CX ; ADVANCE THE LIST POINTER 206 | 7C58 47 00202 INC DI ; AVOID THE ADDRESS 207 | 7C59 47 00203 INC DI 208 | 7C5A 5E 00204 POP SI 209 | 7C5B EBE5 00205 JMP F5 ; COMPARE ANOTHER STATEMENT 210 | 7C5B 00206 211 | 7C5D E8A500 00207 F4: CALL GET_VARIABLE ; TRY VARIABLE 212 | 7C60 50 00208 PUSH AX ; SAVE ADDRESS 213 | 7C61 AC 00209 LODSB ; READ A LINE LETTER 214 | 7C62 3C3D 00210 CMP AL,'=' ; IS IT ASSIGNMENT '=' ? 215 | 7C64 7434 00211 JE ASSIGNMENT ; YES, JUMP TO ASSIGNMENT. 216 | 7C64 00212 217 | 7C64 00213 ; 218 | 7C64 00214 ; AN ERROR HAPPENED 219 | 7C64 00215 ; 220 | 7C66 00216 ERROR: 221 | 7C66 BE6E7C 00217 MOV SI,ERROR_MESSAGE 222 | 7C69 E82601 00218 CALL PRINT_2 ; SHOW ERROR MESSAGE 223 | 7C6C EBA3 00219 JMP MAIN_LOOP ; EXIT TO MAIN LOOP 224 | 7C6C 00220 225 | 7C6E 00221 ERROR_MESSAGE: 226 | 7C6E 4023210D 00222 DB "@#!",0X0D ; GUESS THE WORDS :P 227 | 7C6E 00223 228 | 7C6E 00224 ; 229 | 7C6E 00225 ; HANDLE 'list' STATEMENT 230 | 7C6E 00226 ; 231 | 7C72 00227 LIST_STATEMENT: 232 | 7C72 31C0 00228 XOR AX,AX ; START FROM LINE ZERO 233 | 7C74 50 00229 F29: PUSH AX 234 | 7C75 E8F100 00230 CALL FIND_LINE ; FIND PROGRAM LINE 235 | 7C78 96 00231 XCHG AX,SI 236 | 7C79 803C0D 00232 CMP BYTE [SI],0X0D ; EMPTY LINE? 237 | 7C7C 740B 00233 JE F30 ; YES, JUMP 238 | 7C7E 58 00234 POP AX 239 | 7C7F 50 00235 PUSH AX 240 | 7C80 E89100 00236 CALL OUTPUT_NUMBER ; SHOW LINE NUMBER 241 | 7C83 AC 00237 F32: LODSB ; SHOW LINE CONTENTS 242 | 7C84 E82701 00238 CALL OUTPUT 243 | 7C87 75FA 00239 JNE F32 ; JUMP IF IT WASN't 0x0d (CR) 244 | 245 | 7C89 58 00240 F30: POP AX 246 | 7C8A 40 00241 INC AX ; GO TO NEXT LINE 247 | 7C8B 3DE803 00242 CMP AX,MAX_LINE ; FINISHED? 248 | 7C8E 75E4 00243 JNE F29 ; NO, CONTINUE 249 | 7C90 00244 F6: 250 | 7C90 C3 00245 RET 251 | 7C90 00246 252 | 7C90 00247 ; 253 | 7C90 00248 ; HANDLE 'input' STATEMENT 254 | 7C90 00249 ; 255 | 7C91 00250 INPUT_STATEMENT: 256 | 7C91 E87100 00251 CALL GET_VARIABLE ; GET VARIABLE ADDRESS 257 | 7C94 50 00252 PUSH AX ; SAVE IT 258 | 7C95 B03F 00253 MOV AL,'?' ; PROMPT 259 | 7C97 E8D800 00254 CALL INPUT_LINE ; WAIT FOR LINE 260 | 7C97 00255 ; 261 | 7C97 00256 ; SECOND PART OF THE ASSIGNMENT STATEMENT 262 | 7C97 00257 ; 263 | 7C9A 00258 ASSIGNMENT: 264 | 7C9A E80300 00259 CALL EXPR ; PROCESS EXPRESSION 265 | 7C9D 5F 00260 POP DI 266 | 7C9E AB 00261 STOSW ; SAVE ONTO VARIABLE 267 | 7C9F C3 00262 RET 268 | 7C9F 00263 269 | 7C9F 00264 ; 270 | 7C9F 00265 ; HANDLE AN EXPRESSION. 271 | 7C9F 00266 ; FIRST TIER: ADDITION & SUBTRACTION. 272 | 7C9F 00267 ; 273 | 7CA0 00268 EXPR: 274 | 7CA0 E81C00 00269 CALL EXPR1 ; CALL SECOND TIER 275 | 7CA3 803C2D 00270 F20: CMP BYTE [SI],'-' ; SUBTRACTION OPERATOR? 276 | 7CA6 740E 00271 JE F19 ; YES, JUMP 277 | 7CA8 803C2B 00272 CMP BYTE [SI],'+' ; ADDITION OPERATOR? 278 | 7CAB 75E3 00273 JNE F6 ; NO, RETURN 279 | 7CAD 50 00274 PUSH AX 280 | 7CAE E80D00 00275 CALL EXPR1_2 ; CALL SECOND TIER 281 | 7CB1 59 00276 F15: POP CX 282 | 7CB2 01C8 00277 ADD AX,CX ; ADDITION 283 | 7CB4 EBED 00278 JMP F20 ; FIND MORE OPERATORS 284 | 7CB4 00279 285 | 7CB6 00280 F19: 286 | 7CB6 50 00281 PUSH AX 287 | 7CB7 E80400 00282 CALL EXPR1_2 ; CALL SECOND TIER 288 | 7CBA F7D8 00283 NEG AX ; NEGATE IT (A - B CONVERTED TO A + -B) 289 | 7CBC EBF3 00284 JMP F15 290 | 7CBC 00285 291 | 7CBC 00286 ; 292 | 7CBC 00287 ; HANDLE AN EXPRESSION. 293 | 7CBC 00288 ; SECOND TIER: DIVISION & MULTIPLICATION. 294 | 7CBC 00289 ; 295 | 7CBE 00290 EXPR1_2: 296 | 7CBE 46 00291 INC SI ; AVOID OPERATOR 297 | 7CBF 00292 EXPR1: 298 | 7CBF E81F00 00293 CALL EXPR2 ; CALL THIRD TIER 299 | 7CC2 803C2F 00294 F21: CMP BYTE [SI],'/' ; DIVISION OPERATOR? 300 | 7CC5 740E 00295 JE F23 ; YES, JUMP 301 | 7CC7 803C2A 00296 CMP BYTE [SI],'*' ; MULTIPLICATION OPERATOR? 302 | 7CCA 75C4 00297 JNE F6 ; NO, RETURN 303 | 7CCA 00298 304 | 7CCC 50 00299 PUSH AX 305 | 7CCD E81000 00300 CALL EXPR2_2 ; CALL THIRD TIER 306 | 7CD0 59 00301 POP CX 307 | 7CD1 F7E9 00302 IMUL CX ; MULTIPLICATION 308 | 7CD3 EBED 00303 JMP F21 ; FIND MORE OPERATORS 309 | 7CD3 00304 310 | 7CD5 00305 F23: 311 | 7CD5 50 00306 PUSH AX 312 | 7CD6 E80700 00307 CALL EXPR2_2 ; CALL THIRD TIER 313 | 7CD9 59 00308 POP CX 314 | 7CDA 91 00309 XCHG AX,CX 315 | 7CDB 99 00310 CWD ; EXPAND AX TO DX:AX 316 | 7CDC F7F9 00311 IDIV CX ; SIGNED DIVISION 317 | 7CDE EBE2 00312 JMP F21 ; FIND MORE OPERATORS 318 | 7CDE 00313 319 | 7CDE 00314 ; 320 | 7CDE 00315 ; HANDLE AN EXPRESSION. 321 | 7CDE 00316 ; THIRD TIER: PARENTHESES, NUMBERS AND VARS. 322 | 7CDE 00317 ; 323 | 7CE0 00318 EXPR2_2: 324 | 7CE0 46 00319 INC SI ; AVOID OPERATOR 325 | 7CE1 00320 EXPR2: 326 | 7CE1 E82800 00321 CALL SPACES ; JUMP SPACES 327 | 7CE4 AC 00322 LODSB ; READ CHARACTER 328 | 7CE5 3C28 00323 CMP AL,'(' ; OPEN PARENTHESIS? 329 | 7CE7 750B 00324 JNE F24 330 | 7CE9 E8B4FF 00325 CALL EXPR ; PROCESS INNER EXPR. 331 | 7CEC 803C29 00326 CMP BYTE [SI],')' ; CLOSING PARENTHESIS? 332 | 7CEF 7420 00327 JE SPACES_2 ; YES, AVOID SPACES 333 | 7CF1 E972FF 00328 JMP ERROR ; NO, JUMP TO ERROR 334 | 7CF1 00329 335 | 7CF4 3C40 00330 F24: CMP AL,0X40 ; VARIABLE? 336 | 7CF6 7306 00331 JNC F25 ; YES, JUMP 337 | 7CF8 4E 00332 DEC SI ; BACK ONE LETTER... 338 | 7CF9 E82D00 00333 CALL INPUT_NUMBER ; ...TO READ NUMBER 339 | 7CFC EB0E 00334 JMP SPACES ; AVOID SPACES 340 | 7CFC 00335 341 | 7CFE E80500 00336 F25: CALL GET_VARIABLE_2 ; GET VARIABLE ADDRESS 342 | 7D01 93 00337 XCHG AX,BX 343 | 7D02 8B07 00338 MOV AX,[BX] ; READ 344 | 7D04 C3 00339 RET ; RETURN 345 | 7D04 00340 346 | 7D04 00341 ; 347 | 7D04 00342 ; GET VARIABLE ADDRESS 348 | 7D04 00343 ; 349 | 7D05 00344 GET_VARIABLE: 350 | 7D05 AC 00345 LODSB ; READ SOURCE 351 | 7D06 00346 GET_VARIABLE_2: 352 | 7D06 241F 00347 AND AL,0X1F ; 0X61-0X7A -> 0X01-0X1A 353 | 7D08 00C0 00348 ADD AL,AL ; X 2 (EACH VARIABLE = WORD) 354 | 7D0A B47E 00349 MOV AH,VARS>>8 ; SETUP HIGH-BYTE OF ADDRESS 355 | 7D0A 00350 ; 356 | 7D0A 00351 ; AVOID SPACES 357 | 7D0A 00352 ; 358 | 7D0C 00353 SPACES: 359 | 7D0C 803C20 00354 CMP BYTE [SI],' ' ; SPACE FOUND? 360 | 7D0F 752D 00355 JNE F22 ; NO, RETURN 361 | 7D0F 00356 ; 362 | 7D0F 00357 ; AVOID SPACES AFTER CURRENT CHARACTER 363 | 7D0F 00358 ; 364 | 7D11 00359 SPACES_2: 365 | 7D11 46 00360 INC SI ; ADVANCE TO NEXT CHARACTER 366 | 7D12 EBF8 00361 JMP SPACES 367 | 7D12 00362 368 | 7D12 00363 ; 369 | 7D12 00364 ; OUTPUT UNSIGNED NUMBER 370 | 7D12 00365 ; AX = VALUE 371 | 7D12 00366 ; 372 | 7D14 00367 OUTPUT_NUMBER: 373 | 7D14 00368 F26: 374 | 7D14 31D2 00369 XOR DX,DX ; DX:AX 375 | 7D16 B90A00 00370 MOV CX,10 ; DIVISOR = 10 376 | 7D19 F7F1 00371 DIV CX ; DIVIDE 377 | 7D1B 09C0 00372 OR AX,AX ; NOTHING AT LEFT? 378 | 7D1D 52 00373 PUSH DX 379 | 7D1E 7403 00374 JE F8 ; NO, JUMP 380 | 7D20 E8F1FF 00375 CALL F26 ; YES, OUTPUT LEFT SIDE 381 | 7D23 58 00376 F8: POP AX 382 | 7D24 0430 00377 ADD AL,'0' ; OUTPUT REMAINDER AS... 383 | 7D26 E98500 00378 JMP OUTPUT ; ...ASCII DIGIT 384 | 7D26 00379 385 | 7D26 00380 ; 386 | 7D26 00381 ; READ NUMBER IN INPUT 387 | 7D26 00382 ; AX = RESULT 388 | 7D26 00383 ; 389 | 7D29 00384 INPUT_NUMBER: 390 | 7D29 31DB 00385 XOR BX,BX ; BX = 0 391 | 7D2B AC 00386 F11: LODSB ; READ SOURCE 392 | 7D2C 2C30 00387 SUB AL,'0' 393 | 7D2E 3C0A 00388 CMP AL,10 ; DIGIT VALID? 394 | 7D30 98 00389 CBW 395 | 7D31 93 00390 XCHG AX,BX 396 | 7D32 7309 00391 JNC F12 ; NO, JUMP 397 | 7D34 B90A00 00392 MOV CX,10 ; MULTIPLY BY 10 398 | 7D37 F7E1 00393 MUL CX 399 | 7D39 01C3 00394 ADD BX,AX ; ADD NEW DIGIT 400 | 7D3B EBEE 00395 JMP F11 ; CONTINUE 401 | 7D3B 00396 402 | 7D3D 4E 00397 F12: DEC SI ; SI POINTS TO FIRST NON-DIGIT 403 | 7D3E 00398 F22: 404 | 7D3E C3 00399 RET 405 | 7D3E 00400 406 | 7D3E 00401 ; 407 | 7D3E 00402 ; HANDLE 'system' STATEMENT 408 | 7D3E 00403 ; 409 | 7D3F 00404 SYSTEM_STATEMENT: 410 | 7D3F CD20 00405 INT 0X20 411 | 7D3F 00406 412 | 7D3F 00407 ; 413 | 7D3F 00408 ; HANDLE 'goto' STATEMENT 414 | 7D3F 00409 ; 415 | 7D41 00410 GOTO_STATEMENT: 416 | 7D41 E85CFF 00411 CALL EXPR ; HANDLE EXPRESSION 417 | 7D44 B9 00412 DB 0XB9 ; MOV CX TO JUMP OVER XOR AX,AX 418 | 7D44 00413 419 | 7D44 00414 ; 420 | 7D44 00415 ; HANDLE 'run' STATEMENT 421 | 7D44 00416 ; (EQUIVALENT TO 'goto 0') 422 | 7D44 00417 ; 423 | 7D45 00418 RUN_STATEMENT: 424 | 7D45 31C0 00419 XOR AX,AX 425 | 7D47 00420 F10: 426 | 7D47 E81F00 00421 CALL FIND_LINE ; FIND LINE IN PROGRAM 427 | 7D4A 833E7E7E00 00422 F27: CMP WORD [RUNNING],0 ; ALREADY RUNNING? 428 | 7D4F 7404 00423 JE F31 429 | 7D51 A37E7E 00424 MOV [RUNNING],AX ; YES, TARGET IS NEW LINE 430 | 7D54 C3 00425 RET 431 | 7D55 00426 F31: 432 | 7D55 50 00427 PUSH AX 433 | 7D56 5E 00428 POP SI 434 | 7D57 051400 00429 ADD AX,MAX_LENGTH ; POINT TO NEXT LINE 435 | 7D5A A37E7E 00430 MOV [RUNNING],AX ; SAVE FOR NEXT TIME 436 | 7D5D E8D7FE 00431 CALL STATEMENT ; PROCESS CURRENT STATEMENT 437 | 7D60 A17E7E 00432 MOV AX,[RUNNING] 438 | 7D63 3D20CD 00433 CMP AX,PROGRAM+MAX_SIZE ; REACHED THE END? 439 | 7D66 75ED 00434 JNE F31 ; NO, CONTINUE 440 | 7D68 C3 00435 RET ; YES, RETURN 441 | 7D68 00436 442 | 7D68 00437 ; 443 | 7D68 00438 ; FIND LINE IN PROGRAM 444 | 7D68 00439 ; ENTRY: 445 | 7D68 00440 ; AX = LINE NUMBER 446 | 7D68 00441 ; RESULT: 447 | 7D68 00442 ; AX = POINTER TO PROGRAM 448 | 7D69 00443 FIND_LINE: 449 | 7D69 B91400 00444 MOV CX,MAX_LENGTH 450 | 7D6C F7E1 00445 MUL CX 451 | 7D6E 05007F 00446 ADD AX,PROGRAM 452 | 7D71 C3 00447 RET 453 | 7D71 00448 454 | 7D71 00449 ; 455 | 7D71 00450 ; INPUT LINE FROM KEYBOARD 456 | 7D71 00451 ; ENTRY: 457 | 7D71 00452 ; AL = PROMPT CHARACTER 458 | 7D71 00453 ; RESULT: 459 | 7D71 00454 ; BUFFER 'line' CONTAINS LINE, FINISHED WITH CR 460 | 7D71 00455 ; SI POINTS TO 'line'. 461 | 7D71 00456 ; 462 | 7D72 00457 INPUT_LINE: 463 | 7D72 E83900 00458 CALL OUTPUT 464 | 7D75 BE807E 00459 MOV SI,LINE 465 | 7D78 56 00460 PUSH SI 466 | 7D79 5F 00461 POP DI ; TARGET FOR WRITING LINE 467 | 7D7A E82D00 00462 F1: CALL INPUT_KEY ; READ KEYBOARD 468 | 7D7D AA 00463 STOSB ; SAVE KEY IN BUFFER 469 | 7D7E 3C08 00464 CMP AL,0X08 ; BACKSPACE? 470 | 7D80 7502 00465 JNE F2 ; NO, JUMP 471 | 7D82 4F 00466 DEC DI ; GET BACK ONE CHARACTER 472 | 7D83 4F 00467 DEC DI 473 | 7D84 3C0D 00468 F2: CMP AL,0X0D ; CR PRESSED? 474 | 7D86 75F2 00469 JNE F1 ; NO, WAIT ANOTHER KEY 475 | 7D88 C3 00470 RET ; YES, RETURN 476 | 7D88 00471 477 | 7D88 00472 ; 478 | 7D88 00473 ; HANDLE "print" STATEMENT 479 | 7D88 00474 ; 480 | 7D89 00475 PRINT_STATEMENT: 481 | 7D89 AC 00476 LODSB ; READ SOURCE 482 | 7D8A 3C0D 00477 CMP AL,0X0D ; END OF LINE? 483 | 7D8C 7424 00478 JE NEW_LINE ; YES, GENERATE NEW LINE AND RETURN 484 | 7D8E 3C22 00479 CMP AL,'"' ; DOUBLE QUOTES? 485 | 7D90 750B 00480 JNE F7 ; NO, JUMP 486 | 7D92 00481 PRINT_2: 487 | 7D92 00482 F9: 488 | 7D92 AC 00483 LODSB ; READ STRING CONTENTS 489 | 7D93 3C22 00484 CMP AL,'"' ; DOUBLE QUOTES? 490 | 7D95 740D 00485 JE F18 ; YES, JUMP 491 | 7D97 E81400 00486 CALL OUTPUT ; OUTPUT CHARACTER 492 | 7D9A 75F6 00487 JNE F9 ; JUMP IF NOT FINISHED WITH 0X0D (CR) 493 | 7D9C C3 00488 RET ; RETURN 494 | 7D9C 00489 495 | 7D9D 4E 00490 F7: DEC SI 496 | 7D9E E8FFFE 00491 CALL EXPR ; HANDLE EXPRESSION 497 | 7DA1 E870FF 00492 CALL OUTPUT_NUMBER ; OUTPUT RESULT 498 | 7DA4 AC 00493 F18: LODSB ; READ NEXT CHARACTER 499 | 7DA5 3C3B 00494 CMP AL,';' ; IS IT SEMICOLON? 500 | 7DA7 7509 00495 JNE NEW_LINE ; NO, JUMP TO GENERATE NEW LINE 501 | 7DA9 C3 00496 RET ; YES, RETURN 502 | 7DA9 00497 503 | 7DA9 00498 ; 504 | 7DA9 00499 ; READ A KEY INTO AL 505 | 7DA9 00500 ; ALSO OUTPUTS IT TO SCREEN 506 | 7DA9 00501 ; 507 | 7DAA 00502 INPUT_KEY: 508 | 7DAA B400 00503 MOV AH,0X00 509 | 7DAC CD16 00504 INT 0X16 510 | 7DAC 00505 ; 511 | 7DAC 00506 ; SCREEN OUTPUT OF CHARACTER CONTAINED IN AL 512 | 7DAC 00507 ; EXPANDS 0X0D (CR) INTO 0X0A 0X0D (LF CR) 513 | 7DAC 00508 ; 514 | 7DAE 00509 OUTPUT: 515 | 7DAE 3C0D 00510 CMP AL,0X0D 516 | 7DB0 7507 00511 JNE F17 517 | 7DB0 00512 ; 518 | 7DB0 00513 ; GO TO NEXT LINE (GENERATES LF+CR) 519 | 7DB0 00514 ; 520 | 7DB2 00515 NEW_LINE: 521 | 7DB2 B00A 00516 MOV AL,0X0A 522 | 7DB4 E80200 00517 CALL F17 523 | 7DB7 B00D 00518 MOV AL,0X0D 524 | 7DB9 00519 F17: 525 | 7DB9 B40E 00520 MOV AH,0X0E 526 | 7DBB BB0700 00521 MOV BX,0X0007 527 | 7DBE CD10 00522 INT 0X10 528 | 7DC0 3C0D 00523 CMP AL,0X0D 529 | 7DC2 C3 00524 RET 530 | 7DC2 00525 531 | 7DC2 00526 ; 532 | 7DC2 00527 ; LIST OF STATEMENTS OF BOOTBASIC 533 | 7DC2 00528 ; FIRST ONE BYTE WITH LENGTH OF STRING 534 | 7DC2 00529 ; THEN STRING WITH STATEMENT 535 | 7DC2 00530 ; THEN A WORD WITH THE ADDRESS OF THE CODE 536 | 7DC2 00531 ; 537 | 7DC3 00532 STATEMENTS: 538 | 7DC3 036E6577 00533 DB 3,"new" 539 | 7DC7 007C 00534 DW START 540 | 7DC7 00535 541 | 7DC9 046C697374 00536 DB 4,"list" 542 | 7DCE 727C 00537 DW LIST_STATEMENT 543 | 7DCE 00538 544 | 7DD0 0372756E 00539 DB 3,"run" 545 | 7DD4 457D 00540 DW RUN_STATEMENT 546 | 7DD4 00541 547 | 7DD6 057072696E74 00542 DB 5,"print" 548 | 7DDC 897D 00543 DW PRINT_STATEMENT 549 | 7DDC 00544 550 | 7DDE 05696E707574 00545 DB 5,"input" 551 | 7DE4 917C 00546 DW INPUT_STATEMENT 552 | 7DE4 00547 553 | 7DE6 026966 00548 DB 2,"if" 554 | 7DE9 307C 00549 DW IF_STATEMENT 555 | 7DE9 00550 556 | 7DEB 04676F746F 00551 DB 4,"goto" 557 | 7DF0 417D 00552 DW GOTO_STATEMENT 558 | 7DF0 00553 559 | 7DF2 0673797374656D 00554 DB 6,"system" 560 | 7DF9 3F7D 00555 DW SYSTEM_STATEMENT 561 | 7DF9 00556 562 | 7DFB 00 00557 DB 0 563 | 7DFB 00558 564 | 7DFB 00559 ; 565 | 7DFB 00560 ; BOOT SECTOR FILLER 566 | 7DFB 00561 ; 567 | 7DFB 00562 %IF COM_FILE 568 | 7DFB 00563 %ELSE 569 | 7DFC 4F4F 00564 TIMES 510-($-$$) DB 0X4F 570 | 7DFE 55AA 00565 DB 0X55,0XAA ; MAKE IT A BOOTABLE SECTOR 571 | 7DFE 00566 %ENDIF 572 | 7DFE 00567 573 | 574 | 00000 ERRORS FOUND 575 | 00000 WARNINGS FOUND 576 | 577 | 01024 PROGRAM BYTES 578 | 579 | LABEL VALUE/ADDRESS 580 | 581 | ASSIGNMENT 7c9a 582 | COM_FILE 0000 583 | ERROR 7c66 584 | ERROR_MESSAGE 7c6e 585 | EXPR 7ca0 586 | EXPR1 7cbf 587 | EXPR1_2 7cbe 588 | EXPR2 7ce1 589 | EXPR2_2 7ce0 590 | F1 7d7a 591 | F10 7d47 592 | F11 7d2b 593 | F12 7d3d 594 | F15 7cb1 595 | F16 7c4c 596 | F17 7db9 597 | F18 7da4 598 | F19 7cb6 599 | F2 7d84 600 | F20 7ca3 601 | F21 7cc2 602 | F22 7d3e 603 | F23 7cd5 604 | F24 7cf4 605 | F25 7cfe 606 | F26 7d14 607 | F27 7d4a 608 | F29 7c74 609 | F3 7c56 610 | F30 7c89 611 | F31 7d55 612 | F32 7c83 613 | F4 7c5d 614 | F5 7c42 615 | F6 7c90 616 | F7 7d9d 617 | F8 7d23 618 | F9 7d92 619 | FIND_LINE 7d69 620 | GET_VARIABLE 7d05 621 | GET_VARIABLE_2 7d06 622 | GOTO_STATEMENT 7d41 623 | IF_STATEMENT 7c30 624 | INPUT_KEY 7daa 625 | INPUT_LINE 7d72 626 | INPUT_NUMBER 7d29 627 | INPUT_STATEMENT 7c91 628 | LINE 7e80 629 | LIST_STATEMENT 7c72 630 | MAIN_LOOP 7c11 631 | MAX_LENGTH 0014 632 | MAX_LINE 03e8 633 | MAX_SIZE 4e20 634 | NEW_LINE 7db2 635 | OUTPUT 7dae 636 | OUTPUT_NUMBER 7d14 637 | PRINT_2 7d92 638 | PRINT_STATEMENT 7d89 639 | PROGRAM 7f00 640 | RUNNING 7e7e 641 | RUN_STATEMENT 7d45 642 | SPACES 7d0c 643 | SPACES_2 7d11 644 | STACK ff00 645 | START 7c00 646 | STATEMENT 7c37 647 | STATEMENTS 7dc3 648 | SYSTEM_STATEMENT 7d3f 649 | VARS 7e00 650 | -------------------------------------------------------------------------------- /test/BRICKS.IMG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nanochess/tinyasm/84232d5845a677aa6924b51ee9123c16954cf7d4/test/BRICKS.IMG -------------------------------------------------------------------------------- /test/FBIRD.IMG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nanochess/tinyasm/84232d5845a677aa6924b51ee9123c16954cf7d4/test/FBIRD.IMG -------------------------------------------------------------------------------- /test/INCLUDE.ASM: -------------------------------------------------------------------------------- 1 | ; 2 | ; HelloWorld with %include test programm 3 | ; 4 | ; by DosWorld 5 | ; https://github.com/DosWorld/ 6 | ; 7 | ; (c) Copyright 2019 DosWorld 8 | ; 9 | ; Creation date: Nov/03/2019. 5am. 10 | ; 11 | 12 | org 0x0100 13 | %include "test\include1.inc" 14 | -------------------------------------------------------------------------------- /test/INCLUDE.COM: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nanochess/tinyasm/84232d5845a677aa6924b51ee9123c16954cf7d4/test/INCLUDE.COM -------------------------------------------------------------------------------- /test/INCLUDE.LST: -------------------------------------------------------------------------------- 1 | 00001 ; 2 | 00002 ; HELLOWORLD WITH %INCLUDE TEST PROGRAMM 3 | 00003 ; 4 | 00004 ; BY DOSWORLD 5 | 00005 ; HTTPS://GITHUB.COM/DOSWORLD/ 6 | 00006 ; 7 | 00007 ; (C) COPYRIGHT 2019 DOSWORLD 8 | 00008 ; 9 | 00009 ; CREATION DATE: NOV/03/2019. 5AM. 10 | 00010 ; 11 | 00011 12 | 0100 00012 ORG 0X0100 13 | 0100 00013 %INCLUDE "test\INCLUDE1.INC" 14 | 15 | 0100 00001 %INCLUDE "test\INCLUDE2.INC" 16 | 17 | 0100 B409 00001 MOV AH,9 18 | 0102 BA0901 00002 MOV DX,MSG 19 | 0105 CD21 00003 INT 0X21 20 | 0107 EB0E 00004 JMP SHORT OVERMSG 21 | 0109 48656C6C6F2C2077 00005 MSG: DB "Hello, world!$" 22 | 0117 00006 OVERMSG: 23 | 0117 00002 %INCLUDE "test\INCLUDE3.INC" 24 | 25 | 0117 C3 00001 RET 26 | 27 | 00000 ERRORS FOUND 28 | 00000 WARNINGS FOUND 29 | 30 | 00024 PROGRAM BYTES 31 | 32 | LABEL VALUE/ADDRESS 33 | 34 | MSG 0109 35 | OVERMSG 0117 36 | -------------------------------------------------------------------------------- /test/INCLUDE1.INC: -------------------------------------------------------------------------------- 1 | %include "test\include2.inc" 2 | %include "test\include3.inc" 3 | -------------------------------------------------------------------------------- /test/INCLUDE2.INC: -------------------------------------------------------------------------------- 1 | mov ah,9 2 | mov dx,msg 3 | int 0x21 4 | jmp short overmsg 5 | msg: db "Hello, world!$" 6 | overmsg: 7 | -------------------------------------------------------------------------------- /test/INCLUDE3.INC: -------------------------------------------------------------------------------- 1 | ret 2 | -------------------------------------------------------------------------------- /test/INPUT0.IMG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nanochess/tinyasm/84232d5845a677aa6924b51ee9123c16954cf7d4/test/INPUT0.IMG -------------------------------------------------------------------------------- /test/INPUT1.IMG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nanochess/tinyasm/84232d5845a677aa6924b51ee9123c16954cf7d4/test/INPUT1.IMG -------------------------------------------------------------------------------- /test/INPUT2.IMG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nanochess/tinyasm/84232d5845a677aa6924b51ee9123c16954cf7d4/test/INPUT2.IMG -------------------------------------------------------------------------------- /test/INVADERS.IMG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nanochess/tinyasm/84232d5845a677aa6924b51ee9123c16954cf7d4/test/INVADERS.IMG -------------------------------------------------------------------------------- /test/PILLMAN.IMG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nanochess/tinyasm/84232d5845a677aa6924b51ee9123c16954cf7d4/test/PILLMAN.IMG -------------------------------------------------------------------------------- /test/ROGUE.COM: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nanochess/tinyasm/84232d5845a677aa6924b51ee9123c16954cf7d4/test/ROGUE.COM -------------------------------------------------------------------------------- /test/ROGUE.IMG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nanochess/tinyasm/84232d5845a677aa6924b51ee9123c16954cf7d4/test/ROGUE.IMG -------------------------------------------------------------------------------- /test/ROGUE.LST: -------------------------------------------------------------------------------- 1 | 00001 ; 2 | 00002 ; BOOTROGUE GAME IN 512 BYTES (BOOT SECTOR) 3 | 00003 ; 4 | 00004 ; BY OSCAR TOLEDO G. 5 | 00005 ; HTTP://NANOCHESS.ORG/ 6 | 00006 ; 7 | 00007 ; (C) COPYRIGHT 2019 OSCAR TOLEDO G. 8 | 00008 ; 9 | 00009 ; CREATION DATE: SEP/19/2019. GENERATES ROOM BOXES. 10 | 00010 ; REVISION DATE: SEP/20/2019. CONNECT ROOMS. ALLOWS TO NAVIGATE. 11 | 00011 ; REVISION DATE: SEP/21/2019. ADDED LADDERS TO GO DOWN/UP. SHOWS 12 | 00012 ; AMULET OF YENDOR AT LEVEL 26. ADDED 13 | 00013 ; CIRCLE OF LIGHT. 14 | 00014 ; REVISION DATE: SEP/22/2019. CREATES MONSTERS AND ITEMS. NOW HAS 15 | 00015 ; HP/EXP. FOOD, ARMOR, WEAPON, AND TRAPS 16 | 00016 ; WORKS. ADDED BATTLES. 829 BYTES. 17 | 00017 ; REVISION DATE: SEP/23/2019. LOTS OF OPTIMIZATION. 643 BYTES. 18 | 00018 ; REVISION DATE: SEP/24/2019. AGAIN LOTS OF OPTIMIZATION. 596 BYTES. 19 | 00019 ; REVISION DATE: SEP/25/2019. MANY OPTIMIZATIONS. 553 BYTES. 20 | 00020 ; REVISION DATE: SEP/26/2019. THE FINAL EFFORT. 510 BYTES. 21 | 00021 ; REVISION DATE: SEP/27/2019. THE COM FILE EXITS TO DOS INSTEAD OF HALTING. 22 | 00022 ; 23 | 00023 24 | 00024 CPU 8086 25 | 00025 26 | 00026 ROW_WIDTH: EQU 0X00A0 ; WIDTH IN BYTES OF EACH VIDEO ROW 27 | 00027 BOX_MAX_WIDTH: EQU 23 ; MAX WIDTH OF A ROOM BOX 28 | 00028 BOX_MAX_HEIGHT: EQU 6 ; MAX HEIGHT OF A ROOM BOX 29 | 00029 BOX_WIDTH: EQU 26 ; WIDTH OF BOX AREA IN SCREEN 30 | 00030 BOX_HEIGHT: EQU 8 ; HEIGHT OF BOX AREA IN SCREEN 31 | 00031 32 | 00032 ; SEE PAGE 45 OF MY BOOK 33 | 00033 LIGHT_COLOR: EQU 0X06 ; LIGHT COLOR (BROWN, DARK YELLOW ON EMU) 34 | 00034 HERO_COLOR: EQU 0X0E ; HERO COLOR (YELLOW) 35 | 00035 36 | 00036 ; SEE PAGE 179 OF MY BOOK 37 | 00037 GR_VERT: EQU 0XBA ; VERTICAL LINE GRAPHIC 38 | 00038 GR_TOP_RIGHT: EQU 0XBB ; TOP RIGHT GRAPHIC 39 | 00039 GR_BOT_RIGHT: EQU 0XBC ; BOTTOM RIGHT GRAPHIC 40 | 00040 GR_BOT_LEFT: EQU 0XC8 ; BOTTOM LEFT GRAPHIC 41 | 00041 GR_TOP_LEFT: EQU 0XC9 ; TOP LEFT GRAPHIC 42 | 00042 GR_HORIZ: EQU 0XCD ; HORIZONTAL LINE GRAPHIC 43 | 00043 44 | 00044 GR_TUNNEL: EQU 0XB1 ; TUNNEL GRAPHIC (SHADED BLOCK) 45 | 00045 GR_DOOR: EQU 0XCE ; DOOR GRAPHIC (CROSSHAIR GRAPHIC) 46 | 00046 GR_FLOOR: EQU 0XFA ; FLOOR GRAPHIC (MIDDLE POINT) 47 | 00047 48 | 00048 GR_HERO: EQU 0X01 ; HERO GRAPHIC (SMILING FACE) 49 | 00049 50 | 00050 GR_LADDER: EQU 0XF0 ; LADDER GRAPHIC 51 | 00051 GR_TRAP: EQU 0X04 ; TRAP GRAPHIC (DIAMOND) 52 | 00052 GR_FOOD: EQU 0X05 ; FOOD GRAPHIC (CLOVER) 53 | 00053 GR_ARMOR: EQU 0X08 ; ARMOR GRAPHIC (SQUARE WITH HOLE IN CENTER) 54 | 00054 GR_YENDOR: EQU 0X0C ; AMULET OF YENDOR GRAPHIC (FEMALE SIGN) 55 | 00055 GR_GOLD: EQU 0X0F ; GOLD GRAPHIC (ASTERISK, LIKE BRIGHTNESS) 56 | 00056 GR_WEAPON: EQU 0X18 ; WEAPON GRAPHIC (UP ARROW) 57 | 00057 58 | 00058 YENDOR_LEVEL: EQU 26 ; LEVEL OF APPEARANCE FOR AMULET OF YENDOR 59 | 00059 60 | 00060 %IFDEF COM_FILE 61 | 0006 00061 ORG 0X0100 62 | 0006 00062 %ELSE 63 | 0006 00063 ORG 0X7C00 64 | 0006 00064 %ENDIF 65 | 0006 00065 66 | 0006 00066 ; 67 | 0006 00067 ; SORTED BY ORDER OF PUSH INSTRUCTIONS 68 | 0006 00068 ; 69 | 0006 00069 RND: EQU 0X0006 ; RANDOM SEED 70 | 0006 00070 HP: EQU 0X0004 ; CURRENT HP 71 | 0006 00071 LEVEL: EQU 0X0003 ; CURRENT LEVEL (STARTING AT 0X01) 72 | 0006 00072 YENDOR: EQU 0X0002 ; 0X01 = NOT FOUND. 0XFF = FOUND. 73 | 0006 00073 ARMOR: EQU 0X0001 ; ARMOR LEVEL 74 | 0006 00074 WEAPON: EQU 0X0000 ; WEAPON LEVEL 75 | 0006 00075 76 | 0006 00076 ; 77 | 0006 00077 ; START OF THE ADVENTURE! 78 | 0006 00078 ; 79 | 0100 00079 START: 80 | 0100 E540 00080 IN AX,0X40 ; READ TIMER COUNTER 81 | 0102 50 00081 PUSH AX ; SETUP PSEUDORANDOM NUMBER GENERATOR 82 | 0102 00082 83 | 0103 B81000 00083 MOV AX,16 84 | 0106 50 00084 PUSH AX ; HP 85 | 0107 B001 00085 MOV AL,1 86 | 0109 50 00086 PUSH AX ; YENDOR (LOW BYTE) + LEVEL (HIGH BYTE) 87 | 010A 50 00087 PUSH AX ; WEAPON (LOW BYTE) + ARMOR (HIGH BYTE) 88 | 010B 40 00088 INC AX ; AX = 0X0002 (IT WAS 0X0001) 89 | 010C CD10 00089 INT 0X10 90 | 010E B800B8 00090 MOV AX,0XB800 ; TEXT VIDEO SEGMENT 91 | 0111 8ED8 00091 MOV DS,AX 92 | 0113 8EC0 00092 MOV ES,AX 93 | 0113 00093 94 | 0115 89E5 00094 MOV BP,SP 95 | 0117 00095 GENERATE_DUNGEON: 96 | 0117 00096 ; 97 | 0117 00097 ; ADVANCE TO NEXT LEVEL (CAN GO DEEPER OR HIGHER) 98 | 0117 00098 ; 99 | 0117 8A5E02 00099 MOV BL,[BP+YENDOR] 100 | 011A 005E03 00100 ADD [BP+LEVEL],BL 101 | 011A 00101 %IFDEF COM_FILE 102 | 011D 7503 00102 JNE .0 103 | 011F E91801 00103 JMP QUIT ; STOP IF LEVEL ZERO IS REACHED 104 | 0122 00104 .0: 105 | 0122 00105 %ELSE 106 | 0122 00106 JE $ ; STOP IF LEVEL ZERO IS REACHED 107 | 0122 00107 %ENDIF 108 | 0122 00108 109 | 0122 00109 ; 110 | 0122 00110 ; SELECT A MAZE FOR THE DUNGEON 111 | 0122 00111 ; 112 | 0122 00112 ; THERE ARE MANY COMBINATIONS OF VALUES THAT GENERATE AT LEAST 113 | 0122 00113 ; 16 MAZES IN ORDER TO AVOID A TABLE. 114 | 0122 00114 ; 115 | 0122 8B4606 00115 MOV AX,[BP+RND] 116 | 0125 258241 00116 AND AX,0X4182 117 | 0128 0D6D1A 00117 OR AX,0X1A6D 118 | 012B 96 00118 XCHG AX,SI 119 | 012B 00119 120 | 012B 00120 ; 121 | 012B 00121 ; CLEAN THE SCREEN 122 | 012B 00122 ; 123 | 012C 31C0 00123 XOR AX,AX 124 | 012E 31FF 00124 XOR DI,DI 125 | 0130 B508 00125 MOV CH,0X08 126 | 0132 F3AB 00126 REP STOSW 127 | 0132 00127 128 | 0132 00128 ; 129 | 0132 00129 ; DRAW THE NINE ROOMS 130 | 0132 00130 ; 131 | 0134 B85601 00131 MOV AX,(BOX_HEIGHT/2-2)*ROW_WIDTH+(BOX_WIDTH/2-2)*2 132 | 0137 00132 .7: 133 | 0137 50 00133 PUSH AX 134 | 0138 50 00134 PUSH AX 135 | 0139 05A400 00135 ADD AX,ROW_WIDTH+4 ; GET THE CENTER OF ROOM 136 | 013C 97 00136 XCHG AX,DI 137 | 013D D1EE 00137 SHR SI,1 ; OBTAIN BIT OF RIGHT CONNECTION 138 | 013F B8B100 00138 MOV AX,0X0000+GR_TUNNEL 139 | 0142 B91A00 00139 MOV CX,BOX_WIDTH 140 | 0145 7304 00140 JNC .3 141 | 0147 57 00141 PUSH DI 142 | 0148 F3AB 00142 REP STOSW ; HORIZONTAL TUNNEL 143 | 014A 5F 00143 POP DI 144 | 014B 00144 .3: 145 | 014B D1EE 00145 SHR SI,1 ; OBTAIN BIT OF DOWN CONNECTION 146 | 014D 7309 00146 JNC .5 147 | 014F B108 00147 MOV CL,BOX_HEIGHT 148 | 0151 00148 .4: 149 | 0151 AA 00149 STOSB ; VERTICAL TUNNEL 150 | 0152 81C79F00 00150 ADD DI,ROW_WIDTH-1 151 | 0156 E2F9 00151 LOOP .4 152 | 0158 00152 .5: 153 | 0158 B715 00153 MOV BH,BOX_MAX_WIDTH-2 154 | 015A E89001 00154 CALL RANDOM ; GET A RANDOM WIDTH FOR ROOM. 155 | 015D 91 00155 XCHG AX,CX 156 | 015E B704 00156 MOV BH,BOX_MAX_HEIGHT-2 157 | 0160 E88A01 00157 CALL RANDOM ; GET A RANDOM HEIGHT FOR ROOM. 158 | 0163 88C5 00158 MOV CH,AL 159 | 0165 24FE 00159 AND AL,0XFE ; IT NEEDS A/2*2 SO THIS DOES IT. 160 | 0167 B450 00160 MOV AH,ROW_WIDTH/2 161 | 0169 F6E4 00161 MUL AH 162 | 016B 01C8 00162 ADD AX,CX ; NOW IT HAS A CENTERING OFFSET 163 | 016D 28EC 00163 SUB AH,CH ; BETTER THAN "mov bx,cx mov bh,0" 164 | 016F 24FE 00164 AND AL,0XFE 165 | 0171 5F 00165 POP DI 166 | 0172 29C7 00166 SUB DI,AX ; SUBTRACT FROM ROOM CENTER 167 | 0174 B0C9 00167 MOV AL,GR_TOP_LEFT ; DRAW TOP ROW OF ROOM 168 | 0176 BBCDBB 00168 MOV BX,GR_TOP_RIGHT*256+GR_HORIZ 169 | 0179 E81E01 00169 CALL FILL 170 | 017C 00170 .9: 171 | 017C B0BA 00171 MOV AL,GR_VERT ; DRAW INTERMEDIATE ROW OF ROOM 172 | 017E BBFABA 00172 MOV BX,GR_VERT*256+GR_FLOOR 173 | 0181 E81601 00173 CALL FILL 174 | 0184 FECD 00174 DEC CH 175 | 0186 79F4 00175 JNS .9 176 | 0188 B0C8 00176 MOV AL,GR_BOT_LEFT ; DRAW BOTTOM ROW OF ROOM 177 | 018A BBCDBC 00177 MOV BX,GR_BOT_RIGHT*256+GR_HORIZ 178 | 018D E80A01 00178 CALL FILL 179 | 0190 58 00179 POP AX 180 | 0191 053400 00180 ADD AX,BOX_WIDTH*2 181 | 0194 3CF2 00181 CMP AL,0XF2 ; FINISHED DRAWING THREE ROOMS? 182 | 0196 7503 00182 JNE .6 ; NO, JUMP 183 | 0196 00183 ; YES, GO TO FOLLOWING ROW 184 | 0198 056404 00184 ADD AX,ROW_WIDTH*BOX_HEIGHT-BOX_WIDTH*3*2 185 | 019B 00185 .6: 186 | 019B 3D000F 00186 CMP AX,ROW_WIDTH*BOX_HEIGHT*3 187 | 019E 7297 00187 JB .7 188 | 019E 00188 189 | 019E 00189 ; 190 | 019E 00190 ; PUT THE LADDER AT A RANDOM CORNER ROOM 191 | 019E 00191 ; 192 | 01A0 D16606 00192 SHL WORD [BP+RND],1 193 | 01A3 B8F801 00193 MOV AX,3*ROW_WIDTH+12*2 194 | 01A6 BBF80B 00194 MOV BX,19*ROW_WIDTH+12*2 195 | 01A9 7301 00195 JNC .2 196 | 01AB 93 00196 XCHG AX,BX 197 | 01AC 7903 00197 .2: JNS .8 198 | 01AE 056800 00198 ADD AX,BOX_WIDTH*2*2 199 | 01B1 00199 .8: 200 | 01B1 97 00200 XCHG AX,DI 201 | 01B1 00201 202 | 01B2 C605F0 00202 MOV BYTE [DI],GR_LADDER 203 | 01B2 00203 204 | 01B2 00204 ; 205 | 01B2 00205 ; IF A DEEP LEVEL HAS BEEN REACHED THEN PUT THE AMULET OF YENDOR 206 | 01B2 00206 ; 207 | 01B5 807E031A 00207 CMP BYTE [BP+LEVEL],YENDOR_LEVEL 208 | 01B9 7203 00208 JB .1 209 | 01BB C6070C 00209 MOV BYTE [BX],GR_YENDOR 210 | 01BE 00210 .1: 211 | 01BE 00211 ; 212 | 01BE 00212 ; SETUP HERO START 213 | 01BE 00213 ; 214 | 01BE BF2C07 00214 MOV DI,11*ROW_WIDTH+38*2 215 | 01BE 00215 ; 216 | 01BE 00216 ; MAIN GAME LOOP 217 | 01BE 00217 ; 218 | 01C1 00218 GAME_LOOP: 219 | 01C1 B8C101 00219 MOV AX,GAME_LOOP ; FORCE TO REPEAT, THE WHOLE LOOP... 220 | 01C4 50 00220 PUSH AX ; ...ENDS WITH RET. 221 | 01C4 00221 222 | 01C4 00222 ; 223 | 01C4 00223 ; CIRCLE OF LIGHT AROUND THE PLAYER (3X3) 224 | 01C4 00224 ; 225 | 01C5 BB0500 00225 MOV BX,0X0005 ; BX VALUES 226 | 01C8 4B 00226 .1: DEC BX 227 | 01C9 4B 00227 DEC BX ; -1 1 3 -0X00A0 228 | 01CA B006 00228 MOV AL,LIGHT_COLOR 229 | 01CC 888160FF 00229 MOV [BX+DI-ROW_WIDTH],AL ; -1(1)3 230 | 01D0 8801 00230 MOV [BX+DI],AL 231 | 01D2 8881A000 00231 MOV [BX+DI+ROW_WIDTH],AL ; -1 1 3 +0X00A0 232 | 01D6 79F0 00232 JNS .1 233 | 01D6 00233 234 | 01D6 00234 ; 235 | 01D6 00235 ; SHOW OUR HERO 236 | 01D6 00236 ; 237 | 01D8 FF35 00237 PUSH WORD [DI] ; SAVE CHARACTER UNDER 238 | 01DA C705010E 00238 MOV WORD [DI],HERO_COLOR*256+GR_HERO 239 | 01DE 31C0 00239 XOR AX,AX 240 | 01E0 E86E00 00240 CALL ADD_HP ; UPDATE STATS 241 | 01E0 00241 ; MOV AH,0X00 ; COMES HERE WITH AH = 0 242 | 01E3 CD16 00242 INT 0X16 ; READ KEYBOARD 243 | 01E5 8F05 00243 POP WORD [DI] ; RESTORE CHARACTER UNDER 244 | 01E5 00244 245 | 01E7 88E0 00245 MOV AL,AH 246 | 01E7 00246 %IFDEF COM_FILE 247 | 01E9 3C01 00247 CMP AL,0X01 248 | 01EB 744D 00248 JE QUIT ; EXIT IF ESC KEY IS PRESSED 249 | 01EB 00249 %ENDIF 250 | 01EB 00250 251 | 01ED 2C4C 00251 SUB AL,0X4C 252 | 01EF B402 00252 MOV AH,0X02 ; LEFT/RIGHT MULTIPLIES BY 2 253 | 01F1 3CFF 00253 CMP AL,0XFF ; GOING LEFT (SCANCODE 0X4B) 254 | 01F3 740E 00254 JE .2 255 | 01F5 3C01 00255 CMP AL,0X01 ; GOING RIGHT (SCANCODE 0X4D) 256 | 01F7 740A 00256 JE .2 257 | 01F9 3CFC 00257 CMP AL,0XFC ; GOING UP (SCANCODE 0X48) 258 | 01FB 7404 00258 JE .3 259 | 01FD 3C04 00259 CMP AL,0X04 ; GOING DOWN (SCANCODE 0X50) 260 | 01FF 7538 00260 JNE MOVE_CANCEL 261 | 0201 00261 .3: 262 | 0201 B428 00262 MOV AH,0X28 ; UP/DOWN MULTIPLIES BY 40 263 | 0203 00263 .2: 264 | 0203 F6EC 00264 IMUL AH ; SIGNED MULTIPLICATION 265 | 0203 00265 266 | 0205 93 00266 XCHG AX,BX ; BX = DISPLACEMENT OFFSET 267 | 0206 8A01 00267 MOV AL,[DI+BX] ; READ THE TARGET CONTENTS 268 | 0208 3CF0 00268 CMP AL,GR_LADDER ; GR_LADDER? 269 | 020A 7430 00269 JE LADDER_FOUND ; YES, JUMP TO NEXT LEVEL 270 | 020C 7729 00270 JA MOVE_OVER ; > IT MUST BE GR_FLOOR 271 | 020E 3CCE 00271 CMP AL,GR_DOOR ; GR_DOOR? 272 | 0210 7425 00272 JE MOVE_OVER ; YES, CAN MOVE 273 | 0212 3CB1 00273 CMP AL,GR_TUNNEL ; GR_TUNNEL? 274 | 0214 7421 00274 JE MOVE_OVER ; YES, CAN MOVE 275 | 0216 7721 00275 JA MOVE_CANCEL ; > IT MUST BE BORDER, CANCEL MOVEMENT 276 | 0218 3C04 00276 CMP AL,GR_TRAP ; GR_TRAP? 277 | 021A 721D 00277 JB MOVE_CANCEL ; < IT MUST BE BLANK, CANCEL MOVEMENT 278 | 021C 8D39 00278 LEA DI,[DI+BX] ; DO MOVE. 279 | 021E 742C 00279 JE TRAP_FOUND ; = YES, WENT OVER TRAP 280 | 0220 3C18 00280 CMP AL,GR_WEAPON ; GR_WEAPON? 281 | 0222 774D 00281 JA BATTLE ; > IT's a monster, go to battle 282 | 283 | 0224 C605FA 00282 MOV BYTE [DI],GR_FLOOR ; DELETE ITEM FROM FLOOR 284 | 0227 741A 00283 JE WEAPON_FOUND ; = WEAPON FOUND 285 | 0229 3C08 00284 CMP AL,GR_ARMOR ; GR_ARMOR? 286 | 022B 7412 00285 JE ARMOR_FOUND ; YES, INCREASE ARMOR 287 | 022D 7218 00286 JB FOOD_FOUND ; < IT's GR_FOOD, increase hp 288 | 289 | 022F 3C0F 00287 CMP AL,GR_GOLD ; GR_GOLD? 290 | 0231 7406 00288 JE MOVE_CANCEL ; YES, SIMPLY TAKE IT. 291 | 0231 00289 ; AT THIS POINT 'al' ONLY CAN BE GR_YENDOR 292 | 0231 00290 ; AMULET OF YENDOR FOUND! 293 | 0233 F65E02 00291 NEG BYTE [BP+YENDOR] ; NOW PLAYER GOES UPWARDS OVER LADDERS. 294 | 0236 C3 00292 RET 295 | 0237 00293 MOVE_OVER: 296 | 0237 8D39 00294 LEA DI,[BX+DI] ; DO MOVE. 297 | 0239 00295 MOVE_CANCEL: 298 | 0239 C3 00296 RET ; RETURN TO MAIN LOOP. 299 | 0239 00297 300 | 0239 00298 %IFDEF COM_FILE 301 | 023A 00299 QUIT: 302 | 023A CD20 00300 INT 0X20 303 | 023A 00301 %ENDIF 304 | 023A 00302 305 | 023A 00303 ; 306 | 023A 00304 ; I-- 307 | 023A 00305 ; I-- 308 | 023A 00306 ; I-- 309 | 023A 00307 ; 310 | 023C 00308 LADDER_FOUND: 311 | 023C E9D8FE 00309 JMP GENERATE_DUNGEON 312 | 023C 00310 313 | 023C 00311 ; ______ 314 | 023C 00312 ; I I 315 | 023C 00313 ; I #X I 316 | 023C 00314 ; I X# I 317 | 023C 00315 ; \__/ 318 | 023C 00316 ; 319 | 023F 00317 ARMOR_FOUND: 320 | 023F FE4601 00318 INC BYTE [BP+ARMOR] ; INCREASE ARMOR LEVEL 321 | 0242 C3 00319 RET 322 | 0242 00320 323 | 0242 00321 ; 324 | 0242 00322 ; /| _____________ 325 | 0242 00323 ; (|===|OO>_____________> 326 | 0242 00324 ; \| 327 | 0242 00325 ; 328 | 0243 00326 WEAPON_FOUND: 329 | 0243 FE4600 00327 INC BYTE [BP+WEAPON] ; INCREASE WEAPON LEVEL 330 | 0246 C3 00328 RET 331 | 0246 00329 332 | 0246 00330 ; 333 | 0246 00331 ; /--\ 334 | 0246 00332 ; ==== I 335 | 0246 00333 ; \--/ 336 | 0246 00334 ; 337 | 0247 00335 FOOD_FOUND: 338 | 0247 E8A100 00336 CALL RANDOM6 ; RANDOM 1-6 339 | 024A EB05 00337 JMP SHORT ADD_HP 340 | 024A 00338 341 | 024A 00339 ; 342 | 024A 00340 ; AAAARGHHHH! 343 | 024A 00341 ; 344 | 024C 00342 TRAP_FOUND: 345 | 024C E89C00 00343 CALL RANDOM6 ; RANDOM 1-6 346 | 024F F7D8 00344 SUB_HP: NEG AX ; MAKE IT NEGATIVE 347 | 0251 034604 00345 ADD_HP: ADD AX,[BP+HP] ; ADD TO CURRENT HP 348 | 0251 00346 %IFDEF COM_FILE 349 | 0254 78E4 00347 JS QUIT ; EXIT IF ESC KEY IS PRESSED 350 | 0254 00348 %ELSE 351 | 0254 00349 JS $ ; STALL IF DEAD 352 | 0254 00350 %ENDIF 353 | 0256 894604 00351 MOV [BP+HP],AX ; UPDATE HP. 354 | 0256 00352 ; 355 | 0256 00353 ; UPDATE SCREEN INDICATOR 356 | 0256 00354 ; 357 | 0259 BB980F 00355 MOV BX,0X0F98 ; POINT TO BOTTOM RIGHT CORNER 358 | 025C 00356 .1: 359 | 025C 99 00357 CWD ; EXTEND AX INTO DX 360 | 025D B90A00 00358 MOV CX,10 361 | 0260 F7F1 00359 DIV CX ; DIVIDE BY 10 362 | 0262 81C2300A 00360 ADD DX,0X0A30 ; ADD ASCII DIGIT ZERO AND COLOR TO REMAINDER 363 | 0266 8917 00361 MOV [BX],DX ; PUT ON SCREEN 364 | 0268 4B 00362 DEC BX 365 | 0269 4B 00363 DEC BX 366 | 026A 09C0 00364 OR AX,AX ; MORE DIGITS AVAILABLE? 367 | 026C 75EE 00365 JNZ .1 ; YES, JUMP 368 | 026E 8907 00366 MOV [BX],AX ; ERASE CHARACTER JUST IN CASE NUMBER SHRINKS 369 | 0270 C3 00367 RET 370 | 0270 00368 371 | 0270 00369 ; 372 | 0270 00370 ; LET's battle!!! 373 | 374 | 0270 00371 ; 375 | 0271 00372 BATTLE: 376 | 0271 241F 00373 AND AL,0X1F ; SEPARATE NUMBER OF MONSTER (1-26) 377 | 0273 98 00374 CBW ; EXTEND TO 16 BITS 378 | 0274 D0E0 00375 SHL AL,1 ; MAKE IT SLIGHTLY HARDER 379 | 0276 88C3 00376 MOV BL,AL ; ITS ATTACK IS EQUIVALENT TO ITS NUMBER 380 | 0278 96 00377 XCHG AX,SI ; USE ALSO AS ITS HP 381 | 0278 00378 ; PLAYER's attack 382 | 383 | 0279 00379 .2: 384 | 0279 8A7E00 00380 MOV BH,[BP+WEAPON] ; USE CURRENT WEAPON LEVEL AS DICE 385 | 027C E86E00 00381 CALL RANDOM 386 | 027F 29C6 00382 SUB SI,AX ; SUBTRACT FROM MONSTER's HP 387 | 388 | 0281 88DF 00383 MOV BH,BL 389 | 0283 7211 00384 JC .3 ; KILLED? YES, JUMP 390 | 0283 00385 ; MONSTER's attack 391 | 392 | 0285 E86500 00386 CALL RANDOM ; USE MONSTER NUMBER AS DICE 393 | 0288 2A4601 00387 SUB AL,[BP+ARMOR] ; SUBTRACT ARMOR FROM ATTACK 394 | 028B 7205 00388 JC .4 395 | 028D 53 00389 PUSH BX 396 | 028E E8BEFF 00390 CALL SUB_HP ; SUBTRACT FROM PLAYER's HP 397 | 398 | 0291 5B 00391 POP BX 399 | 0292 00392 .4: 400 | 0292 00393 ; MOV AH,0X00 ; COMES HERE WITH AH = 0 401 | 0292 CD16 00394 INT 0X16 ; WAIT FOR A KEY. 402 | 0294 EBE3 00395 JMP .2 ; ANOTHER BATTLE ROUND. 403 | 0294 00396 404 | 0294 00397 ; 405 | 0294 00398 ; MONSTER IS DEAD 406 | 0294 00399 ; 407 | 0296 00400 .3: 408 | 0296 C605FA 00401 MOV BYTE [DI],GR_FLOOR ; REMOVE FROM SCREEN 409 | 0299 C3 00402 RET 410 | 0299 00403 411 | 0299 00404 ; 412 | 0299 00405 ; FILL A ROW ON SCREEN FOR A ROOM 413 | 0299 00406 ; 414 | 029A 51 00407 FILL: PUSH CX ; SAVE CX BECAUSE IT NEEDS CL VALUE AGAIN 415 | 029B 57 00408 PUSH DI ; SAVE VIDEO POSITION 416 | 029C E81500 00409 CALL DOOR ; LEFT BORDER 417 | 029F 88D8 00410 .1: MOV AL,BL ; FILLER 418 | 02A1 E81000 00411 CALL DOOR 419 | 02A4 FEC9 00412 DEC CL 420 | 02A6 79F7 00413 JNS .1 421 | 02A8 88F8 00414 MOV AL,BH ; RIGHT BORDER 422 | 02AA E80700 00415 CALL DOOR 423 | 02AD 5F 00416 POP DI ; RESTORE VIDEO POSITION 424 | 02AE 59 00417 POP CX ; RESTORE CX 425 | 02AF 81C7A000 00418 ADD DI,0X00A0 ; GOES TO NEXT ROW ON SCREEN 426 | 02B3 C3 00419 RET 427 | 02B3 00420 428 | 02B3 00421 ; 429 | 02B3 00422 ; DRAW A ROOM CHARACTER ON SCREEN 430 | 02B3 00423 ; 431 | 02B4 00424 DOOR: 432 | 02B4 3CFA 00425 CMP AL,GR_FLOOR ; DRAWING FLOOR? 433 | 02B6 7521 00426 JNE .3 ; NO, JUMP 434 | 02B8 53 00427 PUSH BX ; HERE BH IS EQUAL TO GR_VERT 435 | 02B9 E83100 00428 CALL RANDOM ; GET A RANDOM NUMBER 436 | 02BC 3C06 00429 CMP AL,6 ; CHANCE OF CREATING A MONSTER 437 | 02BE 730D 00430 JNC .11 438 | 02C0 024603 00431 ADD AL,[BP+LEVEL] ; MORE DIFFICULT MONSTERS AS LEVEL IS DEEPER 439 | 02C3 00432 .9: 440 | 02C3 2C05 00433 SUB AL,0X05 441 | 02C5 3C17 00434 CMP AL,0X17 ; MAKE SURE IT FITS INSIDE ASCII LETTERS 442 | 02C7 7DFA 00435 JGE .9 443 | 02C9 0444 00436 ADD AL,0X44 ; OFFSET INTO ASCII LETTERS 444 | 02CB EB0B 00437 JMP SHORT .12 445 | 02CB 00438 446 | 02CD 00439 .11: 447 | 02CD BBFC02 00440 MOV BX,ITEMS-6 ; TABLE OF ITEMS 448 | 02D0 3C0B 00441 CMP AL,11 ; CHANCE OF CREATING AN ITEM 449 | 02D2 2ED7 00442 CS XLAT 450 | 02D4 7202 00443 JB .12 451 | 02D6 B0FA 00444 MOV AL,GR_FLOOR ; SHOW ONLY FLOOR. 452 | 02D8 5B 00445 .12: POP BX 453 | 02D9 00446 .3: 454 | 02D9 3CCD 00447 CMP AL,GR_HORIZ 455 | 02DB 7404 00448 JE .1 456 | 02DD 3CBA 00449 CMP AL,GR_VERT 457 | 02DF 7507 00450 JNE .2 458 | 02E1 803DB1 00451 .1: CMP BYTE [DI],GR_TUNNEL 459 | 02E4 7502 00452 JNE .2 460 | 02E6 B0CE 00453 MOV AL,GR_DOOR 461 | 02E8 AA 00454 .2: STOSB 462 | 02E9 47 00455 INC DI 463 | 02EA C3 00456 RET 464 | 02EA 00457 465 | 02EB 00458 RANDOM6: 466 | 02EB B706 00459 MOV BH,0X06 467 | 02EB 00460 468 | 02ED 00461 RANDOM: 469 | 02ED B8A11E 00462 MOV AX,7841 470 | 02F0 F76606 00463 MUL WORD [BP+RND] 471 | 02F3 055300 00464 ADD AX,83 472 | 02F6 894606 00465 MOV [BP+RND],AX 473 | 02F6 00466 474 | 02F6 00467 ; RDTSC ; WOULD MAKE IT DEPENDENT ON PENTIUM II 475 | 02F6 00468 476 | 02F6 00469 ; IN AL,(0X40) ; ONLY WORKS FOR SLOW REQUIREMENTS. 477 | 02F6 00470 478 | 02F9 30E4 00471 XOR AH,AH 479 | 02FB F6F7 00472 DIV BH 480 | 02FD 88E0 00473 MOV AL,AH 481 | 02FF 98 00474 CBW 482 | 0300 40 00475 INC AX 483 | 0301 C3 00476 RET 484 | 0301 00477 485 | 0301 00478 ; 486 | 0301 00479 ; ITEMS 487 | 0301 00480 ; 488 | 0302 00481 ITEMS: 489 | 0302 05 00482 DB GR_FOOD 490 | 0303 0F 00483 DB GR_GOLD 491 | 0304 04 00484 DB GR_TRAP 492 | 0305 18 00485 DB GR_WEAPON 493 | 0306 08 00486 DB GR_ARMOR 494 | 0306 00487 495 | 0306 00488 %IFDEF COM_FILE 496 | 0306 00489 %ELSE 497 | 0306 00490 TIMES 510-($-$$) DB 0X4F 498 | 0306 00491 DB 0X55,0XAA ; MAKE IT A BOOTABLE SECTOR 499 | 0306 00492 %ENDIF 500 | 0306 00493 501 | 502 | 00000 ERRORS FOUND 503 | 00000 WARNINGS FOUND 504 | 505 | 01038 PROGRAM BYTES 506 | 507 | LABEL VALUE/ADDRESS 508 | 509 | ADD_HP 0251 510 | ADD_HP.1 025c 511 | ARMOR 0001 512 | ARMOR_FOUND 023f 513 | BATTLE 0271 514 | BATTLE.2 0279 515 | BATTLE.3 0296 516 | BATTLE.4 0292 517 | BOX_HEIGHT 0008 518 | BOX_MAX_HEIGHT 0006 519 | BOX_MAX_WIDTH 0017 520 | BOX_WIDTH 001a 521 | COM_FILE 0001 522 | DOOR 02b4 523 | DOOR.1 02e1 524 | DOOR.11 02cd 525 | DOOR.12 02d8 526 | DOOR.2 02e8 527 | DOOR.3 02d9 528 | DOOR.9 02c3 529 | FILL 029a 530 | FILL.1 029f 531 | FOOD_FOUND 0247 532 | GAME_LOOP 01c1 533 | GAME_LOOP.1 01c8 534 | GAME_LOOP.2 0203 535 | GAME_LOOP.3 0201 536 | GENERATE_DUNGEON 0117 537 | GENERATE_DUNGEON.0 0122 538 | GENERATE_DUNGEON.1 01be 539 | GENERATE_DUNGEON.2 01ac 540 | GENERATE_DUNGEON.3 014b 541 | GENERATE_DUNGEON.4 0151 542 | GENERATE_DUNGEON.5 0158 543 | GENERATE_DUNGEON.6 019b 544 | GENERATE_DUNGEON.7 0137 545 | GENERATE_DUNGEON.8 01b1 546 | GENERATE_DUNGEON.9 017c 547 | GR_ARMOR 0008 548 | GR_BOT_LEFT 00c8 549 | GR_BOT_RIGHT 00bc 550 | GR_DOOR 00ce 551 | GR_FLOOR 00fa 552 | GR_FOOD 0005 553 | GR_GOLD 000f 554 | GR_HERO 0001 555 | GR_HORIZ 00cd 556 | GR_LADDER 00f0 557 | GR_TOP_LEFT 00c9 558 | GR_TOP_RIGHT 00bb 559 | GR_TRAP 0004 560 | GR_TUNNEL 00b1 561 | GR_VERT 00ba 562 | GR_WEAPON 0018 563 | GR_YENDOR 000c 564 | HERO_COLOR 000e 565 | HP 0004 566 | ITEMS 0302 567 | LADDER_FOUND 023c 568 | LEVEL 0003 569 | LIGHT_COLOR 0006 570 | MOVE_CANCEL 0239 571 | MOVE_OVER 0237 572 | QUIT 023a 573 | RANDOM 02ed 574 | RANDOM6 02eb 575 | RND 0006 576 | ROW_WIDTH 00a0 577 | START 0100 578 | SUB_HP 024f 579 | TRAP_FOUND 024c 580 | WEAPON 0000 581 | WEAPON_FOUND 0243 582 | YENDOR 0002 583 | YENDOR_LEVEL 001a 584 | -------------------------------------------------------------------------------- /test/basic.asm: -------------------------------------------------------------------------------- 1 | ; 2 | ; bootBASIC interpreter in 512 bytes (boot sector) 3 | ; 4 | ; by Oscar Toledo G. 5 | ; http://nanochess.org/ 6 | ; 7 | ; (c) Copyright 2019 Oscar Toledo G. 8 | ; 9 | ; Creation date: Jul/19/2019. 10pm to 12am. 10 | ; Revision date: Jul/20/2019. 10am to 2pm. 11 | ; Added assignment statement. list now 12 | ; works. run/goto now works. Added 13 | ; system and new. 14 | ; Revision date: Jul/22/2019. Boot image now includes 'system' 15 | ; statement. 16 | ; 17 | 18 | ; 19 | ; USER'S MANUAL: 20 | ; 21 | ; Line entry is done with keyboard, finish the line with Enter. 22 | ; Only 19 characters per line as maximum. 23 | ; 24 | ; Backspace can be used, don't be fooled by the fact 25 | ; that screen isn't deleted (it's all right in the buffer). 26 | ; 27 | ; All statements must be in lowercase. 28 | ; 29 | ; Line numbers can be 1 to 999. 30 | ; 31 | ; 26 variables are available (a-z) 32 | ; 33 | ; Numbers (0-65535) can be entered and display as unsigned. 34 | ; 35 | ; To enter new program lines: 36 | ; 10 print "Hello, world!" 37 | ; 38 | ; To erase program lines: 39 | ; 10 40 | ; 41 | ; To test statements directly (interactive syntax): 42 | ; print "Hello, world!" 43 | ; 44 | ; To erase the current program: 45 | ; new 46 | ; 47 | ; To run the current program: 48 | ; run 49 | ; 50 | ; To list the current program: 51 | ; list 52 | ; 53 | ; To exit to command-line: 54 | ; system 55 | ; 56 | ; Statements: 57 | ; var=expr Assign expr value to var (a-z) 58 | ; 59 | ; print expr Print expression value, new line 60 | ; print expr; Print expression value, continue 61 | ; print "hello" Print string, new line 62 | ; print "hello"; Print string, continue 63 | ; 64 | ; input var Input value into variable (a-z) 65 | ; 66 | ; goto expr Goto to indicated line in program 67 | ; 68 | ; if expr1 goto expr2 69 | ; If expr1 is non-zero then go to line, 70 | ; else go to following line. 71 | ; 72 | ; Examples of if: 73 | ; 74 | ; if c-5 goto 20 If c isn't 5, go to line 20 75 | ; 76 | ; Expressions: 77 | ; 78 | ; The operators +, -, / and * are available with 79 | ; common precedence rules and signed operation. 80 | ; 81 | ; You can also use parentheses: 82 | ; 83 | ; 5+6*(10/2) 84 | ; 85 | ; Variables and numbers can be used in expressions. 86 | ; 87 | ; Sample program (counting 1 to 10): 88 | ; 89 | ; 10 a=1 90 | ; 20 print a 91 | ; 30 a=a+1 92 | ; 40 if a-11 goto 20 93 | ; 94 | ; Sample program (Pascal's triangle, each number is the sum 95 | ; of the two over it): 96 | ; 97 | ; 10 input n 98 | ; 20 i=1 99 | ; 30 c=1 100 | ; 40 j=0 101 | ; 50 t=n-i 102 | ; 60 if j-t goto 80 103 | ; 70 goto 110 104 | ; 80 print " "; 105 | ; 90 j=j+1 106 | ; 100 goto 50 107 | ; 110 k=1 108 | ; 120 if k-i-1 goto 140 109 | ; 130 goto 190 110 | ; 140 print c; 111 | ; 150 c=c*(i-k)/k 112 | ; 160 print " "; 113 | ; 170 k=k+1 114 | ; 180 goto 120 115 | ; 190 print 116 | ; 200 i=i+1 117 | ; 210 if i-n-1 goto 30 118 | ; 119 | 120 | cpu 8086 121 | 122 | %ifndef com_file ; If not defined create a boot sector 123 | com_file: equ 0 124 | %endif 125 | 126 | %if com_file 127 | org 0x0100 128 | %else 129 | org 0x7c00 130 | %endif 131 | 132 | vars: equ 0x7e00 ; Variables (multiple of 256) 133 | running: equ 0x7e7e ; Running status 134 | line: equ 0x7e80 ; Line input 135 | program: equ 0x7f00 ; Program address 136 | stack: equ 0xff00 ; Stack address 137 | max_line: equ 1000 ; First unavailable line number 138 | max_length: equ 20 ; Maximum length of line 139 | max_size: equ max_line*max_length ; Max. program size 140 | 141 | start: 142 | %if com_file 143 | %else 144 | push cs ; For boot sector 145 | push cs ; it needs to setup 146 | push cs ; DS, ES and SS. 147 | pop ds 148 | pop es 149 | pop ss 150 | %endif 151 | cld ; Clear Direction flag 152 | mov di,program ; Point to program 153 | mov al,0x0d ; Fill with CR 154 | mov cx,max_size ; Max. program size 155 | rep stosb ; Initialize 156 | 157 | ; 158 | ; Main loop 159 | ; 160 | main_loop: 161 | mov sp,stack ; Reinitialize stack pointer 162 | mov ax,main_loop 163 | push ax 164 | xor ax,ax ; Mark as interactive 165 | mov [running],ax 166 | mov al,'>' ; Show prompt 167 | call input_line ; Accept line 168 | call input_number ; Get number 169 | or ax,ax ; No number or zero? 170 | je statement ; Yes, jump 171 | call find_line ; Find the line 172 | xchg ax,di 173 | ; mov cx,max_length ; CX loaded with this value in 'find_line' 174 | rep movsb ; Copy entered line into program 175 | ret 176 | 177 | ; 178 | ; Handle 'if' statement 179 | ; 180 | if_statement: 181 | call expr ; Process expression 182 | or ax,ax ; Is it zero? 183 | je f6 ; Yes, return (ignore if) 184 | statement: 185 | call spaces ; Avoid spaces 186 | cmp byte [si],0x0d ; Empty line? 187 | je f6 ; Yes, return 188 | mov di,statements ; Point to statements list 189 | f5: mov al,[di] ; Read length of the string 190 | inc di ; Avoid length byte 191 | and ax,0x00ff ; Is it zero? 192 | je f4 ; Yes, jump 193 | xchg ax,cx 194 | push si ; Save current position 195 | f16: rep cmpsb ; Compare statement 196 | jne f3 ; Equal? No, jump 197 | pop ax 198 | call spaces ; Avoid spaces 199 | jmp word [di] ; Jump to process statement 200 | 201 | f3: add di,cx ; Advance the list pointer 202 | inc di ; Avoid the address 203 | inc di 204 | pop si 205 | jmp f5 ; Compare another statement 206 | 207 | f4: call get_variable ; Try variable 208 | push ax ; Save address 209 | lodsb ; Read a line letter 210 | cmp al,'=' ; Is it assignment '=' ? 211 | je assignment ; Yes, jump to assignment. 212 | 213 | ; 214 | ; An error happened 215 | ; 216 | error: 217 | mov si,error_message 218 | call print_2 ; Show error message 219 | jmp main_loop ; Exit to main loop 220 | 221 | error_message: 222 | db "@#!",0x0d ; Guess the words :P 223 | 224 | ; 225 | ; Handle 'list' statement 226 | ; 227 | list_statement: 228 | xor ax,ax ; Start from line zero 229 | f29: push ax 230 | call find_line ; Find program line 231 | xchg ax,si 232 | cmp byte [si],0x0d ; Empty line? 233 | je f30 ; Yes, jump 234 | pop ax 235 | push ax 236 | call output_number ; Show line number 237 | f32: lodsb ; Show line contents 238 | call output 239 | jne f32 ; Jump if it wasn't 0x0d (CR) 240 | f30: pop ax 241 | inc ax ; Go to next line 242 | cmp ax,max_line ; Finished? 243 | jne f29 ; No, continue 244 | f6: 245 | ret 246 | 247 | ; 248 | ; Handle 'input' statement 249 | ; 250 | input_statement: 251 | call get_variable ; Get variable address 252 | push ax ; Save it 253 | mov al,'?' ; Prompt 254 | call input_line ; Wait for line 255 | ; 256 | ; Second part of the assignment statement 257 | ; 258 | assignment: 259 | call expr ; Process expression 260 | pop di 261 | stosw ; Save onto variable 262 | ret 263 | 264 | ; 265 | ; Handle an expression. 266 | ; First tier: addition & subtraction. 267 | ; 268 | expr: 269 | call expr1 ; Call second tier 270 | f20: cmp byte [si],'-' ; Subtraction operator? 271 | je f19 ; Yes, jump 272 | cmp byte [si],'+' ; Addition operator? 273 | jne f6 ; No, return 274 | push ax 275 | call expr1_2 ; Call second tier 276 | f15: pop cx 277 | add ax,cx ; Addition 278 | jmp f20 ; Find more operators 279 | 280 | f19: 281 | push ax 282 | call expr1_2 ; Call second tier 283 | neg ax ; Negate it (a - b converted to a + -b) 284 | jmp f15 285 | 286 | ; 287 | ; Handle an expression. 288 | ; Second tier: division & multiplication. 289 | ; 290 | expr1_2: 291 | inc si ; Avoid operator 292 | expr1: 293 | call expr2 ; Call third tier 294 | f21: cmp byte [si],'/' ; Division operator? 295 | je f23 ; Yes, jump 296 | cmp byte [si],'*' ; Multiplication operator? 297 | jne f6 ; No, return 298 | 299 | push ax 300 | call expr2_2 ; Call third tier 301 | pop cx 302 | imul cx ; Multiplication 303 | jmp f21 ; Find more operators 304 | 305 | f23: 306 | push ax 307 | call expr2_2 ; Call third tier 308 | pop cx 309 | xchg ax,cx 310 | cwd ; Expand AX to DX:AX 311 | idiv cx ; Signed division 312 | jmp f21 ; Find more operators 313 | 314 | ; 315 | ; Handle an expression. 316 | ; Third tier: parentheses, numbers and vars. 317 | ; 318 | expr2_2: 319 | inc si ; Avoid operator 320 | expr2: 321 | call spaces ; Jump spaces 322 | lodsb ; Read character 323 | cmp al,'(' ; Open parenthesis? 324 | jne f24 325 | call expr ; Process inner expr. 326 | cmp byte [si],')' ; Closing parenthesis? 327 | je spaces_2 ; Yes, avoid spaces 328 | jmp error ; No, jump to error 329 | 330 | f24: cmp al,0x40 ; Variable? 331 | jnc f25 ; Yes, jump 332 | dec si ; Back one letter... 333 | call input_number ; ...to read number 334 | jmp spaces ; Avoid spaces 335 | 336 | f25: call get_variable_2 ; Get variable address 337 | xchg ax,bx 338 | mov ax,[bx] ; Read 339 | ret ; Return 340 | 341 | ; 342 | ; Get variable address 343 | ; 344 | get_variable: 345 | lodsb ; Read source 346 | get_variable_2: 347 | and al,0x1f ; 0x61-0x7a -> 0x01-0x1a 348 | add al,al ; x 2 (each variable = word) 349 | mov ah,vars>>8 ; Setup high-byte of address 350 | ; 351 | ; Avoid spaces 352 | ; 353 | spaces: 354 | cmp byte [si],' ' ; Space found? 355 | jne f22 ; No, return 356 | ; 357 | ; Avoid spaces after current character 358 | ; 359 | spaces_2: 360 | inc si ; Advance to next character 361 | jmp spaces 362 | 363 | ; 364 | ; Output unsigned number 365 | ; AX = value 366 | ; 367 | output_number: 368 | f26: 369 | xor dx,dx ; DX:AX 370 | mov cx,10 ; Divisor = 10 371 | div cx ; Divide 372 | or ax,ax ; Nothing at left? 373 | push dx 374 | je f8 ; No, jump 375 | call f26 ; Yes, output left side 376 | f8: pop ax 377 | add al,'0' ; Output remainder as... 378 | jmp output ; ...ASCII digit 379 | 380 | ; 381 | ; Read number in input 382 | ; AX = result 383 | ; 384 | input_number: 385 | xor bx,bx ; BX = 0 386 | f11: lodsb ; Read source 387 | sub al,'0' 388 | cmp al,10 ; Digit valid? 389 | cbw 390 | xchg ax,bx 391 | jnc f12 ; No, jump 392 | mov cx,10 ; Multiply by 10 393 | mul cx 394 | add bx,ax ; Add new digit 395 | jmp f11 ; Continue 396 | 397 | f12: dec si ; SI points to first non-digit 398 | f22: 399 | ret 400 | 401 | ; 402 | ; Handle 'system' statement 403 | ; 404 | system_statement: 405 | int 0x20 406 | 407 | ; 408 | ; Handle 'goto' statement 409 | ; 410 | goto_statement: 411 | call expr ; Handle expression 412 | db 0xb9 ; MOV CX to jump over XOR AX,AX 413 | 414 | ; 415 | ; Handle 'run' statement 416 | ; (equivalent to 'goto 0') 417 | ; 418 | run_statement: 419 | xor ax,ax 420 | f10: 421 | call find_line ; Find line in program 422 | f27: cmp word [running],0 ; Already running? 423 | je f31 424 | mov [running],ax ; Yes, target is new line 425 | ret 426 | f31: 427 | push ax 428 | pop si 429 | add ax,max_length ; Point to next line 430 | mov [running],ax ; Save for next time 431 | call statement ; Process current statement 432 | mov ax,[running] 433 | cmp ax,program+max_size ; Reached the end? 434 | jne f31 ; No, continue 435 | ret ; Yes, return 436 | 437 | ; 438 | ; Find line in program 439 | ; Entry: 440 | ; ax = line number 441 | ; Result: 442 | ; ax = pointer to program 443 | find_line: 444 | mov cx,max_length 445 | mul cx 446 | add ax,program 447 | ret 448 | 449 | ; 450 | ; Input line from keyboard 451 | ; Entry: 452 | ; al = prompt character 453 | ; Result: 454 | ; buffer 'line' contains line, finished with CR 455 | ; SI points to 'line'. 456 | ; 457 | input_line: 458 | call output 459 | mov si,line 460 | push si 461 | pop di ; Target for writing line 462 | f1: call input_key ; Read keyboard 463 | stosb ; Save key in buffer 464 | cmp al,0x08 ; Backspace? 465 | jne f2 ; No, jump 466 | dec di ; Get back one character 467 | dec di 468 | f2: cmp al,0x0d ; CR pressed? 469 | jne f1 ; No, wait another key 470 | ret ; Yes, return 471 | 472 | ; 473 | ; Handle "print" statement 474 | ; 475 | print_statement: 476 | lodsb ; Read source 477 | cmp al,0x0d ; End of line? 478 | je new_line ; Yes, generate new line and return 479 | cmp al,'"' ; Double quotes? 480 | jne f7 ; No, jump 481 | print_2: 482 | f9: 483 | lodsb ; Read string contents 484 | cmp al,'"' ; Double quotes? 485 | je f18 ; Yes, jump 486 | call output ; Output character 487 | jne f9 ; Jump if not finished with 0x0d (CR) 488 | ret ; Return 489 | 490 | f7: dec si 491 | call expr ; Handle expression 492 | call output_number ; Output result 493 | f18: lodsb ; Read next character 494 | cmp al,';' ; Is it semicolon? 495 | jne new_line ; No, jump to generate new line 496 | ret ; Yes, return 497 | 498 | ; 499 | ; Read a key into al 500 | ; Also outputs it to screen 501 | ; 502 | input_key: 503 | mov ah,0x00 504 | int 0x16 505 | ; 506 | ; Screen output of character contained in al 507 | ; Expands 0x0d (CR) into 0x0a 0x0d (LF CR) 508 | ; 509 | output: 510 | cmp al,0x0d 511 | jne f17 512 | ; 513 | ; Go to next line (generates LF+CR) 514 | ; 515 | new_line: 516 | mov al,0x0a 517 | call f17 518 | mov al,0x0d 519 | f17: 520 | mov ah,0x0e 521 | mov bx,0x0007 522 | int 0x10 523 | cmp al,0x0d 524 | ret 525 | 526 | ; 527 | ; List of statements of bootBASIC 528 | ; First one byte with length of string 529 | ; Then string with statement 530 | ; Then a word with the address of the code 531 | ; 532 | statements: 533 | db 3,"new" 534 | dw start 535 | 536 | db 4,"list" 537 | dw list_statement 538 | 539 | db 3,"run" 540 | dw run_statement 541 | 542 | db 5,"print" 543 | dw print_statement 544 | 545 | db 5,"input" 546 | dw input_statement 547 | 548 | db 2,"if" 549 | dw if_statement 550 | 551 | db 4,"goto" 552 | dw goto_statement 553 | 554 | db 6,"system" 555 | dw system_statement 556 | 557 | db 0 558 | 559 | ; 560 | ; Boot sector filler 561 | ; 562 | %if com_file 563 | %else 564 | times 510-($-$$) db 0x4f 565 | db 0x55,0xaa ; Make it a bootable sector 566 | %endif 567 | 568 | -------------------------------------------------------------------------------- /test/bricks.asm: -------------------------------------------------------------------------------- 1 | ; 2 | ; Bricks game in one boot sector 3 | ; 4 | ; by Oscar Toledo G. 5 | ; 6 | ; Creation date: Nov/02/2019. 7 | ; 8 | 9 | cpu 8086 10 | 11 | ; 12 | ; Press Left Shift to start the game 13 | ; Press Left Ctrl to move the paddle to left 14 | ; Press Left Alt to move the paddle to right 15 | ; 16 | 17 | %ifdef com_file 18 | org 0x0100 19 | %else 20 | org 0x7c00 21 | %endif 22 | 23 | old_time: equ 16 ; Old time 24 | ball_x: equ 14 ; X-coordinate of ball (8.8 fraction) 25 | ball_y: equ 12 ; Y-coordinate of ball (8.8 fraction) 26 | ball_xs: equ 10 ; X-speed of ball (8.8 fraction) 27 | ball_ys: equ 8 ; Y-speed of ball (8.8 fraction) 28 | beep: equ 6 ; Frame count to turn off sound 29 | bricks: equ 4 ; Remaining bricks 30 | balls: equ 2 ; Remaining balls 31 | score: equ 0 ; Current score 32 | 33 | ; 34 | ; Start of the game 35 | ; 36 | start: 37 | mov ax,0x0002 ; Text mode 80x25x16 colors 38 | int 0x10 ; Setup 39 | mov ax,0xb800 ; Address of video screen 40 | mov ds,ax ; Setup DS 41 | mov es,ax ; Setup ES 42 | sub sp,32 43 | xor ax,ax 44 | push ax ; Reset score 45 | mov al,4 46 | push ax ; Balls remaining 47 | mov bp,sp ; Setup stack frame for globals 48 | ; 49 | ; Start another level 50 | ; 51 | another_level: 52 | mov word [bp+bricks],273 ; 273 bricks on screen 53 | xor di,di 54 | mov ax,0x01b1 ; Draw top border 55 | mov cx,80 56 | cld 57 | rep stosw 58 | mov cl,24 ; 24 rows 59 | .1: 60 | stosw ; Draw left border 61 | mov ax,0x20 ; No bricks on this row 62 | push cx 63 | cmp cl,23 64 | jnb .2 65 | sub cl,15 66 | jbe .2 67 | mov al,0xdb ; Bricks on this row 68 | mov ah,cl 69 | .2: 70 | mov cl,39 ; 39 bricks per row 71 | .3: 72 | stosw 73 | stosw 74 | inc ah ; Increase attribute color 75 | cmp ah,0x08 76 | jne .4 77 | mov ah,0x01 78 | .4: 79 | loop .3 80 | pop cx 81 | 82 | mov ax,0x01b1 ; Draw right border 83 | stosw 84 | loop .1 85 | 86 | ; 87 | ; Start another ball 88 | ; 89 | mov di,0x0f4a ; Position of paddle 90 | another_ball: 91 | mov byte [bp+ball_x+1],0x28 ; Center X 92 | mov byte [bp+ball_y+1],0x14 ; Center Y 93 | xor ax,ax 94 | mov [bp+ball_xs],ax ; Static on screen 95 | mov [bp+ball_ys],ax 96 | mov byte [bp+beep],0x01 97 | 98 | mov si,0x0ffe ; Don't erase ball yet 99 | game_loop: 100 | call wait_frame ; Wait 1/18.2 secs. 101 | 102 | mov word [si],0x0000 ; Erase ball 103 | 104 | call update_score ; Update score 105 | 106 | mov ah,0x02 ; Read modifier keys 107 | int 0x16 108 | test al,0x04 ; Left ctrl 109 | je .1 110 | mov byte [di+6],0 ; Erase right side of paddle 111 | mov byte [di+8],0 112 | sub di,byte 4 ; Move paddle to left 113 | cmp di,0x0f02 ; Limit 114 | ja .1 115 | mov di,0x0f02 116 | .1: 117 | test al,0x08 ; Left alt 118 | je .2 119 | xor ax,ax ; Erase left side of paddle 120 | stosw 121 | stosw ; DI increased automatically 122 | cmp di,0x0f94 ; Limit 123 | jb .2 124 | mov di,0x0f94 125 | .2: 126 | test al,0x02 ; Left shift 127 | je .15 128 | mov ax,[bp+ball_xs] ; Ball moving? 129 | add ax,[bp+ball_ys] 130 | jne .15 ; Yes, jump 131 | ; Setup movement of ball 132 | mov word [bp+ball_xs],0xff40 133 | mov word [bp+ball_ys],0xff80 134 | .15: 135 | mov ax,0x0adf ; Paddle graphic and color 136 | push di 137 | stosw ; Draw paddle 138 | stosw 139 | stosw 140 | stosw 141 | stosw 142 | pop di 143 | 144 | mov bx,[bp+ball_x] ; Draw ball 145 | mov ax,[bp+ball_y] 146 | call locate_ball ; Locate on screen 147 | test byte [bp+ball_y],0x80 ; Y-coordinate half fraction? 148 | mov ah,0x60 ; Interchange colors for smooth mov. 149 | je .12 150 | mov ah,0x06 151 | .12: mov al,0xdc ; Graphic 152 | mov [bx],ax ; Draw 153 | push bx 154 | pop si 155 | 156 | .14: 157 | mov bx,[bp+ball_x] ; Ball position 158 | mov ax,[bp+ball_y] 159 | add bx,[bp+ball_xs] ; Add movement speed 160 | add ax,[bp+ball_ys] 161 | push ax 162 | push bx 163 | call locate_ball ; Locate on screen 164 | mov al,[bx] 165 | cmp al,0xb1 ; Touching borders 166 | jne .3 167 | mov cx,5423 ; 1193180 / 220 168 | call speaker ; Generate sound 169 | pop bx 170 | pop ax 171 | cmp bh,0x4f 172 | je .8 173 | test bh,bh 174 | jne .7 175 | .8: 176 | neg word [bp+ball_xs] ; Negate X-speed if touches sides 177 | .7: 178 | test ah,ah 179 | jnz .9 180 | neg word [bp+ball_ys] ; Negate Y-speed if touches sides 181 | .9: jmp .14 182 | 183 | .3: 184 | cmp al,0xdf ; Touching paddle 185 | jne .4 186 | sub bx,di ; Subtract paddle position 187 | sub bx,byte 4 188 | mov cl,6 ; Multiply by 64 189 | shl bx,cl 190 | mov [bp+ball_xs],bx ; New X speed for ball 191 | mov word [bp+ball_ys],0xff80 ; Update Y speed for ball 192 | mov cx,2711 ; 1193180 / 440 193 | call speaker ; Generate sound 194 | pop bx 195 | pop ax 196 | jmp .14 197 | 198 | .4: 199 | cmp al,0xdb ; Touching brick 200 | jne .5 201 | mov cx,1355 ; 1193180 / 880 202 | call speaker ; Generate sound 203 | test bl,2 ; Aligned with brick? 204 | jne .10 ; Yes, jump 205 | dec bx ; Align 206 | dec bx 207 | .10: xor ax,ax ; Erase brick 208 | mov [bx],ax 209 | mov [bx+2],ax 210 | inc word [bp+score] ; Increase score 211 | neg word [bp+ball_ys] ; Negate Y speed (rebound) 212 | pop bx 213 | pop ax 214 | dec word [bp+bricks] ; One brick less on screen 215 | jne .14 ; Fully completed? No, jump. 216 | jmp another_level ; Start another level 217 | 218 | .5: 219 | pop bx 220 | pop ax 221 | .6: 222 | mov [bp+ball_x],bx ; Update ball position 223 | mov [bp+ball_y],ax 224 | cmp ah,0x19 ; Ball exited through bottom? 225 | je ball_lost ; Yes, jump 226 | jmp game_loop ; No, repeat game loop 227 | 228 | ; 229 | ; Ball lost 230 | ; 231 | ball_lost: 232 | mov cx,10846 ; 1193180 / 110 233 | call speaker ; Generate sound 234 | 235 | mov word [si],0 ; Erase ball 236 | dec byte [bp+balls] ; One ball less 237 | js .1 ; All finished? Yes, jump 238 | jmp another_ball ; Start another ball 239 | 240 | .1: call wait_frame.2 ; Turn off sound 241 | int 0x20 ; Exit to DOS / bootOS 242 | 243 | wait_frame: 244 | .0: 245 | mov ah,0x00 ; Read ticks 246 | int 0x1a ; Call BIOS 247 | cmp dx,[bp+old_time] ; Wait for change 248 | je .0 249 | mov [bp+old_time],dx 250 | 251 | dec byte [bp+beep] ; Decrease time to turn off beep 252 | jne .1 253 | .2: 254 | in al,0x61 255 | and al,0xfc ; Turn off 256 | out 0x61,al 257 | .1: 258 | 259 | ret 260 | 261 | ; 262 | ; Generate sound on PC speaker 263 | ; 264 | speaker: 265 | mov al,0xb6 ; Setup timer 2 266 | out 0x43,al 267 | mov al,cl ; Low byte of timer count 268 | out 0x42,al 269 | mov al,ch ; High byte of timer count 270 | out 0x42,al 271 | in al,0x61 272 | or al,0x03 ; Connect PC speaker to timer 2 273 | out 0x61,al 274 | mov byte [bp+beep],3 ; Duration 275 | ret 276 | 277 | ; 278 | ; Locate ball on screen 279 | ; 280 | locate_ball: 281 | mov al,0xa0 282 | mul ah ; AH = Y coordinate (row) 283 | mov bl,bh ; BH = X coordinate (column) 284 | mov bh,0 285 | shl bx,1 286 | add bx,ax 287 | ret 288 | 289 | ; 290 | ; Update score indicator (from bootRogue) 291 | ; 292 | update_score: 293 | mov bx,0x0f98 ; Point to bottom right corner 294 | mov ax,[bp+score] 295 | call .1 296 | mov al,[bp+balls] 297 | .1: 298 | xor cx,cx ; CX = Quotient 299 | .2: inc cx 300 | sub ax,10 ; Division by subtraction 301 | jnc .2 302 | add ax,0x0a3a ; Convert remainder to ASCII digit + color 303 | call .3 ; Put on screen 304 | xchg ax,cx 305 | dec ax ; Quotient is zero? 306 | jnz .1 ; No, jump to show more digits. 307 | 308 | .3: mov [bx],ax 309 | dec bx 310 | dec bx 311 | ret 312 | 313 | %ifdef com_file 314 | %else 315 | times 510-($-$$) db 0x4f 316 | db 0x55,0xaa ; Make it a bootable sector 317 | %endif 318 | -------------------------------------------------------------------------------- /test/doom.asm: -------------------------------------------------------------------------------- 1 | ; 2 | ; CubicDoom 3 | ; 4 | ; by Oscar Toledo G. 5 | ; 6 | ; Creation date: Nov/21/2019. 7 | ; Revision date: Nov/22/2019. Now working. 8 | ; Revision date: Nov/23/2019. Optimized. 9 | ; Revision date: Nov/24/2019. Builds a world. Added evil cubes, and 10 | ; can shoot them. 517 bytes. 11 | ; Revision date: Nov/25/2019. Optimized last bytes. 509 bytes. 12 | ; 13 | 14 | ; 15 | ; Tricks used: 16 | ; o "Slow" ray-casting so doesn't matter if hits horizontal or 17 | ; vertical wall. 18 | ; 19 | 20 | cpu 8086 21 | 22 | EMPTY: equ 0x00 ; Code for empty space 23 | WALL: equ 0x80 ; Code for wall 24 | ENEMY: equ 0xc0 ; Code for enemy, includes shot count 25 | 26 | %ifdef com_file 27 | org 0x0100 28 | %else 29 | org 0x7c00 30 | %endif 31 | 32 | down: equ 0x000b ; Enemies down 33 | shot: equ 0x000a ; Shot made 34 | rnd: equ 0x0008 ; Random number 35 | px: equ 0x0006 ; Current X position (4.12) 36 | py: equ 0x0004 ; Current Y position (4.12) 37 | pa: equ 0x0002 ; Current screen angle 38 | oldtim: equ 0x0000 ; Old time 39 | 40 | maze: equ 0xff00 ; Location of maze (16x16) 41 | 42 | ; 43 | ; Start of the game 44 | ; 45 | start: 46 | mov ax,0x0013 ; Graphics mode 320x200x256 colors 47 | int 0x10 ; Setup video mode 48 | mov ax,0xa000 ; Point to video memory. 49 | mov ds,ax 50 | mov es,ax 51 | restart: 52 | cld 53 | xor cx,cx 54 | push cx ; shot+down 55 | in ax,0x40 56 | push ax ; rnd 57 | mov ah,0x18 ; Start point at maze 58 | push ax ; px 59 | push ax ; py 60 | mov cl,0x04 61 | push cx ; pa 62 | push cx ; oldtim 63 | mov bp,sp ; Setup BP to access variables 64 | 65 | mov bx,maze ; Point to maze 66 | .0: mov al,bl 67 | add al,0x11 ; Right and bottom borders at zero 68 | cmp al,0x22 ; Inside any border? 69 | jb .5 ; Yes, jump 70 | and al,0x0e ; Inside left/right border? 71 | mov al,EMPTY 72 | jne .4 ; No, jump 73 | .5: mov al,WALL 74 | .4: mov [bx],al ; Put into maze 75 | inc bx ; Next square 76 | jne .0 ; If BX is zero, maze completed 77 | 78 | mov cl,12 ; 12 walls and enemies 79 | mov [bp+down],cl ; Take note of enemies down 80 | mov di,maze+34 ; Point to center of maze 81 | mov dl,12 ; Modulo 12 for random number 82 | .2: 83 | call random 84 | mov byte [di+bx],WALL ; Setup a wall 85 | call random 86 | mov byte [di+bx],ENEMY ; Setup an enemy 87 | add di,byte 16 ; Go to next row of maze 88 | loop .2 ; Repeat until filled 89 | game_loop: 90 | call wait_frame ; Wait a frame 91 | 92 | and dl,31 ; 32 frames have passed? 93 | jnz .16 ; No, jump 94 | ; 95 | ; Move cubes 96 | ; 97 | call get_dir ; Get player position, also SI=0 98 | call get_pos ; Convert position to maze address 99 | mov cx,bx ; Save into CX 100 | 101 | mov bl,0 ; BH already ready, start at corner 102 | 103 | .17: cmp byte [bx],ENEMY 104 | jb .18 105 | cmp bx,cx ; Cube over player? 106 | jne .25 ; No, jump 107 | ; 108 | ; Handle death 109 | ; 110 | .22: 111 | mov byte [si],0x0c ; Blood pixel 112 | add si,byte 23 ; Advance by prime number 113 | .23: 114 | je restart ; Zero = full loop, restart game. 115 | jnb .22 ; Carry = one fill complete. 116 | push si 117 | call wait_frame ; Wait a frame (for fast machines) 118 | pop si 119 | jmp .22 ; Continue 120 | 121 | .25: 122 | mov di,bx 123 | mov al,bl 124 | mov ah,cl 125 | mov dx,0x0f0f ; Extract columns 126 | and dx,ax 127 | and ax,0xf0f0 ; Extract rows 128 | cmp ah,al ; Same row? 129 | je .19 ; Yes, jump 130 | lea di,[bx+0x10] ; Cube moves down 131 | jnb .19 132 | lea di,[bx-0x10] ; Cube moves up 133 | .19: cmp dh,dl ; Same column? 134 | je .20 ; Yes, jump 135 | dec di ; Cube goes left 136 | jb .20 137 | inc di ; Cube goes right 138 | inc di 139 | .20: cmp byte [di],0 ; Can move? 140 | jne .18 ; No, jump. 141 | mov al,[bx] ; Take cube 142 | mov byte [bx],0 ; Erase origin 143 | stosb ; Put into new place 144 | .18: 145 | inc bx ; Continue searching the maze... 146 | jne .17 ; ...until the end 147 | 148 | .16: 149 | 150 | ; 151 | ; Draw 3D view 152 | ; 153 | mov di,39 ; Column number is 39 154 | .2: 155 | lea ax,[di-20] ; Almost 60 degrees to left 156 | add ax,[bp+pa] ; Get vision angle 157 | call get_dir ; Get position and direction 158 | .3: 159 | call read_maze ; Verify wall hit 160 | jnc .3 ; Continue if it was open space 161 | 162 | .4: 163 | mov cx,0x1204 ; Add grayscale color set... 164 | ; ...also load CL with 4. (division by 16) 165 | jz .24 ; Jump if normal wall 166 | mov ch,32 ; Rainbow 167 | 168 | cmp di,byte 20 169 | jne .24 ; Jump if not at center 170 | cmp byte [bp+shot],1 171 | je .24 ; Jump if not shooting 172 | call get_pos 173 | inc byte [bx] ; Increase cube hits 174 | cmp byte [bx],ENEMY+3 ; 3 hits? 175 | jne .24 ; No, jump 176 | mov byte [bx],0 ; Yes, remove. 177 | dec byte [bp+down] ; One cube less 178 | je .23 ; Zero means to get another level 179 | .24: 180 | lea ax,[di+12] ; Get cos(-30) to cos(30) 181 | call get_sin ; Get cos (8 bit fraction) 182 | mul si ; Correct wall distance to... 183 | mov bl,ah ; ...avoid fishbowl effect 184 | mov bh,dl ; Divide by 256 185 | inc bx ; Avoid zero value 186 | 187 | mov ax,0x0800 ; Constant for projection plane 188 | cwd 189 | div bx ; Divide 190 | cmp ax,198 ; Limit to screen height 191 | jb .14 192 | mov ax,198 193 | .14: mov si,ax ; Height of wall 194 | 195 | shr ax,cl ; Divide distance by 16 196 | add al,ch ; Add palette index 197 | xchg ax,bx ; Put into BX 198 | 199 | push di 200 | dec cx ; CL=3. Multiply column by 8 pixels 201 | shl di,cl 202 | 203 | mov ax,200 ; Height of screen... 204 | sub ax,si ; ...minus wall height 205 | shr ax,1 ; Divide by 2 206 | 207 | push ax 208 | push si 209 | xchg ax,cx 210 | mov al,[bp+shot] ; Ceil color 211 | call fill_column 212 | xchg ax,bx ; Wall color 213 | pop cx 214 | call fill_column 215 | mov al,0x03 ; Floor color (a la Wolfenstein) 216 | pop cx 217 | call fill_column 218 | pop di 219 | dec di ; Decrease column 220 | jns .2 ; Completed? No, jump. 221 | 222 | mov ah,0x02 ; Service 0x02 = Read modifier keys 223 | int 0x16 ; Call BIOS 224 | 225 | mov bx,[bp+pa] ; Get current angle 226 | test al,0x04 ; Left Ctrl pressed? 227 | je .8 228 | dec bx ; Decrease angle 229 | dec bx 230 | .8: 231 | test al,0x08 ; Left Alt pressed? 232 | je .9 233 | inc bx ; Increase angle 234 | inc bx 235 | .9: 236 | mov ah,1 ; No shot 237 | test al,0x01 ; Right shift pressed? 238 | je .11 239 | test bh,0x01 ; But not before? 240 | jne .11 241 | mov ah,7 ; Indicate shot 242 | 243 | .11: mov [bp+shot],ah 244 | mov bh,al 245 | mov [bp+pa],bx ; Update angle 246 | 247 | test al,0x02 ; Left shift pressed? 248 | je .10 249 | xchg ax,bx ; Put angle into AX 250 | call get_dir ; Get position and direction 251 | .5: call read_maze ; Move and check for wall hit 252 | jc .10 ; Hit, jump without updating position. 253 | cmp si,4 ; Four times (the speed) 254 | jne .5 255 | 256 | mov [bp+px],dx ; Update X position 257 | mov [bp+py],bx ; Update Y position 258 | .10: 259 | jmp game_loop ; Repeat game loop 260 | 261 | ; 262 | ; Get a direction vector 263 | ; 264 | get_dir: 265 | xor si,si ; Wall distance = 0 266 | mov dx,[bp+px] ; Get X position 267 | push ax 268 | call get_sin ; Get sine 269 | xchg ax,cx ; Onto DX 270 | pop ax 271 | add al,32 ; Add 90 degrees to get cosine 272 | ; 273 | ; Get sine 274 | ; 275 | get_sin: 276 | test al,64 ; Angle >= 180 degrees? 277 | pushf 278 | test al,32 ; Angle 90-179 or 270-359 degrees? 279 | je .2 280 | xor al,31 ; Invert bits (reduces table) 281 | .2: 282 | and ax,31 ; Only 90 degrees in table 283 | mov bx,sin_table 284 | cs xlat ; Get fraction 285 | popf 286 | je .1 ; Jump if angle less than 180 287 | neg ax ; Else negate result 288 | .1: 289 | mov bx,[bp+py] ; Get Y position 290 | ret 291 | 292 | ; 293 | ; Read maze 294 | ; 295 | read_maze: 296 | inc si ; Count distance to wall 297 | add dx,cx ; Move X 298 | add bx,ax ; Move Y 299 | push bx 300 | push cx 301 | call get_pos 302 | mov bl,[bx] ; Read maze byte 303 | shl bl,1 ; Carry = 1 = wall, Zero = Wall 0 / 1 304 | pop cx 305 | pop bx 306 | ret ; Return 307 | 308 | ; 309 | ; Convert coordinates to position 310 | ; 311 | get_pos: 312 | mov bl,dh ; X-coordinate 313 | mov cl,0x04 ; Divide by 4096 314 | shr bl,cl 315 | and bh,0xf0 ; Y-coordinate / 4096 * 16 316 | or bl,bh ; Translate to maze array 317 | mov bh,maze>>8 318 | ret 319 | 320 | ; 321 | ; Fill a screen column 322 | ; 323 | fill_column: 324 | mov ah,al ; Duplicate pixel value 325 | .1: 326 | stosw ; Draw 2 pixels 327 | stosw ; Draw 2 pixels 328 | stosw ; Draw 2 pixels 329 | stosw ; Draw 2 pixels 330 | add di,0x0138 ; Go to next row 331 | loop .1 ; Repeat until fully drawn 332 | ret ; Return 333 | 334 | ; 335 | ; Generate a pseudo-random number (from bootRogue) 336 | ; 337 | random: 338 | mov al,251 339 | mul byte [bp+rnd] 340 | add al,83 341 | mov [bp+rnd],al 342 | mov ah,0 343 | div dl 344 | mov bl,ah 345 | mov bh,0 346 | ret 347 | 348 | ; 349 | ; Wait a frame (18.2 hz) 350 | ; 351 | wait_frame: 352 | .1: 353 | mov ah,0x00 ; Get ticks 354 | int 0x1a ; Call BIOS time service 355 | cmp dx,[bp+oldtim] ; Same as old time? 356 | je .1 ; Yes, wait. 357 | mov [bp+oldtim],dx 358 | ret 359 | 360 | ; 361 | ; Sine table (0.8 format) 362 | ; 363 | ; 32 bytes are 90 degrees. 364 | ; 365 | sin_table: 366 | db 0x00,0x09,0x16,0x24,0x31,0x3e,0x47,0x53 367 | db 0x60,0x6c,0x78,0x80,0x8b,0x96,0xa1,0xab 368 | db 0xb5,0xbb,0xc4,0xcc,0xd4,0xdb,0xe0,0xe6 369 | db 0xec,0xf1,0xf5,0xf7,0xfa,0xfd,0xff,0xff 370 | 371 | %ifdef com_file 372 | %else 373 | times 510-($-$$) db 0x4f 374 | db 0x55,0xaa ; Make it a bootable sector 375 | %endif 376 | 377 | -------------------------------------------------------------------------------- /test/fbird.asm: -------------------------------------------------------------------------------- 1 | ; 2 | ; F-bird text game in a bootsector 3 | ; 4 | ; by Oscar Toledo G. 5 | ; http://nanochess.org/ 6 | ; 7 | ; Creation date: Jun/04/2017. A messy unoptimized thing. 8 | ; Revision date: Jun/05/2017. Better usage of graphic charset. 9 | ; Removed a non-8088 long jump. Added 10 | ; sound. Solved bug when overwriting 11 | ; previous score. 12 | ; 13 | 14 | use16 15 | 16 | mov ax,0x0002 ; Set 80x25 text mode 17 | int 0x10 ; Call BIOS 18 | cld ; Reset direction flag (so stosw increments registers) 19 | mov ax,0xb800 ; Point to video segment 20 | mov ds,ax ; Both the source (common access) 21 | mov es,ax ; and target segments 22 | ; 23 | ; Game restart 24 | ; 25 | fb21: mov di,pipe ; Init variables in video segment (saves big bytes) 26 | xor ax,ax 27 | stosw ; pipe 28 | stosw ; score 29 | stosw ; grav 30 | mov al,0xa0 31 | stosw ; next 32 | mov al,0x60 33 | stosw ; bird 34 | 35 | mov di,0x004a ; Game title 36 | mov ax,0x0f46 ; 'F' in white, good old ASCII 37 | stosw 38 | mov al,0x2d ; '-' 39 | stosw 40 | mov al,0x42 ; 'B' 41 | stosw 42 | mov al,0x49 ; 'I' 43 | stosw 44 | mov al,0x52 ; 'R' 45 | stosw 46 | mov al,0x44 ; 'D' 47 | stosw 48 | mov cx,80 ; Introduce 80 columns of scenery 49 | fb1: push cx 50 | call scroll_scenery 51 | pop cx 52 | loop fb1 53 | 54 | fb23: mov ah,0x01 ; Check if key pressed 55 | int 0x16 56 | pushf 57 | xor ax,ax ; Wait for a key 58 | int 0x16 59 | popf 60 | jnz fb23 ; Jump if key was accumulated, if not already waited for key ;) 61 | 62 | ; 63 | ; Main loop 64 | ; 65 | fb12: mov al,[bird] ; Bird falls... 66 | add al,[grav] ; ...because of gravity... 67 | mov [bird],al ; ...into new position. 68 | and al,0xf8 ; Row is a 5.3 fraction, nullify fraction 69 | mov ah,0x14 ; Given integer is x8, multiply by 20 to get 160 per line 70 | mul ah ; Row into screen 71 | add ax,$0020 ; Fixed column 72 | xchg ax,di ; Pass to DI (AX cannot be used as pointer) 73 | mov al,[frame] 74 | and al,4 ; Wing movement each 4 frames 75 | jz fb15 76 | mov al,[di-160] ; Get character below 77 | mov word [di-160],$0d1e ; Draw upper wing 78 | add al,[di] ; Add another character below 79 | shr al,1 ; Normalize 80 | mov word [di],$0d14 ; Draw body 81 | jmp short fb16 82 | 83 | fb15: mov al,[di] ; Get character below 84 | mov word [di],$0d1f ; Draw body 85 | fb16: add al,[di+2] ; Get character below head 86 | mov word [di+2],$0d10 ; Draw head 87 | cmp al,0x40 ; Collision with scenery? 88 | jz fb19 89 | ; 90 | ; Stars and game over 91 | ; 92 | mov byte [di],$2a ; '*' Asterisks to indicate crashing 93 | mov byte [di+2],$2a 94 | mov di,0x07CA 95 | mov ax,0x0f42 ; 'B' in white, good old ASCII 96 | stosw 97 | mov al,0x4F ; 'O' 98 | stosw 99 | mov al,0x4E ; 'N' 100 | stosw 101 | mov al,0x4B ; 'K' 102 | stosw 103 | mov al,0x21 ; '!' 104 | stosw 105 | mov cx,100 ; Wait 100 frames 106 | fb20: push cx 107 | call wait_frame 108 | pop cx 109 | loop fb20 110 | jmp fb21 ; Restart 111 | 112 | fb19: call wait_frame ; Wait for frame 113 | mov al,[frame] 114 | and al,7 ; 8 frames have passed? 115 | jnz fb17 ; No, jump 116 | inc word [grav] ; Increase gravity 117 | fb17: 118 | mov al,$20 119 | mov [di-160],al ; Delete bird from screen 120 | mov [di+2],al 121 | stosb 122 | call scroll_scenery ; Scroll scenery 123 | call scroll_scenery ; Scroll scenery 124 | cmp byte [0x00a0],0xb0 ; Passed a column? 125 | jz fb27 126 | cmp byte [0x00a2],0xb0 ; Passed a column? 127 | fb27: jnz fb24 128 | inc word [score] ; Increase score 129 | mov ax,[score] 130 | mov di,0x008e ; Show current score 131 | fb25: xor dx,dx ; Extend AX to 32 bits 132 | mov bx,10 ; Divisor is 10 133 | div bx ; Divide 134 | add dx,0x0c30 ; Convert remaining 0-9 to ASCII, also put color 135 | xchg ax,dx 136 | std 137 | stosw 138 | mov byte [di],0x20 ; Clean at least one character of prev. score 139 | cld 140 | xchg ax,dx 141 | or ax,ax ; Score digits still remain? 142 | jnz fb25 ; Yes, jump 143 | fb24: mov ah,0x01 ; Any key pressed? 144 | int 0x16 145 | jz fb26 ; No, go to main loop 146 | mov ah,0x00 147 | int 0x16 ; Get key 148 | cmp al,0x1b ; Escape key? 149 | jne fb4 ; No, jump 150 | int 0x20 ; Exit to DOS or to oblivion (boot sector) 151 | fb4: mov ax,[bird] 152 | sub ax,0x10 ; Move bird two rows upward 153 | cmp ax,0x08 ; Make sure the bird doesn't fly free outside screen 154 | jb fb18 155 | mov [bird],ax 156 | fb18: mov byte [grav],0 ; Reset gravity 157 | mov al,0xb6 ; Flap sound 158 | out (0x43),al 159 | mov al,0x90 160 | out (0x42),al 161 | mov al,0x4a 162 | out (0x42),al 163 | in al,(0x61) 164 | or al,0x03 ; Turn on sound 165 | out (0x61),al 166 | fb26: jmp fb12 167 | 168 | ; 169 | ; Scroll scenery one column at a time 170 | ; 171 | scroll_scenery: 172 | ; 173 | ; Move whole screen 174 | ; 175 | mov si,0x00a2 ; Point to row 1, column 1 in SI 176 | mov di,0x00a0 ; Point to row 1, column 0 in DI 177 | fb2: mov cx,79 ; 79 columns 178 | repz ; Scroll!!! 179 | movsw 180 | mov ax,0x0e20 ; Clean last character 181 | stosw 182 | lodsw ; Advance source to keep pair source/target 183 | cmp si,0x0fa2 ; All scrolled? 184 | jnz fb2 ; No, jump 185 | ; 186 | ; Insert houses 187 | ; 188 | mov word [0x0f9e],0x02df ; Terrain 189 | in al,(0x40) ; Get "random" number 190 | and al,0x70 191 | jz fb5 192 | mov bx,0x0408 ; House of one floor 193 | mov [0x0efe],bx 194 | mov di,0x0e5e 195 | and al,0x20 ; Check "random" number 196 | jz fb3 197 | mov [di],bx ; House of two floors 198 | sub di,0x00a0 199 | fb3: mov word [di],0x091e ; Add roof 200 | ; 201 | ; Check if it's time to insert a column 202 | ; 203 | fb5: dec word [next] ; Decrease time (column really) for next pipe 204 | mov bx,[next] 205 | cmp bx,0x03 ; bx = 3,2,1,0 for the four columns making the pipe 206 | ja fb6 207 | jne fb8 208 | in al,(0x40) ; Get "random" number 209 | and ax,0x0007 ; Between 0 and 7 210 | add al,0x04 ; Between 4 and 11 211 | mov [tall],ax ; This will tell how tall the pipe is 212 | fb8: mov cx,[tall] 213 | or bx,bx ; Rightmost? 214 | mov dl,0xb0 215 | jz fb7 ; Yes, jump 216 | mov dl,0xdb 217 | cmp bx,0x03 ; Leftmost? 218 | jb fb7 ; No, jump 219 | mov dl,0xb1 220 | fb7: mov di,0x013e ; Start from top of screen 221 | mov ah,0x0a 222 | mov al,dl 223 | fb9: stosw 224 | add di,0x009e 225 | loop fb9 226 | mov al,0xc4 227 | stosw 228 | add di,0x009e*6+10 229 | mov al,0xdf 230 | stosw 231 | add di,0x009e 232 | fb10: mov al,dl 233 | stosw 234 | add di,0x009e 235 | cmp di,0x0f00 236 | jb fb10 237 | or bx,bx 238 | jnz fb6 239 | mov ax,[pipe] 240 | inc ax ; Increase total pipes shown 241 | mov [pipe],ax 242 | mov cl,3 243 | shr ax,cl 244 | mov ah,0x50 ; Decrease distance between pipes 245 | sub ah,al 246 | cmp ah,0x10 247 | ja fb11 248 | mov ah,0x10 249 | fb11: mov [next],ah ; Time for next pipe 250 | fb6: ret 251 | 252 | ; 253 | ; Wait for a frame 254 | ; 255 | wait_frame: 256 | mov ah,0x00 ; Use base clock tick 257 | int 0x1a 258 | fb14: push dx 259 | mov ah,0x00 ; Read again base clock tick 260 | int 0x1a 261 | pop bx 262 | cmp bx,dx ; Wait for change 263 | jz fb14 264 | inc word [frame] ; Increase frame count 265 | in al,(0x61) 266 | and al,0xfc ; Turn off sound 267 | out (0x61),al 268 | ret 269 | 270 | db "OTG" ; 3 unused bytes 271 | 272 | db 0x55,0xaa ; Bootable signature 273 | 274 | pipe: equ 0x0fa0 275 | score: equ 0x0fa2 276 | grav: equ 0x0fa4 277 | next: equ 0x0fa6 278 | bird: equ 0x0fa8 279 | tall: equ 0x0faa 280 | frame: equ 0x0fac 281 | 282 | -------------------------------------------------------------------------------- /test/input0.asm: -------------------------------------------------------------------------------- 1 | cld 2 | rep stosw 3 | xchg ax,dx 4 | xchg dx,ax 5 | 6 | 7 | -------------------------------------------------------------------------------- /test/input1.asm: -------------------------------------------------------------------------------- 1 | mov cx,[bx] 2 | mov cx,[bp] 3 | mov cx,[si] 4 | mov cx,[di] 5 | mov cx,[bx+si] 6 | mov cx,[bx+di] 7 | mov cx,[bp+si] 8 | mov cx,[bp+di] 9 | mov cx,[bx+5] 10 | mov cx,[bp+5] 11 | mov cx,[si+5] 12 | mov cx,[di+5] 13 | mov cx,[bx+si+5] 14 | mov cx,[bx+di+5] 15 | mov cx,[bp+si+5] 16 | mov cx,[bp+di+5] 17 | mov cx,[bx+128] 18 | mov cx,[bp+128] 19 | mov cx,[si+128] 20 | mov cx,[di+128] 21 | mov cx,[bx+si+128] 22 | mov cx,[bx+di+128] 23 | mov cx,[bp+si+128] 24 | mov cx,[bp+di+128] 25 | 26 | -------------------------------------------------------------------------------- /test/invaders.asm: -------------------------------------------------------------------------------- 1 | ; 2 | ; Invaders in 512 bytes 3 | ; 4 | ; by Oscar Toledo G. 5 | ; 6 | ; (c) Copyright 2015-2019 Oscar Toledo G. 7 | ; 8 | ; Creation: Oct/27/2015. 9 | ; Revision: Nov/06/2015. Adjusted bullet collision. Invaders 10 | ; accelerate. 11 | ; Revision: Apr/03/2019. Invaders now can shoot. Spaceship does 12 | ; explosion. 13 | ; Revision: May/28/2019. Invaders goes down at 11px instead 12px. 14 | ; Now starts another invaders wave more 15 | ; difficult. 16 | ; Revision: Jun/01/2019. Redesigned for 320x200x256 mode. 17 | ; Revision: Jun/02/2019. Now in color. Color carries information 18 | ; about thing being hit. 19 | ; Revision: Jun/03/2019. Optimized, 601 bytes as COM!!! 20 | ; Revision: Jun/04/2019. At last 512 bytes! 21 | ; Revision: Jun/05/2019. By popular demand added pure8088 option. Now 22 | ; the 8088 version also is bootable! so now 23 | ; 8088 is the default. 24 | ; Revision: Jun/06/2019. jtsiomb made the point that the COM file 25 | ; doesn't need to be 512 bytes, so Esc for 26 | ; exiting and also returns to text mode. 27 | ; Revision: Jun/29/2019. Now spaceship moves to left pressing Ctrl, 28 | ; to right pressing Alt, and shoots pressing 29 | ; Shift. Spaceship stops when you depress the 30 | ; direction key. To exit you press Scroll 31 | ; Lock. Used the extra bytes to implement 32 | ; barriers that stop the invaders' bullets. 33 | ; (suggested in Reddit by nils-m-holm). 34 | ; 35 | 36 | ; 37 | ; Using PUSHA and POPA the code can be smaller, not enabled by 38 | ; default. 39 | ; 40 | 41 | %ifndef pure8088 ; Define as 0 to create a 80186/80286 binary 42 | pure8088: equ 1 ; Enabled by default for pure 8088 assembler 43 | 44 | cpu 8086 45 | %endif 46 | 47 | %ifndef com_file ; If not defined create a boot sector 48 | com_file: equ 0 49 | %endif 50 | 51 | base: equ 0xfc80 ; Memory base (same segment as video) 52 | 53 | shots: equ base+0x00 ; Space to contain 4 shots (2 bytes each one) 54 | ; Plus space for a ignored shot (full table) 55 | ; Notice (sprites + SPRITE_SIZE) - (shots + 2) 56 | ; must be divisible by SPRITE_SIZE. 57 | old_time: equ base+0x0c ; Old time 58 | level: equ base+0x10 ; Current level number 59 | lives: equ base+0x11 ; Current lives 60 | sprites: equ base+0x12 ; Space to contain sprite table 61 | 62 | SHIP_ROW: equ 0x5c*OFFSET_X ; Row of spaceship 63 | X_WIDTH: equ 0x0140 ; X-width of video 64 | OFFSET_X: equ X_WIDTH*2 ; X-offset between screen rows (2 pixels) 65 | SPRITE_SIZE: equ 4 ; Size of each sprite in bytes 66 | 67 | ; 68 | ; All colors different (important to distinguish things) 69 | ; 70 | SPACESHIP_COLOR: equ 0x1c ; Must be below 0x20 71 | BARRIER_COLOR: equ 0x0b 72 | SHIP_EXPLOSION_COLOR: equ 0x0a 73 | INVADER_EXPLOSION_COLOR: equ 0x0e 74 | BULLET_COLOR: equ 0x0c 75 | START_COLOR: equ ((sprites+SPRITE_SIZE-(shots+2))/SPRITE_SIZE+0x20) 76 | 77 | %if com_file 78 | org 0x0100 ; Start position for COM files 79 | %else 80 | org 0x7c00 ; Start position for boot sector 81 | %endif 82 | mov ax,0x0013 ; Set mode 0x13 (320x200x256 VGA) 83 | int 0x10 ; Call BIOS 84 | cld 85 | mov ax,0xa000 ; Point to screen memory 86 | mov ds,ax ; Both DS... 87 | mov es,ax ; ...and ES 88 | mov ah,0x04 89 | mov [level],ax ; Level = 0, Lives = 4 90 | restart_game: 91 | xor ax,ax 92 | mov cx,level/2 ; Clear screen and variables (except level/lives) 93 | xor di,di 94 | rep 95 | stosw ; ch is zero from here 96 | 97 | ; 98 | ; Setup descend state 99 | ; 100 | mov ax,[di] ; al now contains level, ah contains lives 101 | inc ax ; Increase by 2 (so invaders descend correctly) 102 | inc ax 103 | stosw ; Advance level 104 | mov ah,al 105 | xchg ax,dx ; Shouldn't damage DX starting here 106 | 107 | ; 108 | ; Setup the spaceship 109 | ; 110 | mov ax,SPACESHIP_COLOR*0x0100+0x00 111 | stosw 112 | mov ax,SHIP_ROW+0x4c*2 113 | stosw 114 | ; 115 | ; Setup the invaders 116 | ; 117 | mov ax,0x08*OFFSET_X+0x28 118 | mov bx,START_COLOR*0x0100+0x10 119 | in1: mov cl,0x0b ; Eleven invaders per row 120 | in5: stosw ; Set invader position 121 | add ax,0x0b*2 ; Go to next column 122 | xchg ax,bx 123 | stosw ; Set invader color and shape 124 | inc ah ; Go to next color 125 | xchg ax,bx 126 | loop in5 ; Loop and also make sure ch is zero 127 | add ax,0x09*OFFSET_X-0x000b*0x000b*2 ; Go to next row 128 | cmp bh,START_COLOR+55 ; Whole board finished? 129 | jne in1 ; No, jump 130 | 131 | ; 132 | ; Draw the barriers 133 | ; 134 | mov di,0x55*0x280+0x10*2 135 | mov cl,5 136 | in48: 137 | mov ax,BARRIER_COLOR*0x0100+0x04 138 | call draw_sprite 139 | add di,0x1e*2 140 | loop in48 141 | 142 | ; CH is zero 143 | 144 | in14: 145 | mov si,sprites+SPRITE_SIZE 146 | 147 | ; 148 | ; Game loop 149 | ; 150 | ; Globals: 151 | ; SI = Next invader to animate 152 | ; DL = state (0=left, 1=right, >=2 down) 153 | ; DH = nstate (next state) 154 | ; CH = dead invaders 155 | ; BP = frame counter 156 | ; 157 | in46: 158 | cmp byte [si+2],0x20 ; Current invader is cosmic debris? 159 | jc in2 ; No, jump 160 | inc ch ; Count another dead invader 161 | cmp ch,55 ; All invaders defeated? 162 | je restart_game ; Yes, jump. 163 | ; 164 | ; Yes, invaders speed up 165 | ; 166 | in6: 167 | lodsw ; Load position in AX 168 | xchg ax,di ; Move to DI 169 | lodsw ; Get type of sprite 170 | cmp al,0x28 ; Destroyed? 171 | je in27 ; Yes, jump 172 | cmp al,0x20 ; Explosion? 173 | jne in29 ; No, jump 174 | mov byte [si-2],0x28 ; Don't draw again 175 | in29: call draw_sprite ; Draw invader on screen 176 | in27: cmp si,sprites+56*SPRITE_SIZE ; Whole board revised? 177 | jne in46 ; No, jump 178 | mov al,dh 179 | sub al,2 ; Going down? 180 | jc in14 ; No, preserve left/right direction 181 | xor al,1 ; Switch direction 182 | mov dl,al 183 | mov dh,al 184 | jmp in14 185 | 186 | in2: 187 | xor byte [si+2],8 ; Invader animation (before possible explosion) 188 | ; 189 | ; Synchronize game to 18.20648 hz. of BIOS 190 | ; 191 | inc bp 192 | and bp,7 ; Each 8 invaders 193 | %if pure8088 194 | push dx 195 | push si 196 | push bp 197 | %else 198 | pusha 199 | %endif 200 | jne in12 201 | in22: 202 | mov ah,0x00 203 | int 0x1a ; BIOS clock read 204 | cmp dx,[old_time] ; Wait for change 205 | je in22 206 | mov [old_time],dx ; Save new current time 207 | in12: 208 | %if 1 209 | ; 210 | ; Handle player bullet 211 | ; 212 | mov si,shots ; Point to shots list 213 | mov cx,4 ; 4 shots at most 214 | lodsw ; Read position (player) 215 | cmp ax,X_WIDTH ; Is it at top of screen? 216 | xchg ax,di 217 | jc in31 ; Erase bullet 218 | ; Doesn't mind doing it all time 219 | call zero ; Remove bullet 220 | sub di,X_WIDTH+2 221 | mov al,[di] ; Read pixel 222 | sub al,0x20 ; Hits invader? 223 | jc in30 ; No, jump 224 | %if pure8088 225 | push si 226 | push di 227 | %else 228 | pusha 229 | %endif 230 | mov ah,SPRITE_SIZE ; The pixel indicates the... 231 | mul ah ; ...invader hit. 232 | add si,ax 233 | lodsw 234 | xchg ax,di 235 | mov byte [si],0x20 ; Erase next time 236 | mov ax,INVADER_EXPLOSION_COLOR*0x0100+0x08 ; But explosion now 237 | call draw_sprite ; Draw sprite 238 | %if pure8088 239 | pop di 240 | pop si 241 | %else 242 | popa 243 | %endif 244 | jmp in31 245 | 246 | ; 247 | ; Handle invader bullets 248 | ; 249 | in24: 250 | lodsw ; Read current coordinate 251 | or ax,ax ; Is it falling? 252 | je in23 ; No, jump 253 | cmp ax,0x60*OFFSET_X ; Pixel lower than spaceship? 254 | xchg ax,di 255 | jnc in31 ; Yes, remove bullet 256 | call zero ; Remove bullet 257 | add di,X_WIDTH-2 ; Bullet falls down 258 | 259 | ; Draw bullet 260 | in30: 261 | mov ax,BULLET_COLOR*0x0100+BULLET_COLOR 262 | mov [si-2],di ; Update position of bullet 263 | cmp byte [di+X_WIDTH],BARRIER_COLOR ; Barrier in path? 264 | jne in7 ; Yes, erase bullet and barrier pixel 265 | 266 | ; Remove bullet 267 | in31: xor ax,ax ; AX contains zero (DI unaffected) 268 | mov [si-2],ax ; Delete bullet from table 269 | 270 | in7: cmp byte [di],SPACESHIP_COLOR ; Check collision with player 271 | jne in41 ; No, jump 272 | mov word [sprites],SHIP_EXPLOSION_COLOR*0x0100+0x38 ; Player explosion 273 | in41: 274 | call big_pixel ; Draw/erase bullet 275 | in23: loop in24 276 | %endif 277 | 278 | ; 279 | ; Spaceship handling 280 | ; 281 | mov si,sprites ; Point to spaceship 282 | lodsw ; Load sprite frame / color 283 | or al,al ; Explosion? 284 | je in42 ; No, jump 285 | add al,0x08 ; Keep explosion 286 | jne in42 ; Finished? No, jump 287 | mov ah,SPACESHIP_COLOR ; Restore color (sprite already) 288 | dec byte [lives] ; Remove one life 289 | js in10 ; Exit if all used 290 | in42: mov [si-2],ax ; Save new frame / color 291 | mov di,[si] ; Load position 292 | call draw_sprite ; Draw sprite (spaceship) 293 | jne in43 ; Jump if still explosion 294 | 295 | mov ah,0x02 ; BIOS Get Keyboard Flags 296 | int 0x16 297 | %if com_file 298 | test al,0x10 ; Test for Scroll Lock and exit 299 | jnz in10 300 | %endif 301 | 302 | test al,0x04 ; Ctrl key? 303 | jz in17 ; No, jump 304 | dec di ; Move 2 pixels to left 305 | dec di 306 | 307 | in17: test al,0x08 ; Alt key? 308 | jz in18 ; No, jump 309 | inc di ; Move 2 pixels to right 310 | inc di 311 | in18: 312 | test al,0x03 ; Shift keys? 313 | jz in35 ; No, jump 314 | cmp word [shots],0 ; Bullet available? 315 | jne in35 ; No, jump 316 | lea ax,[di+(0x04*2)] ; Offset from spaceship 317 | mov [shots],ax ; Start bullet 318 | in35: 319 | xchg ax,di 320 | cmp ax,SHIP_ROW-2 ; Update if not touching border 321 | je in43 322 | cmp ax,SHIP_ROW+0x0132 323 | je in43 324 | in19: mov [si],ax ; Update position 325 | in43: 326 | %if pure8088 327 | pop bp 328 | pop si 329 | pop dx 330 | %else 331 | popa 332 | %endif 333 | 334 | mov ax,[si] ; Get position of current invader 335 | cmp dl,1 ; Going down (state 2)? 336 | jbe in9 ; No, jump 337 | add ax,0x0280 ; Go down by 2 pixels 338 | cmp ax,0x55*0x280 ; Reaches Earth? 339 | jc in8 ; No, jump 340 | in10: 341 | %if com_file 342 | mov ax,0x0003 ; Restore text mode 343 | int 0x10 344 | %endif 345 | int 0x20 ; Exit to DOS 346 | 347 | in9: dec ax ; Moving to left 348 | dec ax 349 | jc in20 350 | add ax,4 ; Moving to right 351 | in20: push ax 352 | shr ax,1 ; Divide position by 2... 353 | mov cl,0xa0 ; ...means we can get column dividing by 0xa0 354 | div cl ; ...instead of 0x0140 (longer code) 355 | dec ah ; Convert 0x00 to 0xff 356 | cmp ah,0x94 ; Border touched? (>= 0x94) 357 | pop ax 358 | jb in8 ; No, jump 359 | or dh,22 ; Goes down by 11 pixels (11 * 2) must be odd 360 | in8: mov [si],ax 361 | add ax,0x06*0x280+0x03*2 ; Offset for bullet 362 | xchg ax,bx 363 | 364 | mov cx,3 ; ch = 0 - invader alive 365 | in al,(0x40) ; Read timer 366 | cmp al,0xfc ; Random event happening? 367 | jc in4 ; No, jump 368 | ; 369 | ; Doesn't work in my computer: 370 | ; 371 | ; mov di,shots+2 372 | ; xor ax,ax 373 | ; repne scasw 374 | ; mov [di-2],bx 375 | ; 376 | mov di,shots+2 377 | in45: cmp word [di],0 ; Search for free slot 378 | je in44 ; It's free, jump! 379 | scasw ; Advance DI 380 | loop in45 ; Until 3 slots searched 381 | in44: 382 | mov [di],bx ; Start invader shot (or put in ignored slot) 383 | in4: 384 | jmp in6 385 | 386 | ; 387 | ; Bitmaps for sprites 388 | ; 389 | bitmaps: 390 | db 0x18,0x18,0x3c,0x24,0x3c,0x7e,0xFf,0x24 ; Spaceship 391 | db 0x00,0x80,0x42,0x18,0x10,0x48,0x82,0x01 ; Explosion 392 | db 0x00,0xbd,0xdb,0x7e,0x24,0x3c,0x66,0xc3 ; Alien (frame 1) 393 | db 0x00,0x3c,0x5a,0xff,0xa5,0x3c,0x66,0x66 ; Alien (frame 2) 394 | db 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ; Erase 395 | 396 | ; 397 | ; Draw pixel per Carry (use AX if Carry=1 or zero if Carry=0) 398 | ; 399 | bit: jc big_pixel 400 | zero: xor ax,ax 401 | ; Draw a big pixel (2x2 pixels) 402 | big_pixel: 403 | mov [di+X_WIDTH],ax 404 | stosw 405 | ret 406 | 407 | ; ah = sprite color 408 | ; al = sprite (x8) 409 | ; di = Target address 410 | draw_sprite: 411 | %if pure8088 412 | push cx 413 | push di 414 | pushf 415 | %else 416 | pusha 417 | %endif 418 | in3: push ax 419 | mov bx,bitmaps 420 | cs xlat ; Extract one byte from bitmap 421 | xchg ax,bx ; bl contains byte, bh contains color 422 | mov cx,10 ; Two extra zero pixels at left and right 423 | clc ; Left pixel as zero (clean) 424 | in0: mov al,bh ; Duplicate color in AX 425 | mov ah,bh 426 | call bit ; Draw pixel 427 | shl bl,1 428 | loop in0 429 | add di,OFFSET_X-20 ; Go to next video line 430 | pop ax 431 | inc ax ; Next bitmap byte 432 | test al,7 ; Sprite complete? 433 | jne in3 ; No, jump 434 | %if pure8088 435 | popf 436 | pop di 437 | pop cx 438 | %else 439 | popa 440 | %endif 441 | ret 442 | 443 | %if com_file 444 | %else 445 | times 510-($-$$) db 0x4f 446 | db 0x55,0xaa ; Make it a bootable sector 447 | %endif 448 | -------------------------------------------------------------------------------- /test/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 | mov si,int_0x20 ; SI now points to int_0x20 192 | mov di,0x0020*4 ; Address of service for int 0x20 193 | mov cl,6 194 | .load_vec: 195 | movsw ; Copy IP address 196 | stosw ; Copy CS address 197 | loop .load_vec 198 | 199 | ; 200 | ; 'ver' command 201 | ; 202 | ver_command: 203 | mov si,intro 204 | call output_string 205 | int int_restart ; Restart bootOS 206 | 207 | ; 208 | ; Warm start of bootOS 209 | ; 210 | restart: 211 | cld ; Clear D flag. 212 | push cs ; Reinit all segment registers 213 | push cs 214 | push cs 215 | pop ds 216 | pop es 217 | pop ss 218 | mov sp,stack ; Restart stack 219 | 220 | mov al,'$' ; Command prompt 221 | call input_line ; Input line 222 | 223 | cmp byte [si],0x00 ; Empty line? 224 | je restart ; Yes, get another line 225 | 226 | mov di,commands ; Point to commands list 227 | 228 | ; Notice that filenames starting with same characters 229 | ; won't be recognized as such (so file dirab cannot be 230 | ; executed). 231 | os11: 232 | mov al,[di] ; Read length of command in chars 233 | inc di 234 | and ax,0x00ff ; Is it zero? 235 | je os12 ; Yes, jump 236 | xchg ax,cx 237 | push si ; Save current position 238 | rep cmpsb ; Compare statement 239 | jne os14 ; Equal? No, jump 240 | call word [di] ; Call command process 241 | jmp restart ; Go to expect another command 242 | 243 | os14: add di,cx ; Advance the list pointer 244 | inc di ; Avoid the address 245 | inc di 246 | pop si 247 | jmp os11 ; Compare another statement 248 | 249 | os12: mov bx,si ; Input pointer 250 | mov di,boot ; Location to read data 251 | int int_load_file ; Load file 252 | jc os7 ; Jump if error 253 | jmp bx 254 | 255 | ; 256 | ; File not found error 257 | ; 258 | os7: 259 | mov si,error_message 260 | call output_string 261 | int int_restart ; Go to expect another command 262 | 263 | ; 264 | ; >> COMMAND << 265 | ; del filename 266 | ; 267 | del_command: 268 | os22: 269 | mov bx,si ; Copy SI (buffer pointer) to BX 270 | lodsb 271 | cmp al,0x20 ; Avoid spaces 272 | je os22 273 | int int_delete_file 274 | jc os7 275 | ret 276 | 277 | ; 278 | ; 'dir' command 279 | ; 280 | dir_command: 281 | call read_dir ; Read the directory 282 | mov di,bx 283 | os18: 284 | cmp byte [di],0 ; Empty entry? 285 | je os17 ; Yes, jump 286 | mov si,di ; Point to data 287 | call output_string ; Show name 288 | os17: call next_entry 289 | jne os18 ; No, jump 290 | ret ; Return 291 | 292 | ; 293 | ; Get filename length and prepare for directory lookup 294 | ; Entry: 295 | ; si = pointer to string 296 | ; Output: 297 | ; si = unaffected 298 | ; di = pointer to start of directory 299 | ; cx = length of filename including zero terminator 300 | ; 301 | filename_length: 302 | push si 303 | xor cx,cx ; cx = 0 304 | .loop: 305 | lodsb ; Read character. 306 | inc cx ; Count character. 307 | cmp al,0 ; Is it zero (end character)? 308 | jne .loop ; No, jump. 309 | 310 | pop si 311 | mov di,sector ; Point to start of directory. 312 | ret 313 | 314 | ; 315 | ; >> SERVICE << 316 | ; Load file 317 | ; 318 | ; Entry: 319 | ; ds:bx = Pointer to filename ended with zero byte. 320 | ; es:di = Destination. 321 | ; Output: 322 | ; Carry flag = Set = not found, clear = successful. 323 | ; 324 | load_file: 325 | push di ; Save destination 326 | push es 327 | call find_file ; Find the file (sanitizes ES) 328 | mov ah,0x02 ; Read sector 329 | shared_file: 330 | pop es 331 | pop bx ; Restore destination on BX 332 | jc ret_cf ; Jump if error 333 | call disk ; Do operation with disk 334 | ; Carry guaranteed to be clear. 335 | ret_cf: 336 | mov bp,sp 337 | rcl byte [bp+4],1 ; Insert Carry flag in Flags (automatic usage of SS) 338 | iret 339 | 340 | ; 341 | ; >> SERVICE << 342 | ; Save file 343 | ; 344 | ; Entry: 345 | ; ds:bx = Pointer to filename ended with zero byte. 346 | ; es:di = Source. 347 | ; Output: 348 | ; Carry flag = Set = error, clear = good. 349 | ; 350 | save_file: 351 | push di ; Save origin 352 | push es 353 | push bx ; Save filename pointer 354 | int int_delete_file ; Delete previous file (sanitizes ES) 355 | pop bx ; Restore filename pointer 356 | call filename_length ; Prepare for lookup 357 | 358 | .find: es cmp byte [di],0 ; Found empty directory entry? 359 | je .empty ; Yes, jump and fill it. 360 | call next_entry 361 | jne .find 362 | jmp shared_file 363 | 364 | .empty: push di 365 | rep movsb ; Copy full name into directory 366 | call write_dir ; Save directory 367 | pop di 368 | call get_location ; Get location of file 369 | mov ah,0x03 ; Write sector 370 | jmp shared_file 371 | 372 | ; 373 | ; >> SERVICE << 374 | ; Delete file 375 | ; 376 | ; Entry: 377 | ; ds:bx = Pointer to filename ended with zero byte. 378 | ; Output: 379 | ; Carry flag = Set = not found, clear = deleted. 380 | ; 381 | delete_file: 382 | call find_file ; Find file (sanitizes ES) 383 | jc ret_cf ; If carry set then not found, jump. 384 | mov cx,entry_size 385 | call write_zero_dir ; Fill whole entry with zero. Write directory. 386 | jmp ret_cf 387 | 388 | ; 389 | ; Find file 390 | ; 391 | ; Entry: 392 | ; ds:bx = Pointer to filename ended with zero byte. 393 | ; Result: 394 | ; es:di = Pointer to directory entry 395 | ; Carry flag = Clear if found, set if not found. 396 | find_file: 397 | push bx 398 | call read_dir ; Read directory (sanitizes ES) 399 | pop si 400 | call filename_length ; Get filename length and setup DI 401 | os6: 402 | push si 403 | push di 404 | push cx 405 | repe cmpsb ; Compare name with entry 406 | pop cx 407 | pop di 408 | pop si 409 | je get_location ; Jump if equal. 410 | call next_entry 411 | jne os6 ; No, jump 412 | ret ; Return 413 | 414 | next_entry: 415 | add di,byte entry_size ; Go to next entry. 416 | cmp di,sector+sector_size ; Complete directory? 417 | stc ; Error, not found. 418 | ret 419 | 420 | ; 421 | ; Get location of file on disk 422 | ; 423 | ; Entry: 424 | ; DI = Pointer to entry in directory. 425 | ; 426 | ; Result 427 | ; CH = Track number in disk. 428 | ; CL = Sector (always 0x01). 429 | ; 430 | ; The position of a file inside the disk depends on its 431 | ; position in the directory. The first entry goes to 432 | ; track 1, the second entry to track 2 and so. 433 | ; 434 | get_location: 435 | lea ax,[di-(sector-entry_size)] ; Get entry pointer into directory 436 | ; Plus one entry (files start on track 1) 437 | mov cl,4 ; 2^(8-4) = entry_size 438 | shl ax,cl ; Shift left and clear Carry flag 439 | inc ax ; AL = Sector 1 440 | xchg ax,cx ; CH = Track, CL = Sector 441 | ret 442 | 443 | ; 444 | ; >> COMMAND << 445 | ; format 446 | ; 447 | format_command: 448 | mov di,sector ; Fill whole sector to zero 449 | mov cx,sector_size 450 | call write_zero_dir 451 | mov bx,osbase ; Copy bootOS onto first sector 452 | dec cx 453 | jmp short disk 454 | 455 | ; 456 | ; Read the directory from disk 457 | ; 458 | read_dir: 459 | push cs ; bootOS code segment... 460 | pop es ; ...to sanitize ES register 461 | mov ah,0x02 462 | jmp short disk_dir 463 | 464 | write_zero_dir: 465 | mov al,0 466 | rep stosb 467 | 468 | ; 469 | ; Write the directory to disk 470 | ; 471 | write_dir: 472 | mov ah,0x03 473 | disk_dir: 474 | mov bx,sector 475 | mov cx,0x0002 476 | ; 477 | ; Do disk operation. 478 | ; 479 | ; Input: 480 | ; AH = 0x02 read disk, 0x03 write disk 481 | ; ES:BX = data source/target 482 | ; CH = Track number 483 | ; CL = Sector number 484 | ; 485 | disk: 486 | push ax 487 | push bx 488 | push cx 489 | push es 490 | mov al,0x01 ; AL = 1 sector 491 | xor dx,dx ; DH = Drive A. DL = Head 0. 492 | int 0x13 493 | pop es 494 | pop cx 495 | pop bx 496 | pop ax 497 | jc disk ; Retry 498 | ret 499 | 500 | ; 501 | ; Input line from keyboard 502 | ; Entry: 503 | ; al = prompt character 504 | ; Result: 505 | ; buffer 'line' contains line, finished with CR 506 | ; SI points to 'line'. 507 | ; 508 | input_line: 509 | int int_output_char ; Output prompt character 510 | mov si,line ; Setup SI and DI to start of line buffer 511 | mov di,si ; Target for writing line 512 | os1: cmp al,0x08 ; Backspace? 513 | jne os2 514 | dec di ; Undo the backspace write 515 | dec di ; Erase a character 516 | os2: int int_input_key ; Read keyboard 517 | cmp al,0x0d ; CR pressed? 518 | jne os10 519 | mov al,0x00 520 | os10: stosb ; Save key in buffer 521 | jne os1 ; No, wait another key 522 | ret ; Yes, return 523 | 524 | ; 525 | ; Read a key into al 526 | ; Also outputs it to screen 527 | ; 528 | input_key: 529 | mov ah,0x00 530 | int 0x16 531 | ; 532 | ; Screen output of character contained in al 533 | ; Expands 0x0d (CR) into 0x0a 0x0d (LF CR) 534 | ; 535 | output_char: 536 | cmp al,0x0d 537 | jne os3 538 | mov al,0x0a 539 | int int_output_char 540 | mov al,0x0d 541 | os3: 542 | mov ah,0x0e ; Output character to TTY 543 | mov bx,0x0007 ; Gray. Required for graphic modes 544 | int 0x10 ; BIOS int 0x10 = Video 545 | iret 546 | 547 | ; 548 | ; Output string 549 | ; 550 | ; Entry: 551 | ; SI = address 552 | ; 553 | ; Implementation: 554 | ; It supposes that SI never points to a zero length string. 555 | ; 556 | output_string: 557 | lodsb ; Read character 558 | int int_output_char ; Output to screen 559 | cmp al,0x00 ; Is it 0x00 (terminator)? 560 | jne output_string ; No, the loop continues 561 | mov al,0x0d 562 | int int_output_char 563 | ret 564 | 565 | ; 566 | ; 'enter' command 567 | ; 568 | enter_command: 569 | mov di,boot ; Point to boot sector 570 | os23: push di 571 | mov al,'h' ; Prompt character 572 | call input_line ; Input line 573 | pop di 574 | cmp byte [si],0 ; Empty line? 575 | je os20 ; Yes, jump 576 | os19: call xdigit ; Get a hexadecimal digit 577 | jnc os23 578 | mov cl,4 579 | shl al,cl 580 | xchg ax,cx 581 | call xdigit ; Get a hexadecimal digit 582 | or al,cl 583 | stosb ; Write one byte 584 | jmp os19 ; Repeat loop to complete line 585 | os20: 586 | mov al,'*' ; Prompt character 587 | call input_line ; Input line with filename 588 | push si 589 | pop bx 590 | mov di,boot ; Point to data entered 591 | int int_save_file ; Save new file 592 | ret 593 | 594 | ; 595 | ; Convert ASCII letter to hexadecimal digit 596 | ; 597 | xdigit: 598 | lodsb 599 | cmp al,0x00 ; Zero character marks end of line 600 | je os15 601 | sub al,0x30 ; Avoid spaces (anything below ASCII 0x30) 602 | jc xdigit 603 | cmp al,0x0a 604 | jc os15 605 | sub al,0x07 606 | and al,0x0f 607 | stc 608 | os15: 609 | ret 610 | 611 | ; 612 | ; Our amazing presentation line 613 | ; 614 | intro: 615 | db "bootOS",0 616 | 617 | error_message: 618 | db "Oops",0 619 | 620 | ; 621 | ; Commands supported by bootOS 622 | ; 623 | commands: 624 | db 3,"dir" 625 | dw dir_command 626 | db 6,"format" 627 | dw format_command 628 | db 5,"enter" 629 | dw enter_command 630 | db 3,"del" 631 | dw del_command 632 | db 3,"ver" 633 | dw ver_command 634 | db 0 635 | 636 | int_restart: equ 0x20 637 | int_input_key: equ 0x21 638 | int_output_char: equ 0x22 639 | int_load_file: equ 0x23 640 | int_save_file: equ 0x24 641 | int_delete_file: equ 0x25 642 | 643 | int_0x20: 644 | dw restart ; int 0x20 645 | dw input_key ; int 0x21 646 | dw output_char ; int 0x22 647 | dw load_file ; int 0x23 648 | dw save_file ; int 0x24 649 | dw delete_file ; int 0x25 650 | 651 | times 510-($-$$) db 0x4f 652 | db 0x55,0xaa ; Make it a bootable sector 653 | -------------------------------------------------------------------------------- /test/os.img: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nanochess/tinyasm/84232d5845a677aa6924b51ee9123c16954cf7d4/test/os.img -------------------------------------------------------------------------------- /test/pillman.asm: -------------------------------------------------------------------------------- 1 | ; 2 | ; Pillman 3 | ; 4 | ; by Oscar Toledo G. 5 | ; http://nanochess.org/ 6 | ; 7 | ; (c) Copyright 2019 Oscar Toledo G. 8 | ; 9 | ; Creation date: Jun/11/2019. 10 | ; Revision date: Jun/12/2019. Draws level. 11 | ; Revision date: Jun/13/2019. Pillman can move. 12 | ; Revision date: Jun/14/2019. Now ghosts don't get stuck. Ghost are 13 | ; transparent. Pillman doesn't leave 14 | ; trash. 15 | ; Revision date: Jun/15/2019. Ghosts can catch pillman. Optimized. 16 | ; 509 bytes. 17 | ; Revision date: Jul/09/2019. Self-modifying code, move subroutine, 18 | ; cache routine address (Peter Ferrie). 19 | ; 504 bytes. 20 | ; Revision date: Jul/22/2019. Added Esc key to exit. 21 | ; 22 | 23 | cpu 8086 24 | 25 | %ifndef com_file ; If not defined create a boot sector 26 | com_file: equ 0 27 | %endif 28 | 29 | base: equ 0xf9fe ; Memory base (same segment as video) 30 | intended_dir: equ base+0x00 ; Next direction for player 31 | frame: equ base+0x01 ; Current video frame 32 | x_player: equ base+0x02 ; Saved X-coordinate of player 33 | y_player: equ ms6+0x01 ; Saved Y-coordinate of player 34 | old_time: equ base+0x06 ; Old time 35 | 36 | ; 37 | ; Maze should start at x,y coordinate multiple of 8 38 | ; 39 | BASE_MAZE: equ 16*X_OFFSET+32 40 | pos1: equ BASE_MAZE+21*8*X_OFFSET 41 | 42 | X_OFFSET: equ 0x0140 43 | 44 | MAZE_COLOR: equ 0x37 ; No color should be higher or equal value 45 | PILL_COLOR: equ 0x02 ; Color for pill 46 | PLAYER_COLOR: equ 0x0e ; Should be unique 47 | 48 | ; 49 | ; XOR combination of these plus PILL_COLOR shouldn't 50 | ; result in PLAYER_COLOR 51 | ; 52 | GHOST1_COLOR: equ 0x21 ; Ghost 1 color 53 | GHOST2_COLOR: equ 0x2e ; Ghost 2 color 54 | GHOST3_COLOR: equ 0x28 ; Ghost 3 color 55 | GHOST4_COLOR: equ 0x34 ; Ghost 4 color 56 | 57 | %if com_file 58 | org 0x0100 ; Start address for COM file 59 | %else 60 | org 0x7c00 ; Start address for boot sector 61 | %endif 62 | restart: 63 | mov ax,0x0013 ; Set mode 0x13 (320x200x256 VGA) 64 | int 0x10 ; Call BIOS 65 | cld 66 | mov ax,0xa000 ; Video segment 67 | mov ds,ax ; Use as source data segment 68 | mov es,ax ; Use as target data segment 69 | ; 70 | ; Draw the maze 71 | ; 72 | mov si,maze ; SI = Address of maze data 73 | mov di,BASE_MAZE ; DI = Address for drawing maze 74 | draw_maze_row: 75 | cs lodsw ; Load one word of data from Code Segment 76 | xchg ax,cx ; Put into AX 77 | mov bx,30*8 ; Offset of mirror position 78 | draw_maze_col: 79 | shl cx,1 ; Extract one tile of maze 80 | mov ax,MAZE_COLOR*0x0100+0x18 ; Carry = 0 = Wall 81 | jnc dm1 ; If bit was zero, jump to dm1 82 | mov ax,PILL_COLOR*0x0100+0x38 ; Carry = 1 = Pill 83 | dm1: call draw_sprite ; Draw tile 84 | add di,bx ; Go to mirror position 85 | sub bx,16 ; Mirror finished? 86 | jc dm2 ; Yes, jump 87 | call draw_sprite ; Draw tile 88 | sub di,bx ; Restore position 89 | sub di,8 ; Advance tile 90 | jmp draw_maze_col ; Repeat until finished 91 | 92 | ; 93 | ; Move ghost 94 | ; bh = color 95 | ; 96 | move_ghost: 97 | lodsw ; Load screen position 98 | xchg ax,di 99 | lodsw ; Load direction 100 | test ah,ah 101 | xchg ax,bx ; Color now in ah 102 | mov al,0x30 103 | push ax 104 | mov byte [si-1],0x02 ; Remove first time setup flag 105 | call move_sprite3 106 | pop ax 107 | ; 108 | ; Draw the sprite/tile 109 | ; 110 | ; ah = sprite color 111 | ; al = sprite (x8) 112 | ; di = Target address 113 | draw_sprite: 114 | push ax 115 | push bx 116 | push cx 117 | push di 118 | ds0: push ax 119 | mov bx,bitmaps-8 120 | cs xlat ; Extract one byte from bitmap 121 | xchg ax,bx 122 | mov cx,8 123 | ds1: mov al,bh 124 | shl bl,1 ; Extract one bit 125 | jc ds2 126 | xor ax,ax ; Background color 127 | ds2: 128 | cmp bh,0x10 ; Color < 0x10 129 | jc ds4 ; Yes, jump 130 | cmp byte [di],PLAYER_COLOR ; "Eats" player? 131 | je restart ; Yes, it should crash after several hundred games 132 | ds3: 133 | xor al,[di] ; XOR ghost again pixel 134 | ds4: 135 | stosb 136 | loop ds1 137 | add di,X_OFFSET-8 ; Go to next video line 138 | pop ax 139 | inc ax ; Next bitmap byte 140 | test al,7 ; Sprite complete? 141 | jne ds0 ; No, jump 142 | pop di 143 | pop cx 144 | pop bx 145 | pop ax 146 | ret 147 | 148 | dm2: 149 | add di,X_OFFSET*8-15*8 ; Go to next row 150 | cmp si,setup_data ; Maze completed? 151 | jne draw_maze_row ; No, jump 152 | 153 | ; 154 | ; Setup characters 155 | ; 156 | ; CX is zero at this point 157 | ; DI is equal to pos1 at this point 158 | ;mov di,pos1 159 | mov cl,5 ; 5 elements (player + ghosts) 160 | mov ax,2 ; Going to right 161 | dm3: 162 | cs movsw ; Copy position from Code Segment 163 | stosw ; Store desired direction 164 | loop dm3 ; Loop 165 | 166 | ; 167 | ; Main game loop 168 | ; 169 | game_loop: 170 | mov ah,0x00 171 | int 0x1a ; BIOS clock read 172 | cmp dx,[old_time] ; Wait for time change 173 | je game_loop 174 | mov [old_time],dx ; Save new time 175 | 176 | mov ah,0x01 ; BIOS Key available 177 | int 0x16 178 | mov ah,0x00 ; BIOS Read Key 179 | je no_key 180 | int 0x16 181 | no_key: 182 | mov al,ah 183 | cmp al,0x01 ; Esc key 184 | jne no_esc 185 | int 0x20 186 | no_esc: 187 | sub al,0x48 ; Code for arrow up? 188 | jc no_key2 ; Out of range, jump. 189 | cmp al,0x09 ; Farther than arrow down? 190 | jnc no_key2 ; Out of range, jump. 191 | mov bx,dirs 192 | cs xlat ; Translate direction to internal code 193 | mov [intended_dir],al ; Save as desired direction 194 | no_key2: 195 | mov si,pos1 ; SI points to data for player 196 | lodsw ; Load screen position 197 | xchg ax,di 198 | lodsw ; Load direction/type 199 | xchg ax,bx 200 | xor ax,ax ; Delete pillman 201 | call move_sprite2 ; Move 202 | xor byte [frame],0x80 ; Alternate frame 203 | mov ax,0x0e28 ; Closed mouth 204 | js close_mouth ; Jump if sign set. 205 | mov al,[pos1+2] ; Using current direction 206 | mov cl,3 ; Multiply by 8 207 | shl al,cl ; Show open mouth 208 | close_mouth: 209 | call draw_sprite ; Draw 210 | ; 211 | ; Move ghosts 212 | ; 213 | mov bp, move_ghost 214 | mov bh,GHOST1_COLOR 215 | call bp 216 | mov bh,GHOST2_COLOR 217 | call bp 218 | mov bh,GHOST3_COLOR 219 | call bp 220 | mov bh,GHOST4_COLOR 221 | call bp 222 | jmp game_loop 223 | 224 | ; 225 | ; DI = address on the screen 226 | ; BL = wanted direction 227 | ; 228 | move_sprite3: 229 | je move_sprite ; If zero, won't remove first time 230 | move_sprite2: 231 | call draw_sprite ; Remove ghost 232 | move_sprite: 233 | mov ax,di ; Prepare to extract pixel row/column 234 | xor dx,dx 235 | mov cx,X_OFFSET 236 | div cx 237 | ; Now AX = Row, DX = Column 238 | mov ah,dl 239 | or ah,al 240 | and ah,7 ; Both aligned at 8 pixels? 241 | jne ms0 ; No, jump because cannot change direction. 242 | ; AH is zero already 243 | ;mov ah,0 244 | ; 245 | ; Get available directions 246 | ; 247 | mov ch,MAZE_COLOR 248 | cmp [di-0x0001],ch ; Left 249 | adc ah,ah ; AH = 0000 000L 250 | cmp [di+X_OFFSET*8],ch ; Down 251 | adc ah,ah ; AH = 0000 00LD 252 | cmp [di+0x0008],ch ; Right 253 | adc ah,ah ; AH = 0000 0LDR 254 | cmp [di-X_OFFSET],ch ; Up 255 | adc ah,ah ; AH = 0000 LDRU 256 | 257 | test bh,bh ; Is it pillman? 258 | je ms4 ; Yes, jump 259 | 260 | ; 261 | ; Ghost 262 | ; 263 | test bl,0x05 ; Test BL for .... .D.U 264 | je ms6 ; No, jump 265 | ; Current direction is up/down 266 | cmp dx,[x_player] ; Compare X coordinate with player 267 | mov al,0x02 ; Go right 268 | jc ms8 ; Jump if X ghost < X player 269 | mov al,0x08 ; Go left 270 | jmp ms8 271 | 272 | ; Current direction is left/right 273 | ms6: cmp al,0x00 ; (SMC) Compare Y coordinate with player 274 | mov al,0x04 ; Go down 275 | jc ms8 ; Jump if Y ghost < Y player 276 | mov al,0x01 ; Go up 277 | ms8: 278 | test ah,al ; Can it go in intended direction? 279 | jne ms1 ; Yes, go in direction 280 | 281 | mov al,bl 282 | ms9: test ah,al ; Can it go in current direction? 283 | jne ms1 ; Yes, jump 284 | shr al,1 ; Try another direction 285 | jne ms9 286 | mov al,0x08 ; Cycle direction 287 | jmp ms9 288 | 289 | ; 290 | ; Pillman 291 | ; 292 | ms4: 293 | mov [x_player],dx ; Save current X coordinate 294 | cs mov [y_player],al ; Save current Y coordinate 295 | 296 | mov al,[intended_dir] 297 | test ah,al ; Can it go in intended direction? 298 | jne ms1 ; Yes, go in that direction 299 | 300 | ms5: and ah,bl ; Can it go in current direction? 301 | je ms2 ; No, stops 302 | 303 | ms0: mov al,bl 304 | 305 | ms1: mov [si-2],al ; Save new direction 306 | test al,5 ; If going up/down... 307 | mov bx,-X_OFFSET*2 ; ...bx = vertical movement 308 | jne ms3 309 | mov bx,1*2 ; ...bx = horizontal movement 310 | ms3: 311 | test al,12 312 | je ms7 313 | neg bx ; Reverse direction 314 | ms7: 315 | add di,bx ; Do move 316 | mov [si-4],di ; Save the new screen position 317 | ms2: 318 | ret 319 | 320 | ; 321 | ; Game bitmaps 322 | ; 323 | bitmaps: 324 | db 0x00,0x42,0xe7,0xe7,0xff,0xff,0x7e,0x3c ; dir = 1 325 | db 0x3c,0x7e,0xfc,0xf0,0xf0,0xfc,0x7e,0x3c ; dir = 2 326 | db 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff ; Maze 327 | db 0x3c,0x7e,0xff,0xff,0xe7,0xe7,0x42,0x00 ; dir = 4 328 | db 0x3c,0x7e,0xff,0xff,0xff,0xff,0x7e,0x3c ; Closed mouth 329 | db 0x3c,0x7e,0xdb,0xdb,0xff,0xff,0xff,0xa5 ; Ghost 330 | db 0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00 ; Pill 331 | db 0x3c,0x7e,0x3f,0x0f,0x0f,0x3f,0x7e,0x3c ; dir = 8 332 | 333 | ; 334 | ; Maze shape 335 | ; 336 | maze: 337 | dw 0b0000_0000_0000_0000 338 | dw 0b0111_1111_1111_1110 339 | dw 0b0100_0010_0000_0010 340 | dw 0b0100_0010_0000_0010 341 | dw 0b0111_1111_1111_1111 342 | dw 0b0100_0010_0100_0000 343 | dw 0b0111_1110_0111_1110 344 | dw 0b0000_0010_0000_0010 345 | dw 0b0000_0010_0111_1111 346 | dw 0b0000_0011_1100_0000 347 | dw 0b0000_0010_0100_0000 348 | dw 0b0000_0010_0111_1111 349 | dw 0b0000_0010_0100_0000 350 | dw 0b0111_1111_1111_1110 351 | dw 0b0100_0010_0000_0010 352 | dw 0b0111_1011_1111_1111 353 | dw 0b0000_1010_0100_0000 354 | dw 0b0111_1110_0111_1110 355 | dw 0b0100_0000_0000_0010 356 | dw 0b0111_1111_1111_1111 357 | dw 0b0000_0000_0000_0000 358 | 359 | ; 360 | ; Starting positions 361 | ; 362 | setup_data: 363 | dw BASE_MAZE+0x78*X_OFFSET+0x78 364 | dw BASE_MAZE+0x30*X_OFFSET+0x70 365 | dw BASE_MAZE+0x40*X_OFFSET+0x78 366 | dw BASE_MAZE+0x20*X_OFFSET+0x80 367 | dw BASE_MAZE+0x30*X_OFFSET+0x88 368 | 369 | ; 370 | ; Convert arrow codes to internal directions 371 | ; 372 | dirs: 373 | db 0x01 ; 0x48 = Up arrow 374 | db 0x00 375 | db 0x00 376 | db 0x08 ; 0x4b = Left arrow 377 | db 0x00 378 | db 0x02 ; 0x4d = Right arrow 379 | db 0x00 380 | db 0x00 381 | db 0x04 ; 0x50 = Down arrow 382 | 383 | %if com_file 384 | %else 385 | times 510-($-$$) db 0x4f 386 | db 0x55,0xaa ; Make it a bootable sector 387 | %endif 388 | -------------------------------------------------------------------------------- /test/rogue.asm: -------------------------------------------------------------------------------- 1 | ; 2 | ; bootRogue game in 512 bytes (boot sector) 3 | ; 4 | ; by Oscar Toledo G. 5 | ; http://nanochess.org/ 6 | ; 7 | ; (c) Copyright 2019 Oscar Toledo G. 8 | ; 9 | ; Creation date: Sep/19/2019. Generates room boxes. 10 | ; Revision date: Sep/20/2019. Connect rooms. Allows to navigate. 11 | ; Revision date: Sep/21/2019. Added ladders to go down/up. Shows 12 | ; Amulet of Yendor at level 26. Added 13 | ; circle of light. 14 | ; Revision date: Sep/22/2019. Creates monsters and items. Now has 15 | ; hp/exp. Food, armor, weapon, and traps 16 | ; works. Added battles. 829 bytes. 17 | ; Revision date: Sep/23/2019. Lots of optimization. 643 bytes. 18 | ; Revision date: Sep/24/2019. Again lots of optimization. 596 bytes. 19 | ; Revision date: Sep/25/2019. Many optimizations. 553 bytes. 20 | ; Revision date: Sep/26/2019. The final effort. 510 bytes. 21 | ; Revision date: Sep/27/2019. The COM file exits to DOS instead of halting. 22 | ; 23 | 24 | CPU 8086 25 | 26 | ROW_WIDTH: EQU 0x00A0 ; Width in bytes of each video row 27 | BOX_MAX_WIDTH: EQU 23 ; Max width of a room box 28 | BOX_MAX_HEIGHT: EQU 6 ; Max height of a room box 29 | BOX_WIDTH: EQU 26 ; Width of box area in screen 30 | BOX_HEIGHT: EQU 8 ; Height of box area in screen 31 | 32 | ; See page 45 of my book 33 | LIGHT_COLOR: EQU 0x06 ; Light color (brown, dark yellow on emu) 34 | HERO_COLOR: EQU 0x0e ; Hero color (yellow) 35 | 36 | ; See page 179 of my book 37 | GR_VERT: EQU 0xba ; Vertical line graphic 38 | GR_TOP_RIGHT: EQU 0xbb ; Top right graphic 39 | GR_BOT_RIGHT: EQU 0xbc ; Bottom right graphic 40 | GR_BOT_LEFT: EQU 0xc8 ; Bottom left graphic 41 | GR_TOP_LEFT: EQU 0xc9 ; Top left graphic 42 | GR_HORIZ: EQU 0xcd ; Horizontal line graphic 43 | 44 | GR_TUNNEL: EQU 0xb1 ; Tunnel graphic (shaded block) 45 | GR_DOOR: EQU 0xce ; Door graphic (crosshair graphic) 46 | GR_FLOOR: EQU 0xfa ; Floor graphic (middle point) 47 | 48 | GR_HERO: EQU 0x01 ; Hero graphic (smiling face) 49 | 50 | GR_LADDER: EQU 0xf0 ; Ladder graphic 51 | GR_TRAP: EQU 0x04 ; Trap graphic (diamond) 52 | GR_FOOD: EQU 0x05 ; Food graphic (clover) 53 | GR_ARMOR: EQU 0x08 ; Armor graphic (square with hole in center) 54 | GR_YENDOR: EQU 0x0c ; Amulet of Yendor graphic (Female sign) 55 | GR_GOLD: EQU 0x0f ; Gold graphic (asterisk, like brightness) 56 | GR_WEAPON: EQU 0x18 ; Weapon graphic (up arrow) 57 | 58 | YENDOR_LEVEL: EQU 26 ; Level of appearance for Amulet of Yendor 59 | 60 | %ifdef com_file 61 | org 0x0100 62 | %else 63 | org 0x7c00 64 | %endif 65 | 66 | ; 67 | ; Sorted by order of PUSH instructions 68 | ; 69 | rnd: equ 0x0006 ; Random seed 70 | hp: equ 0x0004 ; Current HP 71 | level: equ 0x0003 ; Current level (starting at 0x01) 72 | yendor: equ 0x0002 ; 0x01 = Not found. 0xff = Found. 73 | armor: equ 0x0001 ; Armor level 74 | weapon: equ 0x0000 ; Weapon level 75 | 76 | ; 77 | ; Start of the adventure! 78 | ; 79 | start: 80 | in ax,0x40 ; Read timer counter 81 | push ax ; Setup pseudorandom number generator 82 | 83 | mov ax,16 84 | push ax ; hp 85 | mov al,1 86 | push ax ; yendor (low byte) + level (high byte) 87 | push ax ; weapon (low byte) + armor (high byte) 88 | inc ax ; ax = 0x0002 (it was 0x0001) 89 | int 0x10 90 | mov ax,0xb800 ; Text video segment 91 | mov ds,ax 92 | mov es,ax 93 | 94 | mov bp,sp 95 | generate_dungeon: 96 | ; 97 | ; Advance to next level (can go deeper or higher) 98 | ; 99 | mov bl,[bp+yendor] 100 | add [bp+level],bl 101 | %ifdef com_file 102 | jne .0 103 | jmp quit ; Stop if level zero is reached 104 | .0: 105 | %else 106 | je $ ; Stop if level zero is reached 107 | %endif 108 | 109 | ; 110 | ; Select a maze for the dungeon 111 | ; 112 | ; There are many combinations of values that generate at least 113 | ; 16 mazes in order to avoid a table. 114 | ; 115 | mov ax,[bp+rnd] 116 | and ax,0x4182 117 | or ax,0x1a6d 118 | xchg ax,si 119 | 120 | ; 121 | ; Clean the screen 122 | ; 123 | xor ax,ax 124 | xor di,di 125 | mov ch,0x08 126 | rep stosw 127 | 128 | ; 129 | ; Draw the nine rooms 130 | ; 131 | mov ax,(BOX_HEIGHT/2-2)*ROW_WIDTH+(BOX_WIDTH/2-2)*2 132 | .7: 133 | push ax 134 | push ax 135 | add ax,ROW_WIDTH+4 ; Get the center of room 136 | xchg ax,di 137 | shr si,1 ; Obtain bit of right connection 138 | mov ax,0x0000+GR_TUNNEL 139 | mov cx,BOX_WIDTH 140 | jnc .3 141 | push di 142 | rep stosw ; Horizontal tunnel 143 | pop di 144 | .3: 145 | shr si,1 ; Obtain bit of down connection 146 | jnc .5 147 | mov cl,BOX_HEIGHT 148 | .4: 149 | stosb ; Vertical tunnel 150 | add di,ROW_WIDTH-1 151 | loop .4 152 | .5: 153 | mov bh,BOX_MAX_WIDTH-2 154 | call random ; Get a random width for room. 155 | xchg ax,cx 156 | mov bh,BOX_MAX_HEIGHT-2 157 | call random ; Get a random height for room. 158 | mov ch,al 159 | and al,0xfe ; It needs a/2*2 so this does it. 160 | mov ah,ROW_WIDTH/2 161 | mul ah 162 | add ax,cx ; Now it has a centering offset 163 | sub ah,ch ; Better than "mov bx,cx mov bh,0" 164 | and al,0xfe 165 | pop di 166 | sub di,ax ; Subtract from room center 167 | mov al,GR_TOP_LEFT ; Draw top row of room 168 | mov bx,GR_TOP_RIGHT*256+GR_HORIZ 169 | call fill 170 | .9: 171 | mov al,GR_VERT ; Draw intermediate row of room 172 | mov bx,GR_VERT*256+GR_FLOOR 173 | call fill 174 | dec ch 175 | jns .9 176 | mov al,GR_BOT_LEFT ; Draw bottom row of room 177 | mov bx,GR_BOT_RIGHT*256+GR_HORIZ 178 | call fill 179 | pop ax 180 | add ax,BOX_WIDTH*2 181 | cmp al,0xf2 ; Finished drawing three rooms? 182 | jne .6 ; No, jump 183 | ; Yes, go to following row 184 | add ax,ROW_WIDTH*BOX_HEIGHT-BOX_WIDTH*3*2 185 | .6: 186 | cmp ax,ROW_WIDTH*BOX_HEIGHT*3 187 | jb .7 188 | 189 | ; 190 | ; Put the ladder at a random corner room 191 | ; 192 | shl word [bp+rnd],1 193 | mov ax,3*ROW_WIDTH+12*2 194 | mov bx,19*ROW_WIDTH+12*2 195 | jnc .2 196 | xchg ax,bx 197 | .2: jns .8 198 | add ax,BOX_WIDTH*2*2 199 | .8: 200 | xchg ax,di 201 | 202 | mov byte [di],GR_LADDER 203 | 204 | ; 205 | ; If a deep level has been reached then put the Amulet of Yendor 206 | ; 207 | cmp byte [bp+level],YENDOR_LEVEL 208 | jb .1 209 | mov byte [bx],GR_YENDOR 210 | .1: 211 | ; 212 | ; Setup hero start 213 | ; 214 | mov di,11*ROW_WIDTH+38*2 215 | ; 216 | ; Main game loop 217 | ; 218 | game_loop: 219 | mov ax,game_loop ; Force to repeat, the whole loop... 220 | push ax ; ...ends with ret. 221 | 222 | ; 223 | ; Circle of light around the player (3x3) 224 | ; 225 | mov bx,0x0005 ; BX values 226 | .1: dec bx 227 | dec bx ; -1 1 3 -0x00a0 228 | mov al,LIGHT_COLOR 229 | mov [bx+di-ROW_WIDTH],al ; -1(1)3 230 | mov [bx+di],al 231 | mov [bx+di+ROW_WIDTH],al ; -1 1 3 +0x00a0 232 | jns .1 233 | 234 | ; 235 | ; Show our hero 236 | ; 237 | push word [di] ; Save character under 238 | mov word [di],HERO_COLOR*256+GR_HERO 239 | xor ax,ax 240 | call add_hp ; Update stats 241 | ; mov ah,0x00 ; Comes here with ah = 0 242 | int 0x16 ; Read keyboard 243 | pop word [di] ; Restore character under 244 | 245 | mov al,ah 246 | %ifdef com_file 247 | cmp al,0x01 248 | je quit ; Exit if Esc key is pressed 249 | %endif 250 | 251 | sub al,0x4c 252 | mov ah,0x02 ; Left/right multiplies by 2 253 | cmp al,0xff ; Going left (scancode 0x4b) 254 | je .2 255 | cmp al,0x01 ; Going right (scancode 0x4d) 256 | je .2 257 | cmp al,0xfc ; Going up (scancode 0x48) 258 | je .3 259 | cmp al,0x04 ; Going down (scancode 0x50) 260 | jne move_cancel 261 | .3: 262 | mov ah,0x28 ; Up/down multiplies by 40 263 | .2: 264 | imul ah ; Signed multiplication 265 | 266 | xchg ax,bx ; bx = displacement offset 267 | mov al,[di+bx] ; Read the target contents 268 | cmp al,GR_LADDER ; GR_LADDER? 269 | je ladder_found ; Yes, jump to next level 270 | ja move_over ; > it must be GR_FLOOR 271 | cmp al,GR_DOOR ; GR_DOOR? 272 | je move_over ; Yes, can move 273 | cmp al,GR_TUNNEL ; GR_TUNNEL? 274 | je move_over ; Yes, can move 275 | ja move_cancel ; > it must be border, cancel movement 276 | cmp al,GR_TRAP ; GR_TRAP? 277 | jb move_cancel ; < it must be blank, cancel movement 278 | lea di,[di+bx] ; Do move. 279 | je trap_found ; = Yes, went over trap 280 | cmp al,GR_WEAPON ; GR_WEAPON? 281 | ja battle ; > it's a monster, go to battle 282 | mov byte [di],GR_FLOOR ; Delete item from floor 283 | je weapon_found ; = weapon found 284 | cmp al,GR_ARMOR ; GR_ARMOR? 285 | je armor_found ; Yes, increase armor 286 | jb food_found ; < it's GR_FOOD, increase hp 287 | cmp al,GR_GOLD ; GR_GOLD? 288 | je move_cancel ; Yes, simply take it. 289 | ; At this point 'al' only can be GR_YENDOR 290 | ; Amulet of Yendor found! 291 | neg byte [bp+yendor] ; Now player goes upwards over ladders. 292 | ret 293 | move_over: 294 | lea di,[bx+di] ; Do move. 295 | move_cancel: 296 | ret ; Return to main loop. 297 | 298 | %ifdef com_file 299 | quit: 300 | int 0x20 301 | %endif 302 | 303 | ; 304 | ; I-- 305 | ; I-- 306 | ; I-- 307 | ; 308 | ladder_found: 309 | jmp generate_dungeon 310 | 311 | ; ______ 312 | ; I I 313 | ; I #X I 314 | ; I X# I 315 | ; \__/ 316 | ; 317 | armor_found: 318 | inc byte [bp+armor] ; Increase armor level 319 | ret 320 | 321 | ; 322 | ; /| _____________ 323 | ; (|===|oo>_____________> 324 | ; \| 325 | ; 326 | weapon_found: 327 | inc byte [bp+weapon] ; Increase weapon level 328 | ret 329 | 330 | ; 331 | ; /--\ 332 | ; ==== I 333 | ; \--/ 334 | ; 335 | food_found: 336 | call random6 ; Random 1-6 337 | jmp short add_hp 338 | 339 | ; 340 | ; Aaaarghhhh! 341 | ; 342 | trap_found: 343 | call random6 ; Random 1-6 344 | sub_hp: neg ax ; Make it negative 345 | add_hp: add ax,[bp+hp] ; Add to current HP 346 | %ifdef com_file 347 | js quit ; Exit if Esc key is pressed 348 | %else 349 | js $ ; Stall if dead 350 | %endif 351 | mov [bp+hp],ax ; Update HP. 352 | ; 353 | ; Update screen indicator 354 | ; 355 | mov bx,0x0f98 ; Point to bottom right corner 356 | .1: 357 | cwd ; Extend AX into DX 358 | mov cx,10 359 | div cx ; Divide by 10 360 | add dx,0x0a30 ; Add ASCII digit zero and color to remainder 361 | mov [bx],dx ; Put on screen 362 | dec bx 363 | dec bx 364 | or ax,ax ; More digits available? 365 | jnz .1 ; Yes, jump 366 | mov [bx],ax ; Erase character just in case number shrinks 367 | ret 368 | 369 | ; 370 | ; Let's battle!!! 371 | ; 372 | battle: 373 | and al,0x1f ; Separate number of monster (1-26) 374 | cbw ; Extend to 16 bits 375 | shl al,1 ; Make it slightly harder 376 | mov bl,al ; Its attack is equivalent to its number 377 | xchg ax,si ; Use also as its HP 378 | ; Player's attack 379 | .2: 380 | mov bh,[bp+weapon] ; Use current weapon level as dice 381 | call random 382 | sub si,ax ; Subtract from monster's HP 383 | mov bh,bl 384 | jc .3 ; Killed? yes, jump 385 | ; Monster's attack 386 | call random ; Use monster number as dice 387 | sub al,[bp+armor] ; Subtract armor from attack 388 | jc .4 389 | push bx 390 | call sub_hp ; Subtract from player's HP 391 | pop bx 392 | .4: 393 | ; mov ah,0x00 ; Comes here with ah = 0 394 | int 0x16 ; Wait for a key. 395 | jmp .2 ; Another battle round. 396 | 397 | ; 398 | ; Monster is dead 399 | ; 400 | .3: 401 | mov byte [di],GR_FLOOR ; Remove from screen 402 | ret 403 | 404 | ; 405 | ; Fill a row on screen for a room 406 | ; 407 | fill: push cx ; Save CX because it needs CL value again 408 | push di ; Save video position 409 | call door ; Left border 410 | .1: mov al,bl ; Filler 411 | call door 412 | dec cl 413 | jns .1 414 | mov al,bh ; Right border 415 | call door 416 | pop di ; Restore video position 417 | pop cx ; Restore CX 418 | add di,0x00a0 ; Goes to next row on screen 419 | ret 420 | 421 | ; 422 | ; Draw a room character on screen 423 | ; 424 | door: 425 | cmp al,GR_FLOOR ; Drawing floor? 426 | jne .3 ; No, jump 427 | push bx ; Here BH is equal to GR_VERT 428 | call random ; Get a random number 429 | cmp al,6 ; Chance of creating a monster 430 | jnc .11 431 | add al,[bp+level] ; More difficult monsters as level is deeper 432 | .9: 433 | sub al,0x05 434 | cmp al,0x17 ; Make sure it fits inside ASCII letters 435 | jge .9 436 | add al,0x44 ; Offset into ASCII letters 437 | jmp short .12 438 | 439 | .11: 440 | mov bx,items-6 ; Table of items 441 | cmp al,11 ; Chance of creating an item 442 | cs xlat 443 | jb .12 444 | mov al,GR_FLOOR ; Show only floor. 445 | .12: pop bx 446 | .3: 447 | cmp al,GR_HORIZ 448 | je .1 449 | cmp al,GR_VERT 450 | jne .2 451 | .1: cmp byte [di],GR_TUNNEL 452 | jne .2 453 | mov al,GR_DOOR 454 | .2: stosb 455 | inc di 456 | ret 457 | 458 | random6: 459 | mov bh,0x06 460 | 461 | random: 462 | mov ax,7841 463 | mul word [bp+rnd] 464 | add ax,83 465 | mov [bp+rnd],ax 466 | 467 | ; rdtsc ; Would make it dependent on Pentium II 468 | 469 | ; in al,(0x40) ; Only works for slow requirements. 470 | 471 | xor ah,ah 472 | div bh 473 | mov al,ah 474 | cbw 475 | inc ax 476 | ret 477 | 478 | ; 479 | ; Items 480 | ; 481 | items: 482 | db GR_FOOD 483 | db GR_GOLD 484 | db GR_TRAP 485 | db GR_WEAPON 486 | db GR_ARMOR 487 | 488 | %ifdef com_file 489 | %else 490 | times 510-($-$$) db 0x4f 491 | db 0x55,0xaa ; Make it a bootable sector 492 | %endif 493 | 494 | --------------------------------------------------------------------------------