├── .gitattributes ├── .gitignore ├── CMakeLists.txt ├── LICENSE.md ├── README.md ├── doc └── scc.gif ├── lib.h ├── sas.c ├── scc.c ├── scpp.c ├── sim86.c ├── swar.c └── test ├── CMakeLists.txt ├── dossim.c ├── sas ├── CMakeLists.txt ├── sasm.asm ├── sasm.ref ├── scc-self.asm └── scc-self.ref ├── scc ├── CMakeLists.txt ├── array.c ├── bug.c ├── comma.c ├── condop.c ├── deadcode.c ├── decl.c ├── dowhile.c ├── enum.c ├── lgoto.c ├── scc10.c ├── scc10.ref ├── sizeof.c ├── strlit.c ├── struct.c ├── switch.c ├── testlib.c ├── typedef.c ├── union.c └── unsigned.c ├── scpp ├── CMakeLists.txt ├── inc1.h ├── scc0.in ├── scc0.ref ├── test00.in ├── test00.ref ├── test01.in ├── test01.ref ├── test02.in ├── test02.ref ├── test03.in ├── test03.ref ├── test04.in ├── test04.ref ├── test05.in ├── test05.ref ├── test06.in ├── test06.ref ├── test07.in ├── test07.ref ├── test08.in ├── test08.ref ├── test09.in ├── test09.ref ├── test10.in ├── test10.ref ├── test11.in ├── test11.ref ├── test12.in ├── test12.ref ├── test13.in ├── test13.ref ├── test14.in ├── test14.ref ├── test15.in ├── test15.ref ├── test16.in ├── test16.ref ├── test17.in ├── test17.ref ├── test18.in ├── test18.ref ├── test19.in ├── test19.ref ├── test20.in ├── test20.ref ├── test21.in ├── test21.ref ├── test22.in ├── test22.ref ├── test23.in ├── test23.ref ├── test24.in ├── test24.ref ├── test25.in ├── test25.ref ├── test26.in ├── test26.ref ├── test27.in ├── test27.ref ├── test28.in ├── test28.ref ├── test29.in ├── test29.ref ├── test30.in ├── test30.ref ├── test31.in ├── test31.ref ├── test32.in ├── test32.ref ├── test33.in ├── test33.ref ├── test34.in ├── test34.ref ├── test35.in ├── test35.ref ├── test36.in ├── test36.ref ├── test37.in ├── test37.ref ├── test38.in ├── test38.ref ├── test39.in ├── test39.ref ├── test40.in ├── test40.ref ├── test41.in ├── test41.ref ├── test42.in ├── test42.ref ├── test43.in ├── test43.ref ├── test44.in ├── test44.ref ├── test45.in ├── test45.ref ├── test46.in ├── test46.ref ├── test47.in ├── test47.ref ├── test48.in ├── test48.ref ├── test49.in ├── test49.ref ├── test50.in ├── test50.ref ├── test51.in ├── test51.ref ├── test52.in ├── test52.ref ├── test53.in ├── test53.ref ├── test54.in ├── test54.ref ├── test55.in ├── test55.ref ├── test56.in ├── test56.ref ├── test57.in ├── test57.ref ├── test58.in ├── test58.ref ├── test59.in ├── test59.ref ├── test60.in ├── test60.ref ├── test61.in ├── test61.ref ├── test62.in ├── test62.ref ├── test63.in └── test63.ref └── sim86 ├── CMakeLists.txt └── test.asm /.gitattributes: -------------------------------------------------------------------------------- 1 | *.in binary 2 | *.ref binary 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *build/ 2 | *.com 3 | *.map 4 | *.i 5 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.7) 2 | project(scc C) 3 | 4 | if (MSVC) 5 | set(CMAKE_CONFIGURATION_TYPES "Debug;Release" CACHE STRING "Supported build configurations" FORCE) 6 | 7 | # warning C4715: 'X': not all control paths return a value 8 | # warning C4244: 'X': conversion from 'T' to 'U', possible loss of data 9 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /Zi /Zo /sdl /W4 /wd4715 /wd4244") 10 | set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /DEBUG") 11 | add_definitions("-D_CRT_SECURE_NO_WARNINGS") 12 | else() 13 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ggdb -Wall -Wextra -pedantic -std=c99 -Wno-return-type") 14 | endif() 15 | 16 | if (MSVC) 17 | set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT scc) 18 | endif() 19 | 20 | macro(SCC n) 21 | add_custom_command( 22 | OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${n}.com" 23 | COMMAND $ "${CMAKE_CURRENT_SOURCE_DIR}/${n}.c" ./${n}.com 24 | WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} 25 | DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/${n}.c" scc 26 | ) 27 | 28 | add_custom_target(${n}_com ALL DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/${n}.com") 29 | 30 | add_executable(${n} ${n}.c) # Make sure we can build on host as well 31 | endmacro() 32 | 33 | SCC(scc) 34 | SCC(scpp) 35 | SCC(sas) 36 | 37 | if (MSVC) 38 | # C4127: conditional expression is constant 39 | target_compile_options(scpp PRIVATE /wd4127) 40 | endif() 41 | 42 | # Simulator 43 | add_executable(sim86 sim86.c lib.h) 44 | add_custom_command( 45 | OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/sim86.com" 46 | COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/lib.h" . 47 | COMMAND $ "${CMAKE_CURRENT_SOURCE_DIR}/sim86.c" ./sim86.i 48 | COMMAND $ sim86.i sim86.com 49 | WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} 50 | DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/sim86.c" "${CMAKE_CURRENT_SOURCE_DIR}/lib.h" scc scpp 51 | ) 52 | add_custom_target(sim86_com ALL DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/sim86.com") 53 | 54 | # Productivity software :) 55 | add_custom_command( 56 | OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/swar.com" 57 | COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/lib.h" . 58 | COMMAND $ "${CMAKE_CURRENT_SOURCE_DIR}/swar.c" ./swar.i 59 | COMMAND $ swar.i swar.com 60 | WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} 61 | DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/swar.c" "${CMAKE_CURRENT_SOURCE_DIR}/lib.h" scc scpp 62 | ) 63 | add_custom_target(swar_com ALL DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/swar.com") 64 | if (WIN32) 65 | add_executable(swar swar.c lib.h) 66 | endif() 67 | 68 | add_subdirectory(test) 69 | 70 | set(MAKE_DISKS FALSE CACHE BOOL "Build disk images? (Downloads SASM)") 71 | if (MAKE_DISKS) 72 | include(ExternalProject) 73 | ExternalProject_Add(SASM 74 | GIT_REPOSITORY https://github.com/mras0/sasm.git 75 | GIT_TAG v1.1 76 | INSTALL_COMMAND "" 77 | ) 78 | ExternalProject_Get_property(SASM SOURCE_DIR) 79 | ExternalProject_Get_property(SASM BINARY_DIR) 80 | # HACK 81 | set(EXE_DIR "${BINARY_DIR}") 82 | if (MSVC) 83 | set(EXE_DIR "${EXE_DIR}/$") 84 | endif() 85 | set(DISKTOOL "${EXE_DIR}/disktool") 86 | set(BOOTSECT "${BINARY_DIR}/boot.bin") 87 | macro(MAKEDISK NAME SIZE) 88 | set(DISK_IMG "${CMAKE_CURRENT_BINARY_DIR}/${NAME}.img") 89 | message(STATUS "Making disk: ${DISK_IMG} ${SIZE}K") 90 | set(TEMPLATE COMMAND "${DISKTOOL}" "${DISK_IMG}") 91 | set(CMDS "") 92 | set(DEPS "") 93 | foreach(n ${ARGN}) 94 | set(CMDS ${CMDS} ${TEMPLATE} put "${n}") 95 | set(DEPS ${DEPS} "${n}") 96 | endforeach() 97 | add_custom_command( 98 | OUTPUT "${DISK_IMG}" 99 | ${TEMPLATE} create ${SIZE} 100 | ${TEMPLATE} boot "${BINARY_DIR}/boot.bin" 101 | ${TEMPLATE} put "${BINARY_DIR}/os.sys" 102 | ${TEMPLATE} put "${BINARY_DIR}/cmdp.com" 103 | ${TEMPLATE} put "${BINARY_DIR}/uvi.com" 104 | ${TEMPLATE} put "${BINARY_DIR}/debug.com" 105 | ${TEMPLATE} put "${PROJECT_SOURCE_DIR}/LICENSE.md" 106 | ${CMDS} 107 | DEPENDS ${DEPS} SASM 108 | ) 109 | add_custom_target(${NAME} ALL DEPENDS "${DISK_IMG}") 110 | endmacro() 111 | set(NL "\n") 112 | if (NOT WIN32) 113 | set(NL "\r\n") 114 | endif() 115 | file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/build.bat" 116 | "echo scc scc${NL}scc scc.c${NL}" 117 | "echo scc scc${NL}scc scc.c${NL}" 118 | "echo scc scpp${NL}scc scpp.c${NL}" 119 | "echo scpp sim86${NL}scpp sim86.c${NL}" 120 | "echo scc sim86${NL}scc sim86.i${NL}" 121 | "del sim86.i${NL}" # Remove this when debugging 122 | "echo Run \"sim86 scc.com scc.c scc2.com\" to test${NL}" 123 | ) 124 | set(SMALL_CONTENTS 125 | "${PROJECT_BINARY_DIR}/scc.com" 126 | "${PROJECT_BINARY_DIR}/scpp.com" 127 | "${PROJECT_BINARY_DIR}/sas.com" 128 | "${PROJECT_BINARY_DIR}/sim86.com" 129 | "${PROJECT_BINARY_DIR}/swar.com" 130 | "${CMAKE_CURRENT_BINARY_DIR}/build.bat" 131 | "${CMAKE_CURRENT_SOURCE_DIR}/scc.c" 132 | "${CMAKE_CURRENT_SOURCE_DIR}/scpp.c" 133 | "${CMAKE_CURRENT_SOURCE_DIR}/sas.c" 134 | "${CMAKE_CURRENT_SOURCE_DIR}/sim86.c" 135 | "${CMAKE_CURRENT_SOURCE_DIR}/lib.h" 136 | ) 137 | MAKEDISK(small 360 ${SMALL_CONTENTS}) 138 | MAKEDISK(large 1440 139 | ${SMALL_CONTENTS} 140 | "${CMAKE_CURRENT_SOURCE_DIR}/swar.c" 141 | "${SOURCE_DIR}/strap.bat" 142 | "${SOURCE_DIR}/sasm.asm" 143 | "${SOURCE_DIR}/boot.asm" 144 | "${SOURCE_DIR}/os.asm" 145 | "${SOURCE_DIR}/cmdp.asm" 146 | "${SOURCE_DIR}/insboot.asm" 147 | "${SOURCE_DIR}/debug.asm" 148 | "${SOURCE_DIR}/uvi.asm" 149 | ) 150 | 151 | add_custom_target( 152 | release 153 | COMMAND ${CMAKE_COMMAND} -E tar cv scc-dos.zip --format=zip -- 154 | "${PROJECT_BINARY_DIR}/scc.com" 155 | "${PROJECT_BINARY_DIR}/scpp.com" 156 | "${PROJECT_BINARY_DIR}/sas.com" 157 | "${PROJECT_BINARY_DIR}/sim86.com" 158 | "${PROJECT_BINARY_DIR}/swar.com" 159 | DEPENDS 160 | "${CMAKE_CURRENT_BINARY_DIR}/small.img" 161 | "${CMAKE_CURRENT_BINARY_DIR}/large.img" 162 | ) 163 | find_program(QEMU qemu-system-i386) 164 | if (QEMU) 165 | message("Found qemu: ${QEMU}") 166 | set(QEMU_EXTRA_ARGS "" CACHE STRING "Extra arguments to give QEMU. E.g. \"-s -S\" or \"-curses\"") 167 | set(DISK_IMG "${CMAKE_CURRENT_BINARY_DIR}/large.img") 168 | add_custom_target( 169 | qemu_test 170 | COMMAND qemu-system-i386 -drive format=raw,file=${DISK_IMG},if=floppy ${QEMU_EXTRA_ARGS} 171 | DEPENDS ${DISK_IMG} 172 | ) 173 | endif() 174 | endif() 175 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2020 Michael Rasmussen (michaelrasmussen1337@gmail.com) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SCC 2 | 3 | ## Introduction 4 | 5 | [SCC](scc.c) is a limited C compiler producing tiny-model (i.e. single segment) DOS COM files. It should run on most operating systems and can self-host under DOS2.0+ compatible systems (e.g. [SDOS](https://github.com/mras0/sasm)) with 128K RAM (the compiler only uses 64K, but the operating system needs to live as well :)). 6 | 7 | Also included are a small set of supporting software components: 8 | 9 | - [SCPP](scpp.c) - Pre-processor 10 | - [SAS](sas.c) - Assembler that can bootstrap [SDOS/SASM](https://github.com/mras0/sasm) 11 | - [SIM86](sim86.c) - Basic x86 simulator (Just enough to simulate the ecosystem) 12 | 13 | 14 | ## Building 15 | 16 | Compile `scc.c` using a C99 compiler and run the output on `scc.c`, this produces `scc.com` which can then be run under DOS (using e.g. [DOSBox](https://www.dosbox.com/) or the provided [simulator](sim86.c)) where it can self-compile: `scc.com scc.c`. These steps can then be repeated for the remainig software components. 17 | 18 | Or using CMake: 19 | 20 | mkdir build && cd build && cmake -DMAKE_DISKS:BOOL=TRUE .. && cmake --build . 21 | 22 | You can omit `-DMAKE_DISKS:BOOL=TRUE` if you don't want the disk images built. 23 | 24 | Running the test suite (will use DOSBox if available): 25 | 26 | cmake --build . --target test 27 | 28 | If building the disk images is enabled and [QEMU](https://www.qemu.org/) was found, you can build the `qemu_test` target to start QEMU with the large disk image attached. Use the CMake variable `QEMU_EXTRA_ARGS` to pass in extra arguments to QMEU. 29 | 30 | ## Trying 31 | 32 | ![SCC in action](doc/scc.gif) 33 | 34 | Download a [release](https://github.com/mras0/scc/releases) or build the software yourself (see above). 35 | 36 | I recommend using one of the disk images: 37 | 38 | - `small.img` is a 360K disk image containing the bare minimum. It's suitable for use with really old machines. 39 | - `large.img` is a 1440K disk image. You'll want to use this if you can. 40 | 41 | Fire up an emulator (or the real thing if you're lucky) e.g. [PCem](https://pcem-emulator.co.uk/), [QEMU](https://www.qemu.org/) or the excellent online [PCjs](https://www.pcjs.org/devices/pcx86/machine/). 42 | 43 | For example you can use the [IBM PC (Model 5150), 256Kb RAM, Color Display](https://www.pcjs.org/devices/pcx86/machine/5150/cga/256kb/). 44 | 1. Press the "Browse..." button and select the small disk image. 45 | 2. Press "Mount" and then "Ctrl-Alt-Del" 46 | 3. (Optional) At the SDOS prompt (`# `) type `build` and press enter to build the included software. Wait a couple of minutes for the software to build (Or press the 4.77MHz button a couple of times to speed it up). 47 | 4. Use `uvi` (a tiny vi clone) to edit source code, `scpp` to pre-process, `scc` to compile, `debug` to debug and `sim86` to simulate. 48 | 49 | Consider the classic hello world adapted to SCC: 50 | 51 | // hello.c 52 | #include "lib.h" 53 | void main() { 54 | puts("Hello world! From DOS!"); 55 | } 56 | 57 | To build and run (don't actually type the comments): 58 | 59 | uvi hello.c # Edit (press "i" to enter insert mode and press escape to exit it. ":wq" to write & quit) 60 | scpp hello.c # Pre-process to produce hello.i 61 | scc hello.i # Compile 62 | hello.com # Run 63 | 64 | Or on your normal system: 65 | 66 | favorite-editor hello.c && scpp hello.c && scc hello.i && sim86 hello.com 67 | 68 | Some notes: 69 | 70 | - On your host system, you can use `sim86 -p hello.com` to get basic performance information (basically the number of memory accesses performed) 71 | - `lib.h` contains an extremely bare-bones standard library and start-up code that calls `main`. On non-SCC platforms it includes the corresponding headers instead, allowing the same code to compile unmodified on other systems. 72 | - You'll probably want to create a batch file with those steps if you plan on running them more than once. 73 | - If you're doing anything remotely useful, you'll probably want to ditch the pre-processing step and build your own standard library. 74 | 75 | A more realistic hello world example would be: 76 | 77 | void PutChar(int ch) { 78 | _emit 0xB4 _emit 0x02 // MOV AH, 2 79 | _emit 0x8A _emit 0x56 _emit 0x04 // MOV DL, [BP+4] 80 | _emit 0xCD _emit 0x21 // INT 0x21 81 | } 82 | 83 | void _start() { 84 | const char* s = "Hello DOS!\r\n"; 85 | while (*s) PutChar(*s++); 86 | } 87 | 88 | Which should give you a (vague) idea of how to overcome the limitations of SCC (you can see more examples in [lib.h](lib.h) and [sim86.c](sim86.c) by searching for `_emit`). 89 | 90 | ## Q&A 91 | 92 | #### Why would I want this? 93 | 94 | You probably don't. If you're actually looking to compile C code for DOS look into [DJGPP](http://www.delorie.com/djgpp/) or [OpenWatcom](http://openwatcom.org/). 95 | This is just a hobby project to create a compiler for another [NIH](https://en.wikipedia.org/wiki/Not_invented_here) project of mine: [SDOS/SASM](https://github.com/mras0/sasm). 96 | 97 | #### Why is the user experience so terrible? 98 | 99 | Good UX uses precious bytes. Sorry :( 100 | 101 | #### Why not support other memory models? 102 | 103 | I wanted to keep it simple and not have to rely too much on inline "assembly" (read: `_emit` statements) magic. 104 | Out of necessity [SIM86](sim86.c) has to use more than 64K, so there's a clear path to allowing larger output files, but for now I prefer the challenge of keeping the memory usage down :) 105 | 106 | #### What are some similar projects? 107 | 108 | - [TCC](https://bellard.org/tcc/) 109 | - [8cc](https://github.com/rui314/8cc) 110 | - [c4](https://github.com/rswier/c4/) 111 | 112 | ## Limitations 113 | 114 | A non-exhaustive list of the current limitations of SCC: 115 | 116 | - Arbitrary limits to e.g. the number of struct members (you can to some degree change these) 117 | - Very limited error checking/information (see the debugging section for help) 118 | - Integers are limited to 16-bits (and no support for `long`) 119 | - Tiny memory model (i.e. all code and data must live within a single 64K segment). `CS=DS=ES=SS` is assumed. (See [SIM86](sim86.c) for an example of how to work around this limitation) 120 | - Only basic support for initializers (rules are complex, but if it's not in the test suite it's not supported...) 121 | - No floating point support 122 | - No bitfields 123 | - Uncountably many bugs :) 124 | 125 | ## Debugging 126 | 127 | First make sure the source code (to the greatest degree possible) compiles and works with another C compiler. 128 | SCC assumes the source code works and only does a bare minimum of checking. Many invalid C programs are accepted and many valid C programs are rejected (or miscompiled!). 129 | 130 | Try simplifying the code. SCC isn't clever yet. 131 | 132 | Use the simulator ([SIM86](sim86.c)) - on your host system `-p` also provides a convinient disassembly of all reached instructions. 133 | 134 | Change the code of the simulator to debug hard problems! 135 | 136 | #### Stack traces 137 | 138 | Some errors will be reported as a stack trace (see below). For these cases you'll need to refer to the map file produced in addition to the com file (i.e. `scc.map` for `scc.com`). 139 | 140 | Example: 141 | 142 | BP Return address 143 | FFBE 07A5 144 | FFC8 4844 145 | FFD6 48E4 146 | FFDA 4920 147 | FFDE 51FE 148 | FFE8 5892 149 | FFFC 03D5 150 | In line 48: Check failed 151 | 152 | Consulting the map file for the closest (not greater than) symbol: 153 | 154 | 07A5 # 0786 Fail 155 | 4844 # 4666 ParseDeclarator 156 | 48E4 # 48D5 DoDecl 157 | 4920 # 4908 ParseFirstDecl 158 | 51FE # 51F8 ParseExternalDefition 159 | 5892 # 55F3 main 160 | 03D5 # 03A2 _start 161 | 162 | #### Compile errors 163 | 164 | Note that: 165 | 166 | - The reported line number is past the current token (meaning lots of whitespace/comments may have been skipped). 167 | - If the pre-processor was used you need to manually reverse engineer the real line from the pre-processed file (since that's where the line number points) 168 | - Running SCC on your host system may provide a line number to look up in `scc.c` for check failures. 169 | 170 | If you can reproduce the problem on your host system you should use a debugger attached to `scc` to get an idea of where and why the issue happens. 171 | 172 | #### Other problems 173 | 174 | If you suspect a compiler bug, you'll probably have to isolate the issue and look manually inspect the generated code. 175 | 176 | ## Technical Notes 177 | 178 | Apart from some minor exceptions SCC is a single pass compiler. It tries really hard to only consider the current character/token/(sub-)expression to preserve memory. 179 | An [AST](https://en.wikipedia.org/wiki/Abstract_syntax_tree) isn't built, rather code is outputted directly in response to expressions and statements. 180 | 181 | Within these constraints SCC tries to generate somewhat optimized code. Many possible optimizations aren't performed because they don't pay off (meaning they don't optimize SCC itself either in time or size). 182 | 183 | There are some minor optimizations done while doing address fixups to compensate for the limited optimizations. These are the only backward looking optimizations. 184 | 185 | More to come... (maybe). 186 | -------------------------------------------------------------------------------- /doc/scc.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mras0/scc/8cc3ebe4d8940bd60c515a1994b7d5273872585b/doc/scc.gif -------------------------------------------------------------------------------- /lib.h: -------------------------------------------------------------------------------- 1 | #ifndef __SCC__ 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | #ifdef _MSC_VER 14 | #include 15 | #define open _open 16 | #define close _close 17 | #define read _read 18 | #define write _write 19 | #define NORETURN __declspec(noreturn) 20 | #else 21 | #ifndef O_BINARY 22 | #define O_BINARY 0 23 | #endif 24 | #include 25 | #define NORETURN __attribute__((noreturn)) 26 | #endif 27 | 28 | #else 29 | #define NORETURN 30 | enum { 31 | O_RDONLY = 0, 32 | O_WRONLY = 1, 33 | O_CREAT = 1, 34 | O_TRUNC = 1, 35 | O_BINARY = 0, 36 | }; 37 | 38 | void memcpy(void* dst, void* src, int size) 39 | { 40 | _emit 0x8B _emit 0x7E _emit 0x04 // MOV DI,[BP+0x4] 41 | _emit 0x8B _emit 0x76 _emit 0x06 // MOV SI,[BP+0x6] 42 | _emit 0x8B _emit 0x4E _emit 0x08 // MOV CX,[BP+0x8] 43 | _emit 0xF3 _emit 0xA4 // REP MOVSB 44 | } 45 | 46 | void memset(void* ptr, int val, int size) 47 | { 48 | _emit 0x8B _emit 0x7E _emit 0x04 // MOV DI,[BP+0x4] 49 | _emit 0x8B _emit 0x46 _emit 0x06 // MOV AX,[BP+0x6] 50 | _emit 0x8B _emit 0x4E _emit 0x08 // MOV CX,[BP+0x8] 51 | _emit 0xF3 _emit 0xAA // REP STOSB 52 | } 53 | 54 | int DosCall(int* ax, int bx, int cx, int dx) 55 | { 56 | // Use segment override to allow DS to not point at data segment 57 | 58 | _emit 0x8B _emit 0x5E _emit 0x04 // 8B5E04 MOV BX,[BP+0x4] 59 | _emit 0x36 _emit 0x8B _emit 0x07 // 8B07 MOV AX,[SS:BX] 60 | _emit 0x8B _emit 0x5E _emit 0x06 // 8B5E06 MOV BX,[BP+0x6] 61 | _emit 0x8B _emit 0x4E _emit 0x08 // 8B4E08 MOV CX,[BP+0x8] 62 | _emit 0x8B _emit 0x56 _emit 0x0A // 8B560A MOV DX,[BP+0xA] 63 | _emit 0xCD _emit 0x21 // CD21 INT 0x21 64 | _emit 0x8B _emit 0x5E _emit 0x04 // 8B5E04 MOV BX,[BP+0x4] 65 | _emit 0x36 _emit 0x89 _emit 0x07 // 8907 MOV [SS:BX],AX 66 | _emit 0xB8 _emit 0x00 _emit 0x00 // B80000 MOV AX,0x0 67 | _emit 0x19 _emit 0xC0 // 19C0 SBB AX,AX 68 | } 69 | 70 | void exit(int retval) 71 | { 72 | retval = (unsigned char)retval | 0x4c00; 73 | DosCall(&retval, 0, 0, 0); 74 | } 75 | 76 | void raw_putchar(int ch) 77 | { 78 | int ax = 0x200; 79 | DosCall(&ax, 0, 0, ch); 80 | } 81 | 82 | void putchar(int ch) 83 | { 84 | if (ch == '\n') raw_putchar('\r'); 85 | raw_putchar(ch); 86 | } 87 | 88 | int open(const char* filename, int flags, ...) 89 | { 90 | int ax; 91 | if (flags) 92 | ax = 0x3c00; // create or truncate file 93 | else 94 | ax = 0x3d00; // open existing file 95 | 96 | if (DosCall(&ax, 0, 0, filename)) 97 | return -1; 98 | return ax; 99 | } 100 | 101 | void close(int fd) 102 | { 103 | int ax = 0x3e00; 104 | DosCall(&ax, fd, 0, 0); 105 | } 106 | 107 | int read(int fd, char* buf, int count) 108 | { 109 | int ax = 0x3f00; 110 | if (DosCall(&ax, fd, count, buf)) 111 | return 0; 112 | return ax; 113 | } 114 | 115 | int write(int fd, const char* buf, int count) 116 | { 117 | int ax = 0x4000; 118 | if (DosCall(&ax, fd, count, buf)) 119 | return 0; 120 | return ax; 121 | } 122 | 123 | enum { _ARGV = 0x5C }; // Overwrite unopened FCBs 124 | 125 | int ParseArgs() 126 | { 127 | char** Args = _ARGV; 128 | char* CmdLine = (char*)0x80; 129 | int Len = *CmdLine++ & 0x7f; 130 | CmdLine[Len] = 0; 131 | Args[0] = "???"; 132 | int NumArgs = 1; 133 | for (;;) { 134 | while (*CmdLine && *CmdLine <= ' ') 135 | ++CmdLine; 136 | if (!*CmdLine) 137 | break; 138 | Args[NumArgs++] = CmdLine; 139 | while (*CmdLine && *CmdLine > ' ') 140 | ++CmdLine; 141 | if (!*CmdLine) 142 | break; 143 | *CmdLine++ = 0; 144 | } 145 | Args[NumArgs] = 0; 146 | return NumArgs; 147 | } 148 | 149 | int main(int argc, char** argv); 150 | 151 | void _start(void) 152 | { 153 | // Clear BSS 154 | memset(&_SBSS, 0, &_EBSS-&_SBSS); 155 | exit(main(ParseArgs(), _ARGV)); 156 | } 157 | 158 | char* CopyStr(char* dst, const char* src) 159 | { 160 | while (*src) *dst++ = *src++; 161 | *dst = 0; 162 | return dst; 163 | } 164 | 165 | char* vsprintf(char* dest, const char* format, va_list vl) 166 | { 167 | char TempBuf[9]; 168 | char ch; 169 | while ((ch = *format++)) { 170 | if (ch != '%') { 171 | *dest++ = ch; 172 | continue; 173 | } 174 | char fill = ' '; 175 | int w = 0; 176 | Restart: 177 | ch = *format++; 178 | if (!w && ch == '0') { 179 | fill = '0'; 180 | goto Restart; 181 | } else if (ch >= '0' && ch <= '9') { 182 | w = w*10 + ch - '0'; 183 | goto Restart; 184 | } 185 | if (ch == 's') { 186 | dest = CopyStr(dest, va_arg(vl, char*)); 187 | } else if(ch == 'c') { 188 | *dest++ = (char)va_arg(vl, int); 189 | } else if ((ch == '+' && *format == 'd') || ch == 'd') { 190 | char* buf; 191 | int n; 192 | int s; 193 | int always; 194 | if (ch == '+') { 195 | ++format; 196 | always = 1; 197 | } else { 198 | always = 0; 199 | } 200 | n = va_arg(vl, int); 201 | s = 0; 202 | if (n < 0) { 203 | s = 1; 204 | n = -n; 205 | } 206 | buf = TempBuf + sizeof(TempBuf); 207 | *--buf = 0; 208 | do { 209 | *--buf = '0' + n % 10; 210 | n/=10; 211 | --w; 212 | } while (n); 213 | if (s) { 214 | *--buf = '-'; 215 | --w; 216 | } else if (always) { 217 | *--buf = '+'; 218 | --w; 219 | } 220 | while (w-- > 0) *dest++ = fill; 221 | dest = CopyStr(dest, buf); 222 | } else if (ch == 'x' || ch == 'X') { 223 | const char* HexD = "0123456789ABCDEF"; 224 | int n = va_arg(vl, int); 225 | int i = 3; 226 | if (w) i = w-1; 227 | for (; i >= 0; --i) { 228 | *dest++ = HexD[(n>>i*4)&0xf]; 229 | } 230 | } else { 231 | *dest++ = ch; 232 | } 233 | } 234 | *dest = 0; 235 | return dest; 236 | } 237 | 238 | void sprintf(char* str, const char* format, ...) 239 | { 240 | va_list args; 241 | va_start(args, format); 242 | vsprintf(str, format, args); 243 | va_end(args); 244 | } 245 | 246 | void PutStr(const char* s) 247 | { 248 | while (*s) putchar(*s++); 249 | } 250 | 251 | char msg[256]; // XXX 252 | void printf(const char* format, ...) 253 | { 254 | va_list args; 255 | va_start(args, format); 256 | vsprintf(msg, format, args); 257 | va_end(args); 258 | PutStr(msg); 259 | } 260 | 261 | void puts(const char* s) 262 | { 263 | PutStr(s); 264 | putchar('\n'); 265 | } 266 | 267 | void strcpy(char* dst, const char* src) 268 | { 269 | CopyStr(dst, src); 270 | } 271 | 272 | int strcmp(const char* a, const char* b) 273 | { 274 | _emit 0x8B _emit 0x76 _emit 0x04 // MOV SI, [BP+4] 275 | _emit 0x8B _emit 0x7E _emit 0x06 // MOV DI, [BP+6] 276 | _emit 0xAC // L: LODSB 277 | _emit 0x8A _emit 0x25 // MOV AH, [DI] 278 | _emit 0x47 // INC DI 279 | _emit 0x28 _emit 0xE0 // SUB AL, AH 280 | _emit 0x75 _emit 0x04 // JNZ DONE 281 | _emit 0x20 _emit 0xE4 // AND AH, AH 282 | _emit 0x75 _emit 0xF4 // JNZ L 283 | _emit 0x98 // DONE: CBW 284 | } 285 | 286 | int strlen(const char* s) 287 | { 288 | _emit 0x8B _emit 0x4E _emit 0x04 // MOV CX, [BP+4] 289 | _emit 0x89 _emit 0xCE // MOV SI, CX 290 | _emit 0xAC // L: LODSB 291 | _emit 0x20 _emit 0xC0 // AND AL, AL 292 | _emit 0x75 _emit 0xFB // JNZ L 293 | _emit 0x96 // XCHG AX, SI 294 | _emit 0x29 _emit 0xC8 // SUB AX, CX 295 | _emit 0x48 // DEC AX 296 | } 297 | 298 | int* GetBP(void) 299 | { 300 | _emit 0x8B _emit 0x46 _emit 0x00 // MOV AX, [BP] 301 | } 302 | 303 | int GetDS(void) 304 | { 305 | _emit 0x8C _emit 0xD8 // MOV AX, DS 306 | } 307 | 308 | int FarPeek(int seg, int off) 309 | { 310 | _emit 0x1E // PUSH DS 311 | _emit 0x8E _emit 0x5E _emit 0x04 // MOV DS, [BP+4] 312 | _emit 0x8B _emit 0x76 _emit 0x06 // MOV SI, [BP+6] 313 | _emit 0xAC // LODSB 314 | _emit 0x30 _emit 0xE4 // XOR AH, AH 315 | _emit 0x1F // POP DS 316 | } 317 | 318 | void FarPoke(int seg, int off, int val) 319 | { 320 | _emit 0x06 // PUSH ES 321 | _emit 0x8E _emit 0x46 _emit 0x04 // MOV ES, [BP+4] 322 | _emit 0x8B _emit 0x7E _emit 0x06 // MOV DI, [BP+6] 323 | _emit 0x8A _emit 0x46 _emit 0x08 // MOV AL, [BP+8] 324 | _emit 0xAA // STOSB 325 | _emit 0x07 // POP ES 326 | } 327 | 328 | int clock() 329 | { 330 | _emit 0x31 _emit 0xC0 // XOR AX, AX 331 | _emit 0x8E _emit 0xD8 // MOV DS, AX 332 | _emit 0xA1 _emit 0x6C _emit 0x04 // MOV AX, [0x46C] 333 | _emit 0x0E // PUSH CS 334 | _emit 0x1F // POP DS 335 | } 336 | 337 | void assert(int res) 338 | { 339 | if (!res) { 340 | printf("Assertion failed\n"); 341 | int* BP = GetBP(); 342 | printf("\nBP Return address\n"); 343 | while (*BP) { 344 | printf("%X %X\n", BP[0], BP[1]); 345 | BP = (int*)BP[0]; 346 | } 347 | exit(1); 348 | } 349 | } 350 | #endif 351 | -------------------------------------------------------------------------------- /sas.c: -------------------------------------------------------------------------------- 1 | // SAS - Very basic assembler for DOS (part of SCC) 2 | // Copyright 2020 Michael Rasmussen. See LICENSE.md for details. 3 | 4 | #ifndef __SCC__ 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #ifdef _MSC_VER 16 | #include 17 | #define open _open 18 | #define close _close 19 | #define read _read 20 | #define write _write 21 | #else 22 | #ifndef O_BINARY 23 | #define O_BINARY 0 24 | #endif 25 | #include 26 | #endif 27 | 28 | #define CREATE_FLAGS O_WRONLY | O_CREAT | O_TRUNC | O_BINARY 29 | 30 | #else 31 | // Small "standard library" 32 | 33 | enum { CREATE_FLAGS = 1 }; // Ugly hack 34 | 35 | void memcpy(char* dst, const char* src, int size) 36 | { 37 | while (size--) 38 | *dst++ = *src++; 39 | } 40 | 41 | void memset(char* dst, int val, int size) 42 | { 43 | while (size--) 44 | *dst++ = val; 45 | } 46 | 47 | int DosCall(int* ax, int bx, int cx, int dx) 48 | { 49 | _emit 0x8B _emit 0x5E _emit 0x04 // 8B5E04 MOV BX,[BP+0x4] 50 | _emit 0x8B _emit 0x07 // 8B07 MOV AX,[BX] 51 | _emit 0x8B _emit 0x5E _emit 0x06 // 8B5E06 MOV BX,[BP+0x6] 52 | _emit 0x8B _emit 0x4E _emit 0x08 // 8B4E08 MOV CX,[BP+0x8] 53 | _emit 0x8B _emit 0x56 _emit 0x0A // 8B560A MOV DX,[BP+0xA] 54 | _emit 0xCD _emit 0x21 // CD21 INT 0x21 55 | _emit 0x8B _emit 0x5E _emit 0x04 // 8B5E04 MOV BX,[BP+0x4] 56 | _emit 0x89 _emit 0x07 // 8907 MOV [BX],AX 57 | _emit 0xB8 _emit 0x00 _emit 0x00 // B80000 MOV AX,0x0 58 | _emit 0x19 _emit 0xC0 // 19C0 SBB AX,AX 59 | } 60 | 61 | void exit(int retval) 62 | { 63 | retval = (retval & 0xff) | 0x4c00; 64 | DosCall(&retval, 0, 0, 0); 65 | } 66 | 67 | void raw_putchar(int ch) 68 | { 69 | int ax; 70 | ax = 0x200; 71 | DosCall(&ax, 0, 0, ch); 72 | } 73 | 74 | void putchar(int ch) 75 | { 76 | if (ch == '\n') raw_putchar('\r'); 77 | raw_putchar(ch); 78 | } 79 | 80 | int open(const char* filename, int flags, ...) 81 | { 82 | int ax; 83 | if (flags) 84 | ax = 0x3c00; // create or truncate file 85 | else 86 | ax = 0x3d00; // open existing file 87 | 88 | if (DosCall(&ax, 0, 0, filename)) 89 | return -1; 90 | return ax; 91 | } 92 | 93 | void close(int fd) 94 | { 95 | int ax; 96 | ax = 0x3e00; 97 | DosCall(&ax, fd, 0, 0); 98 | } 99 | 100 | int read(int fd, char* buf, int count) 101 | { 102 | int ax; 103 | ax = 0x3f00; 104 | if (DosCall(&ax, fd, count, buf)) 105 | return 0; 106 | return ax; 107 | } 108 | 109 | int write(int fd, const char* buf, int count) 110 | { 111 | int ax; 112 | ax = 0x4000; 113 | if (DosCall(&ax, fd, count, buf)) 114 | return 0; 115 | return ax; 116 | } 117 | 118 | int main(int argc, char** argv); 119 | void _start(void) 120 | { 121 | // Clear BSS 122 | memset(&_SBSS, 0, &_EBSS-&_SBSS); 123 | 124 | char* CmdLine = (char*)0x80; 125 | int Len = *CmdLine++ & 0xff; 126 | CmdLine[Len] = 0; 127 | 128 | char *Args[10]; // Arbitrary limit 129 | int NumArgs; 130 | 131 | Args[0] = "sas"; 132 | NumArgs = 1; 133 | 134 | while (*CmdLine) { 135 | while (*CmdLine && *CmdLine <= ' ') 136 | ++CmdLine; 137 | if (!*CmdLine) 138 | break; 139 | Args[NumArgs++] = CmdLine; 140 | while (*CmdLine && *CmdLine > ' ') 141 | ++CmdLine; 142 | if (!*CmdLine) 143 | break; 144 | *CmdLine++ = 0; 145 | } 146 | Args[NumArgs] = 0; 147 | exit(main(NumArgs, Args)); 148 | } 149 | 150 | char* CopyStr(char* dst, const char* src) 151 | { 152 | while (*src) *dst++ = *src++; 153 | *dst = 0; 154 | return dst; 155 | } 156 | 157 | char* vsprintf(char* dest, const char* format, va_list vl) 158 | { 159 | char TempBuf[9]; 160 | char ch; 161 | while ((ch = *format++)) { 162 | if (ch != '%') { 163 | *dest++ = ch; 164 | continue; 165 | } 166 | ch = *format++; 167 | if (ch == 's') { 168 | dest = CopyStr(dest, va_arg(vl, char*)); 169 | } else if(ch == 'c') { 170 | *dest++ = (char)va_arg(vl, int); 171 | } else if ((ch == '+' && *format == 'd') || ch == 'd') { 172 | char* buf; 173 | int n; 174 | int s; 175 | int always; 176 | if (ch == '+') { 177 | ++format; 178 | always = 1; 179 | } else { 180 | always = 0; 181 | } 182 | n = va_arg(vl, int); 183 | s = 0; 184 | if (n < 0) { 185 | s = 1; 186 | n = -n; 187 | } 188 | buf = TempBuf + sizeof(TempBuf); 189 | *--buf = 0; 190 | do { 191 | *--buf = '0' + n % 10; 192 | n/=10; 193 | } while (n); 194 | if (s) *--buf = '-'; 195 | else if (always) *--buf = '+'; 196 | dest = CopyStr(dest, buf); 197 | } else if (ch == 'X') { 198 | const char* HexD = "0123456789ABCDEF"; 199 | int n = va_arg(vl, int); 200 | int i; 201 | for (i = 3; i >= 0; --i) { 202 | *dest++ = HexD[(n>>i*4)&0xf]; 203 | } 204 | } else { 205 | *dest++ = ch; 206 | } 207 | } 208 | *dest = 0; 209 | return dest; 210 | } 211 | 212 | void sprintf(char* str, const char* format, ...) 213 | { 214 | va_list args; 215 | va_start(args, format); 216 | vsprintf(str, format, args); 217 | va_end(args); 218 | } 219 | 220 | void printf(const char* format, ...) 221 | { 222 | char msg[80]; 223 | va_list args; 224 | va_start(args, format); 225 | vsprintf(msg, format, args); 226 | va_end(args); 227 | char* s = msg; 228 | while (*s) putchar(*s++); 229 | } 230 | 231 | void strcpy(char* dst, const char* src) 232 | { 233 | CopyStr(dst, src); 234 | } 235 | 236 | int strlen(const char* s) 237 | { 238 | int l = 0; 239 | while (*s++) 240 | ++l; 241 | return l; 242 | } 243 | 244 | #endif 245 | 246 | enum { 247 | ID_MAX = 600, 248 | ID_HASH_MAX = 1024, // Must be power of 2 and larger than ID_MAX 249 | IDBUFFER_MAX = 5000, 250 | OUTPUT_MAX = 20000, 251 | INBUF_MAX = 512, 252 | FIXUP_MAX = 600, 253 | }; 254 | 255 | enum { 256 | TOK_EOF, 257 | TOK_NUMBER, 258 | TOK_STRING, 259 | 260 | TOK_PLUS = '+', 261 | TOK_MINUS = '-', 262 | TOK_COLON = ':', 263 | TOK_COMMA = ',', 264 | TOK_LBRACKET = '[', 265 | TOK_RBRACKET = ']', 266 | TOK_PIPE = '|', 267 | TOK_TILDE = '~', 268 | 269 | TOK_LSH = 128, 270 | 271 | // Registers 272 | 273 | // We want the lower bits of the registers to match their index 274 | TOK_AL = 256, TOK_CL, TOK_DL, TOK_BL, TOK_AH, TOK_CH, TOK_DH, TOK_BH, 275 | TOK_AX, TOK_CX, TOK_DX, TOK_BX, TOK_SP, TOK_BP, TOK_SI, TOK_DI, 276 | TOK_ES, TOK_CS, TOK_SS, TOK_DS, 277 | 278 | // Modifiers 279 | TOK_BYTE, 280 | TOK_SHORT, 281 | TOK_WORD, 282 | 283 | // Directives 284 | TOK_CPU, 285 | TOK_DB, 286 | TOK_DW, 287 | TOK_EQU, 288 | TOK_ORG, 289 | TOK_RESB, 290 | TOK_RESW, 291 | 292 | // Instructions 293 | TOK_ADC, 294 | TOK_ADD, 295 | TOK_AND, 296 | TOK_CALL, 297 | TOK_CBW, 298 | TOK_CLC, 299 | TOK_CLI, 300 | TOK_CMP, 301 | TOK_CMPSB, 302 | TOK_CWD, 303 | TOK_DEC, 304 | TOK_DIV, 305 | TOK_IDIV, 306 | TOK_IMUL, 307 | TOK_INC, 308 | TOK_INT, 309 | TOK_JMP, 310 | TOK_LEA, 311 | TOK_LODSB, 312 | TOK_MOV, 313 | TOK_MOVSB, 314 | TOK_MUL, 315 | TOK_NEG, 316 | TOK_NOT, 317 | TOK_OR, 318 | TOK_POP, 319 | TOK_PUSH, 320 | TOK_REP, 321 | TOK_RET, 322 | TOK_SAR, 323 | TOK_SBB, 324 | TOK_SHL, 325 | TOK_SHR, 326 | TOK_STC, 327 | TOK_STI, 328 | TOK_STOSB, 329 | TOK_STOSW, 330 | TOK_SUB, 331 | TOK_TEST, 332 | TOK_XCHG, 333 | TOK_XOR, 334 | 335 | // Jcc 336 | TOK_JO, // 0x0 337 | TOK_JNO, // 0x1 338 | TOK_JC, // 0x2 339 | TOK_JNC, // 0x3 340 | TOK_JZ, // 0x4 341 | TOK_JNZ, // 0x5 342 | TOK_JNA, // 0x6 343 | TOK_JA, // 0x7 344 | TOK_JS, // 0x8 345 | TOK_JNS, // 0x9 346 | TOK_JPE, // 0xa 347 | TOK_JPO, // 0xb 348 | TOK_JL, // 0xc 349 | TOK_JNL, // 0xd 350 | TOK_JNG, // 0xe 351 | TOK_JG, // 0xf 352 | 353 | // Jcc synonyms 354 | TOK_JB, // JC 355 | TOK_JNAE, // JC 356 | TOK_JNB, // JNC 357 | TOK_JAE, // JNC 358 | TOK_JE, // JZ 359 | TOK_JNE, // JNZ 360 | TOK_JBE, // JNA 361 | TOK_JNBE, // JA 362 | TOK_JNGE, // JL 363 | TOK_JGE, // JNL 364 | TOK_JLE, // JNG 365 | TOK_JNLE, // JG 366 | 367 | TOK_USER_ID, 368 | TOK_ID = TOK_AL, 369 | TOK_DIRECTIVE = TOK_CPU, 370 | TOK_INST = TOK_ADC, 371 | }; 372 | 373 | enum { 374 | FIXUP_REL8, 375 | FIXUP_REL16, 376 | FIXUP_DISP16, 377 | }; 378 | 379 | struct Fixup { 380 | char Type; 381 | int Offset; 382 | struct Fixup* Next; 383 | }; 384 | struct Fixup Fixups[FIXUP_MAX]; 385 | struct Fixup* NextFixup; 386 | 387 | enum { 388 | LABEL_NONE, 389 | LABEL_EQU, 390 | LABEL_NORMAL, 391 | }; 392 | 393 | struct Label { 394 | char Type; 395 | int Val; 396 | struct Fixup* Fixups; 397 | }; 398 | 399 | int InFile; 400 | char InBuf[INBUF_MAX]; 401 | int InBufCnt; 402 | int InBufSize; 403 | 404 | int Line = 1; 405 | char CurrentChar; 406 | 407 | char IdBuffer[IDBUFFER_MAX]; 408 | int IdOffset[ID_MAX]; 409 | int IdHash[ID_HASH_MAX]; 410 | int IdBufferIndex; 411 | int IdCount; 412 | 413 | struct Label Labels[ID_MAX-(TOK_USER_ID-TOK_ID)]; 414 | 415 | int TokenType; 416 | 417 | char OutputBuf[OUTPUT_MAX]; 418 | int OutputBufPos; 419 | int VirtAddress; 420 | int InBSS; 421 | 422 | char StringBuf[128]; // FIXME: remove this (use end of OutputBuf?) 423 | 424 | union { 425 | int Num; 426 | struct { 427 | int Len; 428 | const char* Data; 429 | } Str; 430 | } TokenVal; 431 | 432 | enum { 433 | OPTYPE_LIT, 434 | OPTYPE_REG, 435 | OPTYPE_MEM, 436 | }; 437 | 438 | struct Operand { 439 | char Type; 440 | signed char ModRM; 441 | int Disp; 442 | struct Label* Fixup; 443 | }; 444 | 445 | int SizeOverride; 446 | 447 | void Fatal(const char* format, ...) 448 | { 449 | printf("\nLine %d: Fatal error: ", Line); 450 | char msg[80]; 451 | va_list args; 452 | va_start(args, format); 453 | vsprintf(msg, format, args); 454 | va_end(args); 455 | printf("%s\n", msg); 456 | exit(1); 457 | } 458 | 459 | #ifdef __SCC__ 460 | void assert(int res) 461 | { 462 | if (!res) { 463 | Fatal("Assertion failed"); 464 | } 465 | } 466 | #else 467 | #define Fatal(...) do { printf("%s:%d: ", __FILE__, __LINE__); Fatal(__VA_ARGS__); } while (0) 468 | #endif 469 | 470 | void OutputByte(int b) 471 | { 472 | assert(!InBSS); 473 | assert(OutputBufPos < (int)sizeof(OutputBuf)); 474 | OutputBuf[OutputBufPos++] = b; 475 | ++VirtAddress; 476 | } 477 | 478 | void OutputWord(int w) 479 | { 480 | OutputByte(w); 481 | OutputByte(w>>8); 482 | } 483 | 484 | int HashStr(const char* Str) 485 | { 486 | unsigned Hash = 17; 487 | while (*Str) 488 | Hash = Hash*89 + (*Str++ & 0xDF); 489 | return Hash; 490 | } 491 | 492 | int AddId(const char* Str) 493 | { 494 | int l = (int)strlen(Str) + 1; 495 | assert(IdBufferIndex + l <= IDBUFFER_MAX); 496 | memcpy(IdBuffer + IdBufferIndex, Str, l); 497 | IdOffset[IdCount] = IdBufferIndex; 498 | IdBufferIndex += l; 499 | int i = 0; 500 | int Hash = HashStr(Str); 501 | for (;;) { 502 | Hash &= ID_HASH_MAX-1; 503 | if (IdHash[Hash] == -1) { 504 | IdHash[Hash] = IdCount; 505 | break; 506 | } 507 | Hash += 1 + i++; 508 | } 509 | return IdCount++; 510 | } 511 | 512 | const char* IdText(int T) 513 | { 514 | T -= TOK_ID; 515 | assert(T >=0 && T < IdCount); 516 | return IdBuffer + IdOffset[T]; 517 | } 518 | 519 | int LookupId(const char* Str) 520 | { 521 | int Hash = HashStr(Str); 522 | int i, j = 0; 523 | for (;;) { 524 | Hash &= ID_HASH_MAX-1; 525 | const int id = IdHash[Hash]; 526 | if (id == -1) { 527 | break; 528 | } 529 | const char* ThisId = IdText(id+TOK_ID); 530 | for (i = 0;; ++i) { 531 | char c = Str[i]; 532 | if (id < TOK_USER_ID-TOK_ID && (c >= 'a' && c <= 'z')) 533 | c &= 0xDF; // HACK: covert to upper case for non-user defined IDs 534 | if (c != ThisId[i]) 535 | break; 536 | if (!c) { 537 | return id; 538 | } 539 | } 540 | Hash += 1 + j++; 541 | } 542 | return -1; 543 | } 544 | 545 | void ReadChar(void) 546 | { 547 | if (InBufCnt == InBufSize) { 548 | InBufCnt = 0; 549 | InBufSize = read(InFile, InBuf, INBUF_MAX); 550 | if (!InBufSize) { 551 | CurrentChar = 0; 552 | return; 553 | } 554 | } 555 | CurrentChar = InBuf[InBufCnt++]; 556 | if (CurrentChar == '\n') ++Line; 557 | } 558 | 559 | int IsDigit(void) 560 | { 561 | return CurrentChar >= '0' && CurrentChar <= '9'; 562 | } 563 | 564 | int IsAlpha(void) 565 | { 566 | return (CurrentChar >= 'A' && CurrentChar <= 'Z') || (CurrentChar >= 'a' && CurrentChar <= 'z'); 567 | } 568 | 569 | int IsIdChar(void) 570 | { 571 | return IsAlpha() || CurrentChar == '_' || CurrentChar == '.' || CurrentChar == '$'; 572 | } 573 | 574 | void TranslateIdToken(void) 575 | { 576 | if (TokenType >= TOK_USER_ID) { 577 | const struct Label* L = &Labels[TokenType-TOK_USER_ID]; 578 | if (L->Type == LABEL_EQU) { 579 | TokenType = TOK_NUMBER; 580 | TokenVal.Num = L->Val; 581 | } 582 | return; 583 | } 584 | switch (TokenType) { 585 | case TOK_JB: 586 | case TOK_JNAE: 587 | TokenType = TOK_JC; 588 | break; 589 | case TOK_JNB: 590 | case TOK_JAE: 591 | TokenType = TOK_JNC; 592 | break; 593 | case TOK_JE: 594 | TokenType = TOK_JZ; 595 | break; 596 | case TOK_JNE: 597 | TokenType = TOK_JNZ; 598 | break; 599 | case TOK_JBE: 600 | TokenType = TOK_JNA; 601 | break; 602 | case TOK_JNBE: 603 | TokenType = TOK_JA; 604 | break; 605 | case TOK_JNGE: 606 | TokenType = TOK_JL; 607 | break; 608 | case TOK_JGE: 609 | TokenType = TOK_JNL; 610 | break; 611 | case TOK_JLE: 612 | TokenType = TOK_JNG; 613 | break; 614 | case TOK_JNLE: 615 | TokenType = TOK_JG; 616 | break; 617 | } 618 | } 619 | 620 | void GetToken(void) 621 | { 622 | SkipSpaces: 623 | while (CurrentChar && CurrentChar <= ' ') 624 | ReadChar(); 625 | if (CurrentChar == ';') { 626 | while (CurrentChar && CurrentChar != '\n') 627 | ReadChar(); 628 | goto SkipSpaces; 629 | } 630 | 631 | TokenType = CurrentChar; 632 | 633 | if (!CurrentChar) { 634 | // EOF 635 | return; 636 | } else if (IsDigit()) { 637 | int n = CurrentChar-'0'; 638 | ReadChar(); 639 | if (!n) { 640 | if (CurrentChar == 'x' || CurrentChar == 'X') { 641 | ReadChar(); 642 | for (;;) { 643 | if (IsDigit()) 644 | n = n*16+CurrentChar-'0'; 645 | else if (CurrentChar >= 'A' && CurrentChar <= 'F') 646 | n = n*16+CurrentChar-'A'+10; 647 | else if (CurrentChar >= 'a' && CurrentChar <= 'f') 648 | n = n*16+CurrentChar-'a'+10; 649 | else 650 | break; 651 | ReadChar(); 652 | } 653 | } 654 | } 655 | while (IsDigit()) { 656 | n = n*10 + CurrentChar-'0'; 657 | ReadChar(); 658 | } 659 | TokenType = TOK_NUMBER; 660 | TokenVal.Num = n; 661 | return; 662 | } else if (IsIdChar()) { 663 | char Token[17]; 664 | char* Tp = Token; 665 | do { 666 | if (Tp < &Token[sizeof(Token)-1]) 667 | *Tp++ = CurrentChar; 668 | ReadChar(); 669 | } while (IsDigit() || IsIdChar()); 670 | *Tp++ = 0; 671 | TokenType = LookupId(Token); 672 | if (TokenType < 0) 673 | TokenType = AddId(Token); 674 | TokenType += TOK_ID; 675 | TranslateIdToken(); 676 | return; 677 | } else if (CurrentChar == '\'') { 678 | int Len = 0; 679 | ReadChar(); 680 | while (CurrentChar != '\'') { 681 | assert(Len < (int)sizeof(StringBuf)); 682 | if (!CurrentChar) Fatal("Unexpected EOF in string literal"); 683 | StringBuf[Len++] = CurrentChar; 684 | ReadChar(); 685 | } 686 | StringBuf[Len] = 0; 687 | ReadChar(); 688 | TokenType = TOK_STRING; 689 | TokenVal.Str.Len = Len; 690 | TokenVal.Str.Data = StringBuf; 691 | return; 692 | } 693 | 694 | ReadChar(); 695 | 696 | switch (TokenType) { 697 | case TOK_PLUS: 698 | case TOK_MINUS: 699 | case TOK_COLON: 700 | case TOK_COMMA: 701 | case TOK_LBRACKET: 702 | case TOK_RBRACKET: 703 | case TOK_PIPE: 704 | case TOK_TILDE: 705 | return; 706 | case '<': 707 | if (CurrentChar == '<') { 708 | ReadChar(); 709 | TokenType = TOK_LSH; 710 | return; 711 | } 712 | break; 713 | } 714 | Fatal("Unhandled char %c (%d)", TokenType, TokenType); 715 | } 716 | 717 | char TokenTextBuf[32]; 718 | const char* TokenText(void) 719 | { 720 | switch (TokenType) { 721 | case TOK_NUMBER: sprintf(TokenTextBuf, "%d", TokenVal.Num); break; 722 | case TOK_STRING: return TokenVal.Str.Data; 723 | case TOK_LSH: return "<<"; 724 | default: 725 | if (TokenType >= ' ' && TokenType < 127) { 726 | sprintf(TokenTextBuf, "%c", TokenType); 727 | } else { 728 | return IdText(TokenType); 729 | } 730 | } 731 | return TokenTextBuf; 732 | } 733 | 734 | int NumFromString(void) 735 | { 736 | assert(TokenType == TOK_STRING); 737 | if (TokenVal.Str.Len == 0 || TokenVal.Str.Len > 2) 738 | Fatal("Expected number got %s", TokenText()); 739 | const int n = (TokenVal.Str.Data[0] & 0xff) | ((TokenVal.Str.Data[1]&0xff)<<8); 740 | GetToken(); 741 | return n; 742 | } 743 | 744 | int ParsePrimary(void) 745 | { 746 | if (TokenType == TOK_NUMBER) { 747 | const int n = TokenVal.Num; 748 | GetToken(); 749 | return n; 750 | } else if (TokenType == TOK_STRING) { 751 | return NumFromString(); 752 | } else { 753 | Fatal("Expected number got %s", TokenText()); 754 | return 0; 755 | } 756 | } 757 | 758 | int IsUnaryOp(void) 759 | { 760 | return TokenType == TOK_PLUS || TokenType == TOK_MINUS || TokenType == TOK_TILDE; 761 | } 762 | 763 | 764 | int ParseUnary(void) 765 | { 766 | if (IsUnaryOp()) { 767 | int Op = TokenType; 768 | GetToken(); 769 | int n = ParseUnary(); 770 | if (Op == TOK_MINUS) n = -n; 771 | else if (Op == TOK_TILDE) n = ~n; 772 | return n; 773 | } 774 | return ParsePrimary(); 775 | } 776 | 777 | int OperatorPrecedence(void) 778 | { 779 | if (TokenType == TOK_PLUS || TokenType == TOK_MINUS) return 4; 780 | if (TokenType == TOK_LSH) return 5; 781 | if (TokenType == TOK_PIPE) return 10; 782 | return 100; 783 | } 784 | 785 | int ParseExpr(int LHS, int OuterPrecedence) 786 | { 787 | for (;;) { 788 | const int Prec = OperatorPrecedence(); 789 | if (Prec > OuterPrecedence) { 790 | break; 791 | } 792 | const int Op = TokenType; 793 | GetToken(); 794 | 795 | int RHS = ParseUnary(); 796 | for (;;) { 797 | const int LookAheadPrecedence = OperatorPrecedence(); 798 | if (LookAheadPrecedence >= Prec) 799 | break; 800 | RHS = ParseExpr(RHS, LookAheadPrecedence); 801 | } 802 | 803 | switch (Op) { 804 | case TOK_PLUS: LHS += RHS; break; 805 | case TOK_MINUS: LHS -= RHS; break; 806 | case TOK_PIPE: LHS |= RHS; break; 807 | case TOK_LSH: LHS <<= RHS; break; 808 | default: 809 | Fatal("TODO operator %c\n", Op); 810 | } 811 | } 812 | return LHS; 813 | } 814 | 815 | int ParseNumber(void) 816 | { 817 | return ParseExpr(ParseUnary(), 15); 818 | } 819 | 820 | void Expect(int Tok) 821 | { 822 | if (TokenType != Tok) { 823 | assert(Tok > ' ' && Tok < 128); 824 | Fatal("Expected %c got %s\n", Tok, TokenText()); 825 | } 826 | GetToken(); 827 | } 828 | 829 | int Accept(int Tok) 830 | { 831 | if (TokenType == Tok) { 832 | GetToken(); 833 | return 1; 834 | } 835 | return 0; 836 | } 837 | 838 | struct Label* ReferenceLabel(void) 839 | { 840 | assert(TokenType >= TOK_USER_ID); 841 | struct Label* L = &Labels[TokenType - TOK_USER_ID]; 842 | if (L->Type == LABEL_NONE) { 843 | L->Type = LABEL_NORMAL; 844 | L->Val = -1; 845 | L->Fixups = 0; 846 | } else { 847 | assert(L->Type == LABEL_NORMAL); 848 | } 849 | GetToken(); 850 | return L; 851 | } 852 | 853 | int IsShort(int V) 854 | { 855 | return (V&0xffff) == ((signed char)V & 0xffff); 856 | } 857 | 858 | int TranslateMemMod(int Start) 859 | { 860 | switch (TokenType) { 861 | case TOK_BX: 862 | if (Start == -1) 863 | return 7; 864 | else if (Start == 4) 865 | return 0; 866 | break; 867 | case TOK_BP: 868 | if (Start == -1) 869 | return 6; 870 | break; 871 | case TOK_SI: 872 | if (Start == -1) 873 | return 4; 874 | break; 875 | case TOK_DI: 876 | if (Start == -1) 877 | return 5; 878 | break; 879 | } 880 | Fatal("TODO %s in mem parsing orig=%02X\n", TokenText(), Start); 881 | return -1; 882 | } 883 | 884 | void ParseOperand(struct Operand* Oper) 885 | { 886 | int neg = 0; 887 | Oper->Disp = 0; 888 | Oper->Fixup = 0; 889 | if (TokenType == TOK_NUMBER || TokenType == TOK_STRING || IsUnaryOp()) { 890 | Oper->Type = OPTYPE_LIT; 891 | Oper->Disp = ParseNumber(); 892 | } else if (TokenType == TOK_LBRACKET) { 893 | GetToken(); 894 | DoMem: 895 | Oper->Type = OPTYPE_MEM; 896 | Oper->ModRM = -1; 897 | for (;;) { 898 | if (TokenType >= TOK_ES && TokenType <= TOK_DS) { 899 | // Segment override 900 | OutputByte(0x26 | (TokenType-TOK_ES)); 901 | GetToken(); 902 | Expect(TOK_COLON); 903 | continue; 904 | } else if (TokenType == TOK_BX || TokenType == TOK_BP || TokenType == TOK_SI || TokenType == TOK_DI) { 905 | Oper->ModRM = TranslateMemMod(Oper->ModRM); 906 | GetToken(); 907 | } else if (TokenType == TOK_NUMBER) { 908 | if (neg) 909 | Oper->Disp -= TokenVal.Num; 910 | else 911 | Oper->Disp += TokenVal.Num; 912 | GetToken(); 913 | } else if (TokenType >= TOK_USER_ID) { 914 | assert(!Oper->Fixup); 915 | struct Label* L = ReferenceLabel(); 916 | assert(L->Type == LABEL_NORMAL); 917 | if (L->Val == -1) 918 | Oper->Fixup = L; 919 | else 920 | Oper->Disp += L->Val; 921 | } else { 922 | Fatal("Invalid memory operand. %s", TokenText()); 923 | } 924 | if (Accept(TOK_MINUS)) { 925 | neg = 1; 926 | continue; 927 | } 928 | neg = 0; 929 | if (!Accept(TOK_PLUS)) 930 | break; 931 | } 932 | Expect(TOK_RBRACKET); 933 | 934 | if (Oper->ModRM == 6 && !Oper->Disp) 935 | Oper->ModRM |= 0x40; 936 | 937 | if (Oper->ModRM == -1) 938 | Oper->ModRM = 6; 939 | else if (Oper->Fixup) 940 | Oper->ModRM |= 0x80; // Disp16 941 | else if (Oper->Disp) { 942 | if (IsShort(Oper->Disp)) 943 | Oper->ModRM |= 0x40; // Disp8 944 | else 945 | Oper->ModRM |= 0x80; // Disp16 946 | } 947 | } else if (TokenType == TOK_BYTE || TokenType == TOK_WORD) { 948 | SizeOverride = TokenType == TOK_BYTE? 1 : 2; 949 | GetToken(); 950 | Expect(TOK_LBRACKET); 951 | goto DoMem; 952 | } else if (TokenType >= TOK_AL && TokenType <= TOK_DS) { 953 | Oper->Type = OPTYPE_REG; 954 | Oper->Disp = TokenType; 955 | GetToken(); 956 | } else if (TokenType >= TOK_USER_ID) { 957 | struct Label* L = ReferenceLabel(); 958 | assert(L->Type == LABEL_NORMAL); 959 | Oper->Type = OPTYPE_LIT; 960 | if (L->Val == -1) 961 | Oper->Fixup = L; 962 | else 963 | Oper->Disp = L->Val; 964 | } else { 965 | Fatal("Expected operand got %s", TokenText()); 966 | } 967 | } 968 | 969 | void Parse2Operands(struct Operand* op1, struct Operand* op2) 970 | { 971 | ParseOperand(op1); 972 | Expect(TOK_COMMA); 973 | ParseOperand(op2); 974 | } 975 | 976 | void HandleDx(int size) 977 | { 978 | int n = 0; // Keep count of string output for alignment 979 | do { 980 | if (TokenType == TOK_NUMBER) { 981 | if (size == 1) 982 | OutputByte(TokenVal.Num); 983 | else 984 | OutputWord(TokenVal.Num); 985 | GetToken(); 986 | } else if (TokenType == TOK_STRING) { 987 | n += TokenVal.Str.Len; 988 | int i; 989 | for (i = 0; i < TokenVal.Str.Len; ++i) 990 | OutputByte(TokenVal.Str.Data[i]); 991 | GetToken(); 992 | } else if (TokenType >= TOK_USER_ID) { 993 | assert(size == 2); 994 | struct Label* L = ReferenceLabel(); 995 | assert(L->Type == LABEL_NORMAL); 996 | if (L->Val != -1) { 997 | OutputWord(L->Val); 998 | } else { 999 | OutputWord(0); 1000 | printf("TODO: Need DISP16 fixup for DW\n"); 1001 | } 1002 | } else { 1003 | Fatal("Unexpected token in DB/DW directive: %s", TokenText()); 1004 | } 1005 | } while (Accept(TOK_COMMA)); 1006 | if (size == 2 && (n&1)) 1007 | OutputByte(0); 1008 | } 1009 | 1010 | void HandleResx(int size) 1011 | { 1012 | InBSS = 1; 1013 | VirtAddress += size * ParseNumber(); 1014 | } 1015 | 1016 | void HandleDirective(int Dir) 1017 | { 1018 | switch (Dir) { 1019 | case TOK_CPU: 1020 | { 1021 | int CPU = ParseNumber(); 1022 | if (CPU != 8086) 1023 | Fatal("Unsupported CPU %d", CPU); 1024 | } 1025 | return; 1026 | case TOK_DB: 1027 | HandleDx(1); 1028 | return; 1029 | case TOK_DW: 1030 | HandleDx(2); 1031 | return; 1032 | case TOK_ORG: 1033 | VirtAddress = ParseNumber(); 1034 | return; 1035 | case TOK_RESB: 1036 | HandleResx(1); 1037 | return; 1038 | case TOK_RESW: 1039 | HandleResx(2); 1040 | return; 1041 | } 1042 | Fatal("Directive not implemented: %s\n", IdText(Dir)); 1043 | } 1044 | 1045 | void AddFixupHere(struct Label* L, char Type) 1046 | { 1047 | assert(NextFixup); 1048 | struct Fixup* F = NextFixup; 1049 | NextFixup = NextFixup->Next; 1050 | F->Type = Type; 1051 | F->Offset = OutputBufPos; 1052 | F->Next = L->Fixups; 1053 | L->Fixups = F; 1054 | } 1055 | 1056 | void OutputImm8(struct Operand* Op) 1057 | { 1058 | assert(!Op->Fixup); 1059 | OutputByte(Op->Disp); 1060 | } 1061 | 1062 | void OutputImm16(struct Operand* Op) 1063 | { 1064 | if (Op->Fixup) { 1065 | AddFixupHere(Op->Fixup, FIXUP_DISP16); 1066 | } 1067 | OutputWord(Op->Disp); 1068 | } 1069 | 1070 | void OutputImm(struct Operand* Op, int W) 1071 | { 1072 | if (W) { 1073 | OutputImm16(Op); 1074 | } else { 1075 | OutputImm8(Op); 1076 | } 1077 | } 1078 | 1079 | void OutputModRM(struct Operand* Op, int r) 1080 | { 1081 | assert(Op->Type == OPTYPE_MEM); 1082 | assert(r>=0 && r<8); 1083 | OutputByte(Op->ModRM | r<<3); 1084 | if (Op->ModRM == 6 || (Op->ModRM & 0xc0) == 0x80) { 1085 | if (Op->Fixup) 1086 | AddFixupHere(Op->Fixup, FIXUP_DISP16); 1087 | OutputWord(Op->Disp); 1088 | } else if ((Op->ModRM & 0xc0) == 0x40) { 1089 | OutputByte(Op->Disp); 1090 | } 1091 | } 1092 | 1093 | void OutputMR(int Inst, struct Operand* L, struct Operand* R) 1094 | { 1095 | assert(L->Type == OPTYPE_MEM && R->Type == OPTYPE_REG); 1096 | OutputByte(Inst | (R->Disp >= TOK_AX && R->Disp < TOK_ES)); 1097 | OutputModRM(L, (R->Disp&7)); 1098 | } 1099 | 1100 | void OutputMImm(int Inst, int r, struct Operand* M, struct Operand* Imm) 1101 | { 1102 | assert(M->Type == OPTYPE_MEM && Imm->Type == OPTYPE_LIT); 1103 | assert(SizeOverride); 1104 | int W = SizeOverride!=1; 1105 | if (W && Inst == 0x80 && !Imm->Fixup && IsShort(Imm->Disp)) { 1106 | Inst |= 3; 1107 | W = 0; 1108 | } 1109 | OutputByte(Inst|W); 1110 | OutputModRM(M, r); 1111 | OutputImm(Imm, W); 1112 | } 1113 | 1114 | void HandleMOV(void) 1115 | { 1116 | struct Operand op1, op2; 1117 | Parse2Operands(&op1, &op2); 1118 | if (op1.Type == OPTYPE_REG) { 1119 | int W = op1.Disp >= TOK_AX; 1120 | if (op2.Type == OPTYPE_REG) { 1121 | int Inst; 1122 | int ModRM = (op1.Disp&7) | (op2.Disp&7)<<3; 1123 | if (op1.Disp < TOK_ES) { 1124 | Inst = op2.Disp < TOK_ES ? 0x88|W : 0x8c; 1125 | } else { 1126 | Inst = 0x8e; 1127 | ModRM = (op2.Disp&7) | (op1.Disp&7)<<3; 1128 | } 1129 | OutputByte(Inst); 1130 | OutputByte(0xC0 | ModRM); 1131 | } else if (op2.Type == OPTYPE_LIT) { 1132 | OutputByte(0xB0 + op1.Disp - TOK_AL); 1133 | OutputImm(&op2, W); 1134 | } else { 1135 | assert(op2.Type == OPTYPE_MEM); 1136 | if (op2.ModRM == 6 && (op1.Disp == TOK_AL || op1.Disp == TOK_AX)) { 1137 | OutputByte(0xA0 | (op1.Disp == TOK_AX)); 1138 | OutputImm16(&op2); 1139 | } else { 1140 | OutputMR(op1.Disp >= TOK_ES ? 0x8E: 0x8A, &op2, &op1); 1141 | } 1142 | } 1143 | } else if (op1.Type == OPTYPE_MEM) { 1144 | if (op2.Type == OPTYPE_REG) { 1145 | if (op1.ModRM == 6 && (op2.Disp == TOK_AL || op2.Disp == TOK_AX)) { 1146 | OutputByte(0xA2 | (op2.Disp == TOK_AX)); 1147 | OutputImm16(&op1); 1148 | } else { 1149 | OutputMR(op2.Disp >= TOK_ES ? 0x8C: 0x88, &op1, &op2); 1150 | } 1151 | } else { 1152 | assert(op2.Type == OPTYPE_LIT); 1153 | OutputMImm(0xC6, 0, &op1, &op2); 1154 | } 1155 | } else { 1156 | Fatal("TODO: MOV"); 1157 | } 1158 | } 1159 | 1160 | void HandleALU(int Base) 1161 | { 1162 | (void)Base; 1163 | struct Operand op1, op2; 1164 | Parse2Operands(&op1, &op2); 1165 | if (op1.Type == OPTYPE_REG) { 1166 | const int W = op1.Disp >= TOK_AX; 1167 | if (op2.Type == OPTYPE_REG) { 1168 | OutputByte(Base|W); 1169 | OutputByte(0xC0 | (op2.Disp&7)<<3 | (op1.Disp&7)); 1170 | } else if (op2.Type == OPTYPE_LIT) { 1171 | if (op1.Disp == TOK_AL) { 1172 | OutputByte(Base+4); 1173 | OutputImm8(&op2); 1174 | } else if (op1.Disp == TOK_AX) { 1175 | OutputByte(Base+5); 1176 | OutputImm16(&op2); 1177 | } else { 1178 | int inst = 0x80|W; 1179 | if (W && !op2.Fixup && IsShort(op2.Disp)) 1180 | inst |= 2; 1181 | OutputByte(inst); 1182 | OutputByte(0xC0 | (op1.Disp&7) | Base); 1183 | OutputImm(&op2, inst == 0x81); 1184 | } 1185 | } else { 1186 | assert(op2.Type == OPTYPE_MEM); 1187 | OutputMR(Base+2, &op2, &op1); 1188 | } 1189 | } else if (op1.Type == OPTYPE_MEM) { 1190 | if (op2.Type == OPTYPE_REG) { 1191 | OutputMR(Base, &op1, &op2); 1192 | } else { 1193 | assert(op2.Type == OPTYPE_LIT); 1194 | OutputMImm(0x80, Base>>3, &op1, &op2); 1195 | } 1196 | } else { 1197 | Fatal("TODO: ALU"); 1198 | } 1199 | } 1200 | 1201 | void HandleROT(int r) 1202 | { 1203 | struct Operand op1, op2; 1204 | Parse2Operands(&op1, &op2); 1205 | assert(op1.Type == OPTYPE_REG); 1206 | const int W = op1.Disp >= TOK_AX; 1207 | int Inst = 0xD0 | W; 1208 | if (op2.Type == OPTYPE_REG) { 1209 | assert(op2.Disp == TOK_CL); 1210 | Inst |= 2; 1211 | } 1212 | OutputByte(Inst | W); 1213 | OutputByte(0xC0 | r<<3 | (op1.Disp&7)); 1214 | } 1215 | 1216 | void HandleRel8(struct Operand* Op) 1217 | { 1218 | if (Op->Fixup) 1219 | AddFixupHere(Op->Fixup, FIXUP_REL8); 1220 | OutputByte(Op->Disp-(VirtAddress+1)); 1221 | } 1222 | 1223 | void HandleRel16(struct Operand* Op) 1224 | { 1225 | if (Op->Fixup) 1226 | AddFixupHere(Op->Fixup, FIXUP_REL16); 1227 | OutputWord(Op->Disp-(VirtAddress+2)); 1228 | } 1229 | 1230 | void HandleCALL(void) 1231 | { 1232 | struct Operand op; 1233 | ParseOperand(&op); 1234 | if (op.Type == OPTYPE_REG) { 1235 | OutputByte(0xFF); 1236 | OutputByte(0xC0 | 2<<3 | (op.Disp&7)); 1237 | } else { 1238 | assert(op.Type == OPTYPE_LIT); 1239 | OutputByte(0xE8); 1240 | HandleRel16(&op); 1241 | } 1242 | } 1243 | 1244 | void HandleJMP(void) 1245 | { 1246 | if (Accept(TOK_SHORT)) 1247 | SizeOverride = 1; 1248 | struct Operand op; 1249 | ParseOperand(&op); 1250 | assert(op.Type == OPTYPE_LIT); 1251 | if (SizeOverride == 1 || (!op.Fixup && IsShort(op.Disp - (VirtAddress+2)))) { 1252 | OutputByte(0xEb); 1253 | HandleRel8(&op); 1254 | } else { 1255 | OutputByte(0xE9); 1256 | HandleRel16(&op); 1257 | } 1258 | } 1259 | 1260 | void HandleJCC(int CC) 1261 | { 1262 | if (Accept(TOK_SHORT)) 1263 | SizeOverride = 1; 1264 | struct Operand op; 1265 | ParseOperand(&op); 1266 | assert(op.Type == OPTYPE_LIT); 1267 | OutputByte(0x70|CC); 1268 | if (op.Fixup) { 1269 | AddFixupHere(op.Fixup, FIXUP_REL8); 1270 | OutputByte(-(VirtAddress+1)); 1271 | } else { 1272 | OutputByte(op.Disp-(VirtAddress+1)); 1273 | } 1274 | } 1275 | 1276 | void HandleIncDec(int Dec) 1277 | { 1278 | (void)Dec; 1279 | struct Operand op; 1280 | ParseOperand(&op); 1281 | if (op.Type == OPTYPE_REG) { 1282 | if (op.Disp >= TOK_AX) { 1283 | OutputByte(0x40 | Dec<<3 | (op.Disp&7)); 1284 | } else { 1285 | OutputByte(0xFE); 1286 | OutputByte(0xC0 | Dec<<3 | (op.Disp&7)); 1287 | } 1288 | } else { 1289 | assert(op.Type == OPTYPE_MEM && SizeOverride); 1290 | OutputByte(0xFE | (SizeOverride-1)); 1291 | OutputModRM(&op, Dec); 1292 | } 1293 | } 1294 | 1295 | void HandlePUSH(void) 1296 | { 1297 | struct Operand op; 1298 | ParseOperand(&op); 1299 | if (op.Type == OPTYPE_REG) { 1300 | if (op.Disp >= TOK_ES) { 1301 | OutputByte(0x06 | (op.Disp-TOK_ES)<<3); 1302 | } else { 1303 | assert(op.Disp >= TOK_AX); 1304 | OutputByte(0x50 | (op.Disp-TOK_AX)); 1305 | } 1306 | } else { 1307 | assert(op.Type == OPTYPE_MEM && SizeOverride == 2); 1308 | OutputByte(0xFF); 1309 | OutputModRM(&op, 6); 1310 | } 1311 | } 1312 | 1313 | void HandlePOP(void) 1314 | { 1315 | struct Operand op; 1316 | ParseOperand(&op); 1317 | assert(op.Type == OPTYPE_REG); 1318 | if (op.Disp >= TOK_ES) { 1319 | OutputByte(0x07 | (op.Disp-TOK_ES)<<3); 1320 | } else { 1321 | assert(op.Disp >= TOK_AX); 1322 | OutputByte(0x58 | (op.Disp-TOK_AX)); 1323 | } 1324 | } 1325 | 1326 | void HandleINT(void) 1327 | { 1328 | int n = ParseNumber(); 1329 | assert(n>=0 && n<256); 1330 | OutputByte(0xCD); 1331 | OutputByte(n); 1332 | } 1333 | 1334 | void HandleTEST(void) 1335 | { 1336 | struct Operand op1, op2; 1337 | Parse2Operands(&op1, &op2); 1338 | if (op1.Type == OPTYPE_REG) { 1339 | assert(op2.Type == OPTYPE_LIT); 1340 | const int W = op1.Disp >= TOK_AX; 1341 | if (op1.Disp == TOK_AL || op1.Disp == TOK_AX) { 1342 | OutputByte(0xA8 | W); 1343 | } else { 1344 | OutputByte(0xF6 | W); 1345 | OutputByte(0xC0 | (op1.Disp&7)); 1346 | } 1347 | OutputImm(&op2, W); 1348 | } else { 1349 | assert(op1.Type == OPTYPE_MEM); 1350 | assert(op2.Type == OPTYPE_LIT); 1351 | assert(SizeOverride); 1352 | OutputByte(0xF6|(SizeOverride-1)); 1353 | OutputModRM(&op1, 0); 1354 | OutputImm(&op2, SizeOverride-1); 1355 | } 1356 | } 1357 | 1358 | void HandleXCHG(void) 1359 | { 1360 | struct Operand op1, op2; 1361 | Parse2Operands(&op1, &op2); 1362 | if (op1.Type == OPTYPE_REG) { 1363 | int W = op1.Disp >= TOK_AX; 1364 | if (op2.Type == OPTYPE_REG) { 1365 | assert(op1.Disp == TOK_AX); 1366 | OutputByte(0x90 | (op2.Disp&7)); 1367 | } else { 1368 | assert(op2.Type == OPTYPE_MEM); 1369 | OutputByte(0x86|W); 1370 | OutputModRM(&op2, op1.Disp&7); 1371 | } 1372 | } else { 1373 | Fatal("TODO: XCHG"); 1374 | } 1375 | } 1376 | 1377 | void HandleMulDiv(int r) 1378 | { 1379 | (void)r; 1380 | struct Operand op; 1381 | ParseOperand(&op); 1382 | assert(op.Type == OPTYPE_REG); 1383 | OutputByte(0xF6 | (op.Disp >= TOK_AX)); 1384 | OutputByte(0xC0 | r<<3 | (op.Disp&7)); 1385 | } 1386 | 1387 | void HandleNotNeg(int r) 1388 | { 1389 | (void)r; 1390 | struct Operand op; 1391 | ParseOperand(&op); 1392 | assert(op.Type == OPTYPE_REG); 1393 | OutputByte(0xF6 | (op.Disp >= TOK_AX)); 1394 | OutputByte(0xC0 | r<<3 | (op.Disp&7)); 1395 | } 1396 | 1397 | void HandleLEA(void) 1398 | { 1399 | struct Operand op1, op2; 1400 | Parse2Operands(&op1, &op2); 1401 | assert(op1.Type == OPTYPE_REG); 1402 | assert(op2.Type == OPTYPE_MEM); 1403 | OutputMR(0x8D, &op2, &op1); 1404 | } 1405 | 1406 | void HandleInstruction(int Inst) 1407 | { 1408 | switch (Inst) { 1409 | case TOK_ADC: 1410 | HandleALU(0x10); 1411 | return; 1412 | case TOK_ADD: 1413 | HandleALU(0x00); 1414 | return; 1415 | case TOK_AND: 1416 | HandleALU(0x20); 1417 | return; 1418 | case TOK_CALL: 1419 | HandleCALL(); 1420 | return; 1421 | case TOK_CBW: 1422 | OutputByte(0x98); 1423 | return; 1424 | case TOK_CLC: 1425 | OutputByte(0xF8); 1426 | return; 1427 | case TOK_CLI: 1428 | OutputByte(0xFA); 1429 | return; 1430 | case TOK_CMP: 1431 | HandleALU(0x38); 1432 | return; 1433 | case TOK_CMPSB: 1434 | OutputByte(0xA6); 1435 | return; 1436 | case TOK_CWD: 1437 | OutputByte(0x99); 1438 | return; 1439 | case TOK_DEC: 1440 | HandleIncDec(1); 1441 | return; 1442 | case TOK_DIV: 1443 | HandleMulDiv(6); 1444 | return; 1445 | case TOK_IDIV: 1446 | HandleMulDiv(7); 1447 | return; 1448 | case TOK_IMUL: 1449 | HandleMulDiv(5); 1450 | return; 1451 | case TOK_INC: 1452 | HandleIncDec(0); 1453 | return; 1454 | case TOK_INT: 1455 | HandleINT(); 1456 | return; 1457 | case TOK_JMP: 1458 | HandleJMP(); 1459 | return; 1460 | case TOK_LEA: 1461 | HandleLEA(); 1462 | return; 1463 | case TOK_LODSB: 1464 | OutputByte(0xAC); 1465 | return; 1466 | case TOK_MOV: 1467 | HandleMOV(); 1468 | return; 1469 | case TOK_MOVSB: 1470 | OutputByte(0xA4); 1471 | return; 1472 | case TOK_MUL: 1473 | HandleMulDiv(4); 1474 | return; 1475 | case TOK_NEG: 1476 | HandleNotNeg(3); 1477 | return; 1478 | case TOK_NOT: 1479 | HandleNotNeg(2); 1480 | return; 1481 | case TOK_OR: 1482 | HandleALU(0x08); 1483 | return; 1484 | case TOK_POP: 1485 | HandlePOP(); 1486 | return; 1487 | case TOK_PUSH: 1488 | HandlePUSH(); 1489 | return; 1490 | case TOK_REP: 1491 | OutputByte(0xF3); 1492 | return; 1493 | case TOK_RET: 1494 | OutputByte(0xC3); 1495 | return; 1496 | case TOK_SAR: 1497 | HandleROT(7); 1498 | return; 1499 | case TOK_SBB: 1500 | HandleALU(0x18); 1501 | return; 1502 | case TOK_SHL: 1503 | HandleROT(4); 1504 | return; 1505 | case TOK_SHR: 1506 | HandleROT(5); 1507 | return; 1508 | case TOK_STC: 1509 | OutputByte(0xF9); 1510 | return; 1511 | case TOK_STI: 1512 | OutputByte(0xFB); 1513 | return; 1514 | case TOK_STOSB: 1515 | OutputByte(0xAA); 1516 | return; 1517 | case TOK_STOSW: 1518 | OutputByte(0xAB); 1519 | return; 1520 | case TOK_SUB: 1521 | HandleALU(0x28); 1522 | return; 1523 | case TOK_TEST: 1524 | HandleTEST(); 1525 | return; 1526 | case TOK_XCHG: 1527 | HandleXCHG(); 1528 | return; 1529 | case TOK_XOR: 1530 | HandleALU(0x30); 1531 | return; 1532 | case TOK_JO: 1533 | case TOK_JNO: 1534 | case TOK_JC: 1535 | case TOK_JNC: 1536 | case TOK_JZ: 1537 | case TOK_JNZ: 1538 | case TOK_JNA: 1539 | case TOK_JA: 1540 | case TOK_JS: 1541 | case TOK_JNS: 1542 | case TOK_JPE: 1543 | case TOK_JPO: 1544 | case TOK_JL: 1545 | case TOK_JNL: 1546 | case TOK_JNG: 1547 | case TOK_JG: 1548 | HandleJCC(Inst - TOK_JO); 1549 | return; 1550 | } 1551 | Fatal("Instruction not implemented: %s\n", IdText(Inst)); 1552 | } 1553 | 1554 | void AddBuiltins(const char* S) 1555 | { 1556 | char buf[16]; 1557 | int i, c; 1558 | do { 1559 | i = 0; 1560 | while ((c = *S++) > ' ') { 1561 | buf[i++] = c; 1562 | } 1563 | buf[i] = 0; 1564 | AddId(buf); 1565 | } while (c); 1566 | } 1567 | 1568 | void MakeOutputFilename(char* dest, const char* n) 1569 | { 1570 | char* LastDot; 1571 | LastDot = 0; 1572 | while (*n) { 1573 | if (*n == '.') 1574 | LastDot = dest; 1575 | *dest++ = *n++; 1576 | } 1577 | strcpy(LastDot ? LastDot : dest, ".com"); 1578 | } 1579 | 1580 | 1581 | int main(int argc, char** argv) 1582 | { 1583 | if (argc < 2) { 1584 | Fatal("Need argument"); 1585 | } 1586 | 1587 | InFile = open(argv[1], 0); // O_RDONLY 1588 | if (InFile < 0) { 1589 | Fatal("Error opening input file"); 1590 | } 1591 | 1592 | memset(IdHash, -1, sizeof(IdHash)); 1593 | 1594 | AddBuiltins( 1595 | "AL CL DL BL AH CH DH BH" 1596 | " AX CX DX BX SP BP SI DI" 1597 | " ES CS SS DS" 1598 | " BYTE SHORT WORD" 1599 | " CPU DB DW EQU ORG RESB RESW" 1600 | " ADC ADD AND CALL CBW CLC CLI CMP CMPSB CWD DEC DIV IDIV IMUL INC INT JMP" 1601 | " LEA LODSB MOV MOVSB MUL NEG NOT OR POP PUSH REP RET SAR SBB SHL SHR STC STI" 1602 | " STOSB STOSW SUB TEST XCHG XOR" 1603 | " JO JNO JC JNC JZ JNZ JNA JA JS JNS JPE JPO JL JNL JNG JG" 1604 | " JB JNAE JNB JAE JE JNE JBE JNBE JNGE JGE JLE JNLE" 1605 | ); 1606 | assert(IdCount == TOK_JNLE+1-TOK_ID); 1607 | 1608 | int i; 1609 | for (i = 0; i < FIXUP_MAX-1; ++i) { 1610 | Fixups[i].Next = &Fixups[i+1]; 1611 | } 1612 | NextFixup = &Fixups[0]; 1613 | 1614 | ReadChar(); 1615 | GetToken(); 1616 | while (TokenType) { 1617 | if (TokenType >= TOK_USER_ID) { 1618 | struct Label* L = &Labels[TokenType - TOK_USER_ID]; 1619 | const int global = IdText(TokenType)[0] != '.'; 1620 | GetToken(); 1621 | if (Accept(TOK_COLON)) { 1622 | if (L->Type == LABEL_NONE) { 1623 | L->Type = LABEL_NORMAL; 1624 | L->Fixups = 0; 1625 | } else { 1626 | assert(L->Val == -1); 1627 | struct Fixup* F = L->Fixups; 1628 | if (F) { 1629 | struct Fixup* Last = F; 1630 | while (F) { 1631 | switch (F->Type) { 1632 | case FIXUP_REL8: 1633 | OutputBuf[F->Offset] += VirtAddress; 1634 | break; 1635 | case FIXUP_REL16: 1636 | case FIXUP_DISP16: 1637 | { 1638 | const int v = VirtAddress + ((OutputBuf[F->Offset]&0xff)|((OutputBuf[F->Offset+1]&0xff)<<8)); 1639 | OutputBuf[F->Offset] = v; 1640 | OutputBuf[F->Offset+1] = v>>8; 1641 | } 1642 | break; 1643 | } 1644 | Last = F; 1645 | F = F->Next; 1646 | } 1647 | Last->Next = NextFixup; 1648 | NextFixup = L->Fixups; 1649 | } 1650 | } 1651 | L->Val = VirtAddress; 1652 | if (global) { 1653 | // Retire local labels 1654 | for (i = 0; i < IdCount - (TOK_USER_ID-TOK_ID); ++i) { 1655 | if (Labels[i].Type && *IdText(i+TOK_USER_ID) == '.') { 1656 | Labels[i].Type = LABEL_NONE; 1657 | } 1658 | } 1659 | } 1660 | continue; 1661 | } else if (Accept(TOK_EQU)) { 1662 | assert(L->Type == LABEL_NONE); 1663 | L->Type = LABEL_EQU; 1664 | L->Val = ParseNumber(); 1665 | continue; 1666 | } 1667 | goto Error; 1668 | } else if (TokenType < TOK_DIRECTIVE) { 1669 | Error: 1670 | Fatal("Unexpected token %s", TokenText()); 1671 | } 1672 | const int Op = TokenType; 1673 | GetToken(); 1674 | SizeOverride = 0; 1675 | 1676 | if (Op < TOK_INST) { 1677 | HandleDirective(Op); 1678 | } else { 1679 | HandleInstruction(Op); 1680 | } 1681 | } 1682 | 1683 | if (argv[2]) 1684 | strcpy(StringBuf, argv[2]); 1685 | else 1686 | MakeOutputFilename(StringBuf, argv[1]); 1687 | const int OutFile = open(StringBuf, CREATE_FLAGS, 0600); 1688 | if (OutFile < 0) { 1689 | Fatal("Error creating output file"); 1690 | } 1691 | write(OutFile, OutputBuf, OutputBufPos); 1692 | close(OutFile); 1693 | } 1694 | -------------------------------------------------------------------------------- /sim86.c: -------------------------------------------------------------------------------- 1 | // SIM86 - Basic real-mode x86 simulator (part of SCC) 2 | // Copyright 2020 Michael Rasmussen. See LICENSE.md for details. 3 | 4 | #include "lib.h" 5 | 6 | enum { 7 | R_AX, 8 | R_CX, 9 | R_DX, 10 | R_BX, 11 | R_SP, 12 | R_BP, 13 | R_SI, 14 | R_DI 15 | }; 16 | 17 | enum { 18 | SR_ES, 19 | SR_CS, 20 | SR_SS, 21 | SR_DS, 22 | }; 23 | 24 | enum { 25 | OP_ADD, 26 | OP_OR, 27 | OP_ADC, 28 | OP_SBB, 29 | OP_AND, 30 | OP_SUB, 31 | OP_XOR, 32 | OP_CMP, 33 | }; 34 | 35 | enum { 36 | FC = 1<<0, 37 | FZ = 1<<6, 38 | FS = 1<<7, 39 | FO = 1<<11, 40 | }; 41 | 42 | enum { 43 | LOAD_OFFSET = 0x100, 44 | }; 45 | 46 | int reg[8]; 47 | int sreg[4]; 48 | int ip; 49 | int flags; 50 | #ifndef __SCC__ 51 | char mem[1024*1024]; 52 | enum { DEFAULT_SEGMENT = 0x2000 }; 53 | #else 54 | int memseg; 55 | #endif 56 | 57 | int modrm; 58 | int addr; 59 | char dsize; 60 | signed char seg; 61 | 62 | int verbose; 63 | char rmtext[16]; 64 | char insttext[64]; 65 | const char* filename; 66 | 67 | #ifndef __SCC__ 68 | #define PROFILING 69 | #endif 70 | 71 | #ifdef PROFILING 72 | unsigned long long counts[65536]; 73 | unsigned long long total_cycles; 74 | unsigned long long cycles; 75 | char* addrtext[65536]; 76 | int stack_low = 0xffff; 77 | int file_size; 78 | int do_profile; 79 | 80 | struct StackFrame { 81 | int bp; 82 | int ip; 83 | }; 84 | 85 | struct Stack { 86 | struct StackFrame frames[50]; 87 | int depth; 88 | }; 89 | 90 | struct Stack max_stack; 91 | 92 | #endif 93 | 94 | #ifdef __SCC__ 95 | #define TRIM(x) x 96 | #else 97 | #define TRIM(x) ((x)&0xffff) 98 | #define MAKEADDR(sr, off) ((TRIM(sreg[sr])*16+TRIM(off))&0xfffff) 99 | #endif 100 | 101 | void PrintState(void); 102 | 103 | NORETURN void Fatal(const char* fmt, ...) 104 | { 105 | PrintState(); 106 | printf("%s: ", filename); 107 | char buf[80]; 108 | va_list vl; 109 | va_start(vl, fmt); 110 | vsprintf(buf, fmt, vl); 111 | va_end(vl); 112 | puts(buf); 113 | exit(1); 114 | } 115 | 116 | const char Reg8Names[] = "AL\0CL\0DL\0BL\0AH\0CH\0DH\0BH"; 117 | const char Reg16Names[] = "AX\0CX\0DX\0BX\0SP\0BP\0SI\0DI"; 118 | const char SRegNames[] = "ES\0CS\0SS\0DS"; 119 | 120 | const char* RegName(int r) 121 | { 122 | r *= 3; 123 | return dsize ? &Reg16Names[r] : &Reg8Names[r]; 124 | } 125 | 126 | const char* SRegName(int sr) 127 | { 128 | return &SRegNames[sr*3]; 129 | } 130 | 131 | const char* OpName(int op) 132 | { 133 | switch (op) { 134 | default: 135 | assert(0); 136 | case OP_ADD: return "ADD"; 137 | case OP_OR: return "OR"; 138 | case OP_ADC: return "ADC"; 139 | case OP_SBB: return "SBB"; 140 | case OP_AND: return "AND"; 141 | case OP_SUB: return "SUB"; 142 | case OP_XOR: return "XOR"; 143 | case OP_CMP: return "CMP"; 144 | } 145 | } 146 | 147 | void PrintReg(int r) 148 | { 149 | printf("%s=%04X ", RegName(r), TRIM(reg[r])); 150 | } 151 | 152 | void PrintState(void) 153 | { 154 | const int old = dsize; 155 | dsize = 1; 156 | puts(""); 157 | PrintReg(R_AX); 158 | PrintReg(R_BX); 159 | PrintReg(R_CX); 160 | PrintReg(R_DX); 161 | PrintReg(R_SP); 162 | PrintReg(R_BP); 163 | PrintReg(R_SI); 164 | PrintReg(R_DI); 165 | printf("\nDS=%04X ES=%04X SS=%04X CS=%04X IP=%04X ", TRIM(sreg[SR_DS]), TRIM(sreg[SR_ES]), TRIM(sreg[SR_SS]), TRIM(sreg[SR_CS]), TRIM(ip)); 166 | printf("%s %s %s %s\n", flags&FO?"OV":"NV", flags&FS?"NG":"PL", flags&FZ?"ZR":"NZ", flags&FC?"CY":"NC"); 167 | dsize = old; 168 | } 169 | 170 | int Read8(int sr, int off) 171 | { 172 | #ifdef PROFILING 173 | ++cycles; 174 | #endif 175 | 176 | #ifdef __SCC__ 177 | return FarPeek(sreg[sr], off); 178 | #else 179 | return mem[MAKEADDR(sr, off)] & 0xff; 180 | #endif 181 | } 182 | 183 | int Read16(int sr, int off) 184 | { 185 | return Read8(sr, off) | Read8(sr, off+1)<<8; 186 | } 187 | 188 | void Write8(int sr, int off, int val) 189 | { 190 | #ifdef PROFILING 191 | ++cycles; 192 | #endif 193 | 194 | #ifdef __SCC__ 195 | FarPoke(sreg[sr], off, val); 196 | #else 197 | mem[MAKEADDR(sr, off)] = (char)val; 198 | #endif 199 | } 200 | 201 | void Write16(int sr, int off, int val) 202 | { 203 | Write8(sr, off, val); 204 | Write8(sr, off+1, val>>8); 205 | } 206 | 207 | int ReadIByte(void) 208 | { 209 | return Read8(SR_CS, ip++); 210 | } 211 | 212 | int Imm16(void) 213 | { 214 | const int l = ReadIByte(); 215 | return l | ReadIByte() << 8; 216 | } 217 | 218 | void ModRM(void) 219 | { 220 | assert(modrm == -1); 221 | modrm = ReadIByte(); 222 | switch (modrm>>6) { 223 | case 0: 224 | if ((modrm & 7) == 6) { // Pure disp16 225 | addr += Imm16(); 226 | if (verbose) sprintf(rmtext, "[0x%04X]", addr); 227 | if (seg == -1) 228 | seg = SR_DS; 229 | return; 230 | } 231 | break; 232 | case 1: 233 | addr += (signed char)ReadIByte(); 234 | break; 235 | case 2: 236 | addr += Imm16(); 237 | break; 238 | case 3: 239 | if (verbose) strcpy(rmtext, RegName(modrm&7)); 240 | return; 241 | } 242 | if (verbose) { 243 | char sr[4]; 244 | sr[0] = 0; 245 | if (seg != -1) { 246 | sr[0] = "ECSD"[(unsigned char)seg]; 247 | sr[1] = 'S'; 248 | sr[2] = ':'; 249 | sr[3] = 0; 250 | } 251 | switch (modrm&7) { 252 | case 0: sprintf(rmtext, "[%sBX+SI%+d]", sr, addr); break; 253 | case 1: sprintf(rmtext, "[%sBX+DI%+d]", sr, addr); break; 254 | case 2: sprintf(rmtext, "[%sBP+SI%+d]", sr, addr); break; 255 | case 3: sprintf(rmtext, "[%sBP+DI%+d]", sr, addr); break; 256 | case 4: sprintf(rmtext, "[%sSI%+d]", sr, addr); break; 257 | case 5: sprintf(rmtext, "[%sDI%+d]", sr, addr); break; 258 | case 6: sprintf(rmtext, "[%sBP%+d]", sr, addr); break; 259 | case 7: sprintf(rmtext, "[%sBX%+d]", sr, addr); break; 260 | } 261 | } 262 | int sr = SR_DS; 263 | switch (modrm&7) { 264 | case 0: addr += reg[R_BX] + reg[R_SI]; break; 265 | case 1: addr += reg[R_BX] + reg[R_DI]; break; 266 | case 2: addr += reg[R_BP] + reg[R_SI]; sr = SR_SS; break; 267 | case 3: addr += reg[R_BP] + reg[R_DI]; sr = SR_SS; break; 268 | case 4: addr += reg[R_SI]; break; 269 | case 5: addr += reg[R_DI]; break; 270 | case 6: addr += reg[R_BP]; sr = SR_SS; break; 271 | case 7: addr += reg[R_BX]; break; 272 | } 273 | if (seg == -1) 274 | seg = sr; 275 | } 276 | 277 | int ReadReg(int r) 278 | { 279 | if (dsize) 280 | return reg[r]; 281 | return reg[r&3] >> ((r&4)<<1); 282 | } 283 | 284 | void WriteReg(int r, unsigned val) 285 | { 286 | if (dsize) { 287 | reg[r] = val; 288 | return; 289 | } 290 | const int h = r & 4; 291 | r &= 3; 292 | if (h) { 293 | reg[r] = (reg[r]&0xff)|val<<8; 294 | } else { 295 | reg[r] = (reg[r]&0xff00)|(val&0xff); 296 | } 297 | } 298 | 299 | int ReadRM(void) 300 | { 301 | assert(modrm != -1); 302 | if (modrm>>6==3) { 303 | return ReadReg(modrm&7); 304 | } 305 | assert(seg != -1); 306 | return dsize ? Read16(seg, addr) : Read8(seg, addr); 307 | } 308 | 309 | void WriteRM(int val) 310 | { 311 | if (modrm>>6==3) { 312 | WriteReg(modrm&7, val); 313 | return; 314 | } 315 | assert(seg != -1); 316 | if (dsize) 317 | Write16(seg, addr, val); 318 | else 319 | Write8(seg, addr, val); 320 | } 321 | 322 | void DoPush(int val) 323 | { 324 | reg[R_SP] -= 2; 325 | Write16(SR_SS, reg[R_SP], val); 326 | } 327 | 328 | int DoPop(void) 329 | { 330 | const int v = Read16(SR_SS, reg[R_SP]); 331 | reg[R_SP] += 2; 332 | return v; 333 | } 334 | 335 | void DoOp(int op, unsigned l, unsigned r) 336 | { 337 | if (!dsize) { 338 | l &= 0xff; 339 | r &= 0xff; 340 | } 341 | unsigned res; 342 | unsigned carry = 0; 343 | switch (op) { 344 | case OP_ADD: 345 | res = l + r; 346 | AddDone: 347 | carry = (l & r) | ((l | r) & ~res); 348 | break; 349 | case OP_OR: 350 | res = l | r; 351 | break; 352 | case OP_ADC: 353 | res = l + r + (flags & FC); 354 | goto AddDone; 355 | case OP_SBB: 356 | res = l - r - (flags & FC); 357 | SubDone: 358 | carry = (~l & r) | (~(l ^ r) & res); 359 | break; 360 | case OP_AND: 361 | res = l & r; 362 | break; 363 | case OP_CMP: 364 | case OP_SUB: 365 | res = l - r; 366 | goto SubDone; 367 | case OP_XOR: 368 | res = l ^ r; 369 | break; 370 | default: 371 | Fatal("TODO: %s %d, %d", OpName(op), l, r); 372 | } 373 | const int mask = dsize ? 0x8000 : 0x80; 374 | flags = 0; 375 | if (res & mask) 376 | flags |= FS; 377 | if (!TRIM(res)) 378 | flags |= FZ; 379 | if (carry & mask) 380 | flags |= FC; 381 | if (((carry << 1) ^ carry) & mask) 382 | flags |= FO; 383 | if (op != OP_CMP) 384 | WriteRM(res); 385 | } 386 | 387 | void ALUOp(int op, int swap) 388 | { 389 | ModRM(); 390 | int r = modrm>>3&7; 391 | int rmval = ReadRM(); 392 | int rval = ReadReg(r); 393 | 394 | if (swap) { 395 | modrm = 0xc0|r; // Need to write to register 396 | DoOp(op, rval, rmval); 397 | if (verbose) sprintf(insttext, "%s %s, %s", OpName(op), RegName(r), rmtext); 398 | } else { 399 | DoOp(op, rmval, rval); 400 | if (verbose) sprintf(insttext, "%s %s, %s", OpName(op), rmtext, RegName(r)); 401 | } 402 | } 403 | 404 | void MOV(int swap) 405 | { 406 | ModRM(); 407 | int r = modrm>>3&7; 408 | if (swap) { 409 | if (verbose) sprintf(insttext, "MOV %s, %s", RegName(r), rmtext); 410 | WriteReg(r, ReadRM()); 411 | } else { 412 | if (verbose) sprintf(insttext, "MOV %s, %s", rmtext, RegName(r)); 413 | WriteRM(ReadReg(r)); 414 | } 415 | } 416 | 417 | int ReadToMem(int fd, int off, int size) 418 | { 419 | #ifdef __SCC__ 420 | int s = sreg[SR_DS]; 421 | _emit 0x1F // POP DS 422 | _emit 0x0E // PUSH CS (Match stack layout) 423 | read(fd, off, size); 424 | _emit 0x0E // PUSH CS 425 | _emit 0x1F // POP DS 426 | #else 427 | return read(fd, mem+MAKEADDR(SR_DS, off), size); 428 | #endif 429 | } 430 | 431 | int WriteFromMem(int fd, int off, int size) 432 | { 433 | #ifdef __SCC__ 434 | int s = sreg[SR_DS]; 435 | _emit 0x1F // POP DS 436 | _emit 0x0E // PUSH CS (Match stack layout) 437 | write(fd, off, size); 438 | _emit 0x0E // PUSH CS 439 | _emit 0x1F // POP DS 440 | #else 441 | return write(fd, mem+MAKEADDR(SR_DS, off), size); 442 | #endif 443 | } 444 | 445 | void ReadFile(const char* Filename) 446 | { 447 | const int fd = open(Filename, O_BINARY | O_RDONLY); 448 | if (fd < 0) { 449 | Fatal("Error opening %s", Filename); 450 | } 451 | #ifdef PROFILING 452 | file_size = 453 | #endif 454 | ReadToMem(fd, LOAD_OFFSET, 65536-LOAD_OFFSET); 455 | close(fd); 456 | } 457 | 458 | void JMP(int rel) 459 | { 460 | if (verbose) sprintf(insttext, "JMP 0x%04X", TRIM(ip+rel)); 461 | ip += rel; 462 | } 463 | 464 | void PUSH(int r) 465 | { 466 | if (verbose) sprintf(insttext, "PUSH %s", RegName(r)); 467 | DoPush(reg[r]); 468 | } 469 | 470 | void POP(int r) 471 | { 472 | if (verbose) sprintf(insttext, "POP %s", RegName(r)); 473 | reg[r] = DoPop(); 474 | } 475 | 476 | void JCC(int cc) 477 | { 478 | const int rel = (signed char)ReadIByte(); 479 | const char* n; 480 | const int e = !(cc & 1); // Expect true? 481 | int j; 482 | switch (cc>>1) 483 | { 484 | case 0: 485 | n = e ? "JO" : "JNO"; 486 | j = !!(flags & FO); 487 | break; 488 | case 1: 489 | n = e ? "JC" : "JNC"; 490 | j = !!(flags & FC); 491 | break; 492 | case 2: 493 | n = e ? "JZ" : "JNZ"; 494 | j = !!(flags & FZ); 495 | break; 496 | case 3: 497 | n = e ? "JNA" : "JA"; 498 | j = (flags & FZ) || (flags & FC); 499 | break; 500 | case 4: 501 | n = e ? "JS" : "JNS"; 502 | j = !!(flags & FS); 503 | break; 504 | case 6: 505 | n = e ? "JL" : "JNL"; 506 | j = !(flags & FS) != !(flags & FO); 507 | break; 508 | case 7: 509 | n = e ? "JNG" : "JG"; 510 | j = (flags & FZ) || (!(flags & FS) != !(flags & FO)); 511 | break; 512 | default: 513 | Fatal("TODO: JCC cc=%d rel=%d [%X]", cc, rel, ip+rel); 514 | } 515 | if (verbose) sprintf(insttext, "%s 0x%04X", n, TRIM(ip+rel)); 516 | if (j == e) 517 | ip += rel; 518 | } 519 | 520 | // TODO: INC/DEC shouldn't modify carry(?) 521 | void DoIncDec(int v) 522 | { 523 | const int s = ReadRM(); 524 | WriteRM(s+v); 525 | } 526 | 527 | void INC(void) 528 | { 529 | if (verbose) sprintf(insttext, "INC %s", rmtext); 530 | DoIncDec(1); 531 | } 532 | 533 | void DEC(void) 534 | { 535 | if (verbose) sprintf(insttext, "DEC %s", rmtext); 536 | DoIncDec(-1); 537 | } 538 | 539 | void ReadAsciiz(char* dest, int sr, int off) 540 | { 541 | int ch; 542 | do { 543 | ch = Read8(sr, off++); 544 | *dest++ = (char)ch; 545 | } while (ch); 546 | } 547 | 548 | void HandleOpen(int oflags) 549 | { 550 | char fname[64]; 551 | ReadAsciiz(fname, SR_DS, reg[R_DX]); 552 | int fd = open(fname, O_BINARY | oflags, 0600); 553 | if (verbose) printf("\nopen(%s, %d) -> %d\n", fname, oflags, fd); 554 | if (fd < 0) { 555 | flags |= FC; 556 | fd = 2; // File not found 557 | } else { 558 | flags &= ~FC; 559 | } 560 | reg[R_AX] = fd; 561 | } 562 | 563 | void DoExit(int ec) 564 | { 565 | if (verbose) { 566 | if (ec) 567 | printf("\nExiting with error code %d\n", ec); 568 | else 569 | printf("\nNormal exit\n"); 570 | } 571 | #ifdef PROFILING 572 | if (do_profile) { 573 | struct E { 574 | int Addr; 575 | char Id[64]; 576 | unsigned long long Total; 577 | } entries[4096] = { 578 | { 0, "@@start", 0 }, 579 | }; 580 | int num_entries = 1; 581 | char fname[256]; 582 | strcpy(fname, filename); 583 | char* d = strrchr(fname, '.'); 584 | if (d) { 585 | strcpy(d, ".map"); 586 | FILE* fp = fopen(fname, "rt"); 587 | if (fp) { 588 | while (fscanf(fp, "%4X %63s\n", (unsigned*)&entries[num_entries].Addr, entries[num_entries].Id) == 2) { 589 | ++num_entries; 590 | } 591 | fclose(fp); 592 | } 593 | } 594 | 595 | printf("\nFilesize;Total cylces;Stack low;_EBSS\n"); 596 | printf("%d;%llu;%d;%d\n\n", file_size, total_cycles, stack_low&0xffff, entries[num_entries-1].Addr); 597 | int entry = 0; 598 | printf("Count;Address;Name;Instruction\n"); 599 | for (int i = 0; i < 65536; ++i) { 600 | if (entry+1 < num_entries && i >= entries[entry+1].Addr) { 601 | ++entry; 602 | } 603 | if (counts[i]) 604 | printf("%llu;0x%04X;%s+0x%X;%s\n", counts[i], i, entries[entry].Id, i - entries[entry].Addr, addrtext[i] ? addrtext[i] : ""); 605 | if (entry >= 0) 606 | entries[entry].Total += counts[i]; 607 | } 608 | printf("\nCount;Function\n"); 609 | for (int i = 0; i < num_entries; ++i) { 610 | if (entries[i].Total) 611 | printf("%llu;%s\n", entries[i].Total, entries[i].Id); 612 | } 613 | printf("\nMax stack\n"); 614 | printf("\nBP;Return address;Size;Symbol\n"); 615 | for (int i = 0; i < max_stack.depth; ++i) { 616 | struct StackFrame* f = &max_stack.frames[i]; 617 | for (entry = 0; entry < num_entries; ++entry) { 618 | if (entries[entry].Addr > f->ip) { 619 | --entry; 620 | break; 621 | } 622 | } 623 | const struct E* e = &entries[entry]; 624 | const int LastBP = i + 1 < max_stack.depth ? max_stack.frames[i+1].bp : 65536; 625 | printf("0x%4X;0x%04X;%d;%s+0x%X\n", f->bp, f->ip, LastBP-f->bp, e->Id, f->ip - e->Addr); 626 | } 627 | } 628 | #endif 629 | exit(ec); 630 | } 631 | 632 | void INT(void) 633 | { 634 | const int intr = ReadIByte(); 635 | if (verbose) sprintf(insttext, "INT 0x%02X", intr); 636 | if (intr == 0x20) { 637 | DoExit(0); 638 | } 639 | if (intr != 0x21) { 640 | Fatal("TODO: INT %02X", intr); 641 | } 642 | const int func = reg[R_AX]>>8&0xff; 643 | if (func == 0x02) { 644 | int c = reg[R_DX]&0xff; 645 | if (verbose) 646 | printf("\nOutput: 0x%02X (%c)", c, c >= ' ' ? c : '.'); 647 | else 648 | putchar(c); 649 | } else if (func == 0x3C) { 650 | HandleOpen(O_WRONLY | O_CREAT | O_TRUNC); 651 | } else if (func == 0x3D) { 652 | HandleOpen(O_RDONLY); 653 | } else if (func == 0x3E) { 654 | if (verbose) printf("\nclose(%d)\n", reg[R_BX]); 655 | close(reg[R_BX]); 656 | flags &= ~FC; 657 | } else if (func == 0x3F) { 658 | int r = ReadToMem(reg[R_BX], reg[R_DX], reg[R_CX]); 659 | if (r <= 0) { 660 | flags |= FC; 661 | r = 0; 662 | } else { 663 | flags &= ~FC; 664 | } 665 | reg[R_AX] = r; 666 | } else if (func == 0x40) { 667 | int r = WriteFromMem(reg[R_BX], reg[R_DX], reg[R_CX]); 668 | if (r <= 0) { 669 | flags |= FC; 670 | r = 0; 671 | } else { 672 | flags &= ~FC; 673 | } 674 | reg[R_AX] = r; 675 | } else if (func == 0x4C) { 676 | DoExit(reg[R_AX]&0xff); 677 | } else { 678 | Fatal("TODO: INT 0x21/AH=%02X", func); 679 | } 680 | } 681 | 682 | #ifdef PROFILING 683 | #define READSS(off) (mem[MAKEADDR(SR_SS, (off))]&0xff)|mem[MAKEADDR(SR_SS, (off)+1)]<<8 684 | 685 | struct Stack RecordStack(void) 686 | { 687 | struct Stack s; 688 | s.depth = 0; 689 | int sip = TRIM(ip); 690 | for (int bp = TRIM(reg[R_BP]); bp && s.depth < (int)(sizeof(s.frames)/sizeof(*s.frames));) { 691 | struct StackFrame* f = &s.frames[s.depth++]; 692 | f->bp = bp; 693 | f->ip = sip; 694 | sip = TRIM(READSS(bp+2)); 695 | bp = TRIM(READSS(bp)); 696 | } 697 | return s; 698 | } 699 | #undef READSS 700 | #endif 701 | 702 | int main(int argc, char** argv) 703 | { 704 | if (argc < 2) { 705 | Usage: 706 | printf("Usage: sim86 [-v] " 707 | #ifdef PROFILING 708 | "[-p] " 709 | #endif 710 | "com-file [...]\n"); 711 | return 1; 712 | } 713 | #ifdef __SCC__ 714 | memseg = GetDS() + 0x1000; 715 | #else 716 | const int memseg = DEFAULT_SEGMENT; 717 | #endif 718 | sreg[0] = sreg[1] = sreg[2] = sreg[3] = memseg; 719 | 720 | int argi = 1; 721 | for (; argv[argi]; ++argi) { 722 | if (!strcmp(argv[argi], "-v")) { 723 | verbose = 1; 724 | #ifdef PROFILING 725 | } else if (!strcmp(argv[argi], "-p")) { 726 | do_profile = 1; 727 | #endif 728 | } else { 729 | break; 730 | } 731 | } 732 | filename = argv[argi]; 733 | if (!filename) 734 | goto Usage; 735 | 736 | ReadFile(filename); 737 | 738 | Write16(SR_CS, 0, 0x20CD); 739 | Write16(SR_CS, 2, memseg + 0x1000); 740 | reg[R_SP] = 0; 741 | int p = 0; 742 | int i; 743 | for (i = argi + 1; argv[i]; ++i) { 744 | if (i != 2) Write8(SR_DS, 0x81+(p++), ' '); 745 | const char* s = argv[i]; 746 | while (*s) Write8(SR_DS, 0x81+(p++), *s++); 747 | } 748 | Write8(SR_DS, 0x82+p, '\r'); 749 | Write8(SR_DS, 0x80, p); 750 | 751 | DoPush(0); 752 | ip = LOAD_OFFSET; 753 | 754 | int start_ip = ip; 755 | for (;;) { 756 | #ifdef PROFILING 757 | const int orig_ip = TRIM(ip); 758 | int remove_verbose = 0; 759 | if (do_profile) { 760 | cycles = 0; 761 | reg[R_SP]=TRIM(reg[R_SP]); 762 | if (reg[R_SP] && reg[R_SP] < stack_low) { 763 | stack_low = reg[R_SP]; 764 | max_stack = RecordStack(); 765 | } 766 | 767 | if (!addrtext[orig_ip]) { 768 | remove_verbose = !verbose; 769 | verbose = 1; 770 | } 771 | } 772 | #endif 773 | if (verbose) { 774 | start_ip = TRIM(ip); 775 | *insttext = 0; 776 | } 777 | 778 | modrm = -1; 779 | addr = 0; 780 | dsize = 1; 781 | seg = -1; 782 | 783 | int rep = 0; 784 | 785 | Restart: ; 786 | const int inst = ReadIByte(); 787 | 788 | if (inst < 0x40) { 789 | const int type = inst&6; 790 | const int op = (inst >> 3) & 7; 791 | dsize = inst&1; 792 | if (type < 4) { 793 | ALUOp(op, inst & 2); 794 | } else if (type == 4) { 795 | int Imm; 796 | if (dsize) 797 | Imm = Imm16(); 798 | else 799 | Imm = (signed char)ReadIByte(); 800 | modrm = 0xc0; 801 | DoOp(op, reg[R_AX], Imm); 802 | if (verbose) sprintf(insttext, "%s A%c, 0x%04X", OpName(op), dsize?'X':'L', Imm); 803 | } else if (type == 6) { 804 | if (inst < 0x26) { 805 | const int sr = inst>>3; 806 | if (verbose) sprintf(insttext, "%s %s", inst&1?"POP":"PUSH", SRegName(sr)); 807 | if (inst&1) 808 | sreg[sr] = DoPop(); 809 | else 810 | DoPush(sreg[sr]); 811 | } else { 812 | assert(!(inst&1)); 813 | seg = (inst-0x26)>>3; 814 | goto Restart; 815 | } 816 | } else { 817 | goto Unhandled; 818 | } 819 | } else if (inst >= 0x40 && inst < 0x48) { 820 | int r = inst - 0x40; 821 | modrm = 0xc0 | r << 3 | r; 822 | if (verbose) strcpy(rmtext, RegName(r)); 823 | INC(); 824 | } else if (inst >= 0x48 && inst < 0x50) { 825 | int r = inst - 0x48; 826 | modrm = 0xc0 | r << 3 | r; 827 | if (verbose) strcpy(rmtext, RegName(r)); 828 | DEC(); 829 | } else if (inst >= 0x50 && inst < 0x58) { 830 | PUSH(inst-0x50); 831 | } else if (inst >= 0x58 && inst < 0x60) { 832 | POP(inst-0x58); 833 | } else if (inst >= 0x70 && inst < 0x80) { 834 | JCC(inst-0x70); 835 | } else if (inst >= 0x90 && inst < 0x98) { 836 | const int r = inst - 0x90; 837 | if (verbose) { 838 | if (!r) 839 | strcpy(insttext, "NOP"); 840 | else 841 | sprintf(insttext, "XCHG AX, %s", RegName(r)); 842 | } 843 | int rv = reg[r]; 844 | reg[r] = reg[R_AX]; 845 | reg[R_AX] = rv; 846 | } else if (inst >= 0xb0 && inst < 0xb8) { 847 | dsize = 0; 848 | WriteReg(inst-0xb0, ReadIByte()); 849 | if (verbose) sprintf(insttext, "MOV %s, 0x%02X", RegName(inst-0xb0), ReadReg(inst-0xb0)&0xff); 850 | } else if (inst >= 0xb8 && inst < 0xc0) { 851 | const int r = inst-0xb8; 852 | reg[r] = Imm16(); 853 | if (verbose) sprintf(insttext, "MOV %s, 0x%04X", RegName(inst-0xb8), reg[r]); 854 | } else { 855 | switch (inst) { 856 | case 0x80: 857 | case 0x81: 858 | case 0x82: 859 | case 0x83: 860 | { 861 | dsize = inst&1; 862 | ModRM(); 863 | const int imm = inst == 0x81 ? Imm16() : (signed char)ReadIByte(); 864 | DoOp(modrm>>3&7, ReadRM(), imm); 865 | if (verbose) sprintf(insttext, "%s %s, 0x%04X", OpName(modrm>>3&7), rmtext, imm); 866 | } 867 | break; 868 | case 0x88: 869 | case 0x89: 870 | case 0x8A: 871 | case 0x8B: 872 | dsize = inst&1; 873 | MOV(inst&2); 874 | break; 875 | case 0x8C: 876 | ModRM(); 877 | assert((modrm>>3&7) < 4); 878 | if (verbose) sprintf(insttext, "MOV %s, %s", rmtext, SRegName(modrm>>3&7)); 879 | WriteRM(sreg[modrm>>3&7]); 880 | break; 881 | case 0x8D: 882 | { 883 | ModRM(); 884 | if (verbose) sprintf(insttext, "LEA %s, %s", RegName(modrm>>3&7), rmtext); 885 | reg[modrm>>3&7] = addr; 886 | } 887 | break; 888 | case 0x8E: 889 | ModRM(); 890 | assert((modrm>>3&7) < 4); 891 | if (verbose) sprintf(insttext, "MOV %s, %s", SRegName(modrm>>3&7), rmtext); 892 | sreg[modrm>>3&7] = ReadRM(); 893 | break; 894 | case 0x98: 895 | reg[R_AX] = (signed char)reg[R_AX]; 896 | if (verbose) sprintf(insttext, "CBW"); 897 | break; 898 | case 0x99: 899 | reg[R_DX] = reg[R_AX]&0x8000 ? -1 : 0; 900 | if (verbose) sprintf(insttext, "CWD"); 901 | break; 902 | case 0xA0: 903 | case 0xA1: 904 | case 0xA2: 905 | case 0xA3: 906 | { 907 | int Off = ReadIByte(); 908 | Off |= ReadIByte()<<8; 909 | if (verbose) { 910 | if (inst & 2) sprintf(insttext, "MOV [0x%04X], A%c", Off, inst&1?'X':'L'); 911 | else sprintf(insttext, "MOV A%c, [0x%04X]", inst&1?'X':'L', Off); 912 | } 913 | if (inst & 2) { 914 | // Store 915 | if (inst & 1) { 916 | Write16(SR_DS, Off, reg[R_AX]); 917 | } else { 918 | Write8(SR_DS, Off, reg[R_AX] & 0xff); 919 | } 920 | } else { 921 | if (inst & 1) { 922 | reg[R_AX] = Read16(SR_DS, Off); 923 | } else { 924 | reg[R_AX] = (reg[R_AX]&0xff00) | Read8(SR_DS, Off); 925 | } 926 | } 927 | } 928 | break; 929 | case 0xA4: 930 | { 931 | assert(rep == 0xF3); 932 | if (verbose) sprintf(insttext, "REP MOVSB"); 933 | while (reg[R_CX]) { 934 | Write8(SR_ES, reg[R_DI], Read8(SR_DS, reg[R_SI])); 935 | reg[R_CX] -= 1; 936 | reg[R_DI] += 1; 937 | reg[R_SI] += 1; 938 | } 939 | } 940 | break; 941 | case 0xA5: 942 | { 943 | assert(rep == 0xF3); 944 | if (verbose) sprintf(insttext, "REP MOVSW"); 945 | while (reg[R_CX]) { 946 | Write16(SR_ES, reg[R_DI], Read16(SR_DS, reg[R_SI])); 947 | reg[R_CX] -= 1; 948 | reg[R_DI] += 2; 949 | reg[R_SI] += 2; 950 | } 951 | } 952 | break; 953 | case 0xA6: 954 | { 955 | assert(rep == 0xF3); 956 | if (verbose) sprintf(insttext, "REPE CMPSB"); 957 | flags = FZ; 958 | while ((flags & FZ) && reg[R_CX]) { 959 | DoOp(OP_CMP, Read8(SR_DS, reg[R_SI]), Read8(SR_ES, reg[R_DI])); 960 | reg[R_CX] -= 1; 961 | reg[R_DI] += 1; 962 | reg[R_SI] += 1; 963 | } 964 | } 965 | break; 966 | case 0xAA: 967 | { 968 | int iter = 1; 969 | if (rep == 0xF3) { 970 | if (verbose) sprintf(insttext, "REP STOSB"); 971 | iter = reg[R_CX]; 972 | reg[R_CX] -= iter; 973 | } else { 974 | if (verbose) sprintf(insttext, "STOSB"); 975 | } 976 | const int v = reg[R_AX]; 977 | while (iter--) { 978 | Write8(SR_ES, reg[R_DI]++, v); 979 | } 980 | } 981 | break; 982 | case 0xAB: 983 | { 984 | int iter = 1; 985 | if (rep == 0xF3) { 986 | if (verbose) sprintf(insttext, "REP STOSW"); 987 | iter = reg[R_CX]; 988 | reg[R_CX] -= iter; 989 | } else { 990 | if (verbose) sprintf(insttext, "STOSW"); 991 | } 992 | const int v = reg[R_AX]; 993 | while (iter--) { 994 | Write16(SR_ES, reg[R_DI], v); 995 | reg[R_DI] += 2; 996 | } 997 | } 998 | break; 999 | case 0xAC: 1000 | { 1001 | assert(rep == 0x00); 1002 | if (verbose) sprintf(insttext, "LODSB"); 1003 | dsize = 0; 1004 | WriteReg(R_AX, Read8(SR_DS, reg[R_SI])); 1005 | reg[R_SI] += 1; 1006 | } 1007 | break; 1008 | case 0xAD: 1009 | { 1010 | assert(rep == 0x00); 1011 | if (verbose) sprintf(insttext, "LODSW"); 1012 | WriteReg(R_AX, Read16(SR_DS, reg[R_SI])); 1013 | reg[R_SI] += 2; 1014 | } 1015 | break; 1016 | case 0xAE: 1017 | { 1018 | assert(rep == 0xF2); 1019 | if (verbose) strcpy(insttext, "REPNE SCASB"); 1020 | flags = 0; 1021 | while (!(flags & FZ) && reg[R_CX]) { 1022 | DoOp(OP_CMP, (unsigned char)reg[R_AX], Read8(SR_ES, reg[R_DI])); 1023 | reg[R_CX] -= 1; 1024 | reg[R_DI] += 1; 1025 | } 1026 | } 1027 | break; 1028 | case 0xC3: 1029 | if (verbose) sprintf(insttext, "RET"); 1030 | ip = DoPop(); 1031 | break; 1032 | case 0xC6: 1033 | { 1034 | dsize = 0; 1035 | ModRM(); 1036 | int val = ReadIByte(); 1037 | if (verbose) sprintf(insttext, "MOV %s, 0x%04X", rmtext, val); 1038 | WriteRM(val); 1039 | } 1040 | break; 1041 | case 0xC7: 1042 | { 1043 | ModRM(); 1044 | int val = Imm16(); 1045 | if (verbose) sprintf(insttext, "MOV %s, 0x%04X", rmtext, val); 1046 | WriteRM(val); 1047 | } 1048 | break; 1049 | case 0xCD: 1050 | INT(); 1051 | break; 1052 | case 0xD1: 1053 | { 1054 | ModRM(); 1055 | const unsigned v = ReadRM(); 1056 | unsigned res, c; 1057 | switch (modrm>>3&7) { 1058 | case 4: // SHL R/M16, 1 1059 | if (verbose) sprintf(insttext, "SHL %s, 1", rmtext); 1060 | res = v << 1; 1061 | c = v & 0x8000; 1062 | break; 1063 | case 5: // SHR R/M16, 1 1064 | if (verbose) sprintf(insttext, "SHR %s, 1", rmtext); 1065 | res = v>>1; 1066 | c = v&1; 1067 | break; 1068 | case 7: // SAR R/M16, 1 1069 | if (verbose) sprintf(insttext, "SAR %s, 1", rmtext); 1070 | res = (int)v>>1; 1071 | c = v&1; 1072 | break; 1073 | default: 1074 | Fatal("TODO: D1/%d", modrm>>3&7); 1075 | } 1076 | WriteRM(res); 1077 | flags = 0; 1078 | if (c) flags |= FC; 1079 | if (!res) flags |= FZ; 1080 | } 1081 | break; 1082 | case 0xD3: 1083 | { 1084 | ModRM(); 1085 | const unsigned v = ReadRM(); 1086 | const unsigned a = reg[R_CX]&15; 1087 | unsigned res, c=0; 1088 | switch (modrm>>3&7) { 1089 | case 4: // SHL R/M16, CL 1090 | if (verbose) sprintf(insttext, "SHL %s, CL", rmtext); 1091 | res = v << a; 1092 | if (a) c = (v << (a-1)) & 0x8000; 1093 | break; 1094 | case 5: // SHR R/M16, CL 1095 | if (verbose) sprintf(insttext, "SHR %s, CL", rmtext); 1096 | res = v >> a; 1097 | if (a) c = (v >> (a-1)) & 1; 1098 | break; 1099 | case 7: // SAR R/M16, CL 1100 | if (verbose) sprintf(insttext, "SAR %s, CL", rmtext); 1101 | res = (int)v >> a; 1102 | if (a) c = (v >> (a-1)) & 1; 1103 | break; 1104 | default: 1105 | Fatal("TODO: D3/%d", modrm>>3&7); 1106 | } 1107 | WriteRM(res); 1108 | flags = 0; 1109 | if (c) flags |= FC; 1110 | if (!res) flags |= FZ; 1111 | } 1112 | break; 1113 | case 0xE8: 1114 | { 1115 | const int rel = Imm16(); 1116 | if (verbose) sprintf(insttext, "CALL 0x%04X", TRIM(ip+rel)); 1117 | DoPush(ip); 1118 | ip += rel; 1119 | } 1120 | break; 1121 | case 0xE9: 1122 | JMP(Imm16()); 1123 | break; 1124 | case 0xEB: 1125 | JMP((signed char)ReadIByte()); 1126 | break; 1127 | case 0xF2: 1128 | case 0xF3: 1129 | rep = inst; 1130 | goto Restart; 1131 | case 0xF7: 1132 | { 1133 | ModRM(); 1134 | // TODO: Flags 1135 | switch (modrm>>3&7) { 1136 | case 2: // NOT r/m16 1137 | if (verbose) sprintf(insttext, "NOT %s", rmtext); 1138 | WriteRM(~ReadRM()); 1139 | break; 1140 | case 3: // NEG r/m16 1141 | if (verbose) sprintf(insttext, "NEG %s", rmtext); 1142 | WriteRM(-ReadRM()); 1143 | break; 1144 | case 4: // MUL (TODO: Full 32-bit result) 1145 | case 5: // IMUL (TODO: Full 32-bit result) 1146 | { 1147 | if (verbose) sprintf(insttext, "%sMUL %s", modrm&8?"I":"", rmtext); 1148 | const unsigned int m = ReadRM(); 1149 | reg[R_AX] *= m; 1150 | reg[R_DX] = 0; 1151 | } 1152 | break; 1153 | case 6: // DIV (TODO: Full 32-bit divide) 1154 | { 1155 | if (verbose) sprintf(insttext, "DIV %s", rmtext); 1156 | const unsigned int d = ReadRM(); 1157 | assert(reg[R_DX] == 0); 1158 | reg[R_DX] = reg[R_AX] % d; 1159 | reg[R_AX] /= d; 1160 | } 1161 | break; 1162 | case 7: // IDIV (TODO: Full 32-bit divide) 1163 | { 1164 | if (verbose) sprintf(insttext, "IDIV %s", rmtext); 1165 | const int d = ReadRM(); 1166 | assert(reg[R_DX] == (reg[R_AX] & 0x8000 ? -1 : 0)); 1167 | reg[R_DX] = reg[R_AX] % d; 1168 | reg[R_AX] /= d; 1169 | } 1170 | break; 1171 | default: 1172 | Fatal("TODO: F7/%X", modrm>>3&7); 1173 | } 1174 | } 1175 | break; 1176 | case 0xFE: 1177 | dsize = 0; 1178 | // fallthrough 1179 | case 0xFF: 1180 | { 1181 | ModRM(); 1182 | switch (modrm>>3&7) { 1183 | case 0: 1184 | INC(); 1185 | break; 1186 | case 1: 1187 | DEC(); 1188 | break; 1189 | case 2: 1190 | if (verbose) sprintf(insttext, "CALL %s", rmtext); 1191 | DoPush(ip); 1192 | ip = ReadRM(); 1193 | break; 1194 | default: 1195 | Fatal("TODO: %02X/%X", inst, modrm>>3&7); 1196 | } 1197 | } 1198 | break; 1199 | default: 1200 | Unhandled: 1201 | Fatal("Unimplemented/invalid instruction %02X", inst); 1202 | } 1203 | } 1204 | #ifdef PROFILING 1205 | if (do_profile) { 1206 | if (!addrtext[orig_ip]) { 1207 | assert(*insttext); 1208 | const int l = (int)strlen(insttext)+1; 1209 | if ((addrtext[orig_ip] = malloc(l)) == NULL) 1210 | Fatal("Out of memory"); 1211 | memcpy(addrtext[orig_ip], insttext, l); 1212 | if (remove_verbose) { 1213 | verbose = 0; 1214 | } 1215 | } 1216 | 1217 | total_cycles += cycles; 1218 | counts[orig_ip] += cycles; 1219 | } 1220 | #endif 1221 | if (verbose) { 1222 | assert(*insttext); 1223 | 1224 | printf("%04X %s", TRIM(start_ip), insttext); 1225 | PrintState(); 1226 | } 1227 | } 1228 | } 1229 | -------------------------------------------------------------------------------- /swar.c: -------------------------------------------------------------------------------- 1 | // Just a silly and slow exercise of some the more advanced parts of SCC 2 | #include "lib.h" 3 | 4 | enum { 5 | VIDEO_SEG = 0xb800, 6 | FONT_SEG = 0xffa6, 7 | FONT_OFF = 0x000e, 8 | SCREENW = 320, 9 | SCREENH = 200, 10 | }; 11 | 12 | enum { 13 | INT10H_SETMODE = 0x0000, 14 | INT10H_SETPAL = 0x0B00, 15 | INT10H_GETMODE = 0x0F00, 16 | }; 17 | 18 | #ifdef __SCC__ 19 | int Int10h(int ax, int bx) 20 | { 21 | _emit 0x8B _emit 0x46 _emit 0x04 // MOV AX, [BP+4] 22 | _emit 0x8B _emit 0x5E _emit 0x06 // MOV BX, [BP+6] 23 | _emit 0xCD _emit 0x10 // INT 0x10 24 | } 25 | 26 | int KeyPressed() 27 | { 28 | _emit 0xB4 _emit 0x01 // MOV AH, 1 29 | _emit 0xCD _emit 0x16 // INT 0x16 30 | _emit 0xB8 _emit 0x00 _emit 0x00 // MOV AX, 0 31 | _emit 0x74 _emit 0x01 // JZ $+3 32 | _emit 0x40 // INC AX 33 | } 34 | 35 | unsigned ReadKey() 36 | { 37 | _emit 0xB4 _emit 0x00 // MOV AH, 0 38 | _emit 0xCD _emit 0x16 // INT 0x16 39 | } 40 | 41 | int inb(int port) 42 | { 43 | _emit 0x8B _emit 0x56 _emit 0x04 // MOV DX, [BP+4] 44 | _emit 0xEC // IN AL, DX 45 | _emit 0x98 // CBW 46 | } 47 | 48 | void ScreenUpdate() 49 | { 50 | while (!(inb(0x3DA) & 8)) 51 | ; 52 | } 53 | 54 | #else 55 | 56 | static unsigned char VideoMem[16384]; 57 | static char Initialized; 58 | 59 | static const unsigned char Font[1024] = { 60 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x81, 0xa5, 0x81, 0xbd, 0x99, 0x81, 0x7e, 61 | 0x7e, 0xff, 0xdb, 0xff, 0xc3, 0xe7, 0xff, 0x7e, 0x6c, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00, 62 | 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x08, 0x38, 0x7c, 0x38, 0xfe, 0xfe, 0x7c, 0x38, 0x7c, 63 | 0x10, 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x7c, 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x00, 0x00, 64 | 0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff, 0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, 0x00, 65 | 0xff, 0xc3, 0x99, 0xbd, 0xbd, 0x99, 0xc3, 0xff, 0x0f, 0x07, 0x0f, 0x7d, 0xcc, 0xcc, 0xcc, 0x78, 66 | 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18, 0x3f, 0x33, 0x3f, 0x30, 0x30, 0x70, 0xf0, 0xe0, 67 | 0x7f, 0x63, 0x7f, 0x63, 0x63, 0x67, 0xe6, 0xc0, 0x99, 0x5a, 0x3c, 0xe7, 0xe7, 0x3c, 0x5a, 0x99, 68 | 0x80, 0xe0, 0xf8, 0xfe, 0xf8, 0xe0, 0x80, 0x00, 0x02, 0x0e, 0x3e, 0xfe, 0x3e, 0x0e, 0x02, 0x00, 69 | 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x00, 70 | 0x7f, 0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x00, 0x3e, 0x63, 0x38, 0x6c, 0x6c, 0x38, 0xcc, 0x78, 71 | 0x00, 0x00, 0x00, 0x00, 0x7e, 0x7e, 0x7e, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x7e, 0x3c, 0x18, 0xff, 72 | 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, 73 | 0x00, 0x18, 0x0c, 0xfe, 0x0c, 0x18, 0x00, 0x00, 0x00, 0x30, 0x60, 0xfe, 0x60, 0x30, 0x00, 0x00, 74 | 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00, 0x00, 0x24, 0x66, 0xff, 0x66, 0x24, 0x00, 0x00, 75 | 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x7e, 0x3c, 0x18, 0x00, 0x00, 76 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x78, 0x78, 0x30, 0x30, 0x00, 0x30, 0x00, 77 | 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x6c, 0xfe, 0x6c, 0xfe, 0x6c, 0x6c, 0x00, 78 | 0x30, 0x7c, 0xc0, 0x78, 0x0c, 0xf8, 0x30, 0x00, 0x00, 0xc6, 0xcc, 0x18, 0x30, 0x66, 0xc6, 0x00, 79 | 0x38, 0x6c, 0x38, 0x76, 0xdc, 0xcc, 0x76, 0x00, 0x60, 0x60, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 80 | 0x18, 0x30, 0x60, 0x60, 0x60, 0x30, 0x18, 0x00, 0x60, 0x30, 0x18, 0x18, 0x18, 0x30, 0x60, 0x00, 81 | 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00, 0x00, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x00, 0x00, 82 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x60, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x00, 83 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00, 84 | 0x7c, 0xc6, 0xce, 0xde, 0xf6, 0xe6, 0x7c, 0x00, 0x30, 0x70, 0x30, 0x30, 0x30, 0x30, 0xfc, 0x00, 85 | 0x78, 0xcc, 0x0c, 0x38, 0x60, 0xcc, 0xfc, 0x00, 0x78, 0xcc, 0x0c, 0x38, 0x0c, 0xcc, 0x78, 0x00, 86 | 0x1c, 0x3c, 0x6c, 0xcc, 0xfe, 0x0c, 0x1e, 0x00, 0xfc, 0xc0, 0xf8, 0x0c, 0x0c, 0xcc, 0x78, 0x00, 87 | 0x38, 0x60, 0xc0, 0xf8, 0xcc, 0xcc, 0x78, 0x00, 0xfc, 0xcc, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x00, 88 | 0x78, 0xcc, 0xcc, 0x78, 0xcc, 0xcc, 0x78, 0x00, 0x78, 0xcc, 0xcc, 0x7c, 0x0c, 0x18, 0x70, 0x00, 89 | 0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x60, 90 | 0x18, 0x30, 0x60, 0xc0, 0x60, 0x30, 0x18, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0xfc, 0x00, 0x00, 91 | 0x60, 0x30, 0x18, 0x0c, 0x18, 0x30, 0x60, 0x00, 0x78, 0xcc, 0x0c, 0x18, 0x30, 0x00, 0x30, 0x00, 92 | 0x7c, 0xc6, 0xde, 0xde, 0xde, 0xc0, 0x78, 0x00, 0x30, 0x78, 0xcc, 0xcc, 0xfc, 0xcc, 0xcc, 0x00, 93 | 0xfc, 0x66, 0x66, 0x7c, 0x66, 0x66, 0xfc, 0x00, 0x3c, 0x66, 0xc0, 0xc0, 0xc0, 0x66, 0x3c, 0x00, 94 | 0xf8, 0x6c, 0x66, 0x66, 0x66, 0x6c, 0xf8, 0x00, 0xfe, 0x62, 0x68, 0x78, 0x68, 0x62, 0xfe, 0x00, 95 | 0xfe, 0x62, 0x68, 0x78, 0x68, 0x60, 0xf0, 0x00, 0x3c, 0x66, 0xc0, 0xc0, 0xce, 0x66, 0x3e, 0x00, 96 | 0xcc, 0xcc, 0xcc, 0xfc, 0xcc, 0xcc, 0xcc, 0x00, 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00, 97 | 0x1e, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78, 0x00, 0xe6, 0x66, 0x6c, 0x78, 0x6c, 0x66, 0xe6, 0x00, 98 | 0xf0, 0x60, 0x60, 0x60, 0x62, 0x66, 0xfe, 0x00, 0xc6, 0xee, 0xfe, 0xfe, 0xd6, 0xc6, 0xc6, 0x00, 99 | 0xc6, 0xe6, 0xf6, 0xde, 0xce, 0xc6, 0xc6, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00, 100 | 0xfc, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0xdc, 0x78, 0x1c, 0x00, 101 | 0xfc, 0x66, 0x66, 0x7c, 0x6c, 0x66, 0xe6, 0x00, 0x78, 0xcc, 0xe0, 0x70, 0x1c, 0xcc, 0x78, 0x00, 102 | 0xfc, 0xb4, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xfc, 0x00, 103 | 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x00, 0xc6, 0xc6, 0xc6, 0xd6, 0xfe, 0xee, 0xc6, 0x00, 104 | 0xc6, 0xc6, 0x6c, 0x38, 0x38, 0x6c, 0xc6, 0x00, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x30, 0x78, 0x00, 105 | 0xfe, 0xc6, 0x8c, 0x18, 0x32, 0x66, 0xfe, 0x00, 0x78, 0x60, 0x60, 0x60, 0x60, 0x60, 0x78, 0x00, 106 | 0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x02, 0x00, 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x78, 0x00, 107 | 0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 108 | 0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x76, 0x00, 109 | 0xe0, 0x60, 0x60, 0x7c, 0x66, 0x66, 0xdc, 0x00, 0x00, 0x00, 0x78, 0xcc, 0xc0, 0xcc, 0x78, 0x00, 110 | 0x1c, 0x0c, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00, 111 | 0x38, 0x6c, 0x60, 0xf0, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8, 112 | 0xe0, 0x60, 0x6c, 0x76, 0x66, 0x66, 0xe6, 0x00, 0x30, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00, 113 | 0x0c, 0x00, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78, 0xe0, 0x60, 0x66, 0x6c, 0x78, 0x6c, 0xe6, 0x00, 114 | 0x70, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00, 0x00, 0x00, 0xcc, 0xfe, 0xfe, 0xd6, 0xc6, 0x00, 115 | 0x00, 0x00, 0xf8, 0xcc, 0xcc, 0xcc, 0xcc, 0x00, 0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 116 | 0x00, 0x00, 0xdc, 0x66, 0x66, 0x7c, 0x60, 0xf0, 0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0x1e, 117 | 0x00, 0x00, 0xdc, 0x76, 0x66, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x7c, 0xc0, 0x78, 0x0c, 0xf8, 0x00, 118 | 0x10, 0x30, 0x7c, 0x30, 0x30, 0x34, 0x18, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 119 | 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x00, 0x00, 0x00, 0xc6, 0xd6, 0xfe, 0xfe, 0x6c, 0x00, 120 | 0x00, 0x00, 0xc6, 0x6c, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8, 121 | 0x00, 0x00, 0xfc, 0x98, 0x30, 0x64, 0xfc, 0x00, 0x1c, 0x30, 0x30, 0xe0, 0x30, 0x30, 0x1c, 0x00, 122 | 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00, 0xe0, 0x30, 0x30, 0x1c, 0x30, 0x30, 0xe0, 0x00, 123 | 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0x00 124 | }; 125 | 126 | #ifdef _WIN32 127 | #include 128 | 129 | static HWND MainWindow; 130 | static HDC hdc; 131 | static HBITMAP hbm; 132 | static unsigned char Bitmap[SCREENW*SCREENH]; 133 | static DWORD LastFrame; 134 | enum { KBBUF_SIZE = 16 }; 135 | static unsigned short KeyboardBuffer[KBBUF_SIZE]; 136 | static unsigned KBHead, KBTail; 137 | 138 | static void ScreenUpdate(void) 139 | { 140 | MSG msg; 141 | 142 | unsigned char* dst = Bitmap; 143 | for (int y = 0; y < SCREENH; ++y) { 144 | const unsigned char* src = &VideoMem[(y>>1)*80 + (y&1?8192:0)]; 145 | for (int x = 0; x < SCREENW/4; ++x) { 146 | const unsigned char p = *src++; 147 | *dst++ = (p>>6)&3; 148 | *dst++ = (p>>4)&3; 149 | *dst++ = (p>>2)&3; 150 | *dst++ = (p>>0)&3; 151 | } 152 | } 153 | 154 | const DWORD CurTime = GetTickCount(); 155 | if (LastFrame && CurTime - LastFrame < 20) { 156 | Sleep(LastFrame+20-CurTime); 157 | } 158 | 159 | InvalidateRect(MainWindow, NULL, TRUE); 160 | LastFrame = GetTickCount(); 161 | while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) { 162 | if (msg.message == WM_QUIT) { 163 | exit(0); 164 | } 165 | TranslateMessage(&msg); 166 | DispatchMessage(&msg); 167 | } 168 | } 169 | 170 | static int KeyPressed(void) 171 | { 172 | ScreenUpdate(); 173 | return KBHead != KBTail; 174 | } 175 | 176 | static unsigned ReadKey(void) 177 | { 178 | if (KBHead == KBTail) { 179 | while (!KeyPressed()) 180 | ; 181 | } 182 | return KeyboardBuffer[KBHead++ % KBBUF_SIZE]; 183 | } 184 | 185 | static LRESULT CALLBACK WindowProc(HWND hwnd, UINT umsg, WPARAM wparam, LPARAM lparam) 186 | { 187 | switch (umsg) { 188 | case WM_CREATE: 189 | { 190 | HDC whdc = GetWindowDC(hwnd); 191 | hdc = CreateCompatibleDC(whdc); 192 | hbm = CreateCompatibleBitmap(whdc, SCREENW, SCREENH); 193 | SelectObject(hdc, hbm); 194 | ReleaseDC(hwnd, whdc); 195 | } 196 | break; 197 | case WM_DESTROY: 198 | DeleteObject(hbm); 199 | DeleteDC(hdc); 200 | PostQuitMessage(0); 201 | break; 202 | case WM_KEYUP: 203 | { 204 | // Hack to roughly match BIOS output 205 | if (!isdigit(wparam) && (wparam < 'A' || wparam > 'Z') && wparam != 0x0D && wparam != 0x1B && wparam != ' ') 206 | wparam = 0; 207 | if (KBTail - KBHead < sizeof(KeyboardBuffer)) { 208 | KeyboardBuffer[KBTail++ % KBBUF_SIZE] = (wparam&0xff)|((lparam>>8)&0xff00); 209 | } 210 | } 211 | break; 212 | case WM_ERASEBKGND: 213 | return 1; 214 | case WM_PAINT: 215 | { 216 | PAINTSTRUCT ps; 217 | if (BeginPaint(hwnd, &ps) && !IsRectEmpty(&ps.rcPaint)) { 218 | union { 219 | BITMAPINFO bmi; 220 | char buffer[sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * 256]; 221 | } u; 222 | ZeroMemory(&u, sizeof(u)); 223 | u.bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); 224 | u.bmi.bmiHeader.biWidth = SCREENW; 225 | u.bmi.bmiHeader.biHeight = -SCREENH; 226 | u.bmi.bmiHeader.biPlanes = 1; 227 | u.bmi.bmiHeader.biCompression = BI_RGB; 228 | u.bmi.bmiHeader.biBitCount = 8; 229 | const RGBQUAD pal[4] = { 230 | {0x00, 0x00, 0x00, 0xff}, 231 | {0x55, 0xff, 0x55, 0xff}, 232 | {0x55, 0x55, 0xff, 0xff}, 233 | {0x55, 0xff, 0xff, 0xff}, 234 | }; 235 | memcpy(&u.bmi.bmiColors, pal, sizeof(pal)); 236 | if (!SetDIBits(hdc, hbm, 0, SCREENH, Bitmap, &u.bmi, DIB_RGB_COLORS)) 237 | assert(0); 238 | 239 | RECT r; 240 | GetClientRect(hwnd, &r); 241 | StretchBlt(ps.hdc, 0, 0, r.right - r.left, r.bottom - r.top, hdc, 0, 0, SCREENW, SCREENH, SRCCOPY); 242 | EndPaint(hwnd, &ps); 243 | } 244 | } 245 | return 0; 246 | } 247 | return DefWindowProc(hwnd, umsg, wparam, lparam); 248 | } 249 | 250 | static int Init(void) 251 | { 252 | WNDCLASS wc; 253 | ZeroMemory(&wc, sizeof(wc)); 254 | wc.style = CS_HREDRAW | CS_VREDRAW; 255 | wc.lpfnWndProc = &WindowProc; 256 | wc.hInstance = GetModuleHandle(0); 257 | wc.lpszClassName = TEXT("Main window"); 258 | if (!RegisterClass(&wc)) { 259 | printf("Error registering class: %d\n", (int)GetLastError()); 260 | return 0; 261 | } 262 | RECT r = { 0, 0, SCREENW*2, SCREENH*2 }; 263 | AdjustWindowRect(&r, WS_OVERLAPPEDWINDOW, FALSE); 264 | MainWindow = CreateWindow(wc.lpszClassName, TEXT("SWAR"), WS_OVERLAPPEDWINDOW|WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, r.right - r.left, r.bottom - r.top, 0, 0, wc.hInstance, 0); 265 | if (!MainWindow) { 266 | printf("Error creating window: %d\n", (int)GetLastError()); 267 | return 0; 268 | } 269 | return 1; 270 | } 271 | 272 | static void Cleanup(void) 273 | { 274 | if (IsWindow(MainWindow)) 275 | DestroyWindow(MainWindow); 276 | } 277 | #else 278 | #error Not implemented 279 | #endif 280 | 281 | static int Int10h(int ax, int bx) { 282 | if (!Initialized) { 283 | if (!Init()) 284 | exit(1); 285 | atexit(&Cleanup); 286 | Initialized = 1; 287 | } 288 | (void)ax; (void)bx; return 0; 289 | } 290 | 291 | #define LINADDR(seg, off) ((unsigned)(((seg)&0xffff)*16+((off)&0xffff)) & 0xfffff) 292 | 293 | unsigned char* GetPtr(int seg, int off) 294 | { 295 | const unsigned addr = LINADDR(seg, off); 296 | if (addr >= LINADDR(VIDEO_SEG, 0) && addr < LINADDR(VIDEO_SEG, sizeof(VideoMem))) { 297 | return &VideoMem[addr-16*VIDEO_SEG]; 298 | } 299 | if (addr >= LINADDR(FONT_SEG, FONT_OFF) && addr < LINADDR(FONT_SEG, FONT_OFF+sizeof(Font))) { 300 | return (unsigned char*)&Font[addr - LINADDR(FONT_SEG, FONT_OFF)]; 301 | } 302 | abort(); 303 | } 304 | 305 | static int FarPeek(int seg, int off) 306 | { 307 | return *GetPtr(seg, off); 308 | } 309 | 310 | static void FarPoke(int seg, int off, int val) 311 | { 312 | *GetPtr(seg, off) = (unsigned char)val; 313 | } 314 | #endif 315 | 316 | const unsigned char CosTab[] = "\x00\xFF\xFF\xFF\xFE\xFE\xFD\xFC\xFB\xF9\xF8\xF6\xF4\xF3\xF1\xEE\xEC\xEA\xE7\xE4\xE1\xDE\xDB\xD8\xD4\xD1\xCD\xC9\xC5\xC1\xBD\xB9\xB5\xB0\xAB\xA7\xA2\x9D\x98\x93\x8E\x88\x83\x7E\x78\x73\x6D\x67\x61\x5C\x56\x50\x4A\x44\x3E\x38\x31\x2B\x25\x1F\x19\x12\x0C\x06"; 317 | int ICos(int ang) 318 | { 319 | const int q = ang>>6&3; 320 | int r = CosTab[(q&1?-ang:ang)&63]; 321 | if (!r && !(q&1)) 322 | r |= 0x100; 323 | return q>>1^(q&1) ? -r : r; 324 | } 325 | 326 | int ISin(int ang) 327 | { 328 | return ICos(ang - 64); 329 | } 330 | 331 | struct Tank { 332 | int x, y, ang, pow; 333 | char* c; 334 | }; 335 | 336 | 337 | int YTab[SCREENH]; 338 | char XMask[4]; 339 | char Col[4][4]; 340 | 341 | int Grass[SCREENW]; 342 | 343 | int Seed = 43; 344 | int Rand(void) 345 | { 346 | int b = Seed & 1; 347 | Seed >>= 1; 348 | if (b) Seed ^= 0xb40; 349 | return Seed & 0x7fff; 350 | } 351 | 352 | int ReadPixel(int x, int y) 353 | { 354 | int r = (3-(x&3))<<1; 355 | x >>= 2; 356 | x += YTab[y]; 357 | return (FarPeek(VIDEO_SEG, x) >> r) & 3; 358 | } 359 | 360 | void PutPixel(int x, int y, char* cs) 361 | { 362 | int r = x&3; 363 | x >>= 2; 364 | x += YTab[y]; 365 | int old = FarPeek(VIDEO_SEG, x); 366 | FarPoke(VIDEO_SEG, x, (old&XMask[r])|cs[r]); 367 | } 368 | 369 | enum { TW=9, TH=4, TUR=6 }; 370 | void DrawTurret(int x, int y, int ang, char* c) 371 | { 372 | int tx = (x+TW/2)<<7; 373 | int ty = (y+TUR)<<7; 374 | int ca = ICos(ang)>>1; 375 | int sa = -ISin(ang)>>1; 376 | int i; 377 | for (i = 0; i < TUR; ++i) { 378 | PutPixel((tx>>7)&0x1ff, ty>>7, c); 379 | tx += ca; 380 | ty += sa; 381 | } 382 | } 383 | 384 | #define INSCREEN(sx,sy) ((sx)>=0&&(sx)=0&&(sy)ang)>>1; 389 | int sa = -ISin(t->ang)>>1; 390 | 391 | int tx = ((t->x+TW/2)<<7) + ca*TUR; 392 | int ty = ((t->y+TUR)<<7) + sa*TUR; 393 | 394 | int lx=-1, ly=0; 395 | 396 | ca = ca * t->pow / 100; 397 | sa = sa * t->pow / 100; 398 | 399 | int ssa = 0; 400 | 401 | for (;;) { 402 | int sx = (tx>>7)&0x1ff; 403 | int sy = ty>>7; 404 | 405 | if (sx < 0 || sx >= SCREENW || sy >= SCREENH) 406 | break; 407 | 408 | ScreenUpdate(); 409 | 410 | if (INSCREEN(lx, ly)) 411 | PutPixel(lx, ly, Col[0]); 412 | 413 | if (INSCREEN(sx, sy)) { 414 | if (sy > 8) { 415 | const int hit = ReadPixel(sx, sy); 416 | if (hit) { 417 | PutPixel(sx, sy, Col[0]); 418 | return hit; 419 | } 420 | } 421 | PutPixel(sx, sy, Col[1]); 422 | } 423 | 424 | lx = sx; 425 | ly = sy; 426 | 427 | tx += ca; 428 | ty += sa; 429 | 430 | sa += ssa>>10; 431 | 432 | ssa += 5; 433 | } 434 | 435 | if (INSCREEN(lx, ly)) 436 | PutPixel(lx, ly, Col[0]); 437 | 438 | return 0; 439 | } 440 | 441 | void DrawBody(int x, int y, char* c) 442 | { 443 | int tx, ty; 444 | for (ty = 0; ty < TH; ++ty) { 445 | for (tx = 0; tx < TW; ++tx) { 446 | PutPixel(x+tx, y+ty+(TUR+1), c); 447 | } 448 | } 449 | } 450 | 451 | void DrawChar(int x, int y, int ch, char* fg, char* bg) 452 | { 453 | int cx, cy; 454 | for (cy = 0; cy < 8; ++cy) { 455 | int row = FarPeek(FONT_SEG, FONT_OFF+ch*8+cy); 456 | for (cx = 0; cx < 8; ++cx) { 457 | PutPixel(x+cx, y+cy, row&0x80 ? fg : bg); 458 | row <<= 1; 459 | } 460 | } 461 | } 462 | 463 | void DrawStr(int x, int y, const char* str, char* fg, char* bg) 464 | { 465 | while (*str) { 466 | DrawChar(x, y, *str++, fg, bg); 467 | x += 8; 468 | } 469 | } 470 | void ShowTankInfo(struct Tank* t) 471 | { 472 | char temp[32]; 473 | sprintf(temp, "Ang %3d Pwr %3d", t->ang*180>>7, t->pow); 474 | DrawStr(0, 0, temp, t->c, Col[0]); 475 | } 476 | 477 | void DrawTank(struct Tank* t) 478 | { 479 | DrawBody(t->x, t->y, t->c); 480 | DrawTurret(t->x, t->y, t->ang, t->c); 481 | } 482 | 483 | void UpdateTurrent(struct Tank* t, int ang) 484 | { 485 | // Erase old turrent 486 | DrawTurret(t->x, t->y, t->ang, Col[0]); 487 | if (ang < 0) 488 | ang += 129; 489 | if (ang > 128) 490 | ang -= 129; 491 | DrawTurret(t->x, t->y, t->ang = ang, t->c); 492 | } 493 | 494 | int main() 495 | { 496 | #ifdef __SCC__ 497 | Seed = clock(); 498 | #endif 499 | 500 | const int OldMode = Int10h(INT10H_GETMODE, 0) & 0xff; 501 | Int10h(INT10H_SETMODE|4, 0x0000); // Set 320x200 2-bit color mode 502 | Int10h(INT10H_SETPAL, 0x0100); // Set alternate palette 503 | 504 | int x, y; 505 | 506 | for (y = 0; y < SCREENH; ++y) { 507 | YTab[y] = (y>>1)*80 | (y&1)<<13; 508 | } 509 | 510 | for (x = 0; x < 4; ++x) { 511 | int shift = 6-(x&3)*2; 512 | XMask[x] = ~(3< SCREENH-20) y = SCREENH-20; 522 | Grass[x] = y; 523 | int s; 524 | do { 525 | s = Rand() & 7; 526 | } while (s == 7); 527 | y += s-3; 528 | } 529 | 530 | for (x=0; xang; 580 | switch (ch) { 581 | case 0x4B00: 582 | ++ang; 583 | break; 584 | case 0x4D00: 585 | --ang; 586 | break; 587 | case 0x4800: 588 | if (++cur->pow >= 100) 589 | cur->pow = 100; 590 | break; 591 | case 0x5000: 592 | if (--cur->pow < 0) 593 | cur->pow = 0; 594 | break; 595 | case 0x4900: 596 | cur->pow = 100; 597 | break; 598 | case 0x5100: 599 | cur->pow = 0; 600 | break; 601 | default: 602 | continue; 603 | } 604 | 605 | if (ang != cur->ang) 606 | UpdateTurrent(cur, ang); 607 | ShowTankInfo(cur); 608 | } 609 | } 610 | Done: 611 | Int10h(INT10H_SETMODE|OldMode, 0); 612 | } 613 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if (MSVC) 2 | set(CAT type 2>nul:) 3 | set(RM del 2>nul:) 4 | set(DIFF comp /m) 5 | else() 6 | set(CAT cat) 7 | set(RM rm -f) 8 | set(DIFF diff) 9 | endif() 10 | 11 | add_executable(dossim dossim.c) 12 | 13 | find_program(DOSBOX dosbox) 14 | if (NOT DOSBOX) 15 | message(WARNING "DOSBox not found - Using simulator.") 16 | set(DOSBOX $ "$") 17 | endif() 18 | 19 | add_subdirectory(scc) 20 | add_subdirectory(scpp) 21 | add_subdirectory(sas) 22 | add_subdirectory(sim86) 23 | 24 | add_custom_target(test 25 | DEPENDS test_scc test_scpp test_sas test_sim86 26 | WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} 27 | COMMENT Running tests 28 | ) 29 | -------------------------------------------------------------------------------- /test/dossim.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main(int argc, char* argv[]) 6 | { 7 | if (argc < 3) { 8 | fprintf(stderr, "Usage: %s simulator script\n", argv[0]); 9 | return 1; 10 | } 11 | 12 | FILE* fp = fopen(argv[2], "rb"); 13 | if (!fp) { 14 | fprintf(stderr, "Error opening %s\n", argv[2]); 15 | return 1; 16 | } 17 | 18 | char line[120]; 19 | char cmdline[256]; 20 | while (fgets(line, sizeof(line), fp)) { 21 | int l = (int)strlen(line); 22 | if (line[l-1] != '\n') { 23 | fprintf(stderr, "Line too long\n"); 24 | return 1; 25 | } 26 | line[l-1] = 0; 27 | l--; 28 | if (l && line[l-1] == '\r') line[l-1] = 0; 29 | char command[32]; 30 | int i; 31 | for (i = 0; i < (int)sizeof(command)-1 && line[i] && line[i] != ' '; ++i) { 32 | command[i] = line[i]; 33 | if (command[i] >= 'a' && command[i] <= 'z') 34 | command[i] &= 0xdf; 35 | } 36 | command[i] = 0; 37 | if (!strcmp(command, "ECHO")) { 38 | } else { 39 | snprintf(cmdline, sizeof(cmdline), "\"%s\" %s", argv[1], line); 40 | if (system(cmdline)) { 41 | fprintf(stderr, "\"%s\" failed\n", cmdline); 42 | return 1; 43 | } 44 | } 45 | } 46 | 47 | fclose(fp); 48 | return 0; 49 | } 50 | -------------------------------------------------------------------------------- /test/sas/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(TESTS 2 | sasm 3 | scc-self 4 | ) 5 | 6 | set(TEST_CMDS "echo off\nscc.com scc.c\nscc.com sas.c\n") 7 | set(TEST_SOURCES "") 8 | set(TEST_CLEAN "") 9 | set(TEST_COMPARE "") 10 | set(TEST_C_ASM "") 11 | 12 | foreach(TEST ${TESTS}) 13 | set(TEST_CMDS "${TEST_CMDS}sas.com ${TEST}.asm\n") 14 | set(TEST_CLEAN ${TEST_CLEAN} ${TEST}.com) 15 | set(TEST_SOURCES ${TEST_SOURCES} "${CMAKE_CURRENT_SOURCE_DIR}/${TEST}.asm") 16 | set(TEST_C_ASM ${TEST_C_ASM} COMMAND $ "${CMAKE_CURRENT_SOURCE_DIR}/${TEST}.asm" ${TEST}.com) 17 | set(TEST_COMPARE ${TEST_COMPARE} COMMAND ${DIFF} ${TEST}.com \"${CMAKE_CURRENT_SOURCE_DIR}/${TEST}.ref\") 18 | endforeach() 19 | file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/runtests.bat" "${TEST_CMDS}") 20 | 21 | add_custom_target(test_sas 22 | COMMAND ${CMAKE_COMMAND} -E copy ${TEST_SOURCES} . 23 | COMMAND ${CMAKE_COMMAND} -E copy "${PROJECT_BINARY_DIR}/scc.com" . 24 | COMMAND ${CMAKE_COMMAND} -E copy "${PROJECT_SOURCE_DIR}/scc.c" . 25 | COMMAND ${CMAKE_COMMAND} -E copy "${PROJECT_SOURCE_DIR}/sas.c" . 26 | # First check the version compiled with the host compiler 27 | COMMAND ${RM} ${TEST_CLEAN} 28 | ${TEST_C_ASM} 29 | ${TEST_COMPARE} 30 | COMMAND ${RM} ${TEST_CLEAN} 31 | COMMAND ${DOSBOX} runtests.bat 32 | ${TEST_COMPARE} 33 | DEPENDS scc_com ${TEST_SOURCES} "${PROJECT_SOURCE_DIR}/sas.c" 34 | WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} 35 | COMMENT Testing sas 36 | ) 37 | 38 | -------------------------------------------------------------------------------- /test/sas/sasm.ref: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mras0/scc/8cc3ebe4d8940bd60c515a1994b7d5273872585b/test/sas/sasm.ref -------------------------------------------------------------------------------- /test/sas/scc-self.ref: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mras0/scc/8cc3ebe4d8940bd60c515a1994b7d5273872585b/test/sas/scc-self.ref -------------------------------------------------------------------------------- /test/scc/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(TESTS 2 | array 3 | comma 4 | condop 5 | deadcode 6 | decl 7 | dowhile 8 | enum 9 | lgoto # goto would clash with DOS goto builtin 10 | sizeof 11 | struct 12 | switch 13 | union 14 | bug 15 | strlit 16 | unsigned 17 | typedef 18 | ) 19 | 20 | set(TEST_CMDS "echo off\nscc.com scc.c\nscc.com scc.c\n") 21 | set(TEST_SOURCES "") 22 | set(TEST_CLEAN "") 23 | set(SIM_CMDS "") 24 | 25 | foreach(TEST ${TESTS}) 26 | add_custom_target(${TEST}_c 27 | COMMAND ${CAT} testlib.c ${TEST}.c > "${CMAKE_CURRENT_BINARY_DIR}/${TEST}.c" 28 | DEPENDS testlib.c ${TEST}.c 29 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} 30 | ) 31 | set(TEST_CMDS "${TEST_CMDS}scc.com ${TEST}.c\n${TEST}.com\n") 32 | set(TEST_CLEAN ${TEST_CLEAN} ${TEST}.com) 33 | set(TEST_SOURCES ${TEST_SOURCES} ${TEST}_c) 34 | set(SIM_CMDS ${SIM_CMDS} COMMAND $ ${TEST}.com) 35 | endforeach() 36 | 37 | # Check that v1.0 can still be compiled and self-compile 38 | set(TEST_CMDS "${TEST_CMDS}scc.com scc10.c scc10.com\nscc10.com scc10.c scc10b.com\n") 39 | set(TEST_CLEAN ${TEST_CLEAN} scc10.com scc10b.com) 40 | set(SIM_CMDS ${SIM_CMDS} COMMAND ${DIFF} \"${CMAKE_CURRENT_BINARY_DIR}/scc10b.com\" \"${CMAKE_CURRENT_SOURCE_DIR}/scc10.ref\") 41 | 42 | file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/runtests.bat" "${TEST_CMDS}") 43 | 44 | add_custom_target(test_scc 45 | COMMAND ${CMAKE_COMMAND} -E copy "${PROJECT_BINARY_DIR}/scc.com" ./ 46 | COMMAND ${CMAKE_COMMAND} -E copy "${PROJECT_SOURCE_DIR}/scc.c" ./ 47 | COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/scc10.c" ./ 48 | COMMAND ${RM} ${TEST_CLEAN} 49 | COMMAND ${DOSBOX} runtests.bat 50 | ${SIM_CMDS} 51 | DEPENDS scc_com ${TEST_SOURCES} "${PROJECT_SOURCE_DIR}/scc.c" 52 | WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} 53 | COMMENT Testing scc 54 | ) 55 | -------------------------------------------------------------------------------- /test/scc/array.c: -------------------------------------------------------------------------------- 1 | char gc[3]; 2 | int gi[2]; 3 | 4 | char str1[] = "Test"; 5 | unsigned char str2[] = "Xy\x81"; 6 | 7 | int g2[2][3]; 8 | 9 | void main(void) 10 | { 11 | char a[2], b[3]; 12 | a[0] = 42; 13 | b[2] = 55; 14 | 15 | assert_eq(a[0], 42); 16 | assert_eq(b[2], 55); 17 | assert_eq(sizeof(a), 2); 18 | assert_eq(sizeof(b), 3); 19 | 20 | int i[3]; 21 | 22 | i[0] = 1; 23 | i[1] = 2; 24 | i[2] = 3; 25 | 26 | assert_eq(i[0], 1); 27 | assert_eq(i[1], 2); 28 | assert_eq(i[2], 3); 29 | assert_eq(sizeof(i), 6); 30 | 31 | struct S { 32 | int x, y; 33 | } ss[2]; 34 | ss[0].x = 1; ss[0].y = 2; 35 | ss[1].x = 3; ss[1].y = 4; 36 | 37 | assert_eq(ss[0].x, 1); 38 | assert_eq(ss[0].y, 2); 39 | assert_eq(ss[1].x, 3); 40 | assert_eq(ss[1].y, 4); 41 | assert_eq(sizeof(ss[0]), 4); 42 | assert_eq(sizeof(ss), 8); 43 | 44 | struct SA { 45 | int a[3]; 46 | } sa; 47 | sa.a[0] = -1; 48 | sa.a[1] = -2; 49 | sa.a[2] = -3; 50 | assert_eq(sa.a[0], -1); 51 | assert_eq(sa.a[1], -2); 52 | assert_eq(sa.a[2], -3); 53 | assert_eq(sizeof(sa.a), 6); 54 | assert_eq(sizeof(sa), 6); 55 | 56 | char* pc = &gc[2]; 57 | *pc = 'X'; 58 | 59 | *(gi+1) = 666; 60 | 61 | assert_eq(gc[0], 0); 62 | assert_eq(gc[1], 0); 63 | assert_eq(gc[2], 'X'); 64 | 65 | assert_eq(gi[0], 0); 66 | assert_eq(gi[1], 666); 67 | 68 | assert_eq(sizeof(gc), 3); 69 | assert_eq(sizeof(gi), 4); 70 | 71 | assert_eq(sizeof(int[4]), 8); 72 | 73 | char large[512]; 74 | large[1] = 60; 75 | large[511] = 42; 76 | assert_eq(large[1], 60); 77 | assert_eq(large[511], 42); 78 | 79 | assert_eq(sizeof(str1), 5); 80 | assert_eq(str1[0], 'T'); 81 | assert_eq(str1[1], 'e'); 82 | assert_eq(str1[2], 's'); 83 | assert_eq(str1[3], 't'); 84 | assert_eq(str1[4], 0); 85 | 86 | assert_eq(sizeof(g2), 12); 87 | assert_eq(sizeof(g2[0]), 6); 88 | assert_eq(sizeof(g2[0][0]), 2); 89 | assert_eq(g2[0][0], 0); 90 | g2[0][1] = 50; 91 | g2[1][2] = 42; 92 | assert_eq(g2[0][0], 0); 93 | assert_eq(g2[0][1], 50); 94 | assert_eq(g2[0][2], 0); 95 | assert_eq(g2[1][0], 0); 96 | assert_eq(g2[1][1], 0); 97 | assert_eq(g2[1][2], 42); 98 | assert_eq(&g2[0][0], (int*)g2); 99 | 100 | char sm[3][4]; 101 | assert_eq(sizeof(sm), 12); 102 | sm[2][1] = 42; 103 | assert_eq(sm[2][1], 42); 104 | 105 | int arr3[4][5][6]; 106 | assert_eq(sizeof(arr3), 4*5*6*sizeof(int)); 107 | assert_eq(sizeof(arr3[0]), 5*6*sizeof(int)); 108 | assert_eq(sizeof(arr3[0][0]), 6*sizeof(int)); 109 | assert_eq(sizeof(arr3[0][0][0]), sizeof(int)); 110 | arr3[1][1][1] = 123; 111 | assert_eq(arr3[1][1][1], 123); 112 | 113 | assert_eq(sizeof(str2), 4); 114 | assert_eq(str2[0], 'X'); 115 | assert_eq(str2[1], 'y'); 116 | assert_eq(str2[2], 0x81); 117 | assert_eq(str2[3], 0); 118 | } 119 | -------------------------------------------------------------------------------- /test/scc/bug.c: -------------------------------------------------------------------------------- 1 | 2 | char b00_m[2]; 3 | int bug00_r(int i) { return b00_m[i]&0xff; } 4 | void bug00() { 5 | b00_m[0] = 42; 6 | b00_m[1] = 9; 7 | assert_eq(bug00_r(0)|bug00_r(1)<<8, 0x92a); 8 | } 9 | 10 | void bug01() { 11 | int x = 0xAA; 12 | int y = !!(x & 4) != !!(x & 2); 13 | assert_eq(y, 1); 14 | y = !!(x & 4) == !!(x & 2); 15 | assert_eq(y, 0); 16 | } 17 | 18 | void bug02() { 19 | const int flags = 0x40; 20 | const int cc = 0x0e; 21 | enum { 22 | FC = 1<<0, 23 | FZ = 1<<6, 24 | FS = 1<<7, 25 | FO = 1<<11, 26 | }; 27 | 28 | const int e = !(cc & 1); 29 | const int j = (flags & FZ) || (!(flags & FS) != !(flags & FO)); 30 | assert_eq(j, 1); 31 | assert_eq(j==e, 1); 32 | } 33 | 34 | void bug03() { 35 | int x = 1; 36 | if (x) { 37 | } 38 | assert_eq(x, 1); 39 | } 40 | 41 | int b03_f(int a, int b, int c, int d, int e, int f) { 42 | return a+b*2+c*3+d*4+e*5+f*6; 43 | } 44 | 45 | void bug04() { 46 | assert_eq(b03_f(2,3,5,7,11,13), 184); 47 | } 48 | 49 | void main() { 50 | bug00(); 51 | bug01(); 52 | bug02(); 53 | bug03(); 54 | bug04(); 55 | } 56 | -------------------------------------------------------------------------------- /test/scc/comma.c: -------------------------------------------------------------------------------- 1 | int a() { return 1,2,3; } 2 | 3 | int f1() {return 1;} 4 | int f2() {return 2;} 5 | int f3() {return 42;} 6 | 7 | int b() { return f1(),f2(),f3(); } 8 | 9 | int c() { 10 | int a = 0; 11 | int b; 12 | b = ++a, ++a; 13 | return b; 14 | } 15 | 16 | void main(void) { 17 | assert_eq(a(), 3); 18 | assert_eq(b(), 42); 19 | assert_eq(c(), 1); 20 | } 21 | -------------------------------------------------------------------------------- /test/scc/condop.c: -------------------------------------------------------------------------------- 1 | int f(int i) { return i-1; } 2 | 3 | int g; 4 | 5 | int glob = 1+1-2 ? 3*4 : 5*6; 6 | 7 | int x(int i) { g = i; return i; } 8 | 9 | void main() 10 | { 11 | assert_eq(glob, 30); 12 | assert_eq(2 ? x(42) : x(43), 42); 13 | assert_eq(g, 42); 14 | char c = 42; 15 | int i = 33; 16 | assert_eq(f(1) * 4 ? f(i) : c, 42); 17 | assert_eq(f(2) * 4 ? f(i) : c, 32); 18 | i = 0 ? 1 : 2 ? 3 : 4; 19 | assert_eq(i, 3); 20 | 21 | int a = 0; 22 | int b = 0; 23 | *(1-1?&a:&b) = 42; 24 | assert_eq(a, 0); 25 | assert_eq(b, 42); 26 | { 27 | int i = 2; 28 | char c = 3; 29 | int x = 0; 30 | assert_eq(x ? c : i, 2); 31 | } 32 | { 33 | int i = 2; 34 | char c = 3; 35 | int x = 42; 36 | assert_eq(x ? c : i, 3); 37 | } 38 | int xx = 42; 39 | assert_eq(xx ? xx : -1, 42); 40 | assert_eq(!xx ? xx : -1, -1); 41 | 42 | const char* a = "AB"; 43 | const char* b = a ? a : "XY"; 44 | assert_eq(b[0], 'A'); 45 | assert_eq(b[1], 'B'); 46 | 47 | const char* c = 0; 48 | const char* d = c ? c : "XY"; 49 | assert_eq(d[0], 'X'); 50 | assert_eq(d[1], 'Y'); 51 | 52 | int m1 = -1; 53 | int zr = 0; 54 | int p1 = 1; 55 | assert_eq(m1 < 0, 1); 56 | assert_eq(zr < 0, 0); 57 | assert_eq(p1 < 0, 0); 58 | assert_eq(m1 <= 0, 1); 59 | assert_eq(zr <= 0, 1); 60 | assert_eq(p1 <= 0, 0); 61 | assert_eq(m1 == 0, 0); 62 | assert_eq(zr == 0, 1); 63 | assert_eq(p1 == 0, 0); 64 | assert_eq(m1 != 0, 1); 65 | assert_eq(zr != 0, 0); 66 | assert_eq(p1 != 0, 1); 67 | assert_eq(m1 >= 0, 0); 68 | assert_eq(zr >= 0, 1); 69 | assert_eq(p1 >= 0, 1); 70 | assert_eq(m1 > 0, 0); 71 | assert_eq(zr > 0, 0); 72 | assert_eq(p1 > 0, 1); 73 | 74 | unsigned u1 = 0x8000; 75 | assert_eq(u1 < 0, 0); 76 | assert_eq(u1 <= 0, 0); 77 | assert_eq(u1 == 0, 0); 78 | assert_eq(u1 != 0, 1); 79 | assert_eq(u1 >= 0, 1); 80 | assert_eq(u1 > 0, 1); 81 | } 82 | -------------------------------------------------------------------------------- /test/scc/deadcode.c: -------------------------------------------------------------------------------- 1 | void main() 2 | { 3 | int x = 42; 4 | if (0) { 5 | char* s = "test!"; 6 | ++s; 7 | *s = 42; 8 | memset(s, 0, 4); 9 | } 10 | assert_eq(x, 42); 11 | } 12 | -------------------------------------------------------------------------------- /test/scc/decl.c: -------------------------------------------------------------------------------- 1 | int ga = 1, gb = 2, *gp; 2 | 3 | void test1() { 4 | int q = 42; 5 | assert_eq(q, 42); 6 | int a, b = 33, *pi = &a; 7 | *pi = 42; 8 | assert_eq(a, 42); 9 | assert_eq(b, 33); 10 | assert_eq(ga, 1); 11 | assert_eq(gb, 2); 12 | gp = &gb; 13 | *gp = 42; 14 | assert_eq(gb, 42); 15 | 16 | struct S { 17 | char a,b,*pc; 18 | int *pi, a, **pi; 19 | } s, s2; 20 | assert_eq(sizeof(s2), 10); 21 | 22 | assert_eq(no_fwd(42), 43); 23 | } 24 | int no_fwd(int x) { return x+1; } 25 | 26 | int t2_s0() { 27 | static int s; 28 | return s++; 29 | } 30 | 31 | int t2_s1() { 32 | static int s, s2; 33 | s = s*2+1; 34 | s2 = s2*3+7; 35 | return s+s2; 36 | } 37 | 38 | int t2_s2() { 39 | static int x; 40 | static char c; 41 | x++; 42 | c = (char)x; 43 | return c; 44 | } 45 | 46 | static int t2_s3(int i, int x) { 47 | static char a[2]; 48 | if (x) ++a[i]; 49 | return a[i]; 50 | } 51 | 52 | int t2_s4(int* x, int* y, int set) { 53 | static struct S { 54 | int x, y; 55 | } s; 56 | if (set) { 57 | s.x=*x; 58 | s.y=*y; 59 | } else { 60 | *x=s.x+1; 61 | *y=s.y+1; 62 | } 63 | } 64 | 65 | static int a; 66 | static struct T2_1 { 67 | int x, y; 68 | } t2_1; 69 | 70 | void test2() { 71 | assert_eq(t2_s0(), 0); 72 | assert_eq(t2_s0(), 1); 73 | assert_eq(t2_s0(), 2); 74 | 75 | assert_eq(t2_s1(), 8); // 1 7 76 | assert_eq(t2_s1(), 31); // 3 28 77 | assert_eq(t2_s1(), 98); // 7 91 78 | 79 | assert_eq(t2_s2(), 1); 80 | assert_eq(t2_s2(), 2); 81 | assert_eq(t2_s2(), 3); 82 | 83 | assert_eq(t2_s3(0, 0), 0); 84 | assert_eq(t2_s3(1, 0), 0); 85 | assert_eq(t2_s3(0, 1), 1); 86 | assert_eq(t2_s3(1, 0), 0); 87 | assert_eq(t2_s3(1, 1), 1); 88 | assert_eq(t2_s3(0, 0), 1); 89 | 90 | int x,y; 91 | t2_s4(&x, &y, 0); 92 | assert_eq(x, 1); 93 | assert_eq(y, 1); 94 | x=2;y=3; 95 | t2_s4(&x, &y, 1); 96 | t2_s4(&x, &y, 0); 97 | assert_eq(x, 3); 98 | assert_eq(y, 4); 99 | 100 | assert_eq(a, 0); 101 | a = 42; 102 | assert_eq(a, 42); 103 | 104 | assert_eq(sizeof(t2_1), 4); 105 | assert_eq(t2_1.x, 0); 106 | assert_eq(t2_1.y, 0); 107 | t2_1.x = 1; 108 | t2_1.y = 2; 109 | assert_eq(t2_1.x, 1); 110 | assert_eq(t2_1.y, 2); 111 | } 112 | 113 | void undeclstruct_test() { 114 | struct Undeclared* x = 0; 115 | assert_eq(x, 0); 116 | } 117 | 118 | extern ign_ext = 1337; 119 | 120 | void ignoreddecls_test() { 121 | signed char y; 122 | y = -17; 123 | assert_eq(y, -17); 124 | signed short z = 0x8123; 125 | assert_eq(z, -32477); 126 | char x; 127 | char *px = &x; 128 | volatile const char * volatile const * volatile const extreme = &px; 129 | x = 37; 130 | assert_eq(**extreme, 37); 131 | 132 | signed auto a = 42; 133 | assert_eq(a, 42); 134 | 135 | register unsigned int b = 60; 136 | assert_eq(b, 60); 137 | 138 | assert_eq(ign_ext, 1337); 139 | } 140 | 141 | struct FPS { 142 | int (*Op)(int, int); 143 | int x, y; 144 | }; 145 | 146 | int fp_g0() { return -42; } 147 | int fp_g1() { return 123; } 148 | 149 | int fp_add(int x, int y) { return x + y; } 150 | int fp_sub(int x, int y) { return x - y; } 151 | 152 | struct FPS* fp_s() { 153 | static struct FPS fps; 154 | fps.x = 5; 155 | fps.y = 7; 156 | fps.Op = &fp_add; 157 | return &fps; 158 | } 159 | 160 | void funptr_test() { 161 | assert_eq(fp_g0(), -42); 162 | assert_eq(fp_g1(), 123); 163 | 164 | int (*f)(void) = fp_g0; 165 | assert_eq(f(), -42); 166 | f = fp_g1; 167 | assert_eq(f(), 123); 168 | f = fp_g0; 169 | assert_eq(f(), -42); 170 | 171 | int (*g)(); 172 | g = fp_g1; 173 | assert_eq(g()+1, 124); 174 | 175 | struct FPS fps; 176 | fps.x = 42; 177 | fps.y = 20; 178 | fps.Op = fp_add; 179 | assert_eq(fps.Op(fps.x, fps.y), 62); 180 | fps.Op = fp_sub; 181 | assert_eq(fps.Op(fps.x, fps.y), 22); 182 | 183 | int x = (*fps.Op)(3, 4); 184 | assert_eq(x, -1); 185 | 186 | int (*h)(...) = &fp_g0; 187 | int y = 2 + (*h)() + 1; 188 | assert_eq(y, -39); 189 | assert_eq(h(), -42); 190 | assert_eq((***h)(), -42); 191 | 192 | struct FPS* (*s)(void) = &fp_s; 193 | struct FPS* f2 = (*s)(); 194 | assert_eq(f2->x, 5); 195 | assert_eq(f2->y, 7); 196 | assert_eq((*f2->Op)(7,1), 8); 197 | } 198 | 199 | void main() { 200 | test1(); 201 | test2(); 202 | undeclstruct_test(); 203 | ignoreddecls_test(); 204 | funptr_test(); 205 | } 206 | -------------------------------------------------------------------------------- /test/scc/dowhile.c: -------------------------------------------------------------------------------- 1 | void main(void) 2 | { 3 | int x = 0; 4 | do assert_eq(x <= 5, 1); while (x++ != 4); 5 | assert_eq(x, 5); 6 | 7 | int y = 0; 8 | x = 0; 9 | do { 10 | if (x == 5) break; 11 | if (x&1) continue; 12 | y |= 1< 1) goto elab; 5 | ++x; 6 | } 7 | elab: 8 | assert_eq(x, 2); 9 | goto blah; 10 | x = 0; 11 | goto blah; 12 | if (0) 13 | blah: x = 42; 14 | else 15 | x = 2; 16 | assert_eq(x, 42); 17 | } 18 | 19 | void test2() { 20 | int x = 0; 21 | 22 | if (x==0) { 23 | x=1; 24 | goto Skip; 25 | } else { 26 | goto Foo; 27 | } 28 | 29 | if (!x) { 30 | if (~x + 1 == -1) { 31 | Foo: 32 | x=2; 33 | goto Skip; 34 | } else { 35 | Skip: 36 | x=123; 37 | } 38 | } 39 | assert_eq(x, 123); 40 | } 41 | 42 | void main() { 43 | test1(); 44 | test2(); 45 | } 46 | -------------------------------------------------------------------------------- /test/scc/scc10.ref: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mras0/scc/8cc3ebe4d8940bd60c515a1994b7d5273872585b/test/scc/scc10.ref -------------------------------------------------------------------------------- /test/scc/sizeof.c: -------------------------------------------------------------------------------- 1 | void main() 2 | { 3 | assert_eq(sizeof(char), 1); 4 | assert_eq(sizeof(int), 2); 5 | assert_eq(sizeof(char*), 2); 6 | assert_eq(sizeof(int*), 2); 7 | assert_eq(sizeof(int***), 2); 8 | struct S { 9 | int a; 10 | int b; 11 | }; 12 | assert_eq(sizeof(struct S), 4); 13 | assert_eq(sizeof(struct S*), 2); 14 | union U { 15 | struct S s; 16 | int x; 17 | }; 18 | assert_eq(sizeof(union U), 4); 19 | 20 | char c; 21 | int i; 22 | struct S s; 23 | union U u; 24 | assert_eq(sizeof(c), 1); 25 | assert_eq(sizeof(i), 2); 26 | assert_eq(sizeof(s), 4); 27 | assert_eq(sizeof(u), 4); 28 | assert_eq(sizeof(1+1), 2); 29 | 30 | struct S2 { 31 | struct { 32 | int a; 33 | int b; 34 | int c; 35 | } x; 36 | int y; 37 | } s2; 38 | assert_eq(sizeof(s2), 8); 39 | assert_eq(sizeof(s2.x), 6); 40 | assert_eq(sizeof(s2.y), 2); 41 | 42 | assert_eq(sizeof 1+1, 3); 43 | 44 | assert_eq(sizeof("test"), 5); 45 | assert_eq(1, 1); 46 | 47 | int x=1; 48 | int a[4]; 49 | x += sizeof(a[x]); 50 | assert_eq(x, 3); 51 | 52 | assert_eq(sizeof(struct Undefined*), sizeof(void*)); 53 | 54 | assert_eq(sizeof(int (*)(int,char)), sizeof(void*)); 55 | } 56 | -------------------------------------------------------------------------------- /test/scc/strlit.c: -------------------------------------------------------------------------------- 1 | void main() { 2 | const char* s0 = "abc\n\r\t\'\"\\def"; 3 | assert_eq(s0[0], 'a'); 4 | assert_eq(s0[1], 'b'); 5 | assert_eq(s0[2], 'c'); 6 | assert_eq(s0[3], '\n'); 7 | assert_eq(s0[4], '\r'); 8 | assert_eq(s0[5], '\t'); 9 | assert_eq(s0[6], '\''); 10 | assert_eq(s0[7], '"'); 11 | assert_eq(s0[8], '\\'); 12 | assert_eq(s0[9], 'd'); 13 | assert_eq(s0[10], 'e'); 14 | assert_eq(s0[11], 'f'); 15 | assert_eq(s0[12], 0); 16 | 17 | const char* s1 = "123\x00\x12\xFExyz"; 18 | assert_eq(s1[0], '1'); 19 | assert_eq(s1[1], '2'); 20 | assert_eq(s1[2], '3'); 21 | assert_eq(s1[3], 0); 22 | assert_eq(s1[4], 0x12); 23 | assert_eq(s1[5]&0xff, 0xFE); 24 | assert_eq(s1[6], 'x'); 25 | assert_eq(s1[7], 'y'); 26 | assert_eq(s1[8], 'z'); 27 | assert_eq(s1[9], 0); 28 | 29 | const char* s2 = 30 | // Comment! 31 | "0" 32 | /* */ "1" 33 | "2" 34 | #if 1 35 | "3" 36 | #endif 37 | ; 38 | assert_eq(s2[0], '0'); 39 | assert_eq(s2[1], '1'); 40 | assert_eq(s2[2], '2'); 41 | assert_eq(s2[3], '3'); 42 | assert_eq(s2[4], 0); 43 | 44 | const char* s3 = "\0x\123\377\082\129"; 45 | assert_eq(s3[0], 0); 46 | assert_eq(s3[1], 'x'); 47 | assert_eq(s3[2], 0123); 48 | assert_eq(s3[3], -1); 49 | assert_eq(s3[4], 0); 50 | assert_eq(s3[5], '8'); 51 | assert_eq(s3[6], '2'); 52 | assert_eq(s3[7], 012); 53 | assert_eq(s3[8], '9'); 54 | assert_eq(s3[9], 0); 55 | } 56 | -------------------------------------------------------------------------------- /test/scc/struct.c: -------------------------------------------------------------------------------- 1 | struct S { 2 | int a; 3 | int b; 4 | }; 5 | 6 | struct S* s; 7 | 8 | int foo(int x) { return x; } 9 | 10 | void bug1() { 11 | int x = 2; 12 | s = (struct S*)&_EBSS; 13 | struct S* ss = &s[foo(x)]; 14 | ss->a = 42; 15 | ss->b = 60; 16 | assert_eq(ss, s+2); 17 | assert_eq(ss-s, 2); 18 | assert_eq(s[2].a, 42); 19 | assert_eq(s[2].b, 60); 20 | struct S* s2 = s; 21 | assert_eq(++s2, s+1); 22 | s2++; 23 | assert_eq(s2, ss); 24 | assert_eq(--s2, s+1); 25 | s2--; 26 | assert_eq(s2, s); 27 | } 28 | 29 | void assign() { 30 | struct S s1; 31 | s1.a = 1; 32 | s1.b = 2; 33 | struct S s2; 34 | s2 = s1; 35 | assert_eq(s2.a, 1); 36 | assert_eq(s2.b, 2); 37 | } 38 | 39 | struct S gs; 40 | 41 | void global() { 42 | assert_eq(gs.a, 0); 43 | assert_eq(gs.b, 0); 44 | gs.b = 2; 45 | assert_eq(gs.a, 0); 46 | assert_eq(gs.b, 2); 47 | struct S ts; 48 | ts = gs; 49 | assert_eq(ts.a, 0); 50 | assert_eq(ts.b, 2); 51 | ts.a = 3; 52 | ts.b = 4; 53 | gs = ts; 54 | assert_eq(gs.a, 3); 55 | assert_eq(gs.b, 4); 56 | } 57 | 58 | struct L { 59 | struct L* next; 60 | int a; 61 | }; 62 | 63 | void list() { 64 | struct L l1; 65 | struct L l2; 66 | l1.next = &l2; 67 | l2.next = 0; 68 | int n = 0; 69 | struct L* l = &l1; 70 | while (l) { 71 | ++n; 72 | l = l->next; 73 | } 74 | assert_eq(n, 2); 75 | } 76 | 77 | struct A { 78 | int a; 79 | int b; 80 | }; 81 | 82 | struct B { 83 | struct A a1; 84 | struct A a2; 85 | struct C { 86 | int x; 87 | } c; 88 | }; 89 | 90 | void nested() 91 | { 92 | struct B b; 93 | assert_eq(sizeof(struct B), 10); 94 | b.a1.a = 1; 95 | b.a1.b = 2; 96 | b.a2.a = 3; 97 | b.a2.b = 4; 98 | b.c.x = 5; 99 | assert_eq(b.a1.a, 1); 100 | assert_eq(b.a1.b, 2); 101 | assert_eq(b.a2.a, 3); 102 | assert_eq(b.a2.b, 4); 103 | assert_eq(b.c.x, 5); 104 | assert_eq((char*)&b.a2.b - (char*)&b, 6); 105 | assert_eq((char*)&b.c.x - (char*)&b, 8); 106 | } 107 | 108 | void unnamed() { 109 | struct { 110 | struct { 111 | int a; 112 | int b; 113 | } x; 114 | struct { 115 | int c; 116 | } y; 117 | } un; 118 | 119 | un.x.a=1; un.x.b=2; un.y.c=3; 120 | assert_eq(un.x.a, 1); 121 | assert_eq(un.x.b, 2); 122 | assert_eq(un.y.c, 3); 123 | assert_eq((char*)&un.y.c - (char*)&un, 4); 124 | assert_eq(sizeof(un), 6); 125 | } 126 | 127 | void scope() { 128 | struct A { 129 | int x; 130 | }; 131 | struct B { 132 | struct A a; 133 | }; 134 | struct B b1, b2; 135 | b1.a.x = 42; 136 | b2.a = b1.a; 137 | assert_eq(b2.a.x, 42); 138 | } 139 | 140 | void twice() { 141 | struct T { 142 | int a; 143 | }; 144 | struct TL { 145 | struct T t; 146 | }; 147 | struct RS { 148 | struct TL* tl; 149 | }; 150 | struct TL tl; 151 | tl.t.a = 60; 152 | struct RS rs; 153 | rs.tl = &tl; 154 | struct RS* rsp = &rs; 155 | struct T t; 156 | t = rsp->tl->t; 157 | } 158 | 159 | void swap() { 160 | struct S { 161 | int a; 162 | int b; 163 | } ss[2]; 164 | struct S temp; 165 | struct S * x = &ss[0], * y = &ss[1]; 166 | ss[0].a = 42; 167 | ss[1].a = 60; 168 | temp = *x; 169 | *x = *y; 170 | *y = temp; 171 | assert_eq(x->a, 60); 172 | assert_eq(y->a, 42); 173 | } 174 | 175 | void cast() { 176 | struct T { 177 | char* s; 178 | }; 179 | struct A { 180 | int i; 181 | }; 182 | struct A a; 183 | a.i = 42; 184 | struct T t; 185 | t.s = (char*)&a; 186 | assert_eq(((struct A*)t.s)->i, 42); 187 | struct T* tp = &t; 188 | assert_eq(((struct A*)tp->s)->i, 42); 189 | } 190 | 191 | void baz(char* b, int v, int sz) { 192 | while (sz--) *b++ = v; 193 | } 194 | 195 | void with_array() { 196 | struct File { 197 | int p; 198 | char b[512]; 199 | }; 200 | struct File* CurFile = (struct File*)&_EBSS; 201 | baz(CurFile->b, 0, sizeof(CurFile->b)); 202 | CurFile->p = 100; 203 | baz(CurFile->b + CurFile->p, 42, 2); 204 | assert_eq(CurFile->b[99], 0); 205 | assert_eq(CurFile->b[100], 42); 206 | assert_eq(CurFile->b[101], 42); 207 | assert_eq(CurFile->b[102], 0); 208 | assert_eq(CurFile->p, 100); 209 | baz(CurFile, 0, sizeof(*CurFile)); 210 | assert_eq(CurFile->b[99], 0); 211 | assert_eq(CurFile->b[100], 0); 212 | assert_eq(CurFile->b[101], 0); 213 | assert_eq(CurFile->b[102], 0); 214 | assert_eq(CurFile->p, 0); 215 | } 216 | 217 | struct B2_A { 218 | int b; 219 | int e; 220 | } b2_a[1]; 221 | 222 | int b2_st(int t, int e) { 223 | if (t & 1) { 224 | struct B2_A* a = &b2_a[e]; 225 | return b2_st(t & ~1, a->e) * a->b; 226 | } else { 227 | return 2; 228 | } 229 | } 230 | 231 | void bug2() { 232 | b2_a[0].b = 4; 233 | b2_a[0].e = 0; 234 | assert_eq(b2_st(3, 0), 8); 235 | } 236 | 237 | void main() { 238 | bug1(); 239 | assign(); 240 | global(); 241 | list(); 242 | nested(); 243 | unnamed(); 244 | scope(); 245 | twice(); 246 | swap(); 247 | cast(); 248 | with_array(); 249 | bug2(); 250 | } 251 | -------------------------------------------------------------------------------- /test/scc/switch.c: -------------------------------------------------------------------------------- 1 | int sw1(int x) 2 | { 3 | int y = 60; 4 | switch (x) { 5 | case 0: 6 | y = 2; 7 | break; 8 | case 1: 9 | y = 3; 10 | break; 11 | } 12 | return y; 13 | } 14 | 15 | void test1() 16 | { 17 | assert_eq(sw1(-1), 60); 18 | assert_eq(sw1(0), 2); 19 | assert_eq(sw1(1), 3); 20 | assert_eq(sw1(2), 60); 21 | } 22 | 23 | int sw2(int x) 24 | { 25 | int y = 0; 26 | switch (x) { 27 | case 0: 28 | y|=1<<0; 29 | case 1: 30 | y|=1<<1; 31 | case 2: 32 | y|=1<<2; 33 | break; 34 | case 3: 35 | y|=1<<3; 36 | break; 37 | default: 38 | y|=1<<4; 39 | case 4: 40 | y|=1<<5; 41 | } 42 | return y; 43 | } 44 | 45 | void test2() 46 | { 47 | assert_eq(sw2(-1), 1<<4|1<<5); 48 | assert_eq(sw2(0), 1<<0|1<<1|1<<2); 49 | assert_eq(sw2(1), 1<<1|1<<2); 50 | assert_eq(sw2(2), 1<<2); 51 | assert_eq(sw2(3), 1<<3); 52 | assert_eq(sw2(4), 1<<5); 53 | assert_eq(sw2(5), 1<<4|1<<5); 54 | } 55 | 56 | void test3() 57 | { 58 | int y = 0; 59 | switch (42) case 42: y=1; 60 | assert_eq(y,1); 61 | } 62 | 63 | void test4() 64 | { 65 | int i; 66 | int y = 0; 67 | for (i = 0; i < 10; ++i) { 68 | switch (i & 1) case 1: continue; 69 | ++y; 70 | } 71 | assert_eq(y, 5); 72 | } 73 | 74 | int sw5(int x) 75 | { 76 | switch (x & 1) { 77 | case 0: 78 | switch (x & 2) { 79 | case 0: return -8; 80 | case 2: return -7; 81 | } 82 | case 1: 83 | switch (x & 2) { 84 | case 0: return -6; 85 | case 2: return -5; 86 | } 87 | } 88 | return 0; 89 | } 90 | 91 | void test5() 92 | { 93 | assert_eq(sw5(-1), -5); 94 | assert_eq(sw5(0), -8); 95 | assert_eq(sw5(1), -6); 96 | assert_eq(sw5(2), -7); 97 | assert_eq(sw5(3), -5); 98 | assert_eq(sw5(4), -8); 99 | assert_eq(sw5(5), -6); 100 | } 101 | 102 | int sw6(int x) { 103 | int y = 0; 104 | switch (x) { 105 | case 0: 106 | y |= 1; 107 | default: 108 | y |= 8; 109 | case 1: 110 | y |= 2; 111 | break; 112 | case 2: 113 | case 3: 114 | y |= 4; 115 | } 116 | return y; 117 | } 118 | 119 | void test6() 120 | { 121 | assert_eq(sw6(0), 11); 122 | assert_eq(sw6(1), 2); 123 | assert_eq(sw6(2), 4); 124 | assert_eq(sw6(3), 4); 125 | assert_eq(sw6(4), 10); 126 | } 127 | 128 | void main() 129 | { 130 | test1(); 131 | test2(); 132 | test3(); 133 | test4(); 134 | test5(); 135 | test6(); 136 | } 137 | -------------------------------------------------------------------------------- /test/scc/testlib.c: -------------------------------------------------------------------------------- 1 | void putchar(int ch) 2 | { 3 | _emit 0x8B _emit 0x46|2<<3 _emit 0x04 // MOV DX, [BP+4] 4 | _emit 0xB8 _emit 0x00 _emit 0x02 // MOV AX, 0x0200 5 | _emit 0xCD _emit 0x21 // INT 0x21 6 | } 7 | 8 | void putstr(const char* s) 9 | { 10 | while (*s) putchar(*s++); 11 | } 12 | 13 | void putn(int n) { 14 | int i; 15 | for (i = 0; i < 4; ++i) { 16 | putchar("0123456789abcdef"[(n>>12)&0xf]); 17 | n <<= 4; 18 | } 19 | } 20 | 21 | int* GetBP(void) 22 | { 23 | _emit 0x8B _emit 0x46 _emit 0x00 // MOV AX, [BP] 24 | } 25 | 26 | void assert_eq(int a, int b) { 27 | if (a == b) return; 28 | int* BP = GetBP(); 29 | putstr("\nBP Return address\n"); 30 | int i = 0; 31 | while (*BP && ++i < 10) { 32 | putn(BP[0]); putstr(" "); putn(BP[1]); putstr("\n"); 33 | BP = (int*)BP[0]; 34 | } 35 | putn(a); putstr(" != "); putn(b); putstr("\n"); 36 | _emit 0xcc // INT3 37 | _emit 0xB8 _emit 0xFF _emit 0x4C // MOV AX, 0x4CFF 38 | _emit 0xcd _emit 0x21 // INT 0x21 39 | } 40 | 41 | void main(void); 42 | void _start(void) 43 | { 44 | // Clear BSS 45 | char* bss = &_SBSS; 46 | while (bss != &_EBSS) 47 | *bss++ = 0; 48 | main(); 49 | } 50 | -------------------------------------------------------------------------------- /test/scc/typedef.c: -------------------------------------------------------------------------------- 1 | int t00_f(int x) { return x + 1; } 2 | 3 | void t00() { 4 | unsigned char typedef uc; 5 | assert_eq(sizeof(uc), 1); 6 | uc a = -1; 7 | assert_eq(a, 0xff); 8 | typedef int i; 9 | assert_eq(sizeof(i), 2); 10 | i b = -42; 11 | assert_eq(b, -42); 12 | unsigned i c = -1; 13 | assert_eq(sizeof(c), 2); 14 | assert_eq(c, 0xffff); 15 | assert_eq(c>0, 1); 16 | 17 | typedef uc uc2; 18 | uc2 d = -2; 19 | assert_eq(d, 0xfe); 20 | assert_eq(sizeof(d), 1); 21 | assert_eq(sizeof(uc2), 1); 22 | 23 | typedef struct { 24 | int x, y; 25 | } S; 26 | S s; 27 | s.x = 1; 28 | s.y = 2; 29 | assert_eq(s.x, 1); 30 | assert_eq(s.y, 2); 31 | assert_eq(sizeof(S), 4); 32 | 33 | typedef S* SP; 34 | assert_eq(sizeof(SP), 2); 35 | SP sp = &s; 36 | assert_eq(sp->x, 1); 37 | assert_eq(sp->y, 2); 38 | 39 | typedef int (*fptr)(int); 40 | fptr f = &t00_f; 41 | assert_eq(f(2), 3); 42 | } 43 | 44 | void main() { 45 | t00(); 46 | } 47 | -------------------------------------------------------------------------------- /test/scc/union.c: -------------------------------------------------------------------------------- 1 | union U { 2 | char c; 3 | int i; 4 | struct S { 5 | int a; 6 | int b; 7 | } s; 8 | } gu; 9 | 10 | union Empty { 11 | }; 12 | 13 | void main() 14 | { 15 | assert_eq(sizeof(union Empty), 0); 16 | assert_eq(sizeof(union U), 4); 17 | assert_eq((char*)&gu.c, (char*)&gu); 18 | assert_eq((char*)&gu.i, (char*)&gu); 19 | assert_eq((char*)&gu.s, (char*)&gu); 20 | assert_eq((char*)&gu.s.a, (char*)&gu); 21 | assert_eq((char*)&gu.s.b, (char*)&gu + 2); 22 | gu.s.b = -2; 23 | gu.i = 42; 24 | assert_eq(gu.c, 42); 25 | assert_eq(gu.s.a, 42); 26 | assert_eq(gu.s.b, -2); 27 | 28 | union U u; 29 | u = gu; 30 | assert_eq(u.s.a, 42); 31 | assert_eq(u.s.b, -2); 32 | 33 | u.s.b = -3; 34 | 35 | union U* up = &u; 36 | assert_eq(up->s.b, -3); 37 | } 38 | -------------------------------------------------------------------------------- /test/scc/unsigned.c: -------------------------------------------------------------------------------- 1 | void test_uchar() { 2 | unsigned char a = -1; 3 | assert_eq(a, 0xff); 4 | unsigned char b = 42; 5 | assert_eq(b, 42); 6 | a++; 7 | assert_eq(a, 0); 8 | --a; 9 | assert_eq(a, 0xff); 10 | 11 | assert_eq((unsigned char)-2, 0xfe); 12 | int x = 0x1234; 13 | assert_eq((unsigned char)x, 0x34); 14 | 15 | b = 0xABCD; 16 | assert_eq(b, 0xCD); 17 | 18 | b += 0x1234; 19 | assert_eq(b, 0x01); 20 | } 21 | 22 | void test_uint() { 23 | unsigned int a = -1; 24 | assert_eq(a, 0xffff); 25 | ++a; 26 | assert_eq(a, 0); 27 | a--; 28 | assert_eq(a, 0xffff); 29 | a = a + 10; 30 | assert_eq(a, 9); 31 | a = a / 3; 32 | assert_eq(a, 3); 33 | a = a * 2; 34 | assert_eq(a, 6); 35 | a = a / 2; 36 | assert_eq(a, 3); 37 | 38 | unsigned int l = 0x1234; 39 | unsigned int r = 0xFEDC; 40 | 41 | assert_eq(l+r, 0x1110); 42 | assert_eq(l-r, 0x1358); 43 | assert_eq(l*r, 0x3CB0); 44 | assert_eq(l/r, 0); 45 | assert_eq(l%r, 0x1234); 46 | assert_eq(r>>1, 0x7F6E); 47 | assert_eq(r<<1, 0xFDB8); 48 | assert_eq(lr, 0); 51 | assert_eq(l>=r, 0); 52 | assert_eq(l==r, 0); 53 | assert_eq(l!=r, 1); 54 | assert_eq(rl, 1); 57 | assert_eq(r>=l, 1); 58 | assert_eq(r==l, 0); 59 | assert_eq(r!=l, 1); 60 | } 61 | 62 | void test_ptrcmp() { 63 | assert_eq((int*)0x7FFF < (int*)0x8000, 1); 64 | assert_eq((int*)0x7FFF <= (int*)0x8000, 1); 65 | assert_eq((int*)0x7FFF > (int*)0x8000, 0); 66 | assert_eq((int*)0x7FFF >= (int*)0x8000, 0); 67 | } 68 | 69 | void main() { 70 | test_uchar(); 71 | test_uint(); 72 | test_ptrcmp(); 73 | } 74 | -------------------------------------------------------------------------------- /test/scpp/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | set(TEST_CMDS "echo off\nscc.com scc.c\nscc.com scpp.c\n") 3 | set(TEST_SOURCES "") 4 | set(TEST_CLEAN "") 5 | set(TEST_COMPARE "") 6 | 7 | file(GLOB TESTS "${CMAKE_CURRENT_SOURCE_DIR}/*.in") 8 | foreach(SRC ${TESTS}) 9 | get_filename_component(TEST ${SRC} NAME_WE) 10 | set(TEST_CMDS "${TEST_CMDS}scpp.com ${TEST}.in\n") 11 | set(TEST_CLEAN ${TEST_CLEAN} ${TEST}.i) 12 | set(TEST_SOURCES ${TEST_SOURCES} ${SRC}) 13 | set(TEST_COMPARE ${TEST_COMPARE} COMMAND ${DIFF} ${TEST}.i \"${CMAKE_CURRENT_SOURCE_DIR}/${TEST}.ref\") 14 | endforeach() 15 | 16 | # For now just check that the files can be processed w/o errors 17 | set(TEST_CMDS "${TEST_CMDS}scpp.com scc.c\n") 18 | set(TEST_CMDS "${TEST_CMDS}scpp.com scpp.c\n") 19 | set(TEST_CMDS "${TEST_CMDS}scpp.com sas.c\n") 20 | 21 | file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/runtests.bat" "${TEST_CMDS}") 22 | 23 | set(TEST_SOURCES ${TEST_SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/inc1.h) 24 | 25 | add_custom_target(test_scpp 26 | COMMAND ${CMAKE_COMMAND} -E copy ${TEST_SOURCES} . 27 | COMMAND ${CMAKE_COMMAND} -E copy "${PROJECT_BINARY_DIR}/scc.com" . 28 | COMMAND ${CMAKE_COMMAND} -E copy "${PROJECT_SOURCE_DIR}/scc.c" . 29 | COMMAND ${CMAKE_COMMAND} -E copy "${PROJECT_SOURCE_DIR}/scpp.c" . 30 | COMMAND ${CMAKE_COMMAND} -E copy "${PROJECT_SOURCE_DIR}/sas.c" . 31 | COMMAND ${RM} ${TEST_CLEAN} 32 | COMMAND ${DOSBOX} runtests.bat 33 | ${TEST_COMPARE} 34 | DEPENDS scc_com scpp_com ${TEST_SOURCES} "${PROJECT_SOURCE_DIR}/scpp.c" "${PROJECT_SOURCE_DIR}/sas.c" 35 | WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} 36 | COMMENT Testing scpp 37 | ) 38 | 39 | 40 | -------------------------------------------------------------------------------- /test/scpp/inc1.h: -------------------------------------------------------------------------------- 1 | #ifndef EXCLUDE_INC1_H 2 | __FILE__ __LINE__ 3 | 4 | #define EXCLUDE_INC1_H 5 | #include "inc1.h" 6 | #undef EXCLUDE_INC1_H 7 | 8 | #define INC1_H_INCLUDED 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /test/scpp/test00.in: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mras0/scc/8cc3ebe4d8940bd60c515a1994b7d5273872585b/test/scpp/test00.in -------------------------------------------------------------------------------- /test/scpp/test00.ref: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mras0/scc/8cc3ebe4d8940bd60c515a1994b7d5273872585b/test/scpp/test00.ref -------------------------------------------------------------------------------- /test/scpp/test01.in: -------------------------------------------------------------------------------- 1 | #define X 42 2 | X X 3 | -------------------------------------------------------------------------------- /test/scpp/test01.ref: -------------------------------------------------------------------------------- 1 | 42 42 -------------------------------------------------------------------------------- /test/scpp/test02.in: -------------------------------------------------------------------------------- 1 | #define X 42 2 | #define Y X+1 3 | Y*2 4 | -------------------------------------------------------------------------------- /test/scpp/test02.ref: -------------------------------------------------------------------------------- 1 | 42+1*2 -------------------------------------------------------------------------------- /test/scpp/test03.in: -------------------------------------------------------------------------------- 1 | #if 1 2 | 42 3 | #endif 4 | -------------------------------------------------------------------------------- /test/scpp/test03.ref: -------------------------------------------------------------------------------- 1 | 42 -------------------------------------------------------------------------------- /test/scpp/test04.in: -------------------------------------------------------------------------------- 1 | #if 0 2 | 42 3 | #endif 4 | -------------------------------------------------------------------------------- /test/scpp/test04.ref: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mras0/scc/8cc3ebe4d8940bd60c515a1994b7d5273872585b/test/scpp/test04.ref -------------------------------------------------------------------------------- /test/scpp/test05.in: -------------------------------------------------------------------------------- 1 | #if 0 2 | 42 3 | #else 4 | 31 5 | #endif 6 | -------------------------------------------------------------------------------- /test/scpp/test05.ref: -------------------------------------------------------------------------------- 1 | 31 -------------------------------------------------------------------------------- /test/scpp/test06.in: -------------------------------------------------------------------------------- 1 | #define X 42 2 | #define Y (X + 1) 3 | Y + 2 4 | #if !Y 5 | 42 6 | #else 7 | 31 8 | #endif 9 | -------------------------------------------------------------------------------- /test/scpp/test06.ref: -------------------------------------------------------------------------------- 1 | (42 + 1) + 2 2 | 31 -------------------------------------------------------------------------------- /test/scpp/test07.in: -------------------------------------------------------------------------------- 1 | //10 2 | 42 3 | -------------------------------------------------------------------------------- /test/scpp/test07.ref: -------------------------------------------------------------------------------- 1 | 2 | 42 -------------------------------------------------------------------------------- /test/scpp/test08.in: -------------------------------------------------------------------------------- 1 | /*****/42 2 | /*****/ 3 | -------------------------------------------------------------------------------- /test/scpp/test08.ref: -------------------------------------------------------------------------------- 1 | 42 -------------------------------------------------------------------------------- /test/scpp/test09.in: -------------------------------------------------------------------------------- 1 | 2 2 | + 3 3 | -------------------------------------------------------------------------------- /test/scpp/test09.ref: -------------------------------------------------------------------------------- 1 | 2 2 | + 3 -------------------------------------------------------------------------------- /test/scpp/test10.in: -------------------------------------------------------------------------------- 1 | #define A 2 2 | #define B ((A)+1) 3 | #define X A + B 4 | X 5 | -------------------------------------------------------------------------------- /test/scpp/test10.ref: -------------------------------------------------------------------------------- 1 | 2 + ((2)+1) -------------------------------------------------------------------------------- /test/scpp/test11.in: -------------------------------------------------------------------------------- 1 | #define x (4 + y) 2 | #define y (2 * x) 3 | x 4 | y 5 | -------------------------------------------------------------------------------- /test/scpp/test11.ref: -------------------------------------------------------------------------------- 1 | (4 + (2 * x)) 2 | (2 * (4 + y)) -------------------------------------------------------------------------------- /test/scpp/test12.in: -------------------------------------------------------------------------------- 1 | #define X 42 2 | #define Y (X+X) 3 | #if Y == 84 4 | 1 5 | #else 6 | 0 7 | #endif 8 | -------------------------------------------------------------------------------- /test/scpp/test12.ref: -------------------------------------------------------------------------------- 1 | 1 -------------------------------------------------------------------------------- /test/scpp/test13.in: -------------------------------------------------------------------------------- 1 | #if X 2 | 42 3 | #else 4 | 60 5 | #endif 6 | -------------------------------------------------------------------------------- /test/scpp/test13.ref: -------------------------------------------------------------------------------- 1 | 60 -------------------------------------------------------------------------------- /test/scpp/test14.in: -------------------------------------------------------------------------------- 1 | #define X 1 2 | #if defined(X) 3 | 42 4 | #endif -------------------------------------------------------------------------------- /test/scpp/test14.ref: -------------------------------------------------------------------------------- 1 | 42 -------------------------------------------------------------------------------- /test/scpp/test15.in: -------------------------------------------------------------------------------- 1 | #if defined(X) 2 | 42 3 | #endif -------------------------------------------------------------------------------- /test/scpp/test15.ref: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mras0/scc/8cc3ebe4d8940bd60c515a1994b7d5273872585b/test/scpp/test15.ref -------------------------------------------------------------------------------- /test/scpp/test16.in: -------------------------------------------------------------------------------- 1 | #define X 2 | #ifdef X 3 | 1 4 | #endif 5 | #ifdef Y 6 | 2 7 | #endif 8 | #ifndef Y 9 | 3 10 | #endif -------------------------------------------------------------------------------- /test/scpp/test16.ref: -------------------------------------------------------------------------------- 1 | 1 2 | 3 -------------------------------------------------------------------------------- /test/scpp/test17.in: -------------------------------------------------------------------------------- 1 | #if X 2 | 42 3 | #endif -------------------------------------------------------------------------------- /test/scpp/test17.ref: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mras0/scc/8cc3ebe4d8940bd60c515a1994b7d5273872585b/test/scpp/test17.ref -------------------------------------------------------------------------------- /test/scpp/test18.in: -------------------------------------------------------------------------------- 1 | #if 0x2A==42 2 | 1 3 | #endif -------------------------------------------------------------------------------- /test/scpp/test18.ref: -------------------------------------------------------------------------------- 1 | 1 -------------------------------------------------------------------------------- /test/scpp/test19.in: -------------------------------------------------------------------------------- 1 | #if 2==(1?2:3) 2 | 1 3 | #endif -------------------------------------------------------------------------------- /test/scpp/test19.ref: -------------------------------------------------------------------------------- 1 | 1 -------------------------------------------------------------------------------- /test/scpp/test20.in: -------------------------------------------------------------------------------- 1 | #define Y 42 2 | #if X 3 | #elif Y 4 | 50 5 | #else 6 | 2 7 | #endif -------------------------------------------------------------------------------- /test/scpp/test20.ref: -------------------------------------------------------------------------------- 1 | 50 -------------------------------------------------------------------------------- /test/scpp/test21.in: -------------------------------------------------------------------------------- 1 | #if 0 2 | 000 3 | #if 1 4 | 111 5 | #elif 1 6 | 222 7 | #endif 8 | #elif 0 9 | 333 10 | #elif 1 11 | #if 0 12 | 444 13 | #else 14 | 123 15 | #endif 16 | #else 17 | 666 18 | #endif 19 | -------------------------------------------------------------------------------- /test/scpp/test21.ref: -------------------------------------------------------------------------------- 1 | 123 -------------------------------------------------------------------------------- /test/scpp/test22.in: -------------------------------------------------------------------------------- 1 | #define C defined(X) 2 | #if C 3 | 0 4 | #endif 5 | #define X 6 | #if C 7 | 1 8 | #endif 9 | -------------------------------------------------------------------------------- /test/scpp/test22.ref: -------------------------------------------------------------------------------- 1 | 1 -------------------------------------------------------------------------------- /test/scpp/test23.in: -------------------------------------------------------------------------------- 1 | #define W 2 | #define V 0x0502 3 | #define X (V >= 0x0502 || !defined (W)) 4 | #if X && !defined (__C) 5 | 1 6 | #endif 7 | -------------------------------------------------------------------------------- /test/scpp/test23.ref: -------------------------------------------------------------------------------- 1 | 1 -------------------------------------------------------------------------------- /test/scpp/test24.in: -------------------------------------------------------------------------------- 1 | #undef XXX 2 | #define X 123 3 | #ifdef X 4 | X 5 | #endif 6 | #undef X 7 | #ifdef X 8 | 1234 9 | #endif 10 | #if X+1==1 11 | 42 12 | #endif 13 | -------------------------------------------------------------------------------- /test/scpp/test24.ref: -------------------------------------------------------------------------------- 1 | 123 2 | 42 -------------------------------------------------------------------------------- /test/scpp/test25.in: -------------------------------------------------------------------------------- 1 | #define X 123 2 | X 3 | #undef X 4 | #define X 42 5 | X 6 | -------------------------------------------------------------------------------- /test/scpp/test25.ref: -------------------------------------------------------------------------------- 1 | 123 2 | 42 -------------------------------------------------------------------------------- /test/scpp/test26.in: -------------------------------------------------------------------------------- 1 | #define X() (1+2) 2 | X + 1 3 | X () 4 | #define Y() 5 | Y() -------------------------------------------------------------------------------- /test/scpp/test26.ref: -------------------------------------------------------------------------------- 1 | X + 1 2 | (1+2) -------------------------------------------------------------------------------- /test/scpp/test27.in: -------------------------------------------------------------------------------- 1 | #define F(x,y,z) x+y+z 2 | F( 1 , 3 | 2 , 3 4 | ) 5 | -------------------------------------------------------------------------------- /test/scpp/test27.ref: -------------------------------------------------------------------------------- 1 | 1+2+3 -------------------------------------------------------------------------------- /test/scpp/test28.in: -------------------------------------------------------------------------------- 1 | #define A(x) (x)+(x) 2 | A(3* 4) 3 | -------------------------------------------------------------------------------- /test/scpp/test28.ref: -------------------------------------------------------------------------------- 1 | (3* 4)+(3* 4) -------------------------------------------------------------------------------- /test/scpp/test29.in: -------------------------------------------------------------------------------- 1 | #define X 42 2 | #define Y 60 3 | #define F(a,b) a+b 4 | F(X, Y) 5 | -------------------------------------------------------------------------------- /test/scpp/test29.ref: -------------------------------------------------------------------------------- 1 | 42+60 -------------------------------------------------------------------------------- /test/scpp/test30.in: -------------------------------------------------------------------------------- 1 | #define X(a) a+1 2 | #define Y(b) b-2 3 | X(Y(42)) 4 | -------------------------------------------------------------------------------- /test/scpp/test30.ref: -------------------------------------------------------------------------------- 1 | 42-2+1 -------------------------------------------------------------------------------- /test/scpp/test31.in: -------------------------------------------------------------------------------- 1 | #define ADD1(x) ((x)+1) 2 | #define MUL2(x) ((x)*2) 3 | ADD1(MUL2(42)) -------------------------------------------------------------------------------- /test/scpp/test31.ref: -------------------------------------------------------------------------------- 1 | ((((42)*2))+1) -------------------------------------------------------------------------------- /test/scpp/test32.in: -------------------------------------------------------------------------------- 1 | #define ADD1(x) ((x)+1) 2 | #define MUL(x,y) ((x)*(y)) 3 | MUL(ADD1(2),ADD1(3)) 4 | -------------------------------------------------------------------------------- /test/scpp/test32.ref: -------------------------------------------------------------------------------- 1 | ((((2)+1))*(((3)+1))) -------------------------------------------------------------------------------- /test/scpp/test33.in: -------------------------------------------------------------------------------- 1 | #define F(x) x+1 2 | #define Z F(42) 3 | Z -------------------------------------------------------------------------------- /test/scpp/test33.ref: -------------------------------------------------------------------------------- 1 | 42+1 -------------------------------------------------------------------------------- /test/scpp/test34.in: -------------------------------------------------------------------------------- 1 | \ 123 \ 2 | -------------------------------------------------------------------------------- /test/scpp/test34.ref: -------------------------------------------------------------------------------- 1 | \ 123 \ -------------------------------------------------------------------------------- /test/scpp/test35.in: -------------------------------------------------------------------------------- 1 | #define X(a,b) a\ 2 | +b 3 | X(1,2) -------------------------------------------------------------------------------- /test/scpp/test35.ref: -------------------------------------------------------------------------------- 1 | 1+2 -------------------------------------------------------------------------------- /test/scpp/test36.in: -------------------------------------------------------------------------------- 1 | #define assert(c) do {\ 2 | if(!(c)) Fail(); }\ 3 | while (0) 4 | assert(1-1) 5 | -------------------------------------------------------------------------------- /test/scpp/test36.ref: -------------------------------------------------------------------------------- 1 | do {if(!(1-1)) Fail(); } while (0) -------------------------------------------------------------------------------- /test/scpp/test37.in: -------------------------------------------------------------------------------- 1 | #define foo (4 + foo) 2 | foo -------------------------------------------------------------------------------- /test/scpp/test37.ref: -------------------------------------------------------------------------------- 1 | (4 + foo) -------------------------------------------------------------------------------- /test/scpp/test38.in: -------------------------------------------------------------------------------- 1 | #define x (4+y) 2 | #define y (2*x) 3 | x 4 | y 5 | -------------------------------------------------------------------------------- /test/scpp/test38.ref: -------------------------------------------------------------------------------- 1 | (4+(2*x)) 2 | (2*(4+y)) -------------------------------------------------------------------------------- /test/scpp/test39.in: -------------------------------------------------------------------------------- 1 | #define twice(x) (2*(x)) 2 | #define call_with_1(x) x(1) 3 | call_with_1 (twice) 4 | -------------------------------------------------------------------------------- /test/scpp/test39.ref: -------------------------------------------------------------------------------- 1 | (2*(1)) -------------------------------------------------------------------------------- /test/scpp/test40.in: -------------------------------------------------------------------------------- 1 | #define strange(file) fprintf (file, "%s %d", 2 | strange(stderr) p, 35) -------------------------------------------------------------------------------- /test/scpp/test40.ref: -------------------------------------------------------------------------------- 1 | fprintf (stderr, "%s %d", p, 35) -------------------------------------------------------------------------------- /test/scpp/test41.in: -------------------------------------------------------------------------------- 1 | #define min(X, Y) ((X) < (Y) ? (X) : (Y)) 2 | min(a,b); 3 | min(1,2); 4 | min(a+28, *p); 5 | -------------------------------------------------------------------------------- /test/scpp/test41.ref: -------------------------------------------------------------------------------- 1 | ((a) < (b) ? (a) : (b)); 2 | ((1) < (2) ? (1) : (2)); 3 | ((a+28) < (*p) ? (a+28) : (*p)); -------------------------------------------------------------------------------- /test/scpp/test42.in: -------------------------------------------------------------------------------- 1 | #define min(X, Y) ((X) < (Y) ? (X) : (Y)) 2 | min(, b) 3 | min(a, ) 4 | min(,) 5 | min((,),) 6 | -------------------------------------------------------------------------------- /test/scpp/test42.ref: -------------------------------------------------------------------------------- 1 | (() < (b) ? () : (b)) 2 | ((a) < () ? (a) : ()) 3 | (() < () ? () : ()) 4 | (((,)) < () ? ((,)) : ()) -------------------------------------------------------------------------------- /test/scpp/test43.in: -------------------------------------------------------------------------------- 1 | #define foo(x) x, "x" 2 | foo(bar) -------------------------------------------------------------------------------- /test/scpp/test43.ref: -------------------------------------------------------------------------------- 1 | bar, "x" -------------------------------------------------------------------------------- /test/scpp/test44.in: -------------------------------------------------------------------------------- 1 | #define indirect(x) foo(x) 2 | #define foo(x) x 3 | indirect(42 + x) 4 | -------------------------------------------------------------------------------- /test/scpp/test44.ref: -------------------------------------------------------------------------------- 1 | 42 + x -------------------------------------------------------------------------------- /test/scpp/test45.in: -------------------------------------------------------------------------------- 1 | #define xstr(s) str(s) 2 | #define str(s) #s 3 | #define foo 4 4 | str(foo) 5 | xstr(foo) 6 | -------------------------------------------------------------------------------- /test/scpp/test45.ref: -------------------------------------------------------------------------------- 1 | "foo" 2 | "4" -------------------------------------------------------------------------------- /test/scpp/test46.in: -------------------------------------------------------------------------------- 1 | #define str(s) #s " test" 2 | str(hello world 42) 3 | -------------------------------------------------------------------------------- /test/scpp/test46.ref: -------------------------------------------------------------------------------- 1 | "hello world 42" " test" -------------------------------------------------------------------------------- /test/scpp/test47.in: -------------------------------------------------------------------------------- 1 | #ifdef __SCC__ 2 | 42 3 | #endif 4 | __SCC__ 5 | -------------------------------------------------------------------------------- /test/scpp/test47.ref: -------------------------------------------------------------------------------- 1 | 42 2 | 1 -------------------------------------------------------------------------------- /test/scpp/test48.in: -------------------------------------------------------------------------------- 1 | __LINE__ 2 | __LINE__ 3 | 4 | __LINE__ 5 | -------------------------------------------------------------------------------- /test/scpp/test48.ref: -------------------------------------------------------------------------------- 1 | 1 2 | 2 3 | 4 -------------------------------------------------------------------------------- /test/scpp/test49.in: -------------------------------------------------------------------------------- 1 | __FILE__ 2 | -------------------------------------------------------------------------------- /test/scpp/test49.ref: -------------------------------------------------------------------------------- 1 | "test49.in" -------------------------------------------------------------------------------- /test/scpp/test50.in: -------------------------------------------------------------------------------- 1 | #define X(N) foo_ ## N ## _blah 2 | X(test) -------------------------------------------------------------------------------- /test/scpp/test50.ref: -------------------------------------------------------------------------------- 1 | foo_test_blah -------------------------------------------------------------------------------- /test/scpp/test51.in: -------------------------------------------------------------------------------- 1 | #define X(a) a ## _bar 2 | X(foo) -------------------------------------------------------------------------------- /test/scpp/test51.ref: -------------------------------------------------------------------------------- 1 | foo_bar -------------------------------------------------------------------------------- /test/scpp/test52.in: -------------------------------------------------------------------------------- 1 | #define X(a) a ## 1 2 | X(foo) -------------------------------------------------------------------------------- /test/scpp/test52.ref: -------------------------------------------------------------------------------- 1 | foo1 -------------------------------------------------------------------------------- /test/scpp/test53.in: -------------------------------------------------------------------------------- 1 | #define M(a) x ## a ## 1 2 | M(x) -------------------------------------------------------------------------------- /test/scpp/test53.ref: -------------------------------------------------------------------------------- 1 | xx1 -------------------------------------------------------------------------------- /test/scpp/test54.in: -------------------------------------------------------------------------------- 1 | #define P(a,b) a##b 2 | P(1 , 2) -------------------------------------------------------------------------------- /test/scpp/test54.ref: -------------------------------------------------------------------------------- 1 | 12 -------------------------------------------------------------------------------- /test/scpp/test55.in: -------------------------------------------------------------------------------- 1 | #define hash_hash # ## # 2 | #define mkstr(a) # a 3 | #define in_between(a) mkstr(a) 4 | #define join(c, d) in_between(c hash_hash d) 5 | char p[] = join(x, y); 6 | -------------------------------------------------------------------------------- /test/scpp/test55.ref: -------------------------------------------------------------------------------- 1 | char p[] = "x ## y"; -------------------------------------------------------------------------------- /test/scpp/test56.in: -------------------------------------------------------------------------------- 1 | #define DEF(id, str) id, 2 | #define DEF_BWL(x) \ 3 | DEF(TOK_ASM_ ## x ## b, #x "b") \ 4 | DEF(TOK_ASM_ ## x ## w, #x "w") \ 5 | DEF(TOK_ASM_ ## x ## l, #x "l") \ 6 | DEF(TOK_ASM_ ## x, #x) 7 | #define DEF_BWLX DEF_BWL 8 | DEF_BWLX(mov) 9 | -------------------------------------------------------------------------------- /test/scpp/test56.ref: -------------------------------------------------------------------------------- 1 | TOK_ASM_movb, TOK_ASM_movw, TOK_ASM_movl, TOK_ASM_mov, -------------------------------------------------------------------------------- /test/scpp/test57.in: -------------------------------------------------------------------------------- 1 | # define ELFW(type) ELF##32##_##type 2 | #define ELF32_ST_BIND(val) (((unsigned char) (val)) >> 4) 3 | #define ELF32_ST_TYPE(val) ((val) & 0xf) 4 | #define ELF32_ST_INFO(bind, type) (((bind) << 4) + ((type) & 0xf)) 5 | ELFW(ST_INFO)(sym_bind, ELFW(ST_TYPE)(esym->st_info)); 6 | -------------------------------------------------------------------------------- /test/scpp/test57.ref: -------------------------------------------------------------------------------- 1 | (((sym_bind) << 4) + ((((esym->st_info) & 0xf)) & 0xf)); -------------------------------------------------------------------------------- /test/scpp/test58.in: -------------------------------------------------------------------------------- 1 | __FILE__ __LINE__ 2 | 3 | #include 4 | 5 | __FILE__ __LINE__ 6 | 7 | #ifdef EXCLUDE_INC1_H 8 | 42 9 | #endif 10 | 11 | #ifdef INC1_H_INCLUDED 12 | 60 13 | #endif 14 | -------------------------------------------------------------------------------- /test/scpp/test58.ref: -------------------------------------------------------------------------------- 1 | "test58.in" 1 2 | "inc1.h" 2 3 | "test58.in" 5 4 | 60 -------------------------------------------------------------------------------- /test/scpp/test59.in: -------------------------------------------------------------------------------- 1 | #if 0 2 | #error Should not fire 3 | #endif 4 | #pragma foo 5 | #line 123 6 | -------------------------------------------------------------------------------- /test/scpp/test59.ref: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mras0/scc/8cc3ebe4d8940bd60c515a1994b7d5273872585b/test/scpp/test59.ref -------------------------------------------------------------------------------- /test/scpp/test60.in: -------------------------------------------------------------------------------- 1 | #if 0 2 | ijqpisdjpsqidjsjd piqjp ' 3 | " 4 | #error shouldn't fire! \ 5 | #endif 6 | -------------------------------------------------------------------------------- /test/scpp/test60.ref: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mras0/scc/8cc3ebe4d8940bd60c515a1994b7d5273872585b/test/scpp/test60.ref -------------------------------------------------------------------------------- /test/scpp/test61.in: -------------------------------------------------------------------------------- 1 | #define DEF_ASM(x) DEF(TOK_ASM_ ## x, #x) 2 | 3 | #define DEF_ASMTEST(x,suffix) \ 4 | DEF_ASM(x ## o ## suffix) \ 5 | DEF_ASM(x ## no ## suffix) 6 | 7 | #define DEF(id, str) ,id 8 | 9 | DEF_ASMTEST(j,) 10 | DEF_ASMTEST(set,) 11 | DEF_ASMTEST(set,b) 12 | DEF_ASMTEST(cmov,) 13 | -------------------------------------------------------------------------------- /test/scpp/test61.ref: -------------------------------------------------------------------------------- 1 | 2 | ,TOK_ASM_jo ,TOK_ASM_jno 3 | ,TOK_ASM_seto ,TOK_ASM_setno 4 | ,TOK_ASM_setob ,TOK_ASM_setnob 5 | ,TOK_ASM_cmovo ,TOK_ASM_cmovno -------------------------------------------------------------------------------- /test/scpp/test62.in: -------------------------------------------------------------------------------- 1 | #define XXX 123 2 | 3 | #if 0 4 | #undef XXX 5 | XXX 6 | #endif 7 | YYY 8 | XXX 9 | -------------------------------------------------------------------------------- /test/scpp/test62.ref: -------------------------------------------------------------------------------- 1 | 2 | YYY 3 | 123 -------------------------------------------------------------------------------- /test/scpp/test63.in: -------------------------------------------------------------------------------- 1 | #if 0 2 | #invalid 3 | #endif 4 | abc 5 | -------------------------------------------------------------------------------- /test/scpp/test63.ref: -------------------------------------------------------------------------------- 1 | abc -------------------------------------------------------------------------------- /test/sim86/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_custom_command( 2 | OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/test.com" 3 | COMMAND $ "${CMAKE_CURRENT_SOURCE_DIR}/test.asm" "${CMAKE_CURRENT_BINARY_DIR}/test.com" 4 | DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/test.asm" 5 | ) 6 | 7 | add_custom_target(test_sim86 8 | COMMAND $ test.com 9 | # Check simulated self-compilation 10 | COMMAND ${CMAKE_COMMAND} -E copy "${PROJECT_SOURCE_DIR}/scc.c" . 11 | COMMAND ${CMAKE_COMMAND} -E copy "${PROJECT_BINARY_DIR}/scc.com" . 12 | COMMAND $ scc.com scc.c sc1.com 13 | COMMAND ${DIFF} sc1.com scc.com 14 | COMMAND $ sc1.com scc.c sc2.com 15 | COMMAND ${DIFF} sc2.com scc.com 16 | DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/test.com" scc_com 17 | WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} 18 | COMMENT Testing sim86 19 | ) 20 | 21 | 22 | -------------------------------------------------------------------------------- /test/sim86/test.asm: -------------------------------------------------------------------------------- 1 | org 0x100 2 | 3 | mov ax, 123 4 | cmp ax, 32 5 | jz ERR 6 | jng ERR 7 | jl ERR 8 | 9 | mov ax, -123 10 | cmp ax, 32 11 | jz ERR 12 | jg ERR 13 | jnl ERR 14 | 15 | mov ax, 42 16 | cmp ax, 42 17 | jnz ERR 18 | jg ERR 19 | jl ERR 20 | 21 | mov ax, 0x8000 22 | cmp ax, 0x7fff 23 | jz ERR 24 | jg ERR 25 | jnl ERR 26 | 27 | mov ax, 0x7fff 28 | cmp ax, 0x8000 29 | jz ERR 30 | jng ERR 31 | jl ERR 32 | 33 | mov ax, 0x1111 34 | cbw 35 | cmp ax, 0x0011 36 | jne ERR 37 | 38 | mov ax, 0x1180 39 | cbw 40 | cmp ax, 0xFF80 41 | jne ERR 42 | 43 | mov ax, 0xFFC1 44 | push ax 45 | cbw 46 | pop cx 47 | cmp ax, cx 48 | jne ERR 49 | jg ERR 50 | jl ERR 51 | 52 | mov ax, 0x0020 53 | cmp ax, 0x0020 54 | jnz ERR 55 | jg ERR 56 | jl ERR 57 | jng L1 58 | jmp short ERR 59 | L1: 60 | jnl L2 61 | jmp short ERR 62 | L2: 63 | 64 | mov ax, -42 65 | xor ah, ah 66 | jnz ERR 67 | cmp ax, 214 68 | jne ERR 69 | mov ah, 2 70 | cmp ax, 0x2D6 71 | 72 | mov cx, 0x1234 73 | sub ch, 0x14 74 | jz ERR 75 | cmp cx, 0xFE34 76 | jne ERR 77 | 78 | mov cx, 0xABCD 79 | shr cx, 1 80 | jnc ERR 81 | jz ERR 82 | cmp cx, 0x55E6 83 | jne ERR 84 | 85 | mov ax, 2 86 | shr ax, 1 87 | jz ERR 88 | jc ERR 89 | cmp ax, 1 90 | jne ERR 91 | shr ax, 1 92 | jnz ERR 93 | jnc ERR 94 | cmp ax, 0 95 | jne ERR 96 | 97 | mov cl, 2 98 | mov ax, 2 99 | shr ax, cl 100 | jnc ERR 101 | jnz ERR 102 | cmp ax, 0 103 | jne ERR 104 | 105 | mov ax, 0x4c00 106 | int 0x21 107 | ERR: 108 | mov ah, 2 109 | mov dl, '!' 110 | int 0x21 111 | 112 | mov ax, 0x4c01 113 | int 0x21 114 | --------------------------------------------------------------------------------