commit 59ad004c960a2ca5117ff4efd68422f29bc14ed9 Author: Zsolt Tasnadi Date: Sat Jul 18 20:34:55 2026 +0200 initial commit diff --git a/.clangd b/.clangd new file mode 100644 index 0000000..1d95253 --- /dev/null +++ b/.clangd @@ -0,0 +1,4 @@ +# IDE (clangd) segédkonfig: az Oscar64 vendorolt headerjeit így találja +# meg a nyelvi szerver; a tényleges fordítást az oscar64 végzi (Makefile). +CompileFlags: + Add: [-Ioscar64/include, -I../oscar64/include, -Wno-unknown-pragmas] diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..dfe0770 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto diff --git a/.github/workflows/check.yaml b/.github/workflows/check.yaml new file mode 100644 index 0000000..0d9a564 --- /dev/null +++ b/.github/workflows/check.yaml @@ -0,0 +1,61 @@ +name: Run checks + +on: + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + check: + strategy: + fail-fast: false + matrix: + os: + - macos-latest + - ubuntu-24.04-arm + - ubuntu-latest + - windows-latest + + runs-on: "${{ matrix.os }}" + + defaults: + run: + shell: bash + + env: + SCCACHE_GHA_ENABLED: "true" + + steps: + - uses: actions/checkout@v5 + + - name: Start sccache + uses: mozilla-actions/sccache-action@v0.0.9 + + - name: Set CXX var + run: | + case "$RUNNER_OS" in + "Linux") echo "CXX=g++" >> "$GITHUB_ENV" ;; + "macOS") echo "CXX=clang++" >> "$GITHUB_ENV" ;; + "Windows") echo "CXX=g++" >> "$GITHUB_ENV" ;; + esac + + - name: Determine number of cores + run: | + CORES=2 + case "$RUNNER_OS" in + "Linux") CORES="$(nproc)" ;; + "macOS") CORES="$(sysctl -n hw.ncpu)" ;; + "Windows") CORES="$NUMBER_OF_PROCESSORS" ;; + *) echo "Unknown OS, using default." ;; + esac + echo "NUMBER_OF_CORES=$CORES" >> "$GITHUB_ENV" + echo "Using $CORES cores" + + - name: make compiler + run: make -C make -j "$NUMBER_OF_CORES" compiler CXX="sccache ${CXX}" + + - name: make check + run: make -C make -j "$NUMBER_OF_CORES" check diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ec565c3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +# a demo fordítási kimenete (demo.prg + generált .asm/.map/.lbl/.int) +build/ + +# a vendorolt Oscar64 lokálisan épített binárisa és köztes objektumai +# (a Makefile első futáskor újraépíti: make -C oscar64/make compiler) +oscar64/bin/ +oscar64/build/ + +# szerkesztő / OS szemét +.DS_Store +*.swp diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..7df4a12 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "cmake.ignoreCMakeListsMissing": true +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..c354884 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,45 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "deps", + "type": "shell", + "command": "make deps", + "problemMatcher": [] + }, + { + "label": "build", + "type": "shell", + "command": "make build", + "group": { + "kind": "build", + "isDefault": true + }, + "problemMatcher": [] + }, + { + "label": "run", + "type": "shell", + "command": "make run", + "problemMatcher": [] + }, + { + "label": "clean", + "type": "shell", + "command": "make clean", + "problemMatcher": [] + }, + { + "label": "rebuild", + "type": "shell", + "command": "make rebuild", + "problemMatcher": [] + }, + { + "label": "rebuildrun", + "type": "shell", + "command": "make rebuildrun", + "problemMatcher": [] + } + ] +} diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..3bd6dd7 --- /dev/null +++ b/Makefile @@ -0,0 +1,41 @@ +NAME := demo +OSCAR64 := oscar64/bin/oscar64 +SRC := src/main.c +SRCS := $(wildcard src/*.c src/*.h) +PRG := build/$(NAME).prg +EMU := x64sc + +UNAME_S := $(shell uname -s) + +all: build + +deps: +ifeq ($(UNAME_S),Darwin) + @command -v brew >/dev/null 2>&1 || { echo "Homebrew kell ehhez. Telepitsd: https://brew.sh"; exit 1; } + brew install vice +else + @echo "A 'deps' target csak macOS-en (Darwin) automatikus. Detektalt OS: $(UNAME_S)" + @echo "Telepitsd kezzel a VICE emulatort (x64sc). Az Oscar64 vendorolt, magatol epul." + @exit 1 +endif + +$(OSCAR64): + $(MAKE) -C oscar64/make compiler + +build: $(PRG) + +$(PRG): $(SRCS) $(OSCAR64) + @mkdir -p build + $(OSCAR64) -n -O2 -o=$(PRG) $(SRC) + +run: $(PRG) + $(EMU) $(PRG) + +clean: + rm -rf build + +rebuild: clean build + +rebuildrun: clean build run + +.PHONY: all deps build run clean rebuild rebuildrun diff --git a/README.md b/README.md new file mode 100644 index 0000000..fcb87a8 --- /dev/null +++ b/README.md @@ -0,0 +1,138 @@ +# C64 Demo — Teletype Games slide show (Oscar64) + +A Commodore 64 slide show, C-ben, az [Oscar64](https://github.com/drmortalwombat/oscar64) +fordítóval. Az eredetileg 6502 assemblyben (ACME) írt [c64-demo](../c64-demo) +portja: öt slide váltakozik automatikusan, slide-onkénti átmenetekkel feketén +keresztül, és a show billentyűzetről is vezérelhető. + +## A slide-ok + +1. **TTG logó + credits scroller** — óriás TTG betűk 8×8-as bitmapekből, arany + színfutással, pulzáló **TELETYPE GAMES** felirat, sima pixel-scroller a + stáblistával. +2. **Az első játékunk** — *DEFINITELY NOT AN IMPOSTOR* nagy 3×5-ös + blokkbetűkkel, arany wash-sal, „introduced on TIC-80". +3. **A második játékunk** — *IS COMING* nagybetűkkel, vetett árnyékkal; + a raszterbárok csak a kereten futnak, így a szöveg olvasható marad. +4. **Kapcsolat** — pulzáló *CONTACT* cím, web / BBS / IRC sorok, 12 cellás + háromrétegű parallax csillagmező és hat hardveres sprite (négyágú csillagok) + szinusz-pályán a szöveg mögött. +5. **Az assembly vallomás** — sorkizárt szöveg arról, hogy ez a slide show egy + kis AI-segítséggel készült; a törzs élőben gépelődik be (karakterenként egy + frame, fehér blokk-kurzorral), a záró sor a gépelés végén jelenik meg és pulzál. + +Minden slide 8 másodpercig látszik (`SLIDE_TIME` a `src/defs.h`-ban, PAL +frame-ekben); az 5-ös slide 9 másodpercet kap (`S5_TIME`), hogy az írógép végezzen. + +## Vezérlés + +| Billentyű | Funkció | +|-----------|---------| +| `SPACE` | következő slide azonnal | +| `INST/DEL` (backspace) | előző slide | +| `P` | pause — a visszaszámlálás megáll, fehér `P` jelző a jobb felső sarokban; az effektek futnak tovább, SPACE/DEL működik | +| `M` | némítás — a SID zene ki/be | + +A billentyűket közvetlenül a CIA1 mátrixból olvassuk (a KERNAL IRQ le van tiltva). + +## Fordítás és futtatás + +```sh +make build # fordítás -> build/demo.prg (első futáskor a fordítót is megépíti) +make run # futtatás VICE emulátorban (x64sc kell hozzá) +make rebuildrun # tiszta újrafordítás + futtatás +make clean # build mappa törlése +make deps # macOS-en: VICE telepítése Homebrew-val +``` + +Ugyanezek a `.vscode/tasks.json`-ból VS Code taskként is futtathatók +(a `build` az alapértelmezett build task). + +Betöltés igazi vagy emulált C64-en: + +``` +LOAD"*",8,1 +RUN +``` + +## Forrás-elrendezés + +| Fájl | Tartalom | +|------|----------| +| `src/main.c` | főciklus, slide-szekvenszer, billentyűolvasás (SPACE / DEL / P / M), slide-táblák | +| `src/defs.h` | hardvermutatók, színek, elrendezés/időzítés konstansok, screen code charmap | +| `src/transitions.c` | átmenet-motor: show/hide, három átmenettípus (fade, dissolve, wipe), `wait_frame` raszter-szinkron, színsnapshot | +| `src/common.c` | közös rutinok (`clear_screen`, `print_text`, `draw_big`), közös táblák (sor×40, 3×5 blokkfont, wash/pulse/fade rámpák) | +| `src/music.c` | SID zenemotor: háromszólamú pattern-lejátszó (basszus, dob, echo lead) az *Impostor* dallam adataival | +| `src/slide1.c` … `slide5.c` | slide-onként egy fájl: `sN_init` (képernyőrajzolás), `sN_update` (frame-enként egyszer fut) + slide-lokális adatok | + +A fordítási egységeket az Oscar64 `#pragma compile("file.c")` mechanizmusa fűzi +össze: minden header behúzza a hozzá tartozó `.c`-t, ezért a `make`-nek elég a +`src/main.c`-t átadni. + +## Hogyan működik + +### Slide-keretrendszer + +Minden slide egy **init** rutinból (megrajzolja a teljes képernyőt), egy +**update** rutinból (frame-enként egyszer hívódik, pontosan egy frame-et fut) +és egy frame-ben mért időtartamból áll. A `main.c` főciklusa hívja az aktuális +update-et, számolja vissza a frame-eket, és lejáratkor — vagy SPACE/DEL-re +azonnal — a következő slide-ra lép. + +### Időzítés + +Nincs megszakítás: a kód a raszterregisztert (`$D012`, `vic.raster`) pollozza, +és minden képernyőfrissítést az alsó keretben végez (`RAS_BOTTOM`). Az 1-es +slide ezen felül képközépen megbontja a frame-et, hogy csak a scroller sorára +kapcsolja be a vízszintes finomscrollt (`vic.ctrl2`); a 3-as slide a sugarat +üldözve 4 soronként átszínezi a keretet a raszterbárokhoz. + +### Átmenetek + +Minden slide-nak saját be- és kilépő átmenete van (`tin_tbl` / `tout_tbl` a +`main.c`-ben), három típusból: + +- **Fade** — minden color RAM cellát a `fadetbl` rámpán léptet (minden szín → + egy árnyalattal sötétebb; minden lánc legfeljebb 4 lépésben fekete). Az új + slide kikapcsolt képernyővel rajzolódik, színei a `$C000`-es bufferbe + mentődnek és feketére váltanak, majd a fade-in cellánként visszasétál a + mentett színre. +- **Dissolve** — egy 10 bites Galois LFSR (periódus 1023) minden cellát pontosan + egyszer, álvéletlen sorrendben jár be. 25 cella/frame = 40 frame (~0,8 s). +- **Wipe** — oszloponkénti söprés balról jobbra, oszloponként egy frame. + +Minden átmenet rövid fehér-szürke keretvillanással indul. + +### Zene + +A `src/music.c` háromszólamú SID pattern-lejátszó; a dallam az *Impostor* +TIC-80 cart 0-s trackje („room work"), kézzel konvertálva 128 soros +pattern-ráccsá (basszus: 50% pulzus, dob: zaj, echo-kánon: háromszög pluck +3 oktávval feljebb, 2 sorral késleltetve). Egy sor egy bájt szólamonként: +`$00` = semmi, `$01` = key off, `$80|n` = n. hang. A `music_play` frame-enként +egyszer tickel: a főciklusból és a `wait_frame`-ből is, így az átmenetek alatt +sem áll meg. + +## Memóriatérkép + +``` +$0801 .. ... BASIC stub + gépi kód (Oscar64 alapértelmezés) +$0340 .. $037E sprite-alakzat (kazettabuffer, 64-re igazított) +$0400 .. $07E7 képernyő RAM (közvetlen írás) +$C000 .. $C3E7 color RAM snapshot buffer az átmenetekhez +$D000 .. $D02E VIC-II regiszterek (kép, sprite-ok, raszter) +$D400 .. $D418 SID regiszterek (zene) +$D800 .. color RAM +$DC00 / $DC01 CIA1 billentyűmátrix +``` + +## Az eredetihez képest + +- A szövegek a `#pragma charmap(97, 1, 26)` révén fordítási időben + screen code-dá alakulnak, így a C string literálok közvetlenül a képernyőre + írhatók. +- Az önmódosító vektorok és zeropage-mutatók helyett függvénypointerek és + normál C tömbindexelés; a viselkedés (időzítés, effektek, zene) megegyezik. +- A fordítás `-n -O2`-vel történik (teljes natív 6502 kód), hogy a + raszter-pollozó részek elég gyorsak legyenek. diff --git a/oscar64/CMakeLists.txt b/oscar64/CMakeLists.txt new file mode 100644 index 0000000..179e839 --- /dev/null +++ b/oscar64/CMakeLists.txt @@ -0,0 +1,89 @@ +cmake_minimum_required(VERSION 3.15) + +# Project name and version +project(oscar64 VERSION 1.0 LANGUAGES CXX) + +include(GNUInstallDirs) + +# Define the executable +add_executable(oscar64) + + +# Glob all source files in the oscar64/ directory +file(GLOB OSCAR64_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/oscar64/*.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/oscar64/*.h") + +# Add the sources to the target +target_sources(${PROJECT_NAME} PRIVATE ${OSCAR64_SOURCES}) + + +# Add header files +target_include_directories(oscar64 PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} +) + +# Compiler settings based on configuration +set_target_properties(oscar64 PROPERTIES + CXX_STANDARD 17 + CXX_STANDARD_REQUIRED YES + CXX_EXTENSIONS NO +) + +# Set debug and release configurations +target_compile_definitions(oscar64 PRIVATE $<$:_DEBUG;_CONSOLE> $<$:NDEBUG;_CONSOLE>) + +if (WIN32) + target_compile_options(oscar64 PRIVATE $<$:/W3 /WX> $<$:/O2 /W3>) +else () + target_compile_options(oscar64 PRIVATE -Wall) +endif () + +# Post-build steps (only for Release builds) +if(CMAKE_BUILD_TYPE STREQUAL "Release") + add_custom_command(TARGET oscar64 POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy $ ${CMAKE_BINARY_DIR}/bin + ) +endif() + +if (WIN32) + target_link_libraries(oscar64 PRIVATE kernel32 user32 gdi32 winspool comdlg32 advapi32 shell32 ole32 oleaut32 uuid odbc32 odbccp32 version) +elseif (APPLE) + # Add macOS-specific frameworks to replace Windows libraries + target_link_libraries(${PROJECT_NAME} + "-framework Cocoa" # For GUI and application support + "-framework CoreFoundation" # For Core Foundation utilities (similar to advapi32 or shell32) + "-framework CoreGraphics" # For graphics and rendering (similar to gdi32) + "-framework IOKit" # For hardware interaction + "-framework AppKit" # For GUI (equivalent to user32) + ) +elseif (UNIX) + # Add Linux-specific libraries for equivalent functionality + target_link_libraries(${PROJECT_NAME} + pthread # For threading (similar to kernel32) + dl # For dynamic library loading (similar to LoadLibrary) + m # For math library (optional but common on Linux) + X11 # For X Window System (similar to user32 for GUI support) + ) +endif() + + +# Output directories +set_target_properties(oscar64 PROPERTIES + RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/Debug + RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/Release +) + +install(TARGETS oscar64 RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + +# Install oscar64 runtime include files to /include/oscar64/ +# The compiler resolves this at runtime via fallback: basePath + "include/oscar64/crt.h" +install(DIRECTORY include/ + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/oscar64 + FILES_MATCHING + PATTERN "*.h" + PATTERN "*.c" + PATTERN "*.cpp" +) + +install(FILES oscar64.1 + DESTINATION ${CMAKE_INSTALL_MANDIR}/man1 +) diff --git a/oscar64/LICENSE b/oscar64/LICENSE new file mode 100644 index 0000000..e62ec04 --- /dev/null +++ b/oscar64/LICENSE @@ -0,0 +1,674 @@ +GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/oscar64/README.md b/oscar64/README.md new file mode 100644 index 0000000..87b43b8 --- /dev/null +++ b/oscar64/README.md @@ -0,0 +1,49 @@ +# oscar64 + +Oscar64 is a C/C++ cross compiler running on a modern system (such as a Windows PC, Mac or Linux machine) and targets the classic 6502 family of processors. It is mainly focused on Commodore systems such as the C64, PET or VIC20. The compiler supports C99 and many C++ features up to variadic templates and lambda functions. + +The purpose of this compiler is to eliminate the need to write 6502 assembler code to achieve high code density and fast execution speed. It continues to improve with all the games, demos and tools written by it. It supports disk overlays and banked cartridges for larger projects. + +The C64 executes 442 dhrystone V2.2 iteration per second, when compiled with Oscar64 and -O3 (which shows that the ancient dhrystone benchmark is no match to an optimizing compiler). + +[Full reference manual](oscar64.md) +[Additional samples and tutorials](https://github.com/drmortalwombat/OscarTutorials) + +# References + +This is a list of the games written with Oscar64, have a look if you are not convinced that fast paced action games can be written in C/C++ on a C64 (they are all free). + +[Ball and Chain](https://drmortalwombat.itch.io/ball-and-chain) + +[Balls like a Frog](https://drmortalwombat.itch.io/balls-like-a-frog) + +![cores4](https://github.com/user-attachments/assets/15c2bcc7-eafb-44fb-aec2-e5cb101c687b) +[Corescape](https://drmortalwombat.itch.io/corescape) + +[MetalMayhem](https://drmortalwombat.itch.io/metal-mayhem) + +[Mineshaft Gap](https://drmortalwombat.itch.io/mineshaft-gap) + +[Minotrace](https://drmortalwombat.itch.io/minotrace) + +[Missile Defence](https://drmortalwombat.itch.io/missile-defence) + +![cl1b](https://github.com/user-attachments/assets/0d249274-6dab-4284-89e5-ada07fac1986) +[Portal Buster](https://drmortalwombat.itch.io/portal-buster) + +[Roguebot](https://drmortalwombat.itch.io/roguebot) + +[Shallow Domains](https://drmortalwombat.itch.io/shallow-domains) + +[Soiled Iron](https://drmortalwombat.itch.io/soiled-iron) + +[Terminal Walker](https://drmortalwombat.itch.io/terminal-walker) + +[Veggies vs Undead](https://drmortalwombat.itch.io/veggies-vs-undead) + + +# Third Party Projects + +This is a link to third party projects using Oscar64 or providing libraries to use. These links are being provided as a convenience and for informational purposes only. + +[]() diff --git a/oscar64/autotest/ambiguousoverload.c b/oscar64/autotest/ambiguousoverload.c new file mode 100644 index 0000000..5be5ce7 --- /dev/null +++ b/oscar64/autotest/ambiguousoverload.c @@ -0,0 +1,22 @@ +#include "ambiguousoverload.h" + +/** + * Regression test for function pointers in aggregate initialisers. + * + * This setup function must be in a different translation unit from clearView's definition and + * direct call (ambiguousoverload_trigger.c). The bug reused clearView's function declaration as an + * aggregate-initialiser list node and corrupted its overload chain. Parsing the definition in the + * other translation unit then made the otherwise unambiguous direct call fail. + * + * Holder reproduces the original ambiguous-overload diagnostic. The single-member CallbackHolder + * also covers the related initialiser-builder assertion. Both callbacks are invoked to verify that + * their aggregate initialisers contain the correct function address. The pragma in + * ambiguousoverload.h schedules the companion translation unit so this remains one autotest target. + */ +void setup(void) +{ + struct Holder holder = { .value = 1, .callback = clearView }; + struct CallbackHolder callbackHolder = { .callback = clearView }; + holder.callback(); + callbackHolder.callback(); +} diff --git a/oscar64/autotest/ambiguousoverload.h b/oscar64/autotest/ambiguousoverload.h new file mode 100644 index 0000000..ce9b252 --- /dev/null +++ b/oscar64/autotest/ambiguousoverload.h @@ -0,0 +1,20 @@ +#pragma once + +typedef void (*Callback)(void); + +struct Holder +{ + int value; + Callback callback; +}; + +struct CallbackHolder +{ + Callback callback; +}; + +void clearView(void); + +extern int clearViewCalls; + +#pragma compile("ambiguousoverload_trigger.c") diff --git a/oscar64/autotest/ambiguousoverload_trigger.c b/oscar64/autotest/ambiguousoverload_trigger.c new file mode 100644 index 0000000..f85c595 --- /dev/null +++ b/oscar64/autotest/ambiguousoverload_trigger.c @@ -0,0 +1,24 @@ +#include "ambiguousoverload.h" + +#include + + +void setup(void); + +int clearViewCalls = 0; + +// Kept in this companion translation unit to exercise cross-unit merging. +void clearView(void) +{ + clearViewCalls++; +} + +int main(void) +{ + setup(); + assert(clearViewCalls == 2); + // This direct call was incorrectly diagnosed as an ambiguous overload. + clearView(); + assert(clearViewCalls == 3); + return 0; +} diff --git a/oscar64/autotest/andmultest.cpp b/oscar64/autotest/andmultest.cpp new file mode 100644 index 0000000..375eb2e --- /dev/null +++ b/oscar64/autotest/andmultest.cpp @@ -0,0 +1,37 @@ +#include + +template +void checkmul(void) +{ + __striped static const unsigned r[256] = { +#for(i, 256) i * n, + }; + + for(unsigned i=0; i> 1] << 1); + assert((i & ~3) * n == r[i >> 2] << 2); + assert((i & ~7) * n == r[i >> 3] << 3); + assert((i & ~15) * n == r[i >> 4] << 4); + } + for(unsigned i=0; i<256; i++) + { + assert((i & ~0) * n == r[i]); + assert((i & ~1) * n == r[i >> 1] << 1); + assert((i & ~3) * n == r[i >> 2] << 2); + assert((i & ~7) * n == r[i >> 3] << 3); + assert((i & ~15) * n == r[i >> 4] << 4); + } +} + +int main(void) +{ + checkmul<3, 170>(); + checkmul<4, 128>(); + checkmul<5, 204>(); + checkmul<6, 170>(); + checkmul<7, 128>(); + checkmul<8, 64>(); + checkmul<9, 170>(); +} diff --git a/oscar64/autotest/array2dtest.c b/oscar64/autotest/array2dtest.c new file mode 100644 index 0000000..0805831 --- /dev/null +++ b/oscar64/autotest/array2dtest.c @@ -0,0 +1,74 @@ +#include + +char a2[20][10] = { + #for(i, 200) i, +}; + +int sumrow(char y) +{ + int s = 0; + for(char i=0; i<10; i++) + s += a2[y][i]; + return s; +} + +int sumrowp5(char y) +{ + int s = 0; + for(char i=0; i<10; i++) + s += a2[y + 5][i]; + return s; +} + +int sumrowm5(char y) +{ + int s = 0; + for(char i=0; i<10; i++) + s += a2[y - 5][i]; + return s; +} + +char pick(char y, char x) +{ + return a2[y][x]; +} + +char pickp5(char y, char x) +{ + return a2[y + 5][x]; +} + +char pickm5(char y, char x) +{ + return a2[y - 5][x]; +} + +char pickm50(char y, char x) +{ + return a2[y - 50][x]; +} + +char pickp50(signed char y, char x) +{ + return a2[y + 50][x]; +} + + +int main(void) +{ + for(char i=0; i<15; i++) + { + assert(sumrow(i) == sumrowm5(i + 5)); + assert(sumrow(i + 5) == sumrowp5(i)); + + for(char j=0; j<10; j++) + { + assert(pick(i, j) == pickm5(i + 5, j)); + assert(pick(i + 5, j) == pickp5(i, j)); + assert(pick(i, j) == pickm50(i + 50, j)); + assert(pick(i, j) == pickp50(i - 50, j)); + } + } +} + + diff --git a/oscar64/autotest/array2stringinittest.c b/oscar64/autotest/array2stringinittest.c new file mode 100644 index 0000000..0fc5867 --- /dev/null +++ b/oscar64/autotest/array2stringinittest.c @@ -0,0 +1,48 @@ +#include +#include +#include +#include + +char s[25][41] = {"Q QQ QQQQQ QQQQQQQQQQQQQ", + " QQQQQQQQQQQQQQQQQQQQQQQQQQ", + "QQQQQQQQ", + " QQQQQQQQQQQQQQQQQ", + "QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ", + "QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ", + "", + "", + "", + " QQQQQQQQQQQQQQQQQQQQQQQQ", + "QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ", + "QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ", + "QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ", + "QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ", + "QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ", + "QQQQQQQQQ", + "", + "", + "", + "", + "", + "", + "", + "", + " QQQQQQQQQQQQQQ"}; + +int main() +{ + int i=0, sum = 0; + for (unsigned char i = 0; i < 25; i++) + { + int j = 0; + while (s[i][j]) + { + sum += s[i][j] & 3; + j++; + } + } + + assert(sum == 391); + + return 0; +} \ No newline at end of file diff --git a/oscar64/autotest/arrayconstruct.cpp b/oscar64/autotest/arrayconstruct.cpp new file mode 100644 index 0000000..0189fbc --- /dev/null +++ b/oscar64/autotest/arrayconstruct.cpp @@ -0,0 +1,152 @@ +#include + +int t, n, m, k; + +struct C1 +{ + int a; + + C1(void); + ~C1(void); + C1(const C1 & c); + C1 & operator=(const C1 & c); +}; + +struct C2 +{ + C1 nc[10], mc[20]; +}; + +C1::C1(void) +{ + a = 1; + n++; + t++; +} + +C1::C1(const C1 & c) +{ + a = c.a; + k++; + t++; +} + +C1 & C1::operator=(const C1 & c) +{ + a = c.a; + m++; + return *this; +} + +C1::~C1(void) +{ + t--; +} + +void test_local_init(void) +{ + n = 0; + + { + C1 c[10]; + } + + assert(n == 10 && t == 0); +} + +void test_member_init(void) +{ + n = 0; + + { + C2 d; + } + + assert(n == 30 && t == 0); +} + +void test_member_array_init(void) +{ + n = 0; + + { + C2 d[5]; + } + + assert(n == 150 && t == 0); +} + +void test_local_copy(void) +{ + n = 0; + k = 0; + + { + C1 c[10]; + C1 d(c[4]); + } + + assert(n == 10 && k == 1 && t == 0); +} + +void test_member_copy(void) +{ + n = 0; + k = 0; + + { + C2 d; + C2 e(d); + } + + assert(n == 30 && k == 30 && t == 0); +} + +void test_local_assign(void) +{ + n = 0; + k = 0; + m = 0; + + { + C1 c[10]; + C1 d[5]; + + d[4] = c[2]; + } + + assert(n == 15 && k == 0 && m == 1 && t == 0); +} + +void test_member_assign(void) +{ + n = 0; + k = 0; + m = 0; + + { + C2 d; + C2 e; + e = d; + } + + assert(n == 60 && k == 0 && m == 30 && t == 0); +} + + +int main(void) +{ + test_local_init(); + test_member_init(); + + test_member_array_init(); + + test_local_copy(); + test_member_copy(); + + test_local_assign(); + test_member_assign(); + + return 0; +} + diff --git a/oscar64/autotest/arrayindexintrangecheck.c b/oscar64/autotest/arrayindexintrangecheck.c new file mode 100644 index 0000000..946ceba --- /dev/null +++ b/oscar64/autotest/arrayindexintrangecheck.c @@ -0,0 +1,22 @@ +int a[10]; + +int get(int i) +{ + return a[i]; +} + +void put(int i, int x) +{ + a[i] = x; +} + +int main(void) +{ + for(int j=0; j<10; j++) + put(j, j); + int s = -45; + for(int j=0; j<10; j++) + s += get(j); + return s; +} + diff --git a/oscar64/autotest/arrayinittest.c b/oscar64/autotest/arrayinittest.c new file mode 100644 index 0000000..2b1557a --- /dev/null +++ b/oscar64/autotest/arrayinittest.c @@ -0,0 +1,23 @@ +#include +#include + +int fg[15] = {1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610}; + +int main(void) +{ + int fl[15] = {1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610}; + + int sg = 0, sl = 0; + + for(int i=0; i<15; i++) + { + sl += fl[i]; + sg += fg[i]; + } + + assert(sl == 1596); + assert(sg == 1596); + + printf("%d %d\n", sl, sg); + return 0; +} \ No newline at end of file diff --git a/oscar64/autotest/arrayoffsetindex.c b/oscar64/autotest/arrayoffsetindex.c new file mode 100644 index 0000000..d0db177 --- /dev/null +++ b/oscar64/autotest/arrayoffsetindex.c @@ -0,0 +1,20 @@ + + +int a(int * p, int x) +{ + int y = x + 3; + + p[y] = 1; + p[y + 1] = 2; + p[y + 2] = 3; + p[y + 3] = 4; + + return p[y] + p[y + 1] + p[y + 2] + p[y + 3]; +} + +int main(void) +{ + int t[16]; + + return a(t, 4) - 10; +} diff --git a/oscar64/autotest/arraytest.c b/oscar64/autotest/arraytest.c new file mode 100644 index 0000000..dca1b3b --- /dev/null +++ b/oscar64/autotest/arraytest.c @@ -0,0 +1,103 @@ +#include +#include + +int sum(int * a, int s) +{ + int sum = 0; + for(int i=0; i +#include + +float sum(float * a, int s) +{ + float sum = 0; + for(int i=0; i +#include + +int asum(int a, int b) +{ + return __asm + { + clc + lda a + adc b + sta accu + lda a + 1 + adc b + 1 + sta accu + 1 + }; +} + +int bsum(int a, int b) +{ + puts("Hello\n"); + + return __asm + { + clc + lda a + adc b + sta accu + lda a + 1 + adc b + 1 + sta accu + 1 + }; +} + +int b, t[10]; + +int bsome(int x) +{ + return x; +} + + +int qsum(int a, int (* c)(int)) +{ + char n = 0; + b = 0; + for(int i=0; iASMTEST_TARGET + 1 // = #$01 + sta [ASMTEST_ZP + 1] // = $f1 + ldx #1 + lda (ASMTEST_ZP, x) // = (0xf0, 1) + pha + + lda # +#include + +struct X +{ + char t; + long l; +}; + +__striped X xs[16]; +X xf[16]; +char xp; + +__noinline long tt(long n) +{ + return n; +} + +inline auto & tas(void) +{ + return xs[xp].l; +} + +inline auto & taf(void) +{ + return xf[xp].l; +} + +long ts(char n) +{ + return tt(tas()); +} + +long tf(char n) +{ + return tt(taf()); +} + +inline auto bas(void) +{ + return xs[xp].l; +} + +inline auto baf(void) +{ + return xs[xp].l; +} + +long bs(char n) +{ + return tt(bas()); +} + +long bf(char n) +{ + return tt(baf()); +} + +int main(void) +{ + for(char i=0; i<16; i++) + { + xs[i].l = i * i; + xf[i].l = i * i; + } + + for(char i=0; i<16; i++) + { + xp = i; + assert(ts(0) == i * i); + assert(tf(0) == i * i); + assert(bs(0) == i * i); + assert(bf(0) == i * i); + } + + return 0; +} diff --git a/oscar64/autotest/autotest.bat b/oscar64/autotest/autotest.bat new file mode 100644 index 0000000..d1dad50 --- /dev/null +++ b/oscar64/autotest/autotest.bat @@ -0,0 +1,476 @@ +rem @echo off + +@call :test ambiguousoverload.c +@if %errorlevel% neq 0 goto :error + +@call :test array2dtest.c +@if %errorlevel% neq 0 goto :error + +@call :test strparsetest.c +@if %errorlevel% neq 0 goto :error + +@call :test shiftbyteaddconst.cpp +@if %errorlevel% neq 0 goto :error + +@call :test loopunrolltest.cpp +@if %errorlevel% neq 0 goto :error + +@call :test rolrortest.cpp +@if %errorlevel% neq 0 goto :error + +@call :test maskcheck.c +@if %errorlevel% neq 0 goto :error + +@call :test bitfields.cpp +@if %errorlevel% neq 0 goto :error + +@call :testn autorefreturn.cpp +@if %errorlevel% neq 0 goto :error + +@call :testh opp_string.cpp +@if %errorlevel% neq 0 goto :error + +@call :testh opp_hashmap.cpp +@if %errorlevel% neq 0 goto :error + +@call :testh opp_array.cpp +@if %errorlevel% neq 0 goto :error + +@call :testh opp_vector.cpp +@if %errorlevel% neq 0 goto :error + +@call :testh opp_static_vector.cpp +@if %errorlevel% neq 0 goto :error + +@call :testh opp_vector_string.cpp +@if %errorlevel% neq 0 goto :error + +@call :testh opp_string_init.cpp +@if %errorlevel% neq 0 goto :error + +@call :testh opp_optional.cpp +@if %errorlevel% neq 0 goto :error + +@call :testh opp_numeric.cpp +@if %errorlevel% neq 0 goto :error + +@call :testh opp_span.cpp +@if %errorlevel% neq 0 goto :error + +@call :testh opp_streamtest.cpp +@if %errorlevel% neq 0 goto :error + +@call :testh opp_pairtest.cpp +@if %errorlevel% neq 0 goto :error + +@call :testh opp_parts.cpp +@if %errorlevel% neq 0 goto :error + +@call :testh opp_list.cpp +@if %errorlevel% neq 0 goto :error + +@call :testn opp_functional.cpp +@if %errorlevel% neq 0 goto :error + +@call :testh operatoroverload.cpp +@if %errorlevel% neq 0 goto :error + +@call :testh virtualdestruct.cpp +@if %errorlevel% neq 0 goto :error + +@call :testh vcalltest.cpp +@if %errorlevel% neq 0 goto :error + +@call :testh vcalltree.cpp +@if %errorlevel% neq 0 goto :error + +@call :testh constructortest.cpp +@if %errorlevel% neq 0 goto :error + +@call :testn copyconstructor.cpp +@if %errorlevel% neq 0 goto :error + +@call :testh copyassign.cpp +@if %errorlevel% neq 0 goto :error + +@call :testh arrayconstruct.cpp +@if %errorlevel% neq 0 goto :error + +@call :testh stdlibtest.c +@if %errorlevel% neq 0 goto :error + +@call :test mathtest.c +@if %errorlevel% neq 0 goto :error + +@call :test testint16.c +@if %errorlevel% neq 0 goto :error + +@call :test testint32.c +@if %errorlevel% neq 0 goto :error + +@call :test testint16mul.c +@if %errorlevel% neq 0 goto :error + +@call :test testsigned16mul.c +@if %errorlevel% neq 0 goto :error + +@call :test testsigned16div.c +@if %errorlevel% neq 0 goto :error + +@call :test recursiontest.c +@if %errorlevel% neq 0 goto :error + +@call :test copyinitmove.c +@if %errorlevel% neq 0 goto :error + +@call :test fastcalltest.c +@if %errorlevel% neq 0 goto :error + +@call :test strlen.c +@if %errorlevel% neq 0 goto :error + +@call :test strcmptest.c +@if %errorlevel% neq 0 goto :error + +@call :test strcmptest2.c +@if %errorlevel% neq 0 goto :error + +@call :test memmovetest.c +@if %errorlevel% neq 0 goto :error + +@call :test arraytest.c +@if %errorlevel% neq 0 goto :error + +@call :test arraytestfloat.c +@if %errorlevel% neq 0 goto :error + +@call :test optiontest.c +@if %errorlevel% neq 0 goto :error + +@call :test floatcmptest.c +@if %errorlevel% neq 0 goto :error + +@call :test floatmultest.c +@if %errorlevel% neq 0 goto :error + +@call :test floatinttest.c +@if %errorlevel% neq 0 goto :error + +@call :test staticconsttest.c +@if %errorlevel% neq 0 goto :error + +@call :test arrayinittest.c +@if %errorlevel% neq 0 goto :error + +@call :test arrayindexintrangecheck.c +@if %errorlevel% neq 0 goto :error + +@call :test array2stringinittest.c +@if %errorlevel% neq 0 goto :error + +@call :test testint16cmp.c +@if %errorlevel% neq 0 goto :error + +@call :test testint8cmp.c +@if %errorlevel% neq 0 goto :error + +@call :test testint32cmp.c +@if %errorlevel% neq 0 goto :error + +@call :test mixsigncmptest.c +@if %errorlevel% neq 0 goto :error + +@call :test testinterval.c +@if %errorlevel% neq 0 goto :error + +@call :test cmprangeshortcuttest.c +@if %errorlevel% neq 0 goto :error + +@call :test floatstringtest.c +@if %errorlevel% neq 0 goto :error + +@call :test sprintftest.c +@if %errorlevel% neq 0 goto :error + +@call :test qsorttest.c +@if %errorlevel% neq 0 goto :error + +@call :testn plasma.c +@if %errorlevel% neq 0 goto :error + +@call :test loopdomtest.c +@if %errorlevel% neq 0 goto :error + +@call :test loopboundtest.c +@if %errorlevel% neq 0 goto :error + +@call :test byteindextest.c +@if %errorlevel% neq 0 goto :error + +@call :test asmtest.c +@if %errorlevel% neq 0 goto :error + +@call :testb bitshifttest.c +@if %errorlevel% neq 0 goto :error + +@call :test arrparam.c +@if %errorlevel% neq 0 goto :error + +@call :test bsstest.c +@if %errorlevel% neq 0 goto :error + +@call :test copyintvec.c +@if %errorlevel% neq 0 goto :error + +@call :test divmodtest.c +@if %errorlevel% neq 0 goto :error + +@call :test divmod32test.c +@if %errorlevel% neq 0 goto :error + +@call :test fixmathtest.c +@if %errorlevel% neq 0 goto :error + +@call :testn andmultest.cpp +@if %errorlevel% neq 0 goto :error + +@call :test enumswitch.c +@if %errorlevel% neq 0 goto :error + +@call :test incvector.c +@if %errorlevel% neq 0 goto :error + +@call :test structoffsettest2.c +@if %errorlevel% neq 0 goto :error + +@call :test structsplittest.c +@if %errorlevel% neq 0 goto :error + +@call :test funcvartest.c +@if %errorlevel% neq 0 goto :error + +@call :test variadic_macros.c +@if %errorlevel% neq 0 goto :error + +@call :test funcarraycall.c +@if %errorlevel% neq 0 goto :error + +@call :test structassigntest.c +@if %errorlevel% neq 0 goto :error + +@call :test structmembertest.c +@if %errorlevel% neq 0 goto :error + +@call :test structarraycopy.c +@if %errorlevel% neq 0 goto :error + +@call :test randsumtest.c +@if %errorlevel% neq 0 goto :error + +@call :test longcodetest.c +@if %errorlevel% neq 0 goto :error + +@call :test scrolltest.c +@if %errorlevel% neq 0 goto :error + +@call :test charwintest.c +@if %errorlevel% neq 0 goto :error + +@call :test linetest.c +@if %errorlevel% neq 0 goto :error + +@call :test ptrinittest.c +@if %errorlevel% neq 0 goto :error + +@call :test ptrarraycmptest.c +@if %errorlevel% neq 0 goto :error + +@call :test cplxstructtest.c +@if %errorlevel% neq 0 goto :error + +@call :testn stripedarraytest.c +@if %errorlevel% neq 0 goto :error + +@call :testn stripedstructtest.c +@if %errorlevel% neq 0 goto :error + +@call :testn mmultest.c +@if %errorlevel% neq 0 goto :error + +@call :test tileexpand.cpp +@if %errorlevel% neq 0 goto :error + +@call :testi volatiletest.c +@if %errorlevel% neq 0 goto :error + +@call :test constlocaltest.c +@if %errorlevel% neq 0 goto :error + +@call :test structconditionaltest.c +@if %errorlevel% neq 0 goto :error + +@exit /b 0 + +:error +echo Failed with error #%errorlevel%. +exit /b %errorlevel% + +:testh +..\bin\oscar64 -ea -g -bc %~1 +@if %errorlevel% neq 0 goto :error + +..\bin\oscar64 -ea -g -n %~1 +@if %errorlevel% neq 0 goto :error + +..\bin\oscar64 -ea -g -O2 -bc %~1 +@if %errorlevel% neq 0 goto :error + +..\bin\oscar64 -ea -g -O2 -n %~1 +@if %errorlevel% neq 0 goto :error + +..\bin\oscar64 -ea -g -O2 -n -dHEAPCHECK %~1 +@if %errorlevel% neq 0 goto :error + +..\bin\oscar64 -ea -g -O2 -xz -Oz -n %~1 +@if %errorlevel% neq 0 goto :error + +..\bin\oscar64 -ea -g -O2 -Oo -n %~1 +@if %errorlevel% neq 0 goto :error + +..\bin\oscar64 -ea -g -O2 -Ox -n %~1 +@if %errorlevel% neq 0 goto :error + +..\bin\oscar64 -ea -g -O0 -bc %~1 +@if %errorlevel% neq 0 goto :error + +..\bin\oscar64 -ea -g -O0 -n %~1 +@if %errorlevel% neq 0 goto :error + +..\bin\oscar64 -ea -g -Os -bc %~1 +@if %errorlevel% neq 0 goto :error + +..\bin\oscar64 -ea -g -Os -n %~1 +@if %errorlevel% neq 0 goto :error + +..\bin\oscar64 -ea -g -O3 -bc %~1 +@if %errorlevel% neq 0 goto :error + +..\bin\oscar64 -ea -g -O3 -n %~1 +@if %errorlevel% neq 0 goto :error + +..\bin\oscar64 -ea -g -O3 -n -dHEAPCHECK %~1 +@if %errorlevel% neq 0 goto :error + +@exit /b 0 + +:test +..\bin\oscar64 -ea -g -bc %~1 +@if %errorlevel% neq 0 goto :error + +..\bin\oscar64 -ea -g -n %~1 +@if %errorlevel% neq 0 goto :error + +..\bin\oscar64 -ea -g -O2 -bc %~1 +@if %errorlevel% neq 0 goto :error + +..\bin\oscar64 -ea -g -O2 -n %~1 +@if %errorlevel% neq 0 goto :error + +..\bin\oscar64 -ea -g -O0 -bc %~1 +@if %errorlevel% neq 0 goto :error + +..\bin\oscar64 -ea -g -O0 -n %~1 +@if %errorlevel% neq 0 goto :error + +..\bin\oscar64 -ea -g -Os -bc %~1 +@if %errorlevel% neq 0 goto :error + +..\bin\oscar64 -ea -g -Os -n %~1 +@if %errorlevel% neq 0 goto :error + +..\bin\oscar64 -ea -g -O3 -bc %~1 +@if %errorlevel% neq 0 goto :error + +..\bin\oscar64 -ea -g -O3 -n %~1 +@if %errorlevel% neq 0 goto :error + +..\bin\oscar64 -ea -g -O2 -xz -Oz -n %~1 +@if %errorlevel% neq 0 goto :error + +..\bin\oscar64 -ea -g -O2 -Oo -n %~1 +@if %errorlevel% neq 0 goto :error + +..\bin\oscar64 -ea -g -O2 -Ox -n %~1 +@if %errorlevel% neq 0 goto :error + +@exit /b 0 + +:testb +..\bin\oscar64 -ea -g -bc %~1 +@if %errorlevel% neq 0 goto :error + +..\bin\oscar64 -ea -g -bc -O2 %~1 +@if %errorlevel% neq 0 goto :error + +..\bin\oscar64 -ea -g -bc -O0 %~1 +@if %errorlevel% neq 0 goto :error + +..\bin\oscar64 -ea -g -bc -Os %~1 +@if %errorlevel% neq 0 goto :error + +..\bin\oscar64 -ea -g -bc -O3 %~1 +@if %errorlevel% neq 0 goto :error + +@exit /b 0 + +:testn +..\bin\oscar64 -ea -g -n %~1 +@if %errorlevel% neq 0 goto :error + +..\bin\oscar64 -ea -g -O2 -n %~1 +@if %errorlevel% neq 0 goto :error + +..\bin\oscar64 -ea -g -O0 -n %~1 +@if %errorlevel% neq 0 goto :error + +..\bin\oscar64 -ea -g -Os -n %~1 +@if %errorlevel% neq 0 goto :error + +..\bin\oscar64 -ea -g -O3 -n %~1 +@if %errorlevel% neq 0 goto :error + +..\bin\oscar64 -ea -g -O2 -xz -Oz -n %~1 +@if %errorlevel% neq 0 goto :error + +..\bin\oscar64 -ea -g -O2 -Oo -n %~1 +@if %errorlevel% neq 0 goto :error + +..\bin\oscar64 -ea -g -O2 -Ox -n %~1 +@if %errorlevel% neq 0 goto :error + +:testi +..\bin\oscar64 -eai -g -n %~1 +@if %errorlevel% neq 0 goto :error + +..\bin\oscar64 -eai -g -O2 -n %~1 +@if %errorlevel% neq 0 goto :error + +..\bin\oscar64 -eai -g -O0 -n %~1 +@if %errorlevel% neq 0 goto :error + +..\bin\oscar64 -eai -g -Os -n %~1 +@if %errorlevel% neq 0 goto :error + +..\bin\oscar64 -eai -g -O3 -n %~1 +@if %errorlevel% neq 0 goto :error + +..\bin\oscar64 -eai -g -O2 -xz -Oz -n %~1 +@if %errorlevel% neq 0 goto :error + +..\bin\oscar64 -eai -g -O2 -Oo -n %~1 +@if %errorlevel% neq 0 goto :error + +..\bin\oscar64 -eai -g -O2 -Ox -n %~1 +@if %errorlevel% neq 0 goto :error + +@exit /b 0 diff --git a/oscar64/autotest/autotest.h b/oscar64/autotest/autotest.h new file mode 100644 index 0000000..6b0af80 --- /dev/null +++ b/oscar64/autotest/autotest.h @@ -0,0 +1,17 @@ +#ifndef AUTOTEST_H +#define AUTOTEST_H + +struct TestIO +{ + volatile char cnt0, cnt1; + volatile unsigned dmaddr; + volatile char dmdata; + volatile char mirror0, mirror1, mirror2; + volatile long cycles; + volatile char scratch[4]; +}; + +#define testio (*((struct TestIO *)0xdd80)) +#define testio2 (*((struct TestIO *)0xdd90)) + +#endif diff --git a/oscar64/autotest/bitfields.cpp b/oscar64/autotest/bitfields.cpp new file mode 100644 index 0000000..470a3ba --- /dev/null +++ b/oscar64/autotest/bitfields.cpp @@ -0,0 +1,354 @@ +#include + +struct A +{ + char x : 4; + char y : 1; + char z : 3; +}; + +A a = {7, 1, 2}; + +void test_char_fit(void) +{ + assert(a.x == 7); + assert(a.y == 1); + assert(a.z == 2); + assert(sizeof(A) == 1); + + for(int i=0; i<16; i++) + { + a.x = i; + a.y = 0; + a.z = 3; + assert(a.x == i); + assert(a.y == 0); + assert(a.z == 3); + } +} + +struct B +{ + char x : 6; + char y : 6; + char z : 6; + char w : 6; +}; + +B b = {11, 22, 33, 44}; + +void test_char_cross(void) +{ + assert(b.x == 11); + assert(b.y == 22); + assert(b.z == 33); + assert(b.w == 44); + assert(sizeof(B) == 3); + + for(int i=0; i<64; i++) + { + b.x = i * 1; + b.y = i * 3; + b.z = i * 5; + b.w = i * 7; + assert(b.x == ((i * 1) & 0x3f)); + assert(b.y == ((i * 3) & 0x3f)); + assert(b.z == ((i * 5) & 0x3f)); + assert(b.w == ((i * 7) & 0x3f)); + } +} + +struct C +{ + unsigned x : 4; + unsigned y : 1; + unsigned z : 3; +}; + +C c = {7, 1, 2}; + +void test_word_fit(void) +{ + assert(c.x == 7); + assert(c.y == 1); + assert(c.z == 2); + assert(sizeof(C) == 1); + + for(int i=0; i<16; i++) + { + c.x = i; + c.y = 0; + c.z = 3; + assert(c.x == i); + assert(c.y == 0); + assert(c.z == 3); + } +} + +struct D +{ + unsigned x : 10; + unsigned y : 10; + unsigned z : 10; + unsigned w : 10; +}; + +D d = {111, 222, 333, 444}; + +void test_word_cross(void) +{ + assert(d.x == 111); + assert(d.y == 222); + assert(d.z == 333); + assert(d.w == 444); + assert(sizeof(D) == 5); + + for(int i=0; i<1024; i++) + { + d.x = i * 1; + d.y = i * 3; + d.z = i * 5; + d.w = i * 7; + assert(d.x == ((i * 1) & 0x3ff)); + assert(d.y == ((i * 3) & 0x3ff)); + assert(d.z == ((i * 5) & 0x3ff)); + assert(d.w == ((i * 7) & 0x3ff)); + } +} + +struct E +{ + unsigned long x : 4; + unsigned long y : 1; + unsigned long z : 3; +}; + +E e = {7, 1, 2}; + +void test_dword_fit(void) +{ + assert(e.x == 7); + assert(e.y == 1); + assert(e.z == 2); + assert(sizeof(E) == 1); + + for(int i=0; i<16; i++) + { + e.x = i; + e.y = 0; + e.z = 3; + assert(e.x == i); + assert(e.y == 0); + assert(e.z == 3); + } +} + +struct F +{ + unsigned long x : 20; + unsigned long y : 20; + unsigned long z : 20; + unsigned long w : 20; +}; + +F f = {111111UL, 222222UL, 333333UL, 444444UL}; + +void test_dword_cross(void) +{ + assert(f.x == 111111UL); + assert(f.y == 222222UL); + assert(f.z == 333333UL); + assert(f.w == 444444UL); + assert(sizeof(F) == 10); + + for(int i=0; i<1024; i++) + { + f.x = i * 11UL; + f.y = i * 33UL; + f.z = i * 55UL; + f.w = i * 77UL; + assert(f.x == ((i * 11UL) & 0xfffffUL)); + assert(f.y == ((i * 33UL) & 0xfffffUL)); + assert(f.z == ((i * 55UL) & 0xfffffUL)); + assert(f.w == ((i * 77UL) & 0xfffffUL)); + } +} + +struct G +{ + signed char x : 1; + signed char y : 5; + signed char z : 2; +}; + +G g = {0, -1, -2}; + +void test_char_signed(void) +{ + assert(g.x == 0); + assert(g.y == -1); + assert(g.z == -2); + assert(sizeof(G) == 1); + + for(int i=-16; i<16; i++) + { + g.x = -1; + g.y = i; + g.z = 1; + assert(g.x == -1); + assert(g.y == i); + assert(g.z == 1); + } +} + +struct H +{ + int x : 10; + int y : 10; + int z : 10; + int w : 10; +}; + +H h = {111, -222, -333, 444}; + +void test_word_signed(void) +{ + assert(h.x == 111); + assert(h.y == -222); + assert(h.z == -333); + assert(h.w == 444); + assert(sizeof(H) == 5); + + for(int i=-32; i<32; i++) + { + h.x = i * 1; + h.y = i * 3; + h.z = i * 5; + h.w = i * 7; + assert(h.x == i * 1); + assert(h.y == i * 3); + assert(h.z == i * 5); + assert(h.w == i * 7); + } +} + +void test_inc_char_fit(void) +{ + A ai; + ai.x = 7; + ai.y = 1; + ai.z = 2; + + for(int i=0; i<16; i++) + { + assert(ai.x == ((7 + i) & 15)); + assert(ai.y == ((1 + i) & 1)); + assert(ai.z == ((2 + i) & 7)); + ai.x++; + ai.y++; + ai.z++; + } +} + +void test_inc_char_cross(void) +{ + B bi; + bi.x = 11; + bi.y = 22; + bi.z = 33; + bi.w = 44; + + for(int i=0; i<64; i++) + { + assert(bi.x == ((11 + i) & 0x3f)); + assert(bi.y == ((22 + i) & 0x3f)); + assert(bi.z == ((33 + i) & 0x3f)); + assert(bi.w == ((44 + i) & 0x3f)); + bi.x++; + bi.y++; + bi.z++; + bi.w++; + } +} + +void test_add_char_cross(void) +{ + B bi= {0}; + bi.x = 11; + bi.y = 22; + bi.z = 33; + bi.w = 44; + + for(int i=0; i<64; i++) + { + assert(bi.x == ((11 + 5 * i) & 0x3f)); + assert(bi.y == ((22 + 21 * i) & 0x3f)); + assert(bi.z == ((33 - 4 * i) & 0x3f)); + assert(bi.w == ((44 - 11 * i) & 0x3f)); + bi.x += 5; + bi.y += 21; + bi.z -= 4; + bi.w -= 11; + } +} + +void test_add_word_fit(void) +{ + C ci = {0}; + + ci.x = 7; + ci.y = 1; + ci.z = 2; + + for(int i=0; i<16; i++) + { + assert(ci.x == ((7 + 5 * i) & 15)); + assert(ci.y == ((1 + 21 * i) & 1)); + assert(ci.z == ((2 - 4 * i) & 7)); + ci.x += 5; + ci.y += 21; + ci.z -= 4; + } +} + +void test_add_word_cross(void) +{ + D di = {0}; + + di.x = 111; + di.y = 222; + di.z = 333; + di.w = 444; + + for(int i=0; i<1024; i++) + { + assert(di.x == ((111 + 5 * i) & 0x3ff)); + assert(di.y == ((222 + 21 * i) & 0x3ff)); + assert(di.z == ((333 - 4 * i) & 0x3ff)); + assert(di.w == ((444 - 11 * i) & 0x3ff)); + di.x += 5; + di.y += 21; + di.z -= 4; + di.w -= 11; + } +} +int main(void) +{ + test_char_fit(); + test_char_cross(); + test_word_fit(); + test_word_cross(); + test_dword_fit(); + test_dword_cross(); + test_char_signed(); + test_word_signed(); + + test_inc_char_fit(); + test_inc_char_cross(); + test_add_char_cross(); + + test_add_word_fit(); + test_add_word_cross(); + + return 0; +} diff --git a/oscar64/autotest/bitshifttest.c b/oscar64/autotest/bitshifttest.c new file mode 100644 index 0000000..0e03d21 --- /dev/null +++ b/oscar64/autotest/bitshifttest.c @@ -0,0 +1,414 @@ +#include +#include + +#pragma region( main, 0x0a00, 0xd000, , , {code, data, bss, heap, stack} ) + +unsigned shl1b(int n) +{ + return 1 << n; +} + +unsigned shl1n(int n) +{ + return 1 << n; +} + +#pragma native(shl1n) + +unsigned shr1b(int n) +{ + return 0x8000 >> n; +} + +unsigned shr1n(int n) +{ + return 0x8000 >> n; +} + +#pragma native(shr1n) + +unsigned shl4b(int n) +{ + return 0x0010 << n; +} + +unsigned shl4n(int n) +{ + return 0x0010 << n; +} + +#pragma native(shl4n) + +unsigned shr4b(int n) +{ + return 0x0800 >> n; +} + +unsigned shr4n(int n) +{ + return 0x0800 >> n; +} + +#pragma native(shr4n) + + +unsigned shl8b(int n) +{ + return 0x0100 << n; +} + +unsigned shl8n(int n) +{ + return 0x0100 << n; +} + +#pragma native(shl8n) + +unsigned shr8b(int n) +{ + return 0x0080 >> n; +} + +unsigned shr8n(int n) +{ + return 0x0080 >> n; +} + +#pragma native(shr8n) + + +void shl8xb(unsigned char xu, signed char xi) +{ + unsigned char ua[16]; + signed char ia[16]; +#assign s 0 +#repeat + ua[s] = xu << s; + ia[s] = xi << s; +#assign s s + 1 +#until s == 16 + + for(int i=0; i<16; i++) + { + assert(ua[i] == (unsigned char)(xu << i)); + assert(ia[i] == (signed char)(xi << i)); + } +} + +void shr8xb(unsigned char xu, signed char xi) +{ + unsigned char ua[16]; + signed char ia[16]; +#assign s 0 +#repeat + ua[s] = xu >> s; + ia[s] = xi >> s; +#assign s s + 1 +#until s == 16 + + for(int i=0; i<16; i++) + { + assert(ua[i] == (unsigned char)(xu >> i)); + assert(ia[i] == (signed char)(xi >> i)); + } +} + +void shl8xn(unsigned char xu, signed char xi) +{ + unsigned char ua[16]; + signed char ia[16]; +#assign s 0 +#repeat + ua[s] = xu << s; + ia[s] = xi << s; +#assign s s + 1 +#until s == 16 + + for(int i=0; i<16; i++) + { + assert(ua[i] == (unsigned char)(xu << i)); + assert(ia[i] == (signed char)(xi << i)); + } +} + +void shr8xn(unsigned char xu, signed char xi) +{ + unsigned char ua[16]; + signed char ia[16]; +#assign s 0 +#repeat + ua[s] = xu >> s; + ia[s] = xi >> s; +#assign s s + 1 +#until s == 16 + + for(int i=0; i<16; i++) + { + assert(ua[i] == (unsigned char)(xu >> i)); + assert(ia[i] == (signed char)(xi >> i)); + } +} + +#pragma native(shl8xn) +#pragma native(shr8xn) + + + + +void shl16b(unsigned xu, int xi) +{ + unsigned ua[16]; + int ia[16]; +#assign s 0 +#repeat + ua[s] = xu << s; + ia[s] = xi << s; +#assign s s + 1 +#until s == 16 + + for(int i=0; i<16; i++) + { + assert(ua[i] == xu << i); + assert(ia[i] == xi << i); + } +} + +void shr16b(unsigned xu, int xi) +{ + unsigned ua[16]; + int ia[16]; +#assign s 0 +#repeat + ua[s] = xu >> s; + ia[s] = xi >> s; +#assign s s + 1 +#until s == 16 + + for(int i=0; i<16; i++) + { + assert(ua[i] == xu >> i); + assert(ia[i] == xi >> i); + } +} + +void shl16n(unsigned xu, int xi) +{ + unsigned ua[16]; + int ia[16]; +#assign s 0 +#repeat + ua[s] = xu << s; + ia[s] = xi << s; +#assign s s + 1 +#until s == 16 + + for(int i=0; i<16; i++) + { + assert(ua[i] == xu << i); + assert(ia[i] == xi << i); + } +} + +void shr16n(unsigned xu, int xi) +{ + unsigned ua[16]; + int ia[16]; +#assign s 0 +#repeat + ua[s] = xu >> s; + ia[s] = xi >> s; +#assign s s + 1 +#until s == 16 + + for(int i=0; i<16; i++) + { + assert(ua[i] == xu >> i); + assert(ia[i] == xi >> i); + } +} + +#pragma native(shl16n) +#pragma native(shr16n) + + +void shl32b(unsigned long xu, long xi) +{ + unsigned long ua[32]; + long ia[32]; +#assign s 0 +#repeat + ua[s] = xu << s; + ia[s] = xi << s; +#assign s s + 1 +#until s == 32 + + for(int i=0; i<32; i++) + { + assert(ua[i] == xu << i); + assert(ia[i] == xi << i); + } +} + +void shr32b(unsigned long xu, long xi) +{ + unsigned long ua[32]; + long ia[32]; +#assign s 0 +#repeat + ua[s] = xu >> s; + ia[s] = xi >> s; +#assign s s + 1 +#until s == 32 + + for(int i=0; i<32; i++) + { + assert(ua[i] == xu >> i); + assert(ia[i] == xi >> i); + } +} + +void shl32n(unsigned long xu, long xi) +{ + unsigned long ua[32]; + long ia[32]; +#assign s 0 +#repeat + ua[s] = xu << s; + ia[s] = xi << s; +#assign s s + 1 +#until s == 32 + + for(int i=0; i<32; i++) + { + assert(ua[i] == xu << i); + assert(ia[i] == xi << i); + } +} + +void shr32n(unsigned long xu, long xi) +{ + unsigned long ua[32]; + long ia[32]; +#assign s 0 +#repeat + ua[s] = xu >> s; + ia[s] = xi >> s; +#assign s s + 1 +#until s == 32 + + for(int i=0; i<32; i++) + { + assert(ua[i] == xu >> i); + assert(ia[i] == xi >> i); + } +} + +void shl1_32n(void) +{ + static const unsigned long m[] = { + #for(i, 32) 1ul << i, + }; + + for(int i=0; i<32; i++) + { + assert(1ul << i == m[i]); + } +} + +#pragma native(shl32n) +#pragma native(shr32n) + +int main(void) +{ + *(volatile char *)0x01 = 0x36; + + for(int i=0; i<32; i++) + { + printf("1: %.4x : %.4x | %.4x : %.4x\n", shl1b(i), shl1n(i), shr1b(i), shr1n(i)); + assert(shl1b(i) == shl1n(i)); + assert(shr1b(i) == shr1n(i)); + } + + for(int i=0; i<32; i++) + { + printf("4: %.4x : %.4x | %.4x : %.4x\n", shl4b(i), shl4n(i), shr4b(i), shr4n(i)); + assert(shl4b(i) == shl4n(i)); + assert(shr4b(i) == shr4n(i)); + } + + for(int i=0; i<32; i++) + { + printf("8: %.4x : %.4x | %.4x : %.4x\n", shl8b(i), shl8n(i), shr8b(i), shr8n(i)); + assert(shl8b(i) == shl8n(i)); + assert(shr8b(i) == shr8n(i)); + } + + shl8xb(0x00, 0x00); + shl8xb(0xff, 0xff); + shl8xb(0x34, 0x34); + shl8xb(0xdc, 0xdc); + + shr8xb(0x00, 0x00); + shr8xb(0xff, 0xff); + shr8xb(0x34, 0x34); + shr8xb(0xdc, 0xdc); + + shl8xn(0x00, 0x00); + shl8xn(0xff, 0xff); + shl8xn(0x34, 0x34); + shl8xn(0xdc, 0xdc); + + shr8xn(0x00, 0x00); + shr8xn(0xff, 0xff); + shr8xn(0x34, 0x34); + shr8xn(0xdc, 0xdc); + + shl16b(0x0000, 0x0000); + shl16b(0xffff, 0xffff); + shl16b(0x1234, 0x1234); + shl16b(0xfedc, 0xfedc); + + shr16b(0x0000, 0x0000); + shr16b(0xffff, 0xffff); + shr16b(0x1234, 0x1234); + shr16b(0xfedc, 0xfedc); + + shl16n(0x0000, 0x0000); + + shl16n(0xffff, 0xffff); + shl16n(0x1234, 0x1234); + shl16n(0xfedc, 0xfedc); + + shr16n(0x0000, 0x0000); + shr16n(0xffff, 0xffff); + shr16n(0x1234, 0x1234); + shr16n(0xfedc, 0xfedc); + + shl32b(0x00000000UL, 0x00000000L); + shl32b(0x00000001UL, 0x00000001L); + shl32b(0xffffffffUL, 0xffffffffL); + shl32b(0x12345678UL, 0x12345678L); + shl32b(0xfedcba98UL, 0xfedcba98L); + + shr32b(0x00000000UL, 0x00000000L); + shr32b(0x00000001UL, 0x00000001L); + shr32b(0xffffffffUL, 0xffffffffL); + shr32b(0x12345678UL, 0x12345678L); + shr32b(0xfedcba98UL, 0xfedcba98L); + + shl32n(0x00000000UL, 0x00000000L); + shl32n(0x00000001UL, 0x00000001L); + shl32n(0xffffffffUL, 0xffffffffL); + shl32n(0x12345678UL, 0x12345678L); + shl32n(0xfedcba98UL, 0xfedcba98L); + + shr32n(0x00000000UL, 0x00000000L); + shr32n(0x00000001UL, 0x00000001L); + shr32n(0xffffffffUL, 0xffffffffL); + shr32n(0x12345678UL, 0x12345678L); + shr32n(0xfedcba98UL, 0xfedcba98L); + + shl1_32n(); + + return 0; +} diff --git a/oscar64/autotest/bsstest.c b/oscar64/autotest/bsstest.c new file mode 100644 index 0000000..8bc5ea2 --- /dev/null +++ b/oscar64/autotest/bsstest.c @@ -0,0 +1,27 @@ + +#include + +char ch[100]; +char p[] = "HELLO"; +int v[10]; +int w[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + +int sum(int * k) +{ + int s = 0; + for(int i=0; i<10; i++) + s += k[i]; + return s; +} + +int main(void) +{ + strcpy(ch, p); + strcat(ch, " WORLD"); + + for(int i=0; i<10; i++) + v[i] = w[i]; + + return strcmp(ch, "HELLO WORLD") + sum(v) - 55; +} + diff --git a/oscar64/autotest/byteindextest.c b/oscar64/autotest/byteindextest.c new file mode 100644 index 0000000..ef1a502 --- /dev/null +++ b/oscar64/autotest/byteindextest.c @@ -0,0 +1,18 @@ + +#include +#include + +char a[20]; + +int main(void) +{ + for(char i=0; i<20; i++) + a[i] = i; + char x = 0; + for(char i=0; i<20; i++) + x += a[i]; + + assert(x == 190); + + return 0; +} diff --git a/oscar64/autotest/charwintest.c b/oscar64/autotest/charwintest.c new file mode 100644 index 0000000..418926e --- /dev/null +++ b/oscar64/autotest/charwintest.c @@ -0,0 +1,64 @@ +#include +#include +#include + +int main(void) +{ + CharWin cw; + + cwin_init(&cw, (char *)0x0400, 2, 2, 32, 20); + + cwin_clear(&cw); + + for(int y=0; y<20; y++) + { + for(int x=0; x<32; x++) + { + assert(cwin_getat_char_raw(&cw, x, y) == ' '); + } + } + + for(int y=0; y<20; y++) + { + for(int x=0; x<32; x++) + { + cwin_putat_char(&cw, x, y, p'a' - 1 + x, 7); + } + } + + + for(int y=0; y<20; y++) + { + for(int x=0; x<32; x++) + { + assert(cwin_getat_char(&cw, x, y) == p'a' -1 + x); + } + } + + + for(int y=0; y<20; y++) + { + for(int x=0; x<32; x++) + { + cwin_putat_char(&cw, x, y, p'A' - 1 + x, 8); + } + } + + for(int y=0; y<20; y++) + { + for(int x=0; x<32; x++) + { + cwin_putat_char_raw(&cw, x, y, x + 32 * y, 8); + } + } + + for(int y=0; y<8; y++) + { + for(int x=0; x<32; x++) + { + assert(cwin_getat_char_raw(&cw, x, y) == x + 32 * y); + } + } + + return 0; +} diff --git a/oscar64/autotest/cmprangeshortcuttest.c b/oscar64/autotest/cmprangeshortcuttest.c new file mode 100644 index 0000000..624e544 --- /dev/null +++ b/oscar64/autotest/cmprangeshortcuttest.c @@ -0,0 +1,298 @@ +#include + +void check_s_lt(void) +{ + int a5 = 0, a10 = 0, a15 = 0; + + for(int i=5; i<15; i++) + { + if (i < 5) + a5++; + if (i < 10) + a10++; + if (i < 15) + a15++; + } + + assert(a5 == 0); + assert(a10 == 5); + assert(a15 == 10); +} + +void check_s_le(void) +{ + int a4 = 0, a5 = 0, a10 = 0, a14 = 0, a15 = 0; + + for(int i=5; i<15; i++) + { + if (i <= 4) + a4++; + if (i <= 5) + a5++; + if (i <= 10) + a10++; + if (i <= 14) + a14++; + if (i <= 15) + a15++; + } + + assert(a4 == 0); + assert(a5 == 1); + assert(a10 == 6); + assert(a14 == 10); + assert(a15 == 10); +} + +void check_s_ge(void) +{ + int a5 = 0, a10 = 0, a15 = 0; + + for(int i=5; i<15; i++) + { + if (i >= 5) + a5++; + if (i >= 10) + a10++; + if (i >= 15) + a15++; + } + + assert(a5 == 10); + assert(a10 == 5); + assert(a15 == 0); +} + +void check_s_gt(void) +{ + int a4 = 0, a5 = 0, a10 = 0, a14 = 0, a15 = 0; + + for(int i=5; i<15; i++) + { + if (i > 4) + a4++; + if (i > 5) + a5++; + if (i > 10) + a10++; + if (i > 14) + a14++; + if (i > 15) + a15++; + } + + assert(a4 == 10); + assert(a5 == 9); + assert(a10 == 4); + assert(a14 == 0); + assert(a15 == 0); +} + +void check_s_eq(void) +{ + int a4 = 0, a5 = 0, a10 = 0, a14 = 0, a15 = 0; + + for(int i=5; i<15; i++) + { + if (i == 4) + a4++; + if (i == 5) + a5++; + if (i == 10) + a10++; + if (i == 14) + a14++; + if (i == 15) + a15++; + } + + assert(a4 == 0); + assert(a5 == 1); + assert(a10 == 1); + assert(a14 == 1); + assert(a15 == 0); +} + +void check_s_ne(void) +{ + int a4 = 0, a5 = 0, a10 = 0, a14 = 0, a15 = 0; + + for(int i=5; i<15; i++) + { + if (i != 4) + a4++; + if (i != 5) + a5++; + if (i != 10) + a10++; + if (i != 14) + a14++; + if (i != 15) + a15++; + } + + assert(a4 == 10); + assert(a5 == 9); + assert(a10 == 9); + assert(a14 == 9); + assert(a15 == 10); +} + +void check_u_lt(void) +{ + int a5 = 0, a10 = 0, a15 = 0; + + for(unsigned i=5; i<15; i++) + { + if (i < 5) + a5++; + if (i < 10) + a10++; + if (i < 15) + a15++; + } + + assert(a5 == 0); + assert(a10 == 5); + assert(a15 == 10); +} + +void check_u_le(void) +{ + int a4 = 0, a5 = 0, a10 = 0, a14 = 0, a15 = 0; + + for(unsigned i=5; i<15; i++) + { + if (i <= 4) + a4++; + if (i <= 5) + a5++; + if (i <= 10) + a10++; + if (i <= 14) + a14++; + if (i <= 15) + a15++; + } + + assert(a4 == 0); + assert(a5 == 1); + assert(a10 == 6); + assert(a14 == 10); + assert(a15 == 10); +} + +void check_u_ge(void) +{ + int a5 = 0, a10 = 0, a15 = 0; + + for(unsigned i=5; i<15; i++) + { + if (i >= 5) + a5++; + if (i >= 10) + a10++; + if (i >= 15) + a15++; + } + + assert(a5 == 10); + assert(a10 == 5); + assert(a15 == 0); +} + +void check_u_gt(void) +{ + int a4 = 0, a5 = 0, a10 = 0, a14 = 0, a15 = 0; + + for(unsigned i=5; i<15; i++) + { + if (i > 4) + a4++; + if (i > 5) + a5++; + if (i > 10) + a10++; + if (i > 14) + a14++; + if (i > 15) + a15++; + } + + assert(a4 == 10); + assert(a5 == 9); + assert(a10 == 4); + assert(a14 == 0); + assert(a15 == 0); +} + +void check_u_eq(void) +{ + int a4 = 0, a5 = 0, a10 = 0, a14 = 0, a15 = 0; + + for(unsigned i=5; i<15; i++) + { + if (i == 4) + a4++; + if (i == 5) + a5++; + if (i == 10) + a10++; + if (i == 14) + a14++; + if (i == 15) + a15++; + } + + assert(a4 == 0); + assert(a5 == 1); + assert(a10 == 1); + assert(a14 == 1); + assert(a15 == 0); +} + +void check_u_ne(void) +{ + int a4 = 0, a5 = 0, a10 = 0, a14 = 0, a15 = 0; + + for(unsigned i=5; i<15; i++) + { + if (i != 4) + a4++; + if (i != 5) + a5++; + if (i != 10) + a10++; + if (i != 14) + a14++; + if (i != 15) + a15++; + } + + assert(a4 == 10); + assert(a5 == 9); + assert(a10 == 9); + assert(a14 == 9); + assert(a15 == 10); +} + + +int main(void) +{ + check_s_ne(); + check_s_eq(); + check_s_lt(); + check_s_le(); + check_s_gt(); + check_s_ge(); + + check_u_ne(); + check_u_eq(); + check_u_lt(); + check_u_le(); + check_u_gt(); + check_u_ge(); + + return 0; +} + diff --git a/oscar64/autotest/constlocaltest.c b/oscar64/autotest/constlocaltest.c new file mode 100644 index 0000000..6480501 --- /dev/null +++ b/oscar64/autotest/constlocaltest.c @@ -0,0 +1,32 @@ +#include +#include + +static int counter; + +static int read_const_local_twice(void) +{ + const int value = ++counter; + return value + value; +} + +static bool update(unsigned char* value) +{ + const unsigned char before = *value; + const unsigned char after = before + 2; + const bool changed = after != before; + + *value = after; + return changed; +} + +int main(void) +{ + assert(read_const_local_twice() == 2); + assert(counter == 1); + + unsigned char value = 7; + assert(update(&value)); + assert(value == 9); + + return 0; +} diff --git a/oscar64/autotest/constructortest.cpp b/oscar64/autotest/constructortest.cpp new file mode 100644 index 0000000..1418277 --- /dev/null +++ b/oscar64/autotest/constructortest.cpp @@ -0,0 +1,165 @@ +#include + +int t, n; + +struct C1 +{ + int a; + + C1(int x); + ~C1(void); +}; + +C1::C1(int x) : a(x) +{ + t += a; + n++; +} + +C1::~C1(void) +{ + t -= a; +} + +void test_base(void) +{ + n = 0; + + { + C1 x(2); + C1 y(1); + } + + assert(t == 0 && n == 2); +} + +void test_base_loop(void) +{ + n = 0; + + for(int i=0; i<10; i++) + { + C1 x(2); + C1 y(1); + } + + assert(t == 0 && n == 20); +} + +struct C2 +{ + C1 c, d; + + C2(void); +}; + +C2::C2(void) + : c(7), d(11) +{ + +} + +void test_member(void) +{ + n = 0; + + { + C2 x(); + C2 y(); + } + + assert(t == 0 && n == 4); +} + +void test_member_loop(void) +{ + n = 0; + + for(int i=0; i<10; i++) + { + C2 x(); + C2 y(); + } + + assert(t == 0 && n == 40); +} + +struct C3 +{ + C2 x, y; +}; + +void test_default(void) +{ + n = 0; + + { + C3 x(); + C3 y(); + } + + assert(t == 0 && n == 8); +} + +void test_default_loop(void) +{ + n = 0; + + for(int i=0; i<10; i++) + { + C3 x(); + C3 y(); + } + + assert(t == 0 && n == 80); +} + +inline void test_inline_x(void) +{ + C1 x(1), y(2); +} + +void test_inline(void) +{ + n = 0; + + test_inline_x(); + + assert(t == 0 && n == 2); +} + +inline void test_inline_xr(void) +{ + C1 x(1), y(2); + + { + C1 x(3); + return; + } +} + +void test_inline_return(void) +{ + n = 0; + + test_inline_xr(); + + assert(t == 0 && n == 3); +} + +int main(void) +{ + test_base(); + test_base_loop(); + + test_member(); + test_member_loop(); + + test_default(); + test_default_loop(); + + test_inline(); + test_inline_return(); + + return 0; +} diff --git a/oscar64/autotest/copyassign.cpp b/oscar64/autotest/copyassign.cpp new file mode 100644 index 0000000..886a321 --- /dev/null +++ b/oscar64/autotest/copyassign.cpp @@ -0,0 +1,126 @@ +#include + +int t, n; + +struct C0 +{ + int u; + + C0(int a); + ~C0(void); +}; + +C0::C0(int a) : u(a) +{ + t += u; + n++; +} + +C0::~C0(void) +{ + t -= u; +} + +struct C1 +{ + int u; + + C1(int a); + ~C1(void); + C1(const C1 & c); + C1 & operator=(const C1 & c); +}; + +C1::C1(int a) : u(a) +{ + t += u; + n++; +} + +C1::~C1(void) +{ + t -= u; +} + +C1::C1(const C1 & c) : u(c.u) +{ + t += u; + n++; +} + +C1 & C1::operator=(const C1 & c) +{ + t -= u; + u = c.u; + t += u; + return *this; +} + +void test_assign(void) +{ + n = 0; + + { + C1 c(4); + C1 d(5); + c = d; + } + + assert(n == 2 && t == 0); +} + +struct C2 +{ + C1 a, b; + + C2(int x, int y) : a(x), b(y) + {} +}; + +void test_assign_deflt(void) +{ + n = 0; + + { + C2 c(2, 3); + C2 d(5, 10); + c = d; + } + + assert(n == 4 && t == 0); +} + +int k; + +C2 test_ret_v(void) +{ + C2 c(5, 10); + return c; +} + +C2 & test_ret_r(C2 & r) +{ + return r; +} + +void test_assign_return_value(void) +{ + n = 0; + + { + C2 c(2, 3); + c = test_ret_v(); + } + + assert(n == 6 && t == 0); +} + + +int main(void) +{ + test_assign(); + test_assign_deflt(); + test_assign_return_value(); + + return 0; +} diff --git a/oscar64/autotest/copyconstructor.cpp b/oscar64/autotest/copyconstructor.cpp new file mode 100644 index 0000000..88c1c58 --- /dev/null +++ b/oscar64/autotest/copyconstructor.cpp @@ -0,0 +1,215 @@ +#include +#include + +int t, n; + +struct C0 +{ + int u; + + C0(int a); + ~C0(void); +}; + +C0::C0(int a) : u(a) +{ + t += u; + n++; +} + +C0::~C0(void) +{ + t -= u; +} + +struct C1 +{ + int u; + + C1(int a); + ~C1(void); + C1(const C1 & c); +}; + +C1::C1(int a) : u(a) +{ + t += u; + n++; +} + +C1::~C1(void) +{ + t -= u; +} + +C1::C1(const C1 & c) : u(c.u) +{ + t += u; + n++; +} + +void test_dcopy_init(void) +{ + n = 0; + + { + C0 x(4); + C0 y(x); + } + + assert(n == 1 && t == -4); + + t = 0; +} + +void test_copy_init(void) +{ + n = 0; + + { + C1 x(4); + C1 y(x); + } + + assert(n == 2 && t == 0); +} + +struct C2 +{ + C1 a, b; + + C2(void); +}; + +C2::C2(void) : a(1), b(3) +{} + +void test_minit(void) +{ + n = 0; + + { + C2 x; + } + + assert(n == 2 && t == 0); +} + +void test_minit_copy(void) +{ + n = 0; + + { + C2 x; + C2 y(x); + } + + assert(n == 4 && t == 0); +} + +int k; + +void test_param_fv(C2 c) +{ + k += c.a.u; +} + +void test_param_fr(C2 & c) +{ + k += c.a.u; +} + +void test_param_value(void) +{ + n = 0; + + { + C2 x; + test_param_fv(x); + } + + assert(n == 4 && t == 0); +} + +void test_param_ref(void) +{ + n = 0; + + { + C2 x; + test_param_fr(x); + } + + assert(n == 2 && t == 0); +} + +C2 test_ret_v(void) +{ + C2 c; + return c; +} + +C2 & test_ret_r(C2 & r) +{ + return r; +} + +void test_return_value(void) +{ + n = 0; + + { + C2 c(test_ret_v()); + } + + assert(n == 6 && t == 0); +} + +void test_return_reference(void) +{ + n = 0; + + { + C2 d; + C2 c(test_ret_r(d)); + } + + assert(n == 2 && t == 0); +} + +void test_retparam_value(void) +{ + n = 0; + + { + test_param_fv(test_ret_v()); + } + + assert(n == 6 && t == 0); +} + +void test_retparam_reference(void) +{ + n = 0; + + { + test_param_fr(test_ret_v()); + } + + assert(n == 4 && t == 0); +} + +int main(void) +{ + test_dcopy_init(); + test_copy_init(); + test_minit(); + test_minit_copy(); + test_param_value(); + test_param_ref(); + test_return_value(); + test_retparam_value(); + test_retparam_reference(); + + return 0; +} \ No newline at end of file diff --git a/oscar64/autotest/copyinitmove.c b/oscar64/autotest/copyinitmove.c new file mode 100644 index 0000000..d852df3 --- /dev/null +++ b/oscar64/autotest/copyinitmove.c @@ -0,0 +1,27 @@ + +int val(char * c) +{ + return c[0]; +} + +void set(char * c, int a) +{ + c[0] = a; +} + +int main(void) +{ + int sum0 = 0, sum1 = 0, sum2 = 0, sum3 = 0; + + for(int i=0; i<10; i++) + { + char t[10] = {1}; + sum0 += val(t); + sum2 += t[0]; + set(t, i); + sum1 += val(t); + sum3 += t[0]; + } + + return (sum1 - 45) | (sum0 - 10) | (sum3 - 45) | (sum2 - 10); +} diff --git a/oscar64/autotest/copyintvec.c b/oscar64/autotest/copyintvec.c new file mode 100644 index 0000000..0d9369e --- /dev/null +++ b/oscar64/autotest/copyintvec.c @@ -0,0 +1,20 @@ + + +void copyi(int * a, int * b, int n) +{ + for(int i=0; i +#include +#include + +struct cplx +{ + float r, i; +}; + +cplx cplx_add(cplx a, cplx b) +{ + cplx c; + c.r = a.r + b.r; + c.i = a.i + b.i; + return c; +} + +cplx cplx_sub(cplx a, cplx b) +{ + cplx c; + c.r = a.r - b.r; + c.i = a.i - b.i; + return c; +} + +cplx cplx_mul(cplx a, cplx b) +{ + cplx c; + c.r = a.r * b.r - a.i * b.i; + c.i = a.i * b.r + a.r * b.i; + return c; +} + +float cplx_abs(cplx a) +{ + return sqrt(a.r * a.r + a.i * a.i); +} + +cplx cplx_sum(cplx p, const cplx * a, int n) +{ + cplx s = {0.0, 0.0}; + cplx c = {1.0, 0.0}; + + for(int i=0; i + +void check(unsigned long l, unsigned long r) +{ + unsigned long d = l / r, m = l % r; + + assert(d * r + m == l); + assert(m < r); +} + +int main(void) +{ + for(char i=0; i<28; i++) + { + for(char j=0; j<28; j++) + { + check(0xb3ul << i, 0x2bul << j); + check(0xb3ul << i, 0x01ul << j); + check(0x01ul << i, 0xc2ul << j); + check(0xb31ful << i, 0x2bul << j); + check(0xb354ul << i, 0x01ul << j); + check(0xb3ul << i, 0x2b1cul << j); + check(0xb3ul << i, 0x013ful << j); + check(0xb31ful << i, 0x2b23ul << j); + check(0xb354ul << i, 0x0145ul << j); + check(0xb31f24ul << i, 0x2bul << j); + check(0xb35421ul << i, 0x01ul << j); + check(0xb31f24ul << i, 0x2b23ul << j); + check(0xb35421ul << i, 0x0145ul << j); + check(0xb31f24ul << i, 0x2b2356ul << j); + check(0xb35421ul << i, 0x0145a7ul << j); + } + } + + return 0; +} diff --git a/oscar64/autotest/divmodtest.c b/oscar64/autotest/divmodtest.c new file mode 100644 index 0000000..12019d1 --- /dev/null +++ b/oscar64/autotest/divmodtest.c @@ -0,0 +1,39 @@ +#include + +int main(void) +{ + for(unsigned i=0; i<256; i+=11) + { + for(unsigned j=1; j<256; j++) + { + unsigned q = i / j, r = i % j; + + assert(q * j + r == i); + assert(r >= 0 && r < j); + } + } + + for(unsigned i=0; i<7000; i+=11) + { + for(unsigned j=1; j= 0 && r < j); + } + } + + for(unsigned i=0; i<64000; i+=121) + { + for(unsigned j=1; j= 0 && r < j); + } + } + + return 0; +} diff --git a/oscar64/autotest/enumswitch.c b/oscar64/autotest/enumswitch.c new file mode 100644 index 0000000..a3a62fc --- /dev/null +++ b/oscar64/autotest/enumswitch.c @@ -0,0 +1,23 @@ +enum E { + e1, e2, e3, e4 +}; + +int check(E e) +{ + switch(e) + { + case e1: + return 10; + case e2: + return 20; + case e3: + return 30; + default: + return 100; + } +} + +int main(void) +{ + return check(e1) + check(e2) + check(e3) + check(e4) - 160; +} diff --git a/oscar64/autotest/fastcalltest.c b/oscar64/autotest/fastcalltest.c new file mode 100644 index 0000000..28b91cc --- /dev/null +++ b/oscar64/autotest/fastcalltest.c @@ -0,0 +1,28 @@ +#include +#include + +int p1(int a, int b) +{ + return a + b; +} + +int p2(int a, int b) +{ + return a * b; +} + +int c1(int x) +{ + return x; +} + +int c2(int x) +{ + return c1(x); +} + +int main(void) +{ + return p1(5, p2(c2(2), c2(4))) - 13; +} + diff --git a/oscar64/autotest/fixmathtest.c b/oscar64/autotest/fixmathtest.c new file mode 100644 index 0000000..4a0929f --- /dev/null +++ b/oscar64/autotest/fixmathtest.c @@ -0,0 +1,90 @@ +#include +#include +#include + +unsigned tval[] = { + 1, 2, 16, 128, 255, 256, 4096, 32768, 65535 +}; + +void testmuldiv16u(void) +{ + for (char i=0; i<9; i++) + { + assert(lmuldiv16u(tval[i], 0, tval[i]) == 0); + assert(lmuldiv16u(0, tval[i], tval[i]) == 0); + for(char j=0; j<9; j++) + { + assert(lmuldiv16u(tval[i], tval[j], tval[i]) == tval[j]); + assert(lmuldiv16u(tval[j], tval[i], tval[i]) == tval[j]); + } + } + + for(int i=0; i<10000; i++) + { + unsigned a = rand(); + unsigned b = rand(); + unsigned c = rand(); + if (c > 0) + { + unsigned long d = (unsigned long)a * (unsigned long) b / c; + if (d < 0x10000l) + assert(lmuldiv16u(a, b, c) == d); + } + } +} + +unsigned ival[] = { + 1, 2, 16, 128, 255, 256, 4096, 32767, + -1, -2, -16, -128, -255, -256, -4096, -32767 +}; + +void testmuldiv16s(void) +{ + for (char i=0; i<16; i++) + { + assert(lmuldiv16s(ival[i], 0, ival[i]) == 0); + assert(lmuldiv16s(0, ival[i], ival[i]) == 0); + for(char j=0; j<16; j++) + { + assert(lmuldiv16s(ival[i], ival[j], ival[i]) == ival[j]); + assert(lmuldiv16s(ival[j], ival[i], ival[i]) == ival[j]); + } + } + + for(int i=0; i<10000; i++) + { + int a = rand(); + int b = rand(); + int c = rand(); + + if (c > 0) + { + long d = (long)a * (long)b / c; + if (d >= -32768 && d <= 32767) + assert(lmuldiv16s(a, b, c) == d); + } + } + +} + +void testlmul4f12s(void) +{ + for(int i=0; i<20000; i++) + { + int a = rand(); + int b = rand(); + + long d = ((long)a * (long)b) >> 12; + if (d >= -32768 && d <= 32767) + assert(lmul4f12s(a, b) == d); + } +} + +int main(void) +{ + testlmul4f12s(); + testmuldiv16u(); + testmuldiv16s(); + + return 0; +} \ No newline at end of file diff --git a/oscar64/autotest/floatcmptest.c b/oscar64/autotest/floatcmptest.c new file mode 100644 index 0000000..62f537e --- /dev/null +++ b/oscar64/autotest/floatcmptest.c @@ -0,0 +1,135 @@ +#include +#include +#include + +bool feq(float a, float b) +{ + return a == b; +} + +bool flt(float a, float b) +{ + return a < b; +} + +bool fle(float a, float b) +{ + return a <= b; +} + +bool fgt(float a, float b) +{ + return a > b; +} + +bool fge(float a, float b) +{ + return a >= b; +} + + +volatile float f; + +inline void cmpflt(float a, float b, bool eq, bool lt, bool gt) +{ + bool le = eq || lt; + bool ge = eq || gt; + + assert(feq(a, b) == eq); + assert(flt(a, b) == lt); + assert(fgt(a, b) == gt); + assert(fle(a, b) == le); + assert(fge(a, b) == ge); + + f = a; + + assert(feq(f, b) == eq); + assert(flt(f, b) == lt); + assert(fgt(f, b) == gt); + assert(fle(f, b) == le); + assert(fge(f, b) == ge); + + f = b; + + assert(feq(a, f) == eq); + assert(flt(a, f) == lt); + assert(fgt(a, f) == gt); + assert(fle(a, f) == le); + assert(fge(a, f) == ge); +} + +__noinline void test1(void) +{ + cmpflt( 0.0, 1.0, false, true, false); + cmpflt( 0.0, -1.0, false, false, true); + cmpflt( 1.0, 0.0, false, false, true); + cmpflt(-1.0, 0.0, false, true, false); +} + +__noinline void test2(void) +{ + cmpflt( 1.0, 1.0, true, false, false); + cmpflt( 1.0, 2.0, false, true, false); + cmpflt( 2.0, 1.0, false, false, true); +} + +__noinline void test3(void) +{ + + cmpflt(-1.0, -1.0, true, false, false); + cmpflt(-1.0, -2.0, false, false, true); + cmpflt(-2.0, -1.0, false, true, false); +} + +__noinline void test4(void) +{ + + cmpflt( 1.0, -1.0, false, false, true); + cmpflt( 1.0, -2.0, false, false, true); + cmpflt( 2.0, -1.0, false, false, true); +} + +__noinline void test5(void) +{ + + cmpflt(-1.0, 1.0, false, true, false); + cmpflt(-1.0, 2.0, false, true, false); + cmpflt(-2.0, 1.0, false, true, false); +} + +__noinline void test6(void) +{ + cmpflt( 0.0, 0.0, true, false, false); + cmpflt(-0.0, 0.0, true, false, false); + cmpflt( 0.0, -0.0, true, false, false); + cmpflt(-0.0, -0.0, true, false, false); +} + +__noinline void test7(void) +{ + cmpflt( 1.0, 1.000001, false, true, false); + cmpflt( 1.000001, 1.0, false, false, true); + cmpflt( 1.000001, 1.000001, true, false, false); +} + +__noinline void test8(void) +{ + + cmpflt( -1.0, -1.000001, false, false, true); + cmpflt( -1.000001, -1.0, false, true, false); + cmpflt( -1.000001, -1.000001, true, false, false); +} + +int main(void) +{ + test1(); + test2(); + test3(); + test4(); + test5(); + test6(); + test7(); + test8(); + + return 0; +} diff --git a/oscar64/autotest/floatinttest.c b/oscar64/autotest/floatinttest.c new file mode 100644 index 0000000..47143d0 --- /dev/null +++ b/oscar64/autotest/floatinttest.c @@ -0,0 +1,100 @@ +#include +#include +#include + + +int main(void) +{ + float a; + int i; + long li; + unsigned u; + unsigned long lu; + + a = 1.0; + i = 1; + for(int j=0; j<15; j++) + { + assert(i == (int)a); + assert(a == (float)i); + a *= 2.0; + i <<= 1; + } + + a = -1.0; + i = -1; + for(int j=0; j<15; j++) + { + assert(i == (int)a); + assert(a == (float)i); + a *= 2.0; + i <<= 1; + } + + a = 1.0; + i = 1; + for(int j=0; j<15; j++) + { + assert(i == (int)a); + assert(a == (float)i); + a *= 2.0; + a += 1.0; + i <<= 1; + i += 1; + } + + + a = 1.0; + u = 1; + for(int j=0; j<16; j++) + { + assert(u == (unsigned)a); + assert(a == (float)u); + a *= 2.0; + u <<= 1; + } + + a = 1.0; + u = 1; + for(int j=0; j<16; j++) + { + assert(u == (unsigned)a); + assert(a == (float)u); + a *= 2.0; + a += 1; + u <<= 1; + u += 1; + } + + a = 1.0; + li = 1; + for(int j=0; j<31; j++) + { + assert(li == (long)a); + assert(a == (float)li); + a *= 2.0; + li <<= 1; + } + + a = -1.0; + li = -1; + for(int j=0; j<31; j++) + { + assert(li == (long)a); + assert(a == (float)li); + a *= 2.0; + li <<= 1; + } + + a = 1.0; + lu = 1; + for(int j=0; j<32; j++) + { + assert(lu == (unsigned long)a); + assert(a == (float)lu); + a *= 2.0; + lu <<= 1; + } + + return 0; +} diff --git a/oscar64/autotest/floatmultest.c b/oscar64/autotest/floatmultest.c new file mode 100644 index 0000000..35a1809 --- /dev/null +++ b/oscar64/autotest/floatmultest.c @@ -0,0 +1,30 @@ +#include +#include +#include + +float c = 1.25; +float d = 1.0001; + +int main(void) +{ + int i; + float a = 0.0; + + for(i=0; i<50; i++) + { + printf("%d %f %f %f\n", i, i * c, a, i * c - a); + assert(i * c == a); + a += c; + } + + a = d; + + for(i=1; i<50; i++) + { + printf("%d %f %f %f\n", i, i * d, a, fabs(i * d - a) / i); + assert(fabs(i * d - a) < i * 1.0e-6); + a += d; + } + + return 0; +} diff --git a/oscar64/autotest/floatstringtest.c b/oscar64/autotest/floatstringtest.c new file mode 100644 index 0000000..6a09b29 --- /dev/null +++ b/oscar64/autotest/floatstringtest.c @@ -0,0 +1,32 @@ +#include +#include +#include + +int main(void) +{ + printf("%f\n", 0.0); + + + float x = 1.0, y = 1.0; + + char xb[20], yb[20]; + for(int i=0; i<40; i++) + { + ftoa(x, xb); float xr = atof(xb); + ftoa(y, yb); float yr = atof(yb); + + printf("%20g (%s) %20g : %20g (%s) %20g : %10f %10f \n", x, xb, xr, y, yb, yr, fabs(x - xr) / x, fabs(y - yr) / y); + + if (fabs(x - xr) / x > 0.00001 || fabs(y - yr) / y > 0.00001) + return -1; + + x *= 2.5; + y *= 0.8; + } + + return 0; +} + + + + diff --git a/oscar64/autotest/floortest.c b/oscar64/autotest/floortest.c new file mode 100644 index 0000000..a002c23 --- /dev/null +++ b/oscar64/autotest/floortest.c @@ -0,0 +1,13 @@ +#include +#include + +int main(void) +{ + for(int y=1; y<100; y++) + { + float fz = 100.0 / (float)y; + printf("%d %f %f\n", y, floor(fz * 100.0), fz * 100.0); + } + + return 0; +} diff --git a/oscar64/autotest/funcarraycall.c b/oscar64/autotest/funcarraycall.c new file mode 100644 index 0000000..4ea69f2 --- /dev/null +++ b/oscar64/autotest/funcarraycall.c @@ -0,0 +1,30 @@ + +int (*funcs[10])(int); + +#assign x 0 +#repeat +int f##x(int i) +{ + return i + x; +} +#assign x x + 1 +#until x == 10 + +int test(int k) +{ + for(char i=0; i<10; i++) + k = funcs[i](k); + return k; +} + +int main(void) +{ +#assign x 0 +#repeat + funcs[x] = f##x; +#assign x x + 1 +#until x == 10 + + int k = test(-45); + return k; +} diff --git a/oscar64/autotest/funcvartest.c b/oscar64/autotest/funcvartest.c new file mode 100644 index 0000000..01c7018 --- /dev/null +++ b/oscar64/autotest/funcvartest.c @@ -0,0 +1,72 @@ +#include +#include +#include + + +typedef float (*fop)(float a, float b); + +float fadd(float a, float b) +{ + return a + b; +} + +float fmul(float a, float b) +{ + return a * b; +} + + +struct FNode +{ + FNode * left, * right; + float value; + fop call; +}; + +FNode * root; + +float fevalB(FNode * f) +{ + if (f->call) + return f->call(fevalB(f->left), fevalB(f->right)); + else + return f->value; +} + +float fevalN(FNode * f) +{ + if (f->call) + return f->call(fevalN(f->left), fevalN(f->right)); + else + return f->value; +} + +#pragma native(fevalN) + +FNode * bop(fop call, FNode * left, FNode * right) +{ + FNode * f = (FNode *)malloc(sizeof(FNode)); + f->call = call; + f->left = left; + f->right = right; + return f; +} + +FNode * bc(float value) +{ + FNode * f = (FNode *)malloc(sizeof(FNode)); + f->value = value; + return f; +} + +int main(void) +{ + FNode * tree = bop(fadd, bop(fmul, bc(3), bc(5)), bc(6)); + + printf("Eval %f, %d\n", fevalB(tree), fevalN(tree)); + + assert(fevalB(tree) == 3 * 5 + 6); + assert(fevalN(tree) == 3 * 5 + 6); + + return 0; +} diff --git a/oscar64/autotest/incvector.c b/oscar64/autotest/incvector.c new file mode 100644 index 0000000..4757389 --- /dev/null +++ b/oscar64/autotest/incvector.c @@ -0,0 +1,22 @@ + + +void incv(int * a, int n) +{ + for(int i=0; i +#include + + +char * const Hires = (char *)0x4000; +Bitmap Screen; +ClipRect cr = {0, 0, 320, 200}; + + + +int main(void) +{ + bm_init(&Screen, Hires, 40, 25); + + bmu_rect_clear(&Screen, 0, 0, 320, 200); + + bm_line(&Screen, &cr, 0, 0, 199, 199, 0xff, LINOP_SET); + + for(int i=0; i<200; i++) + { + assert(Hires[(i & 7) + 320 * (i >> 3) + (i & ~7)] == 0x80 >> (i & 7)); + } + + return 0; +} diff --git a/oscar64/autotest/longcodetest.c b/oscar64/autotest/longcodetest.c new file mode 100644 index 0000000..493bcd0 --- /dev/null +++ b/oscar64/autotest/longcodetest.c @@ -0,0 +1,30 @@ +#include + +char a[200], b[200]; +bool ok = true; + +int main(void) +{ +#assign ni 0 +#repeat + a[ni] = ni & 255; +#assign ni ni + 1 +#until ni == 200 + +#assign ni 0 +#repeat + if (ok) + b[ni] = ni & 255; +#assign ni ni + 1 +#until ni == 200 + + int asum = 0, bsum = 0, csum = 0; + for(int i=0; i<200; i++) + { + asum += a[i]; + bsum += b[i]; + csum += i & 255; + } + + return asum + bsum - 2 * csum; +} diff --git a/oscar64/autotest/loopboundtest.c b/oscar64/autotest/loopboundtest.c new file mode 100644 index 0000000..5322877 --- /dev/null +++ b/oscar64/autotest/loopboundtest.c @@ -0,0 +1,94 @@ +#include + +struct S +{ + int a[100]; +}; + +int lts(S * s) +{ + int x = 0; + for(int i=0; i<100; i++) + x += s->a[i]; + return x; +} + +int les(S * s) +{ + int x = 0; + for(int i=0; i<=99; i++) + x += s->a[i]; + return x; +} + +int gts(S * s) +{ + int x = 0; + for(int i=100; i>0; i--) + x += s->a[i-1]; + return x; +} + +int ges(S * s) +{ + int x = 0; + for(int i=99; i>=0; i--) + x += s->a[i]; + return x; +} + +int ltu(S * s) +{ + unsigned x = 0; + for(int i=0; i<100; i++) + x += s->a[i]; + return x; +} + +int leu(S * s) +{ + unsigned x = 0; + for(int i=0; i<=99; i++) + x += s->a[i]; + return x; +} + +int gtu(S * s) +{ + unsigned x = 0; + for(int i=100; i>0; i--) + x += s->a[i-1]; + return x; +} + +int geu(S * s) +{ + unsigned x = 0; + for(int i=100; i>=1; i--) + x += s->a[i-1]; + return x; +} + +int main(void) +{ + S s; + + int k = 0; + for(int i=0; i<100; i++) + { + int t = (i & 15) + 3; + s.a[i] = t; + k += t; + } + + assert(lts(&s) == k); + assert(les(&s) == k); + assert(gts(&s) == k); + assert(ges(&s) == k); + assert(ltu(&s) == k); + assert(leu(&s) == k); + assert(gtu(&s) == k); + assert(geu(&s) == k); + + return 0; +} diff --git a/oscar64/autotest/loopdomtest.c b/oscar64/autotest/loopdomtest.c new file mode 100644 index 0000000..76a946f --- /dev/null +++ b/oscar64/autotest/loopdomtest.c @@ -0,0 +1,28 @@ +#include +#include + + +int main(void) +{ + int a[10][10]; + + for(int i=0; i<10; i++) + { + for(int j=0; j<10; j++) + { + a[i][j] = i + j; + } + } + + int s = 0; + + for(int i=0; i<10; i++) + { + s += a[i][i]; + } + + + assert(s == 90); + + return 0; +} diff --git a/oscar64/autotest/loopunrolltest.cpp b/oscar64/autotest/loopunrolltest.cpp new file mode 100644 index 0000000..5088202 --- /dev/null +++ b/oscar64/autotest/loopunrolltest.cpp @@ -0,0 +1,390 @@ +#include + +void unroll1(void) +{ + int sum0 = 0; + int sum2 = 0; + int sum3 = 0; + int sum10 = 0; + int sumfull = 0; + + for(int i=0; i<100; i++) + sum0 += i; + #pragma unroll(2) + for(int i=0; i<100; i++) + sum2 += i; + #pragma unroll(3) + for(int i=0; i<100; i++) + sum3 += i; + #pragma unroll(10) + for(int i=0; i<100; i++) + sum10 += i; + #pragma unroll(full) + for(int i=0; i<100; i++) + sumfull += i; + assert(sum2 == sum0); + assert(sum3 == sum0); + assert(sum10 == sum0); + assert(sumfull == sum0); +} + +void unroll2(void) +{ + int sum0 = 0; + int sum2 = 0; + int sum3 = 0; + int sum10 = 0; + int sumfull = 0; + + for(int i=0; i<100; i+=2) + sum0 += i; + #pragma unroll(2) + for(int i=0; i<100; i+=2) + sum2 += i; + #pragma unroll(3) + for(int i=0; i<100; i+=2) + sum3 += i; + #pragma unroll(10) + for(int i=0; i<100; i+=2) + sum10 += i; + #pragma unroll(full) + for(int i=0; i<100; i+=2) + sumfull += i; + assert(sum2 == sum0); + assert(sum3 == sum0); + assert(sum10 == sum0); + assert(sumfull == sum0); +} + +void unroll3(void) +{ + int sum0 = 0; + int sum2 = 0; + int sum3 = 0; + int sum10 = 0; + int sumfull = 0; + + for(int i=0; i<100; i+=3) + sum0 += i; + #pragma unroll(2) + for(int i=0; i<100; i+=3) + sum2 += i; + #pragma unroll(3) + for(int i=0; i<100; i+=3) + sum3 += i; + #pragma unroll(10) + for(int i=0; i<100; i+=3) + sum10 += i; + #pragma unroll(full) + for(int i=0; i<100; i+=3) + sumfull += i; + assert(sum2 == sum0); + assert(sum3 == sum0); + assert(sum10 == sum0); + assert(sumfull == sum0); +} + +void unroll50(void) +{ + int sum0 = 0; + int sum2 = 0; + int sum3 = 0; + int sum10 = 0; + int sumfull = 0; + + for(int i=0; i<100; i+=50) + sum0 += i; + #pragma unroll(2) + for(int i=0; i<100; i+=50) + sum2 += i; + #pragma unroll(3) + for(int i=0; i<100; i+=50) + sum3 += i; + #pragma unroll(10) + for(int i=0; i<100; i+=50) + sum10 += i; + #pragma unroll(full) + for(int i=0; i<100; i+=50) + sumfull += i; + assert(sum2 == sum0); + assert(sum3 == sum0); + assert(sum10 == sum0); + assert(sumfull == sum0); +} + +void unroll50e(void) +{ + int sum0 = 0; + int sum2 = 0; + int sum3 = 0; + int sum10 = 0; + int sumfull = 0; + + for(int i=0; i<=100; i+=50) + sum0 += i; + #pragma unroll(2) + for(int i=0; i<=100; i+=50) + sum2 += i; + #pragma unroll(3) + for(int i=0; i<=100; i+=50) + sum3 += i; + #pragma unroll(10) + for(int i=0; i<=100; i+=50) + sum10 += i; + #pragma unroll(full) + for(int i=0; i<=100; i+=50) + sumfull += i; + + assert(sum2 == sum0); + assert(sum3 == sum0); + assert(sum10 == sum0); + assert(sumfull == sum0); +} + +void unroll100(void) +{ + int sum0 = 0; + int sum2 = 0; + int sum3 = 0; + int sum10 = 0; + int sumfull = 0; + + for(int i=0; i<100; i+=100) + sum0 += i; + #pragma unroll(2) + for(int i=0; i<100; i+=100) + sum2 += i; + #pragma unroll(3) + for(int i=0; i<100; i+=100) + sum3 += i; + #pragma unroll(10) + for(int i=0; i<100; i+=100) + sum10 += i; + #pragma unroll(full) + for(int i=0; i<100; i+=100) + sumfull += i; + assert(sum2 == sum0); + assert(sum3 == sum0); + assert(sum10 == sum0); + assert(sumfull == sum0); +} + + +void unrolld1(void) +{ + int sum0 = 0; + int sum2 = 0; + int sum3 = 0; + int sum10 = 0; + int sumfull = 0; + + for(int i=100; i>0; i--) + sum0 += i; + #pragma unroll(2) + for(int i=100; i>0; i--) + sum2 += i; + #pragma unroll(3) + for(int i=100; i>0; i--) + sum3 += i; + #pragma unroll(10) + for(int i=100; i>0; i--) + sum10 += i; + #pragma unroll(full) + for(int i=100; i>0; i--) + sumfull += i; + assert(sum2 == sum0); + assert(sum3 == sum0); + assert(sum10 == sum0); + assert(sumfull == sum0); +} + +void unrolld2(void) +{ + int sum0 = 0; + int sum2 = 0; + int sum3 = 0; + int sum10 = 0; + int sumfull = 0; + + for(int i=100; i>0; i-=2) + sum0 += i; + #pragma unroll(2) + for(int i=100; i>0; i-=2) + sum2 += i; + #pragma unroll(3) + for(int i=100; i>0; i-=2) + sum3 += i; + #pragma unroll(10) + for(int i=100; i>0; i-=2) + sum10 += i; + #pragma unroll(full) + for(int i=100; i>0; i-=2) + sumfull += i; + assert(sum2 == sum0); + assert(sum3 == sum0); + assert(sum10 == sum0); + assert(sumfull == sum0); +} + +void unrolld3(void) +{ + int sum0 = 0; + int sum2 = 0; + int sum3 = 0; + int sum10 = 0; + int sumfull = 0; + + for(int i=100; i>0; i-=3) + sum0 += i; + #pragma unroll(2) + for(int i=100; i>0; i-=3) + sum2 += i; + #pragma unroll(3) + for(int i=100; i>0; i-=3) + sum3 += i; + #pragma unroll(10) + for(int i=100; i>0; i-=3) + sum10 += i; + #pragma unroll(full) + for(int i=100; i>0; i-=3) + sumfull += i; + assert(sum2 == sum0); + assert(sum3 == sum0); + assert(sum10 == sum0); + assert(sumfull == sum0); +} + +void unrolld50(void) +{ + int sum0 = 0; + int sum2 = 0; + int sum3 = 0; + int sum10 = 0; + int sumfull = 0; + + for(int i=100; i>0; i-=50) + sum0 += i; + #pragma unroll(2) + for(int i=100; i>0; i-=50) + sum2 += i; + #pragma unroll(3) + for(int i=100; i>0; i-=50) + sum3 += i; + #pragma unroll(10) + for(int i=100; i>0; i-=50) + sum10 += i; + #pragma unroll(full) + for(int i=100; i>0; i-=50) + sumfull += i; + assert(sum2 == sum0); + assert(sum3 == sum0); + assert(sum10 == sum0); + assert(sumfull == sum0); +} + +void unrolld50e(void) +{ + int sum0 = 0; + int sum2 = 0; + int sum3 = 0; + int sum10 = 0; + int sumfull = 0; + + for(int i=100; i>=0; i-=50) + sum0 += i; + #pragma unroll(2) + for(int i=100; i>=0; i-=50) + sum2 += i; + #pragma unroll(3) + for(int i=100; i>=0; i-=50) + sum3 += i; + #pragma unroll(10) + for(int i=100; i>=0; i-=50) + sum10 += i; + #pragma unroll(full) + for(int i=100; i>=0; i-=50) + sumfull += i; + assert(sum2 == sum0); + assert(sum3 == sum0); + assert(sum10 == sum0); + assert(sumfull == sum0); +} + +void unrolld100(void) +{ + int sum0 = 0; + int sum2 = 0; + int sum3 = 0; + int sum10 = 0; + int sumfull = 0; + + for(int i=100; i>0; i-=100) + sum0 += i; + #pragma unroll(2) + for(int i=100; i>0; i-=100) + sum2 += i; + #pragma unroll(3) + for(int i=100; i>0; i-=100) + sum3 += i; + #pragma unroll(10) + for(int i=100; i>0; i-=100) + sum10 += i; + #pragma unroll(full) + for(int i=100; i>0; i-=100) + sumfull += i; + assert(sum2 == sum0); + assert(sum3 == sum0); + assert(sum10 == sum0); + assert(sumfull == sum0); +} + + +char glob[20]; + +void autounroll(void) +{ + char sum0 = 0; + char sum1 = 0; + char sum2 = 0; + char sum3 = 0; + char sum4 = 0; + + for(int i=0; i<10; i++) + sum0 += glob[i]; +// for(int i=10; i<20; i++) +// sum1 += glob[i - 10]; + for(int i=10; i>0; i--) + sum2 += glob[i - 1]; + for(int i=9; i>=0; i--) + sum3 += glob[i]; +// for(int i=0; i<=9; i++) +// sum4 += glob[i]; + +// assert(sum1 == sum0); + assert(sum2 == sum0); +#if 0 + assert(sum3 == sum0); + assert(sum4 == sum0); +#endif +} + +int main(void) +{ + for(int i=0; i<20; i++) + glob[i] = i; +#if 0 + unroll1(); + unroll2(); + unroll3(); + unroll50(); + unroll50e(); + unroll100(); + unrolld1(); + unrolld2(); + unrolld3(); + unrolld50(); + unrolld50e(); + unrolld100(); +#endif + autounroll(); +} diff --git a/oscar64/autotest/makefile b/oscar64/autotest/makefile new file mode 100644 index 0000000..59d6493 --- /dev/null +++ b/oscar64/autotest/makefile @@ -0,0 +1,92 @@ +SRCS=$(filter-out ambiguousoverload_trigger.c opp_part1.cpp opp_part2.cpp, $(wildcard *.c *.cpp)) +EXES=$(patsubst %.c,%,$(SRCS)) +EXES:=$(patsubst %.cpp,%,$(EXES)) + +all: $(EXES) + +%: %.c + $(OSCAR64_CC) -ea -g -bc $< + $(OSCAR64_CC) -ea -g -n $< + $(OSCAR64_CC) -ea -g -O2 -bc $< + $(OSCAR64_CC) -ea -g -O2 -n $< + $(OSCAR64_CC) -ea -g -O0 -bc $< + $(OSCAR64_CC) -ea -g -O0 -n $< + $(OSCAR64_CC) -ea -g -Os -bc $< + $(OSCAR64_CC) -ea -g -Os -n $< + $(OSCAR64_CC) -ea -g -O3 -bc $< + $(OSCAR64_CC) -ea -g -O3 -n $< + +%: %.cpp + $(OSCAR64_CXX) -ea -g -bc $< + $(OSCAR64_CXX) -ea -g -n $< + $(OSCAR64_CXX) -ea -g -O2 -bc $< + $(OSCAR64_CXX) -ea -g -O2 -n $< + $(OSCAR64_CXX) -ea -g -O0 -bc $< + $(OSCAR64_CXX) -ea -g -O0 -n $< + $(OSCAR64_CXX) -ea -g -Os -bc $< + $(OSCAR64_CXX) -ea -g -Os -n $< + $(OSCAR64_CXX) -ea -g -O3 -bc $< + $(OSCAR64_CXX) -ea -g -O3 -n $< + +# testb +bitshifttest: bitshifttest.c + $(OSCAR64_CC) -ea -g -bc $< + $(OSCAR64_CC) -ea -g -bc -O2 $< + $(OSCAR64_CC) -ea -g -bc -O0 $< + $(OSCAR64_CC) -ea -g -bc -Os $< + $(OSCAR64_CC) -ea -g -bc -O3 $< + $(OSCAR64_CC) -ea -g -n $< + +# testn +stripedarraytest: stripedarraytest.c + $(OSCAR64_CC) -ea -g -n $< + $(OSCAR64_CC) -ea -g -O2 -n $< + $(OSCAR64_CC) -ea -g -O0 -n $< + $(OSCAR64_CC) -ea -g -Os -n $< + $(OSCAR64_CC) -ea -g -O3 -n $< + +stripedstructtest: stripedstructtest.c + $(OSCAR64_CC) -ea -g -n $< + $(OSCAR64_CC) -ea -g -O2 -n $< + $(OSCAR64_CC) -ea -g -O0 -n $< + $(OSCAR64_CC) -ea -g -Os -n $< + $(OSCAR64_CC) -ea -g -O3 -n $< + +autorefreturn: autorefreturn.cpp + $(OSCAR64_CC) -ea -g -n $< + $(OSCAR64_CC) -ea -g -O2 -n $< + $(OSCAR64_CC) -ea -g -O0 -n $< + $(OSCAR64_CC) -ea -g -Os -n $< + $(OSCAR64_CC) -ea -g -O3 -n $< + +copyconstructor: copyconstructor.cpp + $(OSCAR64_CC) -ea -g -n $< + $(OSCAR64_CC) -ea -g -O2 -n $< + $(OSCAR64_CC) -ea -g -O0 -n $< + $(OSCAR64_CC) -ea -g -Os -n $< + $(OSCAR64_CC) -ea -g -O3 -n $< + +andmultest: andmultest.cpp + $(OSCAR64_CC) -ea -g -n $< + $(OSCAR64_CC) -ea -g -O2 -n $< + $(OSCAR64_CC) -ea -g -O0 -n $< + $(OSCAR64_CC) -ea -g -Os -n $< + $(OSCAR64_CC) -ea -g -O3 -n $< + +# testi +volatiletest: volatiletest.c + $(OSCAR64_CC) -eai -g -n $< + $(OSCAR64_CC) -eai -g -O2 -n $< + $(OSCAR64_CC) -eai -g -O0 -n $< + $(OSCAR64_CC) -eai -g -Os -n $< + $(OSCAR64_CC) -eai -g -O3 -n $< + +variadic-macros: variadic_macros.c + $(OSCAR64_CC) -eai -g -n $< + $(OSCAR64_CC) -eai -g -O2 -n $< + $(OSCAR64_CC) -eai -g -O0 -n $< + $(OSCAR64_CC) -eai -g -Os -n $< + $(OSCAR64_CC) -eai -g -O3 -n $< + +clean: + @$(RM) *.asm *.bcs *.int *.lbl *.map *.prg diff --git a/oscar64/autotest/maskcheck.c b/oscar64/autotest/maskcheck.c new file mode 100644 index 0000000..7fddbc0 --- /dev/null +++ b/oscar64/autotest/maskcheck.c @@ -0,0 +1,25 @@ +#include + + + +void check_byte(char a) +{ + char b1[8] = {0}, n1[8] = {0}, b0[8] = {0}, n0[8] = {0}; + +#for(i, 8) if ((a & (1 << i)) == (1 << i)) b1[i]++; +#for(i, 8) if ((a & (1 << i)) != (1 << i)) n1[i]++; +#for(i, 8) if ((a & (1 << i)) == 0) b0[i]++; +#for(i, 8) if ((a & (1 << i)) != 0) n0[i]++; + + for(char i=0; i<8; i++) + { + assert(b0[i] == n1[i]); + assert(b1[i] == n0[i]); + } +} + +int main(void) +{ + for(int i=0; i<256; i++) + check_byte(i); +} \ No newline at end of file diff --git a/oscar64/autotest/mathtest.c b/oscar64/autotest/mathtest.c new file mode 100644 index 0000000..27b803e --- /dev/null +++ b/oscar64/autotest/mathtest.c @@ -0,0 +1,32 @@ +#include +#include +#include + +int main(void) +{ + for(int i=0; i<1000; i++) + { + float w = 0.0123 * i; + float co = cos(w), si = sin(w); + + float r = co * co + si * si; + + assert(fabs(r - 1) < 0.001); + + assert(fabs(sqrt(w * w) - w) < 0.001); + + float aw = atan2(si, co); + + float co2 = cos(aw), si2 = sin(aw); + + assert(fabs(co2 - co) < 0.001); + assert(fabs(si2 - si) < 0.001); + + float ex = exp(w), em = exp(-w); + + assert(fabs(log(ex) - w) < 0.001); + assert(fabs(log(em) + w) < 0.001); + } + + return 0; +} diff --git a/oscar64/autotest/memchrtest.c b/oscar64/autotest/memchrtest.c new file mode 100644 index 0000000..c86051e --- /dev/null +++ b/oscar64/autotest/memchrtest.c @@ -0,0 +1,103 @@ +#include +#include +#include + +char testmem[2000]; + +int main(void) +{ + // Test memchr with basic functionality + assert(memcmp(memchr("hello", 'h', 5), "hello", 5) == 0); + assert(memcmp(memchr("hello", 'e', 5), "ello", 4) == 0); + assert(memcmp(memchr("hello", 'l', 5), "llo", 3) == 0); // finds first 'l' + assert(memcmp(memchr("hello", 'o', 5), "o", 1) == 0); + assert(memchr("hello", 'x', 5) == 0); + + // Test memchr with size limits + assert(memchr("hello", 'o', 4) == 0); // 'o' is at position 4, but size is 4 (0-3) + assert(memcmp(memchr("hello", 'l', 3), "llo", 3) == 0); // finds 'l' at position 2 within size 3 + assert(memchr("hello", 'e', 1) == 0); // 'e' is at position 1, but size is 1 (0 only) + + // Test memchr with zero size + assert(memchr("hello", 'h', 0) == 0); + assert(memchr("hello", 'x', 0) == 0); + + // Test memchr with null character + assert(*((char*)memchr("hello", '\0', 6)) == '\0'); // finds null terminator + assert(memchr("hello", '\0', 5) == 0); // null is at position 5, size is 5 (0-4) + + // Test memchr with single byte + assert(memcmp(memchr("a", 'a', 1), "a", 1) == 0); + assert(memchr("a", 'b', 1) == 0); + + // Test memchr with repeated characters + assert(memcmp(memchr("aaaaaa", 'a', 6), "aaaaaa", 6) == 0); // finds first occurrence + assert(memcmp(memchr("abcabc", 'a', 6), "abcabc", 6) == 0); // finds first 'a' + assert(memcmp(memchr("abcabc", 'b', 6), "bcabc", 5) == 0); // finds first 'b' + assert(memcmp(memchr("abcabc", 'c', 6), "cabc", 4) == 0); // finds first 'c' + + // Test memchr with special characters and binary data + assert(memcmp(memchr("hello world", ' ', 11), " world", 6) == 0); + assert(memcmp(memchr("a.b,c;d", '.', 7), ".b,c;d", 6) == 0); + assert(memcmp(memchr("a.b,c;d", ',', 7), ",c;d", 4) == 0); + assert(memcmp(memchr("a.b,c;d", ';', 7), ";d", 2) == 0); + + // Test memchr with binary data (including zeros) + testmem[0] = 'a'; + testmem[1] = '\0'; + testmem[2] = 'b'; + testmem[3] = '\0'; + testmem[4] = 'c'; + + assert(memchr(testmem, 'a', 5) == testmem); + assert(memchr(testmem, '\0', 5) == testmem + 1); // finds first null + assert(memchr(testmem, 'b', 5) == testmem + 2); + assert(memchr(testmem, 'c', 5) == testmem + 4); + assert(memchr(testmem, 'd', 5) == 0); + + // Test memchr with different byte values + for(int i = 0; i < 256; i++) + { + testmem[i] = (unsigned char)i; + } + + // Test finding various byte values + assert(memchr(testmem, 0, 256) == testmem); // byte 0 at position 0 + assert(memchr(testmem, 1, 256) == testmem + 1); // byte 1 at position 1 + assert(memchr(testmem, 127, 256) == testmem + 127); // byte 127 at position 127 + assert(memchr(testmem, 255, 256) == testmem + 255); // byte 255 at position 255 + + // Test memchr with size smaller than target position + assert(memchr(testmem, 100, 50) == 0); // byte 100 is at position 100, but size is 50 + assert(memchr(testmem, 100, 101) == testmem + 100); // finds it with adequate size + + // Test memchr with longer memory region + for(int i = 0; i < 1000; i++) + { + testmem[i] = 'a' + (i & 7); + } + + // Test memchr with pattern + assert(memchr(testmem, 'a', 1000) == testmem); // first 'a' at start + assert(memchr(testmem, 'b', 1000) == testmem + 1); // first 'b' at position 1 + assert(memchr(testmem, 'h', 1000) == testmem + 7); // first 'h' at position 7 + assert(memchr(testmem, 'z', 1000) == 0); // 'z' not in pattern + + // Test memchr starting from middle of buffer + assert(memchr(testmem + 100, 'a', 900) == testmem + 104); // next 'a' after position 100 + assert(memchr(testmem + 100, 'c', 900) == testmem + 106); // next 'c' after position 100 + + // Test memchr with exact size to find character + assert(memchr(testmem, 'h', 8) == testmem + 7); // 'h' at position 7, size exactly 8 + assert(memchr(testmem, 'h', 7) == 0); // 'h' at position 7, size only 7 (0-6) + + // Test memchr character not found in range + for(int i = 0; i < 100; i++) + { + testmem[i] = 'x'; // fill with 'x' + } + assert(memchr(testmem, 'y', 100) == 0); // 'y' not found + assert(memchr(testmem, 'x', 100) == testmem); // 'x' found at start + + return 0; +} diff --git a/oscar64/autotest/memmovetest.c b/oscar64/autotest/memmovetest.c new file mode 100644 index 0000000..db02a7c --- /dev/null +++ b/oscar64/autotest/memmovetest.c @@ -0,0 +1,49 @@ +#include +#include +#include + +unsigned b[4096]; + +void testfwd(unsigned sz) +{ + for(unsigned i=0; i<4096; i++) + b[i] = i; + + memmove(b + 100, b + 101, 2 * sz); + + for(unsigned i=0; i<100; i++) + assert(b[i] == i); + for(unsigned i=100; i<100 + sz; i++) + assert(b[i] == i + 1); + for(unsigned i=100 + sz; i<4096; i++) + assert(b[i] == i); +} + +void testback(unsigned sz) +{ + for(unsigned i=0; i<4096; i++) + b[i] = i; + + memmove(b + 101, b + 100, 2 * sz); + + for(unsigned i=0; i<101; i++) + assert(b[i] == i); + for(unsigned i=101; i<101 + sz; i++) + assert(b[i] == i - 1); + for(unsigned i=101 + sz; i<4096; i++) + assert(b[i] == i); +} + +int main(void) +{ + for(unsigned i=1; i<2048; i *= 2) + { + testfwd(i - 1); + testfwd(i); + testback(i); + testback(i - 1); + } + + return 0; +} + diff --git a/oscar64/autotest/mixsigncmptest.c b/oscar64/autotest/mixsigncmptest.c new file mode 100644 index 0000000..30466db --- /dev/null +++ b/oscar64/autotest/mixsigncmptest.c @@ -0,0 +1,64 @@ +#include + +int main(void) +{ + unsigned n1, n0; + + n1 = 0; n0 = 0; + for(int i=-1000; i<2000; i+=37) + { + for(char j=0; j<255; j++) + { + if (i < j) + n1++; + else + n0++; + } + } + + assert(n1 == 7893 && n0 == 13017); + + n1 = 0; n0 = 0; + for(int i=-1000; i<2000; i+=37) + { + for(char j=0; j<255; j++) + { + if (i <= j) + n1++; + else + n0++; + } + } + + assert(n1 == 7899 && n0 == 13011); + + n1 = 0; n0 = 0; + for(int i=-1000; i<2000; i+=37) + { + for(char j=0; j<255; j++) + { + if (i >= j) + n1++; + else + n0++; + } + } + + assert(n0 == 7893 && n1 == 13017); + + n1 = 0; n0 = 0; + for(int i=-1000; i<2000; i+=37) + { + for(char j=0; j<255; j++) + { + if (i > j) + n1++; + else + n0++; + } + } + + assert(n0 == 7899 && n1 == 13011); + + return 0; +} \ No newline at end of file diff --git a/oscar64/autotest/mmultest.c b/oscar64/autotest/mmultest.c new file mode 100644 index 0000000..edcb10c --- /dev/null +++ b/oscar64/autotest/mmultest.c @@ -0,0 +1,43 @@ +#include +#include +#include + +Matrix4 ml, mr; + +int main(void) +{ + for(char i=0; i<16; i++) + { + for(char j=0; j<16; j++) + { + for(char k=0; k<16; k++) + { + ml.m[k] = (i == k) ? 1.0 : 0.0; + mr.m[k] = (j == k) ? 1.0 : 0.0; + } + + mat4_mmul(&ml, &mr); + +#if 0 + printf("%d, %d\n", i, j); + for(char k=0; k<16; k++) + printf("%f ", ml.m[k]); + printf("\n"); +#endif + + for(char k=0; k<16; k++) + { + char ix = i & 3, iy = i >> 2; + char jx = j & 3, jy = j >> 2; + char kx = k & 3, ky = k >> 2; + + if (ky == jy && kx == ix && jx == iy) + assert(ml.m[k] == 1.0); + else + assert(ml.m[k] == 0.0); + } + } + } + + return 0; +} diff --git a/oscar64/autotest/operatoroverload.cpp b/oscar64/autotest/operatoroverload.cpp new file mode 100644 index 0000000..93c02bb --- /dev/null +++ b/oscar64/autotest/operatoroverload.cpp @@ -0,0 +1,90 @@ +#include + +struct A +{ + int n; + + A(int n_) + : n(n_) {} + + A(const A & a) + : n(a.n) {} + + A operator+(const A & a) const + { + return A(n + a.n); + } + + A operator-(const A & a) const + { + return A(n - a.n); + } + + A & operator+=(const A & a) + { + n += a.n; + return *this; + } + + A & operator-=(const A & a) + { + n -= a.n; + return *this; + } + + A operator-(void) const + { + return A(-n); + } + + A & operator++(void) + { + n++; + return *this; + } + + A & operator--(void) + { + n--; + return *this; + } + + A operator++(int); + + A operator--(int); + +}; + +A A::operator++(int) +{ + A a(*this); + n++; + return a; +} + +A A::operator--(int) +{ + A a(*this); + n--; + return a; +} + +int main(void) +{ + A a(7), b(8), c(9); + + assert((++a).n == 8); + assert(a.n == 8); + + assert((--a).n == 7); + assert(a.n == 7); + + assert((a++).n == 7); + assert(a.n == 8); + assert((a--).n == 8); + assert(a.n == 7); + + assert((a + b - c + -a + -b - -c).n == 0); + + return 0; +} diff --git a/oscar64/autotest/opp_array.cpp b/oscar64/autotest/opp_array.cpp new file mode 100644 index 0000000..b4d0984 --- /dev/null +++ b/oscar64/autotest/opp_array.cpp @@ -0,0 +1,23 @@ +#include +#include + +int main(void) +{ + opp::array a10; + opp::array a20; + + for(int i=0; i<10; i++) + a10[i] = i; + for(int i=0; i<20; i++) + a20[i] = i; + + int s = 0; + for(int i=0; i<10; i++) + s += a10[i]; + for(int i=10; i<20; i++) + s -= a20[i]; + + assert(s == -100); + + return 0; +} diff --git a/oscar64/autotest/opp_functional.cpp b/oscar64/autotest/opp_functional.cpp new file mode 100644 index 0000000..497e69e --- /dev/null +++ b/oscar64/autotest/opp_functional.cpp @@ -0,0 +1,46 @@ +#include + +class Node +{ +public: + virtual int eval(void) const = 0; +}; + +class ConstNode : public Node +{ +private: + int v; +public: + ConstNode(int v_) : v(v_) {} + virtual int eval(void) const + { + return v; + } +}; + +class BinaryNode : public Node +{ +private: + opp::function op; + Node * left, * right; +public: + BinaryNode(opp::function op_, Node * left_, Node * right_); + + virtual int eval(void) const + { + return op(left->eval(), right->eval()); + } +}; + +inline BinaryNode::BinaryNode(opp::function op_, Node * left_, Node * right_) + : op(op_), left(left_), right(right_) {} + +int main(void) +{ + Node * s1 = + new BinaryNode([=](int a, int b){return a + b;}, + new ConstNode(7), new ConstNode(11) + ); + + return s1->eval() - 18; +} diff --git a/oscar64/autotest/opp_hashmap.cpp b/oscar64/autotest/opp_hashmap.cpp new file mode 100644 index 0000000..156c313 --- /dev/null +++ b/oscar64/autotest/opp_hashmap.cpp @@ -0,0 +1,153 @@ +#include +#include +#include +#include + +void test_int_int(void) +{ + opp::hashmap hm; + for(int i=0; i<100; i++) + hm.at(i) = (long)i * (long)i; + + int sum = 0; + for(auto [k, v] : hm) + { + sum += k; + assert(v == (long)k * (long)k); + } + assert(sum == 4950); + + auto it = hm.begin(); + while (it != hm.end()) + { + if (it->key & 1) + it = hm.erase(it); + else + it++; + } + + sum = 0; + for(auto [k, v] : hm) + { + sum += k; + assert(v == (long)k * (long)k); + } + assert(sum == 2450); + + sum = 0; + for(int i=0; i<100; i++) + { + auto it = hm.find(i); + if (it != hm.end()) + { + assert(it->value == (long)it->key * (long)it->key); + sum += it->key; + } + } + assert(sum == 2450); +} + +void test_string_string(void) +{ + opp::hashmap hm; + for(int i=0; i<100; i++) + hm.at(opp::to_string(i)) = opp::to_string((long)i * (long)i); + + int sum = 0; + for(auto [k, v] : hm) + { + long l = v.to_long(); + int i = k.to_int(); + sum += i; + assert(l == (long)i * (long)i); + } + assert(sum == 4950); + + auto it = hm.begin(); + while (it != hm.end()) + { + long i = it->key.to_int(); + if (i & 1) + it = hm.erase(it); + else + it++; + } + + sum = 0; + for(auto [k, v] : hm) + { + long l = v.to_long(); + long i = k.to_int(); + sum += i; + assert(l == (long)i * (long)i); + } + assert(sum == 2450); + + sum = 0; + for(int i=0; i<100; i++) + { + auto it = hm.find(opp::to_string(i)); + if (it != hm.end()) + { + long l = it->value.to_long(); + long k = it->key.to_int(); + assert(l == (long)k * (long)k); + sum += k; + } + } + assert(sum == 2450); +} + +void test_int_string(void) +{ + opp::hashmap hm; + for(int i=0; i<100; i++) + hm.at(i) = opp::to_string((long)i * (long)i); + + int sum = 0; + for(auto [k, v] : hm) + { + sum += k; + long l = v.to_long(); + assert(l == (long)k * (long)k); + } + assert(sum == 4950); + + auto it = hm.begin(); + while (it != hm.end()) + { + if (it->key & 1) + it = hm.erase(it); + else + it++; + } + + sum = 0; + for(auto [k, v] : hm) + { + sum += k; + long l = v.to_long(); + assert(l == (long)k * (long)k); + } + assert(sum == 2450); + + sum = 0; + for(int i=0; i<100; i++) + { + auto it = hm.find(i); + if (it != hm.end()) + { + long l = it->value.to_long(); + assert(l == (long)it->key * (long)it->key); + sum += it->key; + } + } + assert(sum == 2450); +} + +int main(void) +{ + test_int_int(); + test_int_string(); + test_string_string(); +} diff --git a/oscar64/autotest/opp_list.cpp b/oscar64/autotest/opp_list.cpp new file mode 100644 index 0000000..9118b43 --- /dev/null +++ b/oscar64/autotest/opp_list.cpp @@ -0,0 +1,57 @@ +#include +#include +#include +#include + +int main(void) +{ + opp::list a; + + for(int i=0; i<10; i++) + a.push_back(i); + + int s = 0; + for(auto i : a) + s += i; + + assert(s == 45); + + auto li = a.begin(); + for(int i=0; i<5; i++) + { + li = a.erase(li); + li++; + } + + s = 0; + for(auto i : a) + s += i; + + assert(s == 1 + 3 + 5 + 7 + 9); + + opp::list b; + + b = a; + + s = 0; + for(auto i : b) + s += i; + + assert(s == 1 + 3 + 5 + 7 + 9); + + opp::list c = opp::list(b); + + s = 0; + for(auto i : c) + s += i; + + assert(s == 1 + 3 + 5 + 7 + 9); + + s = 0; + for(auto i : b) + s += i; + + assert(s == 1 + 3 + 5 + 7 + 9); + + return 0; +} diff --git a/oscar64/autotest/opp_numeric.cpp b/oscar64/autotest/opp_numeric.cpp new file mode 100644 index 0000000..a8c0d4e --- /dev/null +++ b/oscar64/autotest/opp_numeric.cpp @@ -0,0 +1,36 @@ +#include +#include +#include +#include +#include +#include +#include + +using opp::string, opp::cout; + +int main(void) +{ + opp::array v; + + opp::iota(v.begin(), v.end(), 10); + + string s = opp::accumulate( + opp::next(v.begin()), v.end(), opp::to_string(v[0]), + [](const opp::string & ws, int k) {return ws + "-" + opp::to_string(k);}); + + assert(s == "10-11-12-13-14-15-16-17-18-19"); + + opp::ostringstream osstr; + opp::inclusive_scan(v.begin(), v.end(), opp::ostream_iterator(osstr, ", ")); + assert(osstr.str() == "10, 21, 33, 46, 60, 75, 91, 108, 126, 145, "); + + osstr.str(""); + opp::exclusive_scan(v.begin(), v.end(), opp::ostream_iterator(osstr, ", "), 0); + assert(osstr.str() == "0, 10, 21, 33, 46, 60, 75, 91, 108, 126, "); + + opp::array w; + + opp::iota(w.begin(), w.end(), 1); + + assert(opp::inner_product(v.begin(), v.end(), w.begin(), 0) == 880); +} \ No newline at end of file diff --git a/oscar64/autotest/opp_optional.cpp b/oscar64/autotest/opp_optional.cpp new file mode 100644 index 0000000..e39ea3b --- /dev/null +++ b/oscar64/autotest/opp_optional.cpp @@ -0,0 +1,63 @@ +#include +#include + +#include +#include +#include + +using opp::optional; +using opp::string; + +optional isqrt(int i) +{ + if (i < 0) + return optional(); + else + return i; +} + +optional ssqrt(int i) +{ + if (i < 0) + return optional(); + else + return opp::to_string(i); +} + +int main(void) +{ + int sum = 0; + for(int i=-10; i<=10; i++) + { + for (auto s : isqrt(i)) + sum += s; + } + assert(sum == 55); + + sum = 0; + for(int i=-10; i<=10; i++) + { + if (auto s = ssqrt(i)) + sum += s->to_int(); + } + assert(sum == 55); + + sum = 0; + for(int i=-10; i<=10; i++) + { + for (auto s : ssqrt(i)) + sum += s.to_int(); + } + assert(sum == 55); + + sum = 0; + for(int i=-10; i<=10; i++) + { + auto s = ssqrt(i); + auto t = s; + + if (t) + sum += t->to_int(); + } + assert(sum == 55); +} diff --git a/oscar64/autotest/opp_pairtest.cpp b/oscar64/autotest/opp_pairtest.cpp new file mode 100644 index 0000000..7a1113c --- /dev/null +++ b/oscar64/autotest/opp_pairtest.cpp @@ -0,0 +1,28 @@ +#include +#include +#include +#include + +using namespace opp; + +int main(void) +{ + vector > vii; + + for(int i=0; i<100; i++) + vii.push_back(make_pair(i, i * i)); + + int sum1 = 0; + long sum2 = 0; + for(const auto & v : vii) + { + sum1 += v.first; + sum2 += v.second; + } + + + assert(sum1 == 4950); + assert(sum2 == 328350l); + + return 0; +} diff --git a/oscar64/autotest/opp_part1.cpp b/oscar64/autotest/opp_part1.cpp new file mode 100644 index 0000000..8fc18fb --- /dev/null +++ b/oscar64/autotest/opp_part1.cpp @@ -0,0 +1,2 @@ +#include "opp_part1.h" + diff --git a/oscar64/autotest/opp_part1.h b/oscar64/autotest/opp_part1.h new file mode 100644 index 0000000..7b227ff --- /dev/null +++ b/oscar64/autotest/opp_part1.h @@ -0,0 +1,40 @@ +#pragma once + +#include + +class A +{ +protected: + int a, b; + +public: + A(int a_, int b_) + : a(a_), b(b_) + {} + + int sum(void) const + { + return a * b; + } +}; + +class AS +{ +protected: + opp::vector va; +public: + AS(const opp::vector & v) + : va(v) + {} + + int sum(void) + { + int s = 0; + for(const auto & a : va) + s += a.sum(); + return s; + } +}; + +#pragma compile("opp_part1.cpp") + diff --git a/oscar64/autotest/opp_part2.cpp b/oscar64/autotest/opp_part2.cpp new file mode 100644 index 0000000..f30400c --- /dev/null +++ b/oscar64/autotest/opp_part2.cpp @@ -0,0 +1,13 @@ +#include "opp_part2.h" + +BS::BS(const opp::vector & v) + : va(v) + {} + +int BS::sum(void) +{ + int s = 0; + for(const auto & a : va) + s += a.sum(); + return s; +} diff --git a/oscar64/autotest/opp_part2.h b/oscar64/autotest/opp_part2.h new file mode 100644 index 0000000..5606e0e --- /dev/null +++ b/oscar64/autotest/opp_part2.h @@ -0,0 +1,16 @@ +#pragma once + +#include "opp_part1.h" + +class BS +{ +protected: + opp::vector va; +public: + BS(const opp::vector & v); + int sum(void); +}; + + + +#pragma compile("opp_part2.cpp") diff --git a/oscar64/autotest/opp_parts.cpp b/oscar64/autotest/opp_parts.cpp new file mode 100644 index 0000000..364753e --- /dev/null +++ b/oscar64/autotest/opp_parts.cpp @@ -0,0 +1,25 @@ +#include + +#include "opp_part1.h" +#include "opp_part2.h" + + +int main(void) +{ + opp::vector va; + va.push_back(A(1, 2)); + va.push_back(A(3, 4)); + va.push_back(A(6, 4)); + va.push_back(A(0, 9)); + + AS as(va); + + va.push_back(A(7, 1)); + + BS bs(va); + + assert(bs.sum() == 2 + 12 + 24 + 7); + assert(as.sum() == 2 + 12 + 24); + + return 0; +} diff --git a/oscar64/autotest/opp_span.cpp b/oscar64/autotest/opp_span.cpp new file mode 100644 index 0000000..1799511 --- /dev/null +++ b/oscar64/autotest/opp_span.cpp @@ -0,0 +1,49 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +int main(void) +{ + static const int test[] = {7, 6, 5, 4, 3, 2, 1}; + + opp::span st(test); + opp::span sd(test, 7); + + auto st1 = st.subspan<1>(); + auto sd1 = sd.subspan<1>(); + + auto st2 = st.subspan(2); + auto sd2 = sd.subspan(2); + + auto st3 = st.subspan<1, 4>(); + auto sd3 = sd.subspan<1, 4>(); + + auto st4 = st.subspan(2, 5); + auto sd4 = sd.subspan(2, 5); + + auto st5 = st.last(5); + auto sd5 = sd.last(5); + + assert(opp::accumulate(st.begin(), st.end(), 0) == 28); + assert(opp::accumulate(sd.begin(), sd.end(), 0) == 28); + + assert(opp::accumulate(st1.begin(), st1.end(), 0) == 21); + assert(opp::accumulate(sd1.begin(), sd1.end(), 0) == 21); + + assert(opp::accumulate(st2.begin(), st2.end(), 0) == 15); + assert(opp::accumulate(sd2.begin(), sd2.end(), 0) == 15); + + assert(opp::accumulate(st3.begin(), st3.end(), 0) == 18); + assert(opp::accumulate(sd3.begin(), sd3.end(), 0) == 18); + + assert(opp::accumulate(st4.begin(), st4.end(), 0) == 15); + assert(opp::accumulate(sd4.begin(), sd4.end(), 0) == 15); + + assert(opp::accumulate(st5.begin(), st5.end(), 0) == 15); + assert(opp::accumulate(sd5.begin(), sd5.end(), 0) == 15); +} \ No newline at end of file diff --git a/oscar64/autotest/opp_static_vector.cpp b/oscar64/autotest/opp_static_vector.cpp new file mode 100644 index 0000000..4038eb6 --- /dev/null +++ b/oscar64/autotest/opp_static_vector.cpp @@ -0,0 +1,78 @@ +#include +#include +#include +#include + +int main(void) +{ + opp::static_vector a; + + for(int i=0; i<10; i++) + a.push_back(i); + + int s = 0; + for(int i=0; i v; + + for(int i=0; i<10; i++) + v.push_back(i); + + assert(v.size() == 10); + v.insert(0, 20); + assert(v.size() == 11); + v.insert(6, 21); + assert(v.size() == 12); + v.insert(12, 22); + + int * fi = opp::find(v.begin(), v.end(), 21); + + fi = v.insert(fi, 30); + fi = v.insert(fi, 31); + fi = v.insert(fi, 32); + + assert(v.size() == 16); + assert(v[0] == 20); + assert(v[15] == 22); + assert(v[8] == 32); + + fi = opp::find(v.begin(), v.end(), 32); + + for(int i=0; i<30; i++) + { + fi = v.insert(fi, i + 40); + } + + assert(v.size() == 46); + assert(v[28] == 60); + + v.erase(10, 10); + + for(int i : v) + opp::cout << i << ", "; + opp::cout << "\n"; + + assert(v.size() == 36); + assert(v[18] == 60); + + v.assign(42, 11); + + assert(v.size() == 42); + assert(v[0] == 11); + assert(v[15] == 11); + assert(v[41] == 11); + + return 0; +} diff --git a/oscar64/autotest/opp_streamtest.cpp b/oscar64/autotest/opp_streamtest.cpp new file mode 100644 index 0000000..940f106 --- /dev/null +++ b/oscar64/autotest/opp_streamtest.cpp @@ -0,0 +1,83 @@ +#include +#include +#include +#include +#include + +using opp::ostringstream; +using opp::istringstream; +using opp::endl; + +float fdist(float a, float b) +{ + float d = fabs(a - b); + a = fabs(a); + b = fabs(b); + return d / (a > b ? a : b); +} + +int main(void) +{ + ostringstream os; + + for(int i=0; i<40; i++) + { + os << i << endl; + } + + istringstream is(os.str()); + + int j = 0, k = 47; +#if 1 + + while (is >> k) + { + assert(k == j++); + } + + assert(j == 40); +#endif + os.str(opp::string()); + +#if 0 + cout << "[" << os.str() << "]" << endl; + os << "HELLO"; + cout << "[" << os.str() << "]" << endl; +#endif +#if 1 + + float f = 1.0, g = 1.0; + + for(int i=0; i<10; i++) + { + os << f << " " << g << endl; +// cout << os.str(); + + f *= 5.1; + g *= 0.12; + } + + + is.str(os.str()); + + f = 1.0, g = 1.0; + + float fr, gr; + + j = 0; + while (is >> fr >> gr) + { +// cout << f << " " << fr << ", " << g << " " << gr << ", " << fdist(fr, f) << endl; + + assert(fdist(fr, f) < 1.0e-5); + assert(fdist(gr, g) < 1.0e-5); + + f *= 5.1; + g *= 0.12; + j++; + } + + assert(j == 10); +#endif + return 0; +} diff --git a/oscar64/autotest/opp_string.cpp b/oscar64/autotest/opp_string.cpp new file mode 100644 index 0000000..3a52312 --- /dev/null +++ b/oscar64/autotest/opp_string.cpp @@ -0,0 +1,103 @@ +#include +#include +#include +#include + +using opp::string; + +static const char HelloWorld[] = "Hello World"; +static const char AndBeyond[] = "And Beyond"; +static const char And[] = "And"; +static const char HelloWorldAndBeyond[] = "Hello World And Beyond"; + +__noinline void test_create(void) +{ + string s1(); + string s2(HelloWorld); + string s3(s2); + string s4('a'); + + assert(!strcmp(s2.tocstr(), HelloWorld)); + assert(!strcmp(s3.tocstr(), HelloWorld)); + assert(s4.size() == 1 && s4[0] == 'a'); +} + +__noinline void test_concat(void) +{ + string s1(); + string s2(HelloWorld); + string s3(AndBeyond); + + string s4 = s1 + s2; + string s5 = s2 + " " + s3; + string s6 = s2 + " " + AndBeyond; + + assert(!strcmp(s4.tocstr(), HelloWorld)); + assert(!strcmp(s5.tocstr(), HelloWorldAndBeyond)); + assert(!strcmp(s6.tocstr(), HelloWorldAndBeyond)); +} + +__noinline void test_find(void) +{ + string s1(HelloWorldAndBeyond); + string s2(And); + + assert(s1.find(HelloWorld) == 0); + assert(s1.find(AndBeyond) == 12); + assert(s1.find(And) == 12); + assert(s1.find(s2) == 12); + + assert(s1.find(' ') == 5); + assert(s1.find(' ', 6) == 11); +} + +__noinline void test_assign(void) +{ + string s1(HelloWorld); + string s2(AndBeyond); + string s3; + s3 = s1; + s3 = s2; + s3 = s1; + s3 += " "; + s3 += s2; + + assert(!strcmp(s3.tocstr(), HelloWorldAndBeyond)); + + s3 <<= 12; + + assert(!strcmp(s3.tocstr(), AndBeyond)); + + s3 = HelloWorldAndBeyond; + + assert(!strcmp(s3.tocstr(), HelloWorldAndBeyond)); + + s3 >>= 11; + + assert(!strcmp(s3.tocstr(), HelloWorld)); +} + +static char * test; + +int main(void) +{ + test = new char; + + unsigned avail = heapfree(); + + test_create(); + assert(avail == heapfree()); + + test_concat(); + assert(avail == heapfree()); + + test_find(); + assert(avail == heapfree()); + + test_assign(); + assert(avail == heapfree()); + + delete test; + + return 0; +} diff --git a/oscar64/autotest/opp_string_init.cpp b/oscar64/autotest/opp_string_init.cpp new file mode 100644 index 0000000..15c23b5 --- /dev/null +++ b/oscar64/autotest/opp_string_init.cpp @@ -0,0 +1,193 @@ +#include +#include +#include +#include + +using opp::string; + + +const string s1; +const string s2 = "Hello"; +const string s3{"World"}; + +const string a1[2]; +const string a2[2] = {"Hello", "World"}; +const string a3[2] = {opp::string("Hello"), opp::string("World")}; + + +const string d1[3][2]; +const string d2[3][2] = {{"Hello", "World"}, {"aaa", "bbb"}, {"ccc", "ddd"}}; +const string d3[3][2] = + {{opp::string("Hello"), opp::string("World")}, + {opp::string("aaa"), opp::string("bbb")}, + {opp::string("ccc"), opp::string("ddd")}}; + +void test_global_init(void) +{ + assert(!strcmp(s1.tocstr(), "")); + assert(!strcmp(s2.tocstr(), "Hello")); + assert(!strcmp(s3.tocstr(), "World")); +} + + +void test_global_ainit(void) +{ + assert(!strcmp(a1[0].tocstr(), "")); + assert(!strcmp(a1[1].tocstr(), "")); + + assert(!strcmp(a2[0].tocstr(), "Hello")); + assert(!strcmp(a2[1].tocstr(), "World")); + + assert(!strcmp(a3[0].tocstr(), "Hello")); + assert(!strcmp(a3[1].tocstr(), "World")); +} + +void test_global_dinit(void) +{ + assert(!strcmp(d1[0][0].tocstr(), "")); + assert(!strcmp(d1[2][1].tocstr(), "")); + + assert(!strcmp(d2[0][0].tocstr(), "Hello")); + assert(!strcmp(d2[2][1].tocstr(), "ddd")); + + assert(!strcmp(d3[0][0].tocstr(), "Hello")); + assert(!strcmp(d3[2][1].tocstr(), "ddd")); +} + + +void test_local_init(void) +{ + const string s1; + const string s2 = "Hello"; + const string s3{"World"}; + + assert(!strcmp(s1.tocstr(), "")); + assert(!strcmp(s2.tocstr(), "Hello")); + assert(!strcmp(s3.tocstr(), "World")); +} + + +void test_local_ainit(void) +{ + const string a1[2]; + const string a2[2] = {"Hello", "World"}; + const string a3[2] = {opp::string("Hello"), opp::string("World")}; + + assert(!strcmp(a1[0].tocstr(), "")); + assert(!strcmp(a1[1].tocstr(), "")); + + assert(!strcmp(a2[0].tocstr(), "Hello")); + assert(!strcmp(a2[1].tocstr(), "World")); + + assert(!strcmp(a3[0].tocstr(), "Hello")); + assert(!strcmp(a3[1].tocstr(), "World")); +} + +void test_local_dinit(void) +{ + const string d1[3][2]; + const string d2[3][2] = {{"Hello", "World"}, {"aaa", "bbb"}, {"ccc", "ddd"}}; + const string d3[3][2] = + {{opp::string("Hello"), opp::string("World")}, + {opp::string("aaa"), opp::string("bbb")}, + {opp::string("ccc"), opp::string("ddd")}}; + + assert(!strcmp(d1[0][0].tocstr(), "")); + assert(!strcmp(d1[2][1].tocstr(), "")); + + assert(!strcmp(d2[0][0].tocstr(), "Hello")); + assert(!strcmp(d2[2][1].tocstr(), "ddd")); + + assert(!strcmp(d3[0][0].tocstr(), "Hello")); + assert(!strcmp(d3[2][1].tocstr(), "ddd")); +} + +class X +{ +public: + const string s1; + const string s2 = "Hello"; + const string s3; + + const string a1[2]; + const string a2[2] = {"Hello", "World"}; + + const string d1[3][2]; + const string d2[3][2] = {{"Hello", "World"}, {"aaa", "bbb"}, {"ccc", "ddd"}}; + + X() : s3("World") {} +}; + +void test_member_init(void) +{ + X x; + + assert(!strcmp(x.s1.tocstr(), "")); + assert(!strcmp(x.s2.tocstr(), "Hello")); + assert(!strcmp(x.s3.tocstr(), "World")); +} + +void test_member_ainit(void) +{ + X x; + + assert(!strcmp(x.a1[0].tocstr(), "")); + assert(!strcmp(x.a1[1].tocstr(), "")); + + assert(!strcmp(x.a2[0].tocstr(), "Hello")); + assert(!strcmp(x.a2[1].tocstr(), "World")); +} + +void test_member_dinit(void) +{ + X x; + + assert(!strcmp(x.d1[0][0].tocstr(), "")); + assert(!strcmp(x.d1[2][1].tocstr(), "")); + + assert(!strcmp(x.d2[0][0].tocstr(), "Hello")); + assert(!strcmp(x.d2[2][1].tocstr(), "ddd")); +} + +void test_copy_init(void) +{ + X x; + X y(x); + + assert(!strcmp(y.s1.tocstr(), "")); + assert(!strcmp(y.s2.tocstr(), "Hello")); + assert(!strcmp(y.s3.tocstr(), "World")); +} + +void test_copy_ainit(void) +{ + X x; + X y(x); + + assert(!strcmp(y.a1[0].tocstr(), "")); + assert(!strcmp(y.a1[1].tocstr(), "")); + + assert(!strcmp(y.a2[0].tocstr(), "Hello")); + assert(!strcmp(y.a2[1].tocstr(), "World")); +} + +int main(void) +{ + test_global_init(); + test_global_ainit(); + test_global_dinit(); + + for(int i=0; i<10000; i++) + { + test_local_init(); + test_local_ainit(); + } + + test_member_init(); + test_member_ainit(); + + test_copy_init(); + test_copy_ainit(); + + return 0; +} \ No newline at end of file diff --git a/oscar64/autotest/opp_vector.cpp b/oscar64/autotest/opp_vector.cpp new file mode 100644 index 0000000..cfe14e7 --- /dev/null +++ b/oscar64/autotest/opp_vector.cpp @@ -0,0 +1,78 @@ +#include +#include +#include +#include + +int main(void) +{ + opp::vector a; + + for(int i=0; i<10; i++) + a.push_back(i); + + int s = 0; + for(int i=0; i v; + + for(int i=0; i<10; i++) + v.push_back(i); + + assert(v.size() == 10); + v.insert(0, 20); + assert(v.size() == 11); + v.insert(6, 21); + assert(v.size() == 12); + v.insert(12, 22); + + int * fi = opp::find(v.begin(), v.end(), 21); + + fi = v.insert(fi, 30); + fi = v.insert(fi, 31); + fi = v.insert(fi, 32); + + assert(v.size() == 16); + assert(v[0] == 20); + assert(v[15] == 22); + assert(v[8] == 32); + + fi = opp::find(v.begin(), v.end(), 32); + + for(int i=0; i<30; i++) + { + fi = v.insert(fi, i + 40); + } + + assert(v.size() == 46); + assert(v[28] == 60); + + v.erase(10, 10); + + for(int i : v) + opp::cout << i << ", "; + opp::cout << "\n"; + + assert(v.size() == 36); + assert(v[18] == 60); + + v.assign(42, 11); + + assert(v.size() == 42); + assert(v[0] == 11); + assert(v[15] == 11); + assert(v[41] == 11); + + return 0; +} diff --git a/oscar64/autotest/opp_vector_string.cpp b/oscar64/autotest/opp_vector_string.cpp new file mode 100644 index 0000000..96642be --- /dev/null +++ b/oscar64/autotest/opp_vector_string.cpp @@ -0,0 +1,38 @@ +#include +#include +#include +#include +#include + +using opp::string; +using opp::vector; + +string join(const vector & vs) +{ + string sj; + for(int i=0; i vs; + string a; + + for(int i=0; i<10; i++) + { + vs.push_back(a); + a += "x"; + } + + int s = 0; + for(int i=0; i<10; i++) + s += vs[i].size(); + + assert(s == 45); + + assert(join(vs).size() == 45); + + return 0; +} diff --git a/oscar64/autotest/optiontest.c b/oscar64/autotest/optiontest.c new file mode 100644 index 0000000..ee07ee8 --- /dev/null +++ b/oscar64/autotest/optiontest.c @@ -0,0 +1,15 @@ +#include +#include + +int option(bool a, int b, int c) +{ + return a ? b : c; +} + +int main(void) +{ + assert(option(true, 1, 2) == 1); + assert(option(false, 1, 2) == 2); + + return 0; +} \ No newline at end of file diff --git a/oscar64/autotest/plasma.c b/oscar64/autotest/plasma.c new file mode 100644 index 0000000..ea4c0d3 --- /dev/null +++ b/oscar64/autotest/plasma.c @@ -0,0 +1,145 @@ +#include + +static const char sintab[] = { + 128, 131, 134, 137, 140, 144, 147, 150, 153, 156, 159, 162, 165, 168, 171, 174, 177, 179, 182, 185, 188, 191, 193, 196, 199, 201, 204, 206, 209, 211, 213, 216, 218, 220, 222, 224, 226, 228, 230, 232, 234, 235, 237, 239, 240, 241, 243, 244, 245, 246, 248, 249, 250, 250, 251, 252, 253, 253, 254, 254, 254, 255, 255, 255, + 255, 255, 255, 255, 254, 254, 254, 253, 253, 252, 251, 250, 250, 249, 248, 246, 245, 244, 243, 241, 240, 239, 237, 235, 234, 232, 230, 228, 226, 224, 222, 220, 218, 216, 213, 211, 209, 206, 204, 201, 199, 196, 193, 191, 188, 185, 182, 179, 177, 174, 171, 168, 165, 162, 159, 156, 153, 150, 147, 144, 140, 137, 134, 131, + 128, 125, 122, 119, 116, 112, 109, 106, 103, 100, 97, 94, 91, 88, 85, 82, 79, 77, 74, 71, 68, 65, 63, 60, 57, 55, 52, 50, 47, 45, 43, 40, 38, 36, 34, 32, 30, 28, 26, 24, 22, 21, 19, 17, 16, 15, 13, 12, 11, 10, 8, 7, 6, 6, 5, 4, 3, 3, 2, 2, 2, 1, 1, 1, + 1, 1, 1, 1, 2, 2, 2, 3, 3, 4, 5, 6, 6, 7, 8, 10, 11, 12, 13, 15, 16, 17, 19, 21, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 43, 45, 47, 50, 52, 55, 57, 60, 63, 65, 68, 71, 74, 77, 79, 82, 85, 88, 91, 94, 97, 100, 103, 106, 109, 112, 116, 119, 122, 125 +}; + +char Screen0[1024], Screen1[1024]; + +char colormap0[256], colormap1[256]; + + +char colors0[] = {0, 6, 14, 1, 13, 5, 0}; +char colors1[] = {0, 9, 7, 1, 15, 12, 0}; + +unsigned c1A, c1B, c2A, c2B, c3A, c3B; +int d1A, d1B, d2A, d2B, d3A, d3B; + +void inithires(void) +{ + for(int i=0; i<256; i++) + { + colormap0[i] = colors0[i / 37]; + colormap1[i] = colors1[i / 37] << 4; + } +} + +inline void doplasma(char * scrn) +{ + char xbuf0[40], xbuf1[40]; + char ybuf0[25], ybuf1[25]; + + char c2a = c2A >> 8; + char c2b = c2B >> 8; + char c1a = c1A >> 8; + char c1b = c1B >> 8; + + for (char i = 0; i < 25; i++) { + ybuf0[i] = sintab[(c1a + c2a) & 0xff] + sintab[c1b]; + c1a += 13; + c1b -= 5; + } + + for (char i = 0; i < 40; i++) { + xbuf0[i] = sintab[(c2a + c1b) & 0xff] + sintab[c2b]; + c2a += 11; + c2b -= 7; + } + + c2a = c2B >> 8; + c2b = c3A >> 8; + c1a = c1B >> 8; + c1b = c3B >> 8; + + for (char i = 0; i < 25; i++) { + ybuf1[i] = sintab[(c1b + c2a) & 0xff] + sintab[c1a]; + c1a += 4; + c1b -= 6; + } + + for (char i = 0; i < 40; i++) { + xbuf1[i] = sintab[(c2b + c1a) & 0xff] + sintab[c2a]; + c2a += 7; + c2b -= 9; + } + + #pragma unroll(full) + for (char k=0; k<5; k++) + { + char tbuf0[5], tbuf1[5]; + #pragma unroll(full) + for (char i = 0; i < 4; i++) + { + tbuf0[i] = ybuf0[5 * k + i + 1] - ybuf0[5 * k + i]; + tbuf1[i] = ybuf1[5 * k + i + 1] - ybuf1[5 * k + i]; + } + + for (signed char i = 39; i >= 0; i--) + { + char t = xbuf0[i] + ybuf0[5 * k]; + char u = xbuf1[i] + ybuf1[5 * k]; + + #pragma unroll(full) + for (char j = 0; j < 5; j++) + { + scrn[40 * j + 200 * k + i] = colormap0[u] | colormap1[u]; + t += tbuf0[j]; + u += tbuf1[j]; + } + } + } + + c1A += 8 * ((int)sintab[d1A] - 128); + c1B += 16 * ((int)sintab[d1B] - 128); + c2A += 8 * ((int)sintab[d2A] - 128); + c2B += 16 * ((int)sintab[d2B] - 128); + c3A += 6 * ((int)sintab[d3A] - 128); + c3B += 12 * ((int)sintab[d3B] - 128); + + d1A += 3; + d1B += rand() & 3; + d2A += 5; + d2B += rand() & 3; + d3A += 2; + d3B += rand() & 3; +} + +void doplasma0(void) +{ + doplasma(Screen0); +} + +void doplasma1(void) +{ + doplasma(Screen1); +} + +unsigned checksum(const char * scr) +{ + unsigned s = 0x1234; + for(int i=0; i<1024; i++) + { + unsigned m = s & 1; + s >>= 1; + if (m) + s ^= 0x2152; + s ^= scr[i]; + } + return s; +} +int main(void) +{ + inithires(); + + doplasma0(); + doplasma1(); + doplasma0(); + doplasma1(); + doplasma0(); + doplasma1(); + + return checksum(Screen0) + checksum(Screen1) - 16337; +} diff --git a/oscar64/autotest/ptrarraycmptest.c b/oscar64/autotest/ptrarraycmptest.c new file mode 100644 index 0000000..ef0860a --- /dev/null +++ b/oscar64/autotest/ptrarraycmptest.c @@ -0,0 +1,39 @@ +#include + +struct X +{ + int a; +}; + +X x[5] = { + {1}, {2}, {3}, {4}, {5} +}; + +X * y; + +int main(void) +{ + y = x; + assert(y == x); + y = x + 1; + assert(y == x + 1); + y = &(x[2]); + assert(y == x + 2); + y = x + 3; + assert(y == &(x[3])); + y = x ; + assert(y == (struct X*)x); + + y = x; + assert(x == y); + y = x + 1; + assert(x + 1 == y); + y = &(x[2]); + assert(x + 2 == y); + y = x + 3; + assert(&(x[3]) == y); + y = x ; + assert((struct X*)x == y); + + return 0; +} diff --git a/oscar64/autotest/ptrinittest.c b/oscar64/autotest/ptrinittest.c new file mode 100644 index 0000000..f0ffa0a --- /dev/null +++ b/oscar64/autotest/ptrinittest.c @@ -0,0 +1,37 @@ +#if 1 + +const struct A { + char w; + int b[5]; + struct { + int c[5]; + } o; +} a = {22, + {4, 5, 6, 7, 8}, + { + {4, 5, 6, 7, 8} + } +}; + +const int * t[4] = { + a.b + 1 + 1 +}; + +const int * v[4] = { + a.o.c + 1 + 1 +}; + +int q[5] = {4, 5, 6, 7, 8}; + +const int * u[4] = { + q + 2 +}; + +int main(void) +{ + return + u[0][0] + (q + 2)[2] - 6 - 8 + + v[0][0] + (a.o.c + 2)[2] - 6 - 8 + + t[0][0] + (a.b + 2)[2] - 6 - 8; +} + diff --git a/oscar64/autotest/qsorttest.c b/oscar64/autotest/qsorttest.c new file mode 100644 index 0000000..c764773 --- /dev/null +++ b/oscar64/autotest/qsorttest.c @@ -0,0 +1,83 @@ +#include +#include +#include + +struct Node +{ + int a; + char s[10]; +}; + +void qsort(Node * n, int s) +{ + if (s > 1) + { + Node pn = n[0]; + int pi = 0; + for(int i=1; i +#include + +int main(void) +{ + long lsum = 0; + for(unsigned i=0; i<1000; i++) + lsum += rand(); + + assert(lsum == 32157742L); + + return 0; +} + diff --git a/oscar64/autotest/recursiontest.c b/oscar64/autotest/recursiontest.c new file mode 100644 index 0000000..28801f2 --- /dev/null +++ b/oscar64/autotest/recursiontest.c @@ -0,0 +1,90 @@ +#include +#include +#include +#include + +int fib(int a) +{ + if (a < 2) + return a; + else + return fib(a - 1) + fib(a - 2); +} + +struct Node +{ + char value; + Node * left, * right; +}; + +typedef Node * NodePtr; + + +NodePtr newnode(void) +{ + return (NodePtr)malloc(sizeof(Node)); +} + +Node * insert(Node * tree, char v) +{ + if (tree) + { + if (v < tree->value) + { + tree->left = insert(tree->left, v); + } + else + { + tree->right = insert(tree->right, v); + } + } + else + { + tree = newnode(); + tree->value = v; + tree->left = nullptr; + tree->right = nullptr; + } + + return tree; +} + +char * collect(Node * tree, char * p) +{ + if (tree) + { + p = collect(tree->left, p); + *p++= tree->value; + p = collect(tree->right, p); + } + + return p; +} + +void btest(void) +{ + const char * str = "HELLO WORLD"; + char buff[20]; + + int i = 0; + Node * tree = nullptr; + + while (str[i]) + { + tree = insert(tree, str[i]); + i++; + } + + collect(tree, buff)[0] = 0; + + assert(strcmp(buff, " DEHLLLOORW") == 0); +} + +int main(void) +{ + assert(fib(23) == 28657); + + btest(); + + return 0; +} diff --git a/oscar64/autotest/rolrortest.cpp b/oscar64/autotest/rolrortest.cpp new file mode 100644 index 0000000..f6a93ae --- /dev/null +++ b/oscar64/autotest/rolrortest.cpp @@ -0,0 +1,76 @@ +#include + +__noinline unsigned long rollright32(unsigned long a) { + unsigned long tmp = a & 1; + return ( a >> 1) + (tmp << 31); +} + +__noinline unsigned rollright16(unsigned a) { + unsigned tmp = a & 1; + return ( a >> 1) + (tmp << 15); +} + +__noinline char rollright8(char a) { + char tmp = a & 1; + return ( a >> 1) + (tmp << 7); +} + +__noinline unsigned long rollleft32(unsigned long a) { + unsigned long tmp = (a >> 31) & 1; + return ( a << 1) + tmp; +} + +__noinline unsigned rollleft16(unsigned a) { + unsigned tmp = (a >> 15) & 1; + return ( a << 1) + tmp; +} + +__noinline char rollleft8(char a) { + char tmp = (a >> 7) & 1; + return ( a << 1) + tmp; +} + +int main() { + unsigned long lv = 0x12345678ul; + unsigned val = 0x1234; + char c=0x12; + + unsigned long lvt[33]; + unsigned valt[17]; + char ct[9]; + + lvt[0] = lv; + valt[0] = val; + ct[0] = c; + + assert(rollleft8(rollright8(c)) == c); + assert(rollleft16(rollright16(val)) == val); + assert(rollleft32(rollright32(lv)) == lv); + + for(int i=0; i<32; i++) + lvt[i + 1] = rollright32(lvt[i]); + for(int i=0; i<16; i++) + valt[i + 1] = rollright16(valt[i]); + for(int i=0; i<8; i++) + ct[i + 1] = rollright8(ct[i]); + + for(int i=0; i<=32; i++) + { + assert(lvt[32 - i] == lv); + lv = rollleft32(lv); + } + + for(int i=0; i<=16; i++) + { + assert(valt[16 - i] == val); + val = rollleft16(val); + } + + for(int i=0; i<=8; i++) + { + assert(ct[8 - i] == c); + c = rollleft8(c); + } + + return 0; +} diff --git a/oscar64/autotest/scrolltest.c b/oscar64/autotest/scrolltest.c new file mode 100644 index 0000000..eeafb4f --- /dev/null +++ b/oscar64/autotest/scrolltest.c @@ -0,0 +1,107 @@ +#include + +#define Screen ((char *)0x0400) + +void scroll_left(void) +{ + char * dp = Screen; + for(char y=0; y<25; y++) + { + for(char x=0; x<39; x++) + { + dp[x] = dp[x + 1]; + } + dp += 40; + } +} + +void scroll_right(void) +{ + char * dp = Screen; + for(char y=0; y<25; y++) + { + for(char x=39; x>0; x--) + { + dp[x] = dp[x - 1]; + } + dp += 40; + } +} + +void scroll_up(void) +{ + char * dp = Screen, * sp = dp + 40; + for(char y=0; y<24; y++) + { + for(char x=0; x<40; x++) + { + dp[x] = sp[x]; + } + dp = sp; + sp += 40; + } +} + +void scroll_down(void) +{ + char * dp = Screen + 24 * 40, * sp = dp - 40; + for(char y=0; y<24; y++) + { + for(char x=0; x<40; x++) + { + dp[x] = sp[x]; + } + dp = sp; + sp -= 40; + } +} + +void fill_screen(void) +{ + for(char y=0; y<25; y++) + { + for(char x=0; x<40; x++) + { + Screen[40 * y + x] = 7 * y + x; + } + } +} + +void check_screen(int dy, int dx) +{ + for(int y=0; y<25; y++) + { + for(int x=0; x<40; x++) + { + int sy = y + dy; + int sx = x + dx; + + char c = 7 * y + x; + if (sy >= 0 && sy < 25 && sx >= 0 && sx < 40) + c = 7 * sy + sx; + + assert(Screen[40 * y + x] == c); + } + } +} + +int main(void) +{ + fill_screen(); + scroll_left(); + check_screen(0, 1); + + fill_screen(); + scroll_right(); + check_screen(0, -1); + + fill_screen(); + scroll_up(); + check_screen(1, 0); + + fill_screen(); + scroll_down(); + check_screen(-1, 0); + + return 0; +} diff --git a/oscar64/autotest/shiftbyteaddconst.cpp b/oscar64/autotest/shiftbyteaddconst.cpp new file mode 100644 index 0000000..084badc --- /dev/null +++ b/oscar64/autotest/shiftbyteaddconst.cpp @@ -0,0 +1,57 @@ +#include + +__noinline int base_add(int add, int shl, char a) +{ + return (a << shl) + add; +} + +template +void test_add(char a) +{ + assert((a << shl) + add == base_add(add, shl, a)); +} + +template +void test_adds(char a) +{ + #for(i, 16) test_add(a); +} + +__noinline int base_sub(int sub, int shl, char a) +{ + return (a << shl) - sub; +} + +template +void test_sub(char a) +{ + assert(int(a << shl) - sub == base_sub(sub, shl, a)); +} + +template +void test_subs(char a) +{ + #for(i, 16) test_sub(a); +} + + +int main(void) +{ + for(int i=0; i<256; i++) + test_adds<15>(i); + for(int i=0; i<256; i++) + test_adds<16>(i); + for(int i=0; i<256; i++) + test_adds<111>(i); + for(int i=0; i<256; i++) + test_adds<4096>(i); + for(int i=0; i<256; i++) + test_adds<13421>(i); + for(int i=0; i<256; i++) + test_subs<15>(i); + for(int i=0; i<256; i++) + test_subs<16>(i); + for(int i=0; i<256; i++) + test_subs<4096>(i); +} + diff --git a/oscar64/autotest/sprintftest.c b/oscar64/autotest/sprintftest.c new file mode 100644 index 0000000..0c9e5bc --- /dev/null +++ b/oscar64/autotest/sprintftest.c @@ -0,0 +1,335 @@ +#include +#include +#include + +void testi(const char * fmt, int val, const char * tst) +{ + char buffer[32]; + + sprintf(buffer, fmt, val); + printf("%s:%s\n", buffer, tst); + assert(!strcmp(buffer, tst)); +} + +void testu(const char * fmt, unsigned val, const char * tst) +{ + char buffer[32]; + + sprintf(buffer, fmt, val); + printf("%s:%s\n", buffer, tst); + assert(!strcmp(buffer, tst)); +} + +void testil(const char * fmt, long val, const char * tst) +{ + char buffer[32]; + + sprintf(buffer, fmt, val); + printf("%s:%s\n", buffer, tst); + assert(!strcmp(buffer, tst)); +} + +void testul(const char * fmt, unsigned long val, const char * tst) +{ + char buffer[32]; + + sprintf(buffer, fmt, val); + printf("%s:%s\n", buffer, tst); + assert(!strcmp(buffer, tst)); +} + +void testf(const char * fmt, float val, const char * tst) +{ + char buffer[32]; + + sprintf(buffer, fmt, val); + printf("%s:%s\n", buffer, tst); + assert(!strcmp(buffer, tst)); +} + + +int main(void) +{ + testi("%d", 0, "0"); + testi("%d", 1, "1"); + testi("%d", -1, "-1"); + testi("%d", 12, "12"); + testi("%d", 123, "123"); + testi("%d", 1234, "1234"); + testi("%d", 12345, "12345"); + testi("%d", -12345, "-12345"); + testi("%d", 32767, "32767"); + testi("%d", -32768, "-32768"); + + testi("%3d", 0, " 0"); + testi("%3d", 1, " 1"); + testi("%3d", -1, " -1"); + testi("%3d", 12, " 12"); + testi("%3d", 123, "123"); + testi("%3d", 1234, "1234"); + testi("%3d", 12345, "12345"); + testi("%3d", -12345, "-12345"); + testi("%3d", 32767, "32767"); + testi("%3d", -32768, "-32768"); + + testi("%03d", 0, "000"); + testi("%03d", 1, "001"); +// testi("%03d", -1, "-01"); + testi("%03d", 12, "012"); + testi("%03d", 123, "123"); + testi("%03d", 1234, "1234"); + testi("%03d", 12345, "12345"); + testi("%03d", -12345, "-12345"); + testi("%03d", 32767, "32767"); + testi("%03d", -32768, "-32768"); + + testi("%-4d", 0, "0 "); + testi("%-4d", 1, "1 "); + testi("%-4d", -1, "-1 "); + testi("%-4d", 12, "12 "); + testi("%-4d", 123, "123 "); + testi("%-4d", 1234, "1234"); + testi("%-4d", 12345, "12345"); + testi("%-4d", -12345, "-12345"); + testi("%-4d", 32767, "32767"); + testi("%-4d", -32768, "-32768"); + + testi("%+d", 0, "+0"); + testi("%+d", 1, "+1"); + testi("%+d", -1, "-1"); + testi("%+d", 12, "+12"); + testi("%+d", 123, "+123"); + testi("%+d", 1234, "+1234"); + testi("%+d", 12345, "+12345"); + testi("%+d", -12345, "-12345"); + testi("%+d", 32767, "+32767"); + testi("%+d", -32768, "-32768"); + + + + testil("%ld", 0l, "0"); + testil("%ld", 1l, "1"); + testil("%ld", -1l, "-1"); + testil("%ld", 12l, "12"); + testil("%ld", 123l, "123"); + testil("%ld", 1234l, "1234"); + testil("%ld", 12345l, "12345"); + testil("%ld", -12345l, "-12345"); + testil("%ld", 32767l, "32767"); + testil("%ld", -32768l, "-32768"); + testil("%ld", 2147483647l, "2147483647"); + testil("%ld", -2147483648l, "-2147483648"); + + testil("%3ld", 0l, " 0"); + testil("%3ld", 1l, " 1"); + testil("%3ld", -1l, " -1"); + testil("%3ld", 12l, " 12"); + testil("%3ld", 123l, "123"); + testil("%3ld", 1234l, "1234"); + testil("%3ld", 12345l, "12345"); + testil("%3ld", -12345l, "-12345"); + testil("%3ld", 32767l, "32767"); + testil("%3ld", -32768l, "-32768"); + testil("%3ld", 2147483647l, "2147483647"); + testil("%3ld", -2147483648l, "-2147483648"); + + testil("%03ld", 0l, "000"); + testil("%03ld", 1l, "001"); +// testil("%03ld", -1l, "-01"); + testil("%03ld", 12l, "012"); + testil("%03ld", 123l, "123"); + testil("%03ld", 1234l, "1234"); + testil("%03ld", 12345l, "12345"); + testil("%03ld", -12345l, "-12345"); + testil("%03ld", 32767l, "32767"); + testil("%03ld", -32768l, "-32768"); + testil("%03ld", 2147483647l, "2147483647"); + testil("%03ld", -2147483648l, "-2147483648"); + + testil("%-4ld", 0l, "0 "); + testil("%-4ld", 1l, "1 "); + testil("%-4ld", -1l, "-1 "); + testil("%-4ld", 12l, "12 "); + testil("%-4ld", 123l, "123 "); + testil("%-4ld", 1234l, "1234"); + testil("%-4ld", 12345l, "12345"); + testil("%-4ld", -12345l, "-12345"); + testil("%-4ld", 32767l, "32767"); + testil("%-4ld", -32768l, "-32768"); + testil("%-4ld", 2147483647l, "2147483647"); + testil("%-4ld", -2147483648l, "-2147483648"); + + testil("%+ld", 0l, "+0"); + testil("%+ld", 1l, "+1"); + testil("%+ld", -1l, "-1"); + testil("%+ld", 12l, "+12"); + testil("%+ld", 123l, "+123"); + testil("%+ld", 1234l, "+1234"); + testil("%+ld", 12345l, "+12345"); + testil("%+ld", -12345l, "-12345"); + testil("%+ld", 32767l, "+32767"); + testil("%+ld", -32768l, "-32768"); + testil("%+ld", 2147483647l, "+2147483647"); + testil("%+ld", -2147483648l, "-2147483648"); + + testu("%u", 0, "0"); + testu("%u", 1, "1"); + testu("%u", 12, "12"); + testu("%u", 123, "123"); + testu("%u", 1234, "1234"); + testu("%u", 12345, "12345"); + testu("%u", 32767, "32767"); + testu("%u", 32768, "32768"); + testu("%u", 65535, "65535"); + testu("%x", 0, "0"); + testu("%x", 0x49bf, "49bf"); + testu("%x", 0xffff, "ffff"); + testu("%X", 0x49bf, "49BF"); + testu("%X", 0xffff, "FFFF"); + + testu("%3u", 0, " 0"); + testu("%3u", 1, " 1"); + testu("%3u", 12, " 12"); + testu("%3u", 123, "123"); + testu("%3u", 1234, "1234"); + testu("%3u", 12345, "12345"); + testu("%3u", 32767, "32767"); + testu("%3u", 32768, "32768"); + testu("%3u", 65535, "65535"); + testu("%3x", 0, " 0"); + testu("%3x", 0x49bf, "49bf"); + testu("%3x", 0xffff, "ffff"); + testu("%3X", 0x49bf, "49BF"); + testu("%3X", 0xffff, "FFFF"); + + testu("%03u", 0, "000"); + testu("%03u", 1, "001"); + testu("%03u", 12, "012"); + testu("%03u", 123, "123"); + testu("%03u", 1234, "1234"); + testu("%03u", 12345, "12345"); + testu("%03u", 32767, "32767"); + testu("%03u", 32768, "32768"); + testu("%03u", 65535, "65535"); + testu("%03x", 0, "000"); + testu("%03x", 0x49bf, "49bf"); + testu("%03x", 0xffff, "ffff"); + testu("%03X", 0x49bf, "49BF"); + testu("%03X", 0xffff, "FFFF"); + + testu("%-4u", 0, "0 "); + testu("%-4u", 1, "1 "); + testu("%-4u", 12, "12 "); + testu("%-4u", 123, "123 "); + testu("%-4u", 1234, "1234"); + testu("%-4u", 12345, "12345"); + testu("%-4u", 32767, "32767"); + testu("%-4u", 32768, "32768"); + testu("%-4u", 65535, "65535"); + testu("%-4x", 0, "0 "); + testu("%-4x", 0x49bf, "49bf"); + testu("%-4x", 0xffff, "ffff"); + testu("%-4X", 0x49bf, "49BF"); + testu("%-4X", 0xffff, "FFFF"); + + testul("%3lu", 0l, " 0"); + testul("%3lu", 1l, " 1"); + testul("%3lu", 12l, " 12"); + testul("%3lu", 123l, "123"); + testul("%3lu", 1234l, "1234"); + testul("%3lu", 12345l, "12345"); + testul("%3lu", 32767l, "32767"); + testul("%3lu", 2147483647l, "2147483647"); + testul("%3lu", 4294967295l, "4294967295"); + testul("%3lx", 0, " 0"); + testul("%3lx", 0x3576fbcdl, "3576fbcd"); + testul("%3lx", 0xffffffffl, "ffffffff"); + testul("%3lX", 0x3576fbcdl, "3576FBCD"); + testul("%3lX", 0xffffffffl, "FFFFFFFF"); + + testul("%03lu", 0l, "000"); + testul("%03lu", 1l, "001"); + testul("%03lu", 12l, "012"); + testul("%03lu", 123l, "123"); + testul("%03lu", 1234l, "1234"); + testul("%03lu", 12345l, "12345"); + testul("%03lu", 32767l, "32767"); + testul("%03lu", 2147483647l, "2147483647"); + testul("%03lu", 4294967295l, "4294967295"); + testul("%03lx", 0, "000"); + testul("%03lx", 0x3576fbcdl, "3576fbcd"); + testul("%03lx", 0xffffffffl, "ffffffff"); + testul("%03lX", 0x3576fbcdl, "3576FBCD"); + testul("%03lX", 0xffffffffl, "FFFFFFFF"); + + testul("%-4lu", 0l, "0 "); + testul("%-4lu", 1l, "1 "); + testul("%-4lu", 12l, "12 "); + testul("%-4lu", 123l, "123 "); + testul("%-4lu", 1234l, "1234"); + testul("%-4lu", 12345l, "12345"); + testul("%-4lu", 32767l, "32767"); + testul("%-4lu", 2147483647l, "2147483647"); + testul("%-4lu", 4294967295l, "4294967295"); + testul("%-4lx", 0, "0 "); + testul("%-4lx", 0x3576fbcdl, "3576fbcd"); + testul("%-4lx", 0xffffffffl, "ffffffff"); + testul("%-4lX", 0x3576fbcdl, "3576FBCD"); + testul("%-4lX", 0xffffffffl, "FFFFFFFF"); + + testul("%+lu", 0l, "+0"); + testul("%+lu", 1l, "+1"); + testul("%+lu", 12l, "+12"); + testul("%+lu", 123l, "+123"); + testul("%+lu", 1234l, "+1234"); + testul("%+lu", 12345l, "+12345"); + testul("%+lu", 32767l, "+32767"); + testul("%+lu", 2147483647l, "+2147483647"); + testul("%+lu", 4294967295l, "+4294967295"); + + testf("%f", 0., "0.000000"); + testf("%f", 1., "1.000000"); + testf("%f", -1., "-1.000000"); + testf("%f", 12., "12.000000"); + testf("%f", 123., "123.000000"); + testf("%f", 1234., "1234.000000"); + testf("%f", 12345., "12345.000000"); + testf("%f", 123456., "123456.000000"); + testf("%f", 1234567., "1234567.000000"); + testf("%f", 0.1, "0.100000"); + testf("%f", 0.01, "0.010000"); + testf("%f", 0.001, "0.001000"); + testf("%f", 0.0001, "0.000100"); + testf("%f", 0.00001, "0.000010"); + testf("%f", 0.000001, "0.000001"); + + testf("%5.1f", 0, " 0.0"); + testf("%5.1f", 1, " 1.0"); + testf("%5.1f", -1, " -1.0"); + testf("%5.1f", 10, " 10.0"); + testf("%5.1f", -10, "-10.0"); + testf("%5.1f", 100, "100.0"); + testf("%5.1f", -100, "-100.0"); + testf("%5.1f", 0.1, " 0.1"); + testf("%5.1f", -0.1, " -0.1"); + testf("%5.1f", 0.04, " 0.0"); + testf("%5.1f", -0.04, " -0.0"); + testf("%5.1f", 0.051, " 0.1"); + testf("%5.1f", -0.051, " -0.1"); + + testf("%+5.1f", 0, " +0.0"); + testf("%+5.1f", 1, " +1.0"); + testf("%+5.1f", -1, " -1.0"); + testf("%+5.1f", 10, "+10.0"); + testf("%+5.1f", -10, "-10.0"); + testf("%+5.1f", 100, "+100.0"); + testf("%+5.1f", -100, "-100.0"); + testf("%+5.1f", 0.1, " +0.1"); + testf("%+5.1f", -0.1, " -0.1"); + testf("%+5.1f", 0.04, " +0.0"); + testf("%+5.1f", -0.04, " -0.0"); + testf("%+5.1f", 0.051, " +0.1"); + testf("%+5.1f", -0.051, " -0.1"); + + return 0; +} \ No newline at end of file diff --git a/oscar64/autotest/sscanftest.c b/oscar64/autotest/sscanftest.c new file mode 100644 index 0000000..147c3e2 --- /dev/null +++ b/oscar64/autotest/sscanftest.c @@ -0,0 +1,134 @@ +#include +#include +#include +#include + +#define EPSILON 0.0001 + +int scan_i(const char * fmt, const char * tst) { + int val; + + sscanf(tst, fmt, &val); + printf(fmt, val); + puts("\n"); + + return val; +} + +long scan_l(const char * fmt, const char * tst) { + long val; + + sscanf(tst, fmt, &val); + printf(fmt, val); + puts("\n"); + + return val; +} + +unsigned int scan_ui(const char * fmt, const char * tst) { + unsigned int val; + + sscanf(tst, fmt, &val); + printf(fmt, val); + puts("\n"); + + return val; +} + +unsigned long scan_ul(const char * fmt, const char * tst) { + unsigned long val; + + sscanf(tst, fmt, &val); + printf(fmt, val); + puts("\n"); + + return val; +} + +float scan_f(const char * fmt, const char * tst) { + float val; + + sscanf(tst, fmt, &val); + printf(fmt, val); + puts("\n"); + + return val; +} + +bool closeTo(float a, float b) { + while (a < -1.0 || a > 1.0) a /= 10.0; + while (b < -1.0 || b > 1.0) b /= 10.0; + + return fabs(a - b) < EPSILON; +} + +int main(void) +{ + assert(scan_i("%d", "0") == 0); + assert(scan_i("%d", "1") == 1); + assert(scan_i("%d", "-1") == -1); + assert(scan_i("%d", "12") == 12); + assert(scan_i("%d", "123") == 123); + assert(scan_i("%d", "1234") == 1234); + assert(scan_i("%d", "12345") == 12345); + assert(scan_i("%d", "-12345") == -12345); + assert(scan_i("%d", "32767") == 32767); + assert(scan_i("%d", "-32768") == -32768); + + assert(scan_l("%ld", "0") == 0l); + assert(scan_l("%ld", "1") == 1l); + assert(scan_l("%ld", "-1") == -1l); + assert(scan_l("%ld", "12") == 12l); + assert(scan_l("%ld", "123") == 123l); + assert(scan_l("%ld", "1234") == 1234l); + assert(scan_l("%ld", "12345") == 12345l); + assert(scan_l("%ld", "-12345") == -12345l); + assert(scan_l("%ld", "32767") == 32767l); + assert(scan_l("%ld", "-32768") == -32768l); + assert(scan_l("%ld", "2147483647") == 2147483647l); + assert(scan_l("%ld", "-2147483648") == -2147483648l); + + assert(scan_ui("%u", "0") == 0); + assert(scan_ui("%u", "1") == 1); + assert(scan_ui("%u", "12") == 12); + assert(scan_ui("%u", "123") == 123); + assert(scan_ui("%u", "1234") == 1234); + assert(scan_ui("%u", "12345") == 12345); + assert(scan_ui("%u", "32767") == 32767); + assert(scan_ui("%u", "32768") == 32768); + assert(scan_ui("%u", "65535") == 65535); + assert(scan_ui("%x", "0") == 0); + assert(scan_ui("%x", "49BF") == 0x49bf); + assert(scan_ui("%x", "FFFF") == 0xffff); + + assert(scan_ul("%lu", "000") == 0l); + assert(scan_ul("%lu", "001") == 1l); + assert(scan_ul("%lu", "012") == 12l); + assert(scan_ul("%lu", "123") == 123l); + assert(scan_ul("%lu", "1234") == 1234l); + assert(scan_ul("%lu", "12345") == 12345l); + assert(scan_ul("%lu", "32767") == 32767l); + assert(scan_ul("%lu", "2147483647") == 2147483647l); + assert(scan_ul("%lu", "4294967295") == 4294967295l); + assert(scan_ul("%lx", "000") == 0); + assert(scan_ul("%lx", "3576FBCD") == 0x3576fbcdl); + assert(scan_ul("%lx", "FFFFFFFF") == 0xffffffffl); + + assert(closeTo(scan_f("%f", "0.000000"), 0.)); + assert(closeTo(scan_f("%f", "1.000000"), 1.)); + assert(closeTo(scan_f("%f", "-1.000000"), -1.)); + assert(closeTo(scan_f("%f", "12.000000"), 12.)); + assert(closeTo(scan_f("%f", "123.000000"), 123.)); + assert(closeTo(scan_f("%f", "1234.000000"), 1234.)); + assert(closeTo(scan_f("%f", "12345.000000"), 12345.)); + assert(closeTo(scan_f("%f", "123456.000000"), 123456.)); + assert(closeTo(scan_f("%f", "1234567.000000"), 1234567.)); + assert(closeTo(scan_f("%f", "0.100000"), 0.1)); + assert(closeTo(scan_f("%f", "0.010000"), 0.01)); + assert(closeTo(scan_f("%f", "0.001000"), 0.001)); + assert(closeTo(scan_f("%f", "0.000100"), 0.0001)); + assert(closeTo(scan_f("%f", "0.000010"), 0.00001)); + assert(closeTo(scan_f("%f", "0.000001"), 0.000001)); + + return 0; +} \ No newline at end of file diff --git a/oscar64/autotest/staticconsttest.c b/oscar64/autotest/staticconsttest.c new file mode 100644 index 0000000..1c703f2 --- /dev/null +++ b/oscar64/autotest/staticconsttest.c @@ -0,0 +1,20 @@ +#include +#include + +static const int size = 100; + +int main(void) +{ + int a[size]; + + for(int i=0; i +#include +#include +#include + +void numcheck(int n) +{ + char buffer[10]; + itoa(n, buffer, 10); + int m = atoi(buffer); + + printf("%d : %s -> %d\n", n, buffer, m); + + if (m != n) + exit(-1); +} + +void numchecks(void) +{ + numcheck(0); + numcheck(-1); + numcheck(1); + numcheck(12345); + numcheck(-12345); + numcheck(INT_MIN); + numcheck(INT_MAX); +} + +void heapcheck(void) +{ + void * memp[100]; + int mems[100]; + int n, k, s, i; + + for(n=0; n<100; n++) + { + s = rand() % 100 + 3; + mems[n] = s; + memp[n] = malloc(s); + memset(memp[n], n, s); + } + + for(k=0; k<1000; k++) + { + n = rand() % 100; + int s = mems[n]; + char * p = memp[n]; + for(i=0; i +#include +#include + +char dest[2000], src[2000]; + +int main(void) +{ + // Test strcat with basic strings + memset(dest, '#', sizeof(dest)); + strcpy(dest, "hello"); + strcat(dest, " world"); + assert(strcmp(dest, "hello world") == 0); + + // Test strcat with empty string + memset(dest, '#', sizeof(dest)); + strcpy(dest, "test"); + strcat(dest, ""); + assert(strcmp(dest, "test") == 0); + + // Test strcat to empty string + memset(dest, '#', sizeof(dest)); + strcpy(dest, ""); + strcat(dest, "append"); + assert(strcmp(dest, "append") == 0); + + // Test strcat multiple concatenations + memset(dest, '#', sizeof(dest)); + strcpy(dest, "a"); + strcat(dest, "b"); + strcat(dest, "c"); + strcat(dest, "d"); + assert(strcmp(dest, "abcd") == 0); + + // Test strcat with single characters + memset(dest, '#', sizeof(dest)); + strcpy(dest, "x"); + strcat(dest, "y"); + assert(strcmp(dest, "xy") == 0); + + // Test strcat with longer strings + memset(dest, '#', sizeof(dest)); + strcpy(dest, "beginning"); + strcat(dest, "_middle_"); + strcat(dest, "end"); + assert(strcmp(dest, "beginning_middle_end") == 0); + + // Test strncat with basic functionality + memset(dest, '#', sizeof(dest)); + strcpy(dest, "hello"); + strncat(dest, " world", 6); + assert(memcmp(dest, "hello world\0#", 13) == 0); + + // Test strncat with partial concatenation + memset(dest, '#', sizeof(dest)); + strcpy(dest, "hello"); + strncat(dest, " world", 3); + assert(memcmp(dest, "hello wo\0#", 10) == 0); + + // Test strncat with zero length + memset(dest, '#', sizeof(dest)); + strcpy(dest, "original"); + strncat(dest, "ignored", 0); + assert(memcmp(dest, "original\0#", 10) == 0); + + // Test strncat with exact length + memset(dest, '#', sizeof(dest)); + strcpy(dest, "test"); + strncat(dest, "1234", 4); + assert(memcmp(dest, "test1234\0#", 10) == 0); + + // Test strncat where n is larger than source + memset(dest, '#', sizeof(dest)); + strcpy(dest, "base"); + strncat(dest, "add", 10); + assert(memcmp(dest, "baseadd\0#", 9) == 0); + + // Test strncat with empty source + memset(dest, '#', sizeof(dest)); + strcpy(dest, "keep"); + strncat(dest, "", 5); + assert(memcmp(dest, "keep\0#", 6) == 0); + + // Test strncat with empty destination + memset(dest, '#', sizeof(dest)); + strcpy(dest, ""); + strncat(dest, "new", 3); + assert(memcmp(dest, "new\0#", 4) == 0); + + // Test strncat with single character + memset(dest, '#', sizeof(dest)); + strcpy(dest, "a"); + strncat(dest, "bcdef", 1); + assert(memcmp(dest, "ab\0#", 4) == 0); + + // Test strncat with long strings + memset(dest, '#', sizeof(dest)); + for (int i = 0; i < 500; i++) + { + src[i] = 'a' + (i & 7); + } + src[500] = '\0'; + + strcpy(dest, "prefix_"); + strcat(dest, src); + assert(strlen(dest) == 507); // 7 + 500 + assert(strncmp(dest, "prefix_", 7) == 0); + assert(memcmp(dest + 7, src, 500) == 0); + + // Test strncat with long strings + memset(dest, '#', sizeof(dest)); + strcpy(dest, "start_"); + strncat(dest, src, 100); + assert(strlen(dest) == 106); // 6 + 100 + assert(strncmp(dest, "start_", 6) == 0); + assert(strncmp(dest + 6, src, 100) == 0); + assert(dest[106] == '\0'); // ensure null termination + + // Test strncat with partial long string + memset(dest, '#', sizeof(dest)); + for (int i = 0; i < 1000; i++) + { + src[i] = 'x' + (i & 3); + } + src[1000] = '\0'; + + strcpy(dest, "begin"); + strncat(dest, src, 50); + assert(strlen(dest) == 55); // 5 + 50 + assert(strncmp(dest, "begin", 5) == 0); + assert(strncmp(dest + 5, src, 50) == 0); + assert(dest[55] == '\0'); // ensure null termination + + return 0; +} diff --git a/oscar64/autotest/strchrtest.c b/oscar64/autotest/strchrtest.c new file mode 100644 index 0000000..139566d --- /dev/null +++ b/oscar64/autotest/strchrtest.c @@ -0,0 +1,111 @@ +#include +#include +#include + +char teststr[2000]; + +int main(void) +{ + // Test strchr with basic functionality + assert(strcmp(strchr("hello", 'h'), "hello") == 0); + assert(strcmp(strchr("hello", 'e'), "ello") == 0); + assert(strcmp(strchr("hello", 'l'), "llo") == 0); // finds first 'l' + assert(strcmp(strchr("hello", 'o'), "o") == 0); + assert(strchr("hello", 'x') == nullptr); + + // Test strchr with null character + strcpy(teststr, "hello"); + assert(strchr(teststr, '\0') == teststr + 5); // should point to end of string + + // Test strchr with empty string + assert(strchr("", 'a') == nullptr); + strcpy(teststr, ""); + assert(strchr(teststr, '\0') == teststr); // should point to start of + + // Test strchr with single character string + assert(strcmp(strchr("a", 'a'), "a") == 0); + assert(strchr("a", 'b') == nullptr); + strcpy(teststr, "a"); + assert(strchr(teststr, '\0') == teststr + 1); // should point to end of single character string + + // Test strchr with repeated characters + assert(strcmp(strchr("aaaaaa", 'a'), "aaaaaa") == 0); // finds first occurrence + assert(strcmp(strchr("abcabc", 'a'), "abcabc") == 0); // finds first 'a' + assert(strcmp(strchr("abcabc", 'b'), "bcabc") == 0); // finds first 'b' + assert(strcmp(strchr("abcabc", 'c'), "cabc") == 0); // finds first 'c' + + // Test strchr with special characters + assert(strcmp(strchr("hello world", ' '), " world") == 0); + assert(strcmp(strchr("a.b,c;d", '.'), ".b,c;d") == 0); + assert(strcmp(strchr("a.b,c;d", ','), ",c;d") == 0); + assert(strcmp(strchr("a.b,c;d", ';'), ";d") == 0); + + // Test strrchr with basic functionality + assert(strcmp(strrchr("hello", 'h'), "hello") == 0); + assert(strcmp(strrchr("hello", 'e'), "ello") == 0); + assert(strcmp(strrchr("hello", 'l'), "lo") == 0); // finds last 'l' + assert(strcmp(strrchr("hello", 'o'), "o") == 0); + assert(strrchr("hello", 'x') == nullptr); + + // Test strrchr with null character + strcpy(teststr, "hello"); + assert(strrchr(teststr, '\0') == teststr + 5); // should point to end of string + + // Test strrchr with empty string + assert(strrchr("", 'a') == nullptr); + strcpy(teststr, ""); + assert(strrchr(teststr, '\0') == teststr); // should point to start + + // Test strrchr with single character string + assert(strcmp(strrchr("a", 'a'), "a") == 0); + assert(strrchr("a", 'b') == nullptr); // Test strrchr with repeated characters + assert(strcmp(strrchr("aaaaaa", 'a'), "a") == 0); // finds last 'a' + assert(strcmp(strrchr("abcabc", 'a'), "abc") == 0); // finds last 'a' + assert(strcmp(strrchr("abcabc", 'b'), "bc") == 0); // finds last 'b' + assert(strcmp(strrchr("abcabc", 'c'), "c") == 0); // finds last 'c' + + // Test strrchr with special characters + assert(strcmp(strrchr("hello world test", ' '), " test") == 0); + assert(strcmp(strrchr("a.b.c.d", '.'), ".d") == 0); + assert(strcmp(strrchr("a,b,c,d", ','), ",d") == 0); + + // Test with longer strings + strcpy(teststr, "this is a test string with multiple a characters"); + assert(strchr(teststr, 'a') == teststr + 8); // first 'a' at position 8 + assert(strrchr(teststr, 'a') == teststr + 42); // last 'a' at position 42 + + // Test with character not found in long string + assert(strchr(teststr, 'z') == nullptr); + assert(strrchr(teststr, 'z') == nullptr); + + // Test with first and last character + strcpy(teststr, "abcdefghijklmnopqrstuvwxyza"); + assert(strchr(teststr, 'a') == teststr); // first character + assert(strrchr(teststr, 'a') == teststr + 26); // last character + + // Build a long test string with pattern + for(int i = 0; i < 1000; i++) + { + teststr[i] = 'a' + (i % 26); + } + teststr[1000] = '\0'; + + // Test strchr and strrchr with pattern + assert(strchr(teststr, 'a') == teststr); // first 'a' at start + assert(strchr(teststr, 'z') == teststr + 25); // first 'z' at position 25 + assert(strrchr(teststr, 'a') == teststr + 988); // last 'a' at 988 (988 % 26 == 0) + assert(strrchr(teststr, 'z') == teststr + 987); // last 'z' at 987 (987 % 26 == 25) + + // Test with character that appears only once + strcpy(teststr, "abcdefghijklmnopqrstuvwxyz"); + for(char c = 'a'; c <= 'z'; c++) + { + char *first = strchr(teststr, c); + char *last = strrchr(teststr, c); + assert(first == last); // should be same for unique characters + assert(first != nullptr); + assert(*first == c); + } + + return 0; +} diff --git a/oscar64/autotest/strcmptest.c b/oscar64/autotest/strcmptest.c new file mode 100644 index 0000000..d191a3e --- /dev/null +++ b/oscar64/autotest/strcmptest.c @@ -0,0 +1,11 @@ +#include +#include + +int main(void) +{ + assert(strcmp("ABCD", "ABCD") == 0); + assert(strcmp("ABCE", "ABCD") == 1); + assert(strcmp("ABCD", "ABCE") == -1); + + return 0; +} diff --git a/oscar64/autotest/strcmptest2.c b/oscar64/autotest/strcmptest2.c new file mode 100644 index 0000000..a149c3e --- /dev/null +++ b/oscar64/autotest/strcmptest2.c @@ -0,0 +1,77 @@ +#include +#include + +char aa[2000], ba[2000]; + +int main(void) +{ + assert(strcmp("abcdefgh", "abcdefgh") == 0); + assert(strcmp("abcdefgh", "abcdemgh") < 0); + assert(strcmp("abcdefgh", "abcdefghi") < 0); + assert(strcmp("abcdefghi", "abcdefgh") > 0); + assert(strcmp("abcdemgh", "abcdefgh") > 0); + + for(int i=0; i<1900; i++) + { + aa[i] = 'a' + (i & 7); + } + aa[1900] = 0; + + strcpy(ba, aa); + + assert(strcmp(aa, ba) == 0); + ba[1000] = 'z'; + assert(strcmp(aa, ba) < 0); + assert(strcmp(ba, aa) > 0); + + // Test strncmp with basic comparisons + assert(strncmp("abcdefgh", "abcdefgh", 8) == 0); + assert(strncmp("abcdefgh", "abcdemgh", 8) < 0); + assert(strncmp("abcdefgh", "abcdefghi", 8) == 0); // only compare first 8 chars + assert(strncmp("abcdefghi", "abcdefgh", 9) > 0); + assert(strncmp("abcdemgh", "abcdefgh", 8) > 0); + + // Test strncmp with partial comparisons + assert(strncmp("abcdefgh", "abcdemgh", 4) == 0); // first 4 chars are same + assert(strncmp("abcdefgh", "abcdemgh", 6) < 0); // difference at 5th char + assert(strncmp("hello", "help", 3) == 0); // "hel" vs "hel" + assert(strncmp("hello", "help", 4) < 0); // "hell" vs "help" + + // Test strncmp with zero length + assert(strncmp("different", "strings", 0) == 0); // zero length always equal + assert(strncmp("", "", 0) == 0); + assert(strncmp("a", "b", 0) == 0); + + // Test strncmp with empty strings + assert(strncmp("", "", 5) == 0); + assert(strncmp("hello", "", 5) > 0); + assert(strncmp("", "hello", 5) < 0); + assert(strncmp("hello", "", 1) > 0); + assert(strncmp("", "hello", 1) < 0); + + // Test strncmp with single characters + assert(strncmp("a", "a", 1) == 0); + assert(strncmp("a", "b", 1) < 0); + assert(strncmp("b", "a", 1) > 0); + + // Test strncmp where one string is shorter + assert(strncmp("abc", "abcdef", 3) == 0); + assert(strncmp("abc", "abcdef", 6) < 0); // null vs 'd' + assert(strncmp("abcdef", "abc", 6) > 0); // 'd' vs null + + // Test strncmp with long strings + strcpy(ba, aa); // restore ba to match aa + assert(strncmp(aa, ba, 1900) == 0); + ba[1000] = 'z'; + assert(strncmp(aa, ba, 1000) == 0); // equal up to position 1000 + assert(strncmp(aa, ba, 1001) < 0); // difference at position 1000 + assert(strncmp(ba, aa, 1001) > 0); // difference at position 1000 + assert(strncmp(aa, ba, 999) == 0); // equal before the difference + + // Test strncmp with length larger than string length + strcpy(ba, "short"); + assert(strncmp("short", ba, 100) == 0); // equal even with large n + assert(strncmp("short", "shorx", 100) < 0); // difference within string + + return 0; +} diff --git a/oscar64/autotest/strcpytest.c b/oscar64/autotest/strcpytest.c new file mode 100644 index 0000000..e3c3186 --- /dev/null +++ b/oscar64/autotest/strcpytest.c @@ -0,0 +1,81 @@ +#include +#include +#include + +char dest[1025], src[1025]; + +int main(void) +{ + strcpy(dest, "hello"); + assert(strcmp(dest, "hello") == 0); + + strcpy(dest, ""); + assert(strcmp(dest, "") == 0); + + strcpy(dest, "a"); + assert(strcmp(dest, "a") == 0); + + strcpy(dest, "abcdefghijklmnopqrstuvwxyz"); + assert(strcmp(dest, "abcdefghijklmnopqrstuvwxyz") == 0); + + // Test strcpy with long string + for (int i = 0; i < 1024; i++) + { + src[i] = 'a' + (i & 7); + } + src[1024] = 0; + + strcpy(dest, src); + assert(strcmp(dest, src) == 0); + + // Test strncpy with various lengths + strcpy(dest, "xxxxxxxxxx"); + strncpy(dest, "hello", 5); + dest[5] = 0; // null terminate for comparison + assert(strcmp(dest, "hello") == 0); + + strcpy(dest, "xxxxxxxxxx"); + strncpy(dest, "hello", 3); + dest[3] = 0; // null terminate for comparison + assert(strcmp(dest, "hel") == 0); + + strcpy(dest, "xxxxxxxxxx"); + strncpy(dest, "hi", 5); + assert(dest[0] == 'h'); + assert(dest[1] == 'i'); + assert(dest[2] == 0); + assert(dest[3] == 0); + assert(dest[4] == 0); + + // Test strncpy with exact length + strcpy(dest, "xxxxxxxxxx"); + strncpy(dest, "test", 4); + dest[4] = 0; // null terminate for comparison + assert(strcmp(dest, "test") == 0); + + // Test strncpy with zero length + strcpy(dest, "original"); + strncpy(dest, "new", 0); + assert(strcmp(dest, "original") == 0); + + // Test strncpy copying longer than source + strcpy(dest, "xxxxxxxxxx"); + strncpy(dest, "ab", 8); + assert(dest[0] == 'a'); + assert(dest[1] == 'b'); + assert(dest[2] == 0); + assert(dest[3] == 0); + assert(dest[4] == 0); + assert(dest[5] == 0); + assert(dest[6] == 0); + assert(dest[7] == 0); + + // Test strncpy with large string + strcpy(dest, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); + strncpy(dest, src, 1000); + dest[1000] = 0; // ensure null termination + src[1000] = 0; // truncate source for comparison + assert(strcmp(dest, src) == 0); + + return 0; +} diff --git a/oscar64/autotest/stripedarraytest.c b/oscar64/autotest/stripedarraytest.c new file mode 100644 index 0000000..cbbdc27 --- /dev/null +++ b/oscar64/autotest/stripedarraytest.c @@ -0,0 +1,299 @@ +#include + + +int a100[100]; +__striped int b100[100]; +__striped int c100[100]; + +unsigned a256[256]; +__striped unsigned b256[256]; +__striped unsigned c256[256]; + +#pragma align(c100, 256) +#pragma align(c256, 256) + +void test_ab100(void) +{ + for(char i=0; i<100; i++) + { + a100[i] = i * i; + b100[i] = i * i; + c100[i] = i * i; + } + + a100[31] = 4711; + b100[31] = 4711; + c100[31] = 4711; + + + for(char i=0; i<100; i++) + { + a100[i] += i; a100[i] -= 5; a100[i] = a100[i] + a100[99 - i]; + b100[i] += i; b100[i] -= 5; b100[i] = b100[i] + b100[99 - i]; + c100[i] += i; c100[i] -= 5; c100[i] = c100[i] + c100[99 - i]; + } + + for(char i=0; i<100; i++) + { + assert(a100[i] == b100[i]); + assert(a100[i] == c100[i]); + } +} + +void test_ab256(void) +{ + for(unsigned i=0; i<256; i++) + { + a256[i] = i * i; + b256[i] = i * i; + c256[i] = i * i; + } + + a256[31] = 4711; + b256[31] = 4711; + c256[31] = 4711; + + for(unsigned i=0; i<256; i++) + { + a256[i] += i; a256[i] -= 5; a256[i] = a256[i] + a256[255 - i]; + b256[i] += i; b256[i] -= 5; b256[i] = b256[i] + b256[255 - i]; + c256[i] += i; c256[i] -= 5; c256[i] = c256[i] + c256[255 - i]; + } + + for(unsigned i=0; i<256; i++) + { + assert(a256[i] == b256[i]); + assert(a256[i] == c256[i]); + } +} + +long la50[50]; +__striped long lb50[50]; +__striped long lc50[50]; + +unsigned long la256[256]; +__striped unsigned long lb256[256]; +__striped unsigned long lc256[256]; + +#pragma align(lc50, 256) +#pragma align(lc256, 256) + +void test_lab50(void) +{ + for(char i=0; i<50; i++) + { + long j = i * i; + la50[i] = j * j; + lb50[i] = j * j; + lc50[i] = j * j; + } + + la50[31] = 47110815l; + lb50[31] = 47110815l; + lc50[31] = 47110815l; + + for(char i=0; i<50; i++) + { + la50[i] += i; la50[i] -= 12345678l; la50[i] = la50[i] + la50[49 - i]; + lb50[i] += i; lb50[i] -= 12345678l; lb50[i] = lb50[i] + lb50[49 - i]; + lc50[i] += i; lc50[i] -= 12345678l; lc50[i] = lc50[i] + lc50[49 - i]; + } + + for(char i=0; i<50; i++) + { + assert(la50[i] == lb50[i]); + assert(la50[i] == lc50[i]); + } +} + +void test_lab256(void) +{ + for(unsigned i=0; i<256; i++) + { + unsigned long j = i * i; + la256[i] = j * j; + lb256[i] = j * j; + lc256[i] = j * j; + } + + la256[31] = 47110815ul; + lb256[31] = 47110815ul; + lc256[31] = 47110815ul; + + for(unsigned i=0; i<256; i++) + { + la256[i] += i; la256[i] -= 12345678ul; la256[i] = la256[i] + la256[255 - i]; + lb256[i] += i; lb256[i] -= 12345678ul; lb256[i] = lb256[i] + lb256[255 - i]; + lc256[i] += i; lc256[i] -= 12345678ul; lc256[i] = lc256[i] + lc256[255 - i]; + } + + for(unsigned i=0; i<256; i++) + { + assert(la256[i] == lb256[i]); + assert(la256[i] == lc256[i]); + } +} + +float fa50[50]; +__striped float fb50[50]; +__striped float fc50[50]; + +float fa256[256]; +__striped float fb256[256]; +__striped float fc256[256]; + +#pragma align(fc50, 256) +#pragma align(fc256, 256) + +void test_fab50(void) +{ + for(char i=0; i<50; i++) + { + fa50[i] = i * i; + fb50[i] = i * i; + fc50[i] = i * i; + } + + fa50[31] = 4711.0815; + fb50[31] = 4711.0815; + fc50[31] = 4711.0815; + + for(char i=0; i<50; i++) + { + fa50[i] += i; fa50[i] -= 1234.5678; fa50[i] = fa50[i] + fa50[49 - i]; + fb50[i] += i; fb50[i] -= 1234.5678; fb50[i] = fb50[i] + fb50[49 - i]; + fc50[i] += i; fc50[i] -= 1234.5678; fc50[i] = fc50[i] + fc50[49 - i]; + } + + for(char i=0; i<50; i++) + { + assert(fa50[i] == fb50[i]); + assert(fa50[i] == fc50[i]); + } +} + +void test_fab256(void) +{ + for(unsigned i=0; i<256; i++) + { + fa256[i] = i * i; + fb256[i] = i * i; + fc256[i] = i * i; + } + + fa256[31] = 4711.0815; + fb256[31] = 4711.0815; + fc256[31] = 4711.0815; + + for(unsigned i=0; i<256; i++) + { + fa256[i] += i; fa256[i] -= 1234.5678; fa256[i] = fa256[i] + fa256[255 - i]; + fb256[i] += i; fb256[i] -= 1234.5678; fb256[i] = fb256[i] + fb256[255 - i]; + fc256[i] += i; fc256[i] -= 1234.5678; fc256[i] = fc256[i] + fc256[255 - i]; + } + + for(unsigned i=0; i<256; i++) + { + assert(fa256[i] == fb256[i]); + assert(fa256[i] == fc256[i]); + } +} + + +unsigned da50[50], db50[50], dc50[50]; + +unsigned * pa50[50]; +__striped unsigned * pb50[50]; +__striped unsigned * pc50[50]; + +#pragma align(pc50, 256) + +void test_pab50(void) +{ + for(char i=0; i<50; i++) + { + pa50[i] = da50 + (i * 17) % 50; + pb50[i] = db50 + (i * 17) % 50; + pc50[i] = dc50 + (i * 17) % 50; + } + + for(char i=0; i<50; i++) + { + *pa50[i] = i * i; + *pb50[i] = i * i; + *pc50[i] = i * i; + } + + for(char i=0; i<50; i++) + { + *pa50[i] += i; *pa50[i] -= 5; *pa50[i] = *pa50[i] + *pa50[49 - i]; + *pb50[i] += i; *pb50[i] -= 5; *pb50[i] = *pb50[i] + *pb50[49 - i]; + *pc50[i] += i; *pc50[i] -= 5; *pc50[i] = *pc50[i] + *pc50[49 - i]; + } + + for(char i=0; i<50; i++) + { + assert(*pa50[i] == *pb50[i]); + assert(*pa50[i] == *pc50[i]); + assert(da50[i] == db50[i]); + assert(da50[i] == dc50[i]); + } +} + +unsigned da50_4[50][4], db50_4[50][4], dc50_4[50][4]; + +void test_pab50_4(void) +{ + for(char i=0; i<50; i++) + { + pa50[i] = da50_4[(i * 17) % 50]; + pb50[i] = db50_4[(i * 17) % 50]; + pc50[i] = dc50_4[(i * 17) % 50]; + } + + for(char k=0; k<4; k++) + { + for(char i=0; i<50; i++) + { + pa50[i][k] = i * i; + pb50[i][k] = i * i; + pc50[i][k] = i * i; + } + } + + for(char k=0; k<4; k++) + { + for(char i=0; i<50; i++) + { + pa50[i][k] += i; pa50[i][k] -= 5; pa50[i][k] = pa50[i][k] + pa50[49 - i][k]; + pb50[i][k] += i; pb50[i][k] -= 5; pb50[i][k] = pb50[i][k] + pb50[49 - i][k]; + pc50[i][k] += i; pc50[i][k] -= 5; pc50[i][k] = pc50[i][k] + pc50[49 - i][k]; + } + } + + for(char k=0; k<4; k++) + { + for(char i=0; i<50; i++) + { + assert(pa50[i][k] == pb50[i][k]); + assert(pa50[i][k] == pc50[i][k]); + assert(da50_4[i][k] == db50_4[i][k]); + assert(da50_4[i][k] == dc50_4[i][k]); + } + } +} + + +int main(void) +{ + test_ab100(); + test_ab256(); + test_lab50(); + test_lab256(); + test_fab50(); + test_fab256(); + test_pab50(); + test_pab50_4(); + + return 0; +} diff --git a/oscar64/autotest/stripedstructtest.c b/oscar64/autotest/stripedstructtest.c new file mode 100644 index 0000000..a9cb4c5 --- /dev/null +++ b/oscar64/autotest/stripedstructtest.c @@ -0,0 +1,57 @@ +#include + +struct A +{ + char x; + unsigned y; + long z; +}; + +__striped A a[4]; + +__striped A c[4] = { + {1, 1000, 100000}, + {2, 2000, 200000}, + {3, 3000, 300000}, + {4, 4000, 400000} +}; + +__noinline long test_read(int k, char v) +{ + A t = c[k]; + + if (t.x == v) + return t.y; + else + return t.z; +} + +__noinline long test_write(int k, char v, long l) +{ + A t = a[k]; + + if (t.x == v) + t.y = l; + else + t.z = l; + + return t.z; +} + +int main(void) +{ + assert(test_read(0, 1) == 1000); + assert(test_read(0, 0) == 100000); + assert(test_read(2, 3) == 3000); + assert(test_read(2, 0) == 300000); + + for(char i=0; i<4; i++) + a[i] = c[i]; + + assert(test_write(0, 1, 4711) == 100000); + assert(test_write(0, 0, 4711) == 4711); + assert(test_write(2, 3, 4711) == 300000); + assert(test_write(2, 0, 4711) == 4711); + + return 0; +} diff --git a/oscar64/autotest/strlen.c b/oscar64/autotest/strlen.c new file mode 100644 index 0000000..41ee4dc --- /dev/null +++ b/oscar64/autotest/strlen.c @@ -0,0 +1,27 @@ +#include +#include + +char lstr[1025]; + +int main(void) +{ +#if 1 + assert(strlen("") == 0); + assert(strlen("1") == 1); + assert(strlen("12") == 2); + assert(strlen("123") == 3); + assert(strlen("1234") == 4); + assert(strlen("12345") == 5); + assert(strlen("123456") == 6); +#endif +#if 1 + char * dp = lstr; + for(int i=0; i<1024; i++) + { + *dp = 0; + assert(strlen(lstr) == i); + *dp++ = 'a'; + } +#endif + return 0; +} diff --git a/oscar64/autotest/strparsetest.c b/oscar64/autotest/strparsetest.c new file mode 100644 index 0000000..d9bc983 --- /dev/null +++ b/oscar64/autotest/strparsetest.c @@ -0,0 +1,75 @@ +#include +#include + +void check_strtol(const char * str, char base, long val, char term) +{ + char * t; + assert(val == strtol(str, &t, base)); + assert(*t == term); +} + +void check_strtoul(const char * str, char base, unsigned long val, char term) +{ + char * t; + assert(val == strtoul(str, &t, base)); + assert(*t == term); +} + +void check_strtoi(const char * str, char base, int val, char term) +{ + char * t; + assert(val == strtoi(str, &t, base)); + assert(*t == term); +} + +void check_strtou(const char * str, char base, unsigned val, char term) +{ + char * t; + assert(val == strtou(str, &t, base)); + assert(*t == term); +} + +void check_strtof(const char * str, float val, char term) +{ + char * t; + assert(val == strtof(str, &t)); + assert(*t == term); +} + +int main(void) +{ + check_strtol("123", 10, 123, '\0'); + check_strtol("123x", 10, 123, 'x'); + check_strtol(" 123x", 10, 123, 'x'); + check_strtol("-123x", 10, -123, 'x'); + + check_strtoul("123", 10, 123, '\0'); + check_strtoul("123x", 10, 123, 'x'); + check_strtoul(" 123x", 10, 123, 'x'); + + check_strtoi("123", 10, 123, '\0'); + check_strtoi("123x", 10, 123, 'x'); + check_strtoi(" 123x", 10, 123, 'x'); + check_strtoi("-123x", 10, -123, 'x'); + + check_strtou("123", 10, 123, '\0'); + check_strtou("123x", 10, 123, 'x'); + check_strtou(" 123x", 10, 123, 'x'); + check_strtof("123", 123, '\0'); + check_strtof("123x", 123, 'x'); + check_strtof(" 123x", 123, 'x'); + check_strtof("-123x", -123, 'x'); + check_strtof("123.5", 123.5, '\0'); + check_strtof("123.5x", 123.5, 'x'); + check_strtof(" 123.5x", 123.5, 'x'); + check_strtof("-123.5x", -123.5, 'x'); + check_strtof("123e2", 12300, '\0'); + check_strtof("123e2x", 12300, 'x'); + check_strtof(" 123e2x", 12300, 'x'); + check_strtof("-123e2x", -12300, 'x'); + check_strtof("123.5e2", 12350, '\0'); + check_strtof("123.5e2x", 12350, 'x'); + check_strtof(" 123.5e2x", 12350, 'x'); + check_strtof("-123.5e2x", -12350, 'x'); + return 0; +} \ No newline at end of file diff --git a/oscar64/autotest/strstrtest.c b/oscar64/autotest/strstrtest.c new file mode 100644 index 0000000..87a3156 --- /dev/null +++ b/oscar64/autotest/strstrtest.c @@ -0,0 +1,100 @@ +#include +#include +#include + +char teststr[2000], substr[200]; + +int main(void) +{ + // Test strstr with basic functionality + assert(strcmp(strstr("hello world", "hello"), "hello world") == 0); + assert(strcmp(strstr("hello world", "world"), "world") == 0); + assert(strcmp(strstr("hello world", "lo wo"), "lo world") == 0); + assert(strstr("hello world", "xyz") == nullptr); + + // Test strstr with empty substring + assert(strcmp(strstr("hello", ""), "hello") == 0); // empty substring matches at start + assert(strcmp(strstr("", ""), "") == 0); + + // Test strstr with substring not found + assert(strstr("hello", "world") == nullptr); + assert(strstr("abc", "xyz") == nullptr); + assert(strstr("short", "longer") == nullptr); + + // Test strstr with single character substring + assert(strcmp(strstr("hello", "h"), "hello") == 0); + assert(strcmp(strstr("hello", "e"), "ello") == 0); + assert(strcmp(strstr("hello", "o"), "o") == 0); + assert(strstr("hello", "x") == nullptr); + + // Test strstr with substring at different positions + assert(strcmp(strstr("abcdefgh", "abc"), "abcdefgh") == 0); // at start + assert(strcmp(strstr("abcdefgh", "def"), "defgh") == 0); // in middle + assert(strcmp(strstr("abcdefgh", "fgh"), "fgh") == 0); // at end + + // Test strstr with repeated patterns + assert(strcmp(strstr("ababab", "ab"), "ababab") == 0); // finds first occurrence + assert(strcmp(strstr("ababab", "ba"), "babab") == 0); + assert(strcmp(strstr("ababab", "bab"), "babab") == 0); + + // Test strstr with overlapping patterns + assert(strcmp(strstr("aaaa", "aa"), "aaaa") == 0); // finds first match + assert(strcmp(strstr("ababa", "aba"), "ababa") == 0); + + // Test strstr where substring equals the string + assert(strcmp(strstr("test", "test"), "test") == 0); + assert(strcmp(strstr("a", "a"), "a") == 0); + + // Test strstr with special characters + assert(strcmp(strstr("hello, world!", "o, w"), "o, world!") == 0); + assert(strcmp(strstr("a.b.c.d", ".b."), ".b.c.d") == 0); + assert(strcmp(strstr("x y z", " y "), " y z") == 0); + + // Build a long test string with pattern + for(int i = 0; i < 1900; i++) + { + teststr[i] = 'a' + (i & 7); + } + teststr[1900] = '\0'; + + // Test strstr with long strings + strcpy(substr, "abcdefgh"); + char *result = strstr(teststr, substr); + assert(result == teststr); // pattern starts at beginning + assert(strncmp(result, substr, 8) == 0); + + // Test strstr with pattern that appears later + strcpy(substr, "bcdefgha"); + result = strstr(teststr, substr); + assert(result == teststr + 1); // pattern starts at position 1 + assert(strncmp(result, substr, 8) == 0); + + // Test strstr with pattern not in string + strcpy(substr, "zzzzz"); + assert(strstr(teststr, substr) == nullptr); + + // Test strstr with longer substring than string + strcpy(teststr, "short"); + strcpy(substr, "this is longer"); + assert(strstr(teststr, substr) == nullptr); + + // Test strstr with case sensitivity + assert(strstr("Hello", "hello") == nullptr); // case sensitive + assert(strcmp(strstr("Hello", "Hello"), "Hello") == 0); + + // Test strstr with partial matches that fail + assert(strstr("abcde", "abcx") == nullptr); + assert(strstr("testing", "tester") == nullptr); + + // Test strstr with multiple occurrences (finds first) + strcpy(teststr, "the quick brown fox jumps over the lazy dog"); + assert(strcmp(strstr(teststr, "the"), "the quick brown fox jumps over the lazy dog") == 0); + assert(strcmp(strstr(teststr, "o"), "own fox jumps over the lazy dog") == 0); // first 'o' + + // Test strstr with whole word searches + assert(strcmp(strstr(teststr, "quick"), "quick brown fox jumps over the lazy dog") == 0); + assert(strcmp(strstr(teststr, "fox"), "fox jumps over the lazy dog") == 0); + assert(strcmp(strstr(teststr, "dog"), "dog") == 0); + + return 0; +} diff --git a/oscar64/autotest/structarraycopy.c b/oscar64/autotest/structarraycopy.c new file mode 100644 index 0000000..675ed98 --- /dev/null +++ b/oscar64/autotest/structarraycopy.c @@ -0,0 +1,49 @@ + + +struct Point +{ + int x, y; +}; + + +Point tcorners[8], pcorners[8]; + +int bm_line(void) +{ + return 0; +} + +void drawCube(void) +{ + for(char i=0; i<8; i++) + { + if (!(i & 1)) + bm_line(); + if (!(i & 2)) + bm_line(); + if (!(i & 4)) + bm_line(); + + pcorners[i] = tcorners[i]; + } +} + +int main(void) +{ + for(int i=0; i<8; i++) + { + tcorners[i].x = (i + 1) * 3; + tcorners[i].y = (i + 1) * 7; + } + + drawCube(); + + int sum = 0; + for(int i=0; i<8; i++) + { + sum += pcorners[i].x; + sum -= tcorners[i].y; + } + + return sum + 144; +} \ No newline at end of file diff --git a/oscar64/autotest/structassigntest.c b/oscar64/autotest/structassigntest.c new file mode 100644 index 0000000..da79c55 --- /dev/null +++ b/oscar64/autotest/structassigntest.c @@ -0,0 +1,31 @@ +#include + +struct Vec3 +{ + float x, y, z; +}; + +Vec3 v; + +Vec3 vadd(Vec3 s1, Vec3 s2) +{ + Vec3 r; + r.x = s1.x + s2.x; + r.y = s1.y + s2.y; + r.z = s1.z + s2.z; + return r; +} + + +int main(void) +{ + Vec3 m = {1, 2, -3}, u = {4, 5, -9}, t = {7, -2, -5}; + + v.x = 99; + v.y = 100; + v.z = 101; + + v = vadd(m, vadd(u, t)); + + return v.x + v.y + v.z; +} diff --git a/oscar64/autotest/structconditionaltest.c b/oscar64/autotest/structconditionaltest.c new file mode 100644 index 0000000..55438d5 --- /dev/null +++ b/oscar64/autotest/structconditionaltest.c @@ -0,0 +1,190 @@ +#include + +struct Pair +{ + char first, second; +}; + +static int make_count; + +static struct Pair make_pair(char first, char second) +{ + struct Pair pair = { first, second }; + make_count++; + return pair; +} + +static struct Pair choose_values(int choose_left, struct Pair left, struct Pair right) +{ + return choose_left ? left : right; +} + +static struct Pair choose_calls(int choose_left) +{ + struct Pair pair = choose_left ? make_pair(1, 2) : make_pair(3, 4); + return pair; +} + +static char choose_values_first(int choose_left, struct Pair left, struct Pair right) +{ + return (choose_left ? left : right).first; +} + +static char choose_values_second(int choose_left, struct Pair left, struct Pair right) +{ + return (choose_left ? left : right).second; +} + +static struct Pair choose_calls_return(int choose_left) +{ + return choose_left ? make_pair(1, 2) : make_pair(3, 4); +} + +static char choose_calls_first(int choose_left) +{ + return (choose_left ? make_pair(1, 2) : make_pair(3, 4)).first; +} + +static char choose_calls_second(int choose_left) +{ + return (choose_left ? make_pair(1, 2) : make_pair(3, 4)).second; +} + + +static struct Pair assign_calls(int choose_left) +{ + struct Pair pair; + + if (choose_left) + pair = make_pair(5, 6); + else + pair = make_pair(7, 8); + return pair; +} + +typedef unsigned long u32; + +struct BigPair +{ + u32 first, second; +}; + +static int make_count_big; + +static struct BigPair make_pair_big(u32 first, u32 second) +{ + struct BigPair pair = { first, second }; + make_count_big++; + return pair; +} + +static struct BigPair choose_values_big(int choose_left, struct BigPair left, struct BigPair right) +{ + return choose_left ? left : right; +} + +static u32 choose_values_first_big(int choose_left, struct BigPair left, struct BigPair right) +{ + return (choose_left ? left : right).first; +} + +static u32 choose_values_second_big(int choose_left, struct BigPair left, struct BigPair right) +{ + return (choose_left ? left : right).second; +} + +static struct BigPair choose_calls_big(int choose_left) +{ + struct BigPair pair = choose_left ? make_pair_big(1, 2) : make_pair_big(3, 4); + return pair; +} + +static struct BigPair choose_calls_return_big(int choose_left) +{ + return choose_left ? make_pair_big(1, 2) : make_pair_big(3, 4); +} + +static u32 choose_calls_first_big(int choose_left) +{ + return (choose_left ? make_pair_big(1, 2) : make_pair_big(3, 4)).first; +} + +static u32 choose_calls_second_big(int choose_left) +{ + return (choose_left ? make_pair_big(1, 2) : make_pair_big(3, 4)).second; +} + +static struct BigPair assign_calls_big(int choose_left) +{ + struct BigPair pair; + + if (choose_left) + pair = make_pair_big(5, 6); + else + pair = make_pair_big(7, 8); + return pair; +} + +__noinline void test_pair(void) +{ + struct Pair left = { 9, 10 }, right = { 11, 12 }; + struct Pair a = choose_values(0, left, right); + struct Pair b = choose_calls(1); + struct Pair c = assign_calls(0); + struct Pair d = choose_calls_return(1); + + assert(a.first == 11); + assert(a.second == 12); + assert(b.first == 1); + assert(b.second == 2); + assert(c.first == 7); + assert(c.second == 8); + assert(d.first == 1); + assert(d.second == 2); + + assert(choose_values_first(0, left, right) == 11); + assert(choose_values_second(0, left, right) == 12); + + assert(choose_calls_first(0) == 3); + assert(choose_calls_second(0) == 4); + assert(choose_calls_first(1) == 1); + assert(choose_calls_second(1) == 2); + + assert(make_count == 7); +} + +__noinline void test_pair_big(void) +{ + struct BigPair left = { 9, 10 }, right = { 11, 12 }; + struct BigPair a = choose_values_big(0, left, right); + struct BigPair b = choose_calls_big(1); + struct BigPair c = assign_calls_big(0); + struct BigPair d = choose_calls_return_big(1); + + assert(a.first == 11); + assert(a.second == 12); + assert(b.first == 1); + assert(b.second == 2); + assert(c.first == 7); + assert(c.second == 8); + assert(d.first == 1); + assert(d.second == 2); + + assert(choose_values_first_big(0, left, right) == 11); + assert(choose_values_second_big(0, left, right) == 12); + + assert(choose_calls_first_big(0) == 3); + assert(choose_calls_second_big(0) == 4); + assert(choose_calls_first_big(1) == 1); + assert(choose_calls_second_big(1) == 2); + + assert(make_count_big == 7); +} + +int main(void) +{ + test_pair(); + test_pair_big(); + + return 0; +} diff --git a/oscar64/autotest/structmembertest.c b/oscar64/autotest/structmembertest.c new file mode 100644 index 0000000..f59b361 --- /dev/null +++ b/oscar64/autotest/structmembertest.c @@ -0,0 +1,50 @@ +#include + +struct A +{ + int x[100], y[100]; +}; + +struct V +{ + int x, y, z; +}; + + +void copy(A * a) +{ + for(int i=0; i<100; i++) + a->x[i] = a->y[i]; +} + +void shuffle(V * v) +{ + for(int i=0; i<100; i++) + v[i].x = v[i].y; +} + +int main(void) +{ + A a; + V v[100]; + + for(int i=0; i<100; i++) + { + a.y[i] = i; + v[i].y = i; + } + + copy(&a); + shuffle(v); + + for(int i=0; i<100; i++) + { + assert(a.x[i] == i); + assert(v[i].x == i); + } + + return 0; +} + + + diff --git a/oscar64/autotest/structoffsettest2.c b/oscar64/autotest/structoffsettest2.c new file mode 100644 index 0000000..a0b5a92 --- /dev/null +++ b/oscar64/autotest/structoffsettest2.c @@ -0,0 +1,25 @@ +struct A +{ + int x; + struct B + { + int m; + struct C + { + int w; + } c; + } b; +} q; + +int test(A * a) +{ + a->b.c.w = 1; + return a->b.c.w; +} + +int main(void) +{ + return test(&q) - 1; +} + + diff --git a/oscar64/autotest/structsplittest.c b/oscar64/autotest/structsplittest.c new file mode 100644 index 0000000..0d5b56b --- /dev/null +++ b/oscar64/autotest/structsplittest.c @@ -0,0 +1,329 @@ +#include +#include + +struct A +{ + char a, b; + int c; + long d; +}; + +struct B +{ + char a[3], b; + int c; + long d; +}; + +__noinline void ssplit1(char t) +{ + A x; + x.a = 12; + x.b = 43; + x.c = 100; + for(int i=0; i<10; i++) + { + x.c += t; + x.a++; + } + assert(x.c == t * 10 + 100); + assert(x.a == 22); +} + +__noinline void ssplit2(char t) +{ + A x, y; + x.a = 12; + x.b = 43; + x.c = 100; + y.a = 12; + char * q = &y.a; + for(int i=0; i<10; i++) + { + x.c += t; + x.a++; + } + assert(x.c == t * 10 + 100); + assert(x.a == 22); + assert(*q == 12); +} + + +__noinline void nossplit1(char t) +{ + A x, y; + x.a = 12; + x.b = 43; + x.c = 100; + for(int i=0; i<10; i++) + { + x.c += t; + x.a++; + } + y = x; + assert(y.c == t * 10 + 100); + assert(y.a == 22); +} + +__noinline void nossplit2(char t) +{ + A x, y; + x.a = 12; + x.b = 43; + x.c = 100; + y = x; + for(int i=0; i<10; i++) + { + x.c += t; + x.a++; + } + assert(x.c == t * 10 + 100); + assert(x.a == 22); +} + +__noinline void nossplit3(char t) +{ + A x; + x.a = 12; + x.b = 43; + x.c = 100; + char * q = &x.a; + for(int i=0; i<10; i++) + { + x.c += t; + x.a++; + } + assert(x.c == t * 10 + 100); + assert(*q == 22); +} + +__noinline void nossplit4(char t) +{ + A x = {12, 43, 100}; + for(int i=0; i<10; i++) + { + x.c += t; + x.a++; + } + assert(x.c == t * 10 + 100); + assert(x.a == 22); +} + +__noinline void asplit1(char t) +{ + int x[4]; + x[0] = 12; + x[1] = 43; + x[2] = 100; + for(int i=0; i<10; i++) + { + x[2] += t; + x[0]++; + } + assert(x[2] == t * 10 + 100); + assert(x[0] == 22); +} + +__noinline void asplit2(char t) +{ + int x[4], y[4]; + x[0] = 12; + x[1] = 43; + x[2] = 100; + y[0] = 12; + int * q = &y[0]; + for(int i=0; i<10; i++) + { + x[2] += t; + x[0]++; + } + assert(x[2] == t * 10 + 100); + assert(x[0] == 22); + assert(*q == 12); +} + + +__noinline void noasplit1(char t) +{ + int x[4], y[4]; + x[0] = 12; + x[1] = 43; + x[2] = 100; + for(int i=0; i<10; i++) + { + x[2] += t; + x[0]++; + } + memcpy(y, x, sizeof(y)); + assert(y[2] == t * 10 + 100); + assert(y[0] == 22); +} + +__noinline void noasplit2(char t) +{ + int x[4], y[4]; + x[0] = 12; + x[1] = 43; + x[2] = 100; + memcpy(y, x, sizeof(y)); + for(int i=0; i<10; i++) + { + x[2] += t; + x[0]++; + } + assert(x[2] == t * 10 + 100); + assert(x[0] == 22); +} + +__noinline void noasplit3(char t) +{ + int x[4]; + x[0] = 12; + x[1] = 43; + x[2] = 100; + int * q = &x[0]; + for(int i=0; i<10; i++) + { + x[2] += t; + x[0]++; + } + assert(x[2] == t * 10 + 100); + assert(*q == 22); +} + +__noinline void noasplit4(char t) +{ + int x[4] = {12, 43, 100}; + for(int i=0; i<10; i++) + { + x[2] += t; + x[0]++; + } + assert(x[2] == t * 10 + 100); + assert(x[0] == 22); +} + + +__noinline void sasplit1(char t) +{ + B x; + x.a[1] = 12; + x.b = 43; + x.c = 100; + for(int i=0; i<10; i++) + { + x.c += t; + x.a[1]++; + } + assert(x.c == t * 10 + 100); + assert(x.a[1] == 22); +} + +__noinline void sasplit2(char t) +{ + B x, y; + x.a[1] = 12; + x.b = 43; + x.c = 100; + y.a[1] = 12; + char * q = &y.a[1]; + for(int i=0; i<10; i++) + { + x.c += t; + x.a[1]++; + } + assert(x.c == t * 10 + 100); + assert(x.a[1] == 22); + assert(*q == 12); +} + + +__noinline void nosasplit1(char t) +{ + B x, y; + x.a[1] = 12; + x.b = 43; + x.c = 100; + for(int i=0; i<10; i++) + { + x.c += t; + x.a[1]++; + } + y = x; + assert(y.c == t * 10 + 100); + assert(y.a[1] == 22); +} + +__noinline void nosasplit2(char t) +{ + B x, y; + x.a[1] = 12; + x.b = 43; + x.c = 100; + y = x; + for(int i=0; i<10; i++) + { + x.c += t; + x.a[1]++; + } + assert(x.c == t * 10 + 100); + assert(x.a[1] == 22); +} + +__noinline void nosasplit3(char t) +{ + B x; + x.a[1] = 12; + x.b = 43; + x.c = 100; + char * q = &x.a[1]; + for(int i=0; i<10; i++) + { + x.c += t; + x.a[1]++; + } + assert(x.c == t * 10 + 100); + assert(*q == 22); +} + +__noinline void nosasplit4(char t) +{ + B x = {{4, 12, 7}, 43, 100}; + for(int i=0; i<10; i++) + { + x.c += t; + x.a[1]++; + } + assert(x.c == t * 10 + 100); + assert(x.a[1] == 22); +} + + +int main(void) +{ + ssplit1(11); ssplit1(33); + ssplit2(11); ssplit2(33); + + nossplit1(11); nossplit1(33); + nossplit2(11); nossplit2(33); + nossplit3(11); nossplit3(33); + nossplit4(11); nossplit4(33); + + asplit1(11); asplit1(33); + asplit2(11); asplit2(33); + + noasplit1(11); noasplit1(33); + noasplit2(11); noasplit2(33); + noasplit3(11); noasplit3(33); + noasplit4(11); noasplit4(33); + + sasplit1(11); sasplit1(33); + sasplit2(11); sasplit2(33); + + nosasplit1(11); nosasplit1(33); + nosasplit2(11); nosasplit2(33); + nosasplit3(11); nosasplit3(33); + nosasplit4(11); nosasplit4(33); + + return 0; +} \ No newline at end of file diff --git a/oscar64/autotest/switchlooptest.c b/oscar64/autotest/switchlooptest.c new file mode 100644 index 0000000..9c4c38c --- /dev/null +++ b/oscar64/autotest/switchlooptest.c @@ -0,0 +1,48 @@ +#include +#include +#include + +int main(void) +{ + char text[14]; + + for(char i=0; i<12; i++) + { + switch (i) + { + case 0: + text[i] = 'H'; + break; + case 1: + text[i] = 'E'; + break; + case 2: + case 3: + case 9: + text[i] = 'L'; + break; + case 4: + case 7: + text[i] = 'O'; + break; + case 5: + text[i] = ' '; + break; + case 6: + text[i] = 'W'; + break; + case 8: + text[i] = 'R'; + break; + case 10: + text[i] = 'D'; + break; + default: + text[i] = 0; + } + } + + printf("<%s>\n", text); + + return 0; +} diff --git a/oscar64/autotest/testint16.c b/oscar64/autotest/testint16.c new file mode 100644 index 0000000..66e5ace --- /dev/null +++ b/oscar64/autotest/testint16.c @@ -0,0 +1,121 @@ +#include + +void testmuli(int a, int b, int ab) +{ + assert (a * b == ab); +} + +void testdivi(int a, int b, int ab) +{ + assert (a / b == ab); +} + +void shltesti(int a, int b, int ab) +{ + assert (a << b == ab); +} + +void shrtesti(int a, int b, int ab) +{ + assert (a >> b == ab); +} + +int sieve(int size) +{ + bool sieve[1000]; + + for(int i=0; i +#include + +bool beq(int a, int b) +{ + return a == b; +} + +bool blt(int a, int b) +{ + return a < b; +} + +bool bgt(int a, int b) +{ + return a > b; +} + +bool ble(int a, int b) +{ + return a <= b; +} + +bool bge(int a, int b) +{ + return a >= b; +} + +bool neq(int a, int b) +{ + return a == b; +} + +#pragma native(neq) + +bool nlt(int a, int b) +{ + return a < b; +} + +#pragma native(nlt) + +bool ngt(int a, int b) +{ + return a > b; +} + +#pragma native(ngt) + +bool nle(int a, int b) +{ + return a <= b; +} + +#pragma native(nle) + +bool nge(int a, int b) +{ + return a >= b; +} + +#pragma native(nge) + + + +bool beqz(int a) +{ + return a == 0; +} + +bool bltz(int a) +{ + return a < 0; +} + +bool bgtz(int a) +{ + return a > 0; +} + +bool blez(int a) +{ + return a <= 0; +} + +bool bgez(int a) +{ + return a >= 0; +} + +bool neqz(int a) +{ + return a == 0; +} + +#pragma native(neqz) + +bool nltz(int a) +{ + return a < 0; +} + +#pragma native(nltz) + +bool ngtz(int a) +{ + return a > 0; +} + +#pragma native(ngtz) + +bool nlez(int a) +{ + return a <= 0; +} + +#pragma native(nlez) + +bool ngez(int a) +{ + return a >= 0; +} + +#pragma native(ngez) + + + + +bool bequz(unsigned a) +{ + return a == 0; +} + +bool bltuz(unsigned a) +{ + return a < 0; +} + +bool bgtuz(unsigned a) +{ + return a > 0; +} + +bool bleuz(unsigned a) +{ + return a <= 0; +} + +bool bgeuz(unsigned a) +{ + return a >= 0; +} + +bool nequz(unsigned a) +{ + return a == 0; +} + +#pragma native(nequz) + +bool nltuz(unsigned a) +{ + return a < 0; +} + +#pragma native(nltuz) + +bool ngtuz(unsigned a) +{ + return a > 0; +} + +#pragma native(ngtuz) + +bool nleuz(unsigned a) +{ + return a <= 0; +} + +#pragma native(nleuz) + +bool ngeuz(unsigned a) +{ + return a >= 0; +} + +#pragma native(ngeuz) + + + + +bool beq1(int a) +{ + return a == 1; +} + +bool blt1(int a) +{ + return a < 1; +} + +bool bgt1(int a) +{ + return a > 1; +} + +bool ble1(int a) +{ + return a <= 1; +} + +bool bge1(int a) +{ + return a >= 1; +} + +bool neq1(int a) +{ + return a == 1; +} + +#pragma native(neq1) + +bool nlt1(int a) +{ + return a < 1; +} + +#pragma native(nlt1) + +bool ngt1(int a) +{ + return a > 1; +} + +#pragma native(ngt1) + +bool nle1(int a) +{ + return a <= 1; +} + +#pragma native(nle1) + +bool nge1(int a) +{ + return a >= 1; +} + +#pragma native(nge1) + + + +void cmp(int a, int b) +{ + bool beqf = beq(a, b), bltf = blt(a, b), bgtf = bgt(a, b), blef = ble(a, b), bgef = bge(a, b); + bool neqf = neq(a, b), nltf = nlt(a, b), ngtf = ngt(a, b), nlef = nle(a, b), ngef = nge(a, b); + + printf("BYTE %d, %d : EQ %d LT %d GT %d\r", a, b, beqf, bltf, bgtf); + printf("NATIVE %d, %d : EQ %d LT %d GT %d\r", a, b, neqf, nltf, ngtf); + + assert(beqf == neqf); + assert(bltf == nltf); + assert(bgtf == ngtf); + assert(blef == nlef); + assert(bgef == ngef); +} + +void cmpz(int a) +{ + bool beqf = beqz(a), bltf = bltz(a), bgtf = bgtz(a), blef = blez(a), bgef = bgez(a); + bool neqf = neqz(a), nltf = nltz(a), ngtf = ngtz(a), nlef = nlez(a), ngef = ngez(a); + + printf("BYTE %d, 0 : EQ %d LT %d GT %d\r", a, beqf, bltf, bgtf); + printf("NATIVE %d, 0 : EQ %d LT %d GT %d\r", a, neqf, nltf, ngtf); + + assert(beqf == neqf); + assert(bltf == nltf); + assert(bgtf == ngtf); + assert(blef == nlef); + assert(bgef == ngef); +} + +void cmpuz(unsigned a) +{ + bool beqf = bequz(a), bltf = bltuz(a), bgtf = bgtuz(a), blef = bleuz(a), bgef = bgeuz(a); + bool neqf = nequz(a), nltf = nltuz(a), ngtf = ngtuz(a), nlef = nleuz(a), ngef = ngeuz(a); + + printf("BYTE %u, 0 : EQ %u LT %d GT %u\r", a, beqf, bltf, bgtf); + printf("NATIVE %u, 0 : EQ %u LT %d GT %u\r", a, neqf, nltf, ngtf); + + assert(beqf == neqf); + assert(bltf == nltf); + assert(bgtf == ngtf); + assert(blef == nlef); + assert(bgef == ngef); +} + +void cmp1(int a) +{ + bool beqf = beq1(a), bltf = blt1(a), bgtf = bgt1(a), blef = ble1(a), bgef = bge1(a); + bool neqf = neq1(a), nltf = nlt1(a), ngtf = ngt1(a), nlef = nle1(a), ngef = nge1(a); + + printf("BYTE %d, 1 : EQ %d LT %d GT %d LE %d GE %d\r", a, beqf, bltf, bgtf, blef, bgef); + printf("NATIVE %d, 1 : EQ %d LT %d GT %d LE %d GE %d\r", a, neqf, nltf, ngtf, nlef, ngef); + + assert(beqf == neqf); + assert(bltf == nltf); + assert(bgtf == ngtf); + assert(blef == nlef); + assert(bgef == ngef); +} + +int main(void) +{ + cmp( 0, 1); + cmp( 0, -1); + cmp( 1, 0); + cmp(-1, 0); + + cmp(1, 1); + cmp(1, 2); + cmp(2, 1); + + cmp(-1, -1); + cmp(-1, -2); + cmp(-2, -1); + + cmp( 1, -1); + cmp( 1, -2); + cmp( 2, -1); + + cmp(-1, 1); + cmp(-1, 2); + cmp(-2, 1); + + + cmp( 0, 10000); + cmp( 0, -10000); + cmp( 10000, 0); + cmp(-10000, 0); + + cmp(10000, 10000); + cmp(10000, 20000); + cmp(20000, 10000); + + cmp(-10000, -10000); + cmp(-10000, -20000); + cmp(-20000, -10000); + + cmp( 10000, -10000); + cmp( 10000, -20000); + cmp( 20000, -10000); + + cmp(-10000, 10000); + cmp(-10000, 20000); + cmp(-20000, 10000); + + cmp(-30000, 30000); + cmp(-30000, -30000); + cmp( 30000, 30000); + cmp( 30000, -30000); + + cmp( 0, 1024); + cmp( 0, -1024); + cmp( 1024, 0); + cmp(-1024, 0); + + cmp(1024, 1024); + cmp(1024, 1025); + cmp(1025, 1024); + + cmp(-1024, -1024); + cmp(-1024, -1025); + cmp(-1025, -1024); + + cmp( 1024, -1024); + cmp( 1024, -1025); + cmp( 1025, -1024); + + cmp(-1024, 1024); + cmp(-1024, 1025); + cmp(-1025, 1024); + + cmpz(0); + cmpz(1); + cmpz(255); + cmpz(256); + cmpz(10000); + cmpz(20000); + cmpz(-1); + cmpz(-255); + cmpz(-256); + cmpz(-10000); + cmpz(-20000); + + cmpuz(0); + cmpuz(1); + cmpuz(255); + cmpuz(256); + cmpuz(10000); + cmpuz(20000); + cmpuz(40000); + cmpuz(32767); + cmpuz(32768); + cmpuz(65535); + + cmp1(0); + cmp1(1); + cmp1(2); + cmp1(3); + cmp1(255); + cmp1(256); + cmp1(10000); + cmp1(20000); + cmp1(-1); + cmp1(-2); + cmp1(-3); + cmp1(-255); + cmp1(-256); + cmp1(-10000); + cmp1(-20000); + + return 0; + +} diff --git a/oscar64/autotest/testint16mul.c b/oscar64/autotest/testint16mul.c new file mode 100644 index 0000000..bc799e4 --- /dev/null +++ b/oscar64/autotest/testint16mul.c @@ -0,0 +1,23 @@ +#include + +int mul[32][32]; + +int main(void) +{ + for(int i=0; i<32; i++) + for(int j=0; j<32; j++) + mul[i][j] = i * j; +#assign xi 0 +#repeat + + for(int j=0; j<32; j++) + { + assert(mul[xi][j] == xi * j); + assert(mul[j][xi] == j * xi); + } + +#assign xi xi + 1 +#until xi == 32 + + return 0; +} diff --git a/oscar64/autotest/testint32.c b/oscar64/autotest/testint32.c new file mode 100644 index 0000000..117bf97 --- /dev/null +++ b/oscar64/autotest/testint32.c @@ -0,0 +1,220 @@ +#include + +void testmuli(long a, long b, long ab) +{ + assert (a * b == ab); +} + +void testmulu(unsigned long a, unsigned long b, unsigned long ab) +{ + assert (a * b == ab); +} + +void testmulub(unsigned long a, unsigned char b, unsigned long ab) +{ + assert (a * b == ab); + assert (b * a == ab); +} + +void testmulbu(unsigned char a, unsigned long b, unsigned long ab) +{ + assert (a * b == ab); + assert (b * a == ab); +} + +void testdivi(long a, long b, long ab) +{ + assert (a / b == ab); +} + +void shltesti(long a, long b, long ab) +{ + assert (a << b == ab); +} + +void shrtesti(long a, long b, long ab) +{ + assert (a >> b == ab); +} + +void testmuldiv(unsigned long a, unsigned long b) +{ + unsigned long c = a * b; + if (a != 0) + { + assert(c / a == b); + assert(c % a == 0); + } + if (b != 0) + { + assert(c / b == a); + assert(c % b == 0); + } +} + +long sieve(long size) +{ + bool sieve[1000]; + + for(long i=0; i> i, q >> (31 - i)); + testmuldiv(q >> i, p >> (31 - i)); + } + + return 0; +} diff --git a/oscar64/autotest/testint32cmp.c b/oscar64/autotest/testint32cmp.c new file mode 100644 index 0000000..3a51a34 --- /dev/null +++ b/oscar64/autotest/testint32cmp.c @@ -0,0 +1,499 @@ +#include +#include + +bool beq(long a, long b) +{ + return a == b; +} + +bool blt(long a, long b) +{ + return a < b; +} + +bool bgt(long a, long b) +{ + return a > b; +} + +bool ble(long a, long b) +{ + return a <= b; +} + +bool bge(long a, long b) +{ + return a >= b; +} + +bool neq(long a, long b) +{ + return a == b; +} + +#pragma native(neq) + +bool nlt(long a, long b) +{ + return a < b; +} + +#pragma native(nlt) + +bool ngt(long a, long b) +{ + return a > b; +} + +#pragma native(ngt) + +bool nle(long a, long b) +{ + return a <= b; +} + +#pragma native(nle) + +bool nge(long a, long b) +{ + return a >= b; +} + +#pragma native(nge) + + + + +inline bool ieq(long a, long b) +{ + return a == b; +} + +inline bool ilt(long a, long b) +{ + return a < b; +} + +inline bool igt(long a, long b) +{ + return a > b; +} + +inline bool ile(long a, long b) +{ + return a <= b; +} + +inline bool ige(long a, long b) +{ + return a >= b; +} + + +bool beqz(long a) +{ + return a == 0; +} + +bool bltz(long a) +{ + return a < 0; +} + +bool bgtz(long a) +{ + return a > 0; +} + +bool blez(long a) +{ + return a <= 0; +} + +bool bgez(long a) +{ + return a >= 0; +} + +bool neqz(long a) +{ + return a == 0; +} + +#pragma native(neqz) + +bool nltz(long a) +{ + return a < 0; +} + +#pragma native(nltz) + +bool ngtz(long a) +{ + return a > 0; +} + +#pragma native(ngtz) + +bool nlez(long a) +{ + return a <= 0; +} + +#pragma native(nlez) + +bool ngez(long a) +{ + return a >= 0; +} + +#pragma native(ngez) + + + + +bool beq1(long a) +{ + return a == 1; +} + +bool blt1(long a) +{ + return a < 1; +} + +bool bgt1(long a) +{ + return a > 1; +} + +bool ble1(long a) +{ + return a <= 1; +} + +bool bge1(long a) +{ + return a >= 1; +} + +bool neq1(long a) +{ + return a == 1; +} + +#pragma native(neq1) + +bool nlt1(long a) +{ + return a < 1; +} + +#pragma native(nlt1) + +bool ngt1(long a) +{ + return a > 1; +} + +#pragma native(ngt1) + +bool nle1(long a) +{ + return a <= 1; +} + +#pragma native(nle1) + +bool nge1(long a) +{ + return a >= 1; +} + +#pragma native(nge1) + + + +bool beqm(long a) +{ + return a == -1; +} + +bool bltm(long a) +{ + return a < -1; +} + +bool bgtm(long a) +{ + return a > -1; +} + +bool blem(long a) +{ + return a <= -1; +} + +bool bgem(long a) +{ + return a >= -1; +} + + + +bool neqm(long a) +{ + return a == -1; +} + +#pragma native(neqm) + +bool nltm(long a) +{ + return a < -1; +} + +#pragma native(nltm) + +bool ngtm(long a) +{ + return a > -1; +} + +#pragma native(ngtm) + +bool nlem(long a) +{ + return a <= -1; +} + +#pragma native(nlem) + +bool ngem(long a) +{ + return a >= -1; +} + +#pragma native(ngem) + + + +void cmpc(long a, long b) +{ + bool beqf = beq(a, b), bltf = blt(a, b), bgtf = bgt(a, b), blef = ble(a, b), bgef = bge(a, b); + bool neqf = neq(a, b), nltf = nlt(a, b), ngtf = ngt(a, b), nlef = nle(a, b), ngef = nge(a, b); + + printf("BYTE %ld, %ld : EQ %d LT %d GT %d\r", a, b, beqf, bltf, bgtf); + printf("NATIVE %ld, %ld : EQ %d LT %d GT %d\r", a, b, neqf, nltf, ngtf); + + assert(beqf == neqf); + assert(bltf == nltf); + assert(bgtf == ngtf); + assert(blef == nlef); + assert(bgef == ngef); +} + +void cmpi(long a, long b) +{ + bool ieqf = ieq(a, b), iltf = ilt(a, b), igtf = igt(a, b), ilef = ile(a, b), igef = ige(a, b); + bool neqf = neq(a, b), nltf = nlt(a, b), ngtf = ngt(a, b), nlef = nle(a, b), ngef = nge(a, b); + + printf("INLINE %ld, %ld : EQ %d LT %d GT %d\r", a, b, ieqf, iltf, igtf); + printf("NATIVE %ld, %ld : EQ %d LT %d GT %d\r", a, b, neqf, nltf, ngtf); + + assert(ieqf == neqf); + assert(iltf == nltf); + assert(igtf == ngtf); + assert(ilef == nlef); + assert(igef == ngef); +} + +void cmp(long a, long b) +{ + cmpc(a, b); + cmpi(a, b); +} + +void cmpz(long a) +{ + bool beqf = beqz(a), bltf = bltz(a), bgtf = bgtz(a), blef = blez(a), bgef = bgez(a); + bool neqf = neqz(a), nltf = nltz(a), ngtf = ngtz(a), nlef = nlez(a), ngef = ngez(a); + + printf("BYTE %ld, 0 : EQ %d LT %d GT %d\r", a, beqf, bltf, bgtf); + printf("NATIVE %ld, 0 : EQ %d LT %d GT %d\r", a, neqf, nltf, ngtf); + + assert(beqf == neqf); + assert(bltf == nltf); + assert(bgtf == ngtf); + assert(blef == nlef); + assert(bgef == ngef); +} + +void cmp1(long a) +{ + bool beqf = beq1(a), bltf = blt1(a), bgtf = bgt1(a), blef = ble1(a), bgef = bge1(a); + bool neqf = neq1(a), nltf = nlt1(a), ngtf = ngt1(a), nlef = nle1(a), ngef = nge1(a); + + printf("BYTE %ld, 1 : EQ %d LT %d GT %d LE %d GE %d\r", a, beqf, bltf, bgtf, blef, bgef); + printf("NATIVE %ld, 1 : EQ %d LT %d GT %d LE %d GE %d\r", a, neqf, nltf, ngtf, nlef, ngef); + + assert(beqf == neqf); + assert(bltf == nltf); + assert(bgtf == ngtf); + assert(blef == nlef); + assert(bgef == ngef); +} + +void cmpm(long a) +{ + bool beqf = beqm(a), bltf = bltm(a), bgtf = bgtm(a), blef = blem(a), bgef = bgem(a); + bool neqf = neqm(a), nltf = nltm(a), ngtf = ngtm(a), nlef = nlem(a), ngef = ngem(a); + + printf("BYTE %ld, 1 : EQ %d LT %d GT %d LE %d GE %d\r", a, beqf, bltf, bgtf, blef, bgef); + printf("NATIVE %ld, 1 : EQ %d LT %d GT %d LE %d GE %d\r", a, neqf, nltf, ngtf, nlef, ngef); + + assert(beqf == neqf); + assert(bltf == nltf); + assert(bgtf == ngtf); + assert(blef == nlef); + assert(bgef == ngef); +} + +int main(void) +{ + cmp( 0, 1); + cmp( 0, -1); + cmp( 1, 0); + cmp(-1, 0); + + cmp(1, 1); + cmp(1, 2); + cmp(2, 1); + + cmp(-1, -1); + cmp(-1, -2); + cmp(-2, -1); + + cmp( 1, -1); + cmp( 1, -2); + cmp( 2, -1); + + cmp(-1, 1); + cmp(-1, 2); + cmp(-2, 1); + + + cmp( 0, 10000); + cmp( 0, -10000); + cmp( 10000, 0); + cmp(-10000, 0); + + cmp(10000, 10000); + cmp(10000, 20000); + cmp(20000, 10000); + + cmp(-10000, -10000); + cmp(-10000, -20000); + cmp(-20000, -10000); + + cmp( 10000, -10000); + cmp( 10000, -20000); + cmp( 20000, -10000); + + cmp(-10000, 10000); + cmp(-10000, 20000); + cmp(-20000, 10000); + + cmp( 10000000L, -10000000L); + cmp( 10000000L, -20000000L); + cmp( 20000000L, -10000000L); + + cmp(-10000000L, 10000000L); + cmp(-10000000L, 20000000L); + cmp(-20000000L, 10000000L); + + cmp( 0, 1024); + cmp( 0, -1024); + cmp( 1024, 0); + cmp(-1024, 0); + + cmp(1024, 1024); + cmp(1024, 1025); + cmp(1025, 1024); + + cmp(-1024, -1024); + cmp(-1024, -1025); + cmp(-1025, -1024); + + cmp( 1024, -1024); + cmp( 1024, -1025); + cmp( 1025, -1024); + + cmp(-1024, 1024); + cmp(-1024, 1025); + cmp(-1025, 1024); + + cmpz(0); + cmpz(1); + cmpz(255); + cmpz(256); + cmpz(10000); + cmpz(20000); + cmpz(-1); + cmpz(-255); + cmpz(-256); + cmpz(-10000); + cmpz(-20000); + + cmp1(0); + cmp1(1); + cmp1(2); + cmp1(3); + cmp1(255); + cmp1(256); + cmp1(10000); + cmp1(20000); + cmp1(1000000l); + cmp1(2000000l); + cmp1(100000000l); + cmp1(200000000l); + cmp1(-1); + cmp1(-2); + cmp1(-3); + cmp1(-255); + cmp1(-256); + cmp1(-10000); + cmp1(-20000); + cmp1(-1000000l); + cmp1(-2000000l); + cmp1(-100000000l); + cmp1(-200000000l); + + cmpm(0); + cmpm(1); + cmpm(2); + cmpm(3); + cmpm(255); + cmpm(256); + cmpm(10000); + cmpm(20000); + cmpm(1000000l); + cmpm(2000000l); + cmpm(100000000l); + cmpm(200000000l); + cmpm(-1); + cmpm(-2); + cmpm(-3); + cmpm(-255); + cmpm(-256); + cmpm(-10000); + cmpm(-20000); + cmpm(-1000000l); + cmpm(-2000000l); + cmpm(-100000000l); + cmpm(-200000000l); + + return 0; + +} diff --git a/oscar64/autotest/testint8cmp.c b/oscar64/autotest/testint8cmp.c new file mode 100644 index 0000000..b5a6744 --- /dev/null +++ b/oscar64/autotest/testint8cmp.c @@ -0,0 +1,555 @@ +#include +#include + +typedef signed char int8; +typedef unsigned char uint8; + +bool beq(int8 a, int8 b) +{ + return a == b; +} + +bool blt(int8 a, int8 b) +{ + return a < b; +} + +bool bgt(int8 a, int8 b) +{ + return a > b; +} + +bool ble(int8 a, int8 b) +{ + return a <= b; +} + +bool bge(int8 a, int8 b) +{ + return a >= b; +} + +bool neq(int8 a, int8 b) +{ + return a == b; +} + +#pragma native(neq) + +bool nlt(int8 a, int8 b) +{ + return a < b; +} + +#pragma native(nlt) + +bool ngt(int8 a, int8 b) +{ + return a > b; +} + +#pragma native(ngt) + +bool nle(int8 a, int8 b) +{ + return a <= b; +} + +#pragma native(nle) + +bool nge(int8 a, int8 b) +{ + return a >= b; +} + +#pragma native(nge) + + + +bool beqz(int8 a) +{ + return a == 0; +} + +bool bltz(int8 a) +{ + return a < 0; +} + +bool bgtz(int8 a) +{ + return a > 0; +} + +bool blez(int8 a) +{ + return a <= 0; +} + +bool bgez(int8 a) +{ + return a >= 0; +} + +bool neqz(int8 a) +{ + return a == 0; +} + +#pragma native(neqz) + +bool nltz(int8 a) +{ + return a < 0; +} + +#pragma native(nltz) + +bool ngtz(int8 a) +{ + return a > 0; +} + +#pragma native(ngtz) + +bool nlez(int8 a) +{ + return a <= 0; +} + +#pragma native(nlez) + +bool ngez(int8 a) +{ + return a >= 0; +} + +#pragma native(ngez) + + + + + + + +bool bequz(uint8 a) +{ + return a == 0; +} + +bool bltuz(uint8 a) +{ + return a < 0; +} + +bool bgtuz(uint8 a) +{ + return a > 0; +} + +bool bleuz(uint8 a) +{ + return a <= 0; +} + +bool bgeuz(uint8 a) +{ + return a >= 0; +} + +bool nequz(uint8 a) +{ + return a == 0; +} + +#pragma native(nequz) + +bool nltuz(uint8 a) +{ + return a < 0; +} + +#pragma native(nltuz) + +bool ngtuz(uint8 a) +{ + return a > 0; +} + +#pragma native(ngtuz) + +bool nleuz(uint8 a) +{ + return a <= 0; +} + +#pragma native(nleuz) + +bool ngeuz(uint8 a) +{ + return a >= 0; +} + +#pragma native(ngeuz) + + + + + +bool beq1(int8 a) +{ + return a == 1; +} + +bool blt1(int8 a) +{ + return a < 1; +} + +bool bgt1(int8 a) +{ + return a > 1; +} + +bool ble1(int8 a) +{ + return a <= 1; +} + +bool bge1(int8 a) +{ + return a >= 1; +} + +bool neq1(int8 a) +{ + return a == 1; +} + +#pragma native(neq1) + +bool nlt1(int8 a) +{ + return a < 1; +} + +#pragma native(nlt1) + +bool ngt1(int8 a) +{ + return a > 1; +} + +#pragma native(ngt1) + +bool nle1(int8 a) +{ + return a <= 1; +} + +#pragma native(nle1) + +bool nge1(int8 a) +{ + return a >= 1; +} + +#pragma native(nge1) + + + +void cmp(int8 a, int8 b) +{ + bool beqf = beq(a, b), bltf = blt(a, b), bgtf = bgt(a, b), blef = ble(a, b), bgef = bge(a, b); + bool neqf = neq(a, b), nltf = nlt(a, b), ngtf = ngt(a, b), nlef = nle(a, b), ngef = nge(a, b); + + printf("BYTE %d, %d : EQ %d LT %d GT %d\r", a, b, beqf, bltf, bgtf); + printf("NATIVE %d, %d : EQ %d LT %d GT %d\r", a, b, neqf, nltf, ngtf); + + assert(beqf == neqf); + assert(bltf == nltf); + assert(bgtf == ngtf); + assert(blef == nlef); + assert(bgef == ngef); +} + +void cmpz(int8 a) +{ + bool beqf = beqz(a), bltf = bltz(a), bgtf = bgtz(a), blef = blez(a), bgef = bgez(a); + bool neqf = neqz(a), nltf = nltz(a), ngtf = ngtz(a), nlef = nlez(a), ngef = ngez(a); + + printf("BYTE %d, 0 : EQ %d LT %d GT %d\r", a, beqf, bltf, bgtf); + printf("NATIVE %d, 0 : EQ %d LT %d GT %d\r", a, neqf, nltf, ngtf); + + assert(beqf == neqf); + assert(bltf == nltf); + assert(bgtf == ngtf); + assert(blef == nlef); + assert(bgef == ngef); +} + +void cmpuz(uint8 a) +{ + bool beqf = bequz(a), bltf = bltuz(a), bgtf = bgtuz(a), blef = bleuz(a), bgef = bgeuz(a); + bool neqf = nequz(a), nltf = nltuz(a), ngtf = ngtuz(a), nlef = nleuz(a), ngef = ngeuz(a); + + printf("BYTE %d, 0 : EQ %d LT %d GT %d\r", a, beqf, bltf, bgtf); + printf("NATIVE %d, 0 : EQ %d LT %d GT %d\r", a, neqf, nltf, ngtf); + + assert(beqf == neqf); + assert(bltf == nltf); + assert(bgtf == ngtf); + assert(blef == nlef); + assert(bgef == ngef); +} + +void cmp1(int8 a) +{ + bool beqf = beq1(a), bltf = blt1(a), bgtf = bgt1(a), blef = ble1(a), bgef = bge1(a); + bool neqf = neq1(a), nltf = nlt1(a), ngtf = ngt1(a), nlef = nle1(a), ngef = nge1(a); + + printf("BYTE %d, 1 : EQ %d LT %d GT %d LE %d GE %d\r", a, beqf, bltf, bgtf, blef, bgef); + printf("NATIVE %d, 1 : EQ %d LT %d GT %d LE %d GE %d\r", a, neqf, nltf, ngtf, nlef, ngef); + + assert(beqf == neqf); + assert(bltf == nltf); + assert(bgtf == ngtf); + assert(blef == nlef); + assert(bgef == ngef); +} + +void cmpu(char a, char b, bool lt, bool gt, bool eq) +{ + bool clt = a < b, cgt = a > b, ceq = a == b; + bool nlt = a >= b, ngt = a <= b, neq = a != b; + + printf("CPMPU %d, %d LT %d%d%d GT %d%d%d EQ %d%d%d\n", a, b, lt, clt, nlt, gt, cgt, ngt, eq, ceq, neq); + + assert(clt == lt); + assert(cgt == gt); + assert(ceq == eq); + + assert(nlt != lt); + assert(ngt != gt); + assert(neq != eq); +} + +void cmpu100(char a, bool lt, bool gt, bool eq) +{ + bool clt = a < 100, cgt = a > 100, ceq = a == 100; + bool nlt = a >= 100, ngt = a <= 100, neq = a != 100; + + printf("CPMPU %d, %d LT %d%d%d GT %d%d%d EQ %d%d%d\n", a, 100, lt, clt, nlt, gt, cgt, ngt, eq, ceq, neq); + + assert(clt == lt); + assert(cgt == gt); + assert(ceq == eq); + + assert(nlt != lt); + assert(ngt != gt); + assert(neq != eq); +} + +void cmpu100r(char b, bool lt, bool gt, bool eq) +{ + bool clt = 100 < b, cgt = 100 > b, ceq = 100 == b; + bool nlt = 100 >= b, ngt = 100 <= b, neq = 100 != b; + + printf("CPMPU %d, %d LT %d%d%d GT %d%d%d EQ %d%d%d\n", 100, b, lt, clt, nlt, gt, cgt, ngt, eq, ceq, neq); + + assert(clt == lt); + assert(cgt == gt); + assert(ceq == eq); + + assert(nlt != lt); + assert(ngt != gt); + assert(neq != eq); +} + +void cmpu1000(char a, bool lt, bool gt, bool eq) +{ + bool clt = a < 1000, cgt = a > 1000, ceq = a == 1000; + bool nlt = a >= 1000, ngt = a <= 1000, neq = a != 1000; + + printf("CPMPU %d, %d LT %d%d%d GT %d%d%d EQ %d%d%d\n", a, 1000, lt, clt, nlt, gt, cgt, ngt, eq, ceq, neq); + + assert(clt == lt); + assert(cgt == gt); + assert(ceq == eq); + + assert(nlt != lt); + assert(ngt != gt); + assert(neq != eq); +} + +void cmpu1000r(char b, bool lt, bool gt, bool eq) +{ + bool clt = 1000< b, cgt = 1000 > b, ceq = 1000 == b; + bool nlt = 1000 >= b, ngt = 1000 <= b, neq = 1000 != b; + + printf("CPMPU %d, %d LT %d%d%d GT %d%d%d EQ %d%d%d\n", 1000, b, lt, clt, nlt, gt, cgt, ngt, eq, ceq, neq); + + assert(clt == lt); + assert(cgt == gt); + assert(ceq == eq); + + assert(nlt != lt); + assert(ngt != gt); + assert(neq != eq); +} + +void cmpum100(char a, bool lt, bool gt, bool eq) +{ + bool clt = a < -100, cgt = a > -100, ceq = a == -100; + bool nlt = a >= -100, ngt = a <= -100, neq = a != -100; + + printf("CPMPU %d, %d LT %d%d%d GT %d%d%d EQ %d%d%d\n", a, -100, lt, clt, nlt, gt, cgt, ngt, eq, ceq, neq); + + assert(clt == lt); + assert(cgt == gt); + assert(ceq == eq); + + assert(nlt != lt); + assert(ngt != gt); + assert(neq != eq); +} + +void cmpum100r(char b, bool lt, bool gt, bool eq) +{ + bool clt = -100 < b, cgt = -100 > b, ceq = -100 == b; + bool nlt = -100 >= b, ngt = -100 <= b, neq = -100 != b; + + printf("CPMPU %d, %d LT %d%d%d GT %d%d%d EQ %d%d%d\n", -100, b, lt, clt, nlt, gt, cgt, ngt, eq, ceq, neq); + + assert(clt == lt); + assert(cgt == gt); + assert(ceq == eq); + + assert(nlt != lt); + assert(ngt != gt); + assert(neq != eq); +} + +int main(void) +{ + cmp( 0, 1); + cmp( 0, -1); + cmp( 1, 0); + cmp(-1, 0); + + cmp(1, 1); + cmp(1, 2); + cmp(2, 1); + + cmp(-1, -1); + cmp(-1, -2); + cmp(-2, -1); + + cmp( 1, -1); + cmp( 1, -2); + cmp( 2, -1); + + cmp(-1, 1); + cmp(-1, 2); + cmp(-2, 1); + + + cmp( 0, 100); + cmp( 0, -100); + cmp( 100, 0); + cmp(-100, 0); + + cmp(10, 10); + cmp(10, 20); + cmp(20, 10); + + cmp(-10, -10); + cmp(-10, -20); + cmp(-20, -10); + + cmp( 10, -10); + cmp( 10, -20); + cmp( 20, -10); + + cmp(-10, 10); + cmp(-10, 20); + cmp(-20, 10); + + cmp(-30, 30); + cmp(-30, -30); + cmp( 30, 30); + cmp( 30, -30); + + cmp( 0, 127); + cmp( 0, -128); + cmp( 127, 0); + cmp(-128, 0); + + cmp( 127, 127); + cmp( 127, -128); + cmp(-128, 127); + cmp(-128, -128); + + cmpz(0); + cmpz(1); + cmpz(127); + cmpz(-1); + cmpz(-128); + + cmpuz(0); + cmpuz(1); + cmpuz(127); + cmpuz(128); + cmpuz(255); + + cmp1(0); + cmp1(1); + cmp1(2); + cmp1(3); + cmp1(127); + cmp1(-1); + cmp1(-2); + cmp1(-3); + cmp1(-128); + + cmpu(0, 0, false, false, true); + cmpu(0, 1, true, false, false); + cmpu(1, 0, false ,true, false); + + cmpu(128, 128, false, false, true); + cmpu( 0, 128, true, false, false); + cmpu(128, 0, false ,true, false); + + cmpu(255, 255, false, false, true); + cmpu( 0, 255, true, false, false); + cmpu(255, 0, false ,true, false); + + cmpu(127, 127, false, false, true); + cmpu(128, 255, true, false, false); + cmpu(255, 128, false ,true, false); + + cmpu100(100, false, false, true); + cmpu100( 0, true, false, false); + cmpu100(130, false, true, false); + + cmpu100r(100, false, false, true); + cmpu100r(130, true, false, false); + cmpu100r( 0, false, true, false); + + cmpum100( 0, false, true, false); + cmpum100(130, false, true, false); + + cmpum100r(130, true, false, false); + cmpum100r( 0, true, false, false); + + cmpu1000(100, true, false, false); + cmpu1000( 0, true, false, false); + cmpu1000(130, true, false, false); + + cmpu1000r(100, false, true, false); + cmpu1000r(130, false, true, false); + cmpu1000r( 0, false, true, false); + + + return 0; +} diff --git a/oscar64/autotest/testinterval.c b/oscar64/autotest/testinterval.c new file mode 100644 index 0000000..ca42f76 --- /dev/null +++ b/oscar64/autotest/testinterval.c @@ -0,0 +1,58 @@ +#include + +void testint0(void) +{ + int a0n = 0, an0 = 0; + int b0n = 0, bn0 = 0; + + for(int i=-1000; i<1000; i++) + { + if (i >= 0 && i < 500) + a0n += 1; + if (i < 500 && i >= 0) + an0 += 1; + if (i >= 0 && i <= 499) + b0n += 1; + if (i <= 499 && i >= 0) + bn0 += 1; + } + + assert(a0n == 500); + assert(an0 == 500); + assert(b0n == 500); + assert(bn0 == 500); +} + +typedef signed char int8; + +void testbyte0(void) +{ + int8 a0n = 0, an0 = 0; + int8 b0n = 0, bn0 = 0; + + for(int8 i=-100; i<100; i++) + { + if (i >= 0 && i < 50) + a0n += 1; + if (i < 50 && i >= 0) + an0 += 1; + if (i >= 0 && i <= 49) + b0n += 1; + if (i <= 49 && i >= 0) + bn0 += 1; + } + + assert(a0n == 50); + assert(an0 == 50); + assert(b0n == 50); + assert(bn0 == 50); +} + +int main(void) +{ + testint0(); + testbyte0(); + + return 0; +} + diff --git a/oscar64/autotest/testsigned16div.c b/oscar64/autotest/testsigned16div.c new file mode 100644 index 0000000..4372465 --- /dev/null +++ b/oscar64/autotest/testsigned16div.c @@ -0,0 +1,29 @@ +#include + +int multab[32]; + +void fill_mulli(int m) +{ +#pragma unroll(full) + for(int i=-16; i<16; i++) + if (i != 0) + multab[i + 16] = m / i; +} + +void check_mulli(int m) +{ + for(int i=-16; i<16; i++) + if (i != 0) + assert(multab[i + 16] == m / i); +} + +int main(void) +{ + for(int i=-1024; i<=1024; i++) + { + fill_mulli(i); + check_mulli(i); + } + + return 0; +} diff --git a/oscar64/autotest/testsigned16mul.c b/oscar64/autotest/testsigned16mul.c new file mode 100644 index 0000000..2880461 --- /dev/null +++ b/oscar64/autotest/testsigned16mul.c @@ -0,0 +1,27 @@ +#include + +int multab[32]; + +void fill_mulli(int m) +{ +#pragma unroll(full) + for(int i=-16; i<16; i++) + multab[i + 16] = m * i; +} + +void check_mulli(int m) +{ + for(int i=-16; i<16; i++) + assert(multab[i + 16] == m * i); +} + +int main(void) +{ + for(int i=-1024; i<=1024; i++) + { + fill_mulli(i); + check_mulli(i); + } + + return 0; +} diff --git a/oscar64/autotest/tileexpand.cpp b/oscar64/autotest/tileexpand.cpp new file mode 100644 index 0000000..915e133 --- /dev/null +++ b/oscar64/autotest/tileexpand.cpp @@ -0,0 +1,83 @@ +#include +#include +#include + +#define MAP_WIDTH 10 +#define MAP_HEIGHT 2 + +#define TITLE_TILE_WIDTH 4 +#define TITLE_TILE_HEIGHT 4 + +#define PTR_SCREEN ((char *)0xc000) +#define PTR_BUFFER ((char *)0xc400) +#define PTR_COLOR ((char *)0xd800) +#define PTR_FONTCHARSET ((char *)0xd800) + +const char TitleMap[1024] = { +#for(i, 1024) i * 17, +}; + +const char TitleTiles[4096] = { +#for(i, 4096) i * 31, +}; + +// Custom screen address +extern char* const Screen = PTR_SCREEN; + +// Color mem address +extern char* const Color = PTR_COLOR; + +void RenderLogo(char screenY) +{ + char * sp = Screen; + char * cp = Color; + const char * mp = TitleMap; + + for(char ty=0; ty < MAP_HEIGHT; ty++) + { + for(char tx=0; tx< MAP_WIDTH; tx++) + { + char ti = mp[tx]; + const char* tp = TitleTiles + (TITLE_TILE_WIDTH * TITLE_TILE_HEIGHT) * ti; + + for(char y=0; y +#include +#include +#include + + +void simple_sprintf(char *out, const char *fmt, ...) +{ + va_list args; + char tmp[32]; + int outlen = 0; + + for (va_start(args, fmt); *fmt != '\0'; ++fmt) + { + switch (*fmt) + { + case 'd': { + int i = va_arg(args, int); + sprintf(tmp, "%d", i); + strcpy(out + outlen, tmp); + outlen += strlen(tmp); + break; + } + case 'c': { + int c = va_arg(args, int); + out[outlen++] = (char)c; + break; + } + case 'f': { + float d = va_arg(args, float); + sprintf(tmp, "%f", d); + strcpy(out + outlen, tmp); + outlen += strlen(tmp); + break; + } + default: + puts(p"unknown formatter!"); + assert(false); + goto END; + } + } +END: + out[outlen] = '\0'; + va_end(args); +} + +// Basic Variadic Macro Patterns + +#define PRINT(fmt, ...) printf(fmt "\n", __VA_ARGS__) + +#define ASSERT(cond, fmt, ...) \ + if (!(cond)) \ + { \ + printf(p"FAIL at %s:%d - " fmt "\n", __FILE__, __LINE__, __VA_ARGS__); \ + assert(cond); \ + } + +#define TEST_CODE(name, code, expected, ...) \ + do \ + { \ + printf(p"[%s] ", name); \ + assert( \ + strcmp(name, p"add_test") == 0 \ + || strcmp(name, p"mul_test") == 0 \ + ); \ + int result = (code); \ + printf(p"result=%d, expected=%d " __VA_ARGS__ "\n", result, expected); \ + assert (result == expected); \ + } while (0) + +int add(const int a, const int b) +{ + return a + b; +} + +int multiply(const int a, const int b) +{ + return a * b; +} + +#define CALCULATE_SUM(sum, ...) \ + do { \ + int vals[] = { __VA_ARGS__ }; \ + for (int i = 0; i < (int)(sizeof(vals) / sizeof(vals[0])); i++) \ + sum += vals[i]; \ + } while (0) + + +// Brace-in-macro-argument tests (issue: error 3068) + +// Support outer macro receiving a braced struct initialiser. Tests that { } tokens are accepted +// inside macro arguments (preprocessor fix) +struct BraceRecord +{ + const char *name; + int value; +}; + +#define INNER_RECORD(label, val) {(label), (val)} +#define OUTER_VARIADIC(...) \ + static struct BraceRecord brace_records[] = {__VA_ARGS__} + +// Support variadic passthrough to a function +#define INVOKE(function, ...) \ + do \ + { \ + function(__VA_ARGS__); \ + } \ + while (0) + + +int main(void) +{ + // Test 1: Basic variadic usage with different argument counts + PRINT(p"test 1: %d + %d = %d", 5, 3, add(5, 3)); + PRINT(p"test %d: %d * %d = %d", 2, 4, 7, multiply(4, 7)); + int sum = 0; + CALCULATE_SUM(sum, 1, 2, 3, 4, 5); + PRINT(p"sum is %d", sum); + assert (sum == 15); + + // Test 2: Assertion with custom messages + ASSERT(add(2, 2) == 4, p"addition check: 2+2=%d", add(2, 2)); + ASSERT(multiply(3, 3) == 9, p"multiplication: 3*3=%d", multiply(3, 3)); + + // Test 3: Code generation testing + TEST_CODE(p"add_test", add(10, 20), 30, p"(10+20)"); + TEST_CODE(p"mul_test", multiply(5, 6), 30, p"(5*6)"); + + // Test 4: Brace-in-macro-argument (issue: error 3068) + // Verify that { } tokens inside macro arguments are accepted by the preprocessor + OUTER_VARIADIC( + INNER_RECORD( + p"a", + 1 + ) + ); + assert(strcmp(brace_records[0].name, p"a") == 0); + assert(brace_records[0].value == 1); + + // Test 5: Support variadic passthrough to a function + char buf[64]; + INVOKE(sprintf, buf, p"%s %d", brace_records[0].name, brace_records[0].value); + printf(p"buf: %s\n", buf); + assert(strcmp(buf, p"a 1") == 0); + + // Test 6: Support variadic usage in a function + char simple_buf[64]; + simple_sprintf(simple_buf, "dcff", 3, p'a', 3.14f, 42.5f); + printf(p"simple_buf: %s\n", simple_buf); + assert(strcmp(simple_buf, "3A3.14000042.500000") == 0); + + // Test 7: Escaped quotes inside macro arguments + // Verify that \" inside a string arg and \' inside a char arg do not confuse + // the macro argument scanner (they must not toggle the quote state) + char eq_buf[64]; + INVOKE(sprintf, eq_buf, "%s", p"hello\"world"); + printf(p"eq_buf: %s\n", eq_buf); + assert(strcmp(eq_buf, p"hello\"world") == 0); + + char sq_buf[64]; + INVOKE(sprintf, sq_buf, "%c", p'\''); + printf(p"sq_buf: %s\n", sq_buf); + assert(sq_buf[0] == '\'' && sq_buf[1] == '\0'); + + char mixed_buf[64]; + INVOKE(sprintf, mixed_buf, p"This \"%s\" '%c'orld values.", p"\"hello'\"", p'w'); + printf(p"mixed_buf: %s\n", mixed_buf); + assert(strcmp(mixed_buf, p"This \"\"hello'\"\" 'w'orld values.") == 0); + + return 0; +} diff --git a/oscar64/autotest/vcalltest.cpp b/oscar64/autotest/vcalltest.cpp new file mode 100644 index 0000000..2b1fd56 --- /dev/null +++ b/oscar64/autotest/vcalltest.cpp @@ -0,0 +1,93 @@ +#include + +struct A +{ + virtual int f(int x); +}; + +struct B : A +{ + virtual int f(int x); +}; + +struct C : A +{ + virtual int f(int x); + virtual int g(int y, int z); +}; + +struct D : B +{ + virtual int f(int x); +}; + +struct E : C +{ + virtual int f(int x); + virtual int g(int y, int z); +}; + +struct F : C +{ + virtual int g(int y, int z); +}; + +int A::f(int x) +{ + return x * 1; +} + +int B::f(int x) +{ + return x * 2; +} + +int C::f(int x) +{ + return x * 3; +} + +int C::g(int y, int z) +{ + return (y + z) * 3; +} + +int D::f(int x) +{ + return x * 4; +} + +int E::f(int x) +{ + return x * 5; +} + +int E::g(int y, int z) +{ + return (y + z) * 5; +} + +int F::g(int y, int z) +{ + return (y + z) * 6; +} + +int main(void) +{ + A a; + B b; + C c; + D d; + E e; + F f; + + assert(a.f(3) == c.f(1)); + assert(b.f(4) == d.f(2)); + assert(e.f(2) == b.f(5)); + assert(f.f(5) == e.f(3)); + + assert(c.g(3, 2) == e.g(1, 2)); + assert(c.g(1, 5) == f.g(0, 3)); + + return 0; +} diff --git a/oscar64/autotest/vcalltree.cpp b/oscar64/autotest/vcalltree.cpp new file mode 100644 index 0000000..ef3ef13 --- /dev/null +++ b/oscar64/autotest/vcalltree.cpp @@ -0,0 +1,79 @@ +#include + +struct Node +{ + virtual float eval(void) + { + return 1.0e6; + } +}; + +struct ConstNode : Node +{ + float c; + + ConstNode(float c_) + : c(c_) {} + + virtual float eval(void) + { + return c; + } +}; + +struct BinopNode : Node +{ + Node * left, * right; + + BinopNode(Node * left_, Node * right_) + : left(left_), right(right_) + {} +}; + +struct AddNode : BinopNode +{ + AddNode(Node * left_, Node * right_) + : BinopNode(left_, right_) + {} + + virtual float eval(void) + { + return left->eval() + right->eval(); + } +}; + +struct SubNode : BinopNode +{ + SubNode(Node * left_, Node * right_) + : BinopNode(left_, right_) + {} + + virtual float eval(void) + { + return left->eval() - right->eval(); + } +}; + +struct MulNode : BinopNode +{ + MulNode(Node * left_, Node * right_) + : BinopNode(left_, right_) + {} + + virtual float eval(void) + { + return left->eval() * right->eval(); + } +}; + +int main(void) +{ + Node * n = + new SubNode( + new MulNode(new ConstNode(4), new ConstNode(5)), + new AddNode(new ConstNode(12), new ConstNode(7))); + + assert(n->eval() == 1.0); + + return 0; +} diff --git a/oscar64/autotest/virtualdestruct.cpp b/oscar64/autotest/virtualdestruct.cpp new file mode 100644 index 0000000..a9bd394 --- /dev/null +++ b/oscar64/autotest/virtualdestruct.cpp @@ -0,0 +1,63 @@ +#include + +int a, b, c; + +struct A +{ + A(void) + { + a++; + } + + virtual ~A(void) + { + a--; + } +}; + +struct B : A +{ + B(void) + { + b++; + } + + virtual ~B(void) + { + b--; + } +}; + + +struct C : B +{ + C(void) + { + c++; + } + + virtual ~C(void) + { + c--; + } +}; + + +int main() +{ + A * t[3]; + + t[0] = new A(); + t[1] = new B(); + t[2] = new C(); + + assert(a == 3 && b == 2 && c == 1); + + delete t[0]; + delete t[1]; + delete t[2]; + + assert(a == 0 && b == 0 && c == 0); + + return 0; +} diff --git a/oscar64/autotest/volatiletest.c b/oscar64/autotest/volatiletest.c new file mode 100644 index 0000000..79f72a6 --- /dev/null +++ b/oscar64/autotest/volatiletest.c @@ -0,0 +1,143 @@ +#include +#include + +inline char udiff(char a, char b) +{ + return b - a; +} + +void test_readorder(void) +{ + char u0 = testio.cnt0; + char u1 = testio.cnt0; + char t = testio.cnt0; + char u2 = testio.cnt0; + + t = testio.cnt0; + t = testio.cnt0; + t = testio.cnt0; + char u3 = testio.cnt0; + for(char i=0; i<16; i++) + t = testio.cnt0; + char u4 = testio.cnt0; + + assert(udiff(u0, u1) == 1); + assert(udiff(u1, u2) == 2); + assert(udiff(u2, u3) == 4); + assert(udiff(u3, u4) == 17); +} + +void test_writeorder(void) +{ + char u0 = testio.cnt0; + char u1 = testio.cnt0; + testio.cnt0 = 0; + char u2 = testio.cnt0; + + testio.cnt0 = 0; + testio.cnt0 = 0; + testio.cnt0 = 0; + char u3 = testio.cnt0; + for(char i=0; i<16; i++) + testio.cnt0 = 0; + char u4 = testio.cnt0; + + assert(udiff(u0, u1) == 1); + assert(udiff(u1, u2) == 2); + assert(udiff(u2, u3) == 4); + assert(udiff(u3, u4) == 17); +} + +volatile char buffer[16]; + +void test_dma(void) +{ + testio.dmaddr = unsigned(buffer); + testio.dmdata = 0; + testio.dmdata = 1; + testio.dmdata = 2; + testio.dmdata = 3; + testio.dmdata = 0; + testio.dmdata = 0; + testio.dmdata = 0; + testio.dmdata = 0; + testio.dmdata = 4; + testio.dmdata = 5; + testio.dmdata = 6; + testio.dmdata = 7; + testio.dmdata = 0; + testio.dmdata = 1; + testio.dmdata = 2; + testio.dmdata = 3; + assert(buffer[ 0] == 0); + assert(buffer[ 1] == 1); + assert(buffer[ 2] == 2); + assert(buffer[ 3] == 3); + assert(buffer[ 4] == 0); + assert(buffer[ 5] == 0); + assert(buffer[ 6] == 0); + assert(buffer[ 7] == 0); + assert(buffer[ 8] == 4); + assert(buffer[ 9] == 5); + assert(buffer[10] == 6); + assert(buffer[11] == 7); + assert(buffer[12] == 0); + assert(buffer[13] == 1); + assert(buffer[14] == 2); + assert(buffer[15] == 3); + testio.dmaddr = unsigned(buffer); + char tbuffer[16]; + for(char i=0; i<16; i++) + tbuffer[i] = testio.dmdata; + assert(tbuffer[ 0] == 0); + assert(tbuffer[ 1] == 1); + assert(tbuffer[ 2] == 2); + assert(tbuffer[ 3] == 3); + assert(tbuffer[ 4] == 0); + assert(tbuffer[ 5] == 0); + assert(tbuffer[ 6] == 0); + assert(tbuffer[ 7] == 0); + assert(tbuffer[ 8] == 4); + assert(tbuffer[ 9] == 5); + assert(tbuffer[10] == 6); + assert(tbuffer[11] == 7); + assert(tbuffer[12] == 0); + assert(tbuffer[13] == 1); + assert(tbuffer[14] == 2); + assert(tbuffer[15] == 3); +} + +char read_cnt0(void) +{ + return testio.cnt0; +} + +void test_unusedread(void) +{ + char base0 = testio.cnt0; + read_cnt0(); + char end0 = testio.cnt0; + + assert(udiff(base0, end0) == 2); +} + +void test_readsmallfunc(void) +{ + char base0 = testio.cnt0; + char u0 = read_cnt0(); + char u1 = read_cnt0(); + char end0 = testio.cnt0; + + assert(udiff(base0, u0) == 1); + assert(udiff(u0, u1) == 1); + assert(udiff(u1, end0) == 1); +} + +int main(void) +{ + test_readorder(); + test_writeorder(); + test_dma(); + test_unusedread(); + test_readsmallfunc(); +} diff --git a/oscar64/include/assert.c b/oscar64/include/assert.c new file mode 100644 index 0000000..f43dd2b --- /dev/null +++ b/oscar64/include/assert.c @@ -0,0 +1,8 @@ +#include "assert.h" +#include + +void assert(bool b) +{ + if (!b) + exit(-1); +} diff --git a/oscar64/include/assert.h b/oscar64/include/assert.h new file mode 100644 index 0000000..4f48d6d --- /dev/null +++ b/oscar64/include/assert.h @@ -0,0 +1,8 @@ +#ifndef ASSERT_H +#define ASSERT_H + +void assert(bool b); + +#pragma compile("assert.c") + +#endif diff --git a/oscar64/include/audio/sidfx.c b/oscar64/include/audio/sidfx.c new file mode 100644 index 0000000..49572e7 --- /dev/null +++ b/oscar64/include/audio/sidfx.c @@ -0,0 +1,201 @@ +#include "sidfx.h" + +enum SIDFXState +{ + SIDFX_IDLE, + SIDFX_RESET_0, + SIDFX_RESET_1, + SIDFX_READY, + SIDFX_PLAY, + SIDFX_WAIT +}; + +__striped static struct SIDFXChannel +{ + const SIDFX * volatile com; + byte delay, priority; + volatile byte cnt; + volatile SIDFXState state; + unsigned freq, pwm; + +} channels[3]; + +void sidfx_init(void) +{ + for(char i=0; i<3; i++) + { + channels[i].com = nullptr; + channels[i].state = SIDFX_IDLE; + channels[i].priority = 0; + channels[i].delay = 1; + } +} + +bool sidfx_idle(byte chn) +{ + return channels[chn].state == SIDFX_IDLE; +} + +char sidfx_cnt(byte chn) +{ + return channels[chn].cnt; +} + +void sidfx_play(byte chn, const SIDFX * fx, byte cnt) +{ + SIDFXState ns = channels[chn].state; + + if (ns == SIDFX_IDLE) + ns = SIDFX_READY; + else if (channels[chn].priority <= fx->priority) + ns = SIDFX_RESET_1; + else + return; + + channels[chn].state = SIDFX_IDLE; + channels[chn].delay = 1; + + channels[chn].com = fx; + channels[chn].cnt = cnt - 1; + channels[chn].priority = fx->priority; + + channels[chn].state = ns; +} + +void sidfx_stop(byte chn) +{ + channels[chn].com = nullptr; + if (channels[chn].state != SIDFX_IDLE) + { + channels[chn].state = SIDFX_RESET_0; + channels[chn].delay = 1; + } +} + +inline void sidfx_loop_ch(byte ch) +{ + if (channels[ch].state) + { + const SIDFX * com = channels[ch].com; + + channels[ch].delay--; + if (channels[ch].delay) + { + if (com->dfreq) + { + channels[ch].freq += com->dfreq; + sid.voices[ch].freq = channels[ch].freq; + } + if (com->dpwm) + { + channels[ch].pwm += com->dpwm; + sid.voices[ch].pwm = channels[ch].pwm; + } + } + + while (!channels[ch].delay) + { + switch (channels[ch].state) + { + case SIDFX_IDLE: + channels[ch].delay = 1; + break; + case SIDFX_RESET_0: + sid.voices[ch].ctrl = 0; + sid.voices[ch].attdec = 0; + sid.voices[ch].susrel = 0; + if (com) + channels[ch].state = SIDFX_READY; + else + channels[ch].state = SIDFX_IDLE; + channels[ch].delay = 1; + break; + case SIDFX_RESET_1: + sid.voices[ch].ctrl = SID_CTRL_TEST; + sid.voices[ch].ctrl = 0; + sid.voices[ch].attdec = 0; + sid.voices[ch].susrel = 0; + channels[ch].state = SIDFX_READY; + break; + case SIDFX_READY: + channels[ch].freq = com->freq; + channels[ch].pwm = com->pwm; + + sid.voices[ch].freq = com->freq; + sid.voices[ch].pwm = com->pwm; + sid.voices[ch].attdec = com->attdec; + sid.voices[ch].susrel = com->susrel; + sid.voices[ch].ctrl = com->ctrl; + + if (com->ctrl & SID_CTRL_GATE) + { + channels[ch].delay = com->time1; + channels[ch].state = SIDFX_PLAY; + } + else + { + channels[ch].delay = com->time0; + channels[ch].state = SIDFX_PLAY; + } + break; + case SIDFX_PLAY: + if (com->time0) + { + sid.voices[ch].ctrl = com->ctrl & ~SID_CTRL_GATE; + channels[ch].delay = com->time0 - 1; + channels[ch].state = SIDFX_WAIT; + } + else if (channels[ch].cnt) + { + char sr = com->susrel & 0xf0; + com++; + char ctrl = com->ctrl; + if ((com->attdec & 0xef) == 0 && (ctrl & SID_CTRL_GATE) && (com->susrel & 0xf0) > sr) + { + sid.voices[ch].ctrl = ctrl & ~SID_CTRL_GATE; + sid.voices[ch].ctrl = ctrl | SID_CTRL_GATE; + } + channels[ch].cnt--; + channels[ch].com = com; + channels[ch].priority = com->priority; + channels[ch].state = SIDFX_READY; + } + else + { + com = nullptr; + channels[ch].state = SIDFX_RESET_0; + } + break; + case SIDFX_WAIT: + if (channels[ch].cnt) + { + com++; + channels[ch].cnt--; + channels[ch].com = com; + channels[ch].priority = com->priority; + if (com->ctrl & SID_CTRL_GATE) + channels[ch].state = SIDFX_RESET_0; + else + channels[ch].state = SIDFX_READY; + } + else + { + com = nullptr; + channels[ch].state = SIDFX_RESET_0; + } + break; + } + } + } +} + +void sidfx_loop_2(void) +{ + sidfx_loop_ch(2); +} + +void sidfx_loop(void) +{ + for(byte ch=0; ch<3; ch++) + sidfx_loop_ch(ch); +} diff --git a/oscar64/include/audio/sidfx.h b/oscar64/include/audio/sidfx.h new file mode 100644 index 0000000..4777afc --- /dev/null +++ b/oscar64/include/audio/sidfx.h @@ -0,0 +1,31 @@ +#ifndef SIDFX_H +#define SIDFX_H + +#include + +struct SIDFX +{ + unsigned freq, pwm; + byte ctrl, attdec, susrel; + int dfreq, dpwm; + byte time1, time0; + byte priority; +}; + +void sidfx_init(void); + +inline bool sidfx_idle(byte chn); + +inline void sidfx_play(byte chn, const SIDFX * fx, byte cnt); + +void sidfx_stop(byte chn); + +char sidfx_cnt(byte chn); + +void sidfx_loop(void); + +void sidfx_loop_2(void); + +#pragma compile("sidfx.c") + +#endif diff --git a/oscar64/include/c128/bank1.c b/oscar64/include/c128/bank1.c new file mode 100644 index 0000000..37b4f2e --- /dev/null +++ b/oscar64/include/c128/bank1.c @@ -0,0 +1,84 @@ +#include "bank1.h" +#include "mmu.h" +#include + +void bnk1_init(void) +{ + mmu.cr = 0x3e; + memcpy((char *)0xfc00, (char *)0xf000, 0x0300); + xmmu.rcr |= 0x0c; + mmu.cr = 0x3f; +} + +#pragma code(bnk1code) + +char bnk1_readb(volatile char * p) +{ + mmu.bank1 = 0; + char c = *p; + mmu.bank0 = 0; + return c; +} + +unsigned bnk1_readw(volatile unsigned * p) +{ + mmu.bank1 = 0; + unsigned w = *p; + mmu.bank0 = 1; + return w; +} + +unsigned long bnk1_readl(volatile unsigned long * p) +{ + mmu.bank1 = 0; + unsigned long l = *p; + mmu.bank0 = 3; + return l; +} + +void bnk1_readm(char * dp, volatile char * sp, unsigned size) +{ + while (size > 0) + { + mmu.bank1 = 0; + char c = * sp++; + mmu.bank0 = c; + *dp++ = c; + size--; + } +} + +void bnk1_writeb(volatile char * p, char b) +{ + mmu.bank1 = b; + *p = b; + mmu.bank0 = b; +} + +void bnk1_writew(volatile unsigned * p, unsigned w) +{ + mmu.bank1 = w; + *p = w; + mmu.bank0 = w; +} + +void bnk1_writel(volatile unsigned long * p, unsigned long l) +{ + mmu.bank1 = l; + *p = l; + mmu.bank0 = l; +} + +void bnk1_writem(volatile char * dp, const char * sp, unsigned size) +{ + while (size > 0) + { + char c = * sp++; + mmu.bank1 = c; + *dp++ = c; + mmu.bank0 = c; + size--; + } +} + +#pragma code(code) diff --git a/oscar64/include/c128/bank1.h b/oscar64/include/c128/bank1.h new file mode 100644 index 0000000..f6a49f7 --- /dev/null +++ b/oscar64/include/c128/bank1.h @@ -0,0 +1,34 @@ +#ifndef C128_BANK1_H +#define C128_BANK1_H + + +#pragma section( bnk1code, 0) + +#pragma region( bnk1code, 0xf000, 0xf300, , , {bnk1code}, 0xfc00 ) + +void bnk1_init(void); + +#pragma code(bnk1code) + +__noinline char bnk1_readb(volatile char * p); + +__noinline unsigned bnk1_readw(volatile unsigned * p); + +__noinline unsigned long bnk1_readl(volatile unsigned long * p); + +__noinline void bnk1_readm(char * dp, volatile char * sp, unsigned size); + + +__noinline void bnk1_writeb(volatile char * p, char b); + +__noinline void bnk1_writew(volatile unsigned * p, unsigned w); + +__noinline void bnk1_writel(volatile unsigned long * p, unsigned long l); + +__noinline void bnk1_writem(volatile char * dp, const char * sp, unsigned size); + +#pragma code(code) + +#pragma compile("bank1.c") + +#endif diff --git a/oscar64/include/c128/mmu.c b/oscar64/include/c128/mmu.c new file mode 100644 index 0000000..6239f10 --- /dev/null +++ b/oscar64/include/c128/mmu.c @@ -0,0 +1,8 @@ +#include "mmu.h" + +inline char mmu_set(char cr) +{ + char pcr = mmu.cr; + mmu.cr = cr; + return pcr; +} diff --git a/oscar64/include/c128/mmu.h b/oscar64/include/c128/mmu.h new file mode 100644 index 0000000..d58922a --- /dev/null +++ b/oscar64/include/c128/mmu.h @@ -0,0 +1,35 @@ +#ifndef C128_MMU_H +#define C128_MMU_H + +#include + +struct MMU +{ + volatile __memmap byte cr; + volatile __memmap byte bank0; + volatile __memmap byte bank1; + volatile __memmap byte bank14; + volatile __memmap byte bankx; +}; + +struct XMMU +{ + volatile byte cr; + volatile byte pcr[4]; + volatile byte mcr; + volatile byte rcr; + volatile word page0; + volatile word page1; + volatile byte vr; +}; + +#define mmu (*((struct MMU *)0xff00)) + +#define xmmu (*((struct XMMU *)0xd500)) + +inline char mmu_set(char cr); + +#pragma compile("mmu.c") + +#endif + diff --git a/oscar64/include/c128/vdc.c b/oscar64/include/c128/vdc.c new file mode 100644 index 0000000..4511fd6 --- /dev/null +++ b/oscar64/include/c128/vdc.c @@ -0,0 +1,84 @@ +#include "vdc.h" + + +inline void vdc_reg(VDCRegister reg) +{ + vdc.addr = reg; +} + +inline void vdc_write(byte data) +{ + do {} while (vdc.addr < 128); + vdc.data = data; +} + +inline byte vdc_read(void) +{ + do {} while (vdc.addr < 128); + return vdc.data; +} + + +void vdc_reg_write(VDCRegister reg, byte data) +{ + vdc_reg(reg); + vdc_write(data); +} + +byte vdc_reg_read(VDCRegister reg) +{ + vdc_reg(reg); + return vdc_read(); +} + + +void vdc_mem_addr(unsigned addr) +{ + #pragma callinline() + vdc_reg_write(VDCR_ADDRH, addr >> 8); + #pragma callinline() + vdc_reg_write(VDCR_ADDRL, addr); + #pragma callinline() + vdc_reg(VDCR_DATA); +} + +inline void vdc_mem_write(char data) +{ + vdc_write(data); +} + +inline char vdc_mem_read(void) +{ + return vdc_read(); +} + + +void vdc_mem_write_at(unsigned addr, char data) +{ + #pragma callinline() + vdc_mem_addr(addr); + vdc_write(data); +} + +char vdc_mem_read_at(unsigned addr) +{ + #pragma callinline() + vdc_mem_addr(addr); + return vdc_read(); +} + + +void vdc_mem_write_buffer(unsigned addr, const char * data, char size) +{ + vdc_mem_addr(addr); + for(char i=0; i + +enum VDCRegister +{ + VDCR_HTOTAL, + VDCR_HDISPLAY, + VDCR_HSYNC, + VDCR_SYNCSIZE, + + VDCR_VTOTAL, + VDCR_VADJUST, + VDCR_VDISPLAY, + VDCR_VSYNC, + + VDCR_LACE, + VDCR_CSIZE, + VDCR_CURSOR_START, + VDCR_CURSOR_END, + + VDCR_DISP_ADDRH, + VDCR_DISP_ADDRL, + VDCR_CURSOR_ADDRH, + VDCR_CURSOR_ADDRL, + + VDCR_LPEN_Y, + VDCR_LPEN_X, + VDCR_ADDRH, + VDCR_ADDRL, + + VDCR_ATTR_ADDRH, + VDCR_ATTR_ADDRL, + VDCR_CWIDTH, + VDCR_CHEIGHT, + + VDCR_VSCROLL, + VDCR_HSCROLL, + VDCR_COLOR, + VDCR_ROWINC, + + VDCR_CHAR_ADDRH, + VDCR_UNDERLINE, + VDCR_DSIZE, + VDCR_DATA, + + VDCR_BLOCK_ADDRH, + VDCR_BLOCK_ADDRL, + VDCR_HSTART, + VDCR_HEND, + + VDCR_REFRESH +}; + +struct VDC +{ + volatile char addr; + volatile char data; +}; + +#define vdc (*((struct VDC *)0xd600)) + +inline void vdc_reg(VDCRegister reg); + +inline void vdc_write(byte data); + +inline byte vdc_read(void); + + +void vdc_reg_write(VDCRegister reg, byte data); + +byte vdc_reg_read(VDCRegister reg); + + +void vdc_mem_addr(unsigned addr); + +inline void vdc_mem_write(char data); + +inline char vdc_mem_read(void); + + +void vdc_mem_write_at(unsigned addr, char data); + +char vdc_mem_read_at(unsigned addr); + + +void vdc_mem_write_buffer(unsigned addr, const char * data, char size); + +void vdc_mem_read_buffer(unsigned addr, char * data, char size); + + +#pragma compile("vdc.c") + +#endif diff --git a/oscar64/include/c64/asm6502.c b/oscar64/include/c64/asm6502.c new file mode 100644 index 0000000..db941fa --- /dev/null +++ b/oscar64/include/c64/asm6502.c @@ -0,0 +1,94 @@ +#include "asm6502.h" + +inline byte asm_np(byte * ip, AsmIns ins) +{ + ip[0] = ins & 0xff; + return 1; +} + +inline byte asm_ac(byte * ip, AsmIns ins) +{ + ip[0] = (ins & 0xff) | 0x08; + return 1; +} + +inline byte asm_zp(byte * ip, AsmIns ins, byte addr) +{ + ip[0] = (ins & 0xff) | 0x04; + ip[1] = addr; + return 2; +} + +inline byte asm_rl(byte * ip, AsmIns ins, sbyte addr) +{ + ip[0] = ins & 0xff; + ip[1] = (byte)addr; + return 2; +} + +inline byte asm_im(byte * ip, AsmIns ins, byte value) +{ + ip[0] = (ins & 0xff) | ((ins & 0x01) << 3); + ip[1] = value; + return 2; +} + +inline byte asm_zx(byte * ip, AsmIns ins, byte addr) +{ + ip[0] = (ins & 0xff) | 0x05; + ip[1] = addr; + return 2; +} + +inline byte asm_zy(byte * ip, AsmIns ins, byte addr) +{ + ip[0] = (ins & 0xff) | 0x05; + ip[1] = addr; + return 2; +} + +inline byte asm_ab(byte * ip, AsmIns ins, unsigned addr) +{ + ip[0] = (ins & 0xff) ^ 0x0c; + ip[1] = addr & 0xff; + ip[2] = addr >> 8; + return 3; +} + +inline byte asm_in(byte * ip, AsmIns ins, unsigned addr) +{ + ip[0] = (ins & 0xff) ^ 0x2c; + ip[1] = addr & 0xff; + ip[2] = addr >> 8; + return 3; +} + +inline byte asm_ax(byte * ip, AsmIns ins, unsigned addr) +{ + ip[0] = (ins & 0xff) | 0x1c; + ip[1] = addr & 0xff; + ip[2] = addr >> 8; + return 3; +} + +inline byte asm_ay(byte * ip, AsmIns ins, unsigned addr) +{ + ip[0] = (ins & 0xff) | 0x18; + ip[1] = addr & 0xff; + ip[2] = addr >> 8; + return 3; +} + +inline byte asm_ix(byte * ip, AsmIns ins, byte addr) +{ + ip[0] = (ins & 0xff) | 0x00; + ip[1] = addr; + return 2; +} + +inline byte asm_iy(byte * ip, AsmIns ins, byte addr) +{ + ip[0] = (ins & 0xff) | 0x10; + ip[1] = addr; + return 2; +} diff --git a/oscar64/include/c64/asm6502.h b/oscar64/include/c64/asm6502.h new file mode 100644 index 0000000..f2b6ed8 --- /dev/null +++ b/oscar64/include/c64/asm6502.h @@ -0,0 +1,132 @@ +#ifndef C64_ASM_6502_H +#define C64_ASM_6502_H + +#include "types.h" + +// Base form for the 6502 instructions + +enum AsmIns +{ + // Implied + + ASM_BRK = 0x00, + + ASM_RTI = 0x40, + ASM_RTS = 0x60, + + ASM_PHP = 0x08, + ASM_CLC = 0x18, + ASM_PLP = 0x28, + ASM_SEC = 0x38, + ASM_PHA = 0x48, + + ASM_CLI = 0x58, + ASM_PLA = 0x68, + ASM_SEI = 0x78, + + ASM_DEY = 0x88, + ASM_TYA = 0x98, + ASM_TAY = 0xa8, + ASM_CLV = 0xb8, + + ASM_INY = 0xc8, + ASM_CLD = 0xd8, + ASM_INX = 0xe8, + ASM_SED = 0xf8, + + ASM_TXA = 0x8a, + ASM_TXS = 0x9a, + ASM_TAX = 0xaa, + ASM_TSX = 0xba, + ASM_DEX = 0xca, + ASM_NOP = 0xea, + + // Relative + ASM_BPL = 0x10, + ASM_BMI = 0x30, + ASM_BVC = 0x50, + ASM_BVS = 0x70, + ASM_BCC = 0x90, + ASM_BCS = 0xb0, + ASM_BNE = 0xd0, + ASM_BEQ = 0xf0, + + // Generic address + ASM_ORA = 0x01, + ASM_AND = 0x21, + ASM_EOR = 0x41, + ASM_ADC = 0x61, + ASM_STA = 0x81, + ASM_LDA = 0xa1, + ASM_CMP = 0xc1, + ASM_SBC = 0xe1, + + ASM_STY = 0x80, + ASM_LDY = 0xa0, + ASM_CPY = 0xc0, + ASM_CPX = 0xe0, + + ASM_ASL = 0x02, + ASM_ROL = 0x22, + ASM_LSR = 0x42, + ASM_ROR = 0x62, + + ASM_STX = 0x82, + ASM_LDX = 0xa2, + ASM_DEC = 0xc2, + ASM_INC = 0xe2, + + // Limited Generic + ASM_BIT = 0x20, + + // Jump + ASM_JMP = 0x40, + ASM_JSR = 0x2c +}; + +// the asm_ instructions emit a machine instruction at the given +// location and return the size. + +// implied +inline byte asm_np(byte * ip, AsmIns ins); + +// accu (e.g. rol/ror) +inline byte asm_ac(byte * ip, AsmIns ins); + +// zero page +inline byte asm_zp(byte * ip, AsmIns ins, byte addr); + +// relative branch +inline byte asm_rl(byte * ip, AsmIns ins, sbyte addr); + +// immediate +inline byte asm_im(byte * ip, AsmIns ins, byte value); + +// zero page indexed by x +inline byte asm_zx(byte * ip, AsmIns ins, byte addr); + +// zero page indexed by y +inline byte asm_zy(byte * ip, AsmIns ins, byte addr); + +// absolute +inline byte asm_ab(byte * ip, AsmIns ins, unsigned addr); + +// indirect (jmp) +inline byte asm_in(byte * ip, AsmIns ins, unsigned addr); + +// absolute indexed by x +inline byte asm_ax(byte * ip, AsmIns ins, unsigned addr); + +// absolute indexed by y +inline byte asm_ay(byte * ip, AsmIns ins, unsigned addr); + +// zero page indirect indexed by x +inline byte asm_ix(byte * ip, AsmIns ins, byte addr); + +// zero page indirect indexed by y +inline byte asm_iy(byte * ip, AsmIns ins, byte addr); + +#pragma compile("asm6502.c") + +#endif + diff --git a/oscar64/include/c64/charwin.c b/oscar64/include/c64/charwin.c new file mode 100644 index 0000000..23bc7c0 --- /dev/null +++ b/oscar64/include/c64/charwin.c @@ -0,0 +1,985 @@ +#include "charwin.h" +#include + +static const unsigned mul40[25] = { + 0, 40, 80, 120, 160, + 200, 240, 280, 320, 360, + 400, 440, 480, 520, 560, + 600, 640, 680, 720, 760, + 800, 840, 880, 920, 960 +}; + +static __native inline void copy_fwd(char * sdp, const char * ssp, char * cdp, const char * csp, char n) +{ + for(char i=0; isx = sx; + win->sy = sy; + win->wx = wx; + win->wy = wy; + win->cx = 0; + win->cy = 0; + win->sp = screen + mul40[sy] + sx; + win->cp = (char *)0xd800 + mul40[sy] + sx; +} + + +void cwin_clear(CharWin * win) +{ + cwin_fill(win, ' ', 1); +} + +void cwin_fill(CharWin * win, char ch, char color) +{ + char *sp = win->sp, * cp = win->cp; + for(char y=0; ywy; y++) + { + fill_fwd(sp, cp, ch, color, win->wx); + sp += 40; + cp += 40; + } +} + + +void cwin_cursor_show(CharWin * win, bool show) +{ + char * cp = win->sp + mul40[win->cy] + win->cx; + if (show) + *cp |= 0x80; + else + *cp &= 0x7f; +} + +void cwin_cursor_move(CharWin * win, char cx, char cy) +{ + win->cx = cx; + win->cy = cy; +} + + +bool cwin_cursor_left(CharWin * win) +{ + if (win->cx > 0) + { + win->cx--; + return true; + } + + return false; +} + +bool cwin_cursor_right(CharWin * win) +{ + if (win->cx + 1 < win->wx) + { + win->cx++; + return true; + } + + return false; +} + +bool cwin_cursor_up(CharWin * win) +{ + if (win->cy > 0) + { + win->cy--; + return true; + } + + return false; +} + +bool cwin_cursor_down(CharWin * win) +{ + if (win->cy + 1 < win->wy) + { + win->cy++; + return true; + } + + return false; +} + +bool cwin_cursor_newline(CharWin * win) +{ + win->cx = 0; + if (win->cy + 1 < win->wy) + { + win->cy++; + return true; + } + + return false; +} + +bool cwin_cursor_forward(CharWin * win) +{ + if (win->cx + 1 < win->wx) + { + win->cx++; + return true; + } + else if (win->cy + 1 < win->wy) + { + win->cx = 0; + win->cy++; + return true; + } + + return false; +} + +bool cwin_cursor_backward(CharWin * win) +{ + if (win->cx > 0) + { + win->cx--; + return true; + } + else if (win->cy > 0) + { + win->cx = win->wx - 1; + win->cy--; + return true; + } + + return false; +} + +//static char p2smap[] = {0x00, 0x20, 0x00, 0x40, 0x00, 0x60, 0x40, 0x60}; +static char p2smap[] = {0x00, 0x00, 0x40, 0x20, 0x80, 0xc0, 0x80, 0x80}; +//static char s2pmap[] = {0x40, 0x20, 0x60, 0xa0, 0x40, 0x20, 0x60, 0xa0}; +static char s2pmap[] = {0x40, 0x00, 0x20, 0xc0, 0xc0, 0x80, 0xa0, 0x40}; + +static inline char p2s(char ch) +{ + return ch ^ p2smap[ch >> 5]; +} + +static inline char s2p(char ch) +{ + return ch ^ s2pmap[ch >> 5]; +} + +void cwin_read_string(CharWin * win, char * buffer) +{ + char * sp = win->sp; + + char i = 0; + for(char y=0; ywy; y++) + { + for(char x=0; xwx; x++) + { + buffer[i++] = s2p(sp[x]); + } + sp += 40; + } + while (i > 0 && buffer[i - 1] == ' ') + i--; + buffer[i] = 0; +} + +void cwin_write_string(CharWin * win, const char * buffer) +{ + char * dp = win->sp; + for(char y=0; ywy; y++) + { + for(char x=0; xwx; x++) + { + char ch = *buffer; + if (ch) + { + dp[x] = p2s(ch); + buffer++; + } + else + dp[x] = ' '; + } + dp += 40; + } + +} +void cwin_put_char(CharWin * win, char ch, char color) +{ + cwin_putat_char(win, win->cx, win->cy, ch, color); + win->cx++; + if (win->cx == win->wx) + { + win->cx = 0; + win->cy++; + } +} + +void cwin_put_chars(CharWin * win, const char * chars, char num, char color) +{ + cwin_putat_chars(win, win->cx, win->cy, chars, color); + win->cx += num; + if (win->cx >= win->wx) + { + win->cx = 0; + win->cy++; + } +} + +char cwin_put_string(CharWin * win, const char * str, char color) +{ + char n = cwin_putat_string(win, win->cx, win->cy, str, color); + win->cx += n; + if (win->cx >= win->wx) + { + win->cx = 0; + win->cy++; + } + return n; +} + + +void cwin_put_char_raw(CharWin * win, char ch, char color) +{ + cwin_putat_char_raw(win, win->cx, win->cy, ch, color); + win->cx++; + if (win->cx == win->wx) + { + win->cx = 0; + win->cy++; + } +} + +void cwin_put_chars_raw(CharWin * win, const char * chars, char num, char color) +{ + cwin_putat_chars_raw(win, win->cx, win->cy, chars, color); + win->cx += num; + if (win->cx >= win->wx) + { + win->cx = 0; + win->cy++; + } +} + +char cwin_put_string_raw(CharWin * win, const char * str, char color) +{ + char n = cwin_putat_string_raw(win, win->cx, win->cy, str, color); + win->cx += n; + if (win->cx >= win->wx) + { + win->cx = 0; + win->cy++; + } + return n; +} + + + +void cwin_putat_char(CharWin * win, char x, char y, char ch, char color) +{ + int offset = mul40[y] + x; + + win->sp[offset] = p2s(ch); + win->cp[offset] = color; +} + +#pragma native(cwin_putat_char) + +void cwin_putat_chars(CharWin * win, char x, char y, const char * chars, char num, char color) +{ + int offset = mul40[y] + x; + + char * sp = win->sp + offset; + char * cp = win->cp + offset; + + for(char i=0; isp + offset; + char * cp = win->cp + offset; + + char i = 0; + while (char ch = str[i]) + { + sp[i] = p2s(ch); + cp[i] = color; + i++; + } + + return i; +} + +#pragma native(cwin_putat_string) + +void cwin_putat_char_raw(CharWin * win, char x, char y, char ch, char color) +{ + int offset = mul40[y] + x; + + win->sp[offset] = ch; + win->cp[offset] = color; +} + +#pragma native(cwin_putat_char_raw) + +void cwin_putat_chars_raw(CharWin * win, char x, char y, const char * chars, char num, char color) +{ + int offset = mul40[y] + x; + + char * sp = win->sp + offset; + char * cp = win->cp + offset; + + for(char i=0; isp + offset; + char * cp = win->cp + offset; + + char i = 0; + while (char ch = str[i]) + { + sp[i] = ch; + cp[i] = color; + i++; + } + + return i; +} + +#pragma native(cwin_putat_string_raw) + +char cwin_getat_char(CharWin * win, char x, char y) +{ + char * sp = win->sp + mul40[y] + x; + + return s2p(*sp); +} + +#pragma native(cwin_getat_char) + +void cwin_getat_chars(CharWin * win, char x, char y, char * chars, char num) +{ + char * sp = win->sp + mul40[y] + x; + + for(char i=0; isp + mul40[y] + x; + + return *sp; +} + +#pragma native(cwin_getat_chars_raw) + +void cwin_getat_chars_raw(CharWin * win, char x, char y, char * chars, char num) +{ + char * sp = win->sp + mul40[y] + x; + + for(char i=0; isp + offset; + char * cp = win->cp + offset; + + for(char i=0; isp + offset; + char * cp = win->cp + offset; + + for(char i=0; isp + offset; + + for(char i=0; isp + offset; + + for(char i=0; iwy - 1, rx = win->wx - 1; + + char * sp = win->sp + mul40[y]; + char * cp = win->cp + mul40[y]; + + while (y > win->cy) + { + copy_bwd(sp + 1, sp, cp + 1, cp, rx); + + sp -= 40; + cp -= 40; + sp[40] = sp[rx]; + cp[40] = cp[rx]; + y--; + } + + sp += win->cx; + cp += win->cx; + rx -= win->cx; + + copy_bwd(sp + 1, sp, cp + 1, cp, rx); + + sp[0] = ch; + cp[0] = color; +} + +void cwin_insert_char(CharWin * win, char ch, char color) +{ + cwin_insert_char_raw(win, p2s(ch), color); +} + +void cwin_delete_char(CharWin * win) +{ + char * sp = win->sp + mul40[win->cy]; + char * cp = win->cp + mul40[win->cy]; + + char x = win->cx, rx = win->wx - 1; + + copy_fwd(sp + x, sp + x + 1, cp + x, cp + x + 1, rx - x); + + char y = win->cy + 1; + while (y < win->wy) + { + sp[rx] = sp[40]; + cp[rx] = cp[40]; + + sp += 40; + cp += 40; + + copy_fwd(sp, sp + 1, cp, cp + 1, rx); + + y++; + } + + sp[rx] = ' '; +} + +int cwin_getch(void) +{ + __asm + { + L1: + jsr 0xffe4 + cmp #0 + beq L1 + sta accu + lda #0 + sta accu + 1 + } +} + +int cwin_checkch(void) +{ + __asm + { + L1: + jsr 0xffe4 + sta accu + lda #0 + sta accu + 1 + } +} + +bool cwin_edit_char(CharWin * win, char ch) +{ + switch (ch) + { + case 13: + case 3: + return true; + + case 19: + win->cx = 0; + win->cy = 0; + return false; + + case 147: + cwin_clear(win); + win->cx = 0; + win->cy = 0; + return false; + + case 17: + cwin_cursor_down(win); + return false; + + case 145: // CRSR_UP + cwin_cursor_up(win); + return false; + + case 29: + cwin_cursor_forward(win); + return false; + + case 157: + cwin_cursor_backward(win); + return false; + + case 20: + if (cwin_cursor_backward(win)) + cwin_delete_char(win); + return false; + + default: + if (ch >= 32 && ch < 128 || ch >= 160) + { + if (win->cy + 1 < win->wy || win->cx + 1 < win->wx) + { + cwin_insert_char(win, ch, 1); + cwin_cursor_forward(win); + } + } + return false; + } +} + +char cwin_edit(CharWin * win) +{ + for(;;) + { + cwin_cursor_show(win, true); + char ch = cwin_getch(); + cwin_cursor_show(win, false); + + if (cwin_edit_char(win, ch)) + return ch; + } +} + +void cwin_scroll_left(CharWin * win, char by) +{ + char * sp = win->sp; + char * cp = win->cp; + + char rx = win->wx - by; + + for(char y=0; ywy; y++) + { + copy_fwd(sp, sp + by, cp, cp + by, rx); + } +} + +void cwin_scroll_right(CharWin * win, char by) +{ + char * sp = win->sp; + char * cp = win->cp; + + char rx = win->wx - by; + + for(char y=0; ywy; y++) + { + copy_bwd(sp + by, sp, cp + by, cp, rx); + sp += 40; + cp += 40; + } +} + +void cwin_scroll_up(CharWin * win, char by) +{ + char * sp = win->sp; + char * cp = win->cp; + + char rx = win->wx; + int dst = mul40[by]; + + for(char y=0; ywy - by; y++) + { + copy_fwd(sp, sp + dst, cp, cp + dst, rx); + sp += 40; + cp += 40; + } +} + +void cwin_scroll_down(CharWin * win, char by) +{ + char * sp = win->sp + mul40[win->wy]; + char * cp = win->cp + mul40[win->wy]; + + char rx = win->wx; + + int dst = mul40[by]; + + for(char y=0; ywy - by; y++) + { + sp -= 40; + cp -= 40; + copy_bwd(sp, sp - dst, cp, cp - dst, rx); + } +} + +void cwin_fill_rect_raw(CharWin * win, char x, char y, char w, char h, char ch, char color) +{ + if (w > 0) + { + char * sp = win->sp + mul40[y] + x; + char * cp = win->cp + mul40[y] + x; + + for(char y=0; ycy--; + win->ly--; + cwin_scroll_up(win, 1); + cwin_fill_rect(win, 0, win->wy - 1, win->wx, 1, ' ', 1); +} + +void cwin_console_newline(CharWin * win) +{ + win->cx = 0; + win->cy++; + if (win->cy == win->wy) + cwin_console_scroll_up(win); +} + +void cwin_console_write_char(CharWin * win, char ch, char color) +{ + if (win->cx == win->wx) + cwin_console_newline(win); + + int offset = mul40[win->cy] + win->cx; + + win->sp[offset] = p2s(ch); + win->cp[offset] = color; + win->cx++; +} + +void cwin_console_write_string(CharWin * win, const char * chars, char color) +{ + win->ly = win->cy; + win->lx = win->cx; + + char i = 0; + while (char ch = chars[i]) + { + if (ch == '\n') + cwin_console_newline(win); + else + cwin_console_write_char(win, ch, color); + i++; + } +} + +void cwin_console_clear(CharWin * win) +{ + cwin_fill_rect(win, win->lx, win->ly, win->wx - win->lx, 1, ' ', 1); + cwin_fill_rect(win, 0, win->ly + 1, win->wx, win->wy - win->ly - 1, ' ', 1); +} + +bool cwin_console_cursor_left(CharWin * win) +{ + if (win->cy == win->ly) + { + if (win->cx > win->lx) + { + win->cx--; + return true; + } + } + else if (win->cx > 0) + { + win->cx--; + return true; + } + else + { + win->cy--; + win->cx = win->wx - 1; + return true; + } + return false; +} + +bool cwin_console_cursor_right(CharWin * win) +{ + if (win->cx + 1 < win->wx) + { + win->cx++; + return true; + } + else if (win->cy + 1 < win->wy) + { + win->cy++; + win->cx = 0; + return true; + } + else if (win->ly > 0) + { + win->cx = 0; + win->cy++; + cwin_console_scroll_up(win); + return true; + } + + return false; +} + +void cwin_console_delete_char(CharWin * win) +{ + cwin_delete_char(win); +} + +bool cwin_console_insert_char(CharWin * win, char ch, char color) +{ + if (win->sp[mul40[win->wy - 1] + win->wx - 1] != ' ') + { + if (win->ly == 0) + return false; + cwin_console_scroll_up(win); + } + + cwin_insert_char(win, ch, color); + return true; +} + +bool cwin_console_edit_char(CharWin * win, char ch, char color) +{ + switch (ch) + { + case 13: + case 3: + case 17: + case 145: // CRSR_UP + return true; + + case 19: + win->cx = win->lx; + win->cy = win->ly; + return false; + + case 147: + cwin_console_clear(win); + win->cx = win->lx;; + win->cy = win->ly; + return false; + + case 29: + cwin_console_cursor_right(win); + return false; + + case 157: + cwin_console_cursor_left(win); + return false; + + case 20: + if (cwin_console_cursor_left(win)) + cwin_console_delete_char(win); + return false; + + default: + if (ch >= 32 && ch < 128 || ch >= 160) + { + if (cwin_console_insert_char(win, ch, color)) + cwin_console_cursor_right(win); + } + return false; + } +} + +char cwin_console_edit_string(CharWin * win, char color) +{ + for(;;) + { + cwin_cursor_show(win, true); + char ch = cwin_getch(); + cwin_cursor_show(win, false); + + if (cwin_console_edit_char(win, ch, color)) + { + win->cx = win->lx; + win->cy = win->ly; + return ch; + } + } +} + +void cwin_console_get_string(CharWin * win, char * chars, char size) +{ + char i = 0; + char y = win->ly, x = win->lx; + char * cp = win->sp + mul40[y]; + + while (i < size) + { + chars[i++] = s2p(cp[x++]); + if (x == win->wx) + { + if (y + 1 == win->wy) + break; + x = 0; + cp += 40; + y++; + } + } + + while (i > 0 && chars[i - 1] == ' ') + { + i--; + if (x == 0) + { + y--; + x = win->wx; + } + else + x--; + } + + win->cx = x; + win->cy = y; + + chars[i] = 0; +} + +char * sformat(char * buff, const char * fmt, int * fps, bool print); + +void cwin_console_printf(CharWin * win, char color, const char * fmt, ...) +{ + char buff[200]; + sformat(buff, fmt, (int *)&fmt + 1, false); + cwin_console_write_string(win, buff, color); +} diff --git a/oscar64/include/c64/charwin.h b/oscar64/include/c64/charwin.h new file mode 100644 index 0000000..7b3f457 --- /dev/null +++ b/oscar64/include/c64/charwin.h @@ -0,0 +1,211 @@ +#ifndef C64_CHARWIN_H +#define C64_CHARWIN_H + +struct CharWin +{ + char sx, sy, wx, wy; + char cx, cy, lx, ly; + + char * sp, * cp; +}; + +// Initialize the CharWin structure for the given screen and coordinates, does +// not clear the window +// +void cwin_init(CharWin * win, char * screen, char sx, char sy, char wx, char wy); + + +// Clear the window +// +void cwin_clear(CharWin * win); + +// Fill the window with the given character and color +// +void cwin_fill(CharWin * win, char ch, char color); + + +// Show or hide the cursor by setting or clearing the MSB of the character code +// +void cwin_cursor_show(CharWin * win, bool show); + +// Move the cursor to the given location +// +void cwin_cursor_move(CharWin * win, char cx, char cy); + + +// Move the cursor in the window, returns true if the cursor could be moved +// +bool cwin_cursor_left(CharWin * win); +bool cwin_cursor_right(CharWin * win); +bool cwin_cursor_up(CharWin * win); +bool cwin_cursor_down(CharWin * win); +bool cwin_cursor_forward(CharWin * win); +bool cwin_cursor_backward(CharWin * win); +bool cwin_cursor_newline(CharWin * win); + +// Read the full window into a string +// +void cwin_read_string(CharWin * win, char * buffer); + +// Write the fill window with the given string +// +void cwin_write_string(CharWin * win, const char * buffer); + +// Put a single char at the cursor location and advance the cursor +// +void cwin_put_char(CharWin * win, char ch, char color); + +// Put an array of chars at the cursor location and advance the cursor +// +void cwin_put_chars(CharWin * win, const char * chars, char num, char color); + +// Put a zero terminated string at the cursor location and advance the cursor +// +char cwin_put_string(CharWin * win, const char * str, char color); + +// Put a single raw char at the cursor location and advance the cursor +// +void cwin_put_char_raw(CharWin * win, char ch, char color); + +// Put an array of raw chars at the cursor location and advance the cursor +// +void cwin_put_chars_raw(CharWin * win, const char * chars, char num, char color); + +// Put a zero terminated raw string at the cursor location and advance the cursor +// +char cwin_put_string_raw(CharWin * win, const char * str, char color); + + + +// Put a single char at the given window location +// +void cwin_putat_char(CharWin * win, char x, char y, char ch, char color); + +// Put an array of chars at the given window location +// +void cwin_putat_chars(CharWin * win, char x, char y, const char * chars, char num, char color); + +// Put a zero terminated string at the given window location +// +char cwin_putat_string(CharWin * win, char x, char y, const char * str, char color); + + +// Put a single raw char at the given window location +// +void cwin_putat_char_raw(CharWin * win, char x, char y, char ch, char color); + +// Put an array of raw chars at the given window location +// +void cwin_putat_chars_raw(CharWin * win, char x, char y, const char * chars, char num, char color); + +// Put a zero terminated string at the given window location +// +char cwin_putat_string_raw(CharWin * win, char x, char y, const char * str, char color); + + +// Get a single char at the given window location +// +char cwin_getat_char(CharWin * win, char x, char y); + +// Get an array of chars at the given window location +// +void cwin_getat_chars(CharWin * win, char x, char y, char * chars, char num); + + + +// Get a single char at the given window location +// +char cwin_getat_char_raw(CharWin * win, char x, char y); + +// Get an array of chars at the given window location +// +void cwin_getat_chars_raw(CharWin * win, char x, char y, char * chars, char num); + + +// Put an array of characters into a rectangle in the char win +void cwin_put_rect_raw(CharWin * win, char x, char y, char w, char h, const char * chars, char color); + +void cwin_put_rect(CharWin * win, char x, char y, char w, char h, const char * chars, char color); + +// Get an array of characters from a rectangle of a char win +void cwin_get_rect_raw(CharWin * win, char x, char y, char w, char h, char * chars); + +void cwin_get_rect(CharWin * win, char x, char y, char w, char h, char * chars); + + + +// Insert one space character at the cursor position +// +void cwin_insert_char_raw(CharWin * win, char ch, char color); + +void cwin_insert_char(CharWin * win, char ch, char color); + +// Delete the character at the cursor position +// +void cwin_delete_char(CharWin * win); + +int cwin_getch(void); + +int cwin_checkch(void); + +// Edit the window position using the char as the input +// +bool cwin_edit_char(CharWin * win, char ch); + +// Edit the window using keyboard input, returns the key the exited +// the edit, either return or stop +// +char cwin_edit(CharWin * win); + +// Scroll the window in the given direction, does not fill the new +// empty space +// +void cwin_scroll_left(CharWin * win, char by); +void cwin_scroll_right(CharWin * win, char by); +void cwin_scroll_up(CharWin * win, char by); +void cwin_scroll_down(CharWin * win, char by); + +// Fill the given rectangle with the character and color +// +inline void cwin_fill_rect(CharWin * win, char x, char y, char w, char h, char ch, char color); + +// Fill the given rectangle with the screen code and color +// +void cwin_fill_rect_raw(CharWin * win, char x, char y, char w, char h, char ch, char color); + + +void cwin_console_newline(CharWin * win); + +void cwin_console_scroll_up(CharWin * win); + +void cwin_console_write_char(CharWin * win, char ch, char color); + +void cwin_console_write_string(CharWin * win, const char * chars, char color); + +void cwin_console_clear(CharWin * win); + +bool cwin_console_cursor_left(CharWin * win); +bool cwin_console_cursor_right(CharWin * win); +bool cwin_console_cursor_up(CharWin * win); +bool cwin_console_cursor_down(CharWin * win); + +// Insert one space character at the cursor position +// +bool cwin_console_insert_char(CharWin * win, char ch, char color); + +// Delete the character at the cursor position +// +void cwin_console_delete_char(CharWin * win); + +bool cwin_console_edit_char(CharWin * win, char ch, char color); + +char cwin_console_edit_string(CharWin * win, char color); + +void cwin_console_get_string(CharWin * win, char * chars, char size); + +void cwin_console_printf(CharWin * win, char color, const char * fmt, ...); + +#pragma compile("charwin.c") + + +#endif diff --git a/oscar64/include/c64/cia.c b/oscar64/include/c64/cia.c new file mode 100644 index 0000000..245e4eb --- /dev/null +++ b/oscar64/include/c64/cia.c @@ -0,0 +1,26 @@ +#include "cia.h" + +byte ciaa_pra_def; + +void cia_init(void) +{ + cia1.icr = 0x7f; + cia2.icr = 0x7f; + cia1.pra = 0x7f; + cia1.cra = 0x08; + cia1.crb = 0x08; + cia2.cra = 0x08; + cia2.crb = 0x08; + + cia1.ddrb = 0x00; + cia2.ddrb = 0x00; + cia1.ddra = 0xff; + + cia2.pra = 0x07; + cia2.ddra = 0x3f; + + char i0 = cia1.icr; + char i1 = cia2.icr; + + ciaa_pra_def = 0x7f; +} diff --git a/oscar64/include/c64/cia.h b/oscar64/include/c64/cia.h new file mode 100644 index 0000000..aebb71e --- /dev/null +++ b/oscar64/include/c64/cia.h @@ -0,0 +1,29 @@ +#ifndef C64_CIA +#define C64_CIA + +#include "types.h" + +struct CIA +{ + volatile byte pra, prb; + volatile byte ddra, ddrb; + volatile word ta, tb; + volatile byte todt, tods, todm, todh; + volatile byte sdr; + volatile byte icr; + volatile byte cra, crb; +}; + +#define cia1 (*((struct CIA *)0xdc00)) +#define cia2 (*((struct CIA *)0xdd00)) + +extern byte ciaa_pra_def; + +void cia_init(void); + +#pragma compile("cia.c") + +#endif + + + diff --git a/oscar64/include/c64/easyflash.h b/oscar64/include/c64/easyflash.h new file mode 100644 index 0000000..b280343 --- /dev/null +++ b/oscar64/include/c64/easyflash.h @@ -0,0 +1,63 @@ +#ifndef C64_EASYFLASH_H +#define C64_EASYFLASH_H + +#include "types.h" + +struct EasyFlash +{ + volatile __memmap byte bank; + byte pad1; + volatile byte control; +}; + +#define EFCTRL_GAME 0x01 +#define EFCTRL_EXROM 0x02 +#define EFCTRL_MODE 0x04 +#define EFCTRL_LED 0x80 + + +#define eflash (*(EasyFlash *)0xde00) + +#ifdef __cplusplus + +#ifdef EFPROX_SECTION +#pragma code(EFPROX_SECTION) +#endif + +template +__noinline auto ef_call_p(P... p) +{ + if (back != __bankof(fn)) + eflash.bank = __bankof(fn); + auto r = fn(p...); + if (back != 0xff && back != __bankof(fn)) + eflash.bank = back; + return r; +} + +#ifdef EFPROX_SECTION +#pragma code(code) +#endif + +template +class EFlashCall +{ +public: + template + __forceinline auto operator()(P ... p) const + { + switch(__bankof(0)) + { +#for(i,64) case i: return ef_call_p(p...); + default: + return ef_call_p<0xff, fn, P...>(p...); + } + } +}; + +#define EF_CALL(fn) EFlashCall fn + +#endif + +#endif + diff --git a/oscar64/include/c64/flossiec.c b/oscar64/include/c64/flossiec.c new file mode 100644 index 0000000..c188941 --- /dev/null +++ b/oscar64/include/c64/flossiec.c @@ -0,0 +1,608 @@ +#include "flossiec.h" +#include +#include +#include +#include + +#ifndef FLOSSIEC_NODISPLAY +#define FLOSSIEC_NODISPLAY 0 +#endif +#ifndef FLOSSIEC_NOIRQ +#define FLOSSIEC_NOIRQ 0 +#endif +#ifndef FLOSSIEC_BORDER +#define FLOSSIEC_BORDER 0 +#endif + + +#define VIA_ATNIN 0x80 +#define VIA_ATNOUT 0x10 + +#define VIA_CLKOUT 0x08 +#define VIA_DATAOUT 0x02 + +#define VIA_CLKIN 0x04 +#define VIA_DATAIN 0x01 + +#define PORTB1 0x1800 +#define PORTB2 0x1c00 + +#define WR 0x1d + +#ifdef FLOSSIEC_CODE +#pragma code(FLOSSIEC_CODE) +#endif +#ifdef FLOSSIEC_BSS +#pragma bss(FLOSSIEC_BSS) +#endif + +__asm diskcode +{ + nop + nop + + lda #VIA_CLKOUT + sta PORTB1 + + lda 0x0202 + sta 0x0c + lda 0x0203 + sta 0x0d + lda #$80 + sta 0x03 + + ldx #0 +l0: + txa + lsr + lsr + lsr + lsr + sta 0x0700,x + inx + bne l0 + +lr: + lda 0x03 + bmi lr + + sei + + ldx #0 +l2: + lda #0 + sta PORTB1 + lda 0x0600, x + tay + and #0x0f + ora #VIA_DATAIN +l1: + bit PORTB1 + bne l1 + +l3: + sta PORTB1 + + tya + asl + and #0x0a + sta PORTB1 + + lda 0x0700,y + nop + sta PORTB1 + + asl + nop + and #0x0a + sta PORTB1 + + inx + bne l2 + + lda #VIA_CLKOUT + sta PORTB1 + + + lda 0x0600 + beq w1 + sta 0x0c + lda 0x0601 + sta 0x0d + lda #$80 + sta 0x03 + cli + bne lr +w1: + sta PORTB1 + + cli + rts +} + +#define CIA2B_ATNOUT 0x08 +#define CIA2B_CLKOUT 0x10 +#define CIA2B_DATAOUT 0x20 + +#define CIA2B_CLKIN 0x40 +#define CIA2B_DATAIN 0x80 + +#define CIA2PRA 0xdd00 + +static char remap[256]; +static char rbuffer[256]; +static char xbuffer[256]; +static char flpos; +static char xcmd; +static char xi, xj; +static char fldrive; +static char flvxor; + +__noinline void fl_read_buf(void) +{ + __asm + { +#if FLOSSIEC_NOIRQ + php + sei +#endif + + lda CIA2PRA + and #~CIA2B_CLKOUT + sta accu + sta CIA2PRA + and #~CIA2B_DATAOUT + sta accu + 1 + + l0: + lda CIA2PRA + and #CIA2B_CLKIN + beq l0 +#if !FLOSSIEC_NOIRQ + php + pla + and #$04 + beq iq +#endif + ldy #0 + sec + l1: + ldx accu + 1 +#if !FLOSSIEC_NODISPLAY + l2: + lda 0xd012 + sbc #50 + bcc w1 + and #7 + beq l2 +#endif + w1: + stx CIA2PRA + +#if FLOSSIEC_BORDER + inc 0xd020 +#else + nop + nop + nop +#endif + ldx accu + nop + + lda CIA2PRA + lsr + lsr + nop + eor CIA2PRA + lsr + lsr + nop + eor CIA2PRA + lsr + lsr + sec + eor CIA2PRA + stx CIA2PRA + + sta rbuffer, y + iny + bne l1 + jmp done +#if !FLOSSIEC_NOIRQ + iq: + ldy #0 + sec + l1i: + ldx accu + 1 + l2i: + cli + sei +#if !FLOSSIEC_NODISPLAY + lda 0xd012 + sbc #50 + bcc w1i + and #7 + beq l2i + w1i: +#endif + stx CIA2PRA + +#if FLOSSIEC_BORDER + inc 0xd020 +#else + nop + nop + nop +#endif + ldx accu + nop + + lda CIA2PRA + lsr + lsr + nop + eor CIA2PRA + lsr + lsr + nop + eor CIA2PRA + lsr + lsr + sec + eor CIA2PRA + stx CIA2PRA + + sta rbuffer, y + iny + bne l1i + cli +#endif + done: + +#if FLOSSIEC_NOIRQ + plp +#endif + } +} + + + +inline char flossiec_get(void) +{ + if (!flpos) + { + fl_read_buf(); + flpos = 2; + } + return remap[rbuffer[flpos++]]; +} + +void flossiec_decompress(void) +{ + char i = 0, j = xj, cmd = xcmd; + xi = 0; + + for(;;) + { + if (cmd & 0x80) + { + if (i < cmd) + { + char t = i - cmd; + do { + char ch = xbuffer[j++]; + xbuffer[i++] = ch; + } while (i != t); + cmd = 0; + } + else + { + cmd -= i; + do { + char ch = xbuffer[j++]; + xbuffer[i++] = ch; + } while (i); + break; + } + } + else + { + char ch = flossiec_get(); + if (cmd) + { + xbuffer[i++] = ch; + cmd--; + if (!i) + break; + } + else + { + cmd = ch; + if (!cmd) + break; + if (cmd & 0x80) + { + cmd ^= 0x7f; + cmd++; + j = i - flossiec_get(); + } + } + } + } + xj = j; + xcmd = cmd; +} + +inline char flossiec_get_lzo(void) +{ + if (!xi) + flossiec_decompress(); + return xbuffer[xi++]; +} + +inline bool flossiec_eof(void) +{ + return !remap[rbuffer[0]] && flpos >= remap[rbuffer[1]]; +} + +char * flossiec_read(char * dp, unsigned size) +{ + while (size) + { + *dp++ = flossiec_get(); + size--; + } + + return dp; +} + +char * flossiec_read_lzo(char * dp, unsigned size) +{ + char i = xi; + + dp -= i; + size += i; + + while (size) + { + if (!i) + flossiec_decompress(); + + if (size >= 256) + { + do { + dp[i] = xbuffer[i]; + i++; + } while (i); + dp += 256; + size -= 256; + } + else + { + do { + dp[i] = xbuffer[i]; + i++; + } while (i != (char)size); + dp += i; + break; + } + } + + xi = i; + + return dp; +} + +static void vxorcheck(void) +{ + char vxor = cia2.pra & 7; + vxor ^= vxor >> 2; + vxor ^= 0xff; + + if (vxor != flvxor) + { + flvxor = vxor; + + for(int i=0; i<256; i++) + { + char j = i ^ vxor; + char d = ((j & 0x11) << 3) | + (j & 0x66) | + ((j & 0x88) >> 3); + remap[i] = d; + } + } +} + +bool flossiec_init(char drive) +{ + fldrive = drive; + flvxor = 0; + + iec_open(drive, 2, "#2"); + iec_listen(drive, 2); + for(char j=0; j<127; j++) + iec_write(((char *)diskcode)[j]); + iec_unlisten(); + + iec_close(drive, 2); + + iec_open(drive, 15, ""); + + return true; +} + +void flossiec_shutdown(void) +{ + iec_close(fldrive, 15); +} + + +bool flossiec_open(char track, char sector) +{ + iec_listen(fldrive, 15); + iec_write(P'U'); + iec_write(P'4'); + iec_write(track); + iec_write(sector); + iec_unlisten(); + + cia2.pra |= CIA2B_DATAOUT; + + +#if FLOSSIEC_NODISPLAY + vic.ctrl1 &= ~VIC_CTRL1_DEN; +#endif + + vic_waitFrame(); + vxorcheck(); + vic_waitFrame(); + + flpos = 0; + xi = 0; + + return true; +} + +void flossiec_close(void) +{ + cia2.pra |= CIA2B_DATAOUT; + +#if FLOSSIEC_NODISPLAY + vic.ctrl1 |= VIC_CTRL1_DEN; +#endif +} + +bool flosskio_init(char drive) +{ + fldrive = drive; + flvxor = 0; + + krnio_setnam_n("#2", 2); + krnio_open(2, drive, 2); + krnio_write(2, (char *)diskcode, 128); + krnio_close(2); + + krnio_setnam_n(nullptr, 0); + krnio_open(15, drive, 15); + + return true; +} + +void flosskio_shutdown(void) +{ + krnio_close(15); +} + + +bool flosskio_open(char track, char sector) +{ + krnio_chkout(15); + krnio_chrout(P'U'); + krnio_chrout(P'4'); + krnio_chrout(track); + krnio_chrout(sector); + krnio_clrchn(); + + cia2.pra |= CIA2B_DATAOUT; + +#if FLOSSIEC_NODISPLAY + vic.ctrl1 &= ~VIC_CTRL1_DEN; +#endif + + vic_waitFrame(); + vxorcheck(); + vic_waitFrame(); + + flpos = 0; + xi = 0; + + return true; +} + +void flosskio_close(void) +{ + cia2.pra |= CIA2B_DATAOUT; + +#if FLOSSIEC_NODISPLAY + vic.ctrl1 |= VIC_CTRL1_DEN; +#endif +} + +static bool mapdir(const char * fnames, floss_blk * blks) +{ + do { + fl_read_buf(); + + char si = 0; + do + { + if (remap[rbuffer[si + 2]] == 0x82) + { + char fname[17]; + char j = 0; + while (j < 16 && remap[rbuffer[si + j + 5]] != 0xa0) + { + fname[j] = remap[rbuffer[si + j + 5]]; + j++; + } + fname[j] = 0; + + char sj = 0; + char k = 0; + while (fnames[sj]) + { + j = 0; + while (fname[j] && fname[j] == fnames[sj]) + { + j++; + sj++; + } + if (!fname[j] && (!fnames[sj] || fnames[sj] == ',')) + { + __assume(k < 128); + blks[k].track = remap[rbuffer[si + 3]]; + blks[k].sector = remap[rbuffer[si + 4]]; + break; + } + + while (fnames[sj] && fnames[sj++] != ',') + ; + k++; + } + } + + si += 32; + } while (si); + + } while (remap[rbuffer[0]]); + + return true; +} + +bool flosskio_mapdir(const char * fnames, floss_blk * blks) +{ + if (flosskio_open(18, 1)) + { + mapdir(fnames, blks); + + flosskio_close(); + return true; + } + + return false; +} + +bool flossiec_mapdir(const char * fnames, floss_blk * blks) +{ + if (flossiec_open(18, 1)) + { + mapdir(fnames, blks); + + flossiec_close(); + return true; + } + + return false; +} + diff --git a/oscar64/include/c64/flossiec.h b/oscar64/include/c64/flossiec.h new file mode 100644 index 0000000..78796b0 --- /dev/null +++ b/oscar64/include/c64/flossiec.h @@ -0,0 +1,79 @@ +#ifndef FLOSSIEC_H +#define FLOSSIEC_H + +// When building you can use various defines to change the behaviour + +// FLOSSIEC_BORDER=1 Enable border flashing while loading +// FLOSSIEC_NODISPLAY=1 Disable the display while loading +// FLOSSIEC_NOIRQ=1 Disable IRQ during load +// FLOSSIEC_CODE=cseg Code segment to be used, when defined +// FLOSSIEC_BSS=bseg BSS segment to be used, when defined + +// Initialize the fastloader to be used without the kernal +bool flossiec_init(char drive); + +// Shutdown the fastloader when used without the kernal +void flossiec_shutdown(void); + +// Open a file for read with the fastloader without the kernal. +// The file has to be read to completion before you can close +// it again, +bool flossiec_open(char track, char sector); + +// Close a file after reading +void flossiec_close(void); + + +// Initialize the fastloader to be used with the kernal +bool flosskio_init(char drive); + +// Shutdown the fastloader when used with the kernal +void flosskio_shutdown(void); + + +// Open a file for read with the fastloader with the kernal +// The file has to be read to completion before you can close +// it again, +bool flosskio_open(char track, char sector); + +// Close a file after reading +void flosskio_close(void); + + +// Track and sector start of a file +struct floss_blk +{ + char track, sector; +}; + +// Map a comma separated list of filenames to an array of +// block start positions by reading the directory, using the +// kernal. +bool flosskio_mapdir(const char * fnames, floss_blk * blks); + +// Map a comma separated list of filenames to an array of +// block start positions by reading the directory, without the +// kernal. +bool flossiec_mapdir(const char * fnames, floss_blk * blks); + +// Check for end of file while reading +inline bool flossiec_eof(void); + +// Get one char from uncompressed file +inline char flossiec_get(void); + +// Get one char from compressed file +inline char flossiec_get_lzo(void); + +// Read a section of a file into memory up to size bytes, +// returns the first address after the read +char * flossiec_read(char * dp, unsigned size); + +// Read and expand section of a file into memory up to size +// bytes, returns the first address after the read +char * flossiec_read_lzo(char * dp, unsigned size); + + +#pragma compile("flossiec.c") + +#endif diff --git a/oscar64/include/c64/iecbus.c b/oscar64/include/c64/iecbus.c new file mode 100644 index 0000000..60e904a --- /dev/null +++ b/oscar64/include/c64/iecbus.c @@ -0,0 +1,353 @@ +#include "iecbus.h" +#include +#include + +IEC_STATUS iec_status; +char iec_queue; + +#define CIA2B_ATNOUT 0x08 +#define CIA2B_CLKOUT 0x10 +#define CIA2B_DATAOUT 0x20 + +#define CIA2B_CLKIN 0x40 +#define CIA2B_DATAIN 0x80 + +#pragma optimize(push) +#pragma optimize(1) + +// multiples of 5us +static void delay(char n) +{ + __asm { + ldx n + l1: + dex + bne l1 + } +} + +static inline void data_true(void) +{ + cia2.pra &= ~CIA2B_DATAOUT; +} + +static inline void data_false(void) +{ + cia2.pra |= CIA2B_DATAOUT; +} + +static inline void clock_true(void) +{ + cia2.pra &= ~CIA2B_CLKOUT; +} + +static inline void cdata_true(void) +{ + cia2.pra &= ~(CIA2B_CLKOUT | CIA2B_DATAOUT); +} + +static inline void clock_false(void) +{ + cia2.pra |= CIA2B_CLKOUT; +} + +static inline void atn_true(void) +{ + cia2.pra &= ~CIA2B_ATNOUT; +} + +static inline void atn_false(void) +{ + cia2.pra |= CIA2B_ATNOUT; +} + +static inline bool data_in(void) +{ + return (cia2.pra & CIA2B_DATAIN) != 0; +} + +static inline bool clock_in(void) +{ + return (cia2.pra & CIA2B_CLKIN) != 0; +} + +static bool data_check(void) +{ + char cnt = 200; + while (cnt > 0 && data_in()) + { + delay(5); + cnt--; + } + + if (cnt) + return true; + else + { + iec_status = IEC_DATA_CHECK; + return false; + } +} + +static bool iec_eoib(void) +{ + clock_true(); + + while (!data_in()); + + delay(40); + + return data_check(); +} + +static void iec_writeb(char b) +{ + clock_true(); + + while (!data_in()); + + delay(5); + for(char i=0; i<8; i++) + { + clock_false(); + delay(5); + if (b & 1) + data_true(); + else + data_false(); + clock_true(); + b >>= 1; + delay(5); + } + clock_false(); + data_true(); +} + +bool iec_write(char b) +{ + if (iec_status == IEC_QUEUED) + { + __asm + { + php + sei + } + + iec_status = IEC_OK; + iec_writeb(iec_queue); + + __asm + { + plp + } + + data_check(); + } + if (iec_status < IEC_ERROR) + { + iec_queue = b; + iec_status = IEC_QUEUED; + return true; + } + + return false; +} + +char iec_read(void) +{ + while (!clock_in()); + + __asm + { + php + sei + } + + data_true(); + + char cnt = 100; + while (cnt > 0 && clock_in()) + cnt--; + + if (cnt == 0) + { + iec_status = IEC_EOF; + data_false(); + delay(10); + data_true(); + + cnt = 200; + while (cnt > 0 && clock_in()) + cnt--; + + if (cnt == 0) + { + iec_status = IEC_TIMEOUT; + + __asm + { + plp + } + + return 0; + } + } + + char b = 0; + for(char i=0; i<8; i++) + { + char c; + while (!((c = cia2.pra) & CIA2B_CLKIN)) + ; + + b >>= 1; + b |= c & 0x80; + + while (cia2.pra & CIA2B_CLKIN) + ; + } + + data_false(); + + __asm + { + plp + } + + return b; +} + +void iec_atn(char dev, char sec) +{ + clock_true(); + data_true(); + atn_false(); + clock_false(); + + delay(200); + + while (data_in()); + + iec_writeb(dev); + data_check(); + if (sec != 0xff) + { + iec_writeb(sec); + data_check(); + } + + atn_true(); +} + + +void iec_talk(char dev, char sec) +{ + iec_status = IEC_OK; + + iec_atn(dev | 0x40, sec | 0x60); + data_false(); + + __asm + { + php + sei + } + + clock_true(); + char cnt = 200; + while (cnt > 0 && clock_in()) + cnt--; + + __asm + { + plp + } + + delay(10); +} + +void iec_untalk(void) +{ + iec_atn(0x5f, 0xff); +} + +void iec_listen(char dev, char sec) +{ + iec_status = IEC_OK; + + iec_atn(dev | 0x20, sec | 0x60); +} + +void iec_unlisten(void) +{ + __asm + { + php + sei + } + + if (iec_status == IEC_QUEUED) + { + iec_status = IEC_OK; + iec_eoib(); + iec_writeb(iec_queue); + data_check(); + } + + iec_atn(0x3f, 0xff); + clock_true(); + + __asm + { + plp + } +} + +void iec_open(char dev, char sec, const char * fname) +{ + iec_status = IEC_OK; + + iec_atn(dev | 0x20, sec | 0xf0); + + char i = 0; + while (fname[i]) + { + iec_write(fname[i]); + i++; + } + iec_unlisten(); +} + +void iec_close(char dev, char sec) +{ + iec_atn(dev | 0x20, sec | 0xe0); + iec_unlisten(); +} + +int iec_write_bytes(const char * data, int num) +{ + for(int i=0; ia // readst + BANKOUT +#endif + sta accu + lda #0 + sta accu + 1 + }; +} + +#pragma native(krnio_status) + + +BANKINLINE bool krnio_load(char fnum, char device, char channel) +{ + return char(__asm + { + BANKIN + lda fnum + ldx device + ldy channel + jsr $ffba // setlfs + + lda #0 + ldx #0 + ldy #0 + jsr $FFD5 // load + BANKOUT + + lda #0 + rol + eor #1 + sta accu + }); +} + +#pragma native(krnio_load) + +BANKINLINE bool krnio_save(char device, const char* start, const char* end) +{ + return char(__asm + { + BANKIN + lda #0 + ldx device + ldy #0 + jsr $ffba // setlfs + + lda #start + ldx end + ldy end+1 + jsr $FFD8 // save + + BANKOUT + + lda #0 + rol + eor #1 + sta accu + }); +} + +#pragma native(krnio_save) + +BANKINLINE bool krnio_chkout(char fnum) +{ + return char(__asm + { + BANKIN + ldx fnum + jsr $ffc9 : x->ax // chkout +#if defined(__CBMPET__) + jsr k_checkst +#endif + BANKOUT + + lda #0 + rol + eor #1 + sta accu + }); +} + +#pragma native(krnio_chkout) + +BANKINLINE bool krnio_chkin(char fnum) +{ + return char(__asm + { + BANKIN + ldx fnum + jsr $ffc6 : x->axy // chkin +#if defined(__CBMPET__) + jsr k_checkst +#endif + BANKOUT + + lda #0 + rol + eor #1 + sta accu + }); +} + +#pragma native(krnio_chkin) + +BANKINLINE void krnio_clrchn(void) +{ + __asm + { + BANKIN + jsr $ffcc : ->ax // clrchn + BANKOUT + } +} + +#pragma native(krnio_clrchn) + +BANKINLINE bool krnio_chrout(char ch) +{ + return char(__asm + { + BANKIN + lda ch + jsr $ffd2 : a->a // chrout + sta accu + BANKOUT + }); +} + +#pragma native(krnio_chrout) + +BANKINLINE char krnio_chrin(void) +{ + return __asm + { + BANKIN + jsr $ffcf : a->a // chrin + sta accu + BANKOUT + }; +} + +#pragma native(krnio_chrin) + +#if defined(__PLUS4__) +#pragma code(code) +#endif + +int krnio_getch(char fnum) +{ + if (krnio_pstatus[fnum] == KRNIO_EOF) + return -1; + + int ch = -1; + if (krnio_chkin(fnum)) + { + ch = krnio_chrin(); + krnioerr err = krnio_status(); + krnio_pstatus[fnum] = err; + if (err) + { + if (err == KRNIO_EOF) + ch |= 0x100; + else + ch = -1; + } + } + krnio_clrchn(); + return ch; +} + +int krnio_putch(char fnum, char ch) +{ + if (krnio_chkout(fnum)) + { + krnio_chrout(ch); + krnio_clrchn(); + return 0; + } + else + return -1; +} + + +int krnio_puts(char fnum, const char * data) +{ + if (krnio_chkout(fnum)) + { + int i = 0; + while (data[i]) + krnio_chrout(data[i++]); + krnio_clrchn(); + return i; + } + else + return -1; +} + +#pragma native(krnio_puts) + +int krnio_write(char fnum, const char * data, int num) +{ + if (krnio_chkout(fnum)) + { + for(int i=0; i', '[', '@', '<', + 0, 0, ']', KEY_CLR, 0, 0, '^', '?', + '!', 0, 0, '"', ' ', 0, 'Q', KEY_ESC, + +}; + + +byte keyb_matrix[8]; + +KeyScanCode keyb_key; +static byte keyb_pmatrix[8]; + +bool key_pressed(KeyScanCode code) +{ + return !(keyb_matrix[code >> 3] & (1 << (code & 7))); +} + +bool key_shift(void) +{ + return + !(keyb_matrix[6] & 0x10) || + !(keyb_matrix[1] & 0x80); +} + +void keyb_poll(void) +{ + cia1.ddra = 0xff; + cia1.pra = 0xff; + keyb_key = 0x00; + + if (cia1.prb == 0xff) + { + cia1.ddrb = 0x00; + cia1.pra = 0x00; + + if (cia1.prb != 0xff) + { + keyb_matrix[6] &= 0xef; + keyb_matrix[1] &= 0x7f; + + byte a = 0xfe; + for(byte i=0; i<8; i++) + { + cia1.pra = a; + a = (a << 1) | 1; + + byte p = keyb_matrix[i]; + byte k = cia1.prb; + keyb_matrix[i] = k; + + k = (k ^ 0xff) & p; + if (k) + { + byte j = 8 * i | 0x80; + if (k & 0xf0) + j += 4; + if (k & 0xcc) + j += 2; + if (k & 0xaa) + j++; + keyb_key = j; + } + } + + if (keyb_key && (!(keyb_matrix[1] & 0x80) || (!(keyb_matrix[6] & 0x10)))) + keyb_key |= 0x40; + } + else + { + keyb_matrix[0] = 0xff; + keyb_matrix[1] = 0xff; + keyb_matrix[2] = 0xff; + keyb_matrix[3] = 0xff; + keyb_matrix[4] = 0xff; + keyb_matrix[5] = 0xff; + keyb_matrix[6] = 0xff; + keyb_matrix[7] = 0xff; + } + } + + cia1.pra = ciaa_pra_def; +} diff --git a/oscar64/include/c64/keyboard.h b/oscar64/include/c64/keyboard.h new file mode 100644 index 0000000..bf03122 --- /dev/null +++ b/oscar64/include/c64/keyboard.h @@ -0,0 +1,134 @@ +#ifndef C64_KEYBOARD_H +#define C64_KEYBOARD_H + +#include "types.h" + +#define KEY_CSR_DOWN (17) +#define KEY_CSR_RIGHT (29) +#define KEY_CSR_UP (17 + 128) +#define KEY_CSR_LEFT (29 + 128) + +#define KEY_ARROW_LEFT (95) + +#define KEY_ESC (27) +#define KEY_DEL (20) +#define KEY_INST (148) +#define KEY_RETURN (13) + +#define KEY_HOME (19) +#define KEY_CLR (147) + +#define KEY_F1 (133) +#define KEY_F3 (134) +#define KEY_F5 (135) +#define KEY_F7 (136) + +#define KEY_F2 (137) +#define KEY_F4 (138) +#define KEY_F6 (139) +#define KEY_F8 (140) + +enum KeyScanCode +{ + KSCAN_DEL, + KSCAN_RETURN, + KSCAN_CSR_RIGHT, + KSCAN_F7, + KSCAN_F1, + KSCAN_F3, + KSCAN_F5, + KSCAN_CSR_DOWN, + + KSCAN_3, + KSCAN_W, + KSCAN_A, + KSCAN_4, + KSCAN_Z, + KSCAN_S, + KSCAN_E, + KSCAN_SHIFT_LOCK, + + KSCAN_5, + KSCAN_R, + KSCAN_D, + KSCAN_6, + KSCAN_C, + KSCAN_F, + KSCAN_T, + KSCAN_X, + + KSCAN_7, + KSCAN_Y, + KSCAN_G, + KSCAN_8, + KSCAN_B, + KSCAN_H, + KSCAN_U, + KSCAN_V, + + KSCAN_9, + KSCAN_I, + KSCAN_J, + KSCAN_0, + KSCAN_M, + KSCAN_K, + KSCAN_O, + KSCAN_N, + + KSCAN_PLUS, + KSCAN_P, + KSCAN_L, + KSCAN_MINUS, + KSCAN_DOT, + KSCAN_COLON, + KSCAN_AT, + KSCAN_COMMA, + + KSCAN_POUND, + KSCAN_STAR, + KSCAN_SEMICOLON, + KSCAN_HOME, + KSCAN_RSHIFT, + KSCAN_EQUAL, + KSCAN_ARROW_UP, + KSCAN_SLASH, + + KSCAN_1, + KSCAN_ARROW_LEFT, + KSCAN_CONTROL, + KSCAN_2, + KSCAN_SPACE, + KSCAN_COMMODORE, + KSCAN_Q, + KSCAN_STOP, + + KSCAN_QUAL_SHIFT = 0x40, + KSCAN_QUAL_MASK = 0x7f, + KSCAN_QUAL_DOWN = 0x80, + + KSCAN_MAX = 0xff +}; + +// map of keyboard codes to PETSCII, first 64 without shift +// second 64 with shift + +extern const char keyb_codes[128]; + +// current status of key matrix +extern byte keyb_matrix[8]; + +// current key in scan code - the top level bit KSCAN_QUAL_DOWN is +// used to indicate a key is pressed, so 0 is no key +extern KeyScanCode keyb_key; + +// poll keyboard matrix + +void keyb_poll(void); + +inline bool key_pressed(KeyScanCode code); + +inline bool key_shift(void); + +#pragma compile("keyboard.c") + +#endif diff --git a/oscar64/include/c64/memmap.c b/oscar64/include/c64/memmap.c new file mode 100644 index 0000000..7025d89 --- /dev/null +++ b/oscar64/include/c64/memmap.c @@ -0,0 +1,82 @@ +// This code is meant to keep the C64 running while the program it is part of banks +// out the BASIC or Kernal ROM to have RAM available. If an IRQ or NMI occurs during +// that time and a ROM routine is called that isn't there, the C64 will crash. +// This code replaces the IRQ and NMI handling code to bank the BASIC and Kernal ROMs +// back in, call the original handling routines, and then restore the situation as it +// was when the interrupt happened. + +#include "memmap.h" + +__asm DoneTrampoline +{ + stx $01 // The ROM code at jmp ($fffa) has saved our X value + // so we can restore it to $01. Our banks our back to whatever it was + pla // now we pull X and A and restore them + tax + pla + rti // RTI can now pull the original status byte and return address and + // return to the original code. +} + +__asm IRQTrampoline +{ + pha + txa + pha + + lda #>DoneTrampoline + pha + lda #DoneTrampoline // $01fa save the high byte of DoneTrampoline() + pha + lda # default power on config +// MMAP_NO_BASIC : I/O + KERNAL -> easy extra chunk of contiguous RAM +// MMAP_NO_ROM : I/O -> I/O and COLOR RAM in $d000-$dfff block, rest is RAM +// MMAP_RAM : -> ALL RAM, you'll need to manage some state switching... +// MMAP_CHAR_ROM : CHAR -> no BASIC or KERNAL or I/O, but can copy CHAR ROM +// MMAP_ALL_ROM : BASIC + CHAR + KERNAL -> All ROM functions available, but no I/O + +#define MMAP_ROM 0x37 +#define MMAP_NO_BASIC 0x36 +#define MMAP_NO_ROM 0x35 +#define MMAP_RAM 0x30 +#define MMAP_CHAR_ROM 0x31 +#define MMAP_ALL_ROM 0x33 + +// Install an IRQ an NMI trampoline, that routes the kernal interrupts +// through an intermediate trampoline when the kernal ROM is not paged +// in. The trampoline enables the ROM, executes the interrupt and +// restores the memory map setting before returning. + +void mmap_trampoline(void); + +// Set the memory map in a way that is compatible with the IRQ +// trampoline, returns the previous state + +inline char mmap_set(char pla); + +#pragma compile("memmap.c") + +#endif diff --git a/oscar64/include/c64/mouse.c b/oscar64/include/c64/mouse.c new file mode 100644 index 0000000..70a7078 --- /dev/null +++ b/oscar64/include/c64/mouse.c @@ -0,0 +1,52 @@ +#include "mouse.h" +#include "sid.h" +#include "cia.h" + +sbyte mouse_dx, mouse_dy; +bool mouse_lb, mouse_rb; +static char mouse_px, mouse_py; +static char mouse_port; + +inline signed char dpos(char * old, char mnew) +{ + mnew = (mnew & 0x7f) >> 1; + + char diff = (mnew - *old) & 0x3f; + + if (diff >= 0x20) + { + *old = mnew; + return diff | 0xe0; + } + else if (diff) + { + *old = mnew; + return diff; + } + + return 0; +} + +void mouse_poll(void) +{ + char b = ((volatile char *)0xdc00)[mouse_port]; + mouse_rb = (b & 0x01) == 0; + mouse_lb = (b & 0x10) == 0; + + char x = sid.potx, y = sid.poty; + + mouse_dx = dpos(&mouse_px, x); + mouse_dy = dpos(&mouse_py, y); +} + +void mouse_arm(char n) +{ + mouse_port = n; + cia1.pra = ciaa_pra_def = n ? 0x7f : 0xbf; +} + +void mouse_init(void) +{ + mouse_arm(1); + mouse_poll(); +} diff --git a/oscar64/include/c64/mouse.h b/oscar64/include/c64/mouse.h new file mode 100644 index 0000000..144761b --- /dev/null +++ b/oscar64/include/c64/mouse.h @@ -0,0 +1,26 @@ +#ifndef C64_MOUSE_H +#define C64_MOUSE_H + +#include "types.h" + +extern sbyte mouse_dx, mouse_dy; +extern bool mouse_lb, mouse_rb; + + +void mouse_init(void); + +// arm the potentiometer input for the selected mouse input +// needs ~4ms to stabilize + +void mouse_arm(char n); + +// poll mouse input for selected mouse, but the relative +// movement into mouse_dx/dy and the button state into +// mouse_lb/mouse_rb + +void mouse_poll(void); + +#pragma compile("mouse.c") + +#endif + diff --git a/oscar64/include/c64/rasterirq.c b/oscar64/include/c64/rasterirq.c new file mode 100644 index 0000000..57b8a4f --- /dev/null +++ b/oscar64/include/c64/rasterirq.c @@ -0,0 +1,716 @@ +#include "rasterirq.h" + +#include +#include +#include +#include + +volatile byte rirq_count; +static byte rirq_pcount; + +byte rasterIRQRows[NUM_IRQS + 1]; +byte rasterIRQIndex[NUM_IRQS + 1]; // Sort order of interrupt index, offset by one +#ifdef ZPAGE_IRQS +__zeropage +#endif +byte rasterIRQNext[NUM_IRQS + 1]; // Rasterline of interrupt, terminated by 0xff +byte rasterIRQLow[NUM_IRQS]; // Address of interrupt code +byte rasterIRQHigh[NUM_IRQS]; + +#ifdef ZPAGE_IRQS +__zeropage +#endif +volatile byte nextIRQ; + +// nextIRQ is the index of the next expected IRQ, or $ff if no IRQ is scheduled + +__asm rirq_isr_ram_io +{ + stx plrx + 1 + + ldx nextIRQ + bmi exi + + sta plra + 1 + sty plry + 1 + +l1: + lda rasterIRQNext, x + ldy rasterIRQIndex + 1, x + ldx rasterIRQLow, y + stx ji + 1 + ldx rasterIRQHigh, y + stx ji + 2 + +ji: + jsr $0000 + + inc nextIRQ + ldx nextIRQ + + ldy rasterIRQNext, x + + asl $d019 + + cpy #$ff + beq e2 + + dey + sty $d012 + dey + cpy $d012 + bcc l1 + +plry: + ldy #0 +plra: + lda #0 +plrx: + ldx #0 + rti + +exi: + asl $d019 + jmp plrx + + // No more interrupts to service +e2: + inc rirq_count + + ldy rasterIRQNext + dey + sty $d012 + ldx #0 + stx nextIRQ + beq plry +} + +__asm rirq_isr_io +{ + pha + txa + pha + tya + pha +kentry: + + ldx nextIRQ + bmi exi +l1: + lda rasterIRQNext, x + ldy rasterIRQIndex + 1, x + ldx rasterIRQLow, y + stx ji + 1 + ldx rasterIRQHigh, y + stx ji + 2 + +ji: + jsr $0000 + + inc nextIRQ + ldx nextIRQ + + ldy rasterIRQNext, x + + asl $d019 + + cpy #$ff + beq e2 + + dey + sty $d012 + dey + cpy $d012 + bcc l1 + +exd: + pla + tay + pla + tax + pla + rti + +exi: + asl $d019 + jmp exd + +e2: + inc rirq_count + + ldy rasterIRQNext + dey + sty $d012 + ldx #0 + stx nextIRQ + beq exd +} + +__asm rirq_isr_noio +{ + pha + txa + pha + tya + pha +kentry: + lda $01 + pha + + lda #$35 + sta $01 + + ldx nextIRQ + bmi exi +l1: + lda rasterIRQNext, x + ldy rasterIRQIndex + 1, x + ldx rasterIRQLow, y + stx ji + 1 + ldx rasterIRQHigh, y + stx ji + 2 + +ji: + jsr $0000 + + inc nextIRQ + ldx nextIRQ + + ldy rasterIRQNext, x + + asl $d019 + + cpy #$ff + beq e2 + + dey + sty $d012 + dey + cpy $d012 + bcc l1 + +exd: + pla + sta $01 + + pla + tay + pla + tax + pla + rti + +exi: + asl $d019 + jmp exd + +e2: + inc rirq_count + + ldy rasterIRQNext + dey + sty $d012 + ldx #0 + stx nextIRQ + beq exd +} + +__asm rirq_isr_kernal_io +{ + lda $d019 + bpl ex2 + + ldx nextIRQ + bmi exi +l1: + lda rasterIRQNext, x + ldy rasterIRQIndex + 1, x + ldx rasterIRQLow, y + stx ji + 1 + ldx rasterIRQHigh, y + stx ji + 2 + +ji: + jsr $0000 +jx: + + inc nextIRQ + ldx nextIRQ + + ldy rasterIRQNext, x + + asl $d019 + + cpy #$ff + beq e2 + + dey + dey + sty $d012 + dey + cpy $d012 + bcc l1 + +exd: + jmp $ea81 + +exi: + asl $d019 + jmp $ea81 + +e2: + inc rirq_count + + ldy rasterIRQNext + dey + dey + sty $d012 + ldx #0 + stx nextIRQ + jmp $ea81 + +ex2: + LDA $DC0D + cli + jmp $ea31 +} + +__asm rirq_isr_kernal_noio +{ + lda $01 + pha + lda #$36 + sta $01 + + lda $d019 + bpl ex2 + + ldx nextIRQ + bmi exi +l1: + lda rasterIRQNext, x + ldy rasterIRQIndex + 1, x + ldx rasterIRQLow, y + stx ji + 1 + ldx rasterIRQHigh, y + stx ji + 2 + +ji: + jsr $0000 +jx: + + inc nextIRQ + ldx nextIRQ + + ldy rasterIRQNext, x + + asl $d019 + + cpy #$ff + beq e2 + + dey + dey + sty $d012 + dey + cpy $d012 + bcc l1 +exd: + pla + sta $01 + + jmp $ea81 + +exi: + asl $d019 + jmp exd + +e2: + inc rirq_count + + ldy rasterIRQNext + dey + dey + sty $d012 + ldx #0 + stx nextIRQ + beq exd + +ex2: + LDA $DC0D + cli + pla + sta $01 + + jmp $ea31 +} + +// 0 lda #data0 +// 2 ldy #data1 +// 4 cpx $d012 +// 7 bcc -5 +// 9 sta addr0 +// 12 sty addr1 +// 15 lda #data2 +// 17 sta addr2 +// 20 lda #data3 +// 22 sta addr3 +// ... +// rts + +void rirq_build(RIRQCode * ic, byte size) +{ + __assume(size < 26); + + ic->size = size; + + asm_im(ic->code + 0, ASM_LDY, 0); + asm_im(ic->code + 2, ASM_LDX, 0); + asm_ab(ic->code + 4, ASM_CMP, 0xd012); + asm_rl(ic->code + 7, ASM_BCS, -5); + asm_ab(ic->code + 9, ASM_STY, 0x0000); + + if (size == 0) + { + asm_np(ic->code + 0, ASM_RTS); + } + else if (size == 1) + { + asm_np(ic->code + 12, ASM_RTS); + } + else + { + asm_ab(ic->code + 12, ASM_STX, 0x0000); + + byte p = 15; + for(byte i=2; icode + p, ASM_LDA, 0x00); + p += asm_ab(ic->code + p, ASM_STA, 0x0000); + } + asm_np(ic->code + p, ASM_RTS); + } +} + +RIRQCode * rirq_alloc(byte size) +{ + RIRQCode * ic = (RIRQCode *)malloc(1 + RIRQ_SIZE + 5 * size); + rirq_build(ic, size); + return ic; +} + +#pragma native(rirq_build) + +void rirq_set(byte n, byte row, RIRQCode * write) +{ + rasterIRQLow[n] = (unsigned)&write->code & 0xff; + rasterIRQHigh[n] = (unsigned)&write->code >> 8; + + rasterIRQRows[n] = row; +} + +static const byte irqai[26] = { + RIRQ_ADDR_0, RIRQ_ADDR_1, RIRQ_ADDR_2, RIRQ_ADDR_3, RIRQ_ADDR_4, RIRQ_ADDR_5, RIRQ_ADDR_6, RIRQ_ADDR_7, + RIRQ_ADDR_8, RIRQ_ADDR_9, RIRQ_ADDR_10, RIRQ_ADDR_11, RIRQ_ADDR_12, RIRQ_ADDR_13, RIRQ_ADDR_14, RIRQ_ADDR_15, + RIRQ_ADDR_16, RIRQ_ADDR_17, RIRQ_ADDR_18, RIRQ_ADDR_19, RIRQ_ADDR_20, RIRQ_ADDR_21, RIRQ_ADDR_22, RIRQ_ADDR_23, + RIRQ_ADDR_24, RIRQ_ADDR_25 +}; + +static const byte irqdi[26] = { + RIRQ_DATA_0, RIRQ_DATA_1, RIRQ_DATA_2, RIRQ_DATA_3, RIRQ_DATA_4, RIRQ_DATA_5, RIRQ_DATA_6, RIRQ_DATA_7, + RIRQ_DATA_8, RIRQ_DATA_9, RIRQ_DATA_10, RIRQ_DATA_11, RIRQ_DATA_12, RIRQ_DATA_13, RIRQ_DATA_14, RIRQ_DATA_15, + RIRQ_DATA_16, RIRQ_DATA_17, RIRQ_DATA_18, RIRQ_DATA_19, RIRQ_DATA_20, RIRQ_DATA_21, RIRQ_DATA_22, RIRQ_DATA_23, + RIRQ_DATA_24, RIRQ_DATA_25 +}; + +void rirq_addr(RIRQCode * ic, byte n, void * addr) +{ + byte p = irqai[n]; + ((byte *)ic->code)[p + 0] = (unsigned)addr & 0xff; + ((byte *)ic->code)[p + 1] = (unsigned)addr >> 8; +} + +void rirq_addrhi(RIRQCode * ic, byte n, byte hi) +{ + byte p = irqai[n]; + ((byte *)ic->code)[p + 1] = hi; +} + +void rirq_data(RIRQCode * ic, byte n, byte data) +{ + byte p = irqdi[n]; +// ic->code[p] = data; + (volatile char *)(ic->code)[p] = data; +} + +void rirq_write(RIRQCode * ic, byte n, void * addr, byte data) +{ + byte p = irqai[n]; + ((byte *)ic->code)[p + 0] = (unsigned)addr & 0xff; + ((byte *)ic->code)[p + 1] = (unsigned)addr >> 8; + p = irqdi[n]; + ((byte *)ic->code)[p] = data; +} + +void rirq_call(RIRQCode * ic, byte n, void * addr) +{ + byte p = irqai[n]; + ((byte *)ic->code)[p - 1] = 0x20; + ((byte *)ic->code)[p + 0] = (unsigned)addr & 0xff; + ((byte *)ic->code)[p + 1] = (unsigned)addr >> 8; +} + +void rirq_delay(RIRQCode * ic, byte cycles) +{ + ic->code[ 1] = cycles; + ic->code[ 9] = 0x88; // dey + ic->code[10] = 0xd0; // bne + ic->code[11] = 0xfd; // -3 +} + + +void rirq_move(byte n, byte row) +{ + rasterIRQRows[n] = row; +} + +void rirq_clear(byte n) +{ + rasterIRQRows[n] = 255; +} + +void rirq_init_tables(void) +{ + for(byte i=0; i 0 && rr < rasterIRQRows[(rj = rasterIRQIndex[j - 1])]) + { + rasterIRQIndex[j] = rj; + j--; + } + rasterIRQIndex[j] = ri; + } +#endif + +#if NUM_IRQS & 3 + for(sbyte i=NUM_IRQS-1; i>=0; i--) + rasterIRQNext[i] = rasterIRQRows[rasterIRQIndex[i + 1]]; +#else + for(sbyte i=NUM_IRQS/4-1; i>=0; i--) + { + #pragma unroll(full) + for(int j=0; j<4; j++) + rasterIRQNext[i + j * NUM_IRQS / 4] = rasterIRQRows[rasterIRQIndex[i + j * NUM_IRQS / 4 + 1]]; + } +#endif + + rirq_pcount = rirq_count; + if (inirq) + nextIRQ = NUM_IRQS - 1; + else + { + byte yp = rasterIRQNext[0]; + if (yp != 0xff) + { + vic.raster = yp - 1; + nextIRQ = 0; + } + } +} + +void rirq_start(void) +{ + __asm + { + lda $d011 + and #$7f + sta $d011 + + lda #100 + sta $d012 + + asl $d019 + cli + } +} + +void rirq_stop(void) +{ + __asm + { + sei + } +} + +#pragma native(rirq_sort) +#pragma native(rirq_wait) +#pragma native(rirq_start) +#pragma native(rirq_stop) + diff --git a/oscar64/include/c64/rasterirq.h b/oscar64/include/c64/rasterirq.h new file mode 100644 index 0000000..dd7e3d0 --- /dev/null +++ b/oscar64/include/c64/rasterirq.h @@ -0,0 +1,202 @@ +#ifndef C64_RASTERIRQ_H +#define C64_RASTERIRQ_H + +#include "types.h" + +#ifndef NUM_IRQS +#define NUM_IRQS 16 +#endif + +extern volatile byte rirq_count; + +enum RIRQCodeIndex +{ + RIRQ_DATA_0 = 1, + RIRQ_DATA_1 = 3, + + RIRQ_ADDR_0 = 10, + RIRQ_ADDR_1 = 13, + + RIRQ_DATA_2 = 16, + RIRQ_ADDR_2 = 18, + + RIRQ_DATA_3 = 21, + RIRQ_ADDR_3 = 23, + + RIRQ_DATA_4 = 26, + RIRQ_ADDR_4 = 28, + + RIRQ_SIZE = 31, + + RIRQ_DATA_5 = 31, + RIRQ_ADDR_5 = 33, + + RIRQ_DATA_6 = 36, + RIRQ_ADDR_6 = 38, + + RIRQ_DATA_7 = 41, + RIRQ_ADDR_7 = 43, + + RIRQ_DATA_8 = 46, + RIRQ_ADDR_8 = 48, + + RIRQ_DATA_9 = 51, + RIRQ_ADDR_9 = 53, + + RIRQ_DATA_10 = 56, + RIRQ_ADDR_10 = 58, + + RIRQ_SIZE_10 = 61, + + RIRQ_DATA_11 = 61, + RIRQ_ADDR_11 = 63, + + RIRQ_DATA_12 = 66, + RIRQ_ADDR_12 = 68, + + RIRQ_DATA_13 = 71, + RIRQ_ADDR_13 = 73, + + RIRQ_DATA_14 = 76, + RIRQ_ADDR_14 = 78, + + RIRQ_DATA_15 = 81, + RIRQ_ADDR_15 = 83, + + RIRQ_DATA_16 = 86, + RIRQ_ADDR_16 = 88, + + RIRQ_DATA_17 = 91, + RIRQ_ADDR_17 = 93, + + RIRQ_DATA_18 = 96, + RIRQ_ADDR_18 = 98, + + RIRQ_DATA_19 = 101, + RIRQ_ADDR_19 = 103, + + RIRQ_SIZE_20 = 106, + + RIRQ_DATA_20 = 106, + RIRQ_ADDR_20 = 108, + + RIRQ_DATA_21 = 111, + RIRQ_ADDR_21 = 113, + + RIRQ_DATA_22 = 116, + RIRQ_ADDR_22 = 118, + + RIRQ_DATA_23 = 121, + RIRQ_ADDR_23 = 123, + + RIRQ_DATA_24 = 126, + RIRQ_ADDR_24 = 128, + + RIRQ_DATA_25 = 131, + RIRQ_ADDR_25 = 133 +}; + +// One raster interrupt operation, handles up to five writes +// to arbitrary memory location, or one wait and four writes. +typedef struct RIRQCode +{ + byte size; + byte code[RIRQ_SIZE]; +} RIRQCode; + +typedef struct RIRQCode10 +{ + RIRQCode c; + byte code[RIRQ_SIZE_10 - RIRQ_SIZE]; +} RIRQCode10; + +typedef struct RIRQCode20 +{ + RIRQCode c; + byte code[RIRQ_SIZE_20 - RIRQ_SIZE]; +} RIRQCode20; + +// Build one raster IRQ operation of the given size (wait + #ops) for up to 5 instructions +void rirq_build(RIRQCode * ic, byte size); + +// Allocate one raster IRQ operation of the given size (wait + #ops) +RIRQCode * rirq_alloc(byte size); + +// Add a write command to a raster IRQ +inline void rirq_write(RIRQCode * ic, byte n, void * addr, byte data); + +// Add a call command to a raster IRQ +inline void rirq_call(RIRQCode * ic, byte n, void * addr); + +// Change the address of a raster IRQ write command +inline void rirq_addr(RIRQCode * ic, byte n, void * addr); + +// Change the high byte of the address of a raster IRQ write command +inline void rirq_addrhi(RIRQCode * ic, byte n, byte hi); + +// Change the data of a raster IRQ write command +inline void rirq_data(RIRQCode * ic, byte n, byte data); + +// Add a delay of 5 * cycles to a raster IRQ +inline void rirq_delay(RIRQCode * ic, byte cycles); + +// Place a raster IRQ into one of the 16 slots, the interrupt will fire +// one line below the given row +inline void rirq_set(byte n, byte row, RIRQCode * write); + +// Remove a raster IRQ from one of the 16 slots +inline void rirq_clear(byte n); + +// Change the vertical position of the raster IRQ of one of the slots +inline void rirq_move(byte n, byte row); + + +// Initialize the raster IRQ system with either the kernal IRQ vector +// or the hardware IRQ vector if the kernal ROM is turned off (which is +// the less resource hungry option) +inline void rirq_init(bool kernalIRQ); + +// Raster IRQ through kernal, with IO range always enabled +// calls kernal continuation +void rirq_init_kernal(void); + +// Raster IRQ through kernal, with IO range not always enabled +// calls kernal continuation +void rirq_init_kernal_noio(void); + +// Raster IRQ through RAM and ROM vector, with ROM disabled or not and IO range always enabled +// does not call kernal continuation +void rirq_init_crt(void); + +// Raster IRQ through RAM and ROM vector, with ROM disabled or not and IO range not always enabled +// does not call kernal continuation +void rirq_init_crt_noio(void); + +// Raster IRQ through RAM vector, with ROM disabled and IO range always enabled +// does not call kernal continuation +void rirq_init_io(void); + +// Raster IRQ through RAM vector, with ROM disabled and IO range not always enabled +// does not call kernal continuation +void rirq_init_memmap(void); + +// Start raster IRQ +void rirq_start(void); + +// Stop raster IRQ +void rirq_stop(void); + +// Sort the raster IRQ, must be performed at the end of the frame after changing +// the vertical position of one of the interrupt operations. +// Set the inirq flag to true when calling this from an interrupt +void rirq_sort(bool inirq = false); + +// Wait for the last raster IRQ op to have completed. Must be called before a +// sort if the raster IRQ system is active +void rirq_wait_done(void); + +void rirq_wait(void); + +#pragma compile("rasterirq.c") + +#endif diff --git a/oscar64/include/c64/reu.c b/oscar64/include/c64/reu.c new file mode 100644 index 0000000..1f821ba --- /dev/null +++ b/oscar64/include/c64/reu.c @@ -0,0 +1,97 @@ +#include "reu.h" + +int reu_count_pages(void) +{ + volatile char c, d; + + c = 0; + reu_store(0, &c, 1); + reu_load(0, &d, 1); + + if (d == 0) + { + c = 0x47; + reu_store(0, &c, 1); + reu_load(0, &d, 1); + + if (d == 0x47) + { + for(int i=1; i<256; i++) + { + long l = (long)i << 16; + c = 0x47; + reu_store(l, &c, 1); + c = 0x00; + reu_store(0, &c, 1); + + reu_load(l, &d, 1); + if (d != 0x47) + return i; + } + + return 256; + } + } + + return 0; +} + +inline void reu_store(unsigned long raddr, const volatile char * sp, unsigned length) +{ + reu.laddr = (word)sp; + reu.raddr = raddr; + reu.rbank = raddr >> 16; + reu.length = length; + reu.ctrl = REU_CTRL_INCL | REU_CTRL_INCR; + reu.cmd = REU_CMD_EXEC | REU_CMD_FF00 | REU_CMD_STORE; + +} + +inline void reu_load(unsigned long raddr, volatile char * dp, unsigned length) +{ + reu.laddr = (word)dp; + reu.raddr = raddr; + reu.rbank = raddr >> 16; + reu.length = length; + reu.ctrl = REU_CTRL_INCL | REU_CTRL_INCR; + reu.cmd = REU_CMD_EXEC | REU_CMD_FF00 | REU_CMD_LOAD; +} + +inline void reu_fill(unsigned long raddr, char c, unsigned length) +{ + reu.laddr = (word)&c; + reu.raddr = raddr; + reu.rbank = raddr >> 16; + reu.length = length; + reu.ctrl = REU_CTRL_FIXL | REU_CTRL_INCR; + reu.cmd = REU_CMD_EXEC | REU_CMD_FF00 | REU_CMD_STORE; +} + +inline void reu_load2d(unsigned long raddr, volatile char * dp, char height, unsigned width, unsigned stride) +{ + reu.ctrl = REU_CTRL_INCL | REU_CTRL_INCR; + reu.laddr = (word)dp; + for(char i=0; i> 16; + reu.cmd = REU_CMD_EXEC | REU_CMD_FF00 | REU_CMD_LOAD; + raddr += stride; + } +} + +inline void reu_load2dpage(unsigned long raddr, volatile char * dp, char height, unsigned width, unsigned stride) +{ + reu.ctrl = REU_CTRL_INCL | REU_CTRL_INCR; + reu.laddr = (word)dp; + reu.rbank = raddr >> 16; + for(char i=0; i> 16)) +#define SID_FREQ_NTSC(f) ((unsigned)(((unsigned long)(f) * SID_CLKSCALE_NTSC) >> 16)) + +struct SID +{ + struct Voice + { + volatile unsigned freq; + volatile unsigned pwm; + volatile byte ctrl; + volatile byte attdec; + volatile byte susrel; + } voices[3]; + + volatile unsigned ffreq; + volatile byte resfilt; + volatile byte fmodevol; + + volatile byte potx; + volatile byte poty; + volatile byte random; + volatile byte env3; +}; + +#define NOTE_C(o) (16744U >> (10 - (o))) +#define NOTE_CS(o) (17740U >> (10 - (o))) +#define NOTE_D(o) (18794U >> (10 - (o))) +#define NOTE_DS(o) (19912U >> (10 - (o))) +#define NOTE_E(o) (21096U >> (10 - (o))) +#define NOTE_F(o) (22351U >> (10 - (o))) +#define NOTE_FS(o) (23680U >> (10 - (o))) +#define NOTE_G(o) (25087U >> (10 - (o))) +#define NOTE_GS(o) (26580U >> (10 - (o))) +#define NOTE_A(o) (28160U >> (10 - (o))) +#define NOTE_AS(o) (29834U >> (10 - (o))) +#define NOTE_B(o) (31068U >> (10 - (o))) + +// reference to the SID chip +#define sid (*((struct SID *)0xd400)) + +#pragma compile("sid.c") + +#endif diff --git a/oscar64/include/c64/sprites.c b/oscar64/include/c64/sprites.c new file mode 100644 index 0000000..e2e7011 --- /dev/null +++ b/oscar64/include/c64/sprites.c @@ -0,0 +1,346 @@ +#include "sprites.h" +#include "rasterirq.h" + +static volatile char * vspriteScreen; + +#ifdef VSPRITE_BSS +#pragma bss(VSPRITE_BSS) +#endif + +void spr_init(char * screen) +{ + vspriteScreen = screen + 0x3f8; +} + + +void spr_set(char sp, bool show, int xpos, int ypos, char image, char color, bool multi, bool xexpand, bool yexpand) +{ + __assume (sp < 8); + + char m = 1 << sp; + + if (show) + vic.spr_enable |= m; + else + vic.spr_enable &= ~m; + + if (multi) + vic.spr_multi |= m; + else + vic.spr_multi &= ~m; + + if (xexpand) + vic.spr_expand_x |= m; + else + vic.spr_expand_x &= ~m; + + if (yexpand) + vic.spr_expand_y |= m; + else + vic.spr_expand_y &= ~m; + + vic.spr_pos[sp].y = ypos; + vic.spr_pos[sp].x = xpos & 0xff; + if (xpos & 0x100) + vic.spr_msbx |= m; + else + vic.spr_msbx &= ~m; + + vspriteScreen[sp] = image; + vic.spr_color[sp] = color; +} + +void spr_show(char sp, bool show) +{ + __assume (sp < 8); + + if (show) + vic.spr_enable |= 1 << sp; + else + vic.spr_enable &= ~(1 << sp); +} + +void spr_move(char sp, int xpos, int ypos) +{ + __assume (sp < 8); + + vic.spr_pos[sp].y = ypos; + vic.spr_pos[sp].x = xpos & 0xff; + if (xpos & 0x100) + vic.spr_msbx |= 1 << sp; + else + vic.spr_msbx &= ~(1 << sp); +} + +void spr_move16(char sp, int xpos, int ypos) +{ + __assume (sp < 8); + + if (ypos < 0 || ypos >= 256 || xpos < 0 || xpos >= 384) + xpos = 384; + + vic.spr_pos[sp].y = ypos; + vic.spr_pos[sp].x = xpos & 0xff; + if (xpos & 0x100) + vic.spr_msbx |= 1 << sp; + else + vic.spr_msbx &= ~(1 << sp); +} + +int spr_posx(char sp) +{ + return vic.spr_pos[sp].x | ((vic.spr_msbx & (1 << sp)) ? 256 : 0); +} + +int spr_posy(char sp) +{ + return vic.spr_pos[sp].y; +} + +void spr_image(char sp, char image) +{ + __assume (sp < 8); + + vspriteScreen[sp] = image; +} + +void spr_color(char sp, char color) +{ + __assume (sp < 8); + + vic.spr_color[sp] = color; +} + +void spr_expand(char sp, bool xexpand, bool yexpand) +{ + __assume (sp < 8); + + char m = 1 << sp; + + if (xexpand) + vic.spr_expand_x |= m; + else + vic.spr_expand_x &= ~m; + + if (yexpand) + vic.spr_expand_y |= m; + else + vic.spr_expand_y &= ~m; +} + +static char vspriteYLow[VSPRITES_MAX], vspriteXLow[VSPRITES_MAX], vspriteXHigh[VSPRITES_MAX]; +static char vspriteImage[VSPRITES_MAX], vspriteColor[VSPRITES_MAX]; + +static char spriteOrder[VSPRITES_MAX], spriteYPos[VSPRITES_MAX + 1]; + +static RIRQCode spirq[VSPRITES_MAX - 8], synch; + + +void vspr_init(char * screen) +{ + vspriteScreen = screen + 0x3f8; + + vic.spr_expand_x = 0; + vic.spr_expand_y = 0; + vic.spr_enable = 0xff; + + for(int i=0; i> 8; + #pragma unroll(8) + for(int i=0; i> 8); + vspriteImage[sp] = image; + vspriteColor[sp] = color; +} + +#pragma native(vspr_set) + +void vspr_move(char sp, int xpos, int ypos) +{ + char yp = (char)ypos; + if ((ypos & 0xff00 ) || (xpos & 0xfe00)) + yp = 0xff; + + vspriteYLow[sp] = yp; + vspriteXLow[sp] = (char)xpos; + vspriteXHigh[sp] = (char)(xpos >> 8); +} + +void vspr_movex(char sp, int xpos) +{ + vspriteXLow[sp] = (char)xpos; + vspriteXHigh[sp] = (char)(xpos >> 8); +} + +void vspr_movey(char sp, int ypos) +{ + char yp = (char)ypos; + if (ypos & 0xff00) + yp = 0xff; + + vspriteYLow[sp] = yp; +} + +void vspr_image(char sp, char image) +{ + vspriteImage[sp] = image; +} + +void vspr_color(char sp, char color) +{ + vspriteColor[sp] = color; +} + +void vspr_hide(char sp) +{ + vspriteYLow[sp] = 0xff; +} + +void vspr_sort(void) +{ + byte rm = vspriteYLow[spriteOrder[0]]; + spriteYPos[1] = rm; + + for(char i = 1; i> 1; +#endif + vic.spr_pos[uj].x = vspriteXLow[ri]; + vic.spr_pos[uj].y = spriteYPos[ui + 1]; + +// sypos[ui] = vspriteYLow[ri]; + } + + vic.spr_msbx = xymask; + +#pragma unroll(full) + bool done = false; + + for(char ti=0; ti> (ti & 7); +#else + char m = 1 << (ti & 7); +#endif + + xymask |= m; + if (!(vspriteXHigh[ri] & 1)) + xymask ^= m; + + rirq_data(spirq + ti, 2, spriteYPos[ti + 9]); + rirq_data(spirq + ti, 0, vspriteColor[ri]); + rirq_data(spirq + ti, 1, vspriteXLow[ri]); + rirq_data(spirq + ti, 3, vspriteImage[ri]); + + rirq_data(spirq + ti, 4, xymask); +// spriteYPos[ti + 9] = vspriteYLow[ri]; + } + else + { + rirq_clear(ti); + done = true; + } + } +} + +#pragma native(vspr_update) diff --git a/oscar64/include/c64/sprites.h b/oscar64/include/c64/sprites.h new file mode 100644 index 0000000..1c9c7da --- /dev/null +++ b/oscar64/include/c64/sprites.h @@ -0,0 +1,113 @@ +#ifndef SPRITES_H +#define SPRITES_H + +#include "vic.h" + +// initialize non virtualized sprite system, using only the eight hardware sprites + +void spr_init(char * screen); + +// set one sprite with the given attributes + +void spr_set(char sp, bool show, int xpos, int ypos, char image, char color, bool multi, bool xexpand, bool yexpand); + +// show or hide a sprite + +inline void spr_show(char sp, bool show); + +// move a sprite to the given position, only uses 8 bit y and 9 bit x + +inline void spr_move(char sp, int xpos, int ypos); + +// get current x position of sprite + +inline int spr_posx(char sp); + +// get current y position of sprite + +inline int spr_posy(char sp); + +// move a sprite to the given position, only uses 16 bit y and 16 bit x, +// moves the sprite to a zero y position if offscreen +void spr_move16(char sp, int xpos, int ypos); + +// change the image of a sprite + +inline void spr_image(char sp, char image); + +// change the color of a sprite + +inline void spr_color(char sp, char color); + +// change the image of a sprite + +inline void spr_expand(char sp, bool xexpand, bool yexpand); + +// The virtual sprite system works with the rasterirq library to multiplex +// 16 virtual sprites onto the actual eight hardware sprites. It uses the slots +// 0 to 8 of the rasterirq library to switch the sprites mid screen. The +// application has to race the beam and call at least the vspr_update every +// bottom of the frame to reset the top eight sprites. +// +// A usual frame would look like this: +// +// - off screen game code +// vspr_sort(); +// - more game code +// rirq_wait(); +// vspr_update(); +// - more raster irq stuff +// rirq_sort(); +// + +#ifndef VSPRITES_MAX +#define VSPRITES_MAX 16 +#endif + +// initialize the virtual (multiplexed) sprite system, offering 16 sprites + +void vspr_init(char * screen); + +void vspr_shutdown(void); + +void vspr_screen(char * screen); + +// set one sprite with the given attribute + +void vspr_set(char sp, int xpos, int ypos, char image, char color); + +// move a virtual sprite + +inline void vspr_move(char sp, int xpos, int ypos); + +inline void vspr_movex(char sp, int xpos); + +inline void vspr_movey(char sp, int ypos); + + +// change the image of a virtual sprite + +inline void vspr_image(char sp, char image); + +// change the color of a virtual sprite + +inline void vspr_color(char sp, char color); + +// hide a virtual sprite, show again by moving it into the visual range + +inline void vspr_hide(char sp); + + +// sort the virtual sprites by their y-position + +void vspr_sort(void); + +// update the virtual sprites. Must be called every frame before sorting +// the raster irq list. + +void vspr_update(void); + + +#pragma compile("sprites.c") + +#endif diff --git a/oscar64/include/c64/types.h b/oscar64/include/c64/types.h new file mode 100644 index 0000000..ad7f8e7 --- /dev/null +++ b/oscar64/include/c64/types.h @@ -0,0 +1,10 @@ +#ifndef C64_TYPES_H +#define C64_TYPES_H + +typedef unsigned char byte; +typedef unsigned int word; +typedef unsigned long dword; + +typedef signed char sbyte; + +#endif diff --git a/oscar64/include/c64/vic.c b/oscar64/include/c64/vic.c new file mode 100644 index 0000000..b08ebb5 --- /dev/null +++ b/oscar64/include/c64/vic.c @@ -0,0 +1,140 @@ +#include "vic.h" +#include "cia.h" + +void vic_setbank(char bank) +{ + cia2.pra = (cia2.pra & 0xfc) | (bank ^ 0x03); +} + +void vic_sprxy(byte s, int x, int y) +{ + vic.spr_pos[s].y = y; + vic.spr_pos[s].x = x & 0xff; + if (x & 0x100) + vic.spr_msbx |= 1 << s; + else + vic.spr_msbx &= ~(1 << s); +} + +int vic_sprgetx(byte s) +{ + return vic.spr_pos[s].x | ((vic.spr_msbx & (1 << s)) ? 256 : 0); +} + +void vic_setmode(VicMode mode, const char * text, const char * font) +{ + switch (mode) + { + case VICM_TEXT: + vic.ctrl1 = VIC_CTRL1_DEN | VIC_CTRL1_RSEL | 3; + vic.ctrl2 = VIC_CTRL2_CSEL; + break; + case VICM_TEXT_MC: + vic.ctrl1 = VIC_CTRL1_DEN | VIC_CTRL1_RSEL | 3; + vic.ctrl2 = VIC_CTRL2_CSEL | VIC_CTRL2_MCM; + break; + case VICM_TEXT_ECM: + vic.ctrl1 = VIC_CTRL1_DEN | VIC_CTRL1_ECM | VIC_CTRL1_RSEL | 3; + vic.ctrl2 = VIC_CTRL2_CSEL; + break; + case VICM_HIRES: + vic.ctrl1 = VIC_CTRL1_BMM | VIC_CTRL1_DEN | VIC_CTRL1_RSEL | 3; + vic.ctrl2 = VIC_CTRL2_CSEL; + break; + case VICM_HIRES_MC: + vic.ctrl1 = VIC_CTRL1_BMM | VIC_CTRL1_DEN | VIC_CTRL1_RSEL | 3; + vic.ctrl2 = VIC_CTRL2_CSEL | VIC_CTRL2_MCM; + break; + default: + __assume(false); + } + + cia2.pra = (cia2.pra & 0xfc) | (((unsigned)text >> 14) ^ 0x03); + vic.memptr = (((unsigned)text >> 6) & 0xf0) | (((unsigned)font >> 10) & 0x0e); +} + +bool vic_isBottom(void) +{ + return (vic.ctrl1 & VIC_CTRL1_RST8) != 0; +} + + +void vic_waitBottom(void) +{ + while (!(vic.ctrl1 & VIC_CTRL1_RST8)) + ; +} + +void vic_waitTop(void) +{ + while ((vic.ctrl1 & VIC_CTRL1_RST8)) + ; +} + +void vic_waitFrame(void) +{ + while ((vic.ctrl1 & VIC_CTRL1_RST8)) + ; + while (!(vic.ctrl1 & VIC_CTRL1_RST8)) + ; +} + +void vic_waitFrames(char n) +{ + while (n > 0) + { + vic_waitFrame(); + n--; + } +} + +void vic_waitLine(int line) +{ + char upper = (char)(line >> 1) & VIC_CTRL1_RST8; + char lower = (char)line; + + do + { + while (vic.raster != lower) + ; + } while ((vic.ctrl1 & VIC_CTRL1_RST8) != upper); +} + +void vic_waitBelow(int line) +{ + char upper = (char)(line >> 1) & VIC_CTRL1_RST8; + char lower = (char)line; + + if (upper) + { + do + { + while (vic.raster <= lower) + ; + } while (!(vic.ctrl1 & VIC_CTRL1_RST8)); + } + else + { + while (vic.raster <= lower) + ; + } +} + +void vic_waitRange(char below, char above) +{ + while (vic.ctrl1 & VIC_CTRL1_RST8) + ; + + if (vic.raster >= above) + { + while (!(vic.ctrl1 & VIC_CTRL1_RST8)) + ; + while (vic.ctrl1 & VIC_CTRL1_RST8) + ; + } + + while (vic.raster < below) + ; +} + +#pragma native(vic_waitLine) diff --git a/oscar64/include/c64/vic.h b/oscar64/include/c64/vic.h new file mode 100644 index 0000000..9c63695 --- /dev/null +++ b/oscar64/include/c64/vic.h @@ -0,0 +1,135 @@ +#ifndef C64_VIC_H +#define C64_VIC_H + +#include "types.h" + +#define VIC_CTRL1_RSEL 0x08 +#define VIC_CTRL1_DEN 0x10 +#define VIC_CTRL1_BMM 0x20 +#define VIC_CTRL1_ECM 0x40 +#define VIC_CTRL1_RST8 0x80 + +#define VIC_CTRL2_CSEL 0x08 +#define VIC_CTRL2_MCM 0x10 +#define VIC_CTRL2_RES 0x20 + +#define VIC_INTR_RST 0x01 +#define VIC_INTR_MBC 0x02 +#define VIC_INTR_MMC 0x04 +#define VIC_INTR_ILP 0x08 +#define VIC_INTR_IRQ 0x80 + +enum VICColors +{ + VCOL_BLACK, + VCOL_WHITE, + VCOL_RED, + VCOL_CYAN, + VCOL_PURPLE, + VCOL_GREEN, + VCOL_BLUE, + VCOL_YELLOW, + + VCOL_ORANGE, + VCOL_BROWN, + VCOL_LT_RED, + VCOL_DARK_GREY, + VCOL_MED_GREY, + VCOL_LT_GREEN, + VCOL_LT_BLUE, + VCOL_LT_GREY +}; + +struct VIC +{ + struct XY + { + volatile byte x, y; + } spr_pos[8]; + byte spr_msbx; + + volatile byte ctrl1; + volatile byte raster; + volatile byte lpx, lpy; + volatile byte spr_enable; + volatile byte ctrl2; + volatile byte spr_expand_y; + volatile byte memptr; + volatile byte intr_ctrl; + volatile byte intr_enable; + volatile byte spr_priority; + volatile byte spr_multi; + volatile byte spr_expand_x; + volatile byte spr_sprcol; + volatile byte spr_backcol; + volatile byte color_border; + volatile byte color_back; + volatile byte color_back1; + volatile byte color_back2; + volatile byte color_back3; + volatile byte spr_mcolor0; + volatile byte spr_mcolor1; + volatile byte spr_color[8]; + + volatile byte ext_keymap; + volatile byte ext_2mhz; + volatile byte ext_uturbo; +}; + +// set the 16k Bank for the vic +// 0 : 0x0000..0x3fff +// 1 : 0x4000..0x7fff +// 2 : 0x8000..0xbfff +// 3 : 0xc000..0xffff +void vic_setbank(char bank); + +enum VicMode +{ + VICM_TEXT, + VICM_TEXT_MC, + VICM_TEXT_ECM, + VICM_HIRES, + VICM_HIRES_MC +}; + +// set the display mode and base address. This will also +// adapt the bank. +void vic_setmode(VicMode mode, const char * text, const char * font); + +// put a sprite at the given x/y location, taking care of the +// x MSB +inline void vic_sprxy(byte s, int x, int y); + +// Read the sprite x position from the LSB and MSB register +inline int vic_sprgetx(byte s); + +// wait for the beam to reach the bottom of the visual area +inline void vic_waitBottom(void); + +// wait for the beam to reach the top of the frame +inline void vic_waitTop(void); + +// wait for the top of the frame and then for the bottom of the visual area +inline void vic_waitFrame(void); + +// return true if the beam is below the frame +inline bool vic_isBottom(void); + +// wait for n frames +void vic_waitFrames(char n); + +// wait for a specific raster line +void vic_waitLine(int line); + +// wait for beam to be below a line +void vic_waitBelow(int line); + +// wait for beam to be in a given range on screen +void vic_waitRange(char below, char above); + +// reference to the VIC chip +#define vic (*((struct VIC *)0xd000)) + +#pragma compile("vic.c") + +#endif diff --git a/oscar64/include/conio.c b/oscar64/include/conio.c new file mode 100644 index 0000000..92d278f --- /dev/null +++ b/oscar64/include/conio.c @@ -0,0 +1,426 @@ +#include "conio.h" + +static IOCharMap giocharmap = IOCHM_ASCII; + +#if defined(__C128__) +#pragma code(lowcode) +__asm bsout +{ + ldx #0 + stx 0xff00 + jsr 0xffd2 + sta 0xff01 +} +__asm bsin +{ + lda #0 + sta 0xff00 + jsr 0xffe4 + sta 0xff01 +} +__asm bsget +{ + lda #0 + sta 0xff00 + jsr 0xffcf + sta 0xff01 +} +__asm bsplot +{ + lda #0 + sta 0xff00 + jsr 0xfff0 + sta 0xff01 +} +__asm bsinit +{ + lda #0 + sta 0xff00 + jsr 0xff81 + sta 0xff01 +} +__asm dswap +{ + sta 0xff00 + jsr 0xff5f + sta 0xff01 +} +#pragma code(code) +#elif defined(__C128B__) || defined(__C128E__) +#define dswap 0xff5f +#define bsout 0xffd2 : a->a +#define bsin 0xffe4 +#define bsget 0xffcf : a->a +#define bsplot 0xfff0 +#define bsinit 0xff81 +#elif defined(__PLUS4__) +#pragma code(lowcode) +__asm bsout +{ + sta 0xff3e + jsr 0xffd2 + sta 0xff3f +} +__asm bsin +{ + sta 0xff3e + jsr 0xffe4 + sta 0xff3f +} +__asm bsget +{ + sta 0xff3e + jsr 0xffcf + sta 0xff3f +} +__asm bsplot +{ + sta 0xff3e + jsr 0xfff0 + sta 0xff3f +} +__asm bsinit +{ + sta 0xff3e + jsr 0xff81 + sta 0xff3f +} +#pragma code(code) +#elif defined(__ATARI__) +__asm bsout +{ + tax + lda 0xe407 + pha + lda 0xe406 + pha + txa +} + +__asm bsin +{ + lda 0xe405 + pha + lda 0xe404 + pha +} + +__asm bsget +{ + lda 0xe405 + pha + lda 0xe404 + pha +} + +__asm bsplot +{ + +} +__asm bsinit +{ + +} +#elif defined(__VIC20__) +#define bsout 0xffd2 : a->a +#define bsin 0xffe4 +#define bsplot 0xfff0 +#define bsget 0xffcf : a->a +__asm bsinit +{ + lda #147 + jmp $ffd2 +} +#elif defined(__CBMPET__) +#define bsout 0xffd2 : a->a +#define bsin 0xffe4 +__asm bsplot +{ + /* no equivalent on PET */ +} +__asm bsinit +{ + /* no equivalent on PET */ +} +#define bsget 0xffcf +#else +#define bsout 0xffd2 : a->a +#define bsin 0xffe4 +#define bsplot 0xfff0 +#define bsinit 0xff81 +#define bsget 0xffcf : a->a +#endif + +#if defined(__C128__) || defined(__C128B__) || defined(__C128E__) +void dispmode40col(void) +{ + if (*(volatile char *)0xd7 >= 128) + { + __asm + { + jsr dswap + } + } +} + +void dispmode80col(void) +{ + if (*(volatile char *)0xd7 < 128) + { + __asm + { + jsr dswap + } + } +} +#endif + + +void iocharmap(IOCharMap chmap) +{ + giocharmap = chmap; +#if !defined(__ATARI__) + if (chmap == IOCHM_PETSCII_1) + putrch(128 + 14); + else if (chmap == IOCHM_PETSCII_2) + putrch(14); +#endif +} + +void putrch(char c) +{ + __asm { + lda c + jsr bsout + } +} + +void putpch(char c) +{ +#if defined(__ATARI__) + if (c == 10) + c = 0x9b; +#else + if (giocharmap >= IOCHM_ASCII) + { + if (c == '\n') + c = 13; + else if (c == '\t') + { + char n = wherex() & 3; + do { + putrch(' '); + } while (++n < 4); + return; + } + else if (giocharmap >= IOCHM_PETSCII_1) + { + if (c >= 65 && c < 123) + { + if (c >= 97 || c < 91) + { +#if defined(__CBMPET__) + if (c >= 97) + c ^= 0xa0; + c ^= 0x20; +#else + c ^= 0x20; +#endif + + if (giocharmap == IOCHM_PETSCII_1) + c &= 0xdf; + } + } + } + } + +#endif + + putrch(c); +} + +static char convch(char ch) +{ +#if !defined(__ATARI__) + + if (giocharmap >= IOCHM_ASCII) + { + if (ch == 13) + ch = 10; + else if (giocharmap >= IOCHM_PETSCII_1) + { + if (ch >= 65 && ch < 219) + { + if (ch >= 193) + ch ^= 0xa0; + if (ch < 123 && (ch >= 97 || ch < 91)) + ch ^= 0x20; + } + } + } + +#endif + return ch; +} + +char getrch(void) +{ + return __asm { + jsr bsget + sta accu + }; +} + +char getpch(void) +{ + return convch(getrch()); +} + + +char kbhit(void) +{ +#if defined(__CBMPET__) + return __asm + { + lda $9e + sta accu + }; +#else + return __asm + { + lda $c6 + sta accu + }; +#endif +} + +char getche(void) +{ + char ch; + do { + ch = __asm { + jsr bsin + sta accu + }; + } while (!ch); + + __asm { + lda ch + jsr bsout + } + + return convch(ch); +} + +char getch(void) +{ + char ch; + do { + ch = __asm { + jsr bsin + sta accu + }; + } while (!ch); + + return convch(ch); +} + +char getchx(void) +{ + char ch = __asm { + jsr bsin + sta accu + }; + + return convch(ch); +} + +void putch(char c) +{ + putpch(c); +} + +void clrscr(void) +{ + putrch(147); +} + +void textcursor(bool show) +{ + *(volatile char *)0xcc = show ? 0 : 1; +} + +void gotoxy(char cx, char cy) +{ +#if defined(__CBMPET__) +#define CURS_X 0xc6 +#define CURS_Y 0xd8 +#define SCREEN_PTR 0xc4 +#define SCR_LINELEN 0xd5 + + __assume(cy < 25); + + *(volatile char *)CURS_X = cx; + *(volatile char *)CURS_Y = cy; + + if (*(volatile char *)SCR_LINELEN > 40) + cy <<= 1; + + const unsigned off = cy * 40; + + * (volatile unsigned *)SCREEN_PTR = off + 0x8000; +#else + __asm + { + ldx cy + ldy cx + clc + jsr bsplot + } +#endif +} + +void textcolor(char c) +{ + *(volatile char *)0x0286 = c; +} + +void bgcolor(char c) +{ + *(volatile char *)0xd021 = c; +} + +void bordercolor(char c) +{ + *(volatile char *)0xd020 = c; +} + +void revers(char r) +{ + if (r) + putrch(18); + else + putrch(18 + 128); +} + +char wherex(void) +{ +#if defined(__C128__) || defined(__C128B__) || defined(__C128E__) + return *(volatile char *)0xec; +#elif defined(__PLUS4__) + return *(volatile char *)0xca; +#else + return *(volatile char *)0xd3; +#endif +} + +char wherey(void) +{ +#if defined(__C128__) || defined(__C128B__) || defined(__C128E__) + return *(volatile char *)0xeb; +#elif defined(__PLUS4__) + return *(volatile char *)0xcd; +#else + return *(volatile char *)0xd6; +#endif +} diff --git a/oscar64/include/conio.h b/oscar64/include/conio.h new file mode 100644 index 0000000..60ed66f --- /dev/null +++ b/oscar64/include/conio.h @@ -0,0 +1,114 @@ +#ifndef CONIO_H +#define CONIO_H + +enum IOCharMap +{ + IOCHM_TRANSPARENT, + IOCHM_ASCII, + IOCHM_PETSCII_1, + IOCHM_PETSCII_2 +}; + +extern IOCharMap giocharmap; + +// Switch character map to transparent bypass, petscii font 1 or +// petscii font 2. Translation is performed for all reading and +// writing operations. The ascii mode will only translate the +// line end CR into an LF + +void iocharmap(IOCharMap chmap); + +#if defined(__C128__) || defined(__C128B__) || defined(__C128E__) +void dispmode40col(void); +void dispmode80col(void); +#endif + +#define PETSCII_CURSOR_LEFT 0x9d +#define PETSCII_CURSOR_RIGHT 0x1d +#define PETSCII_CURSOR_UP 0x91 +#define PETSCII_CURSOR_DOWN 0x11 +#define PETSCII_HOME 0x13 +#define PETSCII_CLEAR 0x94 +#define PETSCII_DEL 0x14 +#define PETSCII_INSERT 0x94 +#define PETSCII_STOP 0x03 +#define PETSCII_RETURN 0x0d + +#define PETSCII_F1 0x85 +#define PETSCII_F2 0x89 +#define PETSCII_F3 0x86 +#define PETSCII_F4 0x8a +#define PETSCII_F5 0x87 +#define PETSCII_F6 0x8b +#define PETSCII_F7 0x88 +#define PETSCII_F8 0x8c + +enum ConioColors +{ + COLOR_BLACK, + COLOR_WHITE, + COLOR_RED, + COLOR_CYAN, + COLOR_PURPLE, + COLOR_GREEN, + COLOR_BLUE, + COLOR_YELLOW, + + COLOR_ORANGE, + COLOR_BROWN, + COLOR_LT_RED, + COLOR_DARK_GREY, + COLOR_MED_GREY, + COLOR_LT_GREEN, + COLOR_LT_BLUE, + COLOR_LT_GREY +}; +// Lowlevel console in/out + +// using petscii translation +void putpch(char c); +char getpch(void); + +// using no translation +inline void putrch(char c); +inline char getrch(void); + + +// Standard console in/out + +char kbhit(void); + +char getche(void); + +char getch(void); + +// like getch but does not wait, returns zero if no +// key is pressed +char getchx(void); + +void putch(char c); + +void clrscr(void); + +void gotoxy(char x, char y); + +inline void textcolor(char c); + +inline void bgcolor(char c); + +inline void bordercolor(char c); + +inline void revers(char r); + +inline char wherex(void); + +inline char wherey(void); + +// show or hide the text cursor + +inline void textcursor(bool show); + +#pragma compile("conio.c") + +#endif + diff --git a/oscar64/include/crt.c b/oscar64/include/crt.c new file mode 100644 index 0000000..254bd63 --- /dev/null +++ b/oscar64/include/crt.c @@ -0,0 +1,5249 @@ +// crt.c +#include + +#ifndef OSCAR_BASIC_LINE +#define OSCAR_BASIC_LINE 10 +#endif + +#define tmpy __tmpy +#define tmp __tmp + +#define ip __ip +#define accu __accu +#define addr __addr +#define sp __sp +#define fp __fp + +#define sregs __sregs +#define regs __regs + + +void StackStart, StackEnd, BSSStart, BSSEnd, CodeStart, CodeEnd, ZeroStart, ZeroEnd; + +#pragma section(code, 0x0000, CodeStart, CodeEnd) +#pragma section(stack, 0x0000, StackStart, StackEnd) +#pragma section(bss, 0x0000, BSSStart, BSSEnd) +#pragma section(zeropage, 0x0000, ZeroStart, ZeroEnd) + +char spentry = 0; + +int main(void); + +#if defined (__PLUS4__) + +#pragma code(lowcode) +__asm p4irqx +{ + sta $ff3f + pla + tax + pla + rti +} + +__asm p4irq +{ + pha + txa + pha + sta $ff3e + + lda #>p4irqx + pha + lda #CodeStart + sta sp + 1 + + ldy #0 + lda (ip),y +lx0: + sta accu + bmi wx1 + + clc + lda ip + 0 + adc #1 + sta addr + 0 + lda ip + 1 + adc #0 + sta addr + 1 + + clc + lda addr + 0 + adc accu + sta ip + 0 + lda addr + 1 + adc #0 + sta ip + 1 +lx2: + ldy #0 +lx1: lda (addr), y + sta (sp), y + iny + cpy accu + bne lx1 + + clc + lda sp + adc accu + sta sp + lda sp + 1 + adc #0 + sta sp + 1 + + ldy #0 + lda (ip), y + bne lx0 + jmp w0 +wx1: + lda sp + 0 + sec + ldy #1 + sbc (ip), y + sta addr + lda sp + 1 + sbc #0 + sta addr + 1 + + clc + lda ip + 0 + adc #2 + sta ip + 0 + lda ip + 1 + adc #0 + sta ip + 1 + + lda accu + and #$7f + sta accu + jmp lx2 + +w0: + lda #$2f + sta $00 + lda #$36 + sta $01 + +#elif defined(OSCAR_TARGET_CRT8) || defined(OSCAR_TARGET_CRT16) + +// Start at 0x8000 autostart vectors + + byt 0x09 + byt 0x80 + byt 0x09 + byt 0x80 + byt 0xc3 + byt 0xc2 + byt 0xcd + byt 0x38 + byt 0x30 + + lda #$e7 + sta $01 + lda #$2f + sta $00 + +#elif defined(OSCAR_TARGET_BIN) + +#elif defined(__ATARI__) + +#elif defined(OSCAR_TARGET_NES) + sei + cld +#if defined(__NES_MMC3__) + lda #$00 + sta $8000 +#endif +#else + byt <[OSCAR_BASIC_START + 10] + byt >[OSCAR_BASIC_START + 10] + byt OSCAR_BASIC_LINE + byt 0x9e + byt [OSCAR_BASIC_START + 12] / 1000 % 10 + 0x30 + byt [OSCAR_BASIC_START + 12] / 100 % 10 + 0x30 + byt [OSCAR_BASIC_START + 12] / 10 % 10 + 0x30 + byt [OSCAR_BASIC_START + 12] % 10 + 0x30 + byt 0x00 + byt 0x00 + byt 0x00 + +#if defined(__C128__) + sta 0xff01 +#elif defined(__C128E__) + lda #$0e + sta 0xff00 +#elif defined(__PLUS4__) + lda #p4irq + sta $ffff + sta $ff3f +#endif + +#if !defined(OSCAR_TARGET_CRT8) && !defined(OSCAR_TARGET_CRT16) + tsx + stx spentry +#endif + +#endif + +// Clear BSS Segment + +#ifndef NOBSSCLEAR + ldx #>BSSStart + ldy #BSSEnd + beq w2 +l1: + sta (ip), y + iny + bne l1 + inx + bne l3 +l2: + sta (ip), y + iny +w2: + cpy #StackEnd - 2 + sta sp + 1 + +#ifdef OSCAR_NATIVE_ALL + +// All native code + jsr main +pexec: +tyexec: +yexec: +zexec: +exec: + +#else + +// Init byte code + + lda #bcode + sta ip + 1 + +pexec: + ldy #0 + beq exec +tyexec: + ldy tmpy +yexec: + iny +exec: + lda (ip), y + sta execjmp + 1 + iny +execjmp: + jmp (0x0900) +zexec: + tya + ldy #0 + clc + adc ip + sta ip + bcc exec + inc ip + 1 + bne exec +bcode: + byt BC_CALL_ABS * 2 + byt
main + byt BC_EXIT * 2 +#endif + +spexit: +#if defined(__ATARI__) || defined(OSCAR_TARGET_NES) + jmp spexit +#else + lda #$4c + sta $54 + lda #0 + sta $13 +#if defined(__C128__) || defined(__C128B__) || defined(__C128E__) + sta $1a + lda #$1b + sta $18 +#endif + lda #$19 + sta $16 +#if defined(__PLUS4__) + sta $ff3e +#endif +#endif + rts +} + +#pragma startup(startup) + +__asm bcexec +{ +#ifdef OSCAR_NATIVE_ALL + jmp (accu) +#else + lda ip + pha + lda ip + 1 + pha + lda accu + sta ip + lda accu + 1 + sta ip + 1 + + ldy #0 + lda #bdone + sta (sp), y + jmp startup.pexec +bdone: nop + pla + pla + pla + sta ip + 1 + pla + sta ip + rts +#endif +} + +__asm jmpaddr +{ + jmp (addr) +} + +#pragma runtime(bcexec, bcexec) +#pragma runtime(jmpaddr, jmpaddr) + +__asm negaccu +{ + sec + lda #0 + sbc accu + sta accu + lda #0 + sbc accu + 1 + sta accu + 1 + rts +} + +__asm negtmp +{ + sec + lda #0 + sbc tmp + sta tmp + lda #0 + sbc tmp + 1 + sta tmp + 1 + rts +} + +__asm negtmpb +{ + sec + lda #0 + sbc tmp + 2 + sta tmp + 2 + lda #0 + sbc tmp + 3 + sta tmp + 3 + rts +} + +__asm negaccu32 +{ + sec + lda #0 + sbc accu + sta accu + lda #0 + sbc accu + 1 + sta accu + 1 + lda #0 + sbc accu + 2 + sta accu + 2 + lda #0 + sbc accu + 3 + sta accu + 3 + rts +} + +__asm negtmp32 +{ + sec + lda #0 + sbc tmp + sta tmp + lda #0 + sbc tmp + 1 + sta tmp + 1 + lda #0 + sbc tmp + 2 + sta tmp + 2 + lda #0 + sbc tmp + 3 + sta tmp + 3 + rts +} + +__asm negtmp32b +{ + sec + lda #0 + sbc tmp + 4 + sta tmp + 4 + lda #0 + sbc tmp + 5 + sta tmp + 5 + lda #0 + sbc tmp + 6 + sta tmp + 6 + lda #0 + sbc tmp + 7 + sta tmp + 7 + rts +} + + +// divide accu by tmp result in accu, remainder in tmp + 2 + +__asm divmod +{ + lda accu + 1 + bne WB + lda tmp + 1 + bne BW + +// byte / byte +BB: + // accu is zero at this point + sta tmp + 3 + ldx #4 + asl accu +LBB1: rol + cmp tmp + bcc WBB1 + sbc tmp +WBB1: rol accu + rol + cmp tmp + bcc WBB2 + sbc tmp +WBB2: rol accu + dex + bne LBB1 + sta tmp + 2 + rts + +// byte / word -> 0 +BW: + lda accu + sta tmp + 2 + lda accu + 1 + sta tmp + 3 + lda #0 + sta accu + sta accu + 1 + rts + +DM8: + sta tmp + lda #0 + sta tmp + 1 + lda accu + 1 + beq BB + +WB: + lda tmp + 1 + bne WW + lda tmp + bmi WW + +// word / byte + + lda #0 + sta tmp + 3 + ldx #16 + asl accu + rol accu + 1 +LWB1: rol + cmp tmp + bcc WWB1 + sbc tmp +WWB1: rol accu + rol accu + 1 + dex + bne LWB1 + sta tmp + 2 + rts + +// word / word +WW: + lda #0 + sta tmp + 2 + sta tmp + 3 + + ldy #16 + clc +L1: rol accu + rol accu + 1 + rol tmp + 2 + rol tmp + 3 + sec + lda tmp + 2 + sbc tmp + tax + lda tmp + 3 + sbc tmp + 1 + bcc W1 + stx tmp + 2 + sta tmp + 3 +W1: dey + bne L1 + rol accu + rol accu + 1 + rts +} + +// divide accu by tmp result in accu, remainder in tmp + 4 + +__asm divmod32 +{ + lda #0 + sta tmp + 4 + sta tmp + 5 + sta tmp + 6 + sta tmp + 7 + + lda tmp + 2 + ora tmp + 3 + beq W32N + +W32: +// upper 16 bit are not zero, so ignore first 16 div steps + + ldy #16 + lda accu + 3 + sta tmp + 5 + lda accu + 2 + sta tmp + 4 + lda #0 + sta accu + 2 + sta accu + 3 + + clc +L1: rol accu + rol accu + 1 + rol tmp + 4 + rol tmp + 5 + rol tmp + 6 + rol tmp + 7 + + lda tmp + 4 + cmp tmp + 0 + lda tmp + 5 + sbc tmp + 1 + lda tmp + 6 + sbc tmp + 2 + tax + lda tmp + 7 + sbc tmp + 3 + bcc W1 + stx tmp + 6 + sta tmp + 7 + lda tmp + 4 + sbc tmp + 0 + sta tmp + 4 + lda tmp + 5 + sbc tmp + 1 + sta tmp + 5 + sec +W1: dey + bne L1 + rol accu + rol accu + 1 + ldy tmpy + rts + +W32N: + +// divide 32 by 16 bit + + lda accu + 2 + ora accu + 3 + bne LB0 + +// divide 16 by 16 bit + + jsr divmod + lda tmp + 2 + sta tmp + 4 + lda tmp + 3 + sta tmp + 5 + rts + +LB0: + ldy #32 + + lda tmp + 1 + bne W16 + +// divide 32 by 8 + +// a is zero + clc +LB1: rol accu + rol accu + 1 + rol accu + 2 + rol accu + 3 + rol + bcc LB1a + + sbc tmp + sec + bcs LB1b +LB1a: + cmp tmp + bcc LB1b + sbc tmp +LB1b: +WB1: dey + bne LB1 + sta tmp + 4 + rol accu + rol accu + 1 + rol accu + 2 + rol accu + 3 + ldy tmpy + rts + +W16: +// 0x0000bb?? number in range 256..65535 + lda accu + 3 + bne LS0 + ldx accu + 2 + stx accu + 3 + ldx accu + 1 + stx accu + 2 + ldx accu + 0 + stx accu + 1 + sta accu + 0 + ldy #24 +LS0: + clc +LS1: rol accu + rol accu + 1 + rol accu + 2 + rol accu + 3 + rol tmp + 4 + rol tmp + 5 + bcc LS1a + + lda tmp + 4 + sbc tmp + tax + lda tmp + 5 + sbc tmp + 1 + sec + bcs LS1b +LS1a: + sec + lda tmp + 4 + sbc tmp + tax + lda tmp + 5 + sbc tmp + 1 + bcc WS1 +LS1b: + stx tmp + 4 + sta tmp + 5 +WS1: dey + bne LS1 + rol accu + rol accu + 1 + rol accu + 2 + rol accu + 3 + ldy tmpy + rts + +} + +// Multiply accu by tmp result in tmp + 2 + +__asm mul16 +{ + ldy #0 + sty tmp + 3 + + lda tmp + ldx tmp + 1 + beq W1 + + sec + ror + bcc L2 +L1: + tax + clc + tya + adc accu + tay + lda tmp + 3 + adc accu + 1 + sta tmp + 3 + txa +L2: + asl accu + 0 + rol accu + 1 + lsr + bcc L2 + bne L1 + + lda tmp + 1 +W1: + lsr + bcc L4 +L3: + tax + clc + tya + adc accu + tay + lda tmp + 3 + adc accu + 1 + sta tmp + 3 + txa +L4: + asl accu + 0 + rol accu + 1 + lsr + bcs L3 + bne L4 + + sty tmp + 2 +} + +__asm mul32by8 +{ + ldy #0 + sty tmp + 4 + sty tmp + 5 + sty tmp + 6 + + lsr + bcs W3 + beq E0 +L1: + asl accu + rol accu + 1 + rol accu + 2 + rol accu + 3 +W2: + lsr + bcc L1 +W3: + tax + clc + lda tmp + 4 + adc accu + sta tmp + 4 + lda tmp + 5 + adc accu + 1 + sta tmp + 5 + lda tmp + 6 + adc accu + 2 + sta tmp + 6 + tya + adc accu + 3 + tay + txa + bne L1 +E0: + sty tmp + 7 + rts +} + +__asm mul32 +{ + lda tmp + 1 + ora tmp + 2 + ora tmp + 3 + bne WW + lda tmp + 0 + jmp mul32by8 +WW: + ldy #0 + sty tmp + 4 + sty tmp + 5 + tya + + sec + ror tmp + 0 + + bcc W1 +L1: + tax + clc + lda tmp + 4 + adc accu + sta tmp + 4 + lda tmp + 5 + adc accu + 1 + sta tmp + 5 + tya + adc accu + 2 + tay + txa + adc accu + 3 +W1: + lsr tmp + 1 + bcc W2 + tax + clc + lda tmp + 5 + adc accu + 0 + sta tmp + 5 + tya + adc accu + 1 + tay + txa + adc accu + 2 +W2: + lsr tmp + 2 + bcc W3 + tax + clc + tya + adc accu + 0 + tay + txa + adc accu + 1 +W3: + lsr tmp + 3 + bcc W4 + clc + adc accu + 0 +W4: + asl accu + rol accu + 1 + rol accu + 2 + rol accu + 3 + + lsr tmp + 0 + bcc W1 + bne L1 + + sty tmp + 6 + sta tmp + 7 + rts +} + +#if 1 +__asm mul16by8 +{ + lsr + beq zero + more: + ldx #0 + ldy #0 + bcc skip + odd: + ldy accu + ldx accu + 1 + bcs skip + + loop: + sta tmpy + + clc + tya + adc accu + tay + txa + adc accu + 1 + tax + + lda tmpy + skip: + asl accu + rol accu + 1 + lsr + bcc skip + bne loop + done: + clc + tya + adc accu + sta accu + txa + adc accu + 1 + sta accu + 1 + rts + zero: + bcs one + sta accu + sta accu + 1 + one: + rts +} +#else +__asm mul16by8 +{ + ldy #0 + sty tmp + 3 + + lsr + bcc L2 +L1: + tax + clc + tya + adc accu + tay + lda tmp + 3 + adc accu + 1 + sta tmp + 3 + txa +L2: + asl accu + 0 + rol accu + 1 + lsr + bcs L1 + bne L2 + + sty tmp + 2 + rts +} +#endif + +__asm divs16 +{ + bit accu + 1 + bpl L1 + jsr negaccu + bit tmp + 1 + bpl L2 + jsr negtmp +L3: jmp divmod +L1: bit tmp + 1 + bpl L3 + jsr negtmp +L2: jsr divmod + jmp negaccu +} + +__asm mods16 +{ + bit accu + 1 + bpl L1 + jsr negaccu + bit tmp + 1 + bpl L2 + jsr negtmp +L2: jsr divmod + jmp negtmpb +L1: bit tmp + 1 + bpl L3 + jsr negtmp +L3: jmp divmod + rts +} + +__asm divmods16 +{ + bit accu + 1 + bmi L1 + bit tmp + 1 + bmi L2 + jmp divmod +L2: + jsr negtmp + jsr divmod + jmp negaccu +L1: + jsr negaccu + bit tmp + 3 + bmi L3 + jsr divmod + jsr negtmpb + jmp negaccu +L3: + jsr negtmp + jsr divmod + jmp negtmpb +} + +__asm divs32 +{ + bit accu + 3 + bpl L1 + jsr negaccu32 + bit tmp + 3 + bpl L2 + jsr negtmp32 +L3: jmp divmod32 +L1: bit tmp + 3 + bpl L3 + jsr negtmp32 +L2: jsr divmod32 + jmp negaccu32 +} + +__asm mods32 +{ + bit accu + 3 + bpl L1 + jsr negaccu32 + bit tmp + 3 + bpl L2 + jsr negtmp32 +L2: jsr divmod32 + jmp negtmp32b +L3: jmp divmod32 +L1: bit tmp + 3 + bpl L3 + jsr negtmp32 + jmp divmod32 + +} + +__asm divmods32 +{ + bit accu + 3 + bmi L1 + bit tmp + 3 + bmi L2 + jmp divmod32 +L2: + jsr negtmp32 + jsr divmod32 + jmp negaccu32 +L1: + jsr negaccu32 + bit tmp + 3 + bmi L3 + jsr divmod32 + jsr negtmp32b + jmp negaccu32 +L3: + jsr negtmp32 + jsr divmod32 + jmp negtmp32b +} + +#pragma runtime(mul16, mul16); +#pragma runtime(mul16by8, mul16by8); +#pragma runtime(divu16, divmod); +#pragma runtime(modu16, divmod); +#pragma runtime(divs16, divs16); +#pragma runtime(mods16, mods16); +#pragma runtime(divmods16, divmods16); +#pragma runtime(divu16by8, divmod.DM8) + +#pragma runtime(mul32, mul32); +#pragma runtime(mul32by8, mul32by8); +#pragma runtime(divu32, divmod32); +#pragma runtime(modu32, divmod32); +#pragma runtime(divs32, divs32); +#pragma runtime(mods32, mods32); +#pragma runtime(divmods32, divmods32); + + + +__asm inp_nop +{ + jmp startup.zexec +} + +#pragma bytecode(BC_NOP, inp_nop) + +#pragma bytecode(BC_EXIT, startup.spexit) + +__asm inp_jsr +{ + lda (ip), y + sta P1 + 1 + iny + lda (ip), y + sta P1 + 2 + tya + sec + adc ip + sta ip + bcc P1 + inc ip + 1 +P1: + jsr $0000 + jmp startup.pexec +} +#pragma bytecode(BC_JSR, inp_jsr) + +__asm inp_native +{ + tya + clc + adc ip + sta P1 + 1 + lda ip + 1 + adc #0 + sta P1 + 2 +P1: + jsr $0000 + + ldy #0 + lda (sp), y + sta ip + iny + lda (sp), y + sta ip + 1 + dey + jmp startup.exec +} + +#pragma bytecode(BC_NATIVE, inp_native) + +__asm inp_const_8 +{ + lda (ip), y + tax + iny + lda (ip), y + sta $00, x + jmp startup.yexec +} + +#pragma bytecode(BC_CONST_8, inp_const_8) + +__asm inp_const_p8 +{ + lda (ip), y + tax + iny + lda (ip), y + sta $00, x + lda #0 + sta $01, x + jmp startup.yexec +} + +#pragma bytecode(BC_CONST_P8, inp_const_p8) + +__asm inp_const_n8 +{ + lda (ip), y + tax + iny + lda (ip), y + sta $00, x + lda #$ff + sta $01, x + jmp startup.yexec +} + +#pragma bytecode(BC_CONST_N8, inp_const_n8) + +__asm inp_const_16 +{ + lda (ip), y + tax + iny + lda (ip), y + sta $00, x + iny + lda (ip), y + sta $01, x + jmp startup.yexec +} + +#pragma bytecode(BC_CONST_16, inp_const_16) + +__asm inp_const_32 +{ + lda (ip), y + tax + iny + lda (ip), y + sta $00, x + iny + lda (ip), y + sta $01, x + iny + lda (ip), y + sta $02, x + iny + lda (ip), y + sta $03, x + jmp startup.yexec +} + +#pragma bytecode(BC_CONST_32, inp_const_32) + +__asm inp_load_reg_8 +{ + lda (ip), y + tax + lda $00, x + sta accu + lda #0 + sta accu + 1 + jmp startup.yexec +} + +#pragma bytecode(BC_LOAD_REG_8, inp_load_reg_8) + +__asm inp_store_reg_8 +{ + lda (ip), y + tax + lda accu + sta $00, x + jmp startup.yexec +} + +#pragma bytecode(BC_STORE_REG_8, inp_store_reg_8) + +__asm inp_load_reg_16 +{ + lda (ip), y + tax + lda $00, x + sta accu + lda $01, x + sta accu + 1 + jmp startup.yexec +} + +#pragma bytecode(BC_LOAD_REG_16, inp_load_reg_16) + +__asm inp_store_reg_16 +{ + lda (ip), y + tax + lda accu + sta $00, x + lda accu + 1 + sta $01, x + jmp startup.yexec +} + +#pragma bytecode(BC_STORE_REG_16, inp_store_reg_16) + +__asm inp_load_reg_32 +{ + lda (ip), y + tax + lda $00, x + sta accu + lda $01, x + sta accu + 1 + lda $02, x + sta accu + 2 + lda $03, x + sta accu + 3 + jmp startup.yexec +} + +#pragma bytecode(BC_LOAD_REG_32, inp_load_reg_32) + +__asm inp_store_reg_32 +{ + lda (ip), y + tax + lda accu + sta $00, x + lda accu + 1 + sta $01, x + lda accu + 2 + sta $02, x + lda accu + 3 + sta $03, x + jmp startup.yexec +} + +#pragma bytecode(BC_STORE_REG_32, inp_store_reg_32) + +__asm inp_conv_s8_s16 +{ + lda (ip), y + tax + lda #$80 + and $00, x + bpl W1 + lda #$ff +W1: sta $01, x + jmp startup.yexec +} + +#pragma bytecode(BC_CONV_I8_I16, inp_conv_s8_s16) + +__asm inp_addr_reg +{ + lda (ip), y + tax + lda $00, x + sta addr + lda $01, x + sta addr + 1 + jmp startup.yexec +} + +#pragma bytecode(BC_ADDR_REG, inp_addr_reg) + +__asm inp_load_abs_8 +{ + lda (ip), y + sta addr + iny + lda (ip), y + sta addr + 1 + iny + lda (ip), y + tax + sty tmpy + ldy #0 +L0: + lda (addr), y + sta $00, x + jmp startup.tyexec +inp_load_addr_8: + lda (ip), y + tax + iny + lda (ip), y + sty tmpy + tay + jmp L0 +} + +#pragma bytecode(BC_LOAD_ABS_8, inp_load_abs_8) +#pragma bytecode(BC_LOAD_ADDR_8, inp_load_abs_8.inp_load_addr_8) + +__asm inp_load_abs_u8 +{ + lda (ip), y + sta addr + iny + lda (ip), y + sta addr + 1 + iny + lda (ip), y + tax + sty tmpy + ldy #0 +L0: + lda (addr), y + sta $00, x + lda #0 + sta $01, x + jmp startup.tyexec +inp_load_addr_u8: + lda (ip), y + tax + iny + lda (ip), y + sty tmpy + tay + jmp L0 +} + +#pragma bytecode(BC_LOAD_ABS_U8, inp_load_abs_u8) +#pragma bytecode(BC_LOAD_ADDR_U8, inp_load_abs_u8.inp_load_addr_u8) + +__asm inp_load_abs_16 +{ + lda (ip), y + sta addr + iny + lda (ip), y + sta addr + 1 + iny + lda (ip), y + tax + sty tmpy + ldy #0 +L0: + lda (addr), y + sta $00, x + iny + lda (addr), y + sta $01, x + jmp startup.tyexec + +inp_load_addr_16: + lda (ip), y + tax + iny + lda (ip), y + sty tmpy + tay + jmp L0 +} + +#pragma bytecode(BC_LOAD_ABS_16, inp_load_abs_16) +#pragma bytecode(BC_LOAD_ADDR_16, inp_load_abs_16.inp_load_addr_16) + +__asm inp_load_abs_addr +{ + lda (ip), y + sta addr + iny + lda (ip), y + sta addr + 1 + sty tmpy + + ldy #0 + lda (addr), y + tax + iny + lda (addr), y + sta addr + 1 + stx addr + jmp startup.tyexec +} + +#pragma bytecode(BC_LOAD_ABS_ADDR, inp_load_abs_addr) + + +__asm inp_load_abs_32 +{ + lda (ip), y + sta addr + iny + lda (ip), y + sta addr + 1 + iny + lda (ip), y + tax + sty tmpy + ldy #0 +L0: + + lda (addr), y + sta $00, x + iny + lda (addr), y + sta $01, x + iny + lda (addr), y + sta $02, x + iny + lda (addr), y + sta $03, x + jmp startup.tyexec + +inp_load_addr_32: + lda (ip), y + tax + iny + lda (ip), y + sty tmpy + tay + jmp L0 +} + +#pragma bytecode(BC_LOAD_ABS_32, inp_load_abs_32) +#pragma bytecode(BC_LOAD_ADDR_32, inp_load_abs_32.inp_load_addr_32) + +__asm inp_store_abs_8 +{ + lda (ip), y + sta addr + iny + lda (ip), y + sta addr + 1 + iny + lda (ip), y + tax + sty tmpy + ldy #0 +L0: + + lda $00, x + sta (addr), y + jmp startup.tyexec + +inp_store_addr_8: + lda (ip), y + tax + iny + lda (ip), y + sty tmpy + tay + jmp L0 +} + +#pragma bytecode(BC_STORE_ABS_8, inp_store_abs_8) +#pragma bytecode(BC_STORE_ADDR_8, inp_store_abs_8.inp_store_addr_8) + +__asm inp_store_abs_16 +{ + lda (ip), y + sta addr + iny + lda (ip), y + sta addr + 1 + iny + lda (ip), y + tax + sty tmpy + ldy #0 +L0: + lda $00, x + sta (addr), y + iny + lda $01, x + sta (addr), y + jmp startup.tyexec + +inp_store_addr_16: + lda (ip), y + tax + iny + lda (ip), y + sty tmpy + tay + jmp L0 +} + +#pragma bytecode(BC_STORE_ABS_16, inp_store_abs_16) +#pragma bytecode(BC_STORE_ADDR_16, inp_store_abs_16.inp_store_addr_16) + +__asm inp_store_abs_32 +{ + lda (ip), y + sta addr + iny + lda (ip), y + sta addr + 1 + iny + lda (ip), y + tax + sty tmpy + ldy #0 +L0: + lda $00, x + sta (addr), y + iny + lda $01, x + sta (addr), y + iny + lda $02, x + sta (addr), y + iny + lda $03, x + sta (addr), y + jmp startup.tyexec + +inp_store_addr_32: + lda (ip), y + tax + iny + lda (ip), y + sty tmpy + tay + jmp L0 +} + +#pragma bytecode(BC_STORE_ABS_32, inp_store_abs_32) +#pragma bytecode(BC_STORE_ADDR_32, inp_store_abs_32.inp_store_addr_32) + +__asm inp_lea_abs +{ + lda (ip), y + tax + iny + lda (ip), y + sta $00, x + iny + lda (ip), y + sta $01, x + jmp startup.yexec +} + +#pragma bytecode(BC_LEA_ABS, inp_lea_abs) + +__asm inp_lea_abs_index +{ + lda (ip), y + tax + iny + clc + lda $00, x + adc (ip), y + sta addr + iny + lda $01, x + adc (ip), y + sta addr + 1 + jmp startup.yexec +} + +#pragma bytecode(BC_LEA_ABS_INDEX, inp_lea_abs_index) + +__asm inp_lea_abs_index_u8 +{ + lda (ip), y + tax + iny + clc + lda $00, x + adc (ip), y + sta addr + iny + lda #$00 + adc (ip), y + sta addr + 1 + jmp startup.yexec +} + +#pragma bytecode(BC_LEA_ABS_INDEX_U8, inp_lea_abs_index_u8) + +__asm inp_lea_accu_index +{ + lda (ip), y + tax + clc + lda $00, x + adc accu + sta addr + lda $01, x + adc accu + 1 + sta addr + 1 + jmp startup.yexec +} + +#pragma bytecode(BC_LEA_ACCU_INDEX, inp_lea_accu_index) + +__asm inp_load_local_16 +{ + lda (ip), y + tax + iny + lda (ip), y + sty tmpy + tay + lda (fp), y + sta $00, x + iny + lda (fp), y + sta $01, x + jmp startup.tyexec +} + +#pragma bytecode(BC_LOAD_LOCAL_16, inp_load_local_16) + +__asm inp_load_local_32 +{ + lda (ip), y + tax + iny + lda (ip), y + sty tmpy + tay + lda (fp), y + sta $00, x + iny + lda (fp), y + sta $01, x + iny + lda (fp), y + sta $02, x + iny + lda (fp), y + sta $03, x + jmp startup.tyexec +} + +#pragma bytecode(BC_LOAD_LOCAL_32, inp_load_local_32) + +__asm inp_load_local_8 +{ + lda (ip), y + tax + iny + lda (ip), y + sty tmpy + tay + lda (fp), y + sta $00, x + jmp startup.tyexec +} + +#pragma bytecode(BC_LOAD_LOCAL_8, inp_load_local_8) + +__asm inp_load_local_u8 +{ + lda (ip), y + tax + iny + lda (ip), y + sty tmpy + tay + lda (fp), y + sta $00, x + lda #0 + sta $01, x + jmp startup.tyexec +} + +#pragma bytecode(BC_LOAD_LOCAL_U8, inp_load_local_u8) + +__asm inp_store_local_8 +{ + lda (ip), y + tax + iny + lda (ip), y + sty tmpy + tay + lda $00, x + sta (fp), y + jmp startup.tyexec +} + +#pragma bytecode(BC_STORE_LOCAL_8, inp_store_local_8) + +__asm inp_store_local_16 +{ + lda (ip), y + tax + iny + lda (ip), y + sty tmpy + tay + lda $00, x + sta (fp), y + iny + lda $01, x + sta (fp), y + jmp startup.tyexec +} + +#pragma bytecode(BC_STORE_LOCAL_16, inp_store_local_16) + +__asm inp_store_local_32 +{ + lda (ip), y + tax + iny + lda (ip), y + sty tmpy + tay + lda $00, x + sta (fp), y + iny + lda $01, x + sta (fp), y + iny + lda $02, x + sta (fp), y + iny + lda $03, x + sta (fp), y + jmp startup.tyexec +} + +#pragma bytecode(BC_STORE_LOCAL_32, inp_store_local_32) + +__asm inp_lea_local +{ + lda (ip), y + tax + iny + clc + lda (ip), y + adc fp + sta $00, x + iny + lda (ip), y + adc fp + 1 + sta $01, x + jmp startup.yexec +} + +#pragma bytecode(BC_LEA_LOCAL, inp_lea_local) + +__asm inp_store_frame_8 +{ + lda (ip), y + tax + iny + lda (ip), y + sty tmpy + tay + lda $00, x + sta (sp), y + jmp startup.tyexec +} + +#pragma bytecode(BC_STORE_FRAME_8, inp_store_frame_8) + +__asm inp_store_frame_16 +{ + lda (ip), y + tax + iny + lda (ip), y + sty tmpy + tay + lda $00, x + sta (sp), y + iny + lda $01, x + sta (sp), y + jmp startup.tyexec +} + +#pragma bytecode(BC_STORE_FRAME_16, inp_store_frame_16) + +__asm inp_store_frame_32 +{ + lda (ip), y + tax + iny + lda (ip), y + sty tmpy + tay + lda $00, x + sta (sp), y + iny + lda $01, x + sta (sp), y + iny + lda $02, x + sta (sp), y + iny + lda $03, x + sta (sp), y + jmp startup.tyexec +} + +#pragma bytecode(BC_STORE_FRAME_32, inp_store_frame_32) + +__asm inp_lea_frame +{ + lda (ip), y + tax + iny + clc + lda (ip), y + adc sp + sta $00, x + iny + lda (ip), y + adc sp + 1 + sta $01, x + jmp startup.yexec +} + +#pragma bytecode(BC_LEA_FRAME, inp_lea_frame) + +__asm inp_op_negate_16 +{ + sec + lda #0 + sbc accu + sta accu + lda #0 + sbc accu + 1 + sta accu + 1 + jmp startup.exec +} + +#pragma bytecode(BC_OP_NEGATE_16, inp_op_negate_16) + +__asm inp_op_invert_16 +{ + lda accu + eor #$ff + sta accu + lda accu + 1 + eor #$ff + sta accu + 1 + jmp startup.exec +} + +#pragma bytecode(BC_OP_INVERT_16, inp_op_invert_16) + +__asm inp_binop_addr_16 +{ + lda (ip), y + tax + clc + lda accu + adc $00, x + sta accu + lda accu + 1 + adc $01, x + sta accu + 1 + jmp startup.yexec +} + +#pragma bytecode(BC_BINOP_ADDR_16, inp_binop_addr_16) + +__asm inp_binop_subr_16 +{ + lda (ip), y + tax + sec + lda accu + sbc $00, x + sta accu + lda accu + 1 + sbc $01, x + sta accu + 1 + jmp startup.yexec +} + +#pragma bytecode(BC_BINOP_SUBR_16, inp_binop_subr_16) + +__asm inp_binop_andr_16 +{ + lda (ip), y + tax + lda accu + and $00, x + sta accu + lda accu + 1 + and $01, x + sta accu + 1 + jmp startup.yexec +} + +#pragma bytecode(BC_BINOP_ANDR_16, inp_binop_andr_16) + +__asm inp_binop_orr_16 +{ + lda (ip), y + tax + lda accu + ora $00, x + sta accu + lda accu + 1 + ora $01, x + sta accu + 1 + jmp startup.yexec +} + +#pragma bytecode(BC_BINOP_ORR_16, inp_binop_orr_16) + +__asm inp_binop_xorr_16 +{ + lda (ip), y + tax + lda accu + eor $00, x + sta accu + lda accu + 1 + eor $01, x + sta accu + 1 + jmp startup.yexec +} + +#pragma bytecode(BC_BINOP_XORR_16, inp_binop_xorr_16) + +__asm inp_binop_mulr_16 +{ + lda (ip), y + tax + lda #0 + sta tmp + 2 + sta tmp + 3 + + lda $00, x + sta tmp + 0 + lda $01, x + sta tmp + 1 + ldx #16 +L1: lsr tmp + 1 + ror tmp + 0 + bcc W1 + clc + lda tmp + 2 + adc accu + sta tmp + 2 + lda tmp + 3 + adc accu + 1 + sta tmp + 3 +W1: asl accu + rol accu + 1 + dex + bne L1 + lda tmp + 2 + sta accu + lda tmp + 3 + sta accu + 1 + jmp startup.yexec +} + +#pragma bytecode(BC_BINOP_MULR_16, inp_binop_mulr_16) + +__asm inp_binop_muli8_16 +{ + lda (ip), y + iny + tax + + lda #0 + sta tmp + 2 + sta tmp + 3 + + lda $00, x + sta tmp + 0 + lda $01, x + sta tmp + 1 + + lda (ip), y + + lsr + sta tmp + 4 + bcc L2 +L1: + clc + lda tmp + 2 + adc tmp + 0 + sta tmp + 2 + lda tmp + 3 + adc tmp + 1 + sta tmp + 3 +L2: + asl tmp + 0 + rol tmp + 1 + lsr tmp + 4 + bcs L1 + bne L2 + + lda tmp + 2 + sta $00, x + lda tmp + 3 + sta $01, x + + jmp startup.yexec +} + +#pragma bytecode(BC_BINOP_MULI8_16, inp_binop_muli8_16) + +__asm inp_binop_divr_u16 +{ + lda (ip), y + tax + lda $00, x + sta tmp + 0 + lda $01, x + sta tmp + 1 + sty tmpy + jsr divmod + jmp startup.tyexec +} + +#pragma bytecode(BC_BINOP_DIVR_U16, inp_binop_divr_u16) + +__asm inp_binop_modr_u16 +{ + lda (ip), y + tax + lda $00, x + sta tmp + 0 + lda $01, x + sta tmp + 1 + sty tmpy + jsr divmod + lda tmp + 2 + sta accu + lda tmp + 3 + sta accu + 1 + jmp startup.tyexec +} + +#pragma bytecode(BC_BINOP_MODR_U16, inp_binop_modr_u16) + +__asm inp_binop_divr_s16 +{ + lda (ip), y + sty tmpy + + tax + lda $00, x + sta tmp + 0 + lda $01, x + sta tmp + 1 + bit accu + 1 + bpl L1 + jsr negaccu + bit tmp + 1 + bpl L2 + jsr negtmp +L3: jsr divmod + jmp startup.tyexec +L1: bit tmp + 1 + bpl L3 + jsr negtmp +L2: jsr divmod + jsr negaccu + jmp startup.tyexec +} + +#pragma bytecode(BC_BINOP_DIVR_I16, inp_binop_divr_s16) + +__asm inp_binop_modr_s16 +{ + lda (ip), y + sty tmpy + tax + lda $00, x + sta tmp + 0 + lda $01, x + sta tmp + 1 + bit accu + 1 + bpl L1 + jsr negaccu + bit tmp + 1 + bpl L3 + jsr negtmp +L3: jsr divmod + lda tmp + 2 + sta accu + lda tmp + 3 + sta accu + 1 + jsr negaccu + jmp startup.tyexec +L1: bit tmp + 1 + bpl L2 + jsr negtmp +L2: jsr divmod + lda tmp + 2 + sta accu + lda tmp + 3 + sta accu + 1 + jmp startup.tyexec +} + +#pragma bytecode(BC_BINOP_MODR_I16, inp_binop_modr_s16) + +__asm inp_binop_addi_16 +{ + lda (ip), y + iny + tax + clc + lda $00, x + adc (ip), y + iny + sta $00, x + lda $01, x + adc (ip), y + sta $01, x + jmp startup.yexec +} + +#pragma bytecode(BC_BINOP_ADDI_16, inp_binop_addi_16) + +__asm inp_binop_addi_8 +{ + lda (ip), y + iny + tax + clc + lda $00, x + adc (ip), y + sta $00, x + jmp startup.yexec +} + +#pragma bytecode(BC_BINOP_ADDI_8, inp_binop_addi_8) + +__asm inp_binop_andi_8 +{ + lda (ip), y + iny + tax + lda $00, x + and (ip), y + sta $00, x + jmp startup.yexec +} + +#pragma bytecode(BC_BINOP_ANDI_8, inp_binop_andi_8) + +__asm inp_binop_ori_8 +{ + lda (ip), y + iny + tax + lda $00, x + ora (ip), y + sta $00, x + jmp startup.yexec +} + +#pragma bytecode(BC_BINOP_ORI_8, inp_binop_ori_8) + +__asm inp_binop_subi_16 +{ + lda (ip), y + iny + tax + sec + lda (ip), y + iny + sbc $00, x + sta $00, x + lda (ip), y + sbc $01, x + sta $01, x + jmp startup.yexec +} + +#pragma bytecode(BC_BINOP_SUBI_16, inp_binop_subi_16) + +__asm inp_binop_andi_16 +{ + lda (ip), y + iny + tax + lda $00, x + and (ip), y + iny + sta $00, x + lda $01, x + and (ip), y + sta $01, x + jmp startup.yexec +} + +#pragma bytecode(BC_BINOP_ANDI_16, inp_binop_andi_16) + +__asm inp_binop_ori_16 +{ + lda (ip), y + iny + tax + lda $00, x + ora (ip), y + iny + sta $00, x + lda $01, x + ora (ip), y + sta $01, x + jmp startup.yexec +} + +#pragma bytecode(BC_BINOP_ORI_16, inp_binop_ori_16) + +__asm inp_binop_shl_16 +{ +inp_binop_shlr_16: + lda (ip), y + tax + lda $00, x + byt $2c +inp_binop_shli_16: + lda (ip), y + and #$0f + beq W1 + tax + lda accu + 1 +L1: asl accu + rol + dex + bne L1 + sta accu + 1 +W1: jmp startup.yexec +} + +#pragma bytecode(BC_BINOP_SHLI_16, inp_binop_shl_16.inp_binop_shli_16) +#pragma bytecode(BC_BINOP_SHLR_16, inp_binop_shl_16.inp_binop_shlr_16) + +__asm inp_binop_shr_u16 +{ +inp_binop_shrr_u16: + lda (ip), y + tax + lda $00, x + byt $2c +inp_binop_shri_u16: + lda (ip), y + and #$0f + beq W1 + tax + lda accu + 1 +L1: lsr + ror accu + dex + bne L1 + sta accu + 1 +W1: jmp startup.yexec +} + +#pragma bytecode(BC_BINOP_SHRI_U16, inp_binop_shr_u16.inp_binop_shri_u16) +#pragma bytecode(BC_BINOP_SHRR_U16, inp_binop_shr_u16.inp_binop_shrr_u16) + +__asm inp_binop_shr_s16 +{ +inp_binop_shrr_s16: + lda (ip), y + tax + lda $00, x + byt $2c +inp_binop_shri_s16: + lda (ip), y + and #$0f + beq W1 + tax + lda accu + 1 +L1: cmp #$80 + ror + ror accu + dex + bne L1 + sta accu + 1 +W1: jmp startup.yexec +} + +#pragma bytecode(BC_BINOP_SHRI_I16, inp_binop_shr_s16.inp_binop_shri_s16) +#pragma bytecode(BC_BINOP_SHRR_I16, inp_binop_shr_s16.inp_binop_shrr_s16) + +__asm inp_binop_adda_16 +{ + lda (ip), y + tax + clc + lda $00, x + adc accu + sta $00, x + lda $01, x + adc accu + 1 + sta $01, x + jmp startup.yexec +} + +#pragma bytecode(BC_BINOP_ADDA_16, inp_binop_adda_16) + +__asm cmp16 +{ +inp_binop_cmpr_s16: + lda (ip), y + tax + + sec + lda $01, x + sbc accu + 1 + beq cmpeq +cmpnes: + bvc cmpsv + eor #$80 +cmpsv: bmi cmp_lt + bpl cmp_gt + +inp_binop_cmpr_u16: + lda (ip), y + tax + + lda $01, x + cmp accu + 1 + bne cmpne +cmpeq: + lda $00 , x + cmp accu + bne cmpne + beq cmp_eq + +inp_binop_cmpi_u16: + lda (ip), y + tax + iny + lda (ip), y + + cmp accu + 1 + bne cmpne + cpx accu + bne cmpne +cmp_eq: + lda #0 + sta accu + sta accu + 1 + jmp startup.yexec +cmp_lt: + lda #$ff + sta accu + sta accu +1 + jmp startup.yexec +cmpne: + bcc cmp_lt +cmp_gt: + lda #1 + sta accu + lda #0 + sta accu + 1 + jmp startup.yexec + +inp_binop_cmpi_s16: + lda (ip), y + iny + tax + lda (ip), y + + sec + sbc accu + 1 + bne cmpnes + cpx accu + bne cmpne + beq cmp_eq +} + +#pragma bytecode(BC_BINOP_CMPSR_16, cmp16.inp_binop_cmpr_s16) +#pragma bytecode(BC_BINOP_CMPSI_16, cmp16.inp_binop_cmpi_s16) +#pragma bytecode(BC_BINOP_CMPUR_16, cmp16.inp_binop_cmpr_u16) +#pragma bytecode(BC_BINOP_CMPUI_16, cmp16.inp_binop_cmpi_u16) + + +__asm cmp8 +{ +inp_binop_cmpr_s8: + lda (ip), y + tax + + sec + lda $00, x + sbc accu + beq cmp_eq +cmpnes: + bvc cmpsv + eor #$80 +cmpsv: bmi cmp_lt + bpl cmp_gt + +inp_binop_cmpr_u8: + lda (ip), y + tax + + lda $00, x + cmp accu + bne cmpne + beq cmp_eq + +inp_binop_cmpi_u8: + lda (ip), y + + cmp accu +cmp_check: + bne cmpne +cmp_eq: + lda #0 + sta accu + sta accu + 1 + jmp startup.yexec +cmp_lt: + lda #$ff + sta accu + sta accu +1 + jmp startup.yexec +cmpne: + bcc cmp_lt +cmp_gt: + lda #1 + sta accu + lda #0 + sta accu + 1 + jmp startup.yexec + +inp_binop_cmpi_s8: + lda (ip), y + + sec + sbc accu + bne cmpnes + beq cmp_eq +} + + +#pragma bytecode(BC_BINOP_CMPSR_8, cmp8.inp_binop_cmpr_s8) +#pragma bytecode(BC_BINOP_CMPSI_8, cmp8.inp_binop_cmpi_s8) +#pragma bytecode(BC_BINOP_CMPUR_8, cmp8.inp_binop_cmpr_u8) +#pragma bytecode(BC_BINOP_CMPUI_8, cmp8.inp_binop_cmpi_u8) + +__asm loopu8 +{ + lda (ip),y + tax + inc $00, x + iny + lda (ip), y + cmp $00, x + jmp cmp8.cmp_check +} + +#pragma bytecode(BC_LOOP_U8, loopu8) + +__asm bra +{ +inp_jumps: + lda (ip), y + bmi W1 + sec + adc ip + sta ip + bcc W2 + inc ip + 1 +W2: jmp startup.zexec +W1: sec + adc ip + sta ip + bcs W3 + dec ip + 1 +W3: jmp startup.zexec + +inp_branchs_eq: + lda accu + ora accu + 1 + beq inp_jumps + jmp startup.yexec +inp_branchs_ne: + lda accu + ora accu + 1 + bne inp_jumps + jmp startup.yexec +inp_branchs_gt: + lda accu + 1 + bmi W4 + ora accu + bne inp_jumps +W4: jmp startup.yexec +inp_branchs_ge: + lda accu + 1 + bpl inp_jumps + jmp startup.yexec +inp_branchs_lt: + lda accu + 1 + bmi inp_jumps + jmp startup.yexec +inp_branchs_le: + lda accu + 1 + bmi inp_jumps + ora accu + beq inp_jumps + jmp startup.yexec +} + +#pragma bytecode(BC_JUMPS, bra.inp_jumps) +#pragma bytecode(BC_BRANCHS_EQ, bra.inp_branchs_eq) +#pragma bytecode(BC_BRANCHS_NE, bra.inp_branchs_ne) +#pragma bytecode(BC_BRANCHS_GT, bra.inp_branchs_gt) +#pragma bytecode(BC_BRANCHS_GE, bra.inp_branchs_ge) +#pragma bytecode(BC_BRANCHS_LT, bra.inp_branchs_lt) +#pragma bytecode(BC_BRANCHS_LE, bra.inp_branchs_le) + +__asm braf +{ +inp_jumpf: + sec + lda (ip), y + adc ip + tax + iny + lda (ip) , y + adc ip + 1 + sta ip + 1 + stx ip + jmp startup.zexec + +inp_branchf_eq: + lda accu + ora accu + 1 + beq inp_jumpf + iny + jmp startup.yexec +inp_branchf_ne: + lda accu + ora accu + 1 + bne inp_jumpf + iny + jmp startup.yexec +inp_branchf_gt: + lda accu + 1 + bmi W1 + ora accu + bne inp_jumpf +W1: iny + jmp startup.yexec +inp_branchf_ge: + lda accu + 1 + bpl inp_jumpf + iny + jmp startup.yexec +inp_branchf_lt: + lda accu + 1 + bmi inp_jumpf + iny + jmp startup.yexec +inp_branchf_le: + lda accu + 1 + bmi inp_jumpf + ora accu + beq inp_jumpf + iny + jmp startup.yexec +} + +#pragma bytecode(BC_JUMPF, braf.inp_jumpf) +#pragma bytecode(BC_BRANCHF_EQ, braf.inp_branchf_eq) +#pragma bytecode(BC_BRANCHF_NE, braf.inp_branchf_ne) +#pragma bytecode(BC_BRANCHF_GT, braf.inp_branchf_gt) +#pragma bytecode(BC_BRANCHF_GE, braf.inp_branchf_ge) +#pragma bytecode(BC_BRANCHF_LT, braf.inp_branchf_lt) +#pragma bytecode(BC_BRANCHF_LE, braf.inp_branchf_le) + +__asm inp_enter +{ + // allocate space on stack + sec + lda sp + sbc (ip), y + iny + sta sp + lda sp + 1 + sbc (ip), y + iny + sta sp + 1 + + // number of registers to save + lda (ip), y + sty tmpy + + // save frame pointer at end of list + + tay + lda fp + sta (sp), y + iny + lda fp + 1 + sta (sp), y + + // calculate new frame pointer + + tya + sec + adc sp + sta fp + lda #0 + adc sp + 1 + sta fp + 1 + + // copy registers + dey + beq W1 + +L1: lda sregs - 1, y + dey + sta (sp), y + bne L1 + + // done + +W1: ldy tmpy + + jmp startup.yexec +} + +#pragma bytecode(BC_ENTER, inp_enter) + +__asm inp_return +{ + // number of registers to restore + + lda (ip), y + iny + sty tmpy + + // restore frame pointer + + tay + lda (sp), y + sta fp + iny + lda (sp), y + sta fp + 1 + + // copy registers + + dey + beq W1 + dey + +L1: lda (sp), y + sta sregs, y + dey + bpl L1 +W1: + + // adjust stack space + + ldy tmpy + clc + lda (ip), y + iny + adc sp + sta sp + lda (ip), y + iny + adc sp + 1 + sta sp + 1 + + // reload ip from stack + + ldy #1 + lda (sp), y + sta ip + 1 + dey + lda (sp), y + sta ip + jmp startup.exec +} + +#pragma bytecode(BC_RETURN, inp_return) + +__asm inp_push_frame +{ + sec + lda sp + sbc (ip), y + iny + sta sp + lda sp + 1 + sbc (ip), y + sta sp + 1 + + jmp startup.yexec +} + +#pragma bytecode(BC_PUSH_FRAME, inp_push_frame) + +__asm inp_pop_frame +{ + clc + lda (ip), y + iny + adc sp + sta sp + lda (ip), y + adc sp + 1 + sta sp + 1 + + jmp startup.yexec +} + +#pragma bytecode(BC_POP_FRAME, inp_pop_frame) + +__asm inp_call +{ lda (ip), y + sta addr + iny + lda (ip), y + sta addr + 1 + iny +inp_call_addr: + tya + ldy #0 + clc + adc ip + sta (sp), y + iny + lda ip + 1 + adc #0 + sta (sp), y + lda addr + sta ip + lda addr + 1 + sta ip + 1 + jmp startup.pexec +} + +#pragma bytecode(BC_CALL_ADDR, inp_call.inp_call_addr) +#pragma bytecode(BC_CALL_ABS, inp_call) + +__asm inp_fill +{ + lda (ip), y + sty tmpy + tay + + lda accu +L1: dey + sta (addr), y + bne L1 + + ldy tmpy + jmp startup.yexec +} + +#pragma bytecode(BC_FILL, inp_fill) + +__asm inp_copy +{ + lda (ip), y + sty tmpy + tay + dey + beq W1 +L1: lda (accu), y + sta (addr), y + dey + bne L1 +W1: + lda (accu), y + sta (addr), y + ldy tmpy + jmp startup.yexec +} + +#pragma bytecode(BC_COPY, inp_copy) + +__asm inp_strcpy +{ + sty tmpy + ldy #$ff +L1: iny + lda (accu), y + sta (addr), y + bne L1 + ldy tmpy + jmp startup.exec +} + +#pragma bytecode(BC_STRCPY, inp_strcpy) + +__asm inp_filll +{ + lda (ip), y + sta tmp + iny + lda (ip), y + tax + sty tmpy + + beq W1 + ldy #0 + + lda accu +Loop1: + sta (addr), y + iny + bne Loop1 + inc accu + 1 + inc addr + 1 + dex + bne Loop1 +W1: + lda accu + ldy tmp + beq W2 + dey + beq W3 +Loop2: + sta (addr), y + dey + bne Loop2 +W3: + sta (addr), y +W2: + ldy tmpy + jmp startup.yexec +} + +#pragma bytecode(BC_FILL_LONG, inp_filll) + +__asm inp_copyl +{ + lda (ip), y + sta tmp + iny + lda (ip), y + tax + sty tmpy + + beq W1 + ldy #0 +Loop1: + lda (accu), y + sta (addr), y + iny + bne Loop1 + inc accu + 1 + inc addr + 1 + dex + bne Loop1 +W1: + ldy tmp + beq W2 + dey + beq W3 +Loop2: + lda (accu), y + sta (addr), y + dey + bne Loop2 +W3: + lda (accu), y + sta (addr), y +W2: + ldy tmpy + jmp startup.yexec +} + +#pragma bytecode(BC_COPY_LONG, inp_copyl) + +__asm freg +{ +split_exp: + lda (ip), y + iny + tax +split_xexp: + lda $00, x + sta tmp + 0 + lda $01, x + sta tmp + 1 + lda $02, x + sta tmp + 2 + lda $03, x + sta tmp + 3 +split_texp: + lda tmp + 2 + asl + lda tmp + 3 + rol + sta tmp + 5 + beq ZT + lda tmp + 2 + ora #$80 + sta tmp + 2 +ZT: + +split_aexp: + lda accu + 2 + asl + lda accu + 3 + rol + sta tmp + 4 + beq ZA + lda accu + 2 + ora #$80 + sta accu + 2 +ZA: + rts + +merge_aexp: + asl accu + 3 + lda tmp + 4 + ror + sta accu + 3 + bcs W1 + lda accu + 2 + and #$7f + sta accu + 2 +W1: + rts +} + +__asm faddsub +{ +fsub: + lda tmp + 3 + eor #$80 + sta tmp + 3 +fadd: + lda #$ff + cmp tmp + 4 + beq INF + cmp tmp + 5 + bne nINF +INF: + lda accu + 3 + ora #$7f + sta accu + 3 + lda #$80 + sta accu + 2 + lda #$00 + sta accu + 0 + sta accu + 1 + rts +nINF: + sec + lda tmp + 4 + sbc tmp + 5 + beq fas_aligned + tax + + bcs fas_align2nd + + // check if first operand is below rounding + cpx #-23 + bcs W1 + + lda tmp + 5 + sta tmp + 4 + lda #0 + sta accu + sta accu + 1 + sta accu + 2 + beq fas_aligned +W1: + lda accu + 2 +L1: + lsr + ror accu + 1 + ror accu + inx + bne L1 + sta accu + 2 + lda tmp + 5 + sta tmp + 4 + jmp fas_aligned + +fas_align2nd: + // check if second operand is below rounding + cpx #24 + bcs fas_done + lda tmp + 2 +L2: lsr + ror tmp + 1 + ror tmp + dex + bne L2 + sta tmp + 2 + +fas_aligned: + lda accu + 3 + and #$80 + sta accu + 3 + eor tmp + 3 + bmi fas_sub + + clc + lda accu + adc tmp + sta accu + lda accu + 1 + adc tmp + 1 + sta accu + 1 + lda accu + 2 + adc tmp + 2 + sta accu + 2 + bcc fas_done + ror accu + 2 + ror accu + 1 + ror accu + inc tmp + 4 +fas_done: + lda tmp + 4 + cmp #$ff + beq INF + lsr + ora accu + 3 + sta accu + 3 + bcs W2 + lda accu + 2 + and #$7f + sta accu + 2 +W2: + rts + +fas_sub: + sec + lda accu + sbc tmp + sta accu + lda accu + 1 + sbc tmp + 1 + sta accu + 1 + lda accu + 2 + sbc tmp + 2 + sta accu + 2 + bcs fas_pos + sec + lda #0 + sbc accu + sta accu + lda #0 + sbc accu + 1 + sta accu + 1 + lda #0 + sbc accu + 2 + sta accu + 2 + lda accu + 3 + eor #$80 + sta accu + 3 +fas_pos: + lda accu + 2 + bmi fas_done + + ora accu + 1 + ora accu + 0 + beq fas_zero +L3: + dec tmp + 4 + beq fas_zero // underflow + asl accu + rol accu + 1 + rol accu + 2 + bpl L3 + jmp fas_done +fas_zero: + lda #0 + sta accu + 0 + sta accu + 1 + sta accu + 2 + sta accu + 3 + rts + +} + + +__asm inp_binop_add_f32 +{ + jsr freg.split_exp + jsr faddsub.fadd + jmp startup.exec +} + +#pragma bytecode(BC_BINOP_ADD_F32, inp_binop_add_f32) + +__asm inp_binop_sub_f32 +{ + jsr freg.split_exp + jsr faddsub.fsub + jmp startup.exec +} + +#pragma bytecode(BC_BINOP_SUB_F32, inp_binop_sub_f32) + +__asm crt_fmul8 +{ + sec + ror + bcc L2 +L1: tax + clc + tya + adc tmp + 6 + sta tmp + 6 + lda tmp + 7 + adc accu + 1 + sta tmp + 7 + lda tmp + 8 + adc accu + 2 + ror + sta tmp + 8 + txa + ror tmp + 7 + ror tmp + 6 + lsr + beq W1 + bcs L1 +L2: + ror tmp + 8 + ror tmp + 7 + ror tmp + 6 + lsr + bcc L2 + bne L1 +W1: + rts +} + +__asm crt_fmul +{ + lda accu + ora accu + 1 + ora accu + 2 + beq E3 +W1: + lda tmp + ora tmp + 1 + ora tmp + 2 + bne W2 + sta accu + sta accu + 1 + sta accu + 2 +E3: + sta accu + 3 + rts +W2: + lda accu + 3 + eor tmp + 3 + and #$80 + sta accu + 3 + + lda #$ff + cmp tmp + 4 + beq INF + cmp tmp + 5 + beq INF + + lda #0 + sta tmp + 6 + sta tmp + 7 + sta tmp + 8 + + ldy accu + lda tmp + bne W4 + lda tmp + 1 + beq W5 + bne W6 +W4: + jsr crt_fmul8 + lda tmp + 1 +W6: + jsr crt_fmul8 +W5: + lda tmp + 2 + jsr crt_fmul8 + + sec + lda tmp + 8 + bmi W3 + asl tmp + 6 + rol tmp + 7 + rol + clc +W3: and #$7f + sta tmp + 8 + + lda tmp + 4 + adc tmp + 5 + bcc W7 + + sbc #$7f + bcs INF + cmp #$ff + bne W8 +INF: + lda accu + 3 + ora #$7f + sta accu + 3 + lda #$80 +E2: + sta accu + 2 + lda #$00 + sta accu + 0 + sta accu + 1 + rts +W7: + sbc #$7e + bcc ZERO +W8: + lsr + ora accu + 3 + sta accu + 3 + lda #0 + ror + ora tmp + 8 + sta accu + 2 + lda tmp + 7 + sta accu + 1 + lda tmp + 6 + sta accu + rts +ZERO: + lda #0 + sta accu + 3 + beq E2 +} + +__asm inp_binop_mul_f32 +{ + jsr freg.split_exp + sty tmpy + jsr crt_fmul + ldy tmpy + jmp startup.exec +} + +__asm crt_fdiv +{ + lda accu + ora accu + 1 + ora accu + 2 + bne W1 + sta accu + 3 + rts +W1: + lda accu + 3 + eor tmp + 3 + and #$80 + sta accu + 3 + + lda tmp + 5 + beq INF + lda tmp + 4 + cmp #$ff + beq INF + + lda #0 + sta tmp + 6 + sta tmp + 7 + sta tmp + 8 + ldx #24 + +L1: + lda accu + 0 + cmp tmp + 0 + lda accu + 1 + sbc tmp + 1 + lda accu + 2 + sbc tmp + 2 + bcc W2 + +L2: + lda accu + 0 + sbc tmp + 0 + sta accu + 0 + lda accu + 1 + sbc tmp + 1 + sta accu + 1 + lda accu + 2 + sbc tmp + 2 + sta accu + 2 + sec +W2: + rol tmp + 6 + rol tmp + 7 + rol tmp + 8 + dex + beq W3 + + asl accu + rol accu + 1 + rol accu + 2 + bcs L2 + bcc L1 +W3: + sec + lda tmp + 8 + bmi W4 + asl tmp + 6 + rol tmp + 7 + rol + clc +W4: + and #$7f + sta tmp + 8 + + lda tmp + 4 + sbc tmp + 5 + bcc W5 + + clc + adc #$7f + bcs INF + cmp #$ff + bne W6 +INF: + lda accu + 3 + ora #$7f + sta accu + 3 + lda #$80 + sta accu + 2 + lda #$00 + sta accu + 1 + sta accu + 0 + rts +W5: + adc #$7f + bcc ZERO +W6: + lsr + ora accu + 3 + sta accu + 3 + lda #0 + ror + ora tmp + 8 + sta accu + 2 + lda tmp + 7 + sta accu + 1 + lda tmp + 6 + sta accu + rts + +ZERO: + lda #$00 + sta accu + 3 + sta accu + 2 + sta accu + 1 + sta accu + 0 + rts + + +} + +#pragma bytecode(BC_BINOP_MUL_F32, inp_binop_mul_f32) + +__asm inp_binop_div_f32 +{ + jsr freg.split_exp + jsr crt_fdiv + jmp startup.exec +} + +#pragma bytecode(BC_BINOP_DIV_F32, inp_binop_div_f32) + +__asm crt_fcmp +{ + lda accu + 3 + eor tmp + 3 + bpl W1 + + // different sig, check zero case + + lda accu + 3 + and #$7f + ora accu + 2 + ora accu + 1 + ora accu + bne W2 + + lda tmp + 3 + + and #$7f + ora tmp + 2 + ora tmp + 1 + ora tmp + 0 + beq fcmpeq +W2: lda accu + 3 + bmi fcmpgt + bpl fcmplt + +W1: + // same sign + lda accu + 3 + cmp tmp + 3 + bne W3 + lda accu + 2 + cmp tmp + 2 + bne W3 + lda accu + 1 + cmp tmp + 1 + bne W3 + lda accu + cmp tmp + bne W3 + +fcmpeq: + lda #0 + rts + +W3: bcs W4 + + bit accu + 3 + bmi fcmplt + +fcmpgt: + lda #1 + rts + +W4: bit accu + 3 + bmi fcmpgt + +fcmplt: + lda #$ff + rts +} + + +__asm inp_binop_cmp_f32 +{ + lda (ip), y + iny + tax + + lda accu + 3 + eor $03, x + bpl W1 + + // different sig, check zero case + + lda accu + 3 + and #$7f + ora accu + 2 + ora accu + 1 + ora accu + bne W2 + + lda $03, x + + and #$7f + ora $02, x + ora $01, x + ora $00, x + beq ibcmpf32eq +W2: lda accu + 3 + bmi ibcmpf32gt + bpl ibcmpf32lt + +W1: + // same sign + lda accu + 3 + cmp $03, x + bne W3 + lda accu + 2 + cmp $02, x + bne W3 + lda accu + 1 + cmp $01, x + bne W3 + lda accu + cmp $00, x + bne W3 + +ibcmpf32eq: + lda #0 + sta accu + sta accu + 1 + jmp startup.exec + +W3: bcs W4 + + bit accu + 3 + bmi ibcmpf32lt + +ibcmpf32gt: + lda #0 + sta accu + 1 + lda #1 + sta accu + jmp startup.exec + +W4: bit accu + 3 + bmi ibcmpf32gt + +ibcmpf32lt: + lda #$ff + sta accu + sta accu + 1 + jmp startup.exec +} + +#pragma bytecode(BC_BINOP_CMP_F32, inp_binop_cmp_f32) + +__asm inp_op_negate_f32 +{ + lda accu + 3 + eor #$80 + sta accu + 3 + jmp startup.exec +} + +#pragma bytecode(BC_OP_NEGATE_F32, inp_op_negate_f32) + +__asm uint16_to_float +{ + lda accu + ora accu + 1 + bne W1 + sta accu + 2 + sta accu + 3 + rts +W1: + ldx #$8e + lda accu + 1 + bmi W2 +L1: + dex + asl accu + rol + bpl L1 +W2: + asl + sta accu + 2 + lda accu + sta accu + 1 + txa + lsr + sta accu + 3 + lda #0 + sta accu + ror accu + 2 + rts +} + +__asm sint16_to_float +{ + bit accu + 1 + bmi W1 + jmp uint16_to_float +W1: + sec + lda #0 + sbc accu + sta accu + lda #0 + sbc accu + 1 + sta accu + 1 + jsr uint16_to_float + lda accu + 3 + ora #$80 + sta accu + 3 + rts +} + + +__asm inp_conv_u16_f32 +{ + jsr uint16_to_float + jmp startup.exec +} + +#pragma bytecode(BC_CONV_U16_F32, inp_conv_u16_f32) + +__asm inp_conv_i16_f32 +{ + jsr sint16_to_float + jmp startup.exec +} + +#pragma bytecode(BC_CONV_I16_F32, inp_conv_i16_f32) + +__asm uint32_to_float +{ + lda accu + ora accu + 1 + ora accu + 2 + ora accu + 3 + bne W1 + rts +W1: + ldx #$9e + lda accu + 3 + bmi W2 +L1: + dex + asl accu + rol accu + 1 + rol accu + 2 + rol + bpl L1 +W2: + bit accu + bpl W3 + // check rounding + inc accu + 1 + bne W3 + inc accu + 2 + bne W3 + clc + adc #1 + bcc W3 + lsr + ror accu + 2 + ror accu + 1 + inx +W3: + asl + ldy accu + 1 + sty accu + ldy accu + 2 + sty accu + 1 + sta accu + 2 + + txa + lsr + sta accu + 3 + ror accu + 2 + rts +} + +__asm sint32_to_float +{ + bit accu + 3 + bmi W1 + jmp uint32_to_float +W1: + sec + lda #0 + sbc accu + sta accu + lda #0 + sbc accu + 1 + sta accu + 1 + lda #0 + sbc accu + 2 + sta accu + 2 + lda #0 + sbc accu + 3 + sta accu + 3 + jsr uint32_to_float + lda accu + 3 + ora #$80 + sta accu + 3 + rts +} + + +__asm inp_conv_u32_f32 +{ + sty tmpy + jsr uint32_to_float + ldy tmpy + rts +} + +#pragma bytecode(BC_CONV_U32_F32, inp_conv_u32_f32) + +__asm inp_conv_i32_f32 +{ + sty tmpy + jsr sint32_to_float + ldy tmpy + rts +} + +#pragma bytecode(BC_CONV_I32_F32, inp_conv_i32_f32) + +__asm f32_to_i16 +{ + jsr freg.split_aexp + lda tmp + 4 + cmp #$7f + bcs W1 + lda #0 + sta accu + sta accu + 1 + rts +W1: +// sec // carry is set + sbc #$8e + bcc W2 + bit accu + 3 + bmi W5 + lda #$ff + sta accu + lda #$7f + sta accu + 1 + rts +W5: + lda #$00 + sta accu + lda #$80 + sta accu + 1 + rts +W2: + tax + lda accu + 1 +L1: + lsr accu + 2 + ror + inx + bne L1 +W3: + bit accu + 3 + bpl W4 + + sec + eor #$ff + adc #0 + sta accu + lda #0 + sbc accu + 2 + sta accu + 1 + rts +W4: + sta accu + lda accu + 2 + sta accu + 1 + rts +} + +__asm inp_conv_f32_i16 +{ + jsr f32_to_i16 + jmp startup.exec +} + +#pragma bytecode(BC_CONV_F32_I16, inp_conv_f32_i16) + +__asm f32_to_u16 +{ + jsr freg.split_aexp + lda tmp + 4 + cmp #$7f + bcs W1 + lda #0 +W0: + sta accu + sta accu + 1 + rts +W1: +// sec // carry is set + sbc #$8e + bcc W3 + beq W4 + lda #$ff + bne W0 +W3: + tax + lda accu + 1 +L1: + lsr accu + 2 + ror + inx + bne L1 +W2: + sta accu + lda accu + 2 + sta accu + 1 + + rts +W4: + lda accu + 1 + bcs W2 +} + +__asm inp_conv_f32_u16 +{ + jsr f32_to_u16 + jmp startup.exec +} + +#pragma bytecode(BC_CONV_F32_U16, inp_conv_f32_u16) + +__asm f32_to_u32 +{ + jsr freg.split_aexp + lda tmp + 4 + cmp #$7f + bcs W1 + lda #0 +F0: + sta accu + sta accu + 1 + sta accu + 2 + sta accu + 3 + rts +W1: + sec + sbc #$9e + beq W2 + bcc W3 + lda #$ff + bne F0 + +W3: + tax + + lda #0 +L1: + lsr accu + 2 + ror accu + 1 + ror accu + 0 + ror + inx + bne L1 + +W2: + ldx accu + 2 + stx accu + 3 + ldx accu + 1 + stx accu + 2 + ldx accu + stx accu + 1 + sta accu + + rts +} + +__asm f32_to_i32 +{ + lda accu + 3 + bmi W1 + jmp f32_to_u32 +W1: + jsr f32_to_u32 + + sec + lda #0 + sbc accu + sta accu + lda #0 + sbc accu + 1 + sta accu + 1 + lda #0 + sbc accu + 2 + sta accu + 2 + lda #0 + sbc accu + 3 + sta accu + 3 + + rts +} + +__asm inp_conv_f32_i32 +{ + jmp f32_to_i32 +} + +#pragma bytecode(BC_CONV_F32_I32, inp_conv_f32_i32) + +__asm inp_conv_f32_u32 +{ + jmp f32_to_u32 +} + +#pragma bytecode(BC_CONV_F32_U32, inp_conv_f32_u32) + +__asm inp_op_abs_f32 +{ + lda accu + 3 + and #$7f + sta accu + 3 + jmp startup.exec +} + +#pragma bytecode(BC_OP_ABS_F32, inp_op_abs_f32) + +unsigned char ubitmask[8] = {0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff}; + +unsigned char bitshift[56] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +#pragma runtime(bitshift, bitshift) + +__asm fround { +ffloor: + bit accu + 3 + bpl frdown + bmi frup + +fceil: + bit accu + 3 + bmi frdown + bpl frup + +frdzero: + lda #0 + sta accu + sta accu + 1 + sta accu + 2 + sta accu + 3 + rts + +frdown: + lda tmp + 4 + cmp #$7f + bcc frdzero + cmp #$87 + bcc frd1 + cmp #$8f + bcc frd2 + cmp #$97 + bcs frd3 + + sec + sbc #$8f + tax + lda accu + and ubitmask, x + sta accu + + jmp frd3 +frd1: + sec + sbc #$7f + tax + lda accu + 2 + and ubitmask, x + sta accu + 2 + lda #0 + sta accu + sta accu + 1 + + jmp frd3 +frd2: + sec + sbc #$87 + tax + lda accu + 1 + and ubitmask, x + sta accu + 1 + lda #0 + sta accu + + jmp frd3 + +frd3: + jmp freg.merge_aexp + +frone: + lda #$7f + sta tmp + 4 + lda #0 + sta accu + 0 + sta accu + 1 + lda #$80 + sta accu + 2 + jmp freg.merge_aexp + +frup: + lda accu + ora accu + 1 + ora accu + 2 + beq frdzero + lda tmp + 4 + cmp #$7f + bcc frone + cmp #$87 + bcc fru1 + cmp #$8f + bcc fru2 + cmp #$97 + bcs fru3 + + sec + sbc #$8f + tax + + clc + lda ubitmask, x + eor #$ff + adc accu + sta accu + lda #0 + adc accu + 1 + sta accu + 1 + lda #0 + adc accu + 2 + bcc W1 + ror + ror accu + 1 + ror accu + inc tmp + 4 +W1: sta accu + 2 + jmp frdown +fru1: + sec + sbc #$7f + tax + + clc + lda #$ff + adc accu + lda #$ff + adc accu + 1 + lda ubitmask, x + eor #$ff + adc accu + 2 + bcc W2 + ror + ror accu + 1 + ror accu + inc tmp + 4 +W2: sta accu + 2 + jmp frdown +fru2: + sec + sbc #$87 + tax + + clc + lda #$ff + adc accu + lda ubitmask, x + eor #$ff + adc accu + 1 + sta accu + 1 + lda #0 + adc accu + 2 + bcc W3 + ror + ror accu + 1 + ror accu + inc tmp + 4 +W3: sta accu + 2 + jmp frdown +fru3: + jmp freg.merge_aexp +} + +__asm store32 +{ + lda accu + 0 + sta $00, x + lda accu + 1 + sta $01, x + lda accu + 2 + sta $02, x + lda accu + 3 + sta $03, x + rts +} + +__asm load32 +{ + lda $00, x + sta accu + 0 + lda $01, x + sta accu + 1 + lda $02, x + sta accu + 2 + lda $03, x + sta accu + 3 + rts +} + +#pragma runtime(fsplita, freg.split_aexp) +#pragma runtime(fsplitt, freg.split_texp) +#pragma runtime(fsplitx, freg.split_xexp) +#pragma runtime(fmergea, freg.merge_aexp) +#pragma runtime(fadd, faddsub.fadd) +#pragma runtime(fsub, faddsub.fsub) +#pragma runtime(fmul, crt_fmul) +#pragma runtime(fdiv, crt_fdiv) +#pragma runtime(fcmp, crt_fcmp) +#pragma runtime(ffromi, sint16_to_float) +#pragma runtime(ffromu, uint16_to_float) +#pragma runtime(ftoi, f32_to_i16) +#pragma runtime(ftou, f32_to_u16) +#pragma runtime(ffloor, fround.ffloor) +#pragma runtime(fceil, fround.fceil) +#pragma runtime(ffromli, sint32_to_float) +#pragma runtime(ffromlu, uint32_to_float) +#pragma runtime(ftoli, f32_to_i32) +#pragma runtime(ftolu, f32_to_u32) +#pragma runtime(store32, store32) +#pragma runtime(load32, load32) + +__asm inp_op_floor_f32 +{ + jsr freg.split_aexp + bit accu + 3 + bpl W1 + jsr fround.frup + jmp startup.exec +W1: jsr fround.frdown + jmp startup.exec +} + +#pragma bytecode(BC_OP_FLOOR_F32, inp_op_floor_f32) + + +__asm inp_op_ceil_f32 +{ + jsr freg.split_aexp + bit accu + 3 + bpl W1 + jsr fround.frdown + jmp startup.exec +W1: jsr fround.frup + jmp startup.exec +} + +#pragma bytecode(BC_OP_CEIL_F32, inp_op_ceil_f32) + +__asm inp_op_extrt +{ + lda (ip), y + iny + sta _c1 + 1 + lda (ip), y + iny + sta _c1 + 2 + lda (ip), y + iny + tax + sty tmpy +_c1: jsr $0000 + ldy tmpy + jmp startup.exec +} + +#pragma bytecode(BC_EXTRT, inp_op_extrt) + + +__asm inp_op_ext_u16 +{ + lda #0 + sta accu + 2 + sta accu + 3 +} + +#pragma bytecode(BC_CONV_U16_U32, inp_op_ext_u16) + + +__asm inp_op_ext_s16 +{ + lda accu + 1 + ora #$7f + bmi w1 + lda #0 +w1: + sta accu + 2 + sta accu + 3 +} + +#pragma bytecode(BC_CONV_I16_I32, inp_op_ext_s16) + +__asm inp_op_invert_32 +{ + lda accu + 0 + eor #$ff + sta accu + 0 + lda accu + 1 + eor #$ff + sta accu + 1 + lda accu + 2 + eor #$ff + sta accu + 2 + lda accu + 3 + eor #$ff + sta accu + 3 +} + +#pragma bytecode(BC_OP_INVERT_32, inp_op_invert_32) + + +#pragma bytecode(BC_OP_NEGATE_32, negaccu32) + + +__asm inp_op_addr_32 +{ + clc + lda accu + 0 + adc $00, x + sta accu + 0 + lda accu + 1 + adc $01, x + sta accu + 1 + lda accu + 2 + adc $02, x + sta accu + 2 + lda accu + 3 + adc $03, x + sta accu + 3 +} + +#pragma bytecode(BC_BINOP_ADD_L32, inp_op_addr_32) + +__asm inp_op_subr_32 +{ + sec + lda accu + 0 + sbc $00, x + sta accu + 0 + lda accu + 1 + sbc $01, x + sta accu + 1 + lda accu + 2 + sbc $02, x + sta accu + 2 + lda accu + 3 + sbc $03, x + sta accu + 3 +} + +#pragma bytecode(BC_BINOP_SUB_L32, inp_op_subr_32) + +__asm inp_op_andr_32 +{ + lda accu + 0 + and $00, x + sta accu + 0 + lda accu + 1 + and $01, x + sta accu + 1 + lda accu + 2 + and $02, x + sta accu + 2 + lda accu + 3 + and $03, x + sta accu + 3 +} + +#pragma bytecode(BC_BINOP_AND_L32, inp_op_andr_32) + +__asm inp_op_orr_32 +{ + lda accu + 0 + ora $00, x + sta accu + 0 + lda accu + 1 + ora $01, x + sta accu + 1 + lda accu + 2 + ora $02, x + sta accu + 2 + lda accu + 3 + ora $03, x + sta accu + 3 +} + +#pragma bytecode(BC_BINOP_OR_L32, inp_op_orr_32) + +__asm inp_op_xorr_32 +{ + lda accu + 0 + eor $00, x + sta accu + 0 + lda accu + 1 + eor $01, x + sta accu + 1 + lda accu + 2 + eor $02, x + sta accu + 2 + lda accu + 3 + eor $03, x + sta accu + 3 +} + +#pragma bytecode(BC_BINOP_XOR_L32, inp_op_xorr_32) + +__asm inp_op_mulr_32 +{ + lda #0 + sta tmp + 4 + sta tmp + 5 + sta tmp + 6 + sta tmp + 7 + + lda $00, x + sta tmp + 0 + lda $01, x + sta tmp + 1 + lda $02, x + sta tmp + 2 + lda $03, x + sta tmp + 3 + ldx #32 +L1: lsr tmp + 3 + ror tmp + 2 + ror tmp + 1 + ror tmp + 0 + bcc W1 + clc + lda tmp + 4 + adc accu + sta tmp + 4 + lda tmp + 5 + adc accu + 1 + sta tmp + 5 + lda tmp + 6 + adc accu + 2 + sta tmp + 6 + lda tmp + 7 + adc accu + 3 + sta tmp + 7 +W1: asl accu + rol accu + 1 + rol accu + 2 + rol accu + 3 + dex + bne L1 + lda tmp + 4 + sta accu + lda tmp + 5 + sta accu + 1 + lda tmp + 6 + sta accu + 2 + lda tmp + 7 + sta accu + 3 +} + +#pragma bytecode(BC_BINOP_MUL_L32, inp_op_mulr_32) + +__asm inp_binop_div_u32 +{ + lda $00, x + sta tmp + 0 + lda $01, x + sta tmp + 1 + lda $02, x + sta tmp + 2 + lda $03, x + sta tmp + 3 + jsr divmod32 +} + +#pragma bytecode(BC_BINOP_DIV_U32, inp_binop_div_u32) + +__asm inp_binop_mod_u32 +{ + lda $00, x + sta tmp + 0 + lda $01, x + sta tmp + 1 + lda $02, x + sta tmp + 2 + lda $03, x + sta tmp + 3 + jsr divmod32 + lda tmp + 4 + sta accu + lda tmp + 5 + sta accu + 1 + lda tmp + 6 + sta accu + 2 + lda tmp + 7 + sta accu + 3 +} + +#pragma bytecode(BC_BINOP_MOD_U32, inp_binop_mod_u32) + +__asm inp_binop_div_s32 +{ + lda $00, x + sta tmp + 0 + lda $01, x + sta tmp + 1 + lda $02, x + sta tmp + 2 + lda $03, x + sta tmp + 3 + bit accu + 3 + bpl L1 + jsr negaccu32 + bit tmp + 3 + bpl L2 + jsr negtmp32 +L3: jmp divmod32 +L1: bit tmp + 3 + bpl L3 + jsr negtmp32 +L2: jsr divmod32 + jsr negaccu32 +} + +#pragma bytecode(BC_BINOP_DIV_I32, inp_binop_div_s32) + +__asm inp_binop_mod_s32 +{ + lda $00, x + sta tmp + 0 + lda $01, x + sta tmp + 1 + lda $02, x + sta tmp + 2 + lda $03, x + sta tmp + 3 + bit accu + 3 + bpl L1 + jsr negaccu32 + bit tmp + 3 + bpl L3 + jsr negtmp32 +L3: jsr divmod32 + lda tmp + 4 + sta accu + lda tmp + 5 + sta accu + 1 + lda tmp + 6 + sta accu + 2 + lda tmp + 7 + sta accu + 3 + jmp negaccu32 +L1: bit tmp + 3 + bpl L2 + jsr negtmp32 +L2: jsr divmod32 + lda tmp + 4 + sta accu + lda tmp + 5 + sta accu + 1 + lda tmp + 6 + sta accu + 2 + lda tmp + 7 + sta accu + 3 +} + +#pragma bytecode(BC_BINOP_MOD_I32, inp_binop_mod_s32) + +__asm inp_op_shl_l32 +{ + lda $00, x + and #31 + beq W1 + tax + lda accu + 0 +L1: asl + rol accu + 1 + rol accu + 2 + rol accu + 3 + dex + bne L1 + sta accu + 0 +W1: +} + +#pragma bytecode(BC_BINOP_SHL_L32, inp_op_shl_l32) + + +__asm inp_op_shr_u32 +{ + lda $00, x + and #31 + beq W1 + tax + lda accu + 3 +L1: lsr + ror accu + 2 + ror accu + 1 + ror accu + 0 + dex + bne L1 + sta accu + 3 +W1: +} + +#pragma bytecode(BC_BINOP_SHR_U32, inp_op_shr_u32) + +__asm inp_op_shr_s32 +{ + lda $00, x + and #31 + beq W1 + tax + lda accu + 3 +L1: cmp #$80 + ror + ror accu + 2 + ror accu + 1 + ror accu + 0 + dex + bne L1 + sta accu + 3 +W1: +} + +#pragma bytecode(BC_BINOP_SHR_I32, inp_op_shr_s32) + +__asm inp_op_cmp_u32 +{ + lda $03, x + cmp accu + 3 + bne cmpne + lda $02, x + cmp accu + 2 + bne cmpne + lda $01, x + cmp accu + 1 + bne cmpne + lda $00 , x + cmp accu + bne cmpne + + lda #0 + sta accu + sta accu + 1 + rts +cmp_lt: + lda #$ff + sta accu + sta accu +1 + rts +cmpne: + bcc cmp_lt + lda #1 + sta accu + lda #0 + sta accu + 1 + rts +} + +#pragma bytecode(BC_BINOP_CMP_U32, inp_op_cmp_u32) + +__asm inp_op_cmp_s32 +{ + lda accu + 3 + eor #$80 + sta accu + 3 + lda $03, x + eor #$80 + cmp accu + 3 + bne cmpne + lda $02, x + cmp accu + 2 + bne cmpne + lda $01, x + cmp accu + 1 + bne cmpne + lda $00 , x + cmp accu + bne cmpne + + lda #0 + sta accu + sta accu + 1 + rts +cmp_lt: + lda #$ff + sta accu + sta accu +1 + rts +cmpne: + bcc cmp_lt + lda #1 + sta accu + lda #0 + sta accu + 1 + rts +} + +#pragma bytecode(BC_BINOP_CMP_S32, inp_op_cmp_s32) + + +void HeapStart, HeapEnd; + +struct Heap { + Heap * next, * end; +} HeapNode; + +#pragma section(heap, 0x0000, HeapStart, HeapEnd) + +__asm crt_malloc +{ + // make room for two additional bytes + // to store pointer to end of used memory + // in case of heap check we add six bytes to + // add some guards + // round up to multiple of four bytes to + // keep heap aligned + + clc + lda accu + 0 +#ifdef HEAPCHECK + adc #2 + 4 + 4 + 3 +#else + adc #2 + 3 +#endif + and #$fc + sta tmp + lda accu + 1 + adc #0 + sta tmp + 1 + + // check if heap is initialized + + lda HeapNode + 2 + bne hasHeap + + // initialize heap + + // set next pointer to null + lda #0 + sta HeapStart + 2 + sta HeapStart + 3 + + // set size of dummy node to null + inc HeapNode + 2 + + // set next pointer of dummy node to first free heap block + lda #HeapStart + sta HeapNode + 1 + + // set end of memory block to end of heap + sec + lda #HeapEnd + sbc #0 + sta HeapStart + 5 + +hasHeap: + // remember address of pointer to this + // heap block, to correct if block is a + // perfect fit + + lda #HeapNode + + // Now loop over free nodes, until we find a match +loop: + sta accu + 2 + stx accu + 3 + + // next heap block + + // calculate potential end of block + + clc + ldy #0 + lda (accu + 2), y + sta accu + 0 + adc tmp + sta tmp + 2 + iny + lda (accu + 2), y + sta accu + 1 + + // exit if out of blocks + beq hempty + adc tmp + 1 + sta tmp + 3 + + // exit if overflowing memory + bcs hzempty + + + // Check if in range of current free block + + ldy #2 + lda (accu), y + cmp tmp + 2 + iny + lda (accu), y + sbc tmp + 3 + bcs avail + + // move current block pointer to prev pointer + + lda accu + ldx accu + 1 + jmp loop +hzempty: + lda #0 + sta accu + sta accu + 1 +hempty: + // no more heap blocks +#ifdef HEAPCHECK + byt $02 +#endif + rts + +avail: + // calculate new end of block + + lda tmp + 2 + sta tmp + 4 + lda tmp + 3 + sta tmp + 5 + + // compare with end of free block + + ldy #2 + lda tmp + 4 + cmp (accu), y + bne nofit + iny + lda tmp + 5 + cmp (accu), y + bne nofit + + // perfect fit, so have previous block point to + // next free block + + ldy #0 + lda (accu), y + sta (accu + 2), y + iny + lda (accu), y + sta (accu + 2), y + jmp found + +nofit: + // set next link in new link block + + ldy #0 + lda (accu), y + sta (tmp + 4), y + lda tmp + 4 + sta (accu + 2), y + iny + lda (accu), y + sta (tmp + 4), y + lda tmp + 5 + sta (accu + 2), y + + // set new end of remaining block + iny + lda (accu), y + sta (tmp + 4), y + iny + lda (accu), y + sta (tmp + 4), y + +found: + // remember end of allocated block + + ldy #0 + lda tmp + 2 + sta (accu), y + iny + lda tmp + 3 + sta (accu), y + +#ifdef HEAPCHECK + + // add guard range to memory segment + + ldy #2 + lda #$bd + sta (accu), y + iny + sta (accu), y + iny + sta (accu), y + iny + sta (accu), y + + sec + lda tmp + 2 + sbc #4 + sta tmp + 2 + bcs hc1 + dec tmp + 3 +hc1: + lda #$be + ldy #0 + sta (tmp + 2), y + iny + sta (tmp + 2), y + iny + sta (tmp + 2), y + iny + sta (tmp + 2), y + + clc + lda accu + adc #6 +#else + // advance by two bytes to skip size + clc + lda accu + adc #2 +#endif + sta accu + bcc hc2 + inc accu + 1 +hc2: + rts +} + +__asm inp_malloc +{ + sty tmpy + jsr crt_malloc + ldy tmpy + jmp startup.exec +} + +#pragma bytecode(BC_MALLOC, inp_malloc) + +__asm crt_free +{ + + // check nullptr free + + lda accu + ora accu + 1 + bne notnull + rts +notnull: + + // two bytes back to fix remembered end of block + + sec + lda accu +#ifdef HEAPCHECK + and #3 + bne hfail + lda accu + sbc #6 +#else + sbc #2 +#endif + sta accu + bcs fc1 + dec accu + 1 +fc1: + +#ifdef HEAPCHECK + ldy #2 + lda #$bd + cmp (accu), y + bne hfail + iny + cmp (accu), y + bne hfail + iny + cmp (accu), y + bne hfail + iny + cmp (accu), y + bne hfail + + ldy #0 + sec + lda (accu), y + sbc #4 + sta tmp + 0 + iny + lda (accu), y + sbc #0 + sta tmp + 1 + + ldy #0 + lda #$be + cmp (tmp), y + bne hfail + iny + cmp (tmp), y + bne hfail + iny + cmp (tmp), y + bne hfail + iny + cmp (tmp), y + bne hfail +#endif + +#ifdef HEAPCHECK + lda accu + 1 + ldx accu + + cmp #>HeapStart + bcc hfail + bne hchk1 + cpx #HeapEnd + bcc hchk2 + bne hfail + cpx #HeapNode + +loop: + sta tmp + 2 + stx tmp + 3 + + // check if end of heap + + ldy #1 + lda (tmp + 2), y + beq noend + tax + dey + lda (tmp + 2), y + + // Check if next block is behind the block to free + + cpx accu + 3 + bcc loop + bne noend + + cmp accu + 2 + bcc loop + bne noend + + // The end of the block to free matches the + // start of the next free block + + // Pointer to next next block + + ldy #0 + lda (accu + 2), y + sta (accu), y + iny + lda (accu + 2), y + sta (accu), y + + // Append free space to new block + + iny + lda (accu + 2), y + sta (accu), y + iny + lda (accu + 2), y + sta (accu), y + jmp start + +noend: + // Link to next block + ldy #0 + lda (tmp + 2), y + sta (accu), y + iny + lda (tmp + 2), y + sta (accu), y + + // End of new free block + iny + lda accu + 2 + sta (accu), y + iny + lda accu + 3 + sta (accu), y + +start: + // Check if new block matches end of previous block + + ldy #2 + lda (tmp + 2), y + cmp accu + bne nostart + iny + lda (tmp + 2), y + cmp accu + 1 + bne nostart + + // If so, increase the size of previous block and link + // to free block after + + ldy #0 + lda (accu), y + sta (tmp + 2), y + iny + lda (accu), y + sta (tmp + 2), y + + iny + lda (accu), y + sta (tmp + 2), y + iny + lda (accu), y + sta (tmp + 2), y + + rts + +nostart: + // Link to new free block + + ldy #0 + lda accu + sta (tmp + 2), y + iny + lda accu + 1 + sta (tmp + 2), y + + rts +} + +__asm inp_free +{ + sty tmpy + jsr crt_free + ldy tmpy + jmp startup.exec +} + +#pragma bytecode(BC_FREE, inp_free) + +__asm crt_breakpoint +{ + rts +} + +#pragma runtime(malloc, crt_malloc) +#pragma runtime(free, crt_free) +#pragma runtime(breakpoint, crt_breakpoint) + +#if 0 + +void * malloc(unsigned int size) +{ + size = (size + 7) & ~3; + if (!freeHeapInit) + { + freeHeap = (Heap *)&HeapStart; + freeHeap->next = nullptr; + freeHeap->size = (unsigned int)&HeapEnd - (unsigned int)&HeapStart; + freeHeapInit = true; + } + + Heap * pheap = nullptr, * heap = freeHeap; + while (heap) + { + if (size <= heap->size) + { + if (size == heap->size) + { + if (pheap) + pheap->next = heap->next; + else + freeHeap = heap->next; + } + else + { + Heap * nheap = (Heap *)((int)heap + size); + nheap->size = heap->size - size; + nheap->next = heap->next; + if (pheap) + pheap->next = nheap; + else + freeHeap = nheap; + heap->size = size; + } + + return (void *)((int)heap + 2); + } + pheap = heap; + heap = heap->next; + } + + return nullptr; +} + +void free(void * ptr) +{ + if (!ptr) + return; + + Heap * fheap = (Heap *)((int)ptr - 2); + Heap * eheap = (Heap *)((int)ptr - 2 + fheap->size); + + if (freeHeap) + { + if (eheap == freeHeap) + { + fheap->size += freeHeap->size; + fheap->next = freeHeap->next; + freeHeap = fheap; + } + else if (eheap < freeHeap) + { + fheap->next = freeHeap; + freeHeap = fheap; + } + else + { + Heap * pheap = freeHeap; + while (pheap->next && pheap->next < fheap) + pheap = pheap->next; + Heap * nheap = (Heap *)((int)pheap + pheap->size); + + if (nheap == fheap) + { + pheap->size += fheap->size; + if (pheap->next == eheap) + { + pheap->size += pheap->next->size; + pheap->next = pheap->next->next; + } + } + else if (pheap->next == eheap) + { + fheap->next = pheap->next->next; + fheap->size += pheap->next->size; + pheap->next = fheap; + } + else + { + fheap->next = pheap->next; + pheap->next = fheap; + } + } + } + else + { + freeHeap = fheap; + freeHeap->next = nullptr; + } +} +#endif diff --git a/oscar64/include/crt.h b/oscar64/include/crt.h new file mode 100644 index 0000000..8d428ac --- /dev/null +++ b/oscar64/include/crt.h @@ -0,0 +1,196 @@ +#ifndef CRT_H +#define CRT_H + +extern char spentry; + +enum ByteCode +{ + BC_NOP, + BC_EXIT, + + BC_CONST_8, + BC_CONST_P8, + BC_CONST_N8, + BC_CONST_16, + BC_CONST_32, + + BC_LOAD_REG_8, + BC_STORE_REG_8, + BC_LOAD_REG_16, + BC_STORE_REG_16, + BC_ADDR_REG, + BC_LOAD_REG_32, + BC_STORE_REG_32, + + BC_LOAD_ABS_8, + BC_LOAD_ABS_U8, + BC_LOAD_ABS_16, + BC_LOAD_ABS_32, + BC_LOAD_ABS_ADDR, + + BC_STORE_ABS_8, + BC_STORE_ABS_16, + BC_STORE_ABS_32, + + BC_LEA_ABS, + BC_LEA_ABS_INDEX, + BC_LEA_ABS_INDEX_U8, + BC_LEA_ACCU_INDEX, + + BC_LOAD_LOCAL_8, + BC_LOAD_LOCAL_U8, + BC_LOAD_LOCAL_16, + BC_LOAD_LOCAL_32, + + BC_STORE_LOCAL_8, + BC_STORE_LOCAL_16, + BC_STORE_LOCAL_32, + + BC_LEA_LOCAL, + + BC_STORE_FRAME_8, + BC_STORE_FRAME_16, + BC_STORE_FRAME_32, + + BC_LEA_FRAME, + + BC_LOAD_ADDR_8, + BC_LOAD_ADDR_U8, + BC_LOAD_ADDR_16, + BC_LOAD_ADDR_32, + + BC_STORE_ADDR_8, + BC_STORE_ADDR_16, + BC_STORE_ADDR_32, + + BC_BINOP_ADDR_16, + BC_BINOP_SUBR_16, + BC_BINOP_ANDR_16, + BC_BINOP_ORR_16, + BC_BINOP_XORR_16, + BC_BINOP_MULR_16, + BC_BINOP_DIVR_U16, + BC_BINOP_MODR_U16, + BC_BINOP_DIVR_I16, + BC_BINOP_MODR_I16, + BC_BINOP_SHLR_16, + BC_BINOP_SHRR_U16, + BC_BINOP_SHRR_I16, + + BC_BINOP_ADDA_16, + + BC_BINOP_ADDI_16, + BC_BINOP_SUBI_16, + BC_BINOP_ANDI_16, + BC_BINOP_ORI_16, + BC_BINOP_MULI8_16, + + BC_BINOP_ADDI_8, + BC_BINOP_ANDI_8, + BC_BINOP_ORI_8, + + BC_BINOP_SHLI_16, + BC_BINOP_SHRI_U16, + BC_BINOP_SHRI_I16, + + BC_BINOP_CMPUR_16, + BC_BINOP_CMPSR_16, + + BC_BINOP_CMPUI_16, + BC_BINOP_CMPSI_16, + + BC_BINOP_CMPUR_8, + BC_BINOP_CMPSR_8, + + BC_BINOP_CMPUI_8, + BC_BINOP_CMPSI_8, + + BC_OP_NEGATE_16, + BC_OP_INVERT_16, + + BC_BINOP_ADD_F32, + BC_BINOP_SUB_F32, + BC_BINOP_MUL_F32, + BC_BINOP_DIV_F32, + BC_BINOP_CMP_F32, + BC_OP_NEGATE_F32, + BC_OP_ABS_F32, + BC_OP_FLOOR_F32, + BC_OP_CEIL_F32, + + BC_CONV_U16_F32, + BC_CONV_I16_F32, + BC_CONV_F32_U16, + BC_CONV_F32_I16, + + BC_CONV_I8_I16, + + BC_JUMPS, + BC_BRANCHS_EQ, + BC_BRANCHS_NE, + BC_BRANCHS_GT, + BC_BRANCHS_GE, + BC_BRANCHS_LT, + BC_BRANCHS_LE, + + BC_JUMPF, + BC_BRANCHF_EQ, + BC_BRANCHF_NE, + BC_BRANCHF_GT, + BC_BRANCHF_GE, + BC_BRANCHF_LT, + BC_BRANCHF_LE, + + BC_LOOP_U8, + BC_MALLOC, + BC_FREE, + BC_FILL, + BC_FILL_LONG, + BC_UNUSED_6, + + BC_JSR, + + BC_NATIVE = 0x75, + + BC_ENTER, + BC_RETURN, + BC_CALL_ADDR, + BC_CALL_ABS, + BC_PUSH_FRAME, + BC_POP_FRAME, + + BC_COPY, + BC_COPY_LONG, + BC_STRCPY, + + BC_EXTRT, + + BC_CONV_I16_I32 = 0x80, + BC_CONV_U16_U32, + + BC_OP_NEGATE_32, + BC_OP_INVERT_32, + + BC_BINOP_ADD_L32, + BC_BINOP_SUB_L32, + BC_BINOP_AND_L32, + BC_BINOP_OR_L32, + BC_BINOP_XOR_L32, + BC_BINOP_MUL_L32, + BC_BINOP_DIV_U32, + BC_BINOP_MOD_U32, + BC_BINOP_DIV_I32, + BC_BINOP_MOD_I32, + BC_BINOP_SHL_L32, + BC_BINOP_SHR_U32, + BC_BINOP_SHR_I32, + BC_BINOP_CMP_U32, + BC_BINOP_CMP_S32, + + BC_CONV_U32_F32, + BC_CONV_I32_F32, + BC_CONV_F32_U32, + BC_CONV_F32_I32, +}; + +#endif diff --git a/oscar64/include/ctype.c b/oscar64/include/ctype.c new file mode 100644 index 0000000..650cb36 --- /dev/null +++ b/oscar64/include/ctype.c @@ -0,0 +1,105 @@ +#include "ctype.h" +#include "conio.h" + +#define CC_CTRL 0x00 +#define CC_BREAK 0x01 +#define CC_SPACE 0x02 +#define CC_DIGIT 0x04 +#define CC_LOWER 0x08 +#define CC_UPPER 0x10 +#define CC_HEX 0x20 +#define CC_PUNCT 0x40 + +static const char _cinfo[128] = { + [0 ... 8] = CC_CTRL, + [9] = CC_SPACE, + [10 ... 13] = CC_BREAK, + [14 ... 31] = CC_CTRL, + [32] = CC_SPACE, + [33 ... 47] = CC_PUNCT, + [48 ... 57] = CC_DIGIT, + [58 ... 64] = CC_PUNCT, + [65 ... 70] = CC_UPPER | CC_HEX, + [71 ... 90] = CC_UPPER, + [91 ... 96] = CC_PUNCT, + [97 ... 102] = CC_LOWER | CC_HEX, + [103 ... 122] = CC_LOWER, + [123 ... 126] = CC_PUNCT, + [127] = CC_CTRL +}; + +bool isctrnl(char c) +{ + return (c < 128) && _cinfo[c] == CC_CTRL; +} + +bool isprint(char c) +{ + return (c < 128) && _cinfo[c] != CC_CTRL; +} + +bool isspace(char c) +{ + return (c < 128) && (_cinfo[c] & (CC_SPACE | CC_BREAK)); +} + +bool isblank(char c) +{ + return (c < 128) && (_cinfo[c] & CC_SPACE); +} + +bool isgraph(char c) +{ + return (c < 128) && (_cinfo[c] & (CC_LOWER | CC_UPPER | CC_DIGIT | CC_PUNCT)); +} + +bool ispunct(char c) +{ + return (c < 128) && (_cinfo[c] & CC_PUNCT); +} + +bool isalnum(char c) +{ + return (c < 128) && (_cinfo[c] & (CC_LOWER | CC_UPPER | CC_DIGIT)); +} + +bool isalpha(char c) +{ + return (c < 128) && (_cinfo[c] & (CC_LOWER | CC_UPPER)); +} + +bool isupper(char c) +{ + return (c < 128) && (_cinfo[c] & CC_UPPER); +} + +bool islower(char c) +{ + return (c < 128) && (_cinfo[c] & CC_LOWER); +} + +bool isdigit(char c) +{ + return (c < 128) && (_cinfo[c] & CC_DIGIT); +} + +bool isxdigit(char c) +{ + return (c < 128) && (_cinfo[c] & CC_HEX); +} + +char tolower(char c) +{ + if (c >= 'A' && c <= 'Z') + return c + ('a' - 'A'); + else + return c; +} + +char toupper(char c) +{ + if (c >= 'a' && c <= 'z') + return c + ('A' - 'a'); + else + return c; +} diff --git a/oscar64/include/ctype.h b/oscar64/include/ctype.h new file mode 100644 index 0000000..f4f9a61 --- /dev/null +++ b/oscar64/include/ctype.h @@ -0,0 +1,35 @@ +#ifndef CTYPE_H +#define CTYPE_H + +inline bool isctrnl(char c); + +inline bool isprint(char c); + +inline bool isspace(char c); + +inline bool isblank(char c); + +inline bool isgraph(char c); + +inline bool ispunct(char c); + +inline bool isalnum(char c); + +inline bool isalpha(char c); + +inline bool isupper(char c); + +inline bool islower(char c); + +inline bool isdigit(char c); + +inline bool isxdigit(char c); + +char tolower(char c); + +char toupper(char c); + +#pragma compile("ctype.c") + +#endif + diff --git a/oscar64/include/cx16/vera.c b/oscar64/include/cx16/vera.c new file mode 100644 index 0000000..4fa2cb8 --- /dev/null +++ b/oscar64/include/cx16/vera.c @@ -0,0 +1,166 @@ +#include "vera.h" + +void vram_addr(unsigned long addr) +{ + vera.ctrl &= ~VERA_CTRL_ADDRSEL; + vera.addr = (unsigned)addr; + vera.addrh = (char)((addr >> 16) & 1) | 0x10; +} + +void vram_addr0(unsigned long addr) +{ + vera.ctrl &= ~VERA_CTRL_ADDRSEL; + vera.addr = (unsigned)addr; + vera.addrh = (char)((addr >> 16) & 1) | 0x00; +} + +void vram_addr2(unsigned long addr) +{ + vera.ctrl &= ~VERA_CTRL_ADDRSEL; + vera.addr = (unsigned)addr; + vera.addrh = (char)((addr >> 16) & 1) | 0x20; +} + +void vram_put(char data) +{ + vera.data0 = data; +} + +void vram_putw(unsigned data) +{ + vera.data0 = data & 0xff; + vera.data0 = data >> 8; +} + +char vram_get(void) +{ + return vera.data0; +} + +unsigned vram_getw(void) +{ + unsigned l = vera.data0; + unsigned h = vera.data0; + return (h << 8) | l; +} + + +void vram_put_at(unsigned long addr, char data) +{ + vram_addr(addr); + vram_put(data); +} + +char vram_get_at(unsigned long addr) +{ + vram_addr(addr); + return vram_get();; +} + +void vram_putn(unsigned long addr, const char * data, unsigned size) +{ + vram_addr(addr); + while(size > 0) + { + vram_put(*data++); + size--; + } +} + +void vram_getn(unsigned long addr, char * data, unsigned size) +{ + vram_addr(addr); + + while(size > 0) + { + *data++ = vram_get(); + size--; + } +} + +void vram_fill(unsigned long addr, char data, unsigned size) +{ + vram_addr(addr); + + while(size > 0) + { + vram_put(data); + size--; + } +} + + +void vera_spr_set(char spr, unsigned addr32, VERASpriteMode mode8, VERASpriteSize w, VERASpriteSize h, VERASpritePriority z, char pal) +{ + __assume(spr < 128); + + vram_addr(0x1fc00UL + spr * 8); + vram_putw(addr32 | (mode8 ? 0x8000 : 0x0000)); + vram_putw(0); + vram_putw(0); + vram_put(z << 2); + vram_put((h << 6) | (w << 4) | pal); +} + +void vera_spr_flip(char spr, bool fliph, bool flipv) +{ + __assume(spr < 128); + + vram_addr0(0x1fc00UL + spr * 8 + 6); + char b = vram_get() & 0xfc; + if (fliph) b |= 0x01; + if (flipv) b |= 0x02; + vram_put(b); +} + +void vera_spr_move(char spr, int x, int y) +{ + __assume(spr < 128); + + vram_addr(0x1fc00UL + spr * 8 + 2); + vram_putw(x); + vram_putw(y); +} + +void vera_spr_image(char spr, unsigned addr32) +{ + __assume(spr < 128); + + vram_addr(0x1fc00UL + spr * 8); + vram_put(addr32 & 0xff); + vera.addrh &= 0x0f; + char b = vram_get() & 0x80; + vram_put((addr32 >> 8) | b); +} + +void vera_pal_put(char index, unsigned color) +{ + vram_addr(0x1fa00ul + 2 * index); + vram_putw(color); +} + +unsigned vera_pal_get(char index) +{ + vram_addr(0x1fa00ul + 2 * index); + return vram_getw(); +} + +void vera_pal_putn(char index, const unsigned * color, unsigned size) +{ + vram_addr(0x1fa00ul + 2 * index); + while (size > 0) + { + vram_putw(*color++); + size--; + } +} + +void vera_pal_getn(char index, unsigned * color, unsigned size) +{ + vram_addr(0x1fa00ul + 2 * index); + while (size > 0) + { + *color++ = vram_getw(); + size--; + } +} diff --git a/oscar64/include/cx16/vera.h b/oscar64/include/cx16/vera.h new file mode 100644 index 0000000..6066501 --- /dev/null +++ b/oscar64/include/cx16/vera.h @@ -0,0 +1,157 @@ +#ifndef CX16_VERA_H +#define CX16_VERA_H + +// Thanks to crisps for providing the initial code for this library + +#include + +#define VERA_ADDRH_DECR 0x08 +#define VERA_ADDRH_INC 0xf0 + +#define VERA_CTRL_RESET 0x80 +#define VERA_CTRL_DCSEL 0x02 +#define VERA_CTRL_ADDRSEL 0x01 + +#define VERA_IRQ_LINE_8 0x80 +#define VERA_IRQ_AFLOW 0x08 +#define VERA_IRQ_SPRCOL 0x04 +#define VERA_IRQ_LINE 0x02 +#define VERA_IRQ_VSYNC 0x01 + +#define VERA_DCVIDEO_MODE_OFF 0x00 +#define VERA_DCVIDEO_MODE_VGA 0x01 +#define VERA_DCVIDEO_MODE_NTSC 0x02 +#define VERA_DCVIDEO_MODE_RGBI 0x03 + +#define VERA_DCVIDEO_NCHROMA 0x04 +#define VERA_DCVIDEO_LAYER0 0x10 +#define VERA_DCVIDEO_LAYER1 0x20 +#define VERA_DCVIDEO_SPRITES 0x40 + +#define VERA_LAYER_DEPTH_1 0x00 +#define VERA_LAYER_DEPTH_2 0x01 +#define VERA_LAYER_DEPTH_4 0x02 +#define VERA_LAYER_DEPTH_8 0x03 + +#define VERA_LAYER_BITMAP 0x04 +#define VERA_LAYER_T256C 0x08 +#define VERA_LAYER_WIDTH_32 0x00 +#define VERA_LAYER_WIDTH_64 0x10 +#define VERA_LAYER_WIDTH_128 0x20 +#define VERA_LAYER_WIDTH_256 0x30 +#define VERA_LAYER_HEIGHT_32 0x00 +#define VERA_LAYER_HEIGHT_64 0x40 +#define VERA_LAYER_HEIGHT_128 0x80 +#define VERA_LAYER_HEIGHT_256 0xc0 + +#define VERA_TILE_WIDTH_8 0x00 +#define VERA_TILE_WIDTH_16 0x01 +#define VERA_TILE_HEIGHT_8 0x00 +#define VERA_TILE_HEIGHT_16 0x02 + + +struct VERA +{ + volatile word addr; + volatile byte addrh; + volatile byte data0, data1; + + volatile byte ctrl; + volatile byte ien; + volatile byte isr; + volatile byte irqline; + + volatile byte dcvideo; + volatile byte dchscale; + volatile byte dcvscale; + volatile byte dcborder; + + volatile byte l0config; + volatile byte l0mapbase; + volatile byte l0tilebase; + volatile word l0hscroll; + volatile word l0vscroll; + + volatile byte l1config; + volatile byte l1mapbase; + volatile byte l1tilebase; + volatile word l1hscroll; + volatile word l1vscroll; + + volatile byte audioctrl; + volatile byte audiorate; + volatile byte audiodata; + + volatile byte spidata; + volatile byte spictrl; +}; + +enum VERASpriteMode +{ + VSPRMODE_4, + VSPRMODE_8 +}; + +enum VERASpriteSize +{ + VSPRSZIZE_8, + VSPRSZIZE_16, + VSPRSZIZE_32, + VSPRSZIZE_64 +}; + +enum VERASpritePriority +{ + VSPRPRI_OFF, + VSPRPRI_BACK, + VSPRPRI_MIDDLE, + VSPRPRI_FRONT +}; + +#define VERA_COLOR(r, g, b) (((unsigned)(r) << 8) | ((unsigned)(g) << 4) | (unsigned)(b)) + +#define vera (*(VERA *)0x9f20) + +inline void vram_addr(unsigned long addr); + +inline void vram_addr0(unsigned long addr); + +inline void vram_addr2(unsigned long addr); + +inline void vram_put(char data); + +inline void vram_putw(unsigned data); + +inline char vram_get(void); + +inline unsigned vram_getw(void); + +inline void vram_put_at(unsigned long addr, char data); + +inline char vram_get_at(unsigned long addr); + +void vram_putn(unsigned long addr, const char * data, unsigned size); + +void vram_getn(unsigned long addr, char * data, unsigned size); + +void vram_fill(unsigned long addr, char data, unsigned size); + +void vera_spr_set(char spr, unsigned addr32, VERASpriteMode mode8, VERASpriteSize w, VERASpriteSize h, VERASpritePriority z, char pal); + +void vera_spr_flip(char spr, bool fliph, bool flipv); + +void vera_spr_move(char spr, int x, int y); + +void vera_spr_image(char spr, unsigned addr32); + +void vera_pal_put(char index, unsigned color); + +unsigned vera_pal_get(char index); + +void vera_pal_putn(char index, const unsigned * color, unsigned size); + +void vera_pal_getn(char index, unsigned * color, unsigned size); + +#pragma compile("vera.c") + +#endif diff --git a/oscar64/include/fixmath.c b/oscar64/include/fixmath.c new file mode 100644 index 0000000..23d194e --- /dev/null +++ b/oscar64/include/fixmath.c @@ -0,0 +1,911 @@ +#include "fixmath.h" + +unsigned long lmul16u(unsigned x, unsigned y) +{ + __asm + { + lda #0 + sta accu + 2 + sta accu + 3 + + ldx #16 +L1: lsr x + 1 + ror x + bcc W1 + clc + lda accu + 2 + adc y + sta accu + 2 + lda accu + 3 + adc y + 1 + sta accu + 3 +W1: + ror accu + 3 + ror accu + 2 + ror accu + 1 + ror accu + dex + bne L1 + } +} + +long lmul16s(int x, int y) +{ + __asm + { + bit y + 1 + bpl W0 + + sec + lda #0 + sbc y + sta y + lda #0 + sbc y + 1 + sta y + 1 + + sec + lda #0 + sbc x + sta x + lda #0 + sbc x + 1 + sta x + 1 +W0: + ldx #15 + lda #0 + sta accu + 2 + +L1: lsr x + 1 + ror x + bcc W1 + tay + clc + lda accu + 2 + adc y + sta accu + 2 + tya + adc y + 1 +W1: + ror + ror accu + 2 + ror accu + 1 + ror accu + dex + bne L1 + + lsr x + bcc W2 + + tay + sec + lda accu + 2 + sbc y + sta accu + 2 + tya + sbc y + 1 + + sec +W2: + ror + ror accu + 2 + ror accu + 1 + ror accu + sta accu + 3 + + } +} + +inline int lmul12f4s(int x, int y) +{ + return (int)(lmul16s(x, y) >> 4); +} + +inline int lmul8f8s(int x, int y) +{ + return (int)(lmul16s(x, y) >> 8); +} + +inline unsigned long lsqr4f12s(int x) +{ + if (x < 0) x = -x; + return lmul16u(x, x); +} + +int lmul4f12s(int x, int y) +{ + __asm + { + sec + lda x + ror + sta accu + + lda #0 + sta accu + 1 + + bcc W4 +L2: + tay + clc + lda accu + 1 + adc y + sta accu + 1 + tya + adc y + 1 +W4: + ror + ror accu + 1 + + lsr accu + bcc W4 + bne L2 + + ldx x + 1 + stx accu + + ldx #7 + lsr accu + +L1: + bcc W1 + tay + clc + lda accu + 1 + adc y + sta accu + 1 + tya + adc y + 1 +W1: + ror + ror accu + 1 + ror accu + dex + bne L1 + + bcc W2 + + tay +// sec ; we know it is set here + lda accu + 1 + sbc y + sta accu + 1 + tya + sbc y + 1 +W2: + ror + ror accu + 1 + ror accu + + bit y + 1 + bpl W3 + + tax + sec + lda accu + 1 + sbc x + sta accu + 1 + txa + sbc x + 1 +W3: + lsr + ror accu + 1 + ror accu + + lsr + ror accu + 1 + ror accu + + lsr + ror accu + 1 + ror accu + + lsr + ror accu + 1 + ror accu + } +} + +unsigned ldiv16u(unsigned long x, unsigned y) +{ + __asm + { + lda #0 + sta accu + sta accu + 1 + + ldx #17 + L1: + sec + lda x + 2 + sbc y + tay + lda x + 3 + sbc y + 1 + bcc W1 + sta x + 3 + sty x + 2 + W1: + rol accu + rol accu + 1 + + asl x + rol x + 1 + rol x + 2 + rol x + 3 + + dex + beq E1 + bcc L1 + + lda x + 2 + sbc y + sta x + 2 + lda x + 3 + sbc y + 1 + sta x + 3 + sec + bcs W1 + E1: + } +} + +int ldiv16s(long x, int y) +{ + if (x < 0) + { + if (y < 0) + return ldiv16u(-x, - y); + else + return -ldiv16u(-x, y); + } + else if (y < 0) + return -ldiv16u(x, -y); + else + return ldiv16u(x, y); +} + +inline int ldiv12f4s(int x, int y) +{ + return (int)(ldiv16s((long)x << 4, y)); +} + +inline int ldiv8f8s(int x, int y) +{ + return (int)(ldiv16s((long)x << 8, y)); +} + +inline int ldiv4f12s(int x, int y) +{ + return (int)(ldiv16s((long)x << 12, y)); +} + +unsigned lmuldiv16u(unsigned a, unsigned b, unsigned c) +{ + __asm + { + lda #0 + sta __tmp + 2 + sta __tmp + 3 + + lda a + sec + T1: + ldy #8 + L1: + ror + bcc W1 + tax + clc + lda __tmp + 2 + adc b + sta __tmp + 2 + lda __tmp + 3 + adc b + 1 + sta __tmp + 3 + txa + W1: + ror __tmp + 3 + ror __tmp + 2 + dey + bne L1 + ror + bcc T2 + + sta __tmp + 0 + lda a + 1 + clc + bcc T1 + + T2: + sec + L3: + sta __tmp + 1 + ldx #8 + L2: + rol __tmp + 1 + rol __tmp + 2 + rol __tmp + 3 + bcc W3 + lda __tmp + 2 + sbc c + tay + lda __tmp + 3 + sbc c + 1 + sec + bcs W4 + W3: + sec + lda __tmp + 2 + sbc c + tay + lda __tmp + 3 + sbc c + 1 + bcc W2 + W4: + sta __tmp + 3 + sty __tmp + 2 + W2: + dex + bne L2 + lda __tmp + 1 + rol + bcc T3 + + sta accu + 1 + lda __tmp + 0 + clc + bcc L3 + T3: + sta accu + } +} + +int lmuldiv16s(int a, int b, int c) +{ + bool sign = false; + if (a < 0) + { + a = -a; + sign = !sign; + } + if (b < 0) + { + b = -b; + sign = !sign; + } + if (c < 0) + { + c = -c; + sign = !sign; + } + + __asm + { + lda #0 + sta __tmp + 2 + sta __tmp + 3 + + lda a + sec + T1: + ldy #8 + L1: + ror + bcc W1 + tax + clc + lda __tmp + 2 + adc b + sta __tmp + 2 + lda __tmp + 3 + adc b + 1 + sta __tmp + 3 + txa + W1: + ror __tmp + 3 + ror __tmp + 2 + dey + bne L1 + ror + bcc T2 + + sta __tmp + 0 + lda a + 1 + clc + bcc T1 + + T2: + sec + L3: + sta __tmp + 1 + ldx #8 + L2: + rol __tmp + 1 + rol __tmp + 2 + rol __tmp + 3 + bcc W3 + lda __tmp + 2 + sbc c + tay + lda __tmp + 3 + sbc c + 1 + sec + bcs W4 + W3: + sec + lda __tmp + 2 + sbc c + tay + lda __tmp + 3 + sbc c + 1 + bcc W2 + W4: + sta __tmp + 3 + sty __tmp + 2 + W2: + dex + bne L2 + lda __tmp + 1 + rol + bcc T3 + + sta accu + 1 + lda __tmp + 0 + clc + bcc L3 + T3: + sta accu + + lda sign + beq E1 + + sec + lda #0 + sbc accu + sta accu + lda #0 + sbc accu + 1 + sta accu + 1 + E1: + } +} + + +unsigned lmuldiv16by8(unsigned a, char b, char c) +{ + __asm { + + lda #0 + sta accu + 0 + sta accu + 1 + sta accu + 2 + sta accu + 3 + + lda b + beq z1 + + lda c + l1: + asl + bcs e1 + cmp b + bcs e2 + + asl a + 0 + rol a + 1 + jmp l1 + e2: + clc + e1: + ror + sta c + + ldx #16 + l2: + lda b + sec + sbc c + bcc w1 + sta b + + clc + lda accu + 2 + adc a + sta accu + 2 + lda accu + 3 + adc a + 1 + sta accu + 3 + bcc * + 8 + inc accu + 0 + bne * + 4 + inc accu + 1 + + w1: + asl accu + 2 + rol accu + 3 + rol accu + 0 + rol accu + 1 + + asl b + bcc w2 + lda b + sbc c + sta b + + clc + lda accu + 2 + adc a + sta accu + 2 + lda accu + 3 + adc a + 1 + sta accu + 3 + bcc * + 8 + inc accu + 0 + bne * + 4 + inc accu + 1 + + w2: + dex + bne l2 + z1: + } +} + +unsigned lmuldiv8by8(char a, char b, char c) +{ + __asm { + + ldy #0 + sty accu + 0 + sty accu + 1 + sty accu + 2 + + lda a + beq z1 + lda c + beq z1 + + ldx #8 + lda b + beq z1 + + cmp c + bcc w0 + l1: + lsr + ror accu + 1 + inx + cmp c + bcs l1 + bcc wa + + l2: + asl accu + 2 + rol accu + 0 + wa: + rol accu + 1 + w0: + rol + + bcc w2 + sbc c + bcc w3 + w2: + tay + sec + sbc c + bcc w1 + w3: + tay + + clc + lda accu + 2 + adc a + sta accu + 2 + bcc w1 + inc accu + 0 + bne w1 + inc accu + 1 + w1: + tya + + dex + bne l2 + z1: + } +} + +int lmuldiv16sby8(int a, char b, char c) +{ + if (a < 0) + return -(int)lmuldiv16by8(-a, b, c); + else + return lmuldiv16by8(a, b, c); +} + +unsigned usqrt(unsigned n) +{ + unsigned p, q, r, h; + + p = 0; + r = n; + +#assign q 0x4000 +#repeat + { + h = p | q; + p >>= 1; + if (r >= h) + { + p |= q; + r -= h; + } + } +#assign q q >> 2 +#until q == 0 +#undef q + + return p; +} + +unsigned long lmul16f16(unsigned long x, unsigned long y) +{ + unsigned long hh = lmul16u(x >> 16, y >> 16); + unsigned long lh = lmul16u(x, y >> 16); + unsigned long hl = lmul16u(x >> 16, y); + unsigned long ll = lmul16u(x, y); + + if (ll & 0x8000) + lh++; + ll >>= 16; + ll |= hh << 16; + ll += lh; + ll += hl; + + return ll; +} + +#if 1 +long lmul16f16s(long x, long y) +{ + __asm + { + lda #0 + // fractional + sta __tmp + 0 + sta __tmp + 1 + + // result + sta __accu + 0 + sta __accu + 1 + sta __accu + 2 + sta __accu + 3 + + lda x + 0 + ora x + 1 + ora y + 0 + ora y + 1 + bne w0b + + l2: + + lsr x + 2 + bcc ws1 + + clc + lda y + 2 + adc __accu + 2 + sta __accu + 2 + lda y + 3 + adc __accu + 3 + sta __accu + 3 + ws1: + + lsr x + 3 + bcc ws2 + + clc + lda y + 2 + adc __accu + 3 + sta __accu + 3 + ws2: + + asl y + 2 + rol y + 3 + + lda x + 2 + ora x + 3 + bne l2 + rts + + w0b: + + lda y + 3 + and #$80 + beq w0 + lda #$ff + w0: + // overflow + sta __tmp + 2 + sta __tmp + 3 + + lda x + 3 + bpl w0a + + sec + lda #0 + sbc y + 0 + sta __accu + 2 + lda #0 + sbc y + 1 + sta __accu + 3 + w0a: + + ldx #8 + + l1: + lsr x + 0 + bcc w1 + + clc + lda y + 0 + adc __tmp + 0 + sta __tmp + 0 + lda y + 1 + adc __tmp + 1 + sta __tmp + 1 + lda y + 2 + adc __accu + 0 + sta __accu + 0 + lda y + 3 + adc __accu + 1 + sta __accu + 1 + lda __tmp + 2 + adc __accu + 2 + sta __accu + 2 + lda __tmp + 3 + adc __accu + 3 + sta __accu + 3 + w1: + + lsr x + 1 + bcc w2 + + clc + lda y + 0 + adc __tmp + 1 + sta __tmp + 1 + lda y + 1 + adc __accu + 0 + sta __accu + 0 + lda y + 2 + adc __accu + 1 + sta __accu + 1 + lda y + 3 + adc __accu + 2 + sta __accu + 2 + lda __tmp + 2 + adc __accu + 3 + sta __accu + 3 + w2: + + lsr x + 2 + bcc w3 + + clc + lda y + 0 + adc __accu + 0 + sta __accu + 0 + lda y + 1 + adc __accu + 1 + sta __accu + 1 + lda y + 2 + adc __accu + 2 + sta __accu + 2 + lda y + 3 + adc __accu + 3 + sta __accu + 3 + w3: + + lsr x + 3 + bcc w4 + + clc + lda y + 0 + adc __accu + 1 + sta __accu + 1 + lda y + 1 + adc __accu + 2 + sta __accu + 2 + lda y + 2 + adc __accu + 3 + sta __accu + 3 + w4: + + asl y + 0 + rol y + 1 + rol y + 2 + rol y + 3 + rol __tmp + 2 + rol __tmp + 3 + + dex + beq w5 + jmp l1 + w5: + } +} + +#else +__native long lmul16f16s(long x, long y) +{ + unsigned lox = x; + int hix = x >> 16; + unsigned loy = y; + int hiy = y >> 16; + + long r = (long)(hix * hiy) << 16; + + if (lox) + { + r += lmul16u(lox, hiy); + if (hiy < 0) + r -= (unsigned long)lox << 16; + } + if (loy) + { + r += lmul16u(loy, hix); + if (hix < 0) + r -= (unsigned long)loy << 16; + } + if (lox && loy) + { + r += lmul16u(lox, loy) >> 16; + } + + return r; +} +#endif + +__native unsigned long ldiv16f16(unsigned long x, unsigned long y) +{ + unsigned long k = x >> 16, d = 0; + x <<= 16; + + for(char i=0; i<32; i++) + { + d <<= 1; + k <<= 1; + k |= (x >> 31); + x <<= 1; + if (k >= y) + { + k -= y; + d |= 1; + } + } + + return d; +} + +__native long ldiv16f16s(long x, long y) +{ + bool sign = false; + if (x < 0) + { + x = -x; + sign = true; + } + if (y < 0) + { + y = -y; + sign = !sign; + } + + x = ldiv16f16(x, y); + if (sign) + return -x; + else + return x; +} diff --git a/oscar64/include/fixmath.h b/oscar64/include/fixmath.h new file mode 100644 index 0000000..c3b7ec6 --- /dev/null +++ b/oscar64/include/fixmath.h @@ -0,0 +1,64 @@ +#ifndef FIXMATH_H +#define FIXMATH_H + +// Multiply two unsigned 16bit numbers and return a 32bit result +__native unsigned long lmul16u(unsigned x, unsigned y); + +// Multiply two signed 16bit numbers and return a signed 32bit result +__native long lmul16s(int x, int y); + +// Multiply two 12.4 fixpoint numbers and return a 12.4 fixpoint result +inline int lmul12f4s(int x, int y); + + +// Multiply two 8.8 fixpoint numbers and return an 8.8 fixpoint result +inline int lmul8f8s(int x, int y); + +// Multiply two 4.12 fixpoint numbers and return a 12.4 fixpoint result +__native int lmul4f12s(int x, int y); + +// Square of a 4.12 sigend fixpoint number and return an 8.24 fixpoint result +inline unsigned long lsqr4f12s(int x); + +// Divide a 32bit unsigned number by a 16bit number and return a 16bit number +__native unsigned ldiv16u(unsigned long x, unsigned y); + +// Divide a signed 32bit number by a signed 16bit number and return a signed 16bit number +__native int ldiv16s(long x, int y); + +// Divide a 12.4 fixed point number by a 12.4 fixpoint number +inline int ldiv12f4s(int x, int y); + +// Divide a 8.8 fixed point number by an 8.8 fixpoint number +inline int ldiv8f8s(int x, int y); + +// Divide a 4.12 fixed point number by a 4.12 fixpoint number +inline int ldiv4f12s(int x, int y); + +// Multiply two unsigned 16bit numbers and divide the result by another 16bit number a * b / c +__native unsigned lmuldiv16u(unsigned a, unsigned b, unsigned c); + +// Multiply two signed 16bit numbers and divide the result by another signed 16bit number a * b / c +__native int lmuldiv16s(int a, int b, int c); + + +__native unsigned lmuldiv16by8(unsigned a, char b, char c); + +inline int lmuldiv16sby8(int a, char b, char c); + +__native unsigned lmuldiv8by8(char a, char b, char c); + +__native unsigned usqrt(unsigned n); + +__native unsigned long lmul16f16(unsigned long x, unsigned long y); + +__native long lmul16f16s(long x, long y); + +__native unsigned long ldiv16f16(unsigned long x, unsigned long y); + +__native long ldiv16f16s(long x, long y); + + +#pragma compile("fixmath.c") + +#endif diff --git a/oscar64/include/gfx/bitmap.c b/oscar64/include/gfx/bitmap.c new file mode 100644 index 0000000..43d60ea --- /dev/null +++ b/oscar64/include/gfx/bitmap.c @@ -0,0 +1,1634 @@ +#include "bitmap.h" +#include "tinyfont.h" +#include +#include +#include +#include + +void bm_init(Bitmap * bm, char * data, char cw, char ch) +{ + bm->rdata = nullptr; + bm->data = data; + bm->cwidth = cw; + bm->cheight = ch; + bm->width = cw * 8; +} + +void bm_alloc(Bitmap * bm, char cw, char ch) +{ + bm->rdata = malloc(cw * ch * 8 + 8); + bm->data = bm->rdata + 8 - ((int)bm->rdata & 7); + bm->cwidth = cw; + bm->cheight = ch; + bm->width = cw * 8; +} + +void bm_free(Bitmap * bm) +{ + free(bm->rdata); +} + +void bm_fill(const Bitmap * bm, char data) +{ + memset(bm->data, data, bm->cwidth * bm->cheight * 8); +} + + +void bm_set(const Bitmap * bm, int x, int y) +{ + bm->data[bm->cwidth * (y & ~7) + (x & ~7) + (y & 7)] |= 0x80 >> (x & 7); +} + +#pragma native(bm_set) + +void bm_clr(const Bitmap * bm, int x, int y) +{ + bm->data[bm->cwidth * (y & ~7) + (x & ~7) + (y & 7)] &= ~(0x80 >> (x & 7)); +} + +#pragma native(bm_clr) + +bool bm_get(const Bitmap * bm, int x, int y) +{ + return (bm->data[bm->cwidth * (y & ~7) + (x & ~7) + (y & 7)] & (0x80 >> (x & 7))) != 0; +} + +#pragma native(bm_get) + +void bm_put(const Bitmap * bm, int x, int y, bool c) +{ + char * dp = bm->data + bm->cwidth * (y & ~7) + (x & ~7) + (y & 7); + char m = 0x80 >> (x & 7); + if (c) + *dp |= m; + else + *dp &= ~m; +} + +#pragma native(bm_put) + +char NineShadesOfGrey[9][8] = { + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // 0 + {0x22, 0x00, 0x88, 0x00, 0x22, 0x00, 0x88, 0x00}, // 8 + {0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88}, // 16 + {0x88, 0x55, 0x22, 0x55, 0x88, 0x55, 0x22, 0x55}, // 24 + {0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55}, // 32 + {0xbb, 0x55, 0xee, 0x55, 0xbb, 0x55, 0xee, 0x55}, // 40 + {0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77}, // 48 + {0xff, 0xee, 0xff, 0xbb, 0xff, 0xee, 0xff, 0xbb}, // 56 + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, // 64 +}; + +byte BLIT_CODE[16 * 14]; + + +static const byte lmask[8] = {0xff, 0x7f, 0x3f, 0x1f, 0x0f, 0x07, 0x03, 0x01}; +static const byte rmask[8] = {0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe}; + +void bm_scan_fill(int left, int right, char * lp, int x0, int x1, char pat) +{ + __assume(left >= 0); + __assume(right >= 0); + + if (x0 < left) + x0 = left; + if (x1 > right) + x1 = right; + + if (x1 > x0) + { + char * dp = lp + (x0 & ~7); + unsigned l = (x1 & ~7) - (x0 & ~7); + char lm = lmask[x0 & 7], rm = rmask[x1 & 7]; + char o = 0; + + if (l == 0) + rm &= lm; + else + { + *dp = (*dp & ~lm) | (pat & lm); + o = 8; + if (l >= 256) + { + #pragma unroll(full) + for(char i=0; i<31; i++) + { + dp[o] = pat; + o += 8; + } + dp += 256; + l -= 248; + } + else if (l >= 128) + { + #pragma unroll(full) + for(char i=0; i<15; i++) + { + dp[o] = pat; + o += 8; + } + } + + while (o < (char)l) + { + dp[o] = pat; + o += 8; + } + } + dp[o] = (dp[o] & ~rm) | (pat & rm); + } +} + +#pragma native(bm_scan_fill) + +#if 0 +unsigned bm_usqrt(unsigned n) +{ + unsigned p, q, r, h; + + p = 0; + r = n; + + q = 0x4000; + while (q > r) + q >>= 2; + + while (q != 0) + { + h = p + q; + p >>= 1; + if (r >= h) + { + p += q; + r -= h; + } + q >>= 2; + } + + return p; +} +#else +unsigned bm_usqrt(unsigned n) +{ + unsigned p, q, r, h; + + p = 0; + r = n; + +#assign q 0x4000 +#repeat + { + h = p | q; + p >>= 1; + if (r >= h) + { + p |= q; + r -= h; + } + } +#assign q q >> 2 +#until q == 0 +#undef q + + return p; +} +#endif + +#pragma native(bm_usqrt) + +void bm_circle_fill(const Bitmap * bm, const ClipRect * clip, int x, int y, char r, const char * pattern) +{ + int y0 = y - r, y1 = y + r + 1; + if (y0 < clip->top) + y0 = clip->top; + if (y1 > clip->bottom) + y1 = clip->bottom; + + const char * pat = pattern; + char * lp = bm->data + bm->cwidth * (y0 & ~7) + (y0 & 7); + int stride = 8 * bm->cwidth - 8; + + unsigned rr = r * r + r; + for(char iy=y0; iy<(char)y1; iy++) + { + int d = (iy - y); + + int t = bm_usqrt(rr - d * d); + + bm_scan_fill(clip->left, clip->right, lp, x - t, x + t + 1, pat[iy & 7]); + lp ++; + if (!((int)lp & 7)) + lp += stride; + } +} + +void bm_trapezoid_fill(const Bitmap * bm, const ClipRect * clip, long x0, long x1, long dx0, long dx1, int y0, int y1, const char * pattern) +{ + if (y1 <= clip->top || y0 >= clip->bottom) + return; + + long tx0 = x0, tx1 = x1; + + if (y1 > clip->bottom) + y1 = clip->bottom; + if (y0 < clip->top) + { + tx0 += (clip->top - y0) * dx0; + tx1 += (clip->top - y0) * dx1; + y0 = clip->top; + } + + const char * pat = pattern; + char * lp = bm->data + bm->cwidth * (y0 & ~7) + (y0 & 7); + int stride = 8 * bm->cwidth - 8; + + for(char iy=y0; iy<(char)y1; iy++) + { + bm_scan_fill(clip->left, clip->right, lp, tx0 >> 16, tx1 >> 16, pat[iy & 7]); + tx0 += dx0; + tx1 += dx1; + lp ++; + if (!((int)lp & 7)) + lp += stride; + } +} + + +void bm_triangle_fill(const Bitmap * bm, const ClipRect * clip, int x0, int y0, int x1, int y1, int x2, int y2, const char * pat) +{ + int t; + if (y1 < y0 && y1 < y2) + { + t = y0; y0 = y1; y1 = t; + t = x0; x0 = x1; x1 = t; + } + else if (y2 < y0) + { + t = y0; y0 = y2; y2 = t; + t = x0; x0 = x2; x2 = t; + } + + if (y2 < y1) + { + t = y1; y1 = y2; y2 = t; + t = x1; x1 = x2; x2 = t; + } + + if (y0 < y2) + { + long dx1, lx1; + long dx2 = ((long)(x2 - x0) << 16) / (y2 - y0); + long lx2 = (long)x0 << 16; + + if (y1 > y0) + { + dx1 = ((long)(x1 - x0) << 16) / (y1 - y0); + + if (dx1 < dx2) + bm_trapezoid_fill(bm, clip, lx2, lx2, dx1, dx2, y0, y1, pat); + else + bm_trapezoid_fill(bm, clip, lx2, lx2, dx2, dx1, y0, y1, pat); + if (y2 == y1) + return; + + lx2 += dx2 * (y1 - y0); + } + + dx1 = ((long)(x2 - x1) << 16) / (y2 - y1); + lx1 = (long)x1 << 16; + + if (lx1 < lx2) + bm_trapezoid_fill(bm, clip, lx1, lx2, dx1, dx2, y1, y2, pat); + else + bm_trapezoid_fill(bm, clip, lx2, lx1, dx2, dx1, y1, y2, pat); + } +} + + +void bm_quad_fill(const Bitmap * bm, const ClipRect * clip, int x0, int y0, int x1, int y1, int x2, int y2, int x3, int y3, const char * pat) +{ + bm_triangle_fill(bm, clip, x0, y0, x1, y1, x2, y2, pat); + bm_triangle_fill(bm, clip, x0, y0, x2, y2, x3, y3, pat); +} + +void bm_polygon_fill(const Bitmap * bm, const ClipRect * clip, int * px, int * py, char num, const char * pat) +{ + char mi = 0; + int my = py[0]; + + for(char i=1; i my) + { + long dlx = (((long)px[li] << 16) - lx) / (py[li] - my); + long drx = (((long)px[ri] << 16) - rx) / (py[ri] - my); + + if (lx < rx || lx == rx && dlx < drx) + bm_trapezoid_fill(bm, clip, lx, rx, dlx, drx, my, ty, pat); + else + bm_trapezoid_fill(bm, clip, rx, lx, drx, dlx, my, ty, pat); + + lx += (ty - my) * dlx; + rx += (ty - my) * drx; + + my = ty; + + while (py[li] == my) + { + lx = (long)px[li] << 16; + if (li == 0) + li = num; + li--; + } + + while (py[ri] == my) + { + rx = (long)px[ri] << 16; + ri++; + if (ri == num) + ri = 0; + } + + ty = py[li] < py[ri] ? py[li] : py[ri]; + } + +} + +struct Edge +{ + char minY, maxY; + long px, dx; + Edge * next; +}; + +void bm_polygon_nc_fill(const Bitmap * bm, const ClipRect * clip, int * px, int * py, char num, const char * pattern) +{ + Edge * first = nullptr, * active = nullptr; + Edge * e = (Edge *)BLIT_CODE; + + char n = num; + if (n > 16) + n = 16; + + int top = clip->top, bottom = clip->bottom; + + for(char i=0; i= n) + j = 0; + + if (py[i] != py[j]) + { + if (py[i] > py[j]) + { + k = j; j = i; + } + + int minY = py[k], maxY = py[j]; + if (minY < bottom && maxY > top) + { + e->px = ((long)px[k] << 16) + 0x8000; + e->dx = (((long)px[j] << 16) - e->px) / (maxY - minY); + + if (minY < top) + { + e->px += e->dx * (top - minY); + minY = top; + } + if (maxY > bottom) + maxY = bottom; + + e->minY = minY; e->maxY = maxY; + + Edge * pp = nullptr, * pe = first; + + while (pe && minY >= pe->minY) + { + pp = pe; + pe = pe->next; + } + + e->next = pe; + if (pp) + pp->next = e; + else + first = e; + + e++; + } + } + } + + if (first) + { + char y = first->minY; + + const char * pat = pattern; + char * lp = bm->data + bm->cwidth * (y & ~7) + (y & 7); + int stride = 8 * bm->cwidth - 8; + + while (first || active) + { + while (first && first->minY == y) + { + Edge * next = first->next; + + Edge * pp = nullptr, * pe = active; + while (pe && (first->px > pe->px || first->px == pe->px && first->dx > pe->dx)) + { + pp = pe; + pe = pe->next; + } + + first->next = pe; + if (pp) + pp->next = first; + else + active = first; + + first = next; + } + + Edge * e0 = active; + while (e0) + { + Edge * e1 = e0->next; + bm_scan_fill(clip->left, clip->right, lp, e0->px >> 16, e1->px >> 16, pat[y & 7]); + e0 = e1->next; + } + + lp ++; + if (!((int)lp & 7)) + lp += stride; + + y++; + + // remove final edges + Edge * pp = nullptr, * pe = active; + while (pe) + { + if (pe->maxY == y) + { + if (pp) + pp->next = pe->next; + else + active = pe->next; + } + else + { + pe->px += pe->dx; + pp = pe; + } + pe = pe->next; + } + } + } +} + +#define REG_SP 0x03 +#define REG_DP 0x05 +#define REG_PAT 0x07 +#define REG_S0 0x08 +#define REG_S1 0x09 +#define REG_D0 0x0a +#define REG_D1 0x0b + +static inline void buildline(char ly, char lx, int dx, int dy, int stride, bool left, bool up, char pattern, LineOp op) +{ + char ip = 0; + bool delta16 =((dx | dy) & 0xff80) != 0; + + // ylow + ip += asm_im(BLIT_CODE + ip, ASM_LDY, ly); + ip += asm_im(BLIT_CODE + ip, ASM_LDX, lx); + + // set pixel + ip += asm_zp(BLIT_CODE + ip, ASM_LDA, REG_D0); + + switch (op) + { + case LINOP_SET: + ip += asm_zp(BLIT_CODE + ip, ASM_ASL, REG_PAT); + ip += asm_rl(BLIT_CODE + ip, ASM_BCC, 6); + ip += asm_zp(BLIT_CODE + ip, ASM_INC, REG_PAT); + ip += asm_iy(BLIT_CODE + ip, ASM_ORA, REG_SP); + ip += asm_rl(BLIT_CODE + ip, ASM_BNE, 4); + ip += asm_im(BLIT_CODE + ip, ASM_EOR, 0xff); + ip += asm_iy(BLIT_CODE + ip, ASM_AND, REG_SP); + ip += asm_iy(BLIT_CODE + ip, ASM_STA, REG_SP); + break; + case LINOP_OR: + if (pattern != 0xff) + { + ip += asm_zp(BLIT_CODE + ip, ASM_ASL, REG_PAT); + ip += asm_rl(BLIT_CODE + ip, ASM_BCC, 6); + ip += asm_zp(BLIT_CODE + ip, ASM_INC, REG_PAT); + } + ip += asm_iy(BLIT_CODE + ip, ASM_ORA, REG_SP); + ip += asm_iy(BLIT_CODE + ip, ASM_STA, REG_SP); + break; + case LINOP_XOR: + if (pattern != 0xff) + { + ip += asm_zp(BLIT_CODE + ip, ASM_ASL, REG_PAT); + ip += asm_rl(BLIT_CODE + ip, ASM_BCC, 6); + ip += asm_zp(BLIT_CODE + ip, ASM_INC, REG_PAT); + } + ip += asm_iy(BLIT_CODE + ip, ASM_EOR, REG_SP); + ip += asm_iy(BLIT_CODE + ip, ASM_STA, REG_SP); + break; + case LINOP_AND: + if (pattern != 0xff) + { + ip += asm_zp(BLIT_CODE + ip, ASM_ASL, REG_PAT); + ip += asm_rl(BLIT_CODE + ip, ASM_BCC, 8); + ip += asm_zp(BLIT_CODE + ip, ASM_INC, REG_PAT); + } + ip += asm_im(BLIT_CODE + ip, ASM_EOR, 0xff); + ip += asm_iy(BLIT_CODE + ip, ASM_AND, REG_SP); + ip += asm_iy(BLIT_CODE + ip, ASM_STA, REG_SP); + break; + } + + if (dy) + { + bool delta8 = false; + + if (dx) + { + // m >= 0 + ip += asm_zp(BLIT_CODE + ip, ASM_LDA, REG_DP + delta16); + char n = delta16 ? 18 + 13 + 2 : 18 + 7 + 2; + if (!up) n++; + ip += asm_rl(BLIT_CODE + ip, ASM_BMI, n); + delta8 = !delta16; + } + + if (up) + { + ip += asm_np(BLIT_CODE + ip, ASM_DEY); + ip += asm_rl(BLIT_CODE + ip, ASM_BPL, delta8 ? 17 : 15); + ip += asm_np(BLIT_CODE + ip, ASM_CLC); + ip += asm_im(BLIT_CODE + ip, ASM_LDY, 0x07); + ip += asm_zp(BLIT_CODE + ip, ASM_LDA, REG_SP); + ip += asm_im(BLIT_CODE + ip, ASM_ADC, stride & 0xff); + ip += asm_zp(BLIT_CODE + ip, ASM_STA, REG_SP); + ip += asm_zp(BLIT_CODE + ip, ASM_LDA, REG_SP + 1); + ip += asm_im(BLIT_CODE + ip, ASM_ADC, stride >> 8); + ip += asm_zp(BLIT_CODE + ip, ASM_STA, REG_SP + 1); + } + else + { + ip += asm_np(BLIT_CODE + ip, ASM_INY); + ip += asm_im(BLIT_CODE + ip, ASM_CPY, 0x08); + ip += asm_rl(BLIT_CODE + ip, ASM_BNE, delta8 ? 16 : 14); + ip += asm_im(BLIT_CODE + ip, ASM_LDY, 0x00); + ip += asm_zp(BLIT_CODE + ip, ASM_LDA, REG_SP); + ip += asm_im(BLIT_CODE + ip, ASM_ADC, (stride - 1) & 0xff); + ip += asm_zp(BLIT_CODE + ip, ASM_STA, REG_SP); + ip += asm_zp(BLIT_CODE + ip, ASM_LDA, REG_SP + 1); + ip += asm_im(BLIT_CODE + ip, ASM_ADC, (stride - 1) >> 8); + ip += asm_zp(BLIT_CODE + ip, ASM_STA, REG_SP + 1); + } + + if (dx) + { + ip += asm_zp(BLIT_CODE + ip, ASM_LDA, REG_DP); + ip += asm_np(BLIT_CODE + ip, ASM_SEC); + ip += asm_im(BLIT_CODE + ip, ASM_SBC, dx & 0xff); + ip += asm_zp(BLIT_CODE + ip, ASM_STA, REG_DP); + + if (delta16) + { + ip += asm_zp(BLIT_CODE + ip, ASM_LDA, REG_DP + 1); + ip += asm_im(BLIT_CODE + ip, ASM_SBC, dx >> 8); + ip += asm_zp(BLIT_CODE + ip, ASM_STA, REG_DP + 1); + ip += asm_rl(BLIT_CODE + ip, ASM_BPL, 13 + 4 + 12); + + ip += asm_np(BLIT_CODE + ip, ASM_CLC); + ip += asm_zp(BLIT_CODE + ip, ASM_LDA, REG_DP); + ip += asm_im(BLIT_CODE + ip, ASM_ADC, dy & 0xff); + ip += asm_zp(BLIT_CODE + ip, ASM_STA, REG_DP); + ip += asm_zp(BLIT_CODE + ip, ASM_LDA, REG_DP + 1); + ip += asm_im(BLIT_CODE + ip, ASM_ADC, dy >> 8); + ip += asm_zp(BLIT_CODE + ip, ASM_STA, REG_DP + 1); + } + else + { + // We know regdp to be in the accu at this point + ip += asm_rl(BLIT_CODE + ip, ASM_BPL, 5 + 4 + 12); + ip += asm_np(BLIT_CODE + ip, ASM_CLC); + ip += asm_im(BLIT_CODE + ip, ASM_ADC, dy & 0xff); + ip += asm_zp(BLIT_CODE + ip, ASM_STA, REG_DP); + } + } + + // m < 0 + } + + if (dx) + { + ip += asm_zp(BLIT_CODE + ip, left ? ASM_ASL : ASM_LSR, REG_D0); + ip += asm_rl(BLIT_CODE + ip, ASM_BCC, 12); + + ip += asm_zp(BLIT_CODE + ip, left ? ASM_ROL : ASM_ROR, REG_D0); + + ip += asm_zp(BLIT_CODE + ip, ASM_LDA, REG_SP); + + if (left) + { + ip += asm_im(BLIT_CODE + ip, ASM_ADC, 0xf8); + ip += asm_rl(BLIT_CODE + ip, ASM_BCS, 2); + ip += asm_zp(BLIT_CODE + ip, ASM_DEC, REG_SP + 1); + } + else + { + ip += asm_im(BLIT_CODE + ip, ASM_ADC, 0x08); + ip += asm_rl(BLIT_CODE + ip, ASM_BCC, 2); + ip += asm_zp(BLIT_CODE + ip, ASM_INC, REG_SP + 1); + } + + ip += asm_zp(BLIT_CODE + ip, ASM_STA, REG_SP); + } + + // l -- + ip += asm_np(BLIT_CODE + ip, ASM_DEX); + ip += asm_rl(BLIT_CODE + ip, ASM_BNE, 2 - ip); + ip += asm_zp(BLIT_CODE + ip, ASM_DEC, REG_D1); + ip += asm_rl(BLIT_CODE + ip, ASM_BPL, 2 - ip); + + ip += asm_np(BLIT_CODE + ip, ASM_RTS); +} + +#pragma native(buildline) + +static inline void callline(byte * dst, byte bit, int m, char lh, char pattern) +{ + __asm + { + + lda dst + sta REG_SP + lda dst + 1 + sta REG_SP + 1 + + lda m + sta REG_DP + lda m + 1 + sta REG_DP + 1 + + lda lh + sta REG_D1 + + lda bit + sta REG_D0 + + lda pattern + sta REG_PAT + + jsr BLIT_CODE + } +} + +void bmu_line(const Bitmap * bm, int x0, int y0, int x1, int y1, char pattern, LineOp op) +{ + if (pattern == 0x00) + { + if (op == LINOP_SET) + { + pattern = 0xff; + op = LINOP_AND; + } + else + return; + } + else if (pattern == 0xff && op == LINOP_SET) + op = LINOP_OR; + + int dx = x1 - x0, dy = y1 - y0; + bool left = false; + bool up = false; + + if (dx < 0) + { + left = true; + dx = -dx; + } + if (dy < 0) + { + up = true; + dy = -dy; + } + + int l; + if ((unsigned)dx > (unsigned)dy) + l = dx; + else + l = dy; + + int m = dy - dx; + if (m & 1) + { + dx *= 2; + dy *= 2; + } + else + { + m >>= 1; + } + + char * dp = bm->data + bm->cwidth * (y0 & ~7) + (x0 & ~7); + char bit = 0x80 >> (x0 & 7); + char ry = y0 & 7; + int stride = 8 * bm->cwidth; + + buildline(ry, (l + 1) & 0xff, dx, dy, up ? -stride : stride, left, up, pattern, op); + + callline(dp, bit, m, l >> 8, pattern); +} + +void bm_line(const Bitmap * bm, const ClipRect * clip, int x0, int y0, int x1, int y1, char pattern, LineOp op) +{ + int dx = x1 - x0, dy = y1 - y0; + + if (dx > 0) + { + __assume(clip->left >= 0); + __assume(clip->right >= 0); + + if (x1 < clip->left || x0 >= clip->right) + return; + + if (x0 < clip->left) + { + y0 += lmuldiv16s(clip->left - x0, dy, dx); + x0 = clip->left; + } + + if (x1 >= clip->right) + { + y1 -= lmuldiv16s(x1 + 1 - clip->right, dy, dx); + x1 = clip->right - 1; + } + } + else + { + __assume(clip->left >= 0); + __assume(clip->right >= 0); + + if (x0 < clip->left || x1 >= clip->right) + return; + + if (x1 < clip->left) + { + y1 += lmuldiv16s(clip->left - x1, dy, dx); + x1 = clip->left; + } + + if (x0 >= clip->right) + { + y0 -= lmuldiv16s(x0 + 1- clip->right, dy, dx); + x0 = clip->right - 1; + } + } + + if (dy > 0) + { + __assume(clip->top >= 0); + __assume(clip->bottom >= 0); + + if (y1 < clip->top || y0 >= clip->bottom) + return; + + if (y0 < clip->top) + { + x0 += lmuldiv16s(clip->top - y0, dx, dy); + y0 = clip->top; + } + + if (y1 >= clip->bottom) + { + x1 -= lmuldiv16s(y1 + 1 - clip->bottom, dx, dy); + y1 = clip->bottom - 1; + } + } + else + { + __assume(clip->top >= 0); + __assume(clip->bottom >= 0); + + if (y0 < clip->top || y1 >= clip->bottom) + return; + + if (y1 < clip->top) + { + x1 += lmuldiv16s(clip->top - y1, dx, dy); + y1 = clip->top; + } + + if (y0 >= clip->bottom) + { + x0 -= lmuldiv16s(y0 + 1 - clip->bottom, dx, dy); + y0 = clip->bottom - 1; + } + } + + bmu_line(bm, x0, y0, x1, y1, pattern, op); +} + +static inline void callddop(byte * src, byte * dst, byte pat) +{ + __asm + { + lda src + sta REG_SP + lda src + 1 + sta REG_SP + 1 + lda dst + sta REG_DP + lda dst + 1 + sta REG_DP + 1 + lda pat + sta REG_PAT + jsr BLIT_CODE + } +} + +// build code to fetch src, result in accu + +static char builddop_src(char ip, char shift, bool reverse) +{ + if (shift != 0) + { + ip += asm_zp(BLIT_CODE + ip, ASM_LDA, REG_S0); + if (reverse) + { + if (shift > 4) + ip += asm_ac(BLIT_CODE + ip, ASM_ASL); + } + else + { + if (shift <= 4) + ip += asm_ac(BLIT_CODE + ip, ASM_LSR); + } + ip += asm_zp(BLIT_CODE + ip, ASM_STA, REG_S1); + + ip += asm_iy(BLIT_CODE + ip, ASM_LDA, REG_SP); + ip += asm_zp(BLIT_CODE + ip, ASM_STA, REG_S0); + + if (reverse) + { + if (shift > 4) + { + for(char i=shift; i<7; i++) + { + ip += asm_ac(BLIT_CODE + ip, ASM_ROL); + ip += asm_zp(BLIT_CODE + ip, ASM_ASL, REG_S1); + } + ip += asm_ac(BLIT_CODE + ip, ASM_ROL); + } + else + { + ip += asm_ac(BLIT_CODE + ip, ASM_LSR); + for(char i=1; i 4) + { + ip += asm_ac(BLIT_CODE + ip, ASM_ASL); + for(char i=shift; i<7; i++) + { + ip += asm_zp(BLIT_CODE + ip, ASM_ROL, REG_S1); + ip += asm_ac(BLIT_CODE + ip, ASM_ASL); + } + ip += asm_zp(BLIT_CODE + ip, ASM_LDA, REG_S1); + ip += asm_ac(BLIT_CODE + ip, ASM_ROL); + } + else + { + for(char i=1; i 0) + { + ystart += yinc; + ip += asm_im(BLIT_CODE + ip, ASM_LDY, ystart); + } + } + + if (w > 0) + { + if (w > 1) + { + if (w > 2) + ip += asm_im(BLIT_CODE + ip, ASM_LDX, w - 1); + if (w <= 31 && !shift) + ip += asm_np(BLIT_CODE + ip, asm_clc); + char lp = ip; + + if (usedst) + { + ip += asm_iy(BLIT_CODE + ip, ASM_LDA, REG_DP); + ip += asm_zp(BLIT_CODE + ip, ASM_STA, REG_D0); + } + + if (usesrc) + { + ip = builddop_src(ip, shift, reverse); + } + + ip = builddop_op(ip, op, 0); + + if (w > 2) + { + if (w > 31 || shift) + ip += asm_np(BLIT_CODE + ip, asm_clc); + ip += asm_np(BLIT_CODE + ip, ASM_TYA); + ip += asm_im(BLIT_CODE + ip, asm_adc, 8); + ip += asm_np(BLIT_CODE + ip, ASM_TAY); + if (w > 31) + { + if (usesrc) + { + ip += asm_rl(BLIT_CODE + ip, asm_bcc, 4); + ip += asm_zp(BLIT_CODE + ip, asm_inc, REG_SP + 1); + } + else + { + ip += asm_rl(BLIT_CODE + ip, asm_bcc, 2); + } + ip += asm_zp(BLIT_CODE + ip, asm_inc, REG_DP + 1); + } + + ip += asm_np(BLIT_CODE + ip, ASM_DEX); + ip += asm_rl(BLIT_CODE + ip, ASM_BNE, lp - ip - 2); + } + else + { + ystart += yinc; + ip += asm_im(BLIT_CODE + ip, ASM_LDY, ystart); + } + } + + if (rmask != 0) + { + if (usedst) + { + ip += asm_iy(BLIT_CODE + ip, ASM_LDA, REG_DP); + ip += asm_zp(BLIT_CODE + ip, ASM_STA, REG_D0); + } + + if (usesrc) + { + ip = builddop_src(ip, shift, reverse); + } + + ip = builddop_op(ip, op, ~rmask); + } + } + + ip += asm_np(BLIT_CODE + ip, ASM_RTS); +} + +#pragma native(builddop) + +void bmu_bitblit(const Bitmap * dbm, int dx, int dy, const Bitmap * sbm, int sx, int sy, int w, int h, const char * pattern, BlitOp op) +{ + int rx = dx + w; + char dxh0 = dx >> 3, dxh1 = rx >> 3; + + char lm = lmask[dx & 7], rm = rmask[rx & 7]; + + char cw = dxh1 - dxh0; + + bool reverse = dbm == sbm && (dy > sy || dy == sy && dx > sx) && (op & BLIT_SRC); + + if (reverse) + { + dy += h; + sy += h; + } + + char * dp = dbm->data + dbm->cwidth * (dy & ~7) + (dx & ~7) + (dy & 7); + char * sp = sbm->data + sbm->cwidth * (sy & ~7) + (sx & ~7) + (sy & 7); + + char shift = (dx & 7) - (sx & 7); + if (reverse) + { + sp += 8 * cw - 0xf8; + dp += 8 * cw - 0xf8; + byte t = lm; lm = rm; rm = t; + if (shift & 0x80) + sp += 8; + } + else if (shift) + { + if (!(shift & 0x80)) + sp -= 8; + } + + shift &= 7; + + + + builddop(shift, cw, lm, rm, op, reverse); + + const char * pat = pattern; + + int sstride = 8 * sbm->cwidth - 8; + int dstride = 8 * dbm->cwidth - 8; + + if (op & BLIT_SRC) + { + if (!pat) + pat = sp; + + if (reverse) + { + sstride = -sstride; + dstride = -dstride; + + for(char y=h; y>0; y--) + { + if (((int)sp & 7) == 0) + sp += sstride; + sp--; + + if (((int)dp & 7) == 0) + dp += dstride; + dp--; + + char pi = (int)dp & 7; + + callddop(sp, dp, pat[pi]); + } + } + else + { + for(char y=h; y>0; y--) + { + char pi = (int)dp & 7; + + callddop(sp, dp, pat[pi]); + + sp++; + if (((int)sp & 7) == 0) + sp += sstride; + + dp++; + if (((int)dp & 7) == 0) + dp += dstride; + } + } + } + else if (op & BLIT_PATTERN) + { + for(char y=h; y>0; y--) + { + char pi = (int)dp & 7; + + callddop(dp, dp, pat[pi]); + + dp++; + if (((int)dp & 7) == 0) + dp += dstride; + } + } + else + { + for(char y=h; y>0; y--) + { + callddop(dp, dp, 0); + + dp++; + if (((int)dp & 7) == 0) + dp += dstride; + } + } +} + +#pragma native(bmu_bitblit) + +void bm_bitblit(const Bitmap * dbm, const ClipRect * clip, int dx, int dy, const Bitmap * sbm, int sx, int sy, int w, int h, const char * pattern, BlitOp op) +{ + int d; + + d = dx - clip->left; + + if (d < 0) + { + dx -= d; + sx -= d; + w += d; + } + + d = dy - clip->top; + + if (d < 0) + { + dy -= d; + sy -= d; + h += d; + } + + d = clip->right - dx - w; + + if (d < 0) + w += d; + + d = clip->bottom - dy - h; + + if (d < 0) + h += d; + + if (w > 0 && h > 0) + bmu_bitblit(dbm, dx, dy, sbm, sx, sy, w, h, pattern, op); +} + +inline void bmu_rect_fill(const Bitmap * dbm, int dx, int dy, int w, int h) +{ + bmu_bitblit(dbm, dx, dy, dbm, dx, dy, w, h, nullptr, BLTOP_SET); +} + +inline void bmu_rect_clear(const Bitmap * dbm, int dx, int dy, int w, int h) +{ + bmu_bitblit(dbm, dx, dy, dbm, dx, dy, w, h, nullptr, BLTOP_RESET); +} + +inline void bmu_rect_pattern(const Bitmap * dbm, int dx, int dy, int w, int h, const char * pattern) +{ + bmu_bitblit(dbm, dx, dy, dbm, dx, dy, w, h, pattern, BLTOP_PATTERN); +} + +inline void bmu_rect_copy(const Bitmap * dbm, int dx, int dy, const Bitmap * sbm, int sx, int sy, int w, int h) +{ + bmu_bitblit(dbm, dx, dy, sbm, sx, sy, w, h, nullptr, BLTOP_COPY); +} + + +inline void bm_rect_fill(const Bitmap * dbm, const ClipRect * clip, int dx, int dy, int w, int h) +{ + bm_bitblit(dbm, clip, dx, dy, dbm, dx, dy, w, h, nullptr, BLTOP_SET); +} + +inline void bm_rect_clear(const Bitmap * dbm, const ClipRect * clip, int dx, int dy, int w, int h) +{ + bm_bitblit(dbm, clip, dx, dy, dbm, dx, dy, w, h, nullptr, BLTOP_RESET); +} + +inline void bm_rect_pattern(const Bitmap * dbm, const ClipRect * clip, int dx, int dy, int w, int h, const char * pattern) +{ + bm_bitblit(dbm, clip, dx, dy, dbm, dx, dy, w, h, pattern, BLTOP_PATTERN); +} + +inline void bm_rect_copy(const Bitmap * dbm, const ClipRect * clip, int dx, int dy, const Bitmap * sbm, int sx, int sy, int w, int h) +{ + bm_bitblit(dbm, clip, dx, dy, sbm, sx, sy, w, h, nullptr, BLTOP_COPY); +} + +static char tworks[8]; + +int bmu_text(const Bitmap * bm, char lx, const char * str, char len) +{ + int tw = 0; + + tworks[0] = 0; + tworks[1] = 0; + tworks[2] = 0; + tworks[3] = 0; + tworks[4] = 0; + tworks[5] = 0; + tworks[6] = 0; + tworks[7] = 0; + + char * cp = bm->data; + + for(char fx=0; fx> 2; + tw += w + 1; + + for(char px=0; px> 2) + 1; + } + + return tw; +} + +static char tbuffer[320]; +#pragma align(tbuffer, 8) + +static Bitmap tbitmap = { + tbuffer, nullptr, 40, 1, 320 +}; + +int bmu_put_chars(const Bitmap * bm, int x, int y, const char * str, char len, BlitOp op) +{ + int tw = bmu_text(&tbitmap, x & 7, str, len); + + bmu_bitblit(bm, x, y, &tbitmap, x & 7, 0, tw, 8, nullptr, op); + + return tw; +} + +int bm_put_chars(const Bitmap * bm, const ClipRect * clip, int x, int y, const char * str, char len, BlitOp op) +{ + int tw = 0; + + if (y >= clip->bottom || x >= clip->right || y + 8 <= clip->top) + { + for(char fx=0; fx> 2) + 1; + } + + return tw; + } + + if (x < clip->left) + { + char fx = 0; + while (fx < len) + { + char ch = str[fx] - 32; + char f0 = TinyFont[ch], f1 = TinyFont[ch + 96]; + char w = (f1 >> 2) + 1; + if (x + w >= clip->left) + break; + tw += w; + x += w; + fx++; + } + + str += fx; + len -= fx; + } + + int rx = x; + char fx = 0; + while (fx < len && rx < clip->right) + { + char ch = str[fx] - 32; + char f0 = TinyFont[ch], f1 = TinyFont[ch + 96]; + char w = (f1 >> 2) + 1; + rx += w; + tw += w; + fx++; + } + + int cw = bmu_text(&tbitmap, x & 7, str, fx); + + bm_bitblit(bm, clip, x, y, &tbitmap, x & 7, 0, cw, 8, nullptr, op); + + while (fx < len) + { + char ch = str[fx] - 32; + char f0 = TinyFont[ch], f1 = TinyFont[ch + 96]; + char w = (f1 >> 2) + 1; + tw += w; + fx++; + } + + return tw; +} + +int bm_put_string(const Bitmap * bm, const ClipRect * clip, int x, int y, const char * str, BlitOp op) +{ + return bm_put_chars(bm, clip, x, y, str, strlen(str), op); +} + +int bm_transform(const Bitmap * dbm, const ClipRect * clip, int dx, int dy, int w, int h, const Bitmap * sbm, int sx, int sy, int dxx, int dxy, int dyx, int dyy) +{ + long lsx = (long)sx << 16, lsy = (long)sy << 16; + + char * ldp = dbm->data + dbm->cwidth * (dy & ~7) + (dx & ~7) + (dy & 7); + int dstride = dbm->cwidth * 8 - 8; + + for(int y=0; y> (dx & 7); + + for(int x=0; x> 16); + int iy = (int)(rsy >> 16); + + if (ix >= 0 && iy >= 0 && ix < sbm->width && iy < 8 * sbm->cheight) + { + if (bm_get(sbm, ix, iy)) + *dp |= m; + else + *dp &= ~m; + } + m >>= 1; + if (!m) + { + m = 0x80; + dp += 8; + } + + rsx += (long)dxx << 8; + rsy += (long)dyx << 8; + } + + ldp++; + if (((int)ldp & 7) == 0) + ldp += dstride; + + lsx += (long)dxy << 8; + lsy += (long)dyy << 8; + } +} + +#pragma native(bm_transform) diff --git a/oscar64/include/gfx/bitmap.h b/oscar64/include/gfx/bitmap.h new file mode 100644 index 0000000..5c46e0d --- /dev/null +++ b/oscar64/include/gfx/bitmap.h @@ -0,0 +1,172 @@ +#ifndef BITMAP_H +#define BITMAP_H + +#include + +struct Bitmap +{ + char * data, * rdata; + char cwidth; + char cheight; + unsigned width; +}; + +struct ClipRect +{ + int left, top, right, bottom; +}; + +#define BLIT_OP 0x03 + +#define BLIT_AND 0x01 +#define BLIT_ORA 0x02 +#define BLIT_EOR 0x03 + +#define BLIT_IMM 0x04 +#define BLIT_SRC 0x08 +#define BLIT_DST 0x10 +#define BLIT_PATTERN 0x20 + +#define BLIT_INVERT 0x40 + + +enum BlitOp +{ + BLTOP_SET = BLIT_IMM | BLIT_INVERT, + BLTOP_RESET = BLIT_IMM, + BLTOP_NOT = BLIT_DST | BLIT_IMM | BLIT_INVERT | BLIT_EOR, + + BLTOP_XOR = BLIT_SRC | BLIT_DST | BLIT_EOR, + BLTOP_OR = BLIT_SRC | BLIT_DST | BLIT_ORA, + BLTOP_AND = BLIT_SRC | BLIT_DST | BLIT_AND, + BLTOP_AND_NOT = BLIT_SRC | BLIT_DST | BLIT_INVERT | BLIT_AND, + + BLTOP_COPY = BLIT_SRC, + BLTOP_NCOPY = BLIT_SRC | BLIT_INVERT, + + BLTOP_PATTERN = BLIT_PATTERN, + BLTOP_PATTERN_AND_SRC = BLIT_SRC | BLIT_PATTERN | BLIT_AND +}; + +enum LineOp +{ + LINOP_SET, + LINOP_OR, + LINOP_AND, + LINOP_XOR +}; + +extern char NineShadesOfGrey[9][8]; + +// Fast unsigned integer square root +unsigned bm_usqrt(unsigned n); + +// Initialize a bitmap structure, size is given in char cells (8x8 pixel) +void bm_init(Bitmap * bm, char * data, char cw, char ch); + +// Initialize a bitmap structure with allocated memory, size is given in char cells (8x8 pixel) +void bm_alloc(Bitmap * bm, char cw, char ch); + +// Free the memory of a bitmap +void bm_free(Bitmap * bm); + +// Fill a bitmap with the data byte +void bm_fill(const Bitmap * bm, char data); + + +void bm_scan_fill(int left, int right, char * lp, int x0, int x1, char pat); + +// Fill a circle with center x/y and radius r and the 8x8 pattern pat. +void bm_circle_fill(const Bitmap * bm, const ClipRect * clip, int x, int y, char r, const char * pat); + +// Fill a trapezoid with horizontal top and bottom, top left is in x0, top right in x1 +// dx0 and dx1 are the horizontal delta for each line. Coordinates are in 16.16 fixed point +// numbers. y0 and y1 are vertical coordinates in pixel. +void bm_trapezoid_fill(const Bitmap * bm, const ClipRect * clip, long x0, long x1, long dx0, long dx1, int y0, int y1, const char * pat); + +// Fill a triangle with a pattern, coordinate pairs x0/y0, x1/y1 and x2/y2 are in pixel +void bm_triangle_fill(const Bitmap * bm, const ClipRect * clip, int x0, int y0, int x1, int y1, int x2, int y2, const char * pat); + +// Fill a quad with a pattern, coordinate pairs are in pixel +void bm_quad_fill(const Bitmap * bm, const ClipRect * clip, int x0, int y0, int x1, int y1, int x2, int y2, int x3, int y3, const char * pat); + +// Fill a convex polygon with a pattern, coordinate pairs x[]/y[] are in pixel +void bm_polygon_fill(const Bitmap * bm, const ClipRect * clip, int * x, int * y, char num, const char * pat); + +// Fill an arbitrary polygon with a pattern, coordinate pairs x[]/y[] are in pixel, maximum size is +// sixteen vertices +void bm_polygon_nc_fill(const Bitmap * bm, const ClipRect * clip, int * x, int * y, char num, const char * pat); + +// Set a single pixel +inline void bm_set(const Bitmap * bm, int x, int y); + +// Clear a single pixel +inline void bm_clr(const Bitmap * bm, int x, int y); + +// Get the state of a single pixel +inline bool bm_get(const Bitmap * bm, int x, int y); + +// Set or clear a single pixel +inline void bm_put(const Bitmap * bm, int x, int y, bool c); + +// Draw an unclipped line using an eight bit pattern +void bmu_line(const Bitmap * bm, int x0, int y0, int x1, int y1, char pattern, LineOp op); + +// Draw a clipped line using an eight bit pattern +void bm_line(const Bitmap * bm, const ClipRect * clip, int x0, int y0, int x1, int y1, char pattern, LineOp op); + +// Unclipped bit blit +void bmu_bitblit(const Bitmap * dbm, int dx, int dy, const Bitmap * sbm, int sx, int sy, int w, int h, const char * pattern, BlitOp op); + +// Unclipped rectangle fill +inline void bmu_rect_fill(const Bitmap * dbm, int dx, int dy, int w, int h); + +// Unclipped rectangle clear +inline void bmu_rect_clear(const Bitmap * dbm, int dx, int dy, int w, int h); + +// Unclipped rectangle pattern fill +inline void bmu_rect_pattern(const Bitmap * dbm, int dx, int dy, int w, int h, const char * pattern); + +// Unclipped rectangle copy +inline void bmu_rect_copy(const Bitmap * dbm, int dx, int dy, const Bitmap * sbm, int sx, int sy, int w, int h); + + +// Clipped bit blit +void bm_bitblit(const Bitmap * dbm, const ClipRect * clip, int dx, int dy, const Bitmap * sbm, int sx, int sy, int w, int h, const char * pattern, BlitOp op); + +// Clipped rectangle fill +inline void bm_rect_fill(const Bitmap * dbm, const ClipRect * clip, int dx, int dy, int w, int h); + +// Clipped rectangle clear +inline void bm_rect_clear(const Bitmap * dbm, const ClipRect * clip, int dx, int dy, int w, int h); + +// Clipped rectangle pattern fill +inline void bm_rect_pattern(const Bitmap * dbm, const ClipRect * clip, int dx, int dy, int w, int h, const char * pattern); + +// Clipped rectangle copy +inline void bm_rect_copy(const Bitmap * dbm, const ClipRect * clip, int dx, int dy, const Bitmap * sbm, int sx, int sy, int w, int h); + + +// Unclipped text rendering +int bmu_text(const Bitmap * bm, char lx, const char * str, char len); + +// Calculate size of a char range +int bmu_text_size(const char * str, char len); + +// Unclipped text output to an arbitrary location using a bit blit +int bmu_put_chars(const Bitmap * bm, int x, int y, const char * str, char len, BlitOp op); + +// Clipped text output to an arbitrary location using a bit blit +int bm_put_chars(const Bitmap * bm, const ClipRect * clip, int x, int y, const char * str, char len, BlitOp op); + +// Clipped text output of a zero terminated string to an arbitrary location using a bit blit +inline int bm_put_string(const Bitmap * bm, const ClipRect * clip, int x, int y, const char * str, BlitOp op); + + +// Linear transformation of a source bitmap rectangle to a destination rectangle +int bm_transform(const Bitmap * dbm, const ClipRect * clip, int dx, int dy, int w, int h, const Bitmap * sbm, int sx, int sy, int dxx, int dxy, int dyx, int dyy); + + +#pragma compile("bitmap.c") + +#endif diff --git a/oscar64/include/gfx/mcbitmap.c b/oscar64/include/gfx/mcbitmap.c new file mode 100644 index 0000000..bf5f02f --- /dev/null +++ b/oscar64/include/gfx/mcbitmap.c @@ -0,0 +1,921 @@ +#include "mcbitmap.h" +#include + +static char andmask[8] = {0x3f, 0x3f, 0xcf, 0xcf, 0xf3, 0xf3, 0xfc, 0xfc}; +static char ormask[8] = {0xc0, 0xc0, 0x30, 0x30, 0x0c, 0x0c, 0x03, 0x03}; +static char cbytes[4] = {0x00, 0x55, 0xaa, 0xff}; + +char MixedColors[4][4][8] = { + { + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44}, + {0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88}, + {0x33, 0xcc, 0x33, 0xcc, 0x33, 0xcc, 0x33, 0xcc}, + }, + { + {0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11}, + {0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55}, + {0x66, 0x99, 0x66, 0x99, 0x66, 0x99, 0x66, 0x99}, + {0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd}, + }, + { + {0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22}, + {0x99, 0x66, 0x99, 0x66, 0x99, 0x66, 0x99, 0x66}, + {0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa}, + {0xbb, 0xee, 0xbb, 0xee, 0xbb, 0xee, 0xbb, 0xee}, + }, + { + {0xcc, 0x33, 0xcc, 0x33, 0xcc, 0x33, 0xcc, 0x33}, + {0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77}, + {0xee, 0xbb, 0xee, 0xbb, 0xee, 0xbb, 0xee, 0xbb}, + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + }, +}; + +void bmmc_put(const Bitmap * bm, int x, int y, char c) +{ + char * dp = bm->data + bm->cwidth * (y & ~7) + ((x & ~7) | (y & 7)); + char pat = cbytes[c & 3]; + + *dp = ((*dp ^ pat) & andmask[x & 7]) ^ pat; +} + +char bmmc_get(const Bitmap * bm, int x, int y) +{ + char * dp = bm->data + bm->cwidth * (y & ~7) + (x & ~7) + (y & 7); + + return (*dp >> (6 - (x & 6))) & 3; +} + + +void bmmcu_circle(const Bitmap * bm, int x, int y, char r, char color) +{ + char * lpt = bm->data + bm->cwidth * (y & ~7) + (y & 7); + char * lpb = lpt; + int stride = 8 * bm->cwidth - 8; + char pat = ~cbytes[color & 3]; + + char rx = r, ry = 0; + int d = r / 2; + char * dp; + + while (rx > 0) + { + dp = lpt + ((x + rx) & ~7); + *dp = ((*dp ^ pat) | ormask[(x + rx) & 7]) ^ pat; + dp = lpt + ((x - rx) & ~7); + *dp = ((*dp ^ pat) | ormask[(x - rx) & 7]) ^ pat; + + dp = lpb + ((x + rx) & ~7); + *dp = ((*dp ^ pat) | ormask[(x + rx) & 7]) ^ pat; + dp = lpb + ((x - rx) & ~7); + *dp = ((*dp ^ pat) | ormask[(x - rx) & 7]) ^ pat; + + if (d >= 0) + { + ry++; + d -= ry; + lpb ++; + if (!((int)lpb & 7)) + lpb += stride; + if (!((int)lpt & 7)) + lpt -= stride; + lpt--; + } + if (d < 0) + { + rx--; + d += rx; + } + } + + dp = lpt + (x & ~7); + *dp = ((*dp ^ pat) | ormask[x & 7]) ^ pat; + dp = lpb + (x & ~7); + *dp = ((*dp ^ pat) | ormask[x & 7]) ^ pat; +} + +void bmmc_circle2(const Bitmap * bm, const ClipRect * clip, int x, int y, char r, char color) +{ + char * lpt = bm->data + bm->cwidth * (y & ~7) + (y & 7); + char * lpb = lpt; + int stride = 8 * bm->cwidth - 8; + char pat = ~cbytes[color & 3]; + + char rx = r, ry = 0; + int d = r / 2; + char * dp; + + y -= clip->top; + unsigned h = clip->bottom - clip->top; + unsigned y0 = y, y1 = y; + + while (rx > 0) + { + int x0 = x - rx, x1 = x + rx; + + bool c0 = x0 >= clip->left && x0 < clip->right; + bool c1 = x1 >= clip->left && x1 < clip->right; + + if ((unsigned)y0 < h) + { + if (c0) + { + dp = lpt + (x0 & ~7); + *dp = ((*dp ^ pat) | ormask[x0 & 7]) ^ pat; + } + if (c1) + { + dp = lpt + (x1 & ~7); + *dp = ((*dp ^ pat) | ormask[x1 & 7]) ^ pat; + } + } + + if ((unsigned)y1 < h) + { + if (c0) + { + dp = lpb + (x0 & ~7); + *dp = ((*dp ^ pat) | ormask[x0 & 7]) ^ pat; + } + if (c1) + { + dp = lpb + (x1 & ~7); + *dp = ((*dp ^ pat) | ormask[x1 & 7]) ^ pat; + } + } + + if (d >= 0) + { + ry++; y0--; y1++; + d -= ry; + lpb ++; + if (!((int)lpb & 7)) + lpb += stride; + if (!((int)lpt & 7)) + lpt -= stride; + lpt--; + } + if (d < 0) + { + rx--; + d += rx; + } + } + + if (x >= clip->left && x < clip->right) + { + if ((unsigned)y0 < h) + { + dp = lpt + (x & ~7); + *dp = ((*dp ^ pat) | ormask[x & 7]) ^ pat; + } + if ((unsigned)y1 < h) + { + dp = lpb + (x & ~7); + *dp = ((*dp ^ pat) | ormask[x & 7]) ^ pat; + } + } +} + +void bmmc_circle(const Bitmap * bm, const ClipRect * clip, int x, int y, char r, char color) +{ + if (x - r >= clip->left && x + r < clip->right && y - r >= clip->top && y + r < clip->bottom) + bmmcu_circle(bm, x, y, r, color); + else if (x - r < clip->right && x + r >= clip->left && y - r < clip->bottom && y + r >= clip->top) + bmmc_circle2(bm, clip, x, y, r, color); +} + +void bmmc_scan_fill(int left, int right, char * lp, int x0, int x1, char pat) +{ + bm_scan_fill(left, right, lp, x0 & ~1, (x1 + 1) & ~1, pat); +} + +void bmmc_circle_fill(const Bitmap * bm, const ClipRect * clip, int x, int y, char r, const char * pattern) +{ + int y0 = y - r, y1 = y + r + 1; + if (y0 < clip->top) + y0 = clip->top; + if (y1 > clip->bottom) + y1 = clip->bottom; + + const char * pat = pattern; + char * lp = bm->data + bm->cwidth * (y0 & ~7) + (y0 & 7); + int stride = 8 * bm->cwidth - 8; + + unsigned rr = r * r + r; + unsigned d = rr - (y0 - y) * (y0 - y); + int tt = 2 * (y0 - y) + 1; + for(char iy=y0; iy<(char)y1; iy++) + { + int t = bm_usqrt(d); + + bmmc_scan_fill(clip->left, clip->right, lp, x - t, x + t + 1, pat[iy & 7]); + lp ++; + if (!((int)lp & 7)) + lp += stride; + + d -= tt; + tt += 2; + } +} + +void bmmc_trapezoid_fill(const Bitmap * bm, const ClipRect * clip, long x0, long x1, long dx0, long dx1, int y0, int y1, const char * pattern) +{ + if (y1 <= clip->top || y0 >= clip->bottom) + return; + + long tx0 = x0, tx1 = x1; + + if (y1 > clip->bottom) + y1 = clip->bottom; + if (y0 < clip->top) + { + tx0 += (clip->top - y0) * dx0; + tx1 += (clip->top - y0) * dx1; + y0 = clip->top; + } + + const char * pat = pattern; + char * lp = bm->data + bm->cwidth * (y0 & ~7) + (y0 & 7); + int stride = 8 * bm->cwidth - 8; + + for(char iy=y0; iy<(char)y1; iy++) + { + bmmc_scan_fill(clip->left, clip->right, lp, tx0 >> 16, tx1 >> 16, pat[iy & 7]); + tx0 += dx0; + tx1 += dx1; + lp ++; + if (!((int)lp & 7)) + lp += stride; + } +} + + +void bmmc_triangle_fill(const Bitmap * bm, const ClipRect * clip, int x0, int y0, int x1, int y1, int x2, int y2, const char * pat) +{ + int t; + if (y1 < y0 && y1 < y2) + { + t = y0; y0 = y1; y1 = t; + t = x0; x0 = x1; x1 = t; + } + else if (y2 < y0) + { + t = y0; y0 = y2; y2 = t; + t = x0; x0 = x2; x2 = t; + } + + if (y2 < y1) + { + t = y1; y1 = y2; y2 = t; + t = x1; x1 = x2; x2 = t; + } + + if (y0 < y2) + { + long dx1, lx1; + long dx2 = ((long)(x2 - x0) << 16) / (y2 - y0); + long lx2 = (long)x0 << 16; + + if (y1 > y0) + { + dx1 = ((long)(x1 - x0) << 16) / (y1 - y0); + + if (dx1 < dx2) + bmmc_trapezoid_fill(bm, clip, lx2, lx2, dx1, dx2, y0, y1, pat); + else + bmmc_trapezoid_fill(bm, clip, lx2, lx2, dx2, dx1, y0, y1, pat); + if (y2 == y1) + return; + + lx2 += dx2 * (y1 - y0); + } + + dx1 = ((long)(x2 - x1) << 16) / (y2 - y1); + lx1 = (long)x1 << 16; + + if (lx1 < lx2) + bmmc_trapezoid_fill(bm, clip, lx1, lx2, dx1, dx2, y1, y2, pat); + else + bmmc_trapezoid_fill(bm, clip, lx2, lx1, dx2, dx1, y1, y2, pat); + } +} + + +void bmmc_quad_fill(const Bitmap * bm, const ClipRect * clip, int x0, int y0, int x1, int y1, int x2, int y2, int x3, int y3, const char * pat) +{ + bmmc_triangle_fill(bm, clip, x0, y0, x1, y1, x2, y2, pat); + bmmc_triangle_fill(bm, clip, x0, y0, x2, y2, x3, y3, pat); +} + + +void bmmc_polygon_fill(const Bitmap * bm, const ClipRect * clip, int * px, int * py, char num, const char * pat) +{ + char mi = 0; + int my = py[0]; + + for(char i=1; i my) + { + long dlx = (((long)px[li] << 16) - lx) / (py[li] - my); + long drx = (((long)px[ri] << 16) - rx) / (py[ri] - my); + + if (lx < rx || lx == rx && dlx < drx) + bmmc_trapezoid_fill(bm, clip, lx, rx, dlx, drx, my, ty, pat); + else + bmmc_trapezoid_fill(bm, clip, rx, lx, drx, dlx, my, ty, pat); + + lx += (ty - my) * dlx; + rx += (ty - my) * drx; + + my = ty; + + while (py[li] == my) + { + lx = (long)px[li] << 16; + if (li == 0) + li = num; + li--; + } + + while (py[ri] == my) + { + rx = (long)px[ri] << 16; + ri++; + if (ri == num) + ri = 0; + } + + ty = py[li] < py[ri] ? py[li] : py[ri]; + } + +} + +struct Edge +{ + char minY, maxY; + long px, dx; + Edge * next; +}; + +void bmmc_polygon_nc_fill(const Bitmap * bm, const ClipRect * clip, int * px, int * py, char num, const char * pattern) +{ + Edge * first = nullptr, * active = nullptr; + Edge * e = (Edge *)BLIT_CODE; + + char n = num; + if (n > 16) + n = 16; + + int top = clip->top, bottom = clip->bottom; + + for(char i=0; i= n) + j = 0; + + if (py[i] != py[j]) + { + if (py[i] > py[j]) + { + k = j; j = i; + } + + int minY = py[k], maxY = py[j]; + if (minY < bottom && maxY > top) + { + e->px = ((long)px[k] << 16) + 0x8000; + e->dx = (((long)px[j] << 16) - e->px) / (maxY - minY); + + if (minY < top) + { + e->px += e->dx * (top - minY); + minY = top; + } + if (maxY > bottom) + maxY = bottom; + + e->minY = minY; e->maxY = maxY; + + Edge * pp = nullptr, * pe = first; + + while (pe && minY >= pe->minY) + { + pp = pe; + pe = pe->next; + } + + e->next = pe; + if (pp) + pp->next = e; + else + first = e; + + e++; + } + } + } + + if (first) + { + char y = first->minY; + + const char * pat = pattern; + char * lp = bm->data + bm->cwidth * (y & ~7) + (y & 7); + int stride = 8 * bm->cwidth - 8; + + while (first || active) + { + while (first && first->minY == y) + { + Edge * next = first->next; + + Edge * pp = nullptr, * pe = active; + while (pe && (first->px > pe->px || first->px == pe->px && first->dx > pe->dx)) + { + pp = pe; + pe = pe->next; + } + + first->next = pe; + if (pp) + pp->next = first; + else + active = first; + + first = next; + } + + Edge * e0 = active; + while (e0) + { + Edge * e1 = e0->next; + bmmc_scan_fill(clip->left, clip->right, lp, e0->px >> 16, e1->px >> 16, pat[y & 7]); + e0 = e1->next; + } + + lp ++; + if (!((int)lp & 7)) + lp += stride; + + y++; + + // remove final edges + Edge * pp = nullptr, * pe = active; + while (pe) + { + if (pe->maxY == y) + { + if (pp) + pp->next = pe->next; + else + active = pe->next; + } + else + { + pe->px += pe->dx; + pp = pe; + } + pe = pe->next; + } + } + } +} + + +#define REG_SP 0x03 +#define REG_DP 0x05 +#define REG_PAT 0x07 +#define REG_S0 0x08 +#define REG_S1 0x09 +#define REG_D0 0x0a +#define REG_D1 0x0b + +static void mbuildline(char ly, char lx, int dx, int dy, int stride, bool left, bool up, char color) +{ + char ip = 0; + + // ylow + ip += asm_im(BLIT_CODE + ip, ASM_LDY, ly); + ip += asm_im(BLIT_CODE + ip, ASM_LDX, lx); + + // set pixel + + ip += asm_iy(BLIT_CODE + ip, ASM_LDA, REG_SP); + ip += asm_im(BLIT_CODE + ip, ASM_EOR, color); + ip += asm_zp(BLIT_CODE + ip, ASM_ORA, REG_D0); + ip += asm_im(BLIT_CODE + ip, ASM_EOR, color); + ip += asm_iy(BLIT_CODE + ip, ASM_STA, REG_SP); + + // m >= 0 + ip += asm_zp(BLIT_CODE + ip, ASM_LDA, REG_DP + 1); + ip += asm_rl(BLIT_CODE + ip, ASM_BMI, 5 + 15 + 13); + + ip += asm_np(BLIT_CODE + ip, up ? ASM_DEY : ASM_INY); + ip += asm_im(BLIT_CODE + ip, ASM_CPY, up ? 0xff : 0x08); + ip += asm_rl(BLIT_CODE + ip, ASM_BNE, 15); + + ip += asm_np(BLIT_CODE + ip, ASM_CLC); + ip += asm_zp(BLIT_CODE + ip, ASM_LDA, REG_SP); + ip += asm_im(BLIT_CODE + ip, ASM_ADC, stride & 0xff); + ip += asm_zp(BLIT_CODE + ip, ASM_STA, REG_SP); + ip += asm_zp(BLIT_CODE + ip, ASM_LDA, REG_SP + 1); + ip += asm_im(BLIT_CODE + ip, ASM_ADC, stride >> 8); + ip += asm_zp(BLIT_CODE + ip, ASM_STA, REG_SP + 1); + ip += asm_im(BLIT_CODE + ip, ASM_LDY, up ? 0x07 : 0x00); + + ip += asm_np(BLIT_CODE + ip, ASM_SEC); + ip += asm_zp(BLIT_CODE + ip, ASM_LDA, REG_DP); + ip += asm_im(BLIT_CODE + ip, ASM_SBC, dx & 0xff); + ip += asm_zp(BLIT_CODE + ip, ASM_STA, REG_DP); + ip += asm_zp(BLIT_CODE + ip, ASM_LDA, REG_DP + 1); + ip += asm_im(BLIT_CODE + ip, ASM_SBC, dx >> 8); + ip += asm_zp(BLIT_CODE + ip, ASM_STA, REG_DP + 1); + + // m < 0 + ip += asm_zp(BLIT_CODE + ip, ASM_LDA, REG_DP + 1); + ip += asm_rl(BLIT_CODE + ip, ASM_BPL, 4 + 15 + 13); + + ip += asm_zp(BLIT_CODE + ip, left ? ASM_ASL : ASM_LSR, REG_D0); + ip += asm_zp(BLIT_CODE + ip, left ? ASM_ROL : ASM_ROR, REG_D0); + ip += asm_rl(BLIT_CODE + ip, ASM_BCC, 15); + + ip += asm_zp(BLIT_CODE + ip, left ? ASM_ROL : ASM_ROR, REG_D0); + ip += asm_np(BLIT_CODE + ip, ASM_CLC); + ip += asm_zp(BLIT_CODE + ip, ASM_LDA, REG_SP); + ip += asm_im(BLIT_CODE + ip, ASM_ADC, left ? 0xf8 : 0x08); + ip += asm_zp(BLIT_CODE + ip, ASM_STA, REG_SP); + ip += asm_zp(BLIT_CODE + ip, ASM_LDA, REG_SP + 1); + ip += asm_im(BLIT_CODE + ip, ASM_ADC, left ? 0xff : 0x00); + ip += asm_zp(BLIT_CODE + ip, ASM_STA, REG_SP + 1); + + ip += asm_np(BLIT_CODE + ip, ASM_CLC); + ip += asm_zp(BLIT_CODE + ip, ASM_LDA, REG_DP); + ip += asm_im(BLIT_CODE + ip, ASM_ADC, dy & 0xff); + ip += asm_zp(BLIT_CODE + ip, ASM_STA, REG_DP); + ip += asm_zp(BLIT_CODE + ip, ASM_LDA, REG_DP + 1); + ip += asm_im(BLIT_CODE + ip, ASM_ADC, dy >> 8); + ip += asm_zp(BLIT_CODE + ip, ASM_STA, REG_DP + 1); + + // l -- + ip += asm_np(BLIT_CODE + ip, ASM_DEX); + ip += asm_rl(BLIT_CODE + ip, ASM_BNE, 2 - ip); + ip += asm_zp(BLIT_CODE + ip, ASM_DEC, REG_D1); + ip += asm_rl(BLIT_CODE + ip, ASM_BPL, 2 - ip); + + ip += asm_np(BLIT_CODE + ip, ASM_RTS); +} + +#pragma native(mbuildline) + +static inline void mcallline(byte * dst, byte bit, int m, char lh) +{ + __asm + { + lda dst + sta REG_SP + lda dst + 1 + sta REG_SP + 1 + + lda m + sta REG_DP + lda m + 1 + sta REG_DP + 1 + + lda lh + sta REG_D1 + + lda bit + sta REG_D0 + + jsr BLIT_CODE + } +} + +void bmmcu_line(const Bitmap * bm, int x0, int y0, int x1, int y1, char color) +{ + x0 >>= 1; + x1 >>= 1; + + int dx = x1 - x0, dy = y1 - y0; + byte quad = 0; + if (dx < 0) + { + quad = 1; + dx = -dx; + } + if (dy < 0) + { + quad |= 2; + dy = -dy; + } + + int l; + if (dx > dy) + l = dx; + else + l = dy; + + int m = dy - dx; + dx *= 2; + dy *= 2; + + char * dp = bm->data + bm->cwidth * (y0 & ~7) + 2 * (x0 & ~3); + char bit = ormask[2 * (x0 & 3)]; + char ry = y0 & 7; + int stride = 8 * bm->cwidth; + + mbuildline(ry, (l + 1) & 0xff, dx, dy, (quad & 2) ? -stride : stride, quad & 1, quad & 2, ~cbytes[color]); + + mcallline(dp, bit, m, l >> 8); +} + + +static int mmuldiv(int x, int mul, int div) +{ + return (int)((long)x * mul / div); +} + +void bmmc_line(const Bitmap * bm, const ClipRect * clip, int x0, int y0, int x1, int y1, char color) +{ + int dx = x1 - x0, dy = y1 - y0; + + if (x0 < x1) + { + if (x1 < clip->left || x0 >= clip->right) + return; + + if (x0 < clip->left) + { + y0 += mmuldiv(clip->left - x0, dy, dx); + x0 = clip->left; + } + + if (x1 >= clip->right) + { + y1 -= mmuldiv(x1 + 1 - clip->right, dy, dx); + x1 = clip->right - 1; + } + } + else if (x1 < x0) + { + if (x0 < clip->left || x1 >= clip->right) + return; + + if (x1 < clip->left) + { + y1 += mmuldiv(clip->left - x1, dy, dx); + x1 = clip->left; + } + + if (x0 >= clip->right) + { + y0 -= mmuldiv(x0 + 1- clip->right, dy, dx); + x0 = clip->right - 1; + } + } + else + { + if (x0 < clip->left || x0 >= clip->right) + return; + } + + if (y0 < y1) + { + if (y1 < clip->top || y0 >= clip->bottom) + return; + + if (y0 < clip->top) + { + x0 += mmuldiv(clip->top - y0, dx, dy); + y0 = clip->top; + } + + if (y1 >= clip->bottom) + { + x1 -= mmuldiv(y1 + 1 - clip->bottom, dx, dy); + y1 = clip->bottom - 1; + } + } + else if (y1 < y0) + { + if (y0 < clip->top || y1 >= clip->bottom) + return; + + if (y1 < clip->top) + { + x1 += mmuldiv(clip->top - y1, dx, dy); + y1 = clip->top; + } + + if (y0 >= clip->bottom) + { + x0 -= mmuldiv(y0 + 1 - clip->bottom, dx, dy); + y0 = clip->bottom - 1; + } + } + else + { + if (y0 < clip->top || y0 >= clip->bottom) + return; + } + + bmmcu_line(bm, x0, y0, x1, y1, color); +} + + +void bmmcu_rect_fill(const Bitmap * dbm, int dx, int dy, int w, int h, char color) +{ + int rx = (dx + w + 1) & ~1; + dx &= ~1; + + bmu_bitblit(dbm, dx, dy, dbm, dx, dy, rx - dx, h, MixedColors[color][color], BLTOP_PATTERN); +} + +void bmmcu_rect_pattern(const Bitmap * dbm, int dx, int dy, int w, int h, const char * pattern) +{ + int rx = (dx + w + 1) & ~1; + dx &= ~1; + + bmu_bitblit(dbm, dx, dy, dbm, dx, dy, rx - dx, h, pattern, BLTOP_PATTERN); +} + +void bmmcu_rect_copy(const Bitmap * dbm, int dx, int dy, const Bitmap * sbm, int sx, int sy, int w, int h) +{ + int rx = (dx + w + 1) & ~1; + dx &= ~1; + sx &= ~1; + + bmu_bitblit(dbm, dx, dy, sbm, sx, sy, rx - dx, h, nullptr, BLTOP_COPY); +} + + +void bmmc_rect_fill(const Bitmap * dbm, const ClipRect * clip, int dx, int dy, int w, int h, char color) +{ + int rx = (dx + w + 1) & ~1; + dx &= ~1; + + bm_bitblit(dbm, clip, dx, dy, dbm, dx, dy, rx - dx, h, MixedColors[color][color], BLTOP_PATTERN); +} + +void bmmc_rect_pattern(const Bitmap * dbm, const ClipRect * clip, int dx, int dy, int w, int h, const char * pattern) +{ + int rx = (dx + w + 1) & ~1; + dx &= ~1; + + bm_bitblit(dbm, clip, dx, dy, dbm, dx, dy, rx - dx, h, pattern, BLTOP_PATTERN); +} + +void bmmc_rect_copy(const Bitmap * dbm, const ClipRect * clip, int dx, int dy, const Bitmap * sbm, int sx, int sy, int w, int h) +{ + int rx = (dx + w + 1) & ~1; + dx &= ~1; + sx &= ~1; + + bm_bitblit(dbm, clip, dx, dy, sbm, sx, sy, rx - dx, h, nullptr, BLTOP_COPY); +} + +extern byte BLIT_CODE[16 * 14]; + +inline void bmmc_putdp(char * dp, char x, char c) +{ + dp += 2 * (x & ~3); + + *dp = (*dp & andmask[2 * (x & 3)]) | (c & ormask[2 * (x & 3)]); +} + +inline char bmmc_getdp(char * dp, char x) +{ + dp += 2 * (x & ~3); + + return (*dp >> 2 * (3 - (x & 3))) & 3; +} + +inline char bmmc_checkdp(char * dp, char x, char c) +{ + dp += 2 * (x & ~3); + + return ((*dp ^ c) & ormask[2 * (x & 3)]); +} + + +void bmmc_flood_fill(const Bitmap * bm, const ClipRect * clip, int x, int y, char color) +{ + char bx = (char)(x >> 1), by = (char)y; + char sp = 0; + char left = clip->left >> 1, right = clip->right >> 1; + + char * dp = bm->data + bm->cwidth * (by & ~7) + (by & 7); + + char back = cbytes[bmmc_getdp(dp, bx)]; + + color = cbytes[color]; + + if (back == color) + return; + + BLIT_CODE[sp++] = bx; + BLIT_CODE[sp++] = by; + + while (sp > 0) + { + by = BLIT_CODE[--sp]; + bx = BLIT_CODE[--sp]; + + dp = bm->data + bm->cwidth * (by & ~7) + (by & 7); + + if (bmmc_checkdp(dp, bx, back) == 0) + { + bmmc_putdp(dp, bx, color); + + char x0 = bx; + while (x0 > left && bmmc_checkdp(dp, x0 - 1, back) == 0) + { + x0--; + bmmc_putdp(dp, x0, color); + } + + char x1 = bx; + while (x1 + 1 < right && bmmc_checkdp(dp, x1 + 1, back) == 0) + { + x1++; + bmmc_putdp(dp, x1, color); + } + + bool check = false; + if (by > clip->top) + { + dp = bm->data + bm->cwidth * ((by - 1) & ~7) + ((by - 1) & 7); + + for(bx=x0; bx<=x1; bx++) + { + if (bmmc_checkdp(dp, bx, back) == 0) + { + if (!check) + { + BLIT_CODE[sp++] = bx; + BLIT_CODE[sp++] = by - 1; + check = true; + } + } + else + check = false; + } + } + + check = false; + if (by + 1 < clip->bottom) + { + dp = bm->data + bm->cwidth * ((by + 1) & ~7) + ((by + 1) & 7); + + for(bx=x0; bx<=x1; bx++) + { + if (bmmc_checkdp(dp, bx, back) == 0) + { + if (!check) + { + BLIT_CODE[sp++] = bx; + BLIT_CODE[sp++] = by + 1; + check = true; + } + } + else + check = false; + } + } + } + } +} + +#pragma native(bmmc_flood_fill) diff --git a/oscar64/include/gfx/mcbitmap.h b/oscar64/include/gfx/mcbitmap.h new file mode 100644 index 0000000..7ae8831 --- /dev/null +++ b/oscar64/include/gfx/mcbitmap.h @@ -0,0 +1,76 @@ +#ifndef MCBITMAP_H +#define MCBITMAP_H + +#include "bitmap.h" + + +extern char MixedColors[4][4][8]; + + +// Set a single pixel +void bmmc_put(const Bitmap * bm, int x, int y, char c); + + +// Get the state of a single pixel +char bmmc_get(const Bitmap * bm, int x, int y); + + +// Draw an unclipped line using an eight bit pattern +void bmmcu_line(const Bitmap * bm, int x0, int y0, int x1, int y1, char color); + +// Draw a clipped line using an eight bit pattern +void bmmc_line(const Bitmap * bm, const ClipRect * clip, int x0, int y0, int x1, int y1, char color); + + +inline void bmmc_scan_fill(int left, int right, char * lp, int x0, int x1, char pat); + +void bmmcu_circle(const Bitmap * bm, int x, int y, char r, char color); + +void bmmc_circle(const Bitmap * bm, const ClipRect * clip, int x, int y, char r, char color); + +// Fill a circle with center x/y and radius r and the 8x8 pattern pat. +void bmmc_circle_fill(const Bitmap * bm, const ClipRect * clip, int x, int y, char r, const char * pat); + +// Fill a trapezoid with horizontal top and bottom, top left is in x0, top right in x1 +// dx0 and dx1 are the horizontal delta for each line. Coordinates are in 16.16 fixed point +// numbers. y0 and y1 are vertical coordinates in pixel. +void bmmc_trapezoid_fill(const Bitmap * bm, const ClipRect * clip, long x0, long x1, long dx0, long dx1, int y0, int y1, const char * pat); + +// Fill a triangle with a pattern, coordinate pairs x0/y0, x1/y1 and x2/y2 are in pixel +void bmmc_triangle_fill(const Bitmap * bm, const ClipRect * clip, int x0, int y0, int x1, int y1, int x2, int y2, const char * pat); + +// Fill a quad with a pattern, coordinate pairs are in pixel +void bmmc_quad_fill(const Bitmap * bm, const ClipRect * clip, int x0, int y0, int x1, int y1, int x2, int y2, int x3, int y3, const char * pat); + + +// Fill a convex polygon with a pattern, coordinate pairs x[]/y[] are in pixel +void bmmc_polygon_fill(const Bitmap * bm, const ClipRect * clip, int * x, int * y, char num, const char * pat); + +// Fill an arbitrary polygon with a pattern, coordinate pairs x[]/y[] are in pixel, maximum size is +// sixteen vertices +void bmmc_polygon_nc_fill(const Bitmap * bm, const ClipRect * clip, int * x, int * y, char num, const char * pat); + +inline void bmmcu_rect_fill(const Bitmap * dbm, int dx, int dy, int w, int h, char color); + +// Unclipped rectangle pattern fill +inline void bmmcu_rect_pattern(const Bitmap * dbm, int dx, int dy, int w, int h, const char * pattern); + +// Unclipped rectangle copy +inline void bmmcu_rect_copy(const Bitmap * dbm, int dx, int dy, const Bitmap * sbm, int sx, int sy, int w, int h); + + +// Clipped rectangle fill +inline void bmmc_rect_fill(const Bitmap * dbm, const ClipRect * clip, int dx, int dy, int w, int h, char color); + +// Clipped rectangle pattern fill +inline void bmmc_rect_pattern(const Bitmap * dbm, const ClipRect * clip, int dx, int dy, int w, int h, const char * pattern); + +// Clipped rectangle copy +inline void bmmc_rect_copy(const Bitmap * dbm, const ClipRect * clip, int dx, int dy, const Bitmap * sbm, int sx, int sy, int w, int h); + + +void bmmc_flood_fill(const Bitmap * bm, const ClipRect * clip, int x, int y, char color); + +#pragma compile("mcbitmap.c") + +#endif diff --git a/oscar64/include/gfx/tinyfont.c b/oscar64/include/gfx/tinyfont.c new file mode 100644 index 0000000..4fc1839 --- /dev/null +++ b/oscar64/include/gfx/tinyfont.c @@ -0,0 +1,39 @@ +#include "tinyfont.h" + +const char TinyFont[] = { + 0x00, 0x04, 0x05, 0x08, 0x0D, 0x12, 0x17, 0x1C, 0x1D, 0x21, 0x25, 0x2A, 0x2D, 0x2E, 0x31, 0x32, + 0x35, 0x39, 0x3D, 0x41, 0x45, 0x49, 0x4D, 0x51, 0x55, 0x59, 0x5D, 0x5E, 0x5F, 0x62, 0x65, 0x68, + 0x6C, 0x71, 0x76, 0x7A, 0x7E, 0x82, 0x86, 0x8A, 0x8E, 0x92, 0x95, 0x99, 0x9D, 0xA1, 0xA6, 0xAB, + 0xAF, 0xB3, 0xB7, 0xBB, 0xBF, 0xC4, 0xC8, 0xCD, 0xD2, 0xD7, 0xDC, 0xE1, 0xE5, 0xE8, 0xEC, 0xF1, + 0xF5, 0xF9, 0xFD, 0x01, 0x05, 0x09, 0x0D, 0x10, 0x14, 0x18, 0x19, 0x1B, 0x1F, 0x21, 0x26, 0x2A, + 0x2E, 0x32, 0x36, 0x3A, 0x3E, 0x41, 0x45, 0x49, 0x4E, 0x52, 0x56, 0x5A, 0x5E, 0x5F, 0x63, 0x67, + 0x10, 0x04, 0x0C, 0x14, 0x14, 0x14, 0x14, 0x04, 0x10, 0x10, 0x14, 0x0C, 0x04, 0x0C, 0x04, 0x0C, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x04, 0x04, 0x0C, 0x0C, 0x0C, 0x10, + 0x14, 0x14, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x0C, 0x10, 0x10, 0x10, 0x14, 0x14, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x14, 0x10, 0x14, 0x14, 0x14, 0x14, 0x14, 0x10, 0x0C, 0x10, 0x14, 0x10, + 0x10, 0x10, 0x10, 0x11, 0x11, 0x11, 0x0D, 0x11, 0x11, 0x05, 0x09, 0x11, 0x09, 0x15, 0x11, 0x11, + 0x11, 0x11, 0x11, 0x11, 0x0D, 0x11, 0x11, 0x15, 0x11, 0x11, 0x11, 0x11, 0x05, 0x11, 0x11, 0x11, + 0x00, 0x00, 0x00, 0x00, 0xFA, 0xC0, 0x00, 0xC0, 0x28, 0x7C, 0x28, 0x7C, 0x28, 0x20, 0x54, 0xD6, + 0x54, 0x08, 0x04, 0x48, 0x10, 0x24, 0x40, 0x6C, 0x92, 0x92, 0x6C, 0x0A, 0xC0, 0x38, 0x44, 0x82, + 0x82, 0x82, 0x82, 0x44, 0x38, 0x10, 0x54, 0x38, 0x54, 0x10, 0x10, 0x38, 0x10, 0x03, 0x10, 0x10, + 0x10, 0x02, 0x06, 0x38, 0xC0, 0x7C, 0x82, 0x82, 0x7C, 0x22, 0x42, 0xFE, 0x02, 0x46, 0x8A, 0x92, + 0x62, 0x44, 0x82, 0x92, 0x6C, 0xF0, 0x10, 0x3E, 0x10, 0xE4, 0x92, 0x92, 0x8C, 0x7C, 0x92, 0x92, + 0x4C, 0x80, 0x8E, 0x90, 0xE0, 0x6C, 0x92, 0x92, 0x6C, 0x64, 0x92, 0x92, 0x7C, 0x28, 0x0B, 0x10, + 0x28, 0x44, 0x28, 0x28, 0x28, 0x44, 0x28, 0x10, 0x40, 0x80, 0x9A, 0x60, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3E, 0x50, 0x90, 0x50, 0x3E, 0xFE, 0x92, 0x92, 0x6C, 0x7C, 0x82, 0x82, 0x44, 0xFE, 0x82, + 0x82, 0x7C, 0xFE, 0x92, 0x92, 0x82, 0xFE, 0x90, 0x90, 0x80, 0x7C, 0x82, 0x92, 0x1C, 0xFE, 0x10, + 0x10, 0xFE, 0x82, 0xFE, 0x82, 0x84, 0x82, 0x82, 0xFC, 0xFE, 0x10, 0x28, 0xC6, 0xFE, 0x02, 0x02, + 0x02, 0xFE, 0x40, 0x20, 0x40, 0xFE, 0xFE, 0x40, 0x20, 0x10, 0xFE, 0x7C, 0x82, 0x82, 0x7C, 0xFE, + 0x90, 0x90, 0x60, 0x7C, 0x82, 0x84, 0x7A, 0xFE, 0x90, 0x90, 0x6E, 0x64, 0x92, 0x92, 0x4C, 0x80, + 0x80, 0xFE, 0x80, 0x80, 0xFC, 0x02, 0x02, 0xFC, 0xE0, 0x18, 0x06, 0x18, 0xE0, 0xFE, 0x04, 0x08, + 0x04, 0xFE, 0xC6, 0x28, 0x10, 0x28, 0xC6, 0xE0, 0x10, 0x1E, 0x10, 0xE0, 0x86, 0x8A, 0x92, 0xA2, + 0xC2, 0xFE, 0x82, 0x82, 0x82, 0xC0, 0x38, 0x06, 0x82, 0x82, 0x82, 0xFE, 0x20, 0x40, 0x80, 0x40, + 0x20, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x04, 0x2A, 0x2A, 0x1E, 0xFE, 0x22, 0x22, + 0x1C, 0x1C, 0x22, 0x22, 0x14, 0x1C, 0x22, 0x22, 0xFE, 0x1C, 0x2A, 0x2A, 0x18, 0x20, 0x7E, 0xA0, + 0x18, 0x25, 0x25, 0x3E, 0xFE, 0x20, 0x20, 0x1E, 0xBE, 0x01, 0xBE, 0xFE, 0x10, 0x28, 0x46, 0xFC, + 0x02, 0x3E, 0x20, 0x1E, 0x20, 0x1E, 0x3E, 0x20, 0x20, 0x1E, 0x1C, 0x22, 0x22, 0x1C, 0x3F, 0x24, + 0x24, 0x18, 0x18, 0x24, 0x24, 0x3F, 0x3E, 0x10, 0x20, 0x10, 0x12, 0x2A, 0x2A, 0x04, 0x20, 0x7C, + 0x22, 0x3C, 0x02, 0x02, 0x3E, 0x38, 0x04, 0x02, 0x3C, 0x3C, 0x02, 0x1C, 0x02, 0x3C, 0x26, 0x18, + 0x0C, 0x32, 0x38, 0x05, 0x05, 0x3E, 0x26, 0x2A, 0x2A, 0x32, 0x10, 0x6C, 0x82, 0x82, 0xFE, 0x82, + 0x82, 0x6C, 0x10, 0x10, 0x20, 0x10, 0x20, 0x00, 0x00, 0x00, 0x00 +}; diff --git a/oscar64/include/gfx/tinyfont.h b/oscar64/include/gfx/tinyfont.h new file mode 100644 index 0000000..b8fb0c4 --- /dev/null +++ b/oscar64/include/gfx/tinyfont.h @@ -0,0 +1,8 @@ +#ifndef TINYFONT_H +#define TINYFONT_H + +extern const char TinyFont[]; + +#pragma compile("tinyfont.c") + +#endif diff --git a/oscar64/include/gfx/vector3d.c b/oscar64/include/gfx/vector3d.c new file mode 100644 index 0000000..0bf29ec --- /dev/null +++ b/oscar64/include/gfx/vector3d.c @@ -0,0 +1,1032 @@ +#include "vector3d.h" +#include +#include + +void vec2_set(Vector2 * vd, float x, float y) +{ + vd->v[0] = x; + vd->v[1] = y; +} + +void vec2_sum(Vector2 * vd, const Vector2 * v1, const Vector2 * v2) +{ + vd->v[0] = v1->v[0] + v2->v[0]; + vd->v[1] = v1->v[1] + v2->v[1]; +} + +void vec2_diff(Vector2 * vd, const Vector2 * v1, const Vector2 * v2) +{ + vd->v[0] = v1->v[0] - v2->v[0]; + vd->v[1] = v1->v[1] - v2->v[1]; +} + +void vec2_add(Vector2 * vd, const Vector2 * vs) +{ + vd->v[0] += vs->v[0]; + vd->v[1] += vs->v[1]; +} + +void vec2_madd(Vector2 * vd, float s, const Vector2 * vs) +{ + vd->v[0] += s * vs->v[0]; + vd->v[1] += s * vs->v[1]; +} + +void vec2_sub(Vector2 * vd, const Vector2 * vs) +{ + vd->v[0] -= vs->v[0]; + vd->v[1] -= vs->v[1]; +} + +void vec2_scale(Vector2 * vd, float s) +{ + vd->v[0] *= s; + vd->v[1] *= s; +} + +void vec2_lincomb(Vector2 * vd, const Vector2 * vb, float x, const Vector2 * vx) +{ + vd->v[0] = vb->v[0] + x * vx->v[0]; + vd->v[1] = vb->v[1] + x * vx->v[1]; +} + +void vec2_lincomb2(Vector2 * vd, const Vector2 * vb, float x, const Vector2 * vx, float y, const Vector2 * vy) +{ + vd->v[0] = vb->v[0] + x * vx->v[0] + y * vy->v[0]; + vd->v[1] = vb->v[1] + x * vx->v[1] + y * vy->v[1]; +} + +void vec2_lerp(Vector2 * vd, const Vector2 * v1, const Vector2 * v2, float t) +{ + vd->v[0] = (1 - t) * v1->v[0] + t * v2->v[0]; + vd->v[1] = (1 - t) * v1->v[1] + t * v2->v[1]; +} + +bool vec2_is_zero(const Vector2 * v) +{ + return v->v[0] == 0 && v->v[1] == 0; +} + +float vec2_vmul(const Vector2 * v1, const Vector2 * v2) +{ + return v1->v[0] * v2->v[0] + v1->v[1] * v2->v[1]; +} + +void vec2_norm(Vector2 * v) +{ + vec2_scale(v, 1.0 / vec2_length(v)); +} + +float vec2_length(const Vector2 * v) +{ + return sqrt(vec2_qlength(v)); +} + +float vec2_qlength(const Vector2 * v) +{ + return vec2_vmul(v, v); +} + +float vec2_distance(const Vector2 * v1, const Vector2 * v2) +{ + return sqrt(vec2_qdistance(v1, v2)); +} + +float vec2_qdistance(const Vector2 * v1, const Vector2 * v2) +{ + float dx = v1->v[0] - v2->v[0], dy = v1->v[1] - v2->v[1]; + return dx * dx + dy * dy; +} + + + + +void mat2_ident(Matrix2 * md) +{ + md[0] = 1.0; + md[1] = 0.0; + md[2] = 0.0; + md[3] = 1.0; +} + +void mat2_add(Matrix2 * md, const Matrix2 * ms) +{ + for(char i=0; i<4; i++) + md->m[i] += ms->m[i]; +} + +void mat2_scale(Matrix2 * md, float s) +{ + for(char i=0; i<4; i++) + md->m[i] *= s; +} + +void mat2_mmul(Matrix2 * md, const Matrix2 * ms) +{ + float m0 = md->m[0] * ms->m[0] + md->m[2] * ms->m[1]; + float m1 = md->m[1] * ms->m[0] + md->m[3] * ms->m[1]; + + float m2 = md->m[0] * ms->m[2] + md->m[2] * ms->m[3]; + float m3 = md->m[1] * ms->m[2] + md->m[3] * ms->m[3]; + + md->m[0] = m0; md->m[1] = m1; + md->m[2] = m2; md->m[3] = m3; +} + +void mat2_rmmul(Matrix2 * md, const Matrix2 * ms) +{ + float m0 = ms->m[0] * md->m[0] + ms->m[2] * md->m[1]; + float m1 = ms->m[1] * md->m[0] + ms->m[3] * md->m[1]; + + float m2 = ms->m[0] * md->m[2] + ms->m[2] * md->m[3]; + float m3 = ms->m[1] * md->m[2] + ms->m[3] * md->m[3]; + + md->m[0] = m0; md->m[1] = m1; + md->m[2] = m2; md->m[3] = m3; +} + +void mat2_tranpose(Matrix2 * md) +{ + float t = md->m[1]; md->m[1] = md->m[2]; md->m[2] = t; +} + +void vec2_mmul(Vector2 * vd, const Matrix2 * m, const Vector2 * vs) +{ + float x = m->m[0] * vs->v[0] + m->m[2] * vs->v[1]; + float y = m->m[1] * vs->v[0] + m->m[3] * vs->v[1]; + + vd->v[0] = x; + vd->v[1] = y; +} + +void vec2_mimul(Vector2 * vd, const Matrix2 * m, const Vector2 * vs) +{ + float det = 1 / mat2_det(m); + + float x = det * ( m->m[3] * vs->v[0] - m->m[2] * vs->v[1]); + float y = det * (-m->m[1] * vs->v[0] + m->m[0] * vs->v[1]); + + vd->v[0] = x; + vd->v[1] = y; +} + +void mat2_set_rotate(Matrix2 * md, float a) +{ + float c = cos(a); + float s = sin(a); + md->m[0] = c; md->m[2] = s; + md->m[1] =-s; md->m[3] = c; + +} + +void mat2_rotate(Matrix2 * md, float a) +{ + Matrix2 mr; + mat2_set_rotate(mr, a); + mat2_rmmul(md, mr); +} + +float mat2_det(const Matrix2 * ms) +{ + return ms->m[0] * ms->m[3] - ms->m[1] * ms->m[2]; +} + +void mat2_invert(Matrix2 * md, const Matrix2 * ms) +{ + float det = 1 / mat2_det(ms); + + float m0 = det * ms->m[3]; + float m1 = -det * ms->m[1]; + float m2 = -det * ms->m[2]; + float m3 = det * ms->m[0]; + + md->m[0] = m0; md->m[1] = m1; + md->m[2] = m2; md->m[3] = m3; +} + + +inline void vec3_set(Vector3 * vd, float x, float y, float z) +{ + vd->v[0] = x; + vd->v[1] = y; + vd->v[2] = z; +} + +void vec3_sum(Vector3 * vd, const Vector3 * v1, const Vector3 * v2) +{ + for(char i=0; i<3; i++) + vd->v[i] = v1->v[i] + v2->v[i]; +} + +void vec3_diff(Vector3 * vd, const Vector3 * v1, const Vector3 * v2) +{ + for(char i=0; i<3; i++) + vd->v[i] = v1->v[i] - v2->v[i]; +} + +void vec3_add(Vector3 * vd, const Vector3 * vs) +{ + for(char i=0; i<3; i++) + vd->v[i] += vs->v[i]; +} + +void vec3_madd(Vector3 * vd, float s, const Vector3 * vs) +{ + for(char i=0; i<3; i++) + vd->v[i] += s * vs->v[i]; +} + +void vec3_sub(Vector3 * vd, const Vector3 * vs) +{ + for(char i=0; i<3; i++) + vd->v[i] -= vs->v[i]; +} + +void vec3_scale(Vector3 * vd, float s) +{ + for(char i=0; i<3; i++) + vd->v[i] *= s; +} + +void vec3_lincomb(Vector3 * vd, const Vector3 * vb, float x, const Vector3 * vx) +{ + for(char i=0; i<3; i++) + vd->v[i] = vb->v[i] + x * vx->v[i]; +} + +void vec3_lincomb2(Vector3 * vd, const Vector3 * vb, float x, const Vector3 * vx, float y, const Vector3 * vy) +{ + for(char i=0; i<3; i++) + vd->v[i] = vb->v[i] + x * vx->v[i] + y * vy->v[i]; +} + +void vec3_lerp(Vector3 * vd, const Vector3 * v1, const Vector3 * v2, float t) +{ + for(char i=0; i<3; i++) + vd->v[i] = (1 - t) * v1->v[i] + t * v2->v[i]; +} + +bool vec3_is_zero(const Vector3 * v) +{ + return v->v[0] == 0 && v->v[1] == 0 && v->v[2] == 0; +} + +float vec3_vmul(const Vector3 * v1, const Vector3 * v2) +{ + return v1->v[0] * v2->v[0] + v1->v[1] * v2->v[1] + v1->v[2] * v2->v[2]; +} + +void vec3_norm(Vector3 * v) +{ + vec3_scale(v, 1.0 / vec3_length(v)); +} + +float vec3_length(const Vector3 * v) +{ + return sqrt(vec3_qlength(v)); +} + +float vec3_qlength(const Vector3 * v) +{ + return vec3_vmul(v, v); +} + +float vec3_distance(const Vector3 * v1, const Vector3 * v2) +{ + return sqrt(vec3_qdistance(v1, v2)); +} + +float vec3_qdistance(const Vector3 * v1, const Vector3 * v2) +{ + float dx = v1->v[0] - v2->v[0], dy = v1->v[1] - v2->v[1], dz = v1->v[2] - v2->v[2]; + return dx * dx + dy * dy + dz * dz; +} + +void vec3_mcadd(Vector3 * vd, const Vector3 * v1, const Vector3 * v2) +{ + for(char i=0; i<3; i++) + vd->v[i] += v1->v[i] * v2->v[i]; +} + +void vec3_mscadd(Vector3 * vd, float s, const Vector3 * v1, const Vector3 * v2) +{ + for(char i=0; i<3; i++) + vd->v[i] += s * v1->v[i] * v2->v[i]; +} + +void vec3_cmul(Vector3 * vd, const Vector3 * v1, const Vector3 * v2) +{ + for(char i=0; i<3; i++) + vd->v[i] = v1->v[i] * v2->v[i]; +} + +void vec3_xmul(Vector3 * vd, const Vector3 * v1, const Vector3 * v2) +{ + float tx = v1->v[1] * v2->v[2] - v1->v[2] * v2->v[1]; + float ty = v1->v[2] * v2->v[0] - v1->v[0] * v2->v[2]; + float tz = v1->v[0] * v2->v[1] - v1->v[1] * v2->v[0]; + + vd->v[0] = tx; + vd->v[1] = ty; + vd->v[2] = tz; +} + +void vec3_mbase(Vector3 * v1, Vector3 * v2, Vector3 * v3) +{ + if (fabs(v1->v[2]) >= fabs(v1->v[0]) && fabs(v1->v[2]) >= fabs(v1->v[1])) + { + v2->v[0] = v1->v[2]; + v2->v[1] = 0; + v2->v[2] = - v1->v[0]; + } + else if (fabs(v1->v[1]) >= fabs(v1->v[0]) && fabs(v1->v[1]) >= fabs(v1->v[2])) + { + v2->v[0] = v1->v[1]; + v2->v[1] = -v1->v[0]; + v2->v[2] = 0; + } + else + { + v2->v[0] = -v1->v[1]; + v2->v[1] = v1->v[0]; + v2->v[2] = 0; + } + vec3_norm(v2); + vec3_xmul(v3, v1, v2); +} + +void vec3_bend(Vector3 * vd, const Vector3 * vs, float chi1, float chi2) +{ + float m0 = sqrt(1 - chi2); + + float hn1x, hn1y, hn1z; + float hn2x, hn2y, hn2z; + float hnl; + + if (fabs(vs->v[2]) >= fabs(vs->v[0]) && fabs(vs->v[2]) >= fabs(vs->v[1])) + { + hn1x = vs->v[1]; hn1y = 0; hn1z = - vs->v[0]; + chi2 /= (hn1x * hn1x + hn1z * hn1z); + hn2x = vs->v[1] * hn1z; + hn2y = vs->v[2] * hn1x - vs->v[0] * hn1z; + hn2z = - vs->v[1] * hn1x; + } + else + { + hn1x = - vs->v[1]; hn1y = vs->v[0]; hn1z = 0; + chi2 /= (hn1x * hn1x + hn1y * hn1y ); + hn2x = - vs->v[2] * hn1y; + hn2y = vs->v[2] * hn1x; + hn2z = vs->v[0] * hn1y - vs->v[1] * hn1x; + } + + float m1 = cos(2 * PI * chi1) * sqrt(chi2); + float m2 = sin(2 * PI * chi1) * sqrt(chi2); + + Vector3 vt; + vt.v[0] = vs->v[0] * m0 + hn1x * m1 + hn2x * m2; + vt.v[1] = vs->v[1] * m0 + hn1y * m1 + hn2y * m2; + vt.v[2] = vs->v[2] * m0 + hn1z * m1 + hn2z * m2; + *vd = vt; +} + + + +void mat3_ident(Matrix3 * m) +{ + m->m[0] = 1.0; + m->m[1] = 0.0; + m->m[2] = 0.0; + + m->m[3] = 0.0; + m->m[4] = 1.0; + m->m[5] = 0.0; + + m->m[6] = 0.0; + m->m[7] = 0.0; + m->m[8] = 1.0; +} + +void mat3_scale(Matrix3 * md, float s) +{ + for(char i=0; i<9; i++) + md->m[i] *= s; +} + +void mat3_add(Matrix3 * md, const Matrix3 * ms) +{ + for(char i=0; i<9; i++) + md->m[i] += md->m[i]; +} + +void mat3_mmul(Matrix3 * md, const Matrix3 * ms) +{ + for(char i=0; i<3; i++) + { + char j = 3 * i; + + float m0 = md->m[i + 0] * ms->m[0] + md->m[i + 3] * ms->m[1] + md->m[i + 6] * ms->m[2]; + float m3 = md->m[i + 0] * ms->m[3] + md->m[i + 3] * ms->m[4] + md->m[i + 6] * ms->m[5]; + float m6 = md->m[i + 0] * ms->m[6] + md->m[i + 3] * ms->m[7] + md->m[i + 6] * ms->m[8]; + + md->m[i + 0] = m0; md->m[i + 3] = m3; md->m[i + 6] = m6; + } +} + +void mat3_rmmul(Matrix3 * md, const Matrix3 * ms) +{ + for(char i=0; i<9; i+=3) + { + float m0 = md->m[i + 0] * ms->m[0] + md->m[i + 1] * ms->m[3] + md->m[i + 2] * ms->m[6]; + float m1 = md->m[i + 0] * ms->m[1] + md->m[i + 1] * ms->m[4] + md->m[i + 2] * ms->m[7]; + float m2 = md->m[i + 0] * ms->m[2] + md->m[i + 1] * ms->m[5] + md->m[i + 2] * ms->m[8]; + + md->m[i + 0] = m0; md->m[i + 1] = m1; md->m[i + 2] = m2; + } +} + +void mat3_transpose(Matrix3 * md, const Matrix3 * ms) +{ + float t; + t = ms->m[1]; md->m[1] = ms->m[3]; md->m[3] = t; md->m[0] = ms->m[0]; + t = ms->m[2]; md->m[2] = ms->m[6]; md->m[6] = t; md->m[4] = ms->m[4]; + t = ms->m[5]; md->m[5] = ms->m[7]; md->m[7] = t; md->m[8] = ms->m[8]; +} + +float mat3_det(const Matrix3 * ms) +{ + return ms->m[0] * ms->m[4] * ms->m[8] + + ms->m[1] * ms->m[5] * ms->m[6] + + ms->m[2] * ms->m[3] * ms->m[7] - + ms->m[0] * ms->m[5] * ms->m[7] - + ms->m[2] * ms->m[4] * ms->m[6] - + ms->m[1] * ms->m[3] * ms->m[8]; +} + +void mat3_invert(Matrix3 * md, const Matrix3 * ms) +{ + float det = 1 / mat3_det(ms); + + Matrix3 mt; + mt.m[0] = det * (ms->m[4] * ms->m[8] - ms->m[5] * ms->m[7]); + mt.m[1] = det * (ms->m[7] * ms->m[2] - ms->m[1] * ms->m[8]); + mt.m[2] = det * (ms->m[1] * ms->m[5] - ms->m[4] * ms->m[2]); + + mt.m[3] = det * (ms->m[6] * ms->m[5] - ms->m[3] * ms->m[8]); + mt.m[4] = det * (ms->m[0] * ms->m[8] - ms->m[6] * ms->m[2]); + mt.m[5] = det * (ms->m[3] * ms->m[2] - ms->m[0] * ms->m[5]); + + mt.m[6] = det * (ms->m[3] * ms->m[7] - ms->m[6] * ms->m[4]); + mt.m[7] = det * (ms->m[6] * ms->m[1] - ms->m[0] * ms->m[7]); + mt.m[8] = det * (ms->m[0] * ms->m[4] - ms->m[3] * ms->m[1]); + + *md = mt; +} + +void vec3_mmul(Vector3 * vd, const Matrix3 * m, const Vector3 * vs) +{ + Vector3 vt; + for(char i=0; i<3; i++) + vt.v[i] = m->m[i] * vs->v[0] + m->m[3 + i] * vs->v[1] + m->m[6 + i] * vs->v[2]; + *vd = vt; +} + +void mat3_set_rotate_x(Matrix3 * m, float a) +{ + float c = cos(a); + float s = sin(a); + m->m[0] = 1; m->m[3] = 0; m->m[6] = 0; + m->m[1] = 0; m->m[4] = c; m->m[7] = s; + m->m[2] = 0; m->m[5] =-s; m->m[8] = c; +} + +void mat3_set_rotate_y(Matrix3 * m, float a) +{ + float c = cos(a); + float s = sin(a); + m->m[0] = c; m->m[3] = 0; m->m[6] = s; + m->m[1] = 0; m->m[4] = 1; m->m[7] = 0; + m->m[2] =-s; m->m[5] = 0; m->m[8] = c; +} + + +void mat3_set_rotate_z(Matrix3 * m, float a) +{ + float c = cos(a); + float s = sin(a); + m->m[0] = c; m->m[3] =-s; m->m[6] = 0; + m->m[1] = s; m->m[4] = c; m->m[7] = 0; + m->m[2] = 0; m->m[5] = 0; m->m[8] = 1; +} + +void mat3_set_rotate(Matrix3 * m, const Vector3 * v, float a) +{ + float co = cos(a); + float si = sin(a); + float ico = (1- co); + + m->m[0] = co + v->v[0]*v->v[0]*ico; + m->m[1] = v->v[1]*v->v[0]*ico+v->v[2]*si; + m->m[2] = v->v[2]*v->v[0]*ico-v->v[1]*si; + + m->m[3] = v->v[0]*v->v[1]*ico - v->v[2]*si; + m->m[4] = co + v->v[1]*v->v[1]*ico; + m->m[5] = v->v[2]*v->v[1]*ico+v->v[0]*si; + + m->m[6] = v->v[0]*v->v[2]*ico + v->v[1]*si; + m->m[7] = v->v[1]*v->v[2]*ico - v->v[0]*si; + m->m[8] = co + v->v[2]*v->v[2]*ico; +} + + + + + +inline void vec4_set(Vector4 * vd, float x, float y, float z, float w) +{ + vd->v[0] = x; + vd->v[1] = y; + vd->v[2] = z; + vd->v[3] = w; +} + + +void vec4_sum(Vector4 * vd, const Vector4 * v1, const Vector4 * v2) +{ + for(char i=0; i<4; i++) + vd->v[i] = v1->v[i] + v2->v[i]; +} + +void vec4_diff(Vector4 * vd, const Vector4 * v1, const Vector4 * v2) +{ + for(char i=0; i<4; i++) + vd->v[i] = v1->v[i] - v2->v[i]; +} + +void vec4_add(Vector4 * vd, const Vector4 * vs) +{ + for(char i=0; i<4; i++) + vd->v[i] += vs->v[i]; +} + +void vec4_madd(Vector4 * vd, float s, const Vector4 * vs) +{ + for(char i=0; i<4; i++) + vd->v[i] += s * vs->v[i]; +} + +void vec4_sub(Vector4 * vd, const Vector4 * vs) +{ + for(char i=0; i<4; i++) + vd->v[i] -= vs->v[i]; +} + +void vec4_scale(Vector4 * vd, float s) +{ + for(char i=0; i<4; i++) + vd->v[i] *= s; +} + +void vec4_lincomb(Vector4 * vd, const Vector4 * vb, float x, const Vector4 * vx) +{ + for(char i=0; i<4; i++) + vd->v[i] = vb->v[i] + x * vx->v[i]; +} + +void vec4_lincomb2(Vector4 * vd, const Vector4 * vb, float x, const Vector4 * vx, float y, const Vector4 * vy) +{ + for(char i=0; i<4; i++) + vd->v[i] = vb->v[i] + x * vx->v[i] + y * vy->v[i]; +} + +void vec4_lerp(Vector4 * vd, const Vector4 * v1, const Vector4 * v2, float t) +{ + for(char i=0; i<4; i++) + vd->v[i] = (1 - t) * v1->v[i] + t * v2->v[i]; +} + +bool vec4_is_zero(const Vector4 * v) +{ + return v->v[0] == 0 && v->v[1] == 0 && v->v[2] == 0 && v->v[3] == 0; +} + +float vec4_vmul(const Vector4 * v1, const Vector4 * v2) +{ + return v1->v[0] * v2->v[0] + v1->v[1] * v2->v[1] + v1->v[2] * v2->v[2] + v1->v[3] * v2->v[3]; +} + +void vec4_norm(Vector4 * v) +{ + vec4_scale(v, 1.0 / vec4_length(v)); +} + +float vec4_length(const Vector4 * v) +{ + return sqrt(vec4_qlength(v)); +} + +float vec4_qlength(const Vector4 * v) +{ + return vec4_vmul(v, v); +} + +float vec4_distance(const Vector4 * v1, const Vector4 * v2) +{ + return sqrt(vec4_qdistance(v1, v2)); +} + +float vec4_qdistance(const Vector4 * v1, const Vector4 * v2) +{ + float dx = v1->v[0] - v2->v[0], dy = v1->v[1] - v2->v[1], dz = v1->v[2] - v2->v[2], dw = v1->v[3] - v2->v[3]; + return dx * dx + dy * dy + dz * dz + dw * dw; +} + +void vec4_mcadd(Vector4 * vd, const Vector4 * v1, const Vector4 * v2) +{ + for(char i=0; i<4; i++) + vd->v[i] += v1->v[i] * v2->v[i]; +} + +void vec4_mscadd(Vector4 * vd, float s, const Vector4 * v1, const Vector4 * v2) +{ + for(char i=0; i<4; i++) + vd->v[i] += s * v1->v[i] * v2->v[i]; +} + +void vec4_cmul(Vector4 * vd, const Vector4 * v1, const Vector4 * v2) +{ + for(char i=0; i<4; i++) + vd->v[i] = v1->v[i] * v2->v[i]; +} + + + +void mat4_ident(Matrix4 * m) +{ + for(char i=0; i<4; i++) + for(char j=0; j<4; j++) + m->m[4 * i + j] = (i == j) ? 1 : 0; +} + +void mat4_from_vec4(Matrix4 * m, const Vector4 * vx, const Vector4 * vy, const Vector4 * vz, const Vector4 * vw) +{ + m->m[ 0] = vx->v[0]; + m->m[ 1] = vx->v[1]; + m->m[ 2] = vx->v[2]; + m->m[ 3] = vx->v[3]; + + m->m[ 4] = vy->v[0]; + m->m[ 5] = vy->v[1]; + m->m[ 6] = vy->v[2]; + m->m[ 7] = vy->v[3]; + + m->m[ 8] = vz->v[0]; + m->m[ 9] = vz->v[1]; + m->m[10] = vz->v[2]; + m->m[11] = vz->v[3]; + + m->m[12] = vw->v[0]; + m->m[13] = vw->v[1]; + m->m[14] = vw->v[2]; + m->m[15] = vw->v[3]; +} + +void mat4_from_vec3(Matrix4 * m, const Vector3 * vx, const Vector3 * vy, const Vector3 * vz, const Vector3 * vw) +{ + m->m[ 0] = vx->v[0]; + m->m[ 1] = vx->v[1]; + m->m[ 2] = vx->v[2]; + m->m[ 3] = 0; + + m->m[ 4] = vy->v[0]; + m->m[ 5] = vy->v[1]; + m->m[ 6] = vy->v[2]; + m->m[ 7] = 0; + + m->m[ 8] = vz->v[0]; + m->m[ 9] = vz->v[1]; + m->m[10] = vz->v[2]; + m->m[11] = 0; + + if (vw) + { + m->m[12] = vw->v[0]; + m->m[13] = vw->v[1]; + m->m[14] = vw->v[2]; + m->m[15] = 1; + } + else + { + m->m[12] = 0; + m->m[13] = 0; + m->m[14] = 0; + m->m[15] = 0; + } +} + +void mat4_make_perspective(Matrix4 * m, float fieldOfViewInRadians, float aspect, float near, float far) +{ + float f = tan(PI * 0.5 - 0.5 * fieldOfViewInRadians); + float rangeInv = 1.0 / (far - near); + + m->m[ 0] = f / aspect; + m->m[ 1] = 0; + m->m[ 2] = 0; + m->m[ 3] = 0; + + m->m[ 4] = 0; + m->m[ 5] = f; + m->m[ 6] = 0; + m->m[ 7] = 0; + + m->m[ 8] = 0; + m->m[ 9] = 0; + m->m[10] = (near + far) * rangeInv; + m->m[11] = 1; + + m->m[12] = 0; + m->m[13] = 0; + m->m[14] = near * far * rangeInv * -2; + m->m[15] = 0; +} + +void mat4_make_view(Matrix4 * m, const Vector3 * pos, const Vector3 * target, const Vector3 * up) +{ + Vector3 vx, vy, vz; + + vec3_diff(&vz, target, pos); vec3_norm(&vz); + vec3_xmul(&vx, up, &vz); + vec3_xmul(&vy, &vz, &vx); + + mat4_from_vec3(m, &vx, &vy, &vz, pos); + mat4_invert(m); +} + + +void mat4_scale(Matrix4 * md, float s) +{ + for(char i=0; i<16; i++) + md->m[i] *= s; +} + +void mat4_add(Matrix4 * md, const Matrix4 * ms) +{ + for(char i=0; i<16; i++) + md->m[i] += md->m[i]; +} + +void mat4_mmul(Matrix4 * md, const Matrix4 * ms) +{ + float ma[4]; + for(char i=0; i<4; i++) + { + for(char j=0; j<4; j++) + ma[j] = md->m[i + 0] * ms->m[4 * j] + md->m[i + 4] * ms->m[4 * j + 1] + md->m[i + 8] * ms->m[4 * j + 2] + md->m[i + 12] * ms->m[4 * j + 3]; + for(char j=0; j<4; j++) + md->m[i + 4 * j] = ma[j]; + } +} + +void mat4_rmmul(Matrix4 * md, const Matrix4 * ms) +{ + float ma[4]; + for(char i=0; i<16; i+=4) + { + for(char j=0; j<4; j++) + ma[j] = md->m[i + 0] * ms->m[j] + md->m[i + 1] * ms->m[j + 4] + md->m[i + 2] * ms->m[j + 8] + md->m[i + 3] * ms->m[j + 12]; + + for(char j=0; j<4; j++) + md->m[i + j] = ma[j]; + } +} + +float mat4_det(const Matrix4 * m) +{ + return m->m[ 0] * m->m[ 5] * m->m[10] * m->m[15] + m->m[ 0] * m->m[ 9] * m->m[14] * m->m[ 7] + m->m[ 0] * m->m[13] * m->m[ 6] * m->m[11] + + m->m[ 4] * m->m[ 1] * m->m[14] * m->m[11] + m->m[ 4] * m->m[ 9] * m->m[ 2] * m->m[15] + m->m[ 4] * m->m[13] * m->m[10] * m->m[ 3] + + m->m[ 8] * m->m[ 1] * m->m[ 6] * m->m[15] + m->m[ 8] * m->m[ 5] * m->m[14] * m->m[ 3] + m->m[ 8] * m->m[13] * m->m[ 2] * m->m[ 7] + + m->m[12] * m->m[ 1] * m->m[10] * m->m[ 7] + m->m[12] * m->m[ 5] * m->m[ 2] * m->m[11] + m->m[12] * m->m[ 9] * m->m[ 6] * m->m[ 3] - + m->m[ 0] * m->m[ 5] * m->m[14] * m->m[11] - m->m[ 0] * m->m[ 9] * m->m[ 6] * m->m[15] - m->m[ 0] * m->m[13] * m->m[10] * m->m[ 7] - + m->m[ 4] * m->m[ 1] * m->m[10] * m->m[15] - m->m[ 4] * m->m[ 9] * m->m[14] * m->m[ 3] - m->m[ 4] * m->m[13] * m->m[ 2] * m->m[11] - + m->m[ 8] * m->m[ 1] * m->m[14] * m->m[ 7] - m->m[ 8] * m->m[ 5] * m->m[ 2] * m->m[15] - m->m[ 8] * m->m[13] * m->m[ 6] * m->m[ 3] - + m->m[12] * m->m[ 1] * m->m[ 6] * m->m[11] - m->m[12] * m->m[ 5] * m->m[10] * m->m[ 3] - m->m[12] * m->m[ 9] * m->m[ 2] * m->m[ 7]; +} + +void mat4_invert(Matrix4 * md, const Matrix4 * ms) +{ + float det = 1 / mat4_det(ms); + + Matrix4 mt; + + mt.m[ 0] = det * (ms->m[ 5] * ms->m[10] * ms->m[15]+ms->m[ 9] * ms->m[14] * ms->m[ 7]+ms->m[13] * ms->m[ 6] * ms->m[11]-ms->m[ 5] * ms->m[14] * ms->m[11]-ms->m[ 9] * ms->m[ 6] * ms->m[15]-ms->m[13] * ms->m[10] * ms->m[ 7]); + mt.m[ 4] = det * (ms->m[ 4] * ms->m[14] * ms->m[11]+ms->m[ 8] * ms->m[ 6] * ms->m[15]+ms->m[12] * ms->m[10] * ms->m[ 7]-ms->m[ 4] * ms->m[10] * ms->m[15]-ms->m[ 8] * ms->m[14] * ms->m[ 7]-ms->m[12] * ms->m[ 6] * ms->m[11]); + mt.m[ 8] = det * (ms->m[ 4] * ms->m[ 9] * ms->m[15]+ms->m[ 8] * ms->m[13] * ms->m[ 7]+ms->m[12] * ms->m[ 5] * ms->m[11]-ms->m[ 4] * ms->m[13] * ms->m[11]-ms->m[ 8] * ms->m[ 5] * ms->m[15]-ms->m[12] * ms->m[ 9] * ms->m[ 7]); + mt.m[12] = det * (ms->m[ 4] * ms->m[13] * ms->m[10]+ms->m[ 8] * ms->m[ 5] * ms->m[14]+ms->m[12] * ms->m[ 9] * ms->m[ 6]-ms->m[ 4] * ms->m[ 9] * ms->m[14]-ms->m[ 8] * ms->m[13] * ms->m[ 6]-ms->m[12] * ms->m[ 5] * ms->m[10]); + + mt.m[ 1] = det * (ms->m[ 1] * ms->m[14] * ms->m[11]+ms->m[ 9] * ms->m[ 2] * ms->m[15]+ms->m[13] * ms->m[10] * ms->m[ 3]-ms->m[ 1] * ms->m[10] * ms->m[15]-ms->m[ 9] * ms->m[14] * ms->m[ 3]-ms->m[13] * ms->m[ 2] * ms->m[11]); + mt.m[ 5] = det * (ms->m[ 0] * ms->m[10] * ms->m[15]+ms->m[ 8] * ms->m[14] * ms->m[ 3]+ms->m[12] * ms->m[ 2] * ms->m[11]-ms->m[ 0] * ms->m[14] * ms->m[11]-ms->m[ 8] * ms->m[ 2] * ms->m[15]-ms->m[12] * ms->m[10] * ms->m[ 3]); + mt.m[ 9] = det * (ms->m[ 0] * ms->m[13] * ms->m[11]+ms->m[ 8] * ms->m[ 1] * ms->m[15]+ms->m[12] * ms->m[ 9] * ms->m[ 3]-ms->m[ 0] * ms->m[ 9] * ms->m[15]-ms->m[ 8] * ms->m[13] * ms->m[ 3]-ms->m[12] * ms->m[ 1] * ms->m[11]); + mt.m[13] = det * (ms->m[ 0] * ms->m[ 9] * ms->m[14]+ms->m[ 8] * ms->m[13] * ms->m[ 2]+ms->m[12] * ms->m[ 1] * ms->m[10]-ms->m[ 0] * ms->m[13] * ms->m[10]-ms->m[ 8] * ms->m[ 1] * ms->m[14]-ms->m[12] * ms->m[ 9] * ms->m[ 2]); + + mt.m[ 2] = det * (ms->m[ 1] * ms->m[ 6] * ms->m[15]+ms->m[ 5] * ms->m[14] * ms->m[ 3]+ms->m[13] * ms->m[ 2] * ms->m[ 7]-ms->m[ 1] * ms->m[14] * ms->m[ 7]-ms->m[ 5] * ms->m[ 2] * ms->m[15]-ms->m[13] * ms->m[ 6] * ms->m[ 3]); + mt.m[ 6] = det * (ms->m[ 0] * ms->m[14] * ms->m[ 7]+ms->m[ 4] * ms->m[ 2] * ms->m[15]+ms->m[12] * ms->m[ 6] * ms->m[ 3]-ms->m[ 0] * ms->m[ 6] * ms->m[15]-ms->m[ 4] * ms->m[14] * ms->m[ 3]-ms->m[12] * ms->m[ 2] * ms->m[ 7]); + mt.m[10] = det * (ms->m[ 0] * ms->m[ 5] * ms->m[15]+ms->m[ 4] * ms->m[13] * ms->m[ 3]+ms->m[12] * ms->m[ 1] * ms->m[ 7]-ms->m[ 0] * ms->m[13] * ms->m[ 7]-ms->m[ 4] * ms->m[ 1] * ms->m[15]-ms->m[12] * ms->m[ 5] * ms->m[ 3]); + mt.m[14] = det * (ms->m[ 0] * ms->m[13] * ms->m[ 6]+ms->m[ 4] * ms->m[ 1] * ms->m[14]+ms->m[12] * ms->m[ 5] * ms->m[ 2]-ms->m[ 0] * ms->m[ 5] * ms->m[14]-ms->m[ 4] * ms->m[13] * ms->m[ 2]-ms->m[12] * ms->m[ 1] * ms->m[ 6]); + + mt.m[ 3] = det * (ms->m[ 1] * ms->m[10] * ms->m[ 7]+ms->m[ 5] * ms->m[ 2] * ms->m[11]+ms->m[ 9] * ms->m[ 6] * ms->m[ 3]-ms->m[ 1] * ms->m[ 6] * ms->m[11]-ms->m[ 5] * ms->m[10] * ms->m[ 3]-ms->m[ 9] * ms->m[ 2] * ms->m[ 7]); + mt.m[ 7] = det * (ms->m[ 0] * ms->m[ 6] * ms->m[11]+ms->m[ 4] * ms->m[10] * ms->m[ 3]+ms->m[ 8] * ms->m[ 2] * ms->m[ 7]-ms->m[ 0] * ms->m[10] * ms->m[ 7]-ms->m[ 4] * ms->m[ 2] * ms->m[11]-ms->m[ 8] * ms->m[ 6] * ms->m[ 3]); + mt.m[11] = det * (ms->m[ 0] * ms->m[ 9] * ms->m[ 7]+ms->m[ 4] * ms->m[ 1] * ms->m[11]+ms->m[ 8] * ms->m[ 5] * ms->m[ 3]-ms->m[ 0] * ms->m[ 5] * ms->m[11]-ms->m[ 4] * ms->m[ 9] * ms->m[ 3]-ms->m[ 8] * ms->m[ 1] * ms->m[ 7]); + mt.m[15] = det * (ms->m[ 0] * ms->m[ 5] * ms->m[10]+ms->m[ 4] * ms->m[ 9] * ms->m[ 2]+ms->m[ 8] * ms->m[ 1] * ms->m[ 6]-ms->m[ 0] * ms->m[ 9] * ms->m[ 6]-ms->m[ 4] * ms->m[ 1] * ms->m[10]-ms->m[ 8] * ms->m[ 5] * ms->m[ 2]); + + *md = mt; +} + + +void vec4_mmul(Vector4 * vd, const Matrix4 * m, const Vector4 * vs) +{ + Vector4 vt; + for(char i=0; i<4; i++) + vt.v[i] = m->m[0 + i] * vs->v[0] + m->m[4 + i] * vs->v[1] + m->m[8 + i] * vs->v[2] + m->m[12 + i] * vs->v[3]; + *vd = vt; +} + +void vec3_mmulp(Vector3 * vd, const Matrix4 * m, const Vector3 * vs) +{ + Vector3 vt; + for(char i=0; i<3; i++) + vt.v[i] = m->m[0 + i] * vs->v[0] + m->m[4 + i] * vs->v[1] + m->m[8 + i] * vs->v[2] + m->m[12 + i]; + *vd = vt; +} + +void vec3_mmuld(Vector3 * vd, const Matrix4 * m, const Vector3 * vs) +{ + Vector3 vt; + for(char i=0; i<3; i++) + vt.v[i] = m->m[0 + i] * vs->v[0] + m->m[4 + i] * vs->v[1] + m->m[8 + i] * vs->v[2]; + *vd = vt; +} + + +void mat4_set_rotate_x(Matrix4 * m, float a) +{ + float c = cos(a); + float s = sin(a); + m->m[ 0] = 1; m->m[ 4] = 0; m->m[ 8] = 0; m->m[12] = 0; + m->m[ 1] = 0; m->m[ 5] = c; m->m[ 9] = s; m->m[13] = 0; + m->m[ 2] = 0; m->m[ 6] =-s; m->m[10] = c; m->m[14] = 0; + m->m[ 3] = 0; m->m[ 7] = 0; m->m[11] = 0; m->m[15] = 1; +} + +void mat4_set_rotate_y(Matrix4 * m, float a) +{ + float c = cos(a); + float s = sin(a); + m->m[ 0] = c; m->m[ 4] = 0; m->m[ 8] = s; m->m[12] = 0; + m->m[ 1] = 0; m->m[ 5] = 1; m->m[ 9] = 0; m->m[13] = 0; + m->m[ 2] =-s; m->m[ 6] = 0; m->m[10] = c; m->m[14] = 0; + m->m[ 3] = 0; m->m[ 7] = 0; m->m[11] = 0; m->m[15] = 1; +} + + +void mat4_set_rotate_z(Matrix4 * m, float a) +{ + float c = cos(a); + float s = sin(a); + m->m[ 0] = c; m->m[ 4] =-s; m->m[ 8] = 0; m->m[12] = 0; + m->m[ 1] = s; m->m[ 5] = c; m->m[ 9] = 0; m->m[13] = 0; + m->m[ 2] = 0; m->m[ 6] = 0; m->m[10] = 1; m->m[14] = 0; + m->m[ 3] = 0; m->m[ 7] = 0; m->m[11] = 0; m->m[15] = 1; +} + +void mat4_set_rotate(Matrix4 * m, const Vector3 * v, float a) +{ + float co = cos(a); + float si = sin(a); + float ico = (1- co); + + m->m[ 0] = co + v->v[0]*v->v[0]*ico; + m->m[ 1] = v->v[1]*v->v[0]*ico+v->v[2]*si; + m->m[ 2] = v->v[2]*v->v[0]*ico-v->v[1]*si; + m->m[ 3] = 0; + + m->m[ 4] = v->v[0]*v->v[1]*ico - v->v[2]*si; + m->m[ 5] = co + v->v[1]*v->v[1]*ico; + m->m[ 6] = v->v[2]*v->v[1]*ico+v->v[0]*si; + m->m[ 7] = 0; + + m->m[ 8] = v->v[0]*v->v[2]*ico + v->v[1]*si; + m->m[ 9] = v->v[1]*v->v[2]*ico - v->v[0]*si; + m->m[10] = co + v->v[2]*v->v[2]*ico; + m->m[11] = 0; + + m->m[12] = 0; + m->m[13] = 0; + m->m[14] = 0; + m->m[15] = 1; +} + + +void mat4_set_translate(Matrix4 * m, const Vector3 * v) +{ + mat4_ident(m); + m->m[12] = v->v[0]; + m->m[13] = v->v[1]; + m->m[14] = v->v[2]; +} + +void mat4_set_scale(Matrix4 * m, float s) +{ + mat4_ident(m); + m->m[ 0] = s; + m->m[ 5] = s; + m->m[10] = s; +} + + +void vec3_project(Vector3 * vd, const Matrix4 * m, const Vector3 * vs) +{ + vec3_mmulp(vd, m, vs); + vd->v[0] /= vd->v[2]; + vd->v[1] /= vd->v[2]; +} + + + +void f12mat3_ident(F12Matrix3 * m) +{ + m->m[0] = FIX12_ONE; + m->m[1] = 0; + m->m[2] = 0; + + m->m[3] = 0; + m->m[4] = FIX12_ONE; + m->m[5] = 0; + + m->m[6] = 0; + m->m[7] = 0; + m->m[8] = FIX12_ONE; +} + +void f12mat3_mmul(F12Matrix3 * md, const F12Matrix3 * ms) +{ + for(char i=0; i<3; i++) + { + char j = 3 * i; + + int m0 = lmul4f12s(md->m[i + 0], ms->m[0]) + lmul4f12s(md->m[i + 3], ms->m[1]) + lmul4f12s(md->m[i + 6], ms->m[2]); + int m3 = lmul4f12s(md->m[i + 0], ms->m[3]) + lmul4f12s(md->m[i + 3], ms->m[4]) + lmul4f12s(md->m[i + 6], ms->m[5]); + int m6 = lmul4f12s(md->m[i + 0], ms->m[6]) + lmul4f12s(md->m[i + 3], ms->m[7]) + lmul4f12s(md->m[i + 6], ms->m[8]); + + md->m[i + 0] = m0; md->m[i + 3] = m3; md->m[i + 6] = m6; + } +} + +void f12mat3_rmmul(F12Matrix3 * md, const F12Matrix3 * ms) +{ + for(char i=0; i<9; i+=3) + { + int m0 = lmul4f12s(md->m[i + 0], ms->m[0]) + lmul4f12s(md->m[i + 1], ms->m[3]) + lmul4f12s(md->m[i + 2], ms->m[6]); + int m1 = lmul4f12s(md->m[i + 0], ms->m[1]) + lmul4f12s(md->m[i + 1], ms->m[4]) + lmul4f12s(md->m[i + 2], ms->m[7]); + int m2 = lmul4f12s(md->m[i + 0], ms->m[2]) + lmul4f12s(md->m[i + 1], ms->m[5]) + lmul4f12s(md->m[i + 2], ms->m[8]); + + md->m[i + 0] = m0; md->m[i + 1] = m1; md->m[i + 2] = m2; + } +} + +void f12mat3_set_rotate_x(F12Matrix3 * m, float a) +{ + int c = (int)(FIX12_ONE * cos(a) + 0.5); + int s = (int)(FIX12_ONE * sin(a) + 0.5); + m->m[0] = FIX12_ONE; m->m[3] = 0; m->m[6] = 0; + m->m[1] = 0; m->m[4] = c; m->m[7] = s; + m->m[2] = 0; m->m[5] =-s; m->m[8] = c; +} + +void f12mat3_set_rotate_y(F12Matrix3 * m, float a) +{ + int c = (int)(FIX12_ONE * cos(a) + 0.5); + int s = (int)(FIX12_ONE * sin(a) + 0.5); + m->m[0] = c; m->m[3] = 0; m->m[6] = s; + m->m[1] = 0; m->m[4] = FIX12_ONE; m->m[7] = 0; + m->m[2] =-s; m->m[5] = 0; m->m[8] = c; +} + + +void f12mat3_set_rotate_z(F12Matrix3 * m, float a) +{ + int c = (int)(FIX12_ONE * cos(a) + 0.5); + int s = (int)(FIX12_ONE * sin(a) + 0.5); + m->m[0] = c; m->m[3] =-s; m->m[6] = 0; + m->m[1] = s; m->m[4] = c; m->m[7] = 0; + m->m[2] = 0; m->m[5] = 0; m->m[8] = FIX12_ONE; +} + +void f12vec3_mmul(F12Vector3 * vd, const F12Matrix3 * m, const F12Vector3 * vs) +{ + F12Vector3 vt; + for(char i=0; i<3; i++) + vt.v[i] = lmul4f12s(m->m[i], vs->v[0]) + lmul4f12s(m->m[3 + i], vs->v[1]) + lmul4f12s(m->m[6 + i], vs->v[2]); + *vd = vt; +} + + diff --git a/oscar64/include/gfx/vector3d.h b/oscar64/include/gfx/vector3d.h new file mode 100644 index 0000000..b31acbc --- /dev/null +++ b/oscar64/include/gfx/vector3d.h @@ -0,0 +1,294 @@ +#ifndef VECTOR3D_H +#define VECTOR3D_H + +struct Vector2 +{ + float v[2]; +}; + +inline void vec2_set(Vector2 * vd, float x, float y); + +void vec2_sum(Vector2 * vd, const Vector2 * v1, const Vector2 * v2); + +void vec2_diff(Vector2 * vd, const Vector2 * v1, const Vector2 * v2); + +void vec2_add(Vector2 * vd, const Vector2 * vs); + +void vec2_sub(Vector2 * vd, const Vector2 * vs); + +void vec2_madd(Vector2 * vd, float s, const Vector2 * vs); + +void vec2_scale(Vector2 * vd, float s); + +void vec2_lincomb(Vector2 * vd, const Vector2 * vb, float x, const Vector2 * vx); + +void vec2_lincomb2(Vector2 * vd, const Vector2 * vb, float x, const Vector2 * vx, float y, const Vector2 * vy); + +void vec2_lerp(Vector2 * vd, const Vector2 * v1, const Vector2 * v2, float t); + +bool vec2_is_zero(const Vector2 * v); + +float vec2_vmul(const Vector2 * v1, const Vector2 * v2); + +void vec2_norm(Vector2 * v); + +float vec2_length(const Vector2 * v); + +float vec2_qlength(const Vector2 * v); + +float vec2_distance(const Vector2 * v1, const Vector2 * v2); + +float vec2_qdistance(const Vector2 * v1, const Vector2 * v2); + + +struct Matrix2 +{ + float m[4]; +}; + +void mat2_ident(Matrix2 * md); + +void mat2_add(Matrix2 * md, const Matrix2 * ms); + +void mat2_scale(Matrix2 * md, float s); + +void mat2_mmul(Matrix2 * md, const Matrix2 * ms); + +void mat2_rmmul(Matrix2 * md, const Matrix2 * ms); + +void mat2_tranpose(Matrix2 * md); + +void vec2_mmul(Vector2 * vd, const Matrix2 * m, const Vector2 * vs); + +void vec2_mimul(Vector2 * vd, const Matrix2 * m, const Vector2 * vs); + +void mat2_set_rotate(Matrix2 * md, float a); + +void mat2_rotate(Matrix2 * md, float a); + +float mat2_det(const Matrix2 * ms); + +void mat2_invert(Matrix2 * md, const Matrix2 * ms); + + +struct Vector3 +{ + float v[3]; +}; + +inline void vec3_set(Vector3 * vd, float x, float y, float z); + +void vec3_sum(Vector3 * vd, const Vector3 * v1, const Vector3 * v2); + +void vec3_diff(Vector3 * vd, const Vector3 * v1, const Vector3 * v2); + +void vec3_add(Vector3 * vd, const Vector3 * vs); + +void vec3_sub(Vector3 * vd, const Vector3 * vs); + +void vec3_madd(Vector3 * vd, float s, const Vector3 * vs); + +void vec3_scale(Vector3 * vd, float s); + +void vec3_lincomb(Vector3 * vd, const Vector3 * vb, float x, const Vector3 * vx); + +void vec3_lincomb2(Vector3 * vd, const Vector3 * vb, float x, const Vector3 * vx, float y, const Vector3 * vy); + +void vec3_lerp(Vector3 * vd, const Vector3 * v1, const Vector3 * v2, float t); + +bool vec3_is_zero(const Vector3 * v); + +float vec3_vmul(const Vector3 * v1, const Vector3 * v2); + +void vec3_norm(Vector3 * v); + +float vec3_length(const Vector3 * v); + +float vec3_qlength(const Vector3 * v); + +float vec3_distance(const Vector3 * v1, const Vector3 * v2); + +float vec3_qdistance(const Vector3 * v1, const Vector3 * v2); + +void vec3_mcadd(Vector3 * vd, const Vector3 * v1, const Vector3 * v2); + +void vec3_mscadd(Vector3 * vd, float s, const Vector3 * v1, const Vector3 * v2); + +void vec3_cmul(Vector3 * vd, const Vector3 * v1, const Vector3 * v2); + +void vec3_xmul(Vector3 * vd, const Vector3 * v1, const Vector3 * v2); + +void vec3_mbase(Vector3 * v1, Vector3 * v2, Vector3 * v3); + +void vec3_bend(Vector3 * vd, const Vector3 * vs, float chi1, float chi2); + + + +struct Matrix3 +{ + float m[9]; +}; + +void mat3_ident(Matrix3 * m); + +void mat3_scale(Matrix3 * md, float s); + +void mat3_add(Matrix3 * md, const Matrix3 * ms); + +void mat3_mmul(Matrix3 * md, const Matrix3 * ms); + +void mat3_rmmul(Matrix3 * md, const Matrix3 * ms); + +void mat3_transpose(Matrix3 * md, const Matrix3 * ms); + +float mat3_det(const Matrix3 * ms); + +void mat3_invert(Matrix3 * md, const Matrix3 * ms); + +void vec3_mmul(Vector3 * vd, const Matrix3 * m, const Vector3 * vs); + +void mat3_set_rotate_x(Matrix3 * m, float a); + +void mat3_set_rotate_y(Matrix3 * m, float a); + +void mat3_set_rotate_z(Matrix3 * m, float a); + +void mat3_set_rotate(Matrix3 * m, const Vector3 * v, float a); + + + +struct Vector4 +{ + float v[4]; +}; + +inline void vec4_set(Vector4 * vd, float x, float y, float z, float w); + +void vec4_sum(Vector4 * vd, const Vector4 * v1, const Vector4 * v2); + +void vec4_diff(Vector4 * vd, const Vector4 * v1, const Vector4 * v2); + +void vec4_add(Vector4 * vd, const Vector4 * vs); + +void vec4_sub(Vector4 * vd, const Vector4 * vs); + +void vec4_madd(Vector4 * vd, float s, const Vector4 * vs); + +void vec4_scale(Vector4 * vd, float s); + +void vec4_lincomb(Vector4 * vd, const Vector4 * vb, float x, const Vector4 * vx); + +void vec4_lincomb2(Vector4 * vd, const Vector4 * vb, float x, const Vector4 * vx, float y, const Vector4 * vy); + +void vec4_lerp(Vector4 * vd, const Vector4 * v1, const Vector4 * v2, float t); + +bool vec4_is_zero(const Vector4 * v); + +float vec4_vmul(const Vector4 * v1, const Vector4 * v2); + +void vec4_norm(Vector4 * v); + +float vec4_length(const Vector4 * v); + +float vec4_qlength(const Vector4 * v); + +float vec4_distance(const Vector4 * v1, const Vector4 * v2); + +float vec4_qdistance(const Vector4 * v1, const Vector4 * v2); + +void vec4_mcadd(Vector4 * vd, const Vector4 * v1, const Vector4 * v2); + +void vec4_mscadd(Vector4 * vd, float s, const Vector4 * v1, const Vector4 * v2); + +void vec4_cmul(Vector4 * vd, const Vector4 * v1, const Vector4 * v2); + +void vec4_xmul(Vector4 * vd, const Vector4 * v1, const Vector4 * v2); + +void vec4_mbase(Vector4 * v1, Vector4 * v2, Vector4 * v3); + +void vec4_bend(Vector4 * vd, const Vector4 * vs, float chi1, float chi2); + + +struct Matrix4 +{ + float m[16]; +}; + +void mat4_ident(Matrix4 * m); + +void mat4_from_vec4(Matrix4 * m, const Vector4 * vx, const Vector4 * vy, const Vector4 * vz, const Vector4 * vw); + +void mat4_from_vec3(Matrix4 * m, const Vector3 * vx, const Vector3 * vy, const Vector3 * vz, const Vector3 * vw); + +void mat4_make_perspective(Matrix4 * m, float fieldOfViewInRadians, float aspect, float near, float far); + +void mat4_make_view(Matrix4 * m, const Vector3 * pos, const Vector3 * target, const Vector3 * up); + +void mat4_scale(Matrix4 * md, float s); + +void mat4_add(Matrix4 * md, const Matrix4 * ms); + +void mat4_mmul(Matrix4 * md, const Matrix4 * ms); + +void mat4_rmmul(Matrix4 * md, const Matrix4 * ms); + +float mat4_det(const Matrix4 * m); + +void mat4_invert(Matrix4 * md, const Matrix4 * ms); + +void vec4_mmul(Vector4 * vd, const Matrix4 * m, const Vector4 * vs); + +void vec3_mmulp(Vector3 * vd, const Matrix4 * m, const Vector3 * vs); + +void vec3_mmuld(Vector3 * vd, const Matrix4 * m, const Vector3 * vs); + + +void mat4_set_rotate_x(Matrix4 * m, float a); + +void mat4_set_rotate_y(Matrix4 * m, float a); + +void mat4_set_rotate_z(Matrix4 * m, float a); + +void mat4_set_rotate(Matrix4 * m, const Vector3 * v, float a); + +void mat4_set_translate(Matrix4 * m, const Vector3 * v); + +void mat4_set_scale(Matrix4 * m, float s); + + +void vec3_project(Vector3 * vd, const Matrix4 * m, const Vector3 * vs); + +// And now for some fixpoint math in 4.12 + +struct F12Vector3 +{ + int v[3]; +}; + +struct F12Matrix3 +{ + int m[9]; +}; + +static const int FIX12_ONE = 1 << 12; + +void f12mat3_ident(F12Matrix3 * m); + +void f12mat3_mmul(F12Matrix3 * md, const F12Matrix3 * ms); + +void f12mat3_rmmul(F12Matrix3 * md, const F12Matrix3 * ms); + +void f12mat3_set_rotate_x(F12Matrix3 * m, float a); + +void f12mat3_set_rotate_y(F12Matrix3 * m, float a); + + +void f12mat3_set_rotate_z(F12Matrix3 * m, float a); + +void f12vec3_mmul(F12Vector3 * vd, const F12Matrix3 * m, const F12Vector3 * vs); + + +#pragma compile("vector3d.c") + +#endif + diff --git a/oscar64/include/inttypes.c b/oscar64/include/inttypes.c new file mode 100644 index 0000000..0306527 --- /dev/null +++ b/oscar64/include/inttypes.c @@ -0,0 +1,26 @@ +#include "inttypes.h" +#include "stdlib.h" + + +intmax_t imaxabs(intmax_t n) +{ + return n < 0 ? -n : n; +} + +imaxdiv_t imaxdiv(intmax_t l, intmax_t r) +{ + imaxdiv_t t; + t.quot = l / r; + t.rem = l % r; + return t; +} + +inline intmax_t strtoimax(const char * s, char ** endp, int base) +{ + return strtol(s, endp, base); +} + +inline uintmax_t strtoumax(const char * s, char ** endp, int base) +{ + return strtoul(s, endp, base); +} diff --git a/oscar64/include/inttypes.h b/oscar64/include/inttypes.h new file mode 100644 index 0000000..7d60a49 --- /dev/null +++ b/oscar64/include/inttypes.h @@ -0,0 +1,98 @@ +#ifndef INTTYPES_H +#define INTTYPES_H + +#include + +#define PRId8 "d" +#define PRId16 "d" +#define PRId32 "ld" + +#define PRIdLEAST8 "d" +#define PRIdLEAST16 "d" +#define PRIdLEAST32 "ld" + +#define PRIdFAST8 "d" +#define PRIdFAST16 "d" +#define PRIdFAST32 "ld" + +#define PRIdMAX "ld" +#define PRIdPTR "d" + + +#define PRIo8 "o" +#define PRIo16 "o" +#define PRIo32 "lo" + +#define PRIoLEAST8 "o" +#define PRIoLEAST16 "o" +#define PRIoLEAST32 "lo" + +#define PRIoFAST8 "o" +#define PRIoFAST16 "o" +#define PRIoFAST32 "lo" + +#define PRIoMAX "lo" +#define PRIoPTR "o" + +#define PRIu8 "u" +#define PRIu16 "u" +#define PRIu32 "lu" + +#define PRIuLEAST8 "u" +#define PRIuLEAST16 "u" +#define PRIuLEAST32 "lu" + +#define PRIuFAST8 "u" +#define PRIuFAST16 "u" +#define PRIuFAST32 "lu" + +#define PRIuMAX "lu" +#define PRIuPTR "u" + + +#define PRIx8 "x" +#define PRIx16 "x" +#define PRIx32 "lx" + +#define PRIxLEAST8 "x" +#define PRIxLEAST16 "x" +#define PRIxLEAST32 "lx" + +#define PRIxFAST8 "x" +#define PRIxFAST16 "x" +#define PRIxFAST32 "lx" + +#define PRIxMAX "lx" +#define PRIxPTR "x" + + +#define PRIX8 "X" +#define PRIX16 "X" +#define PRIX32 "lX" + +#define PRIXLEAST8 "X" +#define PRIXLEAST16 "X" +#define PRIXLEAST32 "lX" + +#define PRIXFAST8 "X" +#define PRIXFAST16 "X" +#define PRIXFAST32 "lX" + +#define PRIXMAX "lX" +#define PRIXPTR "X" + + +typedef struct { + intmax_t quot; + intmax_t rem; +} imaxdiv_t; + +intmax_t imaxabs(intmax_t n); +imaxdiv_t imaxdiv(intmax_t l, intmax_t r); +intmax_t strtoimax(const char * s, char ** endp, int base); +uintmax_t strtoumax(const char * s, char ** endp, int base); + + +#pragma compile("inttypes.c") + +#endif diff --git a/oscar64/include/iso646.h b/oscar64/include/iso646.h new file mode 100644 index 0000000..f2ab35e --- /dev/null +++ b/oscar64/include/iso646.h @@ -0,0 +1,16 @@ +#ifndef ISO646_H +#define ISO646_H + +#define and && +#define and_eq &= +#define bitand & +#define bitor | +#define compl ~ +#define not ! +#define not_eq != +#define or || +#define or_eq |= +#define xor ^ +#define xor_eq ^= + +#endif diff --git a/oscar64/include/limits.h b/oscar64/include/limits.h new file mode 100644 index 0000000..673b529 --- /dev/null +++ b/oscar64/include/limits.h @@ -0,0 +1,18 @@ +#ifndef LIMITS_H +#define LIMITS_H + +#define CHAR_BIT 8 + +#define SCHAR_MIN -128 +#define SCHAR_MAX 127 + +#define UCHAR_MAX 255 + +#define CHAR_MIN SCHAR_MIN +#define CHAR_MAX SCHAR_MAX + +#define INT_MIN (-32767-1) +#define INT_MAX 32767 +#define UINT_MAX 65535 + +#endif diff --git a/oscar64/include/math.c b/oscar64/include/math.c new file mode 100644 index 0000000..9a0341f --- /dev/null +++ b/oscar64/include/math.c @@ -0,0 +1,239 @@ +#include "math.h" +#include "stdio.h" + +float cos(float f) +{ + return sin(f + 0.5 * PI); +} + +#define F_SIN_1 6.283168 +#define F_SIN_2 0.053878661 +#define F_SIN_3 -42.5385038 +#define F_SIN_4 9.23583223 +#define F_SIN_5 55.7310503 + + +float sin(float f) +{ + float g = fabs(f); + float m = f < 0.0 ? -1.0 : 1.0; + + g *= 0.5 / PI; + g -= floor(g); + + if (g >= 0.5) + { + m = -m; + g -= 0.5; + } + if (g >= 0.25) + g = 0.5 - g; + + float s = F_SIN_5; + s *= g; s += F_SIN_4; + s *= g; s += F_SIN_3; + s *= g; s += F_SIN_2; + s *= g; s += F_SIN_1; + s *= g; + + return s * m; +} + +float tan(float f) +{ + return sin(f) / cos(f); +} + +float acos(float f) +{ + return atan2(sqrt(1.0 - f * f), f); +} + +float asin(float f) +{ + return atan2(f, sqrt(1.0 - f * f)); +} + +float atan(float f) +{ + return atan2(f, 1.0); +} + +#define F_ATAN_0 -6.435678E-5 +#define F_ATAN_1 0.999648382 +#define F_ATAN_2 0.018278903 +#define F_ATAN_3 -0.444599298 +#define F_ATAN_4 0.263177486 +#define F_ATAN_5 -0.051078392 + + +float atan2(float p, float q) +{ + int quad = 0; + + if (p < 0) + { + quad |= 4; + p = -p; + } + if (q < 0) + { + quad |= 2; + q = -q; + } + + float g; + if (p > q) + { + g = q / p; + quad |= 1; + } + else + { + g = p / q; + } + + float s = F_ATAN_5; + s *= g; s += F_ATAN_4; + s *= g; s += F_ATAN_3; + s *= g; s += F_ATAN_2; + s *= g; s += F_ATAN_1; + s *= g; s += F_ATAN_0; + + if (quad & 1) + s = 0.5 * PI - s; + if (quad & 2) + s = PI - s; + if (quad & 4) + s = -s; + + return s; +} + +#define F_EXP_0 1.0 +#define F_EXP_1 0.69315668 +#define F_EXP_2 0.240132068 +#define F_EXP_3 0.055876024 +#define F_EXP_4 0.008940801 +#define F_EXP_5 0.001894414 + +float exp(float f) +{ + f *= 1.442695041; + + float ff = floor(f), g = f - ff; + + int fi = (int)ff; + + union { + float f; + int i[2]; + } x; + x.f = 0; + + x.i[1] = (fi + 0x7f) << 7; + + float s = F_EXP_5; + s *= g; s += F_EXP_4; + s *= g; s += F_EXP_3; + s *= g; s += F_EXP_2; + s *= g; s += F_EXP_1; + s *= g; s += F_EXP_0; + + return s * x.f; +} + +#define F_LOG_0 -3.78717706 +#define F_LOG_1 10.0960498 +#define F_LOG_2 -13.975654 +#define F_LOG_3 12.7580616 +#define F_LOG_4 -6.48190725 +#define F_LOG_5 1.39064767 + +float log(float f) +{ + if (f == 0.0) + return 1.0; + + union { + float f; + int i[2]; + } x; + + x.f = f; + int ei = x.i[1]; + int ex = (ei >> 7) - 0x7e; + x.i[1] = (ei & 0x007f) | 0x3f00; + + float g = x.f; + + float fex = ex; + + float s = F_LOG_5; + s *= g; s += F_LOG_4; + s *= g; s += F_LOG_3; + s *= g; s += F_LOG_2; + s *= g; s += F_LOG_1; + s *= g; s += F_LOG_0; + + return (fex + s) * 0.6931471806; +} + +float log10(float f) +{ + return log(f) * 0.4342944819; +} + +float pow(float p, float q) +{ + return exp(q * log(p)); +} + +float sqrt(float f) +{ + if (f >= 0) + { + union { + float f; + int i[2]; + } x; + + x.f = f; + int ex = (x.i[1] >> 7) - 0x7f; + ex /= 2; + x.i[1] = (ex + 0x7f) << 7; + float fq = x.f; + fq = 0.5 * (fq + f / fq); + fq = 0.5 * (fq + f / fq); + fq = 0.5 * (fq + f / fq); + fq = 0.5 * (fq + f / fq); + return fq; + } + else + return 0.0; +} + + +bool isinf(float f) +{ + union { + float f; + unsigned i[2]; + } x; + + x.f = f; + + return ((x.i[1] >> 7) & 0xff) == 0xff; +} + +bool isfinite(float f) +{ + union { + float f; + unsigned i[2]; + } x; + + x.f = f; + + return ((x.i[1] >> 7) & 0xff) != 0xff; +} diff --git a/oscar64/include/math.h b/oscar64/include/math.h new file mode 100644 index 0000000..79edcea --- /dev/null +++ b/oscar64/include/math.h @@ -0,0 +1,49 @@ +#ifndef MATH_H +#define MATH_H + +#define PI 3.141592653 + + +float fabs(float f); +float floor(float f); +float ceil(float f); + +float cos(float f); +float sin(float f); +float tan(float f); +float acos(float f); +float asin(float f); +float atan(float f); +float atan2(float p, float q); + +float exp(float f); +float log(float f); +float log10(float f); + +float pow(float p, float q); +float sqrt(float f); + +bool isinf(float f); +bool isfinite(float f); + +#pragma intrinsic(fabs) +#pragma intrinsic(floor) +#pragma intrinsic(ceil) + +#pragma intrinsic(sin) +#pragma intrinsic(cos) +#pragma intrinsic(tan) +#pragma intrinsic(atan) +#pragma intrinsic(atan2) + +#pragma intrinsic(log) +#pragma intrinsic(exp) + +#pragma intrinsic(pow) +#pragma intrinsic(sqrt) + +#pragma compile("math.c") + + +#endif + diff --git a/oscar64/include/nes/mmc1.c b/oscar64/include/nes/mmc1.c new file mode 100644 index 0000000..94a14a9 --- /dev/null +++ b/oscar64/include/nes/mmc1.c @@ -0,0 +1,60 @@ +#include "mmc1.h" + + +void mmc1_reset(void) +{ + *(volatile char *)0x8000 = 0x80; +} + +void mmc1_config(MMC1Mirror mirror, MMC1MPrgMode pmode, MMC1MChrMode cmode) +{ + char reg = mirror | (pmode << 2) | (cmode << 4); + *(volatile char *)0x8000 = reg; + reg >>= 1; + *(volatile char *)0x8000 = reg; + reg >>= 1; + *(volatile char *)0x8000 = reg; + reg >>= 1; + *(volatile char *)0x8000 = reg; + reg >>= 1; + *(volatile char *)0x8000 = reg; +} + +void mmc1_bank_prg(char bank) +{ + *(volatile char *)0xe000 = bank; + bank >>= 1; + *(volatile char *)0xe000 = bank; + bank >>= 1; + *(volatile char *)0xe000 = bank; + bank >>= 1; + *(volatile char *)0xe000 = bank; + bank >>= 1; + *(volatile char *)0xe000 = bank; +} + +void mmc1_bank_chr0(char bank) +{ + *(volatile char *)0xa000 = bank; + bank >>= 1; + *(volatile char *)0xa000 = bank; + bank >>= 1; + *(volatile char *)0xa000 = bank; + bank >>= 1; + *(volatile char *)0xa000 = bank; + bank >>= 1; + *(volatile char *)0xa000 = bank; +} + +void mmc1_bank_chr1(char bank) +{ + *(volatile char *)0xc000 = bank; + bank >>= 1; + *(volatile char *)0xc000 = bank; + bank >>= 1; + *(volatile char *)0xc000 = bank; + bank >>= 1; + *(volatile char *)0xc000 = bank; + bank >>= 1; + *(volatile char *)0xc000 = bank; +} diff --git a/oscar64/include/nes/mmc1.h b/oscar64/include/nes/mmc1.h new file mode 100644 index 0000000..c7583d6 --- /dev/null +++ b/oscar64/include/nes/mmc1.h @@ -0,0 +1,40 @@ +#ifndef NES_MMC1_H +#define NES_MMC1_H + +#include + +enum MMC1Mirror +{ + MMC1M_LOWER, + MMC1M_UPPER, + MMC1M_VERTICAL, + MMC1M_HORIZONTAL +}; + +enum MMC1MPrgMode +{ + MMC1P_32K, + MMC1P_32Kx, + MMC1P_16K_UPPER, + MMC1P_16K_LOWER +}; + +enum MMC1MChrMode +{ + MMC1C_8K, + MMC1C_4Kx, +}; + +void mmc1_reset(void); + +void mmc1_config(MMC1Mirror mirror, MMC1MPrgMode pmode, MMC1MChrMode cmode); + +void mmc1_bank_prg(char bank); + +void mmc1_bank_chr0(char bank); + +void mmc1_bank_chr1(char bank); + +#pragma compile("mmc1.c") + +#endif diff --git a/oscar64/include/nes/mmc3.c b/oscar64/include/nes/mmc3.c new file mode 100644 index 0000000..08b0f0c --- /dev/null +++ b/oscar64/include/nes/mmc3.c @@ -0,0 +1,36 @@ +#include "mmc3.h" + +char mmc3_shadow; + +void mmc3_reset(void) +{ + mmc3_shadow = 0; +} + +void mmc3_config(MMC3MPrgMode pmode, MMC3MChrMode cmode) +{ + mmc3_shadow = (pmode << 6) | (cmode << 7); + *(volatile char *)0x8000 = mmc3_shadow; +} + +void mmc3_bank(MMC3BankReg reg, char bank) +{ + *(volatile char *)0x8000 = reg | mmc3_shadow; + *(volatile char *)0x8001 = bank; +} + +void mmc3_bank_prg(char bank) +{ + mmc3_bank(MMC3B_PRG0, bank * 2 + 0); + mmc3_bank(MMC3B_PRG1, bank * 2 + 1); +} + +void mmc3_bank_chr0(char bank) +{ + mmc3_bank(MMC3B_CHR0, bank); +} + +void mmc3_bank_chr1(char bank) +{ + mmc3_bank(MMC3B_CHR1, bank); +} diff --git a/oscar64/include/nes/mmc3.h b/oscar64/include/nes/mmc3.h new file mode 100644 index 0000000..70b2d0e --- /dev/null +++ b/oscar64/include/nes/mmc3.h @@ -0,0 +1,48 @@ +#ifndef NES_MMC3_H +#define NES_MMC3_H + +#include + + +enum MMC3MPrgMode +{ + MMC3P_8K_LOWER, + MMC3P_8K_UPPER +}; + +enum MMC3MChrMode +{ + MMC3C_2K_LOWER, + MMC3C_2K_HIGHER, +}; + +enum MMC3BankReg +{ + MMC3B_CHR0, + MMC3B_CHR1, + MMC3B_CHR2, + MMC3B_CHR3, + MMC3B_CHR4, + MMC3B_CHR5, + + MMC3B_PRG0, + MMC3B_PRG1 +}; + +extern char mmc3_shadow; + +void mmc3_reset(void); + +void mmc3_config(MMC3MPrgMode pmode, MMC3MChrMode cmode); + +inline void mmc3_bank(MMC3BankReg reg, char bank); + +void mmc3_bank_prg(char bank); + +void mmc3_bank_chr0(char bank); + +void mmc3_bank_chr1(char bank); + +#pragma compile("mmc3.c") + +#endif diff --git a/oscar64/include/nes/nes.c b/oscar64/include/nes/nes.c new file mode 100644 index 0000000..ba02100 --- /dev/null +++ b/oscar64/include/nes/nes.c @@ -0,0 +1,2 @@ +#include "nes.h" + diff --git a/oscar64/include/nes/nes.h b/oscar64/include/nes/nes.h new file mode 100644 index 0000000..802a544 --- /dev/null +++ b/oscar64/include/nes/nes.h @@ -0,0 +1,76 @@ +#ifndef NES_NES_H +#define NES_NES_H + +#include + +#define PPU_CTRL_NT_2000 0b00000000 +#define PPU_CTRL_NT_2400 0b00000001 +#define PPU_CTRL_NT_2800 0b00000010 +#define PPU_CTRL_NT_2C00 0b00000011 +#define PPU_CTRL_INC_1 0b00000000 +#define PPU_CTRL_INC_32 0b00000100 +#define PPU_CTRL_SPR_0000 0b00000000 +#define PPU_CTRL_SPR_1000 0b00001000 +#define PPU_CTRL_BG_0000 0b00000000 +#define PPU_CTRL_BG_1000 0b00010000 +#define PPU_CTRL_SPR_8X8 0b00000000 +#define PPU_CTRL_SPR_8X16 0b00100000 +#define PPU_CTRL_NMI 0b10000000 + +#define PPU_MASK_GREYSCALE 0b00000001 +#define PPU_MASK_BG8 0b00000010 +#define PPU_MASK_SPR8 0b00000100 +#define PPU_MASK_BG_ON 0b00001000 +#define PPU_MASK_SPR_ON 0b00010000 +#define PPU_MASK_EM_RED 0b00100000 +#define PPU_MASK_EM_GREEN 0b01000000 +#define PPU_MASK_EM_BLUE 0b10000000 + +struct PPU +{ + volatile byte control; + volatile byte mask; + volatile byte status; + volatile byte oamaddr; + volatile byte oamdata; + volatile byte scroll; + volatile byte addr; + volatile byte data; +}; + +#define ppu (*((struct PPU *)0x2000)) + +struct NESIO +{ + volatile byte sq1_volume; + volatile byte sq1_sweep; + volatile word sq1_freq; + + volatile byte sq2_volume; + volatile byte sq2_sweep; + volatile word sq2_freq; + + volatile byte tri_volume; + volatile byte tri_pad; + volatile word tri_freq; + + volatile byte noise_volume; + volatile byte noise_pad; + volatile word noise_freq; + + volatile byte dmc_freq; + volatile byte dmc_raw; + volatile byte dmc_start; + volatile byte dmc_length; + + volatile byte oamdma; + volatile byte channels; + volatile byte input[2]; +}; + +#define nesio (*((struct NESIO *)0x4000)) + +#pragma compile("nes.c") + +#endif + diff --git a/oscar64/include/nes/neslib.c b/oscar64/include/nes/neslib.c new file mode 100644 index 0000000..c63a014 --- /dev/null +++ b/oscar64/include/nes/neslib.c @@ -0,0 +1,667 @@ +#include "neslib.h" +// NES hardware-dependent functions by Shiru (shiru@mail.ru) +// with improvements by VEG +// Feel free to do anything you want with this code, consider it Public Domain + +const char palBrightTable[] = { + 0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f, + 0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f, + 0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f, + 0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f, + 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0f,0x0f,0x0f, + 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x00,0x00,0x00, + 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x10,0x10,0x10, + 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x20,0x20,0x20, + 0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30, + 0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30, + 0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30, + 0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30 +}; + +volatile char OAM_BUF[256]; +volatile char PAL_BUF[32]; + +#pragma align(OAM_BUF, 256) + +char NTSC_MODE; +volatile char FRAME_CNT1; +volatile char FRAME_CNT2; +volatile char VRAM_UPDATE; +char * volatile NAME_UPD_ADR; +volatile char NAME_UPD_ENABLE; +volatile char PAL_UPDATE; +const char *volatile PAL_BG_PTR; +const char *volatile PAL_SPR_PTR; +volatile char SCROLL_X; +volatile char SCROLL_Y; +char SCROLL_X1; +char SCROLL_Y1; +char PAD_STATE[2]; +char PAD_STATEP[2]; +char PAD_STATET[2]; +volatile char PPU_CTRL_VAR; +char PPU_CTRL_VAR1; +volatile char PPU_MASK_VAR; +char RAND_SEED[2]; + +int main(void) +{ + ppu.mask = 0; + nesio.dmc_freq = 0; + ppu.control = 0; + + char c = ppu.status; + do {} while (!(ppu.status & 0x80)); + do {} while (!(ppu.status & 0x80)); + + nesio.input[1] = 0x40; + + ppu.addr = 0x3f; + ppu.addr = 0x00; + for(char i=0; i<32; i++) + ppu.data = 0x0f; + + ppu.addr = 0x20; + ppu.addr = 0x00; + + for(unsigned i=0; i<0x1000; i++) + ppu.data = 0x00; + + char i = 0; + do { + ((char *)0x200)[i] = 0; + ((char *)0x300)[i] = 0; + ((char *)0x400)[i] = 0; + ((char *)0x500)[i] = 0; + ((char *)0x600)[i] = 0; + ((char *)0x700)[i] = 0; + i++; + } while (i); + + pal_bright(4); + pal_clear(); + oam_clear(); + + PPU_CTRL_VAR = 0x80; + PPU_MASK_VAR = 0x06; + + ppu.control = 0x80; + + RAND_SEED[0] = 0xfd; + RAND_SEED[1] = 0xfd; + + ppu.scroll = 0x00; + ppu.scroll = 0x00; + ppu.oamaddr = 0x00; + + // detect PAL or NTSC based on Blarg code. + + ppu_wait_nmi(); + + __asm { + ldx #52 + ldy #24 + system_check_wait: + dex + bne system_check_wait + dey + bne system_check_wait + + lda $2002 + and #$80 + sta NTSC_MODE + } + + nes_game(); + + return 0; +} + +__hwinterrupt void nmi(void) +{ + if (PPU_MASK_VAR & 0x18) + { + nesio.oamdma = (unsigned)(&OAM_BUF[0]) >> 8; + + if (PAL_UPDATE) + { + PAL_UPDATE = 0; + ppu.addr = 0x3f; + ppu.addr = 0x00; + + char c = PAL_BG_PTR[PAL_BUF[0]]; + + ppu.data = c; + ppu.data = PAL_BG_PTR[PAL_BUF[1]]; + ppu.data = PAL_BG_PTR[PAL_BUF[2]]; + ppu.data = PAL_BG_PTR[PAL_BUF[3]]; + + #pragma unroll(full) + for(char j=0; j<3; j++) + { + ppu.data = c; + ppu.data = PAL_BG_PTR[PAL_BUF[5 + 4 * j + 0]]; + ppu.data = PAL_BG_PTR[PAL_BUF[5 + 4 * j + 1]]; + ppu.data = PAL_BG_PTR[PAL_BUF[5 + 4 * j + 2]]; + } + + #pragma unroll(full) + for(char j=0; j<4; j++) + { + ppu.data = c; + ppu.data = PAL_SPR_PTR[PAL_BUF[17 + 4 * j + 0]]; + ppu.data = PAL_SPR_PTR[PAL_BUF[17 + 4 * j + 1]]; + ppu.data = PAL_SPR_PTR[PAL_BUF[17 + 4 * j + 2]]; + } + } + + if (VRAM_UPDATE) + { + VRAM_UPDATE = 0; + + if (NAME_UPD_ENABLE) + flush_vram_update(NAME_UPD_ADR); + } + + ppu.addr = 0x00; + ppu.addr = 0x00; + + ppu.scroll = SCROLL_X; + ppu.scroll = SCROLL_Y; + + ppu.control = PPU_CTRL_VAR; + } + + ppu.mask = PPU_MASK_VAR; + + FRAME_CNT1++; + FRAME_CNT2++; + if (FRAME_CNT2 == 6) + FRAME_CNT2 = 0; + + // jsr FamiToneUpdate +} + +void pal_all(const char *data) +{ + for(char i=0; i<32; i++) + PAL_BUF[i] = data[i]; + PAL_UPDATE++; +} + +void pal_bg(const char *data) +{ + for(char i=0; i<16; i++) + PAL_BUF[i] = data[i]; + PAL_UPDATE++; +} + +void pal_spr(const char *data) +{ + for(char i=0; i<16; i++) + PAL_BUF[i + 16] = data[i]; + PAL_UPDATE++; +} + +void pal_col(unsigned char index,unsigned char color) +{ + PAL_BUF[index & 0x1f] = color; + PAL_UPDATE++; +} + +void pal_clear(void) +{ + for(char i=0; i<32; i++) + PAL_BUF[i] = 0x0f; + PAL_UPDATE++; +} + +void pal_spr_bright(unsigned char bright) +{ + PAL_SPR_PTR = palBrightTable + 16 * bright; + PAL_UPDATE++; +} + +void pal_bg_bright(unsigned char bright) +{ + PAL_BG_PTR = palBrightTable + 16 * bright; + PAL_UPDATE++; +} + + + +void pal_bright(unsigned char bright) +{ + pal_spr_bright(bright); + pal_bg_bright(bright); +} + +void ppu_off(void) +{ + PPU_MASK_VAR &= 0b11100111; + ppu_wait_nmi(); +} + +void ppu_on_all(void) +{ + PPU_MASK_VAR|= 0b00011000; + ppu_wait_nmi(); +} + +void ppu_on_bg(void) +{ + PPU_MASK_VAR |= 0b00001000; + ppu_wait_nmi(); +} + +void ppu_on_spr(void) +{ + PPU_MASK_VAR |= 0b00010000; + ppu_wait_nmi(); +} + +void ppu_mask(unsigned char mask) +{ + PPU_MASK_VAR = mask; +} + +unsigned char ppu_system(void) +{ + return NTSC_MODE; +} + +unsigned char get_ppu_ctrl_var(void) +{ + return PPU_CTRL_VAR; +} + +void set_ppu_ctrl_var(unsigned char var) +{ + PPU_CTRL_VAR = var; +} + +void oam_clear(void) +{ + char i = 0; + do { + OAM_BUF[i] = 255; // off screen not top corner. + i += 4; + } while (i); +} + +void oam_size(unsigned char size) +{ + if (size & 1) + PPU_CTRL_VAR |= 0x20; + else + PPU_CTRL_VAR &= ~0x20; +} + +unsigned char oam_spr(unsigned char x,unsigned char y,unsigned char chrnum,unsigned char attr,unsigned char sprid) +{ + OAM_BUF[sprid + 2] = attr; + OAM_BUF[sprid + 1] = chrnum; + OAM_BUF[sprid + 0] = y; + OAM_BUF[sprid + 3] = x; + return sprid + 4; +} + +unsigned char oam_meta_spr(unsigned char x,unsigned char y,unsigned char sprid,const unsigned char *data) +{ + char i = 0; + while (data[i] != 0x80) + { + OAM_BUF[sprid + 3] = x + data[i + 0]; + OAM_BUF[sprid + 0] = y + data[i + 1]; + OAM_BUF[sprid + 1] = data[i + 2]; + OAM_BUF[sprid + 2] = data[i + 3]; + + sprid += 4; + i += 4; + } + return sprid; +} + + +void oam_hide_rest(unsigned char sprid) +{ + do { + OAM_BUF[sprid] = 240; + sprid += 4; + } while (sprid); +} + +void ppu_wait_frame(void) +{ + VRAM_UPDATE = 1; + char c = FRAME_CNT1; + while (c == FRAME_CNT1) ; + if (NTSC_MODE) + { + while (FRAME_CNT2 == 5) ; + } +} + +void ppu_wait_nmi(void) +{ + VRAM_UPDATE = 1; + char c = FRAME_CNT1; + while (c == FRAME_CNT1) ; +} + +void vram_unrle(const unsigned char *data) +{ + char tag = *data++; + char b; + + for(;;) + { + char c = *data++; + if (c != tag) + { + ppu.data = c; + b = c; + } + else + { + c = *data++; + if (!c) + return; + while (c) + { + ppu.data = b; + c--; + } + } + } +} + +void scroll(unsigned int x,unsigned int y) +{ + char b = (PPU_CTRL_VAR & 0xfc) | ((x >> 8) & 1); + + if (y >= 240) + { + y -= 240; + b |= 2; + } + + SCROLL_Y = y; + SCROLL_X = x; + + PPU_CTRL_VAR = b; +} + +void split(unsigned int x,unsigned int y) +{ + char b = (PPU_CTRL_VAR & 0xfc) | ((x >> 8) & 1); + + SCROLL_X1 = x; + PPU_CTRL_VAR1 = b; + + while (ppu.status & 0x40) ; + while (!(ppu.status & 0x40)) ; + + ppu.scroll = SCROLL_X1; + ppu.scroll = 0; + ppu.control = PPU_CTRL_VAR1; +} + +void bank_spr(unsigned char n) +{ + if (n & 1) + PPU_CTRL_VAR |= 0x08; + else + PPU_CTRL_VAR &= ~0x08; +} + +void bank_bg(unsigned char n) +{ + if (n & 1) + PPU_CTRL_VAR |= 0x10; + else + PPU_CTRL_VAR &= ~0x10; +} + +void vram_read(unsigned char *dst,unsigned int size) +{ + for(unsigned i=size; i!=0; i--) + *dst++ = ppu.data; +} + +void vram_write(const unsigned char *src,unsigned int size) +{ + for(unsigned i=size; i!=0; i--) + ppu.data = *src++; +} + +void music_play(unsigned char song) +{ + //_music_play=FamiToneMusicPlay +} + +void music_stop(void) +{ + //_music_stop=FamiToneMusicStop + +} + +void music_pause(unsigned char pause) +{ + //_music_pause=FamiToneMusicPause +} + +void sfx_play(unsigned char sound,unsigned char channel) +{ +#if 0 +_sfx_play: + +.if(FT_SFX_ENABLE) + + and #$03 + tax + lda @sfxPriority,x + tax + jsr popa + jmp FamiToneSfxPlay + +@sfxPriority: + + .byte FT_SFX_CH0,FT_SFX_CH1,FT_SFX_CH2,FT_SFX_CH3 + +.else + rts +.endif +#endif +} + +void sample_play(unsigned char sample) +{ +#if 0 +.if(FT_DPCM_ENABLE) +_sample_play=FamiToneSamplePlay +.else +_sample_play: + rts +.endif +#endif +} + +unsigned char pad_poll(unsigned char pad) +{ + char buf[3]; + + for(char j=0; j<3; j++) + { + nesio.input[0] = 1; + nesio.input[0] = 0; + + char c = 0; + for(char i=0; i<8; i++) + { + c = (c | (nesio.input[pad] << 8)) >> 1; + } + buf[j] = c; + } + + char b = buf[0]; + if (b != buf[1] && b != buf[2]) + b = buf[1]; + + PAD_STATE[pad] = b; + PAD_STATET[pad] = (b ^ PAD_STATEP[pad]) & PAD_STATE[pad]; + PAD_STATEP[pad] = b; + + return b; +} + +unsigned char pad_trigger(unsigned char pad) +{ + pad_poll(pad); + return PAD_STATET[pad]; +} + +unsigned char pad_state(unsigned char pad) +{ + return PAD_STATE[pad]; +} + +unsigned char rand1(void) +{ + if (RAND_SEED[0] & 0x80) + { + RAND_SEED[0] <<= 1; + RAND_SEED[0] ^= 0xcf; + } + else + RAND_SEED[0] <<= 1; + return RAND_SEED[0]; +} + + +unsigned char rand2(void) +{ + if (RAND_SEED[1] & 0x80) + { + RAND_SEED[1] <<= 1; + RAND_SEED[1] ^= 0xd7; + } + else + RAND_SEED[1] <<= 1; + return RAND_SEED[1]; +} + +unsigned char rand8(void) +{ + return rand1() + rand2(); +} + +unsigned int rand16(void) +{ + return (rand1() << 8) | rand2(); +} + +void set_rand(unsigned seed) +{ + RAND_SEED[0] = seed & 0xff; + RAND_SEED[1] = seed >> 8; +} + +void set_vram_update(unsigned char *buf) +{ + NAME_UPD_ADR = buf; + NAME_UPD_ENABLE = buf != nullptr; +} + +void flush_vram_update(unsigned char *buf) +{ + char i = 0; + for(;;) + { + char c = buf[i++]; + if (c < 0x40) + { + ppu.addr = c; + ppu.addr = buf[i++]; + ppu.data = buf[i++]; + } + else + { + if (c < 0x80) + ppu.control = PPU_CTRL_VAR & ~0x04; + else if (c != 0xff) + ppu.control = PPU_CTRL_VAR | 0x04; + else + return; + + ppu.addr = c & 0x3f; + ppu.addr = buf[i++]; + c = buf[i++]; + do { + ppu.data = buf[i++]; + c--; + } while (c); + ppu.control = PPU_CTRL_VAR; + } + } +} + +void vram_adr(unsigned int addr) +{ + ppu.addr = addr >> 8; + ppu.addr = addr & 0xff; +} + +void vram_put(unsigned char n) +{ + ppu.data = n; +} + +void vram_fill(unsigned char n,unsigned int size) +{ + for(unsigned i=size; i!=0; i--) + ppu.data = n; +} + +void vram_inc(unsigned char n) +{ + if (n) + PPU_CTRL_VAR |= 0x04; + else + PPU_CTRL_VAR &= ~0x04; + ppu.control = PPU_CTRL_VAR; +} + +void memfill(void *dst,unsigned char value,unsigned int size) +{ + for(unsigned i=size; i!=0; i--) + *dst++ = value; +} + +unsigned char nesclock(void) +{ + return FRAME_CNT1; +} + +void delay(unsigned char frames) +{ + while (frames) + { + ppu_wait_nmi(); + frames--; + } +} + +#pragma data(boot) + +__export struct Boot +{ + void * nmi, * reset, * irq; +} boot = { + nmi, + (void *)0xff80, + nullptr +}; + +#pragma data(data) + diff --git a/oscar64/include/nes/neslib.h b/oscar64/include/nes/neslib.h new file mode 100644 index 0000000..04bb6c2 --- /dev/null +++ b/oscar64/include/nes/neslib.h @@ -0,0 +1,295 @@ +#ifndef NES_NESLIB_H +#define NES_NESLIB_H + +#include "nes.h" + + +/* + (C) 2015 Alex Semenov (Shiru) + (C) 2016 Lauri Kasanen + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +// NES hardware-dependent functions by Shiru (shiru@mail.ru) +// Feel free to do anything you want with this code, consider it Public Domain + +// Versions history: +// 280215 - fixed palette glitch caused with the active DMC DMA glitch +// 030914 - minor fixes in the vram update system +// 310814 - added vram_flush_update +// 120414 - removed adr argument from vram_write and vram_read, +// unrle_vram renamed to vram_unrle, with adr argument removed +// 060414 - many fixes and improvements, including sequental VRAM updates +// previous versions were created since mid-2011, there were many updates + + +void nes_game(void); + + +// set bg and spr palettes, data is 32 bytes array +void pal_all(const char *data); + +// set bg palette only, data is 16 bytes array +void pal_bg(const char *data); + +// set spr palette only, data is 16 bytes array +void pal_spr(const char *data); + +// set a palette entry, index is 0..31 +inline void pal_col(unsigned char index, unsigned char color); + +// reset palette to $0f +void pal_clear(void); + +// set virtual bright both for sprites and background, 0 is black, 4 is normal, 8 is white +void pal_bright(unsigned char bright); + +// set virtual bright for sprites only +void pal_spr_bright(unsigned char bright); + +// set virtual bright for sprites background only +void pal_bg_bright(unsigned char bright); + + + +// wait actual TV frame, 50hz for PAL, 60hz for NTSC +void ppu_wait_nmi(void); + +// wait virtual frame, it is always 50hz, frame-to-frame in PAL, frameskip in NTSC +void ppu_wait_frame(void); + +// turn off rendering, nmi still enabled when rendering is disabled +void ppu_off(void); + +// turn on bg, spr +void ppu_on_all(void); + +// turn on bg only +void ppu_on_bg(void); + +// turn on spr only +void ppu_on_spr(void); + +// set PPU_MASK directly +inline void ppu_mask(unsigned char mask); + +// get current video system, 0 for PAL, not 0 for NTSC +unsigned char ppu_system(void); + +// Return an 8-bit counter incremented at each vblank +unsigned char nesclock(void); + +// get/set the internal ppu ctrl cache var for manual writing +inline unsigned char get_ppu_ctrl_var(void); +inline void set_ppu_ctrl_var(unsigned char var); + + +// clear OAM buffer, all the sprites are hidden +void oam_clear(void); + +// set sprite display mode, 0 for 8x8 sprites, 1 for 8x16 sprites +inline void oam_size(unsigned char size); + +// set sprite in OAM buffer, chrnum is tile, attr is attribute, sprid is offset in OAM in bytes +// returns sprid+4, which is offset for a next sprite +inline unsigned char oam_spr(unsigned char x, unsigned char y, + unsigned char chrnum, unsigned char attr, + unsigned char sprid); + +// set metasprite in OAM buffer +// meta sprite is a const unsigned char array, it contains four bytes per sprite +// in order x offset, y offset, tile, attribute +// x=128 is end of a meta sprite +// returns sprid+4, which is offset for a next sprite +unsigned char oam_meta_spr(unsigned char x, unsigned char y, + unsigned char sprid, const unsigned char *data); + +// hide all remaining sprites from given offset +void oam_hide_rest(unsigned char sprid); + + + +// play a music in FamiTone format +void music_play(unsigned char song); + +// stop music +void music_stop(void); + +// pause and unpause music +void music_pause(unsigned char pause); + +// play FamiTone sound effect on channel 0..3 +void sfx_play(unsigned char sound, unsigned char channel); + +// play a DPCM sample, 1..63 +void sample_play(unsigned char sample); + + + +// poll controller and return flags like PAD_LEFT etc, input is pad number (0 or 1) +unsigned char pad_poll(unsigned char pad); + +// poll controller in trigger mode, a flag is set only on button down, not hold +// if you need to poll the pad in both normal and trigger mode, poll it in the +// trigger mode for first, then use pad_state +unsigned char pad_trigger(unsigned char pad); + +// get previous pad state without polling ports +inline unsigned char pad_state(unsigned char pad); + + +// set scroll, including rhe top bits +// it is always applied at beginning of a TV frame, not at the function call +void scroll(unsigned int x, unsigned int y); + +// set scroll after screen split invoked by the sprite 0 hit +// warning: all CPU time between the function call and the actual split point will be wasted! +// warning: the program loop has to fit into the frame time, ppu_wait_frame should not be used +// otherwise empty frames without split will be inserted, resulting in jumpy screen +// warning: only X scroll could be changed in this version +void split(unsigned int x, unsigned int y); + + +// select current chr bank for sprites, 0..1 +void bank_spr(unsigned char n); + +// select current chr bank for background, 0..1 +void bank_bg(unsigned char n); + + + +// get random number 0..255 or 0..65535 +unsigned char rand8(void); +unsigned int rand16(void); + +// set random seed +void set_rand(unsigned int seed); + + + +// when display is enabled, vram access could only be done with this vram update system +// the function sets a pointer to the update buffer that contains data and addresses +// in a special format. It allows to write non-sequental bytes, as well as horizontal or +// vertical nametable sequences. +// buffer pointer could be changed during rendering, but it only takes effect on a new frame +// number of transferred bytes is limited by vblank time +// to disable updates, call this function with NULL pointer + +// the update data format: +// MSB, LSB, byte for a non-sequental write +// MSB|NT_UPD_HORZ, LSB, LEN, [bytes] for a horizontal sequence +// MSB|NT_UPD_VERT, LSB, LEN, [bytes] for a vertical sequence +// NT_UPD_EOF to mark end of the buffer + +// length of this data should be under 256 bytes + +inline void set_vram_update(unsigned char *buf); + +// all following vram functions only work when display is disabled + +// do a series of VRAM writes, the same format as for set_vram_update, but writes done right away +void flush_vram_update(unsigned char *buf); + +// set vram pointer to write operations if you need to write some data to vram +inline void vram_adr(unsigned int adr); + +// put a byte at current vram address, works only when rendering is turned off +inline void vram_put(unsigned char n); + +// fill a block with a byte at current vram address, works only when rendering is turned off +void vram_fill(unsigned char n, unsigned int len); + +// set vram autoincrement, 0 for +1 and not 0 for +32 +inline void vram_inc(unsigned char n); + +// read a block from current address of vram, works only when rendering is turned off +void vram_read(unsigned char *dst, unsigned int size); + +// write a block to current address of vram, works only when rendering is turned off +void vram_write(const unsigned char *src, unsigned int size); + + +// unpack RLE data to current address of vram, mostly used for nametables +void vram_unrle(const unsigned char *data); + +// unpack LZ4 data to this address +void vram_unlz4(const unsigned char *in, unsigned char *out, + const unsigned uncompressed_size); +/* + Rough speeds for a full 1024 nametable: + - rle takes 0.5 frames + - uncompressed takes 1.3 frames + - lz4 takes 2.8 frames +*/ + + +// like memset, but does not return anything +void memfill(void *dst, unsigned char value, unsigned int len); + +// delay for N frames +void delay(unsigned char frames); + +// display.sinc functions +void oam_clear_fast(void); +void oam_meta_spr_pal(unsigned char x,unsigned char y,unsigned char pal,const unsigned char *metasprite); +void oam_meta_spr_clip(signed int x,unsigned char y,const unsigned char *metasprite); + + + +#define PAD_A 0x01 +#define PAD_B 0x02 +#define PAD_SELECT 0x04 +#define PAD_START 0x08 +#define PAD_UP 0x10 +#define PAD_DOWN 0x20 +#define PAD_LEFT 0x40 +#define PAD_RIGHT 0x80 + +#define OAM_FLIP_V 0x80 +#define OAM_FLIP_H 0x40 +#define OAM_BEHIND 0x20 + +#define MAX(x1,x2) ((x1)<(x2)?(x2):(x1)) +#define MIN(x1,x2) ((x1)<(x2)?(x1):(x2)) + +#define MASK_SPR 0x10 +#define MASK_BG 0x08 +#define MASK_EDGE_SPR 0x04 +#define MASK_EDGE_BG 0x02 + +#define NAMETABLE_A 0x2000 +#define NAMETABLE_B 0x2400 +#define NAMETABLE_C 0x2800 +#define NAMETABLE_D 0x2c00 + +#define NT_UPD_HORZ 0x40 +#define NT_UPD_VERT 0x80 +#define NT_UPD_EOF 0xff + +// macro to calculate nametable address from X,Y in compile time + +#define NTADR_A(x,y) (NAMETABLE_A|(((y)<<5)|(x))) +#define NTADR_B(x,y) (NAMETABLE_B|(((y)<<5)|(x))) +#define NTADR_C(x,y) (NAMETABLE_C|(((y)<<5)|(x))) +#define NTADR_D(x,y) (NAMETABLE_D|(((y)<<5)|(x))) + +// macro to get MSB and LSB + +#define MSB(x) (((x)>>8)) +#define LSB(x) (((x)&0xff)) + +#pragma compile("neslib.c") + +#endif diff --git a/oscar64/include/new b/oscar64/include/new new file mode 100644 index 0000000..24cda01 --- /dev/null +++ b/oscar64/include/new @@ -0,0 +1,11 @@ +#pragma once + +#include "stddef.h" + +namespace std +{ + + void * operator new(size_t size); + void * operator new(void * ptr, size_t size); + +} diff --git a/oscar64/include/opp/algorithm.h b/oscar64/include/opp/algorithm.h new file mode 100644 index 0000000..a6f5a19 --- /dev/null +++ b/oscar64/include/opp/algorithm.h @@ -0,0 +1,84 @@ +#ifndef OPP_ALGORITHM_H +#define OPP_ALGORITHM_H + +#include "utility.h" +namespace opp { + +template +void sort(T s, T e) +{ + while (s != e) + { + auto p = s; + auto q = s; + + q++; + while (q != e) + { + if (LT(*q, *p)) + { + swap(*q, *p); + p++; + swap(*q, *p); + } + q++; + } + + sort(s, p); + p++; + s = p; + } +} + +template +void sort(T s, T e, LF lt) +{ + while (s != e) + { + auto p = s; + auto q = s; + + q++; + while (q != e) + { + if (lt(*q, *p)) + { + swap(*q, *p); + p++; + swap(*q, *p); + } + q++; + } + + sort(s, p, lt); + p++; + s = p; + } +} + +template +OI copy(II first, II last, OI result) +{ + while (first != last) + { + *result = *first; + ++result; ++first; + } + return result; +} + + +template +II find (II first, II last, const T& val) +{ + while (first != last) + { + if (*first == val) return first; + ++first; + } + return last; +} + +} + +#endif diff --git a/oscar64/include/opp/array.h b/oscar64/include/opp/array.h new file mode 100644 index 0000000..c851fd5 --- /dev/null +++ b/oscar64/include/opp/array.h @@ -0,0 +1,122 @@ +#ifndef OPP_ARRAY_H +#define OPP_ARRAY_H + +#include + +namespace opp { + +template +class array +{ +protected: + T _data[n]; +public: + typedef T element_type; + + size_t size(void) const + { + return n; + } + + size_t max_size(void) const + { + return n; + } + + bool empty(void) const + { + return n == 0; + } + + const T & at(size_t at) const + { + return _data[at]; + } + + T & at(size_t at) + { + return _data[at]; + } + + T & operator[] (size_t at) + { + return _data[at]; + } + + const T & operator[] (size_t at) const + { + return _data[at]; + } + + T * begin(void) + { + return _data; + } + + const T * begin(void) const + { + return _data; + } + + const T * cbegin(void) const + { + return _data; + } + + T * end(void) + { + return _data + n; + } + + const T * end(void) const + { + return _data + n; + } + + const T * cend(void) const + { + return _data + n; + } + + T & back(void) + { + return _data[n - 1]; + } + + const T & back(void) const + { + return _data[n - 1]; + } + + T & front(void) + { + return _data[0]; + } + + const T & front(void) const + { + return _data[0]; + } + + T * data(void) + { + return _data; + } + + const T * data(void) const + { + return _data; + } + + void fill(const T & t) + { + for(int i=0; i +class bindexlist +{ +public: + char _free; + char _pred[n], _succ[n]; + T _data[n]; + + class iterator + { + public: + bindexlist * _l; + char _i; + + iterator(void) : _l(nullptr) {} + iterator(bindexlist * l, char i) + : _l(l), _i(i) {} + + iterator(const iterator & li) : _l(li._l), _i(li._i) {} + iterator & operator=(const iterator & li) + { + _l = li._l; + _i = li._i; + return *this; + } + + T & operator*() + { + return _l->_data[_i]; + } + + T * operator->() + { + return _l->_data + _i; + } + + iterator & operator++(void) + { + _i = _l->_succ[_i]; + return *this; + } + + iterator operator++(int) + { + char i = _i; + _i = _l->_succ[_i]; + return bindexlist::iterator(_l, i); + } + + iterator & operator+=(char n) + { + while (n--) + _i = _l->_succ[_i]; + return *this; + } + + iterator & operator--(void) + { + _i = _l->_pred[_i]; + return *this; + } + + iterator operator++(int) + { + char i = _i; + _i = _l->_pred[_i]; + return bindexlist::iterator(_l, i); + } + + iterator & operator-=(char n) + { + while (n--) + _i = _l->_pred[_i]; + return *this; + } + + bool operator==(const iterator & li) + { + return _i == li._i; + } + + bool operator!=(const iterator & li) + { + return _i != li._i; + } + }; + +public: + typedef T element_type; + typedef iterator iterator_type; + + bindexlist(void) + { + _succ[0] = 0; + _pred[0] = 0; + for(char i=1; i + constexpr auto boundinttype(void) + { + if constexpr (tmin >= 0 && tmax <= 255) + return (char)0; + else if constexpr (tmin >= -128 && tmax <= 127) + return (signed char)0; + else + return (int)0; + } + + template + class boundint + { + protected: + decltype(boundinttype()) v; + public: + boundint(int i) + : v(i) + { + __assume(i >= tmin && i <= tmax); + } + + void operator=(int k) + { + __assume(k >= tmin && k <= tmax); + v = k; + } + + void operator+=(int k) + { + k += v; + __assume(k >= tmin && k <= tmax); + v = k; + } + + void operator-=(int k) + { + k = v - k; + __assume(k >= tmin && k <= tmax); + v = k; + } + + operator int() const + { + int k = v; + __assume(k >= tmin && k <= tmax); + return k; + } + }; + +} + +#endif diff --git a/oscar64/include/opp/functional.h b/oscar64/include/opp/functional.h new file mode 100644 index 0000000..f9aef0b --- /dev/null +++ b/oscar64/include/opp/functional.h @@ -0,0 +1,93 @@ +#ifndef OPP_FUNCTIONAL_H +#define OPP_FUNCTIONAL_H + +namespace opp +{ +template +class function; + +template +class function +{ +private: + struct callif + { + virtual R call(P...) = 0; + virtual ~callif() {} + virtual callif * clone(void) const = 0; + }; + + template + struct callable : callif + { + CA ca; + + callable(CA ca_) : ca(ca_) {} + + R call(P... p) {return ca(p...);} + + callif * clone(void) const + { + return new callable(ca); + } + }; + + callif * c; +public: + template + function(F f) + { + c = new callable(f); + } + + function(const function & f) + { + c = f.c->clone(); + } + + function(function && f) + { + c = f.c; + f.c = nullptr; + } + + function(void) + { + c = nullptr; + } + + function & operator=(const function & f) + { + if (c != f.c) + { + delete c; + c = f.c; + } + return *this; + } + + function & operator=(function && f) + { + if (c != f.c) + { + c = f.c; + f.c = nullptr; + } + return *this; + } + + ~function(void) + { + delete c; + } + + R operator()(P ... p) const + { + return c->call(p...); + } +}; + + +} + +#endif diff --git a/oscar64/include/opp/hashmap.h b/oscar64/include/opp/hashmap.h new file mode 100644 index 0000000..70462b7 --- /dev/null +++ b/oscar64/include/opp/hashmap.h @@ -0,0 +1,180 @@ +#ifndef HASHMAP_H +#define HASHMAP_H + +#include +#include + +namespace opp { + +template +struct Hash +{}; + +template<> +struct Hash { + unsigned operator()(const int & k) + { + return k; + } +}; + +template<> +struct Hash { + unsigned operator()(const string & k) + { + const char * cp = k.begin(); + if (cp) + { + unsigned hash = 0; + while (*cp) + hash = hash * 17 + *cp++; + return hash; + } + else + return 0; + } +}; + +template +struct hashpair +{ + K key; + T value; + unsigned hash; + + hashpair(void) {} + hashpair(const K & k, unsigned h) : key(k), hash(h) {} + hashpair(const hashpair & p) + : key(p.key), value(p.value), hash(p.hash) {} +}; + +template +class hashmap +{ +public: + hashmap(void); + ~hashmap(void); + + typedef hashpair N; + typedef list_iterator I; + + T & at(const K & key); + + void insert(const K & key, const T & value); + void erase(const K & key); + list_iterator > erase(list_iterator > iter); + + I begin(void); + I end(void); + I find(const K & key); + +private: + unsigned mapsize; + unsigned size; + + list list; + I * map; +}; + + +template +hashmap::hashmap(void) + : mapsize(0), size(0), map(nullptr) + {} + + +template +hashmap::~hashmap(void) +{ + delete[] map; +} + +template +hashmap::I hashmap::begin(void) +{ + return list.begin(); +} + +template +hashmap::I hashmap::end(void) +{ + return list.end(); +} + +template +T & hashmap::at(const K & key) +{ + if (mapsize == 0) + { + mapsize = 16; + map = new I[16]; + for(unsigned i=0; i<16; i++) + map[i] = list.end(); + } + + unsigned m = mapsize - 1; + unsigned h = Hash()(key); + unsigned hi = h & m; + + I p = map[hi]; + while (p != list.end() && (p->hash & m) == hi) + { + if (p->key == key) return p->value; + p++; + } + + p = list.insert(map[hi], N(key, h)); + map[hi] = p; + return p->value; +} + +template +hashmap::I hashmap::find(const K & key) +{ + if (mapsize) + { + unsigned m = mapsize - 1; + unsigned h = Hash()(key); + unsigned hi = h & m; + + I p = map[hi]; + while (p != list.end() && (p->hash & m) == hi) + { + if (p->key == key) return p; + p++; + } + } + + return list.end(); +} + + +template +void hashmap::insert(const K & key, const T & value) +{ + this->at(key) = value; +} + +template +void hashmap::erase(const K & key) +{ + erase(find(key)); +} + +template +list_iterator > hashmap::erase(list_iterator > iter) +{ + if (iter != list.end()) + { + unsigned hi = iter->hash & (mapsize - 1); + bool first = iter == map[hi]; + iter = list.erase(iter); + if (first) + map[hi] = iter; + } + return iter; +} + +} + +#endif diff --git a/oscar64/include/opp/ifstream.cpp b/oscar64/include/opp/ifstream.cpp new file mode 100644 index 0000000..d0f6c50 --- /dev/null +++ b/oscar64/include/opp/ifstream.cpp @@ -0,0 +1,24 @@ +#include "ifstream.h" +#include + +namespace opp { + +ifstream::ifstream(char fnum, char device, char channel, const string & name) +{ + this->fnum = fnum; + krnio_setnam(name.tocstr()); + krnio_open(fnum, device, channel); +} + +ifstream::~ifstream(void) +{ + krnio_close(fnum); +} + +void ifstream::refill(void) +{ + mBufferPos = 0; + mBufferFill = krnio_read(fnum, mBuffer, 32); +} + +} diff --git a/oscar64/include/opp/ifstream.h b/oscar64/include/opp/ifstream.h new file mode 100644 index 0000000..6a0394e --- /dev/null +++ b/oscar64/include/opp/ifstream.h @@ -0,0 +1,25 @@ +#ifndef OPP_IFSTREAM_H +#define OPP_IFSTREAM_H + +#include "iostream.h" +#include "string.h" + +namespace opp { + +class ifstream : public istream +{ +public: + ifstream(char fnum, char device, char channel, const string & name); + ~ifstream(void); + +protected: + virtual void refill(void); + + char fnum; +}; + +} + +#pragma compile("ifstream.cpp") + +#endif \ No newline at end of file diff --git a/oscar64/include/opp/iostream.cpp b/oscar64/include/opp/iostream.cpp new file mode 100644 index 0000000..ea12be1 --- /dev/null +++ b/oscar64/include/opp/iostream.cpp @@ -0,0 +1,1010 @@ +#include "iostream.h" +#include +#include + +namespace opp { + +ios::ios(void) + : mFlags(0), mState(0), mWidth(0), mPrecision(6), mFill(' ') +{} + +inline ios::~ios(void) +{ + +} + +char ios::fill(void) const +{ + return mFill; +} + +char ios::fill(char fillch) +{ + char c = mFill; + mFill = fillch; + return c; +} + +char ios::width() const +{ + return mWidth; +} + +char ios::width(char wide) +{ + char w = mWidth; + mWidth = wide; + return w; +} + +char ios::precision() const +{ + return mPrecision; +} + +char ios::precision(char wide) +{ + char w = mPrecision; + mPrecision = wide; + return w; +} + +ios::fmtflags ios::flags(void) const +{ + return mFlags; +} + +ios::fmtflags ios::flags(ios::fmtflags f) +{ + fmtflags pf = mFlags; + mFlags = f; + return pf; +} + +ios::fmtflags ios::setf(ios::fmtflags f) +{ + fmtflags pf = mFlags; + mFlags |= f; + return pf; +} + +ios::fmtflags ios::setf(ios::fmtflags f, ios::fmtflags m) +{ + fmtflags pf = mFlags; + mFlags = (mFlags & ~m) | f; + return pf; +} + +ios::fmtflags ios::unsetf(ios::fmtflags f) +{ + fmtflags pf = mFlags; + mFlags &= ~f; + return pf; +} + +bool ios::good(void) const +{ + return mState == goodbit; +} + +bool ios::eof(void) const +{ + return (mState & eofbit) != 0; +} + +bool ios::fail(void) const +{ + return (mState & (badbit | failbit)) != 0; +} + +bool ios::bad(void) const +{ + return (mState & badbit) != 0; +} + +bool ios::operator!(void) const +{ + return fail(); +} + +ios::operator bool(void) +{ + return !fail(); +} + + +ios::statebits ios::rdstate(void) const +{ + return mState; +} + +void ios::clear(void) +{ + mState = goodbit; +} + + + + +ostream & endl(ostream & os) +{ + os.put('\n'); + return os; +} + + +iosetf setf(ios::fmtflags flags) +{ + return iosetf(flags); +} + +ostream & operator<<(ostream & os, const iosetf & s) +{ + os.setf(s.flags); + return os; +} + +iosetw setw(char width) +{ + return iosetw(width); +} + +ostream & operator<<(ostream & os, const iosetw & s) +{ + os.width(s.width); + return os; +} + + +iosetprecision setprecision(char precision) +{ + return iosetprecision(precision); +} + +ostream & operator<<(ostream & os, const iosetprecision & s) +{ + os.precision(s.precision); + return os; +} + + +iosetfill setfill(char fill) +{ + return iosetfill(fill); +} + +ostream & operator<<(ostream & os, const iosetfill & s) +{ + os.fill(s.fill); + return os; +} + +inline ostream::ostream(void) + {} + + +void ostream::bput(char ch) +{ +} + +ostream & ostream::put(char c) +{ + bput(c); + return * this; +} + +ostream & ostream::write(const char * s, int n) +{ + for(int i=0; i prefix) + bput(buffer[--size]); + } + + if (adj != left) + { + while (r > 0) + { + bput(mFill); + r--; + } + } + + while (size > 0) + bput(buffer[--size]); + + while (r > 0) + { + bput(mFill); + r--; + } + + mFlags = 0; + mWidth = 0; + mFill = ' '; +} + +void ostream::numput(unsigned n, char sign) +{ + char buffer[10]; + + char base = 10; + if (mFlags & hex) + base = 16; + else if (mFlags & oct) + base = 8; + + char i = 0; + char o = 'a' - 10; + if (mFlags & uppercase) + o = 'A' - 10; + + unsigned nt = n; + while (nt) + { + char d = nt % base; + nt /= base; + + if (d < 10) + d += '0'; + else + d += o; + buffer[i++] = d; + } + + if (!i) + buffer[i++] = '0'; + char prefix = i; + if (sign) + buffer[i++] = sign; + + putnum(buffer, prefix, i); +} + +void ostream::numput(unsigned long n, char sign) +{ + char buffer[20]; + + char base = 10; + if (mFlags & hex) + base = 16; + else if (mFlags & oct) + base = 8; + + char i = 0; + char o = 'a' - 10; + if (mFlags & uppercase) + o = 'A' - 10; + + unsigned long nt = n; + while (nt) + { + char d = nt % base; + nt /= base; + + if (d < 10) + d += '0'; + else + d += o; + buffer[i++] = d; + } + + if (!i) + buffer[i++] = '0'; + char prefix = i; + if (sign) + buffer[i++] = sign; + + putnum(buffer, prefix, i); +} + + +ostream & ostream::operator<<(bool val) +{ + if (val) + bput('1'); + else + bput('0'); + return *this; +} + +ostream & ostream::operator<<(char val) +{ + bput(val); + return *this; +} + +ostream & ostream::operator<<(int val) +{ + if (val < 0) + numput((unsigned)-val, '-'); + else if (mFlags & showpos) + numput((unsigned)val, '+'); + else + numput((unsigned)val, 0); + return *this; +} + +ostream & ostream::operator<<(unsigned val) +{ + numput(val, 0); + return *this; +} + +ostream & ostream::operator<<(long val) +{ + if (val < 0) + numput(-val, '-'); + else if (mFlags & showpos) + numput(val, '+'); + else + numput(val, 0); + return *this; +} + +ostream & ostream::operator<<(unsigned long val) +{ + numput(val, 0); + return *this; +} + +static float fround5[] = { + 0.5e-0, 0.5e-1, 0.5e-2, 0.5e-3, 0.5e-4, 0.5e-5, 0.5e-6 +}; + +ostream & ostream::operator<<(float val) +{ + char buffer[20]; + + char d = 0; + + float f = val; + + if (f < 0.0) + { + f = -f; + buffer[d++] = '-'; + } + else if (mFlags & showpos) + buffer[d++] = '+'; + + char prefix = d; + + if (isinf(f)) + { + buffer[d++] = 'I'; + buffer[d++] = 'N'; + buffer[d++] = 'F'; + } + else + { + int exp = 0; + + char fdigits = mPrecision; + + if (f != 0.0) + { + while (f >= 1000.0) + { + f /= 1000; + exp += 3; + } + + while (f < 1.0) + { + f *= 1000; + exp -= 3; + } + + while (f >= 10.0) + { + f /= 10; + exp ++; + } + + } + + char digits = fdigits + 1; + bool fexp = mFlags & scientific; + + if (!(mFlags & fixed)) + { + if (exp > 3 || exp < 0) + fexp = true; + } + + if (!fexp) + { + while (exp < 0) + { + f /= 10.0; + exp++; + } + + digits = fdigits + exp + 1; + + if (digits < 7) + f += fround5[digits - 1]; + else + f += fround5[6]; + + if (f >= 10.0) + { + f /= 10.0; + fdigits--; + } + } + else + { + if (digits < 7) + f += fround5[digits - 1]; + else + f += fround5[6]; + + if (f >= 10.0) + { + f /= 10.0; + exp ++; + } + } + + + char pdigits = digits - fdigits; + + if (digits > 20) + digits = 20; + + if (pdigits == 0) + buffer[d++] = '0'; + + for(char i=0; i 6) + buffer[d++] = '0'; + else + { + int c = (int)f; + f -= (float)c; + f *= 10.0; + buffer[d++] = c + '0'; + } + } + + if (fexp) + { + buffer[d++] = 'E'; + if (exp < 0) + { + buffer[d++] = '-'; + exp = -exp; + } + else + buffer[d++] = '+'; + + buffer[d++] = exp / 10 + '0'; + buffer[d++] = exp % 10 + '0'; + } + } + + char r = 0; + if (d < mWidth) + r = mWidth - d; + + fmtflags adj = mFlags & adjustfield; + + char i = 0; + if (adj == internal) + { + while (i < prefix) + bput(buffer[i++]); + } + + if (adj != left) + { + while (r > 0) + { + bput(mFill); + r--; + } + } + + while (i < d) + bput(buffer[i++]); + + while (r > 0) + { + bput(mFill); + r--; + } + + mFlags = 0; + mWidth = 0; + mFill = ' '; + + return *this; +} + +ostream & ostream::operator<<(const char * p) +{ + char i = 0; + while (p[i]) + bput(p[i++]); + return *this; +} + +ostream & ostream::operator<<(const string & s) +{ + for(char i=0; i ' ') + { + unget(); + return; + } + } +} + + + +istream & istream::get(char & c) +{ + c = get(); + return *this; +} + +istream & istream::get(char * s, char size) +{ + return get(s, size, '\n'); +} + +istream & istream::get(char * s, char size, char delim) +{ + char i = 0; + while (i + 1 < size) + { + char c = get(); + if (c == delim) + { + unget(); + break; + } + s[i++] = c; + } + if (size) + s[i++] = 0; + return *this; +} + +istream & istream::getline(char * s, char size) +{ + return getline(s, size, '\n'); +} + +istream & istream::getline(char * s, char size, char delim) +{ + char i = 0; + while (i + 1 < size) + { + char c = get(); + if (c == delim) + break; + s[i++] = c; + } + if (size) + s[i++] = 0; + return *this; +} + +istream & istream::ignore(char size) +{ + ignore(size, '\n'); + return *this; +} + +istream & istream::ignore(char size, char delim) +{ + while (size) + { + char c = get(); + if (c == delim) + break; + size--; + } + + return *this; +} + +istream & istream::operator>>(bool & val) +{ + return *this; +} + +unsigned istream::getnum(void) +{ + doskipws(); + + fmtflags bflags = mFlags & basefield; + + char base = 10; + if (bflags == hex) + base = 16; + else if (bflags == oct) + base = 8; + + unsigned n = 0; + bool sign = false; + + char ch = get(); + if (ch == '-') + { + sign = true; + ch = get(); + } + else if (ch == '+') + ch = get(); + + bool digits = false; + + if (ch == '0') + { + if (bflags == 0) + { + base = 8; + ch = get(); + if (ch == 'x' || ch == 'X') + { + base = 16; + ch = get(); + } + else + digits = true; + } + else + digits = true; + } + + for(;;) + { + if (ch >= '0' && ch <= '9') + n = n * base + (ch - '0'); + else if (base > 10 && ch >= 'A' && ch <= 'F') + n = n * base + (ch - 'A' + 10); + else if (base > 10 && ch >= 'a' && ch <= 'f') + n = n * base + (ch - 'a' + 10); + else + break; + ch = get(); + digits = true; + } + unget(); + + if (!digits) + mState |= failbit; + + if (sign) + return -n; + else + return n; +} + +unsigned long istream::getnuml(void) +{ + doskipws(); + + fmtflags bflags = mFlags & basefield; + + char base = 10; + if (bflags == hex) + base = 16; + else if (bflags == oct) + base = 8; + + unsigned long n = 0; + bool sign = false; + + char ch = get(); + if (ch == '-') + { + sign = true; + ch = get(); + } + else if (ch == '+') + ch = get(); + + if (ch == '0') + { + if (bflags == 0) + { + base = 8; + ch = get(); + if (ch == 'x' || ch == 'X') + { + base = 16; + ch = get(); + } + } + } + + bool digits = false; + for(;;) + { + if (ch >= '0' && ch <= '9') + n = n * base + (ch - '0'); + else if (base > 10 && ch >= 'A' && ch <= 'F') + n = n * base + (ch - 'A' + 10); + else if (base > 10 && ch >= 'a' && ch <= 'f') + n = n * base + (ch - 'a' + 10); + else + break; + ch = get(); + digits = true; + } + unget(); + + if (!digits) + mState |= failbit; + + if (sign) + return -n; + else + return n; +} + +float istream::getnumf(void) +{ + doskipws(); + + char cs = get(); + + bool sign = false; + if (cs == '-') + { + sign = true; + cs = get(); + } + else if (cs == '+') + cs = get(); + + if (cs >= '0' && cs <= '9' || cs == '.') + { + float vf = 0; + while (cs >= '0' && cs <= '9') + { + vf = vf * 10 + (int)(cs - '0'); + cs = get(); + } + + if (cs == '.') + { + float digits = 1.0; + cs = get(); + while (cs >= '0' && cs <= '9') + { + vf = vf * 10 + (int)(cs - '0'); + digits *= 10; + cs = get(); + } + vf /= digits; + } + + char e = 0; + bool eneg = false; + + if (cs == 'e' || cs == 'E') + { + cs = get(); + if (cs == '-') + { + eneg = true; + cs = get(); + } + else if (cs == '+') + { + cs = get(); + } + + while (cs >= '0' && cs <= '9') + { + e = e * 10 + cs - '0'; + cs = get(); + } + + } + + if (e) + { + if (eneg) + { + while (e > 6) + { + vf /= 1000000.0; + e -= 6; + } + vf /= tpow10[e]; + } + else + { + while (e > 6) + { + vf *= 1000000.0; + e -= 6; + } + vf *= tpow10[e]; + } + } + + if (sign) + return -vf; + else + return vf; + } + else + mState |= failbit; + + return 0; +} + +istream & istream::operator>>(int & val) +{ + val = getnum(); + return *this; +} + +istream & istream::operator>>(unsigned & val) +{ + val = getnum(); + return *this; +} + +istream & istream::operator>>(long & val) +{ + val = getnuml(); + return *this; +} + +istream & istream::operator>>(unsigned long & val) +{ + val = getnuml(); + return *this; +} + +istream & istream::operator>>(float & val) +{ + val = getnumf(); + return *this; +} + +istream & istream::operator>>(char & val) +{ + doskipws(); + val = get(); + return *this; +} + +istream & istream::operator>>(char * p) +{ + doskipws(); + char i = 0; + char c = get(); + while (c > ' ') + { + p[i++] = c; + c = get(); + } + return *this; +} + +istream & istream::operator>>(string & s) +{ + doskipws(); + s.clear(); + char c = get(); + while (c > ' ') + { + s += c; + c = get(); + } + return *this; +} + +cistream::cistream(void) +{} + +void cistream::refill(void) +{ + mBufferPos = 0; + + char fill = 0; + while (fill < 32) + { + char ch = getchar(); + mBuffer[fill++] = ch; + if (ch == '\n') + break; + } + + mBufferFill = fill; +} + +cistream cin; +costream cout; + +} \ No newline at end of file diff --git a/oscar64/include/opp/iostream.h b/oscar64/include/opp/iostream.h new file mode 100644 index 0000000..d2b07f1 --- /dev/null +++ b/oscar64/include/opp/iostream.h @@ -0,0 +1,217 @@ +#ifndef OPP_IOSTREAM_H +#define OPP_IOSTREAM_H + +#include + +namespace opp { + +class ios +{ +public: + + constexpr ios(void); + virtual ~ios(void); + + char fill() const; + char fill(char cillch); + + char width() const; + char width(char wide); + + char precision() const; + char precision(char prec); + + enum fmtflags + { + boolalpha = 0x0001, + dec = 0x0002, + fixed = 0x0004, + hex = 0x0008, + internal = 0x0010, + left = 0x0020, + oct = 0x0040, + right = 0x0080, + scientific = 0x0100, + showbase = 0x0200, + showpoint = 0x0400, + showpos = 0x0800, + skipws = 0x1000, + unitbuf = 0x2000, + uppercase = 0x4000, + + adjustfield = 0x00b0, + basefield = 0x004a, + floatfield = 0x0104 + }; + + enum statebits + { + goodbit = 0, + badbit = 1, + eofbit = 2, + failbit = 4, + }; + + fmtflags flags(void) const; + fmtflags flags(fmtflags f); + + fmtflags setf(fmtflags f); + fmtflags setf(fmtflags f, fmtflags m); + fmtflags unsetf(fmtflags f); + + bool good(void) const; + bool eof(void) const; + bool fail(void) const; + bool bad(void) const; + + bool operator!(void) const; + operator bool(void); + + statebits rdstate(void) const; + void clear(void); + +protected: + fmtflags mFlags; + statebits mState; + char mWidth; + char mPrecision; + char mFill; +}; + +class ostream; + +typedef ostream & (* manip)(ostream &); + +class ostream : public ios +{ +public: + constexpr ostream(void); + + ostream & put(char c); + ostream & write(const char * s, int n); + + ostream & operator<<(bool val); + ostream & operator<<(char val); + ostream & operator<<(int val); + ostream & operator<<(unsigned val); + ostream & operator<<(long val); + ostream & operator<<(unsigned long val); + ostream & operator<<(float val); + + ostream & operator<<(const char * p); + ostream & operator<<(const string & s); + + ostream & operator<<(manip m); +protected: + void putnum(const char * buffer, char prefix, char size); + void numput(unsigned n, char sign); + void numput(unsigned long n, char sign); + + virtual void bput(char ch); +}; + +class istream : public ios +{ +public: + char get(void); + istream & get(char & c); + istream & get(char * s, char size); + istream & get(char * s, char size, char delim); + istream & getline(char * s, char size); + istream & getline(char * s, char size, char delim); + istream & ignore(char size); + istream & ignore(char size, char delim); + istream & putback(char c); + istream & unget(void); + + istream & operator>>(char & val); + istream & operator>>(bool & val); + istream & operator>>(int & val); + istream & operator>>(unsigned & val); + istream & operator>>(long & val); + istream & operator>>(unsigned long & val); + istream & operator>>(float & val); + + istream & operator>>(char * p); + istream & operator>>(string & s); + + istream(void); +protected: + char mBuffer[32]; + char mBufferPos, mBufferFill; + + virtual void refill(void); + + unsigned getnum(void); + unsigned long getnuml(void); + float getnumf(void); + + void doskipws(void); +}; + +class costream : public ostream +{ +public: + constexpr costream(void); + +protected: + void bput(char ch); +}; + +class cistream : public istream +{ +public: + cistream(void); + +protected: + void refill(void); +}; + +ostream & endl(ostream & os); + +struct iosetf { + ios::fmtflags flags; + iosetf(ios::fmtflags flags_) : flags(flags_) {} +}; + +ostream & operator<<(ostream & os, const iosetf & s); + +iosetf setf(ios::fmtflags flags); + +struct iosetw { + char width; + iosetw(char width_) : width(width_) {} +}; + +iosetw setw(char width); + +ostream & operator<<(ostream & os, const iosetw & s); + +struct iosetprecision { + char precision; + iosetprecision(char precision_) : precision(precision_) {} +}; + +iosetprecision setprecision(char precision); + +ostream & operator<<(ostream & os, const iosetprecision & s); + + +struct iosetfill { + char fill; + iosetfill(char fill_) : fill(fill_) {} +}; + +iosetfill setfill(char fill); + +ostream & operator<<(ostream & os, const iosetfill & s); + + +extern cistream cin; +extern costream cout; + +} + +#pragma compile("iostream.cpp"); + +#endif diff --git a/oscar64/include/opp/iterator.h b/oscar64/include/opp/iterator.h new file mode 100644 index 0000000..7c493e0 --- /dev/null +++ b/oscar64/include/opp/iterator.h @@ -0,0 +1,201 @@ +#ifndef OPP_ITERATOR_H +#define OPP_ITERATOR_H + +namespace opp +{ + +template +class back_insert_iterator +{ +protected: + CT * co; + +public: + back_insert_iterator (CT & c) : co(&c) {} + + back_insert_iterator & operator= (const CT::element_type & t) + { + co->push_back(t); + return *this; + } + + back_insert_iterator & operator= (CT::element_type && t) + { + co->push_back(t); + return *this; + } + + back_insert_iterator & operator* (void) + { + return *this; + } + + back_insert_iterator & operator++ (void) + { + return *this; + } + + back_insert_iterator operator++ (int) + { + return *this; + } +}; + +template +class front_insert_iterator +{ +protected: + CT * co; + +public: + front_insert_iterator (CT & c) : co(&c) {} + + front_insert_iterator & operator= (const CT::element_type & t) + { + co->push_front(t); + return *this; + } + + front_insert_iterator & operator= (CT::element_type && t) + { + co->push_front(t); + return *this; + } + + front_insert_iterator & operator* (void) + { + return *this; + } + + front_insert_iterator & operator++ (void) + { + return *this; + } + + front_insert_iterator operator++ (int) + { + return *this; + } +}; + +template +class insert_iterator +{ +protected: + CT * co; + CT::iterator_type ci; + +public: + insert_iterator (CT & c, const CT::iterator_type & i) : co(&c), ci(i) {} + + insert_iterator & operator= (const CT::element_type & t) + { + ci = co->insert(ci, t); ++ci; + return *this; + } + + insert_iterator & operator= (CT::element_type && t) + { + ci = co->insert(ci, t); ++ci; + return *this; + } + + insert_iterator & operator* (void) + { + return *this; + } + + insert_iterator & operator++ (void) + { + return *this; + } + + insert_iterator operator++ (int) + { + return *this; + } +}; + +template +class ostream_iterator +{ +protected: + ostream & stream; + const char * str; +public: + ostream_iterator(ostream & stream_, const char * str_) + : stream(stream_), str(str_) {} + + ostream_iterator & operator= (const T & t) + { + stream << t; + if (str) + stream << str; + return *this; + } + + ostream_iterator & operator= (T && t) + { + stream << t; + if (str) + stream << str; + return *this; + } + + ostream_iterator & operator* (void) + { + return *this; + } + + ostream_iterator & operator++ (void) + { + return *this; + } + + ostream_iterator operator++ (int) + { + return *this; + } +}; + +template +front_insert_iterator front_inserter (CT & c) +{ + return front_insert_iterator(c); +} + +template +back_insert_iterator back_inserter (CT & c) +{ + return back_insert_iterator(c); +} + +template +insert_iterator inserter (CT & c, const CT::iterator_type & i) +{ + return insert_iterator(c, i); +} + +template +CT next(CT it, int n = 1) +{ + while (n > 0) + { + it++; + n--; + } + return it; +} + +template +CT prev(CT it, int n = 1) +{ + while (n > 0) + { + it--; + n--; + } + return it; +} + +} diff --git a/oscar64/include/opp/list.h b/oscar64/include/opp/list.h new file mode 100644 index 0000000..6eadb6d --- /dev/null +++ b/oscar64/include/opp/list.h @@ -0,0 +1,370 @@ +#ifndef OPP_LIST +#define OPP_LIST + +namespace opp +{ + +template +class listhead +{ +public: + listnode * succ, * pred; + + listhead() + { + succ = (listnode *)this; + pred = (listnode *)this; + } +}; + +template +class listnode : public listhead +{ +public: + T data; + + listnode(const T & t) : data(t) {} + listnode(T && t) : data(t) {} +}; + +template +class list_iterator +{ +public: + listnode * node; +public: + list_iterator(void) : node(nullptr) {} + list_iterator(listnode * n) : node(n) {} + list_iterator(const list_iterator & li) : node(li.node) {} + list_iterator & operator=(const list_iterator & li) + { + node = li.node; + return *this; + } + + T & operator*() + { + return node->data; + } + + T * operator->() + { + return &(node->data); + } + + list_iterator & operator++(void) + { + node = node->succ; + return *this; + } + + list_iterator operator++(int) + { + listnode * n = node; + node = node->succ; + return list_iterator(n); + } + + list_iterator & operator+=(int n) + { + while (n--) + node = node->succ; + return *this; + } + + list_iterator & operator--(void) + { + node = node->pred; + return *this; + } + + list_iterator operator++(int) + { + listnode * n = node; + node = node->pred; + return list_iterator(n); + } + + list_iterator & operator-=(int n) + { + while (n--) + node = node->pred; + return *this; + } + + bool operator==(const list_iterator & li) + { + return node == li.node; + } + + bool operator!=(const list_iterator & li) + { + return node != li.node; + } + +}; + + +template +class list +{ +private: + typedef listnode ln; + listhead head; +public: + typedef T element_type; + typedef list_iterator iterator_type; + + list(void) + {} + + list(const list & l); + + list(list && l) + { + head.succ = l.head.succ; + head.pred = l.head.pred; + head.succ->pred = (listnode *)&head; + head.pred->succ = (listnode *)&head; + l.head.succ = (listnode *)&(l.head); + l.head.pred = (listnode *)&(l.head); + } + + list & operator=(const list & l); + + list & operator=(list && l) + { + head.succ = l.head.succ; + head.pred = l.head.pred; + head.succ->pred = (listnode *)&head; + head.pred->succ = (listnode *)&head; + l.head.succ = (listnode *)&(l.head); + l.head.pred = (listnode *)&(l.head); + return *this; + } + + ~list(void) + { + listnode * n = head.succ; + while (n != &head) + { + listnode * m = n->succ; + delete n; + n = m; + } + } + + list_iterator begin(void) + { + return list_iterator(head.succ); + } + + list_iterator end(void) + { + return list_iterator((listnode *)&head); + } + + T & front(void) + { + return head.succ->data; + } + + const T & front(void) const + { + return head.succ->data; + } + + T & back(void) + { + return head.pred->data; + } + + const T & back(void) const + { + return head.pred->data; + } + + list_iterator erase(list_iterator it); + + list_iterator erase(list_iterator first, list_iterator last); + + void pop_front(void); + + void pop_back(void); + + void push_front(const T & t); + + void push_front(T && t); + + void push_back(const T & t); + + void push_back(T && t); + + void clear(void); + + void append(const list & l); + + list_iterator insert(list_iterator it, const T & t); + + list_iterator insert(list_iterator it, T && t); +}; + +template +list::list(const list & l) +{ + append(l); +} + +template +list & list::operator=(const list & l) +{ + if (&l != this) + { + clear(); + append(l); + } + return *this; +} + +template +void list::pop_front(void) +{ + listnode * n = head.succ; + head.succ = n->succ; + n->succ->pred = (listnode *)&head; + delete n; +} + +template +void list::pop_back(void) +{ + listnode * n = head.pred; + head.pred = n->pred; + n->pred->succ = (listnode *)&head; + delete n; +} + +template +void list::push_front(const T & t) +{ + listnode * n = new listnode(t); + n->pred = (listnode *)&head; + n->succ = head.succ; + head.succ->pred = n; + head.succ = n; +} + +template +void list::push_front(T && t) +{ + listnode * n = new listnode(t); + n->pred = (listnode *)&head; + n->succ = head.succ; + head.succ->pred = n; + head.succ = n; +} + +template +void list::push_back(const T & t) +{ + listnode * n = new listnode(t); + n->succ = (listnode *)&head; + n->pred = head.pred; + head.pred->succ = n; + head.pred = n; +} + +template +void list::push_back(T && t) +{ + listnode * n = new listnode(t); + n->succ = (listnode *)&head; + n->pred = head.pred; + head.pred->succ = n; + head.pred = n; +} + + +template +list_iterator list::erase(list_iterator it) +{ + listnode * n = it.node; + listnode * s = n->succ; + + n->succ->pred = n->pred; + n->pred->succ = n->succ; + delete n; + + return list_iterator(s); +} + +template +list_iterator list::erase(list_iterator first, list_iterator last) +{ + listnode * n = first.node; + listnode * s = last.node; + + n->pred->succ = s; + s->pred = n->pred; + + while (n != s) + { + listnode * m = n->succ; + delete n; + n = m; + } + + return list_iterator(s); +} + +template +list_iterator list::insert(list_iterator it, const T & t) +{ + listnode * n = new listnode(t); + n->succ = it.node; + n->pred = it.node->pred; + it.node->pred->succ = n; + it.node->pred = n; + return list_iterator(n); +} + +template +list_iterator list::insert(list_iterator it, T && t) +{ + listnode * n = new listnode(t); + n->succ = it.node; + n->pred = it.node->pred; + it.node->pred->succ = n; + it.node->pred = n; + return list_iterator(n); +} + + +template +void list::clear(void) +{ + listnode * n = head.succ; + while (n != &head) + { + listnode * m = n->succ; + delete n; + n = m; + } + head.succ = (listnode *)&head; + head.pred = (listnode *)&head; +} + +template +void list::append(const list & l) +{ + listnode * n = l.head.succ; + while (n != &(l.head)) + { + push_back(n->data); + n = n->succ; + } +} + +} + +#endif diff --git a/oscar64/include/opp/numeric.h b/oscar64/include/opp/numeric.h new file mode 100644 index 0000000..aaca626 --- /dev/null +++ b/oscar64/include/opp/numeric.h @@ -0,0 +1,122 @@ +#ifndef OPP_NUMERIC_H +#define OPP_NUMERIC_H + +namespace opp { + +template +constexpr T accumulate( InputIt first, InputIt last, T init) +{ + for (; first != last; ++first) + init = opp::move(init) + *first; + return init; +} + +template +constexpr T accumulate( InputIt first, InputIt last, T init, BinaryOperator op) +{ + for (; first != last; ++first) + init = op(opp::move(init), *first); + return init; +} + +template +constexpr void iota(ForwardIt first, ForwardIt last, T value) +{ + for (; first != last; ++first, ++value) + *first = value; +} + + +template +constexpr T inner_product(InputIt1 first1, InputIt1 last1, InputIt2 first2, T init) +{ + while (first1 != last1) + { + init = opp::move(init) + (*first1) * (*first2); + ++first1; + ++first2; + } + + return init; +} + +template +constexpr T inner_product(InputIt1 first1, InputIt1 last1, InputIt2 first2, T init, BinaryOp1 op1, BinaryOp2 op2) +{ + while (first1 != last1) + { + init = op1(opp::move(init), op2(*first1, *first2)); + ++first1; + ++first2; + } + + return init; +} + +template< class InputIt, class OutputIt, class T > +OutputIt exclusive_scan( InputIt first, InputIt last, OutputIt d_first, T init ) +{ + while (first != last) + { + *d_first = init; + init = opp::move(init) + *first; + ++d_first; + ++first; + } + + return d_first; +} + +template< class InputIt, class OutputIt > +OutputIt inclusive_scan( InputIt first, InputIt last, OutputIt d_first ) +{ + if (first == last) + return d_first; + + auto sum = *first; + *d_first = sum; + + while (++first != last) + { + sum = opp::move(sum) + *first; + *++d_first = sum; + } + + return ++d_first; +} + +template< class InputIt, class OutputIt, class T, class BinaryOperator > +OutputIt exclusive_scan( InputIt first, InputIt last, OutputIt d_first, T init, BinaryOperator op ) +{ + while (first != last) + { + *d_first = init; + init = op(opp::move(init), *first); + ++d_first; + ++first; + } + + return d_first; +} + +template< class InputIt, class OutputIt, class BinaryOperator > +OutputIt inclusive_scan( InputIt first, InputIt last, OutputIt d_first, BinaryOperator op ) +{ + if (first == last) + return d_first; + + auto sum = *first; + *d_first = sum; + + while (++first != last) + { + sum = op(opp::move(sum), *first); + *++d_first = sum; + } + + return ++d_first; +} + +} + +#endif diff --git a/oscar64/include/opp/ofstream.cpp b/oscar64/include/opp/ofstream.cpp new file mode 100644 index 0000000..bbbbaaa --- /dev/null +++ b/oscar64/include/opp/ofstream.cpp @@ -0,0 +1,32 @@ +#include "ofstream.h" +#include + +namespace opp { + +ofstream::ofstream(char fnum, char device, char channel, const string & name) +{ + this->fnum = fnum; + krnio_setnam(name.tocstr()); + krnio_open(fnum, device, channel); + + mBufferFill = 0; +} + +ofstream::~ofstream(void) +{ + if (mBufferFill > 0) + krnio_write(fnum, mBuffer, mBufferFill); + krnio_close(fnum); +} + +void ofstream::bput(char ch) +{ + mBuffer[mBufferFill++] = ch; + if (mBufferFill == 32) + { + krnio_write(fnum, mBuffer, mBufferFill); + mBufferFill = 0; + } +} + +} diff --git a/oscar64/include/opp/ofstream.h b/oscar64/include/opp/ofstream.h new file mode 100644 index 0000000..6367fec --- /dev/null +++ b/oscar64/include/opp/ofstream.h @@ -0,0 +1,28 @@ +#ifndef OPP_OFSTREAM_H +#define OPP_OFSTREAM_H + +#include "iostream.h" +#include "string.h" + +namespace opp { + +class ofstream : public ostream +{ +public: + ofstream(char fnum, char device, char channel, const string & name); + ~ofstream(void); + +protected: + virtual void bput(char ch); + + char mBuffer[32]; + char mBufferFill; + + char fnum; +}; + +} + +#pragma compile("ofstream.cpp") + +#endif diff --git a/oscar64/include/opp/optional.h b/oscar64/include/opp/optional.h new file mode 100644 index 0000000..7fb5cab --- /dev/null +++ b/oscar64/include/opp/optional.h @@ -0,0 +1,112 @@ +#ifndef OPP_OPTIONAL_H +#define OPP_OPTIONAL_H + +#include + +namespace opp { + +template +class optional +{ +protected: + char _data[sizeof(T)]; + bool valid; +public: + optional(void) : valid(false) {} + optional(const T & data) : valid(true) + { + new (_data)T(data); + } + + optional(T && data) : valid(true) + { + new (_data)T(opp::move(data)); + } + + optional(const optional & o) + : valid(o.valid) + { + if (valid) + new (_data)T(*((T*)o._data)); + } + + optional(optional && o) + : valid(o.valid) + { + if (valid) + new (_data)T(opp::move(*((T*)o._data))); + } + + ~optional(void) + { + if (valid) + ((T*)_data)->~T(); + } + + optional & operator=(const optional & o) + { + if (this != &o) + { + if (valid) + ((T*)_data)->~T(); + valid = o.valid; + if (valid) + new (_data)T(*((T*)o._data)); + } + return *this; + } + + optional & operator=(optional && o) + { + if (this != &o) + { + if (valid) + ((T*)_data)->~T(); + valid = o.valid; + if (valid) + new (_data)T(opp::move(*((T*)o._data))); + } + return *this; + } + + operator bool(void) const {return valid;} + T & operator *(void) const {return *(T*)_data;} + const T & operator *(void) const {return *(T*)_data;} + + const T * operator->(void) const {return (T*)_data;} + + T * begin(void) + { + return (T*)_data; + } + + const T * begin(void) const + { + return (T*)_data; + } + + const T * cbegin(void) const + { + return (T*)_data; + } + + T * end(void) + { + return (T*)_data + (valid ? 1 : 0); + } + + const T * end(void) const + { + return (T*)_data + (valid ? 1 : 0); + } + + const T * cend(void) const + { + return (T*)_data + (valid ? 1 : 0); + } + +}; + +} + +#endif diff --git a/oscar64/include/opp/slab.h b/oscar64/include/opp/slab.h new file mode 100644 index 0000000..1a61d4d --- /dev/null +++ b/oscar64/include/opp/slab.h @@ -0,0 +1,80 @@ +#ifndef OPP_SLAB_H +#define OPP_SLAB_H + +template +class slabptr +{ +public: + char index; + + slabptr(void) + : index(N) + {} + + slabptr(char i) + : index(i) + {} + + slabptr(const slabptr & i) + : index(i.index) + {} + + auto operator-> (); + auto & operator* (); +}; + +template +class slab +{ +protected: + static __striped T buffer[N]; + static char head; + static char next[N]; + +public: + typedef slabptr ptr; + + static void init(void); + static auto alloc(void); + static void free(ptr p); +}; + + +template +inline auto slabptr::operator-> () +{ + return slab::buffer + index; +} + +template +inline auto & slabptr::operator* () +{ + return slab::buffer[index]; +} + + +template +void slab::init(void) +{ + head = 0; + for(char i=0; i +auto slab::alloc(void) +{ + char i = head; + head = next[head]; + return slabptr(i); +} + +template +void slab::free(slabptr p) +{ + next[p.index] = head; + head = p.index; +} + +#endif + diff --git a/oscar64/include/opp/span.h b/oscar64/include/opp/span.h new file mode 100644 index 0000000..cda98b7 --- /dev/null +++ b/oscar64/include/opp/span.h @@ -0,0 +1,169 @@ +#ifndef OPP_SPAN_H +#define OPP_SPAN_H + +#include +#include +#include + +namespace opp +{ +static const size_t dynamic_extent = 0xffff; + +template +class span +{ +protected: + T * _data; + size_t _size; +public: + span(void) + : _data(nullptr), _size(0) + {} + + span(T * data, size_t size) + : _data(data), _size(size) + {} + + span(T * data) + : _data(data), _size(N) + {} + + span(opp::array & arr) + : _data(arr.data()), _size(N) + {} + + span(opp::vector & vec) + : _data(vec.data()), _size(vec.size()) + {} + + span(const opp::array & arr) + : _data(arr.data()), _size(N) + {} + + span(const opp::vector & vec) + : _data(vec.data()), _size(vec.size()) + {} + + constexpr size_t size(void) const + { + if constexpr (N == dynamic_extent) + return _size; + else + return N; + } + + T & operator[](int i) + {return _data[i];} + + const T & operator[](int i) const + {return _data[i];} + + T * begin(void) + { + return _data; + } + + const T * begin(void) const + { + return _data; + } + + const T * cbegin(void) const + { + return _data; + } + + T * end(void) + { + return _data + size(); + } + + const T * end(void) const + { + return _data + size(); + } + + const T * cend(void) const + { + return _data + size(); + } + + T & back(void) + { + return _data[size() - 1]; + } + + const T & back(void) const + { + return _data[size() - 1]; + } + + T & front(void) + { + return _data[0]; + } + + const T & front(void) const + { + return _data[0]; + } + + T * data(void) + { + return _data; + } + + const T * data(void) const + { + return _data; + } + + template< int Offset, int Count = dynamic_extent > + constexpr auto subspan() const + { + if constexpr (Count == dynamic_extent) + { + if constexpr (N == dynamic_extent) + return opp::span(_data + Offset, _size - Offset); + else + return opp::span(_data + Offset); + } + else + return opp::span(_data + Offset); + } + + constexpr auto subspan(size_t offset, size_t count = dynamic_extent) + { + if (count == dynamic_extent) + return opp::span(_data + offset, size() - offset); + else + return opp::span(_data + offset, count); + } + + template< int Count > + constexpr auto first() const + { + return opp::span(_data); + } + + constexpr auto first( size_t count ) const + { + return opp::span(_data, count); + } + + template< int Count > + constexpr auto last() const + { + return opp::span(_data + size() - Count); + } + + constexpr auto last( size_t count ) const + { + return opp::span(_data + size() - count, count); + } +}; + +} + + +#endif diff --git a/oscar64/include/opp/sstream.cpp b/oscar64/include/opp/sstream.cpp new file mode 100644 index 0000000..51aceb6 --- /dev/null +++ b/oscar64/include/opp/sstream.cpp @@ -0,0 +1,86 @@ +#include "sstream.h" +#include + +namespace opp { + +ostringstream::ostringstream(void) +{ + mBuffer = nullptr; + mBFill = mBSize = 0; +} + +ostringstream::~ostringstream(void) +{ + free(mBuffer); +} + +void ostringstream::bput(char ch) +{ + if (!mBuffer) + { + mBSize = 15; + mBFill = 0; + mBuffer = (char *)malloc(15); + } + else if (mBFill == mBSize) + { + mBSize *= 2; + char * b = (char *)malloc(mBSize); + for(char i=0; i mBSize) + { + free(mBuffer); + mBSize = mBFill; + mBuffer = (char *)malloc(mBSize); + } + str.copyseg(mBuffer, 0, mBFill); +} + +istringstream::istringstream(const string & str) + : mString(str), mSPos(0) +{} + +istringstream::~istringstream(void) +{} + +string istringstream::str(void) const +{ + return mString; +} + +void istringstream::str(const string & str) +{ + mState = goodbit; + mString = str; + mSPos = 0; +} + + +void istringstream::refill(void) +{ + mBufferFill = 0; + mBufferPos = 0; + + char ch; + while (mSPos < mString.size() && mBufferFill < 32) + { + mBuffer[mBufferFill++] = mString[mSPos++]; + } +} + +} diff --git a/oscar64/include/opp/sstream.h b/oscar64/include/opp/sstream.h new file mode 100644 index 0000000..8a0b4a7 --- /dev/null +++ b/oscar64/include/opp/sstream.h @@ -0,0 +1,42 @@ +#ifndef OPP_SSTREAM_H +#define OPP_SSTREAM_H + +#include "iostream.h" + +namespace opp { + +class ostringstream : public ostream +{ +public: + ostringstream(void); + ~ostringstream(void); + + string str(void) const; + void str(const string & str); +protected: + void bput(char ch); + + char * mBuffer; + char mBFill, mBSize; +}; + +class istringstream : public istream +{ +public: + istringstream(const string & str); + ~istringstream(void); + + string str(void) const; + void str(const string & str); +protected: + virtual void refill(void); + + string mString; + char mSPos; +}; + +} + +#pragma compile("sstream.cpp") + +#endif diff --git a/oscar64/include/opp/static_vector.h b/oscar64/include/opp/static_vector.h new file mode 100644 index 0000000..0b2ccc3 --- /dev/null +++ b/oscar64/include/opp/static_vector.h @@ -0,0 +1,308 @@ +#ifndef OPP_STATIC_VECTOR_H +#define OPP_STATIC_VECTOR_H + +#include +#include +#include +#include + +namespace opp { + +template +class static_vector +{ +protected: + enum { m = N } _size; + char _space[N * sizeof(T)]; +public: + typedef T element_type; + + static_vector(void) : _size(0) {} + + static_vector(size_t n) : _size(n) + { +#ifdef CAPACITYCHECK + if (n > N) debugcrash(); +#endif + T * data = (T*)_space; + for(size_t i=0; i + void emplace_back(const P&... p); +}; + + +template +void static_vector::clear(void) +{ + T * data = (T*)_space; + for(size_t i=0; i<_size; i++) + data[i].~T(); + _size = 0; +} + +template +void static_vector::resize(size_t n) +{ +#ifdef CAPACITYCHECK + if (n > N) debugcrash(); +#endif + T * data = (T*)_space; + if (n < _size) + { + for(size_t i=n; i<_size; i++) + data[i].~T(); + } + else if (n > 0) + { + for(size_t i=_size; i +void static_vector::push_back(const T & t) +{ +#ifdef CAPACITYCHECK + if (_size >= N) debugcrash(); +#endif + new ((T*)_space + _size++)T(t); +} + +template +void static_vector::push_back(T && t) +{ +#ifdef CAPACITYCHECK + if (_size >= N) debugcrash(); +#endif + new ((T*)_space + _size++)T(t); +} + +template +template +void static_vector::emplace_back(const P&... p) +{ +#ifdef CAPACITYCHECK + if (_size >= N) debugcrash(); +#endif + new ((T*)_space + _size++)T(p...); +} + +template +void static_vector::assign(size_t count, const T & t) +{ + T * data = (T*)_space; + for(size_t i=0; i<_size; i++) + data[i].~T(); + for(size_t i=0; i +void static_vector::insert(size_t at, const T & t) +{ + T * data = (T*)_space; + new (data + _size)T(); + for(size_t i=_size; i>at; i--) + data[i] = move(data[i - 1]); + data[at] = t; + _size++; +} + +template +void static_vector::erase(size_t at, size_t n) +{ + T * data = (T*)_space; + _size -= n; + for(size_t i=at; i<_size; i++) + data[i] = move(data[i + n]); + for(size_t i=0; i +T * static_vector::insert(T * at, const T & t) +{ +#ifdef CAPACITYCHECK + if (_size >= N) debugcrash(); +#endif + T * data = (T*)_space; + T * dp = data + _size; + new (dp)T(); + while (dp != at) + { + dp--; + dp[1] = move(dp[0]); + } + dp[0] = t; + _size++; + return dp + 1; +} + +} +#endif + diff --git a/oscar64/include/opp/string.cpp b/oscar64/include/opp/string.cpp new file mode 100644 index 0000000..7271090 --- /dev/null +++ b/oscar64/include/opp/string.cpp @@ -0,0 +1,1136 @@ +#include "string.h" +#include +#include +#include + +namespace opp { + +static inline void smemcpy(char * dp, const char * sp, char s) +{ + for(char i=0; itocstr(); +} + +inline const char * string::tocstr(void) const +{ + if (cstr) + return cstr + 1; + else + return ""; +} + +inline unsigned string::size(void) const +{ + if (cstr) + return cstr[0]; + else + return 0; +} + +string string::operator+(const string & s) const +{ + if (cstr) + { + if (s.cstr) + { + char l = cstr[0] + s.cstr[0]; + char * p = (char *)malloc(char(l + 2)); + smemcpy(p + 1, cstr + 1, cstr[0]); + smemcpy(p + 1 + cstr[0], s.cstr + 1, s.cstr[0]); + return string(l, p); + } + else + return *this; + } + else + return s; +} + +string string::operator+(const char * s) const +{ + if (cstr) + { + if (s) + { + char sl = sstrlen(s); + if (sl) + { + char l = cstr[0] + sl; + char * p = (char *)malloc(char(l + 2)); + smemcpy(p + 1, cstr + 1, cstr[0]); + smemcpy(p + 1 + cstr[0], s, sl); + return string(l, p); + } + else + return *this; + } + else + return *this; + } + else + return string(s); +} + +string string::operator+(char c) const +{ + if (cstr) + { + char l = cstr[0] + 1; + char * p = (char *)malloc(char(l + 2)); + smemcpy(p + 1, cstr + 1, cstr[0]); + p[l] = c; + return string(l, p); + } + else + return string(c); +} + +string & string::operator<<=(char n) +{ + if (n > 0 && cstr) + { + if (n >= cstr[0]) + { + free(cstr); + cstr = nullptr; + } + else + { + char i = 1; + n++; + while (cstr[n]) + cstr[i++] = cstr[n++]; + cstr[i] = 0; + cstr[0] = i - 1; + } + } + return *this; +} + +string & string::operator>>=(char n) +{ + if (n > 0 && cstr) + { + if (n >= cstr[0]) + { + free(cstr); + cstr = nullptr; + } + else + { + cstr[0] -= n; + cstr[n + 1] = 0; + } + } + return *this; +} + +string string::operator<<(char n) const +{ + if (n > 0 && cstr) + { + if (n >= cstr[0]) + return string(); + else + { + + } + } + else + return *this; +} + +string string::operator>>(char n) const +{ + +} + +static int scmp(const char * s1, const char * s2) +{ + char n = 0; + while (s2[n]) + { + if (s1[n] != s2[n]) + return s1[n] - s2[n]; + n++; + } + return s1[n]; +} + +inline bool string::operator==(const string & s) const +{ + if (cstr && s.cstr) + return scmp(cstr + 1, s.cstr + 1) == 0; + else + return !cstr && !s.cstr; +} + +inline bool string::operator==(const char * s) const +{ + if (cstr) + return scmp(cstr + 1, s) == 0; + else + return s[0] == 0; +} + +inline bool string::operator!=(const string & s) const +{ + if (cstr && s.cstr) + return scmp(cstr + 1, s.cstr + 1) != 0; + else + return cstr || s.cstr; +} + +inline bool string::operator!=(const char * s) const +{ + if (cstr) + return scmp(cstr + 1, s) == 0; + else + return s[0] != 0; +} + +inline bool string::operator<(const string & s) const +{ + if (cstr && s.cstr) + return scmp(cstr + 1, s.cstr + 1) < 0; + else + return s.cstr; +} + +inline bool string::operator<(const char * s) const +{ + if (cstr) + return scmp(cstr + 1, s) < 0; + else + return s[0] != 0; +} + +inline bool string::operator<=(const string & s) const +{ + if (cstr && s.cstr) + return scmp(cstr + 1, s.cstr + 1) <= 0; + else + return !cstr || s.cstr; +} + +inline bool string::operator<=(const char * s) const +{ + if (cstr) + return scmp(cstr + 1, s) <= 0; + else + return true; +} + +inline bool string::operator>(const string & s) const +{ + if (cstr && s.cstr) + return scmp(cstr + 1, s.cstr + 1) > 0; + else + return cstr; +} + +inline bool string::operator>(const char * s) const +{ + if (cstr) + return scmp(cstr + 1, s) > 0; + else + return false; +} + +inline bool string::operator>=(const string & s) const +{ + if (cstr && s.cstr) + return scmp(cstr + 1, s.cstr + 1) >= 0; + else + return cstr || !s.cstr; +} + +inline bool string::operator>=(const char * s) const +{ + if (cstr) + return scmp(cstr + 1, s) >= 0; + else + return s[0] == 0; +} + +inline char & string::operator[](char t) +{ + return cstr[t + 1]; +} + +inline char string::operator[](char t) const +{ + return cstr[t + 1]; +} + +char * string::begin(void) +{ + return cstr ? cstr + 1 : nullptr; +} + +const char * string::begin(void) const +{ + return cstr ? cstr + 1 : nullptr; +} + +const char * string::cbegin(void) const +{ + return cstr ? cstr + 1 : nullptr; +} + +char * string::end(void) +{ + return cstr ? cstr + 1 + cstr[0] : nullptr; +} + +const char * string::end(void) const +{ + return cstr ? cstr + 1 + cstr[0] : nullptr; +} + +const char * string::cend(void) const +{ + return cstr ? cstr + 1 + cstr[0] : nullptr; +} + + +string string::substr(char pos, char len) const +{ + if (!cstr || len == 0 || pos >= cstr[0]) + return string(); + else + { + char l = cstr[0]; + if (pos + len > l) + len = l - pos; + + char * p = (char *)malloc(len + 2); + memcpy(p + 1, cstr + 1 + pos, len); + return string(len, p); + } +} + +inline int string::find(const string & s) const +{ + return find(s, 0); +} + +inline int string::find(const char * s) const +{ + return find(s, 0); +} + +inline int string::find(char c) const +{ + return find(c, 0); +} + +int string::find(const string & s, char pos) const +{ + if (!s.cstr) + return pos; + if (cstr) + { + char l = cstr[0]; + char sl = s.cstr[0]; + if (sl <= l) + { + l -= sl; + + while (pos <= l) + { + char i = 1; + while (s.cstr[i] && s.cstr[i] == cstr[pos + i]) + i++; + if (!s.cstr[i]) + return pos; + pos++; + } + } + } + + return -1; +} + +int string::find(const char * s, char pos) const +{ + if (cstr) + { + char l = cstr[0]; + char sl = sstrlen(s); + if (sl <= l) + { + l -= sl; + + while (pos <= l) + { + char i = 0; + while (s[i] && s[i] == cstr[pos + i + 1]) + i++; + if (!s[i]) + return pos; + pos++; + } + } + } + + return -1; +} + +int string::find(char c, char pos) const +{ + if (cstr) + { + char l = cstr[0]; + while (pos < l) + { + if (cstr[pos + 1] == c) + return pos; + pos++; + } + } + + return -1; +} + +string to_string(int val) +{ + char buffer[10]; + + bool sign = false; + if (val < 0) + { + val = -val; + sign = true; + } + + char i = 10; + + unsigned u = val; + while (u) + { + char d = u % 10; + u /= 10; + buffer[--i] = d + '0'; + } + + if (i == 10) + buffer[--i] = '0'; + if (sign) + buffer[--i] = '-'; + + return string(buffer + i, 10 - i); +} + +string to_string(long val) +{ + char buffer[12]; + + bool sign = false; + if (val < 0) + { + val = -val; + sign = true; + } + + char i = 12; + + unsigned long u = val; + while (u) + { + char d = u % 10; + u /= 10; + buffer[--i] = d + '0'; + } + + if (i == 12) + buffer[--i] = '0'; + if (sign) + buffer[--i] = '-'; + + return string(buffer + i, 12 - i); +} + +string to_string(unsigned int val) +{ + char buffer[10]; + + char i = 10; + + while (val) + { + char d = val % 10; + val /= 10; + buffer[--i] = d + '0'; + } + + if (i == 10) + buffer[--i] = '0'; + + return string(buffer + i, 10 - i); +} + +string to_string(unsigned long val) +{ + char buffer[12]; + + char i = 12; + + while (val) + { + char d = val % 10; + val /= 10; + buffer[--i] = d + '0'; + } + + if (i == 12) + buffer[--i] = '0'; + + return string(buffer + i, 12 - i); +} + +static float fround5[] = { + 0.5e-0, 0.5e-1, 0.5e-2, 0.5e-3, 0.5e-4, 0.5e-5, 0.5e-6 +}; + +string to_string(float val) +{ + char buffer[20]; + + char d = 0; + + float f = val; + + if (f < 0.0) + { + f = -f; + buffer[d++] = '-'; + } + + char prefix = d; + + if (isinf(f)) + { + buffer[d++] = 'I'; + buffer[d++] = 'N'; + buffer[d++] = 'F'; + } + else + { + int exp = 0; + + char fdigits = 6; + + if (f != 0.0) + { + while (f >= 1000.0) + { + f /= 1000; + exp += 3; + } + + while (f < 1.0) + { + f *= 1000; + exp -= 3; + } + + while (f >= 10.0) + { + f /= 10; + exp ++; + } + + } + + char digits = fdigits + 1; + + while (exp < 0) + { + f /= 10.0; + exp++; + } + + digits = fdigits + exp + 1; + + if (digits < 7) + f += fround5[digits - 1]; + else + f += fround5[6]; + + if (f >= 10.0) + { + f /= 10.0; + fdigits--; + } + + char pdigits = digits - fdigits; + + if (digits > 20) + digits = 20; + + if (pdigits == 0) + buffer[d++] = '0'; + + for(char i=0; i 6) + buffer[d++] = '0'; + else + { + int c = (int)f; + f -= (float)c; + f *= 10.0; + buffer[d++] = c + '0'; + } + } + } + + return string(buffer, d); +} + + +int string::to_int(char * idx, char base) const +{ + char i = 1; + unsigned n = 0; + bool sign = false; + + if (cstr) + { + const char * cp = cstr; + + char ch = cp[i]; + + if (ch == '-') + { + sign = true; + ch = cp[++i]; + } + else if (ch == '+') + ch = cp[++i]; + + if (ch == '0' && base == 0) + { + ch = cp[++i]; + if (ch == 'x' || ch == 'X') + { + base = 16; + ch = cp[++i]; + } + else if (ch == 'b' || ch == 'B') + { + base = 2; + ch = cp[++i]; + } + } + + for(;;) + { + char d; + if (ch >= '0' && ch <= '9') + d = (ch - '0'); + else if (base > 10 && ch >= 'A' && ch <= 'F') + d = (ch - 'A' + 10); + else if (base > 10 && ch >= 'a' && ch <= 'f') + d = (ch - 'a' + 10); + else + break; + + n = n * base + d; + ch = cp[++i]; + } + } + + if (idx) + *idx = i - 1; + + if (sign) + return -(int)n; + else + return n; +} + +long string::to_long(char * idx, char base) const +{ + char i = 1; + unsigned long n = 0; + bool sign = false; + + if (cstr) + { + const char * cp = cstr; + + char ch = cp[i++]; + + if (ch == '-') + { + sign = true; + ch = cp[i++]; + } + else if (ch == '+') + ch = cp[i++]; + + if (ch == '0' && base == 0) + { + ch = cp[i++]; + if (ch == 'x' || ch == 'X') + { + base = 16; + ch = cp[i++]; + } + else if (ch == 'b' || ch == 'B') + { + base = 2; + ch = cp[i++]; + } + } + + for(;;) + { + char d; + if (ch >= '0' && ch <= '9') + d = (ch - '0'); + else if (base > 10 && ch >= 'A' && ch <= 'F') + d = (ch - 'A' + 10); + else if (base > 10 && ch >= 'a' && ch <= 'f') + d = (ch - 'a' + 10); + else + break; + + n = n * base + d; + ch = cp[i++]; + } + i--; + } + + if (idx) + *idx = i - 1; + + if (sign) + return -(long)n; + else + return n; +} + +unsigned string::to_uint(char * idx, char base) const +{ + char i = 1; + unsigned n = 0; + + if (cstr) + { + const char * cp = cstr; + + char ch = cp[i]; + + if (ch == '0' && base == 0) + { + ch = cp[++i]; + if (ch == 'x' || ch == 'X') + { + base = 16; + ch = cp[++i]; + } + else if (ch == 'b' || ch == 'B') + { + base = 2; + ch = cp[++i]; + } + } + + for(;;) + { + char d; + if (ch >= '0' && ch <= '9') + d = (ch - '0'); + else if (base > 10 && ch >= 'A' && ch <= 'F') + d = (ch - 'A' + 10); + else if (base > 10 && ch >= 'a' && ch <= 'f') + d = (ch - 'a' + 10); + else + break; + + n = n * base + d; + ch = cp[++i]; + } + } + + if (idx) + *idx = i - 1; + + return n; +} + +unsigned long string::to_ulong(char * idx, char base) const +{ + char i = 1; + unsigned long n = 0; + + if (cstr) + { + const char * cp = cstr; + + char ch = cp[i++]; + + if (ch == '0' && base == 0) + { + ch = cp[i++]; + if (ch == 'x' || ch == 'X') + { + base = 16; + ch = cp[i++]; + } + else if (ch == 'b' || ch == 'B') + { + base = 2; + ch = cp[i++]; + } + } + + for(;;) + { + char d; + if (ch >= '0' && ch <= '9') + d = (ch - '0'); + else if (base > 10 && ch >= 'A' && ch <= 'F') + d = (ch - 'A' + 10); + else if (base > 10 && ch >= 'a' && ch <= 'f') + d = (ch - 'a' + 10); + else + break; + + n = n * base + d; + ch = cp[i++]; + } + i--; + } + + if (idx) + *idx = i - 1; + + return n; +} + +float string::to_float(char * idx) const +{ + char i = 1; + float vf = 0; + bool sign = false; + + if (cstr) + { + const char * cp = cstr; + + char ch = cp[i++]; + if (ch == '-') + { + sign = true; + ch = cp[i++]; + } + else if (ch == '+') + ch = cp[i++]; + + if (ch >= '0' && ch <= '9' || ch == '.') + { + while (ch >= '0' && ch <= '9') + { + vf = vf * 10 + (int)(ch - '0'); + ch = cp[i++]; + } + + if (ch == '.') + { + float digits = 1.0; + ch = cp[i++]; + while (ch >= '0' && ch <= '9') + { + vf = vf * 10 + (int)(ch - '0'); + digits *= 10; + ch = cp[i++]; + } + vf /= digits; + } + + char e = 0; + bool eneg = false; + + if (ch == 'e' || ch == 'E') + { + ch = cp[i++]; + if (ch == '-') + { + eneg = true; + ch = cp[i++]; + } + else if (ch == '+') + { + ch = cp[i++]; + } + + while (ch >= '0' && ch <= '9') + { + e = e * 10 + ch - '0'; + ch = cp[i++]; + } + + } + + if (e) + { + if (eneg) + { + while (e > 6) + { + vf /= 1000000.0; + e -= 6; + } + vf /= tpow10[e]; + } + else + { + while (e > 6) + { + vf *= 1000000.0; + e -= 6; + } + vf *= tpow10[e]; + } + } + } + i--; + } + + if (idx) + *idx = i - 1; + + if (sign) + return -vf; + else + return vf; +} + +} diff --git a/oscar64/include/opp/string.h b/oscar64/include/opp/string.h new file mode 100644 index 0000000..b62ada3 --- /dev/null +++ b/oscar64/include/opp/string.h @@ -0,0 +1,110 @@ +#ifndef OPP_STRING_H +#define OPP_STRING_H + +namespace opp { + +class string +{ +private: + char * cstr; + + friend void swap(string & u, string & v); + +public: + string(void); + string(const string & s); + string(string && s); + string(const char * s); + string(const char * s, char size); + string(char c); + ~string(void); + + unsigned size(void) const; + + void clear(void); + + string & operator=(const string & s); + string & operator=(string && s); + string & operator=(const char * s); + + string & operator+=(const string & s); + string & operator+=(const char * s); + string & operator+=(char c); + + string operator+(const string & s) const; + string operator+(const char * s) const; + string operator+(char c) const; + + string & operator<<=(char n); + string & operator>>=(char n); + + string operator<<(char n) const; + string operator>>(char n) const; + + bool operator==(const string & s) const; + bool operator==(const char * s) const; + bool operator!=(const string & s) const; + bool operator!=(const char * s) const; + + bool operator<(const string & s) const; + bool operator<(const char * s) const; + bool operator<=(const string & s) const; + bool operator<=(const char * s) const; + + bool operator>(const string & s) const; + bool operator>(const char * s) const; + bool operator>=(const string & s) const; + bool operator>=(const char * s) const; + + char & operator[](char t); + char operator[](char t) const; + + char * begin(void); + const char * begin(void) const; + const char * cbegin(void) const; + + char * end(void); + const char * end(void) const; + const char * cend(void) const; + + const char * c_str(void) const; + const char * tocstr(void) const; + + string substr(char pos, char len) const; + + int find(const string & s) const; + int find(const char * s) const; + int find(char c) const; + + int find(const string & s, char pos) const; + int find(const char * s, char pos) const; + int find(char c, char pos) const; + + void copyseg(char * p, char at, char num) const; + + int to_int(char * idx = nullptr, char base = 10) const; + long to_long(char * idx = nullptr, char base = 10) const; + unsigned to_uint(char * idx = nullptr, char base = 10) const; + unsigned long to_ulong(char * idx = nullptr, char base = 10) const; + float to_float(char * idx = nullptr) const; +protected: + string(char l, char * b); +}; + +void swap(string & u, string & v); + +string to_string(int val); + +string to_string(long val); + +string to_string(unsigned int val); + +string to_string(unsigned long val); + +string to_string(float val); + +} + +#pragma compile("string.cpp") + +#endif diff --git a/oscar64/include/opp/utility.h b/oscar64/include/opp/utility.h new file mode 100644 index 0000000..a9c1bc9 --- /dev/null +++ b/oscar64/include/opp/utility.h @@ -0,0 +1,40 @@ +#ifndef OPP_UTILITY_H +#define OPP_UTILITY_H + +namespace opp { + +template +inline T && move(T && m) +{ + return (T &&)m; +} + +template +inline void swap(T & x, T & y) +{ + T t(x); x = y; y = move(t); +} + + +template +struct pair +{ + T1 first; + T2 second; + + pair(T1 && t1, T2 && t2) + : first(t1), second(t2) + {} +}; + +template +constexpr pair make_pair(T1 && t1, T2 && t2) +{ + return pair(t1, t2); +} + +} + + + +#endif diff --git a/oscar64/include/opp/vector.h b/oscar64/include/opp/vector.h new file mode 100644 index 0000000..ed0dc29 --- /dev/null +++ b/oscar64/include/opp/vector.h @@ -0,0 +1,364 @@ +#ifndef OPP_VECTOR_H +#define OPP_VECTOR_H + +#include +#include +#include + +namespace opp { + +template +class vector +{ +protected: + T * _data; + size_t _size, _capacity; +public: + typedef T element_type; + + vector(void) : _data(nullptr), _size(0), _capacity(0) {} + + vector(size_t n) : _data((T*)malloc(n * sizeof(T))), _size(n), _capacity(n) + { + for(size_t i=0; i + void emplace_back(const P&... p); +protected: + T * add_back(void); +}; + + +template +__noinline void vector::reserve(size_t n) +{ + if (n > _capacity) + { + _capacity = n; + T * d = (T *)malloc(_capacity * sizeof(T)); + size_t s = _size; + for(size_t i=0; i +void vector::clear(void) +{ + for(size_t i=0; i<_size; i++) + _data[i].~T(); + _size = 0; +} + +template +__noinline void vector::resize(size_t n) +{ + if (n < _size) + { + for(size_t i=n; i<_size; i++) + _data[i].~T(); + _size = n; + } + else if (n < _capacity) + { + for(size_t i=_size; i +void vector::shrink_to_fit(void) +{ + if (_size < _capacity) + { + _capacity = _size; + T * d = (T *)malloc(_capacity * sizeof(T)); + for(size_t i=0; i<_size; i++) + { + new (d + i)T(move(_data[i])); + _data[i].~T(); + } + free(_data); + _data = d; + } +} + +template +T * vector::add_back(void) +{ + if (_size == _capacity) + reserve(_size + 1 + (_size >> 1)); + return _data + _size++; +} + +template +void vector::push_back(const T & t) +{ + new (add_back())T(t); +} + +template +void vector::push_back(T && t) +{ + new (add_back())T(t); +} + +template +template +void vector::emplace_back(const P&... p) +{ + new (add_back())T(p...); +} + +template +void vector::assign(size_t count, const T & t) +{ + for(size_t i=0; i<_size; i++) + _data[i].~T(); + if (count > _capacity) + { + _size = 0; + reserve(count); + } + for(size_t i=0; i +void vector::insert(size_t at, const T & t) +{ + if (_size == _capacity) + reserve(_size + 1 + (_size >> 1)); + new (_data + _size)T(); + for(size_t i=_size; i>at; i--) + _data[i] = move(_data[i - 1]); + _data[at] = t; + _size++; +} + +template +void vector::erase(size_t at, size_t n) +{ + _size -= n; + for(size_t i=at; i<_size; i++) + _data[i] = move(_data[i + n]); + for(size_t i=0; i +T * vector::insert(T * at, const T & t) +{ + if (_size == _capacity) + { + unsigned f = unsigned(at) - unsigned(_data); + reserve(_size + 1 + (_size >> 1)); + at = (T *)(f + unsigned(_data)); + } + T * dp = _data + _size; + new (dp)T(); + while (dp != at) + { + dp--; + dp[1] = move(dp[0]); + } + dp[0] = t; + _size++; + return dp + 1; +} + +} +#endif diff --git a/oscar64/include/oscar.c b/oscar64/include/oscar.c new file mode 100644 index 0000000..939328c --- /dev/null +++ b/oscar64/include/oscar.c @@ -0,0 +1,138 @@ +#include "oscar.h" + +const char * oscar_expand_lzo(char * dp, const char * sp) +{ + char cmd = sp[0]; + do + { + const char * cp; + + if (cmd & 0x80) + { + cp = dp - sp[1]; + cmd &= 0x7f; + sp += 2; + } + else + { + sp += 1; + cp = sp; + sp += cmd; + } + + char n = 0x00; + do { + dp[n] = cp[n]; + n++; + } while (n != cmd); + dp += cmd; + + cmd = sp[0]; + } while (cmd); + + return sp + 1; +} + +const char * oscar_expand_rle(char * dp, const char * sp) +{ + char cmd = sp[0]; + + do + { + if (cmd & 0x80) + { + char rep = (cmd & 0x70) >> 4; + char c = sp[1]; + for(signed char i=rep; i>=0; i--) + dp[i] = c; + + rep++; + sp += 2; + dp += rep; + + cmd &= 0x0f; + for(signed char i=cmd; i>=0; i--) + dp[i] = sp[i]; + + cmd++; + sp += cmd; + dp += cmd; + } + else if (cmd & 0x40) + { + cmd &= 0x3f; + sp ++; + for(signed char i=cmd; i>=0; i--) + dp[i] = sp[i]; + + cmd++; + sp += cmd; + dp += cmd; + } + else + { + char c = sp[1]; + for(signed char i=cmd; i>=0; i--) + dp[i] = c; + + cmd++; + sp += 2; + dp += cmd; + } + + cmd = sp[0]; + + } while (cmd); + + return sp + 1; +} + +__native const char * oscar_expand_lzo_buf(char * dp, const char * sp) +{ + char buf[256]; + char b = 0; + + char cmd = sp[0]; + do + { + if (cmd & 0x80) + { + char i = b - sp[1]; + cmd &= 0x7f; + sp += 2; + + char n = 0; + do { + buf[b] = dp[n] = buf[i]; + b++; + i++; + n++; + } while (n != cmd); + } + else + { + sp += 1; + + char n = 0; + do { + buf[b] = dp[n] = sp[n]; + b++; + n++; + } while (n != cmd); + + sp += cmd; + } + dp += cmd; + + cmd = sp[0]; + } while (cmd); + + return sp + 1; +} + +void debugcrash(void) +{ + __asm volatile { + byt $02 + } +} diff --git a/oscar64/include/oscar.h b/oscar64/include/oscar.h new file mode 100644 index 0000000..ca1b03a --- /dev/null +++ b/oscar64/include/oscar.h @@ -0,0 +1,25 @@ +#ifndef OSCAR_H +#define OSCAR_H + + +__native const char * oscar_expand_lzo(char * dp, const char * sp); + +__native const char * oscar_expand_rle(char * dp, const char * sp); + +// Same as oscar_expand_lzo, but does not need read access to the +// target memory area. On the downside, it uses 256 bytes of stack +// memory as buffer +__native const char * oscar_expand_lzo_buf(char * dp, const char * sp); + +// Write a breakpoint instruction into the .lbl file for vice. This +// intrinsic function will change the behaviour of the optimizer to ensure +// that the breakpoint is not move around into wild places. +void breakpoint(void); + +#pragma intrinsic(breakpoint) + +void debugcrash(void); + +#pragma compile("oscar.c") + +#endif diff --git a/oscar64/include/petscii.h b/oscar64/include/petscii.h new file mode 100644 index 0000000..fd3d317 --- /dev/null +++ b/oscar64/include/petscii.h @@ -0,0 +1,5 @@ +/* petscii upper charset */ + +#pragma charmap(97, 65, 26) +#pragma charmap(65, 97, 26) + diff --git a/oscar64/include/plus4/ted.c b/oscar64/include/plus4/ted.c new file mode 100644 index 0000000..44c8153 --- /dev/null +++ b/oscar64/include/plus4/ted.c @@ -0,0 +1,73 @@ +#include "ted.h" + +void ted_waitBottom(void) +{ + while (!(ted.vscan_high & 1)) + ; +} + +void ted_waitTop(void) +{ + while (ted.vscan_high & 1) + ; +} + +void ted_waitFrame(void) +{ + while (ted.vscan_high & 1) + ; + while (!(ted.vscan_high & 1)) + ; +} + +void ted_waitLine(int line) +{ + char upper = (char)(line >> 8) & 1; + char lower = (char)line; + + do + { + while (ted.vscan_low != lower) + ; + } while ((ted.vscan_high & 1) != upper); +} + +void ted_setmode(TedMode mode, char * text, char * font) +{ + switch (mode) + { + case TEDM_TEXT: + ted.ctrl1 = TED_CTRL1_DEN | TED_CTRL1_RSEL | 3; + ted.ctrl2 = TED_CTRL2_CSEL; + break; + case TEDM_TEXT_MC: + ted.ctrl1 = TED_CTRL1_DEN | TED_CTRL1_RSEL | 3; + ted.ctrl2 = TED_CTRL2_CSEL | TED_CTRL2_MCM; + break; + case TEDM_TEXT_ECM: + ted.ctrl1 = TED_CTRL1_DEN | TED_CTRL1_ECM | TED_CTRL1_RSEL | 3; + ted.ctrl2 = TED_CTRL2_CSEL; + break; + case TEDM_HIRES: + ted.ctrl1 = TED_CTRL1_BMM | TED_CTRL1_DEN | TED_CTRL1_RSEL | 3; + ted.ctrl2 = TED_CTRL2_CSEL; + break; + case TEDM_HIRES_MC: + ted.ctrl1 = TED_CTRL1_BMM | TED_CTRL1_DEN | TED_CTRL1_RSEL | 3; + ted.ctrl2 = TED_CTRL2_CSEL | TED_CTRL2_MCM; + break; + default: + __assume(false); + } + + ted.vid_ptr = (unsigned)text >> 8; + + if (mode < TEDM_HIRES) + { + ted.char_ptr = (unsigned)font >> 8; + } + else + { + ted.sound1_high = (ted.sound1_high & 0x3) | ((unsigned)font >> 10); + } +} diff --git a/oscar64/include/plus4/ted.h b/oscar64/include/plus4/ted.h new file mode 100644 index 0000000..ba0b0df --- /dev/null +++ b/oscar64/include/plus4/ted.h @@ -0,0 +1,102 @@ +#ifndef PLUS4_TED_H +#define PLUS4_TED_H + +#include + +#define TED_CTRL1_RSEL 0x08 +#define TED_CTRL1_DEN 0x10 +#define TED_CTRL1_BMM 0x20 +#define TED_CTRL1_ECM 0x40 + +#define TED_CTRL2_CSEL 0x08 +#define TED_CTRL2_MCM 0x10 +#define TED_CTRL2_RES 0x20 +#define TED_CTRL2_NTSC 0x40 +#define TED_CTRL2_INV 0x80 + +#define TED_INTR_RST 0x02 +#define TED_INTR_LPEN 0x04 +#define TED_INTR_CNT1 0x08 +#define TED_INTR_CNT2 0x10 +#define TED_INTR_CNT3 0x20 +#define TED_INTR_IRQ 0x80 + +#define TED_SND_SQUARE1 0x10 +#define TES_SND_SQUARE2 0x20 +#define TES_SND_NOISE2 0x40 +#define TES_SND_DA 0x80 + +#define TES_CHAR_ROM 0x04 + +struct TED +{ + volatile word timer0; + volatile word timer1; + + volatile word timer2; + volatile byte ctrl1; + volatile byte ctrl2; + + volatile byte keybin; + volatile byte intr_ctrl; + volatile byte intr_enable; + volatile byte raster; + + volatile word cursor_pos; + volatile byte sound1_low; + volatile byte sound2_low; + + volatile byte sound2_high; + volatile byte sound_ctrl; + volatile byte sound1_high; + volatile byte char_ptr; + + volatile byte vid_ptr; + volatile byte color_back; + volatile byte color_back1; + volatile byte color_back2; + + volatile byte color_back3; + volatile byte color_border; + volatile byte char_pos_high; + volatile byte char_pos_low; + + volatile byte vscan_high; + volatile byte vscan_low; + volatile byte hscan; + volatile byte flash; +}; + +enum TedMode +{ + TEDM_TEXT, + TEDM_TEXT_MC, + TEDM_TEXT_ECM, + TEDM_HIRES, + TEDM_HIRES_MC +}; + +// set the display mode and base address. This will also +// adapt the bank. +void ted_setmode(TedMode mode, char * text, char * font); + +// wait for the beam to reach the bottom of the visual area +inline void ted_waitBottom(void); + +// wait for the beam to reach the top of the frame +inline void ted_waitTop(void); + +// wait for the top of the frame and then for the bottom of the visual area +inline void ted_waitFrame(void); + +// wait for a specific raster line +void ted_waitLine(int line); + + +// reference to the TED chip +#define ted (*((struct TED *)0xff00)) + +#pragma compile("ted.c") + +#endif + diff --git a/oscar64/include/setjmp.c b/oscar64/include/setjmp.c new file mode 100644 index 0000000..27922b6 --- /dev/null +++ b/oscar64/include/setjmp.c @@ -0,0 +1,161 @@ +#include "setjmp.h" + +int setjmpsp(jmp_buf env, void * sp) +{ + __asm + { + ldy #0 + lda sp + 0 + sta (env), y + iny + lda sp + 1 + sta (env), y + iny + + lda __ip + 0 + sta (env), y + iny + lda __ip + 1 + sta (env), y + iny + + lda __fp + 0 + sta (env), y + iny + lda __fp + 1 + sta (env), y + iny + + ldx #0 + l1: + lda __sregs, x + sta (env), y + iny + inx + cpx #32 + bne l1 + + tsx + txa + sta (env), y + + inx + l2: + iny + lda $0100, x + sta (env), y + inx + bne l2 + } + + return 0; +} + +int setjmp(jmp_buf env) +{ + __asm + { + ldy #0 + lda __sp + 0 + sta (env), y + iny + lda __sp + 1 + sta (env), y + iny + + lda __ip + 0 + sta (env), y + iny + lda __ip + 1 + sta (env), y + iny + + lda __fp + 0 + sta (env), y + iny + lda __fp + 1 + sta (env), y + iny + + ldx #0 + l1: + lda __sregs, x + sta (env), y + iny + inx + cpx #32 + bne l1 + + tsx + txa + sta (env), y + + inx + l2: + iny + lda $0100, x + sta (env), y + inx + bne l2 + } + + return 0; +} + +#pragma native(setjmp) + +void longjmp(jmp_buf env, int value) +{ + __asm + { + ldy #0 + lda (env), y + sta __sp + 0 + iny + lda (env), y + sta __sp + 1 + iny + + lda (env), y + sta __ip + 0 + iny + lda (env), y + sta __ip + 1 + iny + + lda (env), y + sta __fp + 0 + iny + lda (env), y + sta __fp + 1 + iny + + ldx #0 + l1: + lda (env), y + sta __sregs, x + iny + inx + cpx #32 + bne l1 + + lda (env), y + tax + txs + + inx + l2: + iny + lda (env), y + sta $0100, x + inx + bne l2 + + lda value + sta __accu + lda value + 1 + sta __accu + 1 + } +} + +#pragma native(longjmp) diff --git a/oscar64/include/setjmp.h b/oscar64/include/setjmp.h new file mode 100644 index 0000000..663646a --- /dev/null +++ b/oscar64/include/setjmp.h @@ -0,0 +1,24 @@ +#ifndef SETJMP_H +#define SETJMP_H + +struct _jmp_buf +{ + void * sp, * ip, * fp; + char tmps[32]; + char cpup; + char stack[48]; +}; + +typedef struct _jmp_buf jmp_buf[1]; + +__dynstack int setjmp(jmp_buf env); + +__dynstack int setjmpsp(jmp_buf env, void * sp); + +__dynstack void longjmp(jmp_buf env, int value); + + +#pragma compile("setjmp.c") + +#endif + diff --git a/oscar64/include/stdarg.h b/oscar64/include/stdarg.h new file mode 100644 index 0000000..678d770 --- /dev/null +++ b/oscar64/include/stdarg.h @@ -0,0 +1,12 @@ +#ifndef STDARG_H +#define STDARG_H + +typedef void *va_list; + +#define va_start(list, name) (void) (list = &name + 1) + +#define va_arg(list, mode) ((mode *)(list = (char *)list + sizeof (mode)))[-1] + +#define va_end(list) (void)0 + +#endif diff --git a/oscar64/include/stdbool.h b/oscar64/include/stdbool.h new file mode 100644 index 0000000..7d4eea2 --- /dev/null +++ b/oscar64/include/stdbool.h @@ -0,0 +1,11 @@ +#ifndef STDBOOL_H +#define STDBOOL_H + +#define bool _Bool +#define true (0 == 0) +#define false (0 != 0) + +#define __bool_true_false_are_defined 1 + +#endif + diff --git a/oscar64/include/stddef.h b/oscar64/include/stddef.h new file mode 100644 index 0000000..f94d588 --- /dev/null +++ b/oscar64/include/stddef.h @@ -0,0 +1,11 @@ +#ifndef STDDEF_H +#define STDDEF_H + +#define NULL nullptr + +typedef unsigned int size_t; +typedef signed int ptrdiff_t; + +#define offsetof(st, m) ((size_t)&(((st *)0)->m)) + +#endif diff --git a/oscar64/include/stdint.h b/oscar64/include/stdint.h new file mode 100644 index 0000000..fcba492 --- /dev/null +++ b/oscar64/include/stdint.h @@ -0,0 +1,96 @@ +#ifndef STDINT_H +#define STDINT_H + +typedef signed char int8_t; +typedef short int16_t; +typedef long int32_t; + +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned long uint32_t; + +typedef signed char int_least8_t; +typedef short int_least16_t; +typedef long int_least32_t; + +typedef unsigned char uint_least8_t; +typedef unsigned short uint_least16_t; +typedef unsigned long uint_least32_t; + +typedef signed char int_fast8_t; +typedef short int_fast16_t; +typedef long int_fast32_t; + +typedef unsigned char uint_fast8_t; +typedef unsigned short uint_fast16_t; +typedef unsigned long uint_fast32_t; + +typedef int intptr_t; +typedef unsigned int uintptr_t; + +typedef long intmax_t; +typedef unsigned long uintmax_t; + + /* LIMIT MACROS */ +#define INT8_MIN (-0x7f - 1) +#define INT16_MIN (-0x7fff - 1) +#define INT32_MIN (-0x7fffffffL - 1) + +#define INT8_MAX 0x7f +#define INT16_MAX 0x7fff +#define INT32_MAX 0x7fffffffL + +#define UINT8_MAX 0xff +#define UINT16_MAX 0xffff +#define UINT32_MAX 0xffffffffUL + +#define INT_LEAST8_MIN (-0x7f - 1) +#define INT_LEAST16_MIN (-0x7fff - 1) +#define INT_LEAST32_MIN (-0x7fffffffL - 1) + +#define INT_LEAST8_MAX 0x7f +#define INT_LEAST16_MAX 0x7fff +#define INT_LEAST32_MAX 0x7fffffffL + +#define UINT_LEAST8_MAX 0xff +#define UINT_LEAST16_MAX 0xffff +#define UINT_LEAST32_MAX 0xffffffffUL + +#define INT_FAST8_MIN (-0x7f - 1) +#define INT_FAST16_MIN (-0x7fff - 1) +#define INT_FAST32_MIN (-0x7fffffffL - 1) + +#define INT_FAST8_MAX 0x7f +#define INT_FAST16_MAX 0x7fff +#define INT_FAST32_MAX 0x7fffffffL + +#define UINT_FAST8_MAX 0xff +#define UINT_FAST16_MAX 0xffff +#define UINT_FAST32_MAX 0xffffffffUL + +#define INTPTR_MIN (-0x7fff - 1) +#define INTPTR_MAX 0x7fff +#define UINTPTR_MAX 0xffff + +#define INT8_C(x) (x) +#define INT16_C(x) (x) +#define INT32_C(x) ((x) + (INT32_MAX - INT32_MAX)) + +#define UINT8_C(x) (x) +#define UINT16_C(x) (x) +#define UINT32_C(x) ((x) + (UINT32_MAX - UINT32_MAX)) + +#define INTMAX_C(x) ((x) + (INT32_MAX - INT32_MAX)) +#define UINTMAX_C(x) ((x) + (UINT32_MAX - UINT32_MAX)) + +#define PTRDIFF_MIN INT16_MIN +#define PTRDIFF_MAX INT16_MAX + +#define SIZE_MAX UINT16_MAX + +#define INTMAX_MIN (-0x7fffffffL - 1) +#define INTMAX_MAX 0x7fffffffL +#define UINTMAX_MAX 0xffffffffUL + + +#endif diff --git a/oscar64/include/stdio.c b/oscar64/include/stdio.c new file mode 100644 index 0000000..34d7b59 --- /dev/null +++ b/oscar64/include/stdio.c @@ -0,0 +1,1213 @@ +#include "stdio.h" +#include "conio.h" +#include "stdlib.h" + + +void putchar(char c) +{ + putpch(c); +} + +char getchar(void) +{ + return getpch(); +} + +void puts(const char * str) +{ + while (char ch = *str++) + putpch(ch); +} + +char * gets(char * str) +{ + char i = 0; + while ((char ch = getpch()) != '\n') + str[i++] = ch; + str[i] = 0; + return str; +} + +char * gets_s(char * str, size_t n) +{ + if (str == NULL) + return NULL; + + if (n < 2) + return NULL; + char i = 0, t = n - 1; + + while ((char ch = getpch()) != '\n') + { + if (i < t) + str[i] = ch; + ++i; + } + str[(i < t) ? i : t] = '\0'; + + if (i > t) + return NULL; + return str; +} + +typedef void * (* putstrfn)(void * handle, const char * str); + +void * putstrio(void * handle, const char * str) +{ + puts(str); + return handle; +} + +void * putstrstr(void * handle, const char * str) +{ + char * d = (char *)handle; + const char * s = str; + + do {} while (*d++ = *s++); + + return d - 1; +} + +struct sinfo +{ + char fill; + char width, precision, cha; + char base; + bool sign, left, prefix; +}; + +char nformi(const sinfo * si, char * str, int v, bool s) +{ + char buffer[16]; + char base = si->base; + + unsigned int u = v; + bool neg = false; + + if (s && v < 0) + { + neg = true; + u = -v; + } + + char i = 16; + while (u > 0) + { + char c = u % base; + c += c >= 10 ? si->cha - 10 : '0'; + buffer[--i] = c; + u /= base; + } + + char digits = si->precision != 255 ? 16 - si->precision : 15; + + while (i > digits) + buffer[--i] = '0'; + + if (si->prefix && base == 16) + { + buffer[--i] = si->cha + ('x' - 'a'); + buffer[--i] = '0'; + } + + if (neg) + buffer[--i] = '-'; + else if (si->sign) + buffer[--i] = '+'; + + char j = 0; + if (si->left) + { + while (i < 16) + str[j++] = buffer[i++]; + while (j < si->width) + str[j++] = si->fill; + } + else + { + while (i + si->width > 16) + buffer[--i] = si->fill; + while (i < 16) + str[j++] = buffer[i++]; + } + + return j; +} + +char nforml(const sinfo * si, char * str, long v, bool s) +{ + char buffer[16]; + + unsigned long u = v; + bool neg = false; + + if (s && v < 0) + { + neg = true; + u = -v; + } + + char i = 16; + + while (u > 0) + { + int c = u % si->base; + c += c >= 10 ? si->cha - 10 : '0'; + buffer[--i] = c; + u /= si->base; + } + + char digits = si->precision != 255 ? 16 - si->precision : 15; + + while (i > digits) + buffer[--i] = '0'; + + if (si->prefix && si->base == 16) + { + buffer[--i] = si->cha + ('x' - 'a'); + buffer[--i] = '0'; + } + + if (neg) + buffer[--i] = '-'; + else if (si->sign) + buffer[--i] = '+'; + + char j = 0; + if (si->left) + { + while (i < 16) + str[j++] = buffer[i++]; + while (j < si->width) + str[j++] = si->fill; + } + else + { + while (i + si->width > 16) + buffer[--i] = si->fill; + while (i < 16) + str[j++] = buffer[i++]; + } + + return j; +} + +static float fround5[] = { + 0.5e-0, 0.5e-1, 0.5e-2, 0.5e-3, 0.5e-4, 0.5e-5, 0.5e-6 +}; + +char nformf(const sinfo * si, char * str, float f, char type) +{ + char * sp = str; + + char d = 0; + + if (f < 0.0) + { + f = -f; + sp[d++] = '-'; + } + else if (si->sign) + sp[d++] = '+'; + + if (isinf(f)) + { + sp[d++] = si->cha + ('i' - 'a'); + sp[d++] = si->cha + ('n' - 'a'); + sp[d++] = si->cha + ('f' - 'a'); + } + else + { + int exp = 0; + + char fdigits = si->precision != 255 ? si->precision : 6; + + if (f != 0.0) + { + while (f >= 1000.0) + { + f /= 1000; + exp += 3; + } + + while (f < 1.0) + { + f *= 1000; + exp -= 3; + } + + while (f >= 10.0) + { + f /= 10; + exp ++; + } + + } + + char digits = fdigits + 1; + bool fexp = type == 'e'; + + if (type == 'g') + { + if (exp > 3 || exp < 0) + fexp = true; + } + + if (!fexp) + { + while (exp < 0) + { + f /= 10.0; + exp++; + } + + digits = fdigits + exp + 1; + + if (digits < 7) + f += fround5[digits - 1]; + else + f += fround5[6]; + + if (f >= 10.0) + { + f /= 10.0; + fdigits--; + } + } + else + { + if (digits < 7) + f += fround5[digits - 1]; + else + f += fround5[6]; + + if (f >= 10.0) + { + f /= 10.0; + exp ++; + } + } + + + char pdigits = digits - fdigits; + + if (digits > 20) + digits = 20; + + if (pdigits == 0) + sp[d++] = '0'; + + for(char i=0; i 6) + sp[d++] = '0'; + else + { + int c = (int)f; + f -= (float)c; + f *= 10.0; + sp[d++] = c + '0'; + } + } + + if (fexp) + { + sp[d++] = si->cha + ('e' - 'a'); + if (exp < 0) + { + sp[d++] = '-'; + exp = -exp; + } + else + sp[d++] = '+'; + + sp[d++] = exp / 10 + '0'; + sp[d++] = exp % 10 + '0'; + } + } + + if (d < si->width) + { + if (si->left) + { + for(char i=d; iwidth; i++) + sp[i] = ' '; + } + else + { + for(char i=1; i<=d; i++) + sp[si->width - i] = sp[d - i]; + for(char i=0; iwidth-d; i++) + sp[i] = ' '; + } + d = si->width; + } + + return d; +} + +char * sformat(char * buff, const char * fmt, int * fps, bool print) +{ + const char * p = fmt; + char * bp = buff; + char c; + char bi = 0; + sinfo si; + + while (c = *p++) + { + if (c == '%') + { + if (bi) + { + if (print) + { + bp[bi] = 0; + puts(bp); + } + else + bp += bi; + bi = 0; + } + c = *p++; + + si.base = 10; + si.width = 0; + si.precision = 255; + si.fill = ' '; + si.sign = false; + si.left = false; + si.prefix = false; + + while(true) + { + if (c == '+') + si.sign = true; + else if (c == '0') + si.fill = '0'; + else if (c == '#') + si.prefix = true; + else if (c == '-') + si.left = true; + else + break; + c = *p++; + } + + if (c >= '0' && c <='9') + { + char i = 0; + while (c >= '0' && c <='9') + { + i = i * 10 + c - '0'; + c = *p++; + } + si.width = i; + } + + if (c == '.') + { + char i = 0; + c = *p++; + while (c >= '0' && c <='9') + { + i = i * 10 + c - '0'; + c = *p++; + } + si.precision = i; + } + + if (c == 'd' || c == p'd' || c == 'i' || c == p'i') + { + bi = nformi(&si, bp, *fps++, true); + } + else if (c == 'u' || c == p'u') + { + bi = nformi(&si, bp, *fps++, false); + } + else if (c == 'x' || c == p'x' || c == 'X') + { + si.cha = (c & 0xe0) | 1; + si.base = 16; + bi = nformi(&si, bp, *fps++, false); + } +#ifndef NOLONG + else if (c == 'l' || c == p'l') + { + long l = *(long *)fps; + fps ++; + fps ++; + + c = *p++; + if (c == 'd' || c == p'd' || c == 'i' || c == p'i') + { + bi = nforml(&si, bp, l, true); + } + else if (c == 'u' || c == p'u') + { + bi = nforml(&si, bp, l, false); + } + else if (c == 'x' || c == p'x' || c == 'X') + { + si.cha = (c & 0xe0) | 1; + si.base = 16; + bi = nforml(&si, bp, l, false); + } + } +#endif +#ifndef NOFLOAT + else if ( + c == 'f' || c == 'g' || c == 'e' || + c == p'f' || c == p'g' || c == p'e' || + c == 'F' || c == 'G' || c == 'E') + { + si.cha = (c & 0xe0) | 1; + bi = nformf(&si, bp, *(float *)fps, c - si.cha + 'a'); + fps ++; + fps ++; + } +#endif + else if (c == 's' || c == p's') + { + char * sp = (char *)*fps++; + + char n = 0; + if (si.width) + { + while (sp[n]) + n++; + } + + if (!si.left) + { + while (n < si.width) + { + bp[bi++] = si.fill; + n++; + } + } + + if (print) + { + if (bi) + { + bp[bi] = 0; + puts(bp); + bi = 0; + } + puts(sp); + } + else + { + while (char c = *sp++) + bp[bi++] = c; + + } + + if (si.left) + { + while (n < si.width) + { + bp[bi++] = si.fill; + n++; + } + } + } + else if (c == 'c' || c == p'c') + { + bp[bi++] = *fps++; + } + else if (c) + { + bp[bi++] = c; + } + } + else + { + bp[bi++] = c; + if (bi >= 40) + { + if (print) + { + bp[bi] = 0; + puts(bp); + } + else + bp += bi; + bi = 0; + } + } + } + bp[bi] = 0; + if (bi) + { + if (print) + puts(bp); + else + bp += bi; + } + + return bp; +} + +void printf(const char * fmt, ...) +{ + char buff[50]; + sformat(buff, fmt, (int *)&fmt + 1, true); +} + +int sprintf(char * str, const char * fmt, ...) +{ + char * d = sformat(str, fmt, (int *)&fmt + 1, false); + return d - str; +} + +void vprintf(const char * fmt, va_list vlist) +{ + char buff[50]; + sformat(buff, fmt, (int *)vlist, true); +} + +int vsprintf(char * str, const char * fmt, va_list vlist) +{ + char * d = sformat(str, fmt, (int *)vlist, false); + return d - str; +} + +static inline bool isspace(char c) +{ + return (c == ' ' || c == '\t' || c == '\n' || c == '\r'); +} + +static int hexch(char cs) +{ + if (cs >= '0' && cs <= '9') + return cs - '0'; + else if (cs >= 'a' && cs <= 'f') + return cs - 'a' + 10; + else if (cs >= 'A' && cs <= 'F') + return cs - 'A' + 10; + else + return -1; +} + +static const char * scanpat(const char * fmt, char * mask) +{ + for(char i=0; i<32; i++) + mask[i] = 0; + + bool negated = false; + char fc = *fmt++; + + if (fc == '^') + { + negated = true; + fc = *fmt++; + } + + do + { + char fd = fc; + char nc = *fmt++; + + if (nc == '-') + { + nc = *fmt; + if (nc != ']') + { + fmt++; + fd = nc; + nc = *fmt++; + } + } + + while (fc <= fd) + { + mask[fc >> 3] |= 0x01 << (fc & 7); + fc++; + } + + fc = nc; + + } while (fc && fc != ']'); + + if (negated) + { + for(char i=4; i<32; i++) + mask[i] = ~mask[i]; + } + + return fmt; +} + +int fpscanf(const char * fmt, int (* ffunc)(void * p), void * fparam, void ** params) +{ + char fc, cs; + int nv = 0; + unsigned nch = 0; + + cs = ffunc(fparam); + nch++; + + while (cs > 0 && (fc = *fmt++)) + { + switch (fc) + { + case ' ': + while (cs > 0 && isspace(cs)) + { + cs = ffunc(fparam); + nch++; + } + break; + case '%': + { + int width = 0x7fff; + bool issigned = true; + bool ignore = false; + bool islong = false; + unsigned base = 10; + + fc = *fmt++; + if (fc == '*') + { + ignore = true; + fc = *fmt++; + } + + if (fc >= '0' && fc <= '9') + { + width = (int)(fc - '0'); + fc = *fmt++; + while (fc >= '0' && fc <= '9') + { + width = width * 10 + (int)(fc - '0'); + fc = *fmt++; + } + } + + if (fc == 'l' || fc == p'l') + { + islong = true; + fc = *fmt++; + } + + switch (fc) + { +#ifndef __PETSCII__ + case p'n': +#endif + case 'n': + *(unsigned *)*params = nch; + params++; + nv++; + break; + + case 'x': +#ifndef __PETSCII__ + case p'x': +#else + case 'X': +#endif + base = 16; + case 'u': +#ifndef __PETSCII__ + case p'u': +#endif + issigned = false; + case 'i': + case 'd': +#ifndef __PETSCII__ + case p'i': + case p'd': +#endif + { + bool sign = false; + if (cs == '-') + { + sign = true; + cs = ffunc(fparam); + nch++; + } + else if (cs == '+') + { + cs = ffunc(fparam); + nch++; + } + + int cv; + if ((cv = hexch(cs)) >= 0) + { + cs = ffunc(fparam); + nch++; + + if (cv == 0 && cs == 'x') + { + base = 16; + cs = ffunc(fparam); + nch++; + } +#ifndef NOLONG + if (islong) + { + unsigned long vi = (unsigned long)cv; + while ((cv = hexch(cs)) >= 0) + { + vi = vi * base + (unsigned long)cv; + cs = ffunc(fparam); + nch++; + } + + if (!ignore) + { + if (sign && issigned) + *(long *)*params = -(long)vi; + else + *(unsigned long *)*params = vi; + + params++; + nv++; + } + } + else +#endif + { + unsigned vi = (unsigned)cv; + while ((cv = hexch(cs)) >= 0) + { + vi = vi * base + (unsigned)cv; + cs = ffunc(fparam); + nch++; + } + + if (!ignore) + { + if (sign && issigned) + *(int *)*params = -(int)vi; + else + *(unsigned *)*params = vi; + + params++; + nv++; + } + } + } + else + return nv; + + } break; +#ifndef NOFLOAT + + case 'f': + case 'e': + case 'g': +#ifndef __PETSCII__ + case p'f': + case p'e': + case p'g': +#else + case 'F': + case 'E': + case 'G': +#endif + { + bool sign = false; + if (cs == '-') + { + sign = true; + cs = ffunc(fparam); + nch++; + } + else if (cs == '+') + { + cs = ffunc(fparam); + nch++; + } + + if (cs >= '0' && cs <= '9' || cs == '.') + { + float vf = 0; + while (cs >= '0' && cs <= '9') + { + vf = vf * 10 + (int)(cs - '0'); + cs = ffunc(fparam); + nch++; + } + + if (cs == '.') + { + float digits = 1.0; + cs = ffunc(fparam); + while (cs >= '0' && cs <= '9') + { + vf = vf * 10 + (int)(cs - '0'); + digits *= 10; + cs = ffunc(fparam); + nch++; + } + vf /= digits; + } + + char e = 0; + bool eneg = false; + + if (cs == 'e' || cs == 'E') + { + cs = ffunc(fparam); + nch++; + if (cs == '-') + { + eneg = true; + cs = ffunc(fparam); + nch++; + } + else if (cs == '+') + { + cs = ffunc(fparam); + nch++; + } + + while (cs >= '0' && cs <= '9') + { + e = e * 10 + cs - '0'; + cs = ffunc(fparam); + nch++; + } + + } + + if (!ignore) + { + if (e) + { + if (eneg) + { + while (e > 6) + { + vf /= 1000000.0; + e -= 6; + } + vf /= tpow10[e]; + } + else + { + while (e > 6) + { + vf *= 1000000.0; + e -= 6; + } + vf *= tpow10[e]; + } + } + + if (sign) + *(float *)*params = -vf; + else + *(float *)*params = vf; + + params++; + nv++; + } + + } + else + return nv; + + } break; +#endif + case 's': +#ifndef __PETSCII__ + case p's': +#endif + { + char * pch = (char *)*params; + while (width > 0 && cs > 0 && !isspace(cs)) + { + if (!ignore) + *pch++ = cs; + cs = ffunc(fparam); + nch++; + width--; + } + if (!ignore) + { + *pch = 0; + params++; + nv++; + } + } break; + + case '[': + { + char pat[32]; + fmt = scanpat(fmt, pat); + + char * pch = (char *)*params; + while (width > 0 && (pat[cs >> 3] & (1 << (cs & 7)))) + { + if (!ignore) + *pch++ = cs; + cs = ffunc(fparam); + nch++; + width--; + } + if (!ignore) + { + *pch = 0; + params++; + nv++; + } + + } break; + + case 'c': +#ifndef __PETSCII__ + case p'c': +#endif + { + char * pch = (char *)*params; + if (width == 0x7fff) + width = 1; + while (width > 0 && cs > 0) + { + if (!ignore) + *pch++ = cs; + cs = ffunc(fparam); + nch++; + width--; + } + if (!ignore) + { + params++; + nv++; + } + } break; + } + + } break; + + + default: + if (cs == fc) + { + cs = ffunc(fparam); + nch++; + } + else + return nv; + break; + } + } + + return nv; +} + + +int sscanf_func(void * fparam) +{ + char ** cp = (char **)fparam; + char c = **cp; + if (c) + (*cp)++; + return c; +} + +int scanf_func(void * fparam) +{ + return getchar(); +} + +int sscanf(const char * str, const char * fmt, ...) +{ + return fpscanf(fmt, sscanf_func, &str, (void **)((&fmt) + 1)); +} + +int scanf(const char * fmt, ...) +{ + return fpscanf(fmt, scanf_func, nullptr, (void **)((&fmt) + 1)); +} + +#if defined(__CBM__) +#include + + +#define FNUM_BASE 2 + +struct FILE +{ + signed char fnum; +}; + +FILE files[FOPEN_MAX]; +FILE stdio_file = {-1}; + +FILE * stdin = &stdio_file; +FILE * stdout = &stdio_file; + +FILE * fopen(const char * fname, const char * mode) +{ + char i = 0; + while (i < FOPEN_MAX && files[i].fnum) + i++; + + if (i < FOPEN_MAX) + { + char cbmname[32]; + char j = 0, rj = 0; + char pdrive = 0, pdevice = 8, t = 0; + + while (fname[j] >= '0' && fname[j] <= '9') + t = t * 10 + (fname[j++] - '0'); + if (fname[j] == ':') + { + j++; + pdrive = t; + + rj = j; + t = 0; + while (fname[j] >= '0' && fname[j] <= '9') + t = t * 10 + (fname[j++] - '0'); + if (fname[j] == ':') + { + j++; + pdevice = pdrive; + pdrive = t; + } + else + j = rj; + } + else + j = rj; + + char k = 1, s = 1; + if (pdrive >= 10) + { + cbmname[k++] = pdrive / 10 + '0'; + pdrive %= 10; + } + cbmname[k++] = pdrive + '0'; + cbmname[k++] = ':'; + while (fname[j]) + cbmname[k++] = fname[j++]; + cbmname[k++] = ','; + cbmname[k++] = p's'; + cbmname[k++] = ','; + if (mode[0] == 'w' || mode[0] == 'W') + { + cbmname[k++] = p'w'; + cbmname[--s] = p'@'; + } + else if (mode[0] == 'r' || mode[0] == 'R') + cbmname[k++] = p'r'; + else if (mode[0] == 'a' || mode[0] == 'A') + cbmname[k++] = p'a'; + cbmname[k++] = 0; + + krnio_setnam(cbmname + s); + if (krnio_open(i + FNUM_BASE, pdevice, i + FNUM_BASE)) + { + files[i].fnum = i + FNUM_BASE; + return files + i; + } + } + + return nullptr; +} + +int fclose(FILE * fp) +{ + krnio_close(fp->fnum); + fp->fnum = 0; + return 0; +} + +int fgetc(FILE* stream) +{ + if (stream->fnum >= 0) + return krnio_getch(stream->fnum); + else + return getpch(); +} + +char* fgets(char* s, int n, FILE* stream) +{ + if (krnio_gets(stream->fnum, s, n) > 0) + return s; + return nullptr; +} + +int fputc(int c, FILE* stream) +{ + if (stream->fnum >= 0) + return krnio_putch(stream->fnum, c); + else + { + putpch(c); + return 0; + } + +} + +int fputs(const char* s, FILE* stream) +{ + if (stream->fnum >= 0) + return krnio_puts(stream->fnum, s); + else + { + puts(s); + return 0; + } +} + +int feof(FILE * stream) +{ + return stream->fnum >= 0 && krnio_pstatus[stream->fnum] == KRNIO_EOF; +} + +size_t fread( void * buffer, size_t size, size_t count, FILE * stream ) +{ + return krnio_read(stream->fnum, (char *)buffer, size * count) / size; +} + +size_t fwrite( const void* buffer, size_t size, size_t count, FILE* stream ) +{ + return krnio_write(stream->fnum, (const char *)buffer, size * count) / size; +} + +int fprintf( FILE * stream, const char* format, ... ) +{ + char buff[50]; + if (stream->fnum < 0 || krnio_chkout(stream->fnum)) + { + sformat(buff, format, (int *)&format + 1, true); + krnio_clrchn(); + return 0; + } + else + return -1; +} + +int fscanf_func(void * fparam) +{ + char ch = krnio_chrin(); + return ch; +} + +int fscanf( FILE *stream, const char *format, ... ) +{ + if (stream->fnum < 0 || krnio_chkin (stream->fnum)) + { + int res = fpscanf(format, fscanf_func, nullptr, (void **)((&format) + 1)); + krnioerr err = krnio_status(); + krnio_pstatus[stream->fnum] = err; + krnio_clrchn(); + return res; + } + else + return -1; +} + +#endif + diff --git a/oscar64/include/stdio.h b/oscar64/include/stdio.h new file mode 100644 index 0000000..8ceeb4d --- /dev/null +++ b/oscar64/include/stdio.h @@ -0,0 +1,65 @@ +#ifndef STDIO_H +#define STDIO_H + +#include +#include +#include + +void putchar(char c); + +char getchar(void); + +void puts(const char * str); + +char * gets(char * str); + +char * gets_s(char * str, size_t n); + +void printf(const char * fmt, ...); + +int sprintf(char * str, const char * fmt, ...); + +void vprintf(const char * fmt, va_list vlist); + +int vsprintf(char * str, const char * fmt, va_list vlist); + +int scanf(const char * fmt, ...); + +int sscanf(const char * str, const char * fmt, ...); + + +#define FOPEN_MAX 8 +#define FILENAME_MAX 16 +#define EOF -1 + +struct FILE; + +extern FILE * stdin; +extern FILE * stdout; + +FILE * fopen(const char * fname, const char * mode); + +int fclose(FILE * fp); + +int fgetc(FILE* stream); + +char* fgets(char* s, int n, FILE* stream); + +int fputc(int c, FILE* stream); + +int fputs(const char* s, FILE* stream); + +int feof(FILE * stream); + +size_t fread( void * buffer, size_t size, size_t count, FILE * stream ); + +size_t fwrite( const void* buffer, size_t size, size_t count, FILE* stream ); + +int fprintf( FILE * stream, const char* format, ... ); + +int fscanf( FILE *stream, const char *format, ... ); + +#pragma compile("stdio.c") + +#endif + diff --git a/oscar64/include/stdlib.c b/oscar64/include/stdlib.c new file mode 100644 index 0000000..62da9d9 --- /dev/null +++ b/oscar64/include/stdlib.c @@ -0,0 +1,870 @@ +#include "stdlib.h" +#include "string.h" +#include "stdio.h" +#include "math.h" +#include "crt.h" + +void itoa(int n, char * s, unsigned radix) +{ + bool neg = n < 0; + unsigned un = neg ? -n : n; + + char i = 0; + do { + char d = un % radix; + d += d < 10 ? '0' : 'A' - 10;; + s[i++] = d; + } while ((un /= radix) > 0); + + if (neg) + { + s[i++] = '-'; + } + s[i] = 0; + char j = 0; + while (j + 1 < i) + { + char c = s[j]; + s[j++] = s[--i]; + s[i] = c; + } +} + +void utoa(unsigned int n, char * s, unsigned radix) +{ + char i = 0; + do { + char d = n % radix; + d += d < 10 ? '0' : 'A' - 10;; + s[i++] = d; + } while ((n /= radix) > 0); + + s[i] = 0; + char j = 0; + while ((char)(j + 1) < i) + { + char c = s[j]; + s[j++] = s[--i]; + s[i] = c; + } +} + +void ltoa(long n, char * s, unsigned radix) +{ + bool neg = n < 0; + if (neg) + { + n = - n; + } + + char i = 0; + do { + char d = n % radix; + d += d < 10 ? '0' : 'A' - 10;; + s[i++] = d; + } while ((n /= radix) > 0); + + if (neg) + { + s[i++] = '-'; + } + s[i] = 0; + char j = 0; + while (j + 1 < i) + { + char c = s[j]; + s[j++] = s[--i]; + s[i] = c; + } +} + +void ultoa(unsigned long n, char * s, unsigned radix) +{ + char i = 0; + do { + char d = n % radix; + d += d < 10 ? '0' : 'A' - 10;; + s[i++] = d; + } while ((n /= radix) > 0); + + s[i] = 0; + char j = 0; + while (j + 1 < i) + { + char c = s[j]; + s[j++] = s[--i]; + s[i] = c; + } +} + + +void ftoa(float f, char * s) +{ + if (f < 0.0) + { + f = -f; + *s++ = '-'; + } + + int digits = 0; + while (f >= 1000.0) + { + f /= 1000; + digits += 3; + } + + if (f != 0.0) + { + while (f < 1.0) + { + f *= 1000; + digits -= 3; + } + + while (f >= 10.0) + { + f /= 10; + digits ++; + } + + f += 0.0000005; + if (f >= 10.0) + { + f /= 10.0; + digits ++; + } + } + + int exp = 0; + if (digits < 0) + { + exp = digits; + digits = 0; + } + else if (digits > 6) + { + exp = digits; + digits = 0; + } + + for(int i=0; i<7; i++) + { + int c = (int)f; + f -= (float)c; + f *= 10.0; + *s++ = c + '0'; + if (i == digits) + *s++ = '.'; + } + if (exp) + { + *s++ = 'E'; + if (exp < 0) + { + *s++ = '-'; + exp = -exp; + } + else + *s++ = '+'; + + if (exp >= 10) + { + *s++ = exp / 10 + '0'; + exp %= 10; + } + *s++ = exp + '0'; + } + + *s++= 0; +} + +int atoi(const char * s) +{ + char c; + while ((c = *s++) <= ' ') + if (!c) return 0; + + bool neg = false; + if (c == '-') + { + neg = true; + c = *s++; + } + else if (c == '+') + c = *s++; + + int v = 0; + while (c >= '0' && c <= '9') + { + v = v * 10 + (c - '0'); + c = *s++; + } + + if (neg) + v = -v; + + return v; +} + +long atol(const char * s) +{ + char c; + while ((c = *s++) <= ' ') + if (!c) return 0; + + bool neg = false; + if (c == '-') + { + neg = true; + c = *s++; + } + else if (c == '+') + c = *s++; + + long v = 0; + while (c >= '0' && c <= '9') + { + v = v * 10 + (c - '0'); + c = *s++; + } + + if (neg) + v = -v; + + return v; +} + +const float tpow10[7] = {1.0, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0}; + +float atof(const char * s) +{ + return strtof(s, nullptr); +} + +float strtof(const char *s, const char **endp) +{ + if (endp) + *endp = s; + + char c = *s; + while (c <= ' ') + { + if (!c) return 0; + c = *++s; + } + + bool neg = false; + if (c == '-') + { + neg = true; + c = *++s; + } + else if (c == '+') + c = *++s; + + float v = 0; + while (c >= '0' && c <= '9') + { + v = v * 10 + (c - '0'); + c = *++s; + } + + if (c == '.') + { + float d = 1.0; + c = *++s; + while (c >= '0' && c <= '9') + { + v = v * 10 + (c - '0'); + d = d * 10; + c = *++s; + } + + v /= d; + } + + if (c == 'e' || c == 'E') + { + c = *++s; + bool eneg = false; + if (c == '-') + { + eneg = true; + c = *++s; + } + else if (c == '+') + c = *++s; + + int e = 0; + while (c >= '0' && c <= '9') + { + e = e * 10 + (c - '0'); + c = *++s; + } + + if (eneg) + { + while (e > 6) + { + v /= 1000000.0; + e -= 6; + } + v /= tpow10[e]; + } + else + { + while (e > 6) + { + v *= 1000000.0; + e -= 6; + } + v *= tpow10[e]; + } + + } + + if (neg) + v = -v; + + if (endp) + *endp = s; + + return v; +} + +int strtoi(const char *s, const char **endp, char base) +{ + if (endp) + *endp = s; + + char c = *s; + while (c <= ' ') + { + if (!c) return 0; + c = *++s; + } + + bool neg = false; + if (c == '-') + { + neg = true; + c = *++s; + } + else if (c == '+') + c = *++s; + + if (c == '0') + { + c = *++s; + if (c == 'x' || c == 'X') + { + base = 16; + c = *++s; + } + else if (base == 0) + base = 8; + } else if (base == 0) + base = 10; + + int v = 0; + for(;;) + { + if (c >= '0' && c <= '9') + v = v * base + (c - '0'); + else if (c >= 'a' && c <= 'f') + v = v * base + (c - 'a' + 10); + else if (c >= 'A' && c <= 'F') + v = v * base + (c - 'A' + 10); + else + break; + + c = *++s; + } + + if (neg) + v = -v; + + if (endp) + *endp = s; + + return v; +} + +unsigned strtou(const char *s, const char **endp, char base) +{ + if (endp) + *endp = s; + + char c = *s; + while (c <= ' ') + { + if (!c) return 0; + c = *++s; + } + + if (c == '0') + { + c = *++s; + if (c == 'x' || c == 'X') + { + base = 16; + c = *++s; + } + else if (base == 0) + base = 8; + } else if (base == 0) + base = 10; + + unsigned v = 0; + for(;;) + { + if (c >= '0' && c <= '9') + v = v * base + (c - '0'); + else if (c >= 'a' && c <= 'f') + v = v * base + (c - 'a' + 10); + else if (c >= 'A' && c <= 'F') + v = v * base + (c - 'A' + 10); + else + break; + + c = *++s; + } + + if (endp) + *endp = s; + + return v; +} + +long strtol(const char *s, const char **endp, char base) +{ + if (endp) + *endp = s; + + char c = *s; + while (c <= ' ') + { + if (!c) return 0; + c = *++s; + } + + bool neg = false; + if (c == '-') + { + neg = true; + c = *++s; + } + else if (c == '+') + c = *++s; + + if (c == '0') + { + c = *++s; + if (c == 'x' || c == 'X') + { + base = 16; + c = *++s; + } + else if (base == 0) + base = 8; + } else if (base == 0) + base = 10; + + long v = 0; + for(;;) + { + if (c >= '0' && c <= '9') + v = v * base + (c - '0'); + else if (c >= 'a' && c <= 'f') + v = v * base + (c - 'a' + 10); + else if (c >= 'A' && c <= 'F') + v = v * base + (c - 'A' + 10); + else + break; + + c = *++s; + } + + if (neg) + v = -v; + + if (endp) + *endp = s; + + return v; +} + +unsigned long strtoul(const char *s, const char **endp, char base) +{ + if (endp) + *endp = s; + + char c = *s; + while (c <= ' ') + { + if (!c) return 0; + c = *++s; + } + + if (c == '0') + { + c = *++s; + if (c == 'x' || c == 'X') + { + base = 16; + c = *++s; + } + else if (base == 0) + base = 8; + } else if (base == 0) + base = 10; + + unsigned long v = 0; + for(;;) + { + if (c >= '0' && c <= '9') + v = v * base + (c - '0'); + else if (c >= 'a' && c <= 'f') + v = v * base + (c - 'a' + 10); + else if (c >= 'A' && c <= 'F') + v = v * base + (c - 'A' + 10); + else + break; + + c = *++s; + } + + if (endp) + *endp = s; + + return v; +} + +int abs(int n) +{ + return n < 0 ? - n : n; +} + +long labs(long n) +{ + return n < 0 ? - n : n; +} + +div_t div(int numer, int denom) +{ + div_t result; + + if (denom == 0) + { + result.quot = 0; + result.rem = 0; + } + else + { + result.quot = numer / denom; + result.rem = numer % denom; + } + + return result; +} + +ldiv_t ldiv(long int numer, long int denom) +{ + ldiv_t result; + + if (denom == 0) + { + result.quot = 0; + result.rem = 0; + } + else + { + result.quot = numer / denom; + result.rem = numer % denom; + } + + return result; +} + +void exit(int status) +{ + __asm + { + lda status + sta accu + 0 + lda status + 1 + sta accu + 1 + ldx spentry + txs + lda #$4c + sta $54 + lda #0 + sta $13 + } +} + +void abort(void) +{ + exit(-1); +} + + +extern struct Heap { + Heap * next, * end; +} HeapNode; + +unsigned heapfree(void) +{ + unsigned avail = 0; + Heap * h = HeapNode.next; + while (h) + { + avail += (h->end - h) * sizeof(Heap); + h = h->next; + } + return avail; +} + +#if 0 +struct Heap {q + unsigned int size; + Heap * next; +} * freeHeap; + +bool freeHeapInit = false; + +void HeapStart, HeapEnd; + +#pragma section(heap, 0x0000, HeapStart, HeapEnd) + + +void * malloc(unsigned int size) +{ + size = (size + 7) & ~3; + if (!freeHeapInit) + { + freeHeap = (Heap *)&HeapStart; + freeHeap->next = nullptr; + freeHeap->size = (unsigned int)&HeapEnd - (unsigned int)&HeapStart; + freeHeapInit = true; + } + + Heap * pheap = nullptr, * heap = freeHeap; + while (heap) + { + if (size <= heap->size) + { + if (size == heap->size) + { + if (pheap) + pheap->next = heap->next; + else + freeHeap = heap->next; + } + else + { + Heap * nheap = (Heap *)((int)heap + size); + nheap->size = heap->size - size; + nheap->next = heap->next; + if (pheap) + pheap->next = nheap; + else + freeHeap = nheap; + heap->size = size; + } + + return (void *)((int)heap + 2); + } + pheap = heap; + heap = heap->next; + } + + return nullptr; +} + +void free(void * ptr) +{ + if (!ptr) + return; + + Heap * fheap = (Heap *)((int)ptr - 2); + Heap * eheap = (Heap *)((int)ptr - 2 + fheap->size); + + if (freeHeap) + { + if (eheap == freeHeap) + { + fheap->size += freeHeap->size; + fheap->next = freeHeap->next; + freeHeap = fheap; + } + else if (eheap < freeHeap) + { + fheap->next = freeHeap; + freeHeap = fheap; + } + else + { + Heap * pheap = freeHeap; + while (pheap->next && pheap->next < fheap) + pheap = pheap->next; + Heap * nheap = (Heap *)((int)pheap + pheap->size); + + if (nheap == fheap) + { + pheap->size += fheap->size; + if (pheap->next == eheap) + { + pheap->size += pheap->next->size; + pheap->next = pheap->next->next; + } + } + else if (pheap->next == eheap) + { + fheap->next = pheap->next->next; + fheap->size += pheap->next->size; + pheap->next = fheap; + } + else + { + fheap->next = pheap->next; + pheap->next = fheap; + } + } + } + else + { + freeHeap = fheap; + freeHeap->next = nullptr; + } +} +#endif + +void * calloc(int num, int size) +{ + size *= num; + void * p = malloc(size); + if (p) + memclr(p, size); + return p; +} + +void * realloc(void * ptr, unsigned size) +{ + if (ptr) + { +#ifdef HEAPCHECK + Heap * pheap = (Heap *)((char *)ptr - 6); + Heap * eheap = pheap->next; + + unsigned psize = (char *)eheap - (char *)pheap; + + void * nptr = malloc(size); + memcpy(nptr, ptr, psize - 6); + free(ptr); + return nptr; +#else + unsigned nsize = (size + 5) & ~3; + + // Get heap info + Heap * pheap = (Heap *)((char *)ptr - 2); + Heap * eheap = pheap->next; + + unsigned psize = (char *)eheap - (char *)pheap; + + Heap * h = HeapNode.next; + Heap * hp = &HeapNode; + while (h && h < pheap) + { + hp = h; + h = h->next; + } + + if (nsize <= psize) + { + // check if we should free some memory + if (nsize + sizeof(HeapNode) < psize) + { + Heap * nheap = (Heap *)((char *)pheap + nsize); + pheap->next = nheap; + + if (h == eheap) + { + nheap->end = h->end; + nheap->next = h->next; + } + else + { + nheap->end = eheap; + nheap->next = h; + } + + hp->next = nheap; + } + + return ptr; + } + else if (h == eheap) + { + // Free space after this + + // Check if enough space if extending + unsigned xsize = (char *)h->end - (char *)pheap; + if (xsize >= nsize) + { + if (xsize > nsize + sizeof(HeapNode)) + { + Heap * nheap = (Heap *)((char *)pheap + nsize); + pheap->next = nheap; + nheap->end = h->end; + nheap->next = h->next; + hp->next = nheap; + } + else + { + pheap->next = h->end; + hp->next = h->next; + } + + return ptr; + } + } + + void * nptr = malloc(size); + memcpy(nptr, ptr, psize - 2); + free(ptr); + return nptr; +#endif + } + else + return malloc(size); +} + +static unsigned seed = 31232; +static unsigned long seedl = 321432142412; + +unsigned int rand(void) +{ + seed ^= seed << 7; + seed ^= seed >> 9; + seed ^= seed << 8; + return seed; +} + +void srand(unsigned int s) +{ + seed = s; +} + +unsigned long lrand(void) +{ + seedl ^= seedl << 13; + seedl ^= seedl >> 17; + seedl ^= seedl << 5; + return seedl; +} + +void lsrand(unsigned long s) +{ + seedl = s; +} diff --git a/oscar64/include/stdlib.h b/oscar64/include/stdlib.h new file mode 100644 index 0000000..68d8268 --- /dev/null +++ b/oscar64/include/stdlib.h @@ -0,0 +1,89 @@ +#ifndef STDLIB_H +#define STDLIB_H + +#define RAND_MAX 65535u + +#define EXIT_SUCCESS 0 + +#define EXIT_FAILURE -1 + +struct div_t +{ + int quot; + int rem; +}; + +struct ldiv_t +{ + long int quot; + long int rem; +}; + +extern const float tpow10[7]; + +void itoa(int n, char * s, unsigned radix); + +void utoa(unsigned int n, char * s, unsigned radix); + +void ftoa(float f, char * s); + +void ltoa(long n, char * s, unsigned radix); + +void ultoa(unsigned long n, char * s, unsigned radix); + +int atoi(const char * s); + +long atol(const char * s); + +float atof(const char * s); + +float strtof(const char *s, const char **endp); + +int strtoi(const char *s, const char **endp, char base); + +unsigned strtou(const char *s, const char **endp, char base); + +long strtol(const char *s, const char **endp, char base); + +unsigned long strtoul(const char *s, const char **endp, char base); + +int abs(int n); + +long labs(long n); + +div_t div(int numer, int denom); + +ldiv_t ldiv(long int numer, long int denom); + +void exit(int status); + +void abort(void); + +void * malloc(unsigned int size); + +void free(void * ptr); + +void * calloc(int num, int size); + +void * realloc(void * ptr, unsigned size); + +unsigned heapfree(void); + +unsigned int rand(void); + +// This library uses the xorshift random algorithm, so it will not work with a +// seed value of 0. All other values are fine, and the algorithm will also generate +// any number from 1 to 65535 regardless of seed (except 0). +void srand(unsigned int seed); + +unsigned long lrand(void); + +void lsrand(unsigned long seed); + +#pragma intrinsic(malloc) + +#pragma intrinsic(free) + +#pragma compile("stdlib.c") + +#endif diff --git a/oscar64/include/string.c b/oscar64/include/string.c new file mode 100644 index 0000000..389d252 --- /dev/null +++ b/oscar64/include/string.c @@ -0,0 +1,402 @@ +#include "string.h" + +#if 1 + +char * strcpy(char * dst, const char * src) +{ + __asm + { + lda dst + sta accu + lda dst + 1 + sta accu + 1 + + ldy #0 + L1: lda (src), y + sta (dst), y + beq W1 + iny + lda (src), y + sta (dst), y + beq W1 + iny + bne L1 + inc src + 1 + inc dst + 1 + bne L1 + W1: + + } +} + +#else +char * strcpy(char * dst, const char * src) +{ + char * d = dst; + const char * s = src; + + do {} while (*d++ = *s++); + + return dst; +} +#endif + +#pragma native(strcpy) + +char * strncpy(char * dst, const char * src, int n) +{ + char * d = dst; + const char * s = src; + + do {} while (n-- && (*d++ = *s++)); + + // If, after copying the terminating null character from src, count is not reached, + // additional null characters are written to dest until the total of count characters + // have been written. + while (n-- > 0) + *d++ = '\0'; + + return dst; +} + +#pragma native(strncpy) + +#if 1 + +signed char strcmp(const char * ptr1, const char * ptr2) +{ + __asm + { + ldy #0 + L1: lda (ptr1), y + beq W1 + cmp (ptr2), y + bne W2 + iny + lda (ptr1), y + beq W1 + cmp (ptr2), y + bne W2 + iny + bne L1 + inc ptr1 + 1 + inc ptr2 + 1 + bne L1 + + W1: cmp (ptr2), y + beq E + + W2: bcs W3 + + lda #$ff + bmi E + + W3: lda #$01 + + E: + sta accu + } +} + +#else +int strcmp(const char * ptr1, const char * ptr2) +{ + const char * p = ptr1, * q = ptr2; + char c, d; + while ((c = *p++) == (d = *q++)) + { + if (!c) + return 0; + } + if (c < d) + return -1; + else + return 1; +} +#endif + +#pragma native(strcmp) + +signed char strncmp(const char * ptr1, const char * ptr2, int size) +{ + const char *p = ptr1, *q = ptr2; + char c = 0, d = 0; + + while (size-- && (c = *p++) == (d = *q++)) + { + if (!c) + return 0; + } + + if (size < 0) + return 0; // equal up to size + + if (c < d) + return -1; + + if (c > d) + return 1; + + return 0; // equal +} + +#pragma native(strncmp) + +int strlen(const char * str) +{ + const char * s = str; + + int i = 0; + while (s[i]) + i++; + return i; +} + +#pragma native(strlen) + +char * strcat(char * dst, const char * src) +{ + char * d = dst; + const char * s = src; + + while (*d) + d++; + + do {} while (*d++ = *s++); + + return dst; +} + +#pragma native(strcat) + +char * strncat(char * dst, const char * src, int n) +{ + char * d = dst; + const char * s = src; + + while (*d) + d++; + + while (1) { + if (!n--) { + // The terminating null character is always appended in the end + if (*d) { + *d = '\0'; + } + + return dst; + } + + // stopping if the null character is found + if (!(*d++ = *s++)) { + return dst; + } + } +} + +#pragma native(strncat) + +char * cpycat(char * dst, const char * src) +{ + while (*dst = *src++) + dst++; + + return dst; +} + +#pragma native(cpycat) + +char * strchr( const char * str, int ch ) +{ + char * p = (char *)str; + + while (*p != (char)ch) + { + if (!*p) + return nullptr; + p++; + } + return p; +} + +char * strrchr(const char * str, int ch) +{ + const char * p = str; + const char * last = nullptr; + + while (*p) + { + if (*p == (char)ch) + last = p; + p++; + } + + // The terminating null character is considered to be a part of the string and can be found if searching for '\0' + return ch == '\0' ? (char *)p : (char *)last; +} + +char * strstr(const char * str, const char * substr) +{ + const char * p = str; + const char * q = substr; + + if (!*q) + return (char *)str; // empty substring matches at the start + + while (*p) + { + const char * start = p; + q = substr; + + while (*p && *q && *p == *q) + { + p++; + q++; + } + + if (!*q) // reached end of substring + return (char *)start; + + p = start + 1; // move to next character in str + } + + return nullptr; // substring not found +} + +void * memset(void * dst, int value, int size) +{ + __asm + { + lda value + + ldx size + 1 + beq _w1 + ldy #0 + _loop1: + sta (dst), y + iny + bne _loop1 + inc dst + 1 + dex + bne _loop1 + _w1: + ldy size + beq _w2 + _loop2: + dey + sta (dst), y + bne _loop2 + _w2: + } + return dst; +} + + +void * memclr(void * dst, int size) +{ + char * d = dst; + while (size--) + *d++ = 0; + return dst; +} + +void * memcpy(void * dst, const void * src, int size) +{ + __asm + { + ldx size + 1 + beq _w1 + ldy #0 + _loop1: + lda (src), y + sta (dst), y + iny + bne _loop1 + inc src + 1 + inc dst + 1 + dex + bne _loop1 + _w1: + ldy size + beq _w2 + dey + beq _w3 + _loop2: + lda (src), y + sta (dst), y + dey + bne _loop2 + _w3: + lda (src), y + sta (dst), y + _w2: + } + return dst; +#if 0 + + char * d = dst, * s = src; + while (size--) + *d++ = *s++; + return dst; +#endif +} + +#pragma native(memcpy) + +void * memmove(void * dst, const void * src, int size) +{ + int sz = size; + if (sz > 0) + { + char * d = (char *)dst; + const char * s = (const char *)src; + if (d < s) + { + do { + *d++ = *s++; + } while (--sz); + } + else if (d > s) + { + d += sz; + s += sz; + do { + *--d = *--s; + } while (--sz); + } + } + return dst; +} + +signed char memcmp(const void * ptr1, const void * ptr2, int size) +{ + const char * p = (const char *)ptr1, * q = (const char *)ptr2; + char c, d; + + while (size--) + { + c = *p++; + d = *q++; + if (c < d) + return -1; + else if (c > d) + return 1; + } + + return 0; +} + +void * memchr(const void * ptr, int ch, int size) +{ + const unsigned char * p = (const unsigned char *)ptr; + + while (size--) + { + if (*p == (unsigned char)ch) + return (void *)p; + + p++; + } + + return nullptr; // not found +} \ No newline at end of file diff --git a/oscar64/include/string.h b/oscar64/include/string.h new file mode 100644 index 0000000..65d75b9 --- /dev/null +++ b/oscar64/include/string.h @@ -0,0 +1,48 @@ +#ifndef STRING_H +#define STRING_H + +char * strcpy(char * dst, const char * src); + +char * strncpy(char * dst, const char * src, int n); + +signed char strcmp(const char * ptr1, const char * ptr2); + +signed char strncmp(const char * ptr1, const char * ptr2, int n); + +int strlen(const char * str); + +char * strcat(char * dst, const char * src); + +char * strncat(char * dst, const char * src, int n); + +char * strchr(const char * str, int ch); + +char * strrchr(const char * str, int ch); + +char * strstr(const char * str, const char * substr); + +char * cpycat(char * dst, const char * src); + +void * memclr(void * dst, int size); + +void * memset(void * dst, int value, int size); + +void * memcpy(void * dst, const void * src, int size); + +signed char memcmp(const void * ptr1, const void * ptr2, int size); + +void * memmove(void * dst, const void * src, int size); + +void * memchr(const void * ptr, int ch, int size); + +#pragma intrinsic(strcpy) + +#pragma intrinsic(memcpy) + +#pragma intrinsic(memset) + +#pragma intrinsic(memclr) + +#pragma compile("string.c") + +#endif diff --git a/oscar64/include/time.c b/oscar64/include/time.c new file mode 100644 index 0000000..07986cd --- /dev/null +++ b/oscar64/include/time.c @@ -0,0 +1,56 @@ +#include "time.h" + +clock_t clock(void) +{ + __asm + { +#if defined(__PLUS4__) + lda $a5 + sta accu + 0 + lda $a4 + sta accu + 1 + lda $a3 + sta accu + 2 + lda #0 + sta accu + 3 +#elif defined(__CBMPET__) + lda $8f + sta accu + 0 + lda $8e + sta accu + 1 + lda $8d + sta accu + 2 + lda #0 + sta accu + 3 +#elif defined(__ATARI__) +loop: + lda $14 + ldx $13 + ldy $12 + cmp $14 + bne loop + + sta accu + 0 + stx accu + 1 + sty accu + 2 + lda #0 + sta accu + 3 +#elif defined(__X16__) + jsr $ffde + sta accu + 0 + stx accu + 1 + sty accu + 2 + lda #0 + sta accu + 3 +#else + lda $a2 + sta accu + 0 + lda $a1 + sta accu + 1 + lda $a0 + sta accu + 2 + lda #0 + sta accu + 3 +#endif + } +} diff --git a/oscar64/include/time.h b/oscar64/include/time.h new file mode 100644 index 0000000..4f885ab --- /dev/null +++ b/oscar64/include/time.h @@ -0,0 +1,12 @@ +#ifndef TIME_H +#define TIME_H + +typedef long clock_t; + +#define CLOCKS_PER_SEC 60L + +clock_t clock(void); + +#pragma compile("time.c") + +#endif diff --git a/oscar64/include/vic20/vic.c b/oscar64/include/vic20/vic.c new file mode 100644 index 0000000..dae5843 --- /dev/null +++ b/oscar64/include/vic20/vic.c @@ -0,0 +1,2 @@ +#include "vic.h" + diff --git a/oscar64/include/vic20/vic.h b/oscar64/include/vic20/vic.h new file mode 100644 index 0000000..ea7fb9c --- /dev/null +++ b/oscar64/include/vic20/vic.h @@ -0,0 +1,27 @@ +#ifndef VIC20_VIC +#define VIC20_VIC + +#include + +struct VICI +{ + volatile byte hpos; + volatile byte vpos; + volatile byte ncols; + volatile byte nrows; + volatile byte beam; + volatile byte mempos; + + volatile byte hlpen, vlpen; + volatile byte xpaddle, ypaddle; + volatile byte oscfreq[4]; + volatile byte volcol; + volatile byte color; +}; + +#define vici (*((struct VICI *)0x9000)) + +#pragma compile("vic.c") + +#endif + diff --git a/oscar64/make/makefile b/oscar64/make/makefile new file mode 100644 index 0000000..f2f982a --- /dev/null +++ b/oscar64/make/makefile @@ -0,0 +1,140 @@ +project_dir := $(abspath $(patsubst %/,%,$(dir $(abspath $(lastword $(MAKEFILE_LIST)))))/../) +sources = $(wildcard $(project_dir)/oscar64/*.cpp) +srcdir := $(if $(srcdir),$(srcdir),$(project_dir)/build) +objects = $(patsubst $(project_dir)/oscar64/%.cpp,$(srcdir)/%.o,$(sources)) + +CXX = c++ +CPPFLAGS = -g -O2 -std=c++11 -Wno-switch +SED = sed +REMOVE_FORCE_ALL = $(RM) --recursive --dir +export OSCAR64_CC = $(project_dir)/bin/oscar64 +export OSCAR64_CFLAGS = +export OSCAR64_CXX = $(project_dir)/bin/oscar64 +MKDIR_PARENT = mkdir -p -m 755 +INSTALL = install +INSTALL_PROGRAM = $(INSTALL) -m 755 +INSTALL_DATA = $(INSTALL) -m 644 +DESTDIR = +prefix = /usr/local +exec_prefix = $(prefix) +bindir = $(exec_prefix)/bin +includedir = $(prefix)/include +mandir = $(prefix)/man/man1 + +ifdef WINDIR + UNAME_S := $(shell uname -s 2>/dev/null || echo "Windows") + + ifneq ($(findstring MINGW,$(UNAME_S)),) + # MinGW + linklibs = -lversion -lpthread -ldbghelp + else + # Native Windows (MSVC or other) + linklibs = -lpthread -ldbghelp + endif +else + UNAME_S := $(shell uname -s) + + ifeq ($(UNAME_S), Darwin) + linklibs = -lpthread + else + linklibs = -lrt -lpthread + endif +endif + + +all: compiler samples check + + +$(srcdir)/%.o: $(project_dir)/oscar64/%.cpp + @echo "Compiling compiler file" $@ "..." $< + @$(CXX) -c $(CPPFLAGS) $< -o $@ + + +$(srcdir)/%.d: $(project_dir)/oscar64/%.cpp + @$(MKDIR_PARENT) $(srcdir) + @echo "Transforming file" $@ "..." $< + @set -e; \ + $(RM) $@; \ + $(CC) -MM -MT $(patsubst %.d,%.o,$@) $(CPPFLAGS) $< > $@.$$$$; \ + $(SED) 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ + $(RM) $@.$$$$ + + +compiler: --prep-build-dir $(objects) + @$(MKDIR_PARENT) $(srcdir) + @echo "Linking compiler..." + $(CXX) $(CPPFLAGS) $(objects) $(linklibs) -o $(project_dir)/bin/oscar64 + + +.PHONY : clean +clean : + @echo "Cleaning compiler..." + @$(RM) $(srcdir)/*.o + @$(RM) $(srcdir)/*.d + @$(RM) $(project_dir)/bin/oscar64 + @$(MAKE) -C $(project_dir)/samples clean + @$(MAKE) -C $(project_dir)/autotest clean + + +.PHONY : distclean +distclean : + @echo "Distribution cleaning compiler..." + @$(REMOVE_FORCE_ALL) $(srcdir) + @$(REMOVE_FORCE_ALL) $(project_dir)/bin + @$(MAKE) -C $(project_dir)/samples clean + @$(MAKE) -C $(project_dir)/autotest clean + + +samples: compiler + @$(MAKE) -C $(project_dir)/samples all + + +check: compiler + @$(MAKE) -C $(project_dir)/autotest all + +install: compiler + @echo "Installing to" $(DESTDIR)$(prefix) + @$(MKDIR_PARENT) $(DESTDIR)$(bindir) + $(INSTALL_PROGRAM) $(project_dir)/bin/oscar64 $(DESTDIR)$(bindir) + @$(MKDIR_PARENT) $(DESTDIR)$(includedir)/oscar64 + $(INSTALL_DATA) $(wildcard $(project_dir)/include/*.h $(project_dir)/include/*.c) $(DESTDIR)$(includedir)/oscar64 + @$(MKDIR_PARENT) $(DESTDIR)$(includedir)/oscar64/audio + $(INSTALL_DATA) $(wildcard $(project_dir)/include/audio/*.h $(project_dir)/include/audio/*.c) $(DESTDIR)$(includedir)/oscar64/audio + @$(MKDIR_PARENT) $(DESTDIR)$(includedir)/oscar64/c64 + $(INSTALL_DATA) $(wildcard $(project_dir)/include/c64/*.h $(project_dir)/include/c64/*.c) $(DESTDIR)$(includedir)/oscar64/c64 + @$(MKDIR_PARENT) $(DESTDIR)$(includedir)/oscar64/c128 + $(INSTALL_DATA) $(wildcard $(project_dir)/include/c128/*.h $(project_dir)/include/c128/*.c) $(DESTDIR)$(includedir)/oscar64/c128 + @$(MKDIR_PARENT) $(DESTDIR)$(includedir)/oscar64/cx16 + $(INSTALL_DATA) $(wildcard $(project_dir)/include/cx16/*.h $(project_dir)/include/cx16/*.c) $(DESTDIR)$(includedir)/oscar64/cx16 + @$(MKDIR_PARENT) $(DESTDIR)$(includedir)/oscar64/gfx + $(INSTALL_DATA) $(wildcard $(project_dir)/include/gfx/*.h $(project_dir)/include/gfx/*.c) $(DESTDIR)$(includedir)/oscar64/gfx + @$(MKDIR_PARENT) $(DESTDIR)$(includedir)/oscar64/nes + $(INSTALL_DATA) $(wildcard $(project_dir)/include/nes/*.h $(project_dir)/include/nes/*.c) $(DESTDIR)$(includedir)/oscar64/nes + @$(MKDIR_PARENT) $(DESTDIR)$(includedir)/oscar64/opp + $(INSTALL_DATA) $(wildcard $(project_dir)/include/opp/*.h $(project_dir)/include/opp/*.cpp) $(DESTDIR)$(includedir)/oscar64/opp + @$(MKDIR_PARENT) $(DESTDIR)$(includedir)/oscar64/plus4 + $(INSTALL_DATA) $(wildcard $(project_dir)/include/plus4/*.h $(project_dir)/include/plus4/*.c) $(DESTDIR)$(includedir)/oscar64/plus4 + @$(MKDIR_PARENT) $(DESTDIR)$(includedir)/oscar64/vic20 + $(INSTALL_DATA) $(wildcard $(project_dir)/include/vic20/*.h $(project_dir)/include/vic20/*.c) $(DESTDIR)$(includedir)/oscar64/vic20 + @$(MKDIR_PARENT) $(mandir) + $(INSTALL_DATA) $(project_dir)/oscar64.1 $(mandir) + +uninstall: + @echo "Uninstalling..." + @$(RM) $(DESTDIR)$(bindir)/oscar64 + @$(REMOVE_FORCE_ALL) $(DESTDIR)$(includedir)/oscar64/ + + +ifeq ($(UNAME_S), Darwin) + +else + +include $(objects:.o=.d) + +endif + + +--prep-build-dir: + echo "makedir" + @if [ ! -d $(srcdir) ]; then $(MKDIR_PARENT) $(srcdir); fi + @if [ ! -d $(project_dir)/bin ]; then $(MKDIR_PARENT) $(project_dir)/bin; fi diff --git a/oscar64/makezip.bat b/oscar64/makezip.bat new file mode 100644 index 0000000..26e884d --- /dev/null +++ b/oscar64/makezip.bat @@ -0,0 +1,11 @@ +mkdir r:\oscar64 +mkdir r:\oscar64\bin +mkdir r:\oscar64\include + +xcopy /y bin\oscar64.exe r:\oscar64\bin +xcopy /y /e include r:\oscar64\include + +tar -caf r:\oscar64.zip r:\oscar64 + + + diff --git a/oscar64/oscar64.1 b/oscar64/oscar64.1 new file mode 100644 index 0000000..8d6c599 --- /dev/null +++ b/oscar64/oscar64.1 @@ -0,0 +1,335 @@ +.TH oscar64 1 "June 2026" "Version 1.32" "User Commands" +.SH NAME +oscar64 \- optimizing C/C++ cross-compiler for 6502 systems + +.SH SYNOPSIS +.B oscar64 +.RB [ \-h ] +.RB [ \-v [ 1 | 2 | 3 ]] +.RB { \-i= "\fIincludePath\fR}" +.RB [ \-ii= "\fIincludePath\fR" ] +.RB [ \-o= "\fIoutput.prg\fR" ] +.RB [ \-rt= "\fIruntime.c\fR" ] +.RB [ \-tf= "\fIformat\fR" ] +.RB [ \-tm= "\fImachine\fR" ] +.RB [ \-pp ] +.RB [ \-strict ] +.RB [ \-psci ] +.RB [ \-n | \-bc ] +.RB [ \-e [ p | t | b | a | i ]] +.RB [ \-g [ p ]] +.RB [ \-O ( 0 | 1 | 2 | 3 | s )] +.RB [ \-O ( i | ii | a | z | p | o | x | g | m )] +.RB [ \-d \fISYMBOL\fR[=\fIvalue\fR] ] +.RB [ \-D \fISYMBOL\fR[=\fIvalue\fR] ] +.RB [ \-xz ] +.RB [ \-rmp ] +.RB [ \-d64= "\fIdiskname\fR" ] +.RB [ \-f= "\fIfile\fR" ] +.RB [ \-fz= "\fIfile\fR" ] +.RB [ \-fi= "\fIn\fR" ] +.RB [ \-cid= "\fIn\fR" ] +.RB [ \-csub= "\fIn\fR" ] +.RB [ \-cname= "\fIname\fR" ] +.RI { source.c } +.RI { source.cpp } + +.SH DESCRIPTION +\fBoscar64\fR is a C/C++ compiler for the 6502 processor family. It is designed to generate high-performance 8-bit code targeting platforms such as the Commodore 64, VIC-20, PET, Commander X16, NES, Atari 8-bit, and Mega65. + +.SH OPTIONS +.SS "General Options" +.TP +.B \-h +Print help text and exit. +.TP +.BI \-i= includePath +Set the include file search path. +.TP +.BI \-ii= includePath +Set the default include path (overrides built-in path). +.TP +.BI \-o= output.prg +Specify output file name. +.TP +.BI \-rt= runtime.c +Use a custom runtime startup source (or empty for none). +.TP +.B \-pp +Compile in C++ mode (automatically enabled for .cpp files). +.TP +.B \-strict +Use strict ANSI C parsing (no C++ extensions). +.TP +.B \-psci +Use PETSCII encoding for all string literals without prefix. +.TP +.B \-xz +Extended zero page usage. Provides more zero page space but disables return to BASIC. +.TP +.B \-rmp +Generate error files (.error.map and .error.asm) when the linker fails. + +.SS "Symbol Definitions" +.TP +.BI \-d SYMBOL[=value] +Define a preprocessor symbol (like \fB\-D\fR in other compilers). +.TP +.BI \-D SYMBOL[=value] +Define a symbol in a gcc-compatible way (e.g., \fB\-D NAME=VALUE\fR as a separate argument). + +.SS "Code Generation" +.TP +.B \-n +Create pure native code for all functions (now the default). +.TP +.B \-bc +Create byte code for all functions (disables native code generation). +.TP +.B \-e +Execute the result in the integrated emulator after compilation. +.TP +.B \-ep +Execute and profile the result in the integrated emulator. +.TP +.B \-et +Execute with trace output in the integrated emulator. +.TP +.B \-eb +Execute with break trace in the integrated emulator. +.TP +.B \-ea +Execute with asserts enabled in the integrated emulator. +.TP +.B \-ei +Execute with I/O range checking in the integrated emulator. + +.SS "Optimization" +.TP +.B \-O0 +Disable all optimizations. +.TP +.B \-O1 "\fBor\fR" \-O +Default optimizations. +.TP +.B \-O2 +Aggressive speed optimizations (includes auto-inline of small functions). +.TP +.B \-O3 +Aggressive optimization for speed (includes all sub-optimizations). +.TP +.B \-Os +Optimize for code size. +.TP +.B \-Oi +Enable auto-inline of small functions (part of O2/O3). +.TP +.B \-Oii +Enable aggressive auto-inline of all suitable functions. +.TP +.B \-Oa +Optimize inline assembler (part of O2/O3). +.TP +.B \-Oz +Enable auto-placement of global variables in zero page (part of O3). +.TP +.B \-Op +Optimize constant parameters. +.TP +.B \-Oo +Optimize size using "outliner" (extract repeated code sequences into functions). +.TP +.B \-Ox +Optimize pointer arithmetic by blocking shorter arrays from crossing page boundaries. +.TP +.B \-Og +Enable global optimization. +.TP +.B \-Om +Enable merge calls optimization. + +.SS "Debug and Diagnostics" +.TP +.B \-v +Enable verbose output for diagnostics. +.TP +.B \-v2 +More verbose output. +.TP +.B \-v3 +Even more verbose output (implies v2). +.TP +.B \-g +Create source-level debug info and add source line numbers to .asm listing. +.TP +.B \-gp +Create source-level debug info and static profile data. + +.SS "Target Format" +.TP +.BI \-tf= format +Set the output target format. +.RS +.TP +prg +Standard Commodore PRG format (default). +.TP +crt +EasyFlash cartridge format. +.TP +crt8 +Generic 8KB cartridge (0x8000\-0xa000). +.TP +crt16 +Generic 16KB cartridge (0x8000\-0xc000). +.TP +bin +Raw binary format. +.TP +lzo +LZO-compressed PRG output. +.RE + +.SS "Target Machine" +.TP +.BI \-tm= machine +Specify the target machine. +.RS +.TP +c64 +Commodore C64 (0x0800..0xa000, default). +.TP +c128 +Commodore C128 (0x1c00..0xfc00). +.TP +c128b +C128 first 16KB only (0x1c00..0x4000). +.TP +c128e +C128 first 48KB only (0x1c00..0xc000). +.TP +plus4 +Commodore Plus/4 (0x1000..0xfc00). +.TP +vic20 +VIC-20, no expansion (0x1000..0x1e00). +.TP +vic20+3 +VIC-20 +3K RAM (0x0400..0x1e00). +.TP +vic20+8 +VIC-20 +8K RAM (0x1200..0x4000). +.TP +vic20+16 +VIC-20 +16K RAM (0x1200..0x6000). +.TP +vic20+24 +VIC-20 +24K RAM (0x1200..0x8000). +.TP +pet +Commodore PET, 8K RAM (0x0400..0x2000). +.TP +pet16 +Commodore PET, 16K RAM (0x0400..0x4000). +.TP +pet32 +Commodore PET, 32K RAM (0x0400..0x8000). +.TP +nes +NES NROM, 32K ROM, no mirror. +.TP +nes_nrom_h +NES NROM, 32K ROM, horizontal mirror. +.TP +nes_nrom_v +NES NROM, 32K ROM, vertical mirror. +.TP +nes_mmc1 +NES MMC1 mapper, 256K PROM, 128K CROM. +.TP +nes_mmc3 +NES MMC3 mapper, 512K PROM, 256K CROM. +.TP +atari +Atari 8-bit systems (0x2000..0xbc00). +.TP +x16 +Commander X16 (0x0800..0x9f00). +.TP +mega65 +Mega65 (0x2000..0xc000). +.RE + +.SS "Disk Image" +.TP +.BI \-d64= diskname +Create a .d64 disk image with the specified name. +.TP +.BI \-f= file.xxx +Add a binary file to the disk image. +.TP +.BI \-fz= file.xxx +Add a compressed binary file to the disk image. +.TP +.BI \-fi= n +Set sector interleave for data files on disk image (default: 10). + +.SS "Cartridge Options" +.TP +.BI \-cid= n +Set cartridge type ID (used by VICE emulator). +.TP +.BI \-csub= n +Set cartridge sub-type. +.TP +.BI \-cname= name +Set cartridge name string. + +.SH "RUNTIME LIBRARY DEFINES" +.TP +.BI \-d NOLONG +Disable support for long integers in printf. +.TP +.BI \-d NOFLOAT +Disable floating-point support in printf. +.TP +.BI \-d HEAPCHECK +Enable heap allocation/free checking; jam if heap is full or free out of range. +.TP +.BI \-d NOBSSCLEAR +Do not clear BSS segment on startup. +.TP +.BI \-d NOZPCLEAR +Do not clear zero-page BSS segment on startup. + +.SH EXAMPLES +Compile a program with optimizations: +.nf + oscar64 -i=include -O2 -o=demo.prg demo.c +.fi + +Compile with emulator execution and custom runtime: +.nf + oscar64 -e -rt=startup.c -f=assets.dat game.c +.fi + +Create a disk image with compiled output: +.nf + oscar64 -d64=mydisk.d64 -f=demo.prg demo.c +.fi + +Compile in C++ mode with maximum optimization: +.nf + oscar64 -pp -O3 -o=demo.prg demo.cpp +.fi + +.SH FILES +.TP +.B /usr/local/bin/oscar64 +Main compiler executable. +.TP +.B /usr/local/share/oscar64/ +Headers and startup runtime source. + +.SH SEE ALSO +.BR llvm-mos (1) diff --git a/oscar64/oscar64.md b/oscar64/oscar64.md new file mode 100644 index 0000000..40c750f --- /dev/null +++ b/oscar64/oscar64.md @@ -0,0 +1,1746 @@ +# oscar64 + +Oscar64 is a C/C++ cross compiler running on a modern system (such as a Windows PC, Mac or Linux machine) and targets the classic 6502 family of processors. It is mainly focused on Commodore systems such as the C64, PET or VIC20. + +# History and motivation + +When I started this project, I was under the assumption that the 6502 would make a poor C target. Existing compilers generated slow and bloated code. There are various limitations that appear to limit the ability of a 6502 processor to efficiently execute C compiled code: + +* The C standard requires computations to be made with ints which work best if they have the same size as a pointer. +* Signed comparisons require multiple instructions +* No multiply divide or 16 bit operations +* The CPU stack is only 256 bytes and there is no stack relative addressing, making it unsuitable for local variables +* There is no indirect addressig with constant offset for e.g. struct members +* A tiny number of CPU registers + +The original plan was thus to compile to an intermediate language that would be interpreted, offering the 16bit support without the added code cost. This turned out to work great from a code size perspective, but the performance was poor. So I decided to give compiling to native code a shot. + +Most of the 6502 limitations can be overcome by careful code analysis and optimizations. + +* A second data stack relieves the CPU stack. +* Static call graph analysis removes the need for a stack completely +* Using zero page as a register extension +* Integer value range analysis reduces many 16bit operations to 8bit + +In the end, it turned out that the native code is not only significantly faster than the interpreted one, but also smaller in most cases. + +# The compiler + +## Limits and Errors + +There are still several open areas, but most targets have been reached. The current Dhrystone performance is 94 iterations per second with byte code (10993) and 442 iterations with native code (8952 Bytes). This clearly shows that Dhrystone is not a valid benchmark for optimizing compilers, because it puts the 6502 on par with a 4MHz 8088 or 68k, which it clearly is not. + +### Language + +* Missing warnings for all kind of abuses + +### Linker + +* No external libraries + +### Standard Libraries + +* No standard file functions, but CBM based file ops + +### Runtime + +* No NaN support for floats +* Basic zero page variables not restored on stop/restore + +### Intermediate code generation + +* No check for running out of temporary registers + +### Native code generation + +### C++ support level + +The compiler will most likely not support a current C++ standard in the near future, but several C++ features are already implemented. The compiler can be switched into C++ mode with the command line option -pp or by providing a source file with a .cpp extension. + +Supported Features: + +* namespaces +* reference types +* member functions +* constructors and destructors +* operator overloading +* single inheritance of class and struct +* const member functions +* new, delete, new[] and delete[] +* virtual member functions +* string and iostream classes +* default parameters for class members and functions +* templates +* vector, array and list template classes +* lambda functions +* auto variables, parameters and return types +* for range loop +* constexpr compile time evaluation +* parameter packs and templates + +## Installation and Usage + +### Installing on windows + +A windows installer is provided with the release, the compiler is installed into "%programfiles(x86)%\oscar64\bin\oscar64". When not using batch or make files, it might be a good idea to add the folder to the path environment variable. Windows 10 is the minimum release required for the installer version. + +### Building + +The compiler can also be built using MSVC or GCC. A visual studio project and a makefile are part of the source repository. The makefile is in the make folder. +You should build from the top-level directory of Oscar64: + +To build just the compiler: + +* make -C make compiler + +To build just game demonstrations: + +* make -C make samples + +To build everything: + +* make -C make all + +## Compiler arguments + +The compiler is command line driven, and creates an executable .prg file. + + oscar64 {-i=includePath} [-o=output.prg] [-rt=runtime.c] [-tf=format] [-tm=machine] [-e] [-n] [-dSYMBOL[=value]] {source.c|source.cpp} + +* -v : verbose output for diagnostics +* -v2 : more verbose output +* -i : additional include paths +* -ii : set default include path +* -o : optional output file name +* -rt : alternative runtime library, replaces the crt.c (or empty for none) +* -e : execute the result in the integrated emulator +* -ep : execute and profile the result in the integrated emulator +* -bc : create byte code for all functions +* -n : create pure native code for all functions (now default) +* -d : define a symbol (e.g. NOFLOAT or NOLONG to avoid float/long code in printf) +* -D : define a symbol in a gcc compliant way (e.g. -D NAME=VALUE) +* -O1 or -O : default optimizations +* -O0 : disable optimizations +* -O2 : more aggressive speed optimizations including auto inline of small functions +* -O3 : aggressive optimization for speed +* -Os : optimize for size +* -Oi : enable auto inline of small functions (part of O2/O3) +* -Oa : optimize inline assembler (part of O2/O3) +* -Oa : optimize inline assembler (part of O2/O3) +* -Oz : enable auto placement of global variables in zero page (part of O3) +* -Op : optimize constant parameters +* -Oo : optimize size using "outliner" (extract repeated code sequences into functions) +* -Ox : optimize pointer arithmetic by blocking shorter arrays to not cross page boundaries +* -OM : enable generation of self modifying code +* -g : create source level debug info and add source line numbers to asm listing +* -gp : create source level debug info and add source line numbers to asm listing and static profile data +* -tf : target format, may be prg, crt or bin +* -tm : target machine +* -d64 : create a d64 disk image +* -f : add a binary file to the disk image +* -fz : add a compressed binary file to the disk image +* -fi : sector skip for data files on disk image +* -xz : extended zero page usage, more zero page space, but no return to basic +* -cid : cartridge type ID, used by vice emulator +* -csub : cartridge sub type +* -cname : cartridge name +* -pp : compile in C++ mode +* -strict : use strict ANSI C parsing (no C++ goodies) +* -psci : use PETSCII encoding for all strings without prefix +* -rmp : generate error files .error.map, .error.asm when linker fails + +A list of source files can be provided. + +### Run time library defines + +* -dNOLONG : no support for long in printf +* -dNOFLOAT : no float in printf +* -dHEAPCHECK : check heap allocate and free and jam if heap full or free out of range +* -dNOBSSCLEAR : don't clear BSS segment on startup +* -dNOZPCLEAR : don't clear zeropage BSS segment on startup + +### Supported target machines + +* c64 : Commodore C64, (0x0800..0xa000) +* c128 : Commodore C128, memory range (0x1c00..0xfc00) +* c128b : Commodore C128, first 16KB only (0x1c00..0x4000) +* c128e : Commodore C128, first 48KB only (0x1c00..0xc000) +* plus4 : Commodore PLUS4, (0x1000..0xfc00) +* vic20: Commodore VIC20, no extra memory (0x1000..0x1e00) +* vic20+3 : Commodore VIC20, 3K RAM expansion (0x0400..0x1e00) +* vic20+8 : Commodore VIC20, 8K RAM expansion (0x1200..0x4000) +* vic20+16 : Commodore VIC20, 16K RAM expansion (0x1200..0x6000) +* vic20+24 : Commodore VIC20, 24K RAM expansion (0x1200..0x8000) +* pet: Commodore PET, 8K RAM (0x0400..0x2000) +* pet16 : Commodore PET, 16K RAM (0x0400..0x4000) +* pet32 : Commodore PET, 32K RAM (0x0400..0x8000) +* nes : Nintendo entertainment system, NROM 32 K ROM, no mirror +* nes_nrom_h : Nintendo entertainment system, NROM 32 K ROM, h mirror +* nes_nrom_v : Nintendo entertainment system, NROM 32 K ROM, v mirror +* nes_mmc1 : Nintendo entertainment system, MMC1, 256K PROM, 128K CROM +* nes_mmc3 : Nintendo entertainment system, MMC3, 512K PROM, 256K CROM +* atari : Atari 8bit systems, (0x2000..0xbc00) +* x16 : Commander X16, (0x0800..0x9f00) +* mega65 : Mega 65, (0x2000..0xc000) + +### C64 Cartridge formats + +Three cartridge formats are supported + +* -tf=crt : creates an easyflash cartridge with common code expanded from bank 0 into memory +* -tf=crt8 : creates a generic 8KB cartridge from 0x8000-0xa000 with autostart header +* -tf=crt16 : creates a generic 16KB cartridge from 0x8000-0xc000 with autostart header +* -cid=nnn : specifies the type ID, used by vice emul + +The easyflash format cartridge expands the first 16KB bank into memory to provide a +common area of "code" and "data". This is taken from the standard code and data section in region "main". + +The generic cartridge formats use the region "rom" from the first bank and include an autostart header. +The sections "code" and "data" are placed into this "rom" region. The "bss", "stack" and "heap" sections +are placed into the "main" region from 0x0800 to 0x8000. + +The layout can be changed by defining own "main" and "rom" regions. + +## Files generated + +The main file generated by the compiler is a .prg, .crt or .bin with the code and constant data. Additional files are created to support debugging and analysis: + +### Map file ".map" + +Shows the addresses of all regions, sections and objects. This is a good place to look, if your generated code turns out to be too large. + +A map file has four sections: + +* sections : symbolic areas of memory +* regions : physical areas of memory +* objects : placement of objects in memory and sections by address +* objects by size : placement and size of objects ordered by size + + + +### Assembler source ".asm" + +A listing of the generated bytecode and native assembler instructions. A good place to cross reference when stuck in the machine code monitor. This file also includes source references when compiling with the -g option. + +### Intermediate code ".int" + +A listing of the generated intermediate code. + +### Vice debugger ".lbl" + +Creates vice monitor commands to define all static labels. + + al 0801 .startup + al 164d .spentry + al 174c .BSSStart + al 1766 .BSSEnd + al 9fd7 .StackEnd + +One can load the label file in the monitor using the load_labels (ll) command or provide it on the command line for vice with the "-moncommands" command line argument. + +### Complete debug information ".dbj" + +This is a JSON file that contains detailed debug information and is generated with the -g option. + +it has four top sections: + +* memory : complete memory map with labels and source location +* variables : list of all global/static variables with address and type index +* functions : list of all functions with local variables, line numbers and addresses +* types : list of all types + +#### Debug memory information + +The memory partitioning is described using a list of + +* name : name of the memory object +* start : start address (inclusive) +* end : end address (exclusive) +* type : linkage type of memory object +* source : source file that declared this memory object +* line : source line that declared this memory object + +#### Debug type information + +Every defined type has a unique numeric typeid that is used to reference it from the variable sections or from composite types. + +Each type has up to five attributes: + +* name : a string, present for all named types +* typeid : the unique numeric ID for this type +* size : size of a variable of this type in bytes +* type : base type or composition method for this type + * int : signed Integer + * uint : unsigned integer + * bool : boolean value + * enum : enumeration + * ptr : pointer to + * ref : reference + * struct : struct/class + * union +* members : list of members of a composite type or enum +* eid : typeid for pointer or reference targets + +#### Debug variables information + +Global variables have a name, a memory range and a typeid + +* name : a string, present for named variables +* start : start address (inclusive) +* end : end address (exclusive) +* typeid : the type id for this variable + +#### Debug function information + +* name : a string, present for named functions +* start : start address (inclusive) +* end : end address (exclusive) +* source : source file that declared this function +* line : source line that declared this function +* lines : list of all source lines for this function + * start : start address (inclusive) + * end : end address (exclusive) + * source : source file that declared this function + * line : source line that declared this function +* variables : list of all local variables for this function + * name : a string, present for named variables + * start : start address (inclusive) + * end : end address (exclusive) + * enter : first line where the variable becomes visible + * leave : last line where the variable is visible + * typeid : the type id for this variable + * base : zero page register pair for (sp/fp) for stack based variables + +### Annotated code size reference ".csz" + +This file is generated when compiling with the -gp option and includes all source lines annotated with line number, start in memory and number of bytes of generated machine code. This is a good place for part of your source that appear to use an unexpected amount of memory. + + +### Creating a d64 disk file ".d64" + +The compiler can create a .d64 disk file, that includes the compiled .prg file as the first file in the directory and a series of additional resource files. The name of the disk file is provided with the -d64 command line options, additional files with the -f or -fz option. + + oscar64 charsetload.c -d64=charsetload.d64 -fz=../resources/charset.bin + +The compressed files can be loaded into memory and decompressed using the oscar_expand_lzo function from oscar.h or on the fly while reading from disk with the krnio_read_lzo function. + +## Building the samples + +The windows installer puts the samples into the users documents folder, using the directory "%userprofile%\documents\oscar64\samples". A batch file *make.bat* is also placed into this directory which invokes the compiler and builds all samples. It invokes a second batch file in "%userprofile%\documents\oscar64\bin\oscar64.bat" that calls the compiler. + +On a linux installation one can build the samples invoking the *build.sh* shell script in the samples directory, for multithreading use *make -C samples -j*. + + + + +# Debugging + +The .asm file is a great resource when debugging from within e.g. the VICE monitor, but there is also a source level debugging project underway: [Modern VICE PDB Monitor](https://github.com/MihaMarkic/modern-vice-pdb-monitor) by MihaMarkic. The compiler options for a good source level debugging experience should be "-n -g -O0" to generate native code with debugging symbols and no (well less) optimization. + +# Language extensions + +The compiler has various extensions to simplify developing for the C64. + +## Pragmas + +Warnings can be turned on or off using the warning pragma. The scope of the pragma is currently global in most cases, so if it is turned off at some place, it is off everywhere. + + #pragma warning(disable: 2000,2001) + +A message can be displayed during compilation with the message pragma. The message can be a list of values, such as constant strings or numbers that can be compiled at compile time. + + #pragma message("Hello User") + + struct M {int x, y, z;}; + #pragma message("Struct size: ", sizeof(M)); + + +## Embedding binary data + +The compiler supports the #embed preprocessor directive to import binary data. It converts a section of an external binary file into a sequence of numbers that can be placed into an initializer of an array. + + byte data[] = { + + #embed "data.bin" + + }; + +A section of the file can be selected by providing a limit and or an offset into the file before the file name. + + byte data[] = { + + #embed 4096 126 "data.bin" + + }; + +Embedded data can be compressed during compile time and expanded at runtime using one of the expand functions in the oscar.h + + char charset[] = { + #embed 2048 0 lzo "../resources/charset.bin" + }; + + + int main(void) { + oscar_expand_lzo(Charset, charset); + } + +Compression algorithms so far are LZ (lzo) and run length (rle). + +Data can also be embedded as 16bit words with the "word" argument: + + unsigned indices[] = { + #embed 2048 0 word "../resources/index.bin" + }; + +## Embedding sprite and graphics data + +The #embed preprocessor can also import components from spritepad and charpad files (.spd version 5 and .ctm version 8). + +The component to extract (e.g. chars or tiles) is added as a further specifier. + + const char FloorChars[] = { + #embed ctm_chars lzo "floortiles.ctm" + }; + +Imports the character data and compresses it using lzo compression. + + const char CardsTiles[] = { + #embed ctm_tiles8 "cards.ctm" + }; + +Imports the tiles in 8 bit form and builds byte constants + + const unsigned CardsTiles[] = { + #embed ctm_tiles16 word "cards.ctm" + }; + +Imports the tiles in 16 bit form and builds word constants + + const char CardsTiles[] = { + #embed ctm_tiles8sw "cards.ctm" + }; + +Imports the tiles in 8 bit form and builds byte constants but reorders the array dimensions to have the tile index being the innermost array + + const char CardsMap[] = { + #embed ctm_map8 "cards.ctm" + }; + +Imports the map (screen) in 8 bit form and builds byte constants + + const unsigned CardsMap[] = { + #embed ctm_map16 word "cards.ctm" + }; + +Imports the map (screen) in 16 bit form and builds word constants + + const char CardsAttrib1[] = { + #embed ctm_attr1 "cards.ctm" + }; + + const char CardsAttrib2[] = { + #embed ctm_attr2 "cards.ctm" + }; + +Imports the attribute data + + const char SpriteData[] = { + #embed spd_sprites lzo "sprites.spd" + }; + +Imports the sprite data and compresses it using lzo compression + + const char SpriteTiles[] = { + #embed spd_tiles "sprites.spd" + }; + +Imports the sprite tile index table in byte form + + +## Console input and output + +The C64 does not use ASCII it uses a derivative called PETSCII. There are two fonts, one with uppercase and one with uppercase and lowercase characters. It also used CR (13) as line terminator instead of LF (10). The stdio and conio libraries can perform translations. + +The translation mode is selected in conio with the variable "giocharmap" and the function "iocharmap" which will also switch the font. + + iocharmap(IOCHM_PETSCII_2); + printf("Hello World\n"); + +Will switch to the lowercase PETSCII font and translate the strings while printing. + +PETSCII string literals can also be generated using a "p" or "P" prefix such as: + + printf(p"Hello World\n"); + +Screen codes can be generated similar using "s" or "S" prefix. + +Input from the console will also be translated accordingly. + +The character map for string and char constants can be changed with a pragma to match a custom character set or PETSCII. + + #pragma charmap(char, code [,count]) + +There is also a compiler argument -psci that switches all strings to be translated as PETSCII per default. + + +## Additional Optimizer information using __assume() + +The compiler can be provided with additional information using the built in function __assume(cond). This can be useful to mark unreachable code using __assume(false) for e.g. the default of a switch statement. Another good option is to limit the value range of arguments to allow the compiler using byte operations without the need for integer promotion. + +## Function level optimizer settings + + #pragma optimize(option {,option}) + +Set optimizer options that are active for the functions after it + +* push : put current options on a stack +* pop : get options back from stack +* asm : allow optimization of assembler code +* noasm : prevent optimization of assembler code +* size : optimize for size +* speed : optimize for speed +* noinline : no inlinening in any case +* inline : inline if requested with inline storage attribute +* autoinline : auto inline of small or only once used functions +* maxinline : inline any function suitable +* constparams : enable constant parameter folding into called functions +* noconstparams : disable constant parameter folding into called functions +* outline : enable outliner +* nooutline : disable outliner +* selfmod : enable generation of self modifying code +* noselfmod : disable generation of self modifying code +* 0 : no optimization +* 1 : default optimizations +* 2 : aggressive optimizations +* 3 : even more aggressive optimizations + + +## Loop unrolling + +Loop unrolling on 6502 is hard to decide for the compiler. Memory is usually scarce, so it only does it in really obvious cases (and in less obvious cases for O3). On the other hand unrolling is required to get good performance in e.g. scrolling code. Therefore the compiler offers an unrolling pragma, that can be used to specify the amount of unrolling either as a number or "full" for complete. This pragma is always only applied to the next loop, following it. + +The following code scrolls the screen to the left, and completely unrolls the inner vertical loop. + + for(char x=0; x<39; x++) + { + #pragma unroll(full) + for(char y=0; y<25; y++) + screen[y][x] = screen[y][x + 1]; + } + +Sometimes it is better to unroll the loop not in the order it normally executes, but using page size chunks. When e.g. filling a C64 screen with a loop: + + for(int i=0; i<1000; i++) + Screen[i] = ' '; + +Unrolling this loop would not help, the index would still not fit into the 8 bit x or y register. Using a page level unroll, the compiler will unroll the loop into four stores, each 250 bytes apart, and use the y or x register for indexing: + + #pragma unroll(page) + for(int i=0; i<1000; i++) + Screen[i] = ' '; + + 0921 LDY #$00 + 0923 LDA #$20 + 0925 STA $0400,y + 0928 STA $04fa,y + 092b STA $05f4,y + 092e STA $06ee,y + 0931 INY + 0932 CPY #$fa + 0934 BCC $0925 + + +## Striped arrays + +The 6502 has no real address registers for indirect/offset addressing and no native multiply instruction. Accessing array elements of structs or integers is thus more expensive than on most other processors. The index registers and the byte structure of the processor work nice with arrays of byte that are up to 256 elements in size. An array of structs (or 16 bit integers/pointers) could thus be split into individual arrays of bytes for each element, allowing fast access using the index registers. Oscar supports this layout with the __striped storage qualifier. + + int array[8]; + +The memory layout of this array will be + + LHLHLHLHLHLHLHLH + +so the compiler has to multiply the index by two to access each element. A striped layout + + __striped int array[8]; + +will result in this memory structure: + + LLLLLLLLHHHHHHHH + +so the compiler can use absolute with index to access each element. + +Similar for a struct: + + struct A {char x; char y; char z} a[5]; + + xyzxyzxyzxyzxyz + + __striped struct A {char x; char y; char z} a[5]; + + xxxxxyyyyyzzzzz + +The downside of this layout is the inability to have a native pointer to reference one of the elements. The pointer would have to know the memory layout of the array. Oscar borrows the "auto" keyword from C++ to enable some of this functionality: + + auto pa = a + 4; + pa->x = 1; + +This feature is still experimental and only a benefit, if the array has not more than 256 elements. + +## Marking functions as native + +Routines can be marked to be compiled to 6502 machine code with the native pragma: + + void Plot(int x, int y) + { + (*Bitmap)[y >> 3][x >> 3][y & 7] |= 0x80 >> (x & 7); + } + + #pragma native(Plot) + +Or alternatively with a __native storage class specifier + + __native void Plot(int x, int y) + { + (*Bitmap)[y >> 3][x >> 3][y & 7] |= 0x80 >> (x & 7); + } + +## Usage of zero page + +The compiler uses basic zero page space for temporaries, local variables and function arguments. Global and static variables are allocated in the data and bss segment in normal memory. The __zeropage storage class specifier can be applied to global variables and will be allocated in the zero page segment (usually 0x80 to 0xff). This is only useful when the kernal is not used. Zero page allocated global variables are not initialized. + + __zeropage int a; + +## Memory consistency + +The C use of volatile requires memory accesses with this type qualifier to not be reordered regarding other volatile memory accesses, cached or optimized away. This is on one hand too strong a limitation (for e.g. video memory) on the other side a too weak ordering mechanism when using banked memory. The change of a memory bank may not be reordered with any other memory access that would fall into the banked region. + +To provide this additional memory fence the compiler adds the __memmap type qualifier that prevents any memory access to be reordered around an access to a memory location with this qualifier: + + char mmap_set(char pla) + { + char ppla = *((char *)0x01); + *((volatile __memmap char *)0x01) = pla; + return ppla; + } + +So the following recommendations for volatile and __memmap should be used: + +* volatile : for hardware registers or variables that are shared with interrupts +* __memmap : hardware registers that change the memory layout (e.g. bank registers or mmu) +* no qualifier for accesses that may be reordered or cached such as video or sprite memory + + +## Prevent inlining + +With compiler option O2 and greater the compiler will try to inline small functions. This may not always be desirable, so the __noinline qualifier can be added to a function to prevent this. + + __noinline byte EzRead(char bank, const char * p) + { + eflash.bank = bank; + byte b = *p; + eflash.bank = CodeBank; + return b; + } + +## Force inlining + +A function can be forced to be inlined with the __forceinline storage qualifier. + +The call to a function can also be forced to be inlined, even if the function is not marked as inline in its declaration: + + #pragma callinline() + oscar_expand_lzo((char *)0xe800, finlay); + +This only applies to the next call and not the called function in general. + +## Pre-Processor control + +The pre processor has additional commands to control the scanner and allow for dynamic code generation including loops. + +Assign a computed value to a pre processor macro + + #assign + +Loop source code + + #repeat + ... + #until + +This sample fills a single screen column with a given color, by generating 25 assigns to absolute addresses. + + void color_column(char cx, char color) + { + #assign ry 0 + #repeat + Color1[40 * ry + cx] = color; + #assign ry ry + 1 + #until ry == 25 + #undef ry + } + +This sample initially assigns the value 0 to the pre processor macro ry and increments it each time the loop body is replicated. The loop generates 25 copies of the body, each with a different value for ry. + +A simpler loop with only a single line template expansion is provided with the for preprocessor command: + + #for(, ) + +This sample generates an array with pointers to screen rows: + + char * const ScreenRows2[] = { + #for(i,SCREEN_HEIGHT) Screen + SCREEN_WIDTH * i, + }; + +The preprocessor keeps track of the source location while parsing a file to generate error messages and debug symbols. This is problematic for generated code, where the C source file position does not reflect the actual source position. + +The source position can be set with the #line directiv + + #line linenum + + sets the line number + + #line linenum "sourcefile" + + sets the line number and source file name + + +## Linker control + +The linker includes only objects that are referenced, starting by the startup code into main() and so on. + +If you need to have a function or variable present regardless, you can specify it with the __export storage class specifier or use the #pragma reference(name) pragma. + +### Using libraries + +The compiler does a full program compile, the linker step is part of the compilation. It knows all functions during the compilation run and includes only reachable code in the output. Source files are added to the build with the help of a pragma: + + #pragma compile("stdio.c") + +This way you do not need a makefile to build your project. All header files of the provided libraries add their implementation to the build using this pragma. + +### Placement + +The linker uses three levels of objects: + +* Region : A physical region of memory or a bank in a cartridge +* Section : A logical region of memory, may span several sections +* Object : Generated code or data, either initialized or empty (e.g. stack or bss) + +With the default prg target and no further changes, the compiler creates the following regions and sections: + +* "startup" : **0x0801-0x0900** Basic and assembler startup code and interpreter loop + * "startup" +* "bytecode" : **0x0900-0x0a00** Interpreter jump table if not all native + * "bytecode" +* "main": **0x0a00-0xa000** Main region for code, data, bss, heap and stack + * "code" : Compiled code + * "data" : Constant data + * "bss" : Non constant data, initialized to zero on program start + * "heap" : Memory available for allocation + * "stack" : Data stack + +The layout can be changed using #pragma commands. One may e.g. use all memory up to 0xd000 with the following code: + + #include + + #pragma region( main, 0x0a00, 0xd000, , , {code, data, bss, heap, stack} ) + + int main(void) + { + mmap_set(MMAP_NO_BASIC) + +The parameters for a region pragma are: + +* Name of the region to be referenced by code or data pragma +* Start address in memory +* End address (exclusive) +* Flags +* Optional bank for cartridge/overlay +* List of sections to place into this region +* Optional runtime start address (for code that is copied) + +Regions can also be used to place assets such as character sets at fixed location in the prg file to avoid copying: + + #pragma region( lower, 0x0a00, 0x2000, , , {code, data} ) + + #pragma section( charset, 0) + + #pragma region( charset, 0x2000, 0x2800, , , {charset} ) + + #pragma region( main, 0x2800, 0xa000, , , {code, data, bss, heap, stack} ) + + #pragma data(charset) + + char charset[2048] = { + #embed "../resources/charset.bin" + } + + #pragma data(data) + +The #pragma data(), #pragma code() and #pragma bss() control the placement of the generated objects into sections other than the default sections. + +A global variable or function can be aligned on a given power of two start with the align pragma. This is most useful if a page crossing is problematic. The compiler may also be able to generate more efficient code, if a larger variable is page aligned. + + #pragma align(myvar, 8) + #pragma align(myfunc, 256) + +### Additional BSS sections + +Additional bss sections can be defined on request. + + #pragma section( mybss, 0, , , bss ) + #pragma region( mybssregion, 0xc000, 0xd000, , , {mybss} ) + +They will not be cleared on startup, so all variables placed in these segments will be uninitialized. A common use for these sections is overlapping runtime variables with compressed load time assests that get expanded to their actual location during startup. + +### Heap and Stack sections + +The heap and stack sections have an initial size of 1K and 4K. The heap will grow to use all space between the end of the data/bss section and +the start of the stack section. The size of the stack and the minimum size of the heap can be specified using pragmas: + + #pragma stacksize( 4096 ) + + #pragma heapsize( 4096 ) + +The linker will throw an error if the heap or stack cannot be placed without collision. If the program does not use the heap, the heapsize can be set to zero. + + +### Cartridge banks + +The compiler supports up to 64 individual banks for cartridge code. + + #pragma section( bcode1, 0 ) + #pragma section( bdata1, 0 ) + #pragma region(bank1, 0x8000, 0xc000, , 1, { bcode1, bdata1 } ) + +The __bankof operator returns the bank id of a function or constant placed into a ROM bank. The call of __bankof(0) returns the bank id of the calling code itself. + + +### Overlays + +The linker can generate overlay files that are stored as .prg in the .d64 when selected as target. + +The mechanism uses the cartridge bank to denote up to 64 overlays. Each bank can be associated with one overlay file, which will then contain the used content of this bank. + + #pragma overlay( ovl1, 1 ) + +The overlay file can then be loaded in memory during program execution: + + krnio_setnam(P"OVL1"); + krnio_load(1, 8, 1); + +### Inlays + +Inlays are sections that are compressed into constant arrays by the linker and can then be expanded by the program on demand. The array has to be declared with unknown size. + + const char Inlay1[]; + #pragma section( icode1, 0 ) + #pragma region( isec1, 0xc000, 0xd000, , Inlay1, { icode1 } ) + +The size of the array is not known at compile time, so a sizeof() will return 0. The oscar_expand_lzo can be used to expand the code and does not need the size: + + oscar_expand_lzo((char *)0xc000, Inlay1); + + +### NES ROM Banks + +With NROM mappers, the prg and chr code is put into cartridge bank zero. + + #pragma section( tiles, 0 ) + + #pragma region( tbank, 0x0000, 0x2000, , 0, { tiles } ) + + #pragma data(tiles) + + __export char tiles[] = { + #embed "nesmini.chr" + }; + + #pragma data(data) + +The 32KByte of prg code starts at 0x8000 and goes up to 0xff80. A startup section from 0xff80 to 0xfff9 is taken from the crt.c. + +A six byte boot section is placed from 0xfffa to 0xffff in all modes, which has to be populated by the game code with the appropriate pointers. + + #pragma data(boot) + + __export struct Boot + { + void * nmi, * reset, * irq; + } boot = { + nmi, + (void *)0xff80, + nullptr + }; + + #pragma data(data) + +Rom mappers MMC1 and MMC3 share the last rom bank from 0xc000 to 0xffff including the boot section. This is bank 15 in MMC1 and bank 31 in MMC3. + +The pageable ROM is assumed to be 0x8000 to 0xbfff in all banks from zero up. + + #pragma section( code0, 0 ) + #pragma region( code0, 0x8000, 0xc000, , 0, { code0 } ) + +And for bank one + + #pragma section( code1, 0 ) + #pragma region( code1, 0x8000, 0xc000, , 1, { code1 } ) + + +## Inline Assembler + +Inline assembler can be embedded inside of any functions, regardless of their compilation target of byte code or native. + +### Accessing variables in assembler + +Access to local variables and parameters is done with zero page registers, global variables are accessed using absolute addressing. + + void putchar(char c) + { + __asm { + lda c + bne w1 + lda #13 + w1: + jsr 0xffd2 + } + } + +A function return value can be provided in the zero page addresses ACCU (+0..+3). + + char getchar(void) + { + return __asm { + jsr 0xffcf + sta accu + lda #0 + sta accu + 1 + }; + } + +Struct member offsets can be generated with the Type::Member syntax: + + struct P + { + unsigned char np; + unsigned char ns; + + void test() + { + auto that = this; + + __asm { + lda #1 + ldy #P::ns + sta (that),y + } + } + }; + + +Labels are defined with a colon after the name. Pure assembler functions can be defined outside of the scope of a function and accessed using their name inside of other assembler function. One can e.g. set up an interrupt. + + +### Interrupt routines + +The compiler provides two levels of interrupt safe functions. The specifier __interrupt causes all zero page registers used by the function to be saved, the __hwinterrupt also saves the CPU registers and exits the function with rti + + + #include + #include + #include + + __hwinterrupt void irq(void) + { + vic.color_border++; + + // some interrupt code + + vic.color_border--; + vic.intr_ctrl <<= 1; + } + + int main(void) + { + __asm { sei } // Disable interrupt + mmap_set(MMAP_NO_ROM); // Disable kernal rom + cia_init(); // No more CIA interrupts + + *(void **)0xfffe = irq; // Install interrupt routine + vic.intr_enable = VIC_INTR_RST; // Init raster interrupt + vic.ctrl1 &= ~VIC_CTRL1_RST8; + vic.raster = 100; + + __asm { cli } // Re-enable interrupt + + for(;;) + { + // Non interrupt code + } + + return 0 + } + +### Assembler optimizer + +The compiler uses various optimizations on inline assembler that may not have the expected result in all cases. The optimizer can be disabled for a range of code with e.g. #pragma optimize(noasm) or for an individual __asm statement by using __asm volatile {}. +The assembler optimizer is enabled with optimization levels O2 and up. + +# Helping the compiler optimizing + +The compiler does various optimization when compiling the code, but there are some rules to follow, things to avoid and hints to place to help the compiler to generate optimal code. + +## Avoid Recursion + +C is a stack style language. All parameters and local variables are per default placed on a call stack. The 6502 on the other hand is not a stack friendly architecture. The built in stack is too small for parameters or variables, and an additional software stack relies on the rather expensive zeropage indirect addressing mode. The compiler therefore tries to avoid the dynamic stack by analysing the call graph and creates a static stack. This fails in the presence of recursion - various incarnations of the parameters and local variables of a function can be on the stack at the same time. + +## Avoid Function Pointers + +A similar problem to recursion happens with function pointers, which also complicate or prevent call graph analysis. The compiler must therefore fall back on stack variables. In many cases switch statements are faster than function pointers. + +## Be aware of aliasing + +Aliasing in memory terms happens, when the same memory location could be accessed through more than one pointer. Aliasing can create false memory dependency, requiring the compiler to create load and store instructions that would not really be needed. The compiler performs aliasing analysis to detect potential aliasing but has to be very conservative and avoid any optimization that would fail if the potential alias would be an actual alias. + +Most important thing to look at are loop conditions or values in a loop, that could be aliased to a store inside the loop, which will prevent many loop optimizations. + +## Prefer unsigned arithmetic + +Signed arithmetic such as shift, compare or multiply are more expensive than their unsigned counterpart. So if you know that the value cannot be negative, stick with unsigned variables types. + +## Stick to eight bits if possible + +The 6502 is an eight bit processor, all 16 bit operations are much more expensive. The compiler therefore tries to reduce arithmetic to eight bit whenever it can proof that the actual numbers involved would fit into eight bit. This fails for variables addressed through pointers or global variables, that may be changed in a non predictable way. So whenever you know that the number in question would fit into eight bit, use an eight bit type. + +## Prefer enums over defines + +An enum type has a limited value range, thus allowing various optimizations, which would not be possible with constants created by defines. + +## Give the compiler hints + +The __assume keyword gives the programmer the ability to tell the compiler things that cannot be easily expressed in terms of the language. Examples are: + +* Unreachable default cases in switch statements with __assume(false) +* Limited value ranges with __assume(x < 10) +* Non null pointers __assume(p != nullptr) + +## Mark global constants as const + +Marking a global value as const helps the compiler determine, that the value will not change, and may not have to be loaded from memory at all. Knowing the value range of array members also improves the integer range estimation by the compiler. + +In this case the compiler knows, that all values read from the array, will be in the range 1 to 4 + + const char vals[] = {1, 2, 3, 4} + +Using a constant pointer will allow absolute addressing + + char * const Color = (char *)0xd800; + + + +# Samples + +## Character input and output "stdio" + +### helloworld.c + +## Disk file access "kernalio" + +The C64 uses various kernal routines to read and write files on disk. These routines are available with a library All samples in this directory use drive 9, but can easily be changed to drive 8. + +### Reading the directory "diskdir.c" + +Reads the directory from the current disk in drive 9 and displays it on screen. + + +### Writing characters "charwrite.c" + +Opens a ".prg" file on drive 9 for writing and writes 128 characters into the file. + +### Reading characters "charread.c" + +Opens a ".prg" file on drive 9 for reading, reads all characters from the file and prints there character code on screen. + +### Writing binary data "filewrite.c" + +Opens a ".prg" file on drive 9 for writing and writes an array of structs as binary data into the file. + +### Reading binary data "fileread.c" + +Opens a ".prg" file on drive 9 for reading and reads an array of structs as binary data into the file. + +### Writing image data "hireswrite.c" + +Renders a hires image into a buffer at 0xe000..0xff40 and saves it to disk. The added complexity is, that the kernal itself cannot access the memory in this region because it is covered by the kernal ROM itself. The operation is therefore implemented using a 200 byte RAM buffer. + +### Reading image data "hiresread.c" + +Reads a hires image from disk into a buffer at 0xe000..0xff40 and displays it. The read can be performed without a secondary buffer, because writes to the ROM end up in the RAM underneath. + +### Fasload image data "hiresfload.c" + +Use the oscar flossiec fast loader to load a compressed multicolor image to 0xe000..0xff40 and 0xcc00..0xcfff. + +## Remapping memory "memmap" + +The C64 memory map is very flexible. These samples show how to manipulate the memory map and use easyflash ROMs to execute larger programs. + +### Using more memory "largemem.c" + +Moves the BASIC ROM out of the way and allows the use of memory from 0x0800..0xcfff for the compiled C program. + +### Using all memory "allmem.c" + +Moves the BASIC ROM, Kernal ROM and the IO area out of the way and allows the use of memory from 0x0800 to 0xfff0 for the compiled C code. An interrupt trampoline is installed to keep the kernal going. + +### Custom character set "charsetlo.c" + +Embeds a custom character set into the prg file at 0x2000..0x27ff and switches the character set base address in the VIC to this address. The code and data portion of the compiled program is split into two areas to make room for this fixed location data. + +### Himem character set "charsethi.c" + +Embeds a custom character set into the prg file at 0xc800..0xcfff and switches the character set base address in the VIC to this address. + +### Copy character set "charsetcopy.c" + +Embeds a custom character set into the prg file at 0xc000..0xc7ff and copies it to 0xd000 on startup. This frees this area for stack and heap usage. + +### Copy character set "charsetexpand.c" + +Embeds a custom character set into the prg file at 0xc000..0xc7ff using lz compression and expands it to 0xd000 on startup. This frees this area for stack and heap usage. + +### Custom character set "charsetload.c" + +Builds a .d64 image containing the compiled .prg and the compressed character set. The program reads the character set at runtime from disk. + +### Easyflash banking "easyflash.c" + +When compiling for easyflash, the linker will place the code and data section into bank 0 and copy it to 0x0900..0x7fff at startup. The remaining banks are free to be used for data or additional codes and can be banked in as needed. This sample uses banks one to six for additional functions. + +### Easyflash common area "easyflashshared.c" + +This sample reserves a fixed area of several banks for a common code segment. A multiplexer function is placed into this common segment to select a different bank and call a function in that bank. + +### Easyflash relocated area "easyflashreloc.c" + +An alternative to calling the function in the cartridge ROM itself is to copy it to a different location in RAM and execute it there. This sample creates functions in different ROM banks that are linked for a memory area in RAM at 0x7000. The code is copied from the ROM to RAM and then executed. + +### Easyflash low memory usage "easyflash.c" + +This sample will use the memory area starting from 0x0400 for the main code section when copying the code, using the stack page 0x100 for the startup itself, thus wasting small amount of RAM space. + +### Dynamic overlays "overlay.c" + +When compiling for .d64 format, the linker will place code and data sections from virtual cartridge banks into overlay files. These files can be loaded when needed and called using normal function calls. + + +### Terminate stay resident "tsr.c" + +A common usage for the RAM area in 0xc000..0xcfff which is visible but not usable from BASIC is to install a terminate stay resident BASIC extension, which can then be called using sys 49152. + +It would be a waste of disk space and load time to load this directly into 0xc000 together with a BASIC start code at 0x0800. This sample uses the linker to link the code for the TSR at 0xc000 but place it into the prg at 0x0900. The startup then copies this code to 0xc000. + +## Hires graphics "hires" + +The C64 has a hires graphics mode with 320x200 pixels. Oscar provides a library for rendering in on screen and off screen bitmaps. + +### Draw lines "lines.c" + +Draws and clears lines with various patterns. + +### Draw lines "fractaltree.c" + +Draws a recursive fractal tree. + +![Fractal recursive tree](samples/hires/fractaltree.png) + +### Draw 3D wireframe "cube3d.c" + +Draws a rotating 3D wireframe cube using draw (OR) and clear (AND) operations. The 3D operations are performed using 12.4 bit fixpoint math. + +### Software bit blit engine "bitblit.c" + +![Various bitblit modes](samples/hires/bitblit.png) + +Demonstrates the various bit blit modes of the software blit engine, including mask and pattern. + +### Draw polygons "polygon.c" + +Draws a series of pattern filled stars with changing orientation and size. + +### Mixed text and hires screen "splitscreen.c" + +Uses the library to split the screen into an upper hires and lower text mode screen. The text area is used to query the user for x, y and radius of circles to draw. + +### 3D Function plotter "func3d.c" + +![3D Function plotter](samples/hires/func3d.png) + +Draws a 3D function using flat shading and the painters algorithm. All 3D operations are performed with floating point numbers using the vector library . + +## Multicolor bitmaps "hiresmc" + +The C64 bitmap graphics mode can also use 2bit per pixel, resulting in an image of 160x200 with four colors. The oscar library implements various drawing operations in this mode. All x coordinates still range from 0 to 320 to keep a quasi square pixel layout. + +### Draw polygons "polygon.c" + +![Colored stars](samples/hiresmc/polygon.png) + +Similar to its hires counterpart but using different colors and patterns. + +### Fill similar colored areas "floodfill.c" + +Draws filled random circles and fills the space using flood fill. + +### Painting with mouse or joystick "paint.c" + +Paint on the screen with mouse or joystick. Keyboard 0..9 for color selection, HOME to clear the screen. + + +### 3D Function plotter "func3d.c" + +Similar to its hires counterpart but using four shades of grey. + + +## Particle systems "particles" + +Active hires graphics using particle system + +### Hires fireworks "fireworks_hires.c" + +Simple hires particle system using velocity and gravity integration. + +### Multicolor fireworks "fireworks_ptr.c" + +Simple multi color hires particle system using velocity and gravity integration. + +### Fireworks with striped memory layout "fireworks_stripe.c" + +Simple multi color hires particle system using velocity and gravity integration. This version uses striped memory layout for the particles to simplify addressing for the CPU. + + +## Mandelbrot renderer "fractals" + +Various versions of the mandelbrot set using float arithmetic. + +### Text mode fractal "mbtext.c" + +Simple mandelbrot renderer using text cells and colors to generate a 40x25 pixel image. + +### Hires fractal "mbhires.c" + +Hires version using black and white to show the mandelbrot set. + +### Multi color fractal "mbmulti.c" + +Multi color version using pure and mixed colors. + +### 3D shaded fractal "mbmulti3d.c" + +![Mandelbrot fractal in 3D](samples/fractals/mbmulti3d.png) + +Mandelbrot rendered in 3D with shading. The image is drawn in columns from back to front, using two adjacent columns to calculate slope and brightness. + +### Interactive Navigate a fractal "mbzoom.c" + +Navigate using WASD and zoom using + and -. + + +## Raster beam interrupts "rasterirq" + +Interrupts based on the raster beam are an important part of the C64 programmers toolbox. Switching VIC registers on specific lines opens up many additional features, such a more sprites using multiplexing, combining modes or changing colors or scroll offsets. The library provides easy access to this feature using on the fly code generation. + +### Static color changes "colorbars.c" + +![Color bars](samples/rasterirq/colorbars.png) + +Changes the background and border colors several times per frame, creating horizontal color bars. + +### Crawling text at bottom "textcrawler.c" + +Draws a scrolling line of text at the bottom of the screen. + +### Crawling text at bottom in IRQ "autocrawler.c" + +Draws a scrolling line of text at the bottom of the screen, using an interrupt to update the text. + +### Chasing bars "movingbars.c" + +Changing the background and border color at varying vertical positions giving the impression of two chasing colored bars. + +### Freedom for sprites "openborders.c" + +Open the vertical screen borders by switching the vertical size bit at the appropriate raster lines. The opened area is available for sprites only. + + + +## Expand the screen "scrolling" + +Scrolling is an important component of many games and editors, it extends the limited real estate of the screen to provide a small view to a larger world or document. Pixel accurate scrolling is an important feature of the C64's VIC chip. + + +### Large scrolling text "bigfont.c" + +![Large text](samples/scrolling/bigfont.png) + +Expands a text to gigantic size, each pixel covering 2x2 character cells and scrolling from right to left. + +### Fly through tunnel "tunnel.c" + +Scroll a dynamic generated tunnel with variable speed. + +### Scroll text and color "colorram.c" + +Scrolls the screen text buffer and color buffer horizontally at the same time with one pixel per frame and no double buffering, relying on exact raster timing. + + +### X/Y colored tile scrolling "cgrid8way.c" + +Expands coloured 4x4 character tiles and scrolls vertically and horizontally at two pixel per frame without double buffering. + +### Free full speed tile expansion "grid2d.c" + +Expands a 2D 4x4 tile grid at any scroll speed. Uses a raster IRQ to limit the scrolled area. + + +## Moving image blocks "sprites" + +Sprites are independent image blocks, such as players, missiles or enemies that can be shown on top of the background. + +### Control a sprite with a joystick "joycontrol.c" + +Combines reading the joystick with the library and sprite movement with the library. + +### Use raster IRQ to show 16 sprites "multiplexer.c" + +![Large text](samples/sprites/multiplexer.png) + +Shows 16 virtual sprites multiplexed from the physical eight sprites with raster interrupts, using the oscar sprite multiplexer library. + +### Use raster IRQ to show 32 sprites "sprmux32.c" + +![Large text](samples/sprites/sprmux32.png) + +Shows 32 virtual sprites multiplexed from the physical eight sprites with raster interrupts, using the oscar sprite multiplexer library. This sample requires command line defines to increase the default number of multiplexed sprites and raster interrupt slots: + + oscar64 -n sprmux32.c -O2 -dVSPRITES_MAX=32 -dNUM_IRQS=28 + +### Use raster IRQ to show 64 sprites "sprmux64.c" + +![Large text](samples/sprites/sprmux64.png) + +Shows 64 moving sprites using raster interrupts to update the position of the eight physical sprites every 25 screen lines. + + +### Fill the screen with sprites "creditroll.c" + +![Many sprites](samples/sprites/creditroll.png) + +Uses sprite multiplexing and dynamic character to sprite copying to create a vertical scrolling text on top of any image or potentially moving background. + +## What you came for "games" + +The C64 games define much of the 8 bit area and oscar64 is intended as a of a proof that one can write great 8 bit games using C. + + +### Don't bite your end "snake.c" + +One of the least complex computer game and more iconic for the smartphone area. Does not need sprites, scrolling or custom graphics. + +### The eagle has landed "lander.c" + +![Landing the eagle](samples/games/lander.png) + +Controlling a sprite using floating point physics and checking for collision with the background. + +### No way out "maze3d.c" + +![Move through the maze](samples/games/maze3d.png) + +A double buffered 3D maze rendering using intermediate positions for forward movement, and simulated rotation using scrolling. + +### This one has balls "breakout.c" + +![Breakout](samples/games/breakout.png) + +Simplified ball physics using fixed point math. More complex collision checks with the blocks to determine the edge and thus reflection. + +### Too smart for me "connectfour.c" + +![Connect Four](samples/games/connectfour.png) + +Simple turn based strategy game using tree search with alpha-beta pruning and an opening library to determine the computer moves. + +### Defend your city "missile.c" + +![Defend your city](samples/games/missile.png) + +Classic computer game using the multicolor hires mode for circles and lines. The user sprite is controlled in an interrupt routine to cover up the multi frame variable rendering. + +### Fast scroller "hscrollshmup.c" + +![Scroll and Shoot](samples/games/hscrollshmup.png) + +Fast horizontal scrolling shoot 'em up. The scroll code expands a 4x4 tile grid of chars in less than a frame time, so the scroll speed is "unlimited". A parallax level of stars in the background is implemented using an adapted star character. + +The shots usd dynamic created characters to overlay on the background. + + + +# Compiler Targets + +The compiler supports various target machines and formats. The selection is done on the command line with the -tm and -tf options. + +## Adding a new target + +Adding a new target and format requires several changes. + +### Defines "CompilerOptions.h" + +Here we have two major elements, a flag set **COPT_** and an enum **TargetMachine**. The enum is easy to expand but the flagset requires some consideration, whether is is really necessary to consume one of the rare flags. + +### Command Line Parser "oscar64.cpp" + +Look for the rather long if statement that checks for the targetMachine string and sets appropriate flags and defines, e.g.: + + else if (!strcmp(targetMachine, "plus4")) + { + strcpy_s(basicStart, "0x1001"); + compiler->mTargetMachine = TMACH_PLUS4; + compiler->AddDefine(Ident::Unique("__PLUS4__"), "1"); + compiler->AddDefine(Ident::Unique("__CBM__"), "1"); + } + + +### Setting up memory regions "Compiler.cpp" + +The *Compipler::GenerateCode* method sets up the default sections and regions based on the target machine and format. + +The compiler zero page usage is right at the beginning of this method. + +* BC_REG_WORK_Y 1 Byte +* BC_REG_WORK 10 Bytes +* BC_REG_IP 2 Bytes +* BC_REG_ACCU 4 Bytes +* BC_REG_ADDR 2 Bytes +* BC_REG_STACK 2 Bytes +* BC_REG_LOCALS 2 Bytes + +Zero page registers for fast parameter passing + +* BC_REG_FPARAMS..BC_REG_FPARAMS_END 12 Bytes to 24 Bytes + +The next two are the compiler work registers and have to follow each other. + +* BC_REG_TMP 16 Bytes to 32 Bytes +* BC_REG_TMP_SAVED 16 Bytes to whatever + +The zero page region is created if none is provided in the compiled source using an if cascade that checks for the targetMachine, add your own case here: + + else if (mTargetMachine == TMACH_PET_8K || mTargetMachine == TMACH_PET_16K || mTargetMachine == TMACH_PET_32K) + regionZeroPage = mLinker->AddRegion(identZeroPage, 0x00ed, 0x00f7); + +Next the startup region that is either used for the basic startup or the rom reset vectors is created if none is provided. If you create a prg type format look for the commodore targets, for rom targets the NES types might be a good example. You may also need a boot section for rom code. + +Finally the main region that covers your code and data. Again for rom targets, have a look at the NES machine types or the COPT_TARGET_CRT16. + +### Generating the output files "Compiler.cpp" and "Linker.cpp" + +The *Compiler::WriteOutputFile* selects and triggers the files to generate. This again depends on the target machine and format. If you add a new prg type target follow the TMACH_ATARI for a ROM based one, you can either look at the NES or the CRT version. Add a new WriteXXXFile() function to the linker. + +The linker contains various methods to write the actual binary file for the system or drive emulators (or EPROM burners). Take a look at e.g. *Linker::WriteXexFile* for prg files and *Linker::Linker::WriteNesFile* for rom formats. + +### The startup code "library/crt.h" + +The startup stub will have to be adapted to your new target machine: + + __asm startup + +Look for the "__ATARI__" or "OSCAR_TARGET_NES" defines where your code might go. Some targets may need an additional set of boot vectors in the rom e.g.: + + #pragma data(boot) + __export struct Boot + { + void * nmi, * reset, * irq; + } boot = { + nmi, + (void *)0xff80, + nullptr + }; + #pragma data(data) + + + + + + + + +# Implementation Details + +## The bytecode interpreter + +The byte code interpreter is compiled by the compiler itself and placed in the source file "crt.c". Functions implementing byte codes are marked with a pragma: + + #pragma bytecode(BC_CONST_P8, inp_const_p8) + +The functions are written in 6502 assembly with the __asm keyword + + __asm inp_const_p8 + { + lda (ip), y + tax + iny + lda (ip), y + sta $00, x + lda #0 + sta $01, x + iny + jmp startup.exec + } + +The current byte code program counter is (ip),y. The compiler and interpreter loop guarantees that y is always <= 240 and can thus be used to index the additional byte code arguments without the need to check the 16 bit pointer. The interpreter loop itself is quite compact and takes 19 cycles (including the final jump of the byte code function itself). Moving it to zero page would reduce this by another two cycles but is most likely not worth the waste of temporary space. + + exec: + lda (ip), y + sta execjmp + 1 + iny + execjmp: + jmp (0x0900) + +Only JMP/BRANCH and NOP bytecodes check for the y register to exceed it's allowed range of 0..240. The compiler ensures that there are no linear code sequences longer than 240 bytes by inserting NOP bytecodes at appropriate distances. + +## Zero page usage + +The intermediate code generator assumes a large number of registers so the zero page is used for this purpose. The allocation is not yet final (and can be changed using pragmas): + +* **0x02-0x02** spilling of y register +* **0x03-0x0c** workspace for mul/div and floating point routines +* **0x0d-0x1a** function arguments for leaf functions +* **0x19-0x1a** instruction pointer +* **0x1b-0x1e** integer and floating point accumulator +* **0x1f-0x22** pointers for indirect addressing +* **0x23-0x24** stack pointer +* **0x25-0x26** frame pointer +* **0x43-0x52** caller saved registers +* **0x53-0x8f** callee saved registers + +## Compiler Stages + +The compiler uses most of the traditional stages: + +### Scanner/Pre-Processor + +The scanner and pre processor phases are interleaved (causing subtle incompatibilites with C) but allowing additional features such as loop expansion. + +### Parser + +The parser uses an extended recursive descent algorithm and uses already parsed information to control the parsing. It greates a graph built of "Declaration" objects. It performs basic constant folding and limited type checking. + +### Global Analyzer + +The global analyzer looks at the functions of all compilation modules and decides the calling convention and inlining for all functions. + +### Intermediate Code Generator + +The intermediate code generator takes the declaration graph and converts it into intermediate code and linker data objects. This step also performs the final type checks and additional constant folding. Inlining is also performed in this step. + +### Intermediate Code Optimizer + +The intermediate code optimizer works on the intermediate code to perform machine agnostic optimizations, such as value forwarding, strength reduction or loop unrolling. It also tries to build evaluation trains to reduce the amount of temporaries required for execution. + +### Native/Bytecode Generator + +Translates the intermediate code into symbolic bytecode or 6502 assembler instructions. The code generator looks at several instructions at the same time to pick good native instructions. + +### Native/Bytecode Optimizer + +The symbolic assembler code is then further optimized using a specialized optimizer. + +### Native/Bytecode Assembler + +This stage translates the symbolic assembler / bytecode into binary and makes it available for the linker. It also calculates the optimal placement of the basic block to take advantage of the single byte branch offsets. + +### Linker + +The linker determines all objects that are referenced and discards objects that are not. It then places the objects into their sections and spreads the sections over the regions. Then all references between the objects are resolved. Finally the prg or crt file is written. + +### Listing generator + +The final step is generating various listing files to support debugging. + +## Optimizations performed + +Besides simple constant folding, optimizations are performed at three stages. + +* Inlining during intermediate code generation. +* Transforming the intermediate code +* Transforming the native/bytecode + +### Intermediate code optimizations + +#### Value numbering and forwarding + +This step determines duplicate evaluation of the same expression and forwards the result of the first to the second, thus eliminating the duplicate. Duplicate expressions are frequently the result of array indexing. This also covers common subrexpression evaluation. + +#### Strength reduction + +Multiplications and divisions are replaced with shift and/or adds if possible. E.g. indexing in loops using the index variable may end up with a pointer that is incremented each iteration. + +#### Loop Invariants + +Expressions that do not change during the execution of a loop are pushed out of the loop and into the block before the loop. + +#### Loop unrolling + +Simple loops with a fixed number of iterations may be unrolled into a contiguous code sequence. + +#### Dead Code Elimination + +Instructions that are not executed, or do not contribute to the visible state of the program executed are eliminated. E.g. assigning a value to a variable that is later not used is removed. + +#### Basic Block Merge/Split + +Basic blocks are merged and or split to generate longer unbroken linear code sequences. This e.g. moves the condition of a while loop to the end of the loop and adds an additional condition at the start. + +#### Load/Store forwarding + +Performs Load-Load and Store-Load forwarding for memory regions that are known to be non aliased. This also opens new opportunities for constant folding and common sub expression evaluation. This may also completely eliminate some stores. + +#### Integer Range Analysis + +The possible value ranges of all integer instructions is analyzed and used to predict results of conditional instructions. Conditional instructions provide additional bounds for value ranges. This becomes important during native code generation to simplify 16bit code to 8bit. + +The compiler can deduce from the operations performed and the loop boundaries, that the index i can never be outside of the interval 0..10 + + char sum(void) + { + char s = 0; + for(int i=0; i<10; i++) + s += a[i]; + return s; + } + +The 16bit integer loop variable is replaced with an 8 bit register. + + sum: + 0927 LDY #$00 + 0929 LDA #$00 + 092b CLC + 092c ADC $0972,y ; (a + 0) + 092f INY + 0930 CPY #$04 + 0932 BCC $092b + 0934 STA ACCU + 0 + 0936 RTS + +#### Single Branch Expressions + +Expressions that are only needed in a single branch of control flow are moved onto this branch. + + +The sum of the array elements is only needed when the condition is false: + + char test(bool b) + { + char x = a[0] + a[1]; + if (b) x = 1; + return x; + } + +In this case, the evaluation of the sum is moved into an artificial else case: + + test: + 090e LDA P0 + 0910 BNE $091c + 0912 LDA $0923 ; (a + 1) + 0915 CLC + 0916 ADC $0922 ; (a + 0) + 0919 JMP $091e ; (test + 16) + 091c LDA #$01 + 091e STA ACCU + 0 + 0920 RTS + +#### Common head/tail Sequence coalescing + +Instructions that are common to all paths of a basic block sequence are moved into their common head or tail block. This removes duplicate code, frequently removing basic blocks in total. + +#### Collect expression trains + +The compiler tries to put expression chains into code sequences to reduce register pressure and allow for further optimization in the native code generation step. + + +### Native code optimizations + +Many of the intermediate optimiations steps are repeated on the symbolic assembler instructions, but now using the actual CPU architecture, including registers and addressing modes. + +#### Load/Store forwarding + +Load to Load and Stores to Load forwarding is performed using the CPU registers if possible or zero page locations otherwise. Many values are thus never written into memory, but kept in a register. + +#### Index forwarding + +An index in the X and Y register may may follow the path of a continuously changing index. + +#### Address mode simplification + +Indirect addressing is reduced to absolute,x or absolute,y if the 16 bit indirect address can be deduced to be an absolute address and the index short enough. + +#### Register usage + +The X and Y register are used for temporaries that would otherwise be placed in zero page locations. This is attempted on a function level and on a basic block level. + +#### Loop index registers + +Loops with a simple 8 bit counter may be changed to use the X or Y register as the loop index. The direction of a loop may also be changed from up to constant to down to zero if the bounds are known and the index non needed inside the loop. + +#### Strength reduction + +Multiplications and divisions are replaced with shift and/or adds if possible. + +#### Loop Invariants + +Loop invariants are moved outside of the loop + +#### Loop iteration forwarding + +Register values that are known to be identical to a zero page variable at the end of the loop and re loaded at the start of the loop are kept in the register, and the store and load are moved outside of the loop. + +#### Dead code elimination + +This removes many high byte instructions of 16 bit operations generated by the automatic type promotion to integer required by the C standard. + +#### Common head/tail Sequence coalescing + +Instructions that are common to all paths of a basic block sequence are moved into their common head or tail block. This removes duplicate code, frequently removing basic blocks in total. + +Both paths store a single byte into the same register + + void color(bool b) + { + if (b) + vic.color_back = VCOL_BLACK; + else + vic.color_back = VCOL_WHITE; + } + +The store is moved into the basic block that joins the two branches + + color: + 094e LDA P0 + 0950 BEQ $0956 + 0952 LDA #$00 + 0954 BEQ $0958 + 0956 LDA #$01 + 0958 STA $d021 + 095b RTS + +#### Peephole optimizations + +Various small and local optimizations are performed on the code on a per basic block level. + + + + diff --git a/oscar64/oscar64.sln b/oscar64/oscar64.sln new file mode 100644 index 0000000..e230a36 --- /dev/null +++ b/oscar64/oscar64.sln @@ -0,0 +1,41 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.13.35931.197 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "oscar64", "oscar64\oscar64.vcxproj", "{1DBC623E-6109-41FE-B1BB-9B43FC984F7D}" +EndProject +Project("{54435603-DBB4-11D2-8724-00A0C9A8B90C}") = "oscar64setup", "oscar64setup\oscar64setup.vdproj", "{567F9C0F-5888-442A-BC9A-D7F9591E5AB4}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {1DBC623E-6109-41FE-B1BB-9B43FC984F7D}.Debug|x64.ActiveCfg = Debug|x64 + {1DBC623E-6109-41FE-B1BB-9B43FC984F7D}.Debug|x64.Build.0 = Debug|x64 + {1DBC623E-6109-41FE-B1BB-9B43FC984F7D}.Debug|x86.ActiveCfg = Debug|Win32 + {1DBC623E-6109-41FE-B1BB-9B43FC984F7D}.Debug|x86.Build.0 = Debug|Win32 + {1DBC623E-6109-41FE-B1BB-9B43FC984F7D}.Release|x64.ActiveCfg = Release|x64 + {1DBC623E-6109-41FE-B1BB-9B43FC984F7D}.Release|x64.Build.0 = Release|x64 + {1DBC623E-6109-41FE-B1BB-9B43FC984F7D}.Release|x86.ActiveCfg = Release|Win32 + {1DBC623E-6109-41FE-B1BB-9B43FC984F7D}.Release|x86.Build.0 = Release|Win32 + {567F9C0F-5888-442A-BC9A-D7F9591E5AB4}.Debug|x64.ActiveCfg = Debug|x64 + {567F9C0F-5888-442A-BC9A-D7F9591E5AB4}.Debug|x64.Build.0 = Debug|x64 + {567F9C0F-5888-442A-BC9A-D7F9591E5AB4}.Debug|x86.ActiveCfg = Debug|x86 + {567F9C0F-5888-442A-BC9A-D7F9591E5AB4}.Debug|x86.Build.0 = Debug|x86 + {567F9C0F-5888-442A-BC9A-D7F9591E5AB4}.Release|x64.ActiveCfg = Release|x64 + {567F9C0F-5888-442A-BC9A-D7F9591E5AB4}.Release|x64.Build.0 = Release|x64 + {567F9C0F-5888-442A-BC9A-D7F9591E5AB4}.Release|x86.ActiveCfg = Release|x86 + {567F9C0F-5888-442A-BC9A-D7F9591E5AB4}.Release|x86.Build.0 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {C5E1AB04-FB65-4F70-B2F1-3787E7F84A41} + EndGlobalSection +EndGlobal diff --git a/oscar64/oscar64/Array.h b/oscar64/oscar64/Array.h new file mode 100644 index 0000000..01ddff8 --- /dev/null +++ b/oscar64/oscar64/Array.h @@ -0,0 +1,655 @@ +#pragma once + +#include +#include "MachineTypes.h" + +template +class DynamicArray +{ +protected: + int size, range; + T* array; + + void Grow(int by) + { + T* a2; + int i; + + if (size + by > range) + { + range = (size + by) * 2; + a2 = new T[range]; + for (i = 0; i < size; i++) a2[i] = array[i]; + delete[] array; + array = a2; + } + + size += by; + + } +public: + DynamicArray(void) + { + size = 0; + range = 4; + array = new T[range]; + } + + ~DynamicArray(void) + { + delete[] array; + } + + T* GetAndReleaseArray(void) + { + T* a2 = array; + + size = 0; + range = 4; + array = new T[range]; + + return a2; + } + + int Size(void) { return size; } + + bool Insert(T t) + { + Grow(1); + array[size - 1] = t; + + return true; + } + + bool Insert(int n, T t) + { + int m; + + if (n >= 0 && n <= size) + { + Grow(1); + m = size - 1; + while (m > n) + { + array[m] = array[m - 1]; + m--; + } + + array[n] = t; + } + + return true; + } + + bool Lookup(int n, T& t) + { + if (n >= 0 && n < size) + { + t = array[n]; + return true; + } + else + return false; + } + + bool Replace(int n, T t, T& old) + { + if (n >= 0 && n < size) + { + old = array[n]; + array[n] = t; + + return true; + } + else + return false; + } + + bool Swap(int n, int m) + { + T t; + + if (n >= 0 && n < size && m >= 0 && m < size) + { + t = array[n]; array[n] = array[m]; array[m] = t; + + return true; + } + else + return false; + } + + bool Remove(int n) + { + if (n >= 0 && n < size) + { + while (n < size - 1) + { + array[n] = array[n + 1]; + n++; + } + size--; + + return true; + } + else + return false; + } + + int Find(T t) + { + int n; + + n = size - 1; + while (n >= 0 && t != array[n]) + n--; + + return n; + } +}; + + +template +class GrowingArray +{ +protected: + int size, range; + T * array; + T empty; + + void Grow(int to, bool clear) + { + T* a2; + int i; + + if (clear) size = 0; + + if (to > range) + { + int trange = range * sizeof(T) < 65536 ? range * 2 : range + (range >> 2); + + if (trange < 4) + trange = 4; + + if (to > trange) + range = to; + else + range = trange; + + a2 = new T[range]; + if (to > size) + for (i = 0; i < size; i++) a2[i] = array[i]; + else + for (i = 0; i < to; i++) a2[i] = array[i]; + + delete[] array; + array = a2; + } + + for (i = size; i < to; i++) array[i] = empty; + + size = to; + } + +public: + GrowingArray(T empty_) + : empty(empty_) + { + size = 0; + range = 0; + array = nullptr; + } + + GrowingArray(const GrowingArray& a) + : empty(a.empty) + { + size = a.size; + range = a.range; + array = new T[range]; + + int lsize = size; + const T* sap = a.array; + T* dap = array; + for (int i = 0; i < lsize; i++) dap[i] = sap[i]; + } + + GrowingArray & operator=(const GrowingArray& a) + { + if (a.size != size) + Grow(a.size, true); + + int lsize = size; + const T* sap = a.array; + T* dap = array; + for (int i = 0; i < lsize; i++) dap[i] = sap[i]; + + return *this; + } + + ~GrowingArray(void) + { + delete[] array; + } + + void shrink(void) + { + size = 0; + range = 0; + delete[] array; + array = nullptr; + } + + __forceinline T& operator[](int n) + { + assert(n >= 0); + if (n >= size) Grow(n + 1, false); + return array[n]; + } + + __forceinline const T & operator[](int n) const + { + assert(n >= 0); + if (n >= size) return empty; + else return array[n]; + } + + const T getAt(int n) const + { + if (n >= size) return empty; + else return array[n]; + } + + void destroyAt(int n) + { + if (n < size) + array[n] = empty; + } + + void Push(T t) + { + (*this)[size] = t; + } + + T Pop(void) + { + assert(size > 0); + return array[--size]; + } + + void Insert(int at, T t) + { + Grow(size + 1, false); + int j = size - 1; + while (j > at) + { + array[j] = array[j - 1]; + j--; + } + array[at] = t; + } + + void Remove(int at) + { + while (at + 1 < size) + { + array[at] = array[at + 1]; + at++; + } + Grow(at, false); + } + + void Remove(int at, int n) + { + while (at + n < size) + { + array[at] = array[at + n]; + at++; + } + Grow(at, false); + } + + int RemoveAll(const T & t) + { + int j = 0, i = 0; + while (i < size) + { + if (array[i] != t) + { + if (i != j) + array[j] = array[i]; + j++; + } + i++; + } + + Grow(j, false); + + return i - j; + } + + + T Top(void) const + { + return array[size - 1]; + } + + bool IsEmpty(void) const { return size == 0; } + + __forceinline int Size(void) const { return size; } + + T Last() const + { + assert(size > 0); + return array[size - 1]; + } + + void SetSize(int size, bool clear = false) + { + Grow(size, clear); + } + + void Reserve(int to) + { + if (to > range) + { + range = to; + + T* a2 = new T[range]; + if (to > size) + for (int i = 0; i < size; i++) a2[i] = array[i]; + else + for (int i = 0; i < to; i++) a2[i] = array[i]; + + delete[] array; + array = a2; + + for (int i = size; i < range; i++) array[i] = empty; + } + } + + void Clear(void) + { + Grow(size, true); + } + + int IndexOf(const T& t) const + { + for (int i = 0; i < size; i++) + if (array[i] == t) + return i; + return -1; + } + + bool Contains(const T& t) const + { + for (int i = 0; i < size; i++) + if (array[i] == t) + return true; + return false; + } +}; + +template +class ExpandingArray +{ +public: +protected: + int size, range; + T * array; + + void Grow(int to, bool clear) + { + T* a2; + int i; + + if (clear) size = 0; + + if (to > range) + { + if (to > range * 2) + range = to; + else + range = range * 2; + + if (range < 4) + range = 4; + + a2 = new T[range]; + if (to > size) + for (i = 0; i < size; i++) a2[i] = array[i]; + else + for (i = 0; i < to; i++) a2[i] = array[i]; + + delete[] array; + array = a2; + } + + for (int i = size; i < to; i++) array[i] = T{}; + + size = to; + } + + template + void Partition(const F & f, int l, int r) + { + if (r > l + 1) + { + int pi = l; + T p(array[pi]); + + for (int i = l + 1; i < r; i++) + { + if (f(array[i], p)) + { + array[pi++] = array[i]; + array[i] = array[pi]; + } + } + array[pi] = p; + + Partition(f, l, pi); + Partition(f, pi + 1, r); + } + } +public: + ExpandingArray(void) + { + size = 0; + range = 0; + array = nullptr; + } + + ExpandingArray(const ExpandingArray& a) + { + size = a.size; + range = a.range; + array = new T[range]; + + int lsize = size; + const T* sap = a.array; + T* dap = array; + for (int i = 0; i < lsize; i++) dap[i] = sap[i]; + } + + ExpandingArray& operator=(const ExpandingArray& a) + { + if (a.size != size) + Grow(a.size, true); + + int lsize = size; + const T* sap = a.array; + T* dap = array; + for (int i = 0; i < lsize; i++) dap[i] = sap[i]; + + return *this; + } + + ~ExpandingArray(void) + { + delete[] array; + } + + void Push(const T & t) + { + int s = size; + Grow(size + 1, false); + array[s] = t; + } + + T Pop(void) + { + assert(size > 0); + return array[--size]; + } + + void Insert(int at, T t) + { + Grow(size + 1, false); + assert(at >= 0 && at < size); + int j = size - 1; + while (j > at) + { + array[j] = array[j - 1]; + j--; + } + array[at] = t; + } + + void Remove(int at) + { + assert(at >= 0 && at < size); + while (at + 1 < size) + { + array[at] = array[at + 1]; + at++; + } + Grow(at, false); + } + + void Remove(int at, int n) + { + assert(at >= 0 && at + n <= size); + while (at + n < size) + { + array[at] = array[at + n]; + at++; + } + Grow(at, false); + } + + int RemoveAll(const T& t) + { + int j = 0, i = 0; + while (i < size) + { + if (array[i] != t) + { + if (i != j) + array[j] = array[i]; + j++; + } + i++; + } + + Grow(j, false); + + return i - j; + } + + + T Top(void) const + { + assert(size > 0); + return array[size - 1]; + } + + bool IsEmpty(void) const { return size == 0; } + + int Size(void) const { return size; } + + T Last() const + { + assert(size > 0); + return array[size - 1]; + } + + void SetSize(int size, bool clear = false) + { + assert(size >= 0); + Grow(size, clear); + } + + void Reserve(int to) + { + if (to > range) + { + range = to; + + T* a2 = new T[range]; + if (to > size) + for (int i = 0; i < size; i++) a2[i] = array[i]; + else + for (int i = 0; i < to; i++) a2[i] = array[i]; + + delete[] array; + array = a2; + } + } + + int IndexOf(const T& t) const + { + for (int i = 0; i < size; i++) + if (array[i] == t) + return i; + return -1; + } + + int IndexOrPush(const T& t) + { + for (int i = 0; i < size; i++) + if (array[i] == t) + return i; + int s = size; + Grow(size + 1, false); + array[s] = t; + return s; + } + + bool Contains(const T& t) const + { + for (int i = 0; i < size; i++) + if (array[i] == t) + return true; + return false; + } + + void Fill(const T& t) + { + for (int i = 0; i < size; i++) + array[i] = t; + } + + void Clear(void) + { + for (int i = 0; i < size; i++) + array[i] = T{}; + } + + template + void Sort(const F & f) + { + Partition(f, 0, size); + } + + __forceinline T& operator[](int n) + { + assert(n >= 0 && n < size); + return array[n]; + } + + __forceinline const T& operator[](int n) const + { + assert(n >= 0 && n < size); + return array[n]; + } +}; diff --git a/oscar64/oscar64/Assembler.cpp b/oscar64/oscar64/Assembler.cpp new file mode 100644 index 0000000..6ea5428 --- /dev/null +++ b/oscar64/oscar64/Assembler.cpp @@ -0,0 +1,582 @@ +#include "Assembler.h" +#include +#include + + +AsmInsData DecInsData[256] = { + {ASMIT_BRK, ASMIM_IMPLIED}, + {ASMIT_ORA, ASMIM_INDIRECT_X}, + {ASMIT_INV}, + {ASMIT_INV}, + {ASMIT_INV}, + {ASMIT_ORA, ASMIM_ZERO_PAGE}, + {ASMIT_ASL, ASMIM_ZERO_PAGE}, + {ASMIT_INV}, + {ASMIT_PHP, ASMIM_IMPLIED}, + {ASMIT_ORA, ASMIM_IMMEDIATE}, + {ASMIT_ASL, ASMIM_IMPLIED}, + {ASMIT_INV}, + {ASMIT_INV}, + {ASMIT_ORA, ASMIM_ABSOLUTE}, + {ASMIT_ASL, ASMIM_ABSOLUTE}, + {ASMIT_INV}, + + {ASMIT_BPL, ASMIM_RELATIVE }, + {ASMIT_ORA, ASMIM_INDIRECT_Y}, + {ASMIT_INV}, + {ASMIT_INV}, + {ASMIT_INV}, + {ASMIT_ORA, ASMIM_ZERO_PAGE_X}, + {ASMIT_ASL, ASMIM_ZERO_PAGE_X}, + {ASMIT_INV}, + {ASMIT_CLC, ASMIM_IMPLIED}, + {ASMIT_ORA, ASMIM_ABSOLUTE_Y}, + {ASMIT_INV}, + {ASMIT_INV}, + {ASMIT_INV}, + {ASMIT_ORA, ASMIM_ABSOLUTE_X}, + {ASMIT_ASL, ASMIM_ABSOLUTE_X}, + {ASMIT_INV}, + + + {ASMIT_JSR, ASMIM_ABSOLUTE}, + {ASMIT_AND, ASMIM_INDIRECT_X}, + {ASMIT_INV}, + {ASMIT_INV}, + {ASMIT_BIT, ASMIM_ZERO_PAGE}, + {ASMIT_AND, ASMIM_ZERO_PAGE}, + {ASMIT_ROL, ASMIM_ZERO_PAGE}, + {ASMIT_INV}, + {ASMIT_PLP, ASMIM_IMPLIED}, + {ASMIT_AND, ASMIM_IMMEDIATE}, + {ASMIT_ROL, ASMIM_IMPLIED}, + {ASMIT_INV}, + {ASMIT_BIT, ASMIM_ABSOLUTE}, + {ASMIT_AND, ASMIM_ABSOLUTE}, + {ASMIT_ROL, ASMIM_ABSOLUTE}, + {ASMIT_INV}, + + {ASMIT_BMI, ASMIM_RELATIVE }, + {ASMIT_AND, ASMIM_INDIRECT_Y}, + {ASMIT_INV}, + {ASMIT_INV}, + {ASMIT_INV}, + {ASMIT_AND, ASMIM_ZERO_PAGE_X}, + {ASMIT_ROL, ASMIM_ZERO_PAGE_X}, + {ASMIT_INV}, + {ASMIT_SEC, ASMIM_IMPLIED}, + {ASMIT_AND, ASMIM_ABSOLUTE_Y}, + {ASMIT_INV}, + {ASMIT_INV}, + {ASMIT_INV}, + {ASMIT_AND, ASMIM_ABSOLUTE_X}, + {ASMIT_ROL, ASMIM_ABSOLUTE_X}, + {ASMIT_INV}, + + + {ASMIT_RTI, ASMIM_IMPLIED}, + {ASMIT_EOR, ASMIM_INDIRECT_X}, + {ASMIT_INV}, + {ASMIT_INV}, + {ASMIT_INV}, + {ASMIT_EOR, ASMIM_ZERO_PAGE}, + {ASMIT_LSR, ASMIM_ZERO_PAGE}, + {ASMIT_INV}, + {ASMIT_PHA, ASMIM_IMPLIED}, + {ASMIT_EOR, ASMIM_IMMEDIATE}, + {ASMIT_LSR, ASMIM_IMPLIED}, + {ASMIT_INV}, + {ASMIT_JMP, ASMIM_ABSOLUTE}, + {ASMIT_EOR, ASMIM_ABSOLUTE}, + {ASMIT_LSR, ASMIM_ABSOLUTE}, + {ASMIT_INV}, + + {ASMIT_BVC, ASMIM_RELATIVE }, + {ASMIT_EOR, ASMIM_INDIRECT_Y}, + {ASMIT_INV}, + {ASMIT_INV}, + {ASMIT_INV}, + {ASMIT_EOR, ASMIM_ZERO_PAGE_X}, + {ASMIT_LSR, ASMIM_ZERO_PAGE_X}, + {ASMIT_INV}, + {ASMIT_CLI, ASMIM_IMPLIED}, + {ASMIT_EOR, ASMIM_ABSOLUTE_Y}, + {ASMIT_INV}, + {ASMIT_INV}, + {ASMIT_INV}, + {ASMIT_EOR, ASMIM_ABSOLUTE_X}, + {ASMIT_LSR, ASMIM_ABSOLUTE_X}, + {ASMIT_INV}, + + + { ASMIT_RTS, ASMIM_IMPLIED }, + { ASMIT_ADC, ASMIM_INDIRECT_X }, + { ASMIT_INV }, + { ASMIT_INV }, + { ASMIT_INV }, + { ASMIT_ADC, ASMIM_ZERO_PAGE }, + { ASMIT_ROR, ASMIM_ZERO_PAGE }, + { ASMIT_INV }, + { ASMIT_PLA, ASMIM_IMPLIED }, + { ASMIT_ADC, ASMIM_IMMEDIATE }, + { ASMIT_ROR, ASMIM_IMPLIED }, + { ASMIT_INV }, + { ASMIT_JMP, ASMIM_INDIRECT }, + { ASMIT_ADC, ASMIM_ABSOLUTE }, + { ASMIT_ROR, ASMIM_ABSOLUTE }, + { ASMIT_INV }, + + { ASMIT_BVS, ASMIM_RELATIVE }, + { ASMIT_ADC, ASMIM_INDIRECT_Y }, + { ASMIT_INV }, + { ASMIT_INV }, + { ASMIT_INV }, + { ASMIT_ADC, ASMIM_ZERO_PAGE_X }, + { ASMIT_ROR, ASMIM_ZERO_PAGE_X }, + { ASMIT_INV }, + { ASMIT_SEI, ASMIM_IMPLIED }, + { ASMIT_ADC, ASMIM_ABSOLUTE_Y }, + { ASMIT_INV }, + { ASMIT_INV }, + { ASMIT_INV }, + { ASMIT_ADC, ASMIM_ABSOLUTE_X }, + { ASMIT_ROR, ASMIM_ABSOLUTE_X }, + { ASMIT_INV }, + + + { ASMIT_INV }, + { ASMIT_STA, ASMIM_INDIRECT_X }, + { ASMIT_INV }, + { ASMIT_INV }, + { ASMIT_STY, ASMIM_ZERO_PAGE }, + { ASMIT_STA, ASMIM_ZERO_PAGE }, + { ASMIT_STX, ASMIM_ZERO_PAGE }, + { ASMIT_INV }, + { ASMIT_DEY, ASMIM_IMPLIED }, + { ASMIT_INV }, + { ASMIT_TXA, ASMIM_IMPLIED }, + { ASMIT_INV }, + { ASMIT_STY, ASMIM_ABSOLUTE }, + { ASMIT_STA, ASMIM_ABSOLUTE }, + { ASMIT_STX, ASMIM_ABSOLUTE }, + { ASMIT_INV }, + + { ASMIT_BCC, ASMIM_RELATIVE }, + { ASMIT_STA, ASMIM_INDIRECT_Y }, + { ASMIT_INV }, + { ASMIT_INV }, + { ASMIT_STY, ASMIM_ZERO_PAGE_X }, + { ASMIT_STA, ASMIM_ZERO_PAGE_X }, + { ASMIT_STX, ASMIM_ZERO_PAGE_Y }, + { ASMIT_INV }, + { ASMIT_TYA, ASMIM_IMPLIED }, + { ASMIT_STA, ASMIM_ABSOLUTE_Y }, + { ASMIT_TXS }, + { ASMIT_INV }, + { ASMIT_INV }, + { ASMIT_STA, ASMIM_ABSOLUTE_X }, + { ASMIT_INV }, + { ASMIT_INV }, + + + { ASMIT_LDY, ASMIM_IMMEDIATE }, + { ASMIT_LDA, ASMIM_INDIRECT_X }, + { ASMIT_LDX, ASMIM_IMMEDIATE }, + { ASMIT_INV }, + { ASMIT_LDY, ASMIM_ZERO_PAGE }, + { ASMIT_LDA, ASMIM_ZERO_PAGE }, + { ASMIT_LDX, ASMIM_ZERO_PAGE }, + { ASMIT_INV }, + { ASMIT_TAY, ASMIM_IMPLIED }, + { ASMIT_LDA, ASMIM_IMMEDIATE }, + { ASMIT_TAX, ASMIM_IMPLIED }, + { ASMIT_INV }, + { ASMIT_LDY, ASMIM_ABSOLUTE }, + { ASMIT_LDA, ASMIM_ABSOLUTE }, + { ASMIT_LDX, ASMIM_ABSOLUTE }, + { ASMIT_INV }, + + { ASMIT_BCS, ASMIM_RELATIVE }, + { ASMIT_LDA, ASMIM_INDIRECT_Y }, + { ASMIT_INV }, + { ASMIT_INV }, + { ASMIT_LDY, ASMIM_ZERO_PAGE_X }, + { ASMIT_LDA, ASMIM_ZERO_PAGE_X }, + { ASMIT_LDX, ASMIM_ZERO_PAGE_Y }, + { ASMIT_INV }, + { ASMIT_CLV, ASMIM_IMPLIED }, + { ASMIT_LDA, ASMIM_ABSOLUTE_Y }, + { ASMIT_TSX, ASMIM_IMPLIED }, + { ASMIT_INV }, + { ASMIT_LDY, ASMIM_ABSOLUTE_X }, + { ASMIT_LDA, ASMIM_ABSOLUTE_X }, + { ASMIT_LDX, ASMIM_ABSOLUTE_Y }, + { ASMIT_INV }, + + + { ASMIT_CPY, ASMIM_IMMEDIATE }, + { ASMIT_CMP, ASMIM_INDIRECT_X }, + { ASMIT_INV }, + { ASMIT_INV }, + { ASMIT_CPY, ASMIM_ZERO_PAGE }, + { ASMIT_CMP, ASMIM_ZERO_PAGE }, + { ASMIT_DEC, ASMIM_ZERO_PAGE }, + { ASMIT_INV }, + { ASMIT_INY, ASMIM_IMPLIED }, + { ASMIT_CMP, ASMIM_IMMEDIATE }, + { ASMIT_DEX, ASMIM_IMPLIED }, + { ASMIT_INV }, + { ASMIT_CPY, ASMIM_ABSOLUTE }, + { ASMIT_CMP, ASMIM_ABSOLUTE }, + { ASMIT_DEC, ASMIM_ABSOLUTE }, + { ASMIT_INV }, + + { ASMIT_BNE, ASMIM_RELATIVE }, + { ASMIT_CMP, ASMIM_INDIRECT_Y }, + { ASMIT_INV }, + { ASMIT_INV }, + { ASMIT_INV }, + { ASMIT_CMP, ASMIM_ZERO_PAGE_X }, + { ASMIT_DEC, ASMIM_ZERO_PAGE_X }, + { ASMIT_INV }, + { ASMIT_CLD, ASMIM_IMPLIED }, + { ASMIT_CMP, ASMIM_ABSOLUTE_Y }, + { ASMIT_INV }, + { ASMIT_INV }, + { ASMIT_INV }, + { ASMIT_CMP, ASMIM_ABSOLUTE_X }, + { ASMIT_DEC, ASMIM_ABSOLUTE_X }, + { ASMIT_INV }, + + { ASMIT_CPX, ASMIM_IMMEDIATE }, + { ASMIT_SBC, ASMIM_INDIRECT_X }, + { ASMIT_INV }, + { ASMIT_INV }, + { ASMIT_CPX, ASMIM_ZERO_PAGE }, + { ASMIT_SBC, ASMIM_ZERO_PAGE }, + { ASMIT_INC, ASMIM_ZERO_PAGE }, + { ASMIT_INV }, + { ASMIT_INX, ASMIM_IMPLIED }, + { ASMIT_SBC, ASMIM_IMMEDIATE }, + { ASMIT_NOP, ASMIM_IMPLIED }, + { ASMIT_INV }, + { ASMIT_CPX, ASMIM_ABSOLUTE }, + { ASMIT_SBC, ASMIM_ABSOLUTE }, + { ASMIT_INC, ASMIM_ABSOLUTE }, + { ASMIT_INV }, + + { ASMIT_BEQ, ASMIM_RELATIVE }, + { ASMIT_SBC, ASMIM_INDIRECT_Y }, + { ASMIT_INV }, + { ASMIT_INV }, + { ASMIT_INV }, + { ASMIT_SBC, ASMIM_ZERO_PAGE_X }, + { ASMIT_INC, ASMIM_ZERO_PAGE_X }, + { ASMIT_INV }, + { ASMIT_SED, ASMIM_IMPLIED }, + { ASMIT_SBC, ASMIM_ABSOLUTE_Y }, + { ASMIT_INV }, + { ASMIT_INV }, + { ASMIT_INV }, + { ASMIT_SBC, ASMIM_ABSOLUTE_X }, + { ASMIT_INC, ASMIM_ABSOLUTE_X }, + { ASMIT_INV }, +}; + +short AsmInsOpcodes[NUM_ASM_INS_TYPES][NUM_ASM_INS_MODES]; + +const char* AsmInstructionNames[NUM_ASM_INS_TYPES] = { + "ADC", "AND", "ASL", "BCC", "BCS", "BEQ", "BIT", "BMI", "BNE", "BPL", "BRK", "BVC", "BVS", "CLC", + "CLD", "CLI", "CLV", "CMP", "CPX", "CPY", "DEC", "DEX", "DEY", "EOR", "INC", "INX", "INY", "JMP", + "JSR", "LDA", "LDX", "LDY", "LSR", "NOP", "ORA", "PHA", "PHP", "PLA", "PLP", "ROL", "ROR", "RTI", + "RTS", "SBC", "SEC", "SED", "SEI", "STA", "STX", "STY", "TAX", "TAY", "TSX", "TXA", "TXS", "TYA", + "INV", "BYT" +}; + +int AsmInsModeSize[NUM_ASM_INS_MODES_X] = { + 1, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 0, + 2 +}; + +void InitAssembler(void) +{ + for (int i = 0; i < NUM_ASM_INS_TYPES; i++) + { + for (int j = 0; j < NUM_ASM_INS_MODES; j++) + { + AsmInsOpcodes[i][j] = -1; + } + } + + for (int i = 0; i < 256; i++) + { + const AsmInsData& di(DecInsData[i]); + if (di.mType != ASMIT_INV) + { + assert(AsmInsOpcodes[di.mType][di.mMode] == -1); + AsmInsOpcodes[di.mType][di.mMode] = i; + } + } + + AsmInsOpcodes[ASMIT_BYTE][ASMIM_ZERO_PAGE] = 0; +} + +int AsmInsSize(AsmInsType type, AsmInsMode mode) +{ + if (type < ASMIT_INV && mode >= ASMIM_IMPLIED && mode < NUM_ASM_INS_MODES) + return AsmInsModeSize[mode]; + else + return 1; +} + +uint32 AsmInsFlags(AsmInsType type, AsmInsMode mode) +{ + uint32 flags = 0; + + // Accu + switch (type) + { + case ASMIT_LDA: + flags |= ASMIFLG_CHANGES_ACCU | ASMIFLG_CHANGES_ZFLAG; + if (mode != ASMIM_IMMEDIATE) + flags |= ASMIFLG_USES_MEMORY; + break; + + case ASMIT_LDX: + flags |= ASMIFLG_CHANGES_XREG | ASMIFLG_CHANGES_ZFLAG; + if (mode != ASMIM_IMMEDIATE) + flags |= ASMIFLG_USES_MEMORY; + break; + + case ASMIT_LDY: + flags |= ASMIFLG_CHANGES_YREG | ASMIFLG_CHANGES_ZFLAG; + if (mode != ASMIM_IMMEDIATE) + flags |= ASMIFLG_USES_MEMORY; + break; + + case ASMIT_STA: + flags |= ASMIFLG_USES_ACCU | ASMIFLG_CHANGES_MEMORY; + break; + + case ASMIT_STX: + flags |= ASMIFLG_USES_XREG | ASMIFLG_CHANGES_MEMORY; + break; + + case ASMIT_STY: + flags |= ASMIFLG_USES_YREG | ASMIFLG_CHANGES_MEMORY; + break; + + case ASMIT_ADC: + case ASMIT_SBC: + flags |= ASMIFLG_USES_ACCU | ASMIFLG_CHANGES_ACCU | ASMIFLG_CHANGES_ZFLAG | ASMIFLG_USES_CFLAG | ASMIFLG_CHANGES_CFLAG; + if (mode != ASMIM_IMMEDIATE) + flags |= ASMIFLG_USES_MEMORY; + break; + + case ASMIT_AND: + case ASMIT_ORA: + case ASMIT_EOR: + flags |= ASMIFLG_USES_ACCU | ASMIFLG_CHANGES_ACCU | ASMIFLG_CHANGES_ZFLAG; + if (mode != ASMIM_IMMEDIATE) + flags |= ASMIFLG_USES_MEMORY; + break; + + case ASMIT_ASL: + case ASMIT_LSR: + flags |= ASMIFLG_CHANGES_CFLAG | ASMIFLG_CHANGES_ZFLAG; + if (mode == ASMIM_IMPLIED) + flags |= ASMIFLG_USES_ACCU | ASMIFLG_CHANGES_ACCU; + else + flags |= ASMIFLG_USES_MEMORY | ASMIFLG_CHANGES_MEMORY; + break; + + case ASMIT_ROL: + case ASMIT_ROR: + flags |= ASMIFLG_USES_CFLAG | ASMIFLG_CHANGES_CFLAG | ASMIFLG_CHANGES_ZFLAG; + if (mode == ASMIM_IMPLIED) + flags |= ASMIFLG_USES_ACCU | ASMIFLG_CHANGES_ACCU; + else + flags |= ASMIFLG_USES_MEMORY | ASMIFLG_CHANGES_MEMORY; + break; + + case ASMIT_CMP: + flags |= ASMIFLG_USES_ACCU | ASMIFLG_CHANGES_ZFLAG | ASMIFLG_CHANGES_CFLAG; + if (mode != ASMIM_IMMEDIATE) + flags |= ASMIFLG_USES_MEMORY; + break; + case ASMIT_CPX: + flags |= ASMIFLG_USES_XREG | ASMIFLG_CHANGES_ZFLAG | ASMIFLG_CHANGES_CFLAG; + if (mode != ASMIM_IMMEDIATE) + flags |= ASMIFLG_USES_MEMORY; + break; + case ASMIT_CPY: + flags |= ASMIFLG_USES_YREG | ASMIFLG_CHANGES_ZFLAG | ASMIFLG_CHANGES_CFLAG; + if (mode != ASMIM_IMMEDIATE) + flags |= ASMIFLG_USES_MEMORY; + break; + + case ASMIT_DEX: + case ASMIT_INX: + flags |= ASMIFLG_USES_XREG | ASMIFLG_CHANGES_ZFLAG | ASMIFLG_CHANGES_XREG; + break; + + case ASMIT_DEY: + case ASMIT_INY: + flags |= ASMIFLG_USES_YREG | ASMIFLG_CHANGES_ZFLAG | ASMIFLG_CHANGES_YREG; + break; + + case ASMIT_DEC: + case ASMIT_INC: + flags |= ASMIFLG_USES_MEMORY | ASMIFLG_CHANGES_MEMORY | ASMIFLG_CHANGES_ZFLAG; + break; + + case ASMIT_BCC: + case ASMIT_BCS: + flags |= ASMIFLG_USES_CFLAG | ASMIFLG_CONTROL_FLOW; + break; + + case ASMIT_BEQ: + case ASMIT_BMI: + case ASMIT_BNE: + case ASMIT_BPL: + case ASMIT_BVC: + case ASMIT_BVS: + flags |= ASMIFLG_USES_ZFLAG | ASMIFLG_CONTROL_FLOW; + break; + + case ASMIT_JMP: + flags |= ASMIFLG_CONTROL_FLOW; + break; + + case ASMIT_CLC: + case ASMIT_SEC: + flags |= ASMIFLG_CHANGES_CFLAG; + break; + + case ASMIT_BIT: + flags |= ASMIFLG_USES_MEMORY | ASMIFLG_USES_ACCU | ASMIFLG_CHANGES_ZFLAG; + break; + + case ASMIT_CLV: + flags |= ASMIFLG_CHANGES_ZFLAG; + break; + + case ASMIT_TAX: + flags |= ASMIFLG_USES_ACCU | ASMIFLG_CHANGES_XREG | ASMIFLG_CHANGES_ZFLAG; + break; + case ASMIT_TAY: + flags |= ASMIFLG_USES_ACCU | ASMIFLG_CHANGES_YREG | ASMIFLG_CHANGES_ZFLAG; + break; + case ASMIT_TSX: + flags |= ASMIFLG_USES_STACK | ASMIFLG_CHANGES_XREG | ASMIFLG_CHANGES_ZFLAG; + break; + case ASMIT_TXA: + flags |= ASMIFLG_USES_XREG | ASMIFLG_CHANGES_ACCU | ASMIFLG_CHANGES_ZFLAG; + break; + case ASMIT_TXS: + flags |= ASMIFLG_USES_XREG | ASMIFLG_CHANGES_STACK | ASMIFLG_CHANGES_ZFLAG; + break; + case ASMIT_TYA: + flags |= ASMIFLG_USES_YREG | ASMIFLG_CHANGES_ACCU | ASMIFLG_CHANGES_ZFLAG; + break; + + case ASMIT_PLA: + flags |= ASMIFLG_USES_STACK | ASMIFLG_CHANGES_STACK | ASMIFLG_CHANGES_ACCU | ASMIFLG_CHANGES_ZFLAG; + break; + + case ASMIT_PHA: + flags |= ASMIFLG_USES_STACK | ASMIFLG_CHANGES_STACK | ASMIFLG_USES_ACCU; + break; + + case ASMIT_PLP: + flags |= ASMIFLG_USES_STACK | ASMIFLG_CHANGES_STACK | ASMIFLG_CHANGES_CFLAG | ASMIFLG_CHANGES_ZFLAG; + break; + + case ASMIT_PHP: + flags |= ASMIFLG_USES_STACK | ASMIFLG_CHANGES_STACK | ASMIFLG_USES_CFLAG | ASMIFLG_USES_ZFLAG; + break; + + case ASMIT_JSR: + flags |= ASMIFLG_USES_ALL | ASMIFLG_CHANGES_ALL | ASMIFLG_CONTROL_FLOW; + break; + + case ASMIT_RTS: + case ASMIT_RTI: + flags |= ASMIFLG_CONTROL_FLOW; + break; + + case ASMIT_BRK: + flags |= ASMIFLG_CONTROL_FLOW; + break; + + case ASMIT_CLI: + case ASMIT_SEI: + case ASMIT_CLD: + case ASMIT_SED: + break; + + case ASMIT_NOP: + break; + } + + switch (mode) + { + case ASMIM_ABSOLUTE_X: + case ASMIM_ZERO_PAGE_X: + flags |= ASMIFLG_USES_XREG; + break; + case ASMIM_INDIRECT_X: + flags |= ASMIFLG_USES_XREG | ASMIFLG_USES_MEMORY; + break; + + case ASMIM_ABSOLUTE_Y: + case ASMIM_ZERO_PAGE_Y: + flags |= ASMIFLG_USES_YREG; + break; + case ASMIM_INDIRECT_Y: + flags |= ASMIFLG_USES_YREG | ASMIFLG_USES_MEMORY; + break; + } + return flags; +} + +static inline char toupper(char ch) +{ + if (ch >= 'a' && ch <= 'z') + return ch - 'a' + 'A'; + else + return ch; +} +AsmInsType FindAsmInstruction(const char* ins) +{ + if (ins && strlen(ins) == 3) + { + for (int i = 0; i < NUM_ASM_INS_TYPES; i++) + { + if (AsmInstructionNames[i][0] == toupper(ins[0]) && + AsmInstructionNames[i][1] == toupper(ins[1]) && + AsmInstructionNames[i][2] == toupper(ins[2])) + return AsmInsType(i); + } + } + + return ASMIT_INV; +} + +bool HasAsmInstructionMode(AsmInsType type, AsmInsMode mode) +{ + return AsmInsOpcodes[type][mode] != -1; +} + diff --git a/oscar64/oscar64/Assembler.h b/oscar64/oscar64/Assembler.h new file mode 100644 index 0000000..ab26202 --- /dev/null +++ b/oscar64/oscar64/Assembler.h @@ -0,0 +1,83 @@ +#pragma once + +#include "Ident.h" +#include "MachineTypes.h" + +enum AsmInsType : uint8 +{ + ASMIT_ADC, ASMIT_AND, ASMIT_ASL, ASMIT_BCC, ASMIT_BCS, ASMIT_BEQ, ASMIT_BIT, ASMIT_BMI, ASMIT_BNE, ASMIT_BPL, ASMIT_BRK, ASMIT_BVC, ASMIT_BVS, ASMIT_CLC, + ASMIT_CLD, ASMIT_CLI, ASMIT_CLV, ASMIT_CMP, ASMIT_CPX, ASMIT_CPY, ASMIT_DEC, ASMIT_DEX, ASMIT_DEY, ASMIT_EOR, ASMIT_INC, ASMIT_INX, ASMIT_INY, ASMIT_JMP, + ASMIT_JSR, ASMIT_LDA, ASMIT_LDX, ASMIT_LDY, ASMIT_LSR, ASMIT_NOP, ASMIT_ORA, ASMIT_PHA, ASMIT_PHP, ASMIT_PLA, ASMIT_PLP, ASMIT_ROL, ASMIT_ROR, ASMIT_RTI, + ASMIT_RTS, ASMIT_SBC, ASMIT_SEC, ASMIT_SED, ASMIT_SEI, ASMIT_STA, ASMIT_STX, ASMIT_STY, ASMIT_TAX, ASMIT_TAY, ASMIT_TSX, ASMIT_TXA, ASMIT_TXS, ASMIT_TYA, + ASMIT_INV, ASMIT_BYTE, + + NUM_ASM_INS_TYPES +}; + + +enum AsmInsMode : uint8 +{ + ASMIM_IMPLIED, + ASMIM_IMMEDIATE, + ASMIM_ZERO_PAGE, + ASMIM_ZERO_PAGE_X, + ASMIM_ZERO_PAGE_Y, + ASMIM_ABSOLUTE, + ASMIM_ABSOLUTE_X, + ASMIM_ABSOLUTE_Y, + ASMIM_INDIRECT, + ASMIM_INDIRECT_X, + ASMIM_INDIRECT_Y, + ASMIM_RELATIVE, + + NUM_ASM_INS_MODES, + + ASMIM_IMMEDIATE_ADDRESS, + + NUM_ASM_INS_MODES_X, +}; + +struct AsmInsData +{ + AsmInsType mType; + AsmInsMode mMode; +}; + +static const uint32 ASMIFLG_USES_ACCU = 0x00000001; +static const uint32 ASMIFLG_USES_XREG = 0x00000002; +static const uint32 ASMIFLG_USES_YREG = 0x00000004; +static const uint32 ASMIFLG_USES_CFLAG = 0x00000008; +static const uint32 ASMIFLG_USES_ZFLAG = 0x00000010; +static const uint32 ASMIFLG_USES_MEMORY = 0x00000020; +static const uint32 ASMIFLG_USES_STACK = 0x00000040; +static const uint32 ASMIFLG_USES_ALL = 0x000000ff; + +static const uint32 ASMIFLG_CHANGES_ACCU = 0x00000100; +static const uint32 ASMIFLG_CHANGES_XREG = 0x00000200; +static const uint32 ASMIFLG_CHANGES_YREG = 0x00000400; +static const uint32 ASMIFLG_CHANGES_CFLAG = 0x00000800; +static const uint32 ASMIFLG_CHANGES_ZFLAG = 0x00001000; +static const uint32 ASMIFLG_CHANGES_MEMORY = 0x00002000; +static const uint32 ASMIFLG_CHANGES_STACK = 0x00004000; +static const uint32 ASMIFLG_CHANGES_ALL = 0x0000ff00; + +static const uint32 ASMIFLG_CONTROL_FLOW = 0x00010000; + + +extern AsmInsData DecInsData[256]; + +extern short AsmInsOpcodes[NUM_ASM_INS_TYPES][NUM_ASM_INS_MODES]; + +extern int AsmInsModeSize[NUM_ASM_INS_MODES_X]; + +extern const char* AsmInstructionNames[NUM_ASM_INS_TYPES]; + +AsmInsType FindAsmInstruction(const char * ins); + +bool HasAsmInstructionMode(AsmInsType type, AsmInsMode mode); + +int AsmInsSize(AsmInsType type, AsmInsMode mode); + +uint32 AsmInsFlags(AsmInsType type, AsmInsMode mode); + +void InitAssembler(void); diff --git a/oscar64/oscar64/BitVector.h b/oscar64/oscar64/BitVector.h new file mode 100644 index 0000000..f09eb45 --- /dev/null +++ b/oscar64/oscar64/BitVector.h @@ -0,0 +1,334 @@ +#pragma once + +#include +#include +#include +#include + +class BitVector +{ +protected: + unsigned* bits; + unsigned mask; + int size, dwsize; +public: + BitVector(void); + BitVector(const BitVector& v); + BitVector(int size, bool set = false); + BitVector(int size, unsigned char * data); + BitVector& operator=(const BitVector& v); + + ~BitVector(void); + + BitVector operator+(const BitVector v) const; + BitVector operator*(const int n) const; + BitVector operator|(const BitVector v) const; + BitVector operator&(const BitVector v) const; + BitVector operator~(void) const; + + void Set(int n, bool b); + bool Get(int n) const; + int Size(void) const; + int DWSize(void) const; + + void Copy(unsigned* bits) const; +}; + +inline BitVector::BitVector(void) +{ + size = 0; + dwsize = 0; + bits = nullptr; + mask = 0; +} + +inline BitVector::BitVector(const BitVector& v) +{ + int i; + + size = v.size; + dwsize = v.dwsize; + mask = v.mask; + + if (dwsize) + bits = new unsigned[dwsize]; + else + bits = nullptr; + + for (i = 0; i < dwsize; i++) bits[i] = v.bits[i]; +} + +inline BitVector::BitVector(int size, unsigned char * data) +{ + this->size = size; + dwsize = ((size + 31) >> 5); + if ((size & 31) == 0) + mask = 0xffffffff; + else + mask = (1 << (size & 31)) - 1; + + if (dwsize) + bits = new unsigned[dwsize]; + else + bits = nullptr; + + if (size > 0) + { + memcpy(bits, data, (size + 7) / 8); + } +} + +inline BitVector::BitVector(int size, bool set) +{ + int i; + + this->size = size; + dwsize = ((size + 31) >> 5); + if ((size & 31) == 0) + mask = 0xffffffff; + else + mask = (1 << (size & 31)) - 1; + + if (dwsize) + bits = new unsigned[dwsize]; + else + bits = NULL; + + if (size > 0) + { + if (set) + { + for (i = 0; i < dwsize - 1; i++) bits[i] = ~0; + bits[dwsize - 1] = mask; + } + else + for (i = 0; i < dwsize; i++) bits[i] = 0; + } +} + +inline BitVector& BitVector::operator=(const BitVector& v) +{ + int i; + + delete[] bits; + + size = v.size; + dwsize = v.dwsize; + mask = v.mask; + + if (dwsize) + bits = new unsigned[dwsize]; + else + bits = NULL; + + for (i = 0; i < dwsize; i++) bits[i] = v.bits[i]; + + return *this; +} + + +inline BitVector::~BitVector(void) +{ + delete[] bits; +} + +inline BitVector BitVector::operator+(const BitVector v) const +{ + int i; + int s; + unsigned merge; + + BitVector vv(size + v.size); + + if (dwsize == 0) + { + for (i = 0; i < v.dwsize; i++) + vv.bits[i] = v.bits[i]; + } + else if (v.dwsize == 0) + { + for (i = 0; i < dwsize; i++) + vv.bits[i] = bits[i]; + } + else + { + s = size & 31; + + if (s == 0) + { + for (i = 0; i < dwsize; i++) + vv.bits[i] = bits[i]; + for (i = 0; i < v.dwsize; i++) + vv.bits[i + dwsize] = v.bits[i]; + } + else + { + for (i = 0; i < dwsize - 1; i++) + vv.bits[i] = bits[i]; + + merge = bits[dwsize - 1] & mask; + + for (i = 0; i < v.dwsize; i++) + { + vv.bits[i + dwsize - 1] = merge | (v.bits[i] << s); + merge = v.bits[i] >> (32 - s); + } + + if (dwsize - 1 + v.dwsize < vv.dwsize) + vv.bits[dwsize - 1 + v.dwsize] = merge; + } + } + + return vv; +} + +inline BitVector BitVector::operator*(const int n) const +{ + int i, j; + int s, dw; + unsigned merge; + + BitVector vv(size * n); + + if (n > 0) + { + dw = 0; + + for (i = 0; i < dwsize - 1; i++) + vv.bits[dw++] = bits[i]; + + merge = bits[dwsize - 1] & mask; + + for (j = 1; j < n; j++) + { + s = (size * j) & 31; + + if (s == 0) + { + vv.bits[dw++] = merge; + for (i = 0; i < dwsize - 1; i++) + vv.bits[dw++] = bits[i]; + + merge = bits[dwsize - 1] & mask; + } + else + { + for (i = 0; i < dwsize - 1; i++) + { + vv.bits[dw++] = merge | (bits[i] << s); + merge = bits[i] >> (32 - s); + } + if (mask >> (32 - s)) + { + vv.bits[dw++] = merge | (bits[i] << s); + merge = (bits[i] & mask) >> (32 - s); + } + else + { + merge |= (bits[i] & mask) << s; + } + } + } + + if (dw < vv.dwsize) + vv.bits[dw++] = merge; + + assert(dw <= vv.dwsize); + } + + return vv; +} + +inline BitVector BitVector::operator|(const BitVector v) const +{ + int i; + + if (dwsize < v.dwsize) + { + BitVector vv(v.size); + + for (i = 0; i < dwsize; i++) vv.bits[i] = bits[i] | v.bits[i]; + for (i = dwsize; i < v.dwsize; i++) vv.bits[i] = v.bits[i]; + + return vv; + } + else + { + BitVector vv(size); + + for (i = 0; i < v.dwsize; i++) vv.bits[i] = bits[i] | v.bits[i]; + for (i = v.dwsize; i < dwsize; i++) vv.bits[i] = bits[i]; + + return vv; + } +} + +inline BitVector BitVector::operator&(const BitVector v) const +{ + int i; + + if (dwsize < v.dwsize) + { + BitVector vv(v.size); + + for (i = 0; i < dwsize; i++) vv.bits[i] = bits[i] & v.bits[i]; + + return vv; + } + else + { + BitVector vv(size); + + for (i = 0; i < v.dwsize; i++) vv.bits[i] = bits[i] & v.bits[i]; + + return vv; + } +} + +inline BitVector BitVector::operator~(void) const +{ + int i; + + BitVector vv(size); + + for (i = 0; i < dwsize - 1; i++) vv.bits[i] = ~bits[i]; + vv.bits[dwsize - 1] = ~bits[dwsize - 1] & mask; + + return vv; +} + +inline void BitVector::Set(int n, bool b) +{ + if (b) + bits[n >> 5] |= (1 << (n & 31)); + else + bits[n >> 5] &= ~(1 << (n & 31)); +} + +inline bool BitVector::Get(int n) const +{ + return (bits[n >> 5] & (1 << (n & 31))) != 0; +} + +inline int BitVector::Size(void) const +{ + return size; +} + +inline int BitVector::DWSize(void) const +{ + return dwsize; +} + +inline void BitVector::Copy(unsigned* bits) const +{ + int i; + + if (dwsize > 0 && (size & 31)) + this->bits[dwsize - 1] &= (1 << (size & 31)) - 1; + + for (i = 0; i < dwsize; i++) + { + bits[i] = this->bits[i]; + } +} diff --git a/oscar64/oscar64/ByteCodeGenerator.cpp b/oscar64/oscar64/ByteCodeGenerator.cpp new file mode 100644 index 0000000..f82bff0 --- /dev/null +++ b/oscar64/oscar64/ByteCodeGenerator.cpp @@ -0,0 +1,6777 @@ +#include "ByteCodeGenerator.h" +#include "Assembler.h" + +static const uint32 LIVE_ACCU = 0x00000001; + + +static const char* ByteCodeNames[] = { + "NOP", + "EXIT", + + "CONST_8", + "CONST_P8", + "CONST_N8", + "CONST_16", + "CONST_32", + + "LOAD_REG_8", + "STORE_REG_8", + "LOAD_REG_16", + "STORE_REG_16", + "ADDR_REG", + "LOAD_REG_32", + "STORE_REG_32", + + "LOAD_ABS_8", + "LOAD_ABS_U8", + "LOAD_ABS_16", + "LOAD_ABS_32", + "LOAD_ABS_ADDR", + + "STORE_ABS_8", + "STORE_ABS_16", + "STORE_ABS_32", + + "LEA_ABS", + "LEA_ABS_INDEX", + "LEA_ABS_INDEX_U8", + "LEA_ACCU_INDEX", + + "LOAD_LOCAL_8", + "LOAD_LOCAL_U8", + "LOAD_LOCAL_16", + "LOAD_LOCAL_32", + + "STORE_LOCAL_8", + "STORE_LOCAL_16", + "STORE_LOCAL_32", + + "LEA_LOCAL", + + "STORE_FRAME_8", + "STORE_FRAME_16", + "STORE_FRAME_32", + + "LEA_FRAME", + + "LOAD_ADDR_8", + "LOAD_ADDR_U8", + "LOAD_ADDR_16", + "LOAD_ADDR_32", + + "STORE_ADDR_8", + "STORE_ADDR_16", + "STORE_ADDR_32", + + "BINOP_ADDR_16", + "BINOP_SUBR_16", + "BINOP_ANDR_16", + "BINOP_ORR_16", + "BINOP_XORR_16", + "BINOP_MULR_16", + "BINOP_DIVR_U16", + "BINOP_MODR_U16", + "BINOP_DIVR_I16", + "BINOP_MODR_I16", + "BINOP_SHLR_16", + "BINOP_SHRR_U16", + "BINOP_SHRR_I16", + + "BINOP_ADDA_16", + + "BINOP_ADDI_16", + "BINOP_SUBI_16", + "BINOP_ANDI_16", + "BINOP_ORI_16", + "BINOP_MULI8_16", + + "BINOP_ADDI_8", + "BINOP_ANDI_8", + "BINOP_ORI_8", + + "BINOP_SHLI_16", + "BINOP_SHRI_U16", + "BINOP_SHRI_I16", + + "BINOP_CMPUR_16", + "BINOP_CMPSR_16", + + "BINOP_CMPUI_16", + "BINOP_CMPSI_16", + + "BINOP_CMPUR_8", + "BINOP_CMPSR_8", + + "BINOP_CMPUI_8", + "BINOP_CMPSI_8", + + "OP_NEGATE_16", + "OP_INVERT_16", + + "BINOP_ADD_F32", + "BINOP_SUB_F32", + "BINOP_MUL_F32", + "BINOP_DIV_F32", + "BINOP_CMP_F32", + "OP_NEGATE_F32", + "OP_ABS_F32", + "OP_FLOOR_F32", + "OP_CEIL_F32", + + "CONV_U16_F32", + "CONV_I16_F32", + "CONV_F32_U16", + "CONV_F32_I16", + + "CONV_I8_I16", + + "JUMPS", + "BRANCHS_EQ", + "BRANCHS_NE", + "BRANCHS_GT", + "BRANCHS_GE", + "BRANCHS_LT", + "BRANCHS_LE", + + "JUMPF", + "BRANCHF_EQ", + "BRANCHF_NE", + "BRANCHF_GT", + "BRANCHF_GE", + "BRANCHF_LT", + "BRANCHF_LE", + + "LOOP_U8", + "MALLOC", + "FREE", + "FILL", + "FILL_LONG", + nullptr, + + "JSR", //114 + + nullptr, + nullptr, + + "NATIVE", //117 + + "ENTER", + "RETURN", + "CALL_ADDR", + "CALL_ABS", + "PUSH_FRAME", + "POP_FRAME", + + "COPY", + "COPY_LONG", + "STRCPY", + + "EXTRT" +}; + + +static ByteCode StoreTypedTmpCodes[] = { + BC_NOP, + BC_STORE_REG_8, + BC_STORE_REG_8, + BC_STORE_REG_16, + BC_STORE_REG_32, + BC_STORE_REG_32, + BC_STORE_REG_16 +}; + + +static ByteCode InvertBranchCondition(ByteCode code) +{ + switch (code) + { + case BC_BRANCHS_EQ: return BC_BRANCHS_NE; + case BC_BRANCHS_NE: return BC_BRANCHS_EQ; + case BC_BRANCHS_GT: return BC_BRANCHS_LE; + case BC_BRANCHS_GE: return BC_BRANCHS_LT; + case BC_BRANCHS_LT: return BC_BRANCHS_GE; + case BC_BRANCHS_LE: return BC_BRANCHS_GT; + default: + return code; + } +} + +static ByteCode TransposeBranchCondition(ByteCode code) +{ + switch (code) + { + case BC_BRANCHS_EQ: return BC_BRANCHS_EQ; + case BC_BRANCHS_NE: return BC_BRANCHS_NE; + case BC_BRANCHS_GT: return BC_BRANCHS_LT; + case BC_BRANCHS_GE: return BC_BRANCHS_LE; + case BC_BRANCHS_LT: return BC_BRANCHS_GT; + case BC_BRANCHS_LE: return BC_BRANCHS_GE; + default: + return code; + } +} + +ByteCodeInstruction::ByteCodeInstruction(ByteCode code) + : mCode(code), mRelocate(false), mRegisterFinal(false), mLinkerObject(nullptr), mValue(0), mRegister(0) +{ +} + + +bool ByteCodeInstruction::IsStore(void) const +{ + return + mCode == BC_STORE_ABS_8 || + mCode == BC_STORE_ABS_16 || + mCode == BC_STORE_ABS_32 || + mCode == BC_STORE_LOCAL_8 || + mCode == BC_STORE_LOCAL_16 || + mCode == BC_STORE_LOCAL_32 || + mCode == BC_STORE_FRAME_8 || + mCode == BC_STORE_FRAME_16 || + mCode == BC_STORE_FRAME_32 || + mCode == BC_STORE_ADDR_8 || + mCode == BC_STORE_ADDR_16 || + mCode == BC_STORE_ADDR_32; +} + +bool ByteCodeInstruction::ChangesAccu(void) const +{ + return ChangesRegister(BC_REG_ACCU); +} + +bool ByteCodeInstruction::UsesAccu(void) const +{ + return UsesRegister(BC_REG_ACCU); +} + +bool ByteCodeInstruction::ChangesAddr(void) const +{ + return ChangesRegister(BC_REG_ADDR); +} + +bool ByteCodeInstruction::UsesAddr(void) const +{ + return UsesRegister(BC_REG_ADDR); +} + +bool ByteCodeInstruction::LoadsRegister(uint32 reg) const +{ + if (mRegister == reg) + { + if (mCode >= BC_LOAD_ABS_8 && mCode <= BC_LOAD_ABS_32) + return true; + if (mCode >= BC_LOAD_LOCAL_8 && mCode <= BC_LOAD_LOCAL_32) + return true; + if (mCode >= BC_LOAD_ADDR_8 && mCode <= BC_LOAD_ADDR_32) + return true; + if (mCode >= BC_CONST_8 && mCode <= BC_CONST_32) + return true; + if (mCode == BC_LEA_ABS || mCode == BC_LEA_LOCAL || mCode == BC_LEA_FRAME) + return true; + } + + return false; +} + +bool ByteCodeInstruction::IsCommutative(void) const +{ + if (mCode == BC_BINOP_ADDR_16 || mCode == BC_BINOP_ANDR_16 || mCode == BC_BINOP_ORR_16 || mCode == BC_BINOP_XORR_16 || mCode == BC_BINOP_MULR_16) + return true; + + if (mCode == BC_BINOP_ADD_F32 || mCode == BC_BINOP_MUL_F32) + return true; + + if (mCode == BC_BINOP_ADD_L32 || mCode == BC_BINOP_AND_L32 || mCode == BC_BINOP_OR_L32 || mCode == BC_BINOP_XOR_L32 || mCode == BC_BINOP_MUL_L32) + return true; + + return false; +} + +bool ByteCodeInstruction::IsLocalStore(void) const +{ + return mCode >= BC_STORE_LOCAL_8 && mCode <= BC_STORE_LOCAL_32; +} + +bool ByteCodeInstruction::IsLocalLoad(void) const +{ + return mCode >= BC_LOAD_LOCAL_8 && mCode <= BC_LOAD_LOCAL_32 || mCode == BC_COPY || mCode == BC_STRCPY; +} + +bool ByteCodeInstruction::IsLocalAccess(void) const +{ + return IsLocalStore() || IsLocalLoad(); +} + +bool ByteCodeInstruction::IsShiftByRegister(void) const +{ + return + mCode == BC_BINOP_SHLR_16 || mCode == BC_BINOP_SHRR_I16 || mCode == BC_BINOP_SHRR_U16 || + mCode == BC_BINOP_SHL_L32 || mCode == BC_BINOP_SHR_U32 || mCode == BC_BINOP_SHR_I32; +} + + +bool ByteCodeInstruction::IsIntegerConst(void) const +{ + return mCode >= BC_CONST_8 && mCode <= BC_CONST_32; +} + +bool ByteCodeInstruction::IsCompare(void) const +{ + return mCode >= BC_BINOP_CMPUR_16 && mCode <= BC_BINOP_CMPSI_8 || + mCode == BC_BINOP_CMP_F32 || mCode == BC_BINOP_CMP_U32 || mCode == BC_BINOP_CMP_S32; +} + + +bool ByteCodeInstruction::CheckAccuSize(uint32 & used) +{ + bool changed = false; + + switch (mCode) + { + case BC_CONST_8: + case BC_CONST_P8: + case BC_CONST_N8: + case BC_CONST_16: + case BC_CONST_32: + if (mRegister == BC_REG_ACCU) + used = 0; + break; + + case BC_LOAD_REG_8: + if (mRegister != BC_REG_ACCU) + used = 0; + else if (!(used & 0xffffff00)) + { + mCode = BC_NOP; + changed = true; + } + else + used = 0x000000ff; + break; + + case BC_LOAD_REG_16: + if (!(used & 0xffffff00)) + { + mCode = BC_LOAD_REG_8; + changed = true; + } + used = 0; + break; + + case BC_LOAD_REG_32: + if (!(used & 0xffffff00)) + { + mCode = BC_LOAD_REG_8; + changed = true; + } + else if (!(used & 0xffff0000)) + { + mCode = BC_LOAD_REG_16; + changed = true; + } + used = 0; + break; + + case BC_STORE_REG_8: + used |= 0x000000ff; + break; + + case BC_STORE_ABS_8: + case BC_STORE_LOCAL_8: + case BC_STORE_FRAME_8: + case BC_STORE_ADDR_8: + if (mRegister == BC_REG_ACCU) + used |= 0x000000ff; + break; + + case BC_STORE_REG_16: + used |= 0x0000ffff; + break; + + case BC_STORE_ABS_16: + case BC_STORE_LOCAL_16: + case BC_STORE_FRAME_16: + case BC_STORE_ADDR_16: + if (mRegister == BC_REG_ACCU) + used |= 0x0000ffff; + break; + + case BC_STORE_REG_32: + used = 0xffffffff; + break; + + case BC_STORE_ABS_32: + case BC_STORE_LOCAL_32: + case BC_STORE_FRAME_32: + case BC_STORE_ADDR_32: + if (mRegister == BC_REG_ACCU) + used = 0xffffffff; + break; + + case BC_LOAD_ABS_8: + case BC_LOAD_ABS_U8: + if (mRegister == BC_REG_ACCU) + used = 0; + break; + case BC_LOAD_ABS_16: + if (mRegister == BC_REG_ACCU) + { + if (!(used & 0xffffff00)) + { + mCode = BC_LOAD_ABS_8; + changed = true; + } + used = 0; + } + break; + case BC_LOAD_ABS_32: + if (mRegister == BC_REG_ACCU) + { + if (!(used & 0xffffff00)) + { + mCode = BC_LOAD_ABS_8; + changed = true; + } + else if (!(used & 0xffff0000)) + { + mCode = BC_LOAD_ABS_16; + changed = true; + } + used = 0; + } + break; + + case BC_LOAD_ABS_ADDR: + break; + + case BC_LEA_ABS: + case BC_LEA_LOCAL: + case BC_LEA_FRAME: + if (mRegister == BC_REG_ACCU) + used = 0; + break; + + case BC_LEA_ABS_INDEX: + case BC_LEA_ABS_INDEX_U8: + case BC_ADDR_REG: + if (mRegister == BC_REG_ACCU) + used |= 0x0000ffff; + break; + + case BC_LEA_ACCU_INDEX: + used |= 0x0000ffff; + break; + + case BC_LOAD_LOCAL_8: + case BC_LOAD_LOCAL_U8: + if (mRegister == BC_REG_ACCU) + used = 0; + break; + case BC_LOAD_LOCAL_16: + if (mRegister == BC_REG_ACCU) + { + if (!(used & 0xffffff00)) + { + mCode = BC_LOAD_LOCAL_8; + changed = true; + } + used = 0; + } + break; + case BC_LOAD_LOCAL_32: + if (mRegister == BC_REG_ACCU) + { + if (!(used & 0xffffff00)) + { + mCode = BC_LOAD_LOCAL_8; + changed = true; + } + else if (!(used & 0xffff0000)) + { + mCode = BC_LOAD_LOCAL_16; + changed = true; + } + used = 0; + } + break; + + case BC_LOAD_ADDR_8: + case BC_LOAD_ADDR_U8: + if (mRegister == BC_REG_ACCU) + used = 0; + break; + case BC_LOAD_ADDR_16: + if (mRegister == BC_REG_ACCU) + { + if (!(used & 0xffffff00)) + { + mCode = BC_LOAD_ADDR_8; + changed = true; + } + used = 0; + } + break; + case BC_LOAD_ADDR_32: + if (mRegister == BC_REG_ACCU) + { + if (!(used & 0xffffff00)) + { + mCode = BC_LOAD_ADDR_8; + changed = true; + } + else if (!(used & 0xffff0000)) + { + mCode = BC_LOAD_ADDR_16; + changed = true; + } + used = 0; + } + break; + + case BC_BINOP_ADDR_16: + case BC_BINOP_SUBR_16: + case BC_BINOP_ANDR_16: + case BC_BINOP_ORR_16: + case BC_BINOP_XORR_16: + case BC_BINOP_MULR_16: + case BC_BINOP_SUBI_16: + case BC_BINOP_MULI8_16: + case BC_BINOP_SHLR_16: + case BC_BINOP_ADDI_8: + case BC_BINOP_ANDI_8: + case BC_BINOP_ORI_8: + case BC_BINOP_SHLI_16: + case BC_OP_NEGATE_16: + case BC_OP_INVERT_16: + break; + + case BC_BINOP_CMPUR_16: + case BC_BINOP_CMPSR_16: + case BC_BINOP_CMPUI_16: + case BC_BINOP_CMPSI_16: + + case BC_BINOP_DIVR_U16: + case BC_BINOP_MODR_U16: + case BC_BINOP_DIVR_I16: + case BC_BINOP_MODR_I16: + case BC_BINOP_SHRR_U16: + case BC_BINOP_SHRR_I16: + case BC_BINOP_SHRI_U16: + case BC_BINOP_SHRI_I16: + used |= 0x0000ffff; + break; + + case BC_LOOP_U8: + used = 0; + break; + + case BC_MALLOC: + case BC_FREE: + used = 0x0000ffff; + break; + + case BC_BINOP_ADDA_16: + used |= 0x0000ffff; + break; + + case BC_BINOP_ADDI_16: + if (mRegister == BC_REG_ACCU) + { + if (!(used & 0xffffff00)) + { + mCode = BC_BINOP_ADDI_8; + changed = true; + } + } + break; + case BC_BINOP_ANDI_16: + if (mRegister == BC_REG_ACCU) + { + if (!(used & 0xffffff00)) + { + mCode = BC_BINOP_ANDI_8; + changed = true; + } + } + break; + case BC_BINOP_ORI_16: + if (mRegister == BC_REG_ACCU) + { + if (!(used & 0xffffff00)) + { + mCode = BC_BINOP_ORI_8; + changed = true; + } + } + break; + + case BC_BINOP_CMPUR_8: + case BC_BINOP_CMPSR_8: + case BC_BINOP_CMPUI_8: + case BC_BINOP_CMPSI_8: + used = 0x000000ff; + break; + + case BC_BINOP_ADD_F32: + case BC_BINOP_SUB_F32: + case BC_BINOP_MUL_F32: + case BC_BINOP_DIV_F32: + case BC_BINOP_CMP_F32: + case BC_OP_NEGATE_F32: + case BC_OP_ABS_F32: + case BC_OP_FLOOR_F32: + case BC_OP_CEIL_F32: + case BC_CONV_F32_U16: + case BC_CONV_F32_I16: + case BC_CONV_U32_F32: + case BC_CONV_I32_F32: + case BC_CONV_F32_U32: + case BC_CONV_F32_I32: + used = 0xffffffff; + break; + + case BC_CONV_U16_F32: + case BC_CONV_I16_F32: + case BC_BRANCHS_EQ: + case BC_BRANCHS_NE: + case BC_BRANCHS_GT: + case BC_BRANCHS_GE: + case BC_BRANCHS_LT: + case BC_BRANCHS_LE: + case BC_BRANCHF_EQ: + case BC_BRANCHF_NE: + case BC_BRANCHF_GT: + case BC_BRANCHF_GE: + case BC_BRANCHF_LT: + case BC_BRANCHF_LE: + case BC_CONV_I16_I32: + case BC_CONV_U16_U32: + used = 0x0000ffff; + break; + + case BC_CONV_I8_I16: + if (mRegister == BC_REG_ACCU) + { + if (!(used & 0xffffff00)) + { + mCode = BC_NOP; + changed = true; + } + else + used = 0x000000ff; + } + break; + + case BC_BINOP_ADD_L32: + case BC_BINOP_SUB_L32: + case BC_BINOP_AND_L32: + case BC_BINOP_OR_L32: + case BC_BINOP_XOR_L32: + case BC_BINOP_MUL_L32: + case BC_BINOP_DIV_U32: + case BC_BINOP_MOD_U32: + case BC_BINOP_DIV_I32: + case BC_BINOP_MOD_I32: + case BC_BINOP_SHL_L32: + case BC_BINOP_SHR_U32: + case BC_BINOP_SHR_I32: + case BC_BINOP_CMP_U32: + case BC_BINOP_CMP_S32: + used = 0xffffffff; + break; + + case BC_RETURN: + used = 0xffffffff; + break; + + case BC_FILL: + case BC_FILL_LONG: + case BC_COPY: + case BC_COPY_LONG: + case BC_STRCPY: + used = 0x0000ffff; + break; + + default: + break; + } + + return changed; +} + + +bool ByteCodeInstruction::IsSame(const ByteCodeInstruction& ins) const +{ + if (mCode == ins.mCode && mValue == ins.mValue && mRegister == ins.mRegister && mLinkerObject == ins.mLinkerObject) + { + return true; + } + + return false; +} + +bool ByteCodeInstruction::StoresRegister(uint32 reg) const +{ + if (mRegister == reg) + { + if (mCode >= BC_STORE_ABS_8 && mCode <= BC_STORE_ABS_32) + return true; + if (mCode >= BC_STORE_LOCAL_8 && mCode <= BC_STORE_LOCAL_32) + return true; + if (mCode >= BC_STORE_FRAME_8 && mCode <= BC_STORE_FRAME_32) + return true; + if (mCode >= BC_STORE_ADDR_8 && mCode <= BC_STORE_ADDR_32) + return true; + } + + return false; + +} + +bool ByteCodeInstruction::UsesRegister(uint32 reg) const +{ + if (StoresRegister(reg)) + return true; + + if (mRegister == reg) + { + if (mCode == BC_LOAD_REG_8 || mCode == BC_LOAD_REG_16 || mCode == BC_LOAD_REG_32 || mCode == BC_ADDR_REG || mCode == BC_LEA_ABS_INDEX || mCode == BC_LEA_ABS_INDEX_U8 || mCode == BC_LEA_ACCU_INDEX) + return true; + + if (mCode >= BC_BINOP_ADDI_16 && mCode <= BC_BINOP_ORI_8) + return true; + + if (mCode >= BC_BINOP_ADDR_16 && mCode <= BC_BINOP_SHRR_I16) + return true; + + if (mCode == BC_BINOP_CMPUR_16 || mCode == BC_BINOP_CMPSR_16) + return true; + + if (mCode == BC_BINOP_CMPUR_8 || mCode == BC_BINOP_CMPSR_8) + return true; + + if (mCode >= BC_BINOP_ADD_F32 && mCode <= BC_BINOP_CMP_F32) + return true; + + if (mCode >= BC_BINOP_ADD_L32 && mCode <= BC_BINOP_CMP_S32) + return true; + + if (mCode == BC_BINOP_ADDA_16) + return true; + + if (mCode == BC_LOOP_U8) + return true; + } + + if (reg == BC_REG_ACCU) + { + if (mCode == BC_STORE_REG_8 || mCode == BC_STORE_REG_16 || mCode == BC_STORE_REG_32) + return true; + + if (mCode >= BC_BINOP_ADDR_16 && mCode <= BC_BINOP_SHRR_I16) + return true; + if (mCode >= BC_BINOP_CMPUR_16 && mCode <= BC_BINOP_CMPSI_16) + return true; + if (mCode >= BC_BINOP_CMPUR_8 && mCode <= BC_BINOP_CMPSI_8) + return true; + if (mCode >= BC_OP_NEGATE_16 && mCode <= BC_OP_INVERT_16) + return true; + if (mCode >= BC_BINOP_ADD_F32 && mCode <= BC_OP_CEIL_F32) + return true; + if (mCode >= BC_CONV_U16_F32 && mCode <= BC_CONV_F32_I16) + return true; + if (mCode >= BC_BINOP_SHLI_16 && mCode <= BC_BINOP_SHRI_I16) + return true; + if (mCode >= BC_CONV_I16_I32 && mCode <= BC_BINOP_CMP_S32) + return true; + if (mCode == BC_LEA_ACCU_INDEX) + return true; + if (mCode == BC_COPY || mCode == BC_STRCPY || mCode == BC_FILL) + return true; + if (mCode == BC_BINOP_ADDA_16) + return true; + if (mCode == BC_MALLOC || mCode == BC_FREE) + return true; + } + + if (reg == BC_REG_ADDR) + { + if (mCode >= BC_LOAD_ADDR_8 && mCode <= BC_STORE_ADDR_32) + return true; + + if (mCode == BC_COPY || mCode == BC_STRCPY || mCode == BC_FILL) + return true; + + if (mCode == BC_JSR || mCode == BC_CALL_ADDR || mCode == BC_CALL_ABS) + return true; + } + + return false; +} + + +bool ByteCodeInstruction::ChangesRegister(uint32 reg) const +{ + if (mRegister == reg) + { + if (mCode == BC_STORE_REG_8 || mCode == BC_STORE_REG_16 || mCode == BC_STORE_REG_32) + return true; + if (mCode >= BC_LOAD_ABS_8 && mCode <= BC_LOAD_ABS_32) + return true; + if (mCode >= BC_LOAD_LOCAL_8 && mCode <= BC_LOAD_LOCAL_32) + return true; + if (mCode >= BC_LOAD_ADDR_8 && mCode <= BC_LOAD_ADDR_32) + return true; + if (mCode >= BC_CONST_8 && mCode <= BC_CONST_32) + return true; + if (mCode == BC_LEA_ABS || mCode == BC_LEA_LOCAL || mCode == BC_LEA_FRAME) + return true; + if (mCode >= BC_BINOP_ADDI_16 && mCode <= BC_BINOP_ORI_8) + return true; + if (mCode == BC_BINOP_ADDA_16) + return true; + if (mCode == BC_LOOP_U8) + return true; + } + + if (reg >= BC_REG_WORK && reg < BC_REG_FPARAMS_END || reg >= BC_REG_TMP && reg < BC_REG_TMP_SAVED) + { + if (mCode == BC_JSR || mCode == BC_CALL_ADDR || mCode == BC_CALL_ABS) + return true; + } + + if (reg == BC_REG_ACCU) + { + if (mCode == BC_LOAD_REG_8 || mCode == BC_LOAD_REG_16 || mCode == BC_LOAD_REG_32) + return true; + if (mCode >= BC_BINOP_ADDR_16 && mCode <= BC_BINOP_SHRR_I16) + return true; + if (mCode >= BC_BINOP_CMPUR_16 && mCode <= BC_BINOP_CMPSI_16) + return true; + if (mCode >= BC_BINOP_CMPUR_8 && mCode <= BC_BINOP_CMPSI_8) + return true; + if (mCode >= BC_OP_NEGATE_16 && mCode <= BC_OP_INVERT_16) + return true; + if (mCode >= BC_BINOP_ADD_F32 && mCode <= BC_OP_CEIL_F32) + return true; + if (mCode >= BC_CONV_U16_F32 && mCode <= BC_CONV_F32_I16) + return true; + if (mCode >= BC_BINOP_SHLI_16 && mCode <= BC_BINOP_SHRI_I16) + return true; + if (mCode == BC_JSR || mCode == BC_CALL_ADDR || mCode == BC_CALL_ABS) + return true; + if (mCode >= BC_CONV_I16_I32 && mCode <= BC_BINOP_CMP_S32) + return true; + if (mCode == BC_LOOP_U8) + return true; + if (mCode == BC_MALLOC || mCode == BC_FREE) + return true; + } + + if (reg == BC_REG_ADDR) + { + if (mCode == BC_ADDR_REG) + return true; + if (mCode >= BC_LOAD_ABS_8 && mCode <= BC_STORE_ABS_32) + return true; + if (mCode == BC_JSR || mCode == BC_CALL_ADDR || mCode == BC_CALL_ABS) + return true; + if (mCode == BC_LEA_ABS_INDEX || mCode == BC_LEA_ABS_INDEX_U8 || mCode == BC_LEA_ACCU_INDEX) + return true; + } + + if (reg == BC_REG_WORK) + { + if (mCode == BC_JSR || mCode == BC_CALL_ADDR || mCode == BC_CALL_ABS) + return true; + if (mCode == BC_MALLOC || mCode == BC_FREE) + return true; + + if (mCode == BC_BINOP_DIVR_I16 || mCode == BC_BINOP_DIVR_U16 || mCode == BC_BINOP_MODR_I16 || mCode == BC_BINOP_MODR_U16 || + mCode == BC_BINOP_DIV_I32 || mCode == BC_BINOP_DIV_U32 || mCode == BC_BINOP_MOD_I32 || mCode == BC_BINOP_MOD_U32 || + mCode == BC_BINOP_MULR_16 || mCode == BC_BINOP_MULI8_16 || mCode == BC_BINOP_MUL_L32) + return true; + + if (mCode >= BC_BINOP_ADD_F32 && mCode <= BC_OP_CEIL_F32) + return true; + if (mCode >= BC_CONV_U16_F32 && mCode <= BC_CONV_F32_I16) + return true; + } + + return false; +} + +void ByteCodeInstruction::Assemble(ByteCodeGenerator* generator, ByteCodeBasicBlock* block) +{ + switch (mCode) + { + case BC_NOP: + break; + case BC_EXIT: + block->PutCode(generator, mCode); + break; + + case BC_CONST_8: + block->PutCode(generator, BC_CONST_8); block->PutByte(mRegister); block->PutByte(uint8(mValue)); + break; + case BC_CONST_P8: + case BC_CONST_N8: + case BC_CONST_16: + if (mRelocate) + { + block->PutCode(generator, mCode); + block->PutByte(mRegister); + + LinkerReference rl; + rl.mOffset = block->mCode.Size(); + rl.mFlags = LREF_HIGHBYTE | LREF_LOWBYTE; + rl.mRefObject = mLinkerObject; + rl.mRefOffset = mValue; + block->mRelocations.Push(rl); + + block->PutWord(0); + } + else if (mValue >= 0 && mValue < 256) + { + block->PutCode(generator, BC_CONST_P8); block->PutByte(mRegister); block->PutByte(uint8(mValue)); + } + else if (mValue < 0 && mValue >= -256) + { + block->PutCode(generator, BC_CONST_N8); block->PutByte(mRegister); block->PutByte(uint8(mValue)); + } + else + { + block->PutCode(generator, BC_CONST_16); block->PutByte(mRegister); block->PutWord(uint16(mValue)); + } + break; + case BC_CONST_32: + block->PutCode(generator, BC_CONST_32); block->PutByte(mRegister); block->PutDWord(uint32(mValue)); + break; + + case BC_LOAD_REG_8: + case BC_STORE_REG_8: + case BC_LOAD_REG_16: + case BC_STORE_REG_16: + case BC_LOAD_REG_32: + case BC_STORE_REG_32: + case BC_ADDR_REG: + block->PutCode(generator, mCode); + block->PutByte(mRegister); + break; + + case BC_LOAD_ABS_8: + case BC_LOAD_ABS_U8: + case BC_LOAD_ABS_16: + case BC_LOAD_ABS_32: + case BC_STORE_ABS_8: + case BC_STORE_ABS_16: + case BC_STORE_ABS_32: + block->PutCode(generator, mCode); + if (mRelocate) + { + LinkerReference rl; + rl.mOffset = block->mCode.Size(); + rl.mFlags = LREF_HIGHBYTE | LREF_LOWBYTE; + rl.mRefObject = mLinkerObject; + rl.mRefOffset = mValue; + block->mRelocations.Push(rl); + block->PutWord(0); + } + else + block->PutWord(mValue); + + block->PutByte(mRegister); + break; + + case BC_LOAD_ABS_ADDR: + block->PutCode(generator, mCode); + if (mRelocate) + { + LinkerReference rl; + rl.mOffset = block->mCode.Size(); + rl.mFlags = LREF_HIGHBYTE | LREF_LOWBYTE; + rl.mRefObject = mLinkerObject; + rl.mRefOffset = mValue; + block->mRelocations.Push(rl); + block->PutWord(0); + } + else + block->PutWord(mValue); + break; + + case BC_LEA_ABS: + case BC_LEA_ABS_INDEX: + case BC_LEA_ABS_INDEX_U8: + block->PutCode(generator, mCode); block->PutByte(mRegister); + if (mRelocate) + { + LinkerReference rl; + rl.mOffset = block->mCode.Size(); + rl.mFlags = LREF_HIGHBYTE | LREF_LOWBYTE; + rl.mRefObject = mLinkerObject; + rl.mRefOffset = mValue; + block->mRelocations.Push(rl); + block->PutWord(0); + } + else + block->PutWord(uint16(mValue)); + break; + + case BC_LOAD_LOCAL_8: + case BC_LOAD_LOCAL_U8: + case BC_LOAD_LOCAL_16: + case BC_LOAD_LOCAL_32: + case BC_STORE_LOCAL_8: + case BC_STORE_LOCAL_16: + case BC_STORE_LOCAL_32: + case BC_STORE_FRAME_8: + case BC_STORE_FRAME_16: + case BC_STORE_FRAME_32: + block->PutCode(generator, mCode); + block->PutByte(mRegister); + block->PutByte(uint8(mValue)); + break; + + case BC_LEA_LOCAL: + case BC_LEA_FRAME: + block->PutCode(generator, mCode); + block->PutByte(mRegister); + block->PutWord(uint16(mValue)); + break; + + case BC_LEA_ACCU_INDEX: + block->PutCode(generator, mCode); + block->PutByte(mRegister); + break; + + case BC_BINOP_ADDR_16: + case BC_BINOP_SUBR_16: + case BC_BINOP_ANDR_16: + case BC_BINOP_ORR_16: + case BC_BINOP_XORR_16: + case BC_BINOP_MULR_16: + case BC_BINOP_DIVR_U16: + case BC_BINOP_MODR_U16: + case BC_BINOP_DIVR_I16: + case BC_BINOP_MODR_I16: + case BC_BINOP_SHLR_16: + case BC_BINOP_SHRR_U16: + case BC_BINOP_SHRR_I16: + case BC_BINOP_ADDA_16: + block->PutCode(generator, mCode); + block->PutByte(mRegister); + break; + + case BC_BINOP_ADDI_16: + case BC_BINOP_SUBI_16: + case BC_BINOP_ANDI_16: + case BC_BINOP_ORI_16: + block->PutCode(generator, mCode); + block->PutByte(mRegister); + block->PutWord(uint16(mValue)); + break; + case BC_BINOP_MULI8_16: + case BC_BINOP_ADDI_8: + case BC_BINOP_ANDI_8: + case BC_BINOP_ORI_8: + block->PutCode(generator, mCode); + block->PutByte(mRegister); + block->PutByte(mValue); + break; + case BC_BINOP_SHLI_16: + case BC_BINOP_SHRI_U16: + case BC_BINOP_SHRI_I16: + block->PutCode(generator, mCode); + block->PutByte(uint8(mValue)); + break; + + case BC_BINOP_CMPUR_16: + case BC_BINOP_CMPSR_16: + case BC_BINOP_CMPUR_8: + case BC_BINOP_CMPSR_8: + block->PutCode(generator, mCode); + block->PutByte(mRegister); + break; + + case BC_BINOP_CMPUI_16: + case BC_BINOP_CMPSI_16: + block->PutCode(generator, mCode); + block->PutWord(uint16(mValue)); + break; + + case BC_BINOP_CMPUI_8: + case BC_BINOP_CMPSI_8: + block->PutCode(generator, mCode); + block->PutByte(uint8(mValue)); + break; + + case BC_LOOP_U8: + block->PutCode(generator, mCode); + block->PutByte(mRegister); + block->PutByte(uint8(mValue)); + break; + + case BC_OP_NEGATE_16: + case BC_OP_INVERT_16: + block->PutCode(generator, mCode); + break; + + case BC_BINOP_ADD_F32: + case BC_BINOP_SUB_F32: + case BC_BINOP_MUL_F32: + case BC_BINOP_DIV_F32: + case BC_BINOP_CMP_F32: + block->PutCode(generator, mCode); + block->PutByte(mRegister); + break; + + case BC_OP_NEGATE_F32: + case BC_OP_ABS_F32: + case BC_OP_FLOOR_F32: + case BC_OP_CEIL_F32: + case BC_CONV_U16_F32: + case BC_CONV_I16_F32: + case BC_CONV_F32_U16: + case BC_CONV_F32_I16: + block->PutCode(generator, mCode); + break; + + case BC_CONV_I8_I16: + block->PutCode(generator, mCode); + block->PutByte(mRegister); + break; + + case BC_JUMPS: + case BC_BRANCHS_EQ: + case BC_BRANCHS_NE: + case BC_BRANCHS_GT: + case BC_BRANCHS_GE: + case BC_BRANCHS_LT: + case BC_BRANCHS_LE: + assert(false); + break; + + case BC_JUMPF: + case BC_BRANCHF_EQ: + case BC_BRANCHF_NE: + case BC_BRANCHF_GT: + case BC_BRANCHF_GE: + case BC_BRANCHF_LT: + case BC_BRANCHF_LE: + assert(false); + break; + + case BC_ENTER: + case BC_RETURN: + assert(false); + break; + + case BC_MALLOC: + case BC_FREE: + case BC_CALL_ADDR: + block->PutCode(generator, mCode); + break; + + case BC_CALL_ABS: + { + block->PutCode(generator, mCode); + + LinkerReference rl; + rl.mOffset = block->mCode.Size(); + rl.mFlags = LREF_HIGHBYTE | LREF_LOWBYTE; + rl.mRefObject = mLinkerObject; + rl.mRefOffset = 0; + block->mRelocations.Push(rl); + + block->PutWord(0); + } break; + + case BC_PUSH_FRAME: + case BC_POP_FRAME: + block->PutCode(generator, mCode); + block->PutWord(uint16(mValue + 2)); + break; + + case BC_JSR: + { + block->PutCode(generator, mCode); + + LinkerReference rl; + rl.mOffset = block->mCode.Size(); + rl.mFlags = LREF_HIGHBYTE | LREF_LOWBYTE; + rl.mRefObject = mLinkerObject; + rl.mRefOffset = 0; + block->mRelocations.Push(rl); + + block->PutWord(0); + } break; + + case BC_LOAD_ADDR_8: + case BC_LOAD_ADDR_U8: + case BC_LOAD_ADDR_16: + case BC_LOAD_ADDR_32: + case BC_STORE_ADDR_8: + case BC_STORE_ADDR_16: + case BC_STORE_ADDR_32: + block->PutCode(generator, mCode); + block->PutByte(mRegister); + block->PutByte(mValue); + break; + + case BC_FILL: + case BC_FILL_LONG: + if (mValue < 256) + { + block->PutCode(generator, BC_FILL); + block->PutByte(uint8(mValue)); + } + else + { + block->PutCode(generator, BC_FILL_LONG); + block->PutWord(uint16(mValue)); + } + break; + + case BC_COPY: + case BC_COPY_LONG: + if (mValue < 256) + { + block->PutCode(generator, BC_COPY); + block->PutByte(uint8(mValue)); + } + else + { + block->PutCode(generator, BC_COPY_LONG); + block->PutWord(uint16(mValue)); + } + break; + + case BC_STRCPY: + block->PutCode(generator, BC_STRCPY); + break; + + case BC_CONV_I16_I32: + case BC_CONV_U16_U32: + case BC_OP_NEGATE_32: + case BC_OP_INVERT_32: + case BC_BINOP_ADD_L32: + case BC_BINOP_SUB_L32: + case BC_BINOP_AND_L32: + case BC_BINOP_OR_L32: + case BC_BINOP_XOR_L32: + case BC_BINOP_MUL_L32: + case BC_BINOP_DIV_U32: + case BC_BINOP_MOD_U32: + case BC_BINOP_DIV_I32: + case BC_BINOP_MOD_I32: + case BC_BINOP_SHL_L32: + case BC_BINOP_SHR_U32: + case BC_BINOP_SHR_I32: + case BC_BINOP_CMP_U32: + case BC_BINOP_CMP_S32: + { + block->PutCode(generator, BC_EXTRT); + + LinkerReference rl; + rl.mOffset = block->mCode.Size(); + rl.mFlags = LREF_HIGHBYTE | LREF_LOWBYTE; + rl.mRefObject = generator->mExtByteCodes[mCode - 128]; + rl.mRefOffset = 0; + block->mRelocations.Push(rl); + block->PutWord(0); + block->PutByte(mRegister); + break; + } + case BC_CONV_U32_F32: + case BC_CONV_I32_F32: + case BC_CONV_F32_U32: + case BC_CONV_F32_I32: + { + block->PutCode(generator, BC_EXTRT); + + LinkerReference rl; + rl.mOffset = block->mCode.Size(); + rl.mFlags = LREF_HIGHBYTE | LREF_LOWBYTE; + rl.mRefObject = generator->mExtByteCodes[mCode - 128]; + rl.mRefOffset = 0; + block->mRelocations.Push(rl); + block->PutWord(0); + block->PutByte(mRegister); + break; + } + + default: + assert(false); + } +} + + +void ByteCodeBasicBlock::PutByte(uint8 code) +{ + this->mCode.Insert(code); +} + +void ByteCodeBasicBlock::PutWord(uint16 code) +{ + this->mCode.Insert((uint8)(code & 0xff)); + this->mCode.Insert((uint8)(code >> 8)); +} + +void ByteCodeBasicBlock::PutDWord(uint32 code) +{ + this->mCode.Insert((uint8)(code & 0xff)); + this->mCode.Insert((uint8)((code >> 8) & 0xff)); + this->mCode.Insert((uint8)((code >> 16) & 0xff)); + this->mCode.Insert((uint8)((code >> 24) & 0xff)); +} + +void ByteCodeBasicBlock::PutBytes(const uint8* code, int num) +{ + while (num--) + { + this->mCode.Insert(*code++); + } +} + +void ByteCodeBasicBlock::PutCode(ByteCodeGenerator* generator, ByteCode code) +{ + PutByte(uint8(code) * 2); + generator->mByteCodeUsed[code]++; +} + +int ByteCodeBasicBlock::PutBranch(ByteCodeGenerator* generator, ByteCode code, int offset) +{ + if (offset >= -126 && offset <= 129) + { + PutCode(generator, code); + PutByte(offset - 2); + return 2; + } + else + { + PutCode(generator, ByteCode(code + (BC_JUMPF - BC_JUMPS))); + PutWord(offset - 3); + return 3; + } +} + +ByteCodeBasicBlock::ByteCodeBasicBlock(void) + : mRelocations({ 0 }), mIns(ByteCodeInstruction(BC_NOP)), mEntryBlocks(nullptr) +{ + mTrueJump = mFalseJump = NULL; + mTrueLink = mFalseLink = NULL; + mOffset = -1; + mPlaced = false; + mAssembled = false; + mBypassed = false; + mExitLive = 0; +} + +void ByteCodeBasicBlock::IntConstToAccu(int64 val) +{ + ByteCodeInstruction ins(BC_CONST_16); + ins.mRegister = BC_REG_ACCU; + ins.mValue = int(val); + mIns.Push(ins); +} + +void ByteCodeBasicBlock::LongConstToAccu(int64 val) +{ + ByteCodeInstruction bins(BC_CONST_32); + bins.mRegister = BC_REG_ACCU; + bins.mValue = int(val); + mIns.Push(bins); +} + +void ByteCodeBasicBlock::LongConstToWork(int64 val) +{ + ByteCodeInstruction bins(BC_CONST_32); + bins.mRegister = BC_REG_WORK; + bins.mValue = int(val); + mIns.Push(bins); +} + +void ByteCodeBasicBlock::FloatConstToAccu(double val) +{ + union { float f; int v; } cc; + cc.f = float(val); + ByteCodeInstruction bins(BC_CONST_32); + bins.mRegister = BC_REG_ACCU; + bins.mValue = cc.v; + mIns.Push(bins); +} + +void ByteCodeBasicBlock::FloatConstToWork(double val) +{ + union { float f; int v; } cc; + cc.f = float(val); + ByteCodeInstruction bins(BC_CONST_32); + bins.mRegister = BC_REG_WORK; + bins.mValue = cc.v; + mIns.Push(bins); +} + + +void ByteCodeBasicBlock::IntConstToAddr(int64 val) +{ + ByteCodeInstruction ins(BC_CONST_16); + ins.mRegister = BC_REG_ADDR; + ins.mValue = int(val); + mIns.Push(ins); +} + +void ByteCodeBasicBlock::LoadOperandAddress(InterCodeProcedure* proc, const InterOperand& op, int reg) +{ + if (op.mTemp < 0) + { + if (op.mMemory == IM_GLOBAL) + { + ByteCodeInstruction bins(BC_LEA_ABS); + bins.mRegister = reg; + bins.mLinkerObject = op.mLinkerObject; + bins.mValue = int(op.mIntConst); + bins.mRelocate = true; + mIns.Push(bins); + } + else if (op.mMemory == IM_ABSOLUTE) + { + ByteCodeInstruction bins(BC_LEA_ABS); + bins.mRegister = reg; + bins.mValue = int(op.mIntConst); + mIns.Push(bins); + } + else if (op.mMemory == IM_LOCAL) + { + ByteCodeInstruction bins(BC_LEA_LOCAL); + bins.mRegister = reg; + bins.mValue = int(op.mIntConst + proc->mLocalVars[op.mVarIndex]->mOffset); + mIns.Push(bins); + } + else if (op.mMemory == IM_PARAM) + { + ByteCodeInstruction bins(BC_LEA_LOCAL); + bins.mRegister = reg; + bins.mValue = int(op.mIntConst + op.mVarIndex + proc->mLocalSize + 2); + mIns.Push(bins); + } + else if (op.mMemory == IM_FPARAM || op.mMemory == IM_FFRAME) + { + ByteCodeInstruction bins(BC_LEA_ABS); + bins.mRegister = reg; + bins.mValue = int(BC_REG_FPARAMS + op.mIntConst + op.mVarIndex); + mIns.Push(bins); + } + else if (op.mMemory == IM_FRAME) + { + ByteCodeInstruction bins(BC_LEA_FRAME); + bins.mRegister = reg; + bins.mValue = int(op.mVarIndex + op.mIntConst + 2); + mIns.Push(bins); + } + else if (op.mMemory == IM_PROCEDURE) + { + ByteCodeInstruction bins(BC_CONST_16); + bins.mRegister = reg; + bins.mLinkerObject = op.mLinkerObject; + bins.mValue = 0; + bins.mRelocate = true; + mIns.Push(bins); + } + } + else if (reg == BC_REG_ADDR) + { + if (op.mIntConst == 0) + { + ByteCodeInstruction sins(BC_ADDR_REG); + sins.mRegister = BC_REG_TMP + proc->mTempOffset[op.mTemp]; + sins.mRegisterFinal = op.mFinal; + mIns.Push(sins); + } + else + { + ByteCodeInstruction lins(BC_LEA_ABS_INDEX); + lins.mRegister = BC_REG_TMP + proc->mTempOffset[op.mTemp]; + lins.mRegisterFinal = op.mFinal; + lins.mValue = int(op.mIntConst); + mIns.Push(lins); + } + } + else if (reg == BC_REG_ACCU) + { + ByteCodeInstruction dins(BC_LOAD_REG_16); + dins.mRegister = BC_REG_TMP + proc->mTempOffset[op.mTemp]; + dins.mRegisterFinal = op.mFinal; + mIns.Push(dins); + if (op.mIntConst) + { + ByteCodeInstruction iins(BC_BINOP_ADDI_16); + iins.mRegister = BC_REG_ACCU; + iins.mValue = int(op.mIntConst); + mIns.Push(iins); + } + + } +} + +void ByteCodeBasicBlock::LoadConstant(InterCodeProcedure* proc, const InterInstruction * ins) +{ + if (ins->mDst.mType == IT_FLOAT) + { + union { float f; int v; } cc; + cc.f = float(ins->mConst.mFloatConst); + ByteCodeInstruction bins(BC_CONST_32); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + bins.mValue = cc.v; + mIns.Push(bins); + } + else if (ins->mDst.mType == IT_POINTER) + { + if (ins->mConst.mMemory == IM_GLOBAL) + { + ByteCodeInstruction bins(BC_LEA_ABS); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + bins.mLinkerObject = ins->mConst.mLinkerObject; + bins.mValue = int(ins->mConst.mIntConst); + bins.mRelocate = true; + mIns.Push(bins); + } + else if (ins->mConst.mMemory == IM_ABSOLUTE) + { + ByteCodeInstruction bins(BC_LEA_ABS); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + bins.mValue = int(ins->mConst.mIntConst); + mIns.Push(bins); + } + else if (ins->mConst.mMemory == IM_LOCAL) + { + ByteCodeInstruction bins(BC_LEA_LOCAL); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + bins.mValue = int(ins->mConst.mIntConst + proc->mLocalVars[ins->mConst.mVarIndex]->mOffset); + mIns.Push(bins); + } + else if (ins->mConst.mMemory == IM_PARAM) + { + ByteCodeInstruction bins(BC_LEA_LOCAL); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + bins.mValue = int(ins->mConst.mIntConst + ins->mConst.mVarIndex + proc->mLocalSize + 2); + mIns.Push(bins); + } + else if (ins->mConst.mMemory == IM_FPARAM || ins->mConst.mMemory == IM_FFRAME) + { + ByteCodeInstruction bins(BC_LEA_ABS); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + bins.mValue = int(BC_REG_FPARAMS + ins->mConst.mIntConst + ins->mConst.mVarIndex); + mIns.Push(bins); + } + else if (ins->mConst.mMemory == IM_FRAME) + { + ByteCodeInstruction bins(BC_LEA_FRAME); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + bins.mValue = int(ins->mConst.mVarIndex + ins->mSrc[1].mIntConst + 2); + mIns.Push(bins); + } + else if (ins->mConst.mMemory == IM_PROCEDURE) + { + ByteCodeInstruction bins(BC_CONST_16); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + bins.mLinkerObject = ins->mConst.mLinkerObject; + bins.mValue = 0; + bins.mRelocate = true; + mIns.Push(bins); + } + } + else if (ins->mDst.mType == IT_BOOL || ins->mDst.mType == IT_INT8) + { + ByteCodeInstruction bins(BC_CONST_8); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + bins.mValue = int(ins->mConst.mIntConst); + mIns.Push(bins); + } + else if (ins->mDst.mType == IT_INT32) + { + ByteCodeInstruction bins(BC_CONST_32); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + bins.mValue = int(ins->mConst.mIntConst); + mIns.Push(bins); + } + else + { + ByteCodeInstruction bins(BC_CONST_16); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + bins.mValue = int(ins->mConst.mIntConst); + mIns.Push(bins); + } + +} + +void ByteCodeBasicBlock::FillValue(InterCodeProcedure* proc, const InterInstruction* ins) +{ + LoadOperandAddress(proc, ins->mSrc[1], BC_REG_ADDR); + + if (ins->mSrc[0].mTemp < 0) + { + ByteCodeInstruction cins(BC_CONST_8); + cins.mRegister = BC_REG_ACCU; + cins.mValue = int(ins->mSrc[0].mIntConst); + mIns.Push(cins); + } + else + { + ByteCodeInstruction lins(BC_LOAD_REG_8); + lins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + lins.mRegisterFinal = ins->mSrc[0].mFinal; + mIns.Push(lins); + } + + ByteCodeInstruction cins(BC_FILL); + cins.mValue = ins->mConst.mOperandSize; + mIns.Push(cins); +} + +void ByteCodeBasicBlock::CopyValue(InterCodeProcedure* proc, const InterInstruction * ins) +{ + LoadOperandAddress(proc, ins->mSrc[0], BC_REG_ACCU); + LoadOperandAddress(proc, ins->mSrc[1], BC_REG_ADDR); + + ByteCodeInstruction cins(BC_COPY); + cins.mValue = ins->mConst.mOperandSize; + mIns.Push(cins); +} + +void ByteCodeBasicBlock::StrcpyValue(InterCodeProcedure* proc, const InterInstruction* ins) +{ + ByteCodeInstruction sins(BC_ADDR_REG); + sins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp]; + sins.mRegisterFinal = ins->mSrc[1].mFinal; + mIns.Push(sins); + ByteCodeInstruction dins(BC_LOAD_REG_16); + dins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + dins.mRegisterFinal = ins->mSrc[0].mFinal; + mIns.Push(dins); + ByteCodeInstruction cins(BC_STRCPY); + mIns.Push(cins); +} + +void ByteCodeBasicBlock::CallMalloc(InterCodeProcedure* proc, const InterInstruction* ins) +{ + if (ins->mSrc[0].mTemp < 0) + { + ByteCodeInstruction dins(BC_CONST_16); + dins.mRegister = BC_REG_ACCU; + dins.mValue = int(ins->mSrc[0].mIntConst & 0xffff); + mIns.Push(dins); + } + else + { + ByteCodeInstruction dins(BC_LOAD_REG_16); + dins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + dins.mRegisterFinal = ins->mSrc[0].mFinal; + mIns.Push(dins); + } + ByteCodeInstruction cins(BC_MALLOC); + mIns.Push(cins); + + if (ins->mDst.mTemp >= 0) + { + ByteCodeInstruction bins(BC_STORE_REG_16); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + mIns.Push(bins); + } +} + +void ByteCodeBasicBlock::CallFree(InterCodeProcedure* proc, const InterInstruction* ins) +{ + if (ins->mSrc[0].mTemp < 0) + { + ByteCodeInstruction dins(BC_CONST_16); + dins.mRegister = BC_REG_ACCU; + dins.mValue = int(ins->mSrc[0].mIntConst & 0xffff); + mIns.Push(dins); + } + else + { + ByteCodeInstruction dins(BC_LOAD_REG_16); + dins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + dins.mRegisterFinal = ins->mSrc[0].mFinal; + mIns.Push(dins); + } + ByteCodeInstruction cins(BC_FREE); + mIns.Push(cins); +} + +void ByteCodeBasicBlock::StoreDirectValue(InterCodeProcedure* proc, const InterInstruction * ins) +{ + if (ins->mSrc[0].mType == IT_FLOAT) + { + if (ins->mSrc[1].mTemp < 0) + { + if (ins->mSrc[0].mTemp < 0) + { + FloatConstToAccu(ins->mSrc[0].mFloatConst); + + if (ins->mSrc[1].mMemory == IM_GLOBAL) + { + ByteCodeInstruction bins(BC_STORE_ABS_32); + bins.mRelocate = true; + bins.mLinkerObject = ins->mSrc[1].mLinkerObject; + bins.mValue = int(ins->mSrc[1].mIntConst); + bins.mRegister = BC_REG_ACCU; + mIns.Push(bins); + } + else if (ins->mSrc[1].mMemory == IM_ABSOLUTE) + { + ByteCodeInstruction bins(BC_STORE_ABS_32); + bins.mValue = int(ins->mSrc[1].mIntConst); + bins.mRegister = BC_REG_ACCU; + mIns.Push(bins); + } + else if (ins->mSrc[1].mMemory == IM_FPARAM || ins->mSrc[1].mMemory == IM_FFRAME) + { + ByteCodeInstruction bins(BC_STORE_REG_32); + bins.mRegister = int(BC_REG_FPARAMS + ins->mSrc[1].mVarIndex + ins->mSrc[1].mIntConst); + mIns.Push(bins); + } + else if (ins->mSrc[1].mMemory == IM_LOCAL || ins->mSrc[1].mMemory == IM_PARAM) + { + int index = int(ins->mSrc[1].mIntConst); + if (ins->mSrc[1].mMemory == IM_LOCAL) + index += proc->mLocalVars[ins->mSrc[1].mVarIndex]->mOffset; + else + index += ins->mSrc[1].mVarIndex + proc->mLocalSize + 2; + + if (index <= 252) + { + ByteCodeInstruction bins(BC_STORE_LOCAL_32); + bins.mRegister = BC_REG_ACCU; + bins.mRegisterFinal = ins->mSrc[0].mFinal; + bins.mValue = index; + mIns.Push(bins); + } + else + { + ByteCodeInstruction lins(BC_LEA_LOCAL); + lins.mRegister = BC_REG_ADDR; + lins.mValue = index; + mIns.Push(lins); + ByteCodeInstruction bins(BC_STORE_ADDR_32); + bins.mRegister = BC_REG_ACCU; + bins.mRegisterFinal = ins->mSrc[0].mFinal; + bins.mValue = 0; + mIns.Push(bins); + } + } + else if (ins->mSrc[1].mMemory == IM_FRAME) + { + ByteCodeInstruction bins(BC_STORE_FRAME_32); + bins.mRegister = BC_REG_ACCU; + bins.mRegisterFinal = ins->mSrc[0].mFinal; + bins.mValue = int(ins->mSrc[1].mVarIndex + ins->mSrc[1].mIntConst + 2); + mIns.Push(bins); + } + } + else + { + if (ins->mSrc[1].mMemory == IM_GLOBAL) + { + ByteCodeInstruction bins(BC_STORE_ABS_32); + bins.mRelocate = true; + bins.mLinkerObject = ins->mSrc[1].mLinkerObject; + bins.mValue = int(ins->mSrc[1].mIntConst); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + mIns.Push(bins); + } + else if (ins->mSrc[1].mMemory == IM_ABSOLUTE) + { + ByteCodeInstruction bins(BC_STORE_ABS_32); + bins.mValue = int(ins->mSrc[1].mIntConst); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + mIns.Push(bins); + } + else if (ins->mSrc[1].mMemory == IM_FPARAM || ins->mSrc[1].mMemory == IM_FFRAME) + { + ByteCodeInstruction ains(BC_LOAD_REG_32); + ains.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + mIns.Push(ains); + + ByteCodeInstruction bins(BC_STORE_REG_32); + bins.mRegister = int(BC_REG_FPARAMS + ins->mSrc[1].mVarIndex + ins->mSrc[1].mIntConst); + mIns.Push(bins); + } + else if (ins->mSrc[1].mMemory == IM_LOCAL || ins->mSrc[1].mMemory == IM_PARAM) + { + int index = int(ins->mSrc[1].mIntConst); + if (ins->mSrc[1].mMemory == IM_LOCAL) + index += proc->mLocalVars[ins->mSrc[1].mVarIndex]->mOffset; + else + index += ins->mSrc[1].mVarIndex + proc->mLocalSize + 2; + + if (index <= 252) + { + ByteCodeInstruction bins(BC_STORE_LOCAL_32); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + bins.mRegisterFinal = ins->mSrc[0].mFinal; + bins.mValue = index; + mIns.Push(bins); + } + else + { + ByteCodeInstruction lins(BC_LEA_LOCAL); + lins.mRegister = BC_REG_ADDR; + lins.mValue = index; + mIns.Push(lins); + ByteCodeInstruction bins(BC_STORE_ADDR_32); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + bins.mRegisterFinal = ins->mSrc[0].mFinal; + bins.mValue = 0; + mIns.Push(bins); + } + } + else if (ins->mSrc[1].mMemory == IM_FRAME) + { + ByteCodeInstruction bins(BC_STORE_FRAME_32); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + bins.mRegisterFinal = ins->mSrc[0].mFinal; + bins.mValue = int(ins->mSrc[1].mVarIndex + ins->mSrc[1].mIntConst + 2); + mIns.Push(bins); + } + } + } + else + { + if (ins->mSrc[0].mTemp < 0) + { + FloatConstToAccu(ins->mSrc[0].mFloatConst); + + if (ins->mSrc[1].mMemory == IM_INDIRECT) + { + int index = int(ins->mSrc[1].mIntConst); + if (index >= 0 && index + 4 <= 256) + { + ByteCodeInstruction lins(BC_ADDR_REG); + lins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp]; + lins.mRegisterFinal = ins->mSrc[1].mFinal; + mIns.Push(lins); + } + else + { + ByteCodeInstruction lins(BC_LEA_ABS_INDEX); + lins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp]; + lins.mRegisterFinal = ins->mSrc[1].mFinal; + lins.mValue = index; + index = 0; + mIns.Push(lins); + } + + ByteCodeInstruction bins(BC_STORE_ADDR_32); + bins.mRegister = BC_REG_ACCU; + bins.mValue = index; + mIns.Push(bins); + } + } + else + { + if (ins->mSrc[1].mMemory == IM_INDIRECT) + { + int index = int(ins->mSrc[1].mIntConst); + if (index >= 0 && index + 4 <= 256) + { + ByteCodeInstruction lins(BC_ADDR_REG); + lins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp]; + lins.mRegisterFinal = ins->mSrc[1].mFinal; + mIns.Push(lins); + } + else + { + ByteCodeInstruction lins(BC_LEA_ABS_INDEX); + lins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp]; + lins.mRegisterFinal = ins->mSrc[1].mFinal; + lins.mValue = index; + index = 0; + mIns.Push(lins); + } + + ByteCodeInstruction bins(BC_STORE_ADDR_32); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + bins.mRegisterFinal = ins->mSrc[0].mFinal; + bins.mValue = index; + mIns.Push(bins); + } + } + } + } + else if (ins->mSrc[0].mType == IT_POINTER) + { + if (ins->mSrc[1].mTemp < 0) + { + if (ins->mSrc[0].mTemp < 0) + { + IntConstToAccu(ins->mSrc[0].mIntConst); + + if (ins->mSrc[1].mMemory == IM_GLOBAL) + { + ByteCodeInstruction bins(BC_STORE_ABS_16); + bins.mRelocate = true; + bins.mLinkerObject = ins->mSrc[1].mLinkerObject; + bins.mValue = int(ins->mSrc[1].mIntConst); + bins.mRegister = BC_REG_ACCU; + mIns.Push(bins); + } + else if (ins->mSrc[1].mMemory == IM_ABSOLUTE) + { + ByteCodeInstruction bins(BC_STORE_ABS_16); + bins.mValue = int(ins->mSrc[1].mIntConst); + bins.mRegister = BC_REG_ACCU; + mIns.Push(bins); + } + else if (ins->mSrc[1].mMemory == IM_FPARAM || ins->mSrc[1].mMemory == IM_FFRAME) + { + ByteCodeInstruction bins(BC_STORE_REG_16); + bins.mRegister = int(BC_REG_FPARAMS + ins->mSrc[1].mVarIndex + ins->mSrc[1].mIntConst); + mIns.Push(bins); + } + else if (ins->mSrc[1].mMemory == IM_LOCAL || ins->mSrc[1].mMemory == IM_PARAM) + { + int index = int(ins->mSrc[1].mIntConst); + if (ins->mSrc[1].mMemory == IM_LOCAL) + index += proc->mLocalVars[ins->mSrc[1].mVarIndex]->mOffset; + else + index += ins->mSrc[1].mVarIndex + proc->mLocalSize + 2; + + if (index <= 254) + { + ByteCodeInstruction bins(BC_STORE_LOCAL_16); + bins.mRegister = BC_REG_ACCU; + bins.mValue = index; + mIns.Push(bins); + } + else + { + ByteCodeInstruction lins(BC_LEA_LOCAL); + lins.mRegister = BC_REG_ADDR; + lins.mValue = index; + mIns.Push(lins); + ByteCodeInstruction bins(BC_STORE_ADDR_16); + bins.mRegister = BC_REG_ACCU; + bins.mValue = 0; + mIns.Push(bins); + } + } + else if (ins->mSrc[1].mMemory == IM_FRAME) + { + ByteCodeInstruction bins(BC_STORE_FRAME_16); + bins.mRegister = BC_REG_ACCU; + bins.mValue = int(ins->mSrc[1].mVarIndex + ins->mSrc[1].mIntConst + 2); + mIns.Push(bins); + } + } + else + { + if (ins->mSrc[1].mMemory == IM_GLOBAL) + { + ByteCodeInstruction bins(BC_STORE_ABS_16); + bins.mRelocate = true; + bins.mLinkerObject = ins->mSrc[1].mLinkerObject; + bins.mValue = int(ins->mSrc[1].mIntConst); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + mIns.Push(bins); + } + else if (ins->mSrc[1].mMemory == IM_ABSOLUTE) + { + ByteCodeInstruction bins(BC_STORE_ABS_16); + bins.mValue = int(ins->mSrc[1].mIntConst); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + bins.mRegisterFinal = ins->mSrc[0].mFinal; + mIns.Push(bins); + } + else if (ins->mSrc[1].mMemory == IM_FPARAM || ins->mSrc[1].mMemory == IM_FFRAME) + { + ByteCodeInstruction ains(BC_LOAD_REG_16); + ains.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + ains.mRegisterFinal = ins->mSrc[0].mFinal; + mIns.Push(ains); + + ByteCodeInstruction bins(BC_STORE_REG_16); + bins.mRegister = int(BC_REG_FPARAMS + ins->mSrc[1].mVarIndex + ins->mSrc[1].mIntConst); + mIns.Push(bins); + } + else if (ins->mSrc[1].mMemory == IM_LOCAL || ins->mSrc[1].mMemory == IM_PARAM) + { + int index = int(ins->mSrc[1].mIntConst); + if (ins->mSrc[1].mMemory == IM_LOCAL) + index += proc->mLocalVars[ins->mSrc[1].mVarIndex]->mOffset; + else + index += ins->mSrc[1].mVarIndex + proc->mLocalSize + 2; + + if (index <= 254) + { + ByteCodeInstruction bins(BC_STORE_LOCAL_16); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + bins.mRegisterFinal = ins->mSrc[0].mFinal; + bins.mValue = index; + mIns.Push(bins); + } + else + { + ByteCodeInstruction lins(BC_LEA_LOCAL); + lins.mRegister = BC_REG_ADDR; + lins.mValue = index; + mIns.Push(lins); + ByteCodeInstruction bins(BC_STORE_ADDR_16); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + bins.mRegisterFinal = ins->mSrc[0].mFinal; + bins.mValue = 0; + mIns.Push(bins); + } + } + else if (ins->mSrc[1].mMemory == IM_FRAME) + { + ByteCodeInstruction bins(BC_STORE_FRAME_16); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + bins.mRegisterFinal = ins->mSrc[0].mFinal; + bins.mValue = int(ins->mSrc[1].mVarIndex + ins->mSrc[1].mIntConst + 2); + mIns.Push(bins); + } + } + } + else + { + if (ins->mSrc[0].mTemp < 0) + { + IntConstToAccu(ins->mSrc[0].mIntConst); + + if (ins->mSrc[1].mMemory == IM_INDIRECT) + { + int index = int(ins->mSrc[1].mIntConst); + if (index >= 0 && index + 2 <= 256) + { + ByteCodeInstruction lins(BC_ADDR_REG); + lins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp]; + lins.mRegisterFinal = ins->mSrc[1].mFinal; + mIns.Push(lins); + } + else + { + ByteCodeInstruction lins(BC_LEA_ABS_INDEX); + lins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp]; + lins.mRegisterFinal = ins->mSrc[1].mFinal; + lins.mValue = index; + index = 0; + mIns.Push(lins); + } + + ByteCodeInstruction bins(BC_STORE_ADDR_16); + bins.mRegister = BC_REG_ACCU; + bins.mValue = index; + mIns.Push(bins); + } + } + else + { + if (ins->mSrc[1].mMemory == IM_INDIRECT) + { + int index = int(ins->mSrc[1].mIntConst); + if (index >= 0 && index + 2 <= 256) + { + ByteCodeInstruction lins(BC_ADDR_REG); + lins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp]; + lins.mRegisterFinal = ins->mSrc[1].mFinal; + mIns.Push(lins); + } + else + { + ByteCodeInstruction lins(BC_LEA_ABS_INDEX); + lins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp]; + lins.mRegisterFinal = ins->mSrc[1].mFinal; + lins.mValue = index; + index = 0; + mIns.Push(lins); + } + + ByteCodeInstruction bins(BC_STORE_ADDR_16); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + bins.mRegisterFinal = ins->mSrc[0].mFinal; + bins.mValue = index; + mIns.Push(bins); + } + } + } + } + else + { + if (ins->mSrc[1].mTemp < 0) + { + if (ins->mSrc[0].mTemp < 0) + { + if (InterTypeSize[ins->mSrc[0].mType] <= 2) + IntConstToAccu(ins->mSrc[0].mIntConst); + else + LongConstToAccu(ins->mSrc[0].mIntConst); + + if (InterTypeSize[ins->mSrc[0].mType] == 1) + { + if (ins->mSrc[1].mMemory == IM_GLOBAL) + { + ByteCodeInstruction bins(BC_STORE_ABS_8); + bins.mRelocate = true; + bins.mLinkerObject = ins->mSrc[1].mLinkerObject; + bins.mValue = int(ins->mSrc[1].mIntConst); + bins.mRegister = BC_REG_ACCU; + mIns.Push(bins); + } + else if (ins->mSrc[1].mMemory == IM_ABSOLUTE) + { + ByteCodeInstruction bins(BC_STORE_ABS_8); + bins.mValue = int(ins->mSrc[1].mIntConst); + bins.mRegister = BC_REG_ACCU; + mIns.Push(bins); + } + else if (ins->mSrc[1].mMemory == IM_FPARAM || ins->mSrc[1].mMemory == IM_FFRAME) + { + ByteCodeInstruction bins(BC_STORE_REG_8); + bins.mRegister = int(BC_REG_FPARAMS + ins->mSrc[1].mVarIndex + ins->mSrc[1].mIntConst); + mIns.Push(bins); + } + else if (ins->mSrc[1].mMemory == IM_LOCAL || ins->mSrc[1].mMemory == IM_PARAM) + { + int index = int(ins->mSrc[1].mIntConst); + if (ins->mSrc[1].mMemory == IM_LOCAL) + index += proc->mLocalVars[ins->mSrc[1].mVarIndex]->mOffset; + else + index += ins->mSrc[1].mVarIndex + proc->mLocalSize + 2; + + if (index <= 255) + { + ByteCodeInstruction bins(BC_STORE_LOCAL_8); + bins.mRegister = BC_REG_ACCU; + bins.mValue = index; + mIns.Push(bins); + } + else + { + ByteCodeInstruction lins(BC_LEA_LOCAL); + lins.mRegister = BC_REG_ADDR; + lins.mValue = index; + mIns.Push(lins); + ByteCodeInstruction bins(BC_STORE_ADDR_8); + bins.mRegister = BC_REG_ACCU; + bins.mValue = 0; + mIns.Push(bins); + } + } + else if (ins->mSrc[1].mMemory == IM_FRAME) + { + ByteCodeInstruction bins(BC_STORE_FRAME_8); + bins.mRegister = BC_REG_ACCU; + bins.mValue = int(ins->mSrc[1].mVarIndex + ins->mSrc[1].mIntConst + 2); + mIns.Push(bins); + } + } + else if (InterTypeSize[ins->mSrc[0].mType] == 2) + { + if (ins->mSrc[1].mMemory == IM_GLOBAL) + { + ByteCodeInstruction bins(BC_STORE_ABS_16); + bins.mRelocate = true; + bins.mLinkerObject = ins->mSrc[1].mLinkerObject; + bins.mValue = int(ins->mSrc[1].mIntConst); + bins.mRegister = BC_REG_ACCU; + mIns.Push(bins); + } + else if (ins->mSrc[1].mMemory == IM_ABSOLUTE) + { + ByteCodeInstruction bins(BC_STORE_ABS_16); + bins.mValue = int(ins->mSrc[1].mIntConst); + bins.mRegister = BC_REG_ACCU; + mIns.Push(bins); + } + else if (ins->mSrc[1].mMemory == IM_FPARAM || ins->mSrc[1].mMemory == IM_FFRAME) + { + ByteCodeInstruction bins(BC_STORE_REG_16); + bins.mRegister = int(BC_REG_FPARAMS + ins->mSrc[1].mVarIndex + ins->mSrc[1].mIntConst); + mIns.Push(bins); + } + else if (ins->mSrc[1].mMemory == IM_LOCAL || ins->mSrc[1].mMemory == IM_PARAM) + { + int index = int(ins->mSrc[1].mIntConst); + if (ins->mSrc[1].mMemory == IM_LOCAL) + index += proc->mLocalVars[ins->mSrc[1].mVarIndex]->mOffset; + else + index += ins->mSrc[1].mVarIndex + proc->mLocalSize + 2; + + if (index <= 254) + { + ByteCodeInstruction bins(BC_STORE_LOCAL_16); + bins.mRegister = BC_REG_ACCU; + bins.mValue = index; + mIns.Push(bins); + } + else + { + ByteCodeInstruction lins(BC_LEA_LOCAL); + lins.mRegister = BC_REG_ADDR; + lins.mValue = index; + mIns.Push(lins); + ByteCodeInstruction bins(BC_STORE_ADDR_16); + bins.mRegister = BC_REG_ACCU; + bins.mValue = 0; + mIns.Push(bins); + } + } + else if (ins->mSrc[1].mMemory == IM_FRAME) + { + ByteCodeInstruction bins(BC_STORE_FRAME_16); + bins.mRegister = BC_REG_ACCU; + bins.mValue = int(ins->mSrc[1].mVarIndex + ins->mSrc[1].mIntConst + 2); + mIns.Push(bins); + } + } + else if (InterTypeSize[ins->mSrc[0].mType] == 4) + { + if (ins->mSrc[1].mMemory == IM_GLOBAL) + { + ByteCodeInstruction bins(BC_STORE_ABS_32); + bins.mRelocate = true; + bins.mLinkerObject = ins->mSrc[1].mLinkerObject; + bins.mValue = int(ins->mSrc[1].mIntConst); + bins.mRegister = BC_REG_ACCU; + mIns.Push(bins); + } + else if (ins->mSrc[1].mMemory == IM_ABSOLUTE) + { + ByteCodeInstruction bins(BC_STORE_ABS_32); + bins.mValue = int(ins->mSrc[1].mIntConst); + bins.mRegister = BC_REG_ACCU; + mIns.Push(bins); + } + else if (ins->mSrc[1].mMemory == IM_FPARAM || ins->mSrc[1].mMemory == IM_FFRAME) + { + ByteCodeInstruction bins(BC_STORE_REG_32); + bins.mRegister = int(BC_REG_FPARAMS + ins->mSrc[1].mVarIndex + ins->mSrc[1].mIntConst); + mIns.Push(bins); + } + else if (ins->mSrc[1].mMemory == IM_LOCAL || ins->mSrc[1].mMemory == IM_PARAM) + { + int index = int(ins->mSrc[1].mIntConst); + if (ins->mSrc[1].mMemory == IM_LOCAL) + index += proc->mLocalVars[ins->mSrc[1].mVarIndex]->mOffset; + else + index += ins->mSrc[1].mVarIndex + proc->mLocalSize + 2; + + if (index <= 252) + { + ByteCodeInstruction bins(BC_STORE_LOCAL_32); + bins.mRegister = BC_REG_ACCU; + bins.mValue = index; + mIns.Push(bins); + } + else + { + ByteCodeInstruction lins(BC_LEA_LOCAL); + lins.mRegister = BC_REG_ADDR; + lins.mValue = index; + mIns.Push(lins); + ByteCodeInstruction bins(BC_STORE_ADDR_32); + bins.mRegister = BC_REG_ACCU; + bins.mValue = 0; + mIns.Push(bins); + } + } + else if (ins->mSrc[1].mMemory == IM_FRAME) + { + ByteCodeInstruction bins(BC_STORE_FRAME_32); + bins.mRegister = BC_REG_ACCU; + bins.mValue = int(ins->mSrc[1].mVarIndex + ins->mSrc[1].mIntConst + 2); + mIns.Push(bins); + } + } + } + else + { + if (InterTypeSize[ins->mSrc[0].mType] == 1) + { + if (ins->mSrc[1].mMemory == IM_GLOBAL) + { + ByteCodeInstruction bins(BC_STORE_ABS_8); + bins.mRelocate = true; + bins.mLinkerObject = ins->mSrc[1].mLinkerObject; + bins.mValue = int(ins->mSrc[1].mIntConst); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + bins.mRegisterFinal = ins->mSrc[0].mFinal; + mIns.Push(bins); + } + else if (ins->mSrc[1].mMemory == IM_ABSOLUTE) + { + ByteCodeInstruction bins(BC_STORE_ABS_8); + bins.mValue = int(ins->mSrc[1].mIntConst); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + bins.mRegisterFinal = ins->mSrc[0].mFinal; + mIns.Push(bins); + } + else if (ins->mSrc[1].mMemory == IM_FPARAM || ins->mSrc[1].mMemory == IM_FFRAME) + { + ByteCodeInstruction ains(BC_LOAD_REG_8); + ains.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + ains.mRegisterFinal = ins->mSrc[0].mFinal; + mIns.Push(ains); + + ByteCodeInstruction bins(BC_STORE_REG_8); + bins.mRegister = int(BC_REG_FPARAMS + ins->mSrc[1].mVarIndex + ins->mSrc[1].mIntConst); + mIns.Push(bins); + } + else if (ins->mSrc[1].mMemory == IM_LOCAL || ins->mSrc[1].mMemory == IM_PARAM) + { + int index = int(ins->mSrc[1].mIntConst); + if (ins->mSrc[1].mMemory == IM_LOCAL) + index += proc->mLocalVars[ins->mSrc[1].mVarIndex]->mOffset; + else + index += ins->mSrc[1].mVarIndex + proc->mLocalSize + 2; + + if (index <= 255) + { + ByteCodeInstruction bins(BC_STORE_LOCAL_8); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + bins.mRegisterFinal = ins->mSrc[0].mFinal; + bins.mValue = index; + mIns.Push(bins); + } + else + { + ByteCodeInstruction lins(BC_LEA_LOCAL); + lins.mRegister = BC_REG_ADDR; + lins.mValue = index; + mIns.Push(lins); + ByteCodeInstruction bins(BC_STORE_ADDR_8); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + bins.mRegisterFinal = ins->mSrc[0].mFinal; + bins.mValue = 0; + mIns.Push(bins); + } + } + else if (ins->mSrc[1].mMemory == IM_FRAME) + { + ByteCodeInstruction bins(BC_STORE_FRAME_8); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + bins.mRegisterFinal = ins->mSrc[0].mFinal; + bins.mValue = int(ins->mSrc[1].mVarIndex + ins->mSrc[1].mIntConst + 2); + mIns.Push(bins); + } + } + else if (InterTypeSize[ins->mSrc[0].mType] == 2) + { + if (ins->mSrc[1].mMemory == IM_GLOBAL) + { + ByteCodeInstruction bins(BC_STORE_ABS_16); + bins.mRelocate = true; + bins.mLinkerObject = ins->mSrc[1].mLinkerObject; + bins.mValue = int(ins->mSrc[1].mIntConst); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + bins.mRegisterFinal = ins->mSrc[0].mFinal; + mIns.Push(bins); + } + else if (ins->mSrc[1].mMemory == IM_ABSOLUTE) + { + ByteCodeInstruction bins(BC_STORE_ABS_16); + bins.mValue = int(ins->mSrc[1].mIntConst); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + bins.mRegisterFinal = ins->mSrc[0].mFinal; + mIns.Push(bins); + } + else if (ins->mSrc[1].mMemory == IM_FPARAM || ins->mSrc[1].mMemory == IM_FFRAME) + { + ByteCodeInstruction ains(BC_LOAD_REG_16); + ains.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + ains.mRegisterFinal = ins->mSrc[0].mFinal; + mIns.Push(ains); + + ByteCodeInstruction bins(BC_STORE_REG_16); + bins.mRegister = int(BC_REG_FPARAMS + ins->mSrc[1].mVarIndex + ins->mSrc[1].mIntConst); + mIns.Push(bins); + } + else if (ins->mSrc[1].mMemory == IM_LOCAL || ins->mSrc[1].mMemory == IM_PARAM) + { + int index = int(ins->mSrc[1].mIntConst); + if (ins->mSrc[1].mMemory == IM_LOCAL) + index += proc->mLocalVars[ins->mSrc[1].mVarIndex]->mOffset; + else + index += ins->mSrc[1].mVarIndex + proc->mLocalSize + 2; + + if (index <= 254) + { + ByteCodeInstruction bins(BC_STORE_LOCAL_16); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + bins.mRegisterFinal = ins->mSrc[0].mFinal; + bins.mValue = index; + mIns.Push(bins); + } + else + { + ByteCodeInstruction lins(BC_LEA_LOCAL); + lins.mRegister = BC_REG_ADDR; + lins.mValue = index; + mIns.Push(lins); + ByteCodeInstruction bins(BC_STORE_ADDR_16); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + bins.mRegisterFinal = ins->mSrc[0].mFinal; + bins.mValue = 0; + mIns.Push(bins); + } + } + else if (ins->mSrc[1].mMemory == IM_FRAME) + { + ByteCodeInstruction bins(BC_STORE_FRAME_16); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + bins.mRegisterFinal = ins->mSrc[0].mFinal; + bins.mValue = int(ins->mSrc[1].mVarIndex + ins->mSrc[1].mIntConst + 2); + mIns.Push(bins); + } + } + else if (InterTypeSize[ins->mSrc[0].mType] == 4) + { + if (ins->mSrc[1].mMemory == IM_GLOBAL) + { + ByteCodeInstruction bins(BC_STORE_ABS_32); + bins.mRelocate = true; + bins.mLinkerObject = ins->mSrc[1].mLinkerObject; + bins.mValue = int(ins->mSrc[1].mIntConst); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + bins.mRegisterFinal = ins->mSrc[0].mFinal; + mIns.Push(bins); + } + else if (ins->mSrc[1].mMemory == IM_ABSOLUTE) + { + ByteCodeInstruction bins(BC_STORE_ABS_32); + bins.mValue = int(ins->mSrc[1].mIntConst); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + bins.mRegisterFinal = ins->mSrc[0].mFinal; + mIns.Push(bins); + } + else if (ins->mSrc[1].mMemory == IM_FPARAM || ins->mSrc[1].mMemory == IM_FFRAME) + { + ByteCodeInstruction ains(BC_LOAD_REG_32); + ains.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + ains.mRegisterFinal = ins->mSrc[0].mFinal; + mIns.Push(ains); + + ByteCodeInstruction bins(BC_STORE_REG_32); + bins.mRegister = int(BC_REG_FPARAMS + ins->mSrc[1].mVarIndex + ins->mSrc[1].mIntConst); + mIns.Push(bins); + } + else if (ins->mSrc[1].mMemory == IM_LOCAL || ins->mSrc[1].mMemory == IM_PARAM) + { + int index = int(ins->mSrc[1].mIntConst); + if (ins->mSrc[1].mMemory == IM_LOCAL) + index += proc->mLocalVars[ins->mSrc[1].mVarIndex]->mOffset; + else + index += ins->mSrc[1].mVarIndex + proc->mLocalSize + 2; + + if (index <= 252) + { + ByteCodeInstruction bins(BC_STORE_LOCAL_32); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + bins.mRegisterFinal = ins->mSrc[0].mFinal; + bins.mValue = index; + mIns.Push(bins); + } + else + { + ByteCodeInstruction lins(BC_LEA_LOCAL); + lins.mRegister = BC_REG_ADDR; + lins.mValue = index; + mIns.Push(lins); + ByteCodeInstruction bins(BC_STORE_ADDR_32); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + bins.mRegisterFinal = ins->mSrc[0].mFinal; + bins.mValue = 0; + mIns.Push(bins); + } + } + else if (ins->mSrc[1].mMemory == IM_FRAME) + { + ByteCodeInstruction bins(BC_STORE_FRAME_32); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + bins.mRegisterFinal = ins->mSrc[0].mFinal; + bins.mValue = int(ins->mSrc[1].mVarIndex + ins->mSrc[1].mIntConst + 2); + mIns.Push(bins); + } + } + } + } + else + { + if (ins->mSrc[0].mTemp < 0) + { + if (InterTypeSize[ins->mSrc[0].mType] <= 2) + IntConstToAccu(ins->mSrc[0].mIntConst); + else + LongConstToAccu(ins->mSrc[0].mIntConst); + + if (ins->mSrc[1].mMemory == IM_INDIRECT) + { + int index = int(ins->mSrc[1].mIntConst); + if (index >= 0 && index + InterTypeSize[ins->mSrc[0].mType] <= 256) + { + ByteCodeInstruction lins(BC_ADDR_REG); + lins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp]; + lins.mRegisterFinal = ins->mSrc[1].mFinal; + mIns.Push(lins); + } + else + { + ByteCodeInstruction lins(BC_LEA_ABS_INDEX); + lins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp]; + lins.mRegisterFinal = ins->mSrc[1].mFinal; + lins.mValue = index; + index = 0; + mIns.Push(lins); + } + + if (InterTypeSize[ins->mSrc[0].mType] == 1) + { + ByteCodeInstruction bins(BC_STORE_ADDR_8); + bins.mRegister = BC_REG_ACCU; + bins.mValue = index; + mIns.Push(bins); + } + else if (InterTypeSize[ins->mSrc[0].mType] == 2) + { + ByteCodeInstruction bins(BC_STORE_ADDR_16); + bins.mRegister = BC_REG_ACCU; + bins.mValue = index; + mIns.Push(bins); + } + else if (InterTypeSize[ins->mSrc[0].mType] == 4) + { + ByteCodeInstruction bins(BC_STORE_ADDR_32); + bins.mRegister = BC_REG_ACCU; + bins.mValue = index; + mIns.Push(bins); + } + } + } + else + { + if (ins->mSrc[1].mMemory == IM_INDIRECT) + { + int index = int(ins->mSrc[1].mIntConst); + if (index >= 0 && index + InterTypeSize[ins->mSrc[0].mType] <= 256) + { + ByteCodeInstruction lins(BC_ADDR_REG); + lins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp]; + lins.mRegisterFinal = ins->mSrc[1].mFinal; + mIns.Push(lins); + } + else + { + ByteCodeInstruction lins(BC_LEA_ABS_INDEX); + lins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp]; + lins.mRegisterFinal = ins->mSrc[1].mFinal; + lins.mValue = index; + index = 0; + mIns.Push(lins); + } + + if (InterTypeSize[ins->mSrc[0].mType] == 1) + { + ByteCodeInstruction bins(BC_STORE_ADDR_8); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + bins.mRegisterFinal = ins->mSrc[0].mFinal; + bins.mValue = index; + mIns.Push(bins); + } + else if (InterTypeSize[ins->mSrc[0].mType] == 2) + { + ByteCodeInstruction bins(BC_STORE_ADDR_16); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + bins.mRegisterFinal = ins->mSrc[0].mFinal; + bins.mValue = index; + mIns.Push(bins); + } + else if (InterTypeSize[ins->mSrc[0].mType] == 4) + { + ByteCodeInstruction bins(BC_STORE_ADDR_32); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + bins.mRegisterFinal = ins->mSrc[0].mFinal; + bins.mValue = index; + mIns.Push(bins); + } + } + } + } + } +} + +void ByteCodeBasicBlock::LoadDirectValue(InterCodeProcedure* proc, const InterInstruction * ins) +{ + if (ins->mDst.mType == IT_FLOAT) + { + if (ins->mSrc[0].mTemp < 0) + { + if (ins->mSrc[0].mMemory == IM_GLOBAL) + { + ByteCodeInstruction bins(BC_LOAD_ABS_32); + bins.mRelocate = true; + bins.mLinkerObject = ins->mSrc[0].mLinkerObject; + bins.mValue = int(ins->mSrc[0].mIntConst); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + mIns.Push(bins); + } + else if (ins->mSrc[0].mMemory == IM_ABSOLUTE) + { + ByteCodeInstruction bins(BC_LOAD_ABS_32); + bins.mValue = int(ins->mSrc[0].mIntConst); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + mIns.Push(bins); + } + else if (ins->mSrc[0].mMemory == IM_FPARAM || ins->mSrc[0].mMemory == IM_FFRAME) + { + ByteCodeInstruction ains(BC_LOAD_REG_32); + ains.mRegister = int(BC_REG_FPARAMS + ins->mSrc[0].mIntConst + ins->mSrc[0].mVarIndex); + mIns.Push(ains); + + ByteCodeInstruction bins(BC_STORE_REG_32); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + mIns.Push(bins); + } + else if (ins->mSrc[0].mMemory == IM_LOCAL || ins->mSrc[0].mMemory == IM_PARAM) + { + int index = int(ins->mSrc[0].mIntConst); + if (ins->mSrc[0].mMemory == IM_LOCAL) + index += proc->mLocalVars[ins->mSrc[0].mVarIndex]->mOffset; + else + index += ins->mSrc[0].mVarIndex + proc->mLocalSize + 2; + + if (index <= 254) + { + ByteCodeInstruction bins(BC_LOAD_LOCAL_32); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + bins.mValue = index; + mIns.Push(bins); + } + else + { + ByteCodeInstruction lins(BC_LEA_LOCAL); + lins.mRegister = BC_REG_ADDR; + lins.mValue = index; + mIns.Push(lins); + ByteCodeInstruction bins(BC_LOAD_ADDR_32); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + bins.mValue = 0; + mIns.Push(bins); + } + } + } + else + { + if (ins->mSrc[0].mMemory == IM_INDIRECT) + { + int index = int(ins->mSrc[0].mIntConst); + if (index >= 0 && index + 4 <= 256) + { + ByteCodeInstruction lins(BC_ADDR_REG); + lins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + lins.mRegisterFinal = ins->mSrc[0].mFinal; + mIns.Push(lins); + } + else + { + ByteCodeInstruction lins(BC_LEA_ABS_INDEX); + lins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + lins.mRegisterFinal = ins->mSrc[0].mFinal; + lins.mValue = index; + index = 0; + mIns.Push(lins); + } + + ByteCodeInstruction bins(BC_LOAD_ADDR_32); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + bins.mValue = index; + mIns.Push(bins); + } + } + } + else if (ins->mDst.mType == IT_POINTER) + { + if (ins->mSrc[0].mTemp < 0) + { + if (ins->mSrc[0].mMemory == IM_GLOBAL) + { + ByteCodeInstruction bins(BC_LOAD_ABS_16); + bins.mRelocate = true; + bins.mLinkerObject = ins->mSrc[0].mLinkerObject; + bins.mValue = int(ins->mSrc[0].mIntConst); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + mIns.Push(bins); + } + else if (ins->mSrc[0].mMemory == IM_ABSOLUTE) + { + ByteCodeInstruction bins(BC_LOAD_ABS_16); + bins.mValue = int(ins->mSrc[0].mIntConst); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + mIns.Push(bins); + } + else if (ins->mSrc[0].mMemory == IM_FPARAM || ins->mSrc[0].mMemory == IM_FFRAME) + { + ByteCodeInstruction ains(BC_LOAD_REG_16); + ains.mRegister = int(BC_REG_FPARAMS + ins->mSrc[0].mIntConst + ins->mSrc[0].mVarIndex); + mIns.Push(ains); + + ByteCodeInstruction bins(BC_STORE_REG_16); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + mIns.Push(bins); + } + else if (ins->mSrc[0].mMemory == IM_LOCAL || ins->mSrc[0].mMemory == IM_PARAM) + { + int index = int(ins->mSrc[0].mIntConst); + if (ins->mSrc[0].mMemory == IM_LOCAL) + index += proc->mLocalVars[ins->mSrc[0].mVarIndex]->mOffset; + else + index += ins->mSrc[0].mVarIndex + proc->mLocalSize + 2; + + if (index <= 254) + { + ByteCodeInstruction bins(BC_LOAD_LOCAL_16); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + bins.mValue = index; + mIns.Push(bins); + } + else + { + ByteCodeInstruction lins(BC_LEA_LOCAL); + lins.mRegister = BC_REG_ADDR; + lins.mValue = index; + mIns.Push(lins); + ByteCodeInstruction bins(BC_LOAD_ADDR_16); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + bins.mValue = 0; + mIns.Push(bins); + } + } + } + else + { + if (ins->mSrc[0].mMemory == IM_INDIRECT) + { + int index = int(ins->mSrc[0].mIntConst); + if (index >= 0 && index + 2 <= 256) + { + ByteCodeInstruction lins(BC_ADDR_REG); + lins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + lins.mRegisterFinal = ins->mSrc[0].mFinal; + mIns.Push(lins); + } + else + { + ByteCodeInstruction lins(BC_LEA_ABS_INDEX); + lins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + lins.mRegisterFinal = ins->mSrc[0].mFinal; + lins.mValue = index; + index = 0; + mIns.Push(lins); + } + + ByteCodeInstruction bins(BC_LOAD_ADDR_16); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + bins.mValue = index; + mIns.Push(bins); + } + } + } + else + { + if (ins->mSrc[0].mTemp < 0) + { + if (InterTypeSize[ins->mDst.mType] == 1) + { + if (ins->mSrc[0].mMemory == IM_GLOBAL) + { + ByteCodeInstruction bins(BC_LOAD_ABS_8); + bins.mRelocate = true; + bins.mLinkerObject = ins->mSrc[0].mLinkerObject; + bins.mValue = int(ins->mSrc[0].mIntConst); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + mIns.Push(bins); + } + else if (ins->mSrc[0].mMemory == IM_ABSOLUTE) + { + ByteCodeInstruction bins(BC_LOAD_ABS_8); + bins.mValue = int(ins->mSrc[0].mIntConst); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + mIns.Push(bins); + } + else if (ins->mSrc[0].mMemory == IM_FPARAM || ins->mSrc[0].mMemory == IM_FFRAME) + { + ByteCodeInstruction ains(BC_LOAD_REG_8); + ains.mRegister = int(BC_REG_FPARAMS + ins->mSrc[0].mIntConst + ins->mSrc[0].mVarIndex); + mIns.Push(ains); + + ByteCodeInstruction bins(BC_STORE_REG_8); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + mIns.Push(bins); + } + else if (ins->mSrc[0].mMemory == IM_LOCAL || ins->mSrc[0].mMemory == IM_PARAM) + { + int index = int(ins->mSrc[0].mIntConst); + if (ins->mSrc[0].mMemory == IM_LOCAL) + index += proc->mLocalVars[ins->mSrc[0].mVarIndex]->mOffset; + else + index += ins->mSrc[0].mVarIndex + proc->mLocalSize + 2; + + if (index <= 255) + { + ByteCodeInstruction bins(BC_LOAD_LOCAL_8); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + bins.mValue = index; + mIns.Push(bins); + } + else + { + ByteCodeInstruction lins(BC_LEA_LOCAL); + lins.mRegister = BC_REG_ADDR; + lins.mValue = index; + mIns.Push(lins); + ByteCodeInstruction bins(BC_LOAD_ADDR_8); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + bins.mValue = 0; + mIns.Push(bins); + } + } + } + else if (InterTypeSize[ins->mDst.mType] == 2) + { + if (ins->mSrc[0].mMemory == IM_GLOBAL) + { + ByteCodeInstruction bins(BC_LOAD_ABS_16); + bins.mRelocate = true; + bins.mLinkerObject = ins->mSrc[0].mLinkerObject; + bins.mValue = int(ins->mSrc[0].mIntConst); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + mIns.Push(bins); + } + else if (ins->mSrc[0].mMemory == IM_ABSOLUTE) + { + ByteCodeInstruction bins(BC_LOAD_ABS_16); + bins.mValue = int(ins->mSrc[0].mIntConst); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + mIns.Push(bins); + } + else if (ins->mSrc[0].mMemory == IM_FPARAM || ins->mSrc[0].mMemory == IM_FFRAME) + { + ByteCodeInstruction ains(BC_LOAD_REG_16); + ains.mRegister = int(BC_REG_FPARAMS + ins->mSrc[0].mIntConst + ins->mSrc[0].mVarIndex); + mIns.Push(ains); + + ByteCodeInstruction bins(BC_STORE_REG_16); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + mIns.Push(bins); + } + else if (ins->mSrc[0].mMemory == IM_LOCAL || ins->mSrc[0].mMemory == IM_PARAM) + { + int index = int(ins->mSrc[0].mIntConst); + if (ins->mSrc[0].mMemory == IM_LOCAL) + index += proc->mLocalVars[ins->mSrc[0].mVarIndex]->mOffset; + else + index += ins->mSrc[0].mVarIndex + proc->mLocalSize + 2; + + if (index <= 254) + { + ByteCodeInstruction bins(BC_LOAD_LOCAL_16); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + bins.mValue = index; + mIns.Push(bins); + } + else + { + ByteCodeInstruction lins(BC_LEA_LOCAL); + lins.mRegister = BC_REG_ADDR; + lins.mValue = index; + mIns.Push(lins); + ByteCodeInstruction bins(BC_LOAD_ADDR_16); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + bins.mValue = 0; + mIns.Push(bins); + } + } + } + else if (InterTypeSize[ins->mDst.mType] == 4) + { + if (ins->mSrc[0].mMemory == IM_GLOBAL) + { + ByteCodeInstruction bins(BC_LOAD_ABS_32); + bins.mRelocate = true; + bins.mLinkerObject = ins->mSrc[0].mLinkerObject; + bins.mValue = int(ins->mSrc[0].mIntConst); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + mIns.Push(bins); + } + else if (ins->mSrc[0].mMemory == IM_ABSOLUTE) + { + ByteCodeInstruction bins(BC_LOAD_ABS_32); + bins.mValue = int(ins->mSrc[0].mIntConst); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + mIns.Push(bins); + } + else if (ins->mSrc[0].mMemory == IM_FPARAM || ins->mSrc[0].mMemory == IM_FFRAME) + { + ByteCodeInstruction ains(BC_LOAD_REG_32); + ains.mRegister = int(BC_REG_FPARAMS + ins->mSrc[0].mIntConst + ins->mSrc[0].mVarIndex); + mIns.Push(ains); + + ByteCodeInstruction bins(BC_STORE_REG_32); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + mIns.Push(bins); + } + else if (ins->mSrc[0].mMemory == IM_LOCAL || ins->mSrc[0].mMemory == IM_PARAM) + { + int index = int(ins->mSrc[0].mIntConst); + if (ins->mSrc[0].mMemory == IM_LOCAL) + index += proc->mLocalVars[ins->mSrc[0].mVarIndex]->mOffset; + else + index += ins->mSrc[0].mVarIndex + proc->mLocalSize + 2; + + if (index <= 252) + { + ByteCodeInstruction bins(BC_LOAD_LOCAL_32); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + bins.mValue = index; + mIns.Push(bins); + } + else + { + ByteCodeInstruction lins(BC_LEA_LOCAL); + lins.mRegister = BC_REG_ADDR; + lins.mValue = index; + mIns.Push(lins); + ByteCodeInstruction bins(BC_LOAD_ADDR_32); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + bins.mValue = 0; + mIns.Push(bins); + } + } + } + } + else + { + if (ins->mSrc[0].mMemory == IM_INDIRECT) + { + int index = int(ins->mSrc[0].mIntConst); + if (index >= 0 && index + InterTypeSize[ins->mDst.mType] <= 256) + { + ByteCodeInstruction lins(BC_ADDR_REG); + lins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + lins.mRegisterFinal = ins->mSrc[0].mFinal; + mIns.Push(lins); + } + else + { + ByteCodeInstruction lins(BC_LEA_ABS_INDEX); + lins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + lins.mRegisterFinal = ins->mSrc[0].mFinal; + lins.mValue = index; + index = 0; + mIns.Push(lins); + } + + if (InterTypeSize[ins->mDst.mType] == 1) + { + ByteCodeInstruction bins(BC_LOAD_ADDR_8); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + bins.mValue = index; + mIns.Push(bins); + } + else if (InterTypeSize[ins->mDst.mType] == 2) + { + ByteCodeInstruction bins(BC_LOAD_ADDR_16); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + bins.mValue = index; + mIns.Push(bins); + } + else if (InterTypeSize[ins->mDst.mType] == 4) + { + ByteCodeInstruction bins(BC_LOAD_ADDR_32); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + bins.mValue = index; + mIns.Push(bins); + } + } + } + } +} + +void ByteCodeBasicBlock::LoadStoreIndirectValue(InterCodeProcedure* proc, const InterInstruction* rins, const InterInstruction* wins) +{ + int ri = int(rins->mSrc[0].mIntConst), wi = int(wins->mSrc[1].mIntConst); + + if (ri < 252 && wi < 252 && rins->mSrc[0].mTemp == wins->mSrc[1].mTemp && rins->mSrc[0].mMemory == IM_INDIRECT && wins->mSrc[1].mMemory == IM_INDIRECT) + { + ByteCodeInstruction lins(BC_ADDR_REG); + lins.mRegister = BC_REG_TMP + proc->mTempOffset[wins->mSrc[1].mTemp]; + lins.mRegisterFinal = wins->mSrc[1].mFinal; + mIns.Push(lins); + + if (InterTypeSize[rins->mDst.mType] == 1) + { + ByteCodeInstruction bins(BC_LOAD_ADDR_8); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[rins->mDst.mTemp]; + bins.mValue = ri; + mIns.Push(bins); + } + else if (InterTypeSize[rins->mDst.mType] == 2) + { + ByteCodeInstruction bins(BC_LOAD_ADDR_16); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[rins->mDst.mTemp]; + bins.mValue = ri; + mIns.Push(bins); + } + else if (InterTypeSize[rins->mDst.mType] == 4) + { + ByteCodeInstruction bins(BC_LOAD_ADDR_32); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[rins->mDst.mTemp]; + bins.mValue = ri; + mIns.Push(bins); + } + + if (InterTypeSize[wins->mSrc[0].mType] == 1) + { + ByteCodeInstruction bins(BC_STORE_ADDR_8); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[wins->mSrc[0].mTemp]; + bins.mValue = wi; + mIns.Push(bins); + } + else if (InterTypeSize[wins->mSrc[0].mType] == 2) + { + ByteCodeInstruction bins(BC_STORE_ADDR_16); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[wins->mSrc[0].mTemp]; + bins.mValue = wi; + mIns.Push(bins); + } + else if (InterTypeSize[wins->mSrc[0].mType] == 4) + { + ByteCodeInstruction bins(BC_STORE_ADDR_32); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[wins->mSrc[0].mTemp]; + bins.mValue = wi; + mIns.Push(bins); + } + } + else + { + LoadDirectValue(proc, rins); + StoreDirectValue(proc, wins); + } +} + +void ByteCodeBasicBlock::LoadEffectiveAddress(InterCodeProcedure* proc, const InterInstruction * ins) +{ + if (ins->mSrc[0].mTemp < 0) + { + if (ins->mSrc[1].mTemp == ins->mDst.mTemp) + { + ByteCodeInstruction bins(BC_BINOP_ADDI_16); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + bins.mValue = int(ins->mSrc[0].mIntConst); + mIns.Push(bins); + } + else + { + ByteCodeInstruction lins(BC_LOAD_REG_16); + lins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp]; + lins.mRegisterFinal = ins->mSrc[1].mFinal; + mIns.Push(lins); + ByteCodeInstruction ains(BC_BINOP_ADDI_16); + ains.mRegister = BC_REG_ACCU; + ains.mValue = int(ins->mSrc[0].mIntConst); + mIns.Push(ains); + ByteCodeInstruction sins(BC_STORE_REG_16); + sins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + mIns.Push(sins); + } + } + else if (ins->mSrc[1].mTemp < 0) + { + if (ins->mSrc[1].mMemory == IM_GLOBAL) + { + ByteCodeInstruction bins(BC_LEA_ABS); + bins.mRegister = BC_REG_ACCU; + bins.mLinkerObject = ins->mSrc[1].mLinkerObject; + bins.mValue = int(ins->mSrc[1].mIntConst); + bins.mRelocate = true; + mIns.Push(bins); + } + else if (ins->mSrc[1].mMemory == IM_ABSOLUTE) + { + ByteCodeInstruction bins(BC_LEA_ABS); + bins.mRegister = BC_REG_ACCU; + bins.mValue = int(ins->mSrc[1].mIntConst); + mIns.Push(bins); + } + else if (ins->mSrc[1].mMemory == IM_FPARAM || ins->mSrc[1].mMemory == IM_FFRAME) + { + ByteCodeInstruction bins(BC_CONST_16); + bins.mRegister = BC_REG_ACCU; + bins.mValue = int(BC_REG_FPARAMS + ins->mSrc[1].mIntConst + ins->mSrc[1].mVarIndex); + mIns.Push(bins); + } + else if (ins->mSrc[1].mMemory == IM_PROCEDURE) + { + ByteCodeInstruction bins(BC_CONST_16); + bins.mRegister = BC_REG_ACCU; + bins.mLinkerObject = ins->mSrc[1].mLinkerObject; + bins.mValue = 0; + bins.mRelocate = true; + mIns.Push(bins); + } + else if (ins->mSrc[1].mMemory == IM_LOCAL || ins->mSrc[1].mMemory == IM_PARAM) + { + ByteCodeInstruction bins(BC_LEA_LOCAL); + bins.mRegister = BC_REG_ACCU; + bins.mValue = int(ins->mSrc[1].mIntConst + proc->mLocalVars[ins->mSrc[1].mVarIndex]->mOffset); + mIns.Push(bins); + } + + ByteCodeInstruction ains(BC_BINOP_ADDR_16); + ains.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + ains.mRegisterFinal = ins->mSrc[0].mFinal; + mIns.Push(ains); + ByteCodeInstruction sins(BC_STORE_REG_16); + sins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + mIns.Push(sins); + } + else + { + ByteCodeInstruction lins(BC_LOAD_REG_16); + lins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp]; + lins.mRegisterFinal = ins->mSrc[1].mFinal; + mIns.Push(lins); + ByteCodeInstruction ains(BC_BINOP_ADDR_16); + ains.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + ains.mRegisterFinal = ins->mSrc[0].mFinal; + mIns.Push(ains); + ByteCodeInstruction sins(BC_STORE_REG_16); + sins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + mIns.Push(sins); + } +} +void ByteCodeBasicBlock::CallFunction(InterCodeProcedure* proc, const InterInstruction * ins) +{ + if (ins->mSrc[0].mTemp < 0) + { + ByteCodeInstruction bins(BC_CALL_ABS); + bins.mRelocate = true; + bins.mLinkerObject = ins->mSrc[0].mLinkerObject; + bins.mValue = 0; + mIns.Push(bins); + } + else + { + ByteCodeInstruction bins(BC_ADDR_REG); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + bins.mRegisterFinal = ins->mSrc[0].mFinal; + mIns.Push(bins); + ByteCodeInstruction cins(BC_CALL_ADDR); + mIns.Push(cins); + } + + if (ins->mDst.mTemp >= 0) + { + ByteCodeInstruction bins(StoreTypedTmpCodes[ins->mDst.mType]); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + mIns.Push(bins); + } +} + +void ByteCodeBasicBlock::CallAssembler(InterCodeProcedure* proc, const InterInstruction * ins) +{ + if (ins->mSrc[0].mTemp < 0) + { + ByteCodeInstruction bins(BC_JSR); + bins.mRelocate = true; + bins.mLinkerObject = ins->mSrc[0].mLinkerObject; + bins.mValue = int(ins->mSrc[0].mIntConst); + for (int i = 1; i < ins->mNumOperands; i++) + { + if (ins->mSrc[i].mTemp < 0) + { + if (ins->mSrc[i].mMemory == IM_FPARAM || ins->mSrc[i].mMemory == IM_FFRAME) + ins->mSrc[0].mLinkerObject->mTemporaries[i - 1] = int(BC_REG_FPARAMS + ins->mSrc[i].mVarIndex + ins->mSrc[i].mIntConst); + } + else + ins->mSrc[0].mLinkerObject->mTemporaries[i - 1] = BC_REG_TMP + proc->mTempOffset[ins->mSrc[i].mTemp]; + } + mIns.Push(bins); + } + + if (ins->mDst.mTemp >= 0) + { + ByteCodeInstruction bins(StoreTypedTmpCodes[ins->mDst.mType]); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + mIns.Push(bins); + } +} + +void ByteCodeBasicBlock::CallNative(InterCodeProcedure* proc, const InterInstruction* ins) +{ + if (ins->mSrc[0].mTemp < 0) + { + ByteCodeInstruction bins(BC_JSR); + bins.mRelocate = true; + bins.mLinkerObject = ins->mSrc[0].mLinkerObject; + bins.mValue = int(ins->mSrc[0].mIntConst); + mIns.Push(bins); + } + + if (ins->mDst.mTemp >= 0) + { + ByteCodeInstruction bins(StoreTypedTmpCodes[ins->mDst.mType]); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + mIns.Push(bins); + } +} + +ByteCode ByteCodeBasicBlock::RelationalOperator(InterCodeProcedure* proc, const InterInstruction * ins, bool optzero) +{ + ByteCode code; + bool csigned = false; + + switch (ins->mOperator) + { + default: + case IA_CMPEQ: + code = BC_BRANCHS_EQ; + break; + case IA_CMPNE: + code = BC_BRANCHS_NE; + break; + + case IA_CMPGES: + csigned = true; + code = BC_BRANCHS_GE; + break; + case IA_CMPGEU: + code = BC_BRANCHS_GE; + break; + case IA_CMPGS: + csigned = true; + code = BC_BRANCHS_GT; + break; + case IA_CMPGU: + code = BC_BRANCHS_GT; + break; + case IA_CMPLES: + csigned = true; + code = BC_BRANCHS_LE; + break; + case IA_CMPLEU: + code = BC_BRANCHS_LE; + break; + case IA_CMPLS: + csigned = true; + code = BC_BRANCHS_LT; + break; + case IA_CMPLU: + code = BC_BRANCHS_LT; + break; + } + + if (ins->mSrc[0].mType == IT_FLOAT) + { + if (ins->mSrc[0].mTemp < 0) + { + FloatConstToAccu(ins->mSrc[0].mFloatConst); + } + else + { + ByteCodeInstruction lins(BC_LOAD_REG_32); + lins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + lins.mRegisterFinal = ins->mSrc[0].mFinal; + mIns.Push(lins); + } + + ByteCodeInstruction cins(BC_BINOP_CMP_F32); + + if (ins->mSrc[1].mTemp < 0) + { + FloatConstToWork(ins->mSrc[1].mFloatConst); + cins.mRegister = BC_REG_WORK; + } + else + { + cins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp]; + } + + cins.mRegisterFinal = ins->mSrc[1].mFinal; + mIns.Push(cins); + } + else if (ins->mSrc[0].mType == IT_INT32) + { + if (ins->mSrc[0].mTemp < 0) + { + LongConstToAccu(ins->mSrc[0].mIntConst); + } + else + { + ByteCodeInstruction lins(BC_LOAD_REG_32); + lins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + lins.mRegisterFinal = ins->mSrc[0].mFinal; + mIns.Push(lins); + } + + + ByteCodeInstruction cins(BC_BINOP_CMP_U32); + if (csigned) + cins.mCode = BC_BINOP_CMP_S32; + + if (ins->mSrc[1].mTemp < 0) + { + LongConstToWork(ins->mSrc[1].mIntConst); + cins.mRegister = BC_REG_WORK; + } + else + { + cins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp]; + } + + cins.mRegisterFinal = ins->mSrc[1].mFinal; + mIns.Push(cins); + } + else if (ins->mSrc[0].mType == IT_INT8 || ins->mSrc[0].mType == IT_BOOL) + { + if (ins->mSrc[1].mTemp < 0) + { + ByteCodeInstruction lins(BC_LOAD_REG_8); + lins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + lins.mRegisterFinal = ins->mSrc[0].mFinal; + mIns.Push(lins); + if (csigned) + { + ByteCodeInstruction cins(BC_BINOP_CMPSI_8); + cins.mValue = int(ins->mSrc[1].mIntConst); + mIns.Push(cins); + } + else + { + if (optzero && ins->mSrc[1].mIntConst == 0) + { + switch (ins->mOperator) + { + case IA_CMPEQ: + case IA_CMPGEU: + return BC_BRANCHS_EQ; + case IA_CMPNE: + case IA_CMPLU: + return BC_BRANCHS_NE; + case IA_CMPLEU: + return BC_JUMPS; + case IA_CMPGU: + return BC_NOP; + } + } + else + { + ByteCodeInstruction cins(BC_BINOP_CMPUI_8); + cins.mValue = int(ins->mSrc[1].mIntConst); + mIns.Push(cins); + } + } + } + else if (ins->mSrc[0].mTemp < 0) + { + ByteCodeInstruction lins(BC_LOAD_REG_8); + lins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp]; + lins.mRegisterFinal = ins->mSrc[1].mFinal; + mIns.Push(lins); + if (csigned) + { + if (optzero && ins->mSrc[0].mIntConst == 0) + { + ByteCodeInstruction cins(BC_CONV_I8_I16); + cins.mRegister = BC_REG_ACCU; + mIns.Push(cins); + + switch (ins->mOperator) + { + case IA_CMPEQ: + return BC_BRANCHS_EQ; + case IA_CMPNE: + return BC_BRANCHS_NE; + case IA_CMPLES: + return BC_BRANCHS_LE; + case IA_CMPGS: + return BC_BRANCHS_GT; + case IA_CMPGES: + return BC_BRANCHS_GE; + case IA_CMPLS: + return BC_BRANCHS_LT; + } + } + else + { + ByteCodeInstruction cins(BC_BINOP_CMPSI_8); + cins.mValue = int(ins->mSrc[0].mIntConst); + mIns.Push(cins); + } + } + else + { + if (optzero && ins->mSrc[0].mIntConst == 0) + { + switch (ins->mOperator) + { + case IA_CMPEQ: + case IA_CMPLEU: + return BC_BRANCHS_EQ; + case IA_CMPNE: + case IA_CMPGU: + return BC_BRANCHS_NE; + case IA_CMPGEU: + return BC_JUMPS; + case IA_CMPLU: + return BC_NOP; + } + } + else + { + ByteCodeInstruction cins(BC_BINOP_CMPUI_8); + cins.mValue = int(ins->mSrc[0].mIntConst); + mIns.Push(cins); + } + } + code = TransposeBranchCondition(code); + } + else + { + ByteCodeInstruction lins(BC_LOAD_REG_8); + lins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + lins.mRegisterFinal = ins->mSrc[0].mFinal; + mIns.Push(lins); + if (csigned) + { + ByteCodeInstruction cins(BC_BINOP_CMPSR_8); + cins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp]; + cins.mRegisterFinal = ins->mSrc[1].mFinal; + mIns.Push(cins); + } + else + { + ByteCodeInstruction cins(BC_BINOP_CMPUR_8); + cins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp]; + cins.mRegisterFinal = ins->mSrc[1].mFinal; + mIns.Push(cins); + } + } + } + else + { + if (ins->mSrc[1].mTemp < 0) + { + if (ins->mSrc[1].mType == IT_INT16 || ins->mSrc[1].mMemory == IM_ABSOLUTE) + { + ByteCodeInstruction lins(BC_LOAD_REG_16); + lins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + lins.mRegisterFinal = ins->mSrc[0].mFinal; + mIns.Push(lins); + + if (csigned) + { + ByteCodeInstruction cins(BC_BINOP_CMPSI_16); + cins.mValue = int(ins->mSrc[1].mIntConst); + mIns.Push(cins); + } + else + { + ByteCodeInstruction cins(BC_BINOP_CMPUI_16); + cins.mValue = int(ins->mSrc[1].mIntConst); + mIns.Push(cins); + } + } + else + { + ByteCodeInstruction bins(BC_LEA_ABS); + bins.mRegister = BC_REG_ACCU; + bins.mLinkerObject = ins->mSrc[1].mLinkerObject; + bins.mValue = int(ins->mSrc[1].mIntConst); + bins.mRelocate = true; + mIns.Push(bins); + + if (csigned) + { + ByteCodeInstruction cins(BC_BINOP_CMPSR_16); + cins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + cins.mRegisterFinal = ins->mSrc[0].mFinal; + mIns.Push(cins); + } + else + { + ByteCodeInstruction cins(BC_BINOP_CMPUR_16); + cins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + cins.mRegisterFinal = ins->mSrc[0].mFinal; + mIns.Push(cins); + } + + code = TransposeBranchCondition(code); + } + } + else if (ins->mSrc[0].mTemp < 0) + { + if (ins->mSrc[0].mType == IT_INT16 || ins->mSrc[0].mMemory == IM_ABSOLUTE) + { + ByteCodeInstruction lins(BC_LOAD_REG_16); + lins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp]; + lins.mRegisterFinal = ins->mSrc[1].mFinal; + mIns.Push(lins); + if (csigned) + { + if (optzero && ins->mSrc[0].mIntConst == 0) + { + switch (ins->mOperator) + { + case IA_CMPEQ: + return BC_BRANCHS_EQ; + case IA_CMPNE: + return BC_BRANCHS_NE; + case IA_CMPLES: + return BC_BRANCHS_LE; + case IA_CMPGS: + return BC_BRANCHS_GT; + case IA_CMPGES: + return BC_BRANCHS_GE; + case IA_CMPLS: + return BC_BRANCHS_LT; + } + } + else if (ins->mSrc[1].IsUByte()) + { + ByteCodeInstruction cins(BC_BINOP_CMPUI_8); + cins.mValue = int(ins->mSrc[0].mIntConst); + mIns.Push(cins); + } + else + { + ByteCodeInstruction cins(BC_BINOP_CMPSI_16); + cins.mValue = int(ins->mSrc[0].mIntConst); + mIns.Push(cins); + } + } + else + { + if (optzero && ins->mSrc[0].mIntConst == 0) + { + switch (ins->mOperator) + { + case IA_CMPEQ: + case IA_CMPLEU: + return BC_BRANCHS_EQ; + case IA_CMPNE: + case IA_CMPGU: + return BC_BRANCHS_NE; + case IA_CMPGEU: + return BC_JUMPS; + case IA_CMPLU: + return BC_NOP; + } + } + else if (ins->mSrc[1].IsUByte()) + { + ByteCodeInstruction cins(BC_BINOP_CMPUI_8); + cins.mValue = int(ins->mSrc[0].mIntConst); + mIns.Push(cins); + } + else + { + ByteCodeInstruction cins(BC_BINOP_CMPUI_16); + cins.mValue = int(ins->mSrc[0].mIntConst); + mIns.Push(cins); + } + } + code = TransposeBranchCondition(code); + } + else + { + ByteCodeInstruction bins(BC_LEA_ABS); + bins.mRegister = BC_REG_ACCU; + bins.mLinkerObject = ins->mSrc[0].mLinkerObject; + bins.mValue = int(ins->mSrc[0].mIntConst); + bins.mRelocate = true; + mIns.Push(bins); + + if (csigned) + { + ByteCodeInstruction cins(BC_BINOP_CMPSR_16); + cins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp]; + cins.mRegisterFinal = ins->mSrc[1].mFinal; + mIns.Push(cins); + } + else + { + ByteCodeInstruction cins(BC_BINOP_CMPUR_16); + cins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp]; + cins.mRegisterFinal = ins->mSrc[1].mFinal; + mIns.Push(cins); + } + } + } + else + { + ByteCodeInstruction lins(BC_LOAD_REG_16); + lins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + lins.mRegisterFinal = ins->mSrc[0].mFinal; + mIns.Push(lins); + if (csigned) + { + ByteCodeInstruction cins(BC_BINOP_CMPSR_16); + cins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp]; + cins.mRegisterFinal = ins->mSrc[1].mFinal; + mIns.Push(cins); + } + else + { + ByteCodeInstruction cins(BC_BINOP_CMPUR_16); + cins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp]; + cins.mRegisterFinal = ins->mSrc[1].mFinal; + mIns.Push(cins); + } + } + } + + return code; +} + +static ByteCode ByteCodeBinRegOperator(const InterInstruction * ins) +{ + if (ins->mDst.mType == IT_FLOAT) + { + switch (ins->mOperator) + { + case IA_ADD: return BC_BINOP_ADD_F32; + case IA_SUB: return BC_BINOP_SUB_F32; + case IA_MUL: return BC_BINOP_MUL_F32; + case IA_DIVU: return BC_BINOP_DIV_F32; + case IA_DIVS: return BC_BINOP_DIV_F32; + + default: + return BC_EXIT; + } + } + else if (ins->mDst.mType == IT_INT32) + { + switch (ins->mOperator) + { + case IA_ADD: return BC_BINOP_ADD_L32; + case IA_SUB: return BC_BINOP_SUB_L32; + case IA_MUL: return BC_BINOP_MUL_L32; + case IA_DIVU: return BC_BINOP_DIV_U32; + case IA_MODU: return BC_BINOP_MOD_U32; + case IA_DIVS: return BC_BINOP_DIV_I32; + case IA_MODS: return BC_BINOP_MOD_I32; + case IA_AND: return BC_BINOP_AND_L32; + case IA_OR: return BC_BINOP_OR_L32; + case IA_XOR: return BC_BINOP_XOR_L32; + + case IA_SHL: return BC_BINOP_SHL_L32; + case IA_SHR: return BC_BINOP_SHR_U32; + case IA_SAR: return BC_BINOP_SHR_I32; + + default: + return BC_EXIT; + } + } + else + { + switch (ins->mOperator) + { + case IA_ADD: return BC_BINOP_ADDR_16; + case IA_SUB: return BC_BINOP_SUBR_16; + case IA_MUL: return BC_BINOP_MULR_16; + case IA_DIVU: return BC_BINOP_DIVR_U16; + case IA_MODU: return BC_BINOP_MODR_U16; + case IA_DIVS: return BC_BINOP_DIVR_I16; + case IA_MODS: return BC_BINOP_MODR_I16; + case IA_AND: return BC_BINOP_ANDR_16; + case IA_OR: return BC_BINOP_ORR_16; + case IA_XOR: return BC_BINOP_XORR_16; + + case IA_SHL: return BC_BINOP_SHLR_16; + case IA_SHR: return BC_BINOP_SHRR_U16; + case IA_SAR: return BC_BINOP_SHRR_I16; + + default: + return BC_EXIT; + } + } +} + +static ByteCode ByteCodeBinImmOperator(const InterInstruction * ins) +{ + switch (ins->mOperator) + { + case IA_ADD: return BC_BINOP_ADDI_16; + case IA_SUB: return BC_BINOP_SUBI_16; + case IA_AND: return BC_BINOP_ANDI_16; + case IA_OR: return BC_BINOP_ORI_16; + case IA_SHL: return BC_BINOP_SHLI_16; + case IA_SHR: return BC_BINOP_SHRI_U16; + case IA_SAR: return BC_BINOP_SHRI_I16; + case IA_MUL: return BC_BINOP_MULI8_16; + + default: + return BC_EXIT; + } +} + +static ByteCode ByteCodeBinSizeImmOperator(const InterInstruction* ins) +{ + switch (ins->mOperator) + { + case IA_ADD: return InterTypeSize[ins->mDst.mType] == 1 ? BC_BINOP_ADDI_8 : BC_BINOP_ADDI_16; + case IA_SUB: return BC_BINOP_SUBI_16; + case IA_AND: return InterTypeSize[ins->mDst.mType] == 1 ? BC_BINOP_ANDI_8 : BC_BINOP_ANDI_16; + case IA_OR: return InterTypeSize[ins->mDst.mType] == 1 ? BC_BINOP_ORI_8 : BC_BINOP_ORI_16; + case IA_SHL: return BC_BINOP_SHLI_16; + case IA_SHR: return BC_BINOP_SHRI_U16; + case IA_SAR: return BC_BINOP_SHRI_I16; + case IA_MUL: return BC_BINOP_MULI8_16; + + default: + return BC_EXIT; + } +} + +void ByteCodeBasicBlock::NumericConversion(InterCodeProcedure* proc, const InterInstruction * ins) +{ + switch (ins->mOperator) + { + case IA_FLOAT2INT: + { + ByteCodeInstruction lins(BC_LOAD_REG_32); + lins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + lins.mRegisterFinal = ins->mSrc[0].mFinal; + mIns.Push(lins); + + ByteCodeInstruction bins(BC_CONV_F32_I16); + mIns.Push(bins); + + ByteCodeInstruction sins(BC_STORE_REG_16); + sins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + mIns.Push(sins); + + } break; + case IA_INT2FLOAT: + { + ByteCodeInstruction lins(BC_LOAD_REG_16); + lins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + lins.mRegisterFinal = ins->mSrc[0].mFinal; + mIns.Push(lins); + + ByteCodeInstruction bins(BC_CONV_I16_F32); + mIns.Push(bins); + + ByteCodeInstruction sins(BC_STORE_REG_32); + sins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + mIns.Push(sins); + + } break; + case IA_FLOAT2UINT: + { + ByteCodeInstruction lins(BC_LOAD_REG_32); + lins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + lins.mRegisterFinal = ins->mSrc[0].mFinal; + mIns.Push(lins); + + ByteCodeInstruction bins(BC_CONV_F32_U16); + mIns.Push(bins); + + ByteCodeInstruction sins(BC_STORE_REG_16); + sins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + mIns.Push(sins); + + } break; + case IA_UINT2FLOAT: + { + ByteCodeInstruction lins(BC_LOAD_REG_16); + lins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + lins.mRegisterFinal = ins->mSrc[0].mFinal; + mIns.Push(lins); + + ByteCodeInstruction bins(BC_CONV_U16_F32); + mIns.Push(bins); + + ByteCodeInstruction sins(BC_STORE_REG_32); + sins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + mIns.Push(sins); + + } break; + + case IA_FLOAT2LINT: + { + ByteCodeInstruction lins(BC_LOAD_REG_32); + lins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + lins.mRegisterFinal = ins->mSrc[0].mFinal; + mIns.Push(lins); + + ByteCodeInstruction bins(BC_CONV_F32_I32); + mIns.Push(bins); + + ByteCodeInstruction sins(BC_STORE_REG_32); + sins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + mIns.Push(sins); + + } break; + case IA_LINT2FLOAT: + { + ByteCodeInstruction lins(BC_LOAD_REG_32); + lins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + lins.mRegisterFinal = ins->mSrc[0].mFinal; + mIns.Push(lins); + + ByteCodeInstruction bins(BC_CONV_I32_F32); + mIns.Push(bins); + + ByteCodeInstruction sins(BC_STORE_REG_32); + sins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + mIns.Push(sins); + + } break; + case IA_FLOAT2LUINT: + { + ByteCodeInstruction lins(BC_LOAD_REG_32); + lins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + lins.mRegisterFinal = ins->mSrc[0].mFinal; + mIns.Push(lins); + + ByteCodeInstruction bins(BC_CONV_F32_U32); + mIns.Push(bins); + + ByteCodeInstruction sins(BC_STORE_REG_32); + sins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + mIns.Push(sins); + + } break; + case IA_LUINT2FLOAT: + { + ByteCodeInstruction lins(BC_LOAD_REG_32); + lins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + lins.mRegisterFinal = ins->mSrc[0].mFinal; + mIns.Push(lins); + + ByteCodeInstruction bins(BC_CONV_U32_F32); + mIns.Push(bins); + + ByteCodeInstruction sins(BC_STORE_REG_32); + sins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + mIns.Push(sins); + + } break; + + case IA_EXT8TO16S: + { + if (ins->mSrc[0].mTemp == ins->mDst.mTemp) + { + ByteCodeInstruction cins(BC_CONV_I8_I16); + cins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + cins.mRegisterFinal = ins->mSrc[0].mFinal; + mIns.Push(cins); + } + else + { + ByteCodeInstruction lins(BC_LOAD_REG_8); + lins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + lins.mRegisterFinal = ins->mSrc[0].mFinal; + mIns.Push(lins); + + ByteCodeInstruction cins(BC_CONV_I8_I16); + cins.mRegister = BC_REG_ACCU; + mIns.Push(cins); + + ByteCodeInstruction sins(BC_STORE_REG_16); + sins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + mIns.Push(sins); + } + } break; + case IA_EXT8TO16U: + { + if (ins->mSrc[0].mTemp == ins->mDst.mTemp) + { + ByteCodeInstruction cins(BC_BINOP_ANDI_16); + cins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + cins.mRegisterFinal = ins->mSrc[0].mFinal; + cins.mValue = 0x00ff; + mIns.Push(cins); + } + else + { + ByteCodeInstruction lins(BC_LOAD_REG_8); + lins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + lins.mRegisterFinal = ins->mSrc[0].mFinal; + mIns.Push(lins); + + ByteCodeInstruction sins(BC_STORE_REG_16); + sins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + mIns.Push(sins); + } + } break; + case IA_EXT16TO32S: + { + ByteCodeInstruction lins(BC_LOAD_REG_16); + lins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + lins.mRegisterFinal = ins->mSrc[0].mFinal; + mIns.Push(lins); + + ByteCodeInstruction cins(BC_CONV_I16_I32); + cins.mRegister = 0; + mIns.Push(cins); + + ByteCodeInstruction sins(BC_STORE_REG_32); + sins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + mIns.Push(sins); + } break; + + case IA_EXT16TO32U: + { + ByteCodeInstruction lins(BC_LOAD_REG_16); + lins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + lins.mRegisterFinal = ins->mSrc[0].mFinal; + mIns.Push(lins); + + ByteCodeInstruction cins(BC_CONV_U16_U32); + cins.mRuntime = "lextu16"; + cins.mRegister = 0; + mIns.Push(cins); + + ByteCodeInstruction sins(BC_STORE_REG_32); + sins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + mIns.Push(sins); + } break; + + + case IA_EXT8TO32S: + { + ByteCodeInstruction lins(BC_LOAD_REG_8); + lins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + lins.mRegisterFinal = ins->mSrc[0].mFinal; + mIns.Push(lins); + + ByteCodeInstruction cins0(BC_CONV_I8_I16); + cins0.mRegister = BC_REG_ACCU; + mIns.Push(cins0); + + ByteCodeInstruction cins1(BC_CONV_I16_I32); + cins1.mRegister = 0; + mIns.Push(cins1); + + ByteCodeInstruction sins(BC_STORE_REG_32); + sins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + mIns.Push(sins); + } break; + + case IA_EXT8TO32U: + { + ByteCodeInstruction lins(BC_LOAD_REG_8); + lins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + lins.mRegisterFinal = ins->mSrc[0].mFinal; + mIns.Push(lins); + + ByteCodeInstruction cins(BC_CONV_U16_U32); + cins.mRuntime = "lextu16"; + cins.mRegister = 0; + mIns.Push(cins); + + ByteCodeInstruction sins(BC_STORE_REG_32); + sins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + mIns.Push(sins); + } break; + } +} + +void ByteCodeBasicBlock::UnaryOperator(InterCodeProcedure* proc, const InterInstruction * ins) +{ + if (ins->mDst.mType == IT_FLOAT) + { + ByteCodeInstruction lins(BC_LOAD_REG_32); + lins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + lins.mRegisterFinal = ins->mSrc[0].mFinal; + mIns.Push(lins); + + switch (ins->mOperator) + { + case IA_NEG: + { + ByteCodeInstruction oins(BC_OP_NEGATE_F32); + mIns.Push(oins); + } break; + case IA_ABS: + { + ByteCodeInstruction oins(BC_OP_ABS_F32); + mIns.Push(oins); + } break; + case IA_FLOOR: + { + ByteCodeInstruction oins(BC_OP_FLOOR_F32); + mIns.Push(oins); + } break; + case IA_CEIL: + { + ByteCodeInstruction oins(BC_OP_CEIL_F32); + mIns.Push(oins); + } break; + } + + ByteCodeInstruction sins(BC_STORE_REG_32); + sins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + mIns.Push(sins); + } + else if (ins->mDst.mType == IT_INT32) + { + ByteCodeInstruction lins(BC_LOAD_REG_32); + lins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + lins.mRegisterFinal = ins->mSrc[0].mFinal; + mIns.Push(lins); + + switch (ins->mOperator) + { + case IA_NEG: + { + ByteCodeInstruction oins(BC_OP_NEGATE_32); + mIns.Push(oins); + } break; + case IA_NOT: + { + ByteCodeInstruction oins(BC_OP_INVERT_32); + mIns.Push(oins); + } break; + } + + ByteCodeInstruction sins(BC_STORE_REG_32); + sins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + mIns.Push(sins); + } + else + { + ByteCodeInstruction lins(BC_LOAD_REG_16); + lins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + lins.mRegisterFinal = ins->mSrc[0].mFinal; + mIns.Push(lins); + + switch (ins->mOperator) + { + case IA_NEG: + { + ByteCodeInstruction oins(BC_OP_NEGATE_16); + mIns.Push(oins); + } break; + + case IA_NOT: + { + ByteCodeInstruction oins(BC_OP_INVERT_16); + mIns.Push(oins); + } break; + } + + ByteCodeInstruction sins(BC_STORE_REG_16); + sins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + mIns.Push(sins); + } +} + +void ByteCodeBasicBlock::BinaryOperator(InterCodeProcedure* proc, const InterInstruction * ins) +{ + if (ins->mDst.mType == IT_FLOAT) + { + ByteCode bc = ByteCodeBinRegOperator(ins); + + if (ins->mSrc[1].mTemp < 0) + { + FloatConstToAccu(ins->mSrc[1].mFloatConst); + } + else + { + ByteCodeInstruction lins(BC_LOAD_REG_32); + lins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp]; + lins.mRegisterFinal = ins->mSrc[1].mFinal; + mIns.Push(lins); + } + + ByteCodeInstruction bins(bc); + + if (ins->mSrc[0].mTemp < 0) + { + FloatConstToWork(ins->mSrc[0].mFloatConst); + bins.mRegister = BC_REG_WORK; + } + else if (ins->mSrc[1].mTemp == ins->mSrc[0].mTemp) + bins.mRegister = BC_REG_ACCU; + else + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + + bins.mRegisterFinal = ins->mSrc[0].mFinal; + mIns.Push(bins); + + ByteCodeInstruction sins(BC_STORE_REG_32); + sins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + mIns.Push(sins); + } + else if (ins->mDst.mType == IT_INT32) + { + ByteCode bc = ByteCodeBinRegOperator(ins); + + if (ins->mSrc[1].mTemp < 0) + { + LongConstToAccu(ins->mSrc[1].mIntConst); + } + else + { + ByteCodeInstruction lins(BC_LOAD_REG_32); + lins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp]; + lins.mRegisterFinal = ins->mSrc[1].mFinal; + mIns.Push(lins); + } + + ByteCodeInstruction bins(bc); + + if (ins->mSrc[0].mTemp < 0) + { + LongConstToWork(ins->mSrc[0].mIntConst); + bins.mRegister = BC_REG_WORK; + } + else if (ins->mSrc[1].mTemp == ins->mSrc[0].mTemp) + bins.mRegister = BC_REG_ACCU; + else + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + + bins.mRegisterFinal = ins->mSrc[0].mFinal; + mIns.Push(bins); + + ByteCodeInstruction sins(BC_STORE_REG_32); + sins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + mIns.Push(sins); + } + else + { + switch (ins->mOperator) + { + case IA_ADD: + case IA_OR: + case IA_AND: + { + ByteCode bc = ByteCodeBinRegOperator(ins); + ByteCode bci = ByteCodeBinImmOperator(ins); + ByteCode bcis = ByteCodeBinSizeImmOperator(ins); + + if (ins->mSrc[1].mTemp < 0) + { + if (ins->mSrc[0].mTemp == ins->mDst.mTemp) + { + ByteCodeInstruction bins(bcis); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + bins.mValue = int(ins->mSrc[1].mIntConst); + mIns.Push(bins); + return; + } + + ByteCodeInstruction lins(InterTypeSize[ins->mSrc[0].mType] == 1 ? BC_LOAD_REG_8 : BC_LOAD_REG_16); + lins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + lins.mRegisterFinal = ins->mSrc[0].mFinal; + mIns.Push(lins); + + ByteCodeInstruction bins(bci); + bins.mRegister = BC_REG_ACCU; + bins.mValue = int(ins->mSrc[1].mIntConst); + mIns.Push(bins); + } + else if (ins->mSrc[0].mTemp < 0) + { + if (ins->mSrc[1].mTemp == ins->mDst.mTemp) + { + ByteCodeInstruction bins(bcis); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + bins.mValue = int(ins->mSrc[0].mIntConst); + mIns.Push(bins); + return; + } + + ByteCodeInstruction lins(InterTypeSize[ins->mSrc[1].mType] == 1 ? BC_LOAD_REG_8 : BC_LOAD_REG_16); + lins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp]; + lins.mRegisterFinal = ins->mSrc[1].mFinal; + mIns.Push(lins); + + ByteCodeInstruction bins(bci); + bins.mRegister = BC_REG_ACCU; + bins.mValue = int(ins->mSrc[0].mIntConst); + mIns.Push(bins); + } + else + { + ByteCodeInstruction lins(InterTypeSize[ins->mSrc[1].mType] == 1 ? BC_LOAD_REG_8 : BC_LOAD_REG_16); + lins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp]; + lins.mRegisterFinal = ins->mSrc[1].mFinal && (ins->mSrc[1].mTemp != ins->mSrc[0].mTemp); + mIns.Push(lins); + + ByteCodeInstruction bins(bc); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + bins.mRegisterFinal = ins->mSrc[0].mFinal; + mIns.Push(bins); + } + } + break; + case IA_SUB: + if (ins->mSrc[1].mTemp < 0) + { + if (ins->mSrc[0].mTemp == ins->mDst.mTemp) + { + ByteCodeInstruction bins(BC_BINOP_SUBI_16); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + bins.mValue = int(ins->mSrc[1].mIntConst); + mIns.Push(bins); + return; + } + ByteCodeInstruction lins(InterTypeSize[ins->mSrc[1].mType] == 1 ? BC_LOAD_REG_8 : BC_LOAD_REG_16); + lins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + lins.mRegisterFinal = ins->mSrc[0].mFinal; + mIns.Push(lins); + + ByteCodeInstruction bins(BC_BINOP_SUBI_16); + bins.mRegister = BC_REG_ACCU; + bins.mValue = int(ins->mSrc[1].mIntConst); + mIns.Push(bins); + } + else if (ins->mSrc[0].mTemp < 0) + { + if (ins->mSrc[1].mTemp == ins->mDst.mTemp) + { + ByteCodeInstruction bins(InterTypeSize[ins->mSrc[0].mType] == 1 ? BC_BINOP_ADDI_8 : BC_BINOP_ADDI_16); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + bins.mValue = - int(ins->mSrc[0].mIntConst); + mIns.Push(bins); + return; + } + ByteCodeInstruction lins(InterTypeSize[ins->mSrc[0].mType] == 1 ? BC_LOAD_REG_8 : BC_LOAD_REG_16); + lins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp]; + lins.mRegisterFinal = ins->mSrc[1].mFinal; + mIns.Push(lins); + + ByteCodeInstruction bins(BC_BINOP_ADDI_16); + bins.mRegister = BC_REG_ACCU; + bins.mValue = - int(ins->mSrc[0].mIntConst); + mIns.Push(bins); + } + else + { + ByteCodeInstruction lins(InterTypeSize[ins->mSrc[1].mType] == 1 ? BC_LOAD_REG_8 : BC_LOAD_REG_16); + lins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp]; + lins.mRegisterFinal = ins->mSrc[1].mFinal && (ins->mSrc[1].mTemp != ins->mSrc[0].mTemp);; + mIns.Push(lins); + + ByteCodeInstruction bins(BC_BINOP_SUBR_16); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + bins.mRegisterFinal = ins->mSrc[0].mFinal; + mIns.Push(bins); + } + break; + case IA_MUL: + { + ByteCode bc = ByteCodeBinRegOperator(ins); + ByteCode bci = ByteCodeBinImmOperator(ins); + if (ins->mSrc[1].mTemp < 0) + { + if (ins->mSrc[1].mIntConst >= 0 && ins->mSrc[1].mIntConst <= 255) + { + if (ins->mSrc[0].mTemp == ins->mDst.mTemp) + { + ByteCodeInstruction bins(bci); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + bins.mRegisterFinal = ins->mSrc[0].mFinal; + bins.mValue = int(ins->mSrc[1].mIntConst); + mIns.Push(bins); + return; + } + + ByteCodeInstruction lins(BC_LOAD_REG_16); + lins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + lins.mRegisterFinal = ins->mSrc[0].mFinal; + mIns.Push(lins); + + ByteCodeInstruction bins(bci); + bins.mRegister = BC_REG_ACCU; + bins.mValue = int(ins->mSrc[1].mIntConst); + mIns.Push(bins); + } + else + { + IntConstToAccu(ins->mSrc[1].mIntConst); + ByteCodeInstruction bins(bc); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + bins.mRegisterFinal = ins->mSrc[0].mFinal; + mIns.Push(bins); + } + } + else if (ins->mSrc[0].mTemp < 0) + { + if (ins->mSrc[0].mIntConst >= 0 && ins->mSrc[0].mIntConst <= 255) + { + if (ins->mSrc[1].mTemp == ins->mDst.mTemp) + { + ByteCodeInstruction bins(bci); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp]; + bins.mRegisterFinal = ins->mSrc[1].mFinal; + bins.mValue = int(ins->mSrc[0].mIntConst); + mIns.Push(bins); + return; + } + + ByteCodeInstruction lins(BC_LOAD_REG_16); + lins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp]; + lins.mRegisterFinal = ins->mSrc[1].mFinal; + mIns.Push(lins); + + ByteCodeInstruction bins(bci); + bins.mRegister = BC_REG_ACCU; + bins.mValue = int(ins->mSrc[0].mIntConst); + mIns.Push(bins); + } + else + { + IntConstToAccu(ins->mSrc[0].mIntConst); + ByteCodeInstruction bins(bc); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp]; + bins.mRegisterFinal = ins->mSrc[1].mFinal; + mIns.Push(bins); + } + } + else + { + ByteCodeInstruction lins(BC_LOAD_REG_16); + lins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp]; + lins.mRegisterFinal = ins->mSrc[1].mFinal && (ins->mSrc[1].mTemp != ins->mSrc[0].mTemp);; + mIns.Push(lins); + + ByteCodeInstruction bins(bc); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + bins.mRegisterFinal = ins->mSrc[0].mFinal; + mIns.Push(bins); + } + } break; + case IA_XOR: + { + ByteCode bc = ByteCodeBinRegOperator(ins); + if (ins->mSrc[1].mTemp < 0) + { + IntConstToAccu(ins->mSrc[1].mIntConst); + ByteCodeInstruction bins(bc); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + bins.mRegisterFinal = ins->mSrc[0].mFinal; + mIns.Push(bins); + } + else if (ins->mSrc[0].mTemp < 0) + { + IntConstToAccu(ins->mSrc[0].mIntConst); + ByteCodeInstruction bins(bc); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp]; + bins.mRegisterFinal = ins->mSrc[1].mFinal; + mIns.Push(bins); + } + else + { + ByteCodeInstruction lins(BC_LOAD_REG_16); + lins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp]; + lins.mRegisterFinal = ins->mSrc[1].mFinal && (ins->mSrc[1].mTemp != ins->mSrc[0].mTemp);; + mIns.Push(lins); + + ByteCodeInstruction bins(bc); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + bins.mRegisterFinal = ins->mSrc[0].mFinal; + mIns.Push(bins); + } + } break; + case IA_DIVS: + case IA_MODS: + case IA_DIVU: + case IA_MODU: + { + ByteCode bc = ByteCodeBinRegOperator(ins); + if (ins->mSrc[1].mTemp < 0) + { + IntConstToAccu(ins->mSrc[1].mIntConst); + ByteCodeInstruction bins(bc); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + bins.mRegisterFinal = ins->mSrc[0].mFinal; + mIns.Push(bins); + } + else if (ins->mSrc[0].mTemp < 0) + { + ByteCodeInstruction lins(BC_LOAD_REG_16); + lins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp]; + lins.mRegisterFinal = ins->mSrc[1].mFinal; + mIns.Push(lins); + + IntConstToAddr(ins->mSrc[0].mIntConst); + + ByteCodeInstruction bins(bc); + bins.mRegister = BC_REG_ADDR; + mIns.Push(bins); + } + else + { + ByteCodeInstruction lins(BC_LOAD_REG_16); + lins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp]; + lins.mRegisterFinal = ins->mSrc[1].mFinal && (ins->mSrc[1].mTemp != ins->mSrc[0].mTemp);; + mIns.Push(lins); + + ByteCodeInstruction bins(bc); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + bins.mRegisterFinal = ins->mSrc[0].mFinal; + mIns.Push(bins); + } + } break; + + case IA_SHL: + case IA_SHR: + case IA_SAR: + { + ByteCode rbc = ByteCodeBinRegOperator(ins); + ByteCode ibc = ByteCodeBinImmOperator(ins); + + if (ins->mSrc[1].mTemp < 0) + { + IntConstToAccu(ins->mSrc[1].mIntConst); + + ByteCodeInstruction bins(rbc); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + bins.mRegisterFinal = ins->mSrc[0].mFinal; + mIns.Push(bins); + } + else if (ins->mSrc[0].mTemp < 0) + { + ByteCodeInstruction lins(InterTypeSize[ins->mSrc[1].mType] == 1 ? BC_LOAD_REG_8 : BC_LOAD_REG_16); + lins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp]; + lins.mRegisterFinal = ins->mSrc[1].mFinal; + mIns.Push(lins); + + if (ins->mOperator == IA_SAR && InterTypeSize[ins->mSrc[1].mType] == 1) + { + ByteCodeInstruction xins(BC_CONV_I8_I16); + xins.mRegister = BC_REG_ACCU; + mIns.Push(xins); + } + + ByteCodeInstruction bins(ibc); + bins.mValue = int(ins->mSrc[0].mIntConst); + mIns.Push(bins); + } + else + { + ByteCodeInstruction lins(InterTypeSize[ins->mSrc[1].mType] == 1 ? BC_LOAD_REG_8 : BC_LOAD_REG_16); + lins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp]; + lins.mRegisterFinal = ins->mSrc[1].mFinal && (ins->mSrc[1].mTemp != ins->mSrc[0].mTemp);; + mIns.Push(lins); + + ByteCodeInstruction bins(rbc); + bins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + bins.mRegisterFinal = ins->mSrc[0].mFinal; + mIns.Push(bins); + } + + } break; + } + + ByteCodeInstruction sins(InterTypeSize[ins->mSrc[1].mType] == 1 ? BC_STORE_REG_8 : BC_STORE_REG_16); + sins.mRegister = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + mIns.Push(sins); + } +} + +void ByteCodeBasicBlock::Compile(InterCodeProcedure* iproc, ByteCodeProcedure* proc, InterCodeBasicBlock* sblock) +{ + mIndex = sblock->mIndex; + + int i = 0; + while (i < sblock->mInstructions.Size()) + { + const InterInstruction * ins = sblock->mInstructions[i]; + + switch (ins->mCode) + { + case IC_STORE: + StoreDirectValue(iproc, ins); + break; + case IC_LOAD: + if (i + 1 < sblock->mInstructions.Size() && sblock->mInstructions[i + 1]->mCode == IC_STORE && ins->mDst.mTemp == sblock->mInstructions[i + 1]->mSrc[0].mTemp) + { + LoadStoreIndirectValue(iproc, ins, sblock->mInstructions[i + 1]); + i++; + } + else + LoadDirectValue(iproc, ins); + break; + case IC_FILL: + FillValue(iproc, ins); + break; + case IC_COPY: + CopyValue(iproc, ins); + break; + case IC_STRCPY: + StrcpyValue(iproc, ins); + break; + case IC_MALLOC: + CallMalloc(iproc, ins); + break; + case IC_FREE: + CallFree(iproc, ins); + break; + case IC_LOAD_TEMPORARY: + { + if (ins->mSrc[0].mTemp != ins->mDst.mTemp) + { + switch (ins->mDst.mType) + { + case IT_BOOL: + case IT_INT8: + { + ByteCodeInstruction lins(BC_LOAD_REG_8); + lins.mRegister = BC_REG_TMP + iproc->mTempOffset[ins->mSrc[0].mTemp]; + lins.mRegisterFinal = ins->mSrc[0].mFinal; + mIns.Push(lins); + ByteCodeInstruction sins(BC_STORE_REG_8); + sins.mRegister = BC_REG_TMP + iproc->mTempOffset[ins->mDst.mTemp]; + mIns.Push(sins); + } break; + case IT_INT16: + case IT_POINTER: + { + ByteCodeInstruction lins(BC_LOAD_REG_16); + lins.mRegister = BC_REG_TMP + iproc->mTempOffset[ins->mSrc[0].mTemp]; + lins.mRegisterFinal = ins->mSrc[0].mFinal; + mIns.Push(lins); + ByteCodeInstruction sins(BC_STORE_REG_16); + sins.mRegister = BC_REG_TMP + iproc->mTempOffset[ins->mDst.mTemp]; + mIns.Push(sins); + } break; + case IT_INT32: + case IT_FLOAT: + { + ByteCodeInstruction lins(BC_LOAD_REG_32); + lins.mRegister = BC_REG_TMP + iproc->mTempOffset[ins->mSrc[0].mTemp]; + lins.mRegisterFinal = ins->mSrc[0].mFinal; + mIns.Push(lins); + ByteCodeInstruction sins(BC_STORE_REG_32); + sins.mRegister = BC_REG_TMP + iproc->mTempOffset[ins->mDst.mTemp]; + mIns.Push(sins); + } break; + + } + } + } break; + case IC_BINARY_OPERATOR: + BinaryOperator(iproc, ins); + break; + case IC_UNARY_OPERATOR: + UnaryOperator(iproc, ins); + break; + case IC_CONVERSION_OPERATOR: + NumericConversion(iproc, ins); + break; + case IC_LEA: + LoadEffectiveAddress(iproc, ins); + break; + case IC_CONSTANT: + LoadConstant(iproc, ins); + break; + case IC_CALL: + case IC_DISPATCH: + CallFunction(iproc, ins); + break; + case IC_CALL_NATIVE: + CallNative(iproc, ins); + break; + case IC_ASSEMBLER: + CallAssembler(iproc, ins); + break; + case IC_PUSH_FRAME: + { + ByteCodeInstruction bins(BC_PUSH_FRAME); + bins.mValue = int(ins->mConst.mIntConst + 2); + mIns.Push(bins); + + } break; + case IC_POP_FRAME: + { + ByteCodeInstruction bins(BC_POP_FRAME); + bins.mValue = int(ins->mConst.mIntConst + 2); + mIns.Push(bins); + + } break; + + case IC_RELATIONAL_OPERATOR: + if (i + 1 < sblock->mInstructions.Size() && sblock->mInstructions[i + 1]->mCode == IC_BRANCH && sblock->mInstructions[i + 1]->mSrc[0].mFinal) + { + ByteCode code = RelationalOperator(iproc, ins, true); + this->Close(proc->CompileBlock(iproc, sblock->mTrueJump), proc->CompileBlock(iproc, sblock->mFalseJump), code); + i++; + return; + } + else + { + ByteCode code = RelationalOperator(iproc, ins, false); + if (code == BC_JUMPS) + { + IntConstToAccu(1); + } + else if (code == BC_NOP) + { + IntConstToAccu(0); + } + else + { + switch (code) + { + case BC_BRANCHS_EQ: + { + ByteCodeInstruction iins(BC_OP_INVERT_16); + mIns.Push(iins); + ByteCodeInstruction bins(BC_BINOP_ANDI_16); + bins.mRegister = BC_REG_ACCU; + bins.mValue = 1; + mIns.Push(bins); + } break; + case BC_BRANCHS_NE: + { + ByteCodeInstruction bins(BC_BINOP_ANDI_16); + bins.mRegister = BC_REG_ACCU; + bins.mValue = 1; + mIns.Push(bins); + } break; + case BC_BRANCHS_GT: + { + ByteCodeInstruction ains(BC_BINOP_ADDI_16); + ains.mRegister = BC_REG_ACCU; + ains.mValue = 1; + mIns.Push(ains); + ByteCodeInstruction sins(BC_BINOP_SHRI_U16); + sins.mValue = 1; + mIns.Push(sins); + } break; + case BC_BRANCHS_GE: + { + ByteCodeInstruction sins(BC_BINOP_CMPUI_8); + sins.mValue = 0xff; + mIns.Push(sins); + } break; + case BC_BRANCHS_LT: + { + ByteCodeInstruction sins(BC_BINOP_SHRI_U16); + sins.mValue = 1; + mIns.Push(sins); + ByteCodeInstruction bins(BC_BINOP_ANDI_16); + bins.mRegister = BC_REG_ACCU; + bins.mValue = 1; + mIns.Push(bins); + } break; + case BC_BRANCHS_LE: + { + ByteCodeInstruction sins(BC_BINOP_CMPSI_8); + sins.mValue = 1; + mIns.Push(sins); + } break; + } + } + ByteCodeInstruction sins(StoreTypedTmpCodes[ins->mDst.mType]); + sins.mRegister = BC_REG_TMP + iproc->mTempOffset[ins->mDst.mTemp]; + mIns.Push(sins); + } + break; + + case IC_RETURN_VALUE: + if (ins->mSrc[0].mTemp < 0) + IntConstToAccu(ins->mSrc[0].mIntConst); + else if (ins->mSrc[0].mType == IT_FLOAT || ins->mSrc[0].mType == IT_INT32) + { + ByteCodeInstruction lins(BC_LOAD_REG_32); + lins.mRegister = BC_REG_TMP + iproc->mTempOffset[ins->mSrc[0].mTemp]; + lins.mRegisterFinal = ins->mSrc[0].mFinal; + mIns.Push(lins); + } + else + { + ByteCodeInstruction lins(InterTypeSize[ins->mSrc[0].mType] == 1 ? BC_LOAD_REG_8 : BC_LOAD_REG_16); + lins.mRegister = BC_REG_TMP + iproc->mTempOffset[ins->mSrc[0].mTemp]; + lins.mRegisterFinal = ins->mSrc[0].mFinal; + mIns.Push(lins); + } + + mExitLive = LIVE_ACCU; + + this->Close(proc->exitBlock, nullptr, BC_JUMPS); + return; + + case IC_RETURN: + this->Close(proc->exitBlock, nullptr, BC_JUMPS); + return; + + case IC_TYPECAST: + if (ins->mSrc[0].mTemp >= 0 && ins->mDst.mTemp != ins->mSrc[0].mTemp) + { + ByteCodeInstruction lins(BC_LOAD_REG_16); + lins.mRegister = BC_REG_TMP + iproc->mTempOffset[ins->mSrc[0].mTemp]; + lins.mRegisterFinal = ins->mSrc[0].mFinal; + mIns.Push(lins); + ByteCodeInstruction sins(InterTypeSize[ins->mDst.mType] == 1 ? BC_STORE_REG_8 : BC_STORE_REG_16); + sins.mRegister = BC_REG_TMP + iproc->mTempOffset[ins->mDst.mTemp]; + mIns.Push(sins); + } + break; + + case IC_BRANCH: + if (ins->mSrc[0].mTemp < 0) + { + if (ins->mSrc[0].mIntConst == 0) + this->Close(proc->CompileBlock(iproc, sblock->mFalseJump), nullptr, BC_JUMPS); + else + this->Close(proc->CompileBlock(iproc, sblock->mTrueJump), nullptr, BC_JUMPS); + } + else + { + ByteCodeInstruction lins(InterTypeSize[ins->mSrc[0].mType] == 1 ? BC_LOAD_REG_8 : BC_LOAD_REG_16); + lins.mRegister = BC_REG_TMP + iproc->mTempOffset[ins->mSrc[0].mTemp]; + lins.mRegisterFinal = ins->mSrc[0].mFinal; + mIns.Push(lins); + + this->Close(proc->CompileBlock(iproc, sblock->mTrueJump), proc->CompileBlock(iproc, sblock->mFalseJump), BC_BRANCHS_NE); + } + return; + + case IC_UNREACHABLE: + this->Close(proc->exitBlock, nullptr, BC_JUMPS); + return; + } + + i++; + } + + this->Close(proc->CompileBlock(iproc, sblock->mTrueJump), nullptr, BC_JUMPS); +} + +void ByteCodeBasicBlock::CountEntries(ByteCodeBasicBlock* fromJump) +{ + mNumEntries++; + + if (!mVisited) + { + mVisited = true; + + if (mTrueJump) + mTrueJump->CountEntries(this); + if (mFalseJump) + mFalseJump->CountEntries(this); + } +} + +void ByteCodeBasicBlock::CollectEntryBlocks(ByteCodeBasicBlock* block) +{ + if (block) + mEntryBlocks.Push(block); + + if (!mVisited) + { + mVisited = true; + + if (mTrueJump) + mTrueJump->CollectEntryBlocks(this); + if (mFalseJump) + mFalseJump->CollectEntryBlocks(this); + } +} + +bool ByteCodeBasicBlock::SameTail(ByteCodeInstruction& ins) +{ + if (mIns.Size() > 0) + return mIns[mIns.Size() - 1].IsSame(ins); + else + return false; +} + +bool ByteCodeBasicBlock::MergeBasicBlocks(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + if (!mLocked) + { + while (mTrueJump && !mFalseJump && mTrueJump->mNumEntries == 1 && mTrueJump != this && !mTrueJump->mLocked) + { + for (int i = 0; i < mTrueJump->mIns.Size(); i++) + mIns.Push(mTrueJump->mIns[i]); + mBranch = mTrueJump->mBranch; + mFalseJump = mTrueJump->mFalseJump; + mTrueJump = mTrueJump->mTrueJump; + changed = true; + } + + while (mTrueJump && mTrueJump->mIns.Size() == 0 && !mTrueJump->mFalseJump && !mTrueJump->mLocked && mTrueJump != this && mTrueJump->mTrueJump != mTrueJump) + { + mTrueJump->mNumEntries--; + mTrueJump = mTrueJump->mTrueJump; + mTrueJump->mNumEntries++; + changed = true; + } + + while (mFalseJump && mFalseJump->mIns.Size() == 0 && !mFalseJump->mFalseJump && !mFalseJump->mLocked && mFalseJump != this && mFalseJump->mTrueJump != mFalseJump) + { + mFalseJump->mNumEntries--; + mFalseJump = mFalseJump->mTrueJump; + mFalseJump->mNumEntries++; + changed = true; + } + + if (mTrueJump && mTrueJump == mFalseJump) + { + mBranch = BC_JUMPS; + mFalseJump = nullptr; + changed = true; + } + } + + if (mTrueJump) + mTrueJump->MergeBasicBlocks(); + if (mFalseJump) + mFalseJump->MergeBasicBlocks(); + } + return changed; +} + + +bool ByteCodeBasicBlock::JoinTailCodeSequences(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + if (mEntryBlocks.Size() > 1) + { + int i = 0; + while (i < mEntryBlocks.Size() && mEntryBlocks[i]->mBranch == BC_JUMPS) + i++; + if (i == mEntryBlocks.Size()) + { + ByteCodeBasicBlock* eb = mEntryBlocks[0]; + + while (eb->mIns.Size() > 0) + { + ByteCodeInstruction& ins(eb->mIns[eb->mIns.Size() - 1]); + i = 1; + while (i < mEntryBlocks.Size() && mEntryBlocks[i]->SameTail(ins)) + i++; + if (i == mEntryBlocks.Size()) + { + mIns.Insert(0, ins); + for (int i = 0; i < mEntryBlocks.Size(); i++) + { + ByteCodeBasicBlock* b = mEntryBlocks[i]; + b->mIns.SetSize(b->mIns.Size() - 1); + if (mIns.Size() == 1) + mExitLive |= b->mExitLive; + b->mExitLive = LIVE_ACCU; + } + changed = true; + } + else + break; + } + } + } + + if (mTrueJump && mFalseJump && mTrueJump->mEntryBlocks.Size() == 1 && mFalseJump->mEntryBlocks.Size() == 1) + { + while (mTrueJump->mIns.Size() > 0 && mFalseJump->mIns.Size() > 0 && !mTrueJump->mIns[0].ChangesAccu() && mTrueJump->mIns[0].IsSame(mFalseJump->mIns[0])) + { + mIns.Push(mTrueJump->mIns[0]); + mTrueJump->mIns.Remove(0); + mFalseJump->mIns.Remove(0); + } + } + + if (mTrueJump && mTrueJump->JoinTailCodeSequences()) + changed = true; + if (mFalseJump && mFalseJump->JoinTailCodeSequences()) + changed = true; + } + + return changed; +} + +bool ByteCodeBasicBlock::PropagateAccuCrossBorder(int accu, int addr) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + if (mEntryBlocks.Size() != 1) + accu = addr = -1; + else + { + ByteCodeBasicBlock* p = mEntryBlocks[0]; + + int s = p->mIns.Size(); + + if (p->mIns.Size() >= 2 && mIns.Size() == 2 && + mIns[1].IsCompare() && p->mIns[s - 1].IsSame(mIns[1]) && + ((mIns[0].mCode == BC_LOAD_REG_16 && (p->mIns[s - 2].mCode == BC_LOAD_REG_16 || p->mIns[s - 2].mCode == BC_STORE_REG_16)) || + (mIns[0].mCode == BC_LOAD_REG_8 && (p->mIns[s - 2].mCode == BC_LOAD_REG_8 || p->mIns[s - 2].mCode == BC_STORE_REG_8))) && + p->mIns[s - 2].mRegister == mIns[0].mRegister) + { + mIns.SetSize(0); + } + } + + int j = 0; + for (int i = 0; i < mIns.Size(); i++) + { + if (mIns[i].mCode == BC_ADDR_REG && addr == mIns[i].mRegister || + mIns[i].mCode == BC_LOAD_REG_16 && accu == mIns[i].mRegister || + mIns[i].mCode == BC_STORE_REG_16 && accu == mIns[i].mRegister) + { + changed = true; + } + else + { + if (mIns[i].mCode == BC_ADDR_REG) + addr = mIns[i].mRegister; + else if (mIns[i].mCode == BC_LOAD_REG_16) + accu = mIns[i].mRegister; + else if (mIns[i].mCode == BC_STORE_REG_16) + { + accu = mIns[i].mRegister; + if (addr == mIns[i].mRegister) + addr = -1; + } + else + { + if (accu >= 0 && mIns[i].ChangesRegister(accu)) + accu = -1; + if (addr >= 0 && mIns[i].ChangesRegister(addr)) + addr = -1; + + if (accu >= 0 && mIns[i].ChangesAccu()) + accu = -1; + if (addr >= 0 && mIns[i].ChangesAddr()) + addr = -1; + } + + if (i != j) + { + mIns[j] = mIns[i]; + } + j++; + } + } + mIns.SetSize(j); + + if (mTrueJump && mTrueJump->PropagateAccuCrossBorder(accu, addr)) + changed = true; + if (mFalseJump && mFalseJump->PropagateAccuCrossBorder(accu, addr)) + changed = true; + } + + return changed; +} + + +bool ByteCodeBasicBlock::PeepHoleOptimizer(int phase) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + int accuReg = -1; + + bool progress = false; + do { + progress = false; + + int i = 0; + int j = 0; + while (i < mIns.Size()) + { + if (mIns[i].mCode == BC_NOP) + ; + else + { + if (i != j) + mIns[j] = mIns[i]; + j++; + } + i++; + } + mIns.SetSize(j); + + // check reg addr up + // +#if 1 + for (int i = 2; i < mIns.Size(); i++) + { + if (mIns[i].mCode == BC_ADDR_REG && mIns[i].mRegister != BC_REG_ACCU && mIns[i].mRegisterFinal) + { + int j = i; + while (j > 0 && !mIns[j - 1].ChangesAddr() && !mIns[j - 1].UsesAddr() && !mIns[j - 1].ChangesRegister(mIns[j].mRegister) && !mIns[j - 1].UsesRegister(mIns[j].mRegister)) + { + ByteCodeInstruction bins = mIns[j - 1]; + mIns[j - 1] = mIns[j]; + mIns[j] = bins; + j--; + } + } + } +#endif +#if 1 + if (phase == 2) + { + for (int i = 2; i < mIns.Size(); i++) + { + if (mIns[i].mCode >= BC_LOAD_ADDR_8 && mIns[i].mCode <= BC_STORE_ADDR_32) + { + int j = i; + while (j > 0 && !mIns[j - 1].ChangesAddr() && !mIns[j - 1].UsesAddr() && !mIns[j - 1].ChangesRegister(mIns[j].mRegister) && !mIns[j - 1].UsesRegister(mIns[j].mRegister)) + { + ByteCodeInstruction bins = mIns[j - 1]; + mIns[j - 1] = mIns[j]; + mIns[j] = bins; + j--; + } + } + } + } +#endif + + // mark accu live + + uint32 live = mExitLive; + uint32 aused = mExitLive ? 0xffffffff : 0x00000000; + + if (mBranch != BC_JUMPS && mBranch != BC_NOP) + { + live |= LIVE_ACCU; + aused = 0x0000ffff; + } + + for (int i = mIns.Size() - 1; i >= 0; i--) + { + mIns[i].mLive = live; + + if (mIns[i].ChangesAccu()) + live &= ~LIVE_ACCU; + if (mIns[i].UsesAccu()) + live |= LIVE_ACCU; + } + +// assert(!(live & LIVE_ACCU)); + + int accuTemp = -1, addrTemp = -1, accuVal = 0, accuTempByte = -1; + bool accuConst = false, accuLong = false; + + for (int i = 0; i < mIns.Size(); i++) + { + if (i + 4 < mIns.Size()) + { +#if 1 + if ( + mIns[i + 0].mCode == BC_LOAD_REG_8 && + mIns[i + 1].mCode == BC_STORE_REG_16 && + mIns[i + 2].mCode == BC_LOAD_REG_8 && + (mIns[i + 3].mCode == BC_BINOP_ADDR_16 || mIns[i + 3].mCode == BC_BINOP_SUBR_16) && mIns[i + 3].mRegister == mIns[i + 1].mRegister && mIns[i + 3].mRegisterFinal && + mIns[i + 4].mCode == BC_STORE_REG_8 && !(mIns[i + 4].mLive & LIVE_ACCU)) + { + mIns[i + 3].mRegister = mIns[i + 0].mRegister; + mIns[i + 3].mRegisterFinal = mIns[i + 0].mRegisterFinal; + mIns[i + 0].mCode = BC_NOP; + mIns[i + 1].mCode = BC_NOP; + progress = true; + } +#endif +#if 1 + else if ( + mIns[i + 0].mCode == BC_BINOP_ADDR_16 && + mIns[i + 1].mCode == BC_STORE_REG_16 && + !mIns[i + 2].ChangesAccu() && !mIns[i + 2].UsesAccu() && !mIns[i + 2].UsesRegister(mIns[i + 1].mRegister) && !mIns[i + 2].ChangesRegister(mIns[i + 0].mRegister) && !mIns[i + 2].ChangesRegister(mIns[i + 1].mRegister) && + !mIns[i + 3].ChangesAccu() && !mIns[i + 3].UsesAccu() && !mIns[i + 3].UsesRegister(mIns[i + 1].mRegister) && !mIns[i + 3].ChangesRegister(mIns[i + 0].mRegister) && !mIns[i + 3].ChangesRegister(mIns[i + 1].mRegister) && + mIns[i + 4].mCode == BC_ADDR_REG && mIns[i + 4].mRegister == mIns[i + 1].mRegister && mIns[i + 4].mRegisterFinal) + { + mIns[i + 4].mCode = BC_LEA_ACCU_INDEX; + mIns[i + 4].mRegister = mIns[i + 0].mRegister; + mIns[i + 4].mRegisterFinal = mIns[i + 0].mRegisterFinal; + mIns[i + 0].mCode = BC_NOP; + mIns[i + 1].mCode = BC_NOP; + mIns[i + 2].mLive |= LIVE_ACCU; + mIns[i + 3].mLive |= LIVE_ACCU; + progress = true; + } +#endif + } + +#if 1 + if (i + 3 < mIns.Size()) + { + if ( + mIns[i + 0].mCode == BC_LOAD_REG_8 && + mIns[i + 1].mCode == BC_STORE_REG_16 && + mIns[i + 2].IsIntegerConst() && mIns[i + 2].mRegister == BC_REG_ACCU && + mIns[i + 3].IsShiftByRegister() && mIns[i + 3].mRegister == mIns[i + 1].mRegister && mIns[i + 3].mRegisterFinal) + { + mIns[i + 0].mCode = BC_NOP; + mIns[i + 1].mCode = BC_NOP; + mIns[i + 3].mRegister = mIns[i + 0].mRegister; + progress = true; + } + else if ( + mIns[i + 0].mCode == BC_LOAD_REG_8 && + mIns[i + 1].mCode == BC_LEA_ABS && mIns[i + 1].mRegister != BC_REG_ACCU && + mIns[i + 2].mCode == BC_BINOP_ADDR_16 && mIns[i + 2].mRegister == mIns[i + 1].mRegister && mIns[i + 2].mRegisterFinal && + mIns[i + 3].mCode == BC_ADDR_REG && mIns[i + 3].mRegister == BC_REG_ACCU && !(mIns[i + 3].mLive & LIVE_ACCU)) + { + mIns[i + 1].mCode = BC_LEA_ABS_INDEX_U8; + mIns[i + 1].mRegister = mIns[i + 0].mRegister; + mIns[i + 1].mRegisterFinal = mIns[i + 0].mRegisterFinal; + mIns[i + 1].mLive &= ~LIVE_ACCU; + + mIns[i + 0].mCode = BC_NOP; + mIns[i + 2].mCode = BC_NOP; + mIns[i + 3].mCode = BC_NOP; + progress = true; + } +#if 1 + else if ( + mIns[i + 0].mCode == BC_STORE_REG_16 && + !mIns[i + 1].ChangesAccu() && !mIns[i + 1].ChangesRegister(mIns[i + 0].mRegister) && + mIns[i + 2].mCode == BC_LOAD_REG_16 && + mIns[i + 3].IsCommutative() && mIns[i + 3].mRegister == mIns[i + 0].mRegister) + { + mIns[i + 2].mCode = mIns[i + 3].mCode; + mIns[i + 3].mCode = BC_NOP; + if (mIns[i + 3].mRegisterFinal && !mIns[i + 1].UsesRegister(mIns[i + 0].mRegister)) + mIns[i + 0].mCode = BC_NOP; + progress = true; + } +#endif +#if 1 + else if ( + mIns[i + 0].mCode == BC_STORE_REG_16 && + mIns[i + 1].mCode == BC_BINOP_MULI8_16 && mIns[i + 1].mRegister == mIns[i + 0].mRegister && + mIns[i + 2].mCode == BC_LOAD_REG_16 && mIns[i + 2].mRegister != mIns[i + 0].mRegister && + mIns[i + 3].IsCommutative() && mIns[i + 3].mRegister == mIns[i + 0].mRegister && mIns[i + 3].mRegisterFinal) + { + mIns[i + 0].mCode = BC_NOP; + mIns[i + 1].mRegister = BC_REG_ACCU; + mIns[i + 1].mLive |= LIVE_ACCU; + mIns[i + 2].mCode = BC_NOP; + mIns[i + 3].mRegister = mIns[i + 2].mRegister; + mIns[i + 3].mRegisterFinal = mIns[i + 2].mRegisterFinal; + progress = true; + } +#endif + } +#endif +#if 1 + if (i + 2 < mIns.Size()) + { + if (mIns[i].mCode == BC_LOAD_LOCAL_16 && + mIns[i + 1].mCode == BC_LOAD_LOCAL_16 && mIns[i + 1].mRegister != BC_REG_ACCU && mIns[i + 1].mRegister != mIns[i].mRegister && + mIns[i + 2].mCode == BC_LOAD_REG_16 && mIns[i + 2].mRegister == mIns[i].mRegister && mIns[i + 2].mRegisterFinal) + { + mIns[i].mRegister = BC_REG_ACCU; + mIns[i + 2].mCode = BC_NOP; + progress = true; + } + else if (mIns[i].mCode == BC_STORE_REG_16 && + !mIns[i + 1].ChangesAccu() && mIns[i + 1].mRegister != mIns[i].mRegister && + mIns[i + 2].mCode == BC_LOAD_REG_16 && mIns[i].mRegister == mIns[i + 2].mRegister) + { + mIns[i + 2].mCode = BC_NOP; + if (mIns[i + 2].mRegisterFinal) + mIns[i].mCode = BC_NOP; + progress = true; + } + else if (mIns[i].mCode == BC_STORE_REG_8 && + !mIns[i + 1].ChangesAccu() && mIns[i + 1].mRegister != mIns[i].mRegister && + mIns[i + 2].mCode == BC_LOAD_REG_8 && mIns[i].mRegister == mIns[i + 2].mRegister) + { + mIns[i + 2].mRegister = BC_REG_ACCU; + if (mIns[i + 2].mRegisterFinal) + mIns[i].mCode = BC_NOP; + progress = true; + } + else if (mIns[i].mCode == BC_STORE_REG_16 && + (mIns[i + 1].mCode == BC_BINOP_ADDI_16 || mIns[i + 1].mCode == BC_BINOP_MULI8_16 || mIns[i + 1].mCode == BC_BINOP_ANDI_16 || mIns[i + 1].mCode == BC_BINOP_ORI_16) && mIns[i + 1].mRegister == mIns[i].mRegister && + mIns[i + 2].mCode == BC_LOAD_REG_16 && mIns[i].mRegister == mIns[i + 2].mRegister) + { + mIns[i + 0].mCode = BC_NOP; + mIns[i + 1].mRegister = BC_REG_ACCU; + if (mIns[i + 2].mRegisterFinal) + mIns[i + 2].mCode = BC_NOP; + else + mIns[i + 2].mCode = BC_STORE_REG_16; + + progress = true; + } + else if (mIns[i].mCode == BC_STORE_REG_32 && + !mIns[i + 1].ChangesAccu() && mIns[i + 1].mRegister != mIns[i].mRegister && + mIns[i + 2].mCode == BC_LOAD_REG_32 && mIns[i].mRegister == mIns[i + 2].mRegister) + { + mIns[i + 2].mCode = BC_NOP; + if (mIns[i + 2].mRegisterFinal) + mIns[i].mCode = BC_NOP; + progress = true; + } + else if (mIns[i].mCode == BC_STORE_REG_16 && + !mIns[i + 1].ChangesAccu() && mIns[i + 1].mRegister != mIns[i].mRegister && + mIns[i + 2].IsStore() && mIns[i].mRegister == mIns[i + 2].mRegister && mIns[i + 2].mRegisterFinal) + { + mIns[i].mCode = BC_NOP; + mIns[i + 2].mRegister = BC_REG_ACCU; + progress = true; + } + else if (mIns[i].mCode == BC_STORE_REG_16 && + !mIns[i + 1].ChangesAddr() && !mIns[i + 1].UsesAddr() && mIns[i + 1].mRegister != mIns[i].mRegister && + mIns[i + 2].mCode == BC_ADDR_REG && mIns[i].mRegister == mIns[i + 2].mRegister && mIns[i + 2].mRegisterFinal) + { + mIns[i].mCode = BC_ADDR_REG; + mIns[i].mRegister = BC_REG_ACCU; + mIns[i + 2].mCode = BC_NOP; + progress = true; + } + else if ( + mIns[i + 2].mCode == BC_BINOP_ADDR_16 && + mIns[i + 1].mCode == BC_LOAD_REG_16 && mIns[i + 1].mRegister != mIns[i + 2].mRegister && + mIns[i + 0].LoadsRegister(mIns[i + 2].mRegister) && mIns[i + 2].mRegisterFinal) + { + mIns[i + 0].mRegister = BC_REG_ACCU; + mIns[i + 1].mCode = mIns[i + 2].mCode; + mIns[i + 2].mCode = BC_NOP; + progress = true; + } + else if ( + mIns[i + 2].mCode == BC_BINOP_ADDR_16 && + mIns[i + 1].mCode == BC_LOAD_REG_16 && mIns[i + 1].mRegister != mIns[i + 2].mRegister && + mIns[i + 0].mCode == BC_STORE_REG_16 && mIns[i + 0].mRegister == mIns[i + 2].mRegister && mIns[i + 2].mRegisterFinal) + { + mIns[i + 0].mCode = BC_NOP;; + mIns[i + 1].mCode = mIns[i + 2].mCode; + mIns[i + 2].mCode = BC_NOP; + progress = true; + } + else if (mIns[i].mCode == BC_STORE_REG_32 && + mIns[i + 1].mCode == BC_LOAD_REG_32 && mIns[i + 1].mRegister != mIns[i + 2].mRegister && + mIns[i + 2].IsCommutative() && mIns[i].mRegister == mIns[i + 2].mRegister) + { + if (mIns[i + 2].mRegisterFinal) + mIns[i + 0].mCode = BC_NOP; + mIns[i + 1].mCode = BC_NOP; + mIns[i + 2].mRegister = mIns[i + 1].mRegister; + mIns[i + 2].mRegisterFinal = mIns[i + 1].mRegisterFinal; + progress = true; + } + else if (mIns[i].mCode == BC_STORE_REG_32 && + mIns[i + 1].LoadsRegister(BC_REG_ACCU) && + mIns[i + 2].IsCommutative() && mIns[i].mRegister == mIns[i + 2].mRegister && mIns[i + 2].mRegisterFinal) + { + mIns[i + 0].mCode = BC_NOP; + mIns[i + 1].mRegister = mIns[i + 2].mRegister; + progress = true; + } + else if (mIns[i].mCode == BC_STORE_REG_16 && + mIns[i + 1].mCode == BC_LOAD_REG_16 && mIns[i + 1].mRegister != mIns[i + 2].mRegister && + mIns[i + 2].IsCommutative() && mIns[i].mRegister == mIns[i + 2].mRegister) + { + if (mIns[i + 2].mRegisterFinal) + mIns[i + 0].mCode = BC_NOP; + mIns[i + 1].mCode = BC_NOP; + mIns[i + 2].mRegister = mIns[i + 1].mRegister; + mIns[i + 2].mRegisterFinal = mIns[i + 1].mRegisterFinal; + progress = true; + } + else if (mIns[i + 0].mCode == BC_STORE_REG_32 && + mIns[i + 2].mCode == BC_BINOP_CMP_F32 && mIns[i + 2].mRegister == mIns[i + 0].mRegister && mIns[i + 2].mRegisterFinal && + mIns[i + 1].LoadsRegister(BC_REG_ACCU) && i + 3 == mIns.Size()) + { + mIns[i + 1].mRegister = mIns[i + 0].mRegister; + mIns[i + 0].mCode = BC_NOP; + mBranch = TransposeBranchCondition(mBranch); + progress = true; + } + else if (mIns[i + 0].mCode == BC_LOAD_REG_16 && + mIns[i + 1].mCode == BC_STORE_REG_16 && + mIns[i + 2].mCode == BC_LOAD_REG_16 && mIns[i + 0].mRegister == mIns[i + 2].mRegister) + { + mIns[i + 2].mCode = BC_NOP; + progress = true; + } + else if (mIns[i + 0].mCode == BC_LOAD_REG_8 && + mIns[i + 1].mCode == BC_STORE_REG_8 && + mIns[i + 2].mCode == BC_LOAD_REG_8 && mIns[i + 0].mRegister == mIns[i + 2].mRegister) + { + mIns[i + 2].mCode = BC_NOP; + progress = true; + } + else if (mIns[i + 0].mCode == BC_CONST_16 && mIns[i + 2].mCode == BC_CONST_16 && + !mIns[i + 0].mRelocate && !mIns[i + 2].mRelocate && + mIns[i + 0].mRegister == mIns[i + 2].mRegister && mIns[i + 0].mValue == mIns[i + 2].mValue && + !mIns[i + 1].ChangesRegister(mIns[i + 0].mRegister)) + { + if (mIns[i + 0].mRegister == BC_REG_ACCU) + mIns[i + 1].mLive |= LIVE_ACCU; + mIns[i + 2].mCode = BC_NOP; + progress = true; + } + else if (mIns[i + 0].IsLocalStore() && mIns[i + 2].IsSame(mIns[i + 0]) && !mIns[i + 1].IsLocalAccess() && mIns[i + 1].mCode != BC_JSR) + { + mIns[i + 0].mCode = BC_NOP; + progress = true; + } + else if ( + mIns[i + 0].mCode == BC_STORE_REG_16 && + mIns[i + 1].mCode == BC_LOAD_LOCAL_16 && mIns[i + 1].mRegister == BC_REG_ACCU && + mIns[i + 2].IsCommutative() && mIns[i].mRegister == mIns[i + 2].mRegister && mIns[i + 2].mRegisterFinal) + { + mIns[i + 0].mCode = BC_NOP; + mIns[i + 1].mRegister = mIns[i + 0].mRegister; + progress = true; + } + else if ( + mIns[i + 0].mCode == BC_LOAD_REG_8 && + mIns[i + 1].mCode == BC_STORE_REG_8 && + mIns[i + 2].mCode == BC_LOAD_REG_8 && mIns[i + 1].mRegister == mIns[i + 2].mRegister && mIns[i + 2].mRegisterFinal) + { + mIns[i + 1].mCode = BC_NOP; + mIns[i + 2].mCode = BC_NOP; + progress = true; + } + else if ( + mIns[i + 0].mCode == BC_STORE_REG_8 && + mIns[i + 1].mCode == BC_LOAD_REG_8 && mIns[i + 0].mRegister == mIns[i + 1].mRegister && + mIns[i + 2].mCode == BC_STORE_REG_8) + { + if (mIns[i + 1].mRegisterFinal) + mIns[i + 0].mCode = BC_NOP; + mIns[i + 1].mCode = BC_NOP; + progress = true; + } + else if ( + mIns[i + 0].mCode == BC_STORE_REG_16 && + mIns[i + 1].mCode == BC_LOAD_REG_8 && mIns[i + 0].mRegister == mIns[i + 1].mRegister && + mIns[i + 2].mCode == BC_STORE_REG_8) + { + if (mIns[i + 1].mRegisterFinal) + mIns[i + 0].mCode = BC_NOP; + mIns[i + 1].mCode = BC_NOP; + progress = true; + } +#if 1 + else if ( + mIns[i + 0].mCode == BC_STORE_REG_16 && + mIns[i + 1].LoadsRegister(BC_REG_ACCU) && + mIns[i + 2].IsCommutative() && mIns[i].mRegister == mIns[i + 2].mRegister && mIns[i + 2].mRegisterFinal) + { + mIns[i + 0].mCode = BC_NOP; + mIns[i + 1].mRegister = mIns[i + 0].mRegister; + progress = true; + } +#endif +#if 1 + else if ( + mIns[i + 0].mCode == BC_STORE_REG_16 && + mIns[i + 1].mCode == BC_LOAD_REG_16 && + mIns[i + 2].mCode == BC_BINOP_SUBR_16 && mIns[i].mRegister == mIns[i + 2].mRegister && mIns[i + 2].mRegisterFinal) + { + mIns[i + 0].mCode = BC_OP_NEGATE_16; + mIns[i + 1].mCode = BC_BINOP_ADDR_16; + mIns[i + 2].mCode = BC_NOP; + progress = true; + } +#endif + else if ( + mIns[i + 0].mCode == BC_LEA_ABS && mIns[i + 0].mRegister == BC_REG_ACCU && + mIns[i + 1].mCode == BC_BINOP_ADDR_16 && + mIns[i + 2].mCode == BC_ADDR_REG && mIns[i + 2].mRegister == BC_REG_ACCU && !(mIns[i + 2].mLive & LIVE_ACCU)) + { + mIns[i + 0].mCode = BC_LEA_ABS_INDEX; + mIns[i + 0].mRegister = mIns[i + 1].mRegister; + mIns[i + 0].mRegisterFinal = mIns[i + 1].mRegisterFinal; + mIns[i + 0].mLive &= ~LIVE_ACCU; + mIns[i + 1].mCode = BC_NOP; + mIns[i + 2].mCode = BC_NOP; + progress = true; + } + + else if ( + mIns[i + 0].mCode == BC_LOAD_REG_8 && + mIns[i + 1].mCode == BC_STORE_REG_16 && + mIns[i + 2].mCode == BC_LEA_ABS_INDEX && mIns[i + 2].mRegister == mIns[i + 1].mRegister && !(mIns[i + 2].mLive & LIVE_ACCU) && mIns[i + 2].mRegisterFinal) + { + mIns[i + 2].mCode = BC_LEA_ABS_INDEX_U8; + mIns[i + 2].mRegister = mIns[i + 0].mRegister; + mIns[i + 2].mRegisterFinal = mIns[i + 0].mRegisterFinal; + mIns[i + 0].mCode = BC_NOP; + mIns[i + 1].mCode = BC_NOP; + progress = true; + } + + else if ( + mIns[i + 0].mCode == BC_STORE_REG_16 && + (mIns[i + 1].mCode == BC_BINOP_MULI8_16 || mIns[i + 1].mCode == BC_BINOP_ADDI_16) && mIns[i + 1].mRegister == mIns[i + 0].mRegister && + (mIns[i + 2].mCode == BC_STORE_ABS_16 || mIns[i + 2].mCode == BC_STORE_LOCAL_16 || mIns[i + 2].mCode == BC_STORE_FRAME_16 || mIns[i + 2].mCode == BC_STORE_ADDR_16) && mIns[i + 2].mRegister == mIns[i + 0].mRegister && + mIns[i + 2].mRegisterFinal && !(mIns[i + 2].mLive & LIVE_ACCU)) + { + mIns[i + 0].mCode = BC_NOP; + mIns[i + 1].mRegister = BC_REG_ACCU; + mIns[i + 2].mRegister = BC_REG_ACCU; + mIns[i + 1].mLive |= LIVE_ACCU; + progress = true; + } + + else if ( + mIns[i + 0].mCode == BC_LOAD_REG_8 && + mIns[i + 1].mCode == BC_STORE_REG_16 && + mIns[i + 2].mCode == BC_BINOP_ANDI_16 && mIns[i + 2].mRegister == mIns[i + 1].mRegister && mIns[i + 2].mValue == 0x00ff) + { + mIns[i + 2].mCode = BC_STORE_REG_16; + mIns[i + 1].mCode = BC_NOP; + mIns[i + 0].mLive |= LIVE_ACCU; + progress = true; + } + + else if ( + mIns[i + 0].mCode == BC_STORE_REG_16 && + mIns[i + 1].mCode == BC_BINOP_ADDI_16 && mIns[i + 0].mRegister == mIns[i + 1].mRegister && + mIns[i + 2].mCode == BC_LOAD_REG_8 && mIns[i + 2].mRegister == mIns[i + 0].mRegister && mIns[i + 2].mRegisterFinal) + { + mIns[i + 0].mCode = BC_LOAD_REG_8; + mIns[i + 0].mRegister = BC_REG_ACCU; + mIns[i + 0].mLive |= LIVE_ACCU; + + mIns[i + 1].mCode = BC_BINOP_ADDI_8; + mIns[i + 1].mRegister = BC_REG_ACCU; + mIns[i + 1].mLive |= LIVE_ACCU; + + mIns[i + 2].mCode = BC_NOP; + + progress = true; + } + else if ( + mIns[i + 0].mCode == BC_LOAD_REG_8 && + mIns[i + 1].mCode == BC_BINOP_SHRI_U16 && + mIns[i + 2].mCode == BC_LOAD_REG_8 && mIns[i + 2].mRegister == BC_REG_ACCU) + { + mIns[i + 2].mCode = BC_NOP; + progress = true; + } + else if ( + mIns[i + 0].mCode == BC_LEA_ABS && + mIns[i + 1].mCode == BC_BINOP_ADDR_16 && mIns[i + 0].mRegister == mIns[i + 1].mRegister && mIns[i + 1].mRegisterFinal && + mIns[i + 2].mCode == BC_ADDR_REG && mIns[i + 2].mRegister == BC_REG_ACCU && !(mIns[i + 2].mLive & LIVE_ACCU)) + { + mIns[i + 0].mCode = BC_LEA_ABS_INDEX; + mIns[i + 0].mRegister = BC_REG_ACCU; + mIns[i + 1].mCode = BC_NOP; + mIns[i + 2].mCode = BC_NOP; + progress = true; + } + else if ( + mIns[i + 0].mCode == BC_LEA_ABS && mIns[i + 0].mRegister == BC_REG_ACCU && + mIns[i + 1].mCode == BC_BINOP_ADDA_16 && + mIns[i + 2].mCode == BC_ADDR_REG && mIns[i + 1].mRegister == mIns[i + 2].mRegister && mIns[i + 2].mRegisterFinal && !(mIns[i + 2].mLive & LIVE_ACCU)) + { + mIns[i + 0].mCode = BC_LEA_ABS_INDEX; + mIns[i + 0].mRegister = mIns[i + 2].mRegister; + mIns[i + 0].mRegisterFinal = true; + mIns[i + 1].mCode = BC_NOP; + mIns[i + 2].mCode = BC_NOP; + progress = true; + } + else if ( + mIns[i + 0].mCode == BC_LOAD_REG_16 && + mIns[i + 1].mCode == BC_BINOP_ADDR_16 && + mIns[i + 2].mCode == BC_STORE_REG_16 && mIns[i + 0].mRegister == mIns[i + 2].mRegister && !(mIns[i + 2].mLive & LIVE_ACCU)) + { + mIns[i + 0].mRegister = mIns[i + 1].mRegister; + mIns[i + 0].mRegisterFinal = mIns[i + 1].mRegisterFinal; + mIns[i + 1].mCode = BC_NOP; + mIns[i + 2].mCode = BC_BINOP_ADDA_16; + progress = true; + } + else if ( + mIns[i + 0].mCode == BC_LOAD_REG_16 && + mIns[i + 1].mCode == BC_BINOP_SHLI_16 && mIns[i + 1].mValue == 1 && + mIns[i + 2].mCode == BC_STORE_REG_16 && mIns[i + 0].mRegister == mIns[i + 2].mRegister && !(mIns[i + 2].mLive & LIVE_ACCU)) + { + mIns[i + 0].mRegisterFinal = false; + mIns[i + 1].mCode = BC_NOP; + mIns[i + 2].mCode = BC_BINOP_ADDA_16; + progress = true; + } + else if ( + (mIns[i + 0].mCode == BC_LOAD_LOCAL_16 || mIns[i + 0].mCode == BC_LOAD_ABS_16 || mIns[i + 0].mCode == BC_LOAD_ADDR_16 || + mIns[i + 0].mCode == BC_LOAD_LOCAL_U8 || mIns[i + 0].mCode == BC_LOAD_ABS_U8 || mIns[i + 0].mCode == BC_LOAD_ADDR_U8) && + mIns[i + 1].mCode == BC_BINOP_ADDR_16 && mIns[i + 0].mRegister == mIns[i + 1].mRegister && mIns[i + 1].mRegisterFinal && + mIns[i + 2].mCode == BC_STORE_REG_16 && !(mIns[i + 2].mLive & LIVE_ACCU)) + { + mIns[i + 0].mRegister = mIns[i + 2].mRegister; + mIns[i + 1].mCode = BC_NOP; + mIns[i + 2].mCode = BC_BINOP_ADDA_16; + progress = true; + } + else if ( + mIns[i + 0].mCode == BC_STORE_REG_16 && + mIns[i + 2].mCode == BC_LEA_ABS_INDEX && mIns[i + 0].mRegister == mIns[i + 2].mRegister && mIns[i + 2].mRegisterFinal && + (mIns[i + 1].mCode == BC_BINOP_ADDI_16 || mIns[i + 1].mCode == BC_BINOP_ANDI_16 || mIns[i + 1].mCode == BC_BINOP_ORI_16 || mIns[i + 1].mCode == BC_BINOP_MULI8_16) && + mIns[i + 1].mRegister == mIns[i + 0].mRegister && !(mIns[i + 2].mLive & LIVE_ACCU)) + { + mIns[i + 0].mCode = BC_NOP; + mIns[i + 1].mRegister = BC_REG_ACCU; + mIns[i + 2].mRegister = BC_REG_ACCU; + mIns[i + 1].mLive |= LIVE_ACCU; + progress = true; + } + + else if ( + mIns[i + 0].mCode == BC_LEA_ABS && + mIns[i + 1].mCode == BC_BINOP_ADDA_16 && mIns[i + 0].mRegister == mIns[i + 1].mRegister && + mIns[i + 2].mCode == BC_ADDR_REG && mIns[i + 0].mRegister == mIns[i + 2].mRegister && mIns[i + 2].mRegisterFinal) + { + mIns[i + 0].mCode = BC_LEA_ABS_INDEX; + mIns[i + 0].mRegister = BC_REG_ACCU; + mIns[i + 1].mCode = BC_NOP; + mIns[i + 2].mCode = BC_NOP; + progress = true; + } + +#if 1 + else if ( + mIns[i + 0].mCode == BC_LOAD_REG_8 && + mIns[i + 1].mCode == BC_LEA_ABS && + mIns[i + 2].mCode == BC_LEA_ACCU_INDEX && mIns[i + 1].mRegister == mIns[i + 2].mRegister && mIns[i + 2].mRegisterFinal && !(mIns[i + 2].mLive & LIVE_ACCU)) + { + mIns[i + 0].mCode = BC_NOP; + mIns[i + 1].mCode = BC_LEA_ABS_INDEX_U8; + mIns[i + 2].mCode = BC_NOP; + mIns[i + 1].mRegister = mIns[i + 0].mRegister; + progress = true; + } +#endif + else if ( + mIns[i + 0].mCode == BC_BINOP_ADDI_8 && mIns[i + 0].mValue == 1 && + mIns[i + 1].mCode == BC_LOAD_REG_8 && mIns[i + 1].mRegister == mIns[i + 0].mRegister && + mIns[i + 2].mCode == BC_BINOP_CMPUI_8) + { + mIns[i + 2].mCode = BC_LOOP_U8; + mIns[i + 2].mRegister = mIns[i + 0].mRegister; + mIns[i + 0].mCode = BC_NOP; + mIns[i + 1].mCode = BC_NOP; + progress = true; + } + +#if 1 + else if ( + i + 3 == mIns.Size() && mFalseJump && + mIns[i + 0].mCode == BC_STORE_REG_8 && + mIns[i + 1].mCode == BC_LOAD_REG_8 && + mIns[i + 2].mCode == BC_BINOP_CMPUR_8 && mIns[i + 0].mRegister == mIns[i + 2].mRegister && !(mExitLive & LIVE_ACCU) && mIns[i + 2].mRegisterFinal + ) + { + mIns[i + 0].mCode = BC_NOP; + mIns[i + 1].mCode = BC_NOP; + mIns[i + 2].mRegister = mIns[i + 1].mRegister; + mIns[i + 2].mRegisterFinal = mIns[i + 1].mRegisterFinal; + mBranch = TransposeBranchCondition(mBranch); + progress = true; + } + else if ( + i + 3 == mIns.Size() && mFalseJump && + mIns[i + 0].mCode == BC_STORE_REG_16 && + mIns[i + 1].mCode == BC_LOAD_REG_16 && + mIns[i + 2].mCode == BC_BINOP_CMPUR_16 && mIns[i + 0].mRegister == mIns[i + 2].mRegister && !(mExitLive & LIVE_ACCU) && mIns[i + 2].mRegisterFinal + ) + { + mIns[i + 0].mCode = BC_NOP; + mIns[i + 1].mCode = BC_NOP; + mIns[i + 2].mRegister = mIns[i + 1].mRegister; + mIns[i + 2].mRegisterFinal = mIns[i + 1].mRegisterFinal; + mBranch = TransposeBranchCondition(mBranch); + progress = true; + } + +#endif + + } +#endif +#if 1 + if (i + 1 < mIns.Size()) + { + if (mIns[i].mCode == BC_STORE_REG_16 && mIns[i + 1].mCode == BC_LOAD_REG_16 && mIns[i].mRegister == mIns[i + 1].mRegister) + { + mIns[i + 1].mCode = BC_NOP; + if (mIns[i + 1].mRegisterFinal) + mIns[i].mCode = BC_NOP; + progress = true; + } + else if (mIns[i].mCode == BC_LOAD_REG_16 && mIns[i + 1].mCode == BC_STORE_REG_16 && mIns[i].mRegister == mIns[i + 1].mRegister) + { + mIns[i + 1].mCode = BC_NOP; + progress = true; + } + else if (mIns[i].mCode == BC_STORE_REG_32 && mIns[i + 1].mCode == BC_LOAD_REG_32 && mIns[i].mRegister == mIns[i + 1].mRegister) + { + mIns[i + 1].mCode = BC_NOP; + if (mIns[i + 1].mRegisterFinal) + mIns[i].mCode = BC_NOP; + progress = true; + } + else if (mIns[i].mCode == BC_LOAD_REG_32 && mIns[i + 1].mCode == BC_STORE_REG_32 && mIns[i].mRegister == mIns[i + 1].mRegister) + { + mIns[i + 1].mCode = BC_NOP; + progress = true; + } + else if (mIns[i].mCode == BC_STORE_REG_16 && mIns[i + 1].mCode == BC_ADDR_REG && mIns[i].mRegister == mIns[i + 1].mRegister && mIns[i + 1].mRegisterFinal) + { + mIns[i].mCode = BC_ADDR_REG; + mIns[i].mRegister = BC_REG_ACCU; + mIns[i + 1].mCode = BC_NOP; + progress = true; + } + else if (mIns[i + 0].mCode == BC_LOAD_REG_16 && mIns[i + 1].mCode == BC_ADDR_REG && mIns[i + 1].mRegister == BC_REG_ACCU && !(mIns[i + 1].mLive & LIVE_ACCU)) + { + mIns[i + 0].mCode = BC_ADDR_REG; + mIns[i + 1].mCode = BC_NOP; + progress = true; + } + else if (mIns[i + 1].mCode == BC_LOAD_REG_16 && mIns[i].LoadsRegister(mIns[i + 1].mRegister) && mIns[i + 1].mRegisterFinal) + { + mIns[i].mRegister = BC_REG_ACCU; + mIns[i + 1].mCode = BC_NOP; + progress = true; + } + else if (mIns[i + 1].mCode == BC_LOAD_REG_32 && mIns[i].LoadsRegister(mIns[i + 1].mRegister) && mIns[i + 1].mRegisterFinal) + { + mIns[i].mRegister = BC_REG_ACCU; + mIns[i + 1].mCode = BC_NOP; + progress = true; + } + else if (mIns[i].mCode == BC_STORE_REG_16 && mIns[i + 1].StoresRegister(mIns[i].mRegister) && mIns[i + 1].mRegisterFinal) + { + mIns[i + 1].mRegister = BC_REG_ACCU; + mIns[i].mCode = BC_NOP; + progress = true; + } + else if (mIns[i].mCode == BC_STORE_REG_32 && mIns[i + 1].StoresRegister(mIns[i].mRegister) && mIns[i + 1].mRegisterFinal) + { + mIns[i + 1].mRegister = BC_REG_ACCU; + mIns[i].mCode = BC_NOP; + progress = true; + } + else if (mIns[i + 1].mCode == BC_LOAD_REG_32 && mIns[i].LoadsRegister(mIns[i + 1].mRegister) && mIns[i + 1].mRegisterFinal) + { + mIns[i].mRegister = BC_REG_ACCU; + mIns[i + 1].mCode = BC_NOP; + progress = true; + } + else if (mIns[i].mCode == BC_LOAD_LOCAL_16 && mIns[i + 1].mCode == BC_ADDR_REG && mIns[i].mRegister == mIns[i + 1].mRegister && mIns[i + 1].mRegisterFinal) + { + mIns[i].mRegister = BC_REG_ADDR; + mIns[i + 1].mCode = BC_NOP; + progress = true; + } + else if ( + (mIns[i + 0].mCode == BC_LEA_FRAME || mIns[i + 0].mCode == BC_LEA_LOCAL || mIns[i + 0].mCode == BC_LEA_ABS) && + mIns[i + 1].mCode == BC_ADDR_REG && mIns[i + 0].mRegister == mIns[i + 1].mRegister && mIns[i + 1].mRegisterFinal) + { + mIns[i + 0].mRegister = BC_REG_ADDR; + mIns[i + 1].mCode = BC_NOP; + progress = true; + } + else if (mIns[i].mCode == BC_BINOP_ADDI_16 && mIns[i + 1].mCode == BC_BINOP_ADDI_16 && mIns[i].mRegister == mIns[i + 1].mRegister) + { + mIns[i + 1].mValue += mIns[i].mValue; + mIns[i].mCode = BC_NOP; + progress = true; + } + else if (mIns[i].mCode == BC_BINOP_ADDI_16 && mIns[i].mValue == 0) + { + mIns[i].mCode = BC_NOP; + progress = true; + } +#if 1 + else if (mIns[i].mCode == BC_STORE_REG_8 && mIns[i + 1].StoresRegister(mIns[i].mRegister) && mIns[i + 1].mRegisterFinal) + { + mIns[i + 1].mRegister = BC_REG_ACCU; + mIns[i].mCode = BC_NOP; + progress = true; + } + else if (mIns[i].mCode == BC_STORE_REG_8 && mIns[i + 1].mCode == BC_LOAD_REG_8 && mIns[i].mRegister == mIns[i + 1].mRegister && mIns[i + 1].mRegisterFinal) + { + mIns[i + 1].mRegister = BC_REG_ACCU; + mIns[i].mCode = BC_NOP; + progress = true; + } + else if (mIns[i].mCode == BC_LOAD_REG_16 && mIns[i + 1].mCode == BC_LOAD_REG_8 && mIns[i + 1].mRegister == BC_REG_ACCU) + { + mIns[i].mCode = BC_LOAD_REG_8; + mIns[i + 1].mCode = BC_NOP; + progress = true; + } + else if ((mIns[i].mCode == BC_CONST_16 || mIns[i].mCode == BC_CONST_P8 || mIns[i].mCode == BC_CONST_N8) && + (mIns[i + 1].mCode == BC_CONST_16 || mIns[i + 1].mCode == BC_CONST_P8 || mIns[i + 1].mCode == BC_CONST_N8 || mIns[i + 1].mCode == BC_CONST_32) && mIns[i].mRegister == mIns[i + 1].mRegister) + { + mIns[i].mCode = BC_NOP; + progress = true; + } + else if (mIns[i].mCode == BC_CONST_32 && mIns[i + 1].mCode == BC_CONST_32 && mIns[i].mRegister == mIns[i + 1].mRegister) + { + mIns[i].mCode = BC_NOP; + progress = true; + } + else if (mIns[i].mCode == BC_CONST_8 && mIns[i + 1].mCode == BC_CONST_8 && mIns[i].mRegister == mIns[i + 1].mRegister) + { + mIns[i].mCode = BC_NOP; + progress = true; + } +#endif +#if 1 + else if (mIns[i + 1].mCode == BC_LOAD_REG_8 && mIns[i + 0].mCode == BC_LOAD_ADDR_8 && mIns[i + 1].mRegisterFinal && mIns[i + 0].mRegister == mIns[i + 1].mRegister) + { + mIns[i + 0].mCode = BC_LOAD_ADDR_U8; + mIns[i + 0].mRegister = BC_REG_ACCU; + mIns[i + 1].mCode = BC_NOP; + progress = true; + } + else if (mIns[i + 1].mCode == BC_LOAD_REG_8 && mIns[i + 0].mCode == BC_LOAD_ABS_8 && mIns[i + 1].mRegisterFinal && mIns[i + 0].mRegister == mIns[i + 1].mRegister) + { + mIns[i + 0].mCode = BC_LOAD_ABS_U8; + mIns[i + 0].mRegister = BC_REG_ACCU; + mIns[i + 1].mCode = BC_NOP; + progress = true; + } + else if ( + mIns[i + 0].mCode == BC_BINOP_ANDI_16 && mIns[i + 0].mValue == 0x00ff && + mIns[i + 1].mCode == BC_LOAD_REG_16 && mIns[i + 1].mRegister == mIns[i + 0].mRegister) + { + mIns[i + 0].mCode = BC_LOAD_REG_8; + mIns[i + 0].mLive |= LIVE_ACCU; + + if (!mIns[i + 1].mRegisterFinal) + mIns[i + 1].mCode = BC_STORE_REG_16; + else + mIns[i + 1].mCode = BC_NOP; + progress = true; + } + else if ( + mIns[i + 0].mCode == BC_LOAD_REG_8 && + mIns[i + 1].mCode == BC_LOAD_REG_8 && mIns[i + 1].mRegister == BC_REG_ACCU) + { + mIns[i + 1].mCode = BC_NOP; + progress = true; + } + else if ( + mIns[i + 0].mCode == BC_LOAD_REG_8 && + mIns[i + 1].mCode == BC_BINOP_SHRI_I16) + { + mIns[i + 1].mCode = BC_BINOP_SHRI_U16; + progress = true; + } + else if ( + (mIns[i + 0].mCode == BC_STORE_REG_16 || mIns[i + 0].mCode == BC_STORE_REG_8) && + mIns[i + 1].mCode == BC_LOAD_REG_8 && mIns[i + 1].mRegister == mIns[i + 0].mRegister) + { + mIns[i + 1].mRegister = BC_REG_ACCU; + if (mIns[i + 1].mRegisterFinal) + mIns[i + 0].mCode = BC_NOP; + progress = true; + } + else if ( + mIns[i + 0].mCode == BC_STORE_REG_16 && + (mIns[i + 1].mCode == BC_STORE_LOCAL_16 || mIns[i + 1].mCode == BC_STORE_ABS_16 || mIns[i + 1].mCode == BC_STORE_ADDR_16 || mIns[i + 1].mCode == BC_STORE_FRAME_16) && + mIns[i + 1].mRegister == mIns[i + 0].mRegister && mIns[i + 1].mRegisterFinal) + { + mIns[i + 1].mRegister = BC_REG_ACCU; + mIns[i + 0].mCode = BC_NOP; + progress = true; + } + + if ((mIns[i].mCode == BC_LOAD_ABS_U8 || mIns[i].mCode == BC_LOAD_ADDR_U8 || mIns[i].mCode == BC_LOAD_ABS_16 || mIns[i].mCode == BC_LOAD_ADDR_16) && mIns[i].mRegister == BC_REG_ACCU && + mIns[i + 1].mCode == BC_STORE_REG_16 && !(mIns[i + 1].mLive & LIVE_ACCU)) + { + mIns[i].mRegister = mIns[i + 1].mRegister; + mIns[i + 1].mCode = BC_NOP; + progress = true; + } +#endif + else if ( + mIns[i + 1].mCode == BC_STORE_REG_8 && !(mIns[i + 1].mLive & LIVE_ACCU) && mIns[i + 1].mRegister != BC_REG_ADDR && + (mIns[i + 0].mCode == BC_LOAD_ADDR_8 || mIns[i + 0].mCode == BC_LOAD_ADDR_U8 || mIns[i + 0].mCode == BC_LOAD_ADDR_16 || mIns[i + 0].mCode == BC_LOAD_ADDR_32) && + mIns[i + 0].mRegister == BC_REG_ACCU) + { + mIns[i + 0].mCode = BC_LOAD_ADDR_8; + mIns[i + 0].mRegister = mIns[i + 1].mRegister; + mIns[i + 1].mCode = BC_NOP; + progress = true; + } + else if ( + mIns[i + 1].mCode == BC_STORE_REG_8 && !(mIns[i + 1].mLive & LIVE_ACCU) && mIns[i + 1].mRegister != BC_REG_ADDR && + (mIns[i + 0].mCode == BC_LOAD_ABS_8 || mIns[i + 0].mCode == BC_LOAD_ABS_U8 || mIns[i + 0].mCode == BC_LOAD_ABS_16 || mIns[i + 0].mCode == BC_LOAD_ABS_32) && + mIns[i + 0].mRegister == BC_REG_ACCU) + { + mIns[i + 0].mCode = BC_LOAD_ABS_8; + mIns[i + 0].mRegister = mIns[i + 1].mRegister; + mIns[i + 1].mCode = BC_NOP; + progress = true; + } + else if ( + mIns[i + 1].mCode == BC_STORE_REG_8 && !(mIns[i + 1].mLive & LIVE_ACCU) && mIns[i + 1].mRegister != BC_REG_ADDR && + (mIns[i + 0].mCode == BC_LOAD_LOCAL_8 || mIns[i + 0].mCode == BC_LOAD_LOCAL_U8 || mIns[i + 0].mCode == BC_LOAD_LOCAL_16 || mIns[i + 0].mCode == BC_LOAD_LOCAL_32) && + mIns[i + 0].mRegister == BC_REG_ACCU) + { + mIns[i + 0].mCode = BC_LOAD_LOCAL_8; + mIns[i + 0].mRegister = mIns[i + 1].mRegister; + mIns[i + 1].mCode = BC_NOP; + progress = true; + } + else if ( + mIns[i + 0].mCode == BC_LOAD_REG_8 && !(mIns[i + 1].mLive & LIVE_ACCU) && + (mIns[i + 1].mCode == BC_STORE_ABS_8 || mIns[i + 1].mCode == BC_STORE_LOCAL_8 || mIns[i + 1].mCode == BC_STORE_ADDR_8 || mIns[i + 1].mCode == BC_STORE_FRAME_8) && + mIns[i + 1].mRegister == BC_REG_ACCU) + { + mIns[i + 0].mCode = BC_NOP; + mIns[i + 1].mRegister = mIns[i + 0].mRegister; + mIns[i + 1].mRegisterFinal = mIns[i + 0].mRegisterFinal; + progress = true; + } + +#if 1 + else if ( + mIns[i + 0].mCode == BC_CONST_16 && mIns[i + 0].mRegister == BC_REG_ACCU && + mIns[i + 1].mCode == BC_STORE_REG_8 && !(mIns[i + 1].mLive & LIVE_ACCU)) + { + mIns[i + 0].mCode = BC_CONST_8; + mIns[i + 0].mRegister = mIns[i + 1].mRegister; + mIns[i + 1].mCode = BC_NOP; + progress = true; + } +#endif + else if ( + mIns[i + 0].mCode == BC_CONST_16 && mIns[i + 0].mRegister == BC_REG_ACCU && + mIns[i + 1].mCode == BC_STORE_REG_16 && !(mIns[i + 1].mLive & LIVE_ACCU)) + { + mIns[i + 0].mRegister = mIns[i + 1].mRegister; + mIns[i + 1].mCode = BC_NOP; + progress = true; + } + else if ( + (mIns[i + 0].mCode == BC_LEA_LOCAL || mIns[i + 0].mCode == BC_LEA_ABS || mIns[i + 0].mCode == BC_LEA_FRAME) && mIns[i + 0].mRegister == BC_REG_ACCU && + mIns[i + 1].mCode == BC_STORE_REG_16 && !(mIns[i + 1].mLive & LIVE_ACCU)) + { + mIns[i + 0].mRegister = mIns[i + 1].mRegister; + mIns[i + 1].mCode = BC_NOP; + progress = true; + } +#if 1 + else if ( + mIns[i + 0].mCode == BC_BINOP_ADDR_16 && + mIns[i + 1].mCode == BC_ADDR_REG && mIns[i + 1].mRegister == BC_REG_ACCU && !(mIns[i + 1].mLive & LIVE_ACCU)) + { + mIns[i + 0].mCode = BC_LEA_ACCU_INDEX; + mIns[i + 1].mCode = BC_NOP; + progress = true; + } +#endif +#if 1 + else if ( + mIns[i + 0].mCode == BC_BINOP_ADDA_16 && + mIns[i + 1].mCode == BC_ADDR_REG && mIns[i + 1].mRegister == mIns[i + 0].mRegister && mIns[i + 1].mRegisterFinal) + { + mIns[i + 0].mCode = BC_NOP; + mIns[i + 1].mCode = BC_LEA_ACCU_INDEX; + progress = true; + } +#endif + else if ( + mIns[i + 0].mCode == BC_LOAD_LOCAL_8 && + mIns[i + 1].mCode == BC_LOAD_REG_8 && mIns[i + 1].mRegister == mIns[i + 0].mRegister && mIns[i + 1].mRegisterFinal) + { + mIns[i + 0].mCode = BC_LOAD_LOCAL_U8; + mIns[i + 0].mRegister = BC_REG_ACCU; + mIns[i + 0].mLive |= LIVE_ACCU; + mIns[i + 1].mCode = BC_NOP; + progress = true; + } + else if ( + mIns[i + 0].mCode == BC_LOAD_LOCAL_U8 && mIns[i + 0].mRegister == BC_REG_ACCU && + mIns[i + 1].mCode == BC_STORE_REG_16 && !(mIns[i + 1].mLive & LIVE_ACCU)) + { + mIns[i + 0].mRegister = mIns[i + 1].mRegister; + mIns[i + 1].mCode = BC_NOP; + progress = true; + } +#if 1 + else if ( + mIns[i + 0].mCode == BC_BINOP_ADDR_16 && + mIns[i + 1].mCode == BC_STORE_REG_16 && mIns[i + 0].mRegister == mIns[i + 1].mRegister && !(mIns[i + 1].mLive & LIVE_ACCU)) + { + mIns[i + 1].mCode = BC_BINOP_ADDA_16; + mIns[i + 0].mCode = BC_NOP; + progress = true; + } + else if ( + mIns[i + 0].mCode == BC_BINOP_ADDA_16 && + mIns[i + 1].mCode == BC_LOAD_REG_16 && mIns[i + 0].mRegister == mIns[i + 1].mRegister && mIns[i + 1].mRegisterFinal) + { + mIns[i + 1].mCode = BC_BINOP_ADDR_16; + mIns[i + 0].mCode = BC_NOP; + progress = true; + } + else if ( + mIns[i + 0].mCode == BC_BINOP_ADDA_16 && + mIns[i + 1].mCode == BC_ADDR_REG && mIns[i + 0].mRegister == mIns[i + 1].mRegister && mIns[i + 1].mRegisterFinal) + { + mIns[i + 1].mCode = BC_LEA_ACCU_INDEX; + mIns[i + 0].mCode = BC_NOP; + progress = true; + } +#endif +#if 1 + else if ( + mIns[i + 0].mCode == BC_LOAD_ABS_16 && + mIns[i + 1].mCode == BC_ADDR_REG && mIns[i + 0].mRegister == mIns[i + 1].mRegister && mIns[i + 1].mRegisterFinal) + { + mIns[i + 0].mCode = BC_LOAD_ABS_ADDR; + mIns[i + 1].mCode = BC_NOP; + progress = true; + } +#endif + else if ( + (mIns[i + 0].mCode == BC_LOAD_ADDR_U8 || mIns[i + 0].mCode == BC_LOAD_ABS_U8 || mIns[i + 0].mCode == BC_LOAD_LOCAL_U8) && + mIns[i + 1].mCode == BC_BINOP_ANDI_16 && mIns[i + 0].mRegister == mIns[i + 1].mRegister) + { + // upper byte is already zero + mIns[i + 1].mCode = BC_BINOP_ANDI_8; + progress = true; + } + else if ( + mIns[i + 0].mCode == BC_BINOP_ORI_16 && + mIns[i + 1].mCode == BC_STORE_ADDR_8 && mIns[i + 0].mRegister == mIns[i + 1].mRegister && mIns[i + 1].mRegisterFinal) + { + mIns[i + 0].mCode = BC_BINOP_ORI_8; + progress = true; + } + else if ( + mIns[i + 0].mCode == BC_BINOP_ANDI_16 && + mIns[i + 1].mCode == BC_STORE_ADDR_8 && mIns[i + 0].mRegister == mIns[i + 1].mRegister && mIns[i + 1].mRegisterFinal) + { + mIns[i + 0].mCode = BC_BINOP_ANDI_8; + progress = true; + } + else if ( + mIns[i + 0].mCode == BC_LEA_ABS && + mIns[i + 1].mCode == BC_LEA_ACCU_INDEX && mIns[i + 0].mRegister == mIns[i + 1].mRegister && mIns[i + 1].mRegisterFinal) + { + mIns[i + 0].mCode = BC_LEA_ABS_INDEX; + mIns[i + 0].mRegister = BC_REG_ACCU; + mIns[i + 1].mCode = BC_NOP; + progress = true; + } + +#if 1 + else if ( + mIns[i + 0].mCode == BC_LEA_ABS && mIns[i + 0].mRegister == BC_REG_ACCU && + mIns[i + 1].mCode == BC_LEA_ACCU_INDEX && !(mIns[i + 1].mLive & LIVE_ACCU)) + { + mIns[i + 0].mCode = BC_NOP; + mIns[i + 1].mCode = BC_LEA_ABS_INDEX; + mIns[i + 1].mLinkerObject = mIns[i + 0].mLinkerObject; + mIns[i + 1].mValue = mIns[i + 0].mValue; + mIns[i + 1].mRelocate = mIns[i + 0].mRelocate; + progress = true; + } +#endif +#if 1 + else if ( + i + 2 == mIns.Size() && mFalseJump && + mIns[i + 0].mCode == BC_LOAD_REG_8 && + mIns[i + 1].mCode == BC_BINOP_CMPUR_8 && accuTempByte == mIns[i + 1].mRegister && !(mExitLive & LIVE_ACCU) + ) + { + mIns[i + 0].mCode = BC_NOP; + mIns[i + 1].mRegister = mIns[i + 0].mRegister; + mBranch = TransposeBranchCondition(mBranch); + progress = true; + } + +#endif + +#if 0 + else if ((mIns[i].mCode == BC_LOAD_LOCAL_16 || mIns[i].mCode == BC_LOAD_ABS_16) && mIns[i + 1].mCode == BC_ADDR_REG && mIns[i].mRegister == mIns[i + 1].mRegister && mIns[i + 1].mRegisterFinal) + { + mIns[i].mRegister = BC_REG_ADDR; + mIns[i + 1].mCode = BC_NOP; + progress = true; + } +#endif + } + + if (mIns[i].mCode == BC_BINOP_ORI_16 && !(mIns[i].mValue & 0xff00)) + { + mIns[i].mCode = BC_BINOP_ORI_8; + progress = true; + } + + if (mIns[i].mCode == BC_BINOP_ANDI_16 && (mIns[i].mValue & 0xff00) == 0xff00) + { + mIns[i].mCode = BC_BINOP_ANDI_8; + progress = true; + } + + if (mIns[i].mCode == BC_BINOP_ANDI_16 && mIns[i].mRegister == BC_REG_ACCU && mIns[i].mValue == 0x00ff) + { + mIns[i].mCode = BC_LOAD_REG_8; + progress = true; + } +#endif +#if 1 + if ((mIns[i].mCode == BC_LOAD_REG_16 || mIns[i].mCode == BC_STORE_REG_16 || mIns[i].mCode == BC_LOAD_REG_32 || mIns[i].mCode == BC_STORE_REG_32) && accuTemp == mIns[i].mRegister) + { + mIns[i].mCode = BC_NOP; + progress = true; + } + else if ((mIns[i].mCode == BC_LOAD_REG_8 || mIns[i].mCode == BC_STORE_REG_8) && accuTempByte == mIns[i].mRegister) + { + mIns[i].mCode = BC_NOP; + progress = true; + } +#endif + if (mIns[i].mCode == BC_ADDR_REG && mIns[i].mRegister == addrTemp) + { + if (mIns[i].mRegisterFinal) + { + int j = i; + while (j > 0 && !mIns[j - 1].UsesRegister(mIns[i].mRegister) && !mIns[j - 1].ChangesRegister(mIns[i].mRegister)) + j--; + if (j > 0 && mIns[j - 1].UsesRegister(mIns[i].mRegister)) + mIns[j - 1].mRegisterFinal = true; + } + mIns[i].mCode = BC_NOP; + progress = true; + } + + if (accuConst && !mIns[i].mRelocate && mIns[i].mRegister == BC_REG_ACCU) + { + switch (mIns[i].mCode) + { + case BC_CONST_16: + if ((mIns[i].mValue & 0xffff) == (accuVal & 0xffff)) + { + mIns[i].mCode = BC_NOP; + progress = true; + } + break; + case BC_CONST_32: + if (mIns[i].mValue == accuVal && accuLong) + { + mIns[i].mCode = BC_NOP; + progress = true; + } + break; + case BC_CONST_8: + if ((mIns[i].mValue & 0xff) == (accuVal & 0xffff)) + { + mIns[i].mCode = BC_NOP; + progress = true; + } + break; + } + } + + if (mIns[i].ChangesAccu()) + { + accuTemp = -1; + accuTempByte = -1; + accuConst = false; + accuLong = false; + } + if (mIns[i].ChangesAddr()) + addrTemp = -1; + if (accuTemp != -1 && mIns[i].ChangesRegister(accuTemp)) + accuTemp = -1; + if (accuTempByte != -1 && mIns[i].ChangesRegister(accuTempByte)) + accuTempByte = -1; + if (addrTemp != -1 && mIns[i].ChangesRegister(addrTemp)) + addrTemp = -1; + + if (mIns[i].mCode == BC_LOAD_REG_16 || mIns[i].mCode == BC_STORE_REG_16 || mIns[i].mCode == BC_LOAD_REG_32 || mIns[i].mCode == BC_STORE_REG_32) + accuTemp = mIns[i].mRegister; + else if (mIns[i].mCode == BC_LOAD_REG_8) + accuTempByte = mIns[i].mRegister; + else if (mIns[i].mCode == BC_ADDR_REG && mIns[i].mRegister != BC_REG_ACCU) + addrTemp = mIns[i].mRegister; + + if (mIns[i].mRegister == BC_REG_ACCU && !mIns[i].mRelocate) + { + switch (mIns[i].mCode) + { + case BC_CONST_16: + accuVal = mIns[i].mValue & 0xffff; + accuConst = true; + break; + case BC_CONST_32: + accuVal = mIns[i].mValue; + accuConst = true; + accuLong = true; + break; + case BC_CONST_8: + accuVal = mIns[i].mValue & 0xff; + accuConst = true; + break; + } + } + } + + if (phase >= 2) + { + for (int i = mIns.Size() - 1; i >= 0; i--) + { + if (mIns[i].CheckAccuSize(aused)) + progress = true; + } + } + + if (progress) + changed = true; + } while (progress); + + if (mTrueJump && mTrueJump->PeepHoleOptimizer(phase)) + changed = true; + if (mFalseJump && mFalseJump->PeepHoleOptimizer(phase)) + changed = true; + } + + return changed; +} + +void ByteCodeBasicBlock::Assemble(ByteCodeGenerator* generator) +{ + if (!mAssembled) + { + mAssembled = true; + + int nins = 0; + for (int i = 0; i < mIns.Size(); i++) + { + mIns[i].Assemble(generator, this); + if (mCode.Size() > nins + 240) + { + PutCode(generator, BC_NOP); + nins = mCode.Size(); + } + } + + mLinear = mCode.Size() - nins + 3; + + if (this->mTrueJump) + this->mTrueJump->Assemble(generator); + if (this->mFalseJump) + this->mFalseJump->Assemble(generator); + } +} + +void ByteCodeBasicBlock::Close(ByteCodeBasicBlock* trueJump, ByteCodeBasicBlock* falseJump, ByteCode branch) +{ + if (branch == BC_NOP) + { + this->mTrueJump = this->mTrueLink = falseJump; + this->mFalseJump = this->mFalseLink = nullptr; + this->mBranch = BC_JUMPS; + } + else if (branch == BC_JUMPS) + { + this->mTrueJump = this->mTrueLink = trueJump; + this->mFalseJump = this->mFalseLink = nullptr; + this->mBranch = BC_JUMPS; + } + else + { + this->mTrueJump = this->mTrueLink = trueJump; + this->mFalseJump = this->mFalseLink = falseJump; + this->mBranch = branch; + } +} + +static int BranchByteSize(int from, int to) +{ + if (to - from >= -126 && to - from <= 129) + return 2; + else + return 3; +} + +static int JumpByteSize(int from, int to) +{ + if (to - from >= -126 && to - from <= 129) + return 2; + else + return 3; +} + +ByteCodeBasicBlock* ByteCodeBasicBlock::BypassEmptyBlocks(void) +{ + if (mBypassed) + return this; + else if (!mFalseJump && mCode.Size() == 0 && this != mTrueJump) + return mTrueJump->BypassEmptyBlocks(); + else + { + mBypassed = true; + + if (mFalseJump) + mFalseJump = mFalseJump->BypassEmptyBlocks(); + if (mTrueJump) + mTrueJump = mTrueJump->BypassEmptyBlocks(); + + return this; + } +} + +void ByteCodeBasicBlock::CopyCode(ByteCodeGenerator* generator, LinkerObject* linkerObject, uint8 * target) +{ + int i; + int next, end; + + for (int i = 0; i < mRelocations.Size(); i++) + { + LinkerReference rl = mRelocations[i]; + rl.mObject = linkerObject; + rl.mOffset += mOffset; + linkerObject->AddReference(rl); + } + + end = mOffset + mCode.Size(); + next = mOffset + mSize; + + if (mFalseJump) + { + if (mFalseJump->mPlace == mPlace + 1) + end += PutBranch(generator, mBranch, mTrueJump->mOffset - end); + else if (mTrueJump->mPlace == mPlace + 1) + end += PutBranch(generator, InvertBranchCondition(mBranch), mFalseJump->mOffset - end); + else + { + end += PutBranch(generator, mBranch, mTrueJump->mOffset - end); + end += PutBranch(generator, BC_JUMPS, mFalseJump->mOffset - end); + } + } + else if (mTrueJump) + { + if (mTrueJump->mPlace != mPlace + 1) + end += PutBranch(generator, mBranch, mTrueJump->mOffset - end); + } + + assert(end == next); + + for (i = 0; i < mCode.Size(); i++) + mCode.Lookup(i, target[i + mOffset]); +} + +void ByteCodeBasicBlock::BuildPlacement(GrowingArray& placement) +{ + if (!mPlaced) + { + mPlaced = true; + mPlace = placement.Size(); + placement.Push(this); + + if (mFalseJump) + { + if (mFalseJump->mPlaced) + mTrueJump->BuildPlacement(placement); + else if (mTrueJump->mPlaced) + mFalseJump->BuildPlacement(placement); + else if (mTrueJump == mFalseJump) + mTrueJump->BuildPlacement(placement); + else if (!mTrueJump->mFalseJump && !mFalseJump->mFalseJump && mTrueJump->mTrueJump == mFalseJump->mTrueJump) + { + mFalseJump->mPlaced = true; + mFalseJump->mPlace = placement.Size(); + placement.Push(mFalseJump); + + mTrueJump->BuildPlacement(placement); + } + else if (mTrueJump->mFalseJump == mFalseJump || mTrueJump->mTrueJump == mFalseJump) + { + mTrueJump->BuildPlacement(placement); + mFalseJump->BuildPlacement(placement); + } + else if (mFalseJump->mFalseJump == mTrueJump || mFalseJump->mTrueJump == mTrueJump) + { + mFalseJump->BuildPlacement(placement); + mTrueJump->BuildPlacement(placement); + } + else if ( + !mTrueJump->mFalseJump && mTrueJump->mTrueJump && mTrueJump->mTrueJump->mPlaced && mTrueJump->mCode.Size() < 120 || + mTrueJump->mFalseJump && mTrueJump->mTrueJump && mTrueJump->mFalseJump->mPlaced && mTrueJump->mTrueJump->mPlaced && mTrueJump->mCode.Size() < 120) + { + mTrueJump->BuildPlacement(placement); + mFalseJump->BuildPlacement(placement); + } + else if (mFalseJump->mCode.Size() < mTrueJump->mCode.Size()) + { + mFalseJump->BuildPlacement(placement); + mTrueJump->BuildPlacement(placement); + } + else + { + mTrueJump->BuildPlacement(placement); + mFalseJump->BuildPlacement(placement); + } + } + else if (mTrueJump) + { + mTrueJump->BuildPlacement(placement); + } + } +} + +void ByteCodeBasicBlock::InitialOffset(int& total, int& linear) +{ + int size = mCode.Size(); + if (size > 240) + size = 240; + + mNeedsNop = linear + size > 240; + if (mNeedsNop) + { + total++; + linear = 0; + } + + linear += mLinear; + + mOffset = total; + total += mCode.Size(); + if (mFalseJump) + { + total += 3; + if (mFalseJump->mPlace != mPlace + 1 && mTrueJump->mPlace != mPlace + 1) + { + total += 3; + linear = 0; + } + } + else if (mTrueJump) + { + if (mTrueJump->mPlace != mPlace + 1) + { + total += 3; + linear = 0; + } + } + + mSize = total - mOffset; +} + +bool ByteCodeBasicBlock::CalculateOffset(int& total) +{ + if (mNeedsNop) + total++; + + bool changed = total != mOffset; + mOffset = total; + + total += mCode.Size(); + + if (mFalseJump) + { + if (mFalseJump->mPlace == mPlace + 1) + total += BranchByteSize(total, mTrueJump->mOffset); + else if (mTrueJump->mPlace == mPlace + 1) + total += BranchByteSize(total, mFalseJump->mOffset); + else + { + total += BranchByteSize(total, mTrueJump->mOffset); + total += JumpByteSize(total, mFalseJump->mOffset); + } + } + else if (mTrueJump) + { + if (mTrueJump->mPlace != mPlace + 1) + total += BranchByteSize(total, mTrueJump->mOffset); + } + + if (mOffset + mSize != total) + changed = true; + + mSize = total - mOffset; + + return changed; +} + +#if 0 + int next; + + if (mOffset > total) + { + mNeedsNop = false; + linear += mSize + 3; + + mOffset = total; + next = total + mCode.Size(); + + if (mFalseJump) + { + if (mFalseJump->mOffset <= total) + { + // falseJump has been placed + + if (mTrueJump->mOffset <= total) + { + // trueJump and falseJump have been placed, not much to do + + next = next + BranchByteSize(next, mTrueJump->mOffset); + total = next + JumpByteSize(next, mFalseJump->mOffset); + mSize = total - mOffset; + } + else + { + // trueJump has not been placed, but falseJump has + + total = next + BranchByteSize(next, mFalseJump->mOffset); + + if (linear + mTrueJump->mCode.Size() > 240) + { + mNeedsNop = true; + total++; + } + + mSize = total - mOffset; + mTrueJump->CalculateOffset(total); + } + } + else if (mTrueJump->mOffset <= total) + { + // falseJump has not been placed, but trueJump has + + total = next + BranchByteSize(next, mTrueJump->mOffset); + + if (linear + mFalseJump->mCode.Size() > 240) + { + mNeedsNop = true; + total++; + } + + mSize = total - mOffset; + mFalseJump->CalculateOffset(total); + } + else if (mKnownShortBranch) + { + // neither falseJump nor trueJump have been placed, + // but we know from previous iteration that we can do + // a short branch + + total = next + 2; + + if (linear + mFalseJump->mCode.Size() > 240) + { + mNeedsNop = true; + total++; + } + + mSize = total - mOffset; + + mFalseJump->CalculateOffset(total); + if (mTrueJump->mOffset > total) + { + // trueJump was not placed in the process, so lets place it now + mTrueJump->CalculateOffset(total); + } + } +#if 1 + else if (!mTrueJump->mFalseJump && !mFalseJump->mFalseJump && mTrueJump->mTrueJump == mFalseJump->mTrueJump && + mTrueJump->mCode.Size() < 120 && mFalseJump->mCode.Size() < 120 && mTrueJump->mTrueJump->mOffset > mOffset) + { + // Small diamond so place true then false directly behind each other + // with short branches + + total = next + 2; + + if (linear + mFalseJump->mCode.Size() > 240) + { + mNeedsNop = true; + total++; + } + + mSize = total - mOffset; + + mFalseJump->mOffset = next + 2; + mFalseJump->mSize = mFalseJump->mCode.Size() + 2; + + total = mFalseJump->mOffset + mFalseJump->mSize; + mTrueJump->CalculateOffset(total); + } +#endif + else + { + // neither falseJump nor trueJump have been placed + // + + if (mTrueJump->mFalseJump == mFalseJump || mTrueJump->mTrueJump == mFalseJump) + { + ByteCodeBasicBlock* block = mFalseJump; + mFalseJump = mTrueJump; + mTrueJump = block; + mBranch = InvertBranchCondition(mBranch); + } + + // this may lead to some undo operation... + // first assume a full size branch: + + total = next + 3; + mSize = total - mOffset; + + mFalseJump->CalculateOffset(total); + if (mTrueJump->mOffset > total) + { + // trueJump was not placed in the process, so lets place it now + + mTrueJump->CalculateOffset(total); + } + + if (BranchByteSize(next, mTrueJump->mOffset) < 3) + { + // oh, we can replace by a short branch + + mKnownShortBranch = true; + + total = next + 2; + mSize = total - mOffset; + + mFalseJump->CalculateOffset(total); + if (mTrueJump->mOffset > total) + { + // trueJump was not placed in the process, so lets place it now + + mTrueJump->CalculateOffset(total); + } + } + } + } + else if (mTrueJump) + { + if (mTrueJump->mOffset <= total) + { + // trueJump has been placed, so append the branch size + + total = next + JumpByteSize(next, mTrueJump->mOffset); + mSize = total - mOffset; + } + else + { + // we have to place trueJump, so just put it right behind us + + total = next; + mSize = total - mOffset; + + mTrueJump->CalculateOffset(total); + } + } + else + { + // no exit from block + + total += mCode.Size(); + mSize = total - mOffset; + } + } +} +#endif + + +ByteCodeProcedure::ByteCodeProcedure(void) + : mBlocks(nullptr) +{ +} + +ByteCodeProcedure::~ByteCodeProcedure(void) +{ +} + + + +void ByteCodeProcedure::Compile(ByteCodeGenerator* generator, InterCodeProcedure* proc) +{ + mID = proc->mID; + + mNumBlocks = proc->mNumBlocks; + + tblocks = new ByteCodeBasicBlock * [mNumBlocks]; + for (int i = 0; i < mNumBlocks; i++) + tblocks[i] = nullptr; + + int tempSave = proc->mTempSize > 16 ? proc->mTempSize - 16 : 0; + + entryBlock = new ByteCodeBasicBlock(); + mBlocks.Push(entryBlock); + entryBlock->PutCode(generator, BC_ENTER); entryBlock->PutWord(proc->mLocalSize + 2 + tempSave); entryBlock->PutByte(tempSave); + + if (!proc->mLeafProcedure) + { + entryBlock->PutCode(generator, BC_PUSH_FRAME); + entryBlock->PutWord(uint16(proc->mCommonFrameSize + 2)); + } + + tblocks[0] = entryBlock; + + exitBlock = new ByteCodeBasicBlock(); + mBlocks.Push(exitBlock); + + entryBlock->Compile(proc, this, proc->mBlocks[0]); + + bool progress = false; + + entryBlock->mLocked = true; + exitBlock->mLocked = true; +#if 1 + + int phase = 0; + + do { + + progress = false; + + ResetVisited(); + progress = entryBlock->PeepHoleOptimizer(phase); + + ResetVisited(); + entryBlock->CountEntries(nullptr); + + ResetVisited(); + entryBlock->MergeBasicBlocks(); + + ResetVisited(); + for (int i = 0; i < mBlocks.Size(); i++) + mBlocks[i]->mEntryBlocks.SetSize(0); + entryBlock->CollectEntryBlocks(nullptr); + + + ResetVisited(); + if (entryBlock->JoinTailCodeSequences()) + progress = true; + + if (!progress && phase < 4) + { + phase++; + progress = true; + } + + } while (progress); + +#if 1 + ResetVisited(); + entryBlock->PropagateAccuCrossBorder(-1, -1); +#endif + +#endif + + entryBlock->Assemble(generator); + + if (!proc->mLeafProcedure) + { + exitBlock->PutCode(generator, BC_POP_FRAME); + exitBlock->PutWord(uint16(proc->mCommonFrameSize + 2)); + } + exitBlock->PutCode(generator, BC_RETURN); exitBlock->PutByte(tempSave); exitBlock->PutWord(proc->mLocalSize + 2 + tempSave); + + + ByteCodeBasicBlock* lentryBlock = entryBlock->BypassEmptyBlocks(); + + GrowingArray placement(nullptr); + + int total, linear; + total = 0; + linear = 0; + + lentryBlock->BuildPlacement(placement); + + for (int i = 0; i < placement.Size(); i++) + placement[i]->InitialOffset(total, linear); + + do { + progress = false; + total = 0; + for (int i = 0; i < placement.Size(); i++) + if (placement[i]->CalculateOffset(total)) + progress = true; + } while (progress); + + uint8* data = proc->mLinkerObject->AddSpace(total); + + for (int i = 0; i < placement.Size(); i++) + placement[i]->CopyCode(generator, proc->mLinkerObject, data); + + mProgSize = total; +} + +ByteCodeBasicBlock* ByteCodeProcedure::CompileBlock(InterCodeProcedure* iproc, InterCodeBasicBlock* sblock) +{ + if (tblocks[sblock->mIndex]) + return tblocks[sblock->mIndex]; + + ByteCodeBasicBlock * block = new ByteCodeBasicBlock(); + mBlocks.Push(block); + tblocks[sblock->mIndex] = block; + block->Compile(iproc, this, sblock); + + return block; +} + +void ByteCodeProcedure::ResetVisited(void) +{ + for (int i = 0; i < mBlocks.Size(); i++) + { + mBlocks[i]->mVisited = false; + } +} + +ByteCodeGenerator::ByteCodeGenerator(Errors* errors, Linker* linker) + : mErrors(errors), mLinker(linker) +{ + for (int i = 0; i < 128; i++) + { + mByteCodeUsed[i] = 0; + mExtByteCodes[i] = nullptr; + } + + mByteCodeUsed[BC_CALL_ABS] = 1; + mByteCodeUsed[BC_EXIT] = 1; + mByteCodeUsed[BC_NATIVE] = 1; + mByteCodeUsed[BC_NOP] = 1; + + assert(sizeof(ByteCodeNames) == 128 * sizeof(char*)); +} + +ByteCodeGenerator::~ByteCodeGenerator(void) +{ +} + +bool ByteCodeGenerator::WriteByteCodeStats(const char* filename) +{ + FILE* file; + fopen_s(&file, filename, "w"); + if (file) + { + for (int i = 0; i < 128; i++) + { + if (mByteCodeUsed[i] > 0) + fprintf(file, "BC %s : %d\n", ByteCodeNames[i], mByteCodeUsed[i]); + } + fclose(file); + + return true; + } + + return false; +} diff --git a/oscar64/oscar64/ByteCodeGenerator.h b/oscar64/oscar64/ByteCodeGenerator.h new file mode 100644 index 0000000..85f886e --- /dev/null +++ b/oscar64/oscar64/ByteCodeGenerator.h @@ -0,0 +1,361 @@ +#pragma once + +#include "InterCode.h" +#include "Ident.h" +#include "Disassembler.h" + +enum ByteCode +{ + BC_NOP, + BC_EXIT, + + BC_CONST_8, + BC_CONST_P8, + BC_CONST_N8, + BC_CONST_16, + BC_CONST_32, + + BC_LOAD_REG_8, + BC_STORE_REG_8, + BC_LOAD_REG_16, + BC_STORE_REG_16, + BC_ADDR_REG, + BC_LOAD_REG_32, + BC_STORE_REG_32, + + BC_LOAD_ABS_8, + BC_LOAD_ABS_U8, + BC_LOAD_ABS_16, + BC_LOAD_ABS_32, + BC_LOAD_ABS_ADDR, + + BC_STORE_ABS_8, + BC_STORE_ABS_16, + BC_STORE_ABS_32, + + BC_LEA_ABS, + BC_LEA_ABS_INDEX, + BC_LEA_ABS_INDEX_U8, + BC_LEA_ACCU_INDEX, + + BC_LOAD_LOCAL_8, + BC_LOAD_LOCAL_U8, + BC_LOAD_LOCAL_16, + BC_LOAD_LOCAL_32, + + BC_STORE_LOCAL_8, + BC_STORE_LOCAL_16, + BC_STORE_LOCAL_32, + + BC_LEA_LOCAL, + + BC_STORE_FRAME_8, + BC_STORE_FRAME_16, + BC_STORE_FRAME_32, + + BC_LEA_FRAME, + + BC_LOAD_ADDR_8, + BC_LOAD_ADDR_U8, + BC_LOAD_ADDR_16, + BC_LOAD_ADDR_32, + + BC_STORE_ADDR_8, + BC_STORE_ADDR_16, + BC_STORE_ADDR_32, + + BC_BINOP_ADDR_16, + BC_BINOP_SUBR_16, + BC_BINOP_ANDR_16, + BC_BINOP_ORR_16, + BC_BINOP_XORR_16, + BC_BINOP_MULR_16, + BC_BINOP_DIVR_U16, + BC_BINOP_MODR_U16, + BC_BINOP_DIVR_I16, + BC_BINOP_MODR_I16, + BC_BINOP_SHLR_16, + BC_BINOP_SHRR_U16, + BC_BINOP_SHRR_I16, + + BC_BINOP_ADDA_16, + + BC_BINOP_ADDI_16, + BC_BINOP_SUBI_16, + BC_BINOP_ANDI_16, + BC_BINOP_ORI_16, + BC_BINOP_MULI8_16, + + BC_BINOP_ADDI_8, + BC_BINOP_ANDI_8, + BC_BINOP_ORI_8, + + BC_BINOP_SHLI_16, + BC_BINOP_SHRI_U16, + BC_BINOP_SHRI_I16, + + BC_BINOP_CMPUR_16, + BC_BINOP_CMPSR_16, + + BC_BINOP_CMPUI_16, + BC_BINOP_CMPSI_16, + + BC_BINOP_CMPUR_8, + BC_BINOP_CMPSR_8, + + BC_BINOP_CMPUI_8, + BC_BINOP_CMPSI_8, + + BC_OP_NEGATE_16, + BC_OP_INVERT_16, + + BC_BINOP_ADD_F32, + BC_BINOP_SUB_F32, + BC_BINOP_MUL_F32, + BC_BINOP_DIV_F32, + BC_BINOP_CMP_F32, + BC_OP_NEGATE_F32, + BC_OP_ABS_F32, + BC_OP_FLOOR_F32, + BC_OP_CEIL_F32, + + BC_CONV_U16_F32, + BC_CONV_I16_F32, + BC_CONV_F32_U16, + BC_CONV_F32_I16, + + BC_CONV_I8_I16, + + BC_JUMPS, + BC_BRANCHS_EQ, + BC_BRANCHS_NE, + BC_BRANCHS_GT, + BC_BRANCHS_GE, + BC_BRANCHS_LT, + BC_BRANCHS_LE, + + BC_JUMPF, + BC_BRANCHF_EQ, + BC_BRANCHF_NE, + BC_BRANCHF_GT, + BC_BRANCHF_GE, + BC_BRANCHF_LT, + BC_BRANCHF_LE, + + BC_LOOP_U8, + BC_MALLOC, + BC_FREE, + BC_FILL, + BC_FILL_LONG, + BC_UNUSED_6, + + BC_JSR, + + BC_NATIVE = 0x75, + + BC_ENTER, + BC_RETURN, + BC_CALL_ADDR, + BC_CALL_ABS, + BC_PUSH_FRAME, + BC_POP_FRAME, + + BC_COPY, + BC_COPY_LONG, + BC_STRCPY, + + BC_EXTRT, + + BC_CONV_I16_I32 = 0x80, + BC_CONV_U16_U32, + + BC_OP_NEGATE_32, + BC_OP_INVERT_32, + + BC_BINOP_ADD_L32, + BC_BINOP_SUB_L32, + BC_BINOP_AND_L32, + BC_BINOP_OR_L32, + BC_BINOP_XOR_L32, + BC_BINOP_MUL_L32, + BC_BINOP_DIV_U32, + BC_BINOP_MOD_U32, + BC_BINOP_DIV_I32, + BC_BINOP_MOD_I32, + BC_BINOP_SHL_L32, + BC_BINOP_SHR_U32, + BC_BINOP_SHR_I32, + BC_BINOP_CMP_U32, + BC_BINOP_CMP_S32, + + BC_CONV_U32_F32, + BC_CONV_I32_F32, + BC_CONV_F32_U32, + BC_CONV_F32_I32, +}; + +class ByteCodeProcedure; +class ByteCodeGenerator; + +class ByteCodeBasicBlock; + +class ByteCodeInstruction +{ +public: + ByteCodeInstruction(ByteCode code = BC_NOP); + + void Assemble(ByteCodeGenerator* generator, ByteCodeBasicBlock* block); + + ByteCode mCode; + uint32 mRegister; + int mValue; + bool mRelocate, mRegisterFinal; + LinkerObject* mLinkerObject; + const char* mRuntime; + uint32 mLive; + + bool IsStore(void) const; + bool ChangesAccu(void) const; + bool ChangesAddr(void) const; + bool ChangesRegister(uint32 reg) const; + + bool UsesAccu(void) const; + bool UsesAddr(void) const; + bool UsesRegister(uint32 reg) const; + + bool LoadsRegister(uint32 reg) const; + bool StoresRegister(uint32 reg) const; + bool IsLocalStore(void) const; + bool IsLocalLoad(void) const; + bool IsLocalAccess(void) const; + bool IsShiftByRegister(void) const; + bool IsIntegerConst(void) const; + bool IsCompare(void) const; + + bool IsCommutative(void) const; + bool IsSame(const ByteCodeInstruction& ins) const; + + bool ValueForwarding(ByteCodeInstruction*& accuIns, ByteCodeInstruction*& addrIns); + + bool CheckAccuSize(uint32 & used); +}; + +class ByteCodeBasicBlock +{ +public: + DynamicArray mCode; + int mIndex; + + ByteCodeBasicBlock * mTrueJump, * mFalseJump; + ByteCodeBasicBlock * mTrueLink, * mFalseLink; + ByteCode mBranch; + + GrowingArray mIns; + GrowingArray mRelocations; + GrowingArray mEntryBlocks; + + int mOffset, mSize, mPlace, mLinear, mNumEntries; + bool mPlaced, mNeedsNop, mBypassed, mAssembled, mVisited, mLocked; + uint32 mExitLive; + + ByteCodeBasicBlock(void); + + void Assemble(ByteCodeGenerator* generator); + void Compile(InterCodeProcedure* iproc, ByteCodeProcedure * proc, InterCodeBasicBlock * block); + void Close(ByteCodeBasicBlock* trueJump, ByteCodeBasicBlock* falseJump, ByteCode branch); + + void PutByte(uint8 code); + void PutWord(uint16 code); + void PutDWord(uint32 code); + void PutBytes(const uint8* code, int num); + + void PutCode(ByteCodeGenerator* generator, ByteCode code); + int PutBranch(ByteCodeGenerator* generator, ByteCode code, int offset); + + ByteCodeBasicBlock* BypassEmptyBlocks(void); + + void BuildPlacement(GrowingArray & placement); + void InitialOffset(int& total, int& linear); + bool CalculateOffset(int & total); + + void CopyCode(ByteCodeGenerator* generator, LinkerObject * linkerObject, uint8* target); + + void LongConstToAccu(int64 val); + void LongConstToWork(int64 val); + void IntConstToAccu(int64 val); + void IntConstToAddr(int64 val); + void FloatConstToAccu(double val); + void FloatConstToWork(double val); + void FillValue(InterCodeProcedure* proc, const InterInstruction* ins); + void CopyValue(InterCodeProcedure* proc, const InterInstruction * ins); + void StrcpyValue(InterCodeProcedure* proc, const InterInstruction* ins); + void CallMalloc(InterCodeProcedure* proc, const InterInstruction* ins); + void CallFree(InterCodeProcedure* proc, const InterInstruction* ins); + void LoadConstant(InterCodeProcedure* proc, const InterInstruction * ins); + void StoreDirectValue(InterCodeProcedure* proc, const InterInstruction * ins); + void LoadDirectValue(InterCodeProcedure* proc, const InterInstruction * ins); + void LoadStoreIndirectValue(InterCodeProcedure* proc, const InterInstruction* rins, const InterInstruction* wins); + + void LoadOperandAddress(InterCodeProcedure* proc, const InterOperand& op, int reg); + + void LoadEffectiveAddress(InterCodeProcedure* proc, const InterInstruction * ins); + void CallFunction(InterCodeProcedure* proc, const InterInstruction * ins); + void CallAssembler(InterCodeProcedure* proc, const InterInstruction * ins); + void CallNative(InterCodeProcedure* proc, const InterInstruction* ins); + void BinaryOperator(InterCodeProcedure* proc, const InterInstruction * ins); + void UnaryOperator(InterCodeProcedure* proc, const InterInstruction * ins); + void BinaryRROperator(InterCodeProcedure* proc, const InterInstruction * ins); + ByteCode RelationalOperator(InterCodeProcedure* proc, const InterInstruction * ins, bool optzero); + void BinaryIntOperator(InterCodeProcedure* proc, const InterInstruction * ins, ByteCode code); + void NumericConversion(InterCodeProcedure* proc, const InterInstruction * ins); + + + + void CollectEntryBlocks(ByteCodeBasicBlock * block); + void CountEntries(ByteCodeBasicBlock* fromJump); + + bool MergeBasicBlocks(void); + bool JoinTailCodeSequences(void); + bool SameTail(ByteCodeInstruction& ins); + bool PropagateAccuCrossBorder(int accu, int addr); + + bool PeepHoleOptimizer(int phase); +}; + +class ByteCodeGenerator; + +class ByteCodeProcedure +{ +public: + ByteCodeProcedure(void); + ~ByteCodeProcedure(void); + + ByteCodeBasicBlock * entryBlock, * exitBlock; + ByteCodeBasicBlock ** tblocks; + GrowingArray < ByteCodeBasicBlock*> mBlocks; + + int mProgSize, mID, mNumBlocks; + + void Compile(ByteCodeGenerator* generator, InterCodeProcedure* proc); + ByteCodeBasicBlock * CompileBlock(InterCodeProcedure* iproc, InterCodeBasicBlock* block); + + void ResetVisited(void); + +protected: + ByteCodeDisassembler mDisassembler; +}; + +class ByteCodeGenerator +{ +public: + ByteCodeGenerator(Errors* errors, Linker* linker); + ~ByteCodeGenerator(void); + + Errors* mErrors; + Linker* mLinker; + + uint32 mByteCodeUsed[128]; + LinkerObject* mExtByteCodes[128]; + + bool WriteByteCodeStats(const char* filename); +}; diff --git a/oscar64/oscar64/CompilationUnits.cpp b/oscar64/oscar64/CompilationUnits.cpp new file mode 100644 index 0000000..3dce915 --- /dev/null +++ b/oscar64/oscar64/CompilationUnits.cpp @@ -0,0 +1,114 @@ +#include "CompilationUnits.h" +#ifdef _WIN32 +#include +#else +#include +#endif +#include +#include + +CompilationUnits::CompilationUnits(Errors* errors) + : mErrors(errors), mReferenced(nullptr), mUniqueID(0) +{ + mCompilationUnits = nullptr; + mPendingUnits = nullptr; + mScope = new DeclarationScope(nullptr, SLEVEL_GLOBAL); + mRuntimeScope = new DeclarationScope(nullptr, SLEVEL_GLOBAL); + mVTableScope = new DeclarationScope(nullptr, SLEVEL_GLOBAL); + mTemplateScope = new DeclarationScope(nullptr, SLEVEL_GLOBAL); + mStartup = nullptr; + + for (int i = 0; i < 256; i++) + mByteCodes[i] = nullptr; +} + +CompilationUnits::~CompilationUnits(void) +{ + +} + +int CompilationUnits::UniqueID(void) +{ + return mUniqueID++; +} + + +void CompilationUnits::AddReferenced(Declaration* ref) +{ + mReferenced.Push(ref); +} + +bool CompilationUnits::AddUnit(Location& location, const char* name, const char* from) +{ + char filename[200]; + + if (!from) + { + strcpy_s(filename, name); + } + else + { + strcpy_s(filename, from); + ptrdiff_t i = strlen(filename); + while (i > 0 && (filename[i - 1] != '/' && filename[i - 1] != '\\')) + i--; + while (name[0] == '.' && name[1] == '.' && name[2] == '/') + { + name += 3; + while (i > 0 && (filename[i - 1] != '/' && filename[i - 1] != '\\')) + i--; + } + filename[i] = 0; + strcat_s(filename, name); + } + + int i = 0; + while (filename[i]) + { + if (filename[i] == '\\') + filename[i] = '/'; + i++; + } + + if (_access(filename, 0) != 0) + { + mErrors->Error(location, EERR_FILE_NOT_FOUND, "Could not open source file.", filename); + return false; + } + + CompilationUnit* cunit = mCompilationUnits, * punit = nullptr; + while (cunit && strcmp(cunit->mFileName, filename) != 0) + { + punit = cunit; + cunit = cunit->mNext; + } + + if (cunit) + return true; + + cunit = new CompilationUnit(); + cunit->mLocation = location; + strcpy_s(cunit->mFileName, filename); + cunit->mCompiled = false; + cunit->mNext = nullptr; + + if (punit) + punit->mNext = cunit; + else + mCompilationUnits = cunit; + + if (!mPendingUnits) + mPendingUnits = cunit; + + return true; +} + +CompilationUnit* CompilationUnits::PendingUnit(void) +{ + CompilationUnit* unit = mPendingUnits; + + if (mPendingUnits) + mPendingUnits = mPendingUnits->mNext; + + return unit; +} diff --git a/oscar64/oscar64/CompilationUnits.h b/oscar64/oscar64/CompilationUnits.h new file mode 100644 index 0000000..2891f25 --- /dev/null +++ b/oscar64/oscar64/CompilationUnits.h @@ -0,0 +1,46 @@ +#pragma once + +#include "Declaration.h" +#include "Errors.h" +#include "Linker.h" + +class CompilationUnit +{ +public: + Location mLocation; + char mFileName[200]; + CompilationUnit * mNext; + bool mCompiled; +}; + +class CompilationUnits +{ +public: + CompilationUnits(Errors * errors); + ~CompilationUnits(void); + + DeclarationScope* mScope, * mVTableScope, * mTemplateScope; + CompilationUnit* mCompilationUnits, * mPendingUnits; + + Declaration* mStartup; + Declaration* mByteCodes[256]; + GrowingArray mReferenced; + + DeclarationScope* mRuntimeScope; + + LinkerSection* mSectionCode, * mSectionData, * mSectionBSS, * mSectionHeap, * mSectionStack, * mSectionZeroPage, * mSectionLowCode, * mSectionBoot; + Linker* mLinker; + + int mUniqueID; + + int UniqueID(void); + + bool AddUnit(Location & location, const char* name, const char * from); + CompilationUnit* PendingUnit(void); + + void AddReferenced(Declaration* ref); + +protected: + Errors* mErrors; +}; + diff --git a/oscar64/oscar64/Compiler.cpp b/oscar64/oscar64/Compiler.cpp new file mode 100644 index 0000000..113c1e4 --- /dev/null +++ b/oscar64/oscar64/Compiler.cpp @@ -0,0 +1,1927 @@ +#include "Compiler.h" +#include "Scanner.h" +#include "Parser.h" +#include "InterCodeGenerator.h" +#include "InterCode.h" +#include "ByteCodeGenerator.h" +#include "NativeCodeGenerator.h" +#include "Emulator.h" +#include + +Compiler::Compiler(void) + : mByteCodeFunctions(nullptr), mCompilerOptions(COPT_DEFAULT), mDefines({nullptr, nullptr}) +{ + mErrors = new Errors(); + mLinker = new Linker(mErrors); + mCompilationUnits = new CompilationUnits(mErrors); + + mCompilationUnits->mLinker = mLinker; + mCompilationUnits->mSectionCode = mLinker->AddSection(Ident::Unique("code"), LST_DATA); + mCompilationUnits->mSectionData = mLinker->AddSection(Ident::Unique("data"), LST_DATA); + mCompilationUnits->mSectionBSS = mLinker->AddSection(Ident::Unique("bss"), LST_BSS); + mCompilationUnits->mSectionHeap = mLinker->AddSection(Ident::Unique("heap"), LST_HEAP); + mCompilationUnits->mSectionStack = mLinker->AddSection(Ident::Unique("stack"), LST_STACK); + mCompilationUnits->mSectionZeroPage = mLinker->AddSection(Ident::Unique("zeropage"), LST_ZEROPAGE); + mCompilationUnits->mSectionLowCode = nullptr; + mCompilationUnits->mSectionBoot = nullptr; + mCompilationUnits->mSectionStack->mSize = 4096; + mCompilationUnits->mSectionHeap->mSize = 1024; + + mPreprocessor = new Preprocessor(mErrors); + mByteCodeGenerator = new ByteCodeGenerator(mErrors, mLinker); + mInterCodeGenerator = new InterCodeGenerator(mErrors, mLinker); + mNativeCodeGenerator = new NativeCodeGenerator(mErrors, mLinker, mCompilationUnits->mSectionCode); + mInterCodeModule = new InterCodeModule(mErrors, mLinker); + mGlobalAnalyzer = new GlobalAnalyzer(mErrors, mLinker); + mGlobalOptimizer = new GlobalOptimizer(mErrors, mLinker); + + mCartridgeID = 0x0000; + mCartridgeSubType = 0x00; + strcpy_s(mCartridgeName, "OSCAR"); +} + +Compiler::~Compiler(void) +{ + +} + +void Compiler::AddDefine(const Ident* ident, const char* value) +{ + Define define; + define.mIdent = ident; + define.mValue = value; + mDefines.Push(define); +} + + +bool Compiler::ParseSource(void) +{ + if (mTargetMachine == TMACH_ATARI) + { + BC_REG_WORK_Y = 0x80; + BC_REG_WORK = 0x81; + BC_REG_FPARAMS = 0x8b; + BC_REG_FPARAMS_END = 0x97; + + BC_REG_IP = 0x97; + BC_REG_ACCU = 0x99; + BC_REG_ADDR = 0x9d; + BC_REG_STACK = 0xa1; + BC_REG_LOCALS = 0xa3; + + BC_REG_TMP = 0xa5; + BC_REG_TMP_SAVED = 0xc5; + } + else if (mTargetMachine == TMACH_X16) + { + BC_REG_WORK_Y = 0x22; + BC_REG_WORK = 0x23; + BC_REG_FPARAMS = 0x2d; + BC_REG_FPARAMS_END = 0x39; + + BC_REG_IP = 0x39; + BC_REG_ACCU = 0x3b; + BC_REG_ADDR = 0x3f; + BC_REG_STACK = 0x43; + BC_REG_LOCALS = 0x45; + + BC_REG_TMP = 0x47; + BC_REG_TMP_SAVED = 0x67; + } + else if (mCompilerOptions & COPT_EXTENDED_ZERO_PAGE) + { + BC_REG_FPARAMS = 0x0d; + BC_REG_FPARAMS_END = 0x25; + + BC_REG_IP = 0x25; + BC_REG_ACCU = 0x27; + BC_REG_ADDR = 0x2b; + BC_REG_STACK = 0x2f; + BC_REG_LOCALS = 0x31; +#if 1 + BC_REG_TMP = 0x33; + BC_REG_TMP_SAVED = 0x53; +#endif + } + + switch (mTargetMachine) + { + case TMACH_VIC20: + case TMACH_VIC20_3K: + case TMACH_VIC20_8K: + case TMACH_PET_8K: + mCompilationUnits->mSectionStack->mSize = 512; + mCompilationUnits->mSectionHeap->mSize = 512; + break; + case TMACH_PET_16K: + case TMACH_VIC20_16K: + case TMACH_VIC20_24K: + case TMACH_X16: + mCompilationUnits->mSectionStack->mSize = 1024; + mCompilationUnits->mSectionHeap->mSize = 1024; + break; + case TMACH_C128: + case TMACH_PLUS4: + mCompilationUnits->mSectionLowCode = mLinker->AddSection(Ident::Unique("lowcode"), LST_DATA); + break; + case TMACH_NES: + case TMACH_NES_NROM_H: + case TMACH_NES_NROM_V: + case TMACH_NES_MMC1: + case TMACH_NES_MMC3: + mCompilationUnits->mSectionStack->mSize = 256; + mCompilationUnits->mSectionHeap->mSize = 256; + mCompilationUnits->mSectionBoot = mLinker->AddSection(Ident::Unique("boot"), LST_DATA); + break; + } + + mPreprocessor->mCompilerOptions = mCompilerOptions; + mLinker->mCompilerOptions = mCompilerOptions; + + CompilationUnit* cunit; + while (mErrors->mErrorCount == 0 && (cunit = mCompilationUnits->PendingUnit())) + { + if (mPreprocessor->OpenSource("Compiling", cunit->mFileName, true)) + { + Scanner* scanner = new Scanner(mErrors, mPreprocessor); + + for (int i = 0; i < mDefines.Size(); i++) + scanner->AddMacro(mDefines[i].mIdent, mDefines[i].mValue); + + scanner->mCompilerOptions = mCompilerOptions; + + scanner->NextToken(); + + Parser* parser = new Parser(mErrors, scanner, mCompilationUnits); + + parser->mCompilerOptions = mCompilerOptions; + + parser->Parse(); + } + else + mErrors->Error(cunit->mLocation, EERR_FILE_NOT_FOUND, "Could not open source file", cunit->mFileName); + } + + return mErrors->mErrorCount == 0; +} + +void Compiler::RegisterRuntime(const Location & loc, const Ident* ident) +{ + Declaration* bcdec = mCompilationUnits->mRuntimeScope->Lookup(ident); + if (bcdec) + { + LinkerObject* linkerObject = nullptr; + int offset = 0; + + if (bcdec->mType == DT_CONST_ASSEMBLER) + { + if (!bcdec->mLinkerObject) + mInterCodeGenerator->TranslateAssembler(mInterCodeModule, bcdec, nullptr); + linkerObject = bcdec->mLinkerObject; + } + else if (bcdec->mType == DT_LABEL) + { + if (!bcdec->mBase->mLinkerObject) + mInterCodeGenerator->TranslateAssembler(mInterCodeModule, bcdec->mBase, nullptr); + + linkerObject = bcdec->mBase->mLinkerObject; + offset = int(bcdec->mInteger); + } + else if (bcdec->mType == DT_VARIABLE) + { + if (!bcdec->mBase->mLinkerObject) + mInterCodeGenerator->InitGlobalVariable(mInterCodeModule, bcdec); + linkerObject = bcdec->mLinkerObject; + offset = bcdec->mOffset; + } + + mNativeCodeGenerator->RegisterRuntime(ident, linkerObject, offset); + } + else + { + mErrors->Error(loc, EERR_RUNTIME_CODE, "Missing runtime code implementation", ident); + } +} + +static void IndexVTableTree(Declaration* vdec, int & index) +{ + vdec->mVarIndex = index; + vdec->mDefaultConstructor->mInteger = index; + index++; + Declaration* cvdec = vdec->mParams; + while (cvdec) + { + IndexVTableTree(cvdec, index); + cvdec = cvdec->mNext; + } + vdec->mSize = index - vdec->mVarIndex; +} + +static void FillVTableTree(Declaration* vdec) +{ + Declaration* cdec = vdec->mClass; + cdec->mScope->Iterate([=](const Ident* mident, Declaration* mdec) + { + if (mdec->mType == DT_CONST_FUNCTION) + { + while (mdec) + { + if (mdec->mBase->mFlags & DTF_VIRTUAL) + { + for (int i = 0; i < vdec->mSize; i++) + mdec->mVTable->mCalled[vdec->mVarIndex + i - mdec->mVTable->mDefaultConstructor->mVarIndex] = mdec; + } + mdec = mdec->mNext; + } + } + }); + + Declaration* cvdec = vdec->mParams; + while (cvdec) + { + FillVTableTree(cvdec); + cvdec = cvdec->mNext; + } +} +void Compiler::CompleteTemplateExpansion(void) +{ +} + +bool IsSimpleConstReturn(Declaration * mdec) +{ + if (mdec->mBase->mBase->IsSimpleType() && mdec->mBase->mParams->mNext == nullptr) + { + Expression* ex = mdec->mValue; + if (ex->mType == EX_SCOPE) + ex = ex->mLeft; + + if (ex->mType == EX_RETURN && ex->mLeft->mType == EX_CONSTANT) + return true; + } + + return false; +} + +void Compiler::PreCompileExpression(Expression* exp) +{ + switch (exp->mType) + { + case EX_CONSTANT: + case EX_VARIABLE: + case EX_ASSEMBLER: + if (exp->mDecValue) + PreCompileDeclaration(exp->mDecValue); + break; + case EX_PREFIX: + if (exp->mToken == TK_NEW) + { + if (mGlobalNew) + { + exp->mType = EX_CALL; + exp->mRight = exp->mLeft; + exp->mLeft = new Expression(exp->mLocation, EX_CONSTANT); + exp->mLeft->mDecType = mGlobalNew->mBase; + exp->mLeft->mDecValue = mGlobalNew; + } + } + else if (exp->mToken == TK_DELETE) + { + if (mGlobalDelete) + { + exp->mType = EX_CALL; + exp->mRight = exp->mLeft; + exp->mLeft = new Expression(exp->mLocation, EX_CONSTANT); + exp->mLeft->mDecType = mGlobalDelete->mBase; + exp->mLeft->mDecValue = mGlobalDelete; + } + } + break; + } + + if (exp->mLeft) + PreCompileExpression(exp->mLeft); + if (exp->mRight) + PreCompileExpression(exp->mRight); +} + +void Compiler::PreCompileDeclaration(Declaration* dec) +{ + while (dec->mType == DT_VARIABLE_REF || dec->mType == DT_FUNCTION_REF) + dec = dec->mBase; + + if (dec->mType == DT_VARIABLE || dec->mType == DT_CONST_FUNCTION || dec->mType == DT_CONST_ASSEMBLER) + { + if (!(dec->mFlags & DTF_PRECOMPILED)) + { + dec->mFlags |= DTF_PRECOMPILED; + if (dec->mValue) + PreCompileExpression(dec->mValue); + } + } +} + +void Compiler::CheckOperatorNew(void) +{ + mGlobalNew = mCompilationUnits->mScope->Lookup(Ident::Unique("operator-new")); + mGlobalDelete = mCompilationUnits->mScope->Lookup(Ident::Unique("operator-delete")); +} + +void Compiler::BuildVTables(void) +{ + // Connect vdecs with parents + mCompilationUnits->mVTableScope->Iterate([=](const Ident* ident, Declaration* vdec) + { + if (vdec->mBase) + { + vdec->mNext = vdec->mBase->mParams; + vdec->mBase->mParams = vdec; + } + }); + + // Number the child vtables + mCompilationUnits->mVTableScope->Iterate([=](const Ident* ident, Declaration* vdec) + { + if (!vdec->mBase) + { + int index = 0; + IndexVTableTree(vdec, index); + } + }); + + mCompilationUnits->mVTableScope->Iterate([=](const Ident* ident, Declaration* vdec) + { + if (!vdec->mBase) + { + FillVTableTree(vdec); + } + }); + + // Build vtables for functions + mCompilationUnits->mVTableScope->Iterate([=](const Ident* ident, Declaration* vdec) + { + vdec->mScope->Iterate([=](const Ident* mident, Declaration* mdec) + { + bool simpleConst = vdec->mSize > 0; + for (int i = 0; i < vdec->mSize; i++) + if (!IsSimpleConstReturn(mdec->mCalled[i])) + simpleConst = false; + + if (simpleConst) + { + Declaration* vtabt = new Declaration(mdec->mLocation, DT_TYPE_ARRAY); + vtabt->mBase = mdec->mBase->mBase->ToConstType()->ToStriped(vdec->mSize); + vtabt->mSize = vdec->mSize * mdec->mBase->mBase->mSize; + vtabt->mStride = 1; + vtabt->mStripe = 1; + vtabt->mFlags |= DTF_CONST | DTF_DEFINED; + + Declaration* vtaba = new Declaration(mdec->mLocation, DT_VARIABLE); + vtaba->mFlags = DTF_CONST | DTF_GLOBAL | DTF_DEFINED; + vtaba->mBase = vtabt; + vtaba->mSize = vtabt->mSize; + vtaba->mValue = new Expression(mdec->mLocation, EX_CONSTANT); + vtaba->mValue->mDecType = vtabt; + vtaba->mValue->mDecValue = new Declaration(mdec->mLocation, DT_CONST_STRUCT); + vtaba->mIdent = mdec->mIdent; + vtaba->mQualIdent = mdec->mQualIdent->Mangle("$vltable"); + vtaba->mSection = mdec->mSection; + vtaba->mOffset = -vdec->mVarIndex; + + Declaration* last = nullptr; + + for (int i = 0; i < vdec->mSize; i++) + { + Declaration* vmdec = mdec->mCalled[i]; + + Expression* texp = vmdec->mValue; + if (texp->mType == EX_SCOPE) + texp = texp->mLeft; + texp = texp->mLeft; + + Declaration* cdec = texp->mDecValue->Clone(); + cdec->mOffset = i; + + if (last) + last->mNext = cdec; + else + vtaba->mValue->mDecValue->mParams = cdec; + last = cdec; + } + + Expression* vexp = new Expression(mdec->mLocation, EX_QUALIFY); + vexp->mLeft = new Expression(mdec->mLocation, EX_PREFIX); + vexp->mLeft->mDecType = mdec->mBase->mParams->mBase->mBase; + vexp->mLeft->mToken = TK_MUL; + vexp->mLeft->mLeft = new Expression(mdec->mLocation, EX_VARIABLE); + vexp->mLeft->mLeft->mDecType = mdec->mBase->mParams->mBase; + vexp->mLeft->mLeft->mDecValue = mdec->mBase->mParams; + + vexp->mDecValue = new Declaration(mdec->mLocation, DT_ELEMENT); + vexp->mDecValue->mBase = TheCharTypeDeclaration; + vexp->mDecValue->mOffset = vdec->mOffset; + vexp->mDecValue->mSize = 1; + vexp->mDecType = TheCharTypeDeclaration; + + Expression* ecall = new Expression(mdec->mLocation, EX_RETURN); + ecall->mLeft = new Expression(mdec->mLocation, EX_INDEX); + ecall->mLeft->mDecType = mdec->mBase->mBase; + ecall->mDecType = mdec->mBase->mBase; + ecall->mLeft->mLeft = new Expression(mdec->mLocation, EX_VARIABLE); + ecall->mLeft->mLeft->mDecType = vtabt; + ecall->mLeft->mLeft->mDecValue = vtaba; + ecall->mLeft->mRight = vexp; + + mdec->mCalled.SetSize(0); + + mdec->mFlags |= DTF_DEFINED; + mdec->mBase->mFlags &= ~DTF_VIRTUAL; + mdec->mValue = ecall; + } + else + { + Declaration* vtabt = new Declaration(mdec->mLocation, DT_TYPE_ARRAY); + vtabt->mBase = mdec->mBase->ToStriped(vdec->mSize); + vtabt->mSize = vdec->mSize * 2; + vtabt->mStride = 1; + vtabt->mStripe = 1; + vtabt->mFlags |= DTF_CONST | DTF_DEFINED; + + Declaration* vtaba = new Declaration(mdec->mLocation, DT_VARIABLE); + vtaba->mFlags = DTF_CONST | DTF_GLOBAL | DTF_DEFINED; + vtaba->mBase = vtabt; + vtaba->mSize = vtabt->mSize; + vtaba->mValue = new Expression(mdec->mLocation, EX_CONSTANT); + vtaba->mValue->mDecType = vtabt; + vtaba->mValue->mDecValue = new Declaration(mdec->mLocation, DT_CONST_STRUCT); + vtaba->mIdent = mdec->mIdent; + vtaba->mQualIdent = mdec->mQualIdent->Mangle("$vtable"); + vtaba->mSection = mdec->mSection; + vtaba->mOffset = -vdec->mVarIndex; + + Declaration* last = nullptr; + + for (int i = 0; i < vdec->mSize; i++) + { + Declaration* vmdec = mdec->mCalled[i]; + + Expression* texp = new Expression(vmdec->mLocation, EX_CONSTANT); + texp->mDecType = vtabt->mBase; + texp->mDecValue = vmdec; + + Declaration* cdec = new Declaration(vmdec->mLocation, DT_CONST_POINTER); + cdec->mValue = texp; + cdec->mBase = vtabt->mBase; + cdec->mOffset = i; + + if (last) + last->mNext = cdec; + else + vtaba->mValue->mDecValue->mParams = cdec; + last = cdec; + } + + // mCompilationUnits->AddReferenced(vtaba); + + Expression* vexp = new Expression(mdec->mLocation, EX_QUALIFY); + vexp->mLeft = new Expression(mdec->mLocation, EX_PREFIX); + vexp->mLeft->mDecType = mdec->mBase->mParams->mBase->mBase; + vexp->mLeft->mToken = TK_MUL; + vexp->mLeft->mLeft = new Expression(mdec->mLocation, EX_VARIABLE); + vexp->mLeft->mLeft->mDecType = mdec->mBase->mParams->mBase; + vexp->mLeft->mLeft->mDecValue = mdec->mBase->mParams; + + vexp->mDecValue = new Declaration(mdec->mLocation, DT_ELEMENT); + vexp->mDecValue->mBase = TheCharTypeDeclaration; + vexp->mDecValue->mOffset = vdec->mOffset; + vexp->mDecValue->mSize = 1; + vexp->mDecType = TheCharTypeDeclaration; + + Expression* ecall = new Expression(mdec->mLocation, EX_DISPATCH); + ecall->mLeft = new Expression(mdec->mLocation, EX_INDEX); + ecall->mLeft->mDecType = mdec->mBase; + ecall->mDecType = vtabt->mBase; + ecall->mLeft->mLeft = new Expression(mdec->mLocation, EX_VARIABLE); + ecall->mLeft->mLeft->mDecType = vtabt; + ecall->mLeft->mLeft->mDecValue = vtaba; + ecall->mLeft->mRight = vexp; + + mdec->mFlags |= DTF_DEFINED; + mdec->mValue = ecall; + } + + mdec->mQualIdent = mdec->mQualIdent->Mangle("$vcall"); + }); + }); +} + +void Compiler::CompileProcedure(InterCodeProcedure* proc) +{ + if (!proc->mCompiled) + { + proc->mCompiled = true; + + for (int i = 0; i < proc->mCalledFunctions.Size(); i++) + CompileProcedure(proc->mCalledFunctions[i]); + + proc->MapCallerSavedTemps(); + + if (proc->mNativeProcedure) + { + NativeCodeProcedure* ncproc = new NativeCodeProcedure(mNativeCodeGenerator); + if (mCompilerOptions & COPT_VERBOSE2) + printf("Generate native code <%s>\n", proc->mIdent->mString); + + ncproc->Compile(proc); + mNativeCodeGenerator->mProcedures.Push(ncproc); + } + else + { + ByteCodeProcedure* bgproc = new ByteCodeProcedure(); + + if (mCompilerOptions & COPT_VERBOSE2) + printf("Generate byte code <%s>\n", proc->mIdent->mString); + + bgproc->Compile(mByteCodeGenerator, proc); + mByteCodeFunctions.Push(bgproc); + } + } +} + +bool Compiler::GenerateCode(void) +{ + Location loc; + + Declaration* dcrtstart = mCompilationUnits->mStartup; + if (!dcrtstart) + { + mErrors->Error(loc, EERR_RUNTIME_CODE, "Runtime startup not found"); + return false; + } + + const Ident* identStartup = Ident::Unique("startup"); + const Ident* identBytecode = Ident::Unique("bytecode"); + const Ident* identMain = Ident::Unique("main"); + const Ident* identRom = Ident::Unique("rom"); + const Ident* identBoot = Ident::Unique("boot"); + const Ident* identCode = Ident::Unique("code"); + const Ident* identZeroPage = Ident::Unique("zeropage"); + const Ident* identLowcode = Ident::Unique("lowcode"); + + LinkerRegion* regionZeroPage = mLinker->FindRegion(identZeroPage); + if (!regionZeroPage) + { + if (mTargetMachine == TMACH_ATARI) + regionZeroPage = mLinker->AddRegion(identZeroPage, 0x00e0, 0x00ff); + else if (mCompilerOptions & (COPT_EXTENDED_ZERO_PAGE | COPT_TARGET_NES)) + regionZeroPage = mLinker->AddRegion(identZeroPage, 0x0080, 0x00ff); + else if (mTargetMachine == TMACH_PET_8K || mTargetMachine == TMACH_PET_16K || mTargetMachine == TMACH_PET_32K) + regionZeroPage = mLinker->AddRegion(identZeroPage, 0x00ed, 0x00f7); + else + regionZeroPage = mLinker->AddRegion(identZeroPage, 0x00f7, 0x00ff); + } + + LinkerRegion* regionStartup = mLinker->FindRegion(identStartup); + LinkerRegion* regionLowcode = nullptr, * regionBoot = nullptr; + + if (!regionStartup) + { + if (mCompilerOptions & (COPT_TARGET_PRG | COPT_TARGET_NES)) + { + switch (mTargetMachine) + { + case TMACH_MEGA65: + if (mCompilerOptions & COPT_NATIVE) + regionStartup = mLinker->AddRegion(identStartup, 0x2001, 0x2080); + else + regionStartup = mLinker->AddRegion(identStartup, 0x2001, 0x2100); + break; + case TMACH_C64: + case TMACH_X16: + if (mCompilerOptions & COPT_NATIVE) + regionStartup = mLinker->AddRegion(identStartup, 0x0801, 0x0880); + else + regionStartup = mLinker->AddRegion(identStartup, 0x0801, 0x0900); + break; + case TMACH_C128: + if (mCompilerOptions & COPT_NATIVE) + { + regionStartup = mLinker->AddRegion(identStartup, 0x1c01, 0x1c80); + regionLowcode = mLinker->AddRegion(identLowcode, 0x1c80, 0x1d00); + } + else + { + regionStartup = mLinker->AddRegion(identStartup, 0x1c01, 0x1c80); + regionLowcode = mLinker->AddRegion(identLowcode, 0x1c80, 0x1d00); + } + regionLowcode->mSections.Push(mCompilationUnits->mSectionLowCode); + break; + case TMACH_C128B: + case TMACH_C128E: + if (mCompilerOptions & COPT_NATIVE) + regionStartup = mLinker->AddRegion(identStartup, 0x1c01, 0x1c80); + else + regionStartup = mLinker->AddRegion(identStartup, 0x1c01, 0x1d00); + break; + case TMACH_PLUS4: + if (mCompilerOptions & COPT_NATIVE) + { + regionStartup = mLinker->AddRegion(identStartup, 0x1001, 0x1080); + regionLowcode = mLinker->AddRegion(identLowcode, 0x1080, 0x1180); + } + else + { + regionStartup = mLinker->AddRegion(identStartup, 0x1001, 0x1080); + regionLowcode = mLinker->AddRegion(identLowcode, 0x1080, 0x1180); + } + regionLowcode->mSections.Push(mCompilationUnits->mSectionLowCode); + break; + case TMACH_VIC20: + if (mCompilerOptions & COPT_NATIVE) + regionStartup = mLinker->AddRegion(identStartup, 0x1001, 0x1080); + else + regionStartup = mLinker->AddRegion(identStartup, 0x1001, 0x1100); + break; + case TMACH_VIC20_3K: + case TMACH_PET_8K: + case TMACH_PET_16K: + case TMACH_PET_32K: + if (mCompilerOptions & COPT_NATIVE) + regionStartup = mLinker->AddRegion(identStartup, 0x0401, 0x0480); + else + regionStartup = mLinker->AddRegion(identStartup, 0x0401, 0x0500); + break; + case TMACH_VIC20_8K: + case TMACH_VIC20_16K: + case TMACH_VIC20_24K: + if (mCompilerOptions & COPT_NATIVE) + regionStartup = mLinker->AddRegion(identStartup, 0x1201, 0x1280); + else + regionStartup = mLinker->AddRegion(identStartup, 0x1201, 0x1300); + break; + case TMACH_ATARI: + if (mCompilerOptions & COPT_NATIVE) + regionStartup = mLinker->AddRegion(identStartup, 0x2000, 0x2080); + else + regionStartup = mLinker->AddRegion(identStartup, 0x2000, 0x2100); + break; + case TMACH_NES: + case TMACH_NES_NROM_H: + case TMACH_NES_NROM_V: + regionStartup = mLinker->AddRegion(identStartup, 0xff80, 0xfffa); + regionStartup->mCartridgeBanks = 1; + break; + case TMACH_NES_MMC1: + regionStartup = mLinker->AddRegion(identStartup, 0xff80, 0xfffa); + regionStartup->mCartridgeBanks = 1ULL << 15; + break; + case TMACH_NES_MMC3: + regionStartup = mLinker->AddRegion(identStartup, 0xff80, 0xfffa); + regionStartup->mCartridgeBanks = 1ULL << 31; + break; + } + } + else if (mCompilerOptions & (COPT_TARGET_CRT8 | COPT_TARGET_CRT16)) + { + regionStartup = mLinker->AddRegion(identStartup, 0x8000, 0x8080); + regionStartup->mCartridgeBanks = 1; + } + else + regionStartup = mLinker->AddRegion(identStartup, 0x0800, 0x0900); + } + + LinkerRegion* regionBytecode = nullptr; + if (!(mCompilerOptions & COPT_NATIVE)) + { + regionBytecode = mLinker->FindRegion(identBytecode); + if (!regionBytecode) + { + switch (mTargetMachine) + { + case TMACH_MEGA65: + regionBytecode = mLinker->AddRegion(identBytecode, 0x2100, 0x2200); + break; + case TMACH_C64: + case TMACH_X16: + regionBytecode = mLinker->AddRegion(identBytecode, 0x0900, 0x0a00); + break; + case TMACH_C128: + case TMACH_C128B: + case TMACH_C128E: + regionBytecode = mLinker->AddRegion(identBytecode, 0x1d00, 0x1e00); + break; + case TMACH_PLUS4: + regionBytecode = mLinker->AddRegion(identBytecode, 0x1200, 0x1300); + break; + case TMACH_VIC20: + regionBytecode = mLinker->AddRegion(identBytecode, 0x1100, 0x1200); + break; + case TMACH_VIC20_3K: + case TMACH_PET_8K: + case TMACH_PET_16K: + case TMACH_PET_32K: + regionBytecode = mLinker->AddRegion(identBytecode, 0x0500, 0x0600); + break; + case TMACH_VIC20_8K: + case TMACH_VIC20_16K: + case TMACH_VIC20_24K: + regionBytecode = mLinker->AddRegion(identBytecode, 0x1300, 0x1400); + break; + case TMACH_ATARI: + regionBytecode = mLinker->AddRegion(identBytecode, 0x2100, 0x2200); + break; + } + + } + } + + LinkerRegion* regionMain = mLinker->FindRegion(identMain); + LinkerRegion* regionRom = mLinker->FindRegion(identRom); + + LinkerSection * sectionStartup = mLinker->AddSection(identStartup, LST_DATA); + LinkerSection* sectionBytecode = nullptr; + if (regionBytecode) + { + sectionBytecode = mLinker->AddSection(identBytecode, LST_DATA); + } + + regionStartup->mSections.Push(sectionStartup); + + regionZeroPage->mSections.Push(mCompilationUnits->mSectionZeroPage); + + if (regionBytecode) + regionBytecode->mSections.Push(sectionBytecode); + + if (!mLinker->IsSectionPlaced(mCompilationUnits->mSectionCode)) + { + if (!regionMain) + { + if (!(mCompilerOptions & (COPT_TARGET_PRG | COPT_TARGET_NES))) + regionMain = mLinker->AddRegion(identMain, 0x0900, 0x4700); + else if (regionBytecode) + { + switch (mTargetMachine) + { + case TMACH_MEGA65: + regionMain = mLinker->AddRegion(identMain, 0x2300, 0xc000); + break; + case TMACH_C64: + regionMain = mLinker->AddRegion(identMain, 0x0a00, 0xa000); + break; + case TMACH_X16: + regionMain = mLinker->AddRegion(identMain, 0x0a00, 0x9f00); + break; + case TMACH_C128: + regionMain = mLinker->AddRegion(identMain, 0x1e00, 0xfe00); + break; + case TMACH_C128B: + regionMain = mLinker->AddRegion(identMain, 0x1e00, 0x4000); + break; + case TMACH_C128E: + regionMain = mLinker->AddRegion(identMain, 0x1e00, 0xc000); + break; + case TMACH_PLUS4: + regionMain = mLinker->AddRegion(identMain, 0x1300, 0xfc00); + break; + case TMACH_VIC20: + regionMain = mLinker->AddRegion(identMain, 0x1200, 0x1e00); + break; + case TMACH_VIC20_3K: + regionMain = mLinker->AddRegion(identMain, 0x0600, 0x1e00); + break; + case TMACH_VIC20_8K: + regionMain = mLinker->AddRegion(identMain, 0x1400, 0x4000); + break; + case TMACH_VIC20_16K: + regionMain = mLinker->AddRegion(identMain, 0x1400, 0x6000); + break; + case TMACH_VIC20_24K: + regionMain = mLinker->AddRegion(identMain, 0x1400, 0x8000); + break; + case TMACH_PET_8K: + regionMain = mLinker->AddRegion(identMain, 0x0600, 0x2000); + break; + case TMACH_PET_16K: + regionMain = mLinker->AddRegion(identMain, 0x0600, 0x4000); + break; + case TMACH_PET_32K: + regionMain = mLinker->AddRegion(identMain, 0x0600, 0x8000); + break; + case TMACH_ATARI: + regionMain = mLinker->AddRegion(identMain, 0x2200, 0xbc00); + break; + } + } + else + { + switch (mTargetMachine) + { + case TMACH_MEGA65: + // TODO: Disable M65 cartridges for now. + // + // if (mCompilerOptions & (COPT_TARGET_CRT8 | COPT_TARGET_CRT16)) + // regionMain = mLinker->AddRegion(identMain, 0x2666, 0xff00); + // else + regionMain = mLinker->AddRegion(identMain, 0x2080, 0xc000); + break; + case TMACH_C64: + + if (mCompilerOptions & (COPT_TARGET_CRT8 | COPT_TARGET_CRT16)) + regionMain = mLinker->AddRegion(identMain, 0x0800, 0x8000); + else + regionMain = mLinker->AddRegion(identMain, 0x0880, 0xa000); + break; + case TMACH_X16: + regionMain = mLinker->AddRegion(identMain, 0x0880, 0x9f00); + break; + case TMACH_C128: + regionMain = mLinker->AddRegion(identMain, 0x1d00, 0xfe00); + break; + case TMACH_C128B: + regionMain = mLinker->AddRegion(identMain, 0x1c80, 0x4000); + break; + case TMACH_C128E: + regionMain = mLinker->AddRegion(identMain, 0x1c80, 0xc000); + break; + case TMACH_PLUS4: + regionMain = mLinker->AddRegion(identMain, 0x1180, 0xfc00); + break; + case TMACH_VIC20: + regionMain = mLinker->AddRegion(identMain, 0x1080, 0x1e00); + break; + case TMACH_VIC20_3K: + regionMain = mLinker->AddRegion(identMain, 0x0580, 0x1e00); + break; + case TMACH_VIC20_8K: + regionMain = mLinker->AddRegion(identMain, 0x1280, 0x4000); + break; + case TMACH_VIC20_16K: + regionMain = mLinker->AddRegion(identMain, 0x1280, 0x6000); + break; + case TMACH_VIC20_24K: + regionMain = mLinker->AddRegion(identMain, 0x1280, 0x8000); + break; + case TMACH_PET_8K: + regionMain = mLinker->AddRegion(identMain, 0x0480, 0x2000); + break; + case TMACH_PET_16K: + regionMain = mLinker->AddRegion(identMain, 0x0480, 0x4000); + break; + case TMACH_PET_32K: + regionMain = mLinker->AddRegion(identMain, 0x0480, 0x8000); + break; + case TMACH_ATARI: + regionMain = mLinker->AddRegion(identMain, 0x2080, 0xbc00); + break; + case TMACH_NES: + case TMACH_NES_NROM_H: + case TMACH_NES_NROM_V: + regionBoot = mLinker->AddRegion(identBoot, 0xfffa, 0x10000); + regionBoot->mCartridgeBanks = 1; + regionBoot->mSections.Push(mCompilationUnits->mSectionBoot); + regionRom = mLinker->AddRegion(identRom, 0x8000, 0xff80); + regionRom->mCartridgeBanks = 1; + regionMain = mLinker->AddRegion(identMain, 0x0200, 0x0800); + break; + case TMACH_NES_MMC1: + regionBoot = mLinker->AddRegion(identBoot, 0xfffa, 0x10000); + regionBoot->mCartridgeBanks = 1ULL << 15; + regionBoot->mSections.Push(mCompilationUnits->mSectionBoot); + regionRom = mLinker->AddRegion(identRom, 0xc000, 0xff80); + regionRom->mCartridgeBanks = 1ULL << 15; + regionMain = mLinker->AddRegion(identMain, 0x0200, 0x0800); + break; + case TMACH_NES_MMC3: + regionBoot = mLinker->AddRegion(identBoot, 0xfffa, 0x10000); + regionBoot->mCartridgeBanks = 1ULL << 31; + regionBoot->mSections.Push(mCompilationUnits->mSectionBoot); + regionRom = mLinker->AddRegion(identRom, 0xc000, 0xff80); + regionRom->mCartridgeBanks = 1ULL << 31; + regionMain = mLinker->AddRegion(identMain, 0x0200, 0x0800); + break; + } + } + } + + if (!regionRom) + { + if (mCompilerOptions & COPT_TARGET_CRT8) + { + regionRom = mLinker->AddRegion(identRom, 0x8080, 0xa000); + regionRom->mCartridgeBanks = 1; + } + else if (mCompilerOptions & COPT_TARGET_CRT16) + { + regionRom = mLinker->AddRegion(identRom, 0x8080, 0xc000); + regionRom->mCartridgeBanks = 1; + } + } + + if (regionRom) + { + regionRom->mSections.Push(mCompilationUnits->mSectionCode); + regionRom->mSections.Push(mCompilationUnits->mSectionData); + } + else + { + regionMain->mSections.Push(mCompilationUnits->mSectionCode); + regionMain->mSections.Push(mCompilationUnits->mSectionData); + } + + regionMain->mSections.Push(mCompilationUnits->mSectionBSS); + regionMain->mSections.Push(mCompilationUnits->mSectionHeap); + regionMain->mSections.Push(mCompilationUnits->mSectionStack); + } + + mInterCodeModule->InitParamStack(mCompilationUnits->mSectionStack); + + dcrtstart->mSection = sectionStartup; + + if (mCompilerOptions & COPT_CPLUSPLUS) + { + if (mCompilerOptions & COPT_VERBOSE) + printf("Build VTables\n"); + + BuildVTables(); + CheckOperatorNew(); + + PreCompileDeclaration(dcrtstart); + } + + if (mCompilerOptions & COPT_OPTIMIZE_GLOBAL) + { + mGlobalOptimizer->mCompilerOptions = mCompilerOptions; + + if (mCompilerOptions & COPT_VERBOSE) + printf("Global optimizer\n"); + + do { + mGlobalOptimizer->Reset(); + + mGlobalOptimizer->AnalyzeAssembler(dcrtstart->mValue, nullptr); + + for (int i = 0; i < mCompilationUnits->mReferenced.Size(); i++) + { + Declaration* dec = mCompilationUnits->mReferenced[i]; + if (dec->mType == DT_CONST_FUNCTION) + mGlobalOptimizer->AnalyzeProcedure(dec->mValue, dec); + else + mGlobalOptimizer->AnalyzeGlobalVariable(dec); + } + } while (mGlobalOptimizer->Optimize()); + + } + + mGlobalAnalyzer->mCompilerOptions = mCompilerOptions; + + if (mCompilerOptions & COPT_VERBOSE) + printf("Global analyzer\n"); + + mGlobalAnalyzer->AnalyzeAssembler(dcrtstart->mValue, nullptr); + + for (int i = 0; i < mCompilationUnits->mReferenced.Size(); i++) + { + Declaration* dec = mCompilationUnits->mReferenced[i]; + if (dec->mType == DT_CONST_FUNCTION) + mGlobalAnalyzer->AnalyzeProcedure(nullptr, dec->mValue, dec); + else + mGlobalAnalyzer->AnalyzeGlobalVariable(dec); + } + + mGlobalAnalyzer->CheckInterrupt(); + mGlobalAnalyzer->MarkRecursions(); + mGlobalAnalyzer->AutoInline(); + mGlobalAnalyzer->AutoZeroPage(mCompilationUnits->mSectionZeroPage, regionZeroPage->mEnd - regionZeroPage->mStart); + if (mCompilerOptions & COPT_VERBOSE3) + mGlobalAnalyzer->DumpCallGraph(); + + mInterCodeGenerator->mCompilerOptions = mCompilerOptions; + mNativeCodeGenerator->mCompilerOptions = mCompilerOptions; + mInterCodeModule->mCompilerOptions = mCompilerOptions; + + if (mCompilerOptions & COPT_VERBOSE) + printf("Generate intermediate code\n"); + + mInterCodeGenerator->TranslateAssembler(mInterCodeModule, dcrtstart, nullptr); + for (int i = 0; i < mCompilationUnits->mReferenced.Size(); i++) + { + Declaration* dec = mCompilationUnits->mReferenced[i]; + if (dec->mType == DT_CONST_FUNCTION) + { + if (!dec->mLinkerObject) + mInterCodeGenerator->TranslateProcedure(mInterCodeModule, dec->mValue, dec); + } + } + for (int i = 0; i < mCompilationUnits->mReferenced.Size(); i++) + { + Declaration* dec = mCompilationUnits->mReferenced[i]; + if (dec->mType != DT_CONST_FUNCTION) + { + if (!dec->mLinkerObject) + mInterCodeGenerator->InitGlobalVariable(mInterCodeModule, dec); + } + } + + mInterCodeGenerator->CompleteMainInit(mInterCodeModule); + + if (mErrors->mErrorCount != 0) + return false; + + // Register native runtime functions + + if (mInterCodeModule->mProcedures.Size() > 0) + { + RegisterRuntime(loc, Ident::Unique("mul16by8")); + RegisterRuntime(loc, Ident::Unique("mul32by8")); + RegisterRuntime(loc, Ident::Unique("fsplitt")); + RegisterRuntime(loc, Ident::Unique("fsplitx")); + RegisterRuntime(loc, Ident::Unique("fsplita")); + RegisterRuntime(loc, Ident::Unique("fadd")); + RegisterRuntime(loc, Ident::Unique("fsub")); + RegisterRuntime(loc, Ident::Unique("fmul")); + RegisterRuntime(loc, Ident::Unique("fdiv")); + RegisterRuntime(loc, Ident::Unique("mul16")); + RegisterRuntime(loc, Ident::Unique("divs16")); + RegisterRuntime(loc, Ident::Unique("mods16")); + RegisterRuntime(loc, Ident::Unique("divu16")); + RegisterRuntime(loc, Ident::Unique("modu16")); + RegisterRuntime(loc, Ident::Unique("divmods16")); + RegisterRuntime(loc, Ident::Unique("bitshift")); + RegisterRuntime(loc, Ident::Unique("ffloor")); + RegisterRuntime(loc, Ident::Unique("fceil")); + RegisterRuntime(loc, Ident::Unique("ftoi")); + RegisterRuntime(loc, Ident::Unique("ftou")); + RegisterRuntime(loc, Ident::Unique("ftoli")); + RegisterRuntime(loc, Ident::Unique("ftolu")); + RegisterRuntime(loc, Ident::Unique("ffromi")); + RegisterRuntime(loc, Ident::Unique("ffromu")); + RegisterRuntime(loc, Ident::Unique("ffromli")); + RegisterRuntime(loc, Ident::Unique("ffromlu")); + RegisterRuntime(loc, Ident::Unique("fcmp")); + RegisterRuntime(loc, Ident::Unique("bcexec")); + RegisterRuntime(loc, Ident::Unique("jmpaddr")); + RegisterRuntime(loc, Ident::Unique("mul32")); + RegisterRuntime(loc, Ident::Unique("divs32")); + RegisterRuntime(loc, Ident::Unique("mods32")); + RegisterRuntime(loc, Ident::Unique("divu32")); + RegisterRuntime(loc, Ident::Unique("modu32")); + RegisterRuntime(loc, Ident::Unique("divmods32")); + RegisterRuntime(loc, Ident::Unique("divu16by8")); + + RegisterRuntime(loc, Ident::Unique("store32")); + RegisterRuntime(loc, Ident::Unique("load32")); + + RegisterRuntime(loc, Ident::Unique("malloc")); + RegisterRuntime(loc, Ident::Unique("free")); + RegisterRuntime(loc, Ident::Unique("breakpoint")); + } + + // Register extended byte code functions + + for (int i = 0; i < 128; i++) + { + Declaration* bcdec = mCompilationUnits->mByteCodes[i + 128]; + if (bcdec) + { + LinkerObject* linkerObject = nullptr; + + int offset = 0; + if (bcdec->mType == DT_CONST_ASSEMBLER) + { + if (!bcdec->mLinkerObject) + mInterCodeGenerator->TranslateAssembler(mInterCodeModule, bcdec, nullptr); + mByteCodeGenerator->mExtByteCodes[i] = bcdec->mLinkerObject; + } + } + } + + if (mErrors->mErrorCount != 0) + return false; + + if (mCompilerOptions & COPT_VERBOSE) + printf("Optimize static variable usage\n"); + +#if 1 + for (int i = 0; i < mInterCodeModule->mProcedures.Size(); i++) + { + mInterCodeModule->mProcedures[i]->MarkRelevantStatics(); + } + + for (int i = 0; i < mInterCodeModule->mProcedures.Size(); i++) + { + mInterCodeModule->mProcedures[i]->RemoveNonRelevantStatics(); + } +#endif + + if (mCompilerOptions & COPT_VERBOSE) + printf("Generate native code\n"); + + for (int i = 0; i < mInterCodeModule->mProcedures.Size(); i++) + { + InterCodeProcedure* proc = mInterCodeModule->mProcedures[i]; + +// proc->ReduceTemporaries(); + +#if _DEBUG + proc->Disassemble("final"); +#endif + + CompileProcedure(proc); + + if (proc->mLinkerObject->mStackSection) + mCompilationUnits->mSectionStack->mSections.Push(proc->mLinkerObject->mStackSection); + } + + mNativeCodeGenerator->OutlineFunctions(); + + mNativeCodeGenerator->BuildFunctionProxies(); + + for (int i = 0; i < mNativeCodeGenerator->mProcedures.Size(); i++) + { + if (mCompilerOptions & COPT_VERBOSE2) + { + if (mNativeCodeGenerator->mProcedures[i]->mInterProc) + printf("Assemble native code <%s>\n", mNativeCodeGenerator->mProcedures[i]->mInterProc->mIdent->mString); + } + mNativeCodeGenerator->mProcedures[i]->Assemble(); + } + + LinkerObject* byteCodeObject = nullptr; + if (!(mCompilerOptions & COPT_NATIVE)) + { + if (mCompilerOptions & COPT_VERBOSE) + printf("Generate byte code runtime\n"); + + // Compile used byte code functions + + byteCodeObject = mLinker->AddObject(loc, Ident::Unique("bytecode"), sectionBytecode, LOT_RUNTIME); + + for (int i = 0; i < 128; i++) + { + if (mByteCodeGenerator->mByteCodeUsed[i] > 0) + { + Declaration* bcdec = mCompilationUnits->mByteCodes[i]; + if (bcdec) + { + LinkerObject* linkerObject = nullptr; + + int offset = 0; + if (bcdec->mType == DT_CONST_ASSEMBLER) + { + if (!bcdec->mLinkerObject) + mInterCodeGenerator->TranslateAssembler(mInterCodeModule, bcdec, nullptr); + linkerObject = bcdec->mLinkerObject; + } + else if (bcdec->mType == DT_LABEL) + { + if (!bcdec->mBase->mLinkerObject) + mInterCodeGenerator->TranslateAssembler(mInterCodeModule, bcdec->mBase, nullptr); + linkerObject = bcdec->mBase->mLinkerObject; + offset = int(bcdec->mInteger); + } + + assert(linkerObject); + + LinkerReference lref; + lref.mObject = byteCodeObject; + lref.mFlags = LREF_HIGHBYTE | LREF_LOWBYTE; + lref.mOffset = 2 * i; + lref.mRefObject = linkerObject; + lref.mRefOffset = offset; + byteCodeObject->AddReference(lref); + } + else + { + char n[10]; + sprintf_s(n, "%d", i); + mErrors->Error(loc, EERR_RUNTIME_CODE, "Missing byte code implementation", n); + } + } + } + } + + mNativeCodeGenerator->CompleteRuntime(); + + mLinker->CollectReferences(); + + mLinker->ReferenceObject(dcrtstart->mLinkerObject); + + if (!(mCompilerOptions & COPT_NATIVE)) + mLinker->ReferenceObject(byteCodeObject); + + for (int i = 0; i < mCompilationUnits->mReferenced.Size(); i++) + mLinker->ReferenceObject(mCompilationUnits->mReferenced[i]->mLinkerObject); + + if (mCompilerOptions & COPT_OPTIMIZE_BASIC) + { + mLinker->CombineSameConst(); + mLinker->InlineSimpleJumps(); + mLinker->CheckDirectJumps(); + } + + if (mCompilerOptions & COPT_VERBOSE) + printf("Link executable\n"); + + mLinker->Link(); + + return mErrors->mErrorCount == 0; +} + +bool Compiler::BuildLZO(const char* targetPath) +{ + mPreprocessor->mCompilerOptions = mCompilerOptions; + mLinker->mCompilerOptions = mCompilerOptions; + + CompilationUnit* cunit; + + char * data = new char[65536]; + int n = 0; + + while (mErrors->mErrorCount == 0 && (cunit = mCompilationUnits->PendingUnit())) + { + if (mPreprocessor->EmbedData("Compressing", cunit->mFileName, true, 0, 65536, SFM_BINARY_LZO, SFD_NONE)) + { + Scanner* scanner = new Scanner(mErrors, mPreprocessor); + while (scanner->mToken == TK_INTEGER) + { + if (scanner->mTokenInteger < 0 || scanner->mTokenInteger > 255) + mErrors->Error(scanner->mLocation, EWARN_CONSTANT_TRUNCATED, "Constant integer truncated"); + data[n++] = uint8(scanner->mTokenInteger); + do { + scanner->NextToken(); + } while (scanner->mToken == TK_COMMA); + } + } + else + mErrors->Error(cunit->mLocation, EERR_FILE_NOT_FOUND, "Could not open source file", cunit->mFileName); + } + + if (mErrors->mErrorCount == 0) + { + char prgPath[200]; + + strcpy_s(prgPath, targetPath); + ptrdiff_t i = strlen(prgPath); + while (i > 0 && prgPath[i - 1] != '.') + i--; + if (i > 0) + prgPath[i] = 0; + + strcat_s(prgPath, "lzo"); + if (mCompilerOptions & COPT_VERBOSE) + printf("Writing <%s>\n", prgPath); + + FILE* file; + fopen_s(&file, prgPath, "wb"); + if (file) + { + ptrdiff_t done = fwrite(data, 1, n, file); + fclose(file); + delete[] data; + return done == n; + } + else + { + delete[] data; + return false; + } + } + else + { + delete[] data; + return false; + } +} + +bool Compiler::RemoveErrorFile(const char* targetPath) +{ + char prgPath[200], mapPath[200], asmPath[200], intPath[200]; + char basePath[200]; + + strcpy_s(basePath, targetPath); + ptrdiff_t i = strlen(basePath); + while (i > 0 && basePath[i - 1] != '/' && basePath[i - 1] != '\\' && basePath[i - 1] != ':') + i--; + if (i > 0) + basePath[i] = 0; + + strcpy_s(prgPath, targetPath); + i = strlen(prgPath); + while (i > 0 && prgPath[i - 1] != '.') + i--; + if (i > 0) + prgPath[i] = 0; + + strcpy_s(mapPath, prgPath); + strcpy_s(asmPath, prgPath); + strcpy_s(intPath, prgPath); + + strcat_s(mapPath, "error.map"); + strcat_s(asmPath, "error.asm"); + strcat_s(intPath, "error.int"); + + remove(mapPath); + remove(asmPath); + remove(intPath); + + return true; +} + +bool Compiler::WriteErrorFile(const char* targetPath) +{ + char prgPath[200], mapPath[200], asmPath[200], intPath[200]; + char basePath[200]; + + strcpy_s(basePath, targetPath); + ptrdiff_t i = strlen(basePath); + while (i > 0 && basePath[i - 1] != '/' && basePath[i - 1] != '\\' && basePath[i - 1] != ':') + i--; + if (i > 0) + basePath[i] = 0; + + strcpy_s(prgPath, targetPath); + i = strlen(prgPath); + while (i > 0 && prgPath[i - 1] != '.') + i--; + if (i > 0) + prgPath[i] = 0; + + strcpy_s(mapPath, prgPath); + strcpy_s(asmPath, prgPath); + strcpy_s(intPath, prgPath); + + strcat_s(mapPath, "error.map"); + strcat_s(asmPath, "error.asm"); + strcat_s(intPath, "error.int"); + + if (mCompilerOptions & COPT_VERBOSE) + printf("Writing <%s>\n", mapPath); + mLinker->WriteMapFile(mapPath); + + if (mCompilerOptions & COPT_VERBOSE) + printf("Writing <%s>\n", asmPath); + mLinker->WriteAsmFile(asmPath, mVersion); + + if (mCompilerOptions & COPT_VERBOSE) + printf("Writing <%s>\n", intPath); + mInterCodeModule->Disassemble(intPath); + + return true; +} + +bool Compiler::WriteOutputFile(const char* targetPath, DiskImage * d64) +{ + char prgPath[200], mapPath[200], asmPath[200], lblPath[200], intPath[200], bcsPath[200], dbjPath[200], cszPath[200]; + char basePath[200]; + + strcpy_s(basePath, targetPath); + ptrdiff_t i = strlen(basePath); + while (i > 0 && basePath[i - 1] != '/' && basePath[i - 1] != '\\' && basePath[i - 1] != ':') + i--; + if (i > 0) + basePath[i] = 0; + + strcpy_s(prgPath, targetPath); + i = strlen(prgPath); + while (i > 0 && prgPath[i - 1] != '.') + i--; + if (i > 0) + prgPath[i] = 0; + + strcpy_s(mapPath, prgPath); + strcpy_s(asmPath, prgPath); + strcpy_s(lblPath, prgPath); + strcpy_s(intPath, prgPath); + strcpy_s(bcsPath, prgPath); + strcpy_s(dbjPath, prgPath); + strcpy_s(cszPath, prgPath); + + strcat_s(mapPath, "map"); + strcat_s(asmPath, "asm"); + strcat_s(lblPath, "lbl"); + strcat_s(intPath, "int"); + strcat_s(bcsPath, "bcs"); + strcat_s(dbjPath, "dbj"); + strcat_s(cszPath, "csz"); + + if (mCompilerOptions & COPT_TARGET_PRG) + { + if (mTargetMachine == TMACH_ATARI) + { + strcat_s(prgPath, "xex"); + if (mCompilerOptions & COPT_VERBOSE) + printf("Writing <%s>\n", prgPath); + mLinker->WriteXexFile(prgPath); + } + else + { + strcat_s(prgPath, "prg"); + if (mCompilerOptions & COPT_VERBOSE) + printf("Writing <%s>\n", prgPath); + mLinker->WritePrgFile(prgPath, basePath); + } + } + else if (mCompilerOptions & COPT_TARGET_CRT) + { + strcat_s(prgPath, "crt"); + if (mCompilerOptions & COPT_VERBOSE) + printf("Writing <%s>\n", prgPath); + mLinker->WriteCrtFile(prgPath, mCartridgeID, mCartridgeSubType, mCartridgeName); + } + else if (mCompilerOptions & COPT_TARGET_BIN) + { + strcat_s(prgPath, "bin"); + if (mCompilerOptions & COPT_VERBOSE) + printf("Writing <%s>\n", prgPath); + mLinker->WriteBinFile(prgPath); + } + else if (mCompilerOptions & COPT_TARGET_NES) + { + strcpy_s(lblPath, prgPath); + strcat_s(lblPath, "mlb"); + + strcat_s(prgPath, "nes"); + if (mCompilerOptions & COPT_VERBOSE) + printf("Writing <%s>\n", prgPath); + mLinker->WriteNesFile(prgPath, mTargetMachine); + + } + + + if (d64) + { + ptrdiff_t i = strlen(prgPath); + while (i > 0 && prgPath[i - 1] != '.') + i--; + if (i > 0) + prgPath[i - 1] = 0; + + while (i > 0 && prgPath[i - 1] != '/' && prgPath[i - 1] != '\\') + i--; + + mLinker->WritePrgFile(d64, prgPath + i); + } + + if (mCompilerOptions & COPT_VERBOSE) + printf("Writing <%s>\n", mapPath); + mLinker->WriteMapFile(mapPath); + + if (mCompilerOptions & COPT_VERBOSE) + printf("Writing <%s>\n", asmPath); + mLinker->WriteAsmFile(asmPath, mVersion); + + if (mCompilerOptions & COPT_VERBOSE) + printf("Writing <%s>\n", lblPath); + + if (mCompilerOptions & COPT_TARGET_NES) + mLinker->WriteMlbFile(lblPath, mTargetMachine); + else + mLinker->WriteLblFile(lblPath); + + if (mCompilerOptions & COPT_VERBOSE) + printf("Writing <%s>\n", intPath); + mInterCodeModule->Disassemble(intPath); + + if (mCompilerOptions & COPT_DEBUGINFO) + WriteDbjFile(dbjPath); + + if (mCompilerOptions & COPT_PROFILEINFO) + WriteCszFile(cszPath); + + if (!(mCompilerOptions & COPT_NATIVE)) + { + if (mCompilerOptions & COPT_VERBOSE) + printf("Writing <%s>\n", bcsPath); + mByteCodeGenerator->WriteByteCodeStats(bcsPath); + } + + return true; +} + +int Compiler::ExecuteCode(bool profile, int trace, bool asserts, bool iorange) +{ + Location loc; + + printf("Running emulation...\n"); + Emulator* emu = new Emulator(mLinker); + + if (mCompilerOptions & COPT_EXTENDED_ZERO_PAGE) + emu->mJiffies = false; + + int ecode = 20; + if (mCompilerOptions & COPT_TARGET_PRG) + { + memcpy(emu->mMemory + mLinker->mProgramStart, mLinker->mMemory + mLinker->mProgramStart, mLinker->mProgramEnd - mLinker->mProgramStart); + emu->mMemory[0x2d] = mLinker->mProgramEnd & 0xff; + emu->mMemory[0x2e] = mLinker->mProgramEnd >> 8; + LinkerObject* oexit = nullptr; + if (asserts) + oexit = mLinker->FindObjectByName("exit"); + + ecode = emu->Emulate(2061, (oexit ? oexit->mAddress : 0x0000), trace, iorange); + } + else if (mCompilerOptions & COPT_TARGET_CRT) + { + memcpy(emu->mMemory + 0x8000, mLinker->mMemory + 0x0800, 0x4000); + ecode = emu->Emulate(0x8009, 0x0000, trace, iorange); + } + + printf("Emulation result %d\n", ecode); + + if (profile) + emu->DumpProfile(); + + if (ecode != 0) + { + char sd[20]; + sprintf_s(sd, "%d", ecode); + mErrors->Error(loc, EERR_EXECUTION_FAILED, "Execution failed", sd); + } + + return ecode; +} + +static void DumpReferences(FILE* file, Declaration* dec) +{ + if (dec) + { + fprintf(file, ", \"references\": ["); + fprintf(file, "\n\t\t\t{\"source\": \"%s\", \"line\": %d, \"column\": %d}", dec->mLocation.mFileName, dec->mLocation.mLine, dec->mLocation.mColumn); + for (int i = 0; i < dec->mReferences.Size(); i++) + { + fprintf(file, ","); + Expression* exp = dec->mReferences[i]; + fprintf(file, "\n\t\t\t{\"source\": \"%s\", \"line\": %d, \"column\": %d}", exp->mLocation.mFileName, exp->mLocation.mLine, exp->mLocation.mColumn); + } + fprintf(file, "]"); + } + +} + +bool Compiler::WriteCszFile(const char* filename) +{ + FILE* file; + fopen_s(&file, filename, "wb"); + if (file) + { + for (int i = 0; i < mInterCodeModule->mProcedures.Size(); i++) + { + InterCodeProcedure* p(mInterCodeModule->mProcedures[i]); + if (p->mLinkerObject && p->mIdent && p->mDeclaration) + { + LinkerObject* lo = p->mLinkerObject; + + struct SourceCount + { + const char* mFileName; + int mLine, mAddress; + int mBytes; + }; + + ExpandingArray ea; + + for (int j = 0; j < lo->mCodeOrigins.Size(); j++) + { + const CodeLocation& co(lo->mCodeOrigins[j]); + const Location* ls = &(co.mLocation); + while (ls->mFrom) + ls = ls->mFrom; + + int k = 0; + while (k < ea.Size() && (ea[k].mFileName != ls->mFileName || ea[k].mLine != ls->mLine)) + k++; + if (k == ea.Size()) + { + SourceCount sc; + sc.mFileName = ls->mFileName; + sc.mLine = ls->mLine; + sc.mBytes = 0; + sc.mAddress = co.mStart + lo->mAddress; + ea.Push(sc); + } + + ea[k].mBytes += co.mEnd - co.mStart; + } + + if (ea.Size()) + { + ea.Sort([](const SourceCount& l, const SourceCount& r)->bool { + return l.mFileName == r.mFileName ? l.mLine < r.mLine : l.mAddress < r.mAddress; + }); + + FILE* fsrc; + fopen_s(&fsrc, ea[0].mFileName, "r"); + if (fsrc) + { + fprintf(file, "<%s, %s>\n", p->mDeclaration->mQualIdent->mString, ea[0].mFileName); +#if 0 + for (int i = 0; i < ea.Size(); i++) + { + fprintf(file, "%s:%d : %d\n", ea[i].mFileName, ea[i].mLine, ea[i].mBytes); + } +#endif + int k = 0, l = 1; + char line[1024]; + while (k < ea.Size() && fgets(line, 1024, fsrc)) + { + size_t ll = strlen(line); + while (ll > 0 && (line[ll - 1] == '\r' || line[ll - 1] == '\n')) + ll--; + line[ll] = 0; + + if (l < ea[k].mLine) + { + if (k > 0) + fprintf(file, "%5d,---- ( ) : %s\n", l, line); + } + else + { + int ks = k, sum = 0; + do { + sum += ea[ks].mBytes; + ks++; + } while (ks < ea.Size() && ea[ks].mFileName != ea[0].mFileName); + + fprintf(file, "%5d,%04x (%3d) : %s\n", l, ea[k].mAddress, ea[k].mBytes, line); + + k = ks; + } + l++; + } + + fclose(fsrc); + fprintf(file, "\n"); + } + } + } + } + + fclose(file); + return true; + } + else + return false; +} + +bool Compiler::WriteDbjFile(const char* filename) +{ + FILE* file; + fopen_s(&file, filename, "wb"); + if (file) + { + fprintf(file, "{"); + mLinker->WriteDbjFile(file); + fprintf(file, ",\n"); + + ExpandingArray types; + + fprintf(file, "\t\"variables\": [\n"); + bool first = true; + for (int i = 0; i < mInterCodeModule->mGlobalVars.Size(); i++) + { + InterVariable* v(mInterCodeModule->mGlobalVars[i]); + if (v->mLinkerObject && v->mIdent && v->mDeclaration) + { + if (v->mLinkerObject->mSection->mType != LST_STATIC_STACK && v->mLinkerObject->mFlags & LOBJF_PLACED) + { + if (!first) + fprintf(file, ",\n"); + first = false; + + fprintf(file, "\t\t{\"name\": \"%s\", \"start\": %d, \"end\": %d, \"typeid\": %d", v->mIdent->mString, v->mLinkerObject->mAddress, v->mLinkerObject->mAddress + v->mLinkerObject->mSize, types.IndexOrPush(v->mDeclaration->mBase)); + DumpReferences(file, v->mDeclaration); + fprintf(file, "}"); + } + } + } + fprintf(file, "\t],\n"); + + fprintf(file, "\t\"functions\": [\n"); + first = true; + for (int i = 0; i < mInterCodeModule->mProcedures.Size(); i++) + { + InterCodeProcedure* p(mInterCodeModule->mProcedures[i]); + if (p->mLinkerObject && p->mIdent && p->mDeclaration) + { + if (!first) + fprintf(file, ",\n"); + first = false; + + fprintf(file, "\t\t{\"name\": \"%s\", \"xname\": \"%s\", \"start\": %d, \"end\": %d, \"typeid\": %d, \"source\": \"%s\", \"line\": %d, \"lines\": [\n", + p->mIdent->mString, p->mLinkerObject->mFullIdent->mString, p->mLinkerObject->mAddress, p->mLinkerObject->mAddress + p->mLinkerObject->mSize, types.IndexOrPush(p->mDeclaration->mBase), + p->mLocation.mFileName, p->mLocation.mLine); + + bool lfirst = true; + LinkerObject* lo = p->mLinkerObject; + + for (int j = 0; j < lo->mCodeLocations.Size(); j++) + { + if (!lfirst) + fprintf(file, ",\n"); + lfirst = false; + + fprintf(file, "\t\t\t{\"start\": %d, \"end\": %d, \"source\": \"%s\", \"line\": %d}", + lo->mCodeLocations[j].mStart + lo->mAddress, + lo->mCodeLocations[j].mEnd + lo->mAddress, + lo->mCodeLocations[j].mLocation.mFileName, + lo->mCodeLocations[j].mLocation.mLine); + } + fprintf(file, "], \n\t\t\t\"origins\":[\n"); + lfirst = true; + + for (int j = 0; j < lo->mCodeOrigins.Size(); j++) + { + const Location* loc = &(lo->mCodeOrigins[j].mLocation); + while (loc) + { + if (!lfirst) + fprintf(file, ",\n"); + lfirst = false; + + fprintf(file, "\t\t\t{\"start\": %d, \"end\": %d, \"source\": \"%s\", \"line\": %d}", + lo->mCodeOrigins[j].mStart + lo->mAddress, + lo->mCodeOrigins[j].mEnd + lo->mAddress, + loc->mFileName, + loc->mLine); + loc = loc->mFrom; + } + } + + fprintf(file, "], \n\t\t\t\"variables\":[\n"); + + bool vfirst = true; + for (int i = 0; i < p->mParamVars.Size(); i++) + { + InterVariable* v(p->mParamVars[i]); + if (v && v->mIdent) + { + if (!vfirst) + fprintf(file, ",\n"); + vfirst = false; + + if (p->mFastCallProcedure) + fprintf(file, "\t\t\t{\"name\": \"%s\", \"start\": %d, \"end\": %d, \"typeid\": %d}", v->mIdent->mString, i + BC_REG_FPARAMS, i + BC_REG_FPARAMS + v->mSize, types.IndexOrPush(v->mDeclaration->mBase)); + else if (p->mFramePointer) + fprintf(file, "\t\t\t{\"name\": \"%s\", \"start\": %d, \"end\": %d, \"base\": %d, \"typeid\": %d}", v->mIdent->mString, v->mOffset, v->mOffset + v->mSize, BC_REG_LOCALS, types.IndexOrPush(v->mDeclaration->mBase)); + else + fprintf(file, "\t\t\t{\"name\": \"%s\", \"start\": %d, \"end\": %d, \"base\": %d, \"typeid\": %d}", v->mIdent->mString, v->mOffset, v->mOffset + v->mSize, BC_REG_STACK, types.IndexOrPush(v->mDeclaration->mBase)); + } + } + for (int i = 0; i < p->mLocalVars.Size(); i++) + { + InterVariable* v(p->mLocalVars[i]); + if (v && v->mIdent) + { + bool skipped = false; + if (v->mLinkerObject) + { + if (v->mLinkerObject->mFlags & LOBJF_PLACED) + { + if (!vfirst) + fprintf(file, ",\n"); + vfirst = false; + + fprintf(file, "\t\t{\"name\": \"%s\", \"start\": %d, \"end\": %d, \"enter\": %d, \"leave\": %d, \"typeid\": %d", + v->mIdent->mString, v->mLinkerObject->mAddress, v->mLinkerObject->mAddress + v->mLinkerObject->mSize, + v->mDeclaration->mLocation.mLine, v->mDeclaration->mEndLocation.mLine, + types.IndexOrPush(v->mDeclaration->mBase)); + } + else + { + skipped = true; + // Prepared space on the stack but not used + } + } + else if (v->mTemp) + { + skipped = true; + // Promoted to local variable + } + else if (p->mFramePointer) + { + if (!vfirst) + fprintf(file, ",\n"); + vfirst = false; + + fprintf(file, "\t\t\t{\"name\": \"%s\", \"start\": %d, \"end\": %d, \"base\": %d, \"enter\": %d, \"leave\": %d, \"typeid\": %d", + v->mIdent->mString, v->mOffset, v->mOffset + v->mSize, BC_REG_LOCALS, + v->mDeclaration->mLocation.mLine, v->mDeclaration->mEndLocation.mLine, + types.IndexOrPush(v->mDeclaration->mBase)); + } + else + { + if (!vfirst) + fprintf(file, ",\n"); + vfirst = false; + + fprintf(file, "\t\t\t{\"name\": \"%s\", \"start\": %d, \"end\": %d, \"base\": %d, \"enter\": %d, \"leave\": %d, \"typeid\": %d", + v->mIdent->mString, v->mOffset, v->mOffset + v->mSize, BC_REG_STACK, + v->mDeclaration->mLocation.mLine, v->mDeclaration->mEndLocation.mLine, + types.IndexOrPush(v->mDeclaration->mBase)); + } + + if (!skipped) + { + if (v->mDeclaration) + DumpReferences(file, v->mDeclaration); + fprintf(file, "}"); + } + } + } + + fprintf(file, "]"); + DumpReferences(file, p->mDeclaration); + fprintf(file, "}\n"); + } + } + fprintf(file, "\t],\n"); + + first = true; + fprintf(file, "\t\"types\": [\n"); + for (int i = 0; i < types.Size(); i++) + { + if (!first) + fprintf(file, ",\n"); + first = false; + + Declaration* dec = types[i]; + switch (dec->mType) + { + case DT_TYPE_INTEGER: + if (dec->mFlags & DTF_SIGNED) + fprintf(file, "\t\t{\"name\": \"%s\", \"typeid\": %d, \"size\": %d, \"type\": \"int\"}", dec->mQualIdent ? dec->mQualIdent->mString : "", i, dec->mSize); + else + fprintf(file, "\t\t{\"name\": \"%s\", \"typeid\": %d, \"size\": %d, \"type\": \"uint\"}", dec->mQualIdent ? dec->mQualIdent->mString : "", i, dec->mSize); + break; + case DT_TYPE_FLOAT: + fprintf(file, "\t\t{\"name\": \"%s\", \"typeid\": %d, \"size\": %d, \"type\": \"float\"}", dec->mQualIdent ? dec->mQualIdent->mString : "", i, dec->mSize); + break; + case DT_TYPE_BOOL: + fprintf(file, "\t\t{\"name\": \"%s\", \"typeid\": %d, \"size\": %d, \"type\": \"bool\"}", dec->mQualIdent ? dec->mQualIdent->mString : "", i, dec->mSize); + break; + case DT_TYPE_ARRAY: + fprintf(file, "\t\t{\"name\": \"%s\", \"typeid\": %d, \"size\": %d, \"type\": \"array\", \"eid\": %d}", dec->mQualIdent ? dec->mQualIdent->mString : "", i, dec->mSize, types.IndexOrPush(dec->mBase)); + break; + case DT_TYPE_POINTER: + fprintf(file, "\t\t{\"name\": \"%s\", \"typeid\": %d, \"size\": %d, \"type\": \"ptr\", \"eid\": %d}", dec->mQualIdent ? dec->mQualIdent->mString : "", i, dec->mSize, types.IndexOrPush(dec->mBase)); + break; + case DT_TYPE_REFERENCE: + fprintf(file, "\t\t{\"name\": \"%s\", \"typeid\": %d, \"size\": %d, \"type\": \"ref\", \"eid\": %d}", dec->mQualIdent ? dec->mQualIdent->mString : "", i, dec->mSize, types.IndexOrPush(dec->mBase)); + break; + case DT_TYPE_RVALUEREF: + fprintf(file, "\t\t{\"name\": \"%s\", \"typeid\": %d, \"size\": %d, \"type\": \"rref\", \"eid\": %d}", dec->mQualIdent ? dec->mQualIdent->mString : "", i, dec->mSize, types.IndexOrPush(dec->mBase)); + break; + case DT_TYPE_ENUM: + { + fprintf(file, "\t\t{\"name\": \"%s\", \"typeid\": %d, \"size\": %d, \"type\": \"enum\",\"members\": [\n", dec->mQualIdent ? dec->mQualIdent->mString : "", i, dec->mSize); + bool tfirst = true; + Declaration* mdec = dec->mParams; + while (mdec) + { + if (mdec->mIdent) + { + if (!tfirst) + fprintf(file, ",\n"); + tfirst = false; + + fprintf(file, "\t\t\t{\"name\": \"%s\", \"value\": %d}", mdec->mIdent->mString, int(mdec->mInteger)); + } + + mdec = mdec->mNext; + } + fprintf(file, "]}"); + } + break; + break; + case DT_TYPE_STRUCT: + { + fprintf(file, "\t\t{\"name\": \"%s\", \"typeid\": %d, \"size\": %d, \"type\": \"struct\",\"members\": [\n", dec->mQualIdent ? dec->mQualIdent->mString : "", i, dec->mSize); + bool tfirst = true; + Declaration* mdec = dec->mParams; + while (mdec) + { + if (!tfirst) + fprintf(file, ",\n"); + tfirst = false; + + fprintf(file, "\t\t\t{\"name\": \"%s\", \"offset\": %d, \"typeid\": %d}", mdec->mIdent->mString, mdec->mOffset, types.IndexOrPush(mdec->mBase)); + + mdec = mdec->mNext; + } + fprintf(file, "]}"); + } + break; + default: + fprintf(file, "\t\t{\"name\": \"%s\", \"typeid\": %d, \"size\": %d}", dec->mQualIdent ? dec->mQualIdent->mString : "", i, dec->mSize); + } + } + + fprintf(file, "\t]"); + + fprintf(file, "}"); + fclose(file); + + return true; + } + + return false; +} diff --git a/oscar64/oscar64/Compiler.h b/oscar64/oscar64/Compiler.h new file mode 100644 index 0000000..1a9486e --- /dev/null +++ b/oscar64/oscar64/Compiler.h @@ -0,0 +1,72 @@ +#pragma once + +#include "Errors.h" +#include "CompilationUnits.h" +#include "Preprocessor.h" +#include "ByteCodeGenerator.h" +#include "NativeCodeGenerator.h" +#include "InterCodeGenerator.h" +#include "GlobalAnalyzer.h" +#include "GlobalOptimizer.h" +#include "Linker.h" +#include "CompilerTypes.h" + +class Compiler +{ +public: + Compiler(void); + ~Compiler(void); + + Errors* mErrors; + Linker* mLinker; + CompilationUnits* mCompilationUnits; + Preprocessor* mPreprocessor; + ByteCodeGenerator* mByteCodeGenerator; + NativeCodeGenerator* mNativeCodeGenerator; + InterCodeGenerator* mInterCodeGenerator; + InterCodeModule* mInterCodeModule; + GlobalAnalyzer* mGlobalAnalyzer; + GlobalOptimizer* mGlobalOptimizer; + + GrowingArray mByteCodeFunctions; + + TargetMachine mTargetMachine; + uint64 mCompilerOptions; + uint16 mCartridgeID; + uint8 mCartridgeSubType; + char mCartridgeName[32]; + char mVersion[32]; + + struct Define + { + const Ident* mIdent; + const char* mValue; + }; + + GrowingArray mDefines; + + bool BuildLZO(const char* targetPath); + bool ParseSource(void); + bool GenerateCode(void); + bool WriteOutputFile(const char* targetPath, DiskImage * d64); + bool WriteErrorFile(const char* targetPath); + bool RemoveErrorFile(const char* targetPath); + int ExecuteCode(bool profile, int trace, bool asserts, bool iorange); + + void AddDefine(const Ident* ident, const char* value); + + void RegisterRuntime(const Location& loc, const Ident* ident); + + void CompileProcedure(InterCodeProcedure* proc); + void BuildVTables(void); + void CheckOperatorNew(void); + void CompleteTemplateExpansion(void); + + Declaration* mGlobalNew, * mGlobalDelete; + + void PreCompileExpression(Expression* exp); + void PreCompileDeclaration(Declaration* dec); + + bool WriteDbjFile(const char* filename); + bool WriteCszFile(const char* filename); +}; diff --git a/oscar64/oscar64/CompilerTypes.h b/oscar64/oscar64/CompilerTypes.h new file mode 100644 index 0000000..8e6d553 --- /dev/null +++ b/oscar64/oscar64/CompilerTypes.h @@ -0,0 +1,87 @@ +#pragma once + +#include "MachineTypes.h" + +static const uint64 COPT_OPTIMIZE_BASIC = 1ULL << 0; +static const uint64 COPT_OPTIMIZE_INLINE = 1ULL << 1; +static const uint64 COPT_OPTIMIZE_ASSEMBLER = 1ULL << 2; + +static const uint64 COPT_OPTIMIZE_AUTO_INLINE = 1ULL << 4; +static const uint64 COPT_OPTIMIZE_AUTO_INLINE_ALL = 1ULL << 5; +static const uint64 COPT_OPTIMIZE_AUTO_UNROLL = 1ULL << 6; +static const uint64 COPT_OPTIMIZE_CONST_EXPRESSIONS = 1ULL << 7; +static const uint64 COPT_OPTIMIZE_AUTO_ZEROPAGE = 1ULL << 8; +static const uint64 COPT_OPTIMIZE_CONST_PARAMS = 1ULL << 9; +static const uint64 COPT_OPTIMIZE_MERGE_CALLS = 1ULL << 10; +static const uint64 COPT_OPTIMIZE_GLOBAL = 1ULL << 11; +static const uint64 COPT_OPTIMIZE_OUTLINE = 1ULL << 12; +static const uint64 COPT_OPTIMIZE_PAGE_CROSSING = 1ULL << 13; + +static const uint64 COPT_OPTIMIZE_CODE_SIZE = 1ULL << 16; +static const uint64 COPT_NATIVE = 1ULL << 17; +static const uint64 COPT_EXTENDED_ZERO_PAGE = 1ULL << 20; +static const uint64 COPT_OPTIMIZE_SELF_MOD = 1ULL << 21; + +static const uint64 COPT_TARGET_PRG = 1ULL << 32; +static const uint64 COPT_TARGET_CRT8 = 1ULL << 33; +static const uint64 COPT_TARGET_CRT16 = 1ULL << 34; +static const uint64 COPT_TARGET_CRT_EASYFLASH = 1ULL << 35; + +static const uint64 COPT_TARGET_CRT = COPT_TARGET_CRT8 | COPT_TARGET_CRT16 | COPT_TARGET_CRT_EASYFLASH; + +static const uint64 COPT_TARGET_COPY = 1ULL << 36; +static const uint64 COPT_TARGET_BIN = 1ULL << 37; +static const uint64 COPT_TARGET_LZO = 1ULL << 38; +static const uint64 COPT_TARGET_NES = 1ULL << 39; + +static const uint64 COPT_VERBOSE = 1ULL << 48; +static const uint64 COPT_VERBOSE2 = 1ULL << 49; +static const uint64 COPT_VERBOSE3 = 1ULL << 50; + +static const uint64 COPT_DEBUGINFO = 1ULL << 51; + +static const uint64 COPT_CPLUSPLUS = 1ULL << 52; +static const uint64 COPT_PETSCII = 1ULL << 53; +static const uint64 COPT_ERROR_FILES = 1ULL << 54; + +static const uint64 COPT_PROFILEINFO = 1ULL << 55; +static const uint64 COPT_STRICT = 1ULL << 56; + + + +static const uint64 COPT_DEFAULT = COPT_OPTIMIZE_BASIC | COPT_OPTIMIZE_INLINE | COPT_OPTIMIZE_CONST_EXPRESSIONS; + +static const uint64 COPT_OPTIMIZE_DEFAULT = COPT_OPTIMIZE_BASIC | COPT_OPTIMIZE_INLINE | COPT_OPTIMIZE_CONST_EXPRESSIONS; + +static const uint64 COPT_OPTIMIZE_SIZE = COPT_OPTIMIZE_BASIC | COPT_OPTIMIZE_INLINE | COPT_OPTIMIZE_AUTO_INLINE | COPT_OPTIMIZE_CONST_EXPRESSIONS | COPT_OPTIMIZE_CODE_SIZE | COPT_OPTIMIZE_CONST_PARAMS | COPT_OPTIMIZE_MERGE_CALLS | COPT_OPTIMIZE_GLOBAL;// | COPT_OPTIMIZE_OUTLINE; + +static const uint64 COPT_OPTIMIZE_SPEED = COPT_OPTIMIZE_BASIC | COPT_OPTIMIZE_INLINE | COPT_OPTIMIZE_AUTO_INLINE | COPT_OPTIMIZE_AUTO_UNROLL | COPT_OPTIMIZE_CONST_EXPRESSIONS | COPT_OPTIMIZE_ASSEMBLER | COPT_OPTIMIZE_CONST_PARAMS | COPT_OPTIMIZE_MERGE_CALLS | COPT_OPTIMIZE_GLOBAL; + +static const uint64 COPT_OPTIMIZE_ALL = COPT_OPTIMIZE_BASIC | COPT_OPTIMIZE_INLINE | COPT_OPTIMIZE_AUTO_INLINE | COPT_OPTIMIZE_AUTO_INLINE_ALL | COPT_OPTIMIZE_AUTO_UNROLL | COPT_OPTIMIZE_CONST_EXPRESSIONS | COPT_OPTIMIZE_ASSEMBLER | COPT_OPTIMIZE_AUTO_ZEROPAGE | COPT_OPTIMIZE_CONST_PARAMS | COPT_OPTIMIZE_MERGE_CALLS | COPT_OPTIMIZE_GLOBAL; + +enum TargetMachine +{ + TMACH_C64, + TMACH_VIC20, + TMACH_VIC20_3K, + TMACH_VIC20_8K, + TMACH_VIC20_16K, + TMACH_VIC20_24K, + TMACH_C128, + TMACH_C128B, + TMACH_C128E, + TMACH_PET_8K, + TMACH_PET_16K, + TMACH_PET_32K, + TMACH_PLUS4, + TMACH_NES, + TMACH_NES_NROM_H, + TMACH_NES_NROM_V, + TMACH_NES_MMC1, + TMACH_NES_MMC3, + TMACH_ATARI, + TMACH_X16, + TMACH_MEGA65 +}; + + diff --git a/oscar64/oscar64/Compression.cpp b/oscar64/oscar64/Compression.cpp new file mode 100644 index 0000000..23fa6b5 --- /dev/null +++ b/oscar64/oscar64/Compression.cpp @@ -0,0 +1,59 @@ +#include "Compression.h" + +int CompressLZO(uint8* dst, const uint8* source, int size) +{ + int csize = 0; + + int pos = 0; + while (pos < size) + { + int pi = 0; + while (pi < 127 && pos < size) + { + int bi = pi, bj = 0; + for (int i = 1; i <= (pos < 255 ? pos : 255); i++) + { + int j = 0; + while (j < 127 && pos + j < size && source[pos - i + j] == source[pos + j]) + j++; + + if (j > bj) + { + bi = i; + bj = j; + } + } + + if (bj >= 4) + { + if (pi > 0) + { + dst[csize++] = pi; + for (int i = 0; i < pi; i++) + dst[csize++] = source[pos - pi + i]; + pi = 0; + } + + dst[csize++] = 128 + bj; + dst[csize++] = bi; + pos += bj; + } + else + { + pos++; + pi++; + } + } + + if (pi > 0) + { + dst[csize++] = pi; + for (int i = 0; i < pi; i++) + dst[csize++] = source[pos - pi + i]; + } + } + + dst[csize++] = 0; + + return csize; +} diff --git a/oscar64/oscar64/Compression.h b/oscar64/oscar64/Compression.h new file mode 100644 index 0000000..3bd4e3a --- /dev/null +++ b/oscar64/oscar64/Compression.h @@ -0,0 +1,6 @@ +#pragma once + +#include "CompilerTypes.h" + +int CompressLZO(uint8* dst, const uint8* source, int size); + diff --git a/oscar64/oscar64/Constexpr.cpp b/oscar64/oscar64/Constexpr.cpp new file mode 100644 index 0000000..e6cd1f2 --- /dev/null +++ b/oscar64/oscar64/Constexpr.cpp @@ -0,0 +1,1869 @@ +#include "Constexpr.h" +#include + +ConstexprInterpreter::Value::Value(void) + : mDecType(TheVoidTypeDeclaration), mDecValue(nullptr), + mBaseValue(nullptr), mOffset(0), + mData(mShortData), mDataSize(0), + mShift(0), mBits(0) +{ +} + +ConstexprInterpreter::Value::Value(const Location & location) + : mLocation(location), + mDecType(TheVoidTypeDeclaration), mDecValue(nullptr), + mBaseValue(nullptr), mOffset(0), + mData(mShortData), mDataSize(0), + mShift(0), mBits(0) +{ +} + +ConstexprInterpreter::Value::Value(Expression* exp) + : mLocation(exp->mLocation), + mDecType(exp->mDecType), mDecValue(nullptr), + mBaseValue(nullptr), mOffset(0), + mDataSize(exp->mDecValue->mBase->mSize), + mShift(0), mBits(0) +{ + assert(exp->mType == EX_CONSTANT); + + if (mDataSize <= 4) + mData = mShortData; + else + mData = new ValueItem[mDataSize]; + + PutConst(0, exp->mDecValue); +} + +void ConstexprInterpreter::Value::PutConst(int offset, Declaration* dec) +{ + switch (dec->mType) + { + case DT_CONST_INTEGER: + case DT_CONST_ADDRESS: + PutIntAt(dec->mInteger, offset, dec->mBase); + break; + case DT_CONST_FLOAT: + PutFloatAt(float(dec->mNumber), offset, dec->mBase); + break; + case DT_CONST_STRUCT: + for (Declaration* pdec = dec->mParams; pdec; pdec = pdec->mNext) + PutConst(pdec->mOffset, pdec); + break; + case DT_CONST_DATA: + for (int i = 0; i < dec->mBase->mSize; i++) + PutIntAt(dec->mData[i], offset + i, TheConstCharTypeDeclaration); + break; + case DT_CONST_POINTER: + PutPtrAt(new Value(mLocation, dec->mValue->mDecValue, dec->mBase, 0), offset, dec); + break; + } +} + +ConstexprInterpreter::Value::Value(const Location& location, Declaration* dec) + : mLocation(location), + mDecType(dec), mDecValue(nullptr), + mBaseValue(nullptr), mOffset(0), + mDataSize(dec->mSize), + mShift(0), mBits(0) +{ + if (mDataSize <= 4) + mData = mShortData; + else + mData = new ValueItem[mDataSize]; +} + +ConstexprInterpreter::Value::Value(const Location& location, Declaration* dec, int size) + : mLocation(location), + mDecType(dec), mDecValue(nullptr), + mBaseValue(nullptr), mOffset(0), + mDataSize(size), + mShift(0), mBits(0) +{ + if (mDataSize <= 4) + mData = mShortData; + else + mData = new ValueItem[mDataSize]; +} + +ConstexprInterpreter::Value::Value(const Value& value) + : mLocation(value.mLocation), + mDecType(value.mDecType), mDecValue(nullptr), + mBaseValue(value.mBaseValue), mOffset(value.mOffset), + mDataSize(value.mDataSize), + mShift(value.mShift), mBits(value.mBits) +{ + if (mDataSize <= 4) + mData = mShortData; + else + mData = new ValueItem[mDataSize]; + + for (int i = 0; i < mDataSize; i++) + mData[i] = value.mData[i]; +} + +ConstexprInterpreter::Value::Value(Value&& value) + : mLocation(value.mLocation), + mDecType(value.mDecType), mDecValue(nullptr), + mBaseValue(value.mBaseValue), mOffset(value.mOffset), + mDataSize(value.mDataSize), + mShift(value.mShift), mBits(value.mBits) +{ + if (mDataSize <= 4) + { + mData = mShortData; + for (int i = 0; i < mDataSize; i++) + mData[i] = value.mData[i]; + } + else + { + mData = value.mData; + value.mData = value.mShortData; + } +} + +ConstexprInterpreter::Value::Value(Value* value) + : mLocation(value->mLocation), + mDecType(value->mDecType), mDecValue(value->mDecValue), + mBaseValue(value), mOffset(0), + mDataSize(0), mData(mShortData), + mShift(0), mBits(0) +{ +} + +ConstexprInterpreter::Value::Value(const Location& location, const Value* value, Declaration* type, int offset) + : mLocation(location), + mDecType(type), mDecValue(nullptr), + mBaseValue(value), mOffset(offset), + mDataSize(0), mData(mShortData), + mShift(0), mBits(0) +{ +} + +ConstexprInterpreter::Value::Value(const Location& location, const Value* value, Declaration* type, int offset, int shift, int bits) + : mLocation(location), + mDecType(type), mDecValue(nullptr), + mBaseValue(value), mOffset(offset), + mDataSize(0), mData(mShortData), + mShift(shift), mBits(bits) +{ +} + + +ConstexprInterpreter::Value::Value(const Location& location, Declaration* value, Declaration* type, int offset) + : mLocation(location), + mDecType(type), mDecValue(value), + mBaseValue(nullptr), mOffset(offset), + mDataSize(0), mData(mShortData), + mShift(0), mBits(0) +{ + if (value->mValue && value->mValue->mType == EX_CONSTANT) + { + mDataSize = value->mSize; + if (mDataSize <= 4) + mData = mShortData; + else + mData = new ValueItem[mDataSize]; + + PutConst(0, value->mValue->mDecValue); + } +} + +ConstexprInterpreter::Value::Value(const Location& location, const uint8* data, Declaration* type) + : mLocation(location), + mDecType(type), mDecValue(nullptr), + mBaseValue(nullptr), mOffset(0), + mDataSize(type->mSize), + mShift(0), mBits(0) +{ + if (mDataSize <= 4) + mData = mShortData; + else + mData = new ValueItem[mDataSize]; + + for (int i = 0; i < mDataSize; i++) + mData[i].mByte = data[i]; +} + +ConstexprInterpreter::Value::Value(const Location& location, const ValueItem* data, Declaration* type) + : mLocation(location), + mDecType(type), mDecValue(nullptr), + mBaseValue(nullptr), mOffset(0), + mDataSize(type->mSize), + mShift(0), mBits(0) +{ + if (mDataSize <= 4) + mData = mShortData; + else + mData = new ValueItem[mDataSize]; + + for (int i = 0; i < mDataSize; i++) + mData[i] = data[i]; +} + + +ConstexprInterpreter::Value::~Value(void) +{ + if (mData != mShortData) + delete[] mData; +} + + + +ConstexprInterpreter::Value& ConstexprInterpreter::Value::operator=(const Value& v) +{ + if (mData != mShortData) + { + delete[] mData; + mData = mShortData; + } + + mLocation = v.mLocation; + mDecType = v.mDecType; + mBaseValue = v.mBaseValue; + mDataSize = v.mDataSize; + mOffset = v.mOffset; + mBits = v.mBits; + mShift = v.mShift; + + if (mDataSize <= 4) + mData = mShortData; + else + mData = new ValueItem[mDataSize]; + + for (int i = 0; i < mDataSize; i++) + mData[i] = v.mData[i]; + + return *this; +} + +ConstexprInterpreter::Value& ConstexprInterpreter::Value::operator=(Value&& v) +{ + if (mData != mShortData) + { + delete[] mData; + mData = mShortData; + } + + mLocation = v.mLocation; + mDecType = v.mDecType; + mBaseValue = v.mBaseValue; + mDataSize = v.mDataSize; + mOffset = v.mOffset; + mBits = v.mBits; + mShift = v.mShift; + + if (mDataSize <= 4) + { + mData = mShortData; + for (int i = 0; i < mDataSize; i++) + mData[i] = v.mData[i]; + } + else + { + mData = v.mData; + v.mData = v.mShortData; + } + + return *this; +} + + +bool ConstexprInterpreter::Value::CheckSpace(int at, int size) const +{ + if (mBaseValue) + return mBaseValue->CheckSpace(mOffset + at, size); + else + return at + size <= mDataSize; +} + +ConstexprInterpreter::ValueItem* ConstexprInterpreter::Value::GetAddr(void) +{ + if (mBaseValue) + return mBaseValue->mData + mOffset; + else + return mData; +} + +const ConstexprInterpreter::ValueItem* ConstexprInterpreter::Value::GetAddr(void) const +{ + if (mBaseValue) + return mBaseValue->mData + mOffset; + else + return mData; +} + +int64 ConstexprInterpreter::Value::GetInt(void) const +{ + return GetIntAt(0, mDecType); +} + +int64 ConstexprInterpreter::Value::GetInt(Declaration* type) const +{ + int64 val = GetInt(); + val &= 0xffffffffll >> (8 * (4 - type->mSize)); + if (type->mFlags & DTF_SIGNED) + { + if (val & (0x80000000ll >> (8 * (4 - type->mSize)))) + val -= 0x100000000ll >> (8 * (4 - type->mSize)); + } + return val; +} + +double ConstexprInterpreter::Value::GetFloat(void) const +{ + return GetFloatAt(0, mDecType); +} + +ConstexprInterpreter::Value ConstexprInterpreter::Value::GetPtr(void) const +{ + return GetPtrAt(0, mDecType->mBase); +} + +bool ConstexprInterpreter::Value::GetBool(void) const +{ + bool check = false; + if (mDecType->IsIntegerType()) + return GetInt() != 0; + else if (mDecType->IsPointerType()) + { + Value p = GetPtr(); + return p.mBaseValue != nullptr || p.mOffset != 0; + } + else if (mDecType->mType == DT_TYPE_FLOAT) + return GetFloat() != 0; + + return false; +} + +void ConstexprInterpreter::Value::PutInt(int64 v) +{ + PutIntAt(v, 0, mDecType); +} + +void ConstexprInterpreter::Value::PutFloat(double v) +{ + PutFloatAt(v, 0, mDecType); +} + +void ConstexprInterpreter::Value::PutPtr(const Value& v) +{ + PutPtrAt(v, 0, mDecType); +} + + +int64 ConstexprInterpreter::Value::GetIntAt(int at, Declaration* type) const +{ + if (type->mType == DT_TYPE_FLOAT) + { + return int64(GetFloatAt(at, type)); + } + else + { + const ValueItem* dp = GetAddr() + at; + + if (mBits) + { + int nbytes = (mShift + mBits + 7) >> 3; + + assert(CheckSpace(at, nbytes)); + + uint64 vv = 0; + switch (nbytes) + { + case 4: + vv |= (uint64(dp[3].mByte) << 24); + case 3: + vv |= (uint64(dp[2].mByte) << 24); + case 2: + vv |= uint64(dp[1].mByte) << 8; + case 1: + vv |= dp[0].mByte; + } + + uint64 mask = ((1LL << mBits) - 1); + vv = (vv >> mShift) & mask; + if (type->mFlags & DTF_SIGNED) + return (int64)(vv << (64 - mBits)) >> (64 - mBits); + else + return vv; + } + else if (type->mFlags & DTF_SIGNED) + { + switch (type->mSize) + { + case 1: + return int8(dp[0].mByte); + case 2: + return int16(dp[0].mByte | ((uint32)(dp[1].mByte) << 8)); + case 4: + return int32(dp[0].mByte | ((uint32)(dp[1].mByte) << 8) | ((uint32)(dp[2].mByte) << 16) | ((uint32)(dp[3].mByte) << 24)); + } + } + else + { + switch (type->mSize) + { + case 1: + return uint8(dp[0].mByte); + case 2: + return uint16(dp[0].mByte | ((uint32)(dp[1].mByte) << 8)); + case 4: + return uint32(dp[0].mByte | ((uint32)(dp[1].mByte) << 8) | ((uint32)(dp[2].mByte) << 16) | ((uint32)(dp[3].mByte) << 24)); + } + } + } + + return 0; +} + +double ConstexprInterpreter::Value::GetFloatAt(int at, Declaration* type) const +{ + if (type->mType == DT_TYPE_FLOAT) + { + const ValueItem* dp = GetAddr() + at; + + union + { + float f; + uint32 i; + } u; + + u.i = uint32(dp[0].mByte | ((uint32)(dp[1].mByte) << 8) | ((uint32)(dp[2].mByte) << 16) | ((uint32)(dp[3].mByte) << 24)); + return u.f; + } + else + { + int64 iv = GetIntAt(at, type); + return double(iv); + } + +} + +ConstexprInterpreter::Value ConstexprInterpreter::Value::GetPtrAt(int at, Declaration* type) const +{ + const ValueItem* dp = GetAddr() + at; + + return Value(mLocation, dp->mBaseValue, type, uint16(dp[0].mByte | ((uint32)(dp[1].mByte) << 8))); +} + +void ConstexprInterpreter::Value::PutVarAt(Declaration* var, int64 v, int at, Declaration* type) +{ + assert(CheckSpace(at, 2)); + + ValueItem* dp = GetAddr() + at; + dp[0].mByte = uint8(v & 0xff); + dp[1].mByte = uint8((v >> 8) & 0xff); + mDecValue = var; +} + +void ConstexprInterpreter::Value::PutIntAt(int64 v, int at, Declaration* type) +{ + if (type->mType == DT_TYPE_FLOAT) + { + PutFloatAt(float(v), at, type); + } + else + { + ValueItem* dp = GetAddr() + at; + if (mBits) + { + int nbytes = (mShift + mBits + 7) >> 3; + + assert(CheckSpace(at, nbytes)); + + uint64 vv = 0; + switch (nbytes) + { + case 4: + vv |= (uint64(dp[3].mByte) << 24); + case 3: + vv |= (uint64(dp[2].mByte) << 24); + case 2: + vv |= uint64(dp[1].mByte) << 8; + case 1: + vv |= dp[0].mByte; + } + + uint64 mask = ((1LL << mBits) - 1) << mShift; + vv = (vv & ~mask) | (v << mShift) & mask; + + switch (nbytes) + { + case 4: + dp[3].mByte = uint8((vv >> 24) & 0xff); + case 3: + dp[2].mByte = uint8((vv >> 16) & 0xff); + case 2: + dp[1].mByte = uint8((vv >> 8) & 0xff); + case 1: + dp[0].mByte = uint8(vv & 0xff); + } + } + else + { + assert(CheckSpace(at, type->mSize)); + + switch (type->mSize) + { + case 4: + dp[3].mByte = uint8((v >> 24) & 0xff); + dp[2].mByte = uint8((v >> 16) & 0xff); + case 2: + dp[1].mByte = uint8((v >> 8) & 0xff); + case 1: + dp[0].mByte = uint8(v & 0xff); + } + } + } +} + +void ConstexprInterpreter::Value::PutFloatAt(double v, int at, Declaration* type) +{ + if (type->mType == DT_TYPE_FLOAT) + { + assert(CheckSpace(at, 4)); + + ValueItem* dp = GetAddr() + at; + + union + { + float f; + uint32 i; + } u; + + u.f = float(v); + + dp[3].mByte = uint8((u.i >> 24) & 0xff); + dp[2].mByte = uint8((u.i >> 16) & 0xff); + dp[1].mByte = uint8((u.i >> 8) & 0xff); + dp[0].mByte = uint8(u.i & 0xff); + } + else + { + PutIntAt(int64(v), at, type); + } +} + +void ConstexprInterpreter::Value::PutPtrAt(const Value& v, int at, Declaration* type) +{ + assert(CheckSpace(at, 2)); + + ValueItem* dp = GetAddr() + at; + + dp[0].mBaseValue = v.mBaseValue; + dp[1].mByte = uint8((v.mOffset >> 8) & 0xff); + dp[0].mByte = uint8(v.mOffset & 0xff); +} + +ConstexprInterpreter::Value ConstexprInterpreter::Value::ToRValue(Errors * errors) const +{ + if (mBaseValue) + { + if (mDecType->mType == DT_TYPE_ARRAY) + { + Value v(mBaseValue->mLocation, mDecType->BuildArrayPointer()); + v.PutPtr(*this); + return v; + } + else + return Value(mLocation, GetAddr(), mDecType); + } + else + return *this; +} + +void ConstexprInterpreter::Value::Assign(const Value& v, Errors* errors) +{ + switch (mDecType->mType) + { + case DT_TYPE_INTEGER: + case DT_TYPE_BOOL: + case DT_TYPE_ENUM: + if (mBits) + PutInt(v.GetInt()); + else if (CheckSpace(0, mDecType->mSize)) + PutInt(v.GetInt()); + else + errors->Error(v.mLocation, EERR_INVALID_CONSTEXPR, "invalid constexpression store address"); + break; + case DT_TYPE_FLOAT: + if (CheckSpace(0, 4)) + PutFloat(v.GetFloat()); + else + errors->Error(v.mLocation, EERR_INVALID_CONSTEXPR, "invalid constexpression store address"); + break; + case DT_TYPE_STRUCT: + case DT_TYPE_UNION: + case DT_TYPE_ARRAY: + { + if (CheckSpace(0, mDecType->mSize)) + { + const ConstexprInterpreter::ValueItem* sp = v.GetAddr(); + ConstexprInterpreter::ValueItem* dp = GetAddr(); + + for (int i = 0; i < mDecType->mSize; i++) + dp[i] = sp[i]; + } + else + errors->Error(v.mLocation, EERR_INVALID_CONSTEXPR, "invalid constexpression store address"); + } break; + case DT_TYPE_POINTER: + if (CheckSpace(0, 2)) + PutPtr(v.GetPtr()); + else + errors->Error(v.mLocation, EERR_INVALID_CONSTEXPR, "invalid constexpression store address"); + break; + } +} + +Declaration* ConstexprInterpreter::Value::GetConst(int offset, Declaration* type, LinkerSection* dataSection) const +{ + Declaration* dec = nullptr; + + switch (type->mType) + { + case DT_TYPE_INTEGER: + case DT_TYPE_BOOL: + case DT_TYPE_ENUM: + dec = new Declaration(mLocation, DT_CONST_INTEGER); + dec->mBase = type; + dec->mFlags = type->mFlags & DTF_SIGNED; + dec->mSize = type->mSize; + dec->mInteger = GetIntAt(offset, type); + break; + case DT_TYPE_FLOAT: + dec = new Declaration(mLocation, DT_CONST_FLOAT); + dec->mBase = type; + dec->mSize = type->mSize; + dec->mNumber = GetFloatAt(offset, type); + break; + case DT_TYPE_STRUCT: + { + dec = new Declaration(mLocation, DT_CONST_STRUCT); + dec->mBase = type; + dec->mSize = type->mSize; + dec->mSection = dataSection; + dec->mOffset = offset; + + while (type) + { + for (Declaration* mdec = type->mParams; mdec; mdec = mdec->mNext) + { + Declaration* cdec = GetConst(offset + mdec->mOffset, mdec->mBase, dataSection); + cdec->mOffset = mdec->mOffset; + cdec->mNext = dec->mParams; + dec->mParams = cdec; + } + + if (type->mVTable) + { + Declaration * cdec = new Declaration(mLocation, DT_CONST_INTEGER); + cdec->mBase = TheConstCharTypeDeclaration; + cdec->mSize = 1; + cdec->mInteger = type->mVTable->mDefaultConstructor->mInteger; + cdec->mOffset = offset + type->mVTable->mOffset; + cdec->mNext = dec->mParams; + dec->mParams = cdec; + } + + if (type->mBase) + type = type->mBase->mBase; + else + type = nullptr; + } + break; + } + case DT_TYPE_ARRAY: + { + dec = new Declaration(mLocation, DT_CONST_STRUCT); + dec->mBase = type; + dec->mSize = type->mSize; + dec->mSection = dataSection; + dec->mOffset = offset; + Declaration* ldec = nullptr; + for (int i=0; imSize; i += type->mBase->mSize) + { + Declaration* cdec = GetConst(offset + i, type->mBase, dataSection); + if (type->mStride) + cdec->mOffset = i / type->mBase->mSize; + else + cdec->mOffset = i; + + if (ldec) + ldec->mNext = cdec; + else + dec->mParams = cdec; + + ldec = cdec; + } + break; + } + case DT_TYPE_POINTER: + { + Value vp = GetPtrAt(offset, type); + if (vp.mBaseValue) + { + dec = new Declaration(mLocation, DT_CONST_POINTER); + dec->mBase = type; + dec->mSize = type->mSize; + + Declaration* target; + + if (vp.mBaseValue->mDecValue) + { + target = new Declaration(mLocation, DT_VARIABLE_REF); + + if (vp.mBaseValue->mDecValue->mType == DT_VARIABLE_REF) + { + target->mBase = vp.mBaseValue->mDecValue->mBase; + target->mOffset = vp.mBaseValue->mDecValue->mOffset; + } + else + target->mBase = vp.mBaseValue->mDecValue; + + target->mOffset += vp.mOffset; + + dec->mValue = new Expression(mLocation, EX_VARIABLE); + dec->mValue->mDecType = type; + dec->mValue->mDecValue = target; + return dec; + } + else if (vp.mBaseValue->mDecType->mType == DT_TYPE_ARRAY) + { + target = new Declaration(mLocation, DT_CONST_DATA); + target->mSize = vp.mBaseValue->mDataSize; + target->mBase = vp.mBaseValue->mDecType; + target->mSection = dataSection; + + uint8* buffer = new uint8[target->mSize]; + for (int i = 0; i < target->mSize; i++) + buffer[i] = uint8(vp.mBaseValue->GetIntAt(i, TheUnsignedCharTypeDeclaration)); + target->mData = buffer; + + dec->mOffset = vp.mOffset; +#if 0 + if (vp.mOffset) + { + Declaration* tt = new Declaration(mLocation, DT_CONST_POINTER); + tt->mBase = target->mBase; + tt->mValue = new Expression(mLocation, EX_CONSTANT); + tt->mValue->mDecType = target->mBase; + tt->mValue->mDecValue = target; + tt->mOffset = vp.mOffset; + target = tt; + } +#endif + } + else + target = vp.mBaseValue->GetConst(0, vp.mBaseValue->mDecType, dataSection); + + dec->mValue = new Expression(mLocation, EX_CONSTANT); + dec->mValue->mDecType = target->mBase; + dec->mValue->mDecValue = target; + } + else if (vp.mDecValue) + { + dec = new Declaration(mLocation, DT_VARIABLE_REF); + dec->mBase = vp.mDecValue; + dec->mFlags = 0; + dec->mSize = type->mSize; + dec->mOffset = vp.mOffset; + } + else + { + dec = new Declaration(mLocation, DT_CONST_ADDRESS); + dec->mBase = type; + dec->mFlags = 0; + dec->mSize = type->mSize; + dec->mInteger = vp.mOffset; + } + break; + } + + } + + return dec; +} + +Expression* ConstexprInterpreter::Value::ToExpression(LinkerSection* dataSection) const +{ + Expression* exp = new Expression(mLocation, EX_CONSTANT); + + exp->mDecType = mDecType; + exp->mDecValue = GetConst(0, mDecType, dataSection); + + return exp; +} + + +ConstexprInterpreter::ConstexprInterpreter(const Location & location, Errors* err, LinkerSection* dataSection) + : mLocation(location), mErrors(err), mDataSection(dataSection), mParams(Value()), mLocals(Value()), mHeap(nullptr) +{ + +} + +ConstexprInterpreter::~ConstexprInterpreter(void) +{ + for (int i = 0; i < mTemps.Size(); i++) + delete mTemps[i]; +} + +ConstexprInterpreter::Value* ConstexprInterpreter::NewValue(Expression* exp, Declaration* type, int size) +{ + Value* v = new Value(exp->mLocation, type, size); + mHeap->Push(v); + return v; +} + +void ConstexprInterpreter::DeleteValue(const Value* v) +{ + int i = mHeap->IndexOf(v); + if (i >= 0) + { + delete v; + mHeap->Remove(i); + } + else + mErrors->Error(v->mLocation, EERR_DOUBLE_FREE, "Freeing not allocated memory"); +} + +Expression* ConstexprInterpreter::EvalConstructor(Expression* exp) +{ + mProcType = exp->mLeft->mDecType; + + Expression* pex = exp->mRight; + Declaration* cdec = exp->mLeft->mDecType->mParams; + + int pos = 0; + mResult = Value(exp->mLocation, cdec->mBase->mBase); + mParams[pos] = Value(exp->mLocation, cdec->mBase); + mParams[pos].PutPtr(Value(&mResult)); + pos = 2; + + if (pex->mType == EX_LIST) + pex = pex->mRight; + else + pex = nullptr; + cdec = cdec->mNext; + + while (pex && pex->mType == EX_LIST) + { + if (!AddParam(pos, pex->mLeft, cdec)) + return exp; + + pex = pex->mRight; + if (cdec) + cdec = cdec->mNext; + } + + if (pex) + { + if (!AddParam(pos, pex, cdec)) + return exp; + } + + mHeap = new ExpandingArray(); + + Execute(exp->mLeft->mDecValue->mValue); + + if (mHeap->Size() > 0) + mErrors->Error(exp->mLocation, EERR_UNBALANCED_HEAP_USE, "Unbalanced heap use in constexpr"); + delete mHeap; + + return mResult.ToExpression(mDataSection); +} + +Expression* ConstexprInterpreter::EvalTempConstructor(Expression* exp) +{ + Expression* cexp = exp->mLeft->mLeft; + + mProcType = cexp->mLeft->mDecType; + + Expression* pex = cexp->mRight; + Declaration* dec = cexp->mLeft->mDecType->mParams; + + Value othis = Value(exp->mLocation, exp->mRight->mDecType); + + mParams[0] = Value(exp->mLocation, dec->mBase); + mParams[0].PutPtr(Value(&othis)); + + int pos = 2; + if (pex->mType == EX_LIST) + pex = pex->mRight; + else + pex = nullptr; + dec = dec->mNext; + + while (pex && pex->mType == EX_LIST) + { + if (!AddParam(pos, pex->mLeft, dec)) + return exp; + + pex = pex->mRight; + if (dec) + dec = dec->mNext; + } + + if (pex) + { + if (!AddParam(pos, pex, dec)) + return exp; + } + + mHeap = new ExpandingArray(); + + Execute(cexp->mLeft->mDecValue->mValue); + + if (mHeap->Size() > 0) + mErrors->Error(exp->mLocation, EERR_UNBALANCED_HEAP_USE, "Unbalanced heap use in constexpr"); + delete mHeap; + + return othis.ToExpression(mDataSection); +} + +bool ConstexprInterpreter::AddParam(int& pos, Expression* pex, Declaration* dec) +{ + pex = pex->ConstantFold(mErrors, mDataSection); + + if (dec) + pos = dec->mVarIndex; + + if (pex->mType == EX_CONSTANT) + { + if (pex->mDecType->mType == DT_TYPE_ARRAY) + { + Value* tmp = new Value(pex); + mTemps.Push(tmp); + mParams[pos] = Value(pex->mLocation, pex->mDecType->BuildArrayPointer()); + mParams[pos].PutPtr(Value(tmp)); + } + else + mParams[pos] = Value(pex); + } + else if (pex->mType == EX_VARIABLE && (pex->mDecValue->mFlags & DTF_CONST)) + { + mParams[pos] = Value(pex->mLocation, pex->mDecValue->mBase); + if (pex->mDecValue->mSize > 0) + { + if (pex->mDecValue->mValue) + mParams[pos].PutConst(0, pex->mDecValue->mValue->mDecValue); + else + return false; + } + } + else + return false; + + return true; +} + +Expression* ConstexprInterpreter::EvalCall(Expression* exp) +{ + if (!exp->mLeft->mDecValue || !exp->mLeft->mDecValue->mValue) + return exp; + + mProcType = exp->mLeft->mDecType; + + Expression* pex = exp->mRight; + Declaration* dec = exp->mLeft->mDecType->mParams; + + int pos = 0; + if (mProcType->mBase && mProcType->mBase->mType == DT_TYPE_STRUCT) + { + mResult = Value(exp->mLocation, mProcType->mBase); + mParams[0] = Value(&mResult); + pos = 2; + } + + while (pex && pex->mType == EX_LIST) + { + if (!AddParam(pos, pex->mLeft, dec)) + return exp; + + pex = pex->mRight; + if (dec) + dec = dec->mNext; + } + + if (pex) + { + if (!AddParam(pos, pex, dec)) + return exp; + } + + mHeap = new ExpandingArray(); + + Execute(exp->mLeft->mDecValue->mValue); + + if (mHeap->Size() > 0) + mErrors->Error(exp->mLocation, EERR_UNBALANCED_HEAP_USE, "Unbalanced heap use in constexpr"); + delete mHeap; + + return mResult.ToExpression(mDataSection); +} + +static Declaration* CombinedIntType(Declaration* ld, Declaration* rd) +{ + if (ld->mSize < 2 && rd->mSize < 2) + return TheSignedIntTypeDeclaration; + else if (ld->mSize > rd->mSize) + return ld; + else if (rd->mSize > ld->mSize) + return rd; + else if (!(rd->mFlags & DTF_SIGNED)) + return rd; + else + return ld; +} + + +ConstexprInterpreter::Value ConstexprInterpreter::EvalBinary(Expression * exp, const Value& vl, const Value& vr) +{ + Value v(exp->mLocation, exp->mDecType); + + if (exp->mDecType->mType == DT_TYPE_FLOAT) + { + switch (exp->mToken) + { + case TK_ADD: + case TK_ASSIGN_ADD: + v.PutFloat(vl.GetFloat() + vr.GetFloat()); + break; + case TK_SUB: + case TK_ASSIGN_SUB: + v.PutFloat(vl.GetFloat() - vr.GetFloat()); + break; + case TK_MUL: + case TK_ASSIGN_MUL: + v.PutFloat(vl.GetFloat() * vr.GetFloat()); + break; + case TK_DIV: + case TK_ASSIGN_DIV: + if (vr.GetFloat() == 0) + mErrors->Error(exp->mLocation, EERR_INVALID_VALUE, "Constant division by zero"); + else + v.PutFloat(vl.GetFloat() / vr.GetFloat()); + break; + default: + mErrors->Error(exp->mLocation, EERR_INCOMPATIBLE_OPERATOR, "Incompatible operator", TokenNames[exp->mToken]); + } + } + else if (exp->mDecType->mType == DT_TYPE_POINTER) + { + if (exp->mLeft->mDecType->IsPointerType()) + { + Value vlp = vl.GetPtr(); + vlp.mOffset += int(vr.GetInt() * vl.mDecType->mBase->mSize); + v.PutPtr(vlp); + } + else if (exp->mRight->mDecType->IsPointerType()) + { + Value vrp = vr.GetPtr(); + vrp.mOffset += int(vl.GetInt() * vr.mDecType->mBase->mSize); + v.PutPtr(vrp); + } + else + mErrors->Error(exp->mLocation, EERR_INCOMPATIBLE_OPERATOR, "Incompatible operator", TokenNames[exp->mToken]); + } + else + { + Declaration* ctype = CombinedIntType(vl.mDecType, vr.mDecType); + switch (exp->mToken) + { + case TK_ADD: + case TK_ASSIGN_ADD: + v.PutInt(vl.GetInt(ctype) + vr.GetInt(ctype)); + break; + case TK_SUB: + case TK_ASSIGN_SUB: + v.PutInt(vl.GetInt(ctype) - vr.GetInt(ctype)); + break; + case TK_MUL: + case TK_ASSIGN_MUL: + v.PutInt(vl.GetInt(ctype) * vr.GetInt(ctype)); + break; + case TK_DIV: + case TK_ASSIGN_DIV: + if (vr.GetInt(ctype) == 0) + mErrors->Error(exp->mLocation, EERR_INVALID_VALUE, "Constant division by zero"); + else + v.PutInt(vl.GetInt(ctype) / vr.GetInt(ctype)); + break; + case TK_MOD: + case TK_ASSIGN_MOD: + if (vr.GetInt(ctype) == 0) + mErrors->Error(exp->mLocation, EERR_INVALID_VALUE, "Constant division by zero"); + else + v.PutInt(vl.GetInt(ctype) % vr.GetInt(ctype)); + break; + case TK_LEFT_SHIFT: + case TK_ASSIGN_SHL: + v.PutInt(vl.GetInt() << vr.GetInt()); + break; + case TK_RIGHT_SHIFT: + case TK_ASSIGN_SHR: + v.PutInt(vl.GetInt() >> vr.GetInt()); + break; + case TK_BINARY_AND: + case TK_ASSIGN_AND: + v.PutInt(vl.GetInt(ctype) & vr.GetInt(ctype)); + break; + case TK_BINARY_OR: + case TK_ASSIGN_OR: + v.PutInt(vl.GetInt(ctype) | vr.GetInt(ctype)); + break; + case TK_BINARY_XOR: + case TK_ASSIGN_XOR: + v.PutInt(vl.GetInt(ctype) ^ vr.GetInt(ctype)); + break; + default: + mErrors->Error(exp->mLocation, EERR_INCOMPATIBLE_OPERATOR, "Incompatible operator", TokenNames[exp->mToken]); + } + } + + return v; +} + +ConstexprInterpreter::Value ConstexprInterpreter::EvalRelational(Expression* exp, const Value& vl, const Value& vr) +{ + Value v(exp->mLocation, TheBoolTypeDeclaration); + + bool check = false; + if (vl.mDecType->mType == DT_TYPE_FLOAT || vr.mDecType->mType == DT_TYPE_FLOAT) + { + switch (exp->mToken) + { + case TK_EQUAL: + check = vl.GetFloat() == vr.GetFloat(); + break; + case TK_NOT_EQUAL: + check = vl.GetFloat() != vr.GetFloat(); + break; + case TK_GREATER_THAN: + check = vl.GetFloat() > vr.GetFloat(); + break; + case TK_GREATER_EQUAL: + check = vl.GetFloat() >= vr.GetFloat(); + break; + case TK_LESS_THAN: + check = vl.GetFloat() < vr.GetFloat(); + break; + case TK_LESS_EQUAL: + check = vl.GetFloat() <= vr.GetFloat(); + break; + default: + mErrors->Error(exp->mLocation, EERR_INCOMPATIBLE_OPERATOR, "Incompatible operator", TokenNames[exp->mToken]); + } + } + else if (vl.mDecType->mType == DT_TYPE_POINTER && vr.mDecType->mType == DT_TYPE_POINTER) + { + Value pl = vl.GetPtr(); + Value pr = vr.GetPtr(); + + if (pl.mBaseValue == pr.mBaseValue) + { + switch (exp->mToken) + { + case TK_EQUAL: + check = pl.mOffset == pr.mOffset; + break; + case TK_NOT_EQUAL: + check = pl.mOffset != pr.mOffset; + break; + case TK_GREATER_THAN: + check = pl.mOffset > pr.mOffset; + break; + case TK_GREATER_EQUAL: + check = pl.mOffset >= pr.mOffset; + break; + case TK_LESS_THAN: + check = pl.mOffset < pr.mOffset; + break; + case TK_LESS_EQUAL: + check = pl.mOffset <= pr.mOffset; + break; + default: + mErrors->Error(exp->mLocation, EERR_INCOMPATIBLE_OPERATOR, "Incompatible operator", TokenNames[exp->mToken]); + } + } + else + { + switch (exp->mToken) + { + case TK_EQUAL: + check = false; + break; + case TK_NOT_EQUAL: + check = true; + break; + default: + mErrors->Error(exp->mLocation, EERR_INCOMPATIBLE_OPERATOR, "Incompatible operator", TokenNames[exp->mToken]); + } + } + } + else + { + Declaration* ctype = CombinedIntType(vl.mDecType, vr.mDecType); + switch (exp->mToken) + { + case TK_EQUAL: + check = vl.GetInt(ctype) == vr.GetInt(ctype); + break; + case TK_NOT_EQUAL: + check = vl.GetInt(ctype) != vr.GetInt(ctype); + break; + case TK_GREATER_THAN: + check = vl.GetInt(ctype) > vr.GetInt(ctype); + break; + case TK_GREATER_EQUAL: + check = vl.GetInt(ctype) >= vr.GetInt(ctype); + break; + case TK_LESS_THAN: + check = vl.GetInt(ctype) < vr.GetInt(ctype); + break; + case TK_LESS_EQUAL: + check = vl.GetInt(ctype) <= vr.GetInt(ctype); + break; + default: + mErrors->Error(exp->mLocation, EERR_INCOMPATIBLE_OPERATOR, "Incompatible operator", TokenNames[exp->mToken]); + } + } + + v.PutInt(check ? 1 : 0); + + return v; +} + +ConstexprInterpreter::Value ConstexprInterpreter::EvalUnary(Expression* exp, const Value& vl) +{ + Value v(exp->mLocation, exp->mDecType); + + if (exp->mDecType->mType == DT_TYPE_FLOAT) + { + switch (exp->mToken) + { + case TK_ADD: + v.PutFloat(vl.GetFloat()); + break; + case TK_SUB: + v.PutFloat(-vl.GetFloat()); + break; + case TK_INC: + v.PutFloat(vl.GetFloat() + 1); + break; + case TK_DEC: + v.PutFloat(vl.GetFloat() - 1); + break; + case TK_MUL: + return vl.GetPtr(); + case TK_SIZEOF: + v.PutInt(vl.mDecType->mSize); + break; + default: + mErrors->Error(exp->mLocation, EERR_INCOMPATIBLE_OPERATOR, "Incompatible operator", TokenNames[exp->mToken]); + } + } + else + { + switch (exp->mToken) + { + case TK_ADD: + v.PutInt(vl.GetInt()); + break; + case TK_SUB: + v.PutInt(-vl.GetInt()); + break; + case TK_BINARY_NOT: + v.PutInt(~vl.GetInt()); + break; + case TK_INC: + v.PutInt(vl.GetInt() + 1); + break; + case TK_DEC: + v.PutInt(vl.GetInt() - 1); + break; + case TK_BINARY_AND: + if (vl.mBaseValue) + v.PutPtr(vl); + else + mErrors->Error(exp->mLocation, EERR_INCOMPATIBLE_OPERATOR, "Not an addressable value"); + break; + case TK_MUL: + return vl.GetPtr(); + case TK_NEW: + v.PutPtr(Value(NewValue(exp, exp->mDecType->mBase, int(vl.GetInt())))); + break; + case TK_DELETE: + DeleteValue(vl.GetPtr().mBaseValue); + break; + default: + mErrors->Error(exp->mLocation, EERR_INCOMPATIBLE_OPERATOR, "Incompatible operator", TokenNames[exp->mToken]); + } + } + + return v; +} + +ConstexprInterpreter::Value ConstexprInterpreter::EvalTypeCast(Expression* exp, const Value& vl, Declaration* type) +{ + Value v(exp->mLocation, type); + + if (type->mType == DT_TYPE_FLOAT) + v.PutFloat(vl.GetFloat()); + else if (type->IsIntegerType()) + v.PutInt(vl.GetInt()); + + return v; +} + +ConstexprInterpreter::Value ConstexprInterpreter::REval(Expression* exp) +{ + return Eval(exp).ToRValue(mErrors); +} + +ConstexprInterpreter::Value ConstexprInterpreter::EvalCall(Expression* exp, ConstexprInterpreter* caller) +{ + mHeap = caller->mHeap; + + mProcType = exp->mLeft->mDecType; + + Expression* pex = exp->mRight; + Declaration* dec = exp->mLeft->mDecType->mParams; + + int pos = 0; + if (mProcType->mBase && mProcType->mBase->mType == DT_TYPE_STRUCT) + { + mResult = Value(exp->mLocation, mProcType->mBase); + mParams[0] = Value(&mResult); + pos = 2; + } + + while (pex && pex->mType == EX_LIST) + { + if (dec) + pos = dec->mVarIndex; + + if (dec) + mParams[pos] = caller->EvalCoerce(pex->mLeft, caller->Eval(pex->mLeft), dec->mBase); + else + mParams[pos] = caller->REval(pex->mLeft); + + pos += dec->mSize; + pex = pex->mRight; + if (dec) + dec = dec->mNext; + } + if (pex) + { + if (dec) + pos = dec->mVarIndex; + if (dec) + mParams[pos] = caller->EvalCoerce(pex, caller->Eval(pex), dec->mBase); + else + mParams[pos] = caller->REval(pex); + } + + if (exp->mLeft->mDecValue->mFlags & DTF_INTRINSIC) + { + const Ident* iname = exp->mLeft->mDecValue->mIdent; + + if (!strcmp(iname->mString, "fabs")) + { + mResult = Value(exp->mLocation, TheFloatTypeDeclaration); + mResult.PutFloat(fabs(mParams[0].GetFloat())); + } + else if (!strcmp(iname->mString, "floor")) + { + mResult = Value(exp->mLocation, TheFloatTypeDeclaration); + mResult.PutFloat(floor(mParams[0].GetFloat())); + } + else if (!strcmp(iname->mString, "ceil")) + { + mResult = Value(exp->mLocation, TheFloatTypeDeclaration); + mResult.PutFloat(ceil(mParams[0].GetFloat())); + } + else if (!strcmp(iname->mString, "sin")) + { + mResult = Value(exp->mLocation, TheFloatTypeDeclaration); + mResult.PutFloat(sin(mParams[0].GetFloat())); + } + else if (!strcmp(iname->mString, "cos")) + { + mResult = Value(exp->mLocation, TheFloatTypeDeclaration); + mResult.PutFloat(cos(mParams[0].GetFloat())); + } + else if (!strcmp(iname->mString, "tan")) + { + mResult = Value(exp->mLocation, TheFloatTypeDeclaration); + mResult.PutFloat(tan(mParams[0].GetFloat())); + } + else if (!strcmp(iname->mString, "log")) + { + mResult = Value(exp->mLocation, TheFloatTypeDeclaration); + mResult.PutFloat(log(mParams[0].GetFloat())); + } + else if (!strcmp(iname->mString, "exp")) + { + mResult = Value(exp->mLocation, TheFloatTypeDeclaration); + mResult.PutFloat(::exp(mParams[0].GetFloat())); + } + else if (!strcmp(iname->mString, "sqrt")) + { + mResult = Value(exp->mLocation, TheFloatTypeDeclaration); + mResult.PutFloat(::sqrt(mParams[0].GetFloat())); + } + else if (!strcmp(iname->mString, "atan")) + { + mResult = Value(exp->mLocation, TheFloatTypeDeclaration); + mResult.PutFloat(::atan(mParams[0].GetFloat())); + } + else if (!strcmp(iname->mString, "atan2")) + { + mResult = Value(exp->mLocation, TheFloatTypeDeclaration); + mResult.PutFloat(::atan2(mParams[0].GetFloat(), mParams[1].GetFloat())); + } + else if (!strcmp(iname->mString, "pow")) + { + mResult = Value(exp->mLocation, TheFloatTypeDeclaration); + mResult.PutFloat(::pow(mParams[0].GetFloat(), mParams[1].GetFloat())); + } + else + mErrors->Error(exp->mLeft->mDecValue->mLocation, EERR_OBJECT_NOT_FOUND, "Unknown intrinsic function", iname); + } + else + { + Execute(exp->mLeft->mDecValue->mValue); + UnwindDestructStack(0); + } + + return mResult; +} + +ConstexprInterpreter::Value ConstexprInterpreter::EvalCoerce(Expression* exp, const Value& vl, Declaration* type) +{ + if (type->IsReference() || vl.mDecType->mType == DT_TYPE_ARRAY && type->mType == DT_TYPE_ARRAY) + return vl; + else + { + Value v = vl.ToRValue(mErrors); + while (v.mDecType->IsReference()) + v.mDecType = v.mDecType->mBase; + + if (type->mType == DT_TYPE_FLOAT && v.mDecType->IsIntegerType()) + { + Value vf(exp->mLocation, type); + vf.PutFloat(v.GetFloat()); + v = vf; + } + else if (v.mDecType->mType == DT_TYPE_FLOAT && type->IsIntegerType()) + { + Value vf(exp->mLocation, type); + vf.PutInt(v.GetInt()); + v = vf; + } + else if (v.mDecType->IsIntegerType() && type->IsIntegerType() && v.mDecType != type) + { + int64 val = vl.GetInt(); + val &= 0xffffffffll >> (8 * (4 - type->mSize)); + if (type->mFlags & DTF_SIGNED) + { + if (val & (0x80000000ll >> (8 * (4 - type->mSize)))) + val -= 0x100000000ll >> (8 * (4 - type->mSize)); + } + + Value vf(exp->mLocation, type); + vf.PutInt(val); + v = vf; + } + + return v; + } +} + +ConstexprInterpreter::Value ConstexprInterpreter::Eval(Expression* exp) +{ + switch (exp->mType) + { + case EX_SCOPE: + return Eval(exp->mLeft); + case EX_CONSTANT: + if (exp->mDecType->mSize < 4) + return Value(exp); + else + { + Value* tmp = new Value(exp); + mTemps.Push(tmp); + return Value(tmp); + } + case EX_VARIABLE: + if (exp->mDecValue->mType == DT_ARGUMENT) + { + if (mParams[exp->mDecValue->mVarIndex].mBaseValue) + return mParams[exp->mDecValue->mVarIndex]; + else + return Value(&mParams[exp->mDecValue->mVarIndex]); + } + else if (exp->mDecValue->mType == DT_VARIABLE) + { + if (!(exp->mDecValue->mFlags & (DTF_STATIC | DTF_GLOBAL))) + { + if (!mLocals[exp->mDecValue->mVarIndex].mDataSize) + { + mLocals[exp->mDecValue->mVarIndex] = Value(exp->mDecValue->mLocation, exp->mDecValue->mBase); + return Value(&mLocals[exp->mDecValue->mVarIndex]); + } + else if (mLocals[exp->mDecValue->mVarIndex].mBaseValue) + return mLocals[exp->mDecValue->mVarIndex]; + else + return Value(&mLocals[exp->mDecValue->mVarIndex]); + } + } + break; + case EX_BINARY: + return EvalBinary(exp, REval(exp->mLeft), REval(exp->mRight)); + + case EX_RELATIONAL: + return EvalRelational(exp, REval(exp->mLeft), REval(exp->mRight)); + + case EX_PREFIX: + return EvalUnary(exp, Eval(exp->mLeft)); + + case EX_TYPECAST: + return EvalTypeCast(exp, REval(exp->mLeft), exp->mDecType); + + case EX_CALL: + { + ConstexprInterpreter cinter(exp->mLocation, mErrors, mDataSection); + return cinter.EvalCall(exp, this); + } + + case EX_LIST: + case EX_COMMA: + Eval(exp->mLeft); + return Eval(exp->mRight); + + case EX_CONDITIONAL: + { + Value v = REval(exp->mLeft); + if (v.GetBool()) + return Eval(exp->mRight->mLeft); + else + return Eval(exp->mRight->mRight); + } + + case EX_LOGICAL_AND: + { + Value v(exp->mLocation, TheBoolTypeDeclaration); + Value vl = REval(exp->mLeft); + if (!vl.GetBool()) + { + v.PutInt(0); + return v; + } + Value vr = REval(exp->mRight); + if (!vr.GetBool()) + { + v.PutInt(0); + return v; + } + v.PutInt(1); + return v; + } + + case EX_LOGICAL_OR: + { + Value v(exp->mLocation, TheBoolTypeDeclaration); + Value vl = REval(exp->mLeft); + if (vl.GetBool()) + { + v.PutInt(1); + return v; + } + Value vr = REval(exp->mRight); + if (vr.GetBool()) + { + v.PutInt(1); + return v; + } + v.PutInt(0); + return v; + } + + case EX_LOGICAL_NOT: + { + Value v(exp->mLocation, TheBoolTypeDeclaration); + Value vr = REval(exp->mLeft); + + if (vr.GetBool()) + v.PutInt(0); + else + v.PutInt(1); + return v; + } + + case EX_INITIALIZATION: + { + Value lexp = Eval(exp->mLeft); + Value rexp = Eval(exp->mRight); + lexp.Assign(EvalCoerce(exp, rexp, lexp.mDecType), mErrors); + return lexp; + } + + case EX_ASSIGNMENT: + { + Value lexp = Eval(exp->mLeft); + Value rexp = REval(exp->mRight); + + if (exp->mToken != TK_ASSIGN) + rexp = EvalBinary(exp, lexp.ToRValue(mErrors), rexp); + lexp.Assign(EvalCoerce(exp, rexp, lexp.mDecType), mErrors); + return lexp; + } + + case EX_POSTINCDEC: + { + Value vl = Eval(exp->mLeft); + Value vr = vl.ToRValue(mErrors); + vl.Assign(EvalUnary(exp, vr), mErrors); + return vr; + } + + case EX_PREINCDEC: + { + Value vl = Eval(exp->mLeft); + vl.Assign(EvalUnary(exp, vl.ToRValue(mErrors)), mErrors); + return vl; + } + + case EX_QUALIFY: + { + Value v = Eval(exp->mLeft); + if (v.mBaseValue) + return Value(exp->mLocation, v.mBaseValue, exp->mDecType, v.mOffset + exp->mDecValue->mOffset, exp->mDecValue->mShift, exp->mDecValue->mBits); + break; + } + + case EX_INDEX: + { + Value v = Eval(exp->mLeft); + Value vi = REval(exp->mRight); + + if (v.mDecType->mType == DT_TYPE_ARRAY) + { + if (v.mBaseValue) + return Value(exp->mLocation, v.mBaseValue, exp->mDecType, v.mOffset + v.mDecType->mBase->mSize * int(vi.GetInt())); + } + else if (v.mDecType->mType == DT_TYPE_POINTER) + { + Value p = v.GetPtr(); + return Value(exp->mLocation, p.mBaseValue, exp->mDecType, p.mOffset + v.mDecType->mBase->mSize * int(vi.GetInt())); + } + break; + } + + case EX_RESULT: + if (mParams[0].mBaseValue) + return mParams[0]; + else + return Value(&mParams[0]); + + case EX_CONSTRUCT: + if (exp->mLeft->mLeft) + Eval(exp->mLeft->mLeft); + return Eval(exp->mRight); + + case EX_VOID: + return Value(exp->mLocation); + + } + + mErrors->Error(exp->mLocation, EERR_INVALID_CONSTEXPR, "Invalid constexpr"); + + return Value(); +} + +ConstexprInterpreter::Flow ConstexprInterpreter::Execute(Expression* exp) +{ + for (;;) + { + switch (exp->mType) + { + case EX_SCOPE: + { + int ds = mDestructStack.Size(); + Flow f = Execute(exp->mLeft); + UnwindDestructStack(ds); + + return f; + } + + case EX_RETURN: + mResult = EvalCoerce(exp, Eval(exp->mLeft), mProcType->mBase); + return FLOW_RETURN; + case EX_CONSTANT: + case EX_VARIABLE: + case EX_BINARY: + case EX_RELATIONAL: + case EX_PREFIX: + case EX_TYPECAST: + case EX_CALL: + case EX_COMMA: + case EX_LIST: + case EX_CONDITIONAL: + case EX_LOGICAL_AND: + case EX_LOGICAL_OR: + case EX_LOGICAL_NOT: + case EX_INITIALIZATION: + case EX_ASSIGNMENT: + case EX_POSTINCDEC: + case EX_PREINCDEC: + case EX_QUALIFY: + case EX_INDEX: + case EX_RESULT: + Eval(exp); + return FLOW_NEXT; + + case EX_SEQUENCE: + if (exp->mRight) + { + Flow f = Execute(exp->mLeft); + if (f == FLOW_NEXT) + return Execute(exp->mRight); + return f; + } + else + return Execute(exp->mLeft); + + case EX_BREAK: + return FLOW_BREAK; + + case EX_CONTINUE: + return FLOW_CONTINUE; + + case EX_IF: + if (REval(exp->mLeft).GetInt()) + return Execute(exp->mRight->mLeft); + else if (exp->mRight->mRight) + return Execute(exp->mRight->mRight); + else + return FLOW_NEXT; + + case EX_SWITCH: + { + int64 v = REval(exp->mLeft).GetInt(); + + bool found = false; + Expression* sexp = exp->mRight; + while (sexp) + { + Expression* cexp = sexp->mLeft; + if (found || cexp->mType == EX_DEFAULT || v == REval(cexp->mLeft).GetInt()) + { + found = true; + if (cexp->mRight) + { + Flow f = Execute(cexp->mRight); + if (f == FLOW_BREAK) + return FLOW_NEXT; + else if (f != FLOW_NEXT) + return f; + } + } + sexp = sexp->mRight; + } + + return FLOW_NEXT; + } + + case EX_WHILE: + { + int ds = mDestructStack.Size(); + while (REval(exp->mLeft).GetInt()) + { + int ls = mDestructStack.Size(); + Flow f = Execute(exp->mRight); + UnwindDestructStack(ls); + + if (f == FLOW_RETURN) + return FLOW_RETURN; + else if (f == FLOW_BREAK) + break; + } + UnwindDestructStack(ds); + return FLOW_NEXT; + } + + case EX_DO: + { + int ds = mDestructStack.Size(); + do { + int ls = mDestructStack.Size(); + Flow f = Execute(exp->mRight); + UnwindDestructStack(ls); + + if (f == FLOW_RETURN) + return FLOW_RETURN; + else if (f == FLOW_BREAK) + break; + + } while (REval(exp->mLeft).GetInt()); + UnwindDestructStack(ds); + return FLOW_NEXT; + } + + case EX_FOR: + { + int ds = mDestructStack.Size(); + if (exp->mLeft->mRight) + Eval(exp->mLeft->mRight); + + while (!exp->mLeft->mLeft->mLeft || REval(exp->mLeft->mLeft->mLeft).GetInt()) + { + int ls = mDestructStack.Size(); + Flow f = Execute(exp->mRight); + UnwindDestructStack(ls); + + if (f == FLOW_RETURN) + return FLOW_RETURN; + else if (f == FLOW_BREAK) + break; + + if (exp->mLeft->mLeft->mRight) + Eval(exp->mLeft->mLeft->mRight); + } + UnwindDestructStack(ds); + + return FLOW_NEXT; + } + + case EX_CONSTRUCT: + if (exp->mLeft->mLeft) + Eval(exp->mLeft->mLeft); + + if (exp->mLeft->mRight) + mDestructStack.Push(exp->mLeft->mRight); + + if (exp->mRight) + return Execute(exp->mRight); + return FLOW_NEXT; + + case EX_VOID: + return FLOW_NEXT; + + default: + mErrors->Error(exp->mLocation, EERR_INVALID_CONSTEXPR, "Invalid constexpr"); + } + } +} + +void ConstexprInterpreter::UnwindDestructStack(int level) +{ + while (mDestructStack.Size() > level) + Eval(mDestructStack.Pop()); +} + +ConstexprInterpreter::ValueItem::ValueItem(void) + : mByte(0), mBaseValue(nullptr) +{ +} diff --git a/oscar64/oscar64/Constexpr.h b/oscar64/oscar64/Constexpr.h new file mode 100644 index 0000000..21e36b3 --- /dev/null +++ b/oscar64/oscar64/Constexpr.h @@ -0,0 +1,123 @@ +#pragma once + +#include "Declaration.h" + +class ConstexprInterpreter +{ +public: + ConstexprInterpreter(const Location & loc, Errors * err, LinkerSection * dataSection); + ~ConstexprInterpreter(void); + + Expression* EvalConstructor(Expression* exp); + Expression* EvalCall(Expression* exp); + Expression* EvalTempConstructor(Expression* exp); +protected: + + bool AddParam(int& pos, Expression* pex, Declaration* dec); + + struct Value; + + struct ValueItem + { + uint8 mByte; + const Value * mBaseValue; + + ValueItem(void); + }; + + struct Value + { + ~Value(void); + Value(const Location& location); + Value(Expression * exp); + Value(const Location& location, Declaration * dec); + Value(const Location& location, Declaration* dec, int count); + Value(const Value& value); + Value(Value&& value); + Value(const Location& location, const Value * value, Declaration * type, int offset); + Value(const Location& location, const Value* value, Declaration* type, int offset, int shift, int bits); + Value(const Location& location, Declaration * value, Declaration* type, int offset); + Value(Value* value); + Value(const Location& location, const uint8 * data, Declaration* type); + Value(const Location& location, const ValueItem* data, Declaration* type); + Value(void); + + Value& operator=(const Value& v); + Value& operator=(Value&& v); + + Value ToRValue(Errors* err) const; + Expression* ToExpression(LinkerSection* dataSection) const; + void Assign(const Value& v, Errors* err); + + Location mLocation; + Declaration * mDecType, * mDecValue; + const Value * mBaseValue; + int mOffset, mShift, mBits; + ValueItem * mData; + int mDataSize; + ValueItem mShortData[4]; + + bool CheckSpace(int at, int size) const; + + ValueItem* GetAddr(void); + const ValueItem* GetAddr(void) const; + + int64 GetInt(void) const; + int64 GetInt(Declaration * type) const; + double GetFloat(void) const; + Value GetPtr(void) const; + bool GetBool(void) const; + + void PutInt(int64 v); + void PutFloat(double v); + void PutPtr(const Value& v); + + int64 GetIntAt(int at, Declaration* type) const; + double GetFloatAt(int at, Declaration* type) const; + Value GetPtrAt(int at, Declaration* type) const; + void PutIntAt(int64 v, int at, Declaration* type); + void PutFloatAt(double v, int at, Declaration* type); + void PutPtrAt(const Value& v, int at, Declaration* type); + void PutVarAt(Declaration* var, int64 v, int at, Declaration* type); + + void PutConst(int offset, Declaration * dec); + Declaration* GetConst(int offset, Declaration* type, LinkerSection* dataSection) const; + + }; + + Value * NewValue(Expression* exp, Declaration* type, int size); + void DeleteValue(const Value* v); + + Value EvalCall(Expression* exp, ConstexprInterpreter* caller); + Value EvalBinary(Expression* exp, const Value& vl, const Value& vr); + Value EvalUnary(Expression* exp, const Value& vl); + Value EvalRelational(Expression* exp, const Value& vl, const Value& vr); + Value EvalTypeCast(Expression* exp, const Value& vl, Declaration* type); + Value EvalCoerce(Expression* exp, const Value& vl, Declaration* type); + + Value REval(Expression* exp); + Value Eval(Expression* exp); + + enum Flow + { + FLOW_NEXT, + FLOW_CONTINUE, + FLOW_BREAK, + FLOW_RETURN + }; + + Flow Execute(Expression* exp); + void UnwindDestructStack(int level); + + Declaration* mProcType; + Location mLocation; + LinkerSection* mDataSection; + GrowingArray mParams, mLocals; + ExpandingArray mDestructStack; + ExpandingArray * mHeap; + ExpandingArray mTemps; + + Errors * mErrors; + Value mResult; + +}; \ No newline at end of file diff --git a/oscar64/oscar64/Declaration.cpp b/oscar64/oscar64/Declaration.cpp new file mode 100644 index 0000000..b0b15ea --- /dev/null +++ b/oscar64/oscar64/Declaration.cpp @@ -0,0 +1,3549 @@ +#include "Declaration.h" +#include "Constexpr.h" +#include "Linker.h" +#include + +DeclarationScope::DeclarationScope(DeclarationScope* parent, ScopeLevel level, const Ident* name) +{ + mParent = parent; + mLevel = level; + mName = name; + mHashSize = 0; + mHashFill = 0; + mHash = nullptr; +} + +void DeclarationScope::Clear(void) +{ + mHashFill = 0; + if (mHash) + { + for (int i = 0; i < mHashSize; i++) + { + mHash[i].mDec = nullptr; + mHash[i].mIdent = nullptr; + } + } +} + +DeclarationScope::~DeclarationScope(void) +{ + delete[] mHash; +} + +DeclarationScope* DeclarationScope::Clone(void) const +{ + DeclarationScope* scope = new DeclarationScope(mParent, mLevel, mName); + for (int i = 0; i < mHashSize; i++) + { + if (mHash[i].mIdent) + scope->Insert(mHash[i].mIdent, mHash[i].mDec); + } + return scope; +} + +const Ident* DeclarationScope::Mangle(const Ident* ident) const +{ + if (mName && ident) + { + char buffer[200]; + strcpy_s(buffer, mName->mString); + strcat_s(buffer, "::"); + strcat_s(buffer, ident->mString); + return Ident::Unique(buffer); + } + else + return ident; +} + +void DeclarationScope::UseScope(DeclarationScope* scope) +{ + mUsed.Push(scope); +} + +Declaration * DeclarationScope::Insert(const Ident* ident, Declaration* dec) +{ + if (!mHash) + { + mHashSize = 16; + mHashFill = 0; + mHash = new Entry[mHashSize]; + for (int i = 0; i < mHashSize; i++) + { + mHash[i].mDec = nullptr; + mHash[i].mIdent = nullptr; + } + } + + int hm = mHashSize - 1; + int hi = ident->mHash & hm; + + while (mHash[hi].mIdent) + { + if (ident == mHash[hi].mIdent) + return mHash[hi].mDec; + hi = (hi + 1) & hm; + } + + mHash[hi].mIdent = ident; + mHash[hi].mDec = dec; + mHashFill++; + + if (2 * mHashFill >= mHashSize) + { + int size = mHashSize; + Entry* entries = mHash; + mHashSize *= 2; + mHashFill = 0; + mHash = new Entry[mHashSize]; + for (int i = 0; i < mHashSize; i++) + { + mHash[i].mDec = nullptr; + mHash[i].mIdent = nullptr; + } + for (int i = 0; i < size; i++) + { + if (entries[i].mIdent) + Insert(entries[i].mIdent, entries[i].mDec); + } + delete[] entries; + } + + return nullptr; +} + +Declaration* DeclarationScope::Lookup(const Ident* ident, ScopeLevel limit) +{ + if (mLevel < limit) + return nullptr; + + if (mHashSize > 0) + { + unsigned int hm = mHashSize - 1; + unsigned int hi = ident->mHash & hm; + + while (mHash[hi].mIdent) + { + if (ident == mHash[hi].mIdent) + return mHash[hi].mDec; + hi = (hi + 1) & hm; + } + } + + if (limit == SLEVEL_SCOPE) + return nullptr; + + for (int i = 0; i < mUsed.Size(); i++) + { + Declaration* dec = mUsed[i]->Lookup(ident, SLEVEL_NAMESPACE); + if (dec) + return dec; + } + + if (limit == SLEVEL_USING) + return nullptr; + + return mParent ? mParent->Lookup(ident, limit) : nullptr; +} + +void DeclarationScope::End(const Location& loc) +{ + for (int i = 0; i < mHashSize; i++) + { + if (mHash[i].mDec) + mHash[i].mDec->mEndLocation = loc; + } +} + +Expression::Expression(const Location& loc, ExpressionType type) + : mLocation(loc), mEndLocation(loc), mType(type), mLeft(nullptr), mRight(nullptr), mConst(false), mDecType(nullptr), mDecValue(nullptr), mToken(TK_NONE), mFlags(0) +{ + static uint32 gUID = 0; + mUID = gUID++; +} + +Expression::~Expression(void) +{ + +} + +Expression* Expression::ListAppend(Expression* lexp) +{ + if (lexp) + { + Expression* nexp = new Expression(mLocation, EX_LIST); + nexp->mLeft = lexp; + nexp->mRight = this; + nexp->mDecType = mDecType; + + return nexp; + } + else + return this; +} + +void Expression::Dump(int ident) const +{ + for (int i = 0; i < ident; i++) + printf("| "); + + switch (mType) + { + case EX_ERROR: + printf("ERROR"); + break; + case EX_VOID: + printf("VOID"); + break; + case EX_CONSTANT: + printf("CONST"); + if (mDecValue->mIdent) + printf(" '%s'", mDecValue->mIdent->mString); + break; + case EX_VARIABLE: + printf("VAR"); + if (mDecValue->mIdent) + printf(" '%s'", mDecValue->mIdent->mString); + break; + case EX_ASSIGNMENT: + printf("ASSIGN<%s>", TokenNames[mToken]); + break; + case EX_INITIALIZATION: + printf("INIT"); + break; + case EX_BINARY: + printf("BINARY<%s>", TokenNames[mToken]); + break; + case EX_RELATIONAL: + printf("RELATIONAL<%s>", TokenNames[mToken]); + break; + case EX_PREINCDEC: + printf("PREOP<%s>", TokenNames[mToken]); + break; + case EX_PREFIX: + printf("PREFIX<%s>", TokenNames[mToken]); + break; + case EX_POSTFIX: + printf("POSTFIX<%s>", TokenNames[mToken]); + break; + case EX_POSTINCDEC: + printf("POSTOP<%s>", TokenNames[mToken]); + break; + case EX_INDEX: + printf("INDEX"); + break; + case EX_QUALIFY: + if (mDecValue->mIdent) + printf("QUALIFY<%s>", mDecValue->mIdent->mString); + else + printf("QUALIFY<%d>", mDecValue->mOffset); + break; + case EX_CALL: + printf("CALL"); + break; + case EX_INLINE: + printf("INLINE"); + break; + case EX_VCALL: + printf("VCALL"); + break; + case EX_DISPATCH: + printf("DISPATCH"); + break; + case EX_LIST: + printf("LIST"); + break; + case EX_COMMA: + printf("COMMA"); + break; + case EX_RETURN: + printf("RETURN"); + break; + case EX_SEQUENCE: + printf("SEQUENCE"); + break; + case EX_WHILE: + printf("WHILE"); + break; + case EX_IF: + printf("IF"); + break; + case EX_ELSE: + printf("ELSE"); + break; + case EX_FOR: + printf("FOR"); + break; + case EX_FORBODY: + printf("FORBODY"); + break; + case EX_DO: + printf("DO"); + break; + case EX_SCOPE: + printf("SCOPE"); + break; + case EX_BREAK: + printf("BREAK"); + break; + case EX_CONTINUE: + printf("CONTINUE"); + break; + case EX_TYPE: + printf("TYPE"); + break; + case EX_TYPECAST: + printf("TYPECAST"); + break; + case EX_LOGICAL_AND: + printf("AND"); + break; + case EX_LOGICAL_OR: + printf("OR"); + break; + case EX_LOGICAL_NOT: + printf("NOT"); + break; + case EX_ASSEMBLER: + printf("ASSEMBLER"); + break; + case EX_UNDEFINED: + printf("UNDEFINED"); + break; + case EX_SWITCH: + printf("SWITCH"); + break; + case EX_CASE: + printf("CASE"); + break; + case EX_DEFAULT: + printf("DEFAULT"); + break; + case EX_CONDITIONAL: + printf("COND"); + break; + case EX_ASSUME: + printf("ASSUME"); + break; + case EX_BANKOF: + printf("BANKOF"); + break; + case EX_CONSTRUCT: + printf("CONSTRUCT"); + break; + case EX_CLEANUP: + printf("CLEANUP"); + break; + case EX_RESULT: + printf("RESULT"); + break; + case EX_PACK: + printf("PACK"); + break; + case EX_PACK_TYPE: + printf("PACK_TYPE"); + break; + case EX_GOTO: + printf("GOTO %s", mDecValue->mIdent->mString); + break; + case EX_LABEL: + printf("LABEL %s", mDecValue->mIdent->mString); + break; + case EX_AGGREGATE: + printf("AGGREGATE"); + break; + } + printf("\n"); + + if (mLeft) + mLeft->Dump(ident + 1); + if (mRight) + mRight->Dump(ident + 1); +} + +bool Expression::HasSideEffects(void) const +{ + switch (mType) + { + case EX_VARIABLE: + case EX_CONSTANT: + return false; + + case EX_BINARY: + case EX_RELATIONAL: + case EX_INDEX: + return mLeft->HasSideEffects() || mRight->HasSideEffects(); + + case EX_QUALIFY: + case EX_TYPECAST: + case EX_PREFIX: + case EX_POSTFIX: + return mLeft->HasSideEffects(); + + default: + return true; + } +} + +bool Expression::IsRValue(void) const +{ + if (mDecType->mType == DT_TYPE_RVALUEREF) + return true; + else if (mDecType->mType == DT_TYPE_REFERENCE) + return false; + else if (mType == EX_VARIABLE) + { + if (mDecValue->mFlags & DTF_TEMPORARY) + return true; + else + return false; + } + else if (mType == EX_QUALIFY || mType == EX_INDEX || mType == EX_ASSIGNMENT || mType == EX_CONSTANT || mType == EX_PREFIX && mToken == TK_MUL) + return false; + else + return true; +} + +bool Expression::IsVolatile(void) const +{ + if (mDecType && mDecType->mFlags & DTF_VOLATILE) + return true; + if (mLeft && mLeft->IsVolatile()) + return true; + if (mRight && mRight->IsVolatile()) + return true; + return false; +} + +bool Expression::IsConstRef(void) const +{ + if (mDecType->mType == DT_TYPE_RVALUEREF || mDecType->mType == DT_TYPE_REFERENCE) + return true; + else if (mType == EX_VARIABLE || mType == EX_QUALIFY || mType == EX_INDEX || mType == EX_PREFIX && mToken == TK_MUL) + return true; + else + return false; +} + +bool Expression::IsLValue(void) const +{ + if (mDecType->mFlags & DTF_CONST) + return false; + else if (mDecType->mType == DT_TYPE_RVALUEREF || mDecType->mType == DT_TYPE_REFERENCE) + return true; + else if (mType == EX_VARIABLE || mType == EX_QUALIFY || mType == EX_INDEX || mType == EX_PREFIX && mToken == TK_MUL) + return true; + else + return false; +} + +bool Expression::IsSame(const Expression* exp) const +{ + if (!exp || mType != exp->mType) + return false; + + switch (mType) + { + case EX_VARIABLE: + return mDecValue == exp->mDecValue; + + default: + return false; + } +} + +Expression* Expression::LogicInvertExpression(void) +{ + if (mType == EX_LOGICAL_NOT) + return mLeft; + else if (mType == EX_LOGICAL_AND) + { + mType = EX_LOGICAL_OR; + mLeft = mLeft->LogicInvertExpression(); + mRight = mRight->LogicInvertExpression(); + return this; + } + else if (mType == EX_LOGICAL_OR) + { + mType = EX_LOGICAL_AND; + mLeft = mLeft->LogicInvertExpression(); + mRight = mRight->LogicInvertExpression(); + return this; + } + else if (mType == EX_RELATIONAL) + { + switch (mToken) + { + case TK_EQUAL: + mToken = TK_NOT_EQUAL; + break; + case TK_NOT_EQUAL: + mToken = TK_EQUAL; + break; + case TK_GREATER_THAN: + mToken = TK_LESS_EQUAL; + break; + case TK_GREATER_EQUAL: + mToken = TK_LESS_THAN; + break; + case TK_LESS_THAN: + mToken = TK_GREATER_EQUAL; + break; + case TK_LESS_EQUAL: + mToken = TK_GREATER_THAN; + break; + } + + return this; + } + else + { + Expression* ex = new Expression(mLocation, EX_LOGICAL_NOT); + ex->mLeft = this; + ex->mDecType = TheBoolTypeDeclaration; + return ex; + } +} + +void Expression::ReplaceVariable(Declaration* pvar, Declaration* nvar) +{ + if (mLeft) mLeft->ReplaceVariable(pvar, nvar); + if (mRight) mRight->ReplaceVariable(pvar, nvar); + + if (mType == EX_VARIABLE && mDecValue == pvar) + mDecValue = nvar; +} + +Expression* Expression::ToVarConst(Declaration* pvar, Declaration* pconst) const +{ + if (mType == EX_VARIABLE && mDecValue == pvar) + { + Expression* exp = new Expression(mLocation, EX_CONSTANT); + exp->mDecType = mDecType; + exp->mDecValue = pconst; + exp->mConst = true; + return exp; + } + else + { + Expression* exp = new Expression(mLocation, mType); + exp->mToken = mToken; + exp->mDecType = mDecType; + exp->mDecValue = mDecValue; + exp->mConst = mConst; + if (mLeft) exp->mLeft = mLeft->ToVarConst(pvar, pconst); + if (mRight) exp->mRight = mRight->ToVarConst(pvar, pconst); + return exp; + } +} + + +Expression* Expression::Clone(void) const +{ + return this->Clone(mLocation); +} + +Expression* Expression::Clone(const Location& loc) const +{ + Expression* exp = new Expression(loc, mType); + exp->mToken = mToken; + exp->mDecType = mDecType; + exp->mDecValue = mDecValue; + exp->mConst = mConst; + if (mLeft) exp->mLeft = mLeft->Clone(); + if (mRight) exp->mRight = mRight->Clone(); + return exp; +} + +Expression* Expression::ToAlternateThis(Declaration* pthis, Declaration* nthis) +{ + Expression* left = mLeft ? mLeft->ToAlternateThis(pthis, nthis) : nullptr; + Expression* right = mRight ? mRight->ToAlternateThis(pthis, nthis) : nullptr; + Declaration* decType = mDecType, * decValue = mDecValue; + + Declaration* lp = pthis, * np = nthis; + while (lp) + { + if (decType == lp->mBase) + decType = np->mBase; + else if (decType == lp->mBase->mBase) + decType = np->mBase->mBase; + if (decValue == lp) + decValue = np; + + lp = lp->mNext; + np = np->mNext; + } + + if (mType == EX_QUALIFY && mLeft->mDecType != left->mDecType) + { + Declaration* pe = mLeft->mDecType->mParams, * ne = left->mDecType->mParams; + while (pe && ne && pe != mDecValue) + { + pe = pe->mNext; + ne = ne->mNext; + } + decValue = ne; + decType = ne->mBase; + } + + if (left == mLeft && right == mRight && decType == mDecType && decValue == mDecValue) + return this; + else + { + Expression* nexp = new Expression(mLocation, mType); + nexp->mLeft = left; + nexp->mRight = right; + nexp->mDecType = decType; + nexp->mDecValue = decValue; + nexp->mToken = mToken; + nexp->mConst = mConst; + + return nexp; + } +} + +Expression* Expression::ConstantDereference(Errors* errors, LinkerSection* dataSection) +{ + if (mType == EX_VARIABLE) + { + if (mDecType->IsSimpleType()) + { + // A local initialization is executable code and must not be evaluated again at each use. + if (mDecValue->mType == DT_VARIABLE && (mDecValue->mFlags & DTF_CONST) && + mDecValue->mValue && mDecValue->mValue->mType == EX_CONSTANT) + return mDecValue->mValue; + else if (mDecValue->mType == DT_VARIABLE_REF && (mDecValue->mFlags & DTF_CONST) && mDecValue->mBase->mValue && mDecValue->mBase->mValue->mType == EX_CONSTANT && mDecValue->mBase->mValue->mDecValue->mType == DT_CONST_STRUCT) + { + Declaration* mdec = mDecValue->mBase->mValue->mDecValue->mParams; + int coffset = mDecValue->mOffset; + while (mdec) + { + if (mdec->mType == DT_CONST_STRUCT) + { + if (mdec->mOffset > coffset || mdec->mOffset + mdec->mSize <= coffset) + mdec = mdec->mNext; + else + { + coffset -= mdec->mOffset; + mdec = mdec->mParams; + } + } + else if (mdec->mOffset != coffset) + mdec = mdec->mNext; + else + { + Expression* ex = new Expression(mLocation, EX_CONSTANT); + ex->mDecValue = mdec; + ex->mDecType = mDecType; + return ex; + } + } + } + } + } + else if (mType == EX_BINARY || mType == EX_RELATIONAL) + { + Expression* lexp = mLeft->ConstantDereference(errors, dataSection); + Expression* rexp = mRight->ConstantDereference(errors, dataSection); + if (lexp != mLeft || rexp != mRight) + { + Expression* nexp = new Expression(mLocation, mType); + nexp->mToken = mToken; + nexp->mDecType = mDecType; + nexp->mLeft = lexp; + nexp->mRight = rexp; + return nexp->ConstantFold(errors, dataSection)->ConstantDereference(errors, dataSection); + } + } + else if (mType == EX_PREFIX) + { + Expression* lexp = mLeft->ConstantDereference(errors, dataSection); + if (lexp != mLeft) + { + Expression* nexp = new Expression(mLocation, mType); + nexp->mToken = mToken; + nexp->mDecType = mDecType; + nexp->mLeft = lexp; + return nexp->ConstantFold(errors, dataSection)->ConstantDereference(errors, dataSection); + } + } + + return this; +} + +static int64 signextend(int64 v, Declaration* type) +{ + if (type->mFlags & DTF_SIGNED) + { + switch (type->mSize) + { + case 1: + if (v & 0x80) + return (v & 0xff) - 0x100; + else + return v; + case 2: + if (v & 0x8000) + return (v & 0xffff) - 0x10000; + else + return v; + default: + return v; + } + + } + else + { + switch (type->mSize) + { + case 1: + return v & 0xff; + case 2: + return v & 0xffff; + default: + return v & 0xffffffff; + } + } +} + +Expression* Expression::ConstantFold(Errors * errors, LinkerSection * dataSection, Linker* linker) +{ + if (mType == EX_PREFIX && mToken == TK_BANKOF && linker) + { + LinkerRegion* rgn; + if (mLeft->mDecValue && mLeft->mDecValue->mSection && (rgn = linker->FindRegionOfSection(mLeft->mDecValue->mSection))) + { + uint64 i = 0; + while (i < 64 && rgn->mCartridgeBanks != (1ULL << i)) + i++; + if (i >= 64) + i = 0xff; + + if (i < 64 || (mLeft->mDecValue->mFlags & DTF_DEFINED)) + { + Expression* ex = new Expression(mLocation, EX_CONSTANT); + Declaration* dec = new Declaration(mLocation, DT_CONST_INTEGER); + dec->mBase = TheSignedIntTypeDeclaration; + dec->mInteger = i; + ex->mDecValue = dec; + ex->mDecType = dec->mBase; + return ex; + } + } + + return this; + } + else if (mType == EX_PREFIX && mToken == TK_SIZEOF) + { + if (mLeft->mDecType->mFlags & DTF_DEFINED) + { + Expression* ex = new Expression(mLocation, EX_CONSTANT); + Declaration* dec = new Declaration(mLocation, DT_CONST_INTEGER); + dec->mBase = TheSignedIntTypeDeclaration; + dec->mInteger = mLeft->mDecType->mSize; + ex->mDecValue = dec; + ex->mDecType = dec->mBase; + return ex; + } + } + else if (mType == EX_PREFIX && mLeft->mType == EX_CONSTANT) + { + + if (mLeft->mDecValue->mType == DT_CONST_INTEGER) + { + switch (mToken) + { + case TK_ADD: + return mLeft; + case TK_SUB: + { + Expression* ex = new Expression(mLocation, EX_CONSTANT); + Declaration * dec = new Declaration(mLocation, DT_CONST_INTEGER); + + if (mLeft->mDecValue->mBase->mSize < 2) + dec->mBase = TheSignedIntTypeDeclaration; + else + dec->mBase = mLeft->mDecValue->mBase; + + dec->mInteger = signextend ( - mLeft->mDecValue->mInteger, dec->mBase ); + + ex->mDecValue = dec; + ex->mDecType = dec->mBase; + return ex; + } + case TK_BINARY_NOT: + { + Expression* ex = new Expression(mLocation, EX_CONSTANT); + Declaration* dec = new Declaration(mLocation, DT_CONST_INTEGER); + + if (mLeft->mDecValue->mBase->mSize < 2) + dec->mBase = TheSignedIntTypeDeclaration; + else + dec->mBase = mLeft->mDecValue->mBase; + + dec->mInteger = signextend( ~mLeft->mDecValue->mInteger, dec->mBase ); + + ex->mDecValue = dec; + ex->mDecType = dec->mBase; + return ex; + } + + } + } + else if (mLeft->mDecValue->mType == DT_CONST_FLOAT) + { + switch (mToken) + { + case TK_ADD: + return mLeft; + case TK_SUB: + { + Expression* ex = new Expression(mLocation, EX_CONSTANT); + Declaration* dec = new Declaration(mLocation, DT_CONST_FLOAT); + dec->mBase = TheFloatTypeDeclaration; + dec->mNumber = -mLeft->mDecValue->mNumber; + ex->mDecValue = dec; + ex->mDecType = TheFloatTypeDeclaration; + return ex; + } + + } + } + else if (mLeft->mDecValue->mType == DT_CONST_FUNCTION) + { + switch (mToken) + { + case TK_BINARY_AND: + return mLeft; + } + } + } +#if 1 + else if (mType == EX_PREFIX && mToken == TK_BINARY_AND && mLeft->mType == EX_VARIABLE && (mLeft->mDecValue->mFlags & (DTF_STATIC | DTF_GLOBAL))) + { + Expression* ex = new Expression(mLocation, EX_CONSTANT); + Declaration* dec = new Declaration(mLocation, DT_CONST_POINTER); + dec->mValue = mLeft; + dec->mBase = mDecType; + ex->mDecValue = dec; + ex->mDecType = mDecType; + return ex; + } + else if (mType == EX_PREFIX && mToken == TK_BINARY_AND && mLeft->mType == EX_INDEX && + mLeft->mLeft->mType == EX_VARIABLE && mLeft->mLeft->mDecType->mType == DT_TYPE_ARRAY && + (mLeft->mLeft->mDecValue->mFlags & (DTF_STATIC | DTF_GLOBAL)) && mLeft->mRight->mType == EX_CONSTANT) + { + Declaration* vdec = mLeft->mLeft->mDecValue; + + Expression* ex = new Expression(mLocation, EX_VARIABLE); + Declaration* dec = new Declaration(mLocation, DT_VARIABLE_REF); + + if (vdec->mType == DT_VARIABLE_REF) + { + dec->mFlags = vdec->mFlags; + dec->mBase = vdec->mBase; + dec->mSize = mLeft->mLeft->mDecType->mBase->mSize - int(mLeft->mRight->mDecValue->mInteger) * dec->mSize; + dec->mOffset = int(mLeft->mRight->mDecValue->mInteger) * dec->mSize + vdec->mOffset; + } + else + { + dec->mFlags = vdec->mFlags; + dec->mBase = vdec; + dec->mSize = mLeft->mLeft->mDecType->mBase->mSize - int(mLeft->mRight->mDecValue->mInteger) * dec->mSize; + dec->mOffset = int(mLeft->mRight->mDecValue->mInteger) * dec->mSize; + } + ex->mDecValue = dec; + ex->mDecType = mLeft->mLeft->mDecType; + return ex; + } + else if (mType == EX_PREFIX && mToken == TK_BINARY_AND && mLeft->mType == EX_PREFIX && mLeft->mToken == TK_MUL) + { + return mLeft->mLeft; + } +#endif +#if 1 + else if (mType == EX_PREFIX && mToken == TK_BINARY_AND && + mLeft->mType == EX_QUALIFY && + mLeft->mLeft->mType == EX_PREFIX && mLeft->mLeft->mToken == TK_MUL && + mLeft->mLeft->mLeft->mType == EX_CONSTANT) + { + Expression* bex = mLeft->mLeft->mLeft; + if (bex->mDecValue->mType == DT_CONST_ADDRESS) + { + Expression* ex = new Expression(mLocation, EX_CONSTANT); + Declaration* dec = new Declaration(mLocation, DT_CONST_ADDRESS); + dec->mBase = mDecType; + dec->mInteger = bex->mDecValue->mInteger + mLeft->mDecValue->mOffset; + ex->mDecValue = dec; + ex->mDecType = mDecType; + return ex; + } + return this; + } +#endif + else if (mType == EX_TYPECAST && mLeft->mType == EX_CONSTANT) + { + if (mDecType->mType == DT_TYPE_POINTER) + { + if (mLeft->mDecValue->mType == DT_CONST_ADDRESS || mLeft->mDecValue->mType == DT_CONST_INTEGER) + { + Expression* ex = new Expression(mLocation, EX_CONSTANT); + Declaration* dec = new Declaration(mLocation, DT_CONST_ADDRESS); + dec->mBase = mDecType; + dec->mInteger = mLeft->mDecValue->mInteger; + ex->mDecValue = dec; + ex->mDecType = mDecType; + return ex; + } + else if (mLeft->mDecValue->mType == DT_CONST_FUNCTION) + { + Expression* ex = new Expression(mLocation, EX_CONSTANT); + ex->mDecValue = mLeft->mDecValue; + ex->mDecType = mDecType; + return ex; + } + else if (mLeft->mDecValue->mType == DT_CONST_POINTER) + { + Expression* ex = new Expression(mLocation, EX_CONSTANT); + ex->mDecValue = mLeft->mDecValue; + ex->mDecType = mDecType; + return ex; + } + } + else if (mDecType->mType == DT_TYPE_INTEGER) + { + if (mLeft->mDecValue->mType == DT_CONST_FLOAT) + { + Expression* ex = new Expression(mLocation, EX_CONSTANT); + Declaration* dec = new Declaration(mLocation, DT_CONST_INTEGER); + dec->mBase = mDecType; + dec->mInteger = int64(mLeft->mDecValue->mNumber); + ex->mDecValue = dec; + ex->mDecType = mDecType; + return ex; + } + else if (mLeft->mDecValue->mType == DT_CONST_INTEGER || mLeft->mDecValue->mType == DT_CONST_ADDRESS) + { + int64 sval = 1ULL << (8 * mDecType->mSize); + int64 v = mLeft->mDecValue->mInteger & (sval - 1); + + if (mDecType->mFlags & DTF_SIGNED) + { + if (v & (sval >> 1)) + v -= sval; + } + + Expression* ex = new Expression(mLocation, EX_CONSTANT); + Declaration* dec = new Declaration(mLocation, DT_CONST_INTEGER); + dec->mBase = mDecType; + dec->mInteger = v; + ex->mDecValue = dec; + ex->mDecType = mDecType; + return ex; + } + } + else if (mDecType->mType == DT_TYPE_FLOAT) + { + if (mLeft->mDecValue->mType == DT_CONST_INTEGER) + { + Expression* ex = new Expression(mLocation, EX_CONSTANT); + Declaration* dec = new Declaration(mLocation, DT_CONST_FLOAT); + dec->mBase = mDecType; + dec->mNumber = double(mLeft->mDecValue->mInteger); + ex->mDecValue = dec; + ex->mDecType = mDecType; + return ex; + } + } + } + else if (mType == EX_TYPECAST && mLeft->mType == EX_VARIABLE) + { + if (mDecType->mType == DT_TYPE_POINTER && (mLeft->mDecValue->mFlags & (DTF_STATIC | DTF_GLOBAL)) && mLeft->mDecType->mType == DT_TYPE_ARRAY) + { + Expression* ex = new Expression(mLocation, EX_CONSTANT); + Declaration* dec = new Declaration(mLocation, DT_CONST_POINTER); + dec->mValue = mLeft; + dec->mBase = mDecType; + ex->mDecValue = dec; + ex->mDecType = mDecType; + return ex; + } + } + else if (mType == EX_BINARY && mLeft->mType == EX_CONSTANT && mRight->mType == EX_CONSTANT) + { + if (mLeft->mDecValue->mType == DT_CONST_INTEGER && mRight->mDecValue->mType == DT_CONST_INTEGER) + { + int64 ival = 0, ileft = mLeft->mDecValue->mInteger, iright = mRight->mDecValue->mInteger; + + Declaration* dtype = TheSignedIntTypeDeclaration; + if (mLeft->mDecValue->mBase->mSize > mRight->mDecValue->mBase->mSize) + dtype = mLeft->mDecValue->mBase; + else if (mLeft->mDecValue->mBase->mSize < mRight->mDecValue->mBase->mSize) + dtype = mRight->mDecValue->mBase; + else if (mLeft->mDecValue->mBase->mSize > 1) + { + if ((mLeft->mDecValue->mBase->mFlags & DTF_SIGNED) && !(mRight->mDecValue->mBase->mFlags & DTF_SIGNED)) + dtype = mRight->mDecValue->mBase; + else + dtype = mLeft->mDecValue->mBase; + } + + switch (mToken) + { + case TK_ADD: + ival = ileft + iright; + break; + case TK_SUB: + ival = ileft - iright; + break; + case TK_MUL: + ival = ileft * iright; + break; + case TK_DIV: + if (iright == 0) + return this; + else if (dtype->mFlags & DTF_SIGNED) + ival = ileft / iright; + else + ival = (uint64)ileft / (uint64)iright; + break; + case TK_MOD: + if (iright == 0) + return this; + else if (dtype->mFlags & DTF_SIGNED) + ival = ileft % iright; + else + ival = (uint64)ileft % (uint64)iright; + break; + case TK_LEFT_SHIFT: + ival = ileft << iright; + break; + case TK_RIGHT_SHIFT: + ival = ileft >> iright; + break; + case TK_BINARY_AND: + ival = ileft & iright; + break; + case TK_BINARY_OR: + ival = ileft | iright; + break; + case TK_BINARY_XOR: + ival = ileft ^ iright; + break; + default: + ival = 0; + } + + Expression* ex = new Expression(mLocation, EX_CONSTANT); + Declaration* dec = new Declaration(mLocation, DT_CONST_INTEGER); + dec->mBase = dtype; + dec->mInteger = signextend(ival, dec->mBase); + ex->mDecValue = dec; + ex->mDecType = dec->mBase; + return ex; + } + else if ((mLeft->mDecValue->mType == DT_CONST_INTEGER || mLeft->mDecValue->mType == DT_CONST_FLOAT) && (mRight->mDecValue->mType == DT_CONST_INTEGER || mRight->mDecValue->mType == DT_CONST_FLOAT)) + { + double dval; + double dleft = mLeft->mDecValue->mType == DT_CONST_INTEGER ? mLeft->mDecValue->mInteger : mLeft->mDecValue->mNumber; + double dright = mRight->mDecValue->mType == DT_CONST_INTEGER ? mRight->mDecValue->mInteger : mRight->mDecValue->mNumber; + + switch (mToken) + { + case TK_ADD: + dval = dleft + dright; + break; + case TK_SUB: + dval = dleft - dright; + break; + case TK_MUL: + dval = dleft * dright; + break; + case TK_DIV: + dval = dleft / dright; + break; + default: + dval = 0; + } + + Expression* ex = new Expression(mLocation, EX_CONSTANT); + Declaration* dec = new Declaration(mLocation, DT_CONST_FLOAT); + dec->mBase = TheFloatTypeDeclaration; + dec->mNumber = dval; + ex->mDecValue = dec; + ex->mDecType = dec->mBase; + return ex; + } + else if (mLeft->mDecValue->mType == DT_CONST_ADDRESS && mRight->mDecValue->mType == DT_CONST_INTEGER) + { + int64 ival = 0, ileft = mLeft->mDecValue->mInteger, iright = mRight->mDecValue->mInteger; + + switch (mToken) + { + case TK_ADD: + ival = ileft + iright; + break; + case TK_SUB: + ival = ileft - iright; + break; + default: + return this; + } + + Expression* ex = new Expression(mLocation, EX_CONSTANT); + Declaration* dec = new Declaration(mLocation, DT_CONST_ADDRESS); + dec->mBase = mLeft->mDecType; + dec->mInteger = ival; + ex->mDecValue = dec; + ex->mDecType = dec->mBase; + return ex; + } + else if (mLeft->mDecValue->mType == DT_CONST_ADDRESS && mRight->mDecValue->mType == DT_CONST_ADDRESS && mToken == TK_SUB) + { + if (mLeft->mDecType->mType == DT_TYPE_POINTER && mRight->mDecType->mType == DT_TYPE_POINTER && + mLeft->mDecType->mBase->IsConstSame(mRight->mDecType->mBase)) + { + Expression* ex = new Expression(mLocation, EX_CONSTANT); + Declaration* dec = new Declaration(mLocation, DT_CONST_INTEGER); + dec->mBase = TheSignedIntTypeDeclaration; + dec->mInteger = (mLeft->mDecValue->mInteger - mRight->mDecValue->mInteger) / mLeft->mDecType->mBase->mSize; + ex->mDecValue = dec; + ex->mDecType = dec->mBase; + return ex; + } + } +#if 0 + else if (mLeft->mDecValue->mType == DT_CONST_POINTER && mRight->mDecValue->mType == DT_CONST_INTEGER && (mToken == TK_ADD || mToken == TK_SUB)) + { + int64 ileft = 0; + + Declaration* pdec = mLeft->mDecValue->mValue->mDecValue; + if (pdec->mType == DT_VARIABLE_REF) + { + ileft = pdec->mOffset; + pdec = pdec->mBase; + } + + switch (mToken) + { + case TK_ADD: + ileft += mRight->mDecValue->mInteger * mLeft->mDecType->mBase->mSize; + break; + case TK_SUB: + ileft -= mRight->mDecValue->mInteger * mLeft->mDecType->mBase->mSize; + break; + } + + Expression* vex = new Expression(mLocation, EX_VARIABLE); + Declaration* vdec = new Declaration(mLocation, DT_VARIABLE_REF); + vdec->mFlags = pdec->mFlags; + vdec->mBase = pdec; + vdec->mSize = pdec->mBase->mSize; + vdec->mOffset = ileft; + vex->mDecValue = vdec; + vex->mDecType = mLeft->mDecType->mBase; + + Expression* ex = new Expression(mLocation, EX_CONSTANT); + Declaration* dec = new Declaration(mLocation, DT_CONST_POINTER); + dec->mBase = mLeft->mDecValue->mBase; + dec->mValue = vex; + ex->mDecValue = dec; + ex->mDecType = dec->mBase; + return ex; + } +#endif + } + else if (mType == EX_RELATIONAL && mLeft->mType == EX_CONSTANT && mRight->mType == EX_CONSTANT) + { + if (mLeft->mDecValue->mType == DT_CONST_INTEGER && mRight->mDecValue->mType == DT_CONST_INTEGER) + { + int64 ival = 0, ileft = mLeft->mDecValue->mInteger, iright = mRight->mDecValue->mInteger; + + bool check = false; + switch (mToken) + { + case TK_EQUAL: + check = ileft == iright; + break; + case TK_NOT_EQUAL: + check = ileft != iright; + break; + case TK_GREATER_THAN: + check = ileft > iright; + break; + case TK_GREATER_EQUAL: + check = ileft >= iright; + break; + case TK_LESS_THAN: + check = ileft < iright; + break; + case TK_LESS_EQUAL: + check = ileft <= iright; + break; + } + + Declaration * dec = new Declaration(mLocation, DT_CONST_INTEGER); + dec->mBase = TheBoolTypeDeclaration; + dec->mInteger = check ? 1 : 0; + Expression * exp = new Expression(mLocation, EX_CONSTANT); + exp->mDecValue = dec; + exp->mDecType = dec->mBase; + return exp; + + } + } + else if (mType == EX_LOGICAL_AND && mLeft->mType == EX_CONSTANT) + { + if (mLeft->mDecValue->mType == DT_CONST_INTEGER) + { + if (mLeft->mDecValue->mInteger == 0) + return mLeft; + else + return mRight->ConstantFold(errors, dataSection); + } + } + else if (mType == EX_LOGICAL_OR && mLeft->mType == EX_CONSTANT) + { + if (mLeft->mDecValue->mType == DT_CONST_INTEGER) + { + if (mLeft->mDecValue->mInteger != 0) + return mLeft; + else + return mRight->ConstantFold(errors, dataSection); + } + } + else if (mType == EX_CONDITIONAL && mLeft->mType == EX_CONSTANT) + { + if (mLeft->mDecValue->mType == DT_CONST_INTEGER) + { + if (mLeft->mDecValue->mInteger != 0) + return mRight->mLeft->ConstantFold(errors, dataSection); + else + return mRight->mRight->ConstantFold(errors, dataSection); + } + } + else if (mType == EX_BINARY && mToken == TK_ADD && mLeft->mType == EX_VARIABLE && mLeft->mDecValue->mType == DT_VARIABLE && (mLeft->mDecValue->mFlags & DTF_GLOBAL) && mLeft->mDecType->mType == DT_TYPE_ARRAY && mRight->mType == EX_CONSTANT && mRight->mDecValue->mType == DT_CONST_INTEGER) + { + Expression* ex = new Expression(mLocation, EX_VARIABLE); + Declaration* dec = new Declaration(mLocation, DT_VARIABLE_REF); + dec->mFlags = mLeft->mDecValue->mFlags; + dec->mBase = mLeft->mDecValue; + dec->mSize = mLeft->mDecType->mBase->mSize - int(mRight->mDecValue->mInteger) * dec->mSize; + dec->mOffset = int(mRight->mDecValue->mInteger) * dec->mSize; + ex->mDecValue = dec; + ex->mDecType = mLeft->mDecType; + return ex; + } + else if (mType == EX_BINARY && mToken == TK_ADD && mLeft->mType == EX_VARIABLE && mLeft->mDecValue->mType == DT_VARIABLE_REF && (mLeft->mDecValue->mFlags & DTF_GLOBAL) && mLeft->mDecType->mType == DT_TYPE_ARRAY && mRight->mType == EX_CONSTANT && mRight->mDecValue->mType == DT_CONST_INTEGER) + { + Expression* ex = new Expression(mLocation, EX_VARIABLE); + Declaration* dec = new Declaration(mLocation, DT_VARIABLE_REF); + dec->mFlags = mLeft->mDecValue->mFlags; + dec->mBase = mLeft->mDecValue->mBase; + dec->mSize = mLeft->mDecType->mBase->mSize - int(mRight->mDecValue->mInteger) * dec->mSize; + dec->mOffset = mLeft->mDecValue->mOffset + int(mRight->mDecValue->mInteger) * dec->mSize; + ex->mDecValue = dec; + ex->mDecType = mLeft->mDecType; + return ex; + } + else if (mType == EX_BINARY && mToken == TK_ADD && mLeft->mType == EX_VARIABLE && mLeft->mDecValue->mType == DT_VARIABLE && (mLeft->mDecValue->mFlags & DTF_CONST) && + mLeft->mDecValue && mRight->mDecValue && mLeft->mDecValue->mValue && mLeft->mDecValue->mValue->mType == EX_CONSTANT && + mLeft->mDecType->mType == DT_TYPE_POINTER && mRight->mType == EX_CONSTANT && mRight->mDecValue->mType == DT_CONST_INTEGER) + { + mLeft = mLeft->mDecValue->mValue; + return this->ConstantFold(errors, dataSection); + } + else if (mType == EX_BINARY && mToken == TK_ADD && mLeft->mType == EX_CONSTANT && mLeft->mDecValue->mType == DT_CONST_ADDRESS && mLeft->mDecValue->mInteger == 0) + { + Expression* ex = new Expression(mLocation, EX_TYPECAST); + ex->mLeft = mRight; + ex->mDecType = mDecType; + return ex->ConstantFold(errors, dataSection); + } + else if (mType == EX_QUALIFY && mLeft->mType == EX_VARIABLE && mLeft->mDecValue->mType == DT_VARIABLE && (mLeft->mDecValue->mFlags & DTF_GLOBAL) && mLeft->mDecType->mType == DT_TYPE_STRUCT) + { + Expression* ex = new Expression(mLocation, EX_VARIABLE); + Declaration* dec = new Declaration(mLocation, DT_VARIABLE_REF); + dec->mFlags = mLeft->mDecValue->mFlags; + dec->mBase = mLeft->mDecValue; + dec->mOffset = mDecValue->mOffset; + dec->mSize = mDecValue->mSize; + dec->mBits = mDecValue->mBits; + dec->mShift = mDecValue->mShift; + ex->mDecValue = dec; + ex->mDecType = mDecType; + return ex; + } + else if (mType == EX_QUALIFY && mLeft->mType == EX_VARIABLE && mLeft->mDecValue->mType == DT_VARIABLE_REF && (mLeft->mDecValue->mFlags & DTF_GLOBAL) && mLeft->mDecType->mType == DT_TYPE_STRUCT) + { + Expression* ex = new Expression(mLocation, EX_VARIABLE); + Declaration* dec = new Declaration(mLocation, DT_VARIABLE_REF); + dec->mFlags = mLeft->mDecValue->mFlags; + dec->mBase = mLeft->mDecValue->mBase; + dec->mOffset = mLeft->mDecValue->mOffset + mDecValue->mOffset; + dec->mSize = mDecValue->mSize; + dec->mBits = mDecValue->mBits; + dec->mShift = mDecValue->mShift; + ex->mDecValue = dec; + ex->mDecType = mDecType; + return ex; + } + else if (mType == EX_INDEX && mLeft->mType == EX_VARIABLE && mLeft->mDecValue->mType == DT_VARIABLE && (mLeft->mDecValue->mFlags & DTF_GLOBAL) && + mLeft->mDecType->mType == DT_TYPE_ARRAY && mLeft->mDecType->mStride == 0 && + mRight->mType == EX_CONSTANT && mRight->mDecValue->mType == DT_CONST_INTEGER) + { + int offset = int(mDecType->mSize * mRight->mDecValue->mInteger); + + Expression* ex = new Expression(mLocation, EX_VARIABLE); + Declaration* dec = new Declaration(mLocation, DT_VARIABLE_REF); + dec->mFlags = mLeft->mDecValue->mFlags; + dec->mBase = mLeft->mDecValue; + dec->mOffset = offset; + dec->mSize = mDecType->mSize; + ex->mDecValue = dec; + ex->mDecType = mDecType; + return ex; + } + else if (mType == EX_INDEX && mLeft->mType == EX_VARIABLE && mLeft->mDecValue->mType == DT_VARIABLE_REF && (mLeft->mDecValue->mFlags & DTF_GLOBAL) && + mLeft->mDecType->mType == DT_TYPE_ARRAY && mLeft->mDecType->mStride == 0 && + mRight->mType == EX_CONSTANT && mRight->mDecValue->mType == DT_CONST_INTEGER) + { + int offset = mLeft->mDecValue->mOffset + int(mDecType->mSize * mRight->mDecValue->mInteger); + + Expression* ex = new Expression(mLocation, EX_VARIABLE); + Declaration* dec = new Declaration(mLocation, DT_VARIABLE_REF); + dec->mFlags = mLeft->mDecValue->mFlags; + dec->mBase = mLeft->mDecValue->mBase; + dec->mOffset = offset; + dec->mSize = mDecType->mSize; + ex->mDecValue = dec; + ex->mDecType = mDecType; + return ex; + } + else if (mType == EX_VARIABLE && mDecType->IsSimpleType() && (mDecValue->mFlags & DTF_CONST) && mDecValue->mValue && mDecValue->mValue->mType == EX_INITIALIZATION && mDecValue->mValue->mRight->mType == EX_CONSTANT) + { + return mDecValue->mValue->mRight; + } + else if (mType == EX_CALL && mLeft->mType == EX_CONSTANT && (mLeft->mDecValue->mFlags & DTF_INTRINSIC) && mRight && mRight->mType == EX_CONSTANT) + { + Declaration* decf = mLeft->mDecValue, * decp = mRight->mDecValue; + const Ident* iname = decf->mQualIdent; + + if (decp->mType == DT_CONST_FLOAT || decp->mType == DT_CONST_INTEGER) + { + double d = decp->mType == DT_CONST_FLOAT ? decp->mNumber : decp->mInteger; + double e = 0.0; + + bool check = false; + + if (!strcmp(iname->mString, "fabs")) + d = fabs(d); + else if (!strcmp(iname->mString, "floor")) + d = floor(d); + else if (!strcmp(iname->mString, "ceil")) + d = ceil(d); + else if (!strcmp(iname->mString, "sin")) + d = sin(d); + else if (!strcmp(iname->mString, "cos")) + d = cos(d); + else if (!strcmp(iname->mString, "tan")) + d = tan(d); + else if (!strcmp(iname->mString, "log")) + d = log(d); + else if (!strcmp(iname->mString, "exp")) + d = exp(d); + else if (!strcmp(iname->mString, "sqrt")) + d = sqrt(d); + else if (!strcmp(iname->mString, "atan")) + d = atan(d); + else + return this; + + Expression* ex = new Expression(mLocation, EX_CONSTANT); + Declaration* dec = new Declaration(mLocation, DT_CONST_FLOAT); + dec->mBase = TheFloatTypeDeclaration; + dec->mNumber = d; + ex->mDecValue = dec; + ex->mDecType = dec->mBase; + return ex; + } + } + else if (mType == EX_CALL && mLeft->mType == EX_CONSTANT && (mLeft->mDecValue->mFlags & DTF_INTRINSIC) && mRight && + mRight->mType == EX_LIST && mRight->mLeft->mType == EX_CONSTANT && mRight->mRight->mType == EX_CONSTANT) + { + Declaration* decf = mLeft->mDecValue, * decp1 = mRight->mLeft->mDecValue, * decp2 = mRight->mRight->mDecValue; + const Ident* iname = decf->mQualIdent; + + if ((decp1->mType == DT_CONST_FLOAT || decp1->mType == DT_CONST_INTEGER) && + (decp2->mType == DT_CONST_FLOAT || decp2->mType == DT_CONST_INTEGER)) + { + double d1 = decp1->mType == DT_CONST_FLOAT ? decp1->mNumber : decp1->mInteger; + double d2 = decp2->mType == DT_CONST_FLOAT ? decp2->mNumber : decp2->mInteger; + + bool check = false; + + if (!strcmp(iname->mString, "pow")) + d1 = pow(d1, d2); + else if (!strcmp(iname->mString, "atan2")) + d1 = atan2(d1, d2); + else + return this; + + Expression* ex = new Expression(mLocation, EX_CONSTANT); + Declaration* dec = new Declaration(mLocation, DT_CONST_FLOAT); + dec->mBase = TheFloatTypeDeclaration; + dec->mNumber = d1; + ex->mDecValue = dec; + ex->mDecType = dec->mBase; + return ex; + } + } + else if (mType == EX_CALL && mLeft->mType == EX_CONSTANT && (mLeft->mDecValue->mFlags & DTF_CONSTEXPR) && dataSection) + { + ConstexprInterpreter cinter(mLocation, errors, dataSection); + return cinter.EvalCall(this); + } + else if (mType == EX_CONSTRUCT && mLeft->mType == EX_LIST && !mLeft->mRight && mLeft->mLeft->mType == EX_CALL && + mLeft->mLeft->mLeft->mType == EX_CONSTANT && (mLeft->mLeft->mLeft->mDecValue->mFlags & DTF_CONSTEXPR) && + (mRight->mDecValue->mFlags & DTF_TEMPORARY) && + dataSection) + { + ConstexprInterpreter cinter(mLocation, errors, dataSection); + return cinter.EvalTempConstructor(this); + } + + return this; +} + +Declaration::Declaration(const Location& loc, DecType type) + : mLocation(loc), mEndLocation(loc), mType(type), mScope(nullptr), mData(nullptr), mIdent(nullptr), mQualIdent(nullptr), mMangleIdent(nullptr), + mSize(0), mOffset(0), mFlags(0), mComplexity(0), mLocalSize(0), mNumVars(0), + mBase(nullptr), mParams(nullptr), mParamPack(nullptr), mValue(nullptr), mReturn(nullptr), mNext(nullptr), mPrev(nullptr), + mConst(nullptr), mMutable(nullptr), mVolatile(nullptr), mClass(nullptr), + mDefaultConstructor(nullptr), mDestructor(nullptr), mCopyConstructor(nullptr), mCopyAssignment(nullptr), mMoveConstructor(nullptr), mMoveAssignment(nullptr), + mVectorConstructor(nullptr), mVectorDestructor(nullptr), mVectorCopyConstructor(nullptr), mVectorCopyAssignment(nullptr), + mVectorMoveConstructor(nullptr), mVectorMoveAssignment(nullptr), + mVTable(nullptr), mTemplate(nullptr), mForwardParam(nullptr), mForwardCall(nullptr), + mVarIndex(-1), mLinkerObject(nullptr), mAlignment(1), mCallers(nullptr), mCalled(nullptr), mFriends(nullptr), mMayCall(nullptr), + mInteger(0), mNumber(0), mMinValue(-0x80000000LL), mMaxValue(0x7fffffffLL), + mFastCallBase(0), mFastCallSize(0), + mFastCallBase2(0), mFastCallSize2(0), mVariadicSize(0), + mStride(0), mStripe(1), + mCompilerOptions(0), mUseCount(0), mTokens(nullptr), mParser(nullptr), + mShift(0), mBits(0), mOptFlags(0), mInlayRegion(nullptr) +{ + static uint32 gUID = 0; + mUID = gUID++; +} + +Declaration::~Declaration(void) +{ + delete mScope; + delete[] mData; +} + +int Declaration::Stride(void) const +{ + if (mStride > 0) + return mStride; + else if (mBase && mBase->mType != DT_TYPE_VOID) + return mBase->mSize; + else + return 1; +} + +int Declaration::Alignment(void) const +{ + if (mType == DT_TYPE_ARRAY) + return mBase->Alignment(); + else if (mType == DT_TYPE_STRUCT) + { + int alignment = 0; + Declaration* dec = mParams; + while (dec) + { + alignment |= dec->mBase->Alignment() - 1; + dec = dec->mNext; + } + return alignment + 1; + } + else if (mType == DT_TYPE_INTEGER || mType == DT_TYPE_FLOAT || mType == DT_TYPE_POINTER) + return mSize; + else + return 1; +} + +Declaration* Declaration::BuildConstPointer(const Location& loc) +{ + Declaration* pdec = new Declaration(loc, DT_TYPE_POINTER); + pdec->mBase = this; + pdec->mFlags = DTF_DEFINED | DTF_CONST; + pdec->mSize = 2; + return pdec; +} + +Declaration* Declaration::BuildConstReference(const Location& loc, DecType type) +{ + Declaration* pdec = new Declaration(loc, type); + pdec->mBase = this; + pdec->mFlags = DTF_DEFINED | DTF_CONST; + pdec->mSize = 2; + return pdec; +} + +Declaration* Declaration::BuildArrayPointer(void) +{ + if (mType == DT_TYPE_ARRAY) + { + Declaration* pdec = new Declaration(mLocation, DT_TYPE_POINTER); + pdec->mBase = mBase; + pdec->mFlags = DTF_DEFINED; + pdec->mSize = 2; + pdec->mStride = mStride; + return pdec; + } + else + return this; +} + +Declaration* Declaration::BuildAddressOfPointer(void) +{ + /*if (mType == DT_TYPE_ARRAY) + return BuildArrayPointer(); + else*/ if (mType == DT_TYPE_REFERENCE) + return mBase->BuildAddressOfPointer(); + else + return BuildPointer(mLocation); +} + +bool Declaration::IsNullConst(void) const +{ + if (mType == DT_CONST_INTEGER || mType == DT_CONST_ADDRESS) + return mInteger == 0; + else if (mType == DT_CONST_FLOAT) + return mNumber == 0; + else + return false; +} + +int64 Declaration::MinInteger(void) const +{ + if (mFlags & DTF_SIGNED) + return -int64(1ULL << (8 * mSize - 1)); + else + return 0; +} + +int64 Declaration::MaxInteger(void) const +{ + if (mFlags & DTF_SIGNED) + return (1ULL << (8 * mSize - 1)) - 1; + else + return (1ULL << (8 * mSize)) - 1; +} + +int64 Declaration::CastInteger(int64 v) const +{ + v &= (1ULL << (8 * mSize)) - 1; + if (mFlags & DTF_SIGNED) + { + if (v & (1ULL << (8 * mSize - 1))) + v -= 1ULL << (8 * mSize); + } + return v; +} + +Declaration* Declaration::ConstCast(Declaration* ntype) +{ + if (ntype == mBase) + return this; + else if (ntype->mType == DT_TYPE_POINTER) + { + if (mBase->mType == DT_TYPE_POINTER) + { + if (mBase->mBase->IsSame(ntype->mBase)) + return this; + + Declaration* pdec = this->Clone(); + pdec->mBase = ntype; + return pdec; + } + else if (mType == DT_TYPE_INTEGER) + { + Declaration* pdec = this->Clone(); + pdec->mType = DT_CONST_ADDRESS; + pdec->mBase = ntype; + pdec->mSize = 2; + return pdec; + } + else if (mType == DT_VARIABLE && mBase->mType == DT_TYPE_ARRAY) + { + Expression* ex = new Expression(mLocation, EX_VARIABLE); + ex->mDecType = mBase; + ex->mDecValue = this; + + Declaration* pdec = this->Clone(); + pdec->mType = DT_CONST_POINTER; + pdec->mValue = ex; + pdec->mBase = ntype; + pdec->mSize = 2; + + return pdec; + } + else if (mType == DT_VARIABLE_REF) + { + Declaration* vtype = mBase->mBase; + while (vtype && vtype->mType == DT_TYPE_STRUCT) + { + Declaration* dec = vtype->mParams; + while (dec && dec->mOffset != mOffset) + dec = dec->mNext; + if (dec) + vtype = dec->mBase; + else + vtype = nullptr; + } + + if (vtype && vtype->mType == DT_TYPE_ARRAY) + { + Expression* ex = new Expression(mLocation, EX_VARIABLE); + ex->mDecType = mBase->mBase; + ex->mDecValue = this; + + Declaration* pdec = this->Clone(); + pdec->mType = DT_CONST_POINTER; + pdec->mValue = ex; + pdec->mBase = ntype; + pdec->mSize = 2; + pdec->mOffset = 0; + + return pdec; + } + else + return this; + } + else + return this; + } + else if (ntype->mType == DT_TYPE_INTEGER || ntype->mType == DT_TYPE_BOOL || ntype->mType == DT_TYPE_ENUM) + { + if (mType == DT_CONST_FLOAT) + { + Declaration* pdec = this->Clone(); + pdec->mType = DT_CONST_INTEGER; + pdec->mInteger = int64(mNumber); + pdec->mBase = ntype; + pdec->mSize = ntype->mSize; + return pdec; + } + else + { + Declaration* pdec = this->Clone(); + pdec->mBase = ntype; + pdec->mSize = ntype->mSize; + + pdec->mInteger &= (1ULL << (8 * mBase->mSize)) - 1; + if (mBase->mFlags & DTF_SIGNED) + { + if (pdec->mInteger >= 1LL << (8 * mBase->mSize - 1)) + pdec->mInteger -= 1LL << (8 * mBase->mSize); + } + pdec->mInteger &= (1ULL << (8 * pdec->mBase->mSize)) - 1; + if (pdec->mBase->mFlags & DTF_SIGNED) + { + if (pdec->mInteger >= 1LL << (8 * pdec->mBase->mSize - 1)) + pdec->mInteger -= 1LL << (8 * pdec->mBase->mSize); + } + return pdec; + } + } + else if (ntype->mType == DT_TYPE_FLOAT) + { + if (mType == DT_CONST_FLOAT) + { + Declaration* pdec = this->Clone(); + pdec->mBase = ntype; + return pdec; + } + else + { + Declaration* pdec = this->Clone(); + pdec->mType = DT_CONST_FLOAT; + pdec->mNumber = float(mInteger); + pdec->mBase = ntype; + pdec->mSize = ntype->mSize; + return pdec; + } + } + else + return this; +} + +Declaration* Declaration::BuildPointer(const Location& loc) +{ + Declaration* pdec = new Declaration(loc, DT_TYPE_POINTER); + pdec->mBase = this; + pdec->mFlags = DTF_DEFINED; + pdec->mSize = 2; + return pdec; +} + +Declaration* Declaration::BuildReference(const Location& loc, DecType type) +{ + Declaration* pdec = new Declaration(loc, type); + pdec->mBase = this; + pdec->mFlags = DTF_DEFINED; + pdec->mSize = 2; + return pdec; +} + +Declaration* Declaration::BuildRValueRef(const Location& loc) +{ + Declaration* pdec = new Declaration(loc, DT_TYPE_RVALUEREF); + pdec->mBase = this; + pdec->mFlags = DTF_DEFINED; + pdec->mSize = 2; + return pdec; +} + +Declaration* Declaration::NonRefBase(void) +{ + if (IsReference()) + return mBase; + else + return this; +} + +bool Declaration::IsAuto(void) const +{ + return (mType == DT_TYPE_AUTO || IsReference() && mBase->mType == DT_TYPE_AUTO); +} + +bool Declaration::HasConstMember(void) const +{ + if (mFlags & DTF_CONST) + return true; + + if (mType == DT_TYPE_STRUCT) + { + Declaration* dec = mParams; + while (dec) + { + if (dec->mBase->HasConstMember()) + return true; + dec = dec->mNext; + } + } + + return false; +} + + +Declaration* Declaration::DeduceAuto(Declaration * dec) +{ + if (mType == DT_TYPE_AUTO || IsReference() && mBase->mType == DT_TYPE_AUTO) + { + dec = dec->NonRefBase(); + + if (dec->mType == DT_TYPE_ARRAY) + dec = dec->mBase->BuildPointer(mLocation); + + if ((IsReference() ? mBase->mFlags : mFlags) & DTF_CONST) + dec = dec->ToConstType(); + else + dec = dec->ToMutableType(); + + if (IsReference()) + dec = dec->BuildReference(mLocation, mType); + + return dec; + } + else + return this; +} + +Declaration* Declaration::DeduceAutoInit(Declaration* dec) +{ + if (mType == DT_TYPE_AUTO || IsReference() && mBase->mType == DT_TYPE_AUTO) + { + dec = dec->NonRefBase(); + + if ((IsReference() ? mBase->mFlags : mFlags) & DTF_CONST) + dec = dec->ToConstType(); + else + dec = dec->ToMutableType(); + + if (IsReference()) + dec = dec->BuildReference(mLocation, mType); + + return dec; + } + else + return this; +} + + +Declaration* Declaration::BuildConstRValueRef(const Location& loc) +{ + Declaration* pdec = new Declaration(loc, DT_TYPE_RVALUEREF); + pdec->mBase = this; + pdec->mFlags = DTF_DEFINED | DTF_CONST; + pdec->mSize = 2; + return pdec; +} + +DecType Declaration::ValueType(void) const +{ + if (IsReference()) + return mBase->mType; + else + return mType; +} + +Declaration* Declaration::Last(void) +{ + mPrev = nullptr; + Declaration* p = this; + while (p->mNext) + { + p->mNext->mPrev = p; + p = p->mNext; + } + return p; +} + +const Ident* Declaration::FullIdent(void) +{ + if (mType == DT_CONST_FUNCTION) + { + const Ident* tident = MangleIdent()->Mangle("("); + Declaration* dec = mBase->mParams; + while (dec) + { + const Ident* mident = dec->mBase->MangleIdent(); + if (mident) + tident = tident->Mangle(mident->mString); + dec = dec->mNext; + if (dec) + tident = tident->Mangle(","); + } + tident = tident->Mangle(")->"); + tident = tident->Mangle(mBase->mBase ? mBase->mBase->MangleIdent()->mString : "null"); + return tident; + } + else + return MangleIdent(); + +} + +const Ident* Declaration::MangleIdent(void) +{ + if (!mMangleIdent) + { + if (mType == DT_CONST_INTEGER) + { + char buffer[20]; + sprintf_s(buffer, "%d", (int)mInteger); + mMangleIdent = Ident::Unique(buffer); + } + else if (mType == DT_CONST_FUNCTION) + { + mMangleIdent = mQualIdent; + } + else if (mType == DT_TYPE_FUNCTION) + { + mMangleIdent = mBase->MangleIdent(); + mMangleIdent = mMangleIdent->Mangle("(*)("); + Declaration* dec = mParams; + while (dec) + { + mMangleIdent = mMangleIdent->Mangle(dec->mBase ? dec->mBase->MangleIdent()->mString : "null"); + dec = dec->mNext; + if (dec) + mMangleIdent = mMangleIdent->Mangle(","); + } + mMangleIdent = mMangleIdent->Mangle(")"); + } + else if (mType == DT_TYPE_INTEGER) + { + char buffer[20]; + sprintf_s(buffer, "%c%d", (mFlags & DTF_SIGNED) ? 'i' : 'u', mSize * 8); + mMangleIdent = Ident::Unique(buffer); + } + else if (mType == DT_TYPE_FLOAT) + { + mMangleIdent = Ident::Unique("float"); + } + else if (mType == DT_TYPE_BOOL) + { + mMangleIdent = Ident::Unique("bool"); + } + else if (mType == DT_TYPE_REFERENCE) + { + mMangleIdent = mBase->MangleIdent()->Mangle("&"); + } + else if (mType == DT_TYPE_RVALUEREF) + { + mMangleIdent = mBase->MangleIdent()->Mangle("&&"); + } + else if (mType == DT_TYPE_POINTER) + { + mMangleIdent = mBase->MangleIdent()->Mangle("*"); + } + else if (mType == DT_TYPE_ARRAY) + { + mMangleIdent = mBase->MangleIdent()->Mangle("[]"); + } + else if (mType == DT_TYPE_STRUCT) + { + if (mQualIdent) + mMangleIdent = mQualIdent->PreMangle("struct "); + else + mMangleIdent = Ident::Unique("struct S", mUID); + } + else if (mType == DT_TYPE_UNION) + { + if (mQualIdent) + mMangleIdent = mQualIdent->PreMangle("union "); + else + mMangleIdent = Ident::Unique("union U", mUID); + } + else if (mType == DT_TYPE_ENUM) + { + if (mQualIdent) + mMangleIdent = mQualIdent->PreMangle("enum "); + else + mMangleIdent = Ident::Unique("enum E", mUID); + } + else if (mType == DT_TYPE_VOID) + { + mMangleIdent = Ident::Unique("void"); + } + else if (mType == DT_TEMPLATE) + { + mMangleIdent = Ident::Unique("<"); + + Declaration* dec = mParams; + while (dec) + { + const Ident* id; + if (dec->mBase) + id = dec->mBase->MangleIdent(); + else + id = dec->MangleIdent(); + if (id) + mMangleIdent = mMangleIdent->Mangle(id->mString); + + dec = dec->mNext; + if (dec) + mMangleIdent = mMangleIdent->Mangle(","); + } + mMangleIdent = mMangleIdent->Mangle(">"); + } + else if (mType == DT_PACK_TYPE) + { + Declaration* dec = mParams; + if (dec) + { + while (dec) + { + const Ident* id = dec->MangleIdent(); + + if (id) + { + if (mMangleIdent) + mMangleIdent = mMangleIdent->Mangle(id->mString); + else + mMangleIdent = id; + } + + dec = dec->mNext; + if (mMangleIdent && dec) + mMangleIdent = mMangleIdent->Mangle(","); + } + } + else + mMangleIdent = Ident::Unique("void"); + } + else if (mQualIdent) + mMangleIdent = mQualIdent; + else + mMangleIdent = mIdent; + + if (mTemplate) + { + + } + + if (mMangleIdent) + { + if (mFlags & DTF_CONST) + mMangleIdent = mMangleIdent->PreMangle("const "); + if (mFlags & DTF_VOLATILE) + mMangleIdent = mMangleIdent->PreMangle("volatile "); + } + } + + return mMangleIdent; +} + +Declaration* Declaration::ExpandTemplate(DeclarationScope* scope) +{ + if (mType == DT_CONST_FUNCTION) + { + Declaration* dec = this->Clone(); + dec->mBase = dec->mBase->ExpandTemplate(scope); + return dec; + } + else if (mType == DT_TYPE_FUNCTION) + { + Declaration* dec = this->Clone(); + dec->mParams = mParams ? mParams->ExpandTemplate(scope) : nullptr; + dec->mBase = mBase->ExpandTemplate(scope); + Declaration* pdec = dec->mParams; + if (pdec) + { + int vi = pdec->mVarIndex; + while (pdec) + { + pdec->mVarIndex = vi; + vi += pdec->mSize; + pdec = pdec->mNext; + } + } + return dec; + } + else if (mType == DT_ARGUMENT) + { + Declaration* ndec = mNext ? mNext->ExpandTemplate(scope) : nullptr; + Declaration* bdec = mBase->ExpandTemplate(scope); + if (ndec != mNext || bdec != mBase) + { + Declaration* dec = this->Clone(); + dec->mBase = bdec; + dec->mSize = bdec->mSize; + dec->mNext = ndec; + return dec; + } + } + else if (mType == DT_TYPE_REFERENCE || mType == DT_TYPE_POINTER || mType == DT_TYPE_RVALUEREF) + { + Declaration* bdec = mBase->ExpandTemplate(scope); + if (bdec != mBase) + { + Declaration* dec = this->Clone(); + dec->mBase = bdec; + return dec; + } + } + else if (mType == DT_TYPE_TEMPLATE) + return scope->Lookup(mIdent); + + return this; +} + +bool Declaration::ResolveTemplateParameterList(Expression* pexp, Declaration* pdec, bool preliminary) +{ + Declaration* phead = nullptr, * ptail = nullptr; + int pcnt = 0; + + while (pexp) + { + Expression* ex = pexp; + if (pexp->mType == EX_LIST) + { + ex = pexp->mLeft; + pexp = pexp->mRight; + } + else + pexp = nullptr; + + if (pdec) + { + if (pdec->mType == DT_PACK_ARGUMENT) + { + Declaration* tpdec = ex->mDecType; + if (tpdec->IsReference()) + tpdec = tpdec->mBase; + + if (tpdec->mType == DT_TYPE_ARRAY) + tpdec = tpdec->mBase->BuildPointer(tpdec->mLocation); + else + tpdec = tpdec->Clone(); + + if (ptail) + ptail->mNext = tpdec; + else + phead = tpdec; + ptail = tpdec; + } + else + { + if (!ResolveTemplate(ex->mDecType, pdec->mBase, false, preliminary)) + return false; + + pdec = pdec->mNext; + } + } + else + return false; + } + + while (pdec && pdec->mType != DT_PACK_ARGUMENT && pdec->mValue) + { + if (!ResolveTemplate(pdec->mValue->mDecType, pdec->mBase, false, preliminary)) + return false; + + pdec = pdec->mNext; + } + + if (pdec) + { + if (pdec->mType == DT_PACK_ARGUMENT) + { + if (preliminary) + return true; + + Declaration* tpdec = new Declaration(pdec->mLocation, DT_PACK_TYPE); + if (pdec->mBase->mType == DT_TYPE_REFERENCE) + tpdec->mIdent = pdec->mBase->mBase->mIdent; + else + tpdec->mIdent = pdec->mBase->mIdent; + tpdec->mParams = phead; + tpdec->mFlags |= DTF_DEFINED; + mScope->Insert(tpdec->mIdent, tpdec); + } + else + return false; + } + + return true; +} + +bool Declaration::ResolveTemplate(Expression* pexp, Declaration* tdec) +{ + Declaration* pdec = tdec->mBase->mParams; + + // Insert partially resolved templates + Declaration* ptdec = tdec->mTemplate->mParams; + while (ptdec) + { + if (ptdec->mBase) + mScope->Insert(ptdec->mIdent, ptdec->mBase); + ptdec = ptdec->mNext; + } + + if (!ResolveTemplateParameterList(pexp, pdec, true) || !ResolveTemplateParameterList(pexp, pdec, false)) + return false; + + Declaration* ppdec = nullptr; + ptdec = tdec->mTemplate->mParams; + while (ptdec) + { + Declaration* pdec = mScope->Lookup(ptdec->mIdent); + if (!pdec) + return false; + + Declaration * epdec = ptdec->Clone(); + epdec->mBase = pdec; + epdec->mFlags |= DTF_DEFINED; + + if (ppdec) + ppdec->mNext = epdec; + else + mParams = epdec; + ppdec = epdec; + ptdec = ptdec->mNext; + } + + mScope->Clear(); + + return true; +} + +bool Declaration::CanResolveTemplate(Expression* pexp, Declaration* tdec) +{ + Declaration* pdec = tdec->mBase->mParams; + + while (pexp) + { + Expression* ex = pexp; + if (pexp->mType == EX_LIST) + { + ex = pexp->mLeft; + pexp = pexp->mRight; + } + else + pexp = nullptr; + + if (pdec) + { + if (!ResolveTemplate(ex->mDecType, pdec->mBase, false, false)) + return false; + + if (pdec->mType != DT_PACK_ARGUMENT) + pdec = pdec->mNext; + } + else + return false; + } + + if (pdec) + return pdec->mType == DT_PACK_ARGUMENT; + else + return true; +} + +bool Declaration::ResolveTemplate(Declaration* fdec, Declaration* tdec, bool same, bool preliminary) +{ + if (tdec->IsSame(fdec)) + return true; + else if (fdec->IsReference()) + return ResolveTemplate(fdec->mBase, tdec, same, preliminary); + else if (tdec->mType == DT_TYPE_FUNCTION) + { + if (fdec->mType == DT_TYPE_FUNCTION) + { + if (fdec->mBase) + { + if (!tdec->mBase || !ResolveTemplate(fdec->mBase, tdec->mBase, true, preliminary)) + return false; + } + else if (tdec->mBase) + return false; + + Declaration* fpdec = fdec->mParams; + Declaration* tpdec = tdec->mParams; + while (tpdec) + { + if (tpdec->mBase->mType == DT_PACK_TEMPLATE) + { + if (fpdec && fpdec->mBase->mType == DT_PACK_TEMPLATE) + mScope->Insert(tdec->mIdent, fpdec); + else + { + Declaration* ptdec = new Declaration(fdec->mLocation, DT_PACK_TYPE); + Declaration* ppdec = nullptr; + while (fpdec) + { + Declaration* pdec = fpdec->mBase->Clone(); + if (ppdec) + ppdec->mNext = pdec; + else + ptdec->mParams = pdec; + ppdec = pdec; + fpdec = fpdec->mNext; + } + + ptdec->mFlags |= DTF_DEFINED; + mScope->Insert(tpdec->mBase->mIdent, ptdec); + } + + return true; + } + + if (!fpdec) + return false; + + if (!ResolveTemplate(fpdec->mBase, tpdec->mBase, true, preliminary)) + return false; + + fpdec = fpdec->mNext; + tpdec = tpdec->mNext; + } + if (fpdec) + return false; + } + else + return false; + + return true; + } + else if (tdec->mType == DT_TYPE_REFERENCE) + { + return ResolveTemplate(fdec, tdec->mBase, true, preliminary); + } + else if (tdec->mType == DT_TYPE_RVALUEREF) + { + return ResolveTemplate(fdec, tdec->mBase, true, preliminary); + } + else if (tdec->mType == DT_TYPE_POINTER) + { + if (fdec->mType == DT_TYPE_POINTER || fdec->mType == DT_TYPE_ARRAY) + return ResolveTemplate(fdec->mBase, tdec->mBase, true, preliminary); + else + return false; + } + else if (tdec->mType == DT_TYPE_TEMPLATE) + { + if (fdec->mType == DT_TYPE_ARRAY) + fdec = fdec->mBase->BuildPointer(fdec->mLocation); + else if (fdec->mType == DT_TYPE_FUNCTION) + fdec = fdec->BuildPointer(fdec->mLocation); + + Declaration* pdec; + if (tdec->mBase) + { + pdec = mScope->Lookup(tdec->mBase->mIdent); + if (!pdec) + return false; + if (pdec->mType == DT_TYPE_STRUCT) + pdec = pdec->mScope->Lookup(tdec->mIdent); + } + else if (preliminary && !same) + return true; + else + pdec = mScope->Insert(tdec->mIdent, fdec); + + if (pdec && !(same ? pdec->IsSame(fdec) : pdec->CanAssign(fdec))) + return false; + + return true; + } + else if (tdec->mType == DT_PACK_TEMPLATE) + { + Declaration* pdec; + if (tdec->mBase) + { + pdec = mScope->Lookup(tdec->mBase->mIdent); + if (!pdec) + return false; + } + else if (fdec->mType == DT_PACK_TYPE) + { + mScope->Insert(tdec->mIdent, fdec); + } + else + { + pdec = mScope->Lookup(tdec->mIdent); + if (!pdec) + { + pdec = new Declaration(fdec->mLocation, DT_PACK_TYPE); + mScope->Insert(tdec->mIdent, pdec); + } + + if (!pdec->mParams) + { + pdec->mParams = fdec->Clone(); + } + else + { + Declaration* ppdec = pdec->mParams; + while (ppdec->mNext) + ppdec = ppdec->mNext; + ppdec->mNext = fdec->Clone(); + } + } + + return true; + } + else if (tdec->mType == DT_TYPE_ARRAY && fdec->mType == DT_TYPE_ARRAY && tdec->mTemplate && tdec->mBase->IsConstSame(fdec->mBase)) + { + Declaration* ifdec = new Declaration(fdec->mLocation, DT_CONST_INTEGER); + ifdec->mBase = TheSignedIntTypeDeclaration; + ifdec->mSize = 2; + ifdec->mInteger = fdec->mSize / fdec->mBase->mSize; + + Declaration * ipdec = mScope->Insert(tdec->mTemplate->mIdent, ifdec); + if (ipdec && !ipdec->IsSame(ifdec)) + return false; + + return true; + + + } + else if (tdec->mType == DT_TYPE_STRUCT && fdec->mType == DT_TYPE_STRUCT && tdec->mTemplate) + { + Declaration *ftdec = tdec->mTemplate; + while (ftdec) + { + if (ftdec->mBase->mQualIdent == fdec->mQualIdent) + { + Declaration* fpdec = ftdec->mParams; + Declaration* tpdec = tdec->mTemplate->mParams; + + while (fpdec && tpdec) + { + if (tpdec->mBase->mType == DT_TYPE_TEMPLATE || tpdec->mBase->mType == DT_CONST_TEMPLATE) + { + Declaration * pdec = mScope->Insert(tpdec->mBase->mIdent, fpdec->mBase); + if (pdec && !pdec->IsSame(fpdec->mBase)) + return false; + } + else if (tpdec->mBase->mType == DT_PACK_TEMPLATE) + { + Declaration* tppack; + if (fpdec->mType == DT_PACK_TEMPLATE) + tppack = fpdec->mBase; + else + { + tppack = new Declaration(fpdec->mLocation, DT_PACK_TYPE); + + Declaration* ppdec = nullptr; + + while (fpdec) + { + if (fpdec->mType == DT_PACK_TEMPLATE) + { + if (ppdec) + ppdec->mNext = fpdec->mBase->mParams; + else + tppack->mParams = fpdec->mBase->mParams; + break; + } + + Declaration* ndec = fpdec->mBase->Clone(); + + if (ppdec) + ppdec->mNext = ndec; + else + tppack->mParams = ndec; + ppdec = ndec; + + fpdec = fpdec->mNext; + } + + } + + Declaration* pdec = mScope->Insert(tpdec->mBase->mIdent, tppack); + if (pdec && !pdec->IsSame(fpdec->mBase)) + return false; + + return true; + } + + fpdec = fpdec->mNext; + tpdec = tpdec->mNext; + } + + if (fpdec) + return false; + else if (!tpdec) + return true; + else if (tpdec->mBase->mType == DT_PACK_TEMPLATE) + { + Declaration*tppack = new Declaration(tpdec->mLocation, DT_PACK_TYPE); + Declaration* pdec = mScope->Insert(tpdec->mBase->mIdent, tppack); + + return true; + } + else + return false; + } + + ftdec = ftdec->mNext; + } + return false; + } + else if (!same && tdec->CanAssign(fdec)) + return true; + else + return tdec->IsSame(fdec); +} + + +Declaration* Declaration::Clone(void) +{ + Declaration* ndec = new Declaration(mLocation, mType); + ndec->mSize = mSize; + ndec->mOffset = mOffset; + ndec->mStride = mStride; + ndec->mStripe = mStripe; + ndec->mBits = mBits; + ndec->mShift = mShift; + ndec->mBase = mBase; + ndec->mFlags = mFlags; + ndec->mScope = mScope; + ndec->mParams = mParams; + ndec->mParamPack = mParamPack; + ndec->mIdent = mIdent; + ndec->mQualIdent = mQualIdent; + ndec->mValue = mValue; + ndec->mVarIndex = mVarIndex; + ndec->mLinkerObject = mLinkerObject; + ndec->mAlignment = mAlignment; + ndec->mSection = mSection; + ndec->mVTable = mVTable; + ndec->mInteger = mInteger; + ndec->mNumber = mNumber; + ndec->mMinValue = mMinValue; + ndec->mMaxValue = mMaxValue; + ndec->mCompilerOptions = mCompilerOptions; + ndec->mParser = mParser; + ndec->mNumVars = mNumVars; + + return ndec; +} + +Declaration* Declaration::ToStriped(Errors * errors) +{ + Declaration* ndec = this->Clone(); + + if (mType == DT_TYPE_ARRAY) + { + if (mSize == 0) + errors->Error(ndec->mLocation, ERRR_STRIPE_REQUIRES_FIXED_SIZE_ARRAY, "__striped requires fixed size array"); + + ndec->mFlags |= DTF_STRIPED; + if (mBase->mType == DT_TYPE_ARRAY) + { + ndec->mBase = mBase->Clone(); + if (mBase->mSize) + { + ndec->mStride = mBase->mSize / mBase->mBase->mSize; + ndec->mBase->mStride = 1; + ndec->mBase->mBase = mBase->mBase->ToStriped(mSize / mBase->mBase->mSize); + } + else + errors->Error(ndec->mLocation, ERRR_STRIPE_REQUIRES_FIXED_SIZE_ARRAY, "__striped with zero size"); + + } + else + { + ndec->mStride = 1; + if (mBase->mSize > 0) + ndec->mBase = mBase->ToStriped(mSize / mBase->mSize); + } + } + else if (ndec->mBase) + { + ndec->mBase = mBase->ToStriped(errors); + } + else + errors->Error(ndec->mLocation, ERRR_STRIPE_REQUIRES_FIXED_SIZE_ARRAY, "__striped requires fixed size array"); + + return ndec; +} + +Declaration* Declaration::ToStriped(int stripe) +{ + Declaration* ndec = new Declaration(mLocation, mType); + ndec->mSize = mSize; + ndec->mOffset = mOffset * stripe; + ndec->mStride = mStride; + ndec->mStripe = stripe; + ndec->mBits = mBits; + ndec->mShift = mShift; + ndec->mFlags = mFlags; + ndec->mIdent = mIdent; + ndec->mQualIdent = mQualIdent; + ndec->mMinValue = mMinValue; + ndec->mMaxValue = mMaxValue; + + if (mType == DT_ELEMENT) + ndec->mBase = mBase->ToStriped(stripe); + else + ndec->mBase = mBase; + + if (mType == DT_TYPE_STRUCT || mType == DT_TYPE_UNION) + { + ndec->mScope = new DeclarationScope(nullptr, mScope->mLevel); + Declaration * p = mParams; + Declaration* prev = nullptr; + while (p) + { + Declaration* pnec = p->ToStriped(stripe); + + ndec->mScope->Insert(pnec->mIdent, pnec); + + if (prev) + prev->mNext = pnec; + else + ndec->mParams = pnec; + prev = pnec; + p = p->mNext; + } + + Declaration* pndec = ndec->BuildPointer(mLocation); + + mScope->Iterate([=](const Ident* ident, Declaration* vdec) { + if (vdec->mType == DT_CONST_FUNCTION) + { + ndec->mScope->Insert(ident, vdec->ToAlternateThis(pndec)); + } + }); + + ndec->mDestructor = mDestructor ? mDestructor->ToAlternateThis(pndec) : nullptr; + ndec->mDefaultConstructor = mDefaultConstructor ? mDefaultConstructor->ToAlternateThis(pndec) : nullptr; + ndec->mCopyConstructor = mCopyConstructor ? mCopyConstructor->ToAlternateThis(pndec) : nullptr; + ndec->mMoveConstructor = mMoveConstructor ? mMoveConstructor->ToAlternateThis(pndec) : nullptr; + ndec->mVectorConstructor = mVectorConstructor ? mVectorConstructor->ToAlternateThis(pndec, 2) : nullptr; + ndec->mVectorDestructor = mVectorDestructor ? mVectorDestructor->ToAlternateThis(pndec, 2) : nullptr; + ndec->mVectorCopyConstructor = mVectorCopyConstructor ? mVectorCopyConstructor->ToAlternateThis(pndec, 2) : nullptr; + ndec->mVectorMoveConstructor = mVectorMoveConstructor ? mVectorMoveConstructor->ToAlternateThis(pndec, 2) : nullptr; + } + else if (mType == DT_TYPE_FUNCTION) + { + ndec->mParams = mParams; + } + else if (mType == DT_TYPE_ARRAY) + { + ndec->mStride = stripe; + ndec->mBase = mBase->ToStriped(stripe); + } + else + { + ndec->mScope = mScope; + ndec->mParams = mParams; + } + + return ndec; +} + +Declaration* Declaration::ToVolatileType(void) +{ + if (mFlags & DTF_VOLATILE) + return this; + + if (!mVolatile) + { + Declaration* ndec = new Declaration(mLocation, mType); + ndec->mSize = mSize; + ndec->mStride = mStride; + ndec->mBase = mBase; + ndec->mBits = mBits; + ndec->mShift = mShift; + ndec->mFlags = mFlags | DTF_VOLATILE; + ndec->mScope = mScope; + ndec->mParams = mParams; + ndec->mIdent = mIdent; + ndec->mQualIdent = mQualIdent; + ndec->mTemplate = mTemplate; + ndec->mMinValue = mMinValue; + ndec->mMaxValue = mMaxValue; + + if (mType == DT_TYPE_STRUCT || mType == DT_TYPE_UNION) + { + ndec->mScope = new DeclarationScope(nullptr, mScope->mLevel); + Declaration* p = mParams; + Declaration* prev = nullptr; + while (p) + { + Declaration* pnec = p->Clone(); + pnec->mBase = pnec->mBase->ToVolatileType(); + + ndec->mScope->Insert(pnec->mIdent, pnec); + + if (prev) + prev->mNext = pnec; + else + ndec->mParams = pnec; + prev = pnec; + p = p->mNext; + } + } + + ndec->mDestructor = mDestructor; + ndec->mDefaultConstructor = mDefaultConstructor; + ndec->mCopyConstructor = mCopyConstructor; + ndec->mMoveConstructor = mMoveConstructor; + ndec->mVectorConstructor = mVectorConstructor; + ndec->mVectorDestructor = mVectorDestructor; + ndec->mVectorCopyConstructor = mVectorCopyConstructor; + ndec->mVectorMoveConstructor = mVectorMoveConstructor; + ndec->mVTable = mVTable; + + mVolatile = ndec; + } + + return mVolatile; +} + +Declaration* Declaration::ToConstType(void) +{ + if (mFlags & DTF_CONST) + return this; + + if (!mConst) + { + Declaration* ndec = new Declaration(mLocation, mType); + ndec->mSize = mSize; + ndec->mStride = mStride; + ndec->mStripe = mStripe; + if (mType == DT_TYPE_ARRAY) + ndec->mBase = mBase->ToConstType(); + else + ndec->mBase = mBase; + ndec->mBits = mBits; + ndec->mShift = mShift; + ndec->mFlags = mFlags | DTF_CONST; + ndec->mScope = mScope; + ndec->mParams = mParams; + ndec->mIdent = mIdent; + ndec->mQualIdent = mQualIdent; + ndec->mTemplate = mTemplate; + ndec->mMinValue = mMinValue; + ndec->mMaxValue = mMaxValue; + + ndec->mDestructor = mDestructor; + ndec->mDefaultConstructor = mDefaultConstructor; + ndec->mCopyConstructor = mCopyConstructor; + ndec->mMoveConstructor = mMoveConstructor; + ndec->mVectorConstructor = mVectorConstructor; + ndec->mVectorDestructor = mVectorDestructor; + ndec->mVectorCopyConstructor = mVectorCopyConstructor; + ndec->mVectorMoveConstructor = mVectorMoveConstructor; + ndec->mVTable = mVTable; + + ndec->mMutable = this; + mConst = ndec; + } + + return mConst; +} + +Declaration* Declaration::ToAlternateThis(Declaration* pthis, int nthis) +{ + Declaration* ndec = this->Clone(); + + if (mType == DT_CONST_FUNCTION) + { + ndec->mBase = mBase->ToAlternateThis(pthis, nthis); + if (mValue) + ndec->mValue = mValue->ToAlternateThis(mBase->mParams, ndec->mBase->mParams); + } + else + { + Declaration* nparam = ndec->mParams->Clone(); + Declaration* npp = nparam; + Declaration* kpp = ndec->mParams->mNext; + while (kpp) + { + npp->mNext = kpp->Clone(); + npp = npp->mNext; + kpp = npp->mNext; + } + nparam->mBase = pthis; + if (nthis == 2) + nparam->mNext->mBase = pthis; + ndec->mParams = nparam; + } + return ndec; +} + +Declaration* Declaration::ToMutableType(void) +{ + if (!(mFlags & DTF_CONST)) + return this; + + if (!mMutable) + { + Declaration* ndec = new Declaration(mLocation, mType); + ndec->mSize = mSize; + ndec->mStride = mStride; + ndec->mStripe = mStripe; + ndec->mBase = mBase; + ndec->mBits = mBits; + ndec->mShift = mShift; + ndec->mFlags = mFlags & ~DTF_CONST; + ndec->mScope = mScope; + ndec->mParams = mParams; + ndec->mIdent = mIdent; + ndec->mQualIdent = mQualIdent; + ndec->mTemplate = mTemplate; + ndec->mMinValue = mMinValue; + ndec->mMaxValue = mMaxValue; + + ndec->mDestructor = mDestructor; + ndec->mDefaultConstructor = mDefaultConstructor; + ndec->mCopyConstructor = mCopyConstructor; + ndec->mMoveConstructor = mMoveConstructor; + ndec->mVectorConstructor = mVectorConstructor; + ndec->mVectorDestructor = mVectorDestructor; + ndec->mVectorCopyConstructor = mVectorCopyConstructor; + ndec->mVectorMoveConstructor = mVectorMoveConstructor; + ndec->mVTable = mVTable; + + ndec->mConst = this; + mMutable = ndec; + } + + return mMutable; +} + +bool Declaration::IsSameTemplate(const Declaration* dec) const +{ + if (this == dec) + return true; + if (this->mType != dec->mType) + return false; + + if (mType == DT_CONST_FUNCTION) + return mBase->IsSame(dec->mBase); + else if (mType == DT_TYPE_STRUCT) + return true; + + return false; +} + +bool Declaration::IsSubType(const Declaration* dec) const +{ + if (this == dec) + return true; + + if (mType == DT_TYPE_VOID) + return true; + + if (IsReference() && dec->IsReference()) + return mBase->IsSubType(dec->mBase); + + if (mType == DT_TYPE_POINTER || mType == DT_TYPE_ARRAY) + { + if (dec->mType == DT_TYPE_POINTER || dec->mType == DT_TYPE_ARRAY) + return /*this->Stride() == dec->Stride() &&*/ mBase->IsSubType(dec->mBase); + } + + if (mType != dec->mType) + return false; + if (mType != DT_TYPE_STRUCT && mSize != dec->mSize) + return false; + if (mStripe != dec->mStripe) + return false; + + if ((mFlags & DTF_SIGNED) != (dec->mFlags & DTF_SIGNED)) + return false; + + if ((dec->mFlags & ~mFlags) & (DTF_CONST | DTF_VOLATILE)) + return false; + + if (mType == DT_TYPE_INTEGER) + return true; + else if (mType == DT_TYPE_BOOL || mType == DT_TYPE_FLOAT || mType == DT_TYPE_VOID) + return true; + else if (mType == DT_TYPE_STRUCT || mType == DT_TYPE_ENUM) + { + if (mScope == dec->mScope || (mIdent == dec->mIdent && mSize == dec->mSize)) + return true; + + if (dec->mBase) + { + Declaration* bcdec = dec->mBase; + while (bcdec) + { + if (IsSubType(bcdec->mBase)) + return true; + bcdec = bcdec->mNext; + } + } + + return false; + } + else if (mType == DT_TYPE_UNION) + return false; + else if (mType == DT_TYPE_ARRAY) + return mSize <= dec->mSize && mBase->IsSubType(dec->mBase); + else if (mType == DT_TYPE_FUNCTION) + { + if (!dec->mBase->IsSubType(mBase)) + return false; + Declaration* dl = mParams, * dr = dec->mParams; + while (dl && dr) + { + if (!dl->mBase->IsSubType(dr->mBase)) + return false; + dl = dl->mNext; + dr = dr->mNext; + } + + if (dl || dr) + return false; + + if ((mFlags & DTF_INTERRUPT) && !(dec->mFlags & DTF_INTERRUPT)) + return false; + + if ((mFlags & DTF_VARIADIC) != (dec->mFlags & DTF_VARIADIC)) + return false; + + return true; + } + + return false; +} + +bool Declaration::IsSameMutable(const Declaration* dec) const +{ + if (this == dec) + return true; + if (mType != dec->mType) + return false; + if (mSize != dec->mSize) + return false; + if (mStripe != dec->mStripe) + return false; + + if ((mFlags & DTF_SIGNED) != (dec->mFlags & DTF_SIGNED)) + return false; + if ((dec->mFlags & DTF_CONST) && !(mFlags & DTF_CONST)) + return false; + + if (mType == DT_TYPE_INTEGER) + return true; + else if (mType == DT_TYPE_BOOL || mType == DT_TYPE_FLOAT || mType == DT_TYPE_VOID) + return true; + else if (mType == DT_TYPE_ENUM) + return mIdent == dec->mIdent; + else if (mType == DT_TYPE_POINTER || mType == DT_TYPE_ARRAY) + return this->Stride() == dec->Stride() && mBase->IsSame(dec->mBase); + else if (mType == DT_TYPE_STRUCT) + return mScope == dec->mScope || (mIdent == dec->mIdent && mSize == dec->mSize); + else if (mType == DT_TYPE_FUNCTION) + { + if (!mBase->IsSame(dec->mBase)) + return false; + Declaration* dl = mParams, * dr = dec->mParams; + while (dl && dr) + { + if (!dl->mBase->IsSame(dr->mBase)) + return false; + dl = dl->mNext; + dr = dr->mNext; + } + + if (dl || dr) + return false; + + if ((mFlags & DTF_VARIADIC) != (dec->mFlags & DTF_VARIADIC)) + return false; + + return true; + } + + return false; +} + +bool Declaration::IsConstRefSame(const Declaration* dec) const +{ + if (IsReference()) + return mBase->IsConstRefSame(dec); + else if (dec->IsReference()) + return IsConstRefSame(dec->mBase); + else + return IsConstSame(dec); +} + +bool Declaration::IsConstSame(const Declaration* dec) const +{ + if (this == dec) + return true; + if (mType != dec->mType) + return false; + if (mSize != dec->mSize) + return false; + if (mStripe != dec->mStripe) + return false; + + if ((mFlags & DTF_SIGNED) != (dec->mFlags & DTF_SIGNED)) + return false; + + if (mType == DT_TYPE_INTEGER) + return true; + else if (mType == DT_TYPE_BOOL || mType == DT_TYPE_FLOAT || mType == DT_TYPE_VOID) + return true; + else if (mType == DT_TYPE_ENUM) + return mIdent == dec->mIdent; + else if (mType == DT_TYPE_POINTER || mType == DT_TYPE_ARRAY) + return this->Stride() == dec->Stride() && mBase->IsSame(dec->mBase); + else if (mType == DT_TYPE_STRUCT) + return mScope == dec->mScope || (mIdent == dec->mIdent && mSize == dec->mSize); + else if (mType == DT_TYPE_FUNCTION) + { + if (!mBase->IsSame(dec->mBase)) + return false; + Declaration* dl = mParams, * dr = dec->mParams; + while (dl && dr) + { + if (!dl->mBase->IsSame(dr->mBase)) + return false; + dl = dl->mNext; + dr = dr->mNext; + } + + if (dl || dr) + return false; + + if ((mFlags & DTF_VARIADIC) != (dec->mFlags & DTF_VARIADIC)) + return false; + + return true; + } + + return false; +} + +bool Declaration::IsTemplateSameParams(const Declaration* dec, const Declaration* tdec) const +{ + if (mType == DT_TYPE_FUNCTION && dec->mType == DT_TYPE_FUNCTION) + { + Declaration* ld = mParams, * rd = dec->mParams; + if (mFlags & DTF_FUNC_THIS) + ld = ld->mNext; + + while (ld && rd) + { + if (!ld->mBase->IsTemplateSame(rd->mBase, tdec)) + return false; + ld = ld->mNext; + rd = rd->mNext; + } + + return !ld && !rd; + } + else + return false; +} + +bool Declaration::IsSameParams(const Declaration* dec) const +{ + if (mType == DT_TYPE_FUNCTION && dec->mType == DT_TYPE_FUNCTION) + { + Declaration* ld = mParams, * rd = dec->mParams; + while (ld && rd) + { + if (!ld->mBase->IsSame(rd->mBase)) + return false; + ld = ld->mNext; + rd = rd->mNext; + } + + return !ld && !rd; + } + else if (mType == DT_TEMPLATE && dec->mType == DT_TEMPLATE) + { + Declaration* ld = mParams, * rd = dec->mParams; + while (ld && rd) + { + if (ld->mType == DT_CONST_FUNCTION && rd->mType == DT_CONST_FUNCTION) + { + if (ld->mValue != rd->mValue) + return false; + } + else if (ld->mType == DT_PACK_TEMPLATE && rd->mType == DT_PACK_TEMPLATE) + { + if (!ld->mBase->IsSameParams(rd->mBase)) + return false; + } + else if (!ld->mBase || !rd->mBase) + return false; + else if (!ld->mBase->IsSame(rd->mBase)) + return false; + ld = ld->mNext; + rd = rd->mNext; + } + + return !ld && !rd; + } + else if (mType == DT_PACK_TYPE && dec->mType == DT_PACK_TYPE) + { + Declaration* ld = mParams, * rd = dec->mParams; + while (ld && rd) + { + if (!ld->IsSame(rd)) + return false; + ld = ld->mNext; + rd = rd->mNext; + } + + return !ld && !rd; + } + else + return false; +} + +bool Declaration::IsDerivedFrom(const Declaration* dec) const +{ + if (mType != DT_TYPE_FUNCTION || dec->mType != DT_TYPE_FUNCTION) + return false; + + if (!(mFlags & DTF_FUNC_THIS) || !(dec->mFlags & DTF_FUNC_THIS)) + return false; + + if (!mBase->IsSame(dec->mBase)) + return false; + Declaration* dl = mParams->mNext, * dr = dec->mParams->mNext; + while (dl && dr) + { + if (!dl->mBase->IsSame(dr->mBase)) + return false; + dl = dl->mNext; + dr = dr->mNext; + } + + if (dl || dr) + return false; + + return true; +} + +bool Declaration::IsSame(const Declaration* dec) const +{ + if (this == dec) + return true; + if (mType != dec->mType) + return false; + + if ((mFlags & (DTF_SIGNED | DTF_CONST | DTF_VOLATILE)) != (dec->mFlags & (DTF_SIGNED | DTF_CONST | DTF_VOLATILE))) + return false; + + if (!(mFlags & dec->mFlags & DTF_DEFINED) && mType == DT_TYPE_STRUCT) + return mIdent == dec->mIdent; + + if (mSize != dec->mSize) + return false; + if (mStripe != dec->mStripe) + return false; + + if (mType == DT_CONST_INTEGER) + return mInteger == dec->mInteger; + else if (mType == DT_TYPE_INTEGER) + return true; + else if (mType == DT_TYPE_BOOL || mType == DT_TYPE_FLOAT || mType == DT_TYPE_VOID || mType == DT_TYPE_AUTO) + return true; + else if (mType == DT_TYPE_ENUM) + return mIdent == dec->mIdent; + else if (mType == DT_TYPE_POINTER || mType == DT_TYPE_ARRAY) + { + if (mBase->mType == DT_TYPE_STRUCT && dec->mBase->mType == DT_TYPE_STRUCT && mBase->mStripe == dec->mBase->mStripe) + { + if (mBase->mQualIdent == dec->mBase->mQualIdent && + (mBase->mFlags & (DTF_CONST | DTF_VOLATILE)) == (dec->mBase->mFlags & (DTF_CONST | DTF_VOLATILE))) + return true; + else + return false; + } + else + return this->Stride() == dec->Stride() && mBase->IsSame(dec->mBase); + } + else if (IsReference()) + return mBase->IsSame(dec->mBase); + else if (mType == DT_TYPE_STRUCT) + return mScope == dec->mScope || (mQualIdent == dec->mQualIdent && mSize == dec->mSize); + else if (mType == DT_TYPE_FUNCTION) + { + if (!mBase->IsSame(dec->mBase)) + return false; + Declaration* dl = mParams, * dr = dec->mParams; + while (dl && dr) + { + if (!dl->mBase->IsSame(dr->mBase)) + return false; + dl = dl->mNext; + dr = dr->mNext; + } + + if (dl || dr) + return false; + + if ((mFlags & DTF_VARIADIC) != (dec->mFlags & DTF_VARIADIC)) + return false; + + return true; + } + else if (mType == DT_TYPE_TEMPLATE || mType == DT_PACK_TEMPLATE) + { + return mIdent == dec->mIdent; + } + + return false; +} + +bool Declaration::IsTemplateSame(const Declaration* dec, const Declaration * tdec) const +{ + uint64 dflags = dec->mFlags; + + if (dec->mType == DT_TYPE_TEMPLATE) + { + dec = tdec->mScope->Lookup(dec->mIdent); + if (!dec) + return true; + dflags |= dec->mFlags; + } + else if (dec->mType == DT_PACK_TEMPLATE) + { + dec = tdec->mScope->Lookup(dec->mIdent); + if (!dec) + return true; + dflags |= dec->mFlags; + } + + if (this == dec) + return true; + + if (mType != dec->mType) + return false; + + if (dec->mType == DT_TYPE_STRUCT && dec->mTemplate) + return true; + + if (mSize != dec->mSize) + return false; + if (mStripe != dec->mStripe) + return false; + + if ((mFlags & (DTF_SIGNED | DTF_CONST | DTF_VOLATILE)) != (dflags & (DTF_SIGNED | DTF_CONST | DTF_VOLATILE))) + return false; + + if (mType == DT_TYPE_INTEGER) + return true; + else if (mType == DT_TYPE_BOOL || mType == DT_TYPE_FLOAT || mType == DT_TYPE_VOID) + return true; + else if (mType == DT_TYPE_ENUM) + return mIdent == dec->mIdent; + else if (mType == DT_TYPE_POINTER || mType == DT_TYPE_ARRAY) + { + if (mBase->mType == DT_TYPE_STRUCT && dec->mBase->mType == DT_TYPE_STRUCT && mBase->mStripe == dec->mBase->mStripe) + { + if (mBase->mQualIdent == dec->mBase->mQualIdent && + (mBase->mFlags & (DTF_CONST | DTF_VOLATILE)) == (dec->mBase->mFlags & (DTF_CONST | DTF_VOLATILE))) + return true; + else + return false; + } + else if (dec->mBase->mType == DT_TYPE_TEMPLATE) + return mBase->IsTemplateSame(dec->mBase, tdec); + else + return this->Stride() == dec->Stride() && mBase->IsTemplateSame(dec->mBase, tdec); + } + else if (IsReference()) + return mBase->IsTemplateSame(dec->mBase, tdec); + else if (mType == DT_TYPE_STRUCT) + return mScope == dec->mScope || (mIdent == dec->mIdent && mSize == dec->mSize); + else if (mType == DT_TYPE_FUNCTION) + { + if (!mBase->IsTemplateSame(dec->mBase, tdec)) + return false; + Declaration* dl = mParams, * dr = dec->mParams; + while (dl && dr) + { + if (!dl->mBase->IsTemplateSame(dr->mBase, tdec)) + return false; + dl = dl->mNext; + dr = dr->mNext; + } + + if (dl || dr) + return false; + + if ((mFlags & DTF_VARIADIC) != (dec->mFlags & DTF_VARIADIC)) + return false; + + return true; + } + + return false; +} + +bool Declaration::IsSameValue(const Declaration* dec) const +{ + if (mType != dec->mType || mSize != dec->mSize) + return false; + + switch (mType) + { + case DT_CONST_INTEGER: + return mInteger == dec->mInteger; + case DT_CONST_FLOAT: + return mNumber == dec->mNumber; + case DT_CONST_ADDRESS: + return mInteger == dec->mInteger; + case DT_CONST_POINTER: + return mValue && dec->mValue && mValue->mType == dec->mValue->mType && mValue->mDecValue == dec->mValue->mDecValue; + } + + return false; +} + +bool Declaration::CanAssign(const Declaration* fromType) const +{ + if (fromType->IsReference()) + return this->CanAssign(fromType->mBase); + if (IsReference()) + return mBase->IsSubType(fromType); + + if (this->IsSame(fromType)) + return true; + else if (IsNumericType()) + { + if (fromType->IsNumericType()) + return true; + } + else if (mType == DT_TYPE_STRUCT && fromType->mType == DT_TYPE_STRUCT) + { + if (mScope == fromType->mScope || (mIdent == fromType->mIdent && mSize == fromType->mSize)) + return true; + if (fromType->mBase) + return this->CanAssign(fromType->mBase->mBase); + return false; + } + else if (mType == DT_TYPE_ARRAY && fromType->mType == DT_TYPE_ARRAY) + { + return mSize == fromType->mSize && mStride == fromType->mStride && mBase->CanAssign(fromType->mBase); + } + else if (mType == DT_TYPE_POINTER) + { + if (fromType->mType == DT_TYPE_POINTER || fromType->mType == DT_TYPE_ARRAY) + { + if (mBase->mType == DT_TYPE_VOID || fromType->mBase->mType == DT_TYPE_VOID) + { + if (mCompilerOptions & COPT_CPLUSPLUS) + { + if (fromType == TheNullPointerTypeDeclaration) + return true; + return mBase->mType == DT_TYPE_VOID && ((mBase->mFlags & DTF_CONST) || !(fromType->mBase->mFlags & DTF_CONST)); + } + else + return true; + } + else if (mBase->mStripe == fromType->mBase->mStripe && mBase->IsSubType(fromType->mBase)) + return true; + } + else if (mBase->mType == DT_TYPE_FUNCTION && fromType->mType == DT_TYPE_FUNCTION) + { + return mBase->IsSubType(fromType); + } + else if (mBase->mType == DT_TYPE_VOID && fromType->mType == DT_TYPE_ASSEMBLER) + { + return true; + } + else if (mBase->mType == DT_TYPE_VOID && fromType->mType == DT_TYPE_FUNCTION) + { + return true; + } + else if (mBase->mType == DT_TYPE_FUNCTION && fromType->mType == DT_TYPE_ASSEMBLER) + { + return (mBase->mBase->mType == DT_TYPE_VOID && !mBase->mBase->mParams); + } + } + + return false; +} + +bool Declaration::IsPointerType(void) const +{ + return mType == DT_TYPE_ARRAY || mType == DT_TYPE_POINTER; +} + +bool Declaration::IsIntegerType(void) const +{ + return mType == DT_TYPE_INTEGER || mType == DT_TYPE_BOOL || mType == DT_TYPE_ENUM; +} + +bool Declaration::IsNumericType(void) const +{ + return mType == DT_TYPE_INTEGER || mType == DT_TYPE_BOOL || mType == DT_TYPE_FLOAT || mType == DT_TYPE_ENUM; +} + + +bool Declaration::IsReference(void) const +{ + return mType == DT_TYPE_REFERENCE || mType == DT_TYPE_RVALUEREF; +} + +bool Declaration::IsIndexed(void) const +{ + return mType == DT_TYPE_ARRAY || mType == DT_TYPE_POINTER; +} + +bool Declaration::ContainsArray(void) const +{ + if (mType == DT_TYPE_ARRAY) + return true; + else if (mType == DT_TYPE_STRUCT) + { + Declaration* p = mParams; + while (p) + { + if (p->mType == DT_ELEMENT && p->mBase->ContainsArray()) + return true; + p = p->mNext; + } + } + return false; +} + +bool Declaration::IsShortIntStruct(void) const +{ + if (mType == DT_TYPE_STRUCT && mSize <= 4 && !mDestructor && !mCopyConstructor) + { + Declaration* em = mParams; + while (em) + { + if (em->mType == DT_ELEMENT && em->mBase->IsIntegerType() && em->mBits == 0) + em = em->mNext; + else + return false; + } + +// if (strcmp(mIdent->mString, "connection")) return false; + return true; + } + + return false; +} + +bool Declaration::HasConstructor(void) const +{ + return mType == DT_TYPE_STRUCT && mScope && mScope->Lookup(mIdent->PreMangle("+")); +} + +bool Declaration::IsComplexStruct(void) const +{ + return mType == DT_TYPE_STRUCT && !IsShortIntStruct(); +} + +bool Declaration::IsStructOrUnion(void) const +{ + return mType == DT_TYPE_STRUCT || mType == DT_TYPE_UNION; +} + +bool Declaration::IsSimpleType(void) const +{ + return mType == DT_TYPE_INTEGER || mType == DT_TYPE_BOOL || mType == DT_TYPE_FLOAT || mType == DT_TYPE_ENUM || mType == DT_TYPE_POINTER; +} + +bool Declaration::IsValueType(void) const +{ + if (IsSimpleType()) + return true; + + if (mType == DT_TYPE_STRUCT && mSize <= 4 && !mDestructor && !mCopyConstructor) + { + Declaration* em = mParams; + while (em) + { + if (em->mType == DT_ELEMENT && em->mBase->IsValueType()) + em = em->mNext; + else + return false; + } + return true; + } + + return false; +} + +void Declaration::SetDefined(void) +{ + mFlags |= DTF_DEFINED; + if (mConst) + mConst->mFlags |= DTF_DEFINED; +} + +Declaration* TheVoidTypeDeclaration, * TheConstVoidTypeDeclaration, * TheSignedIntTypeDeclaration, * TheUnsignedIntTypeDeclaration, * TheConstCharTypeDeclaration, * TheCharTypeDeclaration, * TheSignedCharTypeDeclaration, * TheUnsignedCharTypeDeclaration; +Declaration* TheBoolTypeDeclaration, * TheFloatTypeDeclaration, * TheConstVoidPointerTypeDeclaration, * TheVoidPointerTypeDeclaration, * TheSignedLongTypeDeclaration, * TheUnsignedLongTypeDeclaration; +Declaration* TheVoidFunctionTypeDeclaration, * TheConstVoidValueDeclaration; +Declaration* TheCharPointerTypeDeclaration, * TheConstCharPointerTypeDeclaration; +Expression* TheVoidExpression; +Declaration* TheNullptrConstDeclaration, * TheZeroIntegerConstDeclaration, * TheZeroFloatConstDeclaration, * TheNullPointerTypeDeclaration; +Declaration* TheTrueConstDeclaration, * TheFalseConstDeclaration; + +void InitDeclarations(void) +{ + static Location noloc; + TheVoidTypeDeclaration = new Declaration(noloc, DT_TYPE_VOID); + TheVoidTypeDeclaration->mSize = 0; + TheVoidTypeDeclaration->mFlags = DTF_DEFINED; + + TheConstVoidTypeDeclaration = new Declaration(noloc, DT_TYPE_VOID); + TheConstVoidTypeDeclaration->mSize = 0; + TheConstVoidTypeDeclaration->mFlags = DTF_DEFINED | DTF_CONST; + + TheVoidPointerTypeDeclaration = new Declaration(noloc, DT_TYPE_POINTER); + TheVoidPointerTypeDeclaration->mBase = TheVoidTypeDeclaration; + TheVoidPointerTypeDeclaration->mSize = 2; + TheVoidPointerTypeDeclaration->mFlags = DTF_DEFINED; + + TheConstVoidPointerTypeDeclaration = new Declaration(noloc, DT_TYPE_POINTER); + TheConstVoidPointerTypeDeclaration->mBase = TheConstVoidTypeDeclaration; + TheConstVoidPointerTypeDeclaration->mSize = 2; + TheConstVoidPointerTypeDeclaration->mFlags = DTF_DEFINED; + + TheNullPointerTypeDeclaration = new Declaration(noloc, DT_TYPE_POINTER); + TheNullPointerTypeDeclaration->mBase = TheVoidTypeDeclaration; + TheNullPointerTypeDeclaration->mSize = 2; + TheNullPointerTypeDeclaration->mFlags = DTF_DEFINED; + + TheVoidFunctionTypeDeclaration = new Declaration(noloc, DT_TYPE_FUNCTION); + TheVoidFunctionTypeDeclaration->mBase = TheVoidTypeDeclaration; + TheVoidFunctionTypeDeclaration->mSize = 2; + TheVoidFunctionTypeDeclaration->mFlags = DTF_DEFINED; + + TheConstVoidValueDeclaration = new Declaration(noloc, DT_CONST_INTEGER); + TheVoidFunctionTypeDeclaration->mBase = TheVoidTypeDeclaration; + TheVoidFunctionTypeDeclaration->mSize = 2; + TheVoidFunctionTypeDeclaration->mInteger = 0; + TheVoidFunctionTypeDeclaration->mFlags = DTF_DEFINED; + + TheSignedIntTypeDeclaration = new Declaration(noloc, DT_TYPE_INTEGER); + TheSignedIntTypeDeclaration->mSize = 2; + TheSignedIntTypeDeclaration->mFlags = DTF_DEFINED | DTF_SIGNED; + + TheUnsignedIntTypeDeclaration = new Declaration(noloc, DT_TYPE_INTEGER); + TheUnsignedIntTypeDeclaration->mSize = 2; + TheUnsignedIntTypeDeclaration->mFlags = DTF_DEFINED; + + TheSignedLongTypeDeclaration = new Declaration(noloc, DT_TYPE_INTEGER); + TheSignedLongTypeDeclaration->mSize = 4; + TheSignedLongTypeDeclaration->mFlags = DTF_DEFINED | DTF_SIGNED; + + TheUnsignedLongTypeDeclaration = new Declaration(noloc, DT_TYPE_INTEGER); + TheUnsignedLongTypeDeclaration->mSize = 4; + TheUnsignedLongTypeDeclaration->mFlags = DTF_DEFINED; + + TheSignedCharTypeDeclaration = new Declaration(noloc, DT_TYPE_INTEGER); + TheSignedCharTypeDeclaration->mSize = 1; + TheSignedCharTypeDeclaration->mFlags = DTF_DEFINED | DTF_SIGNED; + + TheConstCharTypeDeclaration = new Declaration(noloc, DT_TYPE_INTEGER); + TheConstCharTypeDeclaration->mSize = 1; + TheConstCharTypeDeclaration->mFlags = DTF_DEFINED | DTF_CONST; + + TheUnsignedCharTypeDeclaration = new Declaration(noloc, DT_TYPE_INTEGER); + TheUnsignedCharTypeDeclaration->mSize = 1; + TheUnsignedCharTypeDeclaration->mFlags = DTF_DEFINED; + TheCharTypeDeclaration = TheUnsignedCharTypeDeclaration; + + TheBoolTypeDeclaration = new Declaration(noloc, DT_TYPE_BOOL); + TheBoolTypeDeclaration->mSize = 1; + TheBoolTypeDeclaration->mFlags = DTF_DEFINED; + + TheFloatTypeDeclaration = new Declaration(noloc, DT_TYPE_FLOAT); + TheFloatTypeDeclaration->mSize = 4; + TheFloatTypeDeclaration->mFlags = DTF_DEFINED | DTF_SIGNED; + + + TheCharPointerTypeDeclaration = new Declaration(noloc, DT_TYPE_POINTER); + TheCharPointerTypeDeclaration->mBase = TheCharTypeDeclaration; + TheCharPointerTypeDeclaration->mSize = 2; + TheCharPointerTypeDeclaration->mFlags = DTF_DEFINED; + + TheConstCharPointerTypeDeclaration = new Declaration(noloc, DT_TYPE_POINTER); + TheConstCharPointerTypeDeclaration->mBase = TheConstCharTypeDeclaration; + TheConstCharPointerTypeDeclaration->mSize = 2; + TheConstCharPointerTypeDeclaration->mFlags = DTF_DEFINED; + + TheVoidExpression = new Expression(noloc, EX_CONSTANT); + TheVoidExpression->mDecType = TheConstVoidTypeDeclaration; + TheVoidExpression->mDecValue = TheConstVoidValueDeclaration; + + + TheNullptrConstDeclaration = new Declaration(noloc, DT_CONST_ADDRESS); + TheNullptrConstDeclaration->mBase = TheNullPointerTypeDeclaration; + TheNullptrConstDeclaration->mSize = 2; + TheZeroIntegerConstDeclaration = new Declaration(noloc, DT_CONST_INTEGER); + TheZeroIntegerConstDeclaration->mBase = TheSignedIntTypeDeclaration; + TheZeroIntegerConstDeclaration->mSize = 2; + TheZeroFloatConstDeclaration = new Declaration(noloc, DT_CONST_FLOAT); + TheZeroFloatConstDeclaration->mBase = TheFloatTypeDeclaration; + TheZeroFloatConstDeclaration->mSize = 4; + + TheTrueConstDeclaration = new Declaration(noloc, DT_CONST_INTEGER); + TheTrueConstDeclaration->mBase = TheBoolTypeDeclaration; + TheTrueConstDeclaration->mSize = 1; + TheTrueConstDeclaration->mInteger = 1; + + TheFalseConstDeclaration = new Declaration(noloc, DT_CONST_INTEGER); + TheFalseConstDeclaration->mBase = TheBoolTypeDeclaration; + TheFalseConstDeclaration->mSize = 1; + TheFalseConstDeclaration->mInteger = 0; +} diff --git a/oscar64/oscar64/Declaration.h b/oscar64/oscar64/Declaration.h new file mode 100644 index 0000000..cf452cc --- /dev/null +++ b/oscar64/oscar64/Declaration.h @@ -0,0 +1,428 @@ +#pragma once + +#include "Ident.h" +#include "Scanner.h" +#include "MachineTypes.h" +#include "Assembler.h" +#include "Array.h" + +class LinkerObject; +class LinkerSection; +class LinkerRegion; +class Linker; +class Parser; + +enum DecType +{ + DT_TYPE_VOID, + DT_TYPE_NULL, + DT_TYPE_BOOL, + DT_TYPE_INTEGER, + DT_TYPE_FLOAT, + DT_TYPE_ENUM, + DT_TYPE_POINTER, + DT_TYPE_REFERENCE, + DT_TYPE_RVALUEREF, + DT_TYPE_ARRAY, + DT_TYPE_STRUCT, + DT_TYPE_UNION, + DT_TYPE_TEMPLATE, + DT_TYPE_FUNCTION, + DT_TYPE_ASSEMBLER, + DT_TYPE_AUTO, + + DT_CONST_INTEGER, + DT_CONST_FLOAT, + DT_CONST_FUNCTION, + DT_CONST_ADDRESS, + DT_CONST_DATA, + DT_CONST_STRUCT, + DT_CONST_POINTER, + DT_CONST_REFERENCE, + DT_CONST_ASSEMBLER, + DT_CONST_CONSTRUCTOR, + DT_CONST_TEMPLATE, + + DT_PACK_TEMPLATE, + DT_PACK_VARIABLE, + DT_PACK_ARGUMENT, + DT_PACK_TYPE, + DT_PACK_ANON, + + DT_VARIABLE, + DT_ARGUMENT, + DT_ELEMENT, + DT_PADDING, + DT_ANON, + DT_LABEL, + DT_VARIABLE_REF, + DT_FUNCTION_REF, + DT_LABEL_REF, + DT_NAMESPACE, + DT_BASECLASS, + DT_CLABEL, + + DT_BINDING, + + DT_TEMPLATE, + + DT_VTABLE +}; + +// TypeFlags + +static const uint64 DTF_SIGNED = (1ULL << 0); +static const uint64 DTF_DEFINED = (1ULL << 1); +static const uint64 DTF_GLOBAL = (1ULL << 2); +static const uint64 DTF_VARIADIC = (1ULL << 3); +static const uint64 DTF_INTRINSIC = (1ULL << 4); +static const uint64 DTF_STATIC = (1ULL << 5); +static const uint64 DTF_CONST = (1ULL << 6); +static const uint64 DTF_VOLATILE = (1ULL << 7); +static const uint64 DTF_EXTERN = (1ULL << 8); +static const uint64 DTF_NATIVE = (1ULL << 9); +static const uint64 DTF_UPPER_BYTE = (1ULL << 10); +static const uint64 DTF_LOWER_BYTE = (1ULL << 11); +static const uint64 DTF_SECTION_START = (1ULL << 12); +static const uint64 DTF_SECTION_END = (1ULL << 13); +static const uint64 DTF_FASTCALL = (1ULL << 14); +static const uint64 DTF_INLINE = (1ULL << 15); +static const uint64 DTF_ANALYZED = (1ULL << 16); +static const uint64 DTF_REQUEST_INLINE = (1ULL << 17); +static const uint64 DTF_INTERRUPT = (1ULL << 18); +static const uint64 DTF_EXPORT = (1ULL << 19); +static const uint64 DTF_HWINTERRUPT = (1ULL << 20); +static const uint64 DTF_STACKCALL = (1ULL << 21); +static const uint64 DTF_ZEROPAGE = (1ULL << 22); +static const uint64 DTF_PREVENT_INLINE = (1ULL << 23); +static const uint64 DTF_STRIPED = (1ULL << 24); +static const uint64 DTF_DYNSTACK = (1ULL << 25); +static const uint64 DTF_PRIVATE = (1ULL << 26); +static const uint64 DTF_PROTECTED = (1ULL << 27); +static const uint64 DTF_VIRTUAL = (1ULL << 28); +static const uint64 DTF_TEMPORARY = (1ULL << 29); +static const uint64 DTF_COMPLETED = (1ULL << 30); +static const uint64 DTF_CONSTEXPR = (1ULL << 31); + +static const uint64 DTF_AUTO_TEMPLATE = (1ULL << 32); +static const uint64 DTF_BANK_INLAY = (1ULL << 33); +static const uint64 DTF_PURE_VIRTUAL = (1ULL << 34); +static const uint64 DTF_FORCE_INLINE = (1ULL << 35); + +static const uint64 DTF_FUNC_VARIABLE = (1ULL << 36); +static const uint64 DTF_FUNC_ASSEMBLER = (1ULL << 37); +static const uint64 DTF_FUNC_RECURSIVE = (1ULL << 38); +static const uint64 DTF_FUNC_ANALYZING = (1ULL << 39); + +static const uint64 DTF_FUNC_CONSTEXPR = (1ULL << 40); +static const uint64 DTF_FUNC_INTRSAVE = (1ULL << 41); +static const uint64 DTF_FUNC_INTRCALLED = (1ULL << 42); +static const uint64 DTF_FUNC_PURE = (1ULL << 43); + +static const uint64 DTF_FPARAM_CONST = (1ULL << 44); +static const uint64 DTF_FPARAM_NOCONST = (1ULL << 45); +static const uint64 DTF_VAR_ADDRESS = (1ULL << 46); + +static const uint64 DTF_FUNC_THIS = (1ULL << 47); + +static const uint64 DTF_VAR_ALIASING = (1ULL << 48); +static const uint64 DTF_FPARAM_UNUSED = (1ULL << 49); +static const uint64 DTF_DEPRECATED = (1ULL << 50); +static const uint64 DTF_FUNC_NO_RETURN = (1ULL << 51); +static const uint64 DTF_PLACED = (1ULL << 52); +static const uint64 DTF_NO_PAGE_CROSS = (1ULL << 53); + +static const uint64 DTF_ASM_PRESERVE_A = (1ULL << 54); +static const uint64 DTF_ASM_PRESERVE_X = (1ULL << 55); +static const uint64 DTF_ASM_PRESERVE_Y = (1ULL << 56); + +static const uint64 DTF_MEMMAP = (1ULL << 57); +static const uint64 DTF_FPARAM_RANGE_LIMITED = (1ULL << 58); +static const uint64 DTF_PRECOMPILED = (1ULL << 59); +static const uint64 DTF_FUNC_DELETED = (1ULL << 60); + +class Declaration; + +enum ScopeLevel +{ + SLEVEL_SCOPE, + SLEVEL_USING, + SLEVEL_GLOBAL, + SLEVEL_STATIC, + SLEVEL_NAMESPACE, + SLEVEL_TEMPLATE, + SLEVEL_CLASS, + SLEVEL_FUNCTION, + SLEVEL_LOCAL, +}; + +class DeclarationScope +{ +public: + DeclarationScope(DeclarationScope * parent, ScopeLevel level, const Ident * name = nullptr); + ~DeclarationScope(void); + + const Ident* Mangle(const Ident* ident) const; + + Declaration* Insert(const Ident* ident, Declaration* dec); + Declaration* Lookup(const Ident* ident, ScopeLevel limit = SLEVEL_GLOBAL); + + void End(const Location & loc); + void Clear(void); + + void UseScope(DeclarationScope* scope); + + template void Iterate(F && f); + + ScopeLevel mLevel; + const Ident * mName; + + DeclarationScope* Clone(void) const; + + DeclarationScope* mParent; +protected: + struct Entry + { + const Ident* mIdent; + Declaration* mDec; + }; + Entry * mHash; + int mHashSize, mHashFill; + ExpandingArray mUsed; +}; + +template void DeclarationScope::Iterate(F&& f) +{ + for (int i = 0; i < mHashSize; i++) + { + if (mHash[i].mIdent) + f(mHash[i].mIdent, mHash[i].mDec); + } +} + +enum ExpressionType : uint8 +{ + EX_ERROR, + EX_VOID, + EX_CONSTANT, + EX_VARIABLE, + EX_ASSIGNMENT, + EX_INITIALIZATION, + EX_BINARY, + EX_RELATIONAL, + EX_PREINCDEC, + EX_PREFIX, + EX_POSTFIX, + EX_POSTINCDEC, + EX_INDEX, + EX_QUALIFY, + EX_CALL, + EX_INLINE, + EX_VCALL, + EX_DISPATCH, + EX_LIST, + EX_COMMA, + EX_RETURN, + EX_SEQUENCE, + EX_WHILE, + EX_IF, + EX_ELSE, + EX_FOR, + EX_FORBODY, + EX_DO, + EX_SCOPE, + EX_BREAK, + EX_CONTINUE, + EX_TYPE, + EX_TYPECAST, + EX_LOGICAL_AND, + EX_LOGICAL_OR, + EX_LOGICAL_NOT, + EX_ASSEMBLER, + EX_UNDEFINED, + EX_SWITCH, + EX_CASE, + EX_DEFAULT, + EX_CONDITIONAL, + EX_ASSUME, + EX_BANKOF, + EX_CONSTRUCT, + EX_CLEANUP, + EX_RESULT, + EX_PACK, + EX_PACK_TYPE, + EX_LABEL, + EX_GOTO, + EX_AGGREGATE +}; + +static const uint32 ANAFL_LHS = (1U << 0); +static const uint32 ANAFL_RHS = (1U << 1); +static const uint32 ANAFL_ASSIGN = (1U << 2); +static const uint32 ANAFL_ALIAS = (1U << 3); +static const uint32 ANAFL_COPY = (1U << 4); +static const uint32 ANAFL_ADDRESS = (1U << 5); + + +class Expression +{ +public: + Expression(const Location& loc, ExpressionType type); + ~Expression(void); + + + Location mLocation, mEndLocation; + Expression * mLeft, * mRight; + Declaration * mDecValue, * mDecType; + + uint32 mUID; + + Token mToken; + ExpressionType mType; + AsmInsType mAsmInsType; + AsmInsMode mAsmInsMode; + bool mConst; + uint8 mFlags; + + Expression* LogicInvertExpression(void); + Expression* ConstantFold(Errors * errors, LinkerSection* dataSection, Linker * linker = nullptr); + Expression* ConstantDereference(Errors* errors, LinkerSection* dataSection); + bool HasSideEffects(void) const; + Expression* ListAppend(Expression* lexp); + Expression* ToAlternateThis(Declaration* pthis, Declaration* nthis); + Expression* Clone(void) const; + Expression* Clone(const Location & loc) const; + Expression* ToVarConst(Declaration* pvar, Declaration* pconst) const; + + void ReplaceVariable(Declaration* pvar, Declaration* nvar); + + bool IsSame(const Expression* exp) const; + bool IsRValue(void) const; + bool IsLValue(void) const; + bool IsConstRef(void) const; + bool IsVolatile(void) const; + + void Dump(int ident) const; +}; + +class Declaration +{ +public: + Declaration(const Location & loc, DecType type); + ~Declaration(void); + + uint32 mUID; + + Location mLocation, mEndLocation; + DecType mType; + Token mToken; + Declaration * mBase, * mParams, * mParamPack, * mNext, * mPrev, * mConst, * mMutable, * mVolatile; + Declaration * mDefaultConstructor, * mDestructor, * mCopyConstructor, * mCopyAssignment, * mMoveConstructor, * mMoveAssignment; + Declaration * mVectorConstructor, * mVectorDestructor, * mVectorCopyConstructor, * mVectorCopyAssignment, * mVectorMoveConstructor, * mVectorMoveAssignment; + Declaration * mVTable, * mClass, * mTemplate; + Declaration * mForwardParam, * mForwardCall; + + Expression* mValue, * mReturn; + DeclarationScope* mScope; + int mOffset, mSize, mVarIndex, mNumVars, mComplexity, mLocalSize, mAlignment, mFastCallBase, mFastCallSize, mFastCallBase2, mFastCallSize2, mStride, mStripe, mVariadicSize; + uint8 mShift, mBits; + int64 mInteger, mMinValue, mMaxValue; + double mNumber; + uint64 mFlags, mCompilerOptions, mOptFlags; + const Ident * mIdent, * mQualIdent, * mMangleIdent; + LinkerSection * mSection; + LinkerRegion * mInlayRegion; + const uint8 * mData; + LinkerObject * mLinkerObject; + int mUseCount; + TokenSequence * mTokens; + Parser * mParser; + + int64 MinInteger(void) const; + int64 MaxInteger(void) const; + int64 CastInteger(int64 v) const; + + GrowingArray mCallers, mCalled, mFriends, mMayCall; + ExpandingArray mReferences; + + bool CanAssign(const Declaration* fromType) const; + bool IsSame(const Declaration* dec) const; + bool IsDerivedFrom(const Declaration* dec) const; + bool IsSubType(const Declaration* dec) const; + bool IsConstRefSame(const Declaration* dec) const; + bool IsConstSame(const Declaration* dec) const; + bool IsSameValue(const Declaration* dec) const; + bool IsSameParams(const Declaration* dec) const; + bool IsSameMutable(const Declaration* dec) const; + + bool IsTemplateSame(const Declaration* dec, const Declaration* tdec) const; + bool IsTemplateSameParams(const Declaration* dec, const Declaration* tdec) const; + bool IsSameTemplate(const Declaration* dec) const; + + bool IsIntegerType(void) const; + bool IsNumericType(void) const; + bool IsSimpleType(void) const; + bool IsReference(void) const; + bool IsIndexed(void) const; + bool ContainsArray(void) const; + bool IsShortIntStruct(void) const; + bool IsComplexStruct(void) const; + bool HasConstructor(void) const; + bool IsPointerType(void) const; + bool IsValueType(void) const; + bool IsStructOrUnion(void) const; + + void SetDefined(void); + + Declaration* ToConstType(void); + Declaration* ToMutableType(void); + Declaration* ToVolatileType(void); + Declaration* ToAlternateThis(Declaration* pthis, int nthis = 1); + + Declaration* ToStriped(int stripe); + Declaration* ToStriped(Errors* errors); + Declaration* Clone(void); + Declaration* Last(void); + + Declaration* BuildPointer(const Location& loc); + Declaration* BuildReference(const Location& loc, DecType type = DT_TYPE_REFERENCE); + Declaration* BuildConstPointer(const Location& loc); + Declaration* BuildConstReference(const Location& loc, DecType type = DT_TYPE_REFERENCE); + Declaration* BuildRValueRef(const Location& loc); + Declaration* BuildConstRValueRef(const Location& loc); + Declaration* NonRefBase(void); + Declaration* BuildArrayPointer(void); + Declaration* BuildAddressOfPointer(void); + Declaration* DeduceAuto(Declaration* dec); + Declaration* DeduceAutoInit(Declaration* dec); + Declaration* ConstCast(Declaration* ntype); + bool IsNullConst(void) const; + bool IsAuto(void) const; + bool HasConstMember(void) const; + + DecType ValueType(void) const; + + bool CanResolveTemplate(Expression* pexp, Declaration* tdec); + bool ResolveTemplate(Declaration* fdec, Declaration * tdec, bool same, bool preliminary); + bool ResolveTemplate(Expression* pexp, Declaration* tdec); + bool ResolveTemplateParameterList(Expression* pexp, Declaration* pdec, bool preliminary); + + Declaration* ExpandTemplate(DeclarationScope* scope); + + const Ident* MangleIdent(void); + const Ident* FullIdent(void); + + int Stride(void) const; + int Alignment(void) const; +}; + +void InitDeclarations(void); + +extern Declaration* TheVoidTypeDeclaration, * TheConstVoidTypeDeclaration, * TheSignedIntTypeDeclaration, * TheUnsignedIntTypeDeclaration, * TheConstCharTypeDeclaration, * TheCharTypeDeclaration, * TheSignedCharTypeDeclaration, * TheUnsignedCharTypeDeclaration; +extern Declaration* TheBoolTypeDeclaration, * TheFloatTypeDeclaration, * TheVoidPointerTypeDeclaration, * TheConstVoidPointerTypeDeclaration, * TheSignedLongTypeDeclaration, * TheUnsignedLongTypeDeclaration; +extern Declaration* TheVoidFunctionTypeDeclaration, * TheConstVoidValueDeclaration; +extern Declaration* TheCharPointerTypeDeclaration, * TheConstCharPointerTypeDeclaration; +extern Declaration* TheNullptrConstDeclaration, * TheZeroIntegerConstDeclaration, * TheZeroFloatConstDeclaration, * TheNullPointerTypeDeclaration; +extern Declaration* TheTrueConstDeclaration, * TheFalseConstDeclaration; +extern Expression* TheVoidExpression; + diff --git a/oscar64/oscar64/Disassembler.cpp b/oscar64/oscar64/Disassembler.cpp new file mode 100644 index 0000000..ff5cd8d --- /dev/null +++ b/oscar64/oscar64/Disassembler.cpp @@ -0,0 +1,973 @@ +#include "Disassembler.h" +#include "ByteCodeGenerator.h" +#include "Assembler.h" +#include "InterCode.h" +#include "Linker.h" +#include "Declaration.h" + +ByteCodeDisassembler::ByteCodeDisassembler(void) +{ + +} + +ByteCodeDisassembler::~ByteCodeDisassembler(void) +{ + +} + +const char* ByteCodeDisassembler::TempName(uint8 tmp, char* buffer, InterCodeProcedure* proc, Linker* linker) +{ + if (tmp == BC_REG_ADDR) + return "ADDR"; + else if (tmp == BC_REG_ACCU) + return "ACCU"; + else if (tmp >= BC_REG_FPARAMS && tmp < BC_REG_FPARAMS_END) + { + sprintf_s(buffer, 10, "P%d", tmp - BC_REG_FPARAMS); + return buffer; + } + else if (proc && tmp >= BC_REG_TMP && tmp < BC_REG_TMP + proc->mTempSize) + { + int i = 0; + while (i < proc->mTempOffset.Size() && proc->mTempOffset[i] != tmp - BC_REG_TMP) + i++; + if (i < proc->mTempOffset.Size()) + sprintf_s(buffer, 10, "T%d", i); + else + sprintf_s(buffer, 10, "$%02x", tmp); + return buffer; + } + else if (linker) + { + LinkerObject* obj = linker->FindObjectByAddr(tmp); + if (obj && obj->mIdent) + sprintf_s(buffer, 40, "$%02x; %s + %d", tmp, obj->mIdent->mString, tmp - obj->mAddress); + else + sprintf_s(buffer, 10, "$%02x", tmp); + return buffer; + } + else + { + sprintf_s(buffer, 10, "$%02x", tmp); + return buffer; + } +} + +const char* ByteCodeDisassembler::AddrName(int addr, char* buffer, Linker* linker) +{ + if (linker) + { + LinkerObject* obj = linker->FindObjectByAddr(addr); + if (obj && obj->mIdent) + { + sprintf_s(buffer, 160, "%s + %d", obj->mIdent->mString, addr - obj->mAddress); + return buffer; + } + } + + sprintf_s(buffer, 10, "$%04x", addr); + return buffer; +} + +void ByteCodeDisassembler::Disassemble(FILE* file, const uint8* memory, int bank, int start, int size, InterCodeProcedure* proc, const Ident* ident, Linker* linker) +{ + fprintf(file, "--------------------------------------------------------------------\n"); + if (proc && proc->mIdent) + fprintf(file, "%s:\n", proc->mIdent->mString); + else if (ident) + fprintf(file, "%s:\n", ident->mString); + + char tbuffer[160], abuffer[160]; +#if 0 + for (int i = 0; i < proc->mTemporaries.Size(); i++) + printf("T%d = $%.2x\n", i, BC_REG_TMP + proc->mTempOffset[i]); +#endif + int i = 0; + while (i < size) + { + ByteCode bc = ByteCode(memory[start + i] / 2); + + if (bank >= 0) + fprintf(file, "%02x:", bank); + + fprintf(file, "%04x:\t", start + i); + i++; + + switch (bc) + { + case BC_NOP: + fprintf(file, "NOP"); + break; + case BC_EXIT: + fprintf(file, "EXIT"); + break; + + case BC_CONST_8: + fprintf(file, "MOVB\t%s, #%d", TempName(memory[start + i], tbuffer, proc, linker), memory[start + i + 1]); + i += 2; + break; + case BC_CONST_P8: + fprintf(file, "MOV\t%s, #%d", TempName(memory[start + i], tbuffer, proc, linker), memory[start + i + 1]); + i += 2; + break; + case BC_CONST_N8: + fprintf(file, "MOV\t%s, #%d", TempName(memory[start + i], tbuffer, proc, linker), int(memory[start + i + 1]) - 0x100); + i += 2; + break; + case BC_CONST_16: + fprintf(file, "MOV\t%s, #$%04x", TempName(memory[start + i], tbuffer, proc, linker), uint16(memory[start + i + 1] + 256 * memory[start + i + 2])); + i += 3; + break; + case BC_CONST_32: + fprintf(file, "MOVD\t%s, #$%08x", TempName(memory[start + i], tbuffer, proc, linker), uint32(memory[start + i + 1] + 256 * memory[start + i + 2] + 0x10000 * memory[start + i + 3] + 0x1000000 * memory[start + i + 4])); + i += 5; + break; + + case BC_LOAD_REG_8: + fprintf(file, "MOVB\tACCU, %s", TempName(memory[start + i], tbuffer, proc, linker)); + i += 1; + break; + case BC_STORE_REG_8: + fprintf(file, "MOVB\t%s, ACCU", TempName(memory[start + i], tbuffer, proc, linker)); + i += 1; + break; + case BC_LOAD_REG_16: + fprintf(file, "MOV\tACCU, %s", TempName(memory[start + i], tbuffer, proc, linker)); + i += 1; + break; + case BC_STORE_REG_16: + fprintf(file, "MOV\t%s, ACCU", TempName(memory[start + i], tbuffer, proc, linker)); + i += 1; + break; + case BC_LOAD_REG_32: + fprintf(file, "MOVD\tACCU, %s", TempName(memory[start + i], tbuffer, proc, linker)); + i += 1; + break; + case BC_STORE_REG_32: + fprintf(file, "MOVD\t%s, ACCU", TempName(memory[start + i], tbuffer, proc, linker)); + i += 1; + break; + case BC_ADDR_REG: + fprintf(file, "MOV\tADDR, %s", TempName(memory[start + i], tbuffer, proc, linker)); + i += 1; + break; + + case BC_LOAD_ABS_8: + fprintf(file, "MOVB\t%s, %s", TempName(memory[start + i + 2], tbuffer, proc, linker), AddrName(uint16(memory[start + i + 0] + 256 * memory[start + i + 1]), abuffer, linker)); + i += 3; + break; + case BC_LOAD_ABS_U8: + fprintf(file, "MOVUB\t%s, %s", TempName(memory[start + i + 2], tbuffer, proc, linker), AddrName(uint16(memory[start + i + 0] + 256 * memory[start + i + 1]), abuffer, linker)); + i += 3; + break; + case BC_LOAD_ABS_16: + fprintf(file, "MOV\t%s, %s", TempName(memory[start + i + 2], tbuffer, proc, linker), AddrName(uint16(memory[start + i + 0] + 256 * memory[start + i + 1]), abuffer, linker)); + i += 3; + break; + case BC_LOAD_ABS_32: + fprintf(file, "MOVD\t%s, %s", TempName(memory[start + i + 2], tbuffer, proc, linker), AddrName(uint16(memory[start + i + 0] + 256 * memory[start + i + 1]), abuffer, linker)); + i += 3; + break; + case BC_LOAD_ABS_ADDR: + fprintf(file, "MOV\tADDR, %s", AddrName(uint16(memory[start + i + 0] + 256 * memory[start + i + 1]), abuffer, linker)); + i += 2; + break; + + case BC_LEA_ABS: + fprintf(file, "LEA\t%s, %s", TempName(memory[start + i + 0], tbuffer, proc, linker), AddrName(uint16(memory[start + i + 1] + 256 * memory[start + i + 2]), abuffer, linker)); + i += 3; + break; + + case BC_LEA_ABS_INDEX: + fprintf(file, "LEAX\tADDR, %s + %s", AddrName(uint16(memory[start + i + 1] + 256 * memory[start + i + 2]), abuffer, linker), TempName(memory[start + i + 0], tbuffer, proc, linker)); + i += 3; + break; + + case BC_LEA_ACCU_INDEX: + fprintf(file, "LEAX\tADDR, %s + ACCU", TempName(memory[start + i + 0], tbuffer, proc, linker)); + i += 1; + break; + + case BC_LEA_ABS_INDEX_U8: + fprintf(file, "LEAXB\tADDR, %s + %s", AddrName(uint16(memory[start + i + 1] + 256 * memory[start + i + 2]), abuffer, linker), TempName(memory[start + i + 0], tbuffer, proc, linker)); + i += 3; + break; + + case BC_STORE_ABS_8: + fprintf(file, "MOVB\t%s, %s", AddrName(uint16(memory[start + i + 0] + 256 * memory[start + i + 1]), abuffer, linker), TempName(memory[start + i + 2], tbuffer, proc, linker)); + i += 3; + break; + case BC_STORE_ABS_16: + fprintf(file, "MOV\t%s, %s", AddrName(uint16(memory[start + i + 0] + 256 * memory[start + i + 1]), abuffer, linker), TempName(memory[start + i + 2], tbuffer, proc, linker)); + i += 3; + break; + case BC_STORE_ABS_32: + fprintf(file, "MOVD\t%s, %s", AddrName(uint16(memory[start + i + 0] + 256 * memory[start + i + 1]), abuffer, linker), TempName(memory[start + i + 2], tbuffer, proc, linker)); + i += 3; + break; + + case BC_LOAD_LOCAL_8: + fprintf(file, "MOVB\t%s, %d(FP)", TempName(memory[start + i + 0], tbuffer, proc, linker), memory[start + i + 1]); + i += 2; + break; + case BC_LOAD_LOCAL_U8: + fprintf(file, "MOVUB\t%s, %d(FP)", TempName(memory[start + i + 0], tbuffer, proc, linker), memory[start + i + 1]); + i += 2; + break; + case BC_LOAD_LOCAL_16: + fprintf(file, "MOV\t%s, %d(FP)", TempName(memory[start + i + 0], tbuffer, proc, linker), memory[start + i + 1]); + i += 2; + break; + case BC_LOAD_LOCAL_32: + fprintf(file, "MOVD\t%s, %d(FP)", TempName(memory[start + i + 0], tbuffer, proc, linker), memory[start + i + 1]); + i += 2; + break; + + case BC_STORE_LOCAL_8: + fprintf(file, "MOVB\t%d(FP), %s", memory[start + i + 1], TempName(memory[start + i + 0], tbuffer, proc, linker)); + i += 2; + break; + case BC_STORE_LOCAL_16: + fprintf(file, "MOV\t%d(FP), %s", memory[start + i + 1], TempName(memory[start + i + 0], tbuffer, proc, linker)); + i += 2; + break; + case BC_STORE_LOCAL_32: + fprintf(file, "MOVD\t%d(FP), %s", memory[start + i + 1], TempName(memory[start + i + 0], tbuffer, proc, linker)); + i += 2; + break; + + case BC_LEA_LOCAL: + fprintf(file, "LEA\t%s, %d(FP)", TempName(memory[start + i + 0], tbuffer, proc, linker), uint16(memory[start + i + 1] + 256 * memory[start + i + 2])); + i += 3; + break; + + case BC_LEA_FRAME: + fprintf(file, "LEA\t%s, %d(SP)", TempName(memory[start + i + 0], tbuffer, proc, linker), uint16(memory[start + i + 1] + 256 * memory[start + i + 2])); + i += 3; + break; + + case BC_STORE_FRAME_8: + fprintf(file, "MOVB\t%d(SP), %s", memory[start + i + 1], TempName(memory[start + i + 0], tbuffer, proc, linker)); + i += 2; + break; + case BC_STORE_FRAME_16: + fprintf(file, "MOV\t%d(SP), %s", memory[start + i + 1], TempName(memory[start + i + 0], tbuffer, proc, linker)); + i += 2; + break; + case BC_STORE_FRAME_32: + fprintf(file, "MOVD\t%d(SP), %s", memory[start + i + 1], TempName(memory[start + i + 0], tbuffer, proc, linker)); + i += 2; + break; + + case BC_BINOP_ADDR_16: + fprintf(file, "ADD\tACCU, %s", TempName(memory[start + i + 0], tbuffer, proc, linker)); + i += 1; + break; + case BC_BINOP_ADDA_16: + fprintf(file, "ADD\t%s, ACCU", TempName(memory[start + i + 0], tbuffer, proc, linker)); + i += 1; + break; + case BC_BINOP_SUBR_16: + fprintf(file, "SUB\tACCU, %s", TempName(memory[start + i + 0], tbuffer, proc, linker)); + i += 1; + break; + case BC_BINOP_MULR_16: + fprintf(file, "MUL\tACCU, %s", TempName(memory[start + i + 0], tbuffer, proc, linker)); + i += 1; + break; + case BC_BINOP_DIVR_U16: + fprintf(file, "DIVU\tACCU, %s", TempName(memory[start + i + 0], tbuffer, proc, linker)); + i += 1; + break; + case BC_BINOP_MODR_U16: + fprintf(file, "MODU\tACCU, %s", TempName(memory[start + i + 0], tbuffer, proc, linker)); + i += 1; + break; + case BC_BINOP_DIVR_I16: + fprintf(file, "DIVS\tACCU, %s", TempName(memory[start + i + 0], tbuffer, proc, linker)); + i += 1; + break; + case BC_BINOP_MODR_I16: + fprintf(file, "MODS\tACCU, %s", TempName(memory[start + i + 0], tbuffer, proc, linker)); + i += 1; + break; + case BC_BINOP_ANDR_16: + fprintf(file, "AND\tACCU, %s", TempName(memory[start + i + 0], tbuffer, proc, linker)); + i += 1; + break; + case BC_BINOP_ORR_16: + fprintf(file, "OR\tACCU, %s", TempName(memory[start + i + 0], tbuffer, proc, linker)); + i += 1; + break; + case BC_BINOP_XORR_16: + fprintf(file, "XOR\tACCU, %s", TempName(memory[start + i + 0], tbuffer, proc, linker)); + i += 1; + break; + case BC_BINOP_SHLR_16: + fprintf(file, "SHL\tACCU, %s", TempName(memory[start + i + 0], tbuffer, proc, linker)); + i += 1; + break; + case BC_BINOP_SHRR_I16: + fprintf(file, "SHRU\tACCU, %s", TempName(memory[start + i + 0], tbuffer, proc, linker)); + i += 1; + break; + case BC_BINOP_SHRR_U16: + fprintf(file, "SHRI\tACCU, %s", TempName(memory[start + i + 0], tbuffer, proc, linker)); + i += 1; + break; + + case BC_BINOP_ADDI_16: + fprintf(file, "ADD\t%s, #$%04X", TempName(memory[start + i + 0], tbuffer, proc, linker), uint16(memory[start + i + 1] + 256 * memory[start + i + 2])); + i += 3; + break; + case BC_BINOP_SUBI_16: + fprintf(file, "SUBR\t%s, #$%04X", TempName(memory[start + i + 0], tbuffer, proc, linker), uint16(memory[start + i + 1] + 256 * memory[start + i + 2])); + i += 3; + break; + case BC_BINOP_ANDI_16: + fprintf(file, "AND\t%s, #$%04X", TempName(memory[start + i + 0], tbuffer, proc, linker), uint16(memory[start + i + 1] + 256 * memory[start + i + 2])); + i += 3; + break; + case BC_BINOP_ORI_16: + fprintf(file, "OR\t%s, #$%04X", TempName(memory[start + i + 0], tbuffer, proc, linker), uint16(memory[start + i + 1] + 256 * memory[start + i + 2])); + i += 3; + break; + case BC_BINOP_MULI8_16: + fprintf(file, "MUL\t%s, #%d", TempName(memory[start + i + 0], tbuffer, proc, linker), memory[start + i + 1]); + i += 2; + break; + + case BC_BINOP_ADDI_8: + fprintf(file, "ADDB\t%s, #$%04X", TempName(memory[start + i + 0], tbuffer, proc, linker), memory[start + i + 1]); + i += 2; + break; + case BC_BINOP_ANDI_8: + fprintf(file, "ANDB\t%s, #$%04X", TempName(memory[start + i + 0], tbuffer, proc, linker), memory[start + i + 1]); + i += 2; + break; + case BC_BINOP_ORI_8: + fprintf(file, "ORB\t%s, #$%04X", TempName(memory[start + i + 0], tbuffer, proc, linker), memory[start + i + 1]); + i += 2; + break; + + case BC_LOOP_U8: + fprintf(file, "LOOPB\t%s, #$%02X", TempName(memory[start + i + 0], tbuffer, proc, linker), memory[start + i + 1]); + i += 2; + break; + + case BC_CONV_I8_I16: + fprintf(file, "SEXT8\t%s", TempName(memory[start + i + 0], tbuffer, proc, linker)); + i++; + break; + + case BC_BINOP_SHLI_16: + fprintf(file, "SHL\tACCU, #%d", uint8(memory[start + i + 0])); + i += 1; + break; + case BC_BINOP_SHRI_U16: + fprintf(file, "SHRU\tACCU, #%d", uint8(memory[start + i + 0])); + i += 1; + break; + case BC_BINOP_SHRI_I16: + fprintf(file, "SHRS\tACCU, #%d", uint8(memory[start + i + 0])); + i += 1; + break; + + case BC_BINOP_CMPUR_16: + fprintf(file, "CMPU\tACCU, %s", TempName(memory[start + i + 0], tbuffer, proc, linker)); + i += 1; + break; + case BC_BINOP_CMPSR_16: + fprintf(file, "CMPS\tACCU, %s", TempName(memory[start + i + 0], tbuffer, proc, linker)); + i += 1; + break; + + case BC_BINOP_CMPUI_16: + fprintf(file, "CMPU\tACCU, #$%04X", uint16(memory[start + i + 0] + 256 * memory[start + i + 1])); + i += 2; + break; + case BC_BINOP_CMPSI_16: + fprintf(file, "CMPS\tACCU, #$%04X", int16(memory[start + i + 0] + 256 * memory[start + i + 1])); + i += 2; + break; + + case BC_BINOP_CMPUR_8: + fprintf(file, "CMPUB\tACCU, %s", TempName(memory[start + i + 0], tbuffer, proc, linker)); + i += 1; + break; + case BC_BINOP_CMPSR_8: + fprintf(file, "CMPSB\tACCU, %s", TempName(memory[start + i + 0], tbuffer, proc, linker)); + i += 1; + break; + + case BC_BINOP_CMPUI_8: + fprintf(file, "CMPUB\tACCU, #$%04X", uint8(memory[start + i + 0] + 256 * memory[start + i + 1])); + i += 1; + break; + case BC_BINOP_CMPSI_8: + fprintf(file, "CMPSB\tACCU, #$%04X", int8(memory[start + i + 0])); + i += 1; + break; + + case BC_BINOP_ADD_F32: + fprintf(file, "ADDF\tACCU, %s", TempName(memory[start + i + 0], tbuffer, proc, linker)); + i += 1; + break; + case BC_BINOP_SUB_F32: + fprintf(file, "SUBF\tACCU, %s", TempName(memory[start + i + 0], tbuffer, proc, linker)); + i += 1; + break; + case BC_BINOP_MUL_F32: + fprintf(file, "MULF\tACCU, %s", TempName(memory[start + i + 0], tbuffer, proc, linker)); + i += 1; + break; + case BC_BINOP_DIV_F32: + fprintf(file, "DIVF\tACCU, %s", TempName(memory[start + i + 0], tbuffer, proc, linker)); + i += 1; + break; + case BC_BINOP_CMP_F32: + fprintf(file, "CMPF\tACCU, %s", TempName(memory[start + i + 0], tbuffer, proc, linker)); + i += 1; + break; + + case BC_FILL: + fprintf(file, "FILL\t#%d", memory[start + i + 0]); + i++; + break; + case BC_COPY: + fprintf(file, "COPY\t#%d", memory[start + i + 0]); + i++; + break; + case BC_STRCPY: + fprintf(file, "STRCPY"); + break; + + case BC_COPY_LONG: + fprintf(file, "COPYL\t#%d", uint16(memory[start + i + 0] + 256 * memory[start + i + 1])); + i += 2; + break; + case BC_FILL_LONG: + fprintf(file, "FILLL\t#%d", uint16(memory[start + i + 0] + 256 * memory[start + i + 1])); + i += 2; + break; + + case BC_OP_NEGATE_16: + fprintf(file, "NEG\tACCU"); + break; + case BC_OP_INVERT_16: + fprintf(file, "NOT\tACCU"); + break; + + case BC_OP_NEGATE_F32: + fprintf(file, "NEGF\tACCU"); + break; + case BC_OP_ABS_F32: + fprintf(file, "ABSF\tACCU"); + break; + case BC_OP_FLOOR_F32: + fprintf(file, "FLOORF\tACCU"); + break; + case BC_OP_CEIL_F32: + fprintf(file, "CEILF\tACCU"); + break; + + + case BC_CONV_U16_F32: + fprintf(file, "CNVUF\tACCU"); + break; + case BC_CONV_I16_F32: + fprintf(file, "CNVSF\tACCU"); + break; + case BC_CONV_F32_U16: + fprintf(file, "CNVFU\tACCU"); + break; + case BC_CONV_F32_I16: + fprintf(file, "CNVFS\tACCU"); + break; + + case BC_CONV_U32_F32: + fprintf(file, "CNVLUF\tACCU"); + break; + case BC_CONV_I32_F32: + fprintf(file, "CNVLSF\tACCU"); + break; + case BC_CONV_F32_U32: + fprintf(file, "CNVFLU\tACCU"); + break; + case BC_CONV_F32_I32: + fprintf(file, "CNVFLS\tACCU"); + break; + + case BC_MALLOC: + fprintf(file, "MALLOC\tACCU"); + break; + case BC_FREE: + fprintf(file, "FREE\tACCU"); + break; + + case BC_JUMPS: + fprintf(file, "JUMP\t$%04X", start + i + 1 + int8(memory[start + i + 0])); + i++; + break; + case BC_BRANCHS_EQ: + fprintf(file, "BEQ\t$%04X", start + i + 1 + int8(memory[start + i + 0])); + i++; + break; + case BC_BRANCHS_NE: + fprintf(file, "BNE\t$%04X", start + i + 1 + int8(memory[start + i + 0])); + i++; + break; + case BC_BRANCHS_GT: + fprintf(file, "BGT\t$%04X", start + i + 1 + int8(memory[start + i + 0])); + i++; + break; + case BC_BRANCHS_GE: + fprintf(file, "BGE\t$%04X", start + i + 1 + int8(memory[start + i + 0])); + i++; + break; + case BC_BRANCHS_LT: + fprintf(file, "BLT\t$%04X", start + i + 1 + int8(memory[start + i + 0])); + i++; + break; + case BC_BRANCHS_LE: + fprintf(file, "BLE\t$%04X", start + i + 1 + int8(memory[start + i + 0])); + i++; + break; + + case BC_JUMPF: + fprintf(file, "JUMPF\t$%04X", start + i + 2 + int16(memory[start + i + 0] + 256 * memory[start + i + 1])); + i += 2; + break; + case BC_BRANCHF_EQ: + fprintf(file, "BEQF\t$%04X", start + i + 2 + int16(memory[start + i + 0] + 256 * memory[start + i + 1])); + i += 2; + break; + case BC_BRANCHF_NE: + fprintf(file, "BNEF\t$%04X", start + i + 2 + int16(memory[start + i + 0] + 256 * memory[start + i + 1])); + i += 2; + break; + case BC_BRANCHF_GT: + fprintf(file, "BGTF\t$%04X", start + i + 2 + int16(memory[start + i + 0] + 256 * memory[start + i + 1])); + i += 2; + break; + case BC_BRANCHF_GE: + fprintf(file, "BGEF\t$%04X", start + i + 2 + int16(memory[start + i + 0] + 256 * memory[start + i + 1])); + i += 2; + break; + case BC_BRANCHF_LT: + fprintf(file, "BLTF\t$%04X", start + i + 2 + int16(memory[start + i + 0] + 256 * memory[start + i + 1])); + i += 2; + break; + case BC_BRANCHF_LE: + fprintf(file, "BLEF\t$%04X", start + i + 2 + int16(memory[start + i + 0] + 256 * memory[start + i + 1])); + i += 2; + break; + + case BC_ENTER: + fprintf(file, "ENTER\t%d, %d", memory[start + i + 2], uint16(memory[start + i + 0] + 256 * memory[start + i + 1])); + i += 3; + break; + case BC_RETURN: + fprintf(file, "RETURN\t%d, %d", memory[start + i], uint16(memory[start + i + 1] + 256 * memory[start + i + 2])); + i += 3; + break; + case BC_CALL_ADDR: + fprintf(file, "CALL\tADDR"); + break; + case BC_CALL_ABS: + fprintf(file, "CALL\t%s", AddrName(uint16(memory[start + i + 0] + 256 * memory[start + i + 1]), abuffer, linker)); + i += 2; + break; + case BC_JSR: + fprintf(file, "JSR\t%s", AddrName(uint16(memory[start + i + 0] + 256 * memory[start + i + 1]), abuffer, linker)); + i += 2; + break; + + case BC_PUSH_FRAME: + fprintf(file, "PUSH\t#$%04X", uint16(memory[start + i + 0] + 256 * memory[start + i + 1])); + i += 2; + break; + + case BC_POP_FRAME: + fprintf(file, "POP\t#$%04X", uint16(memory[start + i + 0] + 256 * memory[start + i + 1])); + i += 2; + break; + + case BC_LOAD_ADDR_8: + fprintf(file, "MOVB\t%s, %d(ADDR)", TempName(memory[start + i + 0], tbuffer, proc, linker), memory[start + i + 1]); + i += 2; + break; + case BC_LOAD_ADDR_U8: + fprintf(file, "MOVUB\t%s, %d(ADDR)", TempName(memory[start + i + 0], tbuffer, proc, linker), memory[start + i + 1]); + i += 2; + break; + case BC_LOAD_ADDR_16: + fprintf(file, "MOV\t%s, %d(ADDR)", TempName(memory[start + i + 0], tbuffer, proc, linker), memory[start + i + 1]); + i += 2; + break; + case BC_LOAD_ADDR_32: + fprintf(file, "MOVD\t%s, %d(ADDR)", TempName(memory[start + i + 0], tbuffer, proc, linker), memory[start + i + 1]); + i += 2; + break; + + case BC_STORE_ADDR_8: + fprintf(file, "MOVB\t%d(ADDR), %s", memory[start + i + 1], TempName(memory[start + i + 0], tbuffer, proc, linker)); + i += 2; + break; + case BC_STORE_ADDR_16: + fprintf(file, "MOV\t%d(ADDR), %s", memory[start + i + 1], TempName(memory[start + i + 0], tbuffer, proc, linker)); + i += 2; + break; + case BC_STORE_ADDR_32: + fprintf(file, "MOVD\t%d(ADDR), %s", memory[start + i + 1], TempName(memory[start + i + 0], tbuffer, proc, linker)); + i += 2; + break; + + case BC_EXTRT: + fprintf(file, "EXTRT\t%s, %s", TempName(memory[start + i + 2], tbuffer, proc, linker), AddrName(uint16(memory[start + i + 0] + 256 * memory[start + i + 1]), abuffer, linker)); + i += 3; + break; + } + + fprintf(file, "\n"); + } +} + + +NativeCodeDisassembler::NativeCodeDisassembler(void) +{ + +} + +NativeCodeDisassembler::~NativeCodeDisassembler(void) +{ + +} + +void NativeCodeDisassembler::DumpMemory(FILE* file, const uint8* memory, int bank, int start, int size, InterCodeProcedure* proc, const Ident* ident, Linker* linker, LinkerObject * lobj) +{ + fprintf(file, "--------------------------------------------------------------------\n"); + if (proc && proc->mIdent) + fprintf(file, "%s:\n", proc->mIdent->mString); + else if (ident) + fprintf(file, "%s:\n", ident->mString); + + if (lobj->mSection->mType == LST_BSS) + { + if (bank >= 0) + fprintf(file, "%02x:", bank); + + fprintf(file, "%04x : __ __ __ BSS\t%d\n", start, size); + } + else + { + int ip = start; + while (ip < start + size) + { + int n = 16; + if (ip + n > start + size) + n = start + size - ip; + + if (bank >= 0) + fprintf(file, "%02x:", bank); + + fprintf(file, "%04x : __ __ __ BYT", ip); + + for (int i = 0; i < n; i++) + fprintf(file, " %02x", memory[ip + i]); + for (int i = n; i < 16; i++) + fprintf(file, " "); + fprintf(file, " : "); + for (int i = 0; i < n; i++) + { + int k = memory[ip + i]; + if (k >= 32 && k < 127) + fprintf(file, "%c", k); + else + fprintf(file, "."); + } + fprintf(file, "\n"); + + ip += n; + } + } +} + +void NativeCodeDisassembler::Disassemble(FILE* file, const uint8* memory, int bank, int start, int size, InterCodeProcedure* proc, const Ident * ident, Linker* linker, const Ident * fident) +{ + fprintf(file, "--------------------------------------------------------------------\n"); + + if (proc && proc->mIdent) + fprintf(file, "%s: ; %s\n", proc->mIdent->mString, fident->mString); + else if (ident) + fprintf(file, "%s: ; %s\n", ident->mString, fident->mString); + + if (proc) + fprintf(file, ";%4d, \"%s\"\n", proc->mLocation.mLine, proc->mLocation.mFileName); + + char tbuffer[160], abuffer[160]; + + int ip = start; + while (ip < start + size) + { + int iip = ip; + uint8 opcode = memory[ip++]; + AsmInsData d = DecInsData[opcode]; + int addr = 0; + + if (proc && proc->mLinkerObject) + { + int i = 0; + while (i < proc->mLinkerObject->mRanges.Size() && iip - start != proc->mLinkerObject->mRanges[i].mOffset) + i++; + if (i < proc->mLinkerObject->mRanges.Size()) + fprintf(file, ".%s:\n", proc->mLinkerObject->mRanges[i].mIdent->mString); + + i = 0; + while (i < proc->mLinkerObject->mCodeOrigins.Size() && iip - start != proc->mLinkerObject->mCodeOrigins[i].mStart) + i++; + if (i < proc->mLinkerObject->mCodeOrigins.Size()) + fprintf(file, ";%4d, \"%s\"\n", proc->mLinkerObject->mCodeOrigins[i].mLocation.mLine, proc->mLinkerObject->mCodeOrigins[i].mLocation.mFileName); + } + + if (bank >= 0) + fprintf(file, "%02x:", bank); + + switch (d.mMode) + { + case ASMIM_IMPLIED: + fprintf(file, "%04x : %02x __ __ %s\n", iip, memory[iip], AsmInstructionNames[d.mType]); + break; + case ASMIM_IMMEDIATE: + addr = memory[ip++]; + fprintf(file, "%04x : %02x %02x __ %s #$%02x\n", iip, memory[iip], memory[iip + 1], AsmInstructionNames[d.mType], addr); + break; + case ASMIM_ZERO_PAGE: + addr = memory[ip++]; + fprintf(file, "%04x : %02x %02x __ %s %s %s\n", iip, memory[iip], memory[iip + 1], AsmInstructionNames[d.mType], TempName(addr, tbuffer, proc, linker), AddrName(bank, addr, abuffer, proc, linker)); + break; + case ASMIM_ZERO_PAGE_X: + addr = memory[ip++]; + fprintf(file, "%04x : %02x %02x __ %s %s,x %s\n", iip, memory[iip], memory[iip + 1], AsmInstructionNames[d.mType], TempName(addr, tbuffer, proc, linker), AddrName(bank, addr, abuffer, proc, linker)); + break; + case ASMIM_ZERO_PAGE_Y: + addr = memory[ip++]; + fprintf(file, "%04x : %02x %02x __ %s %s,y %s\n", iip, memory[iip], memory[iip + 1], AsmInstructionNames[d.mType], TempName(addr, tbuffer, proc, linker), AddrName(bank, addr, abuffer, proc, linker)); + break; + case ASMIM_ABSOLUTE: + addr = memory[ip] + 256 * memory[ip + 1]; + fprintf(file, "%04x : %02x %02x %02x %s $%04x %s\n", iip, memory[iip], memory[iip + 1], memory[iip + 2], AsmInstructionNames[d.mType], addr, AddrName(bank, addr, abuffer, proc, linker)); + ip += 2; + break; + case ASMIM_ABSOLUTE_X: + addr = memory[ip] + 256 * memory[ip + 1]; + fprintf(file, "%04x : %02x %02x %02x %s $%04x,x %s\n", iip, memory[iip], memory[iip + 1], memory[iip + 2], AsmInstructionNames[d.mType], addr, AddrName(bank, addr, abuffer, proc, linker)); + ip += 2; + break; + case ASMIM_ABSOLUTE_Y: + addr = memory[ip] + 256 * memory[ip + 1]; + fprintf(file, "%04x : %02x %02x %02x %s $%04x,y %s\n", iip, memory[iip], memory[iip + 1], memory[iip + 2], AsmInstructionNames[d.mType], addr, AddrName(bank, addr, abuffer, proc, linker)); + ip += 2; + break; + case ASMIM_INDIRECT: + addr = memory[ip] + 256 * memory[ip + 1]; + ip += 2; + fprintf(file, "%04x : %02x %02x %02x %s ($%04x)\n", iip, memory[iip], memory[iip + 1], memory[iip + 2], AsmInstructionNames[d.mType], addr); + break; + case ASMIM_INDIRECT_X: + addr = memory[ip++]; + fprintf(file, "%04x : %02x %02x __ %s (%s,x) %s\n", iip, memory[iip], memory[iip + 1], AsmInstructionNames[d.mType], TempName(addr, tbuffer, proc, linker), AddrName(bank, addr, abuffer, proc, linker)); + break; + case ASMIM_INDIRECT_Y: + addr = memory[ip++]; + fprintf(file, "%04x : %02x %02x __ %s (%s),y %s\n", iip, memory[iip], memory[iip + 1], AsmInstructionNames[d.mType], TempName(addr, tbuffer, proc, linker), AddrName(bank, addr, abuffer, proc, linker)); + break; + case ASMIM_RELATIVE: + addr = memory[ip++]; + if (addr & 0x80) + addr = addr + ip - 256; + else + addr = addr + ip; + fprintf(file, "%04x : %02x %02x __ %s $%04x %s\n", iip, memory[iip], memory[iip + 1], AsmInstructionNames[d.mType], addr, AddrName(bank, addr, abuffer, proc, linker)); + break; + } + } + +} + +const char* NativeCodeDisassembler::AddrName(int bank, int addr, char* buffer, InterCodeProcedure* proc, Linker* linker) +{ + if (linker) + { + LinkerObject* obj; + + if (proc && proc->mLinkerObject && addr < 256) + { + obj = proc->mLinkerObject; + + int i = 0; + while (i < obj->mZeroPageRanges.Size() && !(addr >= obj->mZeroPageRanges[i].mOffset && addr < obj->mZeroPageRanges[i].mOffset + obj->mZeroPageRanges[i].mSize)) + i++; + + if (i < obj->mZeroPageRanges.Size()) + { + sprintf_s(buffer, 160, "; (%s + %d)", obj->mZeroPageRanges[i].mIdent->mString, addr - obj->mZeroPageRanges[i].mOffset); + return buffer; + + } + } + + if (proc && proc->mLinkerObject && addr >= proc->mLinkerObject->mAddress && addr < proc->mLinkerObject->mAddress + proc->mLinkerObject->mSize) + obj = proc->mLinkerObject; + else + obj = linker->FindObjectByAddr(bank, addr, proc); + + if (obj && obj->mIdent) + { + if (obj->mVariable && obj->mVariable->mDeclaration) + { + int offset = addr - obj->mAddress; + char vname[160]; + strcpy_s(vname, obj->mVariable->mIdent->mString); + const Declaration* dt = obj->mVariable->mDeclaration->mBase; + + for (;;) + { + if (dt->mType == DT_TYPE_ARRAY) + { + int index; + + if (dt->mBase->mStripe) + { + index = offset % dt->mBase->mStripe; + offset -= index; + } + else + { + index = offset / dt->mBase->mSize; + offset -= index * dt->mBase->mSize; + } + char vindex[10]; + sprintf_s(vindex, "[%d]", index); + strcat_s(vname, vindex); + dt = dt->mBase; + } + else if (dt->mType == DT_TYPE_STRUCT) + { + if (dt->mStripe) + { + const Declaration* de = dt->mParams; + while (de && (de->mOffset > offset || de->mOffset + de->mSize * dt->mStripe <= offset)) + de = de->mNext; + if (de) + { + offset -= de->mOffset; + offset /= dt->mStripe; + strcat_s(vname, "."); + strcat_s(vname, de->mIdent->mString); + dt = de->mBase; + } + else + break; + } + else + { + const Declaration* de = dt->mParams; + while (de && (de->mOffset > offset || de->mOffset + de->mSize <= offset)) + de = de->mNext; + if (de) + { + offset -= de->mOffset; + strcat_s(vname, "."); + strcat_s(vname, de->mIdent->mString); + dt = de->mBase; + } + else + break; + } + } + else if (dt->mStripe) + { + offset /= dt->mStripe; + break; + } + else + break; + } + + sprintf_s(buffer, 160, "; (%s + %d)", vname, offset); + } + else + { + int i = 0; + while (i < obj->mRanges.Size() && (addr - obj->mAddress < obj->mRanges[i].mOffset || addr - obj->mAddress - obj->mRanges[i].mOffset >= obj->mRanges[i].mSize)) + i++; + if (i < obj->mRanges.Size() && obj->mRanges[i].mIdent) + sprintf_s(buffer, 160, "; (%s.%s + %d)", obj->mIdent->mString, obj->mRanges[i].mIdent->mString, addr - obj->mAddress - obj->mRanges[i].mOffset); + else + sprintf_s(buffer, 160, "; (%s + %d)", obj->mIdent->mString, addr - obj->mAddress); + } + return buffer; + } + } + + return ""; +} + + +const char* NativeCodeDisassembler::TempName(uint8 tmp, char* buffer, InterCodeProcedure* proc, Linker * linker) +{ + if (tmp >= BC_REG_ADDR && tmp <= BC_REG_ADDR + 3) + { + sprintf_s(buffer, 10, "ADDR + %d", tmp - BC_REG_ADDR); + return buffer; + } + else if (tmp >= BC_REG_ACCU && tmp <= BC_REG_ACCU + 3) + { + sprintf_s(buffer, 10, "ACCU + %d", tmp - BC_REG_ACCU); + return buffer; + } + else if (tmp >= BC_REG_WORK && tmp <= BC_REG_WORK + 8) + { + sprintf_s(buffer, 10, "WORK + %d", tmp - BC_REG_WORK); + return buffer; + } + else if (tmp >= BC_REG_STACK && tmp <= BC_REG_STACK + 1) + { + sprintf_s(buffer, 10, "SP + %d", tmp - BC_REG_STACK); + return buffer; + } + else if (tmp >= BC_REG_IP && tmp <= BC_REG_IP + 1) + { + sprintf_s(buffer, 10, "IP + %d", tmp - BC_REG_IP); + return buffer; + } + else if (tmp >= BC_REG_FPARAMS && tmp < BC_REG_FPARAMS_END) + { + sprintf_s(buffer, 10, "P%d", tmp - BC_REG_FPARAMS); + return buffer; + } + else if (tmp >= BC_REG_LOCALS && tmp <= BC_REG_LOCALS + 1) + { + sprintf_s(buffer, 10, "FP + %d", tmp - BC_REG_LOCALS); + return buffer; + } + else if (proc && tmp >= BC_REG_TMP && tmp < BC_REG_TMP + proc->mTempSize) + { + int i = 0; + while (i < proc->mTempOffset.Size() && !(tmp >= proc->mTempOffset[i] + BC_REG_TMP && tmp < proc->mTempOffset[i] + proc->mTempSizes[i] + BC_REG_TMP)) + i++; + if (i < proc->mTempOffset.Size()) + sprintf_s(buffer, 10, "T%d + %d", i, tmp - (proc->mTempOffset[i] + BC_REG_TMP)); + else + sprintf_s(buffer, 10, "$%02x", tmp); + return buffer; + } + else + { + sprintf_s(buffer, 10, "$%02x", tmp); + return buffer; + } + +} + diff --git a/oscar64/oscar64/Disassembler.h b/oscar64/oscar64/Disassembler.h new file mode 100644 index 0000000..f7a1e80 --- /dev/null +++ b/oscar64/oscar64/Disassembler.h @@ -0,0 +1,37 @@ +#pragma once + +#include +#include "MachineTypes.h" +#include "Ident.h" + +class ByteCodeGenerator; +class InterCodeProcedure; +class Linker; +class LinkerObject; + +class ByteCodeDisassembler +{ +public: + ByteCodeDisassembler(void); + ~ByteCodeDisassembler(void); + + void Disassemble(FILE* file, const uint8* memory, int bank, int start, int size, InterCodeProcedure* proc, const Ident* ident, Linker * linker); +protected: + const char* TempName(uint8 tmp, char* buffer, InterCodeProcedure* proc, Linker* linker); + const char* AddrName(int addr, char* buffer, Linker* linker); +}; + +class NativeCodeDisassembler +{ +public: + NativeCodeDisassembler(void); + ~NativeCodeDisassembler(void); + + void Disassemble(FILE* file, const uint8* memory, int bank, int start, int size, InterCodeProcedure* proc, const Ident* ident, Linker* linker, const Ident* fident); + void DumpMemory(FILE* file, const uint8* memory, int bank, int start, int size, InterCodeProcedure* proc, const Ident* ident, Linker* linker, LinkerObject * lobj); +protected: + const char* TempName(uint8 tmp, char* buffer, InterCodeProcedure* proc, Linker* linker); + const char* AddrName(int bank, int addr, char* buffer, InterCodeProcedure* proc, Linker* linker); +}; + + diff --git a/oscar64/oscar64/DiskImage.cpp b/oscar64/oscar64/DiskImage.cpp new file mode 100644 index 0000000..d8d4b4c --- /dev/null +++ b/oscar64/oscar64/DiskImage.cpp @@ -0,0 +1,359 @@ +#include "DiskImage.h" + +static char SectorsPerTrack[] = { + 0, + + 21, 21, 21, 21, + 21, 21, 21, 21, + 21, 21, 21, 21, + 21, 21, 21, 21, + 21, + + 19, 19, 19, 19, + 19, 19, 19, + + 18, 18, 18, 18, + 18, 18, + + 17, 17, 17, 17, + 17, + + 0, 0, 0, 0, 0 +}; + +static char A2P(char ch) +{ + if (ch >= 'A' && ch <= 'Z' || ch >= 'a' && ch <= 'z') + return (ch ^ 0x20) & 0xdf; + else + return ch; +} + +DiskImage::DiskImage(const char* fname) +{ + mInterleave = 10; + + for (int i = 0; i < 41; i++) + for (int j = 0; j < 21; j++) + memset(mSectors[i][j], 0, 256); + + // init bam + + uint8* bam = mSectors[18][0]; + + bam[0] = 18; bam[1] = 1; bam[2] = 0x41; bam[3] = 0; + for (int i = 1; i < 36; i++) + { + uint8* dp = bam + 4 * i; + dp[0] = SectorsPerTrack[i]; + unsigned k = (1 << SectorsPerTrack[i]) - 1; + dp[3] = (k >> 16) & 255; + dp[2] = (k >> 8) & 255; + dp[1] = k & 255; + } + + for (int i = 0x90; i < 0xab; i++) + bam[i] = 0xa0; + + ptrdiff_t i = strlen(fname); + + while (i > 0 && fname[i - 1] != '/' && fname[i - 1] != '\\') + i--; + + int j = 0; + while (j < 16 && fname[i + j] && fname[i + j] != '.') + { + bam[0x90 + j] = A2P(fname[i + j]); + j++; + } + + // DIsk ID + bam[0xa2] = 'C'; + bam[0xa3] = 'N'; + + // DOS TYPE + bam[0xa5] = '2'; + bam[0xa6] = 'A'; + + MarkBAMSector(18, 0); + + uint8* dir = mSectors[18][1]; + + MarkBAMSector(18, 1); + + dir[1] = 0xff; + +} + +void DiskImage::MarkBAMSector(int track, int sector) +{ + uint8* bam = mSectors[18][0]; + + uint8* dp = bam + 4 * track; + + if (dp[1 + (sector >> 3)] & (1 << (sector & 7))) + { + dp[1 + (sector >> 3)] &= ~(1 << (sector & 7)); + dp[0]--; + } +} + +int DiskImage::AllocBAMSector(int track, int sector) +{ + uint8* bam = mSectors[18][0]; + + uint8* dp = bam + 4 * track; + + if (dp[0] > 0) + { + sector += mInterleave; + if (sector >= SectorsPerTrack[track]) + sector -= SectorsPerTrack[track]; + + while (!(dp[1 + (sector >> 3)] & (1 << (sector & 7)))) + { + sector++; + if (sector >= SectorsPerTrack[track]) + sector -= SectorsPerTrack[track]; + } + + MarkBAMSector(track, sector); + + return sector; + } + else + return -1; +} + +int DiskImage::AllocBAMTrack(int track) +{ + uint8* bam = mSectors[18][0]; + + if (track < 18) + { + while (track > 0 && bam[4 * track] == 0) + track--; + if (track != 0) + return track; + else + track = 19; + } + + while (track < 36 && bam[4 * track] == 0) + track++; + + return track; +} + +DiskImage::~DiskImage(void) +{ + +} + +bool DiskImage::WriteImage(const char* fname) +{ + FILE* file; + fopen_s(&file, fname, "wb"); + if (file) + { + for (int i = 1; i < 36; i++) + { + for(int j=0; j 0 && fname[i - 1] != '/' && fname[i - 1] != '\\') + i--; + + int j = 0; + while (j < 16 && fname[i + j] && fname[i + j] != '.') + { + dname[j] = A2P(fname[i + j]); + j++; + } + dname[j] = 0; + + if (OpenFile(dname)) + { + uint8 * buffer = new uint8[65536], * cbuffer = new uint8[65536]; + ptrdiff_t size = fread(buffer, 1, 65536, file); + int csize = 0; + + if (compressed) + { + int pos = 0; + while (pos < size) + { + int pi = 0; + while (pi < 127 && pos < size) + { + int bi = pi, bj = 0; + for (int i = 1; i < (pos < 255 ? pos : 255); i++) + { + int j = 0; + while (j < 127 && pos + j < size && buffer[pos - i + j] == buffer[pos + j]) + j++; + + if (j > bj) + { + bi = i; + bj = j; + } + } + + if (bj >= 4) + { + if (pi > 0) + { + cbuffer[csize++] = pi; + for (int i = 0; i < pi; i++) + cbuffer[csize++] = buffer[pos - pi + i]; + pi = 0; + } + + cbuffer[csize++] = 128 + bj; + cbuffer[csize++] = bi; + pos += bj; + } + else + { + pos++; + pi++; + } + } + + if (pi > 0) + { + cbuffer[csize++] = pi; + for (int i = 0; i < pi; i++) + cbuffer[csize++] = buffer[pos - pi + i]; + } + } + + cbuffer[csize++] = 0; + WriteBytes(cbuffer, csize); + } + else + WriteBytes(buffer, size); + CloseFile(); + + delete[] buffer; + delete[] cbuffer; + } + + fclose(file); + return true; + } + else + return false; +} + +int DiskImage::WriteBytes(const uint8* data, ptrdiff_t size) +{ + uint8* dp = mSectors[mTrack][mSector]; + for (ptrdiff_t i = 0; i < size; i++) + { + if (mBytes >= 256) + { + mSector = AllocBAMSector(mTrack, mSector); + if (mSector < 0) + { + mTrack = AllocBAMTrack(mTrack); + mSector = AllocBAMSector(mTrack, 0); + } + + dp[0] = mTrack; + dp[1] = mSector; + + mBytes = 2; + if (!++mDirEntry[30]) + mDirEntry[31]++; + + dp = mSectors[mTrack][mSector]; + } + + dp[1] = mBytes; + dp[mBytes] = data[i]; + mBytes++; + } + return 0; +} diff --git a/oscar64/oscar64/DiskImage.h b/oscar64/oscar64/DiskImage.h new file mode 100644 index 0000000..19ed9d0 --- /dev/null +++ b/oscar64/oscar64/DiskImage.h @@ -0,0 +1,29 @@ +#pragma once + +#include "MachineTypes.h" + +class DiskImage +{ +public: + DiskImage(const char * name); + ~DiskImage(void); + + bool WriteImage(const char* fname); + + bool OpenFile(const char* fname); + void CloseFile(void); + + int WriteBytes(const uint8* data, ptrdiff_t size); + bool WriteFile(const char* fname, bool compressed, int interleave); + +protected: + uint8 mSectors[41][21][256]; + + void MarkBAMSector(int track, int sector); + int AllocBAMSector(int track, int sector); + int AllocBAMTrack(int track); + + uint8 * mDirEntry; + int mTrack, mSector, mBytes, mInterleave; + +}; diff --git a/oscar64/oscar64/Emulator.cpp b/oscar64/oscar64/Emulator.cpp new file mode 100644 index 0000000..7f5edf2 --- /dev/null +++ b/oscar64/oscar64/Emulator.cpp @@ -0,0 +1,898 @@ +#include "Emulator.h" +#include "Linker.h" +#include + +Emulator::Emulator(Linker* linker) + : mLinker(linker) +{ + for (int i = 0; i < 0x10000; i++) + mMemory[i] = 0; + mJiffies = true; +} + + +Emulator::~Emulator(void) +{ + +} + +static const uint8 STATUS_SIGN = 0x80; +static const uint8 STATUS_OVERFLOW = 0x40; +static const uint8 STATUS_ZERO = 0x02; +static const uint8 STATUS_CARRY = 0x01; + +void Emulator::UpdateStatus(uint8 result) +{ + mRegP &= ~(STATUS_ZERO | STATUS_SIGN); + if (result == 0) mRegP |= STATUS_ZERO; + if (result & 0x80) mRegP |= STATUS_SIGN; +} + +void Emulator::UpdateStatusCarry(uint8 result, bool carry) +{ + mRegP &= ~(STATUS_ZERO | STATUS_SIGN | STATUS_CARRY); + if (result == 0) mRegP |= STATUS_ZERO; + if (result & 0x80) mRegP |= STATUS_SIGN; + if (carry) + mRegP |= STATUS_CARRY; +} + +void Emulator::DumpCycles(void) +{ + int numTops = 0; + int topIP[101], topCycles[101]; + + int totalCycles = 0; + + GrowingArray lobjs(nullptr); + GrowingArray lobjc(0); + + for (int i = 0; i < 0x10000; i++) + { + int cycles = mCycles[i]; + totalCycles += cycles; + if (cycles > 0) + { + if (mLinker) + { + LinkerObject* lobj = mLinker->FindObjectByAddr(i); + if (lobj) + { + lobjs[lobj->mID] = lobj; + lobjc[lobj->mID] += cycles; + } + } + + int j = numTops; + while (j > 0 && topCycles[j-1] < cycles) + { + topCycles[j] = topCycles[j - 1]; + topIP[j] = topIP[j - 1]; + j--; + } + topCycles[j] = cycles; + topIP[j] = i; + + if (numTops < 40) + numTops++; + } + } + + printf("Total Cycles %d\n", totalCycles); +// return; + + for (int i = 0; i < numTops; i++) + { + printf(" %2d : %04x : %d\n", i, topIP[i], topCycles[i]); + } + + numTops = 0; + for (int i = 0; i < lobjc.Size(); i++) + { + int cycles = lobjc[i]; + if (cycles > 0) + { + int j = numTops; + while (j > 0 && topCycles[j - 1] < cycles) + { + topCycles[j] = topCycles[j - 1]; + topIP[j] = topIP[j - 1]; + j--; + } + topCycles[j] = cycles; + topIP[j] = i; + + if (numTops < 40) + numTops++; + } + + } + + for (int i = 0; i < numTops; i++) + { + if (lobjs[topIP[i]]->mIdent) + printf(" %2d : %s : %d\n", i, lobjs[topIP[i]]->mIdent->mString, topCycles[i]); + } +} + +bool Emulator::EmulateInstruction(AsmInsType type, AsmInsMode mode, int addr, int & cycles, bool cross, bool indexed) +{ + int t; + + switch (type) + { + case ASMIT_ADC: + if (mode != ASMIM_IMMEDIATE) + addr = ReadMemory(addr); + t = mRegA + addr + (mRegP & STATUS_CARRY); + + mRegP = 0; + + if ((mRegA & 0x80) && (addr & 0x80) && !(t & 0x80) || + !(mRegA & 0x80) && !(addr & 0x80) && (t & 0x80)) + mRegP |= STATUS_OVERFLOW; + + mRegA = (t & 255); + UpdateStatusCarry(mRegA, t >= 256); + if (cross) cycles++; + break; + case ASMIT_AND: + if (mode != ASMIM_IMMEDIATE) + addr = ReadMemory(addr); + mRegA &= addr; + UpdateStatus(mRegA); + if (cross) cycles++; + break; + case ASMIT_ASL: + if (mode == ASMIM_IMPLIED) + { + t = mRegA << 1; + mRegA = (t & 255); + UpdateStatusCarry(mRegA, t >= 256); + } + else + { + t = ReadMemory(addr) << 1; + WriteMemory(addr, t & 255); + UpdateStatusCarry(t & 255, t >= 256); + cycles += 2; + if (indexed) cycles++; + } + break; + case ASMIT_BCC: + if (!(mRegP & STATUS_CARRY)) + { + mIP = addr; + cycles++; + } + break; + case ASMIT_BCS: + if ((mRegP & STATUS_CARRY)) + { + mIP = addr; + cycles++; + } + break; + case ASMIT_BEQ: + if ((mRegP & STATUS_ZERO)) + { + mIP = addr; + cycles++; + } + break; + case ASMIT_BIT: + t = ReadMemory(addr); + mRegP &= ~(STATUS_ZERO | STATUS_SIGN | STATUS_OVERFLOW); + if (t & 0x80) mRegP |= STATUS_SIGN; + if (t & 0x40) mRegP |= STATUS_OVERFLOW; + if (!(t & mRegA)) mRegP |= STATUS_ZERO; + break; + case ASMIT_BMI: + if ((mRegP & STATUS_SIGN)) + { + mIP = addr; + cycles++; + } + break; + case ASMIT_BNE: + if (!(mRegP & STATUS_ZERO)) + { + mIP = addr; + cycles++; + } + break; + case ASMIT_BPL: + if (!(mRegP & STATUS_SIGN)) + { + mIP = addr; + cycles++; + } + break; + case ASMIT_BRK: + return false; + break; + case ASMIT_BVC: + if (!(mRegP & STATUS_OVERFLOW)) + { + mIP = addr; + cycles++; + } + break; + case ASMIT_BVS: + if ((mRegP & STATUS_OVERFLOW)) + { + mIP = addr; + cycles++; + } + break; + case ASMIT_CLC: + mRegP &= ~STATUS_CARRY; + break; + case ASMIT_CLD: + break; + case ASMIT_CLI: + break; + case ASMIT_CLV: + mRegP &= ~STATUS_OVERFLOW; + break; + case ASMIT_CMP: + if (mode != ASMIM_IMMEDIATE) + addr = ReadMemory(addr); + t = mRegA + (addr ^ 0xff) + 1; + + mRegP = 0; + + if ((mRegA & 0x80) && !(addr & 0x80) && !(t & 0x80) || + !(mRegA & 0x80) && (addr & 0x80) && (t & 0x80)) + mRegP |= STATUS_OVERFLOW; + + UpdateStatusCarry(t & 255, t >= 256); + if (cross) cycles++; + break; + case ASMIT_CPX: + if (mode != ASMIM_IMMEDIATE) + addr = ReadMemory(addr); + t = mRegX + (addr ^ 0xff) + 1; + + mRegP = 0; + + if ((mRegX & 0x80) && !(addr & 0x80) && !(t & 0x80) || + !(mRegX & 0x80) && (addr & 0x80) && (t & 0x80)) + mRegP |= STATUS_OVERFLOW; + + UpdateStatusCarry(t & 255, t >= 256); + break; + case ASMIT_CPY: + if (mode != ASMIM_IMMEDIATE) + addr = ReadMemory(addr); + t = mRegY + (addr ^ 0xff) + 1; + + mRegP = 0; + + if ((mRegY & 0x80) && !(addr & 0x80) && !(t & 0x80) || + !(mRegY & 0x80) && (addr & 0x80) && (t & 0x80)) + mRegP |= STATUS_OVERFLOW; + + UpdateStatusCarry(t & 255, t >= 256); + break; + case ASMIT_DEC: + if (mode == ASMIM_IMPLIED) + { + t = mRegA - 1; + mRegA = (t & 255); + UpdateStatus(mRegA); + } + else + { + t = ReadMemory(addr) - 1; + WriteMemory(addr, t & 255); + UpdateStatus(t & 255); + cycles += 2; + if (indexed) cycles++; + } + break; + case ASMIT_DEX: + t = mRegX - 1; + mRegX = (t & 255); + UpdateStatus(mRegX); + break; + case ASMIT_DEY: + t = mRegY - 1; + mRegY = (t & 255); + UpdateStatus(mRegY); + break; + case ASMIT_EOR: + if (mode != ASMIM_IMMEDIATE) + addr = ReadMemory(addr); + mRegA ^= addr; + UpdateStatus(mRegA); + if (cross) cycles++; + break; + case ASMIT_INC: + if (mode == ASMIM_IMPLIED) + { + t = mRegA + 1; + mRegA = (t & 255); + UpdateStatus(mRegA); + } + else + { + t = ReadMemory(addr) + 1; + WriteMemory(addr, t & 255); + UpdateStatus(t & 255); + cycles += 2; + if (indexed) cycles++; + } + break; + case ASMIT_INX: + t = mRegX + 1; + mRegX = (t & 255); + UpdateStatus(mRegX); + break; + case ASMIT_INY: + t = mRegY + 1; + mRegY = (t & 255); + UpdateStatus(mRegY); + break; + case ASMIT_JMP: + mIP = addr; + break; + case ASMIT_JSR: + mMemory[0x100 + mRegS] = (mIP - 1) >> 8; + mRegS--; + mMemory[0x100 + mRegS] = (mIP - 1) & 0xff; + mCalls[mRegS] = true; + mRegS--; + mIP = addr; + cycles += 2; + break; + case ASMIT_LDA: + if (mode != ASMIM_IMMEDIATE) + addr = ReadMemory(addr); + mRegA = addr; + UpdateStatus(mRegA); + if (cross) cycles++; + break; + case ASMIT_LDX: + if (mode != ASMIM_IMMEDIATE) + addr = ReadMemory(addr); + mRegX = addr; + UpdateStatus(mRegX); + if (cross) cycles++; + break; + case ASMIT_LDY: + if (mode != ASMIM_IMMEDIATE) + addr = ReadMemory(addr); + mRegY = addr; + UpdateStatus(mRegY); + if (cross) cycles++; + break; + case ASMIT_LSR: + if (mode == ASMIM_IMPLIED) + { + int c = mRegA & 1; + t = mRegA >> 1; + mRegA = (t & 255); + UpdateStatusCarry(mRegA, c != 0); + } + else + { + t = ReadMemory(addr); + int c = t & 1; + t >>= 1; + WriteMemory(addr, t & 255); + UpdateStatusCarry(t & 255, c != 0); + cycles += 2; + if (indexed) cycles++; + } + break; + case ASMIT_NOP: + break; + case ASMIT_ORA: + if (mode != ASMIM_IMMEDIATE) + addr = ReadMemory(addr); + mRegA |= addr; + UpdateStatus(mRegA); + if (cross) cycles++; + break; + case ASMIT_PHA: + mCalls[mRegS] = false; + mMemory[0x100 + mRegS] = mRegA; + mRegS--; + cycles ++; + break; + case ASMIT_PHP: + mCalls[mRegS] = false; + mMemory[0x100 + mRegS] = mRegP; + mRegS--; + cycles++; + break; + case ASMIT_PLA: + mRegS++; + mRegA = mMemory[0x100 + mRegS]; + cycles++; + break; + case ASMIT_PLP: + mRegS++; + mRegP = mMemory[0x100 + mRegS]; + cycles++; + break; + case ASMIT_ROL: + if (mode == ASMIM_IMPLIED) + { + t = (mRegA << 1) | (mRegP & STATUS_CARRY); + mRegA = (t & 255); + UpdateStatusCarry(mRegA, t >= 256); + } + else + { + t = (ReadMemory(addr) << 1) | (mRegP & STATUS_CARRY);; + WriteMemory(addr, t & 255); + UpdateStatusCarry(t & 255, t >= 256); + cycles+=2; + if (indexed) cycles++; + } + break; + case ASMIT_ROR: + if (mode == ASMIM_IMPLIED) + { + int c = mRegA & 1; + t = (mRegA >> 1) | ((mRegP & STATUS_CARRY) << 7); + mRegA = (t & 255); + UpdateStatusCarry(mRegA, c != 0); + } + else + { + t = ReadMemory(addr); + int c = t & 1; + t = (t >> 1) | ((mRegP & STATUS_CARRY) << 7); + WriteMemory(addr, t & 255); + UpdateStatusCarry(t & 255, c != 0); + cycles += 2; + if (indexed) cycles++; + } + break; + case ASMIT_RTI: + break; + case ASMIT_RTS: + mIP = (mMemory[0x101 + mRegS] + 256 * mMemory[0x102 + mRegS] + 1) & 0xffff; + mRegS += 2; + cycles += 4; + break; + case ASMIT_SBC: + if (mode != ASMIM_IMMEDIATE) + addr = ReadMemory(addr); + t = mRegA + (addr ^ 0xff) + (mRegP & STATUS_CARRY); + + mRegP = 0; + + if ((mRegA & 0x80) && !(addr & 0x80) && !(t & 0x80) || + !(mRegA & 0x80) && (addr & 0x80) && (t & 0x80)) + mRegP |= STATUS_OVERFLOW; + + mRegA = (t & 255); + UpdateStatusCarry(t & 255, t >= 256); + if (cross) cycles++; + break; + case ASMIT_SEC: + mRegP |= STATUS_CARRY; + break; + case ASMIT_SED: + break; + case ASMIT_SEI: + break; + case ASMIT_STA: + WriteMemory(addr, mRegA); + if (indexed) cycles++; + break; + case ASMIT_STX: + WriteMemory(addr, mRegX); + break; + case ASMIT_STY: + WriteMemory(addr, mRegY); + break; + case ASMIT_TAX: + mRegX = mRegA; + UpdateStatus(mRegX); + break; + case ASMIT_TAY: + mRegY = mRegA; + UpdateStatus(mRegY); + break; + case ASMIT_TSX: + mRegX = mRegS; + UpdateStatus(mRegX); + break; + case ASMIT_TXA: + mRegA = mRegX; + UpdateStatus(mRegA); + break; + case ASMIT_TXS: + mRegS = mRegX; + break; + case ASMIT_TYA: + mRegA = mRegY; + UpdateStatus(mRegA); + break; + case ASMIT_INV: + return false; + break; + } + + return true; +} + +void Emulator::DumpProfile(void) +{ + DumpCycles(); +} + +void Emulator::DumpCallstack(void) +{ + int ip = mIP; + int sp = mRegS; + + printf("Callstack:\n"); + while (sp < 0xfd) + { + printf("%02x [%04x] : ", sp, ip); + + const LinkerObject* lobj = nullptr; + if (mLinker) + { + lobj = mLinker->FindObjectByAddr(ip); + + const Location* loc = &(lobj->mLocation); + + if (lobj && lobj->mCodeLocations.Size()) + { + int j = 0; + int rip = ip - lobj->mAddress; + while (j < lobj->mCodeLocations.Size() && (rip < lobj->mCodeLocations[j].mStart || rip >= lobj->mCodeLocations[j].mEnd)) + j++; + + if (j < lobj->mCodeLocations.Size()) + loc = &(lobj->mCodeLocations[j].mLocation); + } + + while (loc) + { + printf("(%s,%d) ", loc->mFileName, loc->mLine); + loc = loc->mFrom; + } + } + printf("\n"); + + while (sp < 0xfd && !mCalls[sp]) + sp++; + ip = (mMemory[0x100 + sp] + 256 * mMemory[0x101 + sp] - 2) & 0xffff; + sp += 2; + } +} + +uint8 Emulator::ReadMemory(uint16 addr) +{ + if (mUseIORange && addr >= 0xdd80 && addr < 0xde00) + { + switch (addr & 15) + { + case 0: + return mIORange.mCount0++; + case 1: + return mIORange.mCount1++; + case 2: + return mIORange.mDMAAddress & 0xff; + case 3: + return mIORange.mDMAAddress >> 8; + case 4: + return mMemory[mIORange.mDMAAddress++]; + case 5: + case 6: + case 7: + return mIORange.mMirror; + case 8: + case 9: + case 10: + case 11: + return mIORange.mCycleCount >> 8 * (addr & 3); + case 12: + case 13: + case 14: + case 15: + return mIORange.mScratch[addr & 3]; + default: + return 0; + } + } + else + return mMemory[addr]; +} + +void Emulator::WriteMemory(uint16 addr, uint8 data) +{ + if (mUseIORange && addr >= 0xdd80 && addr < 0xde00) + { + switch (addr & 15) + { + case 0: + mIORange.mCount0++; + break; + case 1: + mIORange.mCount1++; + break; + case 2: + mIORange.mDMAAddress = (mIORange.mDMAAddress & 0xff00) | data; + break; + case 3: + mIORange.mDMAAddress = (mIORange.mDMAAddress & 0x00ff) | (data << 8); + break; + case 4: + mMemory[mIORange.mDMAAddress++] = data; + break; + case 5: + case 6: + case 7: + mIORange.mMirror = data;; + break; + case 8: + case 9: + case 10: + case 11: + mIORange.mCycleCount = mCycleCount; + break; + case 12: + case 13: + case 14: + case 15: + mIORange.mScratch[addr & 3] = data; + break; + } + } + else + mMemory[addr] = data; +} + +int Emulator::Emulate(int startIP, int exitIP, int trace, bool iorange) +{ + mUseIORange = iorange; + mIORange.mCount0 = mIORange.mCount1 = 0; + mIORange.mMirror = 0; + mIORange.mScratch[0] = mIORange.mScratch[1] = mIORange.mScratch[2] =mIORange.mScratch[3] = 0; + + mCycleCount = 0; + for (int i = 0; i < 0x10000; i++) + mCycles[i] = 0; + for (int i = 0; i < 0x100; i++) + mCalls[i] = false; + + if (exitIP) + { + while (mMemory[exitIP] != 0x9a) + exitIP++; + } + + mExitIP = exitIP; + mIP = startIP; + mRegA = 0; + mRegX = 0; + mRegY = 0; + mRegP = 0; + mRegS = 0xfd; + + mMemory[0x1fe] = 0xff; + mMemory[0x1ff] = 0xff; + + mCycleCount = 0; + + int tcycles = 0; + int iip = 0; + while (mIP != 0) + { + if (mJiffies) + { + if (mCycleCount >= tcycles + 16667) + { + mMemory[0xa2]++; + if (!mMemory[0xa2]) + { + mMemory[0xa1]++; + if (!mMemory[0xa1]) + { + mMemory[0xa0]++; + } + } + tcycles += 16667; + } + } + + if (mIP == mExitIP) + { + if (mMemory[BC_REG_ACCU + 1] & 0x80) + DumpCallstack(); + } + else if (mIP == 0xffd2) + { + if (mRegA == 13) + putchar('\n'); + else + putchar(mRegA); + mIP = mMemory[0x101 + mRegS] + 256 * mMemory[0x102 + mRegS] + 1; + mRegS += 2; + } + else if (mIP == 0xffcf) + { + int ch = getchar(); + mRegA = ch; + mIP = mMemory[0x101 + mRegS] + 256 * mMemory[0x102 + mRegS] + 1; + mRegS += 2; + } + else if (mIP == 0xff81) + { + printf("------------------ CLEAR ---------------\n"); + mIP = mMemory[0x101 + mRegS] + 256 * mMemory[0x102 + mRegS] + 1; + mRegS += 2; + } + + uint8 opcode = mMemory[mIP]; + AsmInsData d = DecInsData[opcode]; + int addr = 0, taddr; + int ip = mIP; + + if (ip == 0x0862) + iip = mMemory[BC_REG_IP] + 256 * mMemory[BC_REG_IP + 1] + mRegY; + + bool cross = false, indexed = false; + int icycles = 0; + + mIP++; + switch (d.mMode) + { + case ASMIM_IMPLIED: + if (trace & TRACEF_NATIVE) + printf("%04x : %04x %02x __ __ %s (A:%02x X:%02x Y:%02x P:%02x S:%02x)\n", iip, ip, mMemory[ip], AsmInstructionNames[d.mType], mRegA, mRegX, mRegY, mRegP, mRegS); + icycles = 2; + break; + case ASMIM_IMMEDIATE: + addr = mMemory[mIP++]; + if (trace & TRACEF_NATIVE) + printf("%04x : %04x %02x %02x __ %s #$%02x (A:%02x X:%02x Y:%02x P:%02x S:%02x)\n", iip, ip, mMemory[ip], mMemory[ip+1], AsmInstructionNames[d.mType], addr, mRegA, mRegX, mRegY, mRegP, mRegS); + icycles = 2; + break; + case ASMIM_ZERO_PAGE: + addr = mMemory[mIP++]; + if (trace & TRACEF_NATIVE) + printf("%04x : %04x %02x %02x __ %s $%02x (A:%02x X:%02x Y:%02x P:%02x S:%02x M:%02x)\n", iip, ip, mMemory[ip], mMemory[ip + 1], AsmInstructionNames[d.mType], addr, mRegA, mRegX, mRegY, mRegP, mRegS, mMemory[addr]); + icycles = 3; + break; + case ASMIM_ZERO_PAGE_X: + taddr = mMemory[mIP++]; + addr = (taddr + mRegX) & 0xff; + if (trace & TRACEF_NATIVE) + printf("%04x : %04x %02x %02x __ %s $%02x,x (A:%02x X:%02x Y:%02x P:%02x S:%02x %04x M:%02x)\n", iip, ip, mMemory[ip], mMemory[ip + 1], AsmInstructionNames[d.mType], taddr, mRegA, mRegX, mRegY, mRegP, mRegS, addr, mMemory[addr]); + icycles = 4; + break; + case ASMIM_ZERO_PAGE_Y: + taddr = mMemory[mIP++]; + addr = (taddr + mRegY) & 0xff; + if (trace & TRACEF_NATIVE) + printf("%04x : %04x %02x %02x __ %s $%02x,y (A:%02x X:%02x Y:%02x P:%02x S:%02x %04x M:%02x)\n", iip, ip, mMemory[ip], mMemory[ip + 1], AsmInstructionNames[d.mType], taddr, mRegA, mRegX, mRegY, mRegP, mRegS, addr, mMemory[addr]); + icycles = 4; + break; + case ASMIM_ABSOLUTE: + addr = mMemory[mIP] + 256 * mMemory[mIP + 1]; + if (trace & TRACEF_NATIVE) + printf("%04x : %04x %02x %02x %02x %s $%04x (A:%02x X:%02x Y:%02x P:%02x S:%02x M:%02x)\n", iip, ip, mMemory[ip], mMemory[ip + 1], mMemory[ip + 2], AsmInstructionNames[d.mType], addr, mRegA, mRegX, mRegY, mRegP, mRegS, mMemory[addr]); + mIP += 2; + icycles = 4; + break; + case ASMIM_ABSOLUTE_X: + taddr = mMemory[mIP] + 256 * mMemory[mIP + 1]; + addr = (taddr + mRegX) & 0xffff; + cross = mMemory[mIP] + mRegX >= 256; + indexed = true; + if (trace & TRACEF_NATIVE) + printf("%04x : %04x %02x %02x %02x %s $%04x,x (A:%02x X:%02x Y:%02x P:%02x S:%02x %04x M:%02x)\n", iip, ip, mMemory[ip], mMemory[ip + 1], mMemory[ip + 2], AsmInstructionNames[d.mType], taddr, mRegA, mRegX, mRegY, mRegP, mRegS, addr, mMemory[addr]); + mIP += 2; + icycles = 4; + break; + case ASMIM_ABSOLUTE_Y: + taddr = mMemory[mIP] + 256 * mMemory[mIP + 1]; + addr = (taddr + mRegY) & 0xffff; + cross = mMemory[mIP] + mRegY >= 256; + indexed = true; + if (trace & TRACEF_NATIVE) + printf("%04x : %04x %02x %02x %02x %s $%04x,y (A:%02x X:%02x Y:%02x P:%02x S:%02x %04x M:%02x)\n", iip, ip, mMemory[ip], mMemory[ip + 1], mMemory[ip + 2], AsmInstructionNames[d.mType], taddr, mRegA, mRegX, mRegY, mRegP, mRegS, addr, mMemory[addr]); + mIP += 2; + icycles = 4; + break; + case ASMIM_INDIRECT: + taddr = mMemory[mIP] + 256 * mMemory[mIP + 1]; + mIP += 2; + addr = mMemory[taddr] + 256 * mMemory[taddr + 1]; + if (trace & TRACEF_NATIVE) + printf("%04x : %04x %02x %02x %02x %s ($%04x) (A:%02x X:%02x Y:%02x P:%02x S:%02x %04x)\n", iip, ip, mMemory[ip], mMemory[ip + 1], mMemory[ip + 2], AsmInstructionNames[d.mType], taddr, mRegA, mRegX, mRegY, mRegP, mRegS, addr); + icycles = 6; + break; + case ASMIM_INDIRECT_X: + taddr = (mMemory[mIP++] + mRegX) & 0xff; + addr = mMemory[taddr] + 256 * mMemory[taddr + 1]; + if (trace & TRACEF_NATIVE) + printf("%04x : %04x %02x %02x __ %s ($%02x,x) (A:%02x X:%02x Y:%02x P:%02x S:%02x %02x %04x M:%02x)\n", iip, ip, mMemory[ip], mMemory[ip + 1], AsmInstructionNames[d.mType], mMemory[ip + 1], mRegA, mRegX, mRegY, mRegP, mRegS, taddr, addr, mMemory[addr]); + icycles = 6; + break; + case ASMIM_INDIRECT_Y: + taddr = mMemory[mIP++]; + addr = (mMemory[taddr] + 256 * mMemory[taddr + 1] + mRegY) & 0xffff; + cross = mMemory[taddr] + mRegY >= 256; + indexed = true; + if (trace & TRACEF_NATIVE) + printf("%04x : %04x %02x %02x __ %s ($%02x),y (A:%02x X:%02x Y:%02x P:%02x S:%02x %04x M:%02x)\n", iip, ip, mMemory[ip], mMemory[ip + 1], AsmInstructionNames[d.mType], taddr, mRegA, mRegX, mRegY, mRegP, mRegS, addr, mMemory[addr]); + icycles = 5; + break; + case ASMIM_RELATIVE: + taddr = mMemory[mIP++]; + if (taddr & 0x80) + addr = taddr + mIP - 256; + else + addr = taddr + mIP; + if (trace & TRACEF_NATIVE) + printf("%04x : %04x %02x %02x __ %s $%02x (A:%02x X:%02x Y:%02x P:%02x S:%02x %04x)\n", iip, ip, mMemory[ip], mMemory[ip + 1], AsmInstructionNames[d.mType], taddr, mRegA, mRegX, mRegY, mRegP, mRegS, addr); + icycles = 2; + break; + } + + if ((trace & TRACEF_BYTECODE) && ip == 0x0862) + { + unsigned accu = mMemory[BC_REG_ACCU] + (mMemory[BC_REG_ACCU + 1] << 8) + (mMemory[BC_REG_ACCU + 2] << 16) + (mMemory[BC_REG_ACCU + 3] << 24); + int ptr = mMemory[BC_REG_ADDR] + 256 * mMemory[BC_REG_ADDR + 1]; + int sp = mMemory[BC_REG_STACK] + 256 * mMemory[BC_REG_STACK + 1]; + printf("%04x (A:%08x P:%04x S:%04x) %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x : %04x\n", addr, accu, ptr, sp, + mMemory[BC_REG_TMP + 0] + 256 * mMemory[BC_REG_TMP + 1], + mMemory[BC_REG_TMP + 2] + 256 * mMemory[BC_REG_TMP + 3], + mMemory[BC_REG_TMP + 4] + 256 * mMemory[BC_REG_TMP + 5], + mMemory[BC_REG_TMP + 6] + 256 * mMemory[BC_REG_TMP + 7], + + mMemory[BC_REG_TMP + 8] + 256 * mMemory[BC_REG_TMP + 9], + mMemory[BC_REG_TMP + 10] + 256 * mMemory[BC_REG_TMP + 11], + mMemory[BC_REG_TMP + 12] + 256 * mMemory[BC_REG_TMP + 13], + mMemory[BC_REG_TMP + 14] + 256 * mMemory[BC_REG_TMP + 15], + + mMemory[BC_REG_TMP + 16] + 256 * mMemory[BC_REG_TMP + 17], + mMemory[BC_REG_TMP + 18] + 256 * mMemory[BC_REG_TMP + 19], + mMemory[BC_REG_TMP + 20] + 256 * mMemory[BC_REG_TMP + 21], + mMemory[BC_REG_TMP + 22] + 256 * mMemory[BC_REG_TMP + 23], + + mMemory[BC_REG_TMP + 24] + 256 * mMemory[BC_REG_TMP + 25], + mMemory[BC_REG_TMP + 26] + 256 * mMemory[BC_REG_TMP + 27], + mMemory[BC_REG_TMP + 28] + 256 * mMemory[BC_REG_TMP + 29], + mMemory[BC_REG_TMP + 30] + 256 * mMemory[BC_REG_TMP + 31], + + mMemory[0x9f9e] + 256 * mMemory[0x9f9f] + + + ); + } + + if (!EmulateInstruction(d.mType, d.mMode, addr, icycles, cross, indexed)) + { + if (exitIP) + DumpCallstack(); + return -1; + } + + mCycles[ip] += icycles; + mCycleCount += icycles; + } + + if (mRegS == 0xff) + { +#if 0 + for (int i = 0; i < 256; i++) + if (mMemory[i] != 0) + printf("ZP %02x : %02x\n", i, mMemory[i]); +#endif + return int16(mMemory[BC_REG_ACCU] + 256 * mMemory[BC_REG_ACCU + 1]); + } + + return -1; +} diff --git a/oscar64/oscar64/Emulator.h b/oscar64/oscar64/Emulator.h new file mode 100644 index 0000000..f0273eb --- /dev/null +++ b/oscar64/oscar64/Emulator.h @@ -0,0 +1,47 @@ +#pragma once + +#include "Assembler.h" +#include "MachineTypes.h" + +class Linker; + +static const int TRACEF_BYTECODE = 0x01; +static const int TRACEF_NATIVE = 0x02; + +class Emulator +{ +public: + Emulator(Linker * linker); + ~Emulator(void); + + uint8 mMemory[0x10000]; + int mCycles[0x10000]; + bool mCalls[0x100]; + + int mIP, mExitIP; + uint8 mRegA, mRegX, mRegY, mRegS, mRegP; + bool mJiffies, mUseIORange; + int mCycleCount; + + struct IORange + { + uint8 mCount0, mCount1, mMirror; + uint8 mScratch[4]; + uint16 mDMAAddress; + uint32 mCycleCount; + } mIORange; + + Linker* mLinker; + + int Emulate(int startIP, int exitIP, int trace, bool iorange); + void DumpProfile(void); + bool EmulateInstruction(AsmInsType type, AsmInsMode mode, int addr, int & cycles, bool cross, bool indexed); +protected: + void UpdateStatus(uint8 result); + void UpdateStatusCarry(uint8 result, bool carry); + void DumpCycles(void); + void DumpCallstack(void); + + uint8 ReadMemory(uint16 addr); + void WriteMemory(uint16 addr, uint8 data); +}; \ No newline at end of file diff --git a/oscar64/oscar64/Errors.cpp b/oscar64/oscar64/Errors.cpp new file mode 100644 index 0000000..e6f03b0 --- /dev/null +++ b/oscar64/oscar64/Errors.cpp @@ -0,0 +1,73 @@ +#include "Errors.h" +#include "Ident.h" +#include +#include + +Errors::Errors(void) + : mErrorCount(0), mMinLevel(EINFO_GENERIC), mDisabled(EERR_GENERIC) +{ + +} + +ErrorID Errors::SetMinLevel(ErrorID id) +{ + ErrorID pid = mMinLevel; + mMinLevel = id; + return pid; +} + +void Errors::Error(const Location& loc, ErrorID eid, const char* msg, const Ident* info1, const Ident* info2) +{ + if (!info1) + this->Error(loc, eid, msg); + else if (!info2) + this->Error(loc, eid, msg, info1->mString); + else + this->Error(loc, eid, msg, info1->mString, info2->mString); +} + + +void Errors::Error(const Location& loc, ErrorID eid, const char* msg, const char* info1, const char * info2) +{ + if (eid >= mMinLevel && !(eid < EERR_GENERIC && mDisabled[eid])) + { + const char* level = "info"; + if (eid >= EERR_GENERIC) + { + + level = "error"; + mErrorCount++; + } + else if (eid >= EWARN_GENERIC) + { + level = "warning"; + } + + if (loc.mFileName) + { + if (!info1) + fprintf(stderr, "%s(%d, %d) : %s %d: %s\n", loc.mFileName, loc.mLine, loc.mColumn, level, eid, msg); + else if (!info2) + fprintf(stderr, "%s(%d, %d) : %s %d: %s '%s'\n", loc.mFileName, loc.mLine, loc.mColumn, level, eid, msg, info1); + else + fprintf(stderr, "%s(%d, %d) : %s %d: %s '%s' != '%s'\n", loc.mFileName, loc.mLine, loc.mColumn, level, eid, msg, info1, info2); + + if (loc.mFrom) + Error(*(loc.mFrom), EINFO_EXPANDED, "While expanding here"); + } + else + { + if (!info1) + fprintf(stderr, "oscar64: %s %d: %s\n", level, eid, msg); + else if (!info2) + fprintf(stderr, "oscar64: %s %d: %s '%s'\n", level, eid, msg, info1); + else + fprintf(stderr, "oscar64: %s %d: %s '%s' != '%s'\n", level, eid, msg, info1, info2); + } + + if (mErrorCount > 10 || eid >= EFATAL_GENERIC) + exit(20); + } +} + + diff --git a/oscar64/oscar64/Errors.h b/oscar64/oscar64/Errors.h new file mode 100644 index 0000000..2e88968 --- /dev/null +++ b/oscar64/oscar64/Errors.h @@ -0,0 +1,159 @@ +#pragma once + +#include "NumberSet.h" + + +class Location +{ +public: + const char * mFileName; + int mLine, mColumn; + const Location * mFrom; + + Location() : mFileName(nullptr), mLine(0), mColumn(0), mFrom(nullptr) {} + Location(const Location& loc, const Location* from) + : mFileName(loc.mFileName), mLine(loc.mLine), mColumn(loc.mColumn), mFrom(from) + { + static volatile int k; + if (from) + k = from->mLine; + } +}; + +class Ident; + +enum ErrorID +{ + EINFO_GENERIC = 1000, + EINFO_EXPANDED = 1001, + EINFO_ORIGINAL_DEFINITION = 1002, + EINFO_CALLED_FROM = 1003, + EINFO_SIZE = 1004, + + EWARN_GENERIC = 2000, + EWARN_CONSTANT_TRUNCATED, + EWARN_UNKNOWN_PRAGMA, + EWARN_INDEX_OUT_OF_BOUNDS, + EWARN_SYNTAX, + EWARN_NOT_INTERRUPT_SAFE, + EWARN_BOOL_SHORTCUT, + EWARN_OPTIMIZER_LOCKED, + EWARN_LOOP_UNROLL_IGNORED, + EWARN_USE_OF_UNINITIALIZED_VARIABLE, + EWARN_MISSING_RETURN_STATEMENT, + EWARN_UNREACHABLE_CODE, + EWARN_NULL_POINTER_DEREFERENCED, + EWARN_DESTRUCTOR_MISMATCH, + EWARN_NUMERIC_0_USED_AS_NULLPTR, + EWARN_FLOAT_TO_INT, + EWARN_UNDEFINED_POINTER_ARITHMETIC, + EWARN_INVALID_VALUE_RANGE, + EWARN_DEFAULT_COPY_DEPRECATED, + EWARN_INSUFFICIENT_MEMORY, + EWARN_FUNCTION_NOT_INLINED, + EWARN_INVALID_VOID_POINTER_ARITHMETIC, + EWARN_DIVISION_BY_ZERO, + EWARN_EXPAND_UNDEFINED_MACRO_IDENT, + + EERR_GENERIC = 3000, + EERR_FILE_NOT_FOUND, + EERR_RUNTIME_CODE, + EERR_UNIMPLEMENTED, + EERR_COMMAND_LINE, + EERR_OBJECT_NOT_FOUND, + EERR_SYNTAX, + EERR_EXECUTION_FAILED, + EERR_CONSTANT_INITIALIZER, + EERR_CONSTANT_TYPE, + EERR_VARIABLE_TYPE, + EERR_INVALID_VALUE, + EERR_INCOMPATIBLE_TYPES, + EERR_INCOMPATIBLE_OPERATOR, + EERR_CONST_ASSIGN, + EERR_NOT_AN_LVALUE, + EERR_INVALID_INDEX, + EERR_WRONG_PARAMETER, + EERR_INVALID_RETURN, + EERR_INVALID_BREAK, + EERR_INVALID_CONTINUE, + EERR_DUPLICATE_DEFAULT, + EERR_UNDEFINED_OBJECT, + EERR_DUPLICATE_DEFINITION, + EERR_NOT_A_TYPE, + EERR_DECLARATION_DIFFERS, + EERR_INVALID_IDENTIFIER, + EERR_ASM_INVALD_OPERAND, + EERR_ASM_INVALID_INSTRUCTION, + EERR_ASM_INVALID_MODE, + EERR_ASM_INVALID_REGISTER, + EERR_PRAGMA_PARAMETER, + ERRR_PREPROCESSOR, + ERRR_INVALID_CASE, + ERRR_INSUFFICIENT_MEMORY, + ERRR_INTERRUPT_TO_COMPLEX, + ERRR_INVALID_STORAGE_TYPE, + ERRR_SEMICOLON_EXPECTED, + ERRR_USE_OF_UNINITIALIZED_VARIABLE, + ERRR_STRIPE_REQUIRES_FIXED_SIZE_ARRAY, + ERRR_CANNOT_FIND_BANK_OF_EXPRESSION, + ERRO_NOT_A_NAMESPACE, + ERRO_NOT_A_BASE_CLASS, + ERRO_THIS_OUTSIDE_OF_METHOD, + ERRO_NO_MATCHING_FUNCTION_CALL, + ERRO_AMBIGUOUS_FUNCTION_CALL, + EERR_NO_DEFAULT_CONSTRUCTOR, + EERR_INVALID_OPERATOR, + EERR_MISSING_TEMP, + EERR_NON_STATIC_MEMBER, + EERR_TEMPLATE_PARAMS, + EERR_FUNCTION_TEMPLATE, + EERR_INVALID_BITFIELD, + EERR_INVALID_CAPTURE, + EERR_INVALID_PACK_USAGE, + EERR_INVALID_FOLD_EXPRESSION, + ERRR_INSTANTIATE_ABSTRACT_CLASS, + ERRR_INVALID_GOTO, + EERR_INVALID_INITIALIZER, + ERRR_INVALID_VOID_POINTER_ARITHMETIC, + + EERR_INVALID_CONSTEXPR, + EERR_DOUBLE_FREE, + EERR_UNBALANCED_HEAP_USE, + + ERRR_STACK_OVERFLOW, + ERRR_INVALID_NUMBER, + EERR_OVERLAPPING_DATA_SECTIONS, + EERR_ASSEMBLER_LIMIT, + EERR_SECTION_ON_IN_REGION, + + EERR_INVALID_PREPROCESSOR, + EERR_INVALID_CLASS_INITIALIZER, + EERR_CALL_OF_DELETED_FUNCTION, + + EERR_STATIC_ASSERT, + + ERRR_INVALID_STRUCT_BINDING_TYPE, + ERRR_CALLING_DELETED_FUNCTION, + + EFATAL_GENERIC = 4000, + EFATAL_OUT_OF_MEMORY, + EFATAL_MACRO_EXPANSION_DEPTH, + + ERROR_MAX = 5000 +}; + +class Errors +{ +public: + Errors(void); + + ErrorID SetMinLevel(ErrorID id); + + int mErrorCount; + ErrorID mMinLevel; + + NumberSet mDisabled; + + void Error(const Location& loc, ErrorID eid, const char* msg, const Ident* info1, const Ident* info2 = nullptr); + void Error(const Location& loc, ErrorID eid, const char* msg, const char* info1 = nullptr, const char* info2 = nullptr); +}; diff --git a/oscar64/oscar64/GlobalAnalyzer.cpp b/oscar64/oscar64/GlobalAnalyzer.cpp new file mode 100644 index 0000000..2a4cfd3 --- /dev/null +++ b/oscar64/oscar64/GlobalAnalyzer.cpp @@ -0,0 +1,1392 @@ +#include "GlobalAnalyzer.h" + +GlobalAnalyzer::GlobalAnalyzer(Errors* errors, Linker* linker) + : mErrors(errors), mLinker(linker), mCalledFunctions(nullptr), mCallingFunctions(nullptr), mVariableFunctions(nullptr), mFunctions(nullptr), mGlobalVariables(nullptr), mTopoFunctions(nullptr), mCompilerOptions(COPT_DEFAULT) +{ + +} + +GlobalAnalyzer::~GlobalAnalyzer(void) +{ + +} + +void GlobalAnalyzer::DumpCallGraph(void) +{ + printf("------------------------------\n"); + + for (int i = 0; i < mFunctions.Size(); i++) + { + GrowingArray decs(nullptr); + GrowingArray calls(0); + + Declaration* from = mFunctions[i]; + for (int j = 0; j < from->mCalled.Size(); j++) + { + Declaration* to = from->mCalled[j]; + + int k = decs.IndexOf(to); + if (k == -1) + { + decs.Push(to); + calls.Push(1); + } + else + calls[k]++; + } + + if (decs.Size() > 0) + { + for (int j = 0; j < decs.Size(); j++) + { + if (decs[j]->mType == DT_CONST_FUNCTION) + printf("CALL %s[%d, %08llx] -> %d -> %s[%d, %08llx]\n", from->mQualIdent->mString, from->mComplexity, from->mFlags, calls[j], decs[j]->mQualIdent->mString, decs[j]->mComplexity, decs[j]->mFlags); + else + printf("CALL %s[%d, %08llx] -> %d\n", from->mQualIdent->mString, from->mComplexity, from->mFlags, calls[j]); + } + } + else + { + printf("LEAF %d -> %s[%d, %08llx]\n", from->mCallers.Size(), from->mQualIdent->mString, from->mComplexity, from->mFlags ); + } + } + + for (int i = 0; i < mGlobalVariables.Size(); i++) + { + Declaration* var = mGlobalVariables[i]; + printf("VAR %s[%d, %08llx, %d]\n", var->mQualIdent->mString, var->mSize, var->mFlags, var->mUseCount); + } +} + +static int VarUseCountScale(Declaration* type) +{ + if (type->mType == DT_TYPE_BOOL || type->mType == DT_TYPE_INTEGER || type->mType == DT_TYPE_FLOAT || type->mType == DT_TYPE_ENUM) + return 0x100 / type->mSize; + else if (type->mType == DT_TYPE_POINTER) + return 0x800; + else if (type->mType == DT_TYPE_ARRAY) + { + if (type->mSize > 0) + return VarUseCountScale(type->mBase) / type->mSize; + else + return 0; + } + else if (type->mSize == DT_TYPE_STRUCT) + { + int size = 0; + Declaration* e = type->mParams; + while (e) + { + int t = VarUseCountScale(e->mBase); + if (t == 0) + return 0; + size += t; + e = e->mNext; + } + return size / (type->mSize * type->mSize); + } + else + return 0; +} + +void GlobalAnalyzer::AutoZeroPage(LinkerSection* lszp, int zpsize) +{ + if (mCompilerOptions & COPT_OPTIMIZE_AUTO_ZEROPAGE) + { + GrowingArray vars(nullptr); + + for (int i = 0; i < mGlobalVariables.Size(); i++) + { + Declaration* var = mGlobalVariables[i]; + if (var->mFlags & DTF_ANALYZED) + { + if (var->mFlags & DTF_ZEROPAGE) + zpsize -= var->mSize; + else if (var->mValue) + ; + else + { + var->mUseCount *= VarUseCountScale(var->mBase); + if (var->mUseCount) + { + int j = 0; + while (j < vars.Size() && vars[j]->mUseCount > var->mUseCount) + j++; + vars.Insert(j, var); + } + } + } + } + + int i = 0; + while (i < vars.Size() && zpsize > 0) + { + if (vars[i]->mSize <= zpsize && !vars[i]->mLinkerObject) + { + vars[i]->mSection = lszp; + vars[i]->mFlags |= DTF_ZEROPAGE; + zpsize -= vars[i]->mSize; + } + i++; + } + } +} + +void GlobalAnalyzer::TopoSort(Declaration* procDec) +{ + if (!(procDec->mFlags & DTF_FUNC_ANALYZING)) + { + procDec->mFlags |= DTF_FUNC_ANALYZING; + if (!mTopoFunctions.Contains(procDec)) + { + for (int i = 0; i < procDec->mCalled.Size(); i++) + TopoSort(procDec->mCalled[i]); + mTopoFunctions.Push(procDec); + } + procDec->mFlags &= ~DTF_FUNC_ANALYZING; + } +} + +int GlobalAnalyzer::CallerInvokes(Declaration* called) +{ + int n = 0; + for (int i = 0; i < called->mCallers.Size(); i++) + { + Declaration* f = called->mCallers[i]; + n += CallerInvokes(f, called); + } + return n; +} + +int GlobalAnalyzer::CallerInvokes(Declaration* caller, Declaration* called) +{ + int n = 1; + if (caller->mType == DT_CONST_FUNCTION && (caller->mFlags & (DTF_INLINE | DTF_FORCE_INLINE | DTF_REQUEST_INLINE)) && !(caller->mFlags & DTF_PREVENT_INLINE) && !(caller->mFlags & DTF_FUNC_RECURSIVE) && !(caller->mFlags & DTF_FUNC_VARIABLE) && !(caller->mFlags & DTF_EXPORT)) + n = CallerInvokes(caller); + return n > 1 ? n : 1; +} + +void GlobalAnalyzer::AutoInline(void) +{ + for (int i = 0; i < mFunctions.Size(); i++) + TopoSort(mFunctions[i]); + + bool changed = false; + do + { + changed = false; + + // Reverse order, to check inline from bottom to top of call graph + for (int i = 0; i< mTopoFunctions.Size(); i++) + { + Declaration* f = mTopoFunctions[i]; + + if (f->mType == DT_CONST_FUNCTION && + !(f->mFlags & DTF_INLINE) && + !(f->mFlags & DTF_EXPORT) && + !(f->mFlags & DTF_PREVENT_INLINE) && + !(f->mBase->mFlags & DTF_VARIADIC) && + !(f->mFlags & DTF_FUNC_VARIABLE) && + !((f->mFlags & DTF_FUNC_ASSEMBLER) && !(f->mFlags & DTF_REQUEST_INLINE)) && + !(f->mFlags & DTF_INTRINSIC) && + !(f->mFlags & DTF_FUNC_RECURSIVE) && + !(f->mFlags & DTF_FUNC_NO_RETURN)) + { + int nparams = 0; + Declaration* dec = f->mBase->mParams; + while (dec) + { + nparams += dec->mSize; + dec = dec->mNext; + } + + int invokes = CallerInvokes(f); + int cost = (f->mComplexity - 20 * nparams - 10); + +// printf("CHECK INLINING %s (%d) %d * (%d - 1)\n", f->mIdent->mString, f->mComplexity, cost, invokes); + + bool doinline = false; + if ((f->mCompilerOptions & COPT_OPTIMIZE_INLINE) && (f->mFlags & DTF_REQUEST_INLINE) || (f->mFlags & DTF_FORCE_INLINE)) + doinline = true; + if (f->mLocalSize < 100) + { + if ((f->mCompilerOptions & COPT_OPTIMIZE_AUTO_INLINE) && ((cost - 20) * (invokes - 1) <= 20)) + { + if (f->mCompilerOptions & COPT_OPTIMIZE_CODE_SIZE) + { + if (invokes == 1 && f->mSection == f->mCallers[0]->mSection || cost < 0) + doinline = true; + } + else if (invokes == 1 && f->mComplexity > 100) + { +// printf("CHECK INLINING2 %s <- %s %d\n", f->mIdent->mString, f->mCallers[0]->mIdent->mString, f->mCallers[0]->mCalled.Size()); + if (cost < 0 || f->mCallers[0]->mComplexity + cost < 1000 || f->mCallers[0]->mCalled.Size() == 1) + doinline = true; + } + else + doinline = true; + } + if ((f->mCompilerOptions & COPT_OPTIMIZE_AUTO_INLINE_ALL) && (cost * (invokes - 1) <= 10000)) + doinline = true; + } + + if (doinline) + { +// printf("INLINING %s %d * (%d - 1)\n", f->mIdent->mString, cost, invokes); + + f->mFlags |= DTF_INLINE; + for (int j = 0; j < f->mCallers.Size(); j++) + { + Declaration* cf = f->mCallers[j]; + + int sk = 0, dk = 0; + while (sk < cf->mCalled.Size()) + { + if (cf->mCalled[sk] == f && ((cf->mFlags & DTF_NATIVE) || !(f->mFlags & DTF_NATIVE))) + { + cf->mComplexity += cost; + for (int m = 0; m < f->mCalled.Size(); m++) + { + cf->mCalled.Push(f->mCalled[m]); + f->mCalled[m]->mCallers.Push(cf); + } + } + else + cf->mCalled[dk++] = cf->mCalled[sk]; + sk++; + } + cf->mCalled.SetSize(dk); + } + + changed = true; + } + } + } + + } while (changed); + + for (int i = 0; i < mFunctions.Size(); i++) + { + CheckFastcall(mFunctions[i], true); + } + + for (int i = 0; i < mFunctions.Size(); i++) + { + Declaration* dec = mFunctions[i]; + Declaration* pdec = dec->mBase->mParams; + while (pdec) + { + if (pdec->mFlags & DTF_FPARAM_CONST) + { + pdec->mFlags |= DTF_FPARAM_UNUSED; + + pdec->mVarIndex = dec->mNumVars++; + + Expression* aexp = new Expression(pdec->mLocation, EX_INITIALIZATION); + Expression* pexp = new Expression(pdec->mLocation, EX_VARIABLE); + Expression* lexp = new Expression(dec->mLocation, EX_SEQUENCE); + + pexp->mDecType = pdec->mBase; + pexp->mDecValue = pdec; + + aexp->mDecType = pdec->mBase; + aexp->mToken = TK_ASSIGN; + aexp->mLeft = pexp; + aexp->mRight = pdec->mValue; + + lexp->mLeft = aexp; + lexp->mRight = dec->mValue; + dec->mValue = lexp; + } + else if (pdec->mFlags & DTF_FPARAM_RANGE_LIMITED) + { +// printf("LimitRange %s::%s to %lld..%lld\n", dec->mQualIdent->mString, pdec->mIdent->mString, pdec->mMinValue, pdec->mMaxValue); + + Expression* pexp = new Expression(pdec->mLocation, EX_VARIABLE); + pexp->mDecType = pdec->mBase; + pexp->mDecValue = pdec; + + Expression* assumeExp = new Expression(pdec->mLocation, EX_ASSUME); + Expression* andExp = new Expression(pdec->mLocation, EX_LOGICAL_AND); + assumeExp->mLeft = andExp; + andExp->mDecType = TheBoolTypeDeclaration; + + Expression* minExp = new Expression(pdec->mLocation, EX_RELATIONAL); + andExp->mLeft = minExp; + minExp->mDecType = TheBoolTypeDeclaration; + minExp->mToken = TK_GREATER_EQUAL; + minExp->mLeft = pexp; + minExp->mRight = new Expression(pdec->mLocation, EX_CONSTANT); + minExp->mRight->mDecType = pdec->mBase; + minExp->mRight->mDecValue = new Declaration(pdec->mLocation, DT_CONST_INTEGER); + minExp->mRight->mDecValue->mBase = pdec->mBase; + minExp->mRight->mDecValue->mInteger = pdec->mMinValue; + + Expression* maxExp = new Expression(pdec->mLocation, EX_RELATIONAL); + andExp->mRight = maxExp; + maxExp->mDecType = TheBoolTypeDeclaration; + maxExp->mToken = TK_LESS_EQUAL; + maxExp->mLeft = pexp; + maxExp->mRight = new Expression(pdec->mLocation, EX_CONSTANT); + maxExp->mRight->mDecType = pdec->mBase; + maxExp->mRight->mDecValue = new Declaration(pdec->mLocation, DT_CONST_INTEGER); + maxExp->mRight->mDecValue->mBase = pdec->mBase; + maxExp->mRight->mDecValue->mInteger = pdec->mMaxValue; + + Expression* lexp = new Expression(dec->mLocation, EX_SEQUENCE); + lexp->mLeft = assumeExp; + lexp->mRight = dec->mValue; + dec->mValue = lexp; + } + + pdec = pdec->mNext; + } + } + +} + +bool GlobalAnalyzer::MarkCycle(Declaration* rootDec, Declaration* procDec) +{ + if (rootDec == procDec) + return true; + + if (!(procDec->mFlags & DTF_FUNC_ANALYZING)) + { + procDec->mFlags |= DTF_FUNC_ANALYZING; + + bool cycle = false; + for (int i = 0; i < procDec->mCalled.Size(); i++) + { + if (MarkCycle(rootDec, procDec->mCalled[i])) + cycle = true; + } + if (cycle) + procDec->mFlags |= DTF_FUNC_RECURSIVE; + + procDec->mFlags &= ~DTF_FUNC_ANALYZING; + + return cycle; + } + + return false; +} + +void GlobalAnalyzer::MarkRecursions(void) +{ + for (int i = 0; i < mFunctions.Size(); i++) + { + Declaration* cf = mFunctions[i]; + for (int j = 0; j < cf->mCalled.Size(); j++) + { + if (MarkCycle(cf, cf->mCalled[j])) + cf->mFlags |= DTF_FUNC_RECURSIVE; + } + } +} + +void GlobalAnalyzer::CheckFastcall(Declaration* procDec, bool head) +{ + if (!(procDec->mBase->mFlags & DTF_FASTCALL) && (procDec->mType == DT_CONST_FUNCTION) && !(procDec->mFlags & DTF_FUNC_ANALYZING)) + { +// printf("CheckFastcall1 %s %08llx %08llx\n", procDec->mQualIdent->mString, procDec->mFlags, procDec->mBase->mFlags); + + procDec->mFlags |= DTF_FUNC_ANALYZING; + int nbase = 0, nbase2 = 0; + for (int i = 0; i < procDec->mCalled.Size(); i++) + { + Declaration* cf = procDec->mCalled[i]; + + if (cf->mType == DT_TYPE_FUNCTION) + { + for (int i = 0; i < mVariableFunctions.Size(); i++) + { + Declaration* vf = mVariableFunctions[i]; + + if (vf->mBase->IsSame(cf)) + { + CheckFastcall(vf, false); + + int n = vf->mBase->mFastCallSize; + if (n > nbase) + nbase = n; + int n2 = vf->mBase->mFastCallSize2; + if (n2 > nbase2) + nbase2 = n2; + + if (!procDec->mMayCall.Contains(vf)) + procDec->mMayCall.Push(vf); + } + + } + // procDec->mFlags |= DTF_DYNSTACK; + } + else + CheckFastcall(cf, false); + + if (cf->mFlags & DTF_DYNSTACK) + procDec->mFlags |= DTF_DYNSTACK; + + // if (!(cf->mBase->mFlags & DTF_FASTCALL)) + // procDec->mBase->mFlags |= DTF_STACKCALL; + + cf = cf->mBase; + int n = cf->mFastCallSize; + if (n > nbase) + nbase = n; + int n2 = cf->mFastCallSize2; + if (n2 > nbase2) + nbase2 = n2; + } + +// printf("CheckFastcall2 %s %08llx %08llx\n", procDec->mQualIdent->mString, procDec->mFlags, procDec->mBase->mFlags); + + if (procDec->mValue && procDec->mValue->mType == EX_DISPATCH) + { + Declaration* maxf = nullptr, * maxf2 = nullptr, * reff = nullptr; + + bool stackCall = false; + + for (int i = 0; i < procDec->mCalled.Size(); i++) + { + Declaration* cf = procDec->mCalled[i]; + + if (cf->mBase->mFlags & DTF_STACKCALL) + { + stackCall = true; + reff = cf; + } + + if (!maxf) + maxf = cf; + else if (cf->mBase->mFastCallBase > maxf->mBase->mFastCallBase) + maxf = cf; + + if (!maxf2) + maxf2 = cf; + else if (cf->mBase->mFastCallBase2 > maxf2->mBase->mFastCallBase2) + maxf2 = cf; + } + + if (!reff) + reff = maxf; + + for (int i = 0; i < procDec->mCalled.Size(); i++) + { + Declaration* cf = procDec->mCalled[i]; + + if (stackCall) + { + cf->mBase->mFlags &= ~DTF_FASTCALL; + cf->mBase->mFlags |= DTF_STACKCALL; + } + + if (cf != reff) + { + Declaration* fp = cf->mBase->mParams, * mp = reff->mBase->mParams; + while (fp) + { + fp->mVarIndex = mp->mVarIndex; + fp = fp->mNext; + mp = mp->mNext; + } + + assert(!mp); + cf->mBase->mFastCallSize = cf->mFastCallSize = maxf->mBase->mFastCallSize; + } + + if (cf != maxf) + cf->mBase->mFastCallBase = cf->mFastCallBase = maxf->mBase->mFastCallBase; + if (cf != maxf2) + cf->mBase->mFastCallBase2 = cf->mFastCallBase2 = maxf->mBase->mFastCallBase2; + } + + procDec->mFastCallBase = procDec->mBase->mFastCallBase; + procDec->mFastCallSize = procDec->mBase->mFastCallSize; + procDec->mFastCallBase2 = procDec->mBase->mFastCallBase2; + procDec->mFastCallSize2 = procDec->mBase->mFastCallSize2; + procDec->mFlags &= ~DTF_FUNC_ANALYZING; + + return; + } + + procDec->mFastCallBase = nbase; + procDec->mFastCallSize = nbase; + procDec->mBase->mFastCallBase = nbase; + procDec->mBase->mFastCallSize = nbase; + procDec->mFastCallBase2 = nbase2; + procDec->mFastCallSize2 = nbase2; + procDec->mBase->mFastCallBase2 = nbase2; + procDec->mBase->mFastCallSize2 = nbase2; + + procDec->mFlags &= ~DTF_FUNC_ANALYZING; + +// printf("CheckFastcall3 %s %08llx %08llx\n", procDec->mQualIdent->mString, procDec->mFlags, procDec->mBase->mFlags); + + if (procDec->mBase->mFlags & DTF_STACKCALL) + { + procDec->mBase->mFlags |= DTF_STACKCALL; + } + else if (procDec->mFlags & DTF_FUNC_RECURSIVE) + { +// if (head) + procDec->mBase->mFlags |= DTF_STACKCALL; + } + else if (/*!(procDec->mBase->mFlags & DTF_VARIADIC) &&*/ !(procDec->mFlags & DTF_FUNC_VARIABLE) && !(procDec->mFlags & DTF_DYNSTACK)) + { + int nparams = nbase, nparams2 = nbase2; + int numfpzero = BC_REG_FPARAMS_END - BC_REG_FPARAMS; + + if (procDec->mBase->mBase->IsComplexStruct()) + { + if (nbase + 2 <= numfpzero) + nparams += 2; + else + nparams2 += 2; + } + + int cnparams = nparams, cnparams2 = nparams2; + + Declaration* dec = procDec->mBase->mParams; + while (dec) + { + if (!(dec->mFlags & DTF_FPARAM_UNUSED)) + { + // Check for parameter crossing boundary + if ((procDec->mBase->mFlags & DTF_VARIADIC) && !dec->mNext) + cnparams2 += dec->mBase->mSize; + else if (!dec->mBase->IsComplexStruct() && cnparams + dec->mBase->mSize <= numfpzero) + cnparams += dec->mBase->mSize; + else + cnparams2 += dec->mBase->mSize; + } + dec = dec->mNext; + } + + if (procDec->mBase->mFlags & DTF_VARIADIC) + cnparams2 += procDec->mVariadicSize; + + if (cnparams2 == 0 || ((procDec->mFlags & DTF_NATIVE) || (mCompilerOptions & COPT_NATIVE)) && !(procDec->mFlags & DTF_FUNC_INTRCALLED)) + { + dec = procDec->mBase->mParams; + while (dec) + { + if (dec->mFlags & DTF_FPARAM_UNUSED) + { + + } + else if (dec->mForwardParam && !(procDec->mBase->mFlags & DTF_VARIADIC) && (dec->mForwardCall->mBase->mFlags & DTF_FASTCALL) && !(dec->mForwardCall->mFlags & DTF_INLINE)) + { + dec->mVarIndex = dec->mForwardParam->mVarIndex; + } + else + { + dec->mForwardParam = nullptr; + if ((procDec->mBase->mFlags & DTF_VARIADIC) && !dec->mNext) + { + dec->mVarIndex = nparams2 + numfpzero; + nparams2 += dec->mBase->mSize; + } + else if (!dec->mBase->IsComplexStruct() && nparams + dec->mBase->mSize <= numfpzero) + { + dec->mVarIndex = nparams; + nparams += dec->mBase->mSize; + } + else + { + dec->mVarIndex = nparams2 + numfpzero; + nparams2 += dec->mBase->mSize; + } + } + dec = dec->mNext; + } + + if (procDec->mBase->mFlags & DTF_VARIADIC) + nparams2 += procDec->mVariadicSize; + + procDec->mFastCallBase = nbase; + procDec->mFastCallSize = nparams; + procDec->mBase->mFastCallBase = nbase; + procDec->mBase->mFastCallSize = nparams; + procDec->mFastCallBase2 = nbase2; + procDec->mFastCallSize2 = nparams2; + procDec->mBase->mFastCallBase2 = nbase2; + procDec->mBase->mFastCallSize2 = nparams2; + + procDec->mBase->mFlags |= DTF_FASTCALL; +// printf("FASTCALL %s\n", procDec->mQualIdent->mString); + } + else + { +// printf("STACKCALL %s, %d %d\n", procDec->mQualIdent->mString, cnparams, fplimit); + procDec->mBase->mFlags |= DTF_STACKCALL; + } + } + else + { +// printf("STACKCALL %s, F%d\n", procDec->mQualIdent->mString, !!(procDec->mFlags& DTF_FUNC_VARIABLE)); + procDec->mBase->mFlags |= DTF_STACKCALL; + } + } +} + +void GlobalAnalyzer::CheckInterrupt(void) +{ + bool changed = false; + do + { + changed = false; + + for (int i = 0; i < mFunctions.Size(); i++) + { + Declaration* f = mFunctions[i]; + if (f->mFlags & DTF_FUNC_INTRCALLED) + { + for (int j = 0; j < f->mCalled.Size(); j++) + { + Declaration* cf = f->mCalled[j]; + if (!(cf->mFlags & DTF_FUNC_INTRCALLED)) + { + cf->mFlags |= DTF_FUNC_INTRCALLED; + changed = true; + } + } + } + } + + } while (changed); +} + +bool GlobalAnalyzer::IsStackParam(const Declaration* pdec) const +{ + if (pdec->mType == DT_TYPE_STRUCT) + { + if (pdec->mSize > 16) + return true; + if (pdec->mCopyConstructor) + { + if (!((mCompilerOptions & COPT_OPTIMIZE_INLINE) && (pdec->mCopyConstructor->mFlags & DTF_REQUEST_INLINE))) + return true; + } + if (pdec->mDestructor) + { + if (!((mCompilerOptions & COPT_OPTIMIZE_INLINE) && (pdec->mDestructor->mFlags & DTF_REQUEST_INLINE))) + return true; + } + } + + return false; +} + +void GlobalAnalyzer::UndoParamReference(Expression* exp, Declaration * param) +{ + if (exp) + { + if (exp->mType == EX_VARIABLE) + { + if (exp->mDecValue == param) + exp->mDecType = param->mBase; + } + + UndoParamReference(exp->mLeft, param); + UndoParamReference(exp->mRight, param); + } +} + +void GlobalAnalyzer::AnalyzeProcedure(Expression* cexp, Expression* exp, Declaration* dec) +{ + dec->mUseCount++; + + if (dec->mFlags & DTF_FUNC_ANALYZING) + { + dec->mFlags |= DTF_FUNC_RECURSIVE; + dec->mFlags &= ~DTF_FUNC_CONSTEXPR; + } + + if (!(dec->mFlags & DTF_ANALYZED)) + { + dec->mFlags |= DTF_FUNC_ANALYZING; + + if (dec->mFlags & DTF_DEPRECATED) + { + mErrors->Error(dec->mLocation, EWARN_DEFAULT_COPY_DEPRECATED, "Using deprecated function", dec->mQualIdent->mString); + if (cexp) + mErrors->Error(cexp->mLocation, EINFO_CALLED_FROM, "Called from here"); + } + + mFunctions.Push(dec); + + Declaration* pdec = dec->mBase->mParams; + while (pdec) + { + if (IsStackParam(pdec->mBase)) + dec->mBase->mFlags |= DTF_STACKCALL; + pdec = pdec->mNext; + } + + dec->mFlags |= DTF_ANALYZED; + dec->mFlags |= DTF_FUNC_INTRSAVE; + if (dec->mBase->mBase && dec->mBase->mBase->mType != DT_TYPE_VOID) + dec->mFlags |= DTF_FUNC_NO_RETURN; + + if (dec->mFlags & DTF_INTERRUPT) + dec->mFlags |= DTF_FUNC_INTRCALLED; + + if ((dec->mFlags & DTF_INTRINSIC) && !dec->mValue) + dec->mFlags |= DTF_FUNC_CONSTEXPR; + else if (dec->mFlags & DTF_DEFINED) + { + if (mCompilerOptions & COPT_OPTIMIZE_CONST_EXPRESSIONS) + dec->mFlags |= DTF_FUNC_CONSTEXPR; + dec->mFlags |= DTF_FUNC_PURE; + Analyze(exp, dec, 0); + + Declaration* pdec = dec->mBase->mParams; + int vi = 0; + while (pdec) + { + pdec->mVarIndex += vi; + if (pdec->mBase->mType == DT_TYPE_REFERENCE && pdec->mBase->mBase->IsValueType() && !(pdec->mFlags & DTF_VAR_ADDRESS) && (pdec->mBase->mBase->mFlags & DTF_CONST)) + { + pdec->mBase = pdec->mBase->mBase; + pdec->mSize = pdec->mBase->mSize; + vi += pdec->mSize - 2; + + UndoParamReference(exp, pdec); + } + else if (pdec->mBase->mType == DT_TYPE_RVALUEREF && pdec->mBase->mBase->IsValueType() && !(pdec->mFlags & DTF_VAR_ADDRESS)) + { + pdec->mBase = pdec->mBase->mBase; + pdec->mSize = pdec->mBase->mSize; + vi += pdec->mSize - 2; + + UndoParamReference(exp, pdec); + } + pdec = pdec->mNext; + } + } + else + { + mErrors->Error(dec->mLocation, EERR_UNDEFINED_OBJECT, "Calling undefined function", dec->FullIdent()); + if (cexp) + mErrors->Error(cexp->mLocation, EINFO_CALLED_FROM, "Called from here"); + + } + + dec->mFlags &= ~DTF_FUNC_ANALYZING; + } +} + + +void GlobalAnalyzer::AnalyzeAssembler(Expression* exp, Declaration* procDec) +{ + while (exp) + { + if (procDec) + procDec->mComplexity += 2; + + if (exp->mLeft && exp->mLeft->mDecValue) + { + Declaration* adec = exp->mLeft->mDecValue; + if (adec->mType == DT_LABEL_REF) + { + + } + else if (adec->mType == DT_VARIABLE_REF) + { + if (adec->mBase->mFlags & DTF_GLOBAL) + { + AnalyzeGlobalVariable(adec->mBase); + adec->mBase->mFlags |= DTF_VAR_ALIASING; + } + } + else if (adec->mType == DT_LABEL) + { + + } + else if (adec->mType == DT_VARIABLE) + { + if (adec->mFlags & DTF_GLOBAL) + { + AnalyzeGlobalVariable(adec); + adec->mFlags |= DTF_VAR_ALIASING; + } + } + else if (adec->mType == DT_FUNCTION_REF) + { + AnalyzeProcedure(exp, adec->mBase->mValue, adec->mBase); + RegisterProc(adec->mBase); + } + else if (adec->mType == DT_CONST_FUNCTION) + { + AnalyzeProcedure(exp, adec->mValue, adec); + RegisterCall(procDec, adec); + } + } + + exp = exp->mRight; + } +} + +void GlobalAnalyzer::AnalyzeGlobalVariable(Declaration* dec) +{ + while (dec->mType == DT_VARIABLE_REF) + dec = dec->mBase; + + dec->mUseCount++; + + if (!(dec->mFlags & DTF_ANALYZED)) + { + dec->mFlags |= DTF_ANALYZED; + + mGlobalVariables.Push(dec); + + if (dec->mValue) + { + Analyze(dec->mValue, dec, 0); + } + } +} + +void GlobalAnalyzer::AnalyzeInit(Declaration* mdec) +{ + while (mdec) + { + if (mdec->mValue) + RegisterProc(Analyze(mdec->mValue, mdec, 0)); + else if (mdec->mParams) + AnalyzeInit(mdec->mParams); + mdec = mdec->mNext; + } +} + +Declaration * GlobalAnalyzer::Analyze(Expression* exp, Declaration* procDec, uint32 flags) +{ + Declaration* ldec, * rdec; + + exp->mFlags = flags; + + switch (exp->mType) + { + case EX_ERROR: + case EX_VOID: + break; + case EX_CONSTANT: + if (mCompilerOptions & COPT_DEBUGINFO) + exp->mDecValue->mReferences.Push(exp); + + if (exp->mDecValue->mType == DT_CONST_FUNCTION) + AnalyzeProcedure(exp, exp->mDecValue->mValue, exp->mDecValue); + else if (exp->mDecValue->mType == DT_CONST_STRUCT) + { + AnalyzeInit(exp->mDecValue->mParams); + } + else if (exp->mDecValue->mType == DT_CONST_POINTER) + { + ldec = Analyze(exp->mDecValue->mValue, procDec, ANAFL_LHS | ANAFL_ALIAS); + if (ldec->mType == DT_VARIABLE) + ldec->mFlags |= DTF_VAR_ALIASING; + RegisterProc(ldec); + } + else if (exp->mDecValue->mType == DT_CONST_ADDRESS) + { + procDec->mFlags &= ~DTF_FUNC_CONSTEXPR; + } + else if (exp->mDecValue->mType == DT_CONST_ASSEMBLER) + { + AnalyzeAssembler(exp->mDecValue->mValue, procDec); + } + + return exp->mDecValue; + case EX_VARIABLE: + { + Declaration* vdec = exp->mDecValue; + while (vdec->mType == DT_VARIABLE_REF) + vdec = vdec->mBase; + + if (exp->mDecType->IsSimpleType()) + procDec->mComplexity += 5 * exp->mDecType->mSize; + else + procDec->mComplexity += 10; + + if (mCompilerOptions & COPT_DEBUGINFO) + vdec->mReferences.Push(exp); + + if (flags & ANAFL_ALIAS) + { + vdec->mFlags |= DTF_VAR_ALIASING; + } + + if ((vdec->mFlags & DTF_STATIC) || (vdec->mFlags & DTF_GLOBAL)) + { + Declaration* type = exp->mDecValue->mBase; + while (type->mType == DT_TYPE_ARRAY) + type = type->mBase; + + if (!(type->mFlags & DTF_CONST)) + procDec->mFlags &= ~DTF_FUNC_CONSTEXPR; + + if (flags & ANAFL_LHS) + procDec->mFlags &= ~DTF_FUNC_PURE; + + AnalyzeGlobalVariable(vdec); + } + else + { + if (flags & ANAFL_LHS) + vdec->mFlags |= DTF_VAR_ADDRESS; + + if (!(vdec->mFlags & DTF_ANALYZED)) + { + procDec->mLocalSize += vdec->mSize; + vdec->mFlags |= DTF_ANALYZED; + } + } + return vdec; + } + case EX_INITIALIZATION: + case EX_ASSIGNMENT: + procDec->mComplexity += 5 * exp->mLeft->mDecType->mSize; + + ldec = Analyze(exp->mLeft, procDec, ANAFL_LHS | ANAFL_RHS); + rdec = Analyze(exp->mRight, procDec, ANAFL_RHS); + if (exp->mLeft->mType == EX_VARIABLE && exp->mRight->mType == EX_CALL && exp->mLeft->mDecType->mType == DT_TYPE_STRUCT) + exp->mLeft->mDecValue->mFlags |= DTF_VAR_ALIASING; + RegisterProc(rdec); + return ldec; + + case EX_BINARY: + procDec->mComplexity += 10 * exp->mDecType->mSize; + + ldec = Analyze(exp->mLeft, procDec, flags & ~ANAFL_ALIAS); + rdec = Analyze(exp->mRight, procDec, flags & ~ANAFL_ALIAS); + return ldec; + + case EX_RELATIONAL: + procDec->mComplexity += 10 * exp->mLeft->mDecType->mSize; + + ldec = Analyze(exp->mLeft, procDec, ANAFL_RHS); + rdec = Analyze(exp->mRight, procDec, ANAFL_RHS); + return TheBoolTypeDeclaration; + + case EX_PREINCDEC: + procDec->mComplexity += 10 * exp->mLeft->mDecType->mSize; + + return Analyze(exp->mLeft, procDec, ANAFL_LHS | ANAFL_RHS); + case EX_PREFIX: + if (exp->mToken == TK_BINARY_AND) + { + ldec = Analyze(exp->mLeft, procDec, ANAFL_LHS | ANAFL_ALIAS); + if (ldec->mType == DT_VARIABLE) + ldec->mFlags |= DTF_VAR_ALIASING; + } + else if (exp->mToken == TK_MUL) + { + ldec = Analyze(exp->mLeft, procDec, 0); + procDec->mFlags &= ~DTF_FUNC_CONSTEXPR; + if (flags & ANAFL_LHS) + procDec->mFlags &= ~DTF_FUNC_PURE; + + return exp->mDecType; + } + else if (exp->mToken == TK_BANKOF) + { + return TheUnsignedCharTypeDeclaration; + } + else if (exp->mToken == TK_SIZEOF) + { + return TheUnsignedIntTypeDeclaration; + } + else + { + procDec->mComplexity += 10 * exp->mLeft->mDecType->mSize; + return Analyze(exp->mLeft, procDec, 0); + } + break; + case EX_POSTFIX: + procDec->mComplexity += 10 * exp->mLeft->mDecType->mSize; + break; + case EX_POSTINCDEC: + procDec->mComplexity += 10 * exp->mLeft->mDecType->mSize; + + return Analyze(exp->mLeft, procDec, ANAFL_LHS | ANAFL_RHS); + case EX_INDEX: + procDec->mComplexity += 10 * exp->mRight->mDecType->mSize; + + ldec = Analyze(exp->mLeft, procDec, flags & ~ANAFL_ALIAS); + if (ldec->mType == DT_VARIABLE || ldec->mType == DT_ARGUMENT) + { + ldec = ldec->mBase; + if (ldec->mType == DT_TYPE_POINTER) + { + if (flags & ANAFL_LHS) + procDec->mFlags &= ~DTF_FUNC_PURE; + procDec->mFlags &= ~DTF_FUNC_CONSTEXPR; + } + } + rdec = Analyze(exp->mRight, procDec, 0); + if (ldec->mBase) + return ldec->mBase; + break; + case EX_QUALIFY: + Analyze(exp->mLeft, procDec, flags); + return exp->mDecValue->mBase; + case EX_DISPATCH: + procDec->mFlags |= DTF_PREVENT_INLINE; + Analyze(exp->mLeft, procDec, flags & ~ANAFL_ALIAS); +// RegisterCall(procDec, exp->mLeft->mDecType); + break; + case EX_VCALL: + exp->mType = EX_CALL; + exp->mLeft->mDecValue = exp->mLeft->mDecValue->mVTable; + // intentional fall through + case EX_CALL: + case EX_INLINE: + procDec->mComplexity += 10; + + ldec = Analyze(exp->mLeft, procDec, 0); + if ((ldec->mFlags & DTF_INTRINSIC) && !ldec->mValue) + { + + } + else + { + if (exp->mType == EX_INLINE) + { + for (int i = 0; i < ldec->mCalled.Size(); i++) + RegisterCall(procDec, ldec->mCalled[i]); + } + else + RegisterCall(procDec, ldec); + if (!(GetProcFlags(ldec) & (DTF_FUNC_INTRSAVE | DTF_INTERRUPT))) + { + procDec->mFlags &= ~DTF_FUNC_INTRSAVE; + if (procDec->mFlags & DTF_INTERRUPT) + mErrors->Error(exp->mLocation, EWARN_NOT_INTERRUPT_SAFE, "Calling non interrupt safe function", ldec->mQualIdent); + } + if (!(GetProcFlags(ldec) & DTF_FUNC_PURE)) + procDec->mFlags &= ~DTF_FUNC_PURE; + } + + if (exp->mRight) + { + int variadicSize = 0; + + // Check for struct to struct forwarding + Expression* rex = exp->mRight; + Declaration* pdec = ldec->mBase->mParams; + while (rex) + { + Expression* pex = rex->mType == EX_LIST ? rex->mLeft : rex; + + procDec->mComplexity += 5 * pex->mDecType->mSize; + + if (pdec && !(ldec->mBase->mFlags & DTF_VARIADIC) && !(ldec->mFlags & (DTF_INTRINSIC | DTF_FUNC_ASSEMBLER))) + { +#if 1 + if (mCompilerOptions & COPT_OPTIMIZE_CONST_PARAMS) + { + if (!(pdec->mFlags & DTF_FPARAM_NOCONST)) + { + if (pex->mType == EX_CONSTANT || pex->mType == EX_VARIABLE && pex->mDecValue->mBase->mType == DT_TYPE_ARRAY && pdec->mBase->mType == DT_TYPE_POINTER && (pex->mDecValue->mFlags & (DTF_GLOBAL | DTF_STATIC))) + { + if (pdec->mFlags & DTF_FPARAM_CONST) + { + if (!pex->mDecValue->IsSameValue(pdec->mValue->mDecValue)) + { + pdec->mFlags |= DTF_FPARAM_NOCONST; + pdec->mFlags &= ~DTF_FPARAM_CONST; + } + } + else + { + pdec->mValue = pex; + pdec->mFlags |= DTF_FPARAM_CONST; + } + } + else + { + pdec->mFlags |= DTF_FPARAM_NOCONST; + pdec->mFlags &= ~DTF_FPARAM_CONST; + } + } + } + else + { + pdec->mFlags |= DTF_FPARAM_NOCONST; + pdec->mFlags &= ~DTF_FPARAM_CONST; + } +#endif + } + + if (pdec && pdec->mBase->mType == DT_TYPE_STRUCT && pdec->mBase->mCopyConstructor) + { + if (pdec->mBase->mMoveConstructor) + { + AnalyzeProcedure(exp, pdec->mBase->mMoveConstructor->mValue, pdec->mBase->mMoveConstructor); + RegisterCall(procDec, pdec->mBase->mMoveConstructor); + } + AnalyzeProcedure(exp, pdec->mBase->mCopyConstructor->mValue, pdec->mBase->mCopyConstructor); + RegisterCall(procDec, pdec->mBase->mCopyConstructor); + } + + if (pex->mType == EX_CALL && IsStackParam(pex->mDecType) && !(pdec && (pdec->mBase->mType == DT_TYPE_REFERENCE || pdec->mBase->mType == DT_TYPE_RVALUEREF))) + ldec->mBase->mFlags |= DTF_STACKCALL; + + RegisterProc(Analyze(pex, procDec, (pdec && pdec->mBase->IsReference()) ? ANAFL_LHS : ANAFL_RHS)); + + if (!pdec && (ldec->mBase->mFlags & DTF_VARIADIC)) + { + if (pex->mDecType->IsIntegerType() && pex->mDecType->mSize < 2) + variadicSize += 2; + else if (pex->mDecType->mType == DT_TYPE_ARRAY) + variadicSize += 2; + else + variadicSize += pex->mDecType->mSize; + } + + if (pdec) + pdec = pdec->mNext; + + if (rex->mType == EX_LIST) + rex = rex->mRight; + else + rex = nullptr; + } + + if (ldec->mType == DT_CONST_FUNCTION) + { + if (variadicSize > ldec->mVariadicSize) + ldec->mVariadicSize = variadicSize; + } + } + break; + case EX_LIST: + case EX_COMMA: + RegisterProc(Analyze(exp->mLeft, procDec, 0)); + return Analyze(exp->mRight, procDec, 0); + case EX_RETURN: + if (exp->mLeft) + { + RegisterProc(Analyze(exp->mLeft, procDec, procDec->mBase->mBase->IsReference() ? ANAFL_LHS : ANAFL_RHS)); + if (procDec->mBase->mBase && procDec->mBase->mBase->mType == DT_TYPE_STRUCT && procDec->mBase->mBase->mCopyConstructor) + { + if (procDec->mBase->mBase->mMoveConstructor) + { + AnalyzeProcedure(exp, procDec->mBase->mBase->mMoveConstructor->mValue, procDec->mBase->mBase->mMoveConstructor); + RegisterCall(procDec, procDec->mBase->mBase->mMoveConstructor); + } + AnalyzeProcedure(exp, procDec->mBase->mBase->mCopyConstructor->mValue, procDec->mBase->mBase->mCopyConstructor); + RegisterCall(procDec, procDec->mBase->mBase->mCopyConstructor); + } + + procDec->mFlags &= ~DTF_FUNC_NO_RETURN; + } + break; + case EX_SEQUENCE: + do + { + if (exp->mType == EX_SEQUENCE) + { + if (exp->mLeft) + ldec = Analyze(exp->mLeft, procDec, 0); + exp = exp->mRight; + } + else + return Analyze(exp, procDec, 0); + + } while (exp); + break; + + case EX_SCOPE: + Analyze(exp->mLeft, procDec, 0); + break; + + case EX_CONSTRUCT: + if (exp->mLeft->mLeft) + Analyze(exp->mLeft->mLeft, procDec, 0); + if (exp->mLeft->mRight) + Analyze(exp->mLeft->mRight, procDec, 0); + if (exp->mRight) + return Analyze(exp->mRight, procDec, 0); + break; + + case EX_CLEANUP: + Analyze(exp->mRight, procDec, 0); + return Analyze(exp->mLeft, procDec, flags & ~ANAFL_ALIAS); + + case EX_WHILE: + procDec->mFlags &= ~DTF_FUNC_CONSTEXPR; + + procDec->mComplexity += 20; + + ldec = Analyze(exp->mLeft, procDec, 0); + rdec = Analyze(exp->mRight, procDec, 0); + break; + case EX_IF: + procDec->mComplexity += 20; + + ldec = Analyze(exp->mLeft, procDec, 0); + rdec = Analyze(exp->mRight->mLeft, procDec, 0); + if (exp->mRight->mRight) + rdec = Analyze(exp->mRight->mRight, procDec, 0); + break; + case EX_ELSE: + break; + case EX_FOR: + procDec->mFlags &= ~DTF_FUNC_CONSTEXPR; + + procDec->mComplexity += 30; + + if (exp->mLeft->mRight) + ldec = Analyze(exp->mLeft->mRight, procDec, 0); + if (exp->mLeft->mLeft->mLeft) + ldec = Analyze(exp->mLeft->mLeft->mLeft, procDec, 0); + rdec = Analyze(exp->mRight, procDec, 0); + if (exp->mLeft->mLeft->mRight) + ldec = Analyze(exp->mLeft->mLeft->mRight, procDec, 0); + break; + case EX_FORBODY: + ldec = Analyze(exp->mLeft, procDec, 0); + if (exp->mRight) + Analyze(exp->mRight, procDec, 0); + break; + case EX_DO: + procDec->mComplexity += 20; + + ldec = Analyze(exp->mLeft, procDec, 0); + rdec = Analyze(exp->mRight, procDec, 0); + break; + case EX_BREAK: + case EX_CONTINUE: + case EX_ASSUME: + break; + case EX_TYPE: + break; + case EX_TYPECAST: + return Analyze(exp->mLeft, procDec, flags); + break; + case EX_LOGICAL_AND: + ldec = Analyze(exp->mLeft, procDec, 0); + rdec = Analyze(exp->mRight, procDec, 0); + break; + case EX_LOGICAL_OR: + ldec = Analyze(exp->mLeft, procDec, 0); + rdec = Analyze(exp->mRight, procDec, 0); + break; + case EX_LOGICAL_NOT: + ldec = Analyze(exp->mLeft, procDec, 0); + break; + case EX_ASSEMBLER: + procDec->mFlags |= DTF_FUNC_ASSEMBLER; + procDec->mFlags &= ~DTF_FUNC_CONSTEXPR; + procDec->mFlags &= ~DTF_FUNC_PURE; + AnalyzeAssembler(exp, procDec); + break; + case EX_UNDEFINED: + break; + case EX_SWITCH: + ldec = Analyze(exp->mLeft, procDec, 0); + exp = exp->mRight; + while (exp) + { + procDec->mComplexity += 10; + + if (exp->mLeft->mRight) + rdec = Analyze(exp->mLeft->mRight, procDec, 0); + exp = exp->mRight; + } + break; + case EX_CASE: + break; + case EX_DEFAULT: + break; + case EX_CONDITIONAL: + procDec->mComplexity += exp->mDecType->mSize * 10; + + ldec = Analyze(exp->mLeft, procDec, 0); + RegisterProc(Analyze(exp->mRight->mLeft, procDec, flags)); + RegisterProc(Analyze(exp->mRight->mRight, procDec, flags)); + break; + } + + return TheVoidTypeDeclaration; +} + +uint64 GlobalAnalyzer::GetProcFlags(Declaration* to) const +{ + if (to->mType == DT_CONST_FUNCTION) + return to->mFlags; + else if (to->mType == DT_TYPE_FUNCTION) + return to->mFlags; + else if (to->mType == DT_TYPE_POINTER && to->mBase->mType == DT_TYPE_FUNCTION) + return GetProcFlags(to->mBase); + else if (to->mType == DT_VARIABLE || to->mType == DT_ARGUMENT) + return GetProcFlags(to->mBase); + else + return 0; +} + +void GlobalAnalyzer::RegisterCall(Declaration* from, Declaration* to) +{ + if (from) + { + if (to->mType == DT_VARIABLE || to->mType == DT_ARGUMENT) + to = to->mBase; + + if (to->mType == DT_CONST_FUNCTION) + { + if (to->mFlags & DTF_DYNSTACK) + from->mFlags |= DTF_DYNSTACK; + + if (!(to->mFlags & DTF_FUNC_CONSTEXPR)) + from->mFlags &= ~DTF_FUNC_CONSTEXPR; + + if (to->mCallers.Size() == 0) + mCalledFunctions.Push(to); + to->mCallers.Push(from); + if (from->mCalled.Size() == 0) + mCallingFunctions.Push(from); + from->mCalled.Push(to); + } + else if (to->mType == DT_TYPE_FUNCTION) + { + from->mFlags &= ~DTF_FUNC_CONSTEXPR; + if (from->mCalled.Size() == 0) + mCallingFunctions.Push(from); + from->mCalled.Push(to); + } + else if (to->mType == DT_TYPE_POINTER && to->mBase->mType == DT_TYPE_FUNCTION) + { + from->mFlags &= ~DTF_FUNC_CONSTEXPR; + if (from->mCalled.Size() == 0) + mCallingFunctions.Push(from); + from->mCalled.Push(to->mBase); + } + } +} + +void GlobalAnalyzer::RegisterProc(Declaration* to) +{ + if (to->mType == DT_CONST_FUNCTION) + { +#if 1 + if (to->mBase->mFlags & DTF_VIRTUAL) + { + + } + else +#endif + if (!(to->mFlags & DTF_FUNC_VARIABLE)) + { + to->mFlags |= DTF_FUNC_VARIABLE; + mVariableFunctions.Push(to); + Declaration* pdec = to->mParams; + while (pdec) + { + pdec->mFlags |= DTF_FPARAM_NOCONST; + pdec->mFlags &= ~DTF_FPARAM_CONST; + pdec = pdec->mNext; + } + } + } +} diff --git a/oscar64/oscar64/GlobalAnalyzer.h b/oscar64/oscar64/GlobalAnalyzer.h new file mode 100644 index 0000000..d080dc5 --- /dev/null +++ b/oscar64/oscar64/GlobalAnalyzer.h @@ -0,0 +1,48 @@ +#pragma once + +#include "Declaration.h" +#include "Linker.h" +#include "CompilerTypes.h" + +class GlobalAnalyzer +{ +public: + GlobalAnalyzer(Errors* errors, Linker* linker); + ~GlobalAnalyzer(void); + + void DumpCallGraph(void); + void TopoSort(Declaration * procDec); + void AutoInline(void); + void CheckFastcall(Declaration* procDec, bool head); + void CheckInterrupt(void); + void AutoZeroPage(LinkerSection * lszp, int zpsize); + void MarkRecursions(void); + + void AnalyzeProcedure(Expression* cexp, Expression* exp, Declaration* procDec); + void AnalyzeAssembler(Expression* exp, Declaration* procDec); + void AnalyzeGlobalVariable(Declaration* dec); + + uint64 mCompilerOptions; + +protected: + Errors* mErrors; + Linker* mLinker; + + GrowingArray mCalledFunctions, mCallingFunctions, mVariableFunctions, mFunctions, mTopoFunctions; + GrowingArray mGlobalVariables; + + void AnalyzeInit(Declaration* mdec); + int CallerInvokes(Declaration* called); + int CallerInvokes(Declaration* caller, Declaration* called); + + Declaration* Analyze(Expression* exp, Declaration* procDec, uint32 flags); + + bool IsStackParam(const Declaration* pdec) const; + bool MarkCycle(Declaration* rootDec, Declaration* procDec); + + uint64 GetProcFlags(Declaration* to) const; + void RegisterCall(Declaration* from, Declaration* to); + void RegisterProc(Declaration* to); + void UndoParamReference(Expression* ex, Declaration* param); +}; + diff --git a/oscar64/oscar64/GlobalOptimizer.cpp b/oscar64/oscar64/GlobalOptimizer.cpp new file mode 100644 index 0000000..758b539 --- /dev/null +++ b/oscar64/oscar64/GlobalOptimizer.cpp @@ -0,0 +1,1752 @@ +#include "GlobalOptimizer.h" +#include "Constexpr.h" + + +#define DUMP_OPTS 0 + + +static const uint64 OPTF_ANALYZED = (1ULL << 0); +static const uint64 OPTF_ANALYZING = (1ULL << 1); +static const uint64 OPTF_RECURSIVE = (1ULL << 2); +static const uint64 OPTF_FUNC_VARIABLE = (1ULL << 3); +static const uint64 OPTF_CALLED = (1ULL << 4); +static const uint64 OPTF_CALLING = (1ULL << 5); +static const uint64 OPTF_VAR_MODIFIED = (1ULL << 6); +static const uint64 OPTF_VAR_ADDRESS = (1ULL << 7); +static const uint64 OPTF_VAR_USED = (1ULL << 8); +static const uint64 OPTF_VAR_CONST = (1ULL << 9); +static const uint64 OPTF_VAR_NOCONST = (1ULL << 10); +static const uint64 OPTF_SINGLE_RETURN = (1ULL << 11); +static const uint64 OPTF_MULTI_RETURN = (1ULL << 12); +static const uint64 OPTF_VAR_NO_FORWARD = (1UL << 13); +static const uint64 OPTF_SINGLE_CALL = (1ULL << 14); +static const uint64 OPTF_MULTI_CALL = (1ULL << 15); +static const uint64 OPTF_CONST_FUNCTION = (1ULL << 16); +static const uint64 OPTF_VAR_RANGE_LIMITED = (1ULL << 17); +static const uint64 OPTF_VAR_COPY = (1ULL << 18); +static const uint64 OPTF_VAR_INDEXED = (1ULL << 19); + +GlobalOptimizer::GlobalOptimizer(Errors* errors, Linker* linker) + : mErrors(errors), mLinker(linker) +{ + +} + +GlobalOptimizer::~GlobalOptimizer(void) +{ + +} + +void GlobalOptimizer::Reset(void) +{ + for (int i = 0; i < mFunctions.Size(); i++) + { + Declaration* func = mFunctions[i]; + Declaration* ftype = func->mBase; + func->mOptFlags = 0; + ftype->mOptFlags = 0; + func->mReturn = nullptr; + + Declaration* pdec = ftype->mParams; + while (pdec) + { + pdec->mOptFlags = 0; + pdec->mForwardParam = nullptr; + pdec = pdec->mNext; + } + } + + for (int i = 0; i < mGlobalVariables.Size(); i++) + { + mGlobalVariables[i]->mOptFlags = 0; + } + + mFunctions.SetSize(0); + mGlobalVariables.SetSize(0); + mCalledFunctions.SetSize(0); + mCallingFunctions.SetSize(0); +} + +void GlobalOptimizer::PropagateParamCommas(Expression*& fexp, Expression*& exp) +{ + PropagateCommas(exp); + if (exp->mType == EX_COMMA) + { + Expression* cexp = exp; + exp = cexp->mRight; + cexp->mRight = fexp; + fexp = cexp; + fexp->mDecType = cexp->mRight->mDecType; + } +} + +void GlobalOptimizer::PropagateCommas(Expression*& exp) +{ + if (exp->mType == EX_PREFIX && exp->mLeft->mType == EX_COMMA) + { + Expression* cexp = exp->mLeft; + exp->mLeft = cexp->mRight; + cexp->mDecType = exp->mDecType; + cexp->mRight = exp->ConstantFold(mErrors, nullptr); + exp = cexp; + } + else if (exp->mType == EX_CALL) + { + Expression* pexp = exp; + Expression* rexp = pexp->mRight; + if (rexp) + { + while (rexp && rexp->mType == EX_LIST) + { + PropagateParamCommas(exp, rexp->mLeft); + if (rexp->mLeft-> mType == EX_COMMA) + pexp = rexp; + rexp = rexp->mRight; + } + if (rexp) + PropagateParamCommas(exp, pexp->mRight); + } + } + else + { + if (exp->mLeft) + PropagateCommas(exp->mLeft); + if (exp->mRight) + PropagateCommas(exp->mRight); + } + +} + +bool GlobalOptimizer::CheckSplitAggregates(Declaration* func, Expression* exp) +{ + bool changed = false; + + if (exp->mType == EX_VARIABLE) + { +#if 1 + Declaration* dvar = exp->mDecValue; + if (dvar->mType == DT_VARIABLE && dvar->mIdent && !(dvar->mFlags & (DTF_GLOBAL | DTF_STATIC))) + { + if (exp->mDecType->mType == DT_TYPE_ARRAY && exp->mDecType->mSize <= 16) + { + if (!(dvar->mOptFlags & OPTF_VAR_ADDRESS) && !(dvar->mOptFlags & OPTF_VAR_COPY) && (dvar->mOptFlags & OPTF_VAR_USED)) + { +// printf("Split %s\n", exp->mDecValue->mIdent->mString); + ExpandingArray evars; + int num = exp->mDecType->mSize / exp->mDecType->mBase->mSize; + for (int i = 0; i < num; i++) + { + Declaration* dec = new Declaration(dvar->mLocation, DT_VARIABLE); + char str[8]; + sprintf_s(str, "[%d]", i); + dec->mIdent = dvar->mIdent->Mangle(str); + dec->mBase = exp->mDecType->mBase; + dec->mSize = dec->mSize; + dec->mVarIndex = func->mNumVars++; + evars.Push(dec); + } + + SplitLocalAggregate(func->mValue, dvar, evars); + changed = true; + } + } + else if (exp->mDecType->mType == DT_TYPE_STRUCT && exp->mDecType->mSize <= 16) + { + if (!(dvar->mOptFlags & OPTF_VAR_ADDRESS) && !(dvar->mOptFlags & OPTF_VAR_COPY) && (dvar->mOptFlags & OPTF_VAR_USED)) + { + Declaration* edec = exp->mDecType->mParams; + while (edec && !edec->mBits) + edec = edec->mNext; + if (!edec) + { +// printf("Split %s in %s\n", exp->mDecValue->mIdent->mString, func->mQualIdent->mString); + ExpandingArray evars; + evars.SetSize(exp->mDecType->mSize); + Declaration* edec = exp->mDecType->mParams; + while (edec) + { + Declaration* dec = new Declaration(dvar->mLocation, DT_VARIABLE); + char str[240]; + sprintf_s(str, ".%s", edec->mIdent->mString); + dec->mIdent = dvar->mIdent->Mangle(str); + dec->mBase = edec->mBase; + dec->mSize = dec->mSize; + dec->mVarIndex = func->mNumVars++; + evars[edec->mOffset] = dec; + edec = edec->mNext; + } + + SplitLocalAggregate(func->mValue, dvar, evars); + changed = true; + } + } + } + } +#endif + } + else + { + if (exp->mLeft && CheckSplitAggregates(func, exp->mLeft)) changed = true; + if (exp->mRight && CheckSplitAggregates(func, exp->mRight)) changed = true; + } + + return changed; +} + +void GlobalOptimizer::SplitLocalAggregate(Expression* exp, Declaration* var, ExpandingArray& evars) +{ + if (exp->mType == EX_INDEX) + { + if (exp->mLeft->mType == EX_VARIABLE && exp->mLeft->mDecValue == var) + { + exp->mType = EX_VARIABLE; + exp->mDecValue = evars[int(exp->mRight->mDecValue->mInteger)]; + exp->mLeft = nullptr; + exp->mRight = nullptr; + } + } + else if (exp->mType == EX_QUALIFY) + { + if (exp->mLeft->mType == EX_VARIABLE && exp->mLeft->mDecValue == var) + { + exp->mType = EX_VARIABLE; + exp->mDecValue = evars[int(exp->mDecValue->mOffset)]; + exp->mLeft = nullptr; + exp->mRight = nullptr; + } + } + else if (exp->mType == EX_VARIABLE && exp->mDecValue == var) + { + exp->mType = EX_VOID; + } + + if (exp->mLeft) + SplitLocalAggregate(exp->mLeft, var, evars); + if (exp->mRight) + SplitLocalAggregate(exp->mRight, var, evars); +} + +bool GlobalOptimizer::CheckConstFunction(Expression* exp) +{ + if (exp->mType == EX_CALL || exp->mType == EX_INLINE) + { + if (exp->mLeft->mType == EX_CONSTANT) + { + Declaration* pcall = exp->mLeft->mDecValue; + if (!(pcall->mOptFlags & OPTF_CONST_FUNCTION)) + return false; + } + else + return false; + } + else if (exp->mType == EX_DISPATCH) + return false; + else if (exp->mType == EX_RETURN && !exp->mLeft) + return false; + + if (exp->mLeft && !CheckConstFunction(exp->mLeft)) + return false; + if (exp->mRight && !CheckConstFunction(exp->mRight)) + return false; + + return true; + +} + +bool GlobalOptimizer::CheckUnusedReturns(Expression*& exp) +{ + bool changed = false; + + if (exp->mType == EX_CALL && exp->mDecType && exp->mDecType->mType != DT_TYPE_VOID) + { + if (exp->mLeft->mType == EX_CONSTANT) + { + Declaration* pcall = exp->mLeft->mDecValue; + if (pcall->mBase->mBase->mType == DT_TYPE_VOID) + { + Expression* lexp = new Expression(exp->mLocation, EX_COMMA); + lexp->mLeft = exp; + lexp->mRight = new Expression(exp->mLocation, EX_CONSTANT); + lexp->mRight->mDecValue = TheZeroIntegerConstDeclaration->ConstCast(exp->mDecType); + lexp->mRight->mDecType = exp->mDecType; + lexp->mDecType = exp->mDecType; + exp->mDecType = TheVoidTypeDeclaration; + exp = lexp; + return true; + } + } + } + + if (exp->mLeft && CheckUnusedReturns(exp->mLeft)) + changed = true; + if (exp->mRight && CheckUnusedReturns(exp->mRight)) + changed = true; + + return changed; +} + +bool GlobalOptimizer::ReplaceConstCalls(Expression*& exp) +{ + bool changed = false; + + if (exp->mType == EX_CALL && exp->mDecType && exp->mDecType->mType != DT_TYPE_VOID) + { + if (exp->mLeft->mType == EX_CONSTANT) + { + Declaration* pcall = exp->mLeft->mDecValue; + if (pcall->mOptFlags & OPTF_CONST_FUNCTION) + { + Expression* args = exp->mRight; + bool constargs = true; + while (args && constargs) + { + if (args->mType == EX_LIST) + { + if (args->mLeft->mType != EX_CONSTANT) + constargs = false; + else + args = args->mRight; + } + else if (args->mType != EX_CONSTANT) + constargs = false; + else + args = nullptr; + + } + + if (constargs) + { + ConstexprInterpreter cinter(exp->mLocation, mErrors, pcall->mSection); + Expression * nexp = cinter.EvalCall(exp); + + if (nexp && nexp != exp) + { + exp = nexp; + changed = true; + } + } + } + } + } + + if (exp->mLeft && ReplaceConstCalls(exp->mLeft)) + changed = true; + if (exp->mRight && ReplaceConstCalls(exp->mRight)) + changed = true; + + return changed; +} + + +bool GlobalOptimizer::EstimateCost(Expression* exp, Declaration* vindex, int64& cycles, int64& bytes, bool &cconst) +{ + cycles = 0; + bytes = 0; + cconst = false; + + if (exp) + { + int64 lcycles, lbytes, rcycles, rbytes; + bool lconst, rconst; + + switch (exp->mType) + { + case EX_CONSTANT: + cconst = true; + return true; + + case EX_VARIABLE: + if (exp->mDecValue != vindex) + { + if (exp->mDecValue->mSize > 1 || (exp->mDecValue->mFlags & (DTF_GLOBAL | DTF_STATIC))) + { + cycles = 2; + bytes = 4; + } + else if (!vindex) + { + cycles = 1; + bytes = 1; + } + } + else + cconst = true; + return true; + + case EX_ASSIGNMENT: + case EX_INITIALIZATION: + if (exp->mLeft->mType == EX_VARIABLE && exp->mLeft->mDecValue == vindex) + return false; + + if (EstimateCost(exp->mLeft, vindex, lcycles, lbytes, lconst) && EstimateCost(exp->mRight, vindex, rcycles, rbytes, rconst)) + { + cycles = lcycles + rcycles + exp->mDecType->mSize * (exp->mToken == TK_ASSIGN ? 1 : 3); + bytes = lbytes + rbytes + exp->mDecType->mSize * (exp->mToken == TK_ASSIGN ? 2 : 5); + cconst = rconst; + return true; + } + break; + case EX_BINARY: + case EX_RELATIONAL: + if (EstimateCost(exp->mLeft, vindex, lcycles, lbytes, lconst) && EstimateCost(exp->mRight, vindex, rcycles, rbytes, rconst)) + { + if (!lconst || !rconst) + { + if (exp->mType == EX_RELATIONAL && + (exp->mLeft->mType == EX_CONSTANT && exp->mLeft->mDecType->mType == DT_TYPE_INTEGER && exp->mLeft->mDecType->mSize <= 2 && exp->mLeft->mDecValue->mInteger == 0 || + exp->mRight->mType == EX_CONSTANT && exp->mRight->mDecType->mType == DT_TYPE_INTEGER && exp->mRight->mDecType->mSize <= 2 && exp->mRight->mDecValue->mInteger == 0)) + { + bytes += lbytes + rbytes; + cycles += lcycles + rcycles; + } + else + { + bytes = lbytes + rbytes + 2 * exp->mDecType->mSize; + cycles = lcycles + rcycles; + if (exp->mToken == TK_MUL && lcycles > 0 && rcycles > 0) + cycles += exp->mDecType->mSize * exp->mDecType->mSize * 25; + else if (exp->mToken == TK_DIV || exp->mToken == TK_MOD) + cycles += exp->mDecType->mSize * exp->mDecType->mSize * 40; + else + cycles += exp->mDecType->mSize; + } + } + else + cconst = true; + + return true; + } + + break; + case EX_POSTINCDEC: + case EX_PREINCDEC: + if (exp->mLeft->mType == EX_VARIABLE && exp->mLeft->mDecValue == vindex) + return false; + + if (EstimateCost(exp->mLeft, vindex, lcycles, lbytes, lconst)) + { + bytes = lbytes + 2 * exp->mDecType->mSize; + cycles = lcycles + exp->mDecType->mSize; + cconst = lconst; + + return true; + } + break; + + case EX_PREFIX: + if (exp->mToken == TK_ADD || exp->mToken == TK_SUB) + { + if (EstimateCost(exp->mLeft, vindex, lcycles, lbytes, lconst)) + { + if (!lconst) + { + bytes = lbytes + exp->mDecType->mSize; + cycles = lcycles + exp->mDecType->mSize; + } + else + cconst = true; + + return true; + } + } + else if (exp->mToken == TK_BINARY_AND || exp->mToken == TK_MUL) + { + if (exp->mLeft->mType == EX_VARIABLE && exp->mLeft->mDecValue == vindex) + return false; + + return EstimateCost(exp->mLeft, vindex, cycles, bytes, cconst); + } + break; + + case EX_QUALIFY: + return EstimateCost(exp->mLeft, vindex, cycles, bytes, cconst); + + case EX_INDEX: + if (EstimateCost(exp->mLeft, vindex, lcycles, lbytes, lconst) && EstimateCost(exp->mRight, vindex, rcycles, rbytes, rconst)) + { + cycles = lcycles + rcycles; + bytes = lbytes + rbytes; + if (!rconst) + { + bytes += 5; + cycles += exp->mDecType->mSize == 1 ? 4 : 8; + } + return true; + } + break; + + case EX_SEQUENCE: + case EX_COMMA: + case EX_SCOPE: + if (EstimateCost(exp->mLeft, vindex, lcycles, lbytes, lconst) && EstimateCost(exp->mRight, vindex, rcycles, rbytes, rconst)) + { + cycles = lcycles + rcycles; + bytes = lbytes + rbytes; + cconst = lconst && rconst; + return true; + } + break; + case EX_IF: + if (EstimateCost(exp->mLeft, vindex, lcycles, lbytes, lconst)) + { + int64 tcycles, tbytes, fcycles, fbytes; + bool tconst, fconst; + if (EstimateCost(exp->mRight->mLeft, vindex, tcycles, tbytes, tconst) && EstimateCost(exp->mRight->mRight, vindex, fcycles, fbytes, fconst)) + { + cycles = lcycles + (tcycles + fcycles + 1) / 2; + bytes = lbytes + tbytes + fbytes; + if (!lconst) + { + cycles += 2; + bytes += 2; + } + return true; + } + } + break; + + case EX_CALL: + if (exp->mDecType && exp->mDecType->mType != DT_TYPE_VOID && exp->mLeft->mType == EX_CONSTANT) + { + Declaration* pcall = exp->mLeft->mDecValue; + if (pcall->mOptFlags & OPTF_CONST_FUNCTION) + { + if (exp->mRight && exp->mRight->mType == EX_VARIABLE && exp->mRight->mDecValue == vindex) + { + cycles = 0; + bytes = 0; + cconst = true; + + return true; + } + + if (pcall->mFlags & OPTF_RECURSIVE) + cycles = 500; + else + cycles = 100; + bytes = 3; + + Expression* lexp = exp->mRight; + while (lexp) + { + Expression* pexp = lexp; + if (lexp->mType == EX_LIST) + { + pexp = lexp->mLeft; + lexp = lexp->mRight; + } + else + lexp = nullptr; + + if (!EstimateCost(lexp, vindex, lcycles, lbytes, lconst)) + return false; + + if (!lconst) + { + bytes += lbytes + 2 * exp->mDecType->mSize; + cycles += lcycles + exp->mDecType->mSize; + } + } + + return true; + } + } + + return false; + } + + return false; + } + + return true; + +} + + +bool GlobalOptimizer::UnrollLoops(Expression*& exp) +{ + bool changed = false; + + if (exp->mType == EX_FOR) + { + Expression* assignment = exp->mLeft->mRight; + Expression* condition = exp->mLeft->mLeft->mLeft; + Expression* body = exp->mRight; + Expression* increment = exp->mLeft->mLeft->mRight; + + if (assignment && condition && body && increment) + { + if ((assignment->mType == EX_INITIALIZATION || assignment->mType == EX_ASSIGNMENT) && + assignment->mRight->mType == EX_CONSTANT && + assignment->mLeft->mType == EX_VARIABLE && + assignment->mLeft->mDecType->IsIntegerType() && + !(assignment->mLeft->mDecValue->mFlags & (DTF_GLOBAL | DTF_STATIC))) + { + Declaration* ivdec = assignment->mLeft->mDecValue; + + if (condition->mType == EX_VARIABLE && condition->mDecValue == ivdec || + + condition->mType == EX_RELATIONAL && + condition->mLeft->mType == EX_VARIABLE && + condition->mLeft->mDecValue == ivdec && + condition->mRight->mType == EX_CONSTANT) + { + if ((increment->mType == EX_POSTINCDEC || increment->mType == EX_PREINCDEC) && + increment->mLeft->mType == EX_VARIABLE && + increment->mLeft->mDecValue == ivdec) + { + int64 start = assignment->mRight->mDecValue->mInteger; + int64 end; + Token cmp; + + if (condition->mType == EX_RELATIONAL) + { + end = condition->mRight->mDecValue->mInteger; + cmp = condition->mToken; + } + else + { + end = 0; + cmp = TK_NOT_EQUAL; + } + + int64 step = 1; + int64 count = 0; + + if (increment->mType == EX_POSTINCDEC || increment->mType == EX_PREINCDEC) + { + if (increment->mToken == TK_INC) + step = 1; + else + step = -1; + } + + if (step > 0) + { + if (end > start) + { + if (cmp == TK_LESS_THAN || cmp == TK_NOT_EQUAL) + count = (end + step - 1 - start) / step; + else if (cmp == TK_LESS_EQUAL) + count = (end + step - start) / step; + } + } + else + { + if (end < start) + { + if (cmp == TK_GREATER_THAN || cmp == TK_NOT_EQUAL) + count = (-end - step - 1 + start) / - step; + else if (cmp == TK_GREATER_EQUAL) + count = (-end - step + start) / - step; + } + } + + if (count > 0 && count < 20) + { + int64 pcycles, pbytes, ucycles, ubytes; + bool pconst, uconst; + + if (EstimateCost(body, nullptr, pcycles, pbytes, pconst) && EstimateCost(body, ivdec, ucycles, ubytes, uconst)) + { + int64 byteLimit = 20; + + int64 gcycles = (pcycles - ucycles) * count; + + if (mCompilerOptions & COPT_OPTIMIZE_CODE_SIZE) + byteLimit = 0; + else if (mCompilerOptions & COPT_OPTIMIZE_AUTO_INLINE_ALL) + { + byteLimit += 80; + byteLimit += gcycles / 20; + if (byteLimit > 400) + byteLimit = 400; + } + else + { + byteLimit += gcycles / 100; + if (byteLimit > 100) + byteLimit = 100; + } + + if (ubytes * count < pbytes + 4 + 4 * ivdec->mSize + byteLimit) + { + Expression* seq = nullptr; + for (int64 i = 0; imRight->mDecValue->Clone(); + icdec->mBase = ivdec->mBase; + icdec->mInteger = ivdec->mBase->CastInteger(start + i * step); + Expression* bexp = body->ToVarConst(ivdec, icdec); + if (seq) + { + Expression* nseq = new Expression(exp->mLocation, EX_SEQUENCE); + nseq->mLeft = seq; + nseq->mRight = bexp; + seq = nseq; + } + else + seq = bexp; + } + Declaration* ficdec = assignment->mRight->mDecValue->Clone(); + ficdec->mInteger = start + count * step; + + exp = new Expression(exp->mLocation, EX_SEQUENCE); + exp->mLeft = seq; + exp->mRight = assignment; + assignment->mRight->mDecValue = ficdec; + + return true; + } + } + } + } + } + } + } + } + + if (exp->mLeft && UnrollLoops(exp->mLeft)) + changed = true; + if (exp->mRight && UnrollLoops(exp->mRight)) + changed = true; + + return changed; +} + + +bool GlobalOptimizer::CheckConstReturns(Expression*& exp) +{ + bool changed = false; + + if (exp->mType == EX_CALL && exp->mDecType && exp->mDecType->mType != DT_TYPE_VOID) + { + if (exp->mLeft->mType == EX_CONSTANT) + { + Declaration* pcall = exp->mLeft->mDecValue; + if ((pcall->mOptFlags & OPTF_SINGLE_RETURN) && pcall->mReturn->mLeft) + { + Expression * rexp = pcall->mReturn->mLeft; + if (rexp->mType == EX_CONSTANT) + { +#if DUMP_OPTS + printf("Extract const return\n"); +#endif + + Expression* lexp = new Expression(exp->mLocation, EX_COMMA); + lexp->mLeft = exp; + lexp->mRight = new Expression(pcall->mReturn->mLocation, EX_CONSTANT); + lexp->mRight->mDecValue = pcall->mReturn->mLeft->mDecValue->ConstCast(exp->mDecType); + lexp->mRight->mDecType = exp->mDecType; + lexp->mDecType = exp->mDecType; + exp->mDecType = TheVoidTypeDeclaration; + exp = lexp; + return true; + } + else if (rexp->mType == EX_PREFIX && rexp->mToken == TK_MUL && rexp->mLeft->mType == EX_VARIABLE && rexp->mLeft->mDecValue->mType == DT_ARGUMENT) + { + Declaration* pdec = rexp->mLeft->mDecValue; + if (pdec->mVarIndex == 0 && !(pdec->mOptFlags & OPTF_VAR_ADDRESS)) + { + Expression* pex = exp->mRight; + if (pex->mType == EX_LIST) + pex = pex->mLeft; + if (pex->mType == EX_CONSTANT) + { +#if DUMP_OPTS + printf("Forward this pointer\n"); +#endif + Expression* lexp = new Expression(exp->mLocation, EX_COMMA); + lexp->mLeft = exp; + lexp->mRight = new Expression(pcall->mReturn->mLocation, EX_PREFIX); + lexp->mRight->mToken = TK_MUL; + lexp->mRight->mLeft = pex; + lexp->mRight->mDecType = pcall->mBase->mBase; + lexp->mDecType = lexp->mRight->mDecType; + exp->mDecType = TheVoidTypeDeclaration; + exp = lexp; + return true; + } + } + } + } + } + } + + if (exp->mLeft && CheckConstReturns(exp->mLeft)) + changed = true; + if (exp->mRight && CheckConstReturns(exp->mRight)) + changed = true; + + return changed; +} + +bool GlobalOptimizer::CheckUnusedLocals(Expression*& exp) +{ + bool changed = false; + + if (exp->mType == EX_INITIALIZATION) + { + Expression* vexp = exp->mLeft; + if (vexp->mType == EX_VARIABLE) + { + Declaration* vdec = vexp->mDecValue; + if (vdec->mType == DT_VARIABLE && !(vdec->mFlags & (DTF_GLOBAL | DTF_STATIC)) && !(vdec->mOptFlags & OPTF_VAR_USED) && !exp->mRight->IsVolatile()) + { + exp = exp->mRight; + return true; + } + } + } + + if (exp->mLeft && CheckUnusedLocals(exp->mLeft)) + changed = true; + if (exp->mRight && CheckUnusedLocals(exp->mRight)) + changed = true; + + return changed; +} + +void GlobalOptimizer::RemoveValueReturn(Expression* exp) +{ + if (exp->mType == EX_RETURN && exp->mLeft) + { + if (exp->mLeft->HasSideEffects()) + { + exp->mType = EX_SEQUENCE; + exp->mRight = new Expression(exp->mLocation, EX_RETURN); + } + else + exp->mLeft = nullptr; + } + + if (exp->mLeft) + RemoveValueReturn(exp->mLeft); + if (exp->mRight) + RemoveValueReturn(exp->mRight); +} + +void GlobalOptimizer::UndoParamReference(Expression* exp, Declaration* param) +{ + if (exp) + { + if (exp->mType == EX_VARIABLE && exp->mDecValue == param) + { + exp->mDecType = param->mBase; + } + + UndoParamReference(exp->mLeft, param); + UndoParamReference(exp->mRight, param); + } +} + +bool GlobalOptimizer::ReplaceParamConst(Expression* exp, Declaration* param) +{ + bool changed = false; + if (exp) + { + if (ReplaceParamConst(exp->mLeft, param)) + changed = true; + if (ReplaceParamConst(exp->mRight, param)) + changed = true; + + if (changed) + { + if (exp->mLeft) + exp->mLeft = exp->mLeft->ConstantFold(mErrors, nullptr); + if (exp->mRight) + exp->mRight = exp->mRight->ConstantFold(mErrors, nullptr); + } + + if (exp->mType == EX_VARIABLE && exp->mDecValue == param) + { + exp->mType = EX_CONSTANT; + exp->mDecType = param->mBase; + exp->mDecValue = param->mValue->mDecValue->ConstCast(param->mBase); + changed = true; + } + + } + return changed; +} + +bool GlobalOptimizer::ReplaceGlobalConst(Expression* exp) +{ + bool changed = false; + if (exp) + { + if (exp->mType == EX_VARIABLE && (exp->mDecValue->mFlags & (DTF_GLOBAL | DTF_STATIC)) && !(exp->mDecValue->mOptFlags & (OPTF_VAR_MODIFIED | OPTF_VAR_ADDRESS)) && exp->mDecValue->mValue) + { + + Expression* cexp = exp->mDecValue->mValue; + if (cexp->mType == EX_CONSTANT) + { + + if (cexp->mDecValue->mType == DT_CONST_ADDRESS || cexp->mDecValue->mType == DT_CONST_INTEGER || + cexp->mDecValue->mType == DT_CONST_POINTER || cexp->mDecValue->mType == DT_CONST_FLOAT) + { + exp->mType = EX_CONSTANT; + exp->mDecValue = cexp->mDecValue->ConstCast(exp->mDecType); + changed = true; + } + else if (exp->mDecValue->mBase->mType == DT_TYPE_STRUCT) + { + exp->mDecValue->mBase = exp->mDecValue->mBase->ToConstType(); + } + } + } + + if (ReplaceGlobalConst(exp->mLeft)) + changed = true; + if (ReplaceGlobalConst(exp->mRight)) + changed = true; + } + return changed; +} + +bool GlobalOptimizer::Optimize(void) +{ + bool changed = false; + + do { + changed = false; + for (int i = 0; i < mFunctions.Size(); i++) + { + Declaration* func = mFunctions[i]; + if ((func->mOptFlags & OPTF_CONST_FUNCTION) && !(func->mValue && CheckConstFunction(func->mValue))) + { + func->mOptFlags &= ~OPTF_CONST_FUNCTION; + changed = true; + } + } + + } while (changed); + +#if DUMP_OPTS + printf("OPT---\n"); +#endif + for (int i = 0; i < mFunctions.Size(); i++) + { + Declaration* func = mFunctions[i]; + Declaration* ftype = func->mBase; + +#if DUMP_OPTS + if (func->mOptFlags & OPTF_CONST_FUNCTION) + { + printf("%s const function\n", mFunctions[i]->mQualIdent->mString); + } +#endif + + if (func->mValue && func->mValue->mType != EX_DISPATCH) + { +#if DUMP_OPTS + printf("%s %08llx\n", mFunctions[i]->mQualIdent->mString, mFunctions[i]->mOptFlags); +#endif + if (CheckUnusedLocals(func->mValue)) + changed = true; + + if (CheckConstReturns(func->mValue)) + changed = true; + + if (CheckUnusedReturns(func->mValue)) + changed = true; + + if (ReplaceGlobalConst(func->mValue)) + changed = true; +#if 1 + if (ReplaceConstCalls(func->mValue)) + changed = true; + + if (CheckSplitAggregates(func, func->mValue)) + changed = true; + + if (mCompilerOptions & COPT_OPTIMIZE_AUTO_UNROLL) + { + if (UnrollLoops(func->mValue)) + changed = true; + } +#endif + if (func->mOptFlags & OPTF_MULTI_CALL) + { + Declaration* pdata = ftype->mParams; + while (pdata) + { + pdata->mForwardCall = nullptr; + pdata->mForwardParam = nullptr; + pdata = pdata->mNext; + } + } + + if (!(func->mOptFlags & OPTF_FUNC_VARIABLE) && !(func->mBase->mFlags & DTF_VIRTUAL)) + { + if (!(func->mOptFlags & (OPTF_VAR_USED | OPTF_VAR_ADDRESS)) && func->mBase->mBase && (func->mBase->mBase->IsSimpleType() || func->mBase->mBase->IsReference())) + { +#if DUMP_OPTS + printf("Remove return value\n"); +#endif + RemoveValueReturn(func->mValue); + func->mBase->mBase = TheVoidTypeDeclaration; + changed = true; + } + else if (!(func->mOptFlags & OPTF_VAR_ADDRESS) && func->mBase->mBase && func->mBase->mBase->IsReference() && func->mBase->mBase->mBase->IsSimpleType()) + { +#if DUMP_OPTS + printf("Demote reference return\n"); +#endif + func->mBase->mBase = func->mBase->mBase->mBase; + changed = true; + } + + if (!(func->mFlags & DTF_INTRINSIC)) + { + Declaration* pdec = ftype->mParams; + int vi = 0; + while (pdec) + { + if ((ftype->mFlags & DTF_VARIADIC) && !pdec->mNext) + break; + + + pdec->mVarIndex += vi; + if (!(pdec->mOptFlags & OPTF_VAR_USED) && !(pdec->mFlags & DTF_FPARAM_UNUSED)) + { + if (!pdec->mBase->IsReference() || !(pdec->mOptFlags & OPTF_VAR_ADDRESS)) + { +#if DUMP_OPTS + printf("Unused parameter %s\n", pdec->mIdent ? pdec->mIdent->mString : "_"); +#endif + vi -= pdec->mSize; + + pdec->mFlags |= DTF_FPARAM_UNUSED; + pdec->mVarIndex = func->mNumVars++; + + changed = true; + } + } + else if (!(pdec->mOptFlags & OPTF_VAR_ADDRESS) && pdec->mBase->IsReference() && pdec->mBase->mBase->IsSimpleType()) + { +#if DUMP_OPTS + printf("Reference parameter %s to value\n", pdec->mIdent ? pdec->mIdent->mString : "_"); +#endif + vi += pdec->mSize - 2; + + pdec->mBase = pdec->mBase->mBase; + pdec->mSize = pdec->mBase->mSize; + + UndoParamReference(func->mValue, pdec); + + changed = true; + } + else if ((pdec->mOptFlags & OPTF_VAR_CONST) && !(pdec->mOptFlags & OPTF_VAR_ADDRESS)) + { + if (ReplaceParamConst(func->mValue, pdec)) + { + func->mValue = func->mValue->ConstantFold(mErrors, nullptr); +#if DUMP_OPTS + printf("Const parameter %s\n", pdec->mIdent ? pdec->mIdent->mString : "_"); +#endif + changed = true; + } + } + + if (pdec->mOptFlags & OPTF_VAR_RANGE_LIMITED) + pdec->mFlags |= DTF_FPARAM_RANGE_LIMITED; + + pdec->mOptFlags = 0; + pdec = pdec->mNext; + } + } + } + + PropagateCommas(func->mValue); + } + else if (func->mValue && func->mValue->mType == EX_DISPATCH) + { + if (!(func->mOptFlags & OPTF_FUNC_VARIABLE)) + { + Declaration* pdec = ftype->mParams; + while (pdec) + { + if ((pdec->mOptFlags & OPTF_VAR_CONST) && !(pdec->mOptFlags & OPTF_VAR_ADDRESS)) + { + if (ReplaceParamConst(func->mValue, pdec)) + { +#if DUMP_OPTS + printf("Const parameter %s\n", pdec->mIdent ? pdec->mIdent->mString : "_"); +#endif + changed = true; + } + } + + pdec->mOptFlags = 0; + pdec = pdec->mNext; + } + } + } + } + + return changed; +} + +void GlobalOptimizer::AnalyzeProcedure(Expression* exp, Declaration* procDec) +{ + if (procDec->mOptFlags & OPTF_ANALYZING) + { + procDec->mOptFlags |= OPTF_RECURSIVE; + } + else if (!(procDec->mOptFlags & OPTF_ANALYZED)) + { + procDec->mOptFlags |= OPTF_ANALYZING | OPTF_ANALYZED | OPTF_CONST_FUNCTION; + + if (procDec->mFlags & DTF_PREVENT_INLINE) + procDec->mOptFlags &= ~OPTF_CONST_FUNCTION; + + mFunctions.Push(procDec); + + if ((procDec->mFlags & DTF_INTRINSIC) && !procDec->mValue) + ; + else if (procDec->mFlags & DTF_DEFINED) + { + Analyze(exp, procDec, false); + } + else + mErrors->Error(procDec->mLocation, EERR_UNDEFINED_OBJECT, "Calling undefined function", procDec->FullIdent()); + + procDec->mOptFlags &= ~OPTF_ANALYZING; + } +} + +void GlobalOptimizer::AnalyzeAssembler(Expression* exp, Declaration* procDec) +{ + while (exp) + { + if (exp->mLeft && exp->mLeft->mDecValue) + { + Declaration* adec = exp->mLeft->mDecValue; + if (adec->mType == DT_LABEL_REF) + { + + } + else if (adec->mType == DT_VARIABLE_REF) + { + if (adec->mBase->mFlags & DTF_GLOBAL) + AnalyzeGlobalVariable(adec->mBase); + adec->mBase->mOptFlags |= OPTF_VAR_USED | OPTF_VAR_ADDRESS; + } + else if (adec->mType == DT_LABEL) + { + + } + else if (adec->mType == DT_VARIABLE) + { + if (adec->mFlags & DTF_GLOBAL) + AnalyzeGlobalVariable(adec); + adec->mOptFlags |= OPTF_VAR_USED | OPTF_VAR_ADDRESS; + } + else if (adec->mType == DT_ARGUMENT) + { + adec->mOptFlags |= OPTF_VAR_USED; + } + else if (adec->mType == DT_FUNCTION_REF) + { + adec->mBase->mOptFlags |= OPTF_VAR_USED; + AnalyzeProcedure(adec->mBase->mValue, adec->mBase); + RegisterProc(adec->mBase); + } + else if (adec->mType == DT_CONST_FUNCTION) + { + adec->mOptFlags |= OPTF_VAR_USED; + AnalyzeProcedure(adec->mValue, adec); + RegisterCall(procDec, adec); + } + } + + exp = exp->mRight; + } + +} + +void GlobalOptimizer::AnalyzeGlobalVariable(Declaration* dec) +{ + while (dec->mType == DT_VARIABLE_REF) + dec = dec->mBase; + + if (!(dec->mOptFlags & OPTF_ANALYZED)) + { + dec->mOptFlags |= OPTF_ANALYZED; + + if (dec->mValue && dec->mValue->mType == EX_CONSTRUCT) + { + if (dec->mValue->mLeft->mLeft->mType == EX_CALL && (dec->mValue->mLeft->mLeft->mLeft->mDecValue->mFlags & DTF_CONSTEXPR)) + { + ConstexprInterpreter cinter(dec->mLocation, mErrors, dec->mSection); + dec->mValue = cinter.EvalConstructor(dec->mValue->mLeft->mLeft); + } + } + + mGlobalVariables.Push(dec); + + if (dec->mValue) + { + Analyze(dec->mValue, dec, false); + } + } +} + +void GlobalOptimizer::AnalyzeInit(Declaration* mdec) +{ + while (mdec) + { + if (mdec->mValue) + RegisterProc(Analyze(mdec->mValue, mdec, false)); + else if (mdec->mParams) + AnalyzeInit(mdec->mParams); + mdec = mdec->mNext; + } +} + + +void GlobalOptimizer::RegisterCall(Declaration* from, Declaration* to) +{ + if (from) + { + if (to->mType == DT_VARIABLE || to->mType == DT_ARGUMENT) + to = to->mBase; + + if (to->mType == DT_CONST_FUNCTION) + { + if (!(to->mOptFlags & OPTF_CALLED)) + { + to->mOptFlags |= OPTF_CALLED; + mCalledFunctions.Push(to); + } + + if (!(from->mOptFlags & OPTF_CALLING)) + { + from->mOptFlags |= OPTF_CALLING; + mCallingFunctions.Push(from); + } + } + else if (to->mType == DT_TYPE_FUNCTION) + { + if (!(from->mOptFlags & OPTF_CALLING)) + { + from->mOptFlags |= OPTF_CALLING; + mCallingFunctions.Push(from); + } + } + else if (to->mType == DT_TYPE_POINTER && to->mBase->mType == DT_TYPE_FUNCTION) + { + if (!(from->mOptFlags & OPTF_CALLING)) + { + from->mOptFlags |= OPTF_CALLING; + mCallingFunctions.Push(from); + } + } + } +} + +void GlobalOptimizer::RegisterProc(Declaration* to) +{ + if (to->mType == DT_CONST_FUNCTION) + { + if (to->mBase->mFlags & DTF_VIRTUAL) + { + + } + else if (!(to->mOptFlags & OPTF_FUNC_VARIABLE)) + { + to->mOptFlags |= OPTF_FUNC_VARIABLE; + mVariableFunctions.Push(to); + } + } +} + +Declaration* GlobalOptimizer::Analyze(Expression* exp, Declaration* procDec, uint32 flags) +{ + Declaration* ldec, * rdec; + + switch (exp->mType) + { + case EX_ERROR: + case EX_VOID: + break; + case EX_GOTO: + procDec->mOptFlags &= ~OPTF_CONST_FUNCTION; + break; + case EX_CONSTANT: + if (exp->mDecValue->mType == DT_CONST_FUNCTION) + { + AnalyzeProcedure(exp->mDecValue->mValue, exp->mDecValue); + } + else if (exp->mDecValue->mType == DT_CONST_STRUCT) + { + AnalyzeInit(exp->mDecValue->mParams); + } + else if (exp->mDecValue->mType == DT_CONST_POINTER) + { + ldec = Analyze(exp->mDecValue->mValue, procDec, ANAFL_LHS | ANAFL_RHS); + RegisterProc(ldec); + } + else if (exp->mDecValue->mType == DT_CONST_ADDRESS) + { + } + else if (exp->mDecValue->mType == DT_CONST_ASSEMBLER) + { + AnalyzeAssembler(exp->mDecValue->mValue, procDec); + } + + return exp->mDecValue; + case EX_VARIABLE: + { + Declaration* vdec = exp->mDecValue; + if (vdec->mType == DT_VARIABLE_REF) + vdec = vdec->mBase; + + if ((vdec->mFlags & DTF_STATIC) || (vdec->mFlags & DTF_GLOBAL)) + { + Declaration* type = vdec->mBase; + while (type->mType == DT_TYPE_ARRAY) + type = type->mBase; + + AnalyzeGlobalVariable(vdec); + procDec->mOptFlags &= ~OPTF_CONST_FUNCTION; + } + if (flags & ANAFL_COPY) + vdec->mOptFlags |= OPTF_VAR_COPY; + if (flags & ANAFL_RHS) + vdec->mOptFlags |= OPTF_VAR_USED; + if (flags & ANAFL_LHS) + vdec->mOptFlags |= OPTF_VAR_ADDRESS; + if (vdec->mBase->IsReference() && (flags & ANAFL_ASSIGN)) + vdec->mOptFlags |= OPTF_VAR_USED; + + if (vdec->mType == DT_ARGUMENT && (flags & ANAFL_LHS)) + { + vdec->mOptFlags |= OPTF_VAR_NO_FORWARD; + vdec->mForwardParam = nullptr; + vdec->mForwardCall = nullptr; + } + + if (vdec->mBase->mFlags & DTF_VOLATILE) + procDec->mOptFlags &= ~OPTF_CONST_FUNCTION; + + return vdec; + } + case EX_INITIALIZATION: + case EX_ASSIGNMENT: + if (exp->mToken == TK_ASSIGN) + { + if (exp->mType == EX_ASSIGNMENT) + ldec = Analyze(exp->mLeft, procDec, ANAFL_LHS | ANAFL_ASSIGN | ANAFL_COPY | flags); + else + ldec = Analyze(exp->mLeft, procDec, ANAFL_LHS | ANAFL_COPY | flags); + } + else + ldec = Analyze(exp->mLeft, procDec, ANAFL_LHS | ANAFL_RHS | ANAFL_COPY | flags); + + if (exp->mLeft->mDecType->IsReference()) + rdec = Analyze(exp->mRight, procDec, ANAFL_LHS | ANAFL_RHS | ANAFL_COPY); + else + rdec = Analyze(exp->mRight, procDec, ANAFL_RHS | ANAFL_COPY); + + RegisterProc(rdec); + return ldec; + + case EX_BINARY: + ldec = Analyze(exp->mLeft, procDec, flags); + rdec = Analyze(exp->mRight, procDec, flags); + return ldec; + + case EX_RELATIONAL: + ldec = Analyze(exp->mLeft, procDec, flags); + rdec = Analyze(exp->mRight, procDec, flags); + return TheBoolTypeDeclaration; + + case EX_PREINCDEC: + return Analyze(exp->mLeft, procDec, ANAFL_LHS | ANAFL_RHS); + case EX_PREFIX: + if (exp->mToken == TK_BINARY_AND) + ldec = Analyze(exp->mLeft, procDec, ANAFL_LHS | ANAFL_RHS | ANAFL_ADDRESS); + else if (exp->mToken == TK_MUL) + { + procDec->mOptFlags &= ~OPTF_CONST_FUNCTION; + ldec = Analyze(exp->mLeft, procDec, ANAFL_RHS); + return exp->mDecType; + } + else + return Analyze(exp->mLeft, procDec, flags); + break; + case EX_POSTFIX: + break; + case EX_POSTINCDEC: + return Analyze(exp->mLeft, procDec, ANAFL_LHS | ANAFL_RHS); + case EX_INDEX: + if (exp->mDecType->mFlags & DTF_VOLATILE) + procDec->mOptFlags &= ~OPTF_CONST_FUNCTION; + + procDec->mOptFlags &= ~OPTF_CONST_FUNCTION; +#if 0 + ldec = Analyze(exp->mLeft, procDec, ANAFL_RHS); +#else + if (exp->mLeft->mDecType->mType == DT_TYPE_POINTER) + ldec = Analyze(exp->mLeft, procDec, ANAFL_RHS); + else if (exp->mRight->mType != EX_CONSTANT) + ldec = Analyze(exp->mLeft, procDec, ANAFL_RHS | ANAFL_LHS); + else if (flags & ANAFL_ADDRESS) + ldec = Analyze(exp->mLeft, procDec, flags); + else + ldec = Analyze(exp->mLeft, procDec, ANAFL_RHS); +#endif + if (ldec->mType == DT_VARIABLE || ldec->mType == DT_ARGUMENT) + ldec = ldec->mBase; + rdec = Analyze(exp->mRight, procDec, ANAFL_RHS); + if (ldec->mBase) + return ldec->mBase; + break; + case EX_QUALIFY: + if (exp->mDecType->mFlags & DTF_VOLATILE) + procDec->mOptFlags &= ~OPTF_CONST_FUNCTION; + + if (flags & ANAFL_ADDRESS) + Analyze(exp->mLeft, procDec, flags); + else + Analyze(exp->mLeft, procDec, ANAFL_RHS); + return exp->mDecValue->mBase; + case EX_DISPATCH: + procDec->mOptFlags &= ~OPTF_CONST_FUNCTION; + Analyze(exp->mLeft, procDec, flags); + break; + case EX_VCALL: + exp->mType = EX_CALL; + exp->mLeft->mDecValue = exp->mLeft->mDecValue->mVTable; + // intentional fall through + case EX_CALL: + case EX_INLINE: + if (exp->mLeft->mType == EX_CONSTANT) + { + if (flags & ANAFL_RHS) + exp->mLeft->mDecValue->mOptFlags |= OPTF_VAR_USED; + if (flags & ANAFL_LHS) + exp->mLeft->mDecValue->mOptFlags |= OPTF_VAR_ADDRESS; + } + + ldec = Analyze(exp->mLeft, procDec, ANAFL_RHS); + if ((ldec->mFlags & DTF_INTRINSIC) && !ldec->mValue) + { + + } + else + { + if (procDec->mOptFlags & OPTF_SINGLE_CALL) + procDec->mOptFlags |= OPTF_MULTI_CALL; + else + procDec->mOptFlags |= OPTF_SINGLE_CALL; + RegisterCall(procDec, ldec); + } + + if (exp->mRight) + { + // Check for struct to struct forwarding + + Expression* rex = exp->mRight; + if (rex) + { + Expression* prex = exp; + while (rex && rex->mType == EX_LIST) + { + rex->mLeft = rex->mLeft->ConstantFold(mErrors, nullptr); + prex = rex; + rex = rex->mRight; + } + if (rex) + prex->mRight = rex->ConstantFold(mErrors, nullptr); + rex = exp->mRight; + } + + Declaration* pdec = ldec->mBase->mParams; + while (rex) + { + Expression *& pex(rex->mType == EX_LIST ? rex->mLeft : rex); + + if (pdec) + { + if ((pdec->mFlags & DTF_FPARAM_UNUSED) && !pex->HasSideEffects() && (pex->mType != EX_CONSTANT || !pex->mDecValue->IsNullConst())) + { + if (pdec->mBase->IsSimpleType()) + { + pex = new Expression(pex->mLocation, EX_CONSTANT); + switch (pdec->mBase->mType) + { + case DT_TYPE_INTEGER: + case DT_TYPE_ENUM: + case DT_TYPE_BOOL: + pex->mDecValue = TheZeroIntegerConstDeclaration; + break; + case DT_TYPE_FLOAT: + pex->mDecValue = TheZeroFloatConstDeclaration; + break; + case DT_TYPE_POINTER: + case DT_TYPE_FUNCTION: + pex->mDecValue = TheNullptrConstDeclaration; + break; + } + pex->mDecType = pdec->mBase; + } + } + if (!(pdec->mFlags & DTF_FPARAM_UNUSED) && !(pdec->mOptFlags & OPTF_VAR_NOCONST)) + { + if (pex->mType == EX_CONSTANT && pdec->mBase->IsSimpleType() && pdec->mBase->CanAssign(pex->mDecType)) + { + if (pdec->mOptFlags & OPTF_VAR_CONST) + { + if (!pex->mDecValue->IsSameValue(pdec->mValue->mDecValue)) + { + pdec->mOptFlags |= OPTF_VAR_NOCONST; + pdec->mOptFlags &= ~OPTF_VAR_CONST; + if (pdec->mBase->IsIntegerType()) + { + pdec->mOptFlags |= OPTF_VAR_RANGE_LIMITED; + pdec->mMinValue = int64min(pex->mDecValue->mInteger, pdec->mValue->mDecValue->mInteger); + pdec->mMaxValue = int64max(pex->mDecValue->mInteger, pdec->mValue->mDecValue->mInteger); + } + } + } + else + { + pdec->mValue = pex; + pdec->mOptFlags |= OPTF_VAR_CONST; + } + } + else if (pex->mType == EX_VARIABLE && pdec->mBase->mType == DT_TYPE_POINTER && pex->mDecType->mType == DT_TYPE_ARRAY && (pex->mDecValue->mFlags & (DTF_GLOBAL | DTF_STATIC)) && pdec->mBase->CanAssign(pex->mDecType)) + { + if (pdec->mOptFlags & OPTF_VAR_CONST) + { + if (pdec->mValue->mType != EX_VARIABLE || pdec->mValue->mDecValue != pex->mDecValue) + { + pdec->mOptFlags |= OPTF_VAR_NOCONST; + pdec->mOptFlags &= ~OPTF_VAR_CONST; + } + } + else + { + pdec->mValue = pex; + pdec->mOptFlags |= OPTF_VAR_CONST; + } + } + else + { + pdec->mOptFlags |= OPTF_VAR_NOCONST; + pdec->mOptFlags &= ~OPTF_VAR_CONST; + } + } + else if (!(pdec->mFlags & DTF_FPARAM_UNUSED) && (pdec->mOptFlags & OPTF_VAR_RANGE_LIMITED)) + { + if (pex->mType == EX_CONSTANT && pdec->mBase->IsIntegerType() && pdec->mBase->CanAssign(pex->mDecType)) + { + pdec->mMinValue = int64min(pex->mDecValue->mInteger, pdec->mMinValue); + pdec->mMaxValue = int64max(pex->mDecValue->mInteger, pdec->mMaxValue); + } + else + pdec->mOptFlags &= ~OPTF_VAR_RANGE_LIMITED; + } + + if (pdec->mBase->mType == DT_TYPE_STRUCT && pdec->mBase->mCopyConstructor) + { + if (pdec->mBase->mMoveConstructor) + { + AnalyzeProcedure(pdec->mBase->mMoveConstructor->mValue, pdec->mBase->mMoveConstructor); + RegisterCall(procDec, pdec->mBase->mMoveConstructor); + } + AnalyzeProcedure(pdec->mBase->mCopyConstructor->mValue, pdec->mBase->mCopyConstructor); + RegisterCall(procDec, pdec->mBase->mCopyConstructor); + } + } + + if (pdec && (pdec->mFlags & DTF_FPARAM_UNUSED)) + RegisterProc(Analyze(pex, procDec, 0)); + else if (pdec && pdec->mBase->IsReference()) + { + if (!(procDec->mBase->mFlags & DTF_VIRTUAL) && pdec && pex && pex->mType == EX_VARIABLE && pex->mDecValue->mType == DT_ARGUMENT && pex->mDecValue->mBase->IsReference() && !pex->mDecValue->mForwardParam && !(pex->mDecValue->mOptFlags & OPTF_VAR_NO_FORWARD)) + { + pex->mDecValue->mOptFlags |= OPTF_VAR_USED | OPTF_VAR_ADDRESS; + pex->mDecValue->mForwardParam = pdec; + pex->mDecValue->mForwardCall = ldec; + } + else + RegisterProc(Analyze(pex, procDec, ANAFL_LHS | ANAFL_RHS | ANAFL_COPY)); + } + else if (!(procDec->mBase->mFlags & DTF_VIRTUAL) && pdec && pex && pex->mType == EX_VARIABLE && pex->mDecValue->mType == DT_ARGUMENT && !pex->mDecValue->mForwardParam && !(pex->mDecValue->mOptFlags & OPTF_VAR_NO_FORWARD)) + { + pex->mDecValue->mOptFlags |= OPTF_VAR_USED; + pex->mDecValue->mForwardParam = pdec; + pex->mDecValue->mForwardCall = ldec; + } + else + RegisterProc(Analyze(pex, procDec, ANAFL_RHS | ANAFL_COPY)); + + if (pdec) + pdec = pdec->mNext; + + if (rex->mType == EX_LIST) + rex = rex->mRight; + else + rex = nullptr; + } + } + break; + case EX_LIST: + case EX_COMMA: + { + RegisterProc(Analyze(exp->mLeft, procDec, 0)); + Declaration* dec = Analyze(exp->mRight, procDec, flags); + RegisterProc(dec); + return dec; + } + case EX_RETURN: + if (exp->mLeft) + { + exp->mLeft = exp->mLeft->ConstantFold(mErrors, nullptr); + + if (procDec->mOptFlags & OPTF_SINGLE_RETURN) + { + procDec->mOptFlags &= ~OPTF_SINGLE_RETURN; + procDec->mOptFlags |= OPTF_MULTI_RETURN; + } + else if (!(procDec->mOptFlags & OPTF_MULTI_RETURN)) + { + procDec->mOptFlags |= OPTF_SINGLE_RETURN; + procDec->mReturn = exp; + } + + if (procDec->mBase->mBase->IsReference()) + RegisterProc(Analyze(exp->mLeft, procDec, ANAFL_LHS | ANAFL_RHS)); + else + RegisterProc(Analyze(exp->mLeft, procDec, ANAFL_RHS | ANAFL_COPY)); + + if (procDec->mBase->mBase && procDec->mBase->mBase->mType == DT_TYPE_STRUCT && procDec->mBase->mBase->mCopyConstructor) + { + if (procDec->mBase->mBase->mMoveConstructor) + { + AnalyzeProcedure(procDec->mBase->mBase->mMoveConstructor->mValue, procDec->mBase->mBase->mMoveConstructor); + RegisterCall(procDec, procDec->mBase->mBase->mMoveConstructor); + } + AnalyzeProcedure(procDec->mBase->mBase->mCopyConstructor->mValue, procDec->mBase->mBase->mCopyConstructor); + RegisterCall(procDec, procDec->mBase->mBase->mCopyConstructor); + } + } + break; + case EX_SEQUENCE: + do + { + if (exp->mType == EX_SEQUENCE) + { + if (exp->mLeft) + ldec = Analyze(exp->mLeft, procDec, 0); + exp = exp->mRight; + } + else + return Analyze(exp, procDec, 0); + + } while (exp); + break; + + case EX_SCOPE: + Analyze(exp->mLeft, procDec, 0); + break; + + case EX_CONSTRUCT: + procDec->mOptFlags &= ~OPTF_CONST_FUNCTION; + if (exp->mLeft->mLeft) + Analyze(exp->mLeft->mLeft, procDec, 0); + if (exp->mLeft->mRight) + Analyze(exp->mLeft->mRight, procDec, 0); + if (exp->mRight) + return Analyze(exp->mRight, procDec, ANAFL_RHS); + break; + + case EX_CLEANUP: + Analyze(exp->mRight, procDec, 0); + return Analyze(exp->mLeft, procDec, flags); + + case EX_WHILE: + ldec = Analyze(exp->mLeft, procDec, ANAFL_RHS); + rdec = Analyze(exp->mRight, procDec, 0); + break; + case EX_IF: + ldec = Analyze(exp->mLeft, procDec, ANAFL_RHS); + rdec = Analyze(exp->mRight->mLeft, procDec, 0); + if (exp->mRight->mRight) + rdec = Analyze(exp->mRight->mRight, procDec, 0); + break; + case EX_ELSE: + break; + case EX_FOR: + if (exp->mLeft->mRight) + ldec = Analyze(exp->mLeft->mRight, procDec, 0); + if (exp->mLeft->mLeft->mLeft) + ldec = Analyze(exp->mLeft->mLeft->mLeft, procDec, ANAFL_RHS); + rdec = Analyze(exp->mRight, procDec, 0); + if (exp->mLeft->mLeft->mRight) + ldec = Analyze(exp->mLeft->mLeft->mRight, procDec, 0); + break; + case EX_FORBODY: + ldec = Analyze(exp->mLeft, procDec, 0); + if (exp->mRight) + rdec = Analyze(exp->mRight, procDec, 0); + break; + case EX_DO: + ldec = Analyze(exp->mRight, procDec, 0); + rdec = Analyze(exp->mLeft, procDec, ANAFL_RHS); + break; + case EX_BREAK: + case EX_CONTINUE: + break; + case EX_ASSUME: + return Analyze(exp->mLeft, procDec, ANAFL_RHS); + break; + case EX_TYPE: + break; + case EX_TYPECAST: + return Analyze(exp->mLeft, procDec, ANAFL_RHS); + break; + case EX_LOGICAL_AND: + ldec = Analyze(exp->mLeft, procDec, ANAFL_RHS); + rdec = Analyze(exp->mRight, procDec, ANAFL_RHS); + break; + case EX_LOGICAL_OR: + ldec = Analyze(exp->mLeft, procDec, ANAFL_RHS); + rdec = Analyze(exp->mRight, procDec, ANAFL_RHS); + break; + case EX_LOGICAL_NOT: + ldec = Analyze(exp->mLeft, procDec, ANAFL_RHS); + break; + case EX_ASSEMBLER: + procDec->mOptFlags &= ~OPTF_CONST_FUNCTION; + AnalyzeAssembler(exp, procDec); + break; + case EX_UNDEFINED: + break; + case EX_SWITCH: + ldec = Analyze(exp->mLeft, procDec, ANAFL_RHS); + exp = exp->mRight; + while (exp) + { + if (exp->mLeft->mRight) + rdec = Analyze(exp->mLeft->mRight, procDec, 0); + exp = exp->mRight; + } + break; + case EX_CASE: + break; + case EX_DEFAULT: + break; + case EX_CONDITIONAL: + ldec = Analyze(exp->mLeft, procDec, ANAFL_RHS); + RegisterProc(Analyze(exp->mRight->mLeft, procDec, flags)); + RegisterProc(Analyze(exp->mRight->mRight, procDec, flags)); + break; + } + + return TheVoidTypeDeclaration; +} diff --git a/oscar64/oscar64/GlobalOptimizer.h b/oscar64/oscar64/GlobalOptimizer.h new file mode 100644 index 0000000..77a9e26 --- /dev/null +++ b/oscar64/oscar64/GlobalOptimizer.h @@ -0,0 +1,52 @@ +#pragma once + +#include "Declaration.h" +#include "Linker.h" +#include "CompilerTypes.h" + +class GlobalOptimizer +{ +public: + GlobalOptimizer(Errors* errors, Linker* linker); + ~GlobalOptimizer(void); + + void Reset(void); + bool Optimize(void); + + void AnalyzeProcedure(Expression* exp, Declaration* dec); + void AnalyzeAssembler(Expression* exp, Declaration* dec); + void AnalyzeGlobalVariable(Declaration* dec); + + uint64 mCompilerOptions; + +protected: + Errors * mErrors; + Linker * mLinker; + + ExpandingArray mCalledFunctions, mCallingFunctions, mVariableFunctions, mFunctions; + ExpandingArray mGlobalVariables; + + void AnalyzeInit(Declaration* mdec); + Declaration* Analyze(Expression* exp, Declaration* procDec, uint32 flags); + + void RegisterCall(Declaration* from, Declaration* to); + void RegisterProc(Declaration* to); + + bool EstimateCost(Expression* exp, Declaration* vindex, int64& cycles, int64& bytes, bool& cconst); + + bool CheckConstFunction(Expression* exp); + void RemoveValueReturn(Expression* exp); + bool CheckUnusedReturns(Expression*& exp); + bool CheckConstReturns(Expression*& exp); + bool CheckUnusedLocals(Expression*& exp); + void UndoParamReference(Expression* exp, Declaration* param); + bool ReplaceParamConst(Expression* exp, Declaration* param); + void PropagateCommas(Expression*& exp); + void PropagateParamCommas(Expression *& fexp, Expression*& exp); + bool ReplaceGlobalConst(Expression* exp); + bool ReplaceConstCalls(Expression*& exp); + bool UnrollLoops(Expression*& exp); + + bool CheckSplitAggregates(Declaration* func, Expression* exp); + void SplitLocalAggregate(Expression* exp, Declaration* var, ExpandingArray & evars); +}; diff --git a/oscar64/oscar64/Ident.cpp b/oscar64/oscar64/Ident.cpp new file mode 100644 index 0000000..4f03633 --- /dev/null +++ b/oscar64/oscar64/Ident.cpp @@ -0,0 +1,161 @@ +#include "Ident.h" +#include "MachineTypes.h" +#include + +Ident::~Ident() +{ + delete[] mString; +} + +unsigned int IHash(const char* str) +{ + unsigned int hash = 32324124; + int i = 0; + while (str[i]) + { + hash = hash * 123211 + str[i]; + i++; + } + + return hash; +} + +Ident::Ident(const char* str, unsigned int hash) +{ + ptrdiff_t ssize = strlen(str); + mString = new char[ssize + 1]; + strcpy_s(mString, ssize + 1, str); + + mHash = hash; +} + +Ident * UniqueIdents[0x10000]; + +const Ident* Ident::Unique(const char* str) +{ + unsigned int hash = IHash(str); + int i = hash & 0xffff; + while (UniqueIdents[i]) + { + if (!strcmp(UniqueIdents[i]->mString, str)) + return UniqueIdents[i]; + i = (i + 1) & 0xffff; + } + return UniqueIdents[i] = new Ident(str, hash); +} + + +const Ident* Ident::PreMangle(const char* str) const +{ + char buffer[1000]; + strcpy_s(buffer, str); + strcat_s(buffer, mString); + return Unique(buffer); +} + +const Ident* Ident::Unique(const char* str, int id) +{ + char buffer[1000]; + sprintf_s(buffer, "%s#%d", str, id); + return Unique(buffer); +} + +const Ident* Ident::Mangle(const char* str) const +{ + char buffer[1000]; + strcpy_s(buffer, mString); + strcat_s(buffer, str); + return Unique(buffer); +} + +IdentDict::IdentDict(void) +{ + mHashSize = 0; + mHashFill = 0; + mHash = nullptr; +} + +IdentDict::~IdentDict(void) +{ + delete[] mHash; +} + +void IdentDict::Insert(const Ident* ident, const char* str) +{ + ptrdiff_t s = strlen(str); + char* nstr = new char[s + 1]; + strcpy_s(nstr, s + 1, str); + InsertCopy(ident, nstr); +} + +void IdentDict::InsertCopy(const Ident* ident, char* str) +{ + if (!mHash) + { + mHashSize = 16; + mHashFill = 0; + mHash = new Entry[mHashSize]; + for (int i = 0; i < mHashSize; i++) + { + mHash[i].mString = nullptr; + mHash[i].mIdent = nullptr; + } + } + + int hm = mHashSize - 1; + int hi = ident->mHash & hm; + + while (mHash[hi].mIdent) + { + if (ident == mHash[hi].mIdent) + { + mHash[hi].mString = str; + return; + } + + hi = (hi + 1) & hm; + } + + mHash[hi].mIdent = ident; + mHash[hi].mString = str; + mHashFill++; + + if (2 * mHashFill >= mHashSize) + { + int size = mHashSize; + Entry* entries = mHash; + mHashSize *= 2; + mHashFill = 0; + mHash = new Entry[mHashSize]; + for (int i = 0; i < mHashSize; i++) + { + mHash[i].mString = nullptr; + mHash[i].mIdent = nullptr; + } + for (int i = 0; i < size; i++) + { + if (entries[i].mIdent) + InsertCopy(entries[i].mIdent, entries[i].mString); + } + delete[] entries; + } +} + +const char * IdentDict::Lookup(const Ident* ident) +{ + if (mHashSize > 0) + { + int hm = mHashSize - 1; + int hi = ident->mHash & hm; + + while (mHash[hi].mIdent) + { + if (ident == mHash[hi].mIdent) + return mHash[hi].mString; + hi = (hi + 1) & hm; + } + } + + return nullptr; +} + diff --git a/oscar64/oscar64/Ident.h b/oscar64/oscar64/Ident.h new file mode 100644 index 0000000..93d1470 --- /dev/null +++ b/oscar64/oscar64/Ident.h @@ -0,0 +1,38 @@ +#pragma once + +class Ident +{ +public: + ~Ident(); + + char * mString; + unsigned int mHash; + + static const Ident* Unique(const char* str); + static const Ident* Unique(const char* str, int id); + const Ident* Mangle(const char* str) const; + const Ident* PreMangle(const char* str) const; +protected: + Ident(const char* str, unsigned int hash); +}; + +class IdentDict +{ +public: + IdentDict(void); + ~IdentDict(void); + + void Insert(const Ident* ident, const char * str); + const char * Lookup(const Ident* ident); + +protected: + void InsertCopy(const Ident* ident, char* str); + + struct Entry + { + const Ident* mIdent; + char* mString; + }; + Entry* mHash; + int mHashSize, mHashFill; +}; \ No newline at end of file diff --git a/oscar64/oscar64/InterCode.cpp b/oscar64/oscar64/InterCode.cpp new file mode 100644 index 0000000..4b16f6d --- /dev/null +++ b/oscar64/oscar64/InterCode.cpp @@ -0,0 +1,30949 @@ +#include "InterCode.h" +#include "CompilerTypes.h" + +#include +#include +#include + +#define DISASSEMBLE_OPT 0 + +static bool CheckFunc; +static bool CheckCase; + +int InterTypeSize[] = { + 0, + 1, + 1, + 2, + 4, + 4, + 2 +}; + +static bool IsCommutative(InterOperator op) +{ + return op == IA_ADD || op == IA_MUL || op == IA_AND || op == IA_OR || op == IA_XOR; +} + +static bool IsIntegerType(InterType type) +{ + return type >= IT_INT8 && type <= IT_INT32; +} + +static bool IsScalarType(InterType type) +{ + return type >= IT_INT8 && type <= IT_INT32 || type == IT_BOOL; +} + +static int64 SignedTypeMin(InterType type) +{ + switch (type) + { + case IT_INT8: + return -128; + case IT_INT16: + return -32768; + default: + return -0x80000000LL; + } +} + +static int64 SignedTypeMax(InterType type) +{ + switch (type) + { + case IT_INT8: + return 127; + case IT_INT16: + return 32767; + default: + return 0x7fffffff; + } +} + +static int64 UnsignedTypeMax(InterType type) +{ + switch (type) + { + case IT_INT8: + return 255; + case IT_INT16: + return 65535; + default: + return 0xffffffff; + } +} + +static int64 BuildLowerBitsMask(int64 v) +{ + v |= v >> 32; + v |= v >> 16; + v |= v >> 8; + v |= v >> 4; + v |= v >> 2; + v |= v >> 1; + return v; +} + +static int64 BinMask(int64 n) +{ + n |= n >> 32; + n |= n >> 16; + n |= n >> 8; + n |= n >> 4; + n |= n >> 2; + n |= n >> 1; + return n; +} + +static bool ispow2(int64 v) +{ + if (v > 0) + { + while (!(v & 1)) + v >>= 1; + return v == 1; + } + + return 0; +} + +static int binlog(int64 n) +{ + int k = -1; + + while (n) + { + n >>= 1; + k++; + } + + return k; +} + + + +static int64 LimitIntConstValue(InterType type, int64 v) +{ + switch (type) + { + case IT_INT8: + if (v >= -128 && v < 256) + return v; + else + return v & 255; + break; + case IT_INT16: + if (v >= -32768 && v < 65536) + return v; + else + return v & 65535; + break; + default: + case IT_INT32: + return v; + } +} + + +static int64 ToTypedSigned(int64 val, InterType type) +{ + switch (InterTypeSize[type]) + { + case 1: + return int64(int8(val)); + case 4: + return int64(int32(val)); + default: + return int64(int16(val)); + } +} + +static int64 ToTypedUnsigned(int64 val, InterType type) +{ + switch (InterTypeSize[type]) + { + case 1: + return int64(uint8(val)); + case 4: + return int64(uint32(val)); + default: + return int64(uint16(val)); + } +} + +static int64 TypeShiftMask(InterType type, int64 val) +{ + switch (InterTypeSize[type]) + { + case 1: + return val & 7; + default: + case 2: + return val & 15; + case 4: + return val & 31; + } +} + + +IntegerValueRange::IntegerValueRange(void) + : mMinState(S_UNKNOWN), mMaxState(S_UNKNOWN) +{ + mMinExpanded = 0; + mMaxExpanded = 0; +} + +IntegerValueRange::~IntegerValueRange(void) +{} + +void IntegerValueRange::Reset(void) +{ + mMinState = S_UNKNOWN; + mMaxState = S_UNKNOWN; + mMinExpanded = 0; + mMaxExpanded = 0; +} + +void IntegerValueRange::Restart(void) +{ + if (mMinState == IntegerValueRange::S_UNBOUND) + mMinState = IntegerValueRange::S_UNKNOWN; + if (mMaxState == IntegerValueRange::S_UNBOUND) + mMaxState = IntegerValueRange::S_UNKNOWN; +} + + +bool IntegerValueRange::Weaker(const IntegerValueRange& range) const +{ + bool minWeak = false, maxWeak = false; + + if (range.mMinState == S_UNKNOWN) + minWeak = false; + else if (mMinState == S_UNKNOWN) + minWeak = true; + else if (mMinState == S_BOUND) + { + if (range.mMinState >= S_WEAK && mMinValue < range.mMinValue) + minWeak = true; + } + else if (mMinState == S_WEAK) + { + if (range.mMinState == S_BOUND || range.mMinState == S_UNBOUND) + minWeak = true; + if (range.mMinState == S_WEAK && mMinValue != range.mMinValue) + minWeak = true; + } + else if (mMinState == S_UNBOUND) + { + if (mMinExpanded >= 32 && range.mMinState == S_WEAK) + ; + else if (range.mMinState != S_UNBOUND) + minWeak = true; + } + + if (range.mMaxState == S_UNKNOWN) + maxWeak = false; + else if (mMaxState == S_UNKNOWN) + maxWeak = true; + else if (mMaxState == S_BOUND) + { + if (range.mMaxState >= S_WEAK && mMaxValue > range.mMaxValue) + maxWeak = true; + } + else if (mMaxState == S_WEAK) + { + if (range.mMaxState == S_BOUND || range.mMaxState == S_UNBOUND) + maxWeak = true; + if (range.mMaxState == S_WEAK && mMaxValue != range.mMaxValue) + maxWeak = true; + } + else if (mMaxState == S_UNBOUND) + { + if (mMaxExpanded >= 32 && range.mMaxState == S_WEAK) + ; + else if (range.mMaxState != S_UNBOUND) + maxWeak = true; + } + + return minWeak || maxWeak; +} + +bool IntegerValueRange::Same(const IntegerValueRange& range) const +{ + if (mMinState == range.mMinState && mMaxState == range.mMaxState) + { + if ((mMinState == S_BOUND || mMinState == S_WEAK) && mMinValue != range.mMinValue) + return false; + if ((mMaxState == S_BOUND || mMaxState == S_WEAK) && mMaxValue != range.mMaxValue) + return false; + + return true; + } + + return false; +} + +void IntegerValueRange::SetMin(int64 value) +{ + mMinState = S_BOUND; + mMinValue = value; +} + +bool IntegerValueRange::LimitMin(int64 value) +{ + if (mMinState != S_BOUND || mMinValue < value) + { + mMinState = S_BOUND; + mMinValue = value; + return true; + } + return false; +} + +bool IntegerValueRange::LimitMax(int64 value) +{ + if (mMaxState != S_BOUND || mMaxValue > value) + { + mMaxState = S_BOUND; + mMaxValue = value; + return true; + } + return false; +} + +void IntegerValueRange::LimitMinBound(int64 value) +{ + if (mMinState == S_BOUND && mMinValue < value) + mMinValue = value; +} + +void IntegerValueRange::LimitMaxBound(int64 value) +{ + if (mMaxState == S_BOUND && mMaxValue > value) + mMaxValue = value; +} + +void IntegerValueRange::LimitMinWeak(int64 value) +{ + if (mMinState == S_UNBOUND || mMinState != S_UNKNOWN && mMinValue < value) + { + mMinState = S_BOUND; + mMinValue = value; + } + +} + +void IntegerValueRange::AddConstValue(InterType type, int64 value) +{ + if (value > 0 && IntegerValueRange::S_WEAK) + mMaxState = S_UNBOUND; + else if (value < 0 && mMinState == S_WEAK) + mMinState = S_UNBOUND; + + if (type == IT_INT8 && value >= 128 && mMaxState != S_BOUND) + mMinState = S_UNBOUND; + + mMinValue += value; + mMaxValue += value; + + if (type == IT_INT8) + { + if (mMinState == S_BOUND && mMinValue < -255 || mMaxState == S_BOUND && mMaxValue > 255) + { + LimitMax(255); + LimitMin(-128); + } + } +} + + +void IntegerValueRange::LimitMaxWeak(int64 value) +{ + if (mMaxState == S_UNBOUND || mMaxState != S_UNKNOWN && mMaxValue > value) + { + mMaxState = S_BOUND; + mMaxValue = value; + } +} + +bool IntegerValueRange::IsInvalid(void) const +{ + return mMinState == S_BOUND && mMaxState == S_BOUND && mMinValue > mMaxValue; +} + +bool IntegerValueRange::IsBound(void) const +{ + return mMinState == S_BOUND && mMaxState == S_BOUND && mMinValue <= mMaxValue; +} + +bool IntegerValueRange::IsRange(int64 minval, int64 maxval) const +{ + return + mMinState == S_BOUND && mMinValue >= minval && + mMaxState == S_BOUND && mMaxValue <= maxval && + mMinValue <= mMaxValue; +} + +bool IntegerValueRange::IsConstant(void) const +{ + return mMinState == S_BOUND && mMaxState == S_BOUND && mMinValue == mMaxValue; +} + +void IntegerValueRange::MergeUnknown(const IntegerValueRange& range) +{ + if (mMinState != S_BOUND) + { + mMinState = range.mMinState; + mMinValue = range.mMinValue; + } + else if (range.mMinState == S_BOUND && mMinValue < range.mMinValue) + mMinValue = range.mMinValue; + + if (mMaxState != S_BOUND) + { + mMaxState = range.mMaxState; + mMaxValue = range.mMaxValue; + } + else if (range.mMaxState == S_BOUND && mMaxValue > range.mMaxValue) + mMaxValue = range.mMaxValue; +} + + +void IntegerValueRange::LimitWeak(const IntegerValueRange& range) +{ + if (range.mMinState == S_BOUND) + LimitMinWeak(range.mMinValue); + if (range.mMaxState == S_BOUND) + LimitMaxWeak(range.mMaxValue); +} + +bool IntegerValueRange::Limit(const IntegerValueRange& range) +{ + bool changed = false; + if (range.mMinState == S_BOUND && LimitMin(range.mMinValue)) + changed = true; + + if (range.mMaxState == S_BOUND && LimitMax(range.mMaxValue)) + changed = true; + + return changed; +} + + +void IntegerValueRange::SetConstant(int64 value) +{ + mMinState = S_BOUND; + mMaxState = S_BOUND; + mMinValue = value; + mMaxValue = value; +} + +void IntegerValueRange::SetLimit(int64 minValue, int64 maxValue) +{ + mMinState = S_BOUND; + mMinValue = minValue; + mMaxState = S_BOUND; + mMaxValue = maxValue; +} + +void IntegerValueRange::SetBounds(State minState, int64 minValue, State maxState, int64 maxValue) +{ + mMinState = minState; + mMinValue = minValue; + mMaxState = maxState; + mMaxValue = maxValue; +} + + +void IntegerValueRange::Expand(const IntegerValueRange& range) +{ + if (mMinState == S_BOUND) + { + if (range.mMinState == S_BOUND) + { + if (range.mMinValue > mMinValue) + mMinValue = range.mMinValue; + } + else if (range.mMinState == S_WEAK) + { + if (range.mMinValue > mMinValue) + { + mMinValue = range.mMinValue; + mMinState = S_WEAK; + } + } + } + else if (mMinState == S_WEAK) + { + if (range.mMinState == S_BOUND) + { + mMinState = range.mMinState; + mMinValue = range.mMinValue; + } + else if (range.mMinState == S_WEAK) + { + if (range.mMinValue != mMinValue) + { + mMinExpanded++; + mMinValue = range.mMinValue; + if (mMinExpanded >= 32) + mMinState = S_UNBOUND; + } + } + else if (range.mMinState == S_UNBOUND) + mMinState = S_UNBOUND; + } + else if (mMinState == S_UNKNOWN || range.mMinState != S_UNKNOWN) + { + mMinState = range.mMinState; + mMinValue = range.mMinValue; + } + + if (mMaxState == S_BOUND) + { + if (range.mMaxState == S_BOUND) + { + if (range.mMaxValue < mMaxValue) + mMaxValue = range.mMaxValue; + } + else if (range.mMaxState == S_WEAK) + { + if (range.mMaxValue < mMaxValue) + { + mMaxValue = range.mMaxValue; + mMaxState = S_WEAK; + } + } + } + else if (mMaxState == S_WEAK) + { + if (range.mMaxState == S_BOUND) + { + mMaxValue = range.mMaxValue; + mMaxState = S_BOUND; + } + else if (range.mMaxState == S_WEAK) + { + if (range.mMaxValue != mMaxValue) + { + mMaxExpanded++; + mMaxValue = range.mMaxValue; + if (mMaxExpanded >= 32) + mMaxState = S_UNBOUND; + } + } + else if (range.mMaxState == S_UNBOUND) + mMaxState = S_UNBOUND; + } + else if (mMaxState == S_UNKNOWN || range.mMaxState != S_UNKNOWN) + { + mMaxState = range.mMaxState; + mMaxValue = range.mMaxValue; + } + +#if 0 + if (range.mMinState == S_BOUND && mMinState == S_BOUND && range.mMinValue < mMinValue) + { + mMinValue = range.mMinValue; + if (mMinExpanded >= 32) + mMinState = S_UNBOUND; + else + mMinExpanded++; + } + else + { + mMinState = range.mMinState; + mMinValue = range.mMinValue; + } + if (range.mMaxState == S_BOUND && mMaxState == S_BOUND && range.mMaxValue > mMaxValue) + { + mMaxValue = range.mMaxValue; + if (mMaxExpanded >= 32) + mMaxState = S_UNBOUND; + else + mMaxExpanded++; + } + else + { + mMaxState = range.mMaxState; + mMaxValue = range.mMaxValue; + } +#endif +} + +void IntegerValueRange::Union(const IntegerValueRange& range) +{ + if (range.mMinState == S_UNBOUND || mMinState == S_UNBOUND) + mMinState = S_UNBOUND; + else if (range.mMinState == S_UNKNOWN || mMinState == S_UNKNOWN) + mMinState = S_UNKNOWN; + else + { + mMinValue = int64min(mMinValue, range.mMinValue); + if (range.mMinState == S_WEAK) + mMinState = S_WEAK; + } + if (range.mMaxState == S_UNBOUND || mMaxState == S_UNBOUND) + mMaxState = S_UNBOUND; + else if (range.mMaxState == S_UNKNOWN || mMaxState == S_UNKNOWN) + mMaxState = S_UNKNOWN; + else + { + mMaxValue = int64max(mMaxValue, range.mMaxValue); + if (range.mMaxState == S_WEAK) + mMaxState = S_WEAK; + } +} + +bool IntegerValueRange::Merge(const IntegerValueRange& range, bool head, bool initial) +{ + bool changed = false; + + if (mMinState != S_UNBOUND) + { + if (range.mMinState == S_UNKNOWN) + { + if (head) + { + if (mMinState == S_BOUND) + mMinState = S_WEAK; + } + else if (mMinState != S_UNKNOWN) + { + mMinState = S_UNKNOWN; + changed = true; + } + } + else if (range.mMinState == S_UNBOUND) + { + mMinState = S_UNBOUND; + changed = true; + } + else if (mMinState == S_UNKNOWN) + { + if (head) + { + mMinState = S_WEAK; + mMinValue = range.mMinValue; + changed = true; + } + } + else if (range.mMinValue < mMinValue) + { + if (range.mMinState == S_WEAK && (initial || !head)) + mMinState = S_WEAK; + mMinValue = range.mMinValue; + changed = true; + } + else if (mMinState == S_BOUND && range.mMinState == S_WEAK && (initial || !head)) + { + mMinState = S_WEAK; + changed = true; + } + } + + if (mMaxState != S_UNBOUND) + { + if (range.mMaxState == S_UNKNOWN) + { + if (head) + { + if (mMaxState == S_BOUND) + mMaxState = S_WEAK; + } + else if (mMaxState != S_UNKNOWN) + { + mMaxState = S_UNKNOWN; + changed = true; + } + } + else if (range.mMaxState == S_UNBOUND) + { + mMaxState = S_UNBOUND; + changed = true; + } + else if (mMaxState == S_UNKNOWN) + { + if (head) + { + mMaxState = S_WEAK; + mMaxValue = range.mMaxValue; + changed = true; + } + } + else if (range.mMaxValue > mMaxValue) + { + if (range.mMaxState == S_WEAK && (initial || !head)) + mMaxState = S_WEAK; + mMaxValue = range.mMaxValue; + changed = true; + } + else if (mMaxState == S_BOUND && range.mMaxState == S_WEAK && (initial || !head)) + { + mMaxState = S_WEAK; + changed = true; + } + } + + return changed; +} + + +ValueSet::ValueSet(void) +{ + mSize = 32; + mNum = 0; + mInstructions = new InterInstructionPtr[mSize]; +} + +ValueSet::ValueSet(const ValueSet& values) +{ + int i; + + mSize = values.mSize; + mNum = values.mNum; + mInstructions = new InterInstructionPtr[mSize]; + + for (i = 0; i < mNum; i++) + mInstructions[i] = values.mInstructions[i]; +} + +ValueSet& ValueSet::operator=(const ValueSet& values) +{ + int i; + + mNum = values.mNum; + if (mSize != values.mSize) + { + delete[] mInstructions; + mSize = values.mSize; + mInstructions = new InterInstructionPtr[mSize]; + } + + for (i = 0; i < mNum; i++) + mInstructions[i] = values.mInstructions[i]; + + return *this; +} + +ValueSet::~ValueSet(void) +{ + delete[] mInstructions; +} + +void ValueSet::FlushAll(void) +{ + mNum = 0; +} + +void ValueSet::FlushFrameAliases(void) +{ + int i; + + i = 0; + + while (i < mNum) + { + if (mInstructions[i]->mCode == IC_CONSTANT && mInstructions[i]->mDst.mType == IT_POINTER && mInstructions[i]->mConst.mMemory == IM_FRAME) + { + // + // Address in frame space + // + mNum--; + if (i < mNum) + { + mInstructions[i] = mInstructions[mNum]; + } + } + else + i++; + } +} + + +static bool MemPtrRange(const InterInstruction* ins, const GrowingInstructionPtrArray& tvalue, InterMemory& mem, int& vindex, int& offset) +{ + while (ins && ins->mCode == IC_LEA && ins->mSrc[1].mMemory == IM_INDIRECT) + ins = tvalue[ins->mSrc[1].mTemp]; + + if (ins) + { + if (ins->mCode == IC_CONSTANT) + { + mem = ins->mConst.mMemory; + vindex = ins->mConst.mVarIndex; + offset = int(ins->mConst.mIntConst); + + return true; + } + else if (ins->mCode == IC_LEA) + { + mem = ins->mSrc[1].mMemory; + vindex = ins->mSrc[1].mVarIndex; + offset = int(ins->mSrc[1].mIntConst); + + return true; + } + } + + return false; +} + + +static bool MemRange(const InterInstruction* ins, const GrowingInstructionPtrArray& tvalue, InterMemory& mem, int& vindex, int& offset, int& size) +{ + if (ins->mCode == IC_LOAD && ins->mSrc[0].mMemory == IM_INDIRECT) + { + size = ins->mSrc[0].mOperandSize; + return MemPtrRange(tvalue[ins->mSrc[0].mTemp], tvalue, mem, vindex, offset); + } + else if (ins->mSrc[1].mMemory == IM_INDIRECT) + { + if (ins->mCode == IC_COPY ||ins->mCode == IC_FILL) + size = ins->mConst.mOperandSize; + else + size = ins->mSrc[1].mOperandSize; + return MemPtrRange(tvalue[ins->mSrc[1].mTemp], tvalue, mem, vindex, offset); + } + + if (ins) + { + if (ins->mCode == IC_LOAD) + { + mem = ins->mSrc[0].mMemory; + vindex = ins->mSrc[0].mVarIndex; + offset = int(ins->mSrc[0].mIntConst); + size = ins->mSrc[0].mOperandSize; + } + else + { + mem = ins->mSrc[1].mMemory; + vindex = ins->mSrc[1].mVarIndex; + offset = int(ins->mSrc[1].mIntConst); + size = ins->mSrc[1].mOperandSize; + } + + return true; + } + + return false; +} + +void ValueSet::FlushCallAliases(const GrowingInstructionPtrArray& tvalue, const NumberSet& aliasedLocals, const NumberSet& aliasedParams) +{ + int i; + + InterMemory mem; + int vindex; + int offset; + int size; + + i = 0; + while (i < mNum) + { + if (mInstructions[i]->mCode == IC_LOAD || mInstructions[i]->mCode == IC_STORE) + { + if (MemRange(mInstructions[i], tvalue, mem, vindex, offset, size) && + ((mem == IM_PARAM && !aliasedParams[vindex]) || + (mem == IM_LOCAL && !aliasedLocals[vindex]))) + i++; + else + { + // + // potential alias load + // + mNum--; + if (i < mNum) + { + mInstructions[i] = mInstructions[mNum]; + } + } + } + else + i++; + } +} + +static bool CollidingMemType(InterType type1, InterType type2) +{ + if (type1 == IT_NONE || type2 == IT_NONE) + return true; + else// if (type1 == IT_POINTER || type1 == IT_FLOAT || type2 == IT_POINTER || type2 == IT_FLOAT) + return type1 == type2; +// else +// return false; + +} + +static bool SameMem(const InterOperand& op1, const InterOperand& op2) +{ + if (op1.mMemory != op2.mMemory || op1.mType != op2.mType || op1.mIntConst != op2.mIntConst) + return false; + + switch (op1.mMemory) + { + case IM_LOCAL: + case IM_FPARAM: + case IM_PARAM: + return op1.mVarIndex == op2.mVarIndex; + case IM_ABSOLUTE: + return true; + case IM_GLOBAL: + return op1.mLinkerObject == op2.mLinkerObject; + case IM_INDIRECT: + return op1.mTemp == op2.mTemp; + default: + return false; + } +} + +static bool SameMemRegion(const InterOperand& op1, const InterOperand& op2) +{ + if (op1.mMemory != op2.mMemory) + return false; + + switch (op1.mMemory) + { + case IM_LOCAL: + case IM_FPARAM: + case IM_PARAM: + case IM_FRAME: + case IM_FFRAME: + return true; + case IM_ABSOLUTE: + return true; + case IM_GLOBAL: + return op1.mLinkerObject == op2.mLinkerObject; + case IM_INDIRECT: + return op1.mTemp == op2.mTemp; + default: + return false; + } +} + +// returns true if op2 is part of op1 +static bool SameMemSegment(const InterOperand& op1, const InterOperand& op2) +{ + if (op1.mMemory != op2.mMemory || op1.mStride != op2.mStride) + return false; + + if (op1.mStride == 1) + { + if (op1.mIntConst > op2.mIntConst || op1.mIntConst + op1.mOperandSize < op2.mIntConst + op2.mOperandSize) + return false; + } + else + { + if (op1.mIntConst % op1.mStride != op2.mIntConst % op2.mStride) + return false; + if (op1.mIntConst > op2.mIntConst || op1.mIntConst + op1.mOperandSize * op1.mStride < op2.mIntConst + op2.mOperandSize * op2.mStride) + return false; + } + + switch (op1.mMemory) + { + case IM_LOCAL: + case IM_FPARAM: + case IM_PARAM: + return op1.mVarIndex == op2.mVarIndex; + case IM_ABSOLUTE: + return true; + case IM_GLOBAL: + return op1.mLinkerObject == op2.mLinkerObject; + case IM_INDIRECT: + return op1.mTemp == op2.mTemp; + default: + return false; + } +} + +static bool SameMemAndSize(const InterOperand& op1, const InterOperand& op2) +{ + if (op1.mMemory != op2.mMemory || op1.mType != op2.mType || op1.mIntConst != op2.mIntConst || op1.mOperandSize != op2.mOperandSize || op1.mStride != op2.mStride) + return false; + + switch (op1.mMemory) + { + case IM_LOCAL: + case IM_FPARAM: + case IM_PARAM: + return op1.mVarIndex == op2.mVarIndex; + case IM_ABSOLUTE: + return true; + case IM_GLOBAL: + return op1.mLinkerObject == op2.mLinkerObject; + case IM_INDIRECT: + return op1.mTemp == op2.mTemp; + default: + return false; + } +} + +static bool SameMem(const InterOperand& op, const InterInstruction* ins) +{ + if (ins->mCode == IC_LOAD) + return SameMem(op, ins->mSrc[0]); + else if (ins->mCode == IC_STORE) + return SameMem(op, ins->mSrc[1]); + else + return false; +} + +static bool SameMemAndSize(const InterOperand& op, const InterInstruction* ins) +{ + if (ins->mCode == IC_LOAD) + return SameMemAndSize(op, ins->mSrc[0]); + else if (ins->mCode == IC_STORE) + return SameMemAndSize(op, ins->mSrc[1]); + else + return false; +} + +static bool SameInstruction(const InterInstruction* ins1, const InterInstruction* ins2) +{ + if (ins1->mCode == ins2->mCode && ins1->mNumOperands == ins2->mNumOperands) + { + if ((ins1->mCode == IC_BINARY_OPERATOR || ins1->mCode == IC_UNARY_OPERATOR || ins1->mCode == IC_RELATIONAL_OPERATOR || ins1->mCode == IC_CONVERSION_OPERATOR) && ins1->mOperator != ins2->mOperator) + return false; + + if (ins1->mCode == IC_BINARY_OPERATOR && IsCommutative(ins1->mOperator)) + { + return + ins1->mSrc[0].IsEqual(ins2->mSrc[0]) && ins1->mSrc[1].IsEqual(ins2->mSrc[1]) || + ins1->mSrc[0].IsEqual(ins2->mSrc[1]) && ins1->mSrc[1].IsEqual(ins2->mSrc[0]); + } + else + { + for (int i = 0; i < ins1->mNumOperands; i++) + if (!ins1->mSrc[i].IsEqual(ins2->mSrc[i])) + return false; + return true; + } + } + + return false; +} + +static void SwapInstructions(InterInstruction* it, InterInstruction* ib) +{ + for (int i = 0; i < ib->mNumOperands; i++) + { + if (ib->mSrc[i].mTemp >= 0 && ib->mSrc[i].mFinal) + { + for (int j = 0; j < it->mNumOperands; j++) + { + if (it->mSrc[j].mTemp == ib->mSrc[i].mTemp) + { + it->mSrc[j].mFinal = true; + ib->mSrc[i].mFinal = false; + } + } + } + } +} + + +bool InterCodeBasicBlock::CollidingMem(const InterOperand& op1, InterType type1, const InterOperand& op2, InterType type2) const +{ + if (op1.mMemory != op2.mMemory) + { + if (op1.mMemory == IM_INDIRECT) + { + if (op1.mRestricted) + return false; + else if (op2.mMemory == IM_GLOBAL) + { + if (op2.mVarIndex >= 0) + { + if (op1.mMemoryBase == IM_GLOBAL) + return op1.mVarIndex == op2.mVarIndex && ( + mProc->mModule->mGlobalVars[op2.mVarIndex]->mSize == InterTypeSize[type1] || + mProc->mModule->mGlobalVars[op2.mVarIndex]->mSize == InterTypeSize[type2] || + CollidingMemType(type1, type2)); + else + return mProc->mModule->mGlobalVars[op2.mVarIndex]->mAliased; + } + else if (op1.mVarIndex >= 0) + return false; + else + return op1.mLinkerObject == op2.mLinkerObject; + } + else if (op2.mMemory == IM_FPARAM || op2.mMemory == IM_FFRAME) + return false; + else if (op2.mMemory == IM_LOCAL) + { + if (op1.mMemoryBase == IM_LOCAL) + return op1.mVarIndex == op2.mVarIndex && ( + mProc->mLocalVars[op2.mVarIndex]->mSize == InterTypeSize[type1] || + mProc->mLocalVars[op2.mVarIndex]->mSize == InterTypeSize[type2] || + CollidingMemType(type1, type2)); + else + return mProc->mLocalVars[op2.mVarIndex]->mAliased && CollidingMemType(type1, type2); + } + else if (op2.mMemory == IM_INDIRECT && (op1.mMemoryBase != IM_NONE && op2.mMemoryBase != IM_NONE && op1.mMemoryBase != IM_INDIRECT && op2.mMemoryBase != IM_INDIRECT)) + { + if (op1.mMemoryBase == op2.mMemoryBase) + { + if (op1.mMemoryBase == IM_LOCAL || op1.mMemoryBase == IM_GLOBAL) + return op1.mVarIndex == op2.mVarIndex && CollidingMemType(type1, type2); + else + return CollidingMemType(type1, type2); + } + else + return false; + } + else + return CollidingMemType(type1, type2); + } + else if (op2.mMemory == IM_INDIRECT) + { + if (op2.mRestricted) + return false; + else if (op1.mMemory == IM_GLOBAL) + { + if (op1.mVarIndex >= 0) + { + if (op2.mMemoryBase == IM_GLOBAL) + return op1.mVarIndex == op2.mVarIndex && ( + mProc->mModule->mGlobalVars[op2.mVarIndex]->mSize == InterTypeSize[type1] || + mProc->mModule->mGlobalVars[op2.mVarIndex]->mSize == InterTypeSize[type2] || + CollidingMemType(type1, type2)); + else + return mProc->mModule->mGlobalVars[op1.mVarIndex]->mAliased; + } + else if (op2.mVarIndex >= 0) + return false; + else + return op1.mLinkerObject == op2.mLinkerObject; + } + else if (op1.mMemory == IM_FPARAM || op1.mMemory == IM_FFRAME) + return false; + else if (op1.mMemory == IM_LOCAL) + { + if (op2.mMemoryBase == IM_LOCAL) + return op1.mVarIndex == op2.mVarIndex && ( + mProc->mLocalVars[op1.mVarIndex]->mSize == InterTypeSize[type1] || + mProc->mLocalVars[op1.mVarIndex]->mSize == InterTypeSize[type2] || + CollidingMemType(type1, type2)); + else + return mProc->mLocalVars[op1.mVarIndex]->mAliased && CollidingMemType(type1, type2); + } + else + return CollidingMemType(type1, type2); + } + else + return false; + } + + switch (op1.mMemory) + { + case IM_LOCAL: + case IM_PARAM: + case IM_FRAME: + return op1.mVarIndex == op2.mVarIndex && op1.mIntConst < op2.mIntConst + op2.mOperandSize && op2.mIntConst < op1.mIntConst + op1.mOperandSize; + case IM_FPARAM: + case IM_FFRAME: + return op1.mVarIndex + op1.mIntConst < op2.mVarIndex + op2.mIntConst + op2.mOperandSize && op2.mVarIndex + op2.mIntConst < op1.mVarIndex + op1.mIntConst + op1.mOperandSize; + case IM_ABSOLUTE: + return op1.mIntConst < op2.mIntConst + op2.mOperandSize && op2.mIntConst < op1.mIntConst + op1.mOperandSize; + case IM_GLOBAL: + if (op1.mLinkerObject == op2.mLinkerObject) + return op1.mIntConst < op2.mIntConst + op2.mOperandSize && op2.mIntConst < op1.mIntConst + op1.mOperandSize; + else + return false; + case IM_INDIRECT: + if (op1.mTemp == op2.mTemp) + return op1.mIntConst < op2.mIntConst + op2.mOperandSize && op2.mIntConst < op1.mIntConst + op1.mOperandSize; + else if (op1.mLinkerObject && op2.mLinkerObject && op1.mLinkerObject != op2.mLinkerObject) + return false; + else if (op1.mRestricted && op2.mRestricted && op1.mRestricted != op2.mRestricted) + return false; + else if (op1.mMemoryBase != op2.mMemoryBase && op1.mMemoryBase != IM_INDIRECT && op1.mMemoryBase != IM_NONE && op2.mMemoryBase != IM_INDIRECT && op2.mMemoryBase != IM_NONE) + return false; + else + return CollidingMemType(type1, type2); + default: + return false; + } +} + +bool InterCodeBasicBlock::CollidingMem(const InterOperand& op, InterType type, const InterInstruction* ins) const +{ + if (ins->mCode == IC_LOAD) + return CollidingMem(op, type, ins->mSrc[0], ins->mDst.mType); + else if (ins->mCode == IC_STORE) + return CollidingMem(op, type, ins->mSrc[1], ins->mSrc[0].mType); + else if (ins->mCode == IC_FILL) + return CollidingMem(op, type, ins->mSrc[1], IT_NONE); + else if (ins->mCode == IC_COPY || ins->mCode == IC_STRCPY) + return CollidingMem(op, type, ins->mSrc[0], IT_NONE) || CollidingMem(op, type, ins->mSrc[1], IT_NONE); + else + return false; +} + +bool InterCodeBasicBlock::CollidingMem(const InterInstruction* ins1, const InterInstruction* ins2) const +{ + if (ins1->mCode == IC_LOAD) + return CollidingMem(ins1->mSrc[0], ins1->mDst.mType, ins2); + else if (ins1->mCode == IC_STORE) + return CollidingMem(ins1->mSrc[1], ins1->mSrc[0].mType, ins2); + else if (ins1->mCode == IC_FILL) + return CollidingMem(ins1->mSrc[1], IT_NONE, ins2); + else if (ins1->mCode == IC_COPY || ins1->mCode == IC_STRCPY) + return CollidingMem(ins1->mSrc[0], IT_NONE, ins2) || CollidingMem(ins1->mSrc[1], IT_NONE, ins2); + else + return false; +} + + +bool InterCodeBasicBlock::AliasingMem(const InterInstruction* ins1, const InterInstruction* ins2) const +{ + if (ins1->mCode == IC_LOAD) + return AliasingMem(ins1->mSrc[0], ins1->mDst.mType, ins2); + else if (ins1->mCode == IC_STORE) + return AliasingMem(ins1->mSrc[1], ins1->mSrc[0].mType, ins2); + else if (ins1->mCode == IC_FILL) + return AliasingMem(ins1->mSrc[1], IT_NONE, ins2); + else if (ins1->mCode == IC_COPY || ins1->mCode == IC_STRCPY) + return AliasingMem(ins1->mSrc[0], IT_NONE, ins2) || AliasingMem(ins1->mSrc[1], IT_NONE, ins2); + else + return false; +} + +bool InterCodeBasicBlock::AliasingMem(const InterOperand& op, InterType type, const InterInstruction* ins) const +{ + if (ins->mCode == IC_LOAD) + return AliasingMem(op, type, ins->mSrc[0], ins->mDst.mType); + else if (ins->mCode == IC_STORE) + return AliasingMem(op, type, ins->mSrc[1], ins->mSrc[0].mType); + else if (ins->mCode == IC_FILL) + return AliasingMem(op, type, ins->mSrc[1], IT_NONE); + else if (ins->mCode == IC_COPY || ins->mCode == IC_STRCPY) + return AliasingMem(op, type, ins->mSrc[0], IT_NONE) || AliasingMem(op, type, ins->mSrc[1], IT_NONE); + else + return false; +} + +bool InterCodeBasicBlock::AliasingMem(const InterOperand& op1, InterType type1, const InterOperand& op2, InterType type2) const +{ + if (type1 != IT_NONE && type1 == type2 && SameMem(op1, op2)) + return false; + else + return CollidingMem(op1, type1, op2, type2); +} + +bool InterCodeBasicBlock::AliasingMem(InterCodeBasicBlock* block, InterInstruction* lins, int from, int to) const +{ + if (to > block->mInstructions.Size()) + to = block->mInstructions.Size(); + for (int i = from; i < to; i++) + if (AliasingMem(block->mInstructions[i], lins)) + return true; + return false; +} + + +bool InterCodeBasicBlock::DestroyingMem(const InterInstruction* lins, const InterInstruction* sins) const +{ + if (sins->mCode == IC_LOAD) + return false; + else if (sins->mCode == IC_STORE) + return CollidingMem(sins->mSrc[1], sins->mSrc[0].mType, lins); + else if (sins->mCode == IC_FILL) + return CollidingMem(sins->mSrc[1], IT_NONE, lins); + else if (sins->mCode == IC_COPY || sins->mCode == IC_STRCPY) + return CollidingMem(sins->mSrc[1], IT_NONE, lins); + else if (sins->mCode == IC_FREE) + { + int opmask = 0; + if (lins->mCode == IC_LOAD) + opmask = 1; + else if (lins->mCode == IC_STORE) + opmask = 2; + else if (lins->mCode == IC_FILL) + opmask = 2; + else if (lins->mCode == IC_COPY) + opmask = 3; + + for (int k = 0; k < lins->mNumOperands; k++) + { + if ((1 << k) & opmask) + { + const InterOperand& op(lins->mSrc[k]); + if (op.mMemory == IM_INDIRECT) + return true; + } + } + + return false; + } + else if (sins->mCode == IC_CALL || sins->mCode == IC_CALL_NATIVE) + { + if (sins->mSrc[0].mTemp < 0 && sins->mSrc[0].mLinkerObject) + { + InterCodeProcedure* proc = sins->mSrc[0].mLinkerObject->mProc; + if (proc) + { + int opmask = 0; + if (lins->mCode == IC_LOAD) + opmask = 1; + else if (lins->mCode == IC_STORE) + opmask = 2; + else if (lins->mCode == IC_FILL) + opmask = 2; + else if (lins->mCode == IC_COPY) + opmask = 3; + + for (int k = 0; k < lins->mNumOperands; k++) + { + if ((1 << k) & opmask) + { + const InterOperand& op(lins->mSrc[k]); + + if (op.mTemp >= 0) + { + if (proc->mStoresIndirect) + return true; + } + else if (op.mMemory == IM_FFRAME || op.mMemory == IM_FRAME) + return true; + else if (op.mMemory == IM_GLOBAL) + { + if (proc->ModifiesGlobal(op.mVarIndex)) + return true; + } + else if (op.mMemory == IM_LOCAL && !mProc->mLocalVars[op.mVarIndex]->mAliased) + ; + else if ((op.mMemory == IM_PARAM || op.mMemory == IM_FPARAM) && !mProc->mParamVars[op.mVarIndex]->mAliased) + ; + else + return true; + } + } + + return false; + } + } + + return true; + } + else + return false; +} + +bool InterCodeBasicBlock::DestroyingMem(InterCodeBasicBlock* block, InterInstruction* lins, int from, int to) const +{ + for (int i = from; i < to; i++) + { + InterInstruction* ins = block->mInstructions[i]; + if (DestroyingMem(lins, ins)) + return true; + } + + return false; +} + +bool InterCodeBasicBlock::CanSwapInstructions(const InterInstruction* ins0, const InterInstruction* ins1) const +{ + // Cannot swap branches + if (ins1->mCode == IC_JUMP || ins1->mCode == IC_BRANCH || ins1->mCode == IC_DISPATCH || ins1->mCode == IC_RETURN || ins1->mCode == IC_RETURN_STRUCT || + ins1->mCode == IC_RETURN || ins1->mCode == IC_RETURN_STRUCT || ins1->mCode == IC_RETURN_VALUE) + return false; + + // Check function call + if (ins1->mCode == IC_CALL || ins1->mCode == IC_CALL_NATIVE || ins1->mCode == IC_ASSEMBLER) + { + if (ins0->mCode == IC_CALL || ins0->mCode == IC_CALL_NATIVE || ins0->mCode == IC_ASSEMBLER || + ins0->mCode == IC_RETURN || ins0->mCode == IC_RETURN_STRUCT || ins0->mCode == IC_RETURN_VALUE || + ins0->mCode == IC_PUSH_FRAME || ins0->mCode == IC_POP_FRAME || ins0->mCode == IC_MALLOC || ins0->mCode == IC_FREE || ins0->mCode == IC_BREAKPOINT) + return false; + + if (ins0->mCode == IC_LOAD) + { + if (ins0->mSrc[0].mTemp >= 0 || ins0->mVolatile) + return false; + if (ins0->mSrc[0].mMemory == IM_PARAM || ins0->mSrc[0].mMemory == IM_FPARAM) + { + if (mProc->mParamAliasedSet[ins0->mSrc[0].mVarIndex]) + return false; + } + else if (!ins1->mNoSideEffects) + return false; + } + else if (ins0->mCode == IC_STORE || ins0->mCode == IC_COPY || ins0->mCode == IC_STRCPY || ins0->mCode == IC_FILL) + { + if (ins0->mSrc[1].mMemory == IM_FRAME || ins0->mSrc[1].mMemory == IM_FFRAME) + return false; + if (!ins1->mNoSideEffects || !ins1->mConstExpr) + return false; + } + } + if (ins0->mCode == IC_CALL || ins0->mCode == IC_CALL_NATIVE || ins0->mCode == IC_ASSEMBLER) + { + if (ins1->mCode == IC_RETURN || ins1->mCode == IC_RETURN_STRUCT || ins1->mCode == IC_RETURN_VALUE || + ins1->mCode == IC_PUSH_FRAME || ins1->mCode == IC_POP_FRAME || ins1->mCode == IC_MALLOC || ins1->mCode == IC_FREE || ins1->mCode == IC_BREAKPOINT) + return false; + + if (ins0->mSrc[0].mMemory == IM_PROCEDURE && ins0->mSrc[0].mLinkerObject && ins0->mSrc[0].mLinkerObject->mProc && + ins0->mSrc[0].mLinkerObject->mProc->mLeafProcedure && + ins0->mSrc[0].mLinkerObject->mProc->mParamVars.Size() == 0) + { + if (ins1->mCode == IC_STORE) + { + if (ins1->mSrc[1].mMemory != IM_FRAME && ins1->mSrc[1].mMemory != IM_FFRAME) + return false; + } + else if (ins1->mCode == IC_LOAD || ins1->mCode == IC_STORE || ins1->mCode == IC_COPY || ins1->mCode == IC_STRCPY || ins1->mCode == IC_FILL) + return false; + } + else if (ins1->mCode == IC_LOAD || ins1->mCode == IC_STORE || ins1->mCode == IC_COPY || ins1->mCode == IC_STRCPY || ins1->mCode == IC_FILL) + return false; + } + + if (ins0->mCode == IC_BREAKPOINT && ins1->mCode == IC_STORE) + return false; + if (ins1->mCode == IC_BREAKPOINT && ins0->mCode == IC_STORE) + return false; + + if (ins0->mCode == IC_MALLOC || ins0->mCode == IC_FREE) + { + if (ins1->mCode == IC_MALLOC || ins1->mCode == IC_FREE) + return false; + } + + if (ins0->mCode == IC_FREE) + { + if (ins1->mCode == IC_LOAD || ins1->mCode == IC_STORE || ins1->mCode == IC_COPY || ins1->mCode == IC_STRCPY || ins1->mCode == IC_FILL) + return false; + } + if (ins1->mCode == IC_FREE) + { + if (ins0->mCode == IC_LOAD || ins0->mCode == IC_STORE || ins0->mCode == IC_COPY || ins0->mCode == IC_STRCPY || ins0->mCode == IC_FILL) + return false; + } + + // Check frame pointer + if (ins0->mCode == IC_PUSH_FRAME || ins0->mCode == IC_POP_FRAME) + { + if (ins1->mCode == IC_PUSH_FRAME || ins1->mCode == IC_POP_FRAME) + return false; + if (ins1->mCode == IC_CONSTANT && ins1->mDst.mType == IT_POINTER && ins1->mConst.mMemory == IM_FRAME) + return false; + if (ins1->mCode == IC_STORE && ins1->mSrc[1].mMemory == IM_FRAME) + return false; + } + if (ins1->mCode == IC_PUSH_FRAME || ins1->mCode == IC_POP_FRAME) + { + if (ins0->mCode == IC_CONSTANT && ins0->mDst.mType == IT_POINTER && ins0->mConst.mMemory == IM_FRAME) + return false; + if (ins0->mCode == IC_STORE && ins0->mSrc[1].mMemory == IM_FRAME) + return false; + } + + // False data dependency + if (ins1->mDst.mTemp >= 0) + { + if (ins1->mDst.mTemp == ins0->mDst.mTemp) + return false; + + for (int i = 0; i < ins0->mNumOperands; i++) + if (ins1->mDst.mTemp == ins0->mSrc[i].mTemp) + return false; + } + + // True data dependency + if (ins0->mDst.mTemp >= 0) + { + for (int i = 0; i < ins1->mNumOperands; i++) + if (ins0->mDst.mTemp == ins1->mSrc[i].mTemp) + return false; + } + + if ((ins0->mCode == IC_LOAD || ins0->mCode == IC_STORE || ins0->mCode == IC_COPY || ins0->mCode == IC_STRCPY || ins0->mCode == IC_FILL) && + (ins1->mCode == IC_LOAD || ins1->mCode == IC_STORE || ins1->mCode == IC_COPY || ins1->mCode == IC_STRCPY || ins1->mCode == IC_FILL)) + { + if (ins0->mMemmap || ins1->mMemmap) + return false; + + if (ins0->mVolatile && ins1->mVolatile) + return false; + + if (ins1->mVolatile && ins0->mCode == IC_LOAD) + { + if (ins1->mCode == IC_LOAD) + ; + else if (ins1->mCode == IC_STORE) + { + if (ins1->mSrc[1].mMemory == IM_ABSOLUTE && (ins0->mSrc[0].mMemory != IM_ABSOLUTE && ins0->mSrc[0].mMemory != IM_INDIRECT)) + ; + else + return false; + } + else + return false; + } + + if (ins0->mCode == IC_LOAD) + { + if (DestroyingMem(ins0, ins1)) + return false; + } + else if (ins1->mCode == IC_LOAD) + { + if (DestroyingMem(ins1, ins0)) + return false; + } + else if (ins0->mCode == IC_STORE || ins0->mCode == IC_COPY || ins0->mCode == IC_STRCPY || ins0->mCode == IC_FILL) + { + if (CollidingMem(ins0, ins1)) + return false; + } + } + + return true; +} + + +static int64 ConstantFolding(InterOperator oper, InterType type, int64 val1, int64 val2 = 0) +{ + switch (oper) + { + case IA_ADD: + return val1 + val2; + break; + case IA_SUB: + return val1 - val2; + break; + case IA_MUL: + if (type == IT_INT32 && val1 >= 0 && val2 >= 0) + return val1 * val2 & 0xffffffff; + else + return val1 * val2; + + case IA_DIVU: + if (val2) + { + if (type == IT_INT32) + return (uint32)val1 / (uint32)val2; + else if (type == IT_INT16) + return (uint16)val1 / (uint16)val2; + else + return (uint8)val1 / (uint8)val2; + } + else + return 0; + break; + case IA_DIVS: + if (val2) + return val1 / val2; + else + return 0; + break; + case IA_MODU: + if (val2) + { + if (type == IT_INT32) + return (uint32)val1 % (uint32)val2; + else if (type == IT_INT16) + return (uint16)val1 % (uint16)val2; + else + return (uint8)val1 % (uint8)val2; + } + else + return 0; + break; + case IA_MODS: + if (val2) + return val1 % val2; + else + return 0; + break; + case IA_OR: + return val1 | val2; + break; + case IA_AND: + return val1 & val2; + break; + case IA_XOR: + return val1 ^ val2; + break; + case IA_NEG: + return -val1; + break; + case IA_NOT: + switch (type) + { + case IT_INT8: + return uint8(~val1); + case IT_INT16: + return uint16(~val1); + case IT_INT32: + return uint32(~val1); + default: + return ~val1; + } + break; + case IA_SHL: + { + int64 shift = TypeShiftMask(type, val2); + if (val1 < 0 && shift < 16 && val1 << shift >= SignedTypeMin(type)) + return ToTypedSigned(val1 << shift, type); + else + return ToTypedUnsigned(val1 << shift, type); + } + case IA_SHR: + return ToTypedUnsigned(val1, type) >> TypeShiftMask(type, val2); + case IA_SAR: + return ToTypedSigned(val1, type) >> TypeShiftMask(type, val2); + case IA_CMPEQ: + return ToTypedUnsigned(val1, type) == ToTypedUnsigned(val2, type) ? 1 : 0; + break; + case IA_CMPNE: + return ToTypedUnsigned(val1, type) != ToTypedUnsigned(val2, type) ? 1 : 0; + break; + case IA_CMPGES: + return ToTypedSigned(val1, type) >= ToTypedSigned(val2, type) ? 1 : 0; + break; + case IA_CMPLES: + return ToTypedSigned(val1, type) <= ToTypedSigned(val2, type) ? 1 : 0; + break; + case IA_CMPGS: + return ToTypedSigned(val1, type) > ToTypedSigned(val2, type) ? 1 : 0; + break; + case IA_CMPLS: + return ToTypedSigned(val1, type) < ToTypedSigned(val2, type) ? 1 : 0; + break; + case IA_CMPGEU: + return ToTypedUnsigned(val1, type) >= ToTypedUnsigned(val2, type) ? 1 : 0; + break; + case IA_CMPLEU: + return ToTypedUnsigned(val1, type) <= ToTypedUnsigned(val2, type) ? 1 : 0; + break; + case IA_CMPGU: + return ToTypedUnsigned(val1, type) > ToTypedUnsigned(val2, type) ? 1 : 0; + break; + case IA_CMPLU: + return ToTypedUnsigned(val1, type) < ToTypedUnsigned(val2, type) ? 1 : 0; + break; + default: + return 0; + } +} + +static int64 ConstantRelationalPointerFolding(InterOperator oper, const InterOperand& op1, const InterOperand& op2) +{ + if (op1.mMemory == op2.mMemory) + { + if (op1.mMemory == IM_ABSOLUTE) + return ConstantFolding(oper, IT_INT16, op1.mIntConst, op2.mIntConst); + else if (op1.mMemory != IM_INDIRECT && op1.mVarIndex == op2.mVarIndex) + return ConstantFolding(oper, IT_INT16, op1.mIntConst, op2.mIntConst); + } + + if (oper == IA_CMPNE) + return 1; + else + return 0; +} + +static int64 ConstantRelationalFolding(InterOperator oper, double val1, double val2) +{ + switch (oper) + { + case IA_CMPEQ: + return val1 == val2 ? 1 : 0; + break; + case IA_CMPNE: + return val1 != val2 ? 1 : 0; + break; + case IA_CMPGES: + case IA_CMPGEU: + return val1 >= val2 ? 1 : 0; + break; + case IA_CMPLES: + case IA_CMPLEU: + return val1 <= val2 ? 1 : 0; + break; + case IA_CMPGS: + case IA_CMPGU: + return val1 > val2 ? 1 : 0; + break; + case IA_CMPLS: + case IA_CMPLU: + return val1 < val2 ? 1 : 0; + break; + default: + return 0; + } +} + +static double ConstantFolding(InterOperator oper, double val1, double val2 = 0.0) +{ + switch (oper) + { + case IA_ADD: + return val1 + val2; + break; + case IA_SUB: + return val1 - val2; + break; + case IA_MUL: + return val1 * val2; + break; + case IA_DIVU: + case IA_DIVS: + return val1 / val2; + break; + case IA_NEG: + return -val1; + break; + case IA_ABS: + return fabs(val1); + break; + case IA_FLOOR: + return floor(val1); + break; + case IA_CEIL: + return ceil(val1); + break; + + default: + return 0; + } +} + +InterOperator InvertRelational(InterOperator oper) +{ + switch (oper) + { + case IA_CMPGES: + return IA_CMPLS; + case IA_CMPLES: + return IA_CMPGS; + case IA_CMPGS: + return IA_CMPLES; + case IA_CMPLS: + return IA_CMPGES; + case IA_CMPGEU: + return IA_CMPLU; + case IA_CMPLEU: + return IA_CMPGU; + case IA_CMPGU: + return IA_CMPLEU; + case IA_CMPLU: + return IA_CMPGEU; + case IA_CMPEQ: + return IA_CMPNE; + case IA_CMPNE: + return IA_CMPEQ; + default: + return oper; + } + +} + +static bool IsStrictUnsignedRelational(InterOperator oper) +{ + return + oper == IA_CMPLEU || + oper == IA_CMPGEU || + oper == IA_CMPLU || + oper == IA_CMPGU; +} + +static bool IsSignedRelational(InterOperator oper) +{ + return + oper == IA_CMPEQ || + oper == IA_CMPNE || + oper == IA_CMPLES || + oper == IA_CMPGES || + oper == IA_CMPLS || + oper == IA_CMPGS; +} + +InterOperator MirrorRelational(InterOperator oper) +{ + switch (oper) + { + case IA_CMPGES: + return IA_CMPLES; + case IA_CMPLES: + return IA_CMPGES; + case IA_CMPGS: + return IA_CMPLS; + case IA_CMPLS: + return IA_CMPGS; + case IA_CMPGEU: + return IA_CMPLEU; + case IA_CMPLEU: + return IA_CMPGEU; + case IA_CMPGU: + return IA_CMPLU; + case IA_CMPLU: + return IA_CMPGU; + default: + return oper; + } + +} + + +static void ConversionConstantFold(InterInstruction * ins, const InterOperand & cop) +{ + switch (ins->mOperator) + { + case IA_INT2FLOAT: + ins->mCode = IC_CONSTANT; + ins->mConst.mFloatConst = (double)(cop.mIntConst); + ins->mConst.mType = IT_FLOAT; + ins->mSrc[0].mTemp = -1; + ins->mNumOperands = 0; + break; + case IA_FLOAT2INT: + ins->mCode = IC_CONSTANT; + ins->mConst.mIntConst = (int)(cop.mFloatConst); + ins->mConst.mType = IT_INT16; + ins->mSrc[0].mTemp = -1; + ins->mNumOperands = 0; + break; + case IA_UINT2FLOAT: + ins->mCode = IC_CONSTANT; + ins->mConst.mFloatConst = (double)((uint16)cop.mIntConst); + ins->mConst.mType = IT_FLOAT; + ins->mSrc[0].mTemp = -1; + ins->mNumOperands = 0; + break; + case IA_FLOAT2UINT: + ins->mCode = IC_CONSTANT; + ins->mConst.mIntConst = (int)(cop.mFloatConst); + ins->mConst.mType = IT_INT16; + ins->mSrc[0].mTemp = -1; + ins->mNumOperands = 0; + break; + case IA_LINT2FLOAT: + ins->mCode = IC_CONSTANT; + ins->mConst.mFloatConst = (double)(cop.mIntConst); + ins->mConst.mType = IT_FLOAT; + ins->mSrc[0].mTemp = -1; + ins->mNumOperands = 0; + break; + case IA_FLOAT2LINT: + ins->mCode = IC_CONSTANT; + ins->mConst.mIntConst = (int)(cop.mFloatConst); + ins->mConst.mType = IT_INT32; + ins->mSrc[0].mTemp = -1; + ins->mNumOperands = 0; + break; + case IA_LUINT2FLOAT: + ins->mCode = IC_CONSTANT; + ins->mConst.mFloatConst = (double)((uint32)cop.mIntConst); + ins->mConst.mType = IT_FLOAT; + ins->mSrc[0].mTemp = -1; + ins->mNumOperands = 0; + break; + case IA_FLOAT2LUINT: + ins->mCode = IC_CONSTANT; + ins->mConst.mIntConst = (uint32)(cop.mFloatConst); + ins->mConst.mType = IT_INT32; + ins->mSrc[0].mTemp = -1; + ins->mNumOperands = 0; + break; + case IA_EXT8TO16S: + ins->mCode = IC_CONSTANT; + ins->mConst.mIntConst = (int8)(cop.mIntConst); + ins->mConst.mType = IT_INT16; + ins->mSrc[0].mTemp = -1; + ins->mNumOperands = 0; + break; + case IA_EXT8TO16U: + ins->mCode = IC_CONSTANT; + ins->mConst.mIntConst = (uint8)(cop.mIntConst); + ins->mConst.mType = IT_INT16; + ins->mSrc[0].mTemp = -1; + ins->mNumOperands = 0; + break; + case IA_EXT16TO32S: + ins->mCode = IC_CONSTANT; + ins->mConst.mIntConst = (int16)(cop.mIntConst); + ins->mConst.mType = IT_INT32; + ins->mSrc[0].mTemp = -1; + ins->mNumOperands = 0; + break; + case IA_EXT16TO32U: + ins->mCode = IC_CONSTANT; + ins->mConst.mIntConst = (uint16)(cop.mIntConst); + ins->mConst.mType = IT_INT32; + ins->mSrc[0].mTemp = -1; + ins->mNumOperands = 0; + break; + case IA_EXT8TO32S: + ins->mCode = IC_CONSTANT; + ins->mConst.mIntConst = (int8)(cop.mIntConst); + ins->mConst.mType = IT_INT32; + ins->mSrc[0].mTemp = -1; + ins->mNumOperands = 0; + break; + case IA_EXT8TO32U: + ins->mCode = IC_CONSTANT; + ins->mConst.mIntConst = (uint8)(cop.mIntConst); + ins->mConst.mType = IT_INT32; + ins->mSrc[0].mTemp = -1; + ins->mNumOperands = 0; + break; + } +} + +static InterOperand OperandConstantFolding(InterOperator oper, InterOperand op1, InterOperand op2) +{ + InterOperand dop; + + switch (oper) + { + case IA_INT2FLOAT: + dop.mFloatConst = (double)(op1.mIntConst); + dop.mType = IT_FLOAT; + break; + case IA_FLOAT2INT: + dop.mIntConst = (int)(op1.mFloatConst); + dop.mType = IT_INT16; + break; + case IA_UINT2FLOAT: + dop.mFloatConst = (double)((uint16)op1.mIntConst); + dop.mType = IT_FLOAT; + break; + case IA_FLOAT2UINT: + dop.mIntConst = (int)(op1.mFloatConst); + dop.mType = IT_INT16; + break; + case IA_LINT2FLOAT: + dop.mFloatConst = (double)(op1.mIntConst); + dop.mType = IT_FLOAT; + break; + case IA_FLOAT2LINT: + dop.mIntConst = (int)(op1.mFloatConst); + dop.mType = IT_INT16; + break; + case IA_LUINT2FLOAT: + dop.mFloatConst = (double)((uint32)op1.mIntConst); + dop.mType = IT_FLOAT; + break; + case IA_FLOAT2LUINT: + dop.mIntConst = (uint32)(op1.mFloatConst); + dop.mType = IT_INT16; + break; + case IA_EXT8TO16S: + dop.mIntConst = (int8)(op1.mIntConst); + dop.mType = IT_INT16; + break; + case IA_EXT8TO16U: + dop.mIntConst = (uint8)(op1.mIntConst); + dop.mType = IT_INT16; + break; + case IA_EXT16TO32S: + dop.mIntConst = (int16)(op1.mIntConst); + dop.mType = IT_INT32; + break; + case IA_EXT16TO32U: + dop.mIntConst = (uint16)(op1.mIntConst); + dop.mType = IT_INT32; + break; + case IA_EXT8TO32S: + dop.mIntConst = (int8)(op1.mIntConst); + dop.mType = IT_INT32; + break; + case IA_EXT8TO32U: + dop.mIntConst = (uint8)(op1.mIntConst); + dop.mType = IT_INT32; + break; + case IA_CMPEQ: + if (op1.mType == IT_FLOAT) + dop.mIntConst = op1.mFloatConst == op2.mFloatConst ? 1 : 0; + else if (op1.mType == IT_POINTER) + dop.mIntConst = op1.mMemory == op2.mMemory && op1.mVarIndex == op2.mVarIndex && op1.mIntConst == op2.mIntConst && op1.mLinkerObject == op2.mLinkerObject; + else + dop.mIntConst = ToTypedUnsigned(op1.mIntConst, op1.mType) == ToTypedUnsigned(op2.mIntConst, op2.mType) ? 1 : 0; + dop.mType = IT_BOOL; + break; + case IA_CMPNE: + if (op1.mType == IT_FLOAT) + dop.mIntConst = op1.mFloatConst != op2.mFloatConst ? 1 : 0; + else if (op1.mType == IT_POINTER) + dop.mIntConst = op1.mMemory != op2.mMemory || op1.mVarIndex != op2.mVarIndex || op1.mIntConst != op2.mIntConst || op1.mLinkerObject != op2.mLinkerObject; + else + dop.mIntConst = ToTypedUnsigned(op1.mIntConst, op1.mType) != ToTypedUnsigned(op2.mIntConst, op2.mType) ? 1 : 0; + dop.mType = IT_BOOL; + break; + case IA_CMPGES: + if (op1.mType == IT_FLOAT) + dop.mIntConst = op1.mFloatConst >= op2.mFloatConst ? 1 : 0; + else if (op1.mType == IT_POINTER) + dop.mIntConst = op1.mMemory == op2.mMemory && op1.mVarIndex == op2.mVarIndex && op1.mLinkerObject == op2.mLinkerObject && op1.mIntConst >= op2.mIntConst; + else + dop.mIntConst = ToTypedSigned(op1.mIntConst, op1.mType) >= ToTypedSigned(op2.mIntConst, op2.mType) ? 1 : 0; + dop.mType = IT_BOOL; + break; + case IA_CMPGEU: + if (op1.mType == IT_FLOAT) + dop.mIntConst = op1.mFloatConst >= op2.mFloatConst ? 1 : 0; + else if (op1.mType == IT_POINTER) + dop.mIntConst = op1.mMemory == op2.mMemory && op1.mVarIndex == op2.mVarIndex && op1.mLinkerObject == op2.mLinkerObject && op1.mIntConst >= op2.mIntConst; + else + dop.mIntConst = ToTypedUnsigned(op1.mIntConst, op1.mType) >= ToTypedUnsigned(op2.mIntConst, op2.mType) ? 1 : 0; + dop.mType = IT_BOOL; + break; + case IA_CMPLES: + if (op1.mType == IT_FLOAT) + dop.mIntConst = op1.mFloatConst <= op2.mFloatConst ? 1 : 0; + else if (op1.mType == IT_POINTER) + dop.mIntConst = op1.mMemory == op2.mMemory && op1.mVarIndex == op2.mVarIndex && op1.mLinkerObject == op2.mLinkerObject && op1.mIntConst <= op2.mIntConst; + else + dop.mIntConst = ToTypedSigned(op1.mIntConst, op1.mType) <= ToTypedSigned(op2.mIntConst, op2.mType) ? 1 : 0; + dop.mType = IT_BOOL; + break; + case IA_CMPLEU: + if (op1.mType == IT_FLOAT) + dop.mIntConst = op1.mFloatConst <= op2.mFloatConst ? 1 : 0; + else if (op1.mType == IT_POINTER) + dop.mIntConst = op1.mMemory == op2.mMemory && op1.mVarIndex == op2.mVarIndex && op1.mLinkerObject == op2.mLinkerObject && op1.mIntConst <= op2.mIntConst; + else + dop.mIntConst = ToTypedUnsigned(op1.mIntConst, op1.mType) <= ToTypedUnsigned(op2.mIntConst, op2.mType) ? 1 : 0; + dop.mType = IT_BOOL; + break; + case IA_CMPGS: + if (op1.mType == IT_FLOAT) + dop.mIntConst = op1.mFloatConst > op2.mFloatConst ? 1 : 0; + else if (op1.mType == IT_POINTER) + dop.mIntConst = op1.mMemory == op2.mMemory && op1.mVarIndex == op2.mVarIndex && op1.mLinkerObject == op2.mLinkerObject && op1.mIntConst > op2.mIntConst; + else + dop.mIntConst = ToTypedSigned(op1.mIntConst, op1.mType) > ToTypedSigned(op2.mIntConst, op2.mType) ? 1 : 0; + dop.mType = IT_BOOL; + break; + case IA_CMPGU: + if (op1.mType == IT_FLOAT) + dop.mIntConst = op1.mFloatConst > op2.mFloatConst ? 1 : 0; + else if (op1.mType == IT_POINTER) + dop.mIntConst = op1.mMemory == op2.mMemory && op1.mVarIndex == op2.mVarIndex && op1.mLinkerObject == op2.mLinkerObject && op1.mIntConst > op2.mIntConst; + else + dop.mIntConst = ToTypedUnsigned(op1.mIntConst, op1.mType) > ToTypedUnsigned(op2.mIntConst, op2.mType) ? 1 : 0; + dop.mType = IT_BOOL; + break; + case IA_CMPLS: + if (op1.mType == IT_FLOAT) + dop.mIntConst = op1.mFloatConst < op2.mFloatConst ? 1 : 0; + else if (op1.mType == IT_POINTER) + dop.mIntConst = op1.mMemory == op2.mMemory && op1.mVarIndex == op2.mVarIndex && op1.mLinkerObject == op2.mLinkerObject && op1.mIntConst < op2.mIntConst; + else + dop.mIntConst = ToTypedSigned(op1.mIntConst, op1.mType) < ToTypedSigned(op2.mIntConst, op2.mType) ? 1 : 0; + dop.mType = IT_BOOL; + break; + case IA_CMPLU: + if (op1.mType == IT_FLOAT) + dop.mIntConst = op1.mFloatConst < op2.mFloatConst ? 1 : 0; + else if (op1.mType == IT_POINTER) + dop.mIntConst = op1.mMemory == op2.mMemory && op1.mVarIndex == op2.mVarIndex && op1.mLinkerObject == op2.mLinkerObject && op1.mIntConst < op2.mIntConst; + else + dop.mIntConst = ToTypedUnsigned(op1.mIntConst, op1.mType) < ToTypedUnsigned(op2.mIntConst, op2.mType) ? 1 : 0; + dop.mType = IT_BOOL; + break; + + case IA_ADD: + dop.mType = op1.mType; + if (op1.mType == IT_FLOAT) + dop.mFloatConst = op1.mFloatConst + op2.mFloatConst; + else + dop.mIntConst = LimitIntConstValue(dop.mType, op1.mIntConst + op2.mIntConst); + break; + case IA_SUB: + dop.mType = op1.mType; + if (op1.mType == IT_FLOAT) + dop.mFloatConst = op1.mFloatConst - op2.mFloatConst; + else + dop.mIntConst = LimitIntConstValue(dop.mType, op1.mIntConst - op2.mIntConst); + break; + case IA_MUL: + dop.mType = op1.mType; + if (op1.mType == IT_FLOAT) + dop.mFloatConst = op1.mFloatConst * op2.mFloatConst; + else + dop.mIntConst = LimitIntConstValue(dop.mType, op1.mIntConst * op2.mIntConst); + break; + case IA_DIVU: + dop.mType = op1.mType; + if (op1.mType == IT_FLOAT) + dop.mFloatConst = op1.mFloatConst / op2.mFloatConst; + else + dop.mIntConst = LimitIntConstValue(dop.mType, ToTypedUnsigned(op1.mIntConst, op1.mType) / ToTypedUnsigned(op2.mIntConst, op1.mType)); + break; + case IA_DIVS: + dop.mType = op1.mType; + if (op1.mType == IT_FLOAT) + dop.mFloatConst = op1.mFloatConst / op2.mFloatConst; + else + dop.mIntConst = LimitIntConstValue(dop.mType, ToTypedSigned(op1.mIntConst, op1.mType) / ToTypedSigned(op2.mIntConst, op1.mType)); + break; + case IA_MODU: + dop.mType = op1.mType; + dop.mIntConst = LimitIntConstValue(dop.mType, ToTypedUnsigned(op1.mIntConst, op1.mType) % ToTypedUnsigned(op2.mIntConst, op1.mType)); + break; + case IA_MODS: + dop.mType = op1.mType; + dop.mIntConst = LimitIntConstValue(dop.mType, ToTypedSigned(op1.mIntConst, op1.mType) % ToTypedSigned(op2.mIntConst, op1.mType)); + break; + case IA_OR: + dop.mType = op1.mType; + dop.mIntConst = LimitIntConstValue(dop.mType, op1.mIntConst | op2.mIntConst); + break; + case IA_AND: + dop.mType = op1.mType; + dop.mIntConst = LimitIntConstValue(dop.mType, op1.mIntConst & op2.mIntConst); + break; + case IA_XOR: + dop.mType = op1.mType; + dop.mIntConst = LimitIntConstValue(dop.mType, op1.mIntConst ^ op2.mIntConst); + break; + case IA_NEG: + dop.mType = op1.mType; + if (op1.mType == IT_FLOAT) + dop.mFloatConst = -op1.mFloatConst; + else + dop.mIntConst = -op1.mIntConst; + break; + case IA_ABS: + dop.mType = op1.mType; + if (op1.mType == IT_FLOAT) + { + if (op1.mFloatConst < 0) + dop.mFloatConst = -op1.mFloatConst; + else + dop.mFloatConst = op1.mFloatConst; + } + else + { + if (op1.mIntConst < 0) + dop.mIntConst = -op1.mIntConst; + else + dop.mIntConst = op1.mIntConst; + } + break; + break; + case IA_FLOOR: + dop.mType = op1.mType; + if (op1.mType == IT_FLOAT) + dop.mFloatConst = floor(op1.mFloatConst); + else + dop.mIntConst = op1.mIntConst; + break; + case IA_CEIL: + dop.mType = op1.mType; + if (op1.mType == IT_FLOAT) + dop.mFloatConst = ceil(op1.mFloatConst); + else + dop.mIntConst = op1.mIntConst; + break; + case IA_NOT: + dop.mType = op1.mType; + switch (op1.mType) + { + case IT_INT8: + dop.mIntConst = uint8(~op1.mIntConst); + case IT_INT16: + dop.mIntConst = uint16(~op1.mIntConst); + case IT_INT32: + dop.mIntConst = uint32(~op1.mIntConst); + default: + dop.mIntConst = ~op1.mIntConst; + } + break; + case IA_SHL: + dop.mType = op1.mType; + dop.mIntConst = ToTypedUnsigned(op1.mIntConst << TypeShiftMask(op1.mType, op2.mIntConst), op1.mType); + break; + case IA_SHR: + dop.mType = op1.mType; + dop.mIntConst = ToTypedUnsigned(op1.mIntConst, op1.mType) >> TypeShiftMask(op1.mType, op2.mIntConst); + break; + case IA_SAR: + dop.mType = op1.mType; + dop.mIntConst = ToTypedSigned(op1.mIntConst, op1.mType) >> TypeShiftMask(op1.mType, op2.mIntConst); + break; + } + + assert(dop.mType != IT_NONE); + + return dop; +} + +InterOperand InterCodeBasicBlock::LoadConstantOperand(const InterInstruction* ins, const InterOperand& op, InterType type, const GrowingVariableArray& staticVars, const GrowingInterCodeProcedurePtrArray& staticProcs) +{ + InterOperand cop; + + const uint8* data; + + LinkerObject* lobj; + int offset, stride; + + lobj = op.mLinkerObject; + offset = int(op.mIntConst); + stride = op.mStride; + + + if (offset >= 0 && offset + stride * (InterTypeSize[type] - 1) < lobj->mSize) + { + data = lobj->mData + offset; + + switch (type) + { + case IT_BOOL: + cop.mIntConst = data[0] ? 1 : 0; + case IT_INT8: + cop.mIntConst = data[0]; + break; + case IT_INT16: + cop.mIntConst = (int)data[0 * stride] | ((int)data[1 * stride] << 8); + break; + case IT_POINTER: + { + int i = 0; + while (i < lobj->mReferences.Size() && lobj->mReferences[i]->mOffset != offset) + i++; + if (i < lobj->mReferences.Size()) + { + int j = 0; + while (j < staticVars.Size() && !(staticVars[j] && staticVars[j]->mLinkerObject == lobj->mReferences[i]->mRefObject)) + j++; + if (j < staticVars.Size()) + { + cop.mMemory = IM_GLOBAL; + cop.mVarIndex = staticVars[j]->mIndex; + } + else + { + j = 0; + while (j < staticProcs.Size() && !(staticProcs[j] && staticProcs[j]->mLinkerObject == lobj->mReferences[i]->mRefObject)) + j++; + + if (j < staticProcs.Size()) + { + cop.mMemory = IM_PROCEDURE; + cop.mVarIndex = staticProcs[j]->mID; + } + else + { + cop.mMemory = IM_GLOBAL; + cop.mVarIndex = -1; + } + } + + cop.mLinkerObject = lobj->mReferences[i]->mRefObject; + cop.mIntConst = lobj->mReferences[i]->mRefOffset; + cop.mOperandSize = ins->mConst.mLinkerObject->mSize; + } + else + { + cop.mIntConst = (int)data[0 * stride] | ((int)data[1 * stride] << 8); + cop.mMemory = IM_ABSOLUTE; + } + + } break; + case IT_INT32: + cop.mIntConst = (int)data[0 * stride] | ((int)data[1 * stride] << 8) | ((int)data[2 * stride] << 16) | ((int)data[3 * stride] << 24); + break; + case IT_FLOAT: + { + union { float f; unsigned int v; } cc; + cc.v = (int)data[0 * stride] | (data[1 * stride] << 8) | (data[2 * stride] << 16) | (data[3 * stride] << 24); + cop.mFloatConst = cc.f; + } break; + } + } + else + { + cop.mIntConst = 0; + cop.mFloatConst = 0; + cop.mMemory = IM_ABSOLUTE; + cop.mLinkerObject = nullptr; + + mProc->mModule->mErrors->Error(ins->mLocation, EWARN_INDEX_OUT_OF_BOUNDS, "Constant index out of bounds"); + } + + cop.mType = type; + return cop; +} + +void InterCodeBasicBlock::LoadConstantFold(InterInstruction* ins, InterInstruction* ains, const GrowingVariableArray& staticVars, const GrowingInterCodeProcedurePtrArray&staticProcs) +{ + const uint8* data; + + LinkerObject * lobj; + int offset, stride; + + if (ains) + { + lobj = ains->mConst.mLinkerObject; + offset = int(ains->mConst.mIntConst); + stride = ains->mConst.mStride; + } + else + { + lobj = ins->mSrc[0].mLinkerObject; + offset = int(ins->mSrc[0].mIntConst); + stride = ins->mSrc[0].mStride; + } + + + if (offset >= 0 && offset + stride * (InterTypeSize[ins->mDst.mType] - 1) < lobj->mSize) + { + data = lobj->mData + offset; + + switch (ins->mDst.mType) + { + case IT_BOOL: + ins->mConst.mIntConst = data[0] ? 1 : 0; + case IT_INT8: + ins->mConst.mIntConst = data[0]; + break; + case IT_INT16: + ins->mConst.mIntConst = (int)data[0 * stride] | ((int)data[1 * stride] << 8); + break; + case IT_POINTER: + { + int i = 0; + while (i < lobj->mReferences.Size() && lobj->mReferences[i]->mOffset != offset) + i++; + if (i < lobj->mReferences.Size()) + { + int j = 0; + while (j < staticVars.Size() && !(staticVars[j] && staticVars[j]->mLinkerObject == lobj->mReferences[i]->mRefObject)) + j++; + if (j < staticVars.Size()) + { + ins->mConst.mMemory = IM_GLOBAL; + ins->mConst.mVarIndex = staticVars[j]->mIndex; + } + else + { + j = 0; + while (j < staticProcs.Size() && !(staticProcs[j] && staticProcs[j]->mLinkerObject == lobj->mReferences[i]->mRefObject)) + j++; + + if (j < staticProcs.Size()) + { + ins->mConst.mMemory = IM_PROCEDURE; + ins->mConst.mVarIndex = staticProcs[j]->mID; + } + else + { + ins->mConst.mMemory = IM_GLOBAL; + ins->mConst.mVarIndex = -1; + } + } + + ins->mConst.mLinkerObject = lobj->mReferences[i]->mRefObject; + ins->mConst.mIntConst = lobj->mReferences[i]->mRefOffset; + ins->mConst.mOperandSize = ins->mConst.mLinkerObject->mSize; + } + else + { + ins->mConst.mIntConst = (int)data[0 * stride] | ((int)data[1 * stride] << 8); + ins->mConst.mMemory = IM_ABSOLUTE; + } + + } break; + case IT_INT32: + ins->mConst.mIntConst = (int)data[0 * stride] | ((int)data[1 * stride] << 8) | ((int)data[2 * stride] << 16) | ((int)data[3 * stride] << 24); + break; + case IT_FLOAT: + { + union { float f; unsigned int v; } cc; + cc.v = (int)data[0 * stride] | (data[1 * stride] << 8) | (data[2 * stride] << 16) | (data[3 * stride] << 24); + ins->mConst.mFloatConst = cc.f; + } break; + } + } + else + { + ins->mConst.mIntConst = 0; + ins->mConst.mFloatConst = 0; + ins->mConst.mMemory = IM_ABSOLUTE; + ins->mConst.mLinkerObject = nullptr; + + mProc->mModule->mErrors->Error(ins->mLocation, EWARN_INDEX_OUT_OF_BOUNDS, "Constant index out of bounds"); + } + + ins->mCode = IC_CONSTANT; + ins->mConst.mType = ins->mDst.mType; + ins->mDst.mMemory = ins->mConst.mMemory; + ins->mSrc[0].mTemp = -1; + ins->mNumOperands = 0; +} + +void ValueSet::InsertValue(InterInstruction * ins) +{ + InterInstructionPtr* nins; + int i; + + if (mNum == mSize) + { + mSize *= 2; + nins = new InterInstructionPtr[mSize]; + for (i = 0; i < mNum; i++) + nins[i] = mInstructions[i]; + delete[] mInstructions; + mInstructions = nins; + } + + mInstructions[mNum++] = ins; +} + +static bool HasSideEffect(InterCode code) +{ + return code == IC_CALL || code == IC_CALL_NATIVE || code == IC_ASSEMBLER || code == IC_DISPATCH || code == IC_BREAKPOINT;/* || code == IC_MALLOC || code == IC_FREE */; +} + +static bool IsLoadStore(InterCode code) +{ + return code == IC_STORE || code == IC_LOAD || code == IC_STRCPY || code == IC_FILL || code == IC_COPY; +} + +static bool IsObservable(InterCode code) +{ + return code == IC_CALL || code == IC_CALL_NATIVE || code == IC_ASSEMBLER || code == IC_DISPATCH || code == IC_STORE || code == IC_COPY || code == IC_STRCPY || code == IC_FILL || code == IC_MALLOC || code == IC_FREE || code == IC_BREAKPOINT; +} + +static bool IsMoveable(InterCode code) +{ + if (HasSideEffect(code) || code == IC_COPY || code == IC_STRCPY || code == IC_STORE || code == IC_FILL || code == IC_BRANCH || code == IC_POP_FRAME || code == IC_PUSH_FRAME || code == IC_MALLOC || code == IC_FREE) + return false; + if (code == IC_RETURN || code == IC_RETURN_STRUCT || code == IC_RETURN_VALUE || code == IC_DISPATCH) + return false; + + return true; +} + + +static bool CanBypassLoad(const InterInstruction* lins, const InterInstruction* bins) +{ + // Check ambiguity + if (bins->mCode == IC_COPY || bins->mCode == IC_STRCPY || bins->mCode == IC_FILL) + return false; + + // Side effects + if (bins->mCode == IC_CALL || bins->mCode == IC_CALL_NATIVE || bins->mCode == IC_ASSEMBLER) + return false; + + // True data dependency + if (bins->UsesTemp(lins->mDst.mTemp)) + return false; + + if (bins->mCode == IC_STORE) + { + if (lins->mVolatile) + return false; + else if (lins->mSrc[0].mMemory == IM_INDIRECT && bins->mSrc[1].mMemory == IM_INDIRECT) + { + return lins->mSrc[0].mLinkerObject && bins->mSrc[1].mLinkerObject && lins->mSrc[0].mLinkerObject != bins->mSrc[1].mLinkerObject; + } + else if (lins->mSrc[0].mTemp >= 0 || bins->mSrc[1].mTemp >= 0) + return false; + else if (lins->mSrc[0].mMemory != bins->mSrc[1].mMemory) + return true; + else if (lins->mSrc[0].mMemory == IM_GLOBAL) + { + return lins->mSrc[0].mLinkerObject != bins->mSrc[1].mLinkerObject || + lins->mSrc[0].mIntConst + lins->mSrc[0].mOperandSize <= bins->mSrc[1].mIntConst || + lins->mSrc[0].mIntConst >= bins->mSrc[1].mIntConst + bins->mSrc[1].mOperandSize; + } + else if (lins->mSrc[0].mMemory == IM_ABSOLUTE) + { + return + lins->mSrc[0].mIntConst + lins->mSrc[0].mOperandSize <= bins->mSrc[1].mIntConst || + lins->mSrc[0].mIntConst >= bins->mSrc[1].mIntConst + bins->mSrc[1].mOperandSize; + } + else if (lins->mSrc[0].mMemory == IM_LOCAL) + { + return lins->mSrc[0].mVarIndex != bins->mSrc[1].mVarIndex || + lins->mSrc[0].mIntConst + lins->mSrc[0].mOperandSize <= bins->mSrc[1].mIntConst || + lins->mSrc[0].mIntConst >= bins->mSrc[1].mIntConst + bins->mSrc[1].mOperandSize; + } + else + return false; + } + + // False data dependency + if (lins->mSrc[0].mTemp >= 0 && lins->mSrc[0].mTemp == bins->mDst.mTemp) + return false; + + return true; +} + +static bool CanBypassStoreDown(const InterInstruction* sins, const InterInstruction* bins) +{ + // Check ambiguity + if (bins->mCode == IC_COPY || bins->mCode == IC_STRCPY || bins->mCode == IC_FILL) + return false; + + // Side effects + if (bins->mCode == IC_CALL || bins->mCode == IC_CALL_NATIVE || bins->mCode == IC_ASSEMBLER) + return false; + + // True data dependency + if (bins->mDst.mTemp >= 0 && sins->UsesTemp(bins->mDst.mTemp)) + return false; + + if (bins->mCode == IC_STORE) + { + if (sins->mVolatile || sins->mMemmap) + return false; + else if (sins->mSrc[1].mMemory == IM_INDIRECT && bins->mSrc[1].mMemory == IM_INDIRECT) + { + return sins->mSrc[1].mLinkerObject && bins->mSrc[1].mLinkerObject && sins->mSrc[1].mLinkerObject != bins->mSrc[1].mLinkerObject; + } + else if (sins->mSrc[1].mTemp >= 0 || bins->mSrc[1].mTemp >= 0) + return false; + else if (sins->mSrc[1].mMemory != bins->mSrc[1].mMemory) + return true; + else if (sins->mSrc[1].mMemory == IM_GLOBAL) + { + return sins->mSrc[1].mLinkerObject != bins->mSrc[1].mLinkerObject || + sins->mSrc[1].mIntConst + sins->mSrc[1].mOperandSize <= bins->mSrc[1].mIntConst || + sins->mSrc[1].mIntConst >= bins->mSrc[1].mIntConst + bins->mSrc[1].mOperandSize; + } + else if (sins->mSrc[1].mMemory == IM_ABSOLUTE) + { + return + sins->mSrc[1].mIntConst + sins->mSrc[1].mOperandSize <= bins->mSrc[1].mIntConst || + sins->mSrc[1].mIntConst >= bins->mSrc[1].mIntConst + bins->mSrc[1].mOperandSize; + } + else if (sins->mSrc[1].mMemory == IM_LOCAL) + { + return sins->mSrc[1].mVarIndex != bins->mSrc[1].mVarIndex || + sins->mSrc[1].mIntConst + sins->mSrc[1].mOperandSize <= bins->mSrc[1].mIntConst || + sins->mSrc[1].mIntConst >= bins->mSrc[1].mIntConst + bins->mSrc[1].mOperandSize; + } + else + return false; + } + + if (bins->mCode == IC_LOAD) + { + if (sins->mVolatile || sins->mMemmap) + return false; + else if (sins->mSrc[1].mMemory == IM_INDIRECT && bins->mSrc[0].mMemory == IM_INDIRECT) + { + return sins->mSrc[1].mLinkerObject && bins->mSrc[0].mLinkerObject && sins->mSrc[1].mLinkerObject != bins->mSrc[0].mLinkerObject; + } + else if (sins->mSrc[1].mTemp >= 0 || bins->mSrc[0].mTemp >= 0) + return false; + else if (sins->mSrc[1].mMemory != bins->mSrc[0].mMemory) + return true; + else if (sins->mSrc[1].mMemory == IM_GLOBAL) + { + return sins->mSrc[1].mLinkerObject != bins->mSrc[0].mLinkerObject || + sins->mSrc[1].mIntConst + sins->mSrc[1].mOperandSize <= bins->mSrc[0].mIntConst || + sins->mSrc[1].mIntConst >= bins->mSrc[0].mIntConst + bins->mSrc[0].mOperandSize; + } + else if (sins->mSrc[1].mMemory == IM_ABSOLUTE) + { + return + sins->mSrc[1].mIntConst + sins->mSrc[1].mOperandSize <= bins->mSrc[0].mIntConst || + sins->mSrc[1].mIntConst >= bins->mSrc[0].mIntConst + bins->mSrc[0].mOperandSize; + } + else if (sins->mSrc[1].mMemory == IM_LOCAL) + { + return sins->mSrc[1].mVarIndex != bins->mSrc[0].mVarIndex || + sins->mSrc[1].mIntConst + sins->mSrc[1].mOperandSize <= bins->mSrc[0].mIntConst || + sins->mSrc[1].mIntConst >= bins->mSrc[0].mIntConst + bins->mSrc[0].mOperandSize; + } + else + return false; + } + + return true; +} + +static bool CanBypass(const InterInstruction* lins, const InterInstruction* bins) +{ + if (HasSideEffect(lins->mCode) && HasSideEffect(bins->mCode)) + return false; + + if (lins->mCode == IC_CALL || lins->mCode == IC_CALL_NATIVE) + { + if (bins->mCode == IC_CALL || bins->mCode == IC_CALL_NATIVE || + bins->mCode == IC_RETURN || bins->mCode == IC_RETURN_STRUCT || bins->mCode == IC_RETURN_VALUE || + bins->mCode == IC_PUSH_FRAME || bins->mCode == IC_POP_FRAME) + return false; + if (bins->mCode == IC_LOAD || bins->mCode == IC_STORE || bins->mCode == IC_COPY || bins->mCode == IC_FILL) + return false; + } + + if (lins->mDst.mTemp >= 0) + { + if (lins->mDst.mTemp == bins->mDst.mTemp) + return false; + + for (int i = 0; i < bins->mNumOperands; i++) + if (lins->mDst.mTemp == bins->mSrc[i].mTemp) + return false; + } + if (bins->mDst.mTemp >= 0) + { + for (int i = 0; i < lins->mNumOperands; i++) + if (bins->mDst.mTemp == lins->mSrc[i].mTemp) + return false; + } + if (bins->mCode == IC_PUSH_FRAME || bins->mCode == IC_POP_FRAME) + { + if (lins->mCode == IC_CONSTANT && lins->mDst.mType == IT_POINTER && lins->mConst.mMemory == IM_FRAME) + return false; + } + + return true; +} + +static bool CanBypassUp(const InterInstruction* lins, const InterInstruction* bins) +{ + if (HasSideEffect(lins->mCode) && HasSideEffect(bins->mCode)) + return false; + + if (lins->mDst.mTemp >= 0) + { + if (lins->mDst.mTemp == bins->mDst.mTemp) + return false; + + for (int i = 0; i < bins->mNumOperands; i++) + if (lins->mDst.mTemp == bins->mSrc[i].mTemp) + return false; + } + if (bins->mDst.mTemp >= 0) + { + for (int i = 0; i < lins->mNumOperands; i++) + if (bins->mDst.mTemp == lins->mSrc[i].mTemp) + return false; + } + if (lins->mCode == IC_STORE || lins->mCode == IC_COPY || lins->mCode == IC_FILL) + { + if (bins->mCode == IC_STORE || bins->mCode == IC_LOAD || bins->mCode == IC_COPY || bins->mCode == IC_FILL || bins->mCode == IC_CALL || bins->mCode == IC_CALL_NATIVE) + return false; + } + + if (bins->mCode == IC_PUSH_FRAME || bins->mCode == IC_POP_FRAME) + { + if (lins->mCode == IC_CONSTANT && lins->mDst.mType == IT_POINTER && lins->mConst.mMemory == IM_FRAME) + return false; + } + + return true; +} + +static bool CanBypassLoadUp(const InterInstruction* lins, const InterInstruction* bins) +{ + // Check ambiguity + if (bins->mCode == IC_COPY || bins->mCode == IC_STRCPY || bins->mCode == IC_FILL) + return false; + + // Side effects + if (bins->mCode == IC_CALL || bins->mCode == IC_CALL_NATIVE || bins->mCode == IC_ASSEMBLER) + return false; + + // False data dependency + if (bins->UsesTemp(lins->mDst.mTemp) || bins->mDst.mTemp == lins->mDst.mTemp) + return false; + + // True data dependency + if (lins->mSrc[0].mTemp >= 0 && lins->mSrc[0].mTemp == bins->mDst.mTemp) + return false; + + if (bins->mCode == IC_STORE) + { + if (lins->mVolatile) + return false; + else if (bins->mSrc[1].mMemory == IM_FRAME || bins->mSrc[1].mMemory == IM_FFRAME) + return true; + else if (lins->mSrc[0].mMemory == IM_INDIRECT && bins->mSrc[1].mMemory == IM_INDIRECT) + { + return lins->mSrc[0].mLinkerObject && bins->mSrc[1].mLinkerObject && lins->mSrc[0].mLinkerObject != bins->mSrc[1].mLinkerObject; + } + else if (lins->mSrc[0].mTemp >= 0 || bins->mSrc[1].mTemp >= 0) + return false; + else if (lins->mSrc[0].mMemory != bins->mSrc[1].mMemory) + return true; + else if (lins->mSrc[0].mMemory == IM_GLOBAL) + { + return lins->mSrc[0].mLinkerObject != bins->mSrc[1].mLinkerObject || + lins->mSrc[0].mIntConst + lins->mSrc[0].mOperandSize <= bins->mSrc[1].mIntConst || + lins->mSrc[0].mIntConst >= bins->mSrc[1].mIntConst + bins->mSrc[1].mOperandSize; + } + else if (lins->mSrc[0].mMemory == IM_ABSOLUTE) + { + return + lins->mSrc[0].mIntConst + lins->mSrc[0].mOperandSize <= bins->mSrc[1].mIntConst || + lins->mSrc[0].mIntConst >= bins->mSrc[1].mIntConst + bins->mSrc[1].mOperandSize; + } + else if (lins->mSrc[0].mMemory == IM_LOCAL) + { + return lins->mSrc[0].mVarIndex != bins->mSrc[1].mVarIndex || + lins->mSrc[0].mIntConst + lins->mSrc[0].mOperandSize <= bins->mSrc[1].mIntConst || + lins->mSrc[0].mIntConst >= bins->mSrc[1].mIntConst + bins->mSrc[1].mOperandSize; + } + else + return false; + } + + return true; +} + + +static bool IsChained(const InterInstruction* ins, const InterInstruction* nins) +{ + if (ins->mDst.mTemp >= 0) + { + for (int i = 0; i < nins->mNumOperands; i++) + if (ins->mDst.mTemp == nins->mSrc[i].mTemp) + return true; + } + + return false; +} + +static bool CanBypassStore(const InterInstruction* sins, const InterInstruction* bins) +{ + if (bins->mCode == IC_COPY || bins->mCode == IC_STRCPY || bins->mCode == IC_PUSH_FRAME || bins->mCode == IC_FILL) + return false; + + // True data dependency + if (bins->mDst.mTemp >= 0) + { + for (int i = 0; i < sins->mNumOperands; i++) + if (bins->mDst.mTemp == sins->mSrc[i].mTemp) + return false; + } + + InterMemory sm = IM_NONE, bm = IM_NONE; + int bi = -1, si = -1, bt = -1, st = -1, bo = 0, so = 0, bz = 1, sz = 1; + LinkerObject* slo = nullptr, * blo = nullptr; + + if (sins->mCode == IC_LOAD) + { + sm = sins->mSrc[0].mMemory; + si = sins->mSrc[0].mVarIndex; + st = sins->mSrc[0].mTemp; + so = int(sins->mSrc[0].mIntConst); + slo = sins->mSrc[0].mLinkerObject; + sz = InterTypeSize[sins->mDst.mType]; + } + else if (sins->mCode == IC_LEA || sins->mCode == IC_STORE) + { + sm = sins->mSrc[1].mMemory; + si = sins->mSrc[1].mVarIndex; + st = sins->mSrc[1].mTemp; + so = int(sins->mSrc[1].mIntConst); + slo = sins->mSrc[1].mLinkerObject; + sz = InterTypeSize[sins->mSrc[0].mType]; + } + + if (bins->mCode == IC_LOAD) + { + bm = bins->mSrc[0].mMemory; + bi = bins->mSrc[0].mVarIndex; + bt = bins->mSrc[0].mTemp; + bo = int(bins->mSrc[0].mIntConst); + blo = bins->mSrc[0].mLinkerObject; + bz = InterTypeSize[bins->mDst.mType]; + } + else if (bins->mCode == IC_LEA || bins->mCode == IC_STORE) + { + bm = bins->mSrc[1].mMemory; + bi = bins->mSrc[1].mVarIndex; + bt = bins->mSrc[1].mTemp; + bo = int(bins->mSrc[1].mIntConst); + blo = bins->mSrc[1].mLinkerObject; + bz = InterTypeSize[bins->mSrc[0].mType]; + } + + // Check ambiguity + if (bins->mCode == IC_STORE || bins->mCode == IC_LOAD) + { + if (sm == IM_LOCAL) + { + if (bm == IM_PARAM || bm == IM_GLOBAL || bm == IM_FPARAM) + ; + else if (bm == IM_LOCAL) + { + if (bi == si) + return false; + } + else + return false; + } + else if (sm == IM_FRAME || sm == IM_FFRAME) + ; + else if (sm == IM_FPARAM) + { + if (bm == IM_FPARAM && bi + bz > si && si + bz > bi) + return false; + } + else if (sm == IM_INDIRECT && bm == IM_INDIRECT) + { + if (st == bt) + { + if (so + sz > bo && bo + bz > so) + return false; + } + else + return slo && blo && slo != blo; + } + else + return false; + } + + if (sm == IM_FRAME && (bins->mCode == IC_PUSH_FRAME || bins->mCode == IC_POP_FRAME)) + return false; + + // Side effects + if (bins->mCode == IC_CALL || bins->mCode == IC_CALL_NATIVE || bins->mCode == IC_ASSEMBLER) + return false; + + + return true; +} + +static bool StoreAliasing(const InterInstruction * lins, const InterInstruction* sins, const GrowingInstructionPtrArray& tvalue, const NumberSet& aliasedLocals, const NumberSet& aliasedParams, const GrowingVariableArray& staticVars) +{ + InterMemory lmem, smem; + int lvindex, svindex; + int loffset, soffset; + int lsize, ssize; + + if (MemRange(lins, tvalue, lmem, lvindex, loffset, lsize)) + { + if (MemRange(sins, tvalue, smem, svindex, soffset, ssize)) + { + if (smem == lmem && svindex == lvindex) + { + if (soffset + ssize >= loffset && loffset + lsize >= soffset) + return true; + } + + return false; + } + + if (lmem == IM_LOCAL) + return aliasedLocals[lvindex]; + else if (lmem == IM_PARAM || lmem == IM_FPARAM) + return aliasedParams[lvindex]; + else if (lmem == IM_GLOBAL) + return staticVars[lvindex]->mAliased; + } + else if (MemRange(sins, tvalue, smem, svindex, soffset, ssize)) + { + if (smem == IM_LOCAL) + return aliasedLocals[svindex]; + else if (smem == IM_PARAM || smem == IM_FPARAM) + return aliasedParams[svindex]; + else if (smem == IM_GLOBAL) + return staticVars[svindex]->mAliased; + } + + return true; +} + +void ValueSet::Intersect(ValueSet& set) +{ + int k = 0; + for(int i=0; iDestroy(i); + } +} + +int TempForwardingTable::Size(void) const +{ + return mAssoc.Size(); +} + +void TempForwardingTable::SetSize(int size) +{ + int i; + mAssoc.SetSize(size); + + for (i = 0; i < size; i++) + mAssoc[i] = Assoc(i, i, i); +} + +void TempForwardingTable::Reset(void) +{ + int i; + + for (i = 0; i < mAssoc.Size(); i++) + mAssoc[i] = Assoc(i, i, i); +} + +void TempForwardingTable::Shrink(void) +{ + mAssoc.shrink(); +} + + +int TempForwardingTable::operator[](int n) +{ + return mAssoc[n].mAssoc; +} + +void TempForwardingTable::Destroy(int n) +{ + int i, j; + + if (mAssoc[n].mAssoc == n) + { + i = mAssoc[n].mSucc; + while (i != n) + { + j = mAssoc[i].mSucc; + mAssoc[i] = Assoc(i, i, i); + i = j; + } + } + else + { + mAssoc[mAssoc[n].mPred].mSucc = mAssoc[n].mSucc; + mAssoc[mAssoc[n].mSucc].mPred = mAssoc[n].mPred; + } + + mAssoc[n] = Assoc(n, n, n); +} + +void TempForwardingTable::Build(int from, int to) +{ + int i; + + from = mAssoc[from].mAssoc; + to = mAssoc[to].mAssoc; + + if (from != to) + { + i = mAssoc[from].mSucc; + while (i != from) + { + mAssoc[i].mAssoc = to; + i = mAssoc[i].mSucc; + } + mAssoc[from].mAssoc = to; + + mAssoc[mAssoc[to].mSucc].mPred = mAssoc[from].mPred; + mAssoc[mAssoc[from].mPred].mSucc = mAssoc[to].mSucc; + mAssoc[to].mSucc = from; + mAssoc[from].mPred = to; + } +} + + + +bool InterInstruction::ReferencesTemp(int temp) const +{ + if (temp < 0) + return false; + + if (temp == mDst.mTemp) + return true; + for (int i = 0; i < mNumOperands; i++) + if (mSrc[i].mTemp == temp) + return true; + return false; +} + +void InterInstruction::ReplaceTemp(int from, int to) +{ + if (mDst.mTemp == from) mDst.mTemp = to; + for (int i = 0; i < mNumOperands; i++) + if (mSrc[i].mTemp == from) + mSrc[i].mTemp = to; +} + +InterInstruction* InterInstruction::Clone(void) const +{ + InterInstruction* ins = new InterInstruction(mLocation, mCode); + ins->mDst = mDst; + ins->mConst = mConst; + ins->mOperator = mOperator; + ins->mNumOperands = mNumOperands; + for (int i = 0; i < mNumOperands; i++) + ins->mSrc[i] = mSrc[i]; + ins->mInUse = mInUse; + ins->mInvariant = mInvariant; + ins->mVolatile = mVolatile; + ins->mMemmap = mMemmap; + ins->mExpensive = mExpensive; + ins->mSingleAssignment = mSingleAssignment; + ins->mNoSideEffects = mNoSideEffects; + ins->mConstExpr = mConstExpr; + ins->mRemove = false; + ins->mAliasing = mAliasing; + + return ins; +} + +bool InterInstruction::IsEqual(const InterInstruction* ins) const +{ + if (mCode != ins->mCode) + return false; + + if (mCode == IC_BINARY_OPERATOR || mCode == IC_UNARY_OPERATOR || mCode == IC_RELATIONAL_OPERATOR || mCode == IC_CONVERSION_OPERATOR) + { + if (mOperator != ins->mOperator) + return false; + } + + if (!mDst.IsEqual(ins->mDst)) + return false; + + for (int i = 0; i < mNumOperands; i++) + if (!mSrc[i].IsEqual(ins->mSrc[i])) + return false; + + if (mCode == IC_CONSTANT && !mConst.IsEqual(ins->mConst)) + return false; + + return true; +} + +bool InterInstruction::IsEqualSource(const InterInstruction* ins) const +{ + if (mCode != ins->mCode) + return false; + + if (mCode == IC_BINARY_OPERATOR || mCode == IC_UNARY_OPERATOR || mCode == IC_RELATIONAL_OPERATOR || mCode == IC_CONVERSION_OPERATOR) + { + if (mOperator != ins->mOperator) + return false; + } + + for (int i = 0; i < mNumOperands; i++) + if (!mSrc[i].IsEqual(ins->mSrc[i])) + return false; + + if (mCode == IC_CONSTANT && !mConst.IsEqual(ins->mConst)) + return false; + + return true; +} + +void ValueSet::UpdateValue(InterCodeBasicBlock * block, InterInstruction * ins, const GrowingInstructionPtrArray& tvalue, const NumberSet& aliasedLocals, const NumberSet& aliasedParams, const GrowingVariableArray& staticVars, const GrowingInterCodeProcedurePtrArray& staticProcs) +{ + int i, temp; + + temp = ins->mDst.mTemp; + + if (temp >= 0) + { + i = 0; + while (i < mNum) + { + if (mInstructions[i]->ReferencesTemp(temp)) + { + mNum--; + if (i < mNum) + mInstructions[i] = mInstructions[mNum]; + } + else + i++; + } + } + + for (i = 0; i < ins->mNumOperands; i++) + { + temp = ins->mSrc[i].mTemp; + if (temp >= 0 && tvalue[temp]) + { + ins->mSrc[i].mTemp = tvalue[temp]->mDst.mTemp; + } + } + + if (ins->mMemmap) + { + i = 0; + while (i < mNum) + { + if (IsLoadStore(mInstructions[i]->mCode)) + { + mNum--; + if (i < mNum) + mInstructions[i] = mInstructions[mNum]; + } + else + i++; + } + } + + switch (ins->mCode) + { + case IC_LOAD: + i = 0; + while (i < mNum && + (mInstructions[i]->mCode != IC_LOAD || + mInstructions[i]->mSrc[0].mTemp != ins->mSrc[0].mTemp || + mInstructions[i]->mSrc[0].mOperandSize != ins->mSrc[0].mOperandSize)) + { + i++; + } + + if (i < mNum) + { + ins->mCode = IC_LOAD_TEMPORARY; + ins->mSrc[0].mTemp = mInstructions[i]->mDst.mTemp; + ins->mSrc[0].mType = mInstructions[i]->mDst.mType; + ins->mNumOperands = 1; + assert(ins->mSrc[0].mTemp >= 0); + } + else + { + i = 0; + while (i < mNum && + (mInstructions[i]->mCode != IC_STORE || + mInstructions[i]->mSrc[1].mTemp != ins->mSrc[0].mTemp || + mInstructions[i]->mSrc[1].mOperandSize != ins->mSrc[0].mOperandSize)) + { + i++; + } + + if (i < mNum) + { + if (mInstructions[i]->mSrc[0].mTemp < 0) + { + ins->mCode = IC_CONSTANT; + ins->mSrc[0].mTemp = -1; + ins->mConst.mType = mInstructions[i]->mSrc[0].mType; + ins->mConst.mIntConst = LimitIntConstValue(mInstructions[i]->mDst.mType, mInstructions[i]->mSrc[0].mIntConst); + ins->mNumOperands = 0; + } + else + { + ins->mCode = IC_LOAD_TEMPORARY; + ins->mSrc[0].mTemp = mInstructions[i]->mSrc[0].mTemp; + ins->mSrc[0].mType = mInstructions[i]->mSrc[0].mType; + ins->mNumOperands = 1; + assert(ins->mSrc[0].mTemp >= 0); + } + } + else if (ins->mSrc[0].mTemp >= 0 && tvalue[ins->mSrc[0].mTemp] && tvalue[ins->mSrc[0].mTemp]->mCode == IC_CONSTANT && tvalue[ins->mSrc[0].mTemp]->mConst.mMemory == IM_GLOBAL && (tvalue[ins->mSrc[0].mTemp]->mConst.mLinkerObject->mFlags & LOBJF_CONST)) + { + block->LoadConstantFold(ins, tvalue[ins->mSrc[0].mTemp], staticVars, staticProcs); + InsertValue(ins); + } + else + { + if (!ins->mVolatile) + InsertValue(ins); + } + } + + break; + case IC_STORE: + i = 0; + while (i < mNum) + { + if ((mInstructions[i]->mCode == IC_LOAD || mInstructions[i]->mCode == IC_STORE) && StoreAliasing(mInstructions[i], ins, tvalue, aliasedLocals, aliasedParams, staticVars)) + { + mNum--; + if (mNum > 0) + mInstructions[i] = mInstructions[mNum]; + } + else + i++; + } + + if (!ins->mVolatile) + InsertValue(ins); + break; + case IC_FILL: + case IC_COPY: + case IC_STRCPY: + i = 0; + while (i < mNum) + { + if ((mInstructions[i]->mCode == IC_LOAD || mInstructions[i]->mCode == IC_STORE) && StoreAliasing(mInstructions[i], ins, tvalue, aliasedLocals, aliasedParams, staticVars)) + { + mNum--; + if (mNum > 0) + mInstructions[i] = mInstructions[mNum]; + } + else + i++; + } + + break; + + case IC_CONSTANT: + switch (ins->mDst.mType) + { + case IT_FLOAT: + i = 0; + while (i < mNum && + (mInstructions[i]->mCode != IC_CONSTANT || + mInstructions[i]->mDst.mType != ins->mDst.mType || + mInstructions[i]->mConst.mFloatConst != ins->mConst.mFloatConst)) + { + i++; + } + break; + case IT_POINTER: + i = 0; + while (i < mNum && + (mInstructions[i]->mCode != IC_CONSTANT || + mInstructions[i]->mDst.mType != ins->mDst.mType || + mInstructions[i]->mConst.mIntConst != ins->mConst.mIntConst || + mInstructions[i]->mConst.mMemory != ins->mConst.mMemory || + mInstructions[i]->mConst.mVarIndex != ins->mConst.mVarIndex || + mInstructions[i]->mConst.mLinkerObject != ins->mConst.mLinkerObject)) + { + i++; + } + break; + default: + + i = 0; + while (i < mNum && + (mInstructions[i]->mCode != IC_CONSTANT || + mInstructions[i]->mDst.mType != ins->mDst.mType || + mInstructions[i]->mConst.mIntConst != ins->mConst.mIntConst)) + { + i++; + } + } + + if (i < mNum) + { + ins->mCode = IC_LOAD_TEMPORARY; + ins->mSrc[0].mTemp = mInstructions[i]->mDst.mTemp; + ins->mSrc[0].mType = mInstructions[i]->mDst.mType; + ins->mNumOperands = 1; + assert(ins->mSrc[0].mTemp >= 0); + } + else + { + InsertValue(ins); + } + break; + + case IC_LEA: + i = 0; + while (i < mNum && + (mInstructions[i]->mCode != IC_LEA || + mInstructions[i]->mSrc[0].mTemp != ins->mSrc[0].mTemp || + mInstructions[i]->mSrc[0].mIntConst != ins->mSrc[0].mIntConst || + mInstructions[i]->mSrc[1].mTemp != ins->mSrc[1].mTemp)) + { + i++; + } + + if (i < mNum) + { + ins->mCode = IC_LOAD_TEMPORARY; + ins->mSrc[0].mTemp = mInstructions[i]->mDst.mTemp; + ins->mSrc[0].mType = mInstructions[i]->mDst.mType; + ins->mSrc[1].mTemp = -1; + ins->mNumOperands = 1; + assert(ins->mSrc[0].mTemp >= 0); + } + else + { + InsertValue(ins); + } + break; + + case IC_BINARY_OPERATOR: + switch (ins->mSrc[0].mType) + { + case IT_FLOAT: + if (ins->mSrc[1].mTemp >= 0 && tvalue[ins->mSrc[1].mTemp] && tvalue[ins->mSrc[1].mTemp]->mCode == IC_CONSTANT && + ins->mSrc[0].mTemp >= 0 && tvalue[ins->mSrc[0].mTemp] && tvalue[ins->mSrc[0].mTemp]->mCode == IC_CONSTANT) + { + ins->mCode = IC_CONSTANT; + ins->mConst.mFloatConst = ConstantFolding(ins->mOperator, tvalue[ins->mSrc[1].mTemp]->mConst.mFloatConst, tvalue[ins->mSrc[0].mTemp]->mConst.mFloatConst); + ins->mConst.mType = IT_FLOAT; + ins->mSrc[0].mTemp = -1; + ins->mSrc[1].mTemp = -1; + ins->mNumOperands = 0; + + i = 0; + while (i < mNum && + (mInstructions[i]->mCode != IC_CONSTANT || + mInstructions[i]->mDst.mType != ins->mDst.mType || + mInstructions[i]->mConst.mFloatConst != ins->mConst.mFloatConst)) + { + i++; + } + + if (i < mNum) + { + ins->mCode = IC_LOAD_TEMPORARY; + ins->mSrc[0].mTemp = mInstructions[i]->mDst.mTemp; + ins->mSrc[0].mType = mInstructions[i]->mDst.mType; + ins->mNumOperands = 1; + assert(ins->mSrc[0].mTemp >= 0); + } + else + { + InsertValue(ins); + } + } + else + { + i = 0; + while (i < mNum && + (mInstructions[i]->mCode != IC_BINARY_OPERATOR || + mInstructions[i]->mOperator != ins->mOperator || + mInstructions[i]->mSrc[0].mTemp != ins->mSrc[0].mTemp || + mInstructions[i]->mSrc[1].mTemp != ins->mSrc[1].mTemp)) + { + i++; + } + + if (i < mNum) + { + ins->mCode = IC_LOAD_TEMPORARY; + ins->mSrc[0].mTemp = mInstructions[i]->mDst.mTemp; + ins->mSrc[0].mType = mInstructions[i]->mDst.mType; + ins->mSrc[1].mTemp = -1; + ins->mNumOperands = 1; + assert(ins->mSrc[0].mTemp >= 0); + } + else + { + InsertValue(ins); + } + } + break; + case IT_POINTER: + break; + default: + assert(ins->mSrc[1].mType == IT_INT8 || ins->mSrc[1].mType == IT_INT16 || ins->mSrc[1].mType == IT_INT32); + + if (ins->mSrc[1].mTemp >= 0 && tvalue[ins->mSrc[1].mTemp] && tvalue[ins->mSrc[1].mTemp]->mCode == IC_CONSTANT && + ins->mSrc[0].mTemp >= 0 && tvalue[ins->mSrc[0].mTemp] && tvalue[ins->mSrc[0].mTemp]->mCode == IC_CONSTANT) + { + ins->mCode = IC_CONSTANT; + ins->mConst.mIntConst = LimitIntConstValue(ins->mDst.mType, ConstantFolding(ins->mOperator, ins->mDst.mType, tvalue[ins->mSrc[1].mTemp]->mConst.mIntConst, tvalue[ins->mSrc[0].mTemp]->mConst.mIntConst)); + ins->mConst.mType = ins->mDst.mType; + ins->mSrc[0].mTemp = -1; + ins->mSrc[1].mTemp = -1; + ins->mNumOperands = 0; + + UpdateValue(block, ins, tvalue, aliasedLocals, aliasedParams, staticVars, staticProcs); + + return; + } + + if (ins->mSrc[0].mTemp >= 0 && tvalue[ins->mSrc[0].mTemp] && tvalue[ins->mSrc[0].mTemp]->mCode == IC_CONSTANT) + { + if ((ins->mOperator == IA_ADD || ins->mOperator == IA_SUB || + ins->mOperator == IA_OR || ins->mOperator == IA_XOR || + ins->mOperator == IA_SHL || ins->mOperator == IA_SHR || ins->mOperator == IA_SAR) && tvalue[ins->mSrc[0].mTemp]->mConst.mIntConst == 0 || + (ins->mOperator == IA_MUL || ins->mOperator == IA_DIVU || ins->mOperator == IA_DIVS) && tvalue[ins->mSrc[0].mTemp]->mConst.mIntConst == 1 || + (ins->mOperator == IA_AND) && tvalue[ins->mSrc[0].mTemp]->mConst.mIntConst == -1) + { + ins->mCode = IC_LOAD_TEMPORARY; + ins->mSrc[0].mTemp = ins->mSrc[1].mTemp; + ins->mSrc[0].mType = ins->mSrc[1].mType; + ins->mSrc[1].mTemp = -1; + ins->mNumOperands = 1; + assert(ins->mSrc[0].mTemp >= 0); + + UpdateValue(block, ins, tvalue, aliasedLocals, aliasedParams, staticVars, staticProcs); + + return; + } + else if ((ins->mOperator == IA_MUL || ins->mOperator == IA_AND) && tvalue[ins->mSrc[0].mTemp]->mConst.mIntConst == 0) + { + ins->mCode = IC_CONSTANT; + ins->mConst.mIntConst = 0; + ins->mConst.mType = ins->mDst.mType; + ins->mSrc[0].mTemp = -1; + ins->mSrc[1].mTemp = -1; + ins->mNumOperands = 0; + + UpdateValue(block, ins, tvalue, aliasedLocals, aliasedParams, staticVars, staticProcs); + + return; + } + else if (ins->mOperator == IA_MUL && tvalue[ins->mSrc[0].mTemp]->mConst.mIntConst == -1) + { + ins->mCode = IC_UNARY_OPERATOR; + ins->mOperator = IA_NEG; + ins->mSrc[0] = ins->mSrc[1]; + ins->mSrc[1].mTemp = -1; + ins->mSrc[1].mType = IT_NONE; + ins->mNumOperands = 1; + + UpdateValue(block, ins, tvalue, aliasedLocals, aliasedParams, staticVars, staticProcs); + + return; + } + } + else if (ins->mSrc[1].mTemp >= 0 && tvalue[ins->mSrc[1].mTemp] && tvalue[ins->mSrc[1].mTemp]->mCode == IC_CONSTANT) + { + if ((ins->mOperator == IA_ADD || ins->mOperator == IA_OR || ins->mOperator == IA_XOR) && tvalue[ins->mSrc[1].mTemp]->mConst.mIntConst == 0 || + (ins->mOperator == IA_MUL) && tvalue[ins->mSrc[1].mTemp]->mConst.mIntConst == 1 || + (ins->mOperator == IA_AND) && tvalue[ins->mSrc[1].mTemp]->mConst.mIntConst == -1) + { + ins->mCode = IC_LOAD_TEMPORARY; + ins->mSrc[1].mTemp = -1; + ins->mNumOperands = 1; + assert(ins->mSrc[0].mTemp >= 0); + + UpdateValue(block, ins, tvalue, aliasedLocals, aliasedParams, staticVars, staticProcs); + + return; + } + else if ((ins->mOperator == IA_MUL || ins->mOperator == IA_AND || + ins->mOperator == IA_SHL || ins->mOperator == IA_SHR || ins->mOperator == IA_SAR) && tvalue[ins->mSrc[1].mTemp]->mConst.mIntConst == 0) + { + ins->mCode = IC_CONSTANT; + ins->mConst.mIntConst = 0; + ins->mConst.mType = ins->mDst.mType; + ins->mSrc[0].mTemp = -1; + ins->mSrc[1].mTemp = -1; + ins->mNumOperands = 0; + + UpdateValue(block, ins, tvalue, aliasedLocals, aliasedParams, staticVars, staticProcs); + + return; + } + else if (ins->mOperator == IA_MUL && tvalue[ins->mSrc[1].mTemp]->mConst.mIntConst == -1) + { + ins->mCode = IC_UNARY_OPERATOR; + ins->mOperator = IA_NEG; + ins->mSrc[1].mTemp = -1; + ins->mSrc[1].mType = IT_NONE; + ins->mNumOperands = 1; + + UpdateValue(block, ins, tvalue, aliasedLocals, aliasedParams, staticVars, staticProcs); + + return; + } + else if (ins->mOperator == IA_SUB && tvalue[ins->mSrc[1].mTemp]->mConst.mIntConst == 0) + { + ins->mCode = IC_UNARY_OPERATOR; + ins->mOperator = IA_NEG; + ins->mSrc[1].mTemp = -1; + ins->mNumOperands = 1; + + UpdateValue(block, ins, tvalue, aliasedLocals, aliasedParams, staticVars, staticProcs); + + return; + } + } + else if (ins->mSrc[0].mTemp == ins->mSrc[1].mTemp) + { + if (ins->mOperator == IA_SUB || ins->mOperator == IA_XOR) + { + ins->mCode = IC_CONSTANT; + ins->mConst.mIntConst = 0; + ins->mConst.mType = ins->mDst.mType; + ins->mSrc[0].mTemp = -1; + ins->mSrc[1].mTemp = -1; + ins->mNumOperands = 0; + + UpdateValue(block, ins, tvalue, aliasedLocals, aliasedParams, staticVars, staticProcs); + + return; + } + else if (ins->mOperator == IA_AND || ins->mOperator == IA_OR) + { + ins->mCode = IC_LOAD_TEMPORARY; + ins->mSrc[1].mTemp = -1; + ins->mNumOperands = 1; + assert(ins->mSrc[0].mTemp >= 0); + + UpdateValue(block, ins, tvalue, aliasedLocals, aliasedParams, staticVars, staticProcs); + + return; + } + } + + i = 0; + while (i < mNum && + (mInstructions[i]->mCode != IC_BINARY_OPERATOR || + mInstructions[i]->mOperator != ins->mOperator || + mInstructions[i]->mSrc[0].mTemp != ins->mSrc[0].mTemp || + mInstructions[i]->mSrc[1].mTemp != ins->mSrc[1].mTemp)) + { + i++; + } + + if (i < mNum) + { + ins->mCode = IC_LOAD_TEMPORARY; + ins->mSrc[0].mTemp = mInstructions[i]->mDst.mTemp; + ins->mSrc[0].mType = mInstructions[i]->mDst.mType; + ins->mSrc[1].mTemp = -1; + ins->mNumOperands = 1; + assert(ins->mSrc[0].mTemp >= 0); + } + else + { + InsertValue(ins); + } + break; + } + break; + + case IC_CONVERSION_OPERATOR: + if (ins->mSrc[0].mTemp >= 0 && tvalue[ins->mSrc[0].mTemp] && tvalue[ins->mSrc[0].mTemp]->mCode == IC_CONSTANT) + { + ConversionConstantFold(ins, tvalue[ins->mSrc[0].mTemp]->mConst); + if (ins->mDst.mType == IT_FLOAT) + { + i = 0; + while (i < mNum && + (mInstructions[i]->mCode != IC_CONSTANT || + mInstructions[i]->mDst.mType != ins->mDst.mType || + mInstructions[i]->mConst.mFloatConst != ins->mConst.mFloatConst)) + { + i++; + } + + if (i < mNum) + { + ins->mCode = IC_LOAD_TEMPORARY; + ins->mSrc[0].mTemp = mInstructions[i]->mDst.mTemp; + ins->mSrc[0].mType = mInstructions[i]->mDst.mType; + ins->mNumOperands = 1; + assert(ins->mSrc[0].mTemp >= 0); + } + else + { + InsertValue(ins); + } + } + else + { + InsertValue(ins); + } + } + else + { + i = 0; + while (i < mNum && + (mInstructions[i]->mCode != IC_CONVERSION_OPERATOR || + mInstructions[i]->mOperator != ins->mOperator || + mInstructions[i]->mSrc[0].mTemp != ins->mSrc[0].mTemp)) + { + i++; + } + + if (i < mNum) + { + ins->mCode = IC_LOAD_TEMPORARY; + ins->mSrc[0].mTemp = mInstructions[i]->mDst.mTemp; + ins->mSrc[0].mType = mInstructions[i]->mDst.mType; + ins->mSrc[1].mTemp = -1; + ins->mNumOperands = 1; + assert(ins->mSrc[0].mTemp >= 0); + } + else + { + InsertValue(ins); + } + } + break; + + case IC_UNARY_OPERATOR: + switch (ins->mSrc[0].mType) + { + case IT_FLOAT: + if (ins->mSrc[0].mTemp >= 0 && tvalue[ins->mSrc[0].mTemp] && tvalue[ins->mSrc[0].mTemp]->mCode == IC_CONSTANT) + { + ins->mCode = IC_CONSTANT; + ins->mConst.mFloatConst = ConstantFolding(ins->mOperator, tvalue[ins->mSrc[0].mTemp]->mConst.mFloatConst); + ins->mConst.mType = IT_FLOAT; + ins->mSrc[0].mTemp = -1; + ins->mNumOperands = 0; + + i = 0; + while (i < mNum && + (mInstructions[i]->mCode != IC_CONSTANT || + mInstructions[i]->mDst.mType != ins->mDst.mType || + mInstructions[i]->mConst.mFloatConst != ins->mConst.mFloatConst)) + { + i++; + } + + if (i < mNum) + { + ins->mCode = IC_LOAD_TEMPORARY; + ins->mSrc[0].mTemp = mInstructions[i]->mDst.mTemp; + ins->mSrc[0].mType = mInstructions[i]->mDst.mType; + ins->mNumOperands = 1; + assert(ins->mSrc[0].mTemp >= 0); + } + else + { + InsertValue(ins); + } + } + else + { + i = 0; + while (i < mNum && + (mInstructions[i]->mCode != IC_UNARY_OPERATOR || + mInstructions[i]->mOperator != ins->mOperator || + mInstructions[i]->mSrc[0].mTemp != ins->mSrc[0].mTemp)) + { + i++; + } + + if (i < mNum) + { + ins->mCode = IC_LOAD_TEMPORARY; + ins->mSrc[0].mTemp = mInstructions[i]->mDst.mTemp; + ins->mSrc[0].mType = mInstructions[i]->mDst.mType; + ins->mSrc[1].mTemp = -1; + ins->mNumOperands = 1; + assert(ins->mSrc[0].mTemp >= 0); + } + else + { + InsertValue(ins); + } + } + break; + case IT_POINTER: + break; + default: + if (ins->mSrc[0].mTemp >= 0 && tvalue[ins->mSrc[0].mTemp] && tvalue[ins->mSrc[0].mTemp]->mCode == IC_CONSTANT) + { + ins->mCode = IC_CONSTANT; + ins->mConst.mIntConst = LimitIntConstValue(ins->mDst.mType, ConstantFolding(ins->mOperator, ins->mDst.mType, tvalue[ins->mSrc[0].mTemp]->mConst.mIntConst)); + ins->mConst.mType = ins->mDst.mType; + ins->mSrc[0].mTemp = -1; + ins->mNumOperands = 0; + + i = 0; + while (i < mNum && + (mInstructions[i]->mCode != IC_CONSTANT || + mInstructions[i]->mDst.mType != ins->mDst.mType || + mInstructions[i]->mConst.mIntConst != ins->mConst.mIntConst)) + { + i++; + } + + if (i < mNum) + { + ins->mCode = IC_LOAD_TEMPORARY; + ins->mSrc[0].mTemp = mInstructions[i]->mDst.mTemp; + ins->mSrc[0].mType = mInstructions[i]->mDst.mType; + ins->mNumOperands = 1; + assert(ins->mSrc[0].mTemp >= 0); + } + else + { + InsertValue(ins); + } + } + else + { + i = 0; + while (i < mNum && + (mInstructions[i]->mCode != IC_UNARY_OPERATOR || + mInstructions[i]->mOperator != ins->mOperator || + mInstructions[i]->mSrc[0].mTemp != ins->mSrc[0].mTemp)) + { + i++; + } + + if (i < mNum) + { + ins->mCode = IC_LOAD_TEMPORARY; + ins->mSrc[0].mTemp = mInstructions[i]->mDst.mTemp; + ins->mSrc[0].mType = mInstructions[i]->mDst.mType; + ins->mNumOperands = 1; + assert(ins->mSrc[0].mTemp >= 0); + } + else + { + InsertValue(ins); + } + } + break; + } + break; + + case IC_RELATIONAL_OPERATOR: + switch (ins->mSrc[1].mType) + { + case IT_FLOAT: + if (ins->mSrc[1].mTemp >= 0 && tvalue[ins->mSrc[1].mTemp] && tvalue[ins->mSrc[1].mTemp]->mCode == IC_CONSTANT && + ins->mSrc[0].mTemp >= 0 && tvalue[ins->mSrc[0].mTemp] && tvalue[ins->mSrc[0].mTemp]->mCode == IC_CONSTANT) + { + ins->mCode = IC_CONSTANT; + ins->mConst.mType = IT_BOOL; + ins->mConst.mIntConst = ConstantRelationalFolding(ins->mOperator, tvalue[ins->mSrc[1].mTemp]->mConst.mFloatConst, tvalue[ins->mSrc[0].mTemp]->mConst.mFloatConst); + ins->mSrc[0].mTemp = -1; + ins->mSrc[1].mTemp = -1; + ins->mNumOperands = 0; + + UpdateValue(block, ins, tvalue, aliasedLocals, aliasedParams, staticVars, staticProcs); + } + break; + case IT_POINTER: + break; + default: + if (ins->mSrc[1].mTemp >= 0 && tvalue[ins->mSrc[1].mTemp] && tvalue[ins->mSrc[1].mTemp]->mCode == IC_CONSTANT && + ins->mSrc[0].mTemp >= 0 && tvalue[ins->mSrc[0].mTemp] && tvalue[ins->mSrc[0].mTemp]->mCode == IC_CONSTANT) + { + ins->mCode = IC_CONSTANT; + ins->mConst.mType = IT_BOOL; + ins->mConst.mIntConst = ConstantFolding(ins->mOperator, ins->mSrc[0].mType, tvalue[ins->mSrc[1].mTemp]->mConst.mIntConst, tvalue[ins->mSrc[0].mTemp]->mConst.mIntConst); + ins->mSrc[0].mTemp = -1; + ins->mSrc[1].mTemp = -1; + ins->mNumOperands = 0; + + UpdateValue(block, ins, tvalue, aliasedLocals, aliasedParams, staticVars, staticProcs); + } + else if (ins->mSrc[1].mTemp >= 0 && tvalue[ins->mSrc[1].mTemp] && tvalue[ins->mSrc[1].mTemp]->mCode == IC_CONVERSION_OPERATOR && + ins->mSrc[0].mTemp >= 0 && tvalue[ins->mSrc[0].mTemp] && tvalue[ins->mSrc[0].mTemp]->mCode == IC_CONVERSION_OPERATOR && + tvalue[ins->mSrc[1].mTemp]->mOperator != IA_FLOAT2INT && tvalue[ins->mSrc[1].mTemp]->mOperator != IA_INT2FLOAT && + tvalue[ins->mSrc[1].mTemp]->mOperator != IA_FLOAT2UINT && tvalue[ins->mSrc[1].mTemp]->mOperator != IA_UINT2FLOAT && + tvalue[ins->mSrc[1].mTemp]->mOperator != IA_FLOAT2LINT && tvalue[ins->mSrc[1].mTemp]->mOperator != IA_LINT2FLOAT && + tvalue[ins->mSrc[1].mTemp]->mOperator != IA_FLOAT2LUINT && tvalue[ins->mSrc[1].mTemp]->mOperator != IA_LUINT2FLOAT && + tvalue[ins->mSrc[0].mTemp]->mOperator == tvalue[ins->mSrc[1].mTemp]->mOperator) + { + ins->mSrc[0].mType = tvalue[ins->mSrc[0].mTemp]->mSrc[0].mType; + ins->mSrc[0].mTemp = tvalue[ins->mSrc[0].mTemp]->mSrc[0].mTemp; + ins->mSrc[1].mType = tvalue[ins->mSrc[1].mTemp]->mSrc[0].mType; + ins->mSrc[1].mTemp = tvalue[ins->mSrc[1].mTemp]->mSrc[0].mTemp; + + UpdateValue(block, ins, tvalue, aliasedLocals, aliasedParams, staticVars, staticProcs); + } + else if (ins->mSrc[1].mTemp >= 0 && tvalue[ins->mSrc[1].mTemp] && tvalue[ins->mSrc[1].mTemp]->mCode == IC_CONVERSION_OPERATOR && + ins->mSrc[0].mTemp >= 0 && tvalue[ins->mSrc[0].mTemp] && tvalue[ins->mSrc[0].mTemp]->mCode == IC_CONSTANT && + tvalue[ins->mSrc[1].mTemp]->mOperator != IA_FLOAT2INT && tvalue[ins->mSrc[1].mTemp]->mOperator != IA_INT2FLOAT && + tvalue[ins->mSrc[1].mTemp]->mOperator != IA_FLOAT2UINT && tvalue[ins->mSrc[1].mTemp]->mOperator != IA_UINT2FLOAT && + tvalue[ins->mSrc[1].mTemp]->mOperator != IA_FLOAT2LINT && tvalue[ins->mSrc[1].mTemp]->mOperator != IA_LINT2FLOAT && + tvalue[ins->mSrc[1].mTemp]->mOperator != IA_FLOAT2LUINT && tvalue[ins->mSrc[1].mTemp]->mOperator != IA_LUINT2FLOAT) + { + bool toconst = false; + int cvalue = 0; + + if (tvalue[ins->mSrc[1].mTemp]->mOperator == IA_EXT8TO16S || tvalue[ins->mSrc[1].mTemp]->mOperator == IA_EXT8TO32S) + { + int64 ivalue = tvalue[ins->mSrc[0].mTemp]->mConst.mIntConst; + if (ins->mSrc[1].mType == IT_INT16 && (ivalue < -32768 || ivalue > 32767)) + { + + } + else if (ivalue < -128) + { + toconst = true; + switch (ins->mOperator) + { + case IA_CMPEQ: + case IA_CMPLES: + case IA_CMPLS: + case IA_CMPLEU: + case IA_CMPLU: + cvalue = 0; + break; + case IA_CMPNE: + case IA_CMPGES: + case IA_CMPGS: + case IA_CMPGEU: + case IA_CMPGU: + cvalue = 1; + break; + } + } + else if (ivalue > 127) + { + toconst = true; + switch (ins->mOperator) + { + case IA_CMPEQ: + case IA_CMPGES: + case IA_CMPGS: + case IA_CMPGEU: + case IA_CMPGU: + cvalue = 0; + break; + case IA_CMPNE: + case IA_CMPLES: + case IA_CMPLS: + case IA_CMPLEU: + case IA_CMPLU: + cvalue = 1; + break; + } + } + } + else if (tvalue[ins->mSrc[1].mTemp]->mOperator == IA_EXT8TO16U || tvalue[ins->mSrc[1].mTemp]->mOperator == IA_EXT8TO32U) + { + int64 ivalue = tvalue[ins->mSrc[0].mTemp]->mConst.mIntConst; + if (ivalue < 0) + { + toconst = true; + switch (ins->mOperator) + { + case IA_CMPEQ: + case IA_CMPLES: + case IA_CMPLS: + case IA_CMPLEU: + case IA_CMPLU: + cvalue = 0; + break; + case IA_CMPNE: + case IA_CMPGES: + case IA_CMPGS: + case IA_CMPGEU: + case IA_CMPGU: + cvalue = 1; + break; + } + } + else if (ivalue > 255) + { + toconst = true; + switch (ins->mOperator) + { + case IA_CMPEQ: + case IA_CMPGES: + case IA_CMPGS: + case IA_CMPGEU: + case IA_CMPGU: + cvalue = 0; + break; + case IA_CMPNE: + case IA_CMPLES: + case IA_CMPLS: + case IA_CMPLEU: + case IA_CMPLU: + cvalue = 1; + break; + } + } + else + { + switch (ins->mOperator) + { + case IA_CMPGES: + ins->mOperator = IA_CMPGEU; + break; + case IA_CMPLES: + ins->mOperator = IA_CMPLEU; + break; + case IA_CMPGS: + ins->mOperator = IA_CMPGU; + break; + case IA_CMPLS: + ins->mOperator = IA_CMPLU; + break; + } + } + } + else if (tvalue[ins->mSrc[1].mTemp]->mOperator == IA_EXT16TO32U) + { + int64 ivalue = tvalue[ins->mSrc[0].mTemp]->mConst.mIntConst; + if (ivalue < 0) + { + toconst = true; + switch (ins->mOperator) + { + case IA_CMPEQ: + case IA_CMPLES: + case IA_CMPLS: + case IA_CMPLEU: + case IA_CMPLU: + cvalue = 0; + break; + case IA_CMPNE: + case IA_CMPGES: + case IA_CMPGS: + case IA_CMPGEU: + case IA_CMPGU: + cvalue = 1; + break; + } + } + else if (ivalue > 65535) + { + toconst = true; + switch (ins->mOperator) + { + case IA_CMPEQ: + case IA_CMPGES: + case IA_CMPGS: + case IA_CMPGEU: + case IA_CMPGU: + cvalue = 0; + break; + case IA_CMPNE: + case IA_CMPLES: + case IA_CMPLS: + case IA_CMPLEU: + case IA_CMPLU: + cvalue = 1; + break; + } + } + else + { + switch (ins->mOperator) + { + case IA_CMPGES: + ins->mOperator = IA_CMPGEU; + break; + case IA_CMPLES: + ins->mOperator = IA_CMPLEU; + break; + case IA_CMPGS: + ins->mOperator = IA_CMPGU; + break; + case IA_CMPLS: + ins->mOperator = IA_CMPLU; + break; + } + } + } + else if (tvalue[ins->mSrc[1].mTemp]->mOperator == IA_EXT16TO32S) + { + int64 ivalue = tvalue[ins->mSrc[0].mTemp]->mConst.mIntConst; + if (ivalue < -32768) + { + toconst = true; + switch (ins->mOperator) + { + case IA_CMPEQ: + case IA_CMPLES: + case IA_CMPLS: + case IA_CMPLEU: + case IA_CMPLU: + cvalue = 0; + break; + case IA_CMPNE: + case IA_CMPGES: + case IA_CMPGS: + case IA_CMPGEU: + case IA_CMPGU: + cvalue = 1; + break; + } + } + else if (ivalue > 32767) + { + toconst = true; + switch (ins->mOperator) + { + case IA_CMPEQ: + case IA_CMPGES: + case IA_CMPGS: + case IA_CMPGEU: + case IA_CMPGU: + cvalue = 0; + break; + case IA_CMPNE: + case IA_CMPLES: + case IA_CMPLS: + case IA_CMPLEU: + case IA_CMPLU: + cvalue = 1; + break; + } + } + } + + if (toconst) + { + ins->mCode = IC_CONSTANT; + ins->mConst.mType = IT_BOOL; + ins->mConst.mIntConst = cvalue; + ins->mSrc[0].mTemp = -1; + ins->mSrc[1].mTemp = -1; + ins->mNumOperands = 0; + } + else + { + ins->mSrc[0].mType = tvalue[ins->mSrc[1].mTemp]->mSrc[0].mType; + ins->mSrc[1].mType = tvalue[ins->mSrc[1].mTemp]->mSrc[0].mType; + ins->mSrc[1].mTemp = tvalue[ins->mSrc[1].mTemp]->mSrc[0].mTemp; + } + + UpdateValue(block, ins, tvalue, aliasedLocals, aliasedParams, staticVars, staticProcs); + } + else if (ins->mSrc[0].mTemp >= 0 && tvalue[ins->mSrc[0].mTemp] && tvalue[ins->mSrc[0].mTemp]->mCode == IC_CONVERSION_OPERATOR && + ins->mSrc[1].mTemp >= 0 && tvalue[ins->mSrc[1].mTemp] && tvalue[ins->mSrc[1].mTemp]->mCode == IC_CONSTANT && + tvalue[ins->mSrc[0].mTemp]->mOperator != IA_FLOAT2INT && tvalue[ins->mSrc[0].mTemp]->mOperator != IA_INT2FLOAT && + tvalue[ins->mSrc[0].mTemp]->mOperator != IA_FLOAT2UINT && tvalue[ins->mSrc[0].mTemp]->mOperator != IA_UINT2FLOAT && + tvalue[ins->mSrc[0].mTemp]->mOperator != IA_FLOAT2LINT && tvalue[ins->mSrc[0].mTemp]->mOperator != IA_LINT2FLOAT && + tvalue[ins->mSrc[0].mTemp]->mOperator != IA_FLOAT2LUINT && tvalue[ins->mSrc[0].mTemp]->mOperator != IA_LUINT2FLOAT) + { + bool toconst = false; + int cvalue = 0; + + if (tvalue[ins->mSrc[0].mTemp]->mOperator == IA_EXT8TO16S || tvalue[ins->mSrc[0].mTemp]->mOperator == IA_EXT8TO32S) + { + int64 ivalue = tvalue[ins->mSrc[1].mTemp]->mConst.mIntConst; + if (ivalue > 127) + { + toconst = true; + switch (ins->mOperator) + { + case IA_CMPEQ: + case IA_CMPLES: + case IA_CMPLS: + case IA_CMPLEU: + case IA_CMPLU: + cvalue = 0; + break; + case IA_CMPNE: + case IA_CMPGES: + case IA_CMPGS: + case IA_CMPGEU: + case IA_CMPGU: + cvalue = 1; + break; + } + } + else if (ivalue < -128) + { + toconst = true; + switch (ins->mOperator) + { + case IA_CMPEQ: + case IA_CMPGES: + case IA_CMPGS: + case IA_CMPGEU: + case IA_CMPGU: + cvalue = 0; + break; + case IA_CMPNE: + case IA_CMPLES: + case IA_CMPLS: + case IA_CMPLEU: + case IA_CMPLU: + cvalue = 1; + break; + } + } + } + else if (tvalue[ins->mSrc[0].mTemp]->mOperator == IA_EXT8TO16U || tvalue[ins->mSrc[0].mTemp]->mOperator == IA_EXT8TO32U) + { + int64 ivalue = tvalue[ins->mSrc[1].mTemp]->mConst.mIntConst; + if (ivalue > 255) + { + toconst = true; + switch (ins->mOperator) + { + case IA_CMPEQ: + case IA_CMPLES: + case IA_CMPLS: + case IA_CMPLEU: + case IA_CMPLU: + cvalue = 0; + break; + case IA_CMPNE: + case IA_CMPGES: + case IA_CMPGS: + case IA_CMPGEU: + case IA_CMPGU: + cvalue = 1; + break; + } + } + else if (ivalue < 0) + { + toconst = true; + switch (ins->mOperator) + { + case IA_CMPEQ: + case IA_CMPGES: + case IA_CMPGS: + case IA_CMPGEU: + case IA_CMPGU: + cvalue = 0; + break; + case IA_CMPNE: + case IA_CMPLES: + case IA_CMPLS: + case IA_CMPLEU: + case IA_CMPLU: + cvalue = 1; + break; + } + } + else + { + switch (ins->mOperator) + { + case IA_CMPGES: + ins->mOperator = IA_CMPGEU; + break; + case IA_CMPLES: + ins->mOperator = IA_CMPLEU; + break; + case IA_CMPGS: + ins->mOperator = IA_CMPGU; + break; + case IA_CMPLS: + ins->mOperator = IA_CMPLU; + break; + } + } + } + else if (tvalue[ins->mSrc[0].mTemp]->mOperator == IA_EXT16TO32U) + { + int64 ivalue = tvalue[ins->mSrc[1].mTemp]->mConst.mIntConst; + if (ivalue > 65535) + { + toconst = true; + switch (ins->mOperator) + { + case IA_CMPEQ: + case IA_CMPLES: + case IA_CMPLS: + case IA_CMPLEU: + case IA_CMPLU: + cvalue = 0; + break; + case IA_CMPNE: + case IA_CMPGES: + case IA_CMPGS: + case IA_CMPGEU: + case IA_CMPGU: + cvalue = 1; + break; + } + } + else if (ivalue < 0) + { + toconst = true; + switch (ins->mOperator) + { + case IA_CMPEQ: + case IA_CMPGES: + case IA_CMPGS: + case IA_CMPGEU: + case IA_CMPGU: + cvalue = 0; + break; + case IA_CMPNE: + case IA_CMPLES: + case IA_CMPLS: + case IA_CMPLEU: + case IA_CMPLU: + cvalue = 1; + break; + } + } + else + { + switch (ins->mOperator) + { + case IA_CMPGES: + ins->mOperator = IA_CMPGEU; + break; + case IA_CMPLES: + ins->mOperator = IA_CMPLEU; + break; + case IA_CMPGS: + ins->mOperator = IA_CMPGU; + break; + case IA_CMPLS: + ins->mOperator = IA_CMPLU; + break; + } + } + } + + if (toconst) + { + ins->mCode = IC_CONSTANT; + ins->mConst.mType = IT_BOOL; + ins->mConst.mIntConst = cvalue; + ins->mSrc[0].mTemp = -1; + ins->mSrc[1].mTemp = -1; + ins->mNumOperands = 0; + } + else + { + ins->mSrc[1].mType = tvalue[ins->mSrc[0].mTemp]->mSrc[0].mType; + ins->mSrc[0].mType = tvalue[ins->mSrc[0].mTemp]->mSrc[0].mType; + ins->mSrc[0].mTemp = tvalue[ins->mSrc[0].mTemp]->mSrc[0].mTemp; + } + + UpdateValue(block, ins, tvalue, aliasedLocals, aliasedParams, staticVars, staticProcs); + } + else if (ins->mSrc[1].mTemp == ins->mSrc[0].mTemp) + { + ins->mCode = IC_CONSTANT; + ins->mConst.mType = IT_BOOL; + + switch (ins->mOperator) + { + case IA_CMPEQ: + case IA_CMPGES: + case IA_CMPLES: + case IA_CMPGEU: + case IA_CMPLEU: + ins->mConst.mIntConst = 1; + break; + case IA_CMPNE: + case IA_CMPGS: + case IA_CMPLS: + case IA_CMPGU: + case IA_CMPLU: + ins->mConst.mIntConst = 0; + break; + } + ins->mSrc[0].mTemp = -1; + ins->mSrc[1].mTemp = -1; + ins->mNumOperands = 0; + + UpdateValue(block, ins, tvalue, aliasedLocals, aliasedParams, staticVars, staticProcs); + } + break; + } + break; + case IC_BRANCH: + if (ins->mSrc[0].mTemp >= 0 && tvalue[ins->mSrc[0].mTemp] && tvalue[ins->mSrc[0].mTemp]->mCode == IC_CONSTANT) + { + InterInstruction* tins = tvalue[ins->mSrc[0].mTemp]; + if (IsIntegerType(tins->mConst.mType) || tins->mConst.mType == IT_BOOL) + { + if (tins->mConst.mIntConst) + ins->mCode = IC_JUMP; + else + ins->mCode = IC_JUMPF; + ins->mSrc[0].mTemp = -1; + ins->mNumOperands = 0; + } + else if (tins->mConst.mType == IT_POINTER) + { + if (tins->mConst.mMemory == IM_ABSOLUTE) + { + if (tins->mConst.mIntConst) + ins->mCode = IC_JUMP; + else + ins->mCode = IC_JUMPF; + ins->mSrc[0].mTemp = -1; + ins->mNumOperands = 0; + } + else if (tins->mConst.mMemory == IM_GLOBAL || tins->mConst.mMemory == IM_LOCAL || tins->mConst.mMemory == IM_PARAM || tins->mConst.mMemory == IM_FPARAM) + { + ins->mCode = IC_JUMP; + ins->mSrc[0].mTemp = -1; + ins->mNumOperands = 0; + } + } + else if (tins->mConst.mType == IT_FLOAT) + { + if (tins->mConst.mFloatConst) + ins->mCode = IC_JUMP; + else + ins->mCode = IC_JUMPF; + ins->mSrc[0].mTemp = -1; + ins->mNumOperands = 0; + } + } + break; + case IC_PUSH_FRAME: + case IC_POP_FRAME: + FlushFrameAliases(); + break; + case IC_CALL: + case IC_CALL_NATIVE: + FlushCallAliases(tvalue, aliasedLocals, aliasedParams); + break; + + } +} + + +InterOperand::InterOperand(void) + : mTemp(INVALID_TEMPORARY), mType(IT_NONE), mFinal(false), mIntConst(0), mFloatConst(0), mVarIndex(-1), mOperandSize(0), mLinkerObject(nullptr), mMemory(IM_NONE), mStride(1), mRestricted(0), mMemoryBase(IM_NONE) +{} + +bool InterOperand::IsValid(void) const +{ + return + mRange.mMinState != IntegerValueRange::S_BOUND || + mRange.mMaxState != IntegerValueRange::S_BOUND || + mRange.mMinValue <= mRange.mMaxValue; +} + +bool InterOperand::IsNotUByte(void) const +{ + return !IsValid() || + mRange.mMinState == IntegerValueRange::S_BOUND && mRange.mMinValue < 0 || + mRange.mMaxState == IntegerValueRange::S_BOUND && mRange.mMaxValue >= 256; +} + +bool InterOperand::IsUByte(void) const +{ + return IsValid() && + mRange.mMinState == IntegerValueRange::S_BOUND && mRange.mMinValue >= 0 && + mRange.mMaxState == IntegerValueRange::S_BOUND && mRange.mMaxValue < 256; +} + +bool InterOperand::IsSByte(void) const +{ + return IsValid() && + mRange.mMinState == IntegerValueRange::S_BOUND && mRange.mMinValue >= -128 && + mRange.mMaxState == IntegerValueRange::S_BOUND && mRange.mMaxValue < 128; +} + +bool InterOperand::IsPositive(void) const +{ + return IsValid() && mRange.mMinState == IntegerValueRange::S_BOUND && mRange.mMinValue >= 0; +} + +bool InterOperand::IsInRange(int lower, int upper) const +{ + return IsValid() && + mRange.mMinState == IntegerValueRange::S_BOUND && mRange.mMinValue >= lower && + mRange.mMaxState == IntegerValueRange::S_BOUND && mRange.mMaxValue <= upper; +} + +bool InterOperand::IsUnsigned(void) const +{ + if (IsValid() && mRange.mMinState == IntegerValueRange::S_BOUND && mRange.mMinValue >= 0 && mRange.mMaxState == IntegerValueRange::S_BOUND) + { + switch (mType) + { + case IT_INT8: + return mRange.mMaxValue < 128; + case IT_INT16: + return mRange.mMaxValue < 32768; + case IT_INT32: + return mRange.mMaxValue < 0x80000000ll; + } + } + + return false; +} + + +void InterOperand::ForwardMem(const InterOperand& op) +{ + mIntConst = op.mIntConst; + mFloatConst = op.mFloatConst; + mVarIndex = op.mVarIndex; + mOperandSize = op.mOperandSize; + mLinkerObject = op.mLinkerObject; + mMemory = op.mMemory; + mMemoryBase = op.mMemoryBase; + mTemp = op.mTemp; + mType = op.mType; + mRange = op.mRange; + mStride = op.mStride; + mRestricted = op.mRestricted; + mFinal = false; +} + +void InterOperand::Forward(const InterOperand& op) +{ + mTemp = op.mTemp; + if (mType != IT_INT8 || op.mType != IT_INT16 && op.mType != IT_INT32) + mType = op.mType; + mRange = op.mRange; + mIntConst = op.mIntConst; + mFloatConst = op.mFloatConst; + mFinal = false; +} + +void InterOperand::ForwardTemp(const InterOperand& op) +{ + mTemp = op.mTemp; + if (mType != IT_INT8 || op.mType != IT_INT16 && op.mType != IT_INT32) + mType = op.mType; + mRange = op.mRange; + mFinal = false; +} + +bool InterOperand::IsEqual(const InterOperand& op) const +{ + if (mType != op.mType || mTemp != op.mTemp) + return false; + + if (mTemp < 0 && mMemory != op.mMemory) + return false; + + if (mIntConst != op.mIntConst || mFloatConst != op.mFloatConst) + return false; + + if (mStride != op.mStride) + return false; + + if (mMemory != IM_NONE && mMemory != IM_INDIRECT) + { + if (mVarIndex != op.mVarIndex || mLinkerObject != op.mLinkerObject) + return false; + } + + return true; +} + +InterInstruction::InterInstruction(const Location& loc, InterCode code) + : mLocation(loc), mCode(code), mSrc(mOps) +{ + mOperator = IA_NONE; + + switch (code) + { + case IC_LOAD_TEMPORARY: + case IC_LOAD: + case IC_UNARY_OPERATOR: + case IC_BRANCH: + case IC_TYPECAST: + case IC_RETURN_VALUE: + case IC_RETURN_STRUCT: + case IC_CONVERSION_OPERATOR: + case IC_DISPATCH: + mNumOperands = 1; + break; + + case IC_BINARY_OPERATOR: + case IC_RELATIONAL_OPERATOR: + case IC_STORE: + case IC_LEA: + mNumOperands = 2; + break; + + case IC_CONSTANT: + case IC_JUMP: + case IC_JUMPF: + case IC_RETURN: + mNumOperands = 0; + break; + case IC_ASSEMBLER: + mSrc = new InterOperand[32]; + mNumOperands = 1; + break; + default: + mNumOperands = 3; + break; + } + + mInUse = false; + mVolatile = false; + mInvariant = false; + mSingleAssignment = false; + mNoSideEffects = false; + mConstExpr = false; + mAliasing = false; + mMemmap = false; + mLockOrder = false; +} + +void InterInstruction::Reset(void) +{ + mCode = IC_NONE; + mNumOperands = 0; + mDst.mTemp = -1; +} + +static bool TypeInteger(InterType t) +{ + return t == IT_INT8 || t == IT_INT16 || t == IT_INT32 || t == IT_BOOL || t == IT_POINTER; +} + +static bool TypeCompatible(InterType t1, InterType t2) +{ + return t1 == t2 || TypeInteger(t1) && TypeInteger(t2); +} + +static bool TypeArithmetic(InterType t) +{ + return t == IT_INT8 || t == IT_INT16 || t == IT_INT32 || t == IT_BOOL || t == IT_FLOAT; +} + +static void FilterTempUseUsage(NumberSet& requiredTemps, NumberSet& providedTemps, int temp) +{ + if (temp >= 0) + { + if (!providedTemps[temp]) requiredTemps += temp; + } +} + +static void FilterTempDefineUsage(NumberSet& requiredTemps, NumberSet& providedTemps, int temp) +{ + if (temp >= 0) + { + providedTemps += temp; + } +} + +void InterInstruction::CollectLocalAddressTemps(GrowingIntArray& localTable, GrowingIntArray& paramTable, int& nlocals, int& nparams) +{ + if (mCode == IC_CONSTANT) + { + if (mDst.mType == IT_POINTER && mConst.mMemory == IM_LOCAL) + { + localTable[mDst.mTemp] = mConst.mVarIndex; + if (mConst.mVarIndex >= nlocals) + nlocals = mConst.mVarIndex + 1; + } + else if (mDst.mType == IT_POINTER && (mConst.mMemory == IM_PARAM || mConst.mMemory == IM_FPARAM)) + { + paramTable[mDst.mTemp] = mConst.mVarIndex; + if (mConst.mVarIndex >= nparams) + nparams = mConst.mVarIndex + 1; + } + } + else if (mCode == IC_LEA) + { + if (mSrc[1].mMemory == IM_LOCAL) + { + if (mSrc[1].mTemp >= 0) + localTable[mDst.mTemp] = localTable[mSrc[1].mTemp]; + else + { + localTable[mDst.mTemp] = mSrc[1].mVarIndex; + if (mSrc[1].mVarIndex >= nlocals) + nlocals = mSrc[1].mVarIndex + 1; + } + } + else if (mSrc[1].mMemory == IM_PARAM || mSrc[1].mMemory == IM_FPARAM) + { + if (mSrc[1].mTemp >= 0) + paramTable[mDst.mTemp] = paramTable[mSrc[1].mTemp]; + else + { + paramTable[mDst.mTemp] = mSrc[1].mVarIndex; + if (mSrc[1].mVarIndex >= nparams) + nparams = mSrc[1].mVarIndex + 1; + } + } + else if (mSrc[1].mTemp >= 0) + { + localTable[mDst.mTemp] = localTable[mSrc[1].mTemp]; + paramTable[mDst.mTemp] = paramTable[mSrc[1].mTemp]; + } + } + else if (mCode == IC_LOAD_TEMPORARY) + { + localTable[mDst.mTemp] = localTable[mSrc[0].mTemp]; + paramTable[mDst.mTemp] = paramTable[mSrc[0].mTemp]; + } + else if (mCode == IC_STORE && mSrc[1].mTemp < 0) + { + if (mSrc[1].mMemory == IM_LOCAL) + { + if (mSrc[1].mVarIndex >= nlocals) + nlocals = mSrc[1].mVarIndex + 1; + } + else if (mSrc[1].mMemory == IM_PARAM || mSrc[1].mMemory == IM_FPARAM) + { + if (mSrc[1].mVarIndex >= nparams) + nparams = mSrc[1].mVarIndex + 1; + } + } + else if (mCode == IC_LOAD && mSrc[0].mTemp < 0) + { + if (mSrc[0].mMemory == IM_LOCAL) + { + if (mSrc[0].mVarIndex >= nlocals) + nlocals = mSrc[0].mVarIndex + 1; + } + else if (mSrc[0].mMemory == IM_PARAM || mSrc[0].mMemory == IM_FPARAM) + { + if (mSrc[0].mVarIndex >= nparams) + nparams = mSrc[0].mVarIndex + 1; + } + } +} + +void InterInstruction::MarkAliasedLocalTemps(const GrowingIntArray& localTable, NumberSet& aliasedLocals, const GrowingIntArray& paramTable, NumberSet& aliasedParams) +{ + if (mCode == IC_STORE && mSrc[0].mTemp >= 0) + { + int l = localTable[mSrc[0].mTemp]; + if (l >= 0) + aliasedLocals += l; + l = paramTable[mSrc[0].mTemp]; + if (l >= 0) + aliasedParams += l; + } +} + +void InterInstruction::FilterTempUsage(NumberSet& requiredTemps, NumberSet& providedTemps) +{ + if (mCode != IC_NONE) + { + for (int i = 0; i < mNumOperands; i++) + FilterTempUseUsage(requiredTemps, providedTemps, mSrc[i].mTemp); + FilterTempDefineUsage(requiredTemps, providedTemps, mDst.mTemp); + } +} + +void InterInstruction::FilterStaticVarsUsage(const GrowingVariableArray& staticVars, NumberSet& requiredVars, NumberSet& providedVars) +{ + if (mCode == IC_LOAD) + { + if (mSrc[0].mMemory == IM_INDIRECT) + { + for (int i = 0; i < staticVars.Size(); i++) + { + if (staticVars[i]->mAliased && !providedVars[i]) + requiredVars += i; + } + } + else if (mSrc[0].mMemory == IM_GLOBAL) + { + if (mSrc[0].mVarIndex >= 0 && !providedVars[mSrc[0].mVarIndex]) + requiredVars += mSrc[0].mVarIndex; + } + } + else if (mCode == IC_STORE) + { + if (mSrc[1].mMemory == IM_INDIRECT) + { + for (int i = 0; i < staticVars.Size(); i++) + { + if (staticVars[i]->mAliased && !providedVars[i]) + requiredVars += i; + } + } + else if (mSrc[1].mMemory == IM_GLOBAL) + { + if (mSrc[1].mVarIndex >= 0) + { + if (mSrc[1].mIntConst == 0 && mSrc[1].mOperandSize == staticVars[mSrc[1].mVarIndex]->mSize) + providedVars += mSrc[1].mVarIndex; + else if (!providedVars[mSrc[1].mVarIndex]) + requiredVars += mSrc[1].mVarIndex; + } + } + } + else if (mCode == IC_COPY || mCode == IC_CALL || mCode == IC_CALL_NATIVE || mCode == IC_RETURN || mCode == IC_RETURN_STRUCT || mCode == IC_RETURN_VALUE || mCode == IC_STRCPY || mCode == IC_DISPATCH || mCode == IC_FILL) + { + requiredVars.OrNot(providedVars); + } +} + +void InterInstruction::FilterStaticVarsByteUsage(const GrowingVariableArray& staticVars, NumberSet& requiredVars, NumberSet& providedVars, Errors* errors) +{ + if (mCode == IC_LOAD) + { + if (mSrc[0].mMemory == IM_INDIRECT) + { + if (!mSrc[0].mRestricted) + { + for (int i = 0; i < staticVars.Size(); i++) + { + if (staticVars[i]->mAliased && !providedVars.RangeFilled(staticVars[i]->mByteIndex, staticVars[i]->mSize)) + requiredVars.AddRange(staticVars[i]->mByteIndex, staticVars[i]->mSize); + } + } + } + else if (mSrc[0].mMemory == IM_GLOBAL) + { + if (mSrc[0].mVarIndex >= 0) + { + if (int(mSrc[0].mIntConst) < 0 || int(mSrc[0].mIntConst) + InterTypeSize[mDst.mType] > staticVars[mSrc[0].mVarIndex]->mSize) + errors->Error(mLocation, EWARN_INDEX_OUT_OF_BOUNDS, "Index out of bounds"); + else if (!providedVars.RangeFilled(staticVars[mSrc[0].mVarIndex]->mByteIndex + int(mSrc[0].mIntConst), InterTypeSize[mDst.mType])) + requiredVars.AddRange(staticVars[mSrc[0].mVarIndex]->mByteIndex + int(mSrc[0].mIntConst), InterTypeSize[mDst.mType]); + } + } + } + else if (mCode == IC_STORE) + { + if (mSrc[1].mMemory == IM_INDIRECT) + { + } + else if (mSrc[1].mMemory == IM_GLOBAL) + { + if (mSrc[1].mVarIndex >= 0) + { + if (int(mSrc[1].mIntConst) < 0 || int(mSrc[1].mIntConst) + InterTypeSize[mSrc[0].mType] > staticVars[mSrc[1].mVarIndex]->mSize) + errors->Error(mLocation, EWARN_INDEX_OUT_OF_BOUNDS, "Index out of bounds"); + else + providedVars.AddRange(staticVars[mSrc[1].mVarIndex]->mByteIndex + int(mSrc[1].mIntConst), InterTypeSize[mSrc[0].mType]); + } + } + } + else if (mCode == IC_COPY || mCode == IC_CALL || mCode == IC_CALL_NATIVE || mCode == IC_ASSEMBLER || mCode == IC_RETURN || mCode == IC_RETURN_STRUCT || mCode == IC_RETURN_VALUE || mCode == IC_STRCPY || mCode == IC_DISPATCH || mCode == IC_FILL) + { + requiredVars.OrNot(providedVars); + } +} + +void InterInstruction::FilterLocalVarsByteUsage(const GrowingVariableArray& localVars, NumberSet& requiredVars, NumberSet& providedVars, Errors* errors) +{ + if (mCode == IC_LOAD) + { + if (mSrc[0].mMemory == IM_INDIRECT) + { + if (!mSrc[0].mRestricted) + { + for (int i = 0; i < localVars.Size(); i++) + { + if (localVars[i] && localVars[i]->mAliased && !providedVars.RangeFilled(localVars[i]->mByteIndex, localVars[i]->mSize)) + requiredVars.AddRange(localVars[i]->mByteIndex, localVars[i]->mSize); + } + } + } + else if (mSrc[0].mMemory == IM_LOCAL) + { + if (mSrc[0].mVarIndex >= 0) + { + if (int(mSrc[0].mIntConst) < 0 || int(mSrc[0].mIntConst) + InterTypeSize[mDst.mType] > localVars[mSrc[0].mVarIndex]->mSize) + errors->Error(mLocation, EWARN_INDEX_OUT_OF_BOUNDS, "Index out of bounds"); + else if (!providedVars.RangeFilled(localVars[mSrc[0].mVarIndex]->mByteIndex + int(mSrc[0].mIntConst), InterTypeSize[mDst.mType])) + requiredVars.AddRange(localVars[mSrc[0].mVarIndex]->mByteIndex + int(mSrc[0].mIntConst), InterTypeSize[mDst.mType]); + } + } + } + else if (mCode == IC_STORE) + { + if (mSrc[1].mMemory == IM_INDIRECT) + { + for (int i = 0; i < localVars.Size(); i++) + { + if (localVars[i] && localVars[i]->mAliased) + providedVars.AddRange(localVars[i]->mByteIndex, localVars[i]->mSize); + } + } + else if (mSrc[1].mMemory == IM_LOCAL) + { + if (mSrc[1].mVarIndex >= 0) + { + if (int(mSrc[1].mIntConst) < 0 || int(mSrc[1].mIntConst) + InterTypeSize[mSrc[0].mType] > localVars[mSrc[1].mVarIndex]->mSize) + errors->Error(mLocation, EWARN_INDEX_OUT_OF_BOUNDS, "Index out of bounds"); + else + providedVars.AddRange(localVars[mSrc[1].mVarIndex]->mByteIndex + int(mSrc[1].mIntConst), InterTypeSize[mSrc[0].mType]); + } + } + } + else if (mCode == IC_COPY || mCode == IC_STRCPY) + { + if (mSrc[0].mMemory == IM_LOCAL) + { + requiredVars.AddRange(localVars[mSrc[0].mVarIndex]->mByteIndex + int(mSrc[0].mIntConst), mConst.mOperandSize); + } + else if (mSrc[0].mMemory == IM_INDIRECT) + { + for (int i = 0; i < localVars.Size(); i++) + if (localVars[i] && localVars[i]->mAliased) + requiredVars.AddRange(localVars[i]->mByteIndex, localVars[i]->mSize); + } + + if (mSrc[1].mMemory == IM_LOCAL) + { + providedVars.AddRange(localVars[mSrc[1].mVarIndex]->mByteIndex + int(mSrc[1].mIntConst), mConst.mOperandSize); + } + else if (mSrc[1].mMemory == IM_INDIRECT) + { + for (int i = 0; i < localVars.Size(); i++) + if (localVars[i] && localVars[i]->mAliased) + providedVars.AddRange(localVars[i]->mByteIndex, localVars[i]->mSize); + } + } + else if (mCode == IC_FILL) + { + if (mSrc[1].mMemory == IM_LOCAL) + { + providedVars.AddRange(localVars[mSrc[1].mVarIndex]->mByteIndex + int(mSrc[1].mIntConst), mConst.mOperandSize); + } + else if (mSrc[1].mMemory == IM_INDIRECT) + { + for (int i = 0; i < localVars.Size(); i++) + if (localVars[i] && localVars[i]->mAliased) + providedVars.AddRange(localVars[i]->mByteIndex, localVars[i]->mSize); + } + } + else if (mCode == IC_CALL || mCode == IC_CALL_NATIVE || mCode == IC_ASSEMBLER ||mCode == IC_DISPATCH) + { + for (int i = 0; i < localVars.Size(); i++) + { + if (localVars[i] && localVars[i]->mAliased) + { + providedVars.AddRange(localVars[i]->mByteIndex, localVars[i]->mSize); + requiredVars.AddRange(localVars[i]->mByteIndex, localVars[i]->mSize); + } + } + } +} + +void InterInstruction::FilterVarsUsage(const GrowingVariableArray& localVars, NumberSet& requiredVars, NumberSet& providedVars, const GrowingVariableArray& params, NumberSet& requiredParams, NumberSet& providedParams, InterMemory paramMemory) +{ + if (mCode == IC_LOAD) + { + if (mSrc[0].mMemory == IM_LOCAL) + { + assert(mSrc[0].mTemp < 0); + if (!providedVars[mSrc[0].mVarIndex]) + requiredVars += mSrc[0].mVarIndex; + } + else if (mSrc[0].mMemory == paramMemory) + { + assert(mSrc[0].mTemp < 0); + if (!providedParams[mSrc[0].mVarIndex]) + requiredParams += mSrc[0].mVarIndex; + } + } +#if 0 + else if (mCode == IC_LEA) + { + if (mSrc[1].mMemory == IM_LOCAL) + { + assert(mSrc[1].mTemp < 0); + if (!providedVars[mSrc[1].mVarIndex]) + requiredVars += mSrc[1].mVarIndex; + } + else if (mSrc[1].mMemory == paramMemory) + { + assert(mSrc[1].mTemp < 0); + if (!providedParams[mSrc[1].mVarIndex]) + requiredParams += mSrc[1].mVarIndex; + } + } + else if (mCode == IC_CONSTANT) + { + if (mConst.mMemory == IM_LOCAL) + { + if (!providedVars[mConst.mVarIndex]) + requiredVars += mConst.mVarIndex; + } + else if (mConst.mMemory == paramMemory) + { + assert(mConst.mTemp < 0); + if (!providedParams[mConst.mVarIndex]) + requiredParams += mConst.mVarIndex; + } + } +#endif + else if (mCode == IC_STORE) + { + if (mSrc[1].mMemory == IM_LOCAL) + { + assert(mSrc[1].mTemp < 0); + if (!providedVars[mSrc[1].mVarIndex] && (mSrc[1].mIntConst != 0 || mSrc[1].mOperandSize != localVars[mSrc[1].mVarIndex]->mSize)) + requiredVars += mSrc[1].mVarIndex; + providedVars += mSrc[1].mVarIndex; + } + else if (mSrc[1].mMemory == paramMemory) + { + assert(mSrc[1].mTemp < 0); + if (!providedParams[mSrc[1].mVarIndex] && (mSrc[1].mIntConst != 0 || mSrc[1].mOperandSize != params[mSrc[1].mVarIndex]->mSize)) + requiredParams += mSrc[1].mVarIndex; + providedParams += mSrc[1].mVarIndex; + } + } + else if (mCode == IC_FILL) + { + if (mSrc[1].mMemory == IM_LOCAL) + { + assert(mSrc[1].mTemp < 0); + if (!providedVars[mSrc[1].mVarIndex] && (mSrc[1].mIntConst != 0 || mSrc[1].mOperandSize != localVars[mSrc[1].mVarIndex]->mSize)) + requiredVars += mSrc[1].mVarIndex; + providedVars += mSrc[1].mVarIndex; + } + else if (mSrc[1].mMemory == paramMemory) + { + assert(mSrc[1].mTemp < 0); + if (!providedParams[mSrc[1].mVarIndex] && (mSrc[1].mIntConst != 0 || mSrc[1].mOperandSize != params[mSrc[1].mVarIndex]->mSize)) + requiredParams += mSrc[1].mVarIndex; + providedParams += mSrc[1].mVarIndex; + } + } + else if (mCode == IC_COPY || mCode == IC_STRCPY) + { + if (mSrc[0].mMemory == IM_LOCAL) + { + assert(mSrc[0].mTemp < 0); + if (!providedVars[mSrc[0].mVarIndex]) + requiredVars += mSrc[0].mVarIndex; + } + else if (mSrc[0].mMemory == paramMemory) + { + assert(mSrc[0].mTemp < 0); + if (!providedParams[mSrc[0].mVarIndex]) + requiredParams += mSrc[0].mVarIndex; + } + + if (mSrc[1].mMemory == IM_LOCAL) + { + assert(mSrc[1].mTemp < 0); + if (!providedVars[mSrc[1].mVarIndex] && (mSrc[1].mIntConst != 0 || mSrc[1].mOperandSize != localVars[mSrc[1].mVarIndex]->mSize)) + requiredVars += mSrc[1].mVarIndex; + providedVars += mSrc[1].mVarIndex; + } + else if (mSrc[1].mMemory == paramMemory) + { + assert(mSrc[1].mTemp < 0); + if (!providedParams[mSrc[1].mVarIndex] && (mSrc[1].mIntConst != 0 || mSrc[1].mOperandSize != params[mSrc[1].mVarIndex]->mSize)) + requiredParams += mSrc[1].mVarIndex; + providedParams += mSrc[1].mVarIndex; + } + } + else if (mCode == IC_ASSEMBLER) + { + for (int i = 1; i < mNumOperands; i++) + { + if (mSrc[i].mMemory == IM_LOCAL) + { + if (!providedVars[mSrc[i].mVarIndex]) + requiredVars += mSrc[i].mVarIndex; + } + else if (mSrc[i].mMemory == paramMemory) + { + if (!providedParams[mSrc[i].mVarIndex]) + requiredParams += mSrc[i].mVarIndex; + } + } + } +} + +static void PerformTempUseForwarding(int& temp, TempForwardingTable& forwardingTable) +{ + if (temp >= 0) + temp = forwardingTable[temp]; +} + +static void PerformTempDefineForwarding(int temp, TempForwardingTable& forwardingTable) +{ + if (temp >= 0) + { + forwardingTable.Destroy(temp); + } +} + +bool InterInstruction::PropagateConstTemps(const GrowingInstructionPtrArray& ctemps) +{ + switch (mCode) + { + case IC_LOAD: + case IC_CALL: + case IC_CALL_NATIVE: + if (mSrc[0].mTemp >= 0 && ctemps[mSrc[0].mTemp]) + { + InterInstruction* ains = ctemps[mSrc[0].mTemp]; + mSrc[0].mIntConst = ToTypedUnsigned(mSrc[0].mIntConst + ains->mConst.mIntConst, IT_INT16); + mSrc[0].mLinkerObject = ains->mConst.mLinkerObject; + mSrc[0].mVarIndex = ains->mConst.mVarIndex; + mSrc[0].mMemory = ains->mConst.mMemory; + mSrc[0].mTemp = -1; + return true; + } + break; + case IC_STORE: + if (mSrc[1].mTemp >= 0 && ctemps[mSrc[1].mTemp]) + { + InterInstruction* ains = ctemps[mSrc[1].mTemp]; + mSrc[1].mIntConst = ToTypedUnsigned(mSrc[1].mIntConst + ains->mConst.mIntConst, IT_INT16); + mSrc[1].mLinkerObject = ains->mConst.mLinkerObject; + mSrc[1].mVarIndex = ains->mConst.mVarIndex; + mSrc[1].mMemory = ains->mConst.mMemory; + mSrc[1].mTemp = -1; + return true; + } + if (mSrc[0].mTemp >= 0 && ctemps[mSrc[0].mTemp] && IsIntegerType(mSrc[0].mType)) + { + InterInstruction* ains = ctemps[mSrc[0].mTemp]; + mSrc[0].mIntConst = LimitIntConstValue(mSrc[0].mType, ains->mConst.mIntConst); + mSrc[0].mTemp = -1; + return true; + } + break; + case IC_LOAD_TEMPORARY: + if (mSrc[0].mTemp >= 0 && ctemps[mSrc[0].mTemp]) + { + InterInstruction* ains = ctemps[mSrc[0].mTemp]; + mCode = IC_CONSTANT; + mConst = ains->mConst; + mSrc[0].mTemp = -1; + mNumOperands = 0; + return true; + } + break; + case IC_BINARY_OPERATOR: + { + bool changed = false; + + for (int i = 0; i < 2; i++) + { + if (mSrc[i].mTemp >= 0 && ctemps[mSrc[i].mTemp]) + { + InterInstruction* ains = ctemps[mSrc[i].mTemp]; + mSrc[i] = ains->mConst; + mSrc[i].mType = mDst.mType; + if (IsIntegerType(mSrc[i].mType)) + mSrc[i].mIntConst = LimitIntConstValue(mSrc[i].mType, mSrc[i].mIntConst); + changed = true; + } + } + + if (changed) + { + this->ConstantFolding(); + return true; + } + } break; + + case IC_RELATIONAL_OPERATOR: + { + bool changed = false; + + for (int i = 0; i < 2; i++) + { + if (mSrc[i].mTemp >= 0 && ctemps[mSrc[i].mTemp]) + { + InterType t = mSrc[i].mType; + InterInstruction* ains = ctemps[mSrc[i].mTemp]; + + if (t != IT_POINTER || ains->mConst.mMemory == IM_ABSOLUTE || ains->mConst.mMemory == IM_GLOBAL) + { + mSrc[i] = ains->mConst; + mSrc[i].mType = t; + changed = true; + } + else if (t == IT_POINTER && ains->mConst.mType == IT_INT16) + { + mSrc[i] = ains->mConst; + mSrc[i].mMemory = IM_ABSOLUTE; + mSrc[i].mType = IT_POINTER; + changed = true; + } + } + } + + if (!changed) + { + if (mSrc[0].mType == IT_POINTER && mSrc[1].mType == IT_POINTER && + mSrc[0].mTemp >= 0 && mSrc[1].mTemp >= 0 && + ctemps[mSrc[0].mTemp] && ctemps[mSrc[1].mTemp]) + { + InterInstruction* si0 = ctemps[mSrc[0].mTemp]; + InterInstruction* si1 = ctemps[mSrc[1].mTemp]; + + if (si0->mConst.mMemory != IM_INDIRECT && si1->mConst.mMemory != IM_INDIRECT) + { + mCode = IC_CONSTANT; + mConst.mIntConst = ::ConstantRelationalPointerFolding(mOperator, si1->mConst, si0->mConst); + mConst.mType = IT_BOOL; + mNumOperands = 0; + return true; + } + } + } + + if (changed) + { + this->ConstantFolding(); + return true; + } + + } break; + + case IC_FREE: + { + if (mSrc[0].mTemp >= 0 && ctemps[mSrc[0].mTemp]) + { + InterInstruction* ains = ctemps[mSrc[0].mTemp]; + + if (ains->mConst.mMemory == IM_ABSOLUTE && ains->mConst.mIntConst == 0) + { + mCode = IC_NONE; + mDst.mTemp = -1; + mNumOperands = 0; + return true; + } + } + + } break; + + case IC_CONVERSION_OPERATOR: + case IC_UNARY_OPERATOR: + { + if (mSrc[0].mTemp >= 0 && ctemps[mSrc[0].mTemp]) + { + InterInstruction* ains = ctemps[mSrc[0].mTemp]; + mSrc[0] = ains->mConst; + mSrc[0].mType = ains->mDst.mType; + this->ConstantFolding(); + return true; + } + + } break; + + case IC_LEA: + if (mSrc[0].mTemp >= 0 && ctemps[mSrc[0].mTemp]) + { + InterInstruction* ains = ctemps[mSrc[0].mTemp]; + mSrc[0] = ains->mConst; + mSrc[0].mType = ains->mDst.mType; + + this->ConstantFolding(); + return true; + } + else if (mSrc[1].mTemp >= 0 && ctemps[mSrc[1].mTemp]) + { + InterInstruction* ains = ctemps[mSrc[1].mTemp]; + mSrc[1] = ains->mConst; + mSrc[1].mType = IT_POINTER; + + this->ConstantFolding(); + return true; + } + break; + + case IC_BRANCH: + if (mSrc[0].mTemp >= 0 && ctemps[mSrc[0].mTemp]) + { + InterInstruction* ains = ctemps[mSrc[0].mTemp]; + mSrc[0] = ains->mConst; + + this->ConstantFolding(); + return true; + } + break; + } + + + return false; +} + +void InterInstruction::PerformTempForwarding(TempForwardingTable& forwardingTable, bool reverse) +{ + if (mCode != IC_NONE) + { + for (int i = 0; i < mNumOperands; i++) + PerformTempUseForwarding(mSrc[i].mTemp, forwardingTable); + PerformTempDefineForwarding(mDst.mTemp, forwardingTable); + } + if (mCode == IC_LOAD_TEMPORARY && mDst.mTemp != mSrc[0].mTemp) + { + if (reverse) + forwardingTable.Build(mSrc[0].mTemp, mDst.mTemp); + else + forwardingTable.Build(mDst.mTemp, mSrc[0].mTemp); + } +} + +bool InterInstruction::RemoveUnusedResultInstructions(InterInstruction* pre, NumberSet& requiredTemps) +{ + bool changed = false; + + if (pre && mCode == IC_LOAD_TEMPORARY && pre->mDst.mTemp == mSrc[0].mTemp && !requiredTemps[mSrc[0].mTemp] && pre->mDst.mTemp >= 0) + { + // previous instruction produced result, but it is not needed here + pre->mDst.mTemp = mDst.mTemp; + pre->mSingleAssignment = mSingleAssignment; + + mCode = IC_NONE; + mDst.mTemp = -1; + for (int i = 0; i < mNumOperands; i++) + mSrc[i].mTemp = -1; + mNumOperands = 0; + + changed = true; + } + else if (mCode == IC_LOAD_TEMPORARY && mDst.mTemp == mSrc[0].mTemp) + { + mCode = IC_NONE; + mDst.mTemp = -1; + for (int i = 0; i < mNumOperands; i++) + mSrc[i].mTemp = -1; + mNumOperands = 0; + + changed = true; + } + else if (mCode == IC_COPY && !mVolatile && mSrc[0].mTemp < 0 && mSrc[1].mTemp < 0 && mSrc[0].mMemory == mSrc[1].mMemory && + mSrc[0].mVarIndex == mSrc[1].mVarIndex && mSrc[0].mLinkerObject == mSrc[1].mLinkerObject && mSrc[0].mIntConst == mSrc[1].mIntConst) + { + mNumOperands = 0; + mDst.mTemp = -1; + mCode = IC_NONE; + changed = true; + } + else if (mDst.mTemp != -1) + { + if (!requiredTemps[mDst.mTemp] && mDst.mTemp >= 0) + { + if (mCode == IC_LOAD && mVolatile) + { + } + else if (!HasSideEffect(mCode)) + { + mCode = IC_NONE; + mDst.mTemp = -1; + for (int i = 0; i < mNumOperands; i++) + mSrc[i].mTemp = -1; + mNumOperands = 0; + + changed = true; + } + else + { + mDst.mTemp = -1; + + changed = true; + } + } + else + requiredTemps -= mDst.mTemp; + } + + for (int i = 0; i < mNumOperands; i++) + { + if (mSrc[i].mTemp >= 0) mSrc[i].mFinal = !requiredTemps[mSrc[i].mTemp] && mSrc[i].mTemp >= 0; + } + + for (int i = 0; i < mNumOperands; i++) + { + if (mSrc[i].mTemp >= 0) requiredTemps += mSrc[i].mTemp; + } + + return changed; +} + +void InterInstruction::BuildCallerSaveTempSet(NumberSet& requiredTemps, NumberSet& callerSaveTemps) +{ + if (mDst.mTemp >= 0) + requiredTemps -= mDst.mTemp; + + if (mCode == IC_CALL || mCode == IC_CALL_NATIVE || mCode == IC_DISPATCH) + callerSaveTemps |= requiredTemps; + + for (int i = 0; i < mNumOperands; i++) + if (mSrc[i].mTemp >= 0) requiredTemps += mSrc[i].mTemp; +} + +bool InterInstruction::RemoveUnusedStoreInstructions(const GrowingVariableArray& localVars, NumberSet& requiredVars, const GrowingVariableArray& params, NumberSet& requiredParams, InterMemory paramMemory) +{ + bool changed = false; + + if (mCode == IC_LOAD) + { + if (mSrc[0].mMemory == IM_LOCAL) + { + requiredVars += mSrc[0].mVarIndex; + } + else if (mSrc[0].mMemory == paramMemory) + { + requiredParams += mSrc[0].mVarIndex; + } + } + else if (mCode == IC_STORE) + { + if (mSrc[1].mMemory == IM_LOCAL) + { + if (localVars[mSrc[1].mVarIndex]->mAliased) + ; + else if (requiredVars[mSrc[1].mVarIndex]) + { + if (mSrc[1].mIntConst == 0 && mSrc[1].mOperandSize == localVars[mSrc[1].mVarIndex]->mSize) + requiredVars -= mSrc[1].mVarIndex; + } + else + { + mNumOperands = 0; + mSrc[0].mTemp = -1; + mCode = IC_NONE; + changed = true; + } + } + else if (mSrc[1].mMemory == paramMemory) + { + if (params[mSrc[1].mVarIndex]->mAliased) + ; + else if (requiredParams[mSrc[1].mVarIndex]) + { + if (mSrc[1].mIntConst == 0 && mSrc[1].mOperandSize == params[mSrc[1].mVarIndex]->mSize) + requiredParams -= mSrc[1].mVarIndex; + } + else + { + mNumOperands = 0; + mSrc[0].mTemp = -1; + mCode = IC_NONE; + changed = true; + } + } + } + else if (mCode == IC_SELECT) + { + if (mDst.mType == IT_POINTER) + { + if (mSrc[1].mTemp < 0) + { + if (mSrc[1].mMemory == IM_LOCAL) + { + requiredVars += mSrc[1].mVarIndex; + } + else if (mSrc[1].mMemory == paramMemory) + { + requiredParams += mSrc[1].mVarIndex; + } + } + if (mSrc[2].mTemp < 0) + { + if (mSrc[2].mMemory == IM_LOCAL) + { + requiredVars += mSrc[2].mVarIndex; + } + else if (mSrc[2].mMemory == paramMemory) + { + requiredParams += mSrc[2].mVarIndex; + } + } + } + } + else if (mCode == IC_CONSTANT) + { + if (mConst.mType == IT_POINTER) + { + if (mConst.mMemory == IM_LOCAL) + { + requiredVars += mConst.mVarIndex; + } + else if (mConst.mMemory == paramMemory) + { + requiredParams += mConst.mVarIndex; + } + } + } + else if (mCode == IC_COPY) + { + if (!mVolatile && mSrc[0].mTemp < 0 && mSrc[1].mTemp < 0 && mSrc[0].mMemory == mSrc[1].mMemory && + mSrc[0].mVarIndex == mSrc[1].mVarIndex && mSrc[0].mLinkerObject == mSrc[1].mLinkerObject && mSrc[0].mIntConst == mSrc[1].mIntConst) + { + mNumOperands = 0; + mSrc[0].mTemp = -1; + mCode = IC_NONE; + changed = true; + } + else if (mSrc[1].mMemory == IM_LOCAL) + { + if (localVars[mSrc[1].mVarIndex]->mAliased) + ; + else if (requiredVars[mSrc[1].mVarIndex]) + { + if (mSrc[1].mIntConst == 0 && mSrc[1].mOperandSize == localVars[mSrc[1].mVarIndex]->mSize) + requiredVars -= mSrc[1].mVarIndex; + } + else + { + mNumOperands = 0; + mSrc[0].mTemp = -1; + mCode = IC_NONE; + changed = true; + } + } + else if (mSrc[1].mMemory == paramMemory) + { + if (params[mSrc[1].mVarIndex]->mAliased) + ; + else if (requiredParams[mSrc[1].mVarIndex]) + { + if (mSrc[1].mIntConst == 0 && mSrc[1].mOperandSize == params[mSrc[1].mVarIndex]->mSize) + requiredParams -= mSrc[1].mVarIndex; + } + else + { + mNumOperands = 0; + mSrc[0].mTemp = -1; + mCode = IC_NONE; + changed = true; + } + } + + if (mSrc[0].mMemory == IM_LOCAL) + { + requiredVars += mSrc[0].mVarIndex; + } + else if (mSrc[0].mMemory == paramMemory) + { + requiredParams += mSrc[0].mVarIndex; + } + } + + + return changed; +} + +bool InterInstruction::RemoveUnusedStaticStoreInstructions(InterCodeBasicBlock* block, const GrowingVariableArray& staticVars, NumberSet& requiredVars, GrowingInstructionPtrArray& storeIns) +{ + bool changed = false; + + if (mCode == IC_LOAD) + { + if (mSrc[0].mMemory == IM_INDIRECT) + { + for (int i = 0; i < staticVars.Size(); i++) + { + if (staticVars[i]->mAliased) + requiredVars += i; + } + } + else if (mSrc[0].mMemory == IM_GLOBAL) + { + if (mSrc[0].mVarIndex >= 0) + requiredVars += mSrc[0].mVarIndex; + } + + int k = 0; + for (int i = 0; i < storeIns.Size(); i++) + { + if (!block->CollidingMem(this, storeIns[i])) + storeIns[k++] = storeIns[i]; + } + storeIns.SetSize(k); + } + else if (mCode == IC_STORE) + { + if (mSrc[1].mMemory == IM_GLOBAL && mSrc[1].mVarIndex >= 0) + { + if (requiredVars[mSrc[1].mVarIndex]) + { + if (mSrc[1].mIntConst == 0 && mSrc[1].mOperandSize == staticVars[mSrc[1].mVarIndex]->mSize) + requiredVars -= mSrc[1].mVarIndex; + } + else if (!mVolatile) + { + mNumOperands = 0; + mSrc[0].mTemp = -1; + mCode = IC_NONE; + changed = true; + } + } + else + { + int i = 0; + while (i < storeIns.Size() && !SameMem(mSrc[1], storeIns[i])) + i++; + if (!mVolatile && i < storeIns.Size()) + { + mCode = IC_NONE; + mNumOperands = 0; + changed = true; + } + else + { + int k = 0; + for (int i = 0; i < storeIns.Size(); i++) + { + if (!block->CollidingMem(this, storeIns[i])) + storeIns[k++] = storeIns[i]; + } + storeIns.SetSize(k); + } + } + } + else if (mCode == IC_SELECT) + { + if (mDst.mType == IT_POINTER) + { + if (mSrc[1].mTemp < 0) + { + if (mSrc[1].mMemory == IM_GLOBAL && mSrc[1].mVarIndex >= 0) + requiredVars += mSrc[1].mVarIndex; + } + if (mSrc[2].mTemp < 0) + { + if (mSrc[2].mMemory == IM_GLOBAL && mSrc[2].mVarIndex >= 0) + requiredVars += mSrc[2].mVarIndex; + } + } + } + else if (mCode == IC_COPY || mCode == IC_STRCPY || mCode == IC_FILL) + { + requiredVars.Fill(); + storeIns.SetSize(0); + } + else if (mCode == IC_CALL || mCode == IC_CALL_NATIVE || mCode == IC_ASSEMBLER || mCode == IC_RETURN || mCode == IC_RETURN_STRUCT || mCode == IC_RETURN_VALUE || mCode == IC_DISPATCH) + { + requiredVars.Fill(); + storeIns.SetSize(0); + } + + if (mCode == IC_STORE) + storeIns.Push(this); + + if (mDst.mTemp >= 0) + { + int k = 0; + for (int i = 0; i < storeIns.Size(); i++) + { + if (storeIns[i]->mSrc[1].mTemp != mDst.mTemp) + storeIns[k++] = storeIns[i]; + } + storeIns.SetSize(k); + } + + return changed; +} + +bool InterInstruction::RemoveUndefinedLocalLoadByteInstructions(InterCodeBasicBlock* block, const GrowingVariableArray& localVars, NumberSet& providedVars) +{ + bool changed = false; + + if (mCode == IC_LOAD) + { + if (mSrc[0].mMemory == IM_LOCAL) + { + if (providedVars.RangeClear(localVars[mSrc[0].mVarIndex]->mByteIndex + int(mSrc[0].mIntConst), InterTypeSize[mDst.mType])) + { +// printf("RemoveLoad %s:%d, %s:%d\n", block->mProc->mIdent->mString, block->mIndex, mLocation.mFileName, mLocation.mLine); + mNumOperands = 0; + mSrc[0].mTemp = -1; + mCode = IC_CONSTANT; + mConst.mType = mDst.mType; + mConst.mIntConst = 0; + mConst.mFloatConst = 0.0; + mConst.mLinkerObject = nullptr; + mConst.mMemory = IM_ABSOLUTE; + changed = true; + } + } + } + else if (mCode == IC_STORE) + { + if (mSrc[1].mMemory == IM_INDIRECT) + { + for (int i = 0; i < localVars.Size(); i++) + { + if (localVars[i] && localVars[i]->mAliased) + providedVars.AddRange(localVars[i]->mByteIndex, localVars[i]->mSize); + } + } + else if (mSrc[1].mMemory == IM_LOCAL && mSrc[1].mVarIndex >= 0) + { + providedVars.AddRange(localVars[mSrc[1].mVarIndex]->mByteIndex + int(mSrc[1].mIntConst), InterTypeSize[mSrc[0].mType]); + } + } + else if (mCode == IC_COPY || mCode == IC_STRCPY) + { + if (mSrc[1].mMemory == IM_LOCAL) + { + providedVars.AddRange(localVars[mSrc[1].mVarIndex]->mByteIndex + int(mSrc[1].mIntConst), mConst.mOperandSize); + } + else if (mSrc[1].mMemory == IM_INDIRECT) + { + for (int i = 0; i < localVars.Size(); i++) + if (localVars[i] && localVars[i]->mAliased) + providedVars.AddRange(localVars[i]->mByteIndex, localVars[i]->mSize); + } + } + else if (mCode == IC_FILL) + { + if (mSrc[1].mMemory == IM_LOCAL) + { + providedVars.AddRange(localVars[mSrc[1].mVarIndex]->mByteIndex + int(mSrc[1].mIntConst), mConst.mOperandSize); + } + else if (mSrc[1].mMemory == IM_INDIRECT) + { + for (int i = 0; i < localVars.Size(); i++) + if (localVars[i] && localVars[i]->mAliased) + providedVars.AddRange(localVars[i]->mByteIndex, localVars[i]->mSize); + } + } + else if (mCode == IC_CALL || mCode == IC_CALL_NATIVE || mCode == IC_ASSEMBLER || mCode == IC_DISPATCH) + { + providedVars.Fill(); + } + + return changed; +} + +bool InterInstruction::RemoveUnusedLocalStoreByteInstructions(InterCodeBasicBlock* block, const GrowingVariableArray& localVars, NumberSet& requiredVars) +{ + bool changed = false; + + if (mCode == IC_LOAD) + { + if (mSrc[0].mMemory == IM_INDIRECT) + { + if (!mSrc[0].mRestricted) + { + for (int i = 0; i < localVars.Size(); i++) + { + if (localVars[i] && localVars[i]->mAliased) + requiredVars.AddRange(localVars[i]->mByteIndex, localVars[i]->mSize); + } + } + } + else if (mSrc[0].mMemory == IM_LOCAL) + { + if (mSrc[0].mVarIndex >= 0) + requiredVars.AddRange(localVars[mSrc[0].mVarIndex]->mByteIndex + int(mSrc[0].mIntConst), InterTypeSize[mDst.mType]); + } + } + else if (mCode == IC_STORE) + { + if (mSrc[1].mMemory == IM_LOCAL && mSrc[1].mVarIndex >= 0) + { + if (!requiredVars.RangeClear(localVars[mSrc[1].mVarIndex]->mByteIndex + int(mSrc[1].mIntConst), InterTypeSize[mSrc[0].mType])) + { + requiredVars.SubRange(localVars[mSrc[1].mVarIndex]->mByteIndex + int(mSrc[1].mIntConst), InterTypeSize[mSrc[0].mType]); + } + else if (!mVolatile) + { + mNumOperands = 0; + mSrc[0].mTemp = -1; + mCode = IC_NONE; + changed = true; + } + } + } + else if (mCode == IC_COPY || mCode == IC_STRCPY) + { + if (mSrc[0].mMemory == IM_LOCAL) + { + requiredVars.AddRange(localVars[mSrc[0].mVarIndex]->mByteIndex + int(mSrc[0].mIntConst), mConst.mOperandSize); + } + else if (mSrc[0].mMemory == IM_INDIRECT) + { + for (int i = 0; i < localVars.Size(); i++) + if (localVars[i] && localVars[i]->mAliased) + requiredVars.AddRange(localVars[i]->mByteIndex, localVars[i]->mSize); + } + } + else if (mCode == IC_FILL) + { + } + else if (mCode == IC_CALL || mCode == IC_CALL_NATIVE || mCode == IC_ASSEMBLER || mCode == IC_DISPATCH) + { + requiredVars.Fill(); + } + + return changed; +} + +bool InterInstruction::RemoveUnusedStaticStoreByteInstructions(InterCodeBasicBlock* block, const GrowingVariableArray& staticVars, NumberSet& requiredVars) +{ + bool changed = false; + + if (mCode == IC_LOAD) + { + if (mSrc[0].mMemory == IM_INDIRECT) + { + if (!mSrc[0].mRestricted) + { + for (int i = 0; i < staticVars.Size(); i++) + { + if (staticVars[i]->mAliased) + requiredVars.AddRange(staticVars[i]->mByteIndex, staticVars[i]->mSize); + } + } + } + else if (mSrc[0].mMemory == IM_GLOBAL) + { + if (mSrc[0].mVarIndex >= 0) + requiredVars.AddRange(staticVars[mSrc[0].mVarIndex]->mByteIndex + int(mSrc[0].mIntConst), InterTypeSize[mDst.mType]); + } + } + else if (mCode == IC_STORE) + { + if (mSrc[1].mMemory == IM_GLOBAL && mSrc[1].mVarIndex >= 0) + { + if (!requiredVars.RangeClear(staticVars[mSrc[1].mVarIndex]->mByteIndex + int(mSrc[1].mIntConst), InterTypeSize[mSrc[0].mType])) + { + requiredVars.SubRange(staticVars[mSrc[1].mVarIndex]->mByteIndex + int(mSrc[1].mIntConst), InterTypeSize[mSrc[0].mType]); + } + else if (!mVolatile) + { + mNumOperands = 0; + mSrc[0].mTemp = -1; + mCode = IC_NONE; + changed = true; + } + } + } + else if (mCode == IC_COPY || mCode == IC_STRCPY || mCode == IC_FILL) + { + requiredVars.Fill(); + } + else if (mCode == IC_CALL || mCode == IC_CALL_NATIVE || mCode == IC_ASSEMBLER || mCode == IC_RETURN || mCode == IC_RETURN_STRUCT || mCode == IC_RETURN_VALUE || mCode == IC_DISPATCH) + { + requiredVars.Fill(); + } + + return changed; +} + +int InterInstruction::NumUsedTemps(void) const +{ + int n = 0; + for (int i = 0; i < mNumOperands; i++) + if (mSrc[i].mTemp >= 0) + n++; + return n; +} + +bool InterInstruction::UsesTemp(int temp) const +{ + for (int i = 0; i < mNumOperands; i++) + if (mSrc[i].mTemp == temp) + return true; + return false; +} + +static void DestroySourceValues(int temp, GrowingInstructionPtrArray& tvalue, FastNumberSet& tvalid) +{ + int i, j; + const InterInstruction* ins; + + if (temp >= 0) + { + i = 0; + while (i < tvalid.Num()) + { + j = tvalid.Element(i); + + ins = tvalue.getAt(j); + + if (ins->UsesTemp(temp)) + { + tvalue.destroyAt(j); + tvalid -= j; + } + else + i++; + } + } +} + +void InterInstruction::PerformValueForwarding(GrowingInstructionPtrArray& tvalue, FastNumberSet& tvalid) +{ + DestroySourceValues(mDst.mTemp, tvalue, tvalid); + + if (mCode == IC_LOAD_TEMPORARY) + { + if (tvalue[mSrc[0].mTemp]) + { + tvalue[mDst.mTemp] = tvalue[mSrc[0].mTemp]; + tvalid += mDst.mTemp; + } + } + else + { + if (mDst.mTemp >= 0) + { + tvalue[mDst.mTemp] = this; + tvalid += mDst.mTemp; + } + } +} + +void InterInstruction::LocalRenameRegister(GrowingIntArray& renameTable, int& num) +{ + for (int i = 0; i < mNumOperands; i++) + if (mSrc[i].mTemp >= 0) mSrc[i].mTemp = renameTable[mSrc[i].mTemp]; + + if (mDst.mTemp >= 0) + { + renameTable[mDst.mTemp] = num; + mDst.mTemp = num++; + } +#if 0 + if (mCode == IC_LOAD_TEMPORARY && mSrc[0].mTemp < 0) + mCode = IC_CONSTANT; +#endif +} + +void InterInstruction::GlobalRenameRegister(const GrowingIntArray& renameTable, GrowingTypeArray& temporaries) +{ + for (int i = 0; i < mNumOperands; i++) + if (mSrc[i].mTemp >= 0) mSrc[i].mTemp = renameTable[mSrc[i].mTemp]; + + if (mDst.mTemp >= 0) + { + mDst.mTemp = renameTable[mDst.mTemp]; + if (InterTypeSize[mDst.mType] > InterTypeSize[temporaries[mDst.mTemp]]) + temporaries[mDst.mTemp] = mDst.mType; + } +#if 0 + if (mCode == IC_LOAD_TEMPORARY && mSrc[0].mTemp < 0) + mCode = IC_CONSTANT; +#endif +} + +static void UpdateCollisionSet(NumberSet& liveTemps, NumberSet* collisionSets, int temp) +{ + int i; + + if (temp >= 0 && !liveTemps[temp]) + { + for (i = 0; i < liveTemps.Size(); i++) + { + if (liveTemps[i]) + { + collisionSets[i] += temp; + collisionSets[temp] += i; + } + } + + liveTemps += temp; + } +} + +void InterInstruction::BuildCollisionTable(NumberSet& liveTemps, NumberSet* collisionSets) +{ + if (mDst.mTemp >= 0) + { + // Ensure collision with unused destination register + UpdateCollisionSet(liveTemps, collisionSets, mDst.mTemp); + // if (!liveTemps[ttemp]) __asm int 3 + liveTemps -= mDst.mTemp; + } + + for (int i = 0; i < mNumOperands; i++) + UpdateCollisionSet(liveTemps, collisionSets, mSrc[i].mTemp); +} + +void InterInstruction::ReduceTemporaries(const GrowingIntArray& renameTable, GrowingTypeArray& temporaries) +{ + for (int i = 0; i < mNumOperands; i++) + if (mSrc[i].mTemp >= 0) mSrc[i].mTemp = renameTable[mSrc[i].mTemp]; + + if (mDst.mTemp >= 0) + { + mDst.mTemp = renameTable[mDst.mTemp]; + temporaries[mDst.mTemp] = mDst.mType; + } +} + + +void InterInstruction::CollectActiveTemporaries(FastNumberSet& set) +{ + if (mDst.mTemp >= 0) set += mDst.mTemp; + for (int i = 0; i < mNumOperands; i++) + if (mSrc[i].mTemp >= 0) set += mSrc[i].mTemp; +} + +void InterInstruction::ShrinkActiveTemporaries(FastNumberSet& set, GrowingTypeArray& temporaries) +{ + if (mDst.mTemp >= 0) + { + mDst.mTemp = set.Index(mDst.mTemp); + temporaries[mDst.mTemp] = mDst.mType; + } + for (int i = 0; i < mNumOperands; i++) + if (mSrc[i].mTemp >= 0) mSrc[i].mTemp = set.Index(mSrc[i].mTemp); +} + +void InterInstruction::CollectSimpleLocals(FastNumberSet& complexLocals, FastNumberSet& simpleLocals, GrowingTypeArray& localTypes, FastNumberSet& complexParams, FastNumberSet& simpleParams, GrowingTypeArray& paramTypes) +{ + switch (mCode) + { + case IC_LOAD: + if (mSrc[0].mMemory == IM_LOCAL && mSrc[0].mTemp < 0) + { + if ((localTypes[mSrc[0].mVarIndex] == IT_NONE || localTypes[mSrc[0].mVarIndex] == mDst.mType) && mSrc[0].mIntConst == 0) + { + localTypes[mSrc[0].mVarIndex] = mDst.mType; + simpleLocals += mSrc[0].mVarIndex; + } + else + complexLocals += mSrc[0].mVarIndex; + } + else if ((mSrc[0].mMemory == IM_PARAM || mSrc[0].mMemory == IM_FPARAM) && mSrc[0].mTemp < 0) + { + if ((paramTypes[mSrc[0].mVarIndex] == IT_NONE || paramTypes[mSrc[0].mVarIndex] == mDst.mType) && mSrc[0].mIntConst == 0) + { + paramTypes[mSrc[0].mVarIndex] = mDst.mType; + simpleParams += mSrc[0].mVarIndex; + } + else + complexParams += mSrc[0].mVarIndex; + } + break; + case IC_STORE: + if (mSrc[1].mMemory == IM_LOCAL && mSrc[1].mTemp < 0) + { + if ((localTypes[mSrc[1].mVarIndex] == IT_NONE || localTypes[mSrc[1].mVarIndex] == mSrc[0].mType) && mSrc[1].mIntConst == 0) + { + localTypes[mSrc[1].mVarIndex] = mSrc[0].mType; + simpleLocals += mSrc[1].mVarIndex; + } + else + complexLocals += mSrc[1].mVarIndex; + } + else if ((mSrc[1].mMemory == IM_PARAM || mSrc[1].mMemory == IM_FPARAM) && mSrc[1].mTemp < 0) + { + if ((paramTypes[mSrc[1].mVarIndex] == IT_NONE || paramTypes[mSrc[1].mVarIndex] == mSrc[0].mType) && mSrc[1].mIntConst == 0) + { + paramTypes[mSrc[1].mVarIndex] = mSrc[0].mType; + simpleParams += mSrc[1].mVarIndex; + } + else + complexParams += mSrc[1].mVarIndex; + } + break; + case IC_LEA: + if (mSrc[1].mMemory == IM_LOCAL && mSrc[1].mTemp < 0) + complexLocals += mSrc[1].mVarIndex; + else if ((mSrc[1].mMemory == IM_PARAM || mSrc[1].mMemory == IM_FPARAM) && mSrc[1].mTemp < 0) + complexParams += mSrc[1].mVarIndex; + break; + case IC_COPY: + if (mSrc[1].mMemory == IM_LOCAL && mSrc[1].mTemp < 0) + complexLocals += mSrc[1].mVarIndex; + else if ((mSrc[1].mMemory == IM_PARAM || mSrc[1].mMemory == IM_FPARAM) && mSrc[1].mTemp < 0) + complexParams += mSrc[1].mVarIndex; + if (mSrc[0].mMemory == IM_LOCAL && mSrc[0].mTemp < 0) + complexLocals += mSrc[0].mVarIndex; + else if ((mSrc[0].mMemory == IM_PARAM || mSrc[0].mMemory == IM_FPARAM) && mSrc[0].mTemp < 0) + complexParams += mSrc[0].mVarIndex; + break; + case IC_FILL: + if (mSrc[1].mMemory == IM_LOCAL && mSrc[1].mTemp < 0) + complexLocals += mSrc[1].mVarIndex; + else if ((mSrc[1].mMemory == IM_PARAM || mSrc[1].mMemory == IM_FPARAM) && mSrc[1].mTemp < 0) + complexParams += mSrc[1].mVarIndex; + break; + case IC_CONSTANT: + if (mDst.mType == IT_POINTER && mConst.mMemory == IM_LOCAL) + complexLocals += mConst.mVarIndex; + else if (mDst.mType == IT_POINTER && (mConst.mMemory == IM_PARAM || mConst.mMemory == IM_FPARAM)) + complexParams += mConst.mVarIndex; + break; + } +} + +void InterInstruction::UnionRanges(InterInstruction* ins) +{ + mDst.mRange.Union(ins->mDst.mRange); + for(int i=0; imSrc[i].mRange); +} + +void InterInstruction::SimpleLocalToTemp(int vindex, int temp) +{ + switch (mCode) + { + case IC_LOAD: + if (mSrc[0].mMemory == IM_LOCAL && mSrc[0].mTemp < 0 && vindex == mSrc[0].mVarIndex) + { + mCode = IC_LOAD_TEMPORARY; + mSrc[0].mTemp = temp; + mSrc[0].mType = mDst.mType; + mNumOperands = 1; + + assert(mSrc[0].mTemp >= 0); + + } + break; + case IC_STORE: + if (mSrc[1].mMemory == IM_LOCAL && mSrc[1].mTemp < 0 && vindex == mSrc[1].mVarIndex) + { + if (mSrc[0].mTemp < 0) + { + mCode = IC_CONSTANT; + mConst.mType = mSrc[0].mType; + mConst.mIntConst = mSrc[0].mIntConst; + mConst.mFloatConst = mSrc[0].mFloatConst; + mNumOperands = 0; + } + else + { + mCode = IC_LOAD_TEMPORARY; + mNumOperands = 1; + assert(mSrc[0].mTemp >= 0); + } + + mDst.mTemp = temp; + mDst.mType = mSrc[0].mType; + } + break; + } +} + +bool InterInstruction::ConstantFolding(void) +{ + switch (mCode) + { + case IC_RELATIONAL_OPERATOR: + if (mSrc[0].mTemp < 0 && mSrc[1].mTemp < 0) + { + mCode = IC_CONSTANT; + if (mSrc[0].mType == IT_POINTER) + mConst.mIntConst = ::ConstantRelationalPointerFolding(mOperator, mSrc[1], mSrc[0]); + else if (IsIntegerType(mSrc[0].mType) || mSrc[0].mType == IT_BOOL) + mConst.mIntConst = ::ConstantFolding(mOperator, mSrc[0].mType, mSrc[1].mIntConst, mSrc[0].mIntConst); + else + mConst.mIntConst = ConstantRelationalFolding(mOperator, mSrc[1].mFloatConst, mSrc[0].mFloatConst); + mConst.mType = IT_BOOL; + mNumOperands = 0; + return true; + } + else if (mSrc[0].mTemp == mSrc[1].mTemp) + { + mCode = IC_CONSTANT; + mConst.mIntConst = ::ConstantFolding(mOperator, mSrc[0].mType, 0, 0); + mConst.mType = IT_BOOL; + mSrc[0].mTemp = -1; + mSrc[1].mTemp = -1; + mNumOperands = 0; + return true; + } + break; + case IC_BINARY_OPERATOR: + if (mSrc[0].mTemp < 0 && mSrc[1].mTemp < 0) + { + mCode = IC_CONSTANT; + if (mDst.mType == IT_FLOAT) + mConst.mFloatConst = ::ConstantFolding(mOperator, mSrc[1].mFloatConst, mSrc[0].mFloatConst); + else + mConst.mIntConst = ::ConstantFolding(mOperator, mDst.mType, mSrc[1].mIntConst, mSrc[0].mIntConst); + mConst.mType = mDst.mType; + mNumOperands = 0; + return true; + } +#if 1 + else if (mSrc[0].mTemp < 0) + { + if (mDst.mType == IT_FLOAT) + { + + } + else if (IsIntegerType(mDst.mType)) + { + if ((mOperator == IA_ADD || mOperator == IA_SUB || mOperator == IA_OR || mOperator == IA_XOR || mOperator == IA_SHL || mOperator == IA_SHR || mOperator == IA_SAR) && mSrc[0].mIntConst == 0 || + (mOperator == IA_MUL || mOperator == IA_DIVS || mOperator == IA_DIVU) && mSrc[0].mIntConst == 1) + { + mCode = IC_LOAD_TEMPORARY; + mSrc[0] = mSrc[1]; + mSrc[1].mTemp = -1; + mNumOperands = 1; + assert(mSrc[0].mTemp >= 0); + return true; + } + else if (mOperator == IA_MODU && (mSrc[0].mIntConst & (mSrc[0].mIntConst - 1)) == 0) + { + mOperator = IA_AND; + mSrc[0].mIntConst--; + return true; + } + else if (mOperator == IA_DIVU && (mSrc[0].mIntConst & (mSrc[0].mIntConst - 1)) == 0) + { + int n = 0; + while (mSrc[0].mIntConst > 1) + { + n++; + mSrc[0].mIntConst >>= 1; + } + mOperator = IA_SHR; + mSrc[0].mIntConst = n; + return true; + } + } + } + else if (mSrc[1].mTemp < 0) + { + if (mDst.mType == IT_FLOAT) + { + + } + else if (IsIntegerType(mDst.mType)) + { + if ((mOperator == IA_ADD || mOperator == IA_OR || mOperator == IA_XOR) && mSrc[1].mIntConst == 0 || (mOperator == IA_MUL) && mSrc[1].mIntConst == 1) + { + mCode = IC_LOAD_TEMPORARY; + mSrc[1].mTemp = -1; + mNumOperands = 1; + assert(mSrc[0].mTemp >= 0); + return true; + } + else if ((mOperator == IA_AND || mOperator == IA_MUL || mOperator == IA_SHL || mOperator == IA_SHR || mOperator == IA_SAR) && mSrc[1].mIntConst == 0) + { + mCode = IC_CONSTANT; + mConst.mIntConst = 0; + mConst.mType = mDst.mType; + mNumOperands = 0; + return true; + } + } + } +#endif + break; + case IC_UNARY_OPERATOR: + if (mSrc[0].mTemp < 0) + { + mCode = IC_CONSTANT; + if (mDst.mType == IT_FLOAT) + mConst.mFloatConst = ::ConstantFolding(mOperator, mSrc[0].mFloatConst); + else + mConst.mIntConst = ::ConstantFolding(mOperator, mDst.mType, mSrc[0].mIntConst); + mConst.mType = mDst.mType; + mNumOperands = 0; + return true; + } + break; + case IC_CONVERSION_OPERATOR: + if (mSrc[0].mTemp < 0) + { + ConversionConstantFold(this, mSrc[0]); + return true; + } + break; + case IC_TYPECAST: + if (mSrc[0].mTemp < 0) + { + if (mDst.mType == IT_POINTER && IsIntegerType(mSrc[0].mType)) + { + mCode = IC_CONSTANT; + mConst.mType = IT_POINTER; + mConst.mMemory = IM_ABSOLUTE; + mConst.mIntConst = mSrc[0].mIntConst; + mConst.mLinkerObject = nullptr; + mConst.mOperandSize = 2; + mNumOperands = 0; + return true; + } + } + break; + case IC_LOAD_TEMPORARY: + if (mDst.mTemp == mSrc[0].mTemp) + { + mCode = IC_NONE; + mDst.mTemp = -1; + for (int i = 0; i < mNumOperands; i++) + mSrc[i].mTemp = -1; + mNumOperands = 0; + return true; + } + break; + case IC_LEA: + if (mSrc[0].mTemp < 0 && mSrc[1].mTemp < 0) + { + mCode = IC_CONSTANT; + mConst = mSrc[1]; + mConst.mIntConst += mSrc[0].mIntConst; + mConst.mRange.Reset(); + mNumOperands = 0; + return true; + } + else if (mSrc[0].mTemp < 0 && mSrc[0].mIntConst == 0 && mSrc[1].mIntConst == 0) + { + mCode = IC_LOAD_TEMPORARY; + mSrc[0] = mSrc[1]; + mSrc[1].mTemp = -1; + mNumOperands = 1; + assert(mSrc[0].mTemp >= 0); + return true; + } + break; + case IC_BRANCH: + if (mSrc[0].mTemp < 0) + { + if (IsIntegerType(mSrc[0].mType)) + mSrc[0].mIntConst = mSrc[0].mIntConst != 0; + else if (mSrc[0].mType == IT_FLOAT) + mSrc[0].mIntConst = mSrc[0].mFloatConst != 0; + else if (mSrc[0].mType == IT_POINTER) + { + if (mSrc[0].mMemory == IM_ABSOLUTE) + mSrc[0].mIntConst = mSrc[0].mIntConst != 0; + else + mSrc[0].mIntConst = 1; + } + mSrc[0].mType = IT_BOOL; + return true; + } + break; + case IC_SELECT: + if (mSrc[2].mTemp < 0) + { + bool cond = mSrc[2].mIntConst != 0; + + if (IsIntegerType(mSrc[2].mType)) + cond = mSrc[2].mIntConst != 0; + else if (mSrc[2].mType == IT_FLOAT) + cond = mSrc[2].mFloatConst != 0; + else if (mSrc[2].mType == IT_POINTER) + { + if (mSrc[2].mMemory == IM_ABSOLUTE) + cond = mSrc[2].mIntConst != 0; + else + cond = true; + } + + int ci = cond ? 1 : 0; + if (mSrc[ci].mTemp < 0) + { + mCode = IC_CONSTANT; + mConst = mSrc[ci]; + } + else + { + mCode = IC_LOAD_TEMPORARY; + mSrc[0] = mSrc[ci]; + } + mNumOperands = 1; + return true; + } + break; + } + + return false; +} + + +void InterOperand::Disassemble(FILE* file, InterCodeProcedure* proc) +{ + static char typechars[] = "NBCILFP"; + + if (mTemp >= 0) + { + if (mFinal) + fprintf(file, "R%d(%cF)", mTemp, typechars[mType]); + else + fprintf(file, "R%d(%c)", mTemp, typechars[mType]); + + if (mType == IT_POINTER && mMemory == IM_INDIRECT) + { + fprintf(file, "+%d", int(mIntConst)); + } + + if (mRestricted) + fprintf(file, "{R%d}", mRestricted); + else if (mMemory == IM_INDIRECT && mMemoryBase != IM_INDIRECT && mMemoryBase != IM_NONE) + { + const char* vname = ""; + bool aliased = false; + + if (mMemoryBase == IM_LOCAL) + { + if (!proc->mLocalVars[mVarIndex]) + vname = "null"; + else if (!proc->mLocalVars[mVarIndex]->mIdent) + { + vname = ""; + aliased = proc->mLocalVars[mVarIndex]->mAliased; + } + else + { + vname = proc->mLocalVars[mVarIndex]->mIdent->mString; + aliased = proc->mLocalVars[mVarIndex]->mAliased; + } + } + else if (mMemoryBase == IM_PROCEDURE) + { + if (mVarIndex >= 0 && proc->mModule->mProcedures[mVarIndex]) + vname = proc->mModule->mProcedures[mVarIndex]->mIdent->mString; + else if (mLinkerObject && mLinkerObject->mIdent) + vname = mLinkerObject->mIdent->mString; + } + else if (mMemoryBase == IM_GLOBAL) + { + if (mVarIndex < 0) + { + if (mLinkerObject && mLinkerObject->mIdent) + vname = mLinkerObject->mIdent->mString; + else + vname = ""; + } + else if (!proc->mModule->mGlobalVars[mVarIndex]) + vname = "null"; + else if (!proc->mModule->mGlobalVars[mVarIndex]->mIdent) + { + vname = ""; + aliased = proc->mModule->mGlobalVars[mVarIndex]->mAliased; + } + else + { + vname = proc->mModule->mGlobalVars[mVarIndex]->mIdent->mString; + aliased = proc->mModule->mGlobalVars[mVarIndex]->mAliased; + } + } + else if (mMemoryBase == IM_ABSOLUTE) + { + vname = "ABS"; + } + + if (aliased) + fprintf(file, " {V(%d '%s' A)} ", mVarIndex, vname); + else + fprintf(file, " {V(%d '%s')} ", mVarIndex, vname); + } + + if (mRange.mMinState >= IntegerValueRange::S_WEAK || mRange.mMaxState >= IntegerValueRange::S_WEAK) + { + fprintf(file, "["); + if (mRange.mMinState == IntegerValueRange::S_WEAK) + fprintf(file, "~%lld", mRange.mMinValue); + else if (mRange.mMinState == IntegerValueRange::S_BOUND) + fprintf(file, "%lld", mRange.mMinValue); + else if (mRange.mMinState == IntegerValueRange::S_UNKNOWN) + fprintf(file, "?"); + fprintf(file, ".."); + if (mRange.mMaxState == IntegerValueRange::S_WEAK) + fprintf(file, "~%lld", mRange.mMaxValue); + else if (mRange.mMaxState == IntegerValueRange::S_BOUND) + fprintf(file, "%lld", mRange.mMaxValue); + else if (mRange.mMaxState == IntegerValueRange::S_UNKNOWN) + fprintf(file, "?"); + fprintf(file, "]"); + } + } + else if (mType == IT_POINTER) + { + const char* vname = ""; + bool aliased = false; + + if (mMemory == IM_LOCAL) + { + if (!proc->mLocalVars[mVarIndex]) + vname = "null"; + else if (!proc->mLocalVars[mVarIndex]->mIdent) + { + vname = ""; + aliased = proc->mLocalVars[mVarIndex]->mAliased; + } + else + { + vname = proc->mLocalVars[mVarIndex]->mIdent->mString; + aliased = proc->mLocalVars[mVarIndex]->mAliased; + } + } + else if (mMemory == IM_PROCEDURE) + { + if (mVarIndex >= 0 && proc->mModule->mProcedures[mVarIndex]) + vname = proc->mModule->mProcedures[mVarIndex]->mIdent->mString; + else if (mLinkerObject && mLinkerObject->mIdent) + vname = mLinkerObject->mIdent->mString; + } + else if (mMemory == IM_GLOBAL) + { + if (mVarIndex < 0) + { + if (mLinkerObject && mLinkerObject->mIdent) + vname = mLinkerObject->mIdent->mString; + else + vname = ""; + } + else if (!proc->mModule->mGlobalVars[mVarIndex]) + vname = "null"; + else if (!proc->mModule->mGlobalVars[mVarIndex]->mIdent) + { + vname = ""; + aliased = proc->mModule->mGlobalVars[mVarIndex]->mAliased; + } + else + { + vname = proc->mModule->mGlobalVars[mVarIndex]->mIdent->mString; + aliased = proc->mModule->mGlobalVars[mVarIndex]->mAliased; + } + } + + if (aliased) + fprintf(file, "V(%d '%s' A)+%d ", mVarIndex, vname, int(mIntConst)); + else + fprintf(file, "V(%d '%s')+%d ", mVarIndex, vname, int(mIntConst)); + } + else if (IsIntegerType(mType) || mType == IT_BOOL) + { + fprintf(file, "C%c:%lld", typechars[mType], mIntConst); + } + else if (mType == IT_FLOAT) + { + union { float f; uint32 u; } u; + u.f = (float)mFloatConst; + fprintf(file, "C%c:%f (%08x)", typechars[mType], mFloatConst, u.u); + } +} + +void InterInstruction::Disassemble(FILE* file, InterCodeProcedure* proc) +{ + if (this->mCode != IC_NONE) + { + static char memchars[] = "NPLGFPITAZC"; + + fprintf(file, "\t"); + switch (this->mCode) + { + case IC_LOAD_TEMPORARY: + assert(mNumOperands == 1); + fprintf(file, "MOVE"); + break; + case IC_BINARY_OPERATOR: + assert(mNumOperands == 2); + fprintf(file, "BINOP%d", mOperator); + break; + case IC_UNARY_OPERATOR: + assert(mNumOperands == 1); + fprintf(file, "UNOP%d", mOperator); + break; + case IC_RELATIONAL_OPERATOR: + assert(mNumOperands == 2); + fprintf(file, "RELOP%d", mOperator); + break; + case IC_CONVERSION_OPERATOR: + assert(mNumOperands == 1); + fprintf(file, "CONV%d", mOperator); + break; + case IC_STORE: + assert(mNumOperands == 2); + if (mSrc[1].mStride != 1) + fprintf(file, "STORE%c%d:%d", memchars[mSrc[1].mMemory], mSrc[1].mOperandSize, mSrc[1].mStride); + else + fprintf(file, "STORE%c%d", memchars[mSrc[1].mMemory], mSrc[1].mOperandSize); + break; + case IC_LOAD: + assert(mNumOperands == 1); + if (mSrc[0].mStride != 1) + fprintf(file, "LOAD%c%d:%d", memchars[mSrc[0].mMemory], mSrc[0].mOperandSize, mSrc[0].mStride); + else + fprintf(file, "LOAD%c%d", memchars[mSrc[0].mMemory], mSrc[0].mOperandSize); + break; + case IC_COPY: + if (mSrc[1].mStride != 1) + { + if (mSrc[0].mStride != 1) + fprintf(file, "COPY%d:%c%d%c%d", mConst.mOperandSize, memchars[mSrc[0].mMemory], mSrc[0].mStride, memchars[mSrc[1].mMemory], mSrc[1].mStride); + else + fprintf(file, "COPY%d:%c%c%d", mConst.mOperandSize, memchars[mSrc[0].mMemory], memchars[mSrc[1].mMemory], mSrc[1].mStride); + } + else if (mSrc[0].mStride != 1) + fprintf(file, "COPY%d:%c%d%c", mConst.mOperandSize, memchars[mSrc[0].mMemory], mSrc[0].mStride, memchars[mSrc[1].mMemory]); + else + fprintf(file, "COPY%d:%c%c", mConst.mOperandSize, memchars[mSrc[0].mMemory], memchars[mSrc[1].mMemory]); + break; + case IC_FILL: + assert(mNumOperands == 2); + if (mSrc[1].mStride != 1) + fprintf(file, "FILL%c%d:%d", memchars[mSrc[1].mMemory], mConst.mOperandSize, mSrc[1].mStride); + else + fprintf(file, "FILL%c%d", memchars[mSrc[1].mMemory], mConst.mOperandSize); + break; + + case IC_MALLOC: + assert(mNumOperands == 1); + fprintf(file, "MALLOC"); + break; + case IC_FREE: + assert(mNumOperands == 1); + fprintf(file, "FREE"); + break; + + case IC_STRCPY: + fprintf(file, "STRCPY%c%c", memchars[mSrc[0].mMemory], memchars[mSrc[1].mMemory]); + break; + case IC_LEA: + assert(mNumOperands == 2); + fprintf(file, "LEA%c", memchars[mSrc[1].mMemory]); + break; + case IC_TYPECAST: + assert(mNumOperands == 1); + fprintf(file, "CAST"); + break; + case IC_SELECT: + assert(mNumOperands == 3); + fprintf(file, "SELECT"); + break; + case IC_CONSTANT: + assert(mNumOperands == 0); + fprintf(file, "CONST"); + break; + case IC_BRANCH: + assert(mNumOperands == 1); + fprintf(file, "BRANCH"); + break; + case IC_JUMP: + assert(mNumOperands == 0); + fprintf(file, "JUMP"); + break; + case IC_JUMPF: + assert(mNumOperands == 0); + fprintf(file, "JUMPF"); + break; + case IC_PUSH_FRAME: + fprintf(file, "PUSHF\t%d", int(mConst.mIntConst)); + break; + case IC_POP_FRAME: + fprintf(file, "POPF\t%d", int(mConst.mIntConst)); + break; + case IC_CALL: + fprintf(file, "CALL"); + break; + case IC_CALL_NATIVE: + fprintf(file, "CALLN"); + break; + case IC_ASSEMBLER: + fprintf(file, "JSR"); + break; + case IC_DISPATCH: + fprintf(file, "DISPATCH"); + break; + case IC_RETURN_VALUE: + assert(mNumOperands == 1); + fprintf(file, "RETV"); + break; + case IC_RETURN_STRUCT: + assert(mNumOperands == 1); + fprintf(file, "RETS"); + break; + case IC_RETURN: + fprintf(file, "RET"); + break; + case IC_UNREACHABLE: + fprintf(file, "UNREACHABLE"); + break; + case IC_BREAKPOINT: + fprintf(file, "BREAKPOINT"); + break; + } + static char typechars[] = "NBCILFP"; + + fprintf(file, "\t"); + if (mDst.mTemp >= 0) + mDst.Disassemble(file, proc); + fprintf(file, "\t<-\t"); + + + if (this->mCode == IC_CONSTANT) + { + if (mConst.mType == IT_NONE) + fprintf(file, "?"); + + if (mDst.mType == IT_POINTER) + { + const char* vname = ""; + bool aliased = false; + + if (mConst.mMemory == IM_LOCAL || mConst.mMemoryBase == IM_LOCAL) + { + if (mConst.mVarIndex < 0 || !proc->mLocalVars[mConst.mVarIndex]) + vname = "null"; + else if (!proc->mLocalVars[mConst.mVarIndex]->mIdent) + vname = ""; + else + vname = proc->mLocalVars[mConst.mVarIndex]->mIdent->mString; + } + else if (mConst.mMemory == IM_PROCEDURE) + { + if (mConst.mVarIndex >= 0 && proc->mModule->mProcedures[mConst.mVarIndex]) + vname = proc->mModule->mProcedures[mConst.mVarIndex]->mIdent->mString; + else if (mConst.mLinkerObject && mConst.mLinkerObject->mIdent) + vname = mConst.mLinkerObject->mIdent->mString; + } + else if (mConst.mMemory == IM_GLOBAL) + { + if (mConst.mVarIndex < 0) + { + if (mConst.mLinkerObject && mConst.mLinkerObject->mIdent) + vname = mConst.mLinkerObject->mIdent->mString; + else + vname = ""; + } + else if (!proc->mModule->mGlobalVars[mConst.mVarIndex]) + vname = "null"; + else if (!proc->mModule->mGlobalVars[mConst.mVarIndex]->mIdent) + { + vname = ""; + aliased = proc->mModule->mGlobalVars[mConst.mVarIndex]->mAliased; + } + else + { + vname = proc->mModule->mGlobalVars[mConst.mVarIndex]->mIdent->mString; + aliased = proc->mModule->mGlobalVars[mConst.mVarIndex]->mAliased; + } + } + + if (aliased) + fprintf(file, "C%c%d(%d:%d '%s' A)", memchars[mConst.mMemory], mConst.mOperandSize, mConst.mVarIndex, int(mConst.mIntConst), vname); + else + fprintf(file, "C%c%d(%d:%d '%s')", memchars[mConst.mMemory], mConst.mOperandSize, mConst.mVarIndex, int(mConst.mIntConst), vname); + } + else if (mDst.mType == IT_FLOAT) + fprintf(file, "CF:%f", mConst.mFloatConst); + else + { + fprintf(file, "CI:%lld", mConst.mIntConst); + } + } + else + { + bool first = true; + for (int i = 0; i < mNumOperands; i++) + { + int j = mNumOperands - i - 1; + if (!first) + fprintf(file, ", "); + if (mSrc[j].mType != IT_NONE) + { + mSrc[j].Disassemble(file, proc); + first = false; + } + } + } + + fprintf(file, "\t{"); + if (mInvariant) + fprintf(file, "I"); + if (mVolatile) + fprintf(file, "V"); + if (mMemmap) + fprintf(file, "M"); + if (mNoSideEffects) + fprintf(file, "E"); + if (mConstExpr) + fprintf(file, "C"); + if (mSingleAssignment) + fprintf(file, "S"); + if (mAliasing) + fprintf(file, "A"); + fprintf(file, "}\n"); + } +} + +InterCodeBasicBlock::InterCodeBasicBlock(InterCodeProcedure * proc) + : mProc(proc), + mInstructions(nullptr), mEntryRenameTable(-1), mExitRenameTable(-1), mMergeTValues(nullptr), mMergeAValues(nullptr), mTrueJump(nullptr), mFalseJump(nullptr), mLoopPrefix(nullptr), mDominator(nullptr), + mLoadStoreInstructions(nullptr), mMemoryValueSize(0), mEntryMemoryValueSize(0) +{ + mVisited = false; + mInPath = false; + mLoopHead = false; + mChecked = false; + mTraceIndex = -1; + mUnreachable = false; + mValueRangeValid = false; + + mIndex = proc->mNumBlocks++; + proc->mBlocks.Push(this); +} + +InterCodeBasicBlock::~InterCodeBasicBlock(void) +{ +} + +InterCodeBasicBlock* InterCodeBasicBlock::Clone(void) +{ + InterCodeBasicBlock* nblock = new InterCodeBasicBlock(mProc); + for (int i = 0; i < mInstructions.Size(); i++) + nblock->mInstructions.Push(mInstructions[i]->Clone()); + return nblock; +} + + +void InterCodeBasicBlock::Append(InterInstruction * code) +{ +#if _DEBUG + if (code->mCode == IC_BINARY_OPERATOR) + { + assert(code->mSrc[0].mTemp >= 0); + assert(code->mSrc[1].mTemp >= 0); + + assert(code->mSrc[1].mType != IT_POINTER); + } + if (code->mCode == IC_CONSTANT) + { + assert(code->mDst.mType == code->mConst.mType); + if (code->mDst.mType == IT_INT8) assert(code->mConst.mIntConst >= -128 && code->mConst.mIntConst <= 255); + if (code->mDst.mType == IT_INT16) assert(code->mConst.mIntConst >= -32768 && code->mConst.mIntConst <= 65535); + } + if (code->mCode == IC_CONSTANT && code->mConst.mType == IT_POINTER && code->mConst.mMemory == IM_GLOBAL && code->mConst.mVarIndex >= 0) + { + assert(code->mConst.mVarIndex < mProc->mModule->mGlobalVars.Size()); + assert(mProc->mModule->mGlobalVars[code->mConst.mVarIndex]); + } + if (code->mCode == IC_STORE) + { + assert(code->mSrc[1].mOperandSize > 0); + } + if (code->mCode == IC_LOAD_TEMPORARY) + { + assert(code->mSrc[0].mTemp >= 0); + } + if (code->mDst.mTemp >= 0) + assert(code->mDst.mType != IT_NONE); + for (int i = 0; i < code->mNumOperands; i++) + assert(code->mSrc[i].mType != IT_NONE); + + assert(!(code->mInUse)); +#endif + code->mInUse = true; + this->mInstructions.Push(code); +} + + +void InterCodeBasicBlock::AppendBeforeBranch(InterInstruction* code, bool loopindex) +{ + int ti = mInstructions.Size() - 1; + if (mInstructions[ti]->mCode == IC_BRANCH) + { + if (ti > 0 && mInstructions[ti - 1]->mDst.mTemp == mInstructions[ti]->mSrc[0].mTemp && loopindex || CanBypassUp(code, mInstructions[ti - 1])) + { + ti--; + if (ti > 0 && mInstructions[ti]->UsesTemp(mInstructions[ti - 1]->mDst.mTemp) && CanBypassUp(code, mInstructions[ti - 1])) + ti--; + } + } + + mInstructions.Insert(ti, code); +} + +const InterInstruction* InterCodeBasicBlock::FindByDst(int dst, int depth) const +{ + int n = mInstructions.Size() - 1; + while (n >= 0 && mInstructions[n]->mDst.mTemp != dst) + n--; + if (n >= 0) + return mInstructions[n]; + else if (depth < 10 && mEntryBlocks.Size() == 1) + return mEntryBlocks[0]->FindByDst(dst, depth + 1); + else + return nullptr; +} + +void InterCodeBasicBlock::Close(InterCodeBasicBlock* trueJump, InterCodeBasicBlock* falseJump) +{ + this->mTrueJump = trueJump; + this->mFalseJump = falseJump; + this->mNumEntries = 0; +} + + +void InterCodeBasicBlock::CollectEntryBlocks(InterCodeBasicBlock* from) +{ + if (from) + { + int i = 0; + while (i < mEntryBlocks.Size() && mEntryBlocks[i] != from) + i++; + if (i == mEntryBlocks.Size()) + mEntryBlocks.Push(from); + } + + if (!mVisited) + { + mVisited = true; + + if (mTrueJump) mTrueJump->CollectEntryBlocks(this); + if (mFalseJump) mFalseJump->CollectEntryBlocks(this); + } +} + +void InterCodeBasicBlock::BuildDominatorTree(InterCodeBasicBlock* from) +{ + if (from == this) + return; + else if (!mDominator) + { + assert(!from || mIndex != 0); + mDominator = from; + } + else if (from == mDominator) + return; + else + { + assert(mIndex != 0); + + GrowingInterCodeBasicBlockPtrArray d1(nullptr), d2(nullptr); + + InterCodeBasicBlock* b = mDominator; + while (b) + { + d1.Push(b); + b = b->mDominator; + } + b = from; + while (b) + { + d2.Push(b); + b = b->mDominator; + } + + b = nullptr; + while (d1.Size() > 0 && d2.Size() > 0 && d1.Last() == d2.Last()) + { + b = d1.Pop(); d2.Pop(); + } + + if (mDominator == b) + return; + + mDominator = b; + } + + if (mTrueJump) + mTrueJump->BuildDominatorTree(this); + if (mFalseJump) + mFalseJump->BuildDominatorTree(this); +} + +bool InterCodeBasicBlock::IsDominator(InterCodeBasicBlock* block) +{ + while (block) + { + if (block == this) + return true; + block = block->mDominator; + } + return false; +} + +void InterCodeBasicBlock::CollectEntries(void) +{ + if (mInPath) + { + mLoopDebug = true; + mLoopHead = true; + } + mNumEntries++; + if (!mVisited) + { + mVisited = true; + + mInPath = true; + if (mTrueJump) mTrueJump->CollectEntries(); + if (mFalseJump) mFalseJump->CollectEntries(); + mInPath = false; + } +} + +static bool IsInfiniteLoop(InterCodeBasicBlock* head, InterCodeBasicBlock* block) +{ + if (!block->mChecked) + { + if (block->mTrueJump && !block->mFalseJump) + { + if (block->mTrueJump == head) + return true; + + block->mChecked = true; + bool loop = IsInfiniteLoop(head, block->mTrueJump); + block->mChecked = false; + + return loop; + } + } + + return false; +} + +bool InterCodeBasicBlock::StripLoopHead(int size) +{ + bool changed = false; + + if (!mVisited) + { + if (mLoopHead && mFalseJump && mTrueJump != this && mFalseJump != this && mLoopPrefix && mInstructions.Size() < size) + { +// printf("StripA %s %d\n", mProc->mIdent->mString, mIndex); + + ExpandingArray lblocks; + if (CollectSingleEntryGenericLoop(lblocks)) + { + bool branched = false; + for (int i = 0; i < mEntryBlocks.Size(); i++) + if (mEntryBlocks[i]->mFalseJump) + branched = true; + + if (!branched && (!lblocks.Contains(mTrueJump) || !lblocks.Contains(mFalseJump))) + { +// printf("StripB %s %d\n", mProc->mIdent->mString, mIndex); + + mLoopPrefix->mInstructions.SetSize(0); + for (int i = 0; i < mInstructions.Size(); i++) + mLoopPrefix->mInstructions.Push(mInstructions[i]->Clone()); + + mLoopPrefix->mFalseJump = mFalseJump; + mLoopPrefix->mTrueJump = mTrueJump; + + mEntryBlocks.RemoveAll(mLoopPrefix); + mNumEntries--; + mLoopHead = false; + + mTrueJump->mEntryBlocks.Push(mLoopPrefix); + mTrueJump->mNumEntries++; + mFalseJump->mEntryBlocks.Push(mLoopPrefix); + mFalseJump->mNumEntries++; + + if (!lblocks.Contains(mTrueJump)) + { + mFalseJump->mLoopHead = true; + } + else if (!lblocks.Contains(mFalseJump)) + { + mTrueJump->mLoopHead = true; + } + + changed = true; + } + } + } + + mVisited = true; + + if (mTrueJump && mTrueJump->StripLoopHead(size)) + changed = true; + if (mFalseJump && mFalseJump->StripLoopHead(size)) + changed = true; + } + + return changed; +} + +void InterCodeBasicBlock::GenerateTraces(int expand, bool compact) +{ + if (mInPath) + mLoopHead = true; + + assert(mIndex != 0 || !mLoopHead); + + if (!mVisited) + { + mVisited = true; + mInPath = true; + + // Limit number of contractions + for (int i = 0; i < 100; i++) + { + int sz = mInstructions.Size(); + + if (mFalseJump && sz > 0 && mInstructions[sz - 1]->mCode == IC_BRANCH && (mInstructions[sz - 1]->mSrc[0].mType == IT_BOOL || IsIntegerType(mInstructions[sz - 1]->mSrc[0].mType)) && mInstructions[sz - 1]->mSrc[0].mTemp < 0) + { + mInstructions[sz - 1]->mCode = IC_JUMP; + mInstructions[sz - 1]->mNumOperands = 0; + if (!mInstructions[sz - 1]->mSrc[0].mIntConst) + { + mTrueJump->mNumEntries--; + mTrueJump = mFalseJump; + } + else + mFalseJump->mNumEntries--; + mFalseJump = nullptr; + } +#if 1 + if (mFalseJump && sz > 0 && mInstructions[sz - 1]->mCode == IC_BRANCH && mTrueJump && mTrueJump == mFalseJump) + { + mInstructions[sz - 1]->mCode = IC_JUMP; + mInstructions[sz - 1]->mNumOperands = 0; + mFalseJump->mNumEntries--; + mFalseJump = nullptr; + } +#endif + if (mTrueJump && mTrueJump->mInstructions.Size() == 1 && mTrueJump->mInstructions[0]->mCode == IC_JUMP && !mTrueJump->mLoopHead && mTrueJump->mTraceIndex != mIndex) + { + mTrueJump->mTraceIndex = mIndex; + mTrueJump->mNumEntries--; + mTrueJump = mTrueJump->mTrueJump; + if (mTrueJump) + mTrueJump->mNumEntries++; + } + else if (mFalseJump && mFalseJump->mInstructions.Size() == 1 && mFalseJump->mInstructions[0]->mCode == IC_JUMP && !mFalseJump->mLoopHead && mFalseJump->mTraceIndex != mIndex) + { + mFalseJump->mTraceIndex = mIndex; + mFalseJump->mNumEntries--; + mFalseJump = mFalseJump->mTrueJump; + if (mFalseJump) + mFalseJump->mNumEntries++; + } + else if ( + mTrueJump && + mInstructions.Size() > 0 && + mInstructions.Last()->mCode == IC_BRANCH && + mTrueJump->mInstructions.Size() == 1 && + mTrueJump->mInstructions[0]->mCode == IC_BRANCH && + mTrueJump->mInstructions[0]->mSrc[0].mTemp == mInstructions.Last()->mSrc[0].mTemp) + { + mTrueJump->mNumEntries--; + mTrueJump = mTrueJump->mTrueJump; + if (mTrueJump) + mTrueJump->mNumEntries++; + } + else if ( + mFalseJump && + mInstructions.Size() > 0 && + mInstructions.Last()->mCode == IC_BRANCH && + mFalseJump->mInstructions.Size() == 1 && + mFalseJump->mInstructions[0]->mCode == IC_BRANCH && + mFalseJump->mInstructions[0]->mSrc[0].mTemp == mInstructions.Last()->mSrc[0].mTemp) + { + mFalseJump->mNumEntries--; + mFalseJump = mFalseJump->mFalseJump; + if (mFalseJump) + mFalseJump->mNumEntries++; + } + else if ( + compact && + mFalseJump && + mInstructions.Size() > 0 && + mInstructions.Last()->mCode == IC_BRANCH && + mInstructions.Last()->mSrc[0].mTemp < 0) + { + int ns = mInstructions.Size(); + + if (mInstructions.Last()->mSrc[0].mIntConst) + mFalseJump->mNumEntries--; + else + { + mTrueJump->mNumEntries--; + mTrueJump = mFalseJump; + } + + mFalseJump = nullptr; + mInstructions[ns - 1]->mCode = IC_JUMP; + mInstructions[ns - 1]->mNumOperands = 0; + } + else if (mTrueJump && !mFalseJump && ((mTrueJump->mInstructions.Size() < expand && mTrueJump->mInstructions.Size() > 1 && !mLoopHead) || mTrueJump->mNumEntries == 1) && !mTrueJump->mLoopHead && !IsInfiniteLoop(mTrueJump, mTrueJump)) + { +// if (mLoopDebug) +// printf("StripC %s %d %d\n", mProc->mIdent->mString, mTrueJump->mIndex, mTrueJump->mInstructions.Size()); + + mTrueJump->mNumEntries--; + int n = mTrueJump->mNumEntries; + + mInstructions.Pop(); + for (i = 0; i < mTrueJump->mInstructions.Size(); i++) + mInstructions.Push(mTrueJump->mInstructions[i]->Clone()); + + mFalseJump = mTrueJump->mFalseJump; + mTrueJump = mTrueJump->mTrueJump; + + if (n > 0) + { + if (mTrueJump) + mTrueJump->mNumEntries++; + if (mFalseJump) + mFalseJump->mNumEntries++; + } + } +#if 1 + else if (compact && mTrueJump && !mFalseJump && mTrueJump->mInstructions.Size() == 1 && mTrueJump->mInstructions[0]->mCode == IC_BRANCH && mTrueJump->mFalseJump) + { + InterCodeBasicBlock* tj = mTrueJump; + + int ns = mInstructions.Size(); + + tj->mNumEntries--; + tj->mTrueJump->mNumEntries++; + tj->mFalseJump->mNumEntries++; + + mInstructions[ns - 1]->mCode = IC_BRANCH; + mInstructions[ns - 1]->mOperator = tj->mInstructions[0]->mOperator; + mInstructions[ns - 1]->mSrc[0].Forward(tj->mInstructions[0]->mSrc[0]); + mInstructions[ns - 1]->mNumOperands = 1; + + mTrueJump = tj->mTrueJump; + mFalseJump = tj->mFalseJump; + } +#endif + else + break; + } + + if (mTrueJump) mTrueJump->GenerateTraces(expand, compact); + if (mFalseJump) mFalseJump->GenerateTraces(expand, compact); + + mInPath = false; + } +} + +bool InterCodeBasicBlock::MergeSameConditionTraces(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + if (mTrueJump && mFalseJump && !mTrueJump->mFalseJump && !mFalseJump->mFalseJump && mTrueJump->mTrueJump == mFalseJump->mTrueJump && mTrueJump->mNumEntries == 1 && mFalseJump->mNumEntries == 1) + { + // we have a diamond situation + InterCodeBasicBlock* mb0 = mTrueJump->mTrueJump; + if (mb0 && mb0->mNumEntries == 2) + { + if (mb0->mTrueJump && mb0->mFalseJump && !mb0->mTrueJump->mFalseJump && !mb0->mFalseJump->mFalseJump && mb0->mTrueJump->mTrueJump == mb0->mFalseJump->mTrueJump && mb0->mTrueJump->mNumEntries == 1 && mb0->mFalseJump->mNumEntries == 1) + { + // we have a dual diamond + InterCodeBasicBlock* mb1 = mb0->mTrueJump->mTrueJump; + if (mb1 && mb1->mNumEntries == 2 && mb0 != mb1) + { + int tc = mInstructions.Last()->mSrc[0].mTemp; + if (tc >= 0 && tc == mb0->mInstructions.Last()->mSrc[0].mTemp) + { + if (!mTrueJump->mLocalModifiedTemps[tc] && !mFalseJump->mLocalModifiedTemps[tc] && !mb0->mLocalModifiedTemps[tc]) + { + // Same conditions in both diamonds + if (mb0->mInstructions.Size() < 8) + { + // Join blocks + + mTrueJump->mInstructions.Remove(mTrueJump->mInstructions.Size() - 1); + mFalseJump->mInstructions.Remove(mFalseJump->mInstructions.Size() - 1); + + for (int i = 0; i + 1 < mb0->mInstructions.Size(); i++) + { + mTrueJump->mInstructions.Push(mb0->mInstructions[i]->Clone()); + mFalseJump->mInstructions.Push(mb0->mInstructions[i]->Clone()); + } + + for(int i=0; imTrueJump->mInstructions.Size(); i++) + mTrueJump->mInstructions.Push(mb0->mTrueJump->mInstructions[i]->Clone()); + for (int i = 0; i < mb0->mFalseJump->mInstructions.Size(); i++) + mFalseJump->mInstructions.Push(mb0->mFalseJump->mInstructions[i]->Clone()); + + mTrueJump->mLocalModifiedTemps |= mb0->mLocalModifiedTemps; + mFalseJump->mLocalModifiedTemps |= mb0->mLocalModifiedTemps; + mTrueJump->mLocalModifiedTemps |= mb0->mTrueJump->mLocalModifiedTemps; + mFalseJump->mLocalModifiedTemps |= mb0->mFalseJump->mLocalModifiedTemps; + + mTrueJump->mTrueJump = mb1; + mFalseJump->mTrueJump = mb1; + + changed = true; + } + } + } + } + + } + } + } + + if (mTrueJump && mTrueJump->MergeSameConditionTraces()) + changed = true; + if (mFalseJump && mFalseJump->MergeSameConditionTraces()) + changed = true; + } + + return changed; +} + +static void OptimizeAddress(InterInstruction * ins, const GrowingInstructionPtrArray& tvalue, int offset) +{ + ins->mSrc[offset].mIntConst = 0; + + while (ins->mSrc[offset].mTemp >= 0 && tvalue[ins->mSrc[offset].mTemp]) + { + InterInstruction* ains = tvalue[ins->mSrc[offset].mTemp]; + + if (ains->mCode == IC_CONSTANT) + { + ins->mSrc[offset].mIntConst += ains->mConst.mIntConst; + ins->mSrc[offset].mLinkerObject = ains->mConst.mLinkerObject; + ins->mSrc[offset].mVarIndex = ains->mConst.mVarIndex; + ins->mSrc[offset].mMemory = ains->mConst.mMemory; + ins->mSrc[offset].mTemp = -1; + } + else if (ains->mCode == IC_LEA && ains->mSrc[0].mTemp < 0 && ains->mSrc[1].mTemp >= 0 && tvalue[ains->mSrc[1].mTemp] && ains->mSrc[0].mIntConst >= 0) + { + ins->mSrc[offset].mIntConst += ains->mSrc[0].mIntConst; + ins->mSrc[offset].mTemp = ains->mSrc[1].mTemp; + ins->mSrc[offset].mFinal = false; + ains->mSrc[1].mFinal = false; + } + else if (ains->mCode == IC_BINARY_OPERATOR && ains->mOperator == IA_ADD && ains->mSrc[0].mTemp < 0 && ains->mSrc[1].mTemp >= 0 && tvalue[ains->mSrc[1].mTemp] && ains->mSrc[0].mIntConst >= 0) + { + assert(false); + ins->mSrc[offset].mIntConst = ains->mSrc[0].mIntConst; + ins->mSrc[offset].mTemp = ains->mSrc[1].mTemp; + } + else if (ains->mCode == IC_BINARY_OPERATOR && ains->mOperator == IA_ADD && ains->mSrc[1].mTemp < 0 && ains->mSrc[0].mTemp >= 0 && tvalue[ains->mSrc[0].mTemp] && ains->mSrc[1].mIntConst >= 0) + { + assert(false); + ins->mSrc[offset].mIntConst = ains->mSrc[1].mIntConst; + ins->mSrc[offset].mTemp = ains->mSrc[0].mTemp; + } + else + break; + } +} + + +void InterCodeBasicBlock::CheckValueUsage(InterInstruction * ins, const GrowingInstructionPtrArray& tvalue, const GrowingVariableArray& staticVars, const GrowingInterCodeProcedurePtrArray& staticProcs, FastNumberSet& fsingle) +{ + switch (ins->mCode) + { + case IC_CALL: + case IC_CALL_NATIVE: + if (ins->mSrc[0].mTemp >= 0 && tvalue[ins->mSrc[0].mTemp] && tvalue[ins->mSrc[0].mTemp]->mCode == IC_CONSTANT) + { + ins->mSrc[0].mMemory = tvalue[ins->mSrc[0].mTemp]->mConst.mMemory; + ins->mSrc[0].mLinkerObject = tvalue[ins->mSrc[0].mTemp]->mConst.mLinkerObject; + ins->mSrc[0].mVarIndex = tvalue[ins->mSrc[0].mTemp]->mConst.mVarIndex; + ins->mSrc[0].mOperandSize = tvalue[ins->mSrc[0].mTemp]->mConst.mOperandSize; + ins->mSrc[0].mIntConst = tvalue[ins->mSrc[0].mTemp]->mConst.mIntConst; + ins->mSrc[0].mTemp = -1; + } + + break; + case IC_ASSEMBLER: + if (ins->mSrc[0].mTemp >= 0 && tvalue[ins->mSrc[0].mTemp] && tvalue[ins->mSrc[0].mTemp]->mCode == IC_CONSTANT) + { + ins->mSrc[0].mMemory = tvalue[ins->mSrc[0].mTemp]->mConst.mMemory; + ins->mSrc[0].mLinkerObject = tvalue[ins->mSrc[0].mTemp]->mConst.mLinkerObject; + ins->mSrc[0].mVarIndex = tvalue[ins->mSrc[0].mTemp]->mConst.mVarIndex; + ins->mSrc[0].mOperandSize = tvalue[ins->mSrc[0].mTemp]->mConst.mOperandSize; + ins->mSrc[0].mIntConst = tvalue[ins->mSrc[0].mTemp]->mConst.mIntConst; + ins->mSrc[0].mTemp = -1; + } + for (int i = 1; i < ins->mNumOperands; i++) + { + if (ins->mSrc[i].mTemp >= 0 && tvalue[ins->mSrc[i].mTemp]) + { + InterInstruction* lins = tvalue[ins->mSrc[i].mTemp]; + if (lins->mCode == IC_LOAD && lins->mSrc[0].mTemp < 0 && lins->mSrc[0].mMemory == IM_FPARAM) + { + ins->mSrc[i].mType = IT_POINTER; + ins->mSrc[i].mMemory = IM_FPARAM; + ins->mSrc[i].mVarIndex = lins->mSrc[0].mVarIndex; + ins->mSrc[i].mIntConst = lins->mSrc[0].mIntConst; + ins->mSrc[i].mOperandSize = lins->mSrc[0].mOperandSize; + ins->mSrc[i].mTemp = -1; + } + } + } + + break; + case IC_LOAD_TEMPORARY: + if (ins->mSrc[0].mTemp >= 0 && tvalue[ins->mSrc[0].mTemp] && tvalue[ins->mSrc[0].mTemp]->mCode == IC_CONSTANT) + { + switch (ins->mSrc[0].mType) + { + case IT_FLOAT: + ins->mCode = IC_CONSTANT; + ins->mConst.mFloatConst = tvalue[ins->mSrc[0].mTemp]->mConst.mFloatConst; + ins->mSrc[0].mTemp = -1; + ins->mNumOperands = 0; + break; + case IT_POINTER: + ins->mCode = IC_CONSTANT; + ins->mConst.mMemory = tvalue[ins->mSrc[0].mTemp]->mConst.mMemory; + ins->mConst.mLinkerObject = tvalue[ins->mSrc[0].mTemp]->mConst.mLinkerObject; + ins->mConst.mVarIndex = tvalue[ins->mSrc[0].mTemp]->mConst.mVarIndex; + ins->mConst.mIntConst = tvalue[ins->mSrc[0].mTemp]->mConst.mIntConst; + ins->mConst.mOperandSize = tvalue[ins->mSrc[0].mTemp]->mConst.mOperandSize; + ins->mSrc[0].mTemp = -1; + ins->mNumOperands = 0; + break; + default: + ins->mCode = IC_CONSTANT; + ins->mConst.mIntConst = tvalue[ins->mSrc[0].mTemp]->mConst.mIntConst; + ins->mSrc[0].mTemp = -1; + ins->mNumOperands = 0; + break; + } + ins->mConst.mType = ins->mDst.mType; + } + + break; + + case IC_LOAD: + OptimizeAddress(ins, tvalue, 0); + + if (ins->mSrc[0].mTemp < 0 && ins->mSrc[0].mMemory == IM_GLOBAL && (ins->mSrc[0].mLinkerObject->mFlags & LOBJF_CONST)) + LoadConstantFold(ins, nullptr, staticVars, staticProcs); + + break; + case IC_STORE: + if (ins->mSrc[0].mTemp >= 0 && tvalue[ins->mSrc[0].mTemp] && tvalue[ins->mSrc[0].mTemp]->mCode == IC_CONSTANT) + { + switch (ins->mSrc[0].mType) + { + case IT_FLOAT: + ins->mSrc[0].mFloatConst = tvalue[ins->mSrc[0].mTemp]->mConst.mFloatConst; + ins->mSrc[0].mTemp = -1; + break; + case IT_POINTER: + break; + default: + ins->mSrc[0].mIntConst = tvalue[ins->mSrc[0].mTemp]->mConst.mIntConst; + ins->mSrc[0].mTemp = -1; + break; + } + } + + OptimizeAddress(ins, tvalue, 1); + break; + case IC_FILL: + if (ins->mSrc[0].mTemp >= 0 && tvalue[ins->mSrc[0].mTemp] && tvalue[ins->mSrc[0].mTemp]->mCode == IC_CONSTANT) + { + ins->mSrc[0].mIntConst = tvalue[ins->mSrc[0].mTemp]->mConst.mIntConst; + ins->mSrc[0].mTemp = -1; + } + + OptimizeAddress(ins, tvalue, 1); + break; + + case IC_COPY: + OptimizeAddress(ins, tvalue, 0); + OptimizeAddress(ins, tvalue, 1); + break; + case IC_LEA: + if (ins->mSrc[0].mTemp >= 0 && tvalue[ins->mSrc[0].mTemp] && tvalue[ins->mSrc[0].mTemp]->mCode == IC_CONSTANT) + { + if (ins->mSrc[1].mTemp >= 0 && tvalue[ins->mSrc[1].mTemp] && tvalue[ins->mSrc[1].mTemp]->mCode == IC_CONSTANT) + { + ins->mCode = IC_CONSTANT; + ins->mDst.mType = IT_POINTER; + ins->mConst.mMemory = tvalue[ins->mSrc[1].mTemp]->mConst.mMemory; + ins->mConst.mLinkerObject = tvalue[ins->mSrc[1].mTemp]->mConst.mLinkerObject; + ins->mConst.mVarIndex = tvalue[ins->mSrc[1].mTemp]->mConst.mVarIndex; + ins->mConst.mIntConst = tvalue[ins->mSrc[1].mTemp]->mConst.mIntConst + tvalue[ins->mSrc[0].mTemp]->mConst.mIntConst; + ins->mConst.mOperandSize = tvalue[ins->mSrc[1].mTemp]->mConst.mOperandSize; + ins->mConst.mType = IT_POINTER; + ins->mSrc[0].mTemp = -1; + ins->mSrc[1].mTemp = -1; + ins->mNumOperands = 0; + } + else if (tvalue[ins->mSrc[0].mTemp]->mConst.mIntConst == 0) + { + ins->mCode = IC_LOAD_TEMPORARY; + ins->mSrc[0].mType = ins->mSrc[1].mType; + ins->mSrc[0].mTemp = ins->mSrc[1].mTemp; + ins->mSrc[1].mTemp = -1; + ins->mNumOperands = 1; + assert(ins->mSrc[0].mTemp >= 0); + } +#if 1 + else if (ins->mSrc[1].mTemp >= 0 && fsingle[ins->mSrc[1].mTemp]) + { + InterInstruction* lins = tvalue[ins->mSrc[1].mTemp]; + while (lins && lins->mCode == IC_LEA && lins->mSrc[1].mTemp >= 0 && fsingle[lins->mSrc[1].mTemp] && fsingle[tvalue[lins->mSrc[1].mTemp]->mDst.mTemp]) + lins = tvalue[lins->mSrc[1].mTemp]; + if (lins && lins->mCode == IC_LEA && lins->mSrc[1].mTemp < 0 && (lins->mSrc[1].mMemory == IM_ABSOLUTE || lins->mSrc[1].mMemory == IM_GLOBAL)) + { + lins->mSrc[1].mIntConst += tvalue[ins->mSrc[0].mTemp]->mConst.mIntConst; + ins->mCode = IC_LOAD_TEMPORARY; + ins->mSrc[0].mType = ins->mSrc[1].mType; + ins->mSrc[0].mTemp = ins->mSrc[1].mTemp; + ins->mSrc[1].mTemp = -1; + ins->mNumOperands = 1; + } + else + { + ins->mSrc[0].mIntConst = tvalue[ins->mSrc[0].mTemp]->mConst.mIntConst; + ins->mSrc[0].mTemp = -1; + } + } +#endif + else + { + ins->mSrc[0].mIntConst = tvalue[ins->mSrc[0].mTemp]->mConst.mIntConst; + ins->mSrc[0].mTemp = -1; + } + } + else if (ins->mSrc[1].mTemp >= 0 && tvalue[ins->mSrc[1].mTemp] && tvalue[ins->mSrc[1].mTemp]->mCode == IC_CONSTANT && (tvalue[ins->mSrc[1].mTemp]->mConst.mMemory == IM_GLOBAL || tvalue[ins->mSrc[1].mTemp]->mConst.mMemory == IM_ABSOLUTE)) + { + ins->mSrc[1].mMemory = tvalue[ins->mSrc[1].mTemp]->mConst.mMemory; + ins->mSrc[1].mLinkerObject = tvalue[ins->mSrc[1].mTemp]->mConst.mLinkerObject; + ins->mSrc[1].mVarIndex = tvalue[ins->mSrc[1].mTemp]->mConst.mVarIndex; + ins->mSrc[1].mIntConst = tvalue[ins->mSrc[1].mTemp]->mConst.mIntConst; + ins->mSrc[1].mOperandSize = tvalue[ins->mSrc[1].mTemp]->mConst.mOperandSize; + ins->mSrc[1].mTemp = -1; + + while (ins->mSrc[0].mTemp >= 0 && tvalue[ins->mSrc[0].mTemp] && tvalue[ins->mSrc[0].mTemp]->mCode == IC_BINARY_OPERATOR) + { + InterInstruction* iins = tvalue[ins->mSrc[0].mTemp]; + if (iins->mOperator == IA_ADD) + { + if (iins->mSrc[0].mTemp >= 0 && iins->mSrc[1].mTemp < 0) + { + ins->mSrc[0].mTemp = iins->mSrc[0].mTemp; + ins->mSrc[0].mRange.AddConstValue(IT_INT16, -iins->mSrc[1].mIntConst); + ins->mSrc[1].mIntConst += iins->mSrc[1].mIntConst; + } + else if (iins->mSrc[0].mTemp < 0 && iins->mSrc[1].mTemp >= 0) + { + ins->mSrc[0].mTemp = iins->mSrc[1].mTemp; + ins->mSrc[0].mRange.AddConstValue(IT_INT16, -iins->mSrc[0].mIntConst); + ins->mSrc[1].mIntConst += iins->mSrc[0].mIntConst; + } + else + break; + } + else if (iins->mOperator == IA_SUB) + { + if (iins->mSrc[0].mTemp < 0 && iins->mSrc[1].mTemp >= 0) + { + ins->mSrc[0].mTemp = iins->mSrc[1].mTemp; + ins->mSrc[0].mRange.AddConstValue(IT_INT16, iins->mSrc[0].mIntConst); + ins->mSrc[1].mIntConst -= iins->mSrc[0].mIntConst; + } + else + break; + } + else + break; + } + } + break; + case IC_TYPECAST: + if (ins->mSrc[0].mType == ins->mDst.mType) + { + ins->mCode = IC_LOAD_TEMPORARY; + ins->mNumOperands = 1; + assert(ins->mSrc[0].mTemp >= 0); + } + else if (TypeInteger(ins->mSrc[0].mType) && ins->mDst.mType == IT_POINTER) + { + if (ins->mSrc[0].mTemp >= 0 && tvalue[ins->mSrc[0].mTemp] && tvalue[ins->mSrc[0].mTemp]->mCode == IC_CONSTANT) + { + ins->mCode = IC_CONSTANT; + ins->mDst.mType = IT_POINTER; + ins->mConst.mType = IT_POINTER; + ins->mConst.mMemory = IM_ABSOLUTE; + ins->mConst.mVarIndex = 0; + ins->mConst.mIntConst = tvalue[ins->mSrc[0].mTemp]->mConst.mIntConst; + ins->mSrc[0].mTemp = -1; + ins->mNumOperands = 0; + } + } + else if (ins->mDst.mType == IT_INT16 && ins->mSrc[0].mType == IT_POINTER) + { + if (ins->mSrc[0].mTemp >= 0 && tvalue[ins->mSrc[0].mTemp]) + { + InterInstruction* cins = tvalue[ins->mSrc[0].mTemp]; + if (cins->mCode == IC_CONSTANT && cins->mConst.mMemory == IM_ABSOLUTE) + { + ins->mCode = IC_CONSTANT; + ins->mDst.mType = IT_INT16; + ins->mConst.mType = IT_INT16; + ins->mConst.mIntConst = cins->mConst.mIntConst; + ins->mSrc[0].mTemp = -1; + ins->mNumOperands = 0; + } + } + } + break; + + case IC_CONVERSION_OPERATOR: + if (ins->mSrc[0].mTemp >= 0 && tvalue[ins->mSrc[0].mTemp] && tvalue[ins->mSrc[0].mTemp]->mCode == IC_CONSTANT) + { + ConversionConstantFold(ins, tvalue[ins->mSrc[0].mTemp]->mConst); + } + break; + + case IC_SELECT: + for (int i = 0; i < 3; i++) + { + if (ins->mSrc[i].mTemp >= 0 && tvalue[ins->mSrc[i].mTemp] && tvalue[ins->mSrc[i].mTemp]->mCode == IC_CONSTANT && ins->mSrc[i].mType != IT_POINTER) + { + ins->mSrc[i] = tvalue[ins->mSrc[i].mTemp]->mConst; + ins->mSrc[i].mTemp = -1; + } + } + break; + case IC_RETURN_VALUE: + if (ins->mSrc[0].mTemp >= 0 && tvalue[ins->mSrc[0].mTemp] && tvalue[ins->mSrc[0].mTemp]->mCode == IC_CONSTANT) + { + switch (ins->mSrc[0].mType) + { + case IT_FLOAT: + break; + case IT_POINTER: + break; + default: + ins->mSrc[0].mIntConst = tvalue[ins->mSrc[0].mTemp]->mConst.mIntConst; + ins->mSrc[0].mTemp = -1; + break; + } + } + break; + case IC_BINARY_OPERATOR: + switch (ins->mSrc[0].mType) + { + case IT_FLOAT: + if (ins->mSrc[1].mTemp >= 0 && tvalue[ins->mSrc[1].mTemp] && tvalue[ins->mSrc[1].mTemp]->mCode == IC_CONSTANT) + { + if (ins->mSrc[0].mTemp >= 0 && tvalue[ins->mSrc[0].mTemp] && tvalue[ins->mSrc[0].mTemp]->mCode == IC_CONSTANT) + { + ins->mCode = IC_CONSTANT; + ins->mConst.mFloatConst = ConstantFolding(ins->mOperator, tvalue[ins->mSrc[1].mTemp]->mConst.mFloatConst, tvalue[ins->mSrc[0].mTemp]->mConst.mFloatConst); + ins->mConst.mType = IT_FLOAT; + ins->mSrc[0].mTemp = -1; + ins->mSrc[1].mTemp = -1; + ins->mNumOperands = 0; + } + else + { + ins->mSrc[1].mFloatConst = tvalue[ins->mSrc[1].mTemp]->mConst.mFloatConst; + ins->mSrc[1].mTemp = -1; + + if (ins->mOperator == IA_ADD && ins->mSrc[1].mFloatConst == 0) + { + ins->mCode = IC_LOAD_TEMPORARY; + ins->mNumOperands = 1; + assert(ins->mSrc[0].mTemp >= 0); + } + else if (ins->mOperator == IA_MUL) + { + if (ins->mSrc[1].mFloatConst == 1.0) + { + ins->mCode = IC_LOAD_TEMPORARY; + ins->mNumOperands = 1; + assert(ins->mSrc[0].mTemp >= 0); + } + else if (ins->mSrc[1].mFloatConst == 0.0) + { + ins->mCode = IC_CONSTANT; + ins->mConst.mFloatConst = 0.0; + ins->mConst.mType = IT_FLOAT; + ins->mSrc[0].mTemp = -1; + ins->mSrc[1].mTemp = -1; + ins->mNumOperands = 0; + } + else if (ins->mSrc[1].mFloatConst == 2.0) + { + ins->mOperator = IA_ADD; + ins->mSrc[1].mTemp = ins->mSrc[0].mTemp; + assert(ins->mSrc[0].mTemp >= 0); + } + } + } + } + else if (ins->mSrc[0].mTemp >= 0 && tvalue[ins->mSrc[0].mTemp] && tvalue[ins->mSrc[0].mTemp]->mCode == IC_CONSTANT) + { + ins->mSrc[0].mFloatConst = tvalue[ins->mSrc[0].mTemp]->mConst.mFloatConst; + ins->mSrc[0].mTemp = -1; + + if (ins->mOperator == IA_ADD && ins->mSrc[0].mFloatConst == 0) + { + ins->mCode = IC_LOAD_TEMPORARY; + ins->mSrc[0].mTemp = ins->mSrc[1].mTemp; + ins->mSrc[1].mTemp = -1; + ins->mNumOperands = 1; + assert(ins->mSrc[0].mTemp >= 0); + } + else if (ins->mOperator == IA_MUL) + { + if (ins->mSrc[0].mFloatConst == 1.0) + { + ins->mCode = IC_LOAD_TEMPORARY; + ins->mSrc[0].mTemp = ins->mSrc[1].mTemp; + ins->mSrc[1].mTemp = -1; + ins->mNumOperands = 1; + assert(ins->mSrc[0].mTemp >= 0); + } + else if (ins->mSrc[0].mFloatConst == 0.0) + { + ins->mCode = IC_CONSTANT; + ins->mConst.mFloatConst = 0.0; + ins->mConst.mType = IT_FLOAT; + ins->mSrc[0].mTemp = -1; + ins->mSrc[1].mTemp = -1; + ins->mNumOperands = 0; + } + else if (ins->mSrc[0].mFloatConst == 2.0) + { + ins->mOperator = IA_ADD; + ins->mSrc[0].mTemp = ins->mSrc[1].mTemp; + assert(ins->mSrc[0].mTemp >= 0); + } + } + } + break; + case IT_POINTER: + break; + default: + if (ins->mSrc[1].mTemp >= 0 && tvalue[ins->mSrc[1].mTemp] && tvalue[ins->mSrc[1].mTemp]->mCode == IC_CONSTANT) + { + if (ins->mSrc[0].mTemp >= 0 && tvalue[ins->mSrc[0].mTemp] && tvalue[ins->mSrc[0].mTemp]->mCode == IC_CONSTANT) + { + ins->mCode = IC_CONSTANT; + ins->mConst.mIntConst = LimitIntConstValue(ins->mDst.mType, ConstantFolding(ins->mOperator, ins->mDst.mType, tvalue[ins->mSrc[1].mTemp]->mConst.mIntConst, tvalue[ins->mSrc[0].mTemp]->mConst.mIntConst)); + ins->mConst.mType = ins->mDst.mType; + ins->mSrc[0].mTemp = -1; + ins->mSrc[1].mTemp = -1; + ins->mNumOperands = 0; + } + else + { + ins->mSrc[1].mIntConst = LimitIntConstValue(ins->mSrc[1].mType, tvalue[ins->mSrc[1].mTemp]->mConst.mIntConst); + ins->mSrc[1].mTemp = -1; +#if 1 + if (ins->mOperator == IA_ADD && ins->mSrc[1].mIntConst == 0) + { + ins->mCode = IC_LOAD_TEMPORARY; + ins->mNumOperands = 1; + assert(ins->mSrc[0].mTemp >= 0); + } + else if (ins->mOperator == IA_MUL) + { + if (ins->mSrc[1].mIntConst == 1) + { + ins->mCode = IC_LOAD_TEMPORARY; + ins->mNumOperands = 1; + assert(ins->mSrc[0].mTemp >= 0); + } + else if (ins->mSrc[1].mIntConst == 2) + { + ins->mOperator = IA_SHL; + ins->mSrc[1].mTemp = ins->mSrc[0].mTemp; + ins->mSrc[1].mType = ins->mSrc[0].mType; + ins->mSrc[0].mTemp = -1; + ins->mSrc[0].mIntConst = 1; + } + else if (ins->mSrc[1].mIntConst == 4) + { + ins->mOperator = IA_SHL; + ins->mSrc[1].mTemp = ins->mSrc[0].mTemp; + ins->mSrc[1].mType = ins->mSrc[0].mType; + ins->mSrc[0].mTemp = -1; + ins->mSrc[0].mIntConst = 2; + } + else if (ins->mSrc[1].mIntConst == 8) + { + ins->mOperator = IA_SHL; + ins->mSrc[1].mTemp = ins->mSrc[0].mTemp; + ins->mSrc[1].mType = ins->mSrc[0].mType; + ins->mSrc[0].mTemp = -1; + ins->mSrc[0].mIntConst = 3; + } + else if (ins->mSrc[0].mType == IT_INT32 && ispow2(ins->mSrc[1].mIntConst)) + { + int64 s = ins->mSrc[1].mIntConst; + ins->mOperator = IA_SHL; + ins->mSrc[1].mTemp = ins->mSrc[0].mTemp; + ins->mSrc[1].mType = ins->mSrc[0].mType; + ins->mSrc[0].mTemp = -1; + ins->mSrc[0].mIntConst = 0; + while (s > 1) + { + ins->mSrc[0].mIntConst++; + s >>= 1; + } + } + + } +#endif + } + } + else if (ins->mSrc[0].mTemp >= 0 && tvalue[ins->mSrc[0].mTemp] && tvalue[ins->mSrc[0].mTemp]->mCode == IC_CONSTANT) + { + ins->mSrc[0].mIntConst = LimitIntConstValue(ins->mSrc[0].mType, tvalue[ins->mSrc[0].mTemp]->mConst.mIntConst); + ins->mSrc[0].mTemp = -1; + + if (ins->mOperator == IA_ADD && ins->mSrc[0].mIntConst == 0) + { + ins->mCode = IC_LOAD_TEMPORARY; + ins->mSrc[0].mTemp = ins->mSrc[1].mTemp; + ins->mSrc[1].mTemp = -1; + ins->mNumOperands = 1; + assert(ins->mSrc[0].mTemp >= 0); + } + else if (ins->mOperator == IA_MUL) + { + if (ins->mSrc[0].mIntConst == 1) + { + ins->mCode = IC_LOAD_TEMPORARY; + ins->mSrc[0].mTemp = ins->mSrc[1].mTemp; + ins->mSrc[1].mTemp = -1; + ins->mNumOperands = 1; + assert(ins->mSrc[0].mTemp >= 0); + } + else if (ins->mSrc[0].mIntConst == 2) + { + ins->mOperator = IA_SHL; + ins->mSrc[0].mIntConst = 1; + } + else if (ins->mSrc[0].mIntConst == 4) + { + ins->mOperator = IA_SHL; + ins->mSrc[0].mIntConst = 2; + } + else if (ins->mSrc[0].mIntConst == 8) + { + ins->mOperator = IA_SHL; + ins->mSrc[0].mIntConst = 3; + } + else if (ins->mSrc[1].mType == IT_INT32 && ispow2(ins->mSrc[0].mIntConst)) + { + int64 s = ins->mSrc[0].mIntConst; + ins->mOperator = IA_SHL; + ins->mSrc[0].mIntConst = 0; + while (s > 1) + { + ins->mSrc[0].mIntConst++; + s >>= 1; + } + } + } + else if (ins->mOperator == IA_MODU && (ins->mSrc[0].mIntConst & (ins->mSrc[0].mIntConst - 1)) == 0) + { + ins->mOperator = IA_AND; + ins->mSrc[0].mIntConst--; + } + } + + if (ins->mSrc[0].mTemp > 0 && ins->mSrc[1].mTemp > 0 && ins->mSrc[0].mTemp == ins->mSrc[1].mTemp) + { + if (ins->mOperator == IA_ADD) + { + ins->mOperator = IA_SHL; + ins->mSrc[0].mTemp = -1; + ins->mSrc[0].mIntConst = 1; + } + else if (ins->mOperator == IA_SUB) + { + ins->mCode = IC_CONSTANT; + ins->mConst.mType = ins->mDst.mType; + ins->mConst.mIntConst = 0; + ins->mNumOperands = 0; + } + } + + if (ins->mSrc[1].mTemp < 0 && ins->mSrc[0].mTemp >= 0 && tvalue[ins->mSrc[0].mTemp] && tvalue[ins->mSrc[0].mTemp]->mCode == IC_BINARY_OPERATOR) + { + InterInstruction* pins = tvalue[ins->mSrc[0].mTemp]; + if (ins->mOperator == pins->mOperator && (ins->mOperator == IA_ADD || ins->mOperator == IA_MUL || ins->mOperator == IA_AND || ins->mOperator == IA_OR)) + { + if (pins->mSrc[1].mTemp < 0) + { + ins->mSrc[1].mIntConst = ConstantFolding(ins->mOperator, ins->mDst.mType, ins->mSrc[1].mIntConst, pins->mSrc[1].mIntConst); + ins->mSrc[0].mTemp = pins->mSrc[0].mTemp; + } + else if (pins->mSrc[0].mTemp < 0) + { + ins->mSrc[1].mIntConst = ConstantFolding(ins->mOperator, ins->mDst.mType, ins->mSrc[1].mIntConst, pins->mSrc[0].mIntConst); + ins->mSrc[0].mTemp = pins->mSrc[1].mTemp; + } + } + } + else if (ins->mSrc[0].mTemp < 0 && ins->mSrc[1].mTemp >= 0 && tvalue[ins->mSrc[1].mTemp] && tvalue[ins->mSrc[1].mTemp]->mCode == IC_BINARY_OPERATOR) + { + InterInstruction* pins = tvalue[ins->mSrc[1].mTemp]; + if (ins->mOperator == pins->mOperator && (ins->mOperator == IA_ADD || ins->mOperator == IA_MUL || ins->mOperator == IA_AND || ins->mOperator == IA_OR)) + { + if (pins->mSrc[1].mTemp < 0) + { + ins->mSrc[0].mIntConst = ConstantFolding(ins->mOperator, ins->mDst.mType, ins->mSrc[0].mIntConst, pins->mSrc[1].mIntConst); + ins->mSrc[1].mTemp = pins->mSrc[0].mTemp; + } + else if (pins->mSrc[0].mTemp < 0) + { + ins->mSrc[0].mIntConst = ConstantFolding(ins->mOperator, ins->mDst.mType, ins->mSrc[0].mIntConst, pins->mSrc[0].mIntConst); + ins->mSrc[1].mTemp = pins->mSrc[1].mTemp; + } + } + else if (ins->mOperator == IA_SUB && pins->mOperator == IA_SUB) + { + if (pins->mSrc[0].mTemp < 0) + { + ins->mSrc[0].mIntConst = ConstantFolding(IA_ADD, ins->mDst.mType, ins->mSrc[0].mIntConst, pins->mSrc[0].mIntConst); + ins->mSrc[1].mTemp = pins->mSrc[1].mTemp; + } + } + else if (ins->mOperator == IA_ADD && pins->mOperator == IA_SUB) + { + if (pins->mSrc[0].mTemp < 0) + { + ins->mSrc[0].mIntConst = ConstantFolding(IA_SUB, ins->mDst.mType, ins->mSrc[0].mIntConst, pins->mSrc[0].mIntConst); + ins->mSrc[1].mTemp = pins->mSrc[1].mTemp; + } + } + else if (ins->mOperator == IA_SUB && pins->mOperator == IA_ADD) + { + if (pins->mSrc[1].mTemp < 0) + { + ins->mSrc[0].mIntConst = ConstantFolding(ins->mOperator, ins->mDst.mType, ins->mSrc[0].mIntConst, pins->mSrc[1].mIntConst); + ins->mSrc[1].mTemp = pins->mSrc[0].mTemp; + } + else if (pins->mSrc[0].mTemp < 0) + { + ins->mSrc[0].mIntConst = ConstantFolding(ins->mOperator, ins->mDst.mType, ins->mSrc[0].mIntConst, pins->mSrc[0].mIntConst); + ins->mSrc[1].mTemp = pins->mSrc[1].mTemp; + } + } + else if (ins->mOperator == IA_SHL && (pins->mOperator == IA_SHR || pins->mOperator == IA_SAR) && pins->mSrc[0].mTemp < 0 && ins->mSrc[0].mIntConst == pins->mSrc[0].mIntConst) + { + ins->mOperator = IA_AND; + ins->mSrc[0].mIntConst = -1LL << ins->mSrc[0].mIntConst; + ins->mSrc[1].mTemp = pins->mSrc[1].mTemp; + } + } + + break; + } + break; + case IC_UNARY_OPERATOR: + switch (ins->mSrc[0].mType) + { + case IT_FLOAT: + if (ins->mSrc[0].mTemp >= 0 && tvalue[ins->mSrc[0].mTemp] && tvalue[ins->mSrc[0].mTemp]->mCode == IC_CONSTANT) + { + ins->mCode = IC_CONSTANT; + ins->mConst.mFloatConst = ConstantFolding(ins->mOperator, tvalue[ins->mSrc[0].mTemp]->mConst.mFloatConst); + ins->mConst.mType = IT_FLOAT; + ins->mSrc[0].mTemp = -1; + ins->mNumOperands = 0; + } + break; + case IT_POINTER: + break; + default: + break; + } + break; + case IC_RELATIONAL_OPERATOR: + switch (ins->mSrc[1].mType) + { + case IT_FLOAT: + if (ins->mSrc[1].mTemp >= 0 && tvalue[ins->mSrc[1].mTemp] && tvalue[ins->mSrc[1].mTemp]->mCode == IC_CONSTANT && + ins->mSrc[0].mTemp >= 0 && tvalue[ins->mSrc[0].mTemp] && tvalue[ins->mSrc[0].mTemp]->mCode == IC_CONSTANT) + { + ins->mCode = IC_CONSTANT; + ins->mConst.mIntConst = ConstantRelationalFolding(ins->mOperator, tvalue[ins->mSrc[1].mTemp]->mConst.mFloatConst, tvalue[ins->mSrc[0].mTemp]->mConst.mFloatConst); + ins->mConst.mType = IT_BOOL; + ins->mDst.mType = IT_BOOL; + ins->mSrc[0].mTemp = -1; + ins->mSrc[1].mTemp = -1; + ins->mNumOperands = 0; + } + else + { + if (ins->mSrc[1].mTemp >= 0 && tvalue[ins->mSrc[1].mTemp] && tvalue[ins->mSrc[1].mTemp]->mCode == IC_CONSTANT) + { + ins->mSrc[1].mFloatConst = tvalue[ins->mSrc[1].mTemp]->mConst.mFloatConst; + ins->mSrc[1].mTemp = -1; + } + else if (ins->mSrc[0].mTemp >= 0 && tvalue[ins->mSrc[0].mTemp] && tvalue[ins->mSrc[0].mTemp]->mCode == IC_CONSTANT) + { + ins->mSrc[0].mFloatConst = tvalue[ins->mSrc[0].mTemp]->mConst.mFloatConst; + ins->mSrc[0].mTemp = -1; + } + } + break; + case IT_POINTER: +#if 0 + if (ins->mOperator == IA_CMPEQ || ins->mOperator == IA_CMPNE) + { + if (ins->mSrc[0].mTemp >= 0 && tvalue[ins->mSrc[0].mTemp] && tvalue[ins->mSrc[0].mTemp]->mCode == IC_CONSTANT) + { + ins->mOperandSize = tvalue[ins->mSrc[0].mTemp]->mOperandSize; + ins->mSrc[0].mTemp = -1; + } + else if (ins->mSrc[1].mTemp >= 0 && tvalue[ins->mSrc[1].mTemp] && tvalue[ins->mSrc[1].mTemp]->mCode == IC_CONSTANT) + { + ins->mOperandSize = tvalue[ins->mSrc[1].mTemp]->mOperandSize; + ins->mSrc[1].mTemp = -1; + } + } +#endif + break; + default: + if (ins->mSrc[1].mTemp >= 0 && tvalue[ins->mSrc[1].mTemp] && tvalue[ins->mSrc[1].mTemp]->mCode == IC_CONSTANT && + ins->mSrc[0].mTemp >= 0 && tvalue[ins->mSrc[0].mTemp] && tvalue[ins->mSrc[0].mTemp]->mCode == IC_CONSTANT) + { + ins->mCode = IC_CONSTANT; + ins->mConst.mIntConst = ConstantFolding(ins->mOperator, ins->mDst.mType, tvalue[ins->mSrc[1].mTemp]->mConst.mIntConst, tvalue[ins->mSrc[0].mTemp]->mConst.mIntConst); + ins->mConst.mType = ins->mDst.mType; + ins->mSrc[0].mTemp = -1; + ins->mSrc[1].mTemp = -1; + ins->mNumOperands = 0; + } + else + { + if (ins->mSrc[1].mTemp >= 0 && tvalue[ins->mSrc[1].mTemp] && tvalue[ins->mSrc[1].mTemp]->mCode == IC_CONSTANT) + { + ins->mSrc[1].mIntConst = LimitIntConstValue(ins->mSrc[1].mType, tvalue[ins->mSrc[1].mTemp]->mConst.mIntConst); + ins->mSrc[1].mTemp = -1; + } + else if (ins->mSrc[0].mTemp >= 0 && tvalue[ins->mSrc[0].mTemp] && tvalue[ins->mSrc[0].mTemp]->mCode == IC_CONSTANT) + { + ins->mSrc[0].mIntConst = LimitIntConstValue(ins->mSrc[0].mType, tvalue[ins->mSrc[0].mTemp]->mConst.mIntConst); + ins->mSrc[0].mTemp = -1; + } + } + break; + } + break; + case IC_BRANCH: + if (ins->mSrc[0].mTemp >= 0 && tvalue[ins->mSrc[0].mTemp] && tvalue[ins->mSrc[0].mTemp]->mCode == IC_CONSTANT) + { + ins->mSrc[0].mIntConst = tvalue[ins->mSrc[0].mTemp]->mConst.mIntConst; + ins->mSrc[0].mMemory = tvalue[ins->mSrc[0].mTemp]->mConst.mMemory; + ins->mSrc[0].mTemp = -1; + } + break; + } +} + + +void InterCodeBasicBlock::CollectLocalAddressTemps(GrowingIntArray& localTable, GrowingIntArray& paramTable, int& nlocals, int& nparams) +{ + int i; + + if (!mVisited) + { + mVisited = true; + + for (i = 0; i < mInstructions.Size(); i++) + mInstructions[i]->CollectLocalAddressTemps(localTable, paramTable, nlocals, nparams); + + if (mTrueJump) mTrueJump->CollectLocalAddressTemps(localTable, paramTable, nlocals, nparams); + if (mFalseJump) mFalseJump->CollectLocalAddressTemps(localTable, paramTable, nlocals, nparams); + } +} + +void InterCodeBasicBlock::RecheckLocalAliased(void) +{ + if (!mVisited) + { + mVisited = true; + + for (int i = 0; i < mInstructions.Size(); i++) + { + InterVariable* v = nullptr; + + InterInstruction* ins = mInstructions[i]; + if (ins->mCode == IC_LEA) + { + if (ins->mSrc[1].mTemp < 0) + { + if (ins->mSrc[1].mMemory == IM_LOCAL) + v = mProc->mLocalVars[ins->mSrc[1].mVarIndex]; + else if (ins->mSrc[1].mMemory == IM_PARAM || ins->mSrc[1].mMemory == IM_FPARAM) + v = mProc->mParamVars[ins->mSrc[1].mVarIndex]; + } + } + else if (ins->mCode == IC_CONSTANT && ins->mDst.mType == IT_POINTER) + { + if (ins->mConst.mMemory == IM_LOCAL) + v = mProc->mLocalVars[ins->mConst.mVarIndex]; + else if (ins->mConst.mMemory == IM_PARAM || ins->mConst.mMemory == IM_FPARAM) + v = mProc->mParamVars[ins->mConst.mVarIndex]; + } + + if (v) + { + if (!v->mNotAliased) + v->mAliased = true; + } + } + + if (mTrueJump) mTrueJump->RecheckLocalAliased(); + if (mFalseJump) mFalseJump->RecheckLocalAliased(); + } +} + +void InterCodeBasicBlock::MarkAliasedLocalTemps(const GrowingIntArray& localTable, NumberSet& aliasedLocals, const GrowingIntArray& paramTable, NumberSet& aliasedParams) +{ + int i; + + if (!mVisited) + { + mVisited = true; + + for (i = 0; i < mInstructions.Size(); i++) + mInstructions[i]->MarkAliasedLocalTemps(localTable, aliasedLocals, paramTable, aliasedParams); + + if (mTrueJump) mTrueJump->MarkAliasedLocalTemps(localTable, aliasedLocals, paramTable, aliasedParams); + if (mFalseJump) mFalseJump->MarkAliasedLocalTemps(localTable, aliasedLocals, paramTable, aliasedParams); + } +} + +bool InterCodeBasicBlock::PropagateNonLocalUsedConstTemps(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + int i = 0; + while (i < mInstructions.Size()) + { + InterInstruction* ins(mInstructions[i]); + if (ins->mCode == IC_CONSTANT && ins->mSingleAssignment) + { + int ttemp = ins->mDst.mTemp; + InterCodeBasicBlock* target = this; + while (target && !target->mLocalUsedTemps[ttemp]) + { + InterCodeBasicBlock* ttarget = nullptr; + + if (!target->mFalseJump) + ttarget = target->mTrueJump; + else if (!target->mFalseJump->mFalseJump && target->mFalseJump->mTrueJump == target->mTrueJump && !target->mFalseJump->mLocalUsedTemps[ttemp]) + ttarget = target->mTrueJump; + else if (!target->mTrueJump->mFalseJump && target->mTrueJump->mTrueJump == target->mFalseJump && !target->mTrueJump->mLocalUsedTemps[ttemp]) + ttarget = target->mFalseJump; + + while (ttarget && ttarget->mLoopHead) + { + if (ttarget->mFalseJump == ttarget && !ttarget->mLocalUsedTemps[ttemp]) + ttarget = ttarget->mTrueJump; + else if (ttarget->mTrueJump == ttarget && !ttarget->mLocalUsedTemps[ttemp]) + ttarget = ttarget->mFalseJump; + else + ttarget = nullptr; + } + + target = ttarget; + } + + if (target && this != target) + { + target->mInstructions.Insert(0, ins); + mInstructions.Remove(i); + changed = true; + } + else + i++; + } + else + i++; + } + + if (mTrueJump && mTrueJump->PropagateNonLocalUsedConstTemps()) + changed = true; + if (mFalseJump && mFalseJump->PropagateNonLocalUsedConstTemps()) + changed = true; + } + + return changed; +} + +void InterCodeBasicBlock::CollectAllUsedDefinedTemps(NumberSet& defined, NumberSet& used) +{ + if (!mVisited) + { + mVisited = true; + + for (int i = 0; i < mInstructions.Size(); i++) + { + InterInstruction* ins(mInstructions[i]); + + if (ins->mDst.mTemp >= 0) + defined += ins->mDst.mTemp; + for (int j = 0; j < ins->mNumOperands; j++) + { + if (ins->mSrc[j].mTemp >= 0) + used += ins->mSrc[j].mTemp; + } + } + + if (mTrueJump) mTrueJump->CollectAllUsedDefinedTemps(defined, used); + if (mFalseJump) mFalseJump->CollectAllUsedDefinedTemps(defined, used); + } +} + +void InterCodeBasicBlock::CollectLocalUsedTemps(int numTemps) +{ + if (!mVisited) + { + mVisited = true; + + mLocalUsedTemps.Reset(numTemps); + mLocalModifiedTemps.Reset(numTemps); + + for (int i = 0; i < mInstructions.Size(); i++) + { + InterInstruction* ins(mInstructions[i]); + + if (ins->mDst.mTemp >= 0) + mLocalModifiedTemps += ins->mDst.mTemp; + for (int j = 0; j < ins->mNumOperands; j++) + { + if (ins->mSrc[j].mTemp >= 0) + mLocalUsedTemps += ins->mSrc[j].mTemp; + } + } + + if (mTrueJump) mTrueJump->CollectLocalUsedTemps(numTemps); + if (mFalseJump) mFalseJump->CollectLocalUsedTemps(numTemps); + } +} + +void InterCodeBasicBlock::CollectConstTemps(GrowingInstructionPtrArray& ctemps, NumberSet& assignedTemps) +{ + int i; + + if (!mVisited) + { + mVisited = true; + + GrowingInstructionPtrArray ltemps(nullptr); + + for (i = 0; i < mInstructions.Size(); i++) + { + mInstructions[i]->ConstantFolding(); + mInstructions[i]->PropagateConstTemps(ltemps); + + int ttemp = mInstructions[i]->mDst.mTemp; + if (ttemp >= 0) + { + if (mInstructions[i]->mCode == IC_CONSTANT) + ltemps[ttemp] = mInstructions[i]; + else + ltemps[ttemp] = nullptr; + + if (assignedTemps[ttemp]) + ctemps[ttemp] = nullptr; + else + { + assignedTemps += ttemp; + if (mInstructions[i]->mCode == IC_CONSTANT) + ctemps[ttemp] = mInstructions[i]; + } + } + } + + if (mTrueJump) mTrueJump->CollectConstTemps(ctemps, assignedTemps); + if (mFalseJump) mFalseJump->CollectConstTemps(ctemps, assignedTemps); + } +} + +bool InterCodeBasicBlock::PropagateVariableCopy(const GrowingInstructionPtrArray& ctemps, const GrowingVariableArray& staticVars, const NumberSet& aliasedLocals, const NumberSet& aliasedParams) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + GrowingInstructionPtrArray ltemps(nullptr); + + for (int i = 0; i < mInstructions.Size(); i++) + { + InterInstruction* ins(mInstructions[i]); + + int j; + + if (ins->mDst.mTemp >= 0) + { + j = 0; + for (int k = 0; k < ltemps.Size(); k++) + { + if (ltemps[k]->mSrc[0].mTemp != ins->mDst.mTemp && ltemps[k]->mSrc[1].mTemp != ins->mDst.mTemp) + { + ltemps[j++] = ltemps[k]; + } + } + ltemps.SetSize(j); + } + + switch (ins->mCode) + { + case IC_LOAD: + for (int k = 0; k < ltemps.Size(); k++) + { + if (SameMemSegment(ltemps[k]->mSrc[1], ins->mSrc[0])) + { + ins->mSrc[0].mMemory = ltemps[k]->mSrc[0].mMemory; + ins->mSrc[0].mTemp = ltemps[k]->mSrc[0].mTemp; + ins->mSrc[0].mVarIndex = ltemps[k]->mSrc[0].mVarIndex; + if (ltemps[k]->mSrc[0].mStride != ltemps[k]->mSrc[1].mStride) + ins->mSrc[0].mIntConst = (ins->mSrc[0].mIntConst - ltemps[k]->mSrc[1].mIntConst) / ltemps[k]->mSrc[1].mStride * ltemps[k]->mSrc[0].mStride + ltemps[k]->mSrc[0].mIntConst; + else + ins->mSrc[0].mIntConst = ins->mSrc[0].mIntConst - ltemps[k]->mSrc[1].mIntConst + ltemps[k]->mSrc[0].mIntConst; + ins->mSrc[0].mLinkerObject = ltemps[k]->mSrc[0].mLinkerObject; + ins->mSrc[0].mStride = ltemps[k]->mSrc[0].mStride; + changed = true; + } + } + + break; + + case IC_STORE: + + j = 0; + for (int k = 0; k < ltemps.Size(); k++) + { + if (!CollidingMem(ltemps[k], ins)) + { + ltemps[j++] = ltemps[k]; + } + } + ltemps.SetSize(j); + break; + + case IC_FILL: + j = 0; + for (int k = 0; k < ltemps.Size(); k++) + { + if (!CollidingMem(ltemps[k], ins)) + { + ltemps[j++] = ltemps[k]; + } + } + ltemps.SetSize(j); + break; + + + case IC_COPY: + if (!ins->mVolatile) + { + for (int k = 0; k < ltemps.Size(); k++) + { + if (SameMemAndSize(ltemps[k]->mSrc[1], ins->mSrc[0])) + { + ins->mSrc[0] = ltemps[k]->mSrc[0]; + changed = true; + } + } + } + + j = 0; + for (int k = 0; k < ltemps.Size(); k++) + { + if (!CollidingMem(ltemps[k], ins)) + { + ltemps[j++] = ltemps[k]; + } + } + if (!ins->mVolatile) + { + ltemps.SetSize(j); + ltemps.Push(ins); + } + break; + + case IC_CALL: + case IC_CALL_NATIVE: + if (!ins->mNoSideEffects) + { + j = 0; + for (int k = 0; k < ltemps.Size(); k++) + { + if (ltemps[k]->mSrc[0].mTemp < 0 && ltemps[k]->mSrc[1].mTemp < 0 && + (ltemps[k]->mSrc[0].mMemory == IM_LOCAL && !aliasedLocals[ltemps[k]->mSrc[0].mVarIndex] || + (ltemps[k]->mSrc[0].mMemory == IM_PARAM || ltemps[k]->mSrc[0].mMemory == IM_FPARAM) && !aliasedParams[ltemps[k]->mSrc[0].mVarIndex]) && + (ltemps[k]->mSrc[1].mMemory == IM_LOCAL && !aliasedLocals[ltemps[k]->mSrc[1].mVarIndex] || + (ltemps[k]->mSrc[1].mMemory == IM_PARAM || ltemps[k]->mSrc[1].mMemory == IM_FPARAM) && !aliasedParams[ltemps[k]->mSrc[1].mVarIndex])) + { + ltemps[j++] = ltemps[k]; + } + } + ltemps.SetSize(j); + } + break; + } + + } + + if (mTrueJump && mTrueJump->PropagateVariableCopy(ltemps, staticVars, aliasedLocals, aliasedParams)) + changed = true; + if (mFalseJump && mFalseJump->PropagateVariableCopy(ltemps, staticVars, aliasedLocals, aliasedParams)) + changed = true; + } + + return changed; +} + +bool InterCodeBasicBlock::EarlyBranchElimination(const GrowingInstructionPtrArray& ctemps) +{ + bool changed = false; + + int i; + + if (!mVisited) + { + mVisited = true; + + GrowingInstructionPtrArray temps(ctemps); + if (mNumEntries > 1) + temps.Clear(); + + int sz = mInstructions.Size(); + for (i = 0; i < sz; i++) + { + InterInstruction* ins = mInstructions[i]; + if (ins->mDst.mTemp >= 0) + { + if (ins->mCode == IC_UNARY_OPERATOR && temps[ins->mSrc[0].mTemp]) + { + ins->mConst = OperandConstantFolding(ins->mOperator, temps[ins->mSrc[0].mTemp]->mConst, temps[ins->mSrc[0].mTemp]->mConst); + ins->mCode = IC_CONSTANT; + ins->mNumOperands = 0; + ins->mSrc[0].mTemp = -1; + } + else if (ins->mCode == IC_BINARY_OPERATOR && temps[ins->mSrc[0].mTemp] && temps[ins->mSrc[1].mTemp]) + { + ins->mConst = OperandConstantFolding(ins->mOperator, temps[ins->mSrc[1].mTemp]->mConst, temps[ins->mSrc[0].mTemp]->mConst); + ins->mCode = IC_CONSTANT; + ins->mNumOperands = 0; + ins->mSrc[0].mTemp = -1; + } + else if (ins->mCode == IC_RELATIONAL_OPERATOR && temps[ins->mSrc[0].mTemp] && temps[ins->mSrc[1].mTemp]) + { + ins->mConst = OperandConstantFolding(ins->mOperator, temps[ins->mSrc[1].mTemp]->mConst, temps[ins->mSrc[0].mTemp]->mConst); + ins->mCode = IC_CONSTANT; + ins->mNumOperands = 0; + ins->mSrc[0].mTemp = -1; + } + + if (ins->mCode == IC_CONSTANT) + temps[mInstructions[i]->mDst.mTemp] = ins; + else + temps[mInstructions[i]->mDst.mTemp] = nullptr; + } + } + + if (sz > 0 && mInstructions[sz - 1]->mCode == IC_BRANCH && temps[mInstructions[sz - 1]->mSrc[0].mTemp]) + { + InterInstruction* cins = temps[mInstructions[sz - 1]->mSrc[0].mTemp]; + + if (cins->mConst.mType == IT_BOOL) + { + mInstructions[sz - 1]->mCode = IC_JUMP; + mInstructions[sz - 1]->mNumOperands = 0; + + if (cins->mConst.mIntConst) + mFalseJump->mNumEntries--; + else + { + mTrueJump->mNumEntries--; + mTrueJump = mFalseJump; + } + mFalseJump = nullptr; + changed = true; + } + } + + if (mTrueJump && mTrueJump->EarlyBranchElimination(temps)) + changed = true; + if (mFalseJump && mFalseJump->EarlyBranchElimination(temps)) + changed = true; + } + + return changed; +} + +bool InterCodeBasicBlock::PropagateNonNullPointers(const NumberSet& vtemps) +{ + bool changed = false; + + int i; + + if (!mVisited) + { + mVisited = true; + + NumberSet ltemps(vtemps); + + if (mLoopHead) + { + if (mNumEntries == 2 && (mTrueJump == this || mFalseJump == this)) + { + for (i = 0; i < mInstructions.Size(); i++) + if (mInstructions[i]->mDst.mTemp >= 0) + ltemps -= mInstructions[i]->mDst.mTemp; + } + else + ltemps.Clear(); + } + else if (mNumEntries > 0) + { + if (mNumEntered > 0) + { + for (int i = 0; i < ltemps.Size(); i++) + { + if (!mMergeTemps[i]) + ltemps -= i; + } + } + + mNumEntered++; + + if (mNumEntered < mNumEntries) + { + mMergeTemps = ltemps; + return false; + } + } + + for (i = 0; i < mInstructions.Size(); i++) + { + InterInstruction* ins = mInstructions[i]; + if (ins->mDst.mTemp >= 0) + { + if (ins->mCode == IC_CONSTANT && ins->mDst.mType == IT_POINTER) + { + if (ins->mConst.mMemory != IM_ABSOLUTE || ins->mConst.mIntConst != 0) + ltemps += ins->mDst.mTemp; + else + ltemps -= ins->mDst.mTemp; + } + else + { + ltemps -= ins->mDst.mTemp; + if (ins->mCode == IC_LOAD && ins->mSrc[0].mTemp >= 0) + ltemps += ins->mSrc[0].mTemp; + else if (ins->mCode == IC_RELATIONAL_OPERATOR) + { + if (ins->mSrc[0].mTemp < 0 && ins->mSrc[0].mType == IT_POINTER && ins->mSrc[0].mMemory == IM_ABSOLUTE && ins->mSrc[0].mIntConst == 0 && ins->mSrc[1].mTemp >= 0 && ltemps[ins->mSrc[1].mTemp] || + ins->mSrc[1].mTemp < 0 && ins->mSrc[1].mType == IT_POINTER && ins->mSrc[1].mMemory == IM_ABSOLUTE && ins->mSrc[1].mIntConst == 0 && ins->mSrc[0].mTemp >= 0 && ltemps[ins->mSrc[0].mTemp]) + { + if (ins->mOperator == IA_CMPEQ) + { + ins->mCode = IC_CONSTANT; + ins->mConst.mType = IT_BOOL; + ins->mConst.mIntConst = 0; + ins->mNumOperands = 0; + changed = true; + } + else if (ins->mOperator == IA_CMPNE) + { + ins->mCode = IC_CONSTANT; + ins->mConst.mType = IT_BOOL; + ins->mConst.mIntConst = 1; + ins->mNumOperands = 0; + changed = true; + } + } + } + } + } + else if (ins->mCode == IC_STORE && ins->mSrc[1].mTemp >= 0) + ltemps += ins->mSrc[1].mTemp; + } + + if (mTrueJump && mTrueJump->PropagateNonNullPointers(ltemps)) + changed = true; + if (mFalseJump && mFalseJump->PropagateNonNullPointers(ltemps)) + changed = true; + } + + return changed; +} + +bool InterCodeBasicBlock::PropagateConstCompareResults(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + int sz = mInstructions.Size(); + if (sz >= 2 && mFalseJump && + mInstructions[sz - 1]->mCode == IC_BRANCH && + mInstructions[sz - 2]->mCode == IC_RELATIONAL_OPERATOR && (mInstructions[sz - 2]->mOperator == IA_CMPEQ || mInstructions[sz - 2]->mOperator == IA_CMPNE) && + mInstructions[sz - 1]->mSrc[0].mTemp == mInstructions[sz - 2]->mDst.mTemp) + { + InterCodeBasicBlock* cblock = mTrueJump; + if (mInstructions[sz - 2]->mOperator == IA_CMPNE) + cblock = mFalseJump; + + if (cblock->mNumEntries == 1) + { + if (mInstructions[sz - 2]->mSrc[1].mTemp < 0 && mInstructions[sz - 2]->mSrc[0].mTemp >= 0 && cblock->mEntryRequiredTemps[mInstructions[sz - 2]->mSrc[0].mTemp] && mInstructions[sz - 2]->mSrc[0].mTemp != mInstructions[sz - 2]->mDst.mTemp) + { + InterInstruction* cins = new InterInstruction(mInstructions[sz - 2]->mLocation, IC_CONSTANT); + cins->mDst = mInstructions[sz - 2]->mSrc[0]; + cins->mConst = mInstructions[sz - 2]->mSrc[1]; + cblock->mInstructions.Insert(0, cins); + changed = true; + } + else if (mInstructions[sz - 2]->mSrc[0].mTemp < 0 && mInstructions[sz - 2]->mSrc[1].mTemp >= 0 && cblock->mEntryRequiredTemps[mInstructions[sz - 2]->mSrc[1].mTemp] && mInstructions[sz - 2]->mSrc[1].mTemp != mInstructions[sz - 2]->mDst.mTemp) + { + InterInstruction* cins = new InterInstruction(mInstructions[sz - 2]->mLocation, IC_CONSTANT); + cins->mDst = mInstructions[sz - 2]->mSrc[1]; + cins->mConst = mInstructions[sz - 2]->mSrc[0]; + cblock->mInstructions.Insert(0, cins); + changed = true; + } + } + } + + if (mTrueJump && mTrueJump->PropagateConstCompareResults()) + changed = true; + if (mFalseJump && mFalseJump->PropagateConstCompareResults()) + changed = true; + } + + return changed; +} + +bool InterCodeBasicBlock::ForwardConstTemps(const GrowingInstructionPtrArray& ctemps) +{ + bool changed = false; + + int i; + + if (!mVisited) + { + mVisited = true; + + GrowingInstructionPtrArray ltemps(ctemps); + + if (mLoopHead) + { + if (mNumEntries == 2 && (mTrueJump == this || mFalseJump == this)) + { + for (i = 0; i < mInstructions.Size(); i++) + if (mInstructions[i]->mDst.mTemp >= 0) + ltemps[mInstructions[i]->mDst.mTemp] = nullptr; + } + else + ltemps.Clear(); + } + else if (mNumEntries > 0) + { + if (mNumEntered > 0) + { + for (int i = 0; i < ltemps.Size(); i++) + { + if (mMergeTValues[i] != ltemps[i]) + ltemps[i] = nullptr; + } + } + + mNumEntered++; + + if (mNumEntered < mNumEntries) + { + mMergeTValues = ltemps; + return false; + } + } + + for (i = 0; i < mInstructions.Size(); i++) + { + if (mInstructions[i]->PropagateConstTemps(ltemps)) + changed = true; + if (mInstructions[i]->mDst.mTemp >= 0) + { + if (mInstructions[i]->mCode == IC_CONSTANT) + ltemps[mInstructions[i]->mDst.mTemp] = mInstructions[i]; + else + ltemps[mInstructions[i]->mDst.mTemp] = nullptr; + } + } + + if (mTrueJump && mTrueJump->ForwardConstTemps(ltemps)) + changed = true; + if (mFalseJump && mFalseJump->ForwardConstTemps(ltemps)) + changed = true; + } + + return changed; +} + +bool InterCodeBasicBlock::PropagateConstTemps(const GrowingInstructionPtrArray& ctemps) +{ + bool changed = false; + + int i; + + if (!mVisited) + { + mVisited = true; + + for (i = 0; i < mInstructions.Size(); i++) + { + if (mInstructions[i]->PropagateConstTemps(ctemps)) + changed = true; + } + + if (mTrueJump && mTrueJump->PropagateConstTemps(ctemps)) + changed = true; + if (mFalseJump && mFalseJump->PropagateConstTemps(ctemps)) + changed = true; + } + + return changed; +} + +bool InterCodeBasicBlock::CombineIndirectAddressing(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + GrowingInstructionPtrArray tvalue(nullptr); + + for (int i = 0; i < mInstructions.Size(); i++) + { + InterInstruction* lins = mInstructions[i]; + InterInstruction* tins = nullptr; + + if (lins->mCode == IC_LEA && lins->mSrc[0].mTemp >= 0 && lins->mSrc[1].mTemp < 0 && + !lins->mSrc[0].IsUByte() && + (lins->mSrc[1].mMemory == IM_ABSOLUTE || lins->mSrc[1].mMemory == IM_GLOBAL || lins->mSrc[1].mMemory == IM_LOCAL)) + { + int j = 0; + while (j < tvalue.Size() && + !(tvalue[j]->mSrc[0].mTemp == lins->mSrc[0].mTemp && + tvalue[j]->mSrc[1].mTemp < 0 && + tvalue[j]->mSrc[1].mMemory == lins->mSrc[1].mMemory && + tvalue[j]->mSrc[1].mVarIndex == lins->mSrc[1].mVarIndex && + tvalue[j]->mSrc[1].mIntConst <= lins->mSrc[1].mIntConst && + tvalue[j]->mSrc[1].mIntConst + 256 > lins->mSrc[1].mIntConst)) + j++; + + if (j < tvalue.Size()) + { + int64 offset = lins->mSrc[1].mIntConst - tvalue[j]->mSrc[1].mIntConst; + lins->mSrc[1] = tvalue[j]->mDst; + lins->mSrc[0].mTemp = -1; + lins->mSrc[0].mIntConst = offset; + changed = true; + } + else + tins = lins; + } + + if (HasSideEffect(lins->mCode)) + tvalue.SetSize(0); + else if (lins->mDst.mTemp >= 0) + { + int j = 0; + while (j < tvalue.Size()) + { + if (tvalue[j]->ReferencesTemp(lins->mDst.mTemp)) + tvalue.Remove(j); + else + j++; + } + } + + if (tins) + tvalue.Push(tins); + } + + if (mTrueJump && mTrueJump->CombineIndirectAddressing()) + changed = true; + if (mFalseJump && mFalseJump->CombineIndirectAddressing()) + changed = true; + } + + return changed; +} + +void InterCodeBasicBlock::SimplifyIntegerRangeRelops(void) +{ + if (!mVisited) + { + mVisited = true; + +#if 1 + int sz = mInstructions.Size(); + if (sz >= 2 && mInstructions[sz - 1]->mCode == IC_BRANCH && mInstructions[sz - 2]->mCode == IC_RELATIONAL_OPERATOR && + mInstructions[sz - 2]->mDst.mTemp == mInstructions[sz - 1]->mSrc[0].mTemp && + IsScalarType(mInstructions[sz - 2]->mSrc[0].mType) && IsScalarType(mInstructions[sz - 2]->mSrc[1].mType)) + { + InterInstruction* cins = mInstructions[sz - 2]; + + bool constFalse = false, constTrue = false; + + if ((cins->mSrc[1].mTemp < 0 || (cins->mSrc[1].mRange.mMaxState == IntegerValueRange::S_BOUND && cins->mSrc[1].mRange.mMinState == IntegerValueRange::S_BOUND)) && + (cins->mSrc[0].mTemp < 0 || (cins->mSrc[0].mRange.mMaxState == IntegerValueRange::S_BOUND && cins->mSrc[0].mRange.mMinState == IntegerValueRange::S_BOUND))) + { + bool signedvalid = cins->mSrc[0].mRange.mMaxValue <= SignedTypeMax(cins->mSrc[0].mType) && + cins->mSrc[1].mRange.mMaxValue <= SignedTypeMax(cins->mSrc[1].mType); + + switch (cins->mOperator) + { + case IA_CMPEQ: + if (cins->mSrc[0].mType == IT_INT8 && cins->mSrc[1].mType == IT_INT8 && + (cins->mSrc[0].mRange.mMinValue < 0 && cins->mSrc[1].mRange.mMaxValue >= 256 + cins->mSrc[0].mRange.mMinValue || + cins->mSrc[1].mRange.mMinValue < 0 && cins->mSrc[0].mRange.mMaxValue >= 256 + cins->mSrc[1].mRange.mMinValue)) + ; + else if (cins->mSrc[0].mType == IT_INT16 && cins->mSrc[1].mType == IT_INT16 && + (cins->mSrc[0].mRange.mMinValue < 0 && cins->mSrc[1].mRange.mMaxValue >= 65536 + cins->mSrc[0].mRange.mMinValue || + cins->mSrc[1].mRange.mMinValue < 0 && cins->mSrc[0].mRange.mMaxValue >= 65536 + cins->mSrc[1].mRange.mMinValue)) + ; + else if (cins->mSrc[0].mType == IT_INT32 && cins->mSrc[1].mType == IT_INT32 && + (cins->mSrc[0].mRange.mMinValue < 0 && cins->mSrc[1].mRange.mMaxValue >= 0x100000000ll + cins->mSrc[0].mRange.mMinValue || + cins->mSrc[1].mRange.mMinValue < 0 && cins->mSrc[0].mRange.mMaxValue >= 0x100000000ll + cins->mSrc[1].mRange.mMinValue)) + ; + else if (cins->mSrc[1].mRange.mMaxValue < cins->mSrc[0].mRange.mMinValue || cins->mSrc[1].mRange.mMinValue > cins->mSrc[0].mRange.mMaxValue) + constFalse = true; + break; + case IA_CMPNE: + if (cins->mSrc[0].mType == IT_INT8 && cins->mSrc[1].mType == IT_INT8 && + (cins->mSrc[0].mRange.mMinValue < 0 && cins->mSrc[1].mRange.mMaxValue >= 256 + cins->mSrc[0].mRange.mMinValue || + cins->mSrc[1].mRange.mMinValue < 0 && cins->mSrc[0].mRange.mMaxValue >= 256 + cins->mSrc[1].mRange.mMinValue)) + ; + else if (cins->mSrc[0].mType == IT_INT16 && cins->mSrc[1].mType == IT_INT16 && + (cins->mSrc[0].mRange.mMinValue < 0 && cins->mSrc[1].mRange.mMaxValue >= 65536 + cins->mSrc[0].mRange.mMinValue || + cins->mSrc[1].mRange.mMinValue < 0 && cins->mSrc[0].mRange.mMaxValue >= 65536 + cins->mSrc[1].mRange.mMinValue)) + ; + else if (cins->mSrc[0].mType == IT_INT32 && cins->mSrc[1].mType == IT_INT32 && + (cins->mSrc[0].mRange.mMinValue < 0 && cins->mSrc[1].mRange.mMaxValue >= 0x100000000ll + cins->mSrc[0].mRange.mMinValue || + cins->mSrc[1].mRange.mMinValue < 0 && cins->mSrc[0].mRange.mMaxValue >= 0x100000000ll + cins->mSrc[1].mRange.mMinValue)) + ; + else if (cins->mSrc[1].mRange.mMaxValue < cins->mSrc[0].mRange.mMinValue || cins->mSrc[1].mRange.mMinValue > cins->mSrc[0].mRange.mMaxValue) + constTrue = true; + break; + case IA_CMPLS: + if (signedvalid) + { + if (cins->mSrc[1].mRange.mMaxValue < cins->mSrc[0].mRange.mMinValue) + constTrue = true; + else if (cins->mSrc[1].mRange.mMinValue >= cins->mSrc[0].mRange.mMaxValue) + constFalse = true; + } + break; + case IA_CMPLU: + if (cins->mSrc[0].mTemp < 0 && cins->mSrc[0].mIntConst == 0) + { + constFalse = true; + } + else if (cins->mSrc[1].IsPositive() && cins->mSrc[0].IsPositive()) + { + if (cins->mSrc[1].mRange.mMaxValue < cins->mSrc[0].mRange.mMinValue) + constTrue = true; + else if (cins->mSrc[1].mRange.mMinValue >= cins->mSrc[0].mRange.mMaxValue) + constFalse = true; + } + break; + case IA_CMPLES: + if (signedvalid) + { + if (cins->mSrc[1].mRange.mMaxValue <= cins->mSrc[0].mRange.mMinValue) + constTrue = true; + else if (cins->mSrc[1].mRange.mMinValue > cins->mSrc[0].mRange.mMaxValue) + constFalse = true; + } + break; + case IA_CMPLEU: + if (cins->mSrc[1].IsPositive() && cins->mSrc[0].IsPositive()) + { + if (cins->mSrc[1].mRange.mMaxValue <= cins->mSrc[0].mRange.mMinValue) + constTrue = true; + else if (cins->mSrc[1].mRange.mMinValue > cins->mSrc[0].mRange.mMaxValue) + constFalse = true; + } + break; + case IA_CMPGS: + if (signedvalid) + { + if (cins->mSrc[1].mRange.mMinValue > cins->mSrc[0].mRange.mMaxValue) + constTrue = true; + else if (cins->mSrc[1].mRange.mMaxValue <= cins->mSrc[0].mRange.mMinValue) + constFalse = true; + } + break; + case IA_CMPGU: + if (cins->mSrc[1].mTemp < 0 && cins->mSrc[1].mIntConst == 0) + { + constFalse = true; + } + else if (cins->mSrc[1].IsPositive() && cins->mSrc[1].mRange.mMaxValue == 0) + { + constFalse = true; + } + else if (cins->mSrc[1].IsPositive() && cins->mSrc[0].IsPositive()) + { + if (cins->mSrc[1].mRange.mMinValue > cins->mSrc[0].mRange.mMaxValue) + constTrue = true; + else if (cins->mSrc[1].mRange.mMaxValue <= cins->mSrc[0].mRange.mMinValue) + constFalse = true; + } + break; + case IA_CMPGES: + if (signedvalid) + { + if (cins->mSrc[1].mRange.mMinValue >= cins->mSrc[0].mRange.mMaxValue) + constTrue = true; + else if (cins->mSrc[1].mRange.mMaxValue < cins->mSrc[0].mRange.mMinValue) + constFalse = true; + } + break; + case IA_CMPGEU: + if (cins->mSrc[1].IsPositive() && cins->mSrc[0].IsPositive()) + { + if (cins->mSrc[1].mRange.mMinValue >= cins->mSrc[0].mRange.mMaxValue) + constTrue = true; + else if (cins->mSrc[1].mRange.mMaxValue < cins->mSrc[0].mRange.mMinValue) + constFalse = true; + } + break; + } + } + + + if (constTrue || constFalse) + { + cins->mCode = IC_CONSTANT; + cins->mConst.mType = IT_BOOL; + cins->mConst.mIntConst = constTrue ? 1 : 0; + cins->mNumOperands = 0; + } + else + { + switch (cins->mOperator) + { + case IA_CMPLS: + if (cins->mSrc[0].IsUnsigned() && cins->mSrc[1].IsUnsigned()) + cins->mOperator = IA_CMPLU; + break; + case IA_CMPLES: + if (cins->mSrc[0].IsUnsigned() && cins->mSrc[1].IsUnsigned()) + cins->mOperator = IA_CMPLEU; + break; + case IA_CMPGS: + if (cins->mSrc[0].IsUnsigned() && cins->mSrc[1].IsUnsigned()) + cins->mOperator = IA_CMPGU; + break; + case IA_CMPGES: + if (cins->mSrc[0].IsUnsigned() && cins->mSrc[1].IsUnsigned()) + cins->mOperator = IA_CMPGEU; + break; + } + } + } +#endif + + if (sz >= 1 && mInstructions[sz - 1]->mCode == IC_BRANCH && mInstructions[sz - 1]->mSrc[0].mTemp < 0) + { + InterInstruction* bins = mInstructions[sz - 1]; + + if (bins->mSrc[0].mIntConst) + { + mFalseJump->mNumEntries--; + mFalseJump->WarnUnreachable(); + mFalseJump = nullptr; + bins->mCode = IC_JUMP; + bins->mSrc[0].mTemp = -1; + bins->mNumOperands = 0; + } + else + { + mTrueJump->mNumEntries--; + mTrueJump->WarnUnreachable(); + mTrueJump = mFalseJump; + mFalseJump = nullptr; + bins->mCode = IC_JUMP; + bins->mSrc[0].mTemp = -1; + bins->mNumOperands = 0; + } + } + + if (sz >= 1 && mInstructions[sz - 1]->mCode == IC_BRANCH && mInstructions[sz - 1]->mSrc[0].mRange.IsBound() && mInstructions[sz - 1]->mSrc[0].mRange.mMinValue > 0 && mInstructions[sz - 1]->mSrc[0].mRange.mMaxValue < 255) + { + InterInstruction* bins = mInstructions[sz - 1]; + + mFalseJump->mNumEntries--; + mFalseJump->WarnUnreachable(); + mFalseJump = nullptr; + bins->mCode = IC_JUMP; + bins->mSrc[0].mTemp = -1; + bins->mNumOperands = 0; + } + + if (sz >= 2 && mInstructions[sz - 1]->mCode == IC_BRANCH && mInstructions[sz - 2]->mCode == IC_CONSTANT && mInstructions[sz - 2]->mDst.mTemp == mInstructions[sz - 1]->mSrc[0].mTemp) + { + InterInstruction* bins = mInstructions[sz - 1]; + + if (mInstructions[sz - 2]->mConst.mIntConst) + { + mFalseJump->mNumEntries--; + mFalseJump->WarnUnreachable(); + mFalseJump = nullptr; + bins->mCode = IC_JUMP; + bins->mSrc[0].mTemp = -1; + bins->mNumOperands = 0; + } + else + { + mTrueJump->mNumEntries--; + mTrueJump->WarnUnreachable(); + mTrueJump = mFalseJump; + mFalseJump = nullptr; + bins->mCode = IC_JUMP; + bins->mSrc[0].mTemp = -1; + bins->mNumOperands = 0; + } + } + +#if 1 + if (sz >= 2 && mInstructions[sz - 1]->mCode == IC_BRANCH && mInstructions[sz - 2]->mCode == IC_RELATIONAL_OPERATOR && mInstructions[sz - 2]->mDst.mTemp == mInstructions[sz - 1]->mSrc[0].mTemp && + mInstructions[sz - 2]->mSrc[0].mTemp >= 0 && mInstructions[sz - 2]->mSrc[1].mTemp >= 0) + { + if (mFalseJump->mNumEntries == 1 && mFalseJump->mInstructions.Size() == 2 && mFalseJump->mInstructions[1]->mCode == IC_BRANCH && + mFalseJump->mInstructions[0]->mCode == IC_RELATIONAL_OPERATOR && mFalseJump->mInstructions[0]->mDst.mTemp == mFalseJump->mInstructions[1]->mSrc[0].mTemp) + { + if (mInstructions[sz - 2]->mOperator == IA_CMPLS && mFalseJump->mInstructions[0]->mOperator == IA_CMPGS && + mInstructions[sz - 2]->mSrc[0].mTemp == mFalseJump->mInstructions[0]->mSrc[0].mTemp && + mInstructions[sz - 2]->mSrc[1].mTemp == mFalseJump->mInstructions[0]->mSrc[1].mTemp) + { + mFalseJump->mInstructions[0]->mOperator = IA_CMPNE; + } + else if (mInstructions[sz - 2]->mOperator == IA_CMPGS && mFalseJump->mInstructions[0]->mOperator == IA_CMPLS && + mInstructions[sz - 2]->mSrc[0].mTemp == mFalseJump->mInstructions[0]->mSrc[0].mTemp && + mInstructions[sz - 2]->mSrc[1].mTemp == mFalseJump->mInstructions[0]->mSrc[1].mTemp) + { + mFalseJump->mInstructions[0]->mOperator = IA_CMPNE; + } + else if (mInstructions[sz - 2]->mOperator == IA_CMPLS && mFalseJump->mInstructions[0]->mOperator == IA_CMPLS && + mInstructions[sz - 2]->mSrc[0].mTemp == mFalseJump->mInstructions[0]->mSrc[1].mTemp && + mInstructions[sz - 2]->mSrc[1].mTemp == mFalseJump->mInstructions[0]->mSrc[0].mTemp) + { + mFalseJump->mInstructions[0]->mOperator = IA_CMPNE; + } + else if (mInstructions[sz - 2]->mOperator == IA_CMPGS && mFalseJump->mInstructions[0]->mOperator == IA_CMPGS && + mInstructions[sz - 2]->mSrc[0].mTemp == mFalseJump->mInstructions[0]->mSrc[1].mTemp && + mInstructions[sz - 2]->mSrc[1].mTemp == mFalseJump->mInstructions[0]->mSrc[0].mTemp) + { + mFalseJump->mInstructions[0]->mOperator = IA_CMPNE; + } + } + } +#endif + +#if 1 + for (int i = 0; i < sz; i++) + { + if (i + 1 < sz) + { + if ( + mInstructions[i + 0]->mCode == IC_LEA && mInstructions[i + 0]->mSrc[1].mTemp >= 0 && + mInstructions[i + 1]->mCode == IC_LEA && + mInstructions[i + 0]->mSrc[1].mTemp != mInstructions[i + 0]->mDst.mTemp && + mInstructions[i + 1]->mSrc[1].mTemp == mInstructions[i + 0]->mDst.mTemp && mInstructions[i + 1]->mSrc[1].mFinal && + mInstructions[i + 0]->mSrc[0].IsUByte() && mInstructions[i + 1]->mSrc[0].IsUByte() && + mInstructions[i + 0]->mSrc[0].mRange.mMaxValue + mInstructions[i + 1]->mSrc[0].mRange.mMaxValue < 256) + { + mInstructions[i + 0]->mCode = IC_BINARY_OPERATOR; + mInstructions[i + 0]->mOperator = IA_ADD; + + mInstructions[i + 1]->mSrc[1] = mInstructions[i + 0]->mSrc[1]; + mInstructions[i + 0]->mSrc[1] = mInstructions[i + 1]->mSrc[0]; + mInstructions[i + 1]->mSrc[0].mTemp = mInstructions[i + 0]->mDst.mTemp; + mInstructions[i + 0]->mDst.mType = IT_INT16; + mInstructions[i + 1]->mSrc[0].mType = IT_INT16; + mInstructions[i + 1]->mSrc[0].mRange.mMaxValue += mInstructions[i + 0]->mSrc[0].mRange.mMaxValue; + mInstructions[i + 0]->mDst.mRange = mInstructions[i + 1]->mSrc[0].mRange; + } + } + if (i + 2 < sz) + { + if ( + mInstructions[i + 0]->mCode == IC_LEA && mInstructions[i + 0]->mSrc[1].mTemp >= 0 && + mInstructions[i + 2]->mCode == IC_LEA && + mInstructions[i + 0]->mSrc[1].mTemp != mInstructions[i + 0]->mDst.mTemp && + mInstructions[i + 2]->mSrc[1].mTemp == mInstructions[i + 0]->mDst.mTemp && mInstructions[i + 2]->mSrc[1].mFinal && + mInstructions[i + 0]->mSrc[0].IsUByte() && mInstructions[i + 2]->mSrc[0].IsUByte() && + mInstructions[i + 0]->mSrc[0].mRange.mMaxValue + mInstructions[i + 2]->mSrc[0].mRange.mMaxValue < 256 && + + mInstructions[i + 1]->mCode == IC_CONVERSION_OPERATOR && mInstructions[i + 1]->mOperator == IA_EXT8TO16U && + mInstructions[i + 1]->mDst.mTemp != mInstructions[0]->mSrc[0].mTemp && + mInstructions[i + 1]->mDst.mTemp != mInstructions[0]->mSrc[1].mTemp&& + mInstructions[i + 1]->mDst.mTemp != mInstructions[0]->mDst.mTemp) + { + InterInstruction* ins = mInstructions[i + 1]; + mInstructions[i + 1] = mInstructions[i + 0]; + mInstructions[i + 0] = ins; + } + } +#if 1 + if (mInstructions[i]->mCode == IC_CONVERSION_OPERATOR && mInstructions[i]->mOperator == IA_EXT8TO16S && + mInstructions[i]->mSrc[0].IsUByte() && mInstructions[i]->mSrc[0].mRange.mMaxValue < 128) + { + mInstructions[i]->mOperator = IA_EXT8TO16U; + } + else if (mInstructions[i]->mCode == IC_BINARY_OPERATOR && mInstructions[i]->mOperator == IA_SAR && mInstructions[i]->mSrc[0].IsUnsigned() && mInstructions[i]->mSrc[1].IsUnsigned()) + { + mInstructions[i]->mOperator = IA_SHR; + mInstructions[i]->ConstantFolding(); + } + else if (mInstructions[i]->mCode == IC_BINARY_OPERATOR && mInstructions[i]->mOperator == IA_DIVS && mInstructions[i]->mSrc[0].IsUnsigned() && mInstructions[i]->mSrc[1].IsUnsigned()) + { + mInstructions[i]->mOperator = IA_DIVU; + mInstructions[i]->ConstantFolding(); + } + else if (mInstructions[i]->mCode == IC_BINARY_OPERATOR && mInstructions[i]->mOperator == IA_MODS && mInstructions[i]->mSrc[0].IsUnsigned() && mInstructions[i]->mSrc[1].IsUnsigned()) + { + mInstructions[i]->mOperator = IA_MODU; + mInstructions[i]->ConstantFolding(); + } + +#endif +#if 1 + // Check shortcut double jump + if (mTrueJump && mTrueJump->mInstructions.Size() == 1 && mTrueJump->mInstructions[0]->mCode == IC_BRANCH && + mTrueJump->mInstructions[0]->mSrc[0].mTemp >= 0 && + mTrueJump->mInstructions[0]->mSrc[0].mTemp < mTrueValueRange.Size() && + IsScalarType(mTrueJump->mInstructions[0]->mSrc[0].mType) && + mTrueValueRange[mTrueJump->mInstructions[0]->mSrc[0].mTemp].IsConstant()) + { + mTrueJump->mNumEntries--; + mTrueJump->mEntryBlocks.RemoveAll(this); + if (mTrueValueRange[mTrueJump->mInstructions[0]->mSrc[0].mTemp].mMaxValue == 0) + mTrueJump = mTrueJump->mFalseJump; + else + mTrueJump = mTrueJump->mTrueJump; + mTrueJump->mNumEntries++; + mTrueJump->mEntryBlocks.Push(this); + } + + if (mFalseJump && mFalseJump->mInstructions.Size() == 1 && mFalseJump->mInstructions[0]->mCode == IC_BRANCH && + mFalseJump->mInstructions[0]->mSrc[0].mTemp >= 0 && + mFalseJump->mInstructions[0]->mSrc[0].mTemp < mFalseValueRange.Size() && + IsScalarType(mFalseJump->mInstructions[0]->mSrc[0].mType) && + mFalseValueRange[mFalseJump->mInstructions[0]->mSrc[0].mTemp].IsConstant()) + { + mFalseJump->mNumEntries--; + mFalseJump->mEntryBlocks.RemoveAll(this); + if (mFalseValueRange[mFalseJump->mInstructions[0]->mSrc[0].mTemp].mMaxValue == 0) + mFalseJump = mFalseJump->mFalseJump; + else + mFalseJump = mFalseJump->mTrueJump; + mFalseJump->mNumEntries++; + mFalseJump->mEntryBlocks.Push(this); + } +#endif + } +#endif + if (mTrueJump) + mTrueJump->SimplifyIntegerRangeRelops(); + if (mFalseJump) + mFalseJump->SimplifyIntegerRangeRelops(); + } +} + + +bool InterCodeBasicBlock::InnerLoopIntegerRangeUpdate(void) +{ + bool changed = false; + + if (!mVisited) + { + if (mLoopHead) + { + ExpandingArray body; + body.Push(this); + bool innerLoop = true; + + for (int i = 0; i < mEntryBlocks.Size(); i++) + { + if (mEntryBlocks[i] != mLoopPrefix) + { + if (!mEntryBlocks[i]->CollectLoopBodyRecursive(this, body)) + innerLoop = false; + } + } + + NumberSet loopModifiedTemps(mEntryRequiredTemps.Size()), loopUsedTemps(mEntryRequiredTemps.Size()); + for (int i = 0; i < body.Size(); i++) + { + InterCodeBasicBlock* block = body[i]; + for (int j = 0; j < block->mInstructions.Size(); j++) + { + InterInstruction* ins = block->mInstructions[j]; + + if (ins->mDst.mTemp >= 0) + loopModifiedTemps += ins->mDst.mTemp; + for (int k = 0; k < ins->mNumOperands; k++) + if (ins->mSrc[k].mTemp >= 0) + loopUsedTemps += ins->mSrc[k].mTemp; + } + } + + loopUsedTemps -= loopModifiedTemps; + + bool firstEntry = true; + + for (int j = 0; j < mEntryBlocks.Size(); j++) + { + InterCodeBasicBlock* from = mEntryBlocks[j]; + + if (!body.Contains(from)) + { + GrowingIntegerValueRangeArray& range(this == from->mTrueJump ? from->mTrueValueRange : from->mFalseValueRange); + GrowingIntegerValueRangeArray& prange(this == from->mTrueJump ? from->mTrueParamValueRange : from->mFalseParamValueRange); + + if (range.Size()) + { + if (firstEntry) + { + firstEntry = false; + mProc->mLocalValueRange = range; + } + else + { + for (int i = 0; i < mProc->mLocalValueRange.Size(); i++) + mProc->mLocalValueRange[i].Merge(range[i], false, false); + } + } + } + } + + for (int i = 0; i < body.Size(); i++) + { + InterCodeBasicBlock* block = body[i]; + for (int j = 0; j < block->mInstructions.Size(); j++) + { + InterInstruction* ins = block->mInstructions[j]; + + for (int k = 0; k < ins->mNumOperands; k++) + { + int t = ins->mSrc[k].mTemp; + if (t >= 0 && !loopModifiedTemps[t]) + { + if (ins->mSrc[k].mRange.Limit(mProc->mLocalValueRange[t])) + changed = true; + } + } + } + } + } + + mVisited = true; + + if (mTrueJump && mTrueJump->InnerLoopIntegerRangeUpdate()) + changed = true; + if (mFalseJump && mFalseJump->InnerLoopIntegerRangeUpdate()) + changed = true; + } + + return changed; +} + + +bool InterCodeBasicBlock::BuildGlobalIntegerRangeSets(bool initial) +{ + bool changed = false; + + mNumEntered++; + if (!mLoopHead && mNumEntered < mEntryBlocks.Size()) + return false; + + assert(mProc->mLocalValueRange.Size() == mExitRequiredTemps.Size()); + assert(mLocalParamValueRange.Size() == mProc->mParamVars.Size()); + + bool firstEntry = true; + + for (int j = 0; j < mEntryBlocks.Size(); j++) + { + InterCodeBasicBlock* from = mEntryBlocks[j]; + GrowingIntegerValueRangeArray& range(this == from->mTrueJump ? from->mTrueValueRange : from->mFalseValueRange); + GrowingIntegerValueRangeArray& prange(this == from->mTrueJump ? from->mTrueParamValueRange : from->mFalseParamValueRange); + + if (range.Size()) + { + if (firstEntry) + { + firstEntry = false; + mProc->mLocalValueRange = range; + mLocalParamValueRange = prange; + } + else + { + for (int i = 0; i < mProc->mLocalValueRange.Size(); i++) + { + if (this != from || IsTempModified(i)) + mProc->mLocalValueRange[i].Merge(range[i], mLoopHead, initial); + } + for (int i = 0; i < mLocalParamValueRange.Size(); i++) + mLocalParamValueRange[i].Merge(prange[i], mLoopHead, initial); + } + } + } + + if (firstEntry) + { + mProc->mLocalValueRange.Clear(); + mLocalParamValueRange.Clear(); + } + + assert(mProc->mLocalValueRange.Size() == mExitRequiredTemps.Size()); + assert(mLocalParamValueRange.Size() == mProc->mParamVars.Size()); + + for (int i = 0; i < mProc->mLocalValueRange.Size(); i++) + if (mEntryValueRange[i].Weaker(mProc->mLocalValueRange[i])) + changed = true; + + for (int i = 0; i < mLocalParamValueRange.Size(); i++) + if (mEntryParamValueRange[i].Weaker(mLocalParamValueRange[i])) + changed = true; + + if (mVisited && mNumEntered >= 2 * mEntryBlocks.Size()) + return changed; + + if (mTrueJump && !mFalseJump) + { + for (int i = 0; i < mEntryMemoryValueSize.Size(); i++) + { + if (mEntryMemoryValueSize[i] != mTrueJump->mMemoryValueSize[i]) + changed = true; + } + } + + if (!mVisited || changed) + { + mVisited = true; + + if (changed) + { + for (int i = 0; i < mProc->mLocalValueRange.Size(); i++) + mEntryValueRange[i].Expand(mProc->mLocalValueRange[i]); + for (int i = 0; i < mLocalParamValueRange.Size(); i++) + mEntryParamValueRange[i].Expand(mLocalParamValueRange[i]); + +// mEntryValueRange = mLocalValueRange; +// mEntryParamValueRange = mLocalParamValueRange; + + UpdateLocalIntegerRangeSets(); + + } + + if (mTrueJump && mTrueJump->BuildGlobalIntegerRangeSets(initial)) + changed = true; + if (mFalseJump && mFalseJump->BuildGlobalIntegerRangeSets(initial)) + changed = true; + } + + return changed; +} + +void InterCodeBasicBlock::UnionIntegerRanges(const InterCodeBasicBlock* block) +{ + if (mEntryValueRange.Size() > 0) + { + if (block->mEntryValueRange.Size()) + { + assert(mEntryValueRange.Size() == block->mEntryValueRange.Size()); + + for (int i = 0; i < mEntryValueRange.Size(); i++) + mEntryValueRange[i].Union(block->mEntryValueRange[i]); + } + else + mEntryValueRange.SetSize(0); + } + + for (int i = 0; i < mInstructions.Size(); i++) + { + assert(mInstructions[i]->IsEqual(block->mInstructions[i])); + mInstructions[i]->UnionRanges(block->mInstructions[i]); + } + +} + +void InterCodeBasicBlock::MarkIntegerRangeBoundUp(int temp, int64 value, GrowingIntegerValueRangeArray& range) +{ + range[temp].SetLimit(value, value); + + for (int i = mInstructions.Size() - 1; i >= 0; i--) + { + InterInstruction* ins(mInstructions[i]); + + if (ins->mDst.mTemp == temp) + { + if (ins->mCode == IC_BINARY_OPERATOR && ins->mSrc[1].mTemp == temp && ins->mSrc[0].mTemp < 0) + { + switch (ins->mOperator) + { + case IA_ADD: + value -= ins->mSrc[0].mIntConst; + break; + case IA_SUB: + value += ins->mSrc[0].mIntConst; + break; + default: + return; + } + } + else if (ins->mCode == IC_LOAD_TEMPORARY) + { + if (!IsTempModifiedInRange(i + 1, mInstructions.Size(), ins->mSrc[0].mTemp)) + range[ins->mSrc[0].mTemp].SetLimit(value, value); + temp = ins->mSrc[0].mTemp; + } + else + return; + } + else if (ins->mCode == IC_LOAD_TEMPORARY && ins->mSrc[0].mTemp == temp) + { + if (!IsTempModifiedInRange(i + 1, mInstructions.Size(), ins->mDst.mTemp)) + range[ins->mDst.mTemp].SetLimit(value, value); + } + } +} + + +void InterCodeBasicBlock::UpdateLocalIntegerRangeSetsForward(void) +{ + int sz = mInstructions.Size(); + + for (int i = 0; i < sz; i++) + { + InterInstruction* ins(mInstructions[i]); + + for (int i = 0; i < ins->mNumOperands; i++) + { + if (IsIntegerType(ins->mSrc[i].mType) || ins->mSrc[i].mType == IT_BOOL || ins->mSrc[i].mType == IT_POINTER) + { + if (ins->mSrc[i].mTemp >= 0) + { + ins->mSrc[i].mRange.MergeUnknown(mProc->mLocalValueRange[ins->mSrc[i].mTemp]); +#if 1 + if (ins->mCode != IC_ASSEMBLER && ins->mSrc[i].mType != IT_POINTER && + ins->mSrc[i].mRange.mMinState == IntegerValueRange::S_BOUND && + ins->mSrc[i].mRange.mMaxState == IntegerValueRange::S_BOUND && + ins->mSrc[i].mRange.mMinValue == ins->mSrc[i].mRange.mMaxValue) + { + if (ins->mCode == IC_LOAD_TEMPORARY) + { + ins->mCode = IC_CONSTANT; + ins->mConst.mType = ins->mSrc[0].mType; + ins->mConst.mIntConst = ins->mSrc[0].mRange.mMinValue; + ins->mNumOperands = 0; + } + else + { + ins->mSrc[i].mTemp = -1; + ins->mSrc[i].mIntConst = ins->mSrc[i].mRange.mMinValue; + } + } +#endif + } + else + { + ins->mSrc[i].mRange.mMaxState = ins->mSrc[i].mRange.mMinState = IntegerValueRange::S_BOUND; + ins->mSrc[i].mRange.mMinValue = ins->mSrc[i].mRange.mMaxValue = ins->mSrc[i].mIntConst; + } + } + } + + ins->ConstantFolding(); + + if (ins->mDst.mTemp >= 0 && (IsIntegerType(ins->mDst.mType) || ins->mDst.mType == IT_BOOL)) + { + IntegerValueRange& vr(mProc->mLocalValueRange[ins->mDst.mTemp]); + + switch (ins->mCode) + { + case IC_LOAD: + vr = ins->mDst.mRange; + + if (ins->mSrc[0].mTemp < 0 && ins->mSrc[0].mMemory == IM_FPARAM && ins->mSrc[0].mIntConst == 0) + vr.Limit(mLocalParamValueRange[ins->mSrc[0].mVarIndex]); +#if 1 + if (ins->mDst.mType == IT_INT8) + { + vr.LimitMin(-128); + vr.LimitMax(255); + } + else if (ins->mDst.mType == IT_INT16) + { + vr.LimitMin(-32768); + vr.LimitMax(65535); + } + else if (ins->mDst.mType == IT_BOOL) + { + vr.LimitMin(0); + vr.LimitMax(1); + } +#endif + { + LinkerObject* lo = mInstructions[i]->mSrc[0].mLinkerObject; + + if (i > 0 && + mInstructions[i - 1]->mCode == IC_LEA && mInstructions[i - 1]->mDst.mTemp == ins->mSrc[0].mTemp && + mInstructions[i - 1]->mSrc[1].mTemp < 0 && mInstructions[i - 1]->mSrc[1].mMemory == IM_GLOBAL) + lo = mInstructions[i - 1]->mSrc[1].mLinkerObject; + + if (lo && lo->mFlags & LOBJF_CONST) + { + if (ins->mDst.mType == IT_INT8) + { + bool isUnsigned = false, isSigned = false; + if (i + 1 < mInstructions.Size() && mInstructions[i + 1]->mCode == IC_CONVERSION_OPERATOR && mInstructions[i + 1]->mSrc[0].mTemp == mInstructions[i + 0]->mDst.mTemp && mInstructions[i + 1]->mSrc[0].mFinal) + { + if (mInstructions[i + 1]->mOperator == IA_EXT8TO16U || mInstructions[i + 1]->mOperator == IA_EXT8TO32U) + isUnsigned = true; + else if (mInstructions[i + 1]->mOperator == IA_EXT8TO16S || mInstructions[i + 1]->mOperator == IA_EXT8TO32S) + isSigned = true; + } + + int64 start = 0, end = lo->mSize; +#if 1 + if (ins->mSrc[0].mTemp < 0) + { + start = int(ins->mSrc[0].mIntConst); + end = start + 1; + } + else + { + if (ins->mSrc[0].mRange.mMinState == IntegerValueRange::S_BOUND) + { + start = ins->mSrc[0].mRange.mMinValue + ins->mSrc[0].mIntConst; + } + if (ins->mSrc[0].mRange.mMaxState == IntegerValueRange::S_BOUND) + { + end = ins->mSrc[0].mRange.mMaxValue + ins->mSrc[0].mIntConst + 1; + } + } + + if (start < 0) + start = 0; + if (end > lo->mSize) + end = lo->mSize; +#endif + + int mi = 255, ma = -128; + + if (vr.mMinState == IntegerValueRange::S_BOUND && vr.mMaxState == IntegerValueRange::S_BOUND && + vr.mMinValue >= -128 && vr.mMaxValue <= 127) + { + for (int64 j = start; j < end; j++) + { + int v = isUnsigned ? lo->mData[j] : (int8)(lo->mData[j]); + if (v < mi) + mi = v; + if (v > ma) + ma = v; + } + } + else + { + for (int64 j = start; j < end; j++) + { + int v = lo->mData[j]; + if (isUnsigned) + ; + else if (isSigned) + v = (int8)v; + else if (v & 0x80) + mi = -128; + + if (v < mi) + mi = v; + if (v > ma) + ma = v; + } + } + +// printf("LCheck %s:%d %d..%d (0..%d + %d) -> %d..%d\n", ins->mLocation.mFileName, ins->mLocation.mLine, start, end, lo->mSize, (int)(ins->mSrc[0].mIntConst), mi, ma); + + vr.LimitMax(ma); + vr.LimitMin(mi); + } + } + } + + break; + case IC_CONSTANT: + vr.mMaxState = vr.mMinState = IntegerValueRange::S_BOUND; + vr.mMinValue = vr.mMaxValue = ins->mConst.mIntConst; + break; + case IC_LOAD_TEMPORARY: + vr = ins->mSrc[0].mRange; + break; + case IC_SELECT: + vr = ins->mSrc[1].mRange; + vr.Merge(ins->mSrc[0].mRange, false, false); + break; + case IC_UNARY_OPERATOR: + { + switch (ins->mOperator) + { + case IA_NEG: + { + IntegerValueRange tr; + IntegerValueRange& sr(mProc->mLocalValueRange[ins->mSrc[0].mTemp]); + + tr.mMinState = sr.mMaxState; + tr.mMinValue = -sr.mMaxValue; + tr.mMaxState = sr.mMinState; + tr.mMaxValue = -sr.mMinValue; + vr = tr; + } + break; + default: + vr.mMaxState = vr.mMinState = IntegerValueRange::S_UNBOUND; + } + break; + } + case IC_CONVERSION_OPERATOR: + switch (ins->mOperator) + { + case IA_EXT8TO16S: + case IA_EXT8TO32S: + vr = ins->mSrc[0].mRange; + if (vr.mMaxState != IntegerValueRange::S_BOUND || vr.mMaxValue < -128 || vr.mMaxValue > 127) + { + vr.mMaxState = IntegerValueRange::S_BOUND; + vr.mMaxValue = 127; + vr.mMinState = IntegerValueRange::S_BOUND; + vr.mMinValue = -128; + } + else if (vr.mMinState != IntegerValueRange::S_BOUND || vr.mMinValue < -128 || vr.mMinValue > 127) + { + vr.mMinState = IntegerValueRange::S_BOUND; + vr.mMinValue = -128; + } + break; + + case IA_EXT16TO32S: + vr = ins->mSrc[0].mRange; + if (vr.mMaxState != IntegerValueRange::S_BOUND || vr.mMaxValue < -65536 || vr.mMaxValue > 65535) + { + vr.mMaxState = IntegerValueRange::S_BOUND; + vr.mMaxValue = 65535; + } + if (vr.mMinState != IntegerValueRange::S_BOUND || vr.mMinValue < -65536 || vr.mMinValue > 65535) + { + vr.mMinState = IntegerValueRange::S_BOUND; + vr.mMinValue = -65536; + } + break; + + case IA_EXT8TO16U: + case IA_EXT8TO32U: + vr = ins->mSrc[0].mRange; + if (vr.mMaxState != IntegerValueRange::S_BOUND && vr.mMinState == IntegerValueRange::S_BOUND && vr.mMinValue > 0) + { + vr.mMaxState = IntegerValueRange::S_BOUND; + vr.mMaxValue = 255; + } + else + { + if (vr.mMaxState != IntegerValueRange::S_BOUND || vr.mMaxValue < 0 || vr.mMaxValue > 255 || vr.mMinValue < 0 || + vr.mMinState != IntegerValueRange::S_BOUND) + { + vr.mMaxState = IntegerValueRange::S_BOUND; + vr.mMaxValue = 255; + vr.mMinState = IntegerValueRange::S_BOUND; + vr.mMinValue = 0; + } + if (vr.mMinState != IntegerValueRange::S_BOUND || vr.mMinValue < 0 || vr.mMinValue > 255) + { + vr.mMinState = IntegerValueRange::S_BOUND; + vr.mMinValue = 0; + } + } + break; + case IA_EXT16TO32U: + vr = ins->mSrc[0].mRange; + if (vr.mMaxState != IntegerValueRange::S_BOUND || vr.mMaxValue < 0 || vr.mMaxValue > 65535 || vr.mMinValue < 0 || + vr.mMinState != IntegerValueRange::S_BOUND) + { + vr.mMaxState = IntegerValueRange::S_BOUND; + vr.mMaxValue = 65535; + vr.mMinState = IntegerValueRange::S_BOUND; + vr.mMinValue = 0; + } + if (vr.mMinState != IntegerValueRange::S_BOUND || vr.mMinValue < 0 || vr.mMinValue > 65535) + { + vr.mMinState = IntegerValueRange::S_BOUND; + vr.mMinValue = 0; + } + break; + + default: + vr.mMaxState = vr.mMinState = IntegerValueRange::S_UNBOUND; + } + break; + + case IC_RELATIONAL_OPERATOR: + vr.SetLimit(0, 1); + break; + case IC_BINARY_OPERATOR: + switch (ins->mOperator) + { + case IA_ADD: + if (ins->mSrc[0].mTemp < 0) + { +#if 0 + if (/*ins->mSrc[1].mTemp == ins->mDst.mTemp &&*/ dependTemps[ins->mDst.mTemp] && i + 3 != sz) + { + int64 start = pblock->mTrueValueRange[ins->mDst.mTemp].mMinValue; + vr.SetLimit(start + ins->mSrc[0].mIntConst, start + nloop * ins->mSrc[0].mIntConst); + } + else +#endif + { + vr = mProc->mLocalValueRange[ins->mSrc[1].mTemp]; + if (ins->mSrc[0].mIntConst > 0 && vr.mMaxState == IntegerValueRange::S_WEAK) + vr.mMaxState = IntegerValueRange::S_UNBOUND; + else if (ins->mSrc[0].mIntConst < 0 && vr.mMinState == IntegerValueRange::S_WEAK) + vr.mMinState = IntegerValueRange::S_UNBOUND; + if (ins->mDst.mType == IT_INT8 && ( + ins->mSrc[0].mIntConst >= 128 && vr.mMaxState != IntegerValueRange::S_BOUND || + vr.mMaxState == IntegerValueRange::S_BOUND && vr.mMaxValue + ins->mSrc[0].mIntConst >= 256)) + vr.mMinState = IntegerValueRange::S_UNBOUND; + + vr.mMaxValue += ins->mSrc[0].mIntConst; + vr.mMinValue += ins->mSrc[0].mIntConst; + + if (vr.mMaxState == IntegerValueRange::S_BOUND && vr.mMaxValue > UnsignedTypeMax(ins->mDst.mType) || + vr.mMinState == IntegerValueRange::S_BOUND && vr.mMinValue < SignedTypeMin(ins->mDst.mType)) + { + vr.mMinState = IntegerValueRange::S_UNBOUND; + vr.mMaxState = IntegerValueRange::S_UNBOUND; + } + } + } + else if (ins->mSrc[1].mTemp < 0) + { + vr = mProc->mLocalValueRange[ins->mSrc[0].mTemp]; + if (ins->mSrc[1].mIntConst > 0 && vr.mMaxState == IntegerValueRange::S_WEAK) + vr.mMaxState = IntegerValueRange::S_UNBOUND; + else if (ins->mSrc[1].mIntConst < 0 && vr.mMinState == IntegerValueRange::S_WEAK) + vr.mMinState = IntegerValueRange::S_UNBOUND; + vr.mMaxValue += ins->mSrc[1].mIntConst; + vr.mMinValue += ins->mSrc[1].mIntConst; + + if (vr.mMaxState == IntegerValueRange::S_BOUND && vr.mMaxValue > UnsignedTypeMax(ins->mDst.mType) || + vr.mMinState == IntegerValueRange::S_BOUND && vr.mMinValue < SignedTypeMin(ins->mDst.mType)) + { + vr.mMinState = IntegerValueRange::S_UNBOUND; + vr.mMaxState = IntegerValueRange::S_UNBOUND; + } + } + else + { + if (ins->mSrc[0].mRange.mMaxState == IntegerValueRange::S_BOUND && ins->mSrc[1].mRange.mMaxState == IntegerValueRange::S_BOUND) + { + vr.mMaxState = IntegerValueRange::S_BOUND; + vr.mMaxValue = ins->mSrc[0].mRange.mMaxValue + ins->mSrc[1].mRange.mMaxValue; + } + else + vr.mMaxState = IntegerValueRange::S_UNBOUND; + + if (ins->mSrc[0].mRange.mMinState == IntegerValueRange::S_BOUND && ins->mSrc[1].mRange.mMinState == IntegerValueRange::S_BOUND) + { + vr.mMinState = IntegerValueRange::S_BOUND; + vr.mMinValue = ins->mSrc[0].mRange.mMinValue + ins->mSrc[1].mRange.mMinValue; + } + else + vr.mMinState = IntegerValueRange::S_UNBOUND; + + if (vr.mMaxState == IntegerValueRange::S_BOUND && vr.mMaxValue > UnsignedTypeMax(ins->mDst.mType) || + vr.mMinState == IntegerValueRange::S_BOUND && vr.mMinValue < SignedTypeMin(ins->mDst.mType)) + { + vr.mMinState = IntegerValueRange::S_UNBOUND; + vr.mMaxState = IntegerValueRange::S_UNBOUND; + } + } + +#if 1 + if (ins->mDst.mType == IT_INT8) + { + if (vr.mMinState == IntegerValueRange::S_BOUND && vr.mMinValue < -255 || vr.mMaxState == IntegerValueRange::S_BOUND && vr.mMaxValue > 255) + { + vr.LimitMax(255); + vr.LimitMin(-128); + } + } +#endif + break; + case IA_SUB: + if (ins->mSrc[0].mTemp < 0) + { + vr = mProc->mLocalValueRange[ins->mSrc[1].mTemp]; + if (ins->mSrc[0].mIntConst < 0 && vr.mMaxState == IntegerValueRange::S_WEAK) + vr.mMaxState = IntegerValueRange::S_UNBOUND; + else if (ins->mSrc[0].mIntConst > 0 && vr.mMinState == IntegerValueRange::S_WEAK) + vr.mMinState = IntegerValueRange::S_UNBOUND; + vr.mMaxValue -= ins->mSrc[0].mIntConst; + vr.mMinValue -= ins->mSrc[0].mIntConst; + + if (vr.mMaxState == IntegerValueRange::S_BOUND && vr.mMaxValue > UnsignedTypeMax(ins->mDst.mType) || + vr.mMinState == IntegerValueRange::S_BOUND && vr.mMinValue < SignedTypeMin(ins->mDst.mType)) + { + vr.mMinState = IntegerValueRange::S_UNBOUND; + vr.mMaxState = IntegerValueRange::S_UNBOUND; + } + } + else if (ins->mSrc[1].mTemp < 0) + { + vr = mProc->mLocalValueRange[ins->mSrc[0].mTemp]; + + IntegerValueRange::State s = vr.mMinState; + vr.mMinState = vr.mMaxState; + vr.mMaxState = s; + + int64 maxv = vr.mMaxValue, minv = vr.mMinValue; + + if (vr.mMinState == IntegerValueRange::S_WEAK) + { + if (vr.mMaxState == IntegerValueRange::S_WEAK) + vr.mMinState = IntegerValueRange::S_UNBOUND; + vr.mMaxState = IntegerValueRange::S_UNBOUND; + } + if (vr.mMaxState == IntegerValueRange::S_WEAK) + vr.mMinState = IntegerValueRange::S_UNBOUND; + + vr.mMaxValue = ins->mSrc[1].mIntConst - minv; + vr.mMinValue = ins->mSrc[1].mIntConst - maxv; + } + else + { + if (ins->mSrc[0].mRange.mMinState == IntegerValueRange::S_BOUND && ins->mSrc[1].mRange.mMaxState == IntegerValueRange::S_BOUND) + { + vr.mMaxState = IntegerValueRange::S_BOUND; + vr.mMaxValue = ins->mSrc[1].mRange.mMaxValue - ins->mSrc[0].mRange.mMinValue; + } + else + vr.mMaxState = IntegerValueRange::S_UNBOUND; + + if (ins->mSrc[0].mRange.mMaxState == IntegerValueRange::S_BOUND && ins->mSrc[1].mRange.mMinState == IntegerValueRange::S_BOUND) + { + vr.mMinState = IntegerValueRange::S_BOUND; + vr.mMinValue = ins->mSrc[1].mRange.mMinValue - ins->mSrc[0].mRange.mMaxValue; + } + else + vr.mMinState = IntegerValueRange::S_UNBOUND; + } + + break; + case IA_MUL: + if (ins->mSrc[0].mTemp < 0) + { + vr = mProc->mLocalValueRange[ins->mSrc[1].mTemp]; + if (ins->mSrc[0].mIntConst > 0) + { + if (vr.mMaxState == IntegerValueRange::S_WEAK) + vr.mMaxState = IntegerValueRange::S_UNBOUND; + else if (vr.mMinState == IntegerValueRange::S_WEAK) + vr.mMinState = IntegerValueRange::S_UNBOUND; + + vr.mMaxValue *= ins->mSrc[0].mIntConst; + vr.mMinValue *= ins->mSrc[0].mIntConst; + } + else + { + if (vr.mMaxState == IntegerValueRange::S_WEAK) + { + if (vr.mMinState == IntegerValueRange::S_WEAK) + vr.mMaxState = IntegerValueRange::S_UNBOUND; + vr.mMinState = IntegerValueRange::S_UNBOUND; + } + if (vr.mMinState == IntegerValueRange::S_WEAK) + vr.mMaxState = IntegerValueRange::S_UNBOUND; + + int64 maxv = vr.mMaxValue, minv = vr.mMinValue; + vr.mMaxValue = ins->mSrc[0].mIntConst * minv; + vr.mMinValue = ins->mSrc[0].mIntConst * maxv; + } + + if (vr.mMaxState == IntegerValueRange::S_BOUND && vr.mMaxValue > UnsignedTypeMax(ins->mDst.mType) || + vr.mMinState == IntegerValueRange::S_BOUND && vr.mMinValue < SignedTypeMin(ins->mDst.mType)) + { + vr.mMinState = IntegerValueRange::S_UNBOUND; + vr.mMaxState = IntegerValueRange::S_UNBOUND; + } + + } + else if (ins->mSrc[1].mTemp < 0) + { + vr = mProc->mLocalValueRange[ins->mSrc[0].mTemp]; + if (ins->mSrc[1].mIntConst > 0) + { + if (vr.mMaxState == IntegerValueRange::S_WEAK) + vr.mMaxState = IntegerValueRange::S_UNBOUND; + else if (vr.mMinState == IntegerValueRange::S_WEAK) + vr.mMinState = IntegerValueRange::S_UNBOUND; + + vr.mMaxValue *= ins->mSrc[1].mIntConst; + vr.mMinValue *= ins->mSrc[1].mIntConst; + } + else + { + if (vr.mMaxState == IntegerValueRange::S_WEAK) + { + if (vr.mMinState == IntegerValueRange::S_WEAK) + vr.mMaxState = IntegerValueRange::S_UNBOUND; + vr.mMinState = IntegerValueRange::S_UNBOUND; + } + if (vr.mMinState == IntegerValueRange::S_WEAK) + vr.mMaxState = IntegerValueRange::S_UNBOUND; + + int64 maxv = vr.mMaxValue, minv = vr.mMinValue; + vr.mMaxValue = ins->mSrc[1].mIntConst * minv; + vr.mMinValue = ins->mSrc[1].mIntConst * maxv; + } + + if (vr.mMaxState == IntegerValueRange::S_BOUND && vr.mMaxValue > UnsignedTypeMax(ins->mDst.mType) || + vr.mMinState == IntegerValueRange::S_BOUND && vr.mMinValue < SignedTypeMin(ins->mDst.mType)) + { + vr.mMinState = IntegerValueRange::S_UNBOUND; + vr.mMaxState = IntegerValueRange::S_UNBOUND; + } + } + else + vr.mMaxState = vr.mMinState = IntegerValueRange::S_UNBOUND; + + break; + case IA_SHL: + if (ins->mSrc[0].mTemp < 0) + { + vr = mProc->mLocalValueRange[ins->mSrc[1].mTemp]; + if (vr.mMaxState == IntegerValueRange::S_WEAK) + vr.mMaxState = IntegerValueRange::S_UNBOUND; + else if (vr.mMinState == IntegerValueRange::S_WEAK) + vr.mMinState = IntegerValueRange::S_UNBOUND; + + vr.mMaxValue <<= TypeShiftMask(ins->mDst.mType, ins->mSrc[0].mIntConst); + vr.mMinValue <<= TypeShiftMask(ins->mDst.mType, ins->mSrc[0].mIntConst); + + if (vr.mMaxState == IntegerValueRange::S_BOUND && vr.mMaxValue > UnsignedTypeMax(ins->mDst.mType)) + { + vr.mMinState = IntegerValueRange::S_UNBOUND; + vr.mMaxState = IntegerValueRange::S_UNBOUND; + } + } + else if (ins->mSrc[0].IsUByte() && ins->mSrc[0].mRange.mMaxValue < 16) + { + if (ins->mSrc[1].mTemp < 0) + { + vr.mMinState = IntegerValueRange::S_BOUND; + vr.mMaxState = IntegerValueRange::S_BOUND; + + if (ins->mSrc[1].mIntConst < 0) + { + vr.mMinValue = ins->mSrc[1].mIntConst << ins->mSrc[0].mRange.mMaxValue; + vr.mMaxValue = 0; + } + else + { + vr.mMaxValue = ins->mSrc[1].mIntConst << ins->mSrc[0].mRange.mMaxValue; + if (vr.mMaxValue <= 256) + vr.mMinValue = ins->mSrc[1].mIntConst; + else + vr.mMinValue = 0; + } + } + else + { + vr = mProc->mLocalValueRange[ins->mSrc[1].mTemp]; + if (vr.mMaxState == IntegerValueRange::S_WEAK) + vr.mMaxState = IntegerValueRange::S_UNBOUND; + else if (vr.mMinState == IntegerValueRange::S_WEAK) + vr.mMinState = IntegerValueRange::S_UNBOUND; + + vr.mMaxValue <<= ins->mSrc[0].mRange.mMaxValue; + vr.mMinValue <<= ins->mSrc[0].mRange.mMaxValue; + } + } + else + vr.mMaxState = vr.mMinState = IntegerValueRange::S_UNBOUND; + break; + case IA_SHR: + if (ins->mSrc[0].mTemp < 0) + { + vr = mProc->mLocalValueRange[ins->mSrc[1].mTemp]; + + if (ins->mSrc[0].mIntConst > 0) + { + if (vr.mMinState == IntegerValueRange::S_BOUND && vr.mMinValue >= 0) + { + switch (ins->mSrc[1].mType) + { + case IT_INT16: + vr.mMaxValue = (unsigned short)(int64min(65535, vr.mMaxValue)) >> TypeShiftMask(ins->mDst.mType, ins->mSrc[0].mIntConst); + vr.mMinValue = (unsigned short)(int64max(0, vr.mMinValue)) >> TypeShiftMask(ins->mDst.mType, ins->mSrc[0].mIntConst); + break; + case IT_INT8: + vr.mMaxValue = (unsigned char)(int64min(255, vr.mMaxValue)) >> TypeShiftMask(ins->mDst.mType, ins->mSrc[0].mIntConst); + vr.mMinValue = (unsigned char)(int64max(0, vr.mMinValue)) >> TypeShiftMask(ins->mDst.mType, ins->mSrc[0].mIntConst); + break; + case IT_INT32: + vr.mMaxValue = (unsigned)(vr.mMaxValue) >> TypeShiftMask(ins->mDst.mType, ins->mSrc[0].mIntConst); + vr.mMinValue = (unsigned)(vr.mMinValue) >> TypeShiftMask(ins->mDst.mType, ins->mSrc[0].mIntConst); + break; + } + } + else + { + switch (ins->mSrc[1].mType) + { + case IT_INT16: + vr.mMaxValue = 65535 >> TypeShiftMask(ins->mDst.mType, ins->mSrc[0].mIntConst); + vr.mMinValue = 0; + break; + case IT_INT8: + vr.mMaxValue = 255 >> TypeShiftMask(ins->mDst.mType, ins->mSrc[0].mIntConst); + vr.mMinValue = 0; + break; + case IT_INT32: + vr.mMaxValue = 0x100000000ULL >> TypeShiftMask(ins->mDst.mType, ins->mSrc[0].mIntConst); + vr.mMinValue = 0; + break; + } + vr.mMinState = IntegerValueRange::S_BOUND; + vr.mMaxState = IntegerValueRange::S_BOUND; + } + } + } + else if (ins->mSrc[1].mTemp >= 0) + { + vr = mProc->mLocalValueRange[ins->mSrc[1].mTemp]; + if (vr.mMinValue >= 0) + vr.mMinValue = 0; + } + else + vr.mMaxState = vr.mMinState = IntegerValueRange::S_UNBOUND; + break; + case IA_SAR: + if (ins->mSrc[0].mTemp < 0) + { + vr = mProc->mLocalValueRange[ins->mSrc[1].mTemp]; + + if (ins->mSrc[0].mIntConst > 0) + { + if (vr.mMinState == IntegerValueRange::S_BOUND && vr.mMaxState == IntegerValueRange::S_BOUND) + { + switch (ins->mSrc[1].mType) + { + case IT_INT16: + vr.mMaxValue = (int16)(int64min(32767, vr.mMaxValue)) >> TypeShiftMask(ins->mDst.mType, ins->mSrc[0].mIntConst); + vr.mMinValue = (int16)(int64max(-32768, vr.mMinValue)) >> TypeShiftMask(ins->mDst.mType, ins->mSrc[0].mIntConst); + break; + case IT_INT8: + vr.mMaxValue = (int8)(int64min(127, vr.mMaxValue)) >> TypeShiftMask(ins->mDst.mType, ins->mSrc[0].mIntConst); + vr.mMinValue = (int8)(int64max(-128, vr.mMinValue)) >> TypeShiftMask(ins->mDst.mType, ins->mSrc[0].mIntConst); + break; + case IT_INT32: + vr.mMaxValue = (int32)(int64min(2147483647, vr.mMaxValue)) >> TypeShiftMask(ins->mDst.mType, ins->mSrc[0].mIntConst); + vr.mMinValue = (int32)(int64max(-2147483648, vr.mMinValue)) >> TypeShiftMask(ins->mDst.mType, ins->mSrc[0].mIntConst); + break; + } + } + else + { + switch (ins->mSrc[1].mType) + { + case IT_INT16: + vr.mMaxValue = (int16) 32767 >> TypeShiftMask(ins->mDst.mType, ins->mSrc[0].mIntConst); + vr.mMinValue = (int16)-32768 >> TypeShiftMask(ins->mDst.mType, ins->mSrc[0].mIntConst); + break; + case IT_INT8: + vr.mMaxValue = (int8) 127 >> TypeShiftMask(ins->mDst.mType, ins->mSrc[0].mIntConst); + vr.mMinValue = (int8)-128 >> TypeShiftMask(ins->mDst.mType, ins->mSrc[0].mIntConst); + break; + case IT_INT32: + vr.mMaxValue = (int32) 2147483647 >> TypeShiftMask(ins->mDst.mType, ins->mSrc[0].mIntConst); + vr.mMinValue = (int32)-2147483648 >> TypeShiftMask(ins->mDst.mType, ins->mSrc[0].mIntConst); + break; + } + vr.mMinState = IntegerValueRange::S_BOUND; + vr.mMaxState = IntegerValueRange::S_BOUND; + } + } + } + else if (ins->mSrc[1].mTemp >= 0) + { + vr = mProc->mLocalValueRange[ins->mSrc[1].mTemp]; + if (vr.mMinValue >= 0) + vr.mMinValue = 0; + else if (vr.mMaxValue < 0) + vr.mMaxValue = -1; + } + else + vr.mMaxState = vr.mMinState = IntegerValueRange::S_UNBOUND; + break; + case IA_AND: + if (ins->mSrc[0].IsUnsigned() && ins->mSrc[1].IsUnsigned()) + { + vr.mMaxState = vr.mMinState = IntegerValueRange::S_BOUND; + int64 v0 = (ins->mSrc[0].mRange.mMaxValue & BuildLowerBitsMask(ins->mSrc[1].mRange.mMaxValue)); + int64 v1 = (ins->mSrc[1].mRange.mMaxValue & BuildLowerBitsMask(ins->mSrc[0].mRange.mMaxValue)); + vr.mMaxValue = (v0 > v1) ? v0 : v1; + vr.mMinValue = 0; + } + else if (ins->mSrc[0].mTemp < 0) + { + vr = mProc->mLocalValueRange[ins->mSrc[1].mTemp]; + int64 mask = UnsignedTypeMax(ins->mSrc[1].mType) & ins->mSrc[0].mIntConst; + if (ins->mSrc[1].IsUnsigned()) + { + vr.mMinState = IntegerValueRange::S_BOUND; + vr.mMinValue = 0; + vr.LimitMax(mask & BuildLowerBitsMask(ins->mSrc[1].mRange.mMaxValue)); + } + else if (mask <= SignedTypeMax(ins->mSrc[1].mType)) + { + vr.mMaxState = vr.mMinState = IntegerValueRange::S_BOUND; + vr.mMaxValue = mask; + vr.mMinValue = 0; + } + } + else if (ins->mSrc[1].mTemp < 0) + { + vr = mProc->mLocalValueRange[ins->mSrc[0].mTemp]; + int64 mask = UnsignedTypeMax(ins->mSrc[0].mType) & ins->mSrc[1].mIntConst; + + if (ins->mSrc[0].IsUnsigned()) + { + vr.mMinState = IntegerValueRange::S_BOUND; + vr.mMinValue = 0; + vr.LimitMax(mask& BuildLowerBitsMask(ins->mSrc[0].mRange.mMaxValue)); + } + else if (mask <= SignedTypeMax(ins->mSrc[0].mType)) + { + vr.mMaxState = vr.mMinState = IntegerValueRange::S_BOUND; + vr.mMaxValue = mask; + vr.mMinValue = 0; + } + } + else + vr.mMaxState = vr.mMinState = IntegerValueRange::S_UNBOUND; + + if (vr.mMaxState == IntegerValueRange::S_BOUND) + { + int64 lowmask = -1; + if (ins->mSrc[0].mTemp < 0) + lowmask &= ins->mSrc[0].mIntConst; + if (ins->mSrc[1].mTemp < 0) + lowmask &= ins->mSrc[1].mIntConst; + for (int i = 0; i < 32 && !(lowmask & (1LL << i)); i++) + vr.mMaxValue &= ~(1LL << i); + } + break; + + case IA_OR: + case IA_XOR: + if (ins->mSrc[0].IsUnsigned() && ins->mSrc[1].IsUnsigned()) + { + vr.mMaxState = vr.mMinState = IntegerValueRange::S_BOUND; + vr.mMaxValue = BuildLowerBitsMask(ins->mSrc[1].mRange.mMaxValue) | BuildLowerBitsMask(ins->mSrc[0].mRange.mMaxValue); + vr.mMinValue = 0; + } + else if (ins->mSrc[0].mTemp < 0) + { + vr = mProc->mLocalValueRange[ins->mSrc[1].mTemp]; + + if (ins->mSrc[0].mIntConst >= 0) + vr.mMaxValue = BuildLowerBitsMask(vr.mMaxValue) | ins->mSrc[0].mIntConst; + else if (ins->mSrc[0].mIntConst < 0) + { + vr.mMaxState = IntegerValueRange::S_UNBOUND; + if (vr.mMinState == IntegerValueRange::S_BOUND) + vr.mMinValue = int64min(vr.mMinValue, ins->mSrc[0].mIntConst); + else + { + vr.mMinState = IntegerValueRange::S_BOUND; + vr.mMinValue = ins->mSrc[0].mIntConst; + } + } + } + else if (ins->mSrc[1].mTemp < 0) + { + vr = mProc->mLocalValueRange[ins->mSrc[0].mTemp]; + + if (ins->mSrc[1].mIntConst >= 0) + vr.mMaxValue = BuildLowerBitsMask(vr.mMaxValue) | ins->mSrc[1].mIntConst; + else if (ins->mSrc[1].mIntConst < 0) + { + vr.mMaxState = IntegerValueRange::S_UNBOUND; + if (vr.mMinState == IntegerValueRange::S_BOUND) + vr.mMinValue = int64min(vr.mMinValue, ins->mSrc[1].mIntConst); + else + { + vr.mMinState = IntegerValueRange::S_BOUND; + vr.mMinValue = ins->mSrc[1].mIntConst; + } + } + } + else + vr.mMaxState = vr.mMinState = IntegerValueRange::S_UNBOUND; + break; +#if 1 + case IA_DIVU: + + if (ins->mSrc[1].mTemp >= 0) + { + vr = mProc->mLocalValueRange[ins->mSrc[1].mTemp]; + if (vr.mMaxState != IntegerValueRange::S_BOUND || vr.mMinState < IntegerValueRange::S_WEAK || vr.mMinValue < 0) + { + vr.mMaxValue = IntegerValueRange::S_BOUND; + vr.mMaxValue = UnsignedTypeMax(ins->mSrc[1].mType); + } + } + else + vr.LimitMax(ins->mSrc[1].mIntConst); + + vr.SetMin(0); + + if (ins->mSrc[0].mTemp < 0 && ins->mSrc[0].mIntConst > 1) + vr.mMaxValue /= ins->mSrc[0].mIntConst; + break; + + case IA_MODU: + vr.SetMin(0); + + if (ins->mSrc[0].mTemp < 0) + vr.LimitMax(ins->mSrc[0].mIntConst - 1); + else if (ins->mSrc[0].mRange.mMaxState == IntegerValueRange::S_BOUND) + vr.LimitMax(ins->mSrc[0].mRange.mMaxValue - 1); + else if (ins->mSrc[1].mRange.mMaxState == IntegerValueRange::S_BOUND) + vr.LimitMax(ins->mSrc[1].mRange.mMaxValue); + else + vr.mMaxState = IntegerValueRange::S_UNBOUND; + if (vr.mMaxValue < 0) + vr.mMaxState = IntegerValueRange::S_UNBOUND; + break; +#endif + default: + vr.mMaxState = vr.mMinState = IntegerValueRange::S_UNBOUND; + } + break; + + default: + vr.mMaxState = vr.mMinState = IntegerValueRange::S_UNBOUND; + } + +#if 1 + if (ins->mDst.mType == IT_INT8) + { + vr.LimitMinBound(-128); + vr.LimitMaxBound(255); + } + else if (ins->mDst.mType == IT_INT16) + { + vr.LimitMinBound(-32768); + vr.LimitMaxBound(65535); + } +#endif + ins->mDst.mRange.MergeUnknown(vr); +#if 1 + if (vr.mMaxState == IntegerValueRange::S_BOUND && vr.mMinState == IntegerValueRange::S_BOUND && vr.mMaxValue == vr.mMinValue) + { + ins->mCode = IC_CONSTANT; + ins->mConst.mType = ins->mDst.mType; + ins->mConst.mIntConst = vr.mMaxValue; + ins->mNumOperands = 0; + } +#endif + } + else if (ins->mDst.mTemp >= 0 && ins->mDst.mType == IT_POINTER) + { + IntegerValueRange& vr(mProc->mLocalValueRange[ins->mDst.mTemp]); + + switch (ins->mCode) + { + case IC_CONSTANT: + vr.mMaxState = vr.mMinState = IntegerValueRange::S_BOUND; + vr.mMinValue = vr.mMaxValue = ins->mConst.mIntConst; + break; + case IC_LOAD: + if (ins->mDst.mRange.IsBound()) + vr = ins->mDst.mRange; + break; + case IC_LOAD_TEMPORARY: + vr = ins->mSrc[0].mRange; + break; + case IC_LEA: + if (ins->mSrc[0].mTemp < 0) + { + vr = mProc->mLocalValueRange[ins->mSrc[1].mTemp]; + if (ins->mSrc[0].mIntConst > 0 && vr.mMaxState == IntegerValueRange::S_WEAK) + vr.mMaxState = IntegerValueRange::S_UNBOUND; + else if (ins->mSrc[0].mIntConst < 0 && vr.mMinState == IntegerValueRange::S_WEAK) + vr.mMinState = IntegerValueRange::S_UNBOUND; + + vr.mMaxValue += ins->mSrc[0].mIntConst; + vr.mMinValue += ins->mSrc[0].mIntConst; + } + else if (ins->mSrc[1].mTemp < 0) + { + vr = mProc->mLocalValueRange[ins->mSrc[0].mTemp]; + if (ins->mSrc[1].mIntConst > 0 && vr.mMaxState == IntegerValueRange::S_WEAK) + vr.mMaxState = IntegerValueRange::S_UNBOUND; + else if (ins->mSrc[1].mIntConst < 0 && vr.mMinState == IntegerValueRange::S_WEAK) + vr.mMinState = IntegerValueRange::S_UNBOUND; + vr.mMaxValue += ins->mSrc[1].mIntConst; + vr.mMinValue += ins->mSrc[1].mIntConst; + } + else + { + if (ins->mSrc[0].mRange.mMaxState == IntegerValueRange::S_BOUND && ins->mSrc[1].mRange.mMaxState == IntegerValueRange::S_BOUND) + { + vr.mMaxState = IntegerValueRange::S_BOUND; + vr.mMaxValue = ins->mSrc[0].mRange.mMaxValue + ins->mSrc[1].mRange.mMaxValue; + } + else + vr.mMaxState = IntegerValueRange::S_UNBOUND; + + if (ins->mSrc[0].mRange.mMinState == IntegerValueRange::S_BOUND && ins->mSrc[1].mRange.mMinState == IntegerValueRange::S_BOUND) + { + vr.mMinState = IntegerValueRange::S_BOUND; + vr.mMinValue = ins->mSrc[0].mRange.mMinValue + ins->mSrc[1].mRange.mMinValue; + } + else + vr.mMinState = IntegerValueRange::S_UNBOUND; + } + + break; + + default: + vr.mMaxState = vr.mMinState = IntegerValueRange::S_UNBOUND; + } + + ins->mDst.mRange.MergeUnknown(vr); + } + else if (ins->mCode == IC_STORE) + { + if (ins->mSrc[1].mTemp < 0 && ins->mSrc[1].mMemory == IM_FPARAM && ins->mSrc[0].mIntConst == 0) + mLocalParamValueRange[ins->mSrc[1].mVarIndex] = ins->mSrc[0].mRange; + } + + assert(mProc->mLocalValueRange.Size() == mExitRequiredTemps.Size()); + } +} + +bool InterCodeBasicBlock::UpdateLinearCombinations(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + struct LinearCombo + { + int base, step, count; + }; + + GrowingArray combos(LinearCombo{ 0, 0, 0 }); + + for (int i = 0; i < mInstructions.Size(); i++) + { + InterInstruction* ins = mInstructions[i]; + + bool checked = false; + + if (ins->mCode == IC_BINARY_OPERATOR && (ins->mOperator == IA_MUL || ins->mOperator == IA_SHL) && ins->mSrc[0].mTemp < 0 && ins->mSrc[1].mRange.IsBound()) + { + combos[ins->mDst.mTemp] = LinearCombo { + int(ins->mSrc[1].mRange.mMinValue), + int(ins->mSrc[0].mIntConst), + int(ins->mSrc[1].mRange.mMaxValue + 1 - ins->mSrc[1].mRange.mMinValue) }; + checked = true; + } + else if (ins->mCode == IC_LEA && ins->mSrc[1].mTemp < 0 && + ins->mSrc[1].mLinkerObject && (ins->mSrc[1].mLinkerObject->mFlags & LOBJF_CONST) && + ins->mSrc[0].mTemp >= 0 && combos[ins->mSrc[0].mTemp].step > 0) + { + LinearCombo lc = combos[ins->mSrc[0].mTemp]; + combos[ins->mDst.mTemp] = LinearCombo{ + lc.base + int(ins->mSrc[1].mIntConst), + lc.step, + lc.count }; + checked = true; + } + else if (ins->mCode == IC_LOAD && + ins->mDst.mType == IT_INT8 && + ins->mSrc[0].mLinkerObject && + ins->mSrc[0].mStride == 1 && + ins->mSrc[0].mTemp >= 0 && combos[ins->mSrc[0].mTemp].step > 0) + { + LinkerObject* lo = ins->mSrc[0].mLinkerObject; + LinearCombo lc = combos[ins->mSrc[0].mTemp]; + + int start = lc.base + int(ins->mSrc[0].mIntConst); + int end = start + lc.count * lc.step; + if (end > lo->mSize) end = lo->mSize; + + if (end > start) + { + bool isUnsigned = false, isSigned = false; + if (ins->mDst.IsPositive()) + isUnsigned = true; + + int mi = 255, ma = 0; + + for (int j=start; jmData[j]; + if (isUnsigned) + { + if (v < mi) + mi = v; + if (v > ma) + ma = v; + } + else + { + if (v < mi) + mi = v; + if (v > ma) + ma = v; + if (v & 0x80) + { + v -= 256; + if (v < mi) + mi = v; + } + } + } + + if (ins->mDst.mRange.LimitMax(ma) || ins->mDst.mRange.LimitMin(mi)) + changed = true; + +// printf("MulStep %s:%d : %d (%d, %d)\n", ins->mLocation.mFileName, ins->mLocation.mLine, lc.step, mi, ma); + } + } + + if (ins->mDst.mTemp >= 0 && !checked) + combos[ins->mDst.mTemp].step = 0; + } + + if (mTrueJump && mTrueJump->UpdateLinearCombinations()) changed = true; + if (mFalseJump && mFalseJump->UpdateLinearCombinations()) changed = true; + } + + return changed; +} + +void InterCodeBasicBlock::UpdateLocalIntegerRangeSetsBackward(void) +{ + mProc->mReverseValueRange.SetSize(mProc->mLocalValueRange.Size()); + + for (int i = 0; i < mProc->mReverseValueRange.Size(); i++) + mProc->mReverseValueRange[i].Reset(); + + if (mTrueJump && !mFalseJump) + { + for (int i = 0; i < mMemoryValueSize.Size(); i++) + { + mEntryMemoryValueSize[i] = mMemoryValueSize[i] = mTrueJump->mMemoryValueSize[i]; + } + } + + NumberSet requiredTemps(mExitRequiredTemps); + + int sz = mInstructions.Size(); + for (int i = sz - 1; i >= 0; i--) + { + InterInstruction* ins(mInstructions[i]); + if (ins->mCode == IC_LOAD && ins->mSrc[0].mMemory == IM_INDIRECT && ins->mSrc[0].mTemp >= 0 && requiredTemps[ins->mDst.mTemp]) + mMemoryValueSize[ins->mSrc[0].mTemp] = int64max(mMemoryValueSize[ins->mSrc[0].mTemp], ins->mSrc[0].mIntConst + (InterTypeSize[ins->mDst.mType] - 1) * ins->mSrc[0].mStride + 1); + else if (ins->mCode == IC_STORE && ins->mSrc[1].mMemory == IM_INDIRECT && ins->mSrc[1].mTemp >= 0) + mMemoryValueSize[ins->mSrc[1].mTemp] = int64max(mMemoryValueSize[ins->mSrc[1].mTemp], ins->mSrc[1].mIntConst + (InterTypeSize[ins->mSrc[0].mType] - 1) * ins->mSrc[1].mStride + 1); + else if (ins->mCode == IC_FILL) + { + if (ins->mSrc[1].mMemory == IM_INDIRECT && ins->mSrc[1].mTemp >= 0) + mMemoryValueSize[ins->mSrc[1].mTemp] = ins->mConst.mOperandSize; + } + else if (ins->mCode == IC_COPY) + { + if (ins->mSrc[0].mMemory == IM_INDIRECT && ins->mSrc[0].mTemp >= 0) + mMemoryValueSize[ins->mSrc[0].mTemp] = ins->mConst.mOperandSize; + if (ins->mSrc[1].mMemory == IM_INDIRECT && ins->mSrc[1].mTemp >= 0) + mMemoryValueSize[ins->mSrc[1].mTemp] = ins->mConst.mOperandSize; + } + else if (ins->mCode == IC_LEA && ins->mSrc[1].mMemory != IM_INDIRECT && ins->mSrc[0].mTemp >= 0 && mMemoryValueSize[ins->mDst.mTemp] > 0 && requiredTemps[ins->mDst.mTemp]) + { + int asize = 0; + if (ins->mSrc[1].mMemory == IM_GLOBAL) + asize = ins->mSrc[1].mLinkerObject->mSize; + else if (ins->mSrc[1].mMemory == IM_LOCAL) + asize = mProc->mLocalVars[ins->mSrc[1].mVarIndex]->mSize; + + if (asize > 0) + { + mProc->mReverseValueRange[ins->mSrc[0].mTemp].LimitMin(-ins->mSrc[1].mIntConst); + mProc->mReverseValueRange[ins->mSrc[0].mTemp].LimitMax(asize - ins->mSrc[1].mIntConst - mMemoryValueSize[ins->mDst.mTemp]); + } + } + + bool required = false; + if (ins->mDst.mTemp < 0 || requiredTemps[ins->mDst.mTemp] || IsObservable(ins->mCode)) + { + if (ins->mDst.mTemp >= 0) + requiredTemps -= ins->mDst.mTemp; + required = true; + + if (ins->mCode == IC_SELECT) + { + if (ins->mSrc[0].mTemp >= 0) + requiredTemps += ins->mSrc[0].mTemp; + } + else + { + for (int i = 0; i < ins->mNumOperands; i++) + if (ins->mSrc[i].mTemp >= 0) + requiredTemps += ins->mSrc[i].mTemp; + } + } + + if (ins->mDst.mTemp >= 0) + { + ins->mDst.mRange.Limit(mProc->mReverseValueRange[ins->mDst.mTemp]); + mProc->mReverseValueRange[ins->mDst.mTemp].Reset(); + IntegerValueRange& vr(ins->mDst.mRange); + + switch (ins->mCode) + { + case IC_CONVERSION_OPERATOR: + switch (ins->mOperator) + { + case IA_EXT8TO16U: + if (ins->mSrc[0].mTemp >= 0 && (vr.mMaxValue != 255 || vr.mMinValue != 0)) + mProc->mReverseValueRange[ins->mSrc[0].mTemp].Limit(vr); + break; + case IA_EXT8TO16S: + if (ins->mSrc[0].mTemp >= 0 && (vr.mMaxValue != 127 || vr.mMinValue != -128)) + mProc->mReverseValueRange[ins->mSrc[0].mTemp].Limit(vr); + break; + } + break; + case IC_BINARY_OPERATOR: + switch (ins->mOperator) + { + case IA_SHL: + if (ins->mSrc[0].mTemp < 0 && ins->mSrc[1].mTemp >= 0) + { + if (ins->mDst.mType == IT_INT16) + { + if (vr.mMinState == IntegerValueRange::S_BOUND && vr.mMinValue >= -0x4000 && + vr.mMaxState == IntegerValueRange::S_BOUND && vr.mMaxValue < 0x4000) + { + ins->mSrc[1].mRange.LimitMin(vr.mMinValue >> ins->mSrc[0].mIntConst); + ins->mSrc[1].mRange.LimitMax(vr.mMaxValue >> ins->mSrc[0].mIntConst); + mProc->mReverseValueRange[ins->mSrc[1].mTemp].Limit(ins->mSrc[1].mRange); + } + } + else if (ins->mDst.mType == IT_INT8) + { + if (vr.mMinState == IntegerValueRange::S_BOUND && vr.mMinValue >= -0x40 && + vr.mMaxState == IntegerValueRange::S_BOUND && vr.mMaxValue < 0x40) + { + ins->mSrc[1].mRange.LimitMin(vr.mMinValue >> ins->mSrc[0].mIntConst); + ins->mSrc[1].mRange.LimitMax(vr.mMaxValue >> ins->mSrc[0].mIntConst); + mProc->mReverseValueRange[ins->mSrc[1].mTemp].Limit(ins->mSrc[1].mRange); + } + } + else if (ins->mDst.mType == IT_INT32) + { + if (vr.mMinState == IntegerValueRange::S_BOUND && vr.mMinValue >= -0x40000000 && + vr.mMaxState == IntegerValueRange::S_BOUND && vr.mMaxValue < 0x40000000) + { + ins->mSrc[1].mRange.LimitMin(vr.mMinValue >> ins->mSrc[0].mIntConst); + ins->mSrc[1].mRange.LimitMax(vr.mMaxValue >> ins->mSrc[0].mIntConst); + mProc->mReverseValueRange[ins->mSrc[1].mTemp].Limit(ins->mSrc[1].mRange); + } + } + } + break; + case IA_SUB: + if (ins->mSrc[0].mTemp < 0 && ins->mSrc[1].mTemp >= 0) + { + if (vr.mMinState == IntegerValueRange::S_BOUND) + ins->mSrc[1].mRange.LimitMin(vr.mMinValue + ins->mSrc[0].mIntConst); + if (vr.mMaxState == IntegerValueRange::S_BOUND) + ins->mSrc[1].mRange.LimitMax(vr.mMaxValue + ins->mSrc[0].mIntConst); + + mProc->mReverseValueRange[ins->mSrc[1].mTemp].Limit(ins->mSrc[1].mRange); + } + break; + case IA_ADD: + if (ins->mSrc[0].mTemp < 0 && ins->mSrc[1].mTemp >= 0) + { + if (ins->mDst.mType == IT_INT8) + { + if (vr.mMinState == IntegerValueRange::S_BOUND && vr.mMinValue >= 0 && vr.mMinValue - ins->mSrc[0].mIntConst < 0) + vr.mMaxState = IntegerValueRange::S_UNBOUND; + } + + if (ins->mDst.mType != IT_INT8 || vr.mMinValue > -128 || vr.mMaxValue < 255) + { + if (vr.mMinState == IntegerValueRange::S_BOUND) + ins->mSrc[1].mRange.LimitMin(vr.mMinValue - ins->mSrc[0].mIntConst); + if (vr.mMaxState == IntegerValueRange::S_BOUND) + ins->mSrc[1].mRange.LimitMax(vr.mMaxValue - ins->mSrc[0].mIntConst); + } + + mProc->mReverseValueRange[ins->mSrc[1].mTemp].Limit(ins->mSrc[1].mRange); + } + else if (ins->mSrc[1].mTemp < 0 && ins->mSrc[0].mTemp >= 0) + { + if (ins->mDst.mType == IT_INT8) + { + if (vr.mMinState == IntegerValueRange::S_BOUND && vr.mMinValue >= 0 && vr.mMinValue - ins->mSrc[1].mIntConst < 0) + vr.mMaxState = IntegerValueRange::S_UNBOUND; + } + + if (vr.mMinState == IntegerValueRange::S_BOUND) + ins->mSrc[0].mRange.LimitMin(vr.mMinValue - ins->mSrc[1].mIntConst); + if (vr.mMaxState == IntegerValueRange::S_BOUND) + ins->mSrc[0].mRange.LimitMax(vr.mMaxValue - ins->mSrc[1].mIntConst); + mProc->mReverseValueRange[ins->mSrc[0].mTemp].Limit(ins->mSrc[0].mRange); + } + else if (ins->mSrc[0].mTemp >= 0 && ins->mSrc[1].mTemp >= 0) + { + if (vr.mMinState == IntegerValueRange::S_BOUND && ins->mSrc[0].mRange.mMaxState == IntegerValueRange::S_BOUND) + ins->mSrc[1].mRange.LimitMin(vr.mMinValue - ins->mSrc[0].mRange.mMaxValue); + if (vr.mMaxState == IntegerValueRange::S_BOUND && ins->mSrc[0].mRange.mMinState == IntegerValueRange::S_BOUND) + ins->mSrc[1].mRange.LimitMax(vr.mMaxValue - ins->mSrc[0].mRange.mMinValue); + + if (vr.mMinState == IntegerValueRange::S_BOUND && ins->mSrc[1].mRange.mMaxState == IntegerValueRange::S_BOUND) + ins->mSrc[0].mRange.LimitMin(vr.mMinValue - ins->mSrc[1].mRange.mMaxValue); + if (vr.mMaxState == IntegerValueRange::S_BOUND && ins->mSrc[1].mRange.mMinState == IntegerValueRange::S_BOUND) + ins->mSrc[0].mRange.LimitMax(vr.mMaxValue - ins->mSrc[1].mRange.mMinValue); + + mProc->mReverseValueRange[ins->mSrc[0].mTemp].Limit(ins->mSrc[0].mRange); + mProc->mReverseValueRange[ins->mSrc[1].mTemp].Limit(ins->mSrc[1].mRange); + } + break; + case IA_MUL: + if (ins->mSrc[0].mTemp < 0 && ins->mSrc[1].mTemp >= 0 && ins->mSrc[0].mIntConst > 0) + { + if (vr.mMinState == IntegerValueRange::S_BOUND) + ins->mSrc[1].mRange.LimitMin(vr.mMinValue / ins->mSrc[0].mIntConst); + if (vr.mMaxState == IntegerValueRange::S_BOUND) + ins->mSrc[1].mRange.LimitMax(vr.mMaxValue / ins->mSrc[0].mIntConst); + mProc->mReverseValueRange[ins->mSrc[1].mTemp].Limit(ins->mSrc[1].mRange); + } + else if (ins->mSrc[1].mTemp < 0 && ins->mSrc[0].mTemp >= 0 && ins->mSrc[1].mIntConst > 0) + { + if (vr.mMinState == IntegerValueRange::S_BOUND) + ins->mSrc[0].mRange.LimitMin(vr.mMinValue / ins->mSrc[1].mIntConst); + if (vr.mMaxState == IntegerValueRange::S_BOUND) + ins->mSrc[0].mRange.LimitMax(vr.mMaxValue / ins->mSrc[1].mIntConst); + mProc->mReverseValueRange[ins->mSrc[0].mTemp].Limit(ins->mSrc[0].mRange); + } + break; + } + break; + + case IC_LEA: + if (ins->mSrc[1].mMemory == IM_INDIRECT && mMemoryValueSize[ins->mDst.mTemp] > 0) + { + if (ins->mSrc[0].mTemp < 0) + { + if (ins->mSrc[0].mIntConst >= 0) + mMemoryValueSize[ins->mSrc[1].mTemp] = mMemoryValueSize[ins->mDst.mTemp] - ins->mSrc[0].mIntConst; + } + else if (ins->mSrc[0].mRange.mMinState == IntegerValueRange::S_BOUND) + { + if (ins->mSrc[0].mRange.mMinValue >= 0) + mMemoryValueSize[ins->mSrc[1].mTemp] = mMemoryValueSize[ins->mDst.mTemp] - ins->mSrc[0].mRange.mMinValue; + } + } + break; + } + + } + + for (int i = 0; i < ins->mNumOperands; i++) + { + if (ins->mSrc[i].mTemp >= 0) + { + ins->mSrc[i].mRange.Limit(mProc->mReverseValueRange[ins->mSrc[i].mTemp]); + if (required && requiredTemps[ins->mSrc[i].mTemp]) + mProc->mReverseValueRange[ins->mSrc[i].mTemp] = ins->mSrc[i].mRange; + } + } + + if (ins->mDst.mTemp >= 0) + mMemoryValueSize[ins->mDst.mTemp] = 0; + } +} + +bool InterCodeBasicBlock::TempIsUnsigned(int temp) +{ + for (int i = 0; i < mInstructions.Size(); i++) + { + InterInstruction* ins = mInstructions[i]; + if (ins->UsesTemp(temp)) + { + if (ins->mCode == IC_CONVERSION_OPERATOR && (ins->mOperator == IA_EXT8TO16U || ins->mOperator == IA_EXT8TO32U)) + return true; + } + } + + return false; +} + +void InterCodeBasicBlock::UpdateLocalIntegerRangeSets(void) +{ + mProc->mLocalValueRange = mEntryValueRange; + mLocalParamValueRange = mEntryParamValueRange; + + int sz = mInstructions.Size(); + + assert(mProc->mLocalValueRange.Size() == mExitRequiredTemps.Size()); + + InterCodeBasicBlock * pblock; + int64 nloop; + bool nfixed; + + bool singleLoop = CheckSingleBlockLimitedLoop(pblock, nloop, nfixed); + +#if 0 + FastNumberSet dependTemps(mExitRequiredTemps.Size()); +#endif + + struct TempChain + { + int mBaseTemp, mReadIns, mWriteIns; + bool mConstant; + int64 mOffset; + }; + + ExpandingArray tempChain; + + if (singleLoop) + { + if (nfixed && nloop == 1) + { + InterInstruction* jins = mInstructions.Last(); + jins->mCode = IC_JUMP; + jins->mNumOperands = 0; + if (mTrueJump == this) + mTrueJump = mFalseJump; + + mNumEntries--; + mEntryBlocks.RemoveAll(this); + mFalseJump = nullptr; + } + else + { + tempChain.SetSize(mExitRequiredTemps.Size()); + + for (int i = 0; i < mExitRequiredTemps.Size(); i++) + { + tempChain[i].mBaseTemp = i; + tempChain[i].mOffset = 0; + tempChain[i].mConstant = true; + tempChain[i].mReadIns = -1; + tempChain[i].mWriteIns = -1; + } + + for (int i = 0; i < sz; i++) + { + InterInstruction* ins(mInstructions[i]); + + for (int j = 0; j < ins->mNumOperands; j++) + { + if (ins->mSrc[j].mTemp >= 0) + tempChain[ins->mSrc[j].mTemp].mReadIns = i; + } + if (ins->mCode == IC_BINARY_OPERATOR && ins->mOperator == IA_ADD && + ins->mSrc[1].mTemp >= 0 && ins->mSrc[0].mTemp < 0 && + tempChain[ins->mSrc[1].mTemp].mBaseTemp >= 0) + { + tempChain[ins->mDst.mTemp].mBaseTemp = tempChain[ins->mSrc[1].mTemp].mBaseTemp; + tempChain[ins->mDst.mTemp].mOffset = tempChain[ins->mSrc[1].mTemp].mOffset + ins->mSrc[0].mIntConst; + tempChain[ins->mDst.mTemp].mConstant = tempChain[ins->mSrc[1].mTemp].mConstant; + } + else if (ins->mCode == IC_BINARY_OPERATOR && ins->mOperator == IA_ADD && + ins->mSrc[0].mTemp >= 0 && ins->mSrc[1].mTemp < 0 && + tempChain[ins->mSrc[0].mTemp].mBaseTemp >= 0) + { + tempChain[ins->mDst.mTemp].mBaseTemp = tempChain[ins->mSrc[0].mTemp].mBaseTemp; + tempChain[ins->mDst.mTemp].mOffset = tempChain[ins->mSrc[0].mTemp].mOffset + ins->mSrc[1].mIntConst; + tempChain[ins->mDst.mTemp].mConstant = tempChain[ins->mSrc[0].mTemp].mConstant; + } + else if (ins->mCode == IC_BINARY_OPERATOR && ins->mOperator == IA_SUB && + ins->mSrc[1].mTemp >= 0 && ins->mSrc[0].mTemp < 0 && + tempChain[ins->mSrc[1].mTemp].mBaseTemp >= 0) + { + tempChain[ins->mDst.mTemp].mBaseTemp = tempChain[ins->mSrc[1].mTemp].mBaseTemp; + tempChain[ins->mDst.mTemp].mOffset = tempChain[ins->mSrc[1].mTemp].mOffset - ins->mSrc[0].mIntConst; + tempChain[ins->mDst.mTemp].mConstant = tempChain[ins->mSrc[1].mTemp].mConstant; + } + else if (ins->mCode == IC_BINARY_OPERATOR && ins->mOperator == IA_ADD && + ins->mSrc[1].mTemp >= 0 && ins->mSrc[0].mTemp >= 0 && ins->mSrc[0].mRange.IsBound() && ins->mSrc[0].IsUnsigned() && + tempChain[ins->mSrc[1].mTemp].mBaseTemp >= 0) + { + tempChain[ins->mDst.mTemp].mBaseTemp = tempChain[ins->mSrc[1].mTemp].mBaseTemp; + tempChain[ins->mDst.mTemp].mOffset = tempChain[ins->mSrc[1].mTemp].mOffset + ins->mSrc[0].mRange.mMaxValue; + tempChain[ins->mDst.mTemp].mConstant = false; + } + else if (ins->mCode == IC_BINARY_OPERATOR && ins->mOperator == IA_ADD && + ins->mSrc[0].mTemp >= 0 && ins->mSrc[1].mTemp >= 0 && ins->mSrc[1].mRange.IsBound() && ins->mSrc[1].IsUnsigned() && + tempChain[ins->mSrc[0].mTemp].mBaseTemp >= 0) + { + tempChain[ins->mDst.mTemp].mBaseTemp = tempChain[ins->mSrc[0].mTemp].mBaseTemp; + tempChain[ins->mDst.mTemp].mOffset = tempChain[ins->mSrc[0].mTemp].mOffset + ins->mSrc[1].mRange.mMaxValue; + tempChain[ins->mDst.mTemp].mConstant = false; + } + else if (ins->mCode == IC_CONVERSION_OPERATOR && ins->mOperator == IA_EXT8TO16U && ins->mSrc[0].mTemp >= 0) + { + tempChain[ins->mDst.mTemp] = tempChain[ins->mSrc[0].mTemp]; + } + else if (ins->mDst.mTemp >= 0) + { + tempChain[ins->mDst.mTemp].mBaseTemp = -1; + tempChain[ins->mDst.mTemp].mWriteIns = i; + } + } + + for (int i = 0; i < tempChain.Size(); i++) + { + if (tempChain[i].mBaseTemp == i) + { + IntegerValueRange& r(pblock->mTrueValueRange[i]); + if (r.IsConstant()) + { + if (tempChain[i].mOffset >= 0) + mProc->mLocalValueRange[i].LimitMax(r.mMinValue + (nloop - 1) * tempChain[i].mOffset); + if (tempChain[i].mOffset <= 0) + mProc->mLocalValueRange[i].LimitMin(r.mMaxValue + (nloop - 1) * tempChain[i].mOffset); + } + } + else if (tempChain[i].mReadIns >= 0 && tempChain[i].mWriteIns > tempChain[i].mReadIns) + { + InterInstruction* rins(mInstructions[tempChain[i].mReadIns]); + InterInstruction* wins(mInstructions[tempChain[i].mWriteIns]); + + if (rins->mCode == IC_BINARY_OPERATOR && + wins->mCode == IC_BINARY_OPERATOR && wins->mSrc[1].mTemp == rins->mDst.mTemp) + { + IntegerValueRange& r(pblock->mTrueValueRange[i]); + if (r.IsConstant()) + { + if (rins->mOperator == IA_ADD && wins->mOperator == IA_SHR && wins->mSrc[0].mTemp < 0 && wins->mSrc[0].mIntConst == 8 && r.IsRange(0, 1)) + { + if (rins->mSrc[0].mTemp == i && rins->mSrc[1].mRange.IsRange(0, 510) || + rins->mSrc[1].mTemp == i && rins->mSrc[0].mRange.IsRange(0, 510)) + { + mProc->mLocalValueRange[i].LimitMax(1); + } + } + } + } + } + } + } + } + + UpdateLocalIntegerRangeSetsForward(); +#if 1 + + UpdateLocalIntegerRangeSetsBackward(); +#if 1 + mProc->mLocalValueRange = mEntryValueRange; + for (int i = 0; i < mProc->mLocalValueRange.Size(); i++) + mProc->mLocalValueRange[i].LimitWeak(mProc->mReverseValueRange[i]); + mLocalParamValueRange = mEntryParamValueRange; + + if (singleLoop) + { + for (int i = 0; i < tempChain.Size(); i++) + { + if (tempChain[i].mBaseTemp == i) + { + IntegerValueRange& r(pblock->mTrueValueRange[i]); + if (r.IsConstant()) + { + if (tempChain[i].mOffset >= 0) + mProc->mLocalValueRange[i].LimitMax(r.mMinValue + (nloop - 1) * tempChain[i].mOffset); + if (tempChain[i].mOffset <= 0) + mProc->mLocalValueRange[i].LimitMin(r.mMaxValue + (nloop - 1) * tempChain[i].mOffset); + } + } + } + } + + UpdateLocalIntegerRangeSetsForward(); + UpdateLocalIntegerRangeSetsBackward(); +#endif + +#endif + + mTrueValueRange = mProc->mLocalValueRange; + mFalseValueRange = mProc->mLocalValueRange; + mTrueParamValueRange = mLocalParamValueRange; + mFalseParamValueRange = mLocalParamValueRange; + + if (singleLoop && nfixed) + { + for (int i = 0; i < tempChain.Size(); i++) + { + if (tempChain[i].mBaseTemp == i && tempChain[i].mConstant) + { + IntegerValueRange& r(pblock->mTrueValueRange[i]); + if (r.IsConstant()) + { + if (mTrueJump == this) + mFalseValueRange[i].SetConstant(r.mMinValue + nloop * tempChain[i].mOffset); + else + mTrueValueRange[i].SetConstant(r.mMinValue + nloop * tempChain[i].mOffset); + } + } + } + } + + if (sz >= 1) + { + if (mInstructions[sz - 1]->mCode == IC_BRANCH && mInstructions[sz - 1]->mSrc[0].mTemp >= 0 && mInstructions[sz - 1]->mSrc[0].mType == IT_BOOL) + { + int s = mInstructions[sz - 1]->mSrc[0].mTemp; + + mTrueValueRange[s].mMinState = IntegerValueRange::S_BOUND; + mTrueValueRange[s].mMinValue = 1; + mTrueValueRange[s].mMaxState = IntegerValueRange::S_BOUND; + mTrueValueRange[s].mMaxValue = 1; + + mFalseValueRange[s].mMinState = IntegerValueRange::S_BOUND; + mFalseValueRange[s].mMinValue = 0; + mFalseValueRange[s].mMaxState = IntegerValueRange::S_BOUND; + mFalseValueRange[s].mMaxValue = 0; + } + else if (mInstructions[sz - 1]->mCode == IC_BRANCH && mInstructions[sz - 1]->mSrc[0].mTemp >= 0 && IsIntegerType(mInstructions[sz - 1]->mSrc[0].mType)) + { + int s = mInstructions[sz - 1]->mSrc[0].mTemp; + + mFalseValueRange[s].mMinState = IntegerValueRange::S_BOUND; + mFalseValueRange[s].mMinValue = 0; + mFalseValueRange[s].mMaxState = IntegerValueRange::S_BOUND; + mFalseValueRange[s].mMaxValue = 0; + } + } + + if (sz >= 2) + { + InterInstruction* cins = mInstructions[sz - 2]; + + if (mInstructions[sz - 1]->mCode == IC_BRANCH && cins->mCode == IC_RELATIONAL_OPERATOR && + mInstructions[sz - 1]->mSrc[0].mTemp == cins->mDst.mTemp && IsIntegerType(cins->mSrc[0].mType)) + { + int s1 = cins->mSrc[1].mTemp, s0 = cins->mSrc[0].mTemp; + int s1c = -1, s0c = -1; + InterOperator s1o, s0o; + +#if 1 + for (int si = 0; si < mExitConversionInstructions.Size(); si++) + { + if (mExitConversionInstructions[si]->mCode == IC_CONVERSION_OPERATOR && (mExitConversionInstructions[si]->mOperator == IA_EXT8TO16S || mExitConversionInstructions[si]->mOperator == IA_EXT8TO16U)) + { + if (s1 == mExitConversionInstructions[si]->mSrc[0].mTemp) + { + s1c = mExitConversionInstructions[si]->mDst.mTemp; + s1o = mExitConversionInstructions[si]->mOperator; + } + if (s0 == mExitConversionInstructions[si]->mSrc[0].mTemp) + { + s0c = mExitConversionInstructions[si]->mDst.mTemp; + s0o = mExitConversionInstructions[si]->mOperator; + } + } + } +#else + for(int si=0; simDst.mTemp == s0 || mInstructions[si]->mDst.mTemp == s0c) + s0c = -1; + else if (mInstructions[si]->mDst.mTemp == s1 || mInstructions[si]->mDst.mTemp == s1c) + s1c = -1; + if (mInstructions[si]->mCode == IC_CONVERSION_OPERATOR && (mInstructions[si]->mOperator == IA_EXT8TO16S || mInstructions[si]->mOperator == IA_EXT8TO16U)) + { + if (s1 == mInstructions[si]->mSrc[0].mTemp) + { + s1c = mInstructions[si]->mDst.mTemp; + s1o = mInstructions[si]->mOperator; + } + if (s0 == mInstructions[si]->mSrc[0].mTemp) + { + s0c = mInstructions[si]->mDst.mTemp; + s0o = mInstructions[si]->mOperator; + } + } + } +#endif + switch (cins->mOperator) + { +#if 1 + case IA_CMPEQ: + if (s0 < 0) + { + MarkIntegerRangeBoundUp(s1, cins->mSrc[0].mIntConst, mTrueValueRange); +#if 0 + mTrueValueRange[s1].mMinState = IntegerValueRange::S_BOUND; + mTrueValueRange[s1].mMinValue = mInstructions[sz - 2]->mSrc[0].mIntConst; + mTrueValueRange[s1].mMaxState = IntegerValueRange::S_BOUND; + mTrueValueRange[s1].mMaxValue = mInstructions[sz - 2]->mSrc[0].mIntConst; +#endif + } + else if (s1 < 0) + { + MarkIntegerRangeBoundUp(s0, cins->mSrc[1].mIntConst, mTrueValueRange); +#if 0 + mTrueValueRange[s0].mMinState = IntegerValueRange::S_BOUND; + mTrueValueRange[s0].mMinValue = mInstructions[sz - 2]->mSrc[1].mIntConst; + mTrueValueRange[s0].mMaxState = IntegerValueRange::S_BOUND; + mTrueValueRange[s0].mMaxValue = mInstructions[sz - 2]->mSrc[1].mIntConst; +#endif + } + break; + + case IA_CMPNE: + if (s0 < 0) + { + MarkIntegerRangeBoundUp(s1, cins->mSrc[0].mIntConst, mFalseValueRange); +#if 0 + mFalseValueRange[s1].mMinState = IntegerValueRange::S_BOUND; + mFalseValueRange[s1].mMinValue = mInstructions[sz - 2]->mSrc[0].mIntConst; + mFalseValueRange[s1].mMaxState = IntegerValueRange::S_BOUND; + mFalseValueRange[s1].mMaxValue = mInstructions[sz - 2]->mSrc[0].mIntConst; +#endif + } + else if (s1 < 0) + { + MarkIntegerRangeBoundUp(s0, cins->mSrc[1].mIntConst, mFalseValueRange); +#if 0 + mFalseValueRange[s0].mMinState = IntegerValueRange::S_BOUND; + mFalseValueRange[s0].mMinValue = mInstructions[sz - 2]->mSrc[1].mIntConst; + mFalseValueRange[s0].mMaxState = IntegerValueRange::S_BOUND; + mFalseValueRange[s0].mMaxValue = mInstructions[sz - 2]->mSrc[1].mIntConst; +#endif + } + break; +#endif + case IA_CMPLS: + if (s0 < 0) + { + mTrueValueRange[s1].LimitMax(cins->mSrc[0].mIntConst - 1); + if (mProc->mLocalValueRange[s1].mMaxState == IntegerValueRange::S_BOUND && + mProc->mLocalValueRange[s1].mMaxValue > SignedTypeMax(cins->mSrc[1].mType)) + mTrueValueRange[s1].mMinState = IntegerValueRange::S_UNBOUND; + else + mTrueValueRange[s1].LimitMinWeak(SignedTypeMin(cins->mSrc[1].mType)); + + mFalseValueRange[s1].LimitMin(cins->mSrc[0].mIntConst); + mFalseValueRange[s1].LimitMaxWeak(SignedTypeMax(cins->mSrc[1].mType)); + + if (s1c >= 0 && s1o == IA_EXT8TO16S) + { + mTrueValueRange[s1c].LimitMax(cins->mSrc[0].mIntConst - 1); + mTrueValueRange[s1c].LimitMinWeak(SignedTypeMin(cins->mSrc[1].mType)); + + mFalseValueRange[s1c].LimitMin(cins->mSrc[0].mIntConst); + mFalseValueRange[s1c].LimitMaxWeak(SignedTypeMax(cins->mSrc[1].mType)); + } + } + else if (s1 < 0) + { + mTrueValueRange[s0].LimitMin(cins->mSrc[1].mIntConst + 1); + mTrueValueRange[s0].LimitMaxWeak(SignedTypeMax(cins->mSrc[0].mType)); + + mFalseValueRange[s0].LimitMax(cins->mSrc[1].mIntConst); + mFalseValueRange[s0].LimitMinWeak(SignedTypeMin(cins->mSrc[0].mType)); + + if (s0c >= 0 && s0o == IA_EXT8TO16S) + { + mTrueValueRange[s0c].LimitMin(cins->mSrc[1].mIntConst + 1); + mTrueValueRange[s0c].LimitMaxWeak(SignedTypeMax(cins->mSrc[0].mType)); + + mFalseValueRange[s0c].LimitMax(cins->mSrc[1].mIntConst); + mFalseValueRange[s0c].LimitMinWeak(SignedTypeMin(cins->mSrc[0].mType)); + } + } + else + { + if (mProc->mLocalValueRange[s0].mMaxState == IntegerValueRange::S_BOUND) + mTrueValueRange[s1].LimitMaxWeak(mProc->mLocalValueRange[s0].mMaxValue - 1); + if (mProc->mLocalValueRange[s0].mMinState == IntegerValueRange::S_BOUND) + mFalseValueRange[s1].LimitMinWeak(mProc->mLocalValueRange[s0].mMinValue); + } + break; + case IA_CMPLES: + if (s0 < 0) + { + mTrueValueRange[s1].LimitMax(cins->mSrc[0].mIntConst); + mTrueValueRange[s1].LimitMinWeak(SignedTypeMin(cins->mSrc[1].mType)); + mFalseValueRange[s1].LimitMin(cins->mSrc[0].mIntConst + 1); + mFalseValueRange[s1].LimitMaxWeak(SignedTypeMax(cins->mSrc[1].mType)); + } + else if (s1 < 0) + { + mTrueValueRange[s0].LimitMin(cins->mSrc[1].mIntConst); + mTrueValueRange[s0].LimitMaxWeak(SignedTypeMax(cins->mSrc[0].mType)); + mFalseValueRange[s0].LimitMax(cins->mSrc[1].mIntConst - 1); + mFalseValueRange[s0].LimitMinWeak(SignedTypeMin(cins->mSrc[0].mType)); + } + else + { + if (mProc->mLocalValueRange[s0].mMaxState == IntegerValueRange::S_BOUND) + mFalseValueRange[s1].LimitMin(mProc->mLocalValueRange[s0].mMinValue + 1); + if (mProc->mLocalValueRange[s0].mMinState == IntegerValueRange::S_BOUND) + mTrueValueRange[s1].LimitMax(mProc->mLocalValueRange[s0].mMaxValue); + + if (mProc->mLocalValueRange[s1].mMaxState == IntegerValueRange::S_BOUND) + mFalseValueRange[s0].LimitMax(mProc->mLocalValueRange[s1].mMaxValue - 1); + if (mProc->mLocalValueRange[s1].mMinState == IntegerValueRange::S_BOUND) + mTrueValueRange[s0].LimitMin(mProc->mLocalValueRange[s1].mMinValue); + } + break; + case IA_CMPGS: + if (s0 < 0) + { + mTrueValueRange[s1].LimitMin(cins->mSrc[0].mIntConst + 1); + mTrueValueRange[s1].LimitMaxWeak(SignedTypeMax(cins->mSrc[1].mType)); + mFalseValueRange[s1].LimitMax(cins->mSrc[0].mIntConst); + mFalseValueRange[s1].LimitMinWeak(SignedTypeMin(cins->mSrc[1].mType)); + } + else if (s1 < 0) + { + mTrueValueRange[s0].LimitMax(cins->mSrc[1].mIntConst - 1); + mTrueValueRange[s0].LimitMinWeak(SignedTypeMin(cins->mSrc[0].mType)); + mFalseValueRange[s0].LimitMin(cins->mSrc[1].mIntConst); + mFalseValueRange[s0].LimitMaxWeak(SignedTypeMax(cins->mSrc[0].mType)); + } + else + { + if (mProc->mLocalValueRange[s0].mMaxState == IntegerValueRange::S_BOUND) + mTrueValueRange[s1].LimitMin(mProc->mLocalValueRange[s0].mMinValue + 1); + if (mProc->mLocalValueRange[s0].mMinState == IntegerValueRange::S_BOUND) + mFalseValueRange[s1].LimitMax(mProc->mLocalValueRange[s0].mMaxValue); + + if (mProc->mLocalValueRange[s1].mMaxState == IntegerValueRange::S_BOUND) + mTrueValueRange[s0].LimitMax(mProc->mLocalValueRange[s1].mMaxValue - 1); + if (mProc->mLocalValueRange[s1].mMinState == IntegerValueRange::S_BOUND) + mFalseValueRange[s0].LimitMin(mProc->mLocalValueRange[s1].mMinValue); + } + break; + case IA_CMPGES: + if (s0 < 0) + { + mTrueValueRange[s1].LimitMin(cins->mSrc[0].mIntConst); + mTrueValueRange[s1].LimitMaxWeak(SignedTypeMax(cins->mSrc[1].mType)); + mFalseValueRange[s1].LimitMax(cins->mSrc[0].mIntConst - 1); + mFalseValueRange[s1].LimitMinWeak(SignedTypeMin(cins->mSrc[1].mType)); + } + break; + + case IA_CMPLU: + if (s1 >= 0) + { + if (s0 < 0) + { + if (cins->mSrc[0].mIntConst > 0) + { + mTrueValueRange[s1].LimitMax(cins->mSrc[0].mIntConst - 1); + + if (s1c >= 0 && s1o == IA_EXT8TO16U) + { + mTrueValueRange[s1c].LimitMax(cins->mSrc[0].mIntConst - 1); + mFalseValueRange[s1c].LimitMin(cins->mSrc[0].mIntConst); + } + + if (cins->mSrc[0].mIntConst > 0 && cins->mSrc[0].mIntConst < SignedTypeMax(cins->mSrc[1].mType)) + mTrueValueRange[s1].LimitMin(0); + else + mTrueValueRange[s1].LimitMinWeak(0); + +/* if (cins->mSrc[1].mType == IT_INT8) + { + mFalseValueRange[s1].LimitMin(cins->mSrc[0].mIntConst); + mFalseValueRange[s1].LimitMax(255); + } + else*/ if (mFalseValueRange[s1].mMinState == IntegerValueRange::S_BOUND && mFalseValueRange[s1].mMinValue >= 0) + { + mFalseValueRange[s1].LimitMin(cins->mSrc[0].mIntConst); + } +#if 0 + else if (cins->mSrc[1].IsPositive()) + { + mFalseValueRange[s1].LimitMin(cins->mSrc[0].mIntConst); + } +#endif + } + } + else if (cins->mSrc[0].mRange.mMaxState == IntegerValueRange::S_BOUND) + { + mTrueValueRange[s1].LimitMax(cins->mSrc[0].mRange.mMaxValue - 1); + mTrueValueRange[s1].LimitMinWeak(0); + + if (mFalseValueRange[s1].mMinState == IntegerValueRange::S_BOUND && mFalseValueRange[s1].mMinValue >= 0) + { + mFalseValueRange[s1].LimitMin(cins->mSrc[0].mRange.mMinValue); + } + } + } + else if (s0 >= 0) + { + mTrueValueRange[s0].LimitMin(cins->mSrc[1].mIntConst + 1); + mFalseValueRange[s0].LimitMax(cins->mSrc[1].mIntConst); + } + break; + case IA_CMPLEU: + if (s0 < 0) + { + mTrueValueRange[s1].LimitMax(cins->mSrc[0].mIntConst); + mTrueValueRange[s1].LimitMin(0); + if (s1c >= 0) + { + mTrueValueRange[s1c].LimitMax(cins->mSrc[0].mIntConst); + mTrueValueRange[s1c].LimitMin(0); + } + + if (mFalseValueRange[s1].mMinState == IntegerValueRange::S_BOUND && + mFalseValueRange[s1].mMinValue >= 0 || mFalseJump->TempIsUnsigned(s1)) + { + mFalseValueRange[s1].LimitMin(cins->mSrc[0].mIntConst + 1); + } + if (s1c >= 0 && s1o == IA_EXT8TO16U) + mFalseValueRange[s1c].LimitMin(cins->mSrc[0].mIntConst + 1); + } + break; + case IA_CMPGU: + if (s1 >= 0) + { + if (cins->mSrc[1].mRange.mMinState == IntegerValueRange::S_BOUND && cins->mSrc[1].mRange.mMinValue < 0) + mTrueValueRange[s1].mMaxState = IntegerValueRange::S_UNBOUND; + + mTrueValueRange[s1].LimitMin(1); + if (s0 < 0) + { + if (cins->mSrc[0].mIntConst >= 0) + { + mTrueValueRange[s1].LimitMin(cins->mSrc[0].mIntConst + 1); + mFalseValueRange[s1].LimitMax(cins->mSrc[0].mIntConst); + mFalseValueRange[s1].LimitMin(0); + } + } + } + break; + case IA_CMPGEU: + if (s0 < 0) + { + if (cins->mSrc[1].mRange.mMinState == IntegerValueRange::S_BOUND && cins->mSrc[1].mRange.mMinValue < 0) + mTrueValueRange[s1].mMaxState = IntegerValueRange::S_UNBOUND; + + mTrueValueRange[s1].LimitMin(cins->mSrc[0].mIntConst); + mFalseValueRange[s1].LimitMax(cins->mSrc[0].mIntConst - 1); + mFalseValueRange[s1].LimitMin(0); + } + else + mFalseValueRange[s0].LimitMin(1); + break; + } + + if (s1 >= 0 && sz > 2 && mInstructions[sz - 3]->mCode == IC_LOAD_TEMPORARY && mInstructions[sz - 3]->mSrc[0].mTemp == s1) + { + mTrueValueRange[mInstructions[sz - 3]->mDst.mTemp] = mTrueValueRange[s1]; + mFalseValueRange[mInstructions[sz - 3]->mDst.mTemp] = mFalseValueRange[s1]; + } + + if (sz >= 3 && mInstructions[sz - 3]->mCode == IC_LOAD) + { + InterInstruction* ins = mInstructions[sz - 3]; + + if (ins->mSrc[0].mTemp < 0 && ins->mSrc[0].mMemory == IM_FPARAM) + { + mTrueParamValueRange[ins->mSrc[0].mVarIndex].Limit(mTrueValueRange[ins->mDst.mTemp]); + mFalseParamValueRange[ins->mSrc[0].mVarIndex].Limit(mFalseValueRange[ins->mDst.mTemp]); + } + } + } + else if (mInstructions[sz - 1]->mCode == IC_BRANCH && mInstructions[sz - 2]->mCode == IC_RELATIONAL_OPERATOR && + mInstructions[sz - 1]->mSrc[0].mTemp == mInstructions[sz - 2]->mDst.mTemp && + cins->mSrc[0].mType == IT_POINTER && cins->mSrc[1].mType == IT_POINTER) + { + + + int s1 = cins->mSrc[1].mTemp, s0 = cins->mSrc[0].mTemp; + + } + + } + + for (int i = 0; i < mProc->mLocalValueRange.Size(); i++) + { + if (!mExitRequiredTemps[i]) + { + mProc->mLocalValueRange[i].mMinState = mProc->mLocalValueRange[i].mMaxState = IntegerValueRange::S_UNKNOWN; + mTrueValueRange[i] = mFalseValueRange[i] = mProc->mLocalValueRange[i]; + } + } +} + + + +void InterCodeBasicBlock::PruneUnusedIntegerRangeSets(void) +{ + if (!mVisited) + { + mVisited = true; + + if (mEntryValueRange.Size() > 0 && mEntryRequiredTemps.Size()) + { + for (int i = 0; i < mEntryValueRange.Size(); i++) + { + if (!mEntryRequiredTemps[i]) + mEntryValueRange[i].Reset(); + } + } + + if (mTrueJump) mTrueJump->PruneUnusedIntegerRangeSets(); + if (mFalseJump) mFalseJump->PruneUnusedIntegerRangeSets(); + } +} + +void InterCodeBasicBlock::RestartLocalIntegerRangeSets(int num) +{ + if (!mVisited) + { + mVisited = true; + + mValueRangeValid = false; + + mEntryValueRange.SetSize(num, false); + mTrueValueRange.SetSize(num, false); + mFalseValueRange.SetSize(num, false); +// mLocalValueRange.SetSize(num, false); + mMemoryValueSize.SetSize(num, false); + mEntryMemoryValueSize.SetSize(num, false); +#if 0 + if (mTrueJump && !mTrueJump->mVisited) + mTrueJump->mMemoryValueSize.SetSize(num, true); + if (mFalseJump && !mFalseJump->mVisited) + mFalseJump->mMemoryValueSize.SetSize(num, true); +#endif + mEntryParamValueRange.SetSize(mProc->mParamVars.Size(), false); + mTrueParamValueRange.SetSize(mProc->mParamVars.Size(), false); + mFalseParamValueRange.SetSize(mProc->mParamVars.Size(), false); + mLocalParamValueRange.SetSize(mProc->mParamVars.Size(), false); + + for (int i = 0; i < mEntryValueRange.Size(); i++) + mEntryValueRange[i].Restart(); + + for (int i = 0; i < mEntryParamValueRange.Size(); i++) + mEntryParamValueRange[i].Restart(); + + for (int i = 0; i < mInstructions.Size(); i++) + { + mInstructions[i]->mDst.mRange.Restart(); + for (int j = 0; j < mInstructions[i]->mNumOperands; j++) + mInstructions[i]->mSrc[j].mRange.Restart(); + } + + UpdateLocalIntegerRangeSets(); + + if (mTrueJump) mTrueJump->RestartLocalIntegerRangeSets(num); + if (mFalseJump) mFalseJump->RestartLocalIntegerRangeSets(num); + } +} + +void InterCodeBasicBlock::BuildLocalIntegerRangeSets(int num) +{ + if (!mVisited) + { + mVisited = true; + + mEntryValueRange.SetSize(num, true); + mTrueValueRange.SetSize(num, true); + mFalseValueRange.SetSize(num, true); +// mLocalValueRange.SetSize(num, true); + mMemoryValueSize.SetSize(num, true); + mEntryMemoryValueSize.SetSize(num, true); + + mEntryParamValueRange.SetSize(mProc->mParamVars.Size(), true); + mTrueParamValueRange.SetSize(mProc->mParamVars.Size(), true); + mFalseParamValueRange.SetSize(mProc->mParamVars.Size(), true); + mLocalParamValueRange.SetSize(mProc->mParamVars.Size(), true); + + UpdateLocalIntegerRangeSets(); + + if (mTrueJump) mTrueJump->BuildLocalIntegerRangeSets(num); + if (mFalseJump) mFalseJump->BuildLocalIntegerRangeSets(num); + } +} + +void InterCodeBasicBlock::PropagateValueRangeSetConversions(const ExpandingInstructionPtrArray& tvalue) +{ + if (!mVisited) + { + if (mLoopHead) + { + mExitConversionInstructions.SetSize(0); + } + else if (mNumEntries > 0) + { + if (mNumEntered > 0) + { + int i = 0, j = 0; + while (i < mExitConversionInstructions.Size()) + { + if (tvalue.Contains(mExitConversionInstructions[i])) + mExitConversionInstructions[j++] = mExitConversionInstructions[i++]; + else + i++; + } + mExitConversionInstructions.SetSize(j); + } + else + mExitConversionInstructions = tvalue; + + mNumEntered++; + + if (mNumEntered < mNumEntries) + return; + } + + for (int k = 0; k < mInstructions.Size(); k++) + { + InterInstruction* ins = mInstructions[k]; + + int reg = ins->mDst.mTemp; + if (reg >= 0) + { + int i = 0, j = 0; + while (i < mExitConversionInstructions.Size()) + { + if (mExitConversionInstructions[i]->ReferencesTemp(reg)) + i++; + else + mExitConversionInstructions[j++] = mExitConversionInstructions[i++]; + } + mExitConversionInstructions.SetSize(j); + if (ins->mCode == IC_CONVERSION_OPERATOR || ins->mCode == IC_LOAD_TEMPORARY) + mExitConversionInstructions.Push(ins); + } + } + + mVisited = true; + + if (mTrueJump) mTrueJump->PropagateValueRangeSetConversions(mExitConversionInstructions); + if (mFalseJump) mFalseJump->PropagateValueRangeSetConversions(mExitConversionInstructions); + } +} + +void InterCodeBasicBlock::BuildConstTempSets(void) +{ + int i; + + if (!mVisited) + { + mVisited = true; + + mEntryConstTemp.Reset(mEntryRequiredTemps.Size()); + mExitConstTemp.Reset(mEntryRequiredTemps.Size()); + + for (i = 0; i < mInstructions.Size(); i++) + { + const InterInstruction* ins = mInstructions[i]; + + if (ins->mDst.mTemp >= 0) + { + if (ins->mCode == IC_CONSTANT) + mExitConstTemp += ins->mDst.mTemp; + else + mExitConstTemp -= ins->mDst.mTemp; + } + } + + if (mTrueJump) mTrueJump->BuildConstTempSets(); + if (mFalseJump) mFalseJump->BuildConstTempSets(); + } +} + +bool InterCodeBasicBlock::PropagateConstOperationsUp(void) +{ +// return false; + + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + if (mTrueJump && mTrueJump->PropagateConstOperationsUp()) + changed = true; + + if (mFalseJump && mFalseJump->PropagateConstOperationsUp()) + changed = true; + + if (mEntryBlocks.Size()) + { + mEntryConstTemp = mEntryBlocks[0]->mExitConstTemp; + + for (int i = 1; i < mEntryBlocks.Size(); i++) + mEntryConstTemp &= mEntryBlocks[i]->mExitConstTemp; + + int i = 0; + while (i + 1 < mInstructions.Size()) + { + const InterInstruction* ins = mInstructions[i]; + + if (!HasSideEffect(ins->mCode) && ins->mCode != IC_CONSTANT && ins->mCode != IC_STORE && ins->mCode != IC_COPY && ins->mCode != IC_FILL) + { + bool isProvided = false; + if (ins->mDst.mTemp >= 0) + { + for (int j = 0; j < mEntryBlocks.Size(); j++) + if (mEntryBlocks[j]->mExitRequiredTemps[ins->mDst.mTemp]) + isProvided = true; + } + + + bool hasop = false; + int j = 0; + while (j < ins->mNumOperands && (ins->mSrc[j].mTemp < 0 || mEntryConstTemp[ins->mSrc[j].mTemp])) + { + if (ins->mSrc[j].mTemp >= 0) + hasop = true; + j++; + } + + if (j == ins->mNumOperands && hasop && !isProvided && CanMoveInstructionBeforeBlock(i)) + { + for (int j = 0; j < mEntryBlocks.Size(); j++) + { + InterInstruction* nins = ins->Clone(); + InterCodeBasicBlock* eb = mEntryBlocks[j]; + + int di = eb->mInstructions.Size() - 1; + if (eb->mInstructions[di]->mCode == IC_BRANCH && di > 0 && eb->mInstructions[di - 1]->mDst.mTemp == eb->mInstructions[di]->mSrc[0].mTemp && + CanSwapInstructions(eb->mInstructions[di - 1], ins)) + { + di--; + } + + eb->mInstructions.Insert(di, nins); + if (ins->mDst.mTemp >= 0) + eb->mExitRequiredTemps += ins->mDst.mTemp; + } + + if (ins->mDst.mTemp >= 0) + mEntryRequiredTemps += ins->mDst.mTemp; + + mInstructions.Remove(i); + changed = true; + } + else + i++; + } + else + i++; + + if (ins->mDst.mTemp >= 0) + mEntryConstTemp -= ins->mDst.mTemp; + } + } + } + + return changed; +} + + + + +void InterCodeBasicBlock::BuildLocalTempSets(int num) +{ + int i; + + if (!mVisited) + { + mVisited = true; + + mLocalRequiredTemps.Reset(num); + mLocalProvidedTemps.Reset(num); + + mEntryRequiredTemps.Reset(num); + mEntryProvidedTemps.Reset(num); + mEntryPotentialTemps.Reset(num); + mExitRequiredTemps.Reset(num); + mExitProvidedTemps.Reset(num); + mExitPotentialTemps.Reset(num); + + for (i = 0; i < mInstructions.Size(); i++) + { + mInstructions[i]->FilterTempUsage(mLocalRequiredTemps, mLocalProvidedTemps); + } + + mEntryRequiredTemps = mLocalRequiredTemps; + mExitProvidedTemps = mLocalProvidedTemps; + mExitPotentialTemps = mLocalProvidedTemps; + + if (mTrueJump) mTrueJump->BuildLocalTempSets(num); + if (mFalseJump) mFalseJump->BuildLocalTempSets(num); + } +} + +void InterCodeBasicBlock::BuildGlobalProvidedTempSet(const NumberSet & fromProvidedTemps, const NumberSet& potentialProvidedTemps) +{ + bool changed = false; + + if (!mVisited) + { + mEntryProvidedTemps = fromProvidedTemps; + mEntryPotentialTemps = potentialProvidedTemps; + changed = true; + } + else + { + if (!(mEntryProvidedTemps <= fromProvidedTemps)) + { + mEntryProvidedTemps &= fromProvidedTemps; + changed = true; + } + if (!(potentialProvidedTemps <= mEntryPotentialTemps)) + { + mEntryPotentialTemps |= potentialProvidedTemps; + changed = true; + } + } + + if (changed) + { + mExitProvidedTemps = mLocalProvidedTemps; + mExitProvidedTemps |= mEntryProvidedTemps; + + mExitPotentialTemps = mLocalProvidedTemps; + mExitPotentialTemps |= mEntryPotentialTemps; + + mVisited = true; + + if (mTrueJump) mTrueJump->BuildGlobalProvidedTempSet(mExitProvidedTemps, mExitPotentialTemps); + if (mFalseJump) mFalseJump->BuildGlobalProvidedTempSet(mExitProvidedTemps, mExitPotentialTemps); + } +} + +static bool SameSingleAssignment(const GrowingInstructionPtrArray& tunified, const InterInstruction* ins, const InterInstruction* cins) +{ + if (ins->mCode == cins->mCode && ins->mOperator == cins->mOperator && ins->mNumOperands == cins->mNumOperands) + { + if (ins->mCode == IC_CONSTANT) + return ins->mConst.IsEqual(cins->mConst); + + for (int i = 0; i < ins->mNumOperands; i++) + { + if (ins->mSrc[i].mTemp < 0) + { + if (cins->mSrc[i].mTemp < 0) + { + if (!ins->mSrc[i].IsEqual(cins->mSrc[i])) + return false; + } + else + return false; + } + else if (cins->mSrc[i].mTemp < 0) + { + return false; + } + else if (!tunified[ins->mSrc[i].mTemp]) + return false; + else if (tunified[ins->mSrc[i].mTemp] != tunified[cins->mSrc[i].mTemp]) + return false; + } + + return true; + } + else + return false; +} + +bool InterCodeBasicBlock::SingleAssignmentTempForwarding(const GrowingInstructionPtrArray& tunified, const GrowingInstructionPtrArray& tvalues) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + GrowingInstructionPtrArray ntunified(tunified), ntvalues(tvalues); + + NumberSet providedTemps(mEntryProvidedTemps); + + for (int i = 0; i < mInstructions.Size(); i++) + { + InterInstruction* ins = mInstructions[i]; + if (ins->mSingleAssignment) + { + int j = 0; + while (j < ntvalues.Size() && !(providedTemps[ntvalues[j]->mDst.mTemp] && SameSingleAssignment(ntunified, ins, ntvalues[j]))) + j++; + if (j < ntvalues.Size()) + { + if (ins->mCode != IC_CONSTANT && !(ins->mCode == IC_LOAD && ins->mSrc[0].mMemory == IM_FPARAM)) + { + ins->mCode = IC_LOAD_TEMPORARY; + ins->mSrc[0].mTemp = ntvalues[j]->mDst.mTemp; + ins->mSrc[0].mType = ntvalues[j]->mDst.mType; + ins->mNumOperands = 1; + assert(ins->mSrc[0].mTemp >= 0); + } + changed = true; + ntunified[ins->mDst.mTemp] = ntvalues[j]; + } + else + { + ntvalues.Push(ins); + ntunified[ins->mDst.mTemp] = ins; + } + } + if (ins->mDst.mTemp >= 0) + providedTemps += ins->mDst.mTemp; + } + + if (mTrueJump && mTrueJump->SingleAssignmentTempForwarding(ntunified, ntvalues)) + changed = true; + if (mFalseJump && mFalseJump->SingleAssignmentTempForwarding(ntunified, ntvalues)) + changed = true; + } + + return changed; +} + +void InterCodeBasicBlock::CalculateSingleUsedTemps(FastNumberSet& fused, FastNumberSet& fsingle) +{ + if (!mVisited) + { + mVisited = true; + + for (int i = 0; i < mInstructions.Size(); i++) + { + InterInstruction* ins = mInstructions[i]; + + for (int j = 0; j < ins->mNumOperands; j++) + { + if (ins->mSrc[j].mTemp >= 0) + { + if (fused[ins->mSrc[j].mTemp]) + fsingle -= ins->mSrc[j].mTemp; + else + { + fused += ins->mSrc[j].mTemp; + fsingle += ins->mSrc[j].mTemp; + } + } + } + } + + if (mTrueJump) + mTrueJump->CalculateSingleUsedTemps(fused, fsingle); + if (mFalseJump) + mFalseJump->CalculateSingleUsedTemps(fused, fsingle); + } +} + +void InterCodeBasicBlock::CheckSingleAssignmentBools(GrowingInstructionPtrArray& tvalue) +{ + if (!mVisited) + { + mVisited = true; + + for (int i = 0; i < mInstructions.Size(); i++) + { + InterInstruction* ins = mInstructions[i]; + + for (int k = 0; k < ins->mNumOperands; k++) + { + if (ins->mCode != IC_BRANCH && ins->mSrc[k].mTemp >= 0 && tvalue[ins->mSrc[k].mTemp]) + tvalue[ins->mSrc[k].mTemp] = nullptr; + } + } + + if (mTrueJump) mTrueJump->CheckSingleAssignmentBools(tvalue); + if (mFalseJump) mFalseJump->CheckSingleAssignmentBools(tvalue); + } +} + +void InterCodeBasicBlock::MapSingleAssignmentBools(const GrowingInstructionPtrArray& tvalue) +{ + if (!mVisited) + { + mVisited = true; + + int sz = mInstructions.Size(); + if (sz > 0 && mInstructions[sz - 1]->mCode == IC_BRANCH && mInstructions[sz - 1]->mSrc[0].mTemp >= 0 && tvalue[mInstructions[sz - 1]->mSrc[0].mTemp]) + { + if (tvalue[mInstructions[sz - 1]->mSrc[0].mTemp]->mOperator == IA_CMPEQ) + { + InterCodeBasicBlock* tb = mTrueJump; mTrueJump = mFalseJump; mFalseJump = tb; + } + + mInstructions[sz - 1]->mSrc[0] = tvalue[mInstructions[sz - 1]->mSrc[0].mTemp]->mDst; + } + + if (mTrueValueRange.Size()) + { + for (int i = 0; i < tvalue.Size(); i++) + { + if (tvalue[i]) + { + mEntryValueRange[i].Reset(); + mTrueValueRange[i].Reset(); + mFalseValueRange[i].Reset(); + } + } + } + + if (mTrueJump) mTrueJump->MapSingleAssignmentBools(tvalue); + if (mFalseJump) mFalseJump->MapSingleAssignmentBools(tvalue); + } +} + +void InterCodeBasicBlock::CollectSingleAssignmentBools(FastNumberSet& tassigned, GrowingInstructionPtrArray& tvalue) +{ + if (!mVisited) + { + mVisited = true; + + for (int i = 0; i < mInstructions.Size(); i++) + { + InterInstruction* ins = mInstructions[i]; + + int t = ins->mDst.mTemp; + if (t >= 0) + { + if (!tassigned[t]) + { + tassigned += t; + + if (ins->mCode == IC_RELATIONAL_OPERATOR && + (ins->mOperator == IA_CMPEQ || ins->mOperator == IA_CMPNE) && + (ins->mSrc[0].mTemp < 0 && ins->mSrc[0].mIntConst == 0 && ins->mSrc[1].IsUByte() || ins->mSrc[1].mTemp < 0 && ins->mSrc[1].mIntConst == 0 && ins->mSrc[0].IsUByte())) + { + if (i + 2 == mInstructions.Size() && mInstructions[i + 1]->mCode == IC_BRANCH && mInstructions[i + 1]->mSrc[0].mTemp == ins->mDst.mTemp) + ; + else + tvalue[t] = ins; + } + } + else + tvalue[t] = nullptr; + } + } + + if (mTrueJump) mTrueJump->CollectSingleAssignmentBools(tassigned, tvalue); + if (mFalseJump) mFalseJump->CollectSingleAssignmentBools(tassigned, tvalue); + } +} + +bool InterCodeBasicBlock::CalculateSingleAssignmentTemps(FastNumberSet& tassigned, GrowingInstructionPtrArray& tvalue, NumberSet& modifiedParams, InterMemory paramMemory) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + for (int i = 0; i < mInstructions.Size(); i++) + { + InterInstruction* ins = mInstructions[i]; + + int t = ins->mDst.mTemp; + if (t >= 0) + { + if (!tassigned[t] || tvalue[t] == ins) + { + if (!tassigned[t]) + { + changed = true; + tassigned += t; + } + + int j = 0; + while (j < ins->mNumOperands && (ins->mSrc[j].mTemp < 0 || tvalue[ins->mSrc[j].mTemp] != nullptr)) + j++; + + bool valid = j == ins->mNumOperands; + if (valid) + { + if (ins->mCode == IC_CALL || ins->mCode == IC_CALL_NATIVE || ins->mCode == IC_ASSEMBLER || ins->mCode == IC_MALLOC || ins->mCode == IC_FREE) + valid = false; + else if (ins->mCode == IC_LOAD) + { + if (ins->mVolatile) + valid = false; + else if (ins->mSrc[0].mMemory == paramMemory) + { + if (modifiedParams[ins->mSrc[0].mVarIndex]) + valid = false; + } + else if (ins->mSrc[0].mMemory == IM_GLOBAL) + { + if (!(ins->mSrc[0].mLinkerObject->mFlags & LOBJF_CONST)) + valid = false; + } + else + valid = false; + } + else if (ins->mCode == IC_LEA) + { + if (ins->mSrc[1].mMemory == paramMemory) + { + if (!modifiedParams[ins->mSrc[1].mVarIndex]) + { + modifiedParams += ins->mSrc[1].mVarIndex; + changed = true; + } + } + } + else if (ins->mCode == IC_CONSTANT) + { + if (ins->mConst.mType == IT_POINTER && ins->mConst.mMemory == paramMemory) + { + if (!modifiedParams[ins->mConst.mVarIndex]) + { + modifiedParams += ins->mConst.mVarIndex; + changed = true; + } + } + } + } + + if (valid) + { + if (!tvalue[t]) + { + tvalue[t] = ins; + changed = true; + } + } + else if (tvalue[t]) + { + tvalue[t] = nullptr; + changed = true; + } + } + else if (tvalue[t]) + { + tvalue[t] = nullptr; + changed = true; + } + } + else if (ins->mCode == IC_STORE) + { + if (ins->mSrc[1].mMemory == paramMemory) + { + if (!modifiedParams[ins->mSrc[1].mVarIndex]) + { + modifiedParams += ins->mSrc[1].mVarIndex; + changed = true; + } + } + } + } + + if (mTrueJump && mTrueJump->CalculateSingleAssignmentTemps(tassigned, tvalue, modifiedParams, paramMemory)) + changed = true; + if (mFalseJump && mFalseJump->CalculateSingleAssignmentTemps(tassigned, tvalue, modifiedParams, paramMemory)) + changed = true; + } + + return changed; +} + +void InterCodeBasicBlock::PerformTempForwarding(const TempForwardingTable& forwardingTable, bool reverse, bool checkloops) +{ + int i; + + if (!mVisited) + { + if (mLoopHead) + { + if (mNumEntries == 2 && (mTrueJump == this || mFalseJump == this) && mLocalModifiedTemps.Size()) + { + mMergeForwardingTable = forwardingTable; + assert(mMergeForwardingTable.Size() == mLocalModifiedTemps.Size()); + + for (int i = 0; i < mLocalModifiedTemps.Size(); i++) + { + if (mLocalModifiedTemps[i]) + mMergeForwardingTable.Destroy(i); + } + } + else if (mLoopPrefix && checkloops) + { + ExpandingArray body; + body.Push(this); + bool innerLoop = true; + + for (int i = 0; i < mEntryBlocks.Size(); i++) + { + if (mEntryBlocks[i] != mLoopPrefix) + { + if (!mEntryBlocks[i]->CollectLoopBodyRecursive(this, body)) + innerLoop = false; + } + } + + if (innerLoop) + { + mMergeForwardingTable = forwardingTable; + assert(mMergeForwardingTable.Size() == mLocalModifiedTemps.Size()); + + for (int j = 0; j < body.Size(); j++) + { + for (int i = 0; i < mLocalModifiedTemps.Size(); i++) + { + if (body[j]->mLocalModifiedTemps[i]) + mMergeForwardingTable.Destroy(i); + } + } + } + else + mMergeForwardingTable.SetSize(forwardingTable.Size()); + } + else + mMergeForwardingTable.SetSize(forwardingTable.Size()); + } + else + { + if (mNumEntered == 0) + mMergeForwardingTable = forwardingTable; + else + mMergeForwardingTable.Intersect(forwardingTable); + + mNumEntered++; + + if (mNumEntered < mNumEntries) + return; + } + + mVisited = true; + + for (i = 0; i < mInstructions.Size(); i++) + { + mInstructions[i]->PerformTempForwarding(mMergeForwardingTable, reverse); + } + + if (mTrueJump) mTrueJump->PerformTempForwarding(mMergeForwardingTable, reverse, checkloops); + if (mFalseJump) mFalseJump->PerformTempForwarding(mMergeForwardingTable, reverse, checkloops); + + mMergeForwardingTable.Shrink(); + } +} + +bool InterCodeBasicBlock::BuildGlobalRequiredTempSet(NumberSet& fromRequiredTemps) +{ + bool revisit = false; + + if (!mVisited) + { + mVisited = true; + + mNewRequiredTemps = mExitRequiredTemps; + +// NumberSet newRequiredTemps(mExitRequiredTemps); + + if (mTrueJump && mTrueJump->BuildGlobalRequiredTempSet(mNewRequiredTemps)) revisit = true; + if (mFalseJump && mFalseJump->BuildGlobalRequiredTempSet(mNewRequiredTemps)) revisit = true; + + if (!(mNewRequiredTemps <= mExitRequiredTemps)) + { + revisit = true; + + mExitRequiredTemps = mNewRequiredTemps; + mNewRequiredTemps -= mLocalProvidedTemps; + mEntryRequiredTemps |= mNewRequiredTemps; + } + + } + + fromRequiredTemps |= mEntryRequiredTemps; + + return revisit; +} + +bool InterCodeBasicBlock::RemoveUnusedResultInstructions(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + NumberSet requiredTemps(mExitRequiredTemps); + int i; + + if (mInstructions.Size() > 0) + { + for (i = mInstructions.Size() - 1; i > 0; i--) + { + if (mInstructions[i]->RemoveUnusedResultInstructions(mInstructions[i - 1], requiredTemps)) + changed = true; + } + if (mInstructions[0]->RemoveUnusedResultInstructions(NULL, requiredTemps)) + changed = true; + } + + if (mTrueJump) + { + if (mTrueJump->RemoveUnusedResultInstructions()) + changed = true; + } + if (mFalseJump) + { + if (mFalseJump->RemoveUnusedResultInstructions()) + changed = true; + } + } + + return changed; +} + +bool InterCodeBasicBlock::RemoveUnusedLocalStoreInstructions(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + if (mInstructions.Size() > 0 && mInstructions.Last()->mCode == IC_RETURN) + { + int i = mInstructions.Size(); + while (i > 0) + { + i--; + InterInstruction* ins = mInstructions[i]; + if (ins->mCode == IC_STORE && ins->mSrc[1].mTemp < 0 && ins->mSrc[1].mMemory == IM_LOCAL) + { + ins->mCode = IC_NONE; + ins->mNumOperands = 0; + changed = true; + } + else if (ins->mCode == IC_LOAD) + break; + else if (ins->mCode == IC_CALL || ins->mCode == IC_CALL_NATIVE) + break; + else if (ins->mCode == IC_COPY || ins->mCode == IC_STRCPY || ins->mCode == IC_FILL) + break; + } + } + + if (mTrueJump && mTrueJump->RemoveUnusedLocalStoreInstructions()) + changed = true; + if (mFalseJump && mFalseJump->RemoveUnusedLocalStoreInstructions()) + changed = true; + } + + return false; +} + +void InterCodeBasicBlock::BuildCallerSaveTempSet(NumberSet& callerSaveTemps) +{ + if (!mVisited) + { + mVisited = true; + + NumberSet requiredTemps(mExitRequiredTemps); + int i; + + for (i = mInstructions.Size() - 1; i >= 0; i--) + mInstructions[i]->BuildCallerSaveTempSet(requiredTemps, callerSaveTemps); + + if (mTrueJump) + mTrueJump->BuildCallerSaveTempSet(callerSaveTemps); + if (mFalseJump) + mFalseJump->BuildCallerSaveTempSet(callerSaveTemps); + } +} + + +void InterCodeBasicBlock::BuildStaticVariableSet(const GrowingVariableArray& staticVars) +{ + if (!mVisited) + { + mVisited = true; + + mLocalRequiredStatics.Reset(staticVars.Size()); + mLocalProvidedStatics.Reset(staticVars.Size()); + + mEntryRequiredStatics.Reset(staticVars.Size()); + mEntryProvidedStatics.Reset(staticVars.Size()); + mExitRequiredStatics.Reset(staticVars.Size()); + mExitProvidedStatics.Reset(staticVars.Size()); + + for (int i = 0; i < mInstructions.Size(); i++) + mInstructions[i]->FilterStaticVarsUsage(staticVars, mLocalRequiredStatics, mLocalProvidedStatics); + + mEntryRequiredStatics = mLocalRequiredStatics; + mExitProvidedStatics = mLocalProvidedStatics; + + if (mTrueJump) mTrueJump->BuildStaticVariableSet(staticVars); + if (mFalseJump) mFalseJump->BuildStaticVariableSet(staticVars); + } +} + +void InterCodeBasicBlock::BuildGlobalProvidedStaticVariableSet(const GrowingVariableArray& staticVars, NumberSet fromProvidedVars) +{ + if (!mVisited || !(fromProvidedVars <= mEntryProvidedStatics)) + { + mEntryProvidedStatics |= fromProvidedVars; + fromProvidedVars |= mExitProvidedStatics; + + mVisited = true; + + if (mTrueJump) mTrueJump->BuildGlobalProvidedStaticVariableSet(staticVars, fromProvidedVars); + if (mFalseJump) mFalseJump->BuildGlobalProvidedStaticVariableSet(staticVars, fromProvidedVars); + } +} + +bool InterCodeBasicBlock::BuildGlobalRequiredStaticVariableSet(const GrowingVariableArray& staticVars, NumberSet& fromRequiredVars) +{ + bool revisit = false; + + if (!mVisited) + { + mVisited = true; + + NumberSet newRequiredVars(mExitRequiredStatics); + + if (mTrueJump && mTrueJump->BuildGlobalRequiredStaticVariableSet(staticVars, newRequiredVars)) revisit = true; + if (mFalseJump && mFalseJump->BuildGlobalRequiredStaticVariableSet(staticVars, newRequiredVars)) revisit = true; + + if (!(newRequiredVars <= mExitRequiredStatics)) + { + revisit = true; + + mExitRequiredStatics = newRequiredVars; + newRequiredVars -= mLocalProvidedStatics; + mEntryRequiredStatics |= newRequiredVars; + } + + } + + fromRequiredVars |= mEntryRequiredStatics; + + return revisit; +} + +bool InterCodeBasicBlock::RemoveUnusedStaticStoreInstructions(const GrowingVariableArray& staticVars) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + NumberSet requiredVars(mExitRequiredStatics); + GrowingInstructionPtrArray storeIns(nullptr); + + int i; + + for (i = mInstructions.Size() - 1; i >= 0; i--) + { + if (mInstructions[i]->RemoveUnusedStaticStoreInstructions(this, staticVars, requiredVars, storeIns)) + changed = true; + } + + if (mTrueJump) + { + if (mTrueJump->RemoveUnusedStaticStoreInstructions(staticVars)) + changed = true; + } + if (mFalseJump) + { + if (mFalseJump->RemoveUnusedStaticStoreInstructions(staticVars)) + changed = true; + } + } + + return changed; +} + + +void InterCodeBasicBlock::BuildLocalVariableByteSet(const GrowingVariableArray& localVars, int bsize) +{ + if (!mVisited) + { + mVisited = true; + + mLocalRequiredStatics.Reset(bsize); + mLocalProvidedStatics.Reset(bsize); + + mEntryRequiredStatics.Reset(bsize); + mEntryProvidedStatics.Reset(bsize); + mExitRequiredStatics.Reset(bsize); + mExitProvidedStatics.Reset(bsize); + + for (int i = 0; i < mInstructions.Size(); i++) + mInstructions[i]->FilterLocalVarsByteUsage(localVars, mLocalRequiredStatics, mLocalProvidedStatics, mProc->mModule->mErrors); + + mEntryRequiredStatics = mLocalRequiredStatics; + mExitProvidedStatics = mLocalProvidedStatics; + + if (mTrueJump) mTrueJump->BuildLocalVariableByteSet(localVars, bsize); + if (mFalseJump) mFalseJump->BuildLocalVariableByteSet(localVars, bsize); + } +} + +void InterCodeBasicBlock::BuildStaticVariableByteSet(const GrowingVariableArray& staticVars, int bsize) +{ + if (!mVisited) + { + mVisited = true; + + mLocalRequiredStatics.Reset(bsize); + mLocalProvidedStatics.Reset(bsize); + + mEntryRequiredStatics.Reset(bsize); + mEntryProvidedStatics.Reset(bsize); + mExitRequiredStatics.Reset(bsize); + mExitProvidedStatics.Reset(bsize); + + for (int i = 0; i < mInstructions.Size(); i++) + mInstructions[i]->FilterStaticVarsByteUsage(staticVars, mLocalRequiredStatics, mLocalProvidedStatics, mProc->mModule->mErrors); + + mEntryRequiredStatics = mLocalRequiredStatics; + mExitProvidedStatics = mLocalProvidedStatics; + + if (mTrueJump) mTrueJump->BuildStaticVariableByteSet(staticVars, bsize); + if (mFalseJump) mFalseJump->BuildStaticVariableByteSet(staticVars, bsize); + } +} + +bool InterCodeBasicBlock::RemoveUndefinedPartialLocalInstructions(const GrowingVariableArray& localVars, int bsize) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + NumberSet requiredVars(mExitRequiredStatics); + + int i; + +#if 0 + for (i = mInstructions.Size() - 1; i >= 0; i--) + { + if (mInstructions[i]->RemoveUnusedLocalStoreByteInstructions(this, localVars, requiredVars)) + changed = true; + } +#endif + + NumberSet providedVars(mEntryProvidedStatics); + for (i = 0; iRemoveUndefinedLocalLoadByteInstructions(this, localVars, providedVars)) + changed = true; + } + + if (mTrueJump) + { + if (mTrueJump->RemoveUndefinedPartialLocalInstructions(localVars, bsize)) + changed = true; + } + if (mFalseJump) + { + if (mFalseJump->RemoveUndefinedPartialLocalInstructions(localVars, bsize)) + changed = true; + } + } + + return changed; +} + +bool InterCodeBasicBlock::RemoveUnusedStaticStoreByteInstructions(const GrowingVariableArray& staticVars, int bsize) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + NumberSet requiredVars(mExitRequiredStatics); + + int i; + + for (i = mInstructions.Size() - 1; i >= 0; i--) + { + if (mInstructions[i]->RemoveUnusedStaticStoreByteInstructions(this, staticVars, requiredVars)) + changed = true; + } + + if (mTrueJump) + { + if (mTrueJump->RemoveUnusedStaticStoreByteInstructions(staticVars, bsize)) + changed = true; + } + if (mFalseJump) + { + if (mFalseJump->RemoveUnusedStaticStoreByteInstructions(staticVars, bsize)) + changed = true; + } + } + + return changed; +} + + + +void InterCodeBasicBlock::BuildLocalVariableSets(const GrowingVariableArray& localVars, const GrowingVariableArray& params, InterMemory paramMemory) +{ + int i; + + if (!mVisited) + { + mVisited = true; + + mLocalRequiredVars.Reset(localVars.Size()); + mLocalProvidedVars.Reset(localVars.Size()); + + mEntryRequiredVars.Reset(localVars.Size()); + mEntryProvidedVars.Reset(localVars.Size()); + mExitRequiredVars.Reset(localVars.Size()); + mExitProvidedVars.Reset(localVars.Size()); + + mLocalRequiredParams.Reset(params.Size()); + mLocalProvidedParams.Reset(params.Size()); + + mEntryRequiredParams.Reset(params.Size()); + mEntryProvidedParams.Reset(params.Size()); + mExitRequiredParams.Reset(params.Size()); + mExitProvidedParams.Reset(params.Size()); + + for (i = 0; i < mInstructions.Size(); i++) + { + mInstructions[i]->FilterVarsUsage(localVars, mLocalRequiredVars, mLocalProvidedVars, params, mLocalRequiredParams, mLocalProvidedParams, paramMemory); + } + + mEntryRequiredVars = mLocalRequiredVars; + mExitProvidedVars = mLocalProvidedVars; + + mEntryRequiredParams = mLocalRequiredParams; + mExitProvidedParams = mLocalProvidedParams; + + if (mTrueJump) mTrueJump->BuildLocalVariableSets(localVars, params, paramMemory); + if (mFalseJump) mFalseJump->BuildLocalVariableSets(localVars, params, paramMemory); + } +} + +void InterCodeBasicBlock::BuildGlobalProvidedVariableSet(const GrowingVariableArray& localVars, NumberSet fromProvidedVars, const GrowingVariableArray& params, NumberSet fromProvidedParams, InterMemory paramMemory) +{ + if (!mVisited || !(fromProvidedVars <= mEntryProvidedVars) || !(fromProvidedParams <= mEntryProvidedParams)) + { + mEntryProvidedVars |= fromProvidedVars; + fromProvidedVars |= mExitProvidedVars; + + mEntryProvidedParams |= fromProvidedParams; + fromProvidedParams |= mExitProvidedParams; + + mVisited = true; + + if (mTrueJump) mTrueJump->BuildGlobalProvidedVariableSet(localVars, fromProvidedVars, params, fromProvidedParams, paramMemory); + if (mFalseJump) mFalseJump->BuildGlobalProvidedVariableSet(localVars, fromProvidedVars, params, fromProvidedParams, paramMemory); + } +} + +bool InterCodeBasicBlock::BuildGlobalRequiredVariableSet(const GrowingVariableArray& localVars, NumberSet& fromRequiredVars, const GrowingVariableArray& params, NumberSet& fromRequiredParams, InterMemory paramMemory) +{ + bool revisit = false; + + if (!mVisited) + { + mVisited = true; + + NumberSet newRequiredVars(mExitRequiredVars); + NumberSet newRequiredParams(mExitRequiredParams); + + if (mTrueJump && mTrueJump->BuildGlobalRequiredVariableSet(localVars, newRequiredVars, params, newRequiredParams, paramMemory)) revisit = true; + if (mFalseJump && mFalseJump->BuildGlobalRequiredVariableSet(localVars, newRequiredVars, params, newRequiredParams, paramMemory)) revisit = true; + + if (!(newRequiredVars <= mExitRequiredVars) || !(newRequiredParams <= mExitRequiredParams)) + { + revisit = true; + + mExitRequiredVars = newRequiredVars; + newRequiredVars -= mLocalProvidedVars; + mEntryRequiredVars |= newRequiredVars; + + mExitRequiredParams = newRequiredParams; + newRequiredParams -= mLocalProvidedParams; + mEntryRequiredParams |= newRequiredParams; + } + + } + + fromRequiredVars |= mEntryRequiredVars; + fromRequiredParams |= mEntryRequiredParams; + + return revisit; +} + + + + + +bool InterCodeBasicBlock::RemoveUnusedStoreInstructions(const GrowingVariableArray& localVars, const GrowingVariableArray& params, InterMemory paramMemory) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + NumberSet requiredVars(mExitRequiredVars); + NumberSet requiredParams(mExitRequiredParams); + + int i; + + for (i = mInstructions.Size() - 1; i >= 0; i--) + { + if (mInstructions[i]->RemoveUnusedStoreInstructions(localVars, requiredVars, params, requiredParams, paramMemory)) + changed = true; + } + + if (mTrueJump) + { + if (mTrueJump->RemoveUnusedStoreInstructions(localVars, params, paramMemory)) + changed = true; + } + if (mFalseJump) + { + if (mFalseJump->RemoveUnusedStoreInstructions(localVars, params, paramMemory)) + changed = true; + } + } + + return changed; + +} + +void InterCodeBasicBlock::CollectUnusedRestricted(NumberSet& restrictSet) +{ + if (!mVisited) + { + mVisited = true; + + NumberSet activeSet(restrictSet.Size()); + + for (int i = 0; i < mInstructions.Size(); i++) + { + const InterInstruction* ins(mInstructions[i]); + + switch (ins->mCode) + { + case IC_MALLOC: + activeSet += ins->mDst.mRestricted; + break; + case IC_STORE: + activeSet -= ins->mSrc[0].mRestricted; + break; + case IC_LEA: + activeSet -= ins->mSrc[0].mRestricted; + break; + case IC_LOAD: + activeSet -= ins->mSrc[0].mRestricted; + break; + case IC_COPY: + case IC_STRCPY: + activeSet -= ins->mSrc[0].mRestricted; + break; + case IC_FREE: + if (activeSet[ins->mSrc[0].mRestricted]) + restrictSet += ins->mSrc[0].mRestricted; + break; + } + } + + if (mTrueJump) mTrueJump->CollectUnusedRestricted(restrictSet); + if (mFalseJump) mFalseJump->CollectUnusedRestricted(restrictSet); + } +} + +bool InterCodeBasicBlock::RemoveUnusedRestricted(const NumberSet& restrictSet) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + for (int i = 0; i < mInstructions.Size(); i++) + { + InterInstruction* ins(mInstructions[i]); + + switch (ins->mCode) + { + case IC_STORE: + if (ins->mSrc[0].mRestricted && restrictSet[ins->mSrc[0].mRestricted] || + ins->mSrc[1].mRestricted && restrictSet[ins->mSrc[1].mRestricted]) + { + ins->mCode = IC_NONE; + ins->mNumOperands = 0; + changed = true; + } + break; + case IC_COPY: + case IC_STRCPY: + if (ins->mSrc[1].mRestricted && restrictSet[ins->mSrc[1].mRestricted]) + { + ins->mCode = IC_NONE; + ins->mNumOperands = 0; + changed = true; + } + break; + case IC_FREE: + if (ins->mSrc[0].mRestricted && restrictSet[ins->mSrc[0].mRestricted]) + { + ins->mCode = IC_NONE; + ins->mNumOperands = 0; + changed = true; + } + break; + case IC_MALLOC: + if (ins->mDst.mRestricted && restrictSet[ins->mDst.mRestricted]) + { + ins->mCode = IC_NONE; + ins->mDst.mTemp = -1; + ins->mNumOperands = 0; + changed = true; + } + break; + } + } + + if (mTrueJump && mTrueJump->RemoveUnusedRestricted(restrictSet)) + changed = true; + if (mFalseJump && mFalseJump->RemoveUnusedRestricted(restrictSet)) + changed = true; + } + + return changed; + +} + +bool InterCodeBasicBlock::RemoveUnusedArgumentStoreInstructions(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + mEntryRequiredArgs.Reset(64, true); + + if (mTrueJump && mTrueJump == this) + { + if (mFalseJump) + { + if (mFalseJump->RemoveUnusedArgumentStoreInstructions()) + changed = true; + mEntryRequiredArgs = mFalseJump->mEntryRequiredArgs; + } + else + mEntryRequiredArgs.Reset(64, false); + } + else if (mFalseJump && mFalseJump == this) + { + if (mTrueJump->RemoveUnusedArgumentStoreInstructions()) + changed = true; + mEntryRequiredArgs = mTrueJump->mEntryRequiredArgs; + } + else + { + if (mTrueJump && mTrueJump->RemoveUnusedArgumentStoreInstructions()) + changed = true; + if (mFalseJump && mFalseJump->RemoveUnusedArgumentStoreInstructions()) + changed = true; + + if (mTrueJump) + { + mEntryRequiredArgs = mTrueJump->mEntryRequiredArgs; + if (mFalseJump) + mEntryRequiredArgs |= mTrueJump->mEntryRequiredArgs; + } + else + mEntryRequiredArgs.Reset(64, false); + } + + int i = mInstructions.Size() - 1; + while (i >= 0) + { + InterInstruction* ins(mInstructions[i]); + + if (ins->mCode == IC_CALL || ins->mCode == IC_CALL_NATIVE) + { + mEntryRequiredArgs.Fill(); + } + else if (ins->mCode == IC_STORE && ins->mSrc[1].mMemory == IM_FFRAME && ins->mSrc[1].mVarIndex + int(ins->mSrc[1].mIntConst) + InterTypeSize[ins->mSrc[0].mType] < 64) + { + if (mEntryRequiredArgs.RangeClear(ins->mSrc[1].mVarIndex + int(ins->mSrc[1].mIntConst), InterTypeSize[ins->mSrc[0].mType])) + { + mInstructions.Remove(i); + changed = true; + } + else + mEntryRequiredArgs.SubRange(ins->mSrc[1].mVarIndex + int(ins->mSrc[1].mIntConst), InterTypeSize[ins->mSrc[0].mType]); + } + i--; + } + } + + return changed; +} + +bool InterCodeBasicBlock::RemoveUnusedIndirectStoreInstructions(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + ExpandingArray stores; + + for (int i = mInstructions.Size() - 1; i >= 0; i--) + { + InterInstruction* ins = mInstructions[i]; + + if (ins->mCode == IC_STORE) + { + int j = 0; + while (j < stores.Size() && !SameMemAndSize(ins->mSrc[1], stores[j]->mSrc[1])) + j++; + + if (j < stores.Size()) + { + if (ins->mVolatile) + stores[j] = ins; + else + { + ins->mCode = IC_NONE; + ins->mNumOperands = 0; + } + } + else + { + j = 0; + while (j < stores.Size()) + { + if (CollidingMem(ins, stores[j])) + stores.Remove(j); + else + j++; + } + stores.Push(ins); + } + } + else if (ins->mCode == IC_CALL || ins->mCode == IC_CALL_NATIVE) + { + stores.SetSize(0); + } + else + { + int j = 0; + while (j < stores.Size()) + { + if (CollidingMem(ins, stores[j]) || ins->mDst.mTemp >= 0 && ins->mDst.mTemp == stores[j]->mSrc[1].mTemp) + stores.Remove(j); + else + j++; + } + } + } + + if (mTrueJump && mTrueJump->RemoveUnusedIndirectStoreInstructions()) + changed = true; + if (mFalseJump && mFalseJump->RemoveUnusedIndirectStoreInstructions()) + changed = true; + } + + return changed; +} + + +bool InterCodeBasicBlock::EliminateAliasValues(const GrowingInstructionPtrArray& tvalue, const GrowingInstructionPtrArray& avalue) +{ + bool changed = false; + + if (!mVisited) + { + GrowingInstructionPtrArray ltvalue(tvalue); + GrowingInstructionPtrArray lavalue(avalue); + + if (mLoopHead) + { + ltvalue.Clear(); + lavalue.Clear(); + } + else if (mNumEntries > 0) + { + if (mNumEntered > 0) + { + for (int i = 0; i < ltvalue.Size(); i++) + { + if (mMergeTValues[i] != ltvalue[i]) + ltvalue[i] = nullptr; + if (mMergeAValues[i] != lavalue[i]) + lavalue[i] = nullptr; + } + + for (int i = 0; i < ltvalue.Size(); i++) + { + if (lavalue[i] && !ltvalue[lavalue[i]->mSrc[0].mTemp]) + lavalue[i] = nullptr; + } + } + + mNumEntered++; + + if (mNumEntered < mNumEntries) + { + mMergeTValues = ltvalue; + mMergeAValues = lavalue; + return false; + } + } + + mVisited = true; + + for (int i = 0; i < mInstructions.Size(); i++) + { + InterInstruction* ins = mInstructions[i]; + + for (int j = 0; j < ins->mNumOperands; j++) + { + if (ins->mSrc[j].mTemp >= 0 && lavalue[ins->mSrc[j].mTemp]) + { + InterInstruction* mins = lavalue[ins->mSrc[j].mTemp]; + + if (mExitRequiredTemps[mins->mDst.mTemp] && !mExitRequiredTemps[mins->mSrc[0].mTemp]) + { + ins->mSrc[j].ForwardTemp(mins->mDst); + changed = true; + } + } + } + + if (ins->mDst.mTemp >= 0) + { + if (ltvalue[ins->mDst.mTemp] && ltvalue[ins->mDst.mTemp]->mCode == IC_LOAD_TEMPORARY) + lavalue[ltvalue[ins->mDst.mTemp]->mSrc[0].mTemp] = nullptr; + + ltvalue[ins->mDst.mTemp] = ins; + lavalue[ins->mDst.mTemp] = nullptr; + if (ins->mCode == IC_LOAD_TEMPORARY) + lavalue[ins->mSrc[0].mTemp] = ins; + } + } + + + if (mTrueJump && mTrueJump->EliminateAliasValues(ltvalue, lavalue)) + changed = true; + + if (mFalseJump && mFalseJump->EliminateAliasValues(ltvalue, lavalue)) + changed = true; + } + + return changed; +} + +bool InterCodeBasicBlock::EliminateIntegerSumAliasTemps(const GrowingInstructionPtrArray& tvalue) +{ + bool changed = false; + if (!mVisited) + { + GrowingInstructionPtrArray ltvalue(tvalue); + + if (mLoopHead) + { + ltvalue.Clear(); + } + else if (mNumEntries > 0) + { + if (mNumEntered > 0) + { + for (int i = 0; i < ltvalue.Size(); i++) + { + if (mMergeTValues[i] != ltvalue[i]) + ltvalue[i] = nullptr; + } + } + + mNumEntered++; + + if (mNumEntered < mNumEntries) + { + mMergeTValues = ltvalue; + return false; + } + } + + mVisited = true; + + for (int i = 0; i < mInstructions.Size(); i++) + { + InterInstruction* ins = mInstructions[i]; + + int dtemp = ins->mDst.mTemp; + int stemp = -1; + + if (ins->mCode == IC_BINARY_OPERATOR && + ins->mOperator == IA_ADD && + ins->mSrc[0].mTemp < 0 && + ins->mSrc[1].mTemp >= 0) + { + stemp = ins->mSrc[1].mTemp; + + if (ins->mSrc[1].mFinal && ltvalue[stemp]) + { + InterInstruction* sins = ltvalue[stemp]; + int64 diff = ins->mSrc[0].mIntConst - sins->mSrc[0].mIntConst; + if (diff >= -128 && diff < 256 || ins->mSrc[0].mIntConst < -128 || ins->mSrc[0].mIntConst >= 256) + { + ins->mSrc[1] = sins->mDst; + ins->mSrc[0].mIntConst = diff; + changed = true; + } + + stemp = -1; + } + else if (ins->mSrc[1].mFinal) + stemp = -1; + } + + if (dtemp >= 0) + { + for (int i = 0; i < ltvalue.Size(); i++) + { + if (ltvalue[i] && ltvalue[i]->ReferencesTemp(dtemp)) + ltvalue[i] = nullptr; + } + } + if (stemp >= 0) + ltvalue[stemp] = ins; + } + + + if (mTrueJump && mTrueJump->EliminateIntegerSumAliasTemps(ltvalue)) + changed = true; + + if (mFalseJump && mFalseJump->EliminateIntegerSumAliasTemps(ltvalue)) + changed = true; + } + + return changed; +} + +bool InterCodeBasicBlock::MergeIndexedLoadStore(const GrowingInstructionPtrArray& tvalue) +{ + bool changed = false; + + if (!mVisited) + { + GrowingInstructionPtrArray ltvalue(tvalue); + + if (mNumEntries > 1) + ltvalue.Clear(); + + mVisited = true; + + // Move lea to front + + for (int i = 0; i < mInstructions.Size(); i++) + { + InterInstruction* ins = mInstructions[i]; + + if (ins->mCode == IC_LEA) + { + int j = i; + while (j > 0 && CanSwapInstructions(mInstructions[j - 1], ins)) + { + mInstructions[j] = mInstructions[j - 1]; + j--; + } + mInstructions[j] = ins; + } + } +#if 1 + for (int i = 0; i < mInstructions.Size(); i++) + { + InterInstruction* ins = mInstructions[i]; + + if (ins->mCode == IC_STORE || ins->mCode == IC_LOAD) + { + int pi = ins->mCode == IC_LOAD ? 0 : 1; + + if (ins->mSrc[pi].mTemp >= 0 && ltvalue[ins->mSrc[pi].mTemp]) + { + InterInstruction* lins = ltvalue[ins->mSrc[pi].mTemp]; + + if (lins->mSrc[0].mTemp >= 0) + { + if (lins->mSrc[1].mMemory != IM_ABSOLUTE || (lins->mSrc[0].mRange.mMaxState == IntegerValueRange::S_BOUND && lins->mSrc[0].mRange.mMaxValue >= 256)) + { + InterInstruction* bins = lins; + for (int j = 0; j < ltvalue.Size(); j++) + { + InterInstruction* cins = ltvalue[j]; + if (cins && + cins->mSrc[0].mTemp == bins->mSrc[0].mTemp && + cins->mSrc[1].mTemp < 0 && bins->mSrc[1].mTemp < 0 && + cins->mSrc[1].mMemory == bins->mSrc[1].mMemory && + cins->mSrc[1].mVarIndex == bins->mSrc[1].mVarIndex && + cins->mSrc[1].mIntConst < bins->mSrc[1].mIntConst) + { + bins = cins; + } + } + + if (bins != lins && ins->mSrc[pi].mIntConst + lins->mSrc[1].mIntConst - bins->mSrc[1].mIntConst < 252 && ins->mSrc[pi].mIntConst + lins->mSrc[1].mIntConst - bins->mSrc[1].mIntConst >= 0) + { + int64 diff = lins->mSrc[1].mIntConst - bins->mSrc[1].mIntConst; + + ins->mSrc[pi].mTemp = bins->mDst.mTemp; + ins->mSrc[pi].mIntConst += diff; + ins->mSrc[pi].mRange.AddConstValue(IT_INT16, -diff); + changed = true; + } + } + } + } + } + + int dtemp = ins->mDst.mTemp; + + if (dtemp >= 0) + { + + for (int i = 0; i < ltvalue.Size(); i++) + { + if (ltvalue[i] && ltvalue[i]->ReferencesTemp(dtemp)) + ltvalue[i] = nullptr; + } + + if (!ins->UsesTemp(dtemp)) + ltvalue[dtemp] = ins; + } + } +#endif + + if (mTrueJump && mTrueJump->MergeIndexedLoadStore(ltvalue)) + changed = true; + + if (mFalseJump && mFalseJump->MergeIndexedLoadStore(ltvalue)) + changed = true; + } + + return changed; +} + +static bool CheckSimplifyPointerOffsets(const InterInstruction* ins, int temp, int& mino, int& maxo) +{ + if (ins->mDst.mTemp == temp) + return false; + + if (ins->mCode == IC_LOAD && ins->mSrc[0].mTemp == temp) + { + if (ins->mSrc[0].mIntConst < mino) + mino = int(ins->mSrc[0].mIntConst); + if (ins->mSrc[0].mIntConst > maxo) + maxo = int(ins->mSrc[0].mIntConst); + + return true; + } + + if (ins->mCode == IC_STORE && ins->mSrc[1].mTemp == temp) + { + if (ins->mSrc[0].mTemp == temp) + return false; + + if (ins->mSrc[1].mIntConst < mino) + mino = int(ins->mSrc[1].mIntConst); + if (ins->mSrc[1].mIntConst > maxo) + maxo = int(ins->mSrc[1].mIntConst); + + return true; + } + + for (int i = 0; i < ins->mNumOperands; i++) + if (ins->mSrc[i].mTemp == temp) + return false; + + return true; +} + +bool InterCodeBasicBlock::SimplifyPointerOffsets(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + for (int i = 0; i < mInstructions.Size(); i++) + { + InterInstruction* ins = mInstructions[i]; + + if (ins->mCode == IC_LEA && (ins->mSrc[0].mTemp < 0 || ins->mSrc[1].mTemp < 0) && !mExitRequiredTemps[ins->mDst.mTemp]) + { + int minoffset = 65535, maxoffset = -65535; + + int j = i + 1; + while (j < mInstructions.Size() && CheckSimplifyPointerOffsets(mInstructions[j], ins->mDst.mTemp, minoffset, maxoffset)) + j++; + + if (j == mInstructions.Size() && (minoffset < 0 || maxoffset > 255) && maxoffset - minoffset < 256) + { + if (ins->mSrc[0].mTemp < 0) + ins->mSrc[0].mIntConst += minoffset; + else + ins->mSrc[1].mIntConst += minoffset; + + changed = true; + + for (int j = i + 1; j < mInstructions.Size(); j++) + { + InterInstruction* tins = mInstructions[j]; + if (tins->mCode == IC_LOAD && tins->mSrc[0].mTemp == ins->mDst.mTemp) + tins->mSrc[0].mIntConst -= minoffset; + else if (tins->mCode == IC_STORE && tins->mSrc[1].mTemp == ins->mDst.mTemp) + tins->mSrc[1].mIntConst -= minoffset; + } + } + } + } + + if (mTrueJump && mTrueJump->SimplifyPointerOffsets()) + changed = true; + if (mFalseJump && mFalseJump->SimplifyPointerOffsets()) + changed = true; + } + + return true; +} + +static bool IsValidSignedIntRange(InterType t, int64 value) +{ + switch (t) + { + case IT_INT8: + return value >= -128 && value <= 127; + case IT_INT16: + return value >= -32768 && value <= 32767; + case IT_INT32: + return true; + default: + return false; + } +} + +bool InterCodeBasicBlock::ForwardShortLoadStoreOffsets(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + for (int i = 0; i < mInstructions.Size(); i++) + { + InterInstruction* lins = mInstructions[i]; + if (lins->mCode == IC_LEA && lins->mSrc[1].mTemp >= 0 && lins->mSrc[0].mTemp < 0 && lins->mSrc[0].IsUByte()) + { + for (int j = i + 1; j < mInstructions.Size(); j++) + { + InterInstruction* lins2 = mInstructions[j]; + if (lins2->mCode == IC_LEA && lins2->mSrc[1].mTemp == lins->mDst.mTemp && lins2->mSrc[0].mTemp >= 0) + { + int k = j + 1; + while (k < mInstructions.Size() && !mInstructions[k]->ReferencesTemp(lins2->mDst.mTemp) && !mInstructions[k]->ReferencesTemp(lins->mSrc[1].mTemp)) + k++; + if (k < mInstructions.Size()) + { + InterInstruction* mins = mInstructions[k]; + if (mins->mCode == IC_LOAD && mins->mSrc[0].mTemp == lins2->mDst.mTemp && mins->mSrc[0].mFinal) + { + lins2->mSrc[1].mTemp = lins->mSrc[1].mTemp; + lins->mSrc[1].mFinal = false; + mins->mSrc[0].mIntConst += lins->mSrc[0].mIntConst; + changed = true; + } + else if (mins->mCode == IC_STORE && mins->mSrc[1].mTemp == lins2->mDst.mTemp && mins->mSrc[1].mFinal) + { + lins2->mSrc[1].mTemp = lins->mSrc[1].mTemp; + lins->mSrc[1].mFinal = false; + mins->mSrc[1].mIntConst += lins->mSrc[0].mIntConst; + changed = true; + } + else if (mins->mCode == IC_COPY && mins->mSrc[0].mTemp == lins2->mDst.mTemp && mins->mSrc[0].mFinal) + { + lins2->mSrc[1].mTemp = lins->mSrc[1].mTemp; + lins->mSrc[1].mFinal = false; + mins->mSrc[0].mIntConst += lins->mSrc[0].mIntConst; + changed = true; + } + else if (mins->mCode == IC_COPY && mins->mSrc[1].mTemp == lins2->mDst.mTemp && mins->mSrc[1].mFinal) + { + lins2->mSrc[1].mTemp = lins->mSrc[1].mTemp; + lins->mSrc[1].mFinal = false; + mins->mSrc[1].mIntConst += lins->mSrc[0].mIntConst; + changed = true; + } + else if (mins->mCode == IC_FILL && mins->mSrc[1].mTemp == lins2->mDst.mTemp && mins->mSrc[1].mFinal) + { + lins2->mSrc[1].mTemp = lins->mSrc[1].mTemp; + lins->mSrc[1].mFinal = false; + mins->mSrc[1].mIntConst += lins->mSrc[0].mIntConst; + changed = true; + } + } + } + + if (lins2->mDst.mTemp == lins->mDst.mTemp) + break; + } + } + } + + if (mTrueJump && mTrueJump->ForwardShortLoadStoreOffsets()) + changed = true; + if (mFalseJump && mFalseJump->ForwardShortLoadStoreOffsets()) + changed = true; + } + + return changed; +} + +bool InterCodeBasicBlock::SimplifyIntegerNumeric(const GrowingInstructionPtrArray& tvalue, int& spareTemps) +{ + bool changed = false; + + if (!mVisited) + { + GrowingInstructionPtrArray ltvalue(tvalue); + + if (mLoopHead) + { + ltvalue.Clear(); + } + else if (mNumEntries > 0) + { + if (mNumEntered > 0) + { + for (int i = 0; i < ltvalue.Size(); i++) + { + if (mMergeTValues[i] != ltvalue[i]) + ltvalue[i] = nullptr; + } + } + + mNumEntered++; + + if (mNumEntered < mNumEntries) + { + mMergeTValues = ltvalue; + return false; + } + } + + mVisited = true; + + for (int i = 0; i < mInstructions.Size(); i++) + { + InterInstruction* ins = mInstructions[i]; + + switch (ins->mCode) + { + case IC_BINARY_OPERATOR: + { + switch (ins->mOperator) + { + case IA_SHL: +#if 1 + if (ins->mSrc[0].mTemp < 0 && ins->mSrc[1].mTemp >= 0 && ltvalue[ins->mSrc[1].mTemp] && ins->mDst.mType == IT_INT16) + { + InterInstruction* pins = ltvalue[ins->mSrc[1].mTemp]; + if (pins->mCode == IC_CONVERSION_OPERATOR && pins->mOperator == IA_EXT8TO16U && pins->mSrc[0].IsUByte() && pins->mSrc[0].mTemp >= 0 && ltvalue[pins->mSrc[0].mTemp]) + { + InterInstruction* ains = ltvalue[pins->mSrc[0].mTemp]; + + if (ains->mCode == IC_BINARY_OPERATOR && (ains->mOperator == IA_ADD || ains->mOperator == IA_SUB) && ains->mSrc[0].mTemp < 0) + { + if (spareTemps + 2 >= ltvalue.Size()) + return true; + + InterInstruction* cins = new InterInstruction(ins->mLocation, IC_CONVERSION_OPERATOR); + cins->mOperator = IA_EXT8TO16U; + cins->mSrc[0] = ains->mSrc[1]; + cins->mDst.mTemp = spareTemps++; + cins->mDst.mType = IT_INT16; + cins->mDst.mRange = ains->mSrc[1].mRange; + cins->mDst.mRange.LimitMin(0); + mInstructions.Insert(i, cins); + + InterInstruction* nins = new InterInstruction(ins->mLocation, IC_BINARY_OPERATOR); + nins->mOperator = IA_SHL; + nins->mSrc[0] = ins->mSrc[0]; + nins->mSrc[1] = cins->mDst; + nins->mDst.mTemp = spareTemps++; + nins->mDst.mType = IT_INT16; + if (cins->mDst.mRange.mMinState == IntegerValueRange::S_BOUND) + { + nins->mDst.mRange.mMinState = IntegerValueRange::S_BOUND; + nins->mDst.mRange.mMinValue = cins->mDst.mRange.mMinValue << nins->mSrc[0].mIntConst; + } + if (cins->mDst.mRange.mMaxState == IntegerValueRange::S_BOUND) + { + nins->mDst.mRange.mMaxState = IntegerValueRange::S_BOUND; + nins->mDst.mRange.mMaxValue = cins->mDst.mRange.mMaxValue << nins->mSrc[0].mIntConst; + } + mInstructions.Insert(i + 1, nins); + + ins->mOperator = ains->mOperator; + ins->mSrc[0] = ains->mSrc[0]; + ins->mSrc[0].mIntConst <<= nins->mSrc[0].mIntConst; + ins->mSrc[0].mType = IT_INT16; + ins->mSrc[1] = nins->mDst; + + changed = true; + break; + } + } + } +#endif +#if 1 + if (ins->mSrc[1].mTemp < 0 && ins->mSrc[0].mTemp >= 0 && ltvalue[ins->mSrc[0].mTemp] && ins->mDst.mType == IT_INT16) + { + InterInstruction* pins = ltvalue[ins->mSrc[0].mTemp]; + if (pins->mCode == IC_CONVERSION_OPERATOR && pins->mOperator == IA_EXT8TO16U && pins->mSrc[0].IsUByte() && pins->mSrc[0].mRange.mMaxValue < 16 && pins->mSrc[0].mTemp >= 0 && ltvalue[pins->mSrc[0].mTemp]) + { + InterInstruction* ains = ltvalue[pins->mSrc[0].mTemp]; + + if (ains->mCode == IC_BINARY_OPERATOR && ains->mOperator == IA_ADD && ains->mSrc[0].mTemp < 0 && ains->mSrc[1].IsUByte()) + { + ins->mSrc[0] = ains->mSrc[1]; + ins->mSrc[1].mIntConst <<= ains->mSrc[0].mIntConst; + + changed = true; + break; + } + } + } +#endif + break; +#if 1 + case IA_ADD: + if (ins->mSrc[1].mTemp < 0 && ins->mSrc[0].mTemp >= 0 && ltvalue[ins->mSrc[0].mTemp] && ins->mSrc[0].mFinal) + { + InterInstruction* pins = ltvalue[ins->mSrc[0].mTemp]; + + if (pins->mCode == IC_BINARY_OPERATOR && pins->mOperator == IA_ADD) + { + if (pins->mSrc[0].mTemp < 0) + { + ins->mSrc[0].Forward(pins->mSrc[1]); + pins->mSrc[1].mFinal = false; + ins->mSrc[1].mIntConst += pins->mSrc[0].mIntConst; + changed = true; + } + else if (pins->mSrc[1].mTemp < 0) + { + ins->mSrc[0].Forward(pins->mSrc[0]); + pins->mSrc[0].mFinal = false; + ins->mSrc[1].mIntConst += pins->mSrc[1].mIntConst; + changed = true; + } + } + } + else if (ins->mSrc[0].mTemp < 0 && ins->mSrc[1].mTemp >= 0 && ltvalue[ins->mSrc[1].mTemp] && ins->mSrc[1].mFinal) + { + InterInstruction* pins = ltvalue[ins->mSrc[1].mTemp]; + + if (pins->mCode == IC_BINARY_OPERATOR && pins->mOperator == IA_ADD) + { + if (pins->mSrc[0].mTemp < 0) + { + ins->mSrc[1].Forward(pins->mSrc[1]); + pins->mSrc[1].mFinal = false; + ins->mSrc[0].mIntConst += pins->mSrc[0].mIntConst; + changed = true; + } + else if (pins->mSrc[1].mTemp < 0) + { + ins->mSrc[1].Forward(pins->mSrc[0]); + pins->mSrc[0].mFinal = false; + ins->mSrc[0].mIntConst += pins->mSrc[1].mIntConst; + changed = true; + } + } + } + else if (ins->mSrc[0].mTemp >= 0 && ins->mSrc[1].mTemp >= 0 && + ltvalue[ins->mSrc[0].mTemp] && ltvalue[ins->mSrc[1].mTemp] && + ins->mSrc[0].mFinal && ins->mSrc[1].mFinal) + { + InterInstruction* ai0 = ltvalue[ins->mSrc[0].mTemp], * ai1 = ltvalue[ins->mSrc[1].mTemp]; + if (ai0->mCode == IC_BINARY_OPERATOR && ai0->mOperator == IA_SUB && ai0->mSrc[0].mTemp < 0 && ai0->mSrc[1].mFinal && + ai1->mCode == IC_BINARY_OPERATOR && ai1->mOperator == IA_SUB && ai1->mSrc[0].mTemp < 0 && ai1->mSrc[1].mFinal) + { + if (spareTemps + 2 >= ltvalue.Size()) + return true; + + InterInstruction* sins = new InterInstruction(ins->mLocation, IC_BINARY_OPERATOR); + sins->mOperator = IA_SUB; + sins->mDst = ins->mDst; + sins->mSrc[1].mType = IT_INT16; + sins->mSrc[1].mTemp = spareTemps++; + sins->mSrc[1].mFinal = true; + sins->mSrc[0] = ai0->mSrc[0]; + sins->mSrc[0].mIntConst += ai1->mSrc[0].mIntConst; + sins->mSrc[0].mRange.AddConstValue(IT_INT16, -ai1->mSrc[0].mIntConst); + mInstructions.Insert(i + 1, sins); + + ins->mDst = sins->mSrc[1]; + + ai0->mCode = IC_LOAD_TEMPORARY; + ai0->mSrc[0] = ai0->mSrc[1]; + ai0->mDst.mRange = ai0->mSrc[0].mRange; + ai0->mNumOperands = 1; + + ai1->mCode = IC_LOAD_TEMPORARY; + ai1->mSrc[0] = ai1->mSrc[1]; + ai1->mDst.mRange = ai1->mSrc[0].mRange; + ai1->mNumOperands = 1; + + ins->mSrc[0].mRange = ai0->mDst.mRange; + ins->mSrc[1].mRange = ai1->mDst.mRange; + + changed = true; + } + } + + + break; +#endif +#if 1 + case IA_SUB: + if (ins->mSrc[0].mTemp < 0 && ins->mSrc[1].mTemp >= 0 && ltvalue[ins->mSrc[1].mTemp] && ins->mSrc[1].mFinal) + { + InterInstruction* pins = ltvalue[ins->mSrc[1].mTemp]; + + if (pins->mCode == IC_BINARY_OPERATOR && pins->mOperator == IA_ADD) + { + if (pins->mSrc[0].mTemp < 0) + { + ins->mOperator = IA_ADD; + ins->mSrc[1].Forward(pins->mSrc[1]); + pins->mSrc[1].mFinal = false; + ins->mSrc[0].mIntConst = pins->mSrc[0].mIntConst - ins->mSrc[0].mIntConst; + changed = true; + } + else if (pins->mSrc[1].mTemp < 0) + { + ins->mOperator = IA_ADD; + ins->mSrc[1].Forward(pins->mSrc[0]); + pins->mSrc[0].mFinal = false; + ins->mSrc[0].mIntConst = pins->mSrc[1].mIntConst - ins->mSrc[0].mIntConst; + changed = true; + } + } + } + + break; +#endif + } + } break; + + case IC_RELATIONAL_OPERATOR: + if (ins->mOperator == IA_CMPLS || ins->mOperator == IA_CMPLES || ins->mOperator == IA_CMPGS || ins->mOperator == IA_CMPGES) + { + if (ins->mSrc[0].mTemp < 0 && ins->mSrc[1].mTemp >= 0 && ltvalue[ins->mSrc[1].mTemp]) + { + InterInstruction* pins = ltvalue[ins->mSrc[1].mTemp]; + + if (pins->mCode == IC_BINARY_OPERATOR && pins->mOperator == IA_ADD) + { + if (pins->mSrc[0].mTemp < 0) + { + if (IsValidSignedIntRange(ins->mSrc[0].mType, ins->mSrc[0].mIntConst - pins->mSrc[0].mIntConst)) + { + ins->mSrc[1].Forward(pins->mSrc[1]); + pins->mSrc[1].mFinal = false; + ins->mSrc[0].mIntConst -= pins->mSrc[0].mIntConst; + changed = true; + } + } + else if (pins->mSrc[1].mTemp < 0) + { + if (IsValidSignedIntRange(ins->mSrc[0].mType, ins->mSrc[0].mIntConst - pins->mSrc[1].mIntConst)) + { + ins->mSrc[1].Forward(pins->mSrc[0]); + pins->mSrc[0].mFinal = false; + ins->mSrc[0].mIntConst -= pins->mSrc[1].mIntConst; + changed = true; + } + } + } + } + } + break; + + case IC_LEA: + if (ins->mSrc[1].mMemory == IM_INDIRECT && ins->mSrc[1].mTemp >= 0 && tvalue[ins->mSrc[1].mTemp]) + { + InterInstruction* pins = tvalue[ins->mSrc[1].mTemp]; + if (pins->mCode == IC_LEA) + ins->mSrc[1].mLinkerObject = pins->mSrc[1].mLinkerObject; + } + + if (ins->mSrc[1].mTemp < 0 && ins->mSrc[0].mTemp >= 0 && ltvalue[ins->mSrc[0].mTemp]) + { + InterInstruction* pins = ltvalue[ins->mSrc[0].mTemp]; + + if (pins->mCode == IC_BINARY_OPERATOR && pins->mOperator == IA_ADD && pins->mSrc[0].mTemp < 0 && pins->mDst.mType == IT_INT16) + { + ins->mSrc[0] = pins->mSrc[1]; + ins->mSrc[1].mIntConst += pins->mSrc[0].mIntConst; + ins->mSrc[1].mRange.AddConstValue(IT_INT16, -pins->mSrc[0].mIntConst); + changed = true; + } +#if 1 + else if (pins->mCode == IC_BINARY_OPERATOR && pins->mOperator == IA_ADD && pins->mSrc[1].mTemp < 0 && pins->mDst.mType == IT_INT16) + { + ins->mSrc[0] = pins->mSrc[0]; + ins->mSrc[1].mIntConst += pins->mSrc[1].mIntConst; + ins->mSrc[1].mRange.AddConstValue(IT_INT16, -pins->mSrc[1].mIntConst); + changed = true; + } +#endif +#if 1 + else if (pins->mCode == IC_CONVERSION_OPERATOR && pins->mOperator == IA_EXT8TO16U && pins->mSrc[0].IsUByte() && pins->mSrc[0].mTemp >= 0 && ltvalue[pins->mSrc[0].mTemp]) + { + InterInstruction* ains = ltvalue[pins->mSrc[0].mTemp]; + + if (ains->mCode == IC_BINARY_OPERATOR && ains->mOperator == IA_ADD && ains->mSrc[0].mTemp < 0) + { + if (ains->mSrc[1].mType == IT_INT16) + { + ins->mSrc[0] = ains->mSrc[1]; + ins->mSrc[1].mIntConst += ains->mSrc[0].mIntConst; + ins->mSrc[1].mRange.AddConstValue(IT_INT16, -ains->mSrc[0].mIntConst); + changed = true; + } + else if (ains->mSrc[1].mType == IT_INT8) + { + if (spareTemps + 2 >= ltvalue.Size()) + return true; + + InterInstruction* nins = new InterInstruction(ins->mLocation, IC_CONVERSION_OPERATOR); + nins->mOperator = IA_EXT8TO16U; + nins->mSrc[0] = ains->mSrc[1]; + nins->mDst.mTemp = spareTemps++; + nins->mDst.mType = IT_INT16; + nins->mDst.mRange = ains->mSrc[1].mRange; + mInstructions.Insert(i, nins); + + ins->mSrc[0] = nins->mDst; + ins->mSrc[1].mIntConst += ains->mSrc[0].mIntConst; + ins->mSrc[1].mRange.AddConstValue(IT_INT16, -ains->mSrc[0].mIntConst); + changed = true; + } + } + } +#endif + } + else if (ins->mSrc[1].mTemp >= 0 && ltvalue[ins->mSrc[1].mTemp] && ltvalue[ins->mSrc[1].mTemp]->mCode == IC_CONSTANT) + { + InterInstruction* pins = ltvalue[ins->mSrc[1].mTemp]; + + ins->mSrc[1].ForwardMem(pins->mConst); + ins->mSrc[1].mType = IT_POINTER; + changed = true; + } + else if (ins->mSrc[1].mTemp >= 0 && ins->mSrc[0].mTemp < 0 && ltvalue[ins->mSrc[1].mTemp]) + { + InterInstruction* pins = ltvalue[ins->mSrc[1].mTemp]; + + if (pins->mCode == IC_LEA && pins->mSrc[1].mTemp < 0) + { + ins->mSrc[1].ForwardMem(pins->mSrc[1]); + ins->mSrc[1].mIntConst += ins->mSrc[0].mIntConst; + ins->mSrc[1].mRange.AddConstValue(IT_INT16, -ins->mSrc[0].mIntConst); + ins->mSrc[0].Forward(pins->mSrc[0]); + changed = true; + } + } + break; +#if 1 + case IC_CONVERSION_OPERATOR: + if (ins->mOperator == IA_EXT8TO16U) + { + if (ins->mSrc[0].mTemp >= 0 && ltvalue[ins->mSrc[0].mTemp] && ltvalue[ins->mSrc[0].mTemp]->mDst.mType == IT_INT16 && ins->mSrc[0].IsUByte()) + { + ins->mCode = IC_LOAD_TEMPORARY; + ins->mSrc[0].mType = IT_INT16; + assert(ins->mSrc[0].mTemp >= 0); + changed = true; + } + } + else if (ins->mOperator == IA_EXT8TO16S) + { + if (ins->mSrc[0].mTemp >= 0 && ltvalue[ins->mSrc[0].mTemp] && ltvalue[ins->mSrc[0].mTemp]->mDst.mType == IT_INT16 && ins->mSrc[0].IsSByte()) + { + ins->mCode = IC_LOAD_TEMPORARY; + ins->mSrc[0].mType = IT_INT16; + assert(ins->mSrc[0].mTemp >= 0); + changed = true; + } + } + + break; +#endif +#if 1 + case IC_STORE: + if (ins->mSrc[1].mTemp >= 0 && ltvalue[ins->mSrc[1].mTemp]) + { + InterInstruction* pins = ltvalue[ins->mSrc[1].mTemp]; + + if (pins->mCode == IC_LEA) + { + if (ins->mSrc[1].mMemory == IM_INDIRECT) + { + ins->mSrc[1].mLinkerObject = pins->mSrc[1].mLinkerObject; + ins->mSrc[1].mVarIndex = pins->mSrc[1].mVarIndex; + } + + if (pins->mSrc[0].mTemp < 0 && ins->mSrc[1].mIntConst + pins->mSrc[0].mIntConst >= 0) + { + ins->mSrc[1].mMemory = pins->mSrc[1].mMemory; + ins->mSrc[1].ForwardTemp(pins->mSrc[1]); + pins->mSrc[1].mFinal = false; + + int64 diff = pins->mSrc[0].mIntConst + pins->mSrc[1].mIntConst; + ins->mSrc[1].mIntConst += diff; + ins->mSrc[1].mRange = pins->mSrc[1].mRange; + changed = true; + } +#if 1 + else if (pins->mSrc[1].mTemp < 0 && pins->mSrc[0].mTemp >= 0 && ins->mSrc[1].mIntConst && (ins->mSrc[1].mIntConst >= 256 || pins->mSrc[0].IsUByte())) + { + int k = mInstructions.IndexOf(pins); + if (k >= 0) + { + if (spareTemps + 2 >= ltvalue.Size()) + return true; + + InterInstruction* nins = new InterInstruction(ins->mLocation, IC_LEA); + nins->mSrc[0].Forward(pins->mSrc[0]); + nins->mSrc[1].ForwardMem(pins->mSrc[1]); + nins->mSrc[1].mIntConst += ins->mSrc[1].mIntConst; + nins->mDst.mTemp = spareTemps++; + nins->mDst.mType = IT_POINTER; + nins->mDst.mRange = ins->mDst.mRange; + + ins->mSrc[1].mRange.Reset(); + ins->mSrc[1].mIntConst = 0; + ins->mSrc[1].mTemp = nins->mDst.mTemp; + + mInstructions.Insert(k + 1, nins); + changed = true; + } + } +#endif + } + } + break; +#endif +#if 1 + case IC_LOAD: + if (ins->mSrc[0].mTemp >= 0 && ltvalue[ins->mSrc[0].mTemp]) + { + InterInstruction* pins = ltvalue[ins->mSrc[0].mTemp]; + + if (pins->mCode == IC_LEA) + { + if (ins->mSrc[0].mMemory == IM_INDIRECT) + { + ins->mSrc[0].mLinkerObject = pins->mSrc[1].mLinkerObject; + ins->mSrc[0].mVarIndex = pins->mSrc[1].mVarIndex; + } + + if (pins->mSrc[0].mTemp < 0 && ins->mSrc[0].mIntConst + pins->mSrc[0].mIntConst >= 0) + { + int64 offset = ins->mSrc[0].mIntConst + pins->mSrc[0].mIntConst; + int osize = ins->mSrc[0].mOperandSize; + ins->mSrc[0].ForwardMem(pins->mSrc[1]); + ins->mSrc[0].mOperandSize = osize; + pins->mSrc[1].mFinal = false; + ins->mSrc[0].mIntConst += offset; + ins->mSrc[0].mRange = pins->mSrc[1].mRange; + changed = true; + } + else if (pins->mSrc[1].mTemp < 0 && pins->mSrc[0].mTemp >= 0 && ins->mSrc[0].mIntConst && (ins->mSrc[0].mIntConst >= 256 || pins->mSrc[0].IsUByte())) + { + int k = mInstructions.IndexOf(pins); + if (k >= 0) + { + if (spareTemps + 2 >= ltvalue.Size()) + return true; + + InterInstruction* nins = new InterInstruction(ins->mLocation, IC_LEA); + nins->mSrc[0].Forward(pins->mSrc[0]); + nins->mSrc[1].ForwardMem(pins->mSrc[1]); + nins->mSrc[1].mIntConst += ins->mSrc[0].mIntConst; + nins->mDst.mTemp = spareTemps++; + nins->mDst.mType = IT_POINTER; + nins->mDst.mRange.Reset(); + ins->mSrc[0].mRange.Reset(); + ins->mSrc[0].mIntConst = 0; + ins->mSrc[0].mTemp = nins->mDst.mTemp; + + mInstructions.Insert(k + 1, nins); + changed = true; + } + } + } + } + break; +#endif + } + + + // Now kill all instructions that referenced the current destination as source, they are + // not valid anymore + + int dtemp = ins->mDst.mTemp; + + if (dtemp >= 0) + { + for (int i = 0; i < ltvalue.Size(); i++) + { + if (ltvalue[i] && ltvalue[i]->ReferencesTemp(dtemp)) + ltvalue[i] = nullptr; + } + + if (!ins->UsesTemp(dtemp)) + ltvalue[dtemp] = ins; + } + } + +#if _DEBUG + for (int i = 0; i < mInstructions.Size(); i++) + { + InterInstruction* ins = mInstructions[i]; + assert(ins->mCode != IC_LOAD_TEMPORARY || ins->mSrc[0].mTemp >= 0); + } +#endif + + if (mTrueJump && mTrueJump->SimplifyIntegerNumeric(ltvalue, spareTemps)) + changed = true; + + if (mFalseJump && mFalseJump->SimplifyIntegerNumeric(ltvalue, spareTemps)) + changed = true; + } + + return changed; +} + +void InterCodeBasicBlock::PerformValueForwarding(const GrowingInstructionPtrArray& tvalue, const ValueSet& values, FastNumberSet& tvalid, const NumberSet& aliasedLocals, const NumberSet& aliasedParams, int& spareTemps, const GrowingVariableArray& staticVars, const GrowingInterCodeProcedurePtrArray& staticProcs) +{ + int i; + + if (!mVisited) + { + GrowingInstructionPtrArray ltvalue(tvalue); + ValueSet lvalues(values); + + if (mLoopHead) + { + lvalues.FlushAll(); + ltvalue.Clear(); + } +#if 0 + else if (mNumEntries > 1) + { + lvalues.FlushAll(); + ltvalue.Clear(); + } +#endif + else if (mNumEntries > 0) + { + if (mNumEntered > 0) + { + lvalues.Intersect(mMergeValues); + for (int i = 0; i < ltvalue.Size(); i++) + { + if (mMergeTValues[i] != ltvalue[i]) + ltvalue[i] = nullptr; + } + } + + mNumEntered++; + + if (mNumEntered < mNumEntries) + { + mMergeTValues = ltvalue; + mMergeValues = lvalues; + return; + } + } + + mVisited = true; + + tvalid.Clear(); + for (i = 0; i < ltvalue.Size(); i++) + { + if (ltvalue[i]) + tvalid += i; + } + + for (i = 0; i < mInstructions.Size(); i++) + { + InterInstruction* ins = mInstructions[i]; + + // Normalize kommutative operands if one is constant + +#if 1 + if (ins->mCode == IC_BINARY_OPERATOR && + (ins->mOperator == IA_MUL || ins->mOperator == IA_ADD || ins->mOperator == IA_AND || ins->mOperator == IA_OR || ins->mOperator == IA_XOR) && + ltvalue[ins->mSrc[1].mTemp] && ltvalue[ins->mSrc[0].mTemp] && + ltvalue[ins->mSrc[1].mTemp]->mCode == IC_CONSTANT && ltvalue[ins->mSrc[0].mTemp]->mCode != IC_CONSTANT) + { + InterOperand op = ins->mSrc[0]; + ins->mSrc[0] = ins->mSrc[1]; + ins->mSrc[1] = op; + } +#endif + +#if 1 + if (ins->mCode == IC_BINARY_OPERATOR && ins->mOperator == IA_MUL && ins->mDst.mType == IT_INT16 && spareTemps + 1 < tvalid.Size()) + { + InterInstruction* mi0 = ltvalue[ins->mSrc[0].mTemp], * mi1 = ltvalue[ins->mSrc[1].mTemp]; + InterInstruction* mci0 = nullptr; + InterInstruction* mci1 = nullptr; + if (mi0 && mi0->mCode == IC_CONVERSION_OPERATOR) + mci0 = ltvalue[mi0->mSrc[0].mTemp]; + if (mi1 && mi1->mCode == IC_CONVERSION_OPERATOR) + mci1 = ltvalue[mi1->mSrc[0].mTemp]; + + if (mi0 && mi1 && mi1->mCode == IC_CONSTANT && mi0->mCode == IC_BINARY_OPERATOR && mi0->mOperator == IA_ADD) + { + InterInstruction* ai0 = ltvalue[mi0->mSrc[0].mTemp], * ai1 = ltvalue[mi0->mSrc[1].mTemp]; + if (ai0 && ai0->mCode == IC_CONSTANT) + { + InterInstruction* nai = new InterInstruction(ins->mLocation, IC_BINARY_OPERATOR); + nai->mOperator = IA_MUL; + nai->mSrc[0].mTemp = mi0->mSrc[1].mTemp; + nai->mSrc[0].mType = IT_INT16; + nai->mSrc[1].mTemp = ins->mSrc[1].mTemp; + nai->mSrc[1].mType = IT_INT16; + nai->mDst.mTemp = spareTemps++; + nai->mDst.mType = IT_INT16; + mInstructions.Insert(i, nai); + + ltvalue[nai->mDst.mTemp] = nullptr; + + InterInstruction* cai = new InterInstruction(ins->mLocation, IC_CONSTANT); + cai->mDst.mTemp = spareTemps++; + cai->mDst.mType = IT_INT16; + cai->mConst.mIntConst = ai0->mConst.mIntConst * mi1->mConst.mIntConst; + mInstructions.Insert(i, cai); + + ltvalue[cai->mDst.mTemp] = nullptr; + + ins->mOperator = IA_ADD; + ins->mSrc[1].mTemp = nai->mDst.mTemp; + ins->mSrc[0].mTemp = cai->mDst.mTemp; + } + else if (ai1 && ai1->mCode == IC_CONSTANT) + { + InterInstruction* nai = new InterInstruction(ins->mLocation, IC_BINARY_OPERATOR); + nai->mOperator = IA_MUL; + nai->mSrc[0].mTemp = mi0->mSrc[0].mTemp; + nai->mSrc[0].mType = IT_INT16; + nai->mSrc[1].mTemp = ins->mSrc[1].mTemp; + nai->mSrc[1].mType = IT_INT16; + nai->mDst.mTemp = spareTemps++; + nai->mDst.mType = IT_INT16; + mInstructions.Insert(i, nai); + + ltvalue[nai->mDst.mTemp] = nullptr; + + InterInstruction* cai = new InterInstruction(ins->mLocation, IC_CONSTANT); + cai->mDst.mTemp = spareTemps++; + cai->mDst.mType = IT_INT16; + cai->mConst.mIntConst = ai1->mConst.mIntConst * mi1->mConst.mIntConst; + mInstructions.Insert(i, cai); + + ltvalue[cai->mDst.mTemp] = nullptr; + + ins->mOperator = IA_ADD; + ins->mSrc[1].mTemp = nai->mDst.mTemp; + ins->mSrc[0].mTemp = cai->mDst.mTemp; + } + } + else if (mi0 && mi1 && mi0->mCode == IC_CONSTANT && mi1->mCode == IC_BINARY_OPERATOR && mi1->mOperator == IA_ADD) + { + InterInstruction* ai0 = ltvalue[mi1->mSrc[0].mTemp], * ai1 = ltvalue[mi1->mSrc[1].mTemp]; + if (ai0 && ai0->mCode == IC_CONSTANT) + { + InterInstruction* nai = new InterInstruction(ins->mLocation, IC_BINARY_OPERATOR); + nai->mOperator = IA_MUL; + nai->mSrc[0].mTemp = mi1->mSrc[1].mTemp; + nai->mSrc[0].mType = IT_INT16; + nai->mSrc[1].mTemp = ins->mSrc[0].mTemp; + nai->mSrc[1].mType = IT_INT16; + nai->mDst.mTemp = spareTemps++; + nai->mDst.mType = IT_INT16; + mInstructions.Insert(i, nai); + + ltvalue[nai->mDst.mTemp] = nullptr; + + InterInstruction* cai = new InterInstruction(ins->mLocation, IC_CONSTANT); + cai->mDst.mTemp = spareTemps++; + cai->mDst.mType = IT_INT16; + cai->mConst.mIntConst = ai0->mConst.mIntConst * mi0->mConst.mIntConst; + mInstructions.Insert(i, cai); + + ltvalue[cai->mDst.mTemp] = nullptr; + + ins->mOperator = IA_ADD; + ins->mSrc[0].mTemp = nai->mDst.mTemp; + ins->mSrc[1].mTemp = cai->mDst.mTemp; + } + else if (ai1 && ai1->mCode == IC_CONSTANT) + { + InterInstruction* nai = new InterInstruction(ins->mLocation, IC_BINARY_OPERATOR); + nai->mOperator = IA_MUL; + nai->mSrc[0].mTemp = mi1->mSrc[0].mTemp; + nai->mSrc[0].mType = IT_INT16; + nai->mSrc[1].mTemp = ins->mSrc[0].mTemp; + nai->mSrc[1].mType = IT_INT16; + nai->mDst.mTemp = spareTemps++; + nai->mDst.mType = IT_INT16; + mInstructions.Insert(i, nai); + + ltvalue[nai->mDst.mTemp] = nullptr; + + InterInstruction* cai = new InterInstruction(ins->mLocation, IC_CONSTANT); + cai->mDst.mTemp = spareTemps++; + cai->mDst.mType = IT_INT16; + cai->mConst.mIntConst = ai1->mConst.mIntConst * mi0->mConst.mIntConst; + mInstructions.Insert(i, cai); + + ltvalue[cai->mDst.mTemp] = nullptr; + + ins->mOperator = IA_ADD; + ins->mSrc[0].mTemp = nai->mDst.mTemp; + ins->mSrc[1].mTemp = cai->mDst.mTemp; + } + } +#if 1 + else if (mi0 && mi1 && mi0->mCode == IC_CONSTANT && mi1->mCode == IC_BINARY_OPERATOR && mi1->mOperator == IA_SUB) + { + InterInstruction* ai0 = ltvalue[mi1->mSrc[0].mTemp], * ai1 = ltvalue[mi1->mSrc[1].mTemp]; + if (ai0 && ai0->mCode == IC_CONSTANT) + { + InterInstruction* nai = new InterInstruction(ins->mLocation, IC_BINARY_OPERATOR); + nai->mOperator = IA_MUL; + nai->mSrc[0].mTemp = mi1->mSrc[1].mTemp; + nai->mSrc[0].mType = IT_INT16; + nai->mSrc[1].mTemp = ins->mSrc[0].mTemp; + nai->mSrc[1].mType = IT_INT16; + nai->mDst.mTemp = spareTemps++; + nai->mDst.mType = IT_INT16; + mInstructions.Insert(i, nai); + + ltvalue[nai->mDst.mTemp] = nullptr; + + InterInstruction* cai = new InterInstruction(ins->mLocation, IC_CONSTANT); + cai->mDst.mTemp = spareTemps++; + cai->mDst.mType = IT_INT16; + cai->mConst.mIntConst = ai0->mConst.mIntConst * mi0->mConst.mIntConst; + mInstructions.Insert(i, cai); + + ltvalue[cai->mDst.mTemp] = nullptr; + + ins->mOperator = IA_SUB; + ins->mSrc[1].mTemp = nai->mDst.mTemp; + ins->mSrc[0].mTemp = cai->mDst.mTemp; + } + } +#endif +#if 1 + else if (mi0 && mci1 && mci1->mCode == IC_RELATIONAL_OPERATOR) + { + InterInstruction* cai = new InterInstruction(ins->mLocation, IC_CONSTANT); + cai->mDst.mTemp = spareTemps++; + cai->mDst.mType = ins->mDst.mType; + cai->mConst.mIntConst = 0; + cai->mConst.mFloatConst = 0; + mInstructions.Insert(i, cai); + + ins->mCode = IC_SELECT; + ins->mNumOperands = 3; + ins->mSrc[2] = mci1->mDst; + ins->mSrc[1] = ins->mSrc[0]; + ins->mSrc[0].mTemp = cai->mDst.mTemp; + + ltvalue[cai->mDst.mTemp] = nullptr; + } + else if (mci0 && mi1 && mci0->mCode == IC_RELATIONAL_OPERATOR) + { + InterInstruction* cai = new InterInstruction(ins->mLocation, IC_CONSTANT); + cai->mDst.mTemp = spareTemps++; + cai->mDst.mType = ins->mDst.mType; + cai->mConst.mIntConst = 0; + cai->mConst.mFloatConst = 0; + mInstructions.Insert(i, cai); + + ins->mCode = IC_SELECT; + ins->mNumOperands = 3; + ins->mSrc[2] = mci0->mDst; + ins->mSrc[0].mTemp = cai->mDst.mTemp; + + ltvalue[cai->mDst.mTemp] = nullptr; + } +#endif + } +#endif +#if 1 + if (ins->mCode == IC_BINARY_OPERATOR && ins->mOperator == IA_ADD && ins->mDst.mType == IT_INT16 && spareTemps < tvalid.Size()) + { + InterInstruction* mi0 = ltvalue[ins->mSrc[0].mTemp], * mi1 = ltvalue[ins->mSrc[1].mTemp]; + + if (mi0 && mi1) + { + if (mi1->mCode == IC_CONSTANT && mi0->mCode == IC_BINARY_OPERATOR && mi0->mOperator == IA_ADD) + { + InterInstruction* ai0 = ltvalue[mi0->mSrc[0].mTemp], * ai1 = ltvalue[mi0->mSrc[1].mTemp]; + if (ai0 && ai1) + { + if (ai0 && ai0->mCode == IC_CONSTANT) + { + InterInstruction* cai = new InterInstruction(ins->mLocation, IC_CONSTANT); + cai->mDst.mTemp = spareTemps++; + cai->mDst.mType = IT_INT16; + cai->mConst.mIntConst = ai0->mConst.mIntConst + mi1->mConst.mIntConst; + mInstructions.Insert(i, cai); + + ltvalue[cai->mDst.mTemp] = nullptr; + + ins->mSrc[1].mTemp = mi0->mSrc[1].mTemp; + ins->mSrc[0].mTemp = cai->mDst.mTemp; + } + else if (ai1 && ai1->mCode == IC_CONSTANT) + { + InterInstruction* cai = new InterInstruction(ins->mLocation, IC_CONSTANT); + cai->mDst.mTemp = spareTemps++; + cai->mDst.mType = IT_INT16; + cai->mConst.mIntConst = ai1->mConst.mIntConst + mi1->mConst.mIntConst; + mInstructions.Insert(i, cai); + + ltvalue[cai->mDst.mTemp] = nullptr; + + ins->mSrc[1].mTemp = mi0->mSrc[0].mTemp; + ins->mSrc[0].mTemp = cai->mDst.mTemp; + } + } + } + else if (mi0->mCode == IC_CONSTANT && mi1->mCode == IC_BINARY_OPERATOR && mi1->mOperator == IA_ADD) + { + InterInstruction* ai0 = ltvalue[mi1->mSrc[0].mTemp], * ai1 = ltvalue[mi1->mSrc[1].mTemp]; + if (ai0 && ai1) + { + if (ai0 && ai0->mCode == IC_CONSTANT) + { + InterInstruction* cai = new InterInstruction(ins->mLocation, IC_CONSTANT); + cai->mDst.mTemp = spareTemps++; + cai->mDst.mType = IT_INT16; + cai->mConst.mIntConst = ai0->mConst.mIntConst + mi0->mConst.mIntConst; + mInstructions.Insert(i, cai); + + ltvalue[cai->mDst.mTemp] = nullptr; + + ins->mSrc[1].mTemp = mi1->mSrc[1].mTemp; + ins->mSrc[0].mTemp = cai->mDst.mTemp; + } + else if (ai1 && ai1->mCode == IC_CONSTANT) + { + InterInstruction* cai = new InterInstruction(ins->mLocation, IC_CONSTANT); + cai->mDst.mTemp = spareTemps++; + cai->mDst.mType = IT_INT16; + cai->mConst.mIntConst = ai1->mConst.mIntConst + mi0->mConst.mIntConst; + mInstructions.Insert(i, cai); + + ltvalue[cai->mDst.mTemp] = nullptr; + + ins->mSrc[1].mTemp = mi1->mSrc[0].mTemp; + ins->mSrc[0].mTemp = cai->mDst.mTemp; + } + } + } + } + } + + if (ins->mCode == IC_LEA && spareTemps < tvalid.Size()) + { + InterInstruction* li0 = ltvalue[ins->mSrc[0].mTemp], * li1 = ltvalue[ins->mSrc[1].mTemp]; + + if (li0 && li1) + { + if (li1->mCode != IC_CONSTANT && li0->mCode == IC_BINARY_OPERATOR && li0->mOperator == IA_ADD) + { + InterInstruction* ai0 = ltvalue[li0->mSrc[0].mTemp], * ai1 = ltvalue[li0->mSrc[1].mTemp]; + if (ai0 && ai1 && ai0->mCode == IC_CONSTANT && ai0->mConst.mIntConst >= 0) + { + InterInstruction* nai = new InterInstruction(ins->mLocation, IC_LEA); + nai->mSrc[1].mMemory = IM_INDIRECT; + nai->mSrc[0].mTemp = li0->mSrc[1].mTemp; + nai->mSrc[0].mType = IT_INT16; + nai->mSrc[1].mTemp = ins->mSrc[1].mTemp; + nai->mSrc[1].mType = IT_POINTER; + nai->mDst.mTemp = spareTemps++; + nai->mDst.mType = IT_POINTER; + mInstructions.Insert(i, nai); + + ltvalue[nai->mDst.mTemp] = nullptr; + + ins->mSrc[1].mTemp = nai->mDst.mTemp; + ins->mSrc[0].mTemp = li0->mSrc[0].mTemp; + } + else if (ai1 && ai1->mCode == IC_CONSTANT) + { + } + } + else if (li0->mCode == IC_CONSTANT && li1->mCode == IC_LEA) + { + InterInstruction* ai0 = ltvalue[li1->mSrc[0].mTemp], * ai1 = ltvalue[li1->mSrc[1].mTemp]; + if (ai0 && ai1 && ai0->mCode == IC_CONSTANT)// && ai0->mConst.mIntConst >= 0) + { + InterInstruction* cai = new InterInstruction(ins->mLocation, IC_CONSTANT); + cai->mDst.mTemp = spareTemps++; + cai->mDst.mType = IT_INT16; + cai->mConst.mIntConst = ai0->mConst.mIntConst + li0->mConst.mIntConst; + cai->mConst.mType = IT_INT16; + mInstructions.Insert(i, cai); + + ins->mSrc[0].mTemp = cai->mDst.mTemp; + ins->mSrc[1].mTemp = li1->mSrc[1].mTemp; + + ltvalue[cai->mDst.mTemp] = nullptr; + } + } + } + } + +#endif + lvalues.UpdateValue(this, mInstructions[i], ltvalue, aliasedLocals, aliasedParams, staticVars, staticProcs); + mInstructions[i]->PerformValueForwarding(ltvalue, tvalid); + } + + if (mTrueJump) mTrueJump->PerformValueForwarding(ltvalue, lvalues, tvalid, aliasedLocals, aliasedParams, spareTemps, staticVars, staticProcs); + if (mFalseJump) mFalseJump->PerformValueForwarding(ltvalue, lvalues, tvalid, aliasedLocals, aliasedParams, spareTemps, staticVars, staticProcs); + } +} + +void InterCodeBasicBlock::PerformMachineSpecificValueUsageCheck(const GrowingInstructionPtrArray& tvalue, FastNumberSet& tvalid, const GrowingVariableArray& staticVars, const GrowingInterCodeProcedurePtrArray& staticProcs, FastNumberSet &fsingle) +{ + int i; + + if (!mVisited) + { + GrowingInstructionPtrArray ltvalue(tvalue); + + if (mLoopHead) + { + ltvalue.Clear(); + } +#if 0 + else if (mNumEntries > 1) + { + lvalues.FlushAll(); + ltvalue.Clear(); + } +#endif + else if (mNumEntries > 0) + { + if (mNumEntered > 0) + { + for (int i = 0; i < ltvalue.Size(); i++) + { + if (mMergeTValues[i] != ltvalue[i]) + ltvalue[i] = nullptr; + } + } + + mNumEntered++; + + if (mNumEntered < mNumEntries) + { + mMergeTValues = ltvalue; + return; + } + } + + mVisited = true; + + tvalid.Clear(); + for (i = 0; i < ltvalue.Size(); i++) + { + if (ltvalue[i]) + tvalid += i; + } + + for (i = 0; i < mInstructions.Size(); i++) + { + CheckValueUsage(mInstructions[i], ltvalue, staticVars, staticProcs, fsingle); + mInstructions[i]->PerformValueForwarding(ltvalue, tvalid); + } + + if (mTrueJump) mTrueJump->PerformMachineSpecificValueUsageCheck(ltvalue, tvalid, staticVars, staticProcs, fsingle); + if (mFalseJump) mFalseJump->PerformMachineSpecificValueUsageCheck(ltvalue, tvalid, staticVars, staticProcs, fsingle); + } +} + + +bool InterCodeBasicBlock::EliminateDeadBranches(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + if (mInstructions.Size() > 0) + { + if (mInstructions[mInstructions.Size() - 1]->mCode == IC_JUMP && mFalseJump) + { + mFalseJump->mNumEntries--; + mFalseJump = nullptr; + changed = true; + } + else if (mInstructions[mInstructions.Size() - 1]->mCode == IC_JUMPF) + { + mInstructions[mInstructions.Size() - 1]->mCode = IC_JUMP; + mTrueJump->mNumEntries--; + mTrueJump = mFalseJump; + mFalseJump = nullptr; + changed = true; + } + } + + if (mTrueJump && mTrueJump->EliminateDeadBranches()) changed = true; + if (mFalseJump && mFalseJump->EliminateDeadBranches()) changed = true; + } + + return changed; +} + +static void Union(GrowingIntArray& table, int i, int j) +{ + int k, l; + + k = table[j]; + while (j != k) + { + l = table[k]; + table[j] = l; + j = k; k = l; + } + + table[j] = table[i]; +} + +static int Find(GrowingIntArray& table, int i) +{ + int j, k, l; + + j = i; + k = table[j]; + while (j != k) + { + l = table[k]; + table[j] = l; + j = k; k = l; + } + + return j; +} + +void InterCodeBasicBlock::LinkerObjectForwarding(const GrowingInstructionPtrArray& tvalue) +{ + if (!mVisited) + { + GrowingInstructionPtrArray ltvalue(tvalue); + + if (mLoopHead) + { + if (mNumEntries == 2 && (mTrueJump == this || mFalseJump == this)) + { + mLoadStoreInstructions = tvalue; + for (int i = 0; i < mInstructions.Size(); i++) + { + InterInstruction* ins(mInstructions[i]); + if (ins->mDst.mTemp >= 0) + ltvalue[ins->mDst.mTemp] = nullptr; + } + } + else + ltvalue.Clear(); + } + else if (mNumEntries > 0) + { + if (mNumEntered > 0) + { + for (int i = 0; i < ltvalue.Size(); i++) + { + if (mMergeTValues[i] != ltvalue[i]) + ltvalue[i] = nullptr; + } + } + + mNumEntered++; + + if (mNumEntered < mNumEntries) + { + mMergeTValues = ltvalue; + return; + } + } + + mVisited = true; + + for (int i = 0; i < mInstructions.Size(); i++) + { + InterInstruction * ins(mInstructions[i]); + InterInstruction* lins = nullptr; + + if (ins->mCode == IC_LEA) + { + if (ins->mSrc[1].mTemp >= 0 && ltvalue[ins->mSrc[1].mTemp]) + ins->mSrc[1].mLinkerObject = ltvalue[ins->mSrc[1].mTemp]->mSrc[1].mLinkerObject; + + if (ins->mSrc[1].mLinkerObject) + lins = ins; + } + else if (ins->mCode == IC_LOAD) + { + if (ins->mSrc[0].mTemp >= 0 && ltvalue[ins->mSrc[0].mTemp]) + ins->mSrc[0].mLinkerObject = ltvalue[ins->mSrc[0].mTemp]->mSrc[1].mLinkerObject; + } + else if (ins->mCode == IC_STORE) + { + if (ins->mSrc[1].mTemp >= 0 && ltvalue[ins->mSrc[1].mTemp]) + ins->mSrc[1].mLinkerObject = ltvalue[ins->mSrc[1].mTemp]->mSrc[1].mLinkerObject; + } + + if (lins) + ltvalue[lins->mDst.mTemp] = lins; + else if (ins->mDst.mTemp >= 0) + ltvalue[ins->mDst.mTemp] = nullptr; + } + + if (mTrueJump) mTrueJump->LinkerObjectForwarding(ltvalue); + if (mFalseJump) mFalseJump->LinkerObjectForwarding(ltvalue); + } +} + +void InterCodeBasicBlock::ReduceRecursionTempSpilling(InterMemory paramMemory, const GrowingInstructionPtrArray& tvalue) +{ + if (!mVisited) + { + if (!mLoopHead) + { + if (mNumEntries > 0) + { + if (mNumEntered == 0) + mLoadStoreInstructions = tvalue; + else + { + int i = 0; + while (i < mLoadStoreInstructions.Size()) + { + InterInstruction* ins(mLoadStoreInstructions[i]); + InterInstruction* nins = nullptr; + + int j = tvalue.IndexOf(ins); + if (j != -1) + nins = ins; + + if (nins) + mLoadStoreInstructions[i++] = nins; + else + mLoadStoreInstructions.Remove(i); + } + } + + mNumEntered++; + + if (mNumEntered < mNumEntries) + return; + } + } +#if 1 + else if (mNumEntries == 2 && (mTrueJump == this || mFalseJump == this)) + { + mLoadStoreInstructions = tvalue; + for (int i = 0; i < mInstructions.Size(); i++) + { + InterInstruction* ins(mInstructions[i]); + if (ins->mDst.mTemp >= 0) + { + int j = 0; + while (j < mLoadStoreInstructions.Size()) + { + if (mLoadStoreInstructions[j]->ReferencesTemp(ins->mDst.mTemp) || CollidingMem(ins, mLoadStoreInstructions[j])) + mLoadStoreInstructions.Remove(j); + else + j++; + } + } + } + } +#endif + else + mLoadStoreInstructions.SetSize(0); + + mVisited = true; + + NumberSet rtemps(mEntryRequiredTemps); + + for (int i = 0; i < mInstructions.Size(); i++) + { + InterInstruction* ins(mInstructions[i]); + InterInstruction* lins = nullptr; + bool flushMem = false; + + if (ins->mCode == IC_CALL || ins->mCode == IC_CALL_NATIVE) + { + if (ins->mSrc[0].mLinkerObject == mProc->mLinkerObject) + { + for (int j = 0; j < mLoadStoreInstructions.Size(); j++) + { + if (rtemps[mLoadStoreInstructions[j]->mDst.mTemp]) + mInstructions.Insert(i + 1, mLoadStoreInstructions[j]->Clone()); + } + } + } + else if (ins->mCode == IC_LOAD && ins->mSrc[0].mTemp < 0 && ins->mSrc[0].mMemory == paramMemory) + { + if (InterTypeSize[ins->mDst.mType] == ins->mSrc[0].mOperandSize) + lins = ins; + } + + for (int j = 0; j < ins->mNumOperands; j++) + { + if (ins->mSrc[j].mTemp >= 0 && ins->mSrc[j].mFinal) + rtemps -= ins->mSrc[j].mTemp; + } + + int j = 0, k = 0, t = ins->mDst.mTemp; + if (t >= 0 || IsObservable(ins->mCode)) + { + while (j < mLoadStoreInstructions.Size()) + { + if (DestroyingMem(mLoadStoreInstructions[j], ins)) + ; + else if (t != mLoadStoreInstructions[j]->mDst.mTemp) + mLoadStoreInstructions[k++] = mLoadStoreInstructions[j]; + + j++; + } + mLoadStoreInstructions.SetSize(k); + } + + if (lins) + mLoadStoreInstructions.Push(lins); + } + + if (mTrueJump) mTrueJump->ReduceRecursionTempSpilling(paramMemory, mLoadStoreInstructions); + if (mFalseJump) mFalseJump->ReduceRecursionTempSpilling(paramMemory, mLoadStoreInstructions); + } +} + + +bool InterCodeBasicBlock::LoadStoreForwarding(const GrowingInstructionPtrArray& tvalue, const GrowingVariableArray& staticVars) +{ + bool changed = false; + + if (!mVisited) + { + if (!mLoopHead) + { + if (mNumEntries > 0) + { + if (mNumEntered == 0) + mLoadStoreInstructions = tvalue; + else + { + int i = 0; + while (i < mLoadStoreInstructions.Size()) + { + InterInstruction* ins(mLoadStoreInstructions[i]); + InterInstruction* nins = nullptr; + + int j = tvalue.IndexOf(ins); + if (j != -1) + nins = ins; + else + { + if (ins->mCode == IC_LOAD) + { + j = 0; + while (j < tvalue.Size() && !SameMemAndSize(ins->mSrc[0], tvalue[j])) + j++; + if (j < tvalue.Size()) + { + InterInstruction* tins = tvalue[j]; + + if (tvalue[j]->mCode == IC_LOAD && tins->mDst.IsEqual(ins->mDst)) + nins = ins; + else if (tvalue[j]->mCode == IC_STORE && tins->mSrc[0].IsEqual(ins->mDst)) + nins = ins; + } + } + else if (ins->mCode == IC_STORE) + { + j = 0; + while (j < tvalue.Size() && !SameMemAndSize(ins->mSrc[1], tvalue[j])) + j++; + if (j < tvalue.Size()) + { + InterInstruction* tins = tvalue[j]; + + if (tvalue[j]->mCode == IC_LOAD && tvalue[j]->mDst.IsEqual(ins->mSrc[0])) + nins = tvalue[j]; + else if (tvalue[j]->mCode == IC_STORE && tvalue[j]->mSrc[0].IsEqual(ins->mSrc[0])) + { + nins = ins; + if (ins->mSrc[1].mTemp >= 0 && tins->mSrc[1].mTemp >= 0) + { + if (ins->mSrc[1].mMemoryBase != tins->mSrc[1].mMemoryBase || + ins->mSrc[1].mMemoryBase == IM_GLOBAL && ins->mSrc[1].mVarIndex != tins->mSrc[1].mVarIndex) + { + nins = ins->Clone(); + nins->mSrc[1].mMemoryBase = IM_INDIRECT; + } + } + } + } + } + } + + if (nins) + mLoadStoreInstructions[i++] = nins; + else + mLoadStoreInstructions.Remove(i); + } + } + + mNumEntered++; + + if (mNumEntered < mNumEntries) + { + return false; + } + } + } +#if 1 + else if (mNumEntries == 2 && (mTrueJump == this || mFalseJump == this)) + { + mLoadStoreInstructions = tvalue; + for (int i = 0; i < mInstructions.Size(); i++) + { + InterInstruction* ins(mInstructions[i]); + int j = 0; + while (j < mLoadStoreInstructions.Size()) + { + if (InvalidatedBy(mLoadStoreInstructions[j], ins)) + mLoadStoreInstructions.Remove(j); + else + j++; + } + } + } +#if 1 + else if (tvalue.Size() > 0) + { + ExpandingArray body; + body.Push(this); + bool innerLoop = true; + int n = 1; + + for (int i = 0; i < mEntryBlocks.Size(); i++) + { + if (IsDominator( mEntryBlocks[i] )) + { + n++; + if (!mEntryBlocks[i]->CollectLoopBodyRecursive(this, body)) + innerLoop = false; + } + } + + if (innerLoop && n == mEntryBlocks.Size() ) + { + mLoadStoreInstructions = tvalue; + for (int j = 0; j < body.Size(); j++) + { + for (int i = 0; i < body[j]->mInstructions.Size(); i++) + { + InterInstruction* ins(body[j]->mInstructions[i]); + int j = 0; + while (j < mLoadStoreInstructions.Size()) + { + if (InvalidatedBy(mLoadStoreInstructions[j], ins)) + mLoadStoreInstructions.Remove(j); + else + j++; + } + } + } + } + else + mLoadStoreInstructions.SetSize(0); + } +#endif +#endif + else + mLoadStoreInstructions.SetSize(0); + + mVisited = true; + +#if 0 + for (int i = 0; i < mLoadStoreInstructions.Size(); i++) + { + if (mLoadStoreInstructions[i]->mCode == IC_STORE && + mLoadStoreInstructions[i]->mSrc[1].mTemp < 0 && + mLoadStoreInstructions[i]->mSrc[1].mMemory == IM_GLOBAL && + mLoadStoreInstructions[i]->mSrc[1].mVarIndex == 68) + printf("I:%d\n", mIndex); + } +#endif + +#if 1 + // move loads up as far as possible to avoid false aliasing + for (int i = 1; i < mInstructions.Size(); i++) + { + InterInstruction* ins(mInstructions[i]); + if (ins->mCode == IC_LOAD) + { + int j = i; + while (j > 0 && CanSwapInstructions(mInstructions[j - 1], ins)) + { + SwapInstructions(mInstructions[j - 1], ins); + mInstructions[j] = mInstructions[j - 1]; + j--; + } + if (i != j) + mInstructions[j] = ins; + } + } +#endif + + for (int i = 0; i < mInstructions.Size(); i++) + { + InterInstruction* ins(mInstructions[i]); + + InterInstruction * nins = nullptr; + bool flushMem = false; + + if (ins->mCode == IC_LOAD) + { + if (!ins->mVolatile) + { + int j = 0; + while (j < mLoadStoreInstructions.Size() && !SameMemAndSize(ins->mSrc[0], mLoadStoreInstructions[j])) + j++; + if (j < mLoadStoreInstructions.Size()) + { + InterInstruction* lins = mLoadStoreInstructions[j]; + if (lins->mCode == IC_LOAD) + { + ins->mCode = IC_LOAD_TEMPORARY; + ins->mSrc[0] = lins->mDst; + ins->mSrc[0].mRestricted = ins->mDst.mRestricted = lins->mDst.mRestricted; + ins->mNumOperands = 1; + assert(ins->mSrc[0].mTemp >= 0); + changed = true; + } + else if (lins->mCode == IC_STORE) + { + if (lins->mSrc[0].mTemp < 0) + { + ins->mCode = IC_CONSTANT; + ins->mConst = lins->mSrc[0]; + ins->mNumOperands = 0; + changed = true; + } + else + { + ins->mCode = IC_LOAD_TEMPORARY; + ins->mSrc[0] = lins->mSrc[0]; + ins->mSrc[0].mRestricted = ins->mDst.mRestricted = lins->mSrc[0].mRestricted; + ins->mDst.mRange.Limit(ins->mSrc[0].mRange); + ins->mNumOperands = 1; + assert(ins->mSrc[0].mTemp >= 0); + changed = true; + } + } + } + else + { + j = 0; + while (j < mLoadStoreInstructions.Size() && !( + mLoadStoreInstructions[j]->mCode == IC_COPY && SameMemSegment(mLoadStoreInstructions[j]->mSrc[1], ins->mSrc[0]) || + mLoadStoreInstructions[j]->mCode == IC_FILL && SameMemSegment(mLoadStoreInstructions[j]->mSrc[1], ins->mSrc[0]))) + j++; + if (j < mLoadStoreInstructions.Size()) + { + InterInstruction* cins = mLoadStoreInstructions[j]; + + if (cins->mCode == IC_FILL) + { + int64 v = 0; + for (int j = 0; j < InterTypeSize[ins->mDst.mType]; j++) + v = (v << 8) | (cins->mSrc[0].mIntConst & 0xff); + + ins->mCode = IC_CONSTANT; + ins->mConst.mType = ins->mDst.mType; + ins->mConst.mIntConst = v; + ins->mNumOperands = 0; + changed = true; + } + else + { + int64 offset = ins->mSrc[0].mIntConst - cins->mSrc[1].mIntConst; + ins->mSrc[0] = cins->mSrc[0]; + ins->mSrc[0].mOperandSize = InterTypeSize[ins->mDst.mType]; + ins->mSrc[0].mIntConst += offset * cins->mSrc[0].mStride; + changed = true; + } + } + else + nins = ins; + } + } + } + else if (ins->mCode == IC_STORE) + { + if (ins->mMemmap) + { + int j = 0, k = 0; + while (j < mLoadStoreInstructions.Size()) + { + if (!IsLoadStore(mLoadStoreInstructions[j]->mCode)) + mLoadStoreInstructions[k++] = mLoadStoreInstructions[j]; + j++; + } + mLoadStoreInstructions.SetSize(k); + } + else + { + int j = 0, k = 0; + + while (j < mLoadStoreInstructions.Size() && !SameMemAndSize(ins->mSrc[1], mLoadStoreInstructions[j])) + j++; + + if (!ins->mVolatile && j < mLoadStoreInstructions.Size() && mLoadStoreInstructions[j]->mCode == IC_LOAD && ins->mSrc[0].mTemp == mLoadStoreInstructions[j]->mDst.mTemp) + { + ins->mCode = IC_NONE; + ins->mNumOperands = 0; + changed = true; + } + else + { + j = 0; + while (j < mLoadStoreInstructions.Size()) + { + if (!DestroyingMem(mLoadStoreInstructions[j], ins)) + mLoadStoreInstructions[k++] = mLoadStoreInstructions[j]; + j++; + } + mLoadStoreInstructions.SetSize(k); + } + + if (!ins->mVolatile) + nins = ins; + } + } + else if (ins->mCode == IC_COPY) + { + int j = 0, k = 0; + while (j < mLoadStoreInstructions.Size()) + { + if (!DestroyingMem(mLoadStoreInstructions[j], ins)) + mLoadStoreInstructions[k++] = mLoadStoreInstructions[j]; + j++; + } + mLoadStoreInstructions.SetSize(k); +#if 1 + uint64 fillmask = 0; + if (ins->mSrc[0].mOperandSize < 64 && ins->mSrc[0].mStride == 1 && ins->mSrc[1].mStride == 1) + { + for (int j = 0; j < mLoadStoreInstructions.Size(); j++) + { + InterInstruction* sins = mLoadStoreInstructions[j]; + if (sins->mCode == IC_STORE && SameMemSegment(ins->mSrc[0], sins->mSrc[1])) + { + int64 offset = sins->mSrc[1].mIntConst - ins->mSrc[0].mIntConst; + if (offset >= 0 && offset < ins->mSrc[0].mOperandSize) + { + for (int k = 0; k < InterTypeSize[sins->mSrc[0].mType]; k++) + fillmask |= 1ULL << (k + offset); + } + } + } + + if (fillmask + 1 == (1ULL << ins->mSrc[0].mOperandSize)) + { + int n = 0; + for (int j = 0; j < mLoadStoreInstructions.Size(); j++) + { + InterInstruction* sins = mLoadStoreInstructions[j]; + if (sins->mCode == IC_STORE && SameMemSegment(ins->mSrc[0], sins->mSrc[1])) + { + int64 offset = sins->mSrc[1].mIntConst - ins->mSrc[0].mIntConst; + if (offset >= 0 && offset < ins->mSrc[0].mOperandSize) + { + InterInstruction* tins = sins->Clone(); + tins->mSrc[1] = ins->mSrc[1]; + tins->mSrc[1].mOperandSize = sins->mSrc[1].mOperandSize; + tins->mSrc[1].mIntConst = sins->mSrc[1].mIntConst - ins->mSrc[0].mIntConst + ins->mSrc[1].mIntConst; + n++; + mInstructions.Insert(i + n, tins); + } + } + } + ins->mCode = IC_NONE; + ins->mNumOperands = 0; + } + else if (!ins->mVolatile && ins->mSrc[0].mStride == 1 && ins->mSrc[1].mStride == 1) + nins = ins; + } + else if (!ins->mVolatile && ins->mSrc[0].mStride == 1 && ins->mSrc[1].mStride == 1) + nins = ins; + else if (!ins->mVolatile && ins->mSrc[1].mTemp < 0 && ins->mSrc[1].mStride == 1) + nins = ins; +#endif + } + else if (ins->mCode == IC_FILL) + { + int j = 0, k = 0; + while (j < mLoadStoreInstructions.Size()) + { + if (!DestroyingMem(mLoadStoreInstructions[j], ins)) + mLoadStoreInstructions[k++] = mLoadStoreInstructions[j]; + j++; + } + mLoadStoreInstructions.SetSize(k); + + if (!ins->mVolatile && ins->mSrc[1].mStride == 1 && ins->mSrc[0].mTemp < 0) + nins = ins; + } + else if (ins->mCode == IC_STRCPY) + { + int j = 0, k = 0; + while (j < mLoadStoreInstructions.Size()) + { + if (!DestroyingMem(mLoadStoreInstructions[j], ins)) + mLoadStoreInstructions[k++] = mLoadStoreInstructions[j]; + j++; + } + mLoadStoreInstructions.SetSize(k); + } + else if (ins->mCode == IC_LEA || ins->mCode == IC_UNARY_OPERATOR || ins->mCode == IC_BINARY_OPERATOR || ins->mCode == IC_RELATIONAL_OPERATOR || ins->mCode == IC_CONVERSION_OPERATOR) + { + // + int j = 0; + while (j < mLoadStoreInstructions.Size() && !SameInstruction(ins, mLoadStoreInstructions[j])) + j++; + if (j < mLoadStoreInstructions.Size()) + { + InterInstruction* lins = mLoadStoreInstructions[j]; + assert(lins->mDst.mTemp >= 0); + ins->mCode = IC_LOAD_TEMPORARY; + ins->mSrc[0] = lins->mDst; + ins->mSrc[0].mRestricted = ins->mDst.mRestricted = lins->mDst.mRestricted; + ins->mDst.mRange.Limit(ins->mSrc[0].mRange); + ins->mNumOperands = 1; + changed = true; + } + else if (ins->mCode == IC_LEA && ins->mSrc[1].mTemp < 0 && (ins->mSrc[1].mMemory == IM_ABSOLUTE || ins->mSrc[1].mMemory == IM_GLOBAL) && ins->mSrc[0].mTemp >= 0) + { + int offset = int(ins->mSrc[1].mIntConst); + + j = 0; + while (j < mLoadStoreInstructions.Size() && !( + mLoadStoreInstructions[j]->mCode == IC_LEA && mLoadStoreInstructions[j]->mSrc[1].mTemp < 0 && SameMemRegion(mLoadStoreInstructions[j]->mSrc[1], ins->mSrc[1]) && + mLoadStoreInstructions[j]->mSrc[0].mTemp == ins->mSrc[0].mTemp && + (((mLoadStoreInstructions[j]->mSrc[0].mIntConst + mLoadStoreInstructions[j]->mSrc[1].mIntConst - offset) & 255) == 0))) + j++; + if (j < mLoadStoreInstructions.Size()) + { + ins->mSrc[1] = mLoadStoreInstructions[j]->mDst; + ins->mSrc[1].mMemory = IM_INDIRECT; + ins->mSrc[1].mIntConst = 0; + + ins->mSrc[0].mTemp = -1; + ins->mSrc[0].mIntConst = offset - mLoadStoreInstructions[j]->mSrc[1].mIntConst; + changed = true; + } + else + { + if (i + 1 < mInstructions.Size() && + (mInstructions[i + 1]->mCode == IC_STORE && mInstructions[i + 1]->mSrc[1].mTemp == ins->mDst.mTemp && mInstructions[i + 1]->mSrc[1].mFinal || + mInstructions[i + 1]->mCode == IC_LOAD && mInstructions[i + 1]->mSrc[0].mTemp == ins->mDst.mTemp && mInstructions[i + 1]->mSrc[0].mFinal)) + { + int64 loffset = (mInstructions[i + 1]->mCode == IC_STORE ? mInstructions[i + 1]->mSrc[1].mIntConst : mInstructions[i + 1]->mSrc[0].mIntConst) + offset; + + j = 0; + while (j < mLoadStoreInstructions.Size() && !( + mLoadStoreInstructions[j]->mCode == IC_LEA && mLoadStoreInstructions[j]->mSrc[1].mTemp < 0 && SameMemRegion(mLoadStoreInstructions[j]->mSrc[1], ins->mSrc[1]) && + mLoadStoreInstructions[j]->mSrc[0].mTemp == ins->mSrc[0].mTemp && + loffset - mLoadStoreInstructions[j]->mSrc[0].mIntConst - mLoadStoreInstructions[j]->mSrc[1].mIntConst >= 0 && + loffset - mLoadStoreInstructions[j]->mSrc[0].mIntConst - mLoadStoreInstructions[j]->mSrc[1].mIntConst < 255)) + j++; + + if (j < mLoadStoreInstructions.Size()) + { + InterInstruction* lins = mLoadStoreInstructions[j]; + assert(lins->mDst.mTemp >= 0); + ins->mCode = IC_LOAD_TEMPORARY; + ins->mSrc[0] = lins->mDst; + ins->mSrc[0].mRestricted = ins->mDst.mRestricted = lins->mDst.mRestricted; + ins->mDst.mRange = lins->mDst.mRange; + ins->mNumOperands = 1; + + if (mInstructions[i + 1]->mCode == IC_STORE) + { + mInstructions[i + 1]->mSrc[1].mRange = ins->mDst.mRange; + mInstructions[i + 1]->mSrc[1].mIntConst = loffset - mLoadStoreInstructions[j]->mSrc[0].mIntConst - mLoadStoreInstructions[j]->mSrc[1].mIntConst; + } + else + { + mInstructions[i + 1]->mSrc[0].mRange = ins->mDst.mRange; + mInstructions[i + 1]->mSrc[0].mIntConst = loffset - mLoadStoreInstructions[j]->mSrc[0].mIntConst - mLoadStoreInstructions[j]->mSrc[1].mIntConst; + } + + changed = true; + } + else + nins = ins; + } + else + nins = ins; + } + } + else + nins = ins; + } + else if (ins->mCode == IC_CALL_NATIVE && ins->mSrc[0].mTemp < 0 && ins->mSrc[0].mLinkerObject && ins->mSrc[0].mLinkerObject->mProc && ins->mSrc[0].mLinkerObject->mProc->mGlobalsChecked) + { + InterCodeProcedure* proc = ins->mSrc[0].mLinkerObject->mProc; + + int j = 0, k = 0; + while (j < mLoadStoreInstructions.Size()) + { + + int opmask = 0; + + InterInstruction* lins = mLoadStoreInstructions[j]; + + if (lins->mCode == IC_LOAD) + opmask = 1; + else if (lins->mCode == IC_STORE || lins->mCode == IC_FILL) + opmask = 2; + else if (lins->mCode == IC_COPY) + opmask = 3; + + bool flush = false; + for(int k=0; kmNumOperands; k++) + { + if ((1 << k) & opmask) + { + InterOperand& op(lins->mSrc[k]); + + if (op.mTemp >= 0) + { + if (op.mMemoryBase == IM_GLOBAL) + flush = proc->ModifiesGlobal(op.mVarIndex); + else if (op.mMemoryBase == IM_LOCAL && !mProc->mLocalVars[op.mVarIndex]->mAliased) + flush = false; + else if ((op.mMemoryBase == IM_PARAM || op.mMemoryBase == IM_FPARAM) && !mProc->mParamVars[op.mVarIndex]->mAliased) + flush = false; + else + flush = proc->mStoresIndirect; + } + else if (op.mMemory == IM_FFRAME || op.mMemory == IM_FRAME) + flush = true; + else if (op.mMemory == IM_GLOBAL) + flush = proc->ModifiesGlobal(op.mVarIndex); + else if (op.mMemory == IM_LOCAL && !mProc->mLocalVars[op.mVarIndex]->mAliased) + flush = false; + else if ((op.mMemory == IM_PARAM || op.mMemory == IM_FPARAM) && !mProc->mParamVars[op.mVarIndex]->mAliased) + flush = false; + else + flush = true; + } + } + + if (!flush) + mLoadStoreInstructions[k++] = mLoadStoreInstructions[j]; + j++; + } + mLoadStoreInstructions.SetSize(k); + } + else if (HasSideEffect(ins->mCode)) + flushMem = true; + + { + int j = 0, k = 0, t = ins->mDst.mTemp; + + while (j < mLoadStoreInstructions.Size()) + { + if (flushMem && (mLoadStoreInstructions[j]->mCode == IC_LOAD || mLoadStoreInstructions[j]->mCode == IC_STORE || mLoadStoreInstructions[j]->mCode == IC_COPY || mLoadStoreInstructions[j]->mCode == IC_FILL)) + ; + else if (t < 0) + mLoadStoreInstructions[k++] = mLoadStoreInstructions[j]; + else if (t != mLoadStoreInstructions[j]->mDst.mTemp) + { + int l = 0; + while (l < mLoadStoreInstructions[j]->mNumOperands && t != mLoadStoreInstructions[j]->mSrc[l].mTemp) + l++; + if (l == mLoadStoreInstructions[j]->mNumOperands) + mLoadStoreInstructions[k++] = mLoadStoreInstructions[j]; + } + j++; + } + mLoadStoreInstructions.SetSize(k); + + if (nins && t >= 0) + { + // Check self destruction of source operand + int l = 0; + while (l < nins->mNumOperands && t != nins->mSrc[l].mTemp) + l++; + if (l != nins->mNumOperands) + nins = nullptr; + } + } + + if (nins) + mLoadStoreInstructions.Push(nins); + } + +#if 1 + int sz = mInstructions.Size() - 1; + if (sz > 2 && mInstructions[sz]->mCode == IC_BRANCH && mInstructions[sz - 1]->mCode == IC_RELATIONAL_OPERATOR) + sz--; + + // move loads down as far as possible to avoid false aliasing + for (int i = mInstructions.Size() - 2; i >= 0; i--) + { + InterInstruction* ins(mInstructions[i]); + if (ins->mCode == IC_LOAD) + { + int j = i; + while (j + 1 < sz && CanSwapInstructions(ins, mInstructions[j + 1])) + { + SwapInstructions(ins, mInstructions[j + 1]); + mInstructions[j] = mInstructions[j + 1]; + j++; + } + if (i != j) + mInstructions[j] = ins; + } + } +#endif + if (mTrueJump && mTrueJump->LoadStoreForwarding(mLoadStoreInstructions, staticVars)) + changed = true; + if (mFalseJump && mFalseJump->LoadStoreForwarding(mLoadStoreInstructions, staticVars)) + changed = true; + } + + return changed; +} + + +void InterCodeBasicBlock::LocalRenameRegister(const GrowingIntArray& renameTable, int& num) +{ + int i; + + if (!mVisited) + { + mVisited = true; + + mEntryRenameTable.SetSize(renameTable.Size()); + mExitRenameTable.SetSize(renameTable.Size()); + + for (i = 0; i < renameTable.Size(); i++) + { + if (mEntryRequiredTemps[i]) + { + if (renameTable[i] < 0) + { + mExitRenameTable[i] = mEntryRenameTable[i] = num++; + } + else + { + mEntryRenameTable[i] = renameTable[i]; + mExitRenameTable[i] = renameTable[i]; + } + } + else + { + mEntryRenameTable[i] = -1; + mExitRenameTable[i] = -1; + } + } + + RenameValueRanges(mEntryRenameTable, num); + + for (i = 0; i < mInstructions.Size(); i++) + { + mInstructions[i]->LocalRenameRegister(mExitRenameTable, num); + } + + if (mTrueJump) mTrueJump->LocalRenameRegister(mExitRenameTable, num); + if (mFalseJump) mFalseJump->LocalRenameRegister(mExitRenameTable, num); + } +} + +void InterCodeBasicBlock::BuildGlobalRenameRegisterTable(const GrowingIntArray& renameTable, GrowingIntArray& globalRenameTable) +{ + int i; + + for (i = 0; i < renameTable.Size(); i++) + { + if (renameTable[i] >= 0 && mEntryRenameTable[i] >= 0 && renameTable[i] != mEntryRenameTable[i]) + { + Union(globalRenameTable, renameTable[i], mEntryRenameTable[i]); + } + } + + if (!mVisited) + { + mVisited = true; + + if (mTrueJump) mTrueJump->BuildGlobalRenameRegisterTable(mExitRenameTable, globalRenameTable); + if (mFalseJump) mFalseJump->BuildGlobalRenameRegisterTable(mExitRenameTable, globalRenameTable); + } +} + +void InterCodeBasicBlock::GlobalRenameRegister(const GrowingIntArray& renameTable, GrowingTypeArray& temporaries) +{ + int i; + + if (!mVisited) + { + mVisited = true; + + for (i = 0; i < mInstructions.Size(); i++) + { + mInstructions[i]->GlobalRenameRegister(renameTable, temporaries); + } + + RenameValueRanges(renameTable, temporaries.Size()); + + if (mTrueJump) mTrueJump->GlobalRenameRegister(renameTable, temporaries); + if (mFalseJump) mFalseJump->GlobalRenameRegister(renameTable, temporaries); + } +} + +void InterCodeBasicBlock::RenameValueRanges(const GrowingIntArray& renameTable, int numTemps) +{ + if (mEntryValueRange.Size() > 0) + { + mProc->mLocalValueRange = mEntryValueRange; + GrowingArray memoryValueSize(mMemoryValueSize); + + mEntryValueRange.SetSize(numTemps, true); + mMemoryValueSize.SetSize(numTemps, true); + for (int i = 0; i < mProc->mLocalValueRange.Size(); i++) + { + if (renameTable[i] >= 0) + { + assert(mProc->mLocalValueRange[i].mMinState == IntegerValueRange::S_UNKNOWN || mEntryValueRange[renameTable[i]].mMinState == IntegerValueRange::S_UNKNOWN); + assert(mProc->mLocalValueRange[i].mMaxState == IntegerValueRange::S_UNKNOWN || mEntryValueRange[renameTable[i]].mMaxState == IntegerValueRange::S_UNKNOWN); + + mEntryValueRange[renameTable[i]].Limit(mProc->mLocalValueRange[i]); + mMemoryValueSize[renameTable[i]] = int64min(mMemoryValueSize[renameTable[i]], memoryValueSize[i]); + } + } + } +} + +void InterCodeBasicBlock::BuildFriendsTable(GrowingIntArray& friendsMap) +{ + if (!mVisited) + { + mVisited = true; + + for (int i = 0; i < mInstructions.Size(); i++) + { + InterInstruction* ins = mInstructions[i]; + int di = ins->mDst.mTemp, si = -1; + + if (di >= 0) + { + if (ins->mCode == IC_LOAD_TEMPORARY) + si = ins->mSrc[0].mTemp; + else if (ins->mCode == IC_LEA && ins->mSrc[1].mFinal) + si = ins->mSrc[1].mTemp; + else if (ins->mCode == IC_UNARY_OPERATOR && ins->mSrc[0].mFinal) + si = ins->mSrc[0].mTemp; + else if (ins->mCode == IC_BINARY_OPERATOR) + { + if (ins->mSrc[0].mTemp < 0 && ins->mSrc[1].mFinal) + si = ins->mSrc[1].mTemp; + else if (ins->mSrc[1].mTemp < 0 && ins->mSrc[0].mFinal) + si = ins->mSrc[0].mTemp; + } + + if (si != di) + { + if (si >= 0 && friendsMap[di] == -1) + friendsMap[di] = si; + else + friendsMap[di] = -2; + } + } + } + + if (mTrueJump) mTrueJump->BuildFriendsTable(friendsMap); + if (mFalseJump) mFalseJump->BuildFriendsTable(friendsMap); + } +} + +void InterCodeBasicBlock::BuildCollisionTable(NumberSet* collisionSets) +{ + if (!mVisited) + { + mVisited = true; + + NumberSet requiredTemps(mExitRequiredTemps); + int i, j; + + for (i = 0; i < mExitRequiredTemps.Size(); i++) + { + if (mExitRequiredTemps[i]) + { + for (j = 0; j < mExitRequiredTemps.Size(); j++) + { + if (mExitRequiredTemps[j]) + { + collisionSets[i] += j; + } + } + } + } + + for (i = mInstructions.Size() - 1; i >= 0; i--) + { + mInstructions[i]->BuildCollisionTable(requiredTemps, collisionSets); + } + + if (mTrueJump) mTrueJump->BuildCollisionTable(collisionSets); + if (mFalseJump) mFalseJump->BuildCollisionTable(collisionSets); + } +} + +void InterCodeBasicBlock::ReduceTemporaries(const GrowingIntArray& renameTable, GrowingTypeArray& temporaries) +{ + int i; + + if (!mVisited) + { + mVisited = true; + + for (i = 0; i < mInstructions.Size(); i++) + { + mInstructions[i]->ReduceTemporaries(renameTable, temporaries); + } + + if (mTrueJump) mTrueJump->ReduceTemporaries(renameTable, temporaries); + if (mFalseJump) mFalseJump->ReduceTemporaries(renameTable, temporaries); + } +} + +void InterCodeBasicBlock::MapVariables(GrowingVariableArray& globalVars, GrowingVariableArray& localVars) +{ + int i; + + if (!mVisited) + { + mVisited = true; + + for (i = 0; i < mInstructions.Size(); i++) + { + bool found = false; + + switch (mInstructions[i]->mCode) + { + case IC_CONSTANT: + if (mInstructions[i]->mDst.mType == IT_POINTER) + { + if (mInstructions[i]->mConst.mMemory == IM_LOCAL) + { + localVars[mInstructions[i]->mConst.mVarIndex]->mUsed = true; + } + } + break; + + case IC_STORE: + case IC_LEA: + case IC_FILL: + if (mInstructions[i]->mSrc[1].mTemp < 0 && mInstructions[i]->mSrc[1].mMemory == IM_LOCAL) + { + localVars[mInstructions[i]->mSrc[1].mVarIndex]->mUsed = true; + } + break; + + case IC_LOAD: + case IC_CALL_NATIVE: + if (mInstructions[i]->mSrc[0].mTemp < 0 && mInstructions[i]->mSrc[0].mMemory == IM_LOCAL) + { + localVars[mInstructions[i]->mSrc[0].mVarIndex]->mUsed = true; + } + break; + + case IC_COPY: + case IC_STRCPY: + if (mInstructions[i]->mSrc[0].mTemp < 0 && mInstructions[i]->mSrc[0].mMemory == IM_LOCAL) + { + localVars[mInstructions[i]->mSrc[0].mVarIndex]->mUsed = true; + } + if (mInstructions[i]->mSrc[1].mTemp < 0 && mInstructions[i]->mSrc[1].mMemory == IM_LOCAL) + { + localVars[mInstructions[i]->mSrc[1].mVarIndex]->mUsed = true; + } + break; + + } + } + + if (mTrueJump) mTrueJump->MapVariables(globalVars, localVars); + if (mFalseJump) mFalseJump->MapVariables(globalVars, localVars); + } +} + +void InterCodeBasicBlock::MarkRelevantStatics(void) +{ + int i; + + if (!mVisited) + { + mVisited = true; + + for (i = 0; i < mInstructions.Size(); i++) + { + const InterInstruction* ins(mInstructions[i]); + if (ins->mCode == IC_LOAD) + { + if (ins->mSrc[0].mTemp < 0 && ins->mSrc[0].mMemory == IM_GLOBAL) + ins->mSrc[0].mLinkerObject->MarkRelevant(); + } + else if (ins->mCode == IC_LEA) + { + if (ins->mSrc[1].mTemp < 0 && ins->mSrc[1].mMemory == IM_GLOBAL) + ins->mSrc[1].mLinkerObject->MarkRelevant(); + } + else if (ins->mCode == IC_CONSTANT && ins->mDst.mType == IT_POINTER) + { + if (ins->mConst.mMemory == IM_GLOBAL) + ins->mConst.mLinkerObject->MarkRelevant(); + } + else if (ins->mCode == IC_COPY || ins->mCode == IC_STRCPY) + { + if (ins->mSrc[0].mTemp < 0 && ins->mSrc[0].mMemory == IM_GLOBAL) + ins->mSrc[0].mLinkerObject->MarkRelevant(); + if (ins->mSrc[1].mTemp < 0 && ins->mSrc[1].mMemory == IM_GLOBAL) + ins->mSrc[1].mLinkerObject->MarkRelevant(); + } + } + + if (mTrueJump) mTrueJump->MarkRelevantStatics(); + if (mFalseJump) mFalseJump->MarkRelevantStatics(); + } +} + +bool InterCodeBasicBlock::IsInsModified(const InterInstruction* ins) +{ + return IsInsModifiedInRange(0, mInstructions.Size(), ins); +} + +bool InterCodeBasicBlock::IsInsModifiedInRange(int from, int to, const InterInstruction* ins) +{ + if (ins->mDst.mTemp >= 0 && IsTempModifiedInRange(from, to, ins->mDst.mTemp)) + return true; + + for (int i = 0; i < ins->mNumOperands; i++) + { + if (ins->mSrc[i].mTemp >= 0 && IsTempModifiedInRange(from, to, ins->mSrc[i].mTemp)) + return true; + } + + return false; +} + +bool InterCodeBasicBlock::IsTempModified(int temp) +{ + return IsTempModifiedInRange(0, mInstructions.Size(), temp); +} + +bool InterCodeBasicBlock::IsTempModifiedInRange(int from, int to, int temp) +{ + for (int i = from; i < to; i++) + if (mInstructions[i]->mDst.mTemp == temp) + return true; + return false; +} + +bool InterCodeBasicBlock::IsTempUsedInRange(int from, int to, int temp) +{ + for (int i = from; i < to; i++) + { + InterInstruction* ins = mInstructions[i]; + for (int j = 0; j < ins->mNumOperands; j++) + if (ins->mSrc[j].mTemp == temp) + return true; + } + return false; +} + +bool InterCodeBasicBlock::IsTempReferenced(int temp) +{ + return IsTempReferencedInRange(0, mInstructions.Size(), temp); +} + +bool InterCodeBasicBlock::IsTempReferencedInRange(int from, int to, int temp) +{ + for (int i = from; i < to; i++) + { + InterInstruction* ins = mInstructions[i]; + if (ins->mDst.mTemp == temp) + return true; + for (int j = 0; j < ins->mNumOperands; j++) + if (ins->mSrc[j].mTemp == temp) + return true; + } + return false; +} + +InterInstruction* InterCodeBasicBlock::FindTempOrigin(int temp) const +{ + for (int i = mInstructions.Size() - 1; i >= 0; i--) + { + if (mInstructions[i]->mDst.mTemp == temp) + { + mMark = i; + return mInstructions[i]; + } + } + return nullptr; +} + +InterInstruction* InterCodeBasicBlock::FindTempOriginSinglePath(int temp) const +{ + for (int i = mInstructions.Size() - 1; i >= 0; i--) + { + if (mInstructions[i]->mDst.mTemp == temp) + { + mMark = i; + return mInstructions[i]; + } + } + if (mEntryBlocks.Size() == 1) + return mEntryBlocks[0]->FindTempOriginSinglePath(temp); + return nullptr; +} + +bool InterCodeBasicBlock::CanMoveInstructionDown(int si, int ti) const +{ + InterInstruction* ins = mInstructions[si]; + +#if 1 + if (ins->mCode == IC_COPY || ins->mCode == IC_PUSH_FRAME || ins->mCode == IC_POP_FRAME || ins->mCode == IC_FILL || + ins->mCode == IC_RETURN || ins->mCode == IC_RETURN_STRUCT || ins->mCode == IC_RETURN_VALUE || ins->mCode == IC_DISPATCH || + ins->mCode == IC_BREAKPOINT) + return false; + + for (int i = si + 1; i < ti; i++) + if (!CanSwapInstructions(ins, mInstructions[i])) + return false; + return true; + +#else + + if (ins->mCode == IC_LOAD) + { + for (int i = si + 1; i < ti; i++) + if (!CanBypassLoad(ins, mInstructions[i])) + return false; + } + else if (ins->mCode == IC_STORE) + { + for (int i = si + 1; i < ti; i++) + if (!CanBypassStore(ins, mInstructions[i])) + return false; + } + else if (ins->mCode == IC_COPY || ins->mCode == IC_PUSH_FRAME || ins->mCode == IC_POP_FRAME || + ins->mCode == IC_RETURN || ins->mCode == IC_RETURN_STRUCT || ins->mCode == IC_RETURN_VALUE) + return false; + else + { + for (int i = si + 1; i < ti; i++) + if (!CanBypass(ins, mInstructions[i])) + return false; + } + + return true; +#endif +} + +int InterCodeBasicBlock::FindSameInstruction(const InterInstruction* ins) const +{ + int i = mInstructions.Size() - 1; + while (i >= 0 && !mInstructions[i]->IsEqual(ins)) + i--; + return i; +} + +bool InterCodeBasicBlock::CanMoveInstructionBehindBlock(int ii) const +{ + if (CanMoveInstructionDown(ii, mInstructions.Size() - 1)) + { + InterInstruction* ins = mInstructions.Last(); + if (ins->mCode == IC_BRANCH && mInstructions[ii]->mDst.mTemp == ins->mSrc[0].mTemp) + return false; + return true; + } + else + return false; +} + +bool InterCodeBasicBlock::CanMoveInstructionBeforeBlock(int ii, const InterInstruction* ins) const +{ + +#if 1 + if (ins->mCode == IC_CALL || ins->mCode == IC_CALL_NATIVE || ins->mCode == IC_COPY || ins->mCode == IC_PUSH_FRAME || ins->mCode == IC_POP_FRAME || ins->mCode == IC_FILL || + ins->mCode == IC_RETURN || ins->mCode == IC_RETURN_STRUCT || ins->mCode == IC_RETURN_VALUE || ins->mCode == IC_DISPATCH || ins->mCode == IC_BREAKPOINT) + return false; + + for (int i = 0; i < ii; i++) + if (!CanSwapInstructions(mInstructions[i], ins)) + return false; + return true; +#else + if (ins->mCode == IC_LOAD) + { + for (int i = 0; i < ii; i++) + if (!CanBypassLoadUp(ins, mInstructions[i])) + return false; + } + else if (ins->mCode == IC_STORE) + { + for (int i = 0; i < ii; i++) + if (!CanBypassStore(ins, mInstructions[i])) + return false; + } + else if (ins->mCode == IC_CALL || ins->mCode == IC_CALL_NATIVE || ins->mCode == IC_COPY || ins->mCode == IC_PUSH_FRAME || ins->mCode == IC_POP_FRAME || + ins->mCode == IC_RETURN || ins->mCode == IC_RETURN_STRUCT || ins->mCode == IC_RETURN_VALUE) + return false; + else + { + for (int i = 0; i < ii; i++) + if (!CanBypassUp(ins, mInstructions[i])) + return false; + } +#endif + return true; +} + +bool InterCodeBasicBlock::CanMoveInstructionBeforeBlock(int ii) const +{ + return CanMoveInstructionBeforeBlock(ii, mInstructions[ii]); +} + +bool InterCodeBasicBlock::MergeCommonPathInstructions(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + if (mTrueJump && mFalseJump && mTrueJump->mNumEntries == 1 && mFalseJump->mNumEntries == 1 && mTrueJump->mInstructions.Size() && mFalseJump->mInstructions.Size()) + { + int ti = 0; + while (ti < mTrueJump->mInstructions.Size() && !changed) + { + InterInstruction* tins = mTrueJump->mInstructions[ti]; + InterInstruction* nins = (ti + 1 < mTrueJump->mInstructions.Size()) ? mTrueJump->mInstructions[ti + 1] : nullptr; + + if (tins->mCode != IC_BRANCH && tins->mCode != IC_JUMP && !(nins && nins->mCode == IC_BRANCH && tins->mDst.mTemp == nins->mSrc[0].mTemp)) + { + int fi = 0; + while (fi < mFalseJump->mInstructions.Size() && !(tins->mCode == mFalseJump->mInstructions[fi]->mCode && tins->mDst.mType == mFalseJump->mInstructions[fi]->mDst.mType && tins->IsEqualSource(mFalseJump->mInstructions[fi]))) + fi++; + + if (fi < mFalseJump->mInstructions.Size()) + { + InterInstruction* fins = mFalseJump->mInstructions[fi]; + + if ((tins->mDst.mTemp == -1 || !mFalseJump->mEntryRequiredTemps[tins->mDst.mTemp]) && + (fins->mDst.mTemp == -1 || !mTrueJump->mEntryRequiredTemps[fins->mDst.mTemp])) + { + if (mTrueJump->CanMoveInstructionBeforeBlock(ti) && mFalseJump->CanMoveInstructionBeforeBlock(fi)) + { + int tindex = mInstructions.Size() - 1; + if (mInstructions[tindex]->mCode != IC_BRANCH || tins->mDst.mTemp != mInstructions[tindex]->mSrc[0].mTemp) + { + if (mInstructions.Size() >= 2 && mInstructions[tindex - 1]->mDst.mTemp == mInstructions[tindex]->mSrc[0].mTemp && + CanSwapInstructions(mInstructions[tindex - 1], tins)) + // CanBypassUp(tins, mInstructions[tindex - 1])) + tindex--; + + mInstructions.Insert(tindex, tins); + tindex++; + + for (int i = 0; i < tins->mNumOperands; i++) + tins->mSrc[i].mRange.Union(fins->mSrc[i].mRange); + + if (tins->mDst.mTemp != -1) + { + tins->mDst.mRange.Union(fins->mDst.mRange); + + if (fins->mDst.mTemp != tins->mDst.mTemp) + { + InterInstruction* nins = new InterInstruction(tins->mLocation, IC_LOAD_TEMPORARY); + nins->mDst.mTemp = fins->mDst.mTemp; + nins->mDst.mType = fins->mDst.mType; + nins->mSrc[0].mTemp = tins->mDst.mTemp; + nins->mSrc[0].mType = tins->mDst.mType; + assert(nins->mSrc[0].mTemp >= 0); + mFalseJump->mInstructions.Insert(0, nins); + fi++; + } + } + mTrueJump->mInstructions.Remove(ti); + mFalseJump->mInstructions.Remove(fi); + changed = true; + } + } + } + } + } + + ti++; + } + } +#if 1 + if (mNumEntries > 1) + { + int i = 0; + while (i < mEntryBlocks.Size() && mEntryBlocks[i]->mInstructions.Size() > 1 && !mEntryBlocks[i]->mFalseJump) + i++; + if (i == mEntryBlocks.Size()) + { + InterCodeBasicBlock* eb = mEntryBlocks[0]; + + int ebi = eb->mInstructions.Size() - 2; + while (ebi >= 0) + { + InterInstruction* ins = eb->mInstructions[ebi]; + + if (ins && eb->CanMoveInstructionBehindBlock(ebi)) + { + int j = 1, eji = -1; + while (j < mEntryBlocks.Size() && (eji = mEntryBlocks[j]->FindSameInstruction(ins)) >= 0 && mEntryBlocks[j]->CanMoveInstructionBehindBlock(eji)) + j++; + + if (j == mEntryBlocks.Size()) + { + mInstructions.Insert(0, ins); + for (int j = 0; j < mEntryBlocks.Size(); j++) + mEntryBlocks[j]->mInstructions.Remove(mEntryBlocks[j]->FindSameInstruction(ins)); + changed = true; + } + } + + ebi--; + } + } + } +#endif + if (mTrueJump && mTrueJump->MergeCommonPathInstructions()) + changed = true; + if (mFalseJump && mFalseJump->MergeCommonPathInstructions()) + changed = true; + } + + return changed; +} + +static void CollectDominatorPath(InterCodeBasicBlock* block, InterCodeBasicBlock* dom, GrowingInterCodeBasicBlockPtrArray& blocks) +{ + if (blocks.IndexOf(block) != -1) + return; + if (block != dom) + { + blocks.Push(block); + for (int i = 0; i < block->mEntryBlocks.Size(); i++) + CollectDominatorPath(block->mEntryBlocks[i], dom, blocks); + } +} + +static bool CanMoveInstructionBeforePath(const GrowingInterCodeBasicBlockPtrArray& blocks, const InterInstruction* ins) +{ + for (int i = 0; i < blocks.Size(); i++) + if (!blocks[i]->CanMoveInstructionBeforeBlock(blocks[i]->mInstructions.Size(), ins)) + return false; + return true; +} + +bool InterCodeBasicBlock::IsDirectLoopPathBlock(InterCodeBasicBlock* block) +{ + if (this == block) + return true; + + if (block->mLoopHead) + return false; + + if (block->mEntryBlocks.Size() == 0) + return false; + + for (int i = 0; i < block->mEntryBlocks.Size(); i++) + if (IsDirectLoopPathBlock(block->mEntryBlocks[i])) + return true; + + return false; +} + +bool InterCodeBasicBlock::IsDirectDominatorBlock(InterCodeBasicBlock* block) +{ + if (block->mPatched) + return true; + + if (this == block) + return true; + + if (block->mLoopHead) + return false; + + if (block->mEntryBlocks.Size() == 0) + return false; + + for (int i = 0; i < block->mEntryBlocks.Size(); i++) + if (!IsDirectDominatorBlock(block->mEntryBlocks[i])) + return false; + + block->mPatched = true; + return true; +} + +bool InterCodeBasicBlock::CollectBlocksToDominator(InterCodeBasicBlock* dblock, ExpandingArray& body) +{ + if (this == dblock) + return true; + + if (mLoopHead) + return false; + + if (mEntryBlocks.Size() == 0) + return false; + + body.Push(this); + + for (int i = 0; i < mEntryBlocks.Size(); i++) + if (!mEntryBlocks[i]->CollectBlocksToDominator(dblock, body)) + return false; + + return true; +} + +bool InterCodeBasicBlock::HoistCommonConditionalPath(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + if (mFalseJump) + { + InterCodeBasicBlock* cblock = nullptr, * eblock = nullptr; + + mProc->ResetPatched(); + if (!mTrueJump->mFalseJump && mTrueJump->mTrueJump && IsDirectDominatorBlock(mTrueJump->mTrueJump)) + { + cblock = mTrueJump; + eblock = mTrueJump->mTrueJump; + } + else + { + mProc->ResetPatched(); + if (!mFalseJump->mFalseJump && mFalseJump->mTrueJump && IsDirectDominatorBlock(mFalseJump->mTrueJump)) + { + cblock = mFalseJump; + eblock = mFalseJump->mTrueJump; + } + } + + if (cblock && cblock->mNumEntries == 1) + { + ExpandingArray pblocks; + eblock->CollectBlocksToDominator(this, pblocks); + pblocks.RemoveAll(cblock); + pblocks.RemoveAll(eblock); + + for (int i = 0; i < cblock->mInstructions.Size(); i++) + { + InterInstruction* ins = cblock->mInstructions[i]; + + if (cblock->CanMoveInstructionBeforeBlock(i) && !HasSideEffect(ins->mCode) && ins->mDst.mTemp >= 0 && + !cblock->IsTempModifiedInRange(i + 1, cblock->mInstructions.Size(), ins->mDst.mTemp) && + !cblock->mExitRequiredTemps[ins->mDst.mTemp]) + { + int j = 0; + while (j < eblock->mInstructions.Size() && !eblock->mInstructions[j]->IsEqualSource(ins)) + j++; + + if (j < eblock->mInstructions.Size() && !eblock->IsTempModifiedInRange(0, j, ins->mDst.mTemp) && eblock->CanMoveInstructionBeforeBlock(j) && cblock->CanMoveInstructionBeforeBlock(cblock->mInstructions.Size(), eblock->mInstructions[j])) + { + int k = 0; + while (k < pblocks.Size() && !pblocks[k]->IsTempModified(ins->mDst.mTemp)) + k++; + if (k == pblocks.Size()) + { + eblock->mInstructions[j]->mCode = IC_LOAD_TEMPORARY; + eblock->mInstructions[j]->mSrc[0] = ins->mDst; + eblock->mInstructions[j]->mNumOperands = 1; + + mInstructions.Insert(mInstructions.Size() - 1, ins); + cblock->mInstructions.Remove(i); + + mExitRequiredTemps += ins->mDst.mTemp; + cblock->mEntryRequiredTemps += ins->mDst.mTemp; + cblock->mExitRequiredTemps += ins->mDst.mTemp; + eblock->mEntryRequiredTemps += ins->mDst.mTemp; + i--; + changed = true; + } + } + } + } + } + } + + if (mTrueJump && mTrueJump->HoistCommonConditionalPath()) + changed = true; + if (mFalseJump && mFalseJump->HoistCommonConditionalPath()) + changed = true; + } + + return changed; +} + + + +bool InterCodeBasicBlock::MoveTrainCrossBlock(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + if (mDominator) + { + GrowingInterCodeBasicBlockPtrArray path(nullptr); + + CollectDominatorPath(this, mDominator, path); + + if ((!mDominator->mTrueJump || path.IndexOf(mDominator->mTrueJump) != -1) && + (!mDominator->mFalseJump || path.IndexOf(mDominator->mFalseJump) != -1) && + (!mTrueJump || path.IndexOf(mTrueJump) == -1) && + (!mFalseJump || path.IndexOf(mFalseJump) == -1)) + { + int i = 1; + while (i < path.Size() && + (!path[i]->mTrueJump || path.IndexOf(path[i]->mTrueJump) != -1) && + (!path[i]->mFalseJump || path.IndexOf(path[i]->mFalseJump) != -1)) + i++; + if (i == path.Size()) + { + path.Remove(0); + + int i = 0; + while (i < mInstructions.Size()) + { + FastNumberSet nset(mEntryRequiredTemps.Size()); + + InterInstruction* ins(mInstructions[i]); + if (ins->mCode == IC_STORE && ins->mSrc[0].mFinal) + { + for (int k = 0; k < ins->mNumOperands; k++) + { + if (ins->mSrc[k].mTemp >= 0) + nset += ins->mSrc[k].mTemp; + } + + int j = i; + while (j > 0 && mInstructions[j - 1]->mDst.mTemp >= 0 && nset[mInstructions[j - 1]->mDst.mTemp]) + { + j--; + InterInstruction* nins(mInstructions[j]); + + for (int k = 0; k < nins->mNumOperands; k++) + { + if (nins->mSrc[k].mTemp >= 0) + nset += nins->mSrc[k].mTemp; + } + } + + int k = j; + while (k <= i && CanMoveInstructionBeforeBlock(j, mInstructions[k]) && CanMoveInstructionBeforePath(path, mInstructions[k])) + k++; + + if (k > i) + { + for (int k = j; k <= i; k++) + mDominator->mInstructions.Insert(mDominator->mInstructions.Size() - 1, mInstructions[k]); + mInstructions.Remove(j, i - j + 1); + i = j - 1; + changed = true; + } + } + + i++; + } + } + } + } + + if (mTrueJump && mTrueJump->MoveTrainCrossBlock()) + changed = true; + if (mFalseJump && mFalseJump->MoveTrainCrossBlock()) + changed = true; + } + + return changed; +} + +bool InterCodeBasicBlock::ForwardLoopMovedTemp(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + if (mTrueJump && !mFalseJump && mTrueJump->mLoopHead && mTrueJump->mNumEntries == 2) + { + InterCodeBasicBlock* eblock = nullptr; + if (mTrueJump->mTrueJump == mTrueJump) + eblock = mTrueJump->mFalseJump; + else if (mTrueJump->mFalseJump == mTrueJump) + eblock = mTrueJump->mTrueJump; + + if (eblock && eblock->mNumEntries == 1) + { + int i = mInstructions.Size() - 1; + while (i >= 0) + { + if (mInstructions[i]->mCode == IC_LOAD_TEMPORARY && CanMoveInstructionBehindBlock(i) && + !mTrueJump->mLocalUsedTemps[mInstructions[i]->mDst.mTemp] && + !mTrueJump->mLocalModifiedTemps[mInstructions[i]->mSrc[0].mTemp]) + { + eblock->mInstructions.Insert(0, mInstructions[i]); + mInstructions.Remove(i); + changed = true; + } + else + i--; + } + } + } + + if (mTrueJump && mTrueJump->ForwardLoopMovedTemp()) + changed = true; + if (mFalseJump && mFalseJump->ForwardLoopMovedTemp()) + changed = true; + } + + return changed; +} + +void InterCodeBasicBlock::UnionEntryValueRange(const GrowingIntegerValueRangeArray&range, const GrowingIntegerValueRangeArray& paramRange) +{ + int ni = intmin(mEntryValueRange.Size(), range.Size()); + for (int i = 0; i < ni; i++) + mEntryValueRange[i].Union(range[i]); + ni = intmin(mEntryParamValueRange.Size(), paramRange.Size()); + for (int i = 0; i < ni; i++) + mEntryParamValueRange[i].Union(paramRange[i]); +} + +bool InterCodeBasicBlock::ForwardRealDiamondMovedTemp(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + if (mTrueJump && mFalseJump && mTrueJump->mTrueJump && + mTrueJump->mNumEntries == 1 && !mTrueJump->mFalseJump && + mFalseJump->mNumEntries == 1 && !mFalseJump->mFalseJump && + mTrueJump->mTrueJump == mFalseJump->mTrueJump && mTrueJump->mTrueJump->mNumEntries == 2) + { + InterCodeBasicBlock* eblock = mTrueJump->mTrueJump; + + for (int i = mInstructions.Size() - 1; i >= 0; i--) + { + InterInstruction* mins = mInstructions[i]; + + if ((mins->mCode == IC_BINARY_OPERATOR || mins->mCode == IC_LEA) && (mins->mSrc[1].mTemp < 0 || mins->mSrc[0].mTemp < 0) || mins->mCode == IC_UNARY_OPERATOR || mins->mCode == IC_CONVERSION_OPERATOR || mins->mCode == IC_LOAD_TEMPORARY) + { + int ttemp = mins->mDst.mTemp; + int stemp = ((mins->mCode == IC_BINARY_OPERATOR || mins->mCode == IC_LEA) && mins->mSrc[0].mTemp < 0) ? mins->mSrc[1].mTemp : mins->mSrc[0].mTemp; + + if (!mTrueJump->mLocalRequiredTemps[ttemp] && !mTrueJump->mLocalModifiedTemps[stemp] && !mTrueJump->mLocalModifiedTemps[ttemp] && + !mFalseJump->mLocalRequiredTemps[ttemp] && !mFalseJump->mLocalModifiedTemps[stemp] && !mFalseJump->mLocalModifiedTemps[ttemp] && + !IsTempReferencedOnPath(ttemp, i + 1) && !IsTempModifiedOnPath(stemp, i + 1)) + { + mTrueJump->mEntryRequiredTemps += stemp; + mTrueJump->mExitRequiredTemps += stemp; + + mFalseJump->mEntryRequiredTemps += stemp; + mFalseJump->mExitRequiredTemps += stemp; + + eblock->mEntryRequiredTemps += stemp; + mExitRequiredTemps += stemp; + + eblock->mInstructions.Insert(0, mins); + mInstructions.Remove(i); + + changed = true; + } + } + else if (mins->mCode == IC_LOAD) + { + int ttemp = mins->mDst.mTemp; + int stemp = mins->mSrc[0].mTemp; + + if (!mTrueJump->mLocalRequiredTemps[ttemp] && (stemp < 0 || !mTrueJump->mLocalModifiedTemps[stemp]) && !mTrueJump->mLocalModifiedTemps[ttemp] && + !mFalseJump->mLocalRequiredTemps[ttemp] && (stemp < 0 || !mFalseJump->mLocalModifiedTemps[stemp]) && !mFalseJump->mLocalModifiedTemps[ttemp] && + !IsTempReferencedOnPath(ttemp, i + 1) && (stemp < 0 || !IsTempModifiedOnPath(stemp, i + 1)) && + CanMoveInstructionBehindBlock(i) && + !DestroyingMem(mTrueJump, mins, 0, mTrueJump->mInstructions.Size()) && !DestroyingMem(mFalseJump, mins, 0, mFalseJump->mInstructions.Size())) + { + if (stemp >= 0) + { + mTrueJump->mEntryRequiredTemps += stemp; + mTrueJump->mExitRequiredTemps += stemp; + + mFalseJump->mEntryRequiredTemps += stemp; + mFalseJump->mExitRequiredTemps += stemp; + + eblock->mEntryRequiredTemps += stemp; + mExitRequiredTemps += stemp; + } + + eblock->mInstructions.Insert(0, mins); + mInstructions.Remove(i); + + changed = true; + } + } + } + } + + if (mTrueJump && mTrueJump->ForwardRealDiamondMovedTemp()) + changed = true; + if (mFalseJump && mFalseJump->ForwardRealDiamondMovedTemp()) + changed = true; + } + + return changed; +} + +bool InterCodeBasicBlock::ForwardDiamondMovedTemp(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + if (mTrueJump && mTrueJump == mFalseJump) + { + mFalseJump = nullptr; + mInstructions[mInstructions.Size() - 1]->mCode = IC_JUMP; + mInstructions[mInstructions.Size() - 1]->mNumOperands = 0; + mTrueJump->mNumEntries--; + changed = true; + } + + if (mTrueJump && mFalseJump) + { + InterCodeBasicBlock* tblock = nullptr, * fblock = nullptr, * sblock = nullptr; + + if (mTrueJump != mFalseJump && mTrueJump->mFalseJump && mTrueJump->mTrueJump == mFalseJump->mTrueJump && mTrueJump->mFalseJump == mFalseJump->mFalseJump) + { + if (mTrueJump->mInstructions.Size() == 1) + { + tblock = mTrueJump; + fblock = mFalseJump; + } + else if (mFalseJump->mInstructions.Size() == 1) + { + fblock = mTrueJump; + tblock = mFalseJump; + } + + if (fblock && tblock) + { + if (tblock->mInstructions[0]->IsEqual(fblock->mInstructions.Last()) && (tblock->mInstructions[0]->mSrc[0].mTemp < 0 || !fblock->mLocalModifiedTemps[tblock->mInstructions[0]->mSrc[0].mTemp])) + { + tblock->UnionEntryValueRange(fblock->mTrueValueRange, fblock->mTrueParamValueRange); + tblock->UnionEntryValueRange(fblock->mFalseValueRange, fblock->mFalseParamValueRange); + + fblock->mTrueJump = tblock; + fblock->mFalseJump = nullptr; + fblock->mInstructions[fblock->mInstructions.Size() - 1]->mCode = IC_JUMP; + fblock->mInstructions[fblock->mInstructions.Size() - 1]->mNumOperands = 0; + tblock->mNumEntries++; + tblock->mFalseJump->mNumEntries--; + tblock->mTrueJump->mNumEntries--; + changed = true; + } + } + + fblock = nullptr; + tblock = nullptr; + } + + if (!mTrueJump->mFalseJump && mTrueJump->mTrueJump == mFalseJump) + { + tblock = mTrueJump; + fblock = mFalseJump; + } + else if (!mFalseJump->mFalseJump && mFalseJump->mTrueJump == mTrueJump) + { + fblock = mTrueJump; + tblock = mFalseJump; + } + + if (fblock && tblock) + { + if (tblock->mNumEntries == 1 && fblock->mNumEntries == 2) + { + if (tblock->mInstructions.Size() == 2 && tblock->mInstructions[0]->mCode == IC_LEA && tblock->mInstructions[0]->mSrc[0].mTemp < 0 && tblock->mInstructions[0]->mSrc[1].mTemp >= 0) + { + InterInstruction* lins = tblock->mInstructions[0]; + + // Single "lea temp, imm" in diamond + int i = mInstructions.Size(); + while (i > 0 && mInstructions[i - 1]->mDst.mTemp != lins->mSrc[1].mTemp) + i--; + + if (i > 0) + { + i--; + InterInstruction* tins = mInstructions[i]; + + if (mInstructions[i]->mCode == IC_LOAD_TEMPORARY) + { + int offset = 0; + bool fail = false; + for (int j = i + 1; j < mInstructions.Size(); j++) + { + if (mInstructions[j]->mDst.mTemp == tins->mSrc[0].mTemp) + { + if (mInstructions[j]->mCode == IC_LEA && mInstructions[j]->mSrc[1].mTemp == tins->mSrc[0].mTemp && mInstructions[j]->mSrc[0].mTemp < 0) + { + offset += int(mInstructions[j]->mSrc[0].mIntConst); + } + else + fail = true; + } + } + + if (!fail) + { + mExitRequiredTemps += tins->mSrc[0].mTemp; + tblock->mEntryRequiredTemps += tins->mSrc[0].mTemp; + + lins->mSrc[1].mTemp = tins->mSrc[0].mTemp; + lins->mSrc[0].mIntConst -= offset; + changed = true; + } + } + + } + } + + for (int i = mInstructions.Size() - 1; i >= 0; i--) + { + InterInstruction* mins = mInstructions[i]; + + if (mins->mCode == IC_LOAD_TEMPORARY) + { + int ttemp = mins->mDst.mTemp; + int stemp = mins->mSrc[0].mTemp; + + if (!IsTempModifiedOnPath(ttemp, i + 1) && !IsTempModifiedOnPath(stemp, i + 1) && !tblock->mExitRequiredTemps[stemp]) + { + int j = 0; + while (j < tblock->mInstructions.Size() && + tblock->mInstructions[j]->mDst.mTemp != ttemp && + tblock->mInstructions[j]->mDst.mTemp != stemp) + { + j++; + } + + if (j < tblock->mInstructions.Size() && tblock->mInstructions[j]->mDst.mTemp == ttemp) + { + if (!tblock->IsTempModifiedOnPath(stemp, j + 1)) + { + tblock->mInstructions[j]->mDst.mTemp = stemp; + + InterInstruction* nins = new InterInstruction(mins->mLocation, IC_LOAD_TEMPORARY); + nins->mDst.mTemp = ttemp; + nins->mDst.mType = mins->mDst.mType; + nins->mSrc[0].mTemp = stemp; + nins->mSrc[0].mType = mins->mDst.mType; + assert(nins->mSrc[0].mTemp >= 0); + fblock->mInstructions.Insert(0, nins); + + tblock->mExitRequiredTemps += stemp; + + changed = true; + } + } + } + } +#if 1 + else if ((mins->mCode == IC_BINARY_OPERATOR || mins->mCode == IC_LEA) && (mins->mSrc[1].mTemp < 0 || mins->mSrc[0].mTemp < 0) || mins->mCode == IC_UNARY_OPERATOR || mins->mCode == IC_CONVERSION_OPERATOR) + { + int ttemp = mins->mDst.mTemp; + int stemp = ((mins->mCode == IC_BINARY_OPERATOR || mins->mCode == IC_LEA) && mins->mSrc[0].mTemp < 0) ? mins->mSrc[1].mTemp : mins->mSrc[0].mTemp; + + if (!tblock->mLocalRequiredTemps[ttemp] && !tblock->mLocalModifiedTemps[stemp] && !tblock->mLocalModifiedTemps[ttemp] && !IsTempReferencedOnPath(ttemp, i + 1) && !IsTempModifiedOnPath(stemp, i + 1)) + { + tblock->mEntryRequiredTemps += stemp; + tblock->mExitRequiredTemps += stemp; + + fblock->mEntryRequiredTemps += stemp; + mExitRequiredTemps += stemp; + + fblock->mInstructions.Insert(0, mins); + mInstructions.Remove(i); + + changed = true; + } + } +#endif + else if (mins->mCode == IC_LOAD) + { + int ttemp = mins->mDst.mTemp; + int stemp = mins->mSrc[0].mTemp; + + if (!tblock->mLocalRequiredTemps[ttemp] && (stemp < 0 || !tblock->mLocalModifiedTemps[stemp]) && + !tblock->mLocalModifiedTemps[ttemp] && !IsTempReferencedOnPath(ttemp, i + 1) && (stemp < 0 || !IsTempModifiedOnPath(stemp, i + 1)) && + !DestroyingMem(tblock, mins, 0, tblock->mInstructions.Size()) && + CanMoveInstructionBehindBlock(i)) + { + if (stemp >= 0) + { + tblock->mEntryRequiredTemps += stemp; + tblock->mExitRequiredTemps += stemp; + + fblock->mEntryRequiredTemps += stemp; + mExitRequiredTemps += stemp; + } + + fblock->mInstructions.Insert(0, mins); + mInstructions.Remove(i); + + changed = true; + } + } + } + } + } + } + + if (mTrueJump && mTrueJump->ForwardDiamondMovedTemp()) + changed = true; + if (mFalseJump && mFalseJump->ForwardDiamondMovedTemp()) + changed = true; + } + + return changed; +} + +bool InterCodeBasicBlock::IsTempModifiedOnPath(int temp, int at) const +{ + while (at < mInstructions.Size()) + { + if (mInstructions[at]->mDst.mTemp == temp) + return true; + at++; + } + + return false; +} + +bool InterCodeBasicBlock::IsTempReferencedOnPath(int temp, int at) const +{ + while (at < mInstructions.Size()) + { + const InterInstruction * ins = mInstructions[at]; + + if (ins->mDst.mTemp == temp) + return true; + for (int i = 0; i < ins->mNumOperands; i++) + if (ins->mSrc[i].mTemp == temp) + return true; + + at++; + } + + return false; +} + +bool InterCodeBasicBlock::PushSinglePathResultInstructions(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + if (mTrueJump && mFalseJump) + { + InterCodeBasicBlock* joinedBlock = nullptr; + + + NumberSet trueExitRequiredTemps(mTrueJump->mEntryRequiredTemps), falseExitRequiredTems(mFalseJump->mEntryRequiredTemps); + NumberSet providedTemps(mExitRequiredTemps.Size()), requiredTemps(mExitRequiredTemps.Size()); + + if (mTrueJump->mTrueJump && mFalseJump->mTrueJump && !mTrueJump->mFalseJump && !mFalseJump->mFalseJump && + mTrueJump->mNumEntries == 1 && mFalseJump->mNumEntries == 1 && + mTrueJump->mTrueJump == mFalseJump->mTrueJump && mTrueJump->mTrueJump->mNumEntries == 2) + { + joinedBlock = mTrueJump->mTrueJump; + } + + bool hadStore = false; + + int i = mInstructions.Size(); + while (i > 0) + { + i--; + InterInstruction* ins(mInstructions[i]); + + int dtemp = ins->mDst.mTemp; + bool moved = false, pair = false; + + if (i > 0 && ins->mCode == IC_BINARY_OPERATOR && ins->mOperator == IA_DIVU && i > 0 && + mInstructions[i - 1]->mCode == IC_BINARY_OPERATOR && mInstructions[i - 1]->mOperator == IA_MODU && + ins->mSrc[0].IsEqual(mInstructions[i - 1]->mSrc[0]) && + ins->mSrc[1].IsEqual(mInstructions[i - 1]->mSrc[1])) + pair = true; + else if (i > 0 && ins->mCode == IC_BINARY_OPERATOR && ins->mOperator == IA_MODU && i > 0 && + mInstructions[i - 1]->mCode == IC_BINARY_OPERATOR && mInstructions[i - 1]->mOperator == IA_DIVU && + ins->mSrc[0].IsEqual(mInstructions[i - 1]->mSrc[0]) && + ins->mSrc[1].IsEqual(mInstructions[i - 1]->mSrc[1])) + pair = true; + else if (i + 1 < mInstructions.Size() && ins->mCode == IC_BINARY_OPERATOR && ins->mOperator == IA_DIVU && + mInstructions[i + 1]->mCode == IC_BINARY_OPERATOR && mInstructions[i + 1]->mOperator == IA_MODU && + ins->mSrc[0].IsEqual(mInstructions[i + 1]->mSrc[0]) && + ins->mSrc[1].IsEqual(mInstructions[i + 1]->mSrc[1])) + pair = true; + else if (i + 1 < mInstructions.Size() && ins->mCode == IC_BINARY_OPERATOR && ins->mOperator == IA_MODU && + mInstructions[i + 1]->mCode == IC_BINARY_OPERATOR && mInstructions[i + 1]->mOperator == IA_DIVU && + ins->mSrc[0].IsEqual(mInstructions[i + 1]->mSrc[0]) && + ins->mSrc[1].IsEqual(mInstructions[i + 1]->mSrc[1])) + pair = true; + + if (!pair && dtemp >= 0 && !providedTemps[dtemp] && !requiredTemps[dtemp]) + { + int j = 0; + while (j < ins->mNumOperands && (ins->mSrc[j].mTemp < 0 || !(providedTemps[ins->mSrc[j].mTemp] || IsTempModifiedOnPath(ins->mSrc[j].mTemp, i + 1)))) + j++; + + if (j == ins->mNumOperands && IsMoveable(ins->mCode) && CanMoveInstructionBehindBlock(i)) + { + if (mTrueJump->mNumEntries == 1 && trueExitRequiredTemps[dtemp] && !falseExitRequiredTems[dtemp]) + { + if (ins->mDst.mTemp >= 0 && mTrueJump->mEntryValueRange.Size() > ins->mDst.mTemp) + mTrueJump->mEntryValueRange[ins->mDst.mTemp].Reset(); + + for (int j = 0; j < ins->mNumOperands; j++) + { + if (ins->mSrc[j].mTemp >= 0) + { + trueExitRequiredTemps += ins->mSrc[j].mTemp; + mTrueJump->mEntryRequiredTemps += ins->mSrc[j].mTemp; + mTrueJump->mLocalUsedTemps += ins->mSrc[j].mTemp; + } + } + mTrueJump->mLocalModifiedTemps += dtemp; + mTrueJump->mInstructions.Insert(0, ins); + mInstructions.Remove(i); + moved = true; + changed = true; + } + else if (mFalseJump->mNumEntries == 1 && !trueExitRequiredTemps[dtemp] && falseExitRequiredTems[dtemp]) + { + if (ins->mDst.mTemp >= 0 && mFalseJump->mEntryValueRange.Size() > ins->mDst.mTemp) + mFalseJump->mEntryValueRange[ins->mDst.mTemp].Reset(); + + for (int j = 0; j < ins->mNumOperands; j++) + { + if (ins->mSrc[j].mTemp >= 0) + { + falseExitRequiredTems += ins->mSrc[j].mTemp; + mFalseJump->mEntryRequiredTemps += ins->mSrc[j].mTemp; + mFalseJump->mLocalUsedTemps += ins->mSrc[j].mTemp; + } + } + mFalseJump->mLocalModifiedTemps += dtemp; + mFalseJump->mInstructions.Insert(0, ins); + mInstructions.Remove(i); + moved = true; + changed = true; + } +#if 1 + else if (joinedBlock && !HasSideEffect(ins->mCode) && + !mFalseJump->mLocalUsedTemps[dtemp] && !mFalseJump->mLocalModifiedTemps[dtemp] && + !mTrueJump->mLocalUsedTemps[dtemp] && !mTrueJump->mLocalModifiedTemps[dtemp]) + { + int j = 0; + while (j < ins->mNumOperands && !(ins->mSrc[j].mTemp >= 0 && (mFalseJump->mLocalModifiedTemps[ins->mSrc[j].mTemp] || mTrueJump->mLocalModifiedTemps[ins->mSrc[j].mTemp]))) + j++; + + if (j == ins->mNumOperands) + { + if (ins->mCode == IC_LOAD) + { + j = 0; + while (j < mTrueJump->mInstructions.Size() && CanSwapInstructions(ins, mTrueJump->mInstructions[j])) + j++; + } + if (ins->mCode != IC_LOAD || j == mTrueJump->mInstructions.Size()) + { + if (ins->mCode == IC_LOAD) + { + j = 0; + while (j < mFalseJump->mInstructions.Size() && CanSwapInstructions(ins, mFalseJump->mInstructions[j])) + j++; + } + + if (ins->mCode != IC_LOAD || j == mFalseJump->mInstructions.Size()) + { + if (ins->mDst.mTemp >= 0 && joinedBlock->mEntryValueRange.Size() > ins->mDst.mTemp) + joinedBlock->mEntryValueRange[ins->mDst.mTemp].Reset(); + + for (int j = 0; j < ins->mNumOperands; j++) + { + if (ins->mSrc[j].mTemp >= 0) + { + trueExitRequiredTemps += ins->mSrc[j].mTemp; + falseExitRequiredTems += ins->mSrc[j].mTemp; + joinedBlock->mEntryRequiredTemps += ins->mSrc[j].mTemp; + } + } + + joinedBlock->mInstructions.Insert(0, ins); + mInstructions.Remove(i); + moved = true; + changed = true; + } + } + } + } +#endif + } + + providedTemps += ins->mDst.mTemp; + } + + if (!moved) + { + for (int j = 0; j < ins->mNumOperands; j++) + { + if (ins->mSrc[j].mTemp >= 0) + requiredTemps += ins->mSrc[j].mTemp; + } + } + + if (HasSideEffect(ins->mCode)) + hadStore = true; + + } + } + + if (mTrueJump && mTrueJump->PushSinglePathResultInstructions()) + changed = true; + if (mFalseJump && mFalseJump->PushSinglePathResultInstructions()) + changed = true; + } + + return changed; +} + +void InterCodeBasicBlock::RemoveNonRelevantStatics(void) +{ + int i; + + if (!mVisited) + { + mVisited = true; + + for (i = 0; i < mInstructions.Size(); i++) + { + InterInstruction* ins(mInstructions[i]); + if (ins->mCode == IC_STORE || ins->mCode == IC_COPY || ins->mCode == IC_FILL) + { + if (ins->mSrc[1].mTemp < 0 && ins->mSrc[1].mMemory == IM_GLOBAL && !ins->mVolatile) + { + if (!(ins->mSrc[1].mLinkerObject->mFlags & LOBJF_RELEVANT) && (ins->mSrc[1].mLinkerObject->mType == LOT_BSS || ins->mSrc[1].mLinkerObject->mType == LOT_DATA)) + { + ins->mSrc[0].mTemp = -1; + ins->mNumOperands = 0; + ins->mCode = IC_NONE; + } + } + } + } + + if (mTrueJump) mTrueJump->RemoveNonRelevantStatics(); + if (mFalseJump) mFalseJump->RemoveNonRelevantStatics(); + } +} + +bool InterCodeBasicBlock::RecheckOuterFrame(void) +{ + if (!mVisited) + { + mVisited = true; + + for (int i = 0; i < mInstructions.Size(); i++) + { + InterInstruction* ins(mInstructions[i]); + if (ins->mCode == IC_LOAD) + { + if (ins->mSrc[0].mMemory == IM_FRAME) + return true; + } + else if (ins->mCode == IC_STORE || ins->mCode == IC_LEA) + { + if (ins->mSrc[1].mMemory == IM_FRAME) + return true; + } + else if (ins->mCode == IC_COPY) + { + if (ins->mSrc[0].mMemory == IM_FRAME) + return true; + if (ins->mSrc[1].mMemory == IM_FRAME) + return true; + } + else if (ins->mCode == IC_CONSTANT) + { + if (ins->mConst.mType == IT_POINTER && ins->mConst.mMemory == IM_FRAME) + return true; + } + else if (ins->mCode == IC_PUSH_FRAME) + return true; + } + + if (mTrueJump && mTrueJump->RecheckOuterFrame()) + return true; + if (mFalseJump && mFalseJump->RecheckOuterFrame()) + return true; + } + + return false; +} + +void InterCodeBasicBlock::CollectParamVarsSize(int& size) +{ + if (!mVisited) + { + mVisited = true; + + for (int i = 0; i < mInstructions.Size(); i++) + { + InterInstruction* ins(mInstructions[i]); + if (ins->mCode == IC_LOAD) + { + if (ins->mSrc[0].mMemory == IM_PARAM) + { + if (ins->mSrc[0].mVarIndex + InterTypeSize[ins->mDst.mType] > size) + size = ins->mSrc[0].mVarIndex + InterTypeSize[ins->mDst.mType]; + } + } + else if (ins->mCode == IC_STORE) + { + if (ins->mSrc[1].mMemory == IM_PARAM) + { + if (ins->mSrc[1].mVarIndex + InterTypeSize[ins->mSrc[0].mType] > size) + size = ins->mSrc[1].mVarIndex + InterTypeSize[ins->mSrc[0].mType]; + } + } + else if (ins->mCode == IC_LEA) + { + if (ins->mSrc[1].mMemory == IM_PARAM) + { + if (ins->mSrc[1].mVarIndex + mProc->mParamVars[ins->mSrc[1].mVarIndex]->mSize > size) + size = ins->mSrc[1].mVarIndex + mProc->mParamVars[ins->mSrc[1].mVarIndex]->mSize; + } + } + else if (ins->mCode == IC_COPY) + { + if (ins->mSrc[0].mMemory == IM_PARAM) + { + if (ins->mSrc[0].mVarIndex + mProc->mParamVars[ins->mSrc[0].mVarIndex]->mSize > size) + size = ins->mSrc[0].mVarIndex + mProc->mParamVars[ins->mSrc[0].mVarIndex]->mSize; + } + if (ins->mSrc[1].mMemory == IM_PARAM) + { + if (ins->mSrc[1].mVarIndex + mProc->mParamVars[ins->mSrc[1].mVarIndex]->mSize > size) + size = ins->mSrc[1].mVarIndex + mProc->mParamVars[ins->mSrc[1].mVarIndex]->mSize; + } + } + else if (ins->mCode == IC_CONSTANT) + { + if (ins->mConst.mType == IT_POINTER && ins->mConst.mMemory == IM_PARAM) + { + if (ins->mConst.mVarIndex + mProc->mParamVars[ins->mConst.mVarIndex]->mSize > size) + size = ins->mConst.mVarIndex + mProc->mParamVars[ins->mConst.mVarIndex]->mSize; + + } + } + } + + if (mTrueJump) mTrueJump->CollectParamVarsSize(size); + if (mFalseJump) mFalseJump->CollectParamVarsSize(size); + } +} + +void InterCodeBasicBlock::CollectOuterFrame(int level, int& size, bool &inner, bool &inlineAssembler, bool &byteCodeCall) +{ + int i; + + if (!mVisited) + { + mVisited = true; + + for (i = 0; i < mInstructions.Size(); i++) + { + if (mInstructions[i]->mCode == IC_PUSH_FRAME) + { + level++; + if (level == 1) + { + if (mInstructions[i]->mConst.mIntConst > size) + size = int(mInstructions[i]->mConst.mIntConst); + mInstructions[i]->mCode = IC_NONE; + } + else + inner = true; + } + else if (mInstructions[i]->mCode == IC_POP_FRAME) + { + if (level == 1) + { + mInstructions[i]->mCode = IC_NONE; + } + level--; + } + else if (mInstructions[i]->mCode == IC_ASSEMBLER) + inlineAssembler = true; + else if (mInstructions[i]->mCode == IC_CALL) + byteCodeCall = true; + } + + if (mTrueJump) mTrueJump->CollectOuterFrame(level, size, inner, inlineAssembler, byteCodeCall); + if (mFalseJump) mFalseJump->CollectOuterFrame(level, size, inner, inlineAssembler, byteCodeCall); + } +} + +bool InterCodeBasicBlock::IsLeafProcedure(void) +{ + int i; + + if (!mVisited) + { + mVisited = true; + + for (i = 0; i < mInstructions.Size(); i++) + if (mInstructions[i]->mCode == IC_CALL || mInstructions[i]->mCode == IC_CALL_NATIVE || mInstructions[i]->mCode == IC_DISPATCH) + return false; + + if (mTrueJump && !mTrueJump->IsLeafProcedure()) + return false; + if (mFalseJump && !mFalseJump->IsLeafProcedure()) + return false; + } + + return true; +} + + +void InterCodeBasicBlock::ExpandSelect(void) +{ + if (!mVisited) + { + mVisited = true; + + int i = 0; + while (i < mInstructions.Size() && mInstructions[i]->mCode != IC_SELECT) + i++; + + if (i < mInstructions.Size()) + { + InterInstruction* sins = mInstructions[i]; + + InterCodeBasicBlock* tblock = new InterCodeBasicBlock(mProc); + InterCodeBasicBlock* fblock = new InterCodeBasicBlock(mProc); + InterCodeBasicBlock* eblock = new InterCodeBasicBlock(mProc); + + for (int j = i + 1; j < mInstructions.Size(); j++) + eblock->mInstructions.Push(mInstructions[j]); + eblock->Close(mTrueJump, mFalseJump); + + mInstructions.SetSize(i); + + InterInstruction* bins = new InterInstruction(sins->mLocation, IC_BRANCH); + bins->mSrc[0] = sins->mSrc[2]; + mInstructions.Push(bins); + + InterInstruction* tins; + if (sins->mSrc[1].mTemp < 0) + { + tins = new InterInstruction(sins->mLocation, IC_CONSTANT); + tins->mConst = sins->mSrc[1]; + } + else + { + tins = new InterInstruction(sins->mLocation, IC_LOAD_TEMPORARY); + tins->mSrc[0] = sins->mSrc[1]; + } + + tins->mDst = sins->mDst; + + InterInstruction* fins; + if (sins->mSrc[0].mTemp < 0) + { + fins = new InterInstruction(sins->mLocation, IC_CONSTANT); + fins->mConst = sins->mSrc[0]; + } + else + { + fins = new InterInstruction(sins->mLocation, IC_LOAD_TEMPORARY); + fins->mSrc[0] = sins->mSrc[0]; + } + + fins->mDst = sins->mDst; + + tblock->mInstructions.Push(tins); + InterInstruction* jins = new InterInstruction(sins->mLocation, IC_JUMP); + tblock->mInstructions.Push(jins); + + tblock->Close(eblock, nullptr); + + fblock->mInstructions.Push(fins); + jins = new InterInstruction(sins->mLocation, IC_JUMP); + fblock->mInstructions.Push(jins); + + fblock->Close(eblock, nullptr); + + mTrueJump = tblock; + mFalseJump = fblock; + } + + if (mTrueJump) + mTrueJump->ExpandSelect(); + if (mFalseJump) + mFalseJump->ExpandSelect(); + } +} + +void InterCodeBasicBlock::SplitBranches(void) +{ + if (!mVisited) + { + mVisited = true; + + int sz = mInstructions.Size(); + + int nsplit = 0; + + if (mTrueJump && mFalseJump && sz > 1) + { + if (mLoopHead && mNumEntries == 2 && (mTrueJump == this || mFalseJump == this) && sz < 5) + nsplit = 0; + else if (sz >= 2 && mInstructions[sz - 2]->mCode == IC_RELATIONAL_OPERATOR) + nsplit = 2; + else + nsplit = 1; + } + + if (nsplit > 0 && nsplit < sz) + { + InterCodeBasicBlock* block = new InterCodeBasicBlock(mProc); + InterInstruction* ins = mInstructions.Last(); + + for(int i=0; imInstructions.Push(mInstructions[sz - nsplit + i]); + mInstructions.SetSize(sz - nsplit); + + InterInstruction* jins = new InterInstruction(ins->mLocation, IC_JUMP); + mInstructions.Push(jins); + block->Close(mTrueJump, mFalseJump); + mTrueJump = block; + mFalseJump = nullptr; + block->mNumEntries = 1; + + block->SplitBranches(); + } + else + { + if (mTrueJump) + mTrueJump->SplitBranches(); + if (mFalseJump) + mFalseJump->SplitBranches(); + } + } +} + +bool InterCodeBasicBlock::IsEqual(const InterCodeBasicBlock* block) const +{ + if (mTrueJump == block->mTrueJump && mFalseJump == block->mFalseJump && mInstructions.Size() == block->mInstructions.Size()) + { + for (int i = 0; i < mInstructions.Size(); i++) + if (!mInstructions[i]->IsEqual(block->mInstructions[i])) + return false; + return true; + } + + return false; +} + +bool InterCodeBasicBlock::PreventsCallerStaticStack(void) +{ + if (!mVisited) + { + mVisited = true; + + for (int i = 0; i < mInstructions.Size(); i++) + { + InterInstruction* ins(mInstructions[i]); + if (ins->mCode == IC_CALL || ins->mCode == IC_CALL_NATIVE) + { + if (ins->mSrc[0].mTemp >= 0 || !ins->mSrc[0].mLinkerObject) + { + for (int j = 0; j < mProc->mCalledFunctions.Size(); j++) + { + if (!(mProc->mCalledFunctions[j]->mLinkerObject && (mProc->mCalledFunctions[j]->mLinkerObject->mFlags & LOBJF_STATIC_STACK))) + return true; + } + } + else if (ins->mSrc[0].mLinkerObject == mProc->mLinkerObject) + ; // Simple recursion + else if (!(ins->mSrc[0].mLinkerObject->mFlags & LOBJF_STATIC_STACK)) + return true; + } + else if (ins->mCode == IC_DISPATCH) + { + for (int j = 0; j < mProc->mCalledFunctions.Size(); j++) + { + if (!(mProc->mCalledFunctions[j]->mLinkerObject && (mProc->mCalledFunctions[j]->mLinkerObject->mFlags & LOBJF_STATIC_STACK))) + return true; + } + } + } + + if (mTrueJump && mTrueJump->PreventsCallerStaticStack()) + return true; + if (mFalseJump && mFalseJump->PreventsCallerStaticStack()) + return true; + } + + return false; +} + + +bool InterCodeBasicBlock::CheckStaticStack(void) +{ + if (!mVisited) + { + mVisited = true; + + for (int i = 0; i < mInstructions.Size(); i++) + { + if (mInstructions[i]->mCode == IC_CALL || mInstructions[i]->mCode == IC_CALL_NATIVE) + { + if (mInstructions[i]->mSrc[0].mTemp >= 0 || !mInstructions[i]->mSrc[0].mLinkerObject) + { + for (int j = 0; j < mProc->mCalledFunctions.Size(); j++) + { + if (!(mProc->mCalledFunctions[j]->mLinkerObject && (mProc->mCalledFunctions[j]->mLinkerObject->mFlags & LOBJF_STATIC_STACK))) + return false; + } + } + else if (!(mInstructions[i]->mSrc[0].mLinkerObject->mFlags & LOBJF_STATIC_STACK)) + return false; + } + else if (mInstructions[i]->mCode == IC_DISPATCH) + { + for (int j = 0; j < mProc->mCalledFunctions.Size(); j++) + { + if (!(mProc->mCalledFunctions[j]->mLinkerObject && (mProc->mCalledFunctions[j]->mLinkerObject->mFlags & LOBJF_STATIC_STACK))) + return false; + } + } + } + + if (mTrueJump && !mTrueJump->CheckStaticStack()) + return false; + if (mFalseJump && !mFalseJump->CheckStaticStack()) + return false; + } + + return true; +} + +void InterCodeBasicBlock::ApplyStaticStack(InterOperand & iop, const GrowingVariableArray& localVars) +{ + if (iop.mMemory == IM_LOCAL) + { + assert(localVars[iop.mVarIndex]->mIndex < mProc->mModule->mGlobalVars.Size()); + + iop.mMemory = iop.mMemoryBase = IM_GLOBAL; + iop.mLinkerObject = localVars[iop.mVarIndex]->mLinkerObject; + iop.mVarIndex = localVars[iop.mVarIndex]->mIndex; + } +} + +void InterCodeBasicBlock::CollectStaticStackDependencies(LinkerObject* lobj) +{ + if (!mVisited) + { + mVisited = true; + + for (int i = 0; i < mInstructions.Size(); i++) + { + if ((mInstructions[i]->mCode == IC_CALL || mInstructions[i]->mCode == IC_CALL_NATIVE) && mInstructions[i]->mSrc[0].mLinkerObject && mInstructions[i]->mSrc[0].mLinkerObject->mStackSection) + lobj->mStackSection->mSections.Push(mInstructions[i]->mSrc[0].mLinkerObject->mStackSection); + } + + if (mTrueJump) mTrueJump->CollectStaticStackDependencies(lobj); + if (mFalseJump) mFalseJump->CollectStaticStackDependencies(lobj); + } +} + +void InterCodeBasicBlock::CollectStaticStack(LinkerObject* lobj, const GrowingVariableArray& localVars) +{ + if (!mVisited) + { + mVisited = true; + + for (int i = 0; i < mInstructions.Size(); i++) + { + if ((mInstructions[i]->mCode == IC_CALL || mInstructions[i]->mCode == IC_CALL_NATIVE)) + { + if (mInstructions[i]->mSrc[0].mLinkerObject) + { + if (mInstructions[i]->mSrc[0].mLinkerObject->mStackSection) + lobj->mStackSection->mSections.Push(mInstructions[i]->mSrc[0].mLinkerObject->mStackSection); + } + } + + if (mInstructions[i]->mCode == IC_LOAD) + ApplyStaticStack(mInstructions[i]->mSrc[0],localVars); + else if (mInstructions[i]->mCode == IC_STORE || mInstructions[i]->mCode == IC_LEA || mInstructions[i]->mCode == IC_FILL) + ApplyStaticStack(mInstructions[i]->mSrc[1], localVars); + else if (mInstructions[i]->mCode == IC_CONSTANT && mInstructions[i]->mDst.mType == IT_POINTER) + { + ApplyStaticStack(mInstructions[i]->mConst, localVars); + mInstructions[i]->mDst.mMemory = mInstructions[i]->mDst.mMemoryBase = mInstructions[i]->mConst.mMemory; + } + else if (mInstructions[i]->mCode == IC_COPY) + { + ApplyStaticStack(mInstructions[i]->mSrc[0], localVars); + ApplyStaticStack(mInstructions[i]->mSrc[1], localVars); + } + } + + if (mTrueJump) mTrueJump->CollectStaticStack(lobj, localVars); + if (mFalseJump) mFalseJump->CollectStaticStack(lobj, localVars); + } +} + +void PromoteStaticStackParam(InterOperand& iop, LinkerObject* paramlobj) +{ + if (iop.mMemory == IM_FFRAME || iop.mMemory == IM_FPARAM) + { + if (iop.mVarIndex >= BC_REG_FPARAMS_END - BC_REG_FPARAMS) + { + int offset = iop.mVarIndex - (BC_REG_FPARAMS_END - BC_REG_FPARAMS); + iop.mMemory = IM_GLOBAL; + iop.mIntConst += offset; + iop.mLinkerObject = paramlobj; + iop.mVarIndex = -1; + paramlobj->EnsureSpace(int(iop.mIntConst), iop.mOperandSize); + } + } +} + +void InterCodeBasicBlock::PromoteStaticStackParams(LinkerObject* paramlobj) +{ + if (!mVisited) + { + mVisited = true; + + for (int i = 0; i < mInstructions.Size(); i++) + { + if (mInstructions[i]->mCode == IC_LOAD) + PromoteStaticStackParam(mInstructions[i]->mSrc[0], paramlobj); + else if (mInstructions[i]->mCode == IC_STORE || mInstructions[i]->mCode == IC_LEA || mInstructions[i]->mCode == IC_FILL) + PromoteStaticStackParam(mInstructions[i]->mSrc[1], paramlobj); + else if (mInstructions[i]->mCode == IC_CONSTANT && mInstructions[i]->mDst.mType == IT_POINTER) + PromoteStaticStackParam(mInstructions[i]->mConst, paramlobj); + else if (mInstructions[i]->mCode == IC_COPY) + { + PromoteStaticStackParam(mInstructions[i]->mSrc[0], paramlobj); + PromoteStaticStackParam(mInstructions[i]->mSrc[1], paramlobj); + } + } + + if (mTrueJump) mTrueJump->PromoteStaticStackParams(paramlobj); + if (mFalseJump) mFalseJump->PromoteStaticStackParams(paramlobj); + } +} + + +void InterCodeBasicBlock::CollectByteIndexPointers(NumberSet& invtemps, NumberSet& inctemps, GrowingIntArray& vartemps) +{ + if (!mVisited) + { + mVisited = true; + + for (int i = 0; i < mInstructions.Size(); i++) + { + const InterInstruction* ins = mInstructions[i]; + if (ins->mDst.mTemp >= 0 && !invtemps[ins->mDst.mTemp]) + { + if (ins->mDst.mType == IT_POINTER && ins->mDst.mMemoryBase == IM_GLOBAL && ins->mDst.mVarIndex >= 0 && mProc->mModule->mGlobalVars[ins->mDst.mVarIndex]->mSize < 256) + { + if (vartemps[ins->mDst.mTemp] == -1 || vartemps[ins->mDst.mTemp] == ins->mDst.mVarIndex) + { + vartemps[ins->mDst.mTemp] = ins->mDst.mVarIndex; + if (ins->mCode == IC_LEA) + { + if (ins->mSrc[1].mTemp == ins->mDst.mTemp) + inctemps += ins->mDst.mTemp; + else + invtemps += ins->mDst.mTemp; + } + else if (ins->mCode == IC_CONSTANT) + { + + } + else + invtemps += ins->mDst.mTemp; + } + else + invtemps += ins->mDst.mTemp; + } + else + invtemps += ins->mDst.mTemp; + } + + if (ins->mCode == IC_STORE) + { + if (ins->mSrc[0].mTemp >= 0) + invtemps += ins->mSrc[0].mTemp; + } + else if (ins->mCode == IC_LEA) + { + if (ins->mSrc[1].mTemp >= 0 && ins->mSrc[1].mTemp != ins->mDst.mTemp) + invtemps += ins->mSrc[1].mTemp; + if (ins->mSrc[0].mTemp >= 0) + invtemps += ins->mSrc[0].mTemp; + } + else if (ins->mCode == IC_LOAD) + { + } + else if (ins->mCode == IC_CONSTANT) + { + } + else if (ins->mCode == IC_RELATIONAL_OPERATOR && (ins->mSrc[0].mType == IT_POINTER || ins->mSrc[1].mType == IT_POINTER)) + { + int ic = 0, ir = 1; + if (ins->mSrc[0].mTemp > 0) + { + ic = 1; ir = 0; + } + + if (ins->mSrc[ic].mTemp < 0 && ins->mSrc[ir].mTemp >= 0 && !invtemps[ins->mSrc[ir].mTemp]) + { + int treg = ins->mSrc[ir].mTemp; + int vidx = ins->mSrc[ic].mVarIndex; + + if (ins->mSrc[ic].mMemory == IM_GLOBAL && vidx >= 0 && mProc->mModule->mGlobalVars[vidx]->mSize < 256) + { + if (vartemps[treg] == -1 || vartemps[treg] == vidx) + { + vartemps[treg] = vidx; + } + else + invtemps += treg; + } + else + invtemps += treg; + } + else + { + if (ins->mSrc[1].mTemp >= 0) + invtemps += ins->mSrc[1].mTemp; + if (ins->mSrc[0].mTemp >= 0) + invtemps += ins->mSrc[0].mTemp; + } + } + else + { + for (int i = 0; i < ins->mNumOperands; i++) + if (ins->mSrc[i].mTemp >= 0) + invtemps += ins->mSrc[i].mTemp; + } + } + + if (mTrueJump) mTrueJump->CollectByteIndexPointers(invtemps, inctemps, vartemps); + if (mFalseJump) mFalseJump->CollectByteIndexPointers(invtemps, inctemps, vartemps); + } +} + +bool InterCodeBasicBlock::ReplaceByteIndexPointers(const NumberSet& inctemps, const GrowingIntArray& vartemps, int& spareTemps) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + InterType itype = mProc->mNativeProcedure ? IT_INT8 : IT_INT16; + + for (int i = 0; i < mInstructions.Size(); i++) + { + InterInstruction* ins = mInstructions[i]; + switch (ins->mCode) + { + case IC_CONSTANT: + if (inctemps[ins->mDst.mTemp]) + { + ins->mDst.mType = itype; + } + break; + case IC_LOAD: + case IC_STORE: + { + int pi = ins->mCode == IC_STORE ? 1 : 0; + if (ins->mSrc[pi].mTemp >= 0 && inctemps[ins->mSrc[pi].mTemp]) + { + int ireg = ins->mSrc[pi].mTemp; + + InterInstruction* nins = new InterInstruction(ins->mLocation, IC_LEA); + nins->mNumOperands = 2; + nins->mSrc[0].mType = itype; + nins->mSrc[0].mTemp = ireg; + nins->mSrc[1].mType = IT_POINTER; + nins->mSrc[1].mTemp = -1; + nins->mSrc[1].mMemory = IM_GLOBAL; + nins->mSrc[1].mVarIndex = vartemps[ireg]; + nins->mSrc[1].mLinkerObject = mProc->mModule->mGlobalVars[vartemps[ireg]]->mLinkerObject; + mInstructions.Insert(i, nins); + nins->mDst = ins->mSrc[pi]; + ins->mSrc[pi].mTemp = nins->mDst.mTemp = spareTemps++; + ins->mSrc[pi].mFinal = true; + i++; + changed = true; + } + } break; + case IC_RELATIONAL_OPERATOR: + if (ins->mSrc[0].mTemp >= 0 && inctemps[ins->mSrc[0].mTemp] || ins->mSrc[1].mTemp >= 0 && inctemps[ins->mSrc[1].mTemp]) + { + ins->mSrc[0].mType = itype; + ins->mSrc[1].mType = itype; + changed = true; + } + break; + case IC_LEA: + if (inctemps[ins->mDst.mTemp]) + { + ins->mCode = IC_BINARY_OPERATOR; + ins->mOperator = IA_ADD; + ins->mDst.mType = itype; + ins->mSrc[1].mType = itype; + changed = true; + } + break; + } + + } + + if (mTrueJump && mTrueJump->ReplaceByteIndexPointers(inctemps, vartemps, spareTemps)) changed = true; + if (mFalseJump && mFalseJump->ReplaceByteIndexPointers(inctemps, vartemps, spareTemps)) changed = true; + } + + return changed; +} + +bool InterCodeBasicBlock::PropagateByteIndexPointers(void) +{ + return false; + + + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + + if (mTrueJump && mTrueJump->mNumEntries == 1 && !(mFalseJump && mFalseJump->mNumEntries > 1)) + { + for (int i = 0; i < mInstructions.Size() - 1; i++) + { + InterInstruction* ins = mInstructions[i]; + + if (ins->mCode == IC_LEA && ins->mSrc[1].mTemp < 0 && ins->mSrc[1].mMemory == IM_GLOBAL && ins->mSrc[0].IsUByte()) + { + if (CanMoveInstructionBehindBlock(i)) + { + mTrueJump->mInstructions.Insert(0, ins); + if (mFalseJump) + mFalseJump->mInstructions.Insert(0, ins->Clone()); + mInstructions.Remove(i); + i--; + changed = true; + } + } + } + } + + if (mTrueJump && mTrueJump->PropagateByteIndexPointers()) changed = true; + if (mFalseJump && mFalseJump->PropagateByteIndexPointers()) changed = true; + } + + return changed; +} + +bool InterCodeBasicBlock::DropUnreachable(void) +{ + if (!mVisited) + { + mVisited = true; + + int i = 0; + while (i < mInstructions.Size() && mInstructions[i]->mCode != IC_UNREACHABLE) + i++; + + if (i < mInstructions.Size()) + { + // kill all instructions after this + mInstructions.SetSize(i + 1); + mFalseJump = nullptr; + mTrueJump = nullptr; + + if (mInstructions.Size() == 1) + mUnreachable = true; + } + else + { + if (mFalseJump) + { + if (mFalseJump->DropUnreachable()) + { + mInstructions.Last()->mCode = IC_JUMP; + mInstructions.Last()->mNumOperands = 0; + mFalseJump = nullptr; + + if (mTrueJump->DropUnreachable()) + { + mTrueJump = nullptr; + mInstructions.SetSize(mInstructions.Size() - 1); + if (mInstructions.Size() == 0) + mUnreachable = true; + } + } + else if (mTrueJump->DropUnreachable()) + { + mInstructions.Last()->mCode = IC_JUMP; + mInstructions.Last()->mNumOperands = 0; + mTrueJump = mFalseJump; + mFalseJump = nullptr; + } + } + else if (mTrueJump && mTrueJump->DropUnreachable()) + { + mTrueJump = nullptr; + mInstructions.SetSize(mInstructions.Size() - 1); + if (mInstructions.Size() == 0) + mUnreachable = true; + } + } + } + + return mUnreachable; +} + + +bool InterCodeBasicBlock::OptimizeIntervalCompare(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + int sz = mInstructions.Size(); + if (sz >= 2 && mTrueJump && mFalseJump && mInstructions[sz - 1]->mCode == IC_BRANCH && mInstructions[sz - 2]->mCode == IC_RELATIONAL_OPERATOR && + mInstructions[sz - 2]->mDst.mTemp == mInstructions[sz - 1]->mSrc[0].mTemp && !mExitRequiredTemps[mInstructions[sz - 1]->mSrc[0].mTemp] && + IsIntegerType(mInstructions[sz - 2]->mSrc[0].mType)) + { + if (mInstructions[sz - 2]->mOperator == IA_CMPGES && mInstructions[sz - 2]->mSrc[0].mTemp == -1) + { + if (mTrueJump->mInstructions.Size() == 2 && mTrueJump->mInstructions[1]->mCode == IC_BRANCH && mTrueJump->mFalseJump == mFalseJump && + mTrueJump->mInstructions[0]->mCode == IC_RELATIONAL_OPERATOR && mTrueJump->mInstructions[0]->mDst.mTemp == mTrueJump->mInstructions[1]->mSrc[0].mTemp && + mTrueJump->mInstructions[1]->mSrc[0].mFinal) + { + if (mTrueJump->mInstructions[0]->mSrc[0].mTemp == -1 && mTrueJump->mInstructions[0]->mSrc[1].mTemp == mInstructions[sz - 2]->mSrc[1].mTemp) + { + if (mTrueJump->mInstructions[0]->mOperator == IA_CMPLS && mInstructions[sz - 2]->mSrc[0].mIntConst == 0 && mTrueJump->mInstructions[0]->mSrc[0].mIntConst > 0) + { + mInstructions[sz - 2]->mOperator = IA_CMPLU; + mInstructions[sz - 2]->mSrc[0].mIntConst = mTrueJump->mInstructions[0]->mSrc[0].mIntConst; + mTrueJump->mNumEntries--; + mTrueJump = mTrueJump->mTrueJump; + mTrueJump->mNumEntries++; + changed = true; + } + else if (mTrueJump->mInstructions[0]->mOperator == IA_CMPLES && mInstructions[sz - 2]->mSrc[0].mIntConst == 0 && mTrueJump->mInstructions[0]->mSrc[0].mIntConst > 0) + { + mInstructions[sz - 2]->mOperator = IA_CMPLEU; + mInstructions[sz - 2]->mSrc[0].mIntConst = mTrueJump->mInstructions[0]->mSrc[0].mIntConst; + mTrueJump->mNumEntries--; + mTrueJump = mTrueJump->mTrueJump; + mTrueJump->mNumEntries++; + changed = true; + } + } + } + } + else if (mInstructions[sz - 2]->mOperator == IA_CMPLS && mInstructions[sz - 2]->mSrc[0].mTemp == -1) + { + if (mTrueJump->mInstructions.Size() == 2 && mTrueJump->mInstructions[1]->mCode == IC_BRANCH && mTrueJump->mFalseJump == mFalseJump && + mTrueJump->mInstructions[0]->mCode == IC_RELATIONAL_OPERATOR && mTrueJump->mInstructions[0]->mDst.mTemp == mTrueJump->mInstructions[1]->mSrc[0].mTemp && + mTrueJump->mInstructions[1]->mSrc[0].mFinal) + { + if (mTrueJump->mInstructions[0]->mSrc[0].mTemp == -1 && mTrueJump->mInstructions[0]->mSrc[1].mTemp == mInstructions[sz - 2]->mSrc[1].mTemp) + { + if (mTrueJump->mInstructions[0]->mOperator == IA_CMPGES && mInstructions[sz - 2]->mSrc[0].mIntConst > 0 && mTrueJump->mInstructions[0]->mSrc[0].mIntConst == 0) + { + mInstructions[sz - 2]->mOperator = IA_CMPLU; + mTrueJump->mNumEntries--; + mTrueJump = mTrueJump->mTrueJump; + mTrueJump->mNumEntries++; + changed = true; + } + } + } + } + else if (mInstructions[sz - 2]->mOperator == IA_CMPLES && mInstructions[sz - 2]->mSrc[0].mTemp == -1) + { + if (mTrueJump->mInstructions.Size() == 2 && mTrueJump->mInstructions[1]->mCode == IC_BRANCH && mTrueJump->mFalseJump == mFalseJump && + mTrueJump->mInstructions[0]->mCode == IC_RELATIONAL_OPERATOR && mTrueJump->mInstructions[0]->mDst.mTemp == mTrueJump->mInstructions[1]->mSrc[0].mTemp && + mTrueJump->mInstructions[1]->mSrc[0].mFinal) + { + if (mTrueJump->mInstructions[0]->mSrc[0].mTemp == -1 && mTrueJump->mInstructions[0]->mSrc[1].mTemp == mInstructions[sz - 2]->mSrc[1].mTemp) + { + if (mTrueJump->mInstructions[0]->mOperator == IA_CMPGES && mInstructions[sz - 2]->mSrc[0].mIntConst > 0 && mTrueJump->mInstructions[0]->mSrc[0].mIntConst == 0) + { + mInstructions[sz - 2]->mOperator = IA_CMPLEU; + mTrueJump->mNumEntries--; + mTrueJump = mTrueJump->mTrueJump; + mTrueJump->mNumEntries++; + changed = true; + } + } + } + } + } + + if (mTrueJump && mTrueJump->OptimizeIntervalCompare()) + changed = true; + if (mFalseJump && mFalseJump->OptimizeIntervalCompare()) + changed = true; + } + + return changed; +} + +static bool BlockSameCondition(InterCodeBasicBlock* b, InterCodeBasicBlock* s) +{ + int nb = b->mInstructions.Size(); + if (s->mInstructions.Size() == 2 && + s->mInstructions[0]->mCode == IC_RELATIONAL_OPERATOR && + s->mInstructions[0]->mOperator == b->mInstructions[nb - 2]->mOperator && + s->mInstructions[1]->mCode == IC_BRANCH && + s->mInstructions[1]->mSrc[0].mTemp == s->mInstructions[0]->mDst.mTemp && + s->mInstructions[1]->mSrc[0].mFinal && + s->mInstructions[0]->IsEqualSource(b->mInstructions[nb - 2])) + { + return true; + } + + return false; +} + +void InterCodeBasicBlock::FollowJumps(void) +{ + if (!mVisited) + { + mVisited = true; + + while (mInstructions.Size() > 0 && mInstructions[mInstructions.Size() - 1]->mCode == IC_JUMP && mTrueJump->mNumEntries == 1) + { + InterCodeBasicBlock* block = mTrueJump; + mInstructions.SetSize(mInstructions.Size() - 1); + for (int i = 0; i < block->mInstructions.Size(); i++) + mInstructions.Push(block->mInstructions[i]); + + block->mNumEntries = 0; + block->mInstructions.Clear(); + mTrueJump = block->mTrueJump; + mFalseJump = block->mFalseJump; + } + + int sz = mInstructions.Size(); + if (sz > 1 && mInstructions[sz - 1]->mCode == IC_BRANCH && mInstructions[sz - 2]->mCode == IC_RELATIONAL_OPERATOR && + mInstructions[sz - 1]->mSrc[0].mTemp == mInstructions[sz - 2]->mDst.mTemp && + mInstructions[sz - 2]->mSrc[0].mTemp != mInstructions[sz - 2]->mDst.mTemp && + mInstructions[sz - 2]->mSrc[1].mTemp != mInstructions[sz - 2]->mDst.mTemp) + { + if (mTrueJump && BlockSameCondition(this, mTrueJump)) + { + InterCodeBasicBlock* target = mTrueJump->mTrueJump; + mTrueJump->mNumEntries--; + mTrueJump->mEntryBlocks.RemoveAll(this); + target->mNumEntries++; + target->mEntryBlocks.Push(this); + mTrueJump = target; + } + else if (mFalseJump && BlockSameCondition(this, mFalseJump)) + { + InterCodeBasicBlock* target = mFalseJump->mFalseJump; + mFalseJump->mNumEntries--; + mFalseJump->mEntryBlocks.RemoveAll(this); + target->mNumEntries++; + target->mEntryBlocks.Push(this); + mFalseJump = target; + } +#if 1 + else if (mTrueJump && mTrueJump->mInstructions.Size() == 2 && + mTrueJump->mInstructions[0]->mDst.mTemp != mInstructions[sz - 2]->mDst.mTemp && + mTrueJump->mInstructions[1]->mCode == IC_BRANCH && mTrueJump->mInstructions[1]->mSrc[0].mTemp == mInstructions[sz - 2]->mDst.mTemp) + { + InterCodeBasicBlock* block = mTrueJump->Clone(); + block->mInstructions[1]->mCode = IC_JUMP; + block->mInstructions[1]->mNumOperands = 0; + block->Close(mTrueJump->mTrueJump, nullptr); + + block->mTrueJump->mNumEntries++; + block->mTrueJump->mEntryBlocks.Push(block); + + mTrueJump->mNumEntries--; + mTrueJump->mEntryBlocks.RemoveAll(this); + block->mNumEntries++; + block->mEntryBlocks.Push(this); + mTrueJump = block; + } + else if (mFalseJump && mFalseJump->mInstructions.Size() == 2 && + mFalseJump->mInstructions[0]->mDst.mTemp != mInstructions[sz - 2]->mDst.mTemp && + mFalseJump->mInstructions[1]->mCode == IC_BRANCH && mFalseJump->mInstructions[1]->mSrc[0].mTemp == mInstructions[sz - 2]->mDst.mTemp) + { + InterCodeBasicBlock* block = mFalseJump->Clone(); + block->mInstructions[1]->mCode = IC_JUMP; + block->mInstructions[1]->mNumOperands = 0; + block->Close(mFalseJump->mFalseJump, nullptr); + + block->mTrueJump->mNumEntries++; + block->mTrueJump->mEntryBlocks.Push(block); + + mFalseJump->mNumEntries--; + mFalseJump->mEntryBlocks.RemoveAll(this); + block->mNumEntries++; + block->mEntryBlocks.Push(this); + mFalseJump = block; + } +#endif + } + else if (sz > 1 && mInstructions[sz - 1]->mCode == IC_JUMP && mInstructions[sz - 2]->mCode == IC_CONSTANT) + { + if (mTrueJump->mInstructions.Size() == 2 && !mTrueJump->mLoopHead && + mTrueJump->mInstructions[0]->mCode == IC_RELATIONAL_OPERATOR && + mTrueJump->mInstructions[1]->mCode == IC_BRANCH && + mTrueJump->mInstructions[1]->mSrc[0].mTemp == mTrueJump->mInstructions[0]->mDst.mTemp && + mTrueJump->mInstructions[1]->mSrc[0].mFinal) + { + InterCodeBasicBlock* target = nullptr; + + if (mTrueJump->mInstructions[0]->mSrc[0].mTemp == mInstructions[sz - 2]->mDst.mTemp && + mTrueJump->mInstructions[0]->mSrc[1].mTemp < 0) + { + InterOperand op = OperandConstantFolding(mTrueJump->mInstructions[0]->mOperator, mTrueJump->mInstructions[0]->mSrc[1], mInstructions[sz - 2]->mConst); + target = op.mIntConst ? mTrueJump->mTrueJump : mTrueJump->mFalseJump; + } + else if (mTrueJump->mInstructions[0]->mSrc[1].mTemp == mInstructions[sz - 2]->mDst.mTemp && + mTrueJump->mInstructions[0]->mSrc[0].mTemp < 0) + { + InterOperand op = OperandConstantFolding(mTrueJump->mInstructions[0]->mOperator, mInstructions[sz - 2]->mConst, mTrueJump->mInstructions[0]->mSrc[0]); + target = op.mIntConst ? mTrueJump->mTrueJump : mTrueJump->mFalseJump; + } + + if (target) + { + mTrueJump->mNumEntries--; + mTrueJump->mEntryBlocks.RemoveAll(this); + target->mNumEntries++; + target->mEntryBlocks.Push(this); + mTrueJump = target; + } + } + } + + if (mTrueJump) + mTrueJump->FollowJumps(); + if (mFalseJump) + mFalseJump->FollowJumps(); + } +} + +void InterCodeBasicBlock::BuildLoopSuffix(void) +{ + if (!mVisited) + { + mVisited = true; + + if (mFalseJump) + { + if (mTrueJump->mLoopHead && mTrueJump->mNumEntries == 2 && mFalseJump->mNumEntries > 1) + { + InterCodeBasicBlock* suffix = new InterCodeBasicBlock(mProc); + + suffix->mEntryRequiredTemps = mFalseJump->mEntryRequiredTemps; + suffix->mExitRequiredTemps = mFalseJump->mEntryRequiredTemps; + suffix->mLocalModifiedTemps.Reset(mExitRequiredTemps.Size()); + + InterInstruction* jins = new InterInstruction(mInstructions[0]->mLocation, IC_JUMP); + suffix->Append(jins); + suffix->Close(mFalseJump, nullptr); + mFalseJump = suffix; + suffix->mNumEntries = 1; + } + else if (mFalseJump->mLoopHead && mFalseJump->mNumEntries == 2 && mTrueJump->mNumEntries > 1) + { + InterCodeBasicBlock* suffix = new InterCodeBasicBlock(mProc); + suffix->mEntryRequiredTemps = mTrueJump->mEntryRequiredTemps; + suffix->mExitRequiredTemps = mTrueJump->mEntryRequiredTemps; + suffix->mLocalModifiedTemps.Reset(mExitRequiredTemps.Size()); + + InterInstruction* jins = new InterInstruction(mInstructions[0]->mLocation, IC_JUMP); + suffix->Append(jins); + suffix->Close(mTrueJump, nullptr); + mTrueJump = suffix; + suffix->mNumEntries = 1; + } + } +#if 0 + if (mLoopHead && mNumEntries == 2 && mFalseJump) + { + if (mTrueJump == this && mFalseJump != this) + { + if (mFalseJump->mNumEntries > 1) + { + InterCodeBasicBlock* suffix = new InterCodeBasicBlock(mProc); + suffix->mEntryRequiredTemps = mFalseJump->mEntryRequiredTemps; + suffix->mExitRequiredTemps = mFalseJump->mEntryRequiredTemps; + suffix->mLocalModifiedTemps.Reset(mExitRequiredTemps.Size()); + + InterInstruction* jins = new InterInstruction(mInstructions[0]->mLocation, IC_JUMP); + suffix->Append(jins); + suffix->Close(mFalseJump, nullptr); + mFalseJump = suffix; + suffix->mNumEntries = 1; + } + } + else if (mFalseJump == this && mTrueJump != this) + { + if (mTrueJump->mNumEntries > 1) + { + InterCodeBasicBlock* suffix = new InterCodeBasicBlock(mProc); + suffix->mEntryRequiredTemps = mTrueJump->mEntryRequiredTemps; + suffix->mExitRequiredTemps = mTrueJump->mEntryRequiredTemps; + suffix->mLocalModifiedTemps.Reset(mExitRequiredTemps.Size()); + + InterInstruction* jins = new InterInstruction(mInstructions[0]->mLocation, IC_JUMP); + suffix->Append(jins); + suffix->Close(mTrueJump, nullptr); + mTrueJump = suffix; + suffix->mNumEntries = 1; + } + } + } +#endif + + if (mTrueJump) + mTrueJump->BuildLoopSuffix(); + if (mFalseJump) + mFalseJump->BuildLoopSuffix(); + } +} + +InterCodeBasicBlock* InterCodeBasicBlock::BuildLoopPrefix(void) +{ + if (!mVisited) + { + mVisited = true; + + if (mTrueJump) + mTrueJump = mTrueJump->BuildLoopPrefix(); + if (mFalseJump) + mFalseJump = mFalseJump->BuildLoopPrefix(); + + if (mLoopHead) + { + mLoopPrefix = new InterCodeBasicBlock(mProc); + mLoopPrefix->mEntryRequiredTemps = mEntryRequiredTemps; + mLoopPrefix->mExitRequiredTemps = mEntryRequiredTemps; + mLoopPrefix->mLocalModifiedTemps.Reset(mEntryRequiredTemps.Size()); + + InterInstruction* jins = new InterInstruction(mInstructions[0]->mLocation, IC_JUMP); + mLoopPrefix->Append(jins); + mLoopPrefix->Close(this, nullptr); + } + } + + return mLoopPrefix ? mLoopPrefix : this; +} + +bool InterCodeBasicBlock::CollectLoopBody(InterCodeBasicBlock* head, ExpandingArray & body) +{ + if (mLoopHead) + { +#if 0 + return this == head; +#else + if (this == head) + return true; + else if ((mTrueJump == this || mFalseJump == this) && mEntryBlocks.Size() == 2) + { + int j = 0; + while (j < mInstructions.Size() && (mInstructions[j]->mDst.mTemp < 0 || !mExitRequiredTemps[mInstructions[j]->mDst.mTemp])) + j++; + if (j != mInstructions.Size()) + return false; + } + else + return false; +#endif + } + + if (body.IndexOf(this) != -1) + return true; + body.Push(this); + + for (int i = 0; i < mEntryBlocks.Size(); i++) + if (!mEntryBlocks[i]->CollectLoopBody(head, body)) + return false; + + return true; +} + +bool InterCodeBasicBlock::CollectLoopBodyRecursive(InterCodeBasicBlock* head, ExpandingArray& body) +{ + if (this == head) + return true; + + if (body.IndexOf(this) != -1) + return true; + body.Push(this); + + for (int i = 0; i < mEntryBlocks.Size(); i++) + if (!mEntryBlocks[i]->CollectLoopBodyRecursive(head, body)) + return false; + + return true; +} + +void InterCodeBasicBlock::CollectLoopPath(const ExpandingArray& body, ExpandingArray& path) +{ + if (body.IndexOf(this) >= 0) + { + if (!mLoopPath) + { + if (mTrueJump && !mTrueJump->mLoopHead) + { + mTrueJump->CollectLoopPath(body, mLoopPathBlocks); + if (mFalseJump) + { + ExpandingArray fpath; + + if (!mFalseJump->mLoopHead) + mFalseJump->CollectLoopPath(body, fpath); + + int i = 0; + while (i < mLoopPathBlocks.Size()) + { + if (fpath.IndexOf(mLoopPathBlocks[i]) >= 0) + i++; + else + mLoopPathBlocks.Remove(i); + } + } + } + + mLoopPathBlocks.Insert(0, this); + mLoopPath = true; + } + + path = mLoopPathBlocks; + } +} + +static bool IsMatchingStore(const InterInstruction* lins, const InterInstruction* sins) +{ + if (sins->mCode == IC_STORE && + sins->mSrc[1].mTemp < 0 && lins->mSrc[0].mMemory == sins->mSrc[1].mMemory && lins->mSrc[0].mIntConst == sins->mSrc[1].mIntConst && + lins->mDst.mType == sins->mSrc[0].mType) + { + switch (lins->mSrc[0].mMemory) + { + case IM_FPARAM: + return lins->mSrc[0].mVarIndex == sins->mSrc[1].mVarIndex; + } + } + + return false; +} + +bool InterCodeBasicBlock::CollidingMem(InterCodeBasicBlock* block, InterInstruction * lins, int from, int to) const +{ + for (int i = from; i < to; i++) + { + InterInstruction* ins = block->mInstructions[i]; + if (CollidingMem(lins, ins)) + return true; + } + + return false; +} + + + +bool InterCodeBasicBlock::InvalidatedBy(const InterInstruction* ins, const InterInstruction* by) const +{ + if (by->mDst.mTemp >= 0 && ins->ReferencesTemp(by->mDst.mTemp)) + return true; + else if (ins->mCode == IC_STORE || ins->mCode == IC_LOAD) + return DestroyingMem(ins, by); + else + return CollidingMem(by, ins); +} + +void InterCodeBasicBlock::CollectReachable(ExpandingArray& lblock) +{ + if (!mVisited && !mPatched) + { + lblock.Push(this); + mPatched = true; + + if (mTrueJump) mTrueJump->CollectReachable(lblock); + if (mFalseJump) mFalseJump->CollectReachable(lblock); + } +} + +bool InterCodeBasicBlock::CollectGenericLoop(ExpandingArray& lblocks) +{ + ExpandingArray rblocks; + + mProc->ResetPatched(); + CollectReachable(rblocks); + + mProc->ResetPatched(); + + bool changed; + do + { + changed = false; + + for (int i = 0; i < rblocks.Size(); i++) + { + InterCodeBasicBlock* block(rblocks[i]); + + if (!block->mPatched && + (block->mTrueJump && (block->mTrueJump->mPatched || block->mTrueJump == this) || + block->mFalseJump && (block->mFalseJump->mPatched || block->mFalseJump == this))) + { + lblocks.Push(block); + block->mPatched = true; + changed = true; + } + } + + } while (changed); + + return lblocks.Size() > 0; +} + +bool InterCodeBasicBlock::CollectSingleEntryGenericLoop(ExpandingArray& lblocks) +{ + if (CollectGenericLoop(lblocks)) + { + for (int i = 0; i < lblocks.Size(); i++) + { + InterCodeBasicBlock* block = lblocks[i]; + + if (block != this) + { + for (int j = 0; j < block->mEntryBlocks.Size(); j++) + if (!lblocks.Contains(block->mEntryBlocks[j])) + return false; + } + } + + return true; + } + else + return false; +} + + + +bool InterCodeBasicBlock::CollectSingleHeadLoopBody(InterCodeBasicBlock* head, InterCodeBasicBlock* tail, ExpandingArray& body) +{ + int i = 0; + body.Push(head); + + if (head == tail) + return true; + + while (i < body.Size()) + { + InterCodeBasicBlock* block = body[i++]; + if (block->mTrueJump) + { + if (block->mTrueJump == head) + return false; + if (block->mTrueJump != tail && !body.Contains(block->mTrueJump)) + body.Push(block->mTrueJump); + + if (block->mFalseJump) + { + if (block->mFalseJump == head) + return false; + if (block->mFalseJump != tail && !body.Contains(block->mFalseJump)) + body.Push(block->mFalseJump); + } + } + else + return false; + } + + body.Push(tail); + + return true; +} + +static InterInstruction * FindSourceInstruction(InterCodeBasicBlock* block, int temp) +{ + for (;;) + { + int i = block->mInstructions.Size() - 1; + while (i >= 0) + { + if (block->mInstructions[i]->mDst.mTemp == temp) + return block->mInstructions[i]; + i--; + } + + if (block->mEntryBlocks.Size() != 1) + return nullptr; + + block = block->mEntryBlocks[0]; + } +} + +bool InterCodeBasicBlock::MoveLoopHeadCheckToTail(void) +{ + bool modified = false; + if (!mVisited) + { + mVisited = true; + + if (mLoopHead && mEntryBlocks.Size() == 2 && mInstructions.Size() == 2) + { + if (mInstructions[0]->mCode == IC_RELATIONAL_OPERATOR && mInstructions[1]->mCode == IC_BRANCH && + mInstructions[1]->mSrc[0].mTemp == mInstructions[0]->mDst.mTemp) + { + if (mFalseJump != this && mTrueJump->mTrueJump == this && !mTrueJump->mFalseJump) + { + mTrueJump->mInstructions.SetSize(mTrueJump->mInstructions.Size() - 1); + mTrueJump->mInstructions.Push(mInstructions[0]->Clone()); + mTrueJump->mInstructions.Push(mInstructions[1]->Clone()); + mTrueJump->mTrueJump = mTrueJump; + mTrueJump->mFalseJump = mFalseJump; + mTrueJump->mEntryBlocks.Push(mTrueJump); + mTrueJump->mNumEntries++; + mFalseJump->mEntryBlocks.Push(mTrueJump); + mFalseJump->mNumEntries++; + mNumEntries--; + mEntryBlocks.Remove(mEntryBlocks.IndexOf(mTrueJump)); + modified = true; + } + } + } + + if (mTrueJump && mTrueJump->MoveLoopHeadCheckToTail()) + modified = true; + if (mFalseJump && mFalseJump->MoveLoopHeadCheckToTail()) + modified = true; + } + + return modified; +} + +bool SameExitCondition(InterCodeBasicBlock* b1, InterCodeBasicBlock* b2) +{ + if (b1->mTrueJump == b2->mTrueJump && b1->mFalseJump == b2->mFalseJump) + { + int n1 = b1->mInstructions.Size(), n2 = b2->mInstructions.Size(); + if (n1 > 1 && n2 > 1) + { + if (b1->mInstructions[n1 - 1]->mCode == IC_BRANCH && b2->mInstructions[n2 - 1]->mCode == IC_BRANCH && + b1->mInstructions[n1 - 2]->mCode == IC_RELATIONAL_OPERATOR && + b1->mInstructions[n1 - 1]->mSrc[0].mTemp == b1->mInstructions[n1 - 2]->mDst.mTemp && + b1->mInstructions[n1 - 1]->mSrc[0].mFinal && + b2->mInstructions[n2 - 2]->mCode == IC_RELATIONAL_OPERATOR && + b2->mInstructions[n2 - 1]->mSrc[0].mTemp == b2->mInstructions[n2 - 2]->mDst.mTemp && + b2->mInstructions[n2 - 1]->mSrc[0].mFinal && + b1->mInstructions[n1 - 2]->IsEqualSource(b2->mInstructions[n2 - 2])) + { + return true; + } + } + } + + return false; +} + +InterCodeBasicBlock* InterCodeBasicBlock::CheckIsSimpleIntRangeBranch(const GrowingIntegerValueRangeArray& irange) +{ + int sz = mInstructions.Size(); + if (irange.Size() > 0 && + mTrueJump && mFalseJump && mInstructions.Size() == 2 && + mInstructions[sz - 1]->mCode == IC_BRANCH && + mInstructions[sz - 2]->mCode == IC_RELATIONAL_OPERATOR && + IsScalarType(mInstructions[sz - 2]->mSrc[0].mType) && + IsScalarType(mInstructions[sz - 2]->mSrc[1].mType) && + mInstructions[sz - 1]->mSrc[0].mTemp == mInstructions[sz - 2]->mDst.mTemp && mInstructions[sz - 1]->mSrc[0].mFinal) + { + InterType it = mInstructions[sz - 2]->mSrc[0].mType; + + const InterInstruction* cins = mInstructions[sz - 2]; + if ((cins->mSrc[0].mTemp < 0 || irange[cins->mSrc[0].mTemp].IsBound()) && + (cins->mSrc[1].mTemp < 0 || irange[cins->mSrc[1].mTemp].IsBound())) + { + IntegerValueRange r0, r1; + if (cins->mSrc[0].mTemp < 0) + r0.SetConstant(cins->mSrc[0].mIntConst); + else + r0 = irange[cins->mSrc[0].mTemp]; + if (cins->mSrc[1].mTemp < 0) + r1.SetConstant(cins->mSrc[1].mIntConst); + else + r1 = irange[cins->mSrc[1].mTemp]; + + if (r0.mMaxValue <= SignedTypeMax(it) && r1.mMaxValue <= SignedTypeMax(it) || r0.mMinValue >= 0 && r1.mMinValue >= 0) + { + switch (mInstructions[sz - 2]->mOperator) + { + case IA_CMPEQ: + if (r0.mMinValue > r1.mMaxValue || r0.mMaxValue < r1.mMinValue) + return mFalseJump; + break; + case IA_CMPNE: + if (r0.mMinValue > r1.mMaxValue || r0.mMaxValue < r1.mMinValue) + return mTrueJump; + break; + } + } + } + } + + return nullptr; +} + +InterCodeBasicBlock* InterCodeBasicBlock::CheckIsConstBranch(const GrowingInstructionPtrArray& cins) +{ + if (!mFalseJump || mInstructions.Size() < 1 || mInstructions[mInstructions.Size() - 1]->mCode != IC_BRANCH) + return nullptr; + + GrowingInstructionPtrArray tins(cins); + for (int i = 0; i < mInstructions.Size() - 1; i++) + { + InterInstruction* ins(mInstructions[i]); + InterInstruction* nins = nullptr; + if (IsObservable(ins->mCode)) + return nullptr; + if (ins->mDst.mTemp >= 0) + { + if (ins->mCode == IC_CONSTANT) + nins = ins; + else if (ins->mCode == IC_LOAD && !ins->mVolatile) + { + int k = 0; + while (k < tins.Size() && !(tins[k]->mCode == IC_STORE && SameMemAndSize(ins->mSrc[0], tins[k]->mSrc[1]))) + k++; + if (k < tins.Size()) + { + nins = new InterInstruction(ins->mLocation, IC_CONSTANT); + nins->mDst = ins->mDst; + nins->mConst = tins[k]->mSrc[0]; + } + } + else if (ins->mCode == IC_RELATIONAL_OPERATOR && IsIntegerType(ins->mSrc[0].mType)) + { + IntegerValueRange v0, v1; + + if (ins->mSrc[0].mTemp < 0) + v0.SetLimit(ins->mSrc[0].mIntConst, ins->mSrc[0].mIntConst); + else + { + int k = 0; + while (k < tins.Size() && tins[k]->mDst.mTemp != ins->mSrc[0].mTemp) + k++; + if (k < tins.Size()) + v0 = tins[k]->mDst.mRange; + } + + if (ins->mSrc[1].mTemp < 0) + v1.SetLimit(ins->mSrc[1].mIntConst, ins->mSrc[1].mIntConst); + else + { + int k = 0; + while (k < tins.Size() && tins[k]->mDst.mTemp != ins->mSrc[1].mTemp) + k++; + if (k < tins.Size()) + v1 = tins[k]->mDst.mRange; + } + + if (v0.IsBound() && v1.IsBound()) + { + if (ins->mOperator == IA_CMPEQ) + { + if (v0.IsConstant() && v1.IsConstant() && v1.mMinValue == v0.mMinValue) + { + nins = new InterInstruction(ins->mLocation, IC_CONSTANT); + nins->mDst = ins->mDst; + nins->mConst.mType = IT_BOOL; + nins->mConst.mIntConst = 1; + } + else if ((v0.mMaxValue <= SignedTypeMax(ins->mSrc[0].mType) && v1.mMaxValue <= SignedTypeMax(ins->mSrc[0].mType) || v0.mMinValue >= 0 && v1.mMinValue >= 0) && (v0.mMinValue > v1.mMaxValue || v1.mMinValue > v0.mMaxValue)) + { + nins = new InterInstruction(ins->mLocation, IC_CONSTANT); + nins->mDst = ins->mDst; + nins->mConst.mType = IT_BOOL; + nins->mConst.mIntConst = 0; + } + } + } + } + else if (ins->mDst.mTemp >= 0 && ins->mDst.mRange.IsBound()) + nins = ins; + + if (ins->mDst.mTemp >= 0) + { + int k = 0; + while (k < tins.Size()) + { + if (tins[k]->ReferencesTemp(ins->mDst.mTemp)) + tins.Remove(k); + else + k++; + } + } + + if (nins) + tins.Push(nins); + } + } + + InterInstruction* bins = mInstructions[mInstructions.Size() - 1]; + + int k = 0; + while (k < tins.Size() && tins[k]->mDst.mTemp != bins->mSrc[0].mTemp) + k++; + + if (k < tins.Size() && tins[k]->mCode == IC_CONSTANT) + { + InterInstruction* cins = tins[k]; + + bool istrue = false; + InterType it = cins->mDst.mType; + + if (it == IT_FLOAT) + istrue = cins->mConst.mFloatConst != 0; + else if (it == IT_POINTER) + istrue = cins->mConst.mMemory != IM_ABSOLUTE || cins->mConst.mIntConst != 0; + else + istrue = cins->mConst.mIntConst != 0; + + InterCodeBasicBlock* tblock = istrue ? mTrueJump : mFalseJump; + + InterCodeBasicBlock* xblock = this->Clone(); + InterInstruction* bins = xblock->mInstructions.Pop(); + InterInstruction* jins = new InterInstruction(bins->mLocation, IC_JUMP); + xblock->mInstructions.Push(jins); + xblock->mTrueJump = tblock; + tblock->mEntryBlocks.Push(xblock); + tblock->mNumEntries++; + return xblock; + } + + return nullptr; +} + +bool InterCodeBasicBlock::ChangeTrueJump(InterCodeBasicBlock* block) +{ + if (block != mTrueJump) + { + mTrueJump->mEntryBlocks.RemoveAll(this); + mTrueJump->mNumEntries--; + block->mEntryBlocks.Push(this); + block->mNumEntries++; + mTrueJump = block; + return true; + } + + return false; +} + +bool InterCodeBasicBlock::ChangeFalseJump(InterCodeBasicBlock* block) +{ + if (block != mFalseJump) + { + mFalseJump->mEntryBlocks.RemoveAll(this); + mFalseJump->mNumEntries--; + block->mEntryBlocks.Push(this); + block->mNumEntries++; + mFalseJump = block; + return true; + } + + return false; +} + +bool InterCodeBasicBlock::ShortcutDuplicateBranches(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + int sz = mInstructions.Size(); + if (sz >= 2 && mInstructions[sz - 1]->mCode == IC_BRANCH && mInstructions[sz - 2]->mCode == IC_RELATIONAL_OPERATOR && mInstructions[sz - 1]->mSrc[0].mTemp == mInstructions[sz - 2]->mDst.mTemp) + { + InterInstruction* cins = mInstructions[sz - 2]; + InterInstruction* bins = mInstructions[sz - 1]; + + if (mTrueJump->mInstructions.Size() == 2 && mTrueJump->mInstructions[0]->mCode == IC_RELATIONAL_OPERATOR && mTrueJump->mInstructions[1]->mCode == IC_BRANCH && mTrueJump->mInstructions[0]->mDst.mTemp == mTrueJump->mInstructions[1]->mSrc[0].mTemp) + { + InterInstruction* tcins = mTrueJump->mInstructions[0]; + InterInstruction* tbins = mTrueJump->mInstructions[1]; + + if (cins->mSrc[0].IsEqual(tcins->mSrc[0]) && cins->mSrc[1].IsEqual(tcins->mSrc[1])) + { + if (tbins->mSrc[0].mFinal) + { + if (cins->mOperator == tcins->mOperator) + { + if (ChangeTrueJump(mTrueJump->mTrueJump)) + changed = true; + } + else if (cins->mOperator == InvertRelational(tcins->mOperator)) + { + if (ChangeTrueJump(mTrueJump->mFalseJump)) + changed = true; + } + } + else if (bins->mSrc[0].mFinal) + { + if (cins->mOperator == tcins->mOperator) + { + if (ChangeTrueJump(mTrueJump->mTrueJump)) + { + bins->mSrc[0].mTemp = cins->mDst.mTemp = tcins->mDst.mTemp; + bins->mSrc[0].mFinal = false; + mExitRequiredTemps += bins->mSrc[0].mTemp; + changed = true; + } + } + else if (cins->mOperator == InvertRelational(tcins->mOperator)) + { + if (ChangeTrueJump(mTrueJump->mFalseJump)) + { + bins->mSrc[0].mTemp = cins->mDst.mTemp = tcins->mDst.mTemp; + bins->mSrc[0].mFinal = false; + cins->mOperator = tcins->mOperator; + mExitRequiredTemps += bins->mSrc[0].mTemp; + InterCodeBasicBlock* t = mTrueJump; mTrueJump = mFalseJump; mFalseJump = t; + changed = true; + } + } + } + } + } + if (mFalseJump->mInstructions.Size() == 2 && mFalseJump->mInstructions[0]->mCode == IC_RELATIONAL_OPERATOR && mFalseJump->mInstructions[1]->mCode == IC_BRANCH && mFalseJump->mInstructions[0]->mDst.mTemp == mFalseJump->mInstructions[1]->mSrc[0].mTemp) + { + InterInstruction* tcins = mFalseJump->mInstructions[0]; + InterInstruction* tbins = mFalseJump->mInstructions[1]; + + if (cins->mSrc[0].IsEqual(tcins->mSrc[0]) && cins->mSrc[1].IsEqual(tcins->mSrc[1])) + { + if (tbins->mSrc[0].mFinal) + { + if (cins->mOperator == tcins->mOperator) + { + if (ChangeFalseJump(mFalseJump->mFalseJump)) + changed = true; + } + else if (cins->mOperator == InvertRelational(tcins->mOperator)) + { + if (ChangeFalseJump(mFalseJump->mTrueJump)) + changed = true; + } + } + else if (bins->mSrc[0].mFinal) + { + if (cins->mOperator == tcins->mOperator) + { + if (ChangeFalseJump(mFalseJump->mFalseJump)) + { + bins->mSrc[0].mTemp = cins->mDst.mTemp = tcins->mDst.mTemp; + bins->mSrc[0].mFinal = false; + mExitRequiredTemps += bins->mSrc[0].mTemp; + changed = true; + } + } + else if (cins->mOperator == InvertRelational(tcins->mOperator)) + { + if (ChangeFalseJump(mFalseJump->mTrueJump)) + { + bins->mSrc[0].mTemp = cins->mDst.mTemp = tcins->mDst.mTemp; + bins->mSrc[0].mFinal = false; + cins->mOperator = tcins->mOperator; + mExitRequiredTemps += bins->mSrc[0].mTemp; + InterCodeBasicBlock* t = mTrueJump; mTrueJump = mFalseJump; mFalseJump = t; + changed = true; + } + } + } + } + } + } + + if (mTrueJump && mTrueJump->ShortcutDuplicateBranches()) + changed = true; + if (mFalseJump && mFalseJump->ShortcutDuplicateBranches()) + changed = true; + } + + return changed; +} + +bool InterCodeBasicBlock::ShortcutConstBranches(const GrowingInstructionPtrArray& cins) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + GrowingInstructionPtrArray tins(cins); + if (mNumEntries > 1) + tins.SetSize(0); + + for (int i = 0; i < mInstructions.Size(); i++) + { + InterInstruction* ins(mInstructions[i]); + InterInstruction* nins = nullptr; + + if (ins->mCode == IC_CONSTANT) + nins = ins; + else + { + if (IsObservable(ins->mCode)) + { + int k = 0; + while (k < tins.Size()) + { + if (tins[k]->mCode == IC_STORE && DestroyingMem(tins[k], ins)) + tins.Remove(k); + else + k++; + } + } + else if (ins->mDst.mTemp >= 0 && ins->mDst.mRange.IsBound()) + nins = ins; + + if (ins->mCode == IC_STORE && !ins->mVolatile && ins->mSrc[0].mTemp < 0) + nins = ins; + } + + if (ins->mDst.mTemp >= 0) + { + int k = 0; + while (k < tins.Size() && tins[k]->mDst.mTemp != ins->mDst.mTemp) + k++; + if (k < tins.Size()) + tins.Remove(k); + } + + if (nins) + tins.Push(nins); + } + + if (mTrueJump) + { + InterCodeBasicBlock* tblock = mTrueJump->CheckIsConstBranch(tins); + if (!tblock) tblock = mTrueJump->CheckIsSimpleIntRangeBranch(mTrueValueRange); + + if (tblock) + { + mTrueJump->mEntryBlocks.RemoveAll(this); + mTrueJump->mNumEntries--; + tblock->mEntryBlocks.Push(this); + tblock->mNumEntries++; + mTrueJump = tblock; + } + if (mTrueJump->ShortcutConstBranches(tins)) + changed = true; + } + if (mFalseJump) + { + InterCodeBasicBlock* tblock = mFalseJump->CheckIsConstBranch(tins); + if (!tblock) tblock = mFalseJump->CheckIsSimpleIntRangeBranch(mFalseValueRange); + + if (tblock) + { + mFalseJump->mEntryBlocks.RemoveAll(this); + mFalseJump->mNumEntries--; + tblock->mEntryBlocks.Push(this); + tblock->mNumEntries++; + mFalseJump = tblock; + } + if (mFalseJump->ShortcutConstBranches(tins)) + changed = true; + } + } + + return changed; +} + + +void InterCodeBasicBlock::ReplaceCopyFill(void) +{ + if (!mVisited) + { + mVisited = true; + + if (mLoopHead && mEntryBlocks.Size() == 2 && mTrueJump == this) + { + if (mInstructions.Size() == 4 && + mInstructions[0]->mCode == IC_STORE && + mInstructions[0]->mSrc[0].mTemp < 0 && + mInstructions[0]->mSrc[0].mType == IT_INT8 && + mInstructions[0]->mSrc[1].mTemp >= 0 && + mInstructions[1]->mCode == IC_LEA && + mInstructions[1]->mDst.mTemp == mInstructions[0]->mSrc[1].mTemp && + mInstructions[1]->mSrc[1].mTemp == mInstructions[0]->mSrc[1].mTemp && + mInstructions[1]->mSrc[0].mTemp < 0 && + mInstructions[1]->mSrc[0].mIntConst == 1 && + mInstructions[2]->mCode == IC_RELATIONAL_OPERATOR && + mInstructions[2]->mSrc[1].mTemp == mInstructions[1]->mDst.mTemp && + mInstructions[2]->mSrc[0].mTemp < 0 && + mInstructions[3]->mCode == IC_BRANCH && + mInstructions[3]->mSrc[0].mTemp == mInstructions[2]->mDst.mTemp + ) + { + int reg = mInstructions[0]->mSrc[1].mTemp; + + InterCodeBasicBlock* pblock = mEntryBlocks[0]; + if (pblock == this) + pblock = mEntryBlocks[1]; + + const InterInstruction* cins = mInstructions[2]; + const InterInstruction* bins = pblock->FindByDst(reg); + + if (bins && bins->mCode == IC_CONSTANT) + { + if (bins->mConst.mMemory == cins->mSrc[0].mMemory) + { + int64 size = cins->mSrc[0].mIntConst - bins->mConst.mIntConst; + if (size > 0) + { + mInstructions[0]->mCode = IC_FILL; + mInstructions[0]->mConst.mOperandSize = int(size); + mInstructions[0]->mSrc[1] = bins->mConst; + mInstructions[1]->mSrc[0].mIntConst = size; + mInstructions[2]->mCode = IC_NONE; + mInstructions[2]->mDst.mTemp = -1; + mInstructions[2]->mNumOperands = 0; + mInstructions[3]->mCode = IC_JUMP; + mInstructions[3]->mDst.mTemp = -1; + mInstructions[3]->mNumOperands = 0; + + mNumEntries--; + mEntryBlocks.RemoveAll(this); + mTrueJump = mFalseJump; + mFalseJump = nullptr; + } + } + } + } + } + + if (mTrueJump) mTrueJump->ReplaceCopyFill(); + if (mFalseJump) mFalseJump->ReplaceCopyFill(); + } +} + +bool InterCodeBasicBlock::MergeLoopTails(void) +{ + bool modified = false; + + if (!mVisited) + { + mVisited = true; + + if (mLoopHead && mEntryBlocks.Size() > 2) + { + int fi = 0; + while (fi < mEntryBlocks.Size() && mEntryBlocks[fi] == mLoopPrefix) + fi++; + + int i = fi + 1; + while (i < mEntryBlocks.Size() && (mEntryBlocks[i] == mLoopPrefix || SameExitCondition(mEntryBlocks[i], mEntryBlocks[fi]))) + i++; + if (i == mEntryBlocks.Size()) + { + int n = 2; + bool match = true; + while (i == mEntryBlocks.Size() && n < mEntryBlocks[fi]->mInstructions.Size()) + { + i = fi + 1; + while (i < mEntryBlocks.Size() && (mEntryBlocks[i] == mLoopPrefix || + n < mEntryBlocks[i]->mInstructions.Size() && + mEntryBlocks[i]->mInstructions[mEntryBlocks[i]->mInstructions.Size() - n - 1]->IsEqual(mEntryBlocks[fi]->mInstructions[mEntryBlocks[fi]->mInstructions.Size() - n - 1]))) + i++; + + if (i == mEntryBlocks.Size()) + n++; + } + + InterCodeBasicBlock* block = new InterCodeBasicBlock(mProc); + block->mTrueJump = mEntryBlocks[fi]->mTrueJump; + block->mFalseJump = mEntryBlocks[fi]->mFalseJump; + for (int i = 0; i < n; i++) + block->mInstructions.Push(mEntryBlocks[fi]->mInstructions[mEntryBlocks[fi]->mInstructions.Size() - n + i]); + + InterInstruction* bins = mEntryBlocks[fi]->mInstructions.Last(); + + i = 0; + while (i < mEntryBlocks.Size()) + { + if (mEntryBlocks[i] == mLoopPrefix) + i++; + else + { + mEntryBlocks[i]->mInstructions.SetSize(mEntryBlocks[i]->mInstructions.Size() - n); + mEntryBlocks[i]->mFalseJump = nullptr; + mEntryBlocks[i]->mTrueJump = block; + InterInstruction* jins = new InterInstruction(bins->mLocation, IC_JUMP); + mEntryBlocks[i]->mInstructions.Push(jins); + block->mEntryBlocks.Push(mEntryBlocks[i]); + block->mNumEntries++; + mEntryBlocks.Remove(i); + } + } + + mNumEntries = 2; + mEntryBlocks.Push(block); + + modified = true; + } + } + + if (mTrueJump && mTrueJump->MergeLoopTails()) + modified = true; + if (mFalseJump && mFalseJump->MergeLoopTails()) + modified = true; + } + + return modified; +} + +bool IsSingleLoopAssign(int at, InterCodeBasicBlock* block, const ExpandingArray& body) +{ + InterInstruction* ai = block->mInstructions[at]; + if (ai->mDst.mTemp < 0) + return true; + if (block->IsTempReferencedInRange(0, at, ai->mDst.mTemp)) + return false; + if (block->IsTempModifiedInRange(at + 1, block->mInstructions.Size(), ai->mDst.mTemp)) + return false; + for (int i = 1; i < body.Size(); i++) + if (body[i]->IsTempModified(ai->mDst.mTemp)) + return false; + return true; +} + +bool IsLoopInvariantTemp(int tmp, const ExpandingArray& body) +{ + if (tmp < 0) + return true; + + for (int i = 0; i < body.Size(); i++) + if (body[i]->IsTempModified(tmp)) + return false; + return true; +} + +static bool IsSimpleFactor(int64 val) +{ + return (val == 1 || val == 2 || val == 4 || val == 8); +} + + +void InterCodeBasicBlock::LimitLoopIndexIntegerRangeSets(void) +{ + if (!mVisited) + { + if (mLoopHead && mEntryBlocks.Size() == 2) + { + InterCodeBasicBlock* tail; + + if (mEntryBlocks[0] == mLoopPrefix) + tail = mEntryBlocks[1]; + else + tail = mEntryBlocks[0]; + + // Inner exit loop + ExpandingArray body; + + if (CollectSingleEntryGenericLoop(body)) + { + ExpandingArray exits; + for (int i = 0; i < body.Size(); i++) + { + if (body[i]->mTrueJump && !body.Contains(body[i]->mTrueJump) || body[i]->mFalseJump && !(body.Contains(body[i]->mFalseJump))) + exits.Push(body[i]); + } + + if (exits.Size() == 1) + { + InterCodeBasicBlock* eblock = exits[0]; + + int esz = eblock->mInstructions.Size(); + if (esz >= 2) + { + InterInstruction* ci = eblock->mInstructions[esz - 2]; + InterInstruction* bi = eblock->mInstructions[esz - 1]; + + if (ci->mCode == IC_RELATIONAL_OPERATOR && ( + ci->mSrc[0].mTemp < 0 && ci->mSrc[1].mTemp >= 0 && IsIntegerType(ci->mSrc[1].mType) || + ci->mSrc[1].mTemp < 0 && ci->mSrc[0].mTemp >= 0 && IsIntegerType(ci->mSrc[0].mType)) && + bi->mCode == IC_BRANCH && bi->mSrc[0].mTemp == ci->mDst.mTemp) + { + // single compare + int icsrc = ci->mSrc[0].mTemp >= 0 ? 0 : 1; + int itemp = ci->mSrc[icsrc].mTemp; + + if (mDominator->mTrueValueRange[itemp].IsConstant()) + { + // Find increment in tail + int i = 0; + while (i < tail->mInstructions.Size() && tail->mInstructions[i]->mDst.mTemp != itemp) + i++; + if (i < tail->mInstructions.Size()) + { + InterInstruction* ai = tail->mInstructions[i]; + if ((ai->mOperator == IA_ADD || ai->mOperator == IA_SUB) && (ai->mSrc[0].mTemp == itemp && ai->mSrc[1].mTemp < 0 || ai->mSrc[1].mTemp == itemp && ai->mSrc[0].mTemp < 0)) + { + int j = 0; + while (j < body.Size() && (body[j] == tail || !body[j]->IsTempModified(itemp))) + j++; + if (j == body.Size() && !tail->IsTempModifiedInRange(0, i, itemp) && !tail->IsTempModifiedInRange(i + 1, tail->mInstructions.Size(), itemp)) + { + int iasrc = ai->mSrc[0].mTemp >= 0 ? 0 : 1; + + int64 istart = mDominator->mTrueValueRange[itemp].mMaxValue; + int64 iinc, iend; + if (ai->mOperator == IA_ADD) + iinc = ai->mSrc[1 - iasrc].mIntConst; + else + iinc = -ai->mSrc[1 - iasrc].mIntConst; + + InterOperator op; + if (ci->mSrc[0].mTemp < 0) + { + iend = ci->mSrc[0].mIntConst; + op = ci->mOperator; + } + else + { + iend = ci->mSrc[1].mIntConst; + op = MirrorRelational(ci->mOperator); + } + + if (body.Contains(eblock->mFalseJump)) + op = InvertRelational(op); + +// printf("Collected loop %s:%d, %d, %d, %d, [%d %d+%d!=%d] %d (%d..%d)\n", mProc->mIdent->mString, mIndex, body.Size(), eblock->mIndex, tail->mIndex, itemp, int(istart), int(iinc), int(iend), op, int(ai->mSrc[iasrc].mRange.mMinValue), int(ai->mSrc[iasrc].mRange.mMaxValue)); + + if (istart < iend && iinc > 0) // counting up + { + if (op == IA_CMPNE) + { + if ((iend - istart) % iinc == 0) + { + ai->mSrc[iasrc].mRange.LimitMin(istart); + ai->mSrc[iasrc].mRange.LimitMax(iend - iinc); + } + } + } + } + } + } + } + } + } + } + } + + } + + mVisited = true; + + if (mTrueJump) mTrueJump->LimitLoopIndexIntegerRangeSets(); + if (mFalseJump) mFalseJump->LimitLoopIndexIntegerRangeSets(); + } +} + +void InterCodeBasicBlock::LimitLoopIndexRanges(void) +{ + if (!mVisited) + { + mVisited = true; + + if (mLoopHead && mEntryBlocks.Size() == 2) + { + InterCodeBasicBlock* tail, * post; + + if (mEntryBlocks[0] == mLoopPrefix) + tail = mEntryBlocks[1]; + else + tail = mEntryBlocks[0]; + + if (tail->mTrueJump == this) + post = tail->mFalseJump; + else + post = tail->mTrueJump; + + if (post && post->mNumEntries == 1) + { + ExpandingArray body; + + if (tail->CollectSingleHeadLoopBody(this, tail, body)) + { + int tz = tail->mInstructions.Size(); + if (tz > 2) + { + InterInstruction* ai = tail->mInstructions[tz - 3]; + InterInstruction* ci = tail->mInstructions[tz - 2]; + InterInstruction* bi = tail->mInstructions[tz - 1]; + + if (ai->mCode == IC_BINARY_OPERATOR && ai->mOperator == IA_ADD && ai->mSrc[0].mTemp < 0 && ai->mDst.mTemp == ai->mSrc[1].mTemp && ai->mSrc[0].mIntConst > 1 && IsIntegerType(ai->mDst.mType) && + ci->mCode == IC_RELATIONAL_OPERATOR && ci->mOperator == IA_CMPLU && ci->mSrc[0].mTemp < 0 && ci->mSrc[1].mTemp == ai->mDst.mTemp && + bi->mCode == IC_BRANCH && bi->mSrc[0].mTemp == ci->mDst.mTemp && !post->mEntryRequiredTemps[ai->mDst.mTemp] && + !tail->IsTempModifiedInRange(0, tz - 3, ai->mDst.mTemp)) + { + int i = 0; + while (i + 1 < body.Size() && !body[i]->IsTempModifiedInRange(0, body[i]->mInstructions.Size(), ai->mDst.mTemp)) + i++; + if (i + 1 == body.Size()) + { + InterInstruction* si = FindSourceInstruction(mLoopPrefix, ai->mDst.mTemp); + if (si && si->mCode == IC_CONSTANT && si->mConst.mIntConst < ci->mSrc[0].mIntConst) + { + int64 climit = ((ci->mSrc[0].mIntConst - si->mConst.mIntConst + ai->mSrc[0].mIntConst - 1) / ai->mSrc[0].mIntConst - 1) * ai->mSrc[0].mIntConst + si->mConst.mIntConst; + if (climit + 1 != ci->mSrc[0].mIntConst) + { +// printf("LimitLoopIndex %s:%d, %d..%d by %d to %d\n", mProc->mIdent->mString, mIndex, int(si->mConst.mIntConst), int(ci->mSrc[0].mIntConst), int(ai->mSrc[0].mIntConst), int(climit + 1)); + ci->mSrc[0].mIntConst = climit + 1; + } + } + } + } + } + } + } + + } + + if (mTrueJump) mTrueJump->LimitLoopIndexRanges(); + if (mFalseJump) mFalseJump->LimitLoopIndexRanges(); + } + +} + +bool InterCodeBasicBlock::SingleTailLoopOptimization(const NumberSet& aliasedParams, const GrowingVariableArray& staticVars) +{ + bool modified = false; + + if (!mVisited) + { + mVisited = true; + + if (mLoopHead && mEntryBlocks.Size() == 2) + { + InterCodeBasicBlock* tail, *post; + + if (mEntryBlocks[0] == mLoopPrefix) + tail = mEntryBlocks[1]; + else + tail = mEntryBlocks[0]; + + if (tail->mTrueJump == this) + post = tail->mFalseJump; + else + post = tail->mTrueJump; + + if (post && post->mNumEntries == 1) + { + ExpandingArray body; + + if (tail->CollectSingleHeadLoopBody(this, tail, body)) + { + int tz = tail->mInstructions.Size(); + +#if 1 + if (tz > 2) + { + InterInstruction* ai = tail->mInstructions[tz - 3]; + InterInstruction* ci = tail->mInstructions[tz - 2]; + InterInstruction* bi = tail->mInstructions[tz - 1]; + + if (ai->mCode == IC_BINARY_OPERATOR && ai->mOperator == IA_ADD && ai->mSrc[0].mTemp < 0 && ai->mDst.mTemp == ai->mSrc[1].mTemp && ai->mSrc[0].mIntConst > 0 && IsIntegerType(ai->mDst.mType) && + ci->mCode == IC_RELATIONAL_OPERATOR && ci->mOperator == IA_CMPLU && ci->mSrc[0].mTemp < 0 && ci->mSrc[1].mTemp == ai->mDst.mTemp && + bi->mCode == IC_BRANCH && bi->mSrc[0].mTemp == ci->mDst.mTemp && !post->mEntryRequiredTemps[ai->mDst.mTemp] && + !tail->IsTempReferencedInRange(0, tz - 3, ai->mDst.mTemp)) + { + int i = 0; + while (i + 1 < body.Size() && !body[i]->IsTempReferencedInRange(0, body[i]->mInstructions.Size(), ai->mDst.mTemp)) + i++; + if (i + 1 == body.Size()) + { + InterInstruction* si = FindSourceInstruction(mLoopPrefix, ai->mDst.mTemp); + if (si && si->mCode == IC_CONSTANT) + { + int64 num = (ci->mSrc[0].mIntConst - si->mConst.mIntConst + ai->mSrc[0].mIntConst - 1) / ai->mSrc[0].mIntConst; + if (num > 255 && num < 32768) + { + ai->mSrc[0].mIntConst = 1; + + ci->mOperator = IA_CMPNE; + ci->mSrc[0].mIntConst = 0; + ci->mSrc[0].mRange.SetLimit(0, 0); + + InterInstruction* mins = new InterInstruction(si->mLocation, IC_CONSTANT); + mins->mConst.mType = ai->mDst.mType; + mins->mConst.mIntConst = -num; + mins->mDst = ai->mDst; + mins->mDst.mRange.SetLimit(-num, -num); + mLoopPrefix->mInstructions.Insert(mLoopPrefix->mInstructions.Size() - 1, mins); + + for (int k = 0; k < body.Size(); k++) + { + if (body[k]->mEntryValueRange.Size()) + body[k]->mEntryValueRange[ai->mSrc[1].mTemp].SetLimit(-num, -1); + } + + ai->mSrc[1].mRange.SetLimit(-num, -1); + ai->mDst.mRange.SetLimit(-num + 1, 0); + ci->mSrc[1].mRange.SetLimit(-num + 1, 0); + } + else if (num > 0) + { + ai->mOperator = IA_SUB; + ai->mSrc[0].mIntConst = 1; + ci->mOperator = IA_CMPGU; + ci->mSrc[0].mIntConst = 0; + ci->mSrc[0].mRange.SetLimit(0, 0); + + InterInstruction* mins = new InterInstruction(si->mLocation, IC_CONSTANT); + mins->mConst.mType = ai->mDst.mType; + mins->mConst.mIntConst = num; + mins->mDst = ai->mDst; + mins->mDst.mRange.SetLimit(num, num); + mLoopPrefix->mInstructions.Insert(mLoopPrefix->mInstructions.Size() - 1, mins); + + for (int k = 0; k < body.Size(); k++) + { + if (body[k]->mEntryValueRange.Size()) + body[k]->mEntryValueRange[ai->mSrc[1].mTemp].SetLimit(1, num); + } + + if (mEntryValueRange.Size()) + mEntryValueRange[ai->mSrc[1].mTemp].SetLimit(1, num); + + ai->mSrc[1].mRange.SetLimit(1, num); + ai->mDst.mRange.SetLimit(0, num - 1); + ci->mSrc[1].mRange.SetLimit(0, num - 1); + } + + modified = true; + } + } + } + else if (ai->mCode == IC_BINARY_OPERATOR && ai->mOperator == IA_ADD && ai->mSrc[0].mTemp < 0 && ai->mDst.mTemp == ai->mSrc[1].mTemp && ai->mSrc[0].mIntConst == 1 && IsIntegerType(ai->mDst.mType) && + ci->mCode == IC_RELATIONAL_OPERATOR && ci->mOperator == IA_CMPLU && ci->mSrc[0].mTemp >= 0 && ci->mSrc[0].IsPositive() && ci->mSrc[1].mTemp == ai->mDst.mTemp && + bi->mCode == IC_BRANCH && bi->mSrc[0].mTemp == ci->mDst.mTemp && !post->mEntryRequiredTemps[ai->mDst.mTemp] && + !tail->IsTempReferencedInRange(0, tz - 3, ai->mDst.mTemp) && !tail->IsTempModifiedInRange(0, tz - 3, ci->mSrc[0].mTemp)) + { + int i = 0; + while (i + 1 < body.Size() && + !body[i]->IsTempReferenced(ai->mDst.mTemp) && + !body[i]->IsTempModified(ci->mSrc[0].mTemp)) + i++; + if (i + 1 == body.Size()) + { + InterInstruction* si = FindSourceInstruction(mLoopPrefix, ai->mDst.mTemp); + if (si && si->mCode == IC_CONSTANT && si->mConst.mIntConst >= 0) + { + int64 num = ci->mSrc[0].mRange.mMaxValue - si->mConst.mIntConst; + IntegerValueRange::State bound = ci->mSrc[0].mRange.mMaxState; + + InterInstruction* mins = new InterInstruction(si->mLocation, IC_BINARY_OPERATOR); + mins->mOperator = IA_SUB; + mins->mSrc[0] = si->mConst; + mins->mSrc[1] = ci->mSrc[0]; + mins->mDst = ai->mDst; + mins->mDst.mRange.SetBounds(IntegerValueRange::S_BOUND, 1, bound, num); + + mLoopPrefix->mInstructions.Insert(mLoopPrefix->mInstructions.Size() - 1, mins); + + ai->mOperator = IA_SUB; + ai->mSrc[0].mIntConst = 1; + ci->mOperator = IA_CMPGU; + ci->mSrc[0].mTemp = -1; + ci->mSrc[0].mIntConst = 0; + ci->mSrc[0].mRange.SetLimit(0, 0); + + for (int k = 0; k < body.Size(); k++) + { + if (body[k]->mEntryValueRange.Size()) + body[k]->mEntryValueRange[ai->mSrc[1].mTemp].SetBounds(IntegerValueRange::S_BOUND, 1, bound, num); + } + + if (mEntryValueRange.Size()) + mEntryValueRange[ai->mSrc[1].mTemp].SetBounds(IntegerValueRange::S_BOUND, 1, bound, num); + + ai->mSrc[1].mRange.SetBounds(IntegerValueRange::S_BOUND, 1, bound, num); + ai->mDst.mRange.SetBounds(IntegerValueRange::S_BOUND, 0, bound, num - 1); + ci->mSrc[1].mRange.SetBounds(IntegerValueRange::S_BOUND, 0, bound, num - 1); + + modified = true; + } + } + } + } +#endif +#if 1 + GrowingIntArray indexScale(0); + + if (!modified) + { + + + int tz = tail->mInstructions.Size(); + for (int i = 0; i < tz; i++) + { + InterInstruction* ai = tail->mInstructions[i]; + if (ai->mCode == IC_BINARY_OPERATOR && ai->mOperator == IA_ADD && ai->mSrc[0].mTemp < 0 && ai->mDst.mTemp == ai->mSrc[1].mTemp && ai->mSrc[0].mIntConst != 0 && IsIntegerType(ai->mDst.mType) && + !tail->IsTempModifiedInRange(i + 1, tz, ai->mDst.mTemp) && + !tail->IsTempModifiedInRange(0, i - 1, ai->mDst.mTemp)) + { + int j = 0; + while (j + 1 < body.Size() && !body[j]->IsTempModified(ai->mDst.mTemp)) + j++; + if (j + 1 == body.Size()) + { + indexScale[ai->mDst.mTemp] = (int)ai->mSrc[0].mIntConst; + } + } + } + } + + bool hasStore = false; + for (int j = 0; j < body.Size(); j++) + { + int sz = body[j]->mInstructions.Size(); + for (int i = 0; i < sz; i++) + { + InterInstruction* ins = body[j]->mInstructions[i]; + if (IsObservable(ins->mCode)) + hasStore = true; + } + } + + int i = 0; + while (i < mInstructions.Size()) + { + InterInstruction* lins = mInstructions[i]; + + if (lins->mCode == IC_BINARY_OPERATOR) + { + if (lins->mOperator == IA_MUL && lins->mSrc[0].mTemp < 0 && (lins->mDst.IsNotUByte() || !IsSimpleFactor(lins->mSrc[0].mIntConst)) && lins->mSrc[1].mTemp >= 0 && indexScale[lins->mSrc[1].mTemp] != 0 && IsSingleLoopAssign(i, this, body)) + { + mLoopPrefix->mInstructions.Insert(mLoopPrefix->mInstructions.Size() - 1, lins); + mLoopPrefix->mExitRequiredTemps += lins->mDst.mTemp; + mEntryRequiredTemps += lins->mDst.mTemp; + tail->mExitRequiredTemps += lins->mDst.mTemp; + tail->mEntryRequiredTemps += lins->mDst.mTemp; + mInstructions.Remove(i); + + InterInstruction* ains = new InterInstruction(lins->mLocation, IC_BINARY_OPERATOR); + ains->mOperator = IA_ADD; + ains->mDst = lins->mDst; + ains->mSrc[1] = lins->mDst; + ains->mSrc[0] = lins->mSrc[0]; + ains->mSrc[0].mIntConst *= indexScale[lins->mSrc[1].mTemp]; + ains->mDst.mRange.AddConstValue(ains->mDst.mType, ains->mSrc[0].mIntConst); + tail->AppendBeforeBranch(ains); + + indexScale[ains->mDst.mTemp] = (int)ains->mSrc[0].mIntConst; + + modified = true; + continue; + } + else if (lins->mOperator == IA_ADD && lins->mSrc[0].mTemp >= 0 && indexScale[lins->mSrc[0].mTemp] != 0 && IsSingleLoopAssign(i, this, body)) + { + if (i + 1 < mInstructions.Size() && mInstructions[i + 1]->mCode == IC_LEA && mInstructions[i + 1]->mSrc[0].mTemp == lins->mDst.mTemp) + ; + else if (lins->mSrc[1].mTemp < 0 || IsLoopInvariantTemp(lins->mSrc[1].mTemp, body)) + { + assert(IsIntegerType(lins->mDst.mType)); + + int s = indexScale[lins->mSrc[0].mTemp]; + + mLoopPrefix->mInstructions.Insert(mLoopPrefix->mInstructions.Size() - 1, lins); + mLoopPrefix->mExitRequiredTemps += lins->mDst.mTemp; + mEntryRequiredTemps += lins->mDst.mTemp; + tail->mExitRequiredTemps += lins->mDst.mTemp; + tail->mEntryRequiredTemps += lins->mDst.mTemp; + mInstructions.Remove(i); + + InterInstruction* ains = new InterInstruction(lins->mLocation, IC_BINARY_OPERATOR); + ains->mOperator = IA_ADD; + ains->mDst = lins->mDst; + ains->mSrc[1] = lins->mDst; + ains->mSrc[0].mType = lins->mDst.mType; + ains->mSrc[0].mTemp = -1; + ains->mSrc[0].mIntConst = s; + tail->AppendBeforeBranch(ains); + + if (s > 0) + ains->mDst.mRange.mMaxValue += s; + else + ains->mDst.mRange.mMinValue += s; + + indexScale[ains->mDst.mTemp] = s; + + modified = true; + continue; + } + } + else if ((lins->mOperator == IA_ADD || lins->mOperator == IA_SUB) && lins->mSrc[1].mTemp >= 0 && lins->mSrc[1].mTemp != lins->mDst.mTemp && + indexScale[lins->mSrc[1].mTemp] != 0 && IsSingleLoopAssign(i, this, body)) + { + if (i + 1 < mInstructions.Size() && mInstructions[i + 1]->mCode == IC_LEA && mInstructions[i + 1]->mSrc[0].mTemp == lins->mDst.mTemp) + ; + else if (lins->mSrc[0].mTemp < 0 || IsLoopInvariantTemp(lins->mSrc[0].mTemp, body)) + { + assert(IsIntegerType(lins->mDst.mType)); + + int s = indexScale[lins->mSrc[1].mTemp]; + + mLoopPrefix->mInstructions.Insert(mLoopPrefix->mInstructions.Size() - 1, lins); + mLoopPrefix->mExitRequiredTemps += lins->mDst.mTemp; + mEntryRequiredTemps += lins->mDst.mTemp; + tail->mExitRequiredTemps += lins->mDst.mTemp; + tail->mEntryRequiredTemps += lins->mDst.mTemp; + mInstructions.Remove(i); + + InterInstruction* ains = new InterInstruction(lins->mLocation, IC_BINARY_OPERATOR); + ains->mOperator = IA_ADD; + ains->mDst = lins->mDst; + ains->mSrc[1] = lins->mDst; + ains->mSrc[0].mType = lins->mDst.mType; + ains->mSrc[0].mTemp = -1; + ains->mSrc[0].mIntConst = s; + tail->AppendBeforeBranch(ains); + ains->mDst.mRange.AddConstValue(ains->mDst.mType, s); + + indexScale[ains->mDst.mTemp] = s; + + modified = true; + continue; + } + } + } + else if (lins->mCode == IC_CONVERSION_OPERATOR && lins->mOperator == IA_EXT8TO16U && i + 1 < mInstructions.Size() && indexScale[lins->mSrc[0].mTemp] != 0 && IsSingleLoopAssign(i, this, body)) + { + InterInstruction* nins = mInstructions[i + 1]; + + if (nins->mCode == IC_BINARY_OPERATOR) + { + if (nins->mOperator == IA_MUL && nins->mSrc[0].mTemp < 0 && (nins->mDst.IsNotUByte() || !IsSimpleFactor(nins->mSrc[0].mIntConst)) && nins->mSrc[1].mTemp == lins->mDst.mTemp && nins->mSrc[1].mFinal && IsSingleLoopAssign(i + 1, this, body)) + { + mLoopPrefix->mInstructions.Insert(mLoopPrefix->mInstructions.Size() - 1, lins); + mLoopPrefix->mInstructions.Insert(mLoopPrefix->mInstructions.Size() - 1, nins); + mLoopPrefix->mExitRequiredTemps += nins->mDst.mTemp; + mEntryRequiredTemps += nins->mDst.mTemp; + tail->mExitRequiredTemps += nins->mDst.mTemp; + tail->mEntryRequiredTemps += nins->mDst.mTemp; + mInstructions.Remove(i); + mInstructions.Remove(i); + + InterInstruction* ains = new InterInstruction(nins->mLocation, IC_BINARY_OPERATOR); + ains->mOperator = IA_ADD; + ains->mDst = nins->mDst; + ains->mSrc[1] = nins->mDst; + ains->mSrc[0] = nins->mSrc[0]; + ains->mSrc[0].mIntConst *= indexScale[lins->mSrc[0].mTemp]; + tail->AppendBeforeBranch(ains); + ains->mDst.mRange.AddConstValue(ains->mDst.mType, ains->mSrc[0].mIntConst); + + indexScale[ains->mDst.mTemp] = (int)ains->mSrc[0].mIntConst; + + modified = true; + continue; + } + } +#if 1 + if (nins->mCode == IC_LEA && nins->mSrc[0].mTemp == lins->mDst.mTemp) + { + + } + else if (nins->mCode == IC_BINARY_OPERATOR && nins->mOperator == IA_ADD && nins->mSrc[0].mTemp == lins->mDst.mTemp && !nins->mDst.IsNotUByte()) + { + } + else if (lins->mSrc[0].IsUByte()) // ensure no overflow + { + int s = indexScale[lins->mSrc[0].mTemp]; + + mLoopPrefix->mInstructions.Insert(mLoopPrefix->mInstructions.Size() - 1, lins); + mLoopPrefix->mExitRequiredTemps += lins->mDst.mTemp; + mEntryRequiredTemps += lins->mDst.mTemp; + tail->mExitRequiredTemps += lins->mDst.mTemp; + tail->mEntryRequiredTemps += lins->mDst.mTemp; + mInstructions.Remove(i); + + InterInstruction* ains = new InterInstruction(lins->mLocation, IC_BINARY_OPERATOR); + ains->mOperator = IA_ADD; + ains->mDst = lins->mDst; + ains->mSrc[1] = lins->mDst; + ains->mSrc[0].mType = lins->mDst.mType; + ains->mSrc[0].mTemp = -1; + ains->mSrc[0].mIntConst = s; + tail->AppendBeforeBranch(ains, true); + ains->mDst.mRange.AddConstValue(ains->mDst.mType, s); + + indexScale[ains->mDst.mTemp] = s; + + modified = true; + continue; + } +#endif + } + else if (lins->mCode == IC_LEA) + { + if (lins->mSrc[0].mTemp >= 0 && !lins->mSrc[0].IsUByte() && indexScale[lins->mSrc[0].mTemp] != 0 && IsSingleLoopAssign(i, this, body) && IsLoopInvariantTemp(lins->mSrc[1].mTemp, body)) + { + mLoopPrefix->mInstructions.Insert(mLoopPrefix->mInstructions.Size() - 1, lins); + mLoopPrefix->mExitRequiredTemps += lins->mDst.mTemp; + mEntryRequiredTemps += lins->mDst.mTemp; + tail->mExitRequiredTemps += lins->mDst.mTemp; + tail->mEntryRequiredTemps += lins->mDst.mTemp; + mInstructions.Remove(i); + + InterInstruction* ains = new InterInstruction(lins->mLocation, IC_LEA); + ains->mDst = lins->mDst; + ains->mSrc[1] = lins->mDst; + ains->mSrc[0].mTemp = -1; + ains->mSrc[0].mType = IT_INT16; + ains->mSrc[0].mIntConst = indexScale[lins->mSrc[0].mTemp]; + + if (IsTempModifiedInRange(0, i, lins->mSrc[0].mTemp)) + { + lins->mSrc[0].mRange.AddConstValue(lins->mSrc[0].mType, -indexScale[lins->mSrc[0].mTemp]); + lins->mDst.mRange.AddConstValue(IT_INT16, -indexScale[lins->mSrc[0].mTemp]); + ains->mSrc[1].mRange = lins->mDst.mRange; + mInstructions.Insert(i, ains); + } + else + { + tail->AppendBeforeBranch(ains); + if (post->mEntryRequiredTemps[lins->mDst.mTemp]) + { + InterInstruction* rains = new InterInstruction(lins->mLocation, IC_LEA); + rains->mDst = lins->mDst; + rains->mSrc[1] = lins->mDst; + rains->mSrc[0].mTemp = -1; + rains->mSrc[0].mType = IT_INT16; + rains->mSrc[0].mIntConst = -indexScale[lins->mSrc[0].mTemp]; + + post->mInstructions.Insert(0, rains); + } + } + + modified = true; + continue; + } + } + + if (lins->mCode == IC_BINARY_OPERATOR || lins->mCode == IC_CONSTANT || lins->mCode == IC_UNARY_OPERATOR || + lins->mCode == IC_CONVERSION_OPERATOR || lins->mCode == IC_SELECT || + lins->mCode == IC_LEA || + lins->mCode == IC_RELATIONAL_OPERATOR || (lins->mCode == IC_LOAD && !hasStore && !lins->mVolatile)) + { +#if 1 + if (CanMoveInstructionBeforeBlock(i) && !IsInsModifiedInRange(i + 1, mInstructions.Size(), lins) && !tail->IsInsModified(lins) && !lins->UsesTemp(lins->mDst.mTemp)) + { + int j = 1; + while (j < body.Size() && !body[j]->IsInsModified(lins)) + j++; + if (j == body.Size()) + { + mLoopPrefix->mInstructions.Insert(mLoopPrefix->mInstructions.Size() - 1, lins); + mLoopPrefix->mExitRequiredTemps += lins->mDst.mTemp; + mEntryRequiredTemps += lins->mDst.mTemp; + mInstructions.Remove(i); + i--; + + modified = true; + } + } +#endif + } + else if (lins->mCode == IC_LOAD && !lins->mVolatile && lins->mSrc[0].mTemp < 0) + { + if (CanMoveInstructionBeforeBlock(i)) + { + int j = tail->mInstructions.Size() - 1; + while (j >= 0 && !IsMatchingStore(lins, tail->mInstructions[j])) + j--; + + if (j >= 0 && !tail->mExitRequiredTemps[lins->mDst.mTemp]) + { + InterInstruction* sins = tail->mInstructions[j]; + + if (tail->CanMoveInstructionBehindBlock(j)) + { + if (!CollidingMem(this, lins, i + 1, mInstructions.Size()) && + !CollidingMem(tail, lins, 0, j)) + { +#if 1 + int k = 1; + while (k + 1 < body.Size() && !CollidingMem(body[k], lins, 0, body[k]->mInstructions.Size())) + k++; + + if (k + 1 == body.Size()) + { + if (sins->mSrc[0].mTemp >= 0) + tail->mExitRequiredTemps += sins->mSrc[0].mTemp; + + post->mInstructions.Insert(0, sins); + tail->mInstructions.Remove(j); + + if (sins->mSrc[0].mTemp != lins->mDst.mTemp) + { + InterInstruction* mins; + + if (sins->mSrc[0].mTemp >= 0) + { + mins = new InterInstruction(sins->mLocation, IC_LOAD_TEMPORARY); + mins->mSrc[0] = sins->mSrc[0]; + } + else + { + mins = new InterInstruction(sins->mLocation, IC_CONSTANT); + mins->mConst = sins->mSrc[0]; + } + + mins->mDst = lins->mDst; + tail->mExitRequiredTemps += mins->mDst.mTemp; + tail->mInstructions.Insert(tail->mInstructions.Size() - 1, mins); + } + + mLoopPrefix->mInstructions.Insert(mLoopPrefix->mInstructions.Size() - 1, lins); + mLoopPrefix->mExitRequiredTemps += lins->mDst.mTemp; + mEntryRequiredTemps += lins->mDst.mTemp; + mInstructions.Remove(i); + i--; + + modified = true; + } +#endif + } + } + } + else + { + int k = 0; + while (k < body.Size() && !DestroyingMem(body[k], lins, 0, body[k]->mInstructions.Size())) + k++; + if (k == body.Size()) + { +#if 1 + if (!IsInsModifiedInRange(i + 1, mInstructions.Size(), lins) && !tail->IsInsModified(lins)) + { + int j = 1; + while (j < body.Size() && !body[j]->IsInsModified(lins)) + j++; + if (j == body.Size()) + { + mLoopPrefix->mInstructions.Insert(mLoopPrefix->mInstructions.Size() - 1, lins); + mLoopPrefix->mExitRequiredTemps += lins->mDst.mTemp; + mEntryRequiredTemps += lins->mDst.mTemp; + mInstructions.Remove(i); + i--; + + modified = true; + } + } +#endif + } + } + } + } + + i++; + } +#if 1 + if (modified) + { + for (int j = 0; j < indexScale.Size(); j++) + { + if (indexScale[j] != 0 && !post->mEntryRequiredTemps[j]) + { + int k = 0; + int tz = tail->mInstructions.Size(); + while (k < tz && tail->mInstructions[k]->mDst.mTemp != j) + k++; + + if (k < tz && !tail->IsTempReferencedInRange(0, k, j) && !tail->IsTempReferencedInRange(k + 1, tz, j)) + { + int bi = 0; + while (bi + 1 < body.Size() && !body[bi]->IsTempReferenced(j)) + bi++; + if (bi + 1 == body.Size()) + { + tail->mInstructions.Remove(k); + } + } + } + } + } +#endif +#endif + } + } + } + + if (mTrueJump && mTrueJump->SingleTailLoopOptimization(aliasedParams, staticVars)) + modified = true; + if (mFalseJump && mFalseJump->SingleTailLoopOptimization(aliasedParams, staticVars)) + modified = true; + } + + return modified; +} + +bool InterCodeBasicBlock::EmptyLoopOptimization(void) +{ + bool modified = false; + + if (!mVisited) + { + mVisited = true; + + if (mLoopHead && mNumEntries == 2 && (mTrueJump == this || mFalseJump == this)) + { + if (mInstructions.Size() == 3) + { + InterInstruction* ai = mInstructions[0]; + InterInstruction* ci = mInstructions[1]; + InterInstruction* bi = mInstructions[2]; + + if (ai->mCode == IC_BINARY_OPERATOR && ai->mOperator == IA_ADD && ai->mSrc[0].mTemp < 0 && ai->mDst.mTemp == ai->mSrc[1].mTemp && ai->mSrc[0].mIntConst == 1 && IsIntegerType(ai->mDst.mType) && + ci->mCode == IC_RELATIONAL_OPERATOR && ci->mOperator == IA_CMPLU && ci->mSrc[1].mTemp == ai->mDst.mTemp && + bi->mCode == IC_BRANCH && bi->mSrc[0].mTemp == ci->mDst.mTemp) + { + InterInstruction* mi = nullptr; + + InterInstruction* si = FindSourceInstruction(mLoopPrefix, ai->mSrc[1].mTemp); + if (si && si->mDst.mRange.mMaxState == IntegerValueRange::S_BOUND && + ci->mSrc[0].mRange.mMinState == IntegerValueRange::S_BOUND && + si->mDst.mRange.mMaxValue < ci->mSrc[0].mRange.mMinValue) + { + if (ci->mSrc[0].mTemp >= 0) + { + mi = new InterInstruction(ai->mLocation, IC_LOAD_TEMPORARY); + mi->mDst = ai->mDst; + mi->mSrc[0] = ci->mSrc[0]; + mi->mDst.mRange = mi->mSrc[0].mRange; + } + } + + if (mi) + { + mInstructions.SetSize(0); + mInstructions.Push(mi); + + if (mFalseJump != this) + mTrueJump = mFalseJump; + mFalseJump = nullptr; + mLoopHead = false; + mNumEntries = 1; + mEntryBlocks.SetSize(0); + mEntryBlocks.Push(mLoopPrefix); + + InterInstruction* ins = new InterInstruction(mi->mLocation, IC_JUMP); + mInstructions.Push(ins); + + modified = true; + } + } + } + } + + if (mTrueJump && mTrueJump->EmptyLoopOptimization()) + modified = true; + if (mFalseJump && mFalseJump->EmptyLoopOptimization()) + modified = true; + } + + return modified; +} + +void InterCodeBasicBlock::ConstInnerLoopOptimization(void) +{ + if (!mVisited) + { + if (mLoopHead && mEntryBlocks.Size() == 2) + { + InterCodeBasicBlock* tail; + + if (mEntryBlocks[0] == mLoopPrefix) + tail = mEntryBlocks[1]; + else + tail = mEntryBlocks[0]; + + // Inner exit loop + ExpandingArray body; + + if (CollectSingleEntryGenericLoop(body)) + { + ExpandingArray exits; + for (int i = 0; i < body.Size(); i++) + { + if (body[i]->mTrueJump && !body.Contains(body[i]->mTrueJump) || body[i]->mFalseJump && !(body.Contains(body[i]->mFalseJump))) + exits.Push(body[i]); + } + + if (exits.Size() == 1) + { + InterCodeBasicBlock* eblock = exits[0]; + NumberSet cset(mEntryRequiredTemps.Size()), mset(mEntryRequiredTemps.Size()); + + InterCodeBasicBlock* pblock = mLoopPrefix; + while (pblock) + { + for (int i = pblock->mInstructions.Size() - 1; i >= 0; i--) + { + const InterInstruction* ins(pblock->mInstructions[i]); + if (ins->mDst.mTemp >= 0) + { + if (ins->mCode == IC_CONSTANT && !mset[ins->mDst.mTemp]) + cset += ins->mDst.mTemp; + mset += ins->mDst.mTemp; + } + } + if (pblock->mEntryBlocks.Size() == 1) + pblock = pblock->mEntryBlocks[0]; + else + pblock = nullptr; + } + + bool isconst = true; + for (int j = 0; isconst && j < body.Size(); j++) + { + InterCodeBasicBlock* block = body[j]; + + for (int i = 0; isconst && i < block->mInstructions.Size(); i++) + { + const InterInstruction* ins(block->mInstructions[i]); + + if (ins->mCode == IC_CONSTANT || ins->mCode == IC_BRANCH || + ins->mCode == IC_BINARY_OPERATOR || ins->mCode == IC_UNARY_OPERATOR || ins->mCode == IC_RELATIONAL_OPERATOR || ins->mCode == IC_LEA || + ins->mCode == IC_CONVERSION_OPERATOR) + { + int j = 0; + while (j < ins->mNumOperands && (ins->mSrc[j].mTemp < 0 || cset[ins->mSrc[j].mTemp])) + j++; + if (j == ins->mNumOperands) + { + if (ins->mDst.mTemp >= 0) + cset += ins->mDst.mTemp; + } + else + isconst = false; + } + else if (ins->mCode == IC_LOAD && (ins->mSrc[0].mTemp < 0 || cset[ins->mSrc[0].mTemp]) && + ins->mSrc[0].mMemoryBase == IM_GLOBAL && ins->mSrc[0].mLinkerObject && (ins->mSrc[0].mLinkerObject->mFlags & LOBJF_CONST)) + { + cset += ins->mDst.mTemp; + } + else if (ins->mCode == IC_JUMP) + ; + else + isconst = false; + } + } + + if (isconst) + { + ExpandingArray vars; + vars.SetSize(cset.Size()); + mset.Clear(); + + InterCodeBasicBlock* pblock = mLoopPrefix; + while (pblock) + { + for (int i = pblock->mInstructions.Size() - 1; i >= 0; i--) + { + const InterInstruction* ins(pblock->mInstructions[i]); + if (ins->mDst.mTemp >= 0) + { + if (ins->mCode == IC_CONSTANT && !mset[ins->mDst.mTemp]) + { + assert(ins->mConst.mType != IT_NONE); + vars[ins->mDst.mTemp] = ins->mConst; + } + mset += ins->mDst.mTemp; + } + } + if (pblock->mEntryBlocks.Size() == 1) + pblock = pblock->mEntryBlocks[0]; + else + pblock = nullptr; + } + + bool done = false; + int n = 0; + + mset.Clear(); + int ni = 0; + InterCodeBasicBlock* block = this; + + while (n < 20000 && !done) + { + n++; + const InterInstruction* ins(block->mInstructions[ni++]); + + switch (ins->mCode) + { + case IC_CONSTANT: + assert(ins->mConst.mType != IT_NONE); + vars[ins->mDst.mTemp] = ins->mConst; + mset += ins->mDst.mTemp; + break; + case IC_BINARY_OPERATOR: + case IC_RELATIONAL_OPERATOR: + vars[ins->mDst.mTemp] = + OperandConstantFolding(ins->mOperator, + ins->mSrc[1].mTemp < 0 ? ins->mSrc[1] : vars[ins->mSrc[1].mTemp], + ins->mSrc[0].mTemp < 0 ? ins->mSrc[0] : vars[ins->mSrc[0].mTemp]); + mset += ins->mDst.mTemp; + break; + case IC_UNARY_OPERATOR: + case IC_CONVERSION_OPERATOR: + vars[ins->mDst.mTemp] = + OperandConstantFolding(ins->mOperator, + ins->mSrc[0].mTemp < 0 ? ins->mSrc[0] : vars[ins->mSrc[0].mTemp], + ins->mSrc[0].mTemp < 0 ? ins->mSrc[0] : vars[ins->mSrc[0].mTemp]); + mset += ins->mDst.mTemp; + break; + + case IC_LEA: + { + InterOperand op; + int64 index = ins->mSrc[1].mIntConst; + if (ins->mSrc[0].mTemp < 0) + index += ins->mSrc[0].mIntConst; + else + index += vars[ins->mSrc[0].mTemp].mIntConst; + + if (ins->mSrc[1].mTemp < 0) + op = ins->mSrc[1]; + else + { + op = vars[ins->mSrc[1].mTemp]; + index += op.mIntConst; + } + + op.mIntConst = index; + vars[ins->mDst.mTemp] = op; + mset += ins->mDst.mTemp; + } break; + case IC_LOAD: + { + InterOperand op; + if (ins->mSrc[0].mTemp < 0) + op = ins->mSrc[0]; + else + { + op = vars[ins->mSrc[0].mTemp]; + op.mIntConst += ins->mSrc[0].mIntConst; + } + + vars[ins->mDst.mTemp] = LoadConstantOperand(ins, op, ins->mDst.mType, mProc->mModule->mGlobalVars, mProc->mModule->mProcedures); + mset += ins->mDst.mTemp; + + } break; + + case IC_JUMP: + block = block->mTrueJump; + ni = 0; + break; + + case IC_BRANCH: + block = vars[ins->mSrc[0].mTemp].mIntConst ? block->mTrueJump : block->mFalseJump; + ni = 0; + if (!body.Contains(block)) + done = true; + break; + } + } + + if (done) + { + block->mEntryBlocks.RemoveAll(eblock); + block->mEntryBlocks.Push(this); + mTrueJump = block; + mFalseJump = nullptr; + mLoopHead = false; + mNumEntries = 1; + mEntryBlocks.SetSize(0); + mEntryBlocks.Push(mLoopPrefix); + + InterInstruction* last = mInstructions.Last(); + mInstructions.SetSize(0); + for (int i = 0; i < mset.Size(); i++) + { + if (mset[i]) + { + InterInstruction* ins = new InterInstruction(last->mLocation, IC_CONSTANT); + ins->mCode = IC_CONSTANT; + ins->mDst.mTemp = i; + ins->mConst = vars[i]; + ins->mDst.mType = ins->mConst.mType; + mInstructions.Push(ins); + } + } + + InterInstruction* ins = new InterInstruction(last->mLocation, IC_JUMP); + mInstructions.Push(ins); + } + } + } + } + } + + mVisited = true; + + if (mTrueJump) + mTrueJump->ConstInnerLoopOptimization(); + if (mFalseJump) + mFalseJump->ConstInnerLoopOptimization(); + } + +} + +void InterCodeBasicBlock::ConstSingleLoopOptimization(void) +{ + if (!mVisited) + { + mVisited = true; + + if (mLoopHead && mNumEntries == 2 && (mTrueJump == this || mFalseJump == this)) + { + NumberSet cset(mEntryRequiredTemps.Size()), mset(mEntryRequiredTemps.Size()); + + InterCodeBasicBlock* pblock = mLoopPrefix; + while (pblock) + { + for (int i = pblock->mInstructions.Size() - 1; i >= 0; i--) + { + const InterInstruction* ins(pblock->mInstructions[i]); + if (ins->mDst.mTemp >= 0) + { + if (ins->mCode == IC_CONSTANT && !mset[ins->mDst.mTemp]) + cset += ins->mDst.mTemp; + mset += ins->mDst.mTemp; + } + } + if (pblock->mEntryBlocks.Size() == 1) + pblock = pblock->mEntryBlocks[0]; + else + pblock = nullptr; + } + + bool isconst = true; + for (int i = 0; isconst && i < mInstructions.Size(); i++) + { + const InterInstruction* ins(mInstructions[i]); + + if (ins->mCode == IC_CONSTANT || ins->mCode == IC_BRANCH || + ins->mCode == IC_BINARY_OPERATOR || ins->mCode == IC_UNARY_OPERATOR || ins->mCode == IC_RELATIONAL_OPERATOR || ins->mCode == IC_LEA) + { + int j = 0; + while (j < ins->mNumOperands && (ins->mSrc[j].mTemp < 0 || cset[ins->mSrc[j].mTemp])) + j++; + if (j == ins->mNumOperands) + { + if (ins->mDst.mTemp >= 0) + cset += ins->mDst.mTemp; + } + else + isconst = false; + } + else if (ins->mCode == IC_LOAD && (ins->mSrc[0].mTemp < 0 || cset[ins->mSrc[0].mTemp]) && + ins->mSrc[0].mMemoryBase == IM_GLOBAL && ins->mSrc[0].mLinkerObject && (ins->mSrc[0].mLinkerObject->mFlags & LOBJF_CONST)) + { + cset += ins->mDst.mTemp; + } + else + isconst = false; + } + + if (isconst) + { + ExpandingArray vars; + vars.SetSize(cset.Size()); + mset.Clear(); + + InterCodeBasicBlock* pblock = mLoopPrefix; + while (pblock) + { + for (int i = pblock->mInstructions.Size() - 1; i >= 0; i--) + { + const InterInstruction* ins(pblock->mInstructions[i]); + if (ins->mDst.mTemp >= 0) + { + if (ins->mCode == IC_CONSTANT && !mset[ins->mDst.mTemp]) + { + assert(ins->mConst.mType != IT_NONE); + vars[ins->mDst.mTemp] = ins->mConst; + } + mset += ins->mDst.mTemp; + } + } + if (pblock->mEntryBlocks.Size() == 1) + pblock = pblock->mEntryBlocks[0]; + else + pblock = nullptr; + } + + bool done = false; + int n = 0; + + mset.Clear(); + while (n < 20000 && !done) + { + for (int i = 0; i < mInstructions.Size(); i++) + { + n++; + + const InterInstruction* ins(mInstructions[i]); + switch (ins->mCode) + { + case IC_CONSTANT: + assert(ins->mConst.mType != IT_NONE); + vars[ins->mDst.mTemp] = ins->mConst; + mset += ins->mDst.mTemp; + break; + case IC_BINARY_OPERATOR: + case IC_RELATIONAL_OPERATOR: + vars[ins->mDst.mTemp] = + OperandConstantFolding(ins->mOperator, + ins->mSrc[1].mTemp < 0 ? ins->mSrc[1] : vars[ins->mSrc[1].mTemp], + ins->mSrc[0].mTemp < 0 ? ins->mSrc[0] : vars[ins->mSrc[0].mTemp]); + mset += ins->mDst.mTemp; + break; + case IC_UNARY_OPERATOR: + case IC_CONVERSION_OPERATOR: + vars[ins->mDst.mTemp] = + OperandConstantFolding(ins->mOperator, + ins->mSrc[0].mTemp < 0 ? ins->mSrc[0] : vars[ins->mSrc[0].mTemp], + ins->mSrc[0].mTemp < 0 ? ins->mSrc[0] : vars[ins->mSrc[0].mTemp]); + mset += ins->mDst.mTemp; + break; + case IC_LEA: + { + InterOperand op; + int64 index = ins->mSrc[1].mIntConst; + if (ins->mSrc[0].mTemp < 0) + index += ins->mSrc[0].mIntConst; + else + index += vars[ins->mSrc[0].mTemp].mIntConst; + + if (ins->mSrc[1].mTemp < 0) + op = ins->mSrc[1]; + else + { + op = vars[ins->mSrc[1].mTemp]; + index += op.mIntConst; + } + + op.mIntConst = index; + vars[ins->mDst.mTemp] = op; + mset += ins->mDst.mTemp; + } break; + case IC_LOAD: + { + InterOperand op; + if (ins->mSrc[0].mTemp < 0) + op = ins->mSrc[0]; + else + { + op = vars[ins->mSrc[0].mTemp]; + op.mIntConst += ins->mSrc[0].mIntConst; + } + + vars[ins->mDst.mTemp] = LoadConstantOperand(ins, op, ins->mDst.mType, mProc->mModule->mGlobalVars, mProc->mModule->mProcedures); + mset += ins->mDst.mTemp; + + } break; + + case IC_BRANCH: + if ((vars[ins->mSrc[0].mTemp].mIntConst ? mTrueJump : mFalseJump) != this) + done = true; + break; + } + } + } + + if (done) + { + InterInstruction* last = mInstructions.Last(); + mInstructions.SetSize(0); + if (mFalseJump != this) + mTrueJump = mFalseJump; + mFalseJump = nullptr; + mLoopHead = false; + mNumEntries = 1; + mEntryBlocks.SetSize(0); + mEntryBlocks.Push(mLoopPrefix); + + for (int i = 0; i < mset.Size(); i++) + { + if (mset[i]) + { + InterInstruction* ins = new InterInstruction(last->mLocation, IC_CONSTANT); + ins->mCode = IC_CONSTANT; + ins->mDst.mTemp = i; + ins->mConst = vars[i]; + ins->mDst.mType = ins->mConst.mType; + mInstructions.Push(ins); + } + } + + InterInstruction* ins = new InterInstruction(last->mLocation, IC_JUMP); + mInstructions.Push(ins); + } + } + } + + if (mTrueJump) + mTrueJump->ConstSingleLoopOptimization(); + if (mFalseJump) + mFalseJump->ConstSingleLoopOptimization(); + } +} + +void InterCodeBasicBlock::EliminateDoubleLoopCounter(void) +{ + if (!mVisited) + { + mVisited = true; + + if (mLoopHead && mEntryBlocks.Size() == 2 && mLoopPrefix->mEntryBlocks.Size() == 1) + { + ExpandingArray body, path; + body.Push(this); + bool innerLoop = true; + + for (int i = 0; i < mEntryBlocks.Size(); i++) + { + if (mEntryBlocks[i] != mLoopPrefix) + { + if (!mEntryBlocks[i]->CollectLoopBodyRecursive(this, body)) + innerLoop = false; + } + } + + if (innerLoop) + { + InterCodeBasicBlock* eblock; + if (mEntryBlocks[0] == mLoopPrefix) + eblock = mEntryBlocks[1]; + else + eblock = mEntryBlocks[0]; + + struct LoopCounter + { + InterInstruction * mInit, * mInc, * mCmp; + int64 mStart, mEnd, mStep; + bool mReferenced; + }; + + ExpandingArray lcs; + + for (int i = 0; i < eblock->mInstructions.Size(); i++) + { + InterInstruction* ins(eblock->mInstructions[i]); + + LoopCounter lc; + lc.mInc = nullptr; + lc.mInit = nullptr; + lc.mCmp = nullptr; + lc.mReferenced = false; + lc.mEnd = -1; + + if (ins->mCode == IC_BINARY_OPERATOR && ins->mOperator == IA_ADD && IsIntegerType(ins->mDst.mType)) + { + if (ins->mDst.mTemp == ins->mSrc[0].mTemp && ins->mSrc[1].mTemp < 0 || + ins->mDst.mTemp == ins->mSrc[1].mTemp && ins->mSrc[0].mTemp < 0) + { + lc.mInc = ins; + } + } +#if 1 + else if (ins->mCode == IC_BINARY_OPERATOR && ins->mOperator == IA_SUB && IsIntegerType(ins->mDst.mType)) + { + if (ins->mDst.mTemp == ins->mSrc[1].mTemp && ins->mSrc[0].mTemp < 0) + { + lc.mInc = ins; + } + } +#endif + else if (ins->mCode == IC_LEA && ins->mDst.mTemp == ins->mSrc[1].mTemp && ins->mSrc[0].mTemp < 0) + { + lc.mInc = ins; + } + + if (lc.mInc) + { + int temp = lc.mInc->mDst.mTemp; + + if (!eblock->IsTempModifiedInRange(0, i, temp)) + { + int sz = eblock->mInstructions.Size(); + int rz = sz - 1; + if (eblock->mInstructions[sz - 1]->mCode == IC_BRANCH && + eblock->mInstructions[sz - 2]->mCode == IC_RELATIONAL_OPERATOR && eblock->mInstructions[sz - 1]->mSrc[0].mTemp == eblock->mInstructions[sz - 2]->mDst.mTemp && + ((eblock->mInstructions[sz - 2]->mSrc[0].mTemp == temp && eblock->mInstructions[sz - 2]->mSrc[1].mTemp < 0) || + (eblock->mInstructions[sz - 2]->mSrc[1].mTemp == temp && eblock->mInstructions[sz - 2]->mSrc[0].mTemp < 0))) + { + InterInstruction* ci = eblock->mInstructions[sz - 2]; + + if (ci->mOperator == IA_CMPEQ && eblock->mFalseJump == this || + ci->mOperator == IA_CMPNE && eblock->mTrueJump == this || + ci->mOperator == IA_CMPGU && eblock->mTrueJump == this && ci->mSrc[0].mTemp < 0 && ci->mSrc[0].mIntConst == 0 || + ci->mOperator == IA_CMPLU && eblock->mTrueJump == this && ci->mSrc[0].mTemp < 0 || + ci->mOperator == IA_CMPLEU && eblock->mTrueJump == this && ci->mSrc[0].mTemp < 0) + { + if (ci->mSrc[0].mTemp < 0) + lc.mEnd = ci->mSrc[0].mIntConst; + else + lc.mEnd = ci->mSrc[1].mIntConst; + lc.mCmp = eblock->mInstructions[sz - 2]; + rz--; + } + } + + if (!eblock->IsTempModifiedInRange(i + 1, sz, temp)) + { + if (eblock->IsTempReferencedInRange(0, i, temp) || eblock->IsTempReferencedInRange(i + 1, rz, temp)) + lc.mReferenced = true; + + for (int k = 0; k < body.Size(); k++) + { + if (body[k] != eblock && body[k]->IsTempReferenced(temp)) + lc.mReferenced = true; + } + + int k = 0; + while (k < body.Size() && (body[k] == eblock || !body[k]->IsTempModified(lc.mInc->mDst.mTemp))) + k++; + + if (k == body.Size()) + { + lc.mInit = mLoopPrefix->mEntryBlocks[0]->FindTempOrigin(lc.mInc->mDst.mTemp); + if (lc.mInit && lc.mInit->mCode == IC_CONSTANT) + { + lc.mStart = lc.mInit->mConst.mIntConst; + if (lc.mInc->mSrc[0].mTemp < 0) + { + if (lc.mInc->mOperator == IA_SUB) + lc.mStep = -lc.mInc->mSrc[0].mIntConst; + else + lc.mStep = lc.mInc->mSrc[0].mIntConst; + } + else + lc.mStep = lc.mInc->mSrc[1].mIntConst; + lcs.Push(lc); + } + } + } + } + } + } + +// printf("EDLC %d, %d\n", mIndex, lcs.Size()); + + + if (lcs.Size() >= 2) + { + int i = 0; + while (i < lcs.Size()) + { + if (lcs[i].mReferenced) + { + int j = i + 1; + while (j < lcs.Size() && !(lcs[j].mReferenced && + lcs[i].mStart == lcs[j].mStart && + lcs[i].mStep == lcs[j].mStep && + lcs[i].mInc->mCode == lcs[j].mInc->mCode && + (lcs[i].mInc->mCode != IC_LEA || SameMem(lcs[i].mInc->mDst, lcs[j].mInc->mDst)))) + j++; + if (j < lcs.Size()) + { +// printf("Found Same %s : %d-%d\n", mProc->mIdent->mString, i, j); + int ki = eblock->mInstructions.IndexOf(lcs[i].mInc); + int kj = eblock->mInstructions.IndexOf(lcs[j].mInc); + int tmpi = lcs[i].mInc->mDst.mTemp; + int tmpj = lcs[j].mInc->mDst.mTemp; + + if (ki < kj && !eblock->IsTempReferencedInRange(ki + 1, kj, tmpi)) + { + } + else if (kj < ki && !eblock->IsTempReferencedInRange(kj + 1, ki, tmpj)) + { + } + else + { + i++; + continue; + } + + if (lcs[i].mInc->mDst.mType > lcs[j].mInc->mDst.mType || + lcs[i].mInc->mDst.mType == lcs[j].mInc->mDst.mType && lcs[i].mCmp) + { + lcs[j].mInc->mCode = IC_NONE; lcs[j].mInc->mNumOperands = 0; lcs[j].mInc->mDst.mTemp = -1; + for (int bi = 0; bi < body.Size(); bi++) + { + InterCodeBasicBlock* block = body[bi]; + for (int k = 0; k < block->mInstructions.Size(); k++) + block->mInstructions[k]->ReplaceTemp(tmpj, tmpi); + } + lcs[j].mReferenced = false; + } + else + { + lcs[i].mInc->mCode = IC_NONE; lcs[i].mInc->mNumOperands = 0; lcs[i].mInc->mDst.mTemp = -1; + + for (int bi = 0; bi < body.Size(); bi++) + { + InterCodeBasicBlock* block = body[bi]; + for (int k = 0; k < block->mInstructions.Size(); k++) + block->mInstructions[k]->ReplaceTemp(tmpi, tmpj); + } + + if (lcs[i].mCmp) + { + lcs[j].mCmp = lcs[i].mCmp; + lcs[j].mEnd = lcs[i].mEnd; + lcs[i].mCmp = nullptr; + } + + lcs[i].mReferenced = false; + } + } + else + i++; + } + else + i++; + } + } + if (lcs.Size() >= 2) + { + int64 loop = -1; + int k = 0; + while (k < lcs.Size() && !lcs[k].mCmp) + k++; + if (k < lcs.Size()) + { + int64 start = lcs[k].mStart; + int64 end = lcs[k].mEnd; + int64 step = lcs[k].mStep; + + if (lcs[k].mCmp->mOperator == IA_CMPLEU) + end += step; + + if (step > 0 && end > start || step < 0 && end < start) + loop = (end - start) / step; + } + +// printf("Loop %lld, match %d\n", loop, k); +#if 0 + for (int i = 0; i < lcs.Size(); i++) + { + printf("LCS[%d] %lld + %lld -> %lld {%d, %d, %d}\n", i, lcs[i].mStart, lcs[i].mStep, lcs[i].mEnd, lcs[i].mInc->mDst.mTemp, lcs[i].mInc->mCode, lcs[i].mReferenced); + } +#endif + + if (loop > 0) + { + if (!lcs[k].mReferenced) + { + int j = 0; + while (j < lcs.Size() && !(lcs[j].mReferenced && lcs[j].mInc->mCode == IC_BINARY_OPERATOR && lcs[j].mStart + lcs[j].mStep * loop < 65536)) + j++; + + // Pointer compare with constants only in native code path + if (j == lcs.Size() && mProc->mNativeProcedure) + { + j = 0; + while (j < lcs.Size() && !(lcs[j].mReferenced && lcs[j].mInc->mCode == IC_LEA && (lcs[j].mInit->mConst.mMemory == IM_GLOBAL || lcs[j].mInit->mConst.mMemory == IM_ABSOLUTE) && + lcs[j].mStart + lcs[j].mStep * loop <= 65536)) + j++; + } + +// printf("Found %d, %d\n", j, k); + + if (j < lcs.Size()) + { + if (lcs[j].mInc->mCode == IC_LEA) + { + lcs[j].mInc->mDst.mRange.LimitMax(lcs[j].mStart + lcs[j].mStep * loop); + lcs[j].mInc->mSrc[1].mRange.LimitMax(lcs[j].mStart + lcs[j].mStep * (loop - 1)); + } + + int ci = 0, ti = 1; + if (lcs[k].mCmp->mSrc[1].mTemp < 0) + { + ci = 1; + ti = 0; + } + + lcs[k].mCmp->mSrc[ti] = lcs[j].mInc->mDst; + lcs[k].mCmp->mSrc[ci] = lcs[j].mInit->mConst; + lcs[k].mCmp->mSrc[ci].mIntConst += loop * lcs[j].mStep; + if (lcs[k].mCmp->mOperator == IA_CMPGU || lcs[k].mCmp->mOperator == IA_CMPLEU) + lcs[k].mCmp->mOperator = IA_CMPNE; + + InterInstruction* iins = lcs[k].mInit->Clone(); + iins->mConst.mIntConst += loop * lcs[k].mStep; + + mLoopPrefix->mInstructions.Insert(mLoopPrefix->mInstructions.Size() - 1, iins); + + lcs[k].mInc->mCode = IC_NONE; + lcs[k].mInc->mDst.mTemp = -1; + lcs[k].mInc->mNumOperands = 0; + } + } + } + } + } + } + + if (mTrueJump) + mTrueJump->EliminateDoubleLoopCounter(); + if (mFalseJump) + mFalseJump->EliminateDoubleLoopCounter(); + } +} +void InterCodeBasicBlock::InnerLoopOptimization(const NumberSet& aliasedParams) +{ + if (!mVisited) + { + mVisited = true; + + if (mLoopHead) + { + ExpandingArray body, path; + body.Push(this); + bool innerLoop = true; + + for (int i = 0; i < mEntryBlocks.Size(); i++) + { + if (mEntryBlocks[i] != mLoopPrefix) + { + if (!mEntryBlocks[i]->CollectLoopBody(this, body)) + innerLoop = false; + } + } + + if (innerLoop) + { + for (int i = 0; i < body.Size(); i++) + { + body[i]->mLoopPath = false; + body[i]->mLoopPathBlocks.SetSize(0); + } + + this->CollectLoopPath(body, path); + +#if 0 + printf("InnerLoop %d\n", mIndex); + for (int i = 0; i < body.Size(); i++) + printf("body %d\n", body[i]->mIndex); + for (int i = 0; i < path.Size(); i++) + printf("path %d\n", path[i]->mIndex); +#endif + bool hasCall = false, hasFrame = false, hasStore = false; + for (int bi = 0; bi < body.Size(); bi++) + { + InterCodeBasicBlock* block = body[bi]; + + for (int i = 0; i < block->mInstructions.Size(); i++) + { + InterInstruction* ins = block->mInstructions[i]; + ins->mInvariant = false; + ins->mExpensive = false; + if (ins->mCode == IC_CALL || ins->mCode == IC_CALL_NATIVE) + hasCall = true; + else if (ins->mCode == IC_PUSH_FRAME) + hasFrame = true; + else if (ins->mCode == IC_STORE) + { + if (ins->mSrc[1].mTemp >= 0) + hasStore = true; + else if ((ins->mSrc[1].mMemory == IM_PARAM || ins->mSrc[1].mMemory == IM_FPARAM) && !aliasedParams[ins->mSrc[1].mVarIndex]) + ; + else + hasStore = true; + } + else if (ins->mCode == IC_COPY || ins->mCode == IC_STRCPY || ins->mCode == IC_FILL) + hasStore = true; + } + } + + for (int bi = 0; bi < path.Size(); bi++) + { + InterCodeBasicBlock* block = path[bi]; + + for (int i = 0; i < block->mInstructions.Size(); i++) + { + InterInstruction* ins = block->mInstructions[i]; + ins->mInvariant = true; + + if (!IsMoveable(ins->mCode)) + ins->mInvariant = false; + else if (ins->mCode == IC_CONSTANT && ins->mDst.mType == IT_POINTER && ins->mConst.mMemory == IM_FRAME && hasFrame) + ins->mInvariant = false; + else if (ins->mCode == IC_LEA && ins->mSrc[1].mMemory == IM_FRAME && hasFrame) + ins->mInvariant = false; + else if (ins->mCode == IC_LOAD) + { + if (ins->mVolatile) + { + ins->mInvariant = false; + } + else if (ins->mSrc[0].mTemp >= 0 && (hasStore || hasCall)) + { + ins->mInvariant = false; + } + else if (ins->mSrc[0].mMemory == IM_GLOBAL && hasCall) + { + ins->mInvariant = false; + } + else if (ins->mSrc[0].mMemory == IM_ABSOLUTE && hasCall) + { + ins->mInvariant = false; + } + else if (ins->mSrc[0].mMemory == IM_LOCAL && hasCall) + { + ins->mInvariant = false; + } + else if (ins->mSrc[0].mMemory == IM_PARAM && hasCall && aliasedParams[ins->mSrc[0].mVarIndex]) + { + ins->mInvariant = false; + } + else + { + for (int bj = 0; bj < body.Size(); bj++) + { + InterCodeBasicBlock* blockj = body[bj]; + + for (int j = 0; j < blockj->mInstructions.Size(); j++) + { + InterInstruction* sins = blockj->mInstructions[j]; + if (sins->mCode == IC_STORE) + { + if (CollidingMem(ins, sins)) + ins->mInvariant = false; +#if 0 + if (sins->mSrc[1].mTemp >= 0) + { + if ((ins->mSrc[0].mMemory != IM_PARAM && ins->mSrc[0].mMemory != IM_FPARAM) || aliasedParams[ins->mSrc[0].mVarIndex]) + ins->mInvariant = false; + } + else if (ins->mSrc[0].mMemory == sins->mSrc[1].mMemory && ins->mSrc[0].mVarIndex == sins->mSrc[1].mVarIndex && ins->mSrc[0].mLinkerObject == sins->mSrc[1].mLinkerObject) + { + ins->mInvariant = false; + } +#endif + } + else if (sins->mCode == IC_COPY || sins->mCode == IC_FILL) + { + ins->mInvariant = false; + } + } + } + } + } + } + } + + for (int bi = 0; bi < body.Size(); bi++) + { + InterCodeBasicBlock* block = body[bi]; + + for (int i = 0; i < block->mInstructions.Size(); i++) + { + InterInstruction* ins = block->mInstructions[i]; + + if (ins->mCode == IC_LEA && ins->mSrc[1].mMemory != IM_FRAME || ins->mCode == IC_UNARY_OPERATOR || ins->mCode == IC_BINARY_OPERATOR || ins->mCode == IC_RELATIONAL_OPERATOR) + { + if (!block->mEntryRequiredTemps[ins->mDst.mTemp] && !block->mExitRequiredTemps[ins->mDst.mTemp]) + { + ins->mInvariant = true; + } + } + } + + } + + enum Dependency + { + DEP_UNKNOWN, + DEP_DEFINED, + DEP_ITERATED, + DEP_VARIABLE + }; + + GrowingArray dep(DEP_UNKNOWN); + GrowingArray tvalues(nullptr); + + for (int bi = 0; bi < body.Size(); bi++) + { + InterCodeBasicBlock* block = body[bi]; + + for (int i = 0; i < block->mInstructions.Size(); i++) + { + InterInstruction* ins = block->mInstructions[i]; + InterInstruction* nins = nullptr; + if (i + 1 < block->mInstructions.Size()) + nins = block->mInstructions[i + 1]; + + int t = ins->mDst.mTemp; + if (t >= 0) + { + if (HasSideEffect(ins->mCode) || !ins->mInvariant) + dep[t] = DEP_VARIABLE; + else if (dep[t] == DEP_UNKNOWN) + dep[t] = DEP_DEFINED; + else if (dep[t] == DEP_DEFINED) + { + dep[t] = DEP_VARIABLE; + ins->mInvariant = false; + } + } + + if (ins->mInvariant) + { + switch (ins->mCode) + { + case IC_BINARY_OPERATOR: + ins->mExpensive = true; + break; + case IC_UNARY_OPERATOR: + ins->mExpensive = true; + break; + case IC_LEA: + { + int offset = 0; + if (ins->mSrc[0].mTemp < 0) + offset = int(ins->mSrc[0].mIntConst); + + if (ins->mSrc[0].mTemp >= 0 && ins->mSrc[1].mTemp >= 0) + ins->mExpensive = true; + else if (ins->mSrc[0].mTemp >= 0 && ins->mSrc[0].mRange.mMaxState == IntegerValueRange::S_BOUND && ins->mSrc[0].mRange.mMaxValue >= 256) + ins->mExpensive = true; + else if (nins && nins->mCode == IC_LEA && nins->mSrc[0].mTemp >= 0 && (nins->mSrc[0].mRange.mMaxState == IntegerValueRange::S_UNBOUND || nins->mSrc[0].mRange.mMaxValue + offset >= 255)) + ins->mExpensive = true; + } break; + case IC_LOAD: + case IC_STORE: + ins->mExpensive = true; + break; + } + } + } + } + + for (int i = 0; i < dep.Size(); i++) + { + if (dep[i] == DEP_DEFINED) + dep[i] = DEP_ITERATED; + } + + bool changed; + do + { + changed = false; + + for (int bi = 0; bi < body.Size(); bi++) + { + InterCodeBasicBlock* block = body[bi]; + + for (int i = 0; i < block->mInstructions.Size(); i++) + { + InterInstruction* ins = block->mInstructions[i]; + int t = ins->mDst.mTemp; + if (t >= 0) + { + if (dep[t] < DEP_VARIABLE) + { + int j = 0; + while (j < ins->mNumOperands && !(ins->mSrc[j].mTemp >= 0 && dep[ins->mSrc[j].mTemp] >= DEP_ITERATED)) + j++; + if (j < ins->mNumOperands) + { + dep[t] = DEP_VARIABLE; + ins->mInvariant = false; + changed = true; + } + else + { + dep[t] = DEP_DEFINED; + } + } + else + ins->mInvariant = false; + } + } + } + + } while (changed); + +#if 1 + NumberSet required(dep.Size()); + + do + { + changed = false; + + for (int bi = 0; bi < body.Size(); bi++) + { + InterCodeBasicBlock* block = body[bi]; + + for (int i = 0; i < block->mInstructions.Size(); i++) + { + InterInstruction* ins = block->mInstructions[i]; + + if (ins->mInvariant && !ins->mExpensive && ins->mDst.mTemp >= 0 && required[ins->mDst.mTemp]) + ins->mExpensive = true; + + if (ins->mInvariant && ins->mExpensive) + { + for (int i = 0; i < ins->mNumOperands; i++) + { + if (ins->mSrc[i].mTemp >= 0 && !required[ins->mSrc[i].mTemp]) + { + required += ins->mSrc[i].mTemp; + changed = true; + } + } + } + } + } + + } while(changed); + + + for (int bi = 0; bi < body.Size(); bi++) + { + InterCodeBasicBlock* block = body[bi]; + + int j = 0; + for (int i = 0; i < block->mInstructions.Size(); i++) + { + InterInstruction* ins = block->mInstructions[i]; + if (ins->mInvariant && ins->mExpensive) + { + mLoopPrefix->mInstructions.Insert(mLoopPrefix->mInstructions.Size() - 1, ins); + } + else + { + block->mInstructions[j++] = ins; + } + } + + block->mInstructions.SetSize(j); + } +#endif + } + } + + + if (mTrueJump) + mTrueJump->InnerLoopOptimization(aliasedParams); + if (mFalseJump) + mFalseJump->InnerLoopOptimization(aliasedParams); + } +} + +int InterCodeBasicBlock::NumUnrollInstructions(int ireg) +{ + FastNumberSet ffree(mLocalUsedTemps.Size()); + ffree += ireg; + + int nins = 0; + for (int i = 0; i + 3 < mInstructions.Size(); i++) + { + const InterInstruction* ins(mInstructions[i]); + switch (ins->mCode) + { + case IC_BINARY_OPERATOR: + case IC_RELATIONAL_OPERATOR: + case IC_LEA: + if (ins->mSrc[0].mTemp >= 0 && ffree[ins->mSrc[0].mTemp] && ins->mSrc[1].mTemp < 0 || + ins->mSrc[1].mTemp >= 0 && ffree[ins->mSrc[1].mTemp] && ins->mSrc[0].mTemp < 0) + ffree += ins->mDst.mTemp; + else + nins++; + break; + case IC_UNARY_OPERATOR: + if (ffree[ins->mSrc[0].mTemp]) + ffree += ins->mDst.mTemp; + else + nins++; + break; + case IC_CONSTANT: + ffree += ins->mDst.mTemp; + break; + case IC_CONVERSION_OPERATOR: + if (ffree[ins->mSrc[0].mTemp]) + ffree += ins->mDst.mTemp; + else if (ins->mOperator == IA_EXT8TO16U || ins->mOperator == IA_EXT8TO32U || ins->mOperator == IA_EXT16TO32U || ins->mOperator == IA_EXT8TO16S) + ; + else + nins++; + break; + default: + nins++; + } + } + + return nins; +} + +void InterCodeBasicBlock::SingleBlockLoopUnrolling(void) +{ + if (!mVisited) + { + mVisited = true; + + if (mLoopHead && mNumEntries == 2 && mTrueJump == this) + { + int nins = mInstructions.Size(); + + if (nins > 3 && nins < 20) + { + if (mInstructions[nins - 1]->mCode == IC_BRANCH && + mInstructions[nins - 2]->mCode == IC_RELATIONAL_OPERATOR && + (mInstructions[nins - 2]->mOperator == IA_CMPLS || mInstructions[nins - 2]->mOperator == IA_CMPLES || + mInstructions[nins - 2]->mOperator == IA_CMPGS || mInstructions[nins - 2]->mOperator == IA_CMPGES || + mInstructions[nins - 2]->mOperator == IA_CMPLU || mInstructions[nins - 2]->mOperator == IA_CMPLEU || + mInstructions[nins - 2]->mOperator == IA_CMPGU || mInstructions[nins - 2]->mOperator == IA_CMPGEU || + mInstructions[nins - 2]->mOperator == IA_CMPNE) && + mInstructions[nins - 2]->mDst.mTemp == mInstructions[nins - 1]->mSrc[0].mTemp && + mInstructions[nins - 2]->mSrc[0].mTemp < 0 && + mInstructions[nins - 3]->mCode == IC_BINARY_OPERATOR && (mInstructions[nins - 3]->mOperator == IA_ADD || mInstructions[nins - 3]->mOperator == IA_SUB) && mInstructions[nins - 3]->mDst.mTemp == mInstructions[nins - 2]->mSrc[1].mTemp) + { + int ireg = mInstructions[nins - 3]->mDst.mTemp; + if (ireg == mInstructions[nins - 3]->mSrc[0].mTemp && mInstructions[nins - 3]->mSrc[1].mTemp < 0 || + ireg == mInstructions[nins - 3]->mSrc[1].mTemp && mInstructions[nins - 3]->mSrc[0].mTemp < 0) + { + int i = 0; + while (i < nins - 3 && mInstructions[i]->mDst.mTemp != ireg) + i++; + if (i == nins - 3) + { + if (mDominator->mTrueValueRange[ireg].IsConstant()) + { + int64 start = mDominator->mTrueValueRange[ireg].mMinValue; + int64 end = mInstructions[nins - 2]->mSrc[0].mIntConst; + if (mInstructions[nins - 2]->mOperator == IA_CMPLEU || mInstructions[nins - 2]->mOperator == IA_CMPLES) + end++; + else if (mInstructions[nins - 2]->mOperator == IA_CMPGEU || mInstructions[nins - 2]->mOperator == IA_CMPGES) + end--; + + int64 step = mInstructions[nins - 3]->mSrc[0].mTemp < 0 ? mInstructions[nins - 3]->mSrc[0].mIntConst : mInstructions[nins - 3]->mSrc[1].mIntConst; + if (mInstructions[nins - 3]->mOperator == IA_SUB) + step = -step; + + int count = step > 0 ? int((end - start + step - 1) / step) : int((start - end - step - 1) / -step); + +// if (CheckFunc && mIndex == 10) printf("Unroll %lld %lld %lld %d\n", start, end, step, count); + + if (count > 0 && + !( mInstructions[nins - 2]->mOperator == IA_CMPNE && end != start + count * step) && + !((mInstructions[nins - 2]->mOperator == IA_CMPGU || mInstructions[nins - 2]->mOperator == IA_CMPGEU) && (end < 0 || start < 0))) + { + int cins = NumUnrollInstructions(ireg); + + if (count < 5 && (cins - 3) * count < 20) + { + // printf("Unrolling %s,%d\n", mProc->mIdent->mString, mIndex); + + mInstructions.SetSize(nins - 2); + nins -= 2; + for (int i = 1; i < count; i++) + { + for (int j = 0; j < nins; j++) + { + mInstructions.Push(mInstructions[j]->Clone()); + } + } + + mNumEntries--; + mLoopHead = false; + mTrueJump = mFalseJump; + mFalseJump = nullptr; + + InterInstruction* jins = new InterInstruction(mInstructions[0]->mLocation, IC_JUMP); + mInstructions.Push(jins); + } + } + } + } + } + } + else if ( + mInstructions[nins - 1]->mCode == IC_BRANCH && + mInstructions[nins - 2]->mCode == IC_RELATIONAL_OPERATOR && mInstructions[nins - 2]->mOperator == IA_CMPNE && mInstructions[nins - 2]->mDst.mTemp == mInstructions[nins - 1]->mSrc[0].mTemp && + mInstructions[nins - 3]->mCode == IC_LEA && + mInstructions[nins - 3]->mDst.mTemp == mInstructions[nins - 2]->mSrc[1].mTemp && + mInstructions[nins - 3]->mDst.mTemp == mInstructions[nins - 3]->mSrc[1].mTemp && + mInstructions[nins - 3]->mSrc[0].mTemp < 0) + { + InterCodeBasicBlock* prefix = this == mEntryBlocks[0] ? mEntryBlocks[1] : mEntryBlocks[0]; + + int64 step = mInstructions[nins - 3]->mSrc[0].mIntConst; + + if (step > 0) + { + InterInstruction* i1 = FindSourceInstruction(prefix, mInstructions[nins - 2]->mSrc[0].mTemp); + InterInstruction* i2 = FindSourceInstruction(prefix, mInstructions[nins - 2]->mSrc[1].mTemp); + + if (i1 && i2 && i1->mCode == IC_CONSTANT && i2->mCode == IC_CONSTANT && i1->mConst.mMemory == i2->mConst.mMemory) + { + if (i1->mConst.mMemory == IM_GLOBAL && i1->mConst.mLinkerObject == i2->mConst.mLinkerObject) + { + int64 count = (i1->mConst.mIntConst - i2->mConst.mIntConst) / step; + if (count * step + i2->mConst.mIntConst == i1->mConst.mIntConst) + { + if (count < 5 && (nins - 3) * count < 20) + { + mInstructions.SetSize(nins - 2); + nins -= 2; + for (int i = 1; i < count; i++) + { + for (int j = 0; j < nins; j++) + { + mInstructions.Push(mInstructions[j]->Clone()); + } + } + + mNumEntries--; + mLoopHead = false; + mTrueJump = mFalseJump; + mFalseJump = nullptr; + + InterInstruction* jins = new InterInstruction(mInstructions[0]->mLocation, IC_JUMP); + mInstructions.Push(jins); + } + } + } + } + } + + } + + } + } + +#if 1 + if (mLoopHead && mNumEntries == 2 && mTrueJump == this && mFalseJump && mFalseJump->mNumEntries == 1) + { + int nins = mInstructions.Size(); + + if (nins > 3) + { + if (mInstructions[nins - 1]->mCode == IC_BRANCH && + mInstructions[nins - 2]->mCode == IC_RELATIONAL_OPERATOR && + (mInstructions[nins - 2]->mOperator == IA_CMPLU || mInstructions[nins - 2]->mOperator == IA_CMPLEU || mInstructions[nins - 2]->mOperator == IA_CMPLS || mInstructions[nins - 2]->mOperator == IA_CMPLES || mInstructions[nins - 2]->mOperator == IA_CMPNE) && + mInstructions[nins - 2]->mDst.mTemp == mInstructions[nins - 1]->mSrc[0].mTemp && + mInstructions[nins - 2]->mSrc[0].mTemp < 0 && + mInstructions[nins - 3]->mCode == IC_BINARY_OPERATOR && mInstructions[nins - 3]->mOperator == IA_ADD && mInstructions[nins - 3]->mDst.mTemp == mInstructions[nins - 2]->mSrc[1].mTemp) + { + int ireg = mInstructions[nins - 3]->mDst.mTemp; + + if (ireg == mInstructions[nins - 3]->mSrc[0].mTemp && mInstructions[nins - 3]->mSrc[1].mTemp < 0 || + ireg == mInstructions[nins - 3]->mSrc[1].mTemp && mInstructions[nins - 3]->mSrc[0].mTemp < 0) + { + + int i = 0; + while (i < nins - 3 && mInstructions[i]->mDst.mTemp != ireg) + i++; + if (i == nins - 3) + { + if (mDominator->mTrueValueRange[ireg].IsConstant()) + { + int64 start = mDominator->mTrueValueRange[ireg].mMinValue; + int64 end = mInstructions[nins - 2]->mSrc[0].mIntConst; + if (mInstructions[nins - 2]->mOperator == IA_CMPLEU || mInstructions[nins - 2]->mOperator == IA_CMPLES) + end++; + + int64 step = mInstructions[nins - 3]->mSrc[0].mTemp < 0 ? mInstructions[nins - 3]->mSrc[0].mIntConst : mInstructions[nins - 3]->mSrc[1].mIntConst; + int count = int((end - start + step - 1) / step); + + if (mInstructions[nins - 2]->mOperator != IA_CMPNE || end == start + count * step) + { + int i = 0; + while (i < mInstructions.Size()) + { + InterInstruction* ins = mInstructions[i]; + + if (ins->mCode == IC_BINARY_OPERATOR && + (ins->mOperator == IA_ADD || ins->mOperator == IA_SUB) && + (ins->mDst.mTemp == ins->mSrc[0].mTemp && ins->mSrc[1].mTemp < 0 || ins->mDst.mTemp == ins->mSrc[1].mTemp && ins->mSrc[0].mTemp < 0) && + !IsTempReferencedInRange(0, i, ins->mDst.mTemp) && !IsTempReferencedInRange(i + 1, mInstructions.Size(), ins->mDst.mTemp)) + { +// printf("Extract %s : %s,%d (%d) : %d, %d\n", mProc->mIdent->mString, ins->mLocation.mFileName, ins->mLocation.mLine, mIndex, ins->mSrc[0].mTemp, ins->mSrc[1].mTemp); + + if (mTrueValueRange.Size()) + { + mTrueValueRange[ins->mDst.mTemp].Reset(); + mFalseValueRange[ins->mDst.mTemp].Reset(); + mFalseJump->mEntryValueRange[ins->mDst.mTemp].Reset(); + } + + if (ins->mSrc[0].mTemp < 0) + ins->mSrc[0].mIntConst *= count; + else + ins->mSrc[1].mIntConst *= count; + mFalseJump->mInstructions.Insert(0, ins); + mInstructions.Remove(i); + } + else + i++; + } + } + } + } + } + } + } + } +#endif + + if (mTrueJump) + mTrueJump->SingleBlockLoopUnrolling(); + if (mFalseJump) + mFalseJump->SingleBlockLoopUnrolling(); + } +} + +static int FindStore(InterCodeBasicBlock* block, int pos, const InterOperand& op) +{ + while (pos > 0) + { + pos--; + InterInstruction* ins(block->mInstructions[pos]); + if (ins->mCode == IC_STORE && ins->mSrc[1].mTemp < 0) + { + if ((op.mMemory == IM_PARAM && ins->mSrc[1].mMemory == IM_FRAME || + op.mMemory == IM_FPARAM && ins->mSrc[1].mMemory == IM_FFRAME) && + op.mVarIndex == ins->mSrc[1].mVarIndex) + return pos; + } + if (ins->mCode == IC_POP_FRAME && op.mMemory == IM_PARAM) + return -1; + } + + return -1; +} + +bool InterCodeBasicBlock::StructReturnPropagation(void) +{ + if (mInstructions.Size() >= 6) + { + int sz = mInstructions.Size(); + if (mInstructions[sz - 1]->mCode == IC_RETURN && + mInstructions[sz - 2]->mCode == IC_COPY && mInstructions[sz - 2]->mSrc[0].mTemp < 0 && mInstructions[sz - 2]->mSrc[1].mTemp == mInstructions[sz - 3]->mDst.mTemp && + mInstructions[sz - 2]->mSrc[0].mMemory == IM_GLOBAL && mInstructions[sz - 2]->mSrc[0].mLinkerObject && (mInstructions[sz - 2]->mSrc[0].mLinkerObject->mFlags & LOBJF_LOCAL_VAR) && + mInstructions[sz - 3]->mCode == IC_LOAD && mInstructions[sz - 3]->mSrc[0].mTemp < 0 && (mInstructions[sz - 3]->mSrc[0].mMemory == IM_FPARAM || mInstructions[sz - 3]->mSrc[0].mMemory == IM_PARAM)) + { + LinkerObject* lo = mInstructions[sz - 2]->mSrc[0].mLinkerObject; + + int match = -1; + for (int i = 0; i < sz - 3; i++) + { + InterInstruction* ins = mInstructions[i]; + if (ins->mCode == IC_STORE || ins->mCode == IC_COPY || ins->mCode == IC_STRCPY || ins->mCode == IC_FILL || ins->mCode == IC_LEA) + { + if (ins->mSrc[1].mLinkerObject == lo) + return false; + } + else if (ins->mCode == IC_LOAD || ins->mCode == IC_COPY || ins->mCode == IC_STRCPY) + { + if (ins->mSrc[0].mLinkerObject == lo) + return false; + } + else if (ins->mCode == IC_CONSTANT && ins->mDst.mType == IT_POINTER && ins->mConst.mLinkerObject == lo) + { + if (match == -1) + match = i; + else + return false; + } + } + + if (match >= 0) + { + if (mInstructions[match + 1]->mCode == IC_STORE && mInstructions[match + 1]->mSrc[0].mTemp == mInstructions[match + 0]->mDst.mTemp && mInstructions[match + 1]->mSrc[0].mFinal) + { + mInstructions[match]->mCode = IC_LOAD; + mInstructions[match]->mSrc[0] = mInstructions[sz - 3]->mSrc[0]; + mInstructions[match]->mNumOperands = 1; + + mInstructions[sz - 3]->mCode = IC_NONE; mInstructions[sz - 3]->mNumOperands = 0; mInstructions[sz - 3]->mDst.mTemp = -1; + mInstructions[sz - 2]->mCode = IC_NONE; mInstructions[sz - 2]->mNumOperands = 0; mInstructions[sz - 2]->mDst.mTemp = -1; + + return true; + } + } + else + return false; + } + } + + return false; +} + +bool InterCodeBasicBlock::CollapseDispatch() +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + for (int i = 0; i + 2 < mInstructions.Size(); i++) + { + if (mInstructions[i + 0]->mCode == IC_LEA && mInstructions[i + 0]->mSrc[1].mTemp < 0 && + mInstructions[i + 1]->mCode == IC_LOAD && mInstructions[i + 1]->mSrc[0].mTemp == mInstructions[i + 0]->mDst.mTemp && mInstructions[i + 1]->mSrc[0].mStride == 2 && + mInstructions[i + 2]->mCode == IC_DISPATCH) + { + LinkerObject* lo = mInstructions[i + 0]->mSrc[1].mLinkerObject; + if (lo && lo->mReferences.Size() > 0) + { + int j = 1; + while (2 * j < lo->mReferences.Size() && lo->mReferences[0]->mRefObject == lo->mReferences[2 * j]->mRefObject) + j++; + if (2 * j == lo->mReferences.Size()) + { + mInstructions[i + 1]->mCode = IC_CONSTANT; + mInstructions[i + 1]->mNumOperands = 0; + mInstructions[i + 1]->mConst.mType = IT_POINTER; + mInstructions[i + 1]->mConst.mMemory = IM_GLOBAL; + mInstructions[i + 1]->mConst.mLinkerObject = lo->mReferences[0]->mRefObject; + changed = true; + printf("dispatch"); + } + } + } + } + + if (mTrueJump && mTrueJump->CollapseDispatch()) + changed = true; + if (mFalseJump && mFalseJump->CollapseDispatch()) + changed = true; + } + + return changed; +} + +bool InterCodeBasicBlock::CheapInlining(int & numTemps) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + for (int i = 0; i < mInstructions.Size(); i++) + { + InterInstruction* ins(mInstructions[i]); + + if (ins->mCode == IC_CALL_NATIVE && ins->mSrc[0].mTemp < 0 && ins->mSrc[0].mLinkerObject && ins->mSrc[0].mLinkerObject->mProc && ins->mSrc[0].mLinkerObject->mProc->mCheapInline) + { + InterCodeProcedure* proc = ins->mSrc[0].mLinkerObject->mProc; + InterCodeBasicBlock* block = proc->mEntryBlock; + + int ntemps = numTemps; + GrowingArray tmap(-1); + + bool fail = false; + for (int j = 0; j < block->mInstructions.Size(); j++) + { + InterInstruction* nins(block->mInstructions[j]); + if (nins->mCode == IC_LOAD && nins->mSrc[0].mTemp < 0 && FindStore(this, i, nins->mSrc[0]) < 0) + fail = true; + } + + if (!fail) + { +// printf("Cheap Inline %s into %s\n", proc->mIdent->mString, mProc->mIdent->mString); + + for (int j = 0; j < i; j++) + mInstructions[j]->mRemove = false; + + mInstructions.Remove(i); + changed = true; + + if (proc->mCommonFrameSize) + { + InterInstruction* fins = new InterInstruction(ins->mLocation, IC_PUSH_FRAME); + fins->mNumOperands = 0; + fins->mConst.mIntConst = proc->mCommonFrameSize; + mInstructions.Insert(i, fins); + i++; + } + + for (int j = 0; j < block->mInstructions.Size(); j++) + { + InterInstruction* nins(block->mInstructions[j]); + switch (nins->mCode) + { + case IC_LOAD: + { + int k = FindStore(this, i, nins->mSrc[0]); + if (k >= 0) + { + InterInstruction* pins = mInstructions[k]->Clone(); + pins->mLocation.mFrom = &(ins->mLocation); + mInstructions[k]->mRemove = true; + + if (pins->mSrc[0].mTemp < 0) + { + pins->mCode = IC_CONSTANT; + pins->mConst = pins->mSrc[0]; + pins->mNumOperands = 0; + } + else + { + pins->mCode = IC_LOAD_TEMPORARY; + pins->mNumOperands = 1; + } + + pins->mDst = nins->mDst; + pins->mDst.mTemp = ntemps; + mInstructions.Insert(k + 1, pins); + i++; + } + else + { + InterInstruction* pins = nins->Clone(); + pins->mLocation.mFrom = &(ins->mLocation); + if (pins->mSrc[0].mTemp >= 0) + pins->mSrc[0].mTemp = tmap[pins->mSrc[0].mTemp]; + if (pins->mDst.mTemp >= 0) + pins->mDst.mTemp = ntemps; + mInstructions.Insert(i, pins); + i++; + } + } break; + case IC_STORE: + { + InterInstruction* pins = nins->Clone(); + pins->mLocation.mFrom = &(ins->mLocation); + if (pins->mSrc[0].mTemp >= 0) + pins->mSrc[0].mTemp = tmap[pins->mSrc[0].mTemp]; + if (pins->mSrc[1].mTemp >= 0) + pins->mSrc[1].mTemp = tmap[pins->mSrc[1].mTemp]; + mInstructions.Insert(i, pins); + i++; + } break; + case IC_CALL: + case IC_CALL_NATIVE: + { + InterInstruction* pins = nins->Clone(); + pins->mLocation.mFrom = &(ins->mLocation); + if (pins->mDst.mTemp >= 0) + pins->mDst.mTemp = ntemps; + mInstructions.Insert(i, pins); + i++; + } break; + case IC_RETURN: + break; + case IC_RETURN_VALUE: + { + if (ins->mDst.mTemp >= 0) + { + InterInstruction* pins = nins->Clone(); + pins->mLocation.mFrom = &(ins->mLocation); + if (pins->mSrc[0].mTemp < 0) + { + pins->mCode = IC_CONSTANT; + pins->mConst = pins->mSrc[0]; + pins->mNumOperands = 0; + } + else + { + pins->mCode = IC_LOAD_TEMPORARY; + pins->mNumOperands = 1; + } + + pins->mDst = ins->mDst; + if (pins->mSrc[0].mTemp >= 0) + pins->mSrc[0].mTemp = tmap[pins->mSrc[0].mTemp]; + mInstructions.Insert(i, pins); + i++; + } + } break; + default: + { + InterInstruction* pins = nins->Clone(); + pins->mLocation.mFrom = &(ins->mLocation); + for (int k = 0; k < pins->mNumOperands; k++) + if (pins->mSrc[k].mTemp >= 0) + pins->mSrc[k].mTemp = tmap[pins->mSrc[k].mTemp]; + if (pins->mDst.mTemp >= 0) + pins->mDst.mTemp = ntemps; + mInstructions.Insert(i, pins); + i++; + } break; + } + + if (nins->mDst.mTemp >= 0) + tmap[nins->mDst.mTemp] = ntemps++; + } + + numTemps = ntemps; + + if (proc->mCommonFrameSize) + { + InterInstruction* fins = new InterInstruction(ins->mLocation, IC_POP_FRAME); + fins->mNumOperands = 0; + fins->mConst.mIntConst = proc->mCommonFrameSize; + mInstructions.Insert(i, fins); + i++; + } + + for (int j = 0; j < i; j++) + { + if (mInstructions[j]->mRemove) + { + mInstructions[j]->mCode = IC_NONE; + mInstructions[j]->mDst.mTemp = -1; + mInstructions[j]->mNumOperands = 0; + } + } + } + } + } + + if (mTrueJump && mTrueJump->CheapInlining(numTemps)) + changed = true; + if (mFalseJump && mFalseJump->CheapInlining(numTemps)) + changed = true; + } + + return changed; +} + +bool InterCodeBasicBlock::MapLateIntrinsics(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + for (int i = 0; i < mInstructions.Size(); i++) + { + InterInstruction* ins(mInstructions[i]); + + if (ins->mCode == IC_CALL_NATIVE && ins->mSrc[0].mLinkerObject && + ins->mSrc[0].mLinkerObject->mProc && + ins->mSrc[0].mLinkerObject->mProc->mIntrinsicFunction && + !strcmp(ins->mSrc[0].mLinkerObject->mIdent->mString, "memcpy")) + { + int ops[3]; + bool fail = false; + + for (int vi = 0; vi < 3; vi++) + { + InterOperand op; + op.mType = vi < 2 ? IT_POINTER : IT_INT16; + op.mTemp = -1; + op.mMemory = IM_FPARAM; + op.mVarIndex = 2 * vi; + + if ((ops[vi] = FindStore(this, i, op)) < 0) + { + fail = true; + break; + } + } + + if (!fail && mInstructions[ops[2]]->mSrc[0].mTemp < 0 && mInstructions[ops[2]]->mSrc[0].mIntConst < 256) + { + ins->mCode = IC_COPY; + ins->mSrc[0] = mInstructions[ops[1]]->mSrc[0]; mInstructions[ops[1]]->mSrc[0].mFinal = false; + ins->mSrc[1] = mInstructions[ops[0]]->mSrc[0]; mInstructions[ops[0]]->mSrc[0].mFinal = false; + ins->mSrc[0].mOperandSize = ins->mSrc[1].mOperandSize = ins->mConst.mOperandSize = int(mInstructions[ops[2]]->mSrc[0].mIntConst); + ins->mNumOperands = 2; + + mInstructions[ops[0]]->mCode = IC_NONE; mInstructions[ops[0]]->mNumOperands = 0; mInstructions[ops[0]]->mDst.mTemp = -1; + mInstructions[ops[1]]->mCode = IC_NONE; mInstructions[ops[1]]->mNumOperands = 0; mInstructions[ops[1]]->mDst.mTemp = -1; + mInstructions[ops[2]]->mCode = IC_NONE; mInstructions[ops[2]]->mNumOperands = 0; mInstructions[ops[2]]->mDst.mTemp = -1; + + changed = true; + } + } + } + + if (mTrueJump && mTrueJump->MapLateIntrinsics()) + changed = true; + if (mFalseJump && mFalseJump->MapLateIntrinsics()) + changed = true; + } + + return changed; +} + +bool InterCodeBasicBlock::PullStoreUpToConstAddress(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + for (int i = 0; i < mInstructions.Size(); i++) + { + InterInstruction* ins = mInstructions[i], * cins; + if (ins->mCode == IC_STORE && ins->mSrc[0].mTemp < 0 && ins->mSrc[1].mTemp >= 0 && CanMoveInstructionBeforeBlock(i)) + { + int j = 0; + while (j < mEntryBlocks.Size() && (cins = mEntryBlocks[j]->FindTempOriginSinglePath(ins->mSrc[1].mTemp)) && cins->mCode == IC_CONSTANT) + j++; + + if (j == mEntryBlocks.Size()) + { + for (int j = 0; j < mEntryBlocks.Size(); j++) + mEntryBlocks[j]->AppendBeforeBranch(ins->Clone()); + changed = true; + mInstructions.Remove(i); + i--; + } + } + } + + if (mTrueJump && mTrueJump->PullStoreUpToConstAddress()) + changed = true; + if (mFalseJump && mFalseJump->PullStoreUpToConstAddress()) + changed = true; + } + + return false; +} + +void InterCodeBasicBlock::ReduceTempLivetimes(void) +{ + if (!mVisited) + { + mVisited = true; + + bool changed; + do { + changed = false; + + for (int i = 0; i < mInstructions.Size(); i++) + { + InterInstruction* ins = mInstructions[i]; + + if (ins->mCode == IC_BINARY_OPERATOR && ins->mSrc[0].mTemp >= 0 && ins->mSrc[1].mTemp >= 0 && ins->mSrc[0].mFinal && ins->mSrc[1].mFinal) + { + bool calls = false; + int j = i; + while (j > 0 && CanSwapInstructions(mInstructions[j - 1], ins)) + { + if (mInstructions[j - 1]->mCode == IC_CALL || mInstructions[j - 1]->mCode == IC_CALL_NATIVE) + calls = true; + j--; + } + + if (calls && j > 0 && j < i) + { + while (i > j) + { + SwapInstructions(mInstructions[i - 1], ins); + mInstructions[i] = mInstructions[i - 1]; + i--; + } + mInstructions[i] = ins; + changed = true; + } + } + } + + } while (changed); + + if (mTrueJump) mTrueJump->ReduceTempLivetimes(); + if (mFalseJump) mFalseJump->ReduceTempLivetimes(); + } +} + +void InterCodeBasicBlock::RemoveUnusedMallocs(void) +{ + if (!mVisited) + { + mVisited = true; + + for (int i = 0; i < mInstructions.Size(); i++) + { + InterInstruction* mins = mInstructions[i]; + if (mins->mCode == IC_MALLOC) + { + int mtemp = mins->mDst.mTemp; + bool used = false; + int j = i + 1; + while (j < mInstructions.Size() && !used) + { + InterInstruction* fins = mInstructions[j]; + if (fins->mCode == IC_FREE && fins->mSrc[0].mTemp == mtemp) + break; + if (fins->ReferencesTemp(mtemp)) + { + if (fins->mCode != IC_STORE || fins->mSrc[1].mTemp != mtemp) + used = true; + } + j++; + } + + if (j < mInstructions.Size() && !used) + { + mins->mCode = IC_NONE; mins->mNumOperands = 0; + for (int k = i + 1; k <= j; k++) + { + InterInstruction* lins = mInstructions[k]; + if (lins->UsesTemp(mtemp)) + { + lins->mCode = IC_NONE; + lins->mDst.mTemp = -1; + lins->mNumOperands = 0; + } + } + } + } + } + + if (mTrueJump) mTrueJump->RemoveUnusedMallocs(); + if (mFalseJump) mFalseJump->RemoveUnusedMallocs(); + } +} + +bool InterCodeBasicBlock::UntangleLoadStoreSequence(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + for (int i = 0; i < mInstructions.Size(); i++) + { + if (i + 2 < mInstructions.Size()) + { + if (mInstructions[i + 0]->mCode == IC_LOAD && + mInstructions[i + 1]->mCode == IC_STORE && + mInstructions[i + 2]->mCode == IC_STORE && + mInstructions[i + 1]->mSrc[0].mTemp != mInstructions[i + 0]->mDst.mTemp && + mInstructions[i + 2]->mSrc[0].mTemp == mInstructions[i + 0]->mDst.mTemp && mInstructions[i + 2]->mSrc[0].mFinal && + CanSwapInstructions(mInstructions[i + 1], mInstructions[i + 2])) + { + SwapInstructions(mInstructions[i + 1], mInstructions[i + 2]); + InterInstruction* ins = mInstructions[i + 1]; + mInstructions[i + 1] = mInstructions[i + 2]; + mInstructions[i + 2] = ins; + changed = true; + } + } + if (i + 3 < mInstructions.Size()) + { + if (mInstructions[i + 0]->mCode == IC_LOAD && + mInstructions[i + 1]->mCode == IC_STORE && + mInstructions[i + 1]->mSrc[0].mTemp != mInstructions[i + 0]->mDst.mTemp && + mInstructions[i + 2]->mCode == IC_LOAD && + mInstructions[i + 3]->mCode == IC_STORE && + mInstructions[i + 3]->mSrc[0].mTemp == mInstructions[i + 2]->mDst.mTemp && mInstructions[i + 3]->mSrc[0].mFinal && + CanSwapInstructions(mInstructions[i + 1], mInstructions[i + 2]) && + CanSwapInstructions(mInstructions[i + 1], mInstructions[i + 3]) && + CanSwapInstructions(mInstructions[i + 0], mInstructions[i + 2]) && + CanSwapInstructions(mInstructions[i + 0], mInstructions[i + 3])) + { + SwapInstructions(mInstructions[i + 1], mInstructions[i + 2]); + SwapInstructions(mInstructions[i + 1], mInstructions[i + 3]); + SwapInstructions(mInstructions[i + 0], mInstructions[i + 2]); + SwapInstructions(mInstructions[i + 0], mInstructions[i + 3]); + + InterInstruction* ins = mInstructions[i + 0]; + mInstructions[i + 0] = mInstructions[i + 2]; + mInstructions[i + 2] = ins; + ins = mInstructions[i + 1]; + mInstructions[i + 1] = mInstructions[i + 3]; + mInstructions[i + 3] = ins; + + changed = true; + } + } + } + + if (mTrueJump && mTrueJump->UntangleLoadStoreSequence()) + changed = true; + if (mFalseJump && mFalseJump->UntangleLoadStoreSequence()) + changed = true; + } + + return changed; +} + +void InterCodeBasicBlock::PropagateMemoryAliasingInfo(const GrowingInstructionPtrArray& tvalue, bool loops) +{ + if (!mVisited) + { + GrowingInstructionPtrArray ltvalue(tvalue); + + if (mLoopHead) + { + if (mNumEntries == 2 && (mTrueJump == this || mFalseJump == this)) + { + for (int i = 0; i < ltvalue.Size(); i++) + { + if (ltvalue[i]) + { + for (int j = 0; j < mInstructions.Size(); j++) + { + if (mInstructions[j]->mDst.mTemp == i) + { + if (mInstructions[j]->mCode == IC_LEA && mInstructions[j]->mSrc[1].mTemp == i) + ; + else + { + ltvalue[i] = nullptr; + break; + } + } + } + } + } + } +#if 1 + else if (loops) + { + ExpandingArray lblocks; + + if (CollectSingleEntryGenericLoop(lblocks)) + { + for (int i = 0; i < ltvalue.Size(); i++) + { + if (ltvalue[i]) + { + bool fail = false; + + for (int k = 0; k < lblocks.Size() && !fail; k++) + { + InterCodeBasicBlock* b = lblocks[k]; + for (int j = 0; j < b->mInstructions.Size() && !fail; j++) + { + InterInstruction* ins = b->mInstructions[j]; + if (ins->mDst.mTemp == i) + { + if (ins->mCode == IC_LEA && ins->mSrc[1].mTemp == i) + ; + else + fail = true; + } + } + } + + if (fail) + ltvalue[i] = nullptr; + } + } + } + else + ltvalue.Clear(); + } +#else + else if (loops && mNumEntries == 2) + { + InterCodeBasicBlock* tail, * post; + + if (mEntryBlocks[0] == mLoopPrefix) + tail = mEntryBlocks[1]; + else + tail = mEntryBlocks[0]; + + if (tail->mTrueJump == this) + post = tail->mFalseJump; + else + post = tail->mTrueJump; + + if (post && post->mNumEntries == 1) + { + ExpandingArray body; + + if (tail->CollectSingleHeadLoopBody(this, tail, body)) + { + for (int i = 0; i < ltvalue.Size(); i++) + { + if (ltvalue[i]) + { + bool fail = false; + + for (int k = 0; k < body.Size() && !fail; k++) + { + InterCodeBasicBlock* b = body[k]; + for (int j = 0; j < b->mInstructions.Size() && !fail; j++) + { + InterInstruction* ins = b->mInstructions[j]; + if (ins->mDst.mTemp == i) + { + if (ins->mCode == IC_LEA && ins->mSrc[1].mTemp == i) + ; + else + fail = true; + } + } + } + + if (fail) + { + ltvalue[i] = nullptr; + } + } + } + } + } + } +#endif + else + ltvalue.Clear(); + } + else if (mNumEntries > 0) + { + if (mNumEntered > 0) + { + for (int i = 0; i < ltvalue.Size(); i++) + { + if (mMergeTValues[i] != ltvalue[i]) + ltvalue[i] = nullptr; + } + } + + mNumEntered++; + + if (mNumEntered < mNumEntries) + { + mMergeTValues = ltvalue; + return; + } + } + + mVisited = true; + + for (int i = 0; i < mInstructions.Size(); i++) + { + InterInstruction* ins = mInstructions[i]; + + for (int j = 0; j < ins->mNumOperands; j++) + { + if (ins->mSrc[j].mTemp >= 0 && ltvalue[ins->mSrc[j].mTemp] && ins->mSrc[j].mType == IT_POINTER) + { + ins->mSrc[j].mRestricted = ltvalue[ins->mSrc[j].mTemp]->mDst.mRestricted; + ins->mSrc[j].mMemoryBase = ltvalue[ins->mSrc[j].mTemp]->mDst.mMemoryBase; + ins->mSrc[j].mVarIndex = ltvalue[ins->mSrc[j].mTemp]->mDst.mVarIndex; + ins->mSrc[j].mLinkerObject = ltvalue[ins->mSrc[j].mTemp]->mDst.mLinkerObject; + if (ins->mSrc[j].mMemory == IM_NONE && ins->mSrc[j].mMemoryBase != IM_NONE) + ins->mSrc[j].mMemory = IM_INDIRECT; + + assert(ins->mSrc[j].mMemoryBase != IM_LOCAL || ins->mSrc[j].mVarIndex >= 0); + } + else if (ins->mSrc[j].mTemp < 0 && ins->mSrc[j].mType == IT_POINTER) + ins->mSrc[j].mMemoryBase = ins->mSrc[j].mMemory; + } + + if (ins->mCode == IC_LEA) + { + ins->mDst.mMemory = ins->mSrc[1].mMemory; + ins->mDst.mRestricted = ins->mSrc[1].mRestricted; + if (ins->mSrc[1].mMemory != IM_INDIRECT) + ins->mSrc[1].mMemoryBase = ins->mSrc[1].mMemory; + ins->mDst.mMemoryBase = ins->mSrc[1].mMemoryBase; + ins->mDst.mVarIndex = ins->mSrc[1].mVarIndex; + ins->mDst.mLinkerObject = ins->mSrc[1].mLinkerObject; + } + else if (ins->mCode == IC_LOAD && ins->mDst.mType == IT_POINTER) + { + if (ins->mSrc[0].mMemoryBase == IM_GLOBAL && (mInstructions[i]->mSrc[0].mLinkerObject->mFlags & LOBJF_CONST)) + { + LinkerObject* lobj = mInstructions[i]->mSrc[0].mLinkerObject; + if (lobj->mReferences.Size() == 0) + { + ins->mDst.mMemory = IM_ABSOLUTE; + ins->mDst.mMemoryBase = IM_ABSOLUTE; + ins->mDst.mVarIndex = -1; + ins->mDst.mLinkerObject = nullptr; + + if (ins->mSrc[0].mRange.IsBound()) + { + int minval = int(ins->mSrc[0].mRange.mMinValue), maxval = int(ins->mSrc[0].mRange.mMaxValue); + if (minval >= 0 && maxval > minval && maxval + ins->mSrc[0].mStride < lobj->mSize) + { + int minptr = 0xffff, maxptr = 0x0000; + if (ins->mSrc[0].mStride == 1) + { + for (int i = minval; i <= maxval; i+=2) + { + int iptr = lobj->mData[i] + 256 * lobj->mData[i + 1]; + if (iptr < minptr) minptr = iptr; + if (iptr > maxptr) maxptr = iptr; + } + } + else + { + for (int i = minval; i <= maxval; i++) + { + int iptr = lobj->mData[i] + 256 * lobj->mData[i + ins->mSrc[0].mStride]; + if (iptr < minptr) minptr = iptr; + if (iptr > maxptr) maxptr = iptr; + } + } + ins->mDst.mRange.SetLimit(minptr, maxptr); + } + } + } + } + } + else if (ins->mCode == IC_LOAD_TEMPORARY) + { + ins->mDst.mMemory = ins->mSrc[0].mMemory; + ins->mDst.mRestricted = ins->mSrc[0].mRestricted; + ins->mDst.mMemoryBase = ins->mSrc[0].mMemoryBase; + ins->mDst.mVarIndex = ins->mSrc[0].mVarIndex; + ins->mDst.mLinkerObject = ins->mSrc[0].mLinkerObject; + } + else if (ins->mCode == IC_CONSTANT) + { + ins->mDst.mMemory = ins->mConst.mMemory; + ins->mDst.mRestricted = ins->mConst.mRestricted; + ins->mDst.mMemoryBase = ins->mConst.mMemory; + ins->mDst.mVarIndex = ins->mConst.mVarIndex; + ins->mDst.mLinkerObject = ins->mConst.mLinkerObject; + } + else if (ins->mCode == IC_MALLOC) + { + ins->mDst.mMemoryBase = IM_INDIRECT; + } + + if (ins->mDst.mTemp >= 0) + { + if (ins->mDst.mRestricted || ins->mDst.mMemoryBase != IM_NONE) + ltvalue[ins->mDst.mTemp] = ins; + else + ltvalue[ins->mDst.mTemp] = nullptr; + } + } + + + if (mTrueJump) mTrueJump->PropagateMemoryAliasingInfo(ltvalue, loops); + if (mFalseJump) mFalseJump->PropagateMemoryAliasingInfo(ltvalue, loops); + } +} + +static bool IsTempModifiedInBlocks(const ExpandingArray& body, int temp) +{ + for (int j = 0; j < body.Size(); j++) + if (body[j]->IsTempModified(temp)) + return true; + return false; +} + +static bool IsInsSrcModifiedInBlocks(const ExpandingArray& body, const InterInstruction * ins) +{ + for (int i = 0; i < ins->mNumOperands; i++) + { + if (ins->mSrc[i].mTemp >= 0 && IsTempModifiedInBlocks(body, ins->mSrc[i].mTemp)) + return true; + } + return false; +} + +void InterCodeBasicBlock::InnerLoopCountZeroCheck(void) +{ + if (!mVisited) + { + mVisited = true; + + if (mLoopHead && mEntryBlocks.Size() == 2) + { + ExpandingArray body, path; + body.Push(this); + bool innerLoop = true; + + InterCodeBasicBlock* lblock = nullptr; + InterCodeBasicBlock* pblock = mLoopPrefix; + + while (pblock->mInstructions.Size() == 1 && pblock->mInstructions[0]->mCode == IC_JUMP && pblock->mEntryBlocks.Size() == 1) + pblock = pblock->mEntryBlocks[0]; + + for (int i = 0; i < mEntryBlocks.Size(); i++) + { + if (mEntryBlocks[i] != mLoopPrefix) + { + lblock = mEntryBlocks[i]; + if (!mEntryBlocks[i]->CollectLoopBody(this, body)) + innerLoop = false; + } + } + + if (pblock && this != lblock && innerLoop && lblock && lblock->mInstructions.Size() > 2) + { + int sz = lblock->mInstructions.Size(); + + if (lblock->mInstructions[sz - 1]->mCode == IC_BRANCH && + lblock->mInstructions[sz - 2]->mCode == IC_RELATIONAL_OPERATOR && + lblock->mInstructions[sz - 3]->mCode == IC_BINARY_OPERATOR && lblock->mInstructions[sz - 3]->mOperator == IA_ADD) + { + InterInstruction* ains = lblock->mInstructions[sz - 3]; + InterInstruction* cins = lblock->mInstructions[sz - 2]; + InterInstruction* bins = lblock->mInstructions[sz - 1]; + + if (bins->mSrc[0].mTemp == cins->mDst.mTemp && + cins->mSrc[1].mTemp == ains->mDst.mTemp && + cins->mSrc[0].mTemp < 0 && + ains->mSrc[1].mTemp == ains->mDst.mTemp && + ains->mSrc[0].mTemp < 0 && + cins->mOperator == IA_CMPLU && + ains->mSrc[0].mIntConst >= 1 && + cins->mSrc[0].mIntConst > 0) + { + int ctemp = ains->mDst.mTemp; + + bool fail = false; + for (int i = 0; i < body.Size(); i++) + { + int sz = body[i]->mInstructions.Size(); + if (body[i] == lblock) + sz -= 3; + if (body[i]->IsTempReferencedInRange(0, sz, ains->mDst.mTemp)) + { + fail = true; + break; + } + else if ( + body[i]->mTrueJump && !body.Contains(body[i]->mTrueJump) && body[i]->mTrueJump->mEntryRequiredTemps[ctemp] || + body[i]->mFalseJump && !body.Contains(body[i]->mFalseJump) && body[i]->mFalseJump->mEntryRequiredTemps[ctemp]) + { + fail = true; + break; + } + } + + if (!fail) + { + int pi = pblock->mInstructions.Size() - 1; + while (pi >= 0 && pblock->mInstructions[pi]->mDst.mTemp != ctemp) + pi--; + if (pi >= 0 && pblock->mInstructions[pi]->mCode == IC_CONSTANT) + { + int64 istart = pblock->mInstructions[pi]->mConst.mIntConst; + if (istart >= 0) + { + int64 icount = (cins->mSrc[0].mIntConst - istart + ains->mSrc[0].mIntConst - 1) / ains->mSrc[0].mIntConst; + + if (icount > 0 && icount < 256) + { + ains->mSrc[0].mIntConst = -1; + cins->mSrc[0].mIntConst = 0; + pblock->mInstructions[pi]->mConst.mIntConst = icount; + + ains->mSrc[1].mRange.SetLimit(1, icount); + ains->mDst.mRange.SetLimit(0, icount - 1); + cins->mSrc[1].mRange.SetLimit(0, icount - 1); + cins->mOperator = IA_CMPNE; + + for (int i = 0; i < body.Size(); i++) + { + if (body[i]->mEntryValueRange.Size() > 0) + body[i]->mEntryValueRange[ains->mSrc[1].mTemp] = ains->mSrc[1].mRange; + } + } + } + } + } + } + } + + } + } + + if (mTrueJump) mTrueJump->InnerLoopCountZeroCheck(); + if (mFalseJump) mFalseJump->InnerLoopCountZeroCheck(); + } +} + +void InterCodeBasicBlock::SingleLoopCountZeroCheck(void) +{ + if (!mVisited) + { + mVisited = true; + + if (mLoopHead && mEntryBlocks.Size() == 2 && mFalseJump && (mTrueJump == this || mFalseJump == this) && mInstructions.Size() > 3) + { + int nins = mInstructions.Size(); + + InterCodeBasicBlock * pblock = mEntryBlocks[0]; + if (pblock == this) + pblock = mEntryBlocks[1]; + while (pblock->mInstructions.Size() == 1 && pblock->mInstructions[0]->mCode == IC_JUMP && pblock->mEntryBlocks.Size() == 1) + pblock = pblock->mEntryBlocks[0]; + + if (mInstructions[nins - 1]->mCode == IC_BRANCH && + mInstructions[nins - 2]->mCode == IC_RELATIONAL_OPERATOR && + mInstructions[nins - 3]->mCode == IC_BINARY_OPERATOR && mInstructions[nins - 3]->mOperator == IA_ADD) + { + InterInstruction* ains = mInstructions[nins - 3]; + InterInstruction* cins = mInstructions[nins - 2]; + InterInstruction* bins = mInstructions[nins - 1]; + + if (bins->mSrc[0].mTemp == cins->mDst.mTemp && + cins->mSrc[1].mTemp == ains->mDst.mTemp && + cins->mSrc[0].mTemp < 0 && + ains->mSrc[1].mTemp == ains->mDst.mTemp && + ains->mSrc[0].mTemp < 0 && + cins->mOperator == IA_CMPGS && + ains->mSrc[0].mIntConst < -1 && + cins->mSrc[0].mIntConst > 0 && + cins->mSrc[0].mIntConst < - ains->mSrc[0].mIntConst && + !IsTempModifiedInRange(0, nins - 3, ains->mDst.mTemp)) + { + int pi = pblock->mInstructions.Size() - 1; + while (pi >= 0 && pblock->mInstructions[pi]->mDst.mTemp != ains->mDst.mTemp) + pi--; + if (pi >= 0 && pblock->mInstructions[pi]->mCode == IC_CONSTANT) + { + int64 istart = pblock->mInstructions[pi]->mConst.mIntConst; + if (istart > 0) + { + int64 iend = istart % -ains->mSrc[0].mIntConst; + + if (cins->mSrc[0].mIntConst < iend) + { + cins->mSrc[0].mIntConst = 0; + cins->mOperator = IA_CMPGES; + } + } + } + } + + } + } + + if (mTrueJump) mTrueJump->SingleLoopCountZeroCheck(); + if (mFalseJump) mFalseJump->SingleLoopCountZeroCheck(); + } +} + +bool InterCodeBasicBlock::MoveConditionOutOfLoop(void) +{ + if (!mVisited) + { + mVisited = true; + + if (mLoopHead) + { + ExpandingArray body, path; + body.Push(this); + bool innerLoop = true; + + for (int i = 0; i < mEntryBlocks.Size(); i++) + { + if (mEntryBlocks[i] != mLoopPrefix) + { + if (!mEntryBlocks[i]->CollectLoopBody(this, body)) + innerLoop = false; + } + } + + if (innerLoop) + { + int nscale = 4, nlimit = 4, nmaxlimit = 8; + if (mProc->mCompilerOptions & COPT_OPTIMIZE_AUTO_UNROLL) + { + nscale = 1; + nlimit = 10; + nmaxlimit = 16; + } + + // Find all conditions based on invariants + for (int i = 0; i < body.Size(); i++) + { + InterCodeBasicBlock* block = body[i]; + int nins = block->mInstructions.Size(); + if (block->mFalseJump && block->mInstructions[nins-1]->mCode == IC_BRANCH && body.Contains(block->mFalseJump) && body.Contains(block->mTrueJump)) + { + int ncins = 0; + if (!IsInsSrcModifiedInBlocks(body, block->mInstructions[nins - 1])) + ncins = 1; + else if (nins > 1 && block->mInstructions[nins - 2]->mCode == IC_RELATIONAL_OPERATOR && + block->mInstructions[nins - 1]->mSrc[0].mTemp == block->mInstructions[nins - 2]->mDst.mTemp && block->mInstructions[nins - 1]->mSrc[0].mFinal && + !IsInsSrcModifiedInBlocks(body, block->mInstructions[nins - 2])) + ncins = 2; + + if (ncins > 0) + { + // The condition is not modified on the path + // Now check the number of instructions in the conditional section + + int ninside = 0, noutside = 0; + for (int i = 0; i < body.Size(); i++) + { + bool tdom = block->mTrueJump->IsDirectLoopPathBlock(body[i]); + bool fdom = block->mFalseJump->IsDirectLoopPathBlock(body[i]); + if (tdom != fdom) + ninside += body[i]->mInstructions.Size() + 1; + else + noutside += body[i]->mInstructions.Size() + 1; + } + + // Less than four instructions outside of condition, or twice as many + // inside as outside is the trigger + if (noutside - ncins < nmaxlimit && (noutside - ncins < nlimit || 2 * ninside > nscale * (noutside - ncins))) + { + // Now clone the loop into a true and a false branch + + GrowingArray copies(nullptr); + for (int i = 0; i < body.Size(); i++) + { + InterCodeBasicBlock* nblock = body[i]->Clone(); + copies[body[i]->mIndex] = nblock; + } + + for (int i = 0; i < body.Size(); i++) + { + InterCodeBasicBlock* rblock = body[i]; + InterCodeBasicBlock* nblock = copies[rblock->mIndex]; + if (rblock->mTrueJump) + { + InterCodeBasicBlock* tblock = copies[rblock->mTrueJump->mIndex]; + if (tblock) + nblock->mTrueJump = tblock; + else + nblock->mTrueJump = rblock->mTrueJump; + } + if (rblock->mFalseJump) + { + InterCodeBasicBlock* tblock = copies[rblock->mFalseJump->mIndex]; + if (tblock) + nblock->mFalseJump = tblock; + else + nblock->mFalseJump = rblock->mFalseJump; + } + } + mLoopPrefix->mInstructions.Pop(); + for (int i = 0; i < ncins; i++) + mLoopPrefix->mInstructions.Push(block->mInstructions[nins - ncins + i]->Clone()); + + block->mInstructions[nins - 1]->mSrc[0].mType = IT_BOOL; + block->mInstructions[nins - 1]->mSrc[0].mTemp = -1; + block->mInstructions[nins - 1]->mSrc[0].mIntConst = 1; + + mLoopPrefix->mFalseJump = copies[mLoopPrefix->mTrueJump->mIndex]; + + InterCodeBasicBlock* nblock = copies[block->mIndex]; + nblock->mInstructions[nins - 1]->mSrc[0].mType = IT_BOOL; + nblock->mInstructions[nins - 1]->mSrc[0].mTemp = -1; + nblock->mInstructions[nins - 1]->mSrc[0].mIntConst = 0; + + return true; + } + } + } + } + } + else if (mEntryBlocks.Size() == 2 && mInstructions.Size() == 1 && mInstructions[0]->mCode == IC_BRANCH && mInstructions[0]->mSrc[0].mTemp >= 0) + { + InterCodeBasicBlock* tail, * post; + + if (mEntryBlocks[0] == mLoopPrefix) + tail = mEntryBlocks[1]; + else + tail = mEntryBlocks[0]; + + if (mTrueJump == tail || mFalseJump == tail) + { + if (tail->mTrueJump == this) + post = tail->mFalseJump; + else + post = tail->mTrueJump; + + if (post && post->mNumEntries == 1) + { + ExpandingArray lbody; + + if (tail->CollectSingleHeadLoopBody(this, tail, lbody)) + { + int tz = tail->mInstructions.Size(); + int ct = mInstructions[0]->mSrc[0].mTemp; + + int i = 0; + while (i < lbody.Size() && !lbody[i]->IsTempModified(ct)) + i++; + + if (i == lbody.Size()) + { + i = 0; + while (i < tz && !IsObservable(tail->mInstructions[i]->mCode) && (tail->mInstructions[i]->mDst.mTemp < 0 || !post->mEntryRequiredTemps[tail->mInstructions[i]->mDst.mTemp])) + i++; + + if (i == tz) + { + InterInstruction * ins = mLoopPrefix->mInstructions.Pop(); + mLoopPrefix->mInstructions.Push(mInstructions.Pop()); + mInstructions.Push(ins); + + tail->mEntryBlocks.RemoveAll(this); + tail->mNumEntries--; + + post->mEntryBlocks.Push(mLoopPrefix); + post->mNumEntries++; + + if (mTrueJump == tail) + { + mTrueJump = mFalseJump; + mLoopPrefix->mTrueJump = post; + mLoopPrefix->mFalseJump = this; + } + else + mLoopPrefix->mFalseJump = post; + mFalseJump = nullptr; + + return true; + } + } + } + } + } + } + } + + if (mTrueJump && mTrueJump->MoveConditionOutOfLoop()) + return true; + if (mFalseJump && mFalseJump->MoveConditionOutOfLoop()) + return true; + } + + return false; +} + + + +void InterCodeBasicBlock::PushMoveOutOfLoop(void) +{ + if (!mVisited) + { + mVisited = true; + + if (mTrueJump && mFalseJump) + { + InterCodeBasicBlock* eblock = nullptr, * lblock = nullptr; + + if (mTrueJump->mLoopHead) + { + lblock = mTrueJump; + eblock = mFalseJump; + } + else if (mFalseJump->mLoopHead) + { + lblock = mFalseJump; + eblock = mTrueJump; + } + + if (eblock) + { + int i = 0; + while (i < mInstructions.Size()) + { + InterInstruction* mins = mInstructions[i]; + if (mins->mCode == IC_LOAD_TEMPORARY && !mins->mSrc[0].mFinal) + { + if (!lblock->mEntryRequiredTemps[mins->mDst.mTemp] && eblock->mEntryRequiredTemps[mins->mDst.mTemp] && !eblock->mExitRequiredTemps[mins->mDst.mTemp]) + { + int offset = 0; + int j = i + 1; + bool fail = false; + + while (j < mInstructions.Size() && !fail) + { + InterInstruction* cins = mInstructions[j]; + if (cins->ReferencesTemp(mins->mDst.mTemp)) + fail = true; + else if (cins->mDst.mTemp == mins->mSrc[0].mTemp) + { + if (cins->mCode == IC_LEA && cins->mSrc[1].mTemp == mins->mSrc[0].mTemp && cins->mSrc[0].mTemp < 0) + offset += int(cins->mSrc[0].mIntConst); + else + fail = true; + } + j++; + } + + if (!fail) + { + int j = 0; + while (j < eblock->mInstructions.Size() && !fail) + { + InterInstruction* cins = eblock->mInstructions[j]; + + if (cins->ReferencesTemp(mins->mDst.mTemp)) + { + if (cins->mCode == IC_LEA && cins->mSrc[1].mTemp == mins->mDst.mTemp && cins->mSrc[0].mTemp < 0) + { + if (cins->mSrc[1].mFinal) + break; + } + else + fail = true; + } + + if (cins->mDst.mTemp == mins->mSrc[0].mTemp) + fail = true; + + j++; + } + + if (!fail) + { + eblock->mEntryRequiredTemps += mins->mSrc[0].mTemp; + + j = 0; + while (j < eblock->mInstructions.Size()) + { + InterInstruction* cins = eblock->mInstructions[j]; + if (cins->ReferencesTemp(mins->mDst.mTemp)) + { + if (cins->mCode == IC_LEA && cins->mSrc[1].mTemp == mins->mDst.mTemp && cins->mSrc[0].mTemp < 0) + { + cins->mSrc[1].mTemp = mins->mSrc[0].mTemp; + cins->mSrc[0].mIntConst -= offset; + + if (cins->mSrc[1].mFinal) + break; + } + } + j++; + } + } + } + } + } + + i++; + } + } + } + + if (mTrueJump) + mTrueJump->PushMoveOutOfLoop(); + if (mFalseJump) + mFalseJump->PushMoveOutOfLoop(); + } +} + +bool InterCodeBasicBlock::CheckSingleBlockLimitedLoop(InterCodeBasicBlock*& pblock, int64& nloop, bool& nfixed) +{ + if (mLoopHead && mEntryBlocks.Size() == 2 && mFalseJump && (mTrueJump == this || mFalseJump == this) && mInstructions.Size() > 3) + { + int nins = mInstructions.Size(); + + pblock = mEntryBlocks[0]; + if (pblock == this) + pblock = mEntryBlocks[1]; + + if (mInstructions[nins - 1]->mCode == IC_BRANCH && + mInstructions[nins - 2]->mCode == IC_RELATIONAL_OPERATOR && + mInstructions[nins - 3]->mCode == IC_BINARY_OPERATOR && mInstructions[nins - 3]->mOperator == IA_ADD) + { + InterInstruction* ains = mInstructions[nins - 3]; + InterInstruction* cins = mInstructions[nins - 2]; + InterInstruction* bins = mInstructions[nins - 1]; + + if (bins->mSrc[0].mTemp == cins->mDst.mTemp && + cins->mSrc[1].mTemp == ains->mDst.mTemp && + cins->mSrc[0].mTemp < 0 && + ains->mSrc[1].mTemp == ains->mDst.mTemp && + ains->mSrc[0].mTemp < 0 && + (cins->mOperator == IA_CMPLU || cins->mOperator == IA_CMPLEU) && + cins->mSrc[0].mIntConst < 255 && + ains->mSrc[0].mIntConst > 0) + { + InterInstruction* iins = pblock->FindTempOriginSinglePath(ains->mDst.mTemp); + + if (iins && iins->mCode == IC_CONSTANT) + { + int i = 0; + while (i < nins - 3 && mInstructions[i]->mDst.mTemp != ains->mDst.mTemp) + i++; + if (i == nins - 3) + { + nloop = cins->mSrc[0].mIntConst - iins->mConst.mIntConst; + if (cins->mOperator == IA_CMPLEU) + nloop++; + nloop = (nloop + ains->mSrc[0].mIntConst - 1) / ains->mSrc[0].mIntConst; + nfixed = true; + + return true; + } + } + } + else if (bins->mSrc[0].mTemp == cins->mDst.mTemp && + cins->mSrc[1].mTemp == ains->mDst.mTemp && + cins->mSrc[0].mRange.mMaxState == IntegerValueRange::S_BOUND && + ains->mSrc[1].mTemp == ains->mDst.mTemp && + ains->mSrc[0].mTemp < 0 && + (cins->mOperator == IA_CMPLU || cins->mOperator == IA_CMPLEU) && + cins->mSrc[0].mRange.mMaxValue < 255 && + ains->mSrc[0].mRange.mMaxValue > 0) + { + InterInstruction* iins = pblock->FindTempOriginSinglePath(ains->mDst.mTemp); + + if (iins && iins->mCode == IC_CONSTANT) + { + int i = 0; + while (i < nins - 3 && mInstructions[i]->mDst.mTemp != ains->mDst.mTemp) + i++; + if (i == nins - 3) + { + nloop = cins->mSrc[0].mRange.mMaxValue - iins->mConst.mIntConst; + if (cins->mOperator == IA_CMPLEU) + nloop++; + nloop = (nloop + ains->mSrc[0].mIntConst - 1) / ains->mSrc[0].mIntConst; + nfixed = false; + + return true; + } + } + } + } + else if (mInstructions[nins - 1]->mCode == IC_BRANCH && + mInstructions[nins - 2]->mCode == IC_RELATIONAL_OPERATOR && + mInstructions[nins - 3]->mCode == IC_BINARY_OPERATOR && mInstructions[nins - 3]->mOperator == IA_SUB) + { + InterInstruction* ains = mInstructions[nins - 3]; + InterInstruction* cins = mInstructions[nins - 2]; + InterInstruction* bins = mInstructions[nins - 1]; + + if (bins->mSrc[0].mTemp == cins->mDst.mTemp && + cins->mSrc[1].mTemp == ains->mDst.mTemp && + cins->mSrc[0].mTemp < 0 && + ains->mSrc[1].mTemp == ains->mDst.mTemp && + ains->mSrc[0].mTemp < 0 && + (cins->mOperator == IA_CMPGU || cins->mOperator == IA_CMPGEU) && + ains->mSrc[0].mIntConst > 0) + { + InterInstruction* iins = pblock->FindTempOriginSinglePath(ains->mDst.mTemp); + + if (iins && iins->mCode == IC_CONSTANT) + { + int i = 0; + while (i < nins - 3 && mInstructions[i]->mDst.mTemp != ains->mDst.mTemp) + i++; + if (i == nins - 3) + { + nloop = iins->mConst.mIntConst - cins->mSrc[0].mIntConst; + if (cins->mOperator == IA_CMPGEU) + nloop++; + nloop = (nloop + ains->mSrc[0].mIntConst - 1) / ains->mSrc[0].mIntConst; + nfixed = true; + + return true; + } + } + } + } + else if ( + mInstructions[nins - 1]->mCode == IC_BRANCH && + mInstructions[nins - 2]->mCode == IC_BINARY_OPERATOR && mInstructions[nins - 2]->mOperator == IA_ADD) + { + InterInstruction* ains = mInstructions[nins - 2]; + InterInstruction* bins = mInstructions[nins - 1]; + + if (bins->mSrc[0].mTemp == ains->mDst.mTemp && + ains->mSrc[1].mTemp == ains->mDst.mTemp && + ains->mSrc[0].mTemp < 0 && + ains->mSrc[0].mIntConst == -1) + { + InterInstruction* iins = pblock->FindTempOriginSinglePath(ains->mDst.mTemp); + + if (iins && iins->mCode == IC_CONSTANT) + { + int i = 0; + while (i < nins - 2 && mInstructions[i]->mDst.mTemp != ains->mDst.mTemp) + i++; + if (i == nins - 2) + { + nloop = iins->mConst.mIntConst; + + mProc->mLocalValueRange[ains->mDst.mTemp].LimitMin(1); + mProc->mLocalValueRange[ains->mDst.mTemp].LimitMax(iins->mConst.mIntConst); + + nfixed = false; + return true; + } + } + } + } + else if ( + mInstructions[nins - 1]->mCode == IC_BRANCH && + mInstructions[nins - 2]->mCode == IC_RELATIONAL_OPERATOR && + mInstructions[nins - 3]->mCode == IC_LEA) + { + InterInstruction* ains = mInstructions[nins - 3]; + InterInstruction* cins = mInstructions[nins - 2]; + InterInstruction* bins = mInstructions[nins - 1]; + + if (bins->mSrc[0].mTemp == cins->mDst.mTemp && + (cins->mSrc[1].mTemp == ains->mDst.mTemp && cins->mSrc[0].mTemp < 0 || cins->mSrc[0].mTemp == ains->mDst.mTemp && cins->mSrc[1].mTemp < 0) && + ains->mSrc[1].mTemp == ains->mDst.mTemp && + ains->mSrc[0].mTemp < 0 && + cins->mOperator == IA_CMPNE && + ains->mSrc[0].mIntConst != 0) + { + int ci = cins->mSrc[0].mTemp < 0 ? 0 : 1; + + InterInstruction* iins = pblock->FindTempOriginSinglePath(ains->mDst.mTemp); + + if (iins && iins->mCode == IC_CONSTANT) + { + int i = 0; + while (i < nins - 3 && mInstructions[i]->mDst.mTemp != ains->mDst.mTemp) + i++; + + if (i == nins - 3 && SameMemRegion(iins->mConst, cins->mSrc[ci])) + { + int64 ndiv = cins->mSrc[ci].mIntConst - iins->mConst.mIntConst; + nloop = ndiv / ains->mSrc[0].mIntConst; + if (ndiv == nloop * ains->mSrc[0].mIntConst) + { + nfixed = true; + return true; + } + } + } + } + } + } + + return false; +} + +bool InterCodeBasicBlock::SingleBlockLoopIndexReduction(int& spareTemps) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + if (mLoopHead && mEntryBlocks.Size() == 2 && mFalseJump && (mTrueJump == this || mFalseJump == this) && mInstructions.Size() > 3) + { + int nins = mInstructions.Size(); + + InterCodeBasicBlock* pblock = mEntryBlocks[0], * eblock = mFalseJump; + if (pblock == this) + pblock = mEntryBlocks[1]; + if (eblock == this) + eblock = mTrueJump; + + if (mInstructions[nins - 1]->mCode == IC_BRANCH && + mInstructions[nins - 2]->mCode == IC_RELATIONAL_OPERATOR && + mInstructions[nins - 3]->mCode == IC_BINARY_OPERATOR && mInstructions[nins - 3]->mOperator == IA_ADD) + { + InterInstruction* ains = mInstructions[nins - 3]; + InterInstruction* cins = mInstructions[nins - 2]; + InterInstruction* bins = mInstructions[nins - 1]; + + int itemp = ains->mDst.mTemp; + + if (bins->mSrc[0].mTemp == cins->mDst.mTemp && + cins->mSrc[1].mTemp == itemp && + cins->mSrc[0].mTemp >= 0 && + ains->mSrc[1].mTemp == itemp && + ains->mSrc[0].mTemp < 0 && + cins->mOperator == IA_CMPLU && + ains->mSrc[0].mIntConst > 1 && + ispow2(ains->mSrc[0].mIntConst) && + !IsTempReferencedInRange(0, nins - 3, itemp) && + !IsTempModifiedInRange(0, nins - 3, cins->mSrc[0].mTemp) && + !eblock->mEntryRequiredTemps[itemp]) + { + int pi = pblock->mInstructions.Size() - 1; + while (pi >= 0 && pblock->mInstructions[pi]->mDst.mTemp != itemp) + pi--; + if (pi >= 0 && pblock->mInstructions[pi]->mCode == IC_CONSTANT) + { + int64 istart = pblock->mInstructions[pi]->mConst.mIntConst; + if (istart == 0) + { + InterInstruction* tins = new InterInstruction(cins->mLocation, IC_BINARY_OPERATOR); + tins->mOperator = IA_SHR; + tins->mSrc[1] = cins->mSrc[0]; + tins->mSrc[0].mTemp = -1; + tins->mSrc[0].mType = IT_INT16; + tins->mSrc[0].mIntConst = binlog(ains->mSrc[0].mIntConst); + tins->mDst = tins->mSrc[1]; + tins->mDst.mRange.mMinValue >>= tins->mSrc[0].mIntConst; + tins->mDst.mRange.mMaxValue >>= tins->mSrc[0].mIntConst; + tins->mDst.mTemp = spareTemps++; + + ains->mSrc[0].mIntConst = 1; + cins->mSrc[0] = tins->mDst; + + pblock->AppendBeforeBranch(tins); + changed = true; + } + } + } + + } + } + + if (mTrueJump && mTrueJump->SingleBlockLoopIndexReduction(spareTemps)) changed = true; + if (mFalseJump && mFalseJump->SingleBlockLoopIndexReduction(spareTemps)) changed = true; + } + + return changed; +} + + +bool InterCodeBasicBlock::SingleBlockLoopSinking(int& spareTemps) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + if (mLoopHead && mNumEntries == 2 && mFalseJump && (mTrueJump == this || mFalseJump == this) && mInstructions.Size() > 3) + { + InterCodeBasicBlock* pblock = mEntryBlocks[0], * eblock = mFalseJump; + if (pblock == this) + pblock = mEntryBlocks[1]; + if (eblock == this) + eblock = mTrueJump; + + if (eblock->mNumEntries == 1) + { + int j = mInstructions.Size() - 1; + while (j >= 0) + { + InterInstruction* ins = mInstructions[j]; + + if (ins->mCode != IC_LOAD_TEMPORARY && !HasSideEffect(ins->mCode) && ins->mDst.mTemp >= 0 && !IsTempUsedInRange(0, mInstructions.Size(), ins->mDst.mTemp) && !ins->mVolatile) + { + if (spareTemps + ins->mNumOperands >= mEntryRequiredTemps.Size() + 16) + return true; + + mInstructions.Remove(j); + eblock->mInstructions.Insert(0, ins); + + for (int i = 0; i < ins->mNumOperands; i++) + { + if (ins->mSrc[i].mTemp >= 0 && IsTempModifiedInRange(j, mInstructions.Size(), ins->mSrc[i].mTemp)) + { + InterInstruction* tins = new InterInstruction(ins->mLocation, IC_LOAD_TEMPORARY); + tins->mSrc[0] = ins->mSrc[i]; + tins->mDst = tins->mSrc[0]; + tins->mDst.mTemp = spareTemps++; + ins->mSrc[i] = tins->mDst; + mInstructions.Insert(j, tins); + } + } + + changed = true; + } + + j--; + } + } + } + + if (mTrueJump && mTrueJump->SingleBlockLoopSinking(spareTemps)) + changed = true; + if (mFalseJump && mFalseJump->SingleBlockLoopSinking(spareTemps)) + changed = true; + } + + return changed; +} + +bool InterCodeBasicBlock::SingleBlockLoopPointerToByte(int& spareTemps) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + if (mLoopHead && mNumEntries == 2 && mFalseJump && (mTrueJump == this || mFalseJump == this) && mInstructions.Size() > 3) + { + int nins = mInstructions.Size(); + + InterCodeBasicBlock* pblock = mEntryBlocks[0], * eblock = mFalseJump; + if (pblock == this) + pblock = mEntryBlocks[1]; + if (eblock == this) + eblock = mTrueJump; + + if (mInstructions[nins - 1]->mCode == IC_BRANCH && + mInstructions[nins - 2]->mCode == IC_RELATIONAL_OPERATOR && + mInstructions[nins - 3]->mCode == IC_BINARY_OPERATOR && mInstructions[nins - 3]->mOperator == IA_ADD) + { + InterInstruction* ains = mInstructions[nins - 3]; + InterInstruction* cins = mInstructions[nins - 2]; + InterInstruction* bins = mInstructions[nins - 1]; + + if (bins->mSrc[0].mTemp == cins->mDst.mTemp && + cins->mSrc[1].mTemp == ains->mDst.mTemp && + cins->mSrc[0].mTemp < 0 && + ains->mSrc[1].mTemp == ains->mDst.mTemp && + ains->mSrc[0].mTemp < 0 && + (cins->mOperator == IA_CMPLU || cins->mOperator == IA_CMPLEU) && + cins->mSrc[0].mIntConst < 255 && + ains->mSrc[0].mIntConst > 0) + { + GrowingArray tvalues(nullptr); + tvalues.SetSize(mEntryRequiredTemps.Size() + 16); + + GrowingArray mtemps(-1); + + int pi = pblock->mInstructions.Size() - 1; + while (pi >= 0 && pblock->mInstructions[pi]->mDst.mTemp != ains->mDst.mTemp) + pi--; + + int i = 0; + while (i < nins - 3 && mInstructions[i]->mDst.mTemp != ains->mDst.mTemp) + i++; + if (i == nins - 3) + { + int nloop = int(cins->mSrc[0].mIntConst); + if (cins->mOperator == IA_CMPLEU) + nloop++; + nloop /= int(ains->mSrc[0].mIntConst); + + for (int i = 0; i < mInstructions.Size() - 3; i++) + { + InterInstruction* lins = mInstructions[i]; + if (lins->mCode == IC_LEA && lins->mDst.mTemp == lins->mSrc[1].mTemp && lins->mSrc[0].mTemp < 0 && lins->mSrc[0].mIntConst > 0 && lins->mSrc[0].mIntConst * nloop < 256 && + !IsTempReferencedInRange(i + 1, mInstructions.Size(), lins->mDst.mTemp) && !IsTempModifiedInRange(0, i, lins->mDst.mTemp) && + !eblock->mEntryRequiredTemps[lins->mDst.mTemp]) + { + bool isglobal = false; + InterInstruction* slins = pblock->FindTempOrigin(lins->mSrc[1].mTemp); + if (slins) + { + if (slins->mCode == IC_CONSTANT) + { + if (slins->mConst.mMemory == IM_ABSOLUTE || slins->mConst.mMemory == IM_GLOBAL) + isglobal = true; + } + else if (slins->mCode == IC_LEA) + { + if (slins->mSrc[0].mTemp < 0) + { + if (slins->mSrc[1].mMemory == IM_ABSOLUTE || slins->mSrc[1].mMemory == IM_GLOBAL) + isglobal = true; + } + } + } + + bool failed = false; + for (int j = 0; j < i; j++) + { + InterInstruction* jins = mInstructions[j]; + + if (jins->ReferencesTemp(lins->mDst.mTemp)) + { + if (jins->mCode == IC_LOAD) + { + if (!isglobal && (jins->mSrc[0].mIntConst < 0 || jins->mSrc[0].mIntConst > 2)) + failed = true; + } + else if (jins->mCode == IC_STORE) + { + if (!isglobal && (jins->mSrc[1].mIntConst < 0 || jins->mSrc[1].mIntConst > 2)) + failed = true; + } + else + failed = true; + } + } + + if (!failed) + { + if (spareTemps + 2 >= mEntryRequiredTemps.Size() + 16) + return true; + + int inc = int(lins->mSrc[0].mIntConst); + + int ireg = mtemps[inc]; + + if (ireg < 0) + { + ireg = spareTemps++; + + InterInstruction* cins = new InterInstruction(lins->mLocation, IC_CONSTANT); + cins->mDst.mTemp = ireg; + cins->mDst.mType = IT_INT16; + cins->mConst.mType = IT_INT16; + cins->mConst.mIntConst = 0; + mtemps[inc] = cins->mDst.mTemp; + + pblock->mInstructions.Insert(pblock->mInstructions.Size() - 1, cins); + + InterInstruction* iins = new InterInstruction(lins->mLocation, IC_BINARY_OPERATOR); + iins->mNumOperands = 2; + iins->mOperator = IA_ADD; + iins->mDst = cins->mDst; + iins->mSrc[1] = cins->mDst; + iins->mSrc[0].mTemp = -1; + iins->mSrc[0].mType = IT_INT16; + iins->mSrc[0].mIntConst = lins->mSrc[0].mIntConst; + iins->mSrc[1].mRange.SetLimit(0, inc * (nloop - 1)); + iins->mDst.mRange.SetLimit(lins->mSrc[0].mIntConst, inc * nloop); + mInstructions.Insert(i, iins); + } + + InterInstruction* nins = new InterInstruction(lins->mLocation, IC_LEA); + nins->mNumOperands = 2; + nins->mDst.mTemp = spareTemps++; + nins->mDst.mType = IT_POINTER; + nins->mSrc[1] = lins->mSrc[1]; + nins->mSrc[1].mFinal = false; + nins->mSrc[0].mType = IT_INT16; + nins->mSrc[0].mTemp = ireg; + nins->mSrc[0].mRange.SetLimit(0, inc * (nloop - 1)); + + for (int j = 0; j < i; j++) + { + InterInstruction* jins = mInstructions[j]; + for (int k = 0; k < jins->mNumOperands; k++) + if (jins->mSrc[k].mTemp == lins->mDst.mTemp) + jins->mSrc[k].mTemp = nins->mDst.mTemp; + } + + mInstructions.Insert(0, nins); + + lins->mCode = IC_NONE; + lins->mNumOperands = 0; + lins->mDst.mTemp = -1; + + changed = true; + } + } + } + } + } + } + } + + if (mTrueJump && mTrueJump->SingleBlockLoopPointerToByte(spareTemps)) + changed = true; + if (mFalseJump && mFalseJump->SingleBlockLoopPointerToByte(spareTemps)) + changed = true; + } + + return changed; +} + +bool InterCodeBasicBlock::SingleBlockLoopPointerSplit(int& spareTemps) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + if (mLoopHead && mNumEntries == 2 && mFalseJump && (mTrueJump == this || mFalseJump == this) && mInstructions.Size() > 3) + { + int nins = mInstructions.Size(); + + InterCodeBasicBlock* pblock = mEntryBlocks[0], *eblock = mFalseJump; + if (pblock == this) + pblock = mEntryBlocks[1]; + if (eblock == this) + eblock = mTrueJump; + + if (mInstructions[nins - 1]->mCode == IC_BRANCH && + mInstructions[nins - 2]->mCode == IC_RELATIONAL_OPERATOR && + mInstructions[nins - 3]->mCode == IC_BINARY_OPERATOR && mInstructions[nins - 3]->mOperator == IA_ADD) + { + InterInstruction* ains = mInstructions[nins - 3]; + InterInstruction* cins = mInstructions[nins - 2]; + InterInstruction* bins = mInstructions[nins - 1]; + + if (bins->mSrc[0].mTemp == cins->mDst.mTemp && + cins->mSrc[1].mTemp == ains->mDst.mTemp && + cins->mSrc[0].mTemp < 0 && + ains->mSrc[1].mTemp == ains->mDst.mTemp && + ains->mSrc[0].mTemp < 0) + { + GrowingArray tvalues(nullptr); + tvalues.SetSize(mEntryRequiredTemps.Size() + 16); + + int pi = pblock->mInstructions.Size() - 1; + while (pi >= 0 && pblock->mInstructions[pi]->mDst.mTemp != ains->mDst.mTemp) + pi--; + + int i = 0; + while (i < nins - 3 && mInstructions[i]->mDst.mTemp != ains->mDst.mTemp) + i++; + if (i == nins - 3) + { + InterInstruction* xins = nullptr; + for (int i = 0; i < mInstructions.Size() - 3; i++) + { + InterInstruction* lins = mInstructions[i]; + + if (xins && lins->mDst.mTemp >= 0 && lins->mDst.mTemp == xins->mDst.mTemp) + xins = nullptr; + + if (lins->mCode == IC_LEA && lins->mSrc[0].mTemp == ains->mDst.mTemp && lins->mSrc[0].IsUByte() && lins->mSrc[1].mTemp >= 0 && !mLocalModifiedTemps[lins->mSrc[1].mTemp]) + { + tvalues[lins->mDst.mTemp] = lins; + } + else if (lins->mCode == IC_LEA && xins && lins->mSrc[0].mTemp == xins->mDst.mTemp && lins->mSrc[0].IsUByte() && lins->mSrc[1].mTemp >= 0 && !mLocalModifiedTemps[lins->mSrc[1].mTemp]) + { + tvalues[lins->mDst.mTemp] = lins; + } + else if (lins->mCode == IC_CONVERSION_OPERATOR && lins->mOperator == IA_EXT8TO16U && lins->mSrc[0].mTemp == ains->mDst.mTemp && lins->mSrc[0].IsUByte()) + { + xins = lins; + } + else if (lins->mCode == IC_LEA && lins->mSrc[0].mTemp < 0 && lins->mSrc[0].mIntConst == ains->mSrc[0].mIntConst && lins->mSrc[1].mTemp == lins->mDst.mTemp && + pi >= 0 && pblock->mInstructions[pi]->mCode == IC_CONSTANT && ains->mSrc[1].IsUByte() && pblock->mInstructions[pi]->mConst.mIntConst == 0 && + !IsTempReferencedInRange(i + 1, mInstructions.Size(), lins->mDst.mTemp) && !IsTempModifiedInRange(0, i, lins->mDst.mTemp) && + !eblock->mEntryRequiredTemps[lins->mDst.mTemp]) + { + if (spareTemps + 2 >= mEntryRequiredTemps.Size() + 16) + return true; + + + InterInstruction* nins = new InterInstruction(lins->mLocation, IC_LEA); + InterInstruction* cins = nullptr; + nins->mSrc[1] = lins->mSrc[1]; + + if (ains->mDst.mType == IT_INT16) + nins->mSrc[0] = ains->mSrc[1]; + else + { + cins = new InterInstruction(lins->mLocation, IC_CONVERSION_OPERATOR); + cins->mOperator = IA_EXT8TO16U; + cins->mSrc[0] = ains->mSrc[1]; + cins->mDst.mMemory = IM_INDIRECT; + cins->mDst.mTemp = spareTemps++; + cins->mDst.mType = IT_INT16; + nins->mSrc[0] = cins->mDst; + } + + nins->mDst.mMemory = IM_INDIRECT; + nins->mDst.mTemp = spareTemps++; + nins->mDst.mType = IT_POINTER; + + for (int j = 0; j < i; j++) + { + InterInstruction* tins = mInstructions[j]; + for (int k = 0; k < tins->mNumOperands; k++) + { + if (tins->mSrc[k].mTemp == lins->mDst.mTemp) + tins->mSrc[k].mTemp = nins->mDst.mTemp; + } + } + + mInstructions.Remove(i); + mInstructions.Insert(0, nins); + if (cins) + mInstructions.Insert(0, cins); + + changed = true; + } + else if (lins->mCode == IC_STORE && lins->mSrc[1].mTemp >= 0 && lins->mSrc[1].mIntConst >= 32 && tvalues[lins->mSrc[1].mTemp]) + { + if (spareTemps + 2 >= mEntryRequiredTemps.Size() + 16) + return true; + + InterInstruction* pins = tvalues[lins->mSrc[1].mTemp]; + InterInstruction* nins = new InterInstruction(lins->mLocation, IC_LEA); + nins->mSrc[1] = pins->mSrc[1]; + nins->mSrc[0].mTemp = -1; + nins->mSrc[0].mType = IT_INT16; + nins->mSrc[0].mIntConst = lins->mSrc[1].mIntConst; + nins->mDst.mMemory = IM_INDIRECT; + nins->mDst.mTemp = spareTemps++; + nins->mDst.mType = IT_POINTER; + + pblock->mInstructions.Insert(pblock->mInstructions.Size() - 1, nins); + + InterInstruction* mins = pins->Clone(); + mins->mDst.mTemp = spareTemps++; + mins->mDst.mMemory = IM_INDIRECT; + mins->mDst.mRange.Reset(); + mins->mSrc[1] = nins->mDst; + mInstructions.Insert(i, mins); + + + lins->mSrc[1].mTemp = mins->mDst.mTemp; + lins->mSrc[1].mIntConst = 0; + lins->mSrc[1].mRange.Reset(); + + changed = true; + } + else if (lins->mCode == IC_LOAD && lins->mSrc[0].mTemp >= 0 && lins->mSrc[0].mIntConst >= 16 && tvalues[lins->mSrc[0].mTemp]) + { + if (spareTemps + 2 >= mEntryRequiredTemps.Size() + 16) + return true; + + InterInstruction* pins = tvalues[lins->mSrc[0].mTemp]; + InterInstruction* nins = new InterInstruction(lins->mLocation, IC_LEA); + nins->mSrc[1] = pins->mSrc[1]; + nins->mSrc[0].mTemp = -1; + nins->mSrc[0].mType = IT_INT16; + nins->mSrc[0].mIntConst = lins->mSrc[0].mIntConst; + nins->mDst.mMemory = IM_INDIRECT; + nins->mDst.mTemp = spareTemps++; + nins->mDst.mType = IT_POINTER; + + pblock->mInstructions.Insert(pblock->mInstructions.Size() - 1, nins); + + InterInstruction* mins = pins->Clone(); + mins->mDst.mTemp = spareTemps++; + mins->mDst.mMemory = IM_INDIRECT; + mins->mDst.mRange.Reset(); + mins->mSrc[1] = nins->mDst; + mInstructions.Insert(i, mins); + + + lins->mSrc[0].mTemp = mins->mDst.mTemp; + lins->mSrc[0].mIntConst = 0; + lins->mSrc[0].mRange.Reset(); + + changed = true; + } + else if (lins->mDst.mTemp >= 0) + tvalues[lins->mDst.mTemp] = nullptr; + } + } + } + } + } + + if (mTrueJump && mTrueJump->SingleBlockLoopPointerSplit(spareTemps)) + changed = true; + if (mFalseJump && mFalseJump->SingleBlockLoopPointerSplit(spareTemps)) + changed = true; + } + + return changed; +} + +bool InterCodeBasicBlock::Flatten2DLoop(void) +{ + return false; + + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + if (mLoopHead && mNumEntries == 2 && !mFalseJump && mTrueJump && mTrueJump->mNumEntries == 1 && !mTrueJump->mFalseJump && mTrueJump->mInstructions.Size() == 1) + { + InterCodeBasicBlock* lblock = mTrueJump->mTrueJump; + + if (lblock->mLoopHead && lblock->mNumEntries == 2 && lblock->mTrueJump == lblock) + { + InterCodeBasicBlock* eblock = lblock->mFalseJump; + + if (eblock && eblock->mNumEntries == 1 && eblock->mTrueJump == this) + { + int esz = eblock->mInstructions.Size(); + if (esz >= 3 && + eblock->mInstructions[esz - 1]->mCode == IC_BRANCH && + eblock->mInstructions[esz - 2]->mCode == IC_RELATIONAL_OPERATOR && eblock->mInstructions[esz - 2]->mDst.mTemp == eblock->mInstructions[esz - 1]->mSrc[0].mTemp && + (eblock->mInstructions[esz - 2]->mOperator == IA_CMPLU || eblock->mInstructions[esz - 2]->mOperator == IA_CMPNE) && + eblock->mInstructions[esz - 2]->mSrc[0].mTemp < 0 && eblock->mInstructions[esz - 2]->mSrc[1].IsUnsigned() && eblock->mInstructions[esz - 2]->mSrc[1].mRange.mMaxValue <= eblock->mInstructions[esz - 2]->mSrc[0].mIntConst && + eblock->mInstructions[esz - 3]->mCode == IC_BINARY_OPERATOR && eblock->mInstructions[esz - 3]->mDst.mTemp == eblock->mInstructions[esz - 2]->mSrc[1].mTemp && + eblock->mInstructions[esz - 3]->mDst.mTemp == eblock->mInstructions[esz - 3]->mSrc[1].mTemp && + eblock->mInstructions[esz - 3]->mSrc[0].mTemp < 0 && eblock->mInstructions[esz - 3]->mSrc[0].mIntConst == 1) + { + int otemp = eblock->mInstructions[esz - 3]->mDst.mTemp; + int sz = mInstructions.Size(); + if (sz == 3 && + mInstructions[0]->mCode == IC_BINARY_OPERATOR && (mInstructions[0]->mOperator == IA_SHL || mInstructions[0]->mOperator == IA_MUL) && + mInstructions[0]->mSrc[0].mTemp < 0 && mInstructions[0]->mSrc[1].mTemp == otemp) + { + int64 scale = mInstructions[0]->mSrc[0].mIntConst; + if (mInstructions[0]->mOperator == IA_SHL) + scale = 1LL << scale; + + if (mInstructions[1]->mCode == IC_CONSTANT && mInstructions[1]->mConst.mIntConst == 0) + { + int ilz = lblock->mInstructions.Size(); + if (ilz >= 3 && + lblock->mInstructions[ilz - 1]->mCode == IC_BRANCH && + lblock->mInstructions[ilz - 2]->mCode == IC_RELATIONAL_OPERATOR && lblock->mInstructions[ilz - 2]->mDst.mTemp == lblock->mInstructions[ilz - 1]->mSrc[0].mTemp && + (lblock->mInstructions[ilz - 2]->mOperator == IA_CMPLU || lblock->mInstructions[ilz - 2]->mOperator == IA_CMPNE) && + lblock->mInstructions[ilz - 2]->mSrc[0].mTemp < 0 && lblock->mInstructions[ilz - 2]->mSrc[1].IsUnsigned() && lblock->mInstructions[ilz - 2]->mSrc[1].mRange.mMaxValue <= lblock->mInstructions[ilz - 2]->mSrc[0].mIntConst && + lblock->mInstructions[ilz - 3]->mCode == IC_BINARY_OPERATOR && lblock->mInstructions[ilz - 3]->mDst.mTemp == lblock->mInstructions[ilz - 2]->mSrc[1].mTemp && + lblock->mInstructions[ilz - 3]->mDst.mTemp == lblock->mInstructions[ilz - 3]->mSrc[1].mTemp && + lblock->mInstructions[ilz - 3]->mSrc[0].mTemp < 0 && lblock->mInstructions[ilz - 3]->mSrc[0].mIntConst == 1) + { + int itemp = lblock->mInstructions[ilz - 3]->mDst.mTemp; + int64 icount = lblock->mInstructions[ilz - 2]->mSrc[0].mIntConst; + + if (itemp == mInstructions[1]->mDst.mTemp && icount == scale && + !eblock->mEntryRequiredTemps[itemp] && + !eblock->mFalseJump->mEntryRequiredTemps[otemp]) + { + if (lblock->mInstructions[0]->mCode == IC_BINARY_OPERATOR && lblock->mInstructions[0]->mOperator == IA_ADD && + ((lblock->mInstructions[0]->mSrc[0].mTemp == itemp && lblock->mInstructions[0]->mSrc[1].mTemp == mInstructions[0]->mDst.mTemp) || + (lblock->mInstructions[0]->mSrc[1].mTemp == itemp && lblock->mInstructions[0]->mSrc[0].mTemp == mInstructions[0]->mDst.mTemp)) && + !IsTempReferencedInRange(2, sz, itemp) && + !IsTempReferencedInRange(2, sz, otemp) && + !lblock->IsTempReferencedInRange(1, ilz - 3, itemp) && + !lblock->IsTempReferencedInRange(1, ilz - 3, otemp) && + !eblock->IsTempReferencedInRange(0, esz - 3, otemp)) + { + // Extend loop range + int64 xcount = eblock->mInstructions[esz - 2]->mSrc->mIntConst * scale; + + eblock->mInstructions[esz - 2]->mSrc[0].mIntConst = xcount; + eblock->mInstructions[esz - 2]->mSrc[1].mRange.mMaxValue = xcount; + eblock->mInstructions[esz - 3]->mSrc[1].mRange.mMaxValue = xcount - 1; + eblock->mInstructions[esz - 3]->mDst.mRange.mMaxValue = xcount; + + lblock->mInstructions[ilz - 1]->mCode = IC_JUMP; lblock->mInstructions[ilz - 1]->mNumOperands = 0; + lblock->mTrueJump = eblock; + lblock->mFalseJump = nullptr; + lblock->mLoopHead = false; + lblock->mNumEntries = 1; + + lblock->mInstructions[0]->mCode = IC_LOAD_TEMPORARY; + lblock->mInstructions[0]->mNumOperands = 1; + lblock->mInstructions[0]->mSrc[0] = eblock->mInstructions[esz - 3]->mSrc[1]; + + mEntryValueRange[otemp].mMaxValue = xcount - 1; + lblock->mEntryValueRange[otemp].mMaxValue = xcount - 1; + eblock->mEntryValueRange[otemp].mMaxValue = xcount - 1; + + changed = true; + printf("oopsie"); + } + } + } + } + } + } + } + } + } + + if (mTrueJump && mTrueJump->Flatten2DLoop()) + changed = true; + if (mFalseJump && mFalseJump->Flatten2DLoop()) + changed = true; + } + + return changed; +} + +bool InterCodeBasicBlock::PostDecLoopOptimization(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + if (mLoopHead && mNumEntries == 2 && mFalseJump && (mTrueJump == this || mFalseJump == this) && mInstructions.Size() > 3) + { + int nins = mInstructions.Size(); + + InterCodeBasicBlock* pblock = mEntryBlocks[0], * eblock = mFalseJump; + if (pblock == this) + pblock = mEntryBlocks[1]; + if (eblock == this) + eblock = mTrueJump; + + InterInstruction* movins = nullptr, * decins = nullptr; + int ctemp = -1; + + if (mInstructions[nins - 1]->mCode == IC_BRANCH && !pblock->mFalseJump) + { + ctemp = mInstructions[nins - 1]->mSrc->mTemp; + if (mInstructions[nins - 2]->mCode == IC_RELATIONAL_OPERATOR && mInstructions[nins - 2]->mDst.mTemp == ctemp) + { + if (mInstructions[nins - 2]->mSrc[0].mTemp < 0) + ctemp = mInstructions[nins - 2]->mSrc[1].mTemp; + else + ctemp = mInstructions[nins - 2]->mSrc[0].mTemp; + } + + if (ctemp >= 0) + { + int movi = nins - 1; + while (movi >= 0 && mInstructions[movi]->mDst.mTemp != ctemp) + movi--; + + if (movi >= 0) + { + int ltemp = mInstructions[movi]->mSrc->mTemp; + int inci = movi; + while (inci < nins && mInstructions[inci]->mDst.mTemp != ltemp) + inci++; + if (inci < nins) + { + if (mInstructions[inci]->mCode == IC_BINARY_OPERATOR && mInstructions[inci]->mOperator == IA_ADD) + { + int inco = -1; + if (mInstructions[inci]->mSrc[0].mTemp == ltemp && mInstructions[inci]->mSrc[1].mTemp < 0) + inco = 1; + else if (mInstructions[inci]->mSrc[1].mTemp == ltemp && mInstructions[inci]->mSrc[0].mTemp < 0) + inco = 0; + + if (inco >= 0 && !IsTempReferencedInRange(0, movi, ltemp) && !IsTempReferencedInRange(movi + 1, inci, ltemp) && !IsTempReferencedInRange(inci + 1, nins, ltemp)) + { + if (!eblock->mEntryRequiredTemps[ltemp]) + { + InterInstruction* ins = mInstructions[inci]; + + int64 v = ins->mSrc[inco].mIntConst; + + ins->mDst.mRange.AddConstValue(ins->mDst.mType, -v); + ins->mSrc[1 - inco].mRange.AddConstValue(ins->mDst.mType, -v); + mEntryValueRange[ltemp].AddConstValue(ins->mDst.mType, -v); + + mInstructions.Remove(inci); + mInstructions.Insert(movi, ins); + InterInstruction* pins = ins->Clone(); + pins->mSrc[inco].mIntConst = -v; + + pins->mDst.mRange.AddConstValue(ins->mDst.mType, -v); + ins->mSrc[1 - inco].mRange.AddConstValue(ins->mDst.mType, -v); + + pblock->mInstructions.Insert(pblock->mInstructions.Size() - 1, pins); + changed = true; + } + } + } + } + } + } + } + } + + if (mTrueJump && mTrueJump->PostDecLoopOptimization()) + changed = true; + if (mFalseJump && mFalseJump->PostDecLoopOptimization()) + changed = true; + } + + return changed; +} + +void InterCodeBasicBlock::SingleBlockLoopOptimisation(const NumberSet& aliasedParams, const GrowingVariableArray& staticVars) +{ + if (!mVisited) + { + mVisited = true; + + if (mLoopHead && mNumEntries == 2 && mFalseJump && (mTrueJump == this || mFalseJump == this)) + { + bool hasCall = false; + for (int i = 0; i < mInstructions.Size(); i++) + { + InterInstruction* ins = mInstructions[i]; + if ((ins->mCode == IC_CALL || ins->mCode == IC_CALL_NATIVE) && !ins->mConstExpr) + hasCall = true; + } + +#if 1 + InterCodeBasicBlock* tailBlock = this == mTrueJump ? mFalseJump : mTrueJump; + assert(tailBlock->mNumEntries == 1); + + // remove counting loop without body + if (mInstructions.Size() == 3) + { + InterInstruction* ains = mInstructions[0]; + InterInstruction* cins = mInstructions[1]; + InterInstruction* bins = mInstructions[2]; + + if (ains->mCode == IC_BINARY_OPERATOR && cins->mCode == IC_RELATIONAL_OPERATOR && bins->mCode == IC_BRANCH) + { + if (ains->mSrc[1].mTemp == ains->mDst.mTemp && ains->mSrc[0].mTemp < 0 && + cins->mSrc[1].mTemp == ains->mDst.mTemp && ains->mSrc[0].mTemp < 0 && + bins->mSrc[0].mTemp == cins->mDst.mTemp && bins->mSrc[0].mFinal) + { + if (ains->mOperator == IA_ADD && ains->mSrc[0].mIntConst == 1 && + cins->mOperator == IA_CMPLU && mTrueJump == this && !tailBlock->mEntryRequiredTemps[ains->mDst.mTemp]) + { + cins->mCode = IC_CONSTANT; + cins->mConst.mType = IT_BOOL; + cins->mConst.mIntConst = 0; + cins->mNumOperands = 0; + } + } + } + else if (ains->mCode == IC_LEA && cins->mCode == IC_RELATIONAL_OPERATOR && bins->mCode == IC_BRANCH) + { + if (ains->mSrc[1].mTemp == ains->mDst.mTemp && ains->mSrc[0].mTemp < 0 && + (cins->mSrc[1].mTemp == ains->mDst.mTemp || cins->mSrc[0].mTemp == ains->mDst.mTemp) && + bins->mSrc[0].mTemp == cins->mDst.mTemp && bins->mSrc[0].mFinal) + { + if (cins->mOperator == IA_CMPNE && mTrueJump == this && !tailBlock->mEntryRequiredTemps[ains->mDst.mTemp]) + { + cins->mCode = IC_CONSTANT; + cins->mConst.mType = IT_BOOL; + cins->mConst.mIntConst = 0; + cins->mNumOperands = 0; + } + } + } + } +#endif +#if 1 + if (!hasCall && (mProc->mCompilerOptions & COPT_OPTIMIZE_BASIC)) + { + // Check forwarding globals + + int i = 0; + for (int i = 0; i < mInstructions.Size(); i++) + { + InterInstruction* ins = mInstructions[i]; + + // A global load + if (ins->mCode == IC_LOAD && !ins->mVolatile && ins->mSrc[0].mTemp < 0 && (ins->mSrc[0].mMemory == IM_GLOBAL || ins->mSrc[0].mMemory == IM_FPARAM)) + { + // Find the last store that overlaps the load + int j = mInstructions.Size() - 1; + while (j > i && !(mInstructions[j]->mCode == IC_STORE && CollidingMem(ins, mInstructions[j]))) + j--; + + if (j > i) + { + InterInstruction* sins = mInstructions[j]; + + // Does a full store + if (SameMem(ins->mSrc[0], sins->mSrc[1]) && !AliasingMem(this, ins, 0, mInstructions.Size()) && !DestroyingMem(this, ins, 0, j)) + { + if (sins->mSrc[0].mTemp >= 0) + { + // Check temp not used before load + int k = 0; + while (k < i && !mInstructions[k]->UsesTemp(sins->mSrc[0].mTemp)) + k++; + if (k == i) + { + // Check temp not modified after load + k = j + 1; + while (k < mInstructions.Size() && mInstructions[k]->mDst.mTemp != sins->mSrc[0].mTemp) + k++; + if (k == mInstructions.Size()) + { + assert(!mEntryRequiredTemps[sins->mSrc[0].mTemp]); + + // Move load before loop + mLoopPrefix->mInstructions.Insert(mLoopPrefix->mInstructions.Size() - 1, ins); + InterInstruction* nins = new InterInstruction(sins->mLocation, IC_LOAD_TEMPORARY); + mInstructions[i] = nins; + nins->mDst.Forward(ins->mDst); + nins->mSrc[0].Forward(sins->mSrc[0]); + ins->mDst.Forward(sins->mSrc[0]); + sins->mSrc[0].mFinal = false; + assert(nins->mSrc[0].mTemp >= 0); + + // Propagate all loads to move temps + + for (int t = i + 1; t < mInstructions.Size(); t++) + { + InterInstruction* ti = mInstructions[t]; + if (ti->mCode == IC_LOAD && SameMem(ti->mSrc[0], ins->mSrc[0])) + { + ti->mCode = IC_LOAD_TEMPORARY; + ti->mSrc[0] = ins->mDst; + } + } + + // Move store behind loop + if (sins->mSrc[0].mTemp >= 0) + tailBlock->mEntryRequiredTemps += sins->mSrc[0].mTemp; + tailBlock->mInstructions.Insert(0, sins); + mInstructions.Remove(j); + } + } + } + } + } + } + else if (ins->mCode == IC_STORE && !ins->mVolatile && ins->mSrc[0].mTemp < 0 && ins->mSrc[1].mTemp < 0 && (ins->mSrc[1].mMemory == IM_GLOBAL || ins->mSrc[1].mMemory == IM_FPARAM)) + { + int j = 0; + while (j < mInstructions.Size() && (j == i || !CollidingMem(ins, mInstructions[j]))) + j++; + + if (j == mInstructions.Size()) + { + tailBlock->mInstructions.Insert(0, ins); + mInstructions.Remove(i); + i--; + } + } + } + } +#endif + + GrowingArray tvalues(nullptr); + GrowingArray nassigns(0); + + int frameLevel = 0; + for (int i = 0; i < mInstructions.Size(); i++) + { + InterInstruction* ins = mInstructions[i]; + ins->mInvariant = true; + + if (ins->mCode == IC_PUSH_FRAME) + frameLevel++; + else if (ins->mCode == IC_POP_FRAME) + frameLevel--; + + if (!IsMoveable(ins->mCode)) + ins->mInvariant = false; + else if (ins->mCode == IC_CONSTANT && ins->mDst.mType == IT_POINTER && ins->mConst.mMemory == IM_FRAME && frameLevel != 0) + ins->mInvariant = false; + else if (ins->mCode == IC_LEA && ins->mSrc[1].mMemory == IM_FRAME && frameLevel != 0) + ins->mInvariant = false; + else if (ins->mCode == IC_LOAD) + { + if ((ins->mSrc[0].mTemp >= 0 && mLocalModifiedTemps[ins->mSrc[0].mTemp]) || ins->mVolatile || hasCall) + { + ins->mInvariant = false; + } + else if (ins->mSrc[0].mMemory == IM_GLOBAL && hasCall) + { + ins->mInvariant = false; + } + else if (ins->mSrc[0].mMemory == IM_LOCAL && hasCall) + { + ins->mInvariant = false; + } + else + { + for (int j = 0; j < mInstructions.Size(); j++) + { + InterInstruction* sins = mInstructions[j]; + if (sins->mCode == IC_STORE && CollidingMem(ins, sins)) + { + if (sins->mSrc[1].mTemp >= 0 && ins->mSrc[0].mTemp < 0) + { + if ((ins->mSrc[0].mMemory != IM_PARAM && ins->mSrc[0].mMemory != IM_FPARAM) || aliasedParams[ins->mSrc[0].mVarIndex]) + { + int k = j - 1; + while (k >= 0 && mInstructions[k]->mDst.mTemp != sins->mSrc[1].mTemp) + k--; + if (k >= 0) + { + InterInstruction* lins = mInstructions[k]; + if (lins->mCode == IC_LEA && lins->mSrc[1].mTemp < 0) + { + if (ins->mSrc[0].mMemory == lins->mSrc[1].mMemory && ins->mSrc[0].mVarIndex == lins->mSrc[1].mVarIndex && ins->mSrc[0].mLinkerObject == lins->mSrc[1].mLinkerObject) + ins->mInvariant = false; + } + else + ins->mInvariant = false; + } + else + ins->mInvariant = false; + } + } + else + { + ins->mInvariant = false; + } + } + else if ((sins->mCode == IC_COPY || sins->mCode == IC_FILL) && DestroyingMem(ins, sins)) + { + ins->mInvariant = false; + } + } + } + } + } + + enum Dependency + { + DEP_UNKNOWN, + DEP_DEFINED, + + DEP_INDEX, + DEP_INDEX_DERIVED, + DEP_INDEX_EXTENDED, + + DEP_ITERATED, + DEP_VARIABLE + }; + + GrowingArray dep(DEP_UNKNOWN); + GrowingArray indexStep(0), indexBase(0); + tvalues.SetSize(0); + + for (int i = 0; i < mInstructions.Size(); i++) + { + InterInstruction* ins = mInstructions[i]; + int t = ins->mDst.mTemp; + if (t >= 0) + { + if (HasSideEffect(ins->mCode) || !ins->mInvariant) + dep[t] = DEP_VARIABLE; + else if (dep[t] == DEP_UNKNOWN) + { + if (ins->mCode == IC_BINARY_OPERATOR && ins->mOperator == IA_ADD && IsIntegerType(ins->mDst.mType) && ins->mSrc[0].mTemp < 0 && ins->mSrc[1].mTemp == t)// && ins->mSrc[0].mIntConst > 0) + { + indexStep[t] = ins->mSrc[0].mIntConst; + indexBase[t] = 0; + dep[t] = DEP_INDEX; + ins->mInvariant = false; + } + else + dep[t] = DEP_DEFINED; + } + else if (dep[t] == DEP_DEFINED || dep[t] == DEP_INDEX) + { + dep[t] = DEP_VARIABLE; + ins->mInvariant = false; + } + } + } + + for (int i = 0; i < dep.Size(); i++) + { + if (dep[i] == DEP_DEFINED) + dep[i] = DEP_ITERATED; + } + + indexStep.SetSize(dep.Size()); + indexBase.SetSize(dep.Size()); + + bool changed; + do + { + changed = false; + + for (int i = 0; i < mInstructions.Size(); i++) + { + if (i + 1 < mInstructions.Size()) + { + InterInstruction* ins0 = mInstructions[i + 0]; + InterInstruction* ins1 = mInstructions[i + 1]; + if (ins0->mCode == IC_BINARY_OPERATOR && ins0->mOperator == IA_ADD && ins1->mCode == IC_BINARY_OPERATOR && ins1->mOperator == IA_ADD) + { + if (ins0->mDst.mTemp == ins1->mSrc[1].mTemp && IsIntegerType(ins1->mDst.mType) && ins1->mSrc[1].mFinal) + { + if (ins0->mSrc[0].mTemp >= 0 && ins0->mSrc[1].mTemp >= 0 && ins1->mSrc[0].mTemp >= 0) + { + if ((dep[ins0->mSrc[1].mTemp] == DEP_INDEX || dep[ins0->mSrc[1].mTemp] == DEP_INDEX_EXTENDED) && + (dep[ins0->mSrc[0].mTemp] == DEP_UNKNOWN || dep[ins0->mSrc[0].mTemp] == DEP_VARIABLE) && + (dep[ins1->mSrc[0].mTemp] == DEP_UNKNOWN || dep[ins1->mSrc[0].mTemp] == DEP_VARIABLE)) + { + InterOperand op = ins0->mSrc[1]; + ins0->mSrc[1] = ins1->mSrc[0]; + ins1->mSrc[0] = op; + if (dep[ins0->mSrc[0].mTemp] == DEP_UNKNOWN && dep[ins0->mSrc[1].mTemp] == DEP_UNKNOWN) + ins0->mInvariant = true; + dep[ins0->mDst.mTemp] = DEP_UNKNOWN; + dep[ins1->mDst.mTemp] = DEP_UNKNOWN; + changed = true; + } + } + } + } + + if (ins0->mCode == IC_BINARY_OPERATOR && ins0->mOperator == IA_ADD && ins1->mCode == IC_LEA && ins1->mSrc[1].mTemp >= 0) + { + if (ins1->mSrc[0].mTemp == ins0->mDst.mTemp && ins1->mSrc[0].mFinal && ins0->mSrc[0].mTemp >= 0 && ins0->mSrc[1].mTemp >= 0) + { + if (dep[ins1->mSrc[1].mTemp] == DEP_UNKNOWN && dep[ins0->mSrc[0].mTemp] == DEP_UNKNOWN && dep[ins0->mSrc[1].mTemp] == DEP_INDEX_EXTENDED) + { + InterOperand iop = ins0->mSrc[1]; + InterOperand pop = ins1->mSrc[1]; + + ins0->mCode = IC_LEA; + ins0->mDst.mType = IT_POINTER; + ins0->mDst.mMemory = ins1->mSrc[1].mMemory; + + ins0->mSrc[1] = pop; + + ins1->mSrc[1].mTemp = ins0->mDst.mTemp; + ins1->mSrc[0] = iop; + + if (dep[ins0->mSrc[0].mTemp] == DEP_UNKNOWN && dep[ins0->mSrc[1].mTemp] == DEP_UNKNOWN) + ins0->mInvariant = true; + dep[ins0->mDst.mTemp] = DEP_UNKNOWN; + dep[ins1->mDst.mTemp] = DEP_UNKNOWN; + } + } + } + + } + + InterInstruction* ins = mInstructions[i]; + int t = ins->mDst.mTemp; + if (t >= 0) + { + if (dep[t] < DEP_VARIABLE && dep[t] != DEP_INDEX) + { +#if 0 + if (tailBlock->mEntryRequiredTemps[ins->mDst.mTemp]) + { + dep[t] = DEP_VARIABLE; + ins->mInvariant = false; + changed = true; + } + else +#endif + { + int j = 0; + while (j < ins->mNumOperands && !(ins->mSrc[j].mTemp >= 0 && dep[ins->mSrc[j].mTemp] >= DEP_INDEX)) + j++; + if (j < ins->mNumOperands) + { + if (ins->mCode == IC_BINARY_OPERATOR && ins->mOperator == IA_MUL && IsIntegerType(ins->mDst.mType) && ins->mSrc[1].mTemp < 0 && (dep[ins->mSrc[0].mTemp] == DEP_INDEX || dep[ins->mSrc[0].mTemp] == DEP_INDEX_EXTENDED || dep[ins->mSrc[0].mTemp] == DEP_INDEX_DERIVED) || + ins->mCode == IC_BINARY_OPERATOR && ins->mOperator == IA_MUL && IsIntegerType(ins->mDst.mType) && ins->mSrc[0].mTemp < 0 && (dep[ins->mSrc[1].mTemp] == DEP_INDEX || dep[ins->mSrc[1].mTemp] == DEP_INDEX_EXTENDED || dep[ins->mSrc[1].mTemp] == DEP_INDEX_DERIVED) || + ins->mCode == IC_BINARY_OPERATOR && ins->mOperator == IA_SHL && IsIntegerType(ins->mDst.mType) && ins->mSrc[0].mTemp < 0 && (dep[ins->mSrc[1].mTemp] == DEP_INDEX || dep[ins->mSrc[1].mTemp] == DEP_INDEX_EXTENDED || dep[ins->mSrc[1].mTemp] == DEP_INDEX_DERIVED) || + ins->mCode == IC_BINARY_OPERATOR && ins->mOperator == IA_ADD && IsIntegerType(ins->mDst.mType) && (ins->mSrc[0].mTemp < 0 || dep[ins->mSrc[0].mTemp] == DEP_UNKNOWN || dep[ins->mSrc[0].mTemp] == DEP_DEFINED) && dep[ins->mSrc[1].mTemp] == DEP_INDEX_DERIVED || + ins->mCode == IC_BINARY_OPERATOR && ins->mOperator == IA_ADD && IsIntegerType(ins->mDst.mType) && (ins->mSrc[1].mTemp < 0 || dep[ins->mSrc[1].mTemp] == DEP_UNKNOWN || dep[ins->mSrc[1].mTemp] == DEP_DEFINED) && dep[ins->mSrc[0].mTemp] == DEP_INDEX_DERIVED || + ins->mCode == IC_BINARY_OPERATOR && ins->mOperator == IA_SUB && IsIntegerType(ins->mDst.mType) && (ins->mSrc[0].mTemp < 0 || dep[ins->mSrc[0].mTemp] == DEP_UNKNOWN || dep[ins->mSrc[0].mTemp] == DEP_DEFINED) && dep[ins->mSrc[1].mTemp] == DEP_INDEX_DERIVED || + ins->mCode == IC_BINARY_OPERATOR && ins->mOperator == IA_SUB && IsIntegerType(ins->mDst.mType) && (ins->mSrc[1].mTemp < 0 || dep[ins->mSrc[1].mTemp] == DEP_UNKNOWN || dep[ins->mSrc[1].mTemp] == DEP_DEFINED) && (dep[ins->mSrc[0].mTemp] == DEP_INDEX_DERIVED || dep[ins->mSrc[0].mTemp] == DEP_INDEX) || + ins->mCode == IC_BINARY_OPERATOR && ins->mOperator == IA_ADD && + IsIntegerType(ins->mDst.mType) && + (ins->mSrc[0].mTemp >= 0 && ins->mSrc[0].IsNotUByte() && (dep[ins->mSrc[0].mTemp] == DEP_UNKNOWN || dep[ins->mSrc[0].mTemp] == DEP_DEFINED)) && + (dep[ins->mSrc[1].mTemp] == DEP_INDEX || dep[ins->mSrc[1].mTemp] == DEP_INDEX_EXTENDED || dep[ins->mSrc[1].mTemp] == DEP_INDEX_DERIVED) || + ins->mCode == IC_BINARY_OPERATOR && ins->mOperator == IA_ADD && + IsIntegerType(ins->mDst.mType) && + (ins->mSrc[1].mTemp >= 0 && ins->mSrc[1].IsNotUByte() && (dep[ins->mSrc[1].mTemp] == DEP_UNKNOWN || dep[ins->mSrc[1].mTemp] == DEP_DEFINED)) && + (dep[ins->mSrc[0].mTemp] == DEP_INDEX || dep[ins->mSrc[0].mTemp] == DEP_INDEX_EXTENDED || dep[ins->mSrc[0].mTemp] == DEP_INDEX_DERIVED) || + ins->mCode == IC_LEA && (ins->mSrc[1].mTemp < 0 || dep[ins->mSrc[1].mTemp] == DEP_UNKNOWN || dep[ins->mSrc[1].mTemp] == DEP_DEFINED) && dep[ins->mSrc[0].mTemp] == DEP_INDEX_DERIVED) + { + if (dep[ins->mDst.mTemp] != DEP_INDEX_DERIVED) + { + dep[ins->mDst.mTemp] = DEP_INDEX_DERIVED; + ins->mInvariant = false; + changed = true; + } + } + else if (ins->mCode == IC_CONVERSION_OPERATOR && (ins->mOperator == IA_EXT8TO16S || ins->mOperator == IA_EXT8TO16U) && dep[ins->mSrc[0].mTemp] == DEP_INDEX) + { + if (dep[ins->mDst.mTemp] != DEP_INDEX_EXTENDED) + { + dep[ins->mDst.mTemp] = DEP_INDEX_EXTENDED; + ins->mInvariant = false; + changed = true; + } + } + else + { + dep[t] = DEP_VARIABLE; + ins->mInvariant = false; + changed = true; + } + } + else + { + dep[t] = DEP_DEFINED; + } + } + } + } + } + + } while (changed); + + GrowingArray indexins(nullptr); + GrowingArray pindexins(nullptr); + + + InterInstruction* cins = nullptr; + if (mInstructions.Size() > 2 && mInstructions[mInstructions.Size() - 2]->mCode == IC_RELATIONAL_OPERATOR) + cins = mInstructions[mInstructions.Size() - 2]; + + int j = 0; + for (int i = 0; i < mInstructions.Size(); i++) + { + InterInstruction* ins = mInstructions[i]; + + if (ins->mInvariant) + { + if (ins->mDst.mTemp >= 0 && IsTempReferencedInRange(0, i, ins->mDst.mTemp)) + ins->mInvariant = false; + else + { + for (int j = 0; j < ins->mNumOperands; j++) + if (ins->mSrc[j].mTemp >= 0 && dep[ins->mSrc[j].mTemp] == DEP_VARIABLE) + ins->mInvariant = false; + } + } + else + { + if (ins->mCode == IC_BINARY_OPERATOR && (ins->mOperator == IA_AND || ins->mOperator == IA_OR) && !IsTempReferencedInRange(0, i, ins->mDst.mTemp)) + { + if (ins->mDst.mTemp == ins->mSrc[0].mTemp && ins->mSrc[1].mTemp < 0) + { + if (!IsTempModifiedInRange(i + 1, mInstructions.Size(), ins->mDst.mTemp)) + ins->mInvariant = true; + } + else if (ins->mDst.mTemp == ins->mSrc[1].mTemp && ins->mSrc[0].mTemp < 0) + { + if (!IsTempModifiedInRange(i + 1, mInstructions.Size(), ins->mDst.mTemp)) + ins->mInvariant = true; + } + } + } + + if (ins->mInvariant) + { + mLoopPrefix->mInstructions.Insert(mLoopPrefix->mInstructions.Size() - 1, ins); + } + else if (ins->mDst.mTemp >= 0 && dep[ins->mDst.mTemp] == DEP_INDEX) + { + indexBase[ins->mDst.mTemp] = indexStep[ins->mDst.mTemp]; + mInstructions[j++] = ins; + } + else if (ins->mDst.mTemp >= 0 && dep[ins->mDst.mTemp] == DEP_INDEX_DERIVED) + { + if (ins->mCode == IC_BINARY_OPERATOR && ins->mOperator == IA_MUL && IsIntegerType(ins->mDst.mType) && ins->mSrc[1].mTemp < 0 && (dep[ins->mSrc[0].mTemp] == DEP_INDEX || dep[ins->mSrc[0].mTemp] == DEP_INDEX_EXTENDED || dep[ins->mSrc[0].mTemp] == DEP_INDEX_DERIVED)) + { + indexStep[ins->mDst.mTemp] = ins->mSrc[1].mIntConst * indexStep[ins->mSrc[0].mTemp]; + indexBase[ins->mDst.mTemp] = ins->mSrc[1].mIntConst * indexBase[ins->mSrc[0].mTemp]; + + InterInstruction* bins = new InterInstruction(ins->mLocation, IC_BINARY_OPERATOR); + bins->mOperator = IA_MUL; + bins->mDst = ins->mDst; + bins->mSrc[0] = ins->mSrc[0]; + bins->mSrc[1] = ins->mSrc[1]; + mLoopPrefix->mInstructions.Insert(mLoopPrefix->mInstructions.Size() - 1, bins); + + InterInstruction* ains = new InterInstruction(ins->mLocation, IC_BINARY_OPERATOR); + ains->mOperator = IA_ADD; + ains->mDst = ins->mDst; + ains->mSrc[0] = ins->mDst; + ains->mSrc[1] = ins->mSrc[1]; ains->mSrc[1].mIntConst = ins->mSrc[1].mIntConst * indexBase[ins->mSrc[0].mTemp]; + mLoopPrefix->mInstructions.Insert(mLoopPrefix->mInstructions.Size() - 1, ains); + + ins->mOperator = IA_ADD; + ins->mSrc[1].mIntConst = ins->mSrc[1].mIntConst * indexStep[ins->mSrc[0].mTemp]; + ins->mSrc[0] = ins->mDst; + + if (tailBlock->mEntryRequiredTemps[ins->mDst.mTemp] && !(cins && (cins->mSrc[0].mTemp == ins->mDst.mTemp || cins->mSrc[1].mTemp == ins->mDst.mTemp))) + { + InterInstruction* rins = new InterInstruction(ins->mLocation, IC_BINARY_OPERATOR); + rins->mOperator = IA_SUB; + rins->mDst = ins->mDst; + rins->mSrc[1] = ins->mDst; + rins->mSrc[0] = ins->mSrc[1]; + tailBlock->mInstructions.Insert(0, rins); + } + + indexins.Push(ins); + } + else if (ins->mCode == IC_BINARY_OPERATOR && ins->mOperator == IA_MUL && IsIntegerType(ins->mDst.mType) && ins->mSrc[0].mTemp < 0 && (dep[ins->mSrc[1].mTemp] == DEP_INDEX || dep[ins->mSrc[1].mTemp] == DEP_INDEX_EXTENDED || dep[ins->mSrc[1].mTemp] == DEP_INDEX_DERIVED)) + { + indexStep[ins->mDst.mTemp] = ins->mSrc[0].mIntConst * indexStep[ins->mSrc[1].mTemp]; + indexBase[ins->mDst.mTemp] = ins->mSrc[0].mIntConst * indexBase[ins->mSrc[1].mTemp]; + + InterInstruction* bins = new InterInstruction(ins->mLocation, IC_BINARY_OPERATOR); + bins->mOperator = IA_MUL; + bins->mDst = ins->mDst; + bins->mSrc[0] = ins->mSrc[0]; + bins->mSrc[1] = ins->mSrc[1]; + mLoopPrefix->mInstructions.Insert(mLoopPrefix->mInstructions.Size() - 1, bins); + + InterInstruction* ains = new InterInstruction(ins->mLocation, IC_BINARY_OPERATOR); + ains->mOperator = IA_ADD; + ains->mDst = ins->mDst; + ains->mSrc[1] = ins->mDst; + ains->mSrc[0] = ins->mSrc[0]; ains->mSrc[0].mIntConst = ins->mSrc[0].mIntConst * indexBase[ins->mSrc[1].mTemp]; + mLoopPrefix->mInstructions.Insert(mLoopPrefix->mInstructions.Size() - 1, ains); + + ins->mOperator = IA_ADD; + ins->mSrc[0].mIntConst = ins->mSrc[0].mIntConst * indexStep[ins->mSrc[1].mTemp]; + ins->mSrc[1] = ins->mDst; + + if (tailBlock->mEntryRequiredTemps[ins->mDst.mTemp] && !(cins && (cins->mSrc[0].mTemp == ins->mDst.mTemp || cins->mSrc[1].mTemp == ins->mDst.mTemp))) + { + InterInstruction* rins = new InterInstruction(ins->mLocation, IC_BINARY_OPERATOR); + rins->mOperator = IA_SUB; + rins->mDst = ins->mDst; + rins->mSrc[1] = ins->mDst; + rins->mSrc[0] = ins->mSrc[0]; + tailBlock->mInstructions.Insert(0, rins); + } + + indexins.Push(ins); + } + else if (ins->mCode == IC_BINARY_OPERATOR && ins->mOperator == IA_SHL && IsIntegerType(ins->mDst.mType) && ins->mSrc[0].mTemp < 0 && (dep[ins->mSrc[1].mTemp] == DEP_INDEX || dep[ins->mSrc[1].mTemp] == DEP_INDEX_EXTENDED || dep[ins->mSrc[1].mTemp] == DEP_INDEX_DERIVED)) + { + indexStep[ins->mDst.mTemp] = indexStep[ins->mSrc[1].mTemp] << ins->mSrc[0].mIntConst; + indexBase[ins->mDst.mTemp] = indexBase[ins->mSrc[1].mTemp] << ins->mSrc[0].mIntConst; + + InterInstruction* bins = new InterInstruction(ins->mLocation, IC_BINARY_OPERATOR); + bins->mOperator = IA_SHL; + bins->mDst = ins->mDst; + bins->mSrc[0] = ins->mSrc[0]; + bins->mSrc[1] = ins->mSrc[1]; + mLoopPrefix->mInstructions.Insert(mLoopPrefix->mInstructions.Size() - 1, bins); + + InterInstruction* ains = new InterInstruction(ins->mLocation, IC_BINARY_OPERATOR); + ains->mOperator = IA_ADD; + ains->mDst = ins->mDst; + ains->mSrc[1] = ins->mDst; + ains->mSrc[0] = ins->mSrc[0]; ains->mSrc[0].mIntConst = indexBase[ins->mSrc[1].mTemp] << ins->mSrc[0].mIntConst; + mLoopPrefix->mInstructions.Insert(mLoopPrefix->mInstructions.Size() - 1, ains); + + ins->mOperator = IA_ADD; + ins->mSrc[0].mIntConst = indexStep[ins->mSrc[1].mTemp] << ins->mSrc[0].mIntConst; + ins->mSrc[1] = ins->mDst; + if (ins->mDst.mRange.mMaxState == IntegerValueRange::S_BOUND) + ins->mDst.mRange.mMaxValue += ins->mSrc[0].mIntConst; + + if (tailBlock->mEntryRequiredTemps[ins->mDst.mTemp] && !(cins && (cins->mSrc[0].mTemp == ins->mDst.mTemp || cins->mSrc[1].mTemp == ins->mDst.mTemp))) + { + InterInstruction* rins = new InterInstruction(ins->mLocation, IC_BINARY_OPERATOR); + rins->mOperator = IA_SUB; + rins->mDst = ins->mDst; + rins->mSrc[1] = ins->mDst; + rins->mSrc[0] = ins->mSrc[0]; + tailBlock->mInstructions.Insert(0, rins); + } + + indexins.Push(ins); + } + else if (ins->mCode == IC_BINARY_OPERATOR && (ins->mOperator == IA_ADD || ins->mOperator == IA_SUB) && IsIntegerType(ins->mDst.mType) && (ins->mSrc[0].mTemp < 0 || dep[ins->mSrc[0].mTemp] == DEP_UNKNOWN || dep[ins->mSrc[0].mTemp] == DEP_DEFINED) && (dep[ins->mSrc[1].mTemp] == DEP_INDEX || dep[ins->mSrc[1].mTemp] == DEP_INDEX_EXTENDED || dep[ins->mSrc[1].mTemp] == DEP_INDEX_DERIVED)) + { + indexStep[ins->mDst.mTemp] = indexStep[ins->mSrc[1].mTemp]; + indexBase[ins->mDst.mTemp] = 0; + + mLoopPrefix->mInstructions.Insert(mLoopPrefix->mInstructions.Size() - 1, ins); + if (indexBase[ins->mSrc[1].mTemp]) + { + InterInstruction* bins = new InterInstruction(ins->mLocation, IC_BINARY_OPERATOR); + bins->mOperator = IA_ADD; + bins->mDst = ins->mDst; + bins->mSrc[0] = ins->mDst; + bins->mSrc[1].mType = ins->mDst.mType; + bins->mSrc[1].mTemp = -1; + bins->mSrc[1].mIntConst = indexBase[ins->mSrc[1].mTemp]; + mLoopPrefix->mInstructions.Insert(mLoopPrefix->mInstructions.Size() - 1, bins); + } + + InterInstruction* ains = new InterInstruction(ins->mLocation, IC_BINARY_OPERATOR); + ains->mOperator = IA_ADD; + ains->mDst = ins->mDst; + ains->mSrc[0] = ins->mDst; + ains->mSrc[1] = ins->mDst; + ains->mSrc[1].mTemp = -1; + ains->mSrc[1].mIntConst = indexStep[ins->mSrc[1].mTemp]; + if (ains->mDst.mRange.mMaxState == IntegerValueRange::S_BOUND) + ains->mDst.mRange.mMaxValue += ains->mSrc[1].mIntConst; + + indexins.Push(ains); + } + else if (ins->mCode == IC_BINARY_OPERATOR && ins->mOperator == IA_ADD && IsIntegerType(ins->mDst.mType) && (ins->mSrc[1].mTemp < 0 || dep[ins->mSrc[1].mTemp] == DEP_UNKNOWN || dep[ins->mSrc[1].mTemp] == DEP_DEFINED) && (dep[ins->mSrc[0].mTemp] == DEP_INDEX || dep[ins->mSrc[0].mTemp] == DEP_INDEX_EXTENDED || dep[ins->mSrc[0].mTemp] == DEP_INDEX_DERIVED)) + { + indexStep[ins->mDst.mTemp] = indexStep[ins->mSrc[0].mTemp]; + indexBase[ins->mDst.mTemp] = 0; + + mLoopPrefix->mInstructions.Insert(mLoopPrefix->mInstructions.Size() - 1, ins); + if (indexBase[ins->mSrc[0].mTemp]) + { + InterInstruction* bins = new InterInstruction(ins->mLocation, IC_BINARY_OPERATOR); + bins->mOperator = IA_ADD; + bins->mDst = ins->mDst; + bins->mSrc[0] = ins->mDst; + bins->mSrc[1].mType = ins->mDst.mType; + bins->mSrc[1].mTemp = -1; + bins->mSrc[1].mIntConst = indexBase[ins->mSrc[0].mTemp]; + mLoopPrefix->mInstructions.Insert(mLoopPrefix->mInstructions.Size() - 1, bins); + } + + InterInstruction* ains = new InterInstruction(ins->mLocation, IC_BINARY_OPERATOR); + ains->mOperator = IA_ADD; + ains->mDst = ins->mDst; + ains->mSrc[1] = ins->mDst; + ains->mSrc[0] = ins->mDst; + ains->mSrc[0].mTemp = -1; + ains->mSrc[0].mIntConst = indexStep[ins->mSrc[0].mTemp]; + + if (ains->mDst.mRange.mMaxState == IntegerValueRange::S_BOUND) + ains->mDst.mRange.mMaxValue += ains->mSrc[0].mIntConst; + indexins.Push(ains); + } + else if (ins->mCode == IC_BINARY_OPERATOR && ins->mOperator == IA_SUB && IsIntegerType(ins->mDst.mType) && (ins->mSrc[1].mTemp < 0 || dep[ins->mSrc[1].mTemp] == DEP_UNKNOWN || dep[ins->mSrc[1].mTemp] == DEP_DEFINED) && (dep[ins->mSrc[0].mTemp] == DEP_INDEX || dep[ins->mSrc[0].mTemp] == DEP_INDEX_EXTENDED || dep[ins->mSrc[0].mTemp] == DEP_INDEX_DERIVED)) + { + indexStep[ins->mDst.mTemp] = -indexStep[ins->mSrc[0].mTemp]; + indexBase[ins->mDst.mTemp] = 0; + + mLoopPrefix->mInstructions.Insert(mLoopPrefix->mInstructions.Size() - 1, ins); + if (indexBase[ins->mSrc[0].mTemp]) + { + InterInstruction* bins = new InterInstruction(ins->mLocation, IC_BINARY_OPERATOR); + bins->mOperator = IA_SUB; + bins->mDst = ins->mDst; + bins->mSrc[1] = ins->mDst; + bins->mSrc[0].mType = ins->mDst.mType; + bins->mSrc[0].mTemp = -1; + bins->mSrc[0].mIntConst = indexBase[ins->mSrc[0].mTemp]; + mLoopPrefix->mInstructions.Insert(mLoopPrefix->mInstructions.Size() - 1, bins); + } + + InterInstruction* ains = new InterInstruction(ins->mLocation, IC_BINARY_OPERATOR); + ains->mOperator = IA_SUB; + ains->mDst = ins->mDst; + ains->mSrc[1] = ins->mDst; + ains->mSrc[0] = ins->mDst; + ains->mSrc[0].mTemp = -1; + ains->mSrc[0].mIntConst = indexStep[ins->mSrc[0].mTemp]; + + if (ains->mDst.mRange.mMinState == IntegerValueRange::S_BOUND) + ains->mDst.mRange.mMinValue -= ains->mSrc[0].mIntConst; + indexins.Push(ains); + } + else if (ins->mCode == IC_LEA && ins->mSrc[1].mTemp < 0 && ins->mSrc[0].IsUByte() && (ins->mSrc[1].mMemory == IM_ABSOLUTE || ins->mSrc[1].mMemory == IM_GLOBAL)) + { + mInstructions[j++] = ins; + } + else if (ins->mCode == IC_LEA && (ins->mSrc[1].mTemp < 0 || dep[ins->mSrc[1].mTemp] == DEP_UNKNOWN || dep[ins->mSrc[1].mTemp] == DEP_DEFINED) && dep[ins->mSrc[0].mTemp] == DEP_INDEX_DERIVED) + { +#if 1 + int k = 0; + while (k < pindexins.Size() && !( + ins->mSrc[0].mTemp == pindexins[k]->mSrc[0].mTemp && + ins->mSrc[1].mTemp == pindexins[k]->mSrc[1].mTemp && + ins->mSrc[1].mMemory == pindexins[k]->mSrc[1].mMemory && + ins->mSrc[1].mVarIndex == pindexins[k]->mSrc[1].mVarIndex)) + k++; + + if (k < pindexins.Size() && ins->mSrc[1].mIntConst >= pindexins[k]->mSrc[1].mIntConst) + { + ins->mSrc[0].mTemp = -1; + ins->mSrc[0].mIntConst = ins->mSrc[1].mIntConst - pindexins[k]->mSrc[1].mIntConst; + ins->mSrc[1] = pindexins[k]->mDst; + ins->mSrc[1].mMemory = IM_INDIRECT; + ins->mSrc[1].mIntConst = 0; + mInstructions[j++] = ins; + } + else +#endif + { + indexStep[ins->mDst.mTemp] = indexStep[ins->mSrc[0].mTemp]; + indexBase[ins->mDst.mTemp] = 0; + + mLoopPrefix->mInstructions.Insert(mLoopPrefix->mInstructions.Size() - 1, ins); + + InterInstruction* ains = new InterInstruction(ins->mLocation, IC_LEA); + ains->mDst = ins->mDst; + ains->mSrc[1] = ins->mDst; + ains->mSrc[1].mMemory = IM_INDIRECT; + ains->mSrc[1].mIntConst = 0; + ains->mSrc[0].mType = IT_INT16; + ains->mSrc[0].mTemp = -1; + ains->mSrc[0].mIntConst = indexStep[ins->mSrc[0].mTemp]; + if (ains->mDst.mRange.mMaxState == IntegerValueRange::S_BOUND) + ains->mDst.mRange.mMaxValue += ains->mSrc[0].mIntConst; + + if (tailBlock->mEntryRequiredTemps[ains->mDst.mTemp]) + { + InterInstruction* dins = new InterInstruction(ins->mLocation, IC_LEA); + dins->mDst = ins->mDst; + dins->mSrc[1] = ins->mDst; + dins->mSrc[1].mMemory = IM_INDIRECT; + dins->mSrc[1].mIntConst = 0; + dins->mSrc[0].mType = IT_INT16; + dins->mSrc[0].mTemp = -1; + dins->mSrc[0].mIntConst = -indexStep[ins->mSrc[0].mTemp]; + tailBlock->mInstructions.Insert(0, dins); + } + indexins.Push(ains); + + if (indexStep[ins->mSrc[0].mTemp] > 1) + pindexins.Push(ins); + } + } + } + else if (ins->mDst.mTemp >= 0 && dep[ins->mDst.mTemp] == DEP_INDEX_EXTENDED) + { + indexStep[ins->mDst.mTemp] = indexStep[ins->mSrc[0].mTemp]; + indexBase[ins->mDst.mTemp] = indexBase[ins->mSrc[0].mTemp]; + + mLoopPrefix->mInstructions.Insert(mLoopPrefix->mInstructions.Size() - 1, ins); + + InterInstruction* ains = new InterInstruction(ins->mLocation, ins->mCode); + ains->mOperator = ins->mOperator; + ains->mSrc[0] = ins->mSrc[0]; + ains->mDst = ins->mDst; + + mInstructions[j++] = ains; + } + else + { + mInstructions[j++] = ins; + } + } + + mInstructions.SetSize(j); + + NumberSet requiredTemps(tailBlock->mEntryRequiredTemps); + + for (int i = 0; i < mInstructions.Size(); i++) + { + InterInstruction* ins = mInstructions[i]; + for(int j=0; jmNumOperands; j++) + if (ins->mSrc[j].mTemp >= 0 && ins->mDst.mTemp != ins->mSrc[j].mTemp) + requiredTemps += ins->mSrc[j].mTemp; + } + + int di = mInstructions.Size() - 1; + if (di > 0 && mInstructions[di - 1]->mCode == IC_RELATIONAL_OPERATOR) + { + di--; + if (di > 0 && mInstructions[di - 1]->mDst.mTemp == mInstructions[di]->mSrc[0].mTemp || mInstructions[di - 1]->mDst.mTemp == mInstructions[di]->mSrc[1].mTemp) + di--; + } + + for (int i = 0; i < indexins.Size(); i++) + { + InterInstruction* ins = indexins[i]; + if (requiredTemps[ins->mDst.mTemp]) + { + mInstructions.Insert(di, ins); + di++; + } + } + + int i = 0; + while (i < mInstructions.Size()) + { + InterInstruction* ins = mInstructions[i]; + if (!HasSideEffect(ins->mCode) && !ins->mVolatile && ins->mDst.mTemp >= 0 && !requiredTemps[ins->mDst.mTemp]) + mInstructions.Remove(i); + else + i++; + } + + // move temp moves into tail if not used in loop + + i = mInstructions.Size() - 1; + while (i >= 0) + { + InterInstruction* ins = mInstructions[i]; + if (ins->mCode == IC_LOAD_TEMPORARY && !mEntryRequiredTemps[ins->mDst.mTemp]) + { + int dt = ins->mDst.mTemp, st = ins->mSrc[0].mTemp; + + int toffset = 0; + int j = i + 1; + while (j < mInstructions.Size()) + { + InterInstruction* cins = mInstructions[j]; + + if (cins->mDst.mTemp == dt) + break; + else if (cins->mCode == IC_LOAD_TEMPORARY && cins->mSrc[0].mTemp == st && cins->mSrc[0].mFinal) + st = cins->mDst.mTemp; + else if (cins->mDst.mTemp == st) + { + if (cins->mCode == IC_LEA && cins->mSrc[1].mTemp == st && cins->mSrc[0].mTemp < 0) + toffset += int(cins->mSrc[0].mIntConst); + else if (cins->mCode == IC_BINARY_OPERATOR && cins->mOperator == IA_ADD && cins->mSrc[1].mTemp == st && cins->mSrc[0].mTemp < 0) + toffset += int(cins->mSrc[0].mIntConst); + else + break; + } + else + { + int k = 0; + while (k < cins->mNumOperands && cins->mSrc[k].mTemp != dt) + k++; + + if (k != cins->mNumOperands) + break; + } + + j++; + } + + if (j == mInstructions.Size()) + { + ins->mSrc[0].mTemp = st; + if (toffset != 0) + { + if (ins->mDst.mType == IT_POINTER) + { + ins->mCode = IC_LEA; + ins->mNumOperands = 2; + ins->mSrc[1] = ins->mSrc[0]; + ins->mSrc[1].mMemory = IM_INDIRECT; + ins->mDst.mMemory = IM_INDIRECT; + ins->mSrc[0].mTemp = -1; + ins->mSrc[0].mType = IT_INT16; + ins->mSrc[0].mIntConst = -toffset; + } + else + { + ins->mCode = IC_BINARY_OPERATOR; + ins->mOperator = IA_ADD; + ins->mNumOperands = 2; + ins->mSrc[1] = ins->mSrc[0]; + ins->mSrc[0].mTemp = -1; + ins->mSrc[0].mType = IT_INT16; + ins->mSrc[0].mIntConst = -toffset; + } + } + tailBlock->mInstructions.Insert(0, ins); + mInstructions.Remove(i); + } + } + i--; + } + + + } + + if (mTrueJump) + mTrueJump->SingleBlockLoopOptimisation(aliasedParams, staticVars); + if (mFalseJump) + mFalseJump->SingleBlockLoopOptimisation(aliasedParams, staticVars); + } +} + +static bool IsSimilarLea(InterInstruction* ins0, InterInstruction* ins1) +{ + if (ins0->mCode == IC_LEA && ins1->mCode == IC_LEA && + ins0->mSrc[0].mTemp == ins1->mSrc[0].mTemp && + ins0->mSrc[1].mTemp == ins1->mSrc[1].mTemp && + ins0->mSrc[0].IsUByte() && ins1->mSrc[0].IsUByte()) + { + if (ins0->mSrc[1].mTemp >= 0 && ins0->mSrc[1].mFinal && ins1->mSrc[1].mFinal || + ins0->mSrc[1].mMemory == ins1->mSrc[1].mMemory && + ins0->mSrc[1].mVarIndex == ins1->mSrc[1].mVarIndex && + ins0->mSrc[1].mLinkerObject == ins1->mSrc[1].mLinkerObject) + { + if (ins0->mSrc[1].mIntConst < ins1->mSrc[1].mIntConst) + { + if (ins1->mSrc[0].mRange.mMaxValue + ins1->mSrc[1].mIntConst - ins0->mSrc[1].mIntConst < 128) + return true; + } + else if (ins0->mSrc[1].mIntConst > ins1->mSrc[1].mIntConst) + { + if (ins0->mSrc[0].mRange.mMaxValue + ins0->mSrc[1].mIntConst - ins1->mSrc[1].mIntConst < 128) + return true; + } + else + return true; + } + } + + return false; +} + +bool InterCodeBasicBlock::ShortLeaMerge(int& spareTemps) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + if (!mLoopHead && mEntryBlocks.Size() > 1) + { + int k = 0; + while (k < mEntryBlocks.Size() && !mEntryBlocks[k]->mFalseJump) + k++; + + if (k == mEntryBlocks.Size()) + { + GrowingInstructionArray iins(nullptr); + + for (int i = 0; i < mInstructions.Size(); i++) + { + InterInstruction* ins = mInstructions[i]; + + int ttemp = -1; + if (ins->mCode == IC_STORE && ins->mSrc[1].mTemp >= 0) + ttemp = ins->mSrc[1].mTemp; + else if (ins->mCode == IC_LOAD && ins->mSrc[0].mTemp >= 0) + ttemp = ins->mSrc[0].mTemp; + + if (ttemp >= 0 && !IsTempModifiedInRange(0, i, ttemp)) + { + bool found = true; + + iins.SetSize(0); + for (int k = 0; k < mEntryBlocks.Size(); k++) + { + InterInstruction* sins = mEntryBlocks[k]->FindTempOrigin(ttemp); + if (sins && sins->mCode == IC_LEA && mEntryBlocks[k]->CanMoveInstructionBehindBlock(mEntryBlocks[k]->mMark)) + iins.Push(sins); + else + { + found = false; + break; + } + } + + if (found) + { + int64 minint = iins[0]->mSrc[1].mIntConst; + + for (int k = 1; k < mEntryBlocks.Size(); k++) + { + if (IsSimilarLea(iins[0], iins[k])) + { + if (iins[k]->mSrc[1].mIntConst < minint) + minint = iins[k]->mSrc[1].mIntConst; + } + else + { + found = false; + break; + } + } + + if (found) + { + InterInstruction* lins = iins[0]->Clone(); + lins->mSrc[0].mRange.Reset(); + lins->mSrc[1].mIntConst = minint; + mInstructions.Insert(0, lins); + + if (iins[0]->mSrc[0].mTemp < 0) + { + if (spareTemps + 2 >= mEntryRequiredTemps.Size() + 16) + return true; + int ttemp = spareTemps++; + mInstructions[0]->mSrc[0].mTemp = ttemp; + mInstructions[0]->mSrc[0].mType = IT_INT16; + + for (int k = 0; k < mEntryBlocks.Size(); k++) + { + InterInstruction* sins = iins[k]; + sins->mCode = IC_CONSTANT; + sins->mDst = mInstructions[0]->mSrc[0]; + sins->mConst = sins->mSrc[0]; + sins->mConst.mIntConst -= minint; + sins->mNumOperands = 0; + } + } + else + { + for (int k = 0; k < mEntryBlocks.Size(); k++) + { + InterInstruction* sins = iins[k]; + sins->mCode = IC_BINARY_OPERATOR; + sins->mOperator = IA_ADD; + sins->mDst = sins->mSrc[0]; + sins->mSrc[1].mTemp = -1; + sins->mSrc[1].mType = sins->mSrc[0].mType; + sins->mSrc[1].mIntConst -= minint; + sins->mDst.mRange.mMaxValue += sins->mSrc[1].mIntConst; + mEntryBlocks[k]->mExitRequiredTemps += sins->mDst.mTemp; + } + } + + changed = true; + } + } + } + } + } + } + + if (mTrueJump && mTrueJump->ShortLeaMerge(spareTemps)) changed = true; + if (mFalseJump && mFalseJump->ShortLeaMerge(spareTemps)) changed = true; + } + + return changed; +} + +bool InterCodeBasicBlock::ShortLeaCleanup(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + if (!mLoopHead && mEntryBlocks.Size() > 1) + { + int k = 0; + while (k < mEntryBlocks.Size() && !mEntryBlocks[k]->mFalseJump) + k++; + + if (k == mEntryBlocks.Size()) + { + GrowingInstructionArray iins(nullptr); + + for (int i = 0; i < mInstructions.Size(); i++) + { + InterInstruction* ins = mInstructions[i]; + + int ttemp = -1; + if (ins->mCode == IC_STORE && ins->mSrc[1].mTemp >= 0) + ttemp = ins->mSrc[1].mTemp; + else if (ins->mCode == IC_LOAD && ins->mSrc[0].mTemp >= 0) + ttemp = ins->mSrc[0].mTemp; + + if (ttemp >= 0 && !IsTempModifiedInRange(0, i, ttemp)) + { + bool found = true; + bool shortlea = false, noshortlea = false; + + iins.SetSize(0); + for (int k = 0; k < mEntryBlocks.Size(); k++) + { + InterInstruction* sins = mEntryBlocks[k]->FindTempOrigin(ttemp); + if (ins->mCode == IC_STORE && sins && sins->mCode == IC_LEA && sins->mSrc[1].mTemp < 0 && sins->mSrc[0].IsUByte() && CanMoveInstructionBeforeBlock(i) && !mEntryBlocks[k]->mFalseJump) + shortlea = true; + else + noshortlea = true; + } + + if (shortlea && !noshortlea) + { + for (int k = 0; k < mEntryBlocks.Size(); k++) + { + mEntryBlocks[k]->AppendBeforeBranch(ins->Clone()); + if (ins->mDst.mTemp >= 0) + mEntryBlocks[k]->mExitRequiredTemps += ins->mDst.mTemp; + } + if (ins->mDst.mTemp >= 0) + mEntryRequiredTemps += ins->mDst.mTemp; + ins->mCode = IC_NONE; ins->mNumOperands = 0; ins->mDst.mTemp = -1; + changed = true; + } + } + } + } + } + + if (mTrueJump && mTrueJump->ShortLeaCleanup()) changed = true; + if (mFalseJump && mFalseJump->ShortLeaCleanup()) changed = true; + } + + return changed; +} + +void InterCodeBasicBlock::CompactInstructions(void) +{ + if (!mVisited) + { + mVisited = true; + + int j = 0; + for (int i = 0; i < mInstructions.Size(); i++) + { + if (mInstructions[i]->mCode != IC_NONE) + { + mInstructions[j++] = mInstructions[i]; + } + } + mInstructions.SetSize(j); + + if (mTrueJump) + mTrueJump->CompactInstructions(); + if (mFalseJump) + mFalseJump->CompactInstructions(); + } +} + +void InterCodeBasicBlock::CheckFinalLocal(void) +{ +#if _DEBUG + NumberSet required(mExitRequiredTemps); + + for (int i = mInstructions.Size() - 1; i >= 0; i--) + { + const InterInstruction* ins(mInstructions[i]); + if (ins->mCode == IC_NONE) + assert(ins->mDst.mTemp < 0 && ins->mNumOperands == 0); + + if (ins->mDst.mTemp >= 0) + required -= ins->mDst.mTemp; + for (int j = 0; j < ins->mNumOperands; j++) + { + if (ins->mSrc[j].mTemp >= 0 && ins->mSrc[j].mFinal) + assert(!required[ins->mSrc[j].mTemp]); + } + + for (int j = 0; j < ins->mNumOperands; j++) + if (ins->mSrc[j].mTemp >= 0) + required += ins->mSrc[j].mTemp; + } + + NumberSet provided(mEntryProvidedTemps); + + for (int i = 0; i < mInstructions.Size(); i++) + { + const InterInstruction* ins(mInstructions[i]); + for (int j = 0; j < ins->mNumOperands; j++) + { + if (ins->mSrc[j].mTemp >= 0 && !provided[ins->mSrc[j].mTemp]) + { + // printf("Use of potentially undefined temp %d\n", ins->mSrc[j].mTemp); + } + } + + if (ins->mDst.mTemp >= 0) + provided += ins->mDst.mTemp; + } +#endif +} + +void InterCodeBasicBlock::CheckFinal(void) +{ +#if _DEBUG + if (!mVisited) + { + mVisited = true; + + CheckFinalLocal(); + + if (mTrueJump) mTrueJump->CheckFinal(); + if (mFalseJump) mFalseJump->CheckFinal(); + } +#endif +} + +void InterCodeBasicBlock::CheckBlocks(void) +{ +#if _DEBUG + if (!mVisited) + { + mVisited = true; + + for (int i = 0; i < mInstructions.Size(); i++) + assert(mInstructions[i] != nullptr); + + assert(!mTrueJump || mTrueJump->mIndex > 0); + assert(!mFalseJump || mFalseJump->mIndex > 0); + + if (mTrueJump) mTrueJump->CheckBlocks(); + if (mFalseJump) mFalseJump->CheckBlocks(); + } +#endif +} + +bool InterCodeBasicBlock::PeepholeReplaceOptimization(const GrowingVariableArray& staticVars, const GrowingInterCodeProcedurePtrArray& staticProcs) +{ + int j = 0; + for (int i = 0; i < mInstructions.Size(); i++) + { + if (mInstructions[i]->mCode != IC_NONE) + { + mInstructions[j++] = mInstructions[i]; + } + } + mInstructions.SetSize(j); + + bool changed = false; + + for (int i = 0; i < mInstructions.Size(); i++) + { + if (mInstructions[i]->mCode == IC_LOAD_TEMPORARY && mInstructions[i]->mDst.mTemp == mInstructions[i]->mSrc[0].mTemp) + { + mInstructions[i]->mCode = IC_NONE; + mInstructions[i]->mNumOperands = 0; + mInstructions[i]->mDst.mTemp = -1; + changed = true; + } + if (mInstructions[i]->mCode == IC_LOAD && mInstructions[i]->mSrc[0].mMemory == IM_GLOBAL && (mInstructions[i]->mSrc[0].mLinkerObject->mFlags & LOBJF_CONST)) + { + LoadConstantFold(mInstructions[i], nullptr, staticVars, staticProcs); + changed = true; + } + + if (mInstructions[i]->mCode == IC_BINARY_OPERATOR && mInstructions[i]->mOperator == IA_AND && mInstructions[i]->mSrc[1].mTemp < 0 && mInstructions[i]->mSrc[0].IsUnsigned()) + { + mInstructions[i]->mSrc[1].mIntConst &= BinMask(mInstructions[i]->mSrc[0].mRange.mMaxValue); + if (mInstructions[i]->mSrc[1].mIntConst == BinMask(mInstructions[i]->mSrc[1].mIntConst) && mInstructions[i]->mSrc[0].mRange.mMaxValue <= mInstructions[i]->mSrc[1].mIntConst) + { + mInstructions[i]->mCode = IC_LOAD_TEMPORARY; + mInstructions[i]->mNumOperands = 1; + changed = true; + } + + } + if (mInstructions[i]->mCode == IC_BINARY_OPERATOR && mInstructions[i]->mOperator == IA_AND && mInstructions[i]->mSrc[0].mTemp < 0 && mInstructions[i]->mSrc[1].IsUnsigned()) + { + mInstructions[i]->mSrc[0].mIntConst &= BinMask(mInstructions[i]->mSrc[1].mRange.mMaxValue); + if (mInstructions[i]->mSrc[0].mIntConst == BinMask(mInstructions[i]->mSrc[0].mIntConst) && mInstructions[i]->mSrc[1].mRange.mMaxValue <= mInstructions[i]->mSrc[0].mIntConst) + { + mInstructions[i]->mCode = IC_LOAD_TEMPORARY; + mInstructions[i]->mSrc[0] = mInstructions[i]->mSrc[1]; + mInstructions[i]->mNumOperands = 1; + changed = true; + } + } + + if (mInstructions[i]->mCode == IC_RELATIONAL_OPERATOR && mInstructions[i]->mOperator == IA_CMPGEU && mInstructions[i]->mSrc[0].mTemp < 0 && IsIntegerType(mInstructions[i]->mSrc[0].mType) && mInstructions[i]->mSrc[0].mIntConst == 1) + { + mInstructions[i]->mOperator = IA_CMPNE; + mInstructions[i]->mSrc[0].mIntConst = 0; + changed = true; + } + + if (i + 1 < mInstructions.Size()) + { +#if 1 + if (mInstructions[i + 0]->mCode == IC_RELATIONAL_OPERATOR && + mInstructions[i + 1]->mCode == IC_BRANCH && mInstructions[i + 1]->mSrc[0].mTemp == mInstructions[i + 0]->mDst.mTemp && mInstructions[i + 1]->mSrc[0].mFinal && + (mInstructions[i + 0]->mOperator == IA_CMPEQ || mInstructions[i + 0]->mOperator == IA_CMPNE) && + (mInstructions[i + 0]->mSrc[0].mTemp < 0 && mInstructions[i + 0]->mSrc[1].mType == IT_INT8 && mInstructions[i + 0]->mSrc[0].mIntConst == 0 || + mInstructions[i + 0]->mSrc[1].mTemp < 0 && mInstructions[i + 0]->mSrc[0].mType == IT_INT8 && mInstructions[i + 0]->mSrc[1].mIntConst == 0)) + { + if (mInstructions[i + 0]->mSrc[0].mTemp < 0) + mInstructions[i + 1]->mSrc[0] = mInstructions[i + 0]->mSrc[1]; + else + mInstructions[i + 1]->mSrc[0] = mInstructions[i + 0]->mSrc[0]; + if (mInstructions[i + 0]->mOperator == IA_CMPEQ) + { + InterCodeBasicBlock* b = mTrueJump; mTrueJump = mFalseJump; mFalseJump = b; + } + mInstructions[i + 0]->mCode = IC_NONE; + mInstructions[i + 0]->mNumOperands = 0; + mInstructions[i + 0]->mDst.mTemp = -1; + changed = true; + } +#endif + + if (mInstructions[i + 0]->mCode == IC_LOAD && mInstructions[i + 0]->mSrc[0].mTemp >= 0 && mInstructions[i + 0]->mDst.mTemp != mInstructions[i + 0]->mSrc[0].mTemp && + mInstructions[i + 1]->mCode == IC_LEA && mInstructions[i + 1]->mDst.mTemp == mInstructions[i + 0]->mSrc[0].mTemp && mInstructions[i + 1]->mSrc[1].mTemp == mInstructions[i + 0]->mSrc[0].mTemp && + mInstructions[i + 1]->mSrc[0].mTemp < 0 && mInstructions[i + 0]->mSrc[0].mIntConst == mInstructions[i + 1]->mSrc[0].mIntConst) + { + InterInstruction* ins(mInstructions[i + 0]); + mInstructions[i + 0] = mInstructions[i + 1]; + mInstructions[i + 1] = ins; + mInstructions[i + 1]->mSrc[0].mIntConst = 0; + changed = true; + } + + if (mInstructions[i + 0]->mCode == IC_LEA && mInstructions[i + 0]->mSrc[0].mTemp < 0 && mInstructions[i + 0]->mSrc[1].mTemp >= 0 && mInstructions[i + 0]->mDst.mTemp != mInstructions[i + 0]->mSrc[1].mTemp && + mInstructions[i + 1]->mCode == IC_LEA && mInstructions[i + 1]->mSrc[0].mTemp < 0 && mInstructions[i + 1]->mSrc[1].mTemp == mInstructions[i + 0]->mSrc[1].mTemp && mInstructions[i + 1]->mDst.mTemp != mInstructions[i + 1]->mSrc[1].mTemp && + mInstructions[i + 0]->mSrc[0].mIntConst > mInstructions[i + 1]->mSrc[0].mIntConst && + !mInstructions[i + 2]->ReferencesTemp(mInstructions[i + 1]->mDst.mTemp)) + { + SwapInstructions(mInstructions[i + 0], mInstructions[i + 1]); + InterInstruction* ins(mInstructions[i + 0]); + mInstructions[i + 0] = mInstructions[i + 1]; + mInstructions[i + 1] = ins; + } + } + + if (mInstructions[i]->mCode == IC_LEA && mInstructions[i]->mSrc[1].mFinal && mInstructions[i]->mSrc[0].mTemp < 0) + { + int k = i; + while (k + 1 < mInstructions.Size() && + mInstructions[k + 1]->mCode == IC_STORE && mInstructions[k + 1]->mSrc[1].mTemp == mInstructions[i]->mDst.mTemp && mInstructions[k + 1]->mSrc[0].mTemp != mInstructions[i]->mDst.mTemp && + mInstructions[k + 1]->mSrc[1].mIntConst + mInstructions[i]->mSrc[0].mIntConst < 128) + k++; + if (k > i && mInstructions[k]->mSrc[1].mFinal) + { + while (k > i) + { + mInstructions[k]->mSrc[1].mRange = mInstructions[i]->mSrc[1].mRange; + mInstructions[k]->mSrc[1].mTemp = mInstructions[i]->mSrc[1].mTemp; + mInstructions[k]->mSrc[1].mIntConst += mInstructions[i]->mSrc[0].mIntConst; + k--; + } + mInstructions[i + 0]->mCode = IC_NONE; + mInstructions[i + 0]->mNumOperands = 0; + mInstructions[i + 0]->mDst.mTemp = -1; + changed = true; + } + + } + + if (i + 2 < mInstructions.Size()) + { + if (mInstructions[i + 0]->mCode == IC_NONE) + { + // just skip it + } + else if (mInstructions[i + 0]->mCode == IC_LOAD && + mInstructions[i + 1]->mCode == IC_LOAD && + mInstructions[i + 1]->mSrc[0].mTemp == mInstructions[i + 0]->mSrc[0].mTemp && + mInstructions[i + 0]->mSrc[0].mIntConst > mInstructions[i + 1]->mSrc[0].mIntConst && + mInstructions[i + 1]->mSrc[0].mTemp != mInstructions[i + 0]->mDst.mTemp && + !(mInstructions[i + 0]->mVolatile && mInstructions[i + 1]->mVolatile)) + { + SwapInstructions(mInstructions[i + 0], mInstructions[i + 1]); + InterInstruction* ins(mInstructions[i + 0]); + mInstructions[i + 0] = mInstructions[i + 1]; + mInstructions[i + 1] = ins; + changed = true; + } + else if ( + mInstructions[i + 0]->mCode == IC_BINARY_OPERATOR && mInstructions[i + 0]->mOperator == IA_ADD && + mInstructions[i + 0]->mSrc[0].IsUByte() && mInstructions[i + 0]->mSrc[1].IsUByte() && + + mInstructions[i + 1]->mDst.mRange.IsRange(0, 1) && CanSwapInstructions(mInstructions[i + 0], mInstructions[i + 1]) && + + mInstructions[i + 2]->mCode == IC_BINARY_OPERATOR && mInstructions[i + 2]->mOperator == IA_ADD && + mInstructions[i + 2]->mSrc[0].mTemp == mInstructions[i + 0]->mDst.mTemp && + mInstructions[i + 2]->mSrc[1].mTemp == mInstructions[i + 1]->mDst.mTemp) + { + SwapInstructions(mInstructions[i + 0], mInstructions[i + 1]); + InterInstruction* ins(mInstructions[i + 0]); + mInstructions[i + 0] = mInstructions[i + 1]; + mInstructions[i + 1] = ins; + changed = true; + } + else if (mInstructions[i + 0]->mDst.mTemp >= 0 && + mInstructions[i + 1]->mCode == IC_LOAD_TEMPORARY && mInstructions[i + 1]->mSrc[0].mTemp == mInstructions[i]->mDst.mTemp && + (mInstructions[i + 2]->mCode == IC_RELATIONAL_OPERATOR || mInstructions[i + 2]->mCode == IC_BINARY_OPERATOR) && mInstructions[i + 2]->mSrc[0].mTemp == mInstructions[i]->mDst.mTemp && mInstructions[i + 2]->mSrc[0].mFinal) + { +#if _DEBUG + for (int j = i + 3; j < mInstructions.Size(); j++) + assert(!mInstructions[j]->ReferencesTemp(mInstructions[i]->mDst.mTemp)); +#endif + + int t = mInstructions[i + 0]->mDst.mTemp; + mInstructions[i + 0]->mDst.mTemp = mInstructions[i + 1]->mDst.mTemp; + mInstructions[i + 2]->mSrc[0].mTemp = mInstructions[i + 1]->mDst.mTemp; + mInstructions[i + 2]->mSrc[0].mFinal = false; + if (mInstructions[i + 2]->mSrc[1].mTemp == t) + { + mInstructions[i + 2]->mSrc[1].mTemp = mInstructions[i + 1]->mDst.mTemp; + mInstructions[i + 2]->mSrc[1].mFinal = false; + } + mInstructions[i + 1]->mCode = IC_NONE; + mInstructions[i + 1]->mDst.mTemp = -1; + mInstructions[i + 1]->mNumOperands = 0; + changed = true; + } + else if (mInstructions[i + 0]->mDst.mTemp >= 0 && + mInstructions[i + 1]->mCode == IC_LOAD_TEMPORARY && mInstructions[i + 1]->mSrc[0].mTemp == mInstructions[i]->mDst.mTemp && + (mInstructions[i + 2]->mCode == IC_RELATIONAL_OPERATOR || mInstructions[i + 2]->mCode == IC_BINARY_OPERATOR) && mInstructions[i + 2]->mSrc[1].mTemp == mInstructions[i]->mDst.mTemp && mInstructions[i + 2]->mSrc[1].mFinal) + { +#if _DEBUG + for (int j = i + 3; j < mInstructions.Size(); j++) + assert(!mInstructions[j]->ReferencesTemp(mInstructions[i]->mDst.mTemp)); +#endif + + mInstructions[i + 0]->mDst.mTemp = mInstructions[i + 1]->mDst.mTemp; + mInstructions[i + 2]->mSrc[1].mTemp = mInstructions[i + 1]->mDst.mTemp; + mInstructions[i + 2]->mSrc[1].mFinal = false; + mInstructions[i + 1]->mCode = IC_NONE; + mInstructions[i + 1]->mNumOperands = 0; + mInstructions[i + 1]->mDst.mTemp = -1; + changed = true; + } + else if ( + mInstructions[i + 0]->mCode == IC_BINARY_OPERATOR && mInstructions[i + 0]->mOperator == IA_ADD && + mInstructions[i + 0]->mSrc[0].mTemp < 0 && mInstructions[i + 0]->mSrc[0].mIntConst >= 0 && + mInstructions[i + 1]->mCode == IC_TYPECAST && mInstructions[i + 1]->mSrc[0].mTemp == mInstructions[i + 0]->mDst.mTemp && mInstructions[i + 1]->mSrc[0].mFinal && + mInstructions[i + 1]->mDst.mType == IT_POINTER && mInstructions[i + 1]->mSrc[0].mType == IT_INT16) + { + int64 addr = mInstructions[i + 0]->mSrc[0].mIntConst; + + mInstructions[i + 0]->mCode = IC_LEA; + mInstructions[i + 0]->mSrc[0] = mInstructions[i + 0]->mSrc[1]; + mInstructions[i + 0]->mSrc[1].mTemp = -1; + mInstructions[i + 0]->mSrc[1].mType = IT_POINTER; + mInstructions[i + 0]->mSrc[1].mMemory = IM_ABSOLUTE; + mInstructions[i + 0]->mSrc[1].mIntConst = addr; + mInstructions[i + 0]->mSrc[1].mLinkerObject = nullptr; + mInstructions[i + 0]->mSrc[1].mVarIndex = -1; + mInstructions[i + 0]->mDst = mInstructions[i + 1]->mDst; + + mInstructions[i + 1]->mCode = IC_NONE; + mInstructions[i + 1]->mNumOperands = 0; + mInstructions[i + 1]->mDst.mTemp = -1; + changed = true; + } + else if ( + mInstructions[i + 0]->mCode == IC_BINARY_OPERATOR && mInstructions[i + 0]->mOperator == IA_SAR && mInstructions[i + 0]->mSrc[0].mTemp < 0 && + mInstructions[i + 1]->mCode == IC_BINARY_OPERATOR && mInstructions[i + 1]->mOperator == IA_MUL && mInstructions[i + 1]->mSrc[0].mTemp < 0 && + mInstructions[i + 1]->mSrc[1].mTemp == mInstructions[i + 0]->mDst.mTemp && mInstructions[i + 1]->mSrc[1].mFinal && + (mInstructions[i + 1]->mSrc[0].mIntConst & ((1LL << mInstructions[i + 0]->mSrc[0].mIntConst) - 1)) == 0) + { + int64 shift = mInstructions[i + 0]->mSrc[0].mIntConst; + mInstructions[i + 1]->mSrc[0].mIntConst >>= shift; + mInstructions[i + 0]->mOperator = IA_AND; + mInstructions[i + 0]->mSrc[0].mIntConst = ~((1LL << shift) - 1); + mInstructions[i + 0]->mDst.mRange.Reset(); + changed = true; + } + else if ( + mInstructions[i + 0]->mCode == IC_BINARY_OPERATOR && mInstructions[i + 0]->mOperator == IA_SAR && mInstructions[i + 0]->mSrc[0].mTemp < 0 && + mInstructions[i + 1]->mCode == IC_BINARY_OPERATOR && mInstructions[i + 1]->mOperator == IA_MUL && mInstructions[i + 1]->mSrc[1].mTemp < 0 && + mInstructions[i + 1]->mSrc[0].mTemp == mInstructions[i + 0]->mDst.mTemp && mInstructions[i + 1]->mSrc[0].mFinal && + (mInstructions[i + 1]->mSrc[1].mIntConst & ((1LL << mInstructions[i + 0]->mSrc[0].mIntConst) - 1)) == 0) + { + int64 shift = mInstructions[i + 0]->mSrc[0].mIntConst; + mInstructions[i + 1]->mSrc[1].mIntConst >>= shift; + mInstructions[i + 0]->mOperator = IA_AND; + mInstructions[i + 0]->mSrc[0].mIntConst = ~((1LL << shift) - 1); + mInstructions[i + 0]->mDst.mRange.Reset(); + changed = true; + } + else if ( + mInstructions[i + 0]->mCode == IC_BINARY_OPERATOR && mInstructions[i + 0]->mOperator == IA_SHL && mInstructions[i + 0]->mSrc[0].mTemp < 0 && + mInstructions[i + 1]->mCode == IC_BINARY_OPERATOR && mInstructions[i + 1]->mOperator == IA_MUL && mInstructions[i + 1]->mSrc[0].mTemp < 0 && + mInstructions[i + 1]->mSrc[1].mTemp == mInstructions[i + 0]->mDst.mTemp && mInstructions[i + 1]->mSrc[1].mFinal && + (mInstructions[i + 1]->mSrc[0].mIntConst << mInstructions[i + 0]->mSrc[0].mIntConst) < 65536) + { + mInstructions[i + 1]->mSrc[0].mIntConst <<= mInstructions[i + 0]->mSrc[0].mIntConst;; + mInstructions[i + 1]->mSrc[1] = mInstructions[i + 0]->mSrc[1]; + mInstructions[i + 0]->mCode = IC_NONE; + mInstructions[i + 0]->mNumOperands = 0; + mInstructions[i + 0]->mDst.mTemp = -1; + changed = true; + } + else if ( + mInstructions[i + 0]->mCode == IC_BINARY_OPERATOR && mInstructions[i + 0]->mOperator == IA_MUL && mInstructions[i + 0]->mSrc[0].mTemp < 0 && + mInstructions[i + 1]->mCode == IC_BINARY_OPERATOR && mInstructions[i + 1]->mOperator == IA_SHL && mInstructions[i + 1]->mSrc[0].mTemp < 0 && + mInstructions[i + 1]->mSrc[1].mTemp == mInstructions[i + 0]->mDst.mTemp && mInstructions[i + 1]->mSrc[1].mFinal && + (mInstructions[i + 0]->mSrc[0].mIntConst << mInstructions[i + 1]->mSrc[0].mIntConst) < 65536) + { + mInstructions[i + 1]->mSrc[0].mIntConst = mInstructions[i + 0]->mSrc[0].mIntConst << mInstructions[i + 1]->mSrc[0].mIntConst; + mInstructions[i + 1]->mSrc[1] = mInstructions[i + 0]->mSrc[1]; + mInstructions[i + 1]->mOperator = IA_MUL; + mInstructions[i + 0]->mCode = IC_NONE; + mInstructions[i + 0]->mNumOperands = 0; + mInstructions[i + 0]->mDst.mTemp = -1; + changed = true; + } + else if ( + mInstructions[i + 0]->mCode == IC_BINARY_OPERATOR && mInstructions[i + 0]->mOperator == IA_SHL && mInstructions[i + 0]->mSrc[0].mTemp < 0 && + mInstructions[i + 1]->mCode == IC_BINARY_OPERATOR && mInstructions[i + 1]->mOperator == IA_ADD && mInstructions[i + 1]->mSrc[0].mTemp < 0 && + mInstructions[i + 1]->mSrc[1].mTemp == mInstructions[i + 0]->mDst.mTemp && mInstructions[i + 1]->mSrc[1].mFinal && + mInstructions[i + 0]->mSrc[1].IsUByte() && !mInstructions[i + 1]->mSrc[1].IsUByte() && + mInstructions[i + 0]->mSrc[1].mRange.mMaxValue + (mInstructions[i + 1]->mSrc[0].mIntConst >> mInstructions[i + 0]->mSrc[0].mIntConst) < 256 && + (mInstructions[i + 1]->mSrc[0].mIntConst & ((1 << mInstructions[i + 0]->mSrc[0].mIntConst) - 1)) == 0) + { + int64 shift = mInstructions[i + 0]->mSrc[0].mIntConst, add = mInstructions[i + 1]->mSrc[0].mIntConst; + + mInstructions[i + 0]->mOperator = IA_ADD; mInstructions[i + 0]->mSrc[0].mIntConst = add >> shift; + mInstructions[i + 1]->mOperator = IA_SHL; mInstructions[i + 1]->mSrc[0].mIntConst = shift; + mInstructions[i + 0]->mDst.mRange = mInstructions[i + 0]->mSrc[1].mRange; + mInstructions[i + 0]->mDst.mRange.AddConstValue(mInstructions[i + 0]->mDst.mType, add >> shift); + mInstructions[i + 1]->mSrc[1].mRange = mInstructions[i + 0]->mDst.mRange; + changed = true; + } +#if 1 + else if ( + mInstructions[i + 0]->mCode == IC_BINARY_OPERATOR && mInstructions[i + 0]->mOperator == IA_OR && mInstructions[i + 0]->mSrc[0].mTemp < 0 && + mInstructions[i + 1]->mCode == IC_BINARY_OPERATOR && mInstructions[i + 1]->mOperator == IA_AND && mInstructions[i + 1]->mSrc[0].mTemp < 0 && + mInstructions[i + 1]->mSrc[1].mTemp == mInstructions[i + 0]->mDst.mTemp && mInstructions[i + 1]->mSrc[1].mFinal) + { + int64 ior = mInstructions[i + 0]->mSrc[0].mIntConst & mInstructions[i + 1]->mSrc[0].mIntConst; + int64 iand = mInstructions[i + 1]->mSrc[0].mIntConst; + + mInstructions[i + 0]->mOperator = IA_AND; + mInstructions[i + 0]->mSrc[0].mIntConst = iand; + mInstructions[i + 1]->mOperator = IA_OR; + mInstructions[i + 1]->mSrc[0].mIntConst = ior; + changed = true; + } +#endif +#if 1 + else if ( + mInstructions[i + 0]->mCode == IC_BINARY_OPERATOR && mInstructions[i + 0]->mOperator == IA_AND && mInstructions[i + 0]->mSrc[0].mTemp < 0 && + mInstructions[i + 1]->mCode == IC_BINARY_OPERATOR && mInstructions[i + 1]->mOperator == IA_AND && mInstructions[i + 1]->mSrc[0].mTemp < 0 && + mInstructions[i + 1]->mSrc[1].mTemp == mInstructions[i + 0]->mDst.mTemp && mInstructions[i + 1]->mSrc[1].mFinal) + { + mInstructions[i + 0]->mSrc[0].mIntConst &= mInstructions[i + 1]->mSrc[0].mIntConst; + mInstructions[i + 0]->mDst = mInstructions[i + 1]->mDst; + mInstructions[i + 1]->mCode = IC_NONE; + mInstructions[i + 1]->mNumOperands = 0; + mInstructions[i + 1]->mDst.mTemp = -1; + changed = true; + } + else if ( + mInstructions[i + 0]->mCode == IC_BINARY_OPERATOR && mInstructions[i + 0]->mOperator == IA_OR && mInstructions[i + 0]->mSrc[0].mTemp < 0 && + mInstructions[i + 1]->mCode == IC_BINARY_OPERATOR && mInstructions[i + 1]->mOperator == IA_OR && mInstructions[i + 1]->mSrc[0].mTemp < 0 && + mInstructions[i + 1]->mSrc[1].mTemp == mInstructions[i + 0]->mDst.mTemp && mInstructions[i + 1]->mSrc[1].mFinal) + { + mInstructions[i + 0]->mSrc[0].mIntConst |= mInstructions[i + 1]->mSrc[0].mIntConst; + mInstructions[i + 0]->mDst = mInstructions[i + 1]->mDst; + mInstructions[i + 1]->mCode = IC_NONE; + mInstructions[i + 1]->mNumOperands = 0; + mInstructions[i + 1]->mDst.mTemp = -1; + changed = true; + } + else if ( + mInstructions[i + 0]->mCode == IC_BINARY_OPERATOR && (mInstructions[i + 0]->mOperator == IA_SUB || mInstructions[i + 0]->mOperator == IA_XOR) && + mInstructions[i + 0]->mSrc[0].mTemp >= 0 && mInstructions[i + 0]->mSrc[0].mTemp == mInstructions[i + 0]->mSrc[1].mTemp) + { + mInstructions[i + 0]->mCode = IC_CONSTANT; + mInstructions[i + 0]->mNumOperands = 0; + mInstructions[i + 0]->mConst.mType = mInstructions[i + 0]->mDst.mType; + mInstructions[i + 0]->mConst.mIntConst = 0; + mInstructions[i + 0]->mConst.mFloatConst = 0; + changed = true; + } +#endif + else if ( + mInstructions[i + 0]->mCode == IC_BINARY_OPERATOR && mInstructions[i + 0]->mOperator == IA_AND && mInstructions[i + 0]->mSrc[0].mTemp < 0 && ispow2(mInstructions[i + 0]->mSrc[0].mIntConst) && + mInstructions[i + 1]->mCode == IC_RELATIONAL_OPERATOR && (mInstructions[i + 1]->mOperator == IA_CMPEQ || mInstructions[i + 1]->mOperator == IA_CMPNE) && mInstructions[i + 1]->mSrc[0].mTemp < 0 && mInstructions[i + 0]->mSrc[0].mIntConst == mInstructions[i + 1]->mSrc[0].mIntConst && + mInstructions[i + 1]->mSrc[1].mTemp == mInstructions[i + 0]->mDst.mTemp) + { + mInstructions[i + 1]->mSrc[0].mIntConst = 0; + mInstructions[i + 1]->mOperator = InvertRelational(mInstructions[i + 1]->mOperator); + changed = true; + } + else if ( + mInstructions[i + 0]->mCode == IC_BINARY_OPERATOR && mInstructions[i + 0]->mOperator == IA_AND && mInstructions[i + 0]->mSrc[1].mTemp < 0 && ispow2(mInstructions[i + 0]->mSrc[1].mIntConst) && + mInstructions[i + 1]->mCode == IC_RELATIONAL_OPERATOR && (mInstructions[i + 1]->mOperator == IA_CMPEQ || mInstructions[i + 1]->mOperator == IA_CMPNE) && mInstructions[i + 1]->mSrc[1].mTemp < 0 && mInstructions[i + 0]->mSrc[1].mIntConst == mInstructions[i + 1]->mSrc[1].mIntConst && + mInstructions[i + 1]->mSrc[0].mTemp == mInstructions[i + 0]->mDst.mTemp) + { + mInstructions[i + 1]->mSrc[1].mIntConst = 0; + mInstructions[i + 1]->mOperator = InvertRelational(mInstructions[i + 1]->mOperator); + changed = true; + } + +#if 1 + else if ( + mInstructions[i + 0]->mCode == IC_BINARY_OPERATOR && mInstructions[i + 0]->mOperator == IA_SHR && mInstructions[i + 0]->mSrc[0].mTemp < 0 && + mInstructions[i + 1]->mCode == IC_CONVERSION_OPERATOR && mInstructions[i + 1]->mOperator == IA_EXT8TO16U && + mInstructions[i + 2]->mCode == IC_BINARY_OPERATOR && mInstructions[i + 2]->mOperator == IA_MUL && mInstructions[i + 2]->mSrc[0].mTemp < 0 && + mInstructions[i + 1]->mSrc[0].mTemp == mInstructions[i + 0]->mDst.mTemp && mInstructions[i + 1]->mSrc[0].mFinal && + mInstructions[i + 2]->mSrc[1].mTemp == mInstructions[i + 1]->mDst.mTemp && mInstructions[i + 2]->mSrc[1].mFinal && + (mInstructions[i + 2]->mSrc[0].mIntConst & 1) == 0) + { + + int64 shift = mInstructions[i + 0]->mSrc[0].mIntConst; + int64 mshift = 1; + while (!(mInstructions[i + 2]->mSrc[0].mIntConst & (1ULL << mshift))) + mshift++; + + mInstructions[i + 1]->mCode = IC_BINARY_OPERATOR; + mInstructions[i + 1]->mOperator = IA_AND; + mInstructions[i + 1]->mNumOperands = 2; + mInstructions[i + 1]->mSrc[0].mType = IT_INT16; + mInstructions[i + 1]->mSrc[1].mType = IT_INT16; + mInstructions[i + 1]->mSrc[1].mTemp = -1; + mInstructions[i + 1]->mSrc[1].mIntConst = 255; + + if (mshift < shift) + { + mInstructions[i + 0]->mDst.mRange.mMaxValue <<= mshift; + mInstructions[i + 0]->mSrc[0].mIntConst = shift - mshift; + + mInstructions[i + 1]->mSrc[0].mRange.mMaxValue = mInstructions[i + 0]->mDst.mRange.mMaxValue; + mInstructions[i + 1]->mDst.mRange.mMaxValue = mInstructions[i + 0]->mDst.mRange.mMaxValue; + mInstructions[i + 1]->mSrc[1].mIntConst = 255ULL >> shift << mshift; + mInstructions[i + 2]->mSrc[1].mRange.mMaxValue = mInstructions[i + 0]->mDst.mRange.mMaxValue; + mInstructions[i + 2]->mSrc[0].mIntConst >>= mshift; + } + else if (mshift >= shift) + { + mInstructions[i + 0]->mCode = IC_LOAD_TEMPORARY; + mInstructions[i + 0]->mSrc[0] = mInstructions[i + 0]->mSrc[1]; + mInstructions[i + 0]->mSrc[1].mTemp = -1; + assert(mInstructions[i + 0]->mSrc[0].mTemp >= 0); + + mInstructions[i + 1]->mSrc[1].mIntConst = 255ULL >> shift << shift; + mInstructions[i + 2]->mSrc[0].mIntConst >>= shift; + } + + changed = true; + } +#endif +#if 1 + else if ( + mInstructions[i + 0]->mCode == IC_BINARY_OPERATOR && mInstructions[i + 0]->mOperator == IA_SHR && mInstructions[i + 0]->mSrc[0].mTemp < 0 && + mInstructions[i + 1]->mCode == IC_BINARY_OPERATOR && mInstructions[i + 1]->mOperator == IA_SHL && mInstructions[i + 1]->mSrc[0].mTemp < 0 && + mInstructions[i + 1]->mSrc[1].mTemp == mInstructions[i + 0]->mDst.mTemp && mInstructions[i + 1]->mSrc[1].mFinal) + { + + int64 shift = mInstructions[i + 0]->mSrc[0].mIntConst; + if (shift & 7) + { + int64 mshift = mInstructions[i + 1]->mSrc[0].mIntConst; + + mInstructions[i + 0]->mOperator = IA_AND; + mInstructions[i + 0]->mSrc[0].mType = mInstructions[i + 0]->mSrc[1].mType; + + mInstructions[i + 0]->mSrc[0].mIntConst = (UnsignedTypeMax(mInstructions[i + 0]->mSrc[1].mType) >> shift) << shift; + mInstructions[i + 0]->mDst.mRange = mInstructions[i + 0]->mSrc[1].mRange; + mInstructions[i + 0]->mDst.mRange.LimitMax(mInstructions[i + 0]->mSrc[0].mIntConst); + mInstructions[i + 0]->mDst.mRange.mMinState = IntegerValueRange::S_BOUND; + mInstructions[i + 0]->mDst.mRange.mMinValue = 0; + mInstructions[i + 1]->mSrc[1].mRange = mInstructions[i + 0]->mDst.mRange; + + if (shift > mshift && mInstructions[i + 0]->mDst.mType > mInstructions[i + 1]->mSrc[1].mType) + { + mInstructions[i + 1]->mSrc[1].mType = mInstructions[i + 0]->mDst.mType; + mInstructions[i + 1]->mDst.mType = mInstructions[i + 0]->mDst.mType; + } + + if (shift > mshift) + { + mInstructions[i + 1]->mOperator = IA_SHR; + mInstructions[i + 1]->mSrc[0].mIntConst = shift - mshift; + } + else if (shift < mshift) + { + mInstructions[i + 1]->mOperator = IA_SHL; + mInstructions[i + 1]->mSrc[0].mIntConst = mshift - shift; + } + else + { + mInstructions[i + 0]->mDst = mInstructions[i + 1]->mDst; + mInstructions[i + 1]->mCode = IC_NONE; + mInstructions[i + 1]->mNumOperands = 0; + mInstructions[i + 1]->mDst.mTemp = -1; + } + + changed = true; + } + } +#endif +#if 1 + else if ( + mInstructions[i + 0]->mCode == IC_BINARY_OPERATOR && mInstructions[i + 0]->mOperator == IA_SHR && mInstructions[i + 0]->mSrc[0].mTemp < 0 && + mInstructions[i + 1]->mCode == IC_BINARY_OPERATOR && mInstructions[i + 1]->mOperator == IA_MUL && mInstructions[i + 1]->mSrc[0].mTemp < 0 && ispow2(mInstructions[i + 1]->mSrc[0].mIntConst) && + mInstructions[i + 1]->mSrc[1].mTemp == mInstructions[i + 0]->mDst.mTemp && mInstructions[i + 1]->mSrc[1].mFinal) + { + + int64 shift = mInstructions[i + 0]->mSrc[0].mIntConst; + if (shift & 7) + { + int64 mshift = binlog(mInstructions[i + 1]->mSrc[0].mIntConst); + + mInstructions[i + 0]->mOperator = IA_AND; + mInstructions[i + 0]->mSrc[0].mType = mInstructions[i + 0]->mSrc[1].mType; + + mInstructions[i + 0]->mSrc[0].mIntConst = (UnsignedTypeMax(mInstructions[i + 0]->mSrc[1].mType) >> shift) << shift; + mInstructions[i + 0]->mDst.mRange = mInstructions[i + 0]->mSrc[1].mRange; + mInstructions[i + 0]->mDst.mRange.LimitMax(mInstructions[i + 0]->mSrc[0].mIntConst); + mInstructions[i + 0]->mDst.mRange.mMinState = IntegerValueRange::S_BOUND; + mInstructions[i + 0]->mDst.mRange.mMinValue = 0; + mInstructions[i + 1]->mSrc[1].mRange = mInstructions[i + 0]->mDst.mRange; + + if (shift > mshift && mInstructions[i + 0]->mDst.mType > mInstructions[i + 1]->mSrc[1].mType) + { + mInstructions[i + 1]->mSrc[1].mType = mInstructions[i + 0]->mDst.mType; + mInstructions[i + 1]->mDst.mType = mInstructions[i + 0]->mDst.mType; + } + + if (shift > mshift) + { + mInstructions[i + 1]->mOperator = IA_SHR; + mInstructions[i + 1]->mSrc[0].mIntConst = shift - mshift; + } + else if (shift < mshift) + { + mInstructions[i + 1]->mOperator = IA_SHL; + mInstructions[i + 1]->mSrc[0].mIntConst = mshift - shift; + } + else + { + mInstructions[i + 0]->mDst = mInstructions[i + 1]->mDst; + mInstructions[i + 1]->mCode = IC_NONE; + mInstructions[i + 1]->mNumOperands = 0; + mInstructions[i + 1]->mDst.mTemp = -1; + } + + changed = true; + } + } +#endif +#if 1 + else if ( + mInstructions[i + 0]->mCode == IC_BINARY_OPERATOR && mInstructions[i + 0]->mOperator == IA_SHL && mInstructions[i + 0]->mSrc[0].mTemp < 0 && + mInstructions[i + 1]->mCode == IC_BINARY_OPERATOR && mInstructions[i + 1]->mOperator == IA_SHR && mInstructions[i + 1]->mSrc[0].mTemp < 0 && + mInstructions[i + 1]->mSrc[1].mTemp == mInstructions[i + 0]->mDst.mTemp && mInstructions[i + 1]->mSrc[1].mFinal) + { + int64 shift = mInstructions[i + 0]->mSrc[0].mIntConst; + if (shift & 7) + { + int64 mshift = mInstructions[i + 1]->mSrc[0].mIntConst; + + mInstructions[i + 0]->mOperator = IA_AND; + mInstructions[i + 0]->mSrc[0].mType = mInstructions[i + 1]->mSrc[0].mType; + + switch (mInstructions[i + 0]->mSrc[1].mType) + { + case IT_INT8: + mInstructions[i + 0]->mSrc[0].mIntConst = 0xffu >> shift; + break; + case IT_INT16: + mInstructions[i + 0]->mSrc[0].mIntConst = 0xffffu >> shift; + break; + case IT_INT32: + mInstructions[i + 0]->mSrc[0].mIntConst = 0xffffffffu >> shift; + break; + } + + if (shift > mshift && mInstructions[i + 0]->mDst.mType > mInstructions[i + 1]->mSrc[1].mType) + { + mInstructions[i + 1]->mSrc[1].mType = mInstructions[i + 0]->mDst.mType; + mInstructions[i + 1]->mDst.mType = mInstructions[i + 0]->mDst.mType; + } + + if (shift > mshift) + { + mInstructions[i + 1]->mOperator = IA_SHL; + mInstructions[i + 1]->mSrc[0].mIntConst = shift - mshift; + } + else if (shift < mshift) + { + mInstructions[i + 1]->mOperator = IA_SHR; + mInstructions[i + 1]->mSrc[0].mIntConst = mshift - shift; + } + else + { + mInstructions[i + 0]->mDst = mInstructions[i + 1]->mDst; + mInstructions[i + 1]->mCode = IC_NONE; + mInstructions[i + 1]->mNumOperands = 0; + mInstructions[i + 1]->mDst.mTemp = -1; + } + + changed = true; + } + } +#endif +#if 1 + else if ( + mInstructions[i + 0]->mCode == IC_BINARY_OPERATOR && mInstructions[i + 0]->mOperator == IA_SHR && mInstructions[i + 0]->mSrc[0].mTemp < 0 && + mInstructions[i + 1]->mCode == IC_BINARY_OPERATOR && mInstructions[i + 1]->mOperator == IA_AND && mInstructions[i + 1]->mSrc[0].mTemp < 0 && + mInstructions[i + 2]->mCode == IC_BINARY_OPERATOR && mInstructions[i + 2]->mOperator == IA_SHL && mInstructions[i + 2]->mSrc[0].mTemp < 0 && + mInstructions[i + 1]->mSrc[1].mTemp == mInstructions[i + 0]->mDst.mTemp && mInstructions[i + 1]->mSrc[1].mFinal && + mInstructions[i + 2]->mSrc[1].mTemp == mInstructions[i + 1]->mDst.mTemp && mInstructions[i + 2]->mSrc[1].mFinal) + { + int64 ishr = mInstructions[i + 0]->mSrc[0].mIntConst; + int64 iand = mInstructions[i + 1]->mSrc[0].mIntConst; + int64 ishl = mInstructions[i + 2]->mSrc[0].mIntConst; + + iand = ((iand & (UnsignedTypeMax(mInstructions[i + 2]->mDst.mType) >> ishl)) << ishr) & UnsignedTypeMax(mInstructions[i + 1]->mDst.mType); + + mInstructions[i + 0]->mOperator = IA_AND; + mInstructions[i + 0]->mSrc[0].mIntConst = iand; + mInstructions[i + 0]->mDst.mRange.Reset(); + + if (ishr > ishl) + { + mInstructions[i + 1]->mOperator = IA_SHR; + mInstructions[i + 1]->mSrc[0].mIntConst = ishr - ishl; + mInstructions[i + 1]->mSrc[1].mRange.Reset(); + mInstructions[i + 1]->mDst.mTemp = mInstructions[i + 2]->mDst.mTemp; + + mInstructions[i + 2]->mCode = IC_NONE; + mInstructions[i + 2]->mNumOperands = 0; + mInstructions[i + 2]->mDst.mTemp = -1; + } + else if (ishr < ishl) + { + mInstructions[i + 2]->mOperator = IA_SHL; + mInstructions[i + 2]->mSrc[0].mIntConst = ishl - ishr; + mInstructions[i + 2]->mSrc[1].mTemp = mInstructions[i + 0]->mDst.mTemp; + mInstructions[i + 2]->mSrc[1].mRange.Reset(); + mInstructions[i + 2]->mSrc[1].mType = mInstructions[i + 2]->mDst.mType; + + mInstructions[i + 1]->mCode = IC_NONE; + mInstructions[i + 1]->mNumOperands = 0; + mInstructions[i + 1]->mDst.mTemp = -1; + } + else + { + mInstructions[i + 0]->mDst = mInstructions[i + 2]->mDst; + mInstructions[i + 2]->mCode = IC_NONE; + mInstructions[i + 2]->mNumOperands = 0; + mInstructions[i + 2]->mDst.mTemp = -1; + + mInstructions[i + 1]->mCode = IC_NONE; + mInstructions[i + 1]->mNumOperands = 0; + mInstructions[i + 1]->mDst.mTemp = -1; + } + + + changed = true; + } +#endif +#if 1 + else if ( + mInstructions[i + 0]->mCode == IC_LOAD_TEMPORARY && + mInstructions[i + 1]->mCode == IC_LEA && mInstructions[i + 1]->mSrc[0].mTemp == mInstructions[i + 0]->mDst.mTemp && mInstructions[i + 1]->mSrc[0].mFinal + ) + { + mInstructions[i + 1]->mSrc[0].ForwardTemp(mInstructions[i + 0]->mSrc[0]); + mInstructions[i + 0]->mCode = IC_NONE; mInstructions[i + 0]->mNumOperands = 0; + mInstructions[i + 0]->mDst.mTemp = -1; + changed = true; + } +#endif +#if 1 + else if ( + mInstructions[i + 0]->mCode == IC_LOAD_TEMPORARY && + mInstructions[i + 1]->mCode == IC_LOAD && mInstructions[i + 1]->mSrc[0].mTemp == mInstructions[i + 0]->mDst.mTemp && mInstructions[i + 1]->mSrc[0].mFinal + ) + { + mInstructions[i + 1]->mSrc[0].ForwardTemp(mInstructions[i + 0]->mSrc[0]); + mInstructions[i + 0]->mCode = IC_NONE; mInstructions[i + 0]->mNumOperands = 0; + mInstructions[i + 0]->mDst.mTemp = -1; + changed = true; + } +#endif +#if 1 + else if ( + mInstructions[i + 0]->mCode == IC_RELATIONAL_OPERATOR && + mInstructions[i + 1]->mCode == IC_RELATIONAL_OPERATOR && + (mInstructions[i + 1]->mSrc[0].mTemp == mInstructions[i + 0]->mDst.mTemp && mInstructions[i + 1]->mSrc[0].mFinal && mInstructions[i + 1]->mSrc[1].mTemp < 0 || + mInstructions[i + 1]->mSrc[1].mTemp == mInstructions[i + 0]->mDst.mTemp && mInstructions[i + 1]->mSrc[1].mFinal && mInstructions[i + 1]->mSrc[0].mTemp < 0) + ) + { + int64 v = mInstructions[i + 1]->mSrc[0].mIntConst; + InterOperator op = mInstructions[i + 1]->mOperator; + if (mInstructions[i + 1]->mSrc[0].mTemp >= 0) + { + v = mInstructions[i + 1]->mSrc[1].mIntConst; + op = MirrorRelational(op); + } + + bool flip = false, istrue = false, isfalse = true; + + switch (op) + { + case IA_CMPEQ: + flip = v == 0; + istrue = false; + isfalse = (v != 0 && v != 1); + break; + case IA_CMPNE: + flip = v != 0; + isfalse = false; + istrue = (v != 0 && v != 1); + break; + case IA_CMPGEU: + case IA_CMPGES: + istrue = v <= 0; + isfalse = v > 1; + break; + case IA_CMPGU: + case IA_CMPGS: + istrue = v < 0; + isfalse = v >= 1; + break; + case IA_CMPLEU: + case IA_CMPLES: + flip = true; + isfalse = v < 0; + istrue = v >= 1; + break; + case IA_CMPLU: + case IA_CMPLS: + flip = true; + isfalse = v <= 0; + istrue = v > 1; + break; + } + + if (istrue) + { + mInstructions[i + 1]->mCode = IC_CONSTANT; + mInstructions[i + 1]->mConst.mType = IT_BOOL; + mInstructions[i + 1]->mConst.mIntConst = 1; + mInstructions[i + 1]->mSrc[0].mTemp = -1; + mInstructions[i + 1]->mSrc[0].mType = IT_NONE; + mInstructions[i + 1]->mSrc[1].mTemp = -1; + mInstructions[i + 1]->mSrc[1].mType = IT_NONE; + mInstructions[i + 1]->mNumOperands = 0; + } + else if (isfalse) + { + mInstructions[i + 1]->mCode = IC_CONSTANT; + mInstructions[i + 1]->mConst.mType = IT_BOOL; + mInstructions[i + 1]->mConst.mIntConst = 0; + mInstructions[i + 1]->mSrc[0].mTemp = -1; + mInstructions[i + 1]->mSrc[0].mType = IT_NONE; + mInstructions[i + 1]->mSrc[1].mTemp = -1; + mInstructions[i + 1]->mSrc[1].mType = IT_NONE; + mInstructions[i + 1]->mNumOperands = 0; + } + else + { + mInstructions[i + 0]->mDst = mInstructions[i + 1]->mDst; + mInstructions[i + 1]->mCode = IC_NONE; mInstructions[i + 1]->mNumOperands = 0; + mInstructions[i + 1]->mDst.mTemp = -1; + if (flip) + mInstructions[i + 0]->mOperator = InvertRelational(mInstructions[i + 0]->mOperator); + } + changed = true; + } +#endif +#if 1 + else if ( + mInstructions[i + 1]->mCode == IC_LOAD_TEMPORARY && mExitRequiredTemps[mInstructions[i + 1]->mDst.mTemp] && + (!mExitRequiredTemps[mInstructions[i + 1]->mSrc[0].mTemp] || + (mEntryRequiredTemps[mInstructions[i + 1]->mDst.mTemp] && !mEntryRequiredTemps[mInstructions[i + 1]->mSrc[0].mTemp])) && + mInstructions[i + 0]->mDst.mTemp == mInstructions[i + 1]->mSrc[0].mTemp) + { + mInstructions[i + 0]->mDst.mTemp = mInstructions[i + 1]->mDst.mTemp; + mInstructions[i + 1]->mDst.mTemp = mInstructions[i + 1]->mSrc[0].mTemp; + mInstructions[i + 1]->mSrc[0].mTemp = mInstructions[i + 0]->mDst.mTemp; + mInstructions[i + 1]->mSrc[0].mFinal = false; + mInstructions[i + 0]->mSingleAssignment = mInstructions[i + 1]->mSingleAssignment; + changed = true; + } +#endif + else if ( + mInstructions[i + 0]->mDst.mTemp >= 0 && + mInstructions[i + 1]->mCode == IC_BINARY_OPERATOR && IsCommutative(mInstructions[i + 1]->mOperator) && + mInstructions[i + 0]->mDst.mTemp == mInstructions[i + 1]->mSrc[0].mTemp && + mInstructions[i + 0]->mDst.mTemp != mInstructions[i + 1]->mSrc[1].mTemp) + { + InterOperand io = mInstructions[i + 1]->mSrc[1]; + mInstructions[i + 1]->mSrc[1] = mInstructions[i + 1]->mSrc[0]; + mInstructions[i + 1]->mSrc[0] = io; + changed = true; + } +#if 1 + else if ( + mInstructions[i + 0]->mCode == IC_BINARY_OPERATOR && mInstructions[i + 0]->mOperator == IA_ADD && + mInstructions[i + 1]->mCode == IC_LEA && mInstructions[i + 1]->mSrc[0].mTemp == mInstructions[i + 0]->mDst.mTemp && mInstructions[i + 1]->mSrc[0].mFinal && + mInstructions[i + 0]->mSrc[1].IsUByte() && !mInstructions[i + 0]->mSrc[0].IsUByte() && + mInstructions[i + 0]->mDst.mTemp != mInstructions[i + 0]->mSrc[1].mTemp) + { + mInstructions[i + 1]->mSrc[0] = mInstructions[i + 0]->mSrc[1]; + + mInstructions[i + 0]->mCode = IC_LEA; + mInstructions[i + 0]->mSrc[1] = mInstructions[i + 1]->mSrc[1]; + mInstructions[i + 0]->mDst.mType = IT_POINTER; + mInstructions[i + 0]->mDst.mRange.Reset(); + + mInstructions[i + 1]->mSrc[1] = mInstructions[i + 0]->mDst; + mInstructions[i + 1]->mSrc[1].mMemory = IM_INDIRECT; + + changed = true; + } + else if ( + mInstructions[i + 0]->mCode == IC_BINARY_OPERATOR && mInstructions[i + 0]->mOperator == IA_ADD && + mInstructions[i + 1]->mCode == IC_LEA && mInstructions[i + 1]->mSrc[0].mTemp == mInstructions[i + 0]->mDst.mTemp && mInstructions[i + 1]->mSrc[0].mFinal && + mInstructions[i + 0]->mSrc[0].IsUByte() && !mInstructions[i + 0]->mSrc[1].IsUByte() && + mInstructions[i + 0]->mDst.mTemp != mInstructions[i + 0]->mSrc[0].mTemp) + { + mInstructions[i + 1]->mSrc[0] = mInstructions[i + 0]->mSrc[0]; + + mInstructions[i + 0]->mCode = IC_LEA; + mInstructions[i + 0]->mSrc[0] = mInstructions[i + 0]->mSrc[1]; + mInstructions[i + 0]->mSrc[1] = mInstructions[i + 1]->mSrc[1]; + mInstructions[i + 0]->mDst.mType = IT_POINTER; + mInstructions[i + 0]->mDst.mRange.Reset(); + + mInstructions[i + 1]->mSrc[1] = mInstructions[i + 0]->mDst; + mInstructions[i + 1]->mSrc[1].mMemory = IM_INDIRECT; + changed = true; + } +#endif +#if 1 + else if ( + mInstructions[i + 0]->mCode == IC_BINARY_OPERATOR && mInstructions[i + 0]->mOperator == IA_ADD && mInstructions[i + 0]->mSrc[0].mTemp < 0 && mInstructions[i + 0]->mSrc[0].mIntConst >= 0 && mInstructions[i + 0]->mSrc[0].mIntConst <= 16 && + mInstructions[i + 1]->mCode == IC_LEA && mInstructions[i + 1]->mSrc[0].mTemp == mInstructions[i + 0]->mDst.mTemp && mInstructions[i + 1]->mSrc[0].mFinal) + { + mInstructions[i + 1]->mSrc[0] = mInstructions[i + 0]->mSrc[0]; + + mInstructions[i + 0]->mCode = IC_LEA; + mInstructions[i + 0]->mSrc[0] = mInstructions[i + 0]->mSrc[1]; + mInstructions[i + 0]->mSrc[1] = mInstructions[i + 1]->mSrc[1]; + mInstructions[i + 0]->mDst.mType = IT_POINTER; + mInstructions[i + 0]->mDst.mRange.Reset(); + + mInstructions[i + 1]->mSrc[1] = mInstructions[i + 0]->mDst; + mInstructions[i + 1]->mSrc[1].mMemory = IM_INDIRECT; + changed = true; + } +#endif +#if 1 + else if ( + mInstructions[i + 0]->mCode == IC_BINARY_OPERATOR && mInstructions[i + 0]->mOperator == IA_SUB && mInstructions[i + 0]->mSrc[0].mTemp < 0 && + mInstructions[i + 1]->mCode == IC_LEA && mInstructions[i + 1]->mSrc[1].mTemp < 0 && mInstructions[i + 1]->mSrc[0].mTemp == mInstructions[i + 0]->mDst.mTemp && mInstructions[i + 1]->mSrc[0].mFinal) + { + mInstructions[i + 1]->mSrc[0] = mInstructions[i + 0]->mSrc[1]; + mInstructions[i + 1]->mSrc[1].mIntConst -= mInstructions[i + 0]->mSrc[0].mIntConst; + + mInstructions[i + 0]->mCode = IC_NONE; mInstructions[i + 0]->mNumOperands = 0; + mInstructions[i + 0]->mDst.mTemp = -1; + changed = true; + } +#endif +#if 1 + else if ( + mInstructions[i + 0]->mCode == IC_LEA && mInstructions[i + 0]->mSrc[0].mTemp < 0 && + mInstructions[i + 1]->mCode == IC_LEA && mInstructions[i + 1]->mSrc[0].mTemp < 0 && + mInstructions[i + 1]->mSrc[1].mTemp == mInstructions[i + 0]->mDst.mTemp && mInstructions[i + 1]->mSrc[1].mFinal) + { + mInstructions[i + 0]->mDst = mInstructions[i + 1]->mDst; + mInstructions[i + 0]->mSrc[0].mIntConst += mInstructions[i + 1]->mSrc[0].mIntConst; + + mInstructions[i + 1]->mCode = IC_NONE; mInstructions[i + 1]->mNumOperands = 0; + mInstructions[i + 1]->mDst.mTemp = -1; + changed = true; + } +#endif +#if 1 + else if ( + mInstructions[i + 0]->mCode == IC_LEA && mInstructions[i + 0]->mSrc[0].mTemp >= 0 && + mInstructions[i + 1]->mCode == IC_LEA && mInstructions[i + 1]->mSrc[0].mTemp >= 0 && + mInstructions[i + 1]->mSrc[1].mTemp == mInstructions[i + 0]->mDst.mTemp && + mInstructions[i + 1]->mSrc[1].mFinal && + mInstructions[i + 0]->mSrc[0].IsUByte() && mInstructions[i + 1]->mSrc[0].IsUByte() && mInstructions[i + 0]->mSrc[0].mRange.mMaxValue + mInstructions[i + 1]->mSrc[0].mRange.mMaxValue < 256) + { + mInstructions[i + 1]->mSrc[1] = mInstructions[i + 0]->mSrc[1]; + + mInstructions[i + 0]->mCode = IC_BINARY_OPERATOR; + mInstructions[i + 0]->mOperator = IA_ADD; + mInstructions[i + 0]->mDst.mType = IT_INT16; + mInstructions[i + 0]->mSrc[1] = mInstructions[i + 1]->mSrc[0]; + mInstructions[i + 0]->mDst.mRange = mInstructions[i + 0]->mSrc[0].mRange; + mInstructions[i + 0]->mDst.mRange.mMaxValue += mInstructions[i + 0]->mSrc[1].mRange.mMaxValue; + + mInstructions[i + 1]->mSrc[0] = mInstructions[i + 0]->mDst; + changed = true; + } +#endif + else if ( + mInstructions[i + 0]->mCode == IC_BINARY_OPERATOR && mInstructions[i + 0]->mOperator == IA_ADD && mInstructions[i + 0]->mSrc[1].mTemp < 0 && mInstructions[i + 0]->mSrc[0].mType == IT_INT16 && + mInstructions[i + 1]->mCode == IC_CONVERSION_OPERATOR && mInstructions[i + 1]->mOperator == IA_EXT8TO16U && + mInstructions[i + 1]->mSrc[0].mTemp == mInstructions[i + 0]->mDst.mTemp && mInstructions[i + 1]->mSrc[0].mFinal && + mInstructions[i + 1]->mSrc[0].IsUByte() && + mInstructions[i + 2]->mCode == IC_LEA && mInstructions[i + 2]->mSrc[0].mTemp == mInstructions[i + 1]->mDst.mTemp && mInstructions[i + 2]->mSrc[0].mFinal && + mInstructions[i + 2]->mSrc[1].mTemp < 0) + { + mInstructions[i + 2]->mSrc[0] = mInstructions[i + 0]->mSrc[0]; + mInstructions[i + 2]->mSrc[1].mIntConst += mInstructions[i + 0]->mSrc[1].mIntConst; + + mInstructions[i + 0]->mCode = IC_NONE; mInstructions[i + 0]->mNumOperands = 0; + mInstructions[i + 0]->mDst.mTemp = -1; + mInstructions[i + 1]->mCode = IC_NONE; mInstructions[i + 1]->mNumOperands = 0; + mInstructions[i + 1]->mDst.mTemp = -1; + changed = true; + } + else if ( + mInstructions[i + 0]->mCode == IC_BINARY_OPERATOR && mInstructions[i + 0]->mOperator == IA_ADD && mInstructions[i + 0]->mSrc[0].mTemp < 0 && mInstructions[i + 0]->mSrc[1].mType == IT_INT16 && + mInstructions[i + 1]->mCode == IC_CONVERSION_OPERATOR && mInstructions[i + 1]->mOperator == IA_EXT8TO16U && + mInstructions[i + 1]->mSrc[0].mTemp == mInstructions[i + 0]->mDst.mTemp && mInstructions[i + 1]->mSrc[0].mFinal && + mInstructions[i + 1]->mSrc[0].IsUByte() && + mInstructions[i + 2]->mCode == IC_LEA && mInstructions[i + 2]->mSrc[0].mTemp == mInstructions[i + 1]->mDst.mTemp && mInstructions[i + 2]->mSrc[0].mFinal && + mInstructions[i + 2]->mSrc[1].mTemp < 0) + { + mInstructions[i + 2]->mSrc[0] = mInstructions[i + 0]->mSrc[1]; + mInstructions[i + 2]->mSrc[1].mIntConst += mInstructions[i + 0]->mSrc[0].mIntConst; + + mInstructions[i + 0]->mCode = IC_NONE; mInstructions[i + 0]->mNumOperands = 0; + mInstructions[i + 0]->mDst.mTemp = -1; + mInstructions[i + 1]->mCode = IC_NONE; mInstructions[i + 1]->mNumOperands = 0; + mInstructions[i + 1]->mDst.mTemp = -1; + changed = true; + } + else if ( + mInstructions[i + 0]->mCode == IC_BINARY_OPERATOR && mInstructions[i + 0]->mOperator == IA_SUB && mInstructions[i + 0]->mSrc[0].mTemp < 0 && mInstructions[i + 0]->mSrc[1].mType == IT_INT16 && + mInstructions[i + 1]->mCode == IC_CONVERSION_OPERATOR && mInstructions[i + 1]->mOperator == IA_EXT8TO16U && + mInstructions[i + 1]->mSrc[0].mTemp == mInstructions[i + 0]->mDst.mTemp && mInstructions[i + 1]->mSrc[0].mFinal && + mInstructions[i + 1]->mSrc[0].IsUByte() && + mInstructions[i + 2]->mCode == IC_LEA && mInstructions[i + 2]->mSrc[0].mTemp == mInstructions[i + 1]->mDst.mTemp && mInstructions[i + 2]->mSrc[0].mFinal && + mInstructions[i + 2]->mSrc[1].mTemp < 0) + { + mInstructions[i + 2]->mSrc[0] = mInstructions[i + 0]->mSrc[1]; + mInstructions[i + 2]->mSrc[1].mIntConst -= mInstructions[i + 0]->mSrc[0].mIntConst; + + mInstructions[i + 0]->mCode = IC_NONE; mInstructions[i + 0]->mNumOperands = 0; + mInstructions[i + 0]->mDst.mTemp = -1; + mInstructions[i + 1]->mCode = IC_NONE; mInstructions[i + 1]->mNumOperands = 0; + mInstructions[i + 1]->mDst.mTemp = -1; + changed = true; + } + else if ( + mInstructions[i + 0]->mCode == IC_BINARY_OPERATOR && mInstructions[i + 0]->mOperator == IA_ADD && + mInstructions[i + 0]->mSrc[0].mTemp < 0 && mInstructions[i + 0]->mSrc[1].mTemp >= 0 && + mInstructions[i + 1]->mCode == IC_BINARY_OPERATOR && mInstructions[i + 1]->mOperator == IA_ADD && + mInstructions[i + 1]->mSrc[1].mTemp == mInstructions[i + 0]->mDst.mTemp && mInstructions[i + 1]->mSrc[0].mTemp >= 0 && + mInstructions[i + 2]->mCode == IC_LEA && mInstructions[i + 2]->mSrc[0].mTemp == mInstructions[i + 1]->mDst.mTemp && mInstructions[i + 2]->mSrc[0].mFinal && + mInstructions[i + 2]->mSrc[1].mTemp < 0 && + mInstructions[i + 0]->mDst.mTemp != mInstructions[i + 0]->mSrc[1].mTemp) + { + mInstructions[i + 2]->mSrc[1].mIntConst += mInstructions[i + 0]->mSrc[0].mIntConst; + mInstructions[i + 1]->mSrc[1] = mInstructions[i + 0]->mSrc[1]; + mInstructions[i + 1]->mDst.mRange.Reset(); + mInstructions[i + 0]->mSrc[1].mFinal = false; + mInstructions[i + 1]->mSrc[0].mFinal = false; + changed = true; + } + else if ( + mInstructions[i + 0]->mCode == IC_BINARY_OPERATOR && mInstructions[i + 0]->mOperator == IA_ADD && + mInstructions[i + 0]->mSrc[0].mTemp < 0 && mInstructions[i + 0]->mSrc[1].mTemp >= 0 && + + mInstructions[i + 1]->mCode == IC_BINARY_OPERATOR && mInstructions[i + 1]->mOperator == IA_ADD && + mInstructions[i + 1]->mSrc[0].mTemp < 0 && mInstructions[i + 1]->mSrc[1].mTemp >= 0 && + + mInstructions[i + 2]->mCode == IC_BINARY_OPERATOR && mInstructions[i + 2]->mOperator == IA_ADD && + mInstructions[i + 2]->mSrc[0].mTemp == mInstructions[i + 1]->mDst.mTemp && + mInstructions[i + 2]->mSrc[1].mTemp == mInstructions[i + 0]->mDst.mTemp && + + // !mInstructions[i + 1]->ReferencesTemp(mInstructions[i + 0]->mDst.mTemp) && + (mInstructions[i + 2]->mSrc[0].mFinal || mInstructions[i + 2]->mSrc[1].mFinal)) + { + if (mInstructions[i + 2]->mSrc[1].mFinal) + { + mInstructions[i + 0]->mSrc[0].mIntConst += mInstructions[i + 1]->mSrc[0].mIntConst; + mInstructions[i + 1]->mSrc[1].mFinal = false; + mInstructions[i + 2]->mSrc[0] = mInstructions[i + 1]->mSrc[1]; + mInstructions[i + 0]->mDst.mRange.Reset(); + mInstructions[i + 2]->mDst.mRange.Reset(); + } + else + { + mInstructions[i + 1]->mSrc[1].mIntConst += mInstructions[i + 0]->mSrc[0].mIntConst; + mInstructions[i + 0]->mSrc[1].mFinal = false; + mInstructions[i + 2]->mSrc[0] = mInstructions[i + 0]->mSrc[1]; + + mInstructions[i + 1]->mDst.mRange.Reset(); + mInstructions[i + 2]->mDst.mRange.Reset(); + } + + changed = true; + } + + else if ( + mInstructions[i + 0]->mCode == IC_LEA && mInstructions[i + 0]->mSrc[1].mMemory == IM_GLOBAL && + mInstructions[i + 1]->mCode == IC_LEA && mInstructions[i + 1]->mSrc[1].mTemp == mInstructions[i + 0]->mDst.mTemp && mInstructions[i + 1]->mSrc[1].mFinal && + mInstructions[i + 0]->mSrc[0].IsUByte() && mInstructions[i + 1]->mSrc[0].IsUByte() && mInstructions[i + 0]->mSrc[0].mRange.mMaxValue + mInstructions[i + 1]->mSrc[0].mRange.mMaxValue < 252) + { + mInstructions[i + 1]->mSrc[1] = mInstructions[i + 0]->mSrc[1]; + + mInstructions[i + 0]->mCode = IC_BINARY_OPERATOR; + mInstructions[i + 0]->mOperator = IA_ADD; + mInstructions[i + 0]->mSrc[1] = mInstructions[i + 1]->mSrc[0]; + mInstructions[i + 0]->mDst.mType = IT_INT16; + mInstructions[i + 0]->mDst.mRange.mMaxState = IntegerValueRange::S_BOUND; + mInstructions[i + 0]->mDst.mRange.mMaxValue = mInstructions[i + 0]->mSrc[1].mRange.mMaxValue + mInstructions[i + 0]->mSrc[0].mRange.mMaxValue; + mInstructions[i + 0]->mDst.mRange.mMinState = IntegerValueRange::S_BOUND; + mInstructions[i + 0]->mDst.mRange.mMinValue = 0; + + mInstructions[i + 1]->mSrc[0] = mInstructions[i + 0]->mDst; + changed = true; + } + else if ( + mInstructions[i + 0]->mCode == IC_LEA && mInstructions[i + 0]->mSrc[0].mTemp < 0 && mInstructions[i + 0]->mSrc[1].mTemp >= 0 && + mInstructions[i + 1]->mCode == IC_LOAD && mInstructions[i + 1]->mSrc[0].mTemp == mInstructions[i + 0]->mDst.mTemp && mInstructions[i + 1]->mSrc[0].mFinal && + mInstructions[i + 1]->mSrc[0].mIntConst + mInstructions[i + 0]->mSrc[0].mIntConst < 256) + { + mInstructions[i + 1]->mSrc[0].mTemp = mInstructions[i + 0]->mSrc[1].mTemp; + mInstructions[i + 1]->mSrc[0].mRange = mInstructions[i + 0]->mSrc[1].mRange; + mInstructions[i + 1]->mSrc[0].mIntConst += mInstructions[i + 0]->mSrc[0].mIntConst; + mInstructions[i + 1]->mSrc[0].mFinal = mInstructions[i + 0]->mSrc[1].mFinal; + mInstructions[i + 0]->mCode = IC_NONE; + mInstructions[i + 0]->mNumOperands = 0; + mInstructions[i + 0]->mDst.mTemp = -1; + changed = true; + } + else if ( + mInstructions[i + 0]->mCode == IC_LEA && mInstructions[i + 0]->mSrc[1].mTemp < 0 && + mInstructions[i + 1]->mCode == IC_STORE && mInstructions[i + 1]->mSrc[1].mTemp == mInstructions[i + 0]->mDst.mTemp && mInstructions[i + 1]->mSrc[1].mFinal && + mInstructions[i + 1]->mSrc[1].mIntConst != 0) + { + mInstructions[i + 0]->mSrc[1].mIntConst += mInstructions[i + 1]->mSrc[1].mIntConst; + mInstructions[i + 1]->mSrc[1].mIntConst = 0; + changed = true; + } +#if 1 + else if ( + mInstructions[i + 0]->mCode == IC_LEA && mInstructions[i + 0]->mSrc[1].mMemory == IM_GLOBAL && + mInstructions[i + 1]->mCode == IC_LEA && mInstructions[i + 1]->mSrc[1].mTemp == mInstructions[i + 0]->mDst.mTemp && mInstructions[i + 1]->mSrc[1].mFinal && + mInstructions[i + 1]->mSrc[0].mRange.mMaxState == IntegerValueRange::S_BOUND && !mInstructions[i + 1]->mSrc[0].IsUByte() && + mInstructions[i + 0]->mSrc[0].mRange.mMaxState == IntegerValueRange::S_BOUND && !mInstructions[i + 0]->mSrc[0].IsUByte()) + { + mInstructions[i + 1]->mSrc[1] = mInstructions[i + 0]->mSrc[1]; + + mInstructions[i + 0]->mCode = IC_BINARY_OPERATOR; + mInstructions[i + 0]->mOperator = IA_ADD; + mInstructions[i + 0]->mSrc[1] = mInstructions[i + 1]->mSrc[0]; + mInstructions[i + 0]->mDst.mType = IT_INT16; + mInstructions[i + 0]->mDst.mRange.mMaxState = IntegerValueRange::S_BOUND; + mInstructions[i + 0]->mDst.mRange.mMaxValue = mInstructions[i + 0]->mSrc[1].mRange.mMaxValue + mInstructions[i + 0]->mSrc[0].mRange.mMaxValue; + mInstructions[i + 0]->mDst.mRange.mMinState = IntegerValueRange::S_BOUND; + mInstructions[i + 0]->mDst.mRange.mMinValue = 0; + + mInstructions[i + 1]->mSrc[0] = mInstructions[i + 0]->mDst; + changed = true; + } +#endif + else if ( + mInstructions[i + 0]->mCode == IC_BINARY_OPERATOR && mInstructions[i + 0]->mOperator == IA_ADD && + mInstructions[i + 0]->mSrc[0].mTemp < 0 && + mInstructions[i + 1]->mCode == IC_BINARY_OPERATOR && mInstructions[i + 1]->mOperator == IA_SHL && + mInstructions[i + 1]->mSrc[0].mTemp < 0 && + mInstructions[i + 1]->mSrc[1].mTemp == mInstructions[i + 0]->mDst.mTemp && mInstructions[i + 1]->mSrc[1].mFinal && + mInstructions[i + 2]->mCode == IC_LEA && + mInstructions[i + 2]->mSrc[0].mTemp == mInstructions[i + 1]->mDst.mTemp && mInstructions[i + 2]->mSrc[0].mFinal && + mInstructions[i + 2]->mSrc[1].mTemp < 0) + { + mInstructions[i + 1]->mSrc[1] = mInstructions[i + 0]->mSrc[1]; + mInstructions[i + 2]->mSrc[1].mIntConst += mInstructions[i + 0]->mSrc[0].mIntConst << mInstructions[i + 1]->mSrc[0].mIntConst; + mInstructions[i + 2]->mSrc[0].mRange.AddConstValue(IT_INT16, - (mInstructions[i + 0]->mSrc[0].mIntConst << mInstructions[i + 1]->mSrc[0].mIntConst)); + + mInstructions[i + 0]->mCode = IC_NONE; mInstructions[i + 0]->mNumOperands = 0; + mInstructions[i + 0]->mDst.mTemp = -1; + changed = true; + } + else if ( + mInstructions[i + 0]->mCode == IC_BINARY_OPERATOR && mInstructions[i + 0]->mOperator == IA_ADD && mInstructions[i + 0]->mSrc[0].mTemp < 0 && mInstructions[i + 0]->mDst.mType == IT_INT16 && + mInstructions[i + 1]->mCode == IC_RELATIONAL_OPERATOR && mInstructions[i + 1]->mSrc[1].mTemp == mInstructions[i + 0]->mDst.mTemp && mInstructions[i + 1]->mSrc[1].mFinal && mInstructions[i + 1]->mSrc[0].mTemp < 0 && + mInstructions[i + 0]->mDst.mIntConst >= 0 && + mInstructions[i + 0]->mSrc[1].mRange.mMaxState == IntegerValueRange::S_BOUND && mInstructions[i + 0]->mSrc[1].mRange.mMaxValue + mInstructions[i + 0]->mSrc[0].mIntConst < 32767 && + (IsSignedRelational(mInstructions[i + 1]->mOperator) || mInstructions[i + 0]->mSrc[1].mRange.mMinState == IntegerValueRange::S_BOUND && mInstructions[i + 0]->mSrc[1].mRange.mMinValue >= 0) && + mInstructions[i + 1]->mSrc[0].mIntConst - mInstructions[i + 0]->mSrc[0].mIntConst >= (IsSignedRelational(mInstructions[i + 1]->mOperator) ? 0 : -32768) && + mInstructions[i + 1]->mSrc[0].mIntConst - mInstructions[i + 0]->mSrc[0].mIntConst <= 32767) + { + mInstructions[i + 1]->mSrc[0].mIntConst -= mInstructions[i + 0]->mSrc[0].mIntConst; + mInstructions[i + 1]->mSrc[1] = mInstructions[i + 0]->mSrc[1]; + mInstructions[i + 0]->mSrc[1].mFinal = false; + changed = true; + } + else if ( + mInstructions[i + 0]->mCode == IC_BINARY_OPERATOR && mInstructions[i + 0]->mOperator == IA_SUB && mInstructions[i + 0]->mSrc[0].mTemp < 0 && mInstructions[i + 0]->mDst.mType == IT_INT16 && + mInstructions[i + 0]->mDst.mIntConst >= 0 && + mInstructions[i + 0]->mSrc[1].mRange.mMinState == IntegerValueRange::S_BOUND && mInstructions[i + 0]->mSrc[1].mRange.mMinValue - mInstructions[i + 0]->mSrc[0].mIntConst >= (IsSignedRelational(mInstructions[i + 1]->mOperator) ? -32768 : 0) && + mInstructions[i + 1]->mCode == IC_RELATIONAL_OPERATOR && mInstructions[i + 1]->mSrc[1].mTemp == mInstructions[i + 0]->mDst.mTemp && mInstructions[i + 1]->mSrc[1].mFinal && mInstructions[i + 1]->mSrc[0].mTemp < 0 && + mInstructions[i + 1]->mSrc[0].mIntConst + mInstructions[i + 0]->mSrc[0].mIntConst >= (IsSignedRelational(mInstructions[i + 1]->mOperator) ? -32768 : 0) && + mInstructions[i + 1]->mSrc[0].mIntConst + mInstructions[i + 0]->mSrc[0].mIntConst <= 32767) + { + mInstructions[i + 1]->mSrc[0].mIntConst += mInstructions[i + 0]->mSrc[0].mIntConst; + mInstructions[i + 1]->mSrc[1] = mInstructions[i + 0]->mSrc[1]; + mInstructions[i + 0]->mSrc[1].mFinal = false; + changed = true; + } + else if ( + mInstructions[i + 0]->mCode == IC_LEA && + mInstructions[i + 1]->mCode == IC_BINARY_OPERATOR && + mInstructions[i + 2]->mCode == IC_LOAD && + mInstructions[i + 2]->mSrc[0].mTemp == mInstructions[i + 0]->mDst.mTemp && mInstructions[i + 2]->mSrc[0].mFinal && + CanSwapInstructions(mInstructions[i + 1], mInstructions[i + 2])) + { + InterInstruction* ins = mInstructions[i + 2]; + mInstructions[i + 2] = mInstructions[i + 1]; + mInstructions[i + 1] = ins; + changed = true; + } + else if ( + mInstructions[i + 0]->mCode == IC_STORE && mInstructions[i + 1]->mCode == IC_STORE && + SameMemSegment(mInstructions[i + 1]->mSrc[1], mInstructions[i + 0]->mSrc[1]) && + !mInstructions[i + 0]->mVolatile && !mInstructions[i + 1]->mVolatile) + { + mInstructions[i + 0]->mCode = IC_NONE; + mInstructions[i + 0]->mNumOperands = 0; + mInstructions[i + 0]->mDst.mTemp = -1; + changed = true; + } + else if ( + mInstructions[i + 0]->mCode == IC_LEA && + mInstructions[i + 0]->mSrc[0].mTemp >= 0 && mInstructions[i + 0]->mSrc[1].mTemp >= 0 && + + mInstructions[i + 1]->mCode == IC_LOAD && + mInstructions[i + 1]->mSrc[0].mTemp == mInstructions[i + 0]->mDst.mTemp && mInstructions[i + 1]->mSrc[0].mFinal && + + mInstructions[i + 2]->mCode == IC_BINARY_OPERATOR && mInstructions[i + 2]->mOperator == IA_ADD && + mInstructions[i + 2]->mSrc[1].mTemp == mInstructions[i + 2]->mDst.mTemp && + mInstructions[i + 2]->mSrc[1].mTemp == mInstructions[i + 0]->mSrc[0].mTemp && + mInstructions[i + 2]->mSrc[0].mTemp < 0 && + mInstructions[i + 2]->mSrc[0].mIntConst == mInstructions[i + 1]->mSrc[0].mIntConst && + mInstructions[i + 2]->mSrc[1].mTemp != mInstructions[i + 1]->mDst.mTemp) + { + InterInstruction* iadd = mInstructions[i + 2]; + mInstructions[i + 2] = mInstructions[i + 1]; + mInstructions[i + 1] = mInstructions[i + 0]; + mInstructions[i + 0] = iadd; + mInstructions[i + 2]->mSrc[0].mIntConst = 0; + changed = true; + } +#if 1 + if (i + 2 < mInstructions.Size() && + mInstructions[i + 0]->mCode == IC_BINARY_OPERATOR && mInstructions[i + 0]->mOperator == IA_AND && + mInstructions[i + 0]->mSrc[0].mTemp < 0 && + mInstructions[i + 1]->mCode == IC_BINARY_OPERATOR && mInstructions[i + 1]->mOperator == IA_SHR && + mInstructions[i + 1]->mSrc[0].mTemp < 0 && + mInstructions[i + 1]->mSrc[1].mTemp == mInstructions[i + 0]->mDst.mTemp && mInstructions[i + 1]->mSrc[1].mFinal && + mInstructions[i + 2]->mCode == IC_RELATIONAL_OPERATOR && + mInstructions[i + 2]->mSrc[1].mTemp == mInstructions[i + 1]->mDst.mTemp && mInstructions[i + 2]->mSrc[1].mFinal && + mInstructions[i + 2]->mSrc[0].mTemp < 0) + { + mInstructions[i + 0]->mSrc[0].mIntConst &= ~((1 << mInstructions[i + 1]->mSrc[0].mIntConst) - 1); + mInstructions[i + 2]->mSrc[0].mIntConst <<= mInstructions[i + 1]->mSrc[0].mIntConst; + mInstructions[i + 2]->mSrc[1] = mInstructions[i + 0]->mDst; + mInstructions[i + 1]->mCode = IC_NONE; + mInstructions[i + 1]->mNumOperands = 0; + mInstructions[i + 1]->mDst.mTemp = -1; + changed = true; + } + + if (i + 2 < mInstructions.Size() && + mInstructions[i + 0]->mCode == IC_BINARY_OPERATOR && mInstructions[i + 0]->mOperator == IA_SHR && + mInstructions[i + 0]->mSrc[0].mTemp < 0 && + mInstructions[i + 1]->mCode == IC_RELATIONAL_OPERATOR && + mInstructions[i + 1]->mSrc[1].mTemp == mInstructions[i + 0]->mDst.mTemp && mInstructions[i + 1]->mSrc[1].mFinal && + mInstructions[i + 1]->mSrc[0].mTemp < 0) + { + mInstructions[i + 1]->mSrc[0].mIntConst <<= mInstructions[i + 0]->mSrc[0].mIntConst; + + mInstructions[i + 0]->mOperator = IA_AND; + mInstructions[i + 0]->mSrc[0].mIntConst = ~((1 << mInstructions[i + 0]->mSrc[0].mIntConst) - 1); + mInstructions[i + 0]->mDst.mRange.Reset(); + mInstructions[i + 1]->mSrc[1].mRange.Reset(); + changed = true; + } + + if (i + 3 < mInstructions.Size() && + mInstructions[i + 0]->mCode == IC_BINARY_OPERATOR && mInstructions[i + 0]->mOperator == IA_ADD && mInstructions[i + 0]->mSrc[0].mTemp < 0 && + mInstructions[i + 0]->mDst.IsUByte() && mInstructions[i + 0]->mSrc[1].IsUByte() && + mInstructions[i + 1]->mCode == IC_CONVERSION_OPERATOR && mInstructions[i + 1]->mOperator == IA_EXT8TO16U && + mInstructions[i + 1]->mSrc[0].mTemp == mInstructions[i + 0]->mDst.mTemp && mInstructions[i + 1]->mSrc[0].mFinal && + mInstructions[i + 2]->mCode == IC_LEA && mInstructions[i + 2]->mSrc[1].mTemp >= 0 && + mInstructions[i + 2]->mSrc[0].mTemp == mInstructions[i + 1]->mDst.mTemp && mInstructions[i + 2]->mSrc[0].mFinal && + mInstructions[i + 3]->mCode == IC_STORE && + mInstructions[i + 3]->mSrc[1].mTemp == mInstructions[i + 2]->mDst.mTemp && mInstructions[i + 3]->mSrc[1].mFinal) + { + mInstructions[i + 3]->mSrc[1].mIntConst += mInstructions[i + 0]->mSrc[0].mIntConst; + mInstructions[i + 1]->mSrc[0] = mInstructions[i + 0]->mSrc[1]; + mInstructions[i + 1]->mDst.mRange = mInstructions[i + 0]->mSrc[1].mRange; + mInstructions[i + 2]->mSrc[0].mRange = mInstructions[i + 0]->mSrc[1].mRange; + changed = true; + } + + + if (i + 3 < mInstructions.Size() && + mInstructions[i + 0]->mCode == IC_BINARY_OPERATOR && mInstructions[i + 0]->mOperator == IA_SHR && + mInstructions[i + 0]->mSrc[0].mTemp < 0 && + + mInstructions[i + 1]->mCode == IC_BINARY_OPERATOR && (mInstructions[i + 1]->mOperator == IA_ADD || mInstructions[i + 1]->mOperator == IA_SUB) && + mInstructions[i + 1]->mSrc[0].mTemp < 0 && + mInstructions[i + 1]->mSrc[1].mTemp == mInstructions[i + 0]->mDst.mTemp && mInstructions[i + 1]->mSrc[1].mFinal && + + mInstructions[i + 2]->mCode == IC_BINARY_OPERATOR && mInstructions[i + 2]->mOperator == IA_SHL && + mInstructions[i + 2]->mSrc[0].mTemp < 0 && + mInstructions[i + 2]->mSrc[1].mTemp == mInstructions[i + 1]->mDst.mTemp && mInstructions[i + 2]->mSrc[1].mFinal && + + mInstructions[i + 3]->mCode == IC_BINARY_OPERATOR && mInstructions[i + 3]->mOperator == IA_AND && + mInstructions[i + 3]->mSrc[0].mTemp < 0 && + mInstructions[i + 3]->mSrc[1].mTemp == mInstructions[i + 2]->mDst.mTemp && mInstructions[i + 3]->mSrc[1].mFinal && + + mInstructions[i + 0]->mSrc[0].mIntConst == mInstructions[i + 2]->mSrc[0].mIntConst) + { + mInstructions[i + 3]->mSrc[0].mIntConst &= ~((1 << mInstructions[i + 0]->mSrc[0].mIntConst) - 1); + mInstructions[i + 1]->mSrc[0].mIntConst <<= mInstructions[i + 0]->mSrc[0].mIntConst; + + mInstructions[i + 1]->mSrc[1] = mInstructions[i + 0]->mSrc[1]; + mInstructions[i + 1]->mDst = mInstructions[i + 2]->mDst; + + mInstructions[i + 0]->mCode = IC_NONE; + mInstructions[i + 0]->mNumOperands = 0; + mInstructions[i + 0]->mDst.mTemp = -1; + mInstructions[i + 2]->mCode = IC_NONE; + mInstructions[i + 2]->mNumOperands = 0; + mInstructions[i + 2]->mDst.mTemp = -1; + changed = true; + } + if (i + 2 < mInstructions.Size() && + mInstructions[i + 0]->mCode == IC_BINARY_OPERATOR && mInstructions[i + 0]->mOperator == IA_AND && + mInstructions[i + 0]->mSrc[0].mTemp < 0 && + + mInstructions[i + 1]->mCode == IC_BINARY_OPERATOR && (mInstructions[i + 1]->mOperator == IA_ADD || mInstructions[i + 1]->mOperator == IA_SUB) && + mInstructions[i + 1]->mSrc[0].mTemp < 0 && + mInstructions[i + 1]->mSrc[1].mTemp == mInstructions[i + 0]->mDst.mTemp && mInstructions[i + 1]->mSrc[1].mFinal && + + mInstructions[i + 2]->mCode == IC_BINARY_OPERATOR && mInstructions[i + 2]->mOperator == IA_AND && + mInstructions[i + 2]->mSrc[0].mTemp < 0 && + mInstructions[i + 2]->mSrc[1].mTemp == mInstructions[i + 1]->mDst.mTemp && mInstructions[i + 2]->mSrc[1].mFinal && + + (mInstructions[i + 0]->mSrc[0].mIntConst & (mInstructions[i + 0]->mSrc[0].mIntConst + 1)) == 0 && // Is power of two - 1 + (~mInstructions[i + 0]->mSrc[0].mIntConst & mInstructions[i + 1]->mSrc[0].mIntConst) == 0 && // add is part of initial mask + (~mInstructions[i + 0]->mSrc[0].mIntConst & mInstructions[i + 2]->mSrc[0].mIntConst) == 0) // final mask is part of initial mask + { + mInstructions[i + 1]->mSrc[1] = mInstructions[i + 0]->mSrc[1]; + + mInstructions[i + 0]->mCode = IC_NONE; + mInstructions[i + 0]->mNumOperands = 0; + mInstructions[i + 0]->mDst.mTemp = -1; + changed = true; + } +#endif + +#if 1 + if (i + 2 < mInstructions.Size() && + mInstructions[i + 0]->mCode == IC_CONVERSION_OPERATOR && mInstructions[i + 0]->mOperator == IA_EXT8TO16U && + mInstructions[i + 0]->mSrc[0].mTemp >= 0 && + mInstructions[i + 1]->mDst.mTemp != mInstructions[i + 0]->mSrc[0].mTemp && + + mInstructions[i + 1]->mCode == IC_BINARY_OPERATOR && (mInstructions[i + 1]->mOperator == IA_ADD || mInstructions[i + 1]->mOperator == IA_SUB) && + mInstructions[i + 1]->mSrc[0].mTemp < 0 && + mInstructions[i + 1]->mSrc[1].mTemp == mInstructions[i + 0]->mDst.mTemp && + + mInstructions[i + 2]->mCode == IC_STORE && + mInstructions[i + 2]->mSrc[0].mTemp == mInstructions[i + 1]->mDst.mTemp && mInstructions[i + 2]->mSrc[0].mFinal && + mInstructions[i + 2]->mSrc[0].mType == IT_INT8) + { + mInstructions[i + 1]->mSrc[1] = mInstructions[i + 0]->mSrc[0]; + mInstructions[i + 0]->mSrc[0].mFinal = false; + mInstructions[i + 1]->mDst.mType = IT_INT8; + + changed = true; + } + if (i + 2 < mInstructions.Size() && + mInstructions[i + 0]->mCode == IC_CONVERSION_OPERATOR && mInstructions[i + 0]->mOperator == IA_EXT8TO16U && + mInstructions[i + 0]->mSrc[0].mTemp >= 0 && + mInstructions[i + 1]->mDst.mTemp != mInstructions[i + 0]->mSrc[0].mTemp && + + mInstructions[i + 1]->mCode == IC_BINARY_OPERATOR && (mInstructions[i + 1]->mOperator == IA_ADD || mInstructions[i + 1]->mOperator == IA_SUB) && + mInstructions[i + 1]->mSrc[1].mTemp < 0 && + mInstructions[i + 1]->mSrc[0].mTemp == mInstructions[i + 0]->mDst.mTemp && + + mInstructions[i + 2]->mCode == IC_STORE && + mInstructions[i + 2]->mSrc[0].mTemp == mInstructions[i + 1]->mDst.mTemp && mInstructions[i + 2]->mSrc[0].mFinal && + mInstructions[i + 2]->mSrc[0].mType == IT_INT8) + { + mInstructions[i + 1]->mSrc[0] = mInstructions[i + 0]->mSrc[0]; + mInstructions[i + 0]->mSrc[0].mFinal = false; + mInstructions[i + 1]->mDst.mType = IT_INT8; + + changed = true; + } +#endif + +#if 0 + if (i + 2 < mInstructions.Size() && + mInstructions[i + 0]->mCode == IC_BINARY_OPERATOR && mInstructions[i + 0]->mDst.mType == IT_FLOAT && + + mInstructions[i + 1]->mCode == IC_BINARY_OPERATOR && (mInstructions[i + 1]->mOperator == IA_ADD || mInstructions[i + 1]->mOperator == IA_MUL) && + mInstructions[i + 1]->mSrc[0].mTemp != mInstructions[i + 0]->mDst.mTemp && + mInstructions[i + 1]->mSrc[1].mTemp == mInstructions[i + 0]->mDst.mTemp) + { + InterOperand op = mInstructions[i + 1]->mSrc[0]; + mInstructions[i + 1]->mSrc[0] = mInstructions[i + 1]->mSrc[1]; + mInstructions[i + 1]->mSrc[1] = op; + + changed = true; + } +#endif +#if 1 + if (i + 2 < mInstructions.Size() && + mInstructions[i + 0]->mCode == IC_BINARY_OPERATOR && + mInstructions[i + 1]->mCode == IC_BINARY_OPERATOR && + mInstructions[i + 2]->mCode == IC_BINARY_OPERATOR && + mInstructions[i + 0]->mDst.mTemp != mInstructions[i + 1]->mSrc[0].mTemp && + mInstructions[i + 0]->mDst.mTemp != mInstructions[i + 1]->mSrc[1].mTemp && + mInstructions[i + 2]->mSrc[0].mTemp >= 0 && mInstructions[i + 2]->mSrc[1].mTemp >= 0 && + mInstructions[i + 2]->mSrc[0].mFinal && mInstructions[i + 2]->mSrc[1].mFinal && + mInstructions[i + 2]->mDst.mType == IT_INT16) + { + bool fail = false; + int s0, s1, c0, c1, s2 = i + 2; + + if (mInstructions[i + 2]->mSrc[0].mTemp == mInstructions[i + 0]->mDst.mTemp && mInstructions[i + 2]->mSrc[1].mTemp == mInstructions[i + 1]->mDst.mTemp) + { + s0 = i + 0; s1 = i + 1; + } + else if (mInstructions[i + 2]->mSrc[0].mTemp == mInstructions[i + 1]->mDst.mTemp && mInstructions[i + 2]->mSrc[1].mTemp == mInstructions[i + 0]->mDst.mTemp) + { + s0 = i + 1; s1 = i + 0; + } + else + fail = true; + + if (!fail) + { + if (mInstructions[s0]->mSrc[0].mTemp < 0) + c0 = 0; + else if (mInstructions[s0]->mSrc[1].mTemp < 0) + c0 = 1; + else + fail = true; + + if (mInstructions[s1]->mSrc[0].mTemp < 0) + c1 = 0; + else if (mInstructions[s1]->mSrc[1].mTemp < 0) + c1 = 1; + else + fail = true; + + if (!fail) + { + InterOperator o0 = mInstructions[s0]->mOperator; + InterOperator o1 = mInstructions[s1]->mOperator; + InterOperator o2 = mInstructions[s2]->mOperator; + + if (o2 == IA_SUB) + { + if ((o0 == IA_ADD || o0 == IA_SUB && c0 == 0) && (o1 == IA_ADD || o1 == IA_SUB && c1 == 0)) + { + int64 iconst = + (o1 == IA_ADD ? mInstructions[s1]->mSrc[c1].mIntConst : -mInstructions[s1]->mSrc[c1].mIntConst) - + (o0 == IA_ADD ? mInstructions[s0]->mSrc[c0].mIntConst : -mInstructions[s0]->mSrc[c0].mIntConst); + + mInstructions[s0]->mSrc[0] = mInstructions[s0]->mSrc[1 - c0]; mInstructions[s0]->mSrc[1 - c0].mFinal = false; + mInstructions[s0]->mSrc[1] = mInstructions[s1]->mSrc[1 - c1]; mInstructions[s1]->mSrc[1 - c1].mFinal = false; + mInstructions[s0]->mDst.mRange.Reset(); + mInstructions[s0]->mOperator = IA_SUB; + mInstructions[s2]->mOperator = IA_ADD; + mInstructions[s2]->mSrc[0].mRange = mInstructions[s0]->mDst.mRange; + mInstructions[s2]->mSrc[1].mTemp = -1; + mInstructions[s2]->mSrc[1].mIntConst = iconst; + changed = true; + } + } + else if (o2 == IA_ADD) + { + if ((o0 == IA_ADD || o0 == IA_SUB && c0 == 0) && (o1 == IA_ADD || o1 == IA_SUB && c1 == 0)) + { + int64 iconst = + (o1 == IA_ADD ? mInstructions[s1]->mSrc[c1].mIntConst : -mInstructions[s1]->mSrc[c1].mIntConst) + + (o0 == IA_ADD ? mInstructions[s0]->mSrc[c0].mIntConst : -mInstructions[s0]->mSrc[c0].mIntConst); + + mInstructions[s0]->mSrc[0] = mInstructions[s0]->mSrc[1 - c0]; mInstructions[s0]->mSrc[1 - c0].mFinal = false; + mInstructions[s0]->mSrc[1] = mInstructions[s1]->mSrc[1 - c1]; mInstructions[s1]->mSrc[1 - c1].mFinal = false; + mInstructions[s0]->mDst.mRange.Reset(); + mInstructions[s0]->mOperator = IA_ADD; + mInstructions[s2]->mOperator = IA_ADD; + mInstructions[s2]->mSrc[0].mRange = mInstructions[s0]->mDst.mRange; + mInstructions[s2]->mSrc[1].mTemp = -1; + mInstructions[s2]->mSrc[1].mIntConst = iconst; + changed = true; + } + } + } + } + } +#endif + +#if 1 + // Postincrement artifact + if (mInstructions[i]->mCode == IC_LOAD_TEMPORARY) + { + InterInstruction* mins = mInstructions[i]; + + int j = i + 1; + while (j + 1 < mInstructions.Size() && + !mInstructions[j]->ReferencesTemp(mins->mDst.mTemp) && + !mInstructions[j]->ReferencesTemp(mins->mSrc[0].mTemp)) + j++; + + if (j + 1 < mInstructions.Size()) + { + InterInstruction* bins = mInstructions[j]; + if (bins->mCode == IC_BINARY_OPERATOR && + bins->mSrc[0].mTemp < 0 && + mins->mSrc[0].mTemp == bins->mSrc[1].mTemp && + mins->mSrc[0].mTemp == bins->mDst.mTemp) + { + int ttemp = bins->mDst.mTemp; + int k = j; + while (k + 2 < mInstructions.Size() && + mInstructions[k + 1]->mCode != IC_RELATIONAL_OPERATOR && + !mInstructions[k + 1]->ReferencesTemp(ttemp)) + { + mInstructions[k] = mInstructions[k + 1]; + k++; + } + if (k > j) + { + mInstructions[k] = bins; + changed = true; + } + + j = i; + while (j + 1 < mInstructions.Size() && + !mInstructions[j + 1]->ReferencesTemp(mins->mDst.mTemp) && + !mInstructions[j + 1]->ReferencesTemp(mins->mSrc[0].mTemp)) + { + mInstructions[j] = mInstructions[j + 1]; + j++; + } + if (j > i) + { + mInstructions[j] = mins; + changed = true; + } + } + } + } + + CheckFinalLocal(); +#endif + } +#if 1 + if (i + 2 < mInstructions.Size()) + { + if (mInstructions[i + 0]->mCode == IC_LOAD_TEMPORARY && + IsIntegerType(mInstructions[i + 0]->mDst.mType) && + mInstructions[i + 1]->mCode == IC_BINARY_OPERATOR && + mInstructions[i + 1]->mSrc[0].mTemp < 0 && + mInstructions[i + 0]->mSrc[0].mTemp == mInstructions[i + 1]->mSrc[1].mTemp && + mInstructions[i + 0]->mSrc[0].mTemp == mInstructions[i + 1]->mDst.mTemp && + mInstructions[i + 2]->mCode == IC_RELATIONAL_OPERATOR && + mInstructions[i + 2]->mSrc[1].mTemp == mInstructions[i + 0]->mDst.mTemp && + mInstructions[i + 2]->mSrc[0].mTemp < 0) + { + if ( + mInstructions[i + 1]->mOperator == IA_ADD && + (mInstructions[i + 2]->mOperator == IA_CMPLES || mInstructions[i + 2]->mOperator == IA_CMPLS) && + mInstructions[i + 1]->mSrc[0].mIntConst > 0 && + mInstructions[i + 2]->mSrc[0].mIntConst + mInstructions[i + 1]->mSrc[0].mIntConst < SignedTypeMax(mInstructions[i + 2]->mSrc[0].mType)) + { + mInstructions[i + 2]->mSrc[1] = mInstructions[i + 1]->mDst; + mInstructions[i + 2]->mSrc[1].mFinal = false; + mInstructions[i + 2]->mSrc[0].mIntConst += mInstructions[i + 1]->mSrc[0].mIntConst; + changed = true; + } + } + } +#endif + if (i + 3 < mInstructions.Size()) + { + if ( + mInstructions[i + 0]->mCode == IC_BINARY_OPERATOR && mInstructions[i + 0]->mOperator == IA_ADD && mInstructions[i + 0]->mSrc[0].mTemp < 0 && + mInstructions[i + 1]->mCode == IC_BINARY_OPERATOR && mInstructions[i + 1]->mOperator == IA_MUL && mInstructions[i + 1]->mSrc[0].mTemp < 0 && + mInstructions[i + 1]->mSrc[1].mTemp == mInstructions[i + 0]->mDst.mTemp && mInstructions[i + 1]->mSrc[1].mFinal && + mInstructions[i + 2]->mCode == IC_BINARY_OPERATOR && mInstructions[i + 2]->mOperator == IA_ADD && + mInstructions[i + 2]->mSrc[1].mTemp == mInstructions[i + 1]->mDst.mTemp && mInstructions[i + 2]->mSrc[1].mFinal && + mInstructions[i + 3]->mCode == IC_LEA && mInstructions[i + 3]->mSrc[1].mTemp < 0 && + mInstructions[i + 3]->mSrc[0].mTemp == mInstructions[i + 2]->mDst.mTemp && mInstructions[i + 3]->mSrc[0].mFinal) + { + int64 d = mInstructions[i + 0]->mSrc[0].mIntConst * mInstructions[i + 1]->mSrc[0].mIntConst; + mInstructions[i + 3]->mSrc[1].mIntConst += d; + mInstructions[i + 1]->mSrc[1] = mInstructions[i + 0]->mSrc[1]; + mInstructions[i + 1]->mDst.mRange.mMinValue -= d; mInstructions[i + 1]->mDst.mRange.mMaxValue -= d; + mInstructions[i + 2]->mSrc[1].mRange.mMinValue -= d; mInstructions[i + 2]->mSrc[1].mRange.mMaxValue -= d; + mInstructions[i + 2]->mDst.mRange.mMinValue -= d; mInstructions[i + 2]->mDst.mRange.mMaxValue -= d; + mInstructions[i + 3]->mSrc[0].mRange.mMinValue -= d; mInstructions[i + 3]->mSrc[0].mRange.mMaxValue -= d; + mInstructions[i + 0]->mCode = IC_NONE; mInstructions[i + 0]->mNumOperands = 0; + mInstructions[i + 0]->mDst.mTemp = -1; + changed = true; + } + } + + +#if 1 + if (i + 1 < mInstructions.Size()) + { + if ( + mInstructions[i + 0]->mCode == IC_LEA && mInstructions[i + 0]->mSrc[1].mTemp < 0 && mInstructions[i + 0]->mDst.mTemp != mInstructions[i + 0]->mSrc[0].mTemp && + mInstructions[i + 1]->mCode == IC_LEA && mInstructions[i + 1]->mSrc[1].mTemp == mInstructions[i + 0]->mDst.mTemp && mInstructions[i + 1]->mSrc[0].mTemp < 0 && mInstructions[i + 1]->mSrc[0].mIntConst < 0) + { + InterOperand t0 = mInstructions[i + 0]->mDst; + InterOperand t1 = mInstructions[i + 1]->mDst; + + mInstructions[i + 0]->mDst = t1; + mInstructions[i + 1]->mDst = t0; + mInstructions[i + 1]->mSrc[1] = t1; + mInstructions[i + 0]->mSrc[1].mIntConst += mInstructions[i + 1]->mSrc[0].mIntConst; + mInstructions[i + 1]->mSrc[0].mIntConst = -mInstructions[i + 1]->mSrc[0].mIntConst; + changed = true; + } + if ( + mInstructions[i + 0]->mCode == IC_LEA && mInstructions[i + 0]->mSrc[0].mTemp < 0 && mInstructions[i + 0]->mDst.mTemp != mInstructions[i + 0]->mSrc[1].mTemp && + mInstructions[i + 1]->mCode == IC_LEA && mInstructions[i + 1]->mSrc[1].mTemp == mInstructions[i + 0]->mDst.mTemp && mInstructions[i + 1]->mSrc[0].mTemp < 0 && mInstructions[i + 1]->mSrc[0].mIntConst < 0) + { + InterOperand t0 = mInstructions[i + 0]->mDst; + InterOperand t1 = mInstructions[i + 1]->mDst; + + mInstructions[i + 0]->mDst = t1; + mInstructions[i + 1]->mDst = t0; + mInstructions[i + 1]->mSrc[1] = t1; + mInstructions[i + 0]->mSrc[0].mIntConst += mInstructions[i + 1]->mSrc[0].mIntConst; + mInstructions[i + 1]->mSrc[0].mIntConst = -mInstructions[i + 1]->mSrc[0].mIntConst; + changed = true; + } + } + +#endif +#if 1 + if (i + 1 < mInstructions.Size()) + { + if ( + mInstructions[i + 0]->mCode == IC_LOAD_TEMPORARY && + mInstructions[i + 1]->mCode == IC_RELATIONAL_OPERATOR && mInstructions[i + 1]->mSrc[0].mTemp == mInstructions[i + 0]->mSrc[0].mTemp && mInstructions[i + 1]->mSrc[0].mFinal + ) + { + mInstructions[i + 1]->mSrc[0].mTemp = mInstructions[i + 0]->mDst.mTemp; + mInstructions[i + 1]->mSrc[0].mFinal = false; + changed = true; + } + else if ( + mInstructions[i + 0]->mCode == IC_LOAD_TEMPORARY && + mInstructions[i + 1]->mCode == IC_RELATIONAL_OPERATOR && mInstructions[i + 1]->mSrc[1].mTemp == mInstructions[i + 0]->mSrc[0].mTemp && mInstructions[i + 1]->mSrc[1].mFinal + ) + { + mInstructions[i + 1]->mSrc[1].mTemp = mInstructions[i + 0]->mDst.mTemp; + mInstructions[i + 1]->mSrc[1].mFinal = false; + changed = true; + } + } +#endif +#if 1 + if (i + 1 < mInstructions.Size()) + { + if (mInstructions[i + 0]->mCode == IC_LEA && mInstructions[i + 0]->mSrc[1].mTemp < 0 && + mInstructions[i + 1]->mCode == IC_RELATIONAL_OPERATOR && mInstructions[i + 1]->mSrc[1].mTemp == mInstructions[i + 0]->mDst.mTemp && mInstructions[i + 1]->mSrc[0].mTemp < 0 && + mInstructions[i + 1]->mSrc[0].mMemory == IM_ABSOLUTE && mInstructions[i + 1]->mSrc[0].mIntConst == 0) + { + if (mInstructions[i + 0]->mSrc[1].mMemory != IM_ABSOLUTE) + { + if (mInstructions[i + 1]->mOperator == IA_CMPEQ) + { + mInstructions[i + 1]->mNumOperands = 0; + mInstructions[i + 1]->mCode = IC_CONSTANT; + mInstructions[i + 1]->mConst.mType = IT_BOOL; + mInstructions[i + 1]->mConst.mIntConst = 0; + changed = true; + } + else if (mInstructions[i + 1]->mOperator == IA_CMPNE) + { + mInstructions[i + 1]->mNumOperands = 0; + mInstructions[i + 1]->mCode = IC_CONSTANT; + mInstructions[i + 1]->mConst.mType = IT_BOOL; + mInstructions[i + 1]->mConst.mIntConst = 1; + changed = true; + } + } + } + } +#endif +#if 1 + if (i + 1 < mInstructions.Size()) + { + if (mInstructions[i + 0]->mCode == IC_BINARY_OPERATOR && mInstructions[i + 0]->mOperator == IA_ADD && + mInstructions[i + 1]->mCode == IC_BINARY_OPERATOR && mInstructions[i + 1]->mOperator == IA_ADD && + mInstructions[i + 1]->mSrc[1].mTemp == mInstructions[i + 0]->mDst.mTemp && mInstructions[i + 1]->mSrc[1].mFinal && + mInstructions[i + 0]->mSrc[0].mTemp < 0 && mInstructions[i + 1]->mSrc[0].mTemp < 0 && + IsIntegerType(mInstructions[i + 0]->mDst.mType)) + { + mInstructions[i + 0]->mSrc[0].mIntConst += mInstructions[i + 1]->mSrc[0].mIntConst; + mInstructions[i + 0]->mDst = mInstructions[i + 1]->mDst; + + mInstructions[i + 1]->mCode = IC_NONE; + mInstructions[i + 1]->mNumOperands = 0; + mInstructions[i + 1]->mDst.mTemp = -1; + + changed = true; + } + } +#endif +#if 1 + if (i + 1 < mInstructions.Size()) + { + if (mInstructions[i + 0]->mCode == IC_BINARY_OPERATOR && mInstructions[i + 0]->mOperator == IA_SUB && + mInstructions[i + 1]->mCode == IC_BINARY_OPERATOR && mInstructions[i + 1]->mOperator == IA_SHL && + mInstructions[i + 1]->mSrc[0].mTemp == mInstructions[i + 0]->mDst.mTemp && mInstructions[i + 1]->mSrc[0].mFinal && mInstructions[i + 1]->mSrc[0].IsUByte() && + mInstructions[i + 1]->mSrc[1].mTemp < 0 && ispow2(mInstructions[i + 1]->mSrc[1].mIntConst)) + { + int64 k = binlog(mInstructions[i + 1]->mSrc[1].mIntConst); + + if (mInstructions[i + 0]->mSrc[0].mTemp >= 0 && mInstructions[i + 0]->mSrc[1].mTemp < 0) + { + int64 s = mInstructions[i + 0]->mSrc[1].mIntConst; + mInstructions[i + 0]->mSrc[1] = mInstructions[i + 0]->mSrc[0]; + mInstructions[i + 0]->mSrc[0].mTemp = -1; + mInstructions[i + 0]->mSrc[0].mIntConst = mInstructions[i + 0]->mSrc[1].mRange.mMinValue; + mInstructions[i + 0]->mDst.mRange.SetLimit(0, mInstructions[i + 0]->mSrc[1].mRange.mMaxValue - mInstructions[i + 0]->mSrc[0].mIntConst); + mInstructions[i + 1]->mOperator = IA_SHR; + mInstructions[i + 1]->mSrc[0] = mInstructions[i + 0]->mDst; + int64 d = s - mInstructions[i + 0]->mSrc[0].mIntConst; + if (d < 0) + mInstructions[i + 1]->mSrc[1].mIntConst >>= -d; + else + mInstructions[i + 1]->mSrc[1].mIntConst <<= d; + changed = true; + } + } + if (mInstructions[i + 0]->mCode == IC_BINARY_OPERATOR && mInstructions[i + 0]->mOperator == IA_SUB && + mInstructions[i + 1]->mCode == IC_BINARY_OPERATOR && mInstructions[i + 1]->mOperator == IA_SHR && + mInstructions[i + 1]->mSrc[0].mTemp == mInstructions[i + 0]->mDst.mTemp && mInstructions[i + 1]->mSrc[0].mFinal && mInstructions[i + 1]->mSrc[0].IsUByte() && + mInstructions[i + 1]->mSrc[1].mTemp < 0 && ispow2(mInstructions[i + 1]->mSrc[1].mIntConst)) + { + int64 k = binlog(mInstructions[i + 1]->mSrc[1].mIntConst); + + if (mInstructions[i + 0]->mSrc[0].mTemp >= 0 && mInstructions[i + 0]->mSrc[1].mTemp < 0) + { + int64 s = mInstructions[i + 0]->mSrc[1].mIntConst; + mInstructions[i + 0]->mSrc[1] = mInstructions[i + 0]->mSrc[0]; + mInstructions[i + 0]->mSrc[0].mTemp = -1; + mInstructions[i + 0]->mSrc[0].mIntConst = mInstructions[i + 0]->mSrc[1].mRange.mMinValue; + mInstructions[i + 0]->mDst.mRange.SetLimit(0, mInstructions[i + 0]->mSrc[1].mRange.mMaxValue - mInstructions[i + 0]->mSrc[0].mIntConst); + mInstructions[i + 1]->mOperator = IA_SHL; + mInstructions[i + 1]->mSrc[0] = mInstructions[i + 0]->mDst; + int64 d = s - mInstructions[i + 0]->mSrc[0].mIntConst; + if (d < 0) + mInstructions[i + 1]->mSrc[1].mIntConst <<= -d; + else + mInstructions[i + 1]->mSrc[1].mIntConst >>= d; + changed = true; + } + } + } + + +#endif + } + + return changed; +} + +static int TempUseDelta(const InterInstruction* ins) +{ + int d = 0; + if (ins->mDst.mTemp >= 0) + d++; + for (int i = 0; i < ins->mNumOperands; i++) + if (ins->mSrc[i].mTemp >= 0 && ins->mSrc[i].mFinal) + d--; + return d; +} + +bool InterCodeBasicBlock::IsConstExitTemp(int temp) const +{ + int n = mInstructions.Size() - 1; + while (n >= 0 && mInstructions[n]->mDst.mTemp != temp) + n--; + return n >= 0 && mInstructions[n]->mCode == IC_CONSTANT; +} + +bool InterCodeBasicBlock::SplitSingleBranchUseConst(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + if (!mLoopHead && mFalseJump) + { + for (int i = 0; i + 1 < mInstructions.Size(); i++) + { + InterInstruction* ins = mInstructions[i]; + + if (ins->mCode == IC_CONSTANT) + { + if (CanMoveInstructionBehindBlock(i)) + { + InterCodeBasicBlock* tblock = nullptr; + + if (mTrueJump->mEntryRequiredTemps[ins->mDst.mTemp] && !mFalseJump->mEntryRequiredTemps[ins->mDst.mTemp]) + tblock = mTrueJump; + else if (!mTrueJump->mEntryRequiredTemps[ins->mDst.mTemp] && mFalseJump->mEntryRequiredTemps[ins->mDst.mTemp]) + tblock = mFalseJump; + + if (tblock) + { + if (tblock->mNumEntries > 1) + { + InterCodeBasicBlock* nblock = new InterCodeBasicBlock(mProc); + + nblock->mEntryRequiredTemps = tblock->mEntryRequiredTemps; + nblock->mExitRequiredTemps = tblock->mEntryRequiredTemps; + + mExitRequiredTemps -= ins->mDst.mTemp; + nblock->mEntryRequiredTemps -= ins->mDst.mTemp; + + tblock->mEntryBlocks.RemoveAll(this); + tblock->mEntryBlocks.Push(nblock); + + if (tblock == mTrueJump) + mTrueJump = nblock; + else + mFalseJump = nblock; + + InterInstruction* jins = new InterInstruction(mInstructions.Last()->mLocation, IC_JUMP); + nblock->mInstructions.Push(jins); + nblock->Close(tblock, nullptr); + + nblock->mEntryBlocks.Push(this); + nblock->mNumEntries = 1; + + tblock = nblock; + } + + tblock->mInstructions.Insert(0, ins); + mInstructions.Remove(i); + i--; + changed = true; + } + } + } + } + } + + + if (mTrueJump && mTrueJump->SplitSingleBranchUseConst()) + changed = true; + if (mFalseJump && mFalseJump->SplitSingleBranchUseConst()) + changed = true; + } + + return changed; +} + +bool InterCodeBasicBlock::CommonTailCodeMerge(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + if (!mLoopHead) + { + for (int i = 0; i + 1 < mInstructions.Size(); i++) + { + InterInstruction* ins = mInstructions[i]; + + if (CanMoveInstructionBeforeBlock(i)) + { + bool aconst = true, aany = false; + for (int j = 0; j < ins->mNumOperands; j++) + { + if (ins->mSrc[j].mTemp >= 0) + { + aany = true; + int k = 0; + while (k < mEntryBlocks.Size() && mEntryBlocks[k]->IsConstExitTemp(ins->mSrc[j].mTemp) && !mEntryBlocks[k]->mFalseJump) + k++; + if (k < mEntryBlocks.Size()) + { + aconst = false; + break; + } + } + } + + if (aconst && aany) + { + for (int j = 0; j < mEntryBlocks.Size(); j++) + { + InterCodeBasicBlock* eblock = mEntryBlocks[j]; + eblock->mInstructions.Insert(eblock->mInstructions.Size() - 1, ins->Clone()); + if (ins->mDst.mTemp >= 0) + eblock->mExitRequiredTemps += ins->mDst.mTemp; + } + if (ins->mDst.mTemp >= 0) + mEntryRequiredTemps += ins->mDst.mTemp; + ins->mCode = IC_NONE; + ins->mNumOperands = 0; + ins->mDst.mTemp = -1; + changed = true; + } + + } + } + } + + if (mTrueJump && mTrueJump->CommonTailCodeMerge()) + changed = true; + if (mFalseJump && mFalseJump->CommonTailCodeMerge()) + changed = true; + } + + return changed; +} + +void InterCodeBasicBlock::PeepholeOptimization(const GrowingVariableArray& staticVars, const GrowingInterCodeProcedurePtrArray& staticProcs) +{ + int i; + + if (!mVisited) + { + mVisited = true; + + CheckFinalLocal(); + if (mTrueJump) mTrueJump->CheckFinalLocal(); + if (mFalseJump) mFalseJump->CheckFinalLocal(); + + // Remove none instructions + + int j = 0; + for (i = 0; i < mInstructions.Size(); i++) + { + if (mInstructions[i]->mCode != IC_NONE) + { + mInstructions[j++] = mInstructions[i]; + } + } + mInstructions.SetSize(j); + + // shorten lifespan + + int loopTmp = -1; + + int limit = mInstructions.Size() - 1; + if (limit >= 0 && mInstructions[limit]->mCode == IC_BRANCH) + { + limit--; +#if 1 + // try to move conditional source down + int i = limit; + while (i >= 0 && mInstructions[i]->mDst.mTemp != mInstructions[limit + 1]->mSrc[0].mTemp) + i--; + + if (i >= 0 && i != limit) + { + InterInstruction* ins(mInstructions[i]); + + if (ins->mCode == IC_BINARY_OPERATOR || ins->mCode == IC_RELATIONAL_OPERATOR) + { + if (ins->mSrc[0].mTemp < 0) + { + int k = i; + while (k < limit && CanSwapInstructions(ins, mInstructions[k + 1])) + k++; + + if (k == limit) + { + for (int l = i; l < limit; l++) + { + SwapInstructions(ins, mInstructions[l + 1]); + mInstructions[l] = mInstructions[l + 1]; + } + mInstructions[limit] = ins; + +// mInstructions.Remove(i); +// mInstructions.Insert(limit, ins); + } + } + } + } +#endif + if (limit > 0 && mInstructions[limit]->mCode == IC_RELATIONAL_OPERATOR) + { + if (mInstructions[limit]->mSrc[1].mTemp) + loopTmp = mInstructions[limit]->mSrc[1].mTemp; + else if (mInstructions[limit]->mSrc[0].mTemp) + loopTmp = mInstructions[limit]->mSrc[0].mTemp; + limit--; + + if (loopTmp >= 0) + { + int i = limit; + while (i >= 0 && !(mInstructions[i]->mCode == IC_BINARY_OPERATOR && mInstructions[i]->mDst.mTemp == loopTmp)) + i--; + if (i >= 0 && i < limit) + { + InterInstruction* ins(mInstructions[i]); + int j = i; + while (j < limit && CanSwapInstructions(ins, mInstructions[j + 1])) + { + SwapInstructions(ins, mInstructions[j + 1]); + mInstructions[j] = mInstructions[j + 1]; + j++; + } + if (i != j) + mInstructions[j] = ins; + } + + if (limit > 0 && mInstructions[limit]->mCode == IC_BINARY_OPERATOR && (mInstructions[limit]->mDst.mTemp == loopTmp)) + limit--; + } + } + else if (limit > 0 && mInstructions[limit]->mDst.mTemp == mInstructions[limit + 1]->mSrc[0].mTemp) + limit--; + } + else if (limit >= 0 && mInstructions[limit]->mCode == IC_JUMP) + limit --; + + CheckFinalLocal(); + + int i = limit; +#if 1 + while (i >= 0) + { + // move non indirect loads down + if (mInstructions[i]->mCode == IC_LOAD && (mInstructions[i]->mSrc[0].mMemory != IM_INDIRECT || mInstructions[i]->mDst.mType != IT_INT8 || !mInstructions[i]->mSrc[0].mFinal)) + { + InterInstruction * ins(mInstructions[i]); + int j = i; + while (j < limit && CanSwapInstructions(ins, mInstructions[j + 1])) + { + SwapInstructions(ins, mInstructions[j + 1]); + mInstructions[j] = mInstructions[j + 1]; + j++; + } + if (i != j) + mInstructions[j] = ins; + } + else if (mInstructions[i]->mCode == IC_BINARY_OPERATOR || mInstructions[i]->mCode == IC_UNARY_OPERATOR || mInstructions[i]->mCode == IC_CONVERSION_OPERATOR || mInstructions[i]->mCode == IC_CONSTANT || mInstructions[i]->mCode == IC_LOAD_TEMPORARY && !mInstructions[i]->mSrc[0].mFinal) + { + InterInstruction* ins(mInstructions[i]); + + int k = i; + while (k < limit && CanSwapInstructions(ins, mInstructions[k + 1])) + k++; + if (k < limit) + { + while (k > i && IsChained(mInstructions[k], mInstructions[k + 1])) + k--; + } + + int j = i; + while (j < k) + { + SwapInstructions(ins, mInstructions[j + 1]); + mInstructions[j] = mInstructions[j + 1]; + j++; + } + + if (i != j) + mInstructions[j] = ins; + } + else if (mInstructions[i]->mCode == IC_LEA && (mInstructions[i]->mSrc[0].mTemp < 0 || mInstructions[i]->mSrc[1].mTemp < 0)) + { + InterInstruction* ins(mInstructions[i]); + int j = i; + while (j < limit && CanSwapInstructions(ins, mInstructions[j + 1])) + { + SwapInstructions(ins, mInstructions[j + 1]); + mInstructions[j] = mInstructions[j + 1]; + j++; + } + if (i != j) + mInstructions[j] = ins; + } + + i--; + } + + CheckFinalLocal(); +#endif + +#if 1 + i = 0; + while (i <= limit) + { + InterInstruction* ins(mInstructions[i]); + + if (ins->mDst.mTemp >= 0 && (ins->mDst.mType == IT_FLOAT /* || ins->mDst.mType == IT_INT32*/)) + { + if ((ins->mCode == IC_UNARY_OPERATOR || ins->mCode == IC_CONVERSION_OPERATOR) && ins->mSrc[0].mFinal || + (ins->mCode == IC_BINARY_OPERATOR && (ins->mSrc[0].mTemp < 0 && ins->mSrc[1].mFinal || ins->mSrc[1].mTemp < 0 && ins->mSrc[0].mFinal))) + { + int reg = ins->mSrc[0].mTemp; + if (ins->mCode == IC_BINARY_OPERATOR && reg < 0) + reg = ins->mSrc[1].mTemp; + + int j = i - 1; + while (j >= 0 && CanSwapInstructions(mInstructions[j], ins)) + j--; + if (j >= 0 && j < i - 1 && mInstructions[j]->mDst.mTemp == reg) + { + int k = i - 1; + while (k > j) + { + SwapInstructions(mInstructions[k], ins); + mInstructions[k + 1] = mInstructions[k]; + k--; + } + mInstructions[k + 1] = ins; + } + } + } + i++; + } +#endif + +#if 1 + // move indirect load/store pairs up + i = 0; + while (i + 1 < mInstructions.Size()) + { + if (mInstructions[i + 0]->mCode == IC_LOAD && mInstructions[i + 1]->mCode == IC_STORE && mInstructions[i + 1]->mSrc[0].mTemp == mInstructions[i + 0]->mDst.mTemp && mInstructions[i + 1]->mSrc[0].mFinal) + { + if (mInstructions[i + 0]->mSrc[0].mMemory == IM_INDIRECT) + { + InterInstruction* lins(mInstructions[i + 0]); + InterInstruction* sins(mInstructions[i + 1]); + + int j = i; + while (j > 0 && + CanSwapInstructions(mInstructions[j - 1], lins) && + CanSwapInstructions(mInstructions[j - 1], sins)) + { + SwapInstructions(mInstructions[j - 1], lins); + SwapInstructions(mInstructions[j - 1], sins); + + mInstructions[j + 1] = mInstructions[j - 1]; + j--; + } + + if (i != j) + { + mInstructions[j + 0] = lins; + mInstructions[j + 1] = sins; + } + } + } + + i++; + } + + CheckFinalLocal(); +#endif + +#if 1 + i = 0; + while (i < mInstructions.Size()) + { + // move stores up + if (mInstructions[i]->mCode == IC_STORE) + { + InterInstruction * ins(mInstructions[i]); + int j = i; + while (j > 0 && CanSwapInstructions(mInstructions[j - 1], ins)) + { + if (mInstructions[j - 1]->mCode == IC_STORE && mInstructions[j - 1]->mSrc[1].mMemory == IM_INDIRECT) + { + CanSwapInstructions(mInstructions[j - 1], ins); + } + SwapInstructions(mInstructions[j - 1], ins); + mInstructions[j] = mInstructions[j - 1]; + j--; + } + if (i != j) + mInstructions[j] = ins; + } + else if (mInstructions[i]->mCode == IC_BINARY_OPERATOR && mInstructions[i]->mSrc[0].mTemp >= 0 && mInstructions[i]->mSrc[0].mFinal && mInstructions[i]->mSrc[1].mTemp >= 0 && mInstructions[i]->mSrc[1].mFinal) + { + InterInstruction* ins(mInstructions[i]); + int j = i; + while (j > 0 && CanSwapInstructions(mInstructions[j - 1], ins)) + { + SwapInstructions(mInstructions[j - 1], ins); + mInstructions[j] = mInstructions[j - 1]; + j--; + } + if (i != j) + mInstructions[j] = ins; + } + else if (mInstructions[i]->mCode == IC_LOAD && mInstructions[i]->mSrc[0].mMemory == IM_INDIRECT && mInstructions[i]->mSrc[0].mFinal && mInstructions[i]->mDst.mType == IT_INT8) + { + InterInstruction* ins(mInstructions[i]); + int j = i; + while (j > 0 && CanSwapInstructions(mInstructions[j - 1], ins)) + { + SwapInstructions(mInstructions[j - 1], ins); + mInstructions[j] = mInstructions[j - 1]; + j--; + } + if (i != j) + mInstructions[j] = ins; + } + + i++; + } + + CheckFinalLocal(); +#endif + + // Bubble up unrelated instructions + i = mInstructions.Size() - 2; + while (i >= 0) + { + if (mInstructions[i]->mCode == IC_LOAD || + mInstructions[i]->mCode == IC_UNARY_OPERATOR || + mInstructions[i]->mCode == IC_CONVERSION_OPERATOR || + mInstructions[i]->mCode == IC_BINARY_OPERATOR && (mInstructions[i]->mSrc[0].mTemp < 0 || mInstructions[i]->mSrc[1].mTemp < 0)) + { + InterInstruction* ins(mInstructions[i]); + + if (mInstructions[i + 1]->mCode == IC_CONSTANT && CanSwapInstructions(ins, mInstructions[i + 1])) + { + if (mInstructions[i + 2]->mNumOperands >= 1 && mInstructions[i + 2]->mSrc[0].mTemp == mInstructions[i]->mDst.mTemp && mInstructions[i + 2]->mSrc[0].mFinal || + mInstructions[i + 2]->mNumOperands == 2 && mInstructions[i + 2]->mSrc[1].mTemp == mInstructions[i]->mDst.mTemp && mInstructions[i + 2]->mSrc[1].mFinal) + { + SwapInstructions(ins, mInstructions[i + 1]); + mInstructions[i] = mInstructions[i + 1]; + mInstructions[i + 1] = ins; + } + } + } + i--; + } + +#if 1 + do {} while (PeepholeReplaceOptimization(staticVars, staticProcs)); +#endif + + limit = mInstructions.Size() - 1; + if (limit >= 0 && mInstructions[limit]->mCode == IC_BRANCH) + { + limit--; + if (limit > 0 && mInstructions[limit]->mCode == IC_RELATIONAL_OPERATOR) + limit--; + } + +#if 1 + i = limit; + while (i >= 0) + { + // move non indirect loads down + if (mInstructions[i]->mCode == IC_LOAD && (mInstructions[i]->mSrc[0].mMemory != IM_INDIRECT || mInstructions[i]->mDst.mType != IT_INT8 || !mInstructions[i]->mSrc[0].mFinal)) + { + InterInstruction* ins(mInstructions[i]); + int j = i; + while (j < limit && CanSwapInstructions(ins, mInstructions[j + 1])) + { + SwapInstructions(ins, mInstructions[j + 1]); + mInstructions[j] = mInstructions[j + 1]; + j++; + } + if (i != j) + mInstructions[j] = ins; + } + else if (mInstructions[i]->mCode == IC_CONSTANT || (mInstructions[i]->mCode == IC_LEA && mInstructions[i]->mSrc[0].mTemp == -1)) + { + InterInstruction* ins(mInstructions[i]); + int j = i; + while (j < limit && CanSwapInstructions(ins, mInstructions[j + 1])) + { + SwapInstructions(ins, mInstructions[j + 1]); + mInstructions[j] = mInstructions[j + 1]; + j++; + } + if (i != j) + mInstructions[j] = ins; + } + + i--; + } +#endif + + CheckFinalLocal(); + + do {} while (PeepholeReplaceOptimization(staticVars, staticProcs)); + + // build trains +#if 1 + for(int i = mInstructions.Size() - 1; i > 0; i--) + { + InterInstruction* tins = mInstructions[i]; + + int ti = i; + + j = i - 1; + while (j >= 0 && !tins->ReferencesTemp(mInstructions[j]->mDst.mTemp)) + j--; + while (j >= 0) + { + InterInstruction* jins = mInstructions[j]; + + if (j < ti - 1) + { + if (CanMoveInstructionDown(j, ti)) + { + for (int k = j; k < ti - 1; k++) + { + SwapInstructions(jins, mInstructions[k + 1]); + mInstructions[k] = mInstructions[k + 1]; + } + mInstructions[ti - 1] = jins; + + if (jins->NumUsedTemps() <= 1 && !(jins->mCode == IC_CALL || jins->mCode == IC_CALL_NATIVE)) + ti--; + // mInstructions.Insert(i, mInstructions[j]); + // mInstructions.Remove(j); + } + } + else if (jins->NumUsedTemps() <= 1 && !(jins->mCode == IC_CALL || jins->mCode == IC_CALL_NATIVE)) + ti--; + + j--; + while (j >= 0 && !tins->ReferencesTemp(mInstructions[j]->mDst.mTemp)) + j--; + } + } +#endif + CheckFinalLocal(); + + // check move calls our of range to save register spilling + i = 0; + while (i < mInstructions.Size()) + { + InterInstruction* ins(mInstructions[i]); + if (ins->mCode == IC_CALL || ins->mCode == IC_CALL_NATIVE) + { + int j = i; + while (j > 0 && CanSwapInstructions(ins, mInstructions[j - 1]) && TempUseDelta(mInstructions[j - 1]) >= 0) + j--; + while (j < i && TempUseDelta(mInstructions[j]) == 0) + j++; + + if (j < i) + { + while (i > j) + { + i--; + SwapInstructions(mInstructions[i], ins); + mInstructions[i + 1] = mInstructions[i]; + } + mInstructions[j] = ins; + } + } + i++; + } + + // sort stores up + + bool changed; + int retries = 10; + do + { + changed = false; + retries--; + + for (int i = 0; i + 1 < mInstructions.Size(); i++) + { + if (mInstructions[i + 0]->mCode == IC_STORE && mInstructions[i + 1]->mCode == IC_STORE && + !mInstructions[i + 0]->mVolatile && !mInstructions[i + 1]->mVolatile && + !CollidingMem(mInstructions[i + 0], mInstructions[i + 1]) && + SameMemRegion(mInstructions[i + 0]->mSrc[1], mInstructions[i + 1]->mSrc[1]) && + + (mInstructions[i + 0]->mSrc[1].mVarIndex > mInstructions[i + 1]->mSrc[1].mVarIndex || + mInstructions[i + 0]->mSrc[1].mVarIndex == mInstructions[i + 1]->mSrc[1].mVarIndex && + mInstructions[i + 0]->mSrc[1].mIntConst > mInstructions[i + 1]->mSrc[1].mIntConst)) + { + SwapInstructions(mInstructions[i + 0], mInstructions[i + 1]); + InterInstruction* ins = mInstructions[i + 1]; + mInstructions[i + 1] = mInstructions[i + 0]; + mInstructions[i + 0] = ins; + changed = true; + } +#if 1 + else if (i + 2 < mInstructions.Size() && mInstructions[i + 0]->mCode == IC_STORE && mInstructions[i + 2]->mCode == IC_STORE && + mInstructions[i + 1]->mCode == IC_LOAD && + CanSwapInstructions(mInstructions[i + 0], mInstructions[i + 1]) && + mInstructions[i + 1]->mDst.mTemp == mInstructions[i + 2]->mSrc[0].mTemp && mInstructions[i + 2]->mSrc[0].mFinal && + !mInstructions[i + 0]->mVolatile && !mInstructions[i + 2]->mVolatile && + SameMemRegion(mInstructions[i + 0]->mSrc[1], mInstructions[i + 2]->mSrc[1]) && + + (mInstructions[i + 0]->mSrc[1].mVarIndex > mInstructions[i + 2]->mSrc[1].mVarIndex || + mInstructions[i + 0]->mSrc[1].mVarIndex == mInstructions[i + 2]->mSrc[1].mVarIndex && + mInstructions[i + 0]->mSrc[1].mIntConst > mInstructions[i + 2]->mSrc[1].mIntConst)) + { + InterInstruction* ins = mInstructions[i + 0]; + SwapInstructions(ins, mInstructions[i + 1]); + mInstructions[i + 0] = mInstructions[i + 1]; + SwapInstructions(ins, mInstructions[i + 2]); + mInstructions[i + 1] = mInstructions[i + 2]; + mInstructions[i + 2] = ins; + + changed = true; + } +#endif + } + + } while (changed && retries); + + // Move conversion from float to int upwards + for (int i = 1; i < mInstructions.Size(); i++) + { + InterInstruction* ins(mInstructions[i]); + if (ins->mCode == IC_CONVERSION_OPERATOR && (ins->mOperator == IA_FLOAT2INT || ins->mOperator == IA_FLOAT2UINT || ins->mOperator == IA_FLOAT2LINT || ins->mOperator == IA_FLOAT2LUINT) && ins->mSrc[0].mFinal) + { + int j = i - 1; + while (j > 0 && CanSwapInstructions(ins, mInstructions[j])) + { + SwapInstructions(mInstructions[j], ins); + mInstructions[j + 1] = mInstructions[j]; + j--; + } + mInstructions[j + 1] = ins; + } + } + + bool moved = false; + do { + moved = false; + + // move div up to mod + int imod = -1, idiv = -1; + for (int i = 0; i < mInstructions.Size(); i++) + { + InterInstruction* ins = mInstructions[i]; + if (ins->mCode == IC_BINARY_OPERATOR && ins->mOperator == IA_MODU) + { + if (imod >= 0 && i + 1 < mInstructions.Size() && + mInstructions[i + 1]->mCode == IC_BINARY_OPERATOR && mInstructions[i + 1]->mOperator == IA_DIVU && + ins->mSrc[0].IsEqual(mInstructions[i + 1]->mSrc[0]) && ins->mSrc[1].IsEqual(mInstructions[i + 1]->mSrc[1])) + { + } + else + { + imod = -1; + if (idiv >= 0 && ins->mSrc[0].IsEqual(mInstructions[idiv]->mSrc[0]) && ins->mSrc[1].IsEqual(mInstructions[idiv]->mSrc[1])) + { + int j = i - 1; + while (j > idiv && CanSwapInstructions(mInstructions[j], ins)) + { + SwapInstructions(mInstructions[j], ins); + mInstructions[j + 1] = mInstructions[j]; + j--; + moved = true; + } + mInstructions[j + 1] = ins; + } + else + imod = i; + } + } + else if (ins->mCode == IC_BINARY_OPERATOR && ins->mOperator == IA_DIVU) + { + if (idiv >= 0 && i + 1 < mInstructions.Size() && + mInstructions[i + 1]->mCode == IC_BINARY_OPERATOR && mInstructions[i + 1]->mOperator == IA_MODU && + ins->mSrc[0].IsEqual(mInstructions[i + 1]->mSrc[0]) && ins->mSrc[1].IsEqual(mInstructions[i + 1]->mSrc[1])) + { + } + else + { + idiv = -1; + + if (imod >= 0 && ins->mSrc[0].IsEqual(mInstructions[imod]->mSrc[0]) && ins->mSrc[1].IsEqual(mInstructions[imod]->mSrc[1])) + { + int j = i - 1; + while (j > imod && CanSwapInstructions(mInstructions[j], ins)) + { + SwapInstructions(mInstructions[j + 0], ins); + mInstructions[j + 1] = mInstructions[j]; + j--; + moved = true; + } + mInstructions[j + 1] = ins; + } + else + idiv = i; + } + } + } + + } while (moved); + +#if 1 + // move lea to load/store down + for (int i = 0; i < mInstructions.Size(); i++) + { + InterInstruction* ins = mInstructions[i]; + if (ins->mCode == IC_LEA && ((ins->mSrc[0].mTemp < 0 || !ins->mSrc[0].mFinal) || (ins->mSrc[1].mTemp < 0 || !ins->mSrc[1].mFinal))) + { + int j = i + 1; + while (j < mInstructions.Size() && CanSwapInstructions(ins, mInstructions[j])) + j++; + if (j > i + 1 && j < mInstructions.Size() && + (mInstructions[j]->mCode == IC_LOAD && mInstructions[j]->mSrc[0].mTemp == ins->mDst.mTemp || + mInstructions[j]->mCode == IC_STORE && mInstructions[j]->mSrc[1].mTemp == ins->mDst.mTemp)) + { + for (int k = i; k < j - 1; k++) + { + SwapInstructions(ins, mInstructions[k + 1]); + mInstructions[k] = mInstructions[k + 1]; + } + mInstructions[j - 1] = ins; + } + } + + } +#endif + // Check complex comparison, that may be simpler when using operands + if (mFalseJump && mInstructions.Size() >= 3) + { + int nins = mInstructions.Size(); + if (mInstructions[nins - 1]->mCode == IC_BRANCH && mInstructions[nins - 1]->mSrc[0].mFinal && + mInstructions[nins - 2]->mCode == IC_RELATIONAL_OPERATOR && mInstructions[nins - 2]->mDst.mTemp == mInstructions[nins - 1]->mSrc[0].mTemp && + (mInstructions[nins - 2]->mOperator == IA_CMPEQ || mInstructions[nins - 2]->mOperator == IA_CMPNE)) + { + InterInstruction* rins = mInstructions[nins - 2]; + InterInstruction* ains = mInstructions[nins - 3]; + + if (rins->mSrc[0].mTemp < 0 && ains->mDst.mTemp == rins->mSrc[1].mTemp) + { + if (rins->mSrc[0].mType == IT_POINTER && rins->mSrc[1].mType == IT_POINTER && + rins->mSrc[0].mMemory == rins->mSrc[1].mMemoryBase && + ains->mCode == IC_LEA && + ains->mSrc[1].mTemp < 0 && SameMem(ains->mSrc[1], rins->mSrc[0])) + { + rins->mSrc[1] = ains->mSrc[1]; + ains->mSrc[1].mFinal = false; + rins->mSrc[0].mType = IT_INT16; + rins->mSrc[0].mIntConst = 0; + } + } + else if (rins->mSrc[1].mTemp < 0 && ains->mDst.mTemp == rins->mSrc[0].mTemp) + { + if (rins->mSrc[0].mType == IT_POINTER && rins->mSrc[1].mType == IT_POINTER && + rins->mSrc[1].mMemory == rins->mSrc[0].mMemoryBase && + ains->mCode == IC_LEA && + ains->mSrc[1].mTemp < 0 && SameMem(ains->mSrc[1], rins->mSrc[1])) + { + rins->mSrc[0] = ains->mSrc[0]; + ains->mSrc[0].mFinal = false; + rins->mSrc[1].mType = IT_INT16; + rins->mSrc[1].mIntConst = 0; + } + else if (rins->mSrc[0].mType == IT_INT16 && + rins->mSrc[0].IsInRange(0, 65534) && + ains->mCode == IC_BINARY_OPERATOR && ains->mOperator == IA_MUL) + { + if (ains->mSrc[0].mTemp < 0 && rins->mSrc[1].mIntConst % ains->mSrc[0].mIntConst == 0) + { + rins->mSrc[0] = ains->mSrc[1]; + ains->mSrc[1].mFinal = false; + rins->mSrc[1].mIntConst /= ains->mSrc[0].mIntConst; + } + else if (ains->mSrc[1].mTemp < 0 && rins->mSrc[1].mIntConst % ains->mSrc[1].mIntConst == 0) + { + rins->mSrc[0] = ains->mSrc[0]; + ains->mSrc[0].mFinal = false; + rins->mSrc[1].mIntConst /= ains->mSrc[1].mIntConst; + } + } + } + } + } + + // Check case of cmp signed immediate + if (mFalseJump && mInstructions.Size() > 3) + { + int nins = mInstructions.Size(); + if (mInstructions[nins - 1]->mCode == IC_BRANCH && + mInstructions[nins - 2]->mCode == IC_RELATIONAL_OPERATOR && mInstructions[nins - 2]->mDst.mTemp == mInstructions[nins - 1]->mSrc[0].mTemp && + mInstructions[nins - 2]->mOperator == IA_CMPLS && mInstructions[nins - 2]->mSrc[0].mTemp < 0) + { + int j = nins - 2; + while (j >= 0 && mInstructions[j]->mDst.mTemp != mInstructions[nins - 2]->mSrc[1].mTemp) + j--; + if (j >= 0 && mInstructions[j]->mCode == IC_LOAD_TEMPORARY) + { + int si = mInstructions[j]->mSrc[0].mTemp, di = mInstructions[j]->mDst.mTemp, ioffset = 0; + + InterInstruction* ains = nullptr; + + int k = j + 1; + while (k < nins - 2) + { + InterInstruction* ins = mInstructions[k]; + if (ins->mDst.mTemp == si) + { + if (ins->mCode == IC_BINARY_OPERATOR && ins->mOperator == IA_ADD && ins->mSrc[0].mTemp < 0 && ins->mSrc[1].mTemp == si) + { + ioffset += int(ins->mSrc[0].mIntConst); + ains = ins; + } + else + break; + } + + k++; + } + + if (k == nins - 2) + { + if (ains) + { + mInstructions[nins - 2]->mSrc[1] = ains->mDst; + mInstructions[nins - 2]->mSrc[0].mIntConst += ioffset; + } + } + } + } + } + +#if 1 + // Check orientation of float loop around + if (mLoopHead && (mTrueJump == this || mFalseJump == this)) + { + int fi = 0; + while (fi < mInstructions.Size() && !(mInstructions[fi]->mDst.mTemp >= 0 && mInstructions[fi]->mDst.mType == IT_FLOAT)) + fi++; + if (fi < mInstructions.Size() && mInstructions[fi]->mCode == IC_BINARY_OPERATOR && + (mInstructions[fi]->mOperator == IA_ADD || mInstructions[fi]->mOperator == IA_MUL) && + mInstructions[fi]->mSrc[0].mTemp >= 0 && mInstructions[fi]->mSrc[1].mTemp >= 0) + { + int fj = mInstructions.Size() - 1; + while (fj >= fi && !(mInstructions[fj]->mDst.mTemp >= 0 && mInstructions[fj]->mDst.mType == IT_FLOAT)) + fj--; + + if (fj >= fi) + { + if (mInstructions[fi]->mSrc[1].mTemp == mInstructions[fj]->mDst.mTemp) + { + mInstructions[fi]->mLockOrder = true; + } + else if (mInstructions[fi]->mSrc[0].mTemp == mInstructions[fj]->mDst.mTemp) + { + mInstructions[fi]->mLockOrder = true; + InterOperand op = mInstructions[fi]->mSrc[0]; + mInstructions[fi]->mSrc[0] = mInstructions[fi]->mSrc[1]; + mInstructions[fi]->mSrc[1] = op; + } + else + mInstructions[fi]->mLockOrder = false; + } + } + } +#endif + CheckFinalLocal(); + + do {} while (PeepholeReplaceOptimization(staticVars, staticProcs)); + + CheckFinalLocal(); + + if (mTrueJump) mTrueJump->PeepholeOptimization(staticVars, staticProcs); + if (mFalseJump) mFalseJump->PeepholeOptimization(staticVars, staticProcs); + } +} + +void InterCodeBasicBlock::CheckNullptrDereference(void) +{ + if (!mVisited) + { + mVisited = true; + + for (int i = 0; i < mInstructions.Size(); i++) + { + InterInstruction* ins = mInstructions[i]; + if (ins->mCode == IC_LOAD && ins->mSrc[0].mTemp < 0 && ins->mSrc[0].mMemory == IM_ABSOLUTE && ins->mSrc[0].mIntConst == 0 && !ins->mVolatile) + mProc->mModule->mErrors->Error(ins->mLocation, EWARN_NULL_POINTER_DEREFERENCED, "nullptr dereferenced"); + else if (ins->mCode == IC_STORE && ins->mSrc[1].mTemp < 0 && ins->mSrc[1].mMemory == IM_ABSOLUTE && ins->mSrc[1].mIntConst == 0 && !ins->mVolatile) + mProc->mModule->mErrors->Error(ins->mLocation, EWARN_NULL_POINTER_DEREFERENCED, "nullptr dereferenced"); + } + + if (mTrueJump) mTrueJump->CheckNullptrDereference(); + if (mFalseJump) mFalseJump->CheckNullptrDereference(); + } +} + +void InterCodeBasicBlock::CheckValueReturn(void) +{ + if (!mVisited) + { + mVisited = true; + + for (int i = 0; i < mInstructions.Size(); i++) + { + InterInstruction* ins = mInstructions[i]; + if (ins->mCode == IC_ASSEMBLER || ins->mCode == IC_DISPATCH) + return; + else if (ins->mCode == IC_RETURN) + mProc->mModule->mErrors->Error(ins->mLocation, EWARN_MISSING_RETURN_STATEMENT, "Missing return statement"); + } + + if (mTrueJump) mTrueJump->CheckValueReturn(); + if (mFalseJump) mFalseJump->CheckValueReturn(); + } +} + +void InterCodeBasicBlock::MarkAliasing(const NumberSet& aliasedParams) +{ + if (!mVisited) + { + mVisited = true; + + for (int i = 0; i < mInstructions.Size(); i++) + { + InterInstruction* ins = mInstructions[i]; + + if (ins->mCode == IC_LOAD || ins->mCode == IC_COPY) + { + if ((ins->mSrc[0].mMemory == IM_PARAM || ins->mSrc[0].mMemory == IM_FPARAM) && aliasedParams[ins->mSrc[0].mVarIndex]) + ins->mAliasing = true; + } + + if (ins->mCode == IC_STORE || ins->mCode == IC_FILL || ins->mCode == IC_COPY) + { + if ((ins->mSrc[1].mMemory == IM_PARAM || ins->mSrc[1].mMemory == IM_FPARAM) && aliasedParams[ins->mSrc[1].mVarIndex]) + ins->mAliasing = true; + } + } + + if (mTrueJump) mTrueJump->MarkAliasing(aliasedParams); + if (mFalseJump) mFalseJump->MarkAliasing(aliasedParams); + } +} + +void InterCodeBasicBlock::CollectGlobalReferences(NumberSet& referencedGlobals, NumberSet& modifiedGlobals, bool& storesIndirect, bool& loadsIndirect, bool& globalsChecked) +{ + if (!mVisited) + { + mVisited = true; + + for (int i = 0; i < mInstructions.Size(); i++) + { + InterInstruction* ins = mInstructions[i]; + + switch (ins->mCode) + { + case IC_LOAD: + if (ins->mSrc[0].mTemp < 0 && ins->mSrc[0].mMemory == IM_GLOBAL && ins->mSrc[0].mVarIndex >= 0) + referencedGlobals += ins->mSrc[0].mVarIndex; + else if (ins->mSrc[0].mMemoryBase == IM_GLOBAL && ins->mSrc[0].mVarIndex >= 0) + referencedGlobals += ins->mSrc[0].mVarIndex; + else if (ins->mSrc[0].mTemp >= 0 && (ins->mSrc[0].mMemoryBase == IM_NONE || ins->mSrc[0].mMemoryBase == IM_INDIRECT)) + loadsIndirect = true; + break; + case IC_STORE: + if (ins->mSrc[1].mTemp < 0 && ins->mSrc[1].mMemory == IM_GLOBAL && ins->mSrc[1].mVarIndex >= 0) + { + referencedGlobals += ins->mSrc[1].mVarIndex; + modifiedGlobals += ins->mSrc[1].mVarIndex; + } + else if (ins->mSrc[1].mMemoryBase == IM_GLOBAL && ins->mSrc[1].mVarIndex >= 0) + { + referencedGlobals += ins->mSrc[1].mVarIndex; + modifiedGlobals += ins->mSrc[1].mVarIndex; + } + else if (ins->mSrc[1].mTemp >= 0 && (ins->mSrc[1].mMemoryBase == IM_NONE || ins->mSrc[1].mMemoryBase == IM_INDIRECT)) + storesIndirect = true; + break; + case IC_FILL: + if (ins->mSrc[1].mTemp < 0 && ins->mSrc[1].mMemory == IM_GLOBAL && ins->mSrc[1].mVarIndex >= 0) + { + referencedGlobals += ins->mSrc[1].mVarIndex; + modifiedGlobals += ins->mSrc[1].mVarIndex; + } + else if (ins->mSrc[1].mMemoryBase == IM_GLOBAL && ins->mSrc[1].mVarIndex >= 0) + { + referencedGlobals += ins->mSrc[1].mVarIndex; + modifiedGlobals += ins->mSrc[1].mVarIndex; + } + else if (ins->mSrc[1].mTemp >= 0 && (ins->mSrc[1].mMemoryBase == IM_NONE || ins->mSrc[1].mMemoryBase == IM_INDIRECT)) + storesIndirect = true; + break; + case IC_COPY: + case IC_STRCPY: + if (ins->mSrc[0].mTemp < 0 && ins->mSrc[0].mMemory == IM_GLOBAL && ins->mSrc[0].mVarIndex >= 0) + referencedGlobals += ins->mSrc[0].mVarIndex; + else if (ins->mSrc[0].mMemoryBase == IM_GLOBAL && ins->mSrc[0].mVarIndex >= 0) + referencedGlobals += ins->mSrc[0].mVarIndex; + else if (ins->mSrc[0].mTemp >= 0 && (ins->mSrc[0].mMemoryBase == IM_NONE || ins->mSrc[0].mMemoryBase == IM_INDIRECT)) + loadsIndirect = true; + if (ins->mSrc[1].mTemp < 0 && ins->mSrc[1].mMemory == IM_GLOBAL && ins->mSrc[1].mVarIndex >= 0) + { + referencedGlobals += ins->mSrc[1].mVarIndex; + modifiedGlobals += ins->mSrc[1].mVarIndex; + } + else if (ins->mSrc[1].mMemoryBase == IM_GLOBAL && ins->mSrc[1].mVarIndex >= 0) + { + referencedGlobals += ins->mSrc[1].mVarIndex; + modifiedGlobals += ins->mSrc[1].mVarIndex; + } + else if (ins->mSrc[1].mTemp >= 0 && (ins->mSrc[1].mMemoryBase == IM_NONE || ins->mSrc[1].mMemoryBase == IM_INDIRECT)) + storesIndirect = true; + break; + case IC_ASSEMBLER: + for (int i = 1; i < ins->mNumOperands; i++) + { + if (ins->mSrc[i].mType == IT_POINTER && ins->mSrc[i].mTemp < 0) + { + storesIndirect = true; + loadsIndirect = true; + } + } + break; + case IC_CALL: + case IC_CALL_NATIVE: + if (ins->mSrc[0].mTemp < 0 && ins->mSrc[0].mLinkerObject && ins->mSrc[0].mLinkerObject->mProc) + { + InterCodeProcedure* proc = ins->mSrc[0].mLinkerObject->mProc; + if (proc->mGlobalsChecked) + { + if (proc->mStoresIndirect) + storesIndirect = true; + if (proc->mLoadsIndirect) + loadsIndirect = true; + referencedGlobals |= proc->mReferencedGlobals; + modifiedGlobals |= proc->mModifiedGlobals; + } + else + globalsChecked = false; + } + else + globalsChecked = false; + break; + case IC_DISPATCH: + { + for (int j = 0; j < mProc->mCalledFunctions.Size(); j++) + { + InterCodeProcedure* proc = mProc->mCalledFunctions[j]; + + if (proc->mGlobalsChecked) + { + if (proc->mStoresIndirect) + storesIndirect = true; + if (proc->mLoadsIndirect) + loadsIndirect = true; + referencedGlobals |= proc->mReferencedGlobals; + modifiedGlobals |= proc->mModifiedGlobals; + } + else + globalsChecked = false; + } + } + break; + } + } + + if (mTrueJump) mTrueJump->CollectGlobalReferences(referencedGlobals, modifiedGlobals, storesIndirect, loadsIndirect, globalsChecked); + if (mFalseJump) mFalseJump->CollectGlobalReferences(referencedGlobals, modifiedGlobals, storesIndirect, loadsIndirect, globalsChecked); + } +} + +void InterCodeBasicBlock::WarnInvalidValueRanges(void) +{ + if (!mVisited) + { + mVisited = true; + + for (int i = 0; i < mInstructions.Size(); i++) + { + InterInstruction* ins = mInstructions[i]; + + for (int j = 0; j < ins->mNumOperands; j++) + { + if (ins->mSrc[j].mTemp >= 0 && ins->mSrc[j].mRange.IsInvalid()) + { + mProc->mModule->mErrors->Error(ins->mLocation, EWARN_INVALID_VALUE_RANGE, "Invalid value range"); + } + } + } + + if (mTrueJump) mTrueJump->WarnInvalidValueRanges(); + if (mFalseJump) mFalseJump->WarnInvalidValueRanges(); + } +} + + +void InterCodeBasicBlock::WarnUsedUndefinedVariables(void) +{ + if (!mVisited) + { + mVisited = true; + + NumberSet providedTemps(mEntryProvidedTemps), potentialTemps(mEntryPotentialTemps); + + for (int i = 0; i < mInstructions.Size(); i++) + { + InterInstruction* ins = mInstructions[i]; + + for (int j = 0; j < ins->mNumOperands; j++) + { + if (ins->mSrc[j].mTemp >= 0 && !providedTemps[ins->mSrc[j].mTemp]) + { + int t = ins->mSrc[j].mTemp; + + int k = 0; + while (k < mProc->mLocalVars.Size() && !(mProc->mLocalVars[k] && mProc->mLocalVars[k]->mTempIndex == t)) + k++; + + if (potentialTemps[t]) + { + if (k < mProc->mLocalVars.Size() && mProc->mLocalVars[k]->mIdent) + mProc->mModule->mErrors->Error(ins->mLocation, EWARN_USE_OF_UNINITIALIZED_VARIABLE, "Use of potentially uninitialized variable", mProc->mLocalVars[k]->mIdent); + else + mProc->mModule->mErrors->Error(ins->mLocation, EWARN_USE_OF_UNINITIALIZED_VARIABLE, "Use of potentially uninitialized expression"); + } + else + { + if (k < mProc->mLocalVars.Size() && mProc->mLocalVars[k]->mIdent) + mProc->mModule->mErrors->Error(ins->mLocation, EWARN_USE_OF_UNINITIALIZED_VARIABLE, "Use of uninitialized variable", mProc->mLocalVars[k]->mIdent); + else + mProc->mModule->mErrors->Error(ins->mLocation, EWARN_USE_OF_UNINITIALIZED_VARIABLE, "Use of uninitialized expression"); + + if (ins->mCode == IC_LOAD_TEMPORARY) + { + ins->mCode = IC_CONSTANT; + ins->mConst = ins->mSrc[j]; + ins->mConst.mTemp = -1; + ins->mConst.mIntConst = 0; + ins->mConst.mLinkerObject = nullptr; + ins->mConst.mVarIndex = -1; + ins->mConst.mMemory = IM_ABSOLUTE; + ins->mNumOperands = 0; + } + else + { + ins->mSrc[j].mTemp = -1; + ins->mSrc[j].mIntConst = 0; + } + } + } + } + + if (ins->mDst.mTemp >= 0) + providedTemps += ins->mDst.mTemp; + } + + if (mTrueJump) mTrueJump->WarnUsedUndefinedVariables(); + if (mFalseJump) mFalseJump->WarnUsedUndefinedVariables(); + } +} + +void InterCodeBasicBlock::CollectVariables(GrowingVariableArray& globalVars, GrowingVariableArray& localVars, GrowingVariableArray& paramVars, InterMemory paramMemory) +{ + int i; + + if (!mVisited) + { + mVisited = true; + + for (i = 0; i < mInstructions.Size(); i++) + { + bool found = false; + + InterInstruction* ins = mInstructions[i]; + + switch (ins->mCode) + { + case IC_CONSTANT: + if (ins->mConst.mMemory == IM_LOCAL) + { + int varIndex = ins->mConst.mVarIndex; + if (!localVars[varIndex]) + localVars[varIndex] = new InterVariable; + + int size = int(ins->mConst.mOperandSize + ins->mConst.mIntConst); + if (size > localVars[varIndex]->mSize) + localVars[varIndex]->mSize = size; + localVars[varIndex]->mAliased = true; + } + else if (ins->mConst.mMemory == paramMemory) + { + int varIndex = ins->mConst.mVarIndex; + if (!paramVars[varIndex]) + paramVars[varIndex] = new InterVariable; + + int size = int(ins->mConst.mOperandSize + ins->mConst.mIntConst); + if (size > paramVars[varIndex]->mSize) + paramVars[varIndex]->mSize = size; + paramVars[varIndex]->mAliased = true; + } + break; + + case IC_LEA: + if (ins->mSrc[1].mMemory == IM_LOCAL) + { + int varIndex = ins->mSrc[1].mVarIndex; + if (!localVars[varIndex]) + localVars[varIndex] = new InterVariable; + + int size = int(ins->mSrc[1].mOperandSize + ins->mSrc[1].mIntConst); + if (size > localVars[varIndex]->mSize) + localVars[varIndex]->mSize = size; + localVars[varIndex]->mAliased = true; + } + else if (ins->mSrc[1].mMemory == paramMemory) + { + int varIndex = ins->mSrc[1].mVarIndex; + if (!paramVars[varIndex]) + paramVars[varIndex] = new InterVariable; + + int size = int(ins->mSrc[1].mOperandSize + ins->mSrc[1].mIntConst); + if (size > paramVars[varIndex]->mSize) + paramVars[varIndex]->mSize = size; + paramVars[varIndex]->mAliased = true; + } + break; + + case IC_STORE: + case IC_LOAD: + case IC_COPY: + case IC_FILL: + case IC_STRCPY: + case IC_CALL_NATIVE: + case IC_ASSEMBLER: + + for(int j=0; jmNumOperands; j++) + { + if (ins->mSrc[j].mMemory == IM_LOCAL) + { + int varIndex = ins->mSrc[j].mVarIndex; + if (!localVars[varIndex]) + localVars[varIndex] = new InterVariable; + + int size = int(ins->mSrc[j].mOperandSize + ins->mSrc[j].mIntConst); + if (size > localVars[varIndex]->mSize) + localVars[varIndex]->mSize = size; + } + else if (ins->mSrc[j].mMemory == IM_FPARAM || ins->mSrc[j].mMemory == IM_PARAM) + { + int varIndex = ins->mSrc[j].mVarIndex; + if (!paramVars[varIndex]) + paramVars[varIndex] = new InterVariable; + + int size = int(ins->mSrc[j].mOperandSize + ins->mSrc[j].mIntConst); + if (size > paramVars[varIndex]->mSize) + paramVars[varIndex]->mSize = size; + } + } + break; + } + } + + if (mTrueJump) mTrueJump->CollectVariables(globalVars, localVars, paramVars, paramMemory); + if (mFalseJump) mFalseJump->CollectVariables(globalVars, localVars, paramVars, paramMemory); + } +} + +void InterCodeBasicBlock::CollectSimpleLocals(FastNumberSet& complexLocals, FastNumberSet& simpleLocals, GrowingTypeArray& localTypes, FastNumberSet& complexParams, FastNumberSet& simpleParams, GrowingTypeArray& paramTypes) +{ + int i; + + if (!mVisited) + { + mVisited = true; + + for (i = 0; i < mInstructions.Size(); i++) + { + mInstructions[i]->CollectSimpleLocals(complexLocals, simpleLocals, localTypes, complexParams, simpleParams, paramTypes); + } + + if (mTrueJump) mTrueJump->CollectSimpleLocals(complexLocals, simpleLocals, localTypes, complexParams, simpleParams, paramTypes); + if (mFalseJump) mFalseJump->CollectSimpleLocals(complexLocals, simpleLocals, localTypes, complexParams, simpleParams, paramTypes); + } +} + +void InterCodeBasicBlock::SimpleLocalToTemp(int vindex, int temp) +{ + int i; + + if (!mVisited) + { + mVisited = true; + + for (i = 0; i < mInstructions.Size(); i++) + { + mInstructions[i]->SimpleLocalToTemp(vindex, temp); + } + + if (mTrueJump) mTrueJump->SimpleLocalToTemp(vindex, temp); + if (mFalseJump) mFalseJump->SimpleLocalToTemp(vindex, temp); + } + +} + +void InterCodeBasicBlock::CollectActiveTemporaries(FastNumberSet& set) +{ + int i; + + if (!mVisited) + { + mVisited = true; + + for (i = 0; i < mInstructions.Size(); i++) + { + mInstructions[i]->CollectActiveTemporaries(set); + } + + if (mTrueJump) mTrueJump->CollectActiveTemporaries(set); + if (mFalseJump) mFalseJump->CollectActiveTemporaries(set); + } +} + +void InterCodeBasicBlock::RemapActiveTemporaries(const FastNumberSet& set) +{ + if (!mVisited) + { + mVisited = true; + + GrowingIntegerValueRangeArray entryValueRange(mEntryValueRange); + GrowingIntegerValueRangeArray trueValueRange(mTrueValueRange); + GrowingIntegerValueRangeArray falseValueRange(mFalseValueRange); +// GrowingIntegerValueRangeArray localValueRange(mLocalValueRange); +// GrowingIntegerValueRangeArray reverseValueRange(mReverseValueRange); + GrowingArray memoryValueSize(mMemoryValueSize); + + mEntryValueRange.SetSize(set.Num(), true); + mTrueValueRange.SetSize(set.Num(), true); + mFalseValueRange.SetSize(set.Num(), true); +// mLocalValueRange.SetSize(set.Num(), true); +// mReverseValueRange.SetSize(set.Num(), true); + mMemoryValueSize.SetSize(set.Num(), true); + + for (int i = 0; i < set.Num(); i++) + { + int j = set.Element(i); + if (j < entryValueRange.Size()) + { + mEntryValueRange[i] = entryValueRange[j]; + mTrueValueRange[i] = trueValueRange[j]; + mFalseValueRange[i] = falseValueRange[j]; + // mLocalValueRange[i] = localValueRange[j]; + // mReverseValueRange[i] = reverseValueRange[j]; + mMemoryValueSize[i] = memoryValueSize[j]; + } + } + + if (mTrueJump) mTrueJump->RemapActiveTemporaries(set); + if (mFalseJump) mFalseJump->RemapActiveTemporaries(set); + } +} + +void InterCodeBasicBlock::ShrinkActiveTemporaries(FastNumberSet& set, GrowingTypeArray& temporaries) +{ + int i; + + if (!mVisited) + { + mVisited = true; + + for (i = 0; i < mInstructions.Size(); i++) + { + mInstructions[i]->ShrinkActiveTemporaries(set, temporaries); + } + + if (mTrueJump) mTrueJump->ShrinkActiveTemporaries(set, temporaries); + if (mFalseJump) mFalseJump->ShrinkActiveTemporaries(set, temporaries); + } +} + +void InterCodeBasicBlock::Disassemble(FILE* file, bool dumpSets) +{ + int i; + + if (!mVisited) + { + mVisited = true; + + const char* s = mLoopHead ? "Head" : ""; + + fprintf(file, "L%d: <= D%d: (%d) %s P%d", mIndex, (mDominator ? mDominator->mIndex : -1), mNumEntries, s, (mLoopPrefix ? mLoopPrefix->mIndex : -1)); + if (mInstructions.Size()) + fprintf(file, " %s\n", mInstructions[0]->mLocation.mFileName); + else + fprintf(file, "\n"); + + if (dumpSets) + { + fprintf(file, "Entry required temps : "); + for (i = 0; i < mEntryRequiredTemps.Size(); i++) + { + if (mEntryRequiredTemps[i]) + fprintf(file, "#"); + else if (i % 5 == 0) + fprintf(file, "+"); + else + fprintf(file, "-"); + } + fprintf(file, "\n\n"); + fprintf(file, "Exit required temps : "); + for (i = 0; i < mExitRequiredTemps.Size(); i++) + { + if (mExitRequiredTemps[i]) + fprintf(file, "#"); + else if (i % 5 == 0) + fprintf(file, "+"); + else + fprintf(file, "-"); + } + fprintf(file, "\n\n"); + fprintf(file, "Entry provided temps : "); + for (i = 0; i < mEntryProvidedTemps.Size(); i++) + { + if (mEntryProvidedTemps[i]) + fprintf(file, "#"); + else if (i % 5 == 0) + fprintf(file, "+"); + else + fprintf(file, "-"); + } + fprintf(file, "\n\n"); + fprintf(file, "Exit provided temps : "); + for (i = 0; i < mExitProvidedTemps.Size(); i++) + { + if (mExitProvidedTemps[i]) + fprintf(file, "#"); + else if (i % 5 == 0) + fprintf(file, "+"); + else + fprintf(file, "-"); + } + fprintf(file, "\n\n"); + } + + for (i = 0; i < mInstructions.Size(); i++) + { + if (mInstructions[i]->mCode != IC_NONE) + { + fprintf(file, "%04x (%4d)\t", i, mInstructions[i]->mLocation.mLine); + mInstructions[i]->Disassemble(file, mProc); + } + } + + if (mTrueJump) fprintf(file, "\t\t==> %d\n", mTrueJump->mIndex); + if (mFalseJump) fprintf(file, "\t\t==> %d\n", mFalseJump->mIndex); + + if (mTrueJump) mTrueJump->Disassemble(file, dumpSets); + if (mFalseJump) mFalseJump->Disassemble(file, dumpSets); + } +} + + +void InterCodeBasicBlock::WarnUnreachable(void) +{ + if (mNumEntries == 0 && mProc->mCheckUnreachable) + { + int i = 0; + while (i < mInstructions.Size() && !IsObservable(mInstructions[i]->mCode)) + i++; + if (i < mInstructions.Size()) + mProc->mModule->mErrors->Error(mInstructions[i]->mLocation, EWARN_UNREACHABLE_CODE, "Unreachable code"); + } +} + + + +InterCodeProcedure::InterCodeProcedure(InterCodeModule * mod, const Location & location, const Ident* ident, LinkerObject * linkerObject) + : mTemporaries(IT_NONE), mBlocks(nullptr), mLocation(location), mTempOffset(-1), mTempSizes(0), mNumBlocks(0), + mRenameTable(-1), mRenameUnionTable(-1), mGlobalRenameTable(-1), + mValueForwardingTable(nullptr), mLocalVars(nullptr), mParamVars(nullptr), mModule(mod), + mIdent(ident), mLinkerObject(linkerObject), + mNativeProcedure(false), mLeafProcedure(false), mCallsFunctionPointer(false), mCalledFunctions(nullptr), mFastCallProcedure(false), + mInterrupt(false), mHardwareInterrupt(false), mCompiled(false), mInterruptCalled(false), mDynamicStack(false), mAssembled(false), + mSaveTempsLinkerObject(nullptr), mValueReturn(false), mFramePointer(false), + mCheckUnreachable(true), mReturnType(IT_NONE), mCheapInline(false), mNoInline(false), + mDeclaration(nullptr), mGlobalsChecked(false), mDispatchedCall(false), + mIntrinsicFunction(false), + mNumRestricted(1) +{ + mID = mModule->mProcedures.Size(); + mModule->mProcedures.Push(this); + mLinkerObject->mProc = this; + mLinkerObject->mFlags |= LOBJF_CONST; + mCallerSavedTemps = BC_REG_TMP_SAVED - BC_REG_TMP; +} + +InterCodeProcedure::~InterCodeProcedure(void) +{ +} + +void InterCodeProcedure::ResetEntryBlocks(void) +{ + for (int i = 0; i < mBlocks.Size(); i++) + mBlocks[i]->mEntryBlocks.SetSize(0); +} + +void InterCodeProcedure::ResetPatched(void) +{ + for (int i = 0; i < mBlocks.Size(); i++) + mBlocks[i]->mPatched = false; +} + +void InterCodeProcedure::ResetVisited(void) +{ + int i; + + for (i = 0; i < mBlocks.Size(); i++) + { +#if _DEBUG + for (int j = 0; j < mBlocks[i]->mInstructions.Size(); j++) + { + InterInstruction* ins = mBlocks[i]->mInstructions[j]; + assert(!ins || ins->mCode != IC_LOAD_TEMPORARY || ins->mSrc[0].mTemp >= 0); + } +#endif + + +#if 0 + if (mBlocks[i]->mInstructions.Size() > 0) + { + const InterInstruction* ins(mBlocks[i]->mInstructions.Last()); + if (ins) + { + if (ins->mCode == IC_BRANCH) + { + assert(mBlocks[i]->mTrueJump && mBlocks[i]->mFalseJump && ins->mNumOperands >= 1 && ins->mSrc[0].mTemp >= 0); + } + else if (ins->mCode == IC_JUMP) + { + assert(mBlocks[i]->mTrueJump); + } + } + } +#endif + + mBlocks[i]->mVisited = false; + mBlocks[i]->mChecked = false; + mBlocks[i]->mNumEntered = 0; + } +} + +int InterCodeProcedure::AddTemporary(InterType type) +{ + assert(type != IT_NONE); + + int temp = mTemporaries.Size(); + mTemporaries.Push(type); + return temp; +} + +int InterCodeProcedure::AddRestricted(void) +{ + return mNumRestricted++; +} + +void InterCodeProcedure::CheckBlocks(void) +{ + ResetVisited(); + mEntryBlock->CheckBlocks(); +} + +bool InterCodeProcedure::PropagateByteIndexPointers(void) +{ + bool changed = false; + + ResetVisited(); + changed = mEntryBlock->PropagateByteIndexPointers(); + + DisassembleDebug("PropagateByteIndexPointers"); + + return changed; +} + +bool InterCodeProcedure::ReplaceByteIndexPointers(FastNumberSet& activeSet) +{ + activeSet.Clear(); + + ResetVisited(); + mEntryBlock->CollectActiveTemporaries(activeSet); + + int silvused = activeSet.Num(); + if (silvused != mTemporaries.Size()) + { + mTemporaries.SetSize(silvused, true); + + ResetVisited(); + mEntryBlock->ShrinkActiveTemporaries(activeSet, mTemporaries); + + mLocalValueRange.SetSize(silvused, true); + + ResetVisited(); + mEntryBlock->RemapActiveTemporaries(activeSet); + } + + NumberSet invtemps(silvused), inctemps(silvused); + GrowingIntArray vartemps(-1); + + ResetVisited(); + mEntryBlock->CollectByteIndexPointers(invtemps, inctemps, vartemps); + inctemps -= invtemps; +#if 0 + for (int i = 0; i < silvused; i++) + { + if (inctemps[i]) + { + if (mModule->mGlobalVars[vartemps[i]]->mIdent) + printf("Inctemp %d, %s[%d]\n", i, mModule->mGlobalVars[vartemps[i]]->mIdent->mString, mModule->mGlobalVars[vartemps[i]]->mSize); + else + printf("Inctemp %d, V%d[%d]\n", i, vartemps[i], mModule->mGlobalVars[vartemps[i]]->mSize); + } + } +#endif + ResetVisited(); + if (mEntryBlock->ReplaceByteIndexPointers(inctemps, vartemps, silvused)) + { + activeSet.Clear(); + + ResetVisited(); + mEntryBlock->CollectActiveTemporaries(activeSet); + + silvused = activeSet.Num(); + if (silvused != mTemporaries.Size()) + { + mTemporaries.SetSize(activeSet.Num(), true); + + ResetVisited(); + mEntryBlock->ShrinkActiveTemporaries(activeSet, mTemporaries); + + mLocalValueRange.SetSize(activeSet.Num(), true); + + ResetVisited(); + mEntryBlock->RemapActiveTemporaries(activeSet); + } + } + + return false; +} + +void InterCodeProcedure::ForwardSingleAssignmentBools(void) +{ + int numTemps = mTemporaries.Size(); + + FastNumberSet tassigned(numTemps); + GrowingInstructionPtrArray tvalues(nullptr); + + tvalues.SetSize(numTemps); + + ResetVisited(); + mEntryBlock->CollectSingleAssignmentBools(tassigned, tvalues); + + ResetVisited(); + mEntryBlock->CheckSingleAssignmentBools(tvalues); + + for (int i = 0; i < tassigned.Num(); i++) + { + int t = tassigned.Element(i); + if (tvalues[t]) + { + tvalues[t]->mCode = IC_LOAD_TEMPORARY; + tvalues[t]->mNumOperands = 1; + if (tvalues[t]->mSrc[0].mTemp >= 0) + tvalues[t]->mDst = tvalues[t]->mSrc[0]; + else + { + tvalues[t]->mDst = tvalues[t]->mSrc[1]; + tvalues[t]->mSrc[0] = tvalues[t]->mSrc[1]; + } + tvalues[t]->mDst.mTemp = t; + } + } + + ResetVisited(); + mEntryBlock->MapSingleAssignmentBools(tvalues); + + DisassembleDebug("ForwardSingleAssignmentBools"); + + BuildDataFlowSets(); + TempForwarding(); + RemoveUnusedInstructions(); +} + +void InterCodeProcedure::StructReturnPropagation(void) +{ + if (!mEntryBlock->mTrueJump) + { + mEntryBlock->StructReturnPropagation(); + } +} + +void InterCodeProcedure::CollapseDispatch(void) +{ + ResetVisited(); + mEntryBlock->CollapseDispatch(); +} + +void InterCodeProcedure::CheckFinal(void) +{ + ResetVisited(); + mEntryBlock->CheckFinal(); +} + +void InterCodeProcedure::DisassembleDebug(const char* name, bool dumpSets) +{ + Disassemble(name, dumpSets); +} + +void InterCodeProcedure::RebuildIntegerRangeSet(void) +{ + mLocalValueRange.SetSize(mTemporaries.Size(), false); + + ExpandingInstructionPtrArray tarr; + ResetVisited(); + mEntryBlock->PropagateValueRangeSetConversions(tarr); + + DisassembleDebug("BeforeRebuildIntegerRangeSet"); + + ResetVisited(); + mEntryBlock->RestartLocalIntegerRangeSets(mTemporaries.Size()); + + // No need to re-init the loop specific parts, we are restarting. + // Would lead to infinite pumping weak - bound in some cases +#if 1 + int limit = 10; + do { + DisassembleDebug("tr0"); + + limit--; + ResetVisited(); + } while (mEntryBlock->BuildGlobalIntegerRangeSets(true) && limit > 0); +#endif + do { + DisassembleDebug("tr1"); + + ResetVisited(); + } while (mEntryBlock->BuildGlobalIntegerRangeSets(false)); + + assert(mTemporaries.Size() == mLocalValueRange.Size()); + + ResetVisited(); + mEntryBlock->UpdateLinearCombinations(); + + DisassembleDebug("Estimated value range 2"); +} + +void InterCodeProcedure::EarlyBranchElimination(void) +{ + GrowingInstructionPtrArray ctemps(nullptr); + + ResetVisited(); + while (mEntryBlock->EarlyBranchElimination(ctemps)) + { + BuildTraces(10); + TrimBlocks(); + } +} + +void InterCodeProcedure::BuildTraces(int expand, bool dominators, bool compact) +{ + // Count number of entries + // + ResetVisited(); + for (int i = 0; i < mBlocks.Size(); i++) + { + mBlocks[i]->mNumEntries = 0; + mBlocks[i]->mLoopHead = false; + mBlocks[i]->mLoopDebug = false; + mBlocks[i]->mTraceIndex = -1; + } + mEntryBlock->CollectEntries(); + + // + // Build traces + // + ResetVisited(); + mEntryBlock->GenerateTraces(expand, compact); + + ResetVisited(); + for (int i = 0; i < mBlocks.Size(); i++) + mBlocks[i]->mNumEntries = 0; + mEntryBlock->CollectEntries(); + + ResetEntryBlocks(); + ResetVisited(); + mEntryBlock->CollectEntryBlocks(nullptr); + + ResetVisited(); + for (int i = 0; i < mBlocks.Size(); i++) + mBlocks[i]->mDominator = nullptr; + if (dominators) + mEntryBlock->BuildDominatorTree(nullptr); +} + +void InterCodeProcedure::TrimBlocks(void) +{ + return; + + int j = 1; + for (int i = 1; i < mBlocks.Size(); i++) + { + InterCodeBasicBlock* block = mBlocks[i]; + if (block->mNumEntries > 0) + { + if (block->mLoopPrefix && block->mLoopPrefix->mNumEntries == 0) + block->mLoopPrefix = nullptr; + if (block->mDominator && block->mDominator->mNumEntries == 0) + block->mDominator = nullptr; + + block->mIndex = j; + mBlocks[j++] = block; + } + else + delete block; + } + mBlocks.SetSize(j, false); + mNumBlocks = j; +} + +void InterCodeProcedure::BuildDataFlowSets(void) +{ + int numTemps = mTemporaries.Size(); + + // + // Build set with local provided/required temporaries + // + ResetVisited(); + mEntryBlock->BuildLocalTempSets(numTemps); + + // + // Build set of globally provided temporaries + // + ResetVisited(); + mEntryBlock->BuildGlobalProvidedTempSet(NumberSet(numTemps), NumberSet(numTemps)); + + // + // Build set of globally required temporaries, might need + // multiple iterations until it stabilizes + // + NumberSet totalRequired(numTemps); + + do { + ResetVisited(); + } while (mEntryBlock->BuildGlobalRequiredTempSet(totalRequired)); + + ResetVisited(); + mEntryBlock->CollectLocalUsedTemps(numTemps); +} + +void InterCodeProcedure::RenameTemporaries(void) +{ + int numTemps = mTemporaries.Size(); + + // + // Now we can rename temporaries to remove false dependencies + // + mRenameTable.SetSize(numTemps, true); + + int i, j, numRename; + + numRename = 0; + + // + // First locally rename all temporaries + // + ResetVisited(); + mEntryBlock->LocalRenameRegister(mRenameTable, numRename); + + DisassembleDebug("local renamed temps"); + + // + // Build a union find data structure for rename merging, this + // merges renames temporaries back, that have been renamed differently + // on separate paths. + // + mRenameUnionTable.SetSize(numRename); + for (i = 0; i < numRename; i++) + mRenameUnionTable[i] = i; + + // + // Build global rename table using a union/find algorithm + // + mRenameTable.SetSize(numTemps, true); + + ResetVisited(); + mEntryBlock->BuildGlobalRenameRegisterTable(mRenameTable, mRenameUnionTable); + + // + // Now calculate the global temporary IDs for all local ids + // + int numRenamedTemps; + + mGlobalRenameTable.SetSize(numRename, true); + + numRenamedTemps = 0; + + for (i = 0; i < numRename; i++) + { + j = Find(mRenameUnionTable, i); + + if (mGlobalRenameTable[j] < 0) + mGlobalRenameTable[j] = numRenamedTemps++; + + mGlobalRenameTable[i] = mGlobalRenameTable[j]; + } + + mTemporaries.SetSize(numRenamedTemps, true); + + // + // Set global temporary IDs + // + ResetVisited(); + mEntryBlock->GlobalRenameRegister(mGlobalRenameTable, mTemporaries); + + numTemps = numRenamedTemps; + + DisassembleDebug("global renamed temps"); +} + +void InterCodeProcedure::SingleAssignmentForwarding(void) +{ + int numTemps = mTemporaries.Size(); + + InterMemory paramMemory = mFastCallProcedure ? IM_FPARAM : IM_PARAM; + + FastNumberSet tassigned(numTemps); + GrowingInstructionPtrArray tunified(nullptr), tvalues(nullptr); + NumberSet modifiedParams(mParamAliasedSet.Size()); + + tunified.SetSize(numTemps); + + bool changed; + do + { + ResetVisited(); + changed = mEntryBlock->CalculateSingleAssignmentTemps(tassigned, tunified, modifiedParams, paramMemory); + } while (changed); + + for (int i = 0; i < numTemps; i++) + { + if (tunified[i]) + tunified[i]->mSingleAssignment = true; + } + + tunified.Clear(); + ResetVisited(); + mEntryBlock->SingleAssignmentTempForwarding(tunified, tvalues); + + DisassembleDebug("single assignment forwarding"); + + BuildDataFlowSets(); + TempForwarding(); + RemoveUnusedInstructions(); + +} + +void InterCodeProcedure::PeepholeOptimization(void) +{ + BuildDataFlowSets(); + TempForwarding(); + RemoveUnusedInstructions(); + + Disassemble("Precheck Final"); + CheckFinal(); + + ResetVisited(); + mEntryBlock->PeepholeOptimization(mModule->mGlobalVars, mModule->mProcedures); + + Disassemble("PeepholeOptimization"); + CheckFinal(); +} + + +void InterCodeProcedure::CheckUsedDefinedTemps(void) +{ +#if _DEBUG + int numTemps = mTemporaries.Size(); + + NumberSet defined(numTemps), used(numTemps); + + ResetVisited(); + mEntryBlock->CollectAllUsedDefinedTemps(defined, used); + + for (int i = 0; i < numTemps; i++) + { + assert(!used[i] || defined[i]); + } + +#endif +} + +void InterCodeProcedure::ShortcutConstBranches(void) +{ + GrowingInstructionPtrArray cins(nullptr); + + BuildDataFlowSets(); + TempForwarding(); + RemoveUnusedInstructions(); + + do { + Disassemble("PreShortcutConstBranches"); + ResetVisited(); + } while (mEntryBlock->ShortcutConstBranches(cins)); + + Disassemble("ShortcutConstBranches"); +} + +void InterCodeProcedure::ShortcutDuplicateBranches(void) +{ + BuildDataFlowSets(); + TempForwarding(); + RemoveUnusedInstructions(); + + do { + ResetVisited(); + } while (mEntryBlock->ShortcutDuplicateBranches()); + + Disassemble("ShortcutDuplicateBranches"); +} + + +void InterCodeProcedure::MoveConditionsOutOfLoop(void) +{ + BuildTraces(0); + BuildLoopPrefix(); + + Disassemble("PreMoveConditionOutOfLoop"); + + ResetVisited(); + while (mEntryBlock->MoveConditionOutOfLoop()) + { + Disassemble("MoveConditionOutOfLoop"); + + BuildTraces(0); + + BuildDataFlowSets(); + TempForwarding(); + RemoveUnusedInstructions(); + + BuildTraces(0); + BuildLoopPrefix(); + + ResetVisited(); + mEntryBlock->InnerLoopOptimization(mParamAliasedSet); + + Disassemble("PostMoveConditionOutOfLoop"); + + ResetVisited(); + } +} + +void InterCodeProcedure::EliminateDoubleLoopCounter(void) +{ + BuildTraces(0); + BuildLoopPrefix(); + + ResetVisited(); + mEntryBlock->EliminateDoubleLoopCounter(); +} + +void InterCodeProcedure::LimitLoopIndexIntegerRangeSets(void) +{ + BuildTraces(0); + BuildLoopPrefix(); + + ResetVisited(); + mEntryBlock->LimitLoopIndexIntegerRangeSets(); +} + +void InterCodeProcedure::PropagateMemoryAliasingInfo(bool loops) +{ + GrowingInstructionPtrArray tvalue(nullptr); + + if (loops) + { + BuildTraces(0); + BuildLoopPrefix(); + } + + ResetVisited(); + mEntryBlock->PropagateMemoryAliasingInfo(tvalue, loops); + + Disassemble("PropagateMemoryAliasingInfo"); +} + + +void InterCodeProcedure::WarnUsedUndefinedVariables(void) +{ + ResetEntryBlocks(); + ResetVisited(); + mEntryBlock->CollectEntryBlocks(nullptr); + + ResetVisited(); + mEntryBlock->WarnUsedUndefinedVariables(); +} + +void InterCodeProcedure::WarnInvalidValueRanges(void) +{ + ResetEntryBlocks(); + ResetVisited(); + mEntryBlock->CollectEntryBlocks(nullptr); + + ResetVisited(); + mEntryBlock->WarnInvalidValueRanges(); +} + + +void InterCodeProcedure::TempForwarding(bool reverse, bool checkloops) +{ + int numTemps = mTemporaries.Size(); + + CheckUsedDefinedTemps(); + + ValueSet valueSet; + FastNumberSet tvalidSet(numTemps); + + if (checkloops) + { + BuildLoopPrefix(); + } + + DisassembleDebug("pre temp forwarding"); + + // + // Now remove needless temporary moves, that appear due to + // stack evaluation + // + mTempForwardingTable.SetSize(numTemps); + + mTempForwardingTable.Reset(); + ResetVisited(); + mEntryBlock->PerformTempForwarding(mTempForwardingTable, reverse, checkloops); + + if (checkloops) + DisassembleDebug("loop temp forwarding"); + else + DisassembleDebug("temp forwarding"); +} + + +void InterCodeProcedure::RemoveUnusedInstructions(void) +{ + int numTemps = mTemporaries.Size(); + + do { + ResetVisited(); + mEntryBlock->BuildLocalTempSets(numTemps); + + ResetVisited(); + mEntryBlock->BuildGlobalProvidedTempSet(NumberSet(numTemps), NumberSet(numTemps)); + + NumberSet totalRequired2(numTemps); + + do { + ResetVisited(); + } while (mEntryBlock->BuildGlobalRequiredTempSet(totalRequired2)); + + ResetVisited(); + } while (mEntryBlock->RemoveUnusedResultInstructions()); +} + +void InterCodeProcedure::RemoveUnusedLocalStoreInstructions(void) +{ + if (mCompilerOptions & COPT_OPTIMIZE_BASIC) + { + ResetVisited(); + mEntryBlock->RemoveUnusedLocalStoreInstructions(); + } +} + +void InterCodeProcedure::RemoveUndefinedPartialLocalInstructions(void) +{ + int byteIndex = 0; + for (int i = 0; i < mLocalVars.Size(); i++) + { + if (mLocalVars[i]) + { + mLocalVars[i]->mByteIndex = byteIndex; + byteIndex += mLocalVars[i]->mSize; + } + } + + do { + ResetVisited(); + mEntryBlock->BuildLocalVariableByteSet(mLocalVars, byteIndex); + + ResetVisited(); + mEntryBlock->BuildGlobalProvidedStaticVariableSet(mLocalVars, NumberSet(byteIndex)); + + NumberSet totalRequired2(byteIndex); + + do { + ResetVisited(); + } while (mEntryBlock->BuildGlobalRequiredStaticVariableSet(mLocalVars, totalRequired2)); + + ResetVisited(); + } while (mEntryBlock->RemoveUndefinedPartialLocalInstructions(mLocalVars, byteIndex)); + + DisassembleDebug("removed undefined local byte instructions"); +} + +void InterCodeProcedure::RemoveUnusedPartialStoreInstructions(void) +{ + if (mCompilerOptions & COPT_OPTIMIZE_BASIC) + { + if (mModule->mGlobalVars.Size()) + { + int byteIndex = 0; + for (int i = 0; i < mModule->mGlobalVars.Size(); i++) + { + if (mModule->mGlobalVars[i]) + { + mModule->mGlobalVars[i]->mByteIndex = byteIndex; + byteIndex += mModule->mGlobalVars[i]->mSize; + } + } + + do { + ResetVisited(); + mEntryBlock->BuildStaticVariableByteSet(mModule->mGlobalVars, byteIndex); + + ResetVisited(); + mEntryBlock->BuildGlobalProvidedStaticVariableSet(mModule->mGlobalVars, NumberSet(byteIndex)); + + NumberSet totalRequired2(byteIndex); + + do { + ResetVisited(); + } while (mEntryBlock->BuildGlobalRequiredStaticVariableSet(mModule->mGlobalVars, totalRequired2)); + + ResetVisited(); + } while (mEntryBlock->RemoveUnusedStaticStoreByteInstructions(mModule->mGlobalVars, byteIndex)); + + DisassembleDebug("removed unused static byte stores"); + } + } +} + +void InterCodeProcedure::RemoveUnusedStoreInstructions(InterMemory paramMemory) +{ + if (mCompilerOptions & COPT_OPTIMIZE_BASIC) + { + if (mLocalVars.Size() > 0 || mParamVars.Size() > 0) + { +#if 0 + for (int i = 0; i < mLocalAliasedSet.Size(); i++) + { + if (mLocalVars[i]) + mLocalVars[i]->mAliased = mLocalAliasedSet[i]; + } + for (int i = 0; i < mParamAliasedSet.Size(); i++) + { + if (mParamVars[i]) + mParamVars[i]->mAliased = mParamAliasedSet[i]; + } +#endif + // + // Now remove unused stores + // + + do { + ResetVisited(); + mEntryBlock->BuildLocalVariableSets(mLocalVars, mParamVars, paramMemory); + + ResetVisited(); + mEntryBlock->BuildGlobalProvidedVariableSet(mLocalVars, NumberSet(mLocalVars.Size()), mParamVars, NumberSet(mParamVars.Size()), paramMemory); + + NumberSet totalRequired2(mLocalVars.Size()); + NumberSet totalRequiredParams(mParamVars.Size()); + + do { + ResetVisited(); + } while (mEntryBlock->BuildGlobalRequiredVariableSet(mLocalVars, totalRequired2, mParamVars, totalRequiredParams, paramMemory)); + + ResetVisited(); + } while (mEntryBlock->RemoveUnusedStoreInstructions(mLocalVars, mParamVars, paramMemory)); + + DisassembleDebug("removed unused local stores"); + } + + // Remove unused global stores + + if (mModule->mGlobalVars.Size()) + { + do { + ResetVisited(); + mEntryBlock->BuildStaticVariableSet(mModule->mGlobalVars); + + ResetVisited(); + mEntryBlock->BuildGlobalProvidedStaticVariableSet(mModule->mGlobalVars, NumberSet(mModule->mGlobalVars.Size())); + + NumberSet totalRequired2(mModule->mGlobalVars.Size()); + + do { + ResetVisited(); + } while (mEntryBlock->BuildGlobalRequiredStaticVariableSet(mModule->mGlobalVars, totalRequired2)); + + ResetVisited(); + } while (mEntryBlock->RemoveUnusedStaticStoreInstructions(mModule->mGlobalVars)); + + DisassembleDebug("removed unused static stores"); + } + + // Remove unused indirect stores + + ResetVisited(); + mEntryBlock->RemoveUnusedIndirectStoreInstructions(); + + DisassembleDebug("RemoveUnusedIndirectStoreInstructions"); + } +} + +void InterCodeProcedure::MergeCommonPathInstructions(void) +{ + bool changed; + do + { + ResetVisited(); + mEntryBlock->CompactInstructions(); + + BuildDataFlowSets(); + + ResetVisited(); + changed = mEntryBlock->MergeCommonPathInstructions(); + + DisassembleDebug("Merged common path part"); + + if (changed) + { + TempForwarding(); + RemoveUnusedInstructions(); + + } + + } while (changed); + + DisassembleDebug("Merged common path instructions"); +} + +void InterCodeProcedure::PushSinglePathResultInstructions(void) +{ + bool changed; + do + { + BuildDataFlowSets(); + + ResetVisited(); + changed = mEntryBlock->PushSinglePathResultInstructions(); + + DisassembleDebug("Pushed single path result"); + + } while (changed); +} + +void InterCodeProcedure::CollectVariables(InterMemory paramMemory) +{ + for (int i = 0; i < mLocalVars.Size(); i++) + if (mLocalVars[i]) + mLocalVars[i]->mAliased = false; + for (int i = 0; i < mParamVars.Size(); i++) + if (mParamVars[i]) + mParamVars[i]->mAliased = false; + + ResetVisited(); + mEntryBlock->CollectVariables(mModule->mGlobalVars, mLocalVars, mParamVars, paramMemory); +} + +void InterCodeProcedure::PromoteSimpleLocalsToTemp(InterMemory paramMemory, int nlocals, int nparams) +{ + for (int j = 0; j < 2; j++) + { + CollectVariables(paramMemory); + + RemoveUnusedStoreInstructions(paramMemory); + + // + // Promote local variables to temporaries + // + + FastNumberSet simpleLocals(nlocals), complexLocals(nlocals); + GrowingTypeArray localTypes(IT_NONE); + + FastNumberSet simpleParams(nparams), complexParams(nparams); + GrowingTypeArray paramTypes(IT_NONE); + + ResetVisited(); + mEntryBlock->CollectSimpleLocals(complexLocals, simpleLocals, localTypes, complexParams, simpleParams, paramTypes); + + for (int i = 0; i < simpleLocals.Num(); i++) + { + int vi = simpleLocals.Element(i); + if (!complexLocals[vi]) + { + mLocalVars[vi]->mTemp = true; + mLocalVars[vi]->mTempIndex = AddTemporary(localTypes[vi]); + ResetVisited(); + mEntryBlock->SimpleLocalToTemp(vi, mLocalVars[vi]->mTempIndex); + } + } + + DisassembleDebug("local variables to temps"); + + BuildTraces(0); + + BuildDataFlowSets(); + + WarnUsedUndefinedVariables(); + + DisassembleDebug("WarnUsedUndefinedVariables0", true); + + RenameTemporaries(); + + DisassembleDebug("PreGlobalConstantPropagation", true); + + do { + BuildDataFlowSets(); + + WarnUsedUndefinedVariables(); + + DisassembleDebug("WarnUsedUndefinedVariables1", true); + + TempForwarding(); + } while (GlobalConstantPropagation()); + + DisassembleDebug("GlobalConstantPropagation"); + + // + // Now remove unused instructions + // + + RemoveUnusedInstructions(); + + DisassembleDebug("removed unused instructions 2"); + + TempForwarding(); + } + + + ResetVisited(); + mEntryBlock->CompactInstructions(); +} + +void InterCodeProcedure::MergeIndexedLoadStore(void) +{ + GrowingInstructionPtrArray silvalues(nullptr); + + do + { + BuildDataFlowSets(); + + TempForwarding(); + RemoveUnusedInstructions(); + + silvalues.SetSize(mTemporaries.Size(), true); + + ResetVisited(); + } while (mEntryBlock->MergeIndexedLoadStore(silvalues)); + + BuildDataFlowSets(); + + TempForwarding(); + RemoveUnusedInstructions(); + + DisassembleDebug("MergeIndexedLoadStore"); + + ResetVisited(); + mEntryBlock->SimplifyPointerOffsets(); + + DisassembleDebug("SimplifyPointerOffsets"); +} + +void InterCodeProcedure::SingleBlockLoopPointerToByte(FastNumberSet& activeSet) +{ + int silvused = mTemporaries.Size(); + + do + { + mTemporaries.SetSize(silvused, true); + + DisassembleDebug("SingleBlockLoopPointerToByteA"); + + BuildDataFlowSets(); + + DisassembleDebug("SingleBlockLoopPointerToByteB"); + + TempForwarding(); + RemoveUnusedInstructions(); + + DisassembleDebug("SingleBlockLoopPointerToByteC"); + + activeSet.Clear(); + + ResetVisited(); + mEntryBlock->CollectActiveTemporaries(activeSet); + + silvused = activeSet.Num(); + if (silvused != mTemporaries.Size()) + { + mTemporaries.SetSize(activeSet.Num(), true); + + ResetVisited(); + mEntryBlock->ShrinkActiveTemporaries(activeSet, mTemporaries); + + mLocalValueRange.SetSize(activeSet.Num(), true); + + ResetVisited(); + mEntryBlock->RemapActiveTemporaries(activeSet); + } + + ResetVisited(); + } while (mEntryBlock->SingleBlockLoopPointerToByte(silvused)); + + assert(silvused == mTemporaries.Size()); + + DisassembleDebug("SingleBlockLoopPointerToByte"); + + +} + + +void InterCodeProcedure::SingleBlockLoopPointerSplit(FastNumberSet& activeSet) +{ + int silvused = mTemporaries.Size(); + + do + { + mTemporaries.SetSize(silvused, true); + + DisassembleDebug("SingleBlockLoopPointerSplitA"); + + BuildDataFlowSets(); + + DisassembleDebug("SingleBlockLoopPointerSplitB"); + + TempForwarding(); + RemoveUnusedInstructions(); + + DisassembleDebug("SingleBlockLoopPointerSplitC"); + + activeSet.Clear(); + + ResetVisited(); + mEntryBlock->CollectActiveTemporaries(activeSet); + + silvused = activeSet.Num(); + if (silvused != mTemporaries.Size()) + { + mTemporaries.SetSize(activeSet.Num(), true); + + ResetVisited(); + mEntryBlock->ShrinkActiveTemporaries(activeSet, mTemporaries); + + ResetVisited(); + mEntryBlock->RemapActiveTemporaries(activeSet); + } + + ResetVisited(); + } while (mEntryBlock->SingleBlockLoopPointerSplit(silvused)); + + assert(silvused == mTemporaries.Size()); + + DisassembleDebug("SingleBlockLoopPointerSplit"); + +} + + +void InterCodeProcedure::SingleBlockLoopSinking(FastNumberSet& activeSet) +{ + int silvused = mTemporaries.Size(); + + ResetVisited(); + mEntryBlock->BuildLoopSuffix(); + + do + { + mTemporaries.SetSize(silvused, true); + + DisassembleDebug("SingleBlockLoopSinkingA"); + + BuildDataFlowSets(); + + DisassembleDebug("SingleBlockLoopSinkingB"); + + TempForwarding(); + RemoveUnusedInstructions(); + + DisassembleDebug("SingleBlockLoopSinkingC"); + + activeSet.Clear(); + + ResetVisited(); + mEntryBlock->CollectActiveTemporaries(activeSet); + + silvused = activeSet.Num(); + if (silvused != mTemporaries.Size()) + { + mTemporaries.SetSize(activeSet.Num(), true); + + ResetVisited(); + mEntryBlock->ShrinkActiveTemporaries(activeSet, mTemporaries); + + ResetVisited(); + mEntryBlock->RemapActiveTemporaries(activeSet); + } + + ResetVisited(); + } while (mEntryBlock->SingleBlockLoopSinking(silvused)); + + assert(silvused == mTemporaries.Size()); + + DisassembleDebug("SingleBlockLoopSinking"); + +} + +void InterCodeProcedure::SingleBlockLoopIndexReduction(FastNumberSet& activeSet) +{ + int silvused = mTemporaries.Size(); + + ResetVisited(); + mEntryBlock->BuildLoopSuffix(); + + do + { + mTemporaries.SetSize(silvused, true); + + DisassembleDebug("SingleBlockLoopIndexReductionA"); + + BuildDataFlowSets(); + + DisassembleDebug("SingleBlockLoopIndexReductionB"); + + TempForwarding(); + RemoveUnusedInstructions(); + + DisassembleDebug("SingleBlockLoopIndexReductionC"); + + activeSet.Clear(); + + ResetVisited(); + mEntryBlock->CollectActiveTemporaries(activeSet); + + silvused = activeSet.Num(); + if (silvused != mTemporaries.Size()) + { + mTemporaries.SetSize(activeSet.Num(), true); + + ResetVisited(); + mEntryBlock->ShrinkActiveTemporaries(activeSet, mTemporaries); + + ResetVisited(); + mEntryBlock->RemapActiveTemporaries(activeSet); + } + + ResetVisited(); + } while (mEntryBlock->SingleBlockLoopIndexReduction(silvused)); + + assert(silvused == mTemporaries.Size()); + + DisassembleDebug("SingleBlockLoopIndexReduction"); + +} + +void InterCodeProcedure::SimplifyIntegerNumeric(FastNumberSet& activeSet) +{ + GrowingInstructionPtrArray silvalues(nullptr); + int silvused = mTemporaries.Size(); + + do + { + mTemporaries.SetSize(silvused, true); + + BuildDataFlowSets(); + + TempForwarding(); + RemoveUnusedInstructions(); + + activeSet.Clear(); + + ResetVisited(); + mEntryBlock->CollectActiveTemporaries(activeSet); + + silvused = activeSet.Num(); + silvalues.SetSize(silvused + 16, true); + + if (silvused != mTemporaries.Size()) + { + mTemporaries.SetSize(activeSet.Num(), true); + + ResetVisited(); + mEntryBlock->ShrinkActiveTemporaries(activeSet, mTemporaries); + + ResetVisited(); + mEntryBlock->RemapActiveTemporaries(activeSet); + } + + DisassembleDebug("PreSimplifyIntegerNumeric"); + + ResetVisited(); + + } while (mEntryBlock->SimplifyIntegerNumeric(silvalues, silvused)); + + assert(silvused == mTemporaries.Size()); + + DisassembleDebug("SimplifyIntegerNumeric"); +} + +void InterCodeProcedure::ExpandSelect(void) +{ +#if 1 + ResetVisited(); + mEntryBlock->ExpandSelect(); + + ResetVisited(); + for (int i = 0; i < mBlocks.Size(); i++) + mBlocks[i]->mNumEntries = 0; + mEntryBlock->CollectEntries(); + + ResetEntryBlocks(); + ResetVisited(); + mEntryBlock->CollectEntryBlocks(nullptr); + + DisassembleDebug("ExpandSelect"); +#endif +} + +void InterCodeProcedure::EliminateIntegerSumAliasTemps(void) +{ + GrowingInstructionPtrArray eivalues(nullptr); + do { + RemoveUnusedInstructions(); + + eivalues.SetSize(mTemporaries.Size(), true); + + ResetVisited(); + } while (mEntryBlock->EliminateIntegerSumAliasTemps(eivalues)); + + DisassembleDebug("EliminateIntegerSumAliasTemps"); +} + +void InterCodeProcedure::EliminateAliasValues() +{ + assert(mTemporaries.Size() == mLocalValueRange.Size()); + + GrowingInstructionPtrArray eivalues(nullptr); + do { + BuildDataFlowSets(); + + assert(mTemporaries.Size() == mLocalValueRange.Size()); + + eivalues.SetSize(mTemporaries.Size(), true); + + ResetVisited(); + } while (mEntryBlock->EliminateAliasValues(eivalues, eivalues)); + + DisassembleDebug("EliminateAliasValues"); +} + +void InterCodeProcedure::ReduceRecursionTempSpilling(InterMemory paramMemory) +{ + GrowingInstructionPtrArray gipa(nullptr); + ResetVisited(); + mEntryBlock->ReduceRecursionTempSpilling(paramMemory, gipa); + + DisassembleDebug("ReduceRecursionTempSpilling"); +} + +void InterCodeProcedure::LoadStoreForwarding(InterMemory paramMemory) +{ + BuildTraces(0); + + DisassembleDebug("Load/Store forwardingY"); + + bool changed; + do { + PropagateMemoryAliasingInfo(false); + + GrowingInstructionPtrArray gipa(nullptr); + ResetVisited(); + changed = mEntryBlock->LoadStoreForwarding(gipa, mModule->mGlobalVars); + + DisassembleDebug("Load/Store forwardingX"); + + RemoveUnusedStoreInstructions(paramMemory); + + GlobalConstantPropagation(); + RemoveUnusedMallocs(); + + TempForwarding(); + RemoveUnusedInstructions(); + + DisassembleDebug("Load/Store forwarding"); + } while (changed); + +} + +void InterCodeProcedure::CombineIndirectAddressing(void) +{ + ResetVisited(); + + mEntryBlock->CombineIndirectAddressing(); + BuildDataFlowSets(); +} + +void InterCodeProcedure::PropagateConstOperationsUp(void) +{ +#if 1 + ResetEntryBlocks(); + ResetVisited(); + mEntryBlock->CollectEntryBlocks(nullptr); + + BuildDataFlowSets(); + + bool changed; + do { + changed = false; + + ResetVisited(); + if (mEntryBlock->SplitSingleBranchUseConst()) + changed = true; + + DisassembleDebug("SplitSingleBranchUseConst"); + + ResetVisited(); + if (mEntryBlock->CommonTailCodeMerge()) + { + changed = true; + BuildDataFlowSets(); + } + + DisassembleDebug("CommonTailCodeMerge"); + + ResetVisited(); + mEntryBlock->BuildConstTempSets(); + + ResetVisited(); + if (mEntryBlock->PropagateConstOperationsUp()) + { + BuildDataFlowSets(); + + GlobalConstantPropagation(); + + TempForwarding(); + + RemoveUnusedInstructions(); + + changed = true; + + DisassembleDebug("prop const op up"); + } + } while (changed); +#endif +} + +void InterCodeProcedure::BuildLocalAliasTable(void) +{ + // + // Find all local variables that are never aliased + // + GrowingIntArray localTable(-1), paramTable(-1); + int nlocals = 0, nparams = 0; + + localTable.SetSize(mTemporaries.Size()); + paramTable.SetSize(mTemporaries.Size()); + + ResetVisited(); + mEntryBlock->CollectLocalAddressTemps(localTable, paramTable, nlocals, nparams); + + mLocalAliasedSet.Reset(nlocals); + mParamAliasedSet.Reset(nparams); + ResetVisited(); + mEntryBlock->MarkAliasedLocalTemps(localTable, mLocalAliasedSet, paramTable, mParamAliasedSet); + + Disassemble("Built alias temps"); +} + +void InterCodeProcedure::Close(void) +{ + GrowingTypeArray tstack(IT_NONE); + + CheckFunc = !strcmp(mIdent->mString, "equipment_random"); + CheckCase = false; + + mEntryBlock = mBlocks[0]; + + DisassembleDebug("start"); + + BuildTraces(10); + DisassembleDebug("traces"); + + EarlyBranchElimination(); + BuildTraces(0); + + do { + BuildLoopPrefix(); + ResetVisited(); + } while (mEntryBlock->StripLoopHead(10)); + DisassembleDebug("Loop Head"); + BuildTraces(0); + + + DisassembleDebug("branch elimination"); +#if 0 + do { + BuildLoopPrefix(); + ResetVisited(); + } while (mEntryBlock->StripLoopHead()); + DisassembleDebug("Loop Head"); + BuildTraces(0); +#endif + + ResetVisited(); + mLeafProcedure = mEntryBlock->IsLeafProcedure(); + + mHasDynamicStack = false; + mHasInlineAssembler = false; + mCallsByteCode = false; + if (!mLeafProcedure) + { + int size = 0; + + ResetVisited(); + mEntryBlock->CollectOuterFrame(0, size, mHasDynamicStack, mHasInlineAssembler, mCallsByteCode); + + if (mModule->mCompilerOptions & COPT_NATIVE) + mCallsByteCode = false; + mCommonFrameSize = size; + } + else + mCommonFrameSize = 0; + + BuildDataFlowSets(); + + RenameTemporaries(); + + BuildDataFlowSets(); + + TempForwarding(); + + int numTemps = mTemporaries.Size(); + + BuildLocalAliasTable(); + + ValueSet valueSet; + FastNumberSet tvalidSet(numTemps + 32); + + + bool eliminated; + int retries = 2; + // + // Now forward constant values + // + do { + valueSet.FlushAll(); + mValueForwardingTable.SetSize(numTemps, true); + tvalidSet.Reset(numTemps + 64); + + ResetVisited(); + mEntryBlock->PerformValueForwarding(mValueForwardingTable, valueSet, tvalidSet, mLocalAliasedSet, mParamAliasedSet, numTemps, mModule->mGlobalVars, mModule->mProcedures); + + assert(numTemps <= tvalidSet.Size()); + + DisassembleDebug("PerformValueForwarding"); + + ResetVisited(); + eliminated = mEntryBlock->EliminateDeadBranches(); + if (eliminated) + { + BuildTraces(0); + /* + ResetVisited(); + for (int i = 0; i < mBlocks.Size(); i++) + mBlocks[i]->mNumEntries = 0; + mEntryBlock->CollectEntries(); + */ + } + + mTemporaries.SetSize(numTemps, true); + + BuildDataFlowSets(); + + TempForwarding(); + retries--; + + } while (eliminated || retries > 0); + + + DisassembleDebug("value forwarding"); + + mValueForwardingTable.SetSize(numTemps, true); + mTemporaries.SetSize(numTemps, true); + + RemoveUnusedInstructions(); + + FastNumberSet fusedSet(numTemps), fsingleSet(numTemps); + ResetVisited(); + mEntryBlock->CalculateSingleUsedTemps(fusedSet, fsingleSet); + + ResetVisited(); + mEntryBlock->PerformMachineSpecificValueUsageCheck(mValueForwardingTable, tvalidSet, mModule->mGlobalVars, mModule->mProcedures, fsingleSet); + + DisassembleDebug("machine value forwarding"); + + GlobalConstantPropagation(); + + DisassembleDebug("Global Constant Propagation"); + + // Check for cheap inlining + // +#if 1 + if (mCompilerOptions & COPT_OPTIMIZE_INLINE) + { + ResetVisited(); + if (mEntryBlock->CheapInlining(numTemps)) + { + mValueForwardingTable.SetSize(numTemps, true); + mTemporaries.SetSize(numTemps, true); + + DisassembleDebug("Cheap Inlining"); + + BuildDataFlowSets(); + + if (mCommonFrameSize == 0) + { + int size = 0; + + ResetVisited(); + mEntryBlock->CollectOuterFrame(0, size, mHasDynamicStack, mHasInlineAssembler, mCallsByteCode); + mCommonFrameSize = size; + } + } + } +#endif + ResetVisited(); + mEntryBlock->RemoveUnusedArgumentStoreInstructions(); + + DisassembleDebug("RemoveUnusedArgumentStoreInstructions"); + + // + // + // Now remove needless temporary moves, that appear due to + // stack evaluation + // + mTempForwardingTable.Reset(); + mTempForwardingTable.SetSize(numTemps); + + ResetVisited(); + mEntryBlock->PerformTempForwarding(mTempForwardingTable, false, false); + + DisassembleDebug("temp forwarding 2"); + + // + // Now remove unused instructions + // + + RemoveUnusedInstructions(); + + DisassembleDebug("removed unused instructions"); + + InterMemory paramMemory = mFastCallProcedure ? IM_FPARAM : IM_PARAM; + + if (mCompilerOptions & COPT_OPTIMIZE_BASIC) + PromoteSimpleLocalsToTemp(paramMemory, mLocalAliasedSet.Size(), mParamAliasedSet.Size()); + else + CollectVariables(paramMemory); + + for (int i = 0; i < mLocalVars.Size(); i++) + if (i < mLocalAliasedSet.Size() && mLocalAliasedSet[i]) + mLocalVars[i]->mAliased = true; + + RecheckLocalAliased(); + + BuildDataFlowSets(); + + LoadStoreForwarding(paramMemory); + + RecheckLocalAliased(); + + RemoveUndefinedPartialLocalInstructions(); + + ResetVisited(); + mEntryBlock->OptimizeIntervalCompare(); + + ResetVisited(); + for (int i = 0; i < mBlocks.Size(); i++) + mBlocks[i]->mNumEntries = 0; + mEntryBlock->CollectEntries(); + + DisassembleDebug("interval compare"); + + MergeIndexedLoadStore(); + + PeepholeOptimization(); + + DisassembleDebug("Peephole optimized"); + + BuildLoopPrefix(); + DisassembleDebug("added dominators"); + + BuildDataFlowSets(); + + TempForwarding(); + RemoveUnusedInstructions(); + + DisassembleDebug("pre single block loop opt"); + + ResetVisited(); + mEntryBlock->SingleBlockLoopOptimisation(mParamAliasedSet, mModule->mGlobalVars); + + DisassembleDebug("single block loop opt"); + + BuildDataFlowSets(); + + RenameTemporaries(); + + BuildDataFlowSets(); + + ResetEntryBlocks(); + ResetVisited(); + mEntryBlock->CollectEntryBlocks(nullptr); + +#if 1 + ResetVisited(); + mEntryBlock->InnerLoopOptimization(mParamAliasedSet); + + DisassembleDebug("inner loop opt"); + + BuildDataFlowSets(); +#endif + CheckUsedDefinedTemps(); + + do { + BuildLoopPrefix(); + ResetVisited(); + } while (mEntryBlock->StripLoopHead(4)); + DisassembleDebug("Loop Head 2"); + BuildTraces(0); + + TrimBlocks(); + + BuildDataFlowSets(); + CheckUsedDefinedTemps(); + +#if 0 + ExpandSelect(); + + BuildDataFlowSets(); + + CheckUsedDefinedTemps(); +#endif + SingleAssignmentForwarding(); + + CheckUsedDefinedTemps(); + + PeepholeOptimization(); + + TempForwarding(); + RemoveUnusedInstructions(); + + DisassembleDebug("Peephole optimized"); + + + bool changed = false; + + PushSinglePathResultInstructions(); + + BuildDataFlowSets(); + TempForwarding(); + RemoveUnusedInstructions(); + + DisassembleDebug("Moved single path instructions"); + + PropagateNonLocalUsedTemps(); + + BuildDataFlowSets(); + TempForwarding(); + RemoveUnusedInstructions(); + + DisassembleDebug("propagate non local used temps"); + + MergeCommonPathInstructions(); + + do + { + GrowingInstructionPtrArray cipa(nullptr); + ResetVisited(); + changed = mEntryBlock->PropagateVariableCopy(cipa, mModule->mGlobalVars, mLocalAliasedSet, mParamAliasedSet); + + RemoveUnusedStoreInstructions(paramMemory); + } while (changed); + + DisassembleDebug("Copy forwarding"); + + LoadStoreForwarding(paramMemory); + + RecheckLocalAliased(); + + ResetVisited(); + mEntryBlock->FollowJumps(); + + DisassembleDebug("Followed Jumps"); + + FastNumberSet activeSet(numTemps); + + // + // And remove unused temporaries + // + + ResetVisited(); + mEntryBlock->CollectActiveTemporaries(activeSet); + + mTemporaries.SetSize(activeSet.Num(), true); + + ResetVisited(); + mEntryBlock->ShrinkActiveTemporaries(activeSet, mTemporaries); + + ResetEntryBlocks(); + ResetVisited(); + mEntryBlock->CollectEntryBlocks(nullptr); + +#if 1 + BuildDataFlowSets(); + + do { + Disassemble("gcp+"); + TempForwarding(); + } while (GlobalConstantPropagation()); + Disassemble("gcp-"); +#endif + + BuildTraces(0); + TrimBlocks(); + + DisassembleDebug("Rebuilt traces"); + + + BuildDataFlowSets(); + + TempForwarding(); + RemoveUnusedInstructions(); + + LimitLoopIndexRanges(); + DisassembleDebug("LimitLoopIndexRanges"); + +#if 1 + ExpandingInstructionPtrArray tarr; + ResetVisited(); + mEntryBlock->PropagateValueRangeSetConversions(tarr); + + ResetVisited(); + mEntryBlock->BuildLocalIntegerRangeSets(mTemporaries.Size()); + + do { + DisassembleDebug("tt"); + + ResetVisited(); + } while (mEntryBlock->BuildGlobalIntegerRangeSets(true)); + + TempForwarding(); + RemoveUnusedInstructions(); + + do { + DisassembleDebug("tq"); + + ResetVisited(); + } while (mEntryBlock->BuildGlobalIntegerRangeSets(false)); + + + DisassembleDebug("Estimated value range"); + + + GrowingInstructionPtrArray pipa(nullptr); + ResetVisited(); + mEntryBlock->LinkerObjectForwarding(pipa); + + RebuildIntegerRangeSet(); + + ResetVisited(); + mEntryBlock->SimplifyIntegerRangeRelops(); + + DisassembleDebug("Simplified range limited relational ops 1"); +#endif + + BuildTraces(0); + TrimBlocks(); + DisassembleDebug("Rebuilt traces"); + +#if 1 + SimplifyIntegerNumeric(activeSet); + +#endif + + BuildDataFlowSets(); + + ResetVisited(); + mEntryBlock->FollowJumps(); + + ResetEntryBlocks(); + ResetVisited(); + mEntryBlock->CollectEntryBlocks(nullptr); + BuildDataFlowSets(); + + DisassembleDebug("Followed Jumps 2"); + + RebuildIntegerRangeSet(); + + EliminateAliasValues(); + + SingleBlockLoopPointerSplit(activeSet); + + MergeIndexedLoadStore(); + + SingleBlockLoopPointerToByte(activeSet); + + SingleBlockLoopSinking(activeSet); + + SingleBlockLoopIndexReduction(activeSet); + +#if 1 + DisassembleDebug("PreMoveTrainCrossBlockA"); + + PeepholeOptimization(); + +#if 1 + DisassembleDebug("PreMoveTrainCrossBlockB"); + + ResetVisited(); + mEntryBlock->MoveTrainCrossBlock(); +#endif + PeepholeOptimization(); + + DisassembleDebug("MoveTrainCrossBlock"); + +#endif + + DisassembleDebug("PreHoistCommonConditionalPath"); + HoistCommonConditionalPath(); + DisassembleDebug("HoistCommonConditionalPath"); + +#if 1 + ResetVisited(); + mEntryBlock->MoveLoopHeadCheckToTail(); + +#endif + DisassembleDebug("MoveLoopHeadCheckToTail"); + +#if 1 + LoadStoreForwarding(paramMemory); + + RebuildIntegerRangeSet(); +#endif + +#if 1 + BuildLoopPrefix(); + DisassembleDebug("added dominators"); + + BuildDataFlowSets(); + + RebuildIntegerRangeSet(); + + ResetVisited(); + mEntryBlock->InnerLoopIntegerRangeUpdate(); + DisassembleDebug("InnerLoopIntegerRangeUpdate"); + + RebuildIntegerRangeSet(); + + ResetVisited(); + mEntryBlock->Flatten2DLoop(); + + DisassembleDebug("Flatten2DLoop"); + +#endif + +#if 1 + BuildLoopPrefix(); + DisassembleDebug("added dominators"); + + BuildDataFlowSets(); + + ResetVisited(); + mEntryBlock->PostDecLoopOptimization(); + + DisassembleDebug("PostDecLoopOptimization"); + + ResetVisited(); + mEntryBlock->SingleBlockLoopOptimisation(mParamAliasedSet, mModule->mGlobalVars); + + DisassembleDebug("single block loop opt X"); + + BuildDataFlowSets(); + + ResetEntryBlocks(); + ResetVisited(); + mEntryBlock->CollectEntryBlocks(nullptr); +#endif + + BuildTraces(0); + TrimBlocks(); + +#if 1 + SingleBlockLoopPointerSplit(activeSet); + + MergeIndexedLoadStore(); +#endif + +#if 1 + BuildLoopPrefix(); + DisassembleDebug("added dominators"); + + BuildDataFlowSets(); + + ResetVisited(); + mEntryBlock->InnerLoopOptimization(mParamAliasedSet); + + DisassembleDebug("inner loop opt 2"); + + BuildDataFlowSets(); + + ResetEntryBlocks(); + ResetVisited(); + mEntryBlock->CollectEntryBlocks(nullptr); + + BuildTraces(0); + TrimBlocks(); +#endif + + SingleTailLoopOptimization(paramMemory); + BuildDataFlowSets(); + + PropagateMemoryAliasingInfo(true); + +#if 1 + ExpandSelect(); + + BuildDataFlowSets(); + + CheckUsedDefinedTemps(); +#endif + +#if 1 + BuildTraces(0); + TrimBlocks(); + + PushSinglePathResultInstructions(); + + ResetVisited(); + if (mEntryBlock->MergeSameConditionTraces()) + { + ResetEntryBlocks(); + ResetVisited(); + mEntryBlock->CollectEntryBlocks(nullptr); + } + + BuildDataFlowSets(); + + CheckUsedDefinedTemps(); +#endif + + PeepholeOptimization(); + +#if 1 + BuildDataFlowSets(); + + do { + Disassemble("gcp+"); + TempForwarding(); + } while (GlobalConstantPropagation()); + Disassemble("gcp-"); +#endif + + LoadStoreForwarding(paramMemory); + +#if 1 + RebuildIntegerRangeSet(); +#endif + +#if 1 + ResetVisited(); + mEntryBlock->SimplifyIntegerRangeRelops(); + + DisassembleDebug("Simplified range limited relational ops 2"); +#endif + + LimitLoopIndexIntegerRangeSets(); + DisassembleDebug("LimitLoopIndexIntegerRangeSets"); + +#if 1 + if (mCompilerOptions & COPT_OPTIMIZE_AUTO_UNROLL) + { + ResetVisited(); + mEntryBlock->SingleBlockLoopUnrolling(); + + DisassembleDebug("Single Block loop unrolling"); + + RemoveUnusedInstructions(); + + BuildDataFlowSets(); + + RenameTemporaries(); + } +#endif + +#if 1 + ResetVisited(); + mEntryBlock->DropUnreachable(); + + BuildDataFlowSets(); + + ResetVisited(); + mEntryBlock->ForwardDiamondMovedTemp(); + DisassembleDebug("Diamond move forwarding"); + + ResetVisited(); + mEntryBlock->ForwardRealDiamondMovedTemp(); + DisassembleDebug("Real Diamond move forwarding"); + + ResetEntryBlocks(); + ResetVisited(); + mEntryBlock->CollectEntryBlocks(nullptr); + + BuildDataFlowSets(); + DisassembleDebug("Removed unreachable branches"); + + BuildTraces(0); + TrimBlocks(); + DisassembleDebug("Rebuilt traces"); + + BuildDataFlowSets(); + + do { + TempForwarding(); + } while (GlobalConstantPropagation()); + +#endif + +#if 1 + PeepholeOptimization(); + + TempForwarding(); + RemoveUnusedInstructions(); + + DisassembleDebug("Peephole optimized"); + +#endif + + LoadStoreForwarding(paramMemory); + + RebuildIntegerRangeSet(); + +#if 1 + BuildLoopPrefix(); + DisassembleDebug("added dominators"); + + BuildDataFlowSets(); + + ResetVisited(); + mEntryBlock->SingleBlockLoopOptimisation(mParamAliasedSet, mModule->mGlobalVars); + + DisassembleDebug("single block loop opt 2"); + + BuildDataFlowSets(); + + BuildTraces(0); + TrimBlocks(); + DisassembleDebug("Rebuilt traces"); +#endif + +#if 1 + PeepholeOptimization(); + + TempForwarding(); + RemoveUnusedInstructions(); + + DisassembleDebug("Peephole optimized"); + +#endif + +#if 1 + BuildLoopPrefix(); + DisassembleDebug("added dominators"); + + BuildDataFlowSets(); + + ResetVisited(); + mEntryBlock->SingleBlockLoopOptimisation(mParamAliasedSet, mModule->mGlobalVars); + + DisassembleDebug("single block loop opt 3"); + + BuildDataFlowSets(); + + BuildTraces(0); + TrimBlocks(); + DisassembleDebug("Rebuilt traces"); +#endif + +#if 1 + BuildLoopPrefix(); + DisassembleDebug("added dominators"); + + BuildDataFlowSets(); + + ResetVisited(); + mEntryBlock->InnerLoopOptimization(mParamAliasedSet); + + DisassembleDebug("inner loop opt 3"); + + BuildDataFlowSets(); + + ResetEntryBlocks(); + ResetVisited(); + mEntryBlock->CollectEntryBlocks(nullptr); + + BuildTraces(0); + BuildDataFlowSets(); + BuildTraces(0); +#endif + + PropagateConstOperationsUp(); + +#if 1 + BuildLoopPrefix(); + + DisassembleDebug("Pre MergeLoopTails"); + + ResetVisited(); + if (mEntryBlock->MergeLoopTails()) + { + BuildTraces(0); + BuildDataFlowSets(); + } + + DisassembleDebug("Post MergeLoopTails"); + + SingleTailLoopOptimization(paramMemory); + BuildDataFlowSets(); + +#endif + + PropagateMemoryAliasingInfo(true); + + ResetVisited(); + mEntryBlock->SimplifyIntegerRangeRelops(); + + DisassembleDebug("Simplified range limited relational ops 3"); + +#if 1 + BuildDataFlowSets(); + + ResetVisited(); + mEntryBlock->ForwardDiamondMovedTemp(); + DisassembleDebug("Diamond move forwarding 2"); + + ResetVisited(); + mEntryBlock->ForwardRealDiamondMovedTemp(); + DisassembleDebug("Real Diamond move forwarding 2"); + + TempForwarding(); + + RemoveUnusedInstructions(); +#endif + + RemoveUnusedLocalStoreInstructions(); + + if (mCompilerOptions & COPT_OPTIMIZE_BASIC) + { + MoveConditionsOutOfLoop(); + } + +#if 1 + ResetVisited(); + + if (!mInterruptCalled && !mDynamicStack && mNativeProcedure && mEntryBlock->CheckStaticStack()) + { + mLinkerObject->mFlags |= LOBJF_STATIC_STACK; + mLinkerObject->mStackSection = mModule->mLinker->AddSection(mIdent->Mangle("@stack"), LST_STATIC_STACK); + mLinkerObject->mStackSection->mSections.Push(mModule->mParamLinkerSection); + + for (int i = 0; i < mLocalVars.Size(); i++) + { + InterVariable* var(mLocalVars[i]); + if (var && !var->mTemp && !var->mLinkerObject) + { + var->mLinkerObject = mModule->mLinker->AddObject(mLocation, var->mIdent, mLinkerObject->mStackSection, LOT_BSS, var->mAlignment); + var->mLinkerObject->mOwnerProc = this; + var->mLinkerObject->mVariable = var; + var->mLinkerObject->mFlags |= LOBJF_LOCAL_VAR; + var->mLinkerObject->AddSpace(var->mSize); + var->mIndex = mModule->mGlobalVars.Size(); + mModule->mGlobalVars.Push(var); + } + } + + mSaveTempsLinkerObject = mModule->mLinker->AddObject(mLocation, mIdent->Mangle("@stack"), mLinkerObject->mStackSection, LOT_BSS); + mSaveTempsLinkerObject->mOwnerProc = this; + + ResetVisited(); + mEntryBlock->CollectStaticStack(mLinkerObject, mLocalVars); + + if (mCallsFunctionPointer) + { + for (int i = 0; i < mCalledFunctions.Size(); i++) + { + if (mCalledFunctions[i]->mLinkerObject && mCalledFunctions[i]->mLinkerObject->mStackSection) + mLinkerObject->mStackSection->mSections.Push(mCalledFunctions[i]->mLinkerObject->mStackSection); + } + } + + GrowingInstructionPtrArray pipa(nullptr); + ResetVisited(); + mEntryBlock->LinkerObjectForwarding(pipa); + +#if 1 + BuildLoopPrefix(); + DisassembleDebug("added dominators"); + + BuildDataFlowSets(); + + ResetVisited(); + mEntryBlock->SingleBlockLoopOptimisation(mParamAliasedSet, mModule->mGlobalVars); + + DisassembleDebug("single block loop opt 4"); + + BuildDataFlowSets(); + + BuildTraces(0); + DisassembleDebug("Rebuilt traces"); +#endif + } + else if (!mInterruptCalled && mNativeProcedure) + { +// mLinkerObject->mFlags |= LOBJF_STATIC_STACK; + mLinkerObject->mStackSection = mModule->mLinker->AddSection(mIdent->Mangle("@stack"), LST_STATIC_STACK); + mLinkerObject->mStackSection->mSections.Push(mModule->mParamLinkerSection); + ResetVisited(); + mEntryBlock->CollectStaticStackDependencies(mLinkerObject); + } + +#endif + + DisassembleDebug("PreLoopTemp"); + BuildDataFlowSets(); + ResetVisited(); + mEntryBlock->ForwardLoopMovedTemp(); + DisassembleDebug("PostLoopTemp"); + + BuildDataFlowSets(); + RemoveUnusedInstructions(); + + CheckFinal(); + DisassembleDebug("PreConstP"); +#if 1 + do { + DisassembleDebug("InConstP"); + TempForwarding(); + } while (GlobalConstantPropagation()); + + BuildTraces(0); + DisassembleDebug("Rebuilt traces"); + + + PeepholeOptimization(); + TempForwarding(); + RemoveUnusedInstructions(); + + ShortcutConstBranches(); + + ShortcutDuplicateBranches(); + + DisassembleDebug("Global Constant Prop 1"); + + BuildDataFlowSets(); + ResetVisited(); + mEntryBlock->PushMoveOutOfLoop(); + BuildDataFlowSets(); + + DisassembleDebug("PushMoveOutOfLoop"); + + TempForwarding(false, true); + +#endif + +#if 1 + SimplifyIntegerNumeric(activeSet); + +#endif + + PropagateConstOperationsUp(); + + CombineIndirectAddressing(); + + BuildDataFlowSets(); + ResetVisited(); + mEntryBlock->ForwardShortLoadStoreOffsets(); + DisassembleDebug("ForwardShortLoadStoreOffsets"); + + PeepholeOptimization(); + + ConstSingleLoopOptimization(); + + BuildTraces(0); + + ResetVisited(); + mEntryBlock->EmptyLoopOptimization(); + +#if 1 + BuildDataFlowSets(); + do { + TempForwarding(); + } while (GlobalConstantPropagation()); +#endif +#if 1 + ResetVisited(); + if (mEntryBlock->MapLateIntrinsics()) + { + DisassembleDebug("MapLateIntrinsics"); + + ResetVisited(); + mEntryBlock->RemoveUnusedArgumentStoreInstructions(); + + DisassembleDebug("RemoveUnusedArgumentStoreInstructions"); + + do + { + GrowingInstructionPtrArray cipa(nullptr); + ResetVisited(); + changed = mEntryBlock->PropagateVariableCopy(cipa, mModule->mGlobalVars, mLocalAliasedSet, mParamAliasedSet); + + RemoveUnusedStoreInstructions(paramMemory); + } while (changed); + + DisassembleDebug("Copy forwarding 2"); + + } + + PropagateByteIndexPointers(); + + PropagateMemoryAliasingInfo(true); + + ReplaceByteIndexPointers(activeSet); + + DisassembleDebug("ReplaceByteIndexPointers"); + +#endif + ForwardSingleAssignmentBools(); +#if 1 + { + NumberSet restrictSet(mNumRestricted); + + ResetVisited(); + mEntryBlock->CollectUnusedRestricted(restrictSet); + + ResetVisited(); + mEntryBlock->RemoveUnusedRestricted(restrictSet); + + DisassembleDebug("Remove unused restricted"); + } +#endif + +// CollapseDispatch(); +// DisassembleDebug("CollapseDispatch"); + + +#if 1 + for (int i = 0; i < 8; i++) + { + BuildTraces(0); + TrimBlocks(); + + LoadStoreForwarding(paramMemory); + + RebuildIntegerRangeSet(); + + ResetVisited(); + mEntryBlock->SimplifyIntegerRangeRelops(); + + PeepholeOptimization(); + + DisassembleDebug("Peephole Temp Check"); + + RemoveUnusedInstructions(); + + ReduceTemporaries(); + + CheckBlocks(); + + MergeBasicBlocks(activeSet); + + CheckBlocks(); + + DisassembleDebug("TempForward Rev 1"); + + BuildDataFlowSets(); + + TempForwarding(true); + + RemoveUnusedInstructions(); + + CheckBlocks(); + + BuildDataFlowSets(); + + DisassembleDebug("TempForward Rev 2"); + + TempForwarding(); + + DisassembleDebug("TempForward Rev 3"); + + CheckBlocks(); + + BuildLoopPrefix(); + + BuildDataFlowSets(); + + DisassembleDebug("Checking Unused"); + + RemoveUnusedInstructions(); + + DisassembleDebug("Checked Unused"); + + BuildDataFlowSets(); + + RenameTemporaries(); + + BuildDataFlowSets(); + + TempForwarding(); +#if 1 + CheckBlocks(); + + BuildDataFlowSets(); + do { + TempForwarding(); + } while (GlobalConstantPropagation()); + + DisassembleDebug("GlobalConstantPropagation"); + + LoadStoreForwarding(paramMemory); + + ResetEntryBlocks(); + ResetVisited(); + mEntryBlock->CollectEntryBlocks(nullptr); + MergeCommonPathInstructions(); + + CheckBlocks(); +#if 1 + PushSinglePathResultInstructions(); +#endif + + TempForwarding(); + RemoveUnusedInstructions(); +#endif + + DisassembleDebug("Global Constant Prop 2"); + } +#endif + + BuildDataFlowSets(); + + ResetVisited(); + mEntryBlock->PropagateConstCompareResults(); + DisassembleDebug("PropagateConstCompareResults2"); + + ResetVisited(); + mEntryBlock->PropagateNonNullPointers(NumberSet(mTemporaries.Size())); + DisassembleDebug("PropagateNonNullPointers"); + + GrowingInstructionPtrArray ptemps(nullptr); + ptemps.SetSize(mTemporaries.Size()); + ResetVisited(); + mEntryBlock->ForwardConstTemps(ptemps); + + ResetVisited(); + mEntryBlock->PullStoreUpToConstAddress(); + DisassembleDebug("PullStoreUpToConstAddress"); + + LoadStoreForwarding(paramMemory); + + ConstSingleLoopOptimization(); + + BuildDataFlowSets(); + TempForwarding(false, true); + + EliminateDoubleLoopCounter(); + DisassembleDebug("EliminateDoubleLoopCounter"); + + BuildDataFlowSets(); + + PeepholeOptimization(); + + ResetVisited(); + mEntryBlock->ReplaceCopyFill(); + + ResetVisited(); + mEntryBlock->PropagateConstCompareResults(); + + ptemps.SetSize(mTemporaries.Size(), true); + ResetVisited(); + mEntryBlock->ForwardConstTemps(ptemps); + + ResetVisited(); + mEntryBlock->SingleLoopCountZeroCheck(); + + ResetVisited(); + mEntryBlock->InnerLoopCountZeroCheck(); + + RemoveUnusedPartialStoreInstructions(); + + PeepholeOptimization(); + + EliminateIntegerSumAliasTemps(); + + ResetVisited(); + if (mEntryBlock->Flatten2DLoop()) + { + DisassembleDebug("Flatten2DLoop"); + + ConstSingleLoopOptimization(); + + BuildDataFlowSets(); + TempForwarding(false, true); + + PeepholeOptimization(); + } + + UntangleLoadStoreSequence(); + + ConstInnerLoopOptimization(); + + BuildTraces(0); + + BuildDataFlowSets(); + TempForwarding(false, true); + + ResetVisited(); + mEntryBlock->EmptyLoopOptimization(); + +#if 1 + BuildDataFlowSets(); + do { + TempForwarding(); + } while (GlobalConstantPropagation()); +#endif + + ResetVisited(); + mEntryBlock->ReduceTempLivetimes(); + DisassembleDebug("reduce temp livetimes"); + + MapVariables(); + + DisassembleDebug("mapped variabled"); + + ReduceTemporaries(true); + + DisassembleDebug("Reduced Temporaries"); + + MergeCommonPathInstructions(); + + if (!mFastCallProcedure) + ReduceRecursionTempSpilling(paramMemory); + + // Optimize for size + + MergeBasicBlocks(activeSet); + BuildTraces(0, false, true); + TrimBlocks(); + + DisassembleDebug("Final Merged basic blocks"); + + WarnInvalidValueRanges(); + + BuildDataFlowSets(); + + MapCallerSavedTemps(); + + DisassembleDebug("MapCallerSavedTemps"); + + ResetVisited(); + mEntryBlock->PromoteStaticStackParams(mModule->mParamLinkerObject); + + DisassembleDebug("PromoteStaticStackParams"); + + + if (mValueReturn) + { + ResetVisited(); + mEntryBlock->CheckValueReturn(); + } + + ResetVisited(); + mEntryBlock->CheckNullptrDereference(); + + StructReturnPropagation(); + + if (mSaveTempsLinkerObject && mTempSize > BC_REG_TMP_SAVED - BC_REG_TMP) + mSaveTempsLinkerObject->AddSpace(mTempSize - (BC_REG_TMP_SAVED - BC_REG_TMP)); + + for (int i = 0; i < mParamVars.Size(); i++) + { + InterVariable* v(mParamVars[i]); + if (v && v->mIdent) + { + if (v->mLinkerObject) + { + } + else + { + LinkerObjectRange range; + range.mIdent = v->mIdent; + range.mOffset = i + BC_REG_FPARAMS; + range.mSize = v->mSize; + mLinkerObject->mZeroPageRanges.Push(range); + } + } + } + + if (mTempSize <= BC_REG_TMP_SAVED - BC_REG_TMP && !(mLinkerObject->mFlags & LOBJF_STATIC_STACK) && mLeafProcedure) + { + bool hasLocals = false; + + for (int i = 0; i < mLocalVars.Size(); i++) + if (mLocalVars[i] && mLocalVars[i]->mUsed) + hasLocals = true; + + if (!hasLocals) + mLinkerObject->mFlags |= LOBJF_STATIC_STACK; + } + + if (!(mLinkerObject->mFlags & LOBJF_STATIC_STACK)) + { + ResetVisited(); + if (!mEntryBlock->PreventsCallerStaticStack()) + mLinkerObject->mFlags |= LOBJF_STATIC_STACK; + } + + if (!mNoInline && !mEntryBlock->mTrueJump) + { + NumberSet tlocals(mTemporaries.Size()); + + int nconst = 0, nvariables = 0, nparams = 0, ncalls = 0, nret = 0, nother = 0, nops = 0; + for (int i = 0; i < mEntryBlock->mInstructions.Size(); i++) + { + InterInstruction* ins = mEntryBlock->mInstructions[i]; + switch (ins->mCode) + { + case IC_LOAD: + if (ins->mSrc[0].mTemp < 0) + { + if (ins->mSrc[0].mMemory == IM_FPARAM || ins->mSrc[0].mMemory == IM_PARAM) + { + nparams++; + tlocals += ins->mDst.mTemp; + } + else if (ins->mSrc[0].mMemory == IM_GLOBAL || ins->mSrc[0].mMemory == IM_ABSOLUTE) + { + nvariables++; + tlocals += ins->mDst.mTemp; + } + else + nother++; + } + else if (tlocals[ins->mSrc[0].mTemp]) + nops++; + else + nother++; + break; + case IC_STORE: + if (ins->mSrc[1].mTemp >= 0 || (ins->mSrc[1].mMemory != IM_FFRAME && ins->mSrc[1].mMemory != IM_FRAME)) + nops++; + if (ins->mSrc[0].mTemp < 0) + nconst++; + break; + +#if 1 + case IC_CONSTANT: + if (ins->mDst.mType == IT_POINTER && (ins->mConst.mMemory == IM_FPARAM || ins->mConst.mMemory == IM_PARAM || ins->mConst.mMemory == IM_LOCAL)) + nother++; + else + nconst++; + break; +#endif +#if 1 + case IC_LEA: + if (ins->mSrc[1].mTemp >= 0 || (ins->mSrc[1].mMemory != IM_FPARAM && ins->mSrc[1].mMemory != IM_PARAM && ins->mSrc[1].mMemory != IM_LOCAL)) + nops++; + else + nother++; + break; +#endif +#if 1 + case IC_BINARY_OPERATOR: + case IC_UNARY_OPERATOR: + case IC_RELATIONAL_OPERATOR: + nops++; + break; +#endif + case IC_CALL: + case IC_CALL_NATIVE: + if (ins->mSrc[0].mTemp < 0) + ncalls++; + else + nother++; + break; + case IC_FREE: + ncalls++; + break; + case IC_RETURN: + case IC_RETURN_VALUE: + nret++; + break; + default: + nother++; + } + } + + if (nother == 0 && ncalls <= 1 && nret == 1 && nconst <= 1 + nparams && nops <= 1 + nparams) + mCheapInline = true; + } + + mGlobalsChecked = true; + mStoresIndirect = false; + mLoadsIndirect = false; + mReferencedGlobals.Reset(mModule->mGlobalVars.Size()); + mModifiedGlobals.Reset(mModule->mGlobalVars.Size()); +#if 1 + if (!mLeafProcedure && mCommonFrameSize > 0) + { + ResetVisited(); + if (!mEntryBlock->RecheckOuterFrame()) + mCommonFrameSize = 0; + } + + mParamVarsSize = 0; + ResetVisited(); + mEntryBlock->CollectParamVarsSize(mParamVarsSize); + +#endif + ResetVisited(); + mEntryBlock->CollectGlobalReferences(mReferencedGlobals, mModifiedGlobals, mStoresIndirect, mLoadsIndirect, mGlobalsChecked); + + ResetVisited(); + mEntryBlock->MarkAliasing(mParamAliasedSet); + + DisassembleDebug("Marked Aliasing"); +} + +void InterCodeProcedure::AddCalledFunction(InterCodeProcedure* proc) +{ + assert(proc != nullptr); + mCalledFunctions.Push(proc); +} + +void InterCodeProcedure::CallsFunctionPointer(void) +{ + mCallsFunctionPointer = true; +} + +void InterCodeProcedure::MarkRelevantStatics(void) +{ + ResetVisited(); + mEntryBlock->MarkRelevantStatics(); + +} + +void InterCodeProcedure::RemoveNonRelevantStatics(void) +{ + if (mCompilerOptions & COPT_OPTIMIZE_BASIC) + { + ResetVisited(); + mEntryBlock->RemoveNonRelevantStatics(); + RemoveUnusedInstructions(); + } +} + + +void InterCodeProcedure::LimitLoopIndexRanges(void) +{ + BuildLoopPrefix(); + DisassembleDebug("added dominators"); + + ResetVisited(); + mEntryBlock->LimitLoopIndexRanges(); +} + +void InterCodeProcedure::SingleTailLoopOptimization(InterMemory paramMemory) +{ + bool changed; + + do { + changed = false; + + BuildLoopPrefix(); + DisassembleDebug("added dominators"); + + BuildDataFlowSets(); + + ResetVisited(); + changed = mEntryBlock->SingleTailLoopOptimization(mParamAliasedSet, mModule->mGlobalVars); + DisassembleDebug("SingleTailLoopOptimization"); + + + if (changed) + { + TempForwarding(); + + RemoveUnusedInstructions(); + + RemoveUnusedStoreInstructions(paramMemory); + } + + BuildTraces(0); + DisassembleDebug("Rebuilt traces"); + + } while (changed); +} + +void InterCodeProcedure::MapVariables(void) +{ + ResetVisited(); + mEntryBlock->MapVariables(mModule->mGlobalVars, mLocalVars); + mLocalSize = 0; + for (int i = 0; i < mLocalVars.Size(); i++) + { +#if 0 + if (mLocalVars[i]) + { + printf("MapVars %s, %d: %s %d %d\n", + mIdent->mString, i, mLocalVars[i]->mIdent ? mLocalVars[i]->mIdent->mString : "?", + mLocalVars[i]->mUsed, mLocalVars[i]->mSize); + } +#endif + if (mLocalVars[i] && mLocalVars[i]->mUsed && !mLocalVars[i]->mLinkerObject) + { + mLocalVars[i]->mOffset = mLocalSize; + mLocalSize += mLocalVars[i]->mSize; + } + } +} + +static bool IsReadModifyWrite(const InterCodeBasicBlock* block, int at) +{ + if (block->mInstructions[at]->mCode == IC_LOAD && + block->mInstructions[at + 2]->mCode == IC_STORE && + block->mInstructions[at + 1]->mDst.mTemp == block->mInstructions[at + 2]->mSrc[0].mTemp && + block->mInstructions[at]->mSrc[0].IsEqual(block->mInstructions[at + 2]->mSrc[1])) + { + if (block->mInstructions[at + 1]->mCode == IC_BINARY_OPERATOR) + { + return + block->mInstructions[at + 1]->mSrc[0].mTemp == block->mInstructions[at]->mDst.mTemp || + block->mInstructions[at + 1]->mSrc[1].mTemp == block->mInstructions[at]->mDst.mTemp; + } + else if (block->mInstructions[at + 1]->mCode == IC_UNARY_OPERATOR) + { + return + block->mInstructions[at + 1]->mSrc[0].mTemp == block->mInstructions[at]->mDst.mTemp; + } + } + + return false; +} + +bool InterCodeBasicBlock::SameExitCode(const InterCodeBasicBlock* block) const +{ + int sz0 = mInstructions.Size(); + int sz1 = block->mInstructions.Size(); + + if (sz0 > 1 && sz1 > 1) + { + InterInstruction* ins0 = mInstructions[sz0 - 2]; + InterInstruction* ins1 = block->mInstructions[sz1 - 2]; + + if (ins0->IsEqual(ins1)) + { + if (ins0->mCode == IC_STORE && ins0->mSrc[1].mTemp >= 0) + { + int j0 = sz0 - 3; + while (j0 >= 0 && mInstructions[j0]->mDst.mTemp != ins0->mSrc[1].mTemp) + j0--; + int j1 = sz1 - 3; + while (j1 >= 0 && block->mInstructions[j1]->mDst.mTemp != ins0->mSrc[1].mTemp) + j1--; + + if (j0 >= 0 && j1 >= 0) + { + if (!(mInstructions[j0]->IsEqual(block->mInstructions[j1]))) + { + if (mInstructions[j0]->mCode == IC_LEA && mInstructions[j0]->mSrc[1].mTemp < 0 || + block->mInstructions[j1]->mCode == IC_LEA && block->mInstructions[j1]->mSrc[1].mTemp < 0) + { + if (!IsSimilarLea(mInstructions[j0], block->mInstructions[j1])) + return false; + } + } + } + else if (j0 >= 0) + { + if (mInstructions[j0]->mCode == IC_LEA && mInstructions[j0]->mSrc[1].mTemp < 0 && mInstructions[j0]->mSrc[0].IsUByte()) + return false; + } + else if (j1 >= 0) + { + if (block->mInstructions[j1]->mCode == IC_LEA && block->mInstructions[j1]->mSrc[1].mTemp < 0 && block->mInstructions[j1]->mSrc[0].IsUByte()) + return false; + } + + if (InterTypeSize[ins0->mSrc[0].mType] == 4) + { + bool rm0 = sz0 >= 4 && IsReadModifyWrite(this, sz0 - 4); + bool rm1 = sz1 >= 4 && IsReadModifyWrite(block, sz1 - 4); + + if (rm0 && rm1) + { + if (!(mInstructions[sz0 - 3]->IsEqual(block->mInstructions[sz1 - 3]))) + return false; + } + else if (rm0 || rm1) + return false; + } + + if (ins0->mSrc[1].mStride > 1 && InterTypeSize[ins0->mSrc[0].mType] == 4) + { + return false; + } + } + else if (ins0->mCode == IC_LOAD && ins0->mSrc[0].mTemp >= 0) + { + int j0 = mInstructions.Size() - 3; + while (j0 >= 0 && mInstructions[j0]->mDst.mTemp != ins0->mSrc[0].mTemp) + j0--; + int j1 = block->mInstructions.Size() - 3; + while (j1 >= 0 && block->mInstructions[j1]->mDst.mTemp != ins0->mSrc[0].mTemp) + j1--; + + if (j0 >= 0 && j1 >= 0) + { + if (!(mInstructions[j0]->IsEqual(block->mInstructions[j1]))) + { + if (mInstructions[j0]->mCode == IC_LEA && mInstructions[j0]->mSrc[1].mTemp < 0 || + block->mInstructions[j1]->mCode == IC_LEA && block->mInstructions[j1]->mSrc[1].mTemp < 0) + { + if (!IsSimilarLea(mInstructions[j0], block->mInstructions[j1])) + return false; + } + } + } + } + + return true; + } + } + + return false; +} + +bool PartitionSameExitCode(GrowingArray & eblocks, GrowingArray & mblocks) +{ + int i = 0; + + mblocks.SetSize(0, true); + + while (i + 1 < eblocks.Size()) + { + int j = i + 1; + while (j < eblocks.Size()) + { + if (eblocks[i]->SameExitCode(eblocks[j])) + { + mblocks.Push(eblocks[j]); + eblocks.Remove(j); + } + else + j++; + } + + if (mblocks.Size()) + { + mblocks.Push(eblocks[i]); + eblocks.Remove(i); + return true; + } + + i++; + } + + return false; +} + +bool InterCodeProcedure::ShortLeaMerge(FastNumberSet& activeSet) +{ + DisassembleDebug("PreShortLeaMerge"); + + int silvused = mTemporaries.Size(); + + int n = 0; + do + { + ResetEntryBlocks(); + ResetVisited(); + mEntryBlock->CollectEntryBlocks(nullptr); + + n++; + if (silvused != mTemporaries.Size()) + { + mTemporaries.SetSize(activeSet.Num(), true); + + ResetVisited(); + mEntryBlock->ShrinkActiveTemporaries(activeSet, mTemporaries); + + mLocalValueRange.SetSize(activeSet.Num(), true); + + ResetVisited(); + mEntryBlock->RemapActiveTemporaries(activeSet); + + silvused = mTemporaries.Size(); + } + + ResetVisited(); + } while (mEntryBlock->ShortLeaMerge(silvused)); + + assert(silvused == mTemporaries.Size()); + + DisassembleDebug("ShortLeaMerge"); + + ResetVisited(); + mEntryBlock->ShortLeaCleanup(); + + DisassembleDebug("ShortLeaCleanup"); + + return n > 1; +} + +void InterCodeProcedure::MergeBasicBlocks(FastNumberSet& activeSet) +{ + ResetVisited(); + mEntryBlock->FollowJumps(); + + ResetVisited(); + mEntryBlock->SplitBranches(); + + DisassembleDebug("PostSplit"); + + bool changed; + do + { + changed = false; + + GrowingArray blockMap(nullptr); + + for (int i = 0; i < mBlocks.Size(); i++) + { + InterCodeBasicBlock* block = mBlocks[i]; + if (block->mNumEntries) + { + int j = 0; + while (j < i && !(mBlocks[j]->mNumEntries && mBlocks[j]->IsEqual(block) && mBlocks[j]->mIndex != 0)) + j++; + blockMap[i] = mBlocks[j]; + if (i != j) + mBlocks[j]->UnionIntegerRanges(block); + } + } + + + if (mEntryBlock != blockMap[mEntryBlock->mIndex]) + { + mEntryBlock = blockMap[mEntryBlock->mIndex]; + changed = true; + } + + for (int i = 0; i < mBlocks.Size(); i++) + { + InterCodeBasicBlock* block = mBlocks[i]; + if (block->mNumEntries) + { + if (block->mTrueJump && block->mTrueJump != blockMap[block->mTrueJump->mIndex]) + { + block->mTrueJump = blockMap[block->mTrueJump->mIndex]; + changed = true; + } + if (block->mFalseJump && block->mFalseJump != blockMap[block->mFalseJump->mIndex]) + { + block->mFalseJump = blockMap[block->mFalseJump->mIndex]; + changed = true; + } + } + } + + if (changed) + { + ResetVisited(); + for (int i = 0; i < mBlocks.Size(); i++) + mBlocks[i]->mNumEntries = 0; + mEntryBlock->CollectEntries(); + } + + for (int i = 0; i < mBlocks.Size(); i++) + { + InterCodeBasicBlock* block = mBlocks[i]; +#if 0 + // too eager will need some rework + if (block->mTrueJump && block->mFalseJump && block->mTrueJump->mNumEntries == 1 && block->mFalseJump->mNumEntries == 1) + { + while (block->mTrueJump->mInstructions.Size() && block->mFalseJump->mInstructions.Size() && + block->mTrueJump->mInstructions[0]->IsEqual(block->mFalseJump->mInstructions[0]) && + block->mTrueJump->mInstructions[0]->mCode != IC_BRANCH && + block->mTrueJump->mInstructions[0]->mCode != IC_JUMP && + block->mTrueJump->mInstructions[0]->mCode != IC_RELATIONAL_OPERATOR && + block->mTrueJump->mInstructions[0]->mDst.mTemp != block->mInstructions.Last()->mSrc[0].mTemp) + { + if (block->mInstructions.Size() >= 2 && CanBypass(block->mTrueJump->mInstructions[0], block->mInstructions[block->mInstructions.Size() - 2])) + block->mInstructions.Insert(block->mInstructions.Size() - 2, block->mTrueJump->mInstructions[0]); + else + block->mInstructions.Insert(block->mInstructions.Size() - 1, block->mTrueJump->mInstructions[0]); + block->mTrueJump->mInstructions.Remove(0); + block->mFalseJump->mInstructions.Remove(0); + changed = true; + } + } +#endif + if (block->mNumEntries >= 2) + { + GrowingArray eblocks(nullptr); + + for (int j = 0; j < mBlocks.Size(); j++) + { + InterCodeBasicBlock* eblock = mBlocks[j]; + if (eblock->mNumEntries > 0 && eblock->mTrueJump == block && !eblock->mFalseJump) + eblocks.Push(eblock); + } + + bool allBlocks = eblocks.Size() == block->mNumEntries; +// if () + { + GrowingArray mblocks(nullptr); + + while (PartitionSameExitCode(eblocks, mblocks)) + { + InterCodeBasicBlock* nblock; + + if (!allBlocks || eblocks.Size() || mblocks.IndexOf(block) != -1) + { + nblock = new InterCodeBasicBlock(this); + + for (int i = 0; i < mblocks.Size(); i++) + mblocks[i]->mTrueJump = nblock; + block->mNumEntries -= mblocks.Size(); + + InterInstruction* jins = new InterInstruction(mblocks[0]->mInstructions.Last()->mLocation, IC_JUMP); + nblock->mInstructions.Push(jins); + nblock->Close(block, nullptr); + + nblock->mNumEntries = mblocks.Size(); + block->mNumEntries++; + + for (int i = 0; i < mblocks.Size(); i++) + nblock->mEntryBlocks.Push(mblocks[i]); + + eblocks.Push(nblock); + } + else + nblock = block; + + InterInstruction* ins = mblocks[0]->mInstructions[mblocks[0]->mInstructions.Size() - 2]; + + nblock->mInstructions.Insert(0, ins); + for (int j = 0; j < mblocks.Size(); j++) + { + assert(mblocks[j]->mInstructions[mblocks[j]->mInstructions.Size() - 1]->mCode == IC_JUMP); + assert(mblocks[j]->mInstructions[mblocks[j]->mInstructions.Size() - 2]->IsEqual(ins)); + + ins->UnionRanges(mblocks[j]->mInstructions[mblocks[j]->mInstructions.Size() - 2]); + + mblocks[j]->mInstructions.Remove(mblocks[j]->mInstructions.Size() - 2); + } + + if (nblock->mEntryValueRange.Size()) + { + for (int j = 0; j < ins->mNumOperands; j++) + { + if (ins->mSrc[j].mTemp >= 0) + nblock->mEntryValueRange[ins->mSrc[j].mTemp] = ins->mSrc[j].mRange; + } + } + + changed = true; + } + } + } + } + + if (!changed) + { + ResetVisited(); + mEntryBlock->FollowJumps(); + + BuildDataFlowSets(); + + changed = ShortLeaMerge(activeSet); + } + + } while (changed); + + +} + +bool InterCodeProcedure::ReferencesGlobal(int varindex) +{ + if (mGlobalsChecked) + { + if (varindex >= 0) + { + if (mModule->mGlobalVars[varindex]->mAliased) + return mLoadsIndirect || mStoresIndirect; + else if (varindex < mReferencedGlobals.Size()) + return mReferencedGlobals[varindex]; + else + return false; + } + else + return false; + } + else + return true; +} + +bool InterCodeProcedure::ModifiesGlobal(int varindex) +{ + if (mGlobalsChecked) + { + if (varindex >= 0) + { + if (mModule->mGlobalVars[varindex]->mAliased && mStoresIndirect) + return true; + + if (varindex < mModifiedGlobals.Size()) + return mModifiedGlobals[varindex]; + else + return false; + } + else + return false; + } + else + return true; +} + +void InterCodeProcedure::BuildLoopPrefix(void) +{ + ResetVisited(); + + for (int i = 0; i < mBlocks.Size(); i++) + mBlocks[i]->mLoopPrefix = nullptr; + + mEntryBlock = mEntryBlock->BuildLoopPrefix(); + + ResetVisited(); + for (int i = 0; i < mBlocks.Size(); i++) + mBlocks[i]->mNumEntries = 0; + mEntryBlock->CollectEntries(); + + ResetVisited(); + mEntryBlock->BuildLoopSuffix(); + + ResetEntryBlocks(); + ResetVisited(); + mEntryBlock->CollectEntryBlocks(nullptr); +} + +bool InterCodeProcedure::PropagateNonLocalUsedTemps(void) +{ + ResetVisited(); + mEntryBlock->CollectLocalUsedTemps(mTemporaries.Size()); + + ResetVisited(); + return mEntryBlock->PropagateNonLocalUsedConstTemps(); +} + +bool InterCodeProcedure::GlobalConstantPropagation(void) +{ + NumberSet assignedTemps(mTemporaries.Size()); + GrowingInstructionPtrArray ctemps(nullptr); + + ResetVisited(); + mEntryBlock->CollectConstTemps(ctemps, assignedTemps); + + ResetVisited(); + return mEntryBlock->PropagateConstTemps(ctemps); +} + +void InterCodeProcedure::RemoveUnusedMallocs(void) +{ + ResetVisited(); + mEntryBlock->RemoveUnusedMallocs(); +} + +void InterCodeProcedure::RecheckLocalAliased(void) +{ + for (int i = 0; i < mLocalVars.Size(); i++) + { + InterVariable* v = mLocalVars[i]; + if (v) + { + if (v->mAliased) + v->mAliased = false; + else + v->mNotAliased = true; + } + } + for (int i = 0; i < mParamVars.Size(); i++) + { + InterVariable* v = mParamVars[i]; + if (v) + { + if (v->mAliased) + v->mAliased = false; + else + v->mNotAliased = true; + } + } + ResetVisited(); + mEntryBlock->RecheckLocalAliased(); + + DisassembleDebug("RecheckLocalAliased"); +} + +void InterCodeProcedure::UntangleLoadStoreSequence(void) +{ + do { + ResetVisited(); + } while (mEntryBlock->UntangleLoadStoreSequence()); + + DisassembleDebug("UntangleLoadStoreSequence"); +} + +void InterCodeProcedure::ConstSingleLoopOptimization(void) +{ + BuildTraces(0); + BuildLoopPrefix(); + + GrowingInstructionPtrArray ptemps(nullptr); + ptemps.SetSize(mTemporaries.Size()); + + ResetVisited(); + mEntryBlock->ForwardConstTemps(ptemps); + + Disassemble("PreConstSingleLoopOptimization"); + ResetVisited(); + mEntryBlock->ConstSingleLoopOptimization(); + Disassemble("PostConstSingleLoopOptimization"); +} + +void InterCodeProcedure::ConstInnerLoopOptimization(void) +{ + BuildTraces(0); + BuildLoopPrefix(); + + GrowingInstructionPtrArray ptemps(nullptr); + ptemps.SetSize(mTemporaries.Size()); + + ResetVisited(); + mEntryBlock->ForwardConstTemps(ptemps); + + Disassemble("PreConstInnerLoopOptimization"); + ResetVisited(); + mEntryBlock->ConstInnerLoopOptimization(); + Disassemble("PostConstInnerLoopOptimization"); +} + +void InterCodeProcedure::HoistCommonConditionalPath(void) +{ + for(;;) + { + ResetVisited(); + if (!mEntryBlock->HoistCommonConditionalPath()) + return; + Disassemble("InnerHoist"); + TempForwarding(); + RemoveUnusedInstructions(); + } +} + + +void InterCodeProcedure::ReduceTemporaries(bool final) +{ + NumberSet* collisionSet; + int i, j, numRenamedTemps; + int numTemps = mTemporaries.Size(); + + GrowingIntArray friendsMap(-1); + + NumberSet callerSaved(numTemps); + + if (final) + { + ResetVisited(); + mEntryBlock->BuildCallerSaveTempSet(callerSaved); + } + + ResetVisited(); + mEntryBlock->BuildLocalTempSets(numTemps); + + ResetVisited(); + mEntryBlock->BuildGlobalProvidedTempSet(NumberSet(numTemps), NumberSet(numTemps)); + + NumberSet totalRequired2(numTemps); + + do { + ResetVisited(); + } while (mEntryBlock->BuildGlobalRequiredTempSet(totalRequired2)); + + ResetVisited(); + mEntryBlock->PruneUnusedIntegerRangeSets(); + + collisionSet = new NumberSet[numTemps]; + + for (i = 0; i < numTemps; i++) + collisionSet[i].Reset(numTemps); + + Disassemble("ReduceTemps", true); + + ResetVisited(); + mEntryBlock->BuildCollisionTable(collisionSet); + + friendsMap.SetSize(numTemps, true); + + ResetVisited(); + mEntryBlock->BuildFriendsTable(friendsMap); + + mRenameTable.SetSize(numTemps, true); + + numRenamedTemps = 0; + + NumberSet usedTemps(numTemps); + + if (final) + { + for (int sz = 4; sz > 0; sz >>= 1) + { + for (i = 0; i < numTemps; i++) + { + if (InterTypeSize[mTemporaries[i]] == sz && !callerSaved[i]) + { + usedTemps.Clear(); + + for (j = 0; j < numTemps; j++) + { + if (mRenameTable[j] >= 0 && collisionSet[i][j]) + usedTemps += mRenameTable[j]; + } + + if (friendsMap[i] >= 0 && mRenameTable[friendsMap[i]] >= 0 && !usedTemps[mRenameTable[friendsMap[i]]]) + j = mRenameTable[friendsMap[i]]; + else + { + j = 0; + while (usedTemps[j]) + j++; + } + + mRenameTable[i] = j; + if (j >= numRenamedTemps) numRenamedTemps = j + 1; + } + } + for (i = 0; i < numTemps; i++) + { + if (InterTypeSize[mTemporaries[i]] == sz && callerSaved[i]) + { + usedTemps.Clear(); + + for (j = 0; j < numTemps; j++) + { + if (mRenameTable[j] >= 0 && collisionSet[i][j]) + usedTemps += mRenameTable[j]; + } + + if (friendsMap[i] >= 0 && mRenameTable[friendsMap[i]] >= 0 && !usedTemps[mRenameTable[friendsMap[i]]]) + j = mRenameTable[friendsMap[i]]; + else + { + j = 0; + while (usedTemps[j]) + j++; + } + + mRenameTable[i] = j; + if (j >= numRenamedTemps) numRenamedTemps = j + 1; + } + } + } + } + else + { + for (i = 0; i < numTemps; i++) + { + usedTemps.Clear(); + + for (j = 0; j < numTemps; j++) + { + if (mRenameTable[j] >= 0 && (collisionSet[i][j] || InterTypeSize[mTemporaries[j]] != InterTypeSize[mTemporaries[i]])) + { + usedTemps += mRenameTable[j]; + } + } + + j = 0; + while (usedTemps[j]) + j++; + + mRenameTable[i] = j; + if (j >= numRenamedTemps) numRenamedTemps = j + 1; + } + } + + mTemporaries.SetSize(numRenamedTemps, true); + + ResetVisited(); + mEntryBlock->GlobalRenameRegister(mRenameTable, mTemporaries); + + delete[] collisionSet; + + ResetVisited(); + mEntryBlock->BuildLocalTempSets(numRenamedTemps); + + ResetVisited(); + mEntryBlock->BuildGlobalProvidedTempSet(NumberSet(numRenamedTemps), NumberSet(numRenamedTemps)); + + NumberSet totalRequired3(numRenamedTemps); + + do { + ResetVisited(); + } while (mEntryBlock->BuildGlobalRequiredTempSet(totalRequired3)); +} + +void InterCodeProcedure::MapCallerSavedTemps(void) +{ + NumberSet callerSaved(mTemporaries.Size()); + ResetVisited(); + mEntryBlock->BuildCallerSaveTempSet(callerSaved); + + int callerSavedTemps = 0, calleeSavedTemps = BC_REG_TMP_SAVED - BC_REG_TMP, freeCallerSavedTemps = 0, freeTemps = 0; + + if (mCallsFunctionPointer || mDynamicStack) + freeCallerSavedTemps = BC_REG_TMP_SAVED - BC_REG_TMP; + else + { + for (int i = 0; i < mCalledFunctions.Size(); i++) + { + InterCodeProcedure* proc = mCalledFunctions[i]; + if (proc->mCallerSavedTemps > freeCallerSavedTemps) + freeCallerSavedTemps = proc->mCallerSavedTemps; + } + } + + callerSavedTemps = freeCallerSavedTemps; + mFreeCallerSavedTemps = freeCallerSavedTemps; + + int maxCallerSavedTemps = mCallerSavedTemps; + + assert(freeCallerSavedTemps <= mCallerSavedTemps); + + mTempOffset.SetSize(mTemporaries.Size(), true); + mTempSizes.SetSize(mTemporaries.Size(), true); + + for (int i = 0; i < mTemporaries.Size(); i++) + { + if (!callerSaved[i]) + { + int size = InterTypeSize[mTemporaries[i]]; + + if (freeTemps + size <= freeCallerSavedTemps) + { + mTempOffset[i] = freeTemps; + mTempSizes[i] = size; + freeTemps += size; + } + } + } + +// if (freeTemps > callerSavedTemps) +// callerSavedTemps = freeTemps; + + for (int i = 0; i < mTemporaries.Size(); i++) + { + if (!mTempSizes[i]) + { + int size = InterTypeSize[mTemporaries[i]]; + + if (callerSavedTemps + size <= maxCallerSavedTemps) + { + mTempOffset[i] = callerSavedTemps; + mTempSizes[i] = size; + callerSavedTemps += size; + } + else + { + mTempOffset[i] = calleeSavedTemps; + mTempSizes[i] = size; + calleeSavedTemps += size; + } + } + } + + mTempSize = calleeSavedTemps; + mCallerSavedTemps = callerSavedTemps; + +#if 0 + printf("Map %s, %d, %d, %d, %d\n", mIdent->mString, freeTemps, callerSavedTemps, calleeSavedTemps, freeCallerSavedTemps); + for (int i = 0; i < mTempOffset.Size(); i++) + printf("T%02d : %d, %d\n", i, mTempOffset[i], mTempSizes[i]); +#endif + + if (mSaveTempsLinkerObject && mTempSize > BC_REG_TMP_SAVED - BC_REG_TMP) + mSaveTempsLinkerObject->AddSpace(mTempSize - (BC_REG_TMP_SAVED - BC_REG_TMP)); + +// printf("Map %s, %d, %d, %d, %d\n", mIdent->mString, freeTemps, callerSavedTemps, calleeSavedTemps, freeCallerSavedTemps); +} + +void InterCodeProcedure::Disassemble(FILE* file) +{ + fprintf(file, "--------------------------------------------------------------------\n"); + fprintf(file, "%s: %s:%d\n", mIdent->mString, mLocation.mFileName, mLocation.mLine); + + static char typechars[] = "NBCILFP"; + for (int i = 0; i < mTemporaries.Size(); i++) + { + fprintf(file, "$%02x R%d(%c), ", mTempOffset[i], i, typechars[mTemporaries[i]]); + } + + fprintf(file, "\n"); + + ResetVisited(); + mEntryBlock->Disassemble(file, false); +} + +void InterCodeProcedure::Disassemble(const char* name, bool dumpSets) +{ +#if DISASSEMBLE_OPT +#ifdef _WIN32 + FILE* file; + static bool initial = true; + + if (!CheckFunc) + return; + + if (!initial) + { + fopen_s(&file, "r:\\cldiss.txt", "a"); + } + else + { + fopen_s(&file, "r:\\cldiss.txt", "w"); + initial = false; + } + + if (file) + { + fprintf(file, "--------------------------------------------------------------------\n"); + fprintf(file, "%s : %s:%d\n", name, mLocation.mFileName, mLocation.mLine); + + if (mTempOffset.Size()) + { + static char typechars[] = "NBCILFP"; + for (int i = 0; i < mTemporaries.Size(); i++) + { + fprintf(file, "$%02x T%d(%c), ", mTempOffset[i], i, typechars[mTemporaries[i]]); + } + } + fprintf(file, "\n"); + + ResetVisited(); + mEntryBlock->Disassemble(file, dumpSets); + + fclose(file); + } +#endif +#endif +} + +InterCodeModule::InterCodeModule(Errors* errors, Linker * linker) + : mErrors(errors), mLinker(linker), mGlobalVars(nullptr), mProcedures(nullptr), mCompilerOptions(0), mParamLinkerObject(nullptr), mParamLinkerSection(nullptr) +{ +} + +InterCodeModule::~InterCodeModule(void) +{ + +} + + +void InterCodeModule::InitParamStack(LinkerSection* stackSection) +{ + mParamLinkerSection = mLinker->AddSection(Ident::Unique("sstack"), LST_STATIC_STACK); + stackSection->mSections.Push(mParamLinkerSection); + + mParamLinkerObject = mLinker->AddObject(Location(), Ident::Unique("sstack"), mParamLinkerSection, LOT_STACK); +} + +bool InterCodeModule::Disassemble(const char* filename) +{ + FILE* file; + fopen_s(&file, filename, "wb"); + if (file) + { + for (int i = 0; i < mProcedures.Size(); i++) + { + InterCodeProcedure* proc = mProcedures[i]; + + proc->Disassemble(file); + } + + fclose(file); + + return true; + } + else + return false; +} diff --git a/oscar64/oscar64/InterCode.h b/oscar64/oscar64/InterCode.h new file mode 100644 index 0000000..3d609c3 --- /dev/null +++ b/oscar64/oscar64/InterCode.h @@ -0,0 +1,927 @@ +#pragma once + +#include "Array.h" +#include "NumberSet.h" +#include "Errors.h" +#include "BitVector.h" +#include +#include "MachineTypes.h" +#include "Ident.h" +#include "Linker.h" + +class Declaration; + +enum InterCode : uint8 +{ + IC_NONE, + IC_LOAD_TEMPORARY, + IC_BINARY_OPERATOR, + IC_UNARY_OPERATOR, + IC_RELATIONAL_OPERATOR, + IC_CONVERSION_OPERATOR, + IC_STORE, // store src[0] into src[1] + IC_LOAD, // load from src[0] + IC_LEA, + IC_COPY, // Copy from src[0] to src[1] + IC_STRCPY, + IC_FILL, // Fill src[1] with src[0] + IC_MALLOC, + IC_FREE, + IC_TYPECAST, + IC_CONSTANT, + IC_BRANCH, + IC_JUMP, + IC_PUSH_FRAME, + IC_POP_FRAME, + IC_CALL, + IC_CALL_NATIVE, + IC_RETURN_VALUE, + IC_RETURN_STRUCT, + IC_RETURN, + IC_ASSEMBLER, + IC_JUMPF, + IC_SELECT, + IC_DISPATCH, + IC_UNREACHABLE, + IC_BREAKPOINT +}; + +enum InterType : uint8 +{ + IT_NONE, + IT_BOOL, + IT_INT8, + IT_INT16, + IT_INT32, + IT_FLOAT, + IT_POINTER +}; + +extern int InterTypeSize[]; + +enum InterMemory : uint8 +{ + IM_NONE, + IM_PARAM, // Memory used to access parameters on stack + IM_LOCAL, + IM_GLOBAL, + IM_FRAME, // Memory used to pass parameters on stack + IM_PROCEDURE, + IM_INDIRECT, + IM_TEMPORARY, + IM_ABSOLUTE, + IM_FPARAM, // Memory used to access parameters in zp + IM_FFRAME, // Memory used to pass parameters in zp +}; + +enum InterOperator : uint8 +{ + IA_NONE, + IA_ADD, + IA_SUB, + IA_MUL, + IA_DIVU, + IA_DIVS, + IA_MODU, + IA_MODS, + IA_OR, + IA_AND, + IA_XOR, + IA_NEG, + IA_ABS, + IA_FLOOR, + IA_CEIL, + IA_NOT, + IA_SHL, + IA_SHR, + IA_SAR, + IA_CMPEQ, + IA_CMPNE, + IA_CMPGES, + IA_CMPLES, + IA_CMPGS, + IA_CMPLS, + IA_CMPGEU, + IA_CMPLEU, + IA_CMPGU, + IA_CMPLU, + + IA_FLOAT2INT, + IA_INT2FLOAT, + IA_FLOAT2UINT, + IA_UINT2FLOAT, + IA_FLOAT2LINT, + IA_LINT2FLOAT, + IA_FLOAT2LUINT, + IA_LUINT2FLOAT, + + IA_EXT8TO16U, + IA_EXT8TO32U, + IA_EXT16TO32U, + IA_EXT8TO16S, + IA_EXT8TO32S, + IA_EXT16TO32S +}; + +class InterInstruction; +class InterCodeBasicBlock; +class InterCodeProcedure; +class InterVariable; +class InterCodeModule; + +typedef InterInstruction* InterInstructionPtr; +typedef InterCodeBasicBlock* InterCodeBasicBlockPtr; +typedef InterCodeProcedure* InterCodeProcedurePtr; + +typedef GrowingArray GrowingTypeArray; +typedef GrowingArray GrowingIntArray; +typedef GrowingArray GrowingInstructionPtrArray; +typedef GrowingArray GrowingInterCodeBasicBlockPtrArray; +typedef GrowingArray GrowingInstructionArray; +typedef GrowingArray GrowingInterCodeProcedurePtrArray; + +typedef ExpandingArray ExpandingInstructionPtrArray; + + + +typedef GrowingArray GrowingVariableArray; + +#define INVALID_TEMPORARY (-1) + +InterOperator MirrorRelational(InterOperator oper); +InterOperator InvertRelational(InterOperator oper); + +class IntegerValueRange +{ +public: + IntegerValueRange(void); + ~IntegerValueRange(void); + + void Reset(void); + void Restart(void); + + int64 mMinValue, mMaxValue; + uint8 mMinExpanded, mMaxExpanded; + + enum State : uint8 + { + S_UNKNOWN, + S_UNBOUND, + S_WEAK, + S_BOUND + } mMinState, mMaxState; + + bool Same(const IntegerValueRange& range) const; + bool Weaker(const IntegerValueRange& range) const; + bool Merge(const IntegerValueRange& range, bool head, bool initial); + void Expand(const IntegerValueRange& range); + void Union(const IntegerValueRange& range); + + bool Limit(const IntegerValueRange& range); + void LimitWeak(const IntegerValueRange& range); + void MergeUnknown(const IntegerValueRange& range); + void SetLimit(int64 minValue, int64 maxValue); + void SetBounds(State minState, int64 minValue, State maxState, int64 maxValue); + void SetConstant(int64 value); + + bool IsBound(void) const; + bool IsConstant(void) const; + bool IsInvalid(void) const; + bool IsRange(int64 minVal, int64 maxVal) const; + + void SetMin(int64 value); + + bool LimitMin(int64 value); + bool LimitMax(int64 value); + + void LimitMinBound(int64 value); + void LimitMaxBound(int64 value); + + void LimitMinWeak(int64 value); + void LimitMaxWeak(int64 value); + + void AddConstValue(InterType type, int64 value); +}; + + + +typedef ExpandingArray GrowingIntegerValueRangeArray; + +class ValueSet +{ +protected: + InterInstructionPtr * mInstructions; + int mNum, mSize; +public: + ValueSet(void); + ValueSet(const ValueSet& values); + ~ValueSet(void); + + ValueSet& operator=(const ValueSet& values); + + void FlushAll(void); + void FlushCallAliases(const GrowingInstructionPtrArray& tvalue, const NumberSet& aliasedLocals, const NumberSet& aliasedParams); + void FlushFrameAliases(void); + + + void RemoveValue(int index); + void InsertValue(InterInstruction * ins); + + void UpdateValue(InterCodeBasicBlock* block, InterInstruction * ins, const GrowingInstructionPtrArray& tvalue, const NumberSet& aliasedLocals, const NumberSet& aliasedParams, const GrowingVariableArray& staticVars, const GrowingInterCodeProcedurePtrArray& staticProcs); + void Intersect(ValueSet& set); +}; + +class TempForwardingTable +{ +protected: + struct Assoc + { + int mAssoc, mSucc, mPred; + + Assoc(void) {} + Assoc(int assoc, int succ, int pred) { this->mAssoc = assoc; this->mSucc = succ; this->mPred = pred; } + Assoc(const Assoc& assoc) { this->mAssoc = assoc.mAssoc; this->mSucc = assoc.mSucc; this->mPred = assoc.mPred; } + }; + + GrowingArray mAssoc; + +public: + TempForwardingTable(void); + TempForwardingTable(const TempForwardingTable& table); + + TempForwardingTable& operator=(const TempForwardingTable& table); + + void Intersect(const TempForwardingTable& table); + + int Size(void) const; + void SetSize(int size); + + void Reset(void); + void Shrink(void); + + int operator[](int n); + + void Destroy(int n); + + void Build(int from, int to); +}; + +class InterVariable +{ +public: + bool mUsed, mAliased, mTemp, mNotAliased; + int mIndex, mSize, mOffset, mAlignment, mTempIndex, mByteIndex; + int mNumReferences; + const Ident * mIdent; + LinkerObject * mLinkerObject; + Declaration * mDeclaration; + + InterVariable(void) + : mUsed(false), mAliased(false), mTemp(false), mNotAliased(false), mIndex(-1), mSize(0), mOffset(0), mAlignment(1), mIdent(nullptr), mLinkerObject(nullptr), mTempIndex(-1), mDeclaration(nullptr) + { + } +}; + +class InterOperand +{ +public: + int64 mIntConst; + double mFloatConst; + LinkerObject * mLinkerObject; + IntegerValueRange mRange; + int mTemp; + int mVarIndex, mOperandSize, mStride, mRestricted; + bool mFinal; + InterType mType; + InterMemory mMemory, mMemoryBase; + + void Forward(const InterOperand& op); + void ForwardTemp(const InterOperand& op); + void ForwardMem(const InterOperand& op); + + InterOperand(void); + + bool IsEqual(const InterOperand & op) const; + + bool IsValid(void) const; + bool IsUByte(void) const; + bool IsSByte(void) const; + bool IsUnsigned(void) const; + bool IsPositive(void) const; + bool IsInRange(int lower, int upper) const; + + bool IsNotUByte(void) const; + + void Disassemble(FILE* file, InterCodeProcedure* proc); +}; + +class InterInstruction +{ +public: + Location mLocation; + InterOperand * mSrc; + InterOperand mDst; + InterOperand mConst; + InterOperand mOps[3]; + + short mNumOperands; + + InterCode mCode; + InterOperator mOperator; + + bool mInUse, mInvariant, mVolatile, mExpensive, mSingleAssignment, mNoSideEffects, mConstExpr, mRemove, mAliasing, mMemmap, mLockOrder; + + InterInstruction(const Location& loc, InterCode code); + InterInstruction(const InterInstruction&) = delete; + InterInstruction& operator=(const InterInstruction&) = delete; + + void Reset(void); + + bool IsEqual(const InterInstruction* ins) const; + bool IsEqualSource(const InterInstruction* ins) const; + + InterInstruction* Clone(void) const; + + bool ReferencesTemp(int temp) const; + bool UsesTemp(int temp) const; + int NumUsedTemps(void) const; + void ReplaceTemp(int from, int to); + + void CollectLocalAddressTemps(GrowingIntArray& localTable, GrowingIntArray& paramTable, int& nlocals, int& nparams); + void MarkAliasedLocalTemps(const GrowingIntArray& localTable, NumberSet& aliasedLocals, const GrowingIntArray& paramTable, NumberSet& aliasedParams); + + void FilterTempUsage(NumberSet& requiredTemps, NumberSet& providedTemps); + void FilterVarsUsage(const GrowingVariableArray& localVars, NumberSet& requiredVars, NumberSet& providedVars, const GrowingVariableArray& params, NumberSet& requiredParams, NumberSet& providedParams, InterMemory paramMemory); + void FilterStaticVarsUsage(const GrowingVariableArray& staticVars, NumberSet& requiredVars, NumberSet& providedVars); + void FilterStaticVarsByteUsage(const GrowingVariableArray& staticVars, NumberSet& requiredVars, NumberSet& providedVars, Errors * errors); + void FilterLocalVarsByteUsage(const GrowingVariableArray& lovalVars, NumberSet& requiredVars, NumberSet& providedVars, Errors* errors); + + bool RemoveUnusedResultInstructions(InterInstruction* pre, NumberSet& requiredTemps); + bool RemoveUnusedStoreInstructions(const GrowingVariableArray& localVars, NumberSet& requiredVars, const GrowingVariableArray& params, NumberSet& requiredParams, InterMemory paramMemory); + bool RemoveUnusedStaticStoreInstructions(InterCodeBasicBlock * block, const GrowingVariableArray& staticVars, NumberSet& requiredVars, GrowingInstructionPtrArray& storeIns); + bool RemoveUnusedStaticStoreByteInstructions(InterCodeBasicBlock* block, const GrowingVariableArray& staticVars, NumberSet& requiredVars); + bool RemoveUnusedLocalStoreByteInstructions(InterCodeBasicBlock* block, const GrowingVariableArray& localVars, NumberSet& requiredVars); + bool RemoveUndefinedLocalLoadByteInstructions(InterCodeBasicBlock* block, const GrowingVariableArray& localVars, NumberSet& providedVars); + void PerformValueForwarding(GrowingInstructionPtrArray& tvalue, FastNumberSet& tvalid); + void BuildCallerSaveTempSet(NumberSet& requiredTemps, NumberSet& callerSaveTemps); + + void LocalRenameRegister(GrowingIntArray& renameTable, int& num); + void GlobalRenameRegister(const GrowingIntArray& renameTable, GrowingTypeArray& temporaries); + + void PerformTempForwarding(TempForwardingTable& forwardingTable, bool reverse); + bool PropagateConstTemps(const GrowingInstructionPtrArray& ctemps); + + void BuildCollisionTable(NumberSet& liveTemps, NumberSet* collisionSets); + void ReduceTemporaries(const GrowingIntArray& renameTable, GrowingTypeArray& temporaries); + + void CollectActiveTemporaries(FastNumberSet& set); + void ShrinkActiveTemporaries(FastNumberSet& set, GrowingTypeArray& temporaries); + + void CollectSimpleLocals(FastNumberSet& complexLocals, FastNumberSet& simpleLocals, GrowingTypeArray& localTypes, FastNumberSet& complexParams, FastNumberSet& simpleParams, GrowingTypeArray& paramTypes); + void SimpleLocalToTemp(int vindex, int temp); + + bool ConstantFolding(void); + bool ConstantFoldingRelationRange(void); + + void UnionRanges(InterInstruction* ins); + + void Disassemble(FILE* file, InterCodeProcedure * proc); +}; + +class InterCodeBasicBlock +{ +public: + InterCodeProcedure * mProc; + int mIndex, mNumEntries, mNumEntered, mTraceIndex; + InterCodeBasicBlock * mTrueJump, * mFalseJump, * mLoopPrefix, * mDominator; + GrowingInstructionArray mInstructions; + + bool mVisited, mInPath, mLoopHead, mChecked, mConditionBlockTrue, mUnreachable, mLoopPath, mValueRangeValid, mPatched, mLoopDebug; + mutable int mMark; + + NumberSet mLocalUsedTemps, mLocalModifiedTemps; + NumberSet mLocalRequiredTemps, mLocalProvidedTemps; + NumberSet mEntryRequiredTemps, mEntryProvidedTemps, mEntryPotentialTemps; + NumberSet mExitRequiredTemps, mExitProvidedTemps, mExitPotentialTemps; + NumberSet mEntryConstTemp, mExitConstTemp; + NumberSet mNewRequiredTemps; + + NumberSet mLocalRequiredVars, mLocalProvidedVars; + NumberSet mEntryRequiredVars, mEntryProvidedVars; + NumberSet mExitRequiredVars, mExitProvidedVars; + + NumberSet mLocalRequiredStatics, mLocalProvidedStatics; + NumberSet mEntryRequiredStatics, mEntryProvidedStatics; + NumberSet mExitRequiredStatics, mExitProvidedStatics; + + NumberSet mLocalRequiredParams, mLocalProvidedParams; + NumberSet mEntryRequiredParams, mEntryProvidedParams; + NumberSet mExitRequiredParams, mExitProvidedParams; + + NumberSet mEntryRequiredArgs; + + GrowingInstructionArray mLoadStoreInstructions; + ExpandingInstructionPtrArray mExitConversionInstructions; + + GrowingIntegerValueRangeArray mEntryValueRange, mTrueValueRange, mFalseValueRange; + GrowingIntegerValueRangeArray mEntryParamValueRange, mTrueParamValueRange, mFalseParamValueRange, mLocalParamValueRange; + + GrowingArray mMemoryValueSize, mEntryMemoryValueSize; + + ExpandingArray mEntryBlocks, mLoopPathBlocks; + + GrowingInstructionPtrArray mMergeTValues, mMergeAValues; + ValueSet mMergeValues; + TempForwardingTable mMergeForwardingTable; + NumberSet mMergeTemps; + + InterCodeBasicBlock(InterCodeProcedure * proc); + ~InterCodeBasicBlock(void); + + InterCodeBasicBlock* Clone(void); + + void Append(InterInstruction * code); + void AppendBeforeBranch(InterInstruction* code, bool loopindex = false); + const InterInstruction* FindByDst(int dst, int depth = 0) const; + void Close(InterCodeBasicBlock* trueJump, InterCodeBasicBlock* falseJump); + + void CollectEntries(void); + void CollectEntryBlocks(InterCodeBasicBlock* from); + void GenerateTraces(int expand, bool compact); + void BuildDominatorTree(InterCodeBasicBlock * from); + bool StripLoopHead(int size); + + bool MergeSameConditionTraces(void); + + + void LocalToTemp(int vindex, int temp); + void LoadConstantFold(InterInstruction* ins, InterInstruction* ains, const GrowingVariableArray& staticVars, const GrowingInterCodeProcedurePtrArray& staticProcs); + InterOperand LoadConstantOperand(const InterInstruction* ins, const InterOperand& op, InterType type, const GrowingVariableArray& staticVars, const GrowingInterCodeProcedurePtrArray& staticProcs); + + void CollectAllUsedDefinedTemps(NumberSet& defined, NumberSet& used); + + void CollectLocalAddressTemps(GrowingIntArray& localTable, GrowingIntArray& paramTable, int & nlocals, int & nparams); + void MarkAliasedLocalTemps(const GrowingIntArray& localTable, NumberSet& aliasedLocals, const GrowingIntArray& paramTable, NumberSet& aliasedParams); + void RecheckLocalAliased(void); + + void CollectLocalUsedTemps(int numTemps); + bool PropagateNonLocalUsedConstTemps(void); + void CollectConstTemps(GrowingInstructionPtrArray& ctemps, NumberSet& assignedTemps); + bool PropagateConstTemps(const GrowingInstructionPtrArray& ctemps); + bool ForwardConstTemps(const GrowingInstructionPtrArray& ctemps); + bool PropagateConstCompareResults(void); + bool PropagateNonNullPointers(const NumberSet& vtemps); + + bool EarlyBranchElimination(const GrowingInstructionPtrArray& ctemps); + + bool PropagateVariableCopy(const GrowingInstructionPtrArray& ctemps, const GrowingVariableArray& staticVars, const NumberSet & aliasedLocals, const NumberSet & aliasedParams); + + void BuildLocalTempSets(int num); + void BuildGlobalProvidedTempSet(const NumberSet & fromProvidedTemps, const NumberSet& potentialProvidedTemps); + bool BuildGlobalRequiredTempSet(NumberSet& fromRequiredTemps); + bool RemoveUnusedResultInstructions(void); + void BuildCallerSaveTempSet(NumberSet& callerSaveTemps); + void BuildConstTempSets(void); + bool PropagateConstOperationsUp(void); + bool RemoveUnusedLocalStoreInstructions(void); + + void BuildLocalVariableSets(const GrowingVariableArray& localVars, const GrowingVariableArray& params, InterMemory paramMemory); + void BuildGlobalProvidedVariableSet(const GrowingVariableArray& localVars, NumberSet fromProvidedVars, const GrowingVariableArray& params, NumberSet fromProvidedParams, InterMemory paramMemory); + bool BuildGlobalRequiredVariableSet(const GrowingVariableArray& localVars, NumberSet& fromRequiredVars, const GrowingVariableArray& params, NumberSet& fromRequiredParams, InterMemory paramMemory); + bool RemoveUnusedStoreInstructions(const GrowingVariableArray& localVars, const GrowingVariableArray& params, InterMemory paramMemory); + bool RemoveUnusedIndirectStoreInstructions(void); + + bool RemoveUnusedArgumentStoreInstructions(void); + + void CollectUnusedRestricted(NumberSet& restrictSet); + bool RemoveUnusedRestricted(const NumberSet& restrictSet); + + void BuildStaticVariableSet(const GrowingVariableArray& staticVars); + void BuildGlobalProvidedStaticVariableSet(const GrowingVariableArray& staticVars, NumberSet fromProvidedVars); + bool BuildGlobalRequiredStaticVariableSet(const GrowingVariableArray& staticVars, NumberSet& fromRequiredVars); + bool RemoveUnusedStaticStoreInstructions(const GrowingVariableArray& staticVars); + + void BuildLocalVariableByteSet(const GrowingVariableArray& localVars, int bsize); + bool RemoveUndefinedPartialLocalInstructions(const GrowingVariableArray& localVars, int bsize); + + void BuildStaticVariableByteSet(const GrowingVariableArray& staticVars, int bsize); + bool RemoveUnusedStaticStoreByteInstructions(const GrowingVariableArray& staticVars, int bsize); + + bool CheckSingleBlockLimitedLoop(InterCodeBasicBlock*& pblock, int64 & nloop, bool & nfixed); + + bool TempIsUnsigned(int temp); + + void RestartLocalIntegerRangeSets(int num); + void BuildLocalIntegerRangeSets(int num); + void UpdateLocalIntegerRangeSets(void); + + void UpdateLocalIntegerRangeSetsForward(void); + void UpdateLocalIntegerRangeSetsBackward(void); + + bool UpdateLinearCombinations(void); + + bool BuildGlobalIntegerRangeSets(bool initial); + void SimplifyIntegerRangeRelops(void); + void MarkIntegerRangeBoundUp(int temp, int64 value, GrowingIntegerValueRangeArray& range); + void UnionIntegerRanges(const InterCodeBasicBlock* block); + void PruneUnusedIntegerRangeSets(void); + + bool InnerLoopIntegerRangeUpdate(void); + + void PropagateValueRangeSetConversions(const ExpandingInstructionPtrArray& tvalue); + + void LimitLoopIndexIntegerRangeSets(void); + + bool CombineIndirectAddressing(void); + + void WarnUnreachable(void); + + GrowingIntArray mEntryRenameTable; + GrowingIntArray mExitRenameTable; + + void LinkerObjectForwarding(const GrowingInstructionPtrArray& tvalue); + bool LoadStoreForwarding(const GrowingInstructionPtrArray& tvalue, const GrowingVariableArray& staticVars); + void ReduceRecursionTempSpilling(InterMemory paramMemory, const GrowingInstructionPtrArray& tvalue); + + void LocalRenameRegister(const GrowingIntArray& renameTable, int& num); + void BuildGlobalRenameRegisterTable(const GrowingIntArray& renameTable, GrowingIntArray& globalRenameTable); + void GlobalRenameRegister(const GrowingIntArray& renameTable, GrowingTypeArray& temporaries); + void RenameValueRanges(const GrowingIntArray& renameTable, int numTemps); + + void CheckValueUsage(InterInstruction * ins, const GrowingInstructionPtrArray& tvalue, const GrowingVariableArray& staticVars, const GrowingInterCodeProcedurePtrArray& staticProcs, FastNumberSet& fsingle); + void PerformTempForwarding(const TempForwardingTable& forwardingTable, bool reverse, bool checkloops); + void PerformValueForwarding(const GrowingInstructionPtrArray& tvalue, const ValueSet& values, FastNumberSet& tvalid, const NumberSet& aliasedLocals, const NumberSet& aliasedParams, int & spareTemps, const GrowingVariableArray& staticVars, const GrowingInterCodeProcedurePtrArray& staticProcs); + void PerformMachineSpecificValueUsageCheck(const GrowingInstructionPtrArray& tvalue, FastNumberSet& tvalid, const GrowingVariableArray& staticVars, const GrowingInterCodeProcedurePtrArray& staticProcs, FastNumberSet& fsingle); + bool EliminateDeadBranches(void); + + bool EliminateIntegerSumAliasTemps(const GrowingInstructionPtrArray& tvalue); + + bool MergeIndexedLoadStore(const GrowingInstructionPtrArray& tvalue); + bool SimplifyIntegerNumeric(const GrowingInstructionPtrArray& tvalue, int& spareTemps); + bool SimplifyPointerOffsets(void); + bool EliminateAliasValues(const GrowingInstructionPtrArray& tvalue, const GrowingInstructionPtrArray& avalue); + + bool ForwardShortLoadStoreOffsets(void); + + void CalculateSingleUsedTemps(FastNumberSet& fused, FastNumberSet& fsingle); + + bool CalculateSingleAssignmentTemps(FastNumberSet& tassigned, GrowingInstructionPtrArray& tvalue, NumberSet& modifiedParams, InterMemory paramMemory); + bool SingleAssignmentTempForwarding(const GrowingInstructionPtrArray& tunified, const GrowingInstructionPtrArray& tvalues); + + void CollectSingleAssignmentBools(FastNumberSet& tassigned, GrowingInstructionPtrArray& tvalue); + void CheckSingleAssignmentBools(GrowingInstructionPtrArray& tvalue); + void MapSingleAssignmentBools(const GrowingInstructionPtrArray& tvalue); + + void BuildCollisionTable(NumberSet* collisionSets); + void BuildFriendsTable(GrowingIntArray& friendsMap); + void ReduceTemporaries(const GrowingIntArray& renameTable, GrowingTypeArray& temporaries); + + void CollectSimpleLocals(FastNumberSet& complexLocals, FastNumberSet& simpleLocals, GrowingTypeArray & localTypes, FastNumberSet& complexParams, FastNumberSet& simpleParams, GrowingTypeArray& paramTypes); + void SimpleLocalToTemp(int vindex, int temp); + + void CollectActiveTemporaries(FastNumberSet& set); + void ShrinkActiveTemporaries(FastNumberSet& set, GrowingTypeArray& temporaries); + void RemapActiveTemporaries(const FastNumberSet& set); + + void Disassemble(FILE* file, bool dumpSets); + + void CollectVariables(GrowingVariableArray & globalVars, GrowingVariableArray & localVars, GrowingVariableArray& paramVars, InterMemory paramMemory); + void MapVariables(GrowingVariableArray& globalVars, GrowingVariableArray& localVars); + + void CollectOuterFrame(int level, int& size, bool& inner, bool& inlineAssembler, bool& byteCodeCall); + bool RecheckOuterFrame(void); + void CollectParamVarsSize(int& size); + + bool IsLeafProcedure(void); + bool PreventsCallerStaticStack(void); + + void UnionEntryValueRange(const GrowingIntegerValueRangeArray & range, const GrowingIntegerValueRangeArray& paramRange); + bool ForwardDiamondMovedTemp(void); + bool ForwardRealDiamondMovedTemp(void); + bool ForwardLoopMovedTemp(void); + + bool MoveTrainCrossBlock(void); + bool HoistCommonConditionalPath(void); + bool IsDominator(InterCodeBasicBlock* block); + bool IsDirectDominatorBlock(InterCodeBasicBlock* block); + bool IsDirectLoopPathBlock(InterCodeBasicBlock* block); + bool CollectBlocksToDominator(InterCodeBasicBlock* dblock, ExpandingArray& body); + + void MarkRelevantStatics(void); + void RemoveNonRelevantStatics(void); + + bool IsTempModifiedOnPath(int temp, int at) const; + bool IsTempReferencedOnPath(int temp, int at) const; + + // The memory referenced by lins may be writtne by sind + bool DestroyingMem(const InterInstruction* lins, const InterInstruction* sins) const; + bool DestroyingMem(InterCodeBasicBlock* block, InterInstruction* lins, int from , int to) const; + + // The two memory operations may have overlapping reads/writs and writes + bool CollidingMem(const InterInstruction* ins1, const InterInstruction* ins2) const; + bool CollidingMem(const InterOperand& op, InterType type, const InterInstruction* ins) const; + bool CollidingMem(const InterOperand& op1, InterType type1, const InterOperand& op2, InterType type2) const; + bool CollidingMem(InterCodeBasicBlock* block, InterInstruction* lins, int from, int to) const; + bool InvalidatedBy(const InterInstruction* ins, const InterInstruction* by) const; + + // The two memory regions may be aliased but not just the same + bool AliasingMem(const InterInstruction* ins1, const InterInstruction* ins2) const; + bool AliasingMem(const InterOperand& op, InterType type, const InterInstruction* ins) const; + bool AliasingMem(const InterOperand& op1, InterType type1, const InterOperand& op2, InterType type2) const; + bool AliasingMem(InterCodeBasicBlock* block, InterInstruction* lins, int from, int to) const; + + bool PushSinglePathResultInstructions(void); + + bool CanSwapInstructions(const InterInstruction* ins0, const InterInstruction* ins1) const; + bool CanMoveInstructionBeforeBlock(int ii) const; + bool CanMoveInstructionBeforeBlock(int ii, const InterInstruction * ins) const; + bool CanMoveInstructionBehindBlock(int ii) const; + bool CanMoveInstructionDown(int si, int ti) const; + int FindSameInstruction(const InterInstruction* ins) const; + bool MergeCommonPathInstructions(void); + + bool IsTempModified(int temp); + bool IsTempModifiedInRange(int from, int to, int temp); + bool IsTempUsedInRange(int from, int to, int temp); + bool IsTempReferenced(int temp); + bool IsTempReferencedInRange(int from, int to, int temp); + + bool IsInsModified(const InterInstruction* ins); + bool IsInsModifiedInRange(int from, int to, const InterInstruction* ins); + + InterInstruction* FindTempOrigin(int temp) const; + InterInstruction* FindTempOriginSinglePath(int temp) const; + + bool CheapInlining(int & numTemps); + bool CollapseDispatch(); + bool StructReturnPropagation(void); + bool MapLateIntrinsics(void); + + void CheckFinalLocal(void); + void CheckFinal(void); + void CheckBlocks(void); + + bool IsConstExitTemp(int temp) const; + bool CommonTailCodeMerge(void); + bool SplitSingleBranchUseConst(void); + + void PeepholeOptimization(const GrowingVariableArray& staticVars, const GrowingInterCodeProcedurePtrArray& staticProcs); + bool PeepholeReplaceOptimization(const GrowingVariableArray& staticVars, const GrowingInterCodeProcedurePtrArray& staticProcs); + + int NumUnrollInstructions(int ireg); + + bool MoveLoopHeadCheckToTail(void); + void SingleBlockLoopOptimisation(const NumberSet& aliasedParams, const GrowingVariableArray& staticVars); + void SingleBlockLoopUnrolling(void); + bool SingleBlockLoopPointerSplit(int& spareTemps); + bool SingleBlockLoopPointerToByte(int& spareTemps); + bool SingleBlockLoopSinking(int& spareTemps); + bool SingleBlockLoopIndexReduction(int& spareTemps); + bool CollectLoopBody(InterCodeBasicBlock* head, ExpandingArray & body); + bool CollectLoopBodyRecursive(InterCodeBasicBlock* head, ExpandingArray& body); + void CollectLoopPath(const ExpandingArray& body, ExpandingArray& path); + void InnerLoopOptimization(const NumberSet& aliasedParams); + void ConstSingleLoopOptimization(void); + void ConstInnerLoopOptimization(void); + bool EmptyLoopOptimization(void); + void EliminateDoubleLoopCounter(void); + void PushMoveOutOfLoop(void); + bool MoveConditionOutOfLoop(void); + void SingleLoopCountZeroCheck(void); + void InnerLoopCountZeroCheck(void); + bool PostDecLoopOptimization(void); + bool Flatten2DLoop(void); + + bool UntangleLoadStoreSequence(void); + + void PropagateMemoryAliasingInfo(const GrowingInstructionPtrArray& tvalue, bool loops); + void RemoveUnusedMallocs(void); + + void ReduceTempLivetimes(void); + + bool PullStoreUpToConstAddress(void); + + bool CollectSingleHeadLoopBody(InterCodeBasicBlock* head, InterCodeBasicBlock* tail, ExpandingArray& body); + + bool CollectGenericLoop(ExpandingArray& lblocks); + bool CollectSingleEntryGenericLoop(ExpandingArray& lblocks); + void CollectReachable(ExpandingArray& lblock); + + void LimitLoopIndexRanges(void); + bool SingleTailLoopOptimization(const NumberSet& aliasedParams, const GrowingVariableArray& staticVars); + bool MergeLoopTails(void); + + void ReplaceCopyFill(void); + + bool ChangeTrueJump(InterCodeBasicBlock* block); + bool ChangeFalseJump(InterCodeBasicBlock* block); + + InterCodeBasicBlock* CheckIsSimpleIntRangeBranch(const GrowingIntegerValueRangeArray & irange); + InterCodeBasicBlock* CheckIsConstBranch(const GrowingInstructionPtrArray& cins); + bool ShortcutConstBranches(const GrowingInstructionPtrArray& cins); + bool ShortcutDuplicateBranches(void); + + InterCodeBasicBlock* BuildLoopPrefix(void); + void BuildLoopSuffix(void); + + void ExpandSelect(void); + + void SplitBranches(void); + void FollowJumps(void); + + bool ShortLeaMerge(int& spareTemps); + bool ShortLeaCleanup(void); + + bool IsEqual(const InterCodeBasicBlock* block) const; + + void CompactInstructions(void); + bool OptimizeIntervalCompare(void); + + bool DropUnreachable(void); + + void CollectByteIndexPointers(NumberSet& invtemps, NumberSet& inctemps, GrowingIntArray & vartemps); + bool ReplaceByteIndexPointers(const NumberSet& inctemps, const GrowingIntArray& vartemps, int& spareTemps); + bool PropagateByteIndexPointers(void); + + bool CheckStaticStack(void); + void ApplyStaticStack(InterOperand& iop, const GrowingVariableArray& localVars); + void CollectStaticStack(LinkerObject * lobj, const GrowingVariableArray& localVars); + void CollectStaticStackDependencies(LinkerObject* lobj); + void PromoteStaticStackParams(LinkerObject* paramlobj); + + bool SameExitCode(const InterCodeBasicBlock* block) const; + + void WarnUsedUndefinedVariables(void); + void WarnInvalidValueRanges(void); + void CheckValueReturn(void); + void CheckNullptrDereference(void); + + void CollectGlobalReferences(NumberSet& referencedGlobals, NumberSet& modifiedGlobals, bool & storesIndirect, bool & loadsIndirect, bool & globalsChecked); + + void MarkAliasing(const NumberSet& aliasedParams); +}; + +class InterCodeProcedure +{ +friend class InterCodeBasicBlock; +protected: + GrowingIntArray mRenameTable, mRenameUnionTable, mGlobalRenameTable; + TempForwardingTable mTempForwardingTable; + GrowingInstructionPtrArray mValueForwardingTable; + GrowingIntegerValueRangeArray mLocalValueRange, mReverseValueRange; + + void ResetVisited(void); + void ResetPatched(void); + void ResetEntryBlocks(void); +public: + InterCodeBasicBlock * mEntryBlock; + int mNumBlocks; + GrowingInterCodeBasicBlockPtrArray mBlocks; + GrowingTypeArray mTemporaries; + GrowingIntArray mTempOffset, mTempSizes; + int mTempSize, mCommonFrameSize, mCallerSavedTemps, mFreeCallerSavedTemps, mFastCallBase, mNumRestricted; + bool mLeafProcedure, mNativeProcedure, mCallsFunctionPointer, mHasDynamicStack, mHasInlineAssembler, mCallsByteCode, mFastCallProcedure; + bool mInterrupt, mHardwareInterrupt, mCompiled, mInterruptCalled, mValueReturn, mFramePointer, mDynamicStack, mAssembled; + bool mDispatchedCall; + bool mCheckUnreachable, mIntrinsicFunction; + GrowingInterCodeProcedurePtrArray mCalledFunctions; + bool mCheapInline; + bool mNoInline; + + InterCodeModule * mModule; + int mID; + + int mLocalSize, mNumLocals, mNumParams, mParamVarsSize; + GrowingVariableArray mLocalVars, mParamVars; + NumberSet mLocalAliasedSet, mParamAliasedSet; + + Location mLocation; + const Ident * mIdent, * mSection; + + LinkerObject * mLinkerObject, * mSaveTempsLinkerObject; + Declaration * mDeclaration; + InterType mReturnType; + uint64 mCompilerOptions; + + bool mLoadsIndirect, mStoresIndirect, mGlobalsChecked; + NumberSet mReferencedGlobals, mModifiedGlobals; + + InterCodeProcedure(InterCodeModule * module, const Location & location, const Ident * ident, LinkerObject* linkerObject); + ~InterCodeProcedure(void); + + int AddTemporary(InterType type); + int AddRestricted(void); + + void Close(void); + +// void Set(InterCodeIDMapper* mapper, BitVector localStructure, Scanner scanner, bool debug); + + void AddCalledFunction(InterCodeProcedure* proc); + void CallsFunctionPointer(void); + + void MarkRelevantStatics(void); + void RemoveNonRelevantStatics(void); + + void MapCallerSavedTemps(void); + + bool ReferencesGlobal(int varindex); + bool ModifiesGlobal(int varindex); + + void MapVariables(void); + void ReduceTemporaries(bool final = false); + void Disassemble(FILE* file); + void Disassemble(const char* name, bool dumpSets = false); +protected: + void BuildLocalAliasTable(void); + void BuildTraces(int expand, bool dominators = true, bool compact = false); + void TrimBlocks(void); + void EarlyBranchElimination(void); + void BuildDataFlowSets(void); + void RenameTemporaries(void); + void TempForwarding(bool reverse = false, bool checkloops = false); + void RemoveUnusedInstructions(void); + bool GlobalConstantPropagation(void); + bool PropagateNonLocalUsedTemps(void); + void BuildLoopPrefix(void); + void SingleAssignmentForwarding(void); + void RemoveUnusedStoreInstructions(InterMemory paramMemory); + void RemoveUnusedPartialStoreInstructions(void); + void RemoveUnusedLocalStoreInstructions(void); + void RemoveUndefinedPartialLocalInstructions(void); + void MergeCommonPathInstructions(void); + void PushSinglePathResultInstructions(void); + void CollectVariables(InterMemory paramMemory); + void PromoteSimpleLocalsToTemp(InterMemory paramMemory, int nlocals, int nparams); + void SimplifyIntegerNumeric(FastNumberSet& activeSet); + void SingleBlockLoopPointerSplit(FastNumberSet& activeSet); + void SingleBlockLoopPointerToByte(FastNumberSet& activeSet); + void SingleBlockLoopSinking(FastNumberSet& activeSet); + void SingleBlockLoopIndexReduction(FastNumberSet& activeSet); + void MergeIndexedLoadStore(void); + void EliminateIntegerSumAliasTemps(void); + void EliminateAliasValues(); + void LoadStoreForwarding(InterMemory paramMemory); + void ReduceRecursionTempSpilling(InterMemory paramMemory); + void ExpandSelect(void); + void PropagateConstOperationsUp(void); + void RebuildIntegerRangeSet(void); + void CombineIndirectAddressing(void); + void SingleTailLoopOptimization(InterMemory paramMemory); + void LimitLoopIndexRanges(void); + void HoistCommonConditionalPath(void); + void RemoveUnusedMallocs(void); + void RecheckLocalAliased(void); + void ConstSingleLoopOptimization(void); + void ConstInnerLoopOptimization(void); + void UntangleLoadStoreSequence(void); + + bool ShortLeaMerge(FastNumberSet& activeSet); + + void MergeBasicBlocks(FastNumberSet& activeSet); + void CheckUsedDefinedTemps(void); + void WarnUsedUndefinedVariables(void); + void WarnInvalidValueRanges(void); + void PropagateMemoryAliasingInfo(bool loops); + void MoveConditionsOutOfLoop(void); + void ShortcutConstBranches(void); + void ShortcutDuplicateBranches(void); + void EliminateDoubleLoopCounter(void); + void StructReturnPropagation(void); + void ForwardSingleAssignmentBools(void); + void LimitLoopIndexIntegerRangeSets(void); + + bool PropagateByteIndexPointers(void); + bool ReplaceByteIndexPointers(FastNumberSet& activeSet); + + void CollapseDispatch(void); + + void PeepholeOptimization(void); + + void CheckFinal(void); + void CheckBlocks(void); + + void DisassembleDebug(const char* name, bool dumpSets = false); +}; + +class InterCodeModule +{ +public: + InterCodeModule(Errors* errors, Linker * linker); + ~InterCodeModule(void); + + void InitParamStack(LinkerSection * stackSection); + + bool Disassemble(const char* name); + + GrowingInterCodeProcedurePtrArray mProcedures; + + GrowingVariableArray mGlobalVars; + LinkerObject * mParamLinkerObject; + LinkerSection * mParamLinkerSection; + + Linker * mLinker; + Errors* mErrors; + + uint64 mCompilerOptions; + +}; diff --git a/oscar64/oscar64/InterCodeGenerator.cpp b/oscar64/oscar64/InterCodeGenerator.cpp new file mode 100644 index 0000000..be3d501 --- /dev/null +++ b/oscar64/oscar64/InterCodeGenerator.cpp @@ -0,0 +1,6572 @@ +#include "InterCodeGenerator.h" +#include "Constexpr.h" + +InterCodeGenerator::InterCodeGenerator(Errors* errors, Linker* linker) + : mErrors(errors), mLinker(linker), mCompilerOptions(COPT_DEFAULT) +{ + mMainInitBlock = nullptr; +} + +InterCodeGenerator::~InterCodeGenerator(void) +{ + +} + +static inline InterType InterTypeOfSize(int size) +{ + if (size <= 1) + return IT_INT8; + else if (size <= 2) + return IT_INT16; + else + return IT_INT32; +} + +static inline InterType InterTypeOf(const Declaration* dec) +{ + switch (dec->mType) + { + default: + case DT_TYPE_VOID: + return IT_NONE; + case DT_TYPE_INTEGER: + case DT_TYPE_ENUM: + if (dec->mSize == 1) + return IT_INT8; + else if (dec->mSize == 2) + return IT_INT16; + else + return IT_INT32; + case DT_TYPE_BOOL: + return IT_BOOL; + case DT_TYPE_FLOAT: + return IT_FLOAT; + case DT_TYPE_POINTER: + case DT_TYPE_FUNCTION: + case DT_TYPE_ARRAY: + case DT_TYPE_REFERENCE: + case DT_TYPE_RVALUEREF: + return IT_POINTER; + } +} + +InterCodeGenerator::ExValue InterCodeGenerator::ToValue(InterCodeProcedure* proc, Expression* exp, InterCodeBasicBlock*& block, InlineMapper* inlineMapper, ExValue v) +{ + if (v.mType && v.mType->IsReference()) + { + v.mType = v.mType->mBase; + v.mReference++; + } + + return v; +} + +InterCodeGenerator::ExValue InterCodeGenerator::Dereference(InterCodeProcedure* proc, Expression* exp, InterCodeBasicBlock*& block, InlineMapper* inlineMapper, ExValue v, int level, int limit) +{ + while (v.mReference > level) + { + InterInstruction * ins = new InterInstruction(MapLocation(exp, inlineMapper), IC_LOAD); + ins->mSrc[0].mMemory = IM_INDIRECT; + ins->mSrc[0].mType = IT_POINTER; + ins->mSrc[0].mTemp = v.mTemp; + ins->mDst.mType = v.mReference == 1 ? InterTypeOf(v.mType) : IT_POINTER; + + if (ins->mDst.mType == IT_NONE) + { + mErrors->Error(exp->mLocation, EERR_INVALID_VALUE, "Not a simple type"); + return v; + } + + ins->mDst.mTemp = proc->AddTemporary(ins->mDst.mType); + ins->mSrc[0].mOperandSize = v.mReference == 1 ? v.mType->mSize : 2; + ins->mSrc[0].mStride = v.mReference == 1 ? v.mType->mStripe : 1; + + + if (v.mReference == 1) + { + if (v.mType->mType == DT_TYPE_ENUM && !v.mBits) + { + ins->mDst.mRange.LimitMin(v.mType->mMinValue); + ins->mDst.mRange.LimitMax(v.mType->mMaxValue); + } + else if (v.mType->mType == DT_TYPE_INTEGER && !v.mBits) + { + if (limit > 0) + { + ins->mDst.mRange.LimitMin(0); + ins->mDst.mRange.LimitMax(limit - 1); + } +#if 1 + else + { + ins->mDst.mRange.LimitMin(v.mType->MinInteger()); + ins->mDst.mRange.LimitMax(v.mType->MaxInteger()); + } +#endif + } + } + + if (v.mType->mFlags & DTF_VOLATILE) + ins->mVolatile = true; + block->Append(ins); + + if (v.mReference == 1 && v.mBits) + { + if (v.mBits + v.mShift <= 8) + ins->mDst.mType = IT_INT8; + else if (v.mBits + v.mShift <= 16) + ins->mDst.mType = IT_INT16; + else + ins->mDst.mType = IT_INT32; + + ins->mSrc[0].mOperandSize = InterTypeSize[ins->mDst.mType]; + int nbits = ins->mSrc[0].mOperandSize * 8; + + InterInstruction* clsins = new InterInstruction(MapLocation(exp, inlineMapper), IC_CONSTANT); + clsins->mDst.mType = IT_INT8; + clsins->mDst.mTemp = proc->AddTemporary(clsins->mDst.mType); + clsins->mConst.mType = IT_INT8; + clsins->mConst.mIntConst = nbits - v.mShift - v.mBits; + clsins->mNumOperands = 0; + block->Append(clsins); + + InterInstruction* crsins = new InterInstruction(MapLocation(exp, inlineMapper), IC_CONSTANT); + crsins->mDst.mType = IT_INT8; + crsins->mDst.mTemp = proc->AddTemporary(crsins->mDst.mType); + crsins->mConst.mType = IT_INT8; + crsins->mConst.mIntConst = nbits - v.mBits; + crsins->mNumOperands = 0; + block->Append(crsins); + + InterInstruction* slins = new InterInstruction(MapLocation(exp, inlineMapper), IC_BINARY_OPERATOR); + slins->mOperator = IA_SHL; + slins->mDst.mType = ins->mDst.mType; + slins->mDst.mTemp = proc->AddTemporary(slins->mDst.mType); + slins->mSrc[1] = ins->mDst; + slins->mSrc[0] = clsins->mDst; + slins->mNumOperands = 2; + block->Append(slins); + + InterInstruction* srins = new InterInstruction(MapLocation(exp, inlineMapper), IC_BINARY_OPERATOR); + srins->mOperator = (v.mType->mFlags & DTF_SIGNED) ? IA_SAR : IA_SHR; + srins->mDst.mType = ins->mDst.mType; + srins->mDst.mTemp = proc->AddTemporary(slins->mDst.mType); + srins->mSrc[1] = slins->mDst; + srins->mSrc[0] = crsins->mDst; + srins->mNumOperands = 2; + block->Append(srins); + + if (v.mType->mType == DT_TYPE_ENUM) + { + srins->mDst.mRange.LimitMin(v.mType->mMinValue); + srins->mDst.mRange.LimitMax(v.mType->mMaxValue); + } + + // Promote unsigned bitfields that fit into a signed int to signed int + Declaration* vtype = v.mType; + if (vtype->mSize == 2 && v.mBits < 16 && !(vtype->mFlags & DTF_SIGNED)) + vtype = TheSignedIntTypeDeclaration; + + if (InterTypeSize[ins->mDst.mType] < v.mType->mSize) + { + InterInstruction* crins = new InterInstruction(MapLocation(exp, inlineMapper), IC_CONVERSION_OPERATOR); + crins->mDst.mType = InterTypeOf(v.mType); + crins->mDst.mTemp = proc->AddTemporary(crins->mDst.mType); + crins->mSrc[0] = srins->mDst; + crins->mNumOperands = 1; + if (ins->mDst.mType == IT_INT16) + crins->mOperator = (v.mType->mFlags & DTF_SIGNED) ? IA_EXT16TO32S : IA_EXT16TO32U; + else if (v.mType->mSize == 2) + crins->mOperator = (v.mType->mFlags & DTF_SIGNED) ? IA_EXT8TO16S : IA_EXT8TO16U; + else + crins->mOperator = (v.mType->mFlags & DTF_SIGNED) ? IA_EXT8TO32S : IA_EXT8TO32U; + block->Append(crins); + v = ExValue(vtype, crins->mDst.mTemp, v.mReference - 1); + } + else + v = ExValue(vtype, srins->mDst.mTemp, v.mReference - 1); + } + else + v = ExValue(v.mType, ins->mDst.mTemp, v.mReference - 1, v.mBits, v.mShift); + } + + return v; +} + +void InterCodeGenerator::StoreValue(InterCodeProcedure* proc, Expression* exp, InterCodeBasicBlock*& block, InlineMapper* inlineMapper, ExValue vl, ExValue vr) +{ + // Bitfield assignment + if (vl.mBits) + { + InterType itype; + + if (vl.mBits + vl.mShift <= 8) + itype = IT_INT8; + else if (vl.mBits + vl.mShift <= 16) + itype = IT_INT16; + else + itype = IT_INT32; + + int nbits = InterTypeSize[itype] * 8; + + InterInstruction* lins = new InterInstruction(MapLocation(exp, inlineMapper), IC_LOAD); + lins->mDst.mType = itype; + lins->mDst.mTemp = proc->AddTemporary(lins->mDst.mType); + lins->mSrc[0].mMemory = IM_INDIRECT; + lins->mSrc[0].mType = IT_POINTER; + lins->mSrc[0].mTemp = vl.mTemp; + lins->mSrc[0].mOperandSize = InterTypeSize[itype]; + lins->mVolatile = vl.mType->mFlags & DTF_VOLATILE; + lins->mNumOperands = 1; + block->Append(lins); + + InterInstruction* csins = new InterInstruction(MapLocation(exp, inlineMapper), IC_CONSTANT); + csins->mDst.mType = IT_INT8; + csins->mDst.mTemp = proc->AddTemporary(csins->mDst.mType); + csins->mConst.mType = IT_INT8; + csins->mConst.mIntConst = vl.mShift; + csins->mNumOperands = 0; + block->Append(csins); + + InterInstruction* cmsins = new InterInstruction(MapLocation(exp, inlineMapper), IC_CONSTANT); + cmsins->mDst.mType = itype; + cmsins->mDst.mTemp = proc->AddTemporary(cmsins->mDst.mType); + cmsins->mConst.mType = itype; + cmsins->mConst.mIntConst = ((1LL << vl.mBits) - 1) << vl.mShift; + cmsins->mNumOperands = 0; + block->Append(cmsins); + + InterInstruction* cmlins = new InterInstruction(MapLocation(exp, inlineMapper), IC_CONSTANT); + cmlins->mDst.mType = itype; + cmlins->mDst.mTemp = proc->AddTemporary(cmlins->mDst.mType); + cmlins->mConst.mType = itype; + cmlins->mConst.mIntConst = ~cmsins->mConst.mIntConst & ((1LL << nbits) - 1); + cmlins->mNumOperands = 0; + block->Append(cmlins); + + int rtemp = vr.mTemp; + + if (InterTypeSize[itype] > vr.mType->mSize) + { + InterInstruction* cins = new InterInstruction(MapLocation(exp, inlineMapper), IC_CONVERSION_OPERATOR); + + if (InterTypeSize[itype] == 2) + cins->mOperator = IA_EXT8TO16U; + else if (vr.mType->mSize == 1) + cins->mOperator = IA_EXT8TO32U; + else + cins->mOperator = IA_EXT8TO16U; + + cins->mDst.mType = itype; + cins->mDst.mTemp = proc->AddTemporary(itype); + cins->mSrc[0].mTemp = rtemp; + cins->mSrc[0].mType = InterTypeOf(vr.mType); + block->Append(cins); + + rtemp = cins->mDst.mTemp; + } + + InterInstruction* sins = new InterInstruction(MapLocation(exp, inlineMapper), IC_BINARY_OPERATOR); + sins->mOperator = IA_SHL; + sins->mDst.mType = itype; + sins->mDst.mTemp = proc->AddTemporary(sins->mDst.mType); + sins->mSrc[1].mTemp = rtemp; + sins->mSrc[1].mType = itype; + sins->mSrc[0] = csins->mDst; + sins->mNumOperands = 2; + block->Append(sins); + + InterInstruction* msins = new InterInstruction(MapLocation(exp, inlineMapper), IC_BINARY_OPERATOR); + msins->mOperator = IA_AND; + msins->mDst.mType = itype; + msins->mDst.mTemp = proc->AddTemporary(msins->mDst.mType); + msins->mSrc[0] = sins->mDst; + msins->mSrc[1] = cmsins->mDst; + msins->mNumOperands = 2; + block->Append(msins); + + InterInstruction* mlins = new InterInstruction(MapLocation(exp, inlineMapper), IC_BINARY_OPERATOR); + mlins->mOperator = IA_AND; + mlins->mDst.mType = itype; + mlins->mDst.mTemp = proc->AddTemporary(mlins->mDst.mType); + mlins->mSrc[0] = lins->mDst; + mlins->mSrc[1] = cmlins->mDst; + mlins->mNumOperands = 2; + block->Append(mlins); + + InterInstruction* oins = new InterInstruction(MapLocation(exp, inlineMapper), IC_BINARY_OPERATOR); + oins->mOperator = IA_OR; + oins->mDst.mType = itype; + oins->mDst.mTemp = proc->AddTemporary(oins->mDst.mType); + oins->mSrc[0] = msins->mDst; + oins->mSrc[1] = mlins->mDst; + oins->mNumOperands = 2; + block->Append(oins); + + InterInstruction* ins = new InterInstruction(MapLocation(exp, inlineMapper), IC_STORE); + ins->mSrc[1].mMemory = IM_INDIRECT; + ins->mSrc[0] = oins->mDst; + ins->mSrc[1].mType = IT_POINTER; + ins->mSrc[1].mTemp = vl.mTemp; + ins->mSrc[1].mOperandSize = InterTypeSize[itype]; + ins->mVolatile = vl.mType->mFlags & DTF_VOLATILE; + ins->mMemmap = vl.mType->mFlags & DTF_MEMMAP; + ins->mNumOperands = 2; + block->Append(ins); + } + else + { + InterInstruction* ins = new InterInstruction(MapLocation(exp, inlineMapper), IC_STORE); + + ins->mSrc[1].mMemory = IM_INDIRECT; + ins->mSrc[0].mType = InterTypeOf(vr.mType); + ins->mSrc[0].mTemp = vr.mTemp; + ins->mSrc[1].mType = IT_POINTER; + ins->mSrc[1].mTemp = vl.mTemp; + ins->mSrc[1].mOperandSize = vl.mType->mSize; + ins->mSrc[1].mStride = vl.mType->mStripe; + ins->mVolatile = vl.mType->mFlags & DTF_VOLATILE; + ins->mMemmap = vl.mType->mFlags & DTF_MEMMAP; + ins->mNumOperands = 2; + block->Append(ins); + } +} + +InterCodeGenerator::ExValue InterCodeGenerator::CoerceType(InterCodeProcedure* proc, Expression* exp, InterCodeBasicBlock*& block, InlineMapper* inlineMapper, ExValue v, Declaration* type, bool checkTrunc) +{ + int stemp = v.mTemp; + + if (type->IsReference()) + { + if (v.mType->IsReference()) + { + if (!type->mBase->IsSubType(v.mType->mBase)) + mErrors->Error(exp->mLocation, EERR_INCOMPATIBLE_TYPES, "Incompatible type for reference"); + } + else if (!type->mBase->IsSubType(v.mType)) + mErrors->Error(exp->mLocation, EERR_INCOMPATIBLE_TYPES, "Incompatible type for reference"); + + return v; + } + else if (type->mType == DT_TYPE_STRUCT && type->mBase && type->mBase->IsSubType(v.mType)) + { + v.mType = type; + return v; + } + + if (v.mType->IsReference() && type->IsSimpleType()) + { + v.mReference++; + v.mType = v.mType->mBase; + v = Dereference(proc, exp, block, inlineMapper, v); + } + + if (v.mType->IsIntegerType() && type->mType == DT_TYPE_FLOAT) + { + if (v.mType->mSize == 1) + { + if (v.mType->mFlags & DTF_SIGNED) + { + InterInstruction * xins = new InterInstruction(MapLocation(exp, inlineMapper), IC_CONVERSION_OPERATOR); + xins->mOperator = IA_EXT8TO16S; + xins->mSrc[0].mType = IT_INT8; + xins->mSrc[0].mTemp = stemp; + xins->mDst.mType = IT_INT16; + xins->mDst.mTemp = proc->AddTemporary(IT_INT16); + block->Append(xins); + stemp = xins->mDst.mTemp; + } + else + { + InterInstruction * xins = new InterInstruction(MapLocation(exp, inlineMapper), IC_CONVERSION_OPERATOR); + xins->mOperator = IA_EXT8TO16U; + xins->mSrc[0].mType = IT_INT8; + xins->mSrc[0].mTemp = stemp; + xins->mDst.mType = IT_INT16; + xins->mDst.mTemp = proc->AddTemporary(IT_INT16); + block->Append(xins); + stemp = xins->mDst.mTemp; + } + } + + InterInstruction * cins = new InterInstruction(MapLocation(exp, inlineMapper), IC_CONVERSION_OPERATOR); + if (v.mType->mSize <= 2) + { + cins->mOperator = (v.mType->mFlags & DTF_SIGNED) ? IA_INT2FLOAT : IA_UINT2FLOAT; + cins->mSrc[0].mType = IT_INT16; + } + else + { + cins->mOperator = (v.mType->mFlags & DTF_SIGNED) ? IA_LINT2FLOAT : IA_LUINT2FLOAT; + cins->mSrc[0].mType = IT_INT32; + } + cins->mSrc[0].mTemp = stemp; + cins->mDst.mType = IT_FLOAT; + cins->mDst.mTemp = proc->AddTemporary(IT_FLOAT); + block->Append(cins); + + v.mTemp = cins->mDst.mTemp; + v.mType = type; + } + else if (v.mType->mType == DT_TYPE_FLOAT && type->IsIntegerType()) + { + mErrors->Error(exp->mLocation, EWARN_FLOAT_TO_INT, "Float to int conversion, potential loss of precision"); + + InterInstruction * cins = new InterInstruction(MapLocation(exp, inlineMapper), IC_CONVERSION_OPERATOR); + + if (type->mSize <= 2) + { + cins->mDst.mType = IT_INT16; + if (type->mFlags & DTF_SIGNED) + cins->mOperator = IA_FLOAT2INT; + else + cins->mOperator = IA_FLOAT2UINT; + } + else + { + cins->mDst.mType = IT_INT32; + if (type->mFlags & DTF_SIGNED) + cins->mOperator = IA_FLOAT2LINT; + else + cins->mOperator = IA_FLOAT2LUINT; + } + + cins->mSrc[0].mType = IT_FLOAT; + cins->mSrc[0].mTemp = v.mTemp; + + cins->mDst.mTemp = proc->AddTemporary(cins->mDst.mType); + block->Append(cins); + v.mTemp = cins->mDst.mTemp; + v.mType = type; + } + else if (type->mType == DT_TYPE_POINTER && v.mType->mType == DT_TYPE_ARRAY) + { + v.mType = type; + return v; + } + else if (type->mType == DT_TYPE_POINTER && v.mType->IsNumericType() && (mCompilerOptions & COPT_CPLUSPLUS)) + { + mErrors->Error(exp->mLocation, EERR_INCOMPATIBLE_TYPES, "Cannot assign numeric value to pointer"); + } + else if( type->mType == DT_TYPE_BOOL && v.mType->mType == DT_TYPE_POINTER) + { + InterInstruction* zins = new InterInstruction(MapLocation(exp, inlineMapper), IC_CONSTANT); + zins->mDst.mType = IT_POINTER; + zins->mDst.mTemp = proc->AddTemporary(IT_POINTER); + zins->mConst.mType = IT_POINTER; + zins->mConst.mMemory = IM_ABSOLUTE; + zins->mConst.mIntConst = 0; + block->Append(zins); + + InterInstruction* cins = new InterInstruction(MapLocation(exp, inlineMapper), IC_RELATIONAL_OPERATOR); + cins->mOperator = IA_CMPNE; + cins->mSrc[0].mType = IT_POINTER; + cins->mSrc[0].mTemp = v.mTemp; + cins->mSrc[1].mType = IT_POINTER; + cins->mSrc[1].mTemp = zins->mDst.mTemp; + cins->mDst.mType = IT_BOOL; + cins->mDst.mTemp = proc->AddTemporary(IT_BOOL); + block->Append(cins); + v.mTemp = cins->mDst.mTemp; + v.mType = type; + } +#if 1 + else if (type->mType == DT_TYPE_BOOL && v.mType->IsIntegerType() && v.mType->mType != DT_TYPE_BOOL) + { + InterInstruction* zins = new InterInstruction(MapLocation(exp, inlineMapper), IC_CONSTANT); + zins->mDst.mType = InterTypeOf(v.mType); + zins->mDst.mTemp = proc->AddTemporary(zins->mDst.mType); + zins->mConst.mType = zins->mDst.mType; + zins->mConst.mIntConst = 0; + block->Append(zins); + + InterInstruction* cins = new InterInstruction(MapLocation(exp, inlineMapper), IC_RELATIONAL_OPERATOR); + cins->mOperator = IA_CMPNE; + cins->mSrc[0].mType = zins->mDst.mType; + cins->mSrc[0].mTemp = v.mTemp; + cins->mSrc[1].mType = zins->mDst.mType; + cins->mSrc[1].mTemp = zins->mDst.mTemp; + cins->mDst.mType = IT_BOOL; + cins->mDst.mTemp = proc->AddTemporary(IT_BOOL); + block->Append(cins); + v.mTemp = cins->mDst.mTemp; + v.mType = type; + } +#endif + else if (v.mType->mSize < type->mSize) + { + if (v.mType->mSize == 1 && type->mSize == 2) + { + if (v.mType->mFlags & DTF_SIGNED) + { + InterInstruction * xins = new InterInstruction(MapLocation(exp, inlineMapper), IC_CONVERSION_OPERATOR); + xins->mOperator = IA_EXT8TO16S; + xins->mSrc[0].mType = IT_INT8; + xins->mSrc[0].mTemp = stemp; + xins->mDst.mType = IT_INT16; + xins->mDst.mTemp = proc->AddTemporary(IT_INT16); + block->Append(xins); + stemp = xins->mDst.mTemp; + } + else + { + InterInstruction * xins = new InterInstruction(MapLocation(exp, inlineMapper), IC_CONVERSION_OPERATOR); + xins->mOperator = IA_EXT8TO16U; + xins->mSrc[0].mType = IT_INT8; + xins->mSrc[0].mTemp = stemp; + xins->mDst.mType = IT_INT16; + xins->mDst.mTemp = proc->AddTemporary(IT_INT16); + block->Append(xins); + stemp = xins->mDst.mTemp; + } + } + else if (v.mType->mSize == 2 && type->mSize == 4) + { + if (v.mType->mFlags & DTF_SIGNED) + { + InterInstruction* xins = new InterInstruction(MapLocation(exp, inlineMapper), IC_CONVERSION_OPERATOR); + xins->mOperator = IA_EXT16TO32S; + xins->mSrc[0].mType = IT_INT16; + xins->mSrc[0].mTemp = stemp; + xins->mDst.mType = IT_INT32; + xins->mDst.mTemp = proc->AddTemporary(IT_INT32); + block->Append(xins); + stemp = xins->mDst.mTemp; + } + else + { + InterInstruction* xins = new InterInstruction(MapLocation(exp, inlineMapper), IC_CONVERSION_OPERATOR); + xins->mOperator = IA_EXT16TO32U; + xins->mSrc[0].mType = IT_INT16; + xins->mSrc[0].mTemp = stemp; + xins->mDst.mType = IT_INT32; + xins->mDst.mTemp = proc->AddTemporary(IT_INT32); + block->Append(xins); + stemp = xins->mDst.mTemp; + } + } + else if (v.mType->mSize == 1 && type->mSize == 4) + { + if (v.mType->mFlags & DTF_SIGNED) + { + InterInstruction* xins = new InterInstruction(MapLocation(exp, inlineMapper), IC_CONVERSION_OPERATOR); + xins->mOperator = IA_EXT8TO32S; + xins->mSrc[0].mType = IT_INT8; + xins->mSrc[0].mTemp = stemp; + xins->mDst.mType = IT_INT32; + xins->mDst.mTemp = proc->AddTemporary(IT_INT32); + block->Append(xins); + stemp = xins->mDst.mTemp; + } + else + { + InterInstruction* xins = new InterInstruction(MapLocation(exp, inlineMapper), IC_CONVERSION_OPERATOR); + xins->mOperator = IA_EXT8TO32U; + xins->mSrc[0].mType = IT_INT8; + xins->mSrc[0].mTemp = stemp; + xins->mDst.mType = IT_INT32; + xins->mDst.mTemp = proc->AddTemporary(IT_INT32); + block->Append(xins); + stemp = xins->mDst.mTemp; + } + } + + v.mTemp = stemp; + v.mType = type; + } + else + { + // ignore size reduction + if (checkTrunc && v.mType->mSize > type->mSize) + { + const InterInstruction* ins = block->FindByDst(stemp); + if (ins && ins->mCode == IC_CONSTANT) + { + int64 min = 0, max = 0; + + if (type->mFlags & DTF_SIGNED) + { + switch (type->mSize) + { + case 1: + min = -128; max = 127; + break; + case 2: + min = -32768; max = 32767; + break; + case 4: + min = -2147483648LL; max = 2147483647LL; + break; + } + } + else + { + switch (type->mSize) + { + case 1: + max = 255; + break; + case 2: + max = 65535; + break; + case 4: + max = 429467295LL; + break; + } + } + + if (ins->mConst.mIntConst < min || ins->mConst.mIntConst > max) + { + int64 min = 0, max = 0; + + if (type->mFlags & DTF_SIGNED) + { + switch (type->mSize) + { + case 1: + min = -128; max = 127; + break; + case 2: + min = -32768; max = 32767; + break; + case 4: + min = -2147483648LL; max = 2147483647LL; + break; + } + } + else + { + switch (type->mSize) + { + case 1: + max = 255; + break; + case 2: + max = 65535; + break; + case 4: + max = 429467295LL; + break; + } + } + + if (ins->mConst.mIntConst < min || ins->mConst.mIntConst > max) + { + mErrors->Error(exp->mLocation, EWARN_CONSTANT_TRUNCATED, "Integer constant truncated"); + } + } + } + } + v.mType = type; + } + + return v; +} + +static inline bool InterTypeTypeInteger(InterType t) +{ + return t == IT_INT8 || t == IT_INT16 || t == IT_INT32 || t == IT_BOOL; +} + +static inline InterType InterTypeOfArithmetic(InterType t1, InterType t2) +{ + if (t1 == IT_FLOAT || t2 == IT_FLOAT) + return IT_FLOAT; + else if (InterTypeTypeInteger(t1) && InterTypeTypeInteger(t2)) + return t1 > t2 ? t1 : t2; + else if (t1 == IT_POINTER && t2 == IT_POINTER) + return IT_INT16; + else if (t1 == IT_POINTER || t2 == IT_POINTER) + return IT_POINTER; + else + return IT_INT16; +} + +void InterCodeGenerator::InitParameter(InterCodeProcedure* proc, Declaration* dec, int index) +{ + if (!proc->mParamVars[index]) + { + proc->mParamVars[index] = new InterVariable(); + proc->mParamVars[index]->mIdent = dec->mIdent; + proc->mParamVars[index]->mDeclaration = dec; + } +} + +void InterCodeGenerator::InitLocalVariable(InterCodeProcedure* proc, Declaration* dec, int index) +{ + if (!proc->mLocalVars[index]) + { + proc->mLocalVars[index] = new InterVariable(); + proc->mLocalVars[index]->mIdent = dec->mIdent; + proc->mLocalVars[index]->mDeclaration = dec; + proc->mLocalVars[index]->mAlignment = dec->mBase->Alignment(); + } + else + { + assert(proc->mLocalVars[index]->mIdent == dec->mIdent); + assert(proc->mLocalVars[index]->mDeclaration == dec); + } +} + +static const Ident* StructIdent(const Ident* base, const Ident* item) +{ + if (base) + { + char buffer[200]; + strcpy_s(buffer, base->mString); + strcat_s(buffer, "."); + strcat_s(buffer, item->mString); + return Ident::Unique(buffer); + } + else + return item; +} + +static void AddGlobalVariableRanges(LinkerObject* lo, Declaration* dec, int offset, const Ident* ident) +{ + switch (dec->mType) + { + case DT_TYPE_STRUCT: + { + Declaration* deci = dec->mParams; + while (deci) + { + AddGlobalVariableRanges(lo, deci->mBase, offset + deci->mOffset, StructIdent(ident, deci->mIdent)); + deci = deci->mNext; + } + } break; + case DT_TYPE_INTEGER: + case DT_TYPE_FLOAT: + case DT_TYPE_POINTER: + case DT_TYPE_BOOL: + case DT_TYPE_ENUM: + { + LinkerObjectRange range; + range.mOffset = offset; + range.mSize = dec->mStripe; + switch (dec->mSize) + { + case 1: + range.mIdent = ident; + lo->mRanges.Push(range); + break; + case 2: + range.mIdent = StructIdent(ident, Ident::Unique("lo")); + lo->mRanges.Push(range); + range.mOffset += dec->mStripe; + range.mIdent = StructIdent(ident, Ident::Unique("hi")); + lo->mRanges.Push(range); + break; + case 4: + range.mIdent = StructIdent(ident, Ident::Unique("b0")); + lo->mRanges.Push(range); + range.mOffset += dec->mStripe; + range.mIdent = StructIdent(ident, Ident::Unique("b1")); + lo->mRanges.Push(range); + range.mOffset += dec->mStripe; + range.mIdent = StructIdent(ident, Ident::Unique("b2")); + lo->mRanges.Push(range); + range.mOffset += dec->mStripe; + range.mIdent = StructIdent(ident, Ident::Unique("b3")); + lo->mRanges.Push(range); + break; + } + } break; + + default: + if (ident) + { + LinkerObjectRange range; + range.mIdent = ident; + range.mOffset = offset; + range.mSize = dec->mSize * dec->mStripe; + lo->mRanges.Push(range); + } + } +} + +void InterCodeGenerator::InitGlobalVariable(InterCodeModule * mod, Declaration* dec) +{ + if (!dec->mLinkerObject) + { + InterVariable * var = new InterVariable(); + var->mOffset = 0; + var->mSize = dec->mSize; + var->mLinkerObject = mLinker->AddObject(dec->mLocation, dec->mQualIdent, dec->mSection, LOT_DATA, dec->mAlignment); + var->mLinkerObject->mVariable = var; + var->mIdent = dec->mQualIdent; + + Declaration* decb = dec->mBase; + while (decb && decb->mType == DT_TYPE_ARRAY) + decb = decb->mBase; + + if (decb && decb->mStripe != 1) + { + AddGlobalVariableRanges(var->mLinkerObject, decb, 0, nullptr); + var->mLinkerObject->mStripe = decb->mStripe; + } + + Declaration* type = dec->mBase; + while (type->mType == DT_TYPE_ARRAY) + type = type->mBase; + if (type->mFlags & DTF_CONST) + var->mLinkerObject->mFlags |= LOBJF_CONST; + if (dec->mFlags & DTF_ZEROPAGE) + var->mLinkerObject->mFlags |= LOBJF_ZEROPAGE; + if (dec->mFlags & DTF_NO_PAGE_CROSS) + var->mLinkerObject->mFlags |= LOBJF_NEVER_CROSS | LOBJF_NO_CROSS; + if (mCompilerOptions & COPT_OPTIMIZE_PAGE_CROSSING) + { + if (dec->mSize <= 256 && dec->mSize > 1) + { + if (dec->mBase->ContainsArray()) + var->mLinkerObject->mFlags |= LOBJF_NEVER_CROSS | LOBJF_NO_CROSS; + } + } + + var->mIndex = mod->mGlobalVars.Size(); + var->mDeclaration = dec; + mod->mGlobalVars.Push(var); + + if ((dec->mFlags & DTF_VAR_ALIASING) || (dec->mBase->mType == DT_TYPE_ARRAY && !(type->mFlags & DTF_CONST))) + var->mAliased = true; + + dec->mVarIndex = var->mIndex; + dec->mLinkerObject = var->mLinkerObject; + + if (dec->mFlags & DTF_SECTION_START) + dec->mLinkerObject->mType = LOT_SECTION_START; + else if (dec->mFlags & DTF_SECTION_END) + dec->mLinkerObject->mType = LOT_SECTION_END; + else if (dec->mFlags & DTF_BANK_INLAY) + { + dec->mLinkerObject->mType = LOT_INLAY; + dec->mInlayRegion->mInlayObject = dec->mLinkerObject; + dec->mLinkerObject->mFlags &= ~LOBJF_CONST; + } + + uint8* d = var->mLinkerObject->AddSpace(var->mSize); + if (dec->mValue) + { + if (dec->mValue->mType == EX_CONSTANT) + { + if (dec->mBase->CanAssign(dec->mValue->mDecType)) + BuildInitializer(mod, d, 0, dec->mValue->mDecValue, var); + else + mErrors->Error(dec->mLocation, EERR_INCOMPATIBLE_TYPES, "Incompatible constant initializer"); + } + else if (dec->mValue->mType == EX_CONSTRUCT) + { + DestructStack* destack = nullptr; + GotoNode* gotos = nullptr; + dec->mValue->mRight->mDecValue = dec; + dec->mLinkerObject->mFlags &= ~LOBJF_CONST; + TranslateExpression(nullptr, mMainInitProc, mMainInitBlock, dec->mValue, destack, gotos, BranchTarget(), BranchTarget(), nullptr); + } + else if (dec->mValue->mType == EX_VARIABLE && dec->mValue->mDecType->mType == DT_TYPE_ARRAY && dec->mBase->mType == DT_TYPE_POINTER && dec->mBase->CanAssign(dec->mValue->mDecType)) + { + Declaration* ndec = new Declaration(dec->mValue->mLocation, DT_CONST_POINTER); + ndec->mValue = dec->mValue; + ndec->mBase = dec->mValue->mDecType; + + BuildInitializer(mod, d, 0, ndec, var); + } + else + mErrors->Error(dec->mLocation, EERR_CONSTANT_INITIALIZER, "Non constant initializer"); + } + } +} + +void InterCodeGenerator::TranslateAssembler(InterCodeModule* mod, Declaration * adec,GrowingArray * refvars) +{ + Expression* exp = adec->mValue; + if (!adec->mValue) + { + mErrors->Error(adec->mLocation, EERR_UNDEFINED_OBJECT, "Call of undefined assembler function", adec->mIdent); + if (!adec->mLinkerObject) + adec->mLinkerObject = mLinker->AddObject(adec->mLocation, adec->mQualIdent, adec->mSection, LOT_NATIVE_CODE, adec->mAlignment); + + return; + } + + GrowingArray offsetMap(-1); + + int offset = 0, osize = 0, rsize = 0; + Expression* cexp = exp; + while (cexp) + { + offsetMap[rsize] = osize; + rsize += AsmInsSize(cexp->mAsmInsType, cexp->mAsmInsMode); +#if 1 + Declaration* aexp = nullptr; + if (cexp->mLeft) + aexp = cexp->mLeft->mDecValue; + + if (aexp) + { + if (cexp->mAsmInsMode == ASMIM_ABSOLUTE && HasAsmInstructionMode(cexp->mAsmInsType, ASMIM_ZERO_PAGE) && aexp->mType == DT_VARIABLE && (aexp->mFlags & DTF_GLOBAL) && (aexp->mFlags & DTF_ZEROPAGE)) + { + cexp->mAsmInsMode = ASMIM_ZERO_PAGE; + } + else if (cexp->mAsmInsMode == ASMIM_ABSOLUTE && HasAsmInstructionMode(cexp->mAsmInsType, ASMIM_ZERO_PAGE) && aexp->mType == DT_VARIABLE_REF && (aexp->mBase->mFlags & DTF_GLOBAL) && (aexp->mBase->mFlags & DTF_ZEROPAGE)) + { + cexp->mAsmInsMode = ASMIM_ZERO_PAGE; + } + } +#endif + + osize += AsmInsSize(cexp->mAsmInsType, cexp->mAsmInsMode); + cexp = cexp->mRight; + } + + // Check if remapping of labels due to operand address size change + if (osize != rsize) + { + adec->mBase->mScope->Iterate([=](const Ident* ident, Declaration* dec) { + if (dec->mType == DT_LABEL) + dec->mInteger = offsetMap[int(dec->mInteger)]; + }); + } + + Declaration* dec = exp->mDecValue; + + dec->mLinkerObject = mLinker->AddObject(dec->mLocation, dec->mQualIdent, dec->mSection, LOT_NATIVE_CODE, dec->mAlignment); + + if (dec->mFlags & DTF_ASM_PRESERVE_A) + dec->mLinkerObject->mFlags |= LOBJF_PRESERVE_REG_A; + if (dec->mFlags & DTF_ASM_PRESERVE_X) + dec->mLinkerObject->mFlags |= LOBJF_PRESERVE_REG_X; + if (dec->mFlags & DTF_ASM_PRESERVE_Y) + dec->mLinkerObject->mFlags |= LOBJF_PRESERVE_REG_Y; + + uint8* d = dec->mLinkerObject->AddSpace(osize); + + GrowingArray refVars(nullptr); + + cexp = exp; + while (cexp) + { + Declaration* aexp = nullptr; + if (cexp->mLeft) + aexp = cexp->mLeft->mDecValue; + + if (cexp->mAsmInsType != ASMIT_BYTE) + { + int opcode = AsmInsOpcodes[cexp->mAsmInsType][cexp->mAsmInsMode]; + d[offset++] = opcode; + } + + switch (cexp->mAsmInsMode) + { + case ASMIM_IMPLIED: + break; + case ASMIM_IMMEDIATE: + if (!aexp) + mErrors->Error(cexp->mLocation, EERR_ASM_INVALD_OPERAND, "Missing assembler operand"); + else if (aexp->mType == DT_CONST_INTEGER) + d[offset] = cexp->mLeft->mDecValue->mInteger & 255; + else if (aexp->mType == DT_LABEL_REF) + { + if (aexp->mBase->mBase) + { + if (!aexp->mBase->mBase->mLinkerObject) + TranslateAssembler(mod, aexp->mBase->mBase, nullptr); + + LinkerReference ref; + ref.mObject = dec->mLinkerObject; + ref.mOffset = offset; + if (aexp->mFlags & DTF_UPPER_BYTE) + ref.mFlags = LREF_HIGHBYTE; + else + ref.mFlags = LREF_LOWBYTE; + ref.mRefObject = aexp->mBase->mBase->mLinkerObject; + ref.mRefOffset = aexp->mOffset + int(aexp->mBase->mInteger); + ref.mRefObject->mFlags |= LOBJF_RELEVANT; + dec->mLinkerObject->AddReference(ref); + } + else + mErrors->Error(aexp->mLocation, EERR_ASM_INVALD_OPERAND, "Undefined immediate operand", aexp->mBase->mQualIdent); + } + else if (aexp->mType == DT_VARIABLE_REF) + { + if (aexp->mBase->mFlags & DTF_GLOBAL) + { + InitGlobalVariable(mod, aexp->mBase); + + LinkerReference ref; + ref.mObject = dec->mLinkerObject; + ref.mOffset = offset; + if (aexp->mFlags & DTF_UPPER_BYTE) + ref.mFlags = LREF_HIGHBYTE; + else + ref.mFlags = LREF_LOWBYTE; + ref.mRefObject = aexp->mBase->mLinkerObject; + ref.mRefOffset = aexp->mOffset; + ref.mRefObject->mFlags |= LOBJF_RELEVANT; + dec->mLinkerObject->AddReference(ref); + } + else + mErrors->Error(aexp->mLocation, EERR_ASM_INVALD_OPERAND, "Invalid immediate operand"); + } + else if (aexp->mType == DT_ARGUMENT || aexp->mType == DT_VARIABLE) + { + if (aexp->mFlags & DTF_GLOBAL) + { + InitGlobalVariable(mod, aexp); + + LinkerReference ref; + ref.mObject = dec->mLinkerObject; + ref.mOffset = offset; + ref.mFlags = LREF_LOWBYTE; + ref.mRefObject = aexp->mLinkerObject; + ref.mRefOffset = 0; + ref.mRefObject->mFlags |= LOBJF_RELEVANT; + dec->mLinkerObject->AddReference(ref); + } + else if (refvars) + { + int j = 0; + while (j < refvars->Size() && (*refvars)[j] != aexp) + j++; + if (j == refvars->Size()) + refvars->Push(aexp); + + LinkerReference ref; + ref.mObject = dec->mLinkerObject; + ref.mOffset = offset; + ref.mFlags = LREF_TEMPORARY; + ref.mRefObject = dec->mLinkerObject; + ref.mRefOffset = j; + ref.mRefObject->mFlags |= LOBJF_RELEVANT; + dec->mLinkerObject->AddReference(ref); + + d[offset] = 0; + } + else + mErrors->Error(aexp->mLocation, EERR_ASM_INVALD_OPERAND, "Local variable outside scope"); + } + else if (aexp->mType == DT_FUNCTION_REF) + { + if (!aexp->mBase->mLinkerObject) + { + InterCodeProcedure* cproc = this->TranslateProcedure(mod, aexp->mBase->mValue, aexp->mBase); + } + + LinkerReference ref; + ref.mObject = dec->mLinkerObject; + ref.mOffset = offset; + if (aexp->mFlags & DTF_UPPER_BYTE) + ref.mFlags = LREF_HIGHBYTE; + else + ref.mFlags = LREF_LOWBYTE; + ref.mRefObject = aexp->mBase->mLinkerObject; + ref.mRefOffset = aexp->mOffset; + ref.mRefObject->mFlags |= LOBJF_RELEVANT; + dec->mLinkerObject->AddReference(ref); + } + else + mErrors->Error(aexp->mLocation, EERR_ASM_INVALD_OPERAND, "Invalid immediate operand"); + + offset += 1; + break; + case ASMIM_ZERO_PAGE: + case ASMIM_ZERO_PAGE_X: + case ASMIM_INDIRECT_X: + case ASMIM_INDIRECT_Y: + if (!aexp) + mErrors->Error(cexp->mLocation, EERR_ASM_INVALD_OPERAND, "Missing assembler operand"); + else if (aexp->mType == DT_VARIABLE_REF) + { + if (aexp->mBase->mFlags & DTF_GLOBAL) + { + InitGlobalVariable(mod, aexp->mBase); + + LinkerReference ref; + ref.mObject = dec->mLinkerObject; + ref.mOffset = offset; + ref.mFlags = LREF_LOWBYTE; + ref.mRefObject = aexp->mBase->mLinkerObject; + ref.mRefOffset = aexp->mOffset; + ref.mRefObject->mFlags |= LOBJF_RELEVANT; + dec->mLinkerObject->AddReference(ref); + } + else if (refvars) + { + int j = 0; + while (j < refvars->Size() && (*refvars)[j] != aexp->mBase) + j++; + if (j == refvars->Size()) + refvars->Push(aexp->mBase); + + LinkerReference ref; + ref.mObject = dec->mLinkerObject; + ref.mOffset = offset; + ref.mFlags = LREF_TEMPORARY; + ref.mRefObject = dec->mLinkerObject; + ref.mRefOffset = j; + ref.mRefObject->mFlags |= LOBJF_RELEVANT; + dec->mLinkerObject->AddReference(ref); + + d[offset] = aexp->mOffset; + } + else + mErrors->Error(aexp->mLocation, EERR_ASM_INVALD_OPERAND, "Local variable outside scope"); + } + else if (aexp->mType == DT_ARGUMENT || aexp->mType == DT_VARIABLE) + { + if (aexp->mFlags & DTF_GLOBAL) + { + InitGlobalVariable(mod, aexp); + + LinkerReference ref; + ref.mObject = dec->mLinkerObject; + ref.mOffset = offset; + ref.mFlags = LREF_LOWBYTE; + ref.mRefObject = aexp->mLinkerObject; + ref.mRefOffset = 0; + ref.mRefObject->mFlags |= LOBJF_RELEVANT; + dec->mLinkerObject->AddReference(ref); + } + else if (refvars) + { + int j = 0; + while (j < refvars->Size() && (*refvars)[j] != aexp) + j++; + if (j == refvars->Size()) + refvars->Push(aexp); + + LinkerReference ref; + ref.mObject = dec->mLinkerObject; + ref.mOffset = offset; + ref.mFlags = LREF_TEMPORARY; + ref.mRefObject = dec->mLinkerObject; + ref.mRefOffset = j; + ref.mRefObject->mFlags |= LOBJF_RELEVANT; + dec->mLinkerObject->AddReference(ref); + + d[offset] = 0; + } + else + mErrors->Error(aexp->mLocation, EERR_ASM_INVALD_OPERAND, "Local variable outside scope"); + } + else + d[offset] = uint8(aexp->mInteger); + offset += 1; + break; + case ASMIM_ABSOLUTE: + case ASMIM_INDIRECT: + case ASMIM_ABSOLUTE_X: + case ASMIM_ABSOLUTE_Y: + if (!aexp) + mErrors->Error(cexp->mLocation, EERR_ASM_INVALD_OPERAND, "Missing assembler operand"); + else if (aexp->mType == DT_CONST_INTEGER) + { + d[offset + 0] = uint8(cexp->mLeft->mDecValue->mInteger & 255); + d[offset + 1] = uint8(cexp->mLeft->mDecValue->mInteger >> 8); + } + else if (aexp->mType == DT_LABEL) + { + if (aexp->mBase) + { + if (!aexp->mBase->mLinkerObject) + TranslateAssembler(mod, aexp->mBase, nullptr); + + LinkerReference ref; + ref.mObject = dec->mLinkerObject; + ref.mOffset = offset; + ref.mFlags = LREF_LOWBYTE | LREF_HIGHBYTE; + ref.mRefObject = aexp->mBase->mLinkerObject; + ref.mRefOffset = int(aexp->mInteger); + ref.mRefObject->mFlags |= LOBJF_RELEVANT; + dec->mLinkerObject->AddReference(ref); + } + else + mErrors->Error(aexp->mLocation, EERR_ASM_INVALD_OPERAND, "Undefined label"); + } + else if (aexp->mType == DT_LABEL_REF) + { + if (aexp->mBase->mBase) + { + if (!aexp->mBase->mBase->mLinkerObject) + TranslateAssembler(mod, aexp->mBase->mBase, nullptr); + + LinkerReference ref; + ref.mObject = dec->mLinkerObject; + ref.mOffset = offset; + ref.mFlags = LREF_LOWBYTE | LREF_HIGHBYTE; + ref.mRefObject = aexp->mBase->mBase->mLinkerObject; + ref.mRefOffset = aexp->mOffset + int(aexp->mBase->mInteger); + ref.mRefObject->mFlags |= LOBJF_RELEVANT; + dec->mLinkerObject->AddReference(ref); + } + else + mErrors->Error(aexp->mLocation, EERR_ASM_INVALD_OPERAND, "Undefined label"); + } + else if (aexp->mType == DT_CONST_ASSEMBLER) + { + if (!aexp->mLinkerObject) + TranslateAssembler(mod, aexp, nullptr); + + LinkerReference ref; + ref.mObject = dec->mLinkerObject; + ref.mOffset = offset; + ref.mFlags = LREF_LOWBYTE | LREF_HIGHBYTE; + ref.mRefObject = aexp->mLinkerObject; + ref.mRefOffset = 0; + ref.mRefObject->mFlags |= LOBJF_RELEVANT; + dec->mLinkerObject->AddReference(ref); + } + else if (aexp->mType == DT_VARIABLE) + { + if (aexp->mFlags & DTF_GLOBAL) + { + InitGlobalVariable(mod, aexp); + + LinkerReference ref; + ref.mObject = dec->mLinkerObject; + ref.mOffset = offset; + ref.mFlags = LREF_LOWBYTE | LREF_HIGHBYTE; + ref.mRefObject = aexp->mLinkerObject; + ref.mRefOffset = 0; + ref.mRefObject->mFlags |= LOBJF_RELEVANT; + dec->mLinkerObject->AddReference(ref); + } + } + else if (aexp->mType == DT_VARIABLE_REF) + { + if (aexp->mBase->mFlags & DTF_GLOBAL) + { + InitGlobalVariable(mod, aexp->mBase); + + LinkerReference ref; + ref.mObject = dec->mLinkerObject; + ref.mOffset = offset; + ref.mFlags = LREF_LOWBYTE | LREF_HIGHBYTE; + ref.mRefObject = aexp->mBase->mLinkerObject; + ref.mRefOffset = aexp->mOffset; + ref.mRefObject->mFlags |= LOBJF_RELEVANT; + dec->mLinkerObject->AddReference(ref); + } + } + else if (aexp->mType == DT_CONST_FUNCTION) + { + if (!aexp->mLinkerObject) + { + InterCodeProcedure* cproc = this->TranslateProcedure(mod, aexp->mValue, aexp); + } + + LinkerReference ref; + ref.mObject = dec->mLinkerObject; + ref.mOffset = offset; + ref.mFlags = LREF_LOWBYTE | LREF_HIGHBYTE; + ref.mRefObject = aexp->mLinkerObject; + ref.mRefOffset = 0; + ref.mRefObject->mFlags |= LOBJF_RELEVANT; + dec->mLinkerObject->AddReference(ref); + } + else if (aexp->mType == DT_FUNCTION_REF) + { + if (!aexp->mBase->mLinkerObject) + { + InterCodeProcedure* cproc = this->TranslateProcedure(mod, aexp->mBase->mValue, aexp->mBase); + } + + LinkerReference ref; + ref.mObject = dec->mLinkerObject; + ref.mOffset = offset; + ref.mFlags = LREF_LOWBYTE | LREF_HIGHBYTE; + ref.mRefObject = aexp->mBase->mLinkerObject; + ref.mRefOffset = aexp->mOffset; + ref.mRefObject->mFlags |= LOBJF_RELEVANT; + dec->mLinkerObject->AddReference(ref); + } + + offset += 2; + break; + case ASMIM_RELATIVE: + if (!aexp) + mErrors->Error(cexp->mLocation, EERR_ASM_INVALD_OPERAND, "Missing assembler operand"); + else + { + int64 disp = 0; + if (aexp->mType == DT_LABEL_REF) + { + if (aexp->mBase->mBase) + disp = aexp->mOffset + aexp->mBase->mInteger - offset - 1; + else + mErrors->Error(aexp->mLocation, EERR_ASM_INVALD_OPERAND, "Undefined label", aexp->mBase->mQualIdent); + } + else if (aexp->mType == DT_LABEL) + { + if (aexp->mBase) + disp = aexp->mInteger - offset - 1; + else + mErrors->Error(aexp->mLocation, EERR_ASM_INVALD_OPERAND, "Undefined label", aexp->mQualIdent); + } + + if (disp < -128 || disp > 127) + mErrors->Error(aexp->mLocation, EERR_ASM_INVALD_OPERAND, "Branch target out of range"); + + d[offset] = uint8(disp); + } + offset++; + break; + } + + cexp = cexp->mRight; + } + + assert(offset == osize); +} + +void InterCodeGenerator::BuildSwitchTree(InterCodeProcedure* proc, Expression* exp, InterCodeBasicBlock* block, InlineMapper * inlineMapper, ExValue v, const SwitchNodeArray& nodes, int left, int right, int vleft, int vright, bool csigned, InterCodeBasicBlock* dblock) +{ + if (right - left < 3) + { + for (int i = left; i < right; i++) + { + if (vleft == vright) + { + InterInstruction* jins = new InterInstruction(MapLocation(exp, inlineMapper), IC_JUMP); + block->Append(jins); + block->Close(nodes[i].mBlock, nullptr); + return; + } + else + { + InterCodeBasicBlock* cblock = new InterCodeBasicBlock(proc); + + InterInstruction* vins = new InterInstruction(MapLocation(exp, inlineMapper), IC_CONSTANT); + vins->mConst.mType = IT_INT16; + vins->mConst.mIntConst = nodes[i].mLower; + vins->mDst.mType = IT_INT16; + vins->mDst.mTemp = proc->AddTemporary(vins->mDst.mType); + block->Append(vins); + + if (nodes[i].mLower == nodes[i].mUpper) + { + InterInstruction* cins = new InterInstruction(MapLocation(exp, inlineMapper), IC_RELATIONAL_OPERATOR); + cins->mOperator = IA_CMPEQ; + cins->mSrc[0].mType = vins->mDst.mType; + cins->mSrc[0].mTemp = vins->mDst.mTemp; + cins->mSrc[1].mType = vins->mDst.mType; + cins->mSrc[1].mTemp = v.mTemp; + cins->mDst.mType = IT_BOOL; + cins->mDst.mTemp = proc->AddTemporary(cins->mDst.mType); + + block->Append(cins); + + InterInstruction* bins = new InterInstruction(MapLocation(exp, inlineMapper), IC_BRANCH); + bins->mSrc[0].mType = IT_BOOL; + bins->mSrc[0].mTemp = cins->mDst.mTemp; + block->Append(bins); + } + else + { + InterCodeBasicBlock* tblock = new InterCodeBasicBlock(proc); + + InterInstruction* cins = new InterInstruction(MapLocation(exp, inlineMapper), IC_RELATIONAL_OPERATOR); + cins->mOperator = csigned ? IA_CMPLS : IA_CMPLU; + cins->mSrc[0].mType = vins->mDst.mType; + cins->mSrc[0].mTemp = vins->mDst.mTemp; + cins->mSrc[1].mType = vins->mDst.mType; + cins->mSrc[1].mTemp = v.mTemp; + cins->mDst.mType = IT_BOOL; + cins->mDst.mTemp = proc->AddTemporary(cins->mDst.mType); + + block->Append(cins); + + InterInstruction* bins = new InterInstruction(MapLocation(exp, inlineMapper), IC_BRANCH); + bins->mSrc[0].mType = IT_BOOL; + bins->mSrc[0].mTemp = cins->mDst.mTemp; + block->Append(bins); + block->Close(cblock, tblock); + + block = tblock; + + InterInstruction* vins = new InterInstruction(MapLocation(exp, inlineMapper), IC_CONSTANT); + vins->mConst.mType = IT_INT16; + vins->mConst.mIntConst = nodes[i].mUpper; + vins->mDst.mType = IT_INT16; + vins->mDst.mTemp = proc->AddTemporary(vins->mDst.mType); + block->Append(vins); + + cins = new InterInstruction(MapLocation(exp, inlineMapper), IC_RELATIONAL_OPERATOR); + cins->mOperator = csigned ? IA_CMPLES : IA_CMPLEU; + cins->mSrc[0].mType = vins->mDst.mType; + cins->mSrc[0].mTemp = vins->mDst.mTemp; + cins->mSrc[1].mType = vins->mDst.mType; + cins->mSrc[1].mTemp = v.mTemp; + cins->mDst.mType = IT_BOOL; + cins->mDst.mTemp = proc->AddTemporary(cins->mDst.mType); + block->Append(cins); + + bins = new InterInstruction(MapLocation(exp, inlineMapper), IC_BRANCH); + bins->mSrc[0].mType = IT_BOOL; + bins->mSrc[0].mTemp = cins->mDst.mTemp; + block->Append(bins); + } + + block->Close(nodes[i].mBlock, cblock); + + block = cblock; + + if (vleft == nodes[i].mLower) + vleft = nodes[i].mLower + 1; + } + } + + InterInstruction* jins = new InterInstruction(MapLocation(exp, inlineMapper), IC_JUMP); + + block->Append(jins); + block->Close(dblock, nullptr); + } + else + { + int center = (left + right) >> 1; + + InterCodeBasicBlock* cblock = new InterCodeBasicBlock(proc); + InterCodeBasicBlock* rblock = new InterCodeBasicBlock(proc); + InterCodeBasicBlock* lblock = new InterCodeBasicBlock(proc); + + if (nodes[center].mLower == nodes[center].mUpper) + { + int vcenter = nodes[center].mLower; + + InterInstruction* vins = new InterInstruction(MapLocation(exp, inlineMapper), IC_CONSTANT); + vins->mConst.mType = IT_INT16; + vins->mConst.mIntConst = vcenter; + vins->mDst.mType = IT_INT16; + vins->mDst.mTemp = proc->AddTemporary(vins->mDst.mType); + block->Append(vins); + + InterInstruction* cins = new InterInstruction(MapLocation(exp, inlineMapper), IC_RELATIONAL_OPERATOR); + cins->mOperator = IA_CMPEQ; + cins->mSrc[0].mType = vins->mDst.mType; + cins->mSrc[0].mTemp = vins->mDst.mTemp; + cins->mSrc[1].mType = vins->mDst.mType; + cins->mSrc[1].mTemp = v.mTemp; + cins->mDst.mType = IT_BOOL; + cins->mDst.mTemp = proc->AddTemporary(cins->mDst.mType); + + block->Append(cins); + + InterInstruction* bins = new InterInstruction(MapLocation(exp, inlineMapper), IC_BRANCH); + bins->mSrc[0].mType = IT_BOOL; + bins->mSrc[0].mTemp = cins->mDst.mTemp; + block->Append(bins); + + block->Close(nodes[center].mBlock, cblock); + + InterInstruction* rins = new InterInstruction(MapLocation(exp, inlineMapper), IC_RELATIONAL_OPERATOR); + rins->mOperator = csigned ? IA_CMPLS : IA_CMPLU; + rins->mSrc[0].mType = vins->mDst.mType; + rins->mSrc[0].mTemp = vins->mDst.mTemp; + rins->mSrc[1].mType = vins->mDst.mType; + rins->mSrc[1].mTemp = v.mTemp; + rins->mDst.mType = IT_BOOL; + rins->mDst.mTemp = proc->AddTemporary(rins->mDst.mType); + + cblock->Append(rins); + + InterInstruction* rbins = new InterInstruction(MapLocation(exp, inlineMapper), IC_BRANCH); + rbins->mSrc[0].mType = IT_BOOL; + rbins->mSrc[0].mTemp = rins->mDst.mTemp; + cblock->Append(rbins); + + cblock->Close(lblock, rblock); + + BuildSwitchTree(proc, exp, lblock, inlineMapper, v, nodes, left, center, vleft, vcenter - 1, csigned, dblock); + BuildSwitchTree(proc, exp, rblock, inlineMapper, v, nodes, center + 1, right, vcenter + 1, vright, csigned, dblock); + } + else + { + int vlower = nodes[center].mLower, vupper = nodes[center].mUpper; + + InterInstruction* vins = new InterInstruction(MapLocation(exp, inlineMapper), IC_CONSTANT); + vins->mConst.mType = IT_INT16; + vins->mConst.mIntConst = vlower; + vins->mDst.mType = IT_INT16; + vins->mDst.mTemp = proc->AddTemporary(vins->mDst.mType); + block->Append(vins); + + InterInstruction* cins = new InterInstruction(MapLocation(exp, inlineMapper), IC_RELATIONAL_OPERATOR); + cins->mOperator = csigned ? IA_CMPLS : IA_CMPLU; + cins->mSrc[0].mType = vins->mDst.mType; + cins->mSrc[0].mTemp = vins->mDst.mTemp; + cins->mSrc[1].mType = vins->mDst.mType; + cins->mSrc[1].mTemp = v.mTemp; + cins->mDst.mType = IT_BOOL; + cins->mDst.mTemp = proc->AddTemporary(cins->mDst.mType); + + block->Append(cins); + + InterInstruction* bins = new InterInstruction(MapLocation(exp, inlineMapper), IC_BRANCH); + bins->mSrc[0].mType = IT_BOOL; + bins->mSrc[0].mTemp = cins->mDst.mTemp; + block->Append(bins); + block->Close(lblock, cblock); + + vins = new InterInstruction(MapLocation(exp, inlineMapper), IC_CONSTANT); + vins->mConst.mType = IT_INT16; + vins->mConst.mIntConst = vupper; + vins->mDst.mType = IT_INT16; + vins->mDst.mTemp = proc->AddTemporary(vins->mDst.mType); + cblock->Append(vins); + + cins = new InterInstruction(MapLocation(exp, inlineMapper), IC_RELATIONAL_OPERATOR); + cins->mOperator = csigned ? IA_CMPLES : IA_CMPLEU; + cins->mSrc[0].mType = vins->mDst.mType; + cins->mSrc[0].mTemp = vins->mDst.mTemp; + cins->mSrc[1].mType = vins->mDst.mType; + cins->mSrc[1].mTemp = v.mTemp; + cins->mDst.mType = IT_BOOL; + cins->mDst.mTemp = proc->AddTemporary(cins->mDst.mType); + cblock->Append(cins); + + bins = new InterInstruction(MapLocation(exp, inlineMapper), IC_BRANCH); + bins->mSrc[0].mType = IT_BOOL; + bins->mSrc[0].mTemp = cins->mDst.mTemp; + cblock->Append(bins); + + cblock->Close(nodes[center].mBlock, rblock); + + BuildSwitchTree(proc, exp, lblock, inlineMapper, v, nodes, left, center, vleft, vlower - 1, csigned, dblock); + BuildSwitchTree(proc, exp, rblock, inlineMapper, v, nodes, center + 1, right, vupper + 1, vright, csigned, dblock); + } + + } +} + +void InterCodeGenerator::UnwindDestructStack(Declaration* procType, InterCodeProcedure* proc, InterCodeBasicBlock*& block, DestructStack* stack, DestructStack* bottom, InlineMapper* inlineMapper) +{ + while (stack && stack != bottom) + { + if (stack->mDestruct) + { + DestructStack* destack = nullptr; + GotoNode* gotos = nullptr; + TranslateExpression(procType, proc, block, stack->mDestruct, destack, gotos, BranchTarget(), BranchTarget(), inlineMapper); + } + + stack = stack->mNext; + } + + if (stack != bottom) + mErrors->Error(proc->mLocation, EWARN_DESTRUCTOR_MISMATCH, "Destructor sequence mismatch"); +} + +void InterCodeGenerator::ConnectGotos(Declaration* procType, InterCodeProcedure* proc, GotoNode* gotos, InlineMapper* inlineMapper) +{ + GotoNode* gl = gotos; + while (gl) + { + GotoNode* g = gotos; + while (g && !(g != gl && g->mExpr->mType == EX_LABEL && g->mExpr->mDecValue->mIdent == gl->mExpr->mDecValue->mIdent)) + g = g->mNext; + + if (gl->mExpr->mType == EX_LABEL) + { + if (g) + mErrors->Error(gl->mExpr->mLocation, EERR_DUPLICATE_DEFINITION, "Label defined twice", gl->mExpr->mDecValue->mIdent); + } + else + { + if (g) + { + if (gl->mDestruct) + { + DestructStack* s = gl->mDestruct; + while (s && s != g->mDestruct) + s = s->mNext; + if (s == g->mDestruct) + UnwindDestructStack(procType, proc, gl->mBlock, gl->mDestruct, s, inlineMapper); + else + mErrors->Error(gl->mExpr->mLocation, ERRR_INVALID_GOTO, "Invalid got bypass constructor"); + } + + gl->mBlock->Close(g->mBlock, nullptr); + } + else + mErrors->Error(gl->mExpr->mLocation, EERR_UNDEFINED_OBJECT, "Undefined label", gl->mExpr->mDecValue->mIdent); + } + gl = gl->mNext; + } + +} + +Location InterCodeGenerator::MapLocation(Expression* exp, InlineMapper* inlineMapper) +{ + if (inlineMapper) + return Location(exp->mLocation, inlineMapper->mLocation); + else + return exp->mLocation; +} + +InterCodeGenerator::ExValue InterCodeGenerator::TranslateInline(Declaration* procType, InterCodeProcedure* proc, InterCodeBasicBlock*& block, Expression* exp, DestructStack*& destack, const BranchTarget& breakBlock, const BranchTarget& continueBlock, InlineMapper* inlineMapper, bool inlineConstexpr, ExValue* lrexp) +{ + GotoNode* gotos = nullptr; + + ExValue vl, vr; + + Declaration* fdec = exp->mLeft->mDecValue; + Expression* fexp = fdec->mValue; + Declaration* ftype = fdec->mBase; + + proc->mCheckUnreachable = false; + + InlineMapper nmapper; + nmapper.mReturn = new InterCodeBasicBlock(proc); + nmapper.mVarIndex = proc->mNumLocals; + nmapper.mConstExpr = inlineConstexpr; + nmapper.mLocation = new Location(MapLocation(exp, inlineMapper)); + proc->mNumLocals += fdec->mNumVars; + if (inlineMapper) + nmapper.mDepth = inlineMapper->mDepth + 1; + + Declaration* pdec = ftype->mParams; + Expression* pex = exp->mRight; + while (pex) + { + int nindex = proc->mNumLocals++; + Declaration* vdec = new Declaration(pex->mLocation, DT_VARIABLE); + + InterInstruction* ains = new InterInstruction(MapLocation(pex, inlineMapper), IC_CONSTANT); + ains->mDst.mType = IT_POINTER; + ains->mDst.mTemp = proc->AddTemporary(ains->mDst.mType); + ains->mConst.mType = IT_POINTER; + ains->mConst.mMemory = IM_LOCAL; + ains->mConst.mVarIndex = nindex; + + if (pdec) + { + if (!(pdec->mFlags & DTF_FPARAM_UNUSED)) + nmapper.mParams[pdec->mVarIndex] = nindex; + + vdec->mVarIndex = nindex; + vdec->mBase = pdec->mBase; + if (pdec->mBase->mType == DT_TYPE_ARRAY || pdec->mBase->mType == DT_TYPE_REFERENCE || pdec->mBase->mType == DT_TYPE_RVALUEREF) + ains->mConst.mOperandSize = 2; + else + ains->mConst.mOperandSize = pdec->mSize; + vdec->mSize = ains->mConst.mOperandSize; + vdec->mIdent = pdec->mIdent; + vdec->mQualIdent = pdec->mQualIdent; + } + else + { + mErrors->Error(pex->mLocation, EERR_WRONG_PARAMETER, "Too many arguments for function call"); + } + + block->Append(ains); + + Expression* texp; + + if (pex->mType == EX_LIST) + { + texp = pex->mLeft; + pex = pex->mRight; + } + else + { + texp = pex; + pex = nullptr; + } + + ExValue vp(pdec ? pdec->mBase : TheSignedIntTypeDeclaration, ains->mDst.mTemp, 1); + + if (pdec && (pdec->mBase->mType == DT_TYPE_STRUCT || pdec->mBase->mType == DT_TYPE_UNION)) + vr = TranslateExpression(procType, proc, block, texp, destack, gotos, breakBlock, continueBlock, inlineMapper, &vp); + else + vr = TranslateExpression(procType, proc, block, texp, destack, gotos, breakBlock, continueBlock, inlineMapper, nullptr); + + if (!(pdec && pdec->mBase->IsReference()) && (vr.mType->mType == DT_TYPE_STRUCT || vr.mType->mType == DT_TYPE_UNION)) + { + if (pdec && !pdec->mBase->CanAssign(vr.mType)) + mErrors->Error(texp->mLocation, EERR_INCOMPATIBLE_TYPES, "Cannot assign incompatible types", pdec->mBase->MangleIdent(), vr.mType->MangleIdent()); + + vr = Dereference(proc, texp, block, inlineMapper, vr, 1); + + if (vr.mReference != 1) + mErrors->Error(exp->mLeft->mLocation, EERR_NOT_AN_LVALUE, "Not an addressable expression"); + + if (vp.mTemp != vr.mTemp) + CopyStruct(proc, exp, block, vp, vr, inlineMapper, false); + } + else + { + if (vr.mType->mType == DT_TYPE_ARRAY || vr.mType->mType == DT_TYPE_FUNCTION) + vr = Dereference(proc, texp, block, inlineMapper, vr, 1); + else if (pdec && (pdec->mBase->IsReference() && !vr.mType->IsReference())) + vr = Dereference(proc, texp, block, inlineMapper, vr, 1); + else if (vr.mType->IsReference() && !(pdec && pdec->mBase->IsReference())) + { + vr.mReference++; + vr.mType = vr.mType->mBase; + vr = Dereference(proc, texp, block, inlineMapper, vr); + } + else + vr = Dereference(proc, texp, block, inlineMapper, vr); + + if (pdec && (pdec->mFlags & DTF_FPARAM_UNUSED) && vr.mType->mType == DT_TYPE_VOID) + ; + else if (pdec) + { + if (pdec->mBase->mType == DT_TYPE_POINTER && vr.mType->mType == DT_TYPE_INTEGER) + { + Expression* er = texp; + while (er && er->mType == EX_COMMA) + er = er->mRight; + if (er && er->mType == EX_CONSTANT && er->mDecValue->mType == DT_CONST_INTEGER && er->mDecValue->mInteger == 0) + { + mErrors->Error(exp->mLocation, EWARN_NUMERIC_0_USED_AS_NULLPTR, "Numeric 0 used for nullptr"); + vr = CoerceType(proc, texp, block, inlineMapper, vr, pdec->mBase); + } + } + + if (!pdec->mBase->CanAssign(vr.mType)) + { + pdec->mBase->CanAssign(vr.mType); + mErrors->Error(texp->mLocation, EERR_INCOMPATIBLE_TYPES, "Cannot assign incompatible types", pdec->mBase->MangleIdent(), vr.mType->MangleIdent()); + } + vr = CoerceType(proc, texp, block, inlineMapper, vr, pdec->mBase); + } + else if (vr.mType->IsIntegerType() && vr.mType->mSize < 2) + { + vr = CoerceType(proc, texp, block, inlineMapper, vr, TheSignedIntTypeDeclaration); + } + + InterInstruction* wins = new InterInstruction(MapLocation(texp, inlineMapper), IC_STORE); + wins->mSrc[1].mMemory = IM_INDIRECT; + wins->mSrc[0].mType = vr.mReference > 0 ? IT_POINTER : InterTypeOf(vr.mType); + wins->mSrc[0].mTemp = vr.mTemp; +// assert(wins->mSrc[0].mType != IT_NONE); + wins->mSrc[1].mType = IT_POINTER; + wins->mSrc[1].mTemp = ains->mDst.mTemp; + if (pdec) + { + if (pdec->mBase->mType == DT_TYPE_ARRAY || pdec->mBase->mType == DT_TYPE_REFERENCE || pdec->mBase->mType == DT_TYPE_RVALUEREF) + wins->mSrc[1].mOperandSize = 2; + else + wins->mSrc[1].mOperandSize = pdec->mSize; + } + else if (vr.mType->mSize > 2 && vr.mType->mType != DT_TYPE_ARRAY) + wins->mSrc[1].mOperandSize = vr.mType->mSize; + else + wins->mSrc[1].mOperandSize = 2; + + if (!pdec || !(pdec->mFlags & DTF_FPARAM_UNUSED)) + block->Append(wins); + } + + if (pdec) + pdec = pdec->mNext; + } + + if (pdec) + mErrors->Error(exp->mLocation, EERR_WRONG_PARAMETER, "Not enough arguments for function call"); + + Declaration* rdec = nullptr; + if (ftype->mBase->mType != DT_TYPE_VOID) + { + if (lrexp) + { + nmapper.mResultExp = lrexp; + } + else + { + int nindex = proc->mNumLocals++; + nmapper.mResult = nindex; + + rdec = new Declaration(ftype->mLocation, DT_VARIABLE); + rdec->mVarIndex = nindex; + rdec->mBase = ftype->mBase; + rdec->mSize = rdec->mBase->mSize; + } + } + + DestructStack* idestack = nullptr; + GotoNode* igotos = nullptr; + + vl = TranslateExpression(ftype, proc, block, fexp, idestack, igotos, BranchTarget(), BranchTarget(), &nmapper); + + InterInstruction* jins = new InterInstruction(MapLocation(exp, inlineMapper), IC_JUMP); + block->Append(jins); + + block->Close(nmapper.mReturn, nullptr); + block = nmapper.mReturn; + + ConnectGotos(ftype, proc, igotos, &nmapper); + + // Unwind inner destruct stack + UnwindDestructStack(ftype, proc, block, idestack, nullptr, &nmapper); + + // Uwind parameter passing stack + // UnwindDestructStack(ftype, proc, block, destack, nullptr, inlineMapper); + + if (rdec) + { + InterInstruction* ins = new InterInstruction(MapLocation(exp, inlineMapper), IC_CONSTANT); + ins->mDst.mType = IT_POINTER; + ins->mDst.mTemp = proc->AddTemporary(ins->mDst.mType); + ins->mConst.mType = IT_POINTER; + ins->mConst.mOperandSize = rdec->mSize; + ins->mConst.mIntConst = rdec->mOffset; + ins->mConst.mVarIndex = rdec->mVarIndex; + ins->mConst.mMemory = IM_LOCAL; + + block->Append(ins); + + ExValue rv(rdec->mBase, ins->mDst.mTemp, 1); + if (!rdec->mBase->IsReference() && rdec->mBase->mType != DT_TYPE_STRUCT) + rv = Dereference(proc, exp, block, inlineMapper, rv); + return rv; + } + else if (lrexp) + { + return *lrexp; + } + else + return ExValue(TheVoidTypeDeclaration); +} + +InterInstruction* InterCodeGenerator::CopyStructSimple(InterCodeProcedure* proc, Expression * exp, InterCodeBasicBlock* block, InlineMapper * inlineMapper, ExValue vl, ExValue vr) +{ + int ne = 0; + Declaration* mdec = nullptr; + if (vl.mType->mType == DT_TYPE_STRUCT) + { + Declaration* dec = vl.mType->mParams; + while (dec) + { + if (dec->mType == DT_ELEMENT && !(dec->mFlags & DTF_STATIC) && dec->mBase->IsSimpleType()) + { + mdec = dec->mBase; + ne++; + } + dec = dec->mNext; + } + } + + bool cvol = false; + if ((vl.mType->mFlags & DTF_VOLATILE) || (vr.mType->mFlags & DTF_VOLATILE)) + cvol = true; + else + { + Declaration* dec = vl.mType->mParams; + while (!cvol && dec) + { + if (dec->mType == DT_ELEMENT && (dec->mBase->mFlags & DTF_VOLATILE)) + cvol = true; + dec = dec->mNext; + } + dec = vr.mType->mParams; + while (!cvol && dec) + { + if (dec->mType == DT_ELEMENT && (dec->mBase->mFlags & DTF_VOLATILE)) + cvol = true; + dec = dec->mNext; + } + } + + // Single element structs are copied as individual value + if (ne == 1 && mdec->mSize == vl.mType->mSize) + { + InterInstruction* lins = new InterInstruction(MapLocation(exp, inlineMapper), IC_LOAD); + lins->mNumOperands = 1; + + lins->mSrc[0].mType = IT_POINTER; + lins->mSrc[0].mTemp = vr.mTemp; + lins->mSrc[0].mMemory = IM_INDIRECT; + lins->mSrc[0].mOperandSize = mdec->mSize; + lins->mSrc[0].mStride = mdec->mStripe; + + lins->mDst.mType = InterTypeOf(mdec); + lins->mDst.mTemp = proc->AddTemporary(lins->mDst.mType); + block->Append(lins); + + InterInstruction* sins = new InterInstruction(MapLocation(exp, inlineMapper), IC_STORE); + sins->mNumOperands = 2; + + sins->mSrc[1].mType = IT_POINTER; + sins->mSrc[1].mTemp = vl.mTemp; + sins->mSrc[1].mMemory = IM_INDIRECT; + sins->mSrc[1].mOperandSize = mdec->mSize; + sins->mSrc[1].mStride = mdec->mStripe; + + sins->mSrc[0] = lins->mDst; + + return sins; + } + else + { + InterInstruction* cins = new InterInstruction(MapLocation(exp, inlineMapper), IC_COPY); + cins->mNumOperands = 2; + + cins->mSrc[0].mType = IT_POINTER; + cins->mSrc[0].mTemp = vr.mTemp; + cins->mSrc[0].mMemory = IM_INDIRECT; + cins->mSrc[0].mOperandSize = vr.mType->mSize; + cins->mSrc[0].mStride = vr.mType->mStripe; + + cins->mSrc[1].mOperandSize = vl.mType->mSize; + cins->mSrc[1].mType = IT_POINTER; + cins->mSrc[1].mTemp = vl.mTemp; + cins->mSrc[1].mMemory = IM_INDIRECT; + cins->mSrc[1].mStride = vl.mType->mStripe; + + cins->mConst.mOperandSize = vl.mType->mSize; + cins->mVolatile = cvol; + + return cins; + } +} + +void InterCodeGenerator::CopyStruct(InterCodeProcedure* proc, Expression* exp, InterCodeBasicBlock*& block, ExValue vl, ExValue vr, InlineMapper* inlineMapper, bool moving) +{ + if (vr.mTemp == vl.mTemp) + return; + + if (vl.mType->mCopyConstructor || moving && vl.mType->mMoveConstructor) + { + Declaration* ccdec = vl.mType->mCopyConstructor; + if (moving && vl.mType->mMoveConstructor) + ccdec = vl.mType->mMoveConstructor; + + if (!ccdec->mLinkerObject) + this->TranslateProcedure(proc->mModule, ccdec->mValue, ccdec); + + bool canInline = ((mCompilerOptions & COPT_OPTIMIZE_INLINE) || (ccdec->mFlags & DTF_FORCE_INLINE)) && !(inlineMapper && inlineMapper->mDepth > 10); + bool doInline = false; + + if (canInline) + { + if (ccdec->mFlags & DTF_INLINE) + { + if ((ccdec->mFlags & DTF_REQUEST_INLINE) || (mCompilerOptions & COPT_OPTIMIZE_AUTO_INLINE)) + { + if (proc->mNativeProcedure || !(ccdec->mFlags & DTF_NATIVE)) + doInline = true; + } + } + } + + if ((ccdec->mFlags & DTF_FORCE_INLINE) && !doInline) + mErrors->Error(exp->mLocation, EWARN_FUNCTION_NOT_INLINED, "Function with __forceinline not inlined", ccdec->mQualIdent); + + if (doInline) + { + DestructStack* destack = nullptr; + GotoNode* gotos = nullptr; + + Expression* fexp = ccdec->mValue; + Declaration* ftype = ccdec->mBase; + + InlineMapper nmapper; + nmapper.mReturn = new InterCodeBasicBlock(proc); + nmapper.mVarIndex = proc->mNumLocals; + nmapper.mConstExpr = false; + nmapper.mLocation = new Location(MapLocation(exp, inlineMapper)); + proc->mNumLocals += ccdec->mNumVars; + if (inlineMapper) + nmapper.mDepth = inlineMapper->mDepth + 1; + + Declaration* pdec = ftype->mParams; + int nindex = proc->mNumLocals++; + Declaration* vdec = new Declaration(exp->mLocation, DT_VARIABLE); + + InterInstruction* ains = new InterInstruction(MapLocation(exp, inlineMapper), IC_CONSTANT); + ains->mDst.mType = IT_POINTER; + ains->mDst.mTemp = proc->AddTemporary(ains->mDst.mType); + ains->mConst.mType = IT_POINTER; + ains->mConst.mMemory = IM_LOCAL; + ains->mConst.mVarIndex = nindex; + + if (!(pdec->mFlags & DTF_FPARAM_UNUSED)) + nmapper.mParams[pdec->mVarIndex] = nindex; + + vdec->mVarIndex = nindex; + vdec->mBase = pdec->mBase; + ains->mConst.mOperandSize = 2; + vdec->mSize = ains->mConst.mOperandSize; + vdec->mIdent = pdec->mIdent; + vdec->mQualIdent = pdec->mQualIdent; + block->Append(ains); + + InterInstruction* wins = new InterInstruction(MapLocation(exp, inlineMapper), IC_STORE); + wins->mNumOperands = 2; + wins->mSrc[1].mMemory = IM_INDIRECT; + wins->mSrc[0].mType = vl.mReference > 0 ? IT_POINTER : InterTypeOf(vl.mType); + wins->mSrc[0].mTemp = vl.mTemp; + wins->mSrc[1].mType = IT_POINTER; + wins->mSrc[1].mTemp = ains->mDst.mTemp; + wins->mSrc[1].mOperandSize = 2; + if (!(pdec->mFlags & DTF_FPARAM_UNUSED)) + block->Append(wins); + + pdec = pdec->mNext; + + nindex = proc->mNumLocals++; + vdec = new Declaration(exp->mLocation, DT_VARIABLE); + + ains = new InterInstruction(MapLocation(exp, inlineMapper), IC_CONSTANT); + ains->mNumOperands = 0; + ains->mDst.mType = IT_POINTER; + ains->mDst.mTemp = proc->AddTemporary(ains->mDst.mType); + ains->mConst.mType = IT_POINTER; + ains->mConst.mMemory = IM_LOCAL; + ains->mConst.mVarIndex = nindex; + + if (!(pdec->mFlags & DTF_FPARAM_UNUSED)) + nmapper.mParams[pdec->mVarIndex] = nindex; + + vdec->mVarIndex = nindex; + vdec->mBase = pdec->mBase; + ains->mConst.mOperandSize = 2; + vdec->mSize = ains->mConst.mOperandSize; + vdec->mIdent = pdec->mIdent; + vdec->mQualIdent = pdec->mQualIdent; + block->Append(ains); + + wins = new InterInstruction(MapLocation(exp, inlineMapper), IC_STORE); + wins->mNumOperands = 2; + wins->mSrc[1].mMemory = IM_INDIRECT; + wins->mSrc[0].mType = vr.mReference > 0 ? IT_POINTER : InterTypeOf(vr.mType);; + wins->mSrc[0].mTemp = vr.mTemp; + wins->mSrc[1].mType = IT_POINTER; + wins->mSrc[1].mTemp = ains->mDst.mTemp; + wins->mSrc[1].mOperandSize = 2; + if (!(pdec->mFlags & DTF_FPARAM_UNUSED)) + block->Append(wins); + + if (!fexp) + mErrors->Error(exp->mLocation, EERR_CALL_OF_DELETED_FUNCTION, "Call of deleted copy constructor"); + else + TranslateExpression(ftype, proc, block, fexp, destack, gotos, BranchTarget(), BranchTarget(), &nmapper); + + InterInstruction* jins = new InterInstruction(MapLocation(exp, inlineMapper), IC_JUMP); + block->Append(jins); + + block->Close(nmapper.mReturn, nullptr); + block = nmapper.mReturn; + } + else if (ccdec->mBase->mFlags & DTF_FASTCALL) + { + InterInstruction* psins = new InterInstruction(MapLocation(exp, inlineMapper), IC_CONSTANT); + psins->mDst.mType = IT_POINTER; + psins->mDst.mTemp = proc->AddTemporary(IT_POINTER); + psins->mDst.mOperandSize = 2; + psins->mConst.mType = IT_POINTER; + psins->mConst.mVarIndex = 0; + psins->mConst.mIntConst = 0; + psins->mConst.mOperandSize = 2; + if (ccdec->mBase->mFlags & DTF_FASTCALL) + { + psins->mConst.mMemory = IM_FFRAME; + psins->mConst.mVarIndex += ccdec->mBase->mFastCallBase; + } + else + psins->mConst.mMemory = IM_FRAME; + block->Append(psins); + + InterInstruction* ssins = new InterInstruction(MapLocation(exp, inlineMapper), IC_STORE); + ssins->mSrc[0].mType = IT_POINTER; + ssins->mSrc[0].mTemp = vl.mTemp; + ssins->mSrc[0].mMemory = IM_INDIRECT; + ssins->mSrc[0].mOperandSize = 2; + ssins->mSrc[1] = psins->mDst; + block->Append(ssins); + + InterInstruction* plins = new InterInstruction(MapLocation(exp, inlineMapper), IC_CONSTANT); + plins->mDst.mType = IT_POINTER; + plins->mDst.mTemp = proc->AddTemporary(IT_POINTER); + plins->mDst.mOperandSize = 2; + plins->mConst.mType = IT_POINTER; + plins->mConst.mVarIndex = 2; + plins->mConst.mIntConst = 0; + plins->mConst.mOperandSize = 2; + if (ccdec->mBase->mFlags & DTF_FASTCALL) + { + plins->mConst.mMemory = IM_FFRAME; + plins->mConst.mVarIndex += ccdec->mBase->mFastCallBase; + } + else + plins->mConst.mMemory = IM_FRAME; + block->Append(plins); + + InterInstruction* slins = new InterInstruction(MapLocation(exp, inlineMapper), IC_STORE); + slins->mSrc[0].mType = IT_POINTER; + slins->mSrc[0].mTemp = vr.mTemp; + slins->mSrc[0].mMemory = IM_INDIRECT; + slins->mSrc[0].mOperandSize = 2; + slins->mSrc[1] = plins->mDst; + block->Append(slins); + + proc->AddCalledFunction(proc->mModule->mProcedures[ccdec->mVarIndex]); + + InterInstruction* pcins = new InterInstruction(MapLocation(exp, inlineMapper), IC_CONSTANT); + pcins->mDst.mType = IT_POINTER; + pcins->mDst.mTemp = proc->AddTemporary(IT_POINTER); + pcins->mConst.mType = IT_POINTER; + pcins->mConst.mVarIndex = ccdec->mVarIndex; + pcins->mConst.mIntConst = 0; + pcins->mConst.mOperandSize = 2; + pcins->mConst.mMemory = IM_PROCEDURE; + pcins->mConst.mLinkerObject = ccdec->mLinkerObject; + block->Append(pcins); + + InterInstruction* cins = new InterInstruction(MapLocation(exp, inlineMapper), IC_CALL); + if (ccdec->mFlags & DTF_NATIVE) + cins->mCode = IC_CALL_NATIVE; + else + cins->mCode = IC_CALL; + cins->mSrc[0] = pcins->mDst; + cins->mNumOperands = 1; + + block->Append(cins); + } + } + else + { + block->Append(CopyStructSimple(proc, exp, block, inlineMapper, vl, vr)); + } +} + +InterCodeGenerator::ExValue InterCodeGenerator::TranslateExpression(Declaration* procType, InterCodeProcedure* proc, InterCodeBasicBlock*& block, Expression* exp, DestructStack*& destack, GotoNode*& gotos, const BranchTarget& breakBlock, const BranchTarget& continueBlock, InlineMapper* inlineMapper, ExValue* lrexp) +{ + Declaration* dec; + ExValue vl, vr; + + for (;;) + { + switch (exp->mType) + { + case EX_VOID: + return ExValue(TheVoidTypeDeclaration); + + case EX_SEQUENCE: + case EX_LIST: + case EX_COMMA: + if (exp->mLeft) + vr = TranslateExpression(procType, proc, block, exp->mLeft, destack, gotos, breakBlock, continueBlock, inlineMapper); + exp = exp->mRight; + if (!exp) + return ExValue(TheVoidTypeDeclaration); + break; + + case EX_CONSTRUCT: + { + if (exp->mLeft->mLeft) + TranslateExpression(procType, proc, block, exp->mLeft->mLeft, destack, gotos, breakBlock, continueBlock, inlineMapper, lrexp); + + if (exp->mLeft->mRight) + { + DestructStack* de = new DestructStack(); + de->mNext = destack; + de->mDestruct = exp->mLeft->mRight; + destack = de; + } + + exp = exp->mRight; + if (!exp) + return ExValue(TheVoidTypeDeclaration); + } + break; + + case EX_PACK: + case EX_PACK_TYPE: + mErrors->Error(exp->mLocation, EERR_INVALID_PACK_USAGE, "Invalid pack usage"); + return ExValue(TheVoidTypeDeclaration); + + case EX_RESULT: + { + if (lrexp) + return *lrexp; + + InterInstruction* ains = new InterInstruction(MapLocation(exp, inlineMapper), IC_CONSTANT); + + if (inlineMapper) + { + if (inlineMapper->mResultExp) + { + ains->mDst.mTemp = inlineMapper->mResultExp->mTemp; + } + else + { + ains->mCode = IC_CONSTANT; + ains->mDst.mType = IT_POINTER; + ains->mDst.mTemp = proc->AddTemporary(IT_POINTER); + ains->mConst.mType = IT_POINTER; + ains->mConst.mOperandSize = procType->mBase->mSize; + ains->mConst.mIntConst = 0; + ains->mConst.mVarIndex = inlineMapper->mResult; + ains->mConst.mMemory = IM_LOCAL; + block->Append(ains); + } + } + else + { + InterInstruction* pins = new InterInstruction(MapLocation(exp, inlineMapper), IC_CONSTANT); + pins->mDst.mType = IT_POINTER; + pins->mDst.mTemp = proc->AddTemporary(IT_POINTER); + pins->mConst.mType = IT_POINTER; + pins->mConst.mVarIndex = 0; + pins->mConst.mIntConst = 0; + pins->mConst.mOperandSize = 2; + if (procType->mFlags & DTF_FASTCALL) + { + pins->mConst.mMemory = IM_FPARAM; + if (procType->mFastCallBase + 2 <= BC_REG_FPARAMS_END - BC_REG_FPARAMS) + pins->mConst.mVarIndex += procType->mFastCallBase; + else + pins->mConst.mVarIndex += BC_REG_FPARAMS_END - BC_REG_FPARAMS + procType->mFastCallBase2; + } + else + pins->mConst.mMemory = IM_PARAM; + block->Append(pins); + + ains->mCode = IC_LOAD; + ains->mSrc[0].mMemory = IM_INDIRECT; + ains->mSrc[0].mType = IT_POINTER; + ains->mSrc[0].mTemp = pins->mDst.mTemp; + ains->mDst.mType = IT_POINTER; + ains->mDst.mTemp = proc->AddTemporary(IT_POINTER); + ains->mNumOperands = 1; + block->Append(ains); + } + + return ExValue(exp->mDecType, ains->mDst.mTemp, 1); + } + case EX_CLEANUP: + { + vl = TranslateExpression(procType, proc, block, exp->mLeft, destack, gotos, breakBlock, continueBlock, inlineMapper, lrexp); + TranslateExpression(procType, proc, block, exp->mRight, destack, gotos, breakBlock, continueBlock, inlineMapper); + return vl; + } + + case EX_CONSTANT: + dec = exp->mDecValue; + switch (dec->mType) + { + case DT_CONST_INTEGER: + { + InterInstruction * ins = new InterInstruction(MapLocation(exp, inlineMapper), IC_CONSTANT); + ins->mDst.mType = InterTypeOf(dec->mBase); + ins->mDst.mTemp = proc->AddTemporary(ins->mDst.mType); + ins->mConst.mType = ins->mDst.mType; + + if (ins->mDst.mType == IT_INT8) + { + if (dec->mBase->mFlags & DTF_SIGNED) + { + if (dec->mInteger < -128 || dec->mInteger > 127) + mErrors->Error(exp->mLocation, EWARN_CONSTANT_TRUNCATED, "Integer constant truncated"); + ins->mConst.mIntConst = int8(dec->mInteger); + } + else + { + if (dec->mInteger < 0 || dec->mInteger > 255) + mErrors->Error(exp->mLocation, EWARN_CONSTANT_TRUNCATED, "Unsigned integer constant truncated"); + ins->mConst.mIntConst = uint8(dec->mInteger); + } + } + else if (ins->mDst.mType == IT_INT16) + { + if (dec->mBase->mFlags & DTF_SIGNED) + { + if (dec->mInteger < -32768 || dec->mInteger > 32767) + mErrors->Error(exp->mLocation, EWARN_CONSTANT_TRUNCATED, "Integer constant truncated"); + ins->mConst.mIntConst = int16(dec->mInteger); + } + else + { + if (dec->mInteger < 0 || dec->mInteger > 65535) + mErrors->Error(exp->mLocation, EWARN_CONSTANT_TRUNCATED, "Unsigned integer constant truncated"); + ins->mConst.mIntConst = uint16(dec->mInteger); + } + } + else if (ins->mDst.mType == IT_INT32) + { + if (dec->mBase->mFlags & DTF_SIGNED) + { + if (dec->mInteger < -2147483648LL || dec->mInteger > 2147483647LL) + mErrors->Error(exp->mLocation, EWARN_CONSTANT_TRUNCATED, "Integer constant truncated"); + ins->mConst.mIntConst = int32(dec->mInteger); + } + else + { + if (dec->mInteger < 0 || dec->mInteger > 4294967296LL) + mErrors->Error(exp->mLocation, EWARN_CONSTANT_TRUNCATED, "Unsigned integer constant truncated"); + ins->mConst.mIntConst = uint32(dec->mInteger); + } + } + else + { + ins->mConst.mIntConst = dec->mInteger; + } + block->Append(ins); + return ExValue(dec->mBase, ins->mDst.mTemp); + + } break; + + case DT_CONST_FLOAT: + { + InterInstruction * ins = new InterInstruction(MapLocation(exp, inlineMapper), IC_CONSTANT); + ins->mDst.mType = InterTypeOf(dec->mBase); + ins->mDst.mTemp = proc->AddTemporary(ins->mDst.mType); + ins->mConst.mType = ins->mDst.mType; + ins->mConst.mFloatConst = dec->mNumber; + block->Append(ins); + return ExValue(dec->mBase, ins->mDst.mTemp); + + } break; + + case DT_CONST_ADDRESS: + { + InterInstruction * ins = new InterInstruction(MapLocation(exp, inlineMapper), IC_CONSTANT); + ins->mDst.mType = IT_POINTER; + ins->mDst.mTemp = proc->AddTemporary(IT_POINTER); + ins->mConst.mType = IT_POINTER; + ins->mConst.mIntConst = dec->mInteger; + ins->mConst.mMemory = IM_ABSOLUTE; + block->Append(ins); + return ExValue(dec->mBase, ins->mDst.mTemp); + + } break; + + case DT_CONST_FUNCTION: + { + if (!dec->mLinkerObject) + { + InterCodeProcedure* cproc = this->TranslateProcedure(proc->mModule, dec->mValue, dec); + } + + InterInstruction * ins = new InterInstruction(MapLocation(exp, inlineMapper), IC_CONSTANT); + ins->mDst.mType = InterTypeOf(dec->mBase); + ins->mDst.mTemp = proc->AddTemporary(ins->mDst.mType); + ins->mConst.mType = ins->mDst.mType; + ins->mConst.mVarIndex = dec->mVarIndex; + ins->mConst.mLinkerObject = dec->mLinkerObject; + ins->mConst.mMemory = IM_PROCEDURE; + ins->mConst.mIntConst = 0; + block->Append(ins); + return ExValue(dec->mBase, ins->mDst.mTemp); + + } break; + + case DT_CONST_ASSEMBLER: + { + if (!dec->mLinkerObject) + TranslateAssembler(proc->mModule, dec, nullptr); + + InterInstruction* ins = new InterInstruction(MapLocation(exp, inlineMapper), IC_CONSTANT); + ins->mDst.mType = IT_POINTER; + ins->mDst.mTemp = proc->AddTemporary(ins->mDst.mType); + ins->mConst.mType = IT_POINTER; + ins->mConst.mVarIndex = dec->mVarIndex; + ins->mConst.mLinkerObject = dec->mLinkerObject; + ins->mConst.mMemory = IM_PROCEDURE; + ins->mConst.mIntConst = 0; + block->Append(ins); + return ExValue(TheVoidPointerTypeDeclaration, ins->mDst.mTemp); + } + + case DT_LABEL: + { + if (!dec->mBase->mLinkerObject) + TranslateAssembler(proc->mModule, dec->mBase, nullptr); + + InterInstruction* ins = new InterInstruction(MapLocation(exp, inlineMapper), IC_CONSTANT); + ins->mDst.mType = IT_POINTER; + ins->mDst.mTemp = proc->AddTemporary(ins->mDst.mType); + ins->mConst.mType = IT_POINTER; + ins->mConst.mVarIndex = dec->mBase->mVarIndex; + ins->mConst.mLinkerObject = dec->mBase->mLinkerObject; + ins->mConst.mMemory = IM_PROCEDURE; + ins->mConst.mIntConst = dec->mInteger; + block->Append(ins); + return ExValue(TheVoidPointerTypeDeclaration, ins->mDst.mTemp); + } + + case DT_LABEL_REF: + { + if (!dec->mBase->mBase->mLinkerObject) + TranslateAssembler(proc->mModule, dec->mBase->mBase, nullptr); + + InterInstruction* ins = new InterInstruction(MapLocation(exp, inlineMapper), IC_CONSTANT); + ins->mDst.mType = IT_POINTER; + ins->mDst.mTemp = proc->AddTemporary(ins->mDst.mType); + ins->mConst.mType = IT_POINTER; + ins->mConst.mVarIndex = dec->mBase->mBase->mVarIndex; + ins->mConst.mLinkerObject = dec->mBase->mBase->mLinkerObject; + ins->mConst.mMemory = IM_PROCEDURE; + ins->mConst.mIntConst = dec->mInteger + dec->mBase->mInteger; + block->Append(ins); + return ExValue(TheVoidPointerTypeDeclaration, ins->mDst.mTemp); + } + + case DT_CONST_POINTER: + { + vl = TranslateExpression(procType, proc, block, dec->mValue, destack, gotos, breakBlock, continueBlock, inlineMapper); + vl.mReference--; + vl.mType = exp->mDecType; + if (dec->mOffset) + { + vl = Dereference(proc, exp, block, inlineMapper, vl, 0); + + InterInstruction* cins = new InterInstruction(MapLocation(exp, inlineMapper), IC_CONSTANT); + cins->mConst.mType = IT_INT16; + cins->mConst.mIntConst = dec->mOffset; + cins->mDst.mType = IT_INT16; + cins->mDst.mTemp = proc->AddTemporary(cins->mDst.mType); + block->Append(cins); + + InterInstruction* ains = new InterInstruction(MapLocation(exp, inlineMapper), IC_LEA); + ains->mSrc[1].mMemory = IM_INDIRECT; + ains->mSrc[0].mType = IT_INT16; + ains->mSrc[0].mTemp = cins->mDst.mTemp; + ains->mSrc[1].mType = IT_POINTER; + ains->mSrc[1].mTemp = vl.mTemp; + ains->mDst.mType = IT_POINTER; + ains->mDst.mTemp = proc->AddTemporary(ains->mDst.mType); + block->Append(ains); + + vl.mTemp = ains->mDst.mTemp; + } + return vl; + } + case DT_CONST_DATA: + { + if (!dec->mLinkerObject) + { + dec->mVarIndex = proc->mModule->mGlobalVars.Size(); + InterVariable* var = new InterVariable(); + var->mIndex = dec->mVarIndex; + var->mIdent = dec->mQualIdent; + var->mOffset = 0; + var->mSize = dec->mSize; + var->mLinkerObject = mLinker->AddObject(dec->mLocation, dec->mQualIdent, dec->mSection, LOT_DATA, dec->mAlignment); + var->mLinkerObject->mVariable = var; + + if (dec->mBase->mFlags & DTF_CONST) + var->mLinkerObject->mFlags |= LOBJF_CONST; + else if ((dec->mFlags & DTF_VAR_ALIASING) || dec->mBase->mType == DT_TYPE_ARRAY) + var->mAliased = true; + var->mLinkerObject->AddData(dec->mData, dec->mSize); + + LinkerObject* aobj = mLinker->FindSame(var->mLinkerObject); + if (aobj) + var->mLinkerObject = aobj; + + dec->mLinkerObject = var->mLinkerObject; + proc->mModule->mGlobalVars.Push(var); + } + + InterInstruction * ins = new InterInstruction(MapLocation(exp, inlineMapper), IC_CONSTANT); + ins->mDst.mType = IT_POINTER; + ins->mDst.mTemp = proc->AddTemporary(IT_POINTER); + ins->mConst.mType = IT_POINTER; + ins->mConst.mIntConst = 0; + ins->mConst.mVarIndex = dec->mVarIndex; + assert(dec->mVarIndex >= 0); + ins->mConst.mLinkerObject = dec->mLinkerObject; + ins->mConst.mMemory = IM_GLOBAL; + block->Append(ins); + return ExValue(dec->mBase, ins->mDst.mTemp, 1); + } + + case DT_CONST_STRUCT: + { + if (!dec->mLinkerObject) + { + InterVariable * var = new InterVariable(); + var->mOffset = 0; + var->mSize = dec->mSize; + if (dec->mFlags & DTF_VAR_ALIASING) + var->mAliased = true; + var->mLinkerObject = mLinker->AddObject(dec->mLocation, dec->mQualIdent, dec->mSection, LOT_DATA, dec->mAlignment); + var->mLinkerObject->mVariable = var; + dec->mLinkerObject = var->mLinkerObject; + var->mIdent = dec->mQualIdent; + var->mDeclaration = dec; + dec->mVarIndex = proc->mModule->mGlobalVars.Size(); + var->mIndex = dec->mVarIndex; + proc->mModule->mGlobalVars.Push(var); + + uint8* d = var->mLinkerObject->AddSpace(dec->mSize);; + + BuildInitializer(proc->mModule, d, 0, dec, var); + } + + InterInstruction * ins = new InterInstruction(MapLocation(exp, inlineMapper), IC_CONSTANT); + ins->mDst.mType = IT_POINTER; + ins->mDst.mTemp = proc->AddTemporary(IT_POINTER); + ins->mConst.mType = IT_POINTER; + ins->mConst.mIntConst = 0; + ins->mConst.mVarIndex = dec->mVarIndex; + assert(dec->mVarIndex >= 0); + ins->mConst.mLinkerObject = dec->mLinkerObject; + ins->mConst.mMemory = IM_GLOBAL; + block->Append(ins); + return ExValue(dec->mBase, ins->mDst.mTemp, 1); + } + + default: + mErrors->Error(dec->mLocation, EERR_CONSTANT_TYPE, "Unimplemented constant type"); + } + + return ExValue(TheVoidTypeDeclaration); + + case EX_VARIABLE: + { + dec = exp->mDecValue; + + InterInstruction * ins = new InterInstruction(MapLocation(exp, inlineMapper), IC_CONSTANT); + ins->mDst.mType = IT_POINTER; + ins->mDst.mTemp = proc->AddTemporary(ins->mDst.mType); + ins->mConst.mType = IT_POINTER; + ins->mConst.mOperandSize = dec->mSize; + ins->mConst.mIntConst = dec->mOffset; + + int lbits = dec->mBits, lshift = dec->mShift; + + if (dec->mType == DT_VARIABLE_REF) + dec = dec->mBase; + + ins->mConst.mVarIndex = dec->mVarIndex; + + int ref = 1; + if (dec->mType == DT_ARGUMENT) + { + if (dec->mFlags & DTF_FPARAM_UNUSED) + { + ins->mConst.mMemory = IM_LOCAL; + if (inlineMapper) + ins->mConst.mVarIndex += inlineMapper->mVarIndex; + InitLocalVariable(proc, dec, ins->mConst.mVarIndex); + proc->mCheckUnreachable = false; + } + else if (inlineMapper) + { + ins->mConst.mMemory = IM_LOCAL; + ins->mConst.mVarIndex = inlineMapper->mParams[dec->mVarIndex]; + InitLocalVariable(proc, dec, ins->mConst.mVarIndex); + } + else if (procType->mFlags & DTF_FASTCALL) + { + ins->mConst.mMemory = IM_FPARAM; +// ins->mConst.mVarIndex += procType->mFastCallBase; + InitParameter(proc, dec, ins->mConst.mVarIndex); + } + else + { + ins->mConst.mMemory = IM_PARAM; + InitParameter(proc, dec, ins->mConst.mVarIndex); + } + + if (dec->mBase->mType == DT_TYPE_ARRAY) + { + ref = 2; + ins->mConst.mOperandSize = 2; + } + } + else if (dec->mType == DT_CONST_STRUCT || (dec->mFlags & DTF_GLOBAL)) + { + InitGlobalVariable(proc->mModule, dec); + ins->mConst.mMemory = IM_GLOBAL; + ins->mConst.mLinkerObject = dec->mLinkerObject; + ins->mConst.mVarIndex = dec->mVarIndex; + assert(dec->mVarIndex >= 0); + } + else + { + if (inlineMapper) + ins->mConst.mVarIndex += inlineMapper->mVarIndex; + + InitLocalVariable(proc, dec, ins->mConst.mVarIndex); + + ins->mConst.mMemory = IM_LOCAL; + } + + block->Append(ins); + + if (!(dec->mBase->mFlags & DTF_DEFINED)) + mErrors->Error(dec->mLocation, EERR_VARIABLE_TYPE, "Undefined variable type for", dec->mQualIdent); + + if (exp->mDecType->IsReference()) + return ExValue(exp->mDecType->mBase, ins->mDst.mTemp, ref + 1); + else + return ExValue(exp->mDecType, ins->mDst.mTemp, ref, lbits, lshift); + } + + + case EX_ASSIGNMENT: + case EX_INITIALIZATION: + { + if (exp->mLeft->mDecType && exp->mLeft->mDecType->mType == DT_TYPE_STRUCT) + { + vl = TranslateExpression(procType, proc, block, exp->mLeft, destack, gotos, breakBlock, continueBlock, inlineMapper); + vr = TranslateExpression(procType, proc, block, exp->mRight, destack, gotos, breakBlock, continueBlock, inlineMapper, &vl); + } + else if (exp->mType == EX_INITIALIZATION && exp->mLeft->mDecType && exp->mLeft->mDecType->IsReference()) + { + vr = TranslateExpression(procType, proc, block, exp->mRight, destack, gotos, breakBlock, continueBlock, inlineMapper); + vl = TranslateExpression(procType, proc, block, exp->mLeft, destack, gotos, breakBlock, continueBlock, inlineMapper); + vl.mType = exp->mLeft->mDecType; + } + else + { + vr = TranslateExpression(procType, proc, block, exp->mRight, destack, gotos, breakBlock, continueBlock, inlineMapper); + vl = TranslateExpression(procType, proc, block, exp->mLeft, destack, gotos, breakBlock, continueBlock, inlineMapper); + } + + if (exp->mType == EX_ASSIGNMENT) + { + vl = ToValue(proc, exp, block, inlineMapper, vl); + vr = ToValue(proc, exp, block, inlineMapper, vr); + } + + if (exp->mLeft->mDecType->mType == DT_TYPE_POINTER && vr.mType->mType == DT_TYPE_INTEGER) + { + Expression* er = exp->mRight; + while (er && er->mType == EX_COMMA) + er = er->mRight; + if (er && er->mType == EX_CONSTANT && er->mDecValue->mType == DT_CONST_INTEGER && er->mDecValue->mInteger == 0) + { + mErrors->Error(exp->mLocation, EWARN_NUMERIC_0_USED_AS_NULLPTR, "Numeric 0 used for nullptr"); + vr = CoerceType(proc, exp, block, inlineMapper, vr, exp->mLeft->mDecType); + } + } + + if (exp->mToken == TK_ASSIGN || !(vl.mType->mType == DT_TYPE_POINTER && vr.mType->IsIntegerType() && (exp->mToken == TK_ASSIGN_ADD || exp->mToken == TK_ASSIGN_SUB))) + { + if (!vl.mType->CanAssign(vr.mType)) + { + vl.mType->CanAssign(vr.mType); + mErrors->Error(exp->mLocation, EERR_INCOMPATIBLE_TYPES, "Cannot assign incompatible types", vl.mType->MangleIdent(), vr.mType->MangleIdent()); + } + } + + if (exp->mType != EX_INITIALIZATION && (vl.mType->mFlags & DTF_CONST)) + mErrors->Error(exp->mLocation, EERR_CONST_ASSIGN, "Cannot assign to const type"); + else if (exp->mType != EX_INITIALIZATION && !vl.mType->mCopyAssignment && vl.mType->HasConstMember()) + mErrors->Error(exp->mLocation, EERR_CONST_ASSIGN, "Cannot assign to item with const member"); + + if (exp->mType == EX_INITIALIZATION && exp->mRight->mType == EX_CONSTANT && exp->mRight->mDecValue && exp->mRight->mDecValue->mLinkerObject) + { + if (!(exp->mRight->mDecValue->mFlags & DTF_VAR_ALIASING)) + exp->mRight->mDecValue->mLinkerObject->mFlags |= LOBJF_CONST; + } + + + if (vl.mType->IsReference()) + { + if (vr.mType->IsReference()) + vr = Dereference(proc, exp, block, inlineMapper, vr, 0); + else + { + vr = Dereference(proc, exp, block, inlineMapper, vr, 1); + if (vr.mReference != 1) + mErrors->Error(exp->mLeft->mLocation, EERR_NOT_AN_LVALUE, "Not an addressable expression"); + } + + vl = Dereference(proc, exp, block, inlineMapper, vl, 2); + + if (vl.mReference != 2) + mErrors->Error(exp->mLeft->mLocation, EERR_NOT_AN_LVALUE, "Not a left hand reference expression"); + + if (vr.mTemp != vl.mTemp) + { + InterInstruction* ins = new InterInstruction(MapLocation(exp, inlineMapper), IC_STORE); + ins->mSrc[0].mType = IT_POINTER; + ins->mSrc[0].mTemp = vr.mTemp; + ins->mSrc[0].mMemory = IM_INDIRECT; + ins->mSrc[0].mOperandSize = 2; + ins->mSrc[0].mStride = vr.mType->mStripe; + + ins->mSrc[1].mType = IT_POINTER; + ins->mSrc[1].mTemp = vl.mTemp; + ins->mSrc[1].mMemory = IM_INDIRECT; + ins->mSrc[1].mOperandSize = 2; + ins->mSrc[1].mStride = vl.mType->mStripe; + ins->mNumOperands = 2; + + block->Append(ins); + } + } + else if (vl.mType->mType == DT_TYPE_STRUCT || vl.mType->mType == DT_TYPE_ARRAY || vl.mType->mType == DT_TYPE_UNION) + { + vr = ToValue(proc, exp, block, inlineMapper, vr); + + vr = Dereference(proc, exp, block, inlineMapper, vr, 1); + vl = Dereference(proc, exp, block, inlineMapper, vl, 1); + + if (vl.mReference != 1) + mErrors->Error(exp->mLeft->mLocation, EERR_NOT_AN_LVALUE, "Not a left hand expression"); + if (vr.mReference != 1) + mErrors->Error(exp->mLeft->mLocation, EERR_NOT_AN_LVALUE, "Not an addressable expression"); + + if (vr.mTemp != vl.mTemp) + block->Append(CopyStructSimple(proc, exp, block, inlineMapper, vl, vr)); + } + else + { + vr = ToValue(proc, exp, block, inlineMapper, vr); + + if (vl.mType->mType == DT_TYPE_POINTER && (vr.mType->mType == DT_TYPE_ARRAY || vr.mType->mType == DT_TYPE_FUNCTION)) + { + vr = Dereference(proc, exp, block, inlineMapper, vr, 1); + vr.mReference = 0; + vr.mType = vl.mType; + } + else + vr = Dereference(proc, exp, block, inlineMapper, vr); + + vl = Dereference(proc, exp, block, inlineMapper, vl, 1); + + if (vl.mReference != 1) + mErrors->Error(exp->mLeft->mLocation, EERR_NOT_AN_LVALUE, "Not a left hand expression"); + + if (exp->mToken != TK_ASSIGN) + { + ExValue vll = Dereference(proc, exp, block, inlineMapper, vl); + + if (vl.mType->mType == DT_TYPE_POINTER) + { + InterInstruction * cins = new InterInstruction(MapLocation(exp, inlineMapper), IC_CONSTANT); + cins->mConst.mType = IT_INT16; + + if (exp->mToken == TK_ASSIGN_ADD) + { + cins->mConst.mIntConst = vl.mType->Stride(); + } + else if (exp->mToken == TK_ASSIGN_SUB) + { + cins->mConst.mIntConst = -vl.mType->Stride(); + } + else + mErrors->Error(exp->mLocation, EERR_INCOMPATIBLE_OPERATOR, "Invalid pointer assignment"); + + if (!vr.mType->IsIntegerType()) + mErrors->Error(exp->mLocation, EERR_INCOMPATIBLE_TYPES, "Invalid argument for pointer inc/dec", vr.mType->MangleIdent()); + + cins->mDst.mType = IT_INT16; + cins->mDst.mTemp = proc->AddTemporary(cins->mDst.mType); + block->Append(cins); + + vr = CoerceType(proc, exp, block, inlineMapper, vr, TheSignedIntTypeDeclaration); + + InterInstruction * mins = new InterInstruction(MapLocation(exp, inlineMapper), IC_BINARY_OPERATOR); + mins->mOperator = IA_MUL; + mins->mSrc[0].mType = IT_INT16; + mins->mSrc[0].mTemp = vr.mTemp; + mins->mSrc[1].mType = IT_INT16; + mins->mSrc[1].mTemp = cins->mDst.mTemp; + mins->mDst.mType = IT_INT16; + mins->mDst.mTemp = proc->AddTemporary(mins->mDst.mType); + block->Append(mins); + + InterInstruction * ains = new InterInstruction(MapLocation(exp, inlineMapper), IC_LEA); + ains->mSrc[1].mMemory = IM_INDIRECT; + ains->mSrc[0].mType = IT_INT16; + ains->mSrc[0].mTemp = mins->mDst.mTemp; + ains->mSrc[1].mType = IT_POINTER; + ains->mSrc[1].mTemp = vll.mTemp; + ains->mDst.mType = IT_POINTER; + ains->mDst.mTemp = proc->AddTemporary(ains->mDst.mType); + block->Append(ains); + + vr.mTemp = ains->mDst.mTemp; + vr.mType = vll.mType; + } + else + { + Declaration * otype = vll.mType; + if (vll.mType->mType == DT_TYPE_BOOL && vr.mType->mType == DT_TYPE_BOOL) + otype = TheUnsignedCharTypeDeclaration; + +#if 1 + if ((exp->mRight->mType != EX_CONSTANT || (exp->mToken != TK_ASSIGN_ADD && exp->mToken != TK_ASSIGN_SUB && exp->mToken != TK_ASSIGN_AND && exp->mToken != TK_ASSIGN_OR)) && otype->mSize < 2) +#else + if (otype->mSize < 2) +#endif + { + if ((vll.mType->mFlags | vr.mType->mFlags) & DTF_SIGNED) + otype = TheSignedIntTypeDeclaration; + else + otype = TheUnsignedIntTypeDeclaration; + vll = CoerceType(proc, exp, block, inlineMapper, vll, otype); + } + + vr = CoerceType(proc, exp, block, inlineMapper, vr, otype, exp->mToken != TK_ASSIGN_AND); + + InterInstruction * oins = new InterInstruction(MapLocation(exp, inlineMapper), IC_BINARY_OPERATOR); + oins->mSrc[0].mType = InterTypeOf(otype); + oins->mSrc[0].mTemp = vr.mTemp; + oins->mSrc[1].mType = InterTypeOf(otype); + oins->mSrc[1].mTemp = vll.mTemp; + oins->mDst.mType = InterTypeOf(otype); + oins->mDst.mTemp = proc->AddTemporary(oins->mDst.mType); + + if (!vll.mType->IsNumericType()) + mErrors->Error(exp->mLocation, EERR_INCOMPATIBLE_OPERATOR, "Left hand element type is not numeric"); + if (!vr.mType->IsNumericType()) + mErrors->Error(exp->mLocation, EERR_INCOMPATIBLE_OPERATOR, "Right hand element type is not numeric"); + + switch (exp->mToken) + { + case TK_ASSIGN_ADD: + oins->mOperator = IA_ADD; + break; + case TK_ASSIGN_SUB: + oins->mOperator = IA_SUB; + break; + case TK_ASSIGN_MUL: + oins->mOperator = IA_MUL; + break; + case TK_ASSIGN_DIV: + if (vll.mType->mFlags & DTF_SIGNED) + oins->mOperator = IA_DIVS; + else + oins->mOperator = IA_DIVU; + break; + case TK_ASSIGN_MOD: + if (vll.mType->mFlags & DTF_SIGNED) + oins->mOperator = IA_MODS; + else + oins->mOperator = IA_MODU; + break; + case TK_ASSIGN_SHL: + oins->mOperator = IA_SHL; + break; + case TK_ASSIGN_SHR: + if (vll.mType->mFlags & DTF_SIGNED) + oins->mOperator = IA_SAR; + else + oins->mOperator = IA_SHR; + break; + case TK_ASSIGN_AND: + oins->mOperator = IA_AND; + break; + case TK_ASSIGN_XOR: + oins->mOperator = IA_XOR; + break; + case TK_ASSIGN_OR: + oins->mOperator = IA_OR; + break; + } + + vr.mTemp = oins->mDst.mTemp; + vr.mType = otype; + + block->Append(oins); + + vr = CoerceType(proc, exp, block, inlineMapper, vr, vl.mType); + } + } + else + { + vr = CoerceType(proc, exp, block, inlineMapper, vr, vl.mType); + } + + if (vl.mType->mType != DT_TYPE_VOID) + StoreValue(proc, exp, block, inlineMapper, vl, vr); + } + } + + return vl; + + case EX_INDEX: + { + vl = TranslateExpression(procType, proc, block, exp->mLeft, destack, gotos, breakBlock, continueBlock, inlineMapper); + vr = TranslateExpression(procType, proc, block, exp->mRight, destack, gotos, breakBlock, continueBlock, inlineMapper); + + vl = ToValue(proc, exp, block, inlineMapper, vl); + vr = ToValue(proc, exp, block, inlineMapper, vr); + + int stride = vl.mType->Stride(); + + if (vl.mType->mType == DT_TYPE_ARRAY && exp->mRight->mType == EX_CONSTANT && exp->mRight->mDecValue->mType == DT_CONST_INTEGER) + { + if (vl.mType->mFlags & DTF_DEFINED) + { + int64 index = exp->mRight->mDecValue->mInteger; + if (vl.mType->mSize > 0 && (index < 0 || index * stride >= vl.mType->mSize * vl.mType->mStripe)) + mErrors->Error(exp->mLocation, EWARN_INDEX_OUT_OF_BOUNDS, "Constant array index out of bounds"); + } + } + + vl = Dereference(proc, exp, block, inlineMapper, vl, vl.mType->mType == DT_TYPE_POINTER ? 0 : 1); + + vr = Dereference(proc, exp, block, inlineMapper, vr); + + if (vl.mType->mType != DT_TYPE_ARRAY && vl.mType->mType != DT_TYPE_POINTER) + { + mErrors->Error(exp->mLocation, EERR_INVALID_INDEX, "Invalid type for indexing"); + return ExValue(TheConstVoidTypeDeclaration, -1); + } + + if (!vr.mType->IsIntegerType()) + mErrors->Error(exp->mLocation, EERR_INVALID_INDEX, "Index operand is not integral number"); + + vr = CoerceType(proc, exp, block, inlineMapper, vr, TheSignedIntTypeDeclaration); + + InterInstruction * cins = new InterInstruction(MapLocation(exp, inlineMapper), IC_CONSTANT); + cins->mConst.mType = IT_INT16; + cins->mConst.mIntConst = stride; + cins->mDst.mType = IT_INT16; + cins->mDst.mTemp = proc->AddTemporary(cins->mDst.mType); + block->Append(cins); + + InterInstruction * mins = new InterInstruction(MapLocation(exp, inlineMapper), IC_BINARY_OPERATOR); + mins->mOperator = IA_MUL; + mins->mSrc[0].mType = IT_INT16; + mins->mSrc[0].mTemp = vr.mTemp; + mins->mSrc[1].mType = IT_INT16; + mins->mSrc[1].mTemp = cins->mDst.mTemp; + mins->mDst.mType = IT_INT16; + mins->mDst.mTemp = proc->AddTemporary(mins->mDst.mType); + block->Append(mins); + + InterInstruction * ains = new InterInstruction(MapLocation(exp, inlineMapper), IC_LEA); + ains->mSrc[1].mMemory = IM_INDIRECT; + ains->mSrc[0].mType = IT_INT16; + ains->mSrc[0].mTemp = mins->mDst.mTemp; + if (vl.mType->mType == DT_TYPE_ARRAY && vl.mType->mSize > vl.mType->mBase->mSize && (exp->mFlags & ANAFL_RHS)) + { + ains->mSrc[0].mRange.LimitMin(0); + if (stride == 1) + ains->mSrc[0].mRange.LimitMax(vl.mType->mSize / vl.mType->mBase->mSize); + else + ains->mSrc[0].mRange.LimitMax(vl.mType->mSize); + } + ains->mSrc[1].mType = IT_POINTER; + ains->mSrc[1].mTemp = vl.mTemp; + ains->mDst.mType = IT_POINTER; + ains->mDst.mTemp = proc->AddTemporary(ains->mDst.mType); + block->Append(ains); + + return ExValue(vl.mType->mBase, ains->mDst.mTemp, 1); + } + + case EX_QUALIFY: + { + vl = TranslateExpression(procType, proc, block, exp->mLeft, destack, gotos, breakBlock, continueBlock, inlineMapper); + + vl = ToValue(proc, exp, block, inlineMapper, vl); + + vl = Dereference(proc, exp, block, inlineMapper, vl, 1); + + if (vl.mReference != 1) + mErrors->Error(exp->mLocation, EERR_NOT_AN_LVALUE, "Not an addressable expression"); + + InterInstruction * cins = new InterInstruction(MapLocation(exp, inlineMapper), IC_CONSTANT); + cins->mConst.mType = IT_INT16; + cins->mConst.mIntConst = exp->mDecValue->mOffset; + cins->mDst.mType = IT_INT16; + cins->mDst.mTemp = proc->AddTemporary(cins->mDst.mType); + block->Append(cins); + + InterInstruction * ains = new InterInstruction(MapLocation(exp, inlineMapper), IC_LEA); + ains->mSrc[1].mMemory = IM_INDIRECT; + ains->mSrc[0].mType = IT_INT16; + ains->mSrc[0].mTemp = cins->mDst.mTemp; + ains->mSrc[1].mType = IT_POINTER; + ains->mSrc[1].mTemp = vl.mTemp; + ains->mDst.mType = IT_POINTER; + ains->mDst.mTemp = proc->AddTemporary(ains->mDst.mType); + block->Append(ains); + + if (exp->mDecType->IsReference()) + return ExValue(exp->mDecType->mBase, ains->mDst.mTemp, 2); + else + return ExValue(exp->mDecType, ains->mDst.mTemp, 1, exp->mDecValue->mBits, exp->mDecValue->mShift); + } + + case EX_BINARY: + { + vl = TranslateExpression(procType, proc, block, exp->mLeft, destack, gotos, breakBlock, continueBlock, inlineMapper); + vr = TranslateExpression(procType, proc, block, exp->mRight, destack, gotos, breakBlock, continueBlock, inlineMapper); + + vl = ToValue(proc, exp, block, inlineMapper, vl); + vr = ToValue(proc, exp, block, inlineMapper, vr); + + InterInstruction * ins = new InterInstruction(MapLocation(exp, inlineMapper), IC_BINARY_OPERATOR); + + if (vl.mType->mType == DT_TYPE_POINTER || vl.mType->mType == DT_TYPE_ARRAY) + { + if (vl.mType->mType == DT_TYPE_POINTER) + vl = Dereference(proc, exp, block, inlineMapper, vl); + else + { + vl = Dereference(proc, exp, block, inlineMapper, vl, 1); + + Declaration* ptype = new Declaration(exp->mLocation, DT_TYPE_POINTER); + ptype->mBase = vl.mType->mBase; + ptype->mSize = 2; + ptype->mStride = vl.mType->mStride; + vl.mReference = 0; + vl.mType = ptype; + } + + if (vr.mType->mType == DT_TYPE_POINTER) + vr = Dereference(proc, exp, block, inlineMapper, vr); + else if (vr.mType->mType == DT_TYPE_ARRAY) + { + vr = Dereference(proc, exp, block, inlineMapper, vr, 1); + + Declaration* ptype = new Declaration(exp->mLocation, DT_TYPE_POINTER); + ptype->mBase = vr.mType->mBase; + ptype->mSize = 2; + ptype->mStride = vr.mType->mStride; + vr.mReference = 0; + vr.mType = ptype; + } + else + vr = Dereference(proc, exp, block, inlineMapper, vr); + + if (vr.mType->IsIntegerType()) + { + InterInstruction * cins = new InterInstruction(MapLocation(exp, inlineMapper), IC_CONSTANT); + cins->mConst.mType = IT_INT16; + + if (exp->mToken == TK_ADD) + { + cins->mConst.mIntConst = vl.mType->Stride(); + } + else if (exp->mToken == TK_SUB) + { + cins->mConst.mIntConst = -vl.mType->Stride(); + } + else + mErrors->Error(exp->mLocation, EERR_INCOMPATIBLE_OPERATOR, "Invalid pointer operation"); + + cins->mDst.mType = IT_INT16; + cins->mDst.mTemp = proc->AddTemporary(cins->mDst.mType); + block->Append(cins); + + vr = CoerceType(proc, exp, block, inlineMapper, vr, TheSignedIntTypeDeclaration); + + InterInstruction * mins = new InterInstruction(MapLocation(exp, inlineMapper), IC_BINARY_OPERATOR); + mins->mCode = IC_BINARY_OPERATOR; + mins->mOperator = IA_MUL; + mins->mSrc[0].mType = IT_INT16; + mins->mSrc[0].mTemp = vr.mTemp; + mins->mSrc[1].mType = IT_INT16; + mins->mSrc[1].mTemp = cins->mDst.mTemp; + mins->mDst.mType = IT_INT16; + mins->mDst.mTemp = proc->AddTemporary(mins->mDst.mType); + block->Append(mins); + + ins->mCode = IC_LEA; + ins->mSrc[1].mMemory = IM_INDIRECT; + ins->mSrc[0].mType = IT_INT16; + ins->mSrc[0].mTemp = mins->mDst.mTemp; + ins->mSrc[1].mType = IT_POINTER; + ins->mSrc[1].mTemp = vl.mTemp; + ins->mDst.mType = IT_POINTER; + ins->mDst.mTemp = proc->AddTemporary(ins->mDst.mType); + block->Append(ins); + } + else if ((vr.mType->mType == DT_TYPE_POINTER || vr.mType->mType == DT_TYPE_ARRAY) && (vl.mType->mType == DT_TYPE_POINTER || vl.mType->mType == DT_TYPE_ARRAY) && vr.mType->mBase->IsConstSame(vl.mType->mBase)) + { + if (exp->mToken == TK_SUB) + { + InterInstruction * clins = new InterInstruction(MapLocation(exp, inlineMapper), IC_TYPECAST), * crins = new InterInstruction(MapLocation(exp, inlineMapper), IC_TYPECAST); + clins->mSrc[0].mTemp = vl.mTemp; + clins->mSrc[0].mType = IT_POINTER; + clins->mDst.mType = IT_INT16; + clins->mDst.mTemp = proc->AddTemporary(clins->mDst.mType); + block->Append(clins); + + crins->mSrc[0].mTemp = vr.mTemp; + crins->mSrc[0].mType = IT_POINTER; + crins->mDst.mType = IT_INT16; + crins->mDst.mTemp = proc->AddTemporary(crins->mDst.mType); + block->Append(crins); + + int s = vl.mType->Stride(); + bool binary = !(s & (s - 1)); + if (binary) + { + int n = 0; + while (s > 1) + { + s >>= 1; + n++; + } + s = n; + } + + InterInstruction * cins = new InterInstruction(MapLocation(exp, inlineMapper), IC_CONSTANT); + cins->mConst.mType = IT_INT16; + cins->mConst.mIntConst = s; + cins->mDst.mType = IT_INT16; + cins->mDst.mTemp = proc->AddTemporary(cins->mDst.mType); + block->Append(cins); + + InterInstruction * sins = new InterInstruction(MapLocation(exp, inlineMapper), IC_BINARY_OPERATOR), * dins = new InterInstruction(MapLocation(exp, inlineMapper), IC_BINARY_OPERATOR); + sins->mOperator = IA_SUB; + sins->mSrc[0].mType = IT_INT16; + sins->mSrc[0].mTemp = crins->mDst.mTemp; + sins->mSrc[1].mType = IT_INT16; + sins->mSrc[1].mTemp = clins->mDst.mTemp; + sins->mDst.mType = IT_INT16; + sins->mDst.mTemp = proc->AddTemporary(sins->mDst.mType); + block->Append(sins); + + dins->mOperator = binary ? IA_SAR : IA_DIVS; + dins->mSrc[0].mType = IT_INT16; + dins->mSrc[0].mTemp = cins->mDst.mTemp; + dins->mSrc[1].mType = IT_INT16; + dins->mSrc[1].mTemp = sins->mDst.mTemp; + dins->mDst.mType = IT_INT16; + dins->mDst.mTemp = proc->AddTemporary(dins->mDst.mType); + block->Append(dins); + + return ExValue(TheSignedIntTypeDeclaration, dins->mDst.mTemp); + } + else + mErrors->Error(exp->mLocation, EERR_INCOMPATIBLE_OPERATOR, "Invalid pointer operation"); + } + else + mErrors->Error(exp->mLocation, EERR_INCOMPATIBLE_OPERATOR, "Invalid pointer operation"); + + vl.mType = exp->mDecType; + } + else if (vr.mType->mType == DT_TYPE_POINTER || vr.mType->mType == DT_TYPE_ARRAY) + { + if (vr.mType->mType == DT_TYPE_POINTER) + vr = Dereference(proc, exp, block, inlineMapper, vr); + else + { + vr = Dereference(proc, exp, block, inlineMapper, vr, 1); + + Declaration* ptype = new Declaration(exp->mLocation, DT_TYPE_POINTER); + ptype->mBase = vr.mType->mBase; + ptype->mSize = 2; + vr.mType = ptype; + } + + if (vl.mType->IsIntegerType()) + { + InterInstruction* cins = new InterInstruction(MapLocation(exp, inlineMapper), IC_CONSTANT); + cins->mConst.mType = IT_INT16; + + if (exp->mToken == TK_ADD) + { + cins->mConst.mIntConst = vr.mType->Stride(); + } + else + mErrors->Error(exp->mLocation, EERR_INCOMPATIBLE_OPERATOR, "Invalid pointer operation"); + + cins->mDst.mType = IT_INT16; + cins->mDst.mTemp = proc->AddTemporary(cins->mDst.mType); + block->Append(cins); + + vl = CoerceType(proc, exp, block, inlineMapper, vl, TheSignedIntTypeDeclaration); + + InterInstruction* mins = new InterInstruction(MapLocation(exp, inlineMapper), IC_BINARY_OPERATOR); + mins->mOperator = IA_MUL; + mins->mSrc[0].mType = IT_INT16; + mins->mSrc[0].mTemp = vl.mTemp; + mins->mSrc[1].mType = IT_INT16; + mins->mSrc[1].mTemp = cins->mDst.mTemp; + mins->mDst.mType = IT_INT16; + mins->mDst.mTemp = proc->AddTemporary(mins->mDst.mType); + block->Append(mins); + + ins->mCode = IC_LEA; + ins->mSrc[1].mMemory = IM_INDIRECT; + ins->mSrc[0].mType = IT_INT16; + ins->mSrc[0].mTemp = mins->mDst.mTemp; + ins->mSrc[1].mType = IT_POINTER; + ins->mSrc[1].mTemp = vr.mTemp; + ins->mDst.mType = IT_POINTER; + ins->mDst.mTemp = proc->AddTemporary(ins->mDst.mType); + block->Append(ins); + + vl = vr; + } + else + mErrors->Error(exp->mLocation, EERR_INCOMPATIBLE_OPERATOR, "Invalid pointer operation"); + } + else + { + vl = Dereference(proc, exp, block, inlineMapper, vl); + vr = Dereference(proc, exp, block, inlineMapper, vr); + + if (!vl.mType->IsNumericType()) + mErrors->Error(exp->mLocation, EERR_INCOMPATIBLE_OPERATOR, "Left hand operand type is not numeric"); + if (!vr.mType->IsNumericType()) + mErrors->Error(exp->mLocation, EERR_INCOMPATIBLE_OPERATOR, "Right hand operand type is not numeric"); + + Declaration* dtype; + if (vr.mType->mType == DT_TYPE_FLOAT || vl.mType->mType == DT_TYPE_FLOAT) + dtype = TheFloatTypeDeclaration; + else if ((exp->mToken == TK_BINARY_AND || exp->mToken == TK_BINARY_OR || exp->mToken == TK_BINARY_XOR) && vl.mType->mSize == 1 && vr.mType->mSize == 1) + { + if (vl.mType->mFlags & DTF_SIGNED) + dtype = vl.mType; + else + dtype = vr.mType; + } + else if (exp->mToken == TK_LEFT_SHIFT || exp->mToken == TK_RIGHT_SHIFT) + { + if (vl.mType->mFlags & DTF_SIGNED) + { + if (vl.mType->mSize == 4) + dtype = TheSignedLongTypeDeclaration; + else + dtype = TheSignedIntTypeDeclaration; + } + else + { + if (vl.mType->mSize == 4 || vr.mType->mSize == 4) + dtype = TheUnsignedLongTypeDeclaration; + else + dtype = TheUnsignedIntTypeDeclaration; + } + } + else if (vr.mType->mSize < vl.mType->mSize && (vl.mType->mFlags & DTF_SIGNED)) + { + if (vl.mType->mSize == 4) + dtype = TheSignedLongTypeDeclaration; + else + dtype = TheSignedIntTypeDeclaration; + } + else if (vl.mType->mSize < vr.mType->mSize && (vr.mType->mFlags & DTF_SIGNED)) + { + if (vr.mType->mSize == 4) + dtype = TheSignedLongTypeDeclaration; + else + dtype = TheSignedIntTypeDeclaration; + } + else if ((vr.mType->mFlags & DTF_SIGNED) && (vl.mType->mFlags & DTF_SIGNED)) + { + if (vl.mType->mSize == 4) + dtype = TheSignedLongTypeDeclaration; + else + dtype = TheSignedIntTypeDeclaration; + } + else + { + if (vl.mType->mSize == 4 || vr.mType->mSize == 4) + dtype = TheUnsignedLongTypeDeclaration; + else + dtype = TheUnsignedIntTypeDeclaration; + } + + // Convert bool to char on binary operation + if (dtype->mType == DT_TYPE_BOOL) + { + if (exp->mToken == TK_BINARY_AND || exp->mToken == TK_BINARY_OR) + mErrors->Error(exp->mLocation, EWARN_BOOL_SHORTCUT, "Binary and/or operator used for boolean values, use logical operator ('&&' '||')."); + dtype = TheUnsignedCharTypeDeclaration; + } + + vl = CoerceType(proc, exp, block, inlineMapper, vl, dtype); + vr = CoerceType(proc, exp, block, inlineMapper, vr, dtype); + + bool signedOP = dtype->mFlags & DTF_SIGNED; + + ins->mCode = IC_BINARY_OPERATOR; + switch (exp->mToken) + { + case TK_ADD: + ins->mOperator = IA_ADD; + break; + case TK_SUB: + ins->mOperator = IA_SUB; + break; + case TK_MUL: + ins->mOperator = IA_MUL; + break; + case TK_DIV: + ins->mOperator = signedOP ? IA_DIVS : IA_DIVU; + if (exp->mRight->mType == EX_CONSTANT && exp->mRight->mDecValue->mType == DT_CONST_INTEGER && exp->mRight->mDecValue->mInteger == 0) + mErrors->Error(exp->mLocation, EWARN_DIVISION_BY_ZERO, "Constant division by zero"); + break; + case TK_MOD: + ins->mOperator = signedOP ? IA_MODS : IA_MODU; + if (exp->mRight->mType == EX_CONSTANT && exp->mRight->mDecValue->mType == DT_CONST_INTEGER && exp->mRight->mDecValue->mInteger == 0) + mErrors->Error(exp->mLocation, EWARN_DIVISION_BY_ZERO, "Constant division by zero"); + break; + case TK_LEFT_SHIFT: + ins->mOperator = IA_SHL; + break; + case TK_RIGHT_SHIFT: + ins->mOperator = (vl.mType->mFlags & DTF_SIGNED) ? IA_SAR : IA_SHR; + break; + case TK_BINARY_AND: + ins->mOperator = IA_AND; + break; + case TK_BINARY_OR: + ins->mOperator = IA_OR; + break; + case TK_BINARY_XOR: + ins->mOperator = IA_XOR; + break; + } + + ins->mSrc[0].mType = InterTypeOf(vr.mType);; + ins->mSrc[0].mTemp = vr.mTemp; + ins->mSrc[1].mType = InterTypeOf(vl.mType);; + ins->mSrc[1].mTemp = vl.mTemp; + ins->mDst.mType = InterTypeOfArithmetic(ins->mSrc[0].mType, ins->mSrc[1].mType); + ins->mDst.mTemp = proc->AddTemporary(ins->mDst.mType); + block->Append(ins); + } + + return ExValue(vl.mType, ins->mDst.mTemp); + } + + + case EX_PREINCDEC: + { + vl = TranslateExpression(procType, proc, block, exp->mLeft, destack, gotos, breakBlock, continueBlock, inlineMapper); + vl = ToValue(proc, exp, block, inlineMapper, vl); + vl = Dereference(proc, exp, block, inlineMapper, vl, 1); + + if (vl.mReference != 1) + mErrors->Error(exp->mLeft->mLocation, EERR_NOT_AN_LVALUE, "Not a left hand expression"); + + if (vl.mType->mFlags & DTF_CONST) + mErrors->Error(exp->mLocation, EERR_CONST_ASSIGN, "Cannot change const value"); + + InterInstruction* cins = new InterInstruction(MapLocation(exp, inlineMapper), IC_CONSTANT), * ains = new InterInstruction(MapLocation(exp, inlineMapper), IC_BINARY_OPERATOR);// , * sins = new InterInstruction(MapLocation(exp, inlineMapper), IC_STORE); + + ExValue vdl = Dereference(proc, exp, block, inlineMapper, vl); + + bool ftype = vl.mType->mType == DT_TYPE_FLOAT; + + cins->mDst.mType = ftype ? IT_FLOAT : IT_INT16; + cins->mDst.mTemp = proc->AddTemporary(cins->mDst.mType); + cins->mConst.mType = cins->mDst.mType; + + if (vdl.mType->mType == DT_TYPE_POINTER) + cins->mConst.mIntConst = exp->mToken == TK_INC ? vdl.mType->Stride() : -(vdl.mType->Stride()); + else if (vdl.mType->IsNumericType()) + { + cins->mConst.mIntConst = exp->mToken == TK_INC ? 1 : -1; + cins->mConst.mFloatConst = double(cins->mConst.mIntConst); + } + else + mErrors->Error(exp->mLocation, EERR_INCOMPATIBLE_OPERATOR, "Not a numeric value or pointer"); + + block->Append(cins); + + InterType ttype = InterTypeOf(vdl.mType); + if (ttype == IT_POINTER) + { + ains->mCode = IC_LEA; + ains->mSrc[1].mMemory = IM_INDIRECT; + } + else + { + ains->mCode = IC_BINARY_OPERATOR; + ains->mOperator = IA_ADD; + } + ains->mSrc[0].mType = cins->mDst.mType; + ains->mSrc[0].mTemp = cins->mDst.mTemp; + ains->mSrc[1].mType = ttype; + ains->mSrc[1].mTemp = vdl.mTemp; + ains->mDst.mType = ttype; + ains->mDst.mTemp = proc->AddTemporary(ains->mDst.mType); + block->Append(ains); + + StoreValue(proc, exp, block, inlineMapper, vl, ExValue(vl.mType, ains->mDst.mTemp)); +#if 0 + sins->mSrc[1].mMemory = IM_INDIRECT; + sins->mSrc[0].mType = ains->mDst.mType; + sins->mSrc[0].mTemp = ains->mDst.mTemp; + sins->mSrc[1].mType = IT_POINTER; + sins->mSrc[1].mTemp = vl.mTemp; + sins->mSrc[1].mOperandSize = vl.mType->mSize; + sins->mVolatile = vl.mType->mFlags & DTF_VOLATILE; + block->Append(sins); +#endif + + // Return reference to value + return ExValue(vl.mType, vl.mTemp, 1); + } + break; + + case EX_POSTINCDEC: + { + vl = TranslateExpression(procType, proc, block, exp->mLeft, destack, gotos, breakBlock, continueBlock, inlineMapper); + vl = ToValue(proc, exp, block, inlineMapper, vl); + vl = Dereference(proc, exp, block, inlineMapper, vl, 1); + + if (vl.mReference != 1) + mErrors->Error(exp->mLeft->mLocation, EERR_NOT_AN_LVALUE, "Not a left hand expression"); + + if (vl.mType->mFlags & DTF_CONST) + mErrors->Error(exp->mLocation, EERR_CONST_ASSIGN, "Cannot change const value"); + + InterInstruction* cins = new InterInstruction(MapLocation(exp, inlineMapper), IC_CONSTANT), * ains = new InterInstruction(MapLocation(exp, inlineMapper), IC_BINARY_OPERATOR);// , * sins = new InterInstruction(MapLocation(exp, inlineMapper), IC_STORE); + + ExValue vdl = Dereference(proc, exp, block, inlineMapper, vl); + + bool ftype = vl.mType->mType == DT_TYPE_FLOAT; + + cins->mDst.mType = ftype ? IT_FLOAT : IT_INT16; + cins->mDst.mTemp = proc->AddTemporary(cins->mDst.mType); + cins->mConst.mType = cins->mDst.mType; + + if (vdl.mType->mType == DT_TYPE_POINTER) + cins->mConst.mIntConst = exp->mToken == TK_INC ? vdl.mType->Stride() : -(vdl.mType->Stride()); + else if (vdl.mType->IsNumericType()) + { + cins->mConst.mIntConst = exp->mToken == TK_INC ? 1 : -1; + cins->mConst.mFloatConst = double(cins->mConst.mIntConst); + } + else + mErrors->Error(exp->mLocation, EERR_INCOMPATIBLE_OPERATOR, "Not a numeric value or pointer"); + block->Append(cins); + + InterType ttype = InterTypeOf(vdl.mType); + if (ttype == IT_POINTER) + { + ains->mCode = IC_LEA; + ains->mSrc[1].mMemory = IM_INDIRECT; + } + else + { + ains->mCode = IC_BINARY_OPERATOR; + ains->mOperator = IA_ADD; + } + ains->mSrc[0].mType = cins->mDst.mType; + ains->mSrc[0].mTemp = cins->mDst.mTemp; + ains->mSrc[1].mType = ttype; + ains->mSrc[1].mTemp = vdl.mTemp; + ains->mDst.mType = ttype; + ains->mDst.mTemp = proc->AddTemporary(ttype); + block->Append(ains); + + StoreValue(proc, exp, block, inlineMapper, vl, ExValue(vl.mType, ains->mDst.mTemp)); +#if 0 + sins->mSrc[1].mMemory = IM_INDIRECT; + sins->mSrc[0].mType = ains->mDst.mType; + sins->mSrc[0].mTemp = ains->mDst.mTemp; + sins->mSrc[1].mType = IT_POINTER; + sins->mSrc[1].mTemp = vl.mTemp; + sins->mSrc[1].mOperandSize = vl.mType->mSize; + sins->mVolatile = vl.mType->mFlags & DTF_VOLATILE; + block->Append(sins); +#endif + return ExValue(vdl.mType, vdl.mTemp); + } + break; + + case EX_PREFIX: + { + vl = TranslateExpression(procType, proc, block, exp->mLeft, destack, gotos, breakBlock, continueBlock, inlineMapper); + vl = ToValue(proc, exp, block, inlineMapper, vl); + + InterInstruction * ins = new InterInstruction(MapLocation(exp, inlineMapper), IC_UNARY_OPERATOR); + + switch (exp->mToken) + { + case TK_ADD: + vl = Dereference(proc, exp, block, inlineMapper, vl); + if (!vl.mType->IsNumericType()) + mErrors->Error(exp->mLocation, EERR_INCOMPATIBLE_OPERATOR, "Not a numeric type"); + else if (vl.mType->mType == DT_TYPE_INTEGER && vl.mType->mSize < 2) + vl = CoerceType(proc, exp, block, inlineMapper, vl, TheSignedIntTypeDeclaration); + ins->mCode = IC_LOAD_TEMPORARY; + break; + case TK_SUB: + vl = Dereference(proc, exp, block, inlineMapper, vl); + if (!vl.mType->IsNumericType()) + mErrors->Error(exp->mLocation, EERR_INCOMPATIBLE_OPERATOR, "Not a numeric type"); + else if (vl.mType->mType == DT_TYPE_INTEGER && vl.mType->mSize < 2) + vl = CoerceType(proc, exp, block, inlineMapper, vl, TheSignedIntTypeDeclaration); + ins->mOperator = IA_NEG; + break; + case TK_BINARY_NOT: + vl = Dereference(proc, exp, block, inlineMapper, vl); + if (!(vl.mType->mType == DT_TYPE_POINTER || vl.mType->IsNumericType())) + mErrors->Error(exp->mLocation, EERR_INCOMPATIBLE_OPERATOR, "Not a numeric or pointer type"); + else if (vl.mType->mType == DT_TYPE_INTEGER && vl.mType->mSize < 2) + vl = CoerceType(proc, exp, block, inlineMapper, vl, TheSignedIntTypeDeclaration); + ins->mOperator = IA_NOT; + break; + case TK_MUL: + if (vl.mType->mType == DT_TYPE_ARRAY) + vl = Dereference(proc, exp, block, inlineMapper, vl, 1); + else if (vl.mType->mType != DT_TYPE_POINTER) + mErrors->Error(exp->mLocation, EERR_INCOMPATIBLE_OPERATOR, "Not a pointer type"); + else if (vl.mType->mStride != 1) + vl = Dereference(proc, exp, block, inlineMapper, vl, 0); + else + return ExValue(vl.mType->mBase, vl.mTemp, vl.mReference + 1); + return ExValue(vl.mType->mBase, vl.mTemp, 1); + case TK_BINARY_AND: + { + if (vl.mReference < 1 || vl.mBits) + mErrors->Error(exp->mLocation, EERR_NOT_AN_LVALUE, "Not an addressable value"); + + Declaration* dec = new Declaration(exp->mLocation, DT_TYPE_POINTER); + dec->mBase = vl.mType; + dec->mSize = 2; + dec->mFlags = DTF_DEFINED; + return ExValue(exp->mDecType, vl.mTemp, vl.mReference - 1); + } + case TK_BANKOF: + { + if (exp->mLeft->mDecValue) + { + LinkerSection* section = nullptr; + + if (exp->mLeft->mDecValue->mSection) + { + section = exp->mLeft->mDecValue->mSection; + } + else if (exp->mLeft->mDecValue->mType == DT_CONST_INTEGER) + { + section = proc->mLinkerObject->mSection; + } + + if (section) + { + LinkerRegion* rgn = mLinker->FindRegionOfSection(section); + if (rgn) + { + uint64 i = 0; + while (i < 64 && rgn->mCartridgeBanks != (1ULL << i)) + i++; + if (i == 64) + i = 255; + + ins->mCode = IC_CONSTANT; + ins->mNumOperands = 0; + ins->mConst.mType = IT_INT16; + ins->mConst.mIntConst = i; + ins->mDst.mType = IT_INT16; + ins->mDst.mTemp = proc->AddTemporary(ins->mDst.mType); + block->Append(ins); + return ExValue(TheSignedIntTypeDeclaration, ins->mDst.mTemp, vl.mReference - 1); + } + else + mErrors->Error(exp->mLocation, EERR_SECTION_ON_IN_REGION, "Section not in single banked region", section->mIdent); + } + } + mErrors->Error(exp->mLocation, ERRR_CANNOT_FIND_BANK_OF_EXPRESSION, "Cannot find bank of expression"); + } break; + + case TK_SIZEOF: + { + ins->mCode = IC_CONSTANT; + ins->mNumOperands = 0; + ins->mConst.mType = IT_INT16; + ins->mConst.mIntConst = exp->mLeft->mDecType->mSize; + ins->mDst.mType = IT_INT16; + ins->mDst.mTemp = proc->AddTemporary(ins->mDst.mType); + block->Append(ins); + return ExValue(TheUnsignedIntTypeDeclaration, ins->mDst.mTemp, vl.mReference - 1); + } + + case TK_NEW: + { + ins->mCode = IC_MALLOC; + ins->mSrc[0].mType = InterTypeOf(vl.mType); + ins->mSrc[0].mTemp = vl.mTemp; + ins->mDst.mType = IT_POINTER; + ins->mDst.mTemp = proc->AddTemporary(ins->mDst.mType); + ins->mDst.mMemory = IM_INDIRECT; + ins->mDst.mRestricted = proc->AddRestricted(); + block->Append(ins); + return ExValue(exp->mDecType, ins->mDst.mTemp, 0); + + } break; + + case TK_DELETE: + { + vl = Dereference(proc, exp, block, inlineMapper, vl, 0); + + ins->mCode = IC_FREE; + ins->mSrc[0].mType = IT_POINTER; + ins->mSrc[0].mTemp = vl.mTemp; + block->Append(ins); + return ExValue(TheConstVoidTypeDeclaration, -1); + + } break; + } + + ins->mSrc[0].mType = InterTypeOf(vl.mType); + ins->mSrc[0].mTemp = vl.mTemp; + ins->mDst.mType = ins->mSrc[0].mType; + ins->mDst.mTemp = proc->AddTemporary(ins->mDst.mType); + + block->Append(ins); + + return ExValue(vl.mType, ins->mDst.mTemp); + } + + case EX_RELATIONAL: + { + vl = TranslateExpression(procType, proc, block, exp->mLeft, destack, gotos, breakBlock, continueBlock, inlineMapper); + vl = ToValue(proc, exp, block, inlineMapper, vl); + vl = Dereference(proc, exp, block, inlineMapper, vl, vl.mType->mType == DT_TYPE_ARRAY ? 1 : 0); + vr = TranslateExpression(procType, proc, block, exp->mRight, destack, gotos, breakBlock, continueBlock, inlineMapper); + vr = ToValue(proc, exp, block, inlineMapper, vr); + vr = Dereference(proc, exp, block, inlineMapper, vr, vr.mType->mType == DT_TYPE_ARRAY ? 1 : 0); + + + InterInstruction * ins = new InterInstruction(MapLocation(exp, inlineMapper), IC_RELATIONAL_OPERATOR); + + Declaration* dtype = TheSignedIntTypeDeclaration; + + if (vl.mType->mType == DT_TYPE_POINTER || vr.mType->mType == DT_TYPE_POINTER) + { + dtype = vl.mType; + if (vl.mType->IsIntegerType()) + { + if ((mCompilerOptions & COPT_CPLUSPLUS) || exp->mLeft->mType != EX_CONSTANT || exp->mLeft->mDecValue->mInteger != 0) + mErrors->Error(exp->mLocation, EERR_INCOMPATIBLE_TYPES, "Cannot compare integer and pointer"); + } + else if (vr.mType->IsIntegerType()) + { + if ((mCompilerOptions & COPT_CPLUSPLUS) || exp->mRight->mType != EX_CONSTANT || exp->mRight->mDecValue->mInteger != 0) + mErrors->Error(exp->mLocation, EERR_INCOMPATIBLE_TYPES, "Cannot compare integer and pointer"); + } + else if ((vl.mType->mType == DT_TYPE_POINTER || vl.mType->mType == DT_TYPE_ARRAY) && (vr.mType->mType == DT_TYPE_POINTER || vr.mType->mType == DT_TYPE_ARRAY)) + { + if (vl.mType->mBase->mType == DT_TYPE_VOID || vr.mType->mBase->mType == DT_TYPE_VOID) + ; + else if ((mCompilerOptions & COPT_CPLUSPLUS) && (vl.mType->mBase->IsSubType(vr.mType->mBase) || vr.mType->mBase->IsSubType(vl.mType->mBase))) + ; + else if (!vl.mType->mBase->IsConstRefSame(vr.mType->mBase)) + mErrors->Error(exp->mLocation, EERR_INCOMPATIBLE_OPERATOR, "Incompatible pointer types", vl.mType->mBase->MangleIdent(), vr.mType->mBase->MangleIdent()); + } + else + mErrors->Error(exp->mLocation, EERR_INCOMPATIBLE_OPERATOR, "Incompatible pointer types"); + } + else if (!vl.mType->IsNumericType() || !vr.mType->IsNumericType()) + mErrors->Error(exp->mLocation, EERR_INCOMPATIBLE_OPERATOR, "Not a numeric or pointer type"); + else if (vr.mType->mType == DT_TYPE_FLOAT || vl.mType->mType == DT_TYPE_FLOAT) + dtype = TheFloatTypeDeclaration; + else if (vr.mType->mSize < vl.mType->mSize && (vl.mType->mFlags & DTF_SIGNED)) + { + if (vl.mType->mSize == 4) + dtype = TheSignedLongTypeDeclaration; + else + dtype = TheSignedIntTypeDeclaration; + } + else if (vl.mType->mSize < vr.mType->mSize && (vr.mType->mFlags & DTF_SIGNED)) + { + if (vr.mType->mSize == 4) + dtype = TheSignedLongTypeDeclaration; + else + dtype = TheSignedIntTypeDeclaration; + } + else if ((vr.mType->mFlags & DTF_SIGNED) && (vl.mType->mFlags & DTF_SIGNED)) + { + if (vl.mType->mSize == 4) + dtype = TheSignedLongTypeDeclaration; + else + dtype = TheSignedIntTypeDeclaration; + } + else if (vl.mType->mSize == 1 && vr.mType->mSize == 1 && ((vr.mType->mFlags & DTF_SIGNED) || (vl.mType->mFlags & DTF_SIGNED))) + dtype = TheSignedIntTypeDeclaration; + else + { + if (vl.mType->mSize == 4 || vr.mType->mSize == 4) + dtype = TheUnsignedLongTypeDeclaration; + else + dtype = TheUnsignedIntTypeDeclaration; + } + + vl = CoerceType(proc, exp, block, inlineMapper, vl, dtype); + vr = CoerceType(proc, exp, block, inlineMapper, vr, dtype); + + bool signedCompare = dtype->mFlags & DTF_SIGNED; + + switch (exp->mToken) + { + case TK_EQUAL: + ins->mOperator = IA_CMPEQ; + break; + case TK_NOT_EQUAL: + ins->mOperator = IA_CMPNE; + break; + case TK_GREATER_THAN: + ins->mOperator = signedCompare ? IA_CMPGS : IA_CMPGU; + break; + case TK_GREATER_EQUAL: + ins->mOperator = signedCompare ? IA_CMPGES : IA_CMPGEU; + break; + case TK_LESS_THAN: + ins->mOperator = signedCompare ? IA_CMPLS : IA_CMPLU; + break; + case TK_LESS_EQUAL: + ins->mOperator = signedCompare ? IA_CMPLES : IA_CMPLEU; + break; + } + ins->mSrc[0].mType = InterTypeOf(vr.mType);; + ins->mSrc[0].mTemp = vr.mTemp; + ins->mSrc[1].mType = InterTypeOf(vl.mType);; + ins->mSrc[1].mTemp = vl.mTemp; + ins->mDst.mType = IT_BOOL; + ins->mDst.mTemp = proc->AddTemporary(ins->mDst.mType); + + block->Append(ins); + + return ExValue(TheBoolTypeDeclaration, ins->mDst.mTemp); + } + + case EX_VCALL: + case EX_CALL: + case EX_INLINE: + { + if (exp->mLeft->mType == EX_CONSTANT && exp->mLeft->mDecValue->mType == DT_CONST_FUNCTION && (exp->mLeft->mDecValue->mFlags & DTF_INTRINSIC)) + { + Declaration* decf = exp->mLeft->mDecValue; + const Ident * iname = decf->mQualIdent; + + if (!strcmp(iname->mString, "fabs")) + { + vr = TranslateExpression(procType, proc, block, exp->mRight, destack, gotos, breakBlock, continueBlock, inlineMapper); + vr = Dereference(proc, exp, block, inlineMapper, vr); + + if (decf->mBase->mParams->CanAssign(vr.mType)) + mErrors->Error(exp->mLocation, EERR_INCOMPATIBLE_TYPES, "Cannot assign incompatible types"); + vr = CoerceType(proc, exp, block, inlineMapper, vr, decf->mBase->mParams); + + InterInstruction * ins = new InterInstruction(MapLocation(exp, inlineMapper), IC_UNARY_OPERATOR); + ins->mOperator = IA_ABS; + ins->mSrc[0].mType = IT_FLOAT; + ins->mSrc[0].mTemp = vr.mTemp; + ins->mDst.mType = IT_FLOAT; + ins->mDst.mTemp = proc->AddTemporary(ins->mDst.mType); + block->Append(ins); + + return ExValue(TheFloatTypeDeclaration, ins->mDst.mTemp); + } + else if (!strcmp(iname->mString, "floor")) + { + vr = TranslateExpression(procType, proc, block, exp->mRight, destack, gotos, breakBlock, continueBlock, inlineMapper); + vr = Dereference(proc, exp, block, inlineMapper, vr); + + if (decf->mBase->mParams->CanAssign(vr.mType)) + mErrors->Error(exp->mLocation, EERR_INCOMPATIBLE_TYPES, "Cannot assign incompatible types"); + vr = CoerceType(proc, exp, block, inlineMapper, vr, decf->mBase->mParams); + + InterInstruction * ins = new InterInstruction(MapLocation(exp, inlineMapper), IC_UNARY_OPERATOR); + ins->mOperator = IA_FLOOR; + ins->mSrc[0].mType = IT_FLOAT; + ins->mSrc[0].mTemp = vr.mTemp; + ins->mDst.mType = IT_FLOAT; + ins->mDst.mTemp = proc->AddTemporary(ins->mDst.mType); + block->Append(ins); + + return ExValue(TheFloatTypeDeclaration, ins->mDst.mTemp); + } + else if (!strcmp(iname->mString, "ceil")) + { + vr = TranslateExpression(procType, proc, block, exp->mRight, destack, gotos, breakBlock, continueBlock, inlineMapper); + vr = Dereference(proc, exp, block, inlineMapper, vr); + + if (decf->mBase->mParams->CanAssign(vr.mType)) + mErrors->Error(exp->mLocation, EERR_INCOMPATIBLE_TYPES, "Cannot assign incompatible types"); + vr = CoerceType(proc, exp, block, inlineMapper, vr, decf->mBase->mParams); + + InterInstruction * ins = new InterInstruction(MapLocation(exp, inlineMapper), IC_UNARY_OPERATOR); + ins->mOperator = IA_CEIL; + ins->mSrc[0].mType = IT_FLOAT; + ins->mSrc[0].mTemp = vr.mTemp; + ins->mDst.mType = IT_FLOAT; + ins->mDst.mTemp = proc->AddTemporary(ins->mDst.mType); + block->Append(ins); + + return ExValue(TheFloatTypeDeclaration, ins->mDst.mTemp); + } + else if (!strcmp(iname->mString, "sin")) + { + } + else if (!strcmp(iname->mString, "cos")) + { + } + else if (!strcmp(iname->mString, "tan")) + { + } + else if (!strcmp(iname->mString, "log")) + { + } + else if (!strcmp(iname->mString, "exp")) + { + } + else if (!strcmp(iname->mString, "sqrt")) + { + } + else if (!strcmp(iname->mString, "atan")) + { + } + else if (!strcmp(iname->mString, "pow")) + { + } + else if (!strcmp(iname->mString, "atan2")) + { + } + else if (!strcmp(iname->mString, "breakpoint")) + { + InterInstruction* ins = new InterInstruction(MapLocation(exp, inlineMapper), IC_BREAKPOINT); + ins->mNumOperands = 0; + block->Append(ins); + + return ExValue(TheVoidTypeDeclaration, 0); + } + else if (!strcmp(iname->mString, "malloc")) + { + vr = TranslateExpression(procType, proc, block, exp->mRight, destack, gotos, breakBlock, continueBlock, inlineMapper); + vr = Dereference(proc, exp, block, inlineMapper, vr); + + if (decf->mBase->mParams->CanAssign(vr.mType)) + mErrors->Error(exp->mLocation, EERR_INCOMPATIBLE_TYPES, "Cannot assign incompatible types"); + vr = CoerceType(proc, exp, block, inlineMapper, vr, decf->mBase->mParams); + + InterInstruction* ins = new InterInstruction(MapLocation(exp, inlineMapper), IC_MALLOC); + ins->mSrc[0].mType = IT_INT16; + ins->mSrc[0].mTemp = vr.mTemp; + ins->mNumOperands = 1; + ins->mDst.mType = IT_POINTER; + ins->mDst.mMemory = IM_INDIRECT; + ins->mDst.mTemp = proc->AddTemporary(ins->mDst.mType); + ins->mDst.mRestricted = proc->AddRestricted(); + block->Append(ins); + + return ExValue(TheVoidPointerTypeDeclaration, ins->mDst.mTemp); + } + else if (!strcmp(iname->mString, "free")) + { + vr = TranslateExpression(procType, proc, block, exp->mRight, destack, gotos, breakBlock, continueBlock, inlineMapper); + vr = Dereference(proc, exp, block, inlineMapper, vr); + + if (decf->mBase->mParams->CanAssign(vr.mType)) + mErrors->Error(exp->mLocation, EERR_INCOMPATIBLE_TYPES, "Cannot assign incompatible types"); + vr = CoerceType(proc, exp, block, inlineMapper, vr, decf->mBase->mParams); + + InterInstruction* ins = new InterInstruction(MapLocation(exp, inlineMapper), IC_FREE); + ins->mSrc[0].mType = IT_POINTER; + ins->mSrc[0].mTemp = vr.mTemp; + ins->mNumOperands = 1; + block->Append(ins); + + return ExValue(TheVoidTypeDeclaration, 0); + } + else if (!strcmp(iname->mString, "strcpy")) + { + if (exp->mRight->mType == EX_LIST) + { + Expression* tex = exp->mRight->mLeft, * sex = exp->mRight->mRight; + if ((tex->mDecType->mType == DT_TYPE_ARRAY && tex->mDecType->mSize <= 256) || + (sex->mDecType->mType == DT_TYPE_ARRAY && sex->mDecType->mSize <= 256)) + { + vl = TranslateExpression(procType, proc, block, tex, destack, gotos, breakBlock, continueBlock, inlineMapper); + if (vl.mType->mType == DT_TYPE_ARRAY) + vl = Dereference(proc, exp, block, inlineMapper, vl, 1); + else + vl = Dereference(proc, exp, block, inlineMapper, vl); + + vr = TranslateExpression(procType, proc, block, sex, destack, gotos, breakBlock, continueBlock, inlineMapper); + if (vr.mType->mType == DT_TYPE_ARRAY) + vr = Dereference(proc, exp, block, inlineMapper, vr, 1); + else + vr = Dereference(proc, exp, block, inlineMapper, vr); + + if (!TheCharPointerTypeDeclaration->CanAssign(vl.mType)) + mErrors->Error(tex->mLocation, EERR_INCOMPATIBLE_TYPES, "Cannot assign incompatible types"); + if (!TheConstCharPointerTypeDeclaration->CanAssign(vr.mType)) + mErrors->Error(sex->mLocation, EERR_INCOMPATIBLE_TYPES, "Cannot assign incompatible types"); + + InterInstruction* ins = new InterInstruction(MapLocation(exp, inlineMapper), IC_STRCPY); + ins->mNumOperands = 2; + + ins->mSrc[0].mType = IT_POINTER; + ins->mSrc[0].mMemory = IM_INDIRECT; + ins->mSrc[0].mTemp = vr.mTemp; + ins->mSrc[1].mType = IT_POINTER; + ins->mSrc[1].mMemory = IM_INDIRECT; + ins->mSrc[1].mTemp = vl.mTemp; + block->Append(ins); + + return vl; + } + } + } + else if (!strcmp(iname->mString, "memcpy")) + { + if (exp->mRight->mType == EX_LIST) + { + Expression* tex = exp->mRight->mLeft, * sex = exp->mRight->mRight->mLeft, * nex = exp->mRight->mRight->mRight; + if (nex && nex->mType == EX_CONSTANT && nex->mDecValue->mType == DT_CONST_INTEGER && nex->mDecValue->mInteger < 512) + { + vl = TranslateExpression(procType, proc, block, tex, destack, gotos, breakBlock, continueBlock, inlineMapper); + if (vl.mType->mType == DT_TYPE_ARRAY) + vl = Dereference(proc, exp, block, inlineMapper, vl, 1); + else + vl = Dereference(proc, exp, block, inlineMapper, vl); + + vr = TranslateExpression(procType, proc, block, sex, destack, gotos, breakBlock, continueBlock, inlineMapper); + if (vr.mType->mType == DT_TYPE_ARRAY) + vr = Dereference(proc, exp, block, inlineMapper, vr, 1); + else + vr = Dereference(proc, exp, block, inlineMapper, vr); + + if (!TheVoidPointerTypeDeclaration->CanAssign(vl.mType)) + mErrors->Error(tex->mLocation, EERR_INCOMPATIBLE_TYPES, "Cannot assign incompatible types"); + if (!TheConstVoidPointerTypeDeclaration->CanAssign(vr.mType)) + mErrors->Error(sex->mLocation, EERR_INCOMPATIBLE_TYPES, "Cannot assign incompatible types"); + + InterInstruction* ins = new InterInstruction(MapLocation(exp, inlineMapper), IC_COPY); + ins->mNumOperands = 2; + + ins->mSrc[0].mType = IT_POINTER; + ins->mSrc[0].mMemory = IM_INDIRECT; + ins->mSrc[0].mTemp = vr.mTemp; + ins->mSrc[1].mType = IT_POINTER; + ins->mSrc[1].mMemory = IM_INDIRECT; + ins->mSrc[1].mTemp = vl.mTemp; + ins->mSrc[0].mOperandSize = int(nex->mDecValue->mInteger); + ins->mSrc[1].mOperandSize = int(nex->mDecValue->mInteger); + ins->mConst.mOperandSize = int(nex->mDecValue->mInteger); + if (vl.mType->mBase && vr.mType->mBase) + { + if ((vl.mType->mBase->mFlags & DTF_VOLATILE) || (vr.mType->mBase->mFlags & DTF_VOLATILE)) + ins->mVolatile = true; + } + block->Append(ins); + + return vl; + } + } + } + else if (!strcmp(iname->mString, "memset")) + { + if (exp->mRight->mType == EX_LIST) + { + Expression* tex = exp->mRight->mLeft, * sex = exp->mRight->mRight->mLeft, * nex = exp->mRight->mRight->mRight; + if (nex && nex->mType == EX_CONSTANT && nex->mDecValue->mType == DT_CONST_INTEGER && nex->mDecValue->mInteger <= 1024) + { + vl = TranslateExpression(procType, proc, block, tex, destack, gotos, breakBlock, continueBlock, inlineMapper); + if (vl.mType->mType == DT_TYPE_ARRAY) + vl = Dereference(proc, exp, block, inlineMapper, vl, 1); + else + vl = Dereference(proc, exp, block, inlineMapper, vl); + + vr = TranslateExpression(procType, proc, block, sex, destack, gotos, breakBlock, continueBlock, inlineMapper); + vr = Dereference(proc, exp, block, inlineMapper, vr); + + if (!TheVoidPointerTypeDeclaration->CanAssign(vl.mType)) + mErrors->Error(tex->mLocation, EERR_INCOMPATIBLE_TYPES, "Cannot assign incompatible types"); + if (!TheConstCharTypeDeclaration->CanAssign(vr.mType)) + mErrors->Error(sex->mLocation, EERR_INCOMPATIBLE_TYPES, "Cannot assign incompatible types"); + + InterInstruction* ins = new InterInstruction(MapLocation(exp, inlineMapper), IC_FILL); + ins->mNumOperands = 2; + + ins->mSrc[0].mType = IT_INT8; + ins->mSrc[0].mTemp = vr.mTemp; + ins->mSrc[1].mType = IT_POINTER; + ins->mSrc[1].mMemory = IM_INDIRECT; + ins->mSrc[1].mTemp = vl.mTemp; + ins->mSrc[0].mOperandSize = 1; + ins->mSrc[1].mOperandSize = int(nex->mDecValue->mInteger); + ins->mConst.mOperandSize = int(nex->mDecValue->mInteger); + block->Append(ins); + + return vl; + } + } + } + else if (!strcmp(iname->mString, "memclr")) + { + if (exp->mRight->mType == EX_LIST) + { + Expression* tex = exp->mRight->mLeft, * nex = exp->mRight->mRight; + if (nex && nex->mType == EX_CONSTANT && nex->mDecValue->mType == DT_CONST_INTEGER && nex->mDecValue->mInteger <= 1024) + { + vl = TranslateExpression(procType, proc, block, tex, destack, gotos, breakBlock, continueBlock, inlineMapper); + if (vl.mType->mType == DT_TYPE_ARRAY) + vl = Dereference(proc, exp, block, inlineMapper, vl, 1); + else + vl = Dereference(proc, exp, block, inlineMapper, vl); + + if (!TheVoidPointerTypeDeclaration->CanAssign(vl.mType)) + mErrors->Error(tex->mLocation, EERR_INCOMPATIBLE_TYPES, "Cannot assign incompatible types"); + + InterInstruction * zins = new InterInstruction(MapLocation(exp, inlineMapper), IC_CONSTANT); + zins->mDst.mType = IT_INT8; + zins->mDst.mTemp = proc->AddTemporary(zins->mDst.mType); + zins->mConst.mType = IT_INT8; + zins->mConst.mIntConst = 0; + block->Append(zins); + + InterInstruction* ins = new InterInstruction(MapLocation(exp, inlineMapper), IC_FILL); + ins->mNumOperands = 2; + + ins->mSrc[0].mType = IT_INT8; + ins->mSrc[0].mTemp = zins->mDst.mTemp; + ins->mSrc[1].mType = IT_POINTER; + ins->mSrc[1].mMemory = IM_INDIRECT; + ins->mSrc[1].mTemp = vl.mTemp; + ins->mSrc[0].mOperandSize = 1; + ins->mSrc[1].mOperandSize = int(nex->mDecValue->mInteger); + ins->mConst.mOperandSize = int(nex->mDecValue->mInteger); + block->Append(ins); + + return vl; + } + } + } + else + { + mErrors->Error(exp->mLeft->mDecValue->mLocation, EERR_OBJECT_NOT_FOUND, "Unknown intrinsic function", iname); + return ExValue(TheVoidTypeDeclaration); + } + } + + bool canInline = exp->mLeft->mType == EX_CONSTANT && + exp->mLeft->mDecValue->mType == DT_CONST_FUNCTION && + ((mCompilerOptions & COPT_OPTIMIZE_INLINE) || (exp->mLeft->mDecValue->mFlags & DTF_FORCE_INLINE)) && + !(inlineMapper && inlineMapper->mDepth > 10) && + !(exp->mLeft->mDecValue->mFlags & DTF_PREVENT_INLINE) && + exp->mType != EX_VCALL; + bool doInline = false, inlineConstexpr = false; + + if (canInline) + { + if (inlineMapper && inlineMapper->mConstExpr) + inlineConstexpr = true; + else if (exp->mLeft->mDecValue->mFlags & DTF_FUNC_CONSTEXPR) + { + Expression* pex = exp->mRight; + inlineConstexpr = true; + while (inlineConstexpr && pex) + { + if (pex->mType == EX_LIST) + { + if (pex->mLeft->mType != EX_CONSTANT) + inlineConstexpr = false; + pex = pex->mRight; + } + else + { + if (pex->mType != EX_CONSTANT) + inlineConstexpr = false; + pex = nullptr; + } + } + } + + if (exp->mType == EX_INLINE) + doInline = true; + else if (inlineConstexpr) + doInline = true; + else if (exp->mLeft->mDecValue->mFlags & DTF_INLINE) + { + if ((exp->mLeft->mDecValue->mFlags & DTF_REQUEST_INLINE) || (mCompilerOptions & COPT_OPTIMIZE_AUTO_INLINE)) + { + if (proc->mNativeProcedure || !(exp->mLeft->mDecValue->mFlags & DTF_NATIVE)) + doInline = true; + } + } + } + + if (doInline) + { + return TranslateInline(procType, proc, block, exp, destack, breakBlock, continueBlock, inlineMapper, inlineConstexpr, lrexp); + } + else + { + Expression* funcexp = exp->mLeft; + + if (funcexp->mType == EX_CONSTANT && (funcexp->mDecValue->mFlags & DTF_FORCE_INLINE)) + mErrors->Error(exp->mLocation, EWARN_FUNCTION_NOT_INLINED, "Function with __forceinline not inlined", funcexp->mDecValue->mQualIdent); + + vl = TranslateExpression(procType, proc, block, funcexp, destack, gotos, breakBlock, continueBlock, inlineMapper); + + vl = Dereference(proc, exp, block, inlineMapper, vl); + + int atotal = 0; + + InterCodeBasicBlock* fblock = block; + + Declaration* ftype = vl.mType; + if (ftype->mType == DT_TYPE_POINTER) + ftype = ftype->mBase; + + InterInstruction* fins = nullptr; + + if (ftype->mFlags & DTF_FUNC_DELETED) + { + mErrors->Error(exp->mLocation, ERRR_CALLING_DELETED_FUNCTION, "Calling deleted function", ftype->mIdent); + } + + + if (ftype->mFlags & DTF_FASTCALL) + { + + } + else + { + fins = new InterInstruction(MapLocation(exp, inlineMapper), IC_PUSH_FRAME); + fins->mNumOperands = 0; + fins->mConst.mIntConst = atotal; + block->Append(fins); + } + + Declaration * decResult = nullptr; + GrowingArray defins(nullptr); + + if (ftype->mBase->IsComplexStruct()) + { + int ttemp; + if (lrexp && lrexp->mType->mStripe <= 1) + { + if (!ftype->mBase->CanAssign(lrexp->mType)) + mErrors->Error(exp->mLocation, EERR_INCOMPATIBLE_TYPES, "Cannot assign incompatible types", ftype->mBase->MangleIdent(), lrexp->mType->MangleIdent()); + + ttemp = lrexp->mTemp; + + decResult = lrexp->mType; + } + else + { + int nindex = proc->mNumLocals++; + + Declaration* vdec = new Declaration(exp->mLocation, DT_VARIABLE); + + vdec->mVarIndex = nindex; + vdec->mBase = ftype->mBase; + vdec->mSize = ftype->mBase->mSize; + + decResult = vdec; + + InterInstruction* vins = new InterInstruction(MapLocation(exp, inlineMapper), IC_CONSTANT); + vins->mDst.mType = IT_POINTER; + vins->mDst.mTemp = proc->AddTemporary(IT_POINTER); + vins->mConst.mType = IT_POINTER; + vins->mConst.mMemory = IM_LOCAL; + vins->mConst.mVarIndex = nindex; + vins->mConst.mOperandSize = ftype->mBase->mSize; + block->Append(vins); + + ttemp = vins->mDst.mTemp; + lrexp = nullptr; + } + + InterInstruction* ains = new InterInstruction(MapLocation(exp, inlineMapper), IC_CONSTANT); + ains->mDst.mType = IT_POINTER; + ains->mDst.mTemp = proc->AddTemporary(IT_POINTER); + ains->mConst.mType = IT_POINTER; + ains->mConst.mVarIndex = 0; + ains->mConst.mIntConst = 0; + ains->mConst.mOperandSize = 2; + if (ftype->mFlags & DTF_FASTCALL) + { + ains->mConst.mMemory = IM_FFRAME; + if (ftype->mFastCallBase + 2 <= BC_REG_FPARAMS_END - BC_REG_FPARAMS) + ains->mConst.mVarIndex += ftype->mFastCallBase; + else + ains->mConst.mVarIndex += BC_REG_FPARAMS_END - BC_REG_FPARAMS + ftype->mFastCallBase2; + } + else + ains->mConst.mMemory = IM_FRAME; + block->Append(ains); + + InterInstruction* wins = new InterInstruction(MapLocation(exp, inlineMapper), IC_STORE); + wins->mSrc[1].mMemory = IM_INDIRECT; + wins->mSrc[0].mType = IT_POINTER; + wins->mSrc[0].mTemp = ttemp; + wins->mSrc[1].mType = IT_POINTER; + wins->mSrc[1].mTemp = ains->mDst.mTemp; + wins->mSrc[1].mOperandSize = 2; + + if (ftype->mFlags & DTF_FASTCALL) + defins.Push(wins); + else + block->Append(wins); + + atotal = 2; + } + + if (funcexp->mDecValue && funcexp->mDecValue->mType == DT_CONST_FUNCTION) + proc->AddCalledFunction(proc->mModule->mProcedures[funcexp->mDecValue->mVarIndex]); + else + proc->CallsFunctionPointer(); + + Declaration* pdec = ftype->mParams; + Expression* pex = exp->mRight; + while (pex) + { + InterInstruction * ains = new InterInstruction(MapLocation(exp, inlineMapper), IC_CONSTANT); + ains->mConst.mType = IT_POINTER; + + ains->mDst.mType = IT_POINTER; + ains->mDst.mTemp = proc->AddTemporary(ains->mDst.mType); + if (pdec) + { + ains->mConst.mVarIndex = pdec->mVarIndex; + ains->mConst.mIntConst = pdec->mOffset; + if (pdec->mBase->mType == DT_TYPE_ARRAY) + ains->mConst.mOperandSize = 2; + else + ains->mConst.mOperandSize = pdec->mSize; + } + else if (ftype->mFlags & DTF_VARIADIC) + { + ains->mConst.mVarIndex = atotal; + ains->mConst.mIntConst = 0; + ains->mConst.mOperandSize = 2; + } + else + { + mErrors->Error(pex->mLocation, EERR_WRONG_PARAMETER, "Too many arguments for function call"); + } + + if (ftype->mFlags & DTF_FASTCALL) + { + ains->mConst.mMemory = IM_FFRAME; + ains->mConst.mIntConst = 0; +// ains->mConst.mVarIndex += ftype->mFastCallBase; + } + else + ains->mConst.mMemory = IM_FRAME; + + block->Append(ains); + + Expression* texp; + + if (pex->mType == EX_LIST) + { + texp = pex->mLeft; + pex = pex->mRight; + } + else + { + texp = pex; + pex = nullptr; + } + + Declaration* ptype = pdec ? pdec->mBase : texp->mDecType; + + ExValue vp(ptype ? ptype : TheSignedIntTypeDeclaration, ains->mDst.mTemp, 1); + + if (pdec && pdec->mBase->IsReference() && texp->mType == EX_CALL && !texp->mDecType->IsReference()) + { + mErrors->Error(texp->mLocation, EERR_MISSING_TEMP, "Missing temporary variable"); +#if 0 + int nindex = proc->mNumLocals++; + + Declaration* vdec = new Declaration(exp->mLocation, DT_VARIABLE); + + vdec->mVarIndex = nindex; + vdec->mBase = pdec->mBase->mBase; + vdec->mSize = pdec->mBase->mBase->mSize; + + InterInstruction* vins = new InterInstruction(MapLocation(exp, inlineMapper), IC_CONSTANT); + vins->mDst.mType = IT_POINTER; + vins->mDst.mTemp = proc->AddTemporary(IT_POINTER); + vins->mConst.mMemory = IM_LOCAL; + vins->mConst.mVarIndex = nindex; + vins->mConst.mOperandSize = vdec->mSize; + block->Append(vins); + + vp.mType = pdec->mBase->mBase; + vp.mTemp = vins->mDst.mTemp; +#endif + } + + if (ptype && (ptype->mType == DT_TYPE_STRUCT || ptype->mType == DT_TYPE_UNION)) + vr = TranslateExpression(procType, proc, block, texp, destack, gotos, breakBlock, continueBlock, inlineMapper, &vp); + else + vr = TranslateExpression(procType, proc, block, texp, destack, gotos, breakBlock, continueBlock, inlineMapper, nullptr); + + if (!(pdec && pdec->mBase->IsReference()) && (vr.mType->mType == DT_TYPE_STRUCT || vr.mType->mType == DT_TYPE_UNION)) + { + if (pdec && !pdec->mBase->CanAssign(vr.mType)) + mErrors->Error(texp->mLocation, EERR_INCOMPATIBLE_TYPES, "Cannot assign incompatible types", pdec->mBase->MangleIdent(), vr.mType->MangleIdent()); + + vr = Dereference(proc, texp, block, inlineMapper, vr, 1); + + if (vr.mReference != 1) + mErrors->Error(exp->mLeft->mLocation, EERR_NOT_AN_LVALUE, "Not an addressable expression"); + + if (vp.mTemp != vr.mTemp) + { + if (vr.mType->mDestructor || vr.mType->mCopyConstructor) + CopyStruct(proc, exp, block, vp, vr, inlineMapper, false); + else + { + InterInstruction* cins = CopyStructSimple(proc, exp, block, inlineMapper, vp, vr); + if (ftype->mFlags & DTF_FASTCALL) + defins.Push(cins); + else + block->Append(cins); + } + +#if 0 + InterInstruction* cins = new InterInstruction(texp->mLocation, IC_COPY); + cins->mSrc[0].mType = IT_POINTER; + cins->mSrc[0].mTemp = vr.mTemp; + cins->mSrc[0].mMemory = IM_INDIRECT; + cins->mSrc[0].mOperandSize = vp.mType->mSize; + cins->mSrc[0].mStride = vr.mType->mStripe; + + cins->mSrc[1].mOperandSize = vp.mType->mSize; + cins->mSrc[1].mType = IT_POINTER; + cins->mSrc[1].mTemp = ains->mDst.mTemp; + cins->mSrc[1].mMemory = IM_INDIRECT; + + cins->mConst.mOperandSize = vp.mType->mSize; + block->Append(cins); +#endif + } + + atotal += vr.mType->mSize; + } + else + { + if (vr.mType->mType == DT_TYPE_ARRAY) + { + if (pdec && pdec->mBase->IsReference()) + mErrors->Error(texp->mLocation, EERR_INCOMPATIBLE_TYPES, "Cannot assign incompatible types", pdec->mBase->MangleIdent(), vr.mType->MangleIdent()); + vr = Dereference(proc, texp, block, inlineMapper, vr, 1); + } + else if (pdec && pdec->mBase->mType == DT_TYPE_POINTER && vr.mType->mType == DT_TYPE_INTEGER && texp->mType == EX_CONSTANT && texp->mDecValue->mType == DT_CONST_INTEGER && texp->mDecValue->mInteger == 0) + { + mErrors->Error(texp->mLocation, EWARN_NUMERIC_0_USED_AS_NULLPTR, "Numeric 0 used for nullptr"); + vr = CoerceType(proc, texp, block, inlineMapper, vr, pdec->mBase); + } + else if (pdec && (pdec->mBase->IsReference() && !vr.mType->IsReference())) + { + vr = Dereference(proc, texp, block, inlineMapper, vr, 1); + } + else if (vr.mType->IsReference() && !(pdec && pdec->mBase->IsReference())) + { + vr.mReference++; + vr.mType = vr.mType->mBase; + vr = Dereference(proc, texp, block, inlineMapper, vr); + } + else + vr = Dereference(proc, texp, block, inlineMapper, vr); + + if (pdec) + { + if (!(pdec->mFlags & DTF_FPARAM_UNUSED) && !pdec->mBase->CanAssign(vr.mType)) + { + pdec->mBase->CanAssign(vr.mType); + mErrors->Error(texp->mLocation, EERR_INCOMPATIBLE_TYPES, "Cannot assign incompatible types", pdec->mBase->MangleIdent(), vr.mType->MangleIdent()); + } + vr = CoerceType(proc, texp, block, inlineMapper, vr, pdec->mBase); + } + else if (vr.mType->IsIntegerType() && vr.mType->mSize < 2) + { + vr = CoerceType(proc, texp, block, inlineMapper, vr, TheSignedIntTypeDeclaration); + } + + + InterInstruction* wins = new InterInstruction(MapLocation(exp, inlineMapper), IC_STORE); + wins->mSrc[1].mMemory = IM_INDIRECT; + wins->mSrc[0].mType = InterTypeOf(vr.mType);; + wins->mSrc[0].mTemp = vr.mTemp; + wins->mSrc[1].mType = IT_POINTER; + wins->mSrc[1].mTemp = ains->mDst.mTemp; + if (pdec) + { + if (pdec->mBase->mType == DT_TYPE_ARRAY || pdec->mBase->mType == DT_TYPE_REFERENCE || pdec->mBase->mType == DT_TYPE_RVALUEREF || pdec->mBase->mType == DT_TYPE_FUNCTION) + { + wins->mSrc[1].mOperandSize = 2; + wins->mSrc[0].mType = IT_POINTER; + } + else + wins->mSrc[1].mOperandSize = pdec->mSize; + } + else if (vr.mType->mSize > 2 && vr.mType->mType != DT_TYPE_ARRAY) + wins->mSrc[1].mOperandSize = vr.mType->mSize; + else + wins->mSrc[1].mOperandSize = 2; + + if (!pdec || !(pdec->mFlags & DTF_FPARAM_UNUSED)) + { + if (ftype->mFlags & DTF_FASTCALL) + defins.Push(wins); + else + block->Append(wins); + + if (pdec && (ftype->mFlags & DTF_FASTCALL)) + atotal = pdec->mVarIndex; + atotal += wins->mSrc[1].mOperandSize; + } + else if (!(ftype->mFlags & DTF_FASTCALL)) + atotal += wins->mSrc[1].mOperandSize; + } + + if (pdec) + pdec = pdec->mNext; + } + + if (pdec) + mErrors->Error(exp->mLocation, EERR_WRONG_PARAMETER, "Not enough arguments for function call"); + + for (int i = 0; i < defins.Size(); i++) + block->Append(defins[i]); + + InterInstruction * cins = new InterInstruction(MapLocation(exp, inlineMapper), IC_CALL); + cins->mNumOperands = 1; + if (funcexp->mDecValue && (funcexp->mDecValue->mFlags & DTF_NATIVE) || (mCompilerOptions & COPT_NATIVE)) + cins->mCode = IC_CALL_NATIVE; + else + cins->mCode = IC_CALL; + + if (funcexp->mDecValue && (funcexp->mDecValue->mFlags & DTF_FUNC_PURE)) + cins->mNoSideEffects = true; + + if (funcexp->mType == EX_CONSTANT && (funcexp->mDecValue->mFlags & DTF_FUNC_CONSTEXPR) && funcexp->mDecType->mBase->mType != DT_TYPE_STRUCT) + cins->mConstExpr = true; + + cins->mSrc[0].mType = IT_POINTER; + cins->mSrc[0].mTemp = vl.mTemp; + if (ftype->mBase->IsShortIntStruct()) + { + cins->mDst.mType = InterTypeOfSize(ftype->mBase->mSize); + cins->mDst.mTemp = proc->AddTemporary(cins->mDst.mType); + } + else if (ftype->mBase->mType != DT_TYPE_VOID && ftype->mBase->mType != DT_TYPE_STRUCT) + { + cins->mDst.mType = InterTypeOf(ftype->mBase); + cins->mDst.mTemp = proc->AddTemporary(cins->mDst.mType); + } + + block->Append(cins); + + + if (ftype->mFlags & DTF_FASTCALL) + { + + } + else + { + fins->mConst.mIntConst = atotal; + + InterInstruction* xins = new InterInstruction(MapLocation(exp, inlineMapper), IC_POP_FRAME); + xins->mNumOperands = 0; + xins->mConst.mIntConst = atotal; + block->Append(xins); + } + + if (ftype->mBase->IsShortIntStruct()) + { + int ttemp; + if (lrexp) + { + if (!ftype->mBase->CanAssign(lrexp->mType)) + mErrors->Error(exp->mLocation, EERR_INCOMPATIBLE_TYPES, "Cannot assign incompatible types", ftype->mBase->MangleIdent(), lrexp->mType->MangleIdent()); + + ttemp = lrexp->mTemp; + + decResult = lrexp->mType; + } + else + { + int nindex = proc->mNumLocals++; + + Declaration* vdec = new Declaration(exp->mLocation, DT_VARIABLE); + + vdec->mVarIndex = nindex; + vdec->mBase = ftype->mBase; + vdec->mSize = ftype->mBase->mSize; + + decResult = vdec; + + InterInstruction* vins = new InterInstruction(MapLocation(exp, inlineMapper), IC_CONSTANT); + vins->mDst.mType = IT_POINTER; + vins->mDst.mTemp = proc->AddTemporary(IT_POINTER); + vins->mConst.mType = IT_POINTER; + vins->mConst.mMemory = IM_LOCAL; + vins->mConst.mVarIndex = nindex; + vins->mConst.mOperandSize = ftype->mBase->mSize; + block->Append(vins); + + ttemp = vins->mDst.mTemp; + } + // Unmarshall result from return value into struct + + Declaration* dec = ftype->mBase->mParams; + while (dec) + { + if (dec->mType == DT_ELEMENT) + { + InterInstruction* oins = new InterInstruction(MapLocation(exp, inlineMapper), IC_CONSTANT); + oins->mDst.mType = IT_INT16; + oins->mDst.mTemp = proc->AddTemporary(IT_INT16); + oins->mConst.mType = IT_INT16; + oins->mConst.mIntConst = dec->mOffset * (decResult->mStripe ? decResult->mStripe : 1); + block->Append(oins); + + InterInstruction* ains = new InterInstruction(MapLocation(exp, inlineMapper), IC_LEA); + ains->mSrc[1].mMemory = IM_INDIRECT; + ains->mSrc[1].mType = IT_POINTER; + ains->mSrc[1].mTemp = ttemp; + ains->mSrc[0] = oins->mDst; + ains->mDst.mType = IT_POINTER; + ains->mDst.mMemory = IM_INDIRECT; + ains->mDst.mTemp = proc->AddTemporary(IT_POINTER); + ains->mDst.mOperandSize = dec->mSize; + ains->mNumOperands = 2; + block->Append(ains); + + InterInstruction* csins = new InterInstruction(MapLocation(exp, inlineMapper), IC_CONSTANT); + csins->mDst.mType = IT_INT8; + csins->mDst.mTemp = proc->AddTemporary(csins->mDst.mType); + csins->mConst.mType = IT_INT8; + csins->mConst.mIntConst = 8 * dec->mOffset; + csins->mNumOperands = 0; + block->Append(csins); + + InterInstruction* asins = new InterInstruction(MapLocation(exp, inlineMapper), IC_CONSTANT); + asins->mDst.mType = cins->mDst.mType; + asins->mDst.mTemp = proc->AddTemporary(csins->mDst.mType); + asins->mConst.mType = cins->mDst.mType; + asins->mConst.mIntConst = (1ll << 8 * dec->mSize) - 1; + asins->mNumOperands = 0; + block->Append(asins); + + InterInstruction* shins = new InterInstruction(MapLocation(exp, inlineMapper), IC_BINARY_OPERATOR); + shins->mOperator = IA_SHR; + shins->mDst.mType = cins->mDst.mType; + shins->mDst.mTemp = proc->AddTemporary(shins->mDst.mType); + shins->mSrc[1] = cins->mDst; + shins->mSrc[0] = csins->mDst; + shins->mNumOperands = 2; + block->Append(shins); + + InterInstruction* andins = new InterInstruction(MapLocation(exp, inlineMapper), IC_BINARY_OPERATOR); + andins->mOperator = IA_AND; + andins->mDst.mType = cins->mDst.mType; + andins->mDst.mTemp = proc->AddTemporary(andins->mDst.mType); + andins->mSrc[0] = asins->mDst; + andins->mSrc[1] = shins->mDst; + andins->mNumOperands = 2; + block->Append(andins); + + InterInstruction* sins = new InterInstruction(MapLocation(exp, inlineMapper), IC_STORE); + sins->mSrc[0].mTemp = andins->mDst.mTemp; + sins->mSrc[0].mType = InterTypeOf(dec->mBase); + sins->mSrc[1] = ains->mDst; + sins->mSrc[1].mStride = decResult->mStripe ? decResult->mStripe : 1; + sins->mNumOperands = 2; + block->Append(sins); + } + dec = dec->mNext; + } + + if (lrexp) + return *lrexp; + + return ExValue(ftype->mBase, ttemp, 1); + } + else if (decResult) + { + if (lrexp) + return *lrexp; + + InterInstruction* vins = new InterInstruction(MapLocation(exp, inlineMapper), IC_CONSTANT); + vins->mDst.mType = IT_POINTER; + vins->mDst.mTemp = proc->AddTemporary(IT_POINTER); + vins->mConst.mType = IT_POINTER; + vins->mConst.mMemory = IM_LOCAL; + vins->mConst.mVarIndex = decResult->mVarIndex; + block->Append(vins); + + return ExValue(ftype->mBase, vins->mDst.mTemp, 1); + } + else if (ftype->mBase->IsReference()) + return ExValue(ftype->mBase->mBase, cins->mDst.mTemp, 1); + else + return ExValue(ftype->mBase, cins->mDst.mTemp); + } + + } + + case EX_ASSEMBLER: + { + GrowingArray refvars(nullptr); + + TranslateAssembler(proc->mModule, exp->mDecValue, &refvars); + + Declaration* dec = exp->mDecValue; + + if (block) + { + dec->mLinkerObject->mFlags |= LOBJF_INLINE; + + InterInstruction* ins = new InterInstruction(MapLocation(exp, inlineMapper), IC_CONSTANT); + ins->mDst.mType = IT_POINTER; + ins->mDst.mTemp = proc->AddTemporary(ins->mDst.mType); + ins->mConst.mType = IT_POINTER; + ins->mConst.mOperandSize = dec->mSize; + ins->mConst.mIntConst = 0; + ins->mConst.mMemory = IM_GLOBAL; + ins->mConst.mLinkerObject = dec->mLinkerObject; + ins->mConst.mVarIndex = dec->mVarIndex; + block->Append(ins); + + InterInstruction * jins = new InterInstruction(MapLocation(exp, inlineMapper), IC_ASSEMBLER); + jins->mDst.mTemp = proc->AddTemporary(IT_INT32); + jins->mDst.mType = IT_INT32; + + if (dec->mFlags & DTF_VOLATILE) + jins->mVolatile = true; + + jins->mSrc[0].mType = IT_POINTER; + jins->mSrc[0].mTemp = ins->mDst.mTemp; + jins->mNumOperands = 1; + + for (int i = 0; i < refvars.Size(); i++) + { + InterInstruction* vins = new InterInstruction(MapLocation(exp, inlineMapper), IC_CONSTANT); + vins->mConst.mType = IT_POINTER; + + vins->mDst.mType = IT_POINTER; + vins->mDst.mTemp = proc->AddTemporary(ins->mDst.mType); + + bool reference = false; + + Declaration* vdec = refvars[i]; + if (vdec->mType == DT_ARGUMENT) + { + vins->mConst.mVarIndex = vdec->mVarIndex; + if (vdec->mFlags & DTF_FPARAM_UNUSED) + { + vins->mConst.mMemory = IM_LOCAL; + if (inlineMapper) + vins->mConst.mVarIndex += inlineMapper->mVarIndex; + } + else if (inlineMapper) + { + vins->mConst.mMemory = IM_LOCAL; + vins->mConst.mVarIndex = inlineMapper->mParams[vdec->mVarIndex]; + } + else if (procType->mFlags & DTF_FASTCALL) + { + vins->mConst.mMemory = IM_FPARAM; +// vins->mConst.mVarIndex += procType->mFastCallBase; + } + else + vins->mConst.mMemory = IM_PARAM; + vins->mConst.mOperandSize = vdec->mSize; + vins->mConst.mIntConst = vdec->mOffset; + } + else if (vdec->mType == DT_VARIABLE) + { + vins->mConst.mMemory = IM_LOCAL; + vins->mConst.mVarIndex = vdec->mVarIndex; + vins->mConst.mOperandSize = vdec->mSize; + vins->mConst.mIntConst = vdec->mOffset; + if (inlineMapper) + vins->mConst.mVarIndex += inlineMapper->mVarIndex; + + if (vdec->mBase->mType == DT_TYPE_ARRAY || vdec->mBase->mType == DT_TYPE_STRUCT) + reference = true; + } + + block->Append(vins); + + InterInstruction* lins; + if (reference) + lins = vins; + else + { + lins = new InterInstruction(MapLocation(exp, inlineMapper), IC_LOAD); + lins->mDst.mType = InterTypeOf(vdec->mBase); + lins->mNumOperands = 1; + lins->mSrc[0].mMemory = IM_INDIRECT; + lins->mSrc[0].mType = IT_POINTER; + lins->mSrc[0].mTemp = vins->mDst.mTemp; + lins->mDst.mTemp = proc->AddTemporary(lins->mDst.mType); + lins->mSrc[0].mOperandSize = vdec->mSize; + block->Append(lins); + } + + if (jins->mNumOperands >= 32) + mErrors->Error(exp->mLocation, EERR_ASSEMBLER_LIMIT, "Maximum number of variables in assembler block exceeded", vdec->mIdent); + else + { + jins->mSrc[jins->mNumOperands].mType = InterTypeOf(vdec->mBase); + jins->mSrc[jins->mNumOperands].mTemp = lins->mDst.mTemp; + jins->mNumOperands++; + } + } + + block->Append(jins); + + return ExValue(exp->mDecType, jins->mDst.mTemp); + } + + return ExValue(TheVoidTypeDeclaration); + } + + case EX_RETURN: + { + InterInstruction * ins = new InterInstruction(MapLocation(exp, inlineMapper), IC_RETURN); + if (exp->mLeft) + { + if (procType->mBase->IsReference()) + { + vr = TranslateExpression(procType, proc, block, exp->mLeft, destack, gotos, breakBlock, continueBlock, inlineMapper); + + if (vr.mType->IsReference()) + { + vr.mReference++; + vr.mType = vr.mType->mBase; + } + + vr = Dereference(proc, exp, block, inlineMapper, vr, 1); + + vr = CoerceType(proc, exp, block, inlineMapper, vr, procType->mBase); + + if (vr.mReference == 0) + mErrors->Error(exp->mLocation, EERR_INVALID_VALUE, "Returning value as reference"); + + ins->mSrc[0].mType = IT_POINTER; + ins->mSrc[0].mTemp = vr.mTemp; + ins->mSrc[0].mMemory = IM_INDIRECT; + + if (inlineMapper) + { + if (inlineMapper->mResultExp) + { + if (inlineMapper->mResultExp->mType->IsReference()) + { + ins->mSrc[1].mType = IT_POINTER; + ins->mSrc[1].mTemp = inlineMapper->mResultExp->mTemp; + ins->mSrc[1].mMemory = IM_INDIRECT; + ins->mCode = IC_STORE; + ins->mSrc[1].mOperandSize = 2; + ins->mNumOperands = 2; + } + else + { + //bool moving = exp->mLeft->IsRValue() || exp->mLeft->mType == EX_VARIABLE && !(exp->mLeft->mDecValue->mFlags & (DTF_STATIC | DTF_GLOBAL)) && exp->mLeft->mDecType->mType != DT_TYPE_REFERENCE; + + CopyStruct(proc, exp, block, *(inlineMapper->mResultExp), vr, inlineMapper, false); + ins->mCode = IC_NONE; + } + } + else + { + InterInstruction* ains = new InterInstruction(MapLocation(exp, inlineMapper), IC_CONSTANT); + ains->mDst.mType = IT_POINTER; + ains->mDst.mTemp = proc->AddTemporary(ains->mDst.mType); + ains->mConst.mType = IT_POINTER; + ains->mConst.mOperandSize = procType->mBase->mSize; + ains->mConst.mIntConst = 0; + ains->mConst.mVarIndex = inlineMapper->mResult;; + ains->mConst.mMemory = IM_LOCAL; + block->Append(ains); + + ins->mSrc[1].mType = ains->mDst.mType; + ins->mSrc[1].mTemp = ains->mDst.mTemp; + ins->mSrc[1].mMemory = IM_INDIRECT; + ins->mCode = IC_STORE; + ins->mSrc[1].mOperandSize = ains->mConst.mOperandSize; + ins->mNumOperands = 2; + } + } + else + { + ins->mCode = IC_RETURN_VALUE; + ins->mNumOperands = 1; + } + } + else if (!inlineMapper && procType->mBase->IsShortIntStruct()) + { + vr = TranslateExpression(procType, proc, block, exp->mLeft, destack, gotos, breakBlock, continueBlock, inlineMapper); + + if (vr.mType->IsReference()) + { + vr.mReference++; + vr.mType = vr.mType->mBase; + } + + vr = Dereference(proc, exp, block, inlineMapper, vr, 1); + + if (!procType->mBase || procType->mBase->mType == DT_TYPE_VOID) + mErrors->Error(exp->mLocation, EERR_INVALID_RETURN, "Function has void return type"); + else if (!procType->mBase->CanAssign(vr.mType)) + mErrors->Error(exp->mLocation, EERR_INVALID_RETURN, "Cannot return incompatible type"); + else if (vr.mReference != 1) + mErrors->Error(exp->mLocation, EERR_INVALID_RETURN, "Non addressable object"); + + // Build marshalled struct into single long + InterInstruction* pins = new InterInstruction(MapLocation(exp, inlineMapper), IC_CONSTANT); + pins->mDst.mType = InterTypeOfSize(procType->mBase->mSize); + pins->mDst.mTemp = proc->AddTemporary(pins->mDst.mType); + pins->mConst.mType = pins->mDst.mType; + pins->mConst.mIntConst = 0; + block->Append(pins); + + Declaration* dec = procType->mBase->mParams; + while (dec) + { + if (dec->mType == DT_ELEMENT) + { + InterInstruction * oins = new InterInstruction(MapLocation(exp, inlineMapper), IC_CONSTANT); + oins->mDst.mType = IT_INT16; + oins->mDst.mTemp = proc->AddTemporary(IT_INT16); + oins->mConst.mType = IT_INT16; + oins->mConst.mIntConst = dec->mOffset; + block->Append(oins); + + InterInstruction * ains = new InterInstruction(MapLocation(exp, inlineMapper), IC_LEA); + ains->mSrc[1].mMemory = IM_INDIRECT; + ains->mSrc[1].mType = IT_POINTER; + ains->mSrc[1].mTemp = vr.mTemp; + ains->mSrc[0] = oins->mDst; + ains->mDst.mType = IT_POINTER; + ains->mDst.mMemory = IM_INDIRECT; + ains->mDst.mTemp = proc->AddTemporary(IT_POINTER); + ains->mDst.mOperandSize = dec->mSize; + ains->mNumOperands = 2; + block->Append(ains); + + InterInstruction* lins = new InterInstruction(MapLocation(exp, inlineMapper), IC_LOAD); + lins->mSrc[0] = ains->mDst; + lins->mDst.mType = InterTypeOf(dec->mBase); + lins->mDst.mTemp = proc->AddTemporary(lins->mDst.mType); + lins->mNumOperands = 1; + block->Append(lins); + + if (pins->mDst.mType == IT_INT32) + { + if (dec->mSize < 4) + { + InterInstruction* xins = new InterInstruction(MapLocation(exp, inlineMapper), IC_CONVERSION_OPERATOR); + xins->mOperator = dec->mSize == 1 ? IA_EXT8TO32U : IA_EXT16TO32U; + xins->mSrc[0] = lins->mDst; + xins->mDst.mType = IT_INT32; + xins->mDst.mTemp = proc->AddTemporary(pins->mDst.mType); + xins->mNumOperands = 1; + block->Append(xins); + lins = xins; + } + } + else if (pins->mDst.mType == IT_INT16) + { + if (dec->mSize < 2) + { + InterInstruction* xins = new InterInstruction(MapLocation(exp, inlineMapper), IC_CONVERSION_OPERATOR); + xins->mOperator = IA_EXT8TO16U; + xins->mSrc[0] = lins->mDst; + xins->mDst.mType = IT_INT16; + xins->mDst.mTemp = proc->AddTemporary(pins->mDst.mType); + xins->mNumOperands = 1; + block->Append(xins); + lins = xins; + } + } + + InterInstruction* csins = new InterInstruction(MapLocation(exp, inlineMapper), IC_CONSTANT); + csins->mDst.mType = IT_INT8; + csins->mDst.mTemp = proc->AddTemporary(csins->mDst.mType); + csins->mConst.mType = IT_INT8; + csins->mConst.mIntConst = 8 * dec->mOffset; + csins->mNumOperands = 0; + block->Append(csins); + + InterInstruction* sins = new InterInstruction(MapLocation(exp, inlineMapper), IC_BINARY_OPERATOR); + sins->mOperator = IA_SHL; + sins->mDst.mType = pins->mDst.mType; + sins->mDst.mTemp = proc->AddTemporary(sins->mDst.mType); + sins->mSrc[1] = lins->mDst; + sins->mSrc[0] = csins->mDst; + sins->mNumOperands = 2; + block->Append(sins); + + InterInstruction* orins = new InterInstruction(MapLocation(exp, inlineMapper), IC_BINARY_OPERATOR); + orins->mOperator = IA_OR; + orins->mDst.mType = pins->mDst.mType; + orins->mDst.mTemp = proc->AddTemporary(orins->mDst.mType); + orins->mSrc[0] = pins->mDst; + orins->mSrc[1] = sins->mDst; + orins->mNumOperands = 2; + block->Append(orins); + + pins = orins; + } + dec = dec->mNext; + } + + ins->mCode = IC_RETURN_VALUE; + ins->mSrc[0] = pins->mDst; + ins->mNumOperands = 1; + } + else if (procType->mBase->mType == DT_TYPE_STRUCT) + { + InterInstruction* ains = new InterInstruction(MapLocation(exp, inlineMapper), IC_CONSTANT); + + if (inlineMapper) + { + if (inlineMapper->mResultExp) + { + ains->mDst.mTemp = inlineMapper->mResultExp->mTemp; + } + else + { + ains->mCode = IC_CONSTANT; + ains->mDst.mType = IT_POINTER; + ains->mDst.mTemp = proc->AddTemporary(IT_POINTER); + ains->mConst.mType = IT_POINTER; + ains->mConst.mOperandSize = procType->mBase->mSize; + ains->mConst.mIntConst = 0; + ains->mConst.mVarIndex = inlineMapper->mResult; + ains->mConst.mMemory = IM_LOCAL; + block->Append(ains); + } + + ins->mCode = IC_NONE; + } + else + { + InterInstruction* pins = new InterInstruction(MapLocation(exp, inlineMapper), IC_CONSTANT); + pins->mDst.mType = IT_POINTER; + pins->mDst.mTemp = proc->AddTemporary(IT_POINTER); + pins->mConst.mType = IT_POINTER; + pins->mConst.mVarIndex = 0; + pins->mConst.mIntConst = 0; + pins->mConst.mOperandSize = 2; + if (procType->mFlags & DTF_FASTCALL) + { + pins->mConst.mMemory = IM_FPARAM; + + if (procType->mFastCallBase + 2 <= BC_REG_FPARAMS_END - BC_REG_FPARAMS) + pins->mConst.mVarIndex += procType->mFastCallBase; + else + pins->mConst.mVarIndex += BC_REG_FPARAMS_END - BC_REG_FPARAMS + procType->mFastCallBase2; + } + else + pins->mConst.mMemory = IM_PARAM; + block->Append(pins); + + ains->mCode = IC_LOAD; + ains->mSrc[0].mMemory = IM_INDIRECT; + ains->mSrc[0].mType = IT_POINTER; + ains->mSrc[0].mTemp = pins->mDst.mTemp; + ains->mDst.mType = IT_POINTER; + ains->mDst.mTemp = proc->AddTemporary(IT_POINTER); + ains->mNumOperands = 1; + block->Append(ains); + + ins->mCode = IC_RETURN; + } + + ExValue rvr(procType->mBase, ains->mDst.mTemp, 1); + + vr = TranslateExpression(procType, proc, block, exp->mLeft, destack, gotos, breakBlock, continueBlock, inlineMapper, &rvr); + + vr = Dereference(proc, exp, block, inlineMapper, vr, 1); + + if (!procType->mBase || procType->mBase->mType == DT_TYPE_VOID) + mErrors->Error(exp->mLocation, EERR_INVALID_RETURN, "Function has void return type"); + else if (!procType->mBase->CanAssign(vr.mType)) + mErrors->Error(exp->mLocation, EERR_INVALID_RETURN, "Cannot return incompatible type"); + else if (vr.mReference != 1) + mErrors->Error(exp->mLocation, EERR_INVALID_RETURN, "Non addressable object"); + + bool moving = exp->mLeft->IsRValue() || exp->mLeft->mType == EX_VARIABLE && !(exp->mLeft->mDecValue->mFlags & (DTF_STATIC | DTF_GLOBAL)) && exp->mLeft->mDecType->mType != DT_TYPE_REFERENCE; + + CopyStruct(proc, exp, block, rvr, vr, inlineMapper, moving); + } + else + { + vr = TranslateExpression(procType, proc, block, exp->mLeft, destack, gotos, breakBlock, continueBlock, inlineMapper); + + if (procType->mBase->mType == DT_TYPE_POINTER && (vr.mType->mType == DT_TYPE_ARRAY || vr.mType->mType == DT_TYPE_FUNCTION)) + { + vr = Dereference(proc, exp, block, inlineMapper, vr, 1); + vr.mReference = 0; + vr.mType = procType->mBase; + } + else if (vr.mType->mType != DT_TYPE_VOID) + vr = Dereference(proc, exp, block, inlineMapper, vr); + + if (!procType->mBase || procType->mBase->mType == DT_TYPE_VOID) + { + if (vr.mType->mType != DT_TYPE_VOID) + mErrors->Error(exp->mLocation, EERR_INVALID_RETURN, "Function has void return type"); + } + else if (procType->mBase->mType == DT_TYPE_BOOL && (vr.mType->IsIntegerType() || vr.mType->mType == DT_TYPE_POINTER)) + ; + else if (procType->mBase->mType == DT_TYPE_POINTER && exp->mLeft->mType == EX_CONSTANT && exp->mLeft->mDecValue->mType == DT_CONST_INTEGER && exp->mLeft->mDecValue->mInteger == 0) + mErrors->Error(exp->mLocation, EWARN_NUMERIC_0_USED_AS_NULLPTR, "Numeric 0 used for nullptr"); + else if (!procType->mBase->CanAssign(vr.mType)) + mErrors->Error(exp->mLocation, EERR_INVALID_RETURN, "Cannot return incompatible type"); + + if (vr.mType->mType == DT_TYPE_VOID) + { + if (inlineMapper) + ins->mCode = IC_NONE; + else + ins->mCode = IC_RETURN; + } + else + { + vr = CoerceType(proc, exp, block, inlineMapper, vr, procType->mBase); + + ins->mSrc[0].mType = InterTypeOf(vr.mType); + ins->mSrc[0].mTemp = vr.mTemp; + + if (inlineMapper) + { + if (inlineMapper->mResultExp) + { + ins->mSrc[1].mType = IT_POINTER; + ins->mSrc[1].mTemp = inlineMapper->mResultExp->mTemp; + ins->mSrc[1].mMemory = IM_INDIRECT; + ins->mCode = IC_STORE; + ins->mSrc[1].mOperandSize = procType->mBase->mSize; + ins->mNumOperands = 2; + } + else + { + InterInstruction* ains = new InterInstruction(MapLocation(exp, inlineMapper), IC_CONSTANT); + ains->mDst.mType = IT_POINTER; + ains->mDst.mTemp = proc->AddTemporary(ains->mDst.mType); + ains->mConst.mType = IT_POINTER; + ains->mConst.mOperandSize = procType->mBase->mSize; + ains->mConst.mIntConst = 0; + ains->mConst.mVarIndex = inlineMapper->mResult;; + ains->mConst.mMemory = IM_LOCAL; + block->Append(ains); + + ins->mSrc[1].mType = ains->mDst.mType; + ins->mSrc[1].mTemp = ains->mDst.mTemp; + ins->mSrc[1].mMemory = IM_INDIRECT; + ins->mCode = IC_STORE; + ins->mSrc[1].mOperandSize = ains->mConst.mOperandSize; + ins->mNumOperands = 2; + } + } + else + { + ins->mCode = IC_RETURN_VALUE; + ins->mNumOperands = 1; + } + } + } + } + else + { + if (procType->mBase && procType->mBase->mType != DT_TYPE_VOID) + mErrors->Error(exp->mLocation, EERR_INVALID_RETURN, "Function has non void return type"); + + if (inlineMapper) + ins->mCode = IC_NONE; + else + ins->mCode = IC_RETURN; + } + + UnwindDestructStack(procType, proc, block, destack, nullptr, inlineMapper); + + if (ins->mCode != IC_NONE) + block->Append(ins); + + if (inlineMapper) + { + InterInstruction* jins = new InterInstruction(MapLocation(exp, inlineMapper), IC_JUMP); + block->Append(jins); + block->Close(inlineMapper->mReturn, nullptr); + } + else + block->Close(nullptr, nullptr); + block = new InterCodeBasicBlock(proc); + + return ExValue(TheVoidTypeDeclaration); + } + + case EX_BREAK: + { + if (breakBlock.mBlock) + { + UnwindDestructStack(procType, proc, block, destack, breakBlock.mStack, inlineMapper); + InterInstruction * jins = new InterInstruction(MapLocation(exp, inlineMapper), IC_JUMP); + block->Append(jins); + + block->Close(breakBlock.mBlock, nullptr); + block = new InterCodeBasicBlock(proc); + } + else + mErrors->Error(exp->mLocation, EERR_INVALID_BREAK, "No break target"); + + return ExValue(TheVoidTypeDeclaration); + } + + case EX_CONTINUE: + { + if (continueBlock.mBlock) + { + UnwindDestructStack(procType, proc, block, destack, continueBlock.mStack, inlineMapper); + InterInstruction * jins = new InterInstruction(MapLocation(exp, inlineMapper), IC_JUMP); + block->Append(jins); + + block->Close(continueBlock.mBlock, nullptr); + block = new InterCodeBasicBlock(proc); + } + else + mErrors->Error(exp->mLocation, EERR_INVALID_CONTINUE, "No continue target"); + + return ExValue(TheVoidTypeDeclaration); + } + + case EX_ASSUME: + { +#if 1 + InterCodeBasicBlock* tblock = new InterCodeBasicBlock(proc); + InterCodeBasicBlock* fblock = new InterCodeBasicBlock(proc); + + TranslateLogic(procType, proc, block, tblock, fblock, exp->mLeft, destack, gotos, inlineMapper); + + InterInstruction* ins = new InterInstruction(MapLocation(exp, inlineMapper), IC_UNREACHABLE); + ins->mNumOperands = 0; + fblock->Append(ins); + fblock->Close(nullptr, nullptr); + + block = tblock; +#endif + return ExValue(TheVoidTypeDeclaration); + } + case EX_LOGICAL_NOT: + { + vl = TranslateExpression(procType, proc, block, exp->mLeft, destack, gotos, breakBlock, continueBlock, inlineMapper); + vl = Dereference(proc, exp, block, inlineMapper, vl); + + InterInstruction * zins = new InterInstruction(MapLocation(exp, inlineMapper), IC_CONSTANT); + zins->mDst.mType = InterTypeOf(vl.mType); + zins->mDst.mTemp = proc->AddTemporary(zins->mDst.mType); + zins->mConst.mType = zins->mDst.mType; + zins->mConst.mIntConst = 0; + block->Append(zins); + + InterInstruction * ins = new InterInstruction(MapLocation(exp, inlineMapper), IC_RELATIONAL_OPERATOR); + ins->mOperator = IA_CMPEQ; + ins->mSrc[0].mType = InterTypeOf(vl.mType); + ins->mSrc[0].mTemp = zins->mDst.mTemp; + ins->mSrc[1].mType = InterTypeOf(vl.mType); + ins->mSrc[1].mTemp = vl.mTemp; + ins->mDst.mType = IT_BOOL; + ins->mDst.mTemp = proc->AddTemporary(ins->mDst.mType); + block->Append(ins); + + return ExValue(TheBoolTypeDeclaration, ins->mDst.mTemp); + } + + case EX_CONDITIONAL: + { +#if 1 + if (!exp->mRight->mLeft->HasSideEffects() && !exp->mRight->mRight->HasSideEffects()) + { + ExValue vc = TranslateExpression(procType, proc, block, exp->mLeft, destack, gotos, breakBlock, continueBlock, inlineMapper); + + vl = TranslateExpression(procType, proc, block, exp->mRight->mLeft, destack, gotos, breakBlock, continueBlock, inlineMapper); + vr = TranslateExpression(procType, proc, block, exp->mRight->mRight, destack, gotos, breakBlock, continueBlock, inlineMapper); + + vc = Dereference(proc, exp, block, inlineMapper, vc); + + int ttemp, tref = 0; + InterType ttype, stypel, styper; + + stypel = InterTypeOf(vl.mType); + styper = InterTypeOf(vr.mType); + + Declaration* dtype = exp->mDecType; + + if (dtype->IsStructOrUnion()) + { + vl = Dereference(proc, exp, block, inlineMapper, vl, 1); + vr = Dereference(proc, exp, block, inlineMapper, vr, 1); + tref = 1; + + ttype = IT_POINTER; + } + else if (dtype->IsReference()) + { + vl = Dereference(proc, exp, block, inlineMapper, vl, 1); + vr = Dereference(proc, exp, block, inlineMapper, vr, 1); + tref = 1; + + dtype = dtype->mBase; + ttype = IT_POINTER; + } + else if (stypel == IT_POINTER || styper == IT_POINTER) + { + if (vl.mType->mType == DT_TYPE_ARRAY) + vl = Dereference(proc, exp, block, inlineMapper, vl, 1); + else + vl = Dereference(proc, exp, block, inlineMapper, vl); + + if (vr.mType->mType == DT_TYPE_ARRAY) + vr = Dereference(proc, exp, block, inlineMapper, vr, 1); + else + vr = Dereference(proc, exp, block, inlineMapper, vr); + + if (vl.mType->mBase && vr.mType->mBase) + { + if (vl.mType->mBase->IsSubType(vr.mType->mBase)) + dtype = vr.mType; + else if (vr.mType->mBase->IsSubType(vl.mType->mBase)) + dtype = vl.mType; + else + { + mErrors->Error(exp->mLocation, EERR_INCOMPATIBLE_OPERATOR, "Incompatible conditional types"); + dtype = vl.mType; + } + } + else + { + mErrors->Error(exp->mLocation, EERR_INCOMPATIBLE_OPERATOR, "Incompatible conditional types"); + dtype = vl.mType->mBase ? vl.mType : vr.mType; + } + + Declaration* ntype = new Declaration(dtype->mLocation, DT_TYPE_POINTER); + ntype->mBase = dtype->mBase; + dtype = ntype; + + ttype = IT_POINTER; + } + else + { + vl = Dereference(proc, exp, block, inlineMapper, vl); + vr = Dereference(proc, exp, block, inlineMapper, vr); + + if (stypel == styper) + { + ttype = stypel; + dtype = vl.mType; + } + else if (stypel > styper) + { + ttype = stypel; + dtype = vl.mType; + + vr = CoerceType(proc, exp, block, inlineMapper, vr, dtype); + } + else + { + ttype = styper; + dtype = vr.mType; + + vl = CoerceType(proc, exp, block, inlineMapper, vl, dtype); + } + } + + vc = CoerceType(proc, exp, block, inlineMapper, vc, TheBoolTypeDeclaration); + + ttemp = proc->AddTemporary(ttype); + + InterInstruction* sins = new InterInstruction(MapLocation(exp, inlineMapper), IC_SELECT); + sins->mSrc[2].mType = InterTypeOf(vc.mType); + sins->mSrc[2].mTemp = vc.mTemp; + sins->mSrc[1].mType = ttype; + sins->mSrc[1].mTemp = vl.mTemp; + sins->mSrc[0].mType = ttype; + sins->mSrc[0].mTemp = vr.mTemp; + sins->mDst.mType = ttype; + sins->mDst.mTemp = ttemp; + block->Append(sins); + + return ExValue(dtype, ttemp, tref); + } + else +#endif + { + InterInstruction* jins0 = new InterInstruction(MapLocation(exp, inlineMapper), IC_JUMP); + InterInstruction* jins1 = new InterInstruction(MapLocation(exp, inlineMapper), IC_JUMP); + + InterCodeBasicBlock* tblock = new InterCodeBasicBlock(proc); + InterCodeBasicBlock* fblock = new InterCodeBasicBlock(proc); + InterCodeBasicBlock* eblock = new InterCodeBasicBlock(proc); + + TranslateLogic(procType, proc, block, tblock, fblock, exp->mLeft, destack, gotos, inlineMapper); + + vl = TranslateExpression(procType, proc, tblock, exp->mRight->mLeft, destack, gotos, breakBlock, continueBlock, inlineMapper); + vr = TranslateExpression(procType, proc, fblock, exp->mRight->mRight, destack, gotos, breakBlock, continueBlock, inlineMapper); + + int ttemp, tref = 0; + InterType ttype; + + Declaration* dtype = exp->mDecType; + + if (dtype->IsStructOrUnion()) + { + vl = Dereference(proc, exp, block, inlineMapper, vl, 1); + vr = Dereference(proc, exp, block, inlineMapper, vr, 1); + tref = 1; + + ttype = IT_POINTER; + } + else if (dtype->IsReference()) + { + vl = Dereference(proc, exp, block, inlineMapper, vl, 1); + vr = Dereference(proc, exp, block, inlineMapper, vr, 1); + tref = 1; + + dtype = dtype->mBase; + ttype = IT_POINTER; + } + else + { + InterType stypel, styper; + + if (vl.mType->IsReference()) + vl = ToValue(proc, exp, block, inlineMapper, vl); + if (vr.mType->IsReference()) + vr = ToValue(proc, exp, block, inlineMapper, vr); + + stypel = InterTypeOf(vl.mType); + styper = InterTypeOf(vr.mType); + + if (stypel == IT_POINTER || styper == IT_POINTER) + { + if (vl.mType->mType == DT_TYPE_ARRAY) + vl = Dereference(proc, exp, tblock, inlineMapper, vl, 1); + else + vl = Dereference(proc, exp, tblock, inlineMapper, vl); + + if (vr.mType->mType == DT_TYPE_ARRAY) + vr = Dereference(proc, exp, fblock, inlineMapper, vr, 1); + else + vr = Dereference(proc, exp, fblock, inlineMapper, vr); + + if (vl.mType->mBase && vr.mType->mBase) + { + if (vl.mType->mBase->IsSubType(vr.mType->mBase)) + dtype = vr.mType; + else if (vr.mType->mBase->IsSubType(vl.mType->mBase)) + dtype = vl.mType; + else + { + mErrors->Error(exp->mLocation, EERR_INCOMPATIBLE_OPERATOR, "Incompatible conditional types"); + dtype = vl.mType; + } + } + else + { + mErrors->Error(exp->mLocation, EERR_INCOMPATIBLE_OPERATOR, "Incompatible conditional types"); + dtype = vl.mType; + } + + Declaration* ntype = new Declaration(dtype->mLocation, DT_TYPE_POINTER); + ntype->mBase = dtype->mBase; + dtype = ntype; + + ttype = IT_POINTER; + } + else + { + vl = Dereference(proc, exp, tblock, inlineMapper, vl); + vr = Dereference(proc, exp, fblock, inlineMapper, vr); + + if (stypel == styper) + { + ttype = stypel; + dtype = vl.mType; + } + else if (stypel > styper) + { + ttype = stypel; + dtype = vl.mType; + + vr = CoerceType(proc, exp, fblock, inlineMapper, vr, dtype); + } + else + { + ttype = styper; + dtype = vr.mType; + + vl = CoerceType(proc, exp, tblock, inlineMapper, vl, dtype); + } + } + } + + if (ttype != IT_NONE) + { + ttemp = proc->AddTemporary(ttype); + + if (vr.mTemp >= 0) + { + InterInstruction* rins = new InterInstruction(MapLocation(exp, inlineMapper), IC_LOAD_TEMPORARY); + rins->mSrc[0].mType = ttype; + rins->mSrc[0].mTemp = vr.mTemp; + rins->mDst.mType = ttype; + rins->mDst.mTemp = ttemp; + fblock->Append(rins); + } + else + { + InterInstruction* rins = new InterInstruction(MapLocation(exp, inlineMapper), IC_CONSTANT); + rins->mConst.mType = ttype; + rins->mDst.mType = ttype; + rins->mDst.mTemp = ttemp; + tblock->Append(rins); + } + + if (vl.mTemp >= 0) + { + InterInstruction* lins = new InterInstruction(MapLocation(exp, inlineMapper), IC_LOAD_TEMPORARY); + lins->mSrc[0].mType = ttype; + lins->mSrc[0].mTemp = vl.mTemp; + lins->mDst.mType = ttype; + lins->mDst.mTemp = ttemp; + tblock->Append(lins); + } + else + { + InterInstruction* lins = new InterInstruction(MapLocation(exp, inlineMapper), IC_CONSTANT); + lins->mConst.mType = ttype; + lins->mDst.mType = ttype; + lins->mDst.mTemp = ttemp; + tblock->Append(lins); + } + + tblock->Append(jins0); + tblock->Close(eblock, nullptr); + + fblock->Append(jins1); + fblock->Close(eblock, nullptr); + + block = eblock; + + return ExValue(dtype, ttemp, tref); + } + else + { + tblock->Append(jins0); + tblock->Close(eblock, nullptr); + + fblock->Append(jins1); + fblock->Close(eblock, nullptr); + + block = eblock; + + return ExValue(); + } + } + + break; + } + + case EX_TYPECAST: + { + vr = TranslateExpression(procType, proc, block, exp->mLeft, destack, gotos, breakBlock, continueBlock, inlineMapper); + + InterInstruction * ins = new InterInstruction(MapLocation(exp, inlineMapper), IC_CONVERSION_OPERATOR); + + if (vr.mType->IsReference() && !exp->mDecType->IsReference()) + vr = ToValue(proc, exp, block, inlineMapper, vr); + + if (exp->mDecType->mType == DT_TYPE_FLOAT && vr.mType->IsIntegerType()) + { + vr = Dereference(proc, exp, block, inlineMapper, vr); + + int stemp = vr.mTemp; + + if (vr.mType->mSize == 1) + { + if (vr.mType->mFlags & DTF_SIGNED) + { + InterInstruction* xins = new InterInstruction(MapLocation(exp, inlineMapper), IC_CONVERSION_OPERATOR); + xins->mOperator = IA_EXT8TO16S; + xins->mSrc[0].mType = IT_INT8; + xins->mSrc[0].mTemp = stemp; + xins->mDst.mType = IT_INT16; + xins->mDst.mTemp = proc->AddTemporary(IT_INT16); + block->Append(xins); + stemp = xins->mDst.mTemp; + } + else + { + InterInstruction* xins = new InterInstruction(MapLocation(exp, inlineMapper), IC_CONVERSION_OPERATOR); + xins->mOperator = IA_EXT8TO16U; + xins->mSrc[0].mType = IT_INT8; + xins->mSrc[0].mTemp = stemp; + xins->mDst.mType = IT_INT16; + xins->mDst.mTemp = proc->AddTemporary(IT_INT16); + block->Append(xins); + stemp = xins->mDst.mTemp; + } + } + + if (vr.mType->mSize <= 2) + { + ins->mOperator = (vr.mType->mFlags & DTF_SIGNED) ? IA_INT2FLOAT : IA_UINT2FLOAT; + ins->mSrc[0].mType = IT_INT16; + } + else + { + ins->mOperator = (vr.mType->mFlags & DTF_SIGNED) ? IA_LINT2FLOAT : IA_LUINT2FLOAT; + ins->mSrc[0].mType = IT_INT32; + } + + ins->mSrc[0].mTemp = stemp; + ins->mDst.mType = InterTypeOf(exp->mDecType); + ins->mDst.mTemp = proc->AddTemporary(ins->mDst.mType); + block->Append(ins); + } + else if (exp->mDecType->IsIntegerType() && vr.mType->mType == DT_TYPE_FLOAT) + { + vr = Dereference(proc, exp, block, inlineMapper, vr); + if (exp->mDecType->mSize <= 2) + { + ins->mOperator = (exp->mDecType->mFlags & DTF_SIGNED) ? IA_FLOAT2INT : IA_FLOAT2UINT; + ins->mDst.mType = IT_INT16; + } + else + { + ins->mOperator = (exp->mDecType->mFlags & DTF_SIGNED) ? IA_FLOAT2LINT : IA_FLOAT2LUINT; + ins->mDst.mType = IT_INT32; + } + + ins->mSrc[0].mType = InterTypeOf(vr.mType); + ins->mSrc[0].mTemp = vr.mTemp; + ins->mDst.mTemp = proc->AddTemporary(ins->mDst.mType); + block->Append(ins); + + if (exp->mDecType->mSize == 1) + { + InterInstruction* xins = new InterInstruction(MapLocation(exp, inlineMapper), IC_TYPECAST); + xins->mSrc[0].mType = IT_INT16; + xins->mSrc[0].mTemp = ins->mDst.mTemp; + xins->mDst.mType = InterTypeOf(exp->mDecType); + xins->mDst.mTemp = proc->AddTemporary(ins->mDst.mType); + block->Append(xins); + ins = xins; + } + } + else if (exp->mDecType->mType == DT_TYPE_POINTER && vr.mType->mType == DT_TYPE_POINTER) + { + // no need for actual operation when casting pointer to pointer + return ExValue(exp->mDecType, vr.mTemp, vr.mReference); + } + else if (exp->mDecType->mType == DT_TYPE_POINTER && vr.mType->mType == DT_TYPE_ARRAY) + { + // no need for actual operation when casting pointer to pointer + return ExValue(exp->mDecType, vr.mTemp, vr.mReference - 1); + } + else if (exp->mDecType->mType != DT_TYPE_VOID && vr.mType->mType == DT_TYPE_VOID) + { + mErrors->Error(exp->mLocation, EERR_INCOMPATIBLE_OPERATOR, "Cannot cast void object to non void object"); + return ExValue(exp->mDecType, vr.mTemp, vr.mReference); + } + else if (exp->mDecType->IsIntegerType() && vr.mType->IsIntegerType()) + { + vr = Dereference(proc, exp, block, inlineMapper, vr); + return CoerceType(proc, exp, block, inlineMapper, vr, exp->mDecType); + } + else if (exp->mDecType->mType == DT_TYPE_VOID) + { + } + else if (exp->mDecType->IsReference() && exp->mDecType->mBase->IsConstSame(vr.mType)) + { + if (vr.mReference == 0) + mErrors->Error(exp->mLocation, EERR_NOT_AN_LVALUE, "Not an L value"); + + vr = Dereference(proc, exp, block, inlineMapper, vr, 1); + return ExValue(exp->mDecType, vr.mTemp); + } + else + { + if (vr.mType->mType == DT_TYPE_ARRAY) + vr = Dereference(proc, exp, block, inlineMapper, vr, 1); + else + vr = Dereference(proc, exp, block, inlineMapper, vr); + ins->mCode = IC_TYPECAST; + ins->mSrc[0].mType = vr.mReference > 0 ? IT_POINTER : InterTypeOf(vr.mType); + ins->mSrc[0].mTemp = vr.mTemp; + ins->mDst.mType = InterTypeOf(exp->mDecType); + ins->mDst.mTemp = proc->AddTemporary(ins->mDst.mType); + block->Append(ins); + } + + return ExValue(exp->mDecType, ins->mDst.mTemp); + } + break; + + case EX_LOGICAL_AND: + case EX_LOGICAL_OR: + { + InterInstruction* jins0 = new InterInstruction(MapLocation(exp, inlineMapper), IC_JUMP); + InterInstruction* jins1 = new InterInstruction(MapLocation(exp, inlineMapper), IC_JUMP); + + InterCodeBasicBlock* tblock = new InterCodeBasicBlock(proc); + InterCodeBasicBlock* fblock = new InterCodeBasicBlock(proc); + InterCodeBasicBlock* eblock = new InterCodeBasicBlock(proc); + + TranslateLogic(procType, proc, block, tblock, fblock, exp, destack, gotos, inlineMapper); + + int ttemp = proc->AddTemporary(IT_BOOL); + + InterInstruction* tins = new InterInstruction(MapLocation(exp, inlineMapper), IC_CONSTANT); + tins->mConst.mType = IT_BOOL; + tins->mConst.mIntConst = 1; + tins->mDst.mType = IT_BOOL; + tins->mDst.mTemp = ttemp; + tblock->Append(tins); + + InterInstruction* fins = new InterInstruction(MapLocation(exp, inlineMapper), IC_CONSTANT); + fins->mConst.mType = IT_BOOL; + fins->mConst.mIntConst = 0; + fins->mDst.mType = IT_BOOL; + fins->mDst.mTemp = ttemp; + fblock->Append(fins); + + tblock->Append(jins0); + tblock->Close(eblock, nullptr); + + fblock->Append(jins1); + fblock->Close(eblock, nullptr); + + block = eblock; + + return ExValue(TheBoolTypeDeclaration, ttemp); + + } break; + + case EX_SCOPE: + { + DestructStack* odestack = destack; + + TranslateExpression(procType, proc, block, exp->mLeft, destack, gotos, breakBlock, continueBlock, inlineMapper); + + UnwindDestructStack(procType, proc, block, destack, odestack, inlineMapper); + destack = odestack; + + return ExValue(TheVoidTypeDeclaration); + + } break; + + case EX_DISPATCH: + { + vl = TranslateExpression(procType, proc, block, exp->mLeft, destack, gotos, breakBlock, continueBlock, inlineMapper); + vl = Dereference(proc, exp, block, inlineMapper, vl); + + InterInstruction* ins = new InterInstruction(MapLocation(exp, inlineMapper), IC_DISPATCH); + ins->mSrc[0].mType = IT_POINTER; + ins->mSrc[0].mTemp = vl.mTemp; + + block->Append(ins); + + Declaration* dinit = exp->mLeft->mLeft->mDecValue->mValue->mDecValue->mParams; + while (dinit) + { + proc->mModule->mProcedures[dinit->mValue->mDecValue->mVarIndex]->mDispatchedCall = true; + proc->AddCalledFunction(proc->mModule->mProcedures[dinit->mValue->mDecValue->mVarIndex]); + dinit = dinit->mNext; + } + + +// proc->CallsFunctionPointer(); + + return ExValue(TheVoidTypeDeclaration); + } + + case EX_WHILE: + { + DestructStack* odestack = destack; + + InterInstruction * jins0 = new InterInstruction(MapLocation(exp, inlineMapper), IC_JUMP); + InterInstruction* jins1 = new InterInstruction(MapLocation(exp, inlineMapper), IC_JUMP); + + InterCodeBasicBlock* cblock = new InterCodeBasicBlock(proc); + InterCodeBasicBlock* lblock = cblock; + InterCodeBasicBlock* bblock = new InterCodeBasicBlock(proc); + InterCodeBasicBlock* eblock = new InterCodeBasicBlock(proc); + + block->Append(jins0); + block->Close(cblock, nullptr); + + TranslateLogic(procType, proc, cblock, bblock, eblock, exp->mLeft, destack, gotos, inlineMapper); + + DestructStack* idestack = destack; + + vr = TranslateExpression(procType, proc, bblock, exp->mRight, destack, gotos, BranchTarget(eblock, odestack), BranchTarget(lblock, idestack), inlineMapper); + + UnwindDestructStack(procType, proc, bblock, destack, odestack, inlineMapper); +// destack = odestack; + + bblock->Append(jins1); + bblock->Close(lblock, nullptr); + + block = eblock; + + UnwindDestructStack(procType, proc, block, idestack, odestack, inlineMapper); + destack = odestack; + + return ExValue(TheVoidTypeDeclaration); + } + + case EX_IF: + { + DestructStack* odestack = destack; + + InterInstruction * jins0 = new InterInstruction(MapLocation(exp, inlineMapper), IC_JUMP); + InterInstruction* jins1 = new InterInstruction(MapLocation(exp, inlineMapper), IC_JUMP); + + InterCodeBasicBlock* tblock = new InterCodeBasicBlock(proc); + InterCodeBasicBlock* fblock = new InterCodeBasicBlock(proc); + InterCodeBasicBlock* eblock = new InterCodeBasicBlock(proc); + + TranslateLogic(procType, proc, block, tblock, fblock, exp->mLeft, destack, gotos, inlineMapper); + + DestructStack* itdestack = destack; + ErrorID eid; + + bool alwaysTrue = false, alwaysFalse = false; + if (exp->mLeft->mType == EX_CONSTANT && exp->mLeft->mDecValue->mType == DT_CONST_INTEGER) + { + alwaysTrue = exp->mLeft->mDecValue->mInteger != 0; + alwaysFalse = exp->mLeft->mDecValue->mInteger == 0; + } + + if (alwaysFalse) + eid = mErrors->SetMinLevel(EERR_GENERIC); + vr = TranslateExpression(procType, proc, tblock, exp->mRight->mLeft, destack, gotos, breakBlock, continueBlock, inlineMapper); + UnwindDestructStack(procType, proc, tblock, destack, itdestack, inlineMapper); + destack = itdestack; + if (alwaysFalse) + mErrors->SetMinLevel(eid); + + tblock->Append(jins0); + tblock->Close(eblock, nullptr); + + if (exp->mRight->mRight) + { + if (alwaysTrue) + eid = mErrors->SetMinLevel(EERR_GENERIC); + DestructStack* ifdestack = destack; + vr = TranslateExpression(procType, proc, fblock, exp->mRight->mRight, destack, gotos, breakBlock, continueBlock, inlineMapper); + UnwindDestructStack(procType, proc, fblock, destack, ifdestack, inlineMapper); + destack = ifdestack; + if (alwaysTrue) + mErrors->SetMinLevel(eid); + } + fblock->Append(jins1); + fblock->Close(eblock, nullptr); + + block = eblock; + + UnwindDestructStack(procType, proc, block, destack, odestack, inlineMapper); + destack = odestack; + + return ExValue(TheVoidTypeDeclaration); + } + + case EX_FORBODY: + { + DestructStack* odestack = destack; + + InterInstruction* jins0 = new InterInstruction(MapLocation(exp, inlineMapper), IC_JUMP); + InterInstruction* jins1 = new InterInstruction(MapLocation(exp, inlineMapper), IC_JUMP); + + InterCodeBasicBlock* lblock = new InterCodeBasicBlock(proc); + InterCodeBasicBlock* cblock = new InterCodeBasicBlock(proc); + + block->Append(jins0); + block->Close(lblock, nullptr); + + DestructStack* idestack = destack; + + vr = TranslateExpression(procType, proc, lblock, exp->mLeft, destack, gotos, breakBlock, BranchTarget(cblock, idestack), inlineMapper); + + lblock->Append(jins1); + lblock->Close(cblock, nullptr); + + UnwindDestructStack(procType, proc, cblock, destack, idestack, inlineMapper); + destack = idestack; + + block = cblock; + + exp = exp->mRight; + if (!exp) + return ExValue(TheVoidTypeDeclaration); + + } break; + + case EX_FOR: + { + DestructStack* odestack = destack; + + // assignment + if (exp->mLeft->mRight) + TranslateExpression(procType, proc, block, exp->mLeft->mRight, destack, gotos, breakBlock, continueBlock, inlineMapper); + + InterInstruction* jins0 = new InterInstruction(MapLocation(exp, inlineMapper), IC_JUMP); + InterInstruction* jins1 = new InterInstruction(MapLocation(exp, inlineMapper), IC_JUMP); + InterInstruction* jins2 = new InterInstruction(MapLocation(exp, inlineMapper), IC_JUMP); + InterInstruction* jins3 = new InterInstruction(MapLocation(exp, inlineMapper), IC_JUMP); + + InterCodeBasicBlock* cblock = new InterCodeBasicBlock(proc); + InterCodeBasicBlock* lblock = cblock; + InterCodeBasicBlock* bblock = new InterCodeBasicBlock(proc); + InterCodeBasicBlock* iblock = new InterCodeBasicBlock(proc); + InterCodeBasicBlock* eblock = new InterCodeBasicBlock(proc); + + block->Append(jins0); + block->Close(cblock, nullptr); + + // condition + if (exp->mLeft->mLeft->mLeft) + TranslateLogic(procType, proc, cblock, bblock, eblock, exp->mLeft->mLeft->mLeft, destack, gotos, inlineMapper); + else + { + cblock->Append(jins1); + cblock->Close(bblock, nullptr); + } + + // Body + + DestructStack* idestack = destack; + + vr = TranslateExpression(procType, proc, bblock, exp->mRight, destack, gotos, BranchTarget(eblock, odestack), BranchTarget(iblock, idestack), inlineMapper); + + UnwindDestructStack(procType, proc, bblock, destack, idestack, inlineMapper); + destack = idestack; + + bblock->Append(jins2); + bblock->Close(iblock, nullptr); + + // increment + if (exp->mLeft->mLeft->mRight) + TranslateExpression(procType, proc, iblock, exp->mLeft->mLeft->mRight, destack, gotos, breakBlock, continueBlock, inlineMapper); + + iblock->Append(jins3); + iblock->Close(lblock, nullptr); + + block = eblock; + + UnwindDestructStack(procType, proc, block, destack, odestack, inlineMapper); + destack = odestack; + + return ExValue(TheVoidTypeDeclaration); + } + + case EX_DO: + { + DestructStack* odestack = destack; + + InterInstruction * jins = new InterInstruction(MapLocation(exp, inlineMapper), IC_JUMP); + + InterCodeBasicBlock* cblock = new InterCodeBasicBlock(proc); + InterCodeBasicBlock* lblock = cblock; + InterCodeBasicBlock* eblock = new InterCodeBasicBlock(proc); + + block->Append(jins); + block->Close(cblock, nullptr); + + DestructStack* idestack = destack; + + vr = TranslateExpression(procType, proc, cblock, exp->mRight, destack, gotos, BranchTarget(eblock, odestack), BranchTarget(cblock, idestack), inlineMapper); + + UnwindDestructStack(procType, proc, cblock, destack, idestack, inlineMapper); + destack = idestack; + + TranslateLogic(procType, proc, cblock, lblock, eblock, exp->mLeft, destack, gotos, inlineMapper); + + block = eblock; + + UnwindDestructStack(procType, proc, block, destack, odestack, inlineMapper); + destack = odestack; + + return ExValue(TheVoidTypeDeclaration); + } + + case EX_LABEL: + { + GotoNode* g = new GotoNode(); + g->mNext = gotos; + g->mExpr = exp; + g->mBlock = new InterCodeBasicBlock(proc); + g->mDestruct = destack; + InterInstruction* jins = new InterInstruction(MapLocation(exp, inlineMapper), IC_JUMP); + block->Append(jins); + block->Close(g->mBlock, nullptr); + block = g->mBlock; + gotos = g; + return ExValue(TheVoidTypeDeclaration); + } + + case EX_GOTO: + { + GotoNode* g = new GotoNode(); + g->mNext = gotos; + g->mExpr = exp; + g->mBlock = block; + g->mDestruct = destack; + InterInstruction* jins = new InterInstruction(MapLocation(exp, inlineMapper), IC_JUMP); + block->Append(jins); + + block = new InterCodeBasicBlock(proc); + gotos = g; + return ExValue(TheVoidTypeDeclaration); + } + + case EX_AGGREGATE: + mErrors->Error(exp->mLocation, EERR_INVALID_INITIALIZER, "Unexpected aggregate"); + return ExValue(TheVoidTypeDeclaration); + + case EX_SWITCH: + { + DestructStack* odestack = destack; + + vl = TranslateExpression(procType, proc, block, exp->mLeft, destack, gotos, breakBlock, continueBlock, inlineMapper); + vl = Dereference(proc, exp, block, inlineMapper, vl); + + int vleft = 0, vright = 65535; + + Declaration* ttype = vl.mType; + + if (vl.mType->mSize == 1) + { + if (vl.mType->mFlags & DTF_SIGNED) + { + vleft = -128; + vright = 127; + } + else + vright = 255; + ttype = TheSignedIntTypeDeclaration; + } + else if (vl.mType->mFlags & DTF_SIGNED) + { + vleft = -32768; + vright = 32767; + } + + vl = CoerceType(proc, exp, block, inlineMapper, vl, ttype); + + InterCodeBasicBlock * dblock = nullptr; + InterCodeBasicBlock* sblock = block; + + block = nullptr; + + InterCodeBasicBlock* eblock = new InterCodeBasicBlock(proc); + + SwitchNodeArray switchNodes({ 0 }); + + Expression* sexp = exp->mRight; + while (sexp) + { + Expression* cexp = sexp->mLeft; + if (cexp->mType == EX_CASE) + { + SwitchNode snode; + snode.mLower = snode.mUpper = 0; + snode.mBlock = nullptr; + + if (cexp->mLeft->mType == EX_CONSTANT && cexp->mLeft->mDecValue->mType == DT_CONST_INTEGER) + snode.mLower = snode.mUpper = int(cexp->mLeft->mDecValue->mInteger); + else if (cexp->mLeft->mType == EX_LIST) + { + Expression* rexp = cexp->mLeft; + + if (rexp->mLeft->mType == EX_CONSTANT && rexp->mLeft->mDecValue->mType == DT_CONST_INTEGER && + rexp->mRight->mType == EX_CONSTANT && rexp->mRight->mDecValue->mType == DT_CONST_INTEGER) + { + snode.mLower = int(rexp->mLeft->mDecValue->mInteger); + snode.mUpper = int(rexp->mRight->mDecValue->mInteger); + } + else + mErrors->Error(cexp->mLeft->mLocation, ERRR_INVALID_CASE, "Integral constant range expected"); + } + else + mErrors->Error(cexp->mLeft->mLocation, ERRR_INVALID_CASE, "Integral constant expected"); + + int i = 0; + while (i < switchNodes.Size() && snode.mLower > switchNodes[i].mLower) + i++; + if (i < switchNodes.Size() && snode.mUpper >= switchNodes[i].mLower) + mErrors->Error(cexp->mLeft->mLocation, ERRR_INVALID_CASE, "Duplicate case constant"); + else + { + if (block && block->mInstructions.Size() == 0) + snode.mBlock = block; + else + { + InterCodeBasicBlock* nblock = new InterCodeBasicBlock(proc); + snode.mBlock = nblock; + + if (block) + { + InterInstruction* jins = new InterInstruction(MapLocation(exp, inlineMapper), IC_JUMP); + + block->Append(jins); + block->Close(nblock, nullptr); + } + + block = nblock; + } + switchNodes.Insert(i, snode); + } + } + else if (cexp->mType == EX_DEFAULT) + { + if (!dblock) + { + dblock = new InterCodeBasicBlock(proc); + + if (block) + { + InterInstruction* jins = new InterInstruction(MapLocation(exp, inlineMapper), IC_JUMP); + + block->Append(jins); + block->Close(dblock, nullptr); + } + + block = dblock; + } + else + mErrors->Error(cexp->mLocation, EERR_DUPLICATE_DEFAULT, "Duplicate default"); + } + + if (cexp->mRight) + TranslateExpression(procType, proc, block, cexp->mRight, destack, gotos, BranchTarget(eblock, odestack), continueBlock, inlineMapper); + + sexp = sexp->mRight; + } + + if (switchNodes.Size() > 0) + { + int i = 1, j = 1; + while (i < switchNodes.Size()) + { + if (switchNodes[i].mBlock == switchNodes[j - 1].mBlock && switchNodes[i].mLower == switchNodes[j - 1].mUpper + 1) + switchNodes[j - 1].mUpper = switchNodes[i].mUpper; + else + switchNodes[j++] = switchNodes[i]; + i++; + } + switchNodes.SetSize(j); + } + + BuildSwitchTree(proc, exp, sblock, inlineMapper, vl, switchNodes, 0, switchNodes.Size(), vleft, vright, ttype->mFlags & DTF_SIGNED, dblock ? dblock : eblock); + + if (block) + { + UnwindDestructStack(procType, proc, block, destack, odestack, inlineMapper); + + InterInstruction* jins = new InterInstruction(MapLocation(exp, inlineMapper), IC_JUMP); + + block->Append(jins); + block->Close(eblock, nullptr); + } + + block = eblock; + + destack = odestack; + + return ExValue(TheVoidTypeDeclaration); + } + case EX_TYPE: + return ExValue(exp->mDecType); + + default: + mErrors->Error(exp->mLocation, EERR_UNIMPLEMENTED, "Unimplemented expression"); + return ExValue(TheVoidTypeDeclaration); + } + } +} + +void InterCodeGenerator::BuildInitializer(InterCodeModule * mod, uint8* dp, int offset, Declaration* data, InterVariable * variable) +{ + if (data->mType == DT_CONST_DATA) + { + memcpy(dp + offset, data->mData, data->mBase->mSize); + } + else if (data->mType == DT_CONST_STRUCT) + { + Declaration* mdec = data->mParams; + while (mdec) + { + BuildInitializer(mod, dp, offset + mdec->mOffset, mdec, variable); + mdec = mdec->mNext; + } + } + else if (data->mType == DT_CONST_INTEGER) + { + int64 t = data->mInteger; + if (data->mBits) + { + if (data->mBits + data->mShift <= 8) + { + uint8 mask = uint8(((1 << data->mBits) - 1) << data->mShift); + dp[offset] = (dp[offset] & ~mask) | (uint8(t << data->mShift) & mask); + } + else if (data->mBits + data->mShift <= 16) + { + uint16 mask = uint16(((1 << data->mBits) - 1) << data->mShift); + + dp[offset + 0] = (dp[offset + 0] & ~(mask & 0xff)) | (uint8(t << data->mShift) & (mask & 0xff)); + dp[offset + 1] = (dp[offset + 1] & ~(mask >> 8)) | (uint8(t >> (8 - data->mShift)) & (mask >> 8)); + } + else + { + uint32 mask = uint32(((1 << data->mBits) - 1) << data->mShift); + t = (t << data->mShift) & mask; + + dp[offset + 0] = (dp[offset + 0] & ~(mask & 0xff)) | (uint8(t )); + dp[offset + 1] = (dp[offset + 1] & ~(mask >> 8)) | (uint8(t >> 8)); + dp[offset + 2] = (dp[offset + 2] & ~(mask >> 16)) | (uint8(t >> 16)); + dp[offset + 3] = (dp[offset + 3] & ~(mask >> 24)) | (uint8(t >> 24)); + } + } + else + { + for (int i = 0; i < data->mBase->mSize; i++) + { + dp[offset + i * data->mBase->mStripe] = uint8(t & 0xff); + t >>= 8; + } + } + } + else if (data->mType == DT_CONST_ADDRESS) + { + int64 t = data->mInteger; + dp[offset + 0] = uint8(t & 0xff); + dp[offset + data->mBase->mStripe] = uint8(t >> 8); + } + else if (data->mType == DT_CONST_FLOAT) + { + union { float f; uint32 i; } cast; + cast.f = float(data->mNumber); + int64 t = cast.i; + for (int i = 0; i < 4; i++) + { + dp[offset + i * data->mBase->mStripe] = t & 0xff; + t >>= 8; + } + } + else if (data->mType == DT_CONST_ASSEMBLER) + { + if (!data->mLinkerObject) + TranslateAssembler(mod, data, nullptr); + + LinkerReference ref; + ref.mObject = variable->mLinkerObject; + ref.mOffset = offset; + ref.mFlags = LREF_LOWBYTE | LREF_HIGHBYTE; + ref.mRefObject = data->mLinkerObject; + ref.mRefOffset = 0; + variable->mLinkerObject->AddReference(ref); + } + else if (data->mType == DT_LABEL) + { + if (!data->mBase->mLinkerObject) + TranslateAssembler(mod, data->mBase, nullptr); + + LinkerReference ref; + ref.mObject = variable->mLinkerObject; + ref.mOffset = offset; + ref.mFlags = LREF_LOWBYTE | LREF_HIGHBYTE; + ref.mRefObject = data->mBase->mLinkerObject; + ref.mRefOffset = int(data->mInteger); + variable->mLinkerObject->AddReference(ref); + } + else if (data->mType == DT_CONST_FUNCTION) + { + assert(false); + } + else if (data->mType == DT_CONST_CONSTRUCTOR) + { + Declaration* dec = new Declaration(data->mLocation, DT_VARIABLE_REF); + dec->mBase = variable->mDeclaration; + dec->mOffset = offset; + DestructStack* destack = nullptr; + GotoNode* gotos = nullptr; + data->mValue->mRight->mDecValue = dec; + dec->mBase->mFlags |= DTF_VAR_ALIASING; + TranslateExpression(nullptr, mMainInitProc, mMainInitBlock, data->mValue, destack, gotos, BranchTarget(), BranchTarget(), nullptr); + } + else if (data->mType == DT_CONST_POINTER) + { + Expression* exp = data->mValue; + Declaration* dec = exp->mDecValue; + + LinkerReference ref; + ref.mObject = variable->mLinkerObject; + ref.mOffset = offset; + ref.mFlags = LREF_LOWBYTE | LREF_HIGHBYTE; + + switch (dec->mType) + { + case DT_CONST_DATA: + { + if (!dec->mLinkerObject) + { + dec->mVarIndex = mod->mGlobalVars.Size(); + InterVariable* var = new InterVariable(); + var->mIndex = dec->mVarIndex; + var->mOffset = 0; + var->mSize = dec->mSize; + if (dec->mFlags & DTF_VAR_ALIASING) + var->mAliased = true; + var->mLinkerObject = mLinker->AddObject(dec->mLocation, dec->mQualIdent, dec->mSection, LOT_DATA, dec->mAlignment); + var->mLinkerObject->mVariable = var; + dec->mLinkerObject = var->mLinkerObject; + var->mLinkerObject->AddData(dec->mData, dec->mSize); + mod->mGlobalVars.Push(var); + } + + ref.mRefObject = dec->mLinkerObject; + ref.mRefOffset = 0; + break; + } + case DT_CONST_FUNCTION: + if (!dec->mLinkerObject) + { + InterCodeProcedure* cproc = this->TranslateProcedure(mod, dec->mValue, dec); + } + + ref.mRefObject = dec->mLinkerObject; + ref.mRefOffset = 0; + break; + + case DT_VARIABLE: + { + if (!dec->mLinkerObject) + { + InitGlobalVariable(mod, dec); + } + + ref.mRefObject = dec->mLinkerObject; + ref.mRefOffset = 0; + } break; + + case DT_VARIABLE_REF: + { + if (!dec->mBase->mLinkerObject) + { + InitGlobalVariable(mod, dec->mBase); + } + + ref.mRefObject = dec->mBase->mLinkerObject; + ref.mRefOffset = dec->mOffset; + } break; + + } + + if (data->mBase->mStripe == 1) + variable->mLinkerObject->AddReference(ref); + else + { + ref.mFlags = LREF_LOWBYTE; + variable->mLinkerObject->AddReference(ref); + ref.mFlags = LREF_HIGHBYTE; + ref.mOffset += data->mBase->mStripe; + variable->mLinkerObject->AddReference(ref); + } + } +} + +void InterCodeGenerator::TranslateLogic(Declaration* procType, InterCodeProcedure* proc, InterCodeBasicBlock* block, InterCodeBasicBlock* tblock, InterCodeBasicBlock* fblock, Expression* exp, DestructStack*& destack, GotoNode*& gotos, InlineMapper* inlineMapper) +{ + switch (exp->mType) + { + case EX_LOGICAL_NOT: + TranslateLogic(procType, proc, block, fblock, tblock, exp->mLeft, destack, gotos, inlineMapper); + break; + case EX_LOGICAL_AND: + { + InterCodeBasicBlock* ablock = new InterCodeBasicBlock(proc); + TranslateLogic(procType, proc, block, ablock, fblock, exp->mLeft, destack, gotos, inlineMapper); + TranslateLogic(procType, proc, ablock, tblock, fblock, exp->mRight, destack, gotos, inlineMapper); + break; + } + case EX_LOGICAL_OR: + { + InterCodeBasicBlock* oblock = new InterCodeBasicBlock(proc); + TranslateLogic(procType, proc, block, tblock, oblock, exp->mLeft, destack, gotos, inlineMapper); + TranslateLogic(procType, proc, oblock, tblock, fblock, exp->mRight, destack, gotos, inlineMapper); + break; + } + default: + { + ExValue vr = TranslateExpression(procType, proc, block, exp, destack, gotos, BranchTarget(), BranchTarget(), inlineMapper); + + if (vr.mType->mType == DT_TYPE_ARRAY) + vr = Dereference(proc, exp, block, inlineMapper, vr, 1); + else + { + vr = Dereference(proc, exp, block, inlineMapper, vr); + if (!vr.mType->IsSimpleType()) + mErrors->Error(exp->mLocation, EERR_INCOMPATIBLE_TYPES, "Not a valid condition value"); + } + + InterInstruction* ins = new InterInstruction(MapLocation(exp, inlineMapper), IC_BRANCH); + ins->mSrc[0].mType = InterTypeOf(vr.mType); + ins->mSrc[0].mTemp = vr.mTemp; + block->Append(ins); + + block->Close(tblock, fblock); + } + + } +} + +static InterCodeGenerator::DestructStack* FindBase(InterCodeGenerator::DestructStack* sfrom, InterCodeGenerator::DestructStack* sto) +{ + while (sfrom) + { + InterCodeGenerator::DestructStack* s = sto; + while (s) + { + if (s == sfrom) + return s; + s = s->mNext; + } + + sfrom = sfrom->mNext; + } + + return sfrom; +} + +InterCodeProcedure* InterCodeGenerator::TranslateProcedure(InterCodeModule * mod, Expression* exp, Declaration * dec) +{ + InterCodeProcedure* proc = new InterCodeProcedure(mod, dec->mLocation, dec->mQualIdent, mLinker->AddObject(dec->mLocation, dec->mQualIdent, dec->mSection, LOT_BYTE_CODE, dec->mAlignment)); + proc->mLinkerObject->mFullIdent = dec->FullIdent(); + +#if 0 + if (proc->mIdent && !strcmp(proc->mIdent->mString, "test")) + exp->Dump(0); +#endif +#if 0 + if (proc->mIdent) + { + printf("TRANS %s\n", proc->mIdent->mString); + exp->Dump(0); + } +#endif + + uint64 outerCompilerOptions = mCompilerOptions; + mCompilerOptions = dec->mCompilerOptions; + + proc->mCompilerOptions = mCompilerOptions; + + + dec->mVarIndex = proc->mID; + dec->mLinkerObject = proc->mLinkerObject; + proc->mNumLocals = dec->mNumVars; + proc->mNumParams = dec->mBase->mSize; + proc->mDeclaration = dec; + + if (dec->mFlags & DTF_NATIVE) + proc->mNativeProcedure = true; + + if (dec->mFlags & DTF_INTERRUPT) + { + proc->mInterrupt = true; + proc->mCompilerOptions &= ~COPT_OPTIMIZE_OUTLINE; + } + + if (dec->mFlags & DTF_INTRINSIC) + proc->mIntrinsicFunction = true; + + if (dec->mFlags & DTF_HWINTERRUPT) + proc->mHardwareInterrupt = true; + + if (dec->mFlags & DTF_FUNC_INTRCALLED) + proc->mInterruptCalled = true; + + if ((dec->mFlags & DTF_DYNSTACK) || (dec->mFlags & DTF_FUNC_RECURSIVE)) + proc->mDynamicStack = true; + + if ((dec->mFlags & DTF_PREVENT_INLINE) || (dec->mBase->mFlags & DTF_VARIADIC)) + proc->mNoInline = true; + + if (dec->mBase->mFlags & DTF_FASTCALL) + { + proc->mFastCallProcedure = true; + + if (dec->mFastCallSize > 0) + { + proc->mFastCallBase = dec->mFastCallBase; + + if (dec->mFastCallBase < BC_REG_FPARAMS_END - BC_REG_FPARAMS && dec->mFastCallSize > dec->mFastCallBase) + { + dec->mLinkerObject->mNumTemporaries = 1; + dec->mLinkerObject->mTemporaries[0] = BC_REG_FPARAMS + dec->mFastCallBase; + if (dec->mFastCallSize < BC_REG_FPARAMS_END - BC_REG_FPARAMS) + dec->mLinkerObject->mTempSizes[0] = dec->mFastCallSize - dec->mFastCallBase; + else + dec->mLinkerObject->mTempSizes[0] = BC_REG_FPARAMS_END - dec->mFastCallBase; + } + + Declaration* pdec = dec->mBase->mParams; + while (pdec) + { + if (!(pdec->mFlags & DTF_FPARAM_UNUSED)) + { + int start = pdec->mVarIndex + BC_REG_FPARAMS, end = start + pdec->mSize; + if (start < BC_REG_FPARAMS_END) + { + if (end > BC_REG_FPARAMS_END) + end = BC_REG_FPARAMS_END; + + int i = 0; + while (i < dec->mLinkerObject->mNumTemporaries && (dec->mLinkerObject->mTemporaries[i] > end || dec->mLinkerObject->mTemporaries[i] + dec->mLinkerObject->mTempSizes[i] < start)) + i++; + if (i < dec->mLinkerObject->mNumTemporaries) + { + if (dec->mLinkerObject->mTemporaries[i] > start) + { + dec->mLinkerObject->mTempSizes[i] += dec->mLinkerObject->mTemporaries[i] - start; + dec->mLinkerObject->mTemporaries[i] = start; + } + if (dec->mLinkerObject->mTemporaries[i] + dec->mLinkerObject->mTempSizes[i] < end) + dec->mLinkerObject->mTempSizes[i] = end - dec->mLinkerObject->mTemporaries[i]; + } + else + { + dec->mLinkerObject->mTemporaries[i] = start; + dec->mLinkerObject->mTempSizes[i] = end - start; + dec->mLinkerObject->mNumTemporaries++; + } + } + } + pdec = pdec->mNext; + } + } + else + proc->mFastCallBase = BC_REG_FPARAMS_END - BC_REG_FPARAMS; + } + else + proc->mFastCallBase = BC_REG_FPARAMS_END - BC_REG_FPARAMS; + + if (dec->mBase->mBase->IsShortIntStruct()) + { + proc->mValueReturn = true; + proc->mReturnType = IT_INT32; + } + else if (dec->mBase->mBase->mType != DT_TYPE_VOID && dec->mBase->mBase->mType != DT_TYPE_STRUCT) + { + proc->mValueReturn = true; + proc->mReturnType = InterTypeOf(dec->mBase->mBase); + } + + InterCodeBasicBlock* entryBlock = new InterCodeBasicBlock(proc); + + if (!strcmp(proc->mIdent->mString, "main")) + { + mMainInitBlock = entryBlock; + mMainInitProc = proc; + entryBlock = new InterCodeBasicBlock(proc); + } + + InterCodeBasicBlock* exitBlock = entryBlock; + + if (dec->mFlags & DTF_DEFINED) + { + if (mCompilerOptions & COPT_VERBOSE2) + printf("Generate intermediate code <%s>\n", proc->mIdent->mString); + +#if 0 + Declaration* pdec = dec->mBase->mParams; + while (pdec) + { + if (pdec->mFlags & DTF_FPARAM_CONST) + { + pdec->mVarIndex = proc->mNumLocals; + proc->mNumLocals ++; + dec->mNumVars++; + + InitParameter(proc, pdec, pdec->mVarIndex + proc->mFastCallBase); + Expression* aexp = new Expression(pdec->mLocation, EX_ASSIGNMENT); + Expression* pexp = new Expression(pdec->mLocation, EX_VARIABLE); + + pexp->mDecType = pdec->mBase; + pexp->mDecValue = pdec; + + aexp->mDecType = pdec->mBase; + aexp->mToken = TK_ASSIGN; + aexp->mLeft = pexp; + aexp->mRight = pdec->mValue; + + TranslateExpression(dec->mBase, proc, exitBlock, aexp, nullptr, nullptr, nullptr); + } + pdec = pdec->mNext; + } +#endif + + for (int i = 0; i < dec->mMayCall.Size(); i++) + { + if (!dec->mMayCall[i]->mLinkerObject) + this->TranslateProcedure(mod, dec->mMayCall[i]->mValue, dec->mMayCall[i]); + proc->AddCalledFunction(mod->mProcedures[dec->mMayCall[i]->mVarIndex]); + } + + DestructStack* destack = nullptr; + GotoNode* gotos = nullptr; + + TranslateExpression(dec->mBase, proc, exitBlock, exp, destack, gotos, BranchTarget(), BranchTarget(), nullptr); + + ConnectGotos(dec->mBase, proc, gotos, nullptr); + + UnwindDestructStack(dec->mBase, proc, exitBlock, destack, nullptr, nullptr); + + if (!strcmp(proc->mIdent->mString, "main")) + { + InterInstruction* zins = new InterInstruction(dec->mLocation, IC_CONSTANT); + zins->mDst.mType = IT_INT16; + zins->mDst.mTemp = proc->AddTemporary(IT_INT16); + zins->mConst.mType = IT_INT16; + zins->mConst.mIntConst = 0; + exitBlock->Append(zins); + + InterInstruction* rins = new InterInstruction(dec->mLocation, IC_RETURN_VALUE); + rins->mSrc[0].mType = IT_INT16; + rins->mSrc[0].mTemp = zins->mDst.mTemp; + exitBlock->Append(rins); + + mMainStartupBlock = entryBlock; + } + } + else + mErrors->Error(dec->mLocation, EERR_UNDEFINED_OBJECT, "Calling undefined function", dec->FullIdent()); + + if (strcmp(proc->mIdent->mString, "main")) + { + InterInstruction* ins = new InterInstruction(exp ? exp->mEndLocation : dec->mLocation, IC_RETURN); + exitBlock->Append(ins); + } + + exitBlock->Close(nullptr, nullptr); + + if (mErrors->mErrorCount == 0 && proc != mMainInitProc) + { + if (mCompilerOptions & COPT_VERBOSE2) + printf("Optimize intermediate code <%s>\n", proc->mIdent->mString); + + proc->Close(); + } + + mCompilerOptions = outerCompilerOptions; + + return proc; +} + +void InterCodeGenerator::CompleteMainInit(InterCodeModule* mod) +{ + if (mErrors->mErrorCount == 0 && mMainInitBlock) + { + InterInstruction* ins = new InterInstruction(mMainInitProc->mLocation, IC_JUMP); + mMainInitBlock->Append(ins); + mMainInitBlock->Close(mMainStartupBlock, nullptr); + + if (mCompilerOptions & COPT_VERBOSE2) + printf("Optimize intermediate code <%s>\n", mMainInitProc->mIdent->mString); + + mMainInitProc->Close(); + } +} diff --git a/oscar64/oscar64/InterCodeGenerator.h b/oscar64/oscar64/InterCodeGenerator.h new file mode 100644 index 0000000..1207b8c --- /dev/null +++ b/oscar64/oscar64/InterCodeGenerator.h @@ -0,0 +1,109 @@ +#pragma once + +#include "Parser.h" +#include "InterCode.h" +#include "Linker.h" +#include "CompilerTypes.h" + +class InterCodeGenerator +{ +public: + InterCodeGenerator(Errors * errors, Linker * linker); + ~InterCodeGenerator(void); + + struct DestructStack + { + Expression * mDestruct; + DestructStack* mNext; + }; + + struct ExValue + { + Declaration* mType; + int mTemp, mReference; + int mBits, mShift; + + ExValue(Declaration* type = nullptr, int temp = -1, int reference = 0, int bits = 0, int shift = 0) + : mType(type), mTemp(temp), mReference(reference), mBits(bits), mShift(shift) + {} + }; + + struct BranchTarget + { + InterCodeBasicBlock* mBlock; + DestructStack* mStack; + + BranchTarget(void) + : mBlock(nullptr), mStack(nullptr) + {} + BranchTarget(InterCodeBasicBlock * block, DestructStack * stack) + : mBlock(block), mStack(stack) + {} + + }; + + uint64 mCompilerOptions; + + InterCodeProcedure* TranslateProcedure(InterCodeModule* mod, Expression* exp, Declaration * dec); + void TranslateAssembler(InterCodeModule* mod, Declaration* adec,GrowingArray * refvars); + void InitGlobalVariable(InterCodeModule* mod, Declaration* dec); + void InitLocalVariable(InterCodeProcedure* proc, Declaration* dec, int index); + void InitParameter(InterCodeProcedure* proc, Declaration* dec, int index); + + void CompleteMainInit(InterCodeModule* mod); +protected: + + Errors* mErrors; + Linker* mLinker; + + struct InlineMapper + { + GrowingArray mParams; + InterCodeBasicBlock * mReturn; + int mResult, mDepth, mVarIndex; + bool mConstExpr; + ExValue * mResultExp; + Location * mLocation; + + InlineMapper(void) + : mParams(-1), mResult(-1), mDepth(0), mResultExp(nullptr) + {} + }; + + struct SwitchNode + { + int mLower, mUpper; + InterCodeBasicBlock * mBlock; + }; + + typedef GrowingArray SwitchNodeArray; + + struct GotoNode + { + GotoNode * mNext; + InterCodeBasicBlock * mBlock; + Expression * mExpr; + DestructStack * mDestruct; + }; + + InterCodeProcedure* mMainInitProc; + InterCodeBasicBlock* mMainInitBlock, *mMainStartupBlock; + + Location MapLocation(Expression * exp, InlineMapper* inlineMapper); + + void BuildSwitchTree(InterCodeProcedure* proc, Expression* exp, InterCodeBasicBlock* block, InlineMapper * inlineMapper, ExValue v, const SwitchNodeArray& nodes, int left, int right, int vleft, int vright, bool csigned, InterCodeBasicBlock* dblock); + + ExValue ToValue(InterCodeProcedure* proc, Expression* exp, InterCodeBasicBlock*& block, InlineMapper* inlineMapper, ExValue v); + ExValue Dereference(InterCodeProcedure* proc, Expression* exp, InterCodeBasicBlock*& block, InlineMapper* inlineMapper, ExValue v, int level = 0, int limit = 0); + ExValue CoerceType(InterCodeProcedure* proc, Expression* exp, InterCodeBasicBlock*& block, InlineMapper* inlineMapper, ExValue v, Declaration * type, bool checkTrunc = true); + ExValue TranslateExpression(Declaration * procType, InterCodeProcedure * proc, InterCodeBasicBlock*& block, Expression* exp, DestructStack*& destack, GotoNode*& gotos, const BranchTarget & breakBlock, const BranchTarget& continueBlock, InlineMapper * inlineMapper, ExValue * lrexp = nullptr); + void TranslateLogic(Declaration* procType, InterCodeProcedure* proc, InterCodeBasicBlock* block, InterCodeBasicBlock* tblock, InterCodeBasicBlock* fblock, Expression* exp, DestructStack*& destack, GotoNode*& gotos, InlineMapper* inlineMapper); + ExValue TranslateInline(Declaration* procType, InterCodeProcedure* proc, InterCodeBasicBlock*& block, Expression* exp, DestructStack*& destack, const BranchTarget& breakBlock, const BranchTarget& continueBlock, InlineMapper* inlineMapper, bool inlineConstexpr, ExValue* lrexp); + void CopyStruct (InterCodeProcedure* proc, Expression* exp, InterCodeBasicBlock*& block, ExValue vl, ExValue vr, InlineMapper* inlineMapper, bool moving); + InterInstruction * CopyStructSimple(InterCodeProcedure* proc, Expression* exp, InterCodeBasicBlock * block, InlineMapper* inlineMapper, ExValue vl, ExValue vr); + void StoreValue(InterCodeProcedure* proc, Expression* exp, InterCodeBasicBlock*& block, InlineMapper* inlineMapper, ExValue vl, ExValue vr); + + void ConnectGotos(Declaration* procType, InterCodeProcedure* proc, GotoNode* gotos, InlineMapper* inlineMapper); + void UnwindDestructStack(Declaration* procType, InterCodeProcedure* proc, InterCodeBasicBlock*& block, DestructStack* stack, DestructStack * bottom, InlineMapper* inlineMapper); + void BuildInitializer(InterCodeModule* mod, uint8 * dp, int offset, Declaration* data, InterVariable * variable); +}; diff --git a/oscar64/oscar64/Linker.cpp b/oscar64/oscar64/Linker.cpp new file mode 100644 index 0000000..678dbb8 --- /dev/null +++ b/oscar64/oscar64/Linker.cpp @@ -0,0 +1,2094 @@ +#include "Linker.h" +#include +#include +#include "CompilerTypes.h" +#include "Compression.h" + +LinkerRegion::LinkerRegion(void) + : mSections(nullptr), mFreeChunks(FreeChunk{ 0, 0 } ), mLastObject(nullptr), mInlayObject(nullptr), mCartridgeBanks(0) +{} + +LinkerSection::LinkerSection(void) + : mObjects(nullptr), mSections(nullptr), mFlags(0) +{} + + +void LinkerSection::RemObject(LinkerObject* obj) +{ + int i = mObjects.IndexOf(obj); + if (i >= 0) + { + mObjects.Remove(i); + obj->mSection = nullptr; + } +} + +void LinkerSection::AddObject(LinkerObject* obj) +{ + mObjects.Push(obj); + obj->mSection = this; +} + +bool LinkerReference::operator==(const LinkerReference& ref) +{ + return + mFlags == ref.mFlags && + mOffset == ref.mOffset && + mRefOffset == ref.mRefOffset && +// mObject->mMapID == ref.mObject->mMapID && + mRefObject->mMapID == ref.mRefObject->mMapID; +} + +bool LinkerReference::operator!=(const LinkerReference& ref) +{ + return !(*this == ref); +} + +LinkerObject::LinkerObject(void) + : mReferences(nullptr), mNumTemporaries(0), mSize(0), mStripe(0), mAlignment(1), mStackSection(nullptr), mIdent(nullptr), mFullIdent(nullptr), mStartUsed(0x10000), mEndUsed(0x00000), mMemory(nullptr) + , mPrefix(nullptr), mSuffix(nullptr), mProc(nullptr), mNativeProc(nullptr), mOwnerProc(nullptr) +{} + +LinkerObject::~LinkerObject(void) +{ + +} + +LinkerObject* LinkerObject::CloneAssembler(Linker* linker) const +{ + LinkerObject* lo = linker->AddObject(mLocation, mIdent, mSection, mType, mAlignment); + lo->AddData(mData, mSize); + lo->mFlags = mFlags; + + for (int i = 0; i < mReferences.Size(); i++) + { + LinkerReference ref = *(mReferences[i]); + if (ref.mObject == this) ref.mObject = lo; + if (ref.mRefObject == this) ref.mRefObject = lo; + lo->AddReference(ref); + } + + return lo; +} + +void LinkerObject::AddReference(const LinkerReference& ref) +{ + LinkerReference* nref = new LinkerReference(ref); + mReferences.Push(nref); +} + +LinkerReference* LinkerObject::FindReference(int64 offset) +{ + for (int i = 0; i < mReferences.Size(); i++) + { + if (mReferences[i]->mOffset == offset) + return mReferences[i]; + else if ((mReferences[i]->mFlags & LREF_LOWBYTE) && (mReferences[i]->mFlags & LREF_HIGHBYTE) && mReferences[i]->mOffset + 1 == offset) + return mReferences[i]; + } + + return nullptr; +} + + +void LinkerObject::MarkRelevant(void) +{ + if (!(mFlags & LOBJF_RELEVANT)) + { + mFlags |= LOBJF_RELEVANT; + for (int i = 0; i < mReferences.Size(); i++) + if (mReferences[i]->mRefObject) + mReferences[i]->mRefObject->MarkRelevant(); + } +} + +void LinkerObject::MoveToSection(LinkerSection* section) +{ + if (section != mSection) + { + if (mSection) + mSection->RemObject(this); + section->AddObject(this); + } +} + +void LinkerObject::AddData(const uint8* data, int size) +{ + mSize = size; + mData = new uint8[size]; + memcpy(mData, data, size); +} + +void LinkerObject::AddLocations(const ExpandingArray& locations) +{ + for (int i = 0; i < locations.Size(); i++) + mCodeLocations.Push(locations[i]); +} + +void LinkerObject::AddOrigins(const ExpandingArray& locations) +{ + for (int i = 0; i < locations.Size(); i++) + mCodeOrigins.Push(locations[i]); +} + + +void LinkerObject::EnsureSpace(int offset, int size) +{ + if (offset + size > mSize) + AddSpace(offset + size); +} + +uint8* LinkerObject::AddSpace(int size) +{ + if (mSize != size) + { + mSize = size; + mData = new uint8[size]; + memset(mData, 0, size); + } + return mData; +} + +LinkerOverlay::LinkerOverlay(void) + : mCompressed(false) +{ + +} + +LinkerOverlay::~LinkerOverlay(void) +{ + +} + +Linker::Linker(Errors* errors) + : mErrors(errors), mSections(nullptr), mReferences(nullptr), mObjects(nullptr), mRegions(nullptr), mOverlays(nullptr), mBreakpoints(0), mCompilerOptions(COPT_DEFAULT) +{ + for (int i = 0; i < 64; i++) + { + mCartridgeBankUsed[i] = 0; + mCartridgeBankStart[i] = 0x10000; + mCartridgeBankEnd[i] = 0x00000; + memset(mCartridge[i], 0, 0x10000); + } + memset(mMemory, 0, 0x10000); +} + +Linker::~Linker(void) +{ + +} + + +LinkerRegion* Linker::AddRegion(const Ident* region, int start, int end) +{ + LinkerRegion* lrgn = new LinkerRegion(); + lrgn->mIdent = region; + lrgn->mStart = start; + lrgn->mReloc = 0; + lrgn->mEnd = end; + lrgn->mUsed = 0; + lrgn->mNonzero = 0; + lrgn->mCartridgeBanks = 0; + lrgn->mFlags = 0; + mRegions.Push(lrgn); + return lrgn; +} + +LinkerRegion* Linker::FindRegion(const Ident* region) +{ + for (int i = 0; i < mRegions.Size(); i++) + { + if (mRegions[i]->mIdent == region) + return mRegions[i]; + } + + return nullptr; +} + +LinkerSection* Linker::AddSection(const Ident* section, LinkerSectionType type) +{ + LinkerSection* lsec = new LinkerSection; + lsec->mIdent = section; + lsec->mType = type; + mSections.Push(lsec); + return lsec; + +} + +LinkerRegion* Linker::FindRegionOfSection(LinkerSection* section) +{ + LinkerRegion* srgn = nullptr; + for (int i = 0; i < mRegions.Size(); i++) + { + LinkerRegion* rgn = mRegions[i]; + if (rgn->mSections.Contains(section)) + { + if (srgn) + return nullptr; + srgn = rgn; + } + } + + return srgn; +} + +LinkerSection* Linker::FindSection(const Ident* section) +{ + for (int i = 0; i < mSections.Size(); i++) + { + if (mSections[i]->mIdent == section) + return mSections[i]; + } + + return nullptr; +} + +LinkerOverlay* Linker::AddOverlay(const Location& location, const Ident* ident, int bank) +{ + LinkerOverlay* lovl = new LinkerOverlay; + lovl->mLocation = location; + lovl->mIdent = ident; + lovl->mBank = bank; + mOverlays.Push(lovl); + + return lovl; +} + +bool Linker::IsSectionPlaced(LinkerSection* section) +{ + for (int i = 0; i < mRegions.Size(); i++) + { + LinkerRegion* rgn = mRegions[i]; + for (int j = 0; j < rgn->mSections.Size(); j++) + if (section == rgn->mSections[j]) + return true; + } + + return false; +} + +LinkerObject* Linker::FindObjectByAddr(int addr, InterCodeProcedure* proc) +{ + if (proc) + { + for (int i = 0; i < mObjects.Size(); i++) + { + LinkerObject* lobj = mObjects[i]; + if (lobj->mFlags & LOBJF_PLACED) + { + if (addr >= lobj->mAddress && addr < lobj->mAddress + lobj->mSize && lobj->mOwnerProc == proc) + return lobj; + } + } + } + + for (int i = 0; i < mObjects.Size(); i++) + { + LinkerObject* lobj = mObjects[i]; + if (lobj->mFlags & LOBJF_PLACED) + { + if (addr >= lobj->mAddress && addr < lobj->mAddress + lobj->mSize) + return lobj; + } + } + + return nullptr; +} + +LinkerObject* Linker::FindObjectByName(const char* name) +{ + for (int i = 0; i < mObjects.Size(); i++) + { + LinkerObject* lobj = mObjects[i]; + if (lobj->mFlags & LOBJF_PLACED) + { + if (lobj->mIdent && !strcmp(lobj->mIdent->mString, name)) + return lobj; + } + } + + return nullptr; +} + +LinkerObject* Linker::FindObjectByAddr(int bank, int addr, InterCodeProcedure* proc) +{ + if (proc) + { + for (int i = 0; i < mObjects.Size(); i++) + { + LinkerObject* lobj = mObjects[i]; + if (lobj->mFlags & LOBJF_PLACED) + { + if (lobj->mRegion && ((1ULL << bank) & lobj->mRegion->mCartridgeBanks)) + { + if (addr >= lobj->mAddress && addr < lobj->mAddress + lobj->mSize && proc == lobj->mOwnerProc) + return lobj; + } + } + } + } + + for (int i = 0; i < mObjects.Size(); i++) + { + LinkerObject* lobj = mObjects[i]; + if (lobj->mFlags & LOBJF_PLACED) + { + if (lobj->mRegion && ((1ULL << bank) & lobj->mRegion->mCartridgeBanks)) + { + if (addr >= lobj->mAddress && addr < lobj->mAddress + lobj->mSize) + return lobj; + } + } + } + + return FindObjectByAddr(addr, proc); +} + +LinkerObject* Linker::FindSame(LinkerObject* obj) +{ + for (int i = 0; i < mObjects.Size(); i++) + { + LinkerObject* lobj = mObjects[i]; + if (lobj != obj && obj->IsSameConst(lobj)) + return lobj; + } + + return nullptr; +} + +bool LinkerObject::IsSameConst(const LinkerObject* obj) const +{ + if ((mFlags & LOBJF_CONST) && mFlags == obj->mFlags && + mSection == obj->mSection && mSize == obj->mSize && mAlignment == obj->mAlignment && + mReferences.Size() == obj->mReferences.Size()) + { + for (int i = 0; i < mSize; i++) + if (mData[i] != obj->mData[i]) + return false; + + for (int i = 0; i < mReferences.Size(); i++) + if (mReferences[i] != obj->mReferences[i]) + return false; + + return true; + } + + return false; +} + +int LinkerObject::FirstBank(void) const +{ + if (mRegion->mCartridgeBanks) + { + uint64 m = mRegion->mCartridgeBanks; + int i = 1; + while (!(m & 1)) + { + i++; + m >>= 1; + } + return i; + } + else + return 0; +} + +bool LinkerObject::IsBefore(const LinkerObject* obj) const +{ + if (mFlags & LOBJF_PLACED) + { + if (obj->mFlags & LOBJF_PLACED) + { + int b0 = FirstBank(), b1 = obj->FirstBank(); + if (b0 < b1) + return true; + else if (b0 == b1) + return mAddress < obj->mAddress; + else + return false; + } + else + return true; + } + else + return false; +} + +LinkerObject * Linker::AddObject(const Location& location, const Ident* ident, LinkerSection * section, LinkerObjectType type, int alignment) +{ + LinkerObject* obj = new LinkerObject; + obj->mLocation = location; + obj->mID = obj->mMapID = mObjects.Size(); + obj->mType = type; + obj->mData = nullptr; + obj->mSize = 0; + obj->mIdent = ident; + obj->mFullIdent = ident; + obj->mSection = section; + obj->mRegion = nullptr; + obj->mProc = nullptr; + obj->mVariable = nullptr; + obj->mFlags = 0; + obj->mAlignment = alignment; + section->mObjects.Push(obj); + mObjects.Push(obj); + return obj; +} + +bool Linker::Forwards(LinkerObject* pobj, LinkerObject* lobj) +{ + if (lobj->mAlignment == 1 && pobj && lobj->mType == LOT_NATIVE_CODE && pobj->mType == LOT_NATIVE_CODE && lobj->mSection == pobj->mSection) + { + if (pobj->mSize >= 3 && pobj->mData[pobj->mSize - 3] == 0x4c && pobj->mReferences.Size() > 0) + { + int i = 0; + while (i < pobj->mReferences.Size() && pobj->mReferences[i]->mOffset != pobj->mSize - 2) + i++; + if (i < pobj->mReferences.Size() && pobj->mReferences[i]->mRefObject == lobj && pobj->mReferences[i]->mRefOffset == 0) + { + if (mCompilerOptions & COPT_VERBOSE2) + printf("Direct %s -> %s\n", pobj->mIdent->mString, lobj->mIdent->mString); + + pobj->mSuffixReference = i; + + return true; + } + } + } + + return false; +} + +void Linker::InlineSimpleJumps(void) +{ + for (int i = 0; i < mObjects.Size(); i++) + { + LinkerObject* cobj(mObjects[i]); + if (cobj->mType == LOT_NATIVE_CODE) + { + for (int j = 0; j < cobj->mReferences.Size(); j++) + { + LinkerReference* cref = cobj->mReferences[j]; + if (cref->mOffset > 0 && cref->mOffset < cobj->mSize + 2 && + cobj->mData[cref->mOffset - 1] == 0x20 && + (cref->mFlags & (LREF_HIGHBYTE | LREF_LOWBYTE)) == (LREF_HIGHBYTE | LREF_LOWBYTE)) + { + if (cref->mRefObject->mType == LOT_NATIVE_CODE && cref->mRefObject->mSize == 3 && + cobj->mSection == cref->mRefObject->mSection && cref->mRefOffset == 0 && + cref->mRefObject->mData[0] == 0x4c) + { + LinkerObject* tobj(cref->mRefObject); + if (tobj->mReferences.Size()) + { + cref->mRefObject = tobj->mReferences[0]->mRefObject; + cref->mRefOffset = tobj->mReferences[0]->mRefOffset; + } + else + { + cobj->mData[cref->mOffset + 0] = tobj->mData[1]; + cobj->mData[cref->mOffset + 1] = tobj->mData[2]; + cref->mFlags = 0; + cref->mRefObject = cobj; + } + } + } + } + } + + } + +} + + +void Linker::CheckDirectJumps(void) +{ + for (int i = 0; i < mObjects.Size(); i++) + { + for (int j = 0; j < mObjects.Size(); j++) + { + if (i != j && !mObjects[j]->mPrefix && Forwards(mObjects[i], mObjects[j])) + { + mObjects[i]->mSuffix = mObjects[j]; + mObjects[j]->mPrefix = mObjects[i]; + break; + } + } + } +} + +void Linker::CombineSameConst(void) +{ + bool changed = true; + while (changed) + { + changed = false; + + for (int i = 0; i < mObjects.Size(); i++) + { + LinkerObject* dobj(mObjects[i]); + while (dobj->mMapID != mObjects[dobj->mMapID]->mMapID) + dobj->mMapID = mObjects[dobj->mMapID]->mMapID; + + if ((dobj->mFlags & LOBJF_REFERENCED) && (dobj->mFlags & LOBJF_CONST) && dobj->mMapID == dobj->mID && dobj->mType != LOT_INLAY) + { + for (int j = i + 1; j < mObjects.Size(); j++) + { + LinkerObject* sobj(mObjects[j]); + + if ((sobj->mFlags & LOBJF_REFERENCED) && (sobj->mFlags & LOBJF_CONST) && sobj->mMapID == sobj->mID && sobj->mType != LOT_INLAY) + { + if (dobj->mSize == sobj->mSize && dobj->mSection == sobj->mSection && dobj->mReferences.Size() == sobj->mReferences.Size()) + { + int i = 0; + while (i < sobj->mSize && sobj->mData[i] == dobj->mData[i]) + i++; + if (i == sobj->mSize) + { + i = 0; + while (i < sobj->mReferences.Size() && *sobj->mReferences[i] == *dobj->mReferences[i]) + i++; + if (i == sobj->mReferences.Size()) + { + sobj->mMapID = dobj->mMapID; + changed = true; + + if (dobj->mIdent && sobj->mIdent && (mCompilerOptions & COPT_VERBOSE2)) + { + printf("Match %s : %s\n", dobj->mIdent->mString, sobj->mIdent->mString); + } + } + } + } + } + } + } + } + } + + for (int i = 0; i < mObjects.Size(); i++) + { + LinkerObject* lobj(mObjects[i]); + if (lobj->mFlags & LOBJF_REFERENCED) + { + if (lobj->mMapID != lobj->mID) + lobj->mFlags &= ~LOBJF_REFERENCED; + else + { + for (int j = 0; j < lobj->mReferences.Size(); j++) + lobj->mReferences[j]->mRefObject = mObjects[lobj->mReferences[j]->mRefObject->mMapID]; + } + } + } +} + +void Linker::CollectReferences(void) +{ + for (int i = 0; i < mObjects.Size(); i++) + { + LinkerObject* lobj(mObjects[i]); + for (int j = 0; j < lobj->mReferences.Size(); j++) + mReferences.Push(lobj->mReferences[j]); + } +} + +void Linker::ReferenceObject(LinkerObject* obj) +{ + if (!(obj->mFlags & LOBJF_REFERENCED)) + { + obj->mFlags |= LOBJF_REFERENCED; + for (int i = 0; i < mReferences.Size(); i++) + { + LinkerReference* ref = mReferences[i]; + if (ref->mObject == obj) + ReferenceObject(ref->mRefObject); + } + } +} + +bool LinkerRegion::AllocateAppend(Linker* linker, LinkerObject* lobj) +{ + if (lobj->mPrefix && (lobj->mPrefix->mFlags & LOBJF_PLACED)) + { + if (lobj->mPrefix == mLastObject) + { + int start = mStart + mUsed - 3; + int end = start + lobj->mSize; + + if (end <= mEnd) + { + lobj->mPrefix->mReferences[lobj->mPrefix->mSuffixReference]->mFlags = 0; + lobj->mPrefix->mSize -= 3; + + lobj->mFlags |= LOBJF_PLACED; + lobj->mAddress = start; + lobj->mRefAddress = start + mReloc; + lobj->mRegion = this; + mUsed = end - mStart; + + mLastObject = lobj; + + if (lobj->mSuffix && !(lobj->mSuffix->mFlags & LOBJF_PLACED)) + { + if (!Allocate(linker, lobj->mSuffix, true, false)) + return false; + } + + return true; + } + } + else + { + int i = 0; + while (i < mFreeChunks.Size() && lobj->mPrefix != mFreeChunks[i].mLastObject) + i++; + if (i < mFreeChunks.Size()) + { + int start = mFreeChunks[i].mStart - 3; + int end = start + lobj->mSize; + + if (end <= mFreeChunks[i].mEnd) + { + lobj->mPrefix->mReferences[lobj->mPrefix->mSuffixReference]->mFlags = 0; + lobj->mPrefix->mSize -= 3; + + lobj->mFlags |= LOBJF_PLACED; + lobj->mAddress = start; + lobj->mRefAddress = start + mReloc; + lobj->mRegion = this; + + if (end == mFreeChunks[i].mEnd) + mFreeChunks.Remove(i); + else + { + mFreeChunks[i].mStart = end; + mFreeChunks[i].mLastObject = lobj; + } + + if (lobj->mSuffix && !(lobj->mSuffix->mFlags & LOBJF_PLACED)) + { + if (!Allocate(linker, lobj->mSuffix, true, false)) + return false; + } + + return true; + } + } + } + } + return false; +} + +bool LinkerRegion::Allocate(Linker * linker, LinkerObject* lobj, bool merge, bool retry) +{ + if (merge && lobj->mPrefix) + { + if (!(lobj->mPrefix->mFlags & LOBJF_PLACED)) + { + if (!Allocate(linker, lobj->mPrefix, true, false)) + return false; + + if (lobj->mFlags & LOBJF_PLACED) + return true; + } + + if (AllocateAppend(linker, lobj) || (lobj->mFlags & LOBJF_PLACED)) + return true; + } + + int i = 0; + while (i < mFreeChunks.Size()) + { + int start = (mFreeChunks[i].mStart + lobj->mAlignment - 1) & ~(lobj->mAlignment - 1); + int end = start + lobj->mSize; + + if (((lobj->mFlags & LOBJF_NEVER_CROSS) || !(linker->mCompilerOptions & COPT_OPTIMIZE_CODE_SIZE) && (lobj->mFlags & LOBJF_NO_CROSS) && !(lobj->mSection->mFlags & LSECF_PACKED)) && lobj->mSize <= 256 && (start & 0xff00) != ((end - 1) & 0xff00)) + ; + else if (end <= mFreeChunks[i].mEnd) + { + if (merge && lobj->mPrefix && lobj->mPrefix == mFreeChunks[i].mLastObject) + { + lobj->mPrefix->mReferences[lobj->mPrefix->mSuffixReference]->mFlags = 0; + lobj->mPrefix->mSize -= 3; + + start -= 3; + end -= 3; + } + + lobj->mFlags |= LOBJF_PLACED; + lobj->mAddress = start; + lobj->mRefAddress = start + mReloc; + lobj->mRegion = this; + + if (start == mFreeChunks[i].mStart) + { + if (end == mFreeChunks[i].mEnd) + mFreeChunks.Remove(i); + else + { + mFreeChunks[i].mStart = end; + mFreeChunks[i].mLastObject = lobj; + } + } + else if (end == mFreeChunks[i].mEnd) + { + mFreeChunks[i].mEnd = start; + } + else + { + mFreeChunks.Insert(i + 1, FreeChunk{ end, mFreeChunks[i].mEnd, lobj }); + mFreeChunks[i].mEnd = start; + } + + if (merge && lobj->mSuffix && !(lobj->mSuffix->mFlags & LOBJF_PLACED)) + { + if (!Allocate(linker, lobj->mSuffix, true, false)) + return false; + } + + return true; + } + i++; + } + + int start = (mStart + mUsed + lobj->mAlignment - 1) & ~(lobj->mAlignment - 1); + int end = start + lobj->mSize; + + if (((lobj->mFlags & LOBJF_NEVER_CROSS) || !(linker->mCompilerOptions & COPT_OPTIMIZE_CODE_SIZE) && !retry && (lobj->mFlags & LOBJF_NO_CROSS) && !(lobj->mSection->mFlags & LSECF_PACKED)) && !(lobj->mFlags & LOBJF_FORCE_ALIGN) && lobj->mSize <= 256 && (start & 0xff00) != ((end - 1) & 0xff00)) + { + start = (start + 0x00ff) & 0xff00; + end = start + lobj->mSize; + } + + if (end <= mEnd) + { + // Check if directly follows an object that jumps to this new object + if (merge && lobj->mPrefix && lobj->mPrefix == mLastObject) + { + lobj->mPrefix->mReferences[lobj->mPrefix->mSuffixReference]->mFlags = 0; + lobj->mPrefix->mSize -= 3; + + start -= 3; + end -= 3; + mLastObject = nullptr; + } + + lobj->mFlags |= LOBJF_PLACED; + lobj->mAddress = start; + lobj->mRefAddress = start + mReloc; + lobj->mRegion = this; + +#if 1 + if (start != mStart + mUsed) + mFreeChunks.Push( FreeChunk{ mStart + mUsed, start, mLastObject } ); +#endif + mUsed = end - mStart; + + mLastObject = lobj; + + if (merge && lobj->mSuffix && !(lobj->mSuffix->mFlags & LOBJF_PLACED)) + { + if (!Allocate(linker, lobj->mSuffix, true, false)) + return false; + } + + return true; + } + + return false; +} + +void LinkerRegion::PlaceStackSection(LinkerSection* stackSection, LinkerSection* section) +{ + if (!section->mEnd && !(section->mFlags & LSECF_PLACED)) + { + section->mFlags |= LSECF_PLACED; + + int start = stackSection->mEnd; + + for (int i = 0; i < section->mSections.Size(); i++) + { + PlaceStackSection(stackSection, section->mSections[i]); + if (section->mSections[i]->mStart < start) + { + start = section->mSections[i]->mStart; + section->mStart = start; + } + } + + section->mStart = start; + section->mEnd = start; + + for (int i = 0; i < section->mObjects.Size(); i++) + { + LinkerObject* lobj = section->mObjects[i]; + if (lobj->mFlags & LOBJF_REFERENCED) + { + int start = (section->mStart - lobj->mSize) & ~(lobj->mAlignment - 1); + section->mSize += section->mStart - start; + section->mStart = start; + + lobj->mFlags |= LOBJF_PLACED; + lobj->mAddress = section->mStart; + lobj->mRefAddress = section->mStart + mReloc; + lobj->mRegion = this; + } + } + + if (stackSection->mStart > section->mStart) + stackSection->mStart = section->mStart; + } +} + +void Linker::CopyObjects(bool inlays) +{ + bool errors = false; + + for (int i = 0; i < mObjects.Size(); i++) + { + LinkerObject* obj = mObjects[i]; + if (obj->mType == LOT_SECTION_START) + { + obj->mAddress = obj->mSection->mStart; + obj->mRefAddress = obj->mAddress + (obj->mRegion ? obj->mRegion->mReloc : 0); + } + else if (obj->mType == LOT_SECTION_END) + { + obj->mAddress = obj->mSection->mEnd; + obj->mRefAddress = obj->mAddress + (obj->mRegion ? obj->mRegion->mReloc : 0); + } + else if (obj->mFlags & LOBJF_REFERENCED) + { + if (inlays) + { + if (obj->mRegion && obj->mRegion->mInlayObject) + { + LinkerObject* iobj = obj->mRegion->mInlayObject; + if (!iobj->mMemory) + { + iobj->mMemory = new uint8[0x10000]; + memset(iobj->mMemory, 0, 0x10000); + } + + memcpy(iobj->mMemory + obj->mAddress, obj->mData, obj->mSize); + if (obj->mAddress < iobj->mStartUsed) + iobj->mStartUsed = obj->mAddress; + if (obj->mAddress + obj->mSize > iobj->mEndUsed) + iobj->mEndUsed = obj->mAddress + obj->mSize; + } + } + else + { + if (!obj->mRegion) + { + mErrors->Error(obj->mLocation, errors ? EWARN_INSUFFICIENT_MEMORY : ERRR_INSUFFICIENT_MEMORY, "Could not place object", obj->mIdent); + if (mCompilerOptions & COPT_ERROR_FILES) + errors = true; + + int avail = 0; + for (int i = 0; i < mRegions.Size(); i++) + { + if (mRegions[i]->mSections.Contains(obj->mSection)) + { + if (mRegions[i]->mEnd - mRegions[i]->mStart - mRegions[i]->mUsed > avail) + avail = mRegions[i]->mEnd - mRegions[i]->mStart - mRegions[i]->mUsed; + } + } + char buffer[200]; + sprintf_s(buffer, "Size %d Available %d in section", obj->mSize, avail); + mErrors->Error(obj->mLocation, EINFO_SIZE, buffer, obj->mSection->mIdent); + } + else if (obj->mRegion->mInlayObject) + ; + else if (obj->mRegion && obj->mRegion->mCartridgeBanks != 0) + { + for (int i = 0; i < 64; i++) + { + if (obj->mRegion->mCartridgeBanks & (1ULL << i)) + { + mCartridgeBankUsed[i] = true; + memcpy(mCartridge[i] + obj->mAddress, obj->mData, obj->mSize); + if (obj->mAddress < mCartridgeBankStart[i]) + mCartridgeBankStart[i] = obj->mAddress; + if (obj->mAddress + obj->mSize > mCartridgeBankEnd[i]) + mCartridgeBankEnd[i] = obj->mAddress + obj->mSize; + } + } + } + else if (obj->mSection->mType == LST_DATA) + { + memcpy(mMemory + obj->mAddress, obj->mData, obj->mSize); + } + } + } + } +} + +void Linker::CollectBreakpoints(void) +{ + for (int i = 0; i < mReferences.Size(); i++) + { + LinkerReference* ref = mReferences[i]; + if (ref->mFlags & LREF_BREAKPOINT) + { + LinkerObject* obj = ref->mObject; + if (obj->mFlags & LOBJF_REFERENCED) + { + if (obj->mRegion) + { + if (obj->mRegion->mCartridgeBanks) + { + } + else + { + mBreakpoints.Push(obj->mAddress + ref->mOffset); + } + } + } + } + } + +} + +void Linker::PatchReferences(bool inlays) +{ + for (int i = 0; i < mReferences.Size(); i++) + { + LinkerReference* ref = mReferences[i]; + LinkerObject* obj = ref->mObject; + if (obj->mFlags & LOBJF_REFERENCED) + { + if (obj->mRegion) + { + LinkerObject* robj = ref->mRefObject; + + int raddr = robj->mRefAddress + ref->mRefOffset; + uint8* dp; + + if (inlays) + { + if (obj->mRegion->mInlayObject) + { + LinkerObject* iobj = obj->mRegion->mInlayObject; + + dp = iobj->mMemory + obj->mAddress + ref->mOffset; + + if (ref->mFlags & LREF_LOWBYTE) + { + *dp++ = raddr & 0xff; + } + if (ref->mFlags & LREF_HIGHBYTE) + { + *dp++ = (raddr >> 8) & 0xff; + } + if (ref->mFlags & LREF_TEMPORARY) + *dp += obj->mTemporaries[ref->mRefOffset]; + } + } + else if (!obj->mRegion->mInlayObject) + { + if (obj->mRegion->mCartridgeBanks) + { + for (int i = 0; i < 64; i++) + { + if (obj->mRegion->mCartridgeBanks & (1ULL << i)) + { + dp = mCartridge[i] + obj->mAddress + ref->mOffset; + + if (ref->mFlags & LREF_LOWBYTE) + { + *dp++ = raddr & 0xff; + } + if (ref->mFlags & LREF_HIGHBYTE) + { + *dp++ = (raddr >> 8) & 0xff; + } + if (ref->mFlags & LREF_TEMPORARY) + *dp += obj->mTemporaries[ref->mRefOffset]; + } + } + } + else + { + dp = mMemory + obj->mAddress + ref->mOffset; + + if (ref->mFlags & LREF_LOWBYTE) + { + *dp++ = raddr & 0xff; + } + if (ref->mFlags & LREF_HIGHBYTE) + { + *dp++ = (raddr >> 8) & 0xff; + } + if (ref->mFlags & LREF_TEMPORARY) + *dp += obj->mTemporaries[ref->mRefOffset]; + } + } + } + } + } +} + +void Linker::PlaceObjects(bool retry) +{ + for (int i = 0; i < mRegions.Size(); i++) + { + LinkerRegion* lrgn = mRegions[i]; + for (int j = 0; j < lrgn->mSections.Size(); j++) + { + LinkerSection* lsec = lrgn->mSections[j]; + for (int k = 0; k < lsec->mObjects.Size(); k++) + { + LinkerObject* lobj = lsec->mObjects[k]; + if (lobj->mType != LOT_INLAY && (lobj->mFlags & LOBJF_REFERENCED) && !(lobj->mFlags & LOBJF_PLACED) && lrgn->Allocate(this, lobj, mCompilerOptions & COPT_OPTIMIZE_MERGE_CALLS, retry)) + { + if (lobj->mIdent && lobj->mIdent->mString && (mCompilerOptions & COPT_VERBOSE2)) + printf("Placed object <%s> $%04x - $%04x\n", lobj->mIdent->mString, lobj->mAddress, lobj->mAddress + lobj->mSize); + + if (lobj->mAddress < lsec->mStart) + lsec->mStart = lobj->mAddress; + if (lobj->mAddress + lobj->mSize > lsec->mEnd) + lsec->mEnd = lobj->mAddress + lobj->mSize; + + if (lsec->mType == LST_DATA && lsec->mEnd > lrgn->mNonzero) + lrgn->mNonzero = lsec->mEnd; + } + } + } + } +} + +void Linker::SortObjectsPartition(int l, int r) +{ + while (l < r) + { + int pi = (l + r) >> 1; + + LinkerObject* po = mObjects[pi]; + mObjects[pi] = mObjects[l]; + + pi = l; + for (int i = l + 1; i < r; i++) + { + if (mObjects[i]->IsBefore(po)) + { + mObjects[pi++] = mObjects[i]; + mObjects[i] = mObjects[pi]; + } + } + mObjects[pi] = po; + + SortObjectsPartition(l, pi); + l = pi + 1; + } +} + +void Linker::SortObjects(void) +{ + SortObjectsPartition(0, mObjects.Size()); +} + +void Linker::Link(void) +{ + if (mErrors->mErrorCount == 0) + { + + for (int i = 0; i < mSections.Size(); i++) + { + LinkerSection* lsec = mSections[i]; + lsec->mStart = 0x10000; + lsec->mEnd = 0x0000; + } + + // Move objects into regions + PlaceObjects(false); + + // Retry for alignment + PlaceObjects(true); + + // Place stack segment + + for (int i = 0; i < mRegions.Size(); i++) + { + LinkerRegion* lrgn = mRegions[i]; + for (int j = 0; j < lrgn->mSections.Size(); j++) + { + LinkerSection* lsec = lrgn->mSections[j]; + + if (lsec->mType == LST_STACK) + { + lsec->mStart = lsec->mEnd = lrgn->mEnd; + lrgn->mEnd = lsec->mStart - lsec->mSize; + + for (int i = 0; i < lsec->mSections.Size(); i++) + lrgn->PlaceStackSection(lsec, lsec->mSections[i]); + + if (lsec->mStart < lrgn->mEnd) + { + Location loc; + mErrors->Error(loc, ERRR_INSUFFICIENT_MEMORY, "Static stack usage exceeds stack segment"); + } + + lsec->mEnd = lsec->mStart; + lsec->mStart = lrgn->mEnd; + + if (lsec->mStart < lrgn->mStart + lrgn->mUsed) + { + Location loc; + mErrors->Error(loc, ERRR_INSUFFICIENT_MEMORY, "Cannot place stack section"); + } + } + } + } + + CopyObjects(true); + PatchReferences(true); + + // Move inlays into regions + + for (int i = 0; i < mRegions.Size(); i++) + { + LinkerRegion* lrgn = mRegions[i]; + if (lrgn->mInlayObject) + { + LinkerObject* iobj = lrgn->mInlayObject; + + int size = CompressLZO(mWorkspace, iobj->mMemory + iobj->mStartUsed, iobj->mEndUsed - iobj->mStartUsed); + iobj->AddData(mWorkspace, size); + iobj->mType = LOT_DATA; + } + } + + PlaceObjects(false); + + // Calculate BSS storage + + for (int i = 0; i < mRegions.Size(); i++) + { + LinkerRegion* lrgn = mRegions[i]; + for (int j = 0; j < lrgn->mSections.Size(); j++) + { + LinkerSection* lsec = lrgn->mSections[j]; + if (lsec->mType == LST_BSS && lsec->mStart < lrgn->mNonzero) + lsec->mStart = lrgn->mNonzero; + if (lsec->mEnd < lsec->mStart) + lsec->mEnd = lsec->mStart; + } + } + + mProgramStart = 0xffff; + mProgramEnd = 0x0000; + + for (int i = 0; i < mRegions.Size(); i++) + { + LinkerRegion* lrgn = mRegions[i]; + + if (lrgn->mNonzero && lrgn->mCartridgeBanks == 0 && !lrgn->mInlayObject) + { + if (lrgn->mStart < mProgramStart) + mProgramStart = lrgn->mStart; + if (lrgn->mNonzero > mProgramEnd) + mProgramEnd = lrgn->mNonzero; + } + } + + // Now expand the heap section to cover the remainder of the region + + for (int i = 0; i < mRegions.Size(); i++) + { + LinkerRegion* lrgn = mRegions[i]; + for (int j = 0; j < lrgn->mSections.Size(); j++) + { + LinkerSection* lsec = lrgn->mSections[j]; + + if (lsec->mType == LST_HEAP) + { + lsec->mStart = (lrgn->mStart + lrgn->mUsed + 7) & ~7; + lsec->mEnd = lrgn->mEnd & ~7; + + if (lsec->mStart + lsec->mSize > lsec->mEnd) + { + Location loc; + mErrors->Error(loc, ERRR_INSUFFICIENT_MEMORY, "Cannot place heap section"); + } + } + } + } + + // Second patch of references + + CopyObjects(false); + PatchReferences(false); + CollectBreakpoints(); + + for (int i = 0; i < mObjects.Size(); i++) + { + LinkerObject* oi = mObjects[i]; + + if (oi->mSection->mType == LST_DATA && (oi->mFlags & LOBJF_PLACED) && oi->mRegion) + { + for (int j = i + 1; j < mObjects.Size(); j++) + { + LinkerObject* oj = mObjects[j]; + + if (oj->mSection->mType == LST_DATA && (oj->mFlags & LOBJF_PLACED) && oj->mRegion) + { + if (oj->mAddress < oi->mAddress + oi->mSize && oi->mAddress < oj->mAddress + oj->mSize && (oj->mRegion->mCartridgeBanks & oi->mRegion->mCartridgeBanks)) + { + mErrors->Error(oi->mLocation, EERR_OVERLAPPING_DATA_SECTIONS, "Overlapping data section", oi->mIdent); + mErrors->Error(oj->mLocation, EERR_OVERLAPPING_DATA_SECTIONS, "Overlapping data section", oj->mIdent); + } + } + } + } + } + } + + SortObjects(); +} + +static const char * LinkerObjectTypeNames[] = +{ + "NONE", + "PAD", + "BASIC", + "BYTE_CODE", + "NATIVE_CODE", + "RUNTIME", + "DATA", + "BSS", + "HEAP", + "STACK", + "INLAY", + "START", + "END" +}; + +static const char* LinkerSectionTypeNames[] = { + "NONE", + "DATA", + "BSS", + "HEAP", + "STACK", + "SSTACK", + "ZEROPAGE" +}; + +bool Linker::WriteBinFile(const char* filename) +{ + FILE* file; + fopen_s(&file, filename, "wb"); + if (file) + { + ptrdiff_t done = fwrite(mMemory + mProgramStart, 1, mProgramEnd - mProgramStart, file); + fclose(file); + return done == mProgramEnd - mProgramStart; + } + else + return false; +} + +bool Linker::WriteNesFile(const char* filename, TargetMachine machine) +{ + FILE* file; + fopen_s(&file, filename, "wb"); + if (file) + { + char header[16] = { 0x4e, 0x45, 0x53, 0x1a, 0x02, 0x01, 0x01, 0x00, 0x02, 0x00, 0x00 }; + + switch (machine) + { + case TMACH_NES: + header[6] = 0x08; + break; + case TMACH_NES_NROM_H: + header[6] = 0x00; + break; + case TMACH_NES_NROM_V: + header[6] = 0x01; + break; + case TMACH_NES_MMC1: + header[4] = 16; + header[5] = 16; + header[6] = 0x10; + break; + case TMACH_NES_MMC3: + header[4] = 32; + header[5] = 32; + header[6] = 0x48; + break; + } + + ptrdiff_t done = fwrite(header, 1, 16, file); + + switch (machine) + { + case TMACH_NES: + case TMACH_NES_NROM_H: + case TMACH_NES_NROM_V: + fwrite(mCartridge[0] + 0x8000, 1, 0x8000, file); + fwrite(mCartridge[0], 1, 0x2000, file); + break; + case TMACH_NES_MMC1: + for(int i=0; i<15; i++) + fwrite(mCartridge[i] + 0x8000, 1, 0x4000, file); + fwrite(mCartridge[15] + 0xc000, 1, 0x4000, file); + for (int i = 0; i < 16; i++) + fwrite(mCartridge[i], 1, 0x2000, file); + break; + case TMACH_NES_MMC3: + for (int i = 0; i < 31; i++) + fwrite(mCartridge[i] + 0x8000, 1, 0x4000, file); + fwrite(mCartridge[31] + 0xc000, 1, 0x4000, file); + for (int i = 0; i < 32; i++) + fwrite(mCartridge[i], 1, 0x2000, file); + break; + } + + fclose(file); + return done == 16; + } + else + return false; +} + +bool Linker::WritePrgFile(DiskImage* image, const char* filename) +{ + if (image->OpenFile(filename)) + { + mMemory[mProgramStart - 2] = mProgramStart & 0xff; + mMemory[mProgramStart - 1] = mProgramStart >> 8; + + image->WriteBytes(mMemory + mProgramStart - 2, mProgramEnd - mProgramStart + 2); + image->CloseFile(); + + for (int i = 0; i < mOverlays.Size(); i++) + { + if (image->OpenFile(mOverlays[i]->mIdent->mString)) + { + int b = mOverlays[i]->mBank; + int s = mCartridgeBankStart[b]; + + if (mOverlays[i]->mCompressed) + { + uint8 tbuffer[65536]; + tbuffer[0] = s & 0xff; + tbuffer[1] = s >> 8; + int tsize = CompressLZO(tbuffer + 2, mCartridge[b] + s, mCartridgeBankEnd[b] - s); + image->WriteBytes(tbuffer, tsize + 2); + } + else + { + mCartridge[b][s - 2] = s & 0xff; + mCartridge[b][s - 1] = s >> 8; + image->WriteBytes(mCartridge[b] + s - 2, mCartridgeBankEnd[b] - s + 2); + } + + image->CloseFile(); + } + else + return false; + } + + return true; + } + + return false; +} + +bool Linker::WriteXexFile(const char* filename) +{ + FILE* file; + fopen_s(&file, filename, "wb"); + if (file) + { + // prefix + fputc(0xff, file); fputc(0xff, file); + + // first segment + fputc(mProgramStart & 0xff, file); + fputc(mProgramStart >> 8, file); + fputc((mProgramEnd - 1) & 0xff, file); + fputc((mProgramEnd - 1) >> 8, file); + + ptrdiff_t done = fwrite(mMemory + mProgramStart, 1, mProgramEnd - mProgramStart, file); + + fputc(0xe0, file); + fputc(0x02, file); + fputc(0xe1, file); + fputc(0x02, file); + fputc(mProgramStart & 0xff, file); + fputc(mProgramStart >> 8, file); + + fclose(file); + return true; + } + else + return false; +} + +bool Linker::WritePrgFile(const char* filename, const char* pathname) +{ + FILE* file; + fopen_s(&file, filename, "wb"); + if (file) + { + mMemory[mProgramStart - 2] = mProgramStart & 0xff; + mMemory[mProgramStart - 1] = mProgramStart >> 8; + + ptrdiff_t done = fwrite(mMemory + mProgramStart - 2, 1, mProgramEnd - mProgramStart + 2, file); + fclose(file); + if (done == mProgramEnd - mProgramStart + 2) + { + for (int i = 0; i < mOverlays.Size(); i++) + { + char ofname[200]; + strcpy_s(ofname, pathname); + strcat_s(ofname, mOverlays[i]->mIdent->mString); + strcat_s(ofname, ".prg"); + + fopen_s(&file, ofname, "wb"); + if (file) + { + int b = mOverlays[i]->mBank; + int s = mCartridgeBankStart[b]; + + + if (mOverlays[i]->mCompressed) + { + uint8 tbuffer[65536]; + tbuffer[0] = s & 0xff; + tbuffer[1] = s >> 8; + int tsize = CompressLZO(tbuffer + 2, mCartridge[b] + s, mCartridgeBankEnd[b] - s); + fwrite(tbuffer, 1, tsize + 2, file); + } + else + { + mCartridge[b][s - 2] = s & 0xff; + mCartridge[b][s - 1] = s >> 8; + fwrite(mCartridge[b] + s - 2, 1, mCartridgeBankEnd[b] - s + 2, file); + } + fclose(file); + } + else + return false; + } + + return true; + } + } + + return false; +} + +static int memlzcomp(uint8 * dp, const uint8 * sp, int size) +{ + int pos = 0, csize = 0; + while (pos < size) + { + int pi = 0; + while (pi < 127 && pos < size) + { + int bi = pi, bj = 0; + for (int i = 1; i < (pos < 255 ? pos : 255); i++) + { + int j = 0; + while (j < 127 && pos + j < size && sp[pos - i + j] == sp[pos + j]) + j++; + + if (j > bj) + { + bi = i; + bj = j; + } + } + + if (bj >= 4) + { + if (pi > 0) + { + dp[csize++] = pi; + for (int i = 0; i < pi; i++) + dp[csize++] = sp[pos - pi + i]; + pi = 0; + } + + dp[csize++] = 128 + bj; + dp[csize++] = bi; + pos += bj; + } + else + { + pos++; + pi++; + } + } + + if (pi > 0) + { + dp[csize++] = pi; + for (int i = 0; i < pi; i++) + dp[csize++] = sp[pos - pi + i]; + } + } + + dp[csize++] = 0; + + return csize; +} + +static uint16 flip16(uint16 w) +{ + return (w >> 8) | (w << 8); +} + +static uint32 flip32(uint32 d) +{ + return uint32(flip16(uint16(d >> 16))) | (uint32(flip16(uint16(d))) << 16); +} + +bool Linker::WriteCrtFile(const char* filename, uint16 id, uint8 subtype, const char* cname) +{ + FILE* file; + fopen_s(&file, filename, "wb"); + if (file) + { + struct CRIHeader + { + char mSignature[16]; + uint32 mHeaderLength; + uint16 mVersion; + uint8 mIDHi, mIDLo; + uint8 mExrom, mGameLine; + uint8 mSubType; + uint8 mPad[5]; + char mName[32]; + } criHeader = { 0 }; + + memcpy(criHeader.mSignature, "C64 CARTRIDGE ", 16); + criHeader.mHeaderLength = 0x40000000; + criHeader.mVersion = 0x0001; + criHeader.mIDHi = uint8(id >> 8); + criHeader.mIDLo = uint8(id & 0xff); + criHeader.mSubType = subtype; + + if (mCompilerOptions & COPT_TARGET_CRT8) + { + criHeader.mExrom = 0; + criHeader.mGameLine = 1; + } + else if (mCompilerOptions & COPT_TARGET_CRT16) + { + criHeader.mExrom = 0; + criHeader.mGameLine = 0; + } + else + { + criHeader.mExrom = 0; + criHeader.mGameLine = 0; + } + + memset(criHeader.mName, 0, 32); + strcpy_s(criHeader.mName, cname); + + fwrite(&criHeader, sizeof(CRIHeader), 1, file); + + struct CHIPHeader + { + char mSignature[4]; + uint32 mPacketLength; + uint16 mChipType, mBankNumber, mLoadAddress, mImageSize; + } chipHeader = { 0 }; + + + if (mCompilerOptions & COPT_TARGET_CRT_EASYFLASH) // EASYFLASH + { + memcpy(chipHeader.mSignature, "CHIP", 4); + chipHeader.mPacketLength = 0x10200000; + chipHeader.mChipType = 0; + chipHeader.mBankNumber = 0; + chipHeader.mImageSize = 0x0020; + + uint8 bootmem[0x4000]; + + memset(bootmem, 0, 0x4000); + + if (mCartridgeBankUsed[0]) + memcpy(bootmem, mCartridge[0] + 0x8000, 0x4000); + + LinkerRegion* mainRegion = FindRegion(Ident::Unique("main")); + LinkerRegion* startupRegion = FindRegion(Ident::Unique("startup")); + + memcpy(bootmem, mMemory + startupRegion->mStart, startupRegion->mNonzero - startupRegion->mStart); + int usedlz = memlzcomp(bootmem + 0x0100, mMemory + mainRegion->mStart, mainRegion->mNonzero - mainRegion->mStart); + + Location loc; + + if (usedlz > 0x03e00) + { + mErrors->Error(loc, ERRR_INSUFFICIENT_MEMORY, "Can not fit main region into first ROM bank"); + fclose(file); + return false; + } + + bootmem[0x3ffc] = 0x00; + bootmem[0x3ffd] = 0xff; + + uint8 bootcode[] = { + 0xa9, 0x87, + 0x8d, 0x02, 0xde, + 0xa9, 0x00, + 0x8d, 0x00, 0xde, + 0x6c, 0xfc, 0xff + }; + + int j = 0x3f00; + for (int i = 0; i < sizeof(bootcode); i++) + { + bootmem[j++] = 0xa9; + bootmem[j++] = bootcode[i]; + bootmem[j++] = 0x8d; + bootmem[j++] = i; + bootmem[j++] = 0x04; + } + bootmem[j++] = 0x4c; + bootmem[j++] = 0x00; + bootmem[j++] = 0x04; + + chipHeader.mLoadAddress = 0x0080; + fwrite(&chipHeader, sizeof(chipHeader), 1, file); + fwrite(bootmem, 1, 0x2000, file); + + chipHeader.mLoadAddress = 0x00e0; + fwrite(&chipHeader, sizeof(chipHeader), 1, file); + fwrite(bootmem + 0x2000, 1, 0x2000, file); + + mCartridgeBankUsed[0] = true; + mCartridgeBankStart[0] = 0x8000; + mCartridgeBankEnd[0] = 0x8000 + usedlz + 0x200; + + for (int i = 1; i < 64; i++) + { + if (mCartridgeBankUsed[i]) + { + chipHeader.mBankNumber = i << 8; + + chipHeader.mLoadAddress = 0x0080; + fwrite(&chipHeader, sizeof(chipHeader), 1, file); + fwrite(mCartridge[i] + 0x8000, 1, 0x2000, file); + + chipHeader.mLoadAddress = 0x00a0; + fwrite(&chipHeader, sizeof(chipHeader), 1, file); + fwrite(mCartridge[i] + 0xa000, 1, 0x2000, file); + } + } + } + else if (mCompilerOptions & COPT_TARGET_CRT8) + { + int numBanks = 64; + while (numBanks > 1 && !mCartridgeBankUsed[numBanks - 1]) + numBanks--; + + memcpy(chipHeader.mSignature, "CHIP", 4); + chipHeader.mPacketLength = flip32(0x10 + 0x2000); + chipHeader.mChipType = 0; + chipHeader.mBankNumber = 0; + chipHeader.mImageSize = flip16(0x2000); + + for (int i = 0; i < numBanks; i++) + { + if (mCartridgeBankUsed[i]) + { + chipHeader.mBankNumber = flip16(uint16(i)); + + chipHeader.mLoadAddress = flip16(0x8000); + fwrite(&chipHeader, sizeof(chipHeader), 1, file); + fwrite(mCartridge[i] + 0x8000, 1, 0x2000, file); + } + } + } + else if (mCompilerOptions & COPT_TARGET_CRT16) + { + int numBanks = 64; + while (numBanks > 1 && !mCartridgeBankUsed[numBanks - 1]) + numBanks--; + + memcpy(chipHeader.mSignature, "CHIP", 4); + chipHeader.mPacketLength = flip32(0x10 + 0x4000); + chipHeader.mChipType = 0; + chipHeader.mBankNumber = 0; + chipHeader.mImageSize = flip16(0x4000); + + for (int i = 0; i < numBanks; i++) + { + if (mCartridgeBankUsed[i]) + { + chipHeader.mBankNumber = flip16(uint16(i)); + + chipHeader.mLoadAddress = flip16(0x8000); + fwrite(&chipHeader, sizeof(chipHeader), 1, file); + fwrite(mCartridge[i] + 0x8000, 1, 0x4000, file); + } + } + } + + fclose(file); + return true; + } + else + return false; +} + + +bool Linker::WriteMapFile(const char* filename) +{ + bool banked = mCartridgeBankUsed[0] || mCartridgeBankUsed[1]; + + FILE* file; + fopen_s(&file, filename, "wb"); + if (file) + { + fprintf(file, "sections\n"); + for (int i = 0; i < mSections.Size(); i++) + { + LinkerSection* lsec = mSections[i]; + + fprintf(file, "%04x - %04x : %s, %s\n", lsec->mStart, lsec->mEnd, LinkerSectionTypeNames[lsec->mType], lsec->mIdent->mString); + } + + fprintf(file, "\nregions\n"); + + for (int i = 0; i < mRegions.Size(); i++) + { + LinkerRegion * lrgn = mRegions[i]; + + fprintf(file, "%04x - %04x : %04x, %04x, %s\n", lrgn->mStart, lrgn->mEnd, lrgn->mNonzero, lrgn->mUsed, lrgn->mIdent->mString); + } + + fprintf(file, "\nobjects\n"); + + for (int i = 0; i < mObjects.Size(); i++) + { + LinkerObject* obj = mObjects[i]; + + if (obj->mFlags & LOBJF_REFERENCED) + { + if (obj->mRegion) + { + if (banked) + { + int k = 0; + while (k < 64 && !(obj->mRegion->mCartridgeBanks & (1ull << k))) + k++; + if (k < 64) + fprintf(file, "%02x:", k); + else + fprintf(file, "--:"); + } + } + + if (obj->mIdent) + fprintf(file, "%04x - %04x : %s, %s:%s\n", obj->mAddress, obj->mAddress + obj->mSize, obj->mIdent->mString, LinkerObjectTypeNames[obj->mType], obj->mSection->mIdent->mString); + else + fprintf(file, "%04x - %04x : *, %s:%s\n", obj->mAddress, obj->mAddress + obj->mSize, LinkerObjectTypeNames[obj->mType], obj->mSection->mIdent->mString); + } + } + + if (banked) + { + fprintf(file, "\nbanks\n"); + + for (int i = 0; i < 64; i++) + { + if (mCartridgeBankUsed[i]) + fprintf(file, "%02d : %04x .. %04x (%04x)\n", i, mCartridgeBankStart[i], mCartridgeBankEnd[i], mCartridgeBankEnd[i] - mCartridgeBankStart[i]); + } + } + + fprintf(file, "\nobjects by size\n"); + ExpandingArray so; + for (int i = 0; i < mObjects.Size(); i++) + { + LinkerObject* obj = mObjects[i]; + + if ((obj->mFlags & LOBJF_REFERENCED) && obj->mIdent) + { + int k = so.Size(); + so.Push(obj); + while (k > 0 && so[k - 1]->mSize < obj->mSize) + { + so[k] = so[k - 1]; + k--; + } + so[k] = obj; + } + } + + for (int i = 0; i < so.Size(); i++) + { + const LinkerObject* obj = so[i]; + + if (obj->mRegion) + { + if (banked) + { + int k = 0; + while (k < 64 && !(obj->mRegion->mCartridgeBanks & (1ull << k))) + k++; + if (k < 64) + fprintf(file, "%02x:", k); + else + fprintf(file, "--:"); + } + } + + fprintf(file, "%04x (%04x) : %s, %s:%s\n", obj->mAddress, obj->mSize, obj->mIdent->mString, LinkerObjectTypeNames[obj->mType], obj->mSection->mIdent->mString); + } + + fclose(file); + + return true; + } + else + return false; +} + +int Linker::TranslateMlbAddress(int address, int bank, TargetMachine machine) +{ + switch (machine) + { + default: + case TMACH_NES: + case TMACH_NES_NROM_H: + case TMACH_NES_NROM_V: + return address - 0x8000; + case TMACH_NES_MMC1: + if (bank == 15) + return 15 * 0x4000 + address - 0xc000; + else + return bank * 0x4000 + address - 0x8000; + case TMACH_NES_MMC3: + if (bank == 31) + return 31 * 0x4000 + address - 0xc000; + else + return bank * 0x4000 + address - 0x8000; + } +} + +bool Linker::WriteMlbFile(const char* filename, TargetMachine machine) +{ + FILE* file; + fopen_s(&file, filename, "wb"); + if (file) + { + fprintf(file, "R:%02x-%02x:__ACCU\n", BC_REG_ACCU, BC_REG_ACCU + 3); + fprintf(file, "R:%02x-%02x:__ADDR\n", BC_REG_ADDR, BC_REG_ADDR + 1); + fprintf(file, "R:%02x-%02x:__IP\n", BC_REG_IP, BC_REG_IP + 1); + fprintf(file, "R:%02x-%02x:__SP\n", BC_REG_STACK, BC_REG_STACK + 1); + fprintf(file, "R:%02x-%02x:__FP\n", BC_REG_LOCALS, BC_REG_LOCALS + 1); + fprintf(file, "R:%02x-%02x:__P\n", BC_REG_FPARAMS, BC_REG_FPARAMS_END - 1); + fprintf(file, "R:%02x-%02x:__T\n", BC_REG_TMP, 0x7f); + + for (int i = 0; i < mObjects.Size(); i++) + { + LinkerObject* obj = mObjects[i]; + + if ((obj->mFlags & LOBJF_REFERENCED) && obj->mIdent && obj->mSize > 0) + { + int bank = -1; + if (obj->mRegion->mCartridgeBanks) + { + do { bank++; } while (!((1ULL << bank) & obj->mRegion->mCartridgeBanks)); + } + + if (obj->mSection->mType == LST_BSS) + { + if (obj->mRanges.Size() > 0) + { + for(int i=0; imRanges.Size(); i++) + fprintf(file, "R:%04x-%04x:%s@%s\n", obj->mAddress + obj->mRanges[i].mOffset, obj->mAddress + obj->mRanges[i].mOffset + obj->mRanges[i].mSize - 1, obj->mIdent->mString, obj->mRanges[i].mIdent->mString); + } + if (obj->mSize > 1) + fprintf(file, "R:%04x-%04x:%s\n", obj->mAddress, obj->mAddress + obj->mSize - 1, obj->mIdent->mString); + else + fprintf(file, "R:%04x:%s\n", obj->mAddress, obj->mIdent->mString); + } + else if (obj->mType == LOT_DATA) + { + if (obj->mAddress >= 0x8000) + { + if (obj->mRanges.Size() > 0) + { + for (int i = 0; i < obj->mRanges.Size(); i++) + fprintf(file, "P:%04x-%04x:%s@%s\n", TranslateMlbAddress(obj->mAddress + obj->mRanges[i].mOffset, bank, machine), TranslateMlbAddress(obj->mAddress + obj->mRanges[i].mOffset + obj->mRanges[i].mSize - 1, bank, machine), obj->mIdent->mString, obj->mRanges[i].mIdent->mString); + } + fprintf(file, "P:%04x-%04x:%s\n", TranslateMlbAddress(obj->mAddress, bank, machine), TranslateMlbAddress(obj->mAddress + obj->mSize - 1, bank, machine), obj->mIdent->mString); + } + } + else if (obj->mType == LOT_NATIVE_CODE) + { + if (obj->mAddress >= 0x8000) + { + if (obj->mRanges.Size() > 0) + { + for (int i = 0; i < obj->mRanges.Size(); i++) + fprintf(file, "P:%04x:%s@%s\n", TranslateMlbAddress(obj->mAddress + obj->mRanges[i].mOffset, bank, machine), obj->mIdent->mString, obj->mRanges[i].mIdent->mString); + } + + fprintf(file, "P:%04x:%s\n", TranslateMlbAddress(obj->mAddress, bank, machine), obj->mIdent->mString); + } + } + } + } + + fclose(file); + + return true; + } + else + return false; +} + +bool Linker::WriteDbjFile(FILE* file) +{ + fprintf(file, "\t\"memory\": ["); + + bool first = true; + for (int i = 0; i < mObjects.Size(); i++) + { + LinkerObject* obj = mObjects[i]; + + if (obj->mFlags & LOBJF_REFERENCED) + { + if (obj->mIdent) + { + if (!first) + fprintf(file, ",\n"); + first = false; + + fprintf(file, "\t\t{\"name\": \"%s\", \"xname\": \"%s\", \"start\": %d, \"end\": %d, \"type\": \"%s\", \"source\": \"%s\", \"line\": %d }", + obj->mIdent->mString, obj->mFullIdent->mString, obj->mAddress, obj->mAddress + obj->mSize, LinkerObjectTypeNames[obj->mType], + obj->mLocation.mFileName, obj->mLocation.mLine); + } + } + } + + fprintf(file, "]"); + + + return true; +} + +static const char hexchars[] = "0123456789abcdef"; + +bool Linker::WriteLblFile(const char* filename) +{ + FILE* file; + fopen_s(&file, filename, "wb"); + if (file) + { + for (int i = 0; i < mObjects.Size(); i++) + { + LinkerObject* obj = mObjects[i]; + + if (obj->mFlags & LOBJF_REFERENCED) + { + if (obj->mIdent) + { + char buffer[400]; + char nbuffer[500]; + + strcpy_s(buffer, obj->mIdent->mString); + int i = 0, j = 0; + while (buffer[i]) + { + if (buffer[i] >= '0' && buffer[i] <= '9' || buffer[i] >= 'a' && buffer[i] <= 'z' || buffer[i] >= 'A' && buffer[i] <= 'Z' || buffer[i] == '_' || buffer[i] == ':') + nbuffer[j++] = buffer[i]; + else + { + nbuffer[j++] = '?'; + nbuffer[j++] = hexchars[(buffer[i] >> 4) & 0x0f]; + nbuffer[j++] = hexchars[buffer[i] & 0x0f]; + + } + i++; + } + nbuffer[j] = 0; + fprintf(file, "al %04x .%s\n", obj->mAddress, nbuffer); + } + } + } + + for (int i = 0; i < mBreakpoints.Size(); i++) + { + fprintf(file, "break %04x\n", mBreakpoints[i]); + } + + fclose(file); + + return true; + } + else + return false; +} + +bool Linker::WriteAsmFile(const char* filename, const char* version) +{ + FILE* file; + fopen_s(&file, filename, "wb"); + if (file) + { + fprintf(file, "; Compiled with %s\n", version); + + for (int i = 0; i < mObjects.Size(); i++) + { + LinkerObject* obj = mObjects[i]; + + if (obj->mFlags & LOBJF_REFERENCED) + { + switch (obj->mType) + { + case LOT_BYTE_CODE: + mByteCodeDisassembler.Disassemble(file, mMemory, -1, obj->mAddress, obj->mSize, obj->mProc, obj->mIdent, this); + break; + case LOT_NATIVE_CODE: + if (!obj->mRegion) + mNativeDisassembler.Disassemble(file, obj->mData, -1, 0, obj->mSize, obj->mProc, obj->mIdent, this, obj->mFullIdent); + else if (obj->mRegion->mCartridgeBanks) + { + int i = 0; + while (!(obj->mRegion->mCartridgeBanks & (1ULL << i))) + i++; + mNativeDisassembler.Disassemble(file, mCartridge[i], i, obj->mAddress, obj->mSize, obj->mProc, obj->mIdent, this, obj->mFullIdent); + } + else if (obj->mRegion->mInlayObject) + mNativeDisassembler.Disassemble(file, obj->mRegion->mInlayObject->mMemory, 0xa0, obj->mAddress, obj->mSize, obj->mProc, obj->mIdent, this, obj->mFullIdent); + else + mNativeDisassembler.Disassemble(file, mMemory, -1, obj->mAddress, obj->mSize, obj->mProc, obj->mIdent, this, obj->mFullIdent); + break; + case LOT_DATA: + if (!obj->mRegion) + mNativeDisassembler.DumpMemory(file, obj->mData, -1, 0, obj->mSize, obj->mProc, obj->mIdent, this, obj); + else if (obj->mRegion->mCartridgeBanks) + { + int i = 0; + while (!(obj->mRegion->mCartridgeBanks & (1ULL << i))) + i++; + mNativeDisassembler.DumpMemory(file, mCartridge[i], i, obj->mAddress, obj->mSize, obj->mProc, obj->mIdent, this, obj); + } + else if (obj->mRegion->mInlayObject) + mNativeDisassembler.DumpMemory(file, obj->mRegion->mInlayObject->mMemory, 0xa0, obj->mAddress, obj->mSize, obj->mProc, obj->mIdent, this, obj); + else + mNativeDisassembler.DumpMemory(file, mMemory, -1, obj->mAddress, obj->mSize, obj->mProc, obj->mIdent, this, obj); + break; + } + } + } + + fclose(file); + + return true; + } + else + return false; +} + diff --git a/oscar64/oscar64/Linker.h b/oscar64/oscar64/Linker.h new file mode 100644 index 0000000..349e0ff --- /dev/null +++ b/oscar64/oscar64/Linker.h @@ -0,0 +1,341 @@ +#pragma once + +#include "MachineTypes.h" +#include "Ident.h" +#include "Array.h" +#include "Errors.h" +#include "Disassembler.h" +#include "DiskImage.h" +#include "CompilerTypes.h" + +class InterCodeProcedure; +class InterVariable; +class NativeCodeProcedure; + +enum LinkerObjectType +{ + LOT_NONE, + LOT_PAD, + LOT_BASIC, + LOT_BYTE_CODE, + LOT_NATIVE_CODE, + LOT_RUNTIME, + LOT_DATA, + LOT_BSS, + LOT_HEAP, + LOT_STACK, + LOT_INLAY, + LOT_SECTION_START, + LOT_SECTION_END +}; + +enum LinkerSectionType +{ + LST_NONE, + LST_DATA, + LST_BSS, + LST_HEAP, + LST_STACK, + LST_STATIC_STACK, + LST_ZEROPAGE + +}; + +struct ZeroPageSet +{ + ZeroPageSet(void) + { + for (int i = 0; i < 8; i++) + mBits[i] = 0; + } + + uint32 mBits[8]; + + void operator += (int n) + { + mBits[n >> 5] |= 1 << (n & 31); + } + + void operator -= (int n) + { + mBits[n >> 5] &= ~(1 << (n & 31)); + } + + bool operator[] (int n) const + { + return (mBits[n >> 5] & (1 << (n & 31))) != 0; + } + + ZeroPageSet& operator |= (const ZeroPageSet& set) + { + for (int i = 0; i < 8; i++) + mBits[i] |= set.mBits[i]; + return *this; + } +}; + +class LinkerObject; +class LinkerSection; + +class LinkerRegion +{ +public: + const Ident* mIdent; + + uint32 mFlags; + int mStart, mEnd, mUsed, mNonzero, mReloc; + uint64 mCartridgeBanks; + LinkerObject * mInlayObject; + + GrowingArray mSections; + + LinkerRegion(void); + + struct FreeChunk + { + int mStart, mEnd; + LinkerObject* mLastObject; + }; + + GrowingArray mFreeChunks; + LinkerObject * mLastObject; + + bool AllocateAppend(Linker* linker, LinkerObject* obj); + bool Allocate(Linker * linker, LinkerObject* obj, bool merge, bool retry); + void PlaceStackSection(LinkerSection* stackSection, LinkerSection* section); +}; + +static const uint32 LREF_LOWBYTE = 0x00000001; +static const uint32 LREF_HIGHBYTE = 0x00000002; +static const uint32 LREF_TEMPORARY = 0x00000004; +static const uint32 LREF_INBLOCK = 0x00000008; +static const uint32 LREF_LOWBYTE_OFFSET = 0x00000010; +static const uint32 LREF_BREAKPOINT = 0x00000020; +static const uint32 LREF_SELFMOD = 0x00000040; + +class LinkerReference +{ +public: + LinkerObject* mObject, * mRefObject; + int mOffset, mRefOffset; + uint32 mFlags; + + bool operator==(const LinkerReference& ref); + bool operator!=(const LinkerReference& ref); + +}; + +static const uint32 LSECF_PACKED = 0x00000001; +static const uint32 LSECF_PLACED = 0x00000002; + +class LinkerSection +{ +public: + const Ident* mIdent; + + GrowingArray mObjects; + GrowingArray mSections; + + int mStart, mEnd, mSize; + LinkerSectionType mType; + uint32 mFlags; + + LinkerSection(void); + + void RemObject(LinkerObject* obj); + void AddObject(LinkerObject* obj); +}; + +static const uint32 LOBJF_REFERENCED = 0x00000001; +static const uint32 LOBJF_PLACED = 0x00000002; +static const uint32 LOBJF_NO_FRAME = 0x00000004; +static const uint32 LOBJF_INLINE = 0x00000008; +static const uint32 LOBJF_CONST = 0x00000010; +static const uint32 LOBJF_RELEVANT = 0x00000020; +static const uint32 LOBJF_STATIC_STACK = 0x00000040; +static const uint32 LOBJF_NO_CROSS = 0x00000080; +static const uint32 LOBJF_ZEROPAGE = 0x00000100; +static const uint32 LOBJF_FORCE_ALIGN = 0x00000200; +static const uint32 LOBJF_ZEROPAGESET = 0x00000400; +static const uint32 LOBJF_NEVER_CROSS = 0x00000800; + +static const uint32 LOBJF_ARG_REG_A = 0x00001000; +static const uint32 LOBJF_ARG_REG_X = 0x00002000; +static const uint32 LOBJF_ARG_REG_Y = 0x00004000; + +static const uint32 LOBJF_RET_REG_A = 0x00010000; +static const uint32 LOBJF_RET_REG_X = 0x00020000; +static const uint32 LOBJF_RET_REG_Y = 0x00040000; + +static const uint32 LOBJF_PRESERVE_REG_A = 0x00100000; +static const uint32 LOBJF_PRESERVE_REG_X = 0x00200000; +static const uint32 LOBJF_PRESERVE_REG_Y = 0x00400000; + +static const uint32 LOBJF_LOCAL_VAR = 0x01000000; +static const uint32 LOBJF_LOCAL_USED = 0x02000000; + +static const uint32 LOBJF_NO_GLOBAL_STORES = 0x04000000; +static const uint32 LOBJF_NO_GLOBAL_LOADS = 0x08000000; + +class LinkerObjectRange +{ +public: + const Ident * mIdent; + int mOffset, mSize; +}; + +struct CodeLocation +{ + Location mLocation; + int mStart, mEnd; + bool mWeak; +}; + + +class LinkerObject +{ +public: + Location mLocation; + const Ident * mIdent, * mFullIdent; + LinkerObjectType mType; + int mID, mMapID; + int mAddress, mRefAddress; + int mSize, mAlignment, mStripe, mStartUsed, mEndUsed; + LinkerSection * mSection; + LinkerRegion * mRegion; + uint8 * mData, * mMemory; + InterCodeProcedure * mProc, * mOwnerProc; + NativeCodeProcedure * mNativeProc; + InterVariable * mVariable; + uint32 mFlags; + uint8 mTemporaries[16], mTempSizes[16]; + int mNumTemporaries; + ZeroPageSet mZeroPageSet; + LinkerSection * mStackSection; + LinkerObject * mPrefix, * mSuffix; + int mSuffixReference; + + ExpandingArray mRanges; + ExpandingArray mCodeLocations, mCodeOrigins; + ExpandingArray mZeroPageRanges; + + LinkerObject(void); + ~LinkerObject(void); + + LinkerObject* CloneAssembler(Linker * linker) const; + + void AddData(const uint8* data, int size); + void AddLocations(const ExpandingArray& locations); + void AddOrigins(const ExpandingArray& locations); + uint8* AddSpace(int size); + void EnsureSpace(int offset, int size); + + GrowingArray mReferences; + + LinkerReference* FindReference(int64 offset); + + void AddReference(const LinkerReference& ref); + + void MoveToSection(LinkerSection* section); + + void MarkRelevant(void); + + bool IsSameConst(const LinkerObject* obj) const; + + bool IsBefore(const LinkerObject* obj) const; + int FirstBank(void) const; +}; + +class LinkerOverlay +{ +public: + LinkerOverlay(void); + ~LinkerOverlay(void); + + Location mLocation; + const Ident * mIdent; + int mBank; + bool mCompressed; +}; + +class Linker +{ +public: + Linker(Errors * errors); + ~Linker(void); + + LinkerRegion * AddRegion(const Ident* region, int start, int end); + LinkerRegion* FindRegion(const Ident* region); + + LinkerSection * AddSection(const Ident* section, LinkerSectionType type); + LinkerSection* FindSection(const Ident* section); + + LinkerRegion* FindRegionOfSection(LinkerSection* section); + + LinkerObject* FindObjectByAddr(int addr, InterCodeProcedure* proc = nullptr); + LinkerObject* FindObjectByAddr(int bank, int addr, InterCodeProcedure * proc = nullptr); + LinkerObject* FindObjectByName(const char* name); + + bool IsSectionPlaced(LinkerSection* section); + + LinkerObject * AddObject(const Location & location, const Ident* ident, LinkerSection * section, LinkerObjectType type, int alignment = 1); + LinkerObject* FindSame(LinkerObject* obj); + + LinkerOverlay* AddOverlay(const Location& location, const Ident* ident, int bank); + + void SortObjects(void); + +// void AddReference(const LinkerReference& ref); + + bool WritePrgFile(DiskImage * image, const char* filename); + bool WritePrgFile(const char* filename, const char * pathname); + bool WriteXexFile(const char* filename); + bool WriteMapFile(const char* filename); + bool WriteAsmFile(const char* filename, const char * version); + bool WriteLblFile(const char* filename); + bool WriteCrtFile(const char* filename, uint16 id, uint8 subtype, const char * cname); + bool WriteBinFile(const char* filename); + bool WriteNesFile(const char* filename, TargetMachine machine); + bool WriteMlbFile(const char* filename, TargetMachine machine); + bool WriteDbjFile(FILE * file); + + int TranslateMlbAddress(int address, int bank, TargetMachine machine); + + uint64 mCompilerOptions; + + GrowingArray mReferences; + GrowingArray mRegions; + GrowingArray mSections; + GrowingArray mObjects; + GrowingArray mOverlays; + GrowingArray mBreakpoints; + + uint8 mMemory[0x10000], mWorkspace[0x10000]; + uint8 mCartridge[64][0x10000]; + + bool mCartridgeBankUsed[64]; + + int mCartridgeBankStart[64], mCartridgeBankEnd[64]; + + int mProgramStart, mProgramEnd; + + void ReferenceObject(LinkerObject* obj); + + void CheckDirectJumps(void); + void CollectReferences(void); + void CombineSameConst(void); + void InlineSimpleJumps(void); + void PatchReferences(bool inlays); + void CopyObjects(bool inlays); + void PlaceObjects(bool retry); + void Link(void); + void CollectBreakpoints(void); +protected: + NativeCodeDisassembler mNativeDisassembler; + ByteCodeDisassembler mByteCodeDisassembler; + + bool Forwards(LinkerObject* pobj, LinkerObject* lobj); + void SortObjectsPartition(int l, int r); + + Errors* mErrors; +}; diff --git a/oscar64/oscar64/MachineTypes.cpp b/oscar64/oscar64/MachineTypes.cpp new file mode 100644 index 0000000..aeb3c71 --- /dev/null +++ b/oscar64/oscar64/MachineTypes.cpp @@ -0,0 +1,16 @@ +#include "MachineTypes.h" + +uint8 BC_REG_WORK = 0x03; +uint8 BC_REG_WORK_Y = 0x02; +uint8 BC_REG_FPARAMS = 0x0d; +uint8 BC_REG_FPARAMS_END = 0x19; + +uint8 BC_REG_IP = 0x19; +uint8 BC_REG_ACCU = 0x1b; +uint8 BC_REG_ADDR = 0x1f; +uint8 BC_REG_STACK = 0x23; +uint8 BC_REG_LOCALS = 0x25; + +uint8 BC_REG_TMP = 0x43; +uint8 BC_REG_TMP_SAVED = 0x53; + diff --git a/oscar64/oscar64/MachineTypes.h b/oscar64/oscar64/MachineTypes.h new file mode 100644 index 0000000..628ec51 --- /dev/null +++ b/oscar64/oscar64/MachineTypes.h @@ -0,0 +1,141 @@ +#pragma once + +#include +#include +#include +#include +#include + +using std::ptrdiff_t; + +typedef unsigned char uint8; +typedef unsigned short uint16; +typedef unsigned int uint32; +typedef signed char int8; +typedef signed short int16; +typedef signed int int32; +#ifdef _WIN32 +typedef __int64 int64; +typedef unsigned __int64 uint64; +#define MAXPATHLEN _MAX_PATH +#else +#if __linux__ +#include "linux/limits.h" +#else /* __APPLE__ */ +#include "limits.h" +#endif + +typedef long long int64; +typedef unsigned long long uint64; + +#define _access access +#define _strdup strdup + +#define MAXPATHLEN PATH_MAX + +inline char* strcat_s(char* c, const char* d) +{ + return strcat(c, d); +} + +inline char* strcat_s(char* c, int n, const char* d) +{ + return strcat(c, d); +} + +inline char* strcpy_s(char* c, const char* d) +{ + return strcpy(c, d); +} + +inline char* strcpy_s(char* c, int n, const char* d) +{ + return strcpy(c, d); +} + +inline int fopen_s(FILE** f, const char* fname, const char* mode) +{ + *f = fopen(fname, mode); + return *f ? 0 : -1; +} + +inline char* _fullpath(char* absPath, const char* relPath, size_t maxLength) +{ + return realpath(relPath, absPath); +} + +template +inline int sprintf_s(char(&buffer)[size], const char* format, ...) +{ + va_list args; + va_start(args, format); + int n = vsnprintf(buffer, size, format, args); + va_end(args); + return n; +} + +inline int sprintf_s(char* buffer, int size, const char* format, ...) +{ + va_list args; + va_start(args, format); + int n = vsnprintf(buffer, size, format, args); + va_end(args); + return n; +} + +#define __forceinline inline + +#endif + +extern uint8 BC_REG_WORK; +extern uint8 BC_REG_WORK_Y; +extern uint8 BC_REG_FPARAMS; +extern uint8 BC_REG_FPARAMS_END; + +extern uint8 BC_REG_IP; +extern uint8 BC_REG_ACCU; +extern uint8 BC_REG_ADDR; +extern uint8 BC_REG_STACK; +extern uint8 BC_REG_LOCALS; + +extern uint8 BC_REG_TMP; +extern uint8 BC_REG_TMP_SAVED; + +inline int ustrlen(const uint8* s) +{ + int i = 0; + while (s[i]) + i++; + return i; +} + +inline void ustrcpy(uint8* dp, const uint8* sp) +{ + int i = 0; + while (sp[i]) + { + dp[i] = sp[i]; + i++; + } + dp[i] = 0; +} + +inline int64 int64max(int64 a, int64 b) +{ + return a > b ? a : b; +} + +inline int64 int64min(int64 a, int64 b) +{ + return a < b ? a : b; +} + +inline int intmax(int a, int b) +{ + return a > b ? a : b; +} + +inline int intmin(int a, int b) +{ + return a < b ? a : b; +} diff --git a/oscar64/oscar64/NativeCodeGenerator.cpp b/oscar64/oscar64/NativeCodeGenerator.cpp new file mode 100644 index 0000000..86634f1 --- /dev/null +++ b/oscar64/oscar64/NativeCodeGenerator.cpp @@ -0,0 +1,70621 @@ +#include "NativeCodeGenerator.h" +#include "CompilerTypes.h" +#include "NativeCodeOutliner.h" +#include "Declaration.h" + +#define JUMP_TO_BRANCH 1 +#define CHECK_NULLPTR 0 +#define REYCLE_JUMPS 1 +#define DISASSEMBLE_OPT 0 + +static bool CheckFunc; +static bool CheckCase; + + +static const int CPU_REG_A = 256; +static const int CPU_REG_X = 257; +static const int CPU_REG_Y = 258; +static const int CPU_REG_C = 259; +static const int CPU_REG_Z = 260; + +static const int NUM_REGS = 261; + +static const uint32 LIVE_CPU_REG_A = 0x00000001; +static const uint32 LIVE_CPU_REG_X = 0x00000002; +static const uint32 LIVE_CPU_REG_Y = 0x00000004; +static const uint32 LIVE_CPU_REG_C = 0x00000008; +static const uint32 LIVE_CPU_REG_Z = 0x00000010; +static const uint32 LIVE_CPU_REG = 0x0000001f; + +static const uint32 LIVE_MEM = 0x00000020; + +static const uint32 LIVE_ALL = 0x000000ff; + +static int GlobalValueNumber = 0; + +static bool IsPowerOf2(unsigned n) +{ + return (n & (n - 1)) == 0; +} + +static int Binlog(unsigned n) +{ + int k = -1; + + while (n) + { + n >>= 1; + k++; + } + + return k; +} + +static unsigned BinMask(unsigned n) +{ + n |= n >> 8; + n |= n >> 4; + n |= n >> 2; + n |= n >> 1; + return n; +} + +static unsigned LowBinMask(unsigned n) +{ + n = ~n; + n |= n << 16; + n |= n << 8; + n |= n << 4; + n |= n << 2; + n |= n << 1; + n = ~n; + return n; +} + +NativeRegisterData::NativeRegisterData(void) + : mMode(NRDM_UNKNOWN), mValue(GlobalValueNumber++), mMask(0), mMinVal(0), mMaxVal(255) +{ + mUnique = mValue; +} + +void NativeRegisterData::Set(NativeRegisterDataMode mode, int value, LinkerObject* lo, int flags, int minv, int maxv) +{ + mMode = mode; + mValue = value; + mLinkerObject = lo; + mFlags = flags; + mUnique = GlobalValueNumber++; + mMinVal = minv; + mMaxVal = maxv; +} + +void NativeRegisterData::Reset(void) +{ + mFlags = 0; + mMode = NRDM_UNKNOWN; + mValue = mUnique = GlobalValueNumber++; + mMinVal = 0; + mMaxVal = 255; +} + +void NativeRegisterData::ResetMask(void) +{ + mMask = 0; +} + +void NativeRegisterData::SetMask(int mask, int value) +{ + mMask = mask; + mValue = value; +} + +void NativeRegisterData::ResetAliasing(void) +{ + if (mFlags & NCIF_ALIASING) + Reset(); +} + + +bool NativeRegisterData::SameData(const NativeCodeInstruction& ins) const +{ + if (ins.mMode == ASMIM_ZERO_PAGE) + return mMode == NRDM_ZERO_PAGE && mValue == ins.mAddress; + else if (ins.mMode == ASMIM_ABSOLUTE) + return mMode == NRDM_ABSOLUTE && mValue == ins.mAddress && mLinkerObject == ins.mLinkerObject && mFlags == ins.mFlags; + else if (ins.mMode == ASMIM_ABSOLUTE_X) + return mMode == NRDM_ABSOLUTE_X && mValue == ins.mAddress && mLinkerObject == ins.mLinkerObject && mFlags == ins.mFlags; + else if (ins.mMode == ASMIM_ABSOLUTE_Y) + return mMode == NRDM_ABSOLUTE_Y && mValue == ins.mAddress && mLinkerObject == ins.mLinkerObject && mFlags == ins.mFlags; + else if (ins.mMode == ASMIM_INDIRECT_Y) + return mMode == NRDM_INDIRECT_Y && mValue == ins.mAddress; + else + return false; +} + +bool NativeRegisterData::SameData(const NativeRegisterData& d) const +{ + if (mMode != d.mMode) + return false; + + switch (mMode) + { + case NRDM_UNKNOWN: + case NRDM_IMMEDIATE: + case NRDM_ZERO_PAGE: + return mValue == d.mValue; + case NRDM_IMMEDIATE_ADDRESS: + case NRDM_ABSOLUTE: + return mValue == d.mValue && mLinkerObject == d.mLinkerObject && (mFlags & NCIF_IMMADDR_FLAGS) == (d.mFlags & NCIF_IMMADDR_FLAGS); + default: + return false; + } +} + +const NativeRegisterData& NativeRegisterDataSet::operator[](int reg) const +{ + return mRegs[reg]; +} + +void NativeRegisterDataSet::Set(int reg, NativeRegisterDataMode mode, int value, LinkerObject* lo, int flags, int minv, int maxv) +{ + mRegs[reg].Set(mode, value, lo, flags, minv, maxv); +} + +void NativeRegisterDataSet::Set(int reg, const NativeRegisterData& data) +{ + mRegs[reg] = data; +} + +void NativeRegisterDataSet::Reset(int reg) +{ + mRegs[reg].Reset(); +} + +void NativeRegisterDataSet::ResetMask(int reg) +{ + mRegs[reg].ResetMask(); +} + +void NativeRegisterDataSet::SetMask(int reg, int mask, int value) +{ + mRegs[reg].SetMask(mask, value); +} + +void NativeRegisterDataSet::SetMask(int reg, const NativeRegisterData& data) +{ + mRegs[reg].SetMask(data.mMask, data.mValue); +} + +void NativeRegisterDataSet::ResetAliasing(int reg) +{ + mRegs[reg].ResetAliasing(); +} + +void NativeRegisterDataSet::Reset(void) +{ + for (int i = 0; i < NUM_REGS; i++) + mRegs[i].Reset(); +} + +void NativeRegisterDataSet::ResetMask(void) +{ + for (int i = 0; i < NUM_REGS; i++) + mRegs[i].ResetMask(); +} + +void NativeRegisterDataSet::ResetCall(const NativeCodeInstruction& ins, int fastCallBase) +{ + if (!(ins.mFlags & NCIF_PRESERVE_CPU_REG_A)) + mRegs[CPU_REG_A].Reset(); + if (!(ins.mFlags & NCIF_PRESERVE_CPU_REG_X)) + mRegs[CPU_REG_X].Reset(); + if (!(ins.mFlags & NCIF_PRESERVE_CPU_REG_Y)) + mRegs[CPU_REG_Y].Reset(); + + mRegs[CPU_REG_C].Reset(); + mRegs[CPU_REG_Z].Reset(); + + for (int i = 0; i < NUM_REGS; i++) + { + if (mRegs[i].mMode == NRDM_ABSOLUTE || + mRegs[i].mMode == NRDM_ABSOLUTE_X || + mRegs[i].mMode == NRDM_ABSOLUTE_Y) + { + if (mRegs[i].mLinkerObject && mRegs[i].mLinkerObject->mVariable) + { + InterVariable* var = mRegs[i].mLinkerObject->mVariable; + + if (ins.mLinkerObject && ins.mLinkerObject->mProc) + { + if (!(var->mLinkerObject->mFlags & LOBJF_LOCAL_VAR)) + { + if (var->mIndex && var->mIndex < ins.mLinkerObject->mProc->mModule->mGlobalVars.Size() && var == ins.mLinkerObject->mProc->mModule->mGlobalVars[var->mIndex]) + { + if (ins.mLinkerObject->mProc->ModifiesGlobal(var->mIndex)) + mRegs[i].Reset(); + } + else + mRegs[i].Reset(); + } + else if (var->mAliased && ins.mLinkerObject->mProc->mStoresIndirect) + mRegs[i].Reset(); + } + else if (var->mAliased) + mRegs[i].Reset(); + } + else + mRegs[i].Reset(); + } + else if (mRegs[i].mMode == NRDM_INDIRECT_Y) + { + mRegs[i].Reset(); + } + } + + ResetWorkRegs(); + + if (!(ins.mFlags & NCIF_RUNTIME) || (ins.mFlags & NCIF_FEXEC)) + { + ResetAliasing(); + + if (ins.mLinkerObject && ins.mLinkerObject->mProc) + { +#if 1 + ResetZeroPageRange(BC_REG_TMP, ins.mLinkerObject->mProc->mCallerSavedTemps); +#else + for (int i = BC_REG_TMP; i < BC_REG_TMP + ins.mLinkerObject->mProc->mCallerSavedTemps; i++) + ResetZeroPage(i); +#endif + } + else + { +#if 1 + ResetZeroPageRange(BC_REG_TMP, BC_REG_TMP_SAVED - BC_REG_TMP); +#else + for (int i = BC_REG_TMP; i < BC_REG_TMP_SAVED; i++) + ResetZeroPage(i); +#endif + } + + if (ins.mLinkerObject && (ins.mLinkerObject->mFlags & LOBJF_ZEROPAGESET)) + { + for (int i = BC_REG_FPARAMS; i < BC_REG_FPARAMS + fastCallBase; i++) + if (ins.mLinkerObject->mZeroPageSet[i]) + ResetZeroPage(i); + } + else + { +#if 1 + ResetZeroPageRange(BC_REG_FPARAMS, fastCallBase); +#else + for (int i = BC_REG_FPARAMS; i < BC_REG_FPARAMS + fastCallBase; i++) + ResetZeroPage(i); +#endif + } + } +} + + + + +void NativeRegisterDataSet::ResetWorkRegs(void) +{ + ResetZeroPage(BC_REG_WORK_Y); + ResetZeroPageRange(BC_REG_ADDR, 2); + ResetZeroPageRange(BC_REG_ACCU, 4); + ResetZeroPageRange(BC_REG_WORK, 8); +} + +void NativeRegisterDataSet::ResetAliasing(void) +{ + for (int i = 0; i < NUM_REGS; i++) + mRegs[i].ResetAliasing(); +} + +void NativeRegisterDataSet::ResetWorkMasks(void) +{ + mRegs[BC_REG_WORK_Y].ResetMask(); + mRegs[BC_REG_ADDR + 0].ResetMask(); + mRegs[BC_REG_ADDR + 1].ResetMask(); + + for (int i = 0; i < 4; i++) + mRegs[BC_REG_ACCU + i].ResetMask(); + for (int i = 0; i < 8; i++) + mRegs[BC_REG_WORK + i].ResetMask(); +} + + +void NativeRegisterDataSet::ResetZeroPage(int addr) +{ + mRegs[addr].Reset(); + for (int i = 0; i < NUM_REGS; i++) + { + if (mRegs[i].mMode == NRDM_ZERO_PAGE && mRegs[i].mValue == addr) + mRegs[i].Reset(); + else if (mRegs[i].mMode == NRDM_INDIRECT_Y && (mRegs[i].mValue == addr || mRegs[i].mValue + 1 == addr)) + { + mRegs[i].mValue = mRegs[i].mUnique; + mRegs[i].mMode = NRDM_UNKNOWN; // Is not the memory pointed to, but still the same value + } + } +} + +void NativeRegisterDataSet::ResetZeroPageRange(int addr, int num) +{ + for(int i=0; i= addr && mRegs[i].mValue < addr + num) + mRegs[i].Reset(); + else if (mRegs[i].mMode == NRDM_INDIRECT_Y && mRegs[i].mValue + 1 >= addr && mRegs[i].mValue < addr + num) + { + mRegs[i].mValue = mRegs[i].mUnique; + mRegs[i].mMode = NRDM_UNKNOWN; // Is not the memory pointed to, but still the same value + } + } +} + + +int NativeRegisterDataSet::FindAbsolute(LinkerObject* linkerObject, int addr) +{ + for (int i = 0; i < 256; i++) + { + if (mRegs[i].mMode == NRDM_ABSOLUTE && mRegs[i].mLinkerObject == linkerObject && mRegs[i].mValue == addr) + return i; + } + + return -1; +} + +void NativeRegisterDataSet::ResetAbsolute(LinkerObject* linkerObject, int addr) +{ + for (int i = 0; i < NUM_REGS; i++) + { + if ((mRegs[i].mMode == NRDM_ABSOLUTE || mRegs[i].mMode == NRDM_ABSOLUTE_X || mRegs[i].mMode == NRDM_ABSOLUTE_Y) + && mRegs[i].mLinkerObject == linkerObject && mRegs[i].mValue == addr) + mRegs[i].Reset(); + else if (mRegs[i].mMode == NRDM_INDIRECT_Y) + mRegs[i].Reset(); + } +} + +void NativeRegisterDataSet::ResetAbsoluteXY(LinkerObject* linkerObject, int addr) +{ + for (int i = 0; i < NUM_REGS; i++) + { + if (mRegs[i].mMode == NRDM_ABSOLUTE) + { + if (mRegs[i].mLinkerObject == linkerObject && mRegs[i].mValue < addr + 256 && mRegs[i].mValue >= addr) + mRegs[i].Reset(); + } + else if (mRegs[i].mMode == NRDM_ABSOLUTE_X || mRegs[i].mMode == NRDM_ABSOLUTE_Y) + { + if (mRegs[i].mLinkerObject == linkerObject && mRegs[i].mValue < addr + 256 && mRegs[i].mValue + 256 > addr) + mRegs[i].Reset(); + } + else if (mRegs[i].mMode == NRDM_INDIRECT_Y) + mRegs[i].Reset(); + } +} + +void NativeRegisterDataSet::ResetX(void) +{ + for (int i = 0; i < NUM_REGS; i++) + { + if (mRegs[i].mMode == NRDM_ABSOLUTE_X) + { + mRegs[i].mValue = mRegs[i].mUnique; + mRegs[i].mMode = NRDM_UNKNOWN; // Is not the memory pointed to, but still the same value + } + } +} + +void NativeRegisterDataSet::ResetY(void) +{ + for (int i = 0; i < NUM_REGS; i++) + { + if (mRegs[i].mMode == NRDM_ABSOLUTE_Y || mRegs[i].mMode == NRDM_INDIRECT_Y ) + { + mRegs[i].mValue = mRegs[i].mUnique; + mRegs[i].mMode = NRDM_UNKNOWN; // Is not the memory pointed to, but still the same value + } + } +} + +void NativeRegisterDataSet::ResetGlobal(void) +{ + for (int i = 0; i < NUM_REGS; i++) + { + if (mRegs[i].mMode == NRDM_ABSOLUTE || + mRegs[i].mMode == NRDM_ABSOLUTE_X || + mRegs[i].mMode == NRDM_ABSOLUTE_Y || + mRegs[i].mMode == NRDM_INDIRECT_Y) + { + mRegs[i].Reset(); + } + } +} + +void NativeRegisterDataSet::ResetIndirect(int reg) +{ + for (int i = 0; i < NUM_REGS; i++) + { + if (mRegs[i].mMode == NRDM_ABSOLUTE || + mRegs[i].mMode == NRDM_ABSOLUTE_X || + mRegs[i].mMode == NRDM_ABSOLUTE_Y) + { + if (reg == BC_REG_STACK && mRegs[i].mLinkerObject) + ; + else if (mRegs[i].mLinkerObject) + { + if (mRegs[i].mLinkerObject->mVariable && !mRegs[i].mLinkerObject->mVariable->mAliased && mRegs[i].mLinkerObject->mSize <= 2) + ; + else + mRegs[i].Reset(); + } + else + mRegs[i].Reset(); + } + else if (mRegs[i].mMode == NRDM_INDIRECT_Y ) + { + mRegs[i].Reset(); + } + } +} + + +void NativeRegisterDataSet::IntersectMask(const NativeRegisterDataSet& set) +{ + for (int i = 0; i < NUM_REGS; i++) + { + mRegs[i].mMask &= set.mRegs[i].mMask & ~(mRegs[i].mValue ^ set.mRegs[i].mValue); + } +} + + +void NativeRegisterDataSet::Intersect(const NativeRegisterDataSet& set) +{ + + for (int i = 0; i < NUM_REGS; i++) + { + if (mRegs[i].mMode == NRDM_UNKNOWN) + { + if (set.mRegs[i].mMode != NRDM_UNKNOWN || mRegs[i].mValue != set.mRegs[i].mValue) + mRegs[i].Reset(); + } + else if (mRegs[i].mMode == NRDM_IMMEDIATE) + { + if (set.mRegs[i].mMode != NRDM_IMMEDIATE || mRegs[i].mValue != set.mRegs[i].mValue) + mRegs[i].Reset(); + } + else if (mRegs[i].mMode == NRDM_IMMEDIATE_ADDRESS) + { + if (set.mRegs[i].mMode != NRDM_IMMEDIATE_ADDRESS || mRegs[i].mValue != set.mRegs[i].mValue || mRegs[i].mLinkerObject != set.mRegs[i].mLinkerObject || mRegs[i].mFlags != set.mRegs[i].mFlags) + mRegs[i].Reset(); + } + } + + bool changed; + do + { + changed = false; + + for (int i = 0; i < NUM_REGS; i++) + { + if (mRegs[i].mMode == NRDM_ZERO_PAGE) + { + if (set.mRegs[i].mMode != NRDM_ZERO_PAGE || mRegs[i].mValue != set.mRegs[i].mValue) + { + mRegs[i].Reset(); + changed = true; + } +#if 0 + else if (mRegs[mRegs[i].mValue].mValue != set.mRegs[set.mRegs[i].mValue].mValue) + { + mRegs[i].Reset(); + changed = true; + } +#endif + } + else if (mRegs[i].mMode == NRDM_ABSOLUTE) + { + if (set.mRegs[i].mMode != NRDM_ABSOLUTE || mRegs[i].mValue != set.mRegs[i].mValue || mRegs[i].mLinkerObject != set.mRegs[i].mLinkerObject) + { + mRegs[i].Reset(); + changed = true; + } + } + else if (mRegs[i].mMode == NRDM_INDIRECT_Y) + { + if (set.mRegs[i].mMode != NRDM_INDIRECT_Y || mRegs[i].mValue != set.mRegs[i].mValue || !mRegs[CPU_REG_Y].SameData(set.mRegs[CPU_REG_Y])) + { + mRegs[i].Reset(); + changed = true; + } + else + { + int reg = mRegs[i].mValue; + if (!mRegs[reg].SameData(set.mRegs[reg]) || !mRegs[reg + 1].SameData(set.mRegs[reg + 1])) + { + mRegs[i].Reset(); + changed = true; + } + } + } + else if (mRegs[i].mMode == NRDM_ABSOLUTE_X) + { + if (set.mRegs[i].mMode != NRDM_ABSOLUTE_X || + mRegs[i].mLinkerObject != set.mRegs[i].mLinkerObject || + mRegs[i].mValue != set.mRegs[i].mValue || + !mRegs[CPU_REG_X].SameData(set.mRegs[CPU_REG_X])) + { + mRegs[i].Reset(); + changed = true; + } + } + else if (mRegs[i].mMode == NRDM_ABSOLUTE_Y) + { + if (set.mRegs[i].mMode != NRDM_ABSOLUTE_Y || + mRegs[i].mLinkerObject != set.mRegs[i].mLinkerObject || + mRegs[i].mValue != set.mRegs[i].mValue || + !mRegs[CPU_REG_Y].SameData(set.mRegs[CPU_REG_Y])) + { + mRegs[i].Reset(); + changed = true; + } + } + } + + } while (changed); +} + +ValueNumberingData::ValueNumberingData(void) +{ + mIndex = GlobalValueNumber++; + mOffset = 0; +} + +void ValueNumberingData::Reset(void) +{ + mIndex = GlobalValueNumber++; + mOffset = 0; +} + +bool ValueNumberingData::SameBase(const ValueNumberingData& d) const +{ + return mIndex == d.mIndex; +} + +int ValueNumberingDataSet::Find(const ValueNumberingData& data) const +{ + for (int i = 0; i < 256; i++) + { + if (mRegs[i].mIndex == data.mIndex && mRegs[i].mOffset == data.mOffset) + return i; + } + return -1; +} + +void ValueNumberingDataSet::Reset(void) +{ + for (int i = 0; i < 261; i++) + mRegs[i].Reset(); +} + +void ValueNumberingDataSet::ResetWorkRegs(void) +{ + mRegs[BC_REG_WORK_Y].Reset(); + mRegs[BC_REG_ADDR].Reset(); + mRegs[BC_REG_ADDR + 1].Reset(); + + for (int i = 0; i < 4; i++) + mRegs[BC_REG_ACCU + i].Reset(); + for (int i = 0; i < 8; i++) + mRegs[BC_REG_WORK + i].Reset(); +} + +void ValueNumberingDataSet::ResetCall(const NativeCodeInstruction& ins) +{ + mRegs[CPU_REG_C].Reset(); + mRegs[CPU_REG_Z].Reset(); + mRegs[CPU_REG_A].Reset(); + mRegs[CPU_REG_X].Reset(); + mRegs[CPU_REG_Y].Reset(); + + ResetWorkRegs(); + + if (!(ins.mFlags & NCIF_RUNTIME) || (ins.mFlags & NCIF_FEXEC)) + { + if (ins.mLinkerObject && ins.mLinkerObject->mProc) + { + if (!(ins.mFlags & NCIF_FEXEC) && (ins.mLinkerObject->mFlags & LOBJF_ZEROPAGESET)) + { + for (int i = 0; i < 256; i++) + if (ins.mLinkerObject->mZeroPageSet[i]) + mRegs[i].Reset(); + return; + } + + for (int i = BC_REG_TMP; i < BC_REG_TMP + ins.mLinkerObject->mProc->mCallerSavedTemps; i++) + mRegs[i].Reset(); + } + else + { + for (int i = BC_REG_TMP; i < BC_REG_TMP_SAVED; i++) + mRegs[i].Reset(); + } + + for (int i = BC_REG_FPARAMS; i < BC_REG_FPARAMS_END; i++) + mRegs[i].Reset(); + } + +} + +void ValueNumberingDataSet::Intersect(const ValueNumberingDataSet& set) +{ + for (int i = 0; i < 261; i++) + { + if (mRegs[i].mIndex != set.mRegs[i].mIndex || mRegs[i].mOffset != set.mRegs[i].mOffset) + mRegs[i].Reset(); + } +} + +ValueReuseData::ValueReuseData(void) + : mBlock(nullptr) +{ + +} + +void ValueReuseData::Reset(void) +{ + mBlock = nullptr; +} + + +void ValueReuseDataSet::Reset(void) +{ + for (int i = 0; i < 256; i++) + mRegs[i].Reset(); +} + +void ValueReuseDataSet::ResetWorkRegs(void) +{ + mRegs[BC_REG_WORK_Y].Reset(); + mRegs[BC_REG_ADDR].Reset(); + mRegs[BC_REG_ADDR + 1].Reset(); + + for (int i = 0; i < 4; i++) + mRegs[BC_REG_ACCU + i].Reset(); + for (int i = 0; i < 8; i++) + mRegs[BC_REG_WORK + i].Reset(); +} + +void ValueReuseDataSet::ResetCall(const NativeCodeInstruction& ins) +{ + ResetWorkRegs(); + + if (!(ins.mFlags & NCIF_RUNTIME) || (ins.mFlags & NCIF_FEXEC)) + { + if (ins.mLinkerObject && ins.mLinkerObject->mProc) + { + if (!(ins.mFlags & NCIF_FEXEC) && (ins.mLinkerObject->mFlags & LOBJF_ZEROPAGESET)) + { + for (int i = 0; i < 256; i++) + if (ins.mLinkerObject->mZeroPageSet[i]) + mRegs[i].Reset(); + return; + } + + for (int i = BC_REG_TMP; i < BC_REG_TMP + ins.mLinkerObject->mProc->mCallerSavedTemps; i++) + mRegs[i].Reset(); + } + else + { + for (int i = BC_REG_TMP; i < BC_REG_TMP_SAVED; i++) + mRegs[i].Reset(); + } + + for (int i = BC_REG_FPARAMS; i < BC_REG_FPARAMS_END; i++) + mRegs[i].Reset(); + } +} + +void ValueReuseDataSet::Intersect(const ValueReuseDataSet& set) +{ + for (int i = 0; i < 261; i++) + { + if (!set.mRegs[i].mBlock || set.mRegs[i].mBlock != mRegs[i].mBlock || set.mRegs[i].mIndex != mRegs[i].mIndex) + mRegs[i].mBlock = nullptr; + } +} + + +NativeCodeInstruction::NativeCodeInstruction(void) + : mIns(nullptr), mType(ASMIT_INV), mMode(ASMIM_IMPLIED), mAddress(0), mLinkerObject(nullptr), mFlags(NCIF_LOWER | NCIF_UPPER), mParam(0), mLive(LIVE_ALL), mMinVal(0), mMaxVal(255) +{} + +NativeCodeInstruction::NativeCodeInstruction(const InterInstruction* ins, AsmInsType type, AsmInsMode mode, int64 address, LinkerObject* linkerObject, uint32 flags, int param, int minv, int maxv) + : mIns(ins), mType(type), mMode(mode), mAddress(int(address)), mLinkerObject(linkerObject), mFlags(flags), mParam(param), mLive(LIVE_ALL), mMinVal(minv), mMaxVal(maxv) +{ + if (mIns) + { + assert(mIns->mLocation.mFileName); + } + + if (mode == ASMIM_IMMEDIATE_ADDRESS) + { +#if 0 + assert(address >= 0); +#endif + assert((mFlags & (NCIF_LOWER | NCIF_UPPER)) != (NCIF_LOWER | NCIF_UPPER)); + assert(HasAsmInstructionMode(mType, ASMIM_IMMEDIATE)); + } + if (mode == ASMIM_IMMEDIATE) + { + assert(linkerObject || (address >= 0 && address < 256)); + } + if (mode == ASMIM_ZERO_PAGE) + { + assert(linkerObject || (address >= 1 && address < 256)); + } +} + +NativeCodeInstruction::NativeCodeInstruction(const InterInstruction* ins, AsmInsType type, const NativeCodeInstruction& addr) + : mIns(ins), mType(type), mMode(addr.mMode), mAddress(addr.mAddress), mLinkerObject(addr.mLinkerObject), mFlags(addr.mFlags), mParam(addr.mParam), mLive(LIVE_ALL), mMinVal(0), mMaxVal(255) +{ + if (mIns) + { + assert(mIns->mLocation.mFileName); + } +} + +const char* NativeCodeInstruction::AddrName(char* buffer) const +{ + if (mLinkerObject) + { + if (mLinkerObject->mIdent) + sprintf_s(buffer, 160, "%s + %d", mLinkerObject->mIdent->mString, mAddress); + else + sprintf_s(buffer, 160, "_lobj%d + %d", mLinkerObject->mID, mAddress); + } + else if (mAddress < 256) + sprintf_s(buffer, 160, "%02x", mAddress); + else + sprintf_s(buffer, 160, "%04x", mAddress); + + return buffer; +} + +void NativeCodeInstruction::Disassemble(FILE* file) const +{ + char buffer[160]; + char flags[4]; + char i = 0; + if (mFlags & NCIF_VOLATILE) + flags[i++] = 'V'; + if (mFlags & NCIF_MEMMAP) + flags[i++] = 'M'; + flags[i++] = 0; + + switch (mMode) + { + case ASMIM_IMPLIED: + fprintf(file, "%s", AsmInstructionNames[mType]); + break; + case ASMIM_IMMEDIATE: + fprintf(file, "%s #$%02x\t[%02x-%02x]", AsmInstructionNames[mType], mAddress, mMinVal, mMaxVal); + break; + case ASMIM_IMMEDIATE_ADDRESS: + if (mFlags & NCIF_LOWER) + fprintf(file, "%s #<%s\t[%02x-%02x]", AsmInstructionNames[mType], AddrName(buffer), mMinVal, mMaxVal); + else + fprintf(file, "%s #>%s\t[%02x-%02x]", AsmInstructionNames[mType], AddrName(buffer), mMinVal, mMaxVal); + break; + case ASMIM_ZERO_PAGE: + fprintf(file, "%s %s\t[%02x-%02x]", AsmInstructionNames[mType], AddrName(buffer), mMinVal, mMaxVal); + break; + case ASMIM_ZERO_PAGE_X: + fprintf(file, "%s %s, x\t[%02x-%02x]", AsmInstructionNames[mType], AddrName(buffer), mMinVal, mMaxVal); + break; + case ASMIM_ZERO_PAGE_Y: + fprintf(file, "%s %s, y\t[%02x-%02x]", AsmInstructionNames[mType], AddrName(buffer), mMinVal, mMaxVal); + break; + case ASMIM_ABSOLUTE: + fprintf(file, "%s %s\t[%02x-%02x] {%s}", AsmInstructionNames[mType], AddrName(buffer), mMinVal, mMaxVal, flags); + break; + case ASMIM_ABSOLUTE_X: + fprintf(file, "%s %s, x\t[%02x-%02x] {%s}", AsmInstructionNames[mType], AddrName(buffer), mMinVal, mMaxVal, flags); + break; + case ASMIM_ABSOLUTE_Y: + fprintf(file, "%s %s, y\t[%02x-%02x] {%s}", AsmInstructionNames[mType], AddrName(buffer), mMinVal, mMaxVal, flags); + break; + case ASMIM_INDIRECT: + fprintf(file, "%s (%s)", AsmInstructionNames[mType], AddrName(buffer)); + break; + case ASMIM_INDIRECT_X: + fprintf(file, "%s (%s, x)\t[%02x-%02x] {%s}", AsmInstructionNames[mType], AddrName(buffer), mMinVal, mMaxVal, flags); + break; + case ASMIM_INDIRECT_Y: + fprintf(file, "%s (%s), y\t[%02x-%02x] {%s}", AsmInstructionNames[mType], AddrName(buffer), mMinVal, mMaxVal, flags); + break; + case ASMIM_RELATIVE: + fprintf(file, "%s %d", AsmInstructionNames[mType], mAddress); + break; + } +} + +bool NativeCodeInstruction::IsUsedResultInstructions(NumberSet& requiredTemps) +{ + bool used = false; + + mLive = 0; + if (requiredTemps[CPU_REG_A]) + mLive |= LIVE_CPU_REG_A; + if (requiredTemps[CPU_REG_X]) + mLive |= LIVE_CPU_REG_X; + if (requiredTemps[CPU_REG_Y]) + mLive |= LIVE_CPU_REG_Y; + if (requiredTemps[CPU_REG_Z]) + mLive |= LIVE_CPU_REG_Z; + if (requiredTemps[CPU_REG_C]) + mLive |= LIVE_CPU_REG_C; + if (mMode == ASMIM_ZERO_PAGE && requiredTemps[mAddress]) + mLive |= LIVE_MEM; + if (mMode == ASMIM_INDIRECT_Y && (requiredTemps[mAddress] || requiredTemps[mAddress + 1])) + mLive |= LIVE_MEM; + + if (mType == ASMIT_JSR) + { + requiredTemps -= CPU_REG_C; + requiredTemps -= CPU_REG_Z; + if (!(mFlags & NCIF_PRESERVE_CPU_REG_A)) + requiredTemps -= CPU_REG_A; + if (!(mFlags & NCIF_PRESERVE_CPU_REG_X)) + requiredTemps -= CPU_REG_X; + if (!(mFlags & NCIF_PRESERVE_CPU_REG_Y)) + requiredTemps -= CPU_REG_Y; + + if (mFlags & NCIF_USE_CPU_REG_A) + requiredTemps += CPU_REG_A; + if (mFlags & NCIF_USE_CPU_REG_X) + requiredTemps += CPU_REG_X; + if (mFlags & NCIF_USE_CPU_REG_Y) + requiredTemps += CPU_REG_Y; + if (mFlags & NCIF_USE_CPU_REG_C) + requiredTemps += CPU_REG_C; + + if (mFlags & NCIF_RUNTIME) + { + for (int i = 0; i < 4; i++) + { + requiredTemps += BC_REG_ACCU + i; + requiredTemps += BC_REG_WORK + i; + } + + if (mFlags & NCIF_USE_ZP_32_X) + { + for (int i = 0; i < 4; i++) + requiredTemps += mParam + i; + } + + if (mFlags & NCIF_FEXEC) + { + requiredTemps += BC_REG_LOCALS; + requiredTemps += BC_REG_LOCALS + 1; + for(int i= BC_REG_FPARAMS; i< BC_REG_FPARAMS_END; i++) + requiredTemps += i; + } + } + else + { + if (mLinkerObject) + { + for (int i = 0; i < 4; i++) + { + if (mLinkerObject->mZeroPageSet[BC_REG_ACCU + i]) + requiredTemps -= BC_REG_ACCU + i; + if (mLinkerObject->mZeroPageSet[BC_REG_WORK + i]) + requiredTemps -= BC_REG_WORK + i; + } + } + + if (mFlags & NICF_USE_WORKREGS) + { + for (int i = 0; i < 10; i++) + requiredTemps += BC_REG_WORK + i; + } + + requiredTemps += BC_REG_LOCALS; + requiredTemps += BC_REG_LOCALS + 1; + if (mLinkerObject) + { + for (int i = 0; i < mLinkerObject->mNumTemporaries; i++) + { + for (int j = 0; j < mLinkerObject->mTempSizes[i]; j++) + requiredTemps += mLinkerObject->mTemporaries[i] + j; + } + } + } + + return true; + } + + if (mType == ASMIT_RTS) + { +#if 1 + if (mFlags & NCIF_USE_CPU_REG_A) + requiredTemps += CPU_REG_A; + else if (mFlags & NCIF_LOWER) + { + requiredTemps += BC_REG_ACCU; + if (mFlags & NCIF_UPPER) + { + requiredTemps += BC_REG_ACCU + 1; + + if (mFlags & NCIF_LONG) + { + requiredTemps += BC_REG_ACCU + 2; + requiredTemps += BC_REG_ACCU + 3; + } + } + } +#endif +#if 0 + for (int i = 0; i < 4; i++) + { + requiredTemps += BC_REG_ACCU + i; + } +#endif + + requiredTemps += BC_REG_STACK; + requiredTemps += BC_REG_STACK + 1; + requiredTemps += BC_REG_LOCALS; + requiredTemps += BC_REG_LOCALS + 1; + + return true; + } + + if (mType == ASMIT_BYTE) + return true; + + // check side effects + + if (mFlags & NCIF_VOLATILE) + { + if (mMode != ASMIM_IMPLIED && mMode != ASMIM_ZERO_PAGE && !(mMode == ASMIM_ABSOLUTE && mLinkerObject)) + used = true; + } + + switch (mType) + { + case ASMIT_STA: + case ASMIT_STX: + case ASMIT_STY: + case ASMIT_INC: + case ASMIT_DEC: + case ASMIT_ASL: + case ASMIT_LSR: + case ASMIT_ROL: + case ASMIT_ROR: + if (mMode != ASMIM_IMPLIED && mMode != ASMIM_ZERO_PAGE) + used = true; + break; + case ASMIT_JSR: + case ASMIT_JMP: + case ASMIT_BEQ: + case ASMIT_BNE: + case ASMIT_BPL: + case ASMIT_BMI: + case ASMIT_BCC: + case ASMIT_BCS: + used = true; + break; + } + + if (requiredTemps[CPU_REG_C]) + { + switch (mType) + { + case ASMIT_CLC: + case ASMIT_SEC: + case ASMIT_ADC: + case ASMIT_SBC: + case ASMIT_ROL: + case ASMIT_ROR: + case ASMIT_CMP: + case ASMIT_ASL: + case ASMIT_LSR: + case ASMIT_CPX: + case ASMIT_CPY: + used = true; + break; + } + } + + if (requiredTemps[CPU_REG_Z]) + { + switch (mType) + { + case ASMIT_ADC: + case ASMIT_SBC: + case ASMIT_ROL: + case ASMIT_ROR: + case ASMIT_INC: + case ASMIT_DEC: + case ASMIT_CMP: + case ASMIT_CPX: + case ASMIT_CPY: + case ASMIT_ASL: + case ASMIT_LSR: + case ASMIT_ORA: + case ASMIT_EOR: + case ASMIT_AND: + case ASMIT_LDA: + case ASMIT_LDX: + case ASMIT_LDY: + case ASMIT_BIT: + case ASMIT_INX: + case ASMIT_DEX: + case ASMIT_INY: + case ASMIT_DEY: + case ASMIT_TYA: + case ASMIT_TXA: + case ASMIT_TAY: + case ASMIT_TAX: + used = true; + break; + } + } + + if (requiredTemps[CPU_REG_A]) + { + switch (mType) + { + case ASMIT_ROL: + case ASMIT_ROR: + case ASMIT_ASL: + case ASMIT_LSR: + if (mMode == ASMIM_IMPLIED) + used = true; + break; + case ASMIT_ADC: + case ASMIT_SBC: + case ASMIT_ORA: + case ASMIT_EOR: + case ASMIT_AND: + case ASMIT_LDA: + case ASMIT_TXA: + case ASMIT_TYA: + used = true; + break; + } + } + + if (requiredTemps[CPU_REG_X]) + { + switch (mType) + { + case ASMIT_LDX: + case ASMIT_INX: + case ASMIT_DEX: + case ASMIT_TAX: + used = true; + break; + } + } + + if (requiredTemps[CPU_REG_Y]) + { + switch (mType) + { + case ASMIT_LDY: + case ASMIT_INY: + case ASMIT_DEY: + case ASMIT_TAY: + used = true; + break; + } + } + + if (mMode == ASMIM_ZERO_PAGE) + { + switch (mType) + { + case ASMIT_ROL: + case ASMIT_ROR: + case ASMIT_ASL: + case ASMIT_LSR: + case ASMIT_INC: + case ASMIT_DEC: + case ASMIT_STA: + case ASMIT_STX: + case ASMIT_STY: + if (requiredTemps[mAddress]) + used = true; + break; + } + } + + if (used) + { + switch (mMode) + { + case ASMIM_ZERO_PAGE_X: + case ASMIM_INDIRECT_X: + case ASMIM_ABSOLUTE_X: + requiredTemps += CPU_REG_X; + break; + + case ASMIM_ZERO_PAGE_Y: + case ASMIM_ABSOLUTE_Y: + requiredTemps += CPU_REG_Y; + break; + + case ASMIM_INDIRECT_Y: + requiredTemps += CPU_REG_Y; + requiredTemps += mAddress; + requiredTemps += mAddress + 1; + break; + + case ASMIM_IMMEDIATE: + if (mFlags & NICF_TMPREF) + { + if (mFlags & NCIF_LOWER) + requiredTemps += mAddress; + if (mFlags & NCIF_UPPER) + requiredTemps += mAddress + 1; + } + break; + } + + // check carry flags + + switch (mType) + { + case ASMIT_ADC: + case ASMIT_SBC: + case ASMIT_ROL: + case ASMIT_ROR: + requiredTemps += CPU_REG_C; + break; + case ASMIT_CMP: + case ASMIT_ASL: + case ASMIT_LSR: + case ASMIT_CPX: + case ASMIT_CPY: + case ASMIT_CLC: + case ASMIT_SEC: + requiredTemps -= CPU_REG_C; + break; + case ASMIT_BCC: + case ASMIT_BCS: + requiredTemps += CPU_REG_C; + break; + case ASMIT_BEQ: + case ASMIT_BNE: + case ASMIT_BPL: + case ASMIT_BMI: + requiredTemps += CPU_REG_Z; + break; + } + + // check zero flags + + switch (mType) + { + case ASMIT_ADC: + case ASMIT_SBC: + case ASMIT_ROL: + case ASMIT_ROR: + case ASMIT_CMP: + case ASMIT_CPX: + case ASMIT_CPY: + case ASMIT_ASL: + case ASMIT_LSR: + case ASMIT_INC: + case ASMIT_DEC: + case ASMIT_ORA: + case ASMIT_EOR: + case ASMIT_AND: + case ASMIT_LDA: + case ASMIT_LDX: + case ASMIT_LDY: + case ASMIT_BIT: + case ASMIT_TAY: + case ASMIT_TYA: + case ASMIT_TAX: + case ASMIT_TXA: + case ASMIT_INX: + case ASMIT_DEX: + case ASMIT_INY: + case ASMIT_DEY: + requiredTemps -= CPU_REG_Z; + break; + } + + // check CPU register + + switch (mType) + { + case ASMIT_ROL: + case ASMIT_ROR: + case ASMIT_ASL: + case ASMIT_LSR: + if (mMode == ASMIM_IMPLIED) + requiredTemps += CPU_REG_A; + break; + + case ASMIT_LDA: + requiredTemps -= CPU_REG_A; + break; + + case ASMIT_ADC: + case ASMIT_SBC: + case ASMIT_ORA: + case ASMIT_EOR: + case ASMIT_AND: + requiredTemps += CPU_REG_A; + break; + case ASMIT_LDX: + requiredTemps -= CPU_REG_X; + break; + case ASMIT_INX: + case ASMIT_DEX: + requiredTemps += CPU_REG_X; + break; + case ASMIT_LDY: + requiredTemps -= CPU_REG_Y; + break; + case ASMIT_INY: + case ASMIT_DEY: + requiredTemps += CPU_REG_Y; + break; + + case ASMIT_CMP: + case ASMIT_STA: + requiredTemps += CPU_REG_A; + break; + case ASMIT_CPX: + case ASMIT_STX: + requiredTemps += CPU_REG_X; + break; + case ASMIT_CPY: + case ASMIT_STY: + requiredTemps += CPU_REG_Y; + break; + + case ASMIT_TXA: + requiredTemps += CPU_REG_X; + requiredTemps -= CPU_REG_A; + break; + case ASMIT_TYA: + requiredTemps += CPU_REG_Y; + requiredTemps -= CPU_REG_A; + break; + case ASMIT_TAX: + requiredTemps += CPU_REG_A; + requiredTemps -= CPU_REG_X; + break; + case ASMIT_TAY: + requiredTemps += CPU_REG_A; + requiredTemps -= CPU_REG_Y; + break; + } + + if (mMode == ASMIM_ZERO_PAGE) + { + switch (mType) + { + case ASMIT_STA: + case ASMIT_STX: + case ASMIT_STY: + requiredTemps -= mAddress; + break; + default: + requiredTemps += mAddress; + } + } + + return true; + } + + return false; +} + +bool NativeCodeInstruction::LoadsAccu(void) const +{ + return mType == ASMIT_LDA || mType == ASMIT_TXA || mType == ASMIT_TYA || mType == ASMIT_JSR; +} + +bool NativeCodeInstruction::ChangesAccuAndFlag(void) const +{ + if (mType == ASMIT_LDA || mType == ASMIT_TXA || mType == ASMIT_TYA || + mType == ASMIT_ORA || mType == ASMIT_AND || mType == ASMIT_EOR || + mType == ASMIT_SBC || mType == ASMIT_ADC) + return true; + else if (mType == ASMIT_LSR || mType == ASMIT_ASL || mType == ASMIT_ROR || mType == ASMIT_ROL) + return mMode == ASMIM_IMPLIED; + else + return false; +} + +bool NativeCodeInstruction::ChangesFlagToAccu(void) const +{ + if (mType == ASMIT_LDA || mType == ASMIT_TXA || mType == ASMIT_TYA || + mType == ASMIT_ORA || mType == ASMIT_AND || mType == ASMIT_EOR || + mType == ASMIT_SBC || mType == ASMIT_ADC || + mType == ASMIT_TAX || mType == ASMIT_TAY) + return true; + else if (mType == ASMIT_LSR || mType == ASMIT_ASL || mType == ASMIT_ROR || mType == ASMIT_ROL) + return mMode == ASMIM_IMPLIED; + else + return false; +} + +uint32 NativeCodeInstruction::NeedsLive(void) const +{ + uint32 live = mLive; + if (mMode == ASMIM_ABSOLUTE_Y || mMode == ASMIM_INDIRECT_Y || mMode == ASMIM_ZERO_PAGE_Y) + live |= LIVE_CPU_REG_Y; + if (mMode == ASMIM_ABSOLUTE_X || mMode == ASMIM_INDIRECT_X || mMode == ASMIM_ZERO_PAGE_X) + live |= LIVE_CPU_REG_X; + + if (mType == ASMIT_TYA || mType == ASMIT_STY || mType == ASMIT_CPY || mType == ASMIT_INY || mType == ASMIT_DEY) + live |= LIVE_CPU_REG_Y; + if (mType == ASMIT_TXA || mType == ASMIT_STX || mType == ASMIT_CPX || mType == ASMIT_INX || mType == ASMIT_DEX) + live |= LIVE_CPU_REG_X; + + if (mMode == ASMIM_IMPLIED && (mType == ASMIT_TAX || mType == ASMIT_TAY || + mType == ASMIT_ASL || mType == ASMIT_LSR || mType == ASMIT_ROL || mType == ASMIT_ROR)) + live |= LIVE_CPU_REG_A; + + if (mType == ASMIT_STA || + mType == ASMIT_ORA || mType == ASMIT_AND || mType == ASMIT_EOR || + mType == ASMIT_SBC || mType == ASMIT_ADC || mType == ASMIT_CMP) + live |= LIVE_CPU_REG_A; + + if (mType == ASMIT_ADC || mType == ASMIT_SBC || mType == ASMIT_ROL || mType == ASMIT_ROR) + live |= LIVE_CPU_REG_C; + + return live; +} + +bool NativeCodeInstruction::RequiresYReg(void) const +{ + if (mMode == ASMIM_ABSOLUTE_Y || mMode == ASMIM_INDIRECT_Y || mMode == ASMIM_ZERO_PAGE_Y) + return true; + if (mType == ASMIT_TYA || mType == ASMIT_STY || mType == ASMIT_CPY || mType == ASMIT_INY || mType == ASMIT_DEY) + return true; + + return false; +} + +bool NativeCodeInstruction::RequiresXReg(void) const +{ + if (mMode == ASMIM_ABSOLUTE_X || mMode == ASMIM_INDIRECT_X || mMode == ASMIM_ZERO_PAGE_X) + return true; + if (mType == ASMIT_TXA || mType == ASMIT_STX || mType == ASMIT_CPX || mType == ASMIT_INX || mType == ASMIT_DEX) + return true; + + return false; +} + + +bool NativeCodeInstruction::ReplaceYRegWithXReg(void) +{ + bool changed = false; + + switch (mType) + { + case ASMIT_LDY: + mType = ASMIT_LDX; + changed = true; + break; + case ASMIT_STY: + mType = ASMIT_STX; + changed = true; + break; + case ASMIT_CPY: + mType = ASMIT_CPX; + changed = true; + break; + case ASMIT_TYA: + mType = ASMIT_TXA; + changed = true; + break; + case ASMIT_TAY: + mType = ASMIT_TAX; + changed = true; + break; + case ASMIT_INY: + mType = ASMIT_INX; + changed = true; + break; + case ASMIT_DEY: + mType = ASMIT_DEX; + changed = true; + break; + } + + if (mMode == ASMIM_ABSOLUTE_Y) + { + assert(HasAsmInstructionMode(mType, ASMIM_ABSOLUTE_X)); + mMode = ASMIM_ABSOLUTE_X; + changed = true; + } + + if (mLive & LIVE_CPU_REG_Y) + mLive |= LIVE_CPU_REG_X; + + return changed; +} + +bool NativeCodeInstruction::CanSwapXYReg(void) +{ + if (mMode == ASMIM_INDIRECT_X || mMode == ASMIM_INDIRECT_Y || mMode == ASMIM_ZERO_PAGE_X || mMode == ASMIM_ZERO_PAGE_Y) + return false; + else if (mMode == ASMIM_ABSOLUTE_X) + return mType == ASMIT_LDY || HasAsmInstructionMode(mType, ASMIM_ABSOLUTE_Y); + else if (mMode == ASMIM_ABSOLUTE_Y) + return mType == ASMIT_LDX || HasAsmInstructionMode(mType, ASMIM_ABSOLUTE_X); + else if (mType == ASMIT_JSR && (mFlags & (NCIF_USE_CPU_REG_X | NCIF_USE_CPU_REG_Y | NCIF_PROVIDE_CPU_REG_X | NCIF_PROVIDE_CPU_REG_Y))) + return false; + else + return true; +} + +bool NativeCodeInstruction::SwapXYReg(void) +{ + bool changed = false; + + switch (mType) + { + case ASMIT_LDX: + mType = ASMIT_LDY; + changed = true; + break; + case ASMIT_STX: + mType = ASMIT_STY; + changed = true; + break; + case ASMIT_CPX: + mType = ASMIT_CPY; + changed = true; + break; + case ASMIT_TXA: + mType = ASMIT_TYA; + changed = true; + break; + case ASMIT_TAX: + mType = ASMIT_TAY; + changed = true; + break; + case ASMIT_INX: + mType = ASMIT_INY; + changed = true; + break; + case ASMIT_DEX: + mType = ASMIT_DEY; + changed = true; + break; + case ASMIT_LDY: + mType = ASMIT_LDX; + changed = true; + break; + case ASMIT_STY: + mType = ASMIT_STX; + changed = true; + break; + case ASMIT_CPY: + mType = ASMIT_CPX; + changed = true; + break; + case ASMIT_TYA: + mType = ASMIT_TXA; + changed = true; + break; + case ASMIT_TAY: + mType = ASMIT_TAX; + changed = true; + break; + case ASMIT_INY: + mType = ASMIT_INX; + changed = true; + break; + case ASMIT_DEY: + mType = ASMIT_DEX; + changed = true; + break; + } + + if (mMode == ASMIM_ABSOLUTE_X) + { + assert(HasAsmInstructionMode(mType, ASMIM_ABSOLUTE_Y)); + mMode = ASMIM_ABSOLUTE_Y; + changed = true; + } + else if (mMode == ASMIM_ABSOLUTE_Y) + { + assert(HasAsmInstructionMode(mType, ASMIM_ABSOLUTE_X)); + mMode = ASMIM_ABSOLUTE_X; + changed = true; + } + + uint32 live = mLive; + mLive &= ~(LIVE_CPU_REG_X | LIVE_CPU_REG_Y); + if (live & LIVE_CPU_REG_X) + mLive |= LIVE_CPU_REG_Y; + if (live & LIVE_CPU_REG_Y) + mLive |= LIVE_CPU_REG_X; + + return changed; + +} + +static void UpdateCollisionSet(NumberSet& liveTemps, NumberSet* collisionSets, int temp) +{ + int i; + + if (temp >= 0 && !liveTemps[temp]) + { + for (i = 0; i < liveTemps.Size(); i++) + { + if (liveTemps[i]) + { + collisionSets[i] += temp; + collisionSets[temp] += i; + } + } + + liveTemps += temp; + } +} + +void NativeCodeInstruction::BuildCollisionTable(NumberSet& liveTemps, NumberSet* collisionSets) +{ + if (mMode == ASMIM_ZERO_PAGE) + { + if (ChangesAddress()) + liveTemps -= mAddress; + if (UsesAddress()) + UpdateCollisionSet(liveTemps, collisionSets, mAddress); + } + if (mMode == ASMIM_INDIRECT_Y) + { + UpdateCollisionSet(liveTemps, collisionSets, mAddress); + UpdateCollisionSet(liveTemps, collisionSets, mAddress + 1); + } + if (mType == ASMIT_JSR) + { + for(int i= BC_REG_ACCU; i< BC_REG_ACCU + 4; i++) + UpdateCollisionSet(liveTemps, collisionSets, i); + for (int i = BC_REG_WORK; i < BC_REG_WORK + 4; i++) + UpdateCollisionSet(liveTemps, collisionSets, i); + + if (mFlags & NCIF_RUNTIME) + { + + if (mFlags & NCIF_USE_ZP_32_X) + { + for (int i = mParam; i < mParam + 4; i++) + UpdateCollisionSet(liveTemps, collisionSets, i); + } + + if (mFlags & NCIF_FEXEC) + { + for (int i = BC_REG_FPARAMS; i < BC_REG_FPARAMS_END; i++) + UpdateCollisionSet(liveTemps, collisionSets, i); + } + } + else + { + for (int i = BC_REG_FPARAMS; i < BC_REG_FPARAMS_END; i++) + UpdateCollisionSet(liveTemps, collisionSets, i); + + if (mLinkerObject && mLinkerObject->mProc) + { + for (int i = BC_REG_TMP; i < BC_REG_TMP + mLinkerObject->mProc->mCallerSavedTemps; i++) + UpdateCollisionSet(liveTemps, collisionSets, i); + } + else if (mLinkerObject && mLinkerObject->mNumTemporaries) + { + for (int i = 0; i < mLinkerObject->mNumTemporaries; i++) + { + for (int j = 0; j < mLinkerObject->mTempSizes[i]; j++) + UpdateCollisionSet(liveTemps, collisionSets, mLinkerObject->mTemporaries[i] + j); + } + } + else + { + for (int i = BC_REG_TMP; i < BC_REG_TMP_SAVED; i++) + UpdateCollisionSet(liveTemps, collisionSets, i); + } + } + } + else if (mType == ASMIT_RTS) + { + if (mFlags & NCIF_LOWER) + { + UpdateCollisionSet(liveTemps, collisionSets, BC_REG_ACCU + 0); + + if (mFlags & NCIF_UPPER) + { + UpdateCollisionSet(liveTemps, collisionSets, BC_REG_ACCU + 1); + + if (mFlags & NCIF_LONG) + { + UpdateCollisionSet(liveTemps, collisionSets, BC_REG_ACCU + 2); + UpdateCollisionSet(liveTemps, collisionSets, BC_REG_ACCU + 3); + } + } + } + } +} + + +bool NativeCodeInstruction::ReplaceXRegWithYReg(void) +{ + bool changed = false; + + switch (mType) + { + case ASMIT_LDX: + mType = ASMIT_LDY; + changed = true; + break; + case ASMIT_STX: + mType = ASMIT_STY; + changed = true; + break; + case ASMIT_CPX: + mType = ASMIT_CPY; + changed = true; + break; + case ASMIT_TXA: + mType = ASMIT_TYA; + changed = true; + break; + case ASMIT_TAX: + mType = ASMIT_TAY; + changed = true; + break; + case ASMIT_INX: + mType = ASMIT_INY; + changed = true; + break; + case ASMIT_DEX: + mType = ASMIT_DEY; + changed = true; + break; + } + + if (mMode == ASMIM_ABSOLUTE_X) + { + assert(HasAsmInstructionMode(mType, ASMIM_ABSOLUTE_Y)); + mMode = ASMIM_ABSOLUTE_Y; + changed = true; + } + + if (mLive & LIVE_CPU_REG_X) + mLive |= LIVE_CPU_REG_Y; + + return changed; +} + +bool NativeCodeInstruction::ChangesYReg(void) const +{ + if (mType == ASMIT_JSR) + return !(mFlags & NCIF_PRESERVE_CPU_REG_Y); + else + return mType == ASMIT_TAY || mType == ASMIT_LDY || mType == ASMIT_INY || mType == ASMIT_DEY; +} + +bool NativeCodeInstruction::ChangesXReg(void) const +{ + if (mType == ASMIT_JSR) + return !(mFlags & NCIF_PRESERVE_CPU_REG_X); + else + return mType == ASMIT_TAX || mType == ASMIT_LDX || mType == ASMIT_INX || mType == ASMIT_DEX; +} + +bool NativeCodeInstruction::ReferencesCarry(void) const +{ + return ChangesCarry() || RequiresCarry(); +} + +bool NativeCodeInstruction::ReferencesAccu(void) const +{ + return ChangesAccu() || RequiresAccu(); +} + +bool NativeCodeInstruction::ReferencesYReg(void) const +{ + return ChangesYReg() || RequiresYReg(); +} + +bool NativeCodeInstruction::ReferencesXReg(void) const +{ + return ChangesXReg() || RequiresXReg(); +} + +bool NativeCodeInstruction::ReferencesZeroPage(int address) const +{ + return UsesZeroPage(address) || ChangesZeroPage(address); +} + +bool NativeCodeInstruction::ChangesZeroPage(int address) const +{ + if (mMode == ASMIM_ZERO_PAGE && mAddress == address) + return mType == ASMIT_INC || mType == ASMIT_DEC || mType == ASMIT_ASL || mType == ASMIT_LSR || mType == ASMIT_ROL || mType == ASMIT_ROR || mType == ASMIT_STA || mType == ASMIT_STX || mType == ASMIT_STY; + else if (mType == ASMIT_JSR) + { + if (address == BC_REG_WORK_Y) + return true; + if (address >= BC_REG_ACCU && address < BC_REG_ACCU + 4) + return true; + if (address >= BC_REG_WORK && address < BC_REG_WORK + 8) + return true; + if (address >= BC_REG_ADDR && address < BC_REG_ADDR + 4) + return true; + + if (!(mFlags & NCIF_RUNTIME) || (mFlags & NCIF_FEXEC)) + { + if (mLinkerObject && mLinkerObject->mProc) + { + if (!(mFlags & NCIF_FEXEC) && (mLinkerObject->mFlags & LOBJF_ZEROPAGESET)) + return mLinkerObject->mZeroPageSet[address]; + if (address >= BC_REG_TMP && address < BC_REG_TMP + mLinkerObject->mProc->mCallerSavedTemps) + return true; + } + else if (!mLinkerObject) + { + return false; + } + else + { + if (address >= BC_REG_TMP && address < BC_REG_TMP_SAVED) + return true; + } + + if (address >= BC_REG_FPARAMS && address < BC_REG_FPARAMS_END) + return true; + } + + return false; + } + else + return false; +} + +bool NativeCodeInstruction::UsesZeroPage(int address) const +{ + if (mMode == ASMIM_ZERO_PAGE && mAddress == address) + return true; + else if (mMode == ASMIM_INDIRECT_Y && (mAddress == address || mAddress + 1 == address)) + return true; + else if (mType == ASMIT_JSR) + { + if (mFlags & NCIF_RUNTIME) + { + if (address >= BC_REG_ACCU && address < BC_REG_ACCU + 4) + return true; + + if (address >= BC_REG_WORK && address < BC_REG_WORK + 8) + return true; + + if (mFlags & NCIF_USE_ZP_32_X) + { + if (address >= mParam && address < mParam + 4) + return true; + } + + if (mFlags & NCIF_FEXEC) + { + if (address >= BC_REG_FPARAMS && address < BC_REG_FPARAMS_END) + return true; + } + } + else + { + if (mFlags & NICF_USE_WORKREGS) + { + if (address >= BC_REG_WORK && address < BC_REG_WORK + 10) + return true; + } + + if (address >= BC_REG_FPARAMS && address < BC_REG_FPARAMS_END) + return true; + + if (mLinkerObject) + { + for (int i = 0; i < mLinkerObject->mNumTemporaries; i++) + { + if (address >= mLinkerObject->mTemporaries[i] && address < mLinkerObject->mTemporaries[i] + mLinkerObject->mTempSizes[i]) + return true; + } + } + } + + return false; + } + else if (mType == ASMIT_RTS) + { + if (mFlags & NCIF_LOWER) + { + if (address == BC_REG_ACCU + 0) + return true; + + if (mFlags & NCIF_UPPER) + { + if (address == BC_REG_ACCU + 1) + return true; + + if (mFlags & NCIF_LONG) + { + if (address == BC_REG_ACCU + 2 || address == BC_REG_ACCU + 3) + return true; + } + } + } + + return false; + } + else + return false; +} + +bool NativeCodeInstruction::LoadsZeroPage(int address) const +{ + if (mMode == ASMIM_ZERO_PAGE && mAddress == address) + return true; + else if (mType == ASMIT_JSR) + { + if (mFlags & NCIF_RUNTIME) + { + if (address >= BC_REG_ACCU && address < BC_REG_ACCU + 4) + return true; + + if (address >= BC_REG_WORK && address < BC_REG_WORK + 8) + return true; + + if (mFlags & NCIF_USE_ZP_32_X) + { + if (address >= mParam && address < mParam + 4) + return true; + } + + if (mFlags & NCIF_FEXEC) + { + if (address >= BC_REG_FPARAMS && address < BC_REG_FPARAMS_END) + return true; + } + } + else + { + if (mFlags & NICF_USE_WORKREGS) + { + if (address >= BC_REG_WORK && address < BC_REG_WORK + 10) + return true; + } + + if (address >= BC_REG_FPARAMS && address < BC_REG_FPARAMS_END) + return true; + + if (mLinkerObject) + { + for (int i = 0; i < mLinkerObject->mNumTemporaries; i++) + { + if (address >= mLinkerObject->mTemporaries[i] && address < mLinkerObject->mTemporaries[i] + mLinkerObject->mTempSizes[i]) + return true; + } + } + } + + return false; + } + else if (mType == ASMIT_RTS) + { + if (mFlags & NCIF_LOWER) + { + if (address == BC_REG_ACCU + 0) + return true; + + if (mFlags & NCIF_UPPER) + { + if (address == BC_REG_ACCU + 1) + return true; + + if (mFlags & NCIF_LONG) + { + if (address == BC_REG_ACCU + 2 || address == BC_REG_ACCU + 3) + return true; + } + } + } + + return false; + } + else + return false; +} + + +bool NativeCodeInstruction::IsPure(void) const +{ + return (mMode == ASMIM_IMMEDIATE || mMode == ASMIM_IMMEDIATE_ADDRESS || mMode == ASMIM_IMPLIED); +} + +bool NativeCodeInstruction::ChangesGlobalMemory(void) const +{ + if (mMode == ASMIM_INDIRECT_Y || mMode == ASMIM_ABSOLUTE || mMode == ASMIM_ABSOLUTE_X || mMode == ASMIM_ABSOLUTE_Y) + return mType == ASMIT_INC || mType == ASMIT_DEC || mType == ASMIT_ASL || mType == ASMIT_LSR || mType == ASMIT_ROL || mType == ASMIT_ROR || mType == ASMIT_STA || mType == ASMIT_STX || mType == ASMIT_STY || mType == ASMIT_JSR; + else + return false; +} + +bool NativeCodeInstruction::ReferencesGlobalMemory(void) const +{ + return mMode == ASMIM_INDIRECT_Y || mMode == ASMIM_ABSOLUTE || mMode == ASMIM_ABSOLUTE_X || mMode == ASMIM_ABSOLUTE_Y; +} + +bool NativeCodeInstruction::RequiresCarry(void) const +{ + if (mType == ASMIT_ADC || mType == ASMIT_SBC || + mType == ASMIT_ROL || mType == ASMIT_ROR) + { + return true; + } + else if (mType == ASMIT_JSR) + { + return mFlags & NCIF_USE_CPU_REG_C; + } + else + return false; +} + +bool NativeCodeInstruction::ChangesZFlag(void) const +{ + return + mType == ASMIT_ADC || mType == ASMIT_SBC || + mType == ASMIT_LSR || mType == ASMIT_ASL || mType == ASMIT_ROL || mType == ASMIT_ROR || + mType == ASMIT_INC || mType == ASMIT_DEC || + mType == ASMIT_INY || mType == ASMIT_DEY || + mType == ASMIT_INX || mType == ASMIT_DEX || + mType == ASMIT_TAX || mType == ASMIT_TAY || mType == ASMIT_TXA || mType == ASMIT_TYA || + mType == ASMIT_CMP || mType == ASMIT_CPX || mType == ASMIT_CPY || + mType == ASMIT_LDA || mType == ASMIT_LDX || mType == ASMIT_LDY || + mType == ASMIT_AND || mType == ASMIT_ORA || mType == ASMIT_EOR || + mType == ASMIT_BIT || + mType == ASMIT_JSR; +} + +bool NativeCodeInstruction::ChangesCarry(void) const +{ + return + mType == ASMIT_CLC || mType == ASMIT_SEC || + mType == ASMIT_ADC || mType == ASMIT_SBC || + mType == ASMIT_LSR || mType == ASMIT_ASL || mType == ASMIT_ROL || mType == ASMIT_ROR || + mType == ASMIT_CMP || mType == ASMIT_CPX || mType == ASMIT_CPY || + mType == ASMIT_JSR; +} + +bool NativeCodeInstruction::RequiresAccu(void) const +{ + if (mMode == ASMIM_IMPLIED) + { + return + mType == ASMIT_TAX || mType == ASMIT_TAY || + mType == ASMIT_ASL || mType == ASMIT_LSR || mType == ASMIT_ROL || mType == ASMIT_ROR; + } + else if (mType == ASMIT_JSR) + { + return (mFlags & NCIF_USE_CPU_REG_A); + } + else + { + return + mType == ASMIT_STA || + mType == ASMIT_ORA || mType == ASMIT_AND || mType == ASMIT_EOR || + mType == ASMIT_SBC || mType == ASMIT_ADC || mType == ASMIT_CMP; + } +} + +bool NativeCodeInstruction::UsesAccu(void) const +{ + if (ChangesAccu()) + return true; + + return mType == ASMIT_STA || mType == ASMIT_CMP || mType == ASMIT_TAX || mType == ASMIT_TAY; +} + +bool NativeCodeInstruction::ChangesAccu(void) const +{ + if (mMode == ASMIM_IMPLIED) + { + return + mType == ASMIT_TXA || mType == ASMIT_TYA || + mType == ASMIT_ASL || mType == ASMIT_LSR || mType == ASMIT_ROL || mType == ASMIT_ROR; + } + else if (mType == ASMIT_JSR) + return !(mFlags & NCIF_PRESERVE_CPU_REG_A); + else + { + return + mType == ASMIT_LDA || + mType == ASMIT_ORA || mType == ASMIT_AND || mType == ASMIT_EOR || + mType == ASMIT_SBC || mType == ASMIT_ADC; + } +} + + + +bool NativeCodeInstruction::UsesAddress(void) const +{ + if (mMode != ASMIM_IMPLIED) + { + return + mType == ASMIT_INC || mType == ASMIT_DEC || mType == ASMIT_ASL || mType == ASMIT_LSR || mType == ASMIT_ROL || mType == ASMIT_ROR || + mType == ASMIT_LDA || mType == ASMIT_LDX || mType == ASMIT_LDY || + mType == ASMIT_CMP || mType == ASMIT_CPX || mType == ASMIT_CPY || + mType == ASMIT_ADC || mType == ASMIT_SBC || mType == ASMIT_AND || mType == ASMIT_ORA || mType == ASMIT_EOR || mType == ASMIT_BIT; + } + else + return false; +} + +bool NativeCodeInstruction::ChangesAddress(void) const +{ + if (mMode != ASMIM_IMPLIED) + return mType == ASMIT_INC || mType == ASMIT_DEC || mType == ASMIT_ASL || mType == ASMIT_LSR || mType == ASMIT_ROL || mType == ASMIT_ROR || mType == ASMIT_STA || mType == ASMIT_STX || mType == ASMIT_STY; + else + return false; +} + +bool NativeCodeInstruction::IsSimpleJSR(void) const +{ + return mType == ASMIT_JSR && mMode == ASMIM_ABSOLUTE && !(mLinkerObject && (mLinkerObject->mFlags & LOBJF_INLINE)) && !(mFlags & NCIF_BREAKPOINT); +} + +bool NativeCodeInstruction::IsShift(void) const +{ + return mType == ASMIT_ASL || mType == ASMIT_LSR || mType == ASMIT_ROL || mType == ASMIT_ROR; +} + +bool NativeCodeInstruction::IsShiftOrInc(void) const +{ + return mType == ASMIT_INC || mType == ASMIT_DEC || mType == ASMIT_ASL || mType == ASMIT_LSR; +} + +bool NativeCodeInstruction::IsShiftRotOrInc(void) const +{ + return mType == ASMIT_INC || mType == ASMIT_DEC || mType == ASMIT_ASL || mType == ASMIT_LSR || mType == ASMIT_ROL || mType == ASMIT_ROR; +} + +bool NativeCodeInstruction::IsArithmetic(void) const +{ + return mType == ASMIT_ADC || mType == ASMIT_SBC || mType == ASMIT_AND || mType == ASMIT_ORA || mType == ASMIT_EOR; +} + +bool NativeCodeInstruction::IsAddSub(void) const +{ + return mType == ASMIT_ADC || mType == ASMIT_SBC; +} + +bool NativeCodeInstruction::IsCommutative(void) const +{ + return mType == ASMIT_ADC || mType == ASMIT_AND || mType == ASMIT_ORA || mType == ASMIT_EOR; +} + +bool NativeCodeInstruction::IsStore(void) const +{ + return mType == ASMIT_STA || mType == ASMIT_STX || mType == ASMIT_STY; +} + +bool NativeCodeInstruction::IsLoad(void) const +{ + return mType == ASMIT_LDA || mType == ASMIT_LDX || mType == ASMIT_LDY; +} + +bool NativeCodeInstruction::IsLogic(void) const +{ + return mType == ASMIT_AND || mType == ASMIT_ORA || mType == ASMIT_EOR; +} + + + +bool NativeCodeInstruction::IsSame(const NativeCodeInstruction& ins) const +{ + if (mType == ins.mType && mMode == ins.mMode && mParam == ins.mParam) + { + switch (mMode) + { + case ASMIM_IMPLIED: + return true; + case ASMIM_IMMEDIATE: + case ASMIM_ZERO_PAGE: + case ASMIM_ZERO_PAGE_X: + case ASMIM_ZERO_PAGE_Y: + case ASMIM_INDIRECT_X: + case ASMIM_INDIRECT_Y: + return ins.mAddress == mAddress; + case ASMIM_IMMEDIATE_ADDRESS: + return (ins.mLinkerObject == mLinkerObject && ins.mAddress == mAddress && ins.mFlags == mFlags); + case ASMIM_ABSOLUTE: + case ASMIM_ABSOLUTE_X: + case ASMIM_ABSOLUTE_Y: + return (ins.mLinkerObject == mLinkerObject && ins.mAddress == mAddress); + default: + return false; + } + } + else + return false; +} + +bool NativeCodeInstruction::IsSameLS(const NativeCodeInstruction& ins) const +{ + if ((mType == ins.mType || mType == ASMIT_STA && ins.mType == ASMIT_LDA) && mMode == ins.mMode && mParam == ins.mParam) + { + switch (mMode) + { + case ASMIM_IMPLIED: + return true; + case ASMIM_IMMEDIATE: + case ASMIM_ZERO_PAGE: + case ASMIM_ZERO_PAGE_X: + case ASMIM_ZERO_PAGE_Y: + case ASMIM_INDIRECT_X: + case ASMIM_INDIRECT_Y: + return ins.mAddress == mAddress; + case ASMIM_IMMEDIATE_ADDRESS: + return (ins.mLinkerObject == mLinkerObject && ins.mAddress == mAddress && ins.mFlags == mFlags); + case ASMIM_ABSOLUTE: + case ASMIM_ABSOLUTE_X: + case ASMIM_ABSOLUTE_Y: + return (ins.mLinkerObject == mLinkerObject && ins.mAddress == mAddress); + default: + return false; + } + } + else + return false; +} + +bool NativeCodeInstruction::MayBeMovedBefore(const NativeCodeInstruction& ins) const +{ + if (mType == ASMIT_RTS) + return false; + if (mType == ASMIT_JSR || ins.mType == ASMIT_JSR) + return false; + if ((mFlags & NCIF_VOLATILE) && (ins.mFlags & NCIF_VOLATILE)) + return false; + if ((mFlags & NCIF_MEMMAP) && ins.ReferencesGlobalMemory()) + return false; + if ((ins.mFlags & NCIF_MEMMAP) && ReferencesGlobalMemory()) + return false; + + if ((ChangesAddress() || ins.ChangesAddress()) && MayBeSameAddress(ins)) + return false; + if (ChangesAddress() && mMode == ASMIM_ZERO_PAGE && ins.mMode == ASMIM_INDIRECT_Y && (mAddress == ins.mAddress || mAddress == ins.mAddress + 1)) + return false; + if (ins.ChangesAddress() && ins.mMode == ASMIM_ZERO_PAGE && mMode == ASMIM_INDIRECT_Y && (mAddress == ins.mAddress || mAddress + 1 == ins.mAddress)) + return false; + if (RequiresXReg() && ins.ChangesXReg() || ins.RequiresXReg() && ChangesXReg()) + return false; + if (RequiresYReg() && ins.ChangesYReg() || ins.RequiresYReg() && ChangesYReg()) + return false; + if (RequiresAccu() && ins.ChangesAccu() || ins.RequiresAccu() && ChangesAccu()) + return false; + if (RequiresCarry() && ins.ChangesCarry() || ins.RequiresCarry() && ChangesCarry()) + return false; + if ((mLive & LIVE_CPU_REG_A) && ins.ChangesAccu()) + return false; + if ((mLive & LIVE_CPU_REG_X) && ins.ChangesXReg()) + return false; + if ((mLive & LIVE_CPU_REG_Y) && ins.ChangesYReg()) + return false; + if ((mLive & LIVE_CPU_REG_C) && ins.ChangesCarry()) + return false; + if ((mLive & LIVE_CPU_REG_Z) && ins.ChangesZFlag()) + return false; + + return true; +} + +bool NativeCodeInstruction::MayBeMovedBehind(const NativeCodeInstruction& ins) const +{ + return ins.MayBeMovedBefore(*this); +} + +bool NativeCodeInstruction::MayBeSameAddress(const NativeCodeInstruction& ins, bool sameXY) const +{ + if (ins.mMode == ASMIM_ZERO_PAGE) + { + if (mMode == ASMIM_ZERO_PAGE) + return mAddress == ins.mAddress; + else + return false; + } + else if (ins.mMode == ASMIM_ZERO_PAGE_X || ins.mMode == ASMIM_ZERO_PAGE_Y) + { + return mMode == ASMIM_ZERO_PAGE || mMode == ASMIM_INDIRECT_X || mMode == ASMIM_INDIRECT_Y || mMode == ASMIM_ZERO_PAGE_X || mMode == ASMIM_ZERO_PAGE_Y; + } + else if (ins.mMode == ASMIM_ABSOLUTE) + { + if (mMode == ASMIM_ABSOLUTE) + return mLinkerObject == ins.mLinkerObject && mAddress == ins.mAddress; + else if (mMode == ASMIM_ABSOLUTE_X || mMode == ASMIM_ABSOLUTE_Y) + return mLinkerObject == ins.mLinkerObject && mAddress <= ins.mAddress && mAddress + 256 > ins.mAddress; + else if (mMode == ASMIM_INDIRECT_Y || mMode == ASMIM_INDIRECT_X) + { + if (ins.mLinkerObject && ins.mLinkerObject->mVariable && !ins.mLinkerObject->mVariable->mAliased) + return false; + return true; + } + else + return false; + } + else if (ins.mMode == ASMIM_ABSOLUTE_X || ins.mMode == ASMIM_ABSOLUTE_Y) + { + if (mMode == ASMIM_ABSOLUTE || mMode == ASMIM_ABSOLUTE_X || mMode == ASMIM_ABSOLUTE_Y) + { + if (mLinkerObject != ins.mLinkerObject) + return false; + else if (mAddress >= ins.mAddress + 256 || ins.mAddress >= mAddress + 256) + return false; + else if (mMode == ASMIM_ABSOLUTE && ins.mAddress > mAddress) + return false; + else + return mMode != ins.mMode || !sameXY || mAddress == ins.mAddress; + } + else if (mMode == ASMIM_INDIRECT_Y || mMode == ASMIM_INDIRECT_X) + { + if (ins.mLinkerObject && ins.mLinkerObject->mVariable && !ins.mLinkerObject->mVariable->mAliased) + return false; + return true; + } + else + return false; + } + else if (ins.mMode == ASMIM_INDIRECT_Y || ins.mMode == ASMIM_INDIRECT_X) + { + if (mMode == ASMIM_ABSOLUTE || mMode == ASMIM_ABSOLUTE_X || mMode == ASMIM_ABSOLUTE_Y) + { + if (mLinkerObject && mLinkerObject->mVariable && !mLinkerObject->mVariable->mAliased) + return false; + return true; + } + else + return mMode == ASMIM_INDIRECT_Y || mMode == ASMIM_INDIRECT_X; + } + else + return false; +} + +bool NativeCodeInstruction::MayReference(const NativeCodeInstruction& ins, bool sameXY) const +{ + if (!ins.ChangesAddress()) + return false; + + if (mMode == ASMIM_IMMEDIATE || mMode == ASMIM_IMMEDIATE_ADDRESS || mMode == ASMIM_IMPLIED) + return false; + + if (mType == ASMIT_JSR) + { + if (ins.mFlags & NCIF_ALIASING) + return true; + + if (ins.mMode == ASMIM_ZERO_PAGE) + return ReferencesZeroPage(ins.mAddress); + else + return true; + } + + if (ins.mMode == ASMIM_ZERO_PAGE) + return ReferencesZeroPage(ins.mAddress); + else if (mMode == ASMIM_ZERO_PAGE) + return false; + else if (mMode == ASMIM_ABSOLUTE) + { + if (ins.mMode == ASMIM_ABSOLUTE) + return mLinkerObject == ins.mLinkerObject && mAddress == ins.mAddress; + else if (ins.mMode == ASMIM_ABSOLUTE_X || ins.mMode == ASMIM_ABSOLUTE_Y) + return mLinkerObject == ins.mLinkerObject; + else if (ins.mMode == ASMIM_INDIRECT_Y || ins.mMode == ASMIM_INDIRECT_X) + return ins.mAddress != BC_REG_STACK; + else + return false; + } + else if (mMode == ASMIM_ABSOLUTE_X || mMode == ASMIM_ABSOLUTE_Y) + { + if (ins.mMode == ASMIM_ABSOLUTE || ins.mMode == ASMIM_ABSOLUTE_X || ins.mMode == ASMIM_ABSOLUTE_Y) + { + if (mLinkerObject != ins.mLinkerObject) + return false; + else + return mMode != ins.mMode || !sameXY || mAddress == ins.mAddress; + } + else if (ins.mMode == ASMIM_INDIRECT_Y || ins.mMode == ASMIM_INDIRECT_X) + return ins.mAddress != BC_REG_STACK; + else + return false; + } + else if (mMode == ASMIM_INDIRECT_Y || mMode == ASMIM_INDIRECT_X) + { + if (ins.mMode == ASMIM_ABSOLUTE || ins.mMode == ASMIM_ABSOLUTE_X || ins.mMode == ASMIM_ABSOLUTE_Y) + return mAddress != BC_REG_STACK; + else + return ins.mMode == ASMIM_INDIRECT_Y || ins.mMode == ASMIM_INDIRECT_X; + } + else + return false; +} + +bool NativeCodeInstruction::SameLinkerObjectVariableRange(const NativeCodeInstruction& ins, bool sameXY) const +{ + if (mLinkerObject == ins.mLinkerObject) + { + if (mMode == ASMIM_ABSOLUTE && ins.mMode == ASMIM_ABSOLUTE) + return mAddress == ins.mAddress; + else if (mMode == ins.mMode && sameXY) + return mAddress == ins.mAddress; + else if (mLinkerObject && mLinkerObject->mStripe > 1) + return mAddress / mLinkerObject->mStripe == ins.mAddress / mLinkerObject->mStripe; + else if (mMode == ASMIM_ABSOLUTE && mAddress < ins.mAddress) + return false; + else if (ins.mMode == ASMIM_ABSOLUTE && ins.mAddress < mAddress) + return false; + else + return true; + } + else + return false; +} + +bool NativeCodeInstruction::MayBeChangedOnAddress(const NativeCodeInstruction& ins, bool sameXY) const +{ + if (mMode == ASMIM_IMMEDIATE || mMode == ASMIM_IMMEDIATE_ADDRESS) + return false; + + if (ins.mFlags & NCIF_MEMMAP) + { + if (mMode == ASMIM_ABSOLUTE || mMode == ASMIM_ABSOLUTE_X || mMode == ASMIM_ABSOLUTE_Y || mMode == ASMIM_INDIRECT_Y) + return true; + } + + if (ins.mType == ASMIT_JSR) + { + if (mFlags & NCIF_ALIASING) + return true; + if (mMode == ASMIM_ZERO_PAGE) + return ins.ChangesZeroPage(mAddress); + else if (mMode == ASMIM_IMPLIED || mMode == ASMIM_IMMEDIATE || mMode == ASMIM_IMMEDIATE_ADDRESS) + return false; + else + return true; + } + + if (!ins.ChangesAddress()) + return false; + + if (ins.mMode == ASMIM_ZERO_PAGE) + { + if (mMode == ASMIM_ZERO_PAGE) + return mAddress == ins.mAddress; + else if (mMode == ASMIM_INDIRECT_X || mMode == ASMIM_INDIRECT_Y) + return mAddress == ins.mAddress || mAddress + 1 == ins.mAddress; + else + return mMode == ASMIM_ZERO_PAGE_X || mMode == ASMIM_ZERO_PAGE_Y; + } + else if (ins.mMode == ASMIM_ZERO_PAGE_X || ins.mMode == ASMIM_ZERO_PAGE_Y) + { + return mMode == ASMIM_ZERO_PAGE || mMode == ASMIM_INDIRECT_X || mMode == ASMIM_INDIRECT_Y || mMode == ASMIM_ZERO_PAGE_X || mMode == ASMIM_ZERO_PAGE_Y; + } + else if (ins.mMode == ASMIM_ABSOLUTE) + { + if (mMode == ASMIM_ABSOLUTE || mMode == ASMIM_ABSOLUTE_X || mMode == ASMIM_ABSOLUTE_Y) + return SameLinkerObjectVariableRange(ins, sameXY); + else if (mMode == ASMIM_INDIRECT_Y || mMode == ASMIM_INDIRECT_X) + return mAddress != BC_REG_STACK; + else + return false; + } + else if (ins.mMode == ASMIM_ABSOLUTE_X || ins.mMode == ASMIM_ABSOLUTE_Y) + { + if (mMode == ASMIM_ABSOLUTE || mMode == ASMIM_ABSOLUTE_X || mMode == ASMIM_ABSOLUTE_Y) + return SameLinkerObjectVariableRange(ins, sameXY); + else if (mMode == ASMIM_INDIRECT_Y || mMode == ASMIM_INDIRECT_X) + return mAddress != BC_REG_STACK; + else + return false; + } + else if (ins.mMode == ASMIM_INDIRECT_Y || ins.mMode == ASMIM_INDIRECT_X) + { + if (mMode == ASMIM_ABSOLUTE || mMode == ASMIM_ABSOLUTE_X || mMode == ASMIM_ABSOLUTE_Y) + return ins.mAddress != BC_REG_STACK; + else + return mMode == ASMIM_INDIRECT_Y || mMode == ASMIM_INDIRECT_X; + } + else + return false; +} + +bool NativeCodeInstruction::UsesMemoryOf(const NativeCodeInstruction& ins) const +{ + if (ins.mMode == ASMIM_ZERO_PAGE) + return UsesZeroPage(ins.mAddress); + else if (UsesAddress()) + { + if (ins.mMode == ASMIM_ABSOLUTE && mMode == ASMIM_ABSOLUTE) + return mLinkerObject == ins.mLinkerObject && mAddress == ins.mAddress; + else if ( + (ins.mMode == ASMIM_ABSOLUTE || ins.mMode == ASMIM_ABSOLUTE_X || ins.mMode == ASMIM_ABSOLUTE_Y) && + (mMode == ASMIM_ABSOLUTE || mMode == ASMIM_ABSOLUTE_X || mMode == ASMIM_ABSOLUTE_Y)) + return mLinkerObject == ins.mLinkerObject; + else if (ins.mMode == ASMIM_INDIRECT_Y || mMode == ASMIM_INDIRECT_Y) + return true; + else + return false; + } + else if (mType == ASMIT_JSR) + return true; + else + return false; + +} + + +bool NativeCodeInstruction::SameEffectiveAddress(const NativeCodeInstruction& ins) const +{ + if (mMode != ins.mMode) + return false; + + switch (mMode) + { + case ASMIM_ZERO_PAGE: + case ASMIM_ZERO_PAGE_X: + case ASMIM_ZERO_PAGE_Y: + case ASMIM_INDIRECT_X: + case ASMIM_INDIRECT_Y: + case ASMIM_IMMEDIATE: + return ins.mAddress == mAddress; + case ASMIM_ABSOLUTE: + case ASMIM_ABSOLUTE_X: + case ASMIM_ABSOLUTE_Y: + return (ins.mLinkerObject == mLinkerObject && ins.mAddress == mAddress); + case ASMIM_IMMEDIATE_ADDRESS: + return (ins.mLinkerObject == mLinkerObject && ins.mAddress == mAddress && ins.mFlags == mFlags); + default: + return false; + } +} + +bool NativeCodeInstruction::ApplySimulation(const NativeRegisterDataSet& data) +{ + switch (mType) + { + case ASMIT_LDA: + case ASMIT_LDX: + case ASMIT_LDY: + case ASMIT_CMP: + case ASMIT_CPX: + case ASMIT_CPY: + case ASMIT_ADC: + case ASMIT_SBC: + case ASMIT_AND: + case ASMIT_ORA: + case ASMIT_EOR: + if (mMode == ASMIM_ZERO_PAGE && data[mAddress].mMode == NRDM_IMMEDIATE) + { + mMode = ASMIM_IMMEDIATE; + mAddress = data[mAddress].mValue; + return true; + } + else if (mMode == ASMIM_ZERO_PAGE && data[mAddress].mMode == NRDM_IMMEDIATE_ADDRESS) + { + mMode = ASMIM_IMMEDIATE_ADDRESS; + mLinkerObject = data[mAddress].mLinkerObject; + mFlags = data[mAddress].mFlags; + mAddress = data[mAddress].mValue; + assert((mFlags & (NCIF_LOWER | NCIF_UPPER)) != (NCIF_LOWER | NCIF_UPPER)); + return true; + } + break; + } + + if (mMode == ASMIM_INDIRECT_Y && data[mAddress].mMode == NRDM_IMMEDIATE && data[mAddress + 1].mMode == NRDM_IMMEDIATE) + { + mMode = ASMIM_ABSOLUTE_Y; + mAddress = data[mAddress].mValue + 256 * data[mAddress + 1].mValue; + mLinkerObject = nullptr; + } + else if (mMode == ASMIM_INDIRECT_Y && data[mAddress].mMode == NRDM_IMMEDIATE_ADDRESS && data[mAddress + 1].mMode == NRDM_IMMEDIATE_ADDRESS && data[mAddress].mLinkerObject == data[mAddress + 1].mLinkerObject) + { + mMode = ASMIM_ABSOLUTE_Y; + mLinkerObject = data[mAddress].mLinkerObject; + mAddress = data[mAddress].mValue; + } + + return false; +} + +void NativeCodeInstruction::Simulate(NativeRegisterDataSet& data) +{ + int reg = -1; + if (mMode == ASMIM_ZERO_PAGE) + reg = mAddress; + else if (mMode == ASMIM_IMPLIED) + reg = CPU_REG_A; + + switch (mType) + { + case ASMIT_JSR: + data.Reset(CPU_REG_C); + data.Reset(CPU_REG_Z); + data.Reset(CPU_REG_A); + data.Reset(CPU_REG_X); + data.Reset(CPU_REG_Y); + + data.ResetWorkRegs(); + + if (mFlags & NCIF_FEXEC) + { + for (int i = BC_REG_TMP; i < BC_REG_TMP_SAVED; i++) + data.Reset(i); + } + else if (!(mFlags & NCIF_RUNTIME)) + { + if (mLinkerObject && mLinkerObject->mProc) + { + for (int i = BC_REG_TMP; i < BC_REG_TMP + mLinkerObject->mProc->mCallerSavedTemps; i++) + data.Reset(i); + } + else + { + for (int i = BC_REG_TMP; i < BC_REG_TMP_SAVED; i++) + data.Reset(i); + } + } + break; + + case ASMIT_ROL: + if (reg >= 0) + { + if (data[reg].mMode == NRDM_IMMEDIATE && data[CPU_REG_C].mMode == NRDM_IMMEDIATE) + { + int t = (data[reg].mValue << 1) | data[CPU_REG_C].mValue; + data.Set(CPU_REG_C, NRDM_IMMEDIATE, t >= 256); + data.Set(reg, NRDM_IMMEDIATE, t & 255); + data.Set(CPU_REG_Z, NRDM_IMMEDIATE, t & 255); + } + else if (data[reg].mMode == NRDM_IMMEDIATE) + { + data.Reset(CPU_REG_Z); + data.Set(CPU_REG_C, NRDM_IMMEDIATE, data[reg].mValue >= 128); + data.Reset(reg); + } + else + { + data.Reset(reg); + data.Reset(CPU_REG_C); + data.Reset(CPU_REG_Z); + } + } + else + { + data.Reset(CPU_REG_C); + data.Reset(CPU_REG_Z); + } + break; + + case ASMIT_ROR: + if (reg >= 0) + { + if (data[reg].mMode == NRDM_IMMEDIATE && data[CPU_REG_C].mMode == NRDM_IMMEDIATE) + { + int t = (data[reg].mValue >> 1) | (data[CPU_REG_C].mValue << 7); + data.Set(CPU_REG_C, NRDM_IMMEDIATE, data[reg].mValue & 1); + data.Set(reg, NRDM_IMMEDIATE, t & 255); + data.Set(CPU_REG_Z, NRDM_IMMEDIATE, t & 255); + } + else if (data[reg].mMode == NRDM_IMMEDIATE) + { + data.Reset(CPU_REG_Z); + data.Set(CPU_REG_C, NRDM_IMMEDIATE, data[reg].mValue & 1); + data.Reset(reg); + } + else + { + data.Reset(reg); + data.Reset(CPU_REG_C); + data.Reset(CPU_REG_Z); + } + } + else + { + data.Reset(CPU_REG_C); + data.Reset(CPU_REG_Z); + } + break; + + case ASMIT_ASL: + if (reg >= 0) + { + if (data[reg].mMode == NRDM_IMMEDIATE) + { + int t = (data[reg].mValue << 1); + data.Set(CPU_REG_C, NRDM_IMMEDIATE, t >= 256); + data.Set(reg, NRDM_IMMEDIATE, t & 255); + data.Set(CPU_REG_Z, NRDM_IMMEDIATE, t & 255); + } + else + { + data.Reset(reg); + data.Reset(CPU_REG_C); + data.Reset(CPU_REG_Z); + } + } + else + { + data.Reset(CPU_REG_C); + data.Reset(CPU_REG_Z); + } + break; + + case ASMIT_LSR: + if (reg >= 0) + { + if (data[reg].mMode == NRDM_IMMEDIATE) + { + int t = (data[reg].mValue >> 1); + data.Set(CPU_REG_C, NRDM_IMMEDIATE, data[reg].mValue & 1); + data.Set(reg, NRDM_IMMEDIATE, t & 255); + data.Set(CPU_REG_Z, NRDM_IMMEDIATE, t & 255); + } + else + { + data.Reset(reg); + data.Reset(CPU_REG_C); + data.Reset(CPU_REG_Z); + } + } + else + { + data.Reset(CPU_REG_C); + data.Reset(CPU_REG_Z); + } + break; + + case ASMIT_INC: + if (reg >= 0) + { + if (data[reg].mMode == NRDM_IMMEDIATE) + { + data.Set(reg, NRDM_IMMEDIATE, (data[reg].mValue + 1) & 255); + data.Set(CPU_REG_Z, NRDM_IMMEDIATE, data[reg].mValue); + } + else + { + data.Reset(reg); + data.Reset(CPU_REG_Z); + } + } + else + data.Reset(CPU_REG_Z); + break; + + case ASMIT_DEC: + if (reg >= 0) + { + if (data[reg].mMode == NRDM_IMMEDIATE) + { + data.Set(reg, NRDM_IMMEDIATE, (data[reg].mValue - 1) & 255); + data.Set(CPU_REG_Z, NRDM_IMMEDIATE, data[reg].mValue); + } + else + { + data.Reset(reg); + data.Reset(CPU_REG_Z); + } + } + else + data.Reset(CPU_REG_Z); + break; + + case ASMIT_ADC: + if (reg >= 0) + { + if (data[reg].mMode == NRDM_IMMEDIATE && data[CPU_REG_A].mMode == NRDM_IMMEDIATE && data[CPU_REG_C].mMode == NRDM_IMMEDIATE) + { + int t = data[reg].mValue + data[CPU_REG_A].mValue + data[CPU_REG_C].mValue; + data.Set(CPU_REG_C, NRDM_IMMEDIATE, t >= 256); + data.Set(CPU_REG_A, NRDM_IMMEDIATE, t & 255); + data.Set(CPU_REG_Z, NRDM_IMMEDIATE, t & 255); + } + else + { + data.Reset(CPU_REG_A); + data.Reset(CPU_REG_C); + data.Reset(CPU_REG_Z); + } + } + else + { + data.Reset(CPU_REG_A); + data.Reset(CPU_REG_C); + data.Reset(CPU_REG_Z); + } + break; + + case ASMIT_SBC: + if (reg >= 0) + { + if (data[reg].mMode == NRDM_IMMEDIATE && data[CPU_REG_A].mMode == NRDM_IMMEDIATE && data[CPU_REG_C].mMode == NRDM_IMMEDIATE) + { + int t = (data[reg].mValue ^ 0xff) + data[CPU_REG_A].mValue + data[CPU_REG_C].mValue; + data.Set(CPU_REG_C, NRDM_IMMEDIATE, t >= 256); + data.Set(CPU_REG_A, NRDM_IMMEDIATE, t & 255); + data.Set(CPU_REG_Z, NRDM_IMMEDIATE, t & 255); + } + else + { + data.Reset(CPU_REG_A); + data.Reset(CPU_REG_C); + data.Reset(CPU_REG_Z); + } + } + else + { + data.Reset(CPU_REG_A); + data.Reset(CPU_REG_C); + data.Reset(CPU_REG_Z); + } + break; + + case ASMIT_AND: + if (reg >= 0) + { + if (data[reg].mMode == NRDM_IMMEDIATE && data[CPU_REG_A].mMode == NRDM_IMMEDIATE) + { + int t = data[reg].mValue & data[CPU_REG_A].mValue; + data.Set(CPU_REG_A, NRDM_IMMEDIATE, t & 255); + data.Set(CPU_REG_Z, NRDM_IMMEDIATE, t & 255); + } + else if ((data[reg].mMode == NRDM_IMMEDIATE && data[reg].mValue == 0) || (data[CPU_REG_A].mMode == NRDM_IMMEDIATE && data[CPU_REG_A].mValue == 0)) + { + data.Set(CPU_REG_A, NRDM_IMMEDIATE, 0); + data.Set(CPU_REG_Z, NRDM_IMMEDIATE, 0); + } + else + { + data.Reset(CPU_REG_A); + data.Reset(CPU_REG_Z); + } + } + else + { + data.Reset(CPU_REG_A); + data.Reset(CPU_REG_Z); + } + break; + + case ASMIT_ORA: + if (reg >= 0) + { + if (data[reg].mMode == NRDM_IMMEDIATE && data[CPU_REG_A].mMode == NRDM_IMMEDIATE) + { + int t = data[reg].mValue | data[CPU_REG_A].mValue; + data.Set(CPU_REG_A, NRDM_IMMEDIATE, t & 255); + data.Set(CPU_REG_Z, NRDM_IMMEDIATE, t & 255); + } + else if ((data[reg].mMode == NRDM_IMMEDIATE && data[reg].mValue == 0xff) || (data[CPU_REG_A].mMode == NRDM_IMMEDIATE && data[CPU_REG_A].mValue == 0xff)) + { + data.Set(CPU_REG_A, NRDM_IMMEDIATE, 0xff); + data.Set(CPU_REG_Z, NRDM_IMMEDIATE, 0xff); + } + else + { + data.Reset(CPU_REG_A); + data.Reset(CPU_REG_Z); + } + } + else + { + data.Reset(CPU_REG_A); + data.Reset(CPU_REG_Z); + } + break; + + case ASMIT_EOR: + if (reg >= 0) + { + if (data[reg].mMode == NRDM_IMMEDIATE && data[CPU_REG_A].mMode == NRDM_IMMEDIATE) + { + int t = data[reg].mValue ^ data[CPU_REG_A].mValue; + data.Set(CPU_REG_A, NRDM_IMMEDIATE, t & 255); + data.Set(CPU_REG_Z, NRDM_IMMEDIATE, t & 255); + } + else + { + data.Reset(CPU_REG_A); + data.Reset(CPU_REG_Z); + } + } + else + { + data.Reset(CPU_REG_A); + data.Reset(CPU_REG_Z); + } + break; + + case ASMIT_INX: + if (data[CPU_REG_X].mMode == NRDM_IMMEDIATE) + { + data.Set(CPU_REG_X, NRDM_IMMEDIATE, (data[CPU_REG_X].mValue + 1) & 255); + data.Set(CPU_REG_Z, NRDM_IMMEDIATE, data[CPU_REG_X].mValue); + } + else + { + data.Reset(CPU_REG_X); + data.Reset(CPU_REG_Z); + } + break; + + case ASMIT_DEX: + if (data[CPU_REG_X].mMode == NRDM_IMMEDIATE) + { + data.Set(CPU_REG_X, NRDM_IMMEDIATE, (data[CPU_REG_X].mValue - 1) & 255); + data.Set(CPU_REG_Z, NRDM_IMMEDIATE, data[CPU_REG_X].mValue); + } + else + { + data.Reset(CPU_REG_X); + data.Reset(CPU_REG_Z); + } + break; + + case ASMIT_INY: + if (data[CPU_REG_Y].mMode == NRDM_IMMEDIATE) + { + data.Set(CPU_REG_Y, NRDM_IMMEDIATE, (data[CPU_REG_Y].mValue + 1) & 255); + data.Set(CPU_REG_Z, NRDM_IMMEDIATE, data[CPU_REG_Y].mValue); + } + else + { + data.Reset(CPU_REG_Y); + data.Reset(CPU_REG_Z); + } + break; + + case ASMIT_DEY: + if (data[CPU_REG_Y].mMode == NRDM_IMMEDIATE) + { + data.Set(CPU_REG_Y, NRDM_IMMEDIATE, (data[CPU_REG_Y].mValue - 1) & 255); + data.Set(CPU_REG_Z, NRDM_IMMEDIATE, data[CPU_REG_Y].mValue); + } + else + { + data.Reset(CPU_REG_Y); + data.Reset(CPU_REG_Z); + } + break; + + case ASMIT_TXA: + if (data[CPU_REG_X].mMode == NRDM_IMMEDIATE) + { + data.Set(CPU_REG_A, NRDM_IMMEDIATE, data[CPU_REG_X].mValue); + data.Set(CPU_REG_Z, NRDM_IMMEDIATE, data[CPU_REG_X].mValue); + } + else + { + data.Set(CPU_REG_A, data[CPU_REG_X]); + data.Reset(CPU_REG_Z); + } + break; + + case ASMIT_TYA: + if (data[CPU_REG_Y].mMode == NRDM_IMMEDIATE) + { + data.Set(CPU_REG_A, NRDM_IMMEDIATE, data[CPU_REG_Y].mValue); + data.Set(CPU_REG_Z, NRDM_IMMEDIATE, data[CPU_REG_Y].mValue); + } + else + { + data.Set(CPU_REG_A, data[CPU_REG_Y]); + data.Reset(CPU_REG_Z); + } + break; + + case ASMIT_TAX: + if (data[CPU_REG_A].mMode == NRDM_IMMEDIATE) + { + data.Set(CPU_REG_X, NRDM_IMMEDIATE, data[CPU_REG_A].mValue); + data.Set(CPU_REG_Z, NRDM_IMMEDIATE, data[CPU_REG_A].mValue); + } + else + { + data.Set(CPU_REG_X, data[CPU_REG_A]); + data.Reset(CPU_REG_Z); + } + break; + + case ASMIT_TAY: + if (data[CPU_REG_A].mMode == NRDM_IMMEDIATE) + { + data.Set(CPU_REG_Y, NRDM_IMMEDIATE, data[CPU_REG_A].mValue); + data.Set(CPU_REG_Z, NRDM_IMMEDIATE, data[CPU_REG_A].mValue); + } + else + { + data.Set(CPU_REG_Y, data[CPU_REG_A]); + data.Reset(CPU_REG_Z); + } + break; + + case ASMIT_CMP: + if (reg >= 0) + { + if (data[reg].mMode == NRDM_IMMEDIATE && data[CPU_REG_A].mMode == NRDM_IMMEDIATE) + { + int t = (data[reg].mValue ^ 0xff) + data[CPU_REG_A].mValue + 1; + data.Set(CPU_REG_C, NRDM_IMMEDIATE, t >= 256); + data.Set(CPU_REG_Z, NRDM_IMMEDIATE, t & 255); + } + else + { + data.Reset(CPU_REG_C); + data.Reset(CPU_REG_Z); + } + } + else if (mMode == ASMIM_IMMEDIATE && mAddress == 0) + { + data.Set(CPU_REG_C, NRDM_IMMEDIATE, 1); + data.Reset(CPU_REG_Z); + } + else + { + data.Reset(CPU_REG_C); + data.Reset(CPU_REG_Z); + } + break; + + case ASMIT_CPX: + if (reg >= 0) + { + if (data[reg].mMode == NRDM_IMMEDIATE && data[CPU_REG_X].mMode == NRDM_IMMEDIATE) + { + int t = (data[reg].mValue ^ 0xff) + data[CPU_REG_X].mValue + 1; + data.Set(CPU_REG_C, NRDM_IMMEDIATE, t >= 256); + data.Set(CPU_REG_Z, NRDM_IMMEDIATE, t & 255); + } + else + { + data.Reset(CPU_REG_C); + data.Reset(CPU_REG_Z); + } + } + else if (mMode == ASMIM_IMMEDIATE && mAddress == 0) + { + data.Set(CPU_REG_C, NRDM_IMMEDIATE, 1); + data.Reset(CPU_REG_Z); + } + else + { + data.Reset(CPU_REG_C); + data.Reset(CPU_REG_Z); + } + break; + + case ASMIT_CPY: + if (reg >= 0) + { + if (data[reg].mMode == NRDM_IMMEDIATE && data[CPU_REG_Y].mMode == NRDM_IMMEDIATE) + { + int t = (data[reg].mValue ^ 0xff) + data[CPU_REG_Y].mValue + 1; + data.Set(CPU_REG_C, NRDM_IMMEDIATE, t >= 256); + data.Set(CPU_REG_Z, NRDM_IMMEDIATE, t & 255); + } + else + { + data.Reset(CPU_REG_C); + data.Reset(CPU_REG_Z); + } + } + else if (mMode == ASMIM_IMMEDIATE && mAddress == 0) + { + data.Set(CPU_REG_C, NRDM_IMMEDIATE, 1); + data.Reset(CPU_REG_Z); + } + else + { + data.Reset(CPU_REG_C); + data.Reset(CPU_REG_Z); + } + break; + + case ASMIT_BIT: + data.Reset(CPU_REG_Z); + break; + + case ASMIT_LDA: + if (reg >= 0) + { + if (data[reg].mMode == NRDM_IMMEDIATE) + { + data.Set(CPU_REG_A, data[reg]); + data.Set(CPU_REG_Z, NRDM_IMMEDIATE, data[CPU_REG_A].mValue); + } + else + { + data.Set(CPU_REG_A, data[reg]); + data.Reset(CPU_REG_Z); + } + } + else if (mMode == ASMIM_IMMEDIATE) + { + data.Set(CPU_REG_A, NRDM_IMMEDIATE, mAddress); + data.Set(CPU_REG_Z, NRDM_IMMEDIATE, mAddress); + } + else if (mMode == ASMIM_IMMEDIATE_ADDRESS) + { + data.Set(CPU_REG_A, NRDM_IMMEDIATE_ADDRESS, mAddress, mLinkerObject, mFlags); + data.Reset(CPU_REG_Z); + } + else + { + data.Reset(CPU_REG_A); + data.Reset(CPU_REG_Z); + } + break; + + case ASMIT_LDX: + if (reg >= 0) + { + if (data[reg].mMode == NRDM_IMMEDIATE) + { + data.Set(CPU_REG_X, data[reg]); + data.Set(CPU_REG_Z, NRDM_IMMEDIATE, data[CPU_REG_X].mValue); + } + else + { + data.Set(CPU_REG_X, data[reg]); + data.Reset(CPU_REG_Z); + } + } + else if (mMode == ASMIM_IMMEDIATE) + { + data.Set(CPU_REG_X, NRDM_IMMEDIATE, mAddress); + data.Set(CPU_REG_Z, NRDM_IMMEDIATE, mAddress); + } + else if (mMode == ASMIM_IMMEDIATE_ADDRESS) + { + data.Set(CPU_REG_X, NRDM_IMMEDIATE_ADDRESS, mAddress, mLinkerObject, mFlags); + data.Reset(CPU_REG_Z); + } + else + { + data.Reset(CPU_REG_X); + data.Reset(CPU_REG_Z); + } + break; + + case ASMIT_LDY: + if (reg >= 0) + { + if (data[reg].mMode == NRDM_IMMEDIATE) + { + data.Set(CPU_REG_Y, data[reg]); + data.Set(CPU_REG_Z, NRDM_IMMEDIATE, data[CPU_REG_Y].mValue); + } + else + { + data.Set(CPU_REG_Y, data[reg]); + data.Reset(CPU_REG_Z); + } + } + else if (mMode == ASMIM_IMMEDIATE) + { + data.Set(CPU_REG_Y, NRDM_IMMEDIATE, mAddress); + data.Set(CPU_REG_Z, NRDM_IMMEDIATE, mAddress); + } + else if (mMode == ASMIM_IMMEDIATE_ADDRESS) + { + data.Set(CPU_REG_Y, NRDM_IMMEDIATE_ADDRESS, mAddress, mLinkerObject, mFlags); + data.Reset(CPU_REG_Z); + } + else + { + data.Reset(CPU_REG_Y); + data.Reset(CPU_REG_Z); + } + break; + + case ASMIT_STA: + if (reg >= 0) + { + if (data[CPU_REG_A].mMode == NRDM_IMMEDIATE || data[CPU_REG_A].mMode == NRDM_IMMEDIATE_ADDRESS) + data.Set(reg, data[CPU_REG_A]); + else + data.Reset(reg); + } + break; + + case ASMIT_STX: + if (reg >= 0) + { + if (data[CPU_REG_X].mMode == NRDM_IMMEDIATE || data[CPU_REG_X].mMode == NRDM_IMMEDIATE_ADDRESS) + data.Set(reg, data[CPU_REG_X]); + else + data.Reset(reg); + } + break; + + case ASMIT_STY: + if (reg >= 0) + { + if (data[CPU_REG_Y].mMode == NRDM_IMMEDIATE || data[CPU_REG_Y].mMode == NRDM_IMMEDIATE_ADDRESS) + data.Set(reg, data[CPU_REG_Y]); + else + data.Reset(reg); + } + break; + + case ASMIT_CLC: + data.Set(CPU_REG_C, NRDM_IMMEDIATE, 0); + break; + + case ASMIT_SEC: + data.Set(CPU_REG_C, NRDM_IMMEDIATE, 1); + break; + } +} + +bool NativeCodeInstruction::BitFieldForwarding(NativeRegisterDataSet& data, AsmInsType& carryop) +{ + bool changed = false; + + int iaddr = -1; + + if (mMode == ASMIM_IMPLIED) + iaddr = CPU_REG_A; + else if (mMode == ASMIM_ZERO_PAGE) + iaddr = mAddress; + + int opmask = 0, opvalue = 0; + if (mMode == ASMIM_IMMEDIATE) + { + opmask = 0xff; + opvalue = mAddress; + } + else if (mMode == ASMIM_ZERO_PAGE) + { + opmask = data[mAddress].mMask; + opvalue = data[mAddress].mValue; + } + else if (mMode == ASMIM_ABSOLUTE && mLinkerObject && (mLinkerObject->mFlags & LOBJF_CONST) && mLinkerObject->mReferences.Size() == 0 && mType != ASMIT_JSR) + { + opmask = 0xff; + opvalue = mLinkerObject->mData[mAddress]; + } +#if 1 + else if ((mMode == ASMIM_ABSOLUTE_X || mMode == ASMIM_ABSOLUTE_Y) && mLinkerObject && (mLinkerObject->mFlags & LOBJF_CONST) && mLinkerObject->mReferences.Size() == 0) + { + int mor = 0; + int mand = 0xff; + int ior = 0; + int iand = 0xff; + + if (mMode == ASMIM_ABSOLUTE_X) + { + ior = data[CPU_REG_X].mMask & data[CPU_REG_X].mValue; + iand = (~data[CPU_REG_X].mMask | data[CPU_REG_X].mValue) & 0xff; + } + else if (mMode == ASMIM_ABSOLUTE_Y) + { + ior = data[CPU_REG_Y].mMask & data[CPU_REG_Y].mValue; + iand = (~data[CPU_REG_Y].mMask | data[CPU_REG_Y].mValue) & 0xff; + } + + int size = mLinkerObject->mSize - mAddress; + if (mLinkerObject->mStripe > 1) + { + int ssize = mLinkerObject->mStripe - mAddress % mLinkerObject->mStripe; + if (ssize == 1 && size > mLinkerObject->mStripe) + size = mLinkerObject->mStripe + 1; + else + size = ssize; + } + if (size > 256) + size = 256; + + for (int i = 0; i < size; i++) + { + if ((i & ~iand) == 0 && (i & ior) == ior) + { + mor |= mLinkerObject->mData[mAddress + i]; + mand &= mLinkerObject->mData[mAddress + i]; + } + } + opmask = (mand | ~mor) & 0xff; + opvalue = mand; + } +#endif + else + { + opmask = ~BinMask(mMaxVal) & 0xff; + opvalue = 0; + } + + switch (mType) + { + case ASMIT_JSR: + data.ResetMask(CPU_REG_C); + data.ResetMask(CPU_REG_Z); + if (!(mFlags & NCIF_PRESERVE_CPU_REG_A)) + data.ResetMask(CPU_REG_A); + if (!(mFlags & NCIF_PRESERVE_CPU_REG_X)) + data.ResetMask(CPU_REG_X); + if (!(mFlags & NCIF_PRESERVE_CPU_REG_Y)) + data.ResetMask(CPU_REG_Y); + + data.ResetWorkMasks(); + + if (!(mFlags & NCIF_RUNTIME) || (mFlags & NCIF_FEXEC)) + { + if (mLinkerObject && mLinkerObject->mProc) + { + for (int i = BC_REG_TMP; i < BC_REG_TMP + mLinkerObject->mProc->mCallerSavedTemps; i++) + data.ResetMask(i); + } + else + { + for (int i = BC_REG_TMP; i < BC_REG_TMP_SAVED; i++) + data.ResetMask(i); + } + + for (int i = BC_REG_FPARAMS; i < BC_REG_FPARAMS_END; i++) + data.ResetMask(i); + } + break; + case ASMIT_CLC: + data.SetMask(CPU_REG_C, 1, 0); + break; + case ASMIT_SEC: + data.SetMask(CPU_REG_C, 1, 1); + break; + + case ASMIT_CMP: + case ASMIT_CPX: + case ASMIT_CPY: + data.ResetMask(CPU_REG_C); + break; + + case ASMIT_ADC: + if (mMode == ASMIM_IMMEDIATE && data[CPU_REG_C].mMask == 1 && data[CPU_REG_C].mValue == 0) + { + if ((mAddress & ~data[CPU_REG_A].mMask) == 0 && (mAddress & data[CPU_REG_A].mValue) == 0) + { + mType = ASMIT_ORA; + data.SetMask(CPU_REG_A, data[CPU_REG_A].mMask, data[CPU_REG_A].mValue | mAddress); + changed = true; + } + else if (mAddress == 1 && (data[CPU_REG_A].mMask & 3) == 3 && (data[CPU_REG_A].mValue & 3) == 1) + { + mType = ASMIT_EOR; + mAddress = 3; + data.SetMask(CPU_REG_A, data[CPU_REG_A].mMask, data[CPU_REG_A].mValue ^ 3); + changed = true; + } + else if (mMode == ASMIM_IMMEDIATE && !(mLive & LIVE_CPU_REG_C) && !(mAddress & BinMask(data[CPU_REG_A].mMask ^ 0xff))) + { + int mask = BinMask(data[CPU_REG_A].mMask ^ 0xff) ^ 0xff; + mType = ASMIT_EOR; + mAddress = ((mAddress + data[CPU_REG_A].mValue) ^ data[CPU_REG_A].mValue) & mask; + changed = true; + data.SetMask(CPU_REG_C, 1, 0); + data.SetMask(CPU_REG_A, data[CPU_REG_A].mMask, data[CPU_REG_A].mValue^ mAddress); + } + else if (mMode == ASMIM_IMMEDIATE && mAddress + ((data[CPU_REG_A].mValue & data[CPU_REG_A].mMask) | (~data[CPU_REG_A].mMask & 255)) < 256) + { + data.SetMask(CPU_REG_C, 1, 0); + data.SetMask(CPU_REG_A, ~BinMask(mAddress + ((data[CPU_REG_A].mValue & data[CPU_REG_A].mMask) | (~data[CPU_REG_A].mMask & 255))) & 255, 0); + } + else + { + data.ResetMask(CPU_REG_C); + data.ResetMask(CPU_REG_A); + } + } + else if (mMode == ASMIM_IMMEDIATE && mAddress == 0) + { + int zeros = data[CPU_REG_A].mMask & ~data[CPU_REG_A].mValue; + + int fzero = 0x01; + while (fzero < 0x100 && (fzero & zeros) == 0) + fzero <<= 1; + + fzero |= (fzero - 1); + + data.SetMask(CPU_REG_A, data[CPU_REG_A].mMask & ~fzero, data[CPU_REG_A].mValue); + + if (fzero >= 0x100) + data.ResetMask(CPU_REG_C); + else + data.SetMask(CPU_REG_C, 1, 0); + } + else if (data[CPU_REG_C].mMask == 1 && data[CPU_REG_C].mValue == 0) + { + if ((((~opmask | opvalue) & (~data[CPU_REG_A].mMask | data[CPU_REG_A].mValue)) & 0xff) == 0) + { + mType = ASMIT_ORA; + data.SetMask(CPU_REG_A, data[CPU_REG_A].mMask & opmask, data[CPU_REG_A].mValue | opvalue); + changed = true; + } + else + { + data.ResetMask(CPU_REG_C); + data.ResetMask(CPU_REG_A); + } + } + else + { + data.ResetMask(CPU_REG_C); + data.ResetMask(CPU_REG_A); + } + + break; + case ASMIT_SBC: + data.ResetMask(CPU_REG_C); + data.ResetMask(CPU_REG_A); + break; + + case ASMIT_LDA: + if (mMode == ASMIM_IMMEDIATE) + { + if (data[CPU_REG_A].mMask == 0xff && data[CPU_REG_A].mValue == mAddress && !(mLive & LIVE_CPU_REG_Z)) + { + mType = ASMIT_NOP; + mMode = ASMIM_IMPLIED; + changed = true; + } + else + data.SetMask(CPU_REG_A, 0xff, mAddress & 0xff); + } + else + { + data.SetMask(CPU_REG_A, opmask, opvalue); + if (opmask == 0xff) + { + mType = ASMIT_LDA; + mMode = ASMIM_IMMEDIATE; + mAddress = opvalue; + changed = true; + } + } + break; + case ASMIT_STA: + if (mMode == ASMIM_ZERO_PAGE) + data.SetMask(mAddress, data[CPU_REG_A]); + break; + + case ASMIT_LDX: + if (mMode == ASMIM_IMMEDIATE) + data.SetMask(CPU_REG_X, 0xff, mAddress & 0xff); + else + { + data.SetMask(CPU_REG_X, opmask, opvalue); + if (opmask == 0xff) + { + mType = ASMIT_LDX; + mMode = ASMIM_IMMEDIATE; + mAddress = opvalue; + changed = true; + } + } + break; + case ASMIT_STX: + if (mMode == ASMIM_ZERO_PAGE) + data.Set(mAddress, data[CPU_REG_X]); + break; + + case ASMIT_LDY: + if (mMode == ASMIM_IMMEDIATE) + data.SetMask(CPU_REG_Y, 0xff, mAddress & 0xff); + else + { + data.SetMask(CPU_REG_Y, opmask, opvalue); + if (opmask == 0xff) + { + mType = ASMIT_LDY; + mMode = ASMIM_IMMEDIATE; + mAddress = opvalue; + changed = true; + } + } + break; + case ASMIT_STY: + if (mMode == ASMIM_ZERO_PAGE) + data.SetMask(mAddress, data[CPU_REG_Y]); + break; + + case ASMIT_AND: + { + int zeros = + (opmask & ~opvalue) | + (data[CPU_REG_A].mMask & ~data[CPU_REG_A].mValue); + + int ones = + (opmask & opvalue) & + (data[CPU_REG_A].mMask & data[CPU_REG_A].mValue); + + int cbits = (~data[CPU_REG_A].mMask | data[CPU_REG_A].mValue) & (~opmask | ~opvalue) & 0xff; + + if (cbits == 0) + { + if (mMode != ASMIM_IMMEDIATE || mAddress != 0xff) + { + mMode = ASMIM_IMMEDIATE; + mAddress = 0xff; + changed = true; + } + } + else + { + data.SetMask(CPU_REG_A, (ones | zeros) & 0xff, ones & 0xff); + + if (data[CPU_REG_A].mMask == 0xff) + { + mType = ASMIT_LDA; + mMode = ASMIM_IMMEDIATE; + mAddress = data[CPU_REG_A].mValue; + changed = true; + } + } + + } break; + + case ASMIT_ORA: + { + int ones = + (opmask & opvalue) | + (data[CPU_REG_A].mMask & data[CPU_REG_A].mValue); + + int zeros = + (opmask & ~opvalue) & + (data[CPU_REG_A].mMask & ~data[CPU_REG_A].mValue); + + data.SetMask(CPU_REG_A, (ones | zeros) & 0xff, ones & 0xff); + + if (data[CPU_REG_A].mMask == 0xff) + { + mType = ASMIT_LDA; + mMode = ASMIM_IMMEDIATE; + mAddress = data[CPU_REG_A].mValue; + changed = true; + } + } break; + + case ASMIT_EOR: + if (mMode == ASMIM_IMMEDIATE) + data.SetMask(CPU_REG_A, data[CPU_REG_A].mMask, (data[CPU_REG_A].mValue ^ mAddress) & data[CPU_REG_A].mMask); + else + data.ResetMask(CPU_REG_A); + break; + + case ASMIT_TAX: + data.SetMask(CPU_REG_X, data[CPU_REG_A]); + break; + case ASMIT_TAY: + data.SetMask(CPU_REG_Y, data[CPU_REG_A]); + break; + + case ASMIT_TXA: + data.SetMask(CPU_REG_A, data[CPU_REG_X]); + break; + + case ASMIT_TYA: + data.SetMask(CPU_REG_A, data[CPU_REG_Y]); + break; + + case ASMIT_INX: + case ASMIT_DEX: + data.ResetMask(CPU_REG_X); + break; + + case ASMIT_INY: + case ASMIT_DEY: + data.ResetMask(CPU_REG_Y); + break; + + case ASMIT_INC: + case ASMIT_DEC: + if (mMode == ASMIM_ZERO_PAGE) + data.ResetMask(mAddress); + break; + + case ASMIT_ASL: + if (iaddr >= 0) + { + int mask = data[iaddr].mMask, value = data[iaddr].mValue; + + data.SetMask(iaddr, ((mask << 1) & 0xff) | 0x01, (value << 1) & 0xff); + + if (mask & 0x80) + data.SetMask(CPU_REG_C, 1, value >> 7); + else + data.ResetMask(CPU_REG_C); + + if (mMode == ASMIM_IMPLIED && data[CPU_REG_A].mMask == 0xff && data[CPU_REG_C].mMask) + { + carryop = data[CPU_REG_C].mValue ? ASMIT_SEC : ASMIT_CLC; + mType = ASMIT_LDA; + mMode = ASMIM_IMMEDIATE; + mAddress = data[CPU_REG_A].mValue; + changed = true; + } + } + else + data.ResetMask(CPU_REG_C); + break; + + case ASMIT_LSR: + if (iaddr >= 0) + { + int mask = data[iaddr].mMask, value = data[iaddr].mValue; + + if (mask == 0xff && value == 0x00) + { + mType = ASMIT_CLC; + mMode = ASMIM_IMPLIED; + data.SetMask(CPU_REG_C, 1, 0); + changed = true; + } + else + { + data.SetMask(iaddr, ((mask >> 1) & 0xff) | 0x80, (value >> 1) & 0x7f); + + if (mask & 0x01) + data.SetMask(CPU_REG_C, 1, value & 1); + else + data.ResetMask(CPU_REG_C); + + if (mMode == ASMIM_IMPLIED && data[CPU_REG_A].mMask == 0xff && data[CPU_REG_C].mMask) + { + carryop = data[CPU_REG_C].mValue ? ASMIT_SEC : ASMIT_CLC; + mType = ASMIT_LDA; + mMode = ASMIM_IMMEDIATE; + mAddress = data[CPU_REG_A].mValue; + changed = true; + } + } + } + else + data.ResetMask(CPU_REG_C); + break; + + case ASMIT_ROL: + if (iaddr >= 0) + { + int mask = data[iaddr].mMask, value = data[iaddr].mValue; + + int nmask = (mask << 1) & 0xff, nvalue = (value << 1) & 0xff; + + + if (data[CPU_REG_C].mMask & 1) + { + nmask |= 1; + nvalue |= data[CPU_REG_C].mValue & 1; + } + + data.SetMask(iaddr, nmask, nvalue); + + if (mask & 0x80) + data.SetMask(CPU_REG_C, 1, value >> 7); + else + data.ResetMask(CPU_REG_C); + + if (mMode == ASMIM_IMPLIED && data[CPU_REG_A].mMask == 0xff && data[CPU_REG_C].mMask) + { + carryop = data[CPU_REG_C].mValue ? ASMIT_SEC : ASMIT_CLC; + mType = ASMIT_LDA; + mMode = ASMIM_IMMEDIATE; + mAddress = data[CPU_REG_A].mValue; + changed = true; + } + } + else + data.ResetMask(CPU_REG_C); + break; + + case ASMIT_ROR: + if (iaddr >= 0) + { + int mask = data[iaddr].mMask, value = data[iaddr].mValue; + + int nmask = (mask >> 1) & 0x7f, nvalue = (value >> 1) & 0x7f; + + if (data[CPU_REG_C].mMask & 1) + { + nmask |= 0x80; + nvalue |= (data[CPU_REG_C].mValue << 7) & 0x80; + } + + data.SetMask(iaddr, nmask, nvalue); + + if (mask & 0x01) + data.SetMask(CPU_REG_C, 1, value & 1); + else + data.ResetMask(CPU_REG_C); + + if (mMode == ASMIM_IMPLIED && data[CPU_REG_A].mMask == 0xff && data[CPU_REG_C].mMask) + { + carryop = data[CPU_REG_C].mValue ? ASMIT_SEC : ASMIT_CLC; + mType = ASMIT_LDA; + mMode = ASMIM_IMMEDIATE; + mAddress = data[CPU_REG_A].mValue; + changed = true; + } + } + else + data.ResetMask(CPU_REG_C); + break; + } + +#if _DEBUG + for (int i = 0; i < NUM_REGS; i++) + { + assert(!(data[i].mMask & 0xff00)); + } +#endif + + return changed; +} + +bool NativeCodeInstruction::ValueForwarding(NativeRegisterDataSet& data, AsmInsType& carryop, bool initial, bool final, int fastCallBase) +{ + bool changed = false; + + carryop = ASMIT_NOP; + + mFlags &= ~NCIF_YZERO; + + if ((data[CPU_REG_Y].mMode & NRDM_IMMEDIATE) && (data[CPU_REG_Y].mValue == 0)) + mFlags |= NCIF_YZERO; + + if (mType == ASMIT_JSR) + { + data.ResetCall(*this, fastCallBase); + + return false; + } + + if (data[CPU_REG_C].mMode == NRDM_IMMEDIATE && data[CPU_REG_C].mValue == 0) + { + switch (mType) + { + case ASMIT_ROL: + mType = ASMIT_ASL; + changed = true; + break; + case ASMIT_ROR: + mType = ASMIT_LSR; + changed = true; + break; + } + } + + switch (mType) + { + case ASMIT_CLC: + data.Set(CPU_REG_C, NRDM_IMMEDIATE, 0); + break; + case ASMIT_SEC: + data.Set(CPU_REG_C, NRDM_IMMEDIATE, 1); + break; + + case ASMIT_ROL: + case ASMIT_ROR: + if (mMode == ASMIM_IMPLIED) + data.Reset(CPU_REG_A); + data.Reset(CPU_REG_C); + data.Reset(CPU_REG_Z); + break; + + case ASMIT_ASL: + case ASMIT_LSR: + if (mMode == ASMIM_IMPLIED) + { + if (data[CPU_REG_A].mMode == NRDM_IMMEDIATE && data[CPU_REG_A].mValue == 0 && !(mLive & LIVE_CPU_REG_Z)) + { + mType = ASMIT_CLC; + data.Set(CPU_REG_C, NRDM_IMMEDIATE, 0); + changed = true; + } + else + { + data.Reset(CPU_REG_A); + data.Reset(CPU_REG_C); + data.Reset(CPU_REG_Z); + } + } + else + { + data.Reset(CPU_REG_C); + data.Reset(CPU_REG_Z); + } + break; + + case ASMIT_INC: + case ASMIT_DEC: + data.Reset(CPU_REG_Z); + break; + + case ASMIT_LDA: + if (mMode == ASMIM_IMMEDIATE) + { + if (data[CPU_REG_A].mMode == NRDM_IMMEDIATE && data[CPU_REG_A].mValue == mAddress && !(mLive & LIVE_CPU_REG_Z)) + { + mType = ASMIT_NOP; + mMode = ASMIM_IMPLIED; + changed = true; + } + else + { + data.Set(CPU_REG_A, NRDM_IMMEDIATE, mAddress); + data.Set(CPU_REG_Z, NRDM_IMMEDIATE, mAddress); + } + } + else if (mMode == ASMIM_IMMEDIATE_ADDRESS) + { + if (data[CPU_REG_A].mMode == NRDM_IMMEDIATE_ADDRESS && + data[CPU_REG_A].mLinkerObject == mLinkerObject && + data[CPU_REG_A].mFlags == mFlags && + data[CPU_REG_A].mValue == mAddress && !(mLive & LIVE_CPU_REG_Z)) + { + mType = ASMIT_NOP; + mMode = ASMIM_IMPLIED; + changed = true; + } + else + { + data.Set(CPU_REG_A, NRDM_IMMEDIATE_ADDRESS, mAddress, mLinkerObject, mFlags); + if (mLinkerObject) + data.Set(CPU_REG_Z, NRDM_IMMEDIATE, 1); + else + data.Reset(CPU_REG_Z); + } + } + else + { + if (mMode != ASMIM_ZERO_PAGE && mMode != ASMIM_ABSOLUTE && mMode != ASMIM_INDIRECT_Y && mMode != ASMIM_ABSOLUTE_X && mMode != ASMIM_ABSOLUTE_Y) + data.Reset(CPU_REG_A); + data.Reset(CPU_REG_Z); + } + break; + + case ASMIT_STA: + if (mMode == ASMIM_ZERO_PAGE && data[CPU_REG_A].mMode == NRDM_ZERO_PAGE && mAddress == data[CPU_REG_A].mValue) + { + mType = ASMIT_NOP; + mMode = ASMIM_IMPLIED; + changed = true; + } + break; + case ASMIT_STX: + if (mMode == ASMIM_ZERO_PAGE && data[CPU_REG_X].mMode == NRDM_ZERO_PAGE && mAddress == data[CPU_REG_X].mValue) + { + mType = ASMIT_NOP; + mMode = ASMIM_IMPLIED; + changed = true; + } + break; + case ASMIT_STY: + if (mMode == ASMIM_ZERO_PAGE && data[CPU_REG_Y].mMode == NRDM_ZERO_PAGE && mAddress == data[CPU_REG_Y].mValue) + { + mType = ASMIT_NOP; + mMode = ASMIM_IMPLIED; + changed = true; + } + break; + + case ASMIT_ADC: + if (data[CPU_REG_C].mMode == NRDM_IMMEDIATE) + { + if (mMode == ASMIM_IMMEDIATE && mAddress == 0 && data[CPU_REG_C].mValue == 0) + { + mType = ASMIT_ORA; + changed = true; + } + else if (data[CPU_REG_A].mMode == NRDM_IMMEDIATE && data[CPU_REG_A].mValue == 0 && data[CPU_REG_C].mValue == 0) + { + mType = ASMIT_LDA; + changed = true; + } + else if (mMode == ASMIM_IMMEDIATE && data[CPU_REG_A].mMode == NRDM_IMMEDIATE) + { + int t = mAddress + data[CPU_REG_A].mValue + data[CPU_REG_C].mValue; + + mType = ASMIT_LDA; + mAddress = t & 0xff; + + int c = t >= 256; + + if (c && !data[CPU_REG_C].mValue) + carryop = ASMIT_SEC; + else if (!c && data[CPU_REG_C].mValue) + carryop = ASMIT_CLC; + + changed = true; + } + } + + data.Reset(CPU_REG_A); + data.Reset(CPU_REG_C); + data.Reset(CPU_REG_Z); + break; + + case ASMIT_SBC: + if (data[CPU_REG_C].mMode == NRDM_IMMEDIATE) + { + if (mMode == ASMIM_IMMEDIATE && mAddress == 0 && data[CPU_REG_C].mValue == 1) + { + mType = ASMIT_ORA; + changed = true; + } + else if (mMode == ASMIM_IMMEDIATE && data[CPU_REG_A].mMode == NRDM_IMMEDIATE) + { + int t = (mAddress ^ 0xff) + data[CPU_REG_A].mValue + data[CPU_REG_C].mValue; + + mType = ASMIT_LDA; + mAddress = t & 0xff; + + int c = t >= 256; + + if (c && !data[CPU_REG_C].mValue) + carryop = ASMIT_SEC; + else if (!c && data[CPU_REG_C].mValue) + carryop = ASMIT_CLC; + + changed = true; + } + else if (mMode == ASMIM_IMMEDIATE_ADDRESS && data[CPU_REG_A].mMode == NRDM_IMMEDIATE_ADDRESS && mLinkerObject == data[CPU_REG_A].mLinkerObject) + { + int t; + if (mFlags & NCIF_LOWER) + t = ((mAddress ^ 0xffff) & 0xff) + (data[CPU_REG_A].mValue & 0xff) + data[CPU_REG_C].mValue; + else + t = ((mAddress ^ 0xffff) >> 8) + (data[CPU_REG_A].mValue >> 8) + data[CPU_REG_C].mValue; + + mType = ASMIT_LDA; + mMode = ASMIM_IMMEDIATE; + mAddress = t & 0xff; + + int c = t >= 256; + + if (c && !data[CPU_REG_C].mValue) + carryop = ASMIT_SEC; + else if (!c && data[CPU_REG_C].mValue) + carryop = ASMIT_CLC; + + changed = true; + } + } + + data.Reset(CPU_REG_A); + data.Reset(CPU_REG_C); + data.Reset(CPU_REG_Z); + break; + case ASMIT_CMP: + if (mMode == ASMIM_IMMEDIATE && data[CPU_REG_A].mMode == NRDM_IMMEDIATE) + { + data.Set(CPU_REG_Z, NRDM_IMMEDIATE, (data[CPU_REG_A].mValue - mAddress) & 0xff); + data.Set(CPU_REG_C, NRDM_IMMEDIATE, data[CPU_REG_A].mValue >= mAddress); + } + else if (mMode == ASMIM_IMMEDIATE_ADDRESS && data[CPU_REG_A].mMode == NRDM_IMMEDIATE_ADDRESS) + { + if (mLinkerObject == data[CPU_REG_A].mLinkerObject) + { + if (mLinkerObject) + { + if (mLinkerObject->mFlags & LOBJF_NEVER_CROSS) + { + if (mFlags & NCIF_LOWER) + data.Set(CPU_REG_Z, NRDM_IMMEDIATE, (data[CPU_REG_A].mValue - mAddress) & 0xff); + else + data.Set(CPU_REG_Z, NRDM_IMMEDIATE, 0); + } + else + { + if (mFlags & NCIF_LOWER) + { + data.Set(CPU_REG_Z, NRDM_IMMEDIATE, (data[CPU_REG_A].mValue & 0xff) == (mAddress & 0xff) ? 0 : 1); + } + else if (data[CPU_REG_A].mValue == mAddress) + { + data.Set(CPU_REG_Z, NRDM_IMMEDIATE, 0); + } + else + data.Reset(CPU_REG_Z); + } + } + else + { + if (mFlags & NCIF_LOWER) + { + data.Set(CPU_REG_Z, NRDM_IMMEDIATE, (data[CPU_REG_A].mValue - mAddress) & 0xff); + } + else + { + data.Set(CPU_REG_Z, NRDM_IMMEDIATE, ((data[CPU_REG_A].mValue - mAddress) >> 8) & 0xff); + } + } + } + else + data.Set(CPU_REG_Z, NRDM_IMMEDIATE, 1); + data.Reset(CPU_REG_C); + } + else if (mMode == ASMIM_IMMEDIATE && mAddress == 0) + { + data.Set(CPU_REG_C, NRDM_IMMEDIATE, 1); + data.Reset(CPU_REG_Z); + } + else + { + data.Reset(CPU_REG_C); + data.Reset(CPU_REG_Z); + } + break; + case ASMIT_CPX: + if (mMode == ASMIM_IMMEDIATE && data[CPU_REG_X].mMode == NRDM_IMMEDIATE) + { + data.Set(CPU_REG_Z, NRDM_IMMEDIATE, (data[CPU_REG_X].mValue - mAddress) & 0xff); + data.Set(CPU_REG_C, NRDM_IMMEDIATE, data[CPU_REG_X].mValue >= mAddress); + } + else + { + data.Reset(CPU_REG_C); + data.Reset(CPU_REG_Z); + } + break; + case ASMIT_CPY: + if (mMode == ASMIM_IMMEDIATE && data[CPU_REG_Y].mMode == NRDM_IMMEDIATE) + { + data.Set(CPU_REG_Z, NRDM_IMMEDIATE, (data[CPU_REG_Y].mValue - mAddress) & 0xff); + data.Set(CPU_REG_C, NRDM_IMMEDIATE, data[CPU_REG_Y].mValue >= mAddress); + } + else + { + data.Reset(CPU_REG_C); + data.Reset(CPU_REG_Z); + } + break; + + case ASMIT_ORA: + case ASMIT_EOR: + case ASMIT_AND: + if (mMode == ASMIM_IMMEDIATE && data[CPU_REG_A].mMode == NRDM_IMMEDIATE) + { + if (mType == ASMIT_ORA) + mAddress |= data[CPU_REG_A].mValue; + else if (mType == ASMIT_AND) + mAddress &= data[CPU_REG_A].mValue; + else if (mType == ASMIT_EOR) + mAddress ^= data[CPU_REG_A].mValue; + mType = ASMIT_LDA; + data.Set(CPU_REG_A, NRDM_IMMEDIATE, mAddress); + data.Set(CPU_REG_Z, NRDM_IMMEDIATE, mAddress); + changed = true; + } + else if (data[CPU_REG_A].mMode == NRDM_IMMEDIATE && data[CPU_REG_A].mValue == 0) + { + if (mType == ASMIT_ORA || mType == ASMIT_EOR) + { + mType = ASMIT_LDA; + data.Reset(CPU_REG_A); + data.Reset(CPU_REG_Z); + } + else + { + mType = ASMIT_LDA; + mMode = ASMIM_IMMEDIATE; + mAddress = 0; + + data.Set(CPU_REG_Z, NRDM_IMMEDIATE, 0); + } + changed = true; + } + else if (mMode == ASMIM_IMMEDIATE && mType == ASMIT_ORA && mAddress != 0x00) + { + data.Set(CPU_REG_Z, NRDM_IMMEDIATE, mAddress); + data.Reset(CPU_REG_A); + } + else if (mMode == ASMIM_IMMEDIATE && ((mAddress == 0 && (mType == ASMIT_ORA || mType == ASMIT_EOR)) || (mAddress == 0xff && mType == ASMIT_AND))) + { + data.Reset(CPU_REG_Z); + } + else + { + data.Reset(CPU_REG_A); + data.Reset(CPU_REG_Z); + } + break; + case ASMIT_LDX: + data.ResetX(); + if (mMode == ASMIM_IMMEDIATE) + { + if (data[CPU_REG_X].mMode == NRDM_IMMEDIATE && data[CPU_REG_X].mValue == mAddress && !(mLive & LIVE_CPU_REG_Z)) + { + mType = ASMIT_NOP; + mMode = ASMIM_IMPLIED; + changed = true; + } + else + { + data.Set(CPU_REG_X, NRDM_IMMEDIATE, mAddress); + data.Set(CPU_REG_Z, NRDM_IMMEDIATE, mAddress); + } + } + else if (mMode == ASMIM_IMMEDIATE_ADDRESS) + { + if (data[CPU_REG_X].mMode == NRDM_IMMEDIATE_ADDRESS && + data[CPU_REG_X].mLinkerObject == mLinkerObject && + data[CPU_REG_X].mFlags == mFlags && + data[CPU_REG_X].mValue == mAddress && !(mLive & LIVE_CPU_REG_Z)) + { + mType = ASMIT_NOP; + mMode = ASMIM_IMPLIED; + changed = true; + } + else + { + data.Set(CPU_REG_X, NRDM_IMMEDIATE_ADDRESS, mAddress, mLinkerObject, mFlags); + data.Reset(CPU_REG_Z); + } + } + else + { + if (mMode != ASMIM_ZERO_PAGE && mMode != ASMIM_ABSOLUTE && mMode != ASMIM_ABSOLUTE_Y) + data.Reset(CPU_REG_X); + data.Reset(CPU_REG_Z); + } + break; + case ASMIT_BIT: + data.Reset(CPU_REG_Z); + break; + case ASMIT_INX: + case ASMIT_DEX: + data.ResetX(); + data.Reset(CPU_REG_X); + data.Reset(CPU_REG_Z); + break; + case ASMIT_LDY: + data.ResetY(); + if (mMode == ASMIM_IMMEDIATE) + { + if (data[CPU_REG_Y].mMode == NRDM_IMMEDIATE && data[CPU_REG_Y].mValue == mAddress && !(mLive & LIVE_CPU_REG_Z)) + { + mType = ASMIT_NOP; + mMode = ASMIM_IMPLIED; + changed = true; + } + else + { + data.Set(CPU_REG_Y, NRDM_IMMEDIATE, mAddress); + data.Set(CPU_REG_Z, NRDM_IMMEDIATE, mAddress); + } + } + else if (mMode == ASMIM_IMMEDIATE_ADDRESS) + { + if (data[CPU_REG_Y].mMode == NRDM_IMMEDIATE_ADDRESS && + data[CPU_REG_Y].mLinkerObject == mLinkerObject && + data[CPU_REG_Y].mFlags == mFlags && + data[CPU_REG_Y].mValue == mAddress && !(mLive & LIVE_CPU_REG_Z)) + { + mType = ASMIT_NOP; + mMode = ASMIM_IMPLIED; + changed = true; + } + else + { + data.Set(CPU_REG_Y, NRDM_IMMEDIATE_ADDRESS, mAddress, mLinkerObject, mFlags); + data.Reset(CPU_REG_Z); + } + } + else + { + if (mMode != ASMIM_ZERO_PAGE && mMode != ASMIM_ABSOLUTE && mMode != ASMIM_ABSOLUTE_X) + data.Reset(CPU_REG_Y); + data.Reset(CPU_REG_Z); + } + break; + case ASMIT_INY: + case ASMIT_DEY: + data.ResetY(); + data.Reset(CPU_REG_Y); + data.Reset(CPU_REG_Z); + break; + + case ASMIT_TXA: + if (data[CPU_REG_A].SameData(data[CPU_REG_X])) + { + if (!(mLive & LIVE_CPU_REG_Z)) + { + mType = ASMIT_NOP; + mMode = ASMIM_IMPLIED; + } + else + { + mType = ASMIT_ORA; + mMode = ASMIM_IMMEDIATE; + mAddress = 0x00; + } + + changed = true; + } + else + { + data.Set(CPU_REG_A, data[CPU_REG_X]); + if (data[CPU_REG_A].mMode == NRDM_IMMEDIATE) + { + if (!final) + { + mType = ASMIT_LDA; + mMode = ASMIM_IMMEDIATE; + mAddress = data[CPU_REG_A].mValue; + } + + data.Set(CPU_REG_Z, NRDM_IMMEDIATE, data[CPU_REG_A].mValue); + } + else + data.Reset(CPU_REG_Z); + } + break; + case ASMIT_TYA: + if (data[CPU_REG_A].SameData(data[CPU_REG_Y]) && !(mLive & LIVE_CPU_REG_Z)) + { + mType = ASMIT_NOP; + mMode = ASMIM_IMPLIED; + changed = true; + } + else + { + data.Set(CPU_REG_A, data[CPU_REG_Y]); + if (data[CPU_REG_A].mMode == NRDM_IMMEDIATE) + { + if (!final) + { + mType = ASMIT_LDA; + mMode = ASMIM_IMMEDIATE; + mAddress = data[CPU_REG_A].mValue; + } + + data.Set(CPU_REG_Z, NRDM_IMMEDIATE, data[CPU_REG_A].mValue); + } + else + data.Reset(CPU_REG_Z); + } + break; + case ASMIT_TAX: + if (data[CPU_REG_X].SameData(data[CPU_REG_A]) && !(mLive & LIVE_CPU_REG_Z)) + { + mType = ASMIT_NOP; + mMode = ASMIM_IMPLIED; + changed = true; + } + else + { + data.ResetX(); + data.Set(CPU_REG_X, data[CPU_REG_A]); + if (data[CPU_REG_X].mMode == NRDM_IMMEDIATE) + data.Set(CPU_REG_Z, NRDM_IMMEDIATE, data[CPU_REG_X].mValue); + else + data.Reset(CPU_REG_Z); + } + break; + case ASMIT_TAY: + if (data[CPU_REG_Y].SameData(data[CPU_REG_A]) && !(mLive & LIVE_CPU_REG_Z)) + { + mType = ASMIT_NOP; + mMode = ASMIM_IMPLIED; + changed = true; + } + else + { + data.ResetY(); + data.Set(CPU_REG_Y, data[CPU_REG_A]); + if (data[CPU_REG_Y].mMode == NRDM_IMMEDIATE) + data.Set(CPU_REG_Z, NRDM_IMMEDIATE, data[CPU_REG_Y].mValue); + else + data.Reset(CPU_REG_Z); + } + break; + } + + if (mFlags & NCIF_MEMMAP) + data.ResetGlobal(); + +#if 1 + if (mMode == ASMIM_ABSOLUTE_X && data[CPU_REG_X].mMode == NRDM_IMMEDIATE) + { + mMode = ASMIM_ABSOLUTE; + mAddress += data[CPU_REG_X].mValue; + changed = true; + } + else if (mMode == ASMIM_ABSOLUTE_Y && data[CPU_REG_Y].mMode == NRDM_IMMEDIATE) + { + mMode = ASMIM_ABSOLUTE; + mAddress += data[CPU_REG_Y].mValue; + changed = true; + } +#endif + +#if 1 + if (mMode == ASMIM_ABSOLUTE_X && data[CPU_REG_X].SameData(data[CPU_REG_Y]) && HasAsmInstructionMode(mType, ASMIM_ABSOLUTE_Y) && !(mFlags & NICT_INDEXFLIPPED)) + { + mFlags |= NICT_INDEXFLIPPED; + mMode = ASMIM_ABSOLUTE_Y; + changed = true; + } + else if (final && mMode == ASMIM_ABSOLUTE_Y && data[CPU_REG_X].SameData(data[CPU_REG_Y]) && HasAsmInstructionMode(mType, ASMIM_ABSOLUTE_X) && !(mFlags & NICT_INDEXFLIPPED)) + { + mFlags |= NICT_INDEXFLIPPED; + mMode = ASMIM_ABSOLUTE_X; + changed = true; + } +#endif + +#if 1 + if (mMode == ASMIM_ABSOLUTE && final && !(mFlags & NCIF_VOLATILE) && !ChangesAddress() && HasAsmInstructionMode(mType, ASMIM_ZERO_PAGE)) + { + if (mLinkerObject && (mLinkerObject->mFlags & LOBJF_ZEROPAGE)) + { + } + else + { + int i = data.FindAbsolute(mLinkerObject, mAddress); + if (i >= 0) + { + mMode = ASMIM_ZERO_PAGE; + mAddress = i; + mLinkerObject = nullptr; + changed = true; + } + } + } +#endif +#if 1 + if (final && mType == ASMIT_STX && data[CPU_REG_A].SameData(data[CPU_REG_X])) + { + mType = ASMIT_STA; + changed = true; + } + else if (final && mType == ASMIT_STY && data[CPU_REG_A].SameData(data[CPU_REG_Y])) + { + mType = ASMIT_STA; + changed = true; + } +#endif + if (mMode == ASMIM_ZERO_PAGE) + { + switch (mType) + { + case ASMIT_LDA: + if (data[CPU_REG_A].mMode == NRDM_ZERO_PAGE && data[CPU_REG_A].mValue == mAddress) + { + if (mLive & LIVE_CPU_REG_Z) + { + mType = ASMIT_ORA; + mMode = ASMIM_IMMEDIATE; + mAddress = 0; + } + else + { + mType = ASMIT_NOP; + mMode = ASMIM_IMPLIED; + } + changed = true; + } + else if (data[mAddress].mMode == NRDM_IMMEDIATE) + { + data.Set(CPU_REG_A, data[mAddress]); + mAddress = data[CPU_REG_A].mValue; + mMode = ASMIM_IMMEDIATE; + changed = true; + } + else if (data[mAddress].mMode == NRDM_IMMEDIATE_ADDRESS) + { + data.Set(CPU_REG_A, data[mAddress]); + mAddress = data[CPU_REG_A].mValue; + mLinkerObject = data[CPU_REG_A].mLinkerObject; + mFlags = (mFlags & ~(NCIF_LOWER | NCIF_UPPER)) | (data[CPU_REG_A].mFlags & (NCIF_LOWER | NCIF_UPPER)); + mMode = ASMIM_IMMEDIATE_ADDRESS; + changed = true; + } + else if (data[mAddress].SameData(data[CPU_REG_A])) + { + if (mLive & LIVE_CPU_REG_Z) + { + mType = ASMIT_ORA; + mMode = ASMIM_IMMEDIATE; + mAddress = 0; + } + else + { + mType = ASMIT_NOP; + mMode = ASMIM_IMPLIED; + } + changed = true; + } +// else if (data[mAddress].mMode == NRDM_ZERO_PAGE && ((mAddress < BC_REG_FPARAMS || mAddress >= BC_REG_FPARAMS_END)))// && (final || mAddress != BC_REG_ACCU))) + else if (data[mAddress].mMode == NRDM_ZERO_PAGE && ((mAddress < BC_REG_FPARAMS || mAddress >= BC_REG_FPARAMS_END) && (final || mAddress != BC_REG_ACCU))) + { + data.Set(CPU_REG_A, data[mAddress]); + mAddress = data[CPU_REG_A].mValue; + changed = true; + } +#if 1 + else if (data[CPU_REG_X].SameData(*this)) + { + mType = ASMIT_TXA; + mMode = ASMIM_IMPLIED; + data.Set(CPU_REG_A, data[CPU_REG_X]); + changed = true; + } + else if (data[CPU_REG_Y].SameData(*this)) + { + mType = ASMIT_TYA; + mMode = ASMIM_IMPLIED; + data.Set(CPU_REG_A, data[CPU_REG_Y]); + changed = true; + } +#else + else if (data.mRegs[CPU_REG_X].mMode == NRDM_ZERO_PAGE && data.mRegs[CPU_REG_X].mValue == mAddress) + { + mType = ASMIT_TXA; + mMode = ASMIM_IMPLIED; + data.mRegs[CPU_REG_A] = data.mRegs[CPU_REG_X]; + changed = true; + } + else if (data.mRegs[CPU_REG_Y].mMode == NRDM_ZERO_PAGE && data.mRegs[CPU_REG_Y].mValue == mAddress) + { + mType = ASMIT_TYA; + mMode = ASMIM_IMPLIED; + data.mRegs[CPU_REG_A] = data.mRegs[CPU_REG_Y]; + changed = true; + } +#endif + else if (final && data[mAddress].mMode == NRDM_ABSOLUTE && data[mAddress].mLinkerObject && (data[mAddress].mLinkerObject->mFlags & LOBJF_ZEROPAGE)) + { + data.Set(CPU_REG_A, data[mAddress]); + mMode = ASMIM_ABSOLUTE; + mFlags |= data[mAddress].mFlags; + mLinkerObject = data[mAddress].mLinkerObject; + mAddress = data[mAddress].mValue; + changed = true; + } + else + { + data.Reset(CPU_REG_A); + if (data[mAddress].mMode == NRDM_IMMEDIATE) + { + data.Set(CPU_REG_A, NRDM_IMMEDIATE, data[mAddress].mValue); + } + else if (initial && mAddress == BC_REG_WORK_Y) + { + data.Reset(CPU_REG_A); + } + else + { + data.Set(CPU_REG_A, NRDM_ZERO_PAGE, mAddress, nullptr, mFlags, mMinVal, mMaxVal); + } + } + break; + + case ASMIT_LDX: + if (data[CPU_REG_X].mMode == NRDM_ZERO_PAGE && data[CPU_REG_X].mValue == mAddress && !(mLive & LIVE_CPU_REG_Z)) + { + mType = ASMIT_NOP; + mMode = ASMIM_IMPLIED; + changed = true; + } + else if (data[mAddress].mMode == NRDM_IMMEDIATE) + { + data.Set(CPU_REG_X, data[mAddress]); + mAddress = data[CPU_REG_X].mValue; + mMode = ASMIM_IMMEDIATE; + changed = true; + } + else if (data[CPU_REG_A].mMode == NRDM_ZERO_PAGE && data[CPU_REG_A].mValue == mAddress) + { + mType = ASMIT_TAX; + mMode = ASMIM_IMPLIED; + data.Set(CPU_REG_X, data[CPU_REG_A]); + changed = true; + } + else if (data[mAddress].SameData(data[CPU_REG_A])) + { + mType = ASMIT_TAX; + mMode = ASMIM_IMPLIED; + data.Set(CPU_REG_X, data[CPU_REG_A]); + changed = true; + } +#if 1 + else if (data[mAddress].mMode == NRDM_ZERO_PAGE) + { + data.Set(CPU_REG_X, data[mAddress]); + mAddress = data[CPU_REG_X].mValue; + changed = true; + } +#endif + else if (final && data[mAddress].mMode == NRDM_ABSOLUTE && data[mAddress].mLinkerObject && (data[mAddress].mLinkerObject->mFlags & LOBJF_ZEROPAGE)) + { + data.Set(CPU_REG_X, data[mAddress]); + mMode = ASMIM_ABSOLUTE; + mFlags |= data[mAddress].mFlags; + mLinkerObject = data[mAddress].mLinkerObject; + mAddress = data[mAddress].mValue; + changed = true; + } + else + { + data.Reset(CPU_REG_X); + if (data[mAddress].mMode == NRDM_IMMEDIATE) + { + data.Set(CPU_REG_X, NRDM_IMMEDIATE, data[mAddress].mValue); + } + else + { + data.Set(CPU_REG_X, NRDM_ZERO_PAGE, mAddress, nullptr, mFlags); + } + } + break; + + case ASMIT_LDY: + if (data[CPU_REG_Y].mMode == NRDM_ZERO_PAGE && data[CPU_REG_Y].mValue == mAddress && !(mLive & LIVE_CPU_REG_Z)) + { + mType = ASMIT_NOP; + mMode = ASMIM_IMPLIED; + changed = true; + } + else if (data[mAddress].mMode == NRDM_IMMEDIATE) + { + data.Set(CPU_REG_Y, data[mAddress]); + mAddress = data[CPU_REG_Y].mValue; + mMode = ASMIM_IMMEDIATE; + changed = true; + } + else if (final && data[CPU_REG_A].mMode == NRDM_ZERO_PAGE && data[CPU_REG_A].mValue == mAddress) + { + mType = ASMIT_TAY; + mMode = ASMIM_IMPLIED; + data.Set(CPU_REG_Y, data[CPU_REG_A]); + changed = true; + } + else if (final && data[mAddress].SameData(data[CPU_REG_A])) + { + mType = ASMIT_TAY; + mMode = ASMIM_IMPLIED; + data.Set(CPU_REG_Y, data[CPU_REG_A]); + changed = true; + } +#if 1 + else if (data[mAddress].mMode == NRDM_ZERO_PAGE) + { + data.Set(CPU_REG_Y, data[mAddress]); + mAddress = data[CPU_REG_Y].mValue; + changed = true; + } +#endif + else if (final && data[mAddress].mMode == NRDM_ABSOLUTE && data[mAddress].mLinkerObject && (data[mAddress].mLinkerObject->mFlags & LOBJF_ZEROPAGE)) + { + data.Set(CPU_REG_Y, data[mAddress]); + mMode = ASMIM_ABSOLUTE; + mFlags |= data[mAddress].mFlags; + mLinkerObject = data[mAddress].mLinkerObject; + mAddress = data[mAddress].mValue; + changed = true; + } + else + { + data.Reset(CPU_REG_Y); + if (data[mAddress].mMode == NRDM_IMMEDIATE) + { + data.Set(CPU_REG_Y, NRDM_IMMEDIATE, data[mAddress].mValue); + } + else + { + data.Set(CPU_REG_Y, NRDM_ZERO_PAGE, mAddress, nullptr, mFlags); + } + } + break; + + case ASMIT_ADC: + case ASMIT_SBC: + case ASMIT_AND: + case ASMIT_ORA: + case ASMIT_EOR: + case ASMIT_CMP: + case ASMIT_CPX: + case ASMIT_CPY: + if (data[mAddress].mMode == NRDM_IMMEDIATE) + { + mAddress = data[mAddress].mValue; + mMode = ASMIM_IMMEDIATE; + changed = true; + } + else if (data[mAddress].mMode == NRDM_ZERO_PAGE) + { + mMinVal = intmax(mMinVal, data[mAddress].mMinVal); + mMaxVal = intmin(mMaxVal, data[mAddress].mMaxVal); + mAddress = data[mAddress].mValue; + changed = true; + } + else if (data[mAddress].mMode == NRDM_ABSOLUTE && !final) + { + mMode = ASMIM_ABSOLUTE; + mLinkerObject = data[mAddress].mLinkerObject; + mAddress = data[mAddress].mValue; + changed = true; + } + break; + + case ASMIT_STA: + if (data[mAddress].SameData(data[CPU_REG_A]) || data[CPU_REG_A].mMode == NRDM_ZERO_PAGE && data[CPU_REG_A].mValue == mAddress) + { + mType = ASMIT_NOP; + mMode = ASMIM_IMPLIED; + changed = true; + } + else if (final && data[CPU_REG_A].mMode == NRDM_IMMEDIATE && data[mAddress].mMode == NRDM_IMMEDIATE && + data[mAddress].mValue == ((data[CPU_REG_A].mValue - 1) & 255)) + { + mType = ASMIT_INC; + data.Set(mAddress, NRDM_IMMEDIATE, data[CPU_REG_A].mValue); + changed = true; + } + else if (final && data[CPU_REG_A].mMode == NRDM_IMMEDIATE && data[mAddress].mMode == NRDM_IMMEDIATE && + data[mAddress].mValue == ((data[CPU_REG_A].mValue + 1) & 255)) + { + mType = ASMIT_DEC; + data.Set(mAddress, NRDM_IMMEDIATE, data[CPU_REG_A].mValue); + changed = true; + } + else + { + data.ResetZeroPage(mAddress); + if (data[CPU_REG_A].mMode == NRDM_IMMEDIATE || data[CPU_REG_A].mMode == NRDM_IMMEDIATE_ADDRESS) + { + data.Set(mAddress, data[CPU_REG_A]); + } + else if (data[CPU_REG_A].mMode == NRDM_ZERO_PAGE) + { +#if 1 + if (data[data[CPU_REG_A].mValue].mMode == NRDM_UNKNOWN && + (mAddress >= BC_REG_FPARAMS && mAddress < BC_REG_FPARAMS_END) && + !(data[CPU_REG_A].mValue >= BC_REG_FPARAMS && data[CPU_REG_A].mValue < BC_REG_FPARAMS_END)) + { + data.Set(data[CPU_REG_A].mValue, NRDM_ZERO_PAGE, mAddress, nullptr, mFlags); + data.Reset(mAddress); + } + else +#endif + { + data.Set(mAddress, data[CPU_REG_A]); + } + + } + else if (data[CPU_REG_A].mMode == NRDM_ABSOLUTE) + { + data.Set(mAddress, data[CPU_REG_A]); + if (!final) + data.Set(CPU_REG_A, NRDM_ZERO_PAGE, mAddress); + } + else + { + data.Set(CPU_REG_A, NRDM_ZERO_PAGE, mAddress); + } + } + break; + case ASMIT_STX: + data.ResetZeroPage(mAddress); + if (data[CPU_REG_X].mMode == NRDM_IMMEDIATE) + { + data.Set(mAddress, NRDM_IMMEDIATE, data[CPU_REG_X].mValue); + } + else if (data[CPU_REG_X].mMode == NRDM_ZERO_PAGE) + { + data.Set(mAddress, NRDM_ZERO_PAGE, data[CPU_REG_X].mValue, nullptr, data[CPU_REG_X].mFlags); + } + else if (data[CPU_REG_X].mMode == NRDM_ABSOLUTE) + { + data.Set(mAddress, data[CPU_REG_X]); + } + else + { + data.Set(CPU_REG_X, NRDM_ZERO_PAGE, mAddress, nullptr, mFlags); + } + break; + case ASMIT_STY: + data.ResetZeroPage(mAddress); + if (data[CPU_REG_Y].mMode == NRDM_IMMEDIATE) + { + data.Set(mAddress, NRDM_IMMEDIATE, data[CPU_REG_Y].mValue); + } + else if (data[CPU_REG_Y].mMode == NRDM_ZERO_PAGE) + { + data.Set(mAddress, NRDM_ZERO_PAGE, data[CPU_REG_Y].mValue, nullptr, data[CPU_REG_Y].mFlags); + } + else if (data[CPU_REG_Y].mMode == NRDM_ABSOLUTE) + { + data.Set(mAddress, data[CPU_REG_Y]); + } + else + { + data.Set(CPU_REG_Y, NRDM_ZERO_PAGE, mAddress, nullptr, mFlags); + } + break; + case ASMIT_INC: + case ASMIT_DEC: + case ASMIT_ASL: + case ASMIT_LSR: + case ASMIT_ROL: + case ASMIT_ROR: + data.ResetZeroPage(mAddress); + break; + } + } + else if (final && mMode == ASMIM_IMMEDIATE) + { +#if 1 + switch (mType) + { + case ASMIT_LDA: + if (data[CPU_REG_Y].mMode == NRDM_IMMEDIATE && data[CPU_REG_Y].mValue == mAddress) + { + data.Set(CPU_REG_A, data[CPU_REG_Y]); + mType = ASMIT_TYA; + mMode = ASMIM_IMPLIED; + changed = true; + } + else if (data[CPU_REG_X].mMode == NRDM_IMMEDIATE && data[CPU_REG_X].mValue == mAddress) + { + data.Set(CPU_REG_A, data[CPU_REG_X]); + mType = ASMIT_TXA; + mMode = ASMIM_IMPLIED; + changed = true; + } + break; + case ASMIT_LDX: + if (data[CPU_REG_A].mMode == NRDM_IMMEDIATE && data[CPU_REG_A].mValue == mAddress) + { + data.Set(CPU_REG_X, data[CPU_REG_A]); + mType = ASMIT_TAX; + mMode = ASMIM_IMPLIED; + changed = true; + } + break; + case ASMIT_LDY: + if (data[CPU_REG_A].mMode == NRDM_IMMEDIATE && data[CPU_REG_A].mValue == mAddress) + { + data.Set(CPU_REG_Y, data[CPU_REG_A]); + mType = ASMIT_TAY; + mMode = ASMIM_IMPLIED; + changed = true; + } + break; + } +#endif + } + else if (mMode == ASMIM_INDIRECT_Y) + { + if (data[mAddress].mMode == NRDM_ZERO_PAGE && data[mAddress + 1].mMode == NRDM_ZERO_PAGE && data[mAddress].mValue + 1 == data[mAddress + 1].mValue) + { + mFlags |= NICT_ZPFLIPPED; + mAddress = data[mAddress].mValue; + if (mType == ASMIT_LDA) + data.Reset(CPU_REG_A); + } + else if (data[mAddress + 0].mMode == NRDM_ZERO_PAGE && data[mAddress + 1].mMode == NRDM_ZERO_PAGE && + data[data[mAddress + 1].mValue - 1].mMode == NRDM_ZERO_PAGE && + data[mAddress + 0].mValue == data[data[mAddress + 1].mValue - 1].mValue) + { + mFlags |= NICT_ZPFLIPPED; + mAddress = data[mAddress + 1].mValue - 1; + if (mType == ASMIT_LDA) + data.Reset(CPU_REG_A); + } + else if (data[mAddress].mMode == NRDM_IMMEDIATE_ADDRESS && data[mAddress + 1].mMode == NRDM_IMMEDIATE_ADDRESS && data[mAddress].mLinkerObject == data[mAddress + 1].mLinkerObject) + { + mMode = ASMIM_ABSOLUTE_Y; + mLinkerObject = data[mAddress].mLinkerObject; + mAddress = data[mAddress + 1].mValue; + if (mType == ASMIT_LDA) + data.Reset(CPU_REG_A); + } + else if (mType == ASMIT_LDA) + { + if (!(mFlags & NCIF_VOLATILE)) + { + if (data[CPU_REG_A].mMode == NRDM_INDIRECT_Y && data[CPU_REG_A].mValue == mAddress) + { + if (mLive & LIVE_CPU_REG_Z) + { + mType = ASMIT_ORA; + mMode = ASMIM_IMMEDIATE; + mAddress = 0; + } + else + { + mType = ASMIT_NOP; + mMode = ASMIM_IMPLIED; + } + changed = true; + } + else if (data[CPU_REG_X].SameData(*this)) + { + mType = ASMIT_TXA; + mMode = ASMIM_IMPLIED; + data.Set(CPU_REG_A, data[CPU_REG_X]); + changed = true; + } + else if (data[CPU_REG_Y].SameData(*this)) + { + mType = ASMIT_TYA; + mMode = ASMIM_IMPLIED; + data.Set(CPU_REG_A, data[CPU_REG_Y]); + changed = true; + } + else + { + data.Set(CPU_REG_A, NRDM_INDIRECT_Y, mAddress, nullptr, mFlags); + } + } + else + data.Reset(CPU_REG_A); + } + + if (ChangesAddress()) + data.ResetIndirect(mAddress); + } + else if (mMode == ASMIM_ABSOLUTE_X) + { + if (mType == ASMIT_LDA) + { + if (!(mFlags & NCIF_VOLATILE)) + { + if (data[CPU_REG_A].mMode == NRDM_ABSOLUTE_X && data[CPU_REG_A].mLinkerObject == mLinkerObject && data[CPU_REG_A].mValue == mAddress) + { + if (mLive & LIVE_CPU_REG_Z) + { + mType = ASMIT_ORA; + mMode = ASMIM_IMMEDIATE; + mAddress = 0; + } + else + { + mType = ASMIT_NOP; + mMode = ASMIM_IMPLIED; + } + changed = true; + } + else + { + data.Set(CPU_REG_A, NRDM_ABSOLUTE_X, mAddress, mLinkerObject, mFlags); + } + } + else + data.Reset(CPU_REG_A); + } + else if (mType == ASMIT_LDY) + { + if (!(mFlags & NCIF_VOLATILE)) + { + if (data[CPU_REG_Y].mMode == NRDM_ABSOLUTE_X && data[CPU_REG_Y].mLinkerObject == mLinkerObject && data[CPU_REG_Y].mValue == mAddress && !(mLive & LIVE_CPU_REG_Z)) + { + mType = ASMIT_NOP; + mMode = ASMIM_IMPLIED; + changed = true; + } + else if (data[CPU_REG_A].mMode == NRDM_ABSOLUTE_X && data[CPU_REG_A].mLinkerObject == mLinkerObject && data[CPU_REG_A].mValue == mAddress) + { + mType = ASMIT_TAY; + mMode = ASMIM_IMPLIED; + data.Set(CPU_REG_Y, data[CPU_REG_A]); + changed = true; + } + else + { + data.Set(CPU_REG_Y, NRDM_ABSOLUTE_X, mAddress, mLinkerObject, mFlags); + } + } + else + data.Reset(CPU_REG_Y); + } + + if (ChangesAddress()) + data.ResetAbsoluteXY(mLinkerObject, mAddress); + } + else if (mMode == ASMIM_ABSOLUTE_Y) + { + if (mType == ASMIT_LDA) + { + if (!(mFlags & NCIF_VOLATILE)) + { + if (data[CPU_REG_A].mMode == NRDM_ABSOLUTE_Y && data[CPU_REG_A].mLinkerObject == mLinkerObject && data[CPU_REG_A].mValue == mAddress) + { + if (mLive & LIVE_CPU_REG_Z) + { + mType = ASMIT_ORA; + mMode = ASMIM_IMMEDIATE; + mAddress = 0; + } + else + { + mType = ASMIT_NOP; + mMode = ASMIM_IMPLIED; + } + changed = true; + } + else + { + data.Set(CPU_REG_A, NRDM_ABSOLUTE_Y, mAddress, mLinkerObject, mFlags); + } + } + else + data.Reset(CPU_REG_A); + } + else if (mType == ASMIT_LDX) + { + if (!(mFlags & NCIF_VOLATILE)) + { + if (data[CPU_REG_X].mMode == NRDM_ABSOLUTE_Y && data[CPU_REG_X].mLinkerObject == mLinkerObject && data[CPU_REG_X].mValue == mAddress && !(mLive & LIVE_CPU_REG_Z)) + { + mType = ASMIT_NOP; + mMode = ASMIM_IMPLIED; + changed = true; + } + else if (data[CPU_REG_A].mMode == NRDM_ABSOLUTE_Y && data[CPU_REG_A].mLinkerObject == mLinkerObject && data[CPU_REG_A].mValue == mAddress) + { + mType = ASMIT_TAX; + mMode = ASMIM_IMPLIED; + data.Set(CPU_REG_X, data[CPU_REG_A]); + changed = true; + } + else + { + data.Set(CPU_REG_X, NRDM_ABSOLUTE_Y, mAddress, mLinkerObject, mFlags); + } + } + else + data.Reset(CPU_REG_X); + } + + if (ChangesAddress()) + data.ResetAbsoluteXY(mLinkerObject, mAddress); + } + else if (mMode == ASMIM_ABSOLUTE) + { + switch (mType) + { + case ASMIT_LDA: + if (!(mFlags & NCIF_VOLATILE)) + { + if (data[CPU_REG_A].mMode == NRDM_ABSOLUTE && data[CPU_REG_A].mLinkerObject == mLinkerObject && data[CPU_REG_A].mValue == mAddress) + { + if (mLive & LIVE_CPU_REG_Z) + { + mType = ASMIT_ORA; + mMode = ASMIM_IMMEDIATE; + mAddress = 0; + } + else + { + mType = ASMIT_NOP; + mMode = ASMIM_IMPLIED; + } + changed = true; + } + else + { + data.Set(CPU_REG_A, NRDM_ABSOLUTE, mAddress, mLinkerObject, mFlags); + } + } + else + data.Reset(CPU_REG_A); + break; +#if 1 + case ASMIT_STA: + if (!(mFlags & NCIF_VOLATILE) && mLinkerObject) + { + if (data[CPU_REG_A].mMode == NRDM_ABSOLUTE && data[CPU_REG_A].mLinkerObject == mLinkerObject && data[CPU_REG_A].mValue == mAddress) + { + mType = ASMIT_NOP; + mMode = ASMIM_IMPLIED; + changed = true; + } + else if (final || data[CPU_REG_A].mMode == NRDM_UNKNOWN) + { + data.ResetAbsolute(mLinkerObject, mAddress); + data.Set(CPU_REG_A, NRDM_ABSOLUTE, mAddress, mLinkerObject, mFlags); + } + else + data.ResetAbsolute(mLinkerObject, mAddress); + } + else + data.ResetAbsolute(mLinkerObject, mAddress); + break; +#endif + case ASMIT_LDY: + if (!(mFlags & NCIF_VOLATILE)) + { + if (data[CPU_REG_Y].mMode == NRDM_ABSOLUTE && data[CPU_REG_Y].mLinkerObject == mLinkerObject && data[CPU_REG_Y].mValue == mAddress) + { + if (!(mLive & LIVE_CPU_REG_Z)) + { + mType = ASMIT_NOP; + mMode = ASMIM_IMPLIED; + changed = true; + } + } + else if (final && data[CPU_REG_A].mMode == NRDM_ABSOLUTE && data[CPU_REG_A].mLinkerObject == mLinkerObject && data[CPU_REG_A].mValue == mAddress) + { + mType = ASMIT_TAY; + mMode = ASMIM_IMPLIED; + + data.Set(CPU_REG_Y, data[CPU_REG_A]); + changed = true; + } + else + { + data.Set(CPU_REG_Y, NRDM_ABSOLUTE, mAddress, mLinkerObject,mFlags); + } + } + else + data.Reset(CPU_REG_Y); + break; + + case ASMIT_STY: + if (!(mFlags & NCIF_VOLATILE) && mLinkerObject) + { + if (data[CPU_REG_Y].mMode == NRDM_ABSOLUTE && data[CPU_REG_Y].mLinkerObject == mLinkerObject && data[CPU_REG_Y].mValue == mAddress) + { + mType = ASMIT_NOP; + mMode = ASMIM_IMPLIED; + changed = true; + } + else if (data[CPU_REG_Y].mMode == NRDM_UNKNOWN) + { + data.ResetAbsolute(mLinkerObject, mAddress); + data.Set(CPU_REG_Y, NRDM_ABSOLUTE, mAddress, mLinkerObject, mFlags); + } + else + data.ResetAbsolute(mLinkerObject, mAddress); + } + else + data.ResetAbsolute(mLinkerObject, mAddress); + break; + + case ASMIT_LDX: + if (!(mFlags & NCIF_VOLATILE)) + { + if (data[CPU_REG_X].mMode == NRDM_ABSOLUTE && data[CPU_REG_X].mLinkerObject == mLinkerObject && data[CPU_REG_X].mValue == mAddress) + { + if (!(mLive & LIVE_CPU_REG_Z)) + { + mType = ASMIT_NOP; + mMode = ASMIM_IMPLIED; + changed = true; + } + } + else if (final && data[CPU_REG_A].mMode == NRDM_ABSOLUTE && data[CPU_REG_A].mLinkerObject == mLinkerObject && data[CPU_REG_A].mValue == mAddress) + { + mType = ASMIT_TAX; + mMode = ASMIM_IMPLIED; + + data.Set(CPU_REG_X, data[CPU_REG_A]); + changed = true; + } + else + { + data.Set(CPU_REG_X, NRDM_ABSOLUTE, mAddress, mLinkerObject, mFlags); + } + } + else + data.Reset(CPU_REG_X); + break; + + case ASMIT_STX: + if (!(mFlags & NCIF_VOLATILE) && mLinkerObject) + { + if (data[CPU_REG_X].mMode == NRDM_ABSOLUTE && data[CPU_REG_X].mLinkerObject == mLinkerObject && data[CPU_REG_X].mValue == mAddress) + { + mType = ASMIT_NOP; + mMode = ASMIM_IMPLIED; + changed = true; + } + else if (data[CPU_REG_X].mMode == NRDM_UNKNOWN) + { + data.ResetAbsolute(mLinkerObject, mAddress); + data.Set(CPU_REG_X, NRDM_ABSOLUTE, mAddress, mLinkerObject, mFlags); + } + else + data.ResetAbsolute(mLinkerObject, mAddress); + } + else + data.ResetAbsolute(mLinkerObject, mAddress); + break; + + default: + if (ChangesAddress()) + data.ResetAbsolute(mLinkerObject, mAddress); + } + } + + return changed; +} + +void NativeCodeInstruction::FilterRegUsage(NumberSet& requiredTemps, NumberSet& providedTemps) +{ + // check runtime calls + + if (mType == ASMIT_JSR) + { +#if 1 + if (mFlags & NCIF_USE_CPU_REG_A) + { + if (!providedTemps[CPU_REG_A]) + requiredTemps += CPU_REG_A; + } + if (mFlags & NCIF_USE_CPU_REG_X) + { + if (!providedTemps[CPU_REG_X]) + requiredTemps += CPU_REG_X; + } + if (mFlags & NCIF_USE_CPU_REG_Y) + { + if (!providedTemps[CPU_REG_Y]) + requiredTemps += CPU_REG_Y; + } + if (mFlags & NCIF_USE_CPU_REG_C) + { + if (!providedTemps[CPU_REG_C]) + requiredTemps += CPU_REG_C; + } + + if (mFlags & NCIF_RUNTIME) + { + for (int i = 0; i < 4; i++) + { + if (!providedTemps[BC_REG_ACCU + i]) + requiredTemps += BC_REG_ACCU + i; + if (!providedTemps[BC_REG_WORK + i]) + requiredTemps += BC_REG_WORK + i; + } + if (mFlags & NCIF_USE_ZP_32_X) + { + for (int i = 0; i < 4; i++) + { + if (!providedTemps[mParam + i]) + requiredTemps += mParam + i; + } + } + + if (mFlags & NCIF_FEXEC) + { + for (int i = BC_REG_FPARAMS; i < BC_REG_FPARAMS_END; i++) + if (!providedTemps[i]) + requiredTemps += i; + } + } + else if (mFlags & NICF_USE_WORKREGS) + { + for (int i = 0; i < 10; i++) + if (!providedTemps[BC_REG_WORK + i]) + requiredTemps += BC_REG_WORK + i; + } + else + { + if (mLinkerObject) + { + for (int i = 0; i < mLinkerObject->mNumTemporaries; i++) + { + for (int j = 0; j < mLinkerObject->mTempSizes[i]; j++) + { + if (!providedTemps[mLinkerObject->mTemporaries[i] + j]) + requiredTemps += mLinkerObject->mTemporaries[i] + j; + } + } + } + } +#endif + providedTemps += BC_REG_ADDR + 0; + providedTemps += BC_REG_ADDR + 1; + + for (int i = 0; i < 4; i++) + providedTemps += BC_REG_ACCU + i; + for (int i = 0; i < 8; i++) + providedTemps += BC_REG_WORK + i; + + if (!(mFlags & NCIF_PRESERVE_CPU_REG_A)) + providedTemps += CPU_REG_A; + if (!(mFlags & NCIF_PRESERVE_CPU_REG_X)) + providedTemps += CPU_REG_X; + if (!(mFlags & NCIF_PRESERVE_CPU_REG_Y)) + providedTemps += CPU_REG_Y; + providedTemps += CPU_REG_C; + providedTemps += CPU_REG_Z; + return; + } + + if (mType == ASMIT_RTS) + { +#if 1 + if (mFlags & NCIF_USE_CPU_REG_A) + { + if (!providedTemps[CPU_REG_A]) + requiredTemps += CPU_REG_A; + } + + if (mFlags & NCIF_LOWER) + { + if (!providedTemps[BC_REG_ACCU + 0]) requiredTemps += BC_REG_ACCU + 0; + + if (mFlags & NCIF_UPPER) + { + if (!providedTemps[BC_REG_ACCU + 1]) requiredTemps += BC_REG_ACCU + 1; + + if (mFlags & NCIF_LONG) + { + if (!providedTemps[BC_REG_ACCU + 2]) requiredTemps += BC_REG_ACCU + 2; + if (!providedTemps[BC_REG_ACCU + 3]) requiredTemps += BC_REG_ACCU + 3; + } + } + } +#endif +#if 0 + for (int i = 0; i < 4; i++) + { + if (!providedTemps[BC_REG_ACCU + i]) + requiredTemps += BC_REG_ACCU + i; + } +#endif + if (!providedTemps[BC_REG_STACK]) + requiredTemps += BC_REG_STACK; + if (!providedTemps[BC_REG_STACK + 1]) + requiredTemps += BC_REG_STACK + 1; + if (!providedTemps[BC_REG_LOCALS]) + requiredTemps += BC_REG_LOCALS; + if (!providedTemps[BC_REG_LOCALS + 1]) + requiredTemps += BC_REG_LOCALS + 1; + + return; + } + + // check index + + switch (mMode) + { + case ASMIM_ZERO_PAGE_X: + case ASMIM_INDIRECT_X: + case ASMIM_ABSOLUTE_X: + if (!providedTemps[CPU_REG_X]) + requiredTemps += CPU_REG_X; + break; + + case ASMIM_ZERO_PAGE_Y: + case ASMIM_ABSOLUTE_Y: + if (!providedTemps[CPU_REG_Y]) + requiredTemps += CPU_REG_Y; + break; + + case ASMIM_INDIRECT_Y: + if (!providedTemps[CPU_REG_Y]) + requiredTemps += CPU_REG_Y; + if (!providedTemps[mAddress]) + requiredTemps += mAddress; + if (!providedTemps[mAddress + 1]) + requiredTemps += mAddress + 1; + break; + } + + // check carry flags + + switch (mType) + { + case ASMIT_ADC: + case ASMIT_SBC: + case ASMIT_ROL: + case ASMIT_ROR: + if (!providedTemps[CPU_REG_C]) + requiredTemps += CPU_REG_C; + providedTemps += CPU_REG_C; + break; + case ASMIT_CMP: + case ASMIT_ASL: + case ASMIT_LSR: + case ASMIT_CPX: + case ASMIT_CPY: + case ASMIT_CLC: + case ASMIT_SEC: + providedTemps += CPU_REG_C; + break; + case ASMIT_BCC: + case ASMIT_BCS: + if (!providedTemps[CPU_REG_C]) + requiredTemps += CPU_REG_C; + break; + case ASMIT_BEQ: + case ASMIT_BNE: + case ASMIT_BPL: + case ASMIT_BMI: + if (!providedTemps[CPU_REG_Z]) + requiredTemps += CPU_REG_Z; + break; + } + + // check zero flag + + switch (mType) + { + case ASMIT_ADC: + case ASMIT_SBC: + case ASMIT_ROL: + case ASMIT_ROR: + case ASMIT_INC: + case ASMIT_DEC: + case ASMIT_CMP: + case ASMIT_CPX: + case ASMIT_CPY: + case ASMIT_ASL: + case ASMIT_LSR: + case ASMIT_ORA: + case ASMIT_EOR: + case ASMIT_AND: + case ASMIT_LDA: + case ASMIT_LDX: + case ASMIT_LDY: + case ASMIT_BIT: + case ASMIT_TAX: + case ASMIT_TXA: + case ASMIT_TAY: + case ASMIT_TYA: + case ASMIT_DEY: + case ASMIT_INY: + case ASMIT_DEX: + case ASMIT_INX: + providedTemps += CPU_REG_Z; + break; + } + + // check CPU register + + switch (mType) + { + case ASMIT_ROL: + case ASMIT_ROR: + case ASMIT_ASL: + case ASMIT_LSR: + if (mMode == ASMIM_IMPLIED) + { + if (!providedTemps[CPU_REG_A]) + requiredTemps += CPU_REG_A; + providedTemps += CPU_REG_A; + } + break; + + case ASMIT_LDA: + providedTemps += CPU_REG_A; + break; + + case ASMIT_CMP: + case ASMIT_STA: + if (!providedTemps[CPU_REG_A]) + requiredTemps += CPU_REG_A; + break; + case ASMIT_CPX: + case ASMIT_STX: + if (!providedTemps[CPU_REG_X]) + requiredTemps += CPU_REG_X; + break; + case ASMIT_CPY: + case ASMIT_STY: + if (!providedTemps[CPU_REG_Y]) + requiredTemps += CPU_REG_Y; + break; + + case ASMIT_ADC: + case ASMIT_SBC: + case ASMIT_ORA: + case ASMIT_EOR: + case ASMIT_AND: + if (!providedTemps[CPU_REG_A]) + requiredTemps += CPU_REG_A; + providedTemps += CPU_REG_A; + break; + case ASMIT_LDX: + providedTemps += CPU_REG_X; + break; + case ASMIT_INX: + case ASMIT_DEX: + if (!providedTemps[CPU_REG_X]) + requiredTemps += CPU_REG_X; + providedTemps += CPU_REG_X; + break; + case ASMIT_LDY: + providedTemps += CPU_REG_Y; + break; + case ASMIT_INY: + case ASMIT_DEY: + if (!providedTemps[CPU_REG_Y]) + requiredTemps += CPU_REG_Y; + providedTemps += CPU_REG_Y; + break; + + case ASMIT_TAX: + if (!providedTemps[CPU_REG_A]) + requiredTemps += CPU_REG_A; + providedTemps += CPU_REG_X; + break; + case ASMIT_TAY: + if (!providedTemps[CPU_REG_A]) + requiredTemps += CPU_REG_A; + providedTemps += CPU_REG_Y; + break; + case ASMIT_TXA: + if (!providedTemps[CPU_REG_X]) + requiredTemps += CPU_REG_X; + providedTemps += CPU_REG_A; + break; + case ASMIT_TYA: + if (!providedTemps[CPU_REG_Y]) + requiredTemps += CPU_REG_Y; + providedTemps += CPU_REG_A; + break; + } + + if (mMode == ASMIM_ZERO_PAGE) + { + switch (mType) + { + case ASMIT_STA: + case ASMIT_STX: + case ASMIT_STY: + providedTemps += mAddress; + break; + case ASMIT_ROL: + case ASMIT_ROR: + case ASMIT_ASL: + case ASMIT_LSR: + case ASMIT_INC: + case ASMIT_DEC: + if (!providedTemps[mAddress]) + requiredTemps += mAddress; + providedTemps += mAddress; + break; + default: + if (!providedTemps[mAddress]) + requiredTemps += mAddress; + } + } +} + +uint32 NativeCodeInstruction::CodeHash(void) const +{ + uint32 hash = mType + 137 * mMode + 4111 * mAddress; + if (mLinkerObject) + hash += mLinkerObject->mID * 135123; + hash ^= hash >> 13; + hash ^= hash << 11; + hash ^= hash >> 23; + return hash; +} + +bool NativeCodeInstruction::CodeSame(const NativeCodeInstruction& ins) +{ + if (mType != ins.mType || mMode != ins.mMode) + return false; + if (mMode != ASMIM_IMPLIED && (mAddress != ins.mAddress || mLinkerObject != ins.mLinkerObject)) + return false; + if (mMode == ASMIM_IMMEDIATE_ADDRESS && (mFlags & (NCIF_LOWER | NCIF_UPPER)) != (ins.mFlags & (NCIF_LOWER | NCIF_UPPER))) + return false; + if ((mFlags & NCIF_USE_ZP_32_X) && mParam != ins.mParam) + return false; + + return true; +} + +void NativeCodeInstruction::CopyMode(const NativeCodeInstruction& ins) +{ + mMode = ins.mMode; + mAddress = ins.mAddress; + mLinkerObject = ins.mLinkerObject; + mFlags = (mFlags & ~(NCIF_LOWER | NCIF_UPPER)) | (ins.mFlags & (NCIF_LOWER | NCIF_UPPER | NCIF_VOLATILE)); +} + +void NativeCodeInstruction::CopyModeAndRange(const NativeCodeInstruction& ins) +{ + mMode = ins.mMode; + mAddress = ins.mAddress; + mLinkerObject = ins.mLinkerObject; + mFlags = (mFlags & ~(NCIF_LOWER | NCIF_UPPER)) | (ins.mFlags & (NCIF_LOWER | NCIF_UPPER | NCIF_VOLATILE)); + mMinVal = ins.mMinVal; + mMaxVal = ins.mMaxVal; +} + +void NativeCodeInstruction::Assemble(NativeCodeBasicBlock* block) +{ + bool weak = true; + + if (mIns) + { + if (ChangesAddress() && (mMode != ASMIM_ZERO_PAGE || mLinkerObject)) + weak = false; + else if (mType == ASMIT_JSR && !(mFlags & NCIF_RUNTIME)) + weak = false; + block->PutLocation(mIns->mLocation, weak); + } + + if (mType == ASMIT_BYTE) + block->PutByte(mAddress); + else if (mType == ASMIT_JSR && (mFlags & NCIF_BREAKPOINT)) + { + LinkerReference rl; + rl.mOffset = block->mCode.Size(); + + rl.mRefObject = nullptr; + rl.mRefOffset = 0; + rl.mFlags = LREF_BREAKPOINT; + + block->mRelocations.Push(rl); + block->PutByte(0xEA); + } + else if (mType == ASMIT_JSR && mLinkerObject && (mLinkerObject->mFlags & LOBJF_INLINE)) + { + int pos = block->mCode.Size(); + int size = mLinkerObject->mSize; + + // skip RTS on embedding + if (mLinkerObject->mData[size - 1] == 0x60) + size--; + + for (int i = 0; i < size; i++) + block->PutByte(mLinkerObject->mData[i]); + for (int i = 0; i < mLinkerObject->mReferences.Size(); i++) + { + LinkerReference rl = *(mLinkerObject->mReferences[i]); + if (rl.mFlags & LREF_TEMPORARY) + { + block->mCode[pos + rl.mOffset] += mLinkerObject->mTemporaries[rl.mRefOffset]; + } + else + { + rl.mOffset += pos; + if (rl.mRefObject == rl.mObject) + { + rl.mRefObject = nullptr; + rl.mRefOffset += pos; + rl.mFlags |= LREF_INBLOCK; + } + + block->mRelocations.Push(rl); + } + } + } + else + { + if ((mType == ASMIT_JSR || mType == ASMIT_JMP) && (mFlags & NCIF_USE_ZP_32_X)) + { + block->PutOpcode(AsmInsOpcodes[ASMIT_LDX][ASMIM_IMMEDIATE]); + block->PutByte(mParam); + } + + AsmInsMode mode = mMode; + + if (mode == ASMIM_ABSOLUTE && !mLinkerObject && mAddress < 256 && HasAsmInstructionMode(mType, ASMIM_ZERO_PAGE)) + mode = ASMIM_ZERO_PAGE; + else if (mode == ASMIM_ABSOLUTE_X && !mLinkerObject && mAddress < 256 && HasAsmInstructionMode(mType, ASMIM_ZERO_PAGE_X)) + mode = ASMIM_ZERO_PAGE_X; + else if (mode == ASMIM_ABSOLUTE_Y && !mLinkerObject && mAddress < 256 && HasAsmInstructionMode(mType, ASMIM_ZERO_PAGE_Y)) + mode = ASMIM_ZERO_PAGE_Y; + else if (mode == ASMIM_ABSOLUTE && mLinkerObject && (mLinkerObject->mFlags & LOBJF_ZEROPAGE) && HasAsmInstructionMode(mType, ASMIM_ZERO_PAGE)) + mode = ASMIM_ZERO_PAGE; + else if (mode == ASMIM_ABSOLUTE_X && mLinkerObject && (mLinkerObject->mFlags & LOBJF_ZEROPAGE) && HasAsmInstructionMode(mType, ASMIM_ZERO_PAGE_X)) + mode = ASMIM_ZERO_PAGE_X; + else if (mode == ASMIM_ABSOLUTE_Y && mLinkerObject && (mLinkerObject->mFlags & LOBJF_ZEROPAGE) && HasAsmInstructionMode(mType, ASMIM_ZERO_PAGE_Y)) + mode = ASMIM_ZERO_PAGE_Y; + + if (mode == ASMIM_IMMEDIATE_ADDRESS) + { + assert((mFlags & (NCIF_LOWER | NCIF_UPPER)) != (NCIF_LOWER | NCIF_UPPER)); + assert(HasAsmInstructionMode(mType, ASMIM_IMMEDIATE)); + block->PutOpcode(AsmInsOpcodes[mType][ASMIM_IMMEDIATE]); + } + else + { + assert(HasAsmInstructionMode(mType, mode)); + block->PutOpcode(AsmInsOpcodes[mType][mode]); + } + + switch (mode) + { + case ASMIM_IMPLIED: + break; + case ASMIM_ZERO_PAGE: + case ASMIM_ZERO_PAGE_X: + case ASMIM_ZERO_PAGE_Y: + case ASMIM_INDIRECT_X: + case ASMIM_INDIRECT_Y: + if (mLinkerObject) + { + LinkerReference rl; + rl.mOffset = block->mCode.Size(); + + rl.mRefObject = mLinkerObject; + rl.mRefOffset = mAddress; + rl.mFlags = LREF_LOWBYTE; + + block->mRelocations.Push(rl); + block->PutByte(0); + } + else + { + if (mAddress == 0 && !(mFlags & NCIF_VOLATILE)) + block->mProc->mGenerator->mErrors->Error(mIns->mLocation, EWARN_NULL_POINTER_DEREFERENCED, "nullptr dereferenced"); + block->PutByte(uint8(mAddress)); + } + break; + case ASMIM_IMMEDIATE: + block->PutByte(uint8(mAddress)); + break; + case ASMIM_IMMEDIATE_ADDRESS: + if (mLinkerObject) + { + LinkerReference rl; + rl.mOffset = block->mCode.Size(); + rl.mFlags = 0; + if (mFlags & NCIF_LOWER) + rl.mFlags |= LREF_LOWBYTE; + if (mFlags & NCIF_UPPER) + rl.mFlags |= LREF_HIGHBYTE; + rl.mRefObject = mLinkerObject; + + rl.mRefObject = mLinkerObject; + rl.mRefOffset = mAddress; + + if (mLinkerObject) + mLinkerObject->mFlags |= LOBJF_NO_CROSS; + + block->mRelocations.Push(rl); + block->PutByte(0); + } + else + { + block->PutByte(uint8(mAddress)); + } + break; + case ASMIM_ABSOLUTE: + case ASMIM_INDIRECT: + case ASMIM_ABSOLUTE_X: + case ASMIM_ABSOLUTE_Y: + if (mLinkerObject) + { + if (mFlags & NCIF_SELFMOD) + { + block->PutSelfModSource(mAddress); + block->PutWord(0); + } + else + { + int w = 0; + + LinkerReference rl; + rl.mOffset = block->mCode.Size(); + + rl.mRefObject = mLinkerObject; + if (mType != ASMIT_JSR && mType != ASMIT_JSR && mMode == ASMIM_ABSOLUTE && mLinkerObject == block->mProc->mLinkerObject) + { + rl.mFlags = LREF_SELFMOD | LREF_LOWBYTE | LREF_HIGHBYTE; + rl.mRefOffset = mAddress; + } + else + { + if (mFlags & NCIF_LOWER) + { + rl.mFlags = LREF_LOWBYTE | LREF_HIGHBYTE; + rl.mRefOffset = mAddress; + } + else + { + rl.mFlags = LREF_HIGHBYTE; + rl.mOffset++; + rl.mRefOffset = mAddress & 0xff00; + + w = mAddress & 0xff; + } + } + + if (mode != ASMIM_ABSOLUTE) + mLinkerObject->mFlags |= LOBJF_NO_CROSS; + + block->mRelocations.Push(rl); + block->PutWord(w); + } + } + else + { + if (mAddress == 0 && !(mFlags & NCIF_VOLATILE)) + block->mProc->mGenerator->mErrors->Error(mIns->mLocation, EWARN_NULL_POINTER_DEREFERENCED, "nullptr dereferenced"); + block->PutWord(uint16(mAddress)); + } + break; + case ASMIM_RELATIVE: + block->PutByte(uint8(mAddress)); + break; + } + } + + if (mIns) + block->PutLocation(mIns->mLocation, weak); +} + +void NativeCodeBasicBlock::PutLocation(const Location& location, bool weak) +{ + int sz = mCodeLocations.Size(); + if (sz > 0 && + mCodeLocations[sz - 1].mLocation.mFileName == location.mFileName && + mCodeLocations[sz - 1].mLocation.mLine == location.mLine) + { + mCodeLocations[sz - 1].mEnd = this->mCode.Size(); + if (mCodeLocations[sz - 1].mWeak) + mCodeLocations[sz - 1].mWeak = weak; + } + else if (sz > 0 && mCodeLocations[sz - 1].mWeak) + { + mCodeLocations[sz - 1].mLocation = location; + mCodeLocations[sz - 1].mEnd = this->mCode.Size(); + mCodeLocations[sz - 1].mWeak = weak; + } + else + { + CodeLocation loc; + loc.mLocation = location; + loc.mStart = loc.mEnd = this->mCode.Size(); + loc.mWeak = weak; + mCodeLocations.Push(loc); + } + + sz = mCodeOrigins.Size(); + if (sz > 0 && + mCodeOrigins[sz - 1].mLocation.mFileName == location.mFileName && + mCodeOrigins[sz - 1].mLocation.mLine == location.mLine) + { + mCodeOrigins[sz - 1].mEnd = this->mCode.Size(); + } + else + { + CodeLocation loc; + loc.mLocation = location; + loc.mStart = loc.mEnd = this->mCode.Size(); + mCodeOrigins.Push(loc); + } +} + +void NativeCodeBasicBlock::PutSelfModSource(int id) +{ + mProc->mSelfModSources[id] = SelfModReference{ this, this->mCode.Size() }; +} + +void NativeCodeBasicBlock::PutOpcode(short opcode) +{ + assert(opcode >= 0 && opcode < 256); + this->mCode.Push(uint8(opcode)); +} + +void NativeCodeBasicBlock::PutByte(uint8 code) +{ + this->mCode.Push(code); +} + +void NativeCodeBasicBlock::PutWord(uint16 code) +{ + this->mCode.Push((uint8)(code & 0xff)); + this->mCode.Push((uint8)(code >> 8)); +} + +static AsmInsType InvertBranchCondition(AsmInsType code) +{ + switch (code) + { + case ASMIT_BEQ: return ASMIT_BNE; + case ASMIT_BNE: return ASMIT_BEQ; + case ASMIT_BPL: return ASMIT_BMI; + case ASMIT_BMI: return ASMIT_BPL; + case ASMIT_BCS: return ASMIT_BCC; + case ASMIT_BCC: return ASMIT_BCS; + default: + return code; + } +} + +static AsmInsType TransposeBranchCondition(AsmInsType code) +{ + switch (code) + { + case ASMIT_BEQ: return ASMIT_BEQ; + case ASMIT_BNE: return ASMIT_BNE; + case ASMIT_BPL: return ASMIT_BMI; + case ASMIT_BMI: return ASMIT_BPL; + case ASMIT_BCS: return ASMIT_BCC; + case ASMIT_BCC: return ASMIT_BCS; + default: + return code; + } +} + + +int NativeCodeBasicBlock::PutJump(NativeCodeProcedure* proc, NativeCodeBasicBlock* target, int from, int to, AsmInsType code) +{ + if (target->mIns.Size() == 1 && target->mIns[0].mType == ASMIT_RTS) + { + if (mIns.Size() > 0 && mIns.Last().IsSimpleJSR()) + { + this->mCode[this->mCode.Size() - 3] = 0x4c; + return 0; + } + else + { + PutByte(0x60); + return 1; + } + } + else if (target->mIns.Size() == 2 && target->mIns[0].IsSimpleJSR() && target->mIns[1].mType == ASMIT_RTS) + { + PutByte(0x4c); + + LinkerReference rl; + rl.mObject = nullptr; + rl.mOffset = mCode.Size(); + rl.mFlags = LREF_LOWBYTE | LREF_HIGHBYTE; + rl.mRefObject = target->mIns[0].mLinkerObject; + rl.mRefOffset = target->mIns[0].mAddress; + mRelocations.Push(rl); + + PutWord(0); + return 3; + } + else if (to - from >= -126 && to - from <= 129) + { + if (code != ASMIT_INV) + { + PutOpcode(AsmInsOpcodes[InvertBranchCondition(code)][ASMIM_RELATIVE]); + PutByte(to - from - 2); + return 2; + } +#if JUMP_TO_BRANCH + else if (mNDataSet[CPU_REG_C].mMode == NRDM_IMMEDIATE) + { + if (mNDataSet[CPU_REG_C].mValue) + PutOpcode(AsmInsOpcodes[ASMIT_BCS][ASMIM_RELATIVE]); + else + PutOpcode(AsmInsOpcodes[ASMIT_BCC][ASMIM_RELATIVE]); + + PutByte(to - from - 2); + return 2; + } + else if (mNDataSet[CPU_REG_Z].mMode == NRDM_IMMEDIATE) + { + if (mNDataSet[CPU_REG_Z].mValue) + PutOpcode(AsmInsOpcodes[ASMIT_BNE][ASMIM_RELATIVE]); + else + PutOpcode(AsmInsOpcodes[ASMIT_BEQ][ASMIM_RELATIVE]); + + PutByte(to - from - 2); + return 2; + } +#endif + } + + target->mAsmFromJump = from; + + if (target->mCode.Size() == 2 && target->mRelocations.Size() == 0 && !target->mFalseJump && target->mTrueJump && target->mTrueJump->mIns.Size() == 1 && target->mTrueJump->mIns[0].mType == ASMIT_RTS) + { + PutByte(target->mCode[0]); + PutByte(target->mCode[1]); + PutByte(0x60); + return 3; + } + + PutByte(0x4c); + + LinkerReference rl; + rl.mObject = nullptr; + rl.mOffset = mCode.Size(); + rl.mFlags = LREF_LOWBYTE | LREF_HIGHBYTE; + rl.mRefObject = nullptr; + rl.mRefOffset = target->mOffset; + mRelocations.Push(rl); + + PutWord(0); + return 3; +} + +int NativeCodeBasicBlock::CheckFinalBranchByteSize(NativeCodeBasicBlock* target, int from, int to) const +{ + if (to - from >= -126 && to - from <= 129) + return 2; + else + { + if (target->mIns.Size() == 1 && target->mIns[0].mType == ASMIT_RTS) + return 3; +#if REYCLE_JUMPS + if (target->mAsmFromJump >= 0 && target->mAsmFromJump - from >= -126) + return 2; +#endif + return 5; + } +} + +int NativeCodeBasicBlock::BranchByteSize(NativeCodeBasicBlock* target, int from, int to, bool final) const +{ + if (to - from >= -126 && to - from <= 129) + return 2; + else + { + if (target->mIns.Size() == 1 && target->mIns[0].mType == ASMIT_RTS) + return 3; + if (final) + { +#if REYCLE_JUMPS + if (target->mAsmFromJump >= 0 && target->mAsmFromJump - from >= -126) + return 2; +#endif + target->mAsmFromJump = from + 2; + return 5; + } + else + { + return 5; + } + } +} + +int NativeCodeBasicBlock::JumpByteSize(NativeCodeBasicBlock* target, int from, int to, bool second, bool final) +{ + if (target->mIns.Size() == 1 && target->mIns[0].mType == ASMIT_RTS) + { + if (mIns.Size() > 0 && mIns.Last().IsSimpleJSR()) + return 0; + else + return 1; + } + else if (target->mIns.Size() == 2 && target->mIns[0].IsSimpleJSR() && target->mIns[1].mType == ASMIT_RTS) + { + return 3; + } + else if (to - from >= -126 && to - from <= 129) + { + if (second) + return 2; +#if JUMP_TO_BRANCH + else if (mNDataSet[CPU_REG_C].mMode == NRDM_IMMEDIATE) + return 2; + else if (mNDataSet[CPU_REG_Z].mMode == NRDM_IMMEDIATE) + return 2; +#endif + else + { + if (final) + target->mAsmFromJump = from; + return 3; + } + } + else + { + if (final) + target->mAsmFromJump = from; + + return 3; + } +} + + +int NativeCodeBasicBlock::PutBranch(NativeCodeProcedure* proc, NativeCodeBasicBlock* target, AsmInsType code, int from, int to) +{ + if (to - from >= -126 && to - from <= 129) + { + PutOpcode(AsmInsOpcodes[code][ASMIM_RELATIVE]); + PutByte(to - from - 2); + return 2; + } +#if REYCLE_JUMPS + else if (target->mAsmFromJump >= 0 && target->mAsmFromJump - from >= -126) + { + PutOpcode(AsmInsOpcodes[code][ASMIM_RELATIVE]); + PutByte(target->mAsmFromJump - from - 2); + return 2; + } +#endif + else + { + PutOpcode(AsmInsOpcodes[InvertBranchCondition(code)][ASMIM_RELATIVE]); + + if (target->mIns.Size() == 1 && target->mIns[0].mType == ASMIT_RTS) + { + PutByte(1); + PutByte(0x60); + return 3; + } + else + { + PutByte(3); + + target->mAsmFromJump = from + 2; + + PutByte(0x4c); + + LinkerReference rl; + rl.mObject = nullptr; + rl.mOffset = mCode.Size(); + rl.mFlags = LREF_LOWBYTE | LREF_HIGHBYTE; + rl.mRefObject = nullptr; + rl.mRefOffset = to; + mRelocations.Push(rl); + + PutWord(0); + return 5; + } + } +} + +void NativeCodeBasicBlock::LoadConstantToReg(InterCodeProcedure* proc, const InterInstruction* ins, const InterOperand& op, InterType type, int reg, bool checkRange) +{ + + if (type == IT_FLOAT) + { + union { float f; unsigned int v; } cc; + cc.f = float(op.mFloatConst); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, cc.v & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (cc.v >> 8) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (cc.v >> 16) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (cc.v >> 24) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg + 3)); + } + else if (type == IT_POINTER) + { + if (op.mMemory == IM_GLOBAL) + { + if (checkRange && (op.mIntConst < 0 || op.mIntConst > op.mLinkerObject->mSize)) + proc->mModule->mErrors->Error(ins->mLocation, EWARN_UNDEFINED_POINTER_ARITHMETIC, "Undefined constant pointer arithmetic"); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE_ADDRESS, op.mIntConst, op.mLinkerObject, NCIF_LOWER)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE_ADDRESS, op.mIntConst, op.mLinkerObject, NCIF_UPPER)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg + 1)); + } + else if (op.mMemory == IM_ABSOLUTE) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, op.mIntConst & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (op.mIntConst >> 8) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg + 1)); + } + else if (op.mMemory == IM_FPARAM || op.mMemory == IM_FFRAME) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, BC_REG_FPARAMS + op.mVarIndex + op.mIntConst)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg + 1)); + } + else if (op.mMemory == IM_FRAME) + { + int index = op.mVarIndex + int(op.mIntConst) + 2; + + mIns.Push(NativeCodeInstruction(ins, ASMIT_CLC, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_STACK)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ADC, ASMIM_IMMEDIATE, index & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_STACK + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ADC, ASMIM_IMMEDIATE, (index >> 8) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg + 1)); + } + else if (op.mMemory == IM_LOCAL || op.mMemory == IM_PARAM) + { + int index = int(op.mIntConst); + int areg = mNoFrame ? BC_REG_STACK : BC_REG_LOCALS; + if (op.mMemory == IM_LOCAL) + index += proc->mLocalVars[op.mVarIndex]->mOffset; + else + index += op.mVarIndex + proc->mLocalSize + 2; + index += mFrameOffset; + CheckFrameIndex(ins, areg, index, 2); + + if (index != 0) + mIns.Push(NativeCodeInstruction(ins, ASMIT_CLC, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, areg)); + if (index != 0) + mIns.Push(NativeCodeInstruction(ins, ASMIT_ADC, ASMIM_IMMEDIATE, index & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, areg + 1)); + if (index != 0) + mIns.Push(NativeCodeInstruction(ins, ASMIT_ADC, ASMIM_IMMEDIATE, (index >> 8) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg + 1)); + } + else if (op.mMemory == IM_PROCEDURE) + { + NativeCodeInstruction lins(ins, ASMIT_LDA, ASMIM_IMMEDIATE_ADDRESS, op.mIntConst, op.mLinkerObject, NCIF_LOWER); + NativeCodeInstruction hins(ins, ASMIT_LDA, ASMIM_IMMEDIATE_ADDRESS, op.mIntConst, op.mLinkerObject, NCIF_UPPER); + + mIns.Push(lins); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg)); + mIns.Push(hins); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg + 1)); + } + } + else if (type == IT_INT32) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, op.mIntConst & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (op.mIntConst >> 8) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (op.mIntConst >> 16) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (op.mIntConst >> 24) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg + 3)); + } + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, op.mIntConst & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg)); + if (InterTypeSize[type] > 1) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (op.mIntConst >> 8) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg + 1)); + } + } +} + +void NativeCodeBasicBlock::LoadConstantToReg(InterCodeProcedure * proc, const InterInstruction * ins, InterType type, int reg) +{ + LoadConstantToReg(proc, ins, ins->mConst, type, reg); +} + +void NativeCodeBasicBlock::LoadConstant(InterCodeProcedure* proc, const InterInstruction * ins) +{ + LoadConstantToReg(proc, ins, ins->mDst.mType, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]); +} + +void NativeCodeBasicBlock::CheckFrameIndex(const InterInstruction* ins, int& reg, int& index, int size, int treg) +{ + if (index < 0 || index + size > 256) + { + if (treg == 0) + treg = BC_REG_ADDR; + mIns.Push(NativeCodeInstruction(ins, ASMIT_CLC, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, reg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ADC, ASMIM_IMMEDIATE, index & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, reg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ADC, ASMIM_IMMEDIATE, (index >> 8) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + index = 0; + reg = treg; + } +} + +void NativeCodeBasicBlock::StoreValue(InterCodeProcedure* proc, const InterInstruction * ins) +{ + uint32 flags = NCIF_LOWER | NCIF_UPPER; + if (ins->mVolatile) + flags |= NCIF_VOLATILE; + if (ins->mMemmap) + flags |= NCIF_MEMMAP; + if (ins->mAliasing) + flags |= NCIF_ALIASING; + + if (ins->mSrc[0].mType == IT_FLOAT) + { + int stride = ins->mSrc[1].mStride; + + if (ins->mSrc[1].mTemp < 0) + { + if (ins->mSrc[0].mTemp < 0) + { + union { float f; unsigned int v; } cc; + cc.f = float(ins->mSrc[0].mFloatConst); + + if (ins->mSrc[1].mMemory == IM_GLOBAL) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, cc.v & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ABSOLUTE, ins->mSrc[1].mIntConst, ins->mSrc[1].mLinkerObject, flags)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (cc.v >> 8) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ABSOLUTE, ins->mSrc[1].mIntConst + 1 * stride, ins->mSrc[1].mLinkerObject, flags)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (cc.v >> 16) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ABSOLUTE, ins->mSrc[1].mIntConst + 2 * stride, ins->mSrc[1].mLinkerObject, flags)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (cc.v >> 24) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ABSOLUTE, ins->mSrc[1].mIntConst + 3 * stride, ins->mSrc[1].mLinkerObject, flags)); + } + else if (ins->mSrc[1].mMemory == IM_ABSOLUTE) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, cc.v & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ABSOLUTE, ins->mSrc[1].mIntConst, nullptr, flags)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (cc.v >> 8) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ABSOLUTE, ins->mSrc[1].mIntConst + 1 * stride, nullptr, flags)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (cc.v >> 16) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ABSOLUTE, ins->mSrc[1].mIntConst + 2 * stride, nullptr, flags)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (cc.v >> 24) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ABSOLUTE, ins->mSrc[1].mIntConst + 3 * stride, nullptr, flags)); + } + else if (ins->mSrc[1].mMemory == IM_FPARAM || ins->mSrc[1].mMemory == IM_FFRAME) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, cc.v & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_FPARAMS + ins->mSrc[1].mVarIndex + ins->mSrc[1].mIntConst)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (cc.v >> 8) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_FPARAMS + ins->mSrc[1].mVarIndex + ins->mSrc[1].mIntConst + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (cc.v >> 16) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_FPARAMS + ins->mSrc[1].mVarIndex + ins->mSrc[1].mIntConst + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (cc.v >> 24) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_FPARAMS + ins->mSrc[1].mVarIndex + ins->mSrc[1].mIntConst + 3)); + } + else if (ins->mSrc[1].mMemory == IM_LOCAL || ins->mSrc[1].mMemory == IM_PARAM) + { + int index = int(ins->mSrc[1].mIntConst); + int reg = mNoFrame ? BC_REG_STACK : BC_REG_LOCALS; + if (ins->mSrc[1].mMemory == IM_LOCAL) + index += proc->mLocalVars[ins->mSrc[1].mVarIndex]->mOffset; + else + index += ins->mSrc[1].mVarIndex + proc->mLocalSize + 2; + index += mFrameOffset; + CheckFrameIndex(ins, reg, index, 4); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, cc.v & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_INDIRECT_Y, reg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (cc.v >> 8) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_INDIRECT_Y, reg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (cc.v >> 16) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_INDIRECT_Y, reg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index + 3)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (cc.v >> 24) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_INDIRECT_Y, reg)); + } + else if (ins->mSrc[1].mMemory == IM_FRAME) + { + int index = int(ins->mSrc[1].mVarIndex + ins->mSrc[1].mIntConst + 2); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, cc.v & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_INDIRECT_Y, BC_REG_STACK)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (cc.v >> 8) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_INDIRECT_Y, BC_REG_STACK)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (cc.v >> 16) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_INDIRECT_Y, BC_REG_STACK)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index + 3)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (cc.v >> 24) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_INDIRECT_Y,BC_REG_STACK)); + } + } + else + { + int sreg = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + + if (ins->mSrc[0].mFinal && CheckPredAccuStore(sreg)) + { + // cull previous store from accu to temp using direcrt forwarding from accu + mIns.SetSize(mIns.Size() - 8); + sreg = BC_REG_ACCU; + } + + if (ins->mSrc[1].mMemory == IM_GLOBAL) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ABSOLUTE, ins->mSrc[1].mIntConst, ins->mSrc[1].mLinkerObject, flags)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ABSOLUTE, ins->mSrc[1].mIntConst + 1 * stride, ins->mSrc[1].mLinkerObject, flags)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ABSOLUTE, ins->mSrc[1].mIntConst + 2 * stride, ins->mSrc[1].mLinkerObject, flags)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 3)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ABSOLUTE, ins->mSrc[1].mIntConst + 3 * stride, ins->mSrc[1].mLinkerObject, flags)); + } + else if (ins->mSrc[1].mMemory == IM_ABSOLUTE) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ABSOLUTE, ins->mSrc[1].mIntConst, nullptr, flags)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ABSOLUTE, ins->mSrc[1].mIntConst + 1 * stride, nullptr, flags)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ABSOLUTE, ins->mSrc[1].mIntConst + 2 * stride, nullptr, flags)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 3)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ABSOLUTE, ins->mSrc[1].mIntConst + 3 * stride, nullptr, flags)); + } + else if (ins->mSrc[1].mMemory == IM_FPARAM || ins->mSrc[1].mMemory == IM_FFRAME) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_FPARAMS + ins->mSrc[1].mVarIndex + ins->mSrc[1].mIntConst)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_FPARAMS + ins->mSrc[1].mVarIndex + ins->mSrc[1].mIntConst + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_FPARAMS + ins->mSrc[1].mVarIndex + ins->mSrc[1].mIntConst + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 3)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_FPARAMS + ins->mSrc[1].mVarIndex + ins->mSrc[1].mIntConst + 3)); + } + else if (ins->mSrc[1].mMemory == IM_LOCAL || ins->mSrc[1].mMemory == IM_PARAM) + { + int index = int(ins->mSrc[1].mIntConst); + int reg = mNoFrame ? BC_REG_STACK : BC_REG_LOCALS; + if (ins->mSrc[1].mMemory == IM_LOCAL) + index += proc->mLocalVars[ins->mSrc[1].mVarIndex]->mOffset; + else + index += ins->mSrc[1].mVarIndex + proc->mLocalSize + 2; + index += mFrameOffset; + CheckFrameIndex(ins, reg, index, 4); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_INDIRECT_Y, reg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_INDIRECT_Y, reg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_INDIRECT_Y, reg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index + 3)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 3)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_INDIRECT_Y, reg)); + } + else if (ins->mSrc[1].mMemory == IM_FRAME) + { + int index = int(ins->mSrc[1].mVarIndex + ins->mSrc[1].mIntConst + 2); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_INDIRECT_Y, BC_REG_STACK)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_INDIRECT_Y, BC_REG_STACK)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_INDIRECT_Y, BC_REG_STACK)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index + 3)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 3)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_INDIRECT_Y, BC_REG_STACK)); + } + } + } + else + { + if (ins->mSrc[0].mTemp < 0) + { + if (ins->mSrc[1].mMemory == IM_INDIRECT) + { + union { float f; unsigned int v; } cc; + cc.f = float(ins->mSrc[0].mFloatConst); + + int reg = BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp]; + int index = int(ins->mSrc[1].mIntConst); + int stride = ins->mSrc[1].mStride; + + if (stride * 4 <= 256) + { + CheckFrameIndex(ins, reg, index, stride * 4); + + for (int i = 0; i < 4; i++) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index + i * stride)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (cc.v >> (8 * i)) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_INDIRECT_Y, reg)); + } + } + else + { + for (int i = 0; i < 4; i++) + { + CheckFrameIndex(ins, reg, index, 1); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (cc.v >> (8 * i)) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_INDIRECT_Y, reg)); + + index += stride; + } + } + } + } + else + { + if (ins->mSrc[1].mMemory == IM_INDIRECT) + { + int reg = BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp]; + int index = int(ins->mSrc[1].mIntConst); + int stride = ins->mSrc[1].mStride; + + if (stride * 4 <= 256) + { + CheckFrameIndex(ins, reg, index, stride * 4); + + for (int i = 0; i < 4; i++) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index + i * stride)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + i)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_INDIRECT_Y, reg)); + } + } + else + { + for (int i = 0; i < 4; i++) + { + CheckFrameIndex(ins, reg, index, 1); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + i)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_INDIRECT_Y, reg)); + + index += stride; + } + } + } + } + } + } + else if (ins->mSrc[0].mType == IT_POINTER) + { + int stride = ins->mSrc[1].mStride; + + if (ins->mSrc[1].mTemp < 0) + { + if (ins->mSrc[0].mTemp < 0) + { + if (ins->mSrc[1].mMemory == IM_GLOBAL) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, ins->mSrc[0].mIntConst & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ABSOLUTE, ins->mSrc[1].mIntConst, ins->mSrc[1].mLinkerObject, flags)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (ins->mSrc[0].mIntConst >> 8) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ABSOLUTE, ins->mSrc[1].mIntConst + stride, ins->mSrc[1].mLinkerObject, flags)); + } + else if (ins->mSrc[1].mMemory == IM_ABSOLUTE) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, ins->mSrc[0].mIntConst & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ABSOLUTE, ins->mSrc[1].mIntConst, nullptr, flags)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (ins->mSrc[0].mIntConst >> 8) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ABSOLUTE, ins->mSrc[1].mIntConst + stride, nullptr, flags)); + } + else if (ins->mSrc[1].mMemory == IM_FPARAM || ins->mSrc[1].mMemory == IM_FFRAME) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, ins->mSrc[0].mIntConst & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_FPARAMS + ins->mSrc[1].mVarIndex + ins->mSrc[1].mIntConst)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (ins->mSrc[0].mIntConst >> 8) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_FPARAMS + ins->mSrc[1].mVarIndex + ins->mSrc[1].mIntConst + 1)); + } + else if (ins->mSrc[1].mMemory == IM_LOCAL || ins->mSrc[1].mMemory == IM_PARAM) + { + int index = int(ins->mSrc[1].mIntConst); + int reg = mNoFrame ? BC_REG_STACK : BC_REG_LOCALS; + if (ins->mSrc[1].mMemory == IM_LOCAL) + index += proc->mLocalVars[ins->mSrc[1].mVarIndex]->mOffset; + else + index += ins->mSrc[1].mVarIndex + proc->mLocalSize + 2; + index += mFrameOffset; + CheckFrameIndex(ins, reg, index, 2); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, ins->mSrc[0].mIntConst & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_INDIRECT_Y, reg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (ins->mSrc[0].mIntConst >> 8) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_INDIRECT_Y, reg)); + } + else if (ins->mSrc[1].mMemory == IM_FRAME) + { + int index = int(ins->mSrc[1].mVarIndex + ins->mSrc[1].mIntConst + 2); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, ins->mSrc[0].mIntConst & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_INDIRECT_Y, BC_REG_STACK)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (ins->mSrc[0].mIntConst >> 8) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_INDIRECT_Y, BC_REG_STACK)); + } + } + else + { + if (ins->mSrc[1].mMemory == IM_GLOBAL) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ABSOLUTE, ins->mSrc[1].mIntConst, ins->mSrc[1].mLinkerObject, flags)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ABSOLUTE, ins->mSrc[1].mIntConst + stride, ins->mSrc[1].mLinkerObject, flags)); + } + else if (ins->mSrc[1].mMemory == IM_ABSOLUTE) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ABSOLUTE, ins->mSrc[1].mIntConst, nullptr, flags)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ABSOLUTE, ins->mSrc[1].mIntConst + stride, nullptr, flags)); + } + else if (ins->mSrc[1].mMemory == IM_FPARAM || ins->mSrc[1].mMemory == IM_FFRAME) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_FPARAMS + ins->mSrc[1].mVarIndex + ins->mSrc[1].mIntConst)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_FPARAMS + ins->mSrc[1].mVarIndex + ins->mSrc[1].mIntConst + 1)); + } + else if (ins->mSrc[1].mMemory == IM_LOCAL || ins->mSrc[1].mMemory == IM_PARAM) + { + int index = int(ins->mSrc[1].mIntConst); + int reg = mNoFrame ? BC_REG_STACK : BC_REG_LOCALS; + if (ins->mSrc[1].mMemory == IM_LOCAL) + index += proc->mLocalVars[ins->mSrc[1].mVarIndex]->mOffset; + else + index += ins->mSrc[1].mVarIndex + proc->mLocalSize + 2; + index += mFrameOffset; + CheckFrameIndex(ins, reg, index, 2); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_INDIRECT_Y, reg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_INDIRECT_Y, reg)); + } + else if (ins->mSrc[1].mMemory == IM_FRAME) + { + int index = int(ins->mSrc[1].mVarIndex + ins->mSrc[1].mIntConst + 2); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_INDIRECT_Y, BC_REG_STACK)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_INDIRECT_Y, BC_REG_STACK)); + } + } + } + else + { + if (ins->mSrc[0].mTemp < 0) + { + if (ins->mSrc[1].mMemory == IM_INDIRECT) + { + int reg = BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp]; + int index = int(ins->mSrc[1].mIntConst); + int stride = ins->mSrc[1].mStride; + + if (2 * stride <= 256) + { + CheckFrameIndex(ins, reg, index, 2 * stride); + + for (int i = 0; i < 2; i++) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index + i * stride)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (ins->mSrc[0].mIntConst >> (8 * i)) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_INDIRECT_Y, reg)); + } + } + else + { + for (int i = 0; i < 2; i++) + { + CheckFrameIndex(ins, reg, index, 1); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (ins->mSrc[0].mIntConst >> (8 * i)) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_INDIRECT_Y, reg)); + + index += stride; + } + } + } + } + else + { + if (ins->mSrc[1].mMemory == IM_INDIRECT) + { + int reg = BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp]; + int index = int(ins->mSrc[1].mIntConst); + int stride = ins->mSrc[1].mStride; + + if (2 * stride <= 256) + { + CheckFrameIndex(ins, reg, index, 2 * stride); + + for (int i = 0; i < 2; i++) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index + i * stride)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + i)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_INDIRECT_Y, reg)); + } + } + else + { + for (int i = 0; i < 2; i++) + { + CheckFrameIndex(ins, reg, index, 1); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + i)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_INDIRECT_Y, reg)); + + index += stride; + } + } + } + } + } + } + else + { + if (ins->mSrc[1].mTemp < 0) + { + if (ins->mSrc[0].mTemp < 0) + { + if (InterTypeSize[ins->mSrc[0].mType] == 1) + { + if (ins->mSrc[1].mMemory == IM_GLOBAL) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, ins->mSrc[0].mIntConst & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ABSOLUTE, ins->mSrc[1].mIntConst, ins->mSrc[1].mLinkerObject, flags)); + } + else if (ins->mSrc[1].mMemory == IM_ABSOLUTE) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, ins->mSrc[0].mIntConst & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ABSOLUTE, ins->mSrc[1].mIntConst, nullptr, flags)); + } + else if (ins->mSrc[1].mMemory == IM_FPARAM || ins->mSrc[1].mMemory == IM_FFRAME) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, ins->mSrc[0].mIntConst & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_FPARAMS + ins->mSrc[1].mVarIndex + ins->mSrc[1].mIntConst)); + } + else if (ins->mSrc[1].mMemory == IM_LOCAL || ins->mSrc[1].mMemory == IM_PARAM) + { + int index = int(ins->mSrc[1].mIntConst); + int reg = mNoFrame ? BC_REG_STACK : BC_REG_LOCALS; + if (ins->mSrc[1].mMemory == IM_LOCAL) + index += proc->mLocalVars[ins->mSrc[1].mVarIndex]->mOffset; + else + index += ins->mSrc[1].mVarIndex + proc->mLocalSize + 2; + index += mFrameOffset; + CheckFrameIndex(ins, reg, index, 1); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, ins->mSrc[0].mIntConst & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_INDIRECT_Y, reg)); + } + else if (ins->mSrc[1].mMemory == IM_FRAME) + { + int index = int(ins->mSrc[1].mVarIndex + ins->mSrc[1].mIntConst + 2); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, ins->mSrc[0].mIntConst & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_INDIRECT_Y, BC_REG_STACK)); + } + } + else if (InterTypeSize[ins->mSrc[0].mType] == 2) + { + int stride = ins->mSrc[1].mStride; + + if (ins->mSrc[1].mMemory == IM_GLOBAL) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, ins->mSrc[0].mIntConst & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ABSOLUTE, ins->mSrc[1].mIntConst, ins->mSrc[1].mLinkerObject, flags)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (ins->mSrc[0].mIntConst >> 8) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ABSOLUTE, ins->mSrc[1].mIntConst + stride, ins->mSrc[1].mLinkerObject, flags)); + } + else if (ins->mSrc[1].mMemory == IM_ABSOLUTE) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, ins->mSrc[0].mIntConst & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ABSOLUTE, ins->mSrc[1].mIntConst, nullptr, flags)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (ins->mSrc[0].mIntConst >> 8) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ABSOLUTE, ins->mSrc[1].mIntConst + stride, nullptr, flags)); + } + else if (ins->mSrc[1].mMemory == IM_FPARAM || ins->mSrc[1].mMemory == IM_FFRAME) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, ins->mSrc[0].mIntConst & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_FPARAMS + ins->mSrc[1].mVarIndex + ins->mSrc[1].mIntConst)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (ins->mSrc[0].mIntConst >> 8) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_FPARAMS + ins->mSrc[1].mVarIndex + ins->mSrc[1].mIntConst + stride)); + } + else if (ins->mSrc[1].mMemory == IM_LOCAL || ins->mSrc[1].mMemory == IM_PARAM) + { + int index = int(ins->mSrc[1].mIntConst); + int reg = mNoFrame ? BC_REG_STACK : BC_REG_LOCALS; + if (ins->mSrc[1].mMemory == IM_LOCAL) + index += proc->mLocalVars[ins->mSrc[1].mVarIndex]->mOffset; + else + index += ins->mSrc[1].mVarIndex + proc->mLocalSize + 2; + index += mFrameOffset; + CheckFrameIndex(ins, reg, index, 2); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, ins->mSrc[0].mIntConst & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_INDIRECT_Y, reg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index + stride)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (ins->mSrc[0].mIntConst >> 8) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_INDIRECT_Y, reg)); + } + else if (ins->mSrc[1].mMemory == IM_FRAME) + { + int index = int(ins->mSrc[1].mVarIndex + ins->mSrc[1].mIntConst + 2); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, ins->mSrc[0].mIntConst & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_INDIRECT_Y, BC_REG_STACK)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index + stride)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (ins->mSrc[0].mIntConst >> 8) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_INDIRECT_Y, BC_REG_STACK)); + } + } + else if (InterTypeSize[ins->mSrc[0].mType] == 4) + { + int stride = ins->mSrc[1].mStride; + + if (ins->mSrc[1].mMemory == IM_GLOBAL) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, ins->mSrc[0].mIntConst & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ABSOLUTE, ins->mSrc[1].mIntConst, ins->mSrc[1].mLinkerObject, flags)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (ins->mSrc[0].mIntConst >> 8) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ABSOLUTE, ins->mSrc[1].mIntConst + 1 * stride, ins->mSrc[1].mLinkerObject, flags)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (ins->mSrc[0].mIntConst >> 16) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ABSOLUTE, ins->mSrc[1].mIntConst + 2 * stride, ins->mSrc[1].mLinkerObject, flags)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (ins->mSrc[0].mIntConst >> 24) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ABSOLUTE, ins->mSrc[1].mIntConst + 3 * stride, ins->mSrc[1].mLinkerObject, flags)); + } + else if (ins->mSrc[1].mMemory == IM_ABSOLUTE) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, ins->mSrc[0].mIntConst & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ABSOLUTE, ins->mSrc[1].mIntConst, nullptr, flags)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (ins->mSrc[0].mIntConst >> 8) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ABSOLUTE, ins->mSrc[1].mIntConst + 1 * stride, nullptr, flags)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (ins->mSrc[0].mIntConst >> 16) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ABSOLUTE, ins->mSrc[1].mIntConst + 2 * stride, nullptr, flags)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (ins->mSrc[0].mIntConst >> 24) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ABSOLUTE, ins->mSrc[1].mIntConst + 3 * stride, nullptr, flags)); + } + else if (ins->mSrc[1].mMemory == IM_FPARAM || ins->mSrc[1].mMemory == IM_FFRAME) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, ins->mSrc[0].mIntConst & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_FPARAMS + ins->mSrc[1].mVarIndex + ins->mSrc[1].mIntConst)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (ins->mSrc[0].mIntConst >> 8) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_FPARAMS + ins->mSrc[1].mVarIndex + ins->mSrc[1].mIntConst + 1 * stride)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (ins->mSrc[0].mIntConst >> 16) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_FPARAMS + ins->mSrc[1].mVarIndex + ins->mSrc[1].mIntConst + 2 * stride)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (ins->mSrc[0].mIntConst >> 24) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_FPARAMS + ins->mSrc[1].mVarIndex + ins->mSrc[1].mIntConst + 3 * stride)); + } + else if (ins->mSrc[1].mMemory == IM_LOCAL || ins->mSrc[1].mMemory == IM_PARAM) + { + int index = int(ins->mSrc[1].mIntConst); + int reg = mNoFrame ? BC_REG_STACK : BC_REG_LOCALS; + if (ins->mSrc[1].mMemory == IM_LOCAL) + index += proc->mLocalVars[ins->mSrc[1].mVarIndex]->mOffset; + else + index += ins->mSrc[1].mVarIndex + proc->mLocalSize + 2; + index += mFrameOffset; + CheckFrameIndex(ins, reg, index, 4); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, ins->mSrc[0].mIntConst & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_INDIRECT_Y, reg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index + 1 * stride)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (ins->mSrc[0].mIntConst >> 8) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_INDIRECT_Y, reg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index + 2 * stride)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (ins->mSrc[0].mIntConst >> 16) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_INDIRECT_Y, reg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index + 3 * stride)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (ins->mSrc[0].mIntConst >> 24) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_INDIRECT_Y, reg)); + } + else if (ins->mSrc[1].mMemory == IM_FRAME) + { + int index = int(ins->mSrc[1].mVarIndex + ins->mSrc[1].mIntConst + 2); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, ins->mSrc[0].mIntConst & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_INDIRECT_Y, BC_REG_STACK)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index + 1 * stride)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (ins->mSrc[0].mIntConst >> 8) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_INDIRECT_Y, BC_REG_STACK)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index + 2 * stride)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (ins->mSrc[0].mIntConst >> 16) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_INDIRECT_Y, BC_REG_STACK)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index + 3 * stride)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (ins->mSrc[0].mIntConst >> 24) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_INDIRECT_Y, BC_REG_STACK)); + } + } + } + else + { + if (InterTypeSize[ins->mSrc[0].mType] == 1) + { + if (ins->mSrc[1].mMemory == IM_GLOBAL) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ABSOLUTE, ins->mSrc[1].mIntConst, ins->mSrc[1].mLinkerObject, flags)); + } + else if (ins->mSrc[1].mMemory == IM_ABSOLUTE) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ABSOLUTE, ins->mSrc[1].mIntConst, nullptr, flags)); + } + else if (ins->mSrc[1].mMemory == IM_FPARAM || ins->mSrc[1].mMemory == IM_FFRAME) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_FPARAMS + ins->mSrc[1].mVarIndex + ins->mSrc[1].mIntConst)); + } + else if (ins->mSrc[1].mMemory == IM_LOCAL || ins->mSrc[1].mMemory == IM_PARAM) + { + int index = int(ins->mSrc[1].mIntConst); + int reg = mNoFrame ? BC_REG_STACK : BC_REG_LOCALS; + if (ins->mSrc[1].mMemory == IM_LOCAL) + index += proc->mLocalVars[ins->mSrc[1].mVarIndex]->mOffset; + else + index += ins->mSrc[1].mVarIndex + proc->mLocalSize + 2; + index += mFrameOffset; + CheckFrameIndex(ins, reg, index, 1); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_INDIRECT_Y, reg)); + } + else if (ins->mSrc[1].mMemory == IM_FRAME) + { + int index = int(ins->mSrc[1].mVarIndex + ins->mSrc[1].mIntConst + 2); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_INDIRECT_Y, BC_REG_STACK)); + } + } + else if (InterTypeSize[ins->mSrc[0].mType] == 2) + { + int stride = ins->mSrc[1].mStride; + + if (ins->mSrc[1].mMemory == IM_GLOBAL) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ABSOLUTE, ins->mSrc[1].mIntConst, ins->mSrc[1].mLinkerObject, flags)); + if (ins->mSrc[0].IsUByte()) + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + else + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ABSOLUTE, ins->mSrc[1].mIntConst + stride, ins->mSrc[1].mLinkerObject, flags)); + } + else if (ins->mSrc[1].mMemory == IM_ABSOLUTE) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ABSOLUTE, ins->mSrc[1].mIntConst, nullptr, flags)); + if (ins->mSrc[0].IsUByte()) + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + else + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ABSOLUTE, ins->mSrc[1].mIntConst + stride, nullptr, flags)); + } + else if (ins->mSrc[1].mMemory == IM_FPARAM || ins->mSrc[1].mMemory == IM_FFRAME) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_FPARAMS + ins->mSrc[1].mVarIndex + ins->mSrc[1].mIntConst)); + if (ins->mSrc[0].IsUByte()) + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + else + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_FPARAMS + ins->mSrc[1].mVarIndex + ins->mSrc[1].mIntConst + stride)); + } + else if (ins->mSrc[1].mMemory == IM_LOCAL || ins->mSrc[1].mMemory == IM_PARAM) + { + int index = int(ins->mSrc[1].mIntConst); + int reg = mNoFrame ? BC_REG_STACK : BC_REG_LOCALS; + + if (ins->mSrc[1].mMemory == IM_LOCAL) + index += proc->mLocalVars[ins->mSrc[1].mVarIndex]->mOffset; + else + index += ins->mSrc[1].mVarIndex + proc->mLocalSize + 2; + index += mFrameOffset; + CheckFrameIndex(ins, reg, index, 2); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_INDIRECT_Y, reg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index + stride)); + if (ins->mSrc[0].IsUByte()) + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + else + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_INDIRECT_Y, reg)); + } + else if (ins->mSrc[1].mMemory == IM_FRAME) + { + int index = int(ins->mSrc[1].mVarIndex + ins->mSrc[1].mIntConst + 2); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_INDIRECT_Y, BC_REG_STACK)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index + stride)); + if (ins->mSrc[0].IsUByte()) + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + else + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_INDIRECT_Y, BC_REG_STACK)); + } + } + else if (InterTypeSize[ins->mSrc[0].mType] == 4) + { + int stride = ins->mSrc[1].mStride; + + if (ins->mSrc[1].mMemory == IM_GLOBAL) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ABSOLUTE, ins->mSrc[1].mIntConst, ins->mSrc[1].mLinkerObject, flags)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ABSOLUTE, ins->mSrc[1].mIntConst + 1 * stride, ins->mSrc[1].mLinkerObject, flags)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ABSOLUTE, ins->mSrc[1].mIntConst + 2 * stride, ins->mSrc[1].mLinkerObject, flags)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 3)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ABSOLUTE, ins->mSrc[1].mIntConst + 3 * stride, ins->mSrc[1].mLinkerObject, flags)); + } + else if (ins->mSrc[1].mMemory == IM_ABSOLUTE) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ABSOLUTE, ins->mSrc[1].mIntConst, nullptr, flags)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ABSOLUTE, ins->mSrc[1].mIntConst + 1 * stride, nullptr, flags)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ABSOLUTE, ins->mSrc[1].mIntConst + 2 * stride, nullptr, flags)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 3)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ABSOLUTE, ins->mSrc[1].mIntConst + 3 * stride, nullptr, flags)); + } + else if (ins->mSrc[1].mMemory == IM_FPARAM || ins->mSrc[1].mMemory == IM_FFRAME) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_FPARAMS + ins->mSrc[1].mVarIndex + ins->mSrc[1].mIntConst)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_FPARAMS + ins->mSrc[1].mVarIndex + ins->mSrc[1].mIntConst + 1 * stride)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_FPARAMS + ins->mSrc[1].mVarIndex + ins->mSrc[1].mIntConst + 2 * stride)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 3)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_FPARAMS + ins->mSrc[1].mVarIndex + ins->mSrc[1].mIntConst + 3 * stride)); + } + else if (ins->mSrc[1].mMemory == IM_LOCAL || ins->mSrc[1].mMemory == IM_PARAM) + { + int index = int(ins->mSrc[1].mIntConst); + int reg = mNoFrame ? BC_REG_STACK : BC_REG_LOCALS; + + if (ins->mSrc[1].mMemory == IM_LOCAL) + index += proc->mLocalVars[ins->mSrc[1].mVarIndex]->mOffset; + else + index += ins->mSrc[1].mVarIndex + proc->mLocalSize + 2; + index += mFrameOffset; + CheckFrameIndex(ins, reg, index, 4); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_INDIRECT_Y, reg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index + 1 * stride)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_INDIRECT_Y, reg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index + 2 * stride)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_INDIRECT_Y, reg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index + 3 * stride)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 3)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_INDIRECT_Y, reg)); + } + else if (ins->mSrc[1].mMemory == IM_FRAME) + { + int index = int(ins->mSrc[1].mVarIndex + ins->mSrc[1].mIntConst + 2); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_INDIRECT_Y, BC_REG_STACK)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index + 1 * stride)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_INDIRECT_Y, BC_REG_STACK)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index + 2 * stride)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_INDIRECT_Y, BC_REG_STACK)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index + 3 * stride)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 3)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_INDIRECT_Y, BC_REG_STACK)); + } + } + } + } + else + { + if (ins->mSrc[0].mTemp < 0) + { + if (ins->mSrc[1].mMemory == IM_INDIRECT) + { + int reg = BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp]; + int index = int(ins->mSrc[1].mIntConst); + int stride = ins->mSrc[1].mStride; + int size = InterTypeSize[ins->mSrc[0].mType]; + + if (stride * size <= 256) + { + CheckFrameIndex(ins, reg, index, stride * size); + + for (int i = 0; i < size; i++) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index + i * stride)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (ins->mSrc[0].mIntConst >> (8 * i)) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_INDIRECT_Y, reg, nullptr, flags)); + } + } + else + { + for (int i = 0; i < size; i++) + { + CheckFrameIndex(ins, reg, index, 1); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (ins->mSrc[0].mIntConst >> (8 * i)) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_INDIRECT_Y, reg, nullptr, flags)); + + index += stride; + } + } + } + } + else + { + if (ins->mSrc[1].mMemory == IM_INDIRECT) + { + int reg = BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp]; + int index = int(ins->mSrc[1].mIntConst); + int stride = ins->mSrc[1].mStride; + int size = InterTypeSize[ins->mSrc[0].mType]; + + if (stride * size <= 256) + { + CheckFrameIndex(ins, reg, index, stride * size); + + for (int i = 0; i < size; i++) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index + i * stride)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + i)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_INDIRECT_Y, reg, nullptr, flags)); + } + } + else + { + for (int i = 0; i < size; i++) + { + CheckFrameIndex(ins, reg, index, 1); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + i)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_INDIRECT_Y, reg, nullptr, flags)); + + index += stride; + } + } + } + } + } + } + +} + +void NativeCodeBasicBlock::LoadByteIndexedValue(InterCodeProcedure* proc, const InterInstruction* iins, const InterInstruction* rins) +{ + mIns.Push(NativeCodeInstruction(rins, ASMIT_LDY, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[iins->mSrc[0].mTemp])); + + int stride = rins->mSrc[0].mStride; + + uint32 flags = NCIF_LOWER | NCIF_UPPER; + if (rins->mVolatile) + flags |= NCIF_VOLATILE; + if (rins->mMemmap) + flags |= NCIF_MEMMAP; + if (rins->mAliasing) + flags |= NCIF_ALIASING; + + for (int i = 0; i < InterTypeSize[rins->mDst.mType]; i++) + { + if (i != 0) + { + if (stride == 1) + mIns.Push(NativeCodeInstruction(rins, ASMIT_INY, ASMIM_IMPLIED)); + else + { + mIns.Push(NativeCodeInstruction(rins, ASMIT_TYA, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(rins, ASMIT_CLC, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(rins, ASMIT_ADC, ASMIM_IMMEDIATE, stride)); + mIns.Push(NativeCodeInstruction(rins, ASMIT_TAY, ASMIM_IMPLIED)); + } + } + mIns.Push(NativeCodeInstruction(rins, ASMIT_LDA, ASMIM_INDIRECT_Y, BC_REG_TMP + proc->mTempOffset[iins->mSrc[1].mTemp], nullptr, flags)); + if (rins->mDst.mTemp == iins->mSrc[1].mTemp) + mIns.Push(NativeCodeInstruction(rins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + i)); + else + mIns.Push(NativeCodeInstruction(rins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[rins->mDst.mTemp] + i)); + } + + if (rins->mDst.mTemp == iins->mSrc[1].mTemp) + { + for (int i = 0; i < InterTypeSize[rins->mDst.mType]; i++) + { + mIns.Push(NativeCodeInstruction(rins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_ACCU + i)); + mIns.Push(NativeCodeInstruction(rins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[rins->mDst.mTemp] + i)); + } + } +} + +void NativeCodeBasicBlock::StoreByteIndexedValue(InterCodeProcedure* proc, const InterInstruction* iins, const InterInstruction* wins) +{ + mIns.Push(NativeCodeInstruction(wins, ASMIT_LDY, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[iins->mSrc[0].mTemp])); + + int stride = wins->mSrc[1].mStride; + + uint32 flags = NCIF_LOWER | NCIF_UPPER; + if (wins->mVolatile) + flags |= NCIF_VOLATILE; + if (wins->mMemmap) + flags |= NCIF_MEMMAP; + if (wins->mAliasing) + flags |= NCIF_ALIASING; + + for (int i = 0; i < InterTypeSize[wins->mSrc[0].mType]; i++) + { + if (i != 0) + { + if (stride == 1) + mIns.Push(NativeCodeInstruction(wins, ASMIT_INY, ASMIM_IMPLIED)); + else + { + mIns.Push(NativeCodeInstruction(wins, ASMIT_TYA, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(wins, ASMIT_CLC, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(wins, ASMIT_ADC, ASMIM_IMMEDIATE, stride)); + mIns.Push(NativeCodeInstruction(wins, ASMIT_TAY, ASMIM_IMPLIED)); + } + } + if (wins->mSrc[0].mTemp < 0) + mIns.Push(NativeCodeInstruction(wins, ASMIT_LDA, ASMIM_IMMEDIATE, (wins->mSrc[0].mIntConst >> (8 * i)) & 0xff)); + else + mIns.Push(NativeCodeInstruction(wins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[wins->mSrc[0].mTemp] + i)); + mIns.Push(NativeCodeInstruction(wins, ASMIT_STA, ASMIM_INDIRECT_Y, BC_REG_TMP + proc->mTempOffset[iins->mSrc[1].mTemp], nullptr, flags)); + } +} + +void NativeCodeBasicBlock::CopyByteIndexedValue(InterCodeProcedure* proc, const InterInstruction* riins, const InterInstruction* wiins, const InterInstruction* rins, const InterInstruction* wins) +{ + mIns.Push(NativeCodeInstruction(rins, ASMIT_LDY, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[riins->mSrc[0].mTemp])); + + int stride = rins->mSrc[1].mStride; + + uint32 rflags = NCIF_LOWER | NCIF_UPPER; + if (rins->mVolatile) + rflags |= NCIF_VOLATILE; + if (rins->mMemmap) + rflags |= NCIF_MEMMAP; + if (rins->mAliasing) + rflags |= NCIF_ALIASING; + uint32 wflags = NCIF_LOWER | NCIF_UPPER; + if (wins->mVolatile) + wflags |= NCIF_VOLATILE; + if (wins->mMemmap) + wflags |= NCIF_MEMMAP; + if (wins->mAliasing) + wflags |= NCIF_ALIASING; + + for (int i = 0; i < InterTypeSize[wins->mSrc[0].mType]; i++) + { + if (i != 0) + { + if (stride == 1) + mIns.Push(NativeCodeInstruction(rins, ASMIT_INY, ASMIM_IMPLIED)); + else + { + mIns.Push(NativeCodeInstruction(rins, ASMIT_TYA, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(rins, ASMIT_CLC, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(rins, ASMIT_ADC, ASMIM_IMMEDIATE, stride)); + mIns.Push(NativeCodeInstruction(rins, ASMIT_TAY, ASMIM_IMPLIED)); + } + } + mIns.Push(NativeCodeInstruction(rins, ASMIT_LDA, ASMIM_INDIRECT_Y, BC_REG_TMP + proc->mTempOffset[riins->mSrc[1].mTemp], nullptr, rflags)); + mIns.Push(NativeCodeInstruction(rins, ASMIT_STA, ASMIM_INDIRECT_Y, BC_REG_TMP + proc->mTempOffset[wiins->mSrc[1].mTemp], nullptr, wflags)); + } +} + +void NativeCodeBasicBlock::LoadAbsoluteByteIndexedValue(InterCodeProcedure* proc, const InterInstruction* iins, const InterInstruction* rins) +{ + mIns.Push(NativeCodeInstruction(iins, ASMIT_LDX, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[iins->mSrc[0].mTemp])); + int address = int(iins->mSrc[1].mIntConst + rins->mSrc[0].mIntConst); + + uint32 flags = NCIF_LOWER | NCIF_UPPER; + if (rins->mVolatile) + flags |= NCIF_VOLATILE; + if (rins->mMemmap) + flags |= NCIF_MEMMAP; + if (rins->mAliasing) + flags |= NCIF_ALIASING; + + for (int i = 0; i < InterTypeSize[rins->mDst.mType]; i++) + { + mIns.Push(NativeCodeInstruction(rins, ASMIT_LDA, ASMIM_ABSOLUTE_X, address + i * rins->mSrc[0].mStride, iins->mSrc[1].mLinkerObject, flags)); + mIns.Push(NativeCodeInstruction(rins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[rins->mDst.mTemp] + i)); + } +} + +void NativeCodeBasicBlock::StoreAbsoluteByteIndexedValue(InterCodeProcedure* proc, const InterInstruction* iins, const InterInstruction* wins) +{ + mIns.Push(NativeCodeInstruction(iins, ASMIT_LDX, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[iins->mSrc[0].mTemp])); + int address = int(iins->mSrc[1].mIntConst + wins->mSrc[1].mIntConst); + + uint32 flags = NCIF_LOWER | NCIF_UPPER; + if (wins->mVolatile) + flags |= NCIF_VOLATILE; + if (wins->mMemmap) + flags |= NCIF_MEMMAP; + if (wins->mAliasing) + flags |= NCIF_ALIASING; + + for (int i = 0; i < InterTypeSize[wins->mSrc[0].mType]; i++) + { + if (wins->mSrc[0].mTemp < 0) + mIns.Push(NativeCodeInstruction(wins, ASMIT_LDA, ASMIM_IMMEDIATE, (wins->mSrc[0].mIntConst >> (8 * i)) & 0xff)); + else + mIns.Push(NativeCodeInstruction(wins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[wins->mSrc[0].mTemp] + i)); + + mIns.Push(NativeCodeInstruction(wins, ASMIT_STA, ASMIM_ABSOLUTE_X, address + i * wins->mSrc[1].mStride, iins->mSrc[1].mLinkerObject, flags)); + } +} + +void NativeCodeBasicBlock::StoreByteOffsetIndexedValue(InterCodeProcedure* proc, const InterInstruction* iins, const InterInstruction* wins) +{ + mIns.Push(NativeCodeInstruction(iins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[iins->mSrc[0].mTemp])); + mIns.Push(NativeCodeInstruction(iins, ASMIT_CLC)); + mIns.Push(NativeCodeInstruction(iins, ASMIT_ADC, ASMIM_IMMEDIATE, wins->mSrc[1].mIntConst)); + mIns.Push(NativeCodeInstruction(iins, ASMIT_TAY)); + + uint32 flags = NCIF_LOWER | NCIF_UPPER; + if (wins->mVolatile) + flags |= NCIF_VOLATILE; + if (wins->mMemmap) + flags |= NCIF_MEMMAP; + if (wins->mAliasing) + flags |= NCIF_ALIASING; + + for (int i = 0; i < InterTypeSize[wins->mSrc[0].mType]; i++) + { + if (i != 0) + mIns.Push(NativeCodeInstruction(wins, ASMIT_INY, ASMIM_IMPLIED)); + if (wins->mSrc[0].mTemp < 0) + mIns.Push(NativeCodeInstruction(wins, ASMIT_LDA, ASMIM_IMMEDIATE, (wins->mSrc[0].mIntConst >> (8 * i)) & 0xff)); + else + mIns.Push(NativeCodeInstruction(wins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[wins->mSrc[0].mTemp] + i)); + mIns.Push(NativeCodeInstruction(wins, ASMIT_STA, ASMIM_INDIRECT_Y, BC_REG_TMP + proc->mTempOffset[iins->mSrc[1].mTemp], nullptr, flags)); + } +} + +void NativeCodeBasicBlock::LoadStoreIndirectPair(InterCodeProcedure* proc, const InterInstruction* wins0, const InterInstruction* wins1) +{ + int sreg = BC_REG_TMP + proc->mTempOffset[wins0->mSrc[0].mTemp]; + + int ireg0 = BC_REG_TMP + proc->mTempOffset[wins0->mSrc[1].mTemp]; + int index0 = int(wins0->mSrc[1].mIntConst); + uint32 wflags0 = NCIF_LOWER | NCIF_UPPER; + if (wins0->mVolatile) + wflags0 |= NCIF_VOLATILE; + if (wins0->mMemmap) + wflags0 |= NCIF_MEMMAP; + if (wins0->mAliasing) + wflags0 |= NCIF_ALIASING; + + int ireg1 = BC_REG_TMP + proc->mTempOffset[wins1->mSrc[1].mTemp]; + int index1 = int(wins1->mSrc[1].mIntConst); + uint32 wflags1 = NCIF_LOWER | NCIF_UPPER; + if (wins1->mVolatile) + wflags1 |= NCIF_VOLATILE; + if (wins1->mMemmap) + wflags1 |= NCIF_MEMMAP; + if (wins1->mAliasing) + wflags1 |= NCIF_ALIASING; + + CheckFrameIndex(wins0, ireg0, index0, 1, BC_REG_ADDR); + CheckFrameIndex(wins1, ireg1, index1, 1, BC_REG_ACCU); + + mIns.Push(NativeCodeInstruction(wins0, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg)); + + mIns.Push(NativeCodeInstruction(wins0, ASMIT_LDY, ASMIM_IMMEDIATE, index0)); + mIns.Push(NativeCodeInstruction(wins0, ASMIT_STA, ASMIM_INDIRECT_Y, ireg0, nullptr, wflags0)); + + mIns.Push(NativeCodeInstruction(wins1, ASMIT_LDY, ASMIM_IMMEDIATE, index1)); + mIns.Push(NativeCodeInstruction(wins1, ASMIT_STA, ASMIM_INDIRECT_Y, ireg1, nullptr, wflags1)); +} + +void NativeCodeBasicBlock::LoadStoreIndirectValue(InterCodeProcedure* proc, const InterInstruction* rins, const InterInstruction* wins) +{ + int size = InterTypeSize[wins->mSrc[0].mType]; + + AsmInsMode rmode = ASMIM_INDIRECT_Y; + int rindex = int(rins->mSrc[0].mIntConst); + int rareg = mNoFrame ? BC_REG_STACK : BC_REG_LOCALS; + LinkerObject* rlobject = nullptr; + int rstride = rins->mSrc[0].mStride; + + uint32 rflags = NCIF_LOWER | NCIF_UPPER; + if (rins->mVolatile) + rflags |= NCIF_VOLATILE; + if (rins->mMemmap) + rflags |= NCIF_MEMMAP; + if (rins->mAliasing) + rflags |= NCIF_ALIASING; + + switch (rins->mSrc[0].mMemory) + { + case IM_PARAM: + rindex += rins->mSrc[0].mVarIndex + proc->mLocalSize + 2 + mFrameOffset; + break; + case IM_LOCAL: + rindex += proc->mLocalVars[rins->mSrc[0].mVarIndex]->mOffset + mFrameOffset; + break; + case IM_PROCEDURE: + case IM_GLOBAL: + rmode = ASMIM_ABSOLUTE; + rlobject = rins->mSrc[0].mLinkerObject; + rindex = int(rins->mSrc[0].mIntConst); + break; + case IM_FRAME: + rindex = int(rins->mSrc[0].mVarIndex + rins->mSrc[0].mIntConst + 2); + rareg = BC_REG_STACK; + break; + case IM_INDIRECT: + rareg = BC_REG_TMP + proc->mTempOffset[rins->mSrc[0].mTemp]; + break; + case IM_ABSOLUTE: + rmode = ASMIM_ABSOLUTE; + rindex = int(rins->mSrc[0].mIntConst); + break; + case IM_FPARAM: + case IM_FFRAME: + rmode = ASMIM_ZERO_PAGE; + rareg = int(BC_REG_FPARAMS + rins->mSrc[0].mVarIndex + rins->mSrc[0].mIntConst); + break; + } + + AsmInsMode wmode = ASMIM_INDIRECT_Y; + int windex = int(wins->mSrc[1].mIntConst); + int wareg = mNoFrame ? BC_REG_STACK : BC_REG_LOCALS; + LinkerObject* wlobject = nullptr; + int wstride = wins->mSrc[1].mStride; + + uint32 wflags = NCIF_LOWER | NCIF_UPPER; + if (wins->mVolatile) + wflags |= NCIF_VOLATILE; + if (wins->mMemmap) + wflags |= NCIF_MEMMAP; + if (wins->mAliasing) + wflags |= NCIF_ALIASING; + + switch (wins->mSrc[1].mMemory) + { + case IM_PARAM: + windex += wins->mSrc[1].mVarIndex + proc->mLocalSize + 2 + mFrameOffset; + break; + case IM_LOCAL: + windex += proc->mLocalVars[wins->mSrc[1].mVarIndex]->mOffset + mFrameOffset; + break; + case IM_PROCEDURE: + case IM_GLOBAL: + wmode = ASMIM_ABSOLUTE; + wlobject = wins->mSrc[1].mLinkerObject; + windex = int(wins->mSrc[1].mIntConst); + break; + case IM_FRAME: + windex = int(wins->mSrc[1].mVarIndex + wins->mSrc[1].mIntConst + 2); + wareg = BC_REG_STACK; + break; + case IM_INDIRECT: + wareg = BC_REG_TMP + proc->mTempOffset[wins->mSrc[1].mTemp]; + break; + case IM_ABSOLUTE: + wmode = ASMIM_ABSOLUTE; + windex = int(wins->mSrc[1].mIntConst); + break; + case IM_FPARAM: + case IM_FFRAME: + wmode = ASMIM_ZERO_PAGE; + wareg = BC_REG_FPARAMS + wins->mSrc[1].mVarIndex + int(wins->mSrc[1].mIntConst); + break; + } + + for (int i = 0; i < size; i++) + { + if (rmode == ASMIM_INDIRECT_Y) + CheckFrameIndex(rins, rareg, rindex, 1, BC_REG_ADDR); + if (wmode == ASMIM_INDIRECT_Y) + CheckFrameIndex(wins, wareg, windex, 1, BC_REG_ACCU); + + if (rmode == ASMIM_INDIRECT_Y) + { + mIns.Push(NativeCodeInstruction(rins, ASMIT_LDY, ASMIM_IMMEDIATE, rindex)); + mIns.Push(NativeCodeInstruction(rins, ASMIT_LDA, ASMIM_INDIRECT_Y, rareg, nullptr, rflags)); + } + else if (rmode == ASMIM_ZERO_PAGE) + mIns.Push(NativeCodeInstruction(rins, ASMIT_LDA, ASMIM_ZERO_PAGE, rareg + i)); + else + mIns.Push(NativeCodeInstruction(rins, ASMIT_LDA, ASMIM_ABSOLUTE, rindex, rlobject, rflags)); + + if (wmode == ASMIM_INDIRECT_Y) + { + mIns.Push(NativeCodeInstruction(wins, ASMIT_LDY, ASMIM_IMMEDIATE, windex)); + mIns.Push(NativeCodeInstruction(wins, ASMIT_STA, ASMIM_INDIRECT_Y, wareg, nullptr, wflags)); + } + else if (wmode == ASMIM_ZERO_PAGE) + mIns.Push(NativeCodeInstruction(wins, ASMIT_STA, ASMIM_ZERO_PAGE, wareg + i)); + else + mIns.Push(NativeCodeInstruction(wins, ASMIT_STA, ASMIM_ABSOLUTE, windex, wlobject, wflags)); + + rindex += rstride; + windex += wstride; + } +} + +void NativeCodeBasicBlock::LoadStoreValue(InterCodeProcedure* proc, const InterInstruction * rins, const InterInstruction * wins) +{ + uint32 rflags = NCIF_LOWER | NCIF_UPPER; + if (rins->mVolatile) + rflags |= NCIF_VOLATILE; + if (rins->mMemmap) + rflags |= NCIF_MEMMAP; + if (rins->mAliasing) + rflags |= NCIF_ALIASING; + + uint32 wflags = NCIF_LOWER | NCIF_UPPER; + if (wins->mVolatile) + wflags |= NCIF_VOLATILE; + if (wins->mMemmap) + wflags |= NCIF_MEMMAP; + if (wins->mAliasing) + wflags |= NCIF_ALIASING; + + + if (rins->mDst.mType == IT_FLOAT) + { + + } + else if (rins->mDst.mType == IT_POINTER) + { + + } + else + { + + if (InterTypeSize[wins->mSrc[0].mType] == 1) + { + if (rins->mSrc[0].mTemp < 0) + { + if (rins->mSrc[0].mMemory == IM_GLOBAL) + { + mIns.Push(NativeCodeInstruction(rins, ASMIT_LDA, ASMIM_ABSOLUTE, rins->mSrc[0].mIntConst, rins->mSrc[0].mLinkerObject, rflags)); + } + else if (rins->mSrc[0].mMemory == IM_ABSOLUTE) + { + mIns.Push(NativeCodeInstruction(rins, ASMIT_LDA, ASMIM_ABSOLUTE, rins->mSrc[0].mIntConst, nullptr, rflags)); + } + else if (rins->mSrc[0].mMemory == IM_FPARAM) + { + mIns.Push(NativeCodeInstruction(rins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_FPARAMS + rins->mSrc[0].mVarIndex + rins->mSrc[0].mIntConst)); + } + else if (rins->mSrc[0].mMemory == IM_LOCAL || rins->mSrc[0].mMemory == IM_PARAM) + { + int index = int(rins->mSrc[0].mIntConst); + int areg = mNoFrame ? BC_REG_STACK : BC_REG_LOCALS; + if (rins->mSrc[0].mMemory == IM_LOCAL) + index += proc->mLocalVars[rins->mSrc[0].mVarIndex]->mOffset; + else + index += rins->mSrc[0].mVarIndex + proc->mLocalSize + 2; + index += mFrameOffset; + CheckFrameIndex(rins, areg, index, 1); + + mIns.Push(NativeCodeInstruction(rins, ASMIT_LDY, ASMIM_IMMEDIATE, index)); + mIns.Push(NativeCodeInstruction(rins, ASMIT_LDA, ASMIM_INDIRECT_Y, areg)); + } + } + else + { + if (rins->mSrc[0].mMemory == IM_INDIRECT) + { + int areg = BC_REG_TMP + proc->mTempOffset[rins->mSrc[0].mTemp]; + int index = int(rins->mSrc[0].mIntConst); + + CheckFrameIndex(rins, areg, index, 1); + + mIns.Push(NativeCodeInstruction(rins, ASMIT_LDY, ASMIM_IMMEDIATE, index)); + mIns.Push(NativeCodeInstruction(rins, ASMIT_LDA, ASMIM_INDIRECT_Y, areg)); + } + } + + if (wins->mSrc[1].mTemp < 0) + { + if (wins->mSrc[1].mMemory == IM_GLOBAL) + { + mIns.Push(NativeCodeInstruction(wins, ASMIT_STA, ASMIM_ABSOLUTE, wins->mSrc[1].mIntConst, wins->mSrc[1].mLinkerObject, wflags)); + } + else if (wins->mSrc[1].mMemory == IM_ABSOLUTE) + { + mIns.Push(NativeCodeInstruction(wins, ASMIT_STA, ASMIM_ABSOLUTE, wins->mSrc[1].mIntConst, nullptr, wflags)); + } + else if (wins->mSrc[1].mMemory == IM_FPARAM || wins->mSrc[1].mMemory == IM_FFRAME) + { + mIns.Push(NativeCodeInstruction(wins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_FPARAMS + wins->mSrc[1].mVarIndex + wins->mSrc[1].mIntConst)); + } + else if (wins->mSrc[1].mMemory == IM_LOCAL || wins->mSrc[1].mMemory == IM_PARAM) + { + int index = int(wins->mSrc[1].mIntConst); + int areg = mNoFrame ? BC_REG_STACK : BC_REG_LOCALS; + if (wins->mSrc[1].mMemory == IM_LOCAL) + index += proc->mLocalVars[wins->mSrc[1].mVarIndex]->mOffset; + else + index += wins->mSrc[1].mVarIndex + proc->mLocalSize + 2; + index += mFrameOffset; + CheckFrameIndex(wins, areg, index, 1); + + mIns.Push(NativeCodeInstruction(wins, ASMIT_LDY, ASMIM_IMMEDIATE, index)); + mIns.Push(NativeCodeInstruction(wins, ASMIT_STA, ASMIM_INDIRECT_Y, areg)); + } + else if (wins->mSrc[1].mMemory == IM_FRAME) + { + int index = wins->mSrc[1].mVarIndex + int(wins->mSrc[1].mIntConst) + 2; + + mIns.Push(NativeCodeInstruction(wins, ASMIT_LDY, ASMIM_IMMEDIATE, index)); + mIns.Push(NativeCodeInstruction(wins, ASMIT_STA, ASMIM_INDIRECT_Y, BC_REG_STACK)); + } + } + else + { + if (wins->mSrc[1].mMemory == IM_INDIRECT) + { + int areg = BC_REG_TMP + proc->mTempOffset[wins->mSrc[1].mTemp]; + int index = int(wins->mSrc[1].mIntConst); + + CheckFrameIndex(wins, areg, index, 1); + + mIns.Push(NativeCodeInstruction(wins, ASMIT_LDY, ASMIM_IMMEDIATE, index)); + mIns.Push(NativeCodeInstruction(wins, ASMIT_STA, ASMIM_INDIRECT_Y, areg)); + } + } + } + } +} + +bool NativeCodeBasicBlock::LoadLoadOpStoreIndirectValue(InterCodeProcedure* proc, const InterInstruction* rins1, const InterInstruction* rins0, const InterInstruction* oins, const InterInstruction* wins) +{ + if (rins1->mSrc[0].mMemory == IM_INDIRECT && rins0->mSrc[0].mMemory == IM_INDIRECT && wins->mSrc[1].mMemory == IM_INDIRECT) + { + if (rins1->mSrc[0].mStride != 1 || rins0->mSrc[0].mStride != 1 || wins->mSrc[1].mStride != 1) + return false; + + int size = InterTypeSize[oins->mDst.mType]; + + if (!wins->mSrc[0].mFinal) + { + if (wins->mSrc[0].mTemp == rins1->mSrc[0].mTemp || wins->mSrc[0].mTemp == rins0->mSrc[0].mTemp) + return false; + } + + switch (oins->mOperator) + { + case IA_ADD: + mIns.Push(NativeCodeInstruction(oins, ASMIT_CLC)); + break; + default: + return false; + } + + for (int i = 0; i < size; i++) + { + if (rins1->mSrc[0].mIntConst == wins->mSrc[1].mIntConst && rins0->mSrc[0].mIntConst != wins->mSrc[1].mIntConst) + { + mIns.Push(NativeCodeInstruction(rins0, ASMIT_LDY, ASMIM_IMMEDIATE, rins0->mSrc[0].mIntConst + i)); + mIns.Push(NativeCodeInstruction(rins0, ASMIT_LDA, ASMIM_INDIRECT_Y, BC_REG_TMP + proc->mTempOffset[rins0->mSrc[0].mTemp])); + + mIns.Push(NativeCodeInstruction(rins1, ASMIT_LDY, ASMIM_IMMEDIATE, rins1->mSrc[0].mIntConst + i)); + mIns.Push(NativeCodeInstruction(oins, ASMIT_ADC, ASMIM_INDIRECT_Y, BC_REG_TMP + proc->mTempOffset[rins1->mSrc[0].mTemp])); + } + else + { + mIns.Push(NativeCodeInstruction(rins1, ASMIT_LDY, ASMIM_IMMEDIATE, rins1->mSrc[0].mIntConst + i)); + mIns.Push(NativeCodeInstruction(rins1, ASMIT_LDA, ASMIM_INDIRECT_Y, BC_REG_TMP + proc->mTempOffset[rins1->mSrc[0].mTemp])); + + mIns.Push(NativeCodeInstruction(rins0, ASMIT_LDY, ASMIM_IMMEDIATE, rins0->mSrc[0].mIntConst + i)); + mIns.Push(NativeCodeInstruction(oins, ASMIT_ADC, ASMIM_INDIRECT_Y, BC_REG_TMP + proc->mTempOffset[rins0->mSrc[0].mTemp])); + } + + mIns.Push(NativeCodeInstruction(wins, ASMIT_LDY, ASMIM_IMMEDIATE, wins->mSrc[1].mIntConst + i)); + mIns.Push(NativeCodeInstruction(wins, ASMIT_STA, ASMIM_INDIRECT_Y, BC_REG_TMP + proc->mTempOffset[wins->mSrc[1].mTemp])); + + if (!wins->mSrc[0].mFinal) + mIns.Push(NativeCodeInstruction(wins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[wins->mSrc[0].mTemp] + i)); + } + + return true; + } +#if 1 + else if (rins1->mSrc[0].mMemory == IM_INDIRECT && wins->mSrc[1].mMemory == IM_INDIRECT && rins0->mSrc[0].mMemory == IM_FPARAM) + { + if (rins1->mSrc[0].mTemp == wins->mSrc[1].mTemp && rins1->mSrc[0].mIntConst == wins->mSrc[1].mIntConst && rins1->mSrc[0].mStride == wins->mSrc[1].mStride && + wins->mSrc[0].mFinal && wins->mSrc[1].mFinal) + { + int size = InterTypeSize[oins->mDst.mType]; + + AsmInsType aop; + + switch (oins->mOperator) + { + case IA_ADD: + mIns.Push(NativeCodeInstruction(oins, ASMIT_CLC)); + aop = ASMIT_ADC; + break; + case IA_OR: + aop = ASMIT_ORA; + break; + case IA_XOR: + aop = ASMIT_EOR; + break; + case IA_AND: + aop = ASMIT_AND; + break; + default: + return false; + } + + int offset = int(wins->mSrc[1].mIntConst); + for (int i = 0; i < size; i++) + { + while (offset >= 256) + { + mIns.Push(NativeCodeInstruction(rins1, ASMIT_INC, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[wins->mSrc[1].mTemp] + 1)); + offset -= 256; + } + + mIns.Push(NativeCodeInstruction(rins1, ASMIT_LDY, ASMIM_IMMEDIATE, offset)); + mIns.Push(NativeCodeInstruction(rins1, ASMIT_LDA, ASMIM_INDIRECT_Y, BC_REG_TMP + proc->mTempOffset[wins->mSrc[1].mTemp])); + + mIns.Push(NativeCodeInstruction(oins, ASMIT_ADC, ASMIM_ZERO_PAGE, BC_REG_FPARAMS + rins0->mSrc[0].mVarIndex + int(rins0->mSrc[0].mIntConst) + i)); + + mIns.Push(NativeCodeInstruction(wins, ASMIT_STA, ASMIM_INDIRECT_Y, BC_REG_TMP + proc->mTempOffset[wins->mSrc[1].mTemp])); + + offset += wins->mSrc[1].mStride; + } + + return true; + } + + + return false; + } + else if (rins0->mSrc[0].mMemory == IM_INDIRECT && wins->mSrc[1].mMemory == IM_INDIRECT && rins1->mSrc[0].mMemory == IM_FPARAM) + { + return false; + } +#endif + else + return false; +} + +bool NativeCodeBasicBlock::LoadUnopStoreIndirectValue(InterCodeProcedure* proc, const InterInstruction* rins, const InterInstruction* oins, const InterInstruction* wins) +{ + int size = InterTypeSize[wins->mSrc[0].mType]; + int rstride = rins->mSrc[0].mStride; + int wstride = wins->mSrc[1].mStride; + + if (size > 1 && rstride != wstride) + return false; + + AsmInsMode ram = ASMIM_INDIRECT_Y, wam = ASMIM_INDIRECT_Y; + bool sfinal = wins->mSrc[0].mFinal; + int imm; + AsmInsType at; + + switch (oins->mOperator) + { + case IA_NEG: + mIns.Push(NativeCodeInstruction(oins, ASMIT_SEC)); + imm = 0x00; + at = ASMIT_SBC; + break; + case IA_NOT: + imm = 0xff; + at = ASMIT_EOR; + break; + default: + return false; + } + + int rindex = int(rins->mSrc[0].mIntConst); + int rareg = mNoFrame ? BC_REG_STACK : BC_REG_LOCALS; + + switch (rins->mSrc[0].mMemory) + { + case IM_INDIRECT: + rareg = BC_REG_TMP + proc->mTempOffset[rins->mSrc[0].mTemp]; + break; + case IM_LOCAL: + rindex += proc->mLocalVars[rins->mSrc[0].mVarIndex]->mOffset + mFrameOffset; + break; + case IM_PARAM: + rindex += rins->mSrc[0].mVarIndex + proc->mLocalSize + 2 + mFrameOffset; + break; + case IM_FPARAM: + case IM_FFRAME: + ram = ASMIM_ZERO_PAGE; + rareg = BC_REG_FPARAMS + rins->mSrc[0].mVarIndex + int(rins->mSrc[0].mIntConst); + break; + default: + return false; + } + + int windex = int(wins->mSrc[1].mIntConst); + int wareg = mNoFrame ? BC_REG_STACK : BC_REG_LOCALS; + + switch (wins->mSrc[1].mMemory) + { + case IM_INDIRECT: + wareg = BC_REG_TMP + proc->mTempOffset[wins->mSrc[1].mTemp]; + break; + case IM_LOCAL: + windex += proc->mLocalVars[wins->mSrc[1].mVarIndex]->mOffset + mFrameOffset; + break; + case IM_PARAM: + windex += wins->mSrc[1].mVarIndex + proc->mLocalSize + 2 + mFrameOffset; + break; + case IM_FPARAM: + case IM_FFRAME: + wam = ASMIM_ZERO_PAGE; + wareg = BC_REG_FPARAMS + +wins->mSrc[1].mVarIndex + int(wins->mSrc[1].mIntConst); + break; + default: + return false; + } + + uint32 rflags = NCIF_LOWER | NCIF_UPPER; + if (rins->mVolatile) + rflags |= NCIF_VOLATILE; + if (rins->mMemmap) + rflags |= NCIF_MEMMAP; + if (rins->mAliasing) + rflags |= NCIF_ALIASING; + + uint32 wflags = NCIF_LOWER | NCIF_UPPER; + if (wins->mVolatile) + wflags |= NCIF_VOLATILE; + if (wins->mMemmap) + wflags |= NCIF_MEMMAP; + if (wins->mAliasing) + wflags |= NCIF_ALIASING; + + + + for (int i = 0; i < size; i++) + { + if (ram == ASMIM_INDIRECT_Y) + CheckFrameIndex(rins, rareg, rindex, 1, BC_REG_ADDR); + if (wam == ASMIM_INDIRECT_Y) + CheckFrameIndex(wins, wareg, windex, 1, BC_REG_ACCU); + + + mIns.Push(NativeCodeInstruction(rins, ASMIT_LDA, ASMIM_IMMEDIATE, imm)); + + if (ram == ASMIM_INDIRECT_Y) + { + mIns.Push(NativeCodeInstruction(rins, ASMIT_LDY, ASMIM_IMMEDIATE, rindex)); + mIns.Push(NativeCodeInstruction(oins, at, ram, rareg)); + } + else + mIns.Push(NativeCodeInstruction(oins, at, ram, rareg + i)); + + if (!sfinal) + mIns.Push(NativeCodeInstruction(wins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[wins->mSrc[0].mTemp] + i)); + + if (wam == ASMIM_INDIRECT_Y) + { + if (ram != ASMIM_INDIRECT_Y || rindex != windex) + mIns.Push(NativeCodeInstruction(wins, ASMIT_LDY, ASMIM_IMMEDIATE, windex)); + mIns.Push(NativeCodeInstruction(wins, ASMIT_STA, wam, wareg)); + } + else + mIns.Push(NativeCodeInstruction(wins, ASMIT_STA, wam, wareg + i)); + + rindex += rstride; + windex += wstride; + } + + return true; + +} + +bool NativeCodeBasicBlock::LoadOpStoreIndirectValue(InterCodeProcedure* proc, const InterInstruction* rins, const InterInstruction* oins, int oindex, const InterInstruction* wins) +{ + int size = InterTypeSize[wins->mSrc[0].mType]; + + AsmInsType at = ASMIT_ADC, an = ASMIT_ADC; + AsmInsMode am = oins->mSrc[oindex].mTemp < 0 ? ASMIM_IMMEDIATE : ASMIM_ZERO_PAGE, ram = ASMIM_INDIRECT_Y, wam = ASMIM_INDIRECT_Y; + bool reverse = false, sfinal = wins->mSrc[0].mFinal; + + switch (oins->mOperator) + { + case IA_ADD: + at = an = ASMIT_ADC; + break; + case IA_SUB: + at = an = ASMIT_SBC; + if (oindex == 1) + reverse = true; + break; + case IA_AND: + at = an = ASMIT_AND; + break; + case IA_OR: + at = an = ASMIT_ORA; + break; + case IA_XOR: + at = an = ASMIT_EOR; + break; + case IA_SHL: + if (oindex == 0 && oins->mSrc[oindex].mTemp < 0 && oins->mSrc[oindex].mIntConst == 1) + { + at = ASMIT_ASL; + an = ASMIT_ROL; + am = ASMIM_IMPLIED; + } + else + return false; + break; + default: + return false; + } + + int rindex = int(rins->mSrc[0].mIntConst); + int rareg = mNoFrame ? BC_REG_STACK : BC_REG_LOCALS; + int rstride = rins->mSrc[0].mStride; + + switch (rins->mSrc[0].mMemory) + { + case IM_INDIRECT: + rareg = BC_REG_TMP + proc->mTempOffset[rins->mSrc[0].mTemp]; + break; + case IM_LOCAL: + rindex += proc->mLocalVars[rins->mSrc[0].mVarIndex]->mOffset + mFrameOffset; + break; + case IM_PARAM: + rindex += rins->mSrc[0].mVarIndex + proc->mLocalSize + 2 + mFrameOffset; + break; + case IM_FPARAM: + case IM_FFRAME: + ram = ASMIM_ZERO_PAGE; + rareg = BC_REG_FPARAMS + rins->mSrc[0].mVarIndex + int(rins->mSrc[0].mIntConst); + break; + default: + return false; + } + + int windex = int(wins->mSrc[1].mIntConst); + int wareg = mNoFrame ? BC_REG_STACK : BC_REG_LOCALS; + int wstride = wins->mSrc[1].mStride; + + switch (wins->mSrc[1].mMemory) + { + case IM_INDIRECT: + wareg = BC_REG_TMP + proc->mTempOffset[wins->mSrc[1].mTemp]; + break; + case IM_LOCAL: + windex += proc->mLocalVars[wins->mSrc[1].mVarIndex]->mOffset + mFrameOffset; + break; + case IM_PARAM: + windex += wins->mSrc[1].mVarIndex + proc->mLocalSize + 2 + mFrameOffset; + break; + case IM_FPARAM: + case IM_FFRAME: + wam = ASMIM_ZERO_PAGE; + wareg = BC_REG_FPARAMS + +wins->mSrc[1].mVarIndex + int(wins->mSrc[1].mIntConst); + break; + default: + return false; + } + + if (rstride * (size - 1) >= 256 || wstride * (size - 1) >= 256) + return false; + + uint32 rflags = NCIF_LOWER | NCIF_UPPER; + if (rins->mVolatile) + rflags |= NCIF_VOLATILE; + if (rins->mMemmap) + rflags |= NCIF_MEMMAP; + if (rins->mAliasing) + rflags |= NCIF_ALIASING; + + uint32 wflags = NCIF_LOWER | NCIF_UPPER; + if (wins->mVolatile) + wflags |= NCIF_VOLATILE; + if (wins->mMemmap) + wflags |= NCIF_MEMMAP; + if (wins->mAliasing) + wflags |= NCIF_ALIASING; + + if (ram == ASMIM_INDIRECT_Y && wam == ASMIM_INDIRECT_Y && rareg == wareg && rindex == windex) + { + CheckFrameIndex(rins, rareg, rindex, (size - 1) * rstride + 1, BC_REG_ADDR); + windex = rindex; + wareg = rareg; + } + else + { + if (ram == ASMIM_INDIRECT_Y) + CheckFrameIndex(rins, rareg, rindex, (size - 1) * rstride + 1, BC_REG_ADDR); + if (wam == ASMIM_INDIRECT_Y) + CheckFrameIndex(wins, wareg, windex, (size - 1) * wstride + 1, BC_REG_ACCU); + } + + switch (oins->mOperator) + { + case IA_ADD: + mIns.Push(NativeCodeInstruction(oins, ASMIT_CLC)); + break; + case IA_SUB: + mIns.Push(NativeCodeInstruction(oins, ASMIT_SEC)); + break; + } + + for (int i = 0; i < size; i++) + { + if (reverse) + { + if (am == ASMIM_IMPLIED) + mIns.Push(NativeCodeInstruction(rins, ASMIT_LDA, ASMIM_IMPLIED)); + else if (am == ASMIM_IMMEDIATE) + mIns.Push(NativeCodeInstruction(rins, ASMIT_LDA, ASMIM_IMMEDIATE, (oins->mSrc[oindex].mIntConst >> (8 * i)) & 0xff)); + else + mIns.Push(NativeCodeInstruction(rins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[oins->mSrc[oindex].mTemp] + i)); + + if (ram == ASMIM_INDIRECT_Y) + { + mIns.Push(NativeCodeInstruction(oins, ASMIT_LDY, ASMIM_IMMEDIATE, rindex + i * rstride)); + mIns.Push(NativeCodeInstruction(oins, at, ram, rareg)); + } + else + mIns.Push(NativeCodeInstruction(oins, at, ram, rareg + i)); + } + else + { + if (ram == ASMIM_INDIRECT_Y) + { + mIns.Push(NativeCodeInstruction(rins, ASMIT_LDY, ASMIM_IMMEDIATE, rindex + i * rstride)); + mIns.Push(NativeCodeInstruction(rins, ASMIT_LDA, ram, rareg)); + } + else + mIns.Push(NativeCodeInstruction(rins, ASMIT_LDA, ram, rareg + i)); + + if (am == ASMIM_IMPLIED) + mIns.Push(NativeCodeInstruction(oins, at, ASMIM_IMPLIED)); + else if (am == ASMIM_IMMEDIATE) + mIns.Push(NativeCodeInstruction(oins, at, ASMIM_IMMEDIATE, (oins->mSrc[oindex].mIntConst >> (8 * i)) & 0xff)); + else + mIns.Push(NativeCodeInstruction(oins, at, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[oins->mSrc[oindex].mTemp] + i)); + } + + if (!sfinal) + mIns.Push(NativeCodeInstruction(wins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[wins->mSrc[0].mTemp] + i)); + + at = an; + if (wam == ASMIM_INDIRECT_Y) + { + if (ram != ASMIM_INDIRECT_Y || rindex != windex) + mIns.Push(NativeCodeInstruction(wins, ASMIT_LDY, ASMIM_IMMEDIATE, windex + i * wstride)); + mIns.Push(NativeCodeInstruction(wins, ASMIT_STA, wam, wareg)); + } + else + mIns.Push(NativeCodeInstruction(wins, ASMIT_STA, wam, wareg + i)); + } + + return true; +} + +void NativeCodeBasicBlock::LoadValueToReg(InterCodeProcedure* proc, const InterInstruction * ins, int reg, const NativeCodeInstruction* ainsl, const NativeCodeInstruction* ainsh) +{ + uint32 flags = NCIF_LOWER | NCIF_UPPER; + if (ins->mVolatile) + flags |= NCIF_VOLATILE; + if (ins->mMemmap) + flags |= NCIF_MEMMAP; + if (ins->mAliasing) + flags |= NCIF_ALIASING; + + if (ins->mDst.mType == IT_FLOAT) + { + int stride = ins->mSrc[0].mStride; + + if (ins->mSrc[0].mTemp < 0) + { + if (ins->mSrc[0].mMemory == IM_GLOBAL) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ABSOLUTE, ins->mSrc[0].mIntConst, ins->mSrc[0].mLinkerObject, flags)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ABSOLUTE, ins->mSrc[0].mIntConst + 1 * stride, ins->mSrc[0].mLinkerObject, flags)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ABSOLUTE, ins->mSrc[0].mIntConst + 2 * stride, ins->mSrc[0].mLinkerObject, flags)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ABSOLUTE, ins->mSrc[0].mIntConst + 3 * stride, ins->mSrc[0].mLinkerObject, flags)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg + 3)); + } + else if (ins->mSrc[0].mMemory == IM_ABSOLUTE) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ABSOLUTE, ins->mSrc[0].mIntConst, nullptr, flags)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ABSOLUTE, ins->mSrc[0].mIntConst + 1 * stride, nullptr, flags)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ABSOLUTE, ins->mSrc[0].mIntConst + 2 * stride, nullptr, flags)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ABSOLUTE, ins->mSrc[0].mIntConst + 3 * stride, nullptr, flags)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg + 3)); + } + else if (ins->mSrc[0].mMemory == IM_FPARAM || ins->mSrc[0].mMemory == IM_FFRAME) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_FPARAMS + ins->mSrc[0].mVarIndex + ins->mSrc[0].mIntConst, nullptr, flags)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_FPARAMS + ins->mSrc[0].mVarIndex + ins->mSrc[0].mIntConst + 1, nullptr, flags)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_FPARAMS + ins->mSrc[0].mVarIndex + ins->mSrc[0].mIntConst + 2, nullptr, flags)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_FPARAMS + ins->mSrc[0].mVarIndex + ins->mSrc[0].mIntConst + 3, nullptr, flags)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg + 3)); + } + else if (ins->mSrc[0].mMemory == IM_LOCAL || ins->mSrc[0].mMemory == IM_PARAM) + { + int index = int(ins->mSrc[0].mIntConst); + int areg = mNoFrame ? BC_REG_STACK : BC_REG_LOCALS; + if (ins->mSrc[0].mMemory == IM_LOCAL) + index += proc->mLocalVars[ins->mSrc[0].mVarIndex]->mOffset; + else + index += ins->mSrc[0].mVarIndex + proc->mLocalSize + 2; + index += mFrameOffset; + CheckFrameIndex(ins, areg, index, 4); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_INDIRECT_Y, areg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_INDIRECT_Y, areg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_INDIRECT_Y, areg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index + 3)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_INDIRECT_Y, areg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg + 3)); + } + } + else + { + if (ins->mSrc[0].mMemory == IM_INDIRECT) + { + int areg = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + int index = int(ins->mSrc[0].mIntConst); + int stride = ins->mSrc[0].mStride; + bool accu = reg == areg; + + if (stride * 4 <= 256) + { + CheckFrameIndex(ins, areg, index, stride * 4); + + for (int i = 0; i < 4; i++) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index + i * stride)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_INDIRECT_Y, areg)); + if (accu) + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + i)); + else + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg + i)); + } + } + else + { + for (int i = 0; i < 4; i++) + { + CheckFrameIndex(ins, areg, index, 1); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_INDIRECT_Y, areg)); + if (accu) + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + i)); + else + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg + i)); + + index += stride; + } + } + + if (accu) + { + for (int i = 0; i < 4; i++) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_ACCU + i)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg + i)); + } + } + } + } + } + else if (ins->mDst.mType == IT_POINTER) + { + int stride = ins->mSrc[0].mStride; + + if (ins->mSrc[0].mTemp < 0) + { + if (ins->mSrc[0].mMemory == IM_GLOBAL) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ABSOLUTE, ins->mSrc[0].mIntConst, ins->mSrc[0].mLinkerObject, flags)); + if (ainsl) + { + if (ainsl->mType == ASMIT_ADC) + mIns.Push(NativeCodeInstruction(ins, ASMIT_CLC, ASMIM_IMPLIED)); + else if (ainsl->mType == ASMIT_SBC) + mIns.Push(NativeCodeInstruction(ins, ASMIT_SEC, ASMIM_IMPLIED)); + mIns.Push(*ainsl); + } + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ABSOLUTE, ins->mSrc[0].mIntConst + stride, ins->mSrc[0].mLinkerObject, flags)); + if (ainsh) mIns.Push(*ainsh); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg + 1)); + } + else if (ins->mSrc[0].mMemory == IM_ABSOLUTE) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ABSOLUTE, ins->mSrc[0].mIntConst, nullptr, flags)); + if (ainsl) + { + if (ainsl->mType == ASMIT_ADC) + mIns.Push(NativeCodeInstruction(ins, ASMIT_CLC, ASMIM_IMPLIED)); + else if (ainsl->mType == ASMIT_SBC) + mIns.Push(NativeCodeInstruction(ins, ASMIT_SEC, ASMIM_IMPLIED)); + mIns.Push(*ainsl); + } + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ABSOLUTE, ins->mSrc[0].mIntConst + stride, nullptr, flags)); + if (ainsh) mIns.Push(*ainsh); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg + 1)); + } + else if (ins->mSrc[0].mMemory == IM_FPARAM || ins->mSrc[0].mMemory == IM_FFRAME) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_FPARAMS + ins->mSrc[0].mVarIndex + ins->mSrc[0].mIntConst, nullptr, flags)); + if (ainsl) + { + if (ainsl->mType == ASMIT_ADC) + mIns.Push(NativeCodeInstruction(ins, ASMIT_CLC, ASMIM_IMPLIED)); + else if (ainsl->mType == ASMIT_SBC) + mIns.Push(NativeCodeInstruction(ins, ASMIT_SEC, ASMIM_IMPLIED)); + mIns.Push(*ainsl); + } + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_FPARAMS + ins->mSrc[0].mVarIndex + ins->mSrc[0].mIntConst + stride, nullptr, flags)); + if (ainsh) mIns.Push(*ainsh); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg + 1)); + } + else if (ins->mSrc[0].mMemory == IM_LOCAL || ins->mSrc[0].mMemory == IM_PARAM) + { + int index = int(ins->mSrc[0].mIntConst); + int areg = mNoFrame ? BC_REG_STACK : BC_REG_LOCALS; + if (ins->mSrc[0].mMemory == IM_LOCAL) + index += proc->mLocalVars[ins->mSrc[0].mVarIndex]->mOffset; + else + index += ins->mSrc[0].mVarIndex + proc->mLocalSize + 2; + index += mFrameOffset; + CheckFrameIndex(ins, areg, index, 2); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_INDIRECT_Y, areg)); + if (ainsl) + { + if (ainsl->mType == ASMIT_ADC) + mIns.Push(NativeCodeInstruction(ins, ASMIT_CLC, ASMIM_IMPLIED)); + else if (ainsl->mType == ASMIT_SBC) + mIns.Push(NativeCodeInstruction(ins, ASMIT_SEC, ASMIM_IMPLIED)); + mIns.Push(*ainsl); + } + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index + stride)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_INDIRECT_Y, areg)); + if (ainsh) mIns.Push(*ainsh); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg + 1)); + } + } + else + { + if (ins->mSrc[0].mMemory == IM_INDIRECT) + { + int areg = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + int index = int(ins->mSrc[0].mIntConst); + + CheckFrameIndex(ins, areg, index, 2); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index)); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_INDIRECT_Y, areg)); + if (ainsl) + { + if (ainsl->mType == ASMIT_ADC) + mIns.Push(NativeCodeInstruction(ins, ASMIT_CLC, ASMIM_IMPLIED)); + else if (ainsl->mType == ASMIT_SBC) + mIns.Push(NativeCodeInstruction(ins, ASMIT_SEC, ASMIM_IMPLIED)); + mIns.Push(*ainsl); + } + if (reg == areg) + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_WORK_Y)); + else + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index + stride)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_INDIRECT_Y, areg)); + if (ainsh) mIns.Push(*ainsh); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg + 1)); + if (reg == areg) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_WORK_Y)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg)); + } + } + } + } + else + { + if (ins->mSrc[0].mTemp < 0) + { + if (InterTypeSize[ins->mDst.mType] == 1) + { + if (ins->mSrc[0].mMemory == IM_GLOBAL) + { + if (ins->mDst.IsUByte()) + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ABSOLUTE, ins->mSrc[0].mIntConst, ins->mSrc[0].mLinkerObject, flags, 0, uint8(ins->mDst.mRange.mMinValue), uint8(ins->mDst.mRange.mMaxValue))); + else + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ABSOLUTE, ins->mSrc[0].mIntConst, ins->mSrc[0].mLinkerObject, flags)); + } + else if (ins->mSrc[0].mMemory == IM_ABSOLUTE) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ABSOLUTE, ins->mSrc[0].mIntConst, nullptr, flags)); + } + else if (ins->mSrc[0].mMemory == IM_FPARAM || ins->mSrc[0].mMemory == IM_FFRAME) + { + if (ins->mDst.IsUByte()) + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_FPARAMS + ins->mSrc[0].mVarIndex + ins->mSrc[0].mIntConst, nullptr, flags, 0, uint8(ins->mDst.mRange.mMinValue), uint8(ins->mDst.mRange.mMaxValue))); + else + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_FPARAMS + ins->mSrc[0].mVarIndex + ins->mSrc[0].mIntConst, nullptr, flags)); + } + else if (ins->mSrc[0].mMemory == IM_LOCAL || ins->mSrc[0].mMemory == IM_PARAM) + { + int index = int(ins->mSrc[0].mIntConst); + int areg = mNoFrame ? BC_REG_STACK : BC_REG_LOCALS; + if (ins->mSrc[0].mMemory == IM_LOCAL) + index += proc->mLocalVars[ins->mSrc[0].mVarIndex]->mOffset; + else + index += ins->mSrc[0].mVarIndex + proc->mLocalSize + 2; + index += mFrameOffset; + CheckFrameIndex(ins, areg, index, 1); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_INDIRECT_Y, areg)); + } + + if (ainsl) + { + if (ainsl->mType == ASMIT_ADC) + mIns.Push(NativeCodeInstruction(ins, ASMIT_CLC, ASMIM_IMPLIED)); + else if (ainsl->mType == ASMIT_SBC) + mIns.Push(NativeCodeInstruction(ins, ASMIT_SEC, ASMIM_IMPLIED)); + mIns.Push(*ainsl); + } + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg)); + + if (InterTypeSize[ins->mDst.mType] > 1) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + if (ainsh) mIns.Push(*ainsh); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg + 1)); + } + } + else if (InterTypeSize[ins->mDst.mType] == 2) + { + int stride = ins->mSrc[0].mStride; + + if (ins->mSrc[0].mMemory == IM_GLOBAL) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ABSOLUTE, ins->mSrc[0].mIntConst, ins->mSrc[0].mLinkerObject, flags)); + if (ainsl) + { + if (ainsl->mType == ASMIT_ADC) + mIns.Push(NativeCodeInstruction(ins, ASMIT_CLC, ASMIM_IMPLIED)); + else if (ainsl->mType == ASMIT_SBC) + mIns.Push(NativeCodeInstruction(ins, ASMIT_SEC, ASMIM_IMPLIED)); + mIns.Push(*ainsl); + } + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ABSOLUTE, ins->mSrc[0].mIntConst + 1 * stride, ins->mSrc[0].mLinkerObject, flags)); + if (ainsh) mIns.Push(*ainsh); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg + 1)); + } + else if (ins->mSrc[0].mMemory == IM_ABSOLUTE) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ABSOLUTE, ins->mSrc[0].mIntConst, nullptr, flags)); + if (ainsl) + { + if (ainsl->mType == ASMIT_ADC) + mIns.Push(NativeCodeInstruction(ins, ASMIT_CLC, ASMIM_IMPLIED)); + else if (ainsl->mType == ASMIT_SBC) + mIns.Push(NativeCodeInstruction(ins, ASMIT_SEC, ASMIM_IMPLIED)); + mIns.Push(*ainsl); + } + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ABSOLUTE, ins->mSrc[0].mIntConst + 1 * stride, nullptr, flags)); + if (ainsh) mIns.Push(*ainsh); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg + 1)); + } + else if (ins->mSrc[0].mMemory == IM_FPARAM || ins->mSrc[0].mMemory == IM_FFRAME) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_FPARAMS + ins->mSrc[0].mVarIndex + ins->mSrc[0].mIntConst, nullptr, flags)); + if (ainsl) + { + if (ainsl->mType == ASMIT_ADC) + mIns.Push(NativeCodeInstruction(ins, ASMIT_CLC, ASMIM_IMPLIED)); + else if (ainsl->mType == ASMIT_SBC) + mIns.Push(NativeCodeInstruction(ins, ASMIT_SEC, ASMIM_IMPLIED)); + mIns.Push(*ainsl); + } + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_FPARAMS + ins->mSrc[0].mVarIndex + ins->mSrc[0].mIntConst + 1 * stride, nullptr, flags)); + if (ainsh) mIns.Push(*ainsh); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg + 1)); + } + else if (ins->mSrc[0].mMemory == IM_LOCAL || ins->mSrc[0].mMemory == IM_PARAM) + { + int index = int(ins->mSrc[0].mIntConst); + int areg = mNoFrame ? BC_REG_STACK : BC_REG_LOCALS; + if (ins->mSrc[0].mMemory == IM_LOCAL) + index += proc->mLocalVars[ins->mSrc[0].mVarIndex]->mOffset; + else + index += ins->mSrc[0].mVarIndex + proc->mLocalSize + 2; + index += mFrameOffset; + CheckFrameIndex(ins, areg, index, 2); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_INDIRECT_Y, areg)); + if (ainsl) + { + if (ainsl->mType == ASMIT_ADC) + mIns.Push(NativeCodeInstruction(ins, ASMIT_CLC, ASMIM_IMPLIED)); + else if (ainsl->mType == ASMIT_SBC) + mIns.Push(NativeCodeInstruction(ins, ASMIT_SEC, ASMIM_IMPLIED)); + mIns.Push(*ainsl); + } + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index + 1 * stride)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_INDIRECT_Y, areg)); + if (ainsh) mIns.Push(*ainsh); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg + 1)); + } + } + else if (InterTypeSize[ins->mDst.mType] == 4) + { + int stride = ins->mSrc[0].mStride; + + if (ins->mSrc[0].mMemory == IM_GLOBAL) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ABSOLUTE, ins->mSrc[0].mIntConst, ins->mSrc[0].mLinkerObject, flags)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ABSOLUTE, ins->mSrc[0].mIntConst + 1 * stride, ins->mSrc[0].mLinkerObject, flags)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ABSOLUTE, ins->mSrc[0].mIntConst + 2 * stride, ins->mSrc[0].mLinkerObject, flags)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ABSOLUTE, ins->mSrc[0].mIntConst + 3 * stride, ins->mSrc[0].mLinkerObject, flags)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg + 3)); + } + else if (ins->mSrc[0].mMemory == IM_ABSOLUTE) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ABSOLUTE, ins->mSrc[0].mIntConst, nullptr, flags)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ABSOLUTE, ins->mSrc[0].mIntConst + 1 * stride, nullptr, flags)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ABSOLUTE, ins->mSrc[0].mIntConst + 2 * stride, nullptr, flags)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ABSOLUTE, ins->mSrc[0].mIntConst + 3 * stride, nullptr, flags)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg + 3)); + } + else if (ins->mSrc[0].mMemory == IM_FPARAM || ins->mSrc[0].mMemory == IM_FFRAME) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_FPARAMS + ins->mSrc[0].mVarIndex + ins->mSrc[0].mIntConst, nullptr, flags)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_FPARAMS + ins->mSrc[0].mVarIndex + ins->mSrc[0].mIntConst + 1 * stride, nullptr, flags)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_FPARAMS + ins->mSrc[0].mVarIndex + ins->mSrc[0].mIntConst + 2 * stride, nullptr, flags)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_FPARAMS + ins->mSrc[0].mVarIndex + ins->mSrc[0].mIntConst + 3 * stride, nullptr, flags)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg + 3)); + } + else if (ins->mSrc[0].mMemory == IM_LOCAL || ins->mSrc[0].mMemory == IM_PARAM) + { + int index = int(ins->mSrc[0].mIntConst); + int areg = mNoFrame ? BC_REG_STACK : BC_REG_LOCALS; + if (ins->mSrc[0].mMemory == IM_LOCAL) + index += proc->mLocalVars[ins->mSrc[0].mVarIndex]->mOffset; + else + index += ins->mSrc[0].mVarIndex + proc->mLocalSize + 2; + index += mFrameOffset; + CheckFrameIndex(ins, areg, index, 4); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_INDIRECT_Y, areg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index + 1 * stride)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_INDIRECT_Y, areg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index + 2 * stride)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_INDIRECT_Y, areg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index + 3 * stride)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_INDIRECT_Y, areg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg + 3)); + } + } + } + else + { + if (ins->mSrc[0].mMemory == IM_INDIRECT) + { + int areg = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + int index = int(ins->mSrc[0].mIntConst); + int stride = ins->mSrc[0].mStride; + int size = InterTypeSize[ins->mDst.mType]; + bool accu = false; + + if (size * stride <= 256) + { + CheckFrameIndex(ins, areg, index, size * stride); + + accu = reg == areg; + + for (int i = 0; i < size; i++) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index + i * stride)); + if (ins->mDst.IsUByte()) + { + if (i == 0) + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_INDIRECT_Y, areg, nullptr, flags, 0, int(ins->mDst.mRange.mMinValue), int(ins->mDst.mRange.mMaxValue))); + else + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + } + else + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_INDIRECT_Y, areg, nullptr, flags)); + + if (i == 0) + { + if (ainsl) + { + if (ainsl->mType == ASMIT_ADC) + mIns.Push(NativeCodeInstruction(ins, ASMIT_CLC, ASMIM_IMPLIED)); + else if (ainsl->mType == ASMIT_SBC) + mIns.Push(NativeCodeInstruction(ins, ASMIT_SEC, ASMIM_IMPLIED)); + mIns.Push(*ainsl); + } + } + else if (ainsh) + mIns.Push(*ainsh); + + if (accu) + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + i)); + else + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg + i)); + } + } + else + { + assert(ainsl == nullptr && ainsh == nullptr); + + for (int i = 0; i < size; i++) + { + CheckFrameIndex(ins, areg, index, 1); + if (reg == areg) + accu = true; + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index)); + if (ins->mDst.IsUByte()) + { + if (i == 0) + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_INDIRECT_Y, areg, nullptr, flags, 0, int(ins->mDst.mRange.mMinValue), int(ins->mDst.mRange.mMaxValue))); + else + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + } + else + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_INDIRECT_Y, areg, nullptr, flags)); + + if (accu) + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + i)); + else + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg + i)); + + index += stride; + } + } + + if (accu) + { + for (int i = 0; i < size; i++) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_ACCU + i)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg + i)); + } + } + } + } + } +} + +void NativeCodeBasicBlock::LoadValue(InterCodeProcedure* proc, const InterInstruction * ins) +{ + LoadValueToReg(proc, ins, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp], nullptr, nullptr); +} + +NativeCodeBasicBlock * NativeCodeBasicBlock::CopyValue(InterCodeProcedure* proc, const InterInstruction * ins, NativeCodeProcedure* nproc) +{ + int size = ins->mConst.mOperandSize; + int msize = 4, dstride = ins->mSrc[1].mStride, sstride = ins->mSrc[0].mStride; + + uint32 flags = NCIF_LOWER | NCIF_UPPER; + if (ins->mVolatile) + flags |= NCIF_VOLATILE; + if (ins->mMemmap) + flags |= NCIF_MEMMAP; + if (ins->mAliasing) + flags |= NCIF_ALIASING; + + if (sstride > 1 || dstride > 1) + msize = 32; + else if (nproc->mCompilerOptions & COPT_OPTIMIZE_AUTO_UNROLL) + msize = 8; + else if (nproc->mCompilerOptions & COPT_OPTIMIZE_CODE_SIZE) + msize = 2; +#if 1 + if (ins->mSrc[0].mTemp < 0 && ins->mSrc[1].mTemp < 0) + { + if (ins->mSrc[0].mMemory == IM_GLOBAL && (ins->mSrc[1].mMemory == IM_FRAME || ins->mSrc[1].mMemory == IM_LOCAL || ins->mSrc[1].mMemory == IM_PARAM) && size < 256 && dstride == 1 && sstride == 1) + { + int areg = BC_REG_STACK; + + int index = int(ins->mSrc[1].mIntConst); + if (ins->mSrc[1].mMemory == IM_FRAME) + index += ins->mSrc[1].mVarIndex + 2; + else + { + if (!mNoFrame) + areg = BC_REG_LOCALS; + + if (ins->mSrc[1].mMemory == IM_LOCAL) + index += proc->mLocalVars[ins->mSrc[1].mVarIndex]->mOffset; + else + index += ins->mSrc[1].mVarIndex + proc->mLocalSize + 2; + index += mFrameOffset; + } + CheckFrameIndex(ins, areg, index, size, BC_REG_ADDR); + + if (size <= msize) + { + for (int i = 0; i < size; i++) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index + i * dstride)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ABSOLUTE, ins->mSrc[0].mIntConst + i * sstride, ins->mSrc[0].mLinkerObject, flags)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_INDIRECT_Y, areg, nullptr, flags)); + } + + return this; + } + else + { + NativeCodeBasicBlock* lblock = nproc->AllocateBlock(); + NativeCodeBasicBlock* eblock = nproc->AllocateBlock(); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index)); + this->Close(ins, lblock, nullptr, ASMIT_JMP); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ABSOLUTE_Y, ins->mSrc[0].mIntConst - index, ins->mSrc[0].mLinkerObject, flags)); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_INDIRECT_Y, areg, nullptr, flags)); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_INY, ASMIM_IMPLIED)); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_CPY, ASMIM_IMMEDIATE, (index + size) & 255)); + lblock->Close(ins, lblock, eblock, ASMIT_BNE); + + return eblock; + } + } + else if ((ins->mSrc[0].mMemory == IM_GLOBAL || ins->mSrc[0].mMemory == IM_ABSOLUTE) && (ins->mSrc[1].mMemory == IM_GLOBAL || ins->mSrc[1].mMemory == IM_ABSOLUTE)) + { + NativeCodeBasicBlock* block = this; + + int offset = 0; + if (size >= 256) + { + block = nproc->AllocateBlock(); + + NativeCodeBasicBlock* lblock = nproc->AllocateBlock(); + + + if (size > 256) + { + if (size < 512 && !(size & 1)) + { + int step = size >> 1; + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, step)); + this->Close(ins, lblock, nullptr, ASMIT_JMP); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ABSOLUTE_Y, ins->mSrc[0].mIntConst - 1, ins->mSrc[0].mLinkerObject, flags)); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ABSOLUTE_Y, ins->mSrc[1].mIntConst - 1, ins->mSrc[1].mLinkerObject, flags)); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ABSOLUTE_Y, ins->mSrc[0].mIntConst + step - 1, ins->mSrc[0].mLinkerObject, flags)); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ABSOLUTE_Y, ins->mSrc[1].mIntConst + step - 1, ins->mSrc[1].mLinkerObject, flags)); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_DEY, ASMIM_IMPLIED)); + lblock->Close(ins, lblock, block, ASMIT_BNE); + + return block; + } + else if (size < 1024 && !(size & 3)) + { + int step = size >> 2; + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, step)); + this->Close(ins, lblock, nullptr, ASMIT_JMP); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ABSOLUTE_Y, ins->mSrc[0].mIntConst - 1, ins->mSrc[0].mLinkerObject, flags)); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ABSOLUTE_Y, ins->mSrc[1].mIntConst - 1, ins->mSrc[1].mLinkerObject, flags)); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ABSOLUTE_Y, ins->mSrc[0].mIntConst + step - 1, ins->mSrc[0].mLinkerObject, flags)); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ABSOLUTE_Y, ins->mSrc[1].mIntConst + step - 1, ins->mSrc[1].mLinkerObject, flags)); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ABSOLUTE_Y, ins->mSrc[0].mIntConst + 2 * step - 1, ins->mSrc[0].mLinkerObject, flags)); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ABSOLUTE_Y, ins->mSrc[1].mIntConst + 2 * step - 1, ins->mSrc[1].mLinkerObject, flags)); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ABSOLUTE_Y, ins->mSrc[0].mIntConst + 3 * step - 1, ins->mSrc[0].mLinkerObject, flags)); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ABSOLUTE_Y, ins->mSrc[1].mIntConst + 3 * step - 1, ins->mSrc[1].mLinkerObject, flags)); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_DEY, ASMIM_IMPLIED)); + lblock->Close(ins, lblock, block, ASMIT_BNE); + + return block; + } + } + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, 0)); + this->Close(ins, lblock, nullptr, ASMIT_JMP); + while (offset + 255 < size) + { + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ABSOLUTE_Y, ins->mSrc[0].mIntConst + offset, ins->mSrc[0].mLinkerObject, flags)); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ABSOLUTE_Y, ins->mSrc[1].mIntConst + offset, ins->mSrc[1].mLinkerObject, flags)); + offset += 256; + } + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_INY, ASMIM_IMPLIED)); + lblock->Close(ins, lblock, block, ASMIT_BNE); + + size &= 255; + } + + if (size <= msize) + { + LinkerObject* slo = ins->mSrc[0].mLinkerObject; + if (slo && (slo->mFlags & LOBJF_CONST)) + { + + for (int i = 0; i < size; i++) + { + int64 si = ins->mSrc[0].mIntConst + offset + i * sstride; + + LinkerReference* lr = slo->FindReference(si); + if (lr) + { + if ((lr->mFlags & LREF_LOWBYTE) && lr->mOffset == si) + block->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE_ADDRESS, lr->mRefOffset, lr->mRefObject, NCIF_LOWER)); + else + block->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE_ADDRESS, lr->mRefOffset, lr->mRefObject, NCIF_UPPER)); + } + else + block->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, slo->mData[si])); + block->mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ABSOLUTE, ins->mSrc[1].mIntConst + offset + i * dstride, ins->mSrc[1].mLinkerObject, flags)); + } + } + else + { + for (int i = 0; i < size; i++) + { + block->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ABSOLUTE, ins->mSrc[0].mIntConst + offset + i * sstride, ins->mSrc[0].mLinkerObject, flags)); + block->mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ABSOLUTE, ins->mSrc[1].mIntConst + offset + i * dstride, ins->mSrc[1].mLinkerObject, flags)); + } + } + } + else if (size <= 128) + { + NativeCodeBasicBlock* lblock = nproc->AllocateBlock(); + NativeCodeBasicBlock* eblock = nproc->AllocateBlock(); + + block->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, size - 1)); + block->Close(ins, lblock, nullptr, ASMIT_JMP); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ABSOLUTE_Y, ins->mSrc[0].mIntConst + offset, ins->mSrc[0].mLinkerObject, flags)); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ABSOLUTE_Y, ins->mSrc[1].mIntConst + offset, ins->mSrc[1].mLinkerObject, flags)); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_DEY, ASMIM_IMPLIED)); + lblock->Close(ins, lblock, eblock, ASMIT_BPL); + + block = eblock; + } + else + { + NativeCodeBasicBlock* lblock = nproc->AllocateBlock(); + NativeCodeBasicBlock* eblock = nproc->AllocateBlock(); + + block->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, size)); + block->Close(ins, lblock, nullptr, ASMIT_JMP); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ABSOLUTE_Y, ins->mSrc[0].mIntConst + offset - 1, ins->mSrc[0].mLinkerObject, flags)); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ABSOLUTE_Y, ins->mSrc[1].mIntConst + offset - 1, ins->mSrc[1].mLinkerObject, flags)); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_DEY, ASMIM_IMPLIED)); + lblock->Close(ins, lblock, eblock, ASMIT_BNE); + + block = eblock; + } + + return block; + } + else if ((ins->mSrc[0].mMemory == IM_GLOBAL || ins->mSrc[0].mMemory == IM_ABSOLUTE) && (ins->mSrc[1].mMemory == IM_FPARAM || ins->mSrc[1].mMemory == IM_FFRAME)) + { + LinkerObject* slo = ins->mSrc[0].mLinkerObject; + if (slo && (slo->mFlags & LOBJF_CONST)) + { + + for (int i = 0; i < size; i++) + { + int64 si = ins->mSrc[0].mIntConst + i * sstride; + + LinkerReference* lr = slo->FindReference(si); + if (lr) + { + if ((lr->mFlags & LREF_LOWBYTE) && lr->mOffset == si) + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE_ADDRESS, lr->mRefOffset, lr->mRefObject, NCIF_LOWER)); + else + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE_ADDRESS, lr->mRefOffset, lr->mRefObject, NCIF_UPPER)); + } + else + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, slo->mData[si])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_FPARAMS + ins->mSrc[1].mVarIndex + ins->mSrc[1].mIntConst + i * dstride)); + } + } + else + { + for (int i = 0; i < size; i++) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ABSOLUTE, ins->mSrc[0].mIntConst + i * sstride, ins->mSrc[0].mLinkerObject, flags)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_FPARAMS + ins->mSrc[1].mVarIndex + ins->mSrc[1].mIntConst + i * dstride)); + } + } + + return this; + } + } +#endif + + int sreg, dreg; + int si = 0, di = 0; + + if (ins->mSrc[0].mTemp < 0) + { + if (ins->mSrc[0].mMemory == IM_GLOBAL) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE_ADDRESS, ins->mSrc[0].mIntConst, ins->mSrc[0].mLinkerObject, NCIF_LOWER)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE_ADDRESS, ins->mSrc[0].mIntConst, ins->mSrc[0].mLinkerObject, NCIF_UPPER)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 1)); + sreg = BC_REG_ACCU; + } + else if (ins->mSrc[0].mMemory == IM_ABSOLUTE) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, ins->mSrc[0].mIntConst & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (ins->mSrc[0].mIntConst >> 8) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 1)); + sreg = BC_REG_ACCU; + } + else if (ins->mSrc[0].mMemory == IM_FPARAM) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, BC_REG_FPARAMS + ins->mSrc[0].mVarIndex + ins->mSrc[0].mIntConst)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 1)); + sreg = BC_REG_ACCU; + } + else if (ins->mSrc[0].mMemory == IM_LOCAL || ins->mSrc[0].mMemory == IM_PARAM) + { + int index = int(ins->mSrc[0].mIntConst); + sreg = mNoFrame ? BC_REG_STACK : BC_REG_LOCALS; + if (ins->mSrc[0].mMemory == IM_LOCAL) + index += proc->mLocalVars[ins->mSrc[0].mVarIndex]->mOffset; + else + index += ins->mSrc[0].mVarIndex + proc->mLocalSize + 2; + index += mFrameOffset; + CheckFrameIndex(ins, sreg, index, 256, BC_REG_ACCU); + } + } + else if (ins->mSrc[0].mIntConst != 0) + { + int index = int(ins->mSrc[0].mIntConst); + if (size <= msize && (size - 1) * sstride + 1 + index <= 256) + { + si = index; + sreg = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + } + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_CLC, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ADC, ASMIM_IMMEDIATE, index & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ADC, ASMIM_IMMEDIATE, (index >> 8) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 1)); + sreg = BC_REG_ACCU; + } + } + else if (size * sstride > 256 && !ins->mSrc[0].mFinal) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 1)); + sreg = BC_REG_ACCU; + } + else + { + sreg = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]; + } + + if (ins->mSrc[1].mTemp < 0) + { + if (ins->mSrc[1].mMemory == IM_GLOBAL) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE_ADDRESS, ins->mSrc[1].mIntConst, ins->mSrc[1].mLinkerObject, NCIF_LOWER)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ADDR + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE_ADDRESS, ins->mSrc[1].mIntConst, ins->mSrc[1].mLinkerObject, NCIF_UPPER)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ADDR + 1)); + dreg = BC_REG_ADDR; + } + else if (ins->mSrc[1].mMemory == IM_ABSOLUTE) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, ins->mSrc[1].mIntConst & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ADDR + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (ins->mSrc[1].mIntConst >> 8) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ADDR + 1)); + dreg = BC_REG_ADDR; + } + else if (ins->mSrc[1].mMemory == IM_FPARAM || ins->mSrc[1].mMemory == IM_FFRAME) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, BC_REG_FPARAMS + ins->mSrc[1].mVarIndex + ins->mSrc[1].mIntConst)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ADDR + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ADDR + 1)); + dreg = BC_REG_ADDR; + } + else if (ins->mSrc[1].mMemory == IM_LOCAL || ins->mSrc[1].mMemory == IM_PARAM) + { + int index = int(ins->mSrc[1].mIntConst); + dreg = mNoFrame ? BC_REG_STACK : BC_REG_LOCALS; + if (ins->mSrc[1].mMemory == IM_LOCAL) + index += proc->mLocalVars[ins->mSrc[1].mVarIndex]->mOffset; + else + index += ins->mSrc[1].mVarIndex + proc->mLocalSize + 2; + index += mFrameOffset; + CheckFrameIndex(ins, dreg, index, 256, BC_REG_ADDR); + } + else if (ins->mSrc[1].mMemory == IM_FRAME) + { + int index = ins->mSrc[1].mVarIndex + int(ins->mSrc[1].mIntConst) + 2; + + mIns.Push(NativeCodeInstruction(ins, ASMIT_CLC, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_STACK)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ADC, ASMIM_IMMEDIATE, index & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ADDR)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_STACK + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ADC, ASMIM_IMMEDIATE, (index >> 8) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ADDR + 1)); + dreg = BC_REG_ADDR; + } + } + else if (ins->mSrc[1].mIntConst != 0) + { + int index = int(ins->mSrc[1].mIntConst); + if (size <= msize && (size - 1) * dstride + 1 + index <= 256) + { + di = index; + dreg = BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp]; + } + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_CLC, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ADC, ASMIM_IMMEDIATE, index & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ADDR)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ADC, ASMIM_IMMEDIATE, (index >> 8) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ADDR + 1)); + dreg = BC_REG_ADDR; + } + } + else if (size * dstride > 256 && !ins->mSrc[1].mFinal) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ADDR)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ADDR + 1)); + dreg = BC_REG_ADDR; + } + else + { + dreg = BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp]; + } + + if (size <= msize) + { + for (int i = 0; i < size; i++) + { + if (ins->mSrc[0].mTemp < 0 && ins->mSrc[0].mMemory == IM_FPARAM) + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_FPARAMS + ins->mSrc[0].mVarIndex + ins->mSrc[0].mIntConst + i)); + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, si)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_INDIRECT_Y, sreg)); + } + if (ins->mSrc[1].mTemp < 0 && ins->mSrc[1].mMemory == IM_FFRAME) + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_FPARAMS + ins->mSrc[1].mVarIndex + ins->mSrc[1].mIntConst + i)); + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, di)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_INDIRECT_Y, dreg)); + } + + si += sstride; + di += dstride; + if (si >= 256) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_INC, ASMIM_ZERO_PAGE, sreg + 1)); + si &= 0xff; + } + if (di >= 256) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_INC, ASMIM_ZERO_PAGE, dreg + 1)); + di &= 0xff; + } + } + + return this; + } + else + { + NativeCodeBasicBlock* block = this; + + if (size >= 256) + { + block = nproc->AllocateBlock(); + + if (sreg != BC_REG_ACCU && !ins->mSrc[0].mFinal) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 1)); + sreg = BC_REG_ACCU; + } + if (dreg != BC_REG_ADDR && !ins->mSrc[1].mFinal) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, dreg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ADDR)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, dreg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ADDR + 1)); + dreg = BC_REG_ADDR; + } + + NativeCodeBasicBlock* lblock = nproc->AllocateBlock(); + NativeCodeBasicBlock* lblock2 = nproc->AllocateBlock(); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, 0)); + if (size >= 512) + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDX, ASMIM_IMMEDIATE, size >> 8)); + this->Close(ins, lblock, nullptr, ASMIT_JMP); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_INDIRECT_Y, sreg, nullptr, flags)); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_INDIRECT_Y, dreg, nullptr, flags)); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_INY, ASMIM_IMPLIED)); + lblock->Close(ins, lblock, lblock2, ASMIT_BNE); + lblock2->mIns.Push(NativeCodeInstruction(ins, ASMIT_INC, ASMIM_ZERO_PAGE, sreg + 1)); + lblock2->mIns.Push(NativeCodeInstruction(ins, ASMIT_INC, ASMIM_ZERO_PAGE, dreg + 1)); + if (size >= 512) + { + lblock2->mIns.Push(NativeCodeInstruction(ins, ASMIT_DEX, ASMIM_IMPLIED)); + lblock2->Close(ins, lblock, block, ASMIT_BNE); + } + else + lblock2->Close(ins, block, nullptr, ASMIT_JMP); + + size &= 0xff; + } + + if (size > 0) + { + NativeCodeBasicBlock* lblock = nproc->AllocateBlock(); + NativeCodeBasicBlock* eblock = nproc->AllocateBlock(); + + if (size < 128) + { + block->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, size - 1)); + block->Close(ins, lblock, nullptr, ASMIT_JMP); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_INDIRECT_Y, sreg, nullptr, flags)); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_INDIRECT_Y, dreg, nullptr, flags)); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_DEY, ASMIM_IMPLIED)); + lblock->Close(ins, lblock, eblock, ASMIT_BPL); + } + else if (size <= 256) + { + block->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, size - 1)); + block->Close(ins, lblock, nullptr, ASMIT_JMP); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_INDIRECT_Y, sreg, nullptr, flags)); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_INDIRECT_Y, dreg, nullptr, flags)); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_DEY, ASMIM_IMPLIED)); + lblock->Close(ins, lblock, eblock, ASMIT_BNE); + eblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_INDIRECT_Y, sreg, nullptr, flags)); + eblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_INDIRECT_Y, dreg, nullptr, flags)); + } + + block = eblock; + } + + return block; + } +} + +NativeCodeBasicBlock* NativeCodeBasicBlock::FillValue(InterCodeProcedure* proc, const InterInstruction* ins, NativeCodeProcedure* nproc) +{ + int size = ins->mConst.mOperandSize; + int msize = 4, dstride = ins->mSrc[1].mStride; + + uint32 flags = NCIF_LOWER | NCIF_UPPER; + if (ins->mVolatile) + flags |= NCIF_VOLATILE; + if (ins->mMemmap) + flags |= NCIF_MEMMAP; + if (ins->mAliasing) + flags |= NCIF_ALIASING; + + if (dstride > 1) + msize = 32; + else if (nproc->mCompilerOptions & COPT_OPTIMIZE_AUTO_UNROLL) + msize = 8; + else if (nproc->mCompilerOptions & COPT_OPTIMIZE_CODE_SIZE) + msize = 2; +#if 1 + if (ins->mSrc[1].mTemp < 0) + { + if ((ins->mSrc[1].mMemory == IM_FRAME || ins->mSrc[1].mMemory == IM_LOCAL || ins->mSrc[1].mMemory == IM_PARAM) && size < 256 && dstride == 1) + { + int areg = BC_REG_STACK; + + int index = int(ins->mSrc[1].mIntConst); + if (ins->mSrc[1].mMemory == IM_FRAME) + index += ins->mSrc[1].mVarIndex + 2; + else + { + if (!mNoFrame) + areg = BC_REG_LOCALS; + + if (ins->mSrc[1].mMemory == IM_LOCAL) + index += proc->mLocalVars[ins->mSrc[1].mVarIndex]->mOffset; + else + index += ins->mSrc[1].mVarIndex + proc->mLocalSize + 2; + index += mFrameOffset; + } + CheckFrameIndex(ins, areg, index, size, BC_REG_ADDR); + + if (ins->mSrc[0].mTemp < 0) + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, ins->mSrc[0].mIntConst & 0xff)); + else + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp])); + + if (size <= msize) + { + for (int i = 0; i < size; i++) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index + i * dstride)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_INDIRECT_Y, areg, nullptr, flags)); + } + + return this; + } + else + { + NativeCodeBasicBlock* lblock = nproc->AllocateBlock(); + NativeCodeBasicBlock* eblock = nproc->AllocateBlock(); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, index)); + this->Close(ins, lblock, nullptr, ASMIT_JMP); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_INDIRECT_Y, areg, nullptr, flags)); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_INY, ASMIM_IMPLIED)); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_CPY, ASMIM_IMMEDIATE, (index + size) & 255)); + lblock->Close(ins, lblock, eblock, ASMIT_BNE); + + return eblock; + } + } + else if ((ins->mSrc[1].mMemory == IM_GLOBAL || ins->mSrc[1].mMemory == IM_ABSOLUTE) && size <= 256 * msize) + { + NativeCodeBasicBlock* block = this; + + if (ins->mSrc[0].mTemp < 0) + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, ins->mSrc[0].mIntConst & 0xff)); + else + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp])); + + int offset = 0; + if (size >= 256) + { + block = nproc->AllocateBlock(); + + NativeCodeBasicBlock* lblock = nproc->AllocateBlock(); + + if (size > 256) + { + if (size < 512 && !(size & 1)) + { + int step = size >> 1; + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, step)); + this->Close(ins, lblock, nullptr, ASMIT_JMP); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_DEY, ASMIM_IMPLIED)); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ABSOLUTE_Y, ins->mSrc[1].mIntConst, ins->mSrc[1].mLinkerObject, flags)); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ABSOLUTE_Y, ins->mSrc[1].mIntConst + step, ins->mSrc[1].mLinkerObject, flags)); + lblock->Close(ins, lblock, block, ASMIT_BNE); + + return block; + } + else if (size < 1024 && !(size & 3)) + { + int step = size >> 2; + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, step)); + this->Close(ins, lblock, nullptr, ASMIT_JMP); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_DEY, ASMIM_IMPLIED)); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ABSOLUTE_Y, ins->mSrc[1].mIntConst, ins->mSrc[1].mLinkerObject, flags)); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ABSOLUTE_Y, ins->mSrc[1].mIntConst + step, ins->mSrc[1].mLinkerObject, flags)); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ABSOLUTE_Y, ins->mSrc[1].mIntConst + 2 * step, ins->mSrc[1].mLinkerObject, flags)); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ABSOLUTE_Y, ins->mSrc[1].mIntConst + 3 * step, ins->mSrc[1].mLinkerObject, flags)); + lblock->Close(ins, lblock, block, ASMIT_BNE); + + return block; + } + } + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, 0)); + this->Close(ins, lblock, nullptr, ASMIT_JMP); + while (offset + 255 < size) + { + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ABSOLUTE_Y, ins->mSrc[1].mIntConst + offset, ins->mSrc[1].mLinkerObject, flags)); + offset += 256; + } + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_INY, ASMIM_IMPLIED)); + lblock->Close(ins, lblock, block, ASMIT_BNE); + + size &= 255; + } + + if (size <= msize) + { + for (int i = 0; i < size; i++) + block->mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ABSOLUTE, ins->mSrc[1].mIntConst + offset + i * dstride, ins->mSrc[1].mLinkerObject, flags)); + } + else + { + NativeCodeBasicBlock* lblock = nproc->AllocateBlock(); + NativeCodeBasicBlock* eblock = nproc->AllocateBlock(); + + block->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, size)); + block->Close(ins, lblock, nullptr, ASMIT_JMP); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_DEY, ASMIM_IMPLIED)); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ABSOLUTE_Y, ins->mSrc[1].mIntConst + offset, ins->mSrc[1].mLinkerObject, flags)); + lblock->Close(ins, lblock, eblock, ASMIT_BNE); + + block = eblock; + } + + return block; + } + else if (ins->mSrc[1].mMemory == IM_FPARAM || ins->mSrc[1].mMemory == IM_FFRAME) + { + if (ins->mSrc[0].mTemp < 0) + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, ins->mSrc[0].mIntConst & 0xff)); + else + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp])); + + for (int i = 0; i < size; i++) + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_FPARAMS + ins->mSrc[1].mVarIndex + ins->mSrc[1].mIntConst + i * dstride)); + + return this; + } + } +#endif + + int dreg; + int di = 0, ioffset = 0; + + if (ins->mSrc[1].mTemp < 0) + { + if (ins->mSrc[1].mMemory == IM_GLOBAL) + { + if ((size > 256) && (size & 255) != 0) + ioffset = 256 - (size & 255); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE_ADDRESS, ins->mSrc[1].mIntConst - ioffset, ins->mSrc[1].mLinkerObject, NCIF_LOWER)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ADDR + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE_ADDRESS, ins->mSrc[1].mIntConst - ioffset, ins->mSrc[1].mLinkerObject, NCIF_UPPER)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ADDR + 1)); + dreg = BC_REG_ADDR; + } + else if (ins->mSrc[1].mMemory == IM_ABSOLUTE) + { + if ((size > 256) && (size & 255) != 0) + ioffset = 256 - (size & 255); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (ins->mSrc[1].mIntConst - ioffset) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ADDR + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, ((ins->mSrc[1].mIntConst - ioffset) >> 8) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ADDR + 1)); + dreg = BC_REG_ADDR; + } + else if (ins->mSrc[1].mMemory == IM_FPARAM || ins->mSrc[1].mMemory == IM_FFRAME) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, BC_REG_FPARAMS + ins->mSrc[1].mVarIndex + ins->mSrc[1].mIntConst)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ADDR + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ADDR + 1)); + dreg = BC_REG_ADDR; + } + else if (ins->mSrc[1].mMemory == IM_LOCAL || ins->mSrc[1].mMemory == IM_PARAM) + { + int index = int(ins->mSrc[1].mIntConst); + dreg = mNoFrame ? BC_REG_STACK : BC_REG_LOCALS; + if (ins->mSrc[1].mMemory == IM_LOCAL) + index += proc->mLocalVars[ins->mSrc[1].mVarIndex]->mOffset; + else + index += ins->mSrc[1].mVarIndex + proc->mLocalSize + 2; + index += mFrameOffset; + CheckFrameIndex(ins, dreg, index, 256, BC_REG_ADDR); + } + else if (ins->mSrc[1].mMemory == IM_FRAME) + { + int index = ins->mSrc[1].mVarIndex + int(ins->mSrc[1].mIntConst) + 2; + + mIns.Push(NativeCodeInstruction(ins, ASMIT_CLC, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_STACK)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ADC, ASMIM_IMMEDIATE, index & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ADDR)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_STACK + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ADC, ASMIM_IMMEDIATE, (index >> 8) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ADDR + 1)); + dreg = BC_REG_ADDR; + } + } + else if (ins->mSrc[1].mIntConst != 0) + { + int index = int(ins->mSrc[1].mIntConst); + if (size <= msize && (size - 1) * dstride + 1 + index <= 256) + { + di = index; + dreg = BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp]; + } + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_CLC, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ADC, ASMIM_IMMEDIATE, index & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ADDR)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ADC, ASMIM_IMMEDIATE, (index >> 8) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ADDR + 1)); + dreg = BC_REG_ADDR; + } + } + else if (size * dstride > 256 && !ins->mSrc[1].mFinal) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ADDR)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ADDR + 1)); + dreg = BC_REG_ADDR; + } + else + { + dreg = BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp]; + } + + size += ioffset; + + if (size <= msize) + { + if (ins->mSrc[0].mTemp < 0) + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, ins->mSrc[0].mIntConst & 0xff)); + else + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp])); + + for (int i = 0; i < size; i++) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, di)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_INDIRECT_Y, dreg)); + + di += dstride; + if (di >= 256) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_INC, ASMIM_ZERO_PAGE, dreg + 1)); + di &= 0xff; + } + } + + return this; + } + else + { + NativeCodeBasicBlock* block = this; + + if (size >= 256) + { + block = nproc->AllocateBlock(); + + if (dreg != BC_REG_ADDR && !ins->mSrc[1].mFinal) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, dreg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ADDR)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, dreg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ADDR + 1)); + dreg = BC_REG_ADDR; + } + + if (ins->mSrc[0].mTemp < 0) + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, ins->mSrc[0].mIntConst & 0xff)); + else + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp])); + + NativeCodeBasicBlock* lblock = nproc->AllocateBlock(); + NativeCodeBasicBlock* lblock2 = nproc->AllocateBlock(); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, ioffset)); + if (size >= 512) + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDX, ASMIM_IMMEDIATE, size >> 8)); + this->Close(ins, lblock, nullptr, ASMIT_JMP); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_INDIRECT_Y, dreg, nullptr, flags)); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_INY, ASMIM_IMPLIED)); + lblock->Close(ins, lblock, lblock2, ASMIT_BNE); + lblock2->mIns.Push(NativeCodeInstruction(ins, ASMIT_INC, ASMIM_ZERO_PAGE, dreg + 1)); + if (size >= 512) + { + lblock2->mIns.Push(NativeCodeInstruction(ins, ASMIT_DEX, ASMIM_IMPLIED)); + lblock2->Close(ins, lblock, block, ASMIT_BNE); + } + else + lblock2->Close(ins, block, nullptr, ASMIT_JMP); + + size &= 0xff; + } + else + { + if (ins->mSrc[0].mTemp < 0) + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, ins->mSrc[0].mIntConst & 0xff)); + else + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp])); + } + + if (size > 0) + { + NativeCodeBasicBlock* lblock = nproc->AllocateBlock(); + NativeCodeBasicBlock* eblock = nproc->AllocateBlock(); + + block->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, size)); + block->Close(ins, lblock, nullptr, ASMIT_JMP); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_DEY, ASMIM_IMPLIED)); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_INDIRECT_Y, dreg, nullptr, flags)); + lblock->Close(ins, lblock, eblock, ASMIT_BNE); + + block = eblock; + } + + return block; + } +} + +void NativeCodeBasicBlock::CallMalloc(InterCodeProcedure* proc, const InterInstruction* ins, NativeCodeProcedure* nproc) +{ + if (ins->mSrc[0].mTemp < 0) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, ins->mSrc[0].mIntConst & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (ins->mSrc[0].mIntConst >> 8) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 1)); + } + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 1)); + } + + NativeCodeGenerator::Runtime& frt(nproc->mGenerator->ResolveRuntime(Ident::Unique("malloc"))); + mIns.Push(NativeCodeInstruction(ins, ASMIT_JSR, ASMIM_ABSOLUTE, frt.mOffset, frt.mLinkerObject, NCIF_RUNTIME | NCIF_LOWER | NCIF_UPPER)); + + if (ins->mDst.mTemp >= 0) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + 1)); + } +} + +void NativeCodeBasicBlock::CallFree(InterCodeProcedure* proc, const InterInstruction* ins, NativeCodeProcedure* nproc) +{ + if (ins->mSrc[0].mTemp < 0) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, ins->mSrc[0].mIntConst & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (ins->mSrc[0].mIntConst >> 8) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 1)); + } + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 1)); + } + + NativeCodeGenerator::Runtime& frt(nproc->mGenerator->ResolveRuntime(Ident::Unique("free"))); + mIns.Push(NativeCodeInstruction(ins, ASMIT_JSR, ASMIM_ABSOLUTE, frt.mOffset, frt.mLinkerObject, NCIF_RUNTIME | NCIF_LOWER | NCIF_UPPER)); +} + + +NativeCodeBasicBlock* NativeCodeBasicBlock::StrcpyValue(InterCodeProcedure* proc, const InterInstruction* ins, NativeCodeProcedure* nproc) +{ + int sreg = BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp], dreg = BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp]; + + NativeCodeBasicBlock* lblock = nproc->AllocateBlock(); + NativeCodeBasicBlock* eblock = nproc->AllocateBlock(); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, 0xff)); + this->Close(ins, lblock, nullptr, ASMIT_JMP); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_INY, ASMIM_IMPLIED)); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_INDIRECT_Y, sreg)); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_INDIRECT_Y, dreg)); + lblock->Close(ins, lblock, eblock, ASMIT_BNE); + + return eblock; +} + +bool NativeCodeBasicBlock::CheckPredAccuStore(int reg) +{ + if (mIns.Size() < 8) + return false; + + int p = mIns.Size() - 8; + + for (int i = 0; i < 4; i++) + { + if (mIns[p + 0].mType != ASMIT_LDA || mIns[p + 0].mMode != ASMIM_ZERO_PAGE || mIns[p + 0].mAddress != BC_REG_ACCU + i) + return false; + if (mIns[p + 1].mType != ASMIT_STA || mIns[p + 1].mMode != ASMIM_ZERO_PAGE || mIns[p + 1].mAddress != reg + i) + return false; + + p += 2; + } + + return true; +} + +bool NativeCodeBasicBlock::CheckIsInAccu(int reg) +{ + int i = mIns.Size() - 1; + while (i > 0) + { + if (mIns[i + 0].mType == ASMIT_STA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && mIns[i + 0].mAddress == reg && + mIns[i - 1].mType == ASMIT_LDA && mIns[i - 1].mMode == ASMIM_ZERO_PAGE && mIns[i - 1].mAddress == BC_REG_ACCU) + return true; + if (mIns[i].ReferencesZeroPage(reg) || mIns[i].ReferencesZeroPage(BC_REG_ACCU)) + return false; + + i--; + } + return false; +} + +void NativeCodeBasicBlock::ShiftRegisterRight(const InterInstruction* ins, int reg, int shift) +{ + if (shift == 0) + { + } + else if (shift == 1) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LSR, ASMIM_ZERO_PAGE, reg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROR, ASMIM_ZERO_PAGE, reg)); + } + else if (shift == 15) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, reg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg + 0)); + } + else if (shift == 14) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, reg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_AND, ASMIM_IMMEDIATE, 3)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg + 1)); + } + else if (shift >= 8) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, reg + 1)); + for (int i = 8; i < shift; i++) + mIns.Push(NativeCodeInstruction(ins, ASMIT_LSR, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0x00)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg + 1)); + } + else if (shift >= 5) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, reg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDX, ASMIM_ZERO_PAGE, reg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STX, ASMIM_ZERO_PAGE, reg)); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_IMPLIED)); + for (int i = shift; i < 8; i++) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL, ASMIM_ZERO_PAGE, reg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL, ASMIM_IMPLIED)); + } + mIns.Push(NativeCodeInstruction(ins, ASMIT_AND, ASMIM_IMMEDIATE, 0xff >> shift)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg + 1)); + } + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, reg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LSR, ASMIM_ZERO_PAGE, reg + 1)); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROR, ASMIM_IMPLIED)); + for (int i = 1; i < shift; i++) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LSR, ASMIM_ZERO_PAGE, reg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROR, ASMIM_IMPLIED)); + } + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg)); + } +} + +void NativeCodeBasicBlock::ShiftRegisterLeft(InterCodeProcedure* proc, const InterInstruction* ins, int reg, int shift) +{ + if (shift == 0) + { + + } + else if (shift == 1) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_ZERO_PAGE, reg + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL, ASMIM_ZERO_PAGE, reg + 1)); + } + else if (shift >= 13) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, reg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LSR, ASMIM_IMPLIED)); + for (int i = shift; i < 16; i++) + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROR, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_AND, ASMIM_IMMEDIATE, (0xff << (shift - 8)) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg)); + } + else if (shift >= 8) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, reg)); + for (int i = 8; i < shift; i++) + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg)); + } + else if (shift >= 5) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, reg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDX, ASMIM_ZERO_PAGE, reg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STX, ASMIM_ZERO_PAGE, reg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LSR, ASMIM_IMPLIED)); + for (int i = shift; i < 8; i++) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROR, ASMIM_ZERO_PAGE, reg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROR, ASMIM_IMPLIED)); + } + mIns.Push(NativeCodeInstruction(ins, ASMIT_AND, ASMIM_IMMEDIATE, (0xff << shift) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg)); + } + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, reg + 1)); + for (int i = 0; i < shift; i++) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_ZERO_PAGE, reg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL, ASMIM_IMPLIED)); + } + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg + 1)); + } +} + +void NativeCodeBasicBlock::ShiftRegisterLeftFromByte(InterCodeProcedure* proc, const InterInstruction* ins, int reg, int shift, int max) +{ + if (shift == 0) + { + + } + else if (shift >= 8) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, reg)); + for (int i = 8; i < shift; i++) + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg)); + } + else if (shift == 1) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_ZERO_PAGE, reg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + if (max >= 128) + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg + 1)); + } + else if (shift >= 5 && (max << shift) >= 512) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, reg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LSR, ASMIM_IMPLIED)); + for (int i = shift; i < 7; i++) + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROR, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_TAX)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROR, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_AND, ASMIM_IMMEDIATE, (0xff << shift) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_TXA)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_AND, ASMIM_IMMEDIATE, (0xff >> (8 - shift)) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg + 1)); + } + else if (shift >= 5 && (max << shift) < 256) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, reg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LSR, ASMIM_IMPLIED)); + for (int i = shift; i < 8; i++) + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROR, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg + 1)); + } + else if (shift == 4 && max >= 128) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, reg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_TAX, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_AND, ASMIM_IMMEDIATE, 0x0f)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_TXA, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_AND, ASMIM_IMMEDIATE, 0xf0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg)); + } + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, reg)); + while (shift > 0 && max < 256) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_IMPLIED)); + shift--; + max *= 2; + } + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + if (max > 255) + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL, ASMIM_IMPLIED)); + while (shift > 0) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_ZERO_PAGE, reg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL, ASMIM_IMPLIED)); + shift--; + } + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg + 1)); + } +} + +void NativeCodeBasicBlock::ShiftRegisterLeftByte(InterCodeProcedure* proc, const InterInstruction* ins, int reg, int shift) +{ + if (shift == 0) + { + + } + else if (shift == 1) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_ZERO_PAGE, reg)); + } + else if (shift >= 6) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, reg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LSR, ASMIM_IMPLIED)); + for (int i = shift; i < 8; i++) + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROR, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_AND, ASMIM_IMMEDIATE, (0xff << shift) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg)); + } + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, reg)); + for (int i = 0; i < shift; i++) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_IMPLIED)); + } + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, reg)); + } +} + +int NativeCodeBasicBlock::ShortSignedDivide(InterCodeProcedure* proc, NativeCodeProcedure* nproc, const InterInstruction* ins, const InterInstruction* sins, int mul) +{ + int dreg = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + int sreg = BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp]; + + if (sins) + { + LoadValueToReg(proc, sins, dreg, nullptr, nullptr); + sreg = dreg; + } + + if (mul == 2) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ADC, ASMIM_IMMEDIATE, 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, dreg + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ADC, ASMIM_IMMEDIATE, 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_CMP, ASMIM_IMMEDIATE, 0x80)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROR)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROR, ASMIM_ZERO_PAGE, dreg + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, dreg + 1)); + } + else if (mul == 4) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0x00)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ADC, ASMIM_IMMEDIATE, 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_EOR, ASMIM_IMMEDIATE, 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_AND, ASMIM_IMMEDIATE, 0x03)); + + + mIns.Push(NativeCodeInstruction(ins, ASMIT_CLC)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ADC, ASMIM_ZERO_PAGE, sreg + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, dreg + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ADC, ASMIM_IMMEDIATE, 0)); + + // Two signed shifts + mIns.Push(NativeCodeInstruction(ins, ASMIT_CMP, ASMIM_IMMEDIATE, 0x80)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROR)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROR, ASMIM_ZERO_PAGE, dreg + 0)); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_CMP, ASMIM_IMMEDIATE, 0x80)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROR)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROR, ASMIM_ZERO_PAGE, dreg + 0)); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, dreg + 1)); + } + else if (mul == 8) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0x00)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ADC, ASMIM_IMMEDIATE, 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_EOR, ASMIM_IMMEDIATE, 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_AND, ASMIM_IMMEDIATE, 0x07)); + + + mIns.Push(NativeCodeInstruction(ins, ASMIT_CLC)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ADC, ASMIM_ZERO_PAGE, sreg + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, dreg + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ADC, ASMIM_IMMEDIATE, 0)); + + // Three signed shifts + mIns.Push(NativeCodeInstruction(ins, ASMIT_LSR)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROR, ASMIM_ZERO_PAGE, dreg + 0)); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LSR)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROR, ASMIM_ZERO_PAGE, dreg + 0)); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LSR)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROR, ASMIM_ZERO_PAGE, dreg + 0)); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_EOR, ASMIM_IMMEDIATE, 0x10)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_SEC)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_SBC, ASMIM_IMMEDIATE, 0x10)); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, dreg + 1)); + } + + return dreg; +} + +int NativeCodeBasicBlock::ShortMultiply(InterCodeProcedure* proc, NativeCodeProcedure* nproc, const InterInstruction * ins, const InterInstruction* sins, int index, int mul) +{ + mul &= 0xffff; + + if (ins->mSrc[index].IsUByte() && ins->mSrc[index].mRange.mMaxValue == 1 && mul > 4 && mul < 256) + { + int dreg = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + int sreg = BC_REG_TMP + proc->mTempOffset[ins->mSrc[index].mTemp]; + + if (sins) + { + LoadValueToReg(proc, sins, dreg, nullptr, nullptr); + sreg = dreg; + } + + if (mul == 128) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LSR)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROR)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, dreg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, dreg + 1)); + } + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_SEC)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, dreg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_SBC, ASMIM_ZERO_PAGE, sreg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_AND, ASMIM_IMMEDIATE, mul)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, dreg)); + } + + return dreg; + } + +#if 1 + if (mul >= 0xfffc) + { + int dreg = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + int sreg = BC_REG_TMP + proc->mTempOffset[ins->mSrc[index].mTemp]; + + if (sins) + { + LoadValueToReg(proc, sins, dreg, nullptr, nullptr); + sreg = dreg; + } + + int wreg = BC_REG_ACCU; + if (sreg != dreg) + wreg = dreg; + + switch (mul) + { + case 0xffff: // -1 + mIns.Push(NativeCodeInstruction(ins, ASMIT_SEC)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_SBC, ASMIM_ZERO_PAGE, sreg + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, dreg + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_SBC, ASMIM_ZERO_PAGE, sreg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, dreg + 1)); + break; + case 0xfffe: // -2 + mIns.Push(NativeCodeInstruction(ins, ASMIT_SEC)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_SBC, ASMIM_ZERO_PAGE, sreg + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, dreg + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_SBC, ASMIM_ZERO_PAGE, sreg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_ZERO_PAGE, dreg + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, dreg + 1)); + break; + case 0xfffd: // -3 + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, wreg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_ZERO_PAGE, wreg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, wreg + 1)); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_SEC, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_SBC, ASMIM_ZERO_PAGE, wreg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, dreg + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_SBC, ASMIM_ZERO_PAGE, wreg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, dreg + 1)); + break; + case 0xfffc: // -4 + mIns.Push(NativeCodeInstruction(ins, ASMIT_SEC)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_SBC, ASMIM_ZERO_PAGE, sreg + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, dreg + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_SBC, ASMIM_ZERO_PAGE, sreg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_ZERO_PAGE, dreg + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_ZERO_PAGE, dreg + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, dreg + 1)); + break; + } + + return dreg; + } +#endif + + int lshift = 0, lmul = mul; + while (!(lmul & 1)) + { + lmul >>= 1; + lshift++; + } + + if (mul > 1 && ((lshift & 7) > 3 || lmul != 1) && ins->mSrc[index].IsUByte() && ins->mSrc[index].mRange.mMaxValue < 16) + { + int dreg = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + + if (sins) + { + LoadValueToReg(proc, sins, dreg, nullptr, nullptr); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDX, ASMIM_ZERO_PAGE, dreg)); + } + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDX, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[index].mTemp])); + } + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ABSOLUTE_X, 0, nproc->mGenerator->AllocateShortMulTable(IA_MUL, mul, int(ins->mSrc[index].mRange.mMaxValue) + 1, false))); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, dreg)); + if (ins->mDst.IsUByte()) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, dreg + 1)); + } + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ABSOLUTE_X, 0, nproc->mGenerator->AllocateShortMulTable(IA_MUL, mul, int(ins->mSrc[index].mRange.mMaxValue) + 1, true))); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, dreg + 1)); + } + + return dreg; + } + + if (lmul == 1 && !sins && ins->mSrc[index].mTemp == ins->mDst.mTemp) + { + // shift in place + + int dreg = BC_REG_TMP + proc->mTempOffset[ins->mSrc[index].mTemp]; + + if (ins->mDst.IsUByte()) + { + ShiftRegisterLeftByte(proc, ins, dreg, lshift); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, dreg + 1)); + } + else if (ins->mDst.IsSByte()) + { + // Upper byte will retain sign + ShiftRegisterLeftByte(proc, ins, dreg, lshift); + } + else if (ins->mSrc[index].IsUByte()) + ShiftRegisterLeftFromByte(proc, ins, dreg, lshift, int(ins->mSrc[index].mRange.mMaxValue)); + else + ShiftRegisterLeft(proc, ins, dreg, lshift); + return dreg; + } + + int dreg = BC_REG_ACCU; + + if (sins) + LoadValueToReg(proc, sins, BC_REG_ACCU, nullptr, nullptr); + else if (ins->mSrc[index].mTemp == ins->mDst.mTemp) + dreg = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + else + { + int sreg = BC_REG_TMP + proc->mTempOffset[ins->mSrc[index].mTemp]; + dreg = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, dreg + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, dreg + 1)); + } + + int wreg = BC_REG_ACCU; + if (dreg == BC_REG_ACCU) + wreg = BC_REG_WORK + 4; + + switch (lmul) + { +#if 1 + case 1: + if (ins->mDst.IsUByte()) + { + ShiftRegisterLeftByte(proc, ins, dreg, lshift); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, dreg + 1)); + } + else if (ins->mDst.IsSByte()) + { + // Upper byte will retain sign + ShiftRegisterLeftByte(proc, ins, dreg, lshift); + } + else if (ins->mSrc[index].IsUByte()) + ShiftRegisterLeftFromByte(proc, ins, dreg, lshift, int(ins->mSrc[index].mRange.mMaxValue)); + else + ShiftRegisterLeft(proc, ins, dreg, lshift); + return dreg; + case 3: + if (ins->mSrc[index].IsUByte() && ins->mSrc[index].mRange.mMaxValue <= 85) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, dreg + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ADC, ASMIM_ZERO_PAGE, dreg + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, dreg + 0)); + } + else if (ins->mSrc[index].IsSByte() && ins->mSrc[index].mRange.mMinValue >= -42 && ins->mSrc[index].mRange.mMaxValue <= 42) + { + // Will retain sign + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, dreg + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_CLC, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ADC, ASMIM_ZERO_PAGE, dreg + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, dreg + 0)); + } + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, dreg + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, wreg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, dreg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, wreg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_CLC, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, wreg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ADC, ASMIM_ZERO_PAGE, dreg + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, dreg + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, wreg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ADC, ASMIM_ZERO_PAGE, dreg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, dreg + 1)); + } + if (ins->mDst.IsUByte()) + { + ShiftRegisterLeftByte(proc, ins, dreg, lshift); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, dreg + 1)); + } + else if (ins->mDst.IsSByte()) + { + // Upper byte will retain sign + ShiftRegisterLeftByte(proc, ins, dreg, lshift); + } + else + ShiftRegisterLeft(proc, ins, dreg, lshift); + return dreg; + case 5: + if (ins->mSrc[index].IsUByte() && ins->mSrc[index].mRange.mMaxValue <= 51) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, dreg + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ADC, ASMIM_ZERO_PAGE, dreg + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, dreg + 0)); + + if (ins->mDst.IsUByte()) + { + ShiftRegisterLeftByte(proc, ins, dreg, lshift); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, dreg + 1)); + } + else + ShiftRegisterLeftFromByte(proc, ins, dreg, lshift, int(ins->mSrc[index].mRange.mMaxValue) * 5); + } + else if (ins->mSrc[index].IsSByte() && ins->mSrc[index].mRange.mMinValue >= -25 && ins->mSrc[index].mRange.mMaxValue <= 25) + { + // Will retain sign + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, dreg + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_CLC, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ADC, ASMIM_ZERO_PAGE, dreg + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, dreg + 0)); + + if (ins->mDst.IsSByte()) + { + // Upper byte will retain sign + ShiftRegisterLeftByte(proc, ins, dreg, lshift); + } + else + ShiftRegisterLeft(proc, ins, dreg, lshift); + } + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, dreg + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, wreg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, dreg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_ZERO_PAGE, wreg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, wreg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_CLC, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, wreg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ADC, ASMIM_ZERO_PAGE, dreg + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, dreg + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, wreg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ADC, ASMIM_ZERO_PAGE, dreg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, dreg + 1)); + + if (ins->mDst.IsUByte()) + { + ShiftRegisterLeftByte(proc, ins, dreg, lshift); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, dreg + 1)); + } + else + ShiftRegisterLeft(proc, ins, dreg, lshift); + } + return dreg; + case 7: + if (ins->mSrc[index].IsUByte() && ins->mSrc[index].mRange.mMaxValue < 64) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, dreg + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, wreg + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, dreg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL, ASMIM_IMPLIED)); + } + else if (ins->mSrc[index].IsUByte() && ins->mSrc[index].mRange.mMaxValue < 128) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, dreg + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, wreg + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, dreg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_ZERO_PAGE, wreg + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL, ASMIM_IMPLIED)); + } + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, dreg + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, wreg + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, dreg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_ZERO_PAGE, wreg + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_ZERO_PAGE, wreg + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL, ASMIM_IMPLIED)); + } + + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, wreg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_SEC, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, wreg + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_SBC, ASMIM_ZERO_PAGE, dreg + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, dreg + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, wreg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_SBC, ASMIM_ZERO_PAGE, dreg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, dreg + 1)); + if (ins->mDst.IsUByte()) + { + ShiftRegisterLeftByte(proc, ins, dreg, lshift); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, dreg + 1)); + } + else + ShiftRegisterLeft(proc, ins, dreg, lshift); + return dreg; + case 9: + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, dreg + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, wreg + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, dreg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_ZERO_PAGE, wreg + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_ZERO_PAGE, wreg + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, wreg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_CLC, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, wreg + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ADC, ASMIM_ZERO_PAGE, dreg + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, dreg + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, wreg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ADC, ASMIM_ZERO_PAGE, dreg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, dreg + 1)); + if (ins->mDst.IsUByte()) + { + ShiftRegisterLeftByte(proc, ins, dreg, lshift); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, dreg + 1)); + } + else + ShiftRegisterLeft(proc, ins, dreg, lshift); + return dreg; +#if 1 + case 25: + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, dreg + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_TAX, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, dreg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_TAY, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_CLC, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_TXA, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ADC, ASMIM_ZERO_PAGE, dreg + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_TAX, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_TYA, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ADC, ASMIM_ZERO_PAGE, dreg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, wreg + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_TXA, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL, ASMIM_ZERO_PAGE, wreg + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL, ASMIM_ZERO_PAGE, wreg + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL, ASMIM_ZERO_PAGE, wreg + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_CLC, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ADC, ASMIM_ZERO_PAGE, dreg + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, dreg + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, wreg + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ADC, ASMIM_ZERO_PAGE, dreg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, dreg + 1)); + ShiftRegisterLeft(proc, ins, dreg, lshift); + return dreg; +#endif +#endif + default: + if (mul & 0xff00) + { + if (ins->mSrc[index].IsUByte()) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, dreg)); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDX, ASMIM_IMMEDIATE, mul & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STX, ASMIM_ZERO_PAGE, BC_REG_ACCU + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDX, ASMIM_IMMEDIATE, mul >> 8)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STX, ASMIM_ZERO_PAGE, BC_REG_ACCU + 1)); + + NativeCodeGenerator::Runtime& rt(nproc->mGenerator->ResolveRuntime(Ident::Unique("mul16by8"))); + mIns.Push(NativeCodeInstruction(ins, ASMIT_JSR, ASMIM_ABSOLUTE, rt.mOffset, rt.mLinkerObject, NCIF_RUNTIME | NCIF_LOWER | NCIF_UPPER | NCIF_USE_CPU_REG_A)); + + return BC_REG_ACCU; + } + else + { + if (dreg != BC_REG_ACCU) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, dreg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, dreg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 1)); + } + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, mul & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_WORK + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, mul >> 8)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_WORK + 1)); + + NativeCodeGenerator::Runtime& frt(nproc->mGenerator->ResolveRuntime(Ident::Unique("mul16"))); + mIns.Push(NativeCodeInstruction(ins, ASMIT_JSR, ASMIM_ABSOLUTE, frt.mOffset, frt.mLinkerObject, NCIF_RUNTIME | NCIF_LOWER | NCIF_UPPER)); + } + } + else + { + if (dreg != BC_REG_ACCU) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, dreg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, dreg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 1)); + } + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, mul)); +// mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_WORK + 0)); + + NativeCodeGenerator::Runtime& rt(nproc->mGenerator->ResolveRuntime(Ident::Unique("mul16by8"))); + mIns.Push(NativeCodeInstruction(ins, ASMIT_JSR, ASMIM_ABSOLUTE, rt.mOffset, rt.mLinkerObject, NCIF_RUNTIME | NCIF_LOWER | NCIF_UPPER | NCIF_USE_CPU_REG_A)); + + return BC_REG_ACCU; + } + + return BC_REG_WORK + 2; + } +} + + +void NativeCodeBasicBlock::AddAsrSignedByte(InterCodeProcedure* proc, const InterInstruction* ains, const InterInstruction* sins) +{ + mIns.Push(NativeCodeInstruction(ains, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ains->mSrc[1].mTemp])); + + for (int i = 0; i < sins->mSrc[0].mIntConst; i++) + { + mIns.Push(NativeCodeInstruction(sins, ASMIT_CMP, ASMIM_IMMEDIATE, 0x80)); + mIns.Push(NativeCodeInstruction(sins, ASMIT_ROR)); + } + mIns.Push(NativeCodeInstruction(sins, ASMIT_ADC, ASMIM_IMMEDIATE, 0x00)); + mIns.Push(NativeCodeInstruction(sins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[sins->mDst.mTemp])); + + mIns.Push(NativeCodeInstruction(sins, ASMIT_ASL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(sins, ASMIT_LDA, ASMIM_IMMEDIATE, 0x00)); + mIns.Push(NativeCodeInstruction(sins, ASMIT_ADC, ASMIM_IMMEDIATE, 0xff)); + mIns.Push(NativeCodeInstruction(sins, ASMIT_EOR, ASMIM_IMMEDIATE, 0xff)); + mIns.Push(NativeCodeInstruction(sins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[sins->mDst.mTemp] + 1)); + +} + +void NativeCodeBasicBlock::BinaryShlAddConstPair(InterCodeProcedure* proc, const InterInstruction* sins, const InterInstruction* ains) +{ + int64 aconst = ains->mSrc[0].mIntConst; + if (ains->mOperator == IA_SUB) + aconst = -aconst; + + int di = BC_REG_TMP + proc->mTempOffset[ains->mDst.mTemp]; + int shift = sins->mSrc[0].mIntConst & 15; + + if (shift >= 8) + { + mIns.Push(NativeCodeInstruction(sins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[sins->mSrc[1].mTemp])); + switch (shift) + { + case 9: + case 10: + case 11: + case 12: + case 13: + for(int i=8; i> 8) & 0xff)); + mIns.Push(NativeCodeInstruction(sins, ASMIT_STA, ASMIM_ZERO_PAGE, di + 1)); + mIns.Push(NativeCodeInstruction(ains, ASMIT_LDA, ASMIM_IMMEDIATE, aconst & 0xff)); + mIns.Push(NativeCodeInstruction(sins, ASMIT_STA, ASMIM_ZERO_PAGE, di)); + } + else + { + char mask = (1 << shift) - 1; + + mIns.Push(NativeCodeInstruction(sins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[sins->mSrc[1].mTemp])); + switch (shift) + { + case 1: + case 2: + case 3: + case 4: + case 5: + mIns.Push(NativeCodeInstruction(sins, ASMIT_CLC, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(sins, ASMIT_ADC, ASMIM_IMMEDIATE, (aconst >> shift) & 0xff)); + for(int i=0; i> (shift + 8) << shift) & 0xff)); + mIns.Push(NativeCodeInstruction(sins, ASMIT_STA, ASMIM_ZERO_PAGE, di + 1)); + break; + case 6: + mIns.Push(NativeCodeInstruction(sins, ASMIT_LSR, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(sins, ASMIT_ROR, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(sins, ASMIT_TAX, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(sins, ASMIT_AND, ASMIM_IMMEDIATE, 0x80)); + mIns.Push(NativeCodeInstruction(sins, ASMIT_ROR, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(sins, ASMIT_CLC, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(sins, ASMIT_ADC, ASMIM_IMMEDIATE, aconst & 0xff)); + mIns.Push(NativeCodeInstruction(sins, ASMIT_STA, ASMIM_ZERO_PAGE, di)); + mIns.Push(NativeCodeInstruction(sins, ASMIT_TXA, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(sins, ASMIT_AND, ASMIM_IMMEDIATE, 0x3f)); + mIns.Push(NativeCodeInstruction(sins, ASMIT_ADC, ASMIM_IMMEDIATE, (aconst >> 8) & 0xff)); + mIns.Push(NativeCodeInstruction(sins, ASMIT_STA, ASMIM_ZERO_PAGE, di + 1)); + break; + case 7: + mIns.Push(NativeCodeInstruction(sins, ASMIT_LSR, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(sins, ASMIT_TAX, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(sins, ASMIT_LDA, ASMIM_IMMEDIATE, 0x00)); + mIns.Push(NativeCodeInstruction(sins, ASMIT_ROR, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(sins, ASMIT_CLC, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(sins, ASMIT_ADC, ASMIM_IMMEDIATE, aconst & 0xff)); + mIns.Push(NativeCodeInstruction(sins, ASMIT_STA, ASMIM_ZERO_PAGE, di)); + mIns.Push(NativeCodeInstruction(sins, ASMIT_TXA, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(sins, ASMIT_ADC, ASMIM_IMMEDIATE, (aconst >> 8) & 0xff)); + mIns.Push(NativeCodeInstruction(sins, ASMIT_STA, ASMIM_ZERO_PAGE, di + 1)); + break; + } + } +} + +void NativeCodeBasicBlock::BinaryAddCarry(InterCodeProcedure* proc, const InterInstruction* ains, const InterInstruction* cins) +{ + if (cins->mSrc[0].mTemp == ains->mDst.mTemp) + mIns.Push(NativeCodeInstruction(cins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[cins->mSrc[1].mTemp])); + else if (cins->mSrc[0].mTemp >= 0) + mIns.Push(NativeCodeInstruction(cins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[cins->mSrc[0].mTemp])); + else + mIns.Push(NativeCodeInstruction(cins, ASMIT_LDA, ASMIM_IMMEDIATE, cins->mSrc[0].mIntConst)); + mIns.Push(NativeCodeInstruction(cins, ASMIT_LSR)); + + mIns.Push(NativeCodeInstruction(ains, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ains->mSrc[0].mTemp])); + mIns.Push(NativeCodeInstruction(ains, ASMIT_ADC, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ains->mSrc[1].mTemp])); + mIns.Push(NativeCodeInstruction(ains, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[cins->mDst.mTemp])); + mIns.Push(NativeCodeInstruction(ains, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + mIns.Push(NativeCodeInstruction(ains, ASMIT_ROL)); + mIns.Push(NativeCodeInstruction(ains, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[cins->mDst.mTemp] + 1)); +} + +void NativeCodeBasicBlock::BinaryAndMulConst(InterCodeProcedure* proc, const InterInstruction* ains, const InterInstruction* mins) +{ + unsigned mask = ains->mSrc[0].mIntConst & 0xff; + unsigned mul = unsigned(mins->mSrc[0].mIntConst); + unsigned vmax = unsigned(mins->mSrc[1].mRange.mMaxValue); + unsigned shift = 0; + + int src = BC_REG_TMP + proc->mTempOffset[ains->mSrc[1].mTemp]; + int dst = BC_REG_TMP + proc->mTempOffset[mins->mDst.mTemp]; + int tmp = BC_REG_TMP + proc->mTempOffset[ains->mDst.mTemp]; + + while (!(mul & 1)) + { + shift++; + mul >>= 1; + } + + switch (mul) + { + case 1: + mIns.Push(NativeCodeInstruction(ains, ASMIT_LDA, ASMIM_ZERO_PAGE, src)); + mIns.Push(NativeCodeInstruction(ains, ASMIT_AND, ASMIM_IMMEDIATE, mask)); + mIns.Push(NativeCodeInstruction(ains, ASMIT_STA, ASMIM_ZERO_PAGE, dst)); + ShiftRegisterLeftFromByte(proc, mins, dst, shift, vmax); + return; + case 3: + if ((mask & 1) == 0) + { + mIns.Push(NativeCodeInstruction(ains, ASMIT_LDA, ASMIM_ZERO_PAGE, src)); + mIns.Push(NativeCodeInstruction(ains, ASMIT_AND, ASMIM_IMMEDIATE, mask)); + mIns.Push(NativeCodeInstruction(ains, ASMIT_STA, ASMIM_ZERO_PAGE, dst)); + mIns.Push(NativeCodeInstruction(mins, ASMIT_LSR, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(mins, ASMIT_ADC, ASMIM_ZERO_PAGE, dst)); + mIns.Push(NativeCodeInstruction(ains, ASMIT_STA, ASMIM_ZERO_PAGE, dst)); + if (vmax <= 170) + ShiftRegisterLeftFromByte(proc, mins, dst, shift + 1, vmax + (vmax >> 1)); + else + { + mIns.Push(NativeCodeInstruction(mins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + mIns.Push(NativeCodeInstruction(mins, ASMIT_ROL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ains, ASMIT_STA, ASMIM_ZERO_PAGE, dst + 1)); + ShiftRegisterLeft(proc, mins, dst, shift + 1); + } + return; + } + break; + case 5: + if ((mask & 3) == 0) + { + mIns.Push(NativeCodeInstruction(ains, ASMIT_LDA, ASMIM_ZERO_PAGE, src)); + mIns.Push(NativeCodeInstruction(ains, ASMIT_AND, ASMIM_IMMEDIATE, mask)); + mIns.Push(NativeCodeInstruction(ains, ASMIT_STA, ASMIM_ZERO_PAGE, dst)); + mIns.Push(NativeCodeInstruction(mins, ASMIT_LSR, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(mins, ASMIT_LSR, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(mins, ASMIT_ADC, ASMIM_ZERO_PAGE, dst)); + mIns.Push(NativeCodeInstruction(ains, ASMIT_STA, ASMIM_ZERO_PAGE, dst)); + if (vmax <= 204) + ShiftRegisterLeftFromByte(proc, mins, dst, shift + 2, vmax + (vmax >> 2)); + else + { + mIns.Push(NativeCodeInstruction(mins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + mIns.Push(NativeCodeInstruction(mins, ASMIT_ROL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ains, ASMIT_STA, ASMIM_ZERO_PAGE, dst + 1)); + ShiftRegisterLeft(proc, mins, dst, shift + 2); + } + return; + } + break; + case 7: + if ((mask & 7) == 0 && vmax <= 144) + { + mIns.Push(NativeCodeInstruction(ains, ASMIT_LDA, ASMIM_ZERO_PAGE, src)); + mIns.Push(NativeCodeInstruction(ains, ASMIT_AND, ASMIM_IMMEDIATE, mask)); + mIns.Push(NativeCodeInstruction(ains, ASMIT_STA, ASMIM_ZERO_PAGE, dst)); + mIns.Push(NativeCodeInstruction(mins, ASMIT_LSR, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(mins, ASMIT_ADC, ASMIM_ZERO_PAGE, dst)); + mIns.Push(NativeCodeInstruction(mins, ASMIT_LSR, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(mins, ASMIT_ADC, ASMIM_ZERO_PAGE, dst)); + mIns.Push(NativeCodeInstruction(ains, ASMIT_STA, ASMIM_ZERO_PAGE, dst)); + ShiftRegisterLeftFromByte(proc, mins, dst, shift + 2, vmax + (vmax >> 1) + (vmax >> 2)); + return; + } + break; + } + + mIns.Push(NativeCodeInstruction(ains, ASMIT_LDA, ASMIM_ZERO_PAGE, src)); + mIns.Push(NativeCodeInstruction(ains, ASMIT_AND, ASMIM_IMMEDIATE, mask)); + mIns.Push(NativeCodeInstruction(ains, ASMIT_STA, ASMIM_ZERO_PAGE, tmp)); + mIns.Push(NativeCodeInstruction(mins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + mIns.Push(NativeCodeInstruction(ains, ASMIT_STA, ASMIM_ZERO_PAGE, tmp + 1)); + + int reg = ShortMultiply(proc, mProc, mins, nullptr, 1, int(mins->mSrc[0].mIntConst)); + + mIns.Push(NativeCodeInstruction(ains, ASMIT_LDA, ASMIM_ZERO_PAGE, reg + 0)); + mIns.Push(NativeCodeInstruction(ains, ASMIT_STA, ASMIM_ZERO_PAGE, dst + 0)); + mIns.Push(NativeCodeInstruction(ains, ASMIT_LDA, ASMIM_ZERO_PAGE, reg + 1)); + mIns.Push(NativeCodeInstruction(ains, ASMIT_STA, ASMIM_ZERO_PAGE, dst + 1)); +} + +void NativeCodeBasicBlock::BinaryDivModPair(InterCodeProcedure* proc, NativeCodeProcedure* nproc, const InterInstruction* ins1, const InterInstruction* ins2, bool sign) +{ + if (ins1->mSrc[1].mTemp < 0) + { + mIns.Push(NativeCodeInstruction(ins1, ASMIT_LDA, ASMIM_IMMEDIATE, ins1->mSrc[1].mIntConst & 0xff)); + mIns.Push(NativeCodeInstruction(ins1, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 0)); + mIns.Push(NativeCodeInstruction(ins1, ASMIT_LDA, ASMIM_IMMEDIATE, (ins1->mSrc[1].mIntConst >> 8) & 0xff)); + mIns.Push(NativeCodeInstruction(ins1, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 1)); + if (ins1->mDst.mType == IT_INT32) + { + mIns.Push(NativeCodeInstruction(ins1, ASMIT_LDA, ASMIM_IMMEDIATE, (ins1->mSrc[1].mIntConst >> 16) & 0xff)); + mIns.Push(NativeCodeInstruction(ins1, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 2)); + mIns.Push(NativeCodeInstruction(ins1, ASMIT_LDA, ASMIM_IMMEDIATE, (ins1->mSrc[1].mIntConst >> 24) & 0xff)); + mIns.Push(NativeCodeInstruction(ins1, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 3)); + } + } + else + { + mIns.Push(NativeCodeInstruction(ins1, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins1->mSrc[1].mTemp])); + mIns.Push(NativeCodeInstruction(ins1, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 0)); + mIns.Push(NativeCodeInstruction(ins1, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins1->mSrc[1].mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins1, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 1)); + if (ins1->mDst.mType == IT_INT32) + { + mIns.Push(NativeCodeInstruction(ins1, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins1->mSrc[1].mTemp] + 2)); + mIns.Push(NativeCodeInstruction(ins1, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 2)); + mIns.Push(NativeCodeInstruction(ins1, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins1->mSrc[1].mTemp] + 3)); + mIns.Push(NativeCodeInstruction(ins1, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 3)); + } + } + + if (!sign && ins1->mSrc[0].IsUByte() && ins1->mDst.mType == IT_INT16) + { + if (ins1->mSrc[0].mTemp < 0) + mIns.Push(NativeCodeInstruction(ins1, ASMIT_LDA, ASMIM_IMMEDIATE, ins1->mSrc[0].mIntConst & 0xff)); + else + mIns.Push(NativeCodeInstruction(ins1, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins1->mSrc[0].mTemp])); + + NativeCodeGenerator::Runtime& frt(nproc->mGenerator->ResolveRuntime(Ident::Unique("divu16by8"))); + mIns.Push(NativeCodeInstruction(ins1, ASMIT_JSR, ASMIM_ABSOLUTE, frt.mOffset, frt.mLinkerObject, NCIF_RUNTIME | NCIF_LOWER | NCIF_UPPER | NCIF_USE_CPU_REG_A)); + } + else + { + if (ins1->mSrc[0].mTemp < 0) + { + mIns.Push(NativeCodeInstruction(ins1, ASMIT_LDA, ASMIM_IMMEDIATE, ins1->mSrc[0].mIntConst & 0xff)); + mIns.Push(NativeCodeInstruction(ins1, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_WORK + 0)); + mIns.Push(NativeCodeInstruction(ins1, ASMIT_LDA, ASMIM_IMMEDIATE, (ins1->mSrc[0].mIntConst >> 8) & 0xff)); + mIns.Push(NativeCodeInstruction(ins1, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_WORK + 1)); + if (ins1->mDst.mType == IT_INT32) + { + mIns.Push(NativeCodeInstruction(ins1, ASMIT_LDA, ASMIM_IMMEDIATE, (ins1->mSrc[0].mIntConst >> 16) & 0xff)); + mIns.Push(NativeCodeInstruction(ins1, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_WORK + 2)); + mIns.Push(NativeCodeInstruction(ins1, ASMIT_LDA, ASMIM_IMMEDIATE, (ins1->mSrc[0].mIntConst >> 24) & 0xff)); + mIns.Push(NativeCodeInstruction(ins1, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_WORK + 3)); + } + } + else + { + mIns.Push(NativeCodeInstruction(ins1, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins1->mSrc[0].mTemp])); + mIns.Push(NativeCodeInstruction(ins1, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_WORK + 0)); + mIns.Push(NativeCodeInstruction(ins1, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins1->mSrc[0].mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins1, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_WORK + 1)); + if (ins1->mDst.mType == IT_INT32) + { + mIns.Push(NativeCodeInstruction(ins1, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins1->mSrc[0].mTemp] + 2)); + mIns.Push(NativeCodeInstruction(ins1, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_WORK + 2)); + mIns.Push(NativeCodeInstruction(ins1, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins1->mSrc[0].mTemp] + 3)); + mIns.Push(NativeCodeInstruction(ins1, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_WORK + 3)); + } + } + + if (sign) + { + if (ins1->mDst.mType == IT_INT32) + { + NativeCodeGenerator::Runtime& frt(nproc->mGenerator->ResolveRuntime(Ident::Unique("divmods32"))); + mIns.Push(NativeCodeInstruction(ins1, ASMIT_JSR, ASMIM_ABSOLUTE, frt.mOffset, frt.mLinkerObject, NCIF_RUNTIME | NCIF_LOWER | NCIF_UPPER)); + } + else + { + NativeCodeGenerator::Runtime& frt(nproc->mGenerator->ResolveRuntime(Ident::Unique("divmods16"))); + mIns.Push(NativeCodeInstruction(ins1, ASMIT_JSR, ASMIM_ABSOLUTE, frt.mOffset, frt.mLinkerObject, NCIF_RUNTIME | NCIF_LOWER | NCIF_UPPER)); + } + } + else + { + if (ins1->mDst.mType == IT_INT32) + { + NativeCodeGenerator::Runtime& frt(nproc->mGenerator->ResolveRuntime(Ident::Unique("divu32"))); + mIns.Push(NativeCodeInstruction(ins1, ASMIT_JSR, ASMIM_ABSOLUTE, frt.mOffset, frt.mLinkerObject, NCIF_RUNTIME | NCIF_LOWER | NCIF_UPPER)); + } + else + { + NativeCodeGenerator::Runtime& frt(nproc->mGenerator->ResolveRuntime(Ident::Unique("divu16"))); + mIns.Push(NativeCodeInstruction(ins1, ASMIT_JSR, ASMIM_ABSOLUTE, frt.mOffset, frt.mLinkerObject, NCIF_RUNTIME | NCIF_LOWER | NCIF_UPPER)); + } + } + } + + mIns.Push(NativeCodeInstruction(ins1, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 0)); + mIns.Push(NativeCodeInstruction(ins1, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins1->mDst.mTemp])); + mIns.Push(NativeCodeInstruction(ins1, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 1)); + mIns.Push(NativeCodeInstruction(ins1, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins1->mDst.mTemp] + 1)); + if (ins1->mDst.mType == IT_INT32) + { + mIns.Push(NativeCodeInstruction(ins1, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 2)); + mIns.Push(NativeCodeInstruction(ins1, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins1->mDst.mTemp] + 2)); + mIns.Push(NativeCodeInstruction(ins1, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 3)); + mIns.Push(NativeCodeInstruction(ins1, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins1->mDst.mTemp] + 3)); + } + + if (ins2->mDst.mType == IT_INT32) + { + mIns.Push(NativeCodeInstruction(ins2, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_WORK + 4)); + mIns.Push(NativeCodeInstruction(ins2, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins2->mDst.mTemp])); + mIns.Push(NativeCodeInstruction(ins2, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_WORK + 5)); + mIns.Push(NativeCodeInstruction(ins2, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins2->mDst.mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins2, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_WORK + 6)); + mIns.Push(NativeCodeInstruction(ins2, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins2->mDst.mTemp] + 2)); + mIns.Push(NativeCodeInstruction(ins2, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_WORK + 7)); + mIns.Push(NativeCodeInstruction(ins2, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins2->mDst.mTemp] + 3)); + } + else + { + mIns.Push(NativeCodeInstruction(ins2, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_WORK + 2)); + mIns.Push(NativeCodeInstruction(ins2, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins2->mDst.mTemp])); + mIns.Push(NativeCodeInstruction(ins2, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_WORK + 3)); + mIns.Push(NativeCodeInstruction(ins2, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins2->mDst.mTemp] + 1)); + } +} + +NativeCodeBasicBlock* NativeCodeBasicBlock::BinaryOperator(InterCodeProcedure* proc, NativeCodeProcedure* nproc, const InterInstruction * ins, const InterInstruction * sins1, const InterInstruction * sins0) +{ + int treg = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + + if (ins->mDst.mType == IT_FLOAT) + { + int sop0 = 0, sop1 = 1; + bool flipop = false; + bool changedSign = false; + + if (ins->mOperator == IA_ADD || ins->mOperator == IA_MUL || ins->mOperator == IA_SUB) + { + if (!sins0 && ins->mSrc[sop0].mTemp >= 0 && CheckPredAccuStore(BC_REG_TMP + proc->mTempOffset[ins->mSrc[sop0].mTemp])) + { + flipop = true; + sop0 = 1; sop1 = 0; + const InterInstruction* sins = sins0; sins0 = sins1; sins1 = sins; + } + else if (!sins1 && !sins0 && ins->mSrc[sop0].mTemp < 0 && ins->mSrc[sop1].mTemp >= 0 && !CheckPredAccuStore(BC_REG_TMP + proc->mTempOffset[ins->mSrc[sop1].mTemp])) + { + flipop = true; + sop0 = 1; sop1 = 0; + const InterInstruction* sins = sins0; sins0 = sins1; sins1 = sins; + } + else if (!sins1 && !sins0 && ins->mSrc[sop1].mTemp >= 0 && CheckPredAccuStore(BC_REG_TMP + proc->mTempOffset[ins->mSrc[sop1].mTemp]) && ins->mSrc[sop1].mFinal) + { + // no flip in this case + } + else if (ins->mLockOrder) + { + printf("LockedOrder %d, %d\n", ins->mSrc[sop0].mTemp, ins->mSrc[sop1].mTemp); + // maintain order due to loop + } + else if (!sins0 && !sins1 && ins->mSrc[sop0].mTemp >= 0 && ins->mSrc[sop1].mTemp >= 0 && ins->mDst.mTemp == ins->mSrc[sop0].mTemp) + { + flipop = true; + sop0 = 1; sop1 = 0; + } + } + + int sreg0 = ins->mSrc[sop0].mTemp < 0 ? -1 : BC_REG_TMP + proc->mTempOffset[ins->mSrc[sop0].mTemp]; + + if (ins->mSrc[sop1].mTemp >= 0 && ins->mSrc[sop0].mTemp >= 0 && sins1 && sins1 == sins0) + { + LoadValueToReg(proc, sins1, BC_REG_ACCU, nullptr, nullptr); + NativeCodeGenerator::Runtime& frt(nproc->mGenerator->ResolveRuntime(Ident::Unique("fsplitx"))); + mIns.Push(NativeCodeInstruction(ins, ASMIT_JSR, ASMIM_ABSOLUTE, frt.mOffset, frt.mLinkerObject, NCIF_RUNTIME | NCIF_LOWER | NCIF_UPPER | NCIF_USE_ZP_32_X, BC_REG_ACCU)); + } + else + { + if (ins->mSrc[sop1].mTemp < 0) + { + union { float f; unsigned int v; } cc; + + if (ins->mOperator == IA_SUB && flipop) + { + changedSign = true; + cc.f = float(-ins->mSrc[sop1].mFloatConst); + } + else + cc.f = float(ins->mSrc[sop1].mFloatConst); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, cc.v & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (cc.v >> 8) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (cc.v >> 16) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (cc.v >> 24) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 3)); + } + else if (sins1) + { + LoadValueToReg(proc, sins1, BC_REG_ACCU, nullptr, nullptr); + } + else if (CheckPredAccuStore(BC_REG_TMP + proc->mTempOffset[ins->mSrc[sop1].mTemp])) + { + if (ins->mSrc[sop1].mFinal) + { + // cull previous store from accu to temp using direcrt forwarding + mIns.SetSize(mIns.Size() - 8); + } + if (sreg0 == BC_REG_TMP + proc->mTempOffset[ins->mSrc[sop1].mTemp]) + sreg0 = BC_REG_ACCU; + } + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[sop1].mTemp] + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[sop1].mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[sop1].mTemp] + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[sop1].mTemp] + 3)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 3)); + } + + if (ins->mSrc[sop0].mTemp < 0) + { + union { float f; unsigned int v; } cc; + + if (ins->mOperator == IA_SUB && !flipop) + { + changedSign = true; + cc.f = float(-ins->mSrc[sop0].mFloatConst); + } + else + cc.f = float(ins->mSrc[sop0].mFloatConst); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, cc.v & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_WORK + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (cc.v >> 8) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_WORK + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (cc.v >> 16) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_WORK + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (cc.v >> 24) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_WORK + 3)); + + NativeCodeGenerator::Runtime& frt(nproc->mGenerator->ResolveRuntime(Ident::Unique("fsplitt"))); + mIns.Push(NativeCodeInstruction(ins, ASMIT_JSR, ASMIM_ABSOLUTE, frt.mOffset, frt.mLinkerObject, NCIF_RUNTIME | NCIF_LOWER | NCIF_UPPER)); + } + else if (sins0) + { + LoadValueToReg(proc, sins0, BC_REG_WORK, nullptr, nullptr); + NativeCodeGenerator::Runtime& frt(nproc->mGenerator->ResolveRuntime(Ident::Unique("fsplitt"))); + mIns.Push(NativeCodeInstruction(ins, ASMIT_JSR, ASMIM_ABSOLUTE, frt.mOffset, frt.mLinkerObject, NCIF_RUNTIME | NCIF_LOWER | NCIF_UPPER)); + } + else + { + NativeCodeGenerator::Runtime& frt(nproc->mGenerator->ResolveRuntime(Ident::Unique("fsplitx"))); + mIns.Push(NativeCodeInstruction(ins, ASMIT_JSR, ASMIM_ABSOLUTE, frt.mOffset, frt.mLinkerObject, NCIF_RUNTIME | NCIF_LOWER | NCIF_UPPER | NCIF_USE_ZP_32_X, sreg0)); + } + } + + + switch (ins->mOperator) + { + case IA_ADD: + { + NativeCodeGenerator::Runtime& art(nproc->mGenerator->ResolveRuntime(Ident::Unique("fadd"))); + mIns.Push(NativeCodeInstruction(ins, ASMIT_JSR, ASMIM_ABSOLUTE, art.mOffset, art.mLinkerObject, NCIF_RUNTIME | NCIF_LOWER | NCIF_UPPER)); + } break; + case IA_SUB: + { + bool add = false; + if (changedSign) + add = true; + else if (flipop) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 3)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_EOR, ASMIM_IMMEDIATE, 0x80)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 3)); + add = true; + } + + NativeCodeGenerator::Runtime& art(nproc->mGenerator->ResolveRuntime(Ident::Unique(add ? "fadd" : "fsub"))); + mIns.Push(NativeCodeInstruction(ins, ASMIT_JSR, ASMIM_ABSOLUTE, art.mOffset, art.mLinkerObject, NCIF_RUNTIME | NCIF_LOWER | NCIF_UPPER)); + } break; + case IA_MUL: + { + NativeCodeGenerator::Runtime& art(nproc->mGenerator->ResolveRuntime(Ident::Unique("fmul"))); + mIns.Push(NativeCodeInstruction(ins, ASMIT_JSR, ASMIM_ABSOLUTE, art.mOffset, art.mLinkerObject, NCIF_RUNTIME | NCIF_LOWER | NCIF_UPPER)); + } break; + case IA_DIVS: + case IA_DIVU: + { + NativeCodeGenerator::Runtime& art(nproc->mGenerator->ResolveRuntime(Ident::Unique("fdiv"))); + mIns.Push(NativeCodeInstruction(ins, ASMIT_JSR, ASMIM_ABSOLUTE, art.mOffset, art.mLinkerObject, NCIF_RUNTIME | NCIF_LOWER | NCIF_UPPER)); + } break; + } + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 3)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + 3)); + } + else if (ins->mDst.mType == IT_INT32) + { + switch (ins->mOperator) + { + case IA_ADD: + case IA_SUB: + case IA_OR: + case IA_AND: + case IA_XOR: + { + if (sins1 && sins0 && sins1->mDst.mTemp == sins0->mSrc[0].mTemp) + { + LoadValueToReg(proc, sins0, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp], nullptr, nullptr); + LoadValueToReg(proc, sins1, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp], nullptr, nullptr); + } + else + { + if (sins1) LoadValueToReg(proc, sins1, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp], nullptr, nullptr); + if (sins0) LoadValueToReg(proc, sins0, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp], nullptr, nullptr); + } + + AsmInsType atype; + switch (ins->mOperator) + { + case IA_ADD: + mIns.Push(NativeCodeInstruction(ins, ASMIT_CLC, ASMIM_IMPLIED)); + atype = ASMIT_ADC; + break; + case IA_SUB: + mIns.Push(NativeCodeInstruction(ins, ASMIT_SEC, ASMIM_IMPLIED)); + atype = ASMIT_SBC; + break; + case IA_OR: + atype = ASMIT_ORA; + break; + case IA_AND: + atype = ASMIT_AND; + break; + case IA_XOR: + atype = ASMIT_EOR; + break; + } + + for (int i = 0; i < 4; i++) + { + if (ins->mSrc[1].mTemp < 0) + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (ins->mSrc[1].mIntConst >> (8 * i)) & 0xff)); + else + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp] + i)); + if (ins->mSrc[0].mTemp < 0) + mIns.Push(NativeCodeInstruction(ins, atype, ASMIM_IMMEDIATE, (ins->mSrc[0].mIntConst >> (8 * i)) & 0xff)); + else + mIns.Push(NativeCodeInstruction(ins, atype, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + i)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + i)); + } + } break; + + case IA_MUL: + case IA_DIVS: + case IA_MODS: + case IA_DIVU: + case IA_MODU: + { + int reg = BC_REG_ACCU; + + if (ins->mOperator == IA_MUL && ins->mSrc[0].IsUByte()) + { + if (sins1) + LoadValueToReg(proc, sins1, BC_REG_ACCU, nullptr, nullptr); + else if (ins->mSrc[1].mTemp < 0) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, ins->mSrc[1].mIntConst & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (ins->mSrc[1].mIntConst >> 8) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (ins->mSrc[1].mIntConst >> 16) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (ins->mSrc[1].mIntConst >> 24) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 3)); + } + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp] + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp] + 3)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 3)); + } + + if (sins0) + { + LoadValueToReg(proc, sins0, BC_REG_WORK, nullptr, nullptr); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_WORK + 0)); + } + else if (ins->mSrc[0].mTemp < 0) + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, ins->mSrc[0].mIntConst & 0xff)); + else + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp])); + + NativeCodeGenerator::Runtime& frt(nproc->mGenerator->ResolveRuntime(Ident::Unique("mul32by8"))); + mIns.Push(NativeCodeInstruction(ins, ASMIT_JSR, ASMIM_ABSOLUTE, frt.mOffset, frt.mLinkerObject, NCIF_RUNTIME | NCIF_LOWER | NCIF_UPPER | NCIF_USE_CPU_REG_A)); + reg = BC_REG_WORK + 4; + } + else if (ins->mOperator == IA_MUL && ins->mSrc[1].IsUByte()) + { + if (sins0) + LoadValueToReg(proc, sins0, BC_REG_ACCU, nullptr, nullptr); + else if (ins->mSrc[0].mTemp < 0) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, ins->mSrc[0].mIntConst & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (ins->mSrc[0].mIntConst >> 8) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (ins->mSrc[0].mIntConst >> 16) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (ins->mSrc[0].mIntConst >> 24) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 3)); + } + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 3)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 3)); + } + + if (sins1) + { + LoadValueToReg(proc, sins1, BC_REG_WORK, nullptr, nullptr); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_WORK + 0)); + } + else if (ins->mSrc[1].mTemp < 0) + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, ins->mSrc[1].mIntConst & 0xff)); + else + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp])); + + NativeCodeGenerator::Runtime& frt(nproc->mGenerator->ResolveRuntime(Ident::Unique("mul32by8"))); + mIns.Push(NativeCodeInstruction(ins, ASMIT_JSR, ASMIM_ABSOLUTE, frt.mOffset, frt.mLinkerObject, NCIF_RUNTIME | NCIF_LOWER | NCIF_UPPER | NCIF_USE_CPU_REG_A)); + reg = BC_REG_WORK + 4; + } + else + { + if (sins1) + LoadValueToReg(proc, sins1, BC_REG_ACCU, nullptr, nullptr); + else if (ins->mSrc[1].mTemp < 0) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, ins->mSrc[1].mIntConst & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (ins->mSrc[1].mIntConst >> 8) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (ins->mSrc[1].mIntConst >> 16) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (ins->mSrc[1].mIntConst >> 24) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 3)); + } + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp] + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp] + 3)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 3)); + } + + if (sins0) + LoadValueToReg(proc, sins0, BC_REG_WORK, nullptr, nullptr); + else if (ins->mSrc[0].mTemp < 0) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, ins->mSrc[0].mIntConst & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_WORK + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (ins->mSrc[0].mIntConst >> 8) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_WORK + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (ins->mSrc[0].mIntConst >> 16) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_WORK + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (ins->mSrc[0].mIntConst >> 24) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_WORK + 3)); + } + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_WORK + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_WORK + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_WORK + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 3)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_WORK + 3)); + } + + switch (ins->mOperator) + { + case IA_MUL: + { + NativeCodeGenerator::Runtime& frt(nproc->mGenerator->ResolveRuntime(Ident::Unique("mul32"))); + mIns.Push(NativeCodeInstruction(ins, ASMIT_JSR, ASMIM_ABSOLUTE, frt.mOffset, frt.mLinkerObject, NCIF_RUNTIME | NCIF_LOWER | NCIF_UPPER)); + reg = BC_REG_WORK + 4; + } break; + case IA_DIVS: + { + NativeCodeGenerator::Runtime& frt(nproc->mGenerator->ResolveRuntime(Ident::Unique("divs32"))); + mIns.Push(NativeCodeInstruction(ins, ASMIT_JSR, ASMIM_ABSOLUTE, frt.mOffset, frt.mLinkerObject, NCIF_RUNTIME | NCIF_LOWER | NCIF_UPPER)); + } break; + case IA_MODS: + { + NativeCodeGenerator::Runtime& frt(nproc->mGenerator->ResolveRuntime(Ident::Unique("mods32"))); + mIns.Push(NativeCodeInstruction(ins, ASMIT_JSR, ASMIM_ABSOLUTE, frt.mOffset, frt.mLinkerObject, NCIF_RUNTIME | NCIF_LOWER | NCIF_UPPER)); + reg = BC_REG_WORK + 4; + } break; + case IA_DIVU: + { + NativeCodeGenerator::Runtime& frt(nproc->mGenerator->ResolveRuntime(Ident::Unique("divu32"))); + mIns.Push(NativeCodeInstruction(ins, ASMIT_JSR, ASMIM_ABSOLUTE, frt.mOffset, frt.mLinkerObject, NCIF_RUNTIME | NCIF_LOWER | NCIF_UPPER)); + } break; + case IA_MODU: + { + NativeCodeGenerator::Runtime& frt(nproc->mGenerator->ResolveRuntime(Ident::Unique("modu32"))); + mIns.Push(NativeCodeInstruction(ins, ASMIT_JSR, ASMIM_ABSOLUTE, frt.mOffset, frt.mLinkerObject, NCIF_RUNTIME | NCIF_LOWER | NCIF_UPPER)); + reg = BC_REG_WORK + 4; + } break; + } + + } + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, reg + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, reg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, reg + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, reg + 3)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 3)); + } break; + case IA_SHL: + { + if (sins1) LoadValueToReg(proc, sins1, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp], nullptr, nullptr); + if (sins0) LoadValueToReg(proc, sins0, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp], nullptr, nullptr); + + if (ins->mSrc[0].mTemp < 0) + { + int shift = ins->mSrc[0].mIntConst & 31; + + int sreg = BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp]; + + if (shift >= 24) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 3)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 2)); + sreg = treg; + shift -= 24; + } + else if (shift >= 16) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 3)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + sreg = treg; + shift -= 16; + } + else if (shift >= 8) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 3)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg)); + sreg = treg; + shift -= 8; + } + + if (shift == 0) + { + if (sreg != treg) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 3)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 3)); + } + } + else if (shift == 1) + { + if (sreg != treg) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 3)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 3)); + } + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_ZERO_PAGE, treg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL, ASMIM_ZERO_PAGE, treg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL, ASMIM_ZERO_PAGE, treg + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL, ASMIM_ZERO_PAGE, treg + 3)); + } + } + else if (shift == 2) + { + if (sreg != treg) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 3)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 3)); + } + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_ZERO_PAGE, treg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL, ASMIM_ZERO_PAGE, treg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL, ASMIM_ZERO_PAGE, treg + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL, ASMIM_ZERO_PAGE, treg + 3)); + } + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_ZERO_PAGE, treg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL, ASMIM_ZERO_PAGE, treg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL, ASMIM_ZERO_PAGE, treg + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL, ASMIM_ZERO_PAGE, treg + 3)); + } + else if (shift == 7) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 3)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LSR, ASMIM_IMPLIED)); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROR, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 3)); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROR, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 2)); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROR, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROR, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 0)); + } + else + { + NativeCodeBasicBlock* lblock = nproc->AllocateBlock(); + NativeCodeBasicBlock* eblock = nproc->AllocateBlock(); + + if (sreg != treg) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 3)); + } + else + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, treg + 3)); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDX, ASMIM_IMMEDIATE, shift)); + this->Close(ins, lblock, nullptr, ASMIT_JMP); + + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_ZERO_PAGE, treg + 0)); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL, ASMIM_ZERO_PAGE, treg + 1)); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL, ASMIM_ZERO_PAGE, treg + 2)); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL, ASMIM_IMPLIED)); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_DEX, ASMIM_IMPLIED)); + lblock->Close(ins, lblock, eblock, ASMIT_BNE); + + eblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 3)); + return eblock; + } + } + else if (ins->mSrc[1].mTemp < 0 && ins->mSrc[1].mIntConst == 1) + { + NativeCodeBasicBlock* lblock = nproc->AllocateBlock(); + NativeCodeBasicBlock* hblock = nproc->AllocateBlock(); + NativeCodeBasicBlock* eblock = nproc->AllocateBlock(); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_AND, ASMIM_IMMEDIATE, 0x1f)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_TAX, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_AND, ASMIM_IMMEDIATE, 0x10)); + this->Close(ins, lblock, hblock, ASMIT_BEQ); + + NativeCodeGenerator::Runtime& frt(nproc->mGenerator->ResolveRuntime(Ident::Unique("bitshift"))); + + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 2)); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 3)); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ABSOLUTE_X, frt.mOffset + 8, frt.mLinkerObject)); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg)); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ABSOLUTE_X, frt.mOffset, frt.mLinkerObject)); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + lblock->Close(ins, eblock, nullptr, ASMIT_JMP); + + hblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + hblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 0)); + hblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + hblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ABSOLUTE_X, frt.mOffset - 8, frt.mLinkerObject)); + hblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 2)); + hblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ABSOLUTE_X, frt.mOffset - 16, frt.mLinkerObject)); + hblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 3)); + hblock->Close(ins, eblock, nullptr, ASMIT_JMP); + + return eblock; + } + else + { + NativeCodeBasicBlock* lblock = nproc->AllocateBlock(); + NativeCodeBasicBlock* eblock = nproc->AllocateBlock(); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_AND, ASMIM_IMMEDIATE, 0x1f)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_TAX, ASMIM_IMPLIED)); + + if (ins->mSrc[1].mTemp < 0) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, ins->mSrc[1].mIntConst & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (ins->mSrc[1].mIntConst >> 8) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (ins->mSrc[1].mIntConst >> 16) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (ins->mSrc[1].mIntConst >> 24) & 0xff)); + } + else if (ins->mSrc[1].mTemp != ins->mDst.mTemp) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp] + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp] + 3)); + } + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, treg + 3)); + } + + mIns.Push(NativeCodeInstruction(ins, ASMIT_CPX, ASMIM_IMMEDIATE, 0x00)); + this->Close(ins, lblock, eblock, ASMIT_BNE); + + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_ZERO_PAGE, treg + 0)); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL, ASMIM_ZERO_PAGE, treg + 1)); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL, ASMIM_ZERO_PAGE, treg + 2)); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL, ASMIM_IMPLIED)); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_DEX, ASMIM_IMPLIED)); + lblock->Close(ins, lblock, eblock, ASMIT_BNE); + + eblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 3)); + return eblock; + } + } break; + + case IA_SHR: + { + if (sins1) LoadValueToReg(proc, sins1, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp], nullptr, nullptr); + if (sins0) LoadValueToReg(proc, sins0, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp], nullptr, nullptr); + + if (ins->mSrc[0].mTemp < 0) + { + int shift = ins->mSrc[0].mIntConst & 31; + + int sreg = BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp]; + int nvbits = 32; + if (ins->mSrc[1].IsUnsigned() && ins->mSrc[1].mRange.mMaxState == IntegerValueRange::S_BOUND) + { + int64 mv = ins->mSrc[1].mRange.mMaxValue; + while (nvbits > 0 && mv < (1LL << (nvbits - 1))) + nvbits--; + } + + if (shift >= nvbits) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 3)); + return this; + } + + if (shift >= 24) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 3)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 3)); + sreg = treg; + shift -= 24; + nvbits -= 24; + } + else if (shift >= 16) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 3)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 3)); + sreg = treg; + shift -= 16; + nvbits -= 16; + } + else if (shift >= 8) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 3)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 2)); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 3)); + sreg = treg; + shift -= 8; + nvbits -= 8; + } + + if (shift == 0) + { + if (sreg != treg) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 3)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 3)); + } + } + else if (shift == 1) + { + if (sreg != treg) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 3)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LSR, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 3)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROR, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROR, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROR, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 0)); + } + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LSR, ASMIM_ZERO_PAGE, treg + 3)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROR, ASMIM_ZERO_PAGE, treg + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROR, ASMIM_ZERO_PAGE, treg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROR, ASMIM_ZERO_PAGE, treg + 0)); + } + } + else if (shift == 2) + { + if (sreg != treg) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 3)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LSR, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 3)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROR, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROR, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROR, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 0)); + } + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LSR, ASMIM_ZERO_PAGE, treg + 3)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROR, ASMIM_ZERO_PAGE, treg + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROR, ASMIM_ZERO_PAGE, treg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROR, ASMIM_ZERO_PAGE, treg + 0)); + } + mIns.Push(NativeCodeInstruction(ins, ASMIT_LSR, ASMIM_ZERO_PAGE, treg + 3)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROR, ASMIM_ZERO_PAGE, treg + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROR, ASMIM_ZERO_PAGE, treg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROR, ASMIM_ZERO_PAGE, treg + 0)); + } + else + { + if (sreg != treg) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 3)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 3)); + } + + int nregs = (nvbits + 7) >> 3; + + if ((nvbits & 7) == 1) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LSR, ASMIM_ZERO_PAGE, treg + nregs - 1)); + for (int i = nregs - 1; i > 0; i--) + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROR, ASMIM_ZERO_PAGE, treg + i - 1)); + nvbits--; + nregs--; + shift--; + } + + if (nregs <= 2) + { + ShiftRegisterRight(ins, treg, shift); + } + else + { + NativeCodeBasicBlock* lblock = nproc->AllocateBlock(); + NativeCodeBasicBlock* eblock = nproc->AllocateBlock(); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, treg + nregs - 1)); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDX, ASMIM_IMMEDIATE, shift)); + this->Close(ins, lblock, nullptr, ASMIT_JMP); + + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_LSR, ASMIM_IMPLIED)); + for (int i = nregs - 1; i > 0; i--) + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_ROR, ASMIM_ZERO_PAGE, treg + i - 1)); + + // lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_ROR, ASMIM_ZERO_PAGE, treg + 1)); + // lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_ROR, ASMIM_ZERO_PAGE, treg + 0)); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_DEX, ASMIM_IMPLIED)); + lblock->Close(ins, lblock, eblock, ASMIT_BNE); + + eblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + nregs - 1)); + return eblock; + } + } + } + else + { + NativeCodeBasicBlock* lblock = nproc->AllocateBlock(); + NativeCodeBasicBlock* eblock = nproc->AllocateBlock(); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_AND, ASMIM_IMMEDIATE, 0x1f)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_TAX, ASMIM_IMPLIED)); + + if (ins->mSrc[1].mTemp < 0) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, ins->mSrc[1].mIntConst & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (ins->mSrc[1].mIntConst >> 8) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (ins->mSrc[1].mIntConst >> 16) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (ins->mSrc[1].mIntConst >> 24) & 0xff)); + } + else if (ins->mSrc[1].mTemp != ins->mDst.mTemp) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp] + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp] + 3)); + } + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, treg + 3)); + } + + mIns.Push(NativeCodeInstruction(ins, ASMIT_CPX, ASMIM_IMMEDIATE, 0x00)); + this->Close(ins, lblock, eblock, ASMIT_BNE); + + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_LSR, ASMIM_IMPLIED)); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_ROR, ASMIM_ZERO_PAGE, treg + 2)); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_ROR, ASMIM_ZERO_PAGE, treg + 1)); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_ROR, ASMIM_ZERO_PAGE, treg + 0)); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_DEX, ASMIM_IMPLIED)); + lblock->Close(ins, lblock, eblock, ASMIT_BNE); + + eblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 3)); + return eblock; + } + } break; + + case IA_SAR: + { + if (sins1) LoadValueToReg(proc, sins1, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp], nullptr, nullptr); + if (sins0) LoadValueToReg(proc, sins0, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp], nullptr, nullptr); + + if (ins->mSrc[0].mTemp < 0) + { + int shift = ins->mSrc[0].mIntConst & 31; + + int sreg = BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp]; + int nbytes = 4; + + if (shift >= 24) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 3)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0x00)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ADC, ASMIM_IMMEDIATE, 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_EOR, ASMIM_IMMEDIATE, 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 3)); + sreg = treg; + shift -= 24; + nbytes = 1; + } + else if (shift >= 16) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 3)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0x00)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ADC, ASMIM_IMMEDIATE, 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_EOR, ASMIM_IMMEDIATE, 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 3)); + sreg = treg; + shift -= 16; + nbytes = 2; + } + else if (shift >= 8) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 3)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0x00)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ADC, ASMIM_IMMEDIATE, 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_EOR, ASMIM_IMMEDIATE, 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 3)); + sreg = treg; + shift -= 8; + nbytes = 3; + } + + if (shift == 0) + { + if (sreg != treg) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 3)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 3)); + } + } + else if (shift == 1) + { + if (sreg != treg) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 3)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_CMP, ASMIM_IMMEDIATE, 0x80)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROR, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 3)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROR, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROR, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROR, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 0)); + } + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, treg + nbytes - 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROR, ASMIM_ZERO_PAGE, treg + nbytes - 1)); + + for(int i=nbytes - 2; i >=0; i--) + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROR, ASMIM_ZERO_PAGE, treg + i)); + } + } + else + { + NativeCodeBasicBlock* lblock = nproc->AllocateBlock(); + NativeCodeBasicBlock* eblock = nproc->AllocateBlock(); + + if (sreg != treg) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 3)); + } + else + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, treg + nbytes - 1)); + + + int lcost = 8 + 2 * (nbytes - 1); + int ucost = shift * (1 + 2 * nbytes); + + if ((nproc->mCompilerOptions & COPT_OPTIMIZE_CODE_SIZE) && lcost < ucost || + !(nproc->mCompilerOptions & COPT_OPTIMIZE_AUTO_UNROLL) && 2 * lcost < ucost) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDX, ASMIM_IMMEDIATE, shift)); + this->Close(ins, lblock, nullptr, ASMIT_JMP); + + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_CMP, ASMIM_IMMEDIATE, 0x80)); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_ROR, ASMIM_IMPLIED)); + for (int i = nbytes - 2; i >= 0; i--) + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_ROR, ASMIM_ZERO_PAGE, treg + i)); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_DEX, ASMIM_IMPLIED)); + lblock->Close(ins, lblock, eblock, ASMIT_BNE); + + eblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + nbytes - 1)); + return eblock; + } + else + { + for (int si = 0; si < shift; si++) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_CMP, ASMIM_IMMEDIATE, 0x80)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROR, ASMIM_IMPLIED)); + for (int i = nbytes - 2; i >= 0; i--) + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROR, ASMIM_ZERO_PAGE, treg + i)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + nbytes - 1)); + } + } + } + } + else + { + NativeCodeBasicBlock* lblock = nproc->AllocateBlock(); + NativeCodeBasicBlock* eblock = nproc->AllocateBlock(); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_AND, ASMIM_IMMEDIATE, 0x1f)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_TAX, ASMIM_IMPLIED)); + + if (ins->mSrc[1].mTemp < 0) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, ins->mSrc[1].mIntConst & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (ins->mSrc[1].mIntConst >> 8) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (ins->mSrc[1].mIntConst >> 16) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (ins->mSrc[1].mIntConst >> 24) & 0xff)); + } + else if (ins->mSrc[1].mTemp != ins->mDst.mTemp) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp] + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp] + 3)); + } + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, treg + 3)); + } + + mIns.Push(NativeCodeInstruction(ins, ASMIT_CPX, ASMIM_IMMEDIATE, 0x00)); + this->Close(ins, lblock, eblock, ASMIT_BNE); + + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_CMP, ASMIM_IMMEDIATE, 0x80)); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_ROR, ASMIM_IMPLIED)); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_ROR, ASMIM_ZERO_PAGE, treg + 2)); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_ROR, ASMIM_ZERO_PAGE, treg + 1)); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_ROR, ASMIM_ZERO_PAGE, treg + 0)); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_DEX, ASMIM_IMPLIED)); + lblock->Close(ins, lblock, eblock, ASMIT_BNE); + + eblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 3)); + return eblock; + } + } break; + } + } + else + { + switch (ins->mOperator) + { + case IA_ADD: + case IA_OR: + case IA_AND: + case IA_XOR: + { + if (ins->mOperator == IA_ADD && InterTypeSize[ins->mDst.mType] == 1 && ( + ins->mSrc[0].mTemp < 0 && ins->mSrc[0].mIntConst == 1 && !sins1 && ins->mSrc[1].mTemp == ins->mDst.mTemp || + ins->mSrc[1].mTemp < 0 && ins->mSrc[1].mIntConst == 1 && !sins0 && ins->mSrc[0].mTemp == ins->mDst.mTemp)) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_INC, ASMIM_ZERO_PAGE, treg)); + if (InterTypeSize[ins->mDst.mType] > 1) + { + if (ins->mDst.IsUByte()) + { + if (ins->mSrc[0].mTemp >= 0 && !ins->mSrc[0].IsUByte() || ins->mSrc[1].mTemp >= 0 && !ins->mSrc[1].IsUByte()) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + } + } + else + { + NativeCodeBasicBlock* iblock = nproc->AllocateBlock(); + NativeCodeBasicBlock* eblock = nproc->AllocateBlock(); + + this->Close(ins, eblock, iblock, ASMIT_BNE); + + iblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_INC, ASMIM_ZERO_PAGE, treg + 1)); + + iblock->Close(ins, eblock, nullptr, ASMIT_JMP); + return eblock; + } + } + } + else if (ins->mOperator == IA_ADD && InterTypeSize[ins->mDst.mType] == 1 && ( + ins->mSrc[0].mTemp < 0 && ins->mSrc[0].mIntConst == -1 && !sins1 && ins->mSrc[1].mTemp == ins->mDst.mTemp || + ins->mSrc[1].mTemp < 0 && ins->mSrc[1].mIntConst == -1 && !sins0 && ins->mSrc[0].mTemp == ins->mDst.mTemp)) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_DEC, ASMIM_ZERO_PAGE, treg)); + } + else + { + NativeCodeInstruction insl, insh; + + AsmInsType atype; + switch (ins->mOperator) + { + case IA_ADD: + atype = ASMIT_ADC; + break; + case IA_OR: + atype = ASMIT_ORA; + break; + case IA_AND: + atype = ASMIT_AND; + break; + case IA_XOR: + atype = ASMIT_EOR; + break; + } + + if (ins->mSrc[1].mTemp < 0) + { + int64 ci = ins->mSrc[1].mIntConst; + if (ins->mOperator == IA_AND && ins->mSrc[0].IsUnsigned()) + { + unsigned uci = unsigned(ci >> 8); + if (uci == BinMask(uci) && (ins->mSrc[0].mRange.mMaxValue >> 8) <= uci) + ci |= 0xff00; + } + + insl = NativeCodeInstruction(ins, atype, ASMIM_IMMEDIATE, ci & 0xff); + insh = NativeCodeInstruction(ins, atype, ASMIM_IMMEDIATE, (ci >> 8) & 0xff); + if (sins0) + { + if (ins->mDst.IsUByte()) + insh = NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0); + + LoadValueToReg(proc, sins0, treg, &insl, &insh); + } + else + { + if (ins->mOperator == IA_ADD) + mIns.Push(NativeCodeInstruction(ins, ASMIT_CLC, ASMIM_IMPLIED)); + if (treg == BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] && + ((ins->mOperator == IA_AND && (ins->mSrc[1].mIntConst & 0xff) == 0xff) || + (ins->mOperator == IA_OR && (ins->mSrc[1].mIntConst & 0xff) == 0x00))) + { + + } + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp])); + mIns.Push(insl); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg)); + } + if (InterTypeSize[ins->mDst.mType] > 1) + { + if (ins->mDst.IsUByte()) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + } + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 1)); + mIns.Push(insh); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + } + } + } + } + else if (ins->mSrc[0].mTemp < 0) + { + int64 ci = ins->mSrc[0].mIntConst; + if (ins->mOperator == IA_AND && ins->mSrc[1].IsUnsigned()) + { + unsigned uci = unsigned(ci >> 8); + if (uci == BinMask(uci) && (ins->mSrc[1].mRange.mMaxValue >> 8) <= uci) + ci |= 0xff00; + } + + insl = NativeCodeInstruction(ins, atype, ASMIM_IMMEDIATE, ci & 0xff); + insh = NativeCodeInstruction(ins, atype, ASMIM_IMMEDIATE, (ci >> 8) & 0xff); + + if (sins1) + { + if (ins->mDst.IsUByte()) + insh = NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0); + + LoadValueToReg(proc, sins1, treg, &insl, &insh); + } + else + { + if (ins->mOperator == IA_ADD) + mIns.Push(NativeCodeInstruction(ins, ASMIT_CLC, ASMIM_IMPLIED)); + if (treg == BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp] && + ((ins->mOperator == IA_AND && (ins->mSrc[0].mIntConst & 0xff) == 0xff) || + (ins->mOperator == IA_OR && (ins->mSrc[0].mIntConst & 0xff) == 0x00))) + { + + } + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp])); + mIns.Push(insl); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg)); + } + if (InterTypeSize[ins->mDst.mType] > 1) + { + if (ins->mDst.IsUByte()) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + } + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp] + 1)); + mIns.Push(insh); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + } + } + } + } + else + { + if (sins1 && sins0) + { + if (sins0->mSrc[0].mMemory == IM_INDIRECT && sins1->mSrc[0].mMemory == IM_INDIRECT && + sins0->mSrc[0].mIntConst + (InterTypeSize[ins->mDst.mType] - 1) * sins0->mSrc[0].mStride < 255 && + sins1->mSrc[0].mIntConst + (InterTypeSize[ins->mDst.mType] - 1) * sins1->mSrc[0].mStride < 255) + { + if (ins->mOperator == IA_ADD) + mIns.Push(NativeCodeInstruction(ins, ASMIT_CLC, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, sins0->mSrc[0].mIntConst + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_INDIRECT_Y, BC_REG_TMP + proc->mTempOffset[sins0->mSrc[0].mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, sins1->mSrc[0].mIntConst + 0)); + mIns.Push(NativeCodeInstruction(ins, atype, ASMIM_INDIRECT_Y, BC_REG_TMP + proc->mTempOffset[sins1->mSrc[0].mTemp])); + if (InterTypeSize[ins->mDst.mType] > 1) + { + if (ins->mDst.mTemp == sins0->mSrc[0].mTemp || ins->mDst.mTemp == sins1->mSrc[0].mTemp) + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_WORK_Y)); + else + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, sins0->mSrc[0].mIntConst + sins0->mSrc[0].mStride)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_INDIRECT_Y, BC_REG_TMP + proc->mTempOffset[sins0->mSrc[0].mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, sins1->mSrc[0].mIntConst + sins1->mSrc[0].mStride)); + mIns.Push(NativeCodeInstruction(ins, atype, ASMIM_INDIRECT_Y, BC_REG_TMP + proc->mTempOffset[sins1->mSrc[0].mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + if (ins->mDst.mTemp == sins0->mSrc[0].mTemp || ins->mDst.mTemp == sins1->mSrc[0].mTemp) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_WORK_Y)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg)); + } + } + else + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg)); + } + else + { + insl = NativeCodeInstruction(ins, atype, ASMIM_ZERO_PAGE, treg); + insh = NativeCodeInstruction(ins, atype, ASMIM_ZERO_PAGE, treg + 1); + + if (sins1->mDst.mTemp == ins->mDst.mTemp) + { + LoadValueToReg(proc, sins1, treg, nullptr, nullptr); + LoadValueToReg(proc, sins0, treg, &insl, &insh); + } + else + { + LoadValueToReg(proc, sins0, treg, nullptr, nullptr); + LoadValueToReg(proc, sins1, treg, &insl, &insh); + } + } + } + else if (sins1) + { + insl = NativeCodeInstruction(ins, atype, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]); + insh = NativeCodeInstruction(ins, atype, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 1); + + LoadValueToReg(proc, sins1, treg, &insl, &insh); + } + else if (sins0) + { + insl = NativeCodeInstruction(ins, atype, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp]); + insh = NativeCodeInstruction(ins, atype, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp] + 1); + + LoadValueToReg(proc, sins0, treg, &insl, &insh); + } + else + { + if (ins->mOperator == IA_ADD) + mIns.Push(NativeCodeInstruction(ins, ASMIT_CLC, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp])); + mIns.Push(NativeCodeInstruction(ins, atype, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg)); + if (InterTypeSize[ins->mDst.mType] > 1) + { +#if 1 + if (ins->mDst.IsUByte()) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + } +#if 1 + else if (ins->mOperator == IA_ADD && ins->mSrc[0].IsUByte() && ins->mSrc[1].IsSByte() && ins->mDst.mTemp != ins->mSrc[1].mTemp && ins->mSrc[1].mFinal) + { +// printf("ADD0 %s:%d, %d+%d->%d\n", mProc->mIdent->mString, mIndex, ins->mSrc[0].mTemp, ins->mSrc[1].mTemp, treg); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_EOR, ASMIM_IMMEDIATE, 0x80)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_AND, ASMIM_IMMEDIATE, 0x01)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_SBC, ASMIM_IMMEDIATE, 0x00)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + } + else if (ins->mOperator == IA_ADD && ins->mSrc[1].IsUByte() && ins->mSrc[0].IsSByte() && ins->mDst.mTemp != ins->mSrc[0].mTemp && ins->mSrc[0].mFinal) + { +// printf("ADD1 %s:%d, %d+%d->%d\n", mProc->mIdent->mString, mIndex, ins->mSrc[0].mTemp, ins->mSrc[1].mTemp, treg); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_EOR, ASMIM_IMMEDIATE, 0x80)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_AND, ASMIM_IMMEDIATE, 0x01)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_SBC, ASMIM_IMMEDIATE, 0x00)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + } +#endif + else +#endif + { + if (ins->mSrc[1].IsUByte()) + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + else + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp] + 1)); + if (ins->mSrc[0].IsUByte()) + mIns.Push(NativeCodeInstruction(ins, atype, ASMIM_IMMEDIATE, 0)); + else + mIns.Push(NativeCodeInstruction(ins, atype, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + } + } + + } + } + } + } break; + case IA_SUB: + { + NativeCodeInstruction insl, insh; + + if (InterTypeSize[ins->mDst.mType] == 1 && + ins->mSrc[0].mTemp < 0 && ins->mSrc[0].mIntConst == 1 && !sins1 && ins->mSrc[1].mTemp == ins->mDst.mTemp) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_DEC, ASMIM_ZERO_PAGE, treg)); + } + else if (ins->mSrc[0].mTemp < 0) + { + insl = NativeCodeInstruction(ins, ASMIT_SBC, ASMIM_IMMEDIATE, ins->mSrc[0].mIntConst & 0xff); + if (ins->mDst.IsUByte()) + insh = NativeCodeInstruction(ins, ASMIT_AND, ASMIM_IMMEDIATE, 0); + else + insh = NativeCodeInstruction(ins, ASMIT_SBC, ASMIM_IMMEDIATE, (ins->mSrc[0].mIntConst >> 8) & 0xff); + + if (sins1) + LoadValueToReg(proc, sins1, treg, &insl, &insh); + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_SEC, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp])); + mIns.Push(insl); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg)); + if (InterTypeSize[ins->mDst.mType] > 1) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp] + 1)); + mIns.Push(insh); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + } + } + } + else if (ins->mSrc[1].mTemp < 0) + { + if ((ins->mSrc[1].mIntConst & (ins->mSrc[1].mIntConst + 1)) == 0 && ins->mSrc[0].IsUnsigned() && ins->mSrc[0].mRange.mMaxValue <= ins->mSrc[1].mIntConst) + { + NativeCodeInstruction insl = NativeCodeInstruction(ins, ASMIT_EOR, ASMIM_IMMEDIATE, ins->mSrc[1].mIntConst & 0xff); + NativeCodeInstruction insh = NativeCodeInstruction(ins, ASMIT_EOR, ASMIM_IMMEDIATE, (ins->mSrc[1].mIntConst >> 8) & 0xff); + if (sins0) + { + if (ins->mDst.IsUByte()) + insh = NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0); + + LoadValueToReg(proc, sins0, treg, &insl, &insh); + } + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp])); + mIns.Push(insl); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg)); + if (InterTypeSize[ins->mDst.mType] > 1) + { + if (ins->mDst.IsUByte()) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + } + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 1)); + mIns.Push(insh); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + } + } + } + } + else if (sins0) + { + LoadValueToReg(proc, sins0, treg, nullptr, nullptr); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_SEC, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, ins->mSrc[1].mIntConst & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_SBC, ASMIM_ZERO_PAGE, treg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg)); + if (InterTypeSize[ins->mDst.mType] > 1) + { + if (ins->mDst.IsUByte()) + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (ins->mSrc[1].mIntConst >> 8) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_SBC, ASMIM_ZERO_PAGE, treg + 1)); + } + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + } + } + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_SEC, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, ins->mSrc[1].mIntConst & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_SBC, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg)); + if (InterTypeSize[ins->mDst.mType] > 1) + { + if (ins->mDst.IsUByte()) + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (ins->mSrc[1].mIntConst >> 8) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_SBC, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 1)); + } + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + } + } + } + else + { + if (sins0) + { + insl = NativeCodeInstruction(ins, ASMIT_SBC, ASMIM_ZERO_PAGE, BC_REG_WORK + 0); + if (ins->mDst.IsUByte()) + insh = NativeCodeInstruction(ins, ASMIT_AND, ASMIM_IMMEDIATE, 0); + else + insh = NativeCodeInstruction(ins, ASMIT_SBC, ASMIM_ZERO_PAGE, BC_REG_WORK + 1); + + LoadValueToReg(proc, sins0, BC_REG_WORK, nullptr, nullptr); + } + else + { + insl = NativeCodeInstruction(ins, ASMIT_SBC, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp]); + if (ins->mDst.IsUByte()) + insh = NativeCodeInstruction(ins, ASMIT_AND, ASMIM_IMMEDIATE, 0); + else + insh = NativeCodeInstruction(ins, ASMIT_SBC, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 1); + } + + if (sins1) + { + LoadValueToReg(proc, sins1, treg, &insl, &insh); + } + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_SEC, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp])); + mIns.Push(insl); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg)); + if (InterTypeSize[ins->mDst.mType] > 1) + { + if (ins->mDst.IsUByte()) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + } + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp] + 1)); + mIns.Push(insh); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + } + } + } + } + } break; + case IA_MUL: + case IA_DIVS: + case IA_MODS: + case IA_DIVU: + case IA_MODU: + { + int reg = BC_REG_ACCU; + + if (ins->mOperator == IA_MUL && ins->mSrc[1].mTemp < 0) + { + reg = ShortMultiply(proc, nproc, ins, sins0, 0, int(ins->mSrc[1].mIntConst)); + } + else if (ins->mOperator == IA_MUL && ins->mSrc[0].mTemp < 0) + { + reg = ShortMultiply(proc, nproc, ins, sins1, 1, int(ins->mSrc[0].mIntConst)); + } + else if (ins->mOperator == IA_MUL && ins->mSrc[0].IsUByte()) + { + if (!sins0 && !sins1 && ins->mSrc[1].IsUByte() && CheckIsInAccu(BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp])) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp])); + } + else + { + if (sins1) + LoadValueToReg(proc, sins1, BC_REG_ACCU, nullptr, nullptr); + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 1)); + } + + if (sins0) + { + LoadValueToReg(proc, sins0, BC_REG_WORK, nullptr, nullptr); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_WORK + 0)); + } + else + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp])); + } + + NativeCodeGenerator::Runtime& frt(nproc->mGenerator->ResolveRuntime(Ident::Unique("mul16by8"))); + mIns.Push(NativeCodeInstruction(ins, ASMIT_JSR, ASMIM_ABSOLUTE, frt.mOffset, frt.mLinkerObject, NCIF_RUNTIME | NCIF_LOWER | NCIF_UPPER | NCIF_USE_CPU_REG_A)); + reg = BC_REG_ACCU; + } + else if (ins->mOperator == IA_MUL && ins->mSrc[1].IsUByte()) + { + if (sins0) + LoadValueToReg(proc, sins0, BC_REG_ACCU, nullptr, nullptr); + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 1)); + } + + if (sins1) + { + LoadValueToReg(proc, sins1, BC_REG_WORK, nullptr, nullptr); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_WORK + 0)); + } + else + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp])); + + NativeCodeGenerator::Runtime& frt(nproc->mGenerator->ResolveRuntime(Ident::Unique("mul16by8"))); + mIns.Push(NativeCodeInstruction(ins, ASMIT_JSR, ASMIM_ABSOLUTE, frt.mOffset, frt.mLinkerObject, NCIF_RUNTIME | NCIF_LOWER | NCIF_UPPER | NCIF_USE_CPU_REG_A)); + reg = BC_REG_ACCU; + } + else if (ins->mOperator == IA_DIVU && ins->mSrc[0].IsUByte()) + { + if (sins1) + LoadValueToReg(proc, sins1, BC_REG_ACCU, nullptr, nullptr); + else if (ins->mSrc[1].mTemp < 0) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, ins->mSrc[1].mIntConst & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (ins->mSrc[1].mIntConst >> 8) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 1)); + } + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 1)); + } + + if (sins0) + { + LoadValueToReg(proc, sins0, BC_REG_WORK, nullptr, nullptr); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_WORK + 0)); + } + else if (ins->mSrc[0].mTemp < 0) + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, ins->mSrc[0].mIntConst & 0xff)); + else + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp])); + + NativeCodeGenerator::Runtime& frt(nproc->mGenerator->ResolveRuntime(Ident::Unique("divu16by8"))); + mIns.Push(NativeCodeInstruction(ins, ASMIT_JSR, ASMIM_ABSOLUTE, frt.mOffset, frt.mLinkerObject, NCIF_RUNTIME | NCIF_LOWER | NCIF_UPPER | NCIF_USE_CPU_REG_A)); + reg = BC_REG_ACCU; + } + else if (ins->mOperator == IA_MODU && ins->mSrc[0].IsUByte()) + { + if (sins1) + LoadValueToReg(proc, sins1, BC_REG_ACCU, nullptr, nullptr); + else if (ins->mSrc[1].mTemp < 0) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, ins->mSrc[1].mIntConst & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (ins->mSrc[1].mIntConst >> 8) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 1)); + } + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 1)); + } + + if (sins0) + { + LoadValueToReg(proc, sins0, BC_REG_WORK, nullptr, nullptr); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_WORK + 0)); + } + else if (ins->mSrc[0].mTemp < 0) + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, ins->mSrc[0].mIntConst & 0xff)); + else + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp])); + + NativeCodeGenerator::Runtime& frt(nproc->mGenerator->ResolveRuntime(Ident::Unique("divu16by8"))); + mIns.Push(NativeCodeInstruction(ins, ASMIT_JSR, ASMIM_ABSOLUTE, frt.mOffset, frt.mLinkerObject, NCIF_RUNTIME | NCIF_LOWER | NCIF_UPPER | NCIF_USE_CPU_REG_A)); + reg = BC_REG_WORK + 2; + } +#if 1 + else if (ins->mOperator == IA_DIVS && ins->mSrc[0].mTemp < 0 && (ins->mSrc[0].mIntConst == 2 || ins->mSrc[0].mIntConst == 4 || ins->mSrc[0].mIntConst == 8)) + { + reg = ShortSignedDivide(proc, nproc, ins, sins1, int(ins->mSrc[0].mIntConst)); + } +#endif + else + { + if (sins1) + LoadValueToReg(proc, sins1, BC_REG_ACCU, nullptr, nullptr); + else if (ins->mSrc[1].mTemp < 0) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, ins->mSrc[1].mIntConst & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (ins->mSrc[1].mIntConst >> 8) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 1)); + } + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 1)); + } + + if (sins0) + LoadValueToReg(proc, sins0, BC_REG_WORK, nullptr, nullptr); + else if (ins->mSrc[0].mTemp < 0) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, ins->mSrc[0].mIntConst & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_WORK + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (ins->mSrc[0].mIntConst >> 8) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_WORK + 1)); + } + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_WORK + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_WORK + 1)); + } + + switch (ins->mOperator) + { + case IA_MUL: + { + NativeCodeGenerator::Runtime& frt(nproc->mGenerator->ResolveRuntime(Ident::Unique("mul16"))); + mIns.Push(NativeCodeInstruction(ins, ASMIT_JSR, ASMIM_ABSOLUTE, frt.mOffset, frt.mLinkerObject, NCIF_RUNTIME | NCIF_LOWER | NCIF_UPPER)); + reg = BC_REG_WORK + 2; + } break; + case IA_DIVS: + { + NativeCodeGenerator::Runtime& frt(nproc->mGenerator->ResolveRuntime(Ident::Unique("divs16"))); + mIns.Push(NativeCodeInstruction(ins, ASMIT_JSR, ASMIM_ABSOLUTE, frt.mOffset, frt.mLinkerObject, NCIF_RUNTIME | NCIF_LOWER | NCIF_UPPER)); + } break; + case IA_MODS: + { + NativeCodeGenerator::Runtime& frt(nproc->mGenerator->ResolveRuntime(Ident::Unique("mods16"))); + mIns.Push(NativeCodeInstruction(ins, ASMIT_JSR, ASMIM_ABSOLUTE, frt.mOffset, frt.mLinkerObject, NCIF_RUNTIME | NCIF_LOWER | NCIF_UPPER)); + reg = BC_REG_WORK + 2; + } break; + case IA_DIVU: + { + NativeCodeGenerator::Runtime& frt(nproc->mGenerator->ResolveRuntime(Ident::Unique("divu16"))); + mIns.Push(NativeCodeInstruction(ins, ASMIT_JSR, ASMIM_ABSOLUTE, frt.mOffset, frt.mLinkerObject, NCIF_RUNTIME | NCIF_LOWER | NCIF_UPPER)); + } break; + case IA_MODU: + { + NativeCodeGenerator::Runtime& frt(nproc->mGenerator->ResolveRuntime(Ident::Unique("modu16"))); + mIns.Push(NativeCodeInstruction(ins, ASMIT_JSR, ASMIM_ABSOLUTE, frt.mOffset, frt.mLinkerObject, NCIF_RUNTIME | NCIF_LOWER | NCIF_UPPER)); + reg = BC_REG_WORK + 2; + } break; + } + } + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, reg + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, reg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + 1)); + } break; + case IA_SHL: + { + if (ins->mSrc[0].mTemp < 0 && (ins->mSrc[0].mIntConst & 15) == 1 && sins1) + { + NativeCodeInstruction insl(ins, ASMIT_ASL, ASMIM_IMPLIED); + NativeCodeInstruction insh(ins, ASMIT_ROL, ASMIM_IMPLIED); + LoadValueToReg(proc, sins1, treg, &insl, &insh); + return this; + } + if (sins1) LoadValueToReg(proc, sins1, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp], nullptr, nullptr); + if (sins0) LoadValueToReg(proc, sins0, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp], nullptr, nullptr); + + if (ins->mSrc[0].mTemp < 0) + { + int sreg = BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp]; + + int shift = ins->mSrc[0].mIntConst & 15; + if (ins->mDst.IsUByte() || InterTypeSize[ins->mDst.mType] == 1) + { + if (shift == 0) + { + if (sreg != treg) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg)); + } + } + else if (shift == 1) + { + if (ins->mSrc[1].mTemp != ins->mDst.mTemp) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg)); + } + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_ZERO_PAGE, treg)); + } + } + else if (shift >= 8) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0x00)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 0)); + } + else if (shift > 5) + { + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LSR, ASMIM_IMPLIED)); + for(int i=shift; i<8; i++) + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROR, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_AND, ASMIM_IMMEDIATE, (0xff << shift) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg )); + } + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg)); + for (int i = 0; i < shift; i++) + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg)); + } + + if (InterTypeSize[ins->mDst.mType] > 1) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + } + } + else if (ins->mDst.IsSByte() && shift < 8) + { + if (shift == 0) + { + if (sreg != treg) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg)); + } + } + else if (shift == 1) + { + if (ins->mSrc[1].mTemp != ins->mDst.mTemp) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg)); + } + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_ZERO_PAGE, treg)); + } + } + else if (shift > 5) + { + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LSR, ASMIM_IMPLIED)); + for (int i = shift; i < 8; i++) + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROR, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_AND, ASMIM_IMMEDIATE, (0xff << shift) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg)); + } + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg)); + for (int i = 0; i < shift; i++) + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg)); + } + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + } + else + { + if (shift == 0) + { + if (ins->mSrc[1].mTemp != ins->mDst.mTemp) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + } + } + else if (shift == 1) + { + if (ins->mSrc[1].mTemp != ins->mDst.mTemp) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg)); + if (ins->mSrc[1].IsUByte()) + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + else + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + } + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_ZERO_PAGE, treg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL, ASMIM_ZERO_PAGE, treg + 1)); + } + } +#if 1 + else if (shift == 6) + { + int sreg = BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp]; + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDX, ASMIM_ZERO_PAGE, sreg + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STX, ASMIM_ZERO_PAGE, treg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LSR, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROR, ASMIM_ZERO_PAGE, treg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROR, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROR, ASMIM_ZERO_PAGE, treg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROR, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_AND, ASMIM_IMMEDIATE, 0xc0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 0)); + } +#endif + else if (shift == 7) + { + int sreg = BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp]; + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LSR, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROR, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0x00)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROR, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 0)); + } + else if (shift == 15) + { + int sreg = BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp]; + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LSR, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0x00)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROR, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + } + else if (shift == 4 && ins->mSrc[1].IsUByte() && ins->mSrc[1].mRange.mMaxValue >= 128) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_TAX, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_AND, ASMIM_IMMEDIATE, 0x0f)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_TXA, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_AND, ASMIM_IMMEDIATE, 0xf0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg)); + } + else if (shift >= 8) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp])); + for (int i = 8; i < shift; i++) + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0x00)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 0)); + } + else + { + if (ins->mSrc[1].mTemp != ins->mDst.mTemp) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg)); + if (ins->mSrc[1].IsUByte()) + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + else + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp] + 1)); + } + else + { + if (ins->mSrc[1].IsUByte()) + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + else + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, treg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_ZERO_PAGE, treg)); + } + + int check = 0xffff; + if (ins->mSrc[1].IsUByte()) + check = int(ins->mSrc[1].mRange.mMaxValue); + + check <<= 1; + if (check >= 0x100) + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL, ASMIM_IMPLIED)); + for (int i = 1; i < shift; i++) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_ZERO_PAGE, treg)); + check <<= 1; + if (check >= 0x100) + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL, ASMIM_IMPLIED)); + } + + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + } + } + } + else if (ins->mSrc[1].mTemp < 0 && IsPowerOf2(ins->mSrc[1].mIntConst & 0xffff)) + { + int l = Binlog(ins->mSrc[1].mIntConst & 0xffff); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp])); + if (!ins->mSrc[0].IsUByte() || ins->mSrc[0].mRange.mMaxValue > 15) + mIns.Push(NativeCodeInstruction(ins, ASMIT_AND, ASMIM_IMMEDIATE, 0x0f)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_TAX, ASMIM_IMPLIED)); + + NativeCodeGenerator::Runtime& frt(nproc->mGenerator->ResolveRuntime(Ident::Unique("bitshift"))); + + if (l < 8) + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ABSOLUTE_X, frt.mOffset + 8 + l, frt.mLinkerObject)); + else + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0x00)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg)); + if (ins->mDst.IsUByte()) + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0x00)); + else + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ABSOLUTE_X, frt.mOffset + l, frt.mLinkerObject)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + } + else if (ins->mSrc[1].mTemp < 0) + { + int size = int(ins->mSrc[0].mRange.mMaxValue) + 1; + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp])); + if (!ins->mSrc[0].IsUByte() || ins->mSrc[0].mRange.mMaxValue > 15) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_AND, ASMIM_IMMEDIATE, 0x0f)); + size = 16; + } + mIns.Push(NativeCodeInstruction(ins, ASMIT_TAX, ASMIM_IMPLIED)); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ABSOLUTE_X, 0, nproc->mGenerator->AllocateShortMulTable(IA_SHL, int(ins->mSrc[1].mIntConst), size, false))); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg)); + if (ins->mDst.IsUByte()) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + } + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ABSOLUTE_X, 0, nproc->mGenerator->AllocateShortMulTable(IA_SHL, int(ins->mSrc[1].mIntConst), size, true))); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + } + } + else + { + NativeCodeBasicBlock* lblock = nproc->AllocateBlock(); + NativeCodeBasicBlock* eblock = nproc->AllocateBlock(); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp])); + if (!ins->mSrc[0].IsUByte() || ins->mSrc[0].mRange.mMaxValue > 15) + mIns.Push(NativeCodeInstruction(ins, ASMIT_AND, ASMIM_IMMEDIATE, 0x0f)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_TAX, ASMIM_IMPLIED)); + + if (ins->mSrc[1].mTemp < 0) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, ins->mSrc[1].mIntConst & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (ins->mSrc[1].mIntConst >> 8) & 0xff)); + } + else if (ins->mSrc[1].mTemp != ins->mDst.mTemp) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp] + 1)); + } + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, treg + 1)); + } + + mIns.Push(NativeCodeInstruction(ins, ASMIT_CPX, ASMIM_IMMEDIATE, 0x00)); + this->Close(ins, lblock, eblock, ASMIT_BNE); + + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_ZERO_PAGE, treg)); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL, ASMIM_IMPLIED)); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_DEX, ASMIM_IMPLIED)); + lblock->Close(ins, lblock, eblock, ASMIT_BNE); + + eblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + return eblock; + } + } break; + case IA_SHR: + { + if (sins1) LoadValueToReg(proc, sins1, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp], nullptr, nullptr); + if (sins0) LoadValueToReg(proc, sins0, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp], nullptr, nullptr); + + if (ins->mSrc[0].mTemp < 0) + { + int shift = ins->mSrc[0].mIntConst & 15; +#if 1 + if (ins->mSrc[1].IsUByte() || InterTypeSize[ins->mSrc[1].mType] == 1) + { + if (shift == 0) + { + if (ins->mSrc[1].mTemp != ins->mDst.mTemp) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg)); + if (InterTypeSize[ins->mDst.mType] > 1) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + } + } + } + else if (shift == 7) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + if (InterTypeSize[ins->mDst.mType] > 1) + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 0)); + } + else if (shift == 6) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_AND, ASMIM_IMMEDIATE, 3)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 0)); + if (InterTypeSize[ins->mDst.mType] > 1) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + } + } + else if (shift >= 8) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 0)); + if (InterTypeSize[ins->mDst.mType] > 1) + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + } + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp])); + for (int i = 0; i < shift; i++) + mIns.Push(NativeCodeInstruction(ins, ASMIT_LSR, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 0)); + if (InterTypeSize[ins->mDst.mType] > 1) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + } + } + } + else +#endif + { + if (shift == 0) + { + if (ins->mSrc[1].mTemp != ins->mDst.mTemp) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + } + } + else if (shift == 1) + { + if (ins->mSrc[1].mTemp != ins->mDst.mTemp) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LSR, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROR, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg)); + } + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LSR, ASMIM_ZERO_PAGE, treg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROR, ASMIM_ZERO_PAGE, treg)); + } + } + else if (shift == 15) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 0)); + } + else if (shift == 14) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_AND, ASMIM_IMMEDIATE, 3)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + } + else if (shift >= 8) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp] + 1)); + for (int i = 8; i < shift; i++) + mIns.Push(NativeCodeInstruction(ins, ASMIT_LSR, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0x00)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + } + else if (shift >= 5) + { + if (ins->mSrc[1].mTemp != ins->mDst.mTemp) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp])); + } + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, treg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDX, ASMIM_ZERO_PAGE, treg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STX, ASMIM_ZERO_PAGE, treg)); + } + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_IMPLIED)); + for (int i = shift; i < 8; i++) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL, ASMIM_ZERO_PAGE, treg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL, ASMIM_IMPLIED)); + } + mIns.Push(NativeCodeInstruction(ins, ASMIT_AND, ASMIM_IMMEDIATE, 0xff >> shift)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + } + else + { + uint32 mask = 0xffff; + if (ins->mSrc[1].IsPositive() && ins->mSrc[1].mRange.mMaxState == IntegerValueRange::S_BOUND) + mask = uint32(ins->mSrc[1].mRange.mMaxValue); + + if (ins->mSrc[1].mTemp != ins->mDst.mTemp) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LSR, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp])); + } + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, treg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LSR, ASMIM_ZERO_PAGE, treg + 1)); + } + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROR, ASMIM_IMPLIED)); + for (int i = 1; i < shift; i++) + { + mask >>= 1; + if (mask > 0xff) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LSR, ASMIM_ZERO_PAGE, treg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROR, ASMIM_IMPLIED)); + } + else + mIns.Push(NativeCodeInstruction(ins, ASMIT_LSR, ASMIM_IMPLIED)); + } + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg)); + } + } + } +#if 1 + else if (ins->mSrc[1].mTemp < 0 && IsPowerOf2(ins->mSrc[1].mIntConst & 0xffff)) + { + int l = Binlog(ins->mSrc[1].mIntConst & 0xffff); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp])); + if (!ins->mSrc[0].IsUByte() || ins->mSrc[0].mRange.mMaxValue > 15) + mIns.Push(NativeCodeInstruction(ins, ASMIT_AND, ASMIM_IMMEDIATE, 0x0f)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_TAX, ASMIM_IMPLIED)); + + NativeCodeGenerator::Runtime& frt(nproc->mGenerator->ResolveRuntime(Ident::Unique("bitshift"))); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ABSOLUTE_X, frt.mOffset + 39 - l, frt.mLinkerObject)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg)); + if (l >= 8) + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ABSOLUTE_X, frt.mOffset + 47 - l, frt.mLinkerObject)); + else + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + } +#endif + else if (ins->mSrc[1].mTemp < 0) + { + int size = int(ins->mSrc[0].mRange.mMaxValue) + 1; + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp])); + if (!ins->mSrc[0].IsUByte() || ins->mSrc[0].mRange.mMaxValue > 15) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_AND, ASMIM_IMMEDIATE, 0x0f)); + size = 16; + } + mIns.Push(NativeCodeInstruction(ins, ASMIT_TAX, ASMIM_IMPLIED)); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ABSOLUTE_X, 0, nproc->mGenerator->AllocateShortMulTable(IA_SHR, int(ins->mSrc[1].mIntConst), size, false))); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg)); + if (ins->mDst.IsUByte()) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + } + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ABSOLUTE_X, 0, nproc->mGenerator->AllocateShortMulTable(IA_SHR, int(ins->mSrc[1].mIntConst), size, true))); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + } + } + else if (ins->mSrc[1].IsUByte()) + { + NativeCodeBasicBlock* lblock = nproc->AllocateBlock(); + NativeCodeBasicBlock* eblock = nproc->AllocateBlock(); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp])); + if (!ins->mSrc[0].IsUByte() || ins->mSrc[0].mRange.mMaxValue > 15) + mIns.Push(NativeCodeInstruction(ins, ASMIT_AND, ASMIM_IMMEDIATE, 0x0f)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_TAX, ASMIM_IMPLIED)); + + if (ins->mSrc[1].mTemp < 0) + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, ins->mSrc[1].mIntConst & 0xff)); + else if (ins->mSrc[1].mTemp != ins->mDst.mTemp) + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp])); + else + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, treg)); + + if (ins->mSrc[0].IsInRange(1, 15)) + this->Close(ins, lblock, nullptr, ASMIT_JMP); + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_CPX, ASMIM_IMMEDIATE, 0x00)); + this->Close(ins, lblock, eblock, ASMIT_BNE); + } + + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_LSR, ASMIM_IMPLIED)); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_DEX, ASMIM_IMPLIED)); + lblock->Close(ins, lblock, eblock, ASMIT_BNE); + + eblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg)); + eblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + eblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + return eblock; + } + else + { + NativeCodeBasicBlock* lblock = nproc->AllocateBlock(); + NativeCodeBasicBlock* eblock = nproc->AllocateBlock(); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp])); + if (!ins->mSrc[0].IsUByte() || ins->mSrc[0].mRange.mMaxValue > 15) + mIns.Push(NativeCodeInstruction(ins, ASMIT_AND, ASMIM_IMMEDIATE, 0x0f)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_TAX, ASMIM_IMPLIED)); + + if (ins->mSrc[1].mTemp < 0) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, ins->mSrc[1].mIntConst & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (ins->mSrc[1].mIntConst >> 8) & 0xff)); + } + else if (ins->mSrc[1].mTemp != ins->mDst.mTemp) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp] + 1)); + } + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, treg + 1)); + } + + if (ins->mSrc[0].IsInRange(1, 15)) + this->Close(ins, lblock, nullptr, ASMIT_JMP); + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_CPX, ASMIM_IMMEDIATE, 0x00)); + this->Close(ins, lblock, eblock, ASMIT_BNE); + } + + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_LSR, ASMIM_IMPLIED)); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_ROR, ASMIM_ZERO_PAGE, treg + 0)); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_DEX, ASMIM_IMPLIED)); + lblock->Close(ins, lblock, eblock, ASMIT_BNE); + + eblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + return eblock; + } + + + + } break; + case IA_SAR: + { + if (sins1) LoadValueToReg(proc, sins1, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp], nullptr, nullptr); + if (sins0) LoadValueToReg(proc, sins0, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp], nullptr, nullptr); + + if (ins->mSrc[0].mTemp < 0) + { + int shift = ins->mSrc[0].mIntConst & 15; + + if (ins->mSrc[1].IsUByte()) + { + if (shift == 0) + { + if (ins->mSrc[1].mTemp != ins->mDst.mTemp) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + } + } + else if (shift == 7) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 0)); + } + else if (shift == 6) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_AND, ASMIM_IMMEDIATE, 3)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + } + else if (shift >= 8) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + } + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp])); + for (int i = 0; i < shift; i++) + mIns.Push(NativeCodeInstruction(ins, ASMIT_LSR, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + } + } + else if (InterTypeSize[ins->mDst.mType] == 1) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp])); + + if (shift > 5) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_IMPLIED)); + for (int i = shift; i < 8; i++) + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_AND, ASMIM_IMMEDIATE, 0xff >> shift)); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_EOR, ASMIM_IMMEDIATE, 0x80 >> shift)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_SEC, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_SBC, ASMIM_IMMEDIATE, 0x80 >> shift)); + } + else if (shift > 0) + { + for (int i = 0; i < shift; i++) + mIns.Push(NativeCodeInstruction(ins, ASMIT_LSR, ASMIM_IMPLIED)); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_EOR, ASMIM_IMMEDIATE, 0x80 >> shift)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_SEC, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_SBC, ASMIM_IMMEDIATE, 0x80 >> shift)); + } + + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 0)); + } + else if (ins->mSrc[1].IsSByte() && shift != 0 && shift < 5) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp])); + + for (int i = 0; i < shift; i++) + mIns.Push(NativeCodeInstruction(ins, ASMIT_LSR, ASMIM_IMPLIED)); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_EOR, ASMIM_IMMEDIATE, 0x80 >> shift)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_SEC, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_SBC, ASMIM_IMMEDIATE, 0x80 >> shift)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_SBC, ASMIM_IMMEDIATE, 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + } + else + { + if (shift == 0) + { + if (ins->mSrc[1].mTemp != ins->mDst.mTemp) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + } + } + else if (shift == 1) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_CMP, ASMIM_IMMEDIATE, 0x80)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROR, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROR, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg)); + } + else if (shift == 7) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0x00)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ADC, ASMIM_IMMEDIATE, 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_EOR, ASMIM_IMMEDIATE, 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + } + else if (shift == 6) + { + int sreg = BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp]; + if (sreg != treg) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg)); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0x00)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ADC, ASMIM_IMMEDIATE, 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_EOR, ASMIM_IMMEDIATE, 0xff)); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_ZERO_PAGE, treg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL, ASMIM_ZERO_PAGE, treg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + } + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_ZERO_PAGE, treg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL, ASMIM_ZERO_PAGE, treg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0x00)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ADC, ASMIM_IMMEDIATE, 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_EOR, ASMIM_IMMEDIATE, 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_ZERO_PAGE, treg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL, ASMIM_ZERO_PAGE, treg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDX, ASMIM_ZERO_PAGE, treg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STX, ASMIM_ZERO_PAGE, treg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + } + } + else if (shift >= 8) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp] + 1)); + for (int i = 8; i < shift; i++) + mIns.Push(NativeCodeInstruction(ins, ASMIT_LSR, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_EOR, ASMIM_IMMEDIATE, 0x80 >> (shift - 8))); + mIns.Push(NativeCodeInstruction(ins, ASMIT_SEC, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_SBC, ASMIM_IMMEDIATE, 0x80 >> (shift - 8))); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_SBC, ASMIM_ZERO_PAGE, treg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + } + else + { + if (ins->mSrc[1].mTemp != ins->mDst.mTemp) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp] + 1)); + } + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, treg + 1)); + } + + for (int i = 0; i < shift; i++) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LSR, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ROR, ASMIM_ZERO_PAGE, treg)); + } + + mIns.Push(NativeCodeInstruction(ins, ASMIT_EOR, ASMIM_IMMEDIATE, 0x80 >> shift)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_SEC, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_SBC, ASMIM_IMMEDIATE, 0x80 >> shift)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + } + } + } +#if 1 + else if (ins->mSrc[1].mTemp < 0 && IsPowerOf2(ins->mSrc[1].mIntConst & 0xffff)) + { + int l = Binlog(ins->mSrc[1].mIntConst & 0xffff); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp])); + if (!ins->mSrc[0].IsUByte() || ins->mSrc[0].mRange.mMaxValue > 15) + mIns.Push(NativeCodeInstruction(ins, ASMIT_AND, ASMIM_IMMEDIATE, 0x0f)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_TAX, ASMIM_IMPLIED)); + + NativeCodeGenerator::Runtime& frt(nproc->mGenerator->ResolveRuntime(Ident::Unique("bitshift"))); + + if (l == 15) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_SEC, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0x00)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_SBC, ASMIM_ABSOLUTE_X, frt.mOffset + 39 - l, frt.mLinkerObject)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0x00)); + if (l >= 8) + mIns.Push(NativeCodeInstruction(ins, ASMIT_SBC, ASMIM_ABSOLUTE_X, frt.mOffset + 47 - l, frt.mLinkerObject)); + else + mIns.Push(NativeCodeInstruction(ins, ASMIT_SBC, ASMIM_IMMEDIATE, 0x00)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + } + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ABSOLUTE_X, frt.mOffset + 39 - l, frt.mLinkerObject)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg)); + if (l >= 8) + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ABSOLUTE_X, frt.mOffset + 47 - l, frt.mLinkerObject)); + else + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0x00)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + } + } +#endif + else if (ins->mSrc[1].mTemp < 0) + { + int size = int(ins->mSrc[0].mRange.mMaxValue) + 1; + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp])); + if (!ins->mSrc[0].IsUByte() || ins->mSrc[0].mRange.mMaxValue > 15) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_AND, ASMIM_IMMEDIATE, 0x0f)); + size = 16; + } + mIns.Push(NativeCodeInstruction(ins, ASMIT_TAX, ASMIM_IMPLIED)); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ABSOLUTE_X, 0, nproc->mGenerator->AllocateShortMulTable(IA_SAR, int(ins->mSrc[1].mIntConst), size, false))); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg)); + if (ins->mDst.IsUByte()) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + } + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ABSOLUTE_X, 0, nproc->mGenerator->AllocateShortMulTable(IA_SAR, int(ins->mSrc[1].mIntConst), size, true))); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + } + } + else + { + NativeCodeBasicBlock* lblock = nproc->AllocateBlock(); + NativeCodeBasicBlock* eblock = nproc->AllocateBlock(); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp])); + if (!ins->mSrc[0].IsUByte() || ins->mSrc[0].mRange.mMaxValue > 15) + mIns.Push(NativeCodeInstruction(ins, ASMIT_AND, ASMIM_IMMEDIATE, 0x0f)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_TAX, ASMIM_IMPLIED)); + + if (ins->mSrc[1].IsUByte()) + { + if (ins->mSrc[1].mTemp < 0) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, ins->mSrc[1].mIntConst & 0xff)); + } + else if (ins->mSrc[1].mTemp != ins->mDst.mTemp) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp])); + } + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, treg)); + } + + if (ins->mSrc[0].IsInRange(1, 15)) + this->Close(ins, lblock, nullptr, ASMIT_JMP); + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_CPX, ASMIM_IMMEDIATE, 0x00)); + this->Close(ins, lblock, eblock, ASMIT_BNE); + } + + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_LSR, ASMIM_IMPLIED)); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_DEX, ASMIM_IMPLIED)); + lblock->Close(ins, lblock, eblock, ASMIT_BNE); + + eblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg)); + eblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0x00)); + eblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + } + else if (ins->mSrc[1].IsSByte()) + { + if (ins->mSrc[1].mTemp < 0) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, ins->mSrc[1].mIntConst & 0xff)); + } + else if (ins->mSrc[1].mTemp != ins->mDst.mTemp) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp])); + } + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, treg)); + } + + if (ins->mSrc[0].IsInRange(1, 15)) + this->Close(ins, lblock, nullptr, ASMIT_JMP); + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_CPX, ASMIM_IMMEDIATE, 0x00)); + this->Close(ins, lblock, eblock, ASMIT_BNE); + } + + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_CMP, ASMIM_IMMEDIATE, 0x80)); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_ROR, ASMIM_IMPLIED)); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_DEX, ASMIM_IMPLIED)); + lblock->Close(ins, lblock, eblock, ASMIT_BNE); + + eblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg)); + if (ins->mSrc[1].mTemp < 0) + { + eblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (ins->mSrc[1].mIntConst >> 8) & 0xff)); + eblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + } + else if (ins->mSrc[1].mTemp != ins->mDst.mTemp) + { + eblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp] + 1)); + eblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + } + } + else + { + if (ins->mSrc[1].mTemp < 0) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, ins->mSrc[1].mIntConst & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (ins->mSrc[1].mIntConst >> 8) & 0xff)); + } + else if (ins->mSrc[1].mTemp != ins->mDst.mTemp) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp] + 1)); + } + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, treg + 1)); + } + + if (ins->mSrc[0].IsInRange(1, 15)) + this->Close(ins, lblock, nullptr, ASMIT_JMP); + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_CPX, ASMIM_IMMEDIATE, 0x00)); + this->Close(ins, lblock, eblock, ASMIT_BNE); + } + + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_CMP, ASMIM_IMMEDIATE, 0x80)); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_ROR, ASMIM_IMPLIED)); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_ROR, ASMIM_ZERO_PAGE, treg + 0)); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_DEX, ASMIM_IMPLIED)); + lblock->Close(ins, lblock, eblock, ASMIT_BNE); + + eblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + } + return eblock; + } + } break; + + } + } + + return this; +} + +void NativeCodeBasicBlock::SignExtendAddImmediate(InterCodeProcedure* proc, const InterInstruction* xins, const InterInstruction* ains) +{ + int val = ains->mSrc[0].mTemp == xins->mDst.mTemp ? int(ains->mSrc[1].mIntConst) : int(ains->mSrc[0].mIntConst); + val -= 128; + + mIns.Push(NativeCodeInstruction(xins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[xins->mSrc[0].mTemp] + 0)); + mIns.Push(NativeCodeInstruction(xins, ASMIT_EOR, ASMIM_IMMEDIATE, 0x80)); + mIns.Push(NativeCodeInstruction(ains, ASMIT_CLC)); + mIns.Push(NativeCodeInstruction(ains, ASMIT_ADC, ASMIM_IMMEDIATE, val & 0xff)); + mIns.Push(NativeCodeInstruction(ains, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ains->mDst.mTemp] + 0)); + mIns.Push(NativeCodeInstruction(ains, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + mIns.Push(NativeCodeInstruction(ains, ASMIT_ADC, ASMIM_IMMEDIATE, (val >> 8) & 0xff)); + mIns.Push(NativeCodeInstruction(ains, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ains->mDst.mTemp] + 1)); +} + +void NativeCodeBasicBlock::BinaryFloatOperatorLookup(InterCodeProcedure* proc, const InterInstruction* cins, const InterInstruction* ins) +{ + mIns.Push(NativeCodeInstruction(cins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[cins->mSrc[0].mTemp] + 0)); + mIns.Push(NativeCodeInstruction(cins, ASMIT_SEC)); + mIns.Push(NativeCodeInstruction(cins, ASMIT_SBC, ASMIM_IMMEDIATE, cins->mSrc[0].mRange.mMinValue)); + mIns.Push(NativeCodeInstruction(cins, ASMIT_TAX)); + + bool reverse = false; + double fconst; + + if (ins->mSrc[0].mTemp < 0) + { + fconst = ins->mSrc[0].mFloatConst; + } + else + { + fconst = ins->mSrc[1].mFloatConst; + if (ins->mOperator == IA_SUB || ins->mOperator == IA_DIVS) + reverse = true; + } + + for (int i = 0; i < 4; i++) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ABSOLUTE_X, 0, mProc->mGenerator->AllocateFloatTable(ins->mOperator, reverse, + int(cins->mSrc[0].mRange.mMinValue), int(cins->mSrc[0].mRange.mMaxValue), float(fconst), i))); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + i)); + } +} + +void NativeCodeBasicBlock::UnaryOperator(InterCodeProcedure* proc, NativeCodeProcedure* nproc, const InterInstruction * ins) +{ + int treg = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + + if (ins->mDst.mType == IT_FLOAT) + { + switch (ins->mOperator) + { + case IA_NEG: + case IA_ABS: + if (ins->mSrc[0].mTemp != ins->mDst.mTemp) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 3)); + } + else + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + 3)); + + if (ins->mOperator == IA_NEG) + mIns.Push(NativeCodeInstruction(ins, ASMIT_EOR, ASMIM_IMMEDIATE, 0x80)); + else + mIns.Push(NativeCodeInstruction(ins, ASMIT_AND, ASMIM_IMMEDIATE, 0x7f)); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + 3)); + break; + case IA_FLOOR: + case IA_CEIL: + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 3)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 3)); + + NativeCodeGenerator::Runtime& frx(nproc->mGenerator->ResolveRuntime(Ident::Unique("fsplita"))); + mIns.Push(NativeCodeInstruction(ins, ASMIT_JSR, ASMIM_ABSOLUTE, frx.mOffset, frx.mLinkerObject, NCIF_RUNTIME | NCIF_LOWER | NCIF_UPPER)); + + if (ins->mOperator == IA_FLOOR) + { + NativeCodeGenerator::Runtime& frt(nproc->mGenerator->ResolveRuntime(Ident::Unique("ffloor"))); + mIns.Push(NativeCodeInstruction(ins, ASMIT_JSR, ASMIM_ABSOLUTE, frt.mOffset, frt.mLinkerObject, NCIF_RUNTIME | NCIF_LOWER | NCIF_UPPER)); + } + else + { + NativeCodeGenerator::Runtime& frt(nproc->mGenerator->ResolveRuntime(Ident::Unique("fceil"))); + mIns.Push(NativeCodeInstruction(ins, ASMIT_JSR, ASMIM_ABSOLUTE, frt.mOffset, frt.mLinkerObject, NCIF_RUNTIME | NCIF_LOWER | NCIF_UPPER)); + } + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 3)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + 3)); + break; + } + } + else + { + switch (ins->mOperator) + { + case IA_NEG: + mIns.Push(NativeCodeInstruction(ins, ASMIT_SEC, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_SBC, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_SBC, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + if (ins->mDst.mType == IT_INT32) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_SBC, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_SBC, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 3)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 3)); + } + break; + + case IA_NOT: + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_EOR, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_EOR, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + if (ins->mDst.mType == IT_INT32) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_EOR, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_EOR, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 3)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 3)); + } + break; + } + } +} + +void NativeCodeBasicBlock::NumericConversion(InterCodeProcedure* proc, NativeCodeProcedure* nproc, const InterInstruction * ins) +{ + switch (ins->mOperator) + { + case IA_FLOAT2INT: + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 3)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 3)); + + NativeCodeGenerator::Runtime& frt(nproc->mGenerator->ResolveRuntime(Ident::Unique("ftoi"))); + mIns.Push(NativeCodeInstruction(ins, ASMIT_JSR, ASMIM_ABSOLUTE, frt.mOffset, frt.mLinkerObject, NCIF_RUNTIME | NCIF_LOWER | NCIF_UPPER)); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + 1)); + + } break; + case IA_INT2FLOAT: + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 1)); + + NativeCodeGenerator::Runtime& frt(nproc->mGenerator->ResolveRuntime(Ident::Unique("ffromi"))); + mIns.Push(NativeCodeInstruction(ins, ASMIT_JSR, ASMIM_ABSOLUTE, frt.mOffset, frt.mLinkerObject, NCIF_RUNTIME | NCIF_LOWER | NCIF_UPPER)); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 3)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + 3)); + + } break; + case IA_FLOAT2UINT: + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 3)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 3)); + + NativeCodeGenerator::Runtime& frt(nproc->mGenerator->ResolveRuntime(Ident::Unique("ftou"))); + mIns.Push(NativeCodeInstruction(ins, ASMIT_JSR, ASMIM_ABSOLUTE, frt.mOffset, frt.mLinkerObject, NCIF_RUNTIME | NCIF_LOWER | NCIF_UPPER)); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + 1)); + + } break; + case IA_UINT2FLOAT: + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 1)); + + NativeCodeGenerator::Runtime& frt(nproc->mGenerator->ResolveRuntime(Ident::Unique("ffromu"))); + mIns.Push(NativeCodeInstruction(ins, ASMIT_JSR, ASMIM_ABSOLUTE, frt.mOffset, frt.mLinkerObject, NCIF_RUNTIME | NCIF_LOWER | NCIF_UPPER)); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 3)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + 3)); + + } break; + case IA_FLOAT2LINT: + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 3)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 3)); + + NativeCodeGenerator::Runtime& frt(nproc->mGenerator->ResolveRuntime(Ident::Unique("ftoli"))); + mIns.Push(NativeCodeInstruction(ins, ASMIT_JSR, ASMIM_ABSOLUTE, frt.mOffset, frt.mLinkerObject, NCIF_RUNTIME | NCIF_LOWER | NCIF_UPPER)); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 3)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + 3)); + + } break; + case IA_LINT2FLOAT: + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 3)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 3)); + + NativeCodeGenerator::Runtime& frt(nproc->mGenerator->ResolveRuntime(Ident::Unique("ffromli"))); + mIns.Push(NativeCodeInstruction(ins, ASMIT_JSR, ASMIM_ABSOLUTE, frt.mOffset, frt.mLinkerObject, NCIF_RUNTIME | NCIF_LOWER | NCIF_UPPER)); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 3)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + 3)); + + } break; + case IA_FLOAT2LUINT: + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 3)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 3)); + + NativeCodeGenerator::Runtime& frt(nproc->mGenerator->ResolveRuntime(Ident::Unique("ftolu"))); + mIns.Push(NativeCodeInstruction(ins, ASMIT_JSR, ASMIM_ABSOLUTE, frt.mOffset, frt.mLinkerObject, NCIF_RUNTIME | NCIF_LOWER | NCIF_UPPER)); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 3)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + 3)); + + } break; + case IA_LUINT2FLOAT: + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 3)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 3)); + + NativeCodeGenerator::Runtime& frt(nproc->mGenerator->ResolveRuntime(Ident::Unique("ffromlu"))); + mIns.Push(NativeCodeInstruction(ins, ASMIT_JSR, ASMIM_ABSOLUTE, frt.mOffset, frt.mLinkerObject, NCIF_RUNTIME | NCIF_LOWER | NCIF_UPPER)); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 3)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + 3)); + + } break; + case IA_EXT8TO16S: + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp])); + if (ins->mSrc[0].mTemp != ins->mDst.mTemp) + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp])); + if (ins->mSrc[0].IsInRange(-128, -1)) + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0xff)); + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0x00)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ADC, ASMIM_IMMEDIATE, 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_EOR, ASMIM_IMMEDIATE, 0xff)); + } + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + 1)); + break; + case IA_EXT8TO16U: + if (ins->mSrc[0].mTemp != ins->mDst.mTemp) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp])); + } + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + 1)); + break; + case IA_EXT16TO32S: + if (ins->mSrc[0].IsUnsigned()) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + } + else + { + if (ins->mSrc[0].mTemp != ins->mDst.mTemp) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp])); + } + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 1)); + if (ins->mSrc[0].mTemp != ins->mDst.mTemp) + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0x00)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ADC, ASMIM_IMMEDIATE, 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_EOR, ASMIM_IMMEDIATE, 0xff)); + } + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + 3)); + break; + case IA_EXT16TO32U: + if (ins->mSrc[0].mTemp != ins->mDst.mTemp) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + 1)); + } + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + 3)); + break; + case IA_EXT8TO32S: + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp])); + if (ins->mSrc[0].mTemp != ins->mDst.mTemp) + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp])); + if (ins->mSrc[0].IsInRange(-128, -1)) + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0xff)); + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0x00)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ADC, ASMIM_IMMEDIATE, 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_EOR, ASMIM_IMMEDIATE, 0xff)); + } + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + 3)); + break; + case IA_EXT8TO32U: + if (ins->mSrc[0].mTemp != ins->mDst.mTemp) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp])); + } + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + 3)); + break; + } +} + +static const bool NearMemCompare(const InterInstruction* ins) +{ + if (ins->mSrc[0].mMemoryBase == ins->mSrc[1].mMemoryBase && ins->mSrc[0].mType == IT_POINTER) + { + if (ins->mSrc[0].mMemoryBase == IM_LOCAL || ins->mSrc[0].mMemoryBase == IM_GLOBAL) + { + if (ins->mSrc[0].mLinkerObject && ins->mSrc[0].mLinkerObject == ins->mSrc[1].mLinkerObject) + { + if (ins->mSrc[0].mLinkerObject->mSize < 256) + return true; + else if (ins->mSrc[0].mTemp < 0 && ins->mSrc[1].mRange.IsBound()) + { + if (ins->mSrc[1].mRange.mMaxValue - ins->mSrc[1].mRange.mMinValue < 256 && + ins->mSrc[0].mIntConst >= ins->mSrc[1].mRange.mMinValue && + ins->mSrc[0].mIntConst <= ins->mSrc[1].mRange.mMaxValue) + return true; + } + else if (ins->mSrc[1].mTemp < 0 && ins->mSrc[0].mRange.IsBound()) + { + if (ins->mSrc[0].mRange.mMaxValue - ins->mSrc[0].mRange.mMinValue < 256 && + ins->mSrc[1].mIntConst >= ins->mSrc[0].mRange.mMinValue && + ins->mSrc[1].mIntConst <= ins->mSrc[0].mRange.mMaxValue) + return true; + } + } + } + } + + return false; + +} + +void NativeCodeBasicBlock::RelationalOperator(InterCodeProcedure* proc, const InterInstruction * ins, NativeCodeProcedure* nproc, NativeCodeBasicBlock* trueJump, NativeCodeBasicBlock* falseJump) +{ + InterOperator op = ins->mOperator; + + bool scmp = (op == IA_CMPGES) || (op == IA_CMPLES) || (op == IA_CMPGS) || (op == IA_CMPLS) || (op == IA_CMPEQ) || (op == IA_CMPNE); + bool ucmp = (op == IA_CMPGEU) || (op == IA_CMPLEU) || (op == IA_CMPGU) || (op == IA_CMPLU) || (op == IA_CMPEQ) || (op == IA_CMPNE); + + if (ins->mSrc[0].mType == IT_FLOAT) + { + if (ins->mSrc[0].mTemp < 0 || ins->mSrc[1].mTemp < 0) + { + InterOperator op = ins->mOperator; + + int ci = 0, vi = 1; + if (ins->mSrc[1].mTemp < 0) + { + ci = 1; + vi = 0; + op = MirrorRelational(op); + } + + union { float f; unsigned int v; } cc; + cc.f = float(ins->mSrc[ci].mFloatConst); + + int ti = BC_REG_TMP + proc->mTempOffset[ins->mSrc[vi].mTemp]; + + if (cc.f == 0) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, ti + 3)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_AND, ASMIM_IMMEDIATE, 0x7f)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ORA, ASMIM_ZERO_PAGE, ti + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ORA, ASMIM_ZERO_PAGE, ti + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ORA, ASMIM_ZERO_PAGE, ti + 0)); + + if (op == IA_CMPEQ) + { + Close(ins, trueJump, falseJump, ASMIT_BEQ); + } + else if (op == IA_CMPNE) + { + Close(ins, trueJump, falseJump, ASMIT_BNE); + } + else + { + NativeCodeBasicBlock* nblock = nproc->AllocateBlock(); + + if (op == IA_CMPGES || op == IA_CMPGEU || op == IA_CMPLES || op == IA_CMPLEU) + Close(ins, trueJump, nblock, ASMIT_BEQ); + else + Close(ins, falseJump, nblock, ASMIT_BEQ); + + nblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, ti + 3)); + if (op == IA_CMPGES || op == IA_CMPGEU || op == IA_CMPGS || op == IA_CMPGU) + nblock->Close(ins, trueJump, falseJump, ASMIT_BPL); + else + nblock->Close(ins, trueJump, falseJump, ASMIT_BMI); + } + return; + } + else + { + NativeCodeBasicBlock* eblock1 = nproc->AllocateBlock(); + NativeCodeBasicBlock* eblock2 = nproc->AllocateBlock(); + NativeCodeBasicBlock* eblock3 = nproc->AllocateBlock(); + + + if (op == IA_CMPEQ) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, ti + 3)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_CMP, ASMIM_IMMEDIATE, (cc.v >> 24) & 0xff)); + Close(ins, eblock1, falseJump, ASMIT_BEQ); + eblock1->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, ti + 2)); + eblock1->mIns.Push(NativeCodeInstruction(ins, ASMIT_CMP, ASMIM_IMMEDIATE, (cc.v >> 16) & 0xff)); + eblock1->Close(ins, eblock2, falseJump, ASMIT_BEQ); + eblock2->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, ti + 1)); + eblock2->mIns.Push(NativeCodeInstruction(ins, ASMIT_CMP, ASMIM_IMMEDIATE, (cc.v >> 8) & 0xff)); + eblock2->Close(ins, eblock3, falseJump, ASMIT_BEQ); + eblock3->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, ti + 0)); + eblock3->mIns.Push(NativeCodeInstruction(ins, ASMIT_CMP, ASMIM_IMMEDIATE, cc.v & 0xff)); + eblock3->Close(ins, trueJump, falseJump, ASMIT_BEQ); + return; + } + else if (op == IA_CMPNE) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, ti + 3)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_CMP, ASMIM_IMMEDIATE, (cc.v >> 24) & 0xff)); + Close(ins, eblock1, trueJump, ASMIT_BEQ); + eblock1->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, ti + 2)); + eblock1->mIns.Push(NativeCodeInstruction(ins, ASMIT_CMP, ASMIM_IMMEDIATE, (cc.v >> 16) & 0xff)); + eblock1->Close(ins, eblock2, trueJump, ASMIT_BEQ); + eblock2->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, ti + 1)); + eblock2->mIns.Push(NativeCodeInstruction(ins, ASMIT_CMP, ASMIM_IMMEDIATE, (cc.v >> 8) & 0xff)); + eblock2->Close(ins, eblock3, trueJump, ASMIT_BEQ); + eblock3->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, ti + 0)); + eblock3->mIns.Push(NativeCodeInstruction(ins, ASMIT_CMP, ASMIM_IMMEDIATE, cc.v & 0xff)); + eblock3->Close(ins, falseJump, trueJump, ASMIT_BEQ); + return; + } + else if (op == IA_CMPGS || op == IA_CMPGES || op == IA_CMPGU || op == IA_CMPGEU) + { + NativeCodeBasicBlock* eblock0 = nproc->AllocateBlock(); + NativeCodeBasicBlock* eblock1 = nproc->AllocateBlock(); + NativeCodeBasicBlock* eblock2 = nproc->AllocateBlock(); + NativeCodeBasicBlock* eblock3 = nproc->AllocateBlock(); + NativeCodeBasicBlock* nblock = nproc->AllocateBlock(); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, ti + 3)); + if (cc.f < 0) + Close(ins, trueJump, eblock0, ASMIT_BPL); + else + Close(ins, falseJump, eblock0, ASMIT_BMI); + eblock0->mIns.Push(NativeCodeInstruction(ins, ASMIT_CMP, ASMIM_IMMEDIATE, (cc.v >> 24) & 0xff)); + eblock0->Close(ins, nblock, eblock1, ASMIT_BNE); + eblock1->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, ti + 2)); + eblock1->mIns.Push(NativeCodeInstruction(ins, ASMIT_CMP, ASMIM_IMMEDIATE, (cc.v >> 16) & 0xff)); + eblock1->Close(ins, nblock, eblock2, ASMIT_BNE); + eblock2->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, ti + 1)); + eblock2->mIns.Push(NativeCodeInstruction(ins, ASMIT_CMP, ASMIM_IMMEDIATE, (cc.v >> 8) & 0xff)); + eblock2->Close(ins, nblock, eblock3, ASMIT_BNE); + eblock3->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, ti + 0)); + eblock3->mIns.Push(NativeCodeInstruction(ins, ASMIT_CMP, ASMIM_IMMEDIATE, cc.v & 0xff)); + if (op == IA_CMPGES || op == IA_CMPLES || op == IA_CMPGEU || op == IA_CMPLEU) + eblock3->Close(ins, nblock, trueJump, ASMIT_BNE); + else + eblock3->Close(ins, nblock, falseJump, ASMIT_BNE); + + if (cc.f < 0) + nblock->Close(ins, trueJump, falseJump, ASMIT_BCC); + else + nblock->Close(ins, trueJump, falseJump, ASMIT_BCS); + return; + } + else + { + NativeCodeBasicBlock* eblock0 = nproc->AllocateBlock(); + NativeCodeBasicBlock* eblock1 = nproc->AllocateBlock(); + NativeCodeBasicBlock* eblock2 = nproc->AllocateBlock(); + NativeCodeBasicBlock* eblock3 = nproc->AllocateBlock(); + NativeCodeBasicBlock* nblock = nproc->AllocateBlock(); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, ti + 3)); + if (cc.f < 0) + Close(ins, falseJump, eblock0, ASMIT_BPL); + else + Close(ins, trueJump, eblock0, ASMIT_BMI); + eblock0->mIns.Push(NativeCodeInstruction(ins, ASMIT_CMP, ASMIM_IMMEDIATE, (cc.v >> 24) & 0xff)); + eblock0->Close(ins, nblock, eblock1, ASMIT_BNE); + eblock1->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, ti + 2)); + eblock1->mIns.Push(NativeCodeInstruction(ins, ASMIT_CMP, ASMIM_IMMEDIATE, (cc.v >> 16) & 0xff)); + eblock1->Close(ins, nblock, eblock2, ASMIT_BNE); + eblock2->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, ti + 1)); + eblock2->mIns.Push(NativeCodeInstruction(ins, ASMIT_CMP, ASMIM_IMMEDIATE, (cc.v >> 8) & 0xff)); + eblock2->Close(ins, nblock, eblock3, ASMIT_BNE); + eblock3->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, ti + 0)); + eblock3->mIns.Push(NativeCodeInstruction(ins, ASMIT_CMP, ASMIM_IMMEDIATE, cc.v & 0xff)); + if (op == IA_CMPGES || op == IA_CMPLES || op == IA_CMPGEU || op == IA_CMPLEU) + eblock3->Close(ins, nblock, trueJump, ASMIT_BNE); + else + eblock3->Close(ins, nblock, falseJump, ASMIT_BNE); + + if (cc.f < 0) + nblock->Close(ins, trueJump, falseJump, ASMIT_BCS); + else + nblock->Close(ins, trueJump, falseJump, ASMIT_BCC); + return; + } + } + } + + int li = 0, ri = 1; + if (op == IA_CMPLEU || op == IA_CMPGU || op == IA_CMPLES || op == IA_CMPGS) + { + li = 1; ri = 0; + } + + if (ins->mSrc[li].mTemp < 0) + { + union { float f; unsigned int v; } cc; + cc.f = float(ins->mSrc[li].mFloatConst); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, cc.v & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (cc.v >> 8) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (cc.v >> 16) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (cc.v >> 24) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 3)); + } + else if (ins->mSrc[li].mFinal && CheckPredAccuStore(BC_REG_TMP + proc->mTempOffset[ins->mSrc[li].mTemp])) + { + // cull previous store from accu to temp using direcrt forwarding + mIns.SetSize(mIns.Size() - 8); + } + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[li].mTemp] + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[li].mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[li].mTemp] + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[li].mTemp] + 3)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 3)); + } + + if (ins->mSrc[ri].mTemp < 0) + { + union { float f; unsigned int v; } cc; + cc.f = float(ins->mSrc[ri].mFloatConst); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, cc.v & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_WORK + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (cc.v >> 8) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_WORK + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (cc.v >> 16) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_WORK + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (cc.v >> 24) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_WORK + 3)); + } + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[ri].mTemp] + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_WORK + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[ri].mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_WORK + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[ri].mTemp] + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_WORK + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[ri].mTemp] + 3)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_WORK + 3)); + } + + NativeCodeGenerator::Runtime& frt(nproc->mGenerator->ResolveRuntime(Ident::Unique("fcmp"))); + mIns.Push(NativeCodeInstruction(ins, ASMIT_JSR, ASMIM_ABSOLUTE, frt.mOffset, frt.mLinkerObject, NCIF_RUNTIME | NCIF_LOWER | NCIF_UPPER | NCIF_JSRFLAGS)); + + switch (op) + { + case IA_CMPEQ: + Close(ins, trueJump, falseJump, ASMIT_BEQ); + break; + case IA_CMPNE: + Close(ins, falseJump, trueJump, ASMIT_BEQ); + break; + case IA_CMPLU: + case IA_CMPLS: + case IA_CMPGU: + case IA_CMPGS: + Close(ins, trueJump, falseJump, ASMIT_BMI); + break; + case IA_CMPLEU: + case IA_CMPLES: + case IA_CMPGEU: + case IA_CMPGES: + Close(ins, falseJump, trueJump, ASMIT_BMI); + break; + } + } + else if (ins->mSrc[0].mType == IT_POINTER) + { + int li = 1, ri = 0; + if (op == IA_CMPLEU || op == IA_CMPGU || op == IA_CMPLES || op == IA_CMPGS) + { + li = 0; ri = 1; + } + + int lreg = ins->mSrc[li].mTemp, rreg = ins->mSrc[ri].mTemp; + if (lreg < 0) + { + lreg = BC_REG_ACCU; + LoadConstantToReg(proc, ins, ins->mSrc[li], IT_POINTER, lreg, false); + } + else + lreg = BC_REG_TMP + proc->mTempOffset[lreg]; + + if (rreg < 0) + { + rreg = BC_REG_ACCU; + LoadConstantToReg(proc, ins, ins->mSrc[ri], IT_POINTER, rreg, false); + } + else + rreg = BC_REG_TMP + proc->mTempOffset[rreg]; + + if ((op == IA_CMPEQ || op == IA_CMPNE) && NearMemCompare(ins)) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, lreg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_CMP, ASMIM_ZERO_PAGE, rreg)); + + Close(ins, trueJump, falseJump, op == IA_CMPEQ ? ASMIT_BEQ : ASMIT_BNE); + } + else + { + NativeCodeBasicBlock* eblock = nproc->AllocateBlock(); + NativeCodeBasicBlock* nblock = nproc->AllocateBlock(); + + if (op == IA_CMPEQ || op == IA_CMPNE) + { + // Lower byte compare more likely to miss on not equal with first + // compare + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, lreg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_CMP, ASMIM_ZERO_PAGE, rreg)); + + Close(ins, nblock, eblock, ASMIT_BNE); + + eblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, lreg + 1)); + eblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_CMP, ASMIM_ZERO_PAGE, rreg + 1)); + } + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, lreg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_CMP, ASMIM_ZERO_PAGE, rreg + 1)); + + Close(ins, nblock, eblock, ASMIT_BNE); + + eblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, lreg)); + eblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_CMP, ASMIM_ZERO_PAGE, rreg)); + } + + switch (op) + { + case IA_CMPEQ: + nblock->Close(ins, falseJump, nullptr, ASMIT_JMP); + eblock->Close(ins, trueJump, falseJump, ASMIT_BEQ); + break; + case IA_CMPNE: + nblock->Close(ins, trueJump, nullptr, ASMIT_JMP); + eblock->Close(ins, falseJump, trueJump, ASMIT_BEQ); + break; + case IA_CMPLU: + case IA_CMPLS: + case IA_CMPGU: + case IA_CMPGS: + eblock->Close(ins, nblock, nullptr, ASMIT_JMP); + nblock->Close(ins, trueJump, falseJump, ASMIT_BCC); + break; + case IA_CMPLEU: + case IA_CMPLES: + case IA_CMPGEU: + case IA_CMPGES: + eblock->Close(ins, nblock, nullptr, ASMIT_JMP); + nblock->Close(ins, falseJump, trueJump, ASMIT_BCC); + break; + + } + } + } + else if (ins->mSrc[0].mType == IT_INT32) + { + if ((op >= IA_CMPGES && op <= IA_CMPLS) && + (ins->mSrc[0].mTemp < 0 && ins->mSrc[0].mIntConst >= 0 && ins->mSrc[0].mIntConst < 256 || + ins->mSrc[1].mTemp < 0 && ins->mSrc[1].mIntConst >= 0 && ins->mSrc[1].mIntConst < 256)) + { + NativeCodeBasicBlock* eblock3 = nproc->AllocateBlock(); + NativeCodeBasicBlock* eblock2 = nproc->AllocateBlock(); + NativeCodeBasicBlock* eblock1 = nproc->AllocateBlock(); + NativeCodeBasicBlock* eblock0 = nproc->AllocateBlock(); + + int li = 1, ri = 0; + if (ins->mSrc[1].mTemp < 0) + { + li = 0; + ri = 1; + op = MirrorRelational(op); + } + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[li].mTemp] + 3)); + if (op == IA_CMPGS || op == IA_CMPGES) + { + if (op == IA_CMPGES && ins->mSrc[ri].mIntConst == 0) + this->Close(ins, falseJump, trueJump, ASMIT_BMI); + else + { + this->Close(ins, falseJump, eblock3, ASMIT_BMI); + eblock3->Close(ins, trueJump, eblock2, ASMIT_BNE); + eblock2->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[li].mTemp] + 2)); + eblock2->Close(ins, trueJump, eblock1, ASMIT_BNE); + eblock1->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[li].mTemp] + 1)); + if (op == IA_CMPGS && ins->mSrc[ri].mIntConst == 255) + eblock1->Close(ins, trueJump, falseJump, ASMIT_BNE); + else + { + eblock1->Close(ins, trueJump, eblock0, ASMIT_BNE); + eblock0->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[li].mTemp])); + eblock0->mIns.Push(NativeCodeInstruction(ins, ASMIT_CMP, ASMIM_IMMEDIATE, ins->mSrc[ri].mIntConst + (op == IA_CMPGS))); + eblock0->Close(ins, trueJump, falseJump, ASMIT_BCS); + } + } + } + else + { + if (op == IA_CMPLS && ins->mSrc[ri].mIntConst == 0) + this->Close(ins, trueJump, falseJump, ASMIT_BMI); + else + { + this->Close(ins, trueJump, eblock3, ASMIT_BMI); + eblock3->Close(ins, falseJump, eblock2, ASMIT_BNE); + eblock2->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[li].mTemp] + 2)); + eblock2->Close(ins, falseJump, eblock1, ASMIT_BNE); + eblock1->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[li].mTemp] + 1)); + eblock1->Close(ins, falseJump, eblock0, ASMIT_BNE); + if (op == IA_CMPLES && ins->mSrc[ri].mIntConst == 255) + eblock1->Close(ins, falseJump, trueJump, ASMIT_BNE); + else + { + eblock0->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[li].mTemp])); + eblock0->mIns.Push(NativeCodeInstruction(ins, ASMIT_CMP, ASMIM_IMMEDIATE, ins->mSrc[ri].mIntConst + (op == IA_CMPLES))); + eblock0->Close(ins, falseJump, trueJump, ASMIT_BCS); + } + } + } + } + else + { + int li = 1, ri = 0; + if (op == IA_CMPLEU || op == IA_CMPGU || op == IA_CMPLES || op == IA_CMPGS) + { + li = 0; ri = 1; + op = MirrorRelational(op); + } + + NativeCodeBasicBlock* eblock3 = nproc->AllocateBlock(); + NativeCodeBasicBlock* eblock2 = nproc->AllocateBlock(); + NativeCodeBasicBlock* eblock1 = nproc->AllocateBlock(); + NativeCodeBasicBlock* nblock = nproc->AllocateBlock(); + NativeCodeBasicBlock* rblock = nproc->AllocateBlock(); + + + if (op >= IA_CMPGES && op <= IA_CMPLS) + { + if (ins->mSrc[ri].mTemp >= 0) + { + NativeCodeBasicBlock* eblock = nproc->AllocateBlock(); + + if (ins->mSrc[li].mTemp < 0) + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (ins->mSrc[li].mIntConst >> 24) & 0xff)); + else + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[li].mTemp] + 3)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_CMP, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[ri].mTemp] + 3)); + + this->Close(ins, eblock3, eblock, ASMIT_BEQ); + eblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_EOR, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[ri].mTemp] + 3)); + eblock->Close(ins, nblock, rblock, ASMIT_BPL); + } + else + { + if (ins->mSrc[ri].mTemp >= 0) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[ri].mTemp] + 3)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_EOR, ASMIM_IMMEDIATE, 0x80)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_WORK)); + } + + if (ins->mSrc[li].mTemp < 0) + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, ((ins->mSrc[li].mIntConst >> 24) & 0xff) ^ 0x80)); + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[li].mTemp] + 3)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_EOR, ASMIM_IMMEDIATE, 0x80)); + } + + if (ins->mSrc[ri].mTemp < 0) + mIns.Push(NativeCodeInstruction(ins, ASMIT_CMP, ASMIM_IMMEDIATE, ((ins->mSrc[ri].mIntConst >> 24) & 0xff) ^ 0x80)); + else + mIns.Push(NativeCodeInstruction(ins, ASMIT_CMP, ASMIM_ZERO_PAGE, BC_REG_WORK)); + this->Close(ins, nblock, eblock3, ASMIT_BNE); + } + } + else + { + if (ins->mSrc[li].mTemp < 0) + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (ins->mSrc[li].mIntConst >> 24) & 0xff)); + else + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[li].mTemp] + 3)); + if (ins->mSrc[ri].mTemp < 0) + mIns.Push(NativeCodeInstruction(ins, ASMIT_CMP, ASMIM_IMMEDIATE, (ins->mSrc[ri].mIntConst >> 24) & 0xff)); + else + mIns.Push(NativeCodeInstruction(ins, ASMIT_CMP, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[ri].mTemp] + 3)); + this->Close(ins, nblock, eblock3, ASMIT_BNE); + } + + + if (ins->mSrc[li].mTemp < 0) + eblock3->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (ins->mSrc[li].mIntConst >> 16) & 0xff)); + else + eblock3->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[li].mTemp] + 2)); + if (ins->mSrc[ri].mTemp < 0) + eblock3->mIns.Push(NativeCodeInstruction(ins, ASMIT_CMP, ASMIM_IMMEDIATE, (ins->mSrc[ri].mIntConst >> 16) & 0xff)); + else + eblock3->mIns.Push(NativeCodeInstruction(ins, ASMIT_CMP, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[ri].mTemp] + 2)); + + eblock3->Close(ins, nblock, eblock2, ASMIT_BNE); + + if (ins->mSrc[li].mTemp < 0) + eblock2->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (ins->mSrc[li].mIntConst >> 8) & 0xff)); + else + eblock2->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[li].mTemp] + 1)); + if (ins->mSrc[ri].mTemp < 0) + eblock2->mIns.Push(NativeCodeInstruction(ins, ASMIT_CMP, ASMIM_IMMEDIATE, (ins->mSrc[ri].mIntConst >> 8) & 0xff)); + else + eblock2->mIns.Push(NativeCodeInstruction(ins, ASMIT_CMP, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[ri].mTemp] + 1)); + + eblock2->Close(ins, nblock, eblock1, ASMIT_BNE); + + if (ins->mSrc[li].mTemp < 0) + eblock1->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, ins->mSrc[li].mIntConst & 0xff)); + else + eblock1->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[li].mTemp])); + if (ins->mSrc[ri].mTemp < 0) + eblock1->mIns.Push(NativeCodeInstruction(ins, ASMIT_CMP, ASMIM_IMMEDIATE, ins->mSrc[ri].mIntConst & 0xff)); + else + eblock1->mIns.Push(NativeCodeInstruction(ins, ASMIT_CMP, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[ri].mTemp])); + + switch (op) + { + case IA_CMPEQ: + nblock->Close(ins, falseJump, nullptr, ASMIT_JMP); + rblock->Close(ins, falseJump, nullptr, ASMIT_JMP); + eblock1->Close(ins, trueJump, falseJump, ASMIT_BEQ); + break; + case IA_CMPNE: + nblock->Close(ins, trueJump, nullptr, ASMIT_JMP); + rblock->Close(ins, trueJump, nullptr, ASMIT_JMP); + eblock1->Close(ins, falseJump, trueJump, ASMIT_BEQ); + break; + case IA_CMPLU: + case IA_CMPLS: + case IA_CMPGU: + case IA_CMPGS: + eblock1->Close(ins, nblock, nullptr, ASMIT_JMP); + nblock->Close(ins, trueJump, falseJump, ASMIT_BCC); + rblock->Close(ins, trueJump, falseJump, ASMIT_BCS); + break; + case IA_CMPLEU: + case IA_CMPLES: + case IA_CMPGEU: + case IA_CMPGES: + eblock1->Close(ins, nblock, nullptr, ASMIT_JMP); + nblock->Close(ins, trueJump, falseJump, ASMIT_BCS); + rblock->Close(ins, trueJump, falseJump, ASMIT_BCC); + break; + + } + } + } + else if (ins->mSrc[1].mTemp < 0 && ins->mSrc[1].mIntConst == 0 || ins->mSrc[0].mTemp < 0 && ins->mSrc[0].mIntConst == 0) + { + int rt = ins->mSrc[1].mTemp; + int ri = 1; + if (rt < 0) + { + rt = ins->mSrc[0].mTemp; + ri = 0; + switch (op) + { + case IA_CMPLEU: + op = IA_CMPGEU; + break; + case IA_CMPGEU: + op = IA_CMPLEU; + break; + case IA_CMPLU: + op = IA_CMPGU; + break; + case IA_CMPGU: + op = IA_CMPLU; + break; + case IA_CMPLES: + op = IA_CMPGES; + break; + case IA_CMPGES: + op = IA_CMPLES; + break; + case IA_CMPLS: + op = IA_CMPGS; + break; + case IA_CMPGS: + op = IA_CMPLS; + break; + } + } + + switch (op) + { + case IA_CMPEQ: + case IA_CMPLEU: + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[rt] + 0)); + if (InterTypeSize[ins->mSrc[0].mType] > 1 && !ins->mSrc[ri].IsUByte() && !ins->mSrc[ri].IsSByte()) + mIns.Push(NativeCodeInstruction(ins, ASMIT_ORA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[rt] + 1)); + this->Close(ins, trueJump, falseJump, ASMIT_BEQ); + break; + case IA_CMPNE: + case IA_CMPGU: + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[rt] + 0)); + if (InterTypeSize[ins->mSrc[0].mType] > 1 && !ins->mSrc[ri].IsUByte() && !ins->mSrc[ri].IsSByte()) + mIns.Push(NativeCodeInstruction(ins, ASMIT_ORA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[rt] + 1)); + this->Close(ins, trueJump, falseJump, ASMIT_BNE); + break; + case IA_CMPGEU: + this->Close(ins, trueJump, nullptr, ASMIT_JMP); + break; + case IA_CMPLU: + this->Close(ins, falseJump, nullptr, ASMIT_JMP); + break; + case IA_CMPGES: + if (InterTypeSize[ins->mSrc[0].mType] == 1 || ins->mSrc[ri].IsSByte()) + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[rt])); + else + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[rt] + 1)); + this->Close(ins, trueJump, falseJump, ASMIT_BPL); + break; + case IA_CMPLS: + if (InterTypeSize[ins->mSrc[0].mType] == 1 || ins->mSrc[ri].IsSByte()) + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[rt])); + else + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[rt] + 1)); + this->Close(ins, trueJump, falseJump, ASMIT_BMI); + break; + case IA_CMPGS: + { + NativeCodeBasicBlock* eblock = nproc->AllocateBlock(); + if (InterTypeSize[ins->mSrc[0].mType] == 1 || ins->mSrc[ri].IsSByte()) + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[rt])); + else + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[rt] + 1)); + this->Close(ins, eblock, falseJump, ASMIT_BPL); + if (InterTypeSize[ins->mSrc[0].mType] != 1 && !ins->mSrc[ri].IsSByte()) + eblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_ORA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[rt] + 0)); + eblock->Close(ins, trueJump, falseJump, ASMIT_BNE); + break; + } + case IA_CMPLES: + { + NativeCodeBasicBlock* eblock = nproc->AllocateBlock(); + if (InterTypeSize[ins->mSrc[0].mType] == 1 || ins->mSrc[ri].IsSByte()) + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[rt])); + else + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[rt] + 1)); + this->Close(ins, eblock, trueJump, ASMIT_BPL); + if (InterTypeSize[ins->mSrc[0].mType] != 1 && !ins->mSrc[ri].IsSByte()) + eblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_ORA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[rt] + 0)); + eblock->Close(ins, trueJump, falseJump, ASMIT_BEQ); + break; + } + + } + } + else if (InterTypeSize[ins->mSrc[0].mType] == 1 || + scmp && ins->mSrc[0].IsSByte() && ins->mSrc[1].IsSByte() || + ucmp && ins->mSrc[0].IsUByte() && ins->mSrc[1].IsUByte() || + op == IA_CMPLU && ins->mSrc[1].IsSByte() && ins->mSrc[0].IsSByte() && ins->mSrc[0].IsPositive()) + { + int li = 1, ri = 0; + if (op == IA_CMPLEU || op == IA_CMPGU || op == IA_CMPLES || op == IA_CMPGS) + { + li = 0; ri = 1; + op = MirrorRelational(op); + } + + int iconst = 0; + bool rconst = false; + + if (ins->mSrc[li].mTemp < 0 && op == IA_CMPGES && int16(ins->mSrc[li].mIntConst) < 127) + { + iconst = int(ins->mSrc[li].mIntConst) + 1; + rconst = true; + li = ri; ri = 1 - li; + op = IA_CMPLS; + } + else if (ins->mSrc[li].mTemp < 0 && op == IA_CMPLS && int16(ins->mSrc[li].mIntConst) < 127) + { + iconst = int(ins->mSrc[li].mIntConst) + 1; + rconst = true; + li = ri; ri = 1 - li; + op = IA_CMPGES; + } + else if (ins->mSrc[ri].mTemp < 0) + { + iconst = int(ins->mSrc[ri].mIntConst); + rconst = true; + } + + if (op >= IA_CMPGES && ins->mOperator <= IA_CMPLS) + { + if (!rconst) + { + NativeCodeBasicBlock* eblock = nproc->AllocateBlock(); + NativeCodeBasicBlock* nblock = nproc->AllocateBlock(); + NativeCodeBasicBlock* pblock = nproc->AllocateBlock(); + + NativeCodeBasicBlock* eqjmp, * gtjmp, * ltjmp; + + if (op == IA_CMPGES || op == IA_CMPLES) + eqjmp = trueJump; + else + eqjmp = falseJump; + + if (op == IA_CMPGS || op == IA_CMPGES) + { + gtjmp = trueJump; + ltjmp = falseJump; + } + else + { + gtjmp = falseJump; + ltjmp = trueJump; + } + + if (ins->mSrc[li].mTemp < 0) + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, ins->mSrc[li].mIntConst & 0xff)); + else + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[li].mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_CMP, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[ri].mTemp])); + + this->Close(ins, eqjmp, eblock, ASMIT_BEQ); + eblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_EOR, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[ri].mTemp])); + eblock->Close(ins, pblock, nblock, ASMIT_BCS); + + pblock->Close(ins, gtjmp, ltjmp, ASMIT_BPL); + nblock->Close(ins, ltjmp, gtjmp, ASMIT_BPL); + return; + } + else + { + if (ins->mSrc[li].mTemp < 0) + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (ins->mSrc[li].mIntConst & 0xff) ^ 0x80)); + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[li].mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_EOR, ASMIM_IMMEDIATE, 0x80)); + } + + mIns.Push(NativeCodeInstruction(ins, ASMIT_CMP, ASMIM_IMMEDIATE, (iconst & 0xff) ^ 0x80)); + } + } + else + { + if (ins->mSrc[li].mTemp < 0) + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, ins->mSrc[li].mIntConst & 0xff)); + else + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[li].mTemp])); + if (rconst) + mIns.Push(NativeCodeInstruction(ins, ASMIT_CMP, ASMIM_IMMEDIATE, iconst & 0xff)); + else + mIns.Push(NativeCodeInstruction(ins, ASMIT_CMP, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[ri].mTemp])); + } + + switch (op) + { + case IA_CMPEQ: + this->Close(ins, trueJump, falseJump, ASMIT_BEQ); + break; + case IA_CMPNE: + this->Close(ins, falseJump, trueJump, ASMIT_BEQ); + break; + case IA_CMPLU: + case IA_CMPLS: + case IA_CMPGU: + case IA_CMPGS: + this->Close(ins, trueJump, falseJump, ASMIT_BCC); + break; + case IA_CMPLEU: + case IA_CMPLES: + case IA_CMPGEU: + case IA_CMPGES: + this->Close(ins, falseJump, trueJump, ASMIT_BCC); + break; + + } + } +#if 1 + else if (ins->mSrc[1].mTemp < 0 && ins->mSrc[1].mIntConst < 256 && ins->mSrc[1].mIntConst > 0 || ins->mSrc[0].mTemp < 0 && ins->mSrc[0].mIntConst < 256 && ins->mSrc[0].mIntConst > 0) + { + int rt = ins->mSrc[1].mTemp; + int ival = int(ins->mSrc[0].mIntConst); + bool u8 = ins->mSrc[1].IsUByte(); + if (rt < 0) + { + rt = ins->mSrc[0].mTemp; + u8 = ins->mSrc[0].IsUByte();; + ival = int(ins->mSrc[1].mIntConst); + switch (op) + { + case IA_CMPLEU: + op = IA_CMPGEU; + break; + case IA_CMPGEU: + op = IA_CMPLEU; + break; + case IA_CMPLU: + op = IA_CMPGU; + break; + case IA_CMPGU: + op = IA_CMPLU; + break; + case IA_CMPLES: + op = IA_CMPGES; + break; + case IA_CMPGES: + op = IA_CMPLES; + break; + case IA_CMPLS: + op = IA_CMPGS; + break; + case IA_CMPGS: + op = IA_CMPLS; + break; + } + } + + NativeCodeBasicBlock* eblock = nproc->AllocateBlock(); + if (u8) + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + else + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[rt] + 1)); + + switch (op) + { + case IA_CMPEQ: + { + this->Close(ins, eblock, falseJump, ASMIT_BEQ); + eblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[rt] + 0)); + eblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_CMP, ASMIM_IMMEDIATE, ival)); + eblock->Close(ins, trueJump, falseJump, ASMIT_BEQ); + break; + } + case IA_CMPNE: + { + this->Close(ins, eblock, trueJump, ASMIT_BEQ); + eblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[rt] + 0)); + eblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_CMP, ASMIM_IMMEDIATE, ival)); + eblock->Close(ins, falseJump, trueJump, ASMIT_BEQ); + break; + } + + case IA_CMPLEU: + { + this->Close(ins, eblock, falseJump, ASMIT_BEQ); + eblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, ival)); + eblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_CMP, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[rt] + 0)); + eblock->Close(ins, falseJump, trueJump, ASMIT_BCC); + break; + } + + case IA_CMPGEU: + { + this->Close(ins, eblock, trueJump, ASMIT_BEQ); + eblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[rt] + 0)); + eblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_CMP, ASMIM_IMMEDIATE, ival)); + eblock->Close(ins, falseJump, trueJump, ASMIT_BCC); + break; + } + + case IA_CMPLU: + { + this->Close(ins, eblock, falseJump, ASMIT_BEQ); + eblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[rt] + 0)); + eblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_CMP, ASMIM_IMMEDIATE, ival)); + eblock->Close(ins, trueJump, falseJump, ASMIT_BCC); + break; + } + + case IA_CMPGU: + { + this->Close(ins, eblock, trueJump, ASMIT_BEQ); + eblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, ival)); + eblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_CMP, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[rt] + 0)); + eblock->Close(ins, trueJump, falseJump, ASMIT_BCC); + break; + } + + case IA_CMPLES: + { + NativeCodeBasicBlock* sblock = nproc->AllocateBlock(); + this->Close(ins, sblock, trueJump, ASMIT_BPL); + sblock->Close(ins, eblock, falseJump, ASMIT_BEQ); + + eblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, ival)); + eblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_CMP, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[rt] + 0)); + eblock->Close(ins, falseJump, trueJump, ASMIT_BCC); + break; + } + + case IA_CMPGES: + { + NativeCodeBasicBlock* sblock = nproc->AllocateBlock(); + this->Close(ins, sblock, falseJump, ASMIT_BPL); + sblock->Close(ins, eblock, trueJump, ASMIT_BEQ); + + eblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[rt] + 0)); + eblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_CMP, ASMIM_IMMEDIATE, ival)); + eblock->Close(ins, falseJump, trueJump, ASMIT_BCC); + break; + } + + case IA_CMPLS: + { + NativeCodeBasicBlock* sblock = nproc->AllocateBlock(); + this->Close(ins, sblock, trueJump, ASMIT_BPL); + sblock->Close(ins, eblock, falseJump, ASMIT_BEQ); + eblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[rt] + 0)); + eblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_CMP, ASMIM_IMMEDIATE, ival)); + eblock->Close(ins, trueJump, falseJump, ASMIT_BCC); + break; + } + + case IA_CMPGS: + { + NativeCodeBasicBlock* sblock = nproc->AllocateBlock(); + this->Close(ins, sblock, falseJump, ASMIT_BPL); + sblock->Close(ins, eblock, trueJump, ASMIT_BEQ); + eblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, ival)); + eblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_CMP, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[rt] + 0)); + eblock->Close(ins, trueJump, falseJump, ASMIT_BCC); + break; + } + + } + } +#endif +#if 1 + // Special case counting to 256 + else if (ins->mSrc[0].mTemp < 0 && ins->mSrc[0].mIntConst == 256 && op == IA_CMPLU && + ins->mSrc[1].mRange.mMinState == IntegerValueRange::S_BOUND && ins->mSrc[1].mRange.mMinValue > 0 && + ins->mSrc[1].mRange.mMaxState == IntegerValueRange::S_BOUND && ins->mSrc[1].mRange.mMaxValue == 256) + { + if (ins->mSrc[1].mTemp < 0) + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, ins->mSrc[1].mIntConst & 0xff)); + else + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp])); + this->Close(ins, falseJump, trueJump, ASMIT_BEQ); + } +#endif +#if 1 + // Special case >= or < with multiple of 256, no need to check low byte then + else if (ins->mSrc[0].mTemp < 0 && !(ins->mSrc[0].mIntConst & 255) && op == IA_CMPGEU) + { + if (ins->mSrc[1].mTemp < 0) + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, ins->mSrc[1].mIntConst >> 8)); + else + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_CMP, ASMIM_IMMEDIATE, ins->mSrc[0].mIntConst >> 8)); + this->Close(ins, trueJump, falseJump, ASMIT_BCS); + } +#endif + else if (ins->mSrc[0].IsUByte() && ins->mSrc[1].IsUByte()) + { + NativeCodeBasicBlock* eblock = nproc->AllocateBlock(); + NativeCodeBasicBlock* nblock = nproc->AllocateBlock(); + + int li = 1, ri = 0; + if (op == IA_CMPLEU || op == IA_CMPGU || op == IA_CMPLES || op == IA_CMPGS) + { + li = 0; ri = 1; + } + + int iconst = 0; + bool rconst = false; + + if (ins->mSrc[ri].mTemp < 0) + { + iconst = int(ins->mSrc[ri].mIntConst); + rconst = true; + } + + if (ins->mSrc[li].mTemp < 0) + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, ins->mSrc[li].mIntConst & 0xff)); + else + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[li].mTemp])); + if (rconst) + mIns.Push(NativeCodeInstruction(ins, ASMIT_CMP, ASMIM_IMMEDIATE, iconst & 0xff)); + else + mIns.Push(NativeCodeInstruction(ins, ASMIT_CMP, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[ri].mTemp])); + + switch (op) + { + case IA_CMPEQ: + this->Close(ins, trueJump, falseJump, ASMIT_BEQ); + break; + case IA_CMPNE: + this->Close(ins, falseJump, trueJump, ASMIT_BEQ); + break; + case IA_CMPLU: + case IA_CMPLS: + case IA_CMPGU: + case IA_CMPGS: + this->Close(ins, trueJump, falseJump, ASMIT_BCC); + break; + case IA_CMPLEU: + case IA_CMPLES: + case IA_CMPGEU: + case IA_CMPGES: + this->Close(ins, falseJump, trueJump, ASMIT_BCC); + break; + + } + + } + else + { + NativeCodeBasicBlock* eblock = nproc->AllocateBlock(); + NativeCodeBasicBlock* nblock = nproc->AllocateBlock(); + NativeCodeBasicBlock* rblock = nproc->AllocateBlock(); + NativeCodeBasicBlock* cblock = this; + + if (op == IA_CMPLU && ins->mSrc[0].mTemp == -1 && ins->mSrc[1].IsUnsigned() && ins->mSrc[1].mRange.mMaxValue == ins->mSrc[0].mIntConst) + op = IA_CMPNE; + + int li = 1, ri = 0; + if (op == IA_CMPLEU || op == IA_CMPGU || op == IA_CMPLES || op == IA_CMPGS) + { + li = 0; ri = 1; + } + + int iconst = 0; + bool rconst = false; + + if (ins->mSrc[li].mTemp < 0 && (op == IA_CMPGES || op == IA_CMPLS) && int16(ins->mSrc[li].mIntConst) > - 32768) + { + iconst = int(ins->mSrc[li].mIntConst) - 1; + rconst = true; + li = ri; ri = 1 - li; + + NativeCodeBasicBlock* t = trueJump; trueJump = falseJump; falseJump = t; + } + else if (ins->mSrc[li].mTemp < 0 && (op == IA_CMPLES || op == IA_CMPGS) && int16(ins->mSrc[li].mIntConst) < 32767) + { + iconst = int(ins->mSrc[li].mIntConst) + 1; + rconst = true; + li = ri; ri = 1 - li; + + NativeCodeBasicBlock* t = trueJump; trueJump = falseJump; falseJump = t; + } + else if (ins->mSrc[ri].mTemp < 0) + { + iconst = int(ins->mSrc[ri].mIntConst); + rconst = true; + } + + if (op >= IA_CMPGES && ins->mOperator <= IA_CMPLS) + { + if (ins->mSrc[li].mTemp >= 0 && ins->mSrc[ri].mTemp >= 0 && ins->mSrc[li].IsPositive() && (op == IA_CMPGS || op == IA_CMPLS)) + { + cblock = nproc->AllocateBlock(); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[ri].mTemp] + 1)); + + this->Close(ins, falseJump, cblock, ASMIT_BMI); + + cblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[li].mTemp] + 1)); + cblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_CMP, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[ri].mTemp] + 1)); + cblock->Close(ins, nblock, eblock, ASMIT_BNE); + } + else if (ins->mSrc[li].mTemp >= 0 && ins->mSrc[ri].mTemp >= 0 && ins->mSrc[ri].IsPositive() && (op == IA_CMPGES || op == IA_CMPLES)) + { + cblock = nproc->AllocateBlock(); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[li].mTemp] + 1)); + + this->Close(ins, falseJump, cblock, ASMIT_BMI); + + cblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[li].mTemp] + 1)); + cblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_CMP, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[ri].mTemp] + 1)); + cblock->Close(ins, nblock, eblock, ASMIT_BNE); + } + else if (ins->mSrc[li].mTemp >= 0 && ins->mSrc[ri].mTemp >= 0 && ins->mSrc[ri].IsPositive() && (op == IA_CMPGS || op == IA_CMPLS)) + { + cblock = nproc->AllocateBlock(); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[li].mTemp] + 1)); + + this->Close(ins, trueJump, cblock, ASMIT_BMI); + + cblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[li].mTemp] + 1)); + cblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_CMP, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[ri].mTemp] + 1)); + cblock->Close(ins, nblock, eblock, ASMIT_BNE); + } + else if (ins->mSrc[li].mTemp >= 0 && ins->mSrc[ri].mTemp >= 0 && ins->mSrc[li].IsPositive() && (op == IA_CMPGES || op == IA_CMPLES)) + { + cblock = nproc->AllocateBlock(); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[ri].mTemp] + 1)); + + this->Close(ins, trueJump, cblock, ASMIT_BMI); + + cblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[li].mTemp] + 1)); + cblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_CMP, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[ri].mTemp] + 1)); + cblock->Close(ins, nblock, eblock, ASMIT_BNE); + } + else + { + if (!rconst) + { + NativeCodeBasicBlock* sblock = nproc->AllocateBlock(); + + if (ins->mSrc[li].mTemp < 0) + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (ins->mSrc[li].mIntConst >> 8) & 0xff)); + else + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[li].mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_CMP, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[ri].mTemp] + 1)); + + this->Close(ins, eblock, sblock, ASMIT_BEQ); + sblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_EOR, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[ri].mTemp] + 1)); + sblock->Close(ins, nblock, rblock, ASMIT_BPL); + } + else + { + if (!rconst) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[ri].mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_EOR, ASMIM_IMMEDIATE, 0x80)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_WORK)); + } + + if (ins->mSrc[li].mTemp < 0) + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, ((ins->mSrc[li].mIntConst >> 8) & 0xff) ^ 0x80)); + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[li].mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_EOR, ASMIM_IMMEDIATE, 0x80)); + } + + if (rconst) + mIns.Push(NativeCodeInstruction(ins, ASMIT_CMP, ASMIM_IMMEDIATE, ((iconst >> 8) & 0xff) ^ 0x80)); + else + mIns.Push(NativeCodeInstruction(ins, ASMIT_CMP, ASMIM_ZERO_PAGE, BC_REG_WORK)); + cblock->Close(ins, nblock, eblock, ASMIT_BNE); + } + } + } + else + { + if (ins->mSrc[li].mTemp < 0) + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (ins->mSrc[li].mIntConst >> 8) & 0xff)); + else + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[li].mTemp] + 1)); + if (rconst) + mIns.Push(NativeCodeInstruction(ins, ASMIT_CMP, ASMIM_IMMEDIATE, (iconst >> 8) & 0xff)); + else + mIns.Push(NativeCodeInstruction(ins, ASMIT_CMP, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[ri].mTemp] + 1)); + cblock->Close(ins, nblock, eblock, ASMIT_BNE); + } + + + if (ins->mSrc[li].mTemp < 0) + eblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, ins->mSrc[li].mIntConst & 0xff)); + else + eblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[li].mTemp])); + if (rconst) + eblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_CMP, ASMIM_IMMEDIATE, iconst & 0xff)); + else + eblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_CMP, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[ri].mTemp])); + + switch (op) + { + case IA_CMPEQ: + nblock->Close(ins, falseJump, nullptr, ASMIT_JMP); + eblock->Close(ins, trueJump, falseJump, ASMIT_BEQ); + break; + case IA_CMPNE: + nblock->Close(ins, trueJump, nullptr, ASMIT_JMP); + eblock->Close(ins, falseJump, trueJump, ASMIT_BEQ); + break; + case IA_CMPLU: + case IA_CMPLS: + case IA_CMPGU: + case IA_CMPGS: + eblock->Close(ins, nblock, nullptr, ASMIT_JMP); + nblock->Close(ins, trueJump, falseJump, ASMIT_BCC); + rblock->Close(ins, trueJump, falseJump, ASMIT_BCS); + break; + case IA_CMPLEU: + case IA_CMPLES: + case IA_CMPGEU: + case IA_CMPGES: + eblock->Close(ins, nblock, nullptr, ASMIT_JMP); + nblock->Close(ins, falseJump, trueJump, ASMIT_BCC); + rblock->Close(ins, falseJump, trueJump, ASMIT_BCS); + break; + + } + } +} + +void NativeCodeBasicBlock::LoadStoreOpAbsolute2D(InterCodeProcedure* proc, const InterInstruction* lins1, const InterInstruction* lins2, const InterInstruction* mins) +{ + mIns.Push(NativeCodeInstruction(lins1, ASMIT_CLC)); + + if (lins1->mSrc[0].mTemp < 0) + mIns.Push(NativeCodeInstruction(lins1, ASMIT_LDA, ASMIM_IMMEDIATE, lins1->mSrc[0].mIntConst)); + else + mIns.Push(NativeCodeInstruction(lins1, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[lins1->mSrc[0].mTemp])); + + if (lins2->mSrc[0].mTemp < 0) + mIns.Push(NativeCodeInstruction(lins2, ASMIT_ADC, ASMIM_IMMEDIATE, lins2->mSrc[0].mIntConst)); + else + mIns.Push(NativeCodeInstruction(lins2, ASMIT_ADC, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[lins2->mSrc[0].mTemp])); + + int64 offset = lins1->mSrc[1].mIntConst; + + if (mins->mCode == IC_STORE) + offset += mins->mSrc[1].mIntConst; + else + offset += mins->mSrc[0].mIntConst; + + if (offset < 0) + { + if (lins1->mSrc[1].mLinkerObject->mSize - offset > 256) + { + mIns.Push(NativeCodeInstruction(mins, ASMIT_CLC)); + mIns.Push(NativeCodeInstruction(mins, ASMIT_ADC, ASMIM_IMMEDIATE, (offset & 255))); + offset = 0; + } + else + offset = (offset & 255) - 256; + } + else if (offset > 0) + { + if (!lins1->mSrc[0].IsUnsigned() || !lins2->mSrc[0].IsUnsigned()) + { + mIns.Push(NativeCodeInstruction(mins, ASMIT_CLC)); + mIns.Push(NativeCodeInstruction(mins, ASMIT_ADC, ASMIM_IMMEDIATE, (offset & 255))); + offset = 0; + } + else + offset &= 255; + } + + mIns.Push(NativeCodeInstruction(mins, ASMIT_TAY)); + + if (mins->mCode == IC_STORE) + { + for (int i = 0; i < InterTypeSize[mins->mSrc[0].mType]; i++) + { + if (mins->mSrc[0].mTemp < 0) + mIns.Push(NativeCodeInstruction(mins, ASMIT_LDA, ASMIM_IMMEDIATE, (mins->mSrc[0].mIntConst >> (8 * i)) & 0xff)); + else + mIns.Push(NativeCodeInstruction(mins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[mins->mSrc[0].mTemp] + i)); + mIns.Push(NativeCodeInstruction(mins, ASMIT_STA, ASMIM_ABSOLUTE_Y, offset + i, lins1->mSrc[1].mLinkerObject)); + } + } + else + { + for (int i = 0; i < InterTypeSize[mins->mDst.mType]; i++) + { + mIns.Push(NativeCodeInstruction(mins, ASMIT_LDA, ASMIM_ABSOLUTE_Y, offset + i, lins1->mSrc[1].mLinkerObject)); + mIns.Push(NativeCodeInstruction(mins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[mins->mDst.mTemp] + i)); + } + } +} + + +void NativeCodeBasicBlock::LoadEffectiveAddress(InterCodeProcedure* proc, const InterInstruction * ins, const InterInstruction* sins1, const InterInstruction* sins0, bool addrvalid, bool addrused) +{ + bool isub = false; + int ireg = ins->mSrc[0].mTemp; + AsmInsType iop = ASMIT_ADC; + + if (sins0) + { + isub = true; + ireg = sins0->mSrc[0].mTemp; + iop = ASMIT_SBC; + } + + if (sins1) + { + if (ins->mSrc[0].mTemp < 0 && ins->mSrc[0].mIntConst == 0) + LoadValueToReg(proc, sins1, ins->mDst.mTemp, nullptr, nullptr); + else + { + if (ins->mSrc[0].mTemp < 0) + { + NativeCodeInstruction ainsl(ins, ASMIT_ADC, ASMIM_IMMEDIATE, ins->mSrc[0].mIntConst & 0xff); + NativeCodeInstruction ainsh(ins, ASMIT_ADC, ASMIM_IMMEDIATE, (ins->mSrc[0].mIntConst >> 8) & 0xff); + + LoadValueToReg(proc, sins1, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp], &ainsl, &ainsh); + } + else + { + NativeCodeInstruction ainsl(ins, iop, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ireg]); + if (ins->mSrc[0].IsUByte()) + { + NativeCodeInstruction ainsh(ins, iop, ASMIM_IMMEDIATE, 0); + + LoadValueToReg(proc, sins1, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp], &ainsl, &ainsh); + } + else + { + NativeCodeInstruction ainsh(ins, iop, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ireg] + 1); + + LoadValueToReg(proc, sins1, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp], &ainsl, &ainsh); + } + } + } + } + else if (ins->mSrc[1].mTemp < 0) + { + + mIns.Push(NativeCodeInstruction(ins, isub ? ASMIT_SEC : ASMIT_CLC, ASMIM_IMPLIED)); + if (ins->mSrc[1].mMemory == IM_GLOBAL) + { + if (!ins->mSrc[0].IsUnsigned() && ins->mSrc[1].mIntConst != 0 && ins->mSrc[1].mLinkerObject->mSize < 256) + { + // Negative index, small global + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (ins->mSrc[1].mIntConst) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, iop, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ireg])); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_CLC)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ADC, ASMIM_IMMEDIATE_ADDRESS, 0, ins->mSrc[1].mLinkerObject, NCIF_LOWER)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE_ADDRESS, 0, ins->mSrc[1].mLinkerObject, NCIF_UPPER)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ADC, ASMIM_IMMEDIATE, 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + 1)); + } + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE_ADDRESS, ins->mSrc[1].mIntConst & 0xffff, ins->mSrc[1].mLinkerObject, NCIF_LOWER)); + mIns.Push(NativeCodeInstruction(ins, iop, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ireg])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp])); + // if the global variable is smaller than 256 bytes, we can safely ignore the upper byte? + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE_ADDRESS, ins->mSrc[1].mIntConst & 0xffff, ins->mSrc[1].mLinkerObject, NCIF_UPPER)); +#if 1 + if (ins->mSrc[0].IsUByte()) + mIns.Push(NativeCodeInstruction(ins, iop, ASMIM_IMMEDIATE, 0)); + else if ((ins->mSrc[1].mIntConst == 0 || ins->mSrc[0].IsUnsigned()) && + (ins->mSrc[1].mLinkerObject->mSize - ins->mSrc[1].mIntConst < 256 || (addrvalid && ins->mSrc[1].mLinkerObject->mSize - ins->mSrc[1].mIntConst <= 256))) + mIns.Push(NativeCodeInstruction(ins, iop, ASMIM_IMMEDIATE, 0)); + else +#endif + mIns.Push(NativeCodeInstruction(ins, iop, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ireg] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + 1)); + } + } + else if (ins->mSrc[1].mMemory == IM_ABSOLUTE) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, ins->mSrc[1].mIntConst & 0xff)); + mIns.Push(NativeCodeInstruction(ins, iop, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ireg])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (ins->mSrc[1].mIntConst >> 8) & 0xff)); +#if 1 + if (ins->mSrc[0].IsUByte()) + { + if (iop == ASMIT_ADC && ins->mSrc[0].mRange.mMaxState == IntegerValueRange::S_BOUND && ins->mSrc[0].mRange.mMaxValue + (ins->mSrc[1].mIntConst & 0xff) < 0x100 && addrused) + ; + else + mIns.Push(NativeCodeInstruction(ins, iop, ASMIM_IMMEDIATE, 0)); + } + else +#endif + mIns.Push(NativeCodeInstruction(ins, iop, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ireg] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + 1)); + } + else if (ins->mSrc[1].mMemory == IM_PROCEDURE) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE_ADDRESS, ins->mSrc[0].mIntConst, ins->mSrc[1].mLinkerObject, NCIF_LOWER)); + mIns.Push(NativeCodeInstruction(ins, iop, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ireg])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE_ADDRESS, ins->mSrc[0].mIntConst, ins->mSrc[1].mLinkerObject, NCIF_UPPER)); +#if 1 + if (ins->mSrc[0].IsUByte()) + mIns.Push(NativeCodeInstruction(ins, iop, ASMIM_IMMEDIATE, 0)); + else +#endif + mIns.Push(NativeCodeInstruction(ins, iop, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ireg] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + 1)); + } + else if (ins->mSrc[1].mMemory == IM_FPARAM || ins->mSrc[1].mMemory == IM_FFRAME) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, BC_REG_FPARAMS + ins->mSrc[1].mVarIndex + ins->mSrc[1].mIntConst)); + mIns.Push(NativeCodeInstruction(ins, iop, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ireg])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + if (ins->mSrc[0].IsUByte()) + mIns.Push(NativeCodeInstruction(ins, iop, ASMIM_IMMEDIATE, 0)); + else + mIns.Push(NativeCodeInstruction(ins, iop, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ireg] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + 1)); + } + else if (ins->mSrc[1].mMemory == IM_LOCAL || ins->mSrc[1].mMemory == IM_PARAM) + { + int dreg = BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp]; + int index = int(ins->mSrc[1].mIntConst); + int areg = mNoFrame ? BC_REG_STACK : BC_REG_LOCALS; + if (ins->mSrc[1].mMemory == IM_LOCAL) + index += proc->mLocalVars[ins->mSrc[1].mVarIndex]->mOffset; + else + index += ins->mSrc[1].mVarIndex + proc->mLocalSize + 2; + index += mFrameOffset; + + if (ins->mSrc[0].IsUByte() && ins->mSrc[0].mRange.mMaxValue + index < 256 && !isub && index >= 0) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ireg])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ADC, ASMIM_IMMEDIATE, index)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_CLC, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ADC, ASMIM_ZERO_PAGE, areg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, dreg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, areg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ADC, ASMIM_IMMEDIATE, 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, dreg + 1)); + } + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, areg)); + mIns.Push(NativeCodeInstruction(ins, iop, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ireg])); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, dreg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, areg + 1)); + if (ins->mSrc[0].IsUByte()) + mIns.Push(NativeCodeInstruction(ins, iop, ASMIM_IMMEDIATE, 0)); + else + mIns.Push(NativeCodeInstruction(ins, iop, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ireg] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, dreg + 1)); + + if (index != 0) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_CLC, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, dreg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ADC, ASMIM_IMMEDIATE, index & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, dreg)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, dreg + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_ADC, ASMIM_IMMEDIATE, (index >> 8) & 0xff)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, dreg + 1)); + } + } + } + } + else + { + bool crossing = true; + if (ins->mSrc[1].mMemoryBase == IM_GLOBAL && ins->mSrc[1].mLinkerObject && (ins->mSrc[1].mLinkerObject->mFlags & LOBJF_NEVER_CROSS)) + crossing = false; + else if (ins->mSrc[1].mMemoryBase == IM_ABSOLUTE && ins->mSrc[1].mRange.IsBound() && ins->mDst.mRange.IsBound() && + ((ins->mSrc[1].mRange.mMinValue ^ ins->mDst.mRange.mMaxValue) & ~0xff) == 0 && + ((ins->mSrc[1].mRange.mMaxValue ^ ins->mDst.mRange.mMinValue) & ~0xff) == 0) + crossing = false; + + if (ins->mSrc[0].mTemp >= 0 || ins->mSrc[0].mIntConst != 0) + mIns.Push(NativeCodeInstruction(ins, isub ? ASMIT_SEC : ASMIT_CLC, ASMIM_IMPLIED)); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp])); + + if (ins->mSrc[0].mTemp < 0) + { + if (ins->mSrc[0].mIntConst) + mIns.Push(NativeCodeInstruction(ins, iop, ASMIM_IMMEDIATE, ins->mSrc[0].mIntConst & 0xff)); + } + else if (ins->mSrc[0].IsUByte()) + mIns.Push(NativeCodeInstruction(ins, iop, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ireg], nullptr, NCIF_LOWER | NCIF_UPPER, 0, int(ins->mSrc[0].mRange.mMinValue), int(ins->mSrc[0].mRange.mMaxValue))); + else + mIns.Push(NativeCodeInstruction(ins, iop, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ireg])); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp])); + + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp] + 1)); + + if (crossing) + { + if (ins->mSrc[0].mTemp < 0) + { + if (ins->mSrc[0].mIntConst) + mIns.Push(NativeCodeInstruction(ins, ASMIT_ADC, ASMIM_IMMEDIATE, (ins->mSrc[0].mIntConst >> 8) & 0xff)); + } + else if (ins->mSrc[0].IsUByte()) + mIns.Push(NativeCodeInstruction(ins, iop, ASMIM_IMMEDIATE, 0)); + else + mIns.Push(NativeCodeInstruction(ins, iop, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ireg] + 1)); + } + + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + 1)); + } +} + +void NativeCodeBasicBlock::CallFunction(InterCodeProcedure* proc, NativeCodeProcedure * nproc, const InterInstruction * ins) +{ + if (ins->mSrc[0].mTemp < 0) + { + if (ins->mSrc[0].mLinkerObject) + { + NativeCodeInstruction lins(ins, ASMIT_LDA, ASMIM_IMMEDIATE_ADDRESS, ins->mSrc[0].mIntConst, ins->mSrc[0].mLinkerObject, NCIF_LOWER); + NativeCodeInstruction hins(ins, ASMIT_LDA, ASMIM_IMMEDIATE_ADDRESS, ins->mSrc[0].mIntConst, ins->mSrc[0].mLinkerObject, NCIF_UPPER); + + mIns.Push(lins); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU)); + mIns.Push(hins); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 1)); + } + else + { + NativeCodeInstruction lins(ins, ASMIT_LDA, ASMIM_IMMEDIATE, ins->mSrc[0].mIntConst & 0xff); + NativeCodeInstruction hins(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (ins->mSrc[0].mIntConst >> 8) & 0xff); + + mIns.Push(lins); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU)); + mIns.Push(hins); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 1)); + } + } + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 1)); + } + + NativeCodeGenerator::Runtime& frt(nproc->mGenerator->ResolveRuntime(Ident::Unique("bcexec"))); + mIns.Push(NativeCodeInstruction(ins, ASMIT_JSR, ASMIM_ABSOLUTE, frt.mOffset, frt.mLinkerObject, NCIF_RUNTIME | NCIF_LOWER | NCIF_UPPER | NCIF_FEXEC)); + + if (ins->mDst.mTemp >= 0) + { + if (ins->mDst.mType == IT_FLOAT) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 3)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + 3)); + } + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + 0)); + if (InterTypeSize[ins->mDst.mType] > 1) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + 1)); + } + if (InterTypeSize[ins->mDst.mType] > 2) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 3)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + 3)); + } + } + } +} + + +void NativeCodeBasicBlock::DisassembleBody(FILE* file) +{ + fprintf(file, "L%d: (", mIndex); + bool first = true; + if (mEntryRequiredRegs.Size() > 0) + { + for (int i = 0; i < 256; i++) + { + if (mEntryRequiredRegs[i]) + { + fprintf(file, "%02x", i); + if (!first) + fprintf(file, ", "); + first = false; + } + } + } + + fprintf(file, ")"); + + if (mNDataSet[CPU_REG_C].mMode == NRDM_IMMEDIATE) + { + fprintf(file, ",C=%d", mNDataSet[CPU_REG_C].mValue); + } + if (mNDataSet[CPU_REG_Z].mMode == NRDM_IMMEDIATE) + { + fprintf(file, ",Z=%d", mNDataSet[CPU_REG_Z].mValue); + } + fprintf(file, "\n"); + + for (int i = 0; i < mIns.Size(); i++) + { + fprintf(file, "%03d (%4d) ", i, mIns[i].mIns ? mIns[i].mIns->mLocation.mLine : 0); + mIns[i].Disassemble(file); + fprintf(file, "\n"); + } + fprintf(file, "%03d (%4d) %s", mIns.Size(), mBranchIns ? mBranchIns->mLocation.mLine : 0, AsmInstructionNames[mBranch]); + if (mTrueJump) + { + fprintf(file, " L%d", mTrueJump->mIndex); + if (mFalseJump) + fprintf(file, ", L%d", mFalseJump->mIndex); + } + fprintf(file, "\n"); +} + +void NativeCodeBasicBlock::Disassemble(FILE* file) +{ + if (!mVisited) + { + mVisited = true; + + DisassembleBody(file); + + if (mTrueJump) mTrueJump->Disassemble(file); + if (mFalseJump) mFalseJump->Disassemble(file); + } +} + + +NativeCodeInstruction NativeCodeBasicBlock::DecodeNative(const InterInstruction* ins, LinkerObject* lobj, int& offset) const +{ + uint8 op = lobj->mData[offset++]; + + AsmInsData d = DecInsData[op]; + int address = 0; + LinkerObject * linkerObject = nullptr; + uint32 flags = NCIF_LOWER | NCIF_UPPER; + LinkerReference * lref; + + switch (d.mMode) + { + case ASMIM_ABSOLUTE: + case ASMIM_ABSOLUTE_X: + case ASMIM_ABSOLUTE_Y: + case ASMIM_INDIRECT: + lref = lobj->FindReference(offset); + address = lobj->mData[offset++]; + address += lobj->mData[offset++] << 8; + if (lref) + { + linkerObject = lref->mRefObject; + address = lref->mRefOffset; + } + else if (d.mType != ASMIT_JSR) + flags |= NCIF_VOLATILE; + break; + case ASMIM_ZERO_PAGE: + case ASMIM_ZERO_PAGE_X: + case ASMIM_ZERO_PAGE_Y: + case ASMIM_INDIRECT_X: + case ASMIM_INDIRECT_Y: + lref = lobj->FindReference(offset); + address = lobj->mData[offset++]; + if (lref && (lref->mFlags & LREF_TEMPORARY)) + address += lobj->mTemporaries[lref->mRefOffset]; + else if (lref) + { + linkerObject = lref->mRefObject; + address = lref->mRefOffset; + } + else if (address >= BC_REG_TMP || d.mMode == ASMIM_ZERO_PAGE && address <= 0x01) + flags |= NCIF_VOLATILE; + break; + case ASMIM_RELATIVE: + address = lobj->mData[offset++]; + address += offset; + break; + case ASMIM_IMMEDIATE: + lref = lobj->FindReference(offset); + address = lobj->mData[offset++]; + if (lref) + { + if (lref->mFlags & LREF_TEMPORARY) + { + address = lobj->mTemporaries[lref->mRefOffset]; + flags |= NICF_TMPREF | NCIF_LOWER; + if (lobj->mTempSizes[lref->mRefOffset] > 1) + flags |= NCIF_UPPER; + } + else + { + d.mMode = ASMIM_IMMEDIATE_ADDRESS; + linkerObject = lref->mRefObject; + address = lref->mRefOffset; + if (lref->mFlags & LREF_LOWBYTE) + flags = NCIF_LOWER; + else + flags = NCIF_UPPER; + } + } + break; + } + + return NativeCodeInstruction(ins, d.mType, d.mMode, address, linkerObject, flags); +} + + +void NativeCodeBasicBlock::CallAssembler(InterCodeProcedure* proc, NativeCodeProcedure * nproc, const InterInstruction* ins) +{ + if (ins->mCode == IC_ASSEMBLER) + { + if (ins->mNumOperands > 1 && ins->mSrc[0].mLinkerObject->mNumTemporaries) + { + ins->mSrc[0].mLinkerObject = ins->mSrc[0].mLinkerObject->CloneAssembler(proc->mModule->mLinker); + } + + for (int i = 1; i < ins->mNumOperands; i++) + { + if (ins->mSrc[i].mTemp < 0) + { + if (ins->mSrc[i].mMemory == IM_FPARAM || ins->mSrc[i].mMemory == IM_FFRAME) + ins->mSrc[0].mLinkerObject->mTemporaries[i - 1] = BC_REG_FPARAMS + ins->mSrc[i].mVarIndex + int(ins->mSrc[i].mIntConst); + } + else + ins->mSrc[0].mLinkerObject->mTemporaries[i - 1] = BC_REG_TMP + proc->mTempOffset[ins->mSrc[i].mTemp]; + ins->mSrc[0].mLinkerObject->mTempSizes[i - 1] = InterTypeSize[ins->mSrc[i].mType]; + } + ins->mSrc[0].mLinkerObject->mNumTemporaries = ins->mNumOperands - 1; + } + + uint32 lf = 0; + + if (ins->mSrc[0].mTemp < 0) + { + uint32 flags = NCIF_LOWER | NCIF_UPPER; + if (ins->mSrc[0].mLinkerObject->mFlags & LOBJF_ARG_REG_A) + { + flags |= NCIF_USE_CPU_REG_A; + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_FPARAMS)); + } + if (ins->mSrc[0].mLinkerObject->mFlags & LOBJF_ARG_REG_X) + { + flags |= NCIF_USE_CPU_REG_X; + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDX, ASMIM_ZERO_PAGE, BC_REG_FPARAMS + 1)); + } + if (ins->mSrc[0].mLinkerObject->mFlags & LOBJF_ARG_REG_Y) + flags |= NCIF_USE_CPU_REG_Y; + + uint32 pflags = 0; + if (ins->mSrc[0].mLinkerObject->mFlags & LOBJF_PRESERVE_REG_A) + pflags |= NCIF_PRESERVE_CPU_REG_A; + if (ins->mSrc[0].mLinkerObject->mFlags & LOBJF_PRESERVE_REG_X) + pflags |= NCIF_PRESERVE_CPU_REG_X; + if (ins->mSrc[0].mLinkerObject->mFlags & LOBJF_PRESERVE_REG_Y) + pflags |= NCIF_PRESERVE_CPU_REG_Y; + + assert(ins->mSrc[0].mLinkerObject); + + if (ins->mCode == IC_ASSEMBLER && (proc->mCompilerOptions & COPT_OPTIMIZE_ASSEMBLER) && ins->mSrc[0].mLinkerObject->mSection == proc->mLinkerObject->mSection && !ins->mVolatile) + { + ExpandingArray tains; + + uint32 uflags = 0; + + bool simple = true, jsrs = false; + int i = 0; + while (i < ins->mSrc[0].mLinkerObject->mSize) + { + NativeCodeInstruction dins = DecodeNative(ins, ins->mSrc[0].mLinkerObject, i); + if (dins.mMode == ASMIM_RELATIVE) + simple = false; + if (dins.mType == ASMIT_JMP) + simple = false; + if (dins.mType == ASMIT_RTS && i != ins->mSrc[0].mLinkerObject->mSize) + simple = false; + if (dins.mType == ASMIT_JSR) + { + dins.mFlags |= uflags | NCIF_JSRFLAGS; + jsrs = true; + } + + if (dins.mType == ASMIT_BRK || dins.mMode == ASMIM_INDIRECT_X || dins.mMode == ASMIM_INDIRECT || + dins.mType == ASMIT_SEI || dins.mType == ASMIT_CLI || dins.mType == ASMIT_SED || dins.mType == ASMIT_CLD || + dins.mType == ASMIT_RTI || dins.mType == ASMIT_TXS || dins.mType == ASMIT_TSX || + dins.mType == ASMIT_PHP || dins.mType == ASMIT_PLP || dins.mType == ASMIT_PHA || dins.mType == ASMIT_PLA) + simple = false; + if (dins.mFlags & NCIF_VOLATILE) + simple = false; + + if (dins.mMode == ASMIM_ZERO_PAGE) + { + if (dins.mAddress >= BC_REG_WORK && dins.mAddress < BC_REG_WORK + 8) + uflags |= NICF_USE_WORKREGS; + else if (dins.mLinkerObject) + dins.mMode = ASMIM_ABSOLUTE; // Make sure we are not aliasing zeropage globals + } + + if (dins.ChangesAccu()) + uflags |= NCIF_USE_CPU_REG_A; + if (dins.ChangesXReg()) + uflags |= NCIF_USE_CPU_REG_X; + if (dins.ChangesYReg()) + uflags |= NCIF_USE_CPU_REG_Y; + if (dins.ChangesCarry()) + uflags |= NCIF_USE_CPU_REG_C; + tains.Push(dins); + } + + if (simple) + { + if (jsrs && (uflags & (NCIF_USE_CPU_REG_A | NCIF_USE_CPU_REG_X | NCIF_USE_CPU_REG_Y))) + { + int live = 0; + for (int i = tains.Size() - 1; i >= 0; i--) + { + if (tains[i].mType == ASMIT_JSR) + { + if (live & LIVE_CPU_REG_A) + tains[i].mFlags |= NCIF_PROVIDE_CPU_REG_A; + if (live & LIVE_CPU_REG_X) + tains[i].mFlags |= NCIF_PROVIDE_CPU_REG_X; + if (live & LIVE_CPU_REG_Y) + tains[i].mFlags |= NCIF_PROVIDE_CPU_REG_Y; + tains[i].mFlags |= pflags; + live = 0; + } + else + { + if (tains[i].ChangesAccu()) + live &= ~LIVE_CPU_REG_A; + if (tains[i].ChangesXReg()) + live &= ~LIVE_CPU_REG_X; + if (tains[i].ChangesYReg()) + live &= ~LIVE_CPU_REG_Y; + if (tains[i].RequiresAccu()) + live |= LIVE_CPU_REG_A; + if (tains[i].RequiresXReg()) + live |= LIVE_CPU_REG_X; + if (tains[i].RequiresYReg()) + live |= LIVE_CPU_REG_Y; + } + } + } + + for (int i = 0; i + 1 < tains.Size(); i++) + mIns.Push(tains[i]); + } + else + mIns.Push(NativeCodeInstruction(ins, ASMIT_JSR, ASMIM_ABSOLUTE, ins->mSrc[0].mIntConst, ins->mSrc[0].mLinkerObject, flags)); + } + else + mIns.Push(NativeCodeInstruction(ins, ASMIT_JSR, ASMIM_ABSOLUTE, ins->mSrc[0].mIntConst, ins->mSrc[0].mLinkerObject, flags)); + + lf = ins->mSrc[0].mLinkerObject->mFlags; + } + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mSrc[0].mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 1)); + + NativeCodeGenerator::Runtime& frt(nproc->mGenerator->ResolveRuntime(Ident::Unique("bcexec"))); + mIns.Push(NativeCodeInstruction(ins, ASMIT_JSR, ASMIM_ABSOLUTE, frt.mOffset, frt.mLinkerObject, NCIF_RUNTIME | NCIF_LOWER | NCIF_UPPER | NCIF_FEXEC)); + } + + if (ins->mDst.mTemp >= 0) + { + if (ins->mDst.mType == IT_FLOAT) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 3)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + 3)); + } + else + { + if (!(lf & LOBJF_RET_REG_A)) + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 0)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + 0)); + if (InterTypeSize[ins->mDst.mType] > 1) + { + if (lf & LOBJF_RET_REG_X) + mIns.Push(NativeCodeInstruction(ins, ASMIT_STX, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + 1)); + else + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 1)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + 1)); + } + } + if (InterTypeSize[ins->mDst.mType] > 2) + { + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + 2)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 3)); + mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + proc->mTempOffset[ins->mDst.mTemp] + 3)); + } + } + } +} + +void NativeCodeBasicBlock::BuildLocalRegSets(void) +{ + int i; + + if (!mVisited) + { + mVisited = true; + + mLocalRequiredRegs.Reset(NUM_REGS); + mLocalProvidedRegs.Reset(NUM_REGS); + + mEntryRequiredRegs.Reset(NUM_REGS); + mEntryProvidedRegs.Reset(NUM_REGS); + mExitRequiredRegs.Reset(NUM_REGS); + mExitProvidedRegs.Reset(NUM_REGS); + + if (mEntryRegA) + mLocalProvidedRegs += CPU_REG_A; + if (mEntryRegX) + mLocalProvidedRegs += CPU_REG_X; + if (mEntryRegY) + mLocalProvidedRegs += CPU_REG_Y; + + for (i = 0; i < mIns.Size(); i++) + { + mIns[i].FilterRegUsage(mLocalRequiredRegs, mLocalProvidedRegs); + } + + switch (mBranch) + { + case ASMIT_BCC: + case ASMIT_BCS: + if (!mLocalProvidedRegs[CPU_REG_C]) + mLocalRequiredRegs += CPU_REG_C; + break; + case ASMIT_BEQ: + case ASMIT_BNE: + case ASMIT_BMI: + case ASMIT_BPL: + if (!mLocalProvidedRegs[CPU_REG_Z]) + mLocalRequiredRegs += CPU_REG_Z; + break; + } + + if (mExitRegA) + { + if (!mLocalProvidedRegs[CPU_REG_A]) + mLocalRequiredRegs += CPU_REG_A; + } + if (mExitRegX) + { + if (!mLocalProvidedRegs[CPU_REG_X]) + mLocalRequiredRegs += CPU_REG_X; + } + + mEntryRequiredRegs = mLocalRequiredRegs; + mExitProvidedRegs = mLocalProvidedRegs; + + if (mTrueJump) mTrueJump->BuildLocalRegSets(); + if (mFalseJump) mFalseJump->BuildLocalRegSets(); + } + +} + +void NativeCodeBasicBlock::BuildGlobalProvidedRegSet(NumberSet fromProvidedRegs) +{ + if (!mVisited || !(fromProvidedRegs <= mEntryProvidedRegs)) + { + mEntryProvidedRegs |= fromProvidedRegs; + fromProvidedRegs |= mExitProvidedRegs; + + mVisited = true; + + if (mTrueJump) mTrueJump->BuildGlobalProvidedRegSet(fromProvidedRegs); + if (mFalseJump) mFalseJump->BuildGlobalProvidedRegSet(fromProvidedRegs); + } + +} + +bool NativeCodeBasicBlock::BuildGlobalRequiredRegSet(NumberSet& fromRequiredRegs) +{ + bool revisit = false; + + if (!mVisited) + { + mVisited = true; + + mNewRequiredRegs = mExitRequiredRegs; + + if (mTrueJump && mTrueJump->BuildGlobalRequiredRegSet(mNewRequiredRegs)) revisit = true; + if (mFalseJump && mFalseJump->BuildGlobalRequiredRegSet(mNewRequiredRegs)) revisit = true; + + if (!(mNewRequiredRegs <= mExitRequiredRegs)) + { + revisit = true; + + mExitRequiredRegs = mNewRequiredRegs; + mNewRequiredRegs -= mLocalProvidedRegs; + mEntryRequiredRegs |= mNewRequiredRegs; + } + + } + + fromRequiredRegs |= mEntryRequiredRegs; + + return revisit; + +} + +bool NativeCodeBasicBlock::RemoveUnusedResultInstructions(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + assert(mIndex == 1000 || mNumEntries == mEntryBlocks.Size()); + + NumberSet requiredRegs(mExitRequiredRegs); + int i; + + switch (mBranch) + { + case ASMIT_BCC: + case ASMIT_BCS: + requiredRegs += CPU_REG_C; + break; + case ASMIT_BEQ: + case ASMIT_BNE: + case ASMIT_BMI: + case ASMIT_BPL: + requiredRegs += CPU_REG_Z; + break; + } + + for (i = mIns.Size() - 1; i >= 0; i--) + { + if (mIns[i].mType != ASMIT_NOP && !mIns[i].IsUsedResultInstructions(requiredRegs)) + { + if (i > 0 && mIns[i - 1].mMode == ASMIM_RELATIVE && mIns[i - 1].mAddress > 0) + { + mIns[i - 1].mType = ASMIT_NOP; + mIns[i - 1].mMode = ASMIM_IMPLIED; + } + mIns[i].mType = ASMIT_NOP; + mIns[i].mMode = ASMIM_IMPLIED; + changed = true; + } + } + + assert(mIndex == 1000 || mNumEntries == mEntryBlocks.Size()); + + if (mTrueJump) + { + if (mTrueJump->RemoveUnusedResultInstructions()) + changed = true; + } + if (mFalseJump) + { + if (mFalseJump->RemoveUnusedResultInstructions()) + changed = true; + } + } + + return changed; +} + +bool NativeCodeBasicBlock::RemoveUnusedBitOps(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + bool usesC = false; + int usesZ = 0x00; + int usesA = mExitRequiredRegs[CPU_REG_A] ? 0xff : 0x0; + int usesX = mExitRequiredRegs[CPU_REG_X] ? 0xff : 0x0; + int usesY = mExitRequiredRegs[CPU_REG_Y] ? 0xff : 0x0; + + if (mExitRequiredRegs[CPU_REG_C]) + usesC = true; + if (mExitRequiredRegs[CPU_REG_Z]) + usesZ = true; + + switch (mBranch) + { + case ASMIT_BCC: + case ASMIT_BCS: + usesC = true; + break; + case ASMIT_BEQ: + case ASMIT_BNE: + usesZ = 0xff; + break; + case ASMIT_BMI: + case ASMIT_BPL: + usesZ = 0x80; + break; + } + + for (int i = mIns.Size() - 1; i >= 0; i--) + { + NativeCodeInstruction& ins(mIns[i]); + + switch (ins.mType) + { + case ASMIT_TAX: + usesA |= usesX | usesZ; + usesX = 0x00; + usesZ = 0x00; + break; + case ASMIT_TXA: + usesX |= usesA | usesZ; + usesA = 0x00; + usesZ = 0x00; + break; + case ASMIT_TAY: + usesA |= usesY | usesZ; + usesY = 0x00; + usesZ = 0x00; + break; + case ASMIT_TYA: + usesY |= usesA | usesZ; + usesA = 0x00; + usesZ = 0x00; + break; + case ASMIT_LDA: + usesA = 0x00; + usesZ = 0x00; + break; + case ASMIT_LDX: + usesX = 0x00; + usesZ = 0x00; + break; + case ASMIT_LDY: + usesY = 0x00; + usesZ = 0x00; + break; + case ASMIT_JSR: + usesA = usesX = usesY = 0xff; + usesZ = 0x00; + usesC = false; + break; + case ASMIT_AND: + if (ins.mMode == ASMIM_IMMEDIATE) + { + if (!usesZ && ins.mAddress == usesA) + { + ins.mType = ASMIT_NOP; + ins.mMode = ASMIM_IMPLIED; + } + else + usesA = ins.mAddress; + } + else + usesA = 0xff; + usesZ = 0x00; + break; + case ASMIT_ORA: + if (ins.mMode == ASMIM_IMMEDIATE) + usesA = ins.mAddress ^ 255; + else + usesA = 0xff; + break; + usesZ = 0x00; + case ASMIT_ASL: + if (ins.mMode == ASMIM_IMPLIED) + { + if (usesZ) usesA = 0xff; + usesA = (usesA >> 1); + if (usesC) + usesA |= 0x80; + } + usesC = false; + usesZ = 0x00; + break; + case ASMIT_LSR: + if (ins.mMode == ASMIM_IMPLIED) + { + if (usesZ) usesA = 0xff; + usesA = (usesA << 1) & 0xff; + if (usesC) + usesA |= 0x01; + } + usesC = false; + usesZ = 0x00; + break; + case ASMIT_ROL: + if (ins.mMode == ASMIM_IMPLIED) + { + if (usesZ) usesA = 0xff; + if (usesA || usesC) + { + bool nc = usesA & 0x01; + usesA = (usesA >> 1); + if (usesC) + usesA |= 0x80; + usesC = nc; + } + else + { + ins.mType = ASMIT_NOP; + changed = true; + } + } + else + usesC = true; + usesZ = 0x00; + break; + case ASMIT_ROR: + { + if (ins.mMode == ASMIM_IMPLIED) + { + if (usesZ) usesA = 0xff; + if (usesA || usesC) + { + bool nc = usesA & 0x80; + usesA = (usesA << 1) & 0xff; + if (usesC) + usesA |= 0x01; + usesC = nc; + } + else + { + ins.mType = ASMIT_NOP; + changed = true; + } + } + else + usesC = true; + usesZ = 0x00; + } break; + case ASMIT_ADC: + if (ins.mMode == ASMIM_IMMEDIATE && ins.mAddress == 0) + { + if (usesC || usesZ) + usesA = 0xff; + else + usesA = BinMask(usesA); + } + else + usesA = 0xff; + usesC = true; + usesZ = 0x00; + break; + case ASMIT_CLC: + case ASMIT_SEC: + usesC = false; + break; + default: + if (ins.ReferencesAccu()) + usesA = 0xff; + if (ins.ReferencesXReg()) + usesX = 0xff; + if (ins.ReferencesYReg()) + usesY = 0xff; + if (ins.ReferencesCarry()) + usesC = true; + if (ins.ChangesZFlag()) + usesZ = 0x00; + break; + } + + switch (ins.mMode) + { + case ASMIM_ABSOLUTE_X: + case ASMIM_ZERO_PAGE_X: + case ASMIM_INDIRECT_X: + usesX = 0xff; + break; + case ASMIM_ABSOLUTE_Y: + case ASMIM_ZERO_PAGE_Y: + case ASMIM_INDIRECT_Y: + usesY = 0xff; + break; + } + } + + if (mTrueJump && mTrueJump->RemoveUnusedBitOps()) + changed = true; + if (mFalseJump && mFalseJump->RemoveUnusedBitOps()) + changed = true; + } + + return changed; +} + + +void NativeCodeBasicBlock::BuildCollisionTable(NumberSet* collisionSets) +{ + if (!mVisited) + { + mVisited = true; + + NumberSet requiredTemps(mExitRequiredRegs); + int i, j; + + for (i = 0; i < mExitRequiredRegs.Size(); i++) + { + if (mExitRequiredRegs[i]) + { + for (j = 0; j < mExitRequiredRegs.Size(); j++) + { + if (mExitRequiredRegs[j]) + { + collisionSets[i] += j; + } + } + } + } + + for (i = mIns.Size() - 1; i >= 0; i--) + { + mIns[i].BuildCollisionTable(requiredTemps, collisionSets); + } + + if (mTrueJump) mTrueJump->BuildCollisionTable(collisionSets); + if (mFalseJump) mFalseJump->BuildCollisionTable(collisionSets); + } +} + +void NativeCodeBasicBlock::BuildDominatorTree(NativeCodeBasicBlock* from, DominatorStacks& stacks) +{ + if (from == this) + return; + else if (!mDominator) + mDominator = from; + else if (from == mDominator) + return; + else + { + stacks.d1.SetSize(0); + stacks.d2.SetSize(0); + + NativeCodeBasicBlock* b = mDominator; + while (b) + { + stacks.d1.Push(b); + b = b->mDominator; + } + b = from; + while (b) + { + stacks.d2.Push(b); + b = b->mDominator; + } + + b = nullptr; + while (stacks.d1.Size() > 0 && stacks.d2.Size() > 0 && stacks.d1.Last() == stacks.d2.Last()) + { + b = stacks.d1.Pop(); stacks.d2.Pop(); + } + + if (mDominator == b) + return; + + mDominator = b; + } + + if (mTrueJump) + mTrueJump->BuildDominatorTree(this, stacks); + if (mFalseJump) + mFalseJump->BuildDominatorTree(this, stacks); +} + + +void NativeCodeBasicBlock::CountEntries(NativeCodeBasicBlock * fromJump) +{ + if (mVisiting) + mLoopHead = true; + + if (mNumEntries == 0) + mFromJump = fromJump; + else + mFromJump = nullptr; + mNumEntries++; + + if (!mVisited) + { + mVisited = true; + mVisiting = true; + + if (mTrueJump) + mTrueJump->CountEntries(this); + if (mFalseJump) + mFalseJump->CountEntries(this); + + mVisiting = false; + } +} + +bool NativeCodeBasicBlock::IsSame(const NativeCodeBasicBlock* block) const +{ + if (block->mIns.Size() != mIns.Size()) + return false; + + if (block->mBranch != mBranch) + return false; + + if (mTrueJump) + { + if (!block->mTrueJump) + return false; + + if (mTrueJump == this) + { + if (block->mTrueJump != block) + return false; + } + else if (mTrueJump != block->mTrueJump) + return false; + } + else if (block->mTrueJump) + return false; + + if (mFalseJump) + { + if (!block->mFalseJump) + return false; + + if (mFalseJump == this) + { + if (block->mFalseJump != block) + return false; + } + else if (mFalseJump != block->mFalseJump) + return false; + } + else if (block->mFalseJump) + return false; + + for (int i = 0; i < mIns.Size(); i++) + if (!mIns[i].IsSame(block->mIns[i])) + return false; + + return true; +} + +bool NativeCodeBasicBlock::FindSameBlocks(NativeCodeProcedure* nproc) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + if (!mSameBlock && this != mProc->mEntryBlock) + { + for (int i = 0; i < nproc->mBlocks.Size(); i++) + { + if (nproc->mBlocks[i] != this && nproc->mBlocks[i]->IsSame(this)) + { + nproc->mBlocks[i]->mSameBlock = this; + changed = true; + } + } + } + + if (mTrueJump && mTrueJump->FindSameBlocks(nproc)) + changed = true; + if (mFalseJump && mFalseJump->FindSameBlocks(nproc)) + changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::MergeSameBlocks(NativeCodeProcedure* nproc) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + if (this == mProc->mEntryBlock && mSameBlock && mIns.Size() > 1) + { + mTrueJump = mSameBlock; + mFalseJump = nullptr; + mBranch = ASMIT_JMP; + mIns.SetSize(0); + changed = true; + mSameBlock = nullptr; + } + + if (mTrueJump && mTrueJump->mSameBlock) + { + mTrueJump = mTrueJump->mSameBlock; + changed = true; + } + + if (mFalseJump && mFalseJump->mSameBlock) + { + mFalseJump = mFalseJump->mSameBlock; + changed = true; + } + + if (mTrueJump && mTrueJump->MergeSameBlocks(nproc)) + changed = true; + if (mFalseJump && mFalseJump->MergeSameBlocks(nproc)) + changed = true; + } + + return changed; +} + +NativeCodeBasicBlock* NativeCodeBasicBlock::ForwardAccuBranch(bool eq, bool ne, bool pl, bool mi, int limit) +{ + if (limit == 4) + return this; + limit++; + + if (mIns.Size() == 0 && mNumEntries == 1 || mIns.Size() == 1 && mIns[0].mType == ASMIT_ORA && mIns[0].mMode == ASMIM_IMMEDIATE && mIns[0].mAddress == 0) + { + if (mBranch == ASMIT_BEQ) + { + if (eq) + return mTrueJump->ForwardAccuBranch(true, false, true, false, limit); + if (ne || mi) + return mFalseJump->ForwardAccuBranch(false, true, pl, mi, limit); + } + else if (mBranch == ASMIT_BNE) + { + if (eq) + return mFalseJump->ForwardAccuBranch(true, false, true, false, limit); + if (ne || mi) + return mTrueJump->ForwardAccuBranch(false, true, pl, mi, limit); + } + else if (mBranch == ASMIT_BPL) + { + if (eq || pl) + return mTrueJump->ForwardAccuBranch(eq, ne, true, false, limit); + if (mi) + return mFalseJump->ForwardAccuBranch(false, true, false, true, limit); + } + else if (mBranch == ASMIT_BMI) + { + if (eq || pl) + return mFalseJump->ForwardAccuBranch(eq, ne, true, false, limit); + if (mi) + return mTrueJump->ForwardAccuBranch(false, true, false, true, limit); + } + } + + return this; +} + + +NativeCodeBasicBlock* NativeCodeBasicBlock::UniqueEntryBlock(NativeCodeBasicBlock* prefix) +{ + if (mNumEntries > 1) + return PrefixLoopBlock(prefix); + else + return this; +} + +NativeCodeBasicBlock* NativeCodeBasicBlock::PrefixLoopBlock(NativeCodeBasicBlock* prefix) +{ + NativeCodeBasicBlock* block = mProc->AllocateBlock(); + + block->mTrueJump = this; + block->mBranch = ASMIT_JMP; + if (this == prefix->mTrueJump) + prefix->mTrueJump = block; + else + prefix->mFalseJump = block; + block->mEntryBlocks.Push(prefix); + block->mNumEntries = 1; + mEntryBlocks.RemoveAll(prefix); + mEntryBlocks.Push(block); + + block->mEntryRequiredRegs = block->mExitRequiredRegs = mEntryRequiredRegs; + + return block; +} + +NativeCodeBasicBlock* NativeCodeBasicBlock::CloneBlock(void) +{ + NativeCodeBasicBlock* block = mProc->AllocateBlock(); + + block->mTrueJump = mTrueJump; + block->mFalseJump = mFalseJump; + block->mBranch = mBranch; + block->mBranchIns = mBranchIns; + + if (mTrueJump) + { + mTrueJump->mEntryBlocks.RemoveAll(this); + mTrueJump->mEntryBlocks.Push(block); + } + if (mFalseJump) + { + mFalseJump->mEntryBlocks.RemoveAll(this); + mFalseJump->mEntryBlocks.Push(block); + } + + block->mNumEntries = 1; + block->mEntryBlocks.Push(this); + + for (int i = 0; i < mIns.Size(); i++) + block->mIns.Push(mIns[i]); + + block->mEntryRequiredRegs = mEntryRequiredRegs; + block->mExitRequiredRegs = mExitRequiredRegs; + block->mEntryProvidedRegs = mEntryProvidedRegs; + block->mExitProvidedRegs = mExitProvidedRegs; + + return block; +} + +NativeCodeBasicBlock* NativeCodeBasicBlock::SplitBlock(int at) +{ + NativeCodeBasicBlock* block = mProc->AllocateBlock(); + block->mTrueJump = mTrueJump; + block->mFalseJump = mFalseJump; + block->mBranch = mBranch; + block->mBranchIns = mBranchIns; + + if (mTrueJump) + { + mTrueJump->mEntryBlocks.RemoveAll(this); + mTrueJump->mEntryBlocks.Push(block); + } + if (mFalseJump) + { + mFalseJump->mEntryBlocks.RemoveAll(this); + mFalseJump->mEntryBlocks.Push(block); + } + + block->mNumEntries = 1; + block->mEntryBlocks.Push(this); + mTrueJump = block; + mFalseJump = nullptr; + mBranch = ASMIT_JMP; + + for (int i = at; i < mIns.Size(); i++) + block->mIns.Push(mIns[i]); + mIns.SetSize(at); + + return block; +} + +bool NativeCodeBasicBlock::RemoveJumpToBranch(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + if (mTrueJump && mTrueJump->RemoveJumpToBranch()) changed = true; + if (mFalseJump && mFalseJump->RemoveJumpToBranch()) changed = true; + + if (mTrueJump && !mFalseJump && mTrueJump != this && mTrueJump->mIns.Size() == 0 && (mTrueJump->mFalseJump || mTrueJump->mTrueJump != mTrueJump)) + { + mTrueJump->mEntryBlocks.RemoveAll(this); + mTrueJump->mNumEntries--; + mBranch = mTrueJump->mBranch; + mFalseJump = mTrueJump->mFalseJump; + mTrueJump = mTrueJump->mTrueJump; + if (mTrueJump) + { + mTrueJump->mNumEntries++; + mTrueJump->mEntryBlocks.Push(this); + } + if (mFalseJump) + { + mFalseJump->mNumEntries++; + mFalseJump->mEntryBlocks.Push(this); + } + changed = true; + } + } + + return changed; +} + +bool NativeCodeBasicBlock::MergeBasicBlocks(void) +{ + bool changed = false; + + if (!mVisited) + { + assert(mIns.Size() == 0 || mIns[0].mType != ASMIT_INV); + + mVisited = true; + + if (!mLocked) + { + if (mTrueJump && mFalseJump && mTrueJump != this && mFalseJump != this && (mBranch == ASMIT_BEQ || mBranch == ASMIT_BNE || mBranch == ASMIT_BPL || mBranch == ASMIT_BMI)) + { + if (mIns.Size() > 0 && mIns[mIns.Size() - 1].mType == ASMIT_LDA) + { + if (mTrueJump->mIns.Size() == 1 && mTrueJump->mIns[0].IsSame(mIns[mIns.Size() - 1])) + { + if (mTrueJump->mBranch == mBranch) + { + mTrueJump = mTrueJump->mTrueJump; + changed = true; + } + else if (mTrueJump->mBranch == InvertBranchCondition(mBranch)) + { + mTrueJump = mTrueJump->mFalseJump; + changed = true; + } + } + if (mFalseJump->mIns.Size() == 1 && mFalseJump->mIns[0].IsSame(mIns[mIns.Size() - 1])) + { + if (mFalseJump->mBranch == mBranch) + { + mFalseJump = mFalseJump->mFalseJump; + changed = true; + } + else if (mFalseJump->mBranch == InvertBranchCondition(mBranch)) + { + mFalseJump = mFalseJump->mTrueJump; + changed = true; + } + } + } + } + + if (mFalseJump) + { + if (mTrueJump->mIns.Size() == 0 && mTrueJump->mFalseJump) + { + if (mTrueJump->mBranch == mBranch) + { + mTrueJump = mTrueJump->mTrueJump; + changed = true; + } + else if (mTrueJump->mBranch == InvertBranchCondition(mBranch)) + { + mTrueJump = mTrueJump->mFalseJump; + changed = true; + } + } + if (mFalseJump->mIns.Size() == 0 && mFalseJump->mFalseJump) + { + if (mFalseJump->mBranch == mBranch) + { + mFalseJump = mFalseJump->mFalseJump; + changed = true; + } + else if (mFalseJump->mBranch == InvertBranchCondition(mBranch)) + { + mFalseJump = mFalseJump->mTrueJump; + changed = true; + } + } + } + + while (mTrueJump && !mFalseJump && mTrueJump->mNumEntries == 1 && mTrueJump != this && !mTrueJump->mLocked) + { + for (int i = 0; i < mTrueJump->mIns.Size(); i++) + mIns.Push(mTrueJump->mIns[i]); + mBranch = mTrueJump->mBranch; + mFalseJump = mTrueJump->mFalseJump; + mTrueJump = mTrueJump->mTrueJump; + changed = true; + } + + int steps = 100; + while (mTrueJump && mTrueJump->mIns.Size() == 0 && !mTrueJump->mFalseJump && !mTrueJump->mLocked && mTrueJump != this && mTrueJump->mTrueJump != mTrueJump && steps > 0) + { + mTrueJump->mNumEntries--; + mTrueJump = mTrueJump->mTrueJump; + mTrueJump->mNumEntries++; + changed = true; + steps--; + } + + steps = 100; + while (mFalseJump && mFalseJump->mTrueJump && mFalseJump->mIns.Size() == 0 && !mFalseJump->mFalseJump && !mFalseJump->mLocked && mFalseJump != this && mFalseJump->mTrueJump != mFalseJump && steps > 0) + { + mFalseJump->mNumEntries--; + mFalseJump = mFalseJump->mTrueJump; + mFalseJump->mNumEntries++; + changed = true; + steps--; + } + + if (mTrueJump && mTrueJump == mFalseJump) + { + mBranch = ASMIT_JMP; + mFalseJump = nullptr; + changed = true; + } +#if 1 + if (mIns.Size() > 0 && mIns.Last().ChangesAccuAndFlag() && mTrueJump && mFalseJump) + { + NativeCodeBasicBlock* ntb = mTrueJump, * nfb = mFalseJump; + + if (mBranch == ASMIT_BEQ) + { + ntb = ntb->ForwardAccuBranch(true, false, true, false, 0); + nfb = nfb->ForwardAccuBranch(false, true, false, false, 0); + } + else if (mBranch == ASMIT_BNE) + { + nfb = nfb->ForwardAccuBranch(true, false, true, false, 0); + ntb = ntb->ForwardAccuBranch(false, true, false, false, 0); + } + else if (mBranch == ASMIT_BPL) + { + ntb = ntb->ForwardAccuBranch(false, false, true, false, 0); + nfb = nfb->ForwardAccuBranch(false, true, false, true, 0); + } + else if (mBranch == ASMIT_BMI) + { + nfb = nfb->ForwardAccuBranch(false, false, true, false, 0); + ntb = ntb->ForwardAccuBranch(false, true, false, true, 0); + } + + if (ntb != mTrueJump) + { + mTrueJump->mNumEntries--; + mTrueJump = ntb; + mTrueJump->mNumEntries++; + changed = true; + } + + if (nfb != mFalseJump) + { + mFalseJump->mNumEntries--; + mFalseJump = nfb; + mFalseJump->mNumEntries++; + changed = true; + } + } +#endif + } + + if (mTrueJump && mTrueJump->MergeBasicBlocks()) + changed = true; + if (mFalseJump && mFalseJump->MergeBasicBlocks()) + changed = true; + + assert(mIns.Size() == 0 || mIns[0].mType != ASMIT_INV); + } + return changed; +} + +bool NativeCodeBasicBlock::ShortcutBranchSequence(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + if (mFalseJump && mIns.Size() && mIns.Last().ChangesFlagToAccu()) + { + NativeCodeBasicBlock* eblock = nullptr, * nblock = nullptr; + + if (mBranch == ASMIT_BEQ) + { + eblock = mTrueJump; + nblock = mFalseJump; + } + else if (mBranch == ASMIT_BNE) + { + eblock = mFalseJump; + nblock = mTrueJump; + } + + if (eblock && nblock) + { + int reg = -1; + if (mIns.Last().mType == ASMIT_LDA && mIns.Last().mMode == ASMIM_ZERO_PAGE) + reg = mIns.Last().mAddress; + + if (nblock && nblock->mIns.Size() == 1 && nblock->mIns[0].mType == ASMIT_ORA && nblock->mIns[0].mMode == ASMIM_IMMEDIATE && nblock->mIns[0].mAddress == 0) + { + NativeCodeBasicBlock* block = mProc->AllocateBlock(); + block->mBranch = nblock->mBranch; + block->mTrueJump = nblock->mTrueJump; + block->mFalseJump = nblock->mFalseJump; + + nblock->mEntryBlocks.RemoveAll(this); + nblock->mNumEntries--; + + block->mTrueJump->mEntryBlocks.Push(this); + block->mTrueJump->mNumEntries++; + block->mFalseJump->mEntryBlocks.Push(this); + block->mFalseJump->mNumEntries++; + + block->mNumEntries++; + block->mEntryBlocks.Push(this); + if (mTrueJump == nblock) + mTrueJump = block; + else + mFalseJump = block; + changed = true; + } + else if (nblock && nblock->mIns.Size() == 0 && nblock->mNumEntries == 1) + { + if (nblock->mBranch == ASMIT_BPL || nblock->mBranch == ASMIT_BMI) + { + NativeCodeBasicBlock* tblock = nblock->mTrueJump; + if (tblock->mFalseJump) + { + if (tblock->mIns.Size() == 0 || + tblock->mIns.Size() == 1 && tblock->mIns[0].mType == ASMIT_LDA && tblock->mIns[0].mMode == ASMIM_ZERO_PAGE && tblock->mIns[0].mAddress == reg) + { + if (tblock->mBranch == ASMIT_BEQ) + { + tblock->mNumEntries--; + tblock->mEntryBlocks.RemoveAll(nblock); + nblock->mTrueJump = tblock->mFalseJump; + nblock->mTrueJump->mNumEntries++; + nblock->mTrueJump->mEntryBlocks.Push(nblock); + changed = true; + } + else if (tblock->mBranch == ASMIT_BNE) + { + tblock->mNumEntries--; + tblock->mEntryBlocks.RemoveAll(nblock); + nblock->mTrueJump = tblock->mTrueJump; + nblock->mTrueJump->mNumEntries++; + nblock->mTrueJump->mEntryBlocks.Push(nblock); + changed = true; + } + } + } + + NativeCodeBasicBlock* fblock = nblock->mFalseJump; + if (fblock->mFalseJump) + { + if (fblock->mIns.Size() == 0 || + fblock->mIns.Size() == 1 && fblock->mIns[0].mType == ASMIT_LDA && fblock->mIns[0].mMode == ASMIM_ZERO_PAGE && fblock->mIns[0].mAddress == reg) + { + if (fblock->mBranch == ASMIT_BEQ) + { + fblock->mNumEntries--; + fblock->mEntryBlocks.RemoveAll(nblock); + nblock->mFalseJump = fblock->mFalseJump; + nblock->mFalseJump->mNumEntries++; + nblock->mFalseJump->mEntryBlocks.Push(nblock); + changed = true; + } + else if (fblock->mBranch == ASMIT_BNE) + { + fblock->mNumEntries--; + fblock->mEntryBlocks.RemoveAll(nblock); + nblock->mFalseJump = fblock->mTrueJump; + nblock->mFalseJump->mNumEntries++; + nblock->mFalseJump->mEntryBlocks.Push(nblock); + changed = true; + } + } + } + } + } + + if (!changed && mIns.Last().mType == ASMIT_LDA && mIns.Last().mMode == ASMIM_ZERO_PAGE) + { + int reg = mIns.Last().mAddress; + + if (nblock && nblock->mIns.Size() == 0 && nblock->mNumEntries == 1) + { + if (nblock->mBranch == ASMIT_BPL || nblock->mBranch == ASMIT_BMI) + { + + } + } + + } + } + } + + if (mTrueJump && mTrueJump->ShortcutBranchSequence()) + changed = true; + if (mFalseJump && mFalseJump->ShortcutBranchSequence()) + changed = true; + } + + return changed; +} + +void NativeCodeBasicBlock::CollectEntryBlocks(NativeCodeBasicBlock* block) +{ + if (block) + mEntryBlocks.Push(block); + + if (!mVisited) + { + mVisited = true; + + if (mTrueJump) + mTrueJump->CollectEntryBlocks(this); + if (mFalseJump) + mFalseJump->CollectEntryBlocks(this); + } +} + +void NativeCodeBasicBlock::BuildEntryDataSet(const NativeRegisterDataSet& set) +{ + if (!mVisited) + mEntryRegisterDataSet = set; + else + { + bool changed = false; + for (int i = 0; i < NUM_REGS; i++) + { + // TODO CHECK for uniqueID + if (set[i].mMode == NRDM_IMMEDIATE) + { + if (mEntryRegisterDataSet[i].mMode == NRDM_IMMEDIATE && set[i].mValue == mEntryRegisterDataSet[i].mValue) + { + } + else if (mEntryRegisterDataSet[i].mMode != NRDM_UNKNOWN) + { + mEntryRegisterDataSet.Reset(i); + mVisited = false; + } + } + else if (set[i].mMode == NRDM_IMMEDIATE_ADDRESS) + { + if (mEntryRegisterDataSet[i].mMode == NRDM_IMMEDIATE_ADDRESS && + set[i].mValue == mEntryRegisterDataSet[i].mValue && + set[i].mLinkerObject == mEntryRegisterDataSet[i].mLinkerObject && + set[i].mFlags == mEntryRegisterDataSet[i].mFlags) + { + } + else if (mEntryRegisterDataSet[i].mMode != NRDM_UNKNOWN) + { + mEntryRegisterDataSet.Reset(i); + mVisited = false; + } + } + else if (mEntryRegisterDataSet[i].mMode != NRDM_UNKNOWN) + { + mEntryRegisterDataSet.Reset(i); + mVisited = false; + } + } + } + + if (!mVisited) + { + mVisited = true; + + mNDataSet = mEntryRegisterDataSet; + + for (int i = 0; i < mIns.Size(); i++) + mIns[i].Simulate(mNDataSet); + + mFDataSet = mNDataSet; + if (mBranch == ASMIT_BCC) + { + mNDataSet.Set(CPU_REG_C, NRDM_IMMEDIATE, 0); + mFDataSet.Set(CPU_REG_C, NRDM_IMMEDIATE, 1); + } + else if (mBranch == ASMIT_BCS) + { + mNDataSet.Set(CPU_REG_C, NRDM_IMMEDIATE, 1); + mFDataSet.Set(CPU_REG_C, NRDM_IMMEDIATE, 0); + } + + if (mTrueJump) + mTrueJump->BuildEntryDataSet(mNDataSet); + if (mFalseJump) + mFalseJump->BuildEntryDataSet(mFDataSet); + } +} + +bool NativeCodeBasicBlock::ApplyEntryDataSet(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + mNDataSet = mEntryRegisterDataSet; + + for (int i = 0; i < mIns.Size(); i++) + { + if (mIns[i].ApplySimulation(mNDataSet)) + changed = true; + mIns[i].Simulate(mNDataSet); + } + + if (mTrueJump && mTrueJump->ApplyEntryDataSet()) + changed = true; + if (mFalseJump && mFalseJump->ApplyEntryDataSet()) + changed = true; + } + + return changed; +} + +void NativeCodeBasicBlock::FindZeroPageAlias(const NumberSet& statics, NumberSet& invalid, uint8* alias, int accu) +{ + if (!mVisited) + { + mVisited = true; + + if (mNumEntries > 1) + accu = -1; + + for (int i = 0; i < mIns.Size(); i++) + { + if (mIns[i].mMode == ASMIM_ZERO_PAGE) + { + if (mIns[i].mType == ASMIT_LDA) + accu = mIns[i].mAddress; + else if (mIns[i].mType == ASMIT_STA) + { + if (accu < 0 || !statics[accu]) + invalid += mIns[i].mAddress; + else if (alias[mIns[i].mAddress]) + { + if (alias[mIns[i].mAddress] != accu) + invalid += mIns[i].mAddress; + } + else + { + alias[mIns[i].mAddress] = accu; + } + } + else if (mIns[i].ChangesAccu()) + accu = -1; + else if (mIns[i].ChangesAddress()) + invalid += mIns[i].mAddress; + + if (mIns[i].mFlags & NCIF_ALIASING) + invalid += mIns[i].mAddress; + } + else if (mIns[i].mType == ASMIT_JSR && (mIns[i].mFlags & NCIF_USE_ZP_32_X)) + { + int j = mIns[i].mParam; + invalid += j; + invalid += j + 1; + invalid += j + 2; + invalid += j + 3; + accu = -1; + } + else if (mIns[i].ChangesAccu()) + accu = -1; + } + + if (mTrueJump) + mTrueJump->FindZeroPageAlias(statics, invalid, alias, accu); + if (mFalseJump) + mFalseJump->FindZeroPageAlias(statics, invalid, alias, accu); + } +} + +void NativeCodeBasicBlock::CheckGlobalAliasing(bool& globalStores, bool& globalLoads) +{ + if (!mVisited) + { + mVisited = true; + + for (int i = 0; i < mIns.Size(); i++) + { + const NativeCodeInstruction& ins(mIns[i]); + if (ins.mType == ASMIT_JSR) + { + if (ins.mLinkerObject) + { + if (!(ins.mLinkerObject->mFlags & LOBJF_NO_GLOBAL_STORES)) + globalStores = true; + if (!(ins.mLinkerObject->mFlags & LOBJF_NO_GLOBAL_LOADS)) + globalLoads = true; + } + else + { + globalLoads = true; + globalStores = true; + } + } + else if (ins.mMode == ASMIM_ABSOLUTE || ins.mMode == ASMIM_ABSOLUTE_X || ins.mMode == ASMIM_ABSOLUTE_Y || ins.mMode == ASMIM_INDIRECT) + { + if (ins.mType == ASMIT_INC || ins.mType == ASMIT_DEC || ins.mType == ASMIT_ASL || ins.mType == ASMIT_LSR || ins.mType == ASMIT_ROL || ins.mType == ASMIT_ROR) + { + globalLoads = true; + globalStores = true; + } + else if (ins.mType == ASMIT_STA || ins.mType == ASMIT_STX || ins.mType == ASMIT_STY) + globalStores = true; + else + globalLoads = true; + } + } + + if (mTrueJump) mTrueJump->CheckGlobalAliasing(globalStores, globalLoads); + if (mFalseJump) mFalseJump->CheckGlobalAliasing(globalStores, globalLoads); + } +} + +bool NativeCodeBasicBlock::CollectZeroPageSet(ZeroPageSet& locals, ZeroPageSet& global, bool ignorefcall) +{ + if (!mVisited) + { + mVisited = true; + + for (int i = 0; i < mIns.Size(); i++) + { + switch (mIns[i].mMode) + { + case ASMIM_ZERO_PAGE: + if (mIns[i].ChangesAddress()) + locals += mIns[i].mAddress; + break; + case ASMIM_ABSOLUTE: + if (mIns[i].mType == ASMIT_JSR) + { + if (mIns[i].mFlags & NCIF_RUNTIME) + { + if (mIns[i].mFlags & NCIF_FEXEC) + { + if (!ignorefcall) + return false; + } + else + { + for (int j = 0; j < 4; j++) + { + locals += BC_REG_ACCU + j; + locals += BC_REG_WORK + j; + } + } + } + + if (mIns[i].mLinkerObject) + { + LinkerObject* lo = mIns[i].mLinkerObject; + + if (lo->mFlags & LOBJF_ZEROPAGESET) + { + global |= lo->mZeroPageSet; + } + else if (!lo->mProc) + { + for (int i = 0; i < lo->mNumTemporaries; i++) + { + for (int j = 0; j < lo->mTempSizes[i]; j++) + global += lo->mTemporaries[i] + j; + } + } + else + return false; + } + } + break; + case ASMIM_ZERO_PAGE_X: + if (mIns[i].ChangesAddress()) + { + if (i > 1 && i + 2 < mIns.Size()) + { + if (mIns[i - 2].mType == ASMIT_LDX && mIns[i - 2].mMode == ASMIM_IMMEDIATE && + mIns[i - 1].mType == ASMIT_LDA && + mIns[i + 0].mType == ASMIT_STA && + mIns[i + 1].mType == ASMIT_DEX && + mIns[i + 2].mType == ASMIT_BPL) + { + for (int j = 0; j < mIns[i - 2].mAddress; j++) + locals += mIns[i].mAddress + j; + } + } + } + break; + } + } + + if (mTrueJump && !mTrueJump->CollectZeroPageSet(locals, global, ignorefcall)) + return false; + if (mFalseJump && !mFalseJump->CollectZeroPageSet(locals, global, ignorefcall)) + return false; + } + + return true; +} + +void NativeCodeBasicBlock::CollectZeroPageUsage(NumberSet& used, NumberSet &modified, NumberSet& pairs) +{ + if (!mVisited) + { + mVisited = true; + + for (int i = 0; i < mIns.Size(); i++) + { + switch (mIns[i].mMode) + { + case ASMIM_ZERO_PAGE: + used += mIns[i].mAddress; + if (mIns[i].ChangesAddress() || (mIns[i].mFlags & NCIF_ALIASING)) + modified += mIns[i].mAddress; + break; + case ASMIM_INDIRECT_Y: + used += mIns[i].mAddress + 0; + used += mIns[i].mAddress + 1; + pairs += mIns[i].mAddress; + break; + case ASMIM_ABSOLUTE: + if (mIns[i].mType == ASMIT_JSR) + { + if (mIns[i].mFlags & NCIF_RUNTIME) + { + for (int j = 0; j < 4; j++) + { + used += BC_REG_ACCU + j; + used += BC_REG_WORK + j; + modified += BC_REG_ACCU + j; + modified += BC_REG_WORK + j; + } + + if (mIns[i].mFlags & NCIF_USE_ZP_32_X) + { + for (int j = 0; j < 4; j++) + used += mIns[i].mParam + j; + for (int j = 0; j < 3; j++) + pairs += mIns[i].mParam + j; + } + } + + if (mIns[i].mLinkerObject) + { + LinkerObject* lo = mIns[i].mLinkerObject; + + for (int i = 0; i < lo->mNumTemporaries; i++) + { + for (int j = 0; j < lo->mTempSizes[i]; j++) + used += lo->mTemporaries[i] + j; + } + + if (lo->mProc) + { + if (!(mIns[i].mFlags & NCIF_FEXEC) && (lo->mFlags & LOBJF_ZEROPAGESET)) + { + for (int i = 0; i < 256; i++) + if (lo->mZeroPageSet[i]) + modified += i; + } + } + } + } + break; + } + } + + if (mTrueJump) + mTrueJump->CollectZeroPageUsage(used, modified, pairs); + if (mFalseJump) + mFalseJump->CollectZeroPageUsage(used, modified, pairs); + } +} + +void NativeCodeBasicBlock::GlobalRegisterXMap(int reg) +{ + if (!mVisited) + { + mVisited = true; + + for (int i = 0; i < mIns.Size(); i++) + { + NativeCodeInstruction& ins(mIns[i]); + if (ins.mMode == ASMIM_ZERO_PAGE && ins.mAddress == reg) + { + switch (ins.mType) + { + case ASMIT_STA: + ins.mType = ASMIT_TAX; + ins.mMode = ASMIM_IMPLIED; + break; + case ASMIT_LDA: + ins.mType = ASMIT_TXA; + ins.mMode = ASMIM_IMPLIED; + break; + case ASMIT_INC: + ins.mType = ASMIT_INX; + ins.mMode = ASMIM_IMPLIED; + break; + case ASMIT_DEC: + ins.mType = ASMIT_DEX; + ins.mMode = ASMIM_IMPLIED; + break; + case ASMIT_LDX: + assert(ins.mAddress == reg); + ins.mType = ASMIT_NOP; + ins.mMode = ASMIM_IMPLIED; + break; + } + } + } + + if (mTrueJump) + mTrueJump->GlobalRegisterXMap(reg); + if (mFalseJump) + mFalseJump->GlobalRegisterXMap(reg); + } +} + +void NativeCodeBasicBlock::GlobalRegisterYMap(int reg) +{ + if (!mVisited) + { + mVisited = true; + + for (int i = 0; i < mIns.Size(); i++) + { + NativeCodeInstruction& ins(mIns[i]); + if (ins.mMode == ASMIM_ZERO_PAGE && ins.mAddress == reg) + { + switch (ins.mType) + { + case ASMIT_STA: + ins.mType = ASMIT_TAY; + ins.mMode = ASMIM_IMPLIED; + break; + case ASMIT_LDA: + ins.mType = ASMIT_TYA; + ins.mMode = ASMIM_IMPLIED; + break; + case ASMIT_INC: + ins.mType = ASMIT_INY; + ins.mMode = ASMIM_IMPLIED; + break; + case ASMIT_DEC: + ins.mType = ASMIT_DEY; + ins.mMode = ASMIM_IMPLIED; + break; + case ASMIT_LDY: + assert(ins.mAddress == reg); + ins.mType = ASMIT_NOP; + ins.mMode = ASMIM_IMPLIED; + break; + } + } + } + + if (mTrueJump) + mTrueJump->GlobalRegisterYMap(reg); + if (mFalseJump) + mFalseJump->GlobalRegisterYMap(reg); + } +} + +bool NativeCodeBasicBlock::ReplaceYRegWithXReg(int start, int end) +{ + bool changed = false; + +// CheckLive(); + + for (int i = start; i < end; i++) + { + NativeCodeInstruction& ins(mIns[i]); + if (ins.ReplaceYRegWithXReg()) + changed = true; + } + +// CheckLive(); + + return changed; +} + +bool NativeCodeBasicBlock::ReduceLocalYPressure(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + CheckLive(); + +#if 1 + if (mLoopHead && mFalseJump && mEntryRequiredRegs.Size() && !mEntryRequiredRegs[CPU_REG_X] && !mExitRequiredRegs[CPU_REG_X] && mEntryBlocks.Size() == 2 && (mFalseJump == this || mTrueJump == this)) + { + NativeCodeBasicBlock* pblock, * nblock; + if (mTrueJump == this) + nblock = mFalseJump; + else + nblock = mTrueJump; + if (mEntryBlocks[0] == this) + pblock = mEntryBlocks[1]; + else + pblock = mEntryBlocks[0]; + + if (!pblock->mFalseJump && !nblock->mEntryRequiredRegs[CPU_REG_Y]) + { + int pz = pblock->mIns.Size(); + while (pz > 0 && !pblock->mIns[pz - 1].ReferencesYReg() && !pblock->mIns[pz - 1].ReferencesXReg()) + pz--; + + if (mEntryRequiredRegs[CPU_REG_Y] && pz > 0 && pblock->mIns[pz - 1].mType == ASMIT_LDY && pblock->mIns[pz - 1].mMode == ASMIM_IMMEDIATE) + { + if (CanReplaceYRegWithXReg(0, mIns.Size())) + { + mEntryRequiredRegs += CPU_REG_X; + mExitRequiredRegs += CPU_REG_X; + pblock->mExitRequiredRegs += CPU_REG_X; pblock->mExitRequiredRegs -= CPU_REG_Y; + + ReplaceYRegWithXReg(0, mIns.Size()); + + mEntryRequiredRegs -= CPU_REG_Y; + mExitRequiredRegs -= CPU_REG_Y; + + pz--; + pblock->mIns[pz].mType = ASMIT_LDX; + while (pz < pblock->mIns.Size()) + { + pblock->mIns[pz].mLive |= LIVE_CPU_REG_X; + pz++; + } + + changed = true; + } + } + } + } +#endif + int start = 0; + while (start < mIns.Size()) + { + const NativeCodeInstruction& ins(mIns[start]); + + if ((ins.mType == ASMIT_LDY || ins.mType == ASMIT_TAY) && ins.mMode != ASMIM_ABSOLUTE_X && !(ins.mLive & LIVE_CPU_REG_X)) + { + int end = start + 1; + while (end < mIns.Size()) + { + const NativeCodeInstruction& eins(mIns[end]); + if (eins.mType == ASMIT_LDY || eins.mType == ASMIT_TAY) + { + ReplaceYRegWithXReg(start, end); + changed = true; + break; + } + else if (eins.mType == ASMIT_JSR && (eins.mFlags & LOBJF_ARG_REG_Y)) + break; + else if (eins.ChangesXReg() || eins.mMode == ASMIM_INDIRECT_Y) + { + break; + } + else if (!(eins.mLive & LIVE_CPU_REG_Y)) + { + end++; + ReplaceYRegWithXReg(start, end); + changed = true; + break; + } + + end++; + } + + start = end; + } + else + start++; + } + + CheckLive(); + + if (mTrueJump && mTrueJump->ReduceLocalYPressure()) + changed = true; + + if (mFalseJump && mFalseJump->ReduceLocalYPressure()) + changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::ForwardAccuAddSub(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + int aoffset = 0, apred = 0, aload = -1; + int carry = 0; + + CheckLive(); + + for (int i = 0; i < mIns.Size(); i++) + { + if (mIns[i].mType == ASMIT_LDA && (mIns[i].mMode == ASMIM_ZERO_PAGE || mIns[i].mMode == ASMIM_ABSOLUTE) && !(mIns[i].mFlags & NCIF_VOLATILE)) + { + if (aload != -1 && mIns[i].SameEffectiveAddress(mIns[aload])) + { + if (aoffset == 0) + { + for (int j = apred; j < i; j++) + mIns[j].mLive |= LIVE_CPU_REG_A; + + if (mIns[i].mLive & LIVE_CPU_REG_Z) + { + mIns[i].mType = ASMIT_ORA; mIns[i].mMode = ASMIM_IMMEDIATE; mIns[i].mAddress = 0; + } + else + { + mIns[i].mType = ASMIT_NOP; mIns[i].mMode = ASMIM_IMPLIED; + } + changed = true; + } + else if (i + 2 < mIns.Size() && mIns[i + 1].mType == ASMIT_CLC && mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && !(mIns[i + 2].mLive & LIVE_CPU_REG_C)) + { + for (int j = apred; j < i; j++) + mIns[j].mLive |= LIVE_CPU_REG_A; + + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 2].mAddress = (mIns[i + 2].mAddress - aoffset) & 0xff; + changed = true; + } + else if (carry == 1 && i + 1 < mIns.Size() && mIns[i + 1].mType == ASMIT_ADC && mIns[i + 1].mMode == ASMIM_IMMEDIATE && !(mIns[i + 1].mLive & LIVE_CPU_REG_C)) + { + for (int j = apred; j < i; j++) + mIns[j].mLive |= LIVE_CPU_REG_A; + + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mAddress = (mIns[i + 1].mAddress - aoffset) & 0xff; + changed = true; + } + else + { + aload = i; + aoffset = 0; + } + } + else + { + aload = i; + aoffset = 0; + } + + apred = i; + } + else if (mIns[i + 0].mType == ASMIT_CLC) + carry = 1; + else if (mIns[i + 0].mType == ASMIT_SEC) + carry = -1; + else if (aload != -1 && carry == 1 && + mIns[i + 0].mType == ASMIT_ADC && mIns[i + 0].mMode == ASMIM_IMMEDIATE) + { + aoffset = (aoffset + mIns[i + 0].mAddress) & 0xff; + apred = i; + carry = 0; + } + else if (aload != -1 && carry == -1 && + mIns[i + 0].mType == ASMIT_SBC && mIns[i + 0].mMode == ASMIM_IMMEDIATE) + { + aoffset = (aoffset - mIns[i + 0].mAddress) & 0xff; + apred = i; + } + else if (aload != -1 && mIns[i].mType == ASMIT_STA && (mIns[i].mMode == ASMIM_ZERO_PAGE || mIns[i].mMode == ASMIM_ABSOLUTE) && mIns[i].SameEffectiveAddress(mIns[aload])) + { + aoffset = 0; + apred = i; + } + else if (aload != -1 && mIns[i].mType == ASMIT_STA && mIns[aload].mMode != ASMIM_ZERO_PAGE&& mIns[i].mMode == ASMIM_ZERO_PAGE) + { + aload = i; + aoffset = 0; + apred = i; + } + else + { + if (mIns[i].ChangesCarry()) + carry = 0; + + if (aload != -1 && mIns[i].ChangesAccu()) + aload = -1; + + if (aload != -1 && mIns[aload].MayBeChangedOnAddress(mIns[i])) + aload = -1; + } + } + + CheckLive(); + + if (mTrueJump && mTrueJump->ForwardAccuAddSub()) + changed = true; + + if (mFalseJump && mFalseJump->ForwardAccuAddSub()) + changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::ForwardAXYReg(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + bool xisa = false, yisa = false; + int xoffset = -1, yoffset = -1; + int zpx = -1, zpy = -1, zpa = -1; + + for (int i = 0; i < mIns.Size(); i++) + { + if (mIns[i].mType == ASMIT_TAX) + { + xisa = true; + xoffset = i; + zpx = zpa; + } + else if (mIns[i].mType == ASMIT_TXA) + { + xisa = true; + yisa = false; + xoffset = i; + zpa = zpx; + } + else if (mIns[i].mType == ASMIT_TAY) + { + yisa = true; + yoffset = i; + zpy = zpa; + } + else if (mIns[i].mType == ASMIT_TYA) + { + yisa = true; + xisa = false; + yoffset = i; + zpa = zpy; + } + else if (mIns[i].mType == ASMIT_LDA && mIns[i].mMode == ASMIM_ZERO_PAGE) + { + if (zpx == mIns[i].mAddress) + { + mIns[i].mType = ASMIT_TXA; mIns[i].mMode = ASMIM_IMPLIED; + xisa = true; + yisa = false; + int j = i; + while (j > 0 && !(mIns[j - 1].mLive & LIVE_CPU_REG_X)) + { + mIns[j - 1].mLive |= LIVE_CPU_REG_X; + j--; + } + } + else if (zpy == mIns[i].mAddress) + { + mIns[i].mType = ASMIT_TYA; mIns[i].mMode = ASMIM_IMPLIED; + yisa = true; + xisa = false; + int j = i; + while (j > 0 && !(mIns[j - 1].mLive & LIVE_CPU_REG_Y)) + { + mIns[j - 1].mLive |= LIVE_CPU_REG_Y; + j--; + } + } + else + { + xisa = false; + yisa = false; + } + zpa = mIns[i].mAddress; + } + else if (mIns[i].mType == ASMIT_LDX && mIns[i].mMode == ASMIM_ZERO_PAGE) + { + xisa = false; + zpx = mIns[i].mAddress; + } + else if (mIns[i].mType == ASMIT_LDY && mIns[i].mMode == ASMIM_ZERO_PAGE) + { + yisa = false; + zpy = mIns[i].mAddress; + } + else if (mIns[i].mType == ASMIT_STA && mIns[i].mMode == ASMIM_ZERO_PAGE) + { + zpa = mIns[i].mAddress; + if (yisa) + zpy = mIns[i].mAddress; + else if (zpy == mIns[i].mAddress) + zpy = -1; + if (xisa) + zpx = mIns[i].mAddress; + else if (zpx == mIns[i].mAddress) + zpx = -1; + } + else if (mIns[i].mType == ASMIT_STX && mIns[i].mMode == ASMIM_ZERO_PAGE) + { + zpx = mIns[i].mAddress; + if (xisa) + zpa = mIns[i].mAddress; + else if (zpa == mIns[i].mAddress) + zpa = -1; + if (zpy == mIns[i].mAddress) + zpy = -1; + } + else if (mIns[i].mType == ASMIT_STY && mIns[i].mMode == ASMIM_ZERO_PAGE) + { + zpy = mIns[i].mAddress; + if (yisa) + zpa = mIns[i].mAddress; + else if (zpa == mIns[i].mAddress) + zpa = -1; + if (zpx == mIns[i].mAddress) + zpx = -1; + } + else if (i + 1 < mIns.Size() && mIns[i].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_ADC && mIns[i + 1].mMode == ASMIM_IMMEDIATE && !(mIns[i + 1].mLive & LIVE_CPU_REG_C)) + { + if (xisa && !(mIns[i + 1].mLive & LIVE_CPU_REG_X)) + { + if (mIns[i + 1].mAddress == 1) + { + mIns[i + 0].mType = ASMIT_INX; + mIns[i + 1].mType = ASMIT_TXA; mIns[i + 1].mMode = ASMIM_IMPLIED; + for (int j = xoffset; j < i + 1; j++) + mIns[j].mLive |= LIVE_CPU_REG_X; + xisa = false; + changed = true; + } + } + else if (yisa && !(mIns[i + 1].mLive & LIVE_CPU_REG_Y)) + { + if (mIns[i + 1].mAddress == 1) + { + mIns[i + 0].mType = ASMIT_INY; + mIns[i + 1].mType = ASMIT_TYA; mIns[i + 1].mMode = ASMIM_IMPLIED; + for (int j = yoffset; j < i + 1; j++) + mIns[j].mLive |= LIVE_CPU_REG_Y; + yisa = false; + changed = true; + } + } + } + else + { + if (mIns[i].ChangesXReg()) + { + xisa = false; + zpx = -1; + } + if (mIns[i].ChangesYReg()) + { + yisa = false; + zpy = -1; + } + if (mIns[i].ChangesAccu()) + { + xisa = false; + yisa = false; + zpa = -1; + } + + if (zpa >= 0 && mIns[i].ChangesZeroPage(zpa)) + zpa = -1; + if (zpx >= 0 && mIns[i].ChangesZeroPage(zpx)) + zpx = -1; + if (zpy >= 0 && mIns[i].ChangesZeroPage(zpy)) + zpy = -1; + } + + } + + if (mTrueJump && mTrueJump->ForwardAXYReg()) + changed = true; + if (mFalseJump && mFalseJump->ForwardAXYReg()) + changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::ForwardZpYIndex(bool full) +{ + CheckLive(); + + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + int yreg = -1, yoffset = 0, ypred = 0; + + for (int i = 0; i < mIns.Size(); i++) + { + if (mIns[i].mType == ASMIT_LDY && mIns[i].mMode == ASMIM_ZERO_PAGE) + { + if (yreg == mIns[i].mAddress) + { + bool zfail = false; + int live = LIVE_CPU_REG_Y; + + if (mIns[i].mLive & LIVE_CPU_REG_Z) + { + int k = i - 1; + while (k > ypred && !mIns[k].ChangesZFlag()) + k--; + if (k != ypred) + zfail = true; + live |= LIVE_CPU_REG_Z; + } + + if (yoffset == 0 && !zfail) + { + for (int j = ypred; j < i; j++) + mIns[j].mLive |= live; + + mIns[i].mType = ASMIT_NOP; mIns[i].mMode = ASMIM_IMPLIED; + changed = true; + } + else if (yoffset == 1 && i + 1 < mIns.Size() && mIns[i + 1].mType == ASMIT_INY) + { + for (int j = ypred; j < i; j++) + mIns[j].mLive |= live; + + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + changed = true; + } + else if (yoffset == 1) + { + for (int j = ypred; j < i; j++) + mIns[j].mLive |= live; + + mIns[i + 0].mType = ASMIT_DEY; mIns[i + 0].mMode = ASMIM_IMPLIED; + yoffset = 0; + changed = true; + } + else if (yoffset == 2 && i + 2 < mIns.Size() && mIns[i + 1].mType == ASMIT_INY && mIns[i + 2].mType == ASMIT_INY) + { + for (int j = ypred; j < i; j++) + mIns[j].mLive |= live; + + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + changed = true; + } + else if (yoffset == 2 && i + 1 < mIns.Size() && mIns[i + 1].mType == ASMIT_INY) + { + for (int j = ypred; j < i; j++) + mIns[j].mLive |= LIVE_CPU_REG_Y; + + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_DEY; + changed = true; + } + else if (yoffset == 3 && i + 2 < mIns.Size() && mIns[i + 1].mType == ASMIT_INY && mIns[i + 2].mType == ASMIT_INY) + { + for (int j = ypred; j < i; j++) + mIns[j].mLive |= LIVE_CPU_REG_Y; + + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_DEY; mIns[i + 2].mMode = ASMIM_IMPLIED; + changed = true; + } + else if (yoffset == 3 && i + 1 < mIns.Size() && mIns[i + 1].mType == ASMIT_INY) + { + for (int j = ypred; j < i; j++) + mIns[j].mLive |= LIVE_CPU_REG_Y; + + mIns[i + 0].mType = ASMIT_DEY; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_DEY; mIns[i + 1].mMode = ASMIM_IMPLIED; + yoffset = 2; + changed = true; + } + + else if (yoffset == 0xff && i + 1 < mIns.Size() && mIns[i + 1].mType == ASMIT_DEY) + { + for (int j = ypred; j < i; j++) + mIns[j].mLive |= LIVE_CPU_REG_Y; + + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + changed = true; + } + else if (yoffset == 0xff) + { + for (int j = ypred; j < i; j++) + mIns[j].mLive |= LIVE_CPU_REG_Y; + + mIns[i + 0].mType = ASMIT_INY; mIns[i + 0].mMode = ASMIM_IMPLIED; + yoffset = 0; + changed = true; + } + else if (yoffset == 0xfe && i + 2 < mIns.Size() && mIns[i + 1].mType == ASMIT_DEY && mIns[i + 2].mType == ASMIT_DEY) + { + for (int j = ypred; j < i; j++) + mIns[j].mLive |= LIVE_CPU_REG_Y; + + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + changed = true; + } + else if (yoffset == 0xfe && i + 1 < mIns.Size() && mIns[i + 1].mType == ASMIT_DEY) + { + for (int j = ypred; j < i; j++) + mIns[j].mLive |= LIVE_CPU_REG_Y; + + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_INY; + changed = true; + } + else if (yoffset == 0xfd && i + 2 < mIns.Size() && mIns[i + 1].mType == ASMIT_DEY && mIns[i + 2].mType == ASMIT_DEY) + { + for (int j = ypred; j < i; j++) + mIns[j].mLive |= LIVE_CPU_REG_Y; + + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_INY; mIns[i + 2].mMode = ASMIM_IMPLIED; + changed = true; + } + else if (yoffset == 0xfd && i + 1 < mIns.Size() && mIns[i + 1].mType == ASMIT_DEY) + { + for (int j = ypred; j < i; j++) + mIns[j].mLive |= LIVE_CPU_REG_Y; + + mIns[i + 0].mType = ASMIT_INY; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_INY; mIns[i + 1].mMode = ASMIM_IMPLIED; + yoffset = 0xfe; + changed = true; + } + else + yoffset = 0; + ypred = i; + } + else + { + if (full && yreg >= 0) + { + int j = i; + while (j < mIns.Size() && !mIns[j].ReferencesZeroPage(yreg)) + j++; + if (j + 2 < mIns.Size() && + mIns[j - 1].mType == ASMIT_CLC && + mIns[j + 0].mType == ASMIT_LDA && mIns[j + 0].mMode == ASMIM_ZERO_PAGE && + mIns[j + 1].mType == ASMIT_ADC && mIns[j + 1].mMode == ASMIM_IMMEDIATE && + mIns[j + 2].mType == ASMIT_STA && mIns[j + 2].mMode == ASMIM_ZERO_PAGE && mIns[j + 2].mAddress == yreg && + !(mIns[j + 2].mLive & LIVE_CPU_REG_C)) + { + if (mIns[j + 1].mAddress == ((yoffset + 1) & 0xff)) + { + for (int k = ypred; k < i; k++) + mIns[k].mLive |= LIVE_CPU_REG_Y; + + mIns.Remove(j + 2); + mIns.Remove(j + 1); + mIns.Remove(j - 1); + mIns.Insert(i, NativeCodeInstruction(mIns[i].mIns, ASMIT_INY)); + mIns.Insert(i + 1, NativeCodeInstruction(mIns[i].mIns, ASMIT_STY, ASMIM_ZERO_PAGE, yreg)); + i += 2; + changed = true; + } + else if (mIns[j + 1].mAddress == ((yoffset - 1) & 0xff)) + { + for (int k = ypred; k < i; k++) + mIns[k].mLive |= LIVE_CPU_REG_Y; + + mIns.Remove(j + 2); + mIns.Remove(j + 1); + mIns.Remove(j - 1); + mIns.Insert(i, NativeCodeInstruction(mIns[i].mIns, ASMIT_DEY)); + mIns.Insert(i + 1, NativeCodeInstruction(mIns[i].mIns, ASMIT_STY, ASMIM_ZERO_PAGE, yreg)); + i += 2; + changed = true; + } + } + } + + yreg = mIns[i].mAddress; + yoffset = 0; + ypred = i; + } + } + else if (i + 3 < mIns.Size() && + mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && + mIns[i + 3].mType == ASMIT_TAY) + { + if (mIns[i + 1].mAddress == yreg && mIns[i + 2].mAddress == ((yoffset + 1) & 0xff) && !(mIns[i + 3].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C))) + { + for (int j = ypred; j < i; j++) + mIns[j].mLive |= LIVE_CPU_REG_Y; + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + mIns[i + 3].mType = ASMIT_INY; + changed = true; + } + else if (mIns[i + 1].mAddress == yreg && mIns[i + 2].mAddress == ((yoffset - 1) & 0xff) && !(mIns[i + 3].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C))) + { + for (int j = ypred; j < i; j++) + mIns[j].mLive |= LIVE_CPU_REG_Y; + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + mIns[i + 3].mType = ASMIT_DEY; + changed = true; + } + else + { + yreg = mIns[i + 1].mAddress; + yoffset = mIns[i + 2].mAddress; + ypred = i + 3; + i += 3; + } + } + else if (i + 3 < mIns.Size() && + mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && mIns[i + 1].mAddress == yreg && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == ((yoffset + 1) & 0xff) && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && + !(mIns[i + 3].mLive & (LIVE_CPU_REG_Y | LIVE_CPU_REG_C))) + { + for (int j = ypred; j < i + 3; j++) + mIns[j].mLive |= LIVE_CPU_REG_Y | (mIns[i + 3].mLive & LIVE_CPU_REG_Z); + mIns[i + 0].mType = ASMIT_INY; + mIns[i + 1].mType = ASMIT_STY; mIns[i + 1].mAddress = mIns[i + 3].mAddress; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + + ypred = i + 1; + yoffset = (yoffset + 1) & 0xff; + + if (mIns[i + 3].mLive & LIVE_CPU_REG_A) + { + mIns[i + 3].mType = ASMIT_TYA; + mIns[i + 3].mMode = ASMIM_IMPLIED; + } + else + { + mIns[i + 3].mType = ASMIT_NOP; + mIns[i + 3].mMode = ASMIM_IMPLIED; + } + changed = true; + } + else if (i + 3 < mIns.Size() && + mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && mIns[i + 1].mAddress == yreg && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == ((yoffset - 1) & 0xff) && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && + !(mIns[i + 3].mLive & (LIVE_CPU_REG_Y | LIVE_CPU_REG_C))) + { + for (int j = ypred; j < i + 3; j++) + mIns[j].mLive |= LIVE_CPU_REG_Y | (mIns[i + 3].mLive & LIVE_CPU_REG_Z); + mIns[i + 0].mType = ASMIT_DEY; + mIns[i + 1].mType = ASMIT_STY; mIns[i + 1].mAddress = mIns[i + 3].mAddress; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + + ypred = i + 1; + yoffset = (yoffset - 1) & 0xff; + + if (mIns[i + 3].mLive & LIVE_CPU_REG_A) + { + mIns[i + 3].mType = ASMIT_TYA; + mIns[i + 3].mMode = ASMIM_IMPLIED; + } + else + { + mIns[i + 3].mType = ASMIT_NOP; + mIns[i + 3].mMode = ASMIM_IMPLIED; + } + changed = true; + } + else if (i + 3 < mIns.Size() && + mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && mIns[i + 1].mAddress == yreg && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == ((yoffset + 2) & 0xff) && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && + !(mIns[i + 3].mLive & (LIVE_CPU_REG_Y | LIVE_CPU_REG_C))) + { + for (int j = ypred; j < i + 3; j++) + mIns[j].mLive |= LIVE_CPU_REG_Y | (mIns[i + 3].mLive & LIVE_CPU_REG_Z); + mIns[i + 0].mType = ASMIT_INY; + mIns[i + 1].mType = ASMIT_INY; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_STY; mIns[i + 2].CopyMode(mIns[i + 3]); + + ypred = i + 1; + yoffset = (yoffset + 2) & 0xff; + + if (mIns[i + 3].mLive & LIVE_CPU_REG_A) + { + mIns[i + 3].mType = ASMIT_TYA; + mIns[i + 3].mMode = ASMIM_IMPLIED; + } + else + { + mIns[i + 3].mType = ASMIT_NOP; + mIns[i + 3].mMode = ASMIM_IMPLIED; + } + + changed = true; + } + else if (i + 1 < mIns.Size() && + mIns[i + 0].mType == ASMIT_INC && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && mIns[i + 0].mAddress == yreg && + mIns[i + 1].mType == ASMIT_LDY && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && mIns[i + 1].mAddress == yreg && yoffset == 0) + { + for (int j = ypred; j < i; j++) + mIns[j].mLive |= LIVE_CPU_REG_Y; + mIns[i + 0].mType = ASMIT_INY; mIns[i + 0].mMode = ASMIM_IMPLIED; mIns[i + 0].mLive |= LIVE_CPU_REG_Y; + mIns[i + 1].mType = ASMIT_STY; + + ypred = i + 1; + yoffset = (yoffset + 1) & 0xff; + + changed = true; + } + else if (i + 2 < mIns.Size() && full && + mIns[i + 0].mType == ASMIT_INC && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && mIns[i + 0].mAddress == yreg && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && mIns[i + 1].mAddress == yreg && yoffset == 0 && !(mIns[i + 1].mLive & LIVE_CPU_REG_Y)) + { + for (int j = ypred; j < i; j++) + mIns[j].mLive |= LIVE_CPU_REG_Y; + mIns.Insert(i, NativeCodeInstruction(mIns[i].mIns, ASMIT_INY, ASMIM_IMPLIED)); + mIns[i + 1].mType = ASMIT_STY; mIns[i + 1].mLive |= LIVE_CPU_REG_Y; + mIns[i + 2].mType = ASMIT_TYA; mIns[i + 2].mMode = ASMIM_IMPLIED; + yreg = -1; + changed = true; + } + else if (mIns[i].mType == ASMIT_INY) + { + yoffset = (yoffset + 1) & 255; + } + else if (mIns[i].mType == ASMIT_DEY) + { + yoffset = (yoffset - 1) & 255; + } + else if (i + 1 < mIns.Size() && mIns[i].mType == ASMIT_TAY && mIns[i + 1].mType == ASMIT_STA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE) + { + i++; + yreg = mIns[i].mAddress; + yoffset = 0; + ypred = i; + } + else if (mIns[i].ChangesYReg()) + { + if (full && yreg >= 0 && !mIns[i].RequiresYReg()) + { + int j = i; + while (j < mIns.Size() && !mIns[j].ReferencesZeroPage(yreg)) + j++; + if (j + 2 < mIns.Size() && + mIns[j - 1].mType == ASMIT_CLC && + mIns[j + 0].mType == ASMIT_LDA && mIns[j + 0].mMode == ASMIM_ZERO_PAGE && + mIns[j + 1].mType == ASMIT_ADC && mIns[j + 1].mMode == ASMIM_IMMEDIATE && + mIns[j + 2].mType == ASMIT_STA && mIns[j + 2].mMode == ASMIM_ZERO_PAGE && mIns[j + 2].mAddress == yreg && + !(mIns[j + 2].mLive & LIVE_CPU_REG_C)) + { + if (mIns[j + 1].mAddress == yoffset + 1) + { + for (int k = ypred; k < i; k++) + mIns[k].mLive |= LIVE_CPU_REG_Y; + + mIns.Remove(j + 2); + mIns.Remove(j + 1); + mIns.Remove(j - 1); + mIns.Insert(i, NativeCodeInstruction(mIns[i].mIns, ASMIT_INY)); + mIns.Insert(i + 1, NativeCodeInstruction(mIns[i].mIns, ASMIT_STY, ASMIM_ZERO_PAGE, yreg)); + i += 2; + changed = true; + } + } + } + + yreg = -1; + } + else if (mIns[i].mType == ASMIT_STY && mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i].mAddress == yreg) + { + yoffset = 0; + ypred = i; + } + else if (mIns[i].mType == ASMIT_INC && mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i].mAddress == yreg && yoffset == 1 && !(mIns[i].mLive & LIVE_CPU_REG_Z)) + { + mIns[i].mType = ASMIT_STY; + for (int j = ypred; j < i; j++) + mIns[j].mLive |= LIVE_CPU_REG_Y; + yoffset = 0; + ypred = i; + changed = true; + } +#if 1 + else if (mIns[i].mType == ASMIT_INC && mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i].mAddress == yreg && yoffset == 0 && mIns[ypred].mType == ASMIT_STY && !(mIns[i].mLive & LIVE_CPU_REG_Y)) + { + for (int j = ypred; j < i; j++) + mIns[j].mLive |= LIVE_CPU_REG_Y; + mIns[i].mType = ASMIT_STY; + mIns.Insert(i, NativeCodeInstruction(mIns[i].mIns, ASMIT_INY)); + ypred = i + 1; + } +#endif + else if (yreg >= 0 && mIns[i].ChangesZeroPage(yreg)) + { + yreg = -1; + } + } + + CheckLive(); + + if (mTrueJump && mTrueJump->ForwardZpYIndex(full)) + changed = true; + + if (mFalseJump && mFalseJump->ForwardZpYIndex(full)) + changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::CanCombineSameYtoX(int start, int end) +{ + for (int i = start; i < end; i++) + { + NativeCodeInstruction& ins(mIns[i]); + + if (ins.mMode == ASMIM_INDIRECT_Y) + return false; + + if (ins.mMode == ASMIM_ABSOLUTE_Y && !HasAsmInstructionMode(ins.mType, ASMIM_ABSOLUTE_X)) + return false; + } + + return true; +} + +bool NativeCodeBasicBlock::CanCombineSameXtoY(int start, int end) +{ + for (int i = start; i < end; i++) + { + NativeCodeInstruction& ins(mIns[i]); + + if (ins.mMode == ASMIM_INDIRECT_X) + return false; + + if (ins.mMode == ASMIM_ABSOLUTE_X && !HasAsmInstructionMode(ins.mType, ASMIM_ABSOLUTE_Y)) + return false; + + } + + return true; +} + + + +bool NativeCodeBasicBlock::CombineSameXtoY(int xpos, int ypos, int end) +{ + if (xpos < ypos) + { + if (CanCombineSameXtoY(xpos, ypos) && + CanCombineSameXtoY(ypos + 1, end) && + !ReferencesYReg(xpos, ypos)) + { + ReplaceXRegWithYReg(xpos, ypos); + ReplaceXRegWithYReg(ypos + 1, end); + + for (int i = xpos; i < ypos; i++) + mIns[i].mLive |= LIVE_CPU_REG_Y; + + if (!(mIns[ypos].mLive & LIVE_CPU_REG_Z)) + { + mIns[ypos].mType = ASMIT_NOP; + mIns[ypos].mMode = ASMIM_IMPLIED; + } + return true; + } + } + else + { + if (CanCombineSameXtoY(xpos, end)) + { + ReplaceXRegWithYReg(xpos, end); + for (int i = ypos; i < xpos; i++) + mIns[i].mLive |= LIVE_CPU_REG_Y; + + if (!(mIns[xpos].mLive & LIVE_CPU_REG_Z)) + { + mIns[xpos].mType = ASMIT_NOP; + mIns[xpos].mMode = ASMIM_IMPLIED; + } + return true; + } + } + + return false; +} + +bool NativeCodeBasicBlock::CombineSameYtoX(int xpos, int ypos, int end) +{ + if (ypos < xpos) + { + if (CanCombineSameYtoX(ypos, xpos) && + CanCombineSameYtoX(xpos + 1, end) && + !ReferencesXReg(ypos, xpos)) + { + ReplaceYRegWithXReg(ypos, xpos); + ReplaceYRegWithXReg(xpos + 1, end); + + for (int i = ypos; i < xpos; i++) + mIns[i].mLive |= LIVE_CPU_REG_X; + + if (!(mIns[xpos].mLive & LIVE_CPU_REG_Z)) + { + mIns[xpos].mType = ASMIT_NOP; + mIns[xpos].mMode = ASMIM_IMPLIED; + } + return true; + } + } + else + { + if (CanCombineSameYtoX(ypos, end)) + { + ReplaceYRegWithXReg(ypos, end); + for (int i = xpos; i < ypos; i++) + mIns[i].mLive |= LIVE_CPU_REG_X; + + if (!(mIns[ypos].mLive & LIVE_CPU_REG_Z)) + { + mIns[ypos].mType = ASMIT_NOP; + mIns[ypos].mMode = ASMIM_IMPLIED; + } + return true; + } + } + + return false; +} + +bool NativeCodeBasicBlock::CombineSameXY(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + int xreg = -1, yreg = -1; + int xpos, ypos; + bool samexy = false; + + CheckLive(); + + for (int i = 0; i < mIns.Size(); i++) + { + NativeCodeInstruction& ins(mIns[i]); + + if (ins.ChangesXReg()) + { + if (samexy) + { + if (!ins.RequiresXReg() && CombineSameXtoY(xpos, ypos, i)) + changed = true; + else if (!ins.RequiresYReg() && !(ins.mLive & LIVE_CPU_REG_Y) && CombineSameYtoX(xpos, ypos, i)) + { + changed = true; + yreg = -1; + } + CheckLive(); + } + + xreg = -1; + samexy = false; + } + if (ins.ChangesYReg()) + { + if (samexy) + { + if (!ins.RequiresYReg() && CombineSameYtoX(xpos, ypos, i)) + changed = true; + else if (!ins.RequiresXReg() && !(ins.mLive & LIVE_CPU_REG_X) && CombineSameXtoY(xpos, ypos, i)) + { + changed = true; + xreg = -1; + } + CheckLive(); + } + + yreg = -1; + samexy = false; + } + + if (ins.mType == ASMIT_TAX) + { + xreg = CPU_REG_A; + xpos = i; + samexy = yreg == xreg; + } + else if (ins.mType == ASMIT_TAY) + { + yreg = CPU_REG_A; + ypos = i; + samexy = xreg == yreg; + } + else if (ins.mType == ASMIT_LDX) + { + if (ins.mMode == ASMIM_ZERO_PAGE) + { + xreg = ins.mAddress; + xpos = i; + samexy = yreg == xreg; + } + else + xreg = -1; + } + else if (ins.mType == ASMIT_LDY) + { + if (ins.mMode == ASMIM_ZERO_PAGE) + { + yreg = ins.mAddress; + ypos = i; + samexy = xreg == yreg; + } + else + yreg = -1; + } + else if (ins.ChangesAccu()) + { + if (xreg == CPU_REG_A) + xreg = -1; + if (yreg == CPU_REG_A) + yreg = -1; + } + else if (ins.mMode == ASMIM_ZERO_PAGE && ins.ChangesAddress()) + { + if (xreg == ins.mAddress) + xreg = -1; + if (yreg == ins.mAddress) + yreg = -1; + } + } + + + if (samexy) + { + if (!mExitRequiredRegs[CPU_REG_X] && CombineSameXtoY(xpos, ypos, mIns.Size())) + changed = true; + else if (!mExitRequiredRegs[CPU_REG_Y] && CombineSameYtoX(xpos, ypos, mIns.Size())) + changed = true; + } + + CheckLive(); + + if (mTrueJump && mTrueJump->CombineSameXY()) + changed = true; + if (mFalseJump && mFalseJump->CombineSameXY()) + changed = true; + } + + return changed; +} + + +bool NativeCodeBasicBlock::JoinXYCrossBlock(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + int xins = -1, yins = -1; + + for (int i = 0; i < mIns.Size(); i++) + { + if (mIns[i].mType == ASMIT_LDX) + { + xins = i; + if (yins >= 0 && mIns[yins].SameEffectiveAddress(mIns[xins])) + { + if (CanCombineSameXtoYCrossBlock(xins + 1)) + { + for (int j = yins; j <= xins; j++) + mIns[j].mLive |= LIVE_CPU_REG_Y; + if (CombineSameXtoYCrossBlock(xins + 1)) + changed = true; + } + else if (CanCombineSameYtoX(yins + 1, xins) && CanCombineSameYtoXCrossBlock(xins + 1)) + { + ReplaceXRegWithYReg(yins, xins); + for (int j = yins; j <= xins; j++) + mIns[j].mLive |= LIVE_CPU_REG_X; + if (CombineSameYtoXCrossBlock(xins + 1)) + changed = true; + } + } + yins = -1; + } + else if (mIns[i].mType == ASMIT_LDY) + { + yins = i; + if (xins >= 0 && mIns[yins].SameEffectiveAddress(mIns[xins])) + { + if (CanCombineSameYtoXCrossBlock(yins + 1)) + { + for (int j = xins; j <= yins; j++) + mIns[j].mLive |= LIVE_CPU_REG_X; + if (CombineSameYtoXCrossBlock(yins + 1)) + changed = true; + } + else if (CanCombineSameXtoY(xins + 1, yins) && CanCombineSameXtoYCrossBlock(yins + 1)) + { + ReplaceYRegWithXReg(xins, yins); + for (int j = xins; j <= yins; j++) + mIns[j].mLive |= LIVE_CPU_REG_Y; + if (CombineSameXtoYCrossBlock(yins + 1)) + changed = true; + } + } + xins = -1; + } + else + { + if (mIns[i].ChangesXReg()) + xins = -1; + if (mIns[i].ChangesYReg()) + yins = -1; + if (xins >= 0 && mIns[xins].MayBeChangedOnAddress(mIns[i])) + xins = -1; + if (yins >= 0 && mIns[yins].MayBeChangedOnAddress(mIns[i])) + yins = -1; + } + } + + if (mTrueJump && mTrueJump->JoinXYCrossBlock()) + changed = true; + if (mFalseJump && mFalseJump->JoinXYCrossBlock()) + changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::CanCombineSameXtoYCrossBlock(int from) +{ + if (!from) + { + if (!mEntryRequiredRegs[CPU_REG_X]) + return true; + if (mEntryBlocks.Size() > 1) + return false; + } + + for (int i = from; i < mIns.Size(); i++) + { + if (mIns[i].ChangesXReg()) + return !mIns[i].ReferencesXReg(); + if (mIns[i].ChangesYReg()) + return false; + if (mIns[i].mMode == ASMIM_INDIRECT_X) + return false; + if (mIns[i].mMode == ASMIM_ABSOLUTE_X && !HasAsmInstructionMode(mIns[i].mType, ASMIM_ABSOLUTE_Y)) + return false; + } + + if (mTrueJump && !mTrueJump->CanCombineSameXtoYCrossBlock(0)) + return false; + if (mFalseJump && !mFalseJump->CanCombineSameXtoYCrossBlock(0)) + return false; + + return true; +} + +bool NativeCodeBasicBlock::CanCombineSameYtoXCrossBlock(int from) +{ + if (!from) + { + if (!mEntryRequiredRegs[CPU_REG_Y]) + return true; + if (mEntryBlocks.Size() > 1) + return false; + } + + for (int i = from; i < mIns.Size(); i++) + { + if (mIns[i].ChangesYReg()) + return !mIns[i].ReferencesYReg(); + if (mIns[i].ChangesXReg()) + return false; + if (mIns[i].mMode == ASMIM_INDIRECT_Y) + return false; + if (mIns[i].mMode == ASMIM_ABSOLUTE_Y && !HasAsmInstructionMode(mIns[i].mType, ASMIM_ABSOLUTE_X)) + return false; + } + + if (mTrueJump && !mTrueJump->CanCombineSameYtoXCrossBlock(0)) + return false; + if (mFalseJump && !mFalseJump->CanCombineSameYtoXCrossBlock(0)) + return false; + + return true; +} + +bool NativeCodeBasicBlock::CombineSameXtoYCrossBlock(int from) +{ + if (!from) + { + if (!mEntryRequiredRegs[CPU_REG_X]) + return false; + mEntryRequiredRegs += CPU_REG_Y; + assert(mEntryBlocks.Size() == 1); + } + + bool changed = false; + + for (int i = from; i < mIns.Size(); i++) + { + if (mIns[i].ChangesXReg()) + return changed; + + mIns[i].mLive |= LIVE_CPU_REG_Y; + + if (mIns[i].mMode == ASMIM_ABSOLUTE_X) + { + mIns[i].mMode = ASMIM_ABSOLUTE_Y; + changed = true; + } + if (mIns[i].mType == ASMIT_TXA) + { + mIns[i].mType = ASMIT_TYA; + changed = true; + } + else if (mIns[i].mType == ASMIT_STX) + { + mIns[i].mType = ASMIT_STY; + changed = true; + } + } + + mExitRequiredRegs += CPU_REG_Y; + + if (mTrueJump && mTrueJump->CombineSameXtoYCrossBlock(0)) + changed = true; + if (mFalseJump && !mFalseJump->CombineSameXtoYCrossBlock(0)) + changed = true; + + return changed; +} + +bool NativeCodeBasicBlock::CombineSameYtoXCrossBlock(int from) +{ + if (!from) + { + if (!mEntryRequiredRegs[CPU_REG_Y]) + return false; + mEntryRequiredRegs += CPU_REG_X; + assert(mEntryBlocks.Size() == 1); + } + + bool changed = false; + + for (int i = from; i < mIns.Size(); i++) + { + if (mIns[i].ChangesYReg()) + return changed; + + mIns[i].mLive |= LIVE_CPU_REG_X; + + if (mIns[i].mMode == ASMIM_ABSOLUTE_Y) + { + mIns[i].mMode = ASMIM_ABSOLUTE_X; + changed = true; + } + if (mIns[i].mType == ASMIT_TYA) + { + mIns[i].mType = ASMIT_TXA; + changed = true; + } + else if (mIns[i].mType == ASMIT_STY) + { + mIns[i].mType = ASMIT_STX; + changed = true; + } + } + + mExitRequiredRegs += CPU_REG_X; + + if (mTrueJump && mTrueJump->CombineSameYtoXCrossBlock(0)) + changed = true; + if (mFalseJump && !mFalseJump->CombineSameYtoXCrossBlock(0)) + changed = true; + + return changed; +} + +bool NativeCodeBasicBlock::JoinXYCascade(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + bool restart; + + do { + restart = false; + int predXPos = -1, predYPos = -1; + + for (int i = 0; i + 2 < mIns.Size(); i++) + { + if (mIns[i + 0].mType == ASMIT_TXA && + mIns[i + 1].mType == ASMIT_CLC && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE) + { + if (predXPos >= 0 && !(mIns[i + 2].mLive & (LIVE_CPU_REG_X | LIVE_CPU_REG_C)) && ((mIns[predXPos + 2].mAddress + 1) & 0xff) == mIns[i + 2].mAddress) + { + // Remove add + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + + // Insert INX + mIns.Insert(i, NativeCodeInstruction(mIns[i].mIns, ASMIT_INX)); + + // Insert TAX + mIns[predXPos + 2].mLive &= ~LIVE_CPU_REG_X; + mIns.Insert(predXPos + 3, NativeCodeInstruction(mIns[i].mIns, ASMIT_TAX)); + + // Restart + restart = true; + predXPos = -1; + predYPos = -1; + + changed = true; + } + else if (predXPos >= 0 && !(mIns[i + 2].mLive & (LIVE_CPU_REG_X | LIVE_CPU_REG_C)) && ((mIns[predXPos + 2].mAddress - 1) & 0xff) == mIns[i + 2].mAddress) + { + // Remove add + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + + // Insert DEX + mIns.Insert(i, NativeCodeInstruction(mIns[i].mIns, ASMIT_DEX)); + + // Insert TAX + mIns[predXPos + 2].mLive &= ~LIVE_CPU_REG_X; + mIns.Insert(predXPos + 3, NativeCodeInstruction(mIns[i].mIns, ASMIT_TAX)); + + // Restart + restart = true; + predXPos = -1; + predYPos = -1; + + changed = true; + } + else + predXPos = i; + } + else if (mIns[i + 0].ReferencesXReg()) + predXPos = -1; + else if (mIns[i + 0].mType == ASMIT_TYA && + mIns[i + 1].mType == ASMIT_CLC && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE) + { + if (predYPos >= 0 && !(mIns[i + 2].mLive & (LIVE_CPU_REG_Y | LIVE_CPU_REG_C)) && ((mIns[predYPos + 2].mAddress + 1) & 0xff) == mIns[i + 2].mAddress) + { + // Remove add + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + + // Insert INY + mIns.Insert(i, NativeCodeInstruction(mIns[i].mIns, ASMIT_INY)); + + // Insert TAX + mIns[predYPos + 2].mLive &= ~LIVE_CPU_REG_Y; + mIns.Insert(predYPos + 3, NativeCodeInstruction(mIns[i].mIns, ASMIT_TAY)); + + // Restart + restart = true; + predXPos = -1; + predYPos = -1; + + changed = true; + } + else if (predYPos >= 0 && !(mIns[i + 2].mLive & (LIVE_CPU_REG_Y | LIVE_CPU_REG_C)) && ((mIns[predYPos + 2].mAddress - 1) & 0xff) == mIns[i + 2].mAddress) + { + // Remove add + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + + // Insert DEY + mIns.Insert(i, NativeCodeInstruction(mIns[i].mIns, ASMIT_DEY)); + + // Insert TAX + mIns[predYPos + 2].mLive &= ~LIVE_CPU_REG_Y; + mIns.Insert(predYPos + 3, NativeCodeInstruction(mIns[i].mIns, ASMIT_TAY)); + + // Restart + restart = true; + predXPos = -1; + predYPos = -1; + + changed = true; + } + else + predYPos = i; + } + else if (mIns[i + 0].ReferencesYReg()) + predYPos = -1; + } + } while (restart); + + if (mTrueJump && mTrueJump->JoinXYCascade()) + changed = true; + if (mFalseJump && mFalseJump->JoinXYCascade()) + changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::GlobalLoadStoreForwarding(bool zpage, const NativeCodeInstruction & als, const NativeCodeInstruction & xls, const NativeCodeInstruction & yls) +{ + bool changed = false; + + if (!mVisited) + { + mNumEntered++; + + if (mLoopHead) + { + mALSIns.mType = ASMIT_INV; + mXLSIns.mType = ASMIT_INV; + mYLSIns.mType = ASMIT_INV; + } + else if (mNumEntered == 1) + { + mALSIns = als; + mXLSIns = xls; + mYLSIns = yls; + } + else + { + if (als.mType == ASMIT_INV || (mALSIns.mType != ASMIT_INV && !mALSIns.SameEffectiveAddress(als))) mALSIns.mType = ASMIT_INV; + if (xls.mType == ASMIT_INV || (mXLSIns.mType != ASMIT_INV && !mXLSIns.SameEffectiveAddress(xls))) mXLSIns.mType = ASMIT_INV; + if (yls.mType == ASMIT_INV || (mYLSIns.mType != ASMIT_INV && !mYLSIns.SameEffectiveAddress(yls))) mYLSIns.mType = ASMIT_INV; + } + + if (!mLoopHead && mNumEntered != mNumEntries) + return false; + + mVisited = true; + + for (int i = 0; i < mIns.Size(); i++) + { + NativeCodeInstruction& ins(mIns[i]); + if (mALSIns.mType != ASMIT_INV && ins.mType == ASMIT_LDA && i + 1 < mIns.Size() && + mIns[i + 1].SameEffectiveAddress(mALSIns) && mIns[i + 1].IsCommutative()) + { + ins.mType = mIns[i + 1].mType; + mIns[i + 1].mType = ASMIT_NOP; + mIns[i + 1].mMode = ASMIM_IMPLIED; + changed = true; + } + else if (mALSIns.mType != ASMIT_INV && ins.mType == ASMIT_LDA && i + 2 < mIns.Size() && + (mIns[i + 1].mType == ASMIT_CLC || mIns[i + 1].mType == ASMIT_SEC) && + mIns[i + 2].SameEffectiveAddress(mALSIns) && mIns[i + 2].IsCommutative()) + { + mIns[i + 2].CopyMode(ins); + ins.mType = ASMIT_NOP; ins.mMode = ASMIM_IMPLIED; + changed = true; + } + else if (mXLSIns.mType != ASMIT_INV && ins.mType == ASMIT_LDA && i + 1 < mIns.Size() && + mIns[i + 1].SameEffectiveAddress(mXLSIns) && mIns[i + 1].IsCommutative()) + { + mIns[i + 1].CopyMode(ins); + ins.mType = ASMIT_TXA; + ins.mMode = ASMIM_IMPLIED; + changed = true; + } + else if (mYLSIns.mType != ASMIT_INV && ins.mType == ASMIT_LDA && i + 1 < mIns.Size() && + mIns[i + 1].SameEffectiveAddress(mYLSIns) && mIns[i + 1].IsCommutative()) + { + mIns[i + 1].CopyMode(ins); + ins.mType = ASMIT_TYA; + ins.mMode = ASMIM_IMPLIED; + changed = true; + } + + if (ins.mType == ASMIT_STA) + { + if (mALSIns.mType != ASMIT_INV && ins.SameEffectiveAddress(mALSIns) && !(ins.mFlags & NCIF_VOLATILE)) + { + ins.mType = ASMIT_NOP; + ins.mMode = ASMIM_IMPLIED; + changed = true; + } + else + { + if (((zpage && ins.mMode == ASMIM_ZERO_PAGE) || ins.mMode == ASMIM_ABSOLUTE || ins.mMode == ASMIM_ABSOLUTE_X || ins.mMode == ASMIM_ABSOLUTE_Y) && !(ins.mFlags & NCIF_VOLATILE)) + mALSIns = ins; + if (mXLSIns.mType != ASMIT_INV && ins.MayBeSameAddress(mXLSIns)) + mXLSIns.mType = ASMIT_INV; + if (mYLSIns.mType != ASMIT_INV && ins.MayBeSameAddress(mYLSIns)) + mYLSIns.mType = ASMIT_INV; + } + } + else if (ins.mType == ASMIT_LDA) + { + if (mALSIns.mType != ASMIT_INV && ins.SameEffectiveAddress(mALSIns) && !(ins.mFlags & NCIF_VOLATILE)) + { + ins.mType = ASMIT_ORA; + ins.mMode = ASMIM_IMMEDIATE; + ins.mAddress = 0; + changed = true; + } + else if (((zpage && ins.mMode == ASMIM_ZERO_PAGE) || ins.mMode == ASMIM_ABSOLUTE || ins.mMode == ASMIM_ABSOLUTE_X || ins.mMode == ASMIM_ABSOLUTE_Y) && !(ins.mFlags & NCIF_VOLATILE)) + mALSIns = ins; + else + mALSIns.mType = ASMIT_INV; + } + else if (ins.mType == ASMIT_STX) + { + if (mXLSIns.mType != ASMIT_INV && ins.SameEffectiveAddress(mXLSIns) && !(ins.mFlags & NCIF_VOLATILE)) + { + ins.mType = ASMIT_NOP; + ins.mMode = ASMIM_IMPLIED; + changed = true; + } + else + { + if (((zpage && ins.mMode == ASMIM_ZERO_PAGE) || ins.mMode == ASMIM_ABSOLUTE || ins.mMode == ASMIM_ABSOLUTE_Y) && !(ins.mFlags & NCIF_VOLATILE)) + mXLSIns = ins; + if (mALSIns.mType != ASMIT_INV && ins.MayBeSameAddress(mALSIns)) + mALSIns.mType = ASMIT_INV; + if (mYLSIns.mType != ASMIT_INV && ins.MayBeSameAddress(mYLSIns)) + mYLSIns.mType = ASMIT_INV; + } + } + else if (ins.mType == ASMIT_LDX) + { + if (mXLSIns.mType != ASMIT_INV && ins.SameEffectiveAddress(mXLSIns) && !(ins.mLive & LIVE_CPU_REG_Z) && !(ins.mFlags & NCIF_VOLATILE)) + { + ins.mType = ASMIT_NOP; + ins.mMode = ASMIM_IMPLIED; + ins.mAddress = 0; + changed = true; + } + else + { + if (((zpage && ins.mMode == ASMIM_ZERO_PAGE) || ins.mMode == ASMIM_ABSOLUTE || ins.mMode == ASMIM_ABSOLUTE_Y) && !(ins.mFlags & NCIF_VOLATILE)) + mXLSIns = ins; + else + mXLSIns.mType = ASMIT_INV; + if (mALSIns.mType != ASMIT_INV && mALSIns.mMode == ASMIM_ABSOLUTE_X) + mALSIns.mType = ASMIT_INV; + if (mYLSIns.mType != ASMIT_INV && mYLSIns.mMode == ASMIM_ABSOLUTE_X) + mYLSIns.mType = ASMIT_INV; + } + } + else if (ins.mType == ASMIT_STY) + { + if (mYLSIns.mType != ASMIT_INV && ins.SameEffectiveAddress(mYLSIns) && !(ins.mFlags & NCIF_VOLATILE)) + { + ins.mType = ASMIT_NOP; + ins.mMode = ASMIM_IMPLIED; + changed = true; + } + else + { + if (((zpage && ins.mMode == ASMIM_ZERO_PAGE) || ins.mMode == ASMIM_ABSOLUTE || ins.mMode == ASMIM_ABSOLUTE_X) && !(ins.mFlags & NCIF_VOLATILE)) + mYLSIns = ins; + if (mALSIns.mType != ASMIT_INV && ins.MayBeSameAddress(mALSIns)) + mALSIns.mType = ASMIT_INV; + if (mXLSIns.mType != ASMIT_INV && ins.MayBeSameAddress(mXLSIns)) + mXLSIns.mType = ASMIT_INV; + } + } + else if (ins.mType == ASMIT_LDY) + { + if (mYLSIns.mType != ASMIT_INV && ins.SameEffectiveAddress(mYLSIns) && !(ins.mLive & LIVE_CPU_REG_Z) && !(ins.mFlags & NCIF_VOLATILE)) + { + ins.mType = ASMIT_NOP; + ins.mMode = ASMIM_IMPLIED; + ins.mAddress = 0; + changed = true; + } + else + { + if (((zpage && ins.mMode == ASMIM_ZERO_PAGE) || ins.mMode == ASMIM_ABSOLUTE || ins.mMode == ASMIM_ABSOLUTE_X) && !(ins.mFlags & NCIF_VOLATILE)) + mYLSIns = ins; + else + mYLSIns.mType = ASMIT_INV; + if (mALSIns.mType != ASMIT_INV && mALSIns.mMode == ASMIM_ABSOLUTE_Y) + mALSIns.mType = ASMIT_INV; + if (mXLSIns.mType != ASMIT_INV && mXLSIns.mMode == ASMIM_ABSOLUTE_Y) + mXLSIns.mType = ASMIT_INV; + } + } + else if (ins.mType == ASMIT_TAY) + { + if (mALSIns.mMode == ASMIM_ABSOLUTE_Y) + mALSIns.mType = ASMIT_INV; + if (mXLSIns.mMode == ASMIM_ABSOLUTE_Y) + mXLSIns.mType = ASMIT_INV; + mYLSIns = mALSIns; + } + else if (ins.mType == ASMIT_TAX) + { + if (mALSIns.mMode == ASMIM_ABSOLUTE_X) + mALSIns.mType = ASMIT_INV; + if (mYLSIns.mMode == ASMIM_ABSOLUTE_X) + mYLSIns.mType = ASMIT_INV; + mXLSIns = mALSIns; + } + else if (ins.mType == ASMIT_TYA) + { + mALSIns = mYLSIns; + } + else if (ins.mType == ASMIT_TXA) + { + mALSIns = mXLSIns; + } + else + { + if (ins.ChangesAccu()) + mALSIns.mType = ASMIT_INV; + if (ins.ChangesXReg()) + { + mXLSIns.mType = ASMIT_INV; + if (mALSIns.mMode == ASMIM_ABSOLUTE_X) + mALSIns.mType = ASMIT_INV; + if (mYLSIns.mMode == ASMIM_ABSOLUTE_X) + mYLSIns.mType = ASMIT_INV; + } + if (ins.ChangesYReg()) + { + mYLSIns.mType = ASMIT_INV; + if (mALSIns.mMode == ASMIM_ABSOLUTE_Y) + mALSIns.mType = ASMIT_INV; + if (mXLSIns.mMode == ASMIM_ABSOLUTE_Y) + mXLSIns.mType = ASMIT_INV; + } + if (ins.ChangesAddress()) + { + if (mALSIns.mType != ASMIT_INV && mALSIns.MayBeSameAddress(ins)) + mALSIns.mType = ASMIT_INV; + if (mXLSIns.mType != ASMIT_INV && mXLSIns.MayBeSameAddress(ins)) + mXLSIns.mType = ASMIT_INV; + if (mYLSIns.mType != ASMIT_INV && mYLSIns.MayBeSameAddress(ins)) + mYLSIns.mType = ASMIT_INV; + } + } + } + + if (mTrueJump && mTrueJump->GlobalLoadStoreForwarding(zpage, mALSIns, mXLSIns, mYLSIns)) + changed = true; + if (mFalseJump && mFalseJump->GlobalLoadStoreForwarding(zpage, mALSIns, mXLSIns, mYLSIns)) + changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::RegisterValueForwarding(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + FastNumberSet xreg(261), yreg(261), areg(261); + + for (int i = 0; i < mIns.Size(); i++) + { + if (mIns[i].mMode == ASMIM_ZERO_PAGE) + { + if (mIns[i].mType == ASMIT_LDX) + { + if (xreg[mIns[i].mAddress] && !(mIns[i].mLive & LIVE_CPU_REG_Z)) + { + mIns[i].mType = ASMIT_NOP; mIns[i].mMode = ASMIM_IMPLIED; + changed = true; + } + else + { + xreg.Clear(); + xreg += mIns[i].mAddress; + } + } + else if (mIns[i].mType == ASMIT_STX) + { + if (xreg[mIns[i].mAddress]) + { + mIns[i].mType = ASMIT_NOP; mIns[i].mMode = ASMIM_IMPLIED; + changed = true; + } + else + { + xreg += mIns[i].mAddress; + yreg -= mIns[i].mAddress; + areg -= mIns[i].mAddress; + } + } + else if (mIns[i].mType == ASMIT_LDY) + { + if (yreg[mIns[i].mAddress] && !(mIns[i].mLive & LIVE_CPU_REG_Z)) + { + mIns[i].mType = ASMIT_NOP; mIns[i].mMode = ASMIM_IMPLIED; + changed = true; + } + else + { + yreg.Clear(); + yreg += mIns[i].mAddress; + } + } + else if (mIns[i].mType == ASMIT_STY) + { + if (yreg[mIns[i].mAddress]) + { + mIns[i].mType = ASMIT_NOP; mIns[i].mMode = ASMIM_IMPLIED; + changed = true; + } + else + { + yreg += mIns[i].mAddress; + xreg -= mIns[i].mAddress; + areg -= mIns[i].mAddress; + } + } + else if (mIns[i].mType == ASMIT_LDA) + { + if (areg[mIns[i].mAddress] && !(mIns[i].mLive & LIVE_CPU_REG_Z)) + { + mIns[i].mType = ASMIT_NOP; mIns[i].mMode = ASMIM_IMPLIED; + changed = true; + } + else + { + areg.Clear(); + areg += mIns[i].mAddress; + } + } + else if (mIns[i].mType == ASMIT_STA) + { + if (areg[mIns[i].mAddress]) + { + mIns[i].mType = ASMIT_NOP; mIns[i].mMode = ASMIM_IMPLIED; + changed = true; + } + else + { + areg += mIns[i].mAddress; + xreg -= mIns[i].mAddress; + yreg -= mIns[i].mAddress; + } + } + else if (mIns[i].ChangesAddress()) + { + xreg -= mIns[i].mAddress; + yreg -= mIns[i].mAddress; + areg -= mIns[i].mAddress; + } + else if (mIns[i].ChangesAccu()) + { + areg.Clear(); + } + } + else if (mIns[i].mType == ASMIT_JSR) + { + xreg.Clear(); + yreg.Clear(); + areg.Clear(); + } + else if (mIns[i].ChangesXReg()) + { + xreg.Clear(); + } + else if (mIns[i].ChangesYReg()) + { + yreg.Clear(); + } + else if (mIns[i].ChangesAccu()) + { + areg.Clear(); + } + } + + if (mTrueJump && mTrueJump->RegisterValueForwarding()) + changed = true; + if (mFalseJump && mFalseJump->RegisterValueForwarding()) + changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::ForwardZpXIndex(bool full) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + int xreg = -1, xoffset = 0, xpred = 0; + + for (int i = 0; i < mIns.Size(); i++) + { + if (mIns[i].mType == ASMIT_LDX && mIns[i].mMode == ASMIM_ZERO_PAGE) + { + if (xreg == mIns[i].mAddress) + { + if (xoffset == 0) + { + for (int j = xpred; j < i; j++) + mIns[j].mLive |= LIVE_CPU_REG_X; + + mIns[i].mType = ASMIT_NOP; mIns[i].mMode = ASMIM_IMPLIED; + changed = true; + } + else if (xoffset == 1 && i + 1 < mIns.Size() && mIns[i + 1].mType == ASMIT_INX) + { + for (int j = xpred; j < i; j++) + mIns[j].mLive |= LIVE_CPU_REG_X; + + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + changed = true; + } + else if (xoffset == 2 && i + 2 < mIns.Size() && mIns[i + 1].mType == ASMIT_INX && mIns[i + 2].mType == ASMIT_INX) + { + for (int j = xpred; j < i; j++) + mIns[j].mLive |= LIVE_CPU_REG_X; + + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + changed = true; + } + else + xoffset = 0; + xpred = i; + } + else + { + xreg = mIns[i].mAddress; + xoffset = 0; + xpred = i; + } + } + else if (i + 3 < mIns.Size() && + mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && + mIns[i + 3].mType == ASMIT_TAX) + { + if (mIns[i + 1].mAddress == xreg && mIns[i + 2].mAddress == xoffset + 1 && !(mIns[i + 3].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C))) + { + for (int j = xpred; j < i + 3; j++) + mIns[j].mLive |= LIVE_CPU_REG_X; + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + mIns[i + 3].mType = ASMIT_INX; + changed = true; + } + else + { + xreg = mIns[i + 1].mAddress; + xoffset = mIns[i + 2].mAddress; + xpred = i + 3; + i += 3; + } + } + else if (i + 3 < mIns.Size() && + mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && mIns[i + 1].mAddress == xreg && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == xoffset + 1 && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && mIns[i + 3].mAddress == xreg && + !(mIns[i + 3].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_X | LIVE_CPU_REG_C))) + { + for (int j = xpred; j < i + 3; j++) + mIns[j].mLive |= LIVE_CPU_REG_X; + mIns[i + 0].mType = ASMIT_INX; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + mIns[i + 3].mType = ASMIT_STX; + + xoffset++; + xpred = i + 1; + + changed = true; + } + else if (i + 1 < mIns.Size() && + mIns[i + 0].mType == ASMIT_INC && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && mIns[i + 0].mAddress == xreg && + mIns[i + 1].mType == ASMIT_LDX && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && mIns[i + 1].mAddress == xreg && xoffset == 0) + { + for (int j = xpred; j < i + 1; j++) + mIns[j].mLive |= LIVE_CPU_REG_X; + mIns[i + 0].mType = ASMIT_INX; mIns[i + 0].mMode = ASMIM_IMPLIED; mIns[i + 0].mLive |= LIVE_CPU_REG_X; + mIns[i + 1].mType = ASMIT_STX; + + xoffset++; + xpred = i + 1; + + changed = true; + } + else if (mIns[i].mType == ASMIT_INX) + { + xoffset = (xoffset + 1) & 255; + } + else if (mIns[i].mType == ASMIT_DEX) + { + xoffset = (xoffset - 1) & 255; + } + else if (mIns[i].ChangesXReg()) + { + xreg = -1; + } + else if (xreg >= 0 && mIns[i].ChangesZeroPage(xreg)) + { + xreg = -1; + } + } + + CheckLive(); + + if (mTrueJump && mTrueJump->ForwardZpXIndex(full)) + changed = true; + + if (mFalseJump && mFalseJump->ForwardZpXIndex(full)) + changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::ReplaceXRegWithYReg(int start, int end) +{ + bool changed = false; + + //CheckLive(); + for (int i = start; i < end; i++) + { + NativeCodeInstruction& ins(mIns[i]); + if (ins.ReplaceXRegWithYReg()) + changed = true; + } + //CheckLive(); + + return changed; +} + +bool NativeCodeBasicBlock::CanReplaceYRegWithXReg(int start, int end) +{ + for (int i = start; i < end; i++) + { + NativeCodeInstruction& ins(mIns[i]); + + if ((ins.mLive & LIVE_CPU_REG_X) && (ins.mLive & LIVE_CPU_REG_Y)) + return false; + + if (mIns[i].mType == ASMIT_LDY && mIns[i].mMode == ASMIM_ABSOLUTE_X) + return false; + + if (ins.mMode == ASMIM_INDIRECT_Y) + return false; + + if (ins.mMode == ASMIM_ABSOLUTE_Y && !HasAsmInstructionMode(ins.mType, ASMIM_ABSOLUTE_X)) + return false; + } + + return true; +} + +bool NativeCodeBasicBlock::CanReplaceXRegWithYReg(int start, int end) +{ + for (int i = start; i < end; i++) + { + NativeCodeInstruction& ins(mIns[i]); + + if ((ins.mLive & LIVE_CPU_REG_X) && (ins.mLive & LIVE_CPU_REG_Y)) + return false; + + if (mIns[i].mType == ASMIT_LDX && mIns[i].mMode == ASMIM_ABSOLUTE_Y) + return false; + + if (ins.mMode == ASMIM_INDIRECT_X) + return false; + + if (ins.mMode == ASMIM_ABSOLUTE_X && !HasAsmInstructionMode(ins.mType, ASMIM_ABSOLUTE_Y)) + return false; + + } + + return true; +} + +bool NativeCodeBasicBlock::CanGlobalSwapXY(void) +{ + if (!mVisited) + { + mVisited = true; + + if (mEntryRegX || mEntryRegY) + return false; + + for (int i = 0; i < mIns.Size(); i++) + { + NativeCodeInstruction& ins(mIns[i]); + + if (ins.mMode == ASMIM_INDIRECT_Y) + return false; + if (ins.mMode == ASMIM_INDIRECT_X) + return false; + if (ins.mMode == ASMIM_ABSOLUTE_X && (ins.mType != ASMIT_LDY && !HasAsmInstructionMode(ins.mType, ASMIM_ABSOLUTE_Y))) + return false; + if (ins.mType == ASMIT_JSR && (ins.mFlags & (NCIF_USE_CPU_REG_X | NCIF_USE_CPU_REG_Y | NCIF_USE_ZP_32_X))) + return false; + } + + if (mTrueJump && !mTrueJump->CanGlobalSwapXY()) + return false; + if (mFalseJump && !mFalseJump->CanGlobalSwapXY()) + return false; + } + + return true; +} + + + +bool NativeCodeBasicBlock::IsSimpleSubExpression(int at, NativeSimpleSubExpression& ex) +{ + ex.mIndex = at; + + if (at + 2 < mIns.Size()) + { + if (mIns[at + 0].mType == ASMIT_LDA && (mIns[at + 0].mMode == ASMIM_ZERO_PAGE || mIns[at + 0].mMode == ASMIM_ABSOLUTE) && + mIns[at + 2].mType == ASMIT_STA && mIns[at + 2].mMode == ASMIM_ZERO_PAGE && + (mIns[at + 1].mMode == ASMIM_IMMEDIATE || mIns[at + 1].mMode == ASMIM_ZERO_PAGE) && (mIns[at + 1].mType == ASMIT_AND || mIns[at + 1].mType == ASMIT_ORA || mIns[at + 1].mType == ASMIT_EOR)) + { + ex.mType = mIns[at + 1].mType; + ex.mValue = mIns[at + 1].mAddress; + ex.mMode = mIns[at + 1].mMode; + ex.mOp = &(mIns[at + 1]); + ex.mSrc = &(mIns[at + 0]); + ex.mDst = &(mIns[at + 2]); + return true; + } + } + if (at + 3 < mIns.Size()) + { + if (mIns[at + 0].mType == ASMIT_LDA && (mIns[at + 0].mMode == ASMIM_ZERO_PAGE || mIns[at + 0].mMode == ASMIM_ABSOLUTE) && + mIns[at + 3].mType == ASMIT_STA && mIns[at + 3].mMode == ASMIM_ZERO_PAGE && + (mIns[at + 2].mMode == ASMIM_IMMEDIATE || mIns[at + 2].mMode == ASMIM_ZERO_PAGE) && + (mIns[at + 1].mType == ASMIT_CLC && mIns[at + 2].mType == ASMIT_ADC || mIns[at + 1].mType == ASMIT_SEC && mIns[at + 2].mType == ASMIT_SBC) && + !(mIns[at + 2].mLive & LIVE_CPU_REG_C)) + { + ex.mType = mIns[at + 2].mType; + ex.mValue = mIns[at + 2].mAddress; + ex.mMode = mIns[at + 2].mMode; + ex.mSrc = &(mIns[at + 0]); + ex.mOp = &(mIns[at + 2]); + ex.mDst = &(mIns[at + 3]); + return true; + } + } + if (at + 3 < mIns.Size()) + { + if (mIns[at + 1].mType == ASMIT_LDA && (mIns[at + 1].mMode == ASMIM_ZERO_PAGE || mIns[at + 1].mMode == ASMIM_ABSOLUTE) && + mIns[at + 3].mType == ASMIT_STA && mIns[at + 3].mMode == ASMIM_ZERO_PAGE && + (mIns[at + 2].mMode == ASMIM_IMMEDIATE || mIns[at + 2].mMode == ASMIM_ZERO_PAGE) && + (mIns[at + 0].mType == ASMIT_CLC && mIns[at + 2].mType == ASMIT_ADC || mIns[at + 0].mType == ASMIT_SEC && mIns[at + 2].mType == ASMIT_SBC) && + !(mIns[at + 2].mLive & LIVE_CPU_REG_C)) + { + ex.mType = mIns[at + 2].mType; + ex.mValue = mIns[at + 2].mAddress; + ex.mMode = mIns[at + 2].mMode; + ex.mSrc = &(mIns[at + 1]); + ex.mOp = &(mIns[at + 2]); + ex.mDst = &(mIns[at + 3]); + return true; + } + } + if (at + 3 < mIns.Size()) + { + if (mIns[at + 0].mType == ASMIT_STA && mIns[at + 0].mMode == ASMIM_ZERO_PAGE && + mIns[at + 3].mType == ASMIT_STA && mIns[at + 3].mMode == ASMIM_ZERO_PAGE && + (mIns[at + 2].mMode == ASMIM_IMMEDIATE || mIns[at + 2].mMode == ASMIM_ZERO_PAGE) && + (mIns[at + 1].mType == ASMIT_CLC && mIns[at + 2].mType == ASMIT_ADC || mIns[at + 1].mType == ASMIT_SEC && mIns[at + 2].mType == ASMIT_SBC) && + !(mIns[at + 2].mLive & LIVE_CPU_REG_C)) + { + ex.mType = mIns[at + 2].mType; + ex.mValue = mIns[at + 2].mAddress; + ex.mMode = mIns[at + 2].mMode; + ex.mSrc = &(mIns[at + 0]); + ex.mOp = &(mIns[at + 2]); + ex.mDst = &(mIns[at + 3]); + return true; + } + } + if (at + 2 < mIns.Size()) + { + if (mIns[at + 0].mType == ASMIT_LDX && (mIns[at + 0].mMode == ASMIM_ZERO_PAGE || mIns[at + 0].mMode == ASMIM_ABSOLUTE) && + mIns[at + 2].mType == ASMIT_STX && mIns[at + 2].mMode == ASMIM_ZERO_PAGE && + mIns[at + 1].mType == ASMIT_INX) + { + ex.mType = ASMIT_ADC; + ex.mValue = 1; + ex.mMode = ASMIM_IMMEDIATE; + ex.mOp = &(mIns[at + 1]); + ex.mSrc = &(mIns[at + 0]); + ex.mDst = &(mIns[at + 2]); + return true; + } + } + if (at + 2 < mIns.Size()) + { + if (mIns[at + 0].mType == ASMIT_LDY && (mIns[at + 0].mMode == ASMIM_ZERO_PAGE || mIns[at + 0].mMode == ASMIM_ABSOLUTE) && + mIns[at + 2].mType == ASMIT_STY && mIns[at + 2].mMode == ASMIM_ZERO_PAGE && + mIns[at + 1].mType == ASMIT_INY) + { + ex.mType = ASMIT_ADC; + ex.mValue = 1; + ex.mMode = ASMIM_IMMEDIATE; + ex.mOp = &(mIns[at + 1]); + ex.mSrc = &(mIns[at + 0]); + ex.mDst = &(mIns[at + 2]); + return true; + } + } + if (at + 3 < mIns.Size()) + { + if (mIns[at + 0].mType == ASMIT_LDA && (mIns[at + 0].mMode == ASMIM_ZERO_PAGE || mIns[at + 0].mMode == ASMIM_ABSOLUTE || mIns[at + 0].mMode == ASMIM_ABSOLUTE_X) && + mIns[at + 3].mType == ASMIT_TAY && + (mIns[at + 2].mMode == ASMIM_IMMEDIATE || mIns[at + 2].mMode == ASMIM_ZERO_PAGE) && + (mIns[at + 1].mType == ASMIT_CLC && mIns[at + 2].mType == ASMIT_ADC || mIns[at + 1].mType == ASMIT_SEC && mIns[at + 2].mType == ASMIT_SBC) && + !(mIns[at + 2].mLive & LIVE_CPU_REG_C)) + { + ex.mType = mIns[at + 2].mType; + ex.mValue = mIns[at + 2].mAddress; + ex.mMode = mIns[at + 2].mMode; + ex.mSrc = &(mIns[at + 0]); + ex.mOp = &(mIns[at + 2]); + ex.mDst = &(mIns[at + 3]); + return true; + } + } + + return false; +} + +int NativeSimpleSubExpressions::FindCommon(const NativeSimpleSubExpression& ex) const +{ + if (ex.mSrc->mType == ASMIT_LDA || ex.mSrc->mType == ASMIT_LDX || ex.mSrc->mType == ASMIT_LDY) + { + for (int i = 0; i < mExps.Size(); i++) + { + if (mExps[i].mType == ex.mType && mExps[i].mMode == ex.mMode && mExps[i].mValue == ex.mValue && ex.mSrc->SameEffectiveAddress(*(mExps[i].mSrc))) + { + if (mExps[i].mDst->mMode == ASMIM_IMPLIED) + { + if (mExps[i].mDst->mType == ex.mDst->mType) + return i; + } + else + return i; + } + } + } + + return -1; +} + +bool NativeSimpleSubExpression::MayBeChangedBy(const NativeCodeInstruction& ins) +{ + if (ins.ChangesAddress()) + { + if (mSrc->MayBeChangedOnAddress(ins)) + return true; + if (&ins != mDst && mDst->MayBeChangedOnAddress(ins)) + return true; + if (mMode == ASMIM_ZERO_PAGE && ins.ChangesZeroPage(mValue)) + return true; + } + if (ins.ChangesYReg()) + { + if (&ins != mDst && mDst->mType == ASMIT_TAY) + return true; + if (mSrc->mMode == ASMIM_ABSOLUTE_Y) + return true; + } + if (ins.ChangesXReg()) + { + if (&ins != mDst && mDst->mType == ASMIT_TAX) + return true; + if (mSrc->mMode == ASMIM_ABSOLUTE_X) + return true; + } + + return false; +} + +void NativeSimpleSubExpressions::Filter(const NativeCodeInstruction& ins) +{ + if (ins.mType == ASMIT_JSR) + mExps.SetSize(0); + else + { + int i = 0; + while (i < mExps.Size()) + { + if (mExps[i].MayBeChangedBy(ins)) + mExps.Remove(i); + else + i++; + } + } +} + +bool NativeCodeBasicBlock::PropagateCommonSubExpression(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + NativeSimpleSubExpressions exps; + + for (int i = 0; i < mIns.Size(); i++) + { + NativeSimpleSubExpression ex; + if (IsSimpleSubExpression(i, ex)) + { + int j = exps.FindCommon(ex); + if (j >= 0) + { + if (ex.mDst->mType == ASMIT_TAY) + { + for (int k = exps.mExps[j].mIndex; k < i + 2; k++) + mIns[k].mLive |= LIVE_CPU_REG_Y; + ex.mOp->mLive |= LIVE_CPU_REG_Y; + ex.mDst->mType = ASMIT_NOP; + } + else if (ex.mDst->mType == ASMIT_TAX) + { + for (int k = exps.mExps[j].mIndex; k < i + 2; k++) + mIns[k].mLive |= LIVE_CPU_REG_X; + ex.mOp->mLive |= LIVE_CPU_REG_Y; + ex.mDst->mType = ASMIT_NOP; + } + else + { + *ex.mSrc = NativeCodeInstruction(ex.mDst->mIns, ex.mSrc->mType, *(exps.mExps[j].mDst)); + ex.mSrc = exps.mExps[j].mSrc; + ex.mOp->mType = ASMIT_NOP; + ex.mOp->mMode = ASMIM_IMPLIED; + } + changed = true; + } + exps.mExps.Push(ex); + } + else + exps.Filter(mIns[i]); + } + + if (mTrueJump && mTrueJump->PropagateCommonSubExpression()) + changed = true; + if (mFalseJump && mFalseJump->PropagateCommonSubExpression()) + changed = true; + } + + return changed; +} + +void NativeCodeBasicBlock::CollectAccuDominatedBlocks(ExpandingArray& dblocks) +{ + if (mPatched) + return; + + for (int i = 0; i < mEntryBlocks.Size(); i++) + if (!mEntryBlocks[i]->mPatchChecked) + return; + + mPatched = true; + dblocks.Push(this); + + if (!ChangesAccu()) + { + mPatchChecked = true; + if (mTrueJump) mTrueJump->CollectAccuDominatedBlocks(dblocks); + if (mFalseJump) mFalseJump->CollectAccuDominatedBlocks(dblocks); + } +} + +bool NativeCodeBasicBlock::PropagateCrossBlockAccuExpression(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + int sz = mIns.Size(); + if (sz >= 3 && + (mIns[sz - 3].mType == ASMIT_LDA || mIns[sz - 3].mType == ASMIT_STA) && mIns[sz - 3].mMode == ASMIM_ZERO_PAGE && + mIns[sz - 2].IsLogic() && mIns[sz - 2].mMode == ASMIM_IMMEDIATE && + !mIns[sz - 1].ChangesAccu()) + { + mProc->ResetPatched(); + mPatchChecked = true; + + ExpandingArray dblocks; + if (mTrueJump) mTrueJump->CollectAccuDominatedBlocks(dblocks); + if (mFalseJump) mFalseJump->CollectAccuDominatedBlocks(dblocks); + + for (int i = 0; i < dblocks.Size(); i++) + { + NativeCodeBasicBlock* b = dblocks[i]; + if (b->mIns.Size() >= 2 && + b->mIns[0].mType == ASMIT_LDA && b->mIns[0].mMode == ASMIM_ZERO_PAGE && b->mIns[0].mAddress == mIns[sz - 3].mAddress && + b->mIns[1].IsSame(mIns[sz - 2]) && !(b->mIns[1].mLive & LIVE_CPU_REG_Z)) + { + b->mIns[0].mType = ASMIT_NOP; b->mIns[0].mMode = ASMIM_IMPLIED; + b->mIns[1].mType = ASMIT_NOP; b->mIns[1].mMode = ASMIM_IMPLIED; + changed = true; + } + } + } + + if (mTrueJump && mTrueJump->PropagateCrossBlockAccuExpression()) + changed = true; + if (mFalseJump && mFalseJump->PropagateCrossBlockAccuExpression()) + changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::ShortSwapXY(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + for (int i = 0; i + 1 < mIns.Size(); i++) + { + if (mIns[i + 1].mMode == ASMIM_ABSOLUTE_X && !(mIns[i + 1].mLive & (LIVE_CPU_REG_X | LIVE_CPU_REG_Y)) && HasAsmInstructionMode(mIns[i + 1].mType, ASMIM_ABSOLUTE_Y)) + { + if (mIns[i + 0].mType == ASMIT_LDX && HasAsmInstructionMode(ASMIT_LDY, mIns[i + 0].mMode)) + { + mIns[i + 0].mType = ASMIT_LDY; mIns[i + 0].mLive |= LIVE_CPU_REG_Y; + mIns[i + 1].mMode = ASMIM_ABSOLUTE_Y; + changed = true; + } + else if (mIns[i + 0].mType == ASMIT_TAX) + { + mIns[i + 0].mType = ASMIT_TAY; mIns[i + 0].mLive |= LIVE_CPU_REG_Y; + mIns[i + 1].mMode = ASMIM_ABSOLUTE_Y; + changed = true; + } + } + else if (mIns[i + 1].mMode == ASMIM_ABSOLUTE_Y && !(mIns[i + 1].mLive & (LIVE_CPU_REG_X | LIVE_CPU_REG_Y)) && HasAsmInstructionMode(mIns[i + 1].mType, ASMIM_ABSOLUTE_X)) + { + if (mIns[i + 0].mType == ASMIT_LDY && HasAsmInstructionMode(ASMIT_LDX, mIns[i + 0].mMode)) + { + mIns[i + 0].mType = ASMIT_LDX; mIns[i + 0].mLive |= LIVE_CPU_REG_X; + mIns[i + 1].mMode = ASMIM_ABSOLUTE_X; + changed = true; + } + else if (mIns[i + 0].mType == ASMIT_TAY) + { + mIns[i + 0].mType = ASMIT_TAX; mIns[i + 0].mLive |= LIVE_CPU_REG_X; + mIns[i + 1].mMode = ASMIM_ABSOLUTE_X; + changed = true; + } + } + } + + if (mTrueJump && mTrueJump->ShortSwapXY()) + changed = true; + if (mFalseJump && mFalseJump->ShortSwapXY()) + changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::LocalSwapXY(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + int pre = -1; + + if (mEntryRequiredRegs[CPU_REG_X] || mEntryRequiredRegs[CPU_REG_Y]) + pre = -2; + + for (int i = 0; i < mIns.Size(); i++) + { + uint32 live = mIns[i].mLive & (LIVE_CPU_REG_X | LIVE_CPU_REG_Y); + if (live) + { + if (pre == -1) + pre = i; + } + else if (!live) + { + if (pre >= 0) + { + int k = pre; + while (k <= i && mIns[k].CanSwapXYReg()) + k++; + if (k > i) + { + for (int k = pre; k <= i; k++) + { + if (mIns[k].SwapXYReg()) + changed = true; + } + } + } + pre = -1; + } + } + + if (mTrueJump && mTrueJump->LocalSwapXY()) + changed = true; + if (mFalseJump && mFalseJump->LocalSwapXY()) + changed = true; + } + + return changed; +} + + +bool NativeCodeBasicBlock::GlobalSwapXY(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + for (int i = 0; i < mIns.Size(); i++) + { + NativeCodeInstruction& ins(mIns[i]); + + if (ins.mMode == ASMIM_ABSOLUTE_X) + { + ins.mMode = ASMIM_ABSOLUTE_Y; + changed = true; + } + else if (ins.mMode == ASMIM_ABSOLUTE_Y) + { + ins.mMode = ASMIM_ABSOLUTE_X; + changed = true; + } + + switch (ins.mType) + { + case ASMIT_LDX: + ins.mType = ASMIT_LDY; + changed = true; + break; + case ASMIT_STX: + ins.mType = ASMIT_STY; + changed = true; + break; + case ASMIT_CPX: + ins.mType = ASMIT_CPY; + changed = true; + break; + case ASMIT_INX: + ins.mType = ASMIT_INY; + changed = true; + break; + case ASMIT_DEX: + ins.mType = ASMIT_DEY; + changed = true; + break; + case ASMIT_TAX: + ins.mType = ASMIT_TAY; + changed = true; + break; + case ASMIT_TXA: + ins.mType = ASMIT_TYA; + changed = true; + break; + + case ASMIT_LDY: + ins.mType = ASMIT_LDX; + changed = true; + break; + case ASMIT_STY: + ins.mType = ASMIT_STX; + changed = true; + break; + case ASMIT_CPY: + ins.mType = ASMIT_CPX; + changed = true; + break; + case ASMIT_INY: + ins.mType = ASMIT_INX; + changed = true; + break; + case ASMIT_DEY: + ins.mType = ASMIT_DEX; + changed = true; + break; + case ASMIT_TAY: + ins.mType = ASMIT_TAX; + changed = true; + break; + case ASMIT_TYA: + ins.mType = ASMIT_TXA; + changed = true; + break; + } + } + + if (mTrueJump && !mTrueJump->GlobalSwapXY()) + changed = true; + if (mFalseJump && !mFalseJump->GlobalSwapXY()) + changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::AlternateXXUsage(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + for (int i = 0; i + 3 < mIns.Size(); i++) + { + if (mIns[i + 0].mType == ASMIT_LDX && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_ABSOLUTE_X && + mIns[i + 2].mType == ASMIT_LDX && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].mMode == ASMIM_ABSOLUTE_X && + !(mIns[i + 3].mLive & LIVE_CPU_REG_Y)) + { + mIns[i + 0].mType = ASMIT_LDY; mIns[i + 0].mLive |= LIVE_CPU_REG_Y; + mIns[i + 1].mMode = ASMIM_ABSOLUTE_Y; + changed = true; + } + else if (mIns[i + 0].mType == ASMIT_LDY && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_ABSOLUTE_Y && + mIns[i + 2].mType == ASMIT_LDY && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].mMode == ASMIM_ABSOLUTE_Y && + !(mIns[i + 3].mLive & LIVE_CPU_REG_X)) + { + mIns[i + 0].mType = ASMIT_LDX; mIns[i + 0].mLive |= LIVE_CPU_REG_X; + mIns[i + 1].mMode = ASMIM_ABSOLUTE_X; + changed = true; + } + } + + if (mTrueJump && mTrueJump->AlternateXXUsage()) + changed = true; + if (mFalseJump && mFalseJump->AlternateXXUsage()) + changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::UntangleXYUsage(bool final) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + if (final) + { + for (int i = 0; i + 2 < mIns.Size(); i++) + { + if (mIns[i + 0].mType == ASMIT_LDX && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && !(mIns[i + 0].mLive & LIVE_CPU_REG_Y) && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_ABSOLUTE_X && + mIns[i + 2].mType == ASMIT_TAX && !(mIns[i + 2].mLive & LIVE_CPU_REG_A)) + { + mIns[i + 0].mType = ASMIT_LDY; mIns[i + 0].mLive |= LIVE_CPU_REG_Y; + mIns[i + 1].mType = ASMIT_LDX; mIns[i + 1].mMode = ASMIM_ABSOLUTE_Y; mIns[i + 1].mLive |= LIVE_CPU_REG_X; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + } + } + } + + int xins = -1, yins = -1; + for (int i = 0; i + 1 < mIns.Size(); i++) + { + NativeCodeInstruction& ins(mIns[i]); + NativeCodeInstruction& nins(mIns[i + 1]); + + if (ins.mType == ASMIT_LDX) + { + if (yins >= 0 && mIns[yins].SameEffectiveAddress(ins) && nins.mMode == ASMIM_ABSOLUTE_X && HasAsmInstructionMode(nins.mType, ASMIM_ABSOLUTE_Y)) + { + if (nins.mLive & LIVE_CPU_REG_Z) + { + ins.mLive |= LIVE_CPU_REG_Y; + nins.mMode = ASMIM_ABSOLUTE_Y; + } + else + { + mIns.Insert(i, NativeCodeInstruction(nins.mIns, nins.mType, ASMIM_ABSOLUTE_Y, nins.mAddress, nins.mLinkerObject, nins.mFlags)); + mIns[i + 1].mLive |= mIns[i].mLive; + mIns.Remove(i + 2); + } + for (int j = yins; j < i; j++) + mIns[j].mLive |= LIVE_CPU_REG_Y; + changed = true; + } + else + xins = i; + } + else if (ins.mType == ASMIT_LDY) + { + if (xins >= 0 && mIns[xins].SameEffectiveAddress(ins) && nins.mMode == ASMIM_ABSOLUTE_Y && HasAsmInstructionMode(nins.mType, ASMIM_ABSOLUTE_X)) + { + if (nins.mLive & LIVE_CPU_REG_Z) + { + ins.mLive = LIVE_CPU_REG_X; + nins.mMode = ASMIM_ABSOLUTE_X; + } + else + { + mIns.Insert(i, NativeCodeInstruction(nins.mIns, nins.mType, ASMIM_ABSOLUTE_X, nins.mAddress, nins.mLinkerObject, nins.mFlags)); + mIns[i + 1].mLive |= mIns[i].mLive; + mIns.Remove(i + 2); + } + for (int j = xins; j < i; j++) + mIns[j].mLive |= LIVE_CPU_REG_X; + changed = true; + } + else + yins = i; + } + else + { + if (xins >= 0 && (mIns[xins].MayBeChangedOnAddress(ins) || ins.ChangesXReg())) + xins = -1; + if (yins >= 0 && (mIns[yins].MayBeChangedOnAddress(ins) || ins.ChangesYReg())) + yins = -1; + } + } + + if (mTrueJump && mTrueJump->UntangleXYUsage(final)) + changed = true; + if (mFalseJump && mFalseJump->UntangleXYUsage(final)) + changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::OptimizeXYPairUsage(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + CheckLive(); + + if (mExitRequiredRegs.Size() && !mExitRequiredRegs[CPU_REG_X] && !mExitRequiredRegs[CPU_REG_Y]) + { + int yreg = -1, xreg = -1, areg = -1; + for (int i = 0; i < mIns.Size(); i++) + { + NativeCodeInstruction & ins(mIns[i]); + + if (ins.mType == ASMIT_LDY && ins.mMode == ASMIM_ZERO_PAGE && ins.mAddress == xreg || + ins.mType == ASMIT_LDX && ins.mMode == ASMIM_ZERO_PAGE && ins.mAddress == yreg) + { + bool checkx = ins.mType == ASMIT_LDY && (ins.mLive & LIVE_CPU_REG_X); + bool checky = ins.mType == ASMIT_LDX && (ins.mLive & LIVE_CPU_REG_Y); + bool fail = false; + + int j = i; + while (j + 1 < mIns.Size() && (mIns[j].mLive & (LIVE_CPU_REG_X | LIVE_CPU_REG_Y)) && mIns[j + 1].CanSwapXYReg() && !fail) + { + if (checkx && (mIns[j].mType == ASMIT_INX || mIns[j].mType == ASMIT_DEX) || + checky && (mIns[j].mType == ASMIT_INY || mIns[j].mType == ASMIT_DEY)) + fail = true; + if (checkx && mIns[j].ChangesXReg()) + checkx = false; + if (checky && mIns[j].ChangesYReg()) + checky = false; + + j++; + } + if (!fail && (j + 1 == mIns.Size() || !(mIns[j].mLive & (LIVE_CPU_REG_X | LIVE_CPU_REG_Y)))) + { + bool tox = ins.mType == ASMIT_LDY, toy = ins.mType == ASMIT_LDX; + for (int k = i; k <= j; k++) + { + if (mIns[k].ChangesXReg()) + tox = false; + if (mIns[k].ChangesYReg()) + toy = false; + + if (tox) + mIns[k].ReplaceYRegWithXReg(); + else if (toy) + mIns[k].ReplaceXRegWithYReg(); + else + mIns[k].SwapXYReg(); + } + + CheckLive(); + + changed = true; + } + } + + if (ins.mType == ASMIT_LDY) + { + if (ins.mMode == ASMIM_ZERO_PAGE) + yreg = ins.mAddress; + else + yreg = -1; + } + else if (ins.mType == ASMIT_LDX) + { + if (ins.mMode == ASMIM_ZERO_PAGE) + xreg = ins.mAddress; + else + xreg = -1; + } + else if (ins.mType == ASMIT_LDA) + { + if (ins.mMode == ASMIM_ZERO_PAGE) + areg = ins.mAddress; + else + areg = -1; + } + else if (ins.mType == ASMIT_TAX) + xreg = areg; + else if (ins.mType == ASMIT_TAY) + yreg = areg; + else if (ins.mType == ASMIT_JSR) + xreg = yreg = areg = false; + else if (ins.ChangesAccu()) + areg = -1; + else if (ins.ChangesXReg()) + xreg = -1; + else if (ins.ChangesYReg()) + yreg = -1; + else if (ins.mMode == ASMIM_ZERO_PAGE && ins.ChangesAddress()) + { + if (ins.mAddress == areg) + areg = -1; + if (ins.mAddress == xreg) + xreg = -1; + if (ins.mAddress == yreg) + yreg = -1; + } + } + } + + CheckLive(); + + if (mTrueJump && mTrueJump->OptimizeXYPairUsage()) + changed = true; + + if (mFalseJump && mFalseJump->OptimizeXYPairUsage()) + changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::MoveImmediateStoreUp(int at) +{ + bool usex = mIns[at + 1].mMode == ASMIM_ABSOLUTE_X || mIns[at + 1].mMode == ASMIM_INDIRECT_X || mIns[at + 1].mMode == ASMIM_ZERO_PAGE_X; + bool usey = mIns[at + 1].mMode == ASMIM_ABSOLUTE_Y || mIns[at + 1].mMode == ASMIM_INDIRECT_Y || mIns[at + 1].mMode == ASMIM_ZERO_PAGE_Y; + bool vol = mIns[at + 1].mFlags & NCIF_VOLATILE; + + int val = mIns[at].mAddress; + int i = at; + while (i > 0) + { + i--; + if (mIns[i].mMode == ASMIM_IMMEDIATE && mIns[i].mAddress == val) + { + if (mIns[i].mType == ASMIT_LDA && (mIns[i].mLive & LIVE_CPU_REG_A)) + { + mIns.Insert(i + 1, NativeCodeInstruction(mIns[at + 1].mIns, ASMIT_STA, mIns[at + 1])); + mIns[i + 1].mLive |= LIVE_CPU_REG_A; + mIns.Remove(at + 1, 2); + return true; + } + else if (mIns[i].mType == ASMIT_LDX && HasAsmInstructionMode(ASMIT_STX, mIns[at + 1].mMode)) + { + mIns.Insert(i + 1, NativeCodeInstruction(mIns[at + 1].mIns, ASMIT_STX, mIns[at + 1])); + mIns[i + 1].mLive |= LIVE_CPU_REG_X; + mIns.Remove(at + 1, 2); + return true; + } + else if (mIns[i].mType == ASMIT_LDY && HasAsmInstructionMode(ASMIT_STY, mIns[at + 1].mMode)) + { + mIns.Insert(i + 1, NativeCodeInstruction(mIns[at + 1].mIns, ASMIT_STY, mIns[at + 1])); + mIns[i + 1].mLive |= LIVE_CPU_REG_Y; + mIns.Remove(at + 1, 2); + return true; + } + } + + if (mIns[i].MayReference(mIns[at + 1])) + return false; + else if (usex && mIns[i].ChangesXReg()) + return false; + else if (usey && mIns[i].ChangesYReg()) + return false; + else if (mIns[at + 1].mMode == ASMIM_INDIRECT_Y && (mIns[i].ChangesZeroPage(mIns[at + 1].mAddress) || mIns[i].ChangesZeroPage(mIns[at + 1].mAddress + 1))) + return false; + else if (vol && (mIns[i].mFlags & NCIF_VOLATILE)) + return false; + } + return false; +} + +bool NativeCodeBasicBlock::MoveImmediateStoreDown(int at) +{ + bool usex = mIns[at + 1].mMode == ASMIM_ABSOLUTE_X || mIns[at + 1].mMode == ASMIM_INDIRECT_X || mIns[at + 1].mMode == ASMIM_ZERO_PAGE_X; + bool usey = mIns[at + 1].mMode == ASMIM_ABSOLUTE_Y || mIns[at + 1].mMode == ASMIM_INDIRECT_Y || mIns[at + 1].mMode == ASMIM_ZERO_PAGE_Y; + bool vol = mIns[at + 1].mFlags & NCIF_VOLATILE; + + int val = mIns[at].mAddress; + int i = at + 2; + while (i < mIns.Size()) + { + if (mIns[i].mMode == ASMIM_IMMEDIATE && mIns[i].mAddress == val) + { + if (mIns[i].mType == ASMIT_LDA && (mIns[i].mLive & LIVE_CPU_REG_A)) + { + if (usex) + { + for (int j = at; j <= i; j++) + mIns[j].mLive |= LIVE_CPU_REG_X; + } + else if (usey) + { + for (int j = at; j <= i; j++) + mIns[j].mLive |= LIVE_CPU_REG_Y; + } + + mIns.Insert(i + 1, NativeCodeInstruction(mIns[at + 1].mIns, ASMIT_STA, mIns[at + 1])); + mIns[i + 1].mLive |= LIVE_CPU_REG_A; + mIns.Remove(at, 2); + return true; + } + else if (mIns[i].mType == ASMIT_LDX && HasAsmInstructionMode(ASMIT_STX, mIns[at + 1].mMode)) + { + if (usey) + { + for (int j = at; j <= i; j++) + mIns[j].mLive |= LIVE_CPU_REG_Y; + } + mIns.Insert(i + 1, NativeCodeInstruction(mIns[at + 1].mIns, ASMIT_STX, mIns[at + 1])); + mIns[i + 1].mLive |= LIVE_CPU_REG_X; + mIns.Remove(at, 2); + return true; + } + else if (mIns[i].mType == ASMIT_LDY && HasAsmInstructionMode(ASMIT_STY, mIns[at + 1].mMode)) + { + if (usex) + { + for (int j = at; j <= i; j++) + mIns[j].mLive |= LIVE_CPU_REG_X; + } + mIns.Insert(i + 1, NativeCodeInstruction(mIns[at + 1].mIns, ASMIT_STY, mIns[at + 1])); + mIns[i + 1].mLive |= LIVE_CPU_REG_Y; + mIns.Remove(at, 2); + return true; + } + } + + if (mIns[i].MayReference(mIns[at + 1])) + return false; + else if (usex && mIns[i].ChangesXReg()) + return false; + else if (usey && mIns[i].ChangesYReg()) + return false; + else if (mIns[at + 1].mMode == ASMIM_INDIRECT_Y && (mIns[i].ChangesZeroPage(mIns[at + 1].mAddress) || mIns[i].ChangesZeroPage(mIns[at + 1].mAddress + 1))) + return false; + else if (vol && (mIns[i].mFlags & NCIF_VOLATILE)) + return false; + else if (mIns[i].mFlags & NCIF_MEMMAP) + return false; + + i++; + } + return false; +} + +bool NativeCodeBasicBlock::RecycleImmediates(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + CheckLive(); + + for (int i = 0; i + 1 < mIns.Size(); i++) + { + NativeCodeInstruction& ins(mIns[i]); + if (mIns[i].mMode == ASMIM_IMMEDIATE) + { + if ((mIns[i].mType == ASMIT_LDA && mIns[i + 1].mType == ASMIT_STA && !(mIns[i + 1].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z))) || + (mIns[i].mType == ASMIT_LDX && mIns[i + 1].mType == ASMIT_STX && !(mIns[i + 1].mLive & (LIVE_CPU_REG_X | LIVE_CPU_REG_Z))) || + (mIns[i].mType == ASMIT_LDY && mIns[i + 1].mType == ASMIT_STY && !(mIns[i + 1].mLive & (LIVE_CPU_REG_Y | LIVE_CPU_REG_Z)))) + { + if (MoveImmediateStoreDown(i) || MoveImmediateStoreUp(i)) + { + CheckLive(); + changed = true; + } + } + } + } + + CheckLive(); + + if (mTrueJump && mTrueJump->RecycleImmediates()) + changed = true; + + if (mFalseJump && mFalseJump->RecycleImmediates()) + changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::MoveLoadStoreDown(int at) +{ + bool usex = + mIns[at + 0].mMode == ASMIM_ABSOLUTE_X || mIns[at + 0].mMode == ASMIM_INDIRECT_X || mIns[at + 0].mMode == ASMIM_ZERO_PAGE_X || + mIns[at + 1].mMode == ASMIM_ABSOLUTE_X || mIns[at + 1].mMode == ASMIM_INDIRECT_X || mIns[at + 1].mMode == ASMIM_ZERO_PAGE_X; + bool usey = + mIns[at + 0].mMode == ASMIM_ABSOLUTE_Y || mIns[at + 0].mMode == ASMIM_INDIRECT_Y || mIns[at + 0].mMode == ASMIM_ZERO_PAGE_Y || + mIns[at + 1].mMode == ASMIM_ABSOLUTE_Y || mIns[at + 1].mMode == ASMIM_INDIRECT_Y || mIns[at + 1].mMode == ASMIM_ZERO_PAGE_Y; + + int i = at + 2; + while (i < mIns.Size()) + { + if ((mIns[i].SameEffectiveAddress(mIns[at + 0]) || mIns[i].SameEffectiveAddress(mIns[at + 1])) && mIns[i].mType == ASMIT_LDA) + { + if (usex) + { + for (int j = at; j <= i; j++) + mIns[j].mLive |= LIVE_CPU_REG_X; + } + else if (usey) + { + for (int j = at; j <= i; j++) + mIns[j].mLive |= LIVE_CPU_REG_Y; + } + + mIns.Remove(i); + if (i != at + 2) + { + mIns.Insert(i + 0, NativeCodeInstruction(mIns[at + 0].mIns, ASMIT_LDA, mIns[at + 0])); + mIns.Insert(i + 1, NativeCodeInstruction(mIns[at + 1].mIns, ASMIT_STA, mIns[at + 1])); + mIns.Remove(at, 2); + } + else + mIns[at + 1].mLive |= LIVE_CPU_REG_A; + return true; + } + + if (mIns[i].MayReference(mIns[at + 1]) || mIns[at].MayBeChangedOnAddress(mIns[i])) + return false; + else if (usex && mIns[i].ChangesXReg()) + return false; + else if (usey && mIns[i].ChangesYReg()) + return false; + else if (mIns[at].mMode == ASMIM_INDIRECT_Y && (mIns[i].ChangesZeroPage(mIns[at].mAddress) || mIns[i].ChangesZeroPage(mIns[at].mAddress + 1))) + return false; + else if (mIns[at + 1].mMode == ASMIM_INDIRECT_Y && (mIns[i].ChangesZeroPage(mIns[at + 1].mAddress) || mIns[i].ChangesZeroPage(mIns[at + 1].mAddress + 1))) + return false; + else if (mIns[i].mFlags & NCIF_MEMMAP) + return false; + + i++; + } + return false; +} + +bool NativeCodeBasicBlock::RecycleLoadStore(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + CheckLive(); + + for (int i = 0; i + 1 < mIns.Size(); i++) + { + NativeCodeInstruction& ins(mIns[i]); + if (mIns[i].mType == ASMIT_LDA && mIns[i + 1].mType == ASMIT_STA && !(mIns[i + 1].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z)) && + !mIns[i].MayBeChangedOnAddress(mIns[i + 1]) && !(mIns[i].mFlags & NCIF_VOLATILE) && !(mIns[i + 1].mFlags & NCIF_VOLATILE)) + { + if (MoveLoadStoreDown(i)) + changed = true; + } + } + + CheckLive(); + + if (mTrueJump && mTrueJump->RecycleLoadStore()) + changed = true; + + if (mFalseJump && mFalseJump->RecycleLoadStore()) + changed = true; + } + + return changed; +} + + +void NativeCodeBasicBlock::BuildUseChangeSets(int start, int end, unsigned & used, unsigned & changed, uint32 & flags) +{ + used = 0; + changed = 0; + flags = 0; + + for (int i = start; i < end; i++) + { + flags |= mIns[i].mFlags; + + if (mIns[i].RequiresCarry() && !(changed & LIVE_CPU_REG_C)) + used |= LIVE_CPU_REG_C; + if (mIns[i].RequiresXReg() && !(changed & LIVE_CPU_REG_X)) + used |= LIVE_CPU_REG_X; + if (mIns[i].RequiresYReg() && !(changed & LIVE_CPU_REG_Y)) + used |= LIVE_CPU_REG_Y; + + if (mIns[i].ChangesCarry()) + changed |= LIVE_CPU_REG_C; + if (mIns[i].ChangesXReg()) + changed |= LIVE_CPU_REG_X; + if (mIns[i].ChangesYReg()) + changed |= LIVE_CPU_REG_Y; + } +} + +bool NativeCodeBasicBlock::CanSwapInstructions(int at) +{ + if (mIns[at].RequiresAccu() && mIns[at + 1].ChangesAccu()) + return false; + if (mIns[at].RequiresXReg() && mIns[at + 1].ChangesXReg()) + return false; + if (mIns[at].RequiresYReg() && mIns[at + 1].ChangesYReg()) + return false; + if (mIns[at].RequiresCarry() && mIns[at + 1].ChangesCarry()) + return false; + + if (mIns[at + 1].RequiresAccu() && mIns[at].ChangesAccu()) + return false; + if (mIns[at + 1].RequiresXReg() && mIns[at].ChangesXReg()) + return false; + if (mIns[at + 1].RequiresYReg() && mIns[at].ChangesYReg()) + return false; + if (mIns[at + 1].RequiresCarry() && mIns[at].ChangesCarry()) + return false; + + if ((mIns[at + 1].mLive & LIVE_CPU_REG_A) && mIns[at].ChangesAccu()) + return false; + if ((mIns[at + 1].mLive & LIVE_CPU_REG_X) && mIns[at].ChangesXReg()) + return false; + if ((mIns[at + 1].mLive & LIVE_CPU_REG_Y) && mIns[at].ChangesYReg()) + return false; + if ((mIns[at + 1].mLive & LIVE_CPU_REG_C) && mIns[at].ChangesCarry()) + return false; + if ((mIns[at + 1].mLive & LIVE_CPU_REG_Z) && mIns[at].ChangesZFlag()) + return false; + + if (mIns[at].MayBeChangedOnAddress(mIns[at + 1]) || + mIns[at + 1].MayBeChangedOnAddress(mIns[at]) || + mIns[at + 1].MayReference(mIns[at])) + return false; + + return true; +} + +bool NativeCodeBasicBlock::CanExchangeSegments(int start, int mid, int end) +{ + unsigned usedFront, changedFront, usedBack, changedBack; + uint32 flagsFront, flagsBack; + + BuildUseChangeSets(start, mid, usedFront, changedFront, flagsFront); + BuildUseChangeSets(mid, end, usedBack, changedBack, flagsBack); + + if (flagsFront & flagsBack & NCIF_VOLATILE) + return false; + if (flagsFront & NCIF_ALIASING) + { + for (int i = mid; i < end; i++) + if (mIns[i].mType == ASMIT_JSR) + return false; + } + if (flagsBack & NCIF_ALIASING) + { + for (int i = start; i < mid; i++) + if (mIns[i].mType == ASMIT_JSR) + return false; + } + + if (usedFront & changedBack) + return false; + if (usedBack & changedFront) + return false; + if (mIns[end - 1].mLive & changedFront) + return false; + + return true; +} + +bool NativeCodeBasicBlock::MoveAccuTrainDown(int end, int start) +{ + int i = end; + while (i > 0) + { + i--; + + for (int j = end + 1; j < start; j++) + { + if (mIns[j].MayBeChangedOnAddress(mIns[i], true) || mIns[i].MayBeChangedOnAddress(mIns[j], true)) + return false; + if (mIns[i].mType == ASMIT_STA && mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[j].ReferencesZeroPage(mIns[i].mAddress)) + return false; + } + + if (mIns[i].mType == ASMIT_LDA) + { + if (mIns[i].RequiresCarry() || (mIns[i].mLive & LIVE_CPU_REG_C)) + { + if (i > 0 && (mIns[i - 1].mType == ASMIT_CLC || mIns[i - 1].mType == ASMIT_SEC)) + i--; + } + + if (!CanExchangeSegments(i, end + 1, start)) + return false; + + int live = mIns[i].mLive; + if (mIns[i].RequiresXReg()) + live |= LIVE_CPU_REG_X; + if (mIns[i].RequiresYReg()) + live |= LIVE_CPU_REG_Y; + + for (int j = end + 1; j < start; j++) + mIns[j].mLive |= live; + + for (int j = end; j >= i; j--) + { + NativeCodeInstruction ins = mIns[j]; + ins.mLive |= mIns[start - 1].mLive; + mIns.Insert(start, ins); + } + + mIns.Remove(i, end - i + 1); + + return true; + } + + if (mIns[i].mType == ASMIT_JSR) + return false; + + } + + return false; +} + +bool NativeCodeBasicBlock::MoveAccuTrainsDown(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + int apos[256]; + + for(int i=0; i<256; i++) + apos[i] = -1; + + CheckLive(); + + for (int i = 0; i < mIns.Size(); i++) + { + if (mIns[i].mType == ASMIT_STA && mIns[i].mMode == ASMIM_ZERO_PAGE && !(mIns[i].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C))) + { + apos[mIns[i].mAddress] = i; + } + else if (i + 1 < mIns.Size() && mIns[i].mType == ASMIT_LDA && mIns[i + 1].IsCommutative() && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && apos[mIns[i + 1].mAddress] >= 0 && HasAsmInstructionMode(mIns[i + 1].mType, mIns[i].mMode)) + { + int addr = mIns[i + 1].mAddress; + + CheckLive(); + if (MoveAccuTrainDown(apos[addr], i)) + { + if (mIns[i].RequiresXReg()) + mIns[i].mLive |= LIVE_CPU_REG_X; + if (mIns[i].RequiresYReg()) + mIns[i].mLive |= LIVE_CPU_REG_Y; + + mIns[i + 1].CopyMode(mIns[i]); + mIns[i] = NativeCodeInstruction(mIns[i].mIns, ASMIT_LDA, ASMIM_ZERO_PAGE, addr); + changed = true; + CheckLive(); + + for (int j = 0; j < 256; j++) + apos[j] = -1; + } + } + else if (mIns[i].mType == ASMIT_LDA && mIns[i].mMode == ASMIM_ZERO_PAGE && apos[mIns[i].mAddress] >= 0) + { + int addr = mIns[i].mAddress; + + CheckLive(); + if (MoveAccuTrainDown(apos[addr], i)) + { + if (mIns[i].RequiresXReg()) + mIns[i].mLive |= LIVE_CPU_REG_X; + if (mIns[i].RequiresYReg()) + mIns[i].mLive |= LIVE_CPU_REG_Y; + + changed = true; + CheckLive(); + + for (int j = 0; j < 256; j++) + apos[j] = -1; + } + else + apos[mIns[i].mAddress] = -1; + } + else if (i + 1 < mIns.Size() && mIns[i].mType == ASMIT_CLC && mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && apos[mIns[i + 1].mAddress] >= 0) + { + int addr = mIns[i + 1].mAddress; + + CheckLive(); + if (MoveAccuTrainDown(apos[addr], i)) + { + if (mIns[i + 1].RequiresXReg()) + mIns[i + 1].mLive |= LIVE_CPU_REG_X; + if (mIns[i + 1].RequiresYReg()) + mIns[i + 1].mLive |= LIVE_CPU_REG_Y; + + changed = true; + CheckLive(); + + for (int j = 0; j < 256; j++) + apos[j] = -1; + } + else + apos[mIns[i + 1].mAddress] = -1; + } + else if (i + 2 < mIns.Size() && mIns[i].mType == ASMIT_LDA && mIns[i + 1].mType == ASMIT_CLC && + mIns[i + 2].IsCommutative() && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && apos[mIns[i + 2].mAddress] >= 0 && HasAsmInstructionMode(mIns[i + 2].mType, mIns[i].mMode)) + { + int addr = mIns[i + 2].mAddress; + + CheckLive(); + if (MoveAccuTrainDown(apos[addr], i)) + { + if (mIns[i].RequiresXReg()) + { + mIns[i].mLive |= LIVE_CPU_REG_X; + mIns[i + 1].mLive |= LIVE_CPU_REG_X; + } + if (mIns[i].RequiresYReg()) + { + mIns[i].mLive |= LIVE_CPU_REG_Y; + mIns[i + 1].mLive |= LIVE_CPU_REG_Y; + } + + mIns[i + 2].CopyMode(mIns[i]); + mIns[i] = NativeCodeInstruction(mIns[i].mIns, ASMIT_LDA, ASMIM_ZERO_PAGE, addr); + changed = true; + CheckLive(); + + for (int j = 0; j < 256; j++) + apos[j] = -1; + + } + } + else if (mIns[i].mMode == ASMIM_ZERO_PAGE) + { + apos[mIns[i].mAddress] = -1; + } + else if (mIns[i].mMode == ASMIM_INDIRECT_Y) + { + apos[mIns[i].mAddress] = -1; + apos[mIns[i].mAddress + 1] = -1; + } + + if (mIns[i].mType == ASMIT_JSR) + { + for (int j = 0; j < 256; j++) + { + if (apos[j] >= 0 && mIns[i].ReferencesZeroPage(j)) + apos[j] = -1; + } + } + } + + CheckLive(); + + if (mTrueJump && mTrueJump->MoveAccuTrainsDown()) + changed = true; + if (mFalseJump && mFalseJump->MoveAccuTrainsDown()) + changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::MoveAccuTrainUp(int at, int end) +{ + CheckLive(); + + bool needXY = (mIns[end - 1].mLive & (LIVE_CPU_REG_X | LIVE_CPU_REG_Y)) != 0; + + int addr = mIns[at].mAddress; + if (mIns[at].mLive & LIVE_CPU_REG_C) + at--; + + int i = at; + while (i > 0) + { + i--; + if ((mIns[i].mType == ASMIT_STA || mIns[i].mType == ASMIT_LDA) && mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i].mAddress == addr) + { + if (mIns[i].mType == ASMIT_LDA) + i++; + + if (mIns[i].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C | LIVE_CPU_REG_Z)) + return false; + + int live = mIns[i].mLive; + + for (int j = i; j < at; j++) + mIns[j].mLive |= mIns[end - 1].mLive; + + for (int j = at; j < end; j++) + { + NativeCodeInstruction ins(mIns[j]); + ins.mLive |= live; + + mIns.Remove(j); + i++; + mIns.Insert(i, ins); + } + + CheckLive(); + + return true; + } + + if (mIns[i].mType == ASMIT_JSR) + { + if (needXY) + return false; + + } + + for (int j = at; j < end; j++) + { + if (mIns[j].RequiresXReg() && mIns[i].ChangesXReg() || mIns[j].ChangesXReg() && mIns[i].RequiresXReg()) + return false; + if (mIns[j].RequiresYReg() && mIns[i].ChangesYReg() || mIns[j].ChangesYReg() && mIns[i].RequiresYReg()) + return false; + if (mIns[j].MayBeChangedOnAddress(mIns[i], true) || mIns[i].MayBeChangedOnAddress(mIns[j], true)) + return false; + if (mIns[i].mType == ASMIT_JSR && mIns[j].mMode == ASMIM_ZERO_PAGE && mIns[j].ChangesAddress() && mIns[i].ReferencesZeroPage(mIns[j].mAddress)) + return false; + } + } + + return false; +} + +bool NativeCodeBasicBlock::MoveAccuTrainsUp(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + FastNumberSet wzero(256); + + int i = 0; + while (i < mIns.Size()) + { + if (mIns[i].mType == ASMIT_STA && mIns[i].mMode == ASMIM_ZERO_PAGE) + { + if (mIns[i].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C | LIVE_CPU_REG_Z)) + wzero -= mIns[i].mAddress; + else + wzero += mIns[i].mAddress; + i++; + } + else if (mIns[i].mType == ASMIT_LDA && mIns[i].mMode == ASMIM_ZERO_PAGE && wzero[mIns[i].mAddress] && + (!(mIns[i].mLive & LIVE_CPU_REG_C) || i > 0 && (mIns[i-1].mType == ASMIT_CLC || mIns[i-1].mType == ASMIT_SEC))) + { + int j = i; + while (j < mIns.Size() && (mIns[j].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C | LIVE_CPU_REG_Z)) && mIns[j].mType != ASMIT_JSR) + j++; + + if (j < mIns.Size() && mIns[j].mType != ASMIT_JSR) + { + j++; + if (MoveAccuTrainUp(i, j)) + { + i = j + 1; + changed = true; + } + else + i++; + } + else + i++; + } + else if (mIns[i].mType == ASMIT_LDA && mIns[i].mMode == ASMIM_ZERO_PAGE && i + 1 < mIns.Size() && mIns[i + 1].mType == ASMIT_STA && !(mIns[i + 1].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C | LIVE_CPU_REG_Z))) + { + wzero += mIns[i].mAddress; + i++; + } + else if (mIns[i].mType == ASMIT_JSR) + { + for (int j = 0; j < 256; j++) + { + if (wzero[j] && mIns[i].ChangesZeroPage(j)) + wzero -= j; + } +#if 0 + for (int j = 0; j < 4; j++) + { + wzero -= BC_REG_ACCU + j; + wzero -= BC_REG_WORK + j; + } + + if (!(mIns[i].mFlags & NCIF_RUNTIME) || (mIns[i].mFlags & NCIF_FEXEC)) + { + wzero -= BC_REG_ADDR; + wzero -= BC_REG_ADDR + 1; + } + +// wzero.Clear(); +#endif + i++; + } + else if (mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i].ChangesAddress()) + { + wzero -= mIns[i].mAddress; + i++; + } + else + i++; + } + + CheckLive(); + + if (mTrueJump && mTrueJump->MoveAccuTrainsUp()) + changed = true; + if (mFalseJump && mFalseJump->MoveAccuTrainsUp()) + changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::AlternateXYUsage(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + int start = 0; + + CheckLive(); + + int predYPos = -1, predXPos = -1, currYPos = -1, currXPos = -1, predYEnd = -1, predXEnd = -1; + + for (int start = 0; start < mIns.Size(); start++) + { + const NativeCodeInstruction& ins(mIns[start]); + + if (ins.mType == ASMIT_LDY) + { + if ((ins.mMode == ASMIM_ZERO_PAGE || ins.mMode == ASMIM_ABSOLUTE) && predYPos >= 0 && ins.SameEffectiveAddress(mIns[predYPos]) && !(ins.mFlags & NCIF_VOLATILE)) + { + if (CanReplaceYRegWithXReg(predYEnd, start)) + { + if (ReplaceYRegWithXReg(predYEnd, start)) + changed = true; + } + } + else + { + if (currYPos >= 0) + { + predYPos = currYPos; + predYEnd = start; + } + + if (ins.mMode == ASMIM_ZERO_PAGE || ins.mMode == ASMIM_ABSOLUTE) + currYPos = start; + else + currYPos = -1; + } + } + else if (ins.mType == ASMIT_LDX) + { + if ((ins.mMode == ASMIM_ZERO_PAGE || ins.mMode == ASMIM_ABSOLUTE) && predXPos >= 0 && ins.SameEffectiveAddress(mIns[predXPos]) && !(ins.mFlags & NCIF_VOLATILE)) + { + if (CanReplaceXRegWithYReg(predXEnd, start)) + { + if (ReplaceXRegWithYReg(predXEnd, start)) + changed = true; + } + } + else + { + if (currXPos >= 0) + { + predXPos = currXPos; + predXEnd = start; + } + + if (ins.mMode == ASMIM_ZERO_PAGE || ins.mMode == ASMIM_ABSOLUTE) + currXPos = start; + else + currXPos = -1; + } + } + else if (ins.mType == ASMIT_JSR) + { + currYPos = predYPos = -1; + currXPos = predXPos = -1; + } + else if (ins.ChangesXReg()) + { + currXPos = -1; + } + else if (ins.ChangesYReg()) + { + currYPos = -1; + } + else + { + if (predYPos >= 0 && mIns[predYPos].MayBeChangedOnAddress(ins)) + predYPos = -1; + if (predXPos >= 0 && mIns[predXPos].MayBeChangedOnAddress(ins)) + predXPos = -1; + if (currYPos >= 0 && mIns[currYPos].MayBeChangedOnAddress(ins)) + currYPos = -1; + if (currXPos >= 0 && mIns[currXPos].MayBeChangedOnAddress(ins)) + currYPos = -1; + } + } + + CheckLive(); + + if (mTrueJump && mTrueJump->AlternateXYUsage()) + changed = true; + + if (mFalseJump && mFalseJump->AlternateXYUsage()) + changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::SimplifyLoopEnd(NativeCodeProcedure* proc) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + if (mTrueJump && !mFalseJump && mTrueJump->mLoopHead) + { + if (mTrueJump->mIns.Size() == 1 && mTrueJump->mFalseJump && mTrueJump->mTrueJump != mTrueJump) + { + mIns.Push(mTrueJump->mIns[0]); + mBranch = mTrueJump->mBranch; + + if (mTrueJump->mExitRequiredRegs[CPU_REG_A]) + mExitRequiredRegs += CPU_REG_A; + if (mTrueJump->mExitRequiredRegs[CPU_REG_X]) + mExitRequiredRegs += CPU_REG_X; + if (mTrueJump->mExitRequiredRegs[CPU_REG_Y]) + mExitRequiredRegs += CPU_REG_Y; + + mTrueJump->RemEntryBlock(this); + + mFalseJump = mTrueJump->mFalseJump; + mTrueJump = mTrueJump->mTrueJump; + + mTrueJump->AddEntryBlock(this); + mFalseJump->AddEntryBlock(this); + + changed = true; + } + } + + if (mTrueJump && mTrueJump->SimplifyLoopEnd(proc)) + changed = true; + if (mFalseJump && mFalseJump->SimplifyLoopEnd(proc)) + changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::CanHoistStore(const NativeCodeInstruction& ains) const +{ + for (int i = 0; i < mIns.Size(); i++) + { + if (ains.MayBeSameAddress(mIns[i])) + return false; + + if (ains.mType == ASMIT_STY && mIns[i].ChangesYReg() || + ains.mType == ASMIT_STX && mIns[i].ChangesXReg()) + return false; + } + + return true; +} + +bool NativeCodeBasicBlock::CanBytepassLoad(const NativeCodeInstruction& ains, int from) const +{ + for (int i = from; i < mIns.Size(); i++) + { + if (ains.MayBeChangedOnAddress(mIns[i])) + return false; + + if (ains.mType == ASMIT_LDY && mIns[i].ReferencesYReg() || + ains.mType == ASMIT_LDX && mIns[i].ReferencesXReg()) + return false; + } + + return true; +} + +bool NativeCodeBasicBlock::JoinDiamondArithmetic(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + NativeCodeBasicBlock* ezero = nullptr, * enzero = nullptr; + if (mEntryBlocks.Size() == 2 && !mEntryBlocks[0]->mFalseJump && !mEntryBlocks[1]->mFalseJump) + { + if (mEntryBlocks[0]->mIns.Size() > 0 && mEntryBlocks[0]->mIns.Last().mType == ASMIT_LDA && mEntryBlocks[0]->mIns.Last().mMode == ASMIM_IMMEDIATE && mEntryBlocks[0]->mIns.Last().mAddress == 0) + { + ezero = mEntryBlocks[0]; + enzero = mEntryBlocks[1]; + } + else if (mEntryBlocks[1]->mIns.Size() > 0 && mEntryBlocks[1]->mIns.Last().mType == ASMIT_LDA && mEntryBlocks[1]->mIns.Last().mMode == ASMIM_IMMEDIATE && mEntryBlocks[1]->mIns.Last().mAddress == 0) + { + ezero = mEntryBlocks[1]; + enzero = mEntryBlocks[0]; + } + + if (ezero) + { + if (mIns.Size() > 2 && mIns[0].mType == ASMIT_CLC && mIns[1].mType == ASMIT_ADC && (mIns[1].mMode == ASMIM_ZERO_PAGE || mIns[1].mMode == ASMIM_IMMEDIATE || mIns[1].mMode == ASMIM_ABSOLUTE)) + { + ezero->mIns[ezero->mIns.Size() - 1].CopyMode(mIns[1]); + ezero->mIns.Push(mIns[0]); + ezero->mExitRequiredRegs += CPU_REG_C; + enzero->mIns.Push(mIns[0]); + enzero->mIns.Push(mIns[1]); + enzero->mExitRequiredRegs += CPU_REG_C; + mIns.Remove(0, 2); + changed = true; + } + } + } + + if (mTrueJump && mTrueJump->JoinDiamondArithmetic()) changed = true; + if (mFalseJump && mFalseJump->JoinDiamondArithmetic()) changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::SimplifyDiamond(NativeCodeProcedure* proc) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + if (mTrueJump && mFalseJump && mTrueJump->mEntryRequiredRegs.Size() && mFalseJump->mEntryRequiredRegs.Size()) + { + if (!mTrueJump->mFalseJump && !mFalseJump->mFalseJump && mTrueJump->mTrueJump && mTrueJump->mTrueJump == mFalseJump->mTrueJump && mTrueJump->mNumEntries == 1 && mFalseJump->mNumEntries == 1 && mTrueJump->mTrueJump->mNumEntries == 2) + { + NativeCodeBasicBlock* mblock = mTrueJump->mTrueJump; + + if (mTrueJump->mIns.Size() == 1 && mFalseJump->mIns.Size() == 1 && + mTrueJump->mIns[0].mMode == ASMIM_IMMEDIATE && mFalseJump->mIns[0].mMode == ASMIM_IMMEDIATE) + { + if (mTrueJump->mIns[0].mType == ASMIT_LDA && mFalseJump->mIns[0].mType == ASMIT_AND && !(mTrueJump->mIns[0].mAddress & ~mFalseJump->mIns[0].mAddress)) + { + mTrueJump->mTrueJump->mIns.Insert(0, mFalseJump->mIns[0]); + mFalseJump->mIns.SetSize(0, true); + changed = true; + } + else if (mTrueJump->mIns[0].mType == ASMIT_AND && mFalseJump->mIns[0].mType == ASMIT_LDA && !(mFalseJump->mIns[0].mAddress & ~mTrueJump->mIns[0].mAddress)) + { + mTrueJump->mTrueJump->mIns.Insert(0, mTrueJump->mIns[0]); + mTrueJump->mIns.SetSize(0, true); + changed = true; + } + } + + if (mTrueJump->mEntryRequiredRegs[CPU_REG_Y] && mFalseJump->mEntryRequiredRegs[CPU_REG_Y] && mTrueJump->mTrueJump->mEntryRequiredRegs[CPU_REG_Y]) + { + int sz = mIns.Size() - 1; + while (sz >= 0 && !mIns[sz].ReferencesYReg()) + sz--; + if (sz >= 0 && mIns[sz].mType == ASMIT_LDY && (mIns[sz].mMode == ASMIM_ZERO_PAGE || mIns[sz].mMode == ASMIM_ABSOLUTE) && !(mIns[sz].mLive & LIVE_CPU_REG_Z)) + { + if (this->CanBytepassLoad(mIns[sz], sz + 1) && mTrueJump->CanBytepassLoad(mIns[sz]) && mFalseJump->CanBytepassLoad(mIns[sz])) + { + if (mIns[sz].mMode == ASMIM_ZERO_PAGE) + { + mExitRequiredRegs += mIns[sz].mAddress; + mTrueJump->mTrueJump->mEntryRequiredRegs += mIns[sz].mAddress; + mTrueJump->mEntryRequiredRegs += mIns[sz].mAddress; + mTrueJump->mExitRequiredRegs += mIns[sz].mAddress; + mFalseJump->mEntryRequiredRegs += mIns[sz].mAddress; + mFalseJump->mExitRequiredRegs += mIns[sz].mAddress; + } + mIns[sz].mLive |= LIVE_MEM; + + mTrueJump->mTrueJump->mIns.Insert(0, NativeCodeInstruction(mIns[sz].mIns, ASMIT_LDY, mIns[sz])); + changed = true; + } + } + } + + if (mTrueJump->mEntryRequiredRegs[CPU_REG_X] && mFalseJump->mEntryRequiredRegs[CPU_REG_X] && mTrueJump->mTrueJump->mEntryRequiredRegs[CPU_REG_X]) + { + int sz = mIns.Size() - 1; + while (sz >= 0 && !mIns[sz].ReferencesXReg()) + sz--; + if (sz >= 0 && mIns[sz].mType == ASMIT_LDX && (mIns[sz].mMode == ASMIM_ZERO_PAGE || mIns[sz].mMode == ASMIM_ABSOLUTE) && !(mIns[sz].mLive & LIVE_CPU_REG_Z)) + { + if (this->CanBytepassLoad(mIns[sz], sz + 1) && mTrueJump->CanBytepassLoad(mIns[sz]) && mFalseJump->CanBytepassLoad(mIns[sz])) + { + if (mIns[sz].mMode == ASMIM_ZERO_PAGE) + { + mExitRequiredRegs += mIns[sz].mAddress; + mTrueJump->mTrueJump->mEntryRequiredRegs += mIns[sz].mAddress; + mTrueJump->mEntryRequiredRegs += mIns[sz].mAddress; + mTrueJump->mExitRequiredRegs += mIns[sz].mAddress; + mFalseJump->mEntryRequiredRegs += mIns[sz].mAddress; + mFalseJump->mExitRequiredRegs += mIns[sz].mAddress; + } + mIns[sz].mLive |= LIVE_MEM; + + mTrueJump->mTrueJump->mIns.Insert(0, NativeCodeInstruction(mIns[sz].mIns, ASMIT_LDX, mIns[sz])); + changed = true; + } + } + } + + if (mblock->mIns.Size() > 0 && mblock->mIns[0].mType == ASMIT_STX && (mblock->mIns[0].mMode == ASMIM_ZERO_PAGE || mblock->mIns[0].mMode == ASMIM_ABSOLUTE) && !(mblock->mIns[0].mLive & LIVE_CPU_REG_X) && + mTrueJump->CanHoistStore(mblock->mIns[0]) && mFalseJump->CanHoistStore(mblock->mIns[0])) + { + if (mblock->mIns[0].mMode == ASMIM_ZERO_PAGE) + { + mExitRequiredRegs += mblock->mIns[0].mAddress; + mTrueJump->mEntryRequiredRegs += mblock->mIns[0].mAddress; + mTrueJump->mExitRequiredRegs += mblock->mIns[0].mAddress; + mFalseJump->mEntryRequiredRegs += mblock->mIns[0].mAddress; + mFalseJump->mExitRequiredRegs += mblock->mIns[0].mAddress; + mblock->mEntryRequiredRegs += mblock->mIns[0].mAddress; + } + + mIns.Push(mblock->mIns[0]); + mblock->mIns.Remove(0); + mIns[mIns.Size() - 1].mLive |= LIVE_CPU_REG_X | LIVE_CPU_REG_C | LIVE_CPU_REG_Z; + changed = true; + } + } +#if 1 + if (mTrueJump->mEntryRequiredRegs[CPU_REG_Y] && !mFalseJump->mEntryRequiredRegs[CPU_REG_Y] && !mTrueJump->mEntryRequiredRegs[CPU_REG_Z] && mTrueJump->mNumEntries == 1) + { + int sz = mIns.Size() - 1; + while (sz >= 0 && !mIns[sz].ReferencesYReg()) + sz--; + if (sz >= 0 && mIns[sz].mType == ASMIT_LDY && (mIns[sz].mMode == ASMIM_ZERO_PAGE || mIns[sz].mMode == ASMIM_ABSOLUTE) && !(mIns[sz].mLive & LIVE_CPU_REG_Z)) + { + int i = sz + 1; + while (i < mIns.Size() && !mIns[sz].MayBeChangedOnAddress(mIns[i])) + i++; + if (i == mIns.Size()) + { + mIns[sz].mLive |= mIns[mIns.Size() - 1].mLive; + if (mIns[sz].mMode == ASMIM_ZERO_PAGE) + { + mExitRequiredRegs += mIns[sz].mAddress; + mTrueJump->mEntryRequiredRegs += mIns[sz].mAddress; + } + mTrueJump->mIns.Insert(0, mIns[sz]); + mIns.Remove(sz); + changed = true; + } + } + } + if (mTrueJump->mEntryRequiredRegs[CPU_REG_X] && !mFalseJump->mEntryRequiredRegs[CPU_REG_X] && !mTrueJump->mEntryRequiredRegs[CPU_REG_Z] && mTrueJump->mNumEntries == 1) + { + int sz = mIns.Size() - 1; + while (sz >= 0 && !mIns[sz].ReferencesXReg()) + sz--; + if (sz >= 0 && mIns[sz].mType == ASMIT_LDX && (mIns[sz].mMode == ASMIM_ZERO_PAGE || mIns[sz].mMode == ASMIM_ABSOLUTE) && !(mIns[sz].mLive & LIVE_CPU_REG_Z)) + { + int i = sz + 1; + while (i < mIns.Size() && !mIns[sz].MayBeChangedOnAddress(mIns[i])) + i++; + if (i == mIns.Size()) + { + mIns[sz].mLive |= mIns[mIns.Size() - 1].mLive; + if (mIns[sz].mMode == ASMIM_ZERO_PAGE) + { + mExitRequiredRegs += mIns[sz].mAddress; + mTrueJump->mEntryRequiredRegs += mIns[sz].mAddress; + } + mTrueJump->mIns.Insert(0, mIns[sz]); + mIns.Remove(sz); + changed = true; + } + } + } + + if (mIns.Size() >= 3) + { + int sz = mIns.Size(); + if (mIns[sz - 3].mType == ASMIT_TAX && + mIns[sz - 2].mType == ASMIT_LDA && HasAsmInstructionMode(ASMIT_LDX, mIns[sz - 2].mMode) && + mIns[sz - 1].mType == ASMIT_CMP && HasAsmInstructionMode(ASMIT_CPX, mIns[sz - 1].mMode) && + !(mIns[sz - 1].mLive & LIVE_CPU_REG_A)) + { + if ((mTrueJump->mNumEntries == 1 && mTrueJump->mIns.Size() > 0 && mTrueJump->mIns[0].mType == ASMIT_TXA && !(mTrueJump->mIns[0].mLive & LIVE_CPU_REG_X) || !(mTrueJump->mEntryRequiredRegs[CPU_REG_X])) && + (mFalseJump->mNumEntries == 1 && mFalseJump->mIns.Size() > 0 && mFalseJump->mIns[0].mType == ASMIT_TXA && !(mFalseJump->mIns[0].mLive & LIVE_CPU_REG_X) || !(mFalseJump->mEntryRequiredRegs[CPU_REG_X]))) + { + mIns[sz - 3].mType = ASMIT_NOP; mIns[sz - 3].mMode = ASMIM_IMPLIED; + mIns[sz - 2].mType = ASMIT_LDX; + mIns[sz - 1].mType = ASMIT_CPX; mIns[sz - 1].mLive |= LIVE_CPU_REG_A; + mExitRequiredRegs += CPU_REG_A; + + if (mTrueJump->mIns.Size() > 0 && mTrueJump->mIns[0].mType == ASMIT_TXA) + { + mTrueJump->mIns[0].mType = ASMIT_ORA; + mTrueJump->mIns[0].mMode = ASMIM_IMMEDIATE; + mTrueJump->mIns[0].mAddress = 0; + + mTrueJump->mEntryRequiredRegs += CPU_REG_A; + } + if (mFalseJump->mIns.Size() > 0 && mFalseJump->mIns[0].mType == ASMIT_TXA) + { + mFalseJump->mIns[0].mType = ASMIT_ORA; + mFalseJump->mIns[0].mMode = ASMIM_IMMEDIATE; + mFalseJump->mIns[0].mAddress = 0; + + mFalseJump->mEntryRequiredRegs += CPU_REG_A; + } + + changed = true; + } + } + + } + + if (mBranch == ASMIT_BCC || mBranch == ASMIT_BCS) + { + NativeCodeBasicBlock* eblock = nullptr, * cblock = nullptr; + if (!mTrueJump->mFalseJump && mTrueJump->mTrueJump == mFalseJump && mTrueJump->mNumEntries == 1 && mFalseJump->mNumEntries == 2) + { + cblock = mTrueJump; + eblock = mFalseJump; + } + if (!mFalseJump->mFalseJump && mFalseJump->mTrueJump == mTrueJump && mFalseJump->mNumEntries == 1 && mTrueJump->mNumEntries == 2) + { + cblock = mFalseJump; + eblock = mTrueJump; + } + + if (cblock && cblock->mIns.Size() > 0 && eblock->mIns.Size() > 0) + { + int cs = cblock->mIns.Size(); + + if (cblock->mIns[0].mType == ASMIT_LDA && eblock->mIns[0].mType == ASMIT_LDA && cblock->mIns[cs - 1].mType == ASMIT_STA && + cblock->mIns[0].SameEffectiveAddress(eblock->mIns[0]) && + cblock->mIns[0].SameEffectiveAddress(cblock->mIns[cs - 1])) + { + cblock->mIns[cs - 1].mLive |= LIVE_CPU_REG_A; + eblock->mIns[0].mLive |= cblock->mIns[0].mLive; + eblock->mIns[0].mLive |= LIVE_CPU_REG_C; + mIns.Push(eblock->mIns[0]); + eblock->mIns.Remove(0); + cblock->mIns.Remove(0); + + eblock->mEntryRequiredRegs += CPU_REG_A; + cblock->mEntryRequiredRegs += CPU_REG_A; + mExitRequiredRegs += CPU_REG_A; + cblock->mExitRequiredRegs += CPU_REG_A; + + changed = true; + } + else if (cs == 3 && + !cblock->mEntryRequiredRegs[CPU_REG_A] && !eblock->mEntryRequiredRegs[CPU_REG_A] && + cblock->mIns[0].mType == ASMIT_TYA && + cblock->mIns[1].IsLogic() && cblock->mIns[1].mMode == ASMIM_IMMEDIATE && + cblock->mIns[2].mType == ASMIT_TAY && !(cblock->mIns[2].mLive & LIVE_CPU_REG_A)) + { + int k = mIns.Size() - 1; + while (k >= 2 && !mIns[k].ReferencesYReg()) + k--; + if (k >= 2) + { + if (mIns[k - 2].mType == ASMIT_TYA && + mIns[k - 1].IsLogic() && mIns[k - 1].mMode == ASMIM_IMMEDIATE && + mIns[k - 0].mType == ASMIT_TAY && !(mIns[k].mLive & LIVE_CPU_REG_A)) + { + mIns[k - 2].mLive |= mIns.Last().mLive; + mIns[k - 1].mLive |= mIns.Last().mLive; + mIns[k - 0].mLive |= mIns.Last().mLive; + + mIns.Push(mIns[k - 2]); + mIns.Push(mIns[k - 1]); + + cblock->mIns.Remove(0); + cblock->mIns.Remove(1); + + eblock->mIns.Insert(0, mIns[k]); + + mIns.Remove(k - 2, 3); + + eblock->mEntryRequiredRegs += CPU_REG_Y; + cblock->mEntryRequiredRegs += CPU_REG_Y; + eblock->mEntryRequiredRegs += CPU_REG_A; + cblock->mEntryRequiredRegs += CPU_REG_A; + mExitRequiredRegs += CPU_REG_Y; + mExitRequiredRegs += CPU_REG_A; + cblock->mExitRequiredRegs += CPU_REG_Y; + + changed = true; + } + } + } + } + } + } +#endif +#if 0 + if (mTrueJump && mFalseJump && !mTrueJump->mFalseJump && mTrueJump->mTrueJump == mFalseJump && mTrueJump->mEntryBlocks.Size() == 1) + { + int sz = mIns.Size(), tsz = mTrueJump->mIns.Size(); + if (sz > 0 && mIns[sz - 1].mType == ASMIT_ORA && mIns[sz - 1].mMode == ASMIM_IMMEDIATE && mIns[sz - 1].mAddress == 0) + sz--; + + if (sz > 0 && tsz > 0) + { + if (mIns[sz - 1].mType == ASMIT_STA && mIns[sz - 1].mMode == ASMIM_ZERO_PAGE && mTrueJump->mIns[tsz - 1].IsSame(mIns[sz - 1])) + { + if (!mTrueJump->ReferencesZeroPage(mIns[sz - 1].mAddress, 0, tsz - 1)) + { + printf("DoopsieT %d\n", mFalseJump->mEntryBlocks.Size()); + } + } + } + } +#endif + if (mTrueJump && mFalseJump && !mFalseJump->mFalseJump && mFalseJump->mTrueJump == mTrueJump && mFalseJump->mEntryBlocks.Size() == 1) + { + int sz = mIns.Size(), tsz = mFalseJump->mIns.Size(); + if (sz > 0 && mIns[sz - 1].mType == ASMIT_ORA && mIns[sz - 1].mMode == ASMIM_IMMEDIATE && mIns[sz - 1].mAddress == 0) + sz--; + + if (sz > 0 && tsz > 0) + { + if (mIns[sz - 1].mType == ASMIT_STA && mIns[sz - 1].mMode == ASMIM_ZERO_PAGE && mFalseJump->mIns[tsz - 1].IsSame(mIns[sz - 1])) + { + if (!mFalseJump->ReferencesZeroPage(mIns[sz - 1].mAddress, 0, tsz - 1)) + { + if (mTrueJump->mEntryBlocks.Size() <= 3 || mTrueJump->mLoopHead) + { + if (mTrueJump->mEntryBlocks.Size() > 2) + { + NativeCodeBasicBlock* pblock = proc->AllocateBlock(); + pblock->mEntryRequiredRegs = mTrueJump->mEntryRequiredRegs; + pblock->mExitRequiredRegs = mTrueJump->mEntryRequiredRegs; + pblock->mTrueJump = mTrueJump; + mTrueJump->mEntryBlocks.RemoveAll(this); + mTrueJump->mEntryBlocks.RemoveAll(mFalseJump); + mTrueJump->mNumEntries -= 2; + pblock->mEntryBlocks.Push(this); + pblock->mEntryBlocks.Push(mFalseJump); + pblock->mNumEntries = 2; + mTrueJump = pblock; + mFalseJump->mTrueJump = pblock; + } + if (sz < mIns.Size()) + mIns[sz - 1].mLive |= LIVE_CPU_REG_A; + mTrueJump->mIns.Push(mIns[sz - 1]); + mIns.Remove(sz - 1); + mFalseJump->mIns.Remove(tsz - 1); + mTrueJump->mEntryRequiredRegs += CPU_REG_A; + mExitRequiredRegs += CPU_REG_A; + mFalseJump->mExitRequiredRegs += CPU_REG_A; + changed = true; + } + } + } + } + } + + if (mTrueJump && mTrueJump->SimplifyDiamond(proc)) + changed = true; + if (mFalseJump && mFalseJump->SimplifyDiamond(proc)) + changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::CompressSwitchCascade(int lower, int upper, int cmp, AsmInsType branch) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + +// if (CheckFunc) +// printf("Compress %d : %d..%d, %d\n", mIndex, lower, upper, cmp); + + if (mNumEntries > 1) + { + lower = 0x00; + upper = 0xff; + cmp = -1; + branch = ASMIT_JMP; + } + + int sz = mIns.Size(); + if (sz > 0) + { + if (ChangesAccu()) + { + lower = 0x00; + upper = 0xff; + branch = ASMIT_JMP; + } + + if (mIns[sz - 1].mType == ASMIT_CMP && mIns[sz - 1].mMode == ASMIM_IMMEDIATE) + { + int pcmp = cmp; + cmp = mIns[sz - 1].mAddress; + + if (!mExitRequiredRegs[CPU_REG_C] && !mExitRequiredRegs[CPU_REG_Z] && mFalseJump) + { + if (cmp != -1 && branch == ASMIT_BNE) + { + if (cmp + 1 == upper && upper == mIns[sz - 1].mAddress) + { + if (mBranch == ASMIT_BEQ) + mBranch = ASMIT_BCS; + else if (mBranch == ASMIT_BNE) + mBranch = ASMIT_BCC; + mIns.Remove(sz - 1); + cmp = pcmp; + changed = true; + } + else if (lower + 1 == cmp && lower == mIns[sz - 1].mAddress) + { + if (mBranch == ASMIT_BEQ) + mBranch = ASMIT_BCC; + else if (mBranch == ASMIT_BNE) + mBranch = ASMIT_BCS; + mIns.Remove(sz - 1); + cmp = pcmp; + changed = true; + } + } + + if (!changed && (mBranch == ASMIT_BEQ || mBranch == ASMIT_BNE)) + { + NativeCodeBasicBlock* fbranch = mBranch == ASMIT_BEQ ? mFalseJump : mTrueJump; + if (fbranch->mIns.Size() == 1 && fbranch->mIns[0].mType == ASMIT_CMP && fbranch->mIns[0].mMode == ASMIM_IMMEDIATE && + (fbranch->mBranch == ASMIT_BEQ || fbranch->mBranch == ASMIT_BNE) && + fbranch->mNumEntries == 1 && !fbranch->mExitRequiredRegs[CPU_REG_C] && !fbranch->mExitRequiredRegs[CPU_REG_Z]) + { + int fcmp = fbranch->mIns[0].mAddress; + if (cmp == lower && fcmp == lower + 1) + { + mIns[sz - 1].mAddress = fcmp; + mBranch = mBranch == ASMIT_BEQ ? ASMIT_BCC : ASMIT_BCS; + cmp = fcmp; + changed = true; + } + } + } + } + } + else + cmp = -1; + } + + if (mFalseJump && cmp != -1) + { + if (cmp == upper) + { + if (mBranch == ASMIT_BCC) + { + mBranch = ASMIT_BNE; + changed = true; + } + else if (mBranch == ASMIT_BCS) + { + mBranch = ASMIT_BEQ; + changed = true; + } + } + + if (mBranch == ASMIT_BEQ && lower == cmp && upper == cmp || + mBranch == ASMIT_BNE && (cmp < lower || cmp > upper) || + mBranch == ASMIT_BCS && cmp <= lower || + mBranch == ASMIT_BCC && cmp > upper) + { + mFalseJump->mEntryBlocks.RemoveAll(this); + mFalseJump->mNumEntries--; + mFalseJump = nullptr; + mBranch = ASMIT_JMP; + mTrueJump->CompressSwitchCascade(lower, upper, cmp, ASMIT_JMP); + return true; + } + + if (mBranch == ASMIT_BNE && lower == cmp && upper == cmp || + mBranch == ASMIT_BEQ && (cmp < lower || cmp > upper) || + mBranch == ASMIT_BCC && cmp <= lower || + mBranch == ASMIT_BCS && cmp > upper) + { + mTrueJump->mEntryBlocks.RemoveAll(this); + mTrueJump->mNumEntries--; + mBranch = ASMIT_JMP; + mTrueJump = mFalseJump; + mFalseJump = nullptr; + mTrueJump->CompressSwitchCascade(lower, upper, cmp, ASMIT_JMP); + return true; + } + + if (mBranch == ASMIT_BEQ) + { + if (mTrueJump->CompressSwitchCascade(cmp, cmp, cmp, ASMIT_BEQ)) changed = true; + if (cmp == lower) lower++; + if (cmp == upper) upper--; + if (mFalseJump->CompressSwitchCascade(lower, upper, cmp, ASMIT_BNE)) changed = true; + return changed; + } + else if (mBranch == ASMIT_BNE) + { + if (mFalseJump->CompressSwitchCascade(cmp, cmp, cmp, ASMIT_BEQ)) changed = true; + if (cmp == lower) lower++; + if (cmp == upper) upper--; + if (mTrueJump->CompressSwitchCascade(lower, upper, cmp, ASMIT_BNE)) changed = true; + return changed; + } + else if (mBranch == ASMIT_BCC) + { + if (mTrueJump->CompressSwitchCascade(lower, cmp - 1, cmp, ASMIT_BCC)) changed = true; + if (branch == ASMIT_BNE) + { + if (mFalseJump->CompressSwitchCascade(cmp + 1, upper, cmp, ASMIT_BCS)) changed = true; + } + else + { + if (mFalseJump->CompressSwitchCascade(cmp, upper, cmp, ASMIT_BCS)) changed = true; + } + return changed; + } + else + { + if (mFalseJump->CompressSwitchCascade(lower, cmp - 1, cmp, ASMIT_BCC)) changed = true; + if (branch == ASMIT_BNE) + { + if (mTrueJump->CompressSwitchCascade(cmp + 1, upper, cmp, ASMIT_BCS)) changed = true; + } + else + { + if (mTrueJump->CompressSwitchCascade(cmp, upper, cmp, ASMIT_BCS)) changed = true; + } + return changed; + } + } + + if (mTrueJump && mTrueJump->CompressSwitchCascade(lower, upper, cmp, mBranch)) changed = true; + if (mFalseJump && mFalseJump->CompressSwitchCascade(lower, upper, cmp, InvertBranchCondition(mBranch))) changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::Split16BitLoopCount(NativeCodeProcedure* proc) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + +#if 1 + int sz = mIns.Size(); + if (sz >= 8 && mBranch == ASMIT_BNE) + { + if (mIns[sz - 8].mType == ASMIT_CLC && + mIns[sz - 7].mType == ASMIT_LDA && + mIns[sz - 6].mType == ASMIT_ADC && mIns[sz - 6].mMode == ASMIM_IMMEDIATE && mIns[sz - 6].mAddress == 0xff && + mIns[sz - 5].mType == ASMIT_STA && mIns[sz - 5].SameEffectiveAddress(mIns[sz - 7]) && + + mIns[sz - 4].mType == ASMIT_LDA && + mIns[sz - 3].mType == ASMIT_ADC && mIns[sz - 3].mMode == ASMIM_IMMEDIATE && mIns[sz - 3].mAddress == 0xff && + mIns[sz - 2].mType == ASMIT_STA && mIns[sz - 2].SameEffectiveAddress(mIns[sz - 4]) && + + mIns[sz - 1].mType == ASMIT_ORA && mIns[sz - 1].SameEffectiveAddress(mIns[sz - 7]) && + + HasAsmInstructionMode(ASMIT_DEC, mIns[sz - 5].mMode) && + HasAsmInstructionMode(ASMIT_DEC, mIns[sz - 2].mMode) && + + !(mIns[sz - 1].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C))) + { + changed = true; + + NativeCodeBasicBlock* pblock = nullptr; + if (mEntryBlocks.Size() == 2) + { + if (mEntryBlocks[0] == this) + pblock = mEntryBlocks[1]; + else if (mEntryBlocks[1] == this) + pblock = mEntryBlocks[0]; + } + + bool preinc = false; + if (pblock && !pblock->mFalseJump && mTrueJump == this && mIns[sz - 7].mMode == ASMIM_ZERO_PAGE && !mFalseJump->mEntryRequiredRegs[mIns[sz - 7].mAddress] && !mEntryRequiredRegs[CPU_REG_A]) + { + int i = 0; + while (i < sz - 8 && !mIns[i].ReferencesZeroPage(mIns[sz - 7].mAddress)) + i++; + if (i == sz - 8) + preinc = true; + } + + NativeCodeInstruction ilow(mIns[sz - 7]); + NativeCodeInstruction ihigh(mIns[sz - 4]); + + mIns.SetSize(sz - 8); + + if (preinc) + { + NativeCodeBasicBlock* dblock = proc->AllocateBlock(); + NativeCodeBasicBlock* zblock = proc->AllocateBlock(); + + zblock->mTrueJump = this; + + dblock->mTrueJump = this; + dblock->mFalseJump = mFalseJump; + dblock->mBranch = ASMIT_BNE; + + mFalseJump = dblock; + + pblock->mIns.Push(NativeCodeInstruction(ilow.mIns, ASMIT_LDA, ilow)); + pblock->mBranch = ASMIT_BEQ; + pblock->mFalseJump = zblock; + zblock->mIns.Push(NativeCodeInstruction(ihigh.mIns, ASMIT_INC, ihigh)); + + mIns.Push(NativeCodeInstruction(ilow.mIns, ASMIT_DEC, ilow)); + dblock->mIns.Push(NativeCodeInstruction(ihigh.mIns, ASMIT_DEC, ihigh)); + } + else + { + NativeCodeBasicBlock* hblock = proc->AllocateBlock(); + NativeCodeBasicBlock* dblock = proc->AllocateBlock(); + NativeCodeBasicBlock* zblock = proc->AllocateBlock(); + + zblock->mTrueJump = mTrueJump; + zblock->mFalseJump = mFalseJump; + zblock->mBranch = ASMIT_BNE; + + dblock->mTrueJump = mTrueJump; + dblock->mFalseJump = zblock; + dblock->mBranch = ASMIT_BNE; + + hblock->mTrueJump = dblock; + hblock->mBranch = ASMIT_JMP; + mTrueJump = dblock; + mFalseJump = hblock; + + mIns.Push(NativeCodeInstruction(ilow.mIns, ASMIT_LDA, ilow)); + + hblock->mIns.Push(NativeCodeInstruction(ihigh.mIns, ASMIT_DEC, ihigh)); + + dblock->mIns.Push(NativeCodeInstruction(ilow.mIns, ASMIT_DEC, ilow)); + + zblock->mIns.Push(NativeCodeInstruction(ihigh.mIns, ASMIT_LDA, ihigh)); + } + } + } +#endif + if (mTrueJump && mTrueJump->Split16BitLoopCount(proc)) + changed = true; + if (mFalseJump && mFalseJump->Split16BitLoopCount(proc)) + changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::MoveStoresBehindCondition(void) +{ + bool changed = false; + if (!mVisited) + { + mVisited = true; + + if (mEntryBlocks.Size() > 1) + { + NativeCodeBasicBlock* eb = mEntryBlocks[0]; + + int addr, index, taddr, tindex; + NativeCodeInstruction ains; + + if (eb->HasTailSTAInto(addr, index, this)) + { + int i = 1; + while (i < mEntryBlocks.Size() && mEntryBlocks[i]->HasTailSTAInto(taddr, tindex, this) && taddr == addr) + i++; + if (i == mEntryBlocks.Size()) + { + mEntryRequiredRegs += CPU_REG_A; + + mIns.Insert(0, eb->mIns[index]); + mIns[0].mLive |= LIVE_CPU_REG_A | LIVE_CPU_REG_X | LIVE_CPU_REG_Y; + for (int i = 0; i < mEntryBlocks.Size(); i++) + { + NativeCodeBasicBlock* b = mEntryBlocks[i]; + b->HasTailSTA(taddr, tindex); + b->mExitRequiredRegs += CPU_REG_A; + + for (int j = tindex + 1; j < b->mIns.Size(); j++) + b->mIns[j].mLive |= LIVE_CPU_REG_A; + b->mIns.Remove(tindex); + } + changed = true; + } + } + + if (eb->HasTailSTXInto(addr, index, this)) + { + int i = 1; + while (i < mEntryBlocks.Size() && mEntryBlocks[i]->HasTailSTXInto(taddr, tindex, this) && taddr == addr) + i++; + if (i == mEntryBlocks.Size()) + { + mEntryRequiredRegs += CPU_REG_X; + + mIns.Insert(0, eb->mIns[index]); + mIns[0].mLive |= LIVE_CPU_REG_A | LIVE_CPU_REG_X | LIVE_CPU_REG_Y; + for (int i = 0; i < mEntryBlocks.Size(); i++) + { + NativeCodeBasicBlock* b = mEntryBlocks[i]; + b->HasTailSTX(taddr, tindex); + b->mExitRequiredRegs += CPU_REG_X; + for (int j = tindex + 1; j < b->mIns.Size(); j++) + b->mIns[j].mLive |= LIVE_CPU_REG_X; + b->mIns.Remove(tindex); + } + changed = true; + } + } + + if (eb->HasTailSTYInto(addr, index, this)) + { + int i = 1; + while (i < mEntryBlocks.Size() && mEntryBlocks[i]->HasTailSTYInto(taddr, tindex, this) && taddr == addr) + i++; + if (i == mEntryBlocks.Size()) + { + mEntryRequiredRegs += CPU_REG_Y; + + mIns.Insert(0, eb->mIns[index]); + mIns[0].mLive |= LIVE_CPU_REG_A | LIVE_CPU_REG_X | LIVE_CPU_REG_Y; + for (int i = 0; i < mEntryBlocks.Size(); i++) + { + NativeCodeBasicBlock* b = mEntryBlocks[i]; + b->HasTailSTY(taddr, tindex); + b->mExitRequiredRegs += CPU_REG_Y; + for (int j = tindex + 1; j < b->mIns.Size(); j++) + b->mIns[j].mLive |= LIVE_CPU_REG_Y; + b->mIns.Remove(tindex); + } + changed = true; + } + } + } + + if (mFalseJump && mTrueJump) + { + NativeCodeBasicBlock* eblock = nullptr, * cblock = nullptr; + if (mTrueJump->mTrueJump == mFalseJump && !mTrueJump->mFalseJump && mFalseJump->mNumEntries == 2 && mTrueJump->mNumEntries == 1) + { + cblock = mTrueJump; + eblock = mFalseJump; + } + if (mFalseJump->mTrueJump == mTrueJump && !mFalseJump->mFalseJump && mTrueJump->mNumEntries == 2 && mFalseJump->mNumEntries == 1) + { + cblock = mFalseJump; + eblock = mTrueJump; + } + + if (eblock && eblock != this) + { + NativeCodeInstruction ins, cins; + int index, cindex; + + if (HasTailSTAGlobal(ins, index) && cblock->HasTailSTAGlobal(cins, cindex) && ins.SameEffectiveAddress(cins)) + { + if (!cblock->ReferencesMemory(ins, 0, cindex)) + { + mExitRequiredRegs += CPU_REG_A; + cblock->mExitRequiredRegs += CPU_REG_A; + eblock->mEntryRequiredRegs += CPU_REG_A; + + uint32 live = LIVE_CPU_REG_A; + if (ins.ReferencesXReg()) + { + live += LIVE_CPU_REG_X; + mExitRequiredRegs += CPU_REG_X; + cblock->mExitRequiredRegs += CPU_REG_X; + } + if (ins.ReferencesYReg()) + { + live += LIVE_CPU_REG_Y; + mExitRequiredRegs += CPU_REG_Y; + cblock->mExitRequiredRegs += CPU_REG_Y; + } + + eblock->mIns.Insert(0, ins); + for (int i = index; i < mIns.Size(); i++) + mIns[i].mLive |= live; + for (int i = cindex; i < cblock->mIns.Size(); i++) + cblock->mIns[i].mLive |= live; + mIns.Remove(index); + cblock->mIns.Remove(cindex); + changed = true; + } + } + } + } + + if (mTrueJump && mTrueJump->MoveStoresBehindCondition()) changed = true; + if (mFalseJump && mFalseJump->MoveStoresBehindCondition()) changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::MoveStoresBeforeDiamond(void) +{ + bool changed = false; + if (!mVisited) + { + mVisited = true; + if (mTrueJump && mFalseJump) + { + NativeCodeBasicBlock* dblock = nullptr, * eblock = nullptr; + + if (mTrueJump->mTrueJump == mFalseJump && !mTrueJump->mFalseJump && mFalseJump->mNumEntries == 2 && mTrueJump->mNumEntries == 1) + { + dblock = mTrueJump; + eblock = mFalseJump; + } + else if (mFalseJump->mTrueJump == mTrueJump && !mFalseJump->mFalseJump && mTrueJump->mNumEntries == 2 && mFalseJump->mNumEntries == 1) + { + dblock = mFalseJump; + eblock = mTrueJump; + } + + if (eblock && eblock->mEntryRequiredRegs.Size() > 0) + { + bool avalid = eblock->mEntryRequiredRegs[CPU_REG_A] && !dblock->ChangesAccu(); + bool xvalid = eblock->mEntryRequiredRegs[CPU_REG_X] && !dblock->ChangesXReg(); + bool yvalid = eblock->mEntryRequiredRegs[CPU_REG_Y] && !dblock->ChangesYReg(); + + int i = 0; + while (i < eblock->mIns.Size()) + { + NativeCodeInstruction& ins(eblock->mIns[i]); + + bool move = false; + if (avalid) + { + if (ins.mType == ASMIT_STA) + move = true; + else if (ins.ChangesAccu()) + avalid = false; + } + else if (xvalid) + { + if (ins.mType == ASMIT_STX) + move = true; + else if (ins.ChangesXReg()) + xvalid = false; + } + else if (yvalid) + { + if (ins.mType == ASMIT_STY) + move = true; + else if (ins.ChangesYReg()) + yvalid = false; + } + else + break; + + if (move && eblock->MayBeMovedBeforeBlock(i, ins) && dblock->MayBeMovedBeforeBlock(dblock->mIns.Size(), ins)) + { + ins.mLive = LIVE_ALL; + mIns.Push(ins); + eblock->mIns.Remove(i); + changed = true; + } + else + i++; + } + } + } + + if (mTrueJump && mTrueJump->MoveStoresBeforeDiamond()) changed = true; + if (mFalseJump && mFalseJump->MoveStoresBeforeDiamond()) changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::CrossBlockFlagsForwarding(void) +{ + bool changed = false; + if (!mVisited) + { + mVisited = true; + + int i = mIns.Size() - 1; + while (i >= 0) + { + if (mIns[i].mType == ASMIT_ORA && mIns[i].mMode == ASMIM_IMMEDIATE && mIns[i].mAddress == 0) + { + int j = i - 1; + while (j >= 0 && !mIns[j].ChangesZFlag()) + j--; + if (j >= 0) + { + if (mIns[j].ChangesAccuAndFlag()) + { + while (j < i) + { + mIns[j].mLive |= LIVE_CPU_REG_Z; + j++; + } + mIns[i].mType = ASMIT_NOP; + mIns[i].mMode = ASMIM_IMPLIED; + changed = true; + } + else if (j > 0 && mIns[j - 1].ChangesAccuAndFlag() && CanSwapInstructions(j - 1)) + { + int live = mIns[j].mLive | mIns[j - 1].mLive; + mIns[j].mLive |= live; + mIns[j - 1].mLive |= live; + + NativeCodeInstruction ins(mIns[j]); + mIns[j] = mIns[j - 1]; + mIns[j - 1] = ins; + + while (j < i) + { + mIns[j].mLive |= LIVE_CPU_REG_Z; + j++; + } + + mIns[i].mType = ASMIT_NOP; + mIns[i].mMode = ASMIM_IMPLIED; + changed = true; + } + } + else + { + int j = 0; + while (j < mEntryBlocks.Size() && mEntryBlocks[j]->mIns.Size() > 0 && mEntryBlocks[j]->mIns.Last().ChangesAccuAndFlag()) + j++; + if (j == mEntryBlocks.Size()) + { + for (int j = 0; j < mEntryBlocks.Size(); j++) + mEntryBlocks[j]->mExitRequiredRegs += CPU_REG_Z; + mEntryRequiredRegs += CPU_REG_Z; + + j = 0; + while (j < i) + { + mIns[j].mLive |= LIVE_CPU_REG_Z; + j++; + } + mIns[i].mType = ASMIT_NOP; + mIns[i].mMode = ASMIM_IMPLIED; + changed = true; + } + } + } + + i--; + } + + if (mTrueJump && mTrueJump->CrossBlockFlagsForwarding()) + changed = true; + if (mFalseJump && mFalseJump->CrossBlockFlagsForwarding()) + changed = true; + } + + return changed; +} + + +bool NativeCodeBasicBlock::LoopRegisterWrapAround(void) +{ + bool changed = false; + if (!mVisited) + { + mVisited = true; + + if (mLoopHead && mNumEntries == 2) + { + NativeCodeBasicBlock* eblock = nullptr, * cblock = nullptr, * bblock = nullptr; + + if (mEntryBlocks[0]->mFalseJump) + { + eblock = mEntryBlocks[0]; + bblock = mEntryBlocks[1]; + } + else if (mEntryBlocks[1]->mFalseJump) + { + eblock = mEntryBlocks[1]; + bblock = mEntryBlocks[0]; + } + + if (eblock && eblock != this && !bblock->mFalseJump) + { + if (eblock->mFalseJump == this) + cblock = eblock->mTrueJump; + else + cblock = eblock->mFalseJump; + + int i = eblock->mIns.Size() - 1; + while (i >= 0 && !eblock->mIns[i].ChangesXReg()) + i--; + if (i >= 0 && eblock->mIns[i].mType == ASMIT_LDX && eblock->mIns[i].mMode == ASMIM_ZERO_PAGE) + { + int addr = eblock->mIns[i].mAddress; + + int j = 0; + while (j < mIns.Size() && !mIns[j].ReferencesXReg() && !mIns[j].ChangesZeroPage(addr)) + j++; + if (j < mIns.Size()) + { + if (mIns[j].mType == ASMIT_STX && mIns[j].mMode == ASMIM_ZERO_PAGE && mIns[j].mAddress == addr) + { + bblock->mIns.Push(mIns[j]); + bblock->mExitRequiredRegs += addr; + mEntryRequiredRegs += addr; + mIns.Remove(j); + changed = true; + } + } + } + + i = eblock->mIns.Size() - 1; + while (i >= 0 && !eblock->mIns[i].ChangesYReg()) + i--; + if (i >= 0 && eblock->mIns[i].mType == ASMIT_LDY && eblock->mIns[i].mMode == ASMIM_ZERO_PAGE) + { + int addr = eblock->mIns[i].mAddress; + + int j = 0; + while (j < mIns.Size() && !mIns[j].ReferencesYReg() && !mIns[j].ChangesZeroPage(addr)) + j++; + if (j < mIns.Size()) + { + if (mIns[j].mType == ASMIT_STY && mIns[j].mMode == ASMIM_ZERO_PAGE && mIns[j].mAddress == addr) + { + bblock->mIns.Push(mIns[j]); + bblock->mExitRequiredRegs += addr; + mEntryRequiredRegs += addr; + mIns.Remove(j); + changed = true; + } + } + } + + i = eblock->mIns.Size() - 1; + while (i >= 0 && !eblock->mIns[i].ChangesAccu()) + i--; + if (i >= 0 && eblock->mIns[i].mType == ASMIT_LDA && eblock->mIns[i].mMode == ASMIM_ZERO_PAGE) + { + int addr = eblock->mIns[i].mAddress; + + int j = 0; + while (j < mIns.Size() && !mIns[j].ReferencesAccu() && !mIns[j].ChangesZeroPage(addr)) + j++; + if (j < mIns.Size()) + { + if (mIns[j].mType == ASMIT_STA && mIns[j].mMode == ASMIM_ZERO_PAGE && mIns[j].mAddress == addr) + { + bblock->mIns.Push(mIns[j]); + bblock->mExitRequiredRegs += addr; + mEntryRequiredRegs += addr; + mIns.Remove(j); + changed = true; + } + } + } +#if 0 + int esz = eblock->mIns.Size(); + if (mIns.Size() > 0 && esz >= 2 && + mIns[0].mType == ASMIT_LDX && + eblock->mIns[esz - 2].mType == ASMIT_LDA && mIns[0].SameEffectiveAddress(eblock->mIns[esz - 2]) && + eblock->mIns[esz - 1].mType == ASMIT_CMP && HasAsmInstructionMode(ASMIT_CPX, eblock->mIns[esz - 1].mMode) && + !(eblock->mIns[esz - 1].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_X))) + { + printf("Doopsie\n"); + + bblock->mIns.Push(mIns[0]); + bblock->mExitRequiredRegs += CPU_REG_X; + mEntryRequiredRegs += CPU_REG_X; + mIns.Remove(0); + + eblock->mExitRequiredRegs += CPU_REG_X; + eblock->mIns[esz - 2].mType = ASMIT_LDX; eblock->mIns[esz - 2].mLive |= LIVE_CPU_REG_X; + eblock->mIns[esz - 1].mType = ASMIT_CPX; eblock->mIns[esz - 1].mLive |= LIVE_CPU_REG_X; + changed = true; + } +#endif + } + } + + if (mTrueJump && mTrueJump->LoopRegisterWrapAround()) + changed = true; + if (mFalseJump && mFalseJump->LoopRegisterWrapAround()) + changed = true; + } + + return changed; +} + + +bool NativeCodeBasicBlock::EliminateDeadLoops(void) +{ + bool changed = false; + if (!mVisited) + { + mVisited = true; + + if (mLoopHead && mNumEntries == 2 && mFalseJump) + { + NativeCodeBasicBlock* eblock = nullptr; + if (mTrueJump == this) + eblock = mFalseJump; + else if (mFalseJump == this) + eblock = mTrueJump; + + if (eblock) + { + if (mIns.Size() == 2 && (mIns[0].mType == ASMIT_INX || mIns[0].mType == ASMIT_DEX) && mIns[1].mType == ASMIT_CPX) + { + if (!(eblock->mEntryRequiredRegs[CPU_REG_X])) + { + mLoopHead = false; + mTrueJump = eblock; + mBranch = ASMIT_JMP; + mEntryBlocks.RemoveAll(this); + mFalseJump = nullptr; + mNumEntries--; + changed = true; + } + } + else if (mIns.Size() == 2 && (mIns[0].mType == ASMIT_INY || mIns[0].mType == ASMIT_DEY) && mIns[1].mType == ASMIT_CPY) + { + if (!(eblock->mEntryRequiredRegs[CPU_REG_Y])) + { + mLoopHead = false; + mTrueJump = eblock; + mBranch = ASMIT_JMP; + mEntryBlocks.RemoveAll(this); + mFalseJump = nullptr; + mNumEntries--; + changed = true; + } + } + } + } + + if (mTrueJump && mTrueJump->EliminateDeadLoops()) + changed = true; + if (mFalseJump && mFalseJump->EliminateDeadLoops()) + changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::SinglePathRegisterForwardY(NativeCodeBasicBlock* path, int yreg) +{ + if (path && path->mNumEntries == 1 && path->mTrueJump && !path->mFalseJump && !path->ChangesYReg() && !path->ChangesZeroPage(yreg)) + { + NativeCodeBasicBlock* mblock = path->mTrueJump; + + if (mblock && mblock->mIns.Size() && mblock->mIns[0].mType == ASMIT_LDY && mblock->mIns[0].mMode == ASMIM_ZERO_PAGE && mblock->mIns[0].mAddress == yreg) + { + bool fail = false; + for (int i = 0; i < mblock->mEntryBlocks.Size(); i++) + { + NativeCodeBasicBlock* eblock = mblock->mEntryBlocks[i]; + if (eblock != path && eblock->mFalseJump) + { + fail = true; + break; + } + } + if (!fail) + { + for (int i = 0; i < mblock->mEntryBlocks.Size(); i++) + { + NativeCodeBasicBlock* eblock = mblock->mEntryBlocks[i]; + if (eblock != path) + { + eblock->mIns.Push(mblock->mIns[0]); + eblock->mExitRequiredRegs += CPU_REG_Y; + } + } + + mblock->mEntryRequiredRegs += CPU_REG_Y; + mExitRequiredRegs += CPU_REG_Y; + for (int i = 0; i < path->mIns.Size(); i++) + path->mIns[i].mLive |= LIVE_CPU_REG_Y; + path->mEntryRequiredRegs += CPU_REG_Y; + path->mExitRequiredRegs += CPU_REG_Y; + int i = mIns.Size() - 1; + while (i >= 0 && !(mIns[i].mLive & LIVE_CPU_REG_Y)) + { + mIns[i].mLive |= LIVE_CPU_REG_Y; + i--; + } + mblock->mIns.Remove(0); + return true; + } + } + } + + return false; +} + +bool NativeCodeBasicBlock::CrossBlockRegZPForward(int areg, int xreg, int yreg) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + NativeCodeBasicBlock* pblock = nullptr; + if (mEntryBlocks.Size() == 1) + pblock = mEntryBlocks[0]; + else + areg = xreg = yreg = -1; + + bool aref = false, xref = false, yref = false; + int nareg = areg, nxreg = xreg, nyreg = yreg; + + for (int i = 0; i < mIns.Size(); i++) + { + NativeCodeInstruction& ins(mIns[i]); + + if (pblock) + { + if (!aref && ins.mType == ASMIT_LDA && ins.mMode == ASMIM_ZERO_PAGE) + { + if (ins.mAddress == xreg) + { + + } + else if (ins.mAddress == yreg) + { + + } + } + else if (!xref && ins.mType == ASMIT_LDX && ins.mMode == ASMIM_ZERO_PAGE) + { + if (ins.mAddress == areg) + { + if (!pblock->mExitRequiredRegs[CPU_REG_X]) + { + int j = pblock->mIns.Size(); + while (j > 0 && !(pblock->mIns[j - 1].mLive & LIVE_CPU_REG_Z) && !pblock->mIns[j - 1].ChangesAccu() && !pblock->mIns[j - 1].ReferencesXReg()) + j--; + if (j > 0 && !(pblock->mIns[j - 1].mLive & LIVE_CPU_REG_Z)) + { + pblock->mIns.Insert(j, NativeCodeInstruction(ins.mIns, ASMIT_TAX)); + while (j < pblock->mIns.Size()) + { + pblock->mIns[j].mLive |= LIVE_CPU_REG_X; + j++; + } + j = 0; + while (j < i) + { + mIns[j].mLive |= LIVE_CPU_REG_X; + j++; + } + pblock->mExitRequiredRegs += CPU_REG_X; + mEntryRequiredRegs += CPU_REG_X; + ins.mType = ASMIT_NOP; + ins.mMode = ASMIM_IMPLIED; + nyreg = areg; + changed = true; + } + } + } + } + else if (!yref && ins.mType == ASMIT_LDY && ins.mMode == ASMIM_ZERO_PAGE) + { + if (ins.mAddress == areg) + { + if (!pblock->mExitRequiredRegs[CPU_REG_Y]) + { + int j = pblock->mIns.Size(); + while (j > 0 && !(pblock->mIns[j - 1].mLive & LIVE_CPU_REG_Z) && !pblock->mIns[j - 1].ChangesAccu() && !pblock->mIns[j - 1].ReferencesYReg()) + j--; + if (j > 0 && !(pblock->mIns[j - 1].mLive & LIVE_CPU_REG_Z)) + { + pblock->mIns.Insert(j, NativeCodeInstruction(ins.mIns, ASMIT_TAY)); + while (j < pblock->mIns.Size()) + { + pblock->mIns[j].mLive |= LIVE_CPU_REG_Y; + j++; + } + j = 0; + while (j < i) + { + mIns[j].mLive |= LIVE_CPU_REG_Y; + j++; + } + pblock->mExitRequiredRegs += CPU_REG_Y; + mEntryRequiredRegs += CPU_REG_Y; + ins.mType = ASMIT_NOP; + ins.mMode = ASMIM_IMPLIED; + nyreg = areg; + changed = true; + } + } + } + } + } + + if (nareg >= 0 && (ins.ChangesAccu() || ins.ChangesZeroPage(nareg))) + nareg = -1; + if (nxreg >= 0 && (ins.ChangesXReg() || ins.ChangesZeroPage(nxreg))) + nxreg = -1; + if (nyreg >= 0 && (ins.ChangesYReg() || ins.ChangesZeroPage(nyreg))) + nyreg = -1; + + if (areg >= 0 && ins.ChangesZeroPage(areg)) + areg = -1; + if (xreg >= 0 && ins.ChangesZeroPage(xreg)) + xreg = -1; + if (yreg >= 0 && ins.ChangesZeroPage(yreg)) + yreg = -1; + + if ((ins.mType == ASMIT_LDA || ins.mType == ASMIT_STA) && ins.mMode == ASMIM_ZERO_PAGE) + nareg = ins.mAddress; + if ((ins.mType == ASMIT_LDX || ins.mType == ASMIT_STX) && ins.mMode == ASMIM_ZERO_PAGE) + nxreg = ins.mAddress; + if ((ins.mType == ASMIT_LDY || ins.mType == ASMIT_STY) && ins.mMode == ASMIM_ZERO_PAGE) + nyreg = ins.mAddress; + + if (!aref && ins.ReferencesAccu()) aref = true; + if (!xref && ins.ReferencesXReg()) xref = true; + if (!yref && ins.ReferencesYReg()) yref = true; + } + + if (!changed && areg != -1 && !mEntryRequiredRegs[CPU_REG_A] && !mEntryRequiredRegs[CPU_REG_C]) + { + for (int i = 0; i < mIns.Size(); i++) + { + if (i + 3 < mIns.Size() && + mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_LDA && (mIns[i + 1].mMode == ASMIM_IMMEDIATE || mIns[i + 1].mMode == ASMIM_IMMEDIATE_ADDRESS) && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && mIns[i + 2].mAddress == areg && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && !(mIns[i].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z))) + { + if (!ChangesZeroPage(areg, 0, i) && !ReferencesZeroPage(mIns[i + 3].mAddress, 0, i)) + { + NativeCodeInstruction cins = mIns[i + 0], ains = mIns[i + 1], sins = mIns[i + 3]; + ains.mType = ASMIT_ADC; + mIns.Remove(i, 4); + mIns.Insert(0, cins); + mIns.Insert(1, ains); + mIns.Insert(2, sins); + return true; + } + } + } + } + + if (mTrueJump && mTrueJump->CrossBlockRegZPForward(nareg, nxreg, nyreg)) + return true; + if (mFalseJump && mFalseJump->CrossBlockRegZPForward(nareg, nxreg, nyreg)) + return true; + } + + return changed; +} + +bool NativeCodeBasicBlock::SinglePathStoreForward(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + if (mFalseJump) + { + NumberSet touched(NUM_REGS); + + int i = mIns.Size(); + while (i > 0) + { + i--; + if (mIns[i].mType == ASMIT_STA && mIns[i].mMode == ASMIM_ZERO_PAGE && !touched[mIns[i].mAddress] && !touched[CPU_REG_A]) + { + if (mTrueJump->mEntryRequiredRegs[mIns[i].mAddress] && !mFalseJump->mEntryRequiredRegs[mIns[i].mAddress] && mTrueJump->mNumEntries == 1) + { + mTrueJump->mIns.Insert(0, NativeCodeInstruction(mIns[i].mIns, ASMIT_STA, mIns[i])); + for (int j = i; j < mIns.Size(); j++) mIns[j].mLive |= LIVE_CPU_REG_A; + mExitRequiredRegs += CPU_REG_A; + mTrueJump->mEntryRequiredRegs += CPU_REG_A; + mIns[i].mType = ASMIT_NOP; mIns[i].mMode = ASMIM_IMPLIED; + changed = true; + } + else if (mFalseJump->mEntryRequiredRegs[mIns[i].mAddress] && !mTrueJump->mEntryRequiredRegs[mIns[i].mAddress] && mFalseJump->mNumEntries == 1) + { + mFalseJump->mIns.Insert(0, NativeCodeInstruction(mIns[i].mIns, ASMIT_STA, mIns[i])); + for (int j = i; j < mIns.Size(); j++) mIns[j].mLive |= LIVE_CPU_REG_A; + mExitRequiredRegs += CPU_REG_A; + mFalseJump->mEntryRequiredRegs += CPU_REG_A; + mIns[i].mType = ASMIT_NOP; mIns[i].mMode = ASMIM_IMPLIED; + changed = true; + } + } + if (mIns[i].mType == ASMIT_STX && mIns[i].mMode == ASMIM_ZERO_PAGE && !touched[mIns[i].mAddress] && !touched[CPU_REG_X]) + { + if (mTrueJump->mEntryRequiredRegs[mIns[i].mAddress] && !mFalseJump->mEntryRequiredRegs[mIns[i].mAddress] && mTrueJump->mNumEntries == 1) + { + mTrueJump->mIns.Insert(0, NativeCodeInstruction(mIns[i].mIns, ASMIT_STX, mIns[i])); + for (int j = i; j < mIns.Size(); j++) mIns[j].mLive |= LIVE_CPU_REG_X; + mExitRequiredRegs += CPU_REG_X; + mTrueJump->mEntryRequiredRegs += CPU_REG_X; + mIns[i].mType = ASMIT_NOP; mIns[i].mMode = ASMIM_IMPLIED; + changed = true; + } + else if (mFalseJump->mEntryRequiredRegs[mIns[i].mAddress] && !mTrueJump->mEntryRequiredRegs[mIns[i].mAddress] && mFalseJump->mNumEntries == 1) + { + mFalseJump->mIns.Insert(0, NativeCodeInstruction(mIns[i].mIns, ASMIT_STX, mIns[i])); + for (int j = i; j < mIns.Size(); j++) mIns[j].mLive |= LIVE_CPU_REG_X; + mExitRequiredRegs += CPU_REG_X; + mFalseJump->mEntryRequiredRegs += CPU_REG_X; + mIns[i].mType = ASMIT_NOP; mIns[i].mMode = ASMIM_IMPLIED; + changed = true; + } + } + if (mIns[i].mType == ASMIT_STY && mIns[i].mMode == ASMIM_ZERO_PAGE && !touched[mIns[i].mAddress] && !touched[CPU_REG_Y]) + { + if (mTrueJump->mEntryRequiredRegs[mIns[i].mAddress] && !mFalseJump->mEntryRequiredRegs[mIns[i].mAddress] && mTrueJump->mNumEntries == 1) + { + mTrueJump->mIns.Insert(0, NativeCodeInstruction(mIns[i].mIns, ASMIT_STY, mIns[i])); + for (int j = i; j < mIns.Size(); j++) mIns[j].mLive |= LIVE_CPU_REG_Y; + mExitRequiredRegs += CPU_REG_Y; + mTrueJump->mEntryRequiredRegs += CPU_REG_Y; + mIns[i].mType = ASMIT_NOP; mIns[i].mMode = ASMIM_IMPLIED; + changed = true; + } + else if (mFalseJump->mEntryRequiredRegs[mIns[i].mAddress] && !mTrueJump->mEntryRequiredRegs[mIns[i].mAddress] && mFalseJump->mNumEntries == 1) + { + mFalseJump->mIns.Insert(0, NativeCodeInstruction(mIns[i].mIns, ASMIT_STY, mIns[i])); + for (int j = i; j < mIns.Size(); j++) mIns[j].mLive |= LIVE_CPU_REG_Y; + mExitRequiredRegs += CPU_REG_Y; + mFalseJump->mEntryRequiredRegs += CPU_REG_Y; + mIns[i].mType = ASMIT_NOP; mIns[i].mMode = ASMIM_IMPLIED; + changed = true; + } + } + + if (mIns[i].ChangesAccu()) touched += CPU_REG_A; + if (mIns[i].ChangesXReg()) touched += CPU_REG_X; + if (mIns[i].ChangesYReg()) touched += CPU_REG_Y; + if (mIns[i].mMode == ASMIM_ZERO_PAGE) touched += mIns[i].mAddress; + if (mIns[i].mMode == ASMIM_INDIRECT_Y) + { + touched += mIns[i].mAddress; + touched += mIns[i].mAddress + 1; + } + if (mIns[i].mType == ASMIT_JSR) + break; + } + } + if (mTrueJump && mTrueJump->SinglePathStoreForward()) + changed = true; + if (mFalseJump && mFalseJump->SinglePathStoreForward()) + changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::SinglePathRegisterForward(void) +{ + bool changed = false; + if (!mVisited) + { + mVisited = true; + + if (mFalseJump) + { + int areg = -1, xreg = -1, yreg = -1; + int aindex = -1, xindex = -1, yindex = -1; + + for (int i = 0; i < mIns.Size(); i++) + { + NativeCodeInstruction& ins(mIns[i]); + + if (mIns[i].mMode == ASMIM_ZERO_PAGE) + { + if (mIns[i].mType == ASMIT_LDA || mIns[i].mType == ASMIT_STA) + { + areg = mIns[i].mAddress; + aindex = i; + } + else if (mIns[i].mType == ASMIT_LDX || mIns[i].mType == ASMIT_STX) + { + xreg = mIns[i].mAddress; + xindex = i; + } + else if (mIns[i].mType == ASMIT_LDY || mIns[i].mType == ASMIT_STY) + { + yreg = mIns[i].mAddress; + yindex = i; + } + else if (mIns[i].ChangesAddress()) + { + if (mIns[i].mAddress == areg) + areg = -1; + if (mIns[i].mAddress == xreg) + xreg = -1; + if (mIns[i].mAddress == yreg) + yreg = -1; + } + } + else + { + if (areg >= 0 && (mIns[i].ChangesAccu() || mIns[i].ChangesZeroPage(areg))) + areg = -1; + if (xreg >= 0 && (mIns[i].ChangesXReg() || mIns[i].ChangesZeroPage(xreg))) + xreg = -1; + if (yreg >= 0 && (mIns[i].ChangesYReg() || mIns[i].ChangesZeroPage(yreg))) + yreg = -1; + } + + if (areg >= 0 && mIns[i].RequiresAccu()) aindex = -1; + if (yreg >= 0 && mIns[i].RequiresXReg()) yindex = -1; + if (xreg >= 0 && mIns[i].RequiresYReg()) xindex = -1; + + } + + if (yreg >= 0) + { + if (SinglePathRegisterForwardY(mTrueJump, yreg) || SinglePathRegisterForwardY(mFalseJump, yreg)) + changed = true; + } + + if (!changed) + { + if (areg >= 0 && aindex >= 0 && mIns[aindex].mType == ASMIT_LDA && !(mIns[aindex].mLive & LIVE_CPU_REG_Z)) + { + if (mTrueJump->mEntryRequiredRegs[CPU_REG_A] && !mFalseJump->mEntryRequiredRegs[CPU_REG_A]) + { + NativeCodeBasicBlock* eblock = mProc->AllocateBlock(); + eblock->mBranch = ASMIT_JMP; + eblock->mTrueJump = mTrueJump; + eblock->mIns.Push(mIns[aindex]); + mIns.Remove(aindex); + eblock->mEntryRequiredRegs = mExitRequiredRegs; + eblock->mExitRequiredRegs = mExitRequiredRegs; + eblock->mNumEntries = 1; + eblock->mEntryBlocks.Push(this); + mTrueJump->mEntryBlocks.RemoveAll(this); + mTrueJump->mEntryBlocks.Push(eblock); + mTrueJump = eblock; + changed = true; + } + else if (mFalseJump->mEntryRequiredRegs[CPU_REG_A] && !mTrueJump->mEntryRequiredRegs[CPU_REG_A]) + { + NativeCodeBasicBlock* eblock = mProc->AllocateBlock(); + eblock->mBranch = ASMIT_JMP; + eblock->mTrueJump = mFalseJump; + eblock->mIns.Push(mIns[aindex]); + mIns.Remove(aindex); + eblock->mEntryRequiredRegs = mExitRequiredRegs; + eblock->mExitRequiredRegs = mExitRequiredRegs; + eblock->mNumEntries = 1; + eblock->mEntryBlocks.Push(this); + mFalseJump->mEntryBlocks.RemoveAll(this); + mFalseJump->mEntryBlocks.Push(eblock); + mFalseJump = eblock; + changed = true; + } + } + } + } + + if (mTrueJump && mTrueJump->SinglePathRegisterForward()) + changed = true; + if (mFalseJump && mFalseJump->SinglePathRegisterForward()) + changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::CrossBlockStoreLoadBypass(NativeCodeProcedure* proc) +{ + bool changed = false; + if (!mVisited) + { + mVisited = true; + + if (mTrueJump && mTrueJump->mTrueJump && !mFalseJump && mIns.Size() > 0 && mTrueJump->mIns.Size() > 0) + { + int sz = mIns.Size(); + + if (mTrueJump->mIns[0].mType == ASMIT_LDA && mTrueJump->mIns[0].mMode == ASMIM_ZERO_PAGE && !(mTrueJump->mIns[0].mLive & LIVE_MEM)) + { + if (mIns[sz - 1].mType == ASMIT_STA && mIns[sz - 1].mMode == ASMIM_ZERO_PAGE && mIns[sz - 1].mAddress == mTrueJump->mIns[0].mAddress) + { + NativeCodeBasicBlock* lblock = proc->AllocateBlock(); + + for (int i = 1; i < mTrueJump->mIns.Size(); i++) + lblock->mIns.Push(mTrueJump->mIns[i]); + mTrueJump->mIns.SetSize(1); + + mTrueJump->mTrueJump->RemEntryBlock(mTrueJump); + mTrueJump->mTrueJump->AddEntryBlock(lblock); + if (mTrueJump->mFalseJump) + { + mTrueJump->mFalseJump->RemEntryBlock(mTrueJump); + mTrueJump->mFalseJump->AddEntryBlock(lblock); + } + + lblock->mTrueJump = mTrueJump->mTrueJump; + lblock->mFalseJump = mTrueJump->mFalseJump; + lblock->mBranch = mTrueJump->mBranch; + + lblock->mNumEntries = 2; + lblock->mEntryBlocks.Push(this); + lblock->mEntryBlocks.Push(mTrueJump); + + lblock->mEntryProvidedRegs = mTrueJump->mEntryRequiredRegs; + lblock->mExitRequiredRegs = mTrueJump->mEntryRequiredRegs; + lblock->mExitRequiredRegs += CPU_REG_A; + + mTrueJump->mEntryRequiredRegs += CPU_REG_A; + + mTrueJump->RemEntryBlock(this); + + mTrueJump->mFalseJump = nullptr; + mTrueJump->mTrueJump = lblock; + mTrueJump->mBranch = ASMIT_JMP; + + mTrueJump = lblock; + + changed = true; + } + } + } + + if (mTrueJump && mTrueJump->CrossBlockStoreLoadBypass(proc)) + changed = true; + if (mFalseJump && mFalseJump->CrossBlockStoreLoadBypass(proc)) + changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::SimpleInlineCalls(void) +{ + bool changed = false; + if (!mVisited) + { + mVisited = true; + + for (int i = 0; i < mIns.Size(); i++) + { + if (mIns[i].mType == ASMIT_JSR) + { + if (mIns[i].mLinkerObject && mIns[i].mLinkerObject->mNativeProc) + { + NativeCodeProcedure* proc = mIns[i].mLinkerObject->mNativeProc; + if (proc->mSimpleInline) + { + NativeCodeBasicBlock* sins = proc->mEntryBlock->mTrueJump; + mIns.Remove(i); + for (int j = 0; j < sins->mIns.Size(); j++) + mIns.Insert(i + j, sins->mIns[j]); + changed = true; + } + } + } + } + + if (mTrueJump && mTrueJump->SimpleInlineCalls()) + changed = true; + if (mFalseJump && mFalseJump->SimpleInlineCalls()) + changed = true; + + } + + return changed; +} + +bool NativeCodeBasicBlock::Expand16BitLoopBranch(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + if (mTrueJump && mFalseJump && mBranch == ASMIT_BNE && mTrueJump->mIns.Size() == 0 && mTrueJump->mBranch == ASMIT_BCC) + { + int sz = mIns.Size(); + if (sz >= 1 && (mIns[sz - 1].mType == ASMIT_CMP || mIns[sz - 1].mType == ASMIT_CPX || mIns[sz - 1].mType == ASMIT_CPY)) + { + if (mTrueJump->mTrueJump->mLoopHead && IsDominatedBy(mTrueJump->mTrueJump) || + mLoopHead && !mTrueJump->mTrueJump->mFalseJump && mTrueJump->mTrueJump->mTrueJump == this) + { + NativeCodeBasicBlock* tblock = mTrueJump->mTrueJump; + NativeCodeBasicBlock* nblock = mProc->AllocateBlock(); + nblock->Close(mIns[sz - 1].mIns, mFalseJump, mTrueJump->mFalseJump, ASMIT_BEQ); + mBranch = ASMIT_BCC; + mFalseJump->RemEntryBlock(this); + mFalseJump->AddEntryBlock(nblock); + mTrueJump->RemEntryBlock(this); + tblock->AddEntryBlock(this); + mFalseJump = nblock; + mTrueJump = tblock; + changed = true; + } + } + } + + if (mTrueJump && mTrueJump->Expand16BitLoopBranch()) + changed = true; + if (mFalseJump && mFalseJump->Expand16BitLoopBranch()) + changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::ExpandADCShortCascadeToBranch(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + for (int i = 0; i + 13 < mIns.Size(); i++) + { + int sreg, dreg, offset; + int sregb, dregb, offsetb; + + if (!(mIns[i].mLive & LIVE_CPU_REG_Y))// || !(mIns[i].mLive & LIVE_CPU_REG_X)) + { + if (Is16BitAddSubImmediate(i, sreg, dreg, offset) && offset >= 0 && offset < 256 && dreg != sreg && + Is16BitAddSubImmediate(i + 7, sregb, dregb, offsetb) && offsetb >= offset && offsetb < 256 && dregb != sreg && sreg == sregb) + { + mIns.Insert(i, NativeCodeInstruction(mIns[i + 1].mIns, ASMIT_LDA, mIns[i + 1])); + i++; + mIns.Insert(i, NativeCodeInstruction(mIns[i + 4].mIns, ASMIT_LDY, mIns[i + 4])); + i++; + + int offset = 0; + NativeCodeBasicBlock* pblock = this; + + while (pblock->Is16BitAddSubImmediate(i, sregb, dregb, offsetb) && offsetb >= offset && offsetb < 256 && dregb != sreg && sreg == sregb) + { + NativeCodeBasicBlock* eblock = pblock->SplitBlock(i + 4); + pblock->mIns.Remove(i + 1, 1); + NativeCodeBasicBlock* iblock = mProc->AllocateBlock(); + pblock->mIns[i + 1].mAddress = offsetb - offset; + offset = offsetb; + pblock->mBranch = ASMIT_BCC; + pblock->mFalseJump = iblock; + iblock->mIns.Push(NativeCodeInstruction(eblock->mIns[0].mIns, ASMIT_INY)); + iblock->mNumEntries = 1; + iblock->mEntryBlocks.Push(pblock); + iblock->Close(eblock->mIns[0].mIns, eblock, nullptr, ASMIT_JMP); + eblock->mNumEntries++; + eblock->mEntryBlocks.Push(iblock); + eblock->mIns.Remove(0, 2); + eblock->mIns[0].mType = ASMIT_STY; + + pblock = eblock; + i = 1; + } + + changed = true; + break; + } + } + } + + if (mTrueJump && mTrueJump->ExpandADCShortCascadeToBranch()) + changed = true; + if (mFalseJump && mFalseJump->ExpandADCShortCascadeToBranch()) + changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::ExpandADCToBranch(NativeCodeProcedure* proc) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + int carry = -1; + if (mEntryRegisterDataSet[CPU_REG_C].mMode == NRDM_IMMEDIATE) + carry = mEntryRegisterDataSet[CPU_REG_C].mValue; + +#if 1 + if (mIns.Size() >= 8 && mTrueJump && mFalseJump && + (mBranch == ASMIT_BEQ || mBranch == ASMIT_BNE)) + { + int sz = mIns.Size(); + + if (mIns[sz - 8].mType == ASMIT_SEC && + mIns[sz - 7].mType == ASMIT_LDA && mIns[sz - 7].mMode == ASMIM_ZERO_PAGE && + mIns[sz - 6].mType == ASMIT_SBC && mIns[sz - 6].mMode == ASMIM_IMMEDIATE && mIns[sz - 6].mAddress == 1 && + mIns[sz - 5].mType == ASMIT_STA && mIns[sz - 5].mMode == ASMIM_ZERO_PAGE && mIns[sz - 5].mAddress == mIns[sz - 7].mAddress && + mIns[sz - 3].mType == ASMIT_SBC && mIns[sz - 3].mMode == ASMIM_IMMEDIATE && mIns[sz - 3].mAddress == 0 && + mIns[sz - 1].mType == ASMIT_ORA && mIns[sz - 1].mMode == ASMIM_ZERO_PAGE && mIns[sz - 1].mAddress == mIns[sz - 7].mAddress && + !(mIns[sz - 1].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C))) + { + if (mIns[sz - 4].mType == ASMIT_TXA && mIns[sz - 2].mType == ASMIT_TAX || + mIns[sz - 4].mType == ASMIT_TYA && mIns[sz - 2].mType == ASMIT_TAY || + mIns[sz - 4].mType == ASMIT_LDA && mIns[sz - 4].mMode == ASMIM_ZERO_PAGE && + mIns[sz - 2].mType == ASMIT_STA && mIns[sz - 2].mMode == ASMIM_ZERO_PAGE && mIns[sz - 2].mAddress == mIns[sz - 4].mAddress) + { + NativeCodeBasicBlock* dhblock = mProc->AllocateBlock(); + NativeCodeBasicBlock* dlblock = mProc->AllocateBlock(); + NativeCodeBasicBlock* chblock = mProc->AllocateBlock(); + + dhblock->mBranch = ASMIT_JMP; dhblock->mTrueJump = dlblock; + dlblock->mBranch = ASMIT_BNE; dlblock->mFalseJump = chblock; + chblock->mBranch = ASMIT_BNE; + + if (mBranch == ASMIT_BEQ) + { + dlblock->mTrueJump = mFalseJump; + chblock->mTrueJump = mFalseJump; + chblock->mFalseJump = mTrueJump; + } + else + { + dlblock->mTrueJump = mTrueJump; + chblock->mTrueJump = mTrueJump; + chblock->mFalseJump = mFalseJump; + } + + mBranch = ASMIT_BNE; + mTrueJump->RemEntryBlock(this); + mFalseJump->RemEntryBlock(this); + + mTrueJump = dlblock; + mFalseJump = dhblock; + + dlblock->AddEntryBlock(this); + dhblock->AddEntryBlock(this); + dlblock->AddEntryBlock(dhblock); + chblock->AddEntryBlock(dlblock); + + dlblock->mTrueJump->AddEntryBlock(dlblock); + chblock->mTrueJump->AddEntryBlock(chblock); + chblock->mFalseJump->AddEntryBlock(chblock); + + dlblock->mIns.Push(NativeCodeInstruction(mIns[sz - 7].mIns, ASMIT_DEC, mIns[sz - 7])); + switch (mIns[sz - 4].mType) + { + case ASMIT_TXA: + dhblock->mIns.Push(NativeCodeInstruction(mIns[sz - 4].mIns, ASMIT_DEX)); + chblock->mIns.Push(NativeCodeInstruction(mIns[sz - 4].mIns, ASMIT_TXA)); + break; + case ASMIT_TYA: + dhblock->mIns.Push(NativeCodeInstruction(mIns[sz - 4].mIns, ASMIT_DEY)); + chblock->mIns.Push(NativeCodeInstruction(mIns[sz - 4].mIns, ASMIT_TYA)); + break; + case ASMIT_LDA: + dhblock->mIns.Push(NativeCodeInstruction(mIns[sz - 4].mIns, ASMIT_DEC, mIns[sz - 4])); + chblock->mIns.Push(NativeCodeInstruction(mIns[sz - 4].mIns, ASMIT_LDA, mIns[sz - 4])); + break; + } + + mIns[sz - 8] = mIns[sz - 7]; + mIns.SetSize(sz - 7); + + changed = true; + } + } + } +#endif + + for (int i = 0; i < mIns.Size(); i++) + { + if (i + 2 < mIns.Size() && + mIns[i + 0].mType == ASMIT_TXA && + mIns[i + 1].mType == ASMIT_ADC && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 0 && + mIns[i + 2].mType == ASMIT_TAX && !(mIns[i + 2].mLive & LIVE_CPU_REG_C)) + { + changed = true; + + NativeCodeBasicBlock* eblock = proc->AllocateBlock(); + NativeCodeBasicBlock* rblock = proc->AllocateBlock(); + + rblock->mTrueJump = mTrueJump; + rblock->mFalseJump = mFalseJump; + rblock->mBranch = mBranch; + + const InterInstruction* iins(mIns[i].mIns); + + rblock->mIns.Push(NativeCodeInstruction(iins, ASMIT_TXA)); + eblock->mIns.Push(NativeCodeInstruction(iins, ASMIT_INX)); + for (int j = i + 3; j < mIns.Size(); j++) + rblock->mIns.Push(mIns[j]); + mIns.SetSize(i); + + mTrueJump = rblock; + mFalseJump = eblock; + mBranch = ASMIT_BCC; + + eblock->Close(iins, rblock, nullptr, ASMIT_JMP); + break; + } + if (i + 2 < mIns.Size() && + mIns[i + 0].mType == ASMIT_TXA && + mIns[i + 1].mType == ASMIT_SBC && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 0 && + mIns[i + 2].mType == ASMIT_TAX && !(mIns[i + 2].mLive & LIVE_CPU_REG_C)) + { + changed = true; + + NativeCodeBasicBlock* eblock = proc->AllocateBlock(); + NativeCodeBasicBlock* rblock = proc->AllocateBlock(); + + rblock->mTrueJump = mTrueJump; + rblock->mFalseJump = mFalseJump; + rblock->mBranch = mBranch; + + const InterInstruction* iins(mIns[i].mIns); + + rblock->mIns.Push(NativeCodeInstruction(iins, ASMIT_TXA)); + eblock->mIns.Push(NativeCodeInstruction(iins, ASMIT_DEX)); + for (int j = i + 3; j < mIns.Size(); j++) + rblock->mIns.Push(mIns[j]); + mIns.SetSize(i); + + mTrueJump = rblock; + mFalseJump = eblock; + mBranch = ASMIT_BCS; + + eblock->Close(iins, rblock, nullptr, ASMIT_JMP); + break; + } + if (i + 2 < mIns.Size() && + mIns[i + 0].mType == ASMIT_TYA && + mIns[i + 1].mType == ASMIT_ADC && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 0 && + mIns[i + 2].mType == ASMIT_TAY && !(mIns[i + 2].mLive & LIVE_CPU_REG_C)) + { + changed = true; + + NativeCodeBasicBlock* eblock = proc->AllocateBlock(); + NativeCodeBasicBlock* rblock = proc->AllocateBlock(); + + rblock->mTrueJump = mTrueJump; + rblock->mFalseJump = mFalseJump; + rblock->mBranch = mBranch; + + const InterInstruction* iins(mIns[i].mIns); + + rblock->mIns.Push(NativeCodeInstruction(iins, ASMIT_TYA)); + eblock->mIns.Push(NativeCodeInstruction(iins, ASMIT_INY)); + for (int j = i + 3; j < mIns.Size(); j++) + rblock->mIns.Push(mIns[j]); + mIns.SetSize(i); + + mTrueJump = rblock; + mFalseJump = eblock; + mBranch = ASMIT_BCC; + + eblock->Close(iins, rblock, nullptr, ASMIT_JMP); + break; + } + if (i + 2 < mIns.Size() && + mIns[i + 0].mType == ASMIT_TYA && + mIns[i + 1].mType == ASMIT_SBC && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 0 && + mIns[i + 2].mType == ASMIT_TAY && !(mIns[i + 2].mLive & LIVE_CPU_REG_C)) + { + changed = true; + + NativeCodeBasicBlock* eblock = proc->AllocateBlock(); + NativeCodeBasicBlock* rblock = proc->AllocateBlock(); + + rblock->mTrueJump = mTrueJump; + rblock->mFalseJump = mFalseJump; + rblock->mBranch = mBranch; + + const InterInstruction* iins(mIns[i].mIns); + + rblock->mIns.Push(NativeCodeInstruction(iins, ASMIT_TYA)); + eblock->mIns.Push(NativeCodeInstruction(iins, ASMIT_DEY)); + for (int j = i + 3; j < mIns.Size(); j++) + rblock->mIns.Push(mIns[j]); + mIns.SetSize(i); + + mTrueJump = rblock; + mFalseJump = eblock; + mBranch = ASMIT_BCS; + + eblock->Close(iins, rblock, nullptr, ASMIT_JMP); + break; + } + + if (i + 4 < mIns.Size() && + mIns[i + 0].mType == ASMIT_TAX && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 0 && + mIns[i + 2].mType == ASMIT_ROL && mIns[i + 2].mMode == ASMIM_IMPLIED && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && + mIns[i + 4].mType == ASMIT_TXA && !(mIns[i + 4].mLive & (LIVE_CPU_REG_C | LIVE_CPU_REG_X | LIVE_CPU_REG_Z))) + { + changed = true; + + NativeCodeBasicBlock* eblock = proc->AllocateBlock(); + NativeCodeBasicBlock* rblock = proc->AllocateBlock(); + + rblock->mTrueJump = mTrueJump; + rblock->mFalseJump = mFalseJump; + rblock->mBranch = mBranch; + + mIns[i + 0].mType = ASMIT_NOP; + mIns[i + 1].mType = ASMIT_LDX; + mIns[i + 2].mType = ASMIT_NOP; + mIns[i + 3].mType = ASMIT_STX; + mIns[i + 4].mType = ASMIT_NOP; + + const InterInstruction* iins(mIns[i].mIns); + + eblock->mIns.Push(NativeCodeInstruction(iins, ASMIT_INX)); + for (int j = i + 3; j < mIns.Size(); j++) + rblock->mIns.Push(mIns[j]); + mIns.SetSize(i + 2); + + mTrueJump = rblock; + mFalseJump = eblock; + mBranch = ASMIT_BCC; + + eblock->Close(iins, rblock, nullptr, ASMIT_JMP); + break; + } +#if 1 + if (i + 4 < mIns.Size() && + mIns[i + 0].mType == ASMIT_TAY && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 0 && + mIns[i + 2].mType == ASMIT_ROL && mIns[i + 2].mMode == ASMIM_IMPLIED && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && + mIns[i + 4].mType == ASMIT_TYA && !(mIns[i + 4].mLive & (LIVE_CPU_REG_C | LIVE_CPU_REG_Y | LIVE_CPU_REG_Z))) + { + changed = true; + + NativeCodeBasicBlock* eblock = proc->AllocateBlock(); + NativeCodeBasicBlock* rblock = proc->AllocateBlock(); + + rblock->mTrueJump = mTrueJump; + rblock->mFalseJump = mFalseJump; + rblock->mBranch = mBranch; + + mIns[i + 0].mType = ASMIT_NOP; + mIns[i + 1].mType = ASMIT_LDY; + mIns[i + 2].mType = ASMIT_NOP; + mIns[i + 3].mType = ASMIT_STY; + mIns[i + 4].mType = ASMIT_NOP; + + const InterInstruction* iins(mIns[i].mIns); + + eblock->mIns.Push(NativeCodeInstruction(iins, ASMIT_INY)); + for (int j = i + 3; j < mIns.Size(); j++) + rblock->mIns.Push(mIns[j]); + mIns.SetSize(i + 2); + + mTrueJump = rblock; + mFalseJump = eblock; + mBranch = ASMIT_BCC; + + eblock->Close(iins, rblock, nullptr, ASMIT_JMP); + break; + } + + + if (i + 4 < mIns.Size() && + mIns[i + 0].mType == ASMIT_TAX && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 0 && + mIns[i + 2].mType == ASMIT_SBC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 0 && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && + mIns[i + 4].mType == ASMIT_TXA && !(mIns[i + 4].mLive & (LIVE_CPU_REG_C | LIVE_CPU_REG_X | LIVE_CPU_REG_Z))) + { + changed = true; + + NativeCodeBasicBlock* eblock = proc->AllocateBlock(); + NativeCodeBasicBlock* rblock = proc->AllocateBlock(); + + rblock->mTrueJump = mTrueJump; + rblock->mFalseJump = mFalseJump; + rblock->mBranch = mBranch; + + mIns[i + 0].mType = ASMIT_NOP; + mIns[i + 1].mType = ASMIT_LDX; + mIns[i + 2].mType = ASMIT_NOP; + mIns[i + 3].mType = ASMIT_STX; + mIns[i + 4].mType = ASMIT_NOP; + + const InterInstruction* iins(mIns[i].mIns); + + eblock->mIns.Push(NativeCodeInstruction(iins, ASMIT_DEX)); + for (int j = i + 3; j < mIns.Size(); j++) + rblock->mIns.Push(mIns[j]); + mIns.SetSize(i + 2); + + mTrueJump = rblock; + mFalseJump = eblock; + mBranch = ASMIT_BCS; + + eblock->Close(iins, rblock, nullptr, ASMIT_JMP); + break; + } + + if (i + 4 < mIns.Size() && + mIns[i + 0].mType == ASMIT_TAY && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 0 && + mIns[i + 2].mType == ASMIT_SBC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 0 && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && + mIns[i + 4].mType == ASMIT_TYA && !(mIns[i + 4].mLive & (LIVE_CPU_REG_C | LIVE_CPU_REG_Y | LIVE_CPU_REG_Z))) + { + changed = true; + + NativeCodeBasicBlock* eblock = proc->AllocateBlock(); + NativeCodeBasicBlock* rblock = proc->AllocateBlock(); + + rblock->mTrueJump = mTrueJump; + rblock->mFalseJump = mFalseJump; + rblock->mBranch = mBranch; + + mIns[i + 0].mType = ASMIT_NOP; + mIns[i + 1].mType = ASMIT_LDY; + mIns[i + 2].mType = ASMIT_NOP; + mIns[i + 3].mType = ASMIT_STY; + mIns[i + 4].mType = ASMIT_NOP; + + const InterInstruction* iins(mIns[i].mIns); + + eblock->mIns.Push(NativeCodeInstruction(iins, ASMIT_DEY)); + for (int j = i + 3; j < mIns.Size(); j++) + rblock->mIns.Push(mIns[j]); + mIns.SetSize(i + 2); + + mTrueJump = rblock; + mFalseJump = eblock; + mBranch = ASMIT_BCS; + + eblock->Close(iins, rblock, nullptr, ASMIT_JMP); + break; + } +#endif +#if 1 + if (i + 4 == mIns.Size() && + mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_LDA && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 1 && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 1].SameEffectiveAddress(mIns[i + 3]) && + !(mIns[i + 3].mLive & LIVE_CPU_REG_A) && + mExitRequiredRegs.Size() && !mExitRequiredRegs[CPU_REG_C] && + (mBranch == ASMIT_BCC || mBranch == ASMIT_BCS)) + { + mIns[i + 3].mType = ASMIT_INC; + mIns[i + 3].mLive |= LIVE_CPU_REG_Z; + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + + if (mBranch == ASMIT_BCC) + mBranch = ASMIT_BNE; + else + mBranch = ASMIT_BEQ; + + changed = true; + break; + + } + else if (i + 4 == mIns.Size() && + mIns[i + 0].mType == ASMIT_LDA && + mIns[i + 1].mType == ASMIT_CLC && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 1 && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 0].SameEffectiveAddress(mIns[i + 3]) && + !(mIns[i + 3].mLive & LIVE_CPU_REG_A) && + mExitRequiredRegs.Size() && !mExitRequiredRegs[CPU_REG_C] && + (mBranch == ASMIT_BCC || mBranch == ASMIT_BCS)) + { + mIns[i + 3].mType = ASMIT_INC; + mIns[i + 3].mLive |= LIVE_CPU_REG_Z; + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + + if (mBranch == ASMIT_BCC) + mBranch = ASMIT_BNE; + else + mBranch = ASMIT_BEQ; + + changed = true; + break; + + } +#endif +#if 1 + if (i + 5 < mIns.Size()) + { + if (mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode != ASMIM_ABSOLUTE_X && + mIns[i + 1].mType == ASMIT_ASL && mIns[i + 1].mMode == ASMIM_IMPLIED && + mIns[i + 2].mType == ASMIT_LDA && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 0x00 && + mIns[i + 3].mType == ASMIT_ADC && mIns[i + 3].mMode == ASMIM_IMMEDIATE && mIns[i + 3].mAddress == 0xff && + mIns[i + 4].mType == ASMIT_EOR && mIns[i + 4].mMode == ASMIM_IMMEDIATE && mIns[i + 4].mAddress == 0xff && + mIns[i + 5].mType == ASMIT_TAX && mIns[i + 5].mMode == ASMIM_IMPLIED && !(mIns[i + 5].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z | LIVE_CPU_REG_C))) + { + changed = true; + + NativeCodeBasicBlock* mblock = proc->AllocateBlock(); + NativeCodeBasicBlock* rblock = proc->AllocateBlock(); + + rblock->mTrueJump = mTrueJump; + rblock->mFalseJump = mFalseJump; + rblock->mBranch = mBranch; + + for (int j = i + 6; j < mIns.Size(); j++) + rblock->mIns.Push(mIns[j]); + + mIns.SetSize(i + 1); + mIns.Insert(i, NativeCodeInstruction(mIns[i].mIns, ASMIT_LDX, ASMIM_IMMEDIATE, 0)); + mIns[i + 1].mLive |= LIVE_CPU_REG_Z | LIVE_CPU_REG_X; + + mTrueJump = mblock; + mFalseJump = rblock; + mBranch = ASMIT_BMI; + + mblock->mIns.Push(NativeCodeInstruction(mIns[i].mIns, ASMIT_LDX, ASMIM_IMMEDIATE, 0xff)); + + mblock->Close(mIns[i].mIns, rblock, nullptr, ASMIT_JMP); + break; + } + } +#endif +#if 1 + if (i + 2 < mIns.Size()) + { + if (mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_IMMEDIATE && mIns[i + 0].mAddress == 0x00 && + mIns[i + 1].mType == ASMIT_SBC && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 0x00 && + mIns[i + 2].mType == ASMIT_TAX && mIns[i + 2].mMode == ASMIM_IMPLIED && !(mIns[i + 2].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z | LIVE_CPU_REG_C))) + { + changed = true; + + NativeCodeBasicBlock* mblock = proc->AllocateBlock(); + NativeCodeBasicBlock* rblock = proc->AllocateBlock(); + + rblock->mTrueJump = mTrueJump; + rblock->mFalseJump = mFalseJump; + rblock->mBranch = mBranch; + + for (int j = i + 3; j < mIns.Size(); j++) + rblock->mIns.Push(mIns[j]); + + mIns.SetSize(i + 1); + mIns.Insert(i, NativeCodeInstruction(mIns[i].mIns, ASMIT_LDX, ASMIM_IMMEDIATE, 0)); + mIns[i + 1].mLive |= LIVE_CPU_REG_C | LIVE_CPU_REG_X; + + mTrueJump = mblock; + mFalseJump = rblock; + mBranch = ASMIT_BCC; + + mblock->mIns.Push(NativeCodeInstruction(mIns[i].mIns, ASMIT_LDX, ASMIM_IMMEDIATE, 0xff)); + + mblock->Close(mIns[i].mIns, rblock, nullptr, ASMIT_JMP); + break; + } + } +#endif +#if 1 + if (i + 6 < mIns.Size()) + { + if (mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_ASL && mIns[i + 1].mMode == ASMIM_IMPLIED && + mIns[i + 2].mType == ASMIT_LDA && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 0x00 && + mIns[i + 3].mType == ASMIT_ADC && mIns[i + 3].mMode == ASMIM_IMMEDIATE && mIns[i + 3].mAddress == 0xff && + mIns[i + 4].mType == ASMIT_EOR && mIns[i + 4].mMode == ASMIM_IMMEDIATE && mIns[i + 4].mAddress == 0xff && + mIns[i + 5].mType == ASMIT_ASL && mIns[i + 5].mMode == ASMIM_ZERO_PAGE && mIns[i + 5].mAddress == mIns[i + 0].mAddress && + mIns[i + 6].mType == ASMIT_ROL && mIns[i + 6].mMode == ASMIM_IMPLIED) + { + changed = true; + + NativeCodeBasicBlock* eblock = proc->AllocateBlock(); + NativeCodeBasicBlock* neblock = proc->AllocateBlock(); + NativeCodeBasicBlock* rblock = proc->AllocateBlock(); + + rblock->mTrueJump = mTrueJump; + rblock->mFalseJump = mFalseJump; + rblock->mBranch = mBranch; + + for (int j = i + 7; j < mIns.Size(); j++) + rblock->mIns.Push(mIns[j]); + mIns.SetSize(i + 1); + mIns[i + 0].mLive |= LIVE_CPU_REG_C; + mIns[i + 0].mType = ASMIT_ASL; + + mTrueJump = neblock; + mFalseJump = eblock; + mBranch = ASMIT_BCS; + + eblock->mIns.Push(NativeCodeInstruction(mIns[i].mIns, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + neblock->mIns.Push(NativeCodeInstruction(mIns[i].mIns, ASMIT_LDA, ASMIM_IMMEDIATE, 0xff)); + + eblock->Close(mIns[i].mIns, rblock, nullptr, ASMIT_JMP); + neblock->Close(mIns[i].mIns, rblock, nullptr, ASMIT_JMP); + break; + } +#if 1 + if (mIns[i + 0].mType == ASMIT_ASL && mIns[i + 0].mMode == ASMIM_IMPLIED && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 0x00 && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 0xff && + mIns[i + 3].mType == ASMIT_EOR && mIns[i + 3].mMode == ASMIM_IMMEDIATE && mIns[i + 3].mAddress == 0xff && + mIns[i + 4].mType == ASMIT_AND && mIns[i + 4].mMode == ASMIM_IMMEDIATE) + { + changed = true; + + NativeCodeBasicBlock* neblock = proc->AllocateBlock(); + NativeCodeBasicBlock* rblock = proc->AllocateBlock(); + + neblock->mIns.Push(NativeCodeInstruction(mIns[i].mIns, ASMIT_LDA, ASMIM_IMMEDIATE, mIns[i + 4].mAddress)); + + rblock->mTrueJump = mTrueJump; + rblock->mFalseJump = mFalseJump; + rblock->mBranch = mBranch; + + for (int j = i + 5; j < mIns.Size(); j++) + rblock->mIns.Push(mIns[j]); + mIns.SetSize(i + 1); + mIns[i + 0].mLive |= LIVE_CPU_REG_Z; + mIns[i + 0].mType = ASMIT_AND; + mIns[i + 0].mMode = ASMIM_IMMEDIATE; + mIns[i + 0].mAddress = 0x80; + + mTrueJump = rblock; + mFalseJump = neblock; + mBranch = ASMIT_BEQ; + + neblock->Close(mIns[i].mIns, rblock, nullptr, ASMIT_JMP); + break; + } +#endif + if (mIns[i + 0].mType == ASMIT_STA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_ASL && mIns[i + 1].mMode == ASMIM_IMPLIED && + mIns[i + 2].mType == ASMIT_LDA && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 0x00 && + mIns[i + 3].mType == ASMIT_ADC && mIns[i + 3].mMode == ASMIM_IMMEDIATE && mIns[i + 3].mAddress == 0xff && + mIns[i + 4].mType == ASMIT_EOR && mIns[i + 4].mMode == ASMIM_IMMEDIATE && mIns[i + 4].mAddress == 0xff && + mIns[i + 5].mType == ASMIT_ASL && mIns[i + 5].mMode == ASMIM_ZERO_PAGE && mIns[i + 5].mAddress == mIns[i + 0].mAddress && + mIns[i + 6].mType == ASMIT_ROL && mIns[i + 6].mMode == ASMIM_IMPLIED) + { + changed = true; + + NativeCodeBasicBlock* eblock = proc->AllocateBlock(); + NativeCodeBasicBlock* neblock = proc->AllocateBlock(); + NativeCodeBasicBlock* rblock = proc->AllocateBlock(); + + rblock->mTrueJump = mTrueJump; + rblock->mFalseJump = mFalseJump; + rblock->mBranch = mBranch; + + for (int j = i + 7; j < mIns.Size(); j++) + rblock->mIns.Push(mIns[j]); + mIns.SetSize(i + 1); + mIns[i + 0].mLive |= LIVE_CPU_REG_C; + mIns.Insert(i + 0, NativeCodeInstruction(mIns[i].mIns, ASMIT_ASL)); + + mTrueJump = neblock; + mFalseJump = eblock; + mBranch = ASMIT_BCS; + + eblock->mIns.Push(NativeCodeInstruction(mIns[i].mIns, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + neblock->mIns.Push(NativeCodeInstruction(mIns[i].mIns, ASMIT_LDA, ASMIM_IMMEDIATE, 0xff)); + + eblock->Close(mIns[i].mIns, rblock, nullptr, ASMIT_JMP); + neblock->Close(mIns[i].mIns, rblock, nullptr, ASMIT_JMP); + break; + } + } +#endif + +#if 1 + if (i + 5 < mIns.Size() && + mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_IMMEDIATE && mIns[i + 0].mAddress == 0x00 && + mIns[i + 1].mType == ASMIT_ROL && mIns[i + 1].mMode == ASMIM_IMPLIED && + mIns[i + 2].mType == ASMIT_EOR && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 0xff && + mIns[i + 3].mType == ASMIT_CLC && + mIns[i + 4].mType == ASMIT_ADC && mIns[i + 4].mMode == ASMIM_IMMEDIATE && mIns[i + 4].mAddress == 0x01 && + mIns[i + 5].mType == ASMIT_AND && mIns[i + 5].mMode == ASMIM_IMMEDIATE) + { + changed = true; + + NativeCodeBasicBlock* csblock = proc->AllocateBlock(); + NativeCodeBasicBlock* rblock = proc->AllocateBlock(); + + csblock->mIns.Push(NativeCodeInstruction(mIns[i].mIns, ASMIT_LDA, ASMIM_IMMEDIATE, mIns[i + 5].mAddress)); + + rblock->mTrueJump = mTrueJump; + rblock->mFalseJump = mFalseJump; + rblock->mBranch = mBranch; + + for (int j = i + 6; j < mIns.Size(); j++) + rblock->mIns.Push(mIns[j]); + mIns.SetSize(i + 1); + + mTrueJump = rblock; + mFalseJump = csblock; + mBranch = ASMIT_BCC; + + csblock->Close(mIns[i].mIns, rblock, nullptr, ASMIT_JMP); + break; + } +#endif + +#if 1 + if (i + 6 < mIns.Size()) + { + if (mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_LDA && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 1 && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 1].SameEffectiveAddress(mIns[i + 3]) && + mIns[i + 4].mType == ASMIT_LDA && + mIns[i + 5].mType == ASMIT_ADC && mIns[i + 5].mMode == ASMIM_IMMEDIATE && mIns[i + 5].mAddress == 0 && + mIns[i + 6].mType == ASMIT_STA && mIns[i + 4].SameEffectiveAddress(mIns[i + 6]) && + HasAsmInstructionMode(ASMIT_INC, mIns[i + 3].mMode) && + HasAsmInstructionMode(ASMIT_INC, mIns[i + 6].mMode) && + !(mIns[i + 6].mLive & (LIVE_CPU_REG_C | LIVE_CPU_REG_Z))) + { + changed = true; + + NativeCodeBasicBlock* iblock = proc->AllocateBlock(); + NativeCodeBasicBlock* fblock = proc->AllocateBlock(); + + + fblock->mTrueJump = mTrueJump; + fblock->mFalseJump = mFalseJump; + fblock->mBranch = mBranch; + fblock->mBranchIns = mBranchIns; + + mIns[i + 0].mType = ASMIT_NOP; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + mIns[i + 3].mType = ASMIT_INC; mIns[i + 3].mLive |= LIVE_CPU_REG_Z; + + if (mIns[i + 6].mLive & LIVE_CPU_REG_A) + fblock->mIns.Push(mIns[i + 4]); + + for (int j = i + 7; j < mIns.Size(); j++) + fblock->mIns.Push(mIns[j]); + + iblock->mIns.Push(mIns[i + 6]); + mIns.SetSize(i + 4); + iblock->mIns[0].mType = ASMIT_INC; + iblock->mTrueJump = fblock; + iblock->mBranch = ASMIT_JMP; + + mTrueJump = fblock; + mFalseJump = iblock; + mBranch = ASMIT_BNE; + mBranchIns = mIns[i + 3].mIns; + break; + } + + if (mIns[i + 0].mType == ASMIT_LDA && + mIns[i + 1].mType == ASMIT_CLC && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 1 && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 0].SameEffectiveAddress(mIns[i + 3]) && + mIns[i + 4].mType == ASMIT_LDA && + mIns[i + 5].mType == ASMIT_ADC && mIns[i + 5].mMode == ASMIM_IMMEDIATE && mIns[i + 5].mAddress == 0 && + mIns[i + 6].mType == ASMIT_STA && mIns[i + 4].SameEffectiveAddress(mIns[i + 6]) && + HasAsmInstructionMode(ASMIT_INC, mIns[i + 3].mMode) && + HasAsmInstructionMode(ASMIT_INC, mIns[i + 6].mMode) && + !(mIns[i + 6].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C | LIVE_CPU_REG_Z))) + { + changed = true; + + NativeCodeBasicBlock* iblock = proc->AllocateBlock(); + NativeCodeBasicBlock* fblock = proc->AllocateBlock(); + + fblock->mTrueJump = mTrueJump; + fblock->mFalseJump = mFalseJump; + fblock->mBranch = mBranch; + fblock->mBranchIns = mBranchIns; + + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_NOP; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + mIns[i + 3].mType = ASMIT_INC; mIns[i + 3].mLive |= LIVE_CPU_REG_Z; + + for (int j = i + 7; j < mIns.Size(); j++) + fblock->mIns.Push(mIns[j]); + iblock->mIns.Push(mIns[i + 6]); + mIns.SetSize(i + 4); + iblock->mIns[0].mType = ASMIT_INC; + iblock->mTrueJump = fblock; + iblock->mBranch = ASMIT_JMP; + + mTrueJump = fblock; + mFalseJump = iblock; + mBranch = ASMIT_BNE; + mBranchIns = mIns[i + 3].mIns; + break; + } + + if (carry == 0 && + mIns[i + 0].mType == ASMIT_LDA && + mIns[i + 1].mType == ASMIT_ADC && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 1 && + mIns[i + 2].mType == ASMIT_STA && mIns[i + 0].SameEffectiveAddress(mIns[i + 2]) && + mIns[i + 3].mType == ASMIT_LDA && + mIns[i + 4].mType == ASMIT_ADC && mIns[i + 4].mMode == ASMIM_IMMEDIATE && mIns[i + 4].mAddress == 0 && + mIns[i + 5].mType == ASMIT_STA && mIns[i + 3].SameEffectiveAddress(mIns[i + 5]) && + HasAsmInstructionMode(ASMIT_INC, mIns[i + 2].mMode) && + HasAsmInstructionMode(ASMIT_INC, mIns[i + 5].mMode) && + !(mIns[i + 5].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C | LIVE_CPU_REG_Z))) + { + changed = true; + + NativeCodeBasicBlock* iblock = proc->AllocateBlock(); + NativeCodeBasicBlock* fblock = proc->AllocateBlock(); + + fblock->mTrueJump = mTrueJump; + fblock->mFalseJump = mFalseJump; + fblock->mBranch = mBranch; + fblock->mBranchIns = mBranchIns; + + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_INC; mIns[i + 2].mLive |= LIVE_CPU_REG_Z; + + for (int j = i + 6; j < mIns.Size(); j++) + fblock->mIns.Push(mIns[j]); + iblock->mIns.Push(mIns[i + 5]); + mIns.SetSize(i + 3); + iblock->mIns[0].mType = ASMIT_INC; + iblock->mTrueJump = fblock; + iblock->mBranch = ASMIT_JMP; + + mTrueJump = fblock; + mFalseJump = iblock; + mBranch = ASMIT_BNE; + mBranchIns = mIns[i + 2].mIns; + break; + } + + + if (mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_LDA && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 0xff && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 1].SameEffectiveAddress(mIns[i + 3]) && + mIns[i + 4].mType == ASMIT_LDA && + mIns[i + 5].mType == ASMIT_ADC && mIns[i + 5].mMode == ASMIM_IMMEDIATE && mIns[i + 5].mAddress == 0xff && + mIns[i + 6].mType == ASMIT_STA && mIns[i + 4].SameEffectiveAddress(mIns[i + 6]) && + HasAsmInstructionMode(ASMIT_DEC, mIns[i + 3].mMode) && + HasAsmInstructionMode(ASMIT_DEC, mIns[i + 6].mMode) && + !(mIns[i + 6].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C | LIVE_CPU_REG_Z))) + { + changed = true; + + NativeCodeBasicBlock* iblock = proc->AllocateBlock(); + NativeCodeBasicBlock* fblock = proc->AllocateBlock(); + + fblock->mTrueJump = mTrueJump; + fblock->mFalseJump = mFalseJump; + fblock->mBranch = mBranch; + fblock->mBranchIns = mBranchIns; + + fblock->mIns.Push(mIns[i + 1]); + fblock->mIns[0].mType = ASMIT_DEC; + + mIns[i + 0].mType = ASMIT_NOP; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + mIns[i + 3].mType = ASMIT_LDA; mIns[i + 3].mLive |= LIVE_CPU_REG_Z; + + for (int j = i + 7; j < mIns.Size(); j++) + fblock->mIns.Push(mIns[j]); + iblock->mIns.Push(mIns[i + 6]); + mIns.SetSize(i + 4); + iblock->mIns[0].mType = ASMIT_DEC; + iblock->mTrueJump = fblock; + iblock->mBranch = ASMIT_JMP; + + mTrueJump = fblock; + mFalseJump = iblock; + mBranch = ASMIT_BNE; + mBranchIns = mIns[i + 3].mIns; + break; + } + + if (mIns[i + 0].mType == ASMIT_TXA && + mIns[i + 1].mType == ASMIT_CLC && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 1 && + mIns[i + 3].mType == ASMIT_TAX && + mIns[i + 4].mType == ASMIT_LDA && + mIns[i + 5].mType == ASMIT_ADC && mIns[i + 5].mMode == ASMIM_IMMEDIATE && mIns[i + 5].mAddress == 0 && + mIns[i + 6].mType == ASMIT_STA && mIns[i + 4].SameEffectiveAddress(mIns[i + 6]) && + HasAsmInstructionMode(ASMIT_INC, mIns[i + 6].mMode) && + !(mIns[i + 6].mLive & (LIVE_CPU_REG_C | LIVE_CPU_REG_Z))) + { + changed = true; + + NativeCodeBasicBlock* iblock = proc->AllocateBlock(); + NativeCodeBasicBlock* fblock = proc->AllocateBlock(); + + fblock->mTrueJump = mTrueJump; + fblock->mFalseJump = mFalseJump; + fblock->mBranch = mBranch; + fblock->mBranchIns = mBranchIns; + + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_NOP; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + mIns[i + 3].mType = ASMIT_INX; mIns[i + 3].mLive |= LIVE_CPU_REG_Z; + + fblock->mIns.Push(mIns[i + 4]); + + for (int j = i + 7; j < mIns.Size(); j++) + fblock->mIns.Push(mIns[j]); + iblock->mIns.Push(mIns[i + 6]); + + + mIns.SetSize(i + 4); + iblock->mIns[0].mType = ASMIT_INC; + + iblock->mTrueJump = fblock; + iblock->mBranch = ASMIT_JMP; + + mTrueJump = fblock; + mFalseJump = iblock; + mBranch = ASMIT_BNE; + mBranchIns = mIns[i + 3].mIns; + break; + } + + if (mIns[i + 0].mType == ASMIT_TXA && + mIns[i + 1].mType == ASMIT_CLC && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 1 && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && !(mIns[i + 3].mLive & LIVE_CPU_REG_X) && + mIns[i + 4].mType == ASMIT_LDA && + mIns[i + 5].mType == ASMIT_ADC && mIns[i + 5].mMode == ASMIM_IMMEDIATE && mIns[i + 5].mAddress == 0 && + mIns[i + 6].mType == ASMIT_STA && mIns[i + 4].SameEffectiveAddress(mIns[i + 6]) && + HasAsmInstructionMode(ASMIT_INC, mIns[i + 6].mMode) && + !(mIns[i + 6].mLive & (LIVE_CPU_REG_C | LIVE_CPU_REG_Z))) + { + changed = true; + + NativeCodeBasicBlock* iblock = proc->AllocateBlock(); + NativeCodeBasicBlock* fblock = proc->AllocateBlock(); + + fblock->mTrueJump = mTrueJump; + fblock->mFalseJump = mFalseJump; + fblock->mBranch = mBranch; + fblock->mBranchIns = mBranchIns; + + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_NOP; + mIns[i + 2].mType = ASMIT_INX; mIns[i + 2].mMode = ASMIM_IMPLIED; mIns[i + 2].mLive |= LIVE_CPU_REG_Z | LIVE_CPU_REG_X; + mIns[i + 3].mType = ASMIT_STX; mIns[i + 3].mLive |= LIVE_CPU_REG_Z; + + fblock->mIns.Push(mIns[i + 4]); + + for (int j = i + 7; j < mIns.Size(); j++) + fblock->mIns.Push(mIns[j]); + iblock->mIns.Push(mIns[i + 6]); + + + mIns.SetSize(i + 4); + iblock->mIns[0].mType = ASMIT_INC; + + iblock->mTrueJump = fblock; + iblock->mBranch = ASMIT_JMP; + + mTrueJump = fblock; + mFalseJump = iblock; + mBranch = ASMIT_BNE; + mBranchIns = mIns[i + 3].mIns; + break; + } + + if (mIns[i + 0].mType == ASMIT_TYA && + mIns[i + 1].mType == ASMIT_CLC && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 1 && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && !(mIns[i + 3].mLive & LIVE_CPU_REG_Y) && + mIns[i + 4].mType == ASMIT_TXA && + mIns[i + 5].mType == ASMIT_ADC && mIns[i + 5].mMode == ASMIM_IMMEDIATE && mIns[i + 5].mAddress == 0 && + mIns[i + 6].mType == ASMIT_TAX && + !(mIns[i + 6].mLive & (LIVE_CPU_REG_C | LIVE_CPU_REG_Z))) + { + changed = true; + + NativeCodeBasicBlock* iblock = proc->AllocateBlock(); + NativeCodeBasicBlock* fblock = proc->AllocateBlock(); + + fblock->mTrueJump = mTrueJump; + fblock->mFalseJump = mFalseJump; + fblock->mBranch = mBranch; + fblock->mBranchIns = mBranchIns; + + mIns[i + 0].mType = ASMIT_NOP; + mIns[i + 1].mType = ASMIT_NOP; + mIns[i + 2].mType = ASMIT_INY; mIns[i + 2].mMode = ASMIM_IMPLIED; mIns[i + 2].mLive |= LIVE_CPU_REG_Z | LIVE_CPU_REG_Y; + mIns[i + 3].mType = ASMIT_STY; mIns[i + 3].mLive |= LIVE_CPU_REG_Z; + + fblock->mIns.Push(NativeCodeInstruction(mIns[i].mIns, ASMIT_TXA)); + + for (int j = i + 7; j < mIns.Size(); j++) + fblock->mIns.Push(mIns[j]); + iblock->mIns.Push(NativeCodeInstruction(mIns[i].mIns, ASMIT_INX)); + + mIns.SetSize(i + 4); + + iblock->mTrueJump = fblock; + iblock->mBranch = ASMIT_JMP; + + mTrueJump = fblock; + mFalseJump = iblock; + mBranch = ASMIT_BNE; + mBranchIns = mIns[i + 3].mIns; + break; + } + + if (mIns[i + 0].mType == ASMIT_TYA && + mIns[i + 1].mType == ASMIT_CLC && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 1 && + mIns[i + 3].mType == ASMIT_TAY && + mIns[i + 4].mType == ASMIT_LDA && + mIns[i + 5].mType == ASMIT_ADC && mIns[i + 5].mMode == ASMIM_IMMEDIATE && mIns[i + 5].mAddress == 0 && + mIns[i + 6].mType == ASMIT_STA && mIns[i + 4].SameEffectiveAddress(mIns[i + 6]) && + HasAsmInstructionMode(ASMIT_INC, mIns[i + 6].mMode) && + !(mIns[i + 6].mLive & (LIVE_CPU_REG_C | LIVE_CPU_REG_Z))) + { + changed = true; + + NativeCodeBasicBlock* iblock = proc->AllocateBlock(); + NativeCodeBasicBlock* fblock = proc->AllocateBlock(); + + fblock->mTrueJump = mTrueJump; + fblock->mFalseJump = mFalseJump; + fblock->mBranch = mBranch; + fblock->mBranchIns = mBranchIns; + + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_NOP; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + mIns[i + 3].mType = ASMIT_INY; mIns[i + 3].mLive |= LIVE_CPU_REG_Z; + + fblock->mIns.Push(mIns[i + 4]); + + for (int j = i + 7; j < mIns.Size(); j++) + fblock->mIns.Push(mIns[j]); + iblock->mIns.Push(mIns[i + 6]); + + + mIns.SetSize(i + 4); + iblock->mIns[0].mType = ASMIT_INC; + + iblock->mTrueJump = fblock; + iblock->mBranch = ASMIT_JMP; + + mTrueJump = fblock; + mFalseJump = iblock; + mBranch = ASMIT_BNE; + mBranchIns = mIns[i + 3].mIns; + break; + } + + } +#endif + +#if 1 + if (i + 5 < mIns.Size() && + mIns[i + 0].ChangesAccuAndFlag() && + mIns[i + 1].mType == ASMIT_CMP && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 0x01 && + mIns[i + 2].mType == ASMIT_LDA && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 0x00 && + mIns[i + 3].mType == ASMIT_ADC && mIns[i + 3].mMode == ASMIM_IMMEDIATE && mIns[i + 3].mAddress == 0xff && + mIns[i + 4].mType == ASMIT_AND && mIns[i + 4].mMode == ASMIM_IMMEDIATE && + mIns[i + 5].mType == ASMIT_EOR && mIns[i + 5].mMode == ASMIM_IMMEDIATE) + { + uint8 veq = mIns[i + 4].mAddress ^ mIns[i + 5].mAddress, vne = mIns[i + 5].mAddress; + + changed = true; + + NativeCodeBasicBlock* eblock = proc->AllocateBlock(); + NativeCodeBasicBlock* neblock = proc->AllocateBlock(); + NativeCodeBasicBlock* rblock = proc->AllocateBlock(); + + rblock->mTrueJump = mTrueJump; + rblock->mFalseJump = mFalseJump; + rblock->mBranch = mBranch; + rblock->mBranchIns = mBranchIns; + + for (int j = i + 6; j < mIns.Size(); j++) + rblock->mIns.Push(mIns[j]); + mIns.SetSize(i + 1); + mIns[i + 0].mLive |= LIVE_CPU_REG_Z; + + mTrueJump = neblock; + mFalseJump = eblock; + mBranch = ASMIT_BNE; + mBranchIns = mIns[i + 0].mIns; + + if (veq != 0) + eblock->mIns.Push(NativeCodeInstruction(mIns[i].mIns, ASMIT_LDA, ASMIM_IMMEDIATE, veq)); + neblock->mIns.Push(NativeCodeInstruction(mIns[i].mIns, ASMIT_LDA, ASMIM_IMMEDIATE, vne)); + + eblock->Close(mIns[i].mIns, rblock, nullptr, ASMIT_JMP); + neblock->Close(mIns[i].mIns, rblock, nullptr, ASMIT_JMP); + break; + } +#endif +#if 1 + if (i + 4 < mIns.Size() && + mIns[i + 0].mType == ASMIT_TAX && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 0x00 && + mIns[i + 2].mType == ASMIT_ROL && mIns[i + 2].mMode == ASMIM_IMPLIED && + mIns[i + 3].mType == ASMIT_TAY && + mIns[i + 4].mType == ASMIT_TXA && !(mIns[i + 4].mLive & (LIVE_CPU_REG_C | LIVE_CPU_REG_Z | LIVE_CPU_REG_X))) + { + changed = true; + + NativeCodeBasicBlock* iblock = proc->AllocateBlock(); + NativeCodeBasicBlock* rblock = proc->AllocateBlock(); + + rblock->mTrueJump = mTrueJump; + rblock->mFalseJump = mFalseJump; + rblock->mBranch = mBranch; + rblock->mBranchIns = mBranchIns; + + const InterInstruction* iins = mIns[i].mIns; + + for (int j = i + 5; j < mIns.Size(); j++) + rblock->mIns.Push(mIns[j]); + mIns.SetSize(i); + mIns.Push(NativeCodeInstruction(iins, ASMIT_LDY, ASMIM_IMMEDIATE, 0)); + + mTrueJump = iblock; + mFalseJump = rblock; + mBranch = ASMIT_BCS; + mBranchIns = iins; + + iblock->mIns.Push(NativeCodeInstruction(iins, ASMIT_INY)); + + iblock->Close(iins, rblock, nullptr, ASMIT_JMP); + break; + } + if (i + 4 < mIns.Size() && + mIns[i + 0].mType == ASMIT_TAY && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 0x00 && + mIns[i + 2].mType == ASMIT_ROL && mIns[i + 2].mMode == ASMIM_IMPLIED && + mIns[i + 3].mType == ASMIT_TAX && + mIns[i + 4].mType == ASMIT_TYA && !(mIns[i + 4].mLive & (LIVE_CPU_REG_C | LIVE_CPU_REG_Z | LIVE_CPU_REG_Y))) + { + changed = true; + + NativeCodeBasicBlock* iblock = proc->AllocateBlock(); + NativeCodeBasicBlock* rblock = proc->AllocateBlock(); + + rblock->mTrueJump = mTrueJump; + rblock->mFalseJump = mFalseJump; + rblock->mBranch = mBranch; + rblock->mBranchIns = mBranchIns; + + const InterInstruction* iins = mIns[i].mIns; + + for (int j = i + 5; j < mIns.Size(); j++) + rblock->mIns.Push(mIns[j]); + mIns.SetSize(i); + mIns.Push(NativeCodeInstruction(iins, ASMIT_LDX, ASMIM_IMMEDIATE, 0)); + + mTrueJump = iblock; + mFalseJump = rblock; + mBranch = ASMIT_BCS; + mBranchIns = iins; + + iblock->mIns.Push(NativeCodeInstruction(iins, ASMIT_INX)); + + iblock->Close(iins, rblock, nullptr, ASMIT_JMP); + break; + } + +#endif +#if 1 + if (i + 3 < mIns.Size() && + mIns[i + 0].ChangesAccuAndFlag() && + mIns[i + 1].mType == ASMIT_CMP && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 0x01 && + mIns[i + 2].mType == ASMIT_LDA && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 0x00 && + mIns[i + 3].mType == ASMIT_ROL && mIns[i + 3].mMode == ASMIM_IMPLIED) + { + changed = true; + + NativeCodeBasicBlock* eblock = proc->AllocateBlock(); + NativeCodeBasicBlock* neblock = proc->AllocateBlock(); + NativeCodeBasicBlock* rblock = proc->AllocateBlock(); + + rblock->mTrueJump = mTrueJump; + rblock->mFalseJump = mFalseJump; + rblock->mBranch = mBranch; + rblock->mBranchIns = mBranchIns; + + for (int j = i + 4; j < mIns.Size(); j++) + rblock->mIns.Push(mIns[j]); + mIns.SetSize(i + 1); + mIns[i + 0].mLive |= LIVE_CPU_REG_Z; + + mTrueJump = neblock; + mFalseJump = eblock; + mBranch = ASMIT_BNE; + mBranchIns = mIns[i + 0].mIns; + + neblock->mIns.Push(NativeCodeInstruction(mIns[i].mIns, ASMIT_LDA, ASMIM_IMMEDIATE, 1)); + + eblock->Close(mIns[i].mIns, rblock, nullptr, ASMIT_JMP); + neblock->Close(mIns[i].mIns, rblock, nullptr, ASMIT_JMP); + break; + } +#endif +#if 1 + if (i + 6 < mIns.Size() && + mIns[i + 0].mType == ASMIT_TAY && + mIns[i + 1].mType == ASMIT_ASL && mIns[i + 1].mMode == ASMIM_IMPLIED && + mIns[i + 2].mType == ASMIT_LDA && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 0 && + mIns[i + 3].mType == ASMIT_ADC && mIns[i + 3].mMode == ASMIM_IMMEDIATE && mIns[i + 3].mAddress == 0xff && + mIns[i + 4].mType == ASMIT_EOR && mIns[i + 4].mMode == ASMIM_IMMEDIATE && mIns[i + 4].mAddress == 0xff && + mIns[i + 5].mType == ASMIT_STA && mIns[i + 5].mMode == ASMIM_ZERO_PAGE && + mIns[i + 6].mType == ASMIT_TYA && + !(mIns[i + 6].mLive & (LIVE_CPU_REG_Y | LIVE_CPU_REG_C | LIVE_CPU_REG_Z))) + { + changed = true; + + NativeCodeBasicBlock* eblock = proc->AllocateBlock(); + NativeCodeBasicBlock* neblock = proc->AllocateBlock(); + NativeCodeBasicBlock* rblock = proc->AllocateBlock(); + + rblock->mTrueJump = mTrueJump; + rblock->mFalseJump = mFalseJump; + rblock->mBranch = mBranch; + rblock->mBranchIns = mBranchIns; + + rblock->mIns.Push(NativeCodeInstruction(mIns[i].mIns, ASMIT_STY, mIns[i + 5])); + for (int j = i + 7; j < mIns.Size(); j++) + rblock->mIns.Push(mIns[j]); + mIns.SetSize(i + 1); + mIns[i + 0].mType = ASMIT_ORA; mIns[i + 0].mMode = ASMIM_IMMEDIATE; mIns[i + 0].mAddress = 0x00; + mIns[i + 0].mLive |= LIVE_CPU_REG_A | LIVE_CPU_REG_Z; + + mTrueJump = eblock; + mFalseJump = neblock; + mBranch = ASMIT_BPL; + mBranchIns = mIns[i + 0].mIns; + + eblock->mIns.Push(NativeCodeInstruction(mIns[i].mIns, ASMIT_LDY, ASMIM_IMMEDIATE, 0x00)); + neblock->mIns.Push(NativeCodeInstruction(mIns[i].mIns, ASMIT_LDY, ASMIM_IMMEDIATE, 0xff)); + + eblock->Close(mIns[i].mIns, rblock, nullptr, ASMIT_JMP); + neblock->Close(mIns[i].mIns, rblock, nullptr, ASMIT_JMP); + break; + } +#endif +#if 1 + if (i + 4 < mIns.Size() && + (mIns[i + 0].mType == ASMIT_CPX || mIns[i + 0].mType == ASMIT_CPY) && mIns[i + 0].mMode == ASMIM_IMMEDIATE && mIns[i + 0].mAddress == 0x01 && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 0x00 && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 0xff && + mIns[i + 3].mType == ASMIT_AND && mIns[i + 3].mMode == ASMIM_IMMEDIATE && + mIns[i + 4].mType == ASMIT_EOR && mIns[i + 4].mMode == ASMIM_IMMEDIATE) + { + uint8 veq = mIns[i + 3].mAddress ^ mIns[i + 4].mAddress, vne = mIns[i + 4].mAddress; + + changed = true; + + NativeCodeBasicBlock* eblock = proc->AllocateBlock(); + NativeCodeBasicBlock* neblock = proc->AllocateBlock(); + NativeCodeBasicBlock* rblock = proc->AllocateBlock(); + + rblock->mTrueJump = mTrueJump; + rblock->mFalseJump = mFalseJump; + rblock->mBranch = mBranch; + rblock->mBranchIns = mBranchIns; + + for (int j = i + 5; j < mIns.Size(); j++) + rblock->mIns.Push(mIns[j]); + mIns.SetSize(i + 1); + if (mIns[i + 0].mType == ASMIT_CPX) + mIns[i + 0].mType = ASMIT_TXA; + else if (mIns[i + 0].mType == ASMIT_CPY) + mIns[i + 0].mType = ASMIT_TYA; + mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 0].mLive |= LIVE_CPU_REG_Z; + + mTrueJump = neblock; + mFalseJump = eblock; + mBranch = ASMIT_BNE; + mBranchIns = mIns[i + 0].mIns; + + if (veq != 0) + eblock->mIns.Push(NativeCodeInstruction(mIns[i].mIns, ASMIT_LDA, ASMIM_IMMEDIATE, veq)); + neblock->mIns.Push(NativeCodeInstruction(mIns[i].mIns, ASMIT_LDA, ASMIM_IMMEDIATE, vne)); + + eblock->Close(mIns[i].mIns, rblock, nullptr, ASMIT_JMP); + neblock->Close(mIns[i].mIns, rblock, nullptr, ASMIT_JMP); + break; + } +#endif +#if 1 + if (i + 4 < mIns.Size() && + mIns[i + 0].ChangesAccuAndFlag() && + mIns[i + 1].mType == ASMIT_CMP && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 0x01 && + mIns[i + 2].mType == ASMIT_LDA && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 0x00 && + mIns[i + 3].mType == ASMIT_ADC && mIns[i + 3].mMode == ASMIM_IMMEDIATE && mIns[i + 3].mAddress == 0xff && + mIns[i + 4].mType == ASMIT_AND && mIns[i + 4].mMode == ASMIM_IMMEDIATE) + { + uint8 veq = mIns[i + 4].mAddress, vne = 0; + + changed = true; + + NativeCodeBasicBlock* eblock = proc->AllocateBlock(); + NativeCodeBasicBlock* neblock = proc->AllocateBlock(); + NativeCodeBasicBlock* rblock = proc->AllocateBlock(); + + rblock->mTrueJump = mTrueJump; + rblock->mFalseJump = mFalseJump; + rblock->mBranch = mBranch; + rblock->mBranchIns = mBranchIns; + + for (int j = i + 5; j < mIns.Size(); j++) + rblock->mIns.Push(mIns[j]); + mIns.SetSize(i + 1); + mIns[i + 0].mLive |= LIVE_CPU_REG_Z; + + mTrueJump = neblock; + mFalseJump = eblock; + mBranch = ASMIT_BNE; + mBranchIns = mIns[i + 0].mIns; + + if (veq != 0) + eblock->mIns.Push(NativeCodeInstruction(mIns[i].mIns, ASMIT_LDA, ASMIM_IMMEDIATE, veq)); + neblock->mIns.Push(NativeCodeInstruction(mIns[i].mIns, ASMIT_LDA, ASMIM_IMMEDIATE, vne)); + + eblock->Close(mIns[i].mIns, rblock, nullptr, ASMIT_JMP); + neblock->Close(mIns[i].mIns, rblock, nullptr, ASMIT_JMP); + break; + } +#endif +#if 1 + if (i + 4 < mIns.Size() && + mIns[i + 0].ChangesAccuAndFlag() && + mIns[i + 1].mType == ASMIT_CMP && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 0x01 && + mIns[i + 2].mType == ASMIT_LDA && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 0x00 && + mIns[i + 3].mType == ASMIT_ADC && mIns[i + 3].mMode == ASMIM_IMMEDIATE && mIns[i + 3].mAddress == 0xff && + mIns[i + 4].mType == ASMIT_EOR && mIns[i + 4].mMode == ASMIM_IMMEDIATE && mIns[i + 4].mAddress == 0xff) + { + changed = true; + + NativeCodeBasicBlock* eblock = proc->AllocateBlock(); + NativeCodeBasicBlock* neblock = proc->AllocateBlock(); + NativeCodeBasicBlock* rblock = proc->AllocateBlock(); + + rblock->mTrueJump = mTrueJump; + rblock->mFalseJump = mFalseJump; + rblock->mBranch = mBranch; + rblock->mBranchIns = mBranchIns; + + for (int j = i + 5; j < mIns.Size(); j++) + rblock->mIns.Push(mIns[j]); + mIns.SetSize(i + 1); + mIns[i + 0].mLive |= LIVE_CPU_REG_Z; + + mTrueJump = neblock; + mFalseJump = eblock; + mBranch = ASMIT_BNE; + mBranchIns = mIns[i + 0].mIns; + + neblock->mIns.Push(NativeCodeInstruction(mIns[i].mIns, ASMIT_LDA, ASMIM_IMMEDIATE, 0xff)); + + eblock->Close(mIns[i].mIns, rblock, nullptr, ASMIT_JMP); + neblock->Close(mIns[i].mIns, rblock, nullptr, ASMIT_JMP); + break; + } +#endif +#if 1 + if (i + 3 < mIns.Size() && + mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_IMMEDIATE && mIns[i + 0].mAddress == 0x00 && + mIns[i + 1].mType == ASMIT_ADC && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 0xff && + mIns[i + 2].mType == ASMIT_EOR && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 0xff && + mIns[i + 3].mType == ASMIT_TAX && !(mIns[i + 3].mLive & (LIVE_CPU_REG_C | LIVE_CPU_REG_Z | LIVE_CPU_REG_A))) + { + changed = true; + + NativeCodeBasicBlock* iblock = proc->AllocateBlock(); + NativeCodeBasicBlock* rblock = proc->AllocateBlock(); + + rblock->mTrueJump = mTrueJump; + rblock->mFalseJump = mFalseJump; + rblock->mBranch = mBranch; + rblock->mBranchIns = mBranchIns; + + const InterInstruction* iins = mIns[i].mIns; + + for (int j = i + 4; j < mIns.Size(); j++) + rblock->mIns.Push(mIns[j]); + mIns.SetSize(i); + mIns.Push(NativeCodeInstruction(iins, ASMIT_LDX, ASMIM_IMMEDIATE, 0)); + + mTrueJump = iblock; + mFalseJump = rblock; + mBranch = ASMIT_BCS; + mBranchIns = iins; + + iblock->mIns.Push(NativeCodeInstruction(iins, ASMIT_LDX, ASMIM_IMMEDIATE, 0xff)); + + iblock->Close(iins, rblock, nullptr, ASMIT_JMP); + break; + } + +#endif +#if 1 + if (i + 3 < mIns.Size() && + mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_IMMEDIATE && mIns[i + 0].mAddress == 0x00 && + mIns[i + 1].mType == ASMIT_ADC && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 0xff && + mIns[i + 2].mType == ASMIT_AND && mIns[i + 2].mMode == ASMIM_IMMEDIATE && + mIns[i + 3].mType == ASMIT_EOR && mIns[i + 3].mMode == ASMIM_IMMEDIATE) + { + uint8 vcc = mIns[i + 2].mAddress ^ mIns[i + 3].mAddress, vcs = mIns[i + 3].mAddress; + + changed = true; + + NativeCodeBasicBlock* ccblock = proc->AllocateBlock(); + NativeCodeBasicBlock* rblock = proc->AllocateBlock(); + + rblock->mTrueJump = mTrueJump; + rblock->mFalseJump = mFalseJump; + rblock->mBranch = mBranch; + rblock->mBranchIns = mBranchIns; + + const InterInstruction* iins = mIns[i].mIns; + + for (int j = i + 4; j < mIns.Size(); j++) + rblock->mIns.Push(mIns[j]); + mIns.SetSize(i + 1); + mIns[i].mAddress = vcs; + + mTrueJump = rblock; + mFalseJump = ccblock; + mBranch = ASMIT_BCS; + + ccblock->mIns.Push(NativeCodeInstruction(mIns[i].mIns, ASMIT_LDA, ASMIM_IMMEDIATE, vcc)); + ccblock->Close(mIns[i].mIns, rblock, nullptr, ASMIT_JMP); + break; + } +#endif + +#if 1 + if (i + 12 < mIns.Size()) + { + // Check for sign extending + if (mIns[i + 0].mType == ASMIT_LDA && + mIns[i + 1].mType == ASMIT_ASL && mIns[i + 1].mMode == ASMIM_IMPLIED && + mIns[i + 2].mType == ASMIT_LDA && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 0x00 && + mIns[i + 3].mType == ASMIT_ADC && mIns[i + 3].mMode == ASMIM_IMMEDIATE && mIns[i + 3].mAddress == 0xff && + mIns[i + 4].mType == ASMIT_EOR && mIns[i + 4].mMode == ASMIM_IMMEDIATE && mIns[i + 4].mAddress == 0xff) + { + // Extended byte is saved + if (mIns[i + 5].mType == ASMIT_STA || mIns[i + 5].mType == ASMIT_TAX || mIns[i + 5].mType == ASMIT_TAY) + { + // There is an addition direct after it + if (mIns[i + 6].mType == ASMIT_LDA && mIns[i + 7].mType == ASMIT_CLC && mIns[i + 8].mType == ASMIT_ADC && mIns[i + 9].mType == ASMIT_STA && + mIns[i + 11].mType == ASMIT_ADC && mIns[i + 12].mType == ASMIT_STA && !(mIns[i + 12].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C | LIVE_CPU_REG_Z))) + { + // Uses the sign extended source + if (mIns[i + 6].SameEffectiveAddress(mIns[i + 0]) || mIns[i + 8].SameEffectiveAddress(mIns[i + 0])) + { + // Using the result of the sign extension for the upper byte add + if (mIns[i + 5].mType == ASMIT_TAX && mIns[i + 10].mType == ASMIT_TXA && !(mIns[i + 10].mLive & LIVE_CPU_REG_X) || + mIns[i + 5].mType == ASMIT_TAY && mIns[i + 10].mType == ASMIT_TYA && !(mIns[i + 10].mLive & LIVE_CPU_REG_Y) || + mIns[i + 5].mType == ASMIT_STA && mIns[i + 10].mType == ASMIT_LDA && mIns[i + 5].SameEffectiveAddress(mIns[i + 10]) && !(mIns[i + 10].mLive & LIVE_MEM)) + { + // Can change to inc + if (mIns[i + 11].SameEffectiveAddress(mIns[i + 12]) && HasAsmInstructionMode(ASMIT_INC, mIns[i + 11].mMode)) + { + changed = true; + + NativeCodeBasicBlock* iblock = proc->AllocateBlock(); + NativeCodeBasicBlock* dblock = proc->AllocateBlock(); + NativeCodeBasicBlock* ablock = proc->AllocateBlock(); + NativeCodeBasicBlock* fblock = proc->AllocateBlock(); + + fblock->mTrueJump = mTrueJump; + fblock->mFalseJump = mFalseJump; + fblock->mBranch = mBranch; + + dblock->mIns.Push(mIns[i + 11]); + dblock->mIns[0].mType = ASMIT_DEC; + dblock->mTrueJump = ablock; + dblock->mBranch = ASMIT_JMP; + + ablock->mIns.Push(mIns[i + 7]); + ablock->mIns.Push(mIns[i + 8]); + ablock->mIns.Push(mIns[i + 9]); + if (mIns[i + 8].SameEffectiveAddress(mIns[i + 0])) + { + ablock->mIns[1].CopyMode(mIns[i + 6]); + } + + ablock->mBranch = ASMIT_BCC; + ablock->mTrueJump = fblock; + ablock->mFalseJump = iblock; + + iblock->mIns.Push(mIns[i + 11]); + iblock->mIns[0].mType = ASMIT_INC; + iblock->mTrueJump = fblock; + iblock->mBranch = ASMIT_JMP; + + mTrueJump = ablock; + mFalseJump = dblock; + mBranch = ASMIT_BPL; + + for (int j = i + 13; j < mIns.Size(); j++) + fblock->mIns.Push(mIns[j]); + mIns.SetSize(i + 1); + break; + } + else if (mIns[i + 5].mType == ASMIT_TAX && HasAsmInstructionMode(ASMIT_LDX, mIns[i + 11].mMode) && HasAsmInstructionMode(ASMIT_STX, mIns[i + 12].mMode)) + { + // Can't do direct inc, so fallback to x register + changed = true; + + NativeCodeBasicBlock* iblock = proc->AllocateBlock(); + NativeCodeBasicBlock* dblock = proc->AllocateBlock(); + NativeCodeBasicBlock* ablock = proc->AllocateBlock(); + NativeCodeBasicBlock* fblock = proc->AllocateBlock(); + + fblock->mTrueJump = mTrueJump; + fblock->mFalseJump = mFalseJump; + fblock->mBranch = mBranch; + + dblock->mIns.Push(NativeCodeInstruction(mIns[i].mIns, ASMIT_DEX)); + dblock->mTrueJump = ablock; + dblock->mBranch = ASMIT_JMP; + + ablock->mIns.Push(mIns[i + 7]); + ablock->mIns.Push(mIns[i + 8]); + ablock->mIns.Push(mIns[i + 9]); + if (mIns[i + 8].SameEffectiveAddress(mIns[i + 0])) + { + ablock->mIns[1].CopyMode(mIns[i + 6]); + } + + ablock->mBranch = ASMIT_BCC; + ablock->mTrueJump = fblock; + ablock->mFalseJump = iblock; + + iblock->mIns.Push(NativeCodeInstruction(mIns[i].mIns, ASMIT_INX)); + iblock->mTrueJump = fblock; + iblock->mBranch = ASMIT_JMP; + + mTrueJump = ablock; + mFalseJump = dblock; + mBranch = ASMIT_BPL; + + fblock->mIns.Push(NativeCodeInstruction(mIns[i].mIns, ASMIT_STX, mIns[i + 12])); + for (int j = i + 13; j < mIns.Size(); j++) + fblock->mIns.Push(mIns[j]); + + mIns.Insert(i, NativeCodeInstruction(mIns[i].mIns, ASMIT_LDX, mIns[i + 11])); + mIns[i + 1].mLive |= LIVE_CPU_REG_X; + mIns.SetSize(i + 2); + + break; + + } + } + } + } + } + } + } +#endif + if (i + 2 < mIns.Size()) + { + if (mIns[i + 0].mType == ASMIT_LDA && + mIns[i + 1].mType == ASMIT_ADC && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 0 && + mIns[i + 2].mType == ASMIT_STA && mIns[i + 0].SameEffectiveAddress(mIns[i + 2]) && + HasAsmInstructionMode(ASMIT_INC, mIns[i + 2].mMode) && + !(mIns[i + 2].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C | LIVE_CPU_REG_Z))) + { + changed = true; + + NativeCodeBasicBlock * iblock = proc->AllocateBlock(); + NativeCodeBasicBlock * fblock = proc->AllocateBlock(); + + fblock->mTrueJump = mTrueJump; + fblock->mFalseJump = mFalseJump; + fblock->mBranch = mBranch; + fblock->mBranchIns = mBranchIns; + + const InterInstruction* iins = mIns[0].mIns; + + for (int j = i + 3; j < mIns.Size(); j++) + fblock->mIns.Push(mIns[j]); + iblock->mIns.Push(mIns[i + 2]); + mIns.SetSize(i); + iblock->mIns[0].mType = ASMIT_INC; + iblock->mTrueJump = fblock; + iblock->mBranch = ASMIT_JMP; + + mTrueJump = fblock; + mFalseJump = iblock; + mBranch = ASMIT_BCC; + mBranchIns = iins; + break; + } + else if (mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_IMMEDIATE && mIns[i + 0].mAddress == 0 && + mIns[i + 1].mType == ASMIT_ADC && + mIns[i + 2].mType == ASMIT_STA && mIns[i + 1].SameEffectiveAddress(mIns[i + 2]) && + HasAsmInstructionMode(ASMIT_INC, mIns[i + 2].mMode) && + !(mIns[i + 2].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C | LIVE_CPU_REG_Z))) + { + changed = true; + + NativeCodeBasicBlock* iblock = proc->AllocateBlock(); + NativeCodeBasicBlock* fblock = proc->AllocateBlock(); + + fblock->mTrueJump = mTrueJump; + fblock->mFalseJump = mFalseJump; + fblock->mBranch = mBranch; + fblock->mBranchIns = mBranchIns; + + const InterInstruction* iins = mIns[0].mIns; + + for (int j = i + 3; j < mIns.Size(); j++) + fblock->mIns.Push(mIns[j]); + iblock->mIns.Push(mIns[i + 2]); + mIns.SetSize(i); + iblock->mIns[0].mType = ASMIT_INC; + iblock->mTrueJump = fblock; + iblock->mBranch = ASMIT_JMP; + + mTrueJump = fblock; + mFalseJump = iblock; + mBranch = ASMIT_BCC; + mBranchIns = iins; + break; + } + else if (mIns[i + 0].mType == ASMIT_LDA && + mIns[i + 1].mType == ASMIT_ADC && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 0xff && + mIns[i + 2].mType == ASMIT_STA && mIns[i + 0].SameEffectiveAddress(mIns[i + 2]) && + HasAsmInstructionMode(ASMIT_DEC, mIns[i + 2].mMode) && + !(mIns[i + 2].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C | LIVE_CPU_REG_Z))) + { + changed = true; + + NativeCodeBasicBlock* iblock = proc->AllocateBlock(); + NativeCodeBasicBlock* fblock = proc->AllocateBlock(); + + fblock->mTrueJump = mTrueJump; + fblock->mFalseJump = mFalseJump; + fblock->mBranch = mBranch; + fblock->mBranchIns = mBranchIns; + + const InterInstruction* iins = mIns[0].mIns; + + for (int j = i + 3; j < mIns.Size(); j++) + fblock->mIns.Push(mIns[j]); + iblock->mIns.Push(mIns[i + 2]); + mIns.SetSize(i); + iblock->mIns[0].mType = ASMIT_DEC; + iblock->mTrueJump = fblock; + iblock->mBranch = ASMIT_JMP; + + mTrueJump = fblock; + mFalseJump = iblock; + mBranch = ASMIT_BCS; + mBranchIns = iins; + break; + } + else if (mIns[i + 0].mType == ASMIT_TXA && + mIns[i + 1].mType == ASMIT_ADC && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 0 && + mIns[i + 2].mType == ASMIT_TAX && + !(mIns[i + 2].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C | LIVE_CPU_REG_Z))) + { + changed = true; + + NativeCodeBasicBlock* iblock = proc->AllocateBlock(); + NativeCodeBasicBlock* fblock = proc->AllocateBlock(); + + fblock->mTrueJump = mTrueJump; + fblock->mFalseJump = mFalseJump; + fblock->mBranch = mBranch; + fblock->mBranchIns = mBranchIns; + + const InterInstruction* iins(mIns[i].mIns); + + for (int j = i + 3; j < mIns.Size(); j++) + fblock->mIns.Push(mIns[j]); + mIns.SetSize(i); + + iblock->mIns.Push(NativeCodeInstruction(iins, ASMIT_INX)); + iblock->mTrueJump = fblock; + iblock->mBranch = ASMIT_JMP; + + mTrueJump = fblock; + mFalseJump = iblock; + mBranch = ASMIT_BCC; + mBranchIns = iins; + break; + } + else if (mIns[i + 0].mType == ASMIT_TYA && + mIns[i + 1].mType == ASMIT_ADC && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 0 && + mIns[i + 2].mType == ASMIT_STA && HasAsmInstructionMode(ASMIT_STY, mIns[i + 2].mMode) && + !(mIns[i + 2].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Y | LIVE_CPU_REG_C | LIVE_CPU_REG_Z))) + { + changed = true; + + NativeCodeBasicBlock* iblock = proc->AllocateBlock(); + NativeCodeBasicBlock* fblock = proc->AllocateBlock(); + + fblock->mTrueJump = mTrueJump; + fblock->mFalseJump = mFalseJump; + fblock->mBranch = mBranch; + fblock->mBranchIns = mBranchIns; + + const InterInstruction* iins(mIns[i].mIns); + + for (int j = i + 2; j < mIns.Size(); j++) + fblock->mIns.Push(mIns[j]); + mIns.SetSize(i); + + fblock->mIns[0].mType = ASMIT_STY; + + iblock->mIns.Push(NativeCodeInstruction(iins, ASMIT_INY)); + iblock->mTrueJump = fblock; + iblock->mBranch = ASMIT_JMP; + + mTrueJump = fblock; + mFalseJump = iblock; + mBranch = ASMIT_BCC; + mBranchIns = iins; + break; + } + else if (mIns[i + 0].mType == ASMIT_TXA && + mIns[i + 1].mType == ASMIT_ADC && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 0 && + mIns[i + 2].mType == ASMIT_STA && HasAsmInstructionMode(ASMIT_STX, mIns[i + 2].mMode) && + !(mIns[i + 2].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_X | LIVE_CPU_REG_C | LIVE_CPU_REG_Z))) + { + changed = true; + + NativeCodeBasicBlock* iblock = proc->AllocateBlock(); + NativeCodeBasicBlock* fblock = proc->AllocateBlock(); + + fblock->mTrueJump = mTrueJump; + fblock->mFalseJump = mFalseJump; + fblock->mBranch = mBranch; + fblock->mBranchIns = mBranchIns; + + const InterInstruction* iins(mIns[i].mIns); + + for (int j = i + 2; j < mIns.Size(); j++) + fblock->mIns.Push(mIns[j]); + mIns.SetSize(i); + + fblock->mIns[0].mType = ASMIT_STX; + + iblock->mIns.Push(NativeCodeInstruction(iins, ASMIT_INX)); + iblock->mTrueJump = fblock; + iblock->mBranch = ASMIT_JMP; + + mTrueJump = fblock; + mFalseJump = iblock; + mBranch = ASMIT_BCC; + mBranchIns = iins; + break; + } + else if (mIns[i + 0].mType == ASMIT_TYA && + mIns[i + 1].mType == ASMIT_ADC && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 0 && + mIns[i + 2].mType == ASMIT_TAY && + !(mIns[i + 2].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C | LIVE_CPU_REG_Z))) + { + changed = true; + + NativeCodeBasicBlock* iblock = proc->AllocateBlock(); + NativeCodeBasicBlock* fblock = proc->AllocateBlock(); + + fblock->mTrueJump = mTrueJump; + fblock->mFalseJump = mFalseJump; + fblock->mBranch = mBranch; + fblock->mBranchIns = mBranchIns; + + const InterInstruction* iins(mIns[i].mIns); + + for (int j = i + 3; j < mIns.Size(); j++) + fblock->mIns.Push(mIns[j]); + mIns.SetSize(i); + + iblock->mIns.Push(NativeCodeInstruction(iins, ASMIT_INY)); + iblock->mTrueJump = fblock; + iblock->mBranch = ASMIT_JMP; + + mTrueJump = fblock; + mFalseJump = iblock; + mBranch = ASMIT_BCC; + mBranchIns = iins; + break; + } + else if (mIns[i + 0].mType == ASMIT_LDA && HasAsmInstructionMode(ASMIT_LDY, mIns[i + 0].mMode) && + mIns[i + 1].mType == ASMIT_ADC && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 0 && + mIns[i + 2].mType == ASMIT_TAY && + !(mIns[i + 2].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C | LIVE_CPU_REG_Z))) + { + changed = true; + + NativeCodeBasicBlock* iblock = proc->AllocateBlock(); + NativeCodeBasicBlock* fblock = proc->AllocateBlock(); + + fblock->mTrueJump = mTrueJump; + fblock->mFalseJump = mFalseJump; + fblock->mBranch = mBranch; + fblock->mBranchIns = mBranchIns; + + const InterInstruction* iins(mIns[i].mIns); + + for (int j = i + 3; j < mIns.Size(); j++) + fblock->mIns.Push(mIns[j]); + mIns[i].mType = ASMIT_LDY; + mIns.SetSize(i + 1); + + iblock->mIns.Push(NativeCodeInstruction(iins, ASMIT_INY)); + iblock->mTrueJump = fblock; + iblock->mBranch = ASMIT_JMP; + + mTrueJump = fblock; + mFalseJump = iblock; + mBranch = ASMIT_BCC; + mBranchIns = iins; + break; + } + else if (mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_IMMEDIATE && mIns[i + 0].mAddress == 0 && + mIns[i + 1].mType == ASMIT_ADC && HasAsmInstructionMode(ASMIT_LDY, mIns[i + 1].mMode) && + mIns[i + 2].mType == ASMIT_TAY && + !(mIns[i + 2].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C | LIVE_CPU_REG_Z))) + { + changed = true; + + NativeCodeBasicBlock* iblock = proc->AllocateBlock(); + NativeCodeBasicBlock* fblock = proc->AllocateBlock(); + + fblock->mTrueJump = mTrueJump; + fblock->mFalseJump = mFalseJump; + fblock->mBranch = mBranch; + fblock->mBranchIns = mBranchIns; + + const InterInstruction* iins(mIns[i].mIns); + + for (int j = i + 3; j < mIns.Size(); j++) + fblock->mIns.Push(mIns[j]); + mIns[i] = mIns[i + 1]; + mIns[i].mType = ASMIT_LDY; + mIns.SetSize(i + 1); + + iblock->mIns.Push(NativeCodeInstruction(iins, ASMIT_INY)); + iblock->mTrueJump = fblock; + iblock->mBranch = ASMIT_JMP; + + mTrueJump = fblock; + mFalseJump = iblock; + mBranch = ASMIT_BCC; + mBranchIns = iins; + break; + } + else if (mIns[i + 0].mType == ASMIT_LDA && + mIns[i + 1].mType == ASMIT_SBC && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 0 && + mIns[i + 2].mType == ASMIT_STA && mIns[i + 0].SameEffectiveAddress(mIns[i + 2]) && + HasAsmInstructionMode(ASMIT_DEC, mIns[i + 2].mMode) && + !(mIns[i + 2].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C | LIVE_CPU_REG_Z))) + { + changed = true; + + NativeCodeBasicBlock* iblock = proc->AllocateBlock(); + NativeCodeBasicBlock* fblock = proc->AllocateBlock(); + + fblock->mTrueJump = mTrueJump; + fblock->mFalseJump = mFalseJump; + fblock->mBranch = mBranch; + fblock->mBranchIns = mBranchIns; + + const InterInstruction* iins(mIns[i].mIns); + + for (int j = i + 3; j < mIns.Size(); j++) + fblock->mIns.Push(mIns[j]); + iblock->mIns.Push(mIns[i + 2]); + mIns.SetSize(i); + iblock->mIns[0].mType = ASMIT_DEC; + iblock->mTrueJump = fblock; + iblock->mBranch = ASMIT_JMP; + + mTrueJump = fblock; + mFalseJump = iblock; + mBranch = ASMIT_BCS; + mBranchIns = iins; + break; + } + + } + + if (mIns[i].mType == ASMIT_CLC) + carry = 0; + else if (mIns[i].mType == ASMIT_SEC) + carry = 0; + else if (mIns[i].ChangesCarry()) + carry = -1; + } + +#if 1 + if (mIns.Size() >= 4 && mTrueJump && mFalseJump && mTrueJump->mEntryRequiredRegs.Size() > 0 && mFalseJump->mEntryRequiredRegs.Size() > 0) + { + int sz = mIns.Size(); + + if (mIns[sz - 4].mType == ASMIT_TYA && + mIns[sz - 3].mType == ASMIT_CLC && + mIns[sz - 2].mType == ASMIT_ADC && mIns[sz - 2].mMode == ASMIM_IMMEDIATE && mIns[sz - 2].mAddress == 1 && + mIns[sz - 1].mType == ASMIT_TAY && !(mIns[sz - 1].mLive & LIVE_CPU_REG_A) && + (mBranch == ASMIT_BCC || mBranch == ASMIT_BCS) && + !mTrueJump->mEntryRequiredRegs[CPU_REG_C] && !mFalseJump->mEntryRequiredRegs[CPU_REG_C]) + { + mIns[sz - 4].mType = ASMIT_INY; mIns[sz - 4].mLive |= LIVE_CPU_REG_Y | LIVE_CPU_REG_Z; + if (mBranch == ASMIT_BCC) + mBranch = ASMIT_BNE; + else + mBranch = ASMIT_BEQ; + mIns.SetSize(sz - 3); + changed = true; + } + } +#endif + +#if 1 + if (mIns.Size() >= 4 && mTrueJump && mFalseJump && mTrueJump->mEntryRequiredRegs.Size() > 0 && mFalseJump->mEntryRequiredRegs.Size() > 0) + { + int sz = mIns.Size(); + + if (mIns[sz - 4].mType == ASMIT_LDA && mIns[sz - 4].mMode == ASMIM_ZERO_PAGE && + mIns[sz - 3].mType == ASMIT_ADC && mIns[sz - 3].mMode == ASMIM_IMMEDIATE && mIns[sz - 3].mAddress == 0 && + mIns[sz - 2].mType == ASMIT_STA && mIns[sz - 2].mMode == ASMIM_ZERO_PAGE && mIns[sz - 2].mAddress == mIns[sz - 4].mAddress && + mIns[sz - 1].mType == ASMIT_CPY && + (mBranch == ASMIT_BNE || mBranch == ASMIT_BEQ) && + !mTrueJump->mEntryRequiredRegs[CPU_REG_C] && !mFalseJump->mEntryRequiredRegs[CPU_REG_C]) + { + if (mBranch == ASMIT_BNE && !mTrueJump->mEntryRequiredRegs[CPU_REG_A] || + mBranch == ASMIT_BEQ && !mFalseJump->mEntryRequiredRegs[CPU_REG_A]) + { + NativeCodeBasicBlock* iblock = SplitAt(sz - 4); + iblock->mIns[0].mType = ASMIT_INC; + iblock->mIns[1].mType = ASMIT_NOP; iblock->mIns[1].mMode = ASMIM_IMPLIED; + iblock->mIns[2].mType = ASMIT_LDA; + NativeCodeBasicBlock* eblock = iblock->SplitAt(2); + + mBranch = ASMIT_BCC; + mFalseJump = mTrueJump; + mTrueJump = eblock; + eblock->mEntryBlocks.Push(this); + eblock->mNumEntries++; + + changed = true; + } + } + } +#endif + + + if (mIns.Size() >= 2 && mTrueJump && mFalseJump && mTrueJump->mEntryRequiredRegs.Size() > 0 && mFalseJump->mEntryRequiredRegs.Size() > 0) + { + int sz = mIns.Size(); + + if (mIns[sz - 2].mType == ASMIT_EOR && mIns[sz - 2].mMode == ASMIM_IMMEDIATE && mIns[sz - 2].mAddress == 0x80 && + mIns[sz - 1].mType == ASMIT_CMP && mIns[sz - 1].mMode == ASMIM_IMMEDIATE && !(mIns[sz - 1].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z)) && + (mBranch == ASMIT_BCC || mBranch == ASMIT_BCS) && + !mTrueJump->mEntryRequiredRegs[CPU_REG_C] && !mFalseJump->mEntryRequiredRegs[CPU_REG_C]) + { + changed = true; + + int addr = mIns[sz - 1].mAddress ^ 0x80; + + const InterInstruction* iins(mIns[sz - 1].mIns); + + NativeCodeBasicBlock* iblock = proc->AllocateBlock(); + iblock->mIns.Push(NativeCodeInstruction(iins, ASMIT_CMP, ASMIM_IMMEDIATE, addr)); + mIns.SetSize(sz - 2); + mIns.Push(NativeCodeInstruction(iins, ASMIT_ORA, ASMIM_IMMEDIATE, 0x00)); + + iblock->mTrueJump = mTrueJump; + iblock->mFalseJump = mFalseJump; + iblock->mBranch = mBranch; + + if (mBranch == ASMIT_BCS) + { + mBranch = ASMIT_BPL; + if (addr & 0x80) + mFalseJump = iblock; + else + mTrueJump = iblock; + } + else + { + mBranch = ASMIT_BMI; + if (addr & 0x80) + mTrueJump = iblock; + else + mFalseJump = iblock; + } + } + + } + + if (mIns.Size() >= 1 && mExitRequiredRegs.Size() > 0) + { + int sz = mIns.Size(); + + if (mIns[sz - 1].mType == ASMIT_CMP && mBranch == ASMIT_BNE) + { + if (mTrueJump->mIns.Size() == 1 && mTrueJump->mBranch == ASMIT_BCC && mTrueJump->mFalseJump == mFalseJump) + { + if (mTrueJump->mIns[0].mType == ASMIT_STA && mTrueJump->mIns[0].mMode == ASMIM_ZERO_PAGE && !mFalseJump->mEntryRequiredRegs[mTrueJump->mIns[0].mAddress]) + { + if (mIns[sz - 1].mMode == ASMIM_IMMEDIATE) + { + mIns.Insert(sz - 1, mTrueJump->mIns[0]); + mIns[sz - 1].mLive |= LIVE_CPU_REG_A; + } + else + mIns.Push(mTrueJump->mIns[0]); + mExitRequiredRegs += mTrueJump->mIns[0].mAddress; + + mTrueJump->mEntryBlocks.RemoveAll(this); + mTrueJump->mNumEntries--; + mBranch = ASMIT_BCC; + mTrueJump = mTrueJump->mTrueJump; + mTrueJump->mNumEntries++; + mTrueJump->mEntryBlocks.Push(this); + + changed = true; + } + } + } + } + + if (mExitRequiredRegs.Size() > 0 && mIns.Size() >= 3 && mFalseJump && (mBranch == ASMIT_BCC || mBranch == ASMIT_BCS)) + { + int sz = mIns.Size(); + + if (mIns[sz - 3].mType == ASMIT_STA && mIns[sz - 3].mMode == ASMIM_ZERO_PAGE && + mIns[sz - 2].mType == ASMIT_LDA && HasAsmInstructionMode(ASMIT_CMP, mIns[sz - 2].mMode) && + mIns[sz - 1].mType == ASMIT_CMP && mIns[sz - 1].mMode == ASMIM_ZERO_PAGE && mIns[sz - 1].mAddress == mIns[sz - 3].mAddress && !(mIns[sz - 1].mLive & LIVE_CPU_REG_A)) + { + changed = true; + + NativeCodeBasicBlock* cblock = proc->AllocateBlock(); + cblock->Close(mBranchIns, mTrueJump, mFalseJump, mBranch); + + mIns[sz - 3].mLive |= LIVE_CPU_REG_A; + mIns[sz - 2].mType = ASMIT_CMP; mIns[sz - 2].mLive |= LIVE_CPU_REG_Z | LIVE_CPU_REG_C; + mIns[sz - 1].mType = ASMIT_NOP; mIns[sz - 1].mMode = ASMIM_IMPLIED; + mExitRequiredRegs += CPU_REG_Z; + + if (mBranch == ASMIT_BCC) + { + mTrueJump = mFalseJump; + mFalseJump = cblock; + cblock->mBranch = ASMIT_BNE; + } + else + { + mFalseJump = mTrueJump; + mTrueJump = cblock; + cblock->mBranch = ASMIT_BEQ; + } + } + } + + if (mIns.Size() >= 2 && !mFalseJump && mTrueJump && mTrueJump->mTrueJump && mTrueJump->mIns.Size() > 0 && mTrueJump != this) + { + int sz = mIns.Size(); + if (mIns[sz - 2].mType == ASMIT_LDA && mIns[sz - 2].mMode == ASMIM_ZERO_PAGE && + mIns[sz - 1].mType == ASMIT_ADC && mIns[sz - 1].mMode == ASMIM_IMMEDIATE && mIns[sz - 1].mAddress == 0 && + mTrueJump->mIns[0].mType == ASMIT_STA && mTrueJump->mIns[0].SameEffectiveAddress(mIns[sz - 2]) && !(mTrueJump->mIns[0].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C | LIVE_CPU_REG_Z))) + { + NativeCodeBasicBlock * tblock = mTrueJump->SplitAt(1); + mTrueJump->mNumEntries--; + mTrueJump->mEntryBlocks.RemoveAll(this); + + NativeCodeBasicBlock* iblock = proc->AllocateBlock(); + iblock->Close(mBranchIns, tblock, nullptr, ASMIT_JMP); + iblock->mIns.Push(NativeCodeInstruction(mIns[sz - 2].mIns, ASMIT_INC, mIns[sz - 2])); + mIns.SetSize(sz - 2); + mBranch = ASMIT_BCC; + mTrueJump = tblock; + mFalseJump = iblock; + + tblock->mNumEntries += 2; + tblock->mEntryBlocks.Push(this); + tblock->mEntryBlocks.Push(iblock); + iblock->mNumEntries = 1; + iblock->mEntryBlocks.Push(this); + + changed = true; + } + + } + + if (mIns.Size() >= 3 && mFalseJump && mBranch == ASMIT_BCC && mExitRequiredRegs.Size()) + { + int sz = mIns.Size(); + if (mIns[sz - 3].mType == ASMIT_LDA && + mIns[sz - 2].mType == ASMIT_ADC && mIns[sz - 2].mMode == ASMIM_IMMEDIATE && mIns[sz - 2].mAddress == 0 && + mIns[sz - 1].mType == ASMIT_STA && mIns[sz - 1].SameEffectiveAddress(mIns[sz - 3]) && + HasAsmInstructionMode(ASMIT_INC, mIns[sz - 1].mMode) && + !(mIns[sz - 1].mLive & LIVE_CPU_REG_A) && !mExitRequiredRegs[CPU_REG_C]) + { + NativeCodeBasicBlock* tblock = this->SplitAt(sz - 3); + + tblock->mBranch = ASMIT_BNE; + tblock->mIns[0].mType = ASMIT_INC; + tblock->mIns.SetSize(1); + + mBranch = ASMIT_BCC; + mFalseJump = tblock; + mTrueJump = tblock->mTrueJump; + mTrueJump->mNumEntries++; + mTrueJump->mEntryBlocks.Push(this); + changed = true; + } + else if (mIns[sz - 3].mType == ASMIT_TXA && + mIns[sz - 2].mType == ASMIT_ADC && mIns[sz - 2].mMode == ASMIM_IMMEDIATE && mIns[sz - 2].mAddress == 0 && + mIns[sz - 1].mType == ASMIT_TAX && + !(mIns[sz - 1].mLive & LIVE_CPU_REG_A) && !mExitRequiredRegs[CPU_REG_C]) + { + NativeCodeBasicBlock* tblock = this->SplitAt(sz - 3); + + tblock->mBranch = ASMIT_BNE; + tblock->mIns[0].mType = ASMIT_INX; + tblock->mIns[0].mMode = ASMIM_IMPLIED; + tblock->mIns[0].mLive |= LIVE_CPU_REG_X | LIVE_CPU_REG_Z; + tblock->mIns.SetSize(1); + + mBranch = ASMIT_BCC; + mFalseJump = tblock; + mTrueJump = tblock->mTrueJump; + mTrueJump->mNumEntries++; + mTrueJump->mEntryBlocks.Push(this); + changed = true; + } + + } + + if (mIns.Size() >= 3 && mFalseJump && (mBranch == ASMIT_BEQ || mBranch == ASMIT_BNE)) + { + int sz = mIns.Size(); + if ((mIns[sz - 3].mType == ASMIT_INC || mIns[sz - 3].mType == ASMIT_DEC) && + mIns[sz - 2].mType == ASMIT_LDA && mIns[sz - 2].SameEffectiveAddress(mIns[sz - 3]) && + mIns[sz - 1].mType == ASMIT_ORA && + !(mIns[sz - 1].mLive & LIVE_CPU_REG_A)) + { + mIns[sz - 3].mLive |= LIVE_CPU_REG_Z; + + NativeCodeBasicBlock* tblock = this->SplitAt(sz - 2); + + if (tblock->mBranch == ASMIT_BEQ) + mTrueJump = tblock->mFalseJump; + else + mTrueJump = tblock->mTrueJump; + + mBranch = ASMIT_BNE; + mFalseJump = tblock; + mTrueJump->mNumEntries++; + mTrueJump->mEntryBlocks.Push(this); + changed = true; + } + + } + + if (mIns.Size() >= 8 && mFalseJump && (mBranch == ASMIT_BNE || mBranch == ASMIT_BEQ)) + { + int sz = mIns.Size(); + + if (mIns[sz - 8].mType == ASMIT_LDA && + mIns[sz - 7].mType == ASMIT_CLC && + mIns[sz - 6].mType == ASMIT_ADC && mIns[sz - 6].mMode == ASMIM_IMMEDIATE && mIns[sz - 6].mAddress == 0xff && + mIns[sz - 5].mType == ASMIT_STA && mIns[sz - 5].SameEffectiveAddress(mIns[sz - 8]) && + mIns[sz - 4].mType == ASMIT_LDA && + mIns[sz - 3].mType == ASMIT_ADC && mIns[sz - 3].mMode == ASMIM_IMMEDIATE && mIns[sz - 3].mAddress == 0xff && + mIns[sz - 2].mType == ASMIT_STA && mIns[sz - 2].SameEffectiveAddress(mIns[sz - 4]) && + mIns[sz - 1].mType == ASMIT_ORA && mIns[sz - 1].SameEffectiveAddress(mIns[sz - 5]) && + HasAsmInstructionMode(ASMIT_DEC, mIns[sz - 5].mMode) && + HasAsmInstructionMode(ASMIT_DEC, mIns[sz - 2].mMode) && + !(mIns[sz - 1].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C))) + { + changed = true; + + NativeCodeBasicBlock* eblock, * neblock; + if (mBranch == ASMIT_BEQ) + { + eblock = mTrueJump; + neblock = mFalseJump; + } + else + { + eblock = mFalseJump; + neblock = mTrueJump; + } + + NativeCodeBasicBlock* hblock = proc->AllocateBlock(); + NativeCodeBasicBlock* lblock = proc->AllocateBlock(); + NativeCodeBasicBlock* oblock = proc->AllocateBlock(); + + mBranch = ASMIT_BNE; + mTrueJump = lblock; + mFalseJump = hblock; + + hblock->mBranch = ASMIT_JMP; + hblock->mTrueJump = lblock; + + lblock->mBranch = ASMIT_BNE; + lblock->mTrueJump = neblock; + lblock->mFalseJump = oblock; + + oblock->mBranch = ASMIT_BNE; + oblock->mTrueJump = neblock; + oblock->mFalseJump = eblock; + + const InterInstruction* iins(mIns[sz - 4].mIns); + + hblock->mIns.Push(NativeCodeInstruction(iins, ASMIT_DEC, mIns[sz - 4])); + lblock->mIns.Push(NativeCodeInstruction(iins, ASMIT_DEC, mIns[sz - 5])); + oblock->mIns.Push(NativeCodeInstruction(iins, ASMIT_LDA, mIns[sz - 4])); + + mIns[sz - 8].mLive |= LIVE_CPU_REG_Z; + mIns.SetSize(sz - 7); + +#if 0 + fblock->mTrueJump = mTrueJump; + fblock->mFalseJump = mFalseJump; + fblock->mBranch = mBranch; + + fblock->mIns.Push(mIns[i + 1]); + fblock->mIns[0].mType = ASMIT_DEC; + + mIns[i + 0].mType = ASMIT_NOP; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + mIns[i + 3].mType = ASMIT_LDA; mIns[i + 3].mLive |= LIVE_CPU_REG_Z; + + for (int j = i + 7; j < mIns.Size(); j++) + fblock->mIns.Push(mIns[j]); + iblock->mIns.Push(mIns[i + 6]); + mIns.SetSize(i + 4); + iblock->mIns[0].mType = ASMIT_DEC; + iblock->mTrueJump = fblock; + iblock->mBranch = ASMIT_JMP; + + mTrueJump = fblock; + mFalseJump = iblock; + mBranch = ASMIT_BNE; +#endif + } + + } + +#if 1 + if (mIns.Size() >= 5 && mFalseJump && mExitRequiredRegs.Size() && (mBranch == ASMIT_BNE || mBranch == ASMIT_BEQ)) + { + int sz = mIns.Size(); + if (mIns[sz - 4].mType == ASMIT_STA && mIns[sz - 4].mMode == ASMIM_ZERO_PAGE && + mIns[sz - 3].mType == ASMIT_LDA && !(mIns[sz - 3].mFlags & NCIF_VOLATILE) && + mIns[sz - 2].mType == ASMIT_AND && !(mIns[sz - 2].mFlags & NCIF_VOLATILE) && !mIns[sz - 2].SameEffectiveAddress(mIns[sz - 4]) && + mIns[sz - 1].mType == ASMIT_ORA && mIns[sz - 1].mMode == ASMIM_ZERO_PAGE && mIns[sz - 1].mAddress == mIns[sz - 4].mAddress && + !(mIns[sz - 1].mLive & (LIVE_CPU_REG_A | LIVE_MEM)) && !mExitRequiredRegs[CPU_REG_Z]) + { + NativeCodeBasicBlock* eblock, * neblock; + if (mBranch == ASMIT_BEQ) + { + eblock = mTrueJump; + neblock = mFalseJump; + } + else + { + eblock = mFalseJump; + neblock = mTrueJump; + } + + NativeCodeBasicBlock* hblock = proc->AllocateBlock(); + + mBranch = ASMIT_BEQ; + mTrueJump = hblock; + mFalseJump = neblock; + + hblock->mBranch = ASMIT_BEQ; + hblock->mTrueJump = eblock; + hblock->mFalseJump = neblock; + + mIns[sz - 4].mType = ASMIT_ORA; + mIns[sz - 4].mMode = ASMIM_IMMEDIATE; + mIns[sz - 4].mAddress = 0; + hblock->mIns.Push(mIns[sz - 3]); + hblock->mIns.Push(mIns[sz - 2]); + mIns.SetSize(sz - 3); + changed = true; + } + } +#endif + + if (mIns.Size() >= 2 && mFalseJump && mBranch == ASMIT_BMI) + { + int sz = mIns.Size(); + if (mIns[sz - 2].mType == ASMIT_LDA && mIns[sz - 2].mMode == ASMIM_IMMEDIATE && mIns[sz - 2].mAddress == 0 && + mIns[sz - 1].mType == ASMIT_SBC && mIns[sz - 1].mMode == ASMIM_IMMEDIATE && mIns[sz - 1].mAddress == 0) + { + if (mExitRequiredRegs[CPU_REG_Z]) + { + if (mFalseJump->mIns.Size() == 0 && mFalseJump->mBranch == ASMIT_BNE) + { + mFalseJump = mFalseJump->mFalseJump; + changed = true; + } + else if (mFalseJump->mIns.Size() == 0 && mFalseJump->mBranch == ASMIT_BEQ) + { + mFalseJump = mFalseJump->mTrueJump; + changed = true; + } + } + else if (!mExitRequiredRegs[CPU_REG_A]) + { + mBranch = ASMIT_BCC; + mIns.SetSize(sz - 2); + changed = true; + } + } + } + +#if 1 + if (mIns.Size() >= 1 && mFalseJump) + { + int sz = mIns.Size(); + + if (mIns[sz - 1].mType == ASMIT_CMP || mIns[sz - 1].mType == ASMIT_CPX || mIns[sz - 1].mType == ASMIT_CPY) + { + if (mBranch == ASMIT_BEQ) + { + if (mIns[sz - 1].mMode == ASMIM_IMMEDIATE && mIns[sz - 1].mAddress == 0) + { + if (mFalseJump->mIns.Size() == 0) + { + if (mFalseJump->mBranch == ASMIT_BCC) + { + mFalseJump = mFalseJump->mFalseJump; + changed = true; + } + else if (mFalseJump->mBranch == ASMIT_BCS) + { + mFalseJump = mFalseJump->mTrueJump; + changed = true; + } + } + if (mTrueJump->mIns.Size() == 0) + { + if (mTrueJump->mBranch == ASMIT_BCC) + { + mTrueJump = mTrueJump->mFalseJump; + changed = true; + } + else if (mTrueJump->mBranch == ASMIT_BCS) + { + mTrueJump = mTrueJump->mTrueJump; + changed = true; + } + } + } + + if (mFalseJump->mNumEntries == 1/* && mTrueJump->mNumEntries == 2*/ && mFalseJump->mIns.Size() == 0) + { + if (mFalseJump->mBranch == ASMIT_BCC) + { + if (mFalseJump->mFalseJump == mTrueJump) + { + mBranch = ASMIT_BCS; + mFalseJump = mFalseJump->mTrueJump; + changed = true; + } + } + else if (mFalseJump->mBranch == ASMIT_BCS) + { + if (mFalseJump->mTrueJump == mTrueJump) + { + mBranch = ASMIT_BCS; + mFalseJump = mFalseJump->mFalseJump; + changed = true; + } + } + } + } + else if (mBranch == ASMIT_BNE) + { + if (mIns[sz - 1].mMode == ASMIM_IMMEDIATE && mIns[sz - 1].mAddress == 0) + { + if (mFalseJump->mIns.Size() == 0) + { + if (mFalseJump->mBranch == ASMIT_BCC) + { + mFalseJump = mFalseJump->mFalseJump; + changed = true; + } + else if (mFalseJump->mBranch == ASMIT_BCS) + { + mFalseJump = mFalseJump->mTrueJump; + changed = true; + } + } + if (mTrueJump->mIns.Size() == 0) + { + if (mTrueJump->mBranch == ASMIT_BCC) + { + mTrueJump = mTrueJump->mFalseJump; + changed = true; + } + else if (mTrueJump->mBranch == ASMIT_BCS) + { + mTrueJump = mTrueJump->mTrueJump; + changed = true; + } + } + } + + if (mTrueJump->mNumEntries == 1/* && mFalseJump->mNumEntries == 2*/ && mTrueJump->mIns.Size() == 0) + { + if (mTrueJump->mBranch == ASMIT_BCC) + { + if (mTrueJump->mFalseJump == mFalseJump) + { + mBranch = ASMIT_BCC; + mTrueJump = mTrueJump->mTrueJump; + changed = true; + } + } + else if (mTrueJump->mBranch == ASMIT_BCS) + { + if (mTrueJump->mTrueJump == mFalseJump) + { + mBranch = ASMIT_BCC; + mTrueJump = mTrueJump->mFalseJump; + changed = true; + } + } + } + } + } + + } +#endif + // Simple diamond split + if (mFalseJump && !mTrueJump->mFalseJump && !mFalseJump->mFalseJump && mTrueJump->mTrueJump == mFalseJump->mTrueJump && mTrueJump->mNumEntries == 1 && mFalseJump->mNumEntries == 1) + { + if (mBranch == ASMIT_BCC || mBranch == ASMIT_BCS) + { + if (mTrueJump->mIns.Size() == 1 && mFalseJump->mIns.Size() == 1) + { + if (mTrueJump->mIns[0].mType == ASMIT_AND && mTrueJump->mIns[0].mMode == ASMIM_IMMEDIATE && + mFalseJump->mIns[0].mType == ASMIT_ORA && mFalseJump->mIns[0].mMode == ASMIM_IMMEDIATE && + (mTrueJump->mIns[0].mAddress ^ mFalseJump->mIns[0].mAddress) == 0xff) + { + mIns.Push(mTrueJump->mIns[0]); + mTrueJump->mIns.SetSize(0); + changed = true; + } + else if (mTrueJump->mIns[0].mType == ASMIT_ORA && mTrueJump->mIns[0].mMode == ASMIM_IMMEDIATE && + mFalseJump->mIns[0].mType == ASMIT_AND && mFalseJump->mIns[0].mMode == ASMIM_IMMEDIATE && + (mTrueJump->mIns[0].mAddress ^ mFalseJump->mIns[0].mAddress) == 0xff) + { + mIns.Push(mTrueJump->mIns[0]); + mTrueJump->mIns.SetSize(0); + changed = true; + } + } + } + } + + if (mFalseJump && mTrueJump->mIns.Size() == 0 && mFalseJump->mIns.Size() == 0 && (mBranch == ASMIT_BCC || mBranch == ASMIT_BCS)) + { + if (mTrueJump->mBranch == ASMIT_BEQ && mFalseJump->mBranch == ASMIT_BNE && mTrueJump->mTrueJump == mFalseJump->mFalseJump) + { + NativeCodeBasicBlock* iblock = proc->AllocateBlock(); + iblock->Close(mBranchIns, mTrueJump->mFalseJump, mFalseJump->mTrueJump, mBranch); + mBranch = ASMIT_BEQ; + + mTrueJump = mTrueJump->mTrueJump; + mFalseJump = iblock; + changed = true; + } + } + + if (mFalseJump && mTrueJump->mTrueJump == mFalseJump && !mTrueJump->mFalseJump && mTrueJump->mNumEntries == 1) + { + int sz = mIns.Size(); + if (sz > 0 && mIns[sz - 1].mType == ASMIT_LDX && mIns[sz - 1].mMode == ASMIM_IMMEDIATE && mTrueJump->mIns.Size() == 1 && mTrueJump->mIns[0].mType == ASMIT_INX) + { + NativeCodeBasicBlock* eblock = mTrueJump->mTrueJump; + int esz = eblock->mIns.Size(); + int i = 0; + while (i < esz && !eblock->mIns[i].ReferencesXReg()) + i++; + if (i < esz && eblock->mIns[i].mType == ASMIT_TXA && !(eblock->mIns[i].mLive & (LIVE_CPU_REG_X | LIVE_CPU_REG_Z))) + { + if (i + 2 < esz && eblock->mIns[i + 1].mType == ASMIT_CLC && eblock->mIns[i + 2].mType == ASMIT_ADC && !(eblock->mIns[i + 2].mLive & (LIVE_CPU_REG_Z | LIVE_CPU_REG_C))) + { + if (eblock->mIns[i + 2].mMode == ASMIM_IMMEDIATE) + { + mIns[sz - 1].mAddress += eblock->mIns[i + 2].mAddress; + eblock->mIns[i + 2].mType = ASMIT_NOP; eblock->mIns[i + 2].mMode = ASMIM_IMPLIED; + changed = true; + } + else if (eblock->mIns[i + 2].mMode == ASMIM_IMMEDIATE_ADDRESS) + { + mIns[sz - 1].mMode = ASMIM_IMMEDIATE_ADDRESS; + mIns[sz - 1].mAddress += eblock->mIns[i + 2].mAddress; + mIns[sz - 1].mFlags = eblock->mIns[i + 2].mFlags; + mIns[sz - 1].mLinkerObject = eblock->mIns[i + 2].mLinkerObject; + eblock->mIns[i + 2].mType = ASMIT_NOP; eblock->mIns[i + 2].mMode = ASMIM_IMPLIED; + changed = true; + } + } + } + } + } + + if (mFalseJump && mIns.Size() >= 8 && (mBranch == ASMIT_BEQ || mBranch == ASMIT_BNE)) + { + int sz = mIns.Size(); + + if (mIns[sz - 8].mType == ASMIT_SEC && + mIns[sz - 7].mType == ASMIT_LDA && + mIns[sz - 6].mType == ASMIT_SBC && mIns[sz - 6].mMode == ASMIM_IMMEDIATE && mIns[sz - 6].mAddress == 0x01 && + mIns[sz - 5].mType == ASMIT_STA && mIns[sz - 5].SameEffectiveAddress(mIns[sz - 7]) && + mIns[sz - 4].mType == ASMIT_LDA && + mIns[sz - 3].mType == ASMIT_SBC && mIns[sz - 3].mMode == ASMIM_IMMEDIATE && mIns[sz - 3].mAddress == 0x00 && + mIns[sz - 2].mType == ASMIT_STA && mIns[sz - 2].SameEffectiveAddress(mIns[sz - 4]) && + mIns[sz - 1].mType == ASMIT_ORA && mIns[sz - 1].SameEffectiveAddress(mIns[sz - 7]) && + HasAsmInstructionMode(ASMIT_DEC, mIns[sz - 5].mMode) && + HasAsmInstructionMode(ASMIT_DEC, mIns[sz - 1].mMode) && + !(mIns[sz - 1].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C))) + { + changed = true; + + NativeCodeBasicBlock* hiblock = proc->AllocateBlock(); + NativeCodeBasicBlock* loblock = proc->AllocateBlock(); + NativeCodeBasicBlock* orblock = proc->AllocateBlock(); + + NativeCodeBasicBlock* eqblock, * neblock; + if (mBranch == ASMIT_BEQ) + { + eqblock = mTrueJump; + neblock = mFalseJump; + } + else + { + neblock = mTrueJump; + eqblock = mFalseJump; + } + + hiblock->mBranch = ASMIT_JMP; + hiblock->mTrueJump = loblock; + + loblock->mBranch = ASMIT_BNE; + loblock->mTrueJump = neblock; + loblock->mFalseJump = orblock; + + orblock->mBranch = ASMIT_BNE; + orblock->mTrueJump = neblock; + orblock->mFalseJump = eqblock; + + mBranch = ASMIT_BNE; + mTrueJump = loblock; + mFalseJump = hiblock; + + hiblock->mIns.Push(NativeCodeInstruction(mIns[sz - 4].mIns, ASMIT_DEC, mIns[sz - 4])); + loblock->mIns.Push(NativeCodeInstruction(mIns[sz - 7].mIns, ASMIT_DEC, mIns[sz - 7])); + orblock->mIns.Push(NativeCodeInstruction(mIns[sz - 4].mIns, ASMIT_LDA, mIns[sz - 4])); + + mIns[sz - 8].mType = ASMIT_NOP; + mIns.SetSize(sz - 6); + } + } + +#if 1 + if (mFalseJump && mIns.Size() >= 3) + { + int sz = mIns.Size(); + if (mIns[sz - 3].mType == ASMIT_CLC && + mIns[sz - 2].mType == ASMIT_ADC && + mIns[sz - 1].mType == ASMIT_CMP && mIns[sz - 1].SameEffectiveAddress(mIns[sz - 2])) + { + if (mTrueJump->mNumEntries == 1 && !mFalseJump->mEntryRequiredRegs[CPU_REG_A]) + { + mTrueJump->mIns.Insert(0, mIns[sz - 3]); + mTrueJump->mIns.Insert(1, mIns[sz - 2]); + mIns[sz - 1].mMode = ASMIM_IMMEDIATE; mIns[sz - 1].mAddress = 0; + mIns.Remove(sz - 3, 2); + changed = true; + } + else if (mFalseJump->mNumEntries == 1 && !mTrueJump->mEntryRequiredRegs[CPU_REG_A]) + { + mFalseJump->mIns.Insert(0, mIns[sz - 3]); + mFalseJump->mIns.Insert(1, mIns[sz - 2]); + mIns[sz - 1].mMode = ASMIM_IMMEDIATE; mIns[sz - 1].mAddress = 0; + mIns.Remove(sz - 3, 2); + changed = true; + } + } + } +#endif + + if (changed && this == mProc->mExitBlock && mTrueJump) + { + mLocked = false; + mProc->mExitBlock = mTrueJump; + mProc->mExitBlock->mLocked = true; + } + + if (mTrueJump && mTrueJump->ExpandADCToBranch(proc)) + changed = true; + if (mFalseJump && mFalseJump->ExpandADCToBranch(proc)) + changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::ReduceLocalXPressure(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + int start = 0; + + while (start < mIns.Size()) + { + const NativeCodeInstruction& ins(mIns[start]); + + if ((ins.mType == ASMIT_LDX || ins.mType == ASMIT_TAX) && ins.mMode != ASMIM_INDIRECT_Y && ins.mMode != ASMIM_ABSOLUTE_Y && !(ins.mLive & LIVE_CPU_REG_Y)) + { + int end = start + 1; + while (end < mIns.Size()) + { + const NativeCodeInstruction& eins(mIns[end]); + if (eins.mType == ASMIT_LDX || eins.mType == ASMIT_TAX) + { + ReplaceXRegWithYReg(start, end); + changed = true; + break; + } + else if (eins.mType == ASMIT_JSR && (eins.mFlags & LOBJF_ARG_REG_X)) + break; + else if (eins.ChangesYReg() || (eins.mMode == ASMIM_ABSOLUTE_X && !HasAsmInstructionMode(eins.mType, ASMIM_ABSOLUTE_Y))) + { + break; + } + else if (!(eins.mLive & LIVE_CPU_REG_X)) + { + end++; + ReplaceXRegWithYReg(start, end); + changed = true; + break; + } + + end++; + } + + start = end; + } + else + start++; + } + + if (mTrueJump && mTrueJump->ReduceLocalXPressure()) + changed = true; + + if (mFalseJump && mFalseJump->ReduceLocalXPressure()) + changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::CombineZPPair(int at, int r0, int r1, bool use0, bool use1, bool & used1) +{ + bool changed = false; + + int i = at; + while (i < mIns.Size() && !use0 && !use1) + { + NativeCodeInstruction& ins(mIns[i]); + + if (ins.mMode == ASMIM_ZERO_PAGE) + { + if (ins.ChangesAddress()) + { + if (ins.mAddress == r0) + use1 = true; + else if (ins.mAddress == r1) + use0 = true; + } + else if (!(ins.mLive & LIVE_MEM)) + { + if (ins.mAddress == r0) + use1 = true; + else if (ins.mAddress == r1) + use0 = true; + } + } + else + { + if (ins.ReferencesZeroPage(r0)) + use0 = true; + if (ins.ReferencesZeroPage(r1)) + use1 = true; + } + + i++; + } + + if (use0 && use1) + return false; + + if (!use0 && !use1) + { + if (!mExitRequiredRegs[r0]) + use1 = true; + else if (!mExitRequiredRegs[r1]) + use0 = true; + else + return false; + } + + bool valid0 = true, valid1 = true; + + used1 = use1; + + i = at; + while (i < mIns.Size() && valid0 && valid1) + { + NativeCodeInstruction& ins(mIns[i]); + + if (ins.mMode == ASMIM_ZERO_PAGE) + { + if (ins.ChangesAddress()) + { + if (ins.mAddress == r0) + valid0 = false; + else if (ins.mAddress == r1) + valid1 = false; + } + else + { + if (use1) + { + if (ins.mAddress == r0) + { + ins.mAddress = r1; + changed = true; + } + else if (ins.mAddress == r1) + ins.mLive |= LIVE_MEM; + } + else if (use0) + { + if (ins.mAddress == r1) + { + ins.mAddress = r0; + changed = true; + } + else if (ins.mAddress == r0) + ins.mLive |= LIVE_MEM; + } + } + } + else + { + if (ins.ChangesZeroPage(r0)) + valid0 = false; + if (ins.ChangesZeroPage(r1)) + valid1 = false; + } + + i++; + } + + return changed; +} + +bool NativeCodeBasicBlock::RemoveDoubleZPStore(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + bool swap; + + for (int i = 0; i + 1 < mIns.Size(); i++) + { + if (mIns[i + 0].mType == ASMIT_STX && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_STX && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && mIns[i + 0].mAddress != mIns[i + 1].mAddress) + { + if (CombineZPPair(i + 2, mIns[i + 0].mAddress, mIns[i + 1].mAddress, false, false, swap)) + { + if (swap) + { + int a = mIns[i + 0].mAddress; mIns[i + 0].mAddress = mIns[i + 1].mAddress; mIns[i + 1].mAddress = a; + } + changed = true; + } + } + else if (mIns[i + 0].mType == ASMIT_STA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_STA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && mIns[i + 0].mAddress != mIns[i + 1].mAddress) + { + if (CombineZPPair(i + 2, mIns[i + 0].mAddress, mIns[i + 1].mAddress, false, false, swap)) + { + if (swap) + { + int a = mIns[i + 0].mAddress; mIns[i + 0].mAddress = mIns[i + 1].mAddress; mIns[i + 1].mAddress = a; + } + changed = true; + } + } + else if (mIns[i + 0].mType == ASMIT_STY && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_STY && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && mIns[i + 0].mAddress != mIns[i + 1].mAddress) + { + if (CombineZPPair(i + 2, mIns[i + 0].mAddress, mIns[i + 1].mAddress, false, false, swap)) + { + if (swap) + { + int a = mIns[i + 0].mAddress; mIns[i + 0].mAddress = mIns[i + 1].mAddress; mIns[i + 1].mAddress = a; + } + changed = true; + } + } + } + + if (mTrueJump && mTrueJump->RemoveDoubleZPStore()) + changed = true; + + if (mFalseJump && mFalseJump->RemoveDoubleZPStore()) + changed = true; + } + + return changed; +} + +struct ValueNumbers +{ + int zvalues[256], avalue, xvalue, yvalue; + int ivalue = 0; + + void Reset(void) + { + for (int i = 0; i < 256; i++) + zvalues[i] = ivalue++; + avalue = ivalue++; + xvalue = ivalue++; + yvalue = ivalue++; + } + +}; + +bool NativeCodeBasicBlock::LocalZeroPageValueNumbering(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + ValueNumbers vn; + vn.Reset(); + + for (int i = 0; i < mIns.Size(); i++) + { + NativeCodeInstruction& ins = mIns[i]; + + if (ins.mType == ASMIT_LDA && ins.mMode == ASMIM_ZERO_PAGE) + { + if (vn.avalue == vn.zvalues[ins.mAddress]) + { + if (ins.mLive & LIVE_CPU_REG_Z) + { + ins.mType = ASMIT_ORA; + ins.mMode = ASMIM_IMMEDIATE; + ins.mAddress = 0; + changed = true; + } + else + { + ins.mType = ASMIT_NOP; + ins.mMode = ASMIM_IMPLIED; + changed = true; + } + } + else + vn.avalue = vn.zvalues[ins.mAddress]; + } + else if (ins.mType == ASMIT_LDX && ins.mMode == ASMIM_ZERO_PAGE) + { + if (vn.xvalue == vn.zvalues[ins.mAddress] && !(ins.mLive & LIVE_CPU_REG_Z)) + { + ins.mType = ASMIT_NOP; + ins.mMode = ASMIM_IMPLIED; + changed = true; + } + else + vn.xvalue = vn.zvalues[ins.mAddress]; + } + else if (ins.mType == ASMIT_LDY && ins.mMode == ASMIM_ZERO_PAGE) + { + if (vn.yvalue == vn.zvalues[ins.mAddress] && !(ins.mLive & LIVE_CPU_REG_Z)) + { + ins.mType = ASMIT_NOP; + ins.mMode = ASMIM_IMPLIED; + changed = true; + } + else + vn.yvalue = vn.zvalues[ins.mAddress]; + } + else if (ins.mType == ASMIT_STA && ins.mMode == ASMIM_ZERO_PAGE) + { + if (vn.avalue == vn.zvalues[ins.mAddress]) + { + ins.mType = ASMIT_NOP; + ins.mMode = ASMIM_IMPLIED; + changed = true; + } + else + vn.zvalues[ins.mAddress] = vn.avalue; + } + else if (ins.mType == ASMIT_STX && ins.mMode == ASMIM_ZERO_PAGE) + { + if (vn.xvalue == vn.zvalues[ins.mAddress]) + { + ins.mType = ASMIT_NOP; + ins.mMode = ASMIM_IMPLIED; + changed = true; + } + else + vn.zvalues[ins.mAddress] = vn.xvalue; + } + else if (ins.mType == ASMIT_STY && ins.mMode == ASMIM_ZERO_PAGE) + { + if (vn.yvalue == vn.zvalues[ins.mAddress]) + { + ins.mType = ASMIT_NOP; + ins.mMode = ASMIM_IMPLIED; + changed = true; + } + else + vn.zvalues[ins.mAddress] = vn.yvalue; + } + else if (ins.mType == ASMIT_TAX) + { + if (vn.avalue == vn.xvalue && !(ins.mLive & LIVE_CPU_REG_Z)) + { + ins.mType = ASMIT_NOP; + ins.mMode = ASMIM_IMPLIED; + changed = true; + } + else + vn.xvalue = vn.avalue; + } + else if (ins.mType == ASMIT_TAY) + { + if (vn.avalue == vn.yvalue && !(ins.mLive & LIVE_CPU_REG_Z)) + { + ins.mType = ASMIT_NOP; + ins.mMode = ASMIM_IMPLIED; + changed = true; + } + else + vn.yvalue = vn.avalue; + } + else if (ins.mType == ASMIT_TXA) + { + if (vn.xvalue == vn.avalue && !(ins.mLive & LIVE_CPU_REG_Z)) + { + ins.mType = ASMIT_NOP; + ins.mMode = ASMIM_IMPLIED; + changed = true; + } + else + vn.avalue = vn.xvalue; + } + else if (ins.mType == ASMIT_TYA) + { + if (vn.yvalue == vn.avalue && !(ins.mLive & LIVE_CPU_REG_Z)) + { + ins.mType = ASMIT_NOP; + ins.mMode = ASMIM_IMPLIED; + changed = true; + } + else + vn.avalue = vn.yvalue; + } + else if (ins.mType == ASMIT_JSR) + vn.Reset(); + else + { + if (ins.mMode == ASMIM_INDIRECT_Y && !(ins.mLive & LIVE_MEM)) + { + int i = 0; + while (i < 255 && (i == ins.mAddress || vn.zvalues[i] != vn.zvalues[ins.mAddress] || vn.zvalues[i + 1] != vn.zvalues[ins.mAddress + 1])) + i++; + if (i < 255) + { + ins.mAddress = i; + } + } + + if (ins.ChangesAccu()) + vn.avalue = vn.ivalue++; + if (ins.ChangesXReg()) + vn.xvalue = vn.ivalue++; + if (ins.ChangesYReg()) + vn.yvalue = vn.ivalue++; + if (ins.mMode == ASMIM_ZERO_PAGE && ins.ChangesAddress()) + vn.zvalues[ins.mAddress] = vn.ivalue++; + } + } + + + if (mTrueJump && mTrueJump->LocalZeroPageValueNumbering()) + changed = true; + + if (mFalseJump && mFalseJump->LocalZeroPageValueNumbering()) + changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::CanReverseCrossBlockChangeAToY(const NativeCodeBasicBlock* block, int depth) +{ + if (mFalseJump) + { + if (block == mTrueJump && (mFalseJump->mEntryRequiredRegs[CPU_REG_A] || mFalseJump->mEntryRequiredRegs[CPU_REG_Y])) + return false; + if (block == mFalseJump && (mTrueJump->mEntryRequiredRegs[CPU_REG_A] || mTrueJump->mEntryRequiredRegs[CPU_REG_Y])) + return false; + } + + if (depth > 10) + return false; + + for (int i = mIns.Size() - 1; i >= 0; i--) + { + if (mIns[i].mType == ASMIT_LDA && HasAsmInstructionMode(ASMIT_LDY, mIns[i].mMode)) + return true; + else if (mIns[i].mType == ASMIT_TYA && !(mIns[i].mLive & LIVE_CPU_REG_Z)) + return true; + else if (mIns[i].mType == ASMIT_CMP && HasAsmInstructionMode(ASMIT_CPY, mIns[i].mMode)) + ; + else if (mIns[i].ReferencesAccu() || mIns[i].ReferencesYReg()) + return false; + } + + if (!mEntryBlocks.Size()) + return false; + + for (int i = 0; i < mEntryBlocks.Size(); i++) + if (!mEntryBlocks[i]->CanReverseCrossBlockChangeAToY(this, depth + 1)) + return false; + + return true; +} + +void NativeCodeBasicBlock::ReverseCrossBlockChangeAToY(void) +{ + if (!mPatched) + { + mPatched = true; + + mExitRequiredRegs += CPU_REG_Y; + + for (int i = mIns.Size() - 1; i >= 0; i--) + { + mIns[i].mLive |= LIVE_CPU_REG_Y; + if (mIns[i].mType == ASMIT_LDA) + { + mIns[i].mType = ASMIT_LDY; + return; + } + if (mIns[i].mType == ASMIT_TYA) + { + mIns[i].mType = ASMIT_NOP; + return; + } + else if (mIns[i].mType == ASMIT_CMP) + { + mIns[i].mType = ASMIT_CPY; + } + } + + mEntryRequiredRegs += CPU_REG_Y; + + for (int i = 0; i < mEntryBlocks.Size(); i++) + mEntryBlocks[i]->ReverseCrossBlockChangeAToY(); + } +} + +bool NativeCodeBasicBlock::CanReverseCrossBlockChangeAToX(const NativeCodeBasicBlock* block, int depth) +{ + if (mFalseJump) + { + if (block == mTrueJump && (mFalseJump->mEntryRequiredRegs[CPU_REG_A] || mFalseJump->mEntryRequiredRegs[CPU_REG_X])) + return false; + if (block == mFalseJump && (mTrueJump->mEntryRequiredRegs[CPU_REG_A] || mTrueJump->mEntryRequiredRegs[CPU_REG_X])) + return false; + } + + if (depth > 10) + return false; + + for (int i = mIns.Size() - 1; i >= 0; i--) + { + if (mIns[i].mType == ASMIT_LDA && HasAsmInstructionMode(ASMIT_LDX, mIns[i].mMode)) + return true; + else if (mIns[i].mType == ASMIT_TXA && !(mIns[i].mLive & LIVE_CPU_REG_Z)) + return true; + else if (mIns[i].mType == ASMIT_CMP && HasAsmInstructionMode(ASMIT_CPX, mIns[i].mMode)) + ; + else if (mIns[i].ReferencesAccu() || mIns[i].ReferencesXReg()) + return false; + } + + if (!mEntryBlocks.Size()) + return false; + + for (int i = 0; i < mEntryBlocks.Size(); i++) + if (!mEntryBlocks[i]->CanReverseCrossBlockChangeAToX(this, depth + 1)) + return false; + + return true; +} + +void NativeCodeBasicBlock::ReverseCrossBlockChangeAToX(void) +{ + if (!mPatched) + { + mPatched = true; + + mExitRequiredRegs += CPU_REG_X; + + for (int i = mIns.Size() - 1; i >= 0; i--) + { + mIns[i].mLive |= LIVE_CPU_REG_X; + if (mIns[i].mType == ASMIT_LDA) + { + mIns[i].mType = ASMIT_LDX; + return; + } + if (mIns[i].mType == ASMIT_TXA) + { + mIns[i].mType = ASMIT_NOP; + return; + } + else if (mIns[i].mType == ASMIT_CMP) + { + mIns[i].mType = ASMIT_CPX; + } + } + + mEntryRequiredRegs += CPU_REG_X; + + for (int i = 0; i < mEntryBlocks.Size(); i++) + mEntryBlocks[i]->ReverseCrossBlockChangeAToX(); + } +} + +bool NativeCodeBasicBlock::ReverseZeroPageRecycling(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + for (int i = 0; i + 2 < mIns.Size(); i++) + { + if (mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && !(mIns[i + 0].mLive & LIVE_MEM) && + mIns[i + 1].IsArithmetic() && !mIns[i + 1].SameEffectiveAddress(mIns[i + 0]) && + mIns[i + 2].mType == ASMIT_STA && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && !mIns[i + 2].SameEffectiveAddress(mIns[i + 0]) && !mIns[i + 2].SameEffectiveAddress(mIns[i + 1])) + { + int as = mIns[i + 0].mAddress, ad = mIns[i + 2].mAddress; + + int j = i - 1; + while (j >= 0 && !(mIns[j].ReferencesZeroPage(as) || mIns[j].ReferencesZeroPage(ad))) + j--; + if (j >= 0 && mIns[j].IsStore() && mIns[j].mMode == ASMIM_ZERO_PAGE && mIns[j].mAddress == as) + { + mIns[j].mAddress = ad; + mIns[i].mAddress = ad; + changed = true; + } + } + } + + if (mTrueJump && mTrueJump->ReverseZeroPageRecycling()) + changed = true; + + if (mFalseJump && mFalseJump->ReverseZeroPageRecycling()) + changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::ReverseCrossBlockRegisterUnification(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + bool afromx = false, afromy = false; + + for (int i = 0; i < mEntryBlocks.Size(); i++) + { + if (mEntryBlocks[i]->mIns.Size() > 0) + { + if (mEntryBlocks[i]->mIns.Last().mType == ASMIT_TXA) + afromx = true; + else if (mEntryBlocks[i]->mIns.Last().mType == ASMIT_TYA) + afromy = true; + } + } + bool refx = false, refy = false; + int i = 0; + while (i < mIns.Size() && !(refx && refy)) + { + if (!refy && mIns[i].mType == ASMIT_TAY && !(mIns[i].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z))) + { + int k = 0; + while (k < mEntryBlocks.Size() && mEntryBlocks[k]->CanReverseCrossBlockChangeAToY(this)) + k++; + if (k == mEntryBlocks.Size()) + { + mProc->ResetPatched(); + for (k = 0; k < mEntryBlocks.Size(); k++) + mEntryBlocks[k]->ReverseCrossBlockChangeAToY(); + for (k = 0; k < i; k++) + mIns[k].mLive |= LIVE_CPU_REG_Y; + mEntryRequiredRegs += CPU_REG_Y; + mIns[i].mType = ASMIT_NOP; + changed = true; + break; + } + } + else if (!refx && mIns[i].mType == ASMIT_TAX && !(mIns[i].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z))) + { + int k = 0; + while (k < mEntryBlocks.Size() && mEntryBlocks[k]->CanReverseCrossBlockChangeAToX(this)) + k++; + if (k == mEntryBlocks.Size()) + { + mProc->ResetPatched(); + for (k = 0; k < mEntryBlocks.Size(); k++) + mEntryBlocks[k]->ReverseCrossBlockChangeAToX(); + for (k = 0; k < i; k++) + mIns[k].mLive |= LIVE_CPU_REG_X; + mEntryRequiredRegs += CPU_REG_X; + mIns[i].mType = ASMIT_NOP; + changed = true; + break; + } + } +#if 1 + else if (afromy && !refy && mIns[i].mType == ASMIT_STA && HasAsmInstructionMode(ASMIT_STY, mIns[i].mMode) && !(mIns[i].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Y))) + { + int k = 0; + while (k < mEntryBlocks.Size() && mEntryBlocks[k]->CanReverseCrossBlockChangeAToY(this)) + k++; + if (k == mEntryBlocks.Size()) + { + mProc->ResetPatched(); + for (k = 0; k < mEntryBlocks.Size(); k++) + mEntryBlocks[k]->ReverseCrossBlockChangeAToY(); + for (k = 0; k < i; k++) + mIns[k].mLive |= LIVE_CPU_REG_Y; + mEntryRequiredRegs += CPU_REG_Y; + mIns[i].mType = ASMIT_STY; + changed = true; + break; + } + } + else if (afromx && !refx && mIns[i].mType == ASMIT_STA && HasAsmInstructionMode(ASMIT_STX, mIns[i].mMode) && !(mIns[i].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_X))) + { + int k = 0; + while (k < mEntryBlocks.Size() && mEntryBlocks[k]->CanReverseCrossBlockChangeAToX(this)) + k++; + if (k == mEntryBlocks.Size()) + { + mProc->ResetPatched(); + for (k = 0; k < mEntryBlocks.Size(); k++) + mEntryBlocks[k]->ReverseCrossBlockChangeAToX(); + for (k = 0; k < i; k++) + mIns[k].mLive |= LIVE_CPU_REG_X; + mEntryRequiredRegs += CPU_REG_X; + mIns[i].mType = ASMIT_STX; + changed = true; + break; + } + } +#endif + if (mIns[i].ReferencesAccu()) + break; + if (mIns[i].ReferencesXReg()) + refx = true; + if (mIns[i].ReferencesYReg()) + refy = true; + + i++; + } + + if (mTrueJump && mTrueJump->ReverseCrossBlockRegisterUnification()) + changed = true; + + if (mFalseJump && mFalseJump->ReverseCrossBlockRegisterUnification()) + changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::LocalRegisterXYMap(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + CheckLive(); + + int xregs[256], yregs[256]; + for (int i = 0; i < 256; i++) + xregs[i] = yregs[i] = -1; + + for (int i = 0; i < mIns.Size(); i++) + { + if (i + 1 < mIns.Size() && mIns[i].mType == ASMIT_LDA && (mIns[i].mMode != ASMIM_ZERO_PAGE || (xregs[mIns[i].mAddress] < 0 && xregs[mIns[i].mAddress] < 0) || (mIns[i + 1].mLive & LIVE_MEM))) + { + if (mIns[i + 1].IsCommutative() && mIns[i + 1].mMode == ASMIM_ZERO_PAGE) + { + if (xregs[mIns[i + 1].mAddress] >= 0 && !(mIns[i + 1].mLive & LIVE_MEM)) + { + int addr = mIns[i + 1].mAddress; + mIns[i + 1].CopyMode(mIns[i]); + + if (mIns[i + 1].RequiresYReg()) + mIns[i].mLive |= LIVE_CPU_REG_Y; + else if (mIns[i + 1].RequiresXReg()) + mIns[i].mLive |= LIVE_CPU_REG_X; + + mIns[i].mMode = ASMIM_ZERO_PAGE; + mIns[i].mAddress = addr; + } + else if (yregs[mIns[i + 1].mAddress] >= 0 && !(mIns[i + 1].mLive & LIVE_MEM)) + { + int addr = mIns[i + 1].mAddress; + mIns[i + 1].CopyMode(mIns[i]); + + if (mIns[i + 1].RequiresYReg()) + mIns[i].mLive |= LIVE_CPU_REG_Y; + else if (mIns[i + 1].RequiresXReg()) + mIns[i].mLive |= LIVE_CPU_REG_X; + + mIns[i].mMode = ASMIM_ZERO_PAGE; + mIns[i].mAddress = addr; + } + } + } + + CheckLive(); + + const NativeCodeInstruction& ins(mIns[i]); + + if (ins.ChangesXReg()) + { + for (int i = 0; i < 256; i++) + xregs[i] = -1; + } + if (ins.ChangesYReg()) + { + for (int i = 0; i < 256; i++) + yregs[i] = -1; + } + + if (ins.mMode == ASMIM_ZERO_PAGE) + { + switch (ins.mType) + { + case ASMIT_STA: +#if 0 + if (ins.mAddress >= BC_REG_ACCU && ins.mAddress < BC_REG_ACCU + 4 || + ins.mAddress >= BC_REG_WORK && ins.mAddress < BC_REG_WORK + 4) + { + } + else +#endif + { + if (!(ins.mLive & LIVE_CPU_REG_X)) + { + if (xregs[ins.mAddress] < 0) + xregs[ins.mAddress] = i; + } + if (!(ins.mLive & LIVE_CPU_REG_Y)) + { + if (yregs[ins.mAddress] < 0) + yregs[ins.mAddress] = i; + } + } + break; + case ASMIT_LDA: + if (xregs[ins.mAddress] >= 0 && !(ins.mLive & LIVE_MEM)) + { + changed = true; + + for (int j = xregs[ins.mAddress]; j <= i; j++) + { + NativeCodeInstruction& rins(mIns[j]); + if (rins.mMode == ASMIM_ZERO_PAGE && rins.mAddress == ins.mAddress) + { + switch (rins.mType) + { + case ASMIT_STA: + rins.mType = ASMIT_TAX; + rins.mMode = ASMIM_IMPLIED; + break; + case ASMIT_LDA: + rins.mType = ASMIT_TXA; + rins.mMode = ASMIM_IMPLIED; + break; + case ASMIT_INC: + rins.mType = ASMIT_INX; + rins.mMode = ASMIM_IMPLIED; + break; + case ASMIT_DEC: + rins.mType = ASMIT_DEX; + rins.mMode = ASMIM_IMPLIED; + break; + } + } + rins.mLive |= LIVE_CPU_REG_X; + } + + CheckLive(); + + for (int i = 0; i < 256; i++) + xregs[i] = -1; + } + else if (yregs[ins.mAddress] >= 0 && !(ins.mLive & LIVE_MEM)) + { + changed = true; + + for (int j = yregs[ins.mAddress]; j <= i; j++) + { + NativeCodeInstruction& rins(mIns[j]); + if (rins.mMode == ASMIM_ZERO_PAGE && rins.mAddress == ins.mAddress) + { + switch (rins.mType) + { + case ASMIT_STA: + rins.mType = ASMIT_TAY; + rins.mMode = ASMIM_IMPLIED; + break; + case ASMIT_LDA: + rins.mType = ASMIT_TYA; + rins.mMode = ASMIM_IMPLIED; + break; + case ASMIT_INC: + rins.mType = ASMIT_INY; + rins.mMode = ASMIM_IMPLIED; + break; + case ASMIT_DEC: + rins.mType = ASMIT_DEY; + rins.mMode = ASMIM_IMPLIED; + break; + } + } + rins.mLive |= LIVE_CPU_REG_Y; + } + + CheckLive(); + + for (int i = 0; i < 256; i++) + yregs[i] = -1; + } + break; + case ASMIT_INC: + case ASMIT_DEC: + break; + default: + xregs[ins.mAddress + 0] = -1; + yregs[ins.mAddress + 0] = -1; + break; + } + } + else if (ins.mMode == ASMIM_INDIRECT_Y) + { + xregs[ins.mAddress + 0] = -1; + xregs[ins.mAddress + 1] = -1; + yregs[ins.mAddress + 0] = -1; + yregs[ins.mAddress + 1] = -1; + } + else if (ins.mType == ASMIT_JSR) + { + for (int i = 0; i < 4; i++) + { + xregs[BC_REG_ACCU + i] = -1; + yregs[BC_REG_ACCU + i] = -1; + xregs[BC_REG_WORK + i] = -1; + yregs[BC_REG_WORK + i] = -1; + } + } + } + + CheckLive(); + + if (mTrueJump && mTrueJump->LocalRegisterXYMap()) + changed = true; + + if (mFalseJump && mFalseJump->LocalRegisterXYMap()) + changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::CanReplaceExitAccuWithX(NativeCodeBasicBlock* target) +{ + if (mTrueJump && target != mTrueJump && (mTrueJump->mEntryRequiredRegs[CPU_REG_X] || mTrueJump->mEntryRequiredRegs[CPU_REG_A])) + return false; + if (mFalseJump && target != mFalseJump && (mFalseJump->mEntryRequiredRegs[CPU_REG_X] || mFalseJump->mEntryRequiredRegs[CPU_REG_A])) + return false; + + int i = mIns.Size() - 1; + while (i >= 0) + { + NativeCodeInstruction& ins(mIns[i]); + if (ins.mType == ASMIT_LDA) + { + if (HasAsmInstructionMode(ASMIT_LDX, ins.mMode)) + return true; + } + if (ins.ReferencesXReg() || ins.ReferencesAccu()) + return false; + i--; + } + + return false; +} + +bool NativeCodeBasicBlock::CanReplaceExitAccuWithY(NativeCodeBasicBlock* target) +{ + if (mTrueJump && target != mTrueJump && (mTrueJump->mEntryRequiredRegs[CPU_REG_Y] || mTrueJump->mEntryRequiredRegs[CPU_REG_A])) + return false; + if (mFalseJump && target != mFalseJump && (mFalseJump->mEntryRequiredRegs[CPU_REG_Y] || mFalseJump->mEntryRequiredRegs[CPU_REG_A])) + return false; + + int i = mIns.Size() - 1; + while (i >= 0) + { + NativeCodeInstruction& ins(mIns[i]); + if (ins.mType == ASMIT_LDA) + { + if (HasAsmInstructionMode(ASMIT_LDY, ins.mMode)) + return true; + } + if (ins.ReferencesYReg() || ins.ReferencesAccu()) + return false; + i--; + } + + return false; +} + +void NativeCodeBasicBlock::ReplaceExitAccuWithX(NativeCodeBasicBlock* target) +{ + int i = mIns.Size() - 1; + while (i >= 0) + { + NativeCodeInstruction& ins(mIns[i]); + ins.mLive |= LIVE_CPU_REG_X; + if (ins.mType == ASMIT_LDA) + { + ins.mType = ASMIT_LDX; + mExitRequiredRegs += CPU_REG_X; + return; + } + i--; + } +} + +void NativeCodeBasicBlock::ReplaceExitAccuWithY(NativeCodeBasicBlock* target) +{ + int i = mIns.Size() - 1; + while (i >= 0) + { + NativeCodeInstruction& ins(mIns[i]); + ins.mLive |= LIVE_CPU_REG_Y; + if (ins.mType == ASMIT_LDA) + { + ins.mType = ASMIT_LDY; + mExitRequiredRegs += CPU_REG_Y; + return; + } + i--; + } +} + +bool NativeCodeBasicBlock::ReverseLoadAccuToRegXY(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + if (mIns.Size() > 2) + { + if (mIns[0].mType == ASMIT_STA && mIns[0].mMode == ASMIM_ZERO_PAGE && (mIns[1].mType == ASMIT_TAX || mIns[1].mType == ASMIT_TAY) && !(mIns[1].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z))) + { + if (mIns[1].mType == ASMIT_TAY) + { + int i = 0; + while (i < mEntryBlocks.Size() && mEntryBlocks[i]->CanReplaceExitAccuWithY(this)) + i++; + if (i == mEntryBlocks.Size()) + { + for (int j = 0; j < mEntryBlocks.Size(); j++) + mEntryBlocks[j]->ReplaceExitAccuWithY(this); + mEntryRequiredRegs += CPU_REG_Y; + mIns[0].mType = ASMIT_STY; mIns[0].mLive |= LIVE_CPU_REG_Y; + mIns[1].mType = ASMIT_NOP; mIns[1].mMode = ASMIM_IMPLIED; + changed = true; + } + } + else + { + int i = 0; + while (i < mEntryBlocks.Size() && mEntryBlocks[i]->CanReplaceExitAccuWithX(this)) + i++; + if (i == mEntryBlocks.Size()) + { + for (int j = 0; j < mEntryBlocks.Size(); j++) + mEntryBlocks[j]->ReplaceExitAccuWithX(this); + mEntryRequiredRegs += CPU_REG_X; + mIns[0].mType = ASMIT_STX; mIns[0].mLive |= LIVE_CPU_REG_X; + mIns[1].mType = ASMIT_NOP; mIns[1].mMode = ASMIM_IMPLIED; + changed = true; + } + } + } + } + + + if (mTrueJump && mTrueJump->ReverseLoadAccuToRegXY()) + changed = true; + if (mFalseJump && mFalseJump->ReverseLoadAccuToRegXY()) + changed = true; + } + + return changed; +} + + + +bool NativeCodeBasicBlock::LoopRegisterXYMap(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + if (mLoopHead && mEntryBlocks.Size() > 1) + { + NativeCodeBasicBlock* pblock = nullptr; + ExpandingArray lblocks, pblocks, eblocks; + + bool addprefix = false; + for (int i = 0; i < mEntryBlocks.Size(); i++) + { + NativeCodeBasicBlock* b = mEntryBlocks[i]; + + if (b == this || b->IsDominatedBy(this)) + lblocks.Push(b); + else + { + if (b->mFalseJump) + addprefix = true; + pblocks.Push(b); + } + } + + if (lblocks.Size() > 0) + { + if (pblocks.Size() > 1 || addprefix) + { + pblock = mProc->AllocateBlock(); + pblock->mEntryRequiredRegs = mEntryRequiredRegs; + pblock->mExitRequiredRegs = mEntryRequiredRegs; + pblock->Close(pblocks[0]->mBranchIns, this, nullptr, ASMIT_JMP); + AddEntryBlock(pblock); + for (int i = 0; i < pblocks.Size(); i++) + { + NativeCodeBasicBlock* b = pblocks[i]; + if (this == b->mTrueJump) + { + b->mTrueJump = pblock; + RemEntryBlock(b); + pblock->AddEntryBlock(b); + } + if (this == b->mFalseJump) + { + b->mFalseJump = pblock; + RemEntryBlock(b); + pblock->AddEntryBlock(b); + } + } + } + else if (pblocks.Size() == 1) + pblock = pblocks[0]; + + if (pblock && lblocks.Size() + 1 == mEntryBlocks.Size()) + { + mProc->ResetPatched(); + mPatched = true; + + if (!lblocks.Contains(this)) + lblocks.Push(this); + + for (int i = 0; i < lblocks.Size(); i++) + lblocks[i]->mPatched = true; + pblock->mPatched = true; + + int i = 0; + while (i < lblocks.Size()) + { + NativeCodeBasicBlock* block = lblocks[i]; + for (int j = 0; j < block->mEntryBlocks.Size(); j++) + { + NativeCodeBasicBlock* cblock = block->mEntryBlocks[j]; + if (!cblock->mPatched) + { + cblock->mPatched = true; + lblocks.Push(cblock); + } + } + i++; + } + + bool inner = true; + + NativeCodeBasicBlock* eblock = nullptr; + i = 0; + while (i < lblocks.Size()) + { + NativeCodeBasicBlock* b = lblocks[i]; + if (b->mLoopHead && b != this) + { + inner = false; + break; + } + if (b->mFalseJump && !(lblocks.Contains(b->mFalseJump) && lblocks.Contains(b->mTrueJump))) + { + NativeCodeBasicBlock* eblock = lblocks.Contains(b->mFalseJump) ? b->mTrueJump : b->mFalseJump; + if (eblock->mEntryBlocks.Size() > 1) + { + int k = 0; + while (k < eblock->mEntryBlocks.Size() && lblocks.Contains(eblock->mEntryBlocks[k])) + k++; + + if (k != eblock->mEntryBlocks.Size()) + { + NativeCodeBasicBlock* eb = mProc->AllocateBlock(); + eb->mEntryRequiredRegs = eblock->mEntryRequiredRegs; + eb->mExitRequiredRegs = eblock->mEntryRequiredRegs; + eb->Close(eblock->mBranchIns, eblock, nullptr, ASMIT_JMP); + eb->AddEntryBlock(b); + eblock->AddEntryBlock(eb); + eblock->RemEntryBlock(b); + if (b->mFalseJump == eblock) + b->mFalseJump = eb; + else + b->mTrueJump = eb; + eblocks.Push(eb); + } + else + eblocks.Push(eblock); + } + else + eblocks.Push(eblock); + } + i++; + } + + if (inner && eblocks.Size() > 0) + { + bool xfree = !mEntryRequiredRegs[CPU_REG_X], yfree = !mEntryRequiredRegs[CPU_REG_Y]; + + if (xfree || yfree) + { + int iregs[256]; + for (int i = 0; i < 256; i++) + iregs[i] = 0; + const InterInstruction* iins[256]; + + + for (int i = 0; i < lblocks.Size(); i++) + { + NativeCodeBasicBlock* b(lblocks[i]); + + for (int j = 0; j < b->mIns.Size(); j++) + { + const NativeCodeInstruction& ins(b->mIns[j]); + + if (ins.ReferencesXReg() || ins.ChangesXReg()) + xfree = false; + if (ins.ReferencesYReg() || ins.ChangesYReg()) + yfree = false; + if (ins.mMode == ASMIM_INDIRECT_Y) + { + iregs[ins.mAddress + 0] = -1; + iregs[ins.mAddress + 1] = -1; + } + else if (ins.mMode == ASMIM_ZERO_PAGE && iregs[ins.mAddress] >= 0) + { + if (ins.mType == ASMIT_INC || ins.mType == ASMIT_DEC) + { + iins[ins.mAddress] = ins.mIns; + iregs[ins.mAddress] += 5; + } + else if (ins.mType == ASMIT_LDA || ins.mType == ASMIT_STA) + { + iins[ins.mAddress] = ins.mIns; + iregs[ins.mAddress] += 3; + } + else if (ins.IsCommutative() && j > 0 && b->mIns[j - 1].mType == ASMIT_LDA && (b->mIns[j - 1].mMode == ASMIM_IMMEDIATE || b->mIns[j - 1].mMode == ASMIM_IMMEDIATE_ADDRESS)) + { + iins[ins.mAddress] = ins.mIns; + iregs[ins.mAddress] += 3; + } + else + iregs[ins.mAddress] = -1; + } + } + } + + if (xfree || yfree) + { + int mini = 0; + int mina = 3; + for (int i = 1; i < 256; i++) + { + if (iregs[i] > mina) + { + mina = iregs[i]; + mini = i; + } + } + + if (mini > 0) + { + if (xfree) + { + pblock->mIns.Push(NativeCodeInstruction(iins[mini], ASMIT_LDX, ASMIM_ZERO_PAGE, mini)); + if (pblock->mExitRequiredRegs[CPU_REG_Z]) + { + int i = pblock->mIns.Size() - 1; + while (i >= 0 && !(pblock->mIns[i].ChangesAccu())) + { + pblock->mIns[i].mLive |= LIVE_CPU_REG_A; + i--; + } + pblock->mIns.Push(NativeCodeInstruction(iins[mini], ASMIT_ORA, ASMIM_IMMEDIATE, 0)); + } + + pblock->mExitRequiredRegs += CPU_REG_X; + for (int i = 0; i < eblocks.Size(); i++) + { + eblocks[i]->mIns.Insert(0, NativeCodeInstruction(iins[mini], ASMIT_STX, ASMIM_ZERO_PAGE, mini)); + eblocks[i]->mEntryRequiredRegs += CPU_REG_X; + } + } + else + { + pblock->mIns.Push(NativeCodeInstruction(iins[mini], ASMIT_LDY, ASMIM_ZERO_PAGE, mini)); + if (pblock->mExitRequiredRegs[CPU_REG_Z]) + pblock->mIns.Push(NativeCodeInstruction(iins[mini], ASMIT_ORA, ASMIM_IMMEDIATE, 0)); + + pblock->mExitRequiredRegs += CPU_REG_Y; + for (int i = 0; i < eblocks.Size(); i++) + { + eblocks[i]->mIns.Insert(0, NativeCodeInstruction(iins[mini], ASMIT_STY, ASMIM_ZERO_PAGE, mini)); + eblocks[i]->mEntryRequiredRegs += CPU_REG_Y; + } + } + + for (int i = 0; i < lblocks.Size(); i++) + { + NativeCodeBasicBlock* b(lblocks[i]); + + if (xfree) + { + b->mEntryRequiredRegs += CPU_REG_X; + b->mExitRequiredRegs += CPU_REG_X; + } + else + { + b->mEntryRequiredRegs += CPU_REG_Y; + b->mExitRequiredRegs += CPU_REG_Y; + } + + for (int j = 0; j < b->mIns.Size(); j++) + { + NativeCodeInstruction& ins(b->mIns[j]); + + if (xfree) + ins.mLive |= LIVE_CPU_REG_X; + else + ins.mLive |= LIVE_CPU_REG_Y; + + if (ins.mMode == ASMIM_ZERO_PAGE && ins.mAddress == mini) + { + if (ins.IsCommutative()) + { + ins.CopyMode(b->mIns[j - 1]); + if (xfree) + b->mIns[j - 1].mType = ASMIT_TXA; + else + b->mIns[j - 1].mType = ASMIT_TYA; + b->mIns[j - 1].mMode = ASMIM_IMPLIED; + } + else + { + switch (ins.mType) + { + case ASMIT_INC: + if (xfree) + ins.mType = ASMIT_INX; + else + ins.mType = ASMIT_INY; + ins.mMode = ASMIM_IMPLIED; + break; + case ASMIT_DEC: + if (xfree) + ins.mType = ASMIT_DEX; + else + ins.mType = ASMIT_DEY; + ins.mMode = ASMIM_IMPLIED; + break; + case ASMIT_LDA: + if (xfree) + ins.mType = ASMIT_TXA; + else + ins.mType = ASMIT_TYA; + ins.mMode = ASMIM_IMPLIED; + break; + case ASMIT_STA: + if (xfree) + ins.mType = ASMIT_TAX; + else + ins.mType = ASMIT_TAY; + ins.mMode = ASMIM_IMPLIED; + break; + } + } + } + } + } + + changed = true; + } + } + } + } + } + } + } + + if (mTrueJump && mTrueJump->LoopRegisterXYMap()) changed = true; + if (mFalseJump && mFalseJump->LoopRegisterXYMap()) changed = true; + } + + return changed; +} + +void NativeCodeBasicBlock::GlobalRegisterXYCheck(int* xregs, int* yregs) +{ + if (!mVisited) + { + mVisited = true; + + int yreg = -1; + int xreg = -1; + + int iinc = mLoopHead ? 2 : 1; + + for (int i = 0; i < mIns.Size(); i++) + { + const NativeCodeInstruction& ins(mIns[i]); + if (ins.mMode == ASMIM_ZERO_PAGE) + { + switch (ins.mType) + { + case ASMIT_LDA: + if (yregs[ins.mAddress] >= 0) + yregs[ins.mAddress] += iinc; + if (xregs[ins.mAddress] >= 0) + xregs[ins.mAddress] += iinc; + break; + case ASMIT_LDY: + if (yregs[ins.mAddress] >= 0) + { + yregs[ins.mAddress] += 2 * iinc; + yreg = ins.mAddress; + } + for (int i = 1; i < 256; i++) + if (i != ins.mAddress) + yregs[i] = -1; + + xregs[ins.mAddress] = -1; + break; + case ASMIT_LDX: + if (xregs[ins.mAddress] >= 0) + { + xregs[ins.mAddress] += 2 * iinc; + xreg = ins.mAddress; + } + for (int i = 1; i < 256; i++) + if (i != ins.mAddress) + xregs[i] = -1; + + yregs[ins.mAddress] = -1; + break; + case ASMIT_STA: + if (yreg == ins.mAddress) + yreg = -1; + if (xreg == ins.mAddress) + xreg = -1; + + if (ins.mLive & LIVE_CPU_REG_Z) + { + xregs[ins.mAddress + 0] = -1; + yregs[ins.mAddress + 0] = -1; + } + else + { + if (yregs[ins.mAddress] >= 0) + yregs[ins.mAddress] += iinc; + if (xregs[ins.mAddress] >= 0) + xregs[ins.mAddress] += iinc; + } + break; + case ASMIT_INC: + case ASMIT_DEC: + if (yreg == ins.mAddress) + yreg = -1; + if (xreg == ins.mAddress) + xreg = -1; + + if (yregs[ins.mAddress] >= 0) + yregs[ins.mAddress] += 3 * iinc; + if (xregs[ins.mAddress] >= 0) + xregs[ins.mAddress] += 3 * iinc; + break; + default: + xregs[ins.mAddress + 0] = -1; + yregs[ins.mAddress + 0] = -1; + break; + } + } + else if (ins.mMode == ASMIM_INDIRECT_Y) + { + for (int i = 1; i < 256; i++) + if (i != yreg) + yregs[i] = -1; + + xregs[ins.mAddress + 0] = -1; + xregs[ins.mAddress + 1] = -1; + yregs[ins.mAddress + 0] = -1; + yregs[ins.mAddress + 1] = -1; + } + else if (ins.mMode == ASMIM_ZERO_PAGE_X || ins.mMode == ASMIM_ZERO_PAGE_Y) + { + + } + else + { + if (ins.RequiresXReg()) + { + for (int i = 1; i < 256; i++) + if (i != xreg) + xregs[i] = -1; + } + if (ins.RequiresYReg()) + { + for (int i = 1; i < 256; i++) + if (i != yreg) + yregs[i] = -1; + } + if (ins.ChangesXReg()) + xregs[0] = -1; + if (ins.ChangesYReg()) + yregs[0] = -1; + } + } + + if (xregs[0] >= 0 || yregs[0] >= 0) + { + if (mTrueJump) + mTrueJump->GlobalRegisterXYCheck(xregs, yregs); + if (mFalseJump) + mFalseJump->GlobalRegisterXYCheck(xregs, yregs); + } + } +} + +bool NativeCodeBasicBlock::RemapZeroPage(const uint8* remap) +{ + bool modified = false; + + if (!mVisited) + { + mVisited = true; + + for (int i = 0; i < mIns.Size(); i++) + { + int addr; + + switch (mIns[i].mMode) + { + case ASMIM_ZERO_PAGE: + case ASMIM_INDIRECT_Y: + addr = remap[mIns[i].mAddress]; + if (addr != mIns[i].mAddress) + { + mIns[i].mAddress = addr; + modified = true; + } + break; + case ASMIM_ABSOLUTE: + if (mIns[i].mType == ASMIT_JSR && mIns[i].mLinkerObject) + { + LinkerObject* lo = mIns[i].mLinkerObject; + + for (int j = 0; j < lo->mNumTemporaries; j++) + { + addr = remap[lo->mTemporaries[j]]; + if (addr != lo->mTemporaries[j]) + { + lo->mTemporaries[j] = addr; + modified = true; + } + } + + if (mIns[i].mFlags & NCIF_USE_ZP_32_X) + mIns[i].mParam = remap[mIns[i].mParam]; + } + break; + } + } + + if (mTrueJump && mTrueJump->RemapZeroPage(remap)) + modified = true; + + if (mFalseJump && mFalseJump->RemapZeroPage(remap)) + modified = true; + } + + return modified; +} + +bool NativeCodeBasicBlock::SameTail(const NativeCodeInstruction& ins) const +{ + if (mIns.Size() > 0) + return mIns[mIns.Size() - 1].IsSame(ins); + else + return false; +} + +bool NativeCodeBasicBlock::HasTailSTAX16(int& addr, int& index) const +{ + int sz = mIns.Size(); + if (sz >= 4) + { + sz -= 4; + if (mIns[sz + 0].mType == ASMIT_LDA && HasAsmInstructionMode(ASMIT_LDX, mIns[sz + 0].mMode) && + mIns[sz + 1].mType == ASMIT_STA && mIns[sz + 1].mMode == ASMIM_ZERO_PAGE && + mIns[sz + 2].mType == ASMIT_LDA && + mIns[sz + 3].mType == ASMIT_STA && mIns[sz + 3].mMode == ASMIM_ZERO_PAGE && mIns[sz + 3].mAddress == mIns[sz + 1].mAddress + 1 && + !mIns[sz + 1].SameEffectiveAddress(mIns[sz + 2])) + { + addr = mIns[sz + 1].mAddress; + index = sz; + return true; + } + } + + return false; +} + +bool NativeCodeBasicBlock::HasTailSTA(int& addr, int& index) const +{ + int i = mIns.Size(); + while (i > 0) + { + i--; + if (mIns[i].ChangesAccu()) + return false; + if (mIns[i].mType == ASMIT_STA && mIns[i].mMode == ASMIM_ZERO_PAGE) + { + index = i; + addr = mIns[i].mAddress; + + i++; + while (i < mIns.Size()) + { + if (mIns[i].ReferencesZeroPage(addr)) + return false; + i++; + } + return true; + } + } + return false; +} + +bool NativeCodeBasicBlock::HasTailSTX(int& addr, int& index) const +{ + int i = mIns.Size(); + while (i > 0) + { + i--; + if (mIns[i].ChangesXReg()) + return false; + if (mIns[i].mType == ASMIT_STX && mIns[i].mMode == ASMIM_ZERO_PAGE) + { + index = i; + addr = mIns[i].mAddress; + + i++; + while (i < mIns.Size()) + { + if (mIns[i].ReferencesZeroPage(addr)) + return false; + i++; + } + return true; + } + } + return false; +} + +bool NativeCodeBasicBlock::HasTailSTY(int& addr, int& index) const +{ + int i = mIns.Size(); + while (i > 0) + { + i--; + if (mIns[i].ChangesYReg()) + return false; + if (mIns[i].mType == ASMIT_STY && mIns[i].mMode == ASMIM_ZERO_PAGE) + { + index = i; + addr = mIns[i].mAddress; + + i++; + while (i < mIns.Size()) + { + if (mIns[i].ReferencesZeroPage(addr)) + return false; + i++; + } + return true; + } + } + return false; +} + +bool NativeCodeBasicBlock::HasTailSTAInto(int& addr, int& index, NativeCodeBasicBlock* tblock) const +{ + if (HasTailSTA(addr, index)) + { + if (mFalseJump) + { + if (tblock == mFalseJump) + return !mTrueJump->mEntryRequiredRegs[addr]; + else + return !mFalseJump->mEntryRequiredRegs[addr]; + } + else + return true; + } + return false; +} + +bool NativeCodeBasicBlock::HasTailSTXInto(int& addr, int& index, NativeCodeBasicBlock* tblock) const +{ + if (HasTailSTX(addr, index)) + { + if (mFalseJump) + { + if (tblock == mFalseJump) + return !mTrueJump->mEntryRequiredRegs[addr]; + else + return !mFalseJump->mEntryRequiredRegs[addr]; + } + else + return true; + } + return false; +} + +bool NativeCodeBasicBlock::HasTailSTYInto(int& addr, int& index, NativeCodeBasicBlock* tblock) const +{ + if (HasTailSTY(addr, index)) + { + if (mFalseJump) + { + if (tblock == mFalseJump) + return !mTrueJump->mEntryRequiredRegs[addr]; + else + return !mFalseJump->mEntryRequiredRegs[addr]; + } + else + return true; + } + return false; + +} + +bool NativeCodeBasicBlock::HasTailAccuReg(int addr) const +{ + int i = mIns.Size() - 1; + while (i >= 0) + { + if ((mIns[i].mType == ASMIT_LDA || mIns[i].mType == ASMIT_STA) && mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i].mAddress == addr) + return true; + if (mIns[i].ChangesAccu() || mIns[i].ChangesZeroPage(addr)) + return false; + i--; + } + return false; +} + +bool NativeCodeBasicBlock::HasTailSTAGlobal(NativeCodeInstruction & ins, int& index) const +{ + int i = mIns.Size(); + while (i > 0) + { + i--; + if (mIns[i].ChangesAccu()) + return false; + if (mIns[i].mType == ASMIT_STA && mIns[i].mMode != ASMIM_ZERO_PAGE) + { + index = i; + ins = mIns[i]; + + i++; + while (i < mIns.Size()) + { + if (mIns[i].MayReference(ins, true)) + return false; + else if (ins.ReferencesXReg() && mIns[i].ChangesXReg()) + return false; + else if (ins.ReferencesYReg() && mIns[i].ChangesYReg()) + return false; + i++; + } + return true; + } + } + return false; +} + +bool NativeCodeBasicBlock::CanMovePairBefore(int at) +{ + int i = at; + while (i > 0) + { + i--; + if (mIns[i].mType == ASMIT_JSR) + return false; + if (mIns[i].ReferencesZeroPage(mIns[at].mAddress)) + return false; + if (mIns[i].MayBeSameAddress(mIns[at + 1], true)) + return false; + if (mIns[at + 1].RequiresYReg() && mIns[i].ChangesYReg()) + return false; + if (mIns[at + 1].RequiresXReg() && mIns[i].ChangesXReg()) + return false; + } + + return true; +} + +bool NativeCodeBasicBlock::CanMoveStoreUp(int addr, const NativeCodeInstruction& ins, int& at) +{ + int i = mIns.Size(); + while (i > 0) + { + i--; + if (mIns[i].mType == ASMIT_STA && mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i].mAddress == addr) + { + at = i; + return true; + } + if (mIns[i].ReferencesZeroPage(addr)) + return false; + if (mIns[i].MayBeSameAddress(ins, true)) + return false; + if (ins.RequiresYReg() && mIns[i].ChangesYReg()) + return false; + if (ins.RequiresXReg() && mIns[i].ChangesXReg()) + return false; + } + + return false; +} + +bool NativeCodeBasicBlock::JoinCommonHeadSequence(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + if (mEntryBlocks.Size() > 1 && mIns.Size() > 0 && !mLoopHead) + { + int ei = 0; + while (ei < mEntryBlocks.Size() && !mEntryBlocks[ei]->mFalseJump) + ei++; + + if (ei == mEntryBlocks.Size()) + { +#if 1 + if (mIns[0].mType == ASMIT_STA) + { + NativeCodeBasicBlock* e0 = mEntryBlocks[0]; + int s0 = e0->mIns.Size(); + if (s0 >= 2 && e0->mIns[s0 - 1].IsCommutative() && e0->mIns[s0 - 2].mType == ASMIT_LDA) + { + bool match = true; + for (int i = 1; i < mEntryBlocks.Size(); i++) + { + NativeCodeBasicBlock* eb = mEntryBlocks[i]; + int sz = eb->mIns.Size(); + if (sz >= 2 && eb->mIns[sz - 1].mType == e0->mIns[s0 - 1].mType && eb->mIns[sz - 2].mType == ASMIT_LDA && eb->mIns[sz - 2].SameEffectiveAddress(e0->mIns[s0 - 2])) + ; + else + { + match = false; + break; + } + } + if (match) + { + mIns.Insert(0, NativeCodeInstruction(e0->mIns[s0 - 1].mIns, e0->mIns[s0 - 1].mType, e0->mIns[s0 - 2])); + for (int i = 0; i < mEntryBlocks.Size(); i++) + { + NativeCodeBasicBlock* eb = mEntryBlocks[i]; + int sz = eb->mIns.Size(); + eb->mIns[sz - 2].CopyMode(eb->mIns[sz - 1]); + eb->mIns[sz - 1].mType = ASMIT_NOP; eb->mIns[sz - 1].mMode = ASMIM_IMPLIED; + } + changed = true; + } + } + if (!changed && s0 >= 2 && e0->mIns[s0 - 1].IsAddSub() && e0->mIns[s0 - 1].mMode == ASMIM_IMMEDIATE && e0->mIns[s0 - 2].mType == ASMIT_LDA) + { + bool match = true; + for (int i = 1; i < mEntryBlocks.Size(); i++) + { + NativeCodeBasicBlock* eb = mEntryBlocks[i]; + int sz = eb->mIns.Size(); + if (sz >= 2 && eb->mIns[sz - 1].IsAddSub() && eb->mIns[sz - 1].mMode == ASMIM_IMMEDIATE && eb->mIns[sz - 2].mType == ASMIT_LDA && eb->mIns[sz - 2].SameEffectiveAddress(e0->mIns[s0 - 2])) + ; + else + { + match = false; + break; + } + } + if (match) + { + mIns.Insert(0, NativeCodeInstruction(e0->mIns[s0 - 1].mIns, ASMIT_ADC, e0->mIns[s0 - 2])); + for (int i = 0; i < mEntryBlocks.Size(); i++) + { + NativeCodeBasicBlock* eb = mEntryBlocks[i]; + int sz = eb->mIns.Size(); + eb->mIns[sz - 2].mType = ASMIT_LDA; eb->mIns[sz - 2].mMode = ASMIM_IMMEDIATE; + eb->mIns[sz - 2].mAddress = (eb->mIns[sz - 1].mType == ASMIT_ADC) ? eb->mIns[sz - 1].mAddress : (eb->mIns[sz - 1].mAddress ^ 0xff); + eb->mIns[sz - 1].mType = ASMIT_NOP; eb->mIns[sz - 1].mMode = ASMIM_IMPLIED; + } + changed = true; + } + } + } +#endif +#if 1 + for (int i = 0; i + 1 < mIns.Size(); i++) + { + if (mIns[i].mType == ASMIT_LDA && mIns[i].mMode == ASMIM_ZERO_PAGE && !(mIns[i].mLive & LIVE_MEM) && + mIns[i + 1].mType == ASMIT_STA && !(mIns[i + 1].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z))) + { + if (CanMovePairBefore(i)) + { + ei = 0; + while (ei < mEntryBlocks.Size() && mEntryBlocks[ei]->CanMoveStoreUp(mIns[i].mAddress, mIns[i + 1], mEntryBlocks[ei]->mTemp)) + ei++; + if (ei == mEntryBlocks.Size()) + { + for (int ei = 0; ei < mEntryBlocks.Size(); ei++) + { + int live = mEntryBlocks[ei]->mIns[mEntryBlocks[ei]->mTemp].mLive; + mEntryBlocks[ei]->mIns[mEntryBlocks[ei]->mTemp] = mIns[i + 1]; + mEntryBlocks[ei]->mIns[mEntryBlocks[ei]->mTemp].mLive |= live; + } + mIns[i].mType = ASMIT_NOP; mIns[i].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + changed = true; + } + } + } + } +#endif + } + } + + if (mTrueJump && mTrueJump->JoinCommonHeadSequence()) changed = true; + if (mFalseJump && mFalseJump->JoinCommonHeadSequence()) changed = true; + } + + return changed; +} + +void NativeCodeBasicBlock::AddEntryBlock(NativeCodeBasicBlock* block) +{ + assert(mEntryBlocks.Size() == mNumEntries); + int i = mEntryBlocks.IndexOf(block); + assert(i < 0); + mEntryBlocks.Push(block); + mNumEntries++; +} + +void NativeCodeBasicBlock::RemEntryBlock(NativeCodeBasicBlock* block) +{ + assert(mEntryBlocks.Size() == mNumEntries); + int i = mEntryBlocks.IndexOf(block); + assert(i >= 0); + mEntryBlocks.Remove(i); + mNumEntries--; +} + + +NativeCodeBasicBlock * NativeCodeBasicBlock::SplitMatchingTails(NativeCodeProcedure* proc) +{ + NativeCodeBasicBlock* nblock = nullptr; + + for (int i = 0; i < mEntryBlocks.Size() - 1; i++) + { + NativeCodeBasicBlock* bi(mEntryBlocks[i]); + + if (bi->mBranch == ASMIT_JMP && bi->mIns.Size() > 0) + { + for (int j = i + 1; j < mEntryBlocks.Size(); j++) + { + NativeCodeBasicBlock* bj(mEntryBlocks[j]); + + if (bj->mBranch == ASMIT_JMP && bj->mIns.Size() > 0) + { + if (bi->mIns[bi->mIns.Size() - 1].IsSame(bj->mIns[bj->mIns.Size() - 1])/* && + bi->mIns[bi->mIns.Size() - 2].IsSame(bj->mIns[bj->mIns.Size() - 2])*/) + { + if (!nblock) + { + nblock = proc->AllocateBlock(); + nblock->mBranch = ASMIT_JMP; + nblock->mVisited = false; + nblock->mTrueJump = this; + + nblock->mEntryBlocks.Push(bi); + nblock->mNumEntries++; + + nblock->mEntryRequiredRegs = mEntryRequiredRegs; + nblock->mExitRequiredRegs = mEntryRequiredRegs; + + bi->mTrueJump = nblock; + mEntryBlocks[i] = nullptr; + mNumEntries--; + } + + nblock->mEntryBlocks.Push(bj); + nblock->mNumEntries++; + + bj->mTrueJump = nblock; + mEntryBlocks[j] = nullptr; + mNumEntries--; + } + } + } + + if (nblock) + { + int i = 0; + while (i < mEntryBlocks.Size()) + { + if (mEntryBlocks[i]) + i++; + else + mEntryBlocks.Remove(i); + } + + mEntryBlocks.Push(nblock); + mNumEntries++; + + return nblock; + } + } + } + + return nullptr; +} + +NativeCodeBasicBlock* NativeCodeBasicBlock::AddDominatorBlock(NativeCodeProcedure* proc, NativeCodeBasicBlock* pblock) +{ + if (pblock->mFalseJump) + { + NativeCodeBasicBlock* tblock = proc->AllocateBlock(); + tblock->mBranch = ASMIT_JMP; + tblock->mTrueJump = this; + tblock->mEntryBlocks.Push(pblock); + + mEntryBlocks[mEntryBlocks.IndexOf(pblock)] = tblock; + + tblock->mEntryRequiredRegs = mEntryRequiredRegs; + tblock->mExitRequiredRegs = mEntryRequiredRegs; + + if (pblock->mTrueJump == this) + pblock->mTrueJump = tblock; + if (pblock->mFalseJump == this) + pblock->mFalseJump = tblock; + + return tblock; + } + else + return pblock; +} + +static bool ReferencedOnPath(const NativeCodeBasicBlock* block, int start, int end, int address) +{ + for (int i = start; i < end; i++) + if (block->mIns[i].ReferencesZeroPage(address)) + return true; + return false; +} + +static bool ChangedOnPath(const NativeCodeBasicBlock* block, int start, int end, int address) +{ + for (int i = start; i < end; i++) + if (block->mIns[i].ChangesZeroPage(address)) + return true; + return false; +} + +static bool ChangesAccuOnPath(const NativeCodeBasicBlock* block, int start, int end) +{ + for (int i = start; i < end; i++) + if (block->mIns[i].ChangesAccu()) + return true; + return false; +} + +static bool ReferencedsRegOnPath(const NativeCodeBasicBlock* block, int start, int end) +{ + for (int i = start; i < end; i++) + if (block->mIns[i].ReferencesYReg()) + return true; + return false; +} + + +void NativeCodeBasicBlock::PrependInstruction(const NativeCodeInstruction& ins) +{ + mIns.Insert(0, ins); + if (mEntryRequiredRegs[CPU_REG_A]) + mIns[0].mLive |= LIVE_CPU_REG_A; + if (mEntryRequiredRegs[CPU_REG_X]) + mIns[0].mLive |= LIVE_CPU_REG_X; + if (mEntryRequiredRegs[CPU_REG_Y]) + mIns[0].mLive |= LIVE_CPU_REG_Y; + if (mEntryRequiredRegs[CPU_REG_C]) + mIns[0].mLive |= LIVE_CPU_REG_C; + if (ins.mMode == ASMIM_ZERO_PAGE && ins.UsesAddress()) + mEntryRequiredRegs += ins.mAddress; +} + +bool NativeCodeBasicBlock::ShortcutBlockExit(void) +{ + bool changed = false; + if (!mVisited) + { + mVisited = true; + + if (!mLoopHead && mTrueJump && mFalseJump && !mTrueJump->mLoopHead && !mFalseJump->mLoopHead) + { + for (int i = 0; i < mTrueJump->mEntryBlocks.Size(); i++) + { + NativeCodeBasicBlock* eblock = mTrueJump->mEntryBlocks[i]; + if (eblock != this) + { + if (eblock->mTrueJump == mTrueJump && eblock->mFalseJump == mFalseJump && eblock->mBranch == mBranch) + { + int n = mIns.Size(), m = eblock->mIns.Size(); + if (m > 0 && n >= m) + { + while (m > 0 && mIns[n - 1].IsSame(eblock->mIns[m - 1])) + { + n--; + m--; + } + + if (m == 0) + { + mTrueJump->mNumEntries--; + mTrueJump->mEntryBlocks.RemoveAll(this); + mFalseJump->mNumEntries--; + mFalseJump->mEntryBlocks.RemoveAll(this); + mFalseJump = nullptr; + mTrueJump = eblock; + eblock->mNumEntries++; + eblock->mEntryBlocks.Push(this); + + mIns.SetSize(n); + mBranch = ASMIT_JMP; + changed = true; + break; + } + } + } + } + } + } + + if (mTrueJump && mTrueJump->ShortcutBlockExit()) + changed = true; + if (mFalseJump && mFalseJump->ShortcutBlockExit()) + changed = true; + + } + + return changed; +} + +bool NativeCodeBasicBlock::ShortcutORACascade(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + CheckLive(); + + if (mTrueJump && mFalseJump && (mBranch == ASMIT_BEQ || mBranch == ASMIT_BNE)) + { + NativeCodeBasicBlock* block; + if (mBranch == ASMIT_BEQ) + block = mFalseJump; + else + block = mTrueJump; + + int sz = mIns.Size(); + if (sz > 0 && (block->mBranch == ASMIT_BEQ || block->mBranch == ASMIT_BNE)) + { + NativeCodeBasicBlock* nblock; + + if (block->mBranch == ASMIT_BEQ) + nblock = block->mFalseJump; + else + nblock = block->mTrueJump; + + if (!nblock->mEntryRequiredRegs[CPU_REG_A]) + { + if (mIns[sz - 1].ChangesAccuAndFlag()) + { + int j = 0; + while (j < block->mIns.Size() && block->mIns[j].mType == ASMIT_ORA) + j++; + if (j == block->mIns.Size()) + { + + block->RemEntryBlock(this); + nblock->AddEntryBlock(this); + if (mBranch == ASMIT_BEQ) + mFalseJump = nblock; + else + mTrueJump = nblock; + nblock->CheckLive(); + + changed = true; + } + } + else if (mIns[sz - 1].IsShiftOrInc() && mIns[sz - 1].mMode == ASMIM_ZERO_PAGE) + { + if (!block->ChangesZeroPage(mIns[sz - 1].mAddress)) + { + int j = block->mIns.Size(); + while (j > 0 && block->mIns[j - 1].mType == ASMIT_ORA) + j--; + if (j > 0 && (block->mIns[j - 1].mType == ASMIT_LDA || block->mIns[j - 1].mType == ASMIT_TXA || block->mIns[j - 1].mType == ASMIT_TYA)) + j--; + if (j == 0) + { + while (j < block->mIns.Size() && !block->mIns[j].SameEffectiveAddress(mIns[sz - 1])) + j++; + if (j < block->mIns.Size()) + { + NativeCodeBasicBlock* nblock; + + if (block->mBranch == ASMIT_BEQ) + nblock = block->mFalseJump; + else + nblock = block->mTrueJump; + + block->RemEntryBlock(this); + nblock->AddEntryBlock(this); + if (mBranch == ASMIT_BEQ) + mFalseJump = nblock; + else + mTrueJump = nblock; + nblock->CheckLive(); + + changed = true; + } + } + } + } + } + } + } + + if (mTrueJump && mTrueJump->ShortcutORACascade()) + changed = true; + if (mFalseJump && mFalseJump->ShortcutORACascade()) + changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::SplitSinglePath(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + int sz = mIns.Size(); + if (sz >= 3 && (mBranch == ASMIT_BEQ || mBranch == ASMIT_BNE) && mExitRequiredRegs.Size() && !mExitRequiredRegs[CPU_REG_C]) + { + if (mIns[sz - 3].mType == ASMIT_TAY && + mIns[sz - 2].mType == ASMIT_LDA && mIns[sz - 2].mMode == ASMIM_IMMEDIATE && mIns[sz - 2].mAddress == 0 && + mIns[sz - 1].mType == ASMIT_CPY && mIns[sz - 1].mMode == ASMIM_IMMEDIATE && mIns[sz - 1].mAddress == 0) + { + NativeCodeBasicBlock* eblock = mProc->AllocateBlock(); + eblock->mBranch = ASMIT_JMP; + if (mBranch == ASMIT_BEQ) + { + eblock->mTrueJump = mFalseJump; + mFalseJump = eblock; + } + else + { + eblock->mTrueJump = mTrueJump; + mTrueJump = eblock; + } + + mIns[sz - 3].mLive |= LIVE_CPU_REG_A | LIVE_CPU_REG_Z; + eblock->mIns.Push(mIns[sz - 2]); + mIns.SetSize(sz - 2); + + eblock->mNumEntries = 1; + eblock->mEntryBlocks.Push(this); + eblock->mTrueJump->mEntryBlocks.RemoveAll(this); + eblock->mTrueJump->mEntryBlocks.Push(eblock); + + eblock->mExitProvidedRegs = mExitProvidedRegs; + changed = true; + } + else if (mIns[sz - 3].mType == ASMIT_TAX && + mIns[sz - 2].mType == ASMIT_LDA && mIns[sz - 2].mMode == ASMIM_IMMEDIATE && mIns[sz - 2].mAddress == 0 && + mIns[sz - 1].mType == ASMIT_CPX && mIns[sz - 1].mMode == ASMIM_IMMEDIATE && mIns[sz - 1].mAddress == 0) + { + NativeCodeBasicBlock* eblock = mProc->AllocateBlock(); + eblock->mBranch = ASMIT_JMP; + if (mBranch == ASMIT_BEQ) + { + eblock->mTrueJump = mFalseJump; + mFalseJump = eblock; + } + else + { + eblock->mTrueJump = mTrueJump; + mTrueJump = eblock; + } + + mIns[sz - 3].mLive |= LIVE_CPU_REG_A | LIVE_CPU_REG_Z; + eblock->mIns.Push(mIns[sz - 2]); + mIns.SetSize(sz - 2); + + eblock->mNumEntries = 1; + eblock->mEntryBlocks.Push(this); + eblock->mTrueJump->mEntryBlocks.RemoveAll(this); + eblock->mTrueJump->mEntryBlocks.Push(eblock); + + eblock->mExitProvidedRegs = mExitProvidedRegs; + changed = true; + } + } + + if (mTrueJump && mFalseJump) + { + if (mTrueJump->mIns.Size() > 0 && mTrueJump->mIns[0].mType == ASMIT_STA && mTrueJump->mIns[0].mMode == ASMIM_ZERO_PAGE) + { + int index; + NativeCodeBasicBlock* block; + + if (IsExitARegZP(mTrueJump->mIns[0].mAddress, index, block)) + { + NativeCodeBasicBlock* eblock = mTrueJump->SplitBlock(1); + eblock->mExitRequiredRegs = mTrueJump->mEntryRequiredRegs; + + mTrueJump->mEntryBlocks.RemoveAll(this); + mTrueJump->mNumEntries--; + + mTrueJump = eblock; + mTrueJump->mEntryBlocks.Push(this); + mTrueJump->mNumEntered++; + + changed = true; + } + } + if (!changed && mFalseJump->mIns.Size() > 0 && mFalseJump->mIns[0].mType == ASMIT_STA && mFalseJump->mIns[0].mMode == ASMIM_ZERO_PAGE) + { + int index; + NativeCodeBasicBlock* block; + + if (IsExitARegZP(mFalseJump->mIns[0].mAddress, index, block)) + { + NativeCodeBasicBlock* eblock = mFalseJump->SplitBlock(1); + eblock->mExitRequiredRegs = mFalseJump->mEntryRequiredRegs; + + mFalseJump->mEntryBlocks.RemoveAll(this); + mFalseJump->mNumEntries--; + + mFalseJump = eblock; + mFalseJump->mEntryBlocks.Push(this); + mFalseJump->mNumEntered++; + + changed = true; + } + } + if (!changed) + { + int sz = mIns.Size(); + if (sz > 0 && mIns[sz - 1].mType == ASMIT_LDA && mIns[sz - 1].mMode == ASMIM_ZERO_PAGE && !(mIns[sz - 1].mLive & LIVE_CPU_REG_A)) + { + if (mTrueJump->mIns.Size() > 0 && !mTrueJump->mLoopHead && mTrueJump->mIns[0].mType == ASMIT_LDX && mTrueJump->mIns[0].mMode == ASMIM_ZERO_PAGE && mTrueJump->mIns[0].mAddress == mIns[sz - 1].mAddress && !(mIns[sz - 1].mLive & LIVE_CPU_REG_X)) + { + NativeCodeBasicBlock* eblock = mTrueJump->SplitBlock(1); + eblock->mExitRequiredRegs = mTrueJump->mEntryRequiredRegs; + + mTrueJump->mEntryBlocks.RemoveAll(this); + mTrueJump->mNumEntries--; + + mTrueJump = eblock; + mTrueJump->mEntryBlocks.Push(this); + mTrueJump->mNumEntered++; + + mIns[sz - 1].mType = ASMIT_LDX; + mIns[sz - 1].mLive |= LIVE_CPU_REG_X; + mExitRequiredRegs += CPU_REG_X; + + changed = true; + } + if (mFalseJump->mIns.Size() > 0 && !mFalseJump->mLoopHead && mFalseJump->mIns[0].mType == ASMIT_LDX && mFalseJump->mIns[0].mMode == ASMIM_ZERO_PAGE && mFalseJump->mIns[0].mAddress == mIns[sz - 1].mAddress && !(mIns[sz - 1].mLive & LIVE_CPU_REG_X)) + { + NativeCodeBasicBlock* eblock = mFalseJump->SplitBlock(1); + eblock->mExitRequiredRegs = mFalseJump->mEntryRequiredRegs; + + mFalseJump->mEntryBlocks.RemoveAll(this); + mFalseJump->mNumEntries--; + + mFalseJump = eblock; + mFalseJump->mEntryBlocks.Push(this); + mFalseJump->mNumEntered++; + + mIns[sz - 1].mType = ASMIT_LDX; + mIns[sz - 1].mLive |= LIVE_CPU_REG_X; + mExitRequiredRegs += CPU_REG_X; + + changed = true; + } + if (mTrueJump->mIns.Size() > 0 && !mTrueJump->mLoopHead && mTrueJump->mIns[0].mType == ASMIT_LDY && mTrueJump->mIns[0].mMode == ASMIM_ZERO_PAGE && mTrueJump->mIns[0].mAddress == mIns[sz - 1].mAddress && !(mIns[sz - 1].mLive & LIVE_CPU_REG_Y)) + { + NativeCodeBasicBlock* eblock = mTrueJump->SplitBlock(1); + eblock->mExitRequiredRegs = mTrueJump->mEntryRequiredRegs; + + mTrueJump->mEntryBlocks.RemoveAll(this); + mTrueJump->mNumEntries--; + + mTrueJump = eblock; + mTrueJump->mEntryBlocks.Push(this); + mTrueJump->mNumEntered++; + + mIns[sz - 1].mType = ASMIT_LDY; + mIns[sz - 1].mLive |= LIVE_CPU_REG_Y; + mExitRequiredRegs += CPU_REG_Y; + + changed = true; + } + if (mFalseJump->mIns.Size() > 0 && !mFalseJump->mLoopHead && mFalseJump->mIns[0].mType == ASMIT_LDY && mFalseJump->mIns[0].mMode == ASMIM_ZERO_PAGE && mFalseJump->mIns[0].mAddress == mIns[sz - 1].mAddress && !(mIns[sz - 1].mLive & LIVE_CPU_REG_Y)) + { + NativeCodeBasicBlock* eblock = mFalseJump->SplitBlock(1); + eblock->mExitRequiredRegs = mFalseJump->mEntryRequiredRegs; + + mFalseJump->mEntryBlocks.RemoveAll(this); + mFalseJump->mNumEntries--; + + mFalseJump = eblock; + mFalseJump->mEntryBlocks.Push(this); + mFalseJump->mNumEntered++; + + mIns[sz - 1].mType = ASMIT_LDY; + mIns[sz - 1].mLive |= LIVE_CPU_REG_Y; + mExitRequiredRegs += CPU_REG_Y; + + changed = true; + } + } + } + } + + + if (mTrueJump && mTrueJump->SplitSinglePath()) changed = true; + if (mFalseJump && mFalseJump->SplitSinglePath()) changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::PropagateSinglePath(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + CheckLive(); + +#if 1 + if (mTrueJump && mFalseJump && mTrueJump->mEntryRequiredRegs.Size() && mFalseJump->mEntryRequiredRegs.Size()) + { + mTempRegs.Reset(mTrueJump->mEntryRequiredRegs.Size()); + + bool changeX = false, changeY = false, changeA = false; + for (int i = mIns.Size() - 1; i >= 0; i--) + { + NativeCodeInstruction ins(mIns[i]); + + if (ins.mMode == ASMIM_ZERO_PAGE) + { + int addr = ins.mAddress; + + if (!mTempRegs[addr]) + { + if (ins.IsShiftOrInc() && !(ins.ChangesCarry() && (ins.mLive & LIVE_CPU_REG_C)) && !(ins.mLive & LIVE_CPU_REG_Z)) + { + if (mTrueJump->mEntryRequiredRegs[addr] && !mFalseJump->mEntryRequiredRegs[addr] && mTrueJump->mNumEntries == 1 && !mTrueJump->mEntryRequiredRegs[CPU_REG_Z] && !mTrueJump->mEntryRequiredRegs[CPU_REG_C] || + mFalseJump->mEntryRequiredRegs[addr] && !mTrueJump->mEntryRequiredRegs[addr] && mFalseJump->mNumEntries == 1 && !mFalseJump->mEntryRequiredRegs[CPU_REG_Z] && !mFalseJump->mEntryRequiredRegs[CPU_REG_C]) + { + if (mTrueJump->mEntryRequiredRegs[addr]) + mTrueJump->PrependInstruction(mIns[i]); + else + mFalseJump->PrependInstruction(mIns[i]); + mIns.Remove(i); + changed = true; + } + else + mTempRegs += addr; + } + else + mTempRegs += addr; + } + } + else if (ins.mMode == ASMIM_INDIRECT_Y) + { + mTempRegs += ins.mAddress; + mTempRegs += ins.mAddress + 1; + } + else if (ins.mType == ASMIT_JSR) + break; + + if (!changeX && i >= 2 && (mIns[i - 2].mType == ASMIT_INX && mIns[i].mType == ASMIT_DEX || mIns[i - 2].mType == ASMIT_DEX && mIns[i].mType == ASMIT_INX) && + mIns[i - 1].mType == ASMIT_STX && mIns[i - 1].mMode == ASMIM_ZERO_PAGE && !mTempRegs[mIns[i - 1].mAddress]) + { + int addr = mIns[i - 1].mAddress; + if (mTrueJump->mEntryRequiredRegs[addr] && !mFalseJump->mEntryRequiredRegs[addr] && mTrueJump->mNumEntries == 1 || + mFalseJump->mEntryRequiredRegs[addr] && !mTrueJump->mEntryRequiredRegs[addr] && mFalseJump->mNumEntries == 1) + { + for (int j = i + 1; j < mIns.Size(); j++) + mIns[j].mLive |= LIVE_CPU_REG_X; + + if (mTrueJump->mEntryRequiredRegs[addr]) + { + mTrueJump->mEntryRequiredRegs += CPU_REG_X; + mTrueJump->PrependInstruction(mIns[i]); + mTrueJump->PrependInstruction(mIns[i - 1]); + mTrueJump->PrependInstruction(mIns[i - 2]); + } + else + { + mFalseJump->mEntryRequiredRegs += CPU_REG_X; + mFalseJump->PrependInstruction(mIns[i]); + mFalseJump->PrependInstruction(mIns[i - 1]); + mFalseJump->PrependInstruction(mIns[i - 2]); + } + + mIns.Remove(i - 2, 3); + i -= 2; + changed = true; + } + } + + if (ins.ChangesAccu()) changeA = true; + if (ins.ChangesXReg()) changeX = true; + if (ins.ChangesYReg()) changeY = true; + } + } +#endif + +#if 1 + if (mTrueJump && mFalseJump && mTrueJump->mEntryProvidedRegs.Size() && mFalseJump->mEntryRequiredRegs.Size()) + { + if (mTrueJump->mEntryRequiredRegs[CPU_REG_A] && !mFalseJump->mEntryRequiredRegs[CPU_REG_A] && !mTrueJump->mEntryRequiredRegs[CPU_REG_Z] || + mFalseJump->mEntryRequiredRegs[CPU_REG_A] && !mTrueJump->mEntryRequiredRegs[CPU_REG_A] && !mFalseJump->mEntryRequiredRegs[CPU_REG_Z]) + { + int i = mIns.Size(); + while (i > 0 && !mIns[i - 1].ReferencesAccu()) + i--; + if (i > 0 && (mIns[i - 1].mType == ASMIT_LDA || mIns[i - 1].mType == ASMIT_ORA || mIns[i - 1].mType == ASMIT_AND || mIns[i - 1].mType == ASMIT_EOR) && !(mIns[i - 1].mLive & LIVE_CPU_REG_Z)) + { + if (mIns[i - 1].mMode == ASMIM_IMMEDIATE || (mIns[i - 1].mMode == ASMIM_ZERO_PAGE && !ChangedOnPath(this, i, mIns.Size(), mIns[i - 1].mAddress))) + { + if (mIns[i - 1].mMode == ASMIM_ZERO_PAGE) + mExitRequiredRegs += mIns[i - 1].mAddress; + + if (mTrueJump->mEntryRequiredRegs[CPU_REG_A]) + { + mTrueJump = mTrueJump->UniqueEntryBlock(this); + mTrueJump->PrependInstruction(mIns[i - 1]); + } + else + { + mFalseJump = mFalseJump->UniqueEntryBlock(this); + mFalseJump->PrependInstruction(mIns[i - 1]); + } + mIns.Remove(i - 1); + changed = true; + } + } + else if (i > 0 && mIns[i - 1].mType == ASMIT_TXA && !(mIns[i - 1].mLive & LIVE_CPU_REG_Z) && !ChangesXReg(i)) + { + for (int j = i; j < mIns.Size(); j++) + mIns[j].mLive |= LIVE_CPU_REG_X; + mExitRequiredRegs += CPU_REG_X; + if (mTrueJump->mEntryRequiredRegs[CPU_REG_A]) + { + mTrueJump = mTrueJump->UniqueEntryBlock(this); + mTrueJump->PrependInstruction(mIns[i - 1]); + mTrueJump->mEntryRequiredRegs += CPU_REG_X; + } + else + { + mFalseJump = mFalseJump->UniqueEntryBlock(this); + mFalseJump->PrependInstruction(mIns[i - 1]); + mFalseJump->mEntryRequiredRegs += CPU_REG_X; + } + mIns.Remove(i - 1); + changed = true; + } + else if (i > 1 && mIns[i - 1].mType == ASMIT_EOR && mIns[i - 1].mMode == ASMIM_IMMEDIATE && (mBranch == ASMIT_BPL || mBranch == ASMIT_BMI) && mIns[i - 2].ChangesFlagToAccu()) + { + if (mIns[i - 1].mAddress & 0x80) + { + if (mBranch == ASMIT_BPL) + mBranch = ASMIT_BMI; + else + mBranch = ASMIT_BPL; + } + mIns[i - 2].mLive |= LIVE_CPU_REG_Z; + + if (mTrueJump->mEntryRequiredRegs[CPU_REG_A]) + { + mTrueJump = mTrueJump->UniqueEntryBlock(this); + mTrueJump->PrependInstruction(mIns[i - 1]); + } + else + { + mFalseJump = mFalseJump->UniqueEntryBlock(this); + mFalseJump->PrependInstruction(mIns[i - 1]); + } + mIns.Remove(i - 1); + changed = true; + } + else if (i > 0 && mIns[i - 1].mType == ASMIT_ROR && mIns[i - 1].mMode == ASMIM_IMPLIED && (mBranch == ASMIT_BPL || mBranch == ASMIT_BMI)) + { + if (mBranch == ASMIT_BPL) + mBranch = ASMIT_BCC; + else + mBranch = ASMIT_BCS; + + mExitRequiredRegs += CPU_REG_C; + + if (mTrueJump->mEntryRequiredRegs[CPU_REG_A]) + { + mTrueJump = mTrueJump->UniqueEntryBlock(this); + mTrueJump->PrependInstruction(mIns[i - 1]); + mTrueJump->mEntryRequiredRegs += CPU_REG_C; + } + else + { + mFalseJump = mFalseJump->UniqueEntryBlock(this); + mFalseJump->PrependInstruction(mIns[i - 1]); + mFalseJump->mEntryRequiredRegs += CPU_REG_C; + } + mIns.Remove(i - 1); + changed = true; + } + + int sz = mIns.Size(); + + if (sz > 1 && mIns[sz - 1].mType == ASMIT_LDA && mIns[sz - 2].IsShiftOrInc() && mIns[sz - 1].SameEffectiveAddress(mIns[sz - 2])) + { + mIns[sz - 2].mLive |= mIns[sz - 1].mLive & LIVE_CPU_REG_Z; + + if (mIns[sz - 1].mMode == ASMIM_ZERO_PAGE) + mExitRequiredRegs += mIns[sz - 1].mAddress; + + if (mTrueJump->mEntryRequiredRegs[CPU_REG_A]) + { + mTrueJump = mTrueJump->UniqueEntryBlock(this); + mTrueJump->PrependInstruction(mIns[sz - 1]); + } + else + { + mFalseJump = mFalseJump->UniqueEntryBlock(this); + mFalseJump->PrependInstruction(mIns[sz - 1]); + } + mIns.Remove(sz - 1); + changed = true; + } + else if (sz > 1 && mIns[sz - 1].mType == ASMIT_ASL && mIns[sz - 1].mMode == ASMIM_IMPLIED && (mBranch == ASMIT_BCC || mBranch == ASMIT_BCS) && !mExitRequiredRegs[CPU_REG_C] && mIns[sz - 2].ChangesAccuAndFlag()) + { + if (mBranch == ASMIT_BCC) + mBranch = ASMIT_BPL; + else + mBranch = ASMIT_BMI; + + if (mTrueJump->mEntryRequiredRegs[CPU_REG_A]) + { + mTrueJump = mTrueJump->UniqueEntryBlock(this); + mTrueJump->PrependInstruction(mIns[sz - 1]); + } + else + { + mFalseJump = mFalseJump->UniqueEntryBlock(this); + mFalseJump->PrependInstruction(mIns[sz - 1]); + } + mIns[sz - 2].mLive |= LIVE_CPU_REG_Z; + mIns.Remove(sz - 1); + changed = true; + } + else if (sz >= 3 && + (mIns[sz - 1].mType == ASMIT_CPX || mIns[sz - 1].mType == ASMIT_CPY) && mIns[sz - 1].mMode == ASMIM_IMMEDIATE && mIns[sz - 1].mAddress == 0 && !(mIns[sz - 1].mLive & LIVE_CPU_REG_C) && + mIns[sz - 3].mType == ASMIT_LDA && !mIns[sz - 2].ChangesXReg() && !mIns[sz - 2].ChangesYReg() && !mIns[sz - 2].ChangesAddress()) + { + if (mTrueJump->mEntryRequiredRegs[CPU_REG_A]) + { + mTrueJump = mTrueJump->UniqueEntryBlock(this); + mTrueJump->PrependInstruction(mIns[sz - 2]); + mTrueJump->PrependInstruction(mIns[sz - 3]); + } + else + { + mFalseJump = mFalseJump->UniqueEntryBlock(this); + mFalseJump->PrependInstruction(mIns[sz - 2]); + mFalseJump->PrependInstruction(mIns[sz - 3]); + } + mIns[sz - 1].mType = mIns[sz - 1].mType == ASMIT_CPX ? ASMIT_TXA : ASMIT_TYA; + mIns[sz - 1].mMode = ASMIM_IMPLIED; + mIns[sz - 1].mLive |= mIns[sz - 2].mLive | mIns[sz - 2].NeedsLive(); + mIns.Remove(sz - 2); + mIns.Remove(sz - 3); + changed = true; + } + } + if (mTrueJump->mEntryRequiredRegs[CPU_REG_X] && !mFalseJump->mEntryRequiredRegs[CPU_REG_X] && mTrueJump->mNumEntries == 1 && !mTrueJump->mEntryRequiredRegs[CPU_REG_Z]|| + mFalseJump->mEntryRequiredRegs[CPU_REG_X] && !mTrueJump->mEntryRequiredRegs[CPU_REG_X] && mFalseJump->mNumEntries == 1 && !mFalseJump->mEntryRequiredRegs[CPU_REG_Z]) + { + int i = mIns.Size(); + while (i > 0 && !mIns[i - 1].ReferencesXReg()) + i--; + if (i > 0 && mIns[i - 1].mType == ASMIT_LDX && !(mIns[i - 1].mLive & LIVE_CPU_REG_Z)) + { + if (mIns[i - 1].mMode == ASMIM_IMMEDIATE || (mIns[i - 1].mMode == ASMIM_ZERO_PAGE && !ChangedOnPath(this, i, mIns.Size(), mIns[i - 1].mAddress))) + { + if (mIns[i - 1].mMode == ASMIM_ZERO_PAGE) + mExitRequiredRegs += mIns[i - 1].mAddress; + + if (mTrueJump->mEntryRequiredRegs[CPU_REG_X]) + mTrueJump->PrependInstruction(mIns[i - 1]); + else + mFalseJump->PrependInstruction(mIns[i - 1]); + mIns.Remove(i - 1); + changed = true; + } + } + else if (i > 0 && (mIns[i - 1].mType == ASMIT_INX || mIns[i - 1].mType == ASMIT_DEX) && !(mIns[i - 1].mLive & LIVE_CPU_REG_Z)) + { + for (int j = i + 1; j < mIns.Size(); j++) + mIns[j].mLive |= LIVE_CPU_REG_X; + + if (mTrueJump->mEntryRequiredRegs[CPU_REG_X]) + mTrueJump->PrependInstruction(mIns[i - 1]); + else + mFalseJump->PrependInstruction(mIns[i - 1]); + mIns.Remove(i - 1); + changed = true; + } + } + + if (mTrueJump->mEntryRequiredRegs[CPU_REG_X] && !mFalseJump->mEntryRequiredRegs[CPU_REG_X] && mTrueJump->mNumEntries == 1 || + mFalseJump->mEntryRequiredRegs[CPU_REG_X] && !mTrueJump->mEntryRequiredRegs[CPU_REG_X] && mFalseJump->mNumEntries == 1) + { + int sz = mIns.Size(); + if (sz >= 2 && mIns[sz - 1].mType == ASMIT_TAX && mIns[sz - 2].ChangesFlagToAccu()) + { + mExitRequiredRegs += CPU_REG_A; + if (mTrueJump->mEntryRequiredRegs[CPU_REG_X]) + { + mTrueJump->mEntryRequiredRegs += CPU_REG_A; + mTrueJump->PrependInstruction(mIns[sz - 1]); + } + else + { + mFalseJump->mEntryRequiredRegs += CPU_REG_A; + mFalseJump->PrependInstruction(mIns[sz - 1]); + } + if (mIns[sz - 1].mLive & LIVE_CPU_REG_Z) + mIns[sz - 2].mLive |= LIVE_CPU_REG_Z; + mIns.Remove(sz - 1); + changed = true; + } + } + + if (mTrueJump->mEntryRequiredRegs[CPU_REG_Y] && !mFalseJump->mEntryRequiredRegs[CPU_REG_Y] && mTrueJump->mNumEntries == 1 && !mTrueJump->mEntryRequiredRegs[CPU_REG_Z] || + mFalseJump->mEntryRequiredRegs[CPU_REG_Y] && !mTrueJump->mEntryRequiredRegs[CPU_REG_Y] && mFalseJump->mNumEntries == 1 && !mFalseJump->mEntryRequiredRegs[CPU_REG_Z]) + { + int i = mIns.Size(); + while (i > 0 && !mIns[i - 1].ReferencesYReg()) + i--; + if (i > 0 && mIns[i - 1].mType == ASMIT_LDY && !(mIns[i - 1].mLive & LIVE_CPU_REG_Z)) + { + if (mIns[i - 1].mMode == ASMIM_IMMEDIATE || (mIns[i - 1].mMode == ASMIM_ZERO_PAGE && !ChangedOnPath(this, i, mIns.Size(), mIns[i - 1].mAddress))) + { + if (mIns[i - 1].mMode == ASMIM_ZERO_PAGE) + mExitRequiredRegs += mIns[i - 1].mAddress; + + if (mTrueJump->mEntryRequiredRegs[CPU_REG_Y]) + mTrueJump->PrependInstruction(mIns[i - 1]); + else + mFalseJump->PrependInstruction(mIns[i - 1]); + mIns.Remove(i - 1); + changed = true; + } + } + else if (i > 0 && mIns[i - 1].mType == ASMIT_TAY && !ChangesAccuOnPath(this, i, mIns.Size())) + { + if (!(mIns[i - 1].mLive & LIVE_CPU_REG_Z) || i > 2 && mIns[i - 2].ChangesAccuAndFlag()) + { + if (mIns[i - 1].mLive & LIVE_CPU_REG_Z) + mIns[i - 2].mLive |= LIVE_CPU_REG_Z; + + mExitRequiredRegs += CPU_REG_A; + if (mTrueJump->mEntryRequiredRegs[CPU_REG_Y]) + { + mTrueJump->mEntryRequiredRegs += CPU_REG_A; + mTrueJump->PrependInstruction(mIns[i - 1]); + } + else + { + mFalseJump->mEntryRequiredRegs += CPU_REG_A; + mFalseJump->PrependInstruction(mIns[i - 1]); + } + mIns.Remove(i - 1); + changed = true; + } + } + } + if (mTrueJump->mNumEntries == 1 && mTrueJump->mIns.Size() > 0 && mTrueJump->mIns[0].mType == ASMIT_TAX && !mFalseJump->mEntryRequiredRegs[CPU_REG_A] && !mFalseJump->mEntryRequiredRegs[CPU_REG_X] && !(mTrueJump->mIns[0].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z))) + { + int sz = mIns.Size(); + if (sz >= 2 && mIns[sz - 2].mType == ASMIT_LDA && mIns[sz - 1].mType == ASMIT_CMP && HasAsmInstructionMode(ASMIT_LDX, mIns[sz - 2].mMode) && HasAsmInstructionMode(ASMIT_CPX, mIns[sz - 1].mMode)) + { + mIns[sz - 2].mType = ASMIT_LDX; mIns[sz - 2].mLive |= LIVE_CPU_REG_X; + mIns[sz - 1].mType = ASMIT_CPX; mIns[sz - 1].mLive |= LIVE_CPU_REG_X; + mExitRequiredRegs += CPU_REG_X; + mTrueJump->mEntryRequiredRegs += CPU_REG_X; + mTrueJump->mIns[0].mType = ASMIT_NOP; + changed = true; + } + } + } +#endif + + if (!changed && mTrueJump && mFalseJump && mExitRequiredRegs.Size()) + { + int sz = mIns.Size(); + int i = sz - 1; + while (i >= 0 && !(mIns[i].ReferencesYReg() || mIns[i].ChangesAccu())) + i--; + if (i >= 0 && mIns[i].mType == ASMIT_TAY && !(mIns[i].mLive & LIVE_CPU_REG_Z)) + { + NativeCodeBasicBlock * tblock = mTrueJump, * fblock = mFalseJump; + NativeCodeBasicBlock* ptblock = this, * pfblock = this; + while (tblock && !(tblock->ChangesAccu() || tblock->ReferencesYReg() || tblock->mFalseJump || tblock->mNumEntries > 1)) + { + ptblock = tblock; + tblock = tblock->mTrueJump; + } + while (fblock && !(fblock->ChangesAccu() || fblock->ReferencesYReg() || fblock->mFalseJump || fblock->mNumEntries > 1)) + { + pfblock = fblock; + fblock = fblock->mTrueJump; + } + + if (fblock && fblock != this) + { + int k = 0; + while (k < fblock->mIns.Size() && !(fblock->mIns[k].ReferencesYReg() || fblock->mIns[k].ChangesAccu())) + k++; + if (k < fblock->mIns.Size() && fblock->mIns[k].mType == ASMIT_TYA && !(fblock->mIns[k].mLive & (LIVE_CPU_REG_Y | LIVE_CPU_REG_Z))) + { + if (k > 0) + { + fblock->mIns.Insert(0, NativeCodeInstruction(fblock->mIns[k].mIns, ASMIT_TYA)); + fblock->mIns.Remove(k + 1); + } + + NativeCodeBasicBlock* shortb = fblock->SplitBlock(1); + fblock->mExitProvidedRegs += CPU_REG_A; + + fblock->mEntryBlocks.RemoveAll(pfblock); + shortb->mEntryBlocks.Push(pfblock); + if (pfblock == this) + mFalseJump = shortb; + else + pfblock->mTrueJump = shortb; + mExitRequiredRegs += CPU_REG_A; + NativeCodeBasicBlock* fb = mFalseJump; + while (fb != shortb) + { + fb->mEntryRequiredRegs += CPU_REG_A; + fb->mExitProvidedRegs += CPU_REG_A; + for (char j = 0; j < fb->mIns.Size(); j++) + fb->mIns[j].mLive |= LIVE_CPU_REG_A; + fb = fb->mTrueJump; + } + + NativeCodeBasicBlock* pt = mTrueJump->UniqueEntryBlock(this); + pt->mEntryRequiredRegs += CPU_REG_A; + pt->mIns.Insert(0, mIns[i]); + mIns.Remove(i); + changed = true; + } + } + if (tblock && tblock != this && !changed) + { + int k = 0; + while (k < tblock->mIns.Size() && !(tblock->mIns[k].ReferencesYReg() || tblock->mIns[k].ChangesAccu())) + k++; + if (k < tblock->mIns.Size() && tblock->mIns[k].mType == ASMIT_TYA && !(tblock->mIns[k].mLive & (LIVE_CPU_REG_Y | LIVE_CPU_REG_Z))) + { + if (k > 0) + { + tblock->mIns.Insert(0, NativeCodeInstruction(tblock->mIns[k].mIns, ASMIT_TYA)); + tblock->mIns.Remove(k + 1); + } + + NativeCodeBasicBlock* shortb = tblock->SplitBlock(1); + tblock->mExitProvidedRegs += CPU_REG_A; + + tblock->mEntryBlocks.RemoveAll(ptblock); + shortb->mEntryBlocks.Push(ptblock); + if (ptblock == this) + mTrueJump = shortb; + else + ptblock->mTrueJump = shortb; + mExitRequiredRegs += CPU_REG_A; + NativeCodeBasicBlock* fb = mTrueJump; + while (fb != shortb) + { + fb->mEntryRequiredRegs += CPU_REG_A; + fb->mExitProvidedRegs += CPU_REG_A; + for (char j = 0; j < fb->mIns.Size(); j++) + fb->mIns[j].mLive |= LIVE_CPU_REG_A; + fb = fb->mTrueJump; + } + + NativeCodeBasicBlock* pt = mFalseJump->UniqueEntryBlock(this); + pt->mEntryRequiredRegs += CPU_REG_A; + pt->mIns.Insert(0, mIns[i]); + mIns.Remove(i); + changed = true; + } + } + } + } + + if (!changed && mTrueJump && mFalseJump && mExitRequiredRegs.Size()) + { + int sz = mIns.Size(); + int i = sz - 1; + while (i >= 0 && !(mIns[i].ChangesYReg() || mIns[i].mType == ASMIT_STY && mIns[i].mMode == ASMIM_ZERO_PAGE)) + i--; + if (i >= 0 && mIns[i].mType == ASMIT_STY && mIns[i].mMode == ASMIM_ZERO_PAGE) + { + int addr = mIns[i].mAddress; + + NativeCodeBasicBlock* tblock = mTrueJump, * fblock = mFalseJump; + NativeCodeBasicBlock* ptblock = this, * pfblock = this; + while (tblock && !(tblock->ChangesYReg() || tblock->ReferencesZeroPage(addr) || tblock->mFalseJump || tblock->mNumEntries > 1)) + { + ptblock = tblock; + tblock = tblock->mTrueJump; + } + while (fblock && !(fblock->ChangesYReg() || fblock->ReferencesZeroPage(addr) || fblock->mFalseJump || fblock->mNumEntries > 1)) + { + pfblock = fblock; + fblock = fblock->mTrueJump; + } + + if (fblock && fblock != this) + { + int k = 0; + while (k < fblock->mIns.Size() && !(fblock->mIns[k].ChangesYReg() || fblock->mIns[k].ReferencesZeroPage(addr))) + k++; + if (k < fblock->mIns.Size() && fblock->mIns[k].mType == ASMIT_LDY && fblock->mIns[k].mMode == ASMIM_ZERO_PAGE && fblock->mIns[k].mAddress == addr && !(fblock->mIns[k].mLive & (LIVE_MEM | LIVE_CPU_REG_Z))) + { + NativeCodeBasicBlock* shortb = fblock->SplitBlock(k + 1); + fblock->mExitProvidedRegs += CPU_REG_Y; + + NativeCodeBasicBlock* prefixb = fblock->CloneBlock(); + prefixb->mIns.SetSize(k); + + fblock->mEntryBlocks.RemoveAll(pfblock); + prefixb->mEntryBlocks.Push(pfblock); + if (pfblock == this) + mFalseJump = prefixb; + else + pfblock->mTrueJump = prefixb; + + mExitRequiredRegs += CPU_REG_Y; + NativeCodeBasicBlock* fb = mFalseJump; + while (fb != shortb) + { + if (fb->mEntryRequiredRegs.Size() > 0) + { + fb->mEntryRequiredRegs += CPU_REG_Y; + fb->mExitProvidedRegs += CPU_REG_Y; + } + for (char j = 0; j < fb->mIns.Size(); j++) + fb->mIns[j].mLive |= LIVE_CPU_REG_Y; + fb = fb->mTrueJump; + } + + NativeCodeBasicBlock* pt = mTrueJump->UniqueEntryBlock(this); + pt->mEntryRequiredRegs += CPU_REG_Y; + pt->mIns.Insert(0, mIns[i]); + pt->mIns[0].mLive |= LIVE_CPU_REG_A | LIVE_CPU_REG_X; + mIns.Remove(i); + changed = true; + + while (i < mIns.Size()) + { + mIns[i].mLive |= LIVE_CPU_REG_Y; + i++; + } + } + } + if (tblock && tblock != this && !changed) + { + int k = 0; + while (k < tblock->mIns.Size() && !(tblock->mIns[k].ChangesYReg() || tblock->mIns[k].ReferencesZeroPage(addr))) + k++; + if (k < tblock->mIns.Size() && tblock->mIns[k].mType == ASMIT_LDY && tblock->mIns[k].mMode == ASMIM_ZERO_PAGE && tblock->mIns[k].mAddress == addr && !(tblock->mIns[k].mLive & (LIVE_MEM | LIVE_CPU_REG_Z))) + { + NativeCodeBasicBlock* shortb = tblock->SplitBlock(k + 1); + tblock->mExitProvidedRegs += CPU_REG_Y; + + NativeCodeBasicBlock* prefixb = tblock->CloneBlock(); + prefixb->mIns.SetSize(k); + + tblock->mEntryBlocks.RemoveAll(ptblock); + prefixb->mEntryBlocks.Push(ptblock); + if (ptblock == this) + mTrueJump = prefixb; + else + ptblock->mTrueJump = prefixb; + + mExitRequiredRegs += CPU_REG_Y; + NativeCodeBasicBlock* fb = mTrueJump; + while (fb != shortb) + { + if (fb->mEntryRequiredRegs.Size() > 0) + { + fb->mEntryRequiredRegs += CPU_REG_Y; + fb->mExitProvidedRegs += CPU_REG_Y; + } + for (char j = 0; j < fb->mIns.Size(); j++) + fb->mIns[j].mLive |= LIVE_CPU_REG_Y; + fb = fb->mTrueJump; + } + + NativeCodeBasicBlock* pt = mFalseJump->UniqueEntryBlock(this); + pt->mEntryRequiredRegs += CPU_REG_Y; + pt->mIns.Insert(0, mIns[i]); + pt->mIns[0].mLive |= LIVE_CPU_REG_A | LIVE_CPU_REG_X; + mIns.Remove(i); + changed = true; + + while (i < mIns.Size()) + { + mIns[i].mLive |= LIVE_CPU_REG_Y; + i++; + } + } + } + } + } + + + if (mTrueJump && mFalseJump && mExitRequiredRegs.Size()) + { + uint32 live = 0; + if (mExitRequiredRegs[CPU_REG_X]) + live |= LIVE_CPU_REG_X; + if (mExitRequiredRegs[CPU_REG_Y]) + live |= LIVE_CPU_REG_Y; + if (mExitRequiredRegs[CPU_REG_C]) + live |= LIVE_CPU_REG_C; + + int i = 0; + while (i < mIns.Size()) + { +#if 1 + if (!mExitRequiredRegs[CPU_REG_A] && + i + 1 < mIns.Size() && + mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_STA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE) + { + if (!ReferencedOnPath(this, i + 2, mIns.Size(), mIns[i + 1].mAddress) && + !ChangedOnPath(this, i + 2, mIns.Size(), mIns[i + 0].mAddress)) + { + if (mTrueJump->mEntryRequiredRegs[mIns[i + 1].mAddress] && + !mFalseJump->mEntryRequiredRegs[mIns[i + 1].mAddress] && + mTrueJump->mNumEntries == 1 && !mTrueJump->mEntryRequiredRegs[CPU_REG_Z]) + { + for (int j = 0; j < 2; j++) + { + mIns[i + j].mLive |= live; + mTrueJump->mIns.Insert(j, mIns[i + j]); + } + mIns.Remove(i + 1); + changed = true; + continue; + } + else if (mFalseJump->mEntryRequiredRegs[mIns[i + 1].mAddress] && + !mTrueJump->mEntryRequiredRegs[mIns[i + 1].mAddress] && + mFalseJump->mNumEntries == 1 && !mFalseJump->mEntryRequiredRegs[CPU_REG_Z]) + { + for (int j = 0; j < 2; j++) + { + mIns[i + j].mLive |= live; + mFalseJump->mIns.Insert(j, mIns[i + j]); + } + mIns.Remove(i + 1); + changed = true; + continue; + } + } + } +#endif +#if 1 + if (!mExitRequiredRegs[CPU_REG_A] && + i + 6 < mIns.Size() && + mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && + mIns[i + 4].mType == ASMIT_LDA && mIns[i + 4].mMode == ASMIM_ZERO_PAGE && + mIns[i + 5].mType == ASMIT_ADC && mIns[i + 5].mMode == ASMIM_IMMEDIATE && + mIns[i + 6].mType == ASMIT_STA && mIns[i + 6].mMode == ASMIM_ZERO_PAGE && + !(mIns[i + 6].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C | LIVE_CPU_REG_Z))) + { + if (!ReferencedOnPath(this, i + 7, mIns.Size(), mIns[i + 3].mAddress) && + !ReferencedOnPath(this, i + 7, mIns.Size(), mIns[i + 6].mAddress) && + !ChangedOnPath(this, i + 7, mIns.Size(), mIns[i + 1].mAddress) && + !ChangedOnPath(this, i + 7, mIns.Size(), mIns[i + 2].mAddress) && + !ChangedOnPath(this, i + 7, mIns.Size(), mIns[i + 4].mAddress)) + { + if (mTrueJump->mEntryRequiredRegs[mIns[i + 3].mAddress] && mTrueJump->mEntryRequiredRegs[mIns[i + 6].mAddress] && + !mFalseJump->mEntryRequiredRegs[mIns[i + 3].mAddress] && !mFalseJump->mEntryRequiredRegs[mIns[i + 6].mAddress] && + !mTrueJump->mEntryRequiredRegs[CPU_REG_C] && + !mTrueJump->mEntryRequiredRegs[CPU_REG_Z] && + mTrueJump->mNumEntries == 1) + { + mTrueJump->mEntryRequiredRegs += mIns[i + 1].mAddress; + mTrueJump->mEntryRequiredRegs += mIns[i + 2].mAddress; + mTrueJump->mEntryRequiredRegs += mIns[i + 4].mAddress; + mTrueJump->mEntryRequiredRegs += mIns[i + 5].mAddress; + + for (int j = 0; j < 7; j++) + { + mIns[i].mLive |= live; + mTrueJump->mIns.Insert(j, mIns[i]); + mIns.Remove(i); + } + changed = true; + continue; + } + else if (mFalseJump->mEntryRequiredRegs[mIns[i + 3].mAddress] && mFalseJump->mEntryRequiredRegs[mIns[i + 6].mAddress] && + !mTrueJump->mEntryRequiredRegs[mIns[i + 3].mAddress] && !mTrueJump->mEntryRequiredRegs[mIns[i + 6].mAddress] && + !mFalseJump->mEntryRequiredRegs[CPU_REG_C] && + !mFalseJump->mEntryRequiredRegs[CPU_REG_Z] && + mFalseJump->mNumEntries == 1) + { + mFalseJump->mEntryRequiredRegs += mIns[i + 1].mAddress; + mFalseJump->mEntryRequiredRegs += mIns[i + 2].mAddress; + mFalseJump->mEntryRequiredRegs += mIns[i + 4].mAddress; + mFalseJump->mEntryRequiredRegs += mIns[i + 5].mAddress; + + for (int j = 0; j < 7; j++) + { + mIns[i].mLive |= live; + mFalseJump->mIns.Insert(j, mIns[i]); + mIns.Remove(i); + } + changed = true; + continue; + } + } + } +#endif +#if 1 + if (!mExitRequiredRegs[CPU_REG_A] && + i + 6 < mIns.Size() && + mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && + mIns[i + 4].mType == ASMIT_LDA && mIns[i + 4].mMode == ASMIM_ZERO_PAGE && + mIns[i + 5].mType == ASMIT_ADC && mIns[i + 5].mMode == ASMIM_IMMEDIATE && + mIns[i + 6].mType == ASMIT_STA && mIns[i + 6].mMode == ASMIM_ZERO_PAGE && + !(mIns[i + 6].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C | LIVE_CPU_REG_Z))) + { + if (!ReferencedOnPath(this, i + 7, mIns.Size(), mIns[i + 3].mAddress) && + !ReferencedOnPath(this, i + 7, mIns.Size(), mIns[i + 6].mAddress) && + !ChangedOnPath(this, i + 7, mIns.Size(), mIns[i + 1].mAddress) && + !ChangedOnPath(this, i + 7, mIns.Size(), mIns[i + 4].mAddress)) + { + if (mTrueJump->mEntryRequiredRegs[mIns[i + 3].mAddress] && mTrueJump->mEntryRequiredRegs[mIns[i + 6].mAddress] && + !mFalseJump->mEntryRequiredRegs[mIns[i + 3].mAddress] && !mFalseJump->mEntryRequiredRegs[mIns[i + 6].mAddress] && + !mTrueJump->mEntryRequiredRegs[CPU_REG_C] && + !mTrueJump->mEntryRequiredRegs[CPU_REG_Z] && + mTrueJump->mNumEntries == 1) + { + mTrueJump->mEntryRequiredRegs += mIns[i + 1].mAddress; + mTrueJump->mEntryRequiredRegs += mIns[i + 4].mAddress; + + for (int j = 0; j < 7; j++) + { + mIns[i].mLive |= live; + mTrueJump->mIns.Insert(j, mIns[i]); + mIns.Remove(i); + } + changed = true; + continue; + } + else if (mFalseJump->mEntryRequiredRegs[mIns[i + 3].mAddress] && mFalseJump->mEntryRequiredRegs[mIns[i + 6].mAddress] && + !mTrueJump->mEntryRequiredRegs[mIns[i + 3].mAddress] && !mTrueJump->mEntryRequiredRegs[mIns[i + 6].mAddress] && + !mFalseJump->mEntryRequiredRegs[CPU_REG_C] && + !mFalseJump->mEntryRequiredRegs[CPU_REG_Z] && + mFalseJump->mNumEntries == 1) + { + mFalseJump->mEntryRequiredRegs += mIns[i + 1].mAddress; + mFalseJump->mEntryRequiredRegs += mIns[i + 4].mAddress; + + for (int j = 0; j < 7; j++) + { + mIns[i].mLive |= live; + mFalseJump->mIns.Insert(j, mIns[i]); + mIns.Remove(i); + } + changed = true; + continue; + } + } + } +#endif + + i++; + } + } + + if (mTrueJump && mTrueJump->PropagateSinglePath()) + changed = true; + if (mFalseJump && mFalseJump->PropagateSinglePath()) + changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::Is16BitAddSubImmediate(int at, int& sreg, int& dreg, int& offset) const +{ + if (at + 6 < mIns.Size() && + mIns[at + 0].mType == ASMIT_CLC && + mIns[at + 1].mType == ASMIT_LDA && mIns[at + 1].mMode == ASMIM_ZERO_PAGE && + mIns[at + 2].mType == ASMIT_ADC && mIns[at + 2].mMode == ASMIM_IMMEDIATE && + mIns[at + 3].mType == ASMIT_STA && mIns[at + 3].mMode == ASMIM_ZERO_PAGE && + mIns[at + 4].mType == ASMIT_LDA && mIns[at + 4].mMode == ASMIM_ZERO_PAGE && mIns[at + 4].mAddress == mIns[at + 1].mAddress + 1 && + mIns[at + 5].mType == ASMIT_ADC && mIns[at + 5].mMode == ASMIM_IMMEDIATE && + mIns[at + 6].mType == ASMIT_STA && mIns[at + 6].mMode == ASMIM_ZERO_PAGE && mIns[at + 6].mAddress == mIns[at + 3].mAddress + 1 && + !(mIns[at + 6].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C | LIVE_CPU_REG_Z))) + { + sreg = mIns[at + 1].mAddress; + dreg = mIns[at + 3].mAddress; + offset = mIns[at + 2].mAddress + 256 * mIns[at + 5].mAddress; + return true; + } + + return false; +} + +bool NativeCodeBasicBlock::CanForward16BitAddSubImmediate(int sreg, int dreg, int offset, int& index) const +{ + int i = mIns.Size() - 1; + while (i >= 6) + { + int asreg, adreg, aoffset; + if (Is16BitAddSubImmediate(i - 6, asreg, adreg, aoffset) && asreg == sreg && adreg == dreg && aoffset == offset) + { + index = i - 6; + return true; + } + + if (mIns[i].ReferencesZeroPage(dreg) || mIns[i].ReferencesZeroPage(dreg + 1) || mIns[i].ChangesZeroPage(sreg) || mIns[i].ChangesZeroPage(sreg + 1)) + return false; + + i--; + } + + return false; +} + +bool NativeCodeBasicBlock::CanForwardLoadStore(const NativeCodeInstruction& lins, const NativeCodeInstruction& sins, int& index) const +{ + int i = mIns.Size() - 1; + while (i > 0) + { + if (lins.MayBeChangedOnAddress(mIns[i])) + return false; + if (mIns[i].MayBeSameAddress(sins)) + { + index = i - 1; + if (mIns[i].mType == ASMIT_STA && mIns[i - 1].mType == ASMIT_LDA && !(mIns[i].mLive & LIVE_CPU_REG_A) || + mIns[i].mType == ASMIT_STX && mIns[i - 1].mType == ASMIT_LDX && !(mIns[i].mLive & LIVE_CPU_REG_X) || + mIns[i].mType == ASMIT_STY && mIns[i - 1].mType == ASMIT_LDY && !(mIns[i].mLive & LIVE_CPU_REG_Y)) + { + if (!(mIns[i].mLive & LIVE_CPU_REG_Z) && mIns[i].SameEffectiveAddress(sins) && mIns[i - 1].SameEffectiveAddress(lins)) + return true; + } + return false; + } + if (mIns[i].mType == ASMIT_JSR) + return false; + if (sins.MayBeChangedOnAddress(mIns[i]) || mIns[i].MayBeChangedOnAddress(sins)) + return false; + i--; + } + + return false; +} + +bool NativeCodeBasicBlock::CanForwardZPMove(int saddr, int daddr, int & index) const +{ + int i = mIns.Size() - 1; + while (i > 0) + { + if (mIns[i].ChangesZeroPage(saddr)) + return false; + if (mIns[i].ChangesZeroPage(daddr)) + { + index = i - 1; + return + (mIns[i].mType == ASMIT_STA && mIns[i - 1].mType == ASMIT_LDA && mIns[i - 1].mMode == ASMIM_ZERO_PAGE && mIns[i - 1].mAddress == saddr && !(mIns[i].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z))) || + (mIns[i].mType == ASMIT_STX && mIns[i - 1].mType == ASMIT_LDX && mIns[i - 1].mMode == ASMIM_ZERO_PAGE && mIns[i - 1].mAddress == saddr && !(mIns[i].mLive & (LIVE_CPU_REG_X | LIVE_CPU_REG_Z))); + } + if (mIns[i].ReferencesZeroPage(daddr)) + return false; + i--; + } + + return false; +} + +bool NativeCodeBasicBlock::CheckShortcutPointerAddForward(int at) +{ + int i = at + 6; + + while (i < mIns.Size()) + { + if (mIns[i].mMode == ASMIM_INDIRECT_Y && mIns[i].mAddress == mIns[at + 1].mAddress) + { + if (!(mIns[i].mLive & LIVE_MEM)) + { + mIns[at + 0].mType = ASMIT_NOP; mIns[at + 0].mMode = ASMIM_IMPLIED; + mIns[at + 1].mType = ASMIT_NOP; mIns[at + 1].mMode = ASMIM_IMPLIED; + mIns[at + 5].mAddress = mIns[at + 4].mAddress; mIns[at + 4].mLive |= LIVE_MEM; + + for (int j = at + 6; j <= i; j++) + { + if (mIns[j].mMode == ASMIM_INDIRECT_Y && mIns[j].mAddress == mIns[at + 1].mAddress) + mIns[j].mAddress = mIns[at + 0].mAddress; + } + + return true; + } + } + else if (mIns[i].ChangesZeroPage(mIns[at + 0].mAddress) || + mIns[i].ReferencesZeroPage(mIns[at + 1].mAddress) || + mIns[i].ReferencesZeroPage(mIns[at + 4].mAddress) || + mIns[i].ReferencesZeroPage(mIns[at + 5].mAddress)) + return false; + + i++; + } + + return false; +} + +bool NativeCodeBasicBlock::ShortcutPointerAddForward(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + for (int i = 0; i + 6 < mIns.Size(); i++) + { + if (mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_STA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + mIns[i + 2].mType == ASMIT_CLC && + mIns[i + 3].mType == ASMIT_LDA && mIns[i + 3].mMode == ASMIM_IMMEDIATE_ADDRESS && + mIns[i + 4].mType == ASMIT_ADC && mIns[i + 4].mMode == ASMIM_ZERO_PAGE && mIns[i + 4].mAddress == mIns[i + 0].mAddress + 1 && !(mIns[i + 4].mLive & LIVE_MEM) && + mIns[i + 5].mType == ASMIT_STA && mIns[i + 5].mMode == ASMIM_ZERO_PAGE && mIns[i + 5].mAddress == mIns[i + 1].mAddress + 1) + { + if (CheckShortcutPointerAddForward(i)) + changed = true; + } + } + + if (mTrueJump && mTrueJump->ShortcutPointerAddForward()) + changed = true; + if (mFalseJump && mFalseJump->ShortcutPointerAddForward()) + changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::MoveIndirectLoadZeroStoreDown(int at) +{ + int yval = mIns[at].mAddress; + + for (int i = at + 3; i < mIns.Size(); i++) + { + if (mIns[i].mType == ASMIT_LDA && mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i].mAddress == mIns[at + 2].mAddress) + { + if (mIns[i].mLive & LIVE_CPU_REG_Y) + return false; + mIns.Insert(i, mIns[at]); + mIns.Insert(i + 1, mIns[at + 1]); + mIns[i + 2].mType = ASMIT_STA; + mIns[i].mLive |= mIns[i + 2].mLive; + mIns[i + 1].mLive |= mIns[i + 2].mLive; + mIns.Remove(at + 1, 2); + return true; + } + + if (mIns[i].ChangesZeroPage(mIns[at + 1].mAddress) || mIns[i].ChangesZeroPage(mIns[at + 1].mAddress + 1) || mIns[i].ReferencesZeroPage(mIns[at + 2].mAddress)) + return false; + + if (mIns[i].mType == ASMIT_JSR) + return false; + + if (mIns[i].ChangesAddress()) + { + if (mIns[i].mMode == ASMIM_INDIRECT_Y && mIns[i].mAddress == mIns[at + 1].mAddress) + { + if (yval == -1 || yval == mIns[at].mAddress) + return false; + } + else if (mIns[at + 1].MayBeChangedOnAddress(mIns[i])) + return false; + } + + if (mIns[i].ChangesYReg()) + { + if (mIns[i].mType == ASMIT_LDY && mIns[i].mMode == ASMIM_IMMEDIATE) + yval = mIns[i].mAddress; + else if (mIns[i].mType == ASMIT_INY && yval >= 0) + yval = (yval + 1) & 255; + else if (mIns[i].mType == ASMIT_DEY && yval >= 0) + yval = (yval - 1) & 255; + else + yval = -1; + } + + } + + return false; +} + +bool NativeCodeBasicBlock::SortIndirectStoreDown(int at) +{ + int i = at + 3; + while (i + 1 < mIns.Size()) + { + if (mIns[i + 0].mType == ASMIT_LDY && mIns[i + 0].mMode == ASMIM_IMMEDIATE && + mIns[i + 1].mMode == ASMIM_INDIRECT_Y && + mIns[i + 1].mAddress == mIns[at + 2].mAddress && + !(mIns[i + 1].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Y | LIVE_CPU_REG_Z))) + { + if (mIns[i + 0].mAddress < mIns[at + 1].mAddress) + { + mIns.Insert(i + 2, mIns[at + 0]); + mIns.Insert(i + 3, mIns[at + 1]); + mIns.Insert(i + 4, mIns[at + 2]); + mIns[i + 2].mLive |= mIns[i + 1].mLive; + mIns[i + 3].mLive |= mIns[i + 1].mLive; + mIns[i + 4].mLive |= mIns[i + 1].mLive; + if (mIns[at + 2].mLive & LIVE_CPU_REG_A) + mIns.Remove(at + 1, 2); + else + mIns.Remove(at, 3); + return true; + } + else if (mIns[i].mAddress == mIns[at + 1].mAddress) + return false; + else + i += 2; + } + else + { + if (mIns[i].MayBeSameAddress(mIns[at + 2])) + return false; + else if (mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i].ChangesAddress() && (mIns[i].mAddress == mIns[at + 2].mAddress || mIns[i].mAddress == mIns[at + 2].mAddress + 1)) + return false; + + i++; + } + } + + return false; +} + +bool NativeCodeBasicBlock::SortIndirectStores(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + int i = 0; + while (i + 2 < mIns.Size()) + { + if (mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_IMMEDIATE && + mIns[i + 1].mType == ASMIT_LDY && mIns[i + 1].mMode == ASMIM_IMMEDIATE && + mIns[i + 2].mType == ASMIT_STA && mIns[i + 2].mMode == ASMIM_INDIRECT_Y) + { + if (SortIndirectStoreDown(i)) + changed = true; + else + i++; + } + else + i++; + } + + if (mTrueJump && mTrueJump->SortIndirectStores()) changed = true; + if (mFalseJump && mFalseJump->SortIndirectStores()) changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::MoveLoadZeroStoreIndirectUp(int at) +{ + int i = at - 1; + while (i >= 0 && !(mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i].mAddress == mIns[at].mAddress)) + i--; + + if (i >= 0 && mIns[i].mType == ASMIT_STA && !(mIns[i].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z | LIVE_CPU_REG_Y))) + { + int yval = -1; + for (int j = i + 1; j < at; j++) + { + if (mIns[j].mType == ASMIT_JSR) + return false; + if (mIns[j].ChangesZeroPage(mIns[at + 2].mAddress) || mIns[j].ChangesZeroPage(mIns[at + 2].mAddress + 1)) + return false; + + if (mIns[j].mMode == ASMIM_INDIRECT_Y && mIns[j].mAddress == mIns[at + 2].mAddress) + { + if (yval == -1 || yval == mIns[at + 1].mAddress) + return false; + } + else if (mIns[j].MayBeSameAddress(mIns[at + 2])) + return false; + + if (mIns[j].ChangesYReg()) + { + if (mIns[j].mType == ASMIT_LDY && mIns[j].mMode == ASMIM_IMMEDIATE) + yval = mIns[j].mAddress; + else if (mIns[j].mType == ASMIT_INY && yval >= 0) + yval = (yval + 1) & 255; + else if (mIns[j].mType == ASMIT_DEY && yval >= 0) + yval = (yval - 1) & 255; + else + yval = -1; + } + } + + NativeCodeInstruction i0 = mIns[at + 1], i1 = mIns[at + 2]; + mIns.Remove(at + 1, 2); + + mIns.Insert(i + 1, i0); + mIns.Insert(i + 2, i1); + mIns[i].mLive |= LIVE_CPU_REG_A; + mIns[i + 1].mLive |= mIns[i].mLive; + mIns[i + 2].mLive |= mIns[i].mLive; + + return true; + } + + return false; +} + + +bool NativeCodeBasicBlock::ShortcutIndirectLoadStore(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + for (int i = 0; i + 2 < mIns.Size(); i++) + { + if (mIns[i + 0].mType == ASMIT_LDY && mIns[i + 0].mMode == ASMIM_IMMEDIATE && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_INDIRECT_Y && + mIns[i + 2].mType == ASMIT_STA && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && !(mIns[i + 2].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z)) && + mIns[i + 2].mAddress != mIns[i + 1].mAddress && mIns[i + 2].mAddress != mIns[i + 1].mAddress + 1) + { + if (MoveIndirectLoadZeroStoreDown(i)) + changed = true; + } + else if (mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_LDY && mIns[i + 1].mMode == ASMIM_IMMEDIATE && + mIns[i + 2].mType == ASMIT_STA && mIns[i + 2].mMode == ASMIM_INDIRECT_Y && !(mIns[i + 2].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z))) + { + if (MoveLoadZeroStoreIndirectUp(i)) + changed = true; + } + } + + if (mTrueJump && mTrueJump->ShortcutIndirectLoadStore()) + changed = true; + if (mFalseJump && mFalseJump->ShortcutIndirectLoadStore()) + changed = true; + } + + return changed; +} + +#if 0 +static int NativeCodeExpressionID; + +struct NativeCodeExpression +{ + int mIndex; + AsmInsType mType; + NativeCodeInstruction * mIns; + int mLeft, mRight; + int mReg; + + NativeCodeExpression(AsmInsType type, NativeCodeInstruction* ins) + : mIndex(NativeCodeExpressionID++), mType(type), mIns(ins), mLeft(-1), mRight(-1), mReg(-1) + { + } + + NativeCodeExpression(AsmInsType type, int left, int right) + : mIndex(NativeCodeExpressionID++), mType(type), mIns(nullptr), mLeft(left), mRight(right), mReg(reg) + { + } +}; + +struct NativeCodeExpressions +{ + ExpandingArray mExps; + + void KillReg(int reg); + void KillYReg(void); + void KillXReg(void); + void KillAddr(const NativeCodeInstruction& ins); + int AddLoad(const NativeCodeInstruction& ins); +}; + + +void NativeCodeExpressions::KillReg(int reg) +{ + int i = 0; + while (i < mExps.Size()) + { + if (mExps[i].mReg == reg) + mExps.Remove(i); + else if (mExps[i].mIns) + { + if (reg == CPU_REG_Y && mExps[i].mIns->RequiresYReg()) + mExps.Remove(i); + else if (reg == CPU_REG_X && mExps[i].mIns->RequiresXReg()) + mExps.Remove(i); + else if (mExps[i].mIns->ReferencesZeroPage(reg)) + mExps.Remove(i); + else + i++; + } + else + i++; + } +} + +void NativeCodeExpressions::KillYReg(void) +{ + int i = 0; + while (i < mExps.Size()) + { + if (mExps[i].mIns && mExps[i].mIns->RequiresYReg()) + mExps.Remove(i); + else + i++; + } +} + +void NativeCodeExpressions::KillAddr(const NativeCodeInstruction& ins) +{ + int i = 0; + while (i < mExps.Size()) + { + if (mExps[i].mIns && mExps[i].mIns->MayBeChangedOnAddress(ins)) + mExps.Remove(i); + else + i++; + } +} + +int NativeCodeExpressions::AddLoad(const NativeCodeInstruction& ins) +{ + int i = 0; + while (i < mExps.Size() && !(mExps[i].mType == ASMIT_LDA && mExps[i].mIns->SameAddress(ins)) + i++; + if (i < mExps.Size()) + return mExps[i].mIndex; + else + { + NativeCodeExpressions e(ASMIT_LDA, &ins); + mExps.Push(e); + return e.mIndex; + } +} + +bool NativeCodeBasicBlock::CommonSubExpressionElimination(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + NativeCodeExpressions exps; + + int aexp = -1, xexp = -1, yexp = -1; + + for (int i = 0; i < mIns.Size(); i++) + { + NativeCodeInstruction& ins(mIns[i]); + + + + switch (ins.mType) + { + case ASMIT_LDA: + aexp = exps.AddLoad(ins); + break; + case ASMIT_LDX: + aexp = exps.AddLoad(ins); + break; + case ASMIT_LDY: + aexp = exps.AddLoad(ins); + break; + } + } + + if (mTrueJump && mTrueJump->CommonSubExpressionElimination()) + changed = true; + if (mFalseJump && mFalseJump->CommonSubExpressionElimination()) + changed = true; + } + + return changed; +} + +#endif + +bool NativeCodeBasicBlock::CanChangeTailZPStoreToX(int addr, const NativeCodeBasicBlock* nblock, const NativeCodeBasicBlock* fblock) const +{ + if (mExitRequiredRegs[CPU_REG_X]) + return false; + + if (mTrueJump && mTrueJump != nblock && mTrueJump != fblock && mTrueJump->mEntryRequiredRegs[addr]) + return false; + if (mFalseJump && mFalseJump != nblock && mFalseJump != fblock && mFalseJump->mEntryRequiredRegs[addr]) + return false; + + int i = mIns.Size(); + while (i > 0) + { + i--; + + if (mIns[i].ChangesXReg()) + return false; + + if (mIns[i].mType == ASMIT_STA && mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i].mAddress == addr) + return true; + + if (mIns[i].ReferencesZeroPage(addr) || mIns[i].RequiresXReg()) + return false; + } + + if (mEntryBlocks.Size() == 1) + return mEntryBlocks[0]->CanChangeTailZPStoreToX(addr, this); + else + return false; +} + +void NativeCodeBasicBlock::ChangeTailZPStoreToX(int addr) +{ + mExitRequiredRegs += CPU_REG_X; + + int i = mIns.Size(); + while (i > 0) + { + i--; + + mIns[i].mLive |= LIVE_CPU_REG_X; + if (mIns[i].mType == ASMIT_STA && mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i].mAddress == addr) + { + mIns[i].mType = ASMIT_TAX; + mIns[i].mMode = ASMIM_IMPLIED; + return; + } + } + + if (mEntryBlocks.Size() == 1) + { + mEntryRequiredRegs += CPU_REG_X; + mEntryBlocks[0]->ChangeTailZPStoreToX(addr); + return; + } + + assert(false); +} + +bool NativeCodeBasicBlock::CanChangeTailZPStoreToY(int addr, const NativeCodeBasicBlock* nblock, const NativeCodeBasicBlock* fblock) const +{ + if (mExitRequiredRegs[CPU_REG_Y]) + return false; + + if (mTrueJump && mTrueJump != nblock && mTrueJump != fblock && mTrueJump->mEntryRequiredRegs[addr]) + return false; + if (mFalseJump && mFalseJump != nblock && mFalseJump != fblock && mFalseJump->mEntryRequiredRegs[addr]) + return false; + + int i = mIns.Size(); + while (i > 0) + { + i--; + + if (mIns[i].ChangesYReg()) + return false; + + if (mIns[i].mType == ASMIT_STA && mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i].mAddress == addr) + return true; + + if (mIns[i].ReferencesZeroPage(addr) || mIns[i].RequiresYReg()) + return false; + } + + if (mEntryBlocks.Size() == 1) + return mEntryBlocks[0]->CanChangeTailZPStoreToY(addr, this); + else + return false; +} + +void NativeCodeBasicBlock::ChangeTailZPStoreToY(int addr) +{ + mExitRequiredRegs += CPU_REG_Y; + + int i = mIns.Size(); + while (i > 0) + { + i--; + + mIns[i].mLive |= LIVE_CPU_REG_Y; + if (mIns[i].mType == ASMIT_STA && mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i].mAddress == addr) + { + mIns[i].mType = ASMIT_TAY; + mIns[i].mMode = ASMIM_IMPLIED; + return; + } + } + + if (mEntryBlocks.Size() == 1) + { + mEntryBlocks[0]->ChangeTailZPStoreToY(addr); + return; + } + + assert(false); +} + +bool NativeCodeBasicBlock::IsExitYRegZP(int addr, int& index, NativeCodeBasicBlock*& block) +{ + int i = mIns.Size() - 1; + while (i >= 0) + { + if (mIns[i].mMode == ASMIM_ZERO_PAGE && (mIns[i].mType == ASMIT_STY || mIns[i].mType == ASMIT_LDY)) + { + if (addr == mIns[i].mAddress) + { + block = this; + index = i; + return true; + } + return false; + } + else if (mIns[i].ChangesYReg()) + return false; + else if (mIns[i].ChangesZeroPage(addr)) + return false; + + i--; + } + + if (mEntryBlocks.Size() == 1) + return mEntryBlocks[0]->IsExitYRegZP(addr, index, block); + + return false; +} + +bool NativeCodeBasicBlock::IsExitXRegZP(int addr, int& index, NativeCodeBasicBlock*& block) +{ + int i = mIns.Size() - 1; + while (i >= 0) + { + if (mIns[i].mMode == ASMIM_ZERO_PAGE && (mIns[i].mType == ASMIT_STX || mIns[i].mType == ASMIT_LDX)) + { + if (addr == mIns[i].mAddress) + { + block = this; + index = i; + return true; + } + return false; + } + else if (mIns[i].ChangesXReg()) + return false; + else if (mIns[i].ChangesZeroPage(addr)) + return false; + + i--; + } + + if (mEntryBlocks.Size() == 1) + return mEntryBlocks[0]->IsExitXRegZP(addr, index, block); + + return false; +} + +bool NativeCodeBasicBlock::IsARegZP(int at, int addr, int& index, NativeCodeBasicBlock*& block) +{ + int i = at - 1; + while (i >= 0) + { + if (mIns[i].mMode == ASMIM_ZERO_PAGE && (mIns[i].mType == ASMIT_STA || mIns[i].mType == ASMIT_LDA)) + { + if (addr == mIns[i].mAddress) + { + block = this; + index = i; + return true; + } + return false; + } + else if (mIns[i].ChangesAccu()) + return false; + else if (mIns[i].ChangesZeroPage(addr)) + return false; + + i--; + } + + if (mEntryBlocks.Size() == 1) + return mEntryBlocks[0]->IsExitARegZP(addr, index, block); + + return false; +} + +bool NativeCodeBasicBlock::IsExitARegZP(int addr, int& index, NativeCodeBasicBlock*& block) +{ + int i = mIns.Size() - 1; + while (i >= 0) + { + if (mIns[i].mMode == ASMIM_ZERO_PAGE && (mIns[i].mType == ASMIT_STA || mIns[i].mType == ASMIT_LDA)) + { + if (addr == mIns[i].mAddress) + { + block = this; + index = i; + return true; + } + return false; + } + else if (mIns[i].ChangesAccu()) + return false; + else if (mIns[i].ChangesZeroPage(addr)) + return false; + + i--; + } + + if (mEntryBlocks.Size() == 1) + return mEntryBlocks[0]->IsExitARegZP(addr, index, block); + + return false; +} + +void NativeCodeBasicBlock::MarkLiveBlockChain(int index, NativeCodeBasicBlock* block, uint32 live, uint32 reg) +{ + mExitRequiredRegs += reg; + if (this == block) + { + for (int i = index; i < mIns.Size(); i++) + mIns[i].mLive |= live; + } + else + { + for (int i = 0; i < mIns.Size(); i++) + mIns[i].mLive |= live; + mEntryRequiredRegs += reg; + mEntryBlocks[0]->MarkLiveBlockChain(index, block, live, reg); + } +} + +bool NativeCodeBasicBlock::CanJoinEntryLoadStoreZP(int saddr, int daddr) +{ + mChecked = true; + + // Avoid moving code into loops + if (mLoopHead && (mTrueJump == this || mFalseJump == this)) + return false; + + if (mFalseJump && mExitRequiredRegs[daddr]) + return false; + + int at = mIns.Size() - 1; + while (at >= 0) + { + NativeCodeInstruction & ins = mIns[at]; + if (ins.ReferencesZeroPage(daddr)) + return false; + if (ins.ChangesZeroPage(saddr)) + { + if (ins.mType == ASMIT_STA || ins.mType == ASMIT_STX || ins.mType == ASMIT_STY) + return true; + else + return false; + } + at--; + } + + if (!mLoopHead && mEntryBlocks.Size() > 0 && daddr >= BC_REG_FPARAMS && daddr < BC_REG_FPARAMS_END) + { + for (int i = 0; i < mEntryBlocks.Size(); i++) + { + if (!mEntryBlocks[i]->CanJoinEntryLoadStoreZP(saddr, daddr)) + return false; + } + + return true; + } + + return false; +} + +bool NativeCodeBasicBlock::DoJoinEntryLoadStoreZP(int saddr, int daddr) +{ + if (!mChecked) + return true; + mChecked = false; + + int at = mIns.Size() - 1; + while (at >= 0) + { + NativeCodeInstruction& ins = mIns[at]; + if (ins.ChangesZeroPage(saddr)) + { + if (ins.mType == ASMIT_STA) + { + ins.mLive |= LIVE_CPU_REG_A; + mIns.Insert(at + 1, NativeCodeInstruction(ins.mIns, ASMIT_STA, ASMIM_ZERO_PAGE, daddr)); + mExitRequiredRegs += daddr; + return true; + } + else if (ins.mType == ASMIT_STX) + { + ins.mLive |= LIVE_CPU_REG_X; + mIns.Insert(at + 1, NativeCodeInstruction(ins.mIns, ASMIT_STX, ASMIM_ZERO_PAGE, daddr)); + mExitRequiredRegs += daddr; + return true; + } + else if (ins.mType == ASMIT_STY) + { + ins.mLive |= LIVE_CPU_REG_Y; + mIns.Insert(at + 1, NativeCodeInstruction(ins.mIns, ASMIT_STY, ASMIM_ZERO_PAGE, daddr)); + mExitRequiredRegs += daddr; + return true; + } + } + at--; + } + + if (!mLoopHead) + { + for (int i = 0; i < mEntryBlocks.Size(); i++) + { + if (!mEntryBlocks[i]->DoJoinEntryLoadStoreZP(saddr, daddr)) + return false; + } + + mEntryRequiredRegs += daddr; + mExitRequiredRegs += daddr; + return true; + } + + return false; +} + +bool NativeCodeBasicBlock::JoinEntryLoadStoreZP(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + CheckLive(); + + if (!mLoopHead && mEntryBlocks.Size() > 0 && mEntryBlocks.Size() < 64 && (!mEntryRequiredRegs[CPU_REG_A] || !mEntryRequiredRegs[CPU_REG_X])) + { + for (int i = 0; i + 1 < mIns.Size(); i++) + { + if (mIns[i].mType == ASMIT_LDA && mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i + 1].mType == ASMIT_STA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && !(mIns[i].mLive & LIVE_MEM) && !(mIns[i + 1].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z)) || + mIns[i].mType == ASMIT_LDX && mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i + 1].mType == ASMIT_STX && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && !(mIns[i].mLive & LIVE_MEM) && !(mIns[i + 1].mLive & (LIVE_CPU_REG_X | LIVE_CPU_REG_Z))) + { + int saddr = mIns[i].mAddress, daddr = mIns[i + 1].mAddress; + if (!ReferencesZeroPage(saddr, 0, i) && !ReferencesZeroPage(daddr, 0, i)) + { + int n = 0; + uint64 mask = 0; + for (int j = 0; j < mEntryBlocks.Size(); j++) + { + if (mEntryBlocks[j]->CanJoinEntryLoadStoreZP(saddr, daddr)) + { + mask |= 1ull << j; + n++; + } + } + + if (n == mEntryBlocks.Size()) + { + for (int j = 0; j < mEntryBlocks.Size(); j++) + { + mEntryBlocks[j]->DoJoinEntryLoadStoreZP(saddr, daddr); + mEntryBlocks[j]->mExitRequiredRegs += daddr; + } + mEntryRequiredRegs += daddr; + changed = true; + mIns.Remove(i, 2); + i--; + } + else if (n >= 2) + { + NativeCodeBasicBlock* xblock = mProc->AllocateBlock(); + + int j = mEntryBlocks.Size(); + while (j > 0) + { + j--; + NativeCodeBasicBlock* eb = mEntryBlocks[j]; + + if (mask & (1ull << j)) + { + eb->DoJoinEntryLoadStoreZP(saddr, daddr); + eb->mExitRequiredRegs += daddr; + } + else + { + if (eb->mTrueJump == this) + eb->mTrueJump = xblock; + if (eb->mFalseJump == this) + eb->mFalseJump = xblock; + mEntryBlocks.Remove(j); + xblock->mEntryBlocks.Push(eb); + xblock->mNumEntries++; + } + } + + xblock->mEntryRequiredRegs = mEntryRequiredRegs; + xblock->mExitRequiredRegs = mEntryRequiredRegs; + xblock->mExitRequiredRegs += daddr; + + if (!mEntryRequiredRegs[CPU_REG_A]) + { + xblock->mIns.Push(NativeCodeInstruction(mIns[i + 0].mIns, ASMIT_LDA, ASMIM_ZERO_PAGE, mIns[i + 0].mAddress)); + xblock->mIns.Push(NativeCodeInstruction(mIns[i + 1].mIns, ASMIT_STA, ASMIM_ZERO_PAGE, mIns[i + 1].mAddress)); + } + else + { + xblock->mIns.Push(NativeCodeInstruction(mIns[i + 0].mIns, ASMIT_LDX, ASMIM_ZERO_PAGE, mIns[i + 0].mAddress)); + xblock->mIns.Push(NativeCodeInstruction(mIns[i + 1].mIns, ASMIT_STX, ASMIM_ZERO_PAGE, mIns[i + 1].mAddress)); + } + + xblock->Close(mIns[i].mIns, this, nullptr, ASMIT_JMP); + mEntryBlocks.Push(xblock); + mNumEntries++; + + mEntryRequiredRegs += daddr; + changed = true; + mIns.Remove(i, 2); + i--; + } + } + } + } + } + + if (mTrueJump && mTrueJump->JoinEntryLoadStoreZP()) + changed = true; + if (mFalseJump && mFalseJump->JoinEntryLoadStoreZP()) + changed = true; + } + + return changed; +} + + +bool NativeCodeBasicBlock::JoinTailCodeSequences(bool loops) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + CheckLive(); + if (mTrueJump) mTrueJump->CheckLive(); + if (mFalseJump) mFalseJump->CheckLive(); + +#if 1 + if (mEntryBlocks.Size() > 1) + { + int i = 0; + while (i < mEntryBlocks.Size() && mEntryBlocks[i]->mBranch == ASMIT_JMP) + i++; + if (i == mEntryBlocks.Size()) + { + NativeCodeBasicBlock* eb = mEntryBlocks[0]; + + if (mEntryRequiredRegs.Size() && !mEntryRequiredRegs[CPU_REG_Z] && (!mEntryRequiredRegs[CPU_REG_A] || !mEntryRequiredRegs[CPU_REG_X]) && !mEntryRequiredRegs[CPU_REG_C]) + { + for (int i = eb->mIns.Size() - 7; i >= 0; i--) + { + int sreg, dreg, offset; + if (eb->Is16BitAddSubImmediate(i, sreg, dreg, offset)) + { + int j = 0; + while (j < mEntryBlocks.Size() && mEntryBlocks[j]->CanForward16BitAddSubImmediate(sreg, dreg, offset, mEntryBlocks[j]->mTemp)) + j++; + + if (j == mEntryBlocks.Size()) + { + for (int k = 0; k < 7; k++) + mIns.Insert(k, eb->mIns[i + k]); + + for (int j = 0; j < mEntryBlocks.Size(); j++) + mEntryBlocks[j]->mIns.Remove(mEntryBlocks[j]->mTemp, 7); + + if (mEntryRequiredRegs[CPU_REG_A]) + { + mIns.Insert(0, NativeCodeInstruction(mIns[0].mIns, ASMIT_TAX)); + for (int i = 1; i < 8; i++) + mIns[i].mLive |= LIVE_CPU_REG_X; + mIns.Insert(8, NativeCodeInstruction(mIns[0].mIns, ASMIT_TXA)); + } + + changed = true; + } + } + } + } + + if (changed) + CheckLive(); + + while (eb->mIns.Size() > 0) + { + int addr, index, taddr, tindex; + if (!mEntryRequiredRegs[CPU_REG_X] && eb->HasTailSTAX16(addr, index)) + { + i = 1; + while (i < mEntryBlocks.Size() && mEntryBlocks[i]->HasTailSTAX16(taddr, tindex) && taddr == addr) + i++; + if (i == mEntryBlocks.Size()) + { + mEntryRequiredRegs += CPU_REG_A; + mEntryRequiredRegs += CPU_REG_X; + + mIns.Insert(0, eb->mIns[index + 3]); + mIns.Insert(0, eb->mIns[index + 1]); + + mIns[0].mType = ASMIT_STX; + mIns[0].mLive |= LIVE_CPU_REG_A | LIVE_CPU_REG_X | LIVE_CPU_REG_Y; + mIns[1].mLive |= LIVE_CPU_REG_A | LIVE_CPU_REG_X | LIVE_CPU_REG_Y; + if (mEntryRequiredRegs[CPU_REG_C]) + { + mIns[0].mLive |= LIVE_CPU_REG_C; + mIns[1].mLive |= LIVE_CPU_REG_C; + } + if (mEntryRequiredRegs[CPU_REG_Z]) + { + mIns[0].mLive |= LIVE_CPU_REG_Z; + mIns[1].mLive |= LIVE_CPU_REG_Z; + } + for (int i = 0; i < mEntryBlocks.Size(); i++) + { + NativeCodeBasicBlock* b = mEntryBlocks[i]; + b->HasTailSTAX16(taddr, tindex); + + b->mExitRequiredRegs += CPU_REG_A; + b->mExitRequiredRegs += CPU_REG_X; + + assert(b->mIns[tindex].mMode != ASMIM_ABSOLUTE_X); + b->mIns[tindex + 0].mType = ASMIT_LDX; + for (int j = tindex; j < b->mIns.Size(); j++) + b->mIns[j].mLive |= LIVE_CPU_REG_A | LIVE_CPU_REG_X; + + b->mIns.Remove(tindex + 3); + b->mIns.Remove(tindex + 1); + } + changed = true; + } + } + + NativeCodeInstruction& ins(eb->mIns[eb->mIns.Size() - 1]); + i = 1; + while (i < mEntryBlocks.Size() && mEntryBlocks[i]->SameTail(ins)) + i++; + if (i == mEntryBlocks.Size()) + { + if (ins.RequiresAccu()) + mEntryRequiredRegs += CPU_REG_A; + if (ins.RequiresYReg()) + mEntryRequiredRegs += CPU_REG_Y; + if (ins.RequiresXReg()) + mEntryRequiredRegs += CPU_REG_X; + if (ins.ChangesZFlag()) + mEntryRequiredRegs -= CPU_REG_Z; + if (ins.ChangesCarry()) + mEntryRequiredRegs -= CPU_REG_C; + if (ins.RequiresCarry()) + mEntryRequiredRegs += CPU_REG_C; + + mIns.Insert(0, ins); + + for (int i = 0; i < mEntryBlocks.Size(); i++) + { + NativeCodeBasicBlock* b = mEntryBlocks[i]; + + if (ins.RequiresAccu()) + b->mExitRequiredRegs += CPU_REG_A; + if (ins.RequiresYReg()) + b->mExitRequiredRegs += CPU_REG_Y; + if (ins.RequiresXReg()) + b->mExitRequiredRegs += CPU_REG_X; + if (ins.ChangesZFlag()) + b->mExitRequiredRegs -= CPU_REG_Z; + if (ins.ChangesCarry()) + b->mExitRequiredRegs -= CPU_REG_C; + if (ins.RequiresCarry()) + b->mExitRequiredRegs += CPU_REG_C; + + b->mIns.SetSize(b->mIns.Size() - 1); + } + changed = true; + } + else + { + if (eb->HasTailSTA(addr, index)) + { + i = 1; + while (i < mEntryBlocks.Size() && mEntryBlocks[i]->HasTailSTA(taddr, tindex) && taddr == addr) + i++; + if (i == mEntryBlocks.Size()) + { + mEntryRequiredRegs += CPU_REG_A; + + mIns.Insert(0, eb->mIns[index]); + mIns[0].mLive |= LIVE_CPU_REG_A | LIVE_CPU_REG_X | LIVE_CPU_REG_Y | LIVE_CPU_REG_C | LIVE_CPU_REG_Z; + for (int i = 0; i < mEntryBlocks.Size(); i++) + { + NativeCodeBasicBlock* b = mEntryBlocks[i]; + b->HasTailSTA(taddr, tindex); + b->mExitRequiredRegs += CPU_REG_A; + + for (int j = tindex + 1; j < b->mIns.Size(); j++) + b->mIns[j].mLive |= LIVE_CPU_REG_A; + b->mIns.Remove(tindex); + } + changed = true; + } + } + + if (eb->HasTailSTX(addr, index)) + { + i = 1; + while (i < mEntryBlocks.Size() && mEntryBlocks[i]->HasTailSTX(taddr, tindex) && taddr == addr) + i++; + if (i == mEntryBlocks.Size()) + { + mEntryRequiredRegs += CPU_REG_X; + + mIns.Insert(0, eb->mIns[index]); + mIns[0].mLive |= LIVE_CPU_REG_A | LIVE_CPU_REG_X | LIVE_CPU_REG_Y | LIVE_CPU_REG_C | LIVE_CPU_REG_Z; + for (int i = 0; i < mEntryBlocks.Size(); i++) + { + NativeCodeBasicBlock* b = mEntryBlocks[i]; + b->HasTailSTX(taddr, tindex); + b->mExitRequiredRegs += CPU_REG_X; + for (int j = tindex + 1; j < b->mIns.Size(); j++) + b->mIns[j].mLive |= LIVE_CPU_REG_X; + b->mIns.Remove(tindex); + } + changed = true; + } + } + + if (eb->HasTailSTY(addr, index)) + { + i = 1; + while (i < mEntryBlocks.Size() && mEntryBlocks[i]->HasTailSTY(taddr, tindex) && taddr == addr) + i++; + if (i == mEntryBlocks.Size()) + { + mEntryRequiredRegs += CPU_REG_Y; + + mIns.Insert(0, eb->mIns[index]); + mIns[0].mLive |= LIVE_CPU_REG_A | LIVE_CPU_REG_X | LIVE_CPU_REG_Y | LIVE_CPU_REG_C | LIVE_CPU_REG_Z; + for (int i = 0; i < mEntryBlocks.Size(); i++) + { + NativeCodeBasicBlock* b = mEntryBlocks[i]; + b->HasTailSTY(taddr, tindex); + b->mExitRequiredRegs += CPU_REG_Y; + for (int j = tindex + 1; j < b->mIns.Size(); j++) + b->mIns[j].mLive |= LIVE_CPU_REG_Y; + b->mIns.Remove(tindex); + } + changed = true; + } + } + + break; + } + } + + if (changed) + CheckLive(); + + if (!changed && !mEntryRequiredRegs[CPU_REG_Z] && (!mEntryRequiredRegs[CPU_REG_A] || !mEntryRequiredRegs[CPU_REG_X])) + { + for (int i = eb->mIns.Size() - 1; i > 0; i--) + { + if (eb->mIns[i - 1].mType == ASMIT_LDA && eb->mIns[i - 1].mMode == ASMIM_ZERO_PAGE && eb->mIns[i - 0].mType == ASMIT_STA && eb->mIns[i - 0].mMode == ASMIM_ZERO_PAGE || + eb->mIns[i - 1].mType == ASMIT_LDX && eb->mIns[i - 1].mMode == ASMIM_ZERO_PAGE && eb->mIns[i - 0].mType == ASMIT_STX && eb->mIns[i - 0].mMode == ASMIM_ZERO_PAGE) + { + int saddr = eb->mIns[i - 1].mAddress, daddr = eb->mIns[i - 0].mAddress; + int j = 0; + while (j < mEntryBlocks.Size() && mEntryBlocks[j]->CanForwardZPMove(saddr, daddr, mEntryBlocks[j]->mTemp)) + j++; + + if (j == mEntryBlocks.Size()) + { + const InterInstruction* iins(eb->mIns[i].mIns); + + if (!mEntryRequiredRegs[CPU_REG_A]) + { + for (int j = 0; j < mEntryBlocks.Size(); j++) + mEntryBlocks[j]->mIns[mEntryBlocks[j]->mTemp].mLive |= LIVE_MEM; + + mIns.Insert(0, NativeCodeInstruction(iins, ASMIT_LDA, ASMIM_ZERO_PAGE, saddr)); + mIns.Insert(1, NativeCodeInstruction(iins, ASMIT_STA, ASMIM_ZERO_PAGE, daddr)); + changed = true; + } + else if (!mEntryRequiredRegs[CPU_REG_X]) + { + for (int j = 0; j < mEntryBlocks.Size(); j++) + mEntryBlocks[j]->mIns[mEntryBlocks[j]->mTemp].mLive |= LIVE_MEM; + + mIns.Insert(0, NativeCodeInstruction(iins, ASMIT_LDX, ASMIM_ZERO_PAGE, saddr)); + mIns.Insert(1, NativeCodeInstruction(iins, ASMIT_STX, ASMIM_ZERO_PAGE, daddr)); + changed = true; + } + } + } + } + if (changed) + CheckLive(); + } +#if 1 + if (!changed && !mEntryRequiredRegs[CPU_REG_Z] && (!mEntryRequiredRegs[CPU_REG_A] || !mEntryRequiredRegs[CPU_REG_X] || !mEntryRequiredRegs[CPU_REG_Y])) + { + for (int i = eb->mIns.Size() - 1; i > 0; i--) + { + if ((eb->mIns[i - 1].mType == ASMIT_LDA && eb->mIns[i - 0].mType == ASMIT_STA || + eb->mIns[i - 1].mType == ASMIT_LDX && eb->mIns[i - 0].mType == ASMIT_STX || + eb->mIns[i - 1].mType == ASMIT_LDY && eb->mIns[i - 0].mType == ASMIT_STY) && + (eb->mIns[i - 1].mMode == ASMIM_ZERO_PAGE || eb->mIns[i - 1].mMode == ASMIM_ABSOLUTE) && + (eb->mIns[i - 0].mMode == ASMIM_ZERO_PAGE || eb->mIns[i - 0].mMode == ASMIM_ABSOLUTE)) + { + int j = 0; + while (j < mEntryBlocks.Size() && mEntryBlocks[j]->CanForwardLoadStore(eb->mIns[i - 1], eb->mIns[i - 0], mEntryBlocks[j]->mTemp)) + j++; + + if (j == mEntryBlocks.Size()) + { + const InterInstruction* iins(eb->mIns[i].mIns); + + if (!mEntryRequiredRegs[CPU_REG_A]) + { + mIns.Insert(0, NativeCodeInstruction(iins, ASMIT_LDA, eb->mIns[i - 1])); + mIns.Insert(1, NativeCodeInstruction(iins, ASMIT_STA, eb->mIns[i])); + changed = true; + } + else if (!mEntryRequiredRegs[CPU_REG_X]) + { + mIns.Insert(0, NativeCodeInstruction(iins, ASMIT_LDX, eb->mIns[i - 1])); + mIns.Insert(1, NativeCodeInstruction(iins, ASMIT_STX, eb->mIns[i])); + changed = true; + } + else if (!mEntryRequiredRegs[CPU_REG_Y]) + { + mIns.Insert(0, NativeCodeInstruction(iins, ASMIT_LDY, eb->mIns[i - 1])); + mIns.Insert(1, NativeCodeInstruction(iins, ASMIT_STY, eb->mIns[i])); + changed = true; + } + + for (int j = 0; j < mEntryBlocks.Size(); j++) + mEntryBlocks[j]->mIns.Remove(mEntryBlocks[j]->mTemp, 2); + } + } + } + if (changed) + CheckLive(); + } +#endif + } + + if (mEntryBlocks.Size() >= 1) + { + NativeCodeBasicBlock* eb = mEntryBlocks[0]; + + if (eb->mIns.Size()) + { + NativeCodeInstruction& ins(eb->mIns[eb->mIns.Size() - 1]); + if (ins.mType == ASMIT_STA && ins.mMode == ASMIM_ZERO_PAGE && + (eb->mBranch == ASMIT_JMP || + eb->mTrueJump == this && eb->mFalseJump && !eb->mFalseJump->mEntryRequiredRegs[ins.mAddress] || + eb->mFalseJump == this && eb->mTrueJump && !eb->mTrueJump->mEntryRequiredRegs[ins.mAddress])) + { + int i = 1; + while (i < mEntryBlocks.Size() && mEntryBlocks[i]->mIns.Size() > 0 && mEntryBlocks[i]->mIns.Last().IsSame(ins) && + (mEntryBlocks[i]->mBranch == ASMIT_JMP || + mEntryBlocks[i]->mTrueJump == this && mEntryBlocks[i]->mFalseJump && !mEntryBlocks[i]->mFalseJump->mEntryRequiredRegs[ins.mAddress] || + mEntryBlocks[i]->mFalseJump == this && mEntryBlocks[i]->mTrueJump && !mEntryBlocks[i]->mTrueJump->mEntryRequiredRegs[ins.mAddress])) + i++; + if (i == mEntryBlocks.Size()) + { + mEntryRequiredRegs += CPU_REG_A; + mEntryRequiredRegs -= ins.mAddress; + + mIns.Insert(0, ins); + for (int i = 0; i < mEntryBlocks.Size(); i++) + { + NativeCodeBasicBlock* meb = mEntryBlocks[i]; + meb->mExitRequiredRegs += CPU_REG_A; + meb->mIns.SetSize(meb->mIns.Size() - 1); + } + + changed = true; + } + } + } + } + + CheckLive(); + + if (mEntryBlocks.Size() > 2) + { + NativeCodeBasicBlock* nblock = SplitMatchingTails(mProc); + if (nblock) + { + if (nblock->JoinTailCodeSequences(loops)) + changed = true; + } + } + + } +#endif +#if 1 + if (mEntryBlocks.Size() == 1) + { + NativeCodeBasicBlock* eblock = mEntryBlocks[0]; + if (mIns.Size() > 0 && eblock->mIns.Size() > 0) + { + int sz = eblock->mIns.Size(); + + if (mIns[0].mType == ASMIT_ORA && mIns[0].mMode == ASMIM_IMMEDIATE && mIns[0].mAddress == 0) + { + if (eblock->mIns.Last().ChangesAccuAndFlag() || + sz >= 2 && !eblock->mIns.Last().ChangesZFlag() && eblock->mIns[sz - 2].ChangesAccuAndFlag()) + { + while (sz > 0) + { + sz--; + eblock->mIns[sz].mLive |= LIVE_CPU_REG_Z; + if (eblock->mIns[sz].ChangesZFlag()) + break; + } + eblock->mExitRequiredRegs += CPU_REG_Z; + mEntryRequiredRegs += CPU_REG_Z; + mIns.Remove(0); + changed = true; + } + } + } + } +#endif + +#if 1 + if (mTrueJump && mFalseJump && mTrueJump->mTrueJump && !mTrueJump->mFalseJump && mFalseJump == mTrueJump->mTrueJump && + mTrueJump->mNumEntries == 1 && mFalseJump->mNumEntries == 2) + + { + // TAX, SHL -> .... LDX --> TXA + // \-------------> + // + int nins = mIns.Size(), tins = mTrueJump->mIns.Size(), fins = mFalseJump->mIns.Size(); + if (nins > 1 && tins > 0 && fins > 0 && mFalseJump->mIns[0].mType == ASMIT_TXA && + mIns[nins - 2].mType == ASMIT_TAX && !mIns[nins - 1].ChangesAccu() && !mFalseJump->mEntryRequiredRegs[CPU_REG_A]) + { + mTrueJump->mIns.Push(NativeCodeInstruction(mFalseJump->mIns[0].mIns, ASMIT_TXA)); + mFalseJump->mIns[0].mType = ASMIT_NOP; mFalseJump->mIns[0].mMode = ASMIM_IMPLIED; + mIns[nins - 1].mLive |= LIVE_CPU_REG_A; + mIns[nins - 2].mLive |= LIVE_CPU_REG_A; + mFalseJump->mEntryRequiredRegs += CPU_REG_A; + mTrueJump->mExitRequiredRegs += CPU_REG_A; + changed = true; + } + } + +#endif + + CheckLive(); + + if (mTrueJump && mFalseJump) + { + int addr, index; + if (HasTailSTX(addr, index)) + { + if (mTrueJump->mEntryRequiredRegs[addr] && !mFalseJump->mEntryRequiredRegs[addr] && mTrueJump->mNumEntries == 1) + { + mTrueJump->mEntryProvidedRegs += CPU_REG_X; + for (int i = index; i < mIns.Size(); i++) + mIns[i].mLive |= LIVE_CPU_REG_X; + mIns[index].mLive |= mIns[mIns.Size() - 1].mLive; + + mExitRequiredRegs += CPU_REG_X; + mTrueJump->mEntryRequiredRegs += CPU_REG_X; + + mTrueJump->mIns.Insert(0, mIns[index]); + mIns.Remove(index); + changed = true; + + mTrueJump->CheckLive(); + mFalseJump->CheckLive(); + + } + else if (mFalseJump->mEntryRequiredRegs[addr] && !mTrueJump->mEntryRequiredRegs[addr] && mFalseJump->mNumEntries == 1) + { + mFalseJump->mEntryProvidedRegs += CPU_REG_X; + for (int i = index; i < mIns.Size(); i++) + mIns[i].mLive |= LIVE_CPU_REG_X; + mIns[index].mLive |= mIns[mIns.Size() - 1].mLive; + + mExitRequiredRegs += CPU_REG_X; + mFalseJump->mEntryRequiredRegs += CPU_REG_X; + + mFalseJump->mIns.Insert(0, mIns[index]); + mIns.Remove(index); + changed = true; + + mTrueJump->CheckLive(); + mFalseJump->CheckLive(); + + } + } + } + +#if 1 + if (mTrueJump && mFalseJump && mIns.Size() >= 2) + { + int nins = mIns.Size(); + if (mIns[nins - 2].mType == ASMIT_LDA && mIns[nins - 2].mMode == ASMIM_IMMEDIATE && + mIns[nins - 1].mType == ASMIT_CMP && mIns[nins - 1].mMode == ASMIM_ZERO_PAGE && (mIns[nins - 1].mLive & LIVE_MEM) && !(mIns[nins - 1].mLive & LIVE_CPU_REG_A) && + !mTrueJump->mEntryRequiredRegs[CPU_REG_C] && !mFalseJump->mEntryRequiredRegs[CPU_REG_C] && !mTrueJump->mEntryRequiredRegs[CPU_REG_Z] && !mFalseJump->mEntryRequiredRegs[CPU_REG_Z]) + { + int im = mIns[nins - 2].mAddress; + + mIns[nins - 2].CopyMode(mIns[nins - 1]); + mIns[nins - 2].mLive |= LIVE_MEM; + mIns[nins - 1].mMode = ASMIM_IMMEDIATE; + mIns[nins - 1].mFlags = NCIF_LOWER | NCIF_UPPER; + + if (mBranch == ASMIT_BCC) + { + if (im == 255) + { + mIns[nins - 1].mType = ASMIT_SEC; + mIns[nins - 1].mMode = ASMIM_IMPLIED; + } + else + { + mIns[nins - 1].mAddress = im + 1; + mBranch = ASMIT_BCS; + } + } + else if (mBranch == ASMIT_BCS) + { + if (im == 255) + { + mIns[nins - 1].mType = ASMIT_SEC; + mIns[nins - 1].mMode = ASMIM_IMPLIED; + } + else + { + mIns[nins - 1].mAddress = im + 1; + mBranch = ASMIT_BCC; + } + } + else + mIns[nins - 1].mAddress = im; + + + changed = true; + } + } +#endif + CheckLive(); + + if (mTrueJump && mFalseJump) + { + if (mTrueJump->mIns.Size() > 0 && mFalseJump->mIns.Size() > 0 && !mExitRequiredRegs[CPU_REG_Z] && (mBranch == ASMIT_BCC || mBranch == ASMIT_BCS) && + mTrueJump->mNumEntries == 1 && mFalseJump->mNumEntries == 1) + { + if (!mTrueJump->mIns[0].ChangesCarry() && mTrueJump->mIns[0].IsSame(mFalseJump->mIns[0])) + { + int live = mTrueJump->mIns[0].mLive | mFalseJump->mIns[0].mLive; + mTrueJump->mIns[0].mLive |= LIVE_CPU_REG_C | live; + mIns.Push(mTrueJump->mIns[0]); + mTrueJump->mIns.Remove(0); + mFalseJump->mIns.Remove(0); + if (live & LIVE_CPU_REG_A) + { + mExitRequiredRegs += CPU_REG_A; + mTrueJump->mEntryRequiredRegs += CPU_REG_A; + mFalseJump->mEntryRequiredRegs += CPU_REG_A; + } + if (live & LIVE_CPU_REG_X) + { + mExitRequiredRegs += CPU_REG_X; + mTrueJump->mEntryRequiredRegs += CPU_REG_X; + mFalseJump->mEntryRequiredRegs += CPU_REG_X; + } + if (live & LIVE_CPU_REG_Y) + { + mExitRequiredRegs += CPU_REG_Y; + mTrueJump->mEntryRequiredRegs += CPU_REG_Y; + mFalseJump->mEntryRequiredRegs += CPU_REG_Y; + } + if (live & LIVE_CPU_REG_Z) + { + mExitRequiredRegs += CPU_REG_Z; + mTrueJump->mEntryRequiredRegs += CPU_REG_Z; + mFalseJump->mEntryRequiredRegs += CPU_REG_Z; + } + + changed = true; + + CheckLive(); + } + } + } +#if 1 + if (mTrueJump && mFalseJump && mIns.Size() >= 2 && (mBranch == ASMIT_BEQ || mBranch == ASMIT_BNE)) + { + int nins = mIns.Size(); + if (mIns[nins - 2].mType == ASMIT_LDA && mIns[nins - 1].mType == ASMIT_CMP && !(mIns[nins - 1].mLive & LIVE_CPU_REG_A) && !mTrueJump->mEntryRequiredRegs[CPU_REG_C] && !mFalseJump->mEntryRequiredRegs[CPU_REG_C]) + { + if (mTrueJump->mIns.Size() > 0 && mTrueJump->mIns[0].mType == ASMIT_LDA && mTrueJump->mNumEntries == 1 && mTrueJump->mIns[0].SameEffectiveAddress(mIns[nins - 1])) + { + mIns[nins - 1].CopyMode(mIns[nins - 2]); + mIns[nins - 2].CopyMode(mTrueJump->mIns[0]); mIns[nins - 2].mLive |= mIns[nins - 1].NeedsLive(); + mIns[nins - 1].mLive |= LIVE_CPU_REG_A; + mExitRequiredRegs += CPU_REG_A; + mTrueJump->mEntryRequiredRegs += CPU_REG_A; + if (mTrueJump->mIns[0].mLive & LIVE_CPU_REG_Z) + { + mTrueJump->mIns[0].mType = ASMIT_ORA; + mTrueJump->mIns[0].mMode = ASMIM_IMMEDIATE; + mTrueJump->mIns[0].mAddress = 0; + } + else + mTrueJump->mIns.Remove(0); + changed = true; + + CheckLive(); + } + else if (mFalseJump->mIns.Size() > 0 && mFalseJump->mIns[0].mType == ASMIT_LDA && mFalseJump->mNumEntries == 1 && mFalseJump->mIns[0].SameEffectiveAddress(mIns[nins - 1])) + { + mIns[nins - 1].CopyMode(mIns[nins - 2]); + mIns[nins - 2].CopyMode(mFalseJump->mIns[0]); mIns[nins - 2].mLive |= mIns[nins - 1].NeedsLive(); + mIns[nins - 1].mLive |= LIVE_CPU_REG_A; + mExitRequiredRegs += CPU_REG_A; + mFalseJump->mEntryRequiredRegs += CPU_REG_A; + if (mFalseJump->mIns[0].mLive & LIVE_CPU_REG_Z) + { + mFalseJump->mIns[0].mType = ASMIT_ORA; + mFalseJump->mIns[0].mMode = ASMIM_IMMEDIATE; + mFalseJump->mIns[0].mAddress = 0; + } + else + mFalseJump->mIns.Remove(0); + changed = true; + + CheckLive(); + } + } + } +#endif + + if (mTrueJump) mTrueJump->CheckLive(); + if (mFalseJump) mFalseJump->CheckLive(); +#if 1 + if (loops && mIns.Size() >= 1 && mEntryBlocks.Size() == 2) + { + NativeCodeBasicBlock* pblock = mEntryBlocks[0], * lblock = mEntryBlocks[1]; + int ps = pblock->mIns.Size(), ls = lblock->mIns.Size(); + + if (ls >= 2) + { + if (mIns[0].mType == ASMIT_LDY && mIns[0].mMode == ASMIM_ZERO_PAGE && !(mIns[0].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z))) + { + if (lblock->mIns[ls - 2].mType == ASMIT_LDA && lblock->mIns[ls - 2].mMode == ASMIM_ZERO_PAGE && lblock->mIns[ls - 2].mAddress == mIns[0].mAddress && + lblock->mIns[ls - 1].mType == ASMIT_CMP && HasAsmInstructionMode(ASMIT_CPY, lblock->mIns[ls - 1].mMode) && !(lblock->mIns[ls - 1].mLive & LIVE_CPU_REG_A)) + { + lblock->mIns[ls - 2].mType = ASMIT_LDY; lblock->mIns[ls - 2].mLive |= LIVE_CPU_REG_Y; + lblock->mIns[ls - 1].mType = ASMIT_CPY; lblock->mIns[ls - 1].mLive |= LIVE_CPU_REG_Y; + + pblock = AddDominatorBlock(mProc, pblock); + + pblock->mIns.Push(mIns[0]); + mIns.Remove(0); + + lblock->mExitRequiredRegs += CPU_REG_Y; + pblock->mExitRequiredRegs += CPU_REG_Y; + mEntryRequiredRegs += CPU_REG_Y; + mExitRequiredRegs += CPU_REG_Y; + + changed = true; + } + else if (lblock->mIns[ls - 1].mType == ASMIT_LDA && lblock->mIns[ls - 1].mMode == ASMIM_ZERO_PAGE && lblock->mIns[ls - 1].mAddress == mIns[0].mAddress && + !(lblock->mIns[ls - 1].mLive & LIVE_CPU_REG_A)) + { + lblock->mIns[ls - 1].mType = ASMIT_LDY; lblock->mIns[ls - 1].mLive |= LIVE_CPU_REG_Y; + + pblock = AddDominatorBlock(mProc, pblock); + + pblock->mIns.Push(mIns[0]); + mIns.Remove(0); + + lblock->mExitRequiredRegs += CPU_REG_Y; + pblock->mExitRequiredRegs += CPU_REG_Y; + mEntryRequiredRegs += CPU_REG_Y; + mExitRequiredRegs += CPU_REG_Y; + + changed = true; + } + } + else if (mIns[0].mType == ASMIT_LDX && mIns[0].mMode == ASMIM_ZERO_PAGE && !(mIns[0].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z))) + { + if (lblock->mIns[ls - 2].mType == ASMIT_LDA && lblock->mIns[ls - 2].mMode == ASMIM_ZERO_PAGE && lblock->mIns[ls - 2].mAddress == mIns[0].mAddress && + lblock->mIns[ls - 1].mType == ASMIT_CMP && HasAsmInstructionMode(ASMIT_CPX, lblock->mIns[ls - 1].mMode) && !(lblock->mIns[ls - 1].mLive & LIVE_CPU_REG_A)) + { + lblock->mIns[ls - 2].mType = ASMIT_LDX; lblock->mIns[ls - 2].mLive |= LIVE_CPU_REG_X; + lblock->mIns[ls - 1].mType = ASMIT_CPX; lblock->mIns[ls - 1].mLive |= LIVE_CPU_REG_X; + + pblock = AddDominatorBlock(mProc, pblock); + + pblock->mIns.Push(mIns[0]); + mIns.Remove(0); + + lblock->mExitRequiredRegs += CPU_REG_X; + pblock->mExitRequiredRegs += CPU_REG_X; + mEntryRequiredRegs += CPU_REG_X; + mExitRequiredRegs += CPU_REG_X; + + changed = true; + } + } + else if (mIns[0].mType == ASMIT_LDA && mIns[0].mMode == ASMIM_ZERO_PAGE && !(mIns[0].mLive & LIVE_CPU_REG_Z)) + { + if (lblock->mIns[ls - 2].mType == ASMIT_LDA && lblock->mIns[ls - 2].mMode == ASMIM_ZERO_PAGE && lblock->mIns[ls - 2].mAddress == mIns[0].mAddress && + lblock->mIns[ls - 1].mType == ASMIT_CMP) + { + pblock = AddDominatorBlock(mProc, pblock); + + pblock->mIns.Push(mIns[0]); + mIns.Remove(0); + + lblock->mExitRequiredRegs += CPU_REG_A; + pblock->mExitRequiredRegs += CPU_REG_A; + mEntryRequiredRegs += CPU_REG_A; + mExitRequiredRegs += CPU_REG_A; + + lblock->mIns[ls - 1].mLive |= LIVE_CPU_REG_A; + + changed = true; + } + } + + if (!changed && mIns[0].mType == ASMIT_LDY && mIns[0].mMode == ASMIM_ZERO_PAGE) + { + if (lblock->mIns[ls - 2].mType == ASMIT_STY && lblock->mIns[ls - 2].mMode == ASMIM_ZERO_PAGE && lblock->mIns[ls - 2].mAddress == mIns[0].mAddress && lblock->mIns[ls - 1].mType == ASMIT_CPY) + { + pblock = AddDominatorBlock(mProc, pblock); + + pblock->mIns.Push(mIns[0]); + mIns.Remove(0); + + pblock->mExitRequiredRegs += CPU_REG_Y; + lblock->mExitRequiredRegs += CPU_REG_Y; + mEntryRequiredRegs += CPU_REG_Y; + mExitRequiredRegs += CPU_REG_Y; + } + } + } + } +#endif +#if 1 + if (mIns.Size() >= 1 && mIns[0].mType == ASMIT_LDA && mIns[0].mMode == ASMIM_ZERO_PAGE && mEntryBlocks.Size() > 1 && !(mIns[0].mLive & LIVE_MEM)) + { + if (mEntryRequiredRegs.Size() > 0 && !mEntryRequiredRegs[CPU_REG_X]) + { + int i = 0; + while (i < mEntryBlocks.Size() && mEntryBlocks[i]->CanChangeTailZPStoreToX(mIns[0].mAddress, this)) + i++; + if (i == mEntryBlocks.Size()) + { + for (int i = 0; i < mEntryBlocks.Size(); i++) + mEntryBlocks[i]->ChangeTailZPStoreToX(mIns[0].mAddress); + mEntryRequiredRegs += CPU_REG_X; + mIns[0].mType = ASMIT_TXA; + mIns[0].mMode = ASMIM_IMPLIED; + changed = true; + + CheckLive(); + } + } + if (!changed && mEntryRequiredRegs.Size() > 0 && !mEntryRequiredRegs[CPU_REG_Y]) + { + int i = 0; + while (i < mEntryBlocks.Size() && mEntryBlocks[i]->CanChangeTailZPStoreToY(mIns[0].mAddress, this)) + i++; + if (i == mEntryBlocks.Size()) + { + for (int i = 0; i < mEntryBlocks.Size(); i++) + mEntryBlocks[i]->ChangeTailZPStoreToY(mIns[0].mAddress); + mEntryRequiredRegs += CPU_REG_Y; + mIns[0].mType = ASMIT_TYA; + mIns[0].mMode = ASMIM_IMPLIED; + changed = true; + + CheckLive(); + } + } + } + + if (mIns.Size() >= 1 && mTrueJump && !mFalseJump && mTrueJump->mIns.Size() > 0) + { + int sz = mIns.Size(); + if (mIns[sz - 1].mType == ASMIT_STA && mTrueJump->mIns[0].mType == ASMIT_LDA && mIns[sz - 1].SameEffectiveAddress(mTrueJump->mIns[0])) + { + int i = 0; + while (i < mTrueJump->mEntryBlocks.Size() && !mTrueJump->mEntryBlocks[i]->mFalseJump) + i++; + if (i == mTrueJump->mEntryBlocks.Size()) + { + bool zlive = mTrueJump->mIns[0].mLive & LIVE_CPU_REG_Z; + + for (int j = 0; j < mTrueJump->mEntryBlocks.Size(); j++) + { + if (mTrueJump->mEntryBlocks[j] != this) + { + mTrueJump->mEntryBlocks[j]->mIns.Push(mTrueJump->mIns[0]); + mTrueJump->mEntryBlocks[j]->mExitRequiredRegs += CPU_REG_A; + } + + if (zlive) + { + mTrueJump->mEntryBlocks[j]->mExitRequiredRegs += CPU_REG_Z; + int k = mTrueJump->mEntryBlocks[j]->mIns.Size(); + while (k > 0) + { + k--; + if (mTrueJump->mEntryBlocks[j]->mIns[k].ChangesZFlag()) + { + if (mTrueJump->mEntryBlocks[j]->mIns[k].ChangesAccuAndFlag()) + mTrueJump->mEntryBlocks[j]->mIns[k].mLive |= LIVE_CPU_REG_Z; + else + mTrueJump->mEntryBlocks[j]->mIns.Insert(k + 1, NativeCodeInstruction(mTrueJump->mIns[0].mIns, ASMIT_ORA, ASMIM_IMMEDIATE, 0)); + break; + } + mTrueJump->mEntryBlocks[j]->mIns[k].mLive |= LIVE_CPU_REG_Z; + } + } + } + mExitRequiredRegs += CPU_REG_A; + + if (zlive) + mExitRequiredRegs += CPU_REG_Z; + + mTrueJump->mIns.Remove(0); + mTrueJump->mEntryRequiredRegs += CPU_REG_A; + + if (zlive) + mTrueJump->mEntryRequiredRegs += CPU_REG_Z; + + changed = true; + + CheckLive(); + } + } + } +#endif + + if (mIns.Size() >= 1 && mIns[0].mType == ASMIT_STA && mIns[0].mMode == ASMIM_ZERO_PAGE && !(mIns[0].mMode & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z)) && mEntryBlocks.Size() == 2) + { + if (!mEntryBlocks[0]->mFalseJump && !mEntryBlocks[1]->mFalseJump && mEntryBlocks[0]->mIns.Size() > 0 && mEntryBlocks[1]->mIns.Size() > 0) + { + if (mEntryBlocks[0]->mIns.Last().mType == ASMIT_LDA && mEntryBlocks[0]->mIns.Last().SameEffectiveAddress(mIns[0])) + { + mEntryBlocks[1]->mIns.Push(NativeCodeInstruction(mIns[0].mIns, ASMIT_STA, mIns[0])); + mIns.Remove(0); + changed = true; + } + else if (mEntryBlocks[1]->mIns.Last().mType == ASMIT_LDA && mEntryBlocks[1]->mIns.Last().SameEffectiveAddress(mIns[0])) + { + mEntryBlocks[0]->mIns.Push(NativeCodeInstruction(mIns[0].mIns, ASMIT_STA, mIns[0])); + mIns.Remove(0); + changed = true; + } + } + } +#if 1 + if (mFalseJump && mTrueJump->mIns.Size() > 0 && mFalseJump->mIns.Size() > 0 && mTrueJump->mNumEntries == 1 && mFalseJump->mNumEntries == 1 && + mTrueJump->mIns[0].mType == ASMIT_LDA && mTrueJump->mIns[0].mMode == ASMIM_ZERO_PAGE && !(mTrueJump->mIns[0].mLive & LIVE_MEM) && + mFalseJump->mIns[0].mType == ASMIT_LDA && mFalseJump->mIns[0].mMode == ASMIM_ZERO_PAGE && !(mFalseJump->mIns[0].mLive & LIVE_MEM) && + mTrueJump->mIns[0].mAddress == mFalseJump->mIns[0].mAddress) + { + if (mExitRequiredRegs.Size() > 0 && !mExitRequiredRegs[CPU_REG_X]) + { + if (CanChangeTailZPStoreToX(mTrueJump->mIns[0].mAddress, mTrueJump, mFalseJump)) + { + ChangeTailZPStoreToX(mTrueJump->mIns[0].mAddress); + + mExitRequiredRegs += CPU_REG_X; + + mTrueJump->mEntryProvidedRegs += CPU_REG_X; + mFalseJump->mEntryProvidedRegs += CPU_REG_X; + + mTrueJump->mEntryRequiredRegs += CPU_REG_X; + mFalseJump->mEntryRequiredRegs += CPU_REG_X; + + mTrueJump->mIns[0].mType = ASMIT_TXA; + mTrueJump->mIns[0].mMode = ASMIM_IMPLIED; + + mFalseJump->mIns[0].mType = ASMIT_TXA; + mFalseJump->mIns[0].mMode = ASMIM_IMPLIED; + + changed = true; + + CheckLive(); + } + } + } +#endif + CheckLive(); + + if (mEntryBlocks.Size() == 1 && mIns.Size() >= 1 && mIns[0].mType == ASMIT_LDA && mIns[0].mMode == ASMIM_ZERO_PAGE && !(mIns[0].mLive & LIVE_CPU_REG_Z)) + { + NativeCodeBasicBlock* eb = mEntryBlocks[0]; + int index, addr; + if (eb->HasTailSTA(addr, index) && addr == mIns[0].mAddress) + { + for (int i = index; i < eb->mIns.Size(); i++) + eb->mIns[i].mLive |= LIVE_CPU_REG_A; + eb->mExitRequiredRegs += CPU_REG_A; + mEntryRequiredRegs += CPU_REG_A; + mIns.Remove(0); + changed = true; + } + else if (eb->HasTailSTY(addr, index) && addr == mIns[0].mAddress) + { + for (int i = index; i < eb->mIns.Size(); i++) + eb->mIns[i].mLive |= LIVE_CPU_REG_Y; + eb->mExitRequiredRegs += CPU_REG_Y; + mEntryRequiredRegs += CPU_REG_Y; + mIns[0].mType = ASMIT_TYA; + mIns[0].mMode = ASMIM_IMPLIED; + changed = true; + } + else if (eb->HasTailSTX(addr, index) && addr == mIns[0].mAddress) + { + for (int i = index; i < eb->mIns.Size(); i++) + eb->mIns[i].mLive |= LIVE_CPU_REG_X; + eb->mExitRequiredRegs += CPU_REG_X; + mEntryRequiredRegs += CPU_REG_X; + mIns[0].mType = ASMIT_TXA; + mIns[0].mMode = ASMIM_IMPLIED; + changed = true; + } + } +#if 1 + if (mIns.Size() >= 1 && mIns[0].mType == ASMIT_TAX && !(mIns[0].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z)) && !mEntryRegA) + { + int i = 0; + while (i < mEntryBlocks.Size() && mEntryBlocks[i]->mIns.Size() > 0 && mEntryBlocks[i]->mIns.Last().mType == ASMIT_LDA && HasAsmInstructionMode(ASMIT_LDX, mEntryBlocks[i]->mIns.Last().mMode) && !mEntryBlocks[i]->mFalseJump) + i++; + if (i == mEntryBlocks.Size()) + { + for (int i = 0; i < mEntryBlocks.Size(); i++) + { + NativeCodeBasicBlock* b = mEntryBlocks[i]; + int sz = b->mIns.Size(); + b->mIns[sz - 1].mType = ASMIT_LDX; + b->mIns[sz - 1].mLive |= LIVE_CPU_REG_X; + b->mExitRequiredRegs += CPU_REG_X; + changed = true; + } + + mEntryRequiredRegs += CPU_REG_X; + mIns[0].mType = ASMIT_NOP; mIns[0].mMode = ASMIM_IMPLIED; + } + } +#endif +#if 1 + if (mIns.Size() >= 1 && mIns[0].mType == ASMIT_TAY && !(mIns[0].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z)) && !mEntryRegA) + { + int i = 0; + while (i < mEntryBlocks.Size() && mEntryBlocks[i]->mIns.Size() > 0 && mEntryBlocks[i]->mIns.Last().mType == ASMIT_LDA && HasAsmInstructionMode(ASMIT_LDY, mEntryBlocks[i]->mIns.Last().mMode) && !mEntryBlocks[i]->mFalseJump) + i++; + if (i == mEntryBlocks.Size()) + { + for (int i = 0; i < mEntryBlocks.Size(); i++) + { + NativeCodeBasicBlock* b = mEntryBlocks[i]; + int sz = b->mIns.Size(); + b->mIns[sz - 1].mType = ASMIT_LDY; + b->mIns[sz - 1].mLive |= LIVE_CPU_REG_Y; + b->mExitRequiredRegs += CPU_REG_Y; + changed = true; + } + + mEntryRequiredRegs += CPU_REG_Y; + mIns[0].mType = ASMIT_NOP; mIns[0].mMode = ASMIM_IMPLIED; + } + } +#endif +#if 1 + if (mIns.Size() >= 1 && mIns[0].mMode == ASMIM_ABSOLUTE_X && !(mIns[0].mLive & (LIVE_CPU_REG_X | LIVE_CPU_REG_Z))) + { + int i = 0; + while (i < mEntryBlocks.Size() && mEntryBlocks[i]->mIns.Size() > 0 && mEntryBlocks[i]->mIns.Last().mType == ASMIT_LDX && mEntryBlocks[i]->mIns.Last().mMode == ASMIM_IMMEDIATE && !mEntryBlocks[i]->mFalseJump) + i++; + if (i == mEntryBlocks.Size()) + { + for (int i = 0; i < mEntryBlocks.Size(); i++) + { + NativeCodeBasicBlock* b = mEntryBlocks[i]; + int sz = b->mIns.Size(); + int index = b->mIns[sz - 1].mAddress; + b->mIns[sz - 1] = mIns[0]; + b->mIns[sz - 1].mMode = ASMIM_ABSOLUTE; + b->mIns[sz - 1].mAddress += index; + changed = true; + } + + mIns[0].mType = ASMIT_NOP; mIns[0].mMode = ASMIM_IMPLIED; + } + } +#endif + +#if 1 + if (mIns.Size() >= 1 && mIns.Last().mType == ASMIT_STA && mIns.Last().mMode == ASMIM_ZERO_PAGE && mTrueJump && mFalseJump && mTrueJump->mEntryRequiredRegs.Size() && mFalseJump->mEntryRequiredRegs.Size()) + { + const NativeCodeInstruction& ins(mIns.Last()); + + if (mTrueJump->mEntryRequiredRegs[ins.mAddress] && !mFalseJump->mEntryRequiredRegs[ins.mAddress] && mTrueJump->mEntryBlocks.Size() == 1) + { + mTrueJump->mIns.Insert(0, ins); + mTrueJump->mIns[0].mLive |= LIVE_CPU_REG_C; + mIns.Remove(mIns.Size() - 1); + mExitRequiredRegs += CPU_REG_A; + mTrueJump->mEntryRequiredRegs += CPU_REG_A; + mTrueJump->CheckLive(); + changed = true; + } + else if (mFalseJump->mEntryRequiredRegs[ins.mAddress] && !mTrueJump->mEntryRequiredRegs[ins.mAddress] && mFalseJump->mEntryBlocks.Size() == 1) + { + mFalseJump->mIns.Insert(0, ins); + mFalseJump->mIns[0].mLive |= LIVE_CPU_REG_C; + mIns.Remove(mIns.Size() - 1); + mExitRequiredRegs += CPU_REG_A; + mFalseJump->mEntryRequiredRegs += CPU_REG_A; + mFalseJump->CheckLive(); + changed = true; + } + } +#endif + if (mIns.Size() >= 1 && mBranch == ASMIT_BNE && (mIns.Last().mType == ASMIT_INC || mIns.Last().mType == ASMIT_DEC) && mIns.Last().mMode == ASMIM_ZERO_PAGE) + { + NativeCodeBasicBlock* b = mTrueJump; + + if (b->mIns.Size() == 2 && !b->mExitRequiredRegs[CPU_REG_A]) + { + if ((b->mIns[0].mType == ASMIT_LDA || b->mIns[0].mType == ASMIT_TXA || b->mIns[0].mType == ASMIT_TYA) && + b->mIns[1].mType == ASMIT_ORA && b->mIns[1].SameEffectiveAddress(mIns.Last())) + { + if (b->mBranch == ASMIT_BEQ) + { + b->mNumEntries--; + b->mEntryBlocks.RemoveAll(this); + mTrueJump = b->mFalseJump; + mTrueJump->mNumEntries++; + mTrueJump->mEntryBlocks.Push(this); + changed = true; + } + else if (b->mBranch == ASMIT_BNE) + { + b->mNumEntries--; + b->mEntryBlocks.RemoveAll(this); + mTrueJump = b->mTrueJump; + mTrueJump->mNumEntries++; + mTrueJump->mEntryBlocks.Push(this); + changed = true; + } + } + } + } + +#if 1 + if (mIns.Size() >= 2 && mFalseJump) + { + int ns = mIns.Size(); + if ((mIns[ns - 2].mType == ASMIT_INY || mIns[ns - 2].mType == ASMIT_DEY) && mIns[ns - 1].mType == ASMIT_TYA) + { + if (mBranch == ASMIT_BEQ && !mFalseJump->mEntryRequiredRegs[CPU_REG_A] && mTrueJump->mNumEntries == 1) + { + mTrueJump->mIns.Insert(0, NativeCodeInstruction(mIns[ns - 1].mIns, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + mIns[ns - 1].mType = ASMIT_NOP; + mIns[ns - 2].mLive |= LIVE_CPU_REG_Z; + changed = true; + } + else if (mBranch == ASMIT_BNE && !mTrueJump->mEntryRequiredRegs[CPU_REG_A] && mFalseJump->mNumEntries == 1) + { + mFalseJump->mIns.Insert(0, NativeCodeInstruction(mIns[ns - 1].mIns, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + mIns[ns - 1].mType = ASMIT_NOP; + mIns[ns - 2].mLive |= LIVE_CPU_REG_Z; + changed = true; + } + } + else if ((mIns[ns - 2].mType == ASMIT_INX || mIns[ns - 2].mType == ASMIT_DEX) && mIns[ns - 1].mType == ASMIT_TXA) + { + if (mBranch == ASMIT_BEQ && !mFalseJump->mEntryRequiredRegs[CPU_REG_A] && mTrueJump->mNumEntries == 1) + { + mTrueJump->mIns.Insert(0, NativeCodeInstruction(mIns[ns - 1].mIns, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + mIns[ns - 1].mType = ASMIT_NOP; + mIns[ns - 2].mLive |= LIVE_CPU_REG_Z; + changed = true; + } + else if (mBranch == ASMIT_BNE && !mTrueJump->mEntryRequiredRegs[CPU_REG_A] && mFalseJump->mNumEntries == 1) + { + mFalseJump->mIns.Insert(0, NativeCodeInstruction(mIns[ns - 1].mIns, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + mIns[ns - 1].mType = ASMIT_NOP; + mIns[ns - 2].mLive |= LIVE_CPU_REG_Z; + changed = true; + } + } + } +#endif +#if 1 + if (mIns.Size() >= 2 && mFalseJump) + { + int ns = mIns.Size(); + + if (mIns[ns - 2].mType == ASMIT_LDA && mIns[ns - 1].mType == ASMIT_CMP && mIns[ns - 1].mMode == ASMIM_ZERO_PAGE && !(mIns[ns - 1].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C))) + { + if (mFalseJump->mIns.Size() > 0 && mFalseJump->mIns[0].mType == ASMIT_LDA && mFalseJump->mIns[0].SameEffectiveAddress(mIns[ns - 1]) || + mTrueJump->mIns.Size() > 0 && mTrueJump->mIns[0].mType == ASMIT_LDA && mTrueJump->mIns[0].SameEffectiveAddress(mIns[ns - 1])) + { + NativeCodeInstruction ins = mIns[ns - 1]; + mIns[ns - 1].CopyMode(mIns[ns - 2]); + mIns[ns - 2].CopyMode(ins); + + if (mIns[ns - 1].RequiresXReg()) + mIns[ns - 2].mLive |= LIVE_CPU_REG_X; + if (mIns[ns - 1].RequiresYReg()) + mIns[ns - 2].mLive |= LIVE_CPU_REG_Y; + changed = true; + } + } + } +#endif + if (mIns.Size() >= 2 && mEntryBlocks.Size() == 1) + { + if (mIns[0].mType == ASMIT_TYA && mIns[1].IsShift() && mIns[1].mMode == ASMIM_IMPLIED) + { + NativeCodeBasicBlock* block = mEntryBlocks[0]; + int ns = block->mIns.Size(); + if (ns >= 2 && block->mIns[ns - 2].mType == ASMIT_TAY && block->mIns[ns - 1].IsSame(mIns[1])) + { + mIns[0].mType = ASMIT_NOP; mIns[0].mMode = ASMIM_IMPLIED; + mIns[1].mType = ASMIT_NOP; mIns[1].mMode = ASMIM_IMPLIED; + block->mIns[ns - 1].mLive |= LIVE_CPU_REG_A | LIVE_CPU_REG_C; + changed = true; + } + } + } + if (mIns.Size() >= 1 && mEntryBlocks.Size() == 1) + { + if (mIns[0].IsShift() && mIns[0].mMode == ASMIM_ZERO_PAGE && !(mIns[0].mLive & LIVE_MEM)) + { + NativeCodeBasicBlock* block = mEntryBlocks[0]; + int ns = block->mIns.Size(); + if (ns >= 2 && + block->mIns[ns - 2].mType == ASMIT_STA && block->mIns[ns - 2].SameEffectiveAddress(mIns[0]) && + block->mIns[ns - 1].mType == mIns[0].mType && block->mIns[ns - 1].mMode == ASMIM_IMPLIED && !(mIns[0].mLive & LIVE_MEM)) + { + mIns[0].mType = ASMIT_NOP; mIns[0].mMode = ASMIM_IMPLIED; + block->mIns[ns - 1].mLive |= LIVE_CPU_REG_A | LIVE_CPU_REG_C; + changed = true; + } + } + } +#if 1 + if (mIns.Size() >= 2) + { + int ns = mIns.Size(); + const NativeCodeInstruction& ins(mIns[ns - 2]); + + CheckLive(); + + if (ins.mType == ASMIT_STA && ins.mMode == ASMIM_ZERO_PAGE && mTrueJump && mFalseJump && !mIns[ns-1].UsesZeroPage(ins.mAddress) && mTrueJump->mEntryRequiredRegs.Size() && mFalseJump->mEntryRequiredRegs.Size()) + { + if (!mIns[ns - 1].ChangesAccu()) + { + if (mTrueJump->mEntryRequiredRegs[ins.mAddress] && !mFalseJump->mEntryRequiredRegs[ins.mAddress] && mTrueJump->mEntryBlocks.Size() == 1) + { + mIns[ns - 1].mLive |= LIVE_CPU_REG_A; + mTrueJump->mIns.Insert(0, ins); + mTrueJump->mIns[0].mLive |= LIVE_CPU_REG_C | mIns[ns - 1].mLive; + mIns.Remove(ns - 2); + mExitRequiredRegs += CPU_REG_A; + mTrueJump->mEntryRequiredRegs += CPU_REG_A; + mTrueJump->CheckLive(); + changed = true; + } + else if (mFalseJump->mEntryRequiredRegs[ins.mAddress] && !mTrueJump->mEntryRequiredRegs[ins.mAddress] && mFalseJump->mEntryBlocks.Size() == 1) + { + mIns[ns - 1].mLive |= LIVE_CPU_REG_A; + mFalseJump->mIns.Insert(0, ins); + mFalseJump->mIns[0].mLive |= LIVE_CPU_REG_C | mIns[ns - 1].mLive; + mIns.Remove(ns - 2); + mExitRequiredRegs += CPU_REG_A; + mFalseJump->mEntryRequiredRegs += CPU_REG_A; + mFalseJump->CheckLive(); + changed = true; + } + } + else if (ns >= 3 && mIns[ns - 3].mType == ASMIT_LDA && mIns[ns - 3].mMode == ASMIM_ZERO_PAGE && !(mIns[ns - 2].mLive & (LIVE_CPU_REG_X | LIVE_CPU_REG_A))) + { + if (mTrueJump->mEntryRequiredRegs[ins.mAddress] && !mFalseJump->mEntryRequiredRegs[ins.mAddress] && mTrueJump->mEntryBlocks.Size() == 1) + { + mIns[ns - 3].mType = ASMIT_LDX; mIns[ns - 3].mLive |= LIVE_CPU_REG_X; + mIns[ns - 2].mType = ASMIT_STX; + mIns[ns - 1].mLive |= LIVE_CPU_REG_X; + mTrueJump->mIns.Insert(0, ins); + mTrueJump->mIns[0].mLive |= LIVE_CPU_REG_C; + if (mTrueJump->mEntryRequiredRegs[CPU_REG_A]) + mTrueJump->mIns[0].mLive |= LIVE_CPU_REG_A; + mIns.Remove(ns - 2); + mExitRequiredRegs += CPU_REG_X; + mTrueJump->mEntryRequiredRegs += CPU_REG_X; + mTrueJump->CheckLive(); + changed = true; + } + else if (mFalseJump->mEntryRequiredRegs[ins.mAddress] && !mTrueJump->mEntryRequiredRegs[ins.mAddress] && mFalseJump->mEntryBlocks.Size() == 1) + { + mIns[ns - 3].mType = ASMIT_LDX; mIns[ns - 3].mLive |= LIVE_CPU_REG_X; + mIns[ns - 2].mType = ASMIT_STX; + mIns[ns - 1].mLive |= LIVE_CPU_REG_X; + mFalseJump->mIns.Insert(0, ins); + mFalseJump->mIns[0].mLive |= LIVE_CPU_REG_C; + if (mFalseJump->mEntryRequiredRegs[CPU_REG_A]) + mFalseJump->mIns[0].mLive |= LIVE_CPU_REG_A; + mIns.Remove(ns - 2); + mExitRequiredRegs += CPU_REG_X; + mFalseJump->mEntryRequiredRegs += CPU_REG_X; + mFalseJump->CheckLive(); + changed = true; + } + } + } + CheckLive(); + } +#endif + +#if 1 + if (mLoopHead && mIns.Size() > 0 && mIns[0].mType == ASMIT_LDA && mIns[0].mMode == ASMIM_ZERO_PAGE && mEntryBlocks.Size() == 2 && !(mIns[0].mLive & LIVE_CPU_REG_Z)) + { + NativeCodeBasicBlock* dblock = nullptr, * lblock = nullptr; + if (mEntryBlocks[0]->mFalseJump && !mEntryBlocks[1]->mFalseJump) + { + lblock = mEntryBlocks[0]; + dblock = mEntryBlocks[1]; + } + else if (mEntryBlocks[1]->mFalseJump && !mEntryBlocks[0]->mFalseJump) + { + lblock = mEntryBlocks[0]; + dblock = mEntryBlocks[1]; + } + + if (lblock && lblock != this) + { + int ls = lblock->mIns.Size(); + if (ls > 0 && lblock->mIns[ls - 1].mType == ASMIT_CMP) + ls--; + if (ls > 0 && lblock->mIns[ls - 1].IsSame(mIns[0])) + { + dblock->mIns.Push(mIns[0]); + mIns.Remove(0); + mEntryRequiredRegs += CPU_REG_A; + dblock->mExitRequiredRegs += CPU_REG_A; + lblock->mExitRequiredRegs += CPU_REG_A; + changed = true; + } + } + + + } +#endif + +#if 1 + if (mTrueJump && mFalseJump && mTrueJump->mLoopHead && mIns.Size() >= 3 && mTrueJump->mNumEntries == 2) + { + int ns = mIns.Size(); + + if (mIns[ns - 1].mType == ASMIT_CPX && mIns[ns - 2].mType == ASMIT_LDX && mIns[ns - 3].mType == ASMIT_INC && mIns[ns - 2].SameEffectiveAddress(mIns[ns - 3])) + { + NativeCodeBasicBlock* pb = mTrueJump->mEntryBlocks[0] == this ? mTrueJump->mEntryBlocks[1] : mTrueJump->mEntryBlocks[0]; + if (pb->mIns.Size() > 0) + { + int ps = pb->mIns.Size(); + if (pb->mIns[ps - 1].mType == ASMIT_STX && pb->mIns[ps - 1].SameEffectiveAddress(mIns[ns - 3])) + { + mTrueJump->mIns.Insert(0, pb->mIns[ps - 1]); + mTrueJump->mEntryRequiredRegs += CPU_REG_X; + + mFalseJump->BuildSingleExit(mProc, this); + + mFalseJump->mIns.Insert(0, pb->mIns[ps - 1]); + mFalseJump->mEntryRequiredRegs += CPU_REG_X; + + pb->mExitProvidedRegs += CPU_REG_X; + mExitProvidedRegs += CPU_REG_Y; + + ns = mIns.Size(); + + mIns[ns - 3] = mIns[ns - 2]; + mIns[ns - 2].mType = ASMIT_INX; mIns[ns - 2].mMode = ASMIM_IMPLIED; + changed = true; + + mTrueJump->CheckLive(); + mFalseJump->CheckLive(); + } + } + } + else if (mIns[ns - 1].mType == ASMIT_CPY && mIns[ns - 2].mType == ASMIT_LDY && mIns[ns - 3].mType == ASMIT_INC && mIns[ns - 2].SameEffectiveAddress(mIns[ns - 3])) + { + NativeCodeBasicBlock* pb = mTrueJump->mEntryBlocks[0] == this ? mTrueJump->mEntryBlocks[1] : mTrueJump->mEntryBlocks[0]; + if (pb->mIns.Size() > 0) + { + int ps = pb->mIns.Size(); + if (pb->mIns[ps - 1].mType == ASMIT_STY && pb->mIns[ps - 1].SameEffectiveAddress(mIns[ns - 3])) + { + mTrueJump->mIns.Insert(0, pb->mIns[ps - 1]); + mTrueJump->mEntryRequiredRegs += CPU_REG_Y; + + mFalseJump->BuildSingleExit(mProc, this); + + mFalseJump->mIns.Insert(0, pb->mIns[ps - 1]); + mFalseJump->mEntryRequiredRegs += CPU_REG_Y; + + pb->mExitProvidedRegs += CPU_REG_Y; + mExitProvidedRegs += CPU_REG_Y; + + ns = mIns.Size(); + + mIns[ns - 3] = mIns[ns - 2]; + mIns[ns - 2].mType = ASMIT_INY; mIns[ns - 2].mMode = ASMIM_IMPLIED; + changed = true; + + mTrueJump->CheckLive(); + mFalseJump->CheckLive(); + } + } + } + CheckLive(); + } +#endif + +#if 1 + if (mTrueJump && mFalseJump && !mTrueJump->mFalseJump && !mFalseJump->mFalseJump && mTrueJump->mTrueJump == mFalseJump->mTrueJump) + { + if (mTrueJump->mIns.Size() > mFalseJump->mIns.Size()) + { + if (mTrueJump->mTrueJump != mFalseJump && mFalseJump->mIns.Size() > 0) + { + int i = 0, offset = mTrueJump->mIns.Size() - mFalseJump->mIns.Size(); + while (i < mFalseJump->mIns.Size() && mFalseJump->mIns[i].IsSame(mTrueJump->mIns[i + offset])) + i++; + if (i == mFalseJump->mIns.Size()) + { + if (mTrueJump->mTrueJump) + mTrueJump->mTrueJump->RemEntryBlock(mTrueJump); + mTrueJump->mTrueJump = mFalseJump; + if (mTrueJump->mTrueJump) + mTrueJump->mTrueJump->AddEntryBlock(mTrueJump); + mTrueJump->mIns.SetSize(offset); + + changed = true; + } + } + } + else + { + if (mFalseJump->mTrueJump != mTrueJump && mTrueJump->mIns.Size() > 0) + { + int i = 0, offset = mFalseJump->mIns.Size() - mTrueJump->mIns.Size(); + while (i < mTrueJump->mIns.Size() && mTrueJump->mIns[i].IsSame(mFalseJump->mIns[i + offset])) + i++; + if (i == mTrueJump->mIns.Size()) + { + if (mFalseJump->mTrueJump) + mFalseJump->mTrueJump->RemEntryBlock(mFalseJump); + mFalseJump->mTrueJump = mTrueJump; + if (mFalseJump->mTrueJump) + mFalseJump->mTrueJump->AddEntryBlock(mFalseJump); + mFalseJump->mIns.SetSize(offset); + changed = true; + } + } + } + } +#endif +#if 1 + if (mTrueJump && mTrueJump->mNumEntries == 1 && mFalseJump && !mTrueJump->mFalseJump && mTrueJump->mTrueJump == mFalseJump) + { + int s = mIns.Size(), ts = mTrueJump->mIns.Size(); + if (s > 1 && ts > 0) + { + if (mIns[s - 2].mType == ASMIT_STA && mIns[s - 2].mMode == ASMIM_ZERO_PAGE && + mTrueJump->mIns[ts - 1].mType == ASMIT_STA && mTrueJump->mIns[ts - 1].mMode == ASMIM_ZERO_PAGE && mTrueJump->mIns[ts - 1].mAddress == mIns[s - 2].mAddress) + { + } + } + } + if (mFalseJump && mFalseJump->mNumEntries == 1 && mTrueJump && !mFalseJump->mFalseJump && mFalseJump->mTrueJump == mTrueJump) + { + mTrueJump->CheckLive(); + mFalseJump->CheckLive(); + + int s = mIns.Size(), ts = mFalseJump->mIns.Size(); + if (s > 1 && ts > 0) + { + if (mIns[s - 2].mType == ASMIT_STA && mIns[s - 2].mMode == ASMIM_ZERO_PAGE && + mFalseJump->mIns[ts - 1].mType == ASMIT_STA && mFalseJump->mIns[ts - 1].mMode == ASMIM_ZERO_PAGE && mFalseJump->mIns[ts - 1].mAddress == mIns[s - 2].mAddress && + mFalseJump->mIns[0].mType == ASMIT_LDA && mFalseJump->mIns[0].mMode == ASMIM_ZERO_PAGE && mFalseJump->mIns[0].mAddress == mIns[s - 2].mAddress) + { + if (!(mIns[s - 1].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Y)) && HasAsmInstructionMode(ASMIT_LDY, mIns[s - 1].mMode)) + { + mIns[s - 1].mType = ASMIT_LDY; + mTrueJump->mIns.Insert(0, mIns[s - 2]); + mIns.Remove(s - 2); + mFalseJump->mIns.Remove(ts - 1); + mFalseJump->mIns.Remove(0); + mExitRequiredRegs += CPU_REG_A; + mFalseJump->mExitRequiredRegs += CPU_REG_A; + mFalseJump->mEntryRequiredRegs += CPU_REG_A; + mTrueJump->mEntryProvidedRegs += CPU_REG_A; + mTrueJump->mEntryRequiredRegs += CPU_REG_A; + changed = true; + + mTrueJump->CheckLive(); + mFalseJump->CheckLive(); + } + else if (!(mIns[s - 1].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_X)) && HasAsmInstructionMode(ASMIT_LDX, mIns[s - 1].mMode)) + { + mIns[s - 1].mType = ASMIT_LDX; + mTrueJump->mIns.Insert(0, mIns[s - 2]); + mIns.Remove(s - 2); + mFalseJump->mIns.Remove(ts - 1); + mFalseJump->mIns.Remove(0); + mExitRequiredRegs += CPU_REG_A; + mFalseJump->mExitRequiredRegs += CPU_REG_A; + mFalseJump->mEntryRequiredRegs += CPU_REG_A; + mTrueJump->mEntryProvidedRegs += CPU_REG_A; + mTrueJump->mEntryRequiredRegs += CPU_REG_A; + changed = true; + + mTrueJump->CheckLive(); + mFalseJump->CheckLive(); + } + } + } + + } +#endif +#if 1 + if (mTrueJump && mTrueJump->mNumEntries == 1 && mFalseJump && mFalseJump->mNumEntries == 1) + { + int s = mIns.Size(); + if (s > 0 && mIns[s - 1].mType == ASMIT_CMP && mIns[s - 1].mMode == ASMIM_IMMEDIATE && !(mIns[s - 1].mLive & LIVE_CPU_REG_X)) + { + while (mTrueJump->mIns.Size() > 1 && mFalseJump->mIns.Size() > 1 && + + ((mTrueJump->mIns[0].mType == ASMIT_LDA && mTrueJump->mIns[1].mType == ASMIT_STA && !(mTrueJump->mIns[1].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z))) || + (mTrueJump->mIns[0].mType == ASMIT_LDX && mTrueJump->mIns[1].mType == ASMIT_STX && !(mTrueJump->mIns[1].mLive & (LIVE_CPU_REG_X | LIVE_CPU_REG_Z)))) && + ((mFalseJump->mIns[0].mType == ASMIT_LDA && mFalseJump->mIns[1].mType == ASMIT_STA && !(mFalseJump->mIns[1].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z))) || + (mFalseJump->mIns[0].mType == ASMIT_LDX && mFalseJump->mIns[1].mType == ASMIT_STX && !(mFalseJump->mIns[1].mLive & (LIVE_CPU_REG_X | LIVE_CPU_REG_Z)))) && + mTrueJump->mIns[0].SameEffectiveAddress(mFalseJump->mIns[0]) && mTrueJump->mIns[1].SameEffectiveAddress(mFalseJump->mIns[1]) && + HasAsmInstructionMode(ASMIT_LDX, mTrueJump->mIns[0].mMode) && HasAsmInstructionMode(ASMIT_STX, mTrueJump->mIns[1].mMode)) + { + uint32 live = mIns[s - 1].mLive | LIVE_CPU_REG_A; + if (mIns[s - 1].RequiresYReg()) + live |= LIVE_CPU_REG_Y; + if (s >= 2) + live |= mIns[s - 2].mLive; + + mTrueJump->mIns[0].mType = ASMIT_LDX; + mTrueJump->mIns[0].mLive |= LIVE_CPU_REG_X | live; + mTrueJump->mIns[1].mType = ASMIT_STX; + mTrueJump->mIns[1].mLive |= live; + + mIns.Insert(s - 1, mTrueJump->mIns[0]); + mIns.Insert(s, mTrueJump->mIns[1]); + s += 2; + mTrueJump->mIns.Remove(0); mTrueJump->mIns.Remove(0); + mFalseJump->mIns.Remove(0); mFalseJump->mIns.Remove(0); + + CheckLive(); + mTrueJump->CheckLive(); + mFalseJump->CheckLive(); + + changed = true; + } + } + else if (s > 0 && mIns[s - 1].mType == ASMIT_LDA && !(mIns[s - 1].mLive & LIVE_CPU_REG_X)) + { + while (mTrueJump->mIns.Size() > 1 && mFalseJump->mIns.Size() > 1 && + + ((mTrueJump->mIns[0].mType == ASMIT_LDA && mTrueJump->mIns[1].mType == ASMIT_STA && !(mTrueJump->mIns[1].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z))) || + (mTrueJump->mIns[0].mType == ASMIT_LDX && mTrueJump->mIns[1].mType == ASMIT_STX && !(mTrueJump->mIns[1].mLive & (LIVE_CPU_REG_X | LIVE_CPU_REG_Z)))) && + ((mFalseJump->mIns[0].mType == ASMIT_LDA && mFalseJump->mIns[1].mType == ASMIT_STA && !(mFalseJump->mIns[1].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z))) || + (mFalseJump->mIns[0].mType == ASMIT_LDX && mFalseJump->mIns[1].mType == ASMIT_STX && !(mFalseJump->mIns[1].mLive & (LIVE_CPU_REG_X | LIVE_CPU_REG_Z)))) && + mTrueJump->mIns[0].SameEffectiveAddress(mFalseJump->mIns[0]) && mTrueJump->mIns[1].SameEffectiveAddress(mFalseJump->mIns[1]) && + HasAsmInstructionMode(ASMIT_LDX, mTrueJump->mIns[0].mMode) && HasAsmInstructionMode(ASMIT_STX, mTrueJump->mIns[1].mMode) && + !mIns[s - 1].MayBeChangedOnAddress(mTrueJump->mIns[1])) + { + uint32 live = mIns[s - 1].mLive; + if (mIns[s - 1].RequiresYReg()) + live |= LIVE_CPU_REG_Y; + if (s >= 2) + live |= mIns[s - 2].mLive; + + mTrueJump->mIns[0].mType = ASMIT_LDX; + mTrueJump->mIns[0].mLive |= LIVE_CPU_REG_X | live; + mTrueJump->mIns[1].mType = ASMIT_STX; + mTrueJump->mIns[1].mLive |= live; + + + mIns.Insert(s - 1, mTrueJump->mIns[0]); + mIns.Insert(s, mTrueJump->mIns[1]); + s += 2; + mTrueJump->mIns.Remove(0); mTrueJump->mIns.Remove(0); + mFalseJump->mIns.Remove(0); mFalseJump->mIns.Remove(0); + + CheckLive(); + mTrueJump->CheckLive(); + mFalseJump->CheckLive(); + + changed = true; + } + } + } +#endif + + if (mEntryBlocks.Size() == 1 && mIns.Size() >= 3) + { + NativeCodeBasicBlock* eblock = mEntryBlocks[0]; + int es = eblock->mIns.Size(); + while (es > 0 && !eblock->mIns[es - 1].ChangesAccu()) + es--; + if (es > 0 && eblock->mIns[es - 1].mType == ASMIT_LDA && (eblock->mIns[es - 1].mMode == ASMIM_ZERO_PAGE || eblock->mIns[es - 1].mMode == ASMIM_ABSOLUTE)) + { + int i = es; + es--; + while (i < eblock->mIns.Size() && !eblock->mIns[es].MayBeChangedOnAddress(eblock->mIns[i])) + i++; + + if (i == eblock->mIns.Size()) + { + if (mIns[2].mType == ASMIT_STA && mIns[2].SameEffectiveAddress(eblock->mIns[es]) && !(mIns[2].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C))) + { + if (mIns[0].mType == ASMIT_SEC && mIns[1].mType == ASMIT_SBC && mIns[1].mMode == ASMIM_IMMEDIATE && mIns[1].mAddress == 1) + { + mIns[0].mType = ASMIT_NOP; + mIns[1].mType = ASMIT_NOP; mIns[1].mMode = ASMIM_IMPLIED; + mIns[2].mType = ASMIT_DEC; + changed = true; + } + else if (mIns[0].mType == ASMIT_CLC && mIns[1].mType == ASMIT_ADC && mIns[1].mMode == ASMIM_IMMEDIATE && mIns[1].mAddress == 1) + { + mIns[0].mType = ASMIT_NOP; + mIns[1].mType = ASMIT_NOP; mIns[1].mMode = ASMIM_IMPLIED; + mIns[2].mType = ASMIT_INC; + changed = true; + } + } + } + } + } + + if (mEntryBlocks.Size() == 1 && mIns.Size() > 0) + { + if (mIns[0].mType == ASMIT_TAY && !(mIns[0].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z))) + { + NativeCodeBasicBlock* pblock = mEntryBlocks[0]; + if (pblock->mTrueJump && pblock->mFalseJump && pblock->mIns.Size() > 0 && !pblock->mExitRequiredRegs[CPU_REG_Y]) + { + if (this == pblock->mTrueJump && !pblock->mFalseJump->mEntryRequiredRegs[CPU_REG_A] || + this == pblock->mFalseJump && !pblock->mTrueJump->mEntryRequiredRegs[CPU_REG_A]) + { + int si = pblock->mIns.Size(); + if (pblock->mIns[si - 1].mType == ASMIT_CMP && HasAsmInstructionMode(ASMIT_CPY, pblock->mIns[si - 1].mMode)) + si--; + + if (si > 0 && pblock->mIns[si - 1].mType == ASMIT_LDA && HasAsmInstructionMode(ASMIT_LDY, pblock->mIns[si - 1].mMode)) + { + pblock->mIns[si - 1].mType = ASMIT_LDY; + pblock->mIns[si - 1].mLive |= LIVE_CPU_REG_Y; + if (si != pblock->mIns.Size()) + { + pblock->mIns[si].mType = ASMIT_CPY; + pblock->mIns[si].mLive |= LIVE_CPU_REG_Y; + } + pblock->mExitRequiredRegs += CPU_REG_Y; + mEntryRequiredRegs += CPU_REG_Y; + mIns[0].mType = ASMIT_NOP; + changed = true; + } + } + } + } + } + +#if 1 + if (mEntryBlocks.Size() > 1 && mIns.Size() > 0) + { + int fi = 0; + while (fi < mIns.Size() && !mIns[fi].ReferencesYReg()) + fi++; + + if (fi < mIns.Size() && mIns[fi].mType == ASMIT_LDY && mIns[fi].mMode == ASMIM_ZERO_PAGE && !(mIns[fi].mLive & LIVE_CPU_REG_Z) && !ChangesZeroPage(mIns[fi].mAddress, 0, fi)) + { + int ei = 0; + int index; + bool found = false, fail = false; + NativeCodeBasicBlock* block; + + for (int i = 0; i < mEntryBlocks.Size(); i++) + { + if (mEntryBlocks[i]->IsExitYRegZP(mIns[fi].mAddress, index, block)) + found = true; + else if (mEntryBlocks[i]->mFalseJump) + fail = true; + } + + if (found && !fail) + { + for (int i = 0; i < fi; i++) + mIns[i].mLive |= LIVE_CPU_REG_Y; + + for (int i = 0; i < mEntryBlocks.Size(); i++) + { + if (mEntryBlocks[i]->IsExitYRegZP(mIns[fi].mAddress, index, block)) + { + mEntryBlocks[i]->MarkLiveBlockChain(index, block, LIVE_CPU_REG_Y, CPU_REG_Y); + } + else + { + mEntryBlocks[i]->mIns.Push(mIns[fi]); + mEntryBlocks[i]->mExitRequiredRegs += CPU_REG_Y; + } + } + + mEntryRequiredRegs += CPU_REG_Y; + mIns.Remove(fi); + changed = true; + } + } + + fi = 0; + while (fi < mIns.Size() && !mIns[fi].ReferencesXReg()) + fi++; + + if (fi < mIns.Size() && mIns[fi].mType == ASMIT_LDX && mIns[fi].mMode == ASMIM_ZERO_PAGE && !(mIns[fi].mLive & LIVE_CPU_REG_Z) && !ChangesZeroPage(mIns[fi].mAddress, 0, fi)) + { + int ei = 0; + int index; + bool found = false, fail = false; + NativeCodeBasicBlock* block; + + for (int i = 0; i < mEntryBlocks.Size(); i++) + { + if (mEntryBlocks[i]->IsExitXRegZP(mIns[fi].mAddress, index, block)) + found = true; + else if (mEntryBlocks[i]->mFalseJump) + fail = true; + } + + if (found && !fail) + { + for (int i = 0; i < fi; i++) + mIns[i].mLive |= LIVE_CPU_REG_X; + + for (int i = 0; i < mEntryBlocks.Size(); i++) + { + if (mEntryBlocks[i]->IsExitXRegZP(mIns[fi].mAddress, index, block)) + { + mEntryBlocks[i]->MarkLiveBlockChain(index, block, LIVE_CPU_REG_X, CPU_REG_X); + } + else + { + mEntryBlocks[i]->mIns.Push(mIns[fi]); + mEntryBlocks[i]->mExitRequiredRegs += CPU_REG_X; + mEntryBlocks[i]->mExitRequiredRegs -= CPU_REG_Z; + } + } + + mEntryRequiredRegs -= CPU_REG_Z; + mEntryRequiredRegs += CPU_REG_X; + mIns.Remove(fi); + changed = true; + } + } + + fi = 0; + while (fi < mIns.Size() && !mIns[fi].ReferencesAccu()) + fi++; + + if (fi < mIns.Size() && mIns[fi].mType == ASMIT_LDA && mIns[fi].mMode == ASMIM_ZERO_PAGE && !(mIns[fi].mLive & LIVE_CPU_REG_Z) && !ChangesZeroPage(mIns[fi].mAddress, 0, fi)) + { + int ei = 0; + int index; + bool found = false, fail = false; + NativeCodeBasicBlock* block; + + for (int i = 0; i < mEntryBlocks.Size(); i++) + { + if (mEntryBlocks[i]->IsExitARegZP(mIns[fi].mAddress, index, block)) + found = true; + else if (mEntryBlocks[i]->mFalseJump) + fail = true; + } + + if (found && !fail) + { + for (int i = 0; i < fi; i++) + mIns[i].mLive |= LIVE_CPU_REG_A; + + for (int i = 0; i < mEntryBlocks.Size(); i++) + { + if (mEntryBlocks[i]->IsExitARegZP(mIns[fi].mAddress, index, block)) + { + mEntryBlocks[i]->MarkLiveBlockChain(index, block, LIVE_CPU_REG_A, CPU_REG_A); + } + else + { + mEntryBlocks[i]->mIns.Push(mIns[fi]); + if (mEntryBlocks[i]->mExitRequiredRegs[CPU_REG_X]) + mEntryBlocks[i]->mIns[mEntryBlocks[i]->mIns.Size() - 1].mLive |= LIVE_CPU_REG_X; + if (mEntryBlocks[i]->mExitRequiredRegs[CPU_REG_Y]) + mEntryBlocks[i]->mIns[mEntryBlocks[i]->mIns.Size() - 1].mLive |= LIVE_CPU_REG_Y; + mEntryBlocks[i]->mExitRequiredRegs += CPU_REG_A; + } + } + + mEntryRequiredRegs += CPU_REG_A; + mIns.Remove(fi); + changed = true; + } + } + } +#endif + +#if 1 + if (mEntryBlocks.Size() > 1 && mIns.Size() > 0) + { + if (mIns[0].mType == ASMIT_STA) + { + int ts; + if ((ts = mEntryBlocks[0]->mIns.Size()) >= 2 && + mEntryBlocks[0]->mIns[ts - 2].mType == ASMIT_LDA && + mEntryBlocks[0]->mIns[ts - 2].mMode == ASMIM_ZERO_PAGE && + mEntryBlocks[0]->mIns[ts - 1].mType == ASMIT_ADC && + !mEntryBlocks[0]->mFalseJump) + { + int addr = mEntryBlocks[0]->mIns[ts - 2].mAddress; + + int i = 1; + while (i < mEntryBlocks.Size() && + (ts = mEntryBlocks[i]->mIns.Size()) >= 2 && + mEntryBlocks[i]->mIns[ts - 2].mType == ASMIT_LDA && + mEntryBlocks[i]->mIns[ts - 2].mMode == ASMIM_ZERO_PAGE && + mEntryBlocks[i]->mIns[ts - 2].mAddress == addr && + mEntryBlocks[i]->mIns[ts - 1].mType == ASMIT_ADC && + !mEntryBlocks[i]->mFalseJump) + i++; + if (i == mEntryBlocks.Size()) + { + ts = mEntryBlocks[0]->mIns.Size(); + mIns.Insert(0, NativeCodeInstruction(mEntryBlocks[0]->mIns[ts - 2].mIns, ASMIT_ADC, mEntryBlocks[0]->mIns[ts - 2])); + + for (int i = 0; i < mEntryBlocks.Size(); i++) + { + ts = mEntryBlocks[i]->mIns.Size(); + mEntryBlocks[i]->mIns[ts - 1].mType = ASMIT_LDA; + mEntryBlocks[i]->mIns[ts - 1].mLive |= LIVE_CPU_REG_C; + mEntryBlocks[i]->mIns[ts - 2].mType = ASMIT_NOP; + mEntryBlocks[i]->mIns[ts - 2].mMode = ASMIM_IMPLIED; + mEntryBlocks[i]->mExitRequiredRegs += addr; + mEntryBlocks[i]->mExitRequiredRegs += CPU_REG_C; + } + mEntryRequiredRegs += addr; + mEntryRequiredRegs += CPU_REG_C; + changed = true; + } + } + } + } +#endif +#if 0 + if (mFalseJump && mIns.Size() > 1) + { + int sz = mIns.Size(); + if (mIns[sz - 1].mType == ASMIT_AND && mIns[sz - 1].mMode == ASMIM_IMMEDIATE && mIns[sz - 1].mAddress == 0x80) + { + if (mBranch == ASMIT_BEQ || mBranch == ASMIT_BPL) + { + if (mTrueJump->mIns.Size() > 0 && mTrueJump->mIns[0].mType == ASMIT_AND && mTrueJump->mEntryBlocks.Size() == 2) + { + NativeCodeBasicBlock* pblock = mTrueJump->mEntryBlocks[0]; + if (pblock == this) + pblock = mTrueJump->mEntryBlocks[1]; + if (!pblock->mFalseJump) + { + pblock->mIns.Push(mTrueJump->mIns[0]); + mTrueJump->mIns.Remove(0); + changed = true; + } + } + } + } + } +#endif + + if (mFalseJump && !mFalseJump->mFalseJump && mTrueJump == mFalseJump->mTrueJump && mTrueJump->mNumEntries == 2 && mTrueJump->mIns.Size() > 0 && mIns.Size() > 0) + { + int sz = mIns.Size(); + + if (mTrueJump->mIns[0].mType == ASMIT_TAX && !mFalseJump->ReferencesXReg() && !mFalseJump->ReferencesAccu() && !(mTrueJump->mIns[0].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z))) + { + if (mIns[sz - 1].mType == ASMIT_LDA && HasAsmInstructionMode(ASMIT_LDX, mIns[sz - 1].mMode)) + { + mIns[sz - 1].mType = ASMIT_LDX; + mIns[sz - 1].mLive |= LIVE_CPU_REG_X; + mTrueJump->mIns[0].mType = ASMIT_NOP; + mExitRequiredRegs += CPU_REG_X; + mTrueJump->mEntryRequiredRegs += CPU_REG_X; + mFalseJump->mEntryRequiredRegs += CPU_REG_X; + mFalseJump->mExitRequiredRegs += CPU_REG_X; + for (int i = 0; i < mFalseJump->mIns.Size(); i++) + mFalseJump->mIns[i].mLive |= LIVE_CPU_REG_X; + changed = true; + } + } + else if (mTrueJump->mIns[0].mType == ASMIT_TAY && !mFalseJump->ReferencesYReg() && !mFalseJump->ReferencesAccu() && !(mTrueJump->mIns[0].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z))) + { + if (mIns[sz - 1].mType == ASMIT_LDA && HasAsmInstructionMode(ASMIT_LDY, mIns[sz - 1].mMode)) + { + mIns[sz - 1].mType = ASMIT_LDY; + mIns[sz - 1].mLive |= LIVE_CPU_REG_Y; + mTrueJump->mIns[0].mType = ASMIT_NOP; + mExitRequiredRegs += CPU_REG_Y; + mTrueJump->mEntryRequiredRegs += CPU_REG_Y; + mFalseJump->mEntryRequiredRegs += CPU_REG_Y; + mFalseJump->mExitRequiredRegs += CPU_REG_Y; + for (int i = 0; i < mFalseJump->mIns.Size(); i++) + mFalseJump->mIns[i].mLive |= LIVE_CPU_REG_Y; + changed = true; + } + } + } + + if (mEntryBlocks.Size() == 1 && mIns.Size() > 0 && mIns[0].mType == ASMIT_TAX && !(mIns[0].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z))) + { + NativeCodeBasicBlock* pblock = mEntryBlocks[0]; + NativeCodeBasicBlock* nblock = pblock->mTrueJump == this ? pblock->mFalseJump : pblock->mTrueJump; + if (!nblock || (!nblock->mEntryRequiredRegs[CPU_REG_A] && !nblock->mEntryRequiredRegs[CPU_REG_X])) + { + int ps = pblock->mIns.Size(); + if (ps > 0 && pblock->mIns[ps - 1].mType == ASMIT_LDA && HasAsmInstructionMode(ASMIT_LDX, pblock->mIns[ps - 1].mMode)) + { + pblock->mIns[ps - 1].mType = ASMIT_LDX; + pblock->mIns[ps - 1].mLive |= LIVE_CPU_REG_X; + pblock->mExitRequiredRegs += CPU_REG_X; + mEntryRequiredRegs += CPU_REG_X; + mIns[0].mType = ASMIT_NOP; + changed = true; + } + } + } + + if (mFalseJump && !mFalseJump->mFalseJump && mFalseJump->mTrueJump == mTrueJump && mFalseJump->mNumEntries == 1 && mTrueJump->mNumEntries == 2) + { + int sz = mIns.Size(); + if (sz > 0 && mIns[sz - 1].mType == ASMIT_LDA && mIns[sz - 1].mMode == ASMIM_IMMEDIATE && mIns[sz - 1].mAddress == 0 && + mFalseJump->mIns.Size() == 1 && mFalseJump->mIns[0].mType == ASMIT_LDA && mFalseJump->mIns[0].mMode == ASMIM_IMMEDIATE && + mTrueJump->mIns.Size() >= 1 && mTrueJump->mIns[0].mType == ASMIT_EOR) + { + mIns[sz - 1].CopyMode(mTrueJump->mIns[0]); + mFalseJump->mEntryRequiredRegs += CPU_REG_A; + mFalseJump->mIns[0].mType = ASMIT_EOR; + mTrueJump->mIns[0].mType = ASMIT_NOP; mTrueJump->mIns[0].mMode = ASMIM_IMPLIED; + changed = true; + } + } + + if (mEntryBlocks.Size() > 1 && !mEntryRequiredRegs[CPU_REG_A]) + { + int ia = 0; + while (ia < mIns.Size() && !mIns[ia].ChangesAccu()) + ia++; + if (ia < mIns.Size() && mIns[ia].mType == ASMIT_LDA && mIns[ia].mMode == ASMIM_ZERO_PAGE && !(mIns[ia].mLive & LIVE_CPU_REG_Z) && !ChangesZeroPage(mIns[ia].mAddress, 0, ia)) + { + int j = 0; + while (j < mEntryBlocks.Size() && mEntryBlocks[j]->HasTailAccuReg(mIns[ia].mAddress)) + j++; + if (j == mEntryBlocks.Size()) + { + for (int i = 0; i < ia; i++) + mIns[i].mLive |= LIVE_CPU_REG_A; + mIns.Remove(ia); + mEntryRequiredRegs += CPU_REG_A; + for (int j = 0; j < mEntryBlocks.Size(); j++) + { + NativeCodeBasicBlock* eblock = mEntryBlocks[j]; + eblock->mExitRequiredRegs += CPU_REG_A; + int k = eblock->mIns.Size() - 1; + while (k >= 0 && !(eblock->mIns[k].mLive & LIVE_CPU_REG_A)) + { + eblock->mIns[k].mLive |= LIVE_CPU_REG_A; + k--; + } + } + changed = true; + } + } + } + + CheckLive(); + mProc->CheckBlocks(); + + if (mTrueJump && mTrueJump->JoinTailCodeSequences(loops)) + changed = true; + if (mFalseJump && mFalseJump->JoinTailCodeSequences(loops)) + changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::MayBeMovedBeforeBlock(int at) +{ + int i = at; + while (i > 0) + { + i--; + if (!mIns[at].MayBeMovedBefore(mIns[i])) + return false; + } + + return true; +} + +bool NativeCodeBasicBlock::MayBeMovedBeforeBlock(int at, const NativeCodeInstruction& ins) +{ + int i = at; + while (i > 0) + { + i--; + if (!ins.MayBeMovedBefore(mIns[i])) + return false; + } + + return true; +} + +bool NativeCodeBasicBlock::SafeInjectSequenceFromBack(NativeCodeBasicBlock* block, int start, int end) +{ + uint32 changes = 0; + uint32 requires = 0; + + bool hasvolatile = false; + + for (int i = start; i < end; i++) + { + const NativeCodeInstruction& ins(block->mIns[i]); + if (ins.RequiresAccu() && !(changes & LIVE_CPU_REG_A)) requires |= LIVE_CPU_REG_A; + if (ins.RequiresXReg() && !(changes & LIVE_CPU_REG_X)) requires |= LIVE_CPU_REG_X; + if (ins.RequiresYReg() && !(changes & LIVE_CPU_REG_Y)) requires |= LIVE_CPU_REG_Y; + if (ins.RequiresCarry() && !(changes & LIVE_CPU_REG_C)) requires |= LIVE_CPU_REG_C; + if (ins.ChangesAccu()) changes |= LIVE_CPU_REG_A; + if (ins.ChangesXReg()) changes |= LIVE_CPU_REG_X; + if (ins.ChangesYReg()) changes |= LIVE_CPU_REG_Y; + if (ins.ChangesCarry()) changes |= LIVE_CPU_REG_C; + if (ins.ChangesZFlag()) changes |= LIVE_CPU_REG_Z; + + if (ins.mFlags & NCIF_VOLATILE) hasvolatile = true; + } + + uint32 live = 0; + if (mBranch == ASMIT_BCC || mBranch == ASMIT_BCS) + live |= LIVE_CPU_REG_C; + if (mBranch == ASMIT_BEQ || mBranch == ASMIT_BNE || mBranch == ASMIT_BMI || mBranch == ASMIT_BPL) + live |= LIVE_CPU_REG_Z; + if (mExitRequiredRegs[CPU_REG_A]) + live |= LIVE_CPU_REG_A; + if (mExitRequiredRegs[CPU_REG_X]) + live |= LIVE_CPU_REG_X; + if (mExitRequiredRegs[CPU_REG_Y]) + live |= LIVE_CPU_REG_Y; + if (mExitRequiredRegs[CPU_REG_C]) + live |= LIVE_CPU_REG_C; + + int at = mIns.Size(); + while (at > 0 && (live & changes)) + { + at--; + const NativeCodeInstruction& ins(mIns[at]); + if (ins.ChangesAccu()) + { + if (requires & LIVE_CPU_REG_A) + return false; + live &= ~LIVE_CPU_REG_A; + } + if (ins.ChangesXReg()) + { + if (requires & LIVE_CPU_REG_X) + return false; + live &= ~LIVE_CPU_REG_X; + } + if (ins.ChangesYReg()) + { + if (requires & LIVE_CPU_REG_Y) + return false; + live &= ~LIVE_CPU_REG_Y; + } + if (ins.ChangesCarry()) + { + if (requires & LIVE_CPU_REG_C) + return false; + live &= ~LIVE_CPU_REG_C; + } + if (ins.ChangesZFlag()) live &= ~LIVE_CPU_REG_Z; + + if (ins.RequiresAccu()) + live |= LIVE_CPU_REG_A; + if (ins.RequiresXReg()) + live |= LIVE_CPU_REG_X; + if (ins.RequiresYReg()) + live |= LIVE_CPU_REG_Y; + if (ins.RequiresCarry()) + live |= LIVE_CPU_REG_C; + + if (hasvolatile && (ins.mFlags & NCIF_VOLATILE)) return false; + } + + if (!(live & changes)) + { + for (int i = start; i < end; i++) + { + for (int j = at; j < mIns.Size(); j++) + { + if (block->mIns[i].MayBeChangedOnAddress(mIns[j]) || mIns[j].MayBeChangedOnAddress(block->mIns[i])) + return false; + } + } + + for (int i = start; i < end; i++) + { + mIns.Insert(at, block->mIns[i]); + mIns[at].mLive |= live; + at++; + } + return true; + } + + return false; +} + +bool NativeCodeBasicBlock::MayBeMovedBeforeBlock(int start, int end) +{ + uint32 changes = 0; + uint32 requires = 0; + + if (start == 0) + return true; + + bool hasvolatile = false; + + for (int i = start; i < end; i++) + { + const NativeCodeInstruction& ins(mIns[i]); + if (ins.RequiresAccu() && !(changes & LIVE_CPU_REG_A)) requires |= LIVE_CPU_REG_A; + if (ins.RequiresXReg() && !(changes & LIVE_CPU_REG_X)) requires |= LIVE_CPU_REG_X; + if (ins.RequiresYReg() && !(changes & LIVE_CPU_REG_Y)) requires |= LIVE_CPU_REG_Y; + if (ins.RequiresCarry() && !(changes & LIVE_CPU_REG_C)) requires |= LIVE_CPU_REG_C; + if (ins.ChangesAccu()) changes |= LIVE_CPU_REG_A; + if (ins.ChangesXReg()) changes |= LIVE_CPU_REG_X; + if (ins.ChangesYReg()) changes |= LIVE_CPU_REG_Y; + if (ins.ChangesCarry()) changes |= LIVE_CPU_REG_C; + if (ins.ChangesZFlag()) changes |= LIVE_CPU_REG_Z; + + if (ins.mFlags & NCIF_VOLATILE) hasvolatile = true; + } + + for (int i = 0; i < start; i++) + { + const NativeCodeInstruction& ins(mIns[i]); + if (ins.ChangesAccu() && (requires & LIVE_CPU_REG_A)) + return false; + if (ins.ChangesXReg() && (requires & LIVE_CPU_REG_X)) + return false; + if (ins.ChangesYReg() && (requires & LIVE_CPU_REG_Y)) + return false; + if (ins.ChangesCarry() && (requires & LIVE_CPU_REG_C)) + return false; + + if (hasvolatile && (ins.mFlags & NCIF_VOLATILE)) return false; + + for (int j = start; j < end; j++) + { + if (mIns[i].MayBeChangedOnAddress(mIns[j]) || mIns[j].MayBeChangedOnAddress(mIns[i]) || + mIns[i].MayReference(mIns[j]) || mIns[j].MayReference(mIns[i])) + return false; + } + } + + return true; +} + +bool NativeCodeBasicBlock::JoinConditionSequence(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + if (mIns.Size() == 2 && mFalseJump && (mBranch == ASMIT_BEQ || mBranch == ASMIT_BNE)) + { + if (mIns[0].mType == ASMIT_LDA && mIns[0].mMode == ASMIM_ZERO_PAGE && + mIns[1].mType == ASMIT_AND && mIns[0].mMode == ASMIM_ZERO_PAGE && !(mIns[1].mLive & LIVE_CPU_REG_A)) + { + NativeCodeBasicBlock* zblock; + if (mBranch == ASMIT_BEQ) + zblock = mTrueJump; + else + zblock = mFalseJump; + + int i = 0; + while (i < mEntryBlocks.Size()) + { + NativeCodeBasicBlock* eblock = mEntryBlocks[i]; + int zp0 = eblock->RetrieveZPValue(mIns[0].mAddress, eblock->mIns.Size() - 1); + int zp1 = eblock->RetrieveZPValue(mIns[1].mAddress, eblock->mIns.Size() - 1); + + if (zp0 == 0 || zp1 == 0) + { + if (eblock->mTrueJump == this) + { + mNumEntries--; + mEntryBlocks.Remove(i); + eblock->mTrueJump = zblock; + zblock->mNumEntries++; + zblock->mEntryBlocks.Push(eblock); + } + if (eblock->mFalseJump == this) + { + mNumEntries--; + mEntryBlocks.Remove(i); + eblock->mFalseJump = zblock; + zblock->mNumEntries++; + zblock->mEntryBlocks.Push(eblock); + } + + changed = true; + } + else + i++; + } + } + } + + int sz = mIns.Size(); + if (sz > 0 && mIns[sz - 1].mType == ASMIT_BIT && mFalseJump && !(mIns[sz - 1].mLive & LIVE_CPU_REG_A)) + { + if (mBranch == ASMIT_BPL && mTrueJump == mFalseJump->mTrueJump && mFalseJump->mNumEntries == 1 && !mFalseJump->mFalseJump) + { + if (mFalseJump->mIns.Size() == 3 && + mFalseJump->mIns[0].mType == ASMIT_LDA && + mFalseJump->mIns[1].mType == ASMIT_ORA && mFalseJump->mIns[1].mMode == ASMIM_IMMEDIATE && mFalseJump->mIns[1].mAddress == 0x80 && + mFalseJump->mIns[2].mType == ASMIT_STA && mFalseJump->mIns[2].SameEffectiveAddress(mFalseJump->mIns[0]) && + !(mFalseJump->mIns[2].mLive & LIVE_CPU_REG_A)) + { + mIns[sz - 1].mType = ASMIT_LDA; mIns[sz - 1].mLive |= LIVE_CPU_REG_A; + mIns.Push(NativeCodeInstruction(mFalseJump->mIns[0].mIns, ASMIT_AND, ASMIM_IMMEDIATE, 0x80)); + mIns.Push(NativeCodeInstruction(mFalseJump->mIns[0].mIns, ASMIT_ORA, mFalseJump->mIns[0])); + mIns.Push(NativeCodeInstruction(mFalseJump->mIns[0].mIns, ASMIT_STA, mFalseJump->mIns[0])); + mBranch = ASMIT_JMP; + mFalseJump = nullptr; + changed = true; + } + } + else if (mBranch == ASMIT_BMI && mFalseJump == mTrueJump->mTrueJump && mTrueJump->mNumEntries == 1 && !mTrueJump->mFalseJump) + { + if (mTrueJump->mIns.Size() == 3 && + mTrueJump->mIns[0].mType == ASMIT_LDA && + mTrueJump->mIns[1].mType == ASMIT_ORA && mTrueJump->mIns[1].mMode == ASMIM_IMMEDIATE && mTrueJump->mIns[1].mAddress == 0x80 && + mTrueJump->mIns[2].mType == ASMIT_STA && mTrueJump->mIns[2].SameEffectiveAddress(mTrueJump->mIns[0]) && + !(mTrueJump->mIns[2].mLive & LIVE_CPU_REG_A)) + { + mIns[sz - 1].mType = ASMIT_LDA; mIns[sz - 1].mLive |= LIVE_CPU_REG_A; + mIns.Push(NativeCodeInstruction(mTrueJump->mIns[0].mIns, ASMIT_AND, ASMIM_IMMEDIATE, 0x80)); + mIns.Push(NativeCodeInstruction(mTrueJump->mIns[0].mIns, ASMIT_ORA, mTrueJump->mIns[0])); + mIns.Push(NativeCodeInstruction(mTrueJump->mIns[0].mIns, ASMIT_STA, mTrueJump->mIns[0])); + mBranch = ASMIT_JMP; + mTrueJump = mFalseJump; + mFalseJump = nullptr; + changed = true; + } + } + } + + + if (mTrueJump && mTrueJump->JoinConditionSequence()) + changed = true; + if (mFalseJump && mFalseJump->JoinConditionSequence()) + changed = true; + } + + return changed; +} + + +bool NativeCodeBasicBlock::JoinCommonBranchCodeSequences(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + if (mTrueJump && mFalseJump && mTrueJump->mIns.Size() > 0 && mFalseJump->mIns.Size() > 0 && + mTrueJump != this && mFalseJump != this && + mTrueJump->mNumEntries == 1 && mFalseJump->mNumEntries == 1) + { + int i = 0; + while (i < mTrueJump->mIns.Size()) + { + int j = 0; + while (j < mFalseJump->mIns.Size() && !mFalseJump->mIns[j].IsSame(mTrueJump->mIns[i])) + j++; + if (j < mFalseJump->mIns.Size()) + { + int n = 0; + int live = LIVE_CPU_REG_C | LIVE_CPU_REG_Z; + + while (i + n < mTrueJump->mIns.Size() && j + n < mFalseJump->mIns.Size() && + mTrueJump->mIns[i + n].IsSame(mFalseJump->mIns[j + n])) + { + if (mTrueJump->mIns[i + n].ChangesAccu()) live |= LIVE_CPU_REG_A; + if (mTrueJump->mIns[i + n].ChangesXReg()) live |= LIVE_CPU_REG_X; + if (mTrueJump->mIns[i + n].ChangesYReg()) live |= LIVE_CPU_REG_Y; + + if (!((mTrueJump->mIns[i + n].mLive & live) || (mFalseJump->mIns[j + n].mLive & live))) + break; + n++; + } + + if (i + n < mTrueJump->mIns.Size() && j + n < mFalseJump->mIns.Size() && mTrueJump->mIns[i + n].IsSame(mFalseJump->mIns[j + n])) + { + if (mTrueJump->MayBeMovedBeforeBlock(i, i + n + 1) && mFalseJump->MayBeMovedBeforeBlock(j, j + n + 1)) + { + if (SafeInjectSequenceFromBack(mTrueJump, i, i + n + 1)) + { + for (int k = 0; k < i; k++) + mTrueJump->mIns[k].mLive |= mTrueJump->mIns[i].mLive; + for (int k = 0; k < j; k++) + mFalseJump->mIns[k].mLive |= mFalseJump->mIns[j].mLive; + if (mTrueJump->mIns[i].mLive & LIVE_CPU_REG_A) + { + mExitRequiredRegs += CPU_REG_A; + mTrueJump->mEntryRequiredRegs += CPU_REG_A; + mFalseJump->mEntryRequiredRegs += CPU_REG_A; + } + if (mTrueJump->mIns[i].mLive & LIVE_CPU_REG_X) + { + mExitRequiredRegs += CPU_REG_X; + mTrueJump->mEntryRequiredRegs += CPU_REG_X; + mFalseJump->mEntryRequiredRegs += CPU_REG_X; + } + if (mTrueJump->mIns[i].mLive & LIVE_CPU_REG_Y) + { + mExitRequiredRegs += CPU_REG_Y; + mTrueJump->mEntryRequiredRegs += CPU_REG_Y; + mFalseJump->mEntryRequiredRegs += CPU_REG_Y; + } + if (mTrueJump->mIns[i].mLive & LIVE_CPU_REG_C) + { + mExitRequiredRegs += CPU_REG_C; + mTrueJump->mEntryRequiredRegs += CPU_REG_C; + mFalseJump->mEntryRequiredRegs += CPU_REG_C; + } + + mTrueJump->mIns.Remove(i, n + 1); + mFalseJump->mIns.Remove(j, n + 1); + changed = true; + } + else + i++; + } + else + i++; + } + else + i++; + } + else + i++; + } + } + + CheckLive(); + + if (mTrueJump && mTrueJump->JoinCommonBranchCodeSequences()) + changed = true; + if (mFalseJump && mFalseJump->JoinCommonBranchCodeSequences()) + changed = true; + } + + return changed; +} + +void NativeCodeBasicBlock::DoCrossBlockAShortcut(int addr) +{ + mExitRequiredRegs += CPU_REG_A; + int i = mIns.Size(); + while (i > 0) + { + i--; + mIns[i].mLive |= LIVE_CPU_REG_A; + if (mIns[i].mType == ASMIT_STA && mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i].mAddress == addr) + return; + } +} + +bool NativeCodeBasicBlock::CanCrossBlockAShortcut(int addr) +{ + if (mExitRequiredRegs[CPU_REG_A]) + return false; + + int i = mIns.Size(); + while (i > 0) + { + i--; + if (mIns[i].ChangesAccu()) + return false; + if (mIns[i].mType == ASMIT_STA && mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i].mAddress == addr) + return true; + + if (mIns[i].ReferencesZeroPage(addr)) + return false; + } + + return false; +} + + +void NativeCodeBasicBlock::DoCrossBlockXShortcut(int addr) +{ + mExitRequiredRegs += CPU_REG_X; + int i = mIns.Size(); + while (i > 0) + { + i--; + mIns[i].mLive |= LIVE_CPU_REG_X; + if (mIns[i].mType == ASMIT_STX && mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i].mAddress == addr) + return; + if (mIns[i].mType == ASMIT_STA && mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i].mAddress == addr) + { + mIns.Insert(i, NativeCodeInstruction(mIns[i].mIns, ASMIT_TAX)); + return; + } + } +} + +bool NativeCodeBasicBlock::CanCrossBlockXShortcut(int addr) +{ + if (mExitRequiredRegs[CPU_REG_X]) + return false; + + bool xref = false; + + int i = mIns.Size(); + while (i > 0) + { + i--; + if (mIns[i].ChangesXReg()) + return false; + if (mIns[i].mType == ASMIT_STX && mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i].mAddress == addr) + return true; + if (mIns[i].mType == ASMIT_STA && mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i].mAddress == addr && !(mIns[i].mLive & LIVE_CPU_REG_Z)) + return !xref; + + if (mIns[i].ReferencesZeroPage(addr)) + return false; + if (mIns[i].ReferencesXReg()) + xref = true; + } + + return false; +} + +void NativeCodeBasicBlock::DoCrossBlockYShortcut(int addr) +{ + mExitRequiredRegs += CPU_REG_Y; + int i = mIns.Size(); + while (i > 0) + { + i--; + mIns[i].mLive |= LIVE_CPU_REG_Y; + if (mIns[i].mType == ASMIT_STY && mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i].mAddress == addr) + return; + if (mIns[i].mType == ASMIT_STA && mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i].mAddress == addr) + { + mIns.Insert(i, NativeCodeInstruction(mIns[i].mIns, ASMIT_TAY)); + return; + } + } +} + +bool NativeCodeBasicBlock::CanCrossBlockYShortcut(int addr) +{ + if (mExitRequiredRegs[CPU_REG_Y]) + return false; + + bool yref = false; + + int i = mIns.Size(); + while (i > 0) + { + i--; + if (mIns[i].ChangesYReg()) + return false; + if (mIns[i].mType == ASMIT_STY && mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i].mAddress == addr) + return true; + if (mIns[i].mType == ASMIT_STA && mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i].mAddress == addr && !(mIns[i].mLive & LIVE_CPU_REG_Z)) + return !yref; + + if (mIns[i].ReferencesZeroPage(addr)) + return false; + if (mIns[i].ReferencesYReg()) + yref = true; + } + + return false; +} + +bool NativeCodeBasicBlock::CrossBlockRegisterAlias(bool sameAX, bool sameAY) +{ + bool changed = false; + if (!mVisited) + { + assert(mLocked || mNumEntries == mEntryBlocks.Size()); + + if (mLoopHead) + { + sameAX = false; + sameAY = false; + } + else + { + if (mNumEntered == 0) + { + mSameAX = sameAX; + mSameAY = sameAY; + } + else + { + if (!sameAX) + mSameAX = false; + if (!sameAY) + mSameAY = false; + } + mNumEntered++; + if (mNumEntered != mNumEntries) + return false; + sameAX = mSameAX; + sameAY = mSameAY; + } + + mVisited = true; + + bool direct = false; + for (int i = 0; i < mIns.Size(); i++) + { + NativeCodeInstruction& ins(mIns[i]); + switch (ins.mType) + { + case ASMIT_TAY: + if (sameAY && !(ins.mLive & LIVE_CPU_REG_Z)) + { + ins.mType = ASMIT_NOP; ins.mMode = ASMIM_IMPLIED; + changed = true; + } + sameAY = true; + break; + case ASMIT_TAX: + if (sameAX && !(ins.mLive & LIVE_CPU_REG_Z)) + { + ins.mType = ASMIT_NOP; ins.mMode = ASMIM_IMPLIED; + changed = true; + } + sameAX = true; + break; + case ASMIT_TYA: + if (sameAY && !(ins.mLive & LIVE_CPU_REG_Z)) + { + ins.mType = ASMIT_NOP; ins.mMode = ASMIM_IMPLIED; + changed = true; + } + sameAY = true; + sameAX = false; + direct = true; + break; + case ASMIT_TXA: + if (sameAX && !(ins.mLive & LIVE_CPU_REG_Z)) + { + ins.mType = ASMIT_NOP; ins.mMode = ASMIM_IMPLIED; + changed = true; + } + sameAY = false; + sameAX = true; + direct = true; + break; + case ASMIT_CMP: + if (!(ins.mLive & LIVE_CPU_REG_A)) + { + if (sameAY && (ins.mLive & LIVE_CPU_REG_Y) && HasAsmInstructionMode(ASMIT_CPY, ins.mMode)) + { + ins.mType = ASMIT_CPY; + changed = true; + } + else if (sameAX && (ins.mLive & LIVE_CPU_REG_X) && HasAsmInstructionMode(ASMIT_CPX, ins.mMode)) + { + ins.mType = ASMIT_CPX; + changed = true; + } + } + break; + case ASMIT_CPX: + if (!direct && !(ins.mLive & LIVE_CPU_REG_X)) + { + if (sameAX && (ins.mLive & LIVE_CPU_REG_A)) + { + ins.mType = ASMIT_CMP; + changed = true; + } + } + break; + case ASMIT_CPY: + if (!direct && !(ins.mLive & LIVE_CPU_REG_Y)) + { + if (sameAY && (ins.mLive & LIVE_CPU_REG_A)) + { + ins.mType = ASMIT_CMP; + changed = true; + } + } + break; + default: + if (ins.ChangesAccu()) + { + sameAY = false; + sameAX = false; + } + if (ins.ChangesXReg()) + sameAX = false; + if (ins.ChangesYReg()) + sameAY = false; + break; + } + } + + + if (mTrueJump && mTrueJump->CrossBlockRegisterAlias(sameAX, sameAY)) + changed = true; + if (mFalseJump && mFalseJump->CrossBlockRegisterAlias(sameAX, sameAY)) + changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::CrossBlockYAliasProgpagation(const int* yalias) +{ + bool changed = false; + + if (!mVisited) + { + if (mLoopHead || !yalias) + { + for (int i = 0; i < 256; i++) + mYAlias[i] = -1; + } + else + { + if (mNumEntered == 0) + { + for (int i = 0; i < 256; i++) + mYAlias[i] = yalias[i]; + } + else + { + for (int i = 0; i < 256; i++) + if (mYAlias[i] != yalias[i]) + mYAlias[i] = -1; + } + + mNumEntered++; + if (mNumEntered < mNumEntries) + return false; + } + + mVisited = true; + + int yoffset = 0; + for (int i = 0; i < mIns.Size(); i++) + { + NativeCodeInstruction& ins(mIns[i]); + if (ins.mType == ASMIT_INY) + yoffset = (yoffset + 1) & 0xff; + else if (ins.mType == ASMIT_DEY) + yoffset = (yoffset - 1) & 0xff; + else if (ins.mType == ASMIT_STY && mIns[i].mMode == ASMIM_ZERO_PAGE) + mYAlias[ins.mAddress] = yoffset; + else if (ins.mMode == ASMIM_ZERO_PAGE && mIns[i].ChangesAddress()) + mYAlias[ins.mAddress] = -1; + else if (ins.mType == ASMIT_LDY && ins.mMode == ASMIM_ZERO_PAGE) + { + if (mYAlias[ins.mAddress] != -1) + { + int diff = (mYAlias[ins.mAddress] - yoffset) & 0xff; + if (diff == 0x01) + { + ins.mType = ASMIT_INY; + ins.mMode = ASMIM_IMPLIED; + changed = true; + } + else if (diff == 0xff) + { + ins.mType = ASMIT_DEY; + ins.mMode = ASMIM_IMPLIED; + changed = true; + } + else if (diff == 0x00 && !(ins.mLive & LIVE_CPU_REG_Z)) + { + ins.mType = ASMIT_NOP; + ins.mMode = ASMIM_IMPLIED; + changed = true; + } + + yoffset = mYAlias[ins.mAddress]; + } + else + { + for (int i = 0; i < 256; i++) + mYAlias[i] = -1; + + yoffset = 0; + mYAlias[ins.mAddress] = yoffset; + } + } + else if (ins.mType == ASMIT_TAY && i > 0 && (mIns[i - 1].mType == ASMIT_STA || mIns[i - 1].mType == ASMIT_LDA) && mIns[i - 1].mMode == ASMIM_ZERO_PAGE) + { + for (int i = 0; i < 256; i++) + mYAlias[i] = -1; + yoffset = 0; + mYAlias[mIns[i - 1].mAddress] = yoffset; + } + else if (ins.mType == ASMIT_LDA && ins.mMode == ASMIM_ZERO_PAGE && mYAlias[ins.mAddress] != -1 && i + 1 < mIns.Size()) + { + if (mIns[i + 1].mType == ASMIT_ADC && mIns[i + 1].mMode == ASMIM_IMMEDIATE && (mYAlias[ins.mAddress] == yoffset || !(mIns[i + 1].mLive & LIVE_CPU_REG_C))) + { + ins.mType = ASMIT_TYA; + ins.mMode = ASMIM_IMPLIED; + mIns[i + 1].mAddress = (mIns[i + 1].mAddress + mYAlias[ins.mAddress] - yoffset) & 0xff; + changed = true; + } + } + else if (ins.ChangesYReg()) + { + for (int i = 0; i < 256; i++) + mYAlias[i] = -1; + yoffset = 0; + } + } + + if (yoffset > 0) + { + for (int i = 0; i < 256; i++) + { + if (mYAlias[i] >= 0) + mYAlias[i] = (mYAlias[i] - yoffset) & 0xff; + + } + } + + if (mTrueJump && mTrueJump->CrossBlockYAliasProgpagation(mYAlias)) + changed = true; + if (mFalseJump && mFalseJump->CrossBlockYAliasProgpagation(mYAlias)) + changed = true; + } + + return changed; +} + + +void NativeCodeBasicBlock::BypassAccuLoadStoreXY(void) +{ + if (!mVisited) + { + mVisited = true; + + for (int i = 0; i + 1 < mIns.Size(); i++) + { + if (mIns[i].mType == ASMIT_LDA && mIns[i + 1].mType == ASMIT_STA && !(mIns[i + 1].mLive & LIVE_CPU_REG_A)) + { + if (!(mIns[i].mLive & LIVE_CPU_REG_X) && HasAsmInstructionMode(ASMIT_LDX, mIns[i].mMode) && HasAsmInstructionMode(ASMIT_STX, mIns[i + 1].mMode)) + { + mIns[i].mType = ASMIT_LDX; mIns[i].mLive |= LIVE_CPU_REG_X; + mIns[i + 1].mType = ASMIT_STX; + } + else if (!(mIns[i].mLive & LIVE_CPU_REG_Y) && HasAsmInstructionMode(ASMIT_LDY, mIns[i].mMode) && HasAsmInstructionMode(ASMIT_STY, mIns[i + 1].mMode)) + { + mIns[i].mType = ASMIT_LDY; mIns[i].mLive |= LIVE_CPU_REG_Y; + mIns[i + 1].mType = ASMIT_STY; + } + } + } + + if (mTrueJump) mTrueJump->BypassAccuLoadStoreXY(); + if (mFalseJump) mFalseJump->BypassAccuLoadStoreXY(); + + } +} + + +bool NativeCodeBasicBlock::CrossBlockXYShortcut(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + if (!mLoopHead && mEntryBlocks.Size() > 1) + { + CheckLive(); + + bool xvalid = !mEntryRequiredRegs[CPU_REG_X]; + bool yvalid = !mEntryRequiredRegs[CPU_REG_Y]; + bool avalid = !mEntryRequiredRegs[CPU_REG_A]; + + int i = 0; + while (i < mIns.Size() && (xvalid || yvalid || avalid)) + { + if (xvalid && mIns[i].ChangesAccu()) + { + if (mIns[i].mType == ASMIT_LDA && mIns[i].mMode == ASMIM_ZERO_PAGE && !(mIns[i].mLive & LIVE_CPU_REG_Z)) + { + int k = i; + while (k > 0 && !mIns[k - 1].ReferencesZeroPage(mIns[i].mAddress)) + k--; + if (k == 0) + { + k = 0; + while (k < mEntryBlocks.Size() && mEntryBlocks[k]->CanCrossBlockAShortcut(mIns[i].mAddress)) + k++; + if (k == mEntryBlocks.Size()) + { + for (int k = 0; k < mEntryBlocks.Size(); k++) + mEntryBlocks[k]->DoCrossBlockAShortcut(mIns[i].mAddress); + changed = true; + mIns[i].mType = ASMIT_NOP; mIns[i].mMode = ASMIM_IMPLIED; + mEntryRequiredRegs += CPU_REG_A; + for (int k = 0; k < i; k++) + mIns[k].mLive |= LIVE_CPU_REG_A; + } + } + } + xvalid = false; + } + if (xvalid && mIns[i].ChangesXReg()) + { + if (mIns[i].mType == ASMIT_LDX && mIns[i].mMode == ASMIM_ZERO_PAGE && !(mIns[i].mLive & LIVE_CPU_REG_Z)) + { + int k = i; + while (k > 0 && !mIns[k - 1].ReferencesZeroPage(mIns[i].mAddress)) + k--; + if (k == 0) + { + k = 0; + while (k < mEntryBlocks.Size() && mEntryBlocks[k]->CanCrossBlockXShortcut(mIns[i].mAddress)) + k++; + if (k == mEntryBlocks.Size()) + { + for (int k = 0; k < mEntryBlocks.Size(); k++) + mEntryBlocks[k]->DoCrossBlockXShortcut(mIns[i].mAddress); + changed = true; + mIns[i].mType = ASMIT_NOP; mIns[i].mMode = ASMIM_IMPLIED; + mEntryRequiredRegs += CPU_REG_X; + for (int k = 0; k < i; k++) + mIns[k].mLive |= LIVE_CPU_REG_X; + } + } + } + xvalid = false; + } + if (yvalid && mIns[i].ChangesYReg()) + { + if (mIns[i].mType == ASMIT_LDY && mIns[i].mMode == ASMIM_ZERO_PAGE && !(mIns[i].mLive & LIVE_CPU_REG_Z)) + { + int k = i; + while (k > 0 && !mIns[k - 1].ReferencesZeroPage(mIns[i].mAddress)) + k--; + if (k == 0) + { + k = 0; + while (k < mEntryBlocks.Size() && mEntryBlocks[k]->CanCrossBlockYShortcut(mIns[i].mAddress)) + k++; + if (k == mEntryBlocks.Size()) + { + for (int k = 0; k < mEntryBlocks.Size(); k++) + mEntryBlocks[k]->DoCrossBlockYShortcut(mIns[i].mAddress); + changed = true; + mIns[i].mType = ASMIT_NOP; mIns[i].mMode = ASMIM_IMPLIED; + mEntryRequiredRegs += CPU_REG_Y; + for (int k = 0; k < i; k++) + mIns[k].mLive |= LIVE_CPU_REG_Y; + } + } + } + yvalid = false; + } + + i++; + } + + CheckLive(); + } + + if (mTrueJump && mTrueJump->CrossBlockXYShortcut()) + changed = true; + if (mFalseJump && mFalseJump->CrossBlockXYShortcut()) + changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::CrossBlockIncToZeroShortcut(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + int sz = mIns.Size(); + if (mFalseJump && mIns.Size() > 0 && mIns[sz - 1].mType == ASMIT_INC && mIns[sz - 1].mMode == ASMIM_ZERO_PAGE) + { + NativeCodeBasicBlock* eblock = nullptr, * nblock = nullptr; + + if (mBranch == ASMIT_BNE) + { + eblock = mFalseJump; + nblock = mTrueJump; + } + else if (mBranch == ASMIT_BEQ) + { + eblock = mTrueJump; + nblock = mFalseJump; + } + if (eblock && !eblock->mFalseJump && eblock->mTrueJump == nblock && !eblock->ChangesZeroPage(mIns[sz - 1].mAddress)) + { + if (nblock->mIns.Size() == 2 && + nblock->mFalseJump && + (nblock->mIns[0].mType == ASMIT_LDA || nblock->mIns[0].mType == ASMIT_TXA || nblock->mIns[0].mType == ASMIT_TYA) && + nblock->mIns[1].mType == ASMIT_ORA) + { + if (nblock->mIns[0].SameEffectiveAddress(mIns[sz - 1]) || nblock->mIns[1].SameEffectiveAddress(mIns[sz - 1])) + { + NativeCodeBasicBlock* tblock = nullptr; + if (nblock->mBranch == ASMIT_BNE) + tblock = nblock->mTrueJump; + else if (nblock->mBranch == ASMIT_BEQ) + tblock = nblock->mFalseJump; + + if (tblock && !tblock->mEntryRequiredRegs[CPU_REG_A]) + { + tblock->AddEntryBlock(this); + nblock->RemEntryBlock(this); + if (mBranch == ASMIT_BNE) + mTrueJump = tblock; + else + mFalseJump = tblock; + changed = true; + } + } + } + } + } + else if (mFalseJump && sz >= 8 && + mIns[sz - 8].mType == ASMIT_CLC && + mIns[sz - 7].mType == ASMIT_LDA && mIns[sz - 7].mMode == ASMIM_ZERO_PAGE && + mIns[sz - 6].mType == ASMIT_ADC && mIns[sz - 6].mMode == ASMIM_IMMEDIATE && mIns[sz - 6].mAddress == 1 && + mIns[sz - 5].mType == ASMIT_STA && mIns[sz - 5].mMode == ASMIM_ZERO_PAGE && mIns[sz - 5].mAddress == mIns[sz - 7].mAddress && + mIns[sz - 4].mType == ASMIT_LDA && mIns[sz - 4].mMode == ASMIM_ZERO_PAGE && + mIns[sz - 3].mType == ASMIT_ADC && mIns[sz - 3].mMode == ASMIM_IMMEDIATE && mIns[sz - 3].mAddress == 0 && + mIns[sz - 2].mType == ASMIT_STA && mIns[sz - 2].mMode == ASMIM_ZERO_PAGE && mIns[sz - 2].mAddress == mIns[sz - 4].mAddress && + mIns[sz - 1].mType == ASMIT_ORA && mIns[sz - 1].mMode == ASMIM_ZERO_PAGE && mIns[sz - 1].mAddress == mIns[sz - 7].mAddress) + { + NativeCodeBasicBlock* eblock = nullptr, * nblock = nullptr; + + if (mBranch == ASMIT_BNE) + { + eblock = mFalseJump; + nblock = mTrueJump; + } + else if (mBranch == ASMIT_BEQ) + { + eblock = mTrueJump; + nblock = mFalseJump; + } + + if (nblock && !nblock->mEntryRequiredRegs[CPU_REG_A]) + { + NativeCodeBasicBlock* iblock = mProc->AllocateBlock(); + iblock->AddEntryBlock(this); + iblock->mIns.Push(mIns[sz - 8]); + iblock->mIns.Push(mIns[sz - 4]); + iblock->mIns.Push(mIns[sz - 6]); + iblock->mIns.Push(mIns[sz - 2]); + iblock->Close(mIns[sz - 1].mIns, nblock, eblock, ASMIT_BNE); + + nblock->AddEntryBlock(iblock); + eblock->AddEntryBlock(iblock); + + iblock->mIns[2].mLive |= LIVE_CPU_REG_Z; + iblock->mIns[3].mLive |= LIVE_CPU_REG_Z; + + mIns[sz - 6].mLive |= LIVE_CPU_REG_Z; + mIns[sz - 5].mLive |= LIVE_CPU_REG_Z; + + mIns.SetSize(sz - 4); + mTrueJump = nblock; + mFalseJump = iblock; + mBranch = ASMIT_BNE; + + mExitRequiredRegs += CPU_REG_C; + + CheckLive(); + + changed = true; + } + } + + if (mTrueJump && mTrueJump->CrossBlockIncToZeroShortcut()) + changed = true; + if (mFalseJump && mFalseJump->CrossBlockIncToZeroShortcut()) + changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::CrossBlockXYPreservation(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + if (mTrueJump && mFalseJump && mTrueJump->mTrueJump && !mTrueJump->mFalseJump && mFalseJump == mTrueJump->mTrueJump && + mTrueJump->mNumEntries == 1 && mFalseJump->mNumEntries == 2) + { + NativeCodeBasicBlock* tblock = mTrueJump, * fblock = mFalseJump; + + if (!mExitRequiredRegs[CPU_REG_Y] && !fblock->mEntryRequiredRegs[CPU_REG_X]) + { + int si = mIns.Size() - 1; + while (si >= 0 && !mIns[si].ChangesXReg()) + si--; + + if (si >= 0 && mIns[si].mType == ASMIT_LDX && mIns[si].mMode == ASMIM_ZERO_PAGE) + { + int addr = mIns[si].mAddress; + + int i = si; + while (i < mIns.Size() && !mIns[i].ChangesZeroPage(addr)) + i++; + if (i == mIns.Size()) + { + int fi = 0; + while (fi < fblock->mIns.Size() && !fblock->mIns[fi].ChangesXReg()) + fi++; + if (fi < fblock->mIns.Size() && fblock->mIns[fi].mType == ASMIT_LDX && fblock->mIns[fi].mMode == ASMIM_ZERO_PAGE && fblock->mIns[fi].mAddress == addr) + { + i = 0; + while (i < fi && !fblock->mIns[i].ChangesZeroPage(addr)) + i++; + if (i == fi) + { + int ti = 0; + while (ti < tblock->mIns.Size() && !tblock->mIns[ti].ChangesXReg()) + ti++; + if (ti < tblock->mIns.Size()) + { + int i = 0; + while (i < tblock->mIns.Size() && !tblock->mIns[i].ChangesYReg() && !tblock->mIns[i].ChangesZeroPage(addr)) + i++; + + if (i == tblock->mIns.Size()) + { + tblock->ReplaceXRegWithYReg(ti, tblock->mIns.Size()); + changed = true; + } + } + } + } + } + } + } + } + + + if (mTrueJump && mTrueJump->CrossBlockXYPreservation()) + changed = true; + if (mFalseJump && mFalseJump->CrossBlockXYPreservation()) + changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::EliminateMicroBlocks(void) +{ + bool changed = false; + if (!mVisited) + { + mVisited = true; + + if (mIns.Size() > 0 && mIns.Size() <= 2 && mTrueJump && !mFalseJump) + { + if (mIns[0].mType == ASMIT_STA && !(mIns[0].mLive & LIVE_CPU_REG_A)) + { + for (int i = 0; i < mEntryBlocks.Size(); i++) + { + NativeCodeBasicBlock* block = mEntryBlocks[i]; + int sz = block->mIns.Size(); + if (sz > 0 && !block->mFalseJump) + { + if (block->mIns[sz - 1].mType == ASMIT_TYA && HasAsmInstructionMode(ASMIT_STY, mIns[0].mMode)) + { + block->mIns[sz - 1] = NativeCodeInstruction(mIns[0].mIns, ASMIT_STY, mIns[0]); + if (mIns.Size() > 1) + block->mIns.Push(mIns[1]); + block->mTrueJump->RemEntryBlock(block); + mTrueJump->AddEntryBlock(block); + block->mTrueJump = mTrueJump; + changed = true; + } + else if (block->mIns[sz - 1].mType == ASMIT_TXA && HasAsmInstructionMode(ASMIT_STX, mIns[0].mMode)) + { + block->mIns[sz - 1] = NativeCodeInstruction(mIns[0].mIns, ASMIT_STX, mIns[0]); + if (mIns.Size() > 1) + block->mIns.Push(mIns[1]); + block->mTrueJump->RemEntryBlock(block); + mTrueJump->AddEntryBlock(block); + block->mTrueJump = mTrueJump; + changed = true; + } + } + } + } + } + + if (mTrueJump && mTrueJump->EliminateMicroBlocks()) + changed = true; + if (mFalseJump && mFalseJump->EliminateMicroBlocks()) + changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::CombineAlternateLoads(void) +{ + bool changed = false; + if (!mVisited) + { + mVisited = true; + + if (mTrueJump && mFalseJump && (mBranch == ASMIT_BCC || mBranch == ASMIT_BCS) && + mTrueJump->mIns.Size() == 1 && mFalseJump->mIns.Size() == 1 && + mTrueJump->mTrueJump == mFalseJump->mTrueJump && + !mTrueJump->mFalseJump && !mFalseJump->mFalseJump && + mTrueJump->mNumEntries == 1 && mFalseJump->mNumEntries == 1) + { + if (mTrueJump->mIns[0].mType == ASMIT_LDA && mTrueJump->mIns[0].mMode == ASMIM_IMMEDIATE && + mFalseJump->mIns[0].mType == ASMIT_LDA && mFalseJump->mIns[0].mMode == ASMIM_IMMEDIATE) + { + mFalseJump->mIns[0].mLive |= LIVE_CPU_REG_C; + mIns.Push(mFalseJump->mIns[0]); + mFalseJump->mIns.Remove(0); + mExitRequiredRegs += CPU_REG_A; + mFalseJump->mEntryRequiredRegs += CPU_REG_A; + changed = true; + } + else if (mTrueJump->mIns[0].mType == ASMIT_LDX && mTrueJump->mIns[0].mMode == ASMIM_IMMEDIATE && + mFalseJump->mIns[0].mType == ASMIT_LDX && mFalseJump->mIns[0].mMode == ASMIM_IMMEDIATE) + { + mFalseJump->mIns[0].mLive |= LIVE_CPU_REG_C; + mIns.Push(mFalseJump->mIns[0]); + mFalseJump->mIns.Remove(0); + mExitRequiredRegs += CPU_REG_X; + mFalseJump->mEntryRequiredRegs += CPU_REG_X; + changed = true; + } + else if (mTrueJump->mIns[0].mType == ASMIT_LDY && mTrueJump->mIns[0].mMode == ASMIM_IMMEDIATE && + mFalseJump->mIns[0].mType == ASMIT_LDY && mFalseJump->mIns[0].mMode == ASMIM_IMMEDIATE) + { + mFalseJump->mIns[0].mLive |= LIVE_CPU_REG_C; + mIns.Push(mFalseJump->mIns[0]); + mFalseJump->mIns.Remove(0); + mExitRequiredRegs += CPU_REG_Y; + mFalseJump->mEntryRequiredRegs += CPU_REG_Y; + changed = true; + } + } + + if (mTrueJump && mTrueJump->CombineAlternateLoads()) + changed = true; + if (mFalseJump && mFalseJump->CombineAlternateLoads()) + changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::FoldLoopEntry(void) +{ + bool changed = false; + if (!mVisited) + { + mVisited = true; + + if (mTrueJump && mFalseJump && mIns.Size() >= 1) + { + int sz = mIns.Size(); + + if (mIns[sz - 1].mType == ASMIT_LDA) + { + if (mTrueJump->mIns.Size() == 1 && mTrueJump != this) + { + if (mTrueJump->mIns[0].mType == ASMIT_LDA && mTrueJump->mIns[0].SameEffectiveAddress(mIns[sz - 1])) + { + if (mBranch == mTrueJump->mBranch && mFalseJump == mTrueJump->mFalseJump && mTrueJump == mTrueJump->mTrueJump || + mBranch == InvertBranchCondition(mTrueJump->mBranch) && mFalseJump == mTrueJump->mTrueJump && mTrueJump == mTrueJump->mFalseJump) + { + mIns[sz - 1].mType = ASMIT_NOP; + mIns[sz - 1].mMode = ASMIM_IMPLIED; + mBranch = ASMIT_JMP; + mFalseJump->RemEntryBlock(this); + mFalseJump = nullptr; + changed = true; + } + } + } + if (!changed && mFalseJump->mIns.Size() == 1 && mFalseJump != this) + { + if (mFalseJump->mIns[0].mType == ASMIT_LDA && mFalseJump->mIns[0].SameEffectiveAddress(mIns[sz - 1])) + { + if (mBranch == mFalseJump->mBranch && mFalseJump == mFalseJump->mFalseJump && mFalseJump == mTrueJump->mTrueJump || + mBranch == InvertBranchCondition(mFalseJump->mBranch) && mFalseJump == mFalseJump->mTrueJump && mTrueJump == mFalseJump->mFalseJump) + { + mIns[sz - 1].mType = ASMIT_NOP; + mIns[sz - 1].mMode = ASMIM_IMPLIED; + mBranch = ASMIT_JMP; + mTrueJump->RemEntryBlock(this); + mTrueJump = mFalseJump; + mFalseJump = nullptr; + changed = true; + } + } + } + } + } + if (!changed && mTrueJump && mFalseJump && mIns.Size() >= 2) + { + int sz = mIns.Size(); + + if (mIns[sz - 2].mType == ASMIT_LDA && ((mIns[sz - 1].mType == ASMIT_AND && mIns[sz - 1].mMode == ASMIM_IMMEDIATE) || (mIns[sz - 1].IsShift() && mIns[sz - 1].mMode == ASMIM_IMPLIED))) + { + if (mTrueJump->mIns.Size() == 2 && mTrueJump != this) + { + if (mTrueJump->mIns[0].IsSame(mIns[sz - 2]) && mTrueJump->mIns[1].IsSame(mIns[sz - 1])) + { + if (mBranch == mTrueJump->mBranch && mFalseJump == mTrueJump->mFalseJump && mTrueJump == mTrueJump->mTrueJump || + mBranch == InvertBranchCondition(mTrueJump->mBranch) && mFalseJump == mTrueJump->mTrueJump && mTrueJump == mTrueJump->mFalseJump) + { + mIns[sz - 1].mType = ASMIT_NOP; mIns[sz - 1].mMode = ASMIM_IMPLIED; + mIns[sz - 2].mType = ASMIT_NOP; mIns[sz - 2].mMode = ASMIM_IMPLIED; + mBranch = ASMIT_JMP; + mFalseJump->RemEntryBlock(this); + mFalseJump = nullptr; + changed = true; + } + } + } + if (!changed && mFalseJump->mIns.Size() == 2 && mFalseJump != this) + { + if (mFalseJump->mIns[0].IsSame(mIns[sz - 2]) && mFalseJump->mIns[1].IsSame(mIns[sz - 1])) + { + if (mBranch == mFalseJump->mBranch && mFalseJump == mFalseJump->mFalseJump && mFalseJump == mTrueJump->mTrueJump || + mBranch == InvertBranchCondition(mFalseJump->mBranch) && mFalseJump == mFalseJump->mTrueJump && mTrueJump == mFalseJump->mFalseJump) + { + mIns[sz - 1].mType = ASMIT_NOP; + mIns[sz - 1].mMode = ASMIM_IMPLIED; + mBranch = ASMIT_JMP; + mTrueJump->RemEntryBlock(this); + mTrueJump = mFalseJump; + mFalseJump = nullptr; + changed = true; + } + } + } + } + } + + if (mTrueJump && mTrueJump->FoldLoopEntry()) + changed = true; + if (mFalseJump && mFalseJump->FoldLoopEntry()) + changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::BypassRegisterConditionBlock(void) +{ + bool changed = false; + if (!mVisited) + { + mVisited = true; + + if (mTrueJump && mFalseJump && mIns.Size() > 0) + { + NativeCodeBasicBlock* eblock = nullptr, * cblock = nullptr; + + if (mTrueJump->mTrueJump == mFalseJump && !mTrueJump->mFalseJump) + { + cblock = mTrueJump; + eblock = mFalseJump; + } + else if (mFalseJump->mTrueJump == mTrueJump && !mFalseJump->mFalseJump) + { + cblock = mFalseJump; + eblock = mTrueJump; + } + + if (cblock && cblock->mNumEntries == 1 && eblock->mNumEntries == 2) + { + if (cblock->mIns.Size() > 0 && !cblock->mEntryRequiredRegs[CPU_REG_Y] && + mIns.Last().mType == ASMIT_TAY && cblock->mIns.Last().mType == ASMIT_TAY && !(mIns.Last().mLive & LIVE_CPU_REG_Z)) + { + eblock->mIns.Insert(0, NativeCodeInstruction(mIns.Last().mIns, ASMIT_TAY)); + mIns[mIns.Size() - 1].mType = ASMIT_NOP; + cblock->mIns[cblock->mIns.Size() - 1].mType = ASMIT_NOP; + + mExitRequiredRegs += CPU_REG_A; + cblock->mExitRequiredRegs += CPU_REG_A; + eblock->mEntryRequiredRegs += CPU_REG_A; + changed = true; + } + if (cblock->mIns.Size() > 0 && !cblock->mEntryRequiredRegs[CPU_REG_X] && + mIns.Last().mType == ASMIT_TAX && cblock->mIns.Last().mType == ASMIT_TAX && !(mIns.Last().mLive & LIVE_CPU_REG_Z)) + { + eblock->mIns.Insert(0, NativeCodeInstruction(mIns.Last().mIns, ASMIT_TAX)); + mIns[mIns.Size() - 1].mType = ASMIT_NOP; + cblock->mIns[cblock->mIns.Size() - 1].mType = ASMIT_NOP; + + mExitRequiredRegs += CPU_REG_A; + cblock->mExitRequiredRegs += CPU_REG_A; + eblock->mEntryRequiredRegs += CPU_REG_A; + changed = true; + } + +#if 0 + if (cblock->mIns.Size() >= 1 && eblock->mIns.Size() >= 1) + { + int csz = cblock->mIns.Size() - 1; + + if (cblock->mIns[csz].mType == ASMIT_LDA && cblock->mIns[csz].mMode == ASMIM_ZERO_PAGE && + eblock->mIns[0].mType == ASMIT_STA && eblock->mIns[0].mMode == ASMIM_ZERO_PAGE && eblock->mIns[0].mAddress == cblock->mIns[csz].mAddress && !(eblock->mIns[0].mLive & LIVE_CPU_REG_A)) + { + mIns.Push(NativeCodeInstruction(ASMIT_STA, eblock->mIns[0])); + cblock->mIns.Remove(csz); + cblock->mExitRequiredRegs -= CPU_REG_A; + changed = true; + } + } +#endif + if (mExitRequiredRegs[CPU_REG_A]) + { + if (!cblock->ReferencesAccu()) + { + int i = mIns.Size() - 1; + while (i >= 0 && !mIns[i].ReferencesAccu()) + i--; + if (i >= 0 && mIns[i].mType == ASMIT_LDA && mIns[i].mMode == ASMIM_ZERO_PAGE && !(mIns[i].mLive & LIVE_CPU_REG_Z)) + { + if (!ChangesZeroPage(mIns[i].mAddress, i + 1) && !cblock->ChangesZeroPage(mIns[i].mAddress)) + { + eblock->mIns.Insert(0, NativeCodeInstruction(mIns[i].mIns, ASMIT_LDA, mIns[i])); + mIns.Remove(i); + changed = true; + mExitRequiredRegs -= CPU_REG_A; + CheckLive(); + } + } + } + else if (!eblock->mEntryRequiredRegs[CPU_REG_A]) + { + int i = mIns.Size() - 1; + while (i >= 0 && !mIns[i].ReferencesAccu()) + i--; + if (i >= 0 && mIns[i].mType == ASMIT_TXA && !(mIns[i].mLive & LIVE_CPU_REG_Z)) + { + if (!ChangesXReg(i + 1)) + { + cblock->mIns.Insert(0, NativeCodeInstruction(mIns[i].mIns, ASMIT_TXA)); + mIns.Remove(i); + while (i < mIns.Size()) + mIns[i++].mLive |= LIVE_CPU_REG_X; + + changed = true; + mExitRequiredRegs -= CPU_REG_A; + mExitRequiredRegs += CPU_REG_X; + cblock->mEntryRequiredRegs += CPU_REG_X; + CheckLive(); + } + } + else if (i >= 0 && mIns[i].mType == ASMIT_TYA && !(mIns[i].mLive & LIVE_CPU_REG_Z)) + { + if (!ChangesYReg(i + 1)) + { + cblock->mIns.Insert(0, NativeCodeInstruction(mIns[i].mIns, ASMIT_TYA)); + mIns.Remove(i); + while (i < mIns.Size()) + mIns[i++].mLive |= LIVE_CPU_REG_Y; + + changed = true; + mExitRequiredRegs -= CPU_REG_A; + mExitRequiredRegs += CPU_REG_Y; + cblock->mEntryRequiredRegs += CPU_REG_Y; + CheckLive(); + } + } + } + } + + if (mExitRequiredRegs[CPU_REG_Y]) + { + if (!cblock->ReferencesYReg()) + { + int i = mIns.Size() - 1; + while (i >= 0 && !mIns[i].ReferencesYReg()) + i--; + if (i >= 0 && mIns[i].mType == ASMIT_LDY && mIns[i].mMode == ASMIM_ZERO_PAGE && !(mIns[i].mLive & LIVE_CPU_REG_Z)) + { + if (!ChangesZeroPage(mIns[i].mAddress, i + 1) && !cblock->ChangesZeroPage(mIns[i].mAddress)) + { + eblock->mIns.Insert(0, NativeCodeInstruction(mIns[i].mIns, ASMIT_LDY, mIns[i])); + mIns.Remove(i); + changed = true; + mExitRequiredRegs -= CPU_REG_Y; + CheckLive(); + } + } + } + } + + if (mExitRequiredRegs[CPU_REG_X]) + { + if (!cblock->ReferencesXReg()) + { + int i = mIns.Size() - 1; + while (i >= 0 && !mIns[i].ReferencesXReg()) + i--; + if (i >= 0 && mIns[i].mType == ASMIT_LDX && mIns[i].mMode == ASMIM_ZERO_PAGE && !(mIns[i].mLive & LIVE_CPU_REG_Z)) + { + if (!ChangesZeroPage(mIns[i].mAddress, i + 1) && !cblock->ChangesZeroPage(mIns[i].mAddress)) + { + eblock->mIns.Insert(0, NativeCodeInstruction(mIns[i].mIns, ASMIT_LDX, mIns[i])); + mIns.Remove(i); + changed = true; + mExitRequiredRegs -= CPU_REG_X; + CheckLive(); + } + } + } + } + +#if 1 + CheckLive(); + + if (eblock->mIns.Size() > 0 && eblock->mIns[0].mType == ASMIT_LDY && eblock->mIns[0].mMode == ASMIM_ZERO_PAGE && + !(eblock->mIns[0].mLive & LIVE_MEM) && !cblock->ReferencesYReg() && cblock->mIns.Size() > 0 && !mExitRequiredRegs[CPU_REG_Y]) + { + NativeCodeInstruction& ins = cblock->mIns[cblock->mIns.Size() - 1]; + if ((ins.mType == ASMIT_INC || ins.mType == ASMIT_DEC) && ins.SameEffectiveAddress(eblock->mIns[0]) && !ChangedOnPath(cblock, 0, cblock->mIns.Size() - 1, ins.mAddress)) + { + int i = mIns.Size(); + bool fail = false; + while (i > 0 && (mIns[i - 1].mLive & LIVE_CPU_REG_Z)) + { + if (mIns[i - 1].ReferencesYReg() || mIns[i - 1].ChangesZeroPage(ins.mAddress)) + { + fail = true; + break; + } + i--; + } + + if (!fail) + { + mIns.Insert(i, eblock->mIns[0]); + mIns[i].mLive |= LIVE_CPU_REG_A | LIVE_CPU_REG_X | LIVE_CPU_REG_C; + + eblock->mIns.Remove(0); + if (ins.mType == ASMIT_INC) + ins.mType = ASMIT_INY; + else if (ins.mType == ASMIT_DEC) + ins.mType = ASMIT_DEY; + ins.mMode = ASMIM_IMPLIED; + + while (i < mIns.Size()) + { + mIns[i].mLive |= LIVE_CPU_REG_Y; + i++; + } + + for (i = 0; i < cblock->mIns.Size(); i++) + cblock->mIns[i].mLive |= LIVE_CPU_REG_Y; + changed = true; + cblock->mEntryRequiredRegs += CPU_REG_Y; + cblock->mExitRequiredRegs += CPU_REG_Y; + eblock->mEntryRequiredRegs += CPU_REG_Y; + CheckLive(); + } + } + } +#endif + } + } + + + if (mTrueJump && mTrueJump->BypassRegisterConditionBlock()) + changed = true; + if (mFalseJump && mFalseJump->BypassRegisterConditionBlock()) + changed = true; + } + + return changed; +} + + +bool NativeCodeBasicBlock::FindPageStartAddress(int at, int reg, int& addr) +{ + int j = at - 2; + while (j >= 0) + { + if (mIns[j + 0].mType == ASMIT_LDA && mIns[j + 0].mMode == ASMIM_IMMEDIATE && + mIns[j + 1].mType == ASMIT_STA && mIns[j + 1].mMode == ASMIM_ZERO_PAGE && mIns[j + 1].mAddress == reg + 1) + { + addr = mIns[j + 0].mAddress << 8; + return true; + } + if (mIns[j + 1].mMode == ASMIM_ZERO_PAGE && (mIns[j + 1].mAddress == reg || mIns[j + 1].mAddress == reg + 1) && mIns[j + 1].ChangesAddress()) + return false; + if (mIns[j + 1].mType == ASMIT_JSR) + return false; + + j--; + } + + if (mFromJump) + return mFromJump->FindPageStartAddress(mFromJump->mIns.Size(), reg, addr); + else + return false; +} + +bool NativeCodeBasicBlock::FindGlobalAddress(int at, int reg, int& apos) +{ + int j = at - 4; + while (j >= 0) + { + if (mIns[j + 0].mType == ASMIT_LDA && mIns[j + 0].mMode == ASMIM_IMMEDIATE_ADDRESS && (mIns[j + 0].mFlags & NCIF_LOWER) && mIns[j + 0].mLinkerObject && + mIns[j + 1].mType == ASMIT_STA && mIns[j + 1].mMode == ASMIM_ZERO_PAGE && mIns[j + 1].mAddress == reg && + mIns[j + 2].mType == ASMIT_LDA && mIns[j + 2].mMode == ASMIM_IMMEDIATE_ADDRESS && (mIns[j + 2].mFlags & NCIF_UPPER) && mIns[j + 2].mLinkerObject == mIns[j + 0].mLinkerObject && + mIns[j + 3].mType == ASMIT_STA && mIns[j + 3].mMode == ASMIM_ZERO_PAGE && mIns[j + 3].mAddress == reg + 1) + { + apos = j + 0; + return true; + } + if (mIns[j + 3].mMode == ASMIM_ZERO_PAGE && (mIns[j + 3].mAddress == reg || mIns[j + 3].mAddress == reg + 1) && mIns[j + 3].ChangesAddress()) + return false; + + j--; + } + + return false; +} + +bool NativeCodeBasicBlock::FindImmediateStore(int at, int reg, const NativeCodeInstruction*& ains) +{ + int j = at - 1; + while (j >= 0) + { + if (mIns[j + 0].mType == ASMIT_LDA && (mIns[j + 0].mMode == ASMIM_IMMEDIATE || mIns[j + 0].mMode == ASMIM_IMMEDIATE_ADDRESS) && + mIns[j + 1].mType == ASMIT_STA && mIns[j + 1].mMode == ASMIM_ZERO_PAGE && mIns[j + 1].mAddress == reg) + { + ains = &(mIns[j + 0]); + return true; + } + else if (mIns[j + 1].ChangesZeroPage(reg)) + return false; + j--; + } + + return false; +} + +int NativeCodeBasicBlock::FindImmediateGlobalStore(int at, const NativeCodeInstruction& ins) +{ + at--; + while (at >= 0 && !ins.MayBeChangedOnAddress(mIns[at])) + at--; + if (at > 0 && mIns[at].mMode == ASMIM_ABSOLUTE && mIns[at].mLinkerObject == ins.mLinkerObject && mIns[at].mAddress == ins.mAddress && + mIns[at - 1].mType == ASMIT_LDA && mIns[at - 1].mMode == ASMIM_IMMEDIATE) + { + return mIns[at - 1].mAddress; + } + if (at < 0 && mEntryBlocks.Size() == 1) + return mEntryBlocks[0]->FindImmediateGlobalStore(mEntryBlocks[0]->mIns.Size(), ins); + + return -1; +} + +bool NativeCodeBasicBlock::CheckPatchFailUse(void) +{ + if (mPatchStart) + return true; + + if (mPatchChecked) + return false; + + if (!mPatchFail) + { + mPatchFail = true; + + if (mTrueJump && !mTrueJump->CheckPatchFailUse()) + return false; + if (mFalseJump && !mFalseJump->CheckPatchFailUse()) + return false; + } + + return true; +} + +bool NativeCodeBasicBlock::CheckPatchFailReg(const NativeCodeBasicBlock* block, int reg) +{ + if (mPatched && mEntryRequiredRegs[reg]) + return false; + + if (!mPatchFail) + { + mPatchFail = true; + + if (this != block) + { + if (mTrueJump && !mTrueJump->CheckPatchFailReg(block, reg)) + return false; + if (mFalseJump && !mFalseJump->CheckPatchFailReg(block, reg)) + return false; + } + } + + return true; +} + +bool NativeCodeBasicBlock::CheckPatchFailRegPair(const NativeCodeBasicBlock* block, int reg) +{ + if (mPatched && (mEntryRequiredRegs[reg] || mEntryRequiredRegs[reg + 1])) + return false; + + if (!mPatchFail) + { + mPatchFail = true; + + if (this != block) + { + if (mTrueJump && !mTrueJump->CheckPatchFailRegPair(block, reg)) + return false; + if (mFalseJump && !mFalseJump->CheckPatchFailRegPair(block, reg)) + return false; + } + } + + return true; +} + +bool NativeCodeBasicBlock::CheckSingleUseGlobalLoad(const NativeCodeBasicBlock* block, int reg, int at, const NativeCodeInstruction& ains, int cycles) +{ + if (!mPatched) + { + if (at == 0) + { + mPatched = true; + + if (!mEntryRequiredRegs[reg]) + { + mPatchFail = true; + +// if (mExitRequiredRegs[reg]) + { + if (mTrueJump && !mTrueJump->CheckPatchFailReg(block, reg)) + return false; + if (mFalseJump && !mFalseJump->CheckPatchFailReg(block, reg)) + return false; + } + return true; + } + + assert(mNumEntries == mEntryBlocks.Size()); + + if (mNumEntries > 1) + { + if (mLoopHead) + return false; + + for (int i = 0; i < mEntryBlocks.Size(); i++) + if (!mEntryBlocks[i]->IsDominatedBy(block)) + return false; + } + } + + if (mPatchFail) + return false; + + while (at < mIns.Size()) + { + NativeCodeInstruction& ins(mIns[at]); + + if (ains.mFlags & ins.mFlags & NCIF_VOLATILE) + return false; + + if (ins.mMode == ASMIM_ZERO_PAGE && ins.mAddress == reg) + { + if (ins.UsesAddress()) + { + if (ins.ChangesAddress()) + return false; + if (!HasAsmInstructionMode(ins.mType, ains.mMode)) + return false; + if (ins.mLive & LIVE_MEM) + { + cycles--; + if (cycles == 0) + return false; + } + else + { +// if (mExitRequiredRegs[reg]) + { + if (mTrueJump && !mTrueJump->CheckPatchFailReg(block, reg)) + return false; + if (mFalseJump && !mFalseJump->CheckPatchFailReg(block, reg)) + return false; + } + + return true; + } + } + else + { +// if (mExitRequiredRegs[reg]) + { + if (mTrueJump && !mTrueJump->CheckPatchFailReg(block, reg)) + return false; + if (mFalseJump && !mFalseJump->CheckPatchFailReg(block, reg)) + return false; + } + + return true; + } + } + else if (ins.mType == ASMIT_JSR) + { + if (ains.mMode == ASMIM_ABSOLUTE_X || ains.mMode == ASMIM_ABSOLUTE_Y || ains.mMode == ASMIM_INDIRECT_Y) + return false; + else if (ins.mFlags & NCIF_RUNTIME) + { + if (ins.UsesZeroPage(reg)) + return false; + } + else if (ains.mFlags & NCIF_VOLATILE) + return false; + else if (ins.mLinkerObject && (ins.mLinkerObject->mFlags & LOBJF_NO_GLOBAL_STORES) && !ins.UsesZeroPage(reg)) + { + + } + else + return false; + } + else if (ains.mMode == ASMIM_ABSOLUTE_X && ins.ChangesXReg()) + return false; + else if (ains.mMode == ASMIM_ABSOLUTE_Y && ins.ChangesYReg()) + return false; + else if (ains.mMode == ASMIM_INDIRECT_Y && (ins.ChangesYReg() || ins.ChangesZeroPage(ains.mAddress) || ins.ChangesZeroPage(ains.mAddress + 1))) + return false; + else if (ins.mMode == ASMIM_INDIRECT_Y && (ins.mAddress == reg || ins.mAddress + 1 == reg)) + return false; + else if (ins.ChangesZeroPage(reg)) + return true; + else if (ains.MayBeChangedOnAddress(ins)) + return false; + + at++; + } + + if (mTrueJump && !mTrueJump->CheckSingleUseGlobalLoad(block, reg, 0, ains, cycles)) + return false; + if (mFalseJump && !mFalseJump->CheckSingleUseGlobalLoad(block, reg, 0, ains, cycles)) + return false; + } + + return true; +} + +bool NativeCodeBasicBlock::PatchSingleUseGlobalLoad(const NativeCodeBasicBlock* block, int reg, int at, const NativeCodeInstruction& ains) +{ + bool changed = false; + + if (!mPatched) + { + mPatched = true; + + if (at == 0 && !mEntryRequiredRegs[reg]) + return false; + + if (at == 0) + { + if (ains.RequiresXReg()) + mEntryRequiredRegs += CPU_REG_X; + if (ains.RequiresYReg()) + mEntryRequiredRegs += CPU_REG_Y; + if (ains.mMode == ASMIM_INDIRECT_Y) + { + mEntryRequiredRegs += ains.mAddress; + mEntryRequiredRegs += ains.mAddress + 1; + } + } + + while (at < mIns.Size()) + { + NativeCodeInstruction& ins(mIns[at]); + + if (ins.mMode == ASMIM_ZERO_PAGE && ins.mAddress == reg) + { + if (ins.UsesAddress()) + { + ins.CopyMode(ains); + if (!(ins.mLive & LIVE_MEM)) + { + if (ains.mMode == ASMIM_INDIRECT_Y) + ins.mLive |= LIVE_MEM; + return true; + } + changed = true; + } + else + return changed; + } + + if (ains.mMode == ASMIM_ABSOLUTE_X) + ins.mLive |= LIVE_CPU_REG_X; + if (ains.mMode == ASMIM_ABSOLUTE_Y || ains.mMode == ASMIM_INDIRECT_Y) + ins.mLive |= LIVE_CPU_REG_Y; + if (ains.mMode == ASMIM_INDIRECT_Y) + ins.mLive |= LIVE_MEM; + + at++; + } + + if (ains.RequiresXReg()) + mExitRequiredRegs += CPU_REG_X; + if (ains.RequiresYReg()) + mExitRequiredRegs += CPU_REG_Y; + if (ains.mMode == ASMIM_INDIRECT_Y) + { + mExitRequiredRegs += ains.mAddress; + mExitRequiredRegs += ains.mAddress + 1; + } + + if (mTrueJump && mTrueJump->PatchSingleUseGlobalLoad(block, reg, 0, ains)) + changed = true; + if (mFalseJump && mFalseJump->PatchSingleUseGlobalLoad(block, reg, 0, ains)) + changed = true; + } + + return changed; +} + + +bool NativeCodeBasicBlock::CheckSingleUseGlobalLoadStruct(const NativeCodeBasicBlock* block, const NativeCodeInstruction& rins, int at, const NativeCodeInstruction& ains, bool cleared, bool poisoned) +{ + bool ok = true; + if (!mPatched) + { + if (at == 0) + mPatched = true; + + if (this != block && !IsDominatedBy(block)) + poisoned = true; + + bool used = false; + + for (int i = at; i < mIns.Size() && !cleared; i++) + { + const NativeCodeInstruction& ins(mIns[i]); + + if (ins.SameEffectiveAddress(rins)) + { + if ((ins.mType == ASMIT_LDA || ins.mType == ASMIT_LDX || ins.mType == ASMIT_LDY) && HasAsmInstructionMode(ins.mType, ains.mMode)) + { + if (poisoned) + return false; + used = true; + } + else if (ins.mType == ASMIT_STA || ins.mType == ASMIT_STX || ins.mType == ASMIT_STY) + { + cleared = true; + poisoned = false; + } + else + return false; +// poisoned = true; + } + else if (poisoned && rins.MayBeSameAddress(ins)) + return false; + else if (rins.MayBeChangedOnAddress(ins, true) || ains.MayBeChangedOnAddress(ins, true)) + poisoned = true; + else if (ins.ChangesXReg() && ains.ReferencesXReg() || ins.ChangesYReg() && ains.ReferencesYReg()) + poisoned = true; + } + + if (this == block && at == 0) + { + mPatchUsed = used; + } + else + { + mPatchUsed = used || !cleared; + + if (mTrueJump && !mTrueJump->CheckSingleUseGlobalLoadStruct(block, rins, 0, ains, cleared, poisoned)) + return false; + if (mFalseJump && !mFalseJump->CheckSingleUseGlobalLoadStruct(block, rins, 0, ains, cleared, poisoned)) + return false; + + mPatchUsed = used || (mTrueJump && mTrueJump->mPatchUsed) || (mFalseJump && mFalseJump->mPatchUsed); + } + } + else if (mPatchUsed && (poisoned || cleared)) + return false; + + return true; +} + +bool NativeCodeBasicBlock::PatchSingleUseGlobalLoadStruct(const NativeCodeBasicBlock* block, const NativeCodeInstruction& rins, int at, const NativeCodeInstruction& ains) +{ + bool changed = false; + if (!mPatched) + { + if (at == 0) + mPatched = true; + + int finat = 0; + bool cleared = false; + for (int i = at; i < mIns.Size() && !cleared; i++) + { + NativeCodeInstruction& ins(mIns[i]); + + if (ins.SameEffectiveAddress(rins)) + { + if (ins.mType == ASMIT_LDA || ins.mType == ASMIT_LDX || ins.mType == ASMIT_LDY) + { + finat = i; + ins.CopyMode(ains); + changed = true; + } + else if (ins.mType == ASMIT_STA || ins.mType == ASMIT_STX || ins.mType == ASMIT_STY) + cleared = true; + } + } + + if (!cleared) + { + if (mTrueJump && mTrueJump->PatchSingleUseGlobalLoadStruct(block, rins, 0, ains)) + { + finat = mIns.Size(); + changed = true; + } + if (mFalseJump && mFalseJump->PatchSingleUseGlobalLoadStruct(block, rins, 0, ains)) + { + finat = mIns.Size(); + changed = true; + } + } + + if (changed) + { + int live = 0; + if (ains.mMode == ASMIM_ABSOLUTE_X) + { + live |= LIVE_CPU_REG_X; + if (at == 0) + mEntryRequiredRegs += CPU_REG_X; + if (finat == mIns.Size()) + mExitRequiredRegs += CPU_REG_X; + + } + else if (ains.mMode == ASMIM_ABSOLUTE_Y || ains.mMode == ASMIM_INDIRECT_Y) + { + live |= LIVE_CPU_REG_Y; + if (at == 0) + mEntryRequiredRegs += CPU_REG_Y; + if (finat == mIns.Size()) + mExitRequiredRegs += CPU_REG_Y; + } + + if (live) + { + for (int i = at; i < finat; i++) + mIns[i].mLive |= live; + } + } + } + + return changed; +} + + +bool NativeCodeBasicBlock::CheckForwardLowYPointer(const NativeCodeBasicBlock* block, int reg, int yreg, int at, int yval) +{ + // Checking only current block as first optimization step + + while (at < mIns.Size()) + { + NativeCodeInstruction& ins(mIns[at]); + + if (ins.mMode == ASMIM_ZERO_PAGE && (ins.mAddress == reg || ins.mAddress == yreg)) + return false; + else if (ins.mMode == ASMIM_INDIRECT_Y && ins.mAddress == reg) + { + if (yval != 0) + return false; + else if (!(ins.mLive & LIVE_MEM)) + return true; + } + + if (ins.mType == ASMIT_LDY && ins.mMode == ASMIM_IMMEDIATE) + yval = ins.mAddress; + else if (ins.mType == ASMIT_INY && yval >= 0) + yval = (yval + 1) & 255; + else if (ins.mType == ASMIT_DEY && yval >= 0) + yval = (yval - 1) & 255; + else if (ins.mType == ASMIT_JSR) + { + if (ins.UsesZeroPage(reg) || ins.ChangesZeroPage(yreg)) + return false; + yval = -1; + } + else if (ins.ChangesZeroPage(reg) || ins.ChangesZeroPage(yreg)) + return false; + else if (ins.ChangesYReg()) + yval = -1; + + at++; + } + + return false; +} + +bool NativeCodeBasicBlock::PatchForwardLowYPointer(const NativeCodeBasicBlock* block, int reg, int yreg, int at, int yval) +{ + bool changed = false; + + while (at < mIns.Size()) + { + NativeCodeInstruction& ins(mIns[at]); + + if (ins.mMode == ASMIM_INDIRECT_Y && ins.mAddress == reg) + { + bool done = !(ins.mLive & LIVE_MEM); + + if (ins.mLive & LIVE_CPU_REG_Y) + mIns.Insert(at + 1, NativeCodeInstruction(ins.mIns, ASMIT_LDY, ASMIM_IMMEDIATE, 0)); + mIns.Insert(at, NativeCodeInstruction(ins.mIns, ASMIT_LDY, ASMIM_ZERO_PAGE, yreg)); + at++; + + changed = true; + + if (done) + return changed; + } + + at++; + } + + return changed; +} + +bool NativeCodeBasicBlock::CheckForwardSumYPointer(const NativeCodeBasicBlock* block, int reg, int base, const NativeCodeInstruction& iins, int at, int yval, int ymax) +{ + if (!mPatched) + { + if (at == 0) + { + mPatched = true; + + if (!mEntryRequiredRegs[reg] && !mEntryRequiredRegs[reg + 1]) + { + mPatchFail = true; + + if (mTrueJump && !mTrueJump->CheckPatchFailRegPair(block, reg)) + return false; + if (mFalseJump && !mFalseJump->CheckPatchFailRegPair(block, reg)) + return false; + + return true; + } + + if (mNumEntries > 1) + { + for (int i = 0; i < mEntryBlocks.Size(); i++) + if (!mEntryBlocks[i]->IsDominatedBy(block)) + return false; + yval = -1; + } + } + + if (mPatchFail) + return false; + + while (at < mIns.Size()) + { + NativeCodeInstruction& ins(mIns[at]); + + if (ins.mMode == ASMIM_ZERO_PAGE && (ins.mAddress == reg || ins.mAddress == reg + 1)) + return false; + else if (ins.mMode == ASMIM_INDIRECT_Y && ins.mAddress == reg) + { + if (iins.mMode == ASMIM_IMMEDIATE && yval + iins.mAddress > 255) + return false; + if (yval < 0) yval = RetrieveYValue(at, 2); + if (yval < 0 || yval > ymax) + return false; + else if (!(ins.mLive & LIVE_MEM)) + return true; + if (yval == 0) + { + if (ins.mIns) + { + if (ins.mIns->mCode == IC_LOAD) + ymax = intmax(InterTypeSize[ins.mIns->mDst.mType] - 1, 255 - iins.mMaxVal); + else if (ins.mIns->mCode == IC_STORE) + ymax = intmax(InterTypeSize[ins.mIns->mSrc[0].mType] - 1, 255 - iins.mMaxVal); + } + } + } + if (ins.mType == ASMIT_LDY && ins.mMode == ASMIM_IMMEDIATE) + yval = ins.mAddress; + else if (ins.mType == ASMIT_INY && yval >= 0) + yval = (yval + 1) & 255; + else if (ins.mType == ASMIT_DEY && yval >= 0) + yval = (yval - 1) & 255; + else if (ins.mType == ASMIT_JSR) + { + if (ins.UsesZeroPage(reg) || ins.UsesZeroPage(reg + 1) || ins.ChangesZeroPage(base) || ins.ChangesZeroPage(base + 1) || iins.MayBeChangedOnAddress(ins)) + return false; + yval = -1; + } + else if (ins.mType == ASMIT_RTS) + { + if ((ins.mFlags & NCIF_LOWER) && (base == BC_REG_ACCU || reg == BC_REG_ACCU)) + return false; + } + else if (ins.ChangesZeroPage(base) || ins.ChangesZeroPage(base + 1) || iins.MayBeChangedOnAddress(ins)) + return false; + else if (ins.ChangesYReg()) + yval = -1; + + at++; + } + + if (mTrueJump && !mTrueJump->CheckForwardSumYPointer(block, reg, base, iins, 0, yval, ymax)) + return false; + if (mFalseJump && !mFalseJump->CheckForwardSumYPointer(block, reg, base, iins, 0, yval, ymax)) + return false; + } + + return true; +} + +bool NativeCodeBasicBlock::PatchForwardSumYPointer(const NativeCodeBasicBlock* block, int reg, int base, const NativeCodeInstruction& iins, int at, int yval) +{ + bool changed = false; + + if (!mPatched) + { + mPatched = true; + if (at == 0 && !mEntryRequiredRegs[reg] && !mEntryRequiredRegs[reg + 1]) + return false; + + while (at < mIns.Size()) + { + NativeCodeInstruction& ins(mIns[at]); + + assert(!(ins.mMode == ASMIM_ZERO_PAGE && (ins.mAddress == reg || ins.mAddress == reg + 1))); + + if (ins.mMode == ASMIM_INDIRECT_Y && ins.mAddress == reg) + { + assert(yval >= 0); + + bool done = !(ins.mLive & LIVE_MEM); + + ins.mAddress = base; + ins.mFlags &= ~NCIF_YZERO; + + const InterInstruction* inins(iins.mIns); + + if (ins.mLive & LIVE_CPU_REG_Y) + { + mIns.Insert(at + 1, NativeCodeInstruction(inins, ASMIT_LDY, ASMIM_IMMEDIATE, yval)); + if (mIns[at].mLive & LIVE_CPU_REG_Z) + { + mIns[at].mLive |= LIVE_CPU_REG_A; + mIns.Insert(at + 2, NativeCodeInstruction(inins, ASMIT_ORA, ASMIM_IMMEDIATE, 0)); + } + } + + mIns.Insert(at, NativeCodeInstruction(inins, ASMIT_LDY, iins)); + mIns[at].mMinVal = iins.mMinVal; + mIns[at].mMaxVal = iins.mMaxVal; + + at++; + for (int i = 0; i < yval; i++) + { + mIns.Insert(at, NativeCodeInstruction(inins, ASMIT_INY, ASMIM_IMPLIED)); + at++; + } + + changed = true; + + if (done) + return changed; + } + else if (ins.mType == ASMIT_LDY && ins.mMode == ASMIM_IMMEDIATE) + yval = ins.mAddress; + else if (ins.mType == ASMIT_INY && yval >= 0) + yval = (yval + 1) & 255; + else if (ins.mType == ASMIT_DEY && yval >= 0) + yval = (yval - 1) & 255; + else if (ins.ChangesYReg()) + yval = -1; + + at++; + } + + if (mTrueJump && mTrueJump->PatchForwardSumYPointer(block, reg, base, iins, 0, yval)) + changed = true; + if (mFalseJump && mFalseJump->PatchForwardSumYPointer(block, reg, base, iins, 0, yval)) + changed = true; + + if (changed) + { + mEntryRequiredRegs += base; + mEntryRequiredRegs += base + 1; + if (iins.mMode == ASMIM_ZERO_PAGE) + mEntryRequiredRegs += iins.mAddress; + } + } + + return changed; +} + + +bool NativeCodeBasicBlock::CrossBlock16BitFlood(NativeCodeProcedure* proc) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + for (int i = 0; i + 3 < mIns.Size(); i++) + { + if (mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && !(mIns[i + 0].mLive & LIVE_MEM) && + mIns[i + 1].mType == ASMIT_STA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + mIns[i + 2].mType == ASMIT_LDA && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && !(mIns[i + 2].mLive & LIVE_MEM) && mIns[i + 2].mAddress == mIns[i + 0].mAddress + 1 && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && mIns[i + 3].mAddress == mIns[i + 1].mAddress + 1) + { + proc->ResetPatched(); + if (CheckCrossBlock16BitFlood(this, mIns[i + 0].mAddress, mIns[i + 1].mAddress, i + 4, false)) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 3].mType = ASMIT_NOP; mIns[i + 3].mMode = ASMIM_IMPLIED; + mIns[i + 2].mLive |= LIVE_MEM; + + proc->ResetPatched(); + if (PatchCrossBlock16BitFlood(this, mIns[i + 0].mAddress, mIns[i + 1].mAddress, i + 4)) + { + changed = true; + } + } + } + } + + if (mTrueJump && mTrueJump->CrossBlock16BitFlood(proc)) + changed = true; + if (mFalseJump && mFalseJump->CrossBlock16BitFlood(proc)) + changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::CheckCrossBlock16BitFlood(const NativeCodeBasicBlock* block, int sreg, int dreg, int at, bool rvalid) +{ + if (at == 0 && this == block) + return false; + + if (!mPatched) + { + mPatched = true; + + if (at == 0) + { + if (!mEntryRequiredRegs[dreg] && !mEntryRequiredRegs[dreg + 1]) + return true; + + if (mEntryRequiredRegs[sreg] || mEntryRequiredRegs[sreg + 1]) + return false; + + if (mNumEntries > 1) + { + for (int i = 0; i < mEntryBlocks.Size(); i++) + if (!mEntryBlocks[i]->CheckCrossBlock16BitFloodExit(block, sreg, dreg, rvalid)) + return false; + } + } + + while (at < mIns.Size()) + { + NativeCodeInstruction& ins(mIns[at]); + + if (ins.ReferencesZeroPage(sreg) || ins.ReferencesZeroPage(sreg + 1)) + return false; + else if ((ins.mType == ASMIT_JSR || ins.mType == ASMIT_RTS) && (ins.ReferencesZeroPage(dreg) || ins.ReferencesZeroPage(dreg + 1))) + return false; + else if (ins.mMode == ASMIM_INDIRECT_Y && ins.mAddress + 1 == dreg) + return false; + + at++; + } + + mPatchExit = true; + + if (mTrueJump && !mTrueJump->CheckCrossBlock16BitFlood(block, sreg, dreg, 0, rvalid)) + return false; + if (mFalseJump && !mFalseJump->CheckCrossBlock16BitFlood(block, sreg, dreg, 0, rvalid)) + return false; + + } + + return true; +} + +bool NativeCodeBasicBlock::CheckCrossBlock16BitFloodExit(const NativeCodeBasicBlock* block, int sreg, int dreg, bool rvalid) +{ + if (!mPatchExit) + { + mPatchExit = true; + + if (mTrueJump && !mTrueJump->CheckCrossBlock16BitFlood(block, sreg, dreg, 0, rvalid)) + return false; + if (mFalseJump && !mFalseJump->CheckCrossBlock16BitFlood(block, sreg, dreg, 0, rvalid)) + return false; + + int at = mIns.Size() - 1; + while (at >= 0) + { + NativeCodeInstruction& ins(mIns[at]); + + if (ins.ReferencesZeroPage(sreg) || ins.ReferencesZeroPage(sreg + 1)) + return false; + else if ((ins.mType == ASMIT_JSR || ins.mType == ASMIT_RTS) && (ins.ReferencesZeroPage(dreg) || ins.ReferencesZeroPage(dreg + 1))) + return false; + + at--; + } + + mPatched = true; + + if (mEntryBlocks.Size() == 0) + return false; + + for (int i = 0; i < mEntryBlocks.Size(); i++) + if (!mEntryBlocks[i]->CheckCrossBlock16BitFloodExit(block, sreg, dreg, rvalid)) + return false; + } + + return true; +} + +bool NativeCodeBasicBlock::PatchCrossBlock16BitFlood(const NativeCodeBasicBlock* block, int sreg, int dreg, int at) +{ + bool changed = false; + + if (!mPatched) + { + mPatched = true; + + if (at == 0) + { + if (!mEntryRequiredRegs[dreg] && !mEntryRequiredRegs[dreg + 1]) + return false; + + for (int i = 0; i < mEntryBlocks.Size(); i++) + if (mEntryBlocks[i]->PatchCrossBlock16BitFloodExit(block, sreg, dreg)) + changed = true; + + mEntryRequiredRegs += sreg; + mEntryRequiredRegs += sreg + 1; + } + + while (at < mIns.Size()) + { + NativeCodeInstruction& ins(mIns[at]); + + if (ins.mMode == ASMIM_ZERO_PAGE || ins.mMode == ASMIM_INDIRECT_Y) + { + if (ins.mAddress == dreg) + { + ins.mAddress = sreg; + changed = true; + } + else if (ins.mAddress == dreg + 1) + { + ins.mAddress = sreg + 1; + changed = true; + } + } + + at++; + } + + mPatchExit = true; + + mExitRequiredRegs += sreg; + mExitRequiredRegs += sreg + 1; + + if (mTrueJump && mTrueJump->PatchCrossBlock16BitFlood(block, sreg, dreg, 0)) + changed = true; + if (mFalseJump && mFalseJump->PatchCrossBlock16BitFlood(block, sreg, dreg, 0)) + changed = true; + } + + return changed; + +} + +bool NativeCodeBasicBlock::PatchCrossBlock16BitFloodExit(const NativeCodeBasicBlock* block, int sreg, int dreg) +{ + bool changed = false; + + if (!mPatchExit) + { + mPatchExit = true; + + mExitRequiredRegs += sreg; + mExitRequiredRegs += sreg + 1; + + if (mTrueJump && mTrueJump->PatchCrossBlock16BitFlood(block, sreg, dreg, 0)) + changed = true; + if (mFalseJump && mFalseJump->PatchCrossBlock16BitFlood(block, sreg, dreg, 0)) + changed = true; + + int at = mIns.Size() - 1; + while (at >= 0) + { + NativeCodeInstruction& ins(mIns[at]); + + if (ins.mMode == ASMIM_ZERO_PAGE || ins.mMode == ASMIM_ZERO_PAGE_X || ins.mMode == ASMIM_ZERO_PAGE_Y || ins.mMode == ASMIM_INDIRECT_X || ins.mMode == ASMIM_INDIRECT_Y) + { + if (ins.mAddress == dreg) + { + ins.mAddress = sreg; + changed = true; + } + else if (ins.mAddress == dreg + 1) + { + ins.mAddress = sreg + 1; + changed = true; + } + } + + at--; + } + + if (mEntryRequiredRegs[dreg] || mEntryRequiredRegs[dreg + 1]) + { + mPatched = true; + mEntryRequiredRegs += sreg; + mEntryRequiredRegs += sreg + 1; + + for (int i = 0; i < mEntryBlocks.Size(); i++) + if (mEntryBlocks[i]->PatchCrossBlock16BitFloodExit(block, sreg, dreg)) + changed = true; + } + + return changed; + } + + return false; +} + +bool NativeCodeBasicBlock::CrossBlockXYFlood(NativeCodeProcedure* proc) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + for (int i = 0; i < mIns.Size(); i++) + { + if ((mIns[i].mType == ASMIT_STA || mIns[i].mType == ASMIT_STX || mIns[i].mType == ASMIT_STY) && mIns[i].mMode == ASMIM_ZERO_PAGE && !(mIns[i].mLive & LIVE_CPU_REG_Z)) + { + proc->ResetPatched(); + if (mIns[i].mType != ASMIT_STY && !(mIns[i].mLive & LIVE_CPU_REG_X) && CheckCrossBlockXFlood(this, mIns[i].mAddress, i + 1, false)) + { + if (mIns[i].mType == ASMIT_STX) + mIns[i].mType = ASMIT_NOP; + else + mIns[i].mType = ASMIT_TAX; + mIns[i].mMode = ASMIM_IMPLIED; + mIns[i].mLive |= LIVE_CPU_REG_X; + + proc->ResetPatched(); + if (PatchCrossBlockXFlood(this, mIns[i].mAddress, i + 1)) + { + changed = true; + } + } + else + { +#if 1 + proc->ResetPatched(); + if (mIns[i].mType == ASMIT_STX && CheckCrossBlockXFlood(this, mIns[i].mAddress, i + 1, true)) + { + mIns[i].mType = ASMIT_NOP; + mIns[i].mMode = ASMIM_IMPLIED; + mIns[i].mLive |= LIVE_CPU_REG_X; + + proc->ResetPatched(); + if (PatchCrossBlockXFlood(this, mIns[i].mAddress, i + 1)) + { + changed = true; + } + } + else +#endif + { + proc->ResetPatched(); + if (mIns[i].mType != ASMIT_STX && !(mIns[i].mLive & LIVE_CPU_REG_Y) && CheckCrossBlockYFlood(this, mIns[i].mAddress, i + 1, false)) + { + if (mIns[i].mType == ASMIT_STY) + mIns[i].mType = ASMIT_NOP; + else + mIns[i].mType = ASMIT_TAY; + mIns[i].mMode = ASMIM_IMPLIED; + mIns[i].mLive |= LIVE_CPU_REG_Y; + + proc->ResetPatched(); + if (PatchCrossBlockYFlood(this, mIns[i].mAddress, i + 1)) + { + changed = true; + } + } + } + } + } + } + + if (mTrueJump && mTrueJump->CrossBlockXYFlood(proc)) + changed = true; + if (mFalseJump && mFalseJump->CrossBlockXYFlood(proc)) + changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::CheckCrossBlockXFlood(const NativeCodeBasicBlock* block, int reg, int at, bool rvalid) +{ + if (at == 0 && this == block) + return false; + + if (!mPatched) + { + mPatched = true; + + if (at == 0) + { + if (!mEntryRequiredRegs[reg]) + return true; + + if (mNumEntries > 1) + { + for (int i = 0; i < mEntryBlocks.Size(); i++) + if (!mEntryBlocks[i]->CheckCrossBlockXFloodExit(block, reg, rvalid)) + return false; + } + } + + while (at < mIns.Size()) + { + NativeCodeInstruction& ins(mIns[at]); + + if (ins.mMode == ASMIM_INDIRECT_Y && (ins.mAddress == reg || ins.mAddress == reg + 1)) + return false; + else if (ins.mMode == ASMIM_ZERO_PAGE && ins.mAddress == reg) + { + if (ins.mType == ASMIT_LDA) + { + if (!(ins.mLive & LIVE_MEM)) + return true; + } + else if (ins.mType == ASMIT_INC || ins.mType == ASMIT_DEC) + { + if (rvalid) return false; + } + else if (ins.mType == ASMIT_LDX) + { + return !(ins.mLive & (LIVE_MEM | LIVE_CPU_REG_Z)); + } + else + return false; + } + else if (ins.ChangesXReg() || (!rvalid && ins.RequiresXReg()) || ins.ChangesZeroPage(reg) || ins.ReferencesZeroPage(reg)) + return false; + + at++; + } + + mPatchExit = true; + + if (mTrueJump && !mTrueJump->CheckCrossBlockXFlood(block, reg, 0, rvalid)) + return false; + if (mFalseJump && !mFalseJump->CheckCrossBlockXFlood(block, reg, 0, rvalid)) + return false; + + } + + return true; +} + +bool NativeCodeBasicBlock::CheckCrossBlockXFloodExit(const NativeCodeBasicBlock* block, int reg, bool rvalid) +{ + if (!mPatchExit) + { + mPatchExit = true; + + if (mTrueJump && !mTrueJump->CheckCrossBlockXFlood(block, reg, 0, rvalid)) + return false; + if (mFalseJump && !mFalseJump->CheckCrossBlockXFlood(block, reg, 0, rvalid)) + return false; + + int at = mIns.Size() - 1; + while (at >= 0) + { + NativeCodeInstruction& ins(mIns[at]); + + if (ins.mMode == ASMIM_INDIRECT_Y && (ins.mAddress == reg || ins.mAddress == reg + 1)) + return false; + else if (ins.mMode == ASMIM_ZERO_PAGE && ins.mAddress == reg) + { + if (ins.mType == ASMIT_LDA) + { + } + else if (ins.mType == ASMIT_INC || ins.mType == ASMIT_DEC) + { + if (rvalid) return false; + } + else if (ins.mType == ASMIT_STA || ins.mType == ASMIT_STX) + { + return !(ins.mLive & (LIVE_CPU_REG_Z | LIVE_CPU_REG_X)); + } + else + return false; + } + else if (ins.ChangesXReg() || (!rvalid && ins.RequiresXReg()) || ins.ChangesZeroPage(reg) || ins.ReferencesZeroPage(reg)) + return false; + + at--; + } + + mPatched = true; + + if (mEntryBlocks.Size() == 0) + return false; + + for (int i = 0; i < mEntryBlocks.Size(); i++) + if (!mEntryBlocks[i]->CheckCrossBlockXFloodExit(block, reg, rvalid)) + return false; + } + + return true; +} + +bool NativeCodeBasicBlock::PatchCrossBlockXFloodExit(const NativeCodeBasicBlock* block, int reg) +{ + bool changed = false; + + if (!mPatchExit) + { + mPatchExit = true; + + mExitRequiredRegs += CPU_REG_X; + + if (mTrueJump && mTrueJump->PatchCrossBlockXFlood(block, reg, 0)) + changed = true; + if (mFalseJump && mFalseJump->PatchCrossBlockXFlood(block, reg, 0)) + changed = true; + + int at = mIns.Size() - 1; + while (at >= 0) + { + NativeCodeInstruction& ins(mIns[at]); + + ins.mLive |= LIVE_CPU_REG_X; + + if (ins.mMode == ASMIM_ZERO_PAGE && ins.mAddress == reg) + { + if (ins.mType == ASMIT_LDA) + { + ins.mType = ASMIT_TXA; + ins.mMode = ASMIM_IMPLIED; + changed = true; + } + else if (ins.mType == ASMIT_INC) + { + ins.mType = ASMIT_INX; + ins.mMode = ASMIM_IMPLIED; + changed = true; + } + else if (ins.mType == ASMIT_DEC) + { + ins.mType = ASMIT_DEX; + ins.mMode = ASMIM_IMPLIED; + changed = true; + } + else if (ins.mType == ASMIT_STA) + { + ins.mType = ASMIT_TAX; + ins.mMode = ASMIM_IMPLIED; + return true; + } + else if (ins.mType == ASMIT_STX) + { + ins.mType = ASMIT_NOP; + ins.mMode = ASMIM_IMPLIED; + return true; + } + } + + at--; + } + + mPatched = true; + mEntryRequiredRegs += CPU_REG_X; + + for (int i = 0; i < mEntryBlocks.Size(); i++) + if (mEntryBlocks[i]->PatchCrossBlockXFloodExit(block, reg)) + changed = true; + + return changed; + } + + return false; +} + +bool NativeCodeBasicBlock::PatchCrossBlockXFlood(const NativeCodeBasicBlock* block, int reg, int at) +{ + bool changed = false; + + if (!mPatched) + { + mPatched = true; + + if (at == 0) + { + if (!mEntryRequiredRegs[reg]) + return false; + for (int i = 0; i < mEntryBlocks.Size(); i++) + if (mEntryBlocks[i]->PatchCrossBlockXFloodExit(block, reg)) + changed = true; + mEntryRequiredRegs += CPU_REG_X; + } + + while (at < mIns.Size()) + { + NativeCodeInstruction& ins(mIns[at]); + + if (ins.mMode == ASMIM_ZERO_PAGE && ins.mAddress == reg) + { + if (ins.mType == ASMIT_LDA) + { + ins.mType = ASMIT_TXA; + ins.mMode = ASMIM_IMPLIED; + if (!(ins.mLive & LIVE_MEM)) + return true; + changed = true; + } + else if (ins.mType == ASMIT_INC) + { + ins.mType = ASMIT_INX; + ins.mMode = ASMIM_IMPLIED; + changed = true; + } + else if (ins.mType == ASMIT_DEC) + { + ins.mType = ASMIT_DEX; + ins.mMode = ASMIM_IMPLIED; + changed = true; + } + else if (ins.mType == ASMIT_LDX) + { + ins.mType = ASMIT_NOP; + ins.mMode = ASMIM_IMPLIED; + return true; + } + } + + ins.mLive |= LIVE_CPU_REG_X; + + at++; + } + + mPatchExit = true; + + mExitRequiredRegs |= CPU_REG_X; + + if (mTrueJump && mTrueJump->PatchCrossBlockXFlood(block, reg, 0)) + changed = true; + if (mFalseJump && mFalseJump->PatchCrossBlockXFlood(block, reg, 0)) + changed = true; + } + + return changed; +} + + +bool NativeCodeBasicBlock::CheckCrossBlockYFlood(const NativeCodeBasicBlock* block, int reg, int at, bool rvalid) +{ + if (at == 0 && this == block) + return false; + + if (!mPatched) + { + mPatched = true; + + if (at == 0) + { + if (!mEntryRequiredRegs[reg]) + return true; + + if (mNumEntries > 1) + { + for (int i = 0; i < mEntryBlocks.Size(); i++) + if (!mEntryBlocks[i]->CheckCrossBlockYFloodExit(block, reg, false)) + return false; +} + } + + while (at < mIns.Size()) + { + NativeCodeInstruction& ins(mIns[at]); + + if (ins.mMode == ASMIM_INDIRECT_Y && (ins.mAddress == reg || ins.mAddress == reg + 1)) + return false; + else if (ins.mMode == ASMIM_ZERO_PAGE && ins.mAddress == reg) + { + if (ins.mType == ASMIT_LDA) + { + if (!(ins.mLive & LIVE_MEM)) + return true; + } + else if (ins.mType == ASMIT_INC || ins.mType == ASMIT_DEC) + { + rvalid = false; + } + else if (ins.mType == ASMIT_LDY) + { + return !(ins.mLive & (LIVE_MEM | LIVE_CPU_REG_Z)); + } + else + return false; + } + else if (ins.ChangesYReg() || (!rvalid && ins.RequiresYReg()) || ins.ChangesZeroPage(reg) || ins.ReferencesZeroPage(reg)) + return false; + + at++; + } + + mPatchExit = true; + + if (mTrueJump && !mTrueJump->CheckCrossBlockYFlood(block, reg, 0, rvalid)) + return false; + if (mFalseJump && !mFalseJump->CheckCrossBlockYFlood(block, reg, 0, rvalid)) + return false; + + } + + return true; +} + +bool NativeCodeBasicBlock::CheckCrossBlockYFloodExit(const NativeCodeBasicBlock* block, int reg, bool rvalid) +{ + if (!mPatchExit) + { + mPatchExit = true; + + if (mTrueJump && !mTrueJump->CheckCrossBlockYFlood(block, reg, 0, false)) + return false; + if (mFalseJump && !mFalseJump->CheckCrossBlockYFlood(block, reg, 0, false)) + return false; + + int at = mIns.Size() - 1; + while (at >= 0) + { + NativeCodeInstruction& ins(mIns[at]); + + if (ins.mMode == ASMIM_INDIRECT_Y && (ins.mAddress == reg || ins.mAddress == reg + 1)) + return false; + else if (ins.mMode == ASMIM_ZERO_PAGE && ins.mAddress == reg) + { + if (ins.mType == ASMIT_LDA) + { + } + else if (ins.mType == ASMIT_INC || ins.mType == ASMIT_DEC) + { + } + else if (ins.mType == ASMIT_STA || ins.mType == ASMIT_STY) + { + return !(ins.mLive & (LIVE_CPU_REG_Z | LIVE_CPU_REG_Y)); + } + else + return false; + } + else if (ins.ChangesYReg() || ins.RequiresYReg() || ins.ChangesZeroPage(reg) || ins.ReferencesZeroPage(reg)) + return false; + + at--; + } + + mPatched = true; + + if (mEntryBlocks.Size() == 0) + return false; + + for (int i = 0; i < mEntryBlocks.Size(); i++) + if (!mEntryBlocks[i]->CheckCrossBlockYFloodExit(block, reg, false)) + return false; + } + + return true; +} + +bool NativeCodeBasicBlock::PatchCrossBlockYFloodExit(const NativeCodeBasicBlock* block, int reg) +{ + bool changed = false; + + if (!mPatchExit) + { + mPatchExit = true; + + mExitRequiredRegs += CPU_REG_Y; + + if (mTrueJump && mTrueJump->PatchCrossBlockYFlood(block, reg, 0)) + changed = true; + if (mFalseJump && mFalseJump->PatchCrossBlockYFlood(block, reg, 0)) + changed = true; + + int at = mIns.Size() - 1; + while (at >= 0) + { + NativeCodeInstruction& ins(mIns[at]); + + ins.mLive |= LIVE_CPU_REG_Y; + + if (ins.mMode == ASMIM_ZERO_PAGE && ins.mAddress == reg) + { + if (ins.mType == ASMIT_LDA) + { + ins.mType = ASMIT_TYA; + ins.mMode = ASMIM_IMPLIED; + changed = true; + } + else if (ins.mType == ASMIT_INC) + { + ins.mType = ASMIT_INY; + ins.mMode = ASMIM_IMPLIED; + changed = true; + } + else if (ins.mType == ASMIT_DEC) + { + ins.mType = ASMIT_DEY; + ins.mMode = ASMIM_IMPLIED; + changed = true; + } + else if (ins.mType == ASMIT_STA) + { + ins.mType = ASMIT_TAY; + ins.mMode = ASMIM_IMPLIED; + return true; + } + else if (ins.mType == ASMIT_STY) + { + ins.mType = ASMIT_NOP; + ins.mMode = ASMIM_IMPLIED; + return true; + } + } + + at--; + } + + mPatched = true; + mEntryRequiredRegs += CPU_REG_Y; + + for (int i = 0; i < mEntryBlocks.Size(); i++) + if (mEntryBlocks[i]->PatchCrossBlockYFloodExit(block, reg)) + changed = true; + + return changed; + } + + return false; +} + +bool NativeCodeBasicBlock::PatchCrossBlockYFlood(const NativeCodeBasicBlock* block, int reg, int at) +{ + bool changed = false; + + if (!mPatched) + { + mPatched = true; + + if (at == 0) + { + if (!mEntryRequiredRegs[reg]) + return false; + for (int i = 0; i < mEntryBlocks.Size(); i++) + if (mEntryBlocks[i]->PatchCrossBlockYFloodExit(block, reg)) + changed = true; + mEntryRequiredRegs += CPU_REG_Y; + } + + while (at < mIns.Size()) + { + NativeCodeInstruction& ins(mIns[at]); + + if (ins.mMode == ASMIM_ZERO_PAGE && ins.mAddress == reg) + { + if (ins.mType == ASMIT_LDA) + { + ins.mType = ASMIT_TYA; + ins.mMode = ASMIM_IMPLIED; + if (!(ins.mLive & LIVE_MEM)) + return true; + changed = true; + } + else if (ins.mType == ASMIT_INC) + { + ins.mType = ASMIT_INY; + ins.mMode = ASMIM_IMPLIED; + changed = true; + } + else if (ins.mType == ASMIT_DEC) + { + ins.mType = ASMIT_DEY; + ins.mMode = ASMIM_IMPLIED; + changed = true; + } + else if (ins.mType == ASMIT_LDY) + { + ins.mType = ASMIT_NOP; + ins.mMode = ASMIM_IMPLIED; + return true; + } + } + + ins.mLive |= LIVE_CPU_REG_Y; + + at++; + } + + mPatchExit = true; + + mExitRequiredRegs |= CPU_REG_Y; + + if (mTrueJump && mTrueJump->PatchCrossBlockYFlood(block, reg, 0)) + changed = true; + if (mFalseJump && mFalseJump->PatchCrossBlockYFlood(block, reg, 0)) + changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::CrossBlockY2XFlood(NativeCodeProcedure* proc) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + for (int i = 0; i < mIns.Size(); i++) + { + if (mIns[i].mType == ASMIT_LDY && HasAsmInstructionMode(ASMIT_LDX, mIns[i].mMode) && !(mIns[i].mLive & LIVE_CPU_REG_X)) + { + proc->ResetPatched(); + if (CheckCrossBlockY2XFlood(this, i + 1)) + { + mIns[i].mType = ASMIT_LDX; + mIns[i].mLive |= LIVE_CPU_REG_X; + + proc->ResetPatched(); + if (PatchCrossBlockY2XFlood(this, i + 1)) + { + changed = true; + } + } + } + } + + if (mTrueJump && mTrueJump->CrossBlockY2XFlood(proc)) + changed = true; + if (mFalseJump && mFalseJump->CrossBlockY2XFlood(proc)) + changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::CheckCrossBlockY2XFlood(const NativeCodeBasicBlock* block, int at) +{ + if (at == 0 && this == block) + return false; + + if (!mPatched) + { + mPatched = true; + + if (at == 0) + { + if (!mEntryRequiredRegs[CPU_REG_Y]) + return true; + + if (mNumEntries > 1) + { + for (int i = 0; i < mEntryBlocks.Size(); i++) + if (!mEntryBlocks[i]->CheckCrossBlockY2XFloodExit(block)) + return false; + } + } + + while (at < mIns.Size()) + { + NativeCodeInstruction& ins(mIns[at]); + + if (ins.mMode == ASMIM_INDIRECT_Y || ins.ReferencesXReg()) + return false; + else if (!(ins.mLive & LIVE_CPU_REG_Y)) + return true; + + at++; + } + + mPatchExit = true; + + if (mTrueJump && !mTrueJump->CheckCrossBlockY2XFlood(block, 0)) + return false; + if (mFalseJump && !mFalseJump->CheckCrossBlockY2XFlood(block, 0)) + return false; + } + + return true; +} + +bool NativeCodeBasicBlock::CheckCrossBlockY2XFloodExit(const NativeCodeBasicBlock* block) +{ + if (!mPatchExit) + { + mPatchExit = true; + + if (mExitRequiredRegs[CPU_REG_X]) return false; + + if (mTrueJump && !mTrueJump->CheckCrossBlockY2XFlood(block, 0)) + return false; + if (mFalseJump && !mFalseJump->CheckCrossBlockY2XFlood(block, 0)) + return false; + + int at = mIns.Size() - 1; + while (at >= 0) + { + NativeCodeInstruction& ins(mIns[at]); + + if (ins.mMode == ASMIM_INDIRECT_Y || ins.ReferencesXReg()) + return false; + else if (ins.mType == ASMIT_LDY || ins.mType == ASMIT_TAY) + return true; + + at--; + } + + mPatched = true; + + for (int i = 0; i < mEntryBlocks.Size(); i++) + if (!mEntryBlocks[i]->CheckCrossBlockY2XFloodExit(block)) + return false; + } + + return true; +} + +bool NativeCodeBasicBlock::PatchCrossBlockY2XFlood(const NativeCodeBasicBlock* block, int at) +{ + bool changed = false; + + if (!mPatched) + { + mPatched = true; + + if (at == 0) + { + if (!mEntryRequiredRegs[CPU_REG_Y]) + return false; + for (int i = 0; i < mEntryBlocks.Size(); i++) + if (mEntryBlocks[i]->PatchCrossBlockY2XFloodExit(block)) + changed = true; + mEntryRequiredRegs += CPU_REG_X; + } + + while (at < mIns.Size()) + { + NativeCodeInstruction& ins(mIns[at]); + + if (ins.ReplaceYRegWithXReg()) + changed = true; + + if (!(ins.mLive & LIVE_CPU_REG_Y)) + return changed; + + at++; + } + + mPatchExit = true; + + mExitRequiredRegs |= CPU_REG_X; + + if (mTrueJump && mTrueJump->PatchCrossBlockY2XFlood(block, 0)) + changed = true; + if (mFalseJump && mFalseJump->PatchCrossBlockY2XFlood(block, 0)) + changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::PatchCrossBlockY2XFloodExit(const NativeCodeBasicBlock* block) +{ + bool changed = false; + + if (!mPatchExit) + { + mPatchExit = true; + + mExitRequiredRegs += CPU_REG_X; + + if (mTrueJump && mTrueJump->PatchCrossBlockY2XFlood(block, 0)) + changed = true; + if (mFalseJump && mFalseJump->PatchCrossBlockY2XFlood(block, 0)) + changed = true; + + int at = mIns.Size() - 1; + while (at >= 0) + { + NativeCodeInstruction& ins(mIns[at]); + + if (ins.ReplaceYRegWithXReg()) + changed = true; + + if (ins.mType == ASMIT_LDX || ins.mType == ASMIT_TAX) + return changed; + + at--; + } + + mPatched = true; + mEntryRequiredRegs += CPU_REG_X; + + for (int i = 0; i < mEntryBlocks.Size(); i++) + if (mEntryBlocks[i]->PatchCrossBlockY2XFloodExit(block)) + changed = true; + + return changed; + } + + return false; +} + +bool NativeCodeBasicBlock::CanSwapAccuZP(int reg, int size) +{ + if (!mVisited) + { + mVisited = true; + + for (int i = 0; i < mIns.Size(); i++) + { + const NativeCodeInstruction& ins(mIns[i]); + if (ins.mType == ASMIT_JSR) + return false; + if (ins.mMode == ASMIM_INDIRECT_Y) + { + if (reg == ins.mAddress + 1) return false; + if (reg == ins.mAddress && size < 2) return false; + if (ins.mAddress > reg && ins.mAddress < reg + size) return false; + } + else if (ins.mMode == ASMIM_ZERO_PAGE_X || ins.mMode == ASMIM_ZERO_PAGE_Y) + return false; + } + + if (mTrueJump && !mTrueJump->CanSwapAccuZP(reg, size)) return false; + if (mFalseJump && !mFalseJump->CanSwapAccuZP(reg, size)) return false; + } + + return true; +} + +void NativeCodeBasicBlock::SwapAccuZP(int reg, int size) +{ + if (!mVisited) + { + mVisited = true; + + for (int i = 0; i < mIns.Size(); i++) + { + NativeCodeInstruction& ins(mIns[i]); + + if (ins.mMode == ASMIM_INDIRECT_Y) + { + if (ins.mAddress >= BC_REG_ACCU && ins.mAddress < BC_REG_ACCU + size) + ins.mAddress += reg - BC_REG_ACCU; + else if (ins.mAddress >= reg && ins.mAddress < reg + size) + ins.mAddress += BC_REG_ACCU - reg; + } + else if (ins.mMode == ASMIM_ZERO_PAGE) + { + if (ins.mAddress >= BC_REG_ACCU && ins.mAddress < BC_REG_ACCU + size) + ins.mAddress += reg - BC_REG_ACCU; + else if (ins.mAddress >= reg && ins.mAddress < reg + size) + ins.mAddress += BC_REG_ACCU - reg; + } + } + + if (mTrueJump) mTrueJump->SwapAccuZP(reg, size); + if (mFalseJump) mFalseJump->SwapAccuZP(reg, size); + } +} + + +bool NativeCodeBasicBlock::CheckNonAliasedLocalStore(int at, const NativeCodeInstruction& sins) +{ + if (mPatched) + return true; + + if (!at) + mPatched = true; + + while (at < mIns.Size()) + { + NativeCodeInstruction& ins(mIns[at]); + + if (ins.mMode == ASMIM_ABSOLUTE && ins.mAddress == sins.mAddress && ins.mLinkerObject == sins.mLinkerObject) + { + if (ins.UsesAddress()) + return false; + if (ins.ChangesAddress()) + break; + } + else if ((ins.mMode == ASMIM_ABSOLUTE_X || ins.mMode == ASMIM_ABSOLUTE_Y) && ins.mLinkerObject == sins.mLinkerObject) + return false; + + at++; + } + + if (mTrueJump && !mTrueJump->CheckNonAliasedLocalStore(0, sins)) + return false; + if (mFalseJump && !mFalseJump->CheckNonAliasedLocalStore(0, sins)) + return false; + + return true; +} + +bool NativeCodeBasicBlock::EliminateNonAliasedLocalStores(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + for (int i = 0; i < mIns.Size(); i++) + { + NativeCodeInstruction& ins(mIns[i]); + + if ((ins.mType == ASMIT_STA || ins.mType == ASMIT_STX || ins.mType == ASMIT_STY) && ins.mMode == ASMIM_ABSOLUTE && ins.mLinkerObject && (ins.mLinkerObject->mFlags & LOBJF_LOCAL_VAR) && ins.mLinkerObject->mVariable && !ins.mLinkerObject->mVariable->mAliased) + { + mProc->ResetPatched(); + if (CheckNonAliasedLocalStore(i + 1, ins)) + { + ins.mType = ASMIT_NOP; + ins.mMode = ASMIM_IMPLIED; + changed = true; + } + } + } + + if (mTrueJump && mTrueJump->EliminateNonAliasedLocalStores()) + changed = true; + if (mFalseJump && mFalseJump->EliminateNonAliasedLocalStores()) + changed = true; + + } + + return changed; +} + +void NativeCodeBasicBlock::PropagateAddGlobalCarry(void) +{ + if (!mVisited) + { + mVisited = true; + + // Accessing memory outside of a global array is undefined behaviour, so + // if we find an indirect memory access and the pointer arithmetic for the + // upper byte included an immediate address without an offset, the carry + // must be clear + + for (int i = 0; i < mIns.Size(); i++) + { + if (mIns[i].mMode == ASMIM_INDIRECT_Y) + { + int j = i - 1; + while (j > 0 && !(mIns[j].mMode == ASMIM_ZERO_PAGE && mIns[j].mAddress == mIns[i].mAddress + 1 && mIns[j].ChangesAddress())) + j--; + if (j > 0 && + mIns[j].mType == ASMIT_STA && + mIns[j - 1].mType == ASMIT_ADC && + mIns[j - 1].mMode == ASMIM_IMMEDIATE_ADDRESS && + (mIns[j - 1].mFlags & NCIF_UPPER) && + mIns[j - 1].mAddress == 0) + { + while (j < mIns.Size() && !mIns[j].ChangesCarry()) + j++; + if (j < mIns.Size() && mIns[j].mType == ASMIT_CLC) + mIns[j].mType = ASMIT_NOP; + } + } + } + + if (mTrueJump) mTrueJump->PropagateAddGlobalCarry(); + if (mFalseJump) mFalseJump->PropagateAddGlobalCarry(); + } +} + +bool NativeCodeBasicBlock::FindSelfModSource(NativeCodeBasicBlock* block, int reg, NativeCodeBasicBlock*& sblock, int& sat) +{ + for (;;) + { + for (int i = block->mIns.Size() - 1; i >= 0; i--) + { + if (block->mIns[i].ReferencesZeroPage(reg)) + { + if (block->mIns[i].IsStore() || block->mIns[i].IsLoad()) + { + sat = i; + sblock = block; + return true; + } + return false; + } + } + if (block->mEntryBlocks.Size() == 1) + { + block = block->mEntryBlocks[0]; + if (block->mFalseJump) + return false; + } + else if (block->mEntryBlocks.Size() == 2) + { + if (!block->mEntryBlocks[0]->mFalseJump && + block->mEntryBlocks[1]->mTrueJump == block && block->mEntryBlocks[1]->mFalseJump == block->mEntryBlocks[0] && + !block->mEntryBlocks[0]->ReferencesZeroPage(reg)) + { + block = block->mEntryBlocks[1]; + } + else if (!block->mEntryBlocks[1]->mFalseJump && + block->mEntryBlocks[0]->mTrueJump == block && block->mEntryBlocks[0]->mFalseJump == block->mEntryBlocks[1] && + !block->mEntryBlocks[1]->ReferencesZeroPage(reg)) + { + block = block->mEntryBlocks[0]; + } + else + return false; + } + else + return false; + } +} + +int NativeCodeBasicBlock::CalcBlockDiffY(void) const +{ + int diff = 0; + for (int i = 0; i < mIns.Size(); i++) + { + if (mIns[i].ChangesYReg()) + { + if (mIns[i].mType == ASMIT_INY) + diff++; + else if (mIns[i].mType == ASMIT_DEY) + diff--; + else + return 256; + } + } + return diff; +} + +int NativeCodeBasicBlock::CalcBlockDiffX(void) const +{ + int diff = 0; + for (int i = 0; i < mIns.Size(); i++) + { + if (mIns[i].ChangesXReg()) + { + if (mIns[i].mType == ASMIT_INX) + diff++; + else if (mIns[i].mType == ASMIT_DEX) + diff--; + else + return 256; + } + } + return diff; +} + +int NativeCodeBasicBlock::SimpleLoopNumIterations(void) +{ + if (mLoopHead && mFalseJump && mTrueJump == this && mNumEntries == 2) + { + NativeCodeBasicBlock* eblock = mEntryBlocks[0]; + if (eblock == this) + eblock = mEntryBlocks[1]; + + int sz = mIns.Size(); + int k = sz - 1; + if (mBranch == ASMIT_BNE || mBranch == ASMIT_BPL || mBranch == ASMIT_BMI) + { + while (k >= 0 && !mIns[k].ChangesZFlag()) + k--; + } + else if (mBranch == ASMIT_BCC || mBranch == ASMIT_BCS) + { + while (k >= 0 && !mIns[k].ChangesZFlag()) + k--; + } + else + return -1; + + if (k >= 0) + { + switch (mIns[k].mType) + { + case ASMIT_CPY: + if (mIns[k].mMode == ASMIM_IMMEDIATE) + { + int start = eblock->RetrieveYValue(); + if (start >= 0) + { + int diff = CalcBlockDiffY(); + if (diff != 256) + { + if (diff > 0 && start < mIns[k].mAddress && (mBranch == ASMIT_BCC || mBranch == ASMIT_BNE)) + return (mIns[k].mAddress - start) / diff; + else if (diff < 0 && start > mIns[k].mAddress && (mBranch == ASMIT_BCS || mBranch == ASMIT_BNE)) + return (start - mIns[k].mAddress) / -diff; + } + } + } + break; + case ASMIT_CPX: + if (mIns[k].mMode == ASMIM_IMMEDIATE) + { + int start = eblock->RetrieveXValue(); + if (start >= 0) + { + int diff = CalcBlockDiffX(); + if (diff != 256) + { + if (diff > 0 && start < mIns[k].mAddress && (mBranch == ASMIT_BCC || mBranch == ASMIT_BNE)) + return (mIns[k].mAddress - start) / diff; + else if (diff < 0 && start > mIns[k].mAddress && (mBranch == ASMIT_BCS || mBranch == ASMIT_BNE)) + return (start - mIns[k].mAddress) / -diff; + } + } + } + break; + case ASMIT_INY: + case ASMIT_DEY: + case ASMIT_TYA: + { + int start = eblock->RetrieveYValue(); + if (start >= 0) + { + int diff = CalcBlockDiffY(); + if (diff != 256) + { + if (diff > 0 && mBranch == ASMIT_BNE) + return (256 - start) / diff; + else if (diff < 0 && mBranch == ASMIT_BNE) + return start / -diff; + else if (diff < 0 && start < 128 && mBranch == ASMIT_BPL) + return (start + 1) / -diff; + else if (diff > 0 && start < 128 && mBranch == ASMIT_BPL) + return (128 - start) / diff; + } + } + } break; + case ASMIT_INX: + case ASMIT_DEX: + case ASMIT_TXA: + { + int start = eblock->RetrieveXValue(); + if (start >= 0) + { + int diff = CalcBlockDiffX(); + if (diff != 256) + { + if (diff > 0 && mBranch == ASMIT_BNE) + return (256 - start) / diff; + else if (diff < 0 && mBranch == ASMIT_BNE) + return start / -diff; + else if (diff < 0 && start < 128 && mBranch == ASMIT_BPL) + return (start + 1) / -diff; + else if (diff > 0 && start < 128 && mBranch == ASMIT_BPL) + return (128 - start) / diff; + } + } + } break; + } + } + } + + + return -1; +} + +bool NativeCodeBasicBlock::CollectSelfModTargets(void) +{ + bool changed = false; + + assert(mEntryRequiredRegs.Size() > 0); + + if (!mVisited) + { + mVisited = true; + + if (mLoopHead && mFalseJump && mTrueJump == this && mNumEntries == 2) + { + + bool simple = true; + for (int i = 0; i < mIns.Size(); i++) + { + if (mIns[i].mType == ASMIT_JSR) + simple = false; + } + + bool useA = false, useX = false, useY = false; + if (!mEntryRequiredRegs[CPU_REG_A]) + useA = true; + else if (!mEntryRequiredRegs[CPU_REG_X]) + useX = true; + else if (!mEntryRequiredRegs[CPU_REG_Y]) + useY = true; + + if (simple && (useA || useX || useY)) + { + NativeCodeBasicBlock* eblock = mEntryBlocks[0]; + if (eblock == this) + eblock = mEntryBlocks[1]; + + int nloops = SimpleLoopNumIterations(); + + if (nloops > 2) + { + for (int i = 0; i < mIns.Size(); i++) + { + if (mIns[i].mMode == ASMIM_INDIRECT_Y && HasAsmInstructionMode(mIns[i].mType, ASMIM_ABSOLUTE_Y)) + { + int uses = 0, addr = mIns[i].mAddress; + bool fail = false; + int cost = 0; + + if (mFalseJump->mEntryRequiredRegs[addr]) + cost -= 3; + if (mFalseJump->mEntryRequiredRegs[addr + 1]) + cost -= 3; + + for (int j = 0; j < mIns.Size(); j++) + { + if (mIns[j].mMode == ASMIM_ZERO_PAGE && (mIns[j].mAddress == addr || mIns[j].mAddress == addr + 1)) + { + if (mIns[j].ChangesAddress()) + { + fail = true; + break; + } + cost -= 3; + } + else if (mIns[j].mMode == ASMIM_INDIRECT_Y && mIns[j].mAddress == addr) + { + if (HasAsmInstructionMode(mIns[j].mType, ASMIM_ABSOLUTE_Y)) + uses++; + else + { + fail = true; + break; + } + } + } + + cost -= 8 * uses; + cost += uses * nloops; + + if (!fail && uses <= 2 && cost > 4) + { + for (int j = 0; j < mIns.Size(); j++) + { + if (mIns[j].mMode == ASMIM_INDIRECT_Y && mIns[j].mAddress == addr) + { + int modID = mProc->mSelfModSources.Size(); + mProc->mSelfModSources.Push(SelfModReference{ nullptr, 0 }); + + mIns[j].mMode = ASMIM_ABSOLUTE_Y; + mIns[j].mLinkerObject = mProc->mLinkerObject; + mIns[j].mAddress = modID; + mIns[j].mFlags |= NCIF_SELFMOD; + + NativeCodeBasicBlock* sblock; + int sat; + + if (FindSelfModSource(eblock, addr, sblock, sat)) + { + switch (sblock->mIns[sat].mType) + { + case ASMIT_LDA: + case ASMIT_STA: + sblock->mIns[sat].mLive |= LIVE_CPU_REG_A; + sblock->mIns.Insert(sat + 1, NativeCodeInstruction(mIns[i].mIns, ASMIT_STA, ASMIM_ABSOLUTE, 2 * modID + 0, mProc->mLinkerObject)); + break; + case ASMIT_LDX: + case ASMIT_STX: + sblock->mIns[sat].mLive |= LIVE_CPU_REG_X; + sblock->mIns.Insert(sat + 1, NativeCodeInstruction(mIns[i].mIns, ASMIT_STX, ASMIM_ABSOLUTE, 2 * modID + 0, mProc->mLinkerObject)); + break; + case ASMIT_LDY: + case ASMIT_STY: + sblock->mIns[sat].mLive |= LIVE_CPU_REG_Y; + sblock->mIns.Insert(sat + 1, NativeCodeInstruction(mIns[i].mIns, ASMIT_STY, ASMIM_ABSOLUTE, 2 * modID + 0, mProc->mLinkerObject)); + break; + } + } + else if (useA) + { + eblock->mIns.Push(NativeCodeInstruction(mIns[i].mIns, ASMIT_LDA, ASMIM_ZERO_PAGE, addr)); + eblock->mIns.Push(NativeCodeInstruction(mIns[i].mIns, ASMIT_STA, ASMIM_ABSOLUTE, 2 * modID + 0, mProc->mLinkerObject)); + } + else if (useX) + { + eblock->mIns.Push(NativeCodeInstruction(mIns[i].mIns, ASMIT_LDX, ASMIM_ZERO_PAGE, addr)); + eblock->mIns.Push(NativeCodeInstruction(mIns[i].mIns, ASMIT_STX, ASMIM_ABSOLUTE, 2 * modID + 0, mProc->mLinkerObject)); + } + else + { + eblock->mIns.Push(NativeCodeInstruction(mIns[i].mIns, ASMIT_LDY, ASMIM_ZERO_PAGE, addr)); + eblock->mIns.Push(NativeCodeInstruction(mIns[i].mIns, ASMIT_STY, ASMIM_ABSOLUTE, 2 * modID + 0, mProc->mLinkerObject)); + } + if (FindSelfModSource(eblock, addr + 1, sblock, sat)) + { + switch (sblock->mIns[sat].mType) + { + case ASMIT_LDA: + case ASMIT_STA: + sblock->mIns[sat].mLive |= LIVE_CPU_REG_A; + sblock->mIns.Insert(sat + 1, NativeCodeInstruction(mIns[i].mIns, ASMIT_STA, ASMIM_ABSOLUTE, 2 * modID + 1, mProc->mLinkerObject)); + break; + case ASMIT_LDX: + case ASMIT_STX: + sblock->mIns[sat].mLive |= LIVE_CPU_REG_X; + sblock->mIns.Insert(sat + 1, NativeCodeInstruction(mIns[i].mIns, ASMIT_STX, ASMIM_ABSOLUTE, 2 * modID + 1, mProc->mLinkerObject)); + break; + case ASMIT_LDY: + case ASMIT_STY: + sblock->mIns[sat].mLive |= LIVE_CPU_REG_Y; + sblock->mIns.Insert(sat + 1, NativeCodeInstruction(mIns[i].mIns, ASMIT_STY, ASMIM_ABSOLUTE, 2 * modID + 1, mProc->mLinkerObject)); + break; + } + } + else if (useA) + { + eblock->mIns.Push(NativeCodeInstruction(mIns[i].mIns, ASMIT_LDA, ASMIM_ZERO_PAGE, addr + 1)); + eblock->mIns.Push(NativeCodeInstruction(mIns[i].mIns, ASMIT_STA, ASMIM_ABSOLUTE, 2 * modID + 1, mProc->mLinkerObject)); + } + else if (useX) + { + eblock->mIns.Push(NativeCodeInstruction(mIns[i].mIns, ASMIT_LDX, ASMIM_ZERO_PAGE, addr + 1)); + eblock->mIns.Push(NativeCodeInstruction(mIns[i].mIns, ASMIT_STX, ASMIM_ABSOLUTE, 2 * modID + 1, mProc->mLinkerObject)); + } + else + { + eblock->mIns.Push(NativeCodeInstruction(mIns[i].mIns, ASMIT_LDY, ASMIM_ZERO_PAGE, addr + 1)); + eblock->mIns.Push(NativeCodeInstruction(mIns[i].mIns, ASMIT_STY, ASMIM_ABSOLUTE, 2 * modID + 1, mProc->mLinkerObject)); + } + } + } + + changed = true; + } + } + } + } + } + } + + if (mTrueJump && mTrueJump->CollectSelfModTargets()) changed = true; + if (mFalseJump && mFalseJump->CollectSelfModTargets()) changed = true; + } + + return changed; +} + +void NativeCodeBasicBlock::PropagateZPAbsolute(const NativeRegisterDataSet& data) +{ + if (!mVisited) + { + mDataSet = data; + + assert(mIndex == 1000 || mNumEntries == mEntryBlocks.Size()); + + if (mLoopHead) + { + if (mNumEntries == 2 && (mTrueJump == this || mFalseJump == this)) + { + ResetModifiedDataSet(mDataSet); + } + else + mDataSet.Reset(); + } + else if (mNumEntries > 0) + { + if (mNumEntered > 0) + mDataSet.Intersect(mNDataSet); + + mNumEntered++; + + if (mNumEntered < mNumEntries) + { + mNDataSet = mDataSet; + return; + } + } + + mVisited = true; + + for (int i = 0; i < mIns.Size(); i++) + { + if (mIns[i].mType == ASMIT_LDA && mIns[i].mMode == ASMIM_ABSOLUTE && mIns[i].mLinkerObject && (mIns[i].mLinkerObject->mFlags & LOBJF_ZEROPAGE)) + { + mDataSet.Set(CPU_REG_A, NRDM_ABSOLUTE, mIns[i].mAddress, mIns[i].mLinkerObject, mIns[i].mFlags); + } + else if (mIns[i].mType == ASMIT_LDX && mIns[i].mMode == ASMIM_ABSOLUTE && mIns[i].mLinkerObject && (mIns[i].mLinkerObject->mFlags & LOBJF_ZEROPAGE)) + { + mDataSet.Set(CPU_REG_X, NRDM_ABSOLUTE, mIns[i].mAddress, mIns[i].mLinkerObject, mIns[i].mFlags); + } + else if (mIns[i].mType == ASMIT_LDY && mIns[i].mMode == ASMIM_ABSOLUTE && mIns[i].mLinkerObject && (mIns[i].mLinkerObject->mFlags & LOBJF_ZEROPAGE)) + { + mDataSet.Set(CPU_REG_Y, NRDM_ABSOLUTE, mIns[i].mAddress, mIns[i].mLinkerObject, mIns[i].mFlags); + } + else if (mIns[i].mType == ASMIT_STA && mIns[i].mMode == ASMIM_ABSOLUTE && mIns[i].mLinkerObject && (mIns[i].mLinkerObject->mFlags & LOBJF_ZEROPAGE)) + { + mDataSet.ResetAbsolute(mIns[i].mLinkerObject, mIns[i].mAddress); + mDataSet.Set(CPU_REG_A, NRDM_ABSOLUTE, mIns[i].mAddress, mIns[i].mLinkerObject, mIns[i].mFlags); + } + else if (mIns[i].mType == ASMIT_STX && mIns[i].mMode == ASMIM_ABSOLUTE && mIns[i].mLinkerObject && (mIns[i].mLinkerObject->mFlags & LOBJF_ZEROPAGE)) + { + mDataSet.ResetAbsolute(mIns[i].mLinkerObject, mIns[i].mAddress); + mDataSet.Set(CPU_REG_X, NRDM_ABSOLUTE, mIns[i].mAddress, mIns[i].mLinkerObject, mIns[i].mFlags); + } + else if (mIns[i].mType == ASMIT_STY && mIns[i].mMode == ASMIM_ABSOLUTE && mIns[i].mLinkerObject && (mIns[i].mLinkerObject->mFlags & LOBJF_ZEROPAGE)) + { + mDataSet.ResetAbsolute(mIns[i].mLinkerObject, mIns[i].mAddress); + mDataSet.Set(CPU_REG_Y, NRDM_ABSOLUTE, mIns[i].mAddress, mIns[i].mLinkerObject, mIns[i].mFlags); + } + else if (mIns[i].mType == ASMIT_STA && mIns[i].mMode == ASMIM_ZERO_PAGE) + mDataSet.Set(mIns[i].mAddress, mDataSet[CPU_REG_A]); + else if (mIns[i].mType == ASMIT_STX && mIns[i].mMode == ASMIM_ZERO_PAGE) + mDataSet.Set(mIns[i].mAddress, mDataSet[CPU_REG_X]); + else if (mIns[i].mType == ASMIT_STY && mIns[i].mMode == ASMIM_ZERO_PAGE) + mDataSet.Set(mIns[i].mAddress, mDataSet[CPU_REG_Y]); + else if (mIns[i].mType == ASMIT_JSR) + { + mDataSet.ResetCall(mIns[i], mProc->mFastCallBase); + // mDataSet.Reset(); + } + else + { + if (mIns[i].mMode == ASMIM_ZERO_PAGE) + { + int r = mIns[i].mAddress; + + if (mIns[i].ChangesAddress()) + mDataSet.ResetZeroPage(r); + else if (mDataSet[r].mMode == NRDM_ABSOLUTE) + { + mIns[i].mMode = ASMIM_ABSOLUTE; + mIns[i].mLinkerObject = mDataSet[r].mLinkerObject; + mIns[i].mAddress = mDataSet[r].mValue; + } + } + else if (mIns[i].mMode == ASMIM_ABSOLUTE) + { + if (mIns[i].ChangesAddress()) + mDataSet.ResetAbsolute(mIns[i].mLinkerObject, mIns[i].mAddress); + } + else if (mIns[i].mMode == ASMIM_ABSOLUTE_X || mIns[i].mMode == ASMIM_ABSOLUTE_Y) + { + if (mIns[i].ChangesAddress()) + mDataSet.ResetAbsoluteXY(mIns[i].mLinkerObject, mIns[i].mAddress); + } + else if (mIns[i].mMode == ASMIM_INDIRECT_Y) + { + int r = mIns[i].mAddress; + + if (mDataSet[r].mMode == NRDM_ABSOLUTE && mDataSet[r + 1].mMode == NRDM_ABSOLUTE && + mDataSet[r].mLinkerObject == mDataSet[r + 1].mLinkerObject && + mDataSet[r].mValue + 1 == mDataSet[r + 1].mValue) + { + mIns[i].mLinkerObject = mDataSet[r].mLinkerObject; + mIns[i].mAddress = mDataSet[r].mValue; + } + } + + if (mIns[i].ChangesAccu()) + mDataSet.Reset(CPU_REG_A); + if (mIns[i].ChangesXReg()) + mDataSet.Reset(CPU_REG_X); + if (mIns[i].ChangesYReg()) + mDataSet.Reset(CPU_REG_Y); + } + } + + if (mTrueJump) mTrueJump->PropagateZPAbsolute(mDataSet); + if (mFalseJump) mFalseJump->PropagateZPAbsolute(mDataSet); + } +} + +void NativeCodeBasicBlock::RegisterFunctionCalls(void) +{ + if (!mVisited) + { + mVisited = true; + + for (int i = 0; i < mIns.Size(); i++) + { + if (mIns[i].mType == ASMIT_JSR && mIns[i].mLinkerObject) + mProc->mGenerator->RegisterFunctionCall(this, i); + } + + if (mTrueJump) mTrueJump->RegisterFunctionCalls(); + if (mFalseJump) mFalseJump->RegisterFunctionCalls(); + } +} + +bool NativeCodeBasicBlock::MergeFunctionCalls(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + for (int i = 0; i < mIns.Size(); i++) + { + if (mIns[i].mType == ASMIT_JSR && mIns[i].mLinkerObject) + { + if (mProc->mGenerator->MergeFunctionCall(this, i)) + changed = true; + } + } + + if (changed) + RemoveNops(); + + if (mTrueJump && mTrueJump->MergeFunctionCalls()) + changed = true; + if (mFalseJump && mFalseJump->MergeFunctionCalls()) + changed = true; + } + + return changed; +} + + +bool NativeCodeBasicBlock::IsDominatedBy(const NativeCodeBasicBlock* block) const +{ + if (this == block) + return true; +#if 0 + if (mEntryBlocks.Size()) + { + if (mLoopHead) + return false; + + for (int i = 0; i < mEntryBlocks.Size(); i++) + if (!mEntryBlocks[i]->IsDominatedBy(block)) + return false; + return true; + } + + return false; +#else + else + { + NativeCodeBasicBlock* dom = mDominator; + while (dom) + { + if (dom == block) + return true; + dom = dom->mDominator; + } + + return false; + } +#endif +} + +bool NativeCodeBasicBlock::JoinSameBranch(NativeCodeBasicBlock* block) +{ + bool changed = false; + + if (!mPatched) + { + mPatched = true; + + if (this != block) + { + if (mBranch == block->mBranch && mTrueJump == block->mTrueJump && mFalseJump == block->mFalseJump) + { + mTrueJump->mNumEntries--; + mFalseJump->mNumEntries--; + block->mNumEntries++; + mBranch = ASMIT_JMP; + mTrueJump = block; + mFalseJump = nullptr; + changed = true; + } + } + + if (mTrueJump && mTrueJump->JoinSameBranch(block)) + changed = true; + if (mFalseJump && mFalseJump->JoinSameBranch(block)) + changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::FinalCheckedSizeReduction(void) +{ + bool changed = false; + if (!mVisited) + { + mVisited = true; + + int i = 0; + int j = 0; + + while (i < mIns.Size()) + { + if (mIns[i].mType == ASMIT_NOP) + { + i++; + } + else if (i + 4 < mIns.Size() && + mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_LDA && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 0x01 && + mIns[i + 3].mType == ASMIT_CLC && + mIns[i + 4].mType == ASMIT_ADC && !(mIns[i + 4].mLive & LIVE_CPU_REG_C | LIVE_CPU_REG_Z)) + { + mIns[j + 0] = mIns[i + 0]; mIns[j + 0].mType = ASMIT_SEC; + mIns[j + 1] = mIns[i + 1]; + mIns[j + 2] = mIns[i + 4]; + j += 3; + i += 5; + changed = true; + } + else if (i + 2 < mIns.Size() && + mIns[i + 0].mType == ASMIT_LDX && HasAsmInstructionMode(ASMIT_INC, mIns[i + 0].mMode) && + mIns[i + 1].mType == ASMIT_INX && + mIns[i + 2].mType == ASMIT_STX && mIns[i + 2].SameEffectiveAddress(mIns[i + 0])) + { + mIns[j + 0] = mIns[i + 0]; + mIns[j + 0].mType = ASMIT_INC; + + j++; + if (mIns[i + 2].mLive & LIVE_CPU_REG_X) + { + mIns[j + 0] = mIns[i + 2]; + mIns[j + 0].mType = ASMIT_LDX; + j++; + } + + i += 3; + changed = true; + } + else if (i + 2 < mIns.Size() && + mIns[i + 0].mType == ASMIT_LDX && HasAsmInstructionMode(ASMIT_DEC, mIns[i + 0].mMode) && + mIns[i + 1].mType == ASMIT_DEX && + mIns[i + 2].mType == ASMIT_STX && mIns[i + 2].SameEffectiveAddress(mIns[i + 0])) + { + mIns[j + 0] = mIns[i + 0]; + mIns[j + 0].mType = ASMIT_DEC; + + j++; + if (mIns[i + 2].mLive & LIVE_CPU_REG_X) + { + mIns[j + 0] = mIns[i + 2]; + mIns[j + 0].mType = ASMIT_LDX; + j++; + } + i += 3; + changed = true; + } + else if (i + 3 < mIns.Size() && + mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_LDA && HasAsmInstructionMode(ASMIT_INC, mIns[i + 1].mMode) && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 1 && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].SameEffectiveAddress(mIns[i + 1]) && + !(mIns[i + 3].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C))) + { + mIns[j + 0] = mIns[i + 1]; + mIns[j + 0].mType = ASMIT_INC; + j += 1; + i += 4; + changed = true; + } + else if (i + 3 < mIns.Size() && + mIns[i + 0].mType == ASMIT_SEC && + mIns[i + 1].mType == ASMIT_LDA && HasAsmInstructionMode(ASMIT_DEC, mIns[i + 1].mMode) && + mIns[i + 2].mType == ASMIT_SBC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 1 && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].SameEffectiveAddress(mIns[i + 1]) && + !(mIns[i + 3].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C))) + { + mIns[j + 0] = mIns[i + 1]; + mIns[j + 0].mType = ASMIT_DEC; + j += 1; + i += 4; + changed = true; + } + else if (i + 3 < mIns.Size() && + mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_LDA && HasAsmInstructionMode(ASMIT_INC, mIns[i + 1].mMode) && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 0xff && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].SameEffectiveAddress(mIns[i + 1]) && + !(mIns[i + 3].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C))) + { + mIns[j + 0] = mIns[i + 1]; + mIns[j + 0].mType = ASMIT_DEC; + j += 1; + i += 4; + changed = true; + } + else if (i + 3 < mIns.Size() && + mIns[i + 0].mType == ASMIT_SEC && + mIns[i + 1].mType == ASMIT_LDA && HasAsmInstructionMode(ASMIT_DEC, mIns[i + 1].mMode) && + mIns[i + 2].mType == ASMIT_SBC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 0xff && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].SameEffectiveAddress(mIns[i + 1]) && + !(mIns[i + 3].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C))) + { + mIns[j + 0] = mIns[i + 1]; + mIns[j + 0].mType = ASMIT_INC; + j += 1; + i += 4; + changed = true; + } + + else if (i + 3 < mIns.Size() && + mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_LDA && HasAsmInstructionMode(ASMIT_LDX, mIns[i + 1].mMode) && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 1 && + mIns[i + 3].mType == ASMIT_STA && HasAsmInstructionMode(ASMIT_STX, mIns[i + 3].mMode) && + !(mIns[i + 3].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C | LIVE_CPU_REG_Z | LIVE_CPU_REG_X))) + { + mIns[j + 0] = mIns[i + 1]; + mIns[j + 0].mType = ASMIT_LDX; + mIns[j + 1].mType = ASMIT_INX; mIns[j + 1].mMode = ASMIM_IMPLIED; + mIns[j + 2].mType = ASMIT_STX; mIns[j + 2].CopyMode(mIns[i + 3]); + j += 3; + i += 4; + changed = true; + } + else if (i + 3 < mIns.Size() && + mIns[i + 0].mType == ASMIT_SEC && + mIns[i + 1].mType == ASMIT_LDA && HasAsmInstructionMode(ASMIT_LDX, mIns[i + 1].mMode) && + mIns[i + 2].mType == ASMIT_SBC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 1 && + mIns[i + 3].mType == ASMIT_STA && HasAsmInstructionMode(ASMIT_STX, mIns[i + 3].mMode) && + !(mIns[i + 3].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C | LIVE_CPU_REG_Z | LIVE_CPU_REG_X))) + { + mIns[j + 0] = mIns[i + 1]; + mIns[j + 0].mType = ASMIT_LDX; + mIns[j + 1].mType = ASMIT_DEX; mIns[j + 1].mMode = ASMIM_IMPLIED; + mIns[j + 2].mType = ASMIT_STX; mIns[j + 2].CopyMode(mIns[i + 3]); + j += 3; + i += 4; + changed = true; + } + + else if (i + 3 < mIns.Size() && + mIns[i + 0].mType == ASMIT_LDA && HasAsmInstructionMode(ASMIT_LDX, mIns[i + 0].mMode) && + mIns[i + 1].mType == ASMIT_CLC && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 1 && + mIns[i + 3].mType == ASMIT_STA && HasAsmInstructionMode(ASMIT_STX, mIns[i + 3].mMode) && + !(mIns[i + 3].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C | LIVE_CPU_REG_Z | LIVE_CPU_REG_X))) + { + mIns[j + 0] = mIns[i + 0]; + mIns[j + 0].mType = ASMIT_LDX; + mIns[j + 1].mType = ASMIT_INX; mIns[j + 1].mMode = ASMIM_IMPLIED; + mIns[j + 2].mType = ASMIT_STX; mIns[j + 2].CopyMode(mIns[i + 3]); + j += 3; + i += 4; + changed = true; + } + else if (i + 3 < mIns.Size() && + mIns[i + 0].mType == ASMIT_LDA && HasAsmInstructionMode(ASMIT_LDX, mIns[i + 0].mMode) && + mIns[i + 1].mType == ASMIT_SEC && + mIns[i + 2].mType == ASMIT_SBC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 1 && + mIns[i + 3].mType == ASMIT_STA && HasAsmInstructionMode(ASMIT_STX, mIns[i + 3].mMode) && + !(mIns[i + 3].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C | LIVE_CPU_REG_Z | LIVE_CPU_REG_X))) + { + mIns[j + 0] = mIns[i + 0]; + mIns[j + 0].mType = ASMIT_LDX; + mIns[j + 1].mType = ASMIT_DEX; mIns[j + 1].mMode = ASMIM_IMPLIED; + mIns[j + 2].mType = ASMIT_STX; mIns[j + 2].CopyMode(mIns[i + 3]); + j += 3; + i += 4; + changed = true; + } + else if (i + 3 < mIns.Size() && + mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_LDA && HasAsmInstructionMode(ASMIT_LDX, mIns[i + 1].mMode) && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 0xff && + mIns[i + 3].mType == ASMIT_STA && HasAsmInstructionMode(ASMIT_STX, mIns[i + 3].mMode) && + !(mIns[i + 3].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C | LIVE_CPU_REG_Z | LIVE_CPU_REG_X))) + { + mIns[j + 0] = mIns[i + 1]; + mIns[j + 0].mType = ASMIT_LDX; + mIns[j + 1].mType = ASMIT_DEX; mIns[j + 1].mMode = ASMIM_IMPLIED; + mIns[j + 2].mType = ASMIT_STX; mIns[j + 2].CopyMode(mIns[i + 3]); + j += 3; + i += 4; + changed = true; + } + else if (i + 3 < mIns.Size() && + mIns[i + 0].mType == ASMIT_SEC && + mIns[i + 1].mType == ASMIT_LDA && HasAsmInstructionMode(ASMIT_LDX, mIns[i + 1].mMode) && + mIns[i + 2].mType == ASMIT_SBC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 0xff && + mIns[i + 3].mType == ASMIT_STA && HasAsmInstructionMode(ASMIT_STX, mIns[i + 3].mMode) && + !(mIns[i + 3].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C | LIVE_CPU_REG_Z | LIVE_CPU_REG_X))) + { + mIns[j + 0] = mIns[i + 1]; + mIns[j + 0].mType = ASMIT_LDX; + mIns[j + 1].mType = ASMIT_INX; mIns[j + 1].mMode = ASMIM_IMPLIED; + mIns[j + 2].mType = ASMIT_STX; mIns[j + 2].CopyMode(mIns[i + 3]); + j += 3; + i += 4; + changed = true; + } + + else if (i + 3 < mIns.Size() && + mIns[i + 0].mType == ASMIT_LDA && HasAsmInstructionMode(ASMIT_LDX, mIns[i + 0].mMode) && + mIns[i + 1].mType == ASMIT_CLC && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 2 && + mIns[i + 3].mType == ASMIT_STA && HasAsmInstructionMode(ASMIT_STX, mIns[i + 3].mMode) && + !(mIns[i + 3].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C | LIVE_CPU_REG_Z | LIVE_CPU_REG_X))) + { + mIns[j + 0] = mIns[i + 0]; + mIns[j + 0].mType = ASMIT_LDX; + mIns[j + 1].mType = ASMIT_INX; mIns[j + 1].mMode = ASMIM_IMPLIED; + mIns[j + 2].mType = ASMIT_INX; mIns[j + 2].mMode = ASMIM_IMPLIED; + mIns[j + 3].mType = ASMIT_STX; mIns[j + 3].CopyMode(mIns[i + 3]); + j += 4; + i += 4; + changed = true; + } + else if (i + 3 < mIns.Size() && + mIns[i + 0].mType == ASMIT_LDA && HasAsmInstructionMode(ASMIT_LDX, mIns[i + 0].mMode) && + mIns[i + 1].mType == ASMIT_SEC && + mIns[i + 2].mType == ASMIT_SBC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 2 && + mIns[i + 3].mType == ASMIT_STA && HasAsmInstructionMode(ASMIT_STX, mIns[i + 3].mMode) && + !(mIns[i + 3].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C | LIVE_CPU_REG_Z | LIVE_CPU_REG_X))) + { + mIns[j + 0] = mIns[i + 0]; + mIns[j + 0].mType = ASMIT_LDX; + mIns[j + 1].mType = ASMIT_DEX; mIns[j + 1].mMode = ASMIM_IMPLIED; + mIns[j + 2].mType = ASMIT_DEX; mIns[j + 2].mMode = ASMIM_IMPLIED; + mIns[j + 3].mType = ASMIT_STX; mIns[j + 3].CopyMode(mIns[i + 3]); + j += 4; + i += 4; + changed = true; + } + + else if (i + 3 < mIns.Size() && + mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 2 && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && mIns[i + 1].mAddress == mIns[i + 3].mAddress && + !(mIns[i + 3].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C | LIVE_CPU_REG_Z))) + { + mIns[j + 0].mType = ASMIT_INC; mIns[j + 0].mMode = ASMIM_ZERO_PAGE; mIns[j + 0].mAddress = mIns[i + 1].mAddress; mIns[j + 0].mLinkerObject = nullptr; + mIns[j + 1].mType = ASMIT_INC; mIns[j + 1].mMode = ASMIM_ZERO_PAGE; mIns[j + 1].mAddress = mIns[i + 3].mAddress; mIns[j + 1].mLinkerObject = nullptr; + j += 2; + i += 4; + changed = true; + } + else if (i + 3 < mIns.Size() && + mIns[i + 0].mType == ASMIT_SEC && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + mIns[i + 2].mType == ASMIT_SBC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 2 && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && mIns[i + 1].mAddress == mIns[i + 3].mAddress && + !(mIns[i + 3].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C | LIVE_CPU_REG_Z))) + { + mIns[j + 0].mType = ASMIT_DEC; mIns[j + 0].mMode = ASMIM_ZERO_PAGE; mIns[j + 0].mAddress = mIns[i + 1].mAddress; mIns[j + 0].mLinkerObject = nullptr; + mIns[j + 1].mType = ASMIT_DEC; mIns[j + 1].mMode = ASMIM_ZERO_PAGE; mIns[j + 1].mAddress = mIns[i + 3].mAddress; mIns[j + 1].mLinkerObject = nullptr; + j += 2; + i += 4; + changed = true; + } + + else if (i + 3 < mIns.Size() && + mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_ABSOLUTE && + mIns[i + 1].mType == ASMIT_CLC && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 2 && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].mMode == ASMIM_ABSOLUTE && mIns[i + 0].mAddress == mIns[i + 3].mAddress && + !(mIns[i + 3].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C | LIVE_CPU_REG_Z))) + { + mIns[j + 0].mType = ASMIT_INC; mIns[j + 0].CopyMode(mIns[i + 0]); + mIns[j + 1].mType = ASMIT_INC; mIns[j + 1].CopyMode(mIns[i + 0]); + j += 2; + i += 4; + changed = true; + } + else if (i + 3 < mIns.Size() && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_ABSOLUTE && + mIns[i + 0].mType == ASMIT_SEC && + mIns[i + 2].mType == ASMIT_SBC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 2 && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].mMode == ASMIM_ABSOLUTE && mIns[i + 0].mAddress == mIns[i + 3].mAddress && + !(mIns[i + 3].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C | LIVE_CPU_REG_Z))) + { + mIns[j + 0].mType = ASMIT_DEC; mIns[j + 0].CopyMode(mIns[i + 0]); + mIns[j + 1].mType = ASMIT_DEC; mIns[j + 1].CopyMode(mIns[i + 0]); + j += 2; + i += 4; + changed = true; + } + + else if (i + 1 < mIns.Size() && + mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_ABSOLUTE_X && + mIns[i + 1].mType == ASMIT_TAY && !(mIns[i + 1].mLive & LIVE_CPU_REG_A)) + { + mIns[j] = mIns[i]; + mIns[j].mType = ASMIT_LDY; + mIns[j].mLive |= LIVE_CPU_REG_Y; + j++; + i += 2; + changed = true; + } + else if (i + 1 < mIns.Size() && + mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_ABSOLUTE_Y && + mIns[i + 1].mType == ASMIT_TAX && !(mIns[i + 1].mLive & LIVE_CPU_REG_A)) + { + mIns[j] = mIns[i]; + mIns[j].mType = ASMIT_LDX; + mIns[j].mLive |= LIVE_CPU_REG_Y; + j++; + i += 2; + changed = true; + } + else if (i + 2 < mIns.Size() && + mIns[i + 0].mType == ASMIT_LDY && !(mIns[i + 0].mLive & LIVE_CPU_REG_X) && (mIns[i + 0].mMode == ASMIM_ZERO_PAGE || mIns[i + 0].mMode == ASMIM_ABSOLUTE) && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_ABSOLUTE_Y && + mIns[i + 2].mType == ASMIT_TAY && !(mIns[i + 2].mLive & LIVE_CPU_REG_A)) + { + mIns[j + 0] = mIns[i + 0]; + mIns[j + 1] = mIns[i + 1]; + + mIns[j + 0].mType = ASMIT_LDX; + mIns[j + 0].mLive |= LIVE_CPU_REG_X; + mIns[j + 1].mType = ASMIT_LDY; + mIns[j + 1].mMode = ASMIM_ABSOLUTE_X; + mIns[j + 1].mLive |= LIVE_CPU_REG_Y; + j += 2; + i += 3; + changed = true; + } + else if (i + 2 < mIns.Size() && + mIns[i + 0].mType == ASMIT_LDX && !(mIns[i + 0].mLive & LIVE_CPU_REG_Y) && (mIns[i + 0].mMode == ASMIM_ZERO_PAGE || mIns[i + 0].mMode == ASMIM_ABSOLUTE) && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_ABSOLUTE_X && + mIns[i + 2].mType == ASMIT_TAX && !(mIns[i + 2].mLive & LIVE_CPU_REG_A)) + { + mIns[j + 0] = mIns[i + 0]; + mIns[j + 1] = mIns[i + 1]; + + mIns[j + 0].mType = ASMIT_LDY; + mIns[j + 0].mLive |= LIVE_CPU_REG_Y; + mIns[j + 1].mType = ASMIT_LDX; + mIns[j + 1].mMode = ASMIM_ABSOLUTE_Y; + mIns[j + 1].mLive |= LIVE_CPU_REG_X; + j += 2; + i += 3; + changed = true; + } +#if 1 + else if (i + 3 < mIns.Size() && + mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_ADC && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 0x01 && + mIns[i + 2].mType == ASMIT_CLC && + mIns[i + 3].mType == ASMIT_ADC) + { + mIns[j + 0] = NativeCodeInstruction(mIns[i + 0].mIns, ASMIT_SEC); + mIns[j + 1] = mIns[i + 3]; + j += 2; + i += 4; + changed = true; + } + else if (i + 4 < mIns.Size() && + mIns[i + 0].mType == ASMIT_SEC && + mIns[i + 1].mType == ASMIT_LDA && + mIns[i + 2].mType == ASMIT_SBC && + mIns[i + 3].mType == ASMIT_SEC && + mIns[i + 4].mType == ASMIT_SBC && mIns[i + 4].mMode == ASMIM_IMMEDIATE && mIns[i + 4].mAddress == 0x01) + { + mIns[j + 0] = NativeCodeInstruction(mIns[i + 0].mIns, ASMIT_CLC); + j += 3; + i += 5; + changed = true; + } +#endif + else if (i + 3 < mIns.Size() && + mIns[i + 0].mType == ASMIT_TAY && + mIns[i + 1].mType == ASMIT_LDA && HasAsmInstructionMode(ASMIT_LDY, mIns[i + 1].mMode) && + mIns[i + 2].mType == ASMIT_STA && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && !(mIns[i + 2].mLive & LIVE_CPU_REG_A) && + mIns[i + 3].mMode == ASMIM_INDIRECT_Y && !(mIns[i + 3].mLive & (LIVE_CPU_REG_Y | LIVE_CPU_REG_Z | LIVE_MEM))) + { + mIns[j + 0] = mIns[i + 2]; + mIns[j + 1] = mIns[i + 1]; mIns[j + 1].mType = ASMIT_LDY; + mIns[j + 2] = mIns[i + 3]; + j += 3; + i += 4; + changed = true; + } + else if (mIns[i + 0].mType == ASMIT_ORA && mIns[i + 0].mMode == ASMIM_IMMEDIATE && mIns[i + 0].mAddress == 0 && !(mIns[i + 0].mLive & LIVE_CPU_REG_X)) + { + mIns[j + 0] = NativeCodeInstruction(mIns[i + 0].mIns, ASMIT_TAX); + i++; + j++; + changed = true; + } + else if (mIns[i + 0].mType == ASMIT_ORA && mIns[i + 0].mMode == ASMIM_IMMEDIATE && mIns[i + 0].mAddress == 0 && !(mIns[i + 0].mLive & LIVE_CPU_REG_Y)) + { + mIns[j + 0] = NativeCodeInstruction(mIns[i + 0].mIns, ASMIT_TAY); + i++; + j++; + changed = true; + } + else if (i + 1 < mIns.Size() && + mIns[i + 0].ChangesZFlag() && mIns[i + 1].mType == ASMIT_LDA && mIns[i + 0].SameEffectiveAddress(mIns[i + 1]) && !(mIns[i + 1].mLive & LIVE_CPU_REG_A) && + !(mIns[i + 0].mFlags & mIns[i + 1].mFlags & NCIF_VOLATILE)) + { + mIns[j++] = mIns[i++]; + i++; + changed = true; + } + else if (mIns[i].mType == ASMIT_CMP && mIns[i].mMode == ASMIM_IMMEDIATE && mIns[i].mAddress == 0x80 && !(mIns[i].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z))) + { + mIns[j + 0] = NativeCodeInstruction(mIns[i + 0].mIns, ASMIT_ASL); + i++; + j++; + changed = true; + } + else if (mIns[i].mType == ASMIT_CPY && mIns[i].mMode == ASMIM_IMMEDIATE && mIns[i].mAddress == 0 && !(mIns[i].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C))) + { + mIns[j + 0] = NativeCodeInstruction(mIns[i + 0].mIns, ASMIT_TYA); + i++; + j++; + changed = true; + } + else if (mIns[i].mType == ASMIT_CPX && mIns[i].mMode == ASMIM_IMMEDIATE && mIns[i].mAddress == 0 && !(mIns[i].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C))) + { + mIns[j + 0] = NativeCodeInstruction(mIns[i + 0].mIns, ASMIT_TXA); + i++; + j++; + changed = true; + } + else + mIns[j++] = mIns[i++]; + } + + mIns.SetSize(j); + + if (mTrueJump && mTrueJump->FinalCheckedSizeReduction()) + changed = true; + if (mFalseJump && mFalseJump->FinalCheckedSizeReduction()) + changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::MergeSameBranch(void) +{ + bool changed = false; + if (!mVisited) + { + mVisited = true; + + if (mIns.Size() == 0 && mFalseJump) + { + mProc->ResetPatched(); + if (mProc->mEntryBlock->JoinSameBranch(this)) + changed = true; + } + + if (mTrueJump && mTrueJump->MergeSameBranch()) + changed = true; + if (mFalseJump && mFalseJump->MergeSameBranch()) + changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::CheckPatchFailLoop(const NativeCodeBasicBlock* block, const NativeCodeBasicBlock* head, int reg, bool changed) +{ + if (!mPatchLoop || (changed && !mPatchLoopChanged)) + { + mPatchLoop = true; + mPatchLoopChanged = changed; + + if (this == block) + return true; + + if (!changed) + { + for (int i = 0; i < mIns.Size(); i++) + { + if (mIns[i].ChangesZeroPage(reg)) + changed = true; + } + } + + if (mTrueJump) + { + if (mTrueJump == head) + { + if (changed) + return false; + } + else if (!mTrueJump->CheckPatchFailLoop(block, head, reg, changed)) + return false; + } + + if (mFalseJump) + { + if (mFalseJump == head) + { + if (changed) + return false; + } + else if (!mFalseJump->CheckPatchFailLoop(block, head, reg, changed)) + return false; + } + } + + return true; +} + +bool NativeCodeBasicBlock::CheckPatchFailLoopPair(const NativeCodeBasicBlock* block, const NativeCodeBasicBlock* head, int reg, bool changed) +{ + if (!mPatchLoop || (changed && !mPatchLoopChanged)) + { + mPatchLoop = true; + mPatchLoopChanged = changed; + + if (this == block) + return true; + + if (!changed) + { + for (int i = 0; i < mIns.Size(); i++) + { + if (mIns[i].ChangesZeroPage(reg) || mIns[i].ChangesZeroPage(reg + 1)) + changed = true; + } + } + + if (mTrueJump) + { + if (mTrueJump == head) + { + if (changed) + return false; + } + else if (!mTrueJump->CheckPatchFailLoopPair(block, head, reg, changed)) + return false; + } + + if (mFalseJump) + { + if (mFalseJump == head) + { + if (changed) + return false; + } + else if (!mFalseJump->CheckPatchFailLoopPair(block, head, reg, changed)) + return false; + } + } + + return true; +} + +bool NativeCodeBasicBlock::CheckBoolBitPropagation(const NativeCodeBasicBlock* block, int at, int reg) +{ + if (!mPatched) + { + if (at == 0) + { + mPatched = true; + + if (!mEntryRequiredRegs[reg]) + return CheckPatchFailUse(); + + if (mPatchFail) + return false; + + if (mLoopHead && !CheckPatchFailLoop(block, this, reg, false)) + return false; + + if (mNumEntries > 1) + { + for (int i = 0; i < mEntryBlocks.Size(); i++) + if (!mEntryBlocks[i]->IsDominatedBy(block)) + return false; + } + + } + else + { + mPatchStart = true; + } + + while (at < mIns.Size()) + { + if (mIns[at].UsesZeroPage(reg)) + { + if (at + 1 == mIns.Size() && (mIns[at].mType == ASMIT_LDA) && !(mIns[at].mLive & LIVE_CPU_REG_A)) + { + mPatchChecked = true; + if ((mBranch == ASMIT_BEQ || mBranch == ASMIT_BNE) && !mExitRequiredRegs[CPU_REG_Z]) + ; + else if (mBranch == ASMIT_BCC || mBranch == ASMIT_BCS) + { + if (mTrueJump->mEntryRequiredRegs[CPU_REG_Z]) + { + if (mTrueJump->mIns.Size() > 0 || mTrueJump->mExitRequiredRegs[CPU_REG_Z] || + !(mTrueJump->mBranch == ASMIT_BEQ || mTrueJump->mBranch == ASMIT_BNE)) + return false; + } + if (mFalseJump->mEntryRequiredRegs[CPU_REG_Z]) + { + if (mFalseJump->mIns.Size() > 0 || mFalseJump->mExitRequiredRegs[CPU_REG_Z] || + !(mFalseJump->mBranch == ASMIT_BEQ || mFalseJump->mBranch == ASMIT_BNE)) + return false; + } + } + } + else + return false; + } + if (mIns[at].ChangesZeroPage(reg)) + { + if (mTrueJump && !mTrueJump->CheckPatchFailUse() || mFalseJump && !mFalseJump->CheckPatchFailUse()) + return false; + return true; + } + + at++; + } + + if (mTrueJump && !mTrueJump->CheckBoolBitPropagation(block, 0, reg)) + return false; + if (mFalseJump && !mFalseJump->CheckBoolBitPropagation(block, 0, reg)) + return false; + } + + return true; +} + +bool NativeCodeBasicBlock::PatchBoolBitPropagation(const NativeCodeBasicBlock* block, int at, int reg, bool inverse) +{ + bool changed = false; + + if (!mPatched) + { + mPatched = true; + + if (at == 0 && !mEntryRequiredRegs[reg]) + return false; + + while (at < mIns.Size()) + { + if (mIns[at].UsesZeroPage(reg)) + { + if (at + 1 == mIns.Size() && (mIns[at].mType == ASMIT_LDA) && !(mIns[at].mLive & LIVE_CPU_REG_A)) + { + if (mBranch == ASMIT_BEQ) + { + if (inverse) + mBranch = ASMIT_BMI; + else + mBranch = ASMIT_BPL; + changed = true; + } + else if (mBranch == ASMIT_BNE) + { + if (inverse) + mBranch = ASMIT_BPL; + else + mBranch = ASMIT_BMI; + changed = true; + } + else if (mBranch == ASMIT_BCC || mBranch == ASMIT_BCS) + { + if (mTrueJump->mEntryRequiredRegs[CPU_REG_Z]) + { + if (mTrueJump->mIns.Size() == 0) + { + if (mTrueJump->mBranch == ASMIT_BEQ) + { + if (inverse) + mBranch = ASMIT_BMI; + else + mBranch = ASMIT_BPL; + changed = true; + } + else if (mTrueJump->mBranch == ASMIT_BNE) + { + if (inverse) + mBranch = ASMIT_BPL; + else + mBranch = ASMIT_BMI; + changed = true; + } + } + } + if (mFalseJump->mEntryRequiredRegs[CPU_REG_Z]) + { + if (mFalseJump->mIns.Size() == 0) + { + if (mFalseJump->mBranch == ASMIT_BEQ) + { + if (inverse) + mBranch = ASMIT_BMI; + else + mBranch = ASMIT_BPL; + changed = true; + } + else if (mFalseJump->mBranch == ASMIT_BNE) + { + if (inverse) + mBranch = ASMIT_BPL; + else + mBranch = ASMIT_BMI; + changed = true; + } + } + } + } + } + } + if (mIns[at].ChangesZeroPage(reg)) + return false; + + at++; + } + + if (mTrueJump && mTrueJump->PatchBoolBitPropagation(block, 0, reg, inverse)) + changed = true; + if (mFalseJump && mFalseJump->PatchBoolBitPropagation(block, 0, reg, inverse)) + changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::CollectRegBoolInstructionsForward(int reg, ExpandingArray& cblocks, ExpandingArray& lins) +{ + if (!mPatchStart) + { + mPatchStart = true; + if (!mEntryRequiredRegs[reg]) + return true; + + int i = 0; + while (i < mIns.Size() && !mIns[i].ReferencesZeroPage(reg)) + i++; + if (i < mIns.Size()) + { + if (mIns[i].UsesZeroPage(reg)) + { + if (i == mIns.Size() - 1 && + (mBranch == ASMIT_BEQ || mBranch == ASMIT_BNE) && + (mIns[i].mType == ASMIT_LDA && !(mIns[i].mLive & LIVE_CPU_REG_A) || + mIns[i].mType == ASMIT_LDX && !(mIns[i].mLive & LIVE_CPU_REG_X) || + mIns[i].mType == ASMIT_LDY && !(mIns[i].mLive & LIVE_CPU_REG_Y))) + { + cblocks.Push(this); + } + else + return false; + } + else if (mIns[i].ChangesZeroPage(reg)) + return true; + } + + for (int i = 0; i < mEntryBlocks.Size(); i++) + { + if (!mEntryBlocks[i]->CollectRegBoolInstructionsBackward(reg, cblocks, lins)) + return false; + } + + if (mTrueJump && !mTrueJump->CollectRegBoolInstructionsForward(reg, cblocks, lins)) + return false; + if (mFalseJump && !mFalseJump->CollectRegBoolInstructionsForward(reg, cblocks, lins)) + return false; + } + + return true; +} + +bool NativeCodeBasicBlock::CollectRegBoolInstructionsBackward(int reg, ExpandingArray& cblocks, ExpandingArray& lins) +{ + if (!mPatchExit) + { + mPatchExit = true; + + int sz = mIns.Size(); + if (mFalseJump && (mBranch == ASMIT_BEQ || mBranch == ASMIT_BNE) && sz > 0 && mIns[sz - 1].mMode == ASMIM_ZERO_PAGE && !(mIns[sz - 1].mLive & LIVE_MEM) && mIns[sz - 1].mAddress == reg) + { + if (mIns[sz - 1].mType == ASMIT_LDA && !(mIns[sz - 1].mLive & LIVE_CPU_REG_A) || + mIns[sz - 1].mType == ASMIT_LDX && !(mIns[sz - 1].mLive & LIVE_CPU_REG_X) || + mIns[sz - 1].mType == ASMIT_LDY && !(mIns[sz - 1].mLive & LIVE_CPU_REG_Y)) + { + cblocks.Push(this); + sz -= 1; + } + } + + int i = sz - 1; + while (i >= 0 && !mIns[i].ReferencesZeroPage(reg)) + i--; + + bool found = false; + if (i >= 0) + { + if (i > 0 && + (mIns[i].mType == ASMIT_STA && mIns[i - 1].mType == ASMIT_LDA && mIns[i - 1].mMode == ASMIM_IMMEDIATE && !(mIns[i].mLive & LIVE_CPU_REG_A) || + mIns[i].mType == ASMIT_STX && mIns[i - 1].mType == ASMIT_LDX && mIns[i - 1].mMode == ASMIM_IMMEDIATE && !(mIns[i].mLive & LIVE_CPU_REG_X) || + mIns[i].mType == ASMIT_STY && mIns[i - 1].mType == ASMIT_LDY && mIns[i - 1].mMode == ASMIM_IMMEDIATE && !(mIns[i].mLive & LIVE_CPU_REG_Y))) + { + lins.Push(&(mIns[i - 1])); + found = true; + } + else if (i == 0 && mIns[0].mType == ASMIT_STA && !(mIns[0].mLive & LIVE_CPU_REG_A)) + { + for (int k = 0; k < mEntryBlocks.Size(); k++) + { + NativeCodeBasicBlock* eb = mEntryBlocks[k]; + int es = eb->mIns.Size(); + if (!eb->mFalseJump && es > 0 && eb->mIns[es - 1].mType == ASMIT_LDA && eb->mIns[es - 1].mMode == ASMIM_IMMEDIATE) + lins.Push(&(eb->mIns[es - 1])); + else + return false; + } + found = true; + } + else + return false; + } + + if (!found) + { + if (!mEntryBlocks.Size()) + return false; + + for (int i = 0; i < mEntryBlocks.Size(); i++) + { + if (!mEntryBlocks[i]->CollectRegBoolInstructionsBackward(reg, cblocks, lins)) + return false; + } + } + + if (mTrueJump && !mTrueJump->CollectRegBoolInstructionsForward(reg, cblocks, lins)) + return false; + if (mFalseJump && !mFalseJump->CollectRegBoolInstructionsForward(reg, cblocks, lins)) + return false; + } + + return true; +} + + + +bool NativeCodeBasicBlock::PatchBitBoolConstOrigin(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + int sz = mIns.Size(); + if (mFalseJump && (mBranch == ASMIT_BEQ || mBranch == ASMIT_BNE) && sz > 0 && mIns[sz - 1].mMode == ASMIM_ZERO_PAGE && !(mIns[sz - 1].mLive & LIVE_MEM)) + { + if (mIns[sz - 1].mType == ASMIT_LDA && !(mIns[sz - 1].mLive & LIVE_CPU_REG_A) || + mIns[sz - 1].mType == ASMIT_LDX && !(mIns[sz - 1].mLive & LIVE_CPU_REG_X) || + mIns[sz - 1].mType == ASMIT_LDY && !(mIns[sz - 1].mLive & LIVE_CPU_REG_Y)) + { + ExpandingArray lblocks; + ExpandingArray lins; + + mProc->ResetPatched(); + + if (CollectRegBoolInstructionsBackward(mIns[sz - 1].mAddress, lblocks, lins)) + { + for (int i = 0; i < lins.Size(); i++) + { + if (lins[i]->mAddress != 0) + lins[i]->mAddress = 0xff; + } + for (int i = 0; i < lblocks.Size(); i++) + { + if (!lblocks[i]->mPatched) + { + lblocks[i]->mPatched = true; + int sz = lblocks[i]->mIns.Size(); + lblocks[i]->mIns[sz - 1].mType = ASMIT_BIT; + if (lblocks[i]->mBranch == ASMIT_BNE) + lblocks[i]->mBranch = ASMIT_BMI; + else + lblocks[i]->mBranch = ASMIT_BPL; + } + } + changed = true; + } + } + } + + if (mTrueJump && mTrueJump->PatchBitBoolConstOrigin()) + changed = true; + if (mFalseJump && mFalseJump->PatchBitBoolConstOrigin()) + changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::CheckGlobalAddressSumYPointer(const NativeCodeBasicBlock * block, int reg, int index, int at, int yval) +{ + if (!mPatched) + { + mPatched = true; + + if (at == 0) + { + if (!mEntryRequiredRegs[reg] && !mEntryRequiredRegs[reg + 1]) + return CheckPatchFailUse(); + + if (mLoopHead && !CheckPatchFailLoopPair(block, this, reg, false)) + return false; + + if (mNumEntries > 1) + { + for (int i = 0; i < mEntryBlocks.Size(); i++) + if (!mEntryBlocks[i]->IsDominatedBy(block)) + return false; + yval = -1; + } + } + else + { + mPatchStart = true; + } + + while (at < mIns.Size()) + { + NativeCodeInstruction& ins(mIns[at]); + + if (index && at + 6 < mIns.Size() && + mIns[at + 0].mType == ASMIT_CLC && + mIns[at + 1].mType == ASMIT_LDA && mIns[at + 1].mMode == ASMIM_ZERO_PAGE && mIns[at + 1].mAddress == reg && + mIns[at + 2].mMode == ASMIM_IMMEDIATE && (mIns[at + 2].mType == ASMIT_ADC || mIns[at + 2].mType == ASMIT_ORA && mIns[at + 2].mAddress == 0) && + mIns[at + 3].mType == ASMIT_STA && mIns[at + 3].mMode == ASMIM_ZERO_PAGE && mIns[at + 3].mAddress != index && + mIns[at + 4].mType == ASMIT_LDA && mIns[at + 4].mMode == ASMIM_ZERO_PAGE && mIns[at + 4].mAddress == reg + 1 && + mIns[at + 5].mType == ASMIT_ADC && mIns[at + 5].mMode == ASMIM_IMMEDIATE && + mIns[at + 6].mType == ASMIT_STA && mIns[at + 6].mMode == ASMIM_ZERO_PAGE && mIns[at + 6].mAddress != index && + !(mIns[at + 6].mLive & LIVE_CPU_REG_C)) + { + if (!(mIns[at + 1].mLive & LIVE_MEM) && !(mIns[at + 4].mLive & LIVE_MEM)) + return true; + at += 6; + } + else if (ins.mType == ASMIT_INC && ins.mMode == ASMIM_ZERO_PAGE && ins.mAddress == reg + 1) + ; + else if (ins.mMode == ASMIM_ZERO_PAGE) + { + if (index && ins.mAddress == index && ins.ChangesAddress()) + return false; + else if (ins.mAddress == reg || ins.mAddress == reg + 1) + { + if (ins.ChangesAddress() || reg == index) + return false; + } + } + else if (ins.mMode == ASMIM_INDIRECT_Y && ins.mAddress == reg) + { + if (yval < 0) + return false; + else if (!(ins.mLive & LIVE_MEM)) + { + if (mPatchFail) + return false; + mPatchChecked = true; + if (mTrueJump && !mTrueJump->CheckPatchFailUse() || mFalseJump && !mFalseJump->CheckPatchFailUse()) + return false; + return true; + } + } + + if (ins.mType == ASMIT_LDY && ins.mMode == ASMIM_IMMEDIATE) + yval = ins.mAddress; + else if (ins.mType == ASMIT_INY && yval >= 0) + yval = (yval + 1) & 255; + else if (ins.mType == ASMIT_DEY && yval >= 0) + yval = (yval - 1) & 255; + else if (ins.mType == ASMIT_JSR || ins.mType == ASMIT_RTS) + { + if (ins.UsesZeroPage(reg) || ins.UsesZeroPage(reg + 1) || (index && ins.ChangesZeroPage(index))) + return false; + yval = -1; + } + else if (ins.ChangesYReg()) + yval = -1; + + at++; + } + + if (mTrueJump && !mTrueJump->CheckGlobalAddressSumYPointer(block, reg, index, 0, yval)) + return false; + if (mFalseJump && !mFalseJump->CheckGlobalAddressSumYPointer(block, reg, index, 0, yval)) + return false; + } + + return true; +} + +bool NativeCodeBasicBlock::PatchGlobalAddressSumYPointer(const NativeCodeBasicBlock* block, int reg, int index, int at, int yval, LinkerObject* lobj, int address, uint32 flags) +{ + bool changed = false; + + if (!mPatched) + { + mPatched = true; + if (at == 0 && !mEntryRequiredRegs[reg] && !mEntryRequiredRegs[reg + 1]) + return false; + + while (at < mIns.Size()) + { + NativeCodeInstruction& ins(mIns[at]); + +// assert(!(ins.mMode == ASMIM_ZERO_PAGE && (ins.mAddress == reg || ins.mAddress == reg + 1) && reg == index)); + + if (index && at + 6 < mIns.Size() && + mIns[at + 0].mType == ASMIT_CLC && + mIns[at + 1].mType == ASMIT_LDA && mIns[at + 1].mMode == ASMIM_ZERO_PAGE && mIns[at + 1].mAddress == reg && + mIns[at + 2].mMode == ASMIM_IMMEDIATE && (mIns[at + 2].mType == ASMIT_ADC || mIns[at + 2].mType == ASMIT_ORA && mIns[at + 2].mAddress == 0) && + mIns[at + 3].mType == ASMIT_STA && mIns[at + 3].mMode == ASMIM_ZERO_PAGE && mIns[at + 3].mAddress != index && + mIns[at + 4].mType == ASMIT_LDA && mIns[at + 4].mMode == ASMIM_ZERO_PAGE && mIns[at + 4].mAddress == reg + 1 && + mIns[at + 5].mType == ASMIT_ADC && mIns[at + 5].mMode == ASMIM_IMMEDIATE && + mIns[at + 6].mType == ASMIT_STA && mIns[at + 6].mMode == ASMIM_ZERO_PAGE && mIns[at + 6].mAddress != index && + !(mIns[at + 6].mLive & LIVE_CPU_REG_C)) + { + bool done = !(mIns[at + 1].mLive & LIVE_MEM) && !(mIns[at + 4].mLive & LIVE_MEM); + + mIns[at + 1].mAddress = index; + + int naddr = address + mIns[at + 2].mAddress + 256 * mIns[at + 5].mAddress; + + mIns[at + 2].mType = ASMIT_ADC; + if (flags & NCIF_LOWER) + { + mIns[at + 2].mMode = lobj ? ASMIM_IMMEDIATE_ADDRESS : ASMIM_IMMEDIATE; + mIns[at + 2].mLinkerObject = lobj; + mIns[at + 2].mAddress = lobj ? naddr : naddr & 0xff; + mIns[at + 2].mFlags = NCIF_LOWER; + } + else + naddr &= 0xff00; + + mIns[at + 4].mMode = ASMIM_IMMEDIATE; + mIns[at + 4].mAddress = 0; + + mIns[at + 5].mMode = lobj ? ASMIM_IMMEDIATE_ADDRESS : ASMIM_IMMEDIATE;; + mIns[at + 5].mLinkerObject = lobj; + mIns[at + 5].mAddress = lobj ? naddr : naddr >> 8; + mIns[at + 5].mFlags = NCIF_UPPER; + + at += 6; + + changed = true; + + if (done) + return changed; + } + else if (ins.mMode == ASMIM_INDIRECT_Y && ins.mAddress == reg) + { + assert(yval >= 0); + + bool done = !(ins.mLive & LIVE_MEM); + + const InterInstruction* iins = ins.mIns; + + if (index) + ins.mMode = ASMIM_ABSOLUTE_Y; + else if (ins.mLinkerObject || address >= 256) + ins.mMode = ASMIM_ABSOLUTE; + else + ins.mMode = ASMIM_ZERO_PAGE; + ins.mLinkerObject = lobj; + ins.mAddress = address + yval; + ins.mFlags = (ins.mFlags & ~(NCIF_LOWER | NCIF_UPPER)) | flags; + if (index) + { + if (ins.mLive & LIVE_CPU_REG_Y) + { + mIns.Insert(at + 1, NativeCodeInstruction(iins, ASMIT_LDY, ASMIM_IMMEDIATE, yval)); + if (mIns[at].mLive & LIVE_CPU_REG_Z) + { + mIns.Insert(at + 2, NativeCodeInstruction(iins, ASMIT_ORA, ASMIM_IMMEDIATE, 0)); + mIns[at].mLive |= LIVE_CPU_REG_A; + } + } + mIns.Insert(at, NativeCodeInstruction(iins, ASMIT_LDY, ASMIM_ZERO_PAGE, index)); + } + at++; + + changed = true; + + if (done) + return changed; + } + else if (ins.mType == ASMIT_LDY && ins.mMode == ASMIM_IMMEDIATE) + yval = ins.mAddress; + else if (ins.mType == ASMIT_INY && yval >= 0) + yval = (yval + 1) & 255; + else if (ins.mType == ASMIT_DEY && yval >= 0) + yval = (yval - 1) & 255; + else if (ins.mType == ASMIT_INC && ins.mMode == ASMIM_ZERO_PAGE && ins.mAddress == reg + 1) + { + ins.mType = ASMIT_NOP; ins.mMode = ASMIM_IMPLIED; + address += 256; + } + else if (ins.ChangesYReg()) + yval = -1; + else if (index && at + 6 < mIns.Size() && + mIns[at + 0].mType == ASMIT_CLC && + mIns[at + 1].mType == ASMIT_LDA && mIns[at + 1].mMode == ASMIM_ZERO_PAGE && mIns[at + 1].mAddress == reg + 0 && + mIns[at + 2].mType == ASMIT_ADC && mIns[at + 2].mMode == ASMIM_IMMEDIATE && + mIns[at + 3].mType == ASMIT_STA && mIns[at + 3].mMode == ASMIM_ZERO_PAGE && + mIns[at + 4].mType == ASMIT_LDA && mIns[at + 4].mMode == ASMIM_ZERO_PAGE && mIns[at + 4].mAddress == reg + 1 && + mIns[at + 5].mType == ASMIT_ADC && mIns[at + 5].mMode == ASMIM_IMMEDIATE && + mIns[at + 6].mType == ASMIT_STA && mIns[at + 6].mMode == ASMIM_ZERO_PAGE) + { + int add = mIns[at + 2].mAddress + 256 * mIns[at + 5].mAddress; + mIns[at + 1].mAddress = index; + + mIns[at + 2].mMode = ASMIM_IMMEDIATE_ADDRESS; + mIns[at + 2].mLinkerObject = lobj; + mIns[at + 2].mAddress = address + add; + mIns[at + 2].mFlags = NCIF_LOWER; + + mIns[at + 4].mMode = ASMIM_IMMEDIATE; + mIns[at + 4].mAddress = 0; + + mIns[at + 5].mMode = ASMIM_IMMEDIATE_ADDRESS; + mIns[at + 5].mLinkerObject = lobj; + mIns[at + 5].mAddress = address + add; + mIns[at + 5].mFlags = NCIF_UPPER; + + changed = true; + } + + + at++; + } + + if (mTrueJump && mTrueJump->PatchGlobalAddressSumYPointer(block, reg, index, 0, yval, lobj, address, flags)) + changed = true; + if (mFalseJump && mFalseJump->PatchGlobalAddressSumYPointer(block, reg, index, 0, yval, lobj, address, flags)) + changed = true; + } + + return changed; +} + + +bool NativeCodeBasicBlock::FindGlobalAddressSumY(int at, int reg, bool direct, int& apos, const NativeCodeInstruction*& ains, const NativeCodeInstruction*& iins, uint32& flags, int& addr) +{ + flags = 0; + + int j = at - 7; + while (j >= 0) + { + if (mIns[j + 0].mType == ASMIT_CLC && + mIns[j + 1].mType == ASMIT_LDA && mIns[j + 1].mMode == ASMIM_IMMEDIATE_ADDRESS && (mIns[j + 1].mFlags & NCIF_LOWER) && mIns[j + 1].mLinkerObject && + mIns[j + 2].mType == ASMIT_ADC && mIns[j + 2].mMode == ASMIM_ZERO_PAGE && + mIns[j + 3].mType == ASMIT_STA && mIns[j + 3].mMode == ASMIM_ZERO_PAGE && mIns[j + 3].mAddress == reg && + mIns[j + 4].mType == ASMIT_LDA && mIns[j + 4].mMode == ASMIM_IMMEDIATE_ADDRESS && (mIns[j + 4].mFlags & NCIF_UPPER) && mIns[j + 4].mLinkerObject == mIns[j + 1].mLinkerObject && + mIns[j + 5].mType == ASMIT_ADC && mIns[j + 5].mMode == ASMIM_IMMEDIATE && mIns[j + 5].mAddress == 0 && + mIns[j + 6].mType == ASMIT_STA && mIns[j + 6].mMode == ASMIM_ZERO_PAGE && mIns[j + 6].mAddress == reg + 1) + { + ains = &(mIns[j + 1]); + iins = &(mIns[j + 2]); + apos = j + 0; + + int ireg = iins->mAddress; + if (reg == ireg) + { + if (!direct) + return false; + + flags = (LIVE_CPU_REG_X | LIVE_CPU_REG_Y) & ~mIns[j + 1].mLive; + + int k = j + 7; + while (k < at) + { + if (mIns[k].ChangesYReg()) + flags &= ~LIVE_CPU_REG_Y; + if (mIns[k].ChangesXReg()) + flags &= ~LIVE_CPU_REG_X; + k++; + } + + return flags != 0; + } + else + { + int k = j + 7; + while (k < at) + { + if (mIns[k].mMode == ASMIM_ZERO_PAGE && mIns[k].mAddress == ireg && mIns[k].ChangesAddress()) + return false; + k++; + } + + return true; + } + } + else if (mIns[j + 0].mType == ASMIT_CLC && + mIns[j + 1].mType == ASMIT_LDA && mIns[j + 1].mMode == ASMIM_ZERO_PAGE && + mIns[j + 2].mType == ASMIT_ADC && mIns[j + 2].mMode == ASMIM_IMMEDIATE_ADDRESS && (mIns[j + 2].mFlags & NCIF_LOWER) && mIns[j + 2].mLinkerObject && + mIns[j + 3].mType == ASMIT_STA && mIns[j + 3].mMode == ASMIM_ZERO_PAGE && mIns[j + 3].mAddress == reg && + mIns[j + 4].mType == ASMIT_LDA && mIns[j + 4].mMode == ASMIM_IMMEDIATE_ADDRESS && (mIns[j + 4].mFlags & NCIF_UPPER) && mIns[j + 4].mLinkerObject == mIns[j + 2].mLinkerObject && + mIns[j + 5].mType == ASMIT_ADC && mIns[j + 5].mMode == ASMIM_IMMEDIATE && mIns[j + 5].mAddress == 0 && + mIns[j + 6].mType == ASMIT_STA && mIns[j + 6].mMode == ASMIM_ZERO_PAGE && mIns[j + 6].mAddress == reg + 1) + { + ains = &(mIns[j + 2]); + iins = &(mIns[j + 1]); + apos = j + 0; + + int ireg = iins->mAddress; + if (reg == ireg) + { + if (!direct) + return false; + + flags = (LIVE_CPU_REG_X | LIVE_CPU_REG_Y) & ~mIns[j + 1].mLive; + + int k = j + 7; + while (k < at) + { + if (mIns[k].ChangesYReg()) + flags &= ~LIVE_CPU_REG_Y; + if (mIns[k].ChangesXReg()) + flags &= ~LIVE_CPU_REG_X; + k++; + } + + return flags != 0; + } + else + { + int k = j + 7; + while (k < at) + { + if (mIns[k].mMode == ASMIM_ZERO_PAGE && mIns[k].mAddress == ireg && mIns[k].ChangesAddress()) + return false; + k++; + } + + return true; + } + } + else if ( + mIns[j + 0].mType == ASMIT_STA && mIns[j + 0].mMode == ASMIM_ZERO_PAGE && + mIns[j + 1].mType == ASMIT_CLC && + mIns[j + 2].mType == ASMIT_ADC && mIns[j + 2].mMode == ASMIM_IMMEDIATE_ADDRESS && (mIns[j + 2].mFlags & NCIF_LOWER) && mIns[j + 2].mLinkerObject && + mIns[j + 3].mType == ASMIT_STA && mIns[j + 3].mMode == ASMIM_ZERO_PAGE && mIns[j + 3].mAddress == reg && + mIns[j + 4].mType == ASMIT_LDA && mIns[j + 4].mMode == ASMIM_IMMEDIATE_ADDRESS && (mIns[j + 4].mFlags & NCIF_UPPER) && mIns[j + 4].mLinkerObject == mIns[j + 2].mLinkerObject && + mIns[j + 5].mType == ASMIT_ADC && mIns[j + 5].mMode == ASMIM_IMMEDIATE && mIns[j + 5].mAddress == 0 && + mIns[j + 6].mType == ASMIT_STA && mIns[j + 6].mMode == ASMIM_ZERO_PAGE && mIns[j + 6].mAddress == reg + 1) + { + ains = &(mIns[j + 2]); + iins = &(mIns[j + 0]); + apos = j + 1; + + int ireg = iins->mAddress; + if (reg == ireg) + { + if (!direct) + return false; + + flags = (LIVE_CPU_REG_X | LIVE_CPU_REG_Y) & ~mIns[j + 1].mLive; + + int k = j + 7; + while (k < at) + { + if (mIns[k].ChangesYReg()) + flags &= ~LIVE_CPU_REG_Y; + if (mIns[k].ChangesXReg()) + flags &= ~LIVE_CPU_REG_X; + k++; + } + + return flags != 0; + } + else + { + int k = j + 7; + while (k < at) + { + if (mIns[k].mMode == ASMIM_ZERO_PAGE && mIns[k].mAddress == ireg && mIns[k].ChangesAddress()) + return false; + k++; + } + + return true; + } + } + else if (mIns[j + 0].mType == ASMIT_CLC && + mIns[j + 1].mType == ASMIT_LDA && mIns[j + 1].mMode == ASMIM_IMMEDIATE_ADDRESS && (mIns[j + 1].mFlags & NCIF_LOWER) && mIns[j + 1].mLinkerObject && + mIns[j + 2].mType == ASMIT_ADC && mIns[j + 2].mMode == ASMIM_ABSOLUTE && + mIns[j + 3].mType == ASMIT_STA && mIns[j + 3].mMode == ASMIM_ZERO_PAGE && mIns[j + 3].mAddress == reg && + mIns[j + 4].mType == ASMIT_LDA && mIns[j + 4].mMode == ASMIM_IMMEDIATE_ADDRESS && (mIns[j + 4].mFlags & NCIF_UPPER) && mIns[j + 4].mLinkerObject == mIns[j + 1].mLinkerObject && + mIns[j + 5].mType == ASMIT_ADC && mIns[j + 5].mMode == ASMIM_IMMEDIATE && mIns[j + 5].mAddress == 0 && + mIns[j + 6].mType == ASMIT_STA && mIns[j + 6].mMode == ASMIM_ZERO_PAGE && mIns[j + 6].mAddress == reg + 1) + { + ains = &(mIns[j + 1]); + iins = &(mIns[j + 2]); + apos = j + 0; + + int k = j + 7; + while (k < at) + { + if (mIns[k].ChangesGlobalMemory()) + return false; + k++; + } + + return true; + } +#if 1 + else if ( + mIns[j + 1].mType == ASMIT_CLC && + mIns[j + 2].mType == ASMIT_ADC && mIns[j + 2].mMode == ASMIM_IMMEDIATE_ADDRESS && (mIns[j + 2].mFlags & NCIF_LOWER) && mIns[j + 2].mLinkerObject && + mIns[j + 3].mType == ASMIT_STA && mIns[j + 3].mMode == ASMIM_ZERO_PAGE && mIns[j + 3].mAddress == reg && + mIns[j + 4].mType == ASMIT_LDA && mIns[j + 4].mMode == ASMIM_IMMEDIATE_ADDRESS && (mIns[j + 4].mFlags & NCIF_UPPER) && mIns[j + 4].mLinkerObject == mIns[j + 2].mLinkerObject && + mIns[j + 5].mType == ASMIT_ADC && mIns[j + 5].mMode == ASMIM_IMMEDIATE && mIns[j + 5].mAddress == 0 && + mIns[j + 6].mType == ASMIT_STA && mIns[j + 6].mMode == ASMIM_ZERO_PAGE && mIns[j + 6].mAddress == reg + 1) + { + ains = &(mIns[j + 2]); + iins = nullptr; + apos = j + 1; + + if (!direct) + return false; + + flags = (LIVE_CPU_REG_X | LIVE_CPU_REG_Y) & ~mIns[j + 1].mLive; + + if (mIns[j + 0].mType == ASMIT_TAX) + flags |= LIVE_CPU_REG_X; + if (mIns[j + 0].mType == ASMIT_TAY) + flags |= LIVE_CPU_REG_Y; + + int k = j + 7; + while (k < at) + { + assert(!(flags & LIVE_CPU_REG_Y) || mIns[k].mType != ASMIT_TYA); + if (mIns[k].ChangesYReg()) + flags &= ~LIVE_CPU_REG_Y; + if (mIns[k].ChangesXReg()) + flags &= ~LIVE_CPU_REG_X; + k++; + } + + return flags != 0; + } + else if ( + mIns[j + 1].mType == ASMIT_CLC && + mIns[j + 2].mType == ASMIT_ADC && mIns[j + 2].mMode == ASMIM_IMMEDIATE_ADDRESS && (mIns[j + 2].mFlags & NCIF_LOWER) && mIns[j + 2].mLinkerObject && + mIns[j + 3].mType == ASMIT_STA && mIns[j + 3].mMode == ASMIM_ZERO_PAGE && mIns[j + 3].mAddress == reg && + mIns[j + 4].mType == ASMIT_LDA && mIns[j + 4].mMode == ASMIM_IMMEDIATE && mIns[j + 4].mAddress == 0 && + mIns[j + 5].mType == ASMIT_ADC && mIns[j + 5].mMode == ASMIM_IMMEDIATE_ADDRESS && (mIns[j + 5].mFlags & NCIF_UPPER) && mIns[j + 5].mLinkerObject == mIns[j + 2].mLinkerObject && + mIns[j + 6].mType == ASMIT_STA && mIns[j + 6].mMode == ASMIM_ZERO_PAGE && mIns[j + 6].mAddress == reg + 1) + { + ains = &(mIns[j + 2]); + iins = nullptr; + apos = j + 1; + + if (!direct) + return false; + + flags = (LIVE_CPU_REG_X | LIVE_CPU_REG_Y) & ~mIns[j + 1].mLive; + + if (mIns[j + 0].mType == ASMIT_TAX) + flags |= LIVE_CPU_REG_X; + if (mIns[j + 0].mType == ASMIT_TAY) + flags |= LIVE_CPU_REG_Y; + + int k = j + 7; + while (k < at) + { + assert(!(flags & LIVE_CPU_REG_Y) || mIns[k].mType != ASMIT_TYA); + if (mIns[k].ChangesYReg()) + flags &= ~LIVE_CPU_REG_Y; + if (mIns[k].ChangesXReg()) + flags &= ~LIVE_CPU_REG_X; + k++; + } + + return flags != 0; + } + else if (mIns[j + 0].mType == ASMIT_CLC && + mIns[j + 1].mType == ASMIT_LDA && mIns[j + 1].mMode == ASMIM_ZERO_PAGE && + mIns[j + 2].mType == ASMIT_ADC && mIns[j + 2].mMode == ASMIM_IMMEDIATE && + mIns[j + 3].mType == ASMIT_STA && mIns[j + 3].mMode == ASMIM_ZERO_PAGE && mIns[j + 3].mAddress == reg && + mIns[j + 4].mType == ASMIT_LDA && mIns[j + 4].mMode == ASMIM_IMMEDIATE && + mIns[j + 5].mType == ASMIT_ADC && mIns[j + 5].mMode == ASMIM_IMMEDIATE && + mIns[j + 6].mType == ASMIT_STA && mIns[j + 6].mMode == ASMIM_ZERO_PAGE && mIns[j + 6].mAddress == reg + 1) + { + ains = &(mIns[j + 2]); + iins = &(mIns[j + 1]); + apos = j + 0; + + addr = mIns[j + 2].mAddress + 256 * (mIns[j + 4].mAddress + mIns[j + 5].mAddress); + + int ireg = iins->mAddress; + if (reg == ireg) + { + if (!direct) + return false; + + flags = (LIVE_CPU_REG_X | LIVE_CPU_REG_Y) & ~mIns[j + 1].mLive; + + int k = j + 7; + while (k < at) + { + if (mIns[k].ChangesYReg()) + flags &= ~LIVE_CPU_REG_Y; + if (mIns[k].ChangesXReg()) + flags &= ~LIVE_CPU_REG_X; + k++; + } + + return flags != 0; + } + else + { + int k = j + 7; + while (k < at) + { + if (mIns[k].mMode == ASMIM_ZERO_PAGE && mIns[k].mAddress == ireg && mIns[k].ChangesAddress()) + return false; + k++; + } + + return true; + } + } + else if (mIns[j + 0].mType == ASMIT_CLC && + mIns[j + 1].mType == ASMIT_LDA && mIns[j + 1].mMode == ASMIM_IMMEDIATE && + mIns[j + 2].mType == ASMIT_ADC && mIns[j + 2].mMode == ASMIM_ZERO_PAGE && + mIns[j + 3].mType == ASMIT_STA && mIns[j + 3].mMode == ASMIM_ZERO_PAGE && mIns[j + 3].mAddress == reg && + mIns[j + 4].mType == ASMIT_LDA && mIns[j + 4].mMode == ASMIM_IMMEDIATE && + mIns[j + 5].mType == ASMIT_ADC && mIns[j + 5].mMode == ASMIM_IMMEDIATE && + mIns[j + 6].mType == ASMIT_STA && mIns[j + 6].mMode == ASMIM_ZERO_PAGE && mIns[j + 6].mAddress == reg + 1) + { + ains = &(mIns[j + 1]); + iins = &(mIns[j + 2]); + apos = j + 0; + + addr = mIns[j + 1].mAddress + 256 * (mIns[j + 4].mAddress + mIns[j + 5].mAddress); + + int ireg = iins->mAddress; + if (reg == ireg) + { + if (!direct) + return false; + + flags = (LIVE_CPU_REG_X | LIVE_CPU_REG_Y) & ~mIns[j + 1].mLive; + + int k = j + 7; + while (k < at) + { + if (mIns[k].ChangesYReg()) + flags &= ~LIVE_CPU_REG_Y; + if (mIns[k].ChangesXReg()) + flags &= ~LIVE_CPU_REG_X; + k++; + } + + return flags != 0; + } + else + { + int k = j + 7; + while (k < at) + { + if (mIns[k].mMode == ASMIM_ZERO_PAGE && mIns[k].mAddress == ireg && mIns[k].ChangesAddress()) + return false; + k++; + } + + return true; + } + } + else if ( + mIns[j + 1].mType == ASMIT_CLC && + mIns[j + 2].mType == ASMIT_ADC && mIns[j + 2].mMode == ASMIM_IMMEDIATE && + mIns[j + 3].mType == ASMIT_STA && mIns[j + 3].mMode == ASMIM_ZERO_PAGE && mIns[j + 3].mAddress == reg && + mIns[j + 4].mType == ASMIT_LDA && mIns[j + 4].mMode == ASMIM_IMMEDIATE && + mIns[j + 5].mType == ASMIT_ADC && mIns[j + 5].mMode == ASMIM_IMMEDIATE && + mIns[j + 6].mType == ASMIT_STA && mIns[j + 6].mMode == ASMIM_ZERO_PAGE && mIns[j + 6].mAddress == reg + 1) + { + ains = &(mIns[j + 2]); + iins = nullptr; + apos = j + 1; + + addr = mIns[j + 2].mAddress + 256 * (mIns[j + 4].mAddress + mIns[j + 5].mAddress); + + if (!direct) + return false; + + flags = (LIVE_CPU_REG_X | LIVE_CPU_REG_Y) & ~mIns[j + 1].mLive; + + if (mIns[j + 0].mType == ASMIT_TAX) + flags |= LIVE_CPU_REG_X; + if (mIns[j + 0].mType == ASMIT_TAY) + flags |= LIVE_CPU_REG_Y; + + int k = j + 7; + while (k < at) + { + assert(!(flags & LIVE_CPU_REG_Y) || mIns[k].mType != ASMIT_TYA); + if (mIns[k].ChangesYReg()) + flags &= ~LIVE_CPU_REG_Y; + if (mIns[k].ChangesXReg()) + flags &= ~LIVE_CPU_REG_X; + k++; + } + + return flags != 0; + } +#endif + + if (mIns[j + 6].mMode == ASMIM_ZERO_PAGE && (mIns[j + 6].mAddress == reg || mIns[j + 6].mAddress == reg + 1) && mIns[j + 6].ChangesAddress()) + return false; + + j--; + } + + if (at >= 6 && + mIns[0].mType == ASMIT_CLC && + mIns[1].mType == ASMIT_ADC && mIns[1].mMode == ASMIM_IMMEDIATE_ADDRESS && (mIns[1].mFlags & NCIF_LOWER) && mIns[1].mLinkerObject && + mIns[2].mType == ASMIT_STA && mIns[2].mMode == ASMIM_ZERO_PAGE && mIns[2].mAddress == reg && + mIns[3].mType == ASMIT_LDA && mIns[3].mMode == ASMIM_IMMEDIATE_ADDRESS && (mIns[3].mFlags & NCIF_UPPER) && mIns[3].mLinkerObject == mIns[1].mLinkerObject && + mIns[4].mType == ASMIT_ADC && mIns[4].mMode == ASMIM_IMMEDIATE && mIns[4].mAddress == 0 && + mIns[5].mType == ASMIT_STA && mIns[5].mMode == ASMIM_ZERO_PAGE && mIns[5].mAddress == reg + 1) + { + ains = &(mIns[1]); + iins = nullptr; + apos = 0; + + if (!direct) + return false; + + flags = (LIVE_CPU_REG_X | LIVE_CPU_REG_Y) & ~mIns[0].mLive; + + int k = 6; + while (k < at) + { + assert(!(flags & LIVE_CPU_REG_Y) || mIns[k].mType != ASMIT_TYA); + if (mIns[k].ChangesYReg()) + flags &= ~LIVE_CPU_REG_Y; + if (mIns[k].ChangesXReg()) + flags &= ~LIVE_CPU_REG_X; + k++; + } + + return flags != 0; + } + + if (mFromJump) + { + while (j >= -6) + { + if (mIns[j + 6].mMode == ASMIM_ZERO_PAGE && (mIns[j + 6].mAddress == reg || mIns[j + 6].mAddress == reg + 1) && mIns[j + 6].ChangesAddress()) + return false; + j--; + } + if (mFromJump->FindGlobalAddressSumY(mFromJump->mIns.Size(), reg, false, apos, ains, iins, flags, addr)) + { + if (iins->mMode == ASMIM_ZERO_PAGE) + { + int ireg = iins->mAddress; + int k = 0; + while (k < at) + { + if (mIns[k].mMode == ASMIM_ZERO_PAGE && mIns[k].mAddress == ireg && mIns[k].ChangesAddress()) + return false; + k++; + } + } + else + { + int k = 0; + while (k < at) + { + if (mIns[k].ChangesGlobalMemory()) + return false; + k++; + } + } + + return true; + } + } + + return false; +} + +bool NativeCodeBasicBlock::FindSharedGlobalAddressSumY(int at, int reg, const NativeCodeInstruction*& ains, const NativeCodeInstruction*& iins) +{ + int j = at - 7; + while (j >= 0) + { + if (mIns[j + 0].mType == ASMIT_CLC && + mIns[j + 1].mType == ASMIT_LDA && mIns[j + 1].mMode == ASMIM_IMMEDIATE_ADDRESS && (mIns[j + 1].mFlags & NCIF_LOWER) && mIns[j + 1].mLinkerObject && + mIns[j + 2].mType == ASMIT_ADC && mIns[j + 2].mMode == ASMIM_ZERO_PAGE && + mIns[j + 3].mType == ASMIT_STA && mIns[j + 3].mMode == ASMIM_ZERO_PAGE && mIns[j + 3].mAddress == reg && + mIns[j + 4].mType == ASMIT_LDA && mIns[j + 4].mMode == ASMIM_IMMEDIATE_ADDRESS && (mIns[j + 4].mFlags & NCIF_UPPER) && mIns[j + 4].mLinkerObject == mIns[j + 1].mLinkerObject && + mIns[j + 5].mType == ASMIT_ADC && mIns[j + 5].mMode == ASMIM_IMMEDIATE && mIns[j + 5].mAddress == 0 && + mIns[j + 6].mType == ASMIT_STA && mIns[j + 6].mMode == ASMIM_ZERO_PAGE && mIns[j + 6].mAddress == reg + 1) + { + ains = &(mIns[j + 1]); + iins = &(mIns[j + 2]); + + int ireg = iins->mAddress; + if (reg == ireg) + return false; + + if (ChangesZeroPage(ireg, j + 7, at)) + return false; + + return true; + } + + if (mIns[j + 6].ChangesZeroPage(reg) || mIns[j + 6].ChangesZeroPage(reg + 1)) + return false; + + j--; + } + + if (mLoopHead) + return false; + + if (mEntryBlocks.Size() > 0) + { + while (j >= -6) + { + if (mIns[j + 6].ChangesZeroPage(reg) || mIns[j + 6].ChangesZeroPage(reg + 1)) + return false; + j--; + } + + if (mEntryBlocks[0]->FindSharedGlobalAddressSumY(mEntryBlocks[0]->mIns.Size(), reg, ains, iins)) + { + if (ChangesZeroPage(iins->mAddress, 0, at)) + return false; + + const NativeCodeInstruction* ains0; + const NativeCodeInstruction* iins0; + + int i = 1; + while (i < mEntryBlocks.Size() && mEntryBlocks[i]->FindSharedGlobalAddressSumY(mEntryBlocks[i]->mIns.Size(), reg, ains0, iins0) && ains0->IsSame(*ains) && iins0->IsSame(*iins)) + i++; + if (i == mEntryBlocks.Size()) + return true; + } + } + + return false; +} + +bool NativeCodeBasicBlock::JoinTAXARange(int from, int to) +{ + int start = from; + if (from >= 2) + { + start = from - 2; + if (mIns[start].mType == ASMIT_LDA && mIns[start].mMode == ASMIM_ZERO_PAGE && mIns[start + 1].mType == ASMIT_AND && mIns[start + 1].mMode == ASMIM_IMMEDIATE) + { + + for (int i = from + 1; i < to; i++) + { + if (mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i].mAddress == mIns[start].mAddress && mIns[i].ChangesAddress()) + return false; + } + + int live = mIns[to].mLive; + mIns.Remove(to); + for (int i = start; i < from; i++) + { + mIns.Insert(to, mIns[start]); + mIns[to].mLive |= live; + mIns.Remove(start); + } + mIns.Remove(start); + + CheckLive(); + + return true; + } + else if (mIns[start].mType == ASMIT_TXA && mIns[start + 1].mType == ASMIT_AND && mIns[start + 1].mMode == ASMIM_IMMEDIATE && !(mIns[start + 2].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z))) + { + mIns.Insert(to + 1, NativeCodeInstruction(mIns[start + 1].mIns, ASMIT_AND, ASMIM_IMMEDIATE, mIns[start + 1].mAddress)); + mIns.Remove(start, 3); + + CheckLive(); + + return true; + } + else if (mIns[start].mType == ASMIT_LDA && mIns[start].mMode == ASMIM_ABSOLUTE_X && mIns[start + 1].ChangesAccu() && !mIns[start + 1].ChangesAddress() && !mIns[start + 1].RequiresYReg()) + { + for (int i = from + 1; i < to; i++) + { + if (mIns[start].MayBeChangedOnAddress(mIns[i]) || mIns[start + 1].MayBeChangedOnAddress(mIns[i])) + return false; + if (mIns[start + 1].RequiresCarry() && mIns[i].ChangesCarry()) + return false; + if ((mIns[start + 1].mLive & LIVE_CPU_REG_C) && mIns[i].RequiresCarry()) + return false; + } + + int live = mIns[to].mLive; + mIns.Remove(to); + for (int i = start; i < from; i++) + { + mIns.Insert(to, mIns[start]); + mIns[to].mLive |= live; + mIns.Remove(start); + } + mIns.Remove(start); + + CheckLive(); + + return true; + } + else if (mIns[start + 0].mType == ASMIT_LDY && mIns[start + 0].mMode == ASMIM_IMMEDIATE && + mIns[start + 1].mType == ASMIT_LDA && mIns[start + 1].mMode == ASMIM_INDIRECT_Y && + !(mIns[start + 1].mLive & LIVE_CPU_REG_Y) && !(mIns[to].mLive & LIVE_CPU_REG_Y)) + { + + for (int i = from + 1; i < to; i++) + { + if (mIns[i].mMode == ASMIM_ZERO_PAGE && (mIns[i].mAddress == mIns[start + 1].mAddress || mIns[i].mAddress == mIns[start + 1].mAddress + 1) && mIns[i].ChangesAddress()) + return false; + if (mIns[i].ChangesGlobalMemory()) + return false; + } + + int live = mIns[to].mLive; + mIns.Remove(to); + for (int i = start; i < from; i++) + { + mIns.Insert(to, mIns[start]); + mIns[to].mLive |= live; + mIns.Remove(start); + } + mIns.Remove(start); + + CheckLive(); + + return true; + } + + if (!(mIns[from].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C | LIVE_CPU_REG_Z)) && !(mIns[to].mLive & LIVE_CPU_REG_C)) + { + start = from; + while (start > 0 && + mIns[start - 1].mType != ASMIT_LDA && + mIns[start - 1].mType != ASMIT_TAY && + mIns[start - 1].mType != ASMIT_TAX && + (mIns[start - 1].mMode == ASMIM_IMMEDIATE || mIns[start - 1].mMode == ASMIM_IMPLIED)) + start--; + if (start > 0 && mIns[start - 1].mType == ASMIT_LDA && mIns[start - 1].mMode == ASMIM_ZERO_PAGE) + { + for (int i = from + 1; i < to; i++) + { + if (mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i].mAddress == mIns[start - 1].mAddress && mIns[i].ChangesAddress()) + return false; + if ((mIns[start - 1].mLive & LIVE_CPU_REG_C) && mIns[i].ChangesCarry()) + return false; + } + + start--; + + int plive = mIns[start].mLive; + for (int i = from; i < to; i++) + mIns[i].mLive |= plive; + + if (mIns[to].mLive & LIVE_CPU_REG_Y) + { + for (int i = start; i < from; i++) + mIns[i].mLive |= LIVE_CPU_REG_Y; + } + + int live = mIns[to].mLive; + mIns.Remove(to); + for (int i = start; i < from; i++) + { + mIns.Insert(to, mIns[start]); + mIns[to].mLive |= live; + mIns.Remove(start); + } + mIns.Remove(start); + + CheckLive(); + + return true; + } + } + } + + if (from > 0 && mIns[from - 1].mType == ASMIT_LDA && mIns[from - 1].mMode == ASMIM_ABSOLUTE_X) + { + for (int i = from + 1; i < to; i++) + { + if (mIns[from - 1].MayBeChangedOnAddress(mIns[i])) + return false; + } + + int live = mIns[to].mLive; + mIns.Remove(to); + mIns.Insert(to, mIns[from - 1]); + mIns[to].mLive |= live; + mIns.Remove(from - 1); + + CheckLive(); + + return true; + } + + if (to + 2 < mIns.Size() && mIns[to + 2].mType == ASMIT_STA && HasAsmInstructionMode(ASMIT_STX, mIns[to + 2].mMode) && !(mIns[to + 2].mLive & LIVE_CPU_REG_X) && !ReferencesXReg(from + 1, to)) + { + if (mIns[to + 1].mType == ASMIT_ORA && mIns[to + 1].mMode == ASMIM_IMMEDIATE) + { + if (mIns[to + 2].mLive & LIVE_CPU_REG_A) + { + if (to + 3 < mIns.Size() && mIns[to + 3].mType == ASMIT_STA && HasAsmInstructionMode(ASMIT_STX, mIns[to + 3].mMode) && !(mIns[to + 3].mLive & LIVE_CPU_REG_A)) + { + mIns[to + 0].mType = ASMIT_NOP; mIns[to + 0].mMode = ASMIM_IMPLIED; + mIns[to + 1].mType = ASMIT_NOP; mIns[to + 1].mMode = ASMIM_IMPLIED; + mIns[to + 2].mType = ASMIT_STX; mIns[to + 2].mLive |= LIVE_CPU_REG_X; + mIns[to + 3].mType = ASMIT_STX; + mIns.Insert(from, NativeCodeInstruction(mIns[to + 0].mIns, ASMIT_ORA, ASMIM_IMMEDIATE, mIns[to + 1].mAddress)); + return true; + } + } + else + { + mIns[to + 0].mType = ASMIT_NOP; mIns[to + 0].mMode = ASMIM_IMPLIED; + mIns[to + 1].mType = ASMIT_NOP; mIns[to + 1].mMode = ASMIM_IMPLIED; + mIns[to + 2].mType = ASMIT_STX; + mIns.Insert(from, NativeCodeInstruction(mIns[to + 0].mIns, ASMIT_ORA, ASMIM_IMMEDIATE, mIns[to + 1].mAddress)); + return true; + } + } + } + + int i = from + 1; + while (i < to && (mIns[i].mType == ASMIT_LDA || mIns[i].mType == ASMIT_STA) && (mIns[i].mMode == ASMIM_IMMEDIATE || mIns[i].mMode == ASMIM_IMMEDIATE_ADDRESS || mIns[i].mMode == ASMIM_ABSOLUTE || mIns[i].mMode == ASMIM_ZERO_PAGE)) + i++; + if (i == to) + { + for (int i = from + 1; i < to; i++) + { + if (mIns[i].mType == ASMIT_LDA) + mIns[i].mType = ASMIT_LDX; + else if (mIns[i].mType == ASMIT_STA) + mIns[i].mType = ASMIT_STX; + mIns[i].mLive |= LIVE_CPU_REG_X | LIVE_CPU_REG_A; + } + mIns[from].mType = ASMIT_NOP; mIns[from].mMode = ASMIM_IMPLIED; + mIns[to].mType = ASMIT_NOP; mIns[to].mMode = ASMIM_IMPLIED; + + CheckLive(); + + return true; + } + + if (!(mIns[from].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z)) && to + 1 < mIns.Size() && mIns[to + 1].IsLogic() && !(mIns[to + 1].mLive & LIVE_CPU_REG_Z)) + { + int k = from + 1; + while (k < to && !mIns[to + 1].MayBeChangedOnAddress(mIns[k])) + k++; + if (k == to) + { + NativeCodeInstruction ins(mIns[to + 1]); + ins.mLive |= mIns[from].mLive & (LIVE_CPU_REG_C | LIVE_CPU_REG_Y); + mIns.Remove(to + 1); + mIns.Insert(from, ins); + + CheckLive(); + + return true; + } + } + + if (from > 0 && mIns[from - 1].mType == ASMIT_STA && mIns[from - 1].mMode == ASMIM_ZERO_PAGE && !(mIns[to].mLive & LIVE_CPU_REG_X) && + !ChangesZeroPage(mIns[from - 1].mAddress, from, to) && !ReferencesXReg(from + 1, to)) + { + mIns[to].mType = ASMIT_LDA; mIns[to].mMode = ASMIM_ZERO_PAGE; mIns[to].mAddress = mIns[from - 1].mAddress; + mIns[from].mType = ASMIT_NOP; + + CheckLive(); + + return true; + } + + if (to + 1 < mIns.Size() && mIns[to + 1].mType == ASMIT_STA && !(mIns[to + 1].mLive & LIVE_CPU_REG_A)) + { + NativeCodeInstruction ins(mIns[to + 1]); + + if (ins.mMode == ASMIM_ABSOLUTE_Y) + { + for (int i = from + 1; i < to; i++) + { + if (mIns[i].ChangesAddress()) + { + if (mIns[i].mMode == ASMIM_INDIRECT_Y || mIns[i].mMode == ASMIM_ABSOLUTE_X) + return false; + else if (mIns[i].mMode == ASMIM_ABSOLUTE) + { + if (mIns[i].mLinkerObject == ins.mLinkerObject && mIns[i].mAddress >= ins.mAddress && mIns[i].mAddress < ins.mAddress + 256) + return false; + } + else if (mIns[i].mMode == ASMIM_ABSOLUTE_Y) + { + if (mIns[i].mLinkerObject == ins.mLinkerObject && mIns[i].mAddress == ins.mAddress) + return false; + } + } + else if (mIns[i].ChangesYReg()) + return false; + } + + mIns.Remove(to); + mIns.Remove(to); + mIns.Remove(from); + + mIns.Insert(from, ins); + + CheckLive(); + + return true; + } + else if (ins.mMode == ASMIM_INDIRECT_Y) + { + for (int i = from + 1; i < to; i++) + { + if (mIns[i].MayBeSameAddress(ins)) + return false; + else if (mIns[i].ChangesZeroPage(ins.mAddress) || mIns[i].ChangesZeroPage(ins.mAddress + 1)) + return false; + else if (mIns[i].ChangesYReg()) + return false; + } + + ins.mLive |= mIns[from].mLive; + + mIns.Remove(to); + mIns.Remove(to); + mIns.Remove(from); + + mIns.Insert(from, ins); + + CheckLive(); + + return true; + } + } + + return false; +} + +bool NativeCodeBasicBlock::JoinTAYARange(int from, int to) +{ + int start = from; + if (from >= 2) + { + start = from - 2; + if (mIns[start + 0].mType == ASMIT_LDY && mIns[start + 0].mMode == ASMIM_IMMEDIATE && + mIns[start + 1].mType == ASMIT_LDA && mIns[start + 1].mMode == ASMIM_INDIRECT_Y) + { + + for (int i = from + 1; i < to; i++) + { + if (mIns[i].mMode == ASMIM_ZERO_PAGE && (mIns[i].mAddress == mIns[start + 1].mAddress || mIns[i].mAddress == mIns[start + 1].mAddress + 1) && mIns[i].ChangesAddress()) + return false; + if (mIns[i].ChangesGlobalMemory()) + return false; + } + + mIns.Remove(to); + for (int i = start; i < from; i++) + { + mIns.Insert(to, mIns[start]); + mIns.Remove(start); + } + mIns.Remove(start); + + CheckLive(); + + return true; + } + } + if (from >= 1) + { + if (mIns[from - 1].mMode == ASMIM_IMMEDIATE && !mIns[from - 1].RequiresCarry()) + { + mIns.Insert(to + 1, mIns[from - 1]); + mIns[to + 1].mLive |= mIns[to].mLive; + mIns.Remove(from - 1); + CheckLive(); + return true; + } + } +#if 1 + if (to + 2 < mIns.Size() && + !(mIns[from].mLive & LIVE_CPU_REG_A) && + mIns[to + 1].mType == ASMIT_LDY && + mIns[to + 2].mType == ASMIT_STA && !(mIns[to + 2].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z))) + { + bool usesX = mIns[to + 1].ReferencesXReg() || mIns[to + 2].ReferencesXReg(); + uint32 vflag = (mIns[to + 1].mFlags | mIns[to + 2].mFlags) & NCIF_VOLATILE; + int i = to - 1; + while (i > from && + !mIns[i].MayBeChangedOnAddress(mIns[to + 2]) && + !mIns[to + 1].MayBeChangedOnAddress(mIns[i]) && + !(mIns[to + 2].mMode == ASMIM_INDIRECT_Y && mIns[i].mMode == ASMIM_ZERO_PAGE && (mIns[i].mAddress == mIns[to + 2].mAddress || mIns[i].mAddress == mIns[to + 2].mAddress + 1) && mIns[i].ChangesAddress()) && + !mIns[i].ReferencesYReg() && + !(mIns[i].mFlags & vflag) && + !(usesX && mIns[i].ChangesXReg())) + { + i--; + } + if (i == from) + { + NativeCodeInstruction i0 = mIns[to + 1]; + NativeCodeInstruction i1 = mIns[to + 2]; + + i0.mLive |= mIns[from].mLive; + i1.mLive |= mIns[from].mLive; + + mIns.Remove(to, 3); + mIns.Remove(from); + mIns.Insert(from, i0); + mIns.Insert(from + 1, i1); + return true; + } + } +#endif + + + return false; +} + +bool NativeCodeBasicBlock::JoinTAYARangeLive(int from, int to) +{ + int k = from + 1; + while (k < to && + (mIns[k].mType == ASMIT_LDA && HasAsmInstructionMode(ASMIT_LDY, mIns[k].mMode) || + mIns[k].mType == ASMIT_STA && HasAsmInstructionMode(ASMIT_STY, mIns[k].mMode))) + k++; + if (k == to) + { + for (int i = from + 1; i < to; i++) + { + if (mIns[i].mType == ASMIT_LDA) + mIns[i].mType = ASMIT_LDY; + else if (mIns[i].mType == ASMIT_STA) + mIns[i].mType = ASMIT_STY; + mIns[i].mLive |= LIVE_CPU_REG_A; + } + mIns[from].mType = ASMIT_NOP; + mIns[to].mType = ASMIT_TAY; + return true; + } + + return false; +} + +bool NativeCodeBasicBlock::JoinTAXARangeLive(int from, int to) +{ + int k = from + 1; + while (k < to && + (mIns[k].mType == ASMIT_LDA && HasAsmInstructionMode(ASMIT_LDX, mIns[k].mMode) || + mIns[k].mType == ASMIT_STA && HasAsmInstructionMode(ASMIT_STX, mIns[k].mMode))) + k++; + if (k == to) + { + for (int i = from + 1; i < to; i++) + { + if (mIns[i].mType == ASMIT_LDA) + mIns[i].mType = ASMIT_LDX; + else if (mIns[i].mType == ASMIT_STA) + mIns[i].mType = ASMIT_STX; + mIns[i].mLive |= LIVE_CPU_REG_A; + } + mIns[from].mType = ASMIT_NOP; + mIns[to].mType = ASMIT_TAX; + return true; + } + + return false; +} + +bool NativeCodeBasicBlock::ReverseReplaceTAX(int at) +{ + int i = at; + while (i > 0) + { + if (mIns[i - 1].mType == ASMIT_LDA && HasAsmInstructionMode(ASMIT_LDX, mIns[i - 1].mMode)) + { + mIns[i - 1].mType = ASMIT_LDX; + mIns[i - 1].mLive |= LIVE_CPU_REG_X; + while (i < at) + { + mIns[i].mLive |= LIVE_CPU_REG_X; + if (mIns[i].mType == ASMIT_STA) + mIns[i].mType = ASMIT_STX; + + i++; + } + mIns[at].mType = ASMIT_NOP; + + return true; + } + else if (mIns[i - 1].mType == ASMIT_STA && HasAsmInstructionMode(ASMIT_STX, mIns[i - 1].mMode)) + { + + } + else + return false; + + i--; + } + + return false; +} + + +bool NativeCodeBasicBlock::MergeXYSameValue(int from) +{ + int to = from; + while (to < mIns.Size()) + { + if (mIns[to].ChangesYReg() || mIns[to].ChangesXReg()) + return false; + + if (!(mIns[to].mLive & LIVE_CPU_REG_X)) + { + for (int i = from; i <= to; i++) + { + if (mIns[i].mMode == ASMIM_INDIRECT_X) + return false; + else if (mIns[i].mMode == ASMIM_ABSOLUTE_X) + { + if (!HasAsmInstructionMode(mIns[i].mType, ASMIM_ABSOLUTE_Y)) + return false; + } + } + + return ReplaceXRegWithYReg(from, to + 1); + } + else if (!(mIns[to].mLive & LIVE_CPU_REG_Y)) + { + for (int i = from; i <= to; i++) + { + if (mIns[i].mMode == ASMIM_INDIRECT_Y) + return false; + else if (mIns[i].mMode == ASMIM_ABSOLUTE_Y) + { + if (!HasAsmInstructionMode(mIns[i].mType, ASMIM_ABSOLUTE_X)) + return false; + } + } + + return ReplaceYRegWithXReg(from, to + 1); + } + + to++; + } + + return false; +} + +int NativeCodeBasicBlock::FindFreeAccu(int at) const +{ + bool used[4] = { false, false, false, false }; + for (int i = 0; i < 4; i++) + if (mEntryRequiredRegs[i + BC_REG_ACCU]) + used[i] = true; + + for (int i = 0; i < at; i++) + { + for (int j = 0; j < 4; j++) + if (mIns[i].ReferencesZeroPage(j + BC_REG_ACCU)) + used[j] = true; + } + + for(int j=0; j<4; j++) + if (!used[j]) + return j + BC_REG_ACCU; + + return -1; +} + +bool NativeCodeBasicBlock::IsSameZPValue(int reg1, int reg2, int depth) const +{ + if (depth < 10) + { + int i = mIns.Size() - 1; + while (i >= 0 && !mIns[i].ChangesZeroPage(reg1) && !mIns[i].ChangesZeroPage(reg2)) + i--; + + if (i < 0) + { + for (int j = 0; j < mEntryBlocks.Size(); j++) + if (!mEntryBlocks[j]->IsSameZPValue(reg1, reg2, depth + 1)) + return false; + return true; + } + else if (i > 0) + { + i--; + if (mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_STA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + (mIns[i + 0].mAddress == reg1 && mIns[i + 1].mAddress == reg2 || mIns[i + 0].mAddress == reg2 && mIns[i + 1].mAddress == reg1)) + { + return true; + } + else if (mIns[i + 0].mType == ASMIT_LDX && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_STX && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + (mIns[i + 0].mAddress == reg1 && mIns[i + 1].mAddress == reg2 || mIns[i + 0].mAddress == reg2 && mIns[i + 1].mAddress == reg1)) + { + return true; + } + else if (mIns[i + 0].mType == ASMIT_LDY && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_STY && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + (mIns[i + 0].mAddress == reg1 && mIns[i + 1].mAddress == reg2 || mIns[i + 0].mAddress == reg2 && mIns[i + 1].mAddress == reg1)) + { + return true; + } + } + } + + return false; +} + +int NativeCodeBasicBlock::RetrieveZPValue(int reg, int at, int depth) const +{ + while (at >= 0 && !mIns[at].ChangesZeroPage(reg)) + at--; + + if (at >= 0) + { + if (mIns[at].mType == ASMIT_STA) + return RetrieveAValue(at); + else if (mIns[at].mType == ASMIT_STX) + return RetrieveXValue(at); + else if (mIns[at].mType == ASMIT_STY) + return RetrieveYValue(at); + else + return -1; + } + else if (mEntryBlocks.Size() == 1 && depth < 20) + return mEntryBlocks[0]->RetrieveZPValue(reg, mEntryBlocks[0]->mIns.Size() - 1, depth + 1); + else + return -1; +} + +int NativeCodeBasicBlock::RetrieveYValue(int at, int depth) const +{ + if (at >= mIns.Size()) + at = mIns.Size() - 1; + + int offset = 0; + while (at >= 0) + { + if (mIns[at].mType == ASMIT_INY) + offset++; + else if (mIns[at].mType == ASMIT_DEY) + offset--; + else if (mIns[at].ChangesYReg()) + break; + at--; + } + + if (at >= 0) + { + if (mIns[at].mType == ASMIT_LDY && mIns[at].mMode == ASMIM_IMMEDIATE) + return (mIns[at].mAddress + offset) & 0xff; + else + return -1; + } + else if (mEntryBlocks.Size() >= 1 && depth < 20) + { + int yval = mEntryBlocks[0]->RetrieveYValue(mEntryBlocks[0]->mIns.Size() - 1, depth + 1); + if (yval >= 0) + { + for (int i = 1; i < mEntryBlocks.Size(); i++) + { + if (yval != mEntryBlocks[i]->RetrieveYValue(mEntryBlocks[i]->mIns.Size() - 1, depth + 5)) + return -1; + } + return (yval + offset) & 0xff; + } + else + return -1; + } + else + return -1; +} + +int NativeCodeBasicBlock::RetrieveXValue(int at, int depth) const +{ + if (at >= mIns.Size()) + at = mIns.Size() - 1; + + int offset = 0; + while (at >= 0) + { + if (mIns[at].mType == ASMIT_INX) + offset++; + else if (mIns[at].mType == ASMIT_DEX) + offset--; + else if (mIns[at].ChangesXReg()) + break; + at--; + } + + if (at >= 0) + { + if (mIns[at].mType == ASMIT_LDX && mIns[at].mMode == ASMIM_IMMEDIATE) + return (mIns[at].mAddress + offset) & 0xff; + else + return -1; + } + else if (mEntryBlocks.Size() >= 1 && depth < 20) + { + int xval = mEntryBlocks[0]->RetrieveXValue(mEntryBlocks[0]->mIns.Size() - 1, depth + 1); + if (xval >= 0) + { + for (int i = 1; i < mEntryBlocks.Size(); i++) + { + if (xval != mEntryBlocks[i]->RetrieveXValue(mEntryBlocks[i]->mIns.Size() - 1, depth + 5)) + return -1; + } + return (xval + offset) & 0xff; + } + else + return -1; + } + else + return -1; +} + +int NativeCodeBasicBlock::RetrieveCValue(int at, int depth) const +{ + while (at >= 0 && !mIns[at].ChangesCarry()) + at--; + if (at >= 0) + { + if (mIns[at].mType == ASMIT_CLC) + return 0; + else if (mIns[at].mType == ASMIT_SEC) + return 1; + else + return -1; + } + else if (mEntryBlocks.Size() == 1 && depth < 20) + return mEntryBlocks[0]->RetrieveCValue(mEntryBlocks[0]->mIns.Size() - 1, depth + 1); + else if (mEntryBlocks.Size() == 2 && depth < 20) + { + int e0 = -1, e1 = -1; + if (mEntryBlocks[0]->mBranch == ASMIT_BCC) + { + if (this == mEntryBlocks[0]->mTrueJump) + e0 = 0; + else + e0 = 1; + } + else if (mEntryBlocks[0]->mBranch == ASMIT_BCS) + { + if (this == mEntryBlocks[0]->mTrueJump) + e0 = 1; + else + e0 = 0; + } + else + e0 = mEntryBlocks[0]->RetrieveCValue(mEntryBlocks[0]->mIns.Size() - 1, depth + 1); + + if (mEntryBlocks[1]->mBranch == ASMIT_BCC) + { + if (this == mEntryBlocks[1]->mTrueJump) + e1 = 0; + else + e1 = 1; + } + else if (mEntryBlocks[1]->mBranch == ASMIT_BCS) + { + if (this == mEntryBlocks[1]->mTrueJump) + e1 = 1; + else + e1 = 0; + } + else + e1 = mEntryBlocks[1]->RetrieveCValue(mEntryBlocks[1]->mIns.Size() - 1, depth + 1); + + if (e0 >= 0 && e1 >= 0 && e0 == e1) + return e0; + else + return -1; + } + else + return -1; +} + +int NativeCodeBasicBlock::RetrieveAValue(int at, int depth) const +{ + while (at >= 0 && !mIns[at].ChangesAccu()) + at--; + if (at >= 0) + { + if (mIns[at].mType == ASMIT_LDA && mIns[at].mMode == ASMIM_IMMEDIATE) + return mIns[at].mAddress; + else + return -1; + } + else if (mEntryBlocks.Size() == 1 && depth < 20) + { + if (mEntryBlocks[0]->mBranch == ASMIT_BEQ && this == mEntryBlocks[0]->mTrueJump || + mEntryBlocks[0]->mBranch == ASMIT_BNE && this == mEntryBlocks[0]->mFalseJump) + { + if (mEntryBlocks[0]->mIns.Size() > 0 && mEntryBlocks[0]->mIns.Last().ChangesAccuAndFlag()) + return 0; + } + return mEntryBlocks[0]->RetrieveAValue(mEntryBlocks[0]->mIns.Size() - 1, depth + 1); + } + else + return -1; +} + +void NativeCodeBasicBlock::RepairLoadYImmediate(int at, int val) +{ + if (mIns[at].mLive & LIVE_CPU_REG_Y) + { + const InterInstruction* iins = mIns[at].mIns; + + if (mIns[at].mLive & LIVE_CPU_REG_Z) + { + int i = at; + while (i + 1 < mIns.Size() && !mIns[i + 1].ReferencesYReg()) + { + if (!(mIns[i].mLive & LIVE_CPU_REG_Z)) + { + mIns.Insert(i + 1, NativeCodeInstruction(iins, ASMIT_LDY, ASMIM_IMMEDIATE, val)); + return; + } + i++; + } + + mIns[at].mLive |= LIVE_CPU_REG_A; + + mIns.Insert(at + 1, NativeCodeInstruction(iins, ASMIT_ORA, ASMIM_IMMEDIATE, 0)); + mIns.Insert(at + 1, NativeCodeInstruction(iins, ASMIT_LDY, ASMIM_IMMEDIATE, val)); + } + else + mIns.Insert(at + 1, NativeCodeInstruction(iins, ASMIT_LDY, ASMIM_IMMEDIATE, val)); + } +} + +bool NativeCodeBasicBlock::PatchGlobalAdressSumYByX(int at, int reg, const NativeCodeInstruction& ains, int addr) +{ + int yindex = 0; + int last = at; + + while (last < mIns.Size()) + { + if (mIns[last].mType == ASMIT_LDY && mIns[last].mMode == ASMIM_IMMEDIATE) + yindex = mIns[last].mAddress; + else if (mIns[last].ChangesYReg()) + return false; + else if (mIns[last].mMode == ASMIM_ZERO_PAGE && (mIns[last].mAddress == reg || mIns[last].mAddress == reg + 1) && mIns[last].ChangesAddress()) + return false; + else if (mIns[last].mMode == ASMIM_INDIRECT_Y && mIns[last].mAddress == reg) + { + if (!(mIns[last].mLive & LIVE_MEM)) + break; + } + else if (mIns[last].RequiresXReg()) + return false; + last++; + } + + if (last == mIns.Size()) + return false; + + yindex = 0; + for (int i = at; i <= last; i++) + { + mIns[i].mLive |= LIVE_CPU_REG_X; + + if (mIns[i].mType == ASMIT_LDY && mIns[i].mMode == ASMIM_IMMEDIATE) + yindex = mIns[i].mAddress; + else if (mIns[i].mMode == ASMIM_INDIRECT_Y && mIns[i].mAddress == reg) + { + mIns[i].mMode = ASMIM_ABSOLUTE_X; + if (ains.mMode == ASMIM_IMMEDIATE) + { + mIns[i].mLinkerObject = nullptr; + mIns[i].mAddress = addr + yindex; + } + else + { + mIns[i].mLinkerObject = ains.mLinkerObject; + mIns[i].mAddress = ains.mAddress + yindex; + } + } + } + + return true; +} + + +bool NativeCodeBasicBlock::PatchDirectAddressSumY(int at, int reg, int apos, int breg) +{ + int yindex = 0; + int last = at; + + while (last < mIns.Size()) + { + if (mIns[last].mType == ASMIT_LDY && mIns[last].mMode == ASMIM_IMMEDIATE && (mIns[last].mAddress == yindex || mIns[last].mAddress == yindex + 1)) + yindex = mIns[last].mAddress; + else if (mIns[last].ChangesYReg()) + return false; + else if (mIns[last].mMode == ASMIM_ZERO_PAGE && (mIns[last].mAddress == breg || mIns[last].mAddress == breg + 1 || mIns[last].mAddress == reg || mIns[last].mAddress == reg + 1) && mIns[last].ChangesAddress()) + return false; + else if (mIns[last].mMode == ASMIM_INDIRECT_Y && mIns[last].mAddress == reg) + { + if (!(mIns[last].mLive & LIVE_MEM)) + break; + } + else if (mIns[last].RequiresYReg()) + return false; + last++; + } + + if (last == mIns.Size()) + return false; + + RepairLoadYImmediate(last, yindex); + + mIns[apos].mType = ASMIT_TAY; + for (int i = 0; i < 5; i++) + { + mIns[apos + i + 1].mType = ASMIT_NOP; + mIns[apos + i + 1].mMode = ASMIM_IMPLIED; + } + yindex = 0; + + for (int i = apos; i <= last; i++) + { + mIns[i].mLive |= LIVE_CPU_REG_Y; + + if (mIns[i].mType == ASMIT_LDY && mIns[i].mMode == ASMIM_IMMEDIATE) + { + if (mIns[i].mAddress == yindex) + { + mIns[i].mType = ASMIT_NOP; + mIns[i].mMode = ASMIM_IMPLIED; + } + else + { + mIns[i].mType = ASMIT_INY; + mIns[i].mMode = ASMIM_IMPLIED; + yindex++; + } + } + else if (mIns[i].mMode == ASMIM_INDIRECT_Y && mIns[i].mAddress == reg) + { + mIns[i].mAddress = breg; + } + } + + return true; +} + +bool NativeCodeBasicBlock::PatchAddressSumY(int at, int reg, int apos, int breg, int ireg) +{ + int yindex = 0; + int last = apos + 7; + + while (last < mIns.Size()) + { + if (mIns[last].mType == ASMIT_LDY && mIns[last].mMode == ASMIM_IMMEDIATE && (mIns[last].mAddress == yindex || mIns[last].mAddress == yindex + 1 || mIns[last].mAddress + 1 == yindex)) + yindex = mIns[last].mAddress; + else if (mIns[last].ChangesYReg()) + return false; + else if (mIns[last].mMode == ASMIM_ZERO_PAGE && (mIns[last].mAddress == breg || mIns[last].mAddress == breg + 1 || mIns[last].mAddress == reg || mIns[last].mAddress == reg + 1) && mIns[last].ChangesAddress()) + return false; + else if (mIns[last].mMode == ASMIM_INDIRECT_Y && mIns[last].mAddress == reg) + { + if (!(mIns[last].mLive & LIVE_MEM)) + break; + } + else if (mIns[last].RequiresYReg()) + return false; + last++; + } + + if (last == mIns.Size()) + return false; + + RepairLoadYImmediate(last, yindex); + + for (int i = 0; i < 5; i++) + { + mIns[apos + i + 2].mType = ASMIT_NOP; + mIns[apos + i + 2].mMode = ASMIM_IMPLIED; + } + mIns[apos + 1].mType = ASMIT_LDY; + mIns[apos + 1].mMode = ASMIM_ZERO_PAGE; + mIns[apos + 1].mAddress = ireg; + mIns[apos + 1].mLive |= LIVE_MEM | LIVE_CPU_REG_Y; + + yindex = 0; + + for (int i = apos + 7; i <= last; i++) + { + mIns[i].mLive |= LIVE_CPU_REG_Y; + + if (mIns[i].mType == ASMIT_LDY && mIns[i].mMode == ASMIM_IMMEDIATE) + { + if (mIns[i].mAddress == yindex) + { + mIns[i].mType = ASMIT_NOP; + mIns[i].mMode = ASMIM_IMPLIED; + } + else if (mIns[i].mAddress == yindex + 1) + { + mIns[i].mType = ASMIT_INY; + mIns[i].mMode = ASMIM_IMPLIED; + yindex++; + } + else + { + mIns[i].mType = ASMIT_DEY; + mIns[i].mMode = ASMIM_IMPLIED; + yindex--; + } + } + else if (mIns[i].mMode == ASMIM_INDIRECT_Y && mIns[i].mAddress == reg) + { + mIns[i].mFlags &= ~NCIF_YZERO; + mIns[i].mAddress = breg; + } + } + + return true; +} + +bool NativeCodeBasicBlock::FindAbsoluteAddressSumY(int at, int reg, int& apos, int& offset) +{ + int j = at - 6; + while (j >= 0) + { + if ( + mIns[j + 0].mType == ASMIT_CLC && + mIns[j + 1].mType == ASMIT_LDA && mIns[j + 1].mMode == ASMIM_IMMEDIATE && + mIns[j + 2].mType == ASMIT_ADC && mIns[j + 2].mMode == ASMIM_ABSOLUTE && + mIns[j + 3].mType == ASMIT_STA && mIns[j + 3].mMode == ASMIM_ZERO_PAGE && mIns[j + 3].mAddress == reg && + mIns[j + 4].mType == ASMIT_LDA && mIns[j + 4].mMode == ASMIM_IMMEDIATE && mIns[j + 4].mAddress == 0 && + mIns[j + 5].mType == ASMIT_ADC && mIns[j + 5].mMode == ASMIM_ABSOLUTE && mIns[j + 5].mAddress == mIns[j + 2].mAddress + 1 && mIns[j + 5].mLinkerObject == mIns[j + 2].mLinkerObject && + mIns[j + 6].mType == ASMIT_STA && mIns[j + 6].mMode == ASMIM_ZERO_PAGE && mIns[j + 6].mAddress == reg + 1) + { + int k = j + 6; + while (k < at) + { + if (mIns[j + 2].MayBeChangedOnAddress(mIns[k]) || mIns[j + 5].MayBeChangedOnAddress(mIns[k])) + return false; + + k++; + } + + offset = mIns[j + 1].mAddress; + apos = j; + + return true; + + } + + if (mIns[j + 6].ReferencesZeroPage(reg) || mIns[j + 6].ReferencesZeroPage(reg + 1)) + return false; + + j--; + } + + return false; +} + +bool NativeCodeBasicBlock::PatchAbsoluteAddressSumY(int at, int reg, int apos) +{ + mIns[apos + 0].mType = ASMIT_NOP; mIns[apos + 0].mMode = ASMIM_IMPLIED; + mIns[apos + 1].mType = ASMIT_NOP; mIns[apos + 1].mMode = ASMIM_IMPLIED; + mIns[apos + 2].mType = ASMIT_LDA; + mIns[apos + 4].mType = ASMIT_NOP; mIns[apos + 4].mMode = ASMIM_IMPLIED; + mIns[apos + 5].mType = ASMIT_LDA; + + return true; +} + +bool NativeCodeBasicBlock::FindBypassAddressSumY(int at, int reg, int& apos, int& breg) +{ + int j = at - 6; + while (j >= 0) + { + if ( + mIns[j + 0].mType == ASMIT_CLC && + mIns[j + 1].mType == ASMIT_ADC && mIns[j + 1].mMode == ASMIM_ZERO_PAGE && + mIns[j + 2].mType == ASMIT_STA && mIns[j + 2].mMode == ASMIM_ZERO_PAGE && mIns[j + 2].mAddress == reg && + mIns[j + 3].mType == ASMIT_LDA && mIns[j + 3].mMode == ASMIM_ZERO_PAGE && mIns[j + 3].mAddress == mIns[j + 1].mAddress + 1 && + mIns[j + 4].mType == ASMIT_ADC && mIns[j + 4].mMode == ASMIM_IMMEDIATE && mIns[j + 4].mAddress == 0 && + mIns[j + 5].mType == ASMIT_STA && mIns[j + 5].mMode == ASMIM_ZERO_PAGE && mIns[j + 5].mAddress == reg + 1) + { + breg = mIns[j + 1].mAddress; + int k = j + 6; + while (k < at) + { + if (mIns[k].ChangesZeroPage(breg) || mIns[k].ChangesZeroPage(breg + 1)) + return false; + + k++; + } + + apos = j; + + return true; + } + + if (mIns[j + 5].ReferencesZeroPage(reg) || mIns[j + 5].ReferencesZeroPage(reg + 1)) + return false; + + j--; + } + + return false; +} + +bool NativeCodeBasicBlock::PatchBypassAddressSumY(int at, int reg, int apos, int breg) +{ + mIns[apos + 0].mType = ASMIT_NOP; mIns[apos + 0].mMode = ASMIM_IMPLIED; + mIns[apos + 1].mType = ASMIT_NOP; mIns[apos + 1].mMode = ASMIM_IMPLIED; + mIns[apos + 3].mType = ASMIT_NOP; mIns[apos + 3].mMode = ASMIM_IMPLIED; + mIns[apos + 4].mType = ASMIT_NOP; mIns[apos + 4].mMode = ASMIM_IMPLIED; + mIns[apos + 5].mType = ASMIT_NOP; mIns[apos + 5].mMode = ASMIM_IMPLIED; + + mIns[at + 0].mMode = ASMIM_ZERO_PAGE; + mIns[at + 0].mAddress = reg; + mIns[at + 1].mAddress = breg; + mIns[at + 1].mLive |= LIVE_MEM; + + return true; +} + +bool NativeCodeBasicBlock::FindDirectAddressSumY(int at, int reg, int& apos, int& breg) +{ + int j = at - 6; + while (j >= 0) + { + if ( + mIns[j + 0].mType == ASMIT_CLC && + mIns[j + 1].mType == ASMIT_ADC && mIns[j + 1].mMode == ASMIM_ZERO_PAGE && + mIns[j + 2].mType == ASMIT_STA && mIns[j + 2].mMode == ASMIM_ZERO_PAGE && mIns[j + 2].mAddress == reg && + mIns[j + 3].mType == ASMIT_LDA && mIns[j + 3].mMode == ASMIM_ZERO_PAGE && mIns[j + 3].mAddress == mIns[j + 1].mAddress + 1 && + mIns[j + 4].mType == ASMIT_ADC && mIns[j + 4].mMode == ASMIM_IMMEDIATE && mIns[j + 4].mAddress == 0 && + mIns[j + 5].mType == ASMIT_STA && mIns[j + 5].mMode == ASMIM_ZERO_PAGE && mIns[j + 5].mAddress == reg + 1) + { + breg = mIns[j + 1].mAddress; + int k = j + 6; + while (k < at) + { + if (mIns[k].mMode == ASMIM_ZERO_PAGE && (mIns[k].mAddress == breg || mIns[k].mAddress == breg + 1) && mIns[k].ChangesAddress()) + return false; + + if (mIns[k].mMode == ASMIM_ZERO_PAGE && (mIns[k].mAddress == breg || mIns[k].mAddress == breg + 1)) + return false; + if (mIns[k].mMode == ASMIM_INDIRECT_Y && mIns[k].mAddress == breg) + return false; + + k++; + } + + apos = j; + + return true; + } + + if (mIns[j + 5].mMode == ASMIM_ZERO_PAGE && (mIns[j + 5].mAddress == reg || mIns[j + 5].mAddress == reg + 1) && mIns[j + 5].ChangesAddress() || mIns[j + 5].ChangesYReg() || mIns[j + 5].RequiresYReg()) + return false; + + j--; + } + + return false; +} + +bool NativeCodeBasicBlock::FindExternAddressSumY(int at, int reg, int& breg, int& ireg) +{ + int j = at - 7; + while (j >= 0) + { + if ( + mIns[j + 0].mType == ASMIT_CLC && + mIns[j + 1].mType == ASMIT_LDA && mIns[j + 1].mMode == ASMIM_ZERO_PAGE && + mIns[j + 2].mType == ASMIT_ADC && mIns[j + 2].mMode == ASMIM_ZERO_PAGE && + mIns[j + 3].mType == ASMIT_STA && mIns[j + 3].mMode == ASMIM_ZERO_PAGE && mIns[j + 3].mAddress == reg && + mIns[j + 4].mType == ASMIT_LDA && mIns[j + 4].mMode == ASMIM_ZERO_PAGE && mIns[j + 4].mAddress == mIns[j + 1].mAddress + 1 && + mIns[j + 5].mType == ASMIT_ADC && mIns[j + 5].mMode == ASMIM_IMMEDIATE && mIns[j + 5].mAddress == 0 && + mIns[j + 6].mType == ASMIT_STA && mIns[j + 6].mMode == ASMIM_ZERO_PAGE && mIns[j + 6].mAddress == reg + 1) + { + breg = mIns[j + 1].mAddress; + ireg = mIns[j + 2].mAddress; + + if (ireg == breg || reg == breg || ireg == reg) + return false; + + int k = j + 7; + while (k < at) + { + if (mIns[k].mMode == ASMIM_ZERO_PAGE && (mIns[k].mAddress == breg || mIns[k].mAddress == breg + 1 || mIns[k].mAddress == ireg) && mIns[k].ChangesAddress()) + return false; + k++; + } + + return true; + } + else if ( + mIns[j + 0].mType == ASMIT_CLC && + mIns[j + 1].mType == ASMIT_LDA && mIns[j + 1].mMode == ASMIM_ZERO_PAGE && + mIns[j + 2].mType == ASMIT_ADC && mIns[j + 2].mMode == ASMIM_ZERO_PAGE && + mIns[j + 3].mType == ASMIT_STA && mIns[j + 3].mMode == ASMIM_ZERO_PAGE && mIns[j + 3].mAddress == reg && + mIns[j + 4].mType == ASMIT_LDA && mIns[j + 4].mMode == ASMIM_ZERO_PAGE && mIns[j + 4].mAddress == mIns[j + 2].mAddress + 1 && + mIns[j + 5].mType == ASMIT_ADC && mIns[j + 5].mMode == ASMIM_IMMEDIATE && mIns[j + 5].mAddress == 0 && + mIns[j + 6].mType == ASMIT_STA && mIns[j + 6].mMode == ASMIM_ZERO_PAGE && mIns[j + 6].mAddress == reg + 1) + { + breg = mIns[j + 2].mAddress; + ireg = mIns[j + 1].mAddress; + + if (ireg == breg || reg == breg || ireg == reg) + return false; + + int k = j + 7; + while (k < at) + { + if (mIns[k].mMode == ASMIM_ZERO_PAGE && (mIns[k].mAddress == breg || mIns[k].mAddress == breg + 1 || mIns[k].mAddress == ireg) && mIns[k].ChangesAddress()) + return false; + k++; + } + + return true; + } + else if ( + mIns[j + 0].mType == ASMIT_STA && mIns[j + 0].mMode == ASMIM_ZERO_PAGE && + mIns[j + 1].mType == ASMIT_CLC && + mIns[j + 2].mType == ASMIT_ADC && mIns[j + 2].mMode == ASMIM_ZERO_PAGE && + mIns[j + 3].mType == ASMIT_STA && mIns[j + 3].mMode == ASMIM_ZERO_PAGE && mIns[j + 3].mAddress == reg && + mIns[j + 4].mType == ASMIT_LDA && mIns[j + 4].mMode == ASMIM_ZERO_PAGE && mIns[j + 4].mAddress == mIns[j + 2].mAddress + 1 && + mIns[j + 5].mType == ASMIT_ADC && mIns[j + 5].mMode == ASMIM_IMMEDIATE && mIns[j + 5].mAddress == 0 && + mIns[j + 6].mType == ASMIT_STA && mIns[j + 6].mMode == ASMIM_ZERO_PAGE && mIns[j + 6].mAddress == reg + 1) + { + breg = mIns[j + 2].mAddress; + ireg = mIns[j + 0].mAddress; + + if (ireg == breg || reg == breg || ireg == reg) + return false; + + int k = j + 7; + while (k < at) + { + if (mIns[k].mMode == ASMIM_ZERO_PAGE && (mIns[k].mAddress == breg || mIns[k].mAddress == breg + 1 || mIns[k].mAddress == ireg) && mIns[k].ChangesAddress()) + return false; + k++; + } + + return true; + } + + if (mIns[j + 6].mMode == ASMIM_ZERO_PAGE && (mIns[j + 6].mAddress == reg || mIns[j + 6].mAddress == reg + 1) && mIns[j + 6].ChangesAddress()) + return false; + + j--; + } + + if (mFromJump) + { + while (j >= -6) + { + if (mIns[j + 6].mMode == ASMIM_ZERO_PAGE && (mIns[j + 6].mAddress == reg || mIns[j + 6].mAddress == reg + 1) && mIns[j + 6].ChangesAddress()) + return false; + j--; + } + + if (mFromJump->FindExternAddressSumY(mFromJump->mIns.Size(), reg, breg, ireg)) + { + int k = 0; + while (k < at) + { + if (mIns[k].mMode == ASMIM_ZERO_PAGE && (mIns[k].mAddress == breg || mIns[k].mAddress == breg + 1 || mIns[k].mAddress == ireg) && mIns[k].ChangesAddress()) + return false; + k++; + } + + return true; + } + } + + + return false; + +} + +bool NativeCodeBasicBlock::FindLoadAddressSumY(int at, int reg, int& apos, int& ireg) +{ + int j = at - 7; + while (j >= 0) + { + if (mIns[j + 0].mType == ASMIT_LDA && mIns[j + 0].mMode != ASMIM_ZERO_PAGE && + mIns[j + 1].mType == ASMIT_CLC && + mIns[j + 2].mType == ASMIT_ADC && mIns[j + 2].mMode == ASMIM_ZERO_PAGE && + mIns[j + 3].mType == ASMIT_STA && mIns[j + 3].mMode == ASMIM_ZERO_PAGE && mIns[j + 3].mAddress == reg && + mIns[j + 4].mType == ASMIT_LDA && mIns[j + 4].mMode == mIns[j + 0].mMode && + mIns[j + 5].mType == ASMIT_ADC && mIns[j + 5].mMode == ASMIM_IMMEDIATE && mIns[j + 5].mAddress == 0 && + mIns[j + 6].mType == ASMIT_STA && mIns[j + 6].mMode == ASMIM_ZERO_PAGE && mIns[j + 6].mAddress == reg + 1) + { + ireg = mIns[j + 2].mAddress; + + if (ireg == mIns[j + 3].mAddress || ireg == mIns[j + 6].mAddress) + return false; + + int k = j + 7; + while (k < at) + { + if (mIns[k].mMode == ASMIM_ZERO_PAGE && mIns[k].mAddress == ireg && mIns[k].ChangesAddress()) + return false; + k++; + } + + apos = j; + return true; + } + + if (mIns[j + 6].mMode == ASMIM_ZERO_PAGE && (mIns[j + 6].mAddress == reg || mIns[j + 6].mAddress == reg + 1)) + return false; + if (mIns[j + 6].mMode == ASMIM_INDIRECT_Y && (mIns[j + 6].mAddress == reg || mIns[j + 6].mAddress + 1 == reg || mIns[j + 6].mAddress == reg + 1)) + return false; + if (mIns[j + 6].mType == ASMIT_JSR) + return false; + + j--; + } + return false; +} + +bool NativeCodeBasicBlock::PatchLoadAddressSumY(int at, int reg, int apos, int ireg) +{ + mIns[apos + 1].mType = ASMIT_NOP; mIns[apos + 1].mMode = ASMIM_IMPLIED; + mIns[apos + 2].mType = ASMIT_NOP; mIns[apos + 2].mMode = ASMIM_IMPLIED; + mIns[apos + 5].mType = ASMIT_NOP; mIns[apos + 5].mMode = ASMIM_IMPLIED; + return true; +} + +bool NativeCodeBasicBlock::FindAddressSumY(int at, int reg, int & apos, int& breg, int& ireg) +{ + int j = at - 7; + while (j >= 0) + { + if ( +#if 1 + mIns[j + 0].mType == ASMIT_CLC && + mIns[j + 1].mType == ASMIT_LDA && mIns[j + 1].mMode == ASMIM_ZERO_PAGE && + mIns[j + 2].mType == ASMIT_ADC && mIns[j + 2].mMode == ASMIM_ZERO_PAGE && + mIns[j + 3].mType == ASMIT_STA && mIns[j + 3].mMode == ASMIM_ZERO_PAGE && mIns[j + 3].mAddress == reg && + mIns[j + 4].mType == ASMIT_LDA && mIns[j + 4].mMode == ASMIM_ZERO_PAGE && mIns[j + 4].mAddress == mIns[j + 1].mAddress + 1 && + mIns[j + 5].mType == ASMIT_ADC && mIns[j + 5].mMode == ASMIM_IMMEDIATE && mIns[j + 5].mAddress == 0 && + mIns[j + 6].mType == ASMIT_STA && mIns[j + 6].mMode == ASMIM_ZERO_PAGE && mIns[j + 6].mAddress == reg + 1) + { + breg = mIns[j + 1].mAddress; + ireg = mIns[j + 2].mAddress; + + int k = j + 7; + while (k < at) + { + if (mIns[k].mMode == ASMIM_ZERO_PAGE && (mIns[k].mAddress == breg || mIns[k].mAddress == breg + 1 || mIns[k].mAddress == ireg) && mIns[k].ChangesAddress()) + return false; + if (breg == reg || ireg == breg) + { + if (mIns[k].mMode == ASMIM_ZERO_PAGE && (mIns[k].mAddress == breg || mIns[k].mAddress == breg + 1)) + return false; + if (mIns[k].mMode == ASMIM_INDIRECT_Y && mIns[k].mAddress == breg) + return false; + } + else if (ireg == reg) + { + if (mIns[k].mMode == ASMIM_ZERO_PAGE && mIns[k].mAddress == ireg) + return false; + if (mIns[k].mMode == ASMIM_INDIRECT_Y && mIns[k].mAddress == ireg) + return false; + } + k++; + } + + apos = j; + + return true; + } + else if ( +#endif + mIns[j + 0].mType == ASMIT_CLC && + mIns[j + 1].mType == ASMIT_LDA && mIns[j + 1].mMode == ASMIM_ZERO_PAGE && + mIns[j + 2].mType == ASMIT_ADC && mIns[j + 2].mMode == ASMIM_ZERO_PAGE && + mIns[j + 3].mType == ASMIT_STA && mIns[j + 3].mMode == ASMIM_ZERO_PAGE && mIns[j + 3].mAddress == reg && + mIns[j + 4].mType == ASMIT_LDA && mIns[j + 4].mMode == ASMIM_ZERO_PAGE && mIns[j + 4].mAddress == mIns[j + 2].mAddress + 1 && + mIns[j + 5].mType == ASMIT_ADC && mIns[j + 5].mMode == ASMIM_IMMEDIATE && mIns[j + 5].mAddress == 0 && + mIns[j + 6].mType == ASMIT_STA && mIns[j + 6].mMode == ASMIM_ZERO_PAGE && mIns[j + 6].mAddress == reg + 1) + { + breg = mIns[j + 2].mAddress; + ireg = mIns[j + 1].mAddress; + + int k = j + 7; + while (k < at) + { + if (mIns[k].mMode == ASMIM_ZERO_PAGE && (mIns[k].mAddress == breg || mIns[k].mAddress == breg + 1 || mIns[k].mAddress == ireg) && mIns[k].ChangesAddress()) + return false; + if (breg == reg || ireg == breg) + { + if (mIns[k].mMode == ASMIM_ZERO_PAGE && (mIns[k].mAddress == breg || mIns[k].mAddress == breg + 1)) + return false; + if (mIns[k].mMode == ASMIM_INDIRECT_Y && mIns[k].mAddress == breg) + return false; + } + else if (ireg == reg) + { + if (mIns[k].mMode == ASMIM_ZERO_PAGE && mIns[k].mAddress == ireg) + return false; + if (mIns[k].mMode == ASMIM_INDIRECT_Y && mIns[k].mAddress == ireg) + return false; + } + k++; + } + + apos = j; + + return true; + } + else if ( + mIns[j + 0].mType == ASMIT_STA && mIns[j + 0].mMode == ASMIM_ZERO_PAGE && + mIns[j + 1].mType == ASMIT_CLC && + mIns[j + 2].mType == ASMIT_ADC && mIns[j + 2].mMode == ASMIM_ZERO_PAGE && + mIns[j + 3].mType == ASMIT_STA && mIns[j + 3].mMode == ASMIM_ZERO_PAGE && mIns[j + 3].mAddress == reg && + mIns[j + 4].mType == ASMIT_LDA && mIns[j + 4].mMode == ASMIM_ZERO_PAGE && mIns[j + 4].mAddress == mIns[j + 2].mAddress + 1 && + mIns[j + 5].mType == ASMIT_ADC && mIns[j + 5].mMode == ASMIM_IMMEDIATE && mIns[j + 5].mAddress == 0 && + mIns[j + 6].mType == ASMIT_STA && mIns[j + 6].mMode == ASMIM_ZERO_PAGE && mIns[j + 6].mAddress == reg + 1) + { + breg = mIns[j + 2].mAddress; + ireg = mIns[j + 0].mAddress; + + int k = j + 7; + while (k < at) + { + if (mIns[k].mMode == ASMIM_ZERO_PAGE && (mIns[k].mAddress == breg || mIns[k].mAddress == breg + 1 || mIns[k].mAddress == ireg) && mIns[k].ChangesAddress()) + return false; + if (breg == reg || ireg == breg) + { + if (mIns[k].mMode == ASMIM_ZERO_PAGE && (mIns[k].mAddress == breg || mIns[k].mAddress == breg + 1)) + return false; + if (mIns[k].mMode == ASMIM_INDIRECT_Y && mIns[k].mAddress == breg) + return false; + } + k++; + } + + apos = j; + + return true; + } + + if (mIns[j + 6].mMode == ASMIM_ZERO_PAGE && (mIns[j + 6].mAddress == reg || mIns[j + 6].mAddress == reg + 1) && mIns[j + 6].ChangesAddress()) + return false; + + j--; + } + + return false; +} + +bool NativeCodeBasicBlock::MoveIndirectLoadStoreDown(int at) +{ + int j = at + 2; + + while (j < mIns.Size()) + { + if (mIns[j].mMode == ASMIM_ZERO_PAGE && mIns[j].mAddress == mIns[at + 1].mAddress) + { + if (!(mIns[j].mLive & LIVE_MEM) && HasAsmInstructionMode(mIns[j].mType, ASMIM_INDIRECT_Y)) + { + mIns[j].mMode = ASMIM_INDIRECT_Y; + mIns[j].mAddress = mIns[at].mAddress; + mIns[j].mLive |= LIVE_MEM; + mIns[at + 0].mType = ASMIT_NOP; mIns[at + 0].mMode = ASMIM_IMPLIED; + mIns[at + 1].mType = ASMIT_NOP; mIns[at + 1].mMode = ASMIM_IMPLIED; + + for (int k = at; k < j; k++) + mIns[k].mLive |= LIVE_CPU_REG_Y; + + return true; + } + + return false; + } + + if (mIns[j].ChangesYReg()) + return false; + if (mIns[j].ChangesZeroPage(mIns[at].mAddress) || mIns[j].ChangesZeroPage(mIns[at].mAddress + 1)) + return false; + if (mIns[j].ChangesGlobalMemory()) + return false; + + j++; + } + + return false; +} + +bool NativeCodeBasicBlock::MoveLDXBeforeZ(int at) +{ + int i = at; + while (i > 0) + { + i--; + NativeCodeInstruction& ins(mIns[i]); + if (ins.ReferencesXReg()) + return false; + + if (ins.ChangesZFlag()) + { + if (ins.ChangesAccuAndFlag()) + { + mIns[at].mLive |= mIns[i].mLive; + if (mIns[i].ReferencesCarry()) mIns[at].mLive |= LIVE_CPU_REG_C; + + mIns.Insert(i, mIns[at]); + mIns.Remove(at + 1); + mIns.Remove(at + 1); + for (int j = i; j < at + 1; j++) + mIns[j].mLive |= LIVE_CPU_REG_X | LIVE_CPU_REG_Z; + return true; + } + else + return false; + } + + if (ins.ChangesXReg() || ins.ChangesAccu()) + return false; + } + + return false; +} + +bool NativeCodeBasicBlock::MoveLDXUp(int at) +{ + NativeCodeInstruction& lins(mIns[at]); + + int i = at; + while (i > 0) + { + i--; + NativeCodeInstruction& ins(mIns[i]); + if (ins.mType == ASMIT_STA && ins.SameEffectiveAddress(lins)) + { + if (ins.mLive & LIVE_CPU_REG_Z) + return false; + ins.mLive |= LIVE_CPU_REG_A; + + mIns.Insert(i + 1, NativeCodeInstruction(lins.mIns, ASMIT_TAX)); + mIns.Remove(at + 1); + + while (i <= at) + { + mIns[i].mLive |= LIVE_CPU_REG_X; + i++; + } + return true; + } + if (ins.ReferencesXReg() || lins.MayBeChangedOnAddress(ins)) + return false; + } + + return false; +} + +bool NativeCodeBasicBlock::MoveLDYUp(int at) +{ + NativeCodeInstruction& lins(mIns[at]); + + int i = at; + bool referenced = false; + while (i > 0) + { + i--; + NativeCodeInstruction& ins(mIns[i]); + if (ins.mType == ASMIT_STA && ins.SameEffectiveAddress(lins)) + { + if (ins.mLive & LIVE_CPU_REG_Z) + return false; + ins.mLive |= LIVE_CPU_REG_A; + + mIns.Insert(i + 1, NativeCodeInstruction(lins.mIns, ASMIT_TAY)); + mIns.Remove(at + 1); + + while (i <= at) + { + mIns[i].mLive |= LIVE_CPU_REG_Y; + i++; + } + return true; + } + else if (lins.mMode == ASMIM_ZERO_PAGE && ins.mType == ASMIT_STX && ins.SameEffectiveAddress(lins) && !referenced) + { + if (lins.mLive & LIVE_MEM) + return false; + if (ins.mLive & (LIVE_CPU_REG_Z | LIVE_CPU_REG_A)) + return false; + + mIns[i].mType = ASMIT_TXA; mIns[i].mMode = ASMIM_IMPLIED; mIns[i].mLive |= LIVE_CPU_REG_A; + mIns.Insert(i + 1, NativeCodeInstruction(lins.mIns, ASMIT_TAY)); + mIns.Remove(at + 1); + + while (i <= at) + { + mIns[i].mLive |= LIVE_CPU_REG_Y; + i++; + } + return true; + } + if (ins.ReferencesYReg() || lins.MayBeChangedOnAddress(ins)) + return false; + if (lins.mMode == ASMIM_ZERO_PAGE && ins.ReferencesZeroPage(lins.mAddress)) + referenced = true; + + } + + return false; +} + +bool NativeCodeBasicBlock::MoveIndirectLoadStoreDownY(int at) +{ + int j = at + 3; + + int saddr = mIns[at + 1].mAddress, daddr = mIns[at + 2].mAddress; + + NativeCodeBasicBlock* block = this; + + while (block) + { + while (j < block->mIns.Size()) + { + if (block->mIns[j].mMode == ASMIM_ZERO_PAGE && block->mIns[j].mAddress == daddr) + { + if (!(block->mIns[j].mLive & (LIVE_MEM | LIVE_CPU_REG_Y)) && HasAsmInstructionMode(block->mIns[j].mType, ASMIM_INDIRECT_Y)) + { + block->mIns[j].mMode = ASMIM_INDIRECT_Y; + block->mIns[j].mAddress = saddr; + block->mIns[j].mLive |= LIVE_MEM; + mIns[at + 1].mType = ASMIT_NOP; mIns[at + 1].mMode = ASMIM_IMPLIED; + mIns[at + 2].mType = ASMIT_NOP; mIns[at + 2].mMode = ASMIM_IMPLIED; + block->mIns.Insert(j, NativeCodeInstruction(mIns[at + 0].mIns, ASMIT_LDY, ASMIM_IMMEDIATE, mIns[at + 0].mAddress)); + + while (block != this) + { + block->mEntryRequiredRegs += saddr; + block->mEntryRequiredRegs += saddr + 1; + block = block->mEntryBlocks[0]; + block->mExitRequiredRegs += saddr; + block->mExitRequiredRegs += saddr + 1; + } + + return true; + } + + return false; + } + + if (block->mIns[j].ReferencesZeroPage(daddr)) + return false; + if (block->mIns[j].ChangesZeroPage(saddr) || block->mIns[j].ChangesZeroPage(saddr + 1)) + return false; + if (block->mIns[j].ChangesGlobalMemory()) + return false; + + j++; + } + + if (block->mFalseJump) + { + if (block->mTrueJump->mNumEntries == 1 && !block->mFalseJump->mEntryRequiredRegs[daddr]) + { + block = block->mTrueJump; + j = 0; + } + else if (block->mFalseJump->mNumEntries == 1 && !block->mTrueJump->mEntryRequiredRegs[daddr]) + { + block = block->mFalseJump; + j = 0; + } + else + block = nullptr; + } + else + block = nullptr; + } + + return false; +} + +bool NativeCodeBasicBlock::MoveLoadIndirectTempStoreUp(int at) +{ + // ldy #imm + // lda (t0), y + // sta t1 + + int j = at - 1; + while (j >= 3) + { + if (mIns[j].mType == ASMIT_STA && mIns[j].mMode == ASMIM_ZERO_PAGE && (mIns[j].mAddress == mIns[at + 1].mAddress || mIns[j].mAddress == mIns[at + 1].mAddress + 1)) + { + if (mIns[j - 3].mType == ASMIT_LDA && mIns[j - 3].mMode == ASMIM_ZERO_PAGE && + mIns[j - 2].mType == ASMIT_STA && mIns[j - 2].mMode == ASMIM_ZERO_PAGE && + mIns[j - 1].mType == ASMIT_LDA && mIns[j - 1].mMode == ASMIM_ZERO_PAGE && + mIns[j - 0].mType == ASMIT_STA && mIns[j - 0].mMode == ASMIM_ZERO_PAGE) + { + if (mIns[j - 2].mAddress == mIns[at + 1].mAddress && + mIns[j - 0].mAddress == mIns[at + 1].mAddress + 1 && + mIns[j - 1].mAddress == mIns[j - 3].mAddress + 1) + { + for (int k = j + 1; k < at; k++) + mIns[k].mLive |= mIns[at + 2].mLive & LIVE_CPU_REG_Y; + + mIns[at + 0].mLive |= mIns[j].mLive; + mIns[at + 1].mLive |= mIns[j].mLive; + mIns[at + 2].mLive |= mIns[j].mLive; + + mIns[at + 1].mAddress = mIns[j - 3].mAddress; + mIns[at + 1].mLive |= LIVE_MEM; + + mIns.Insert(j + 1, mIns[at + 2]); + mIns.Insert(j + 1, mIns[at + 2]); + mIns.Insert(j + 1, mIns[at + 2]); + + mIns.Remove(at + 3); + mIns.Remove(at + 3); + mIns.Remove(at + 3); + + return true; + } + } + + return false; + } + if (mIns[j].mType == ASMIT_STX && mIns[j].mMode == ASMIM_ZERO_PAGE && (mIns[j].mAddress == mIns[at + 1].mAddress || mIns[j].mAddress == mIns[at + 1].mAddress + 1)) + { + if (mIns[j - 3].mType == ASMIT_LDX && mIns[j - 3].mMode == ASMIM_ZERO_PAGE && + mIns[j - 2].mType == ASMIT_STX && mIns[j - 2].mMode == ASMIM_ZERO_PAGE && + mIns[j - 1].mType == ASMIT_LDX && mIns[j - 1].mMode == ASMIM_ZERO_PAGE && + mIns[j - 0].mType == ASMIT_STX && mIns[j - 0].mMode == ASMIM_ZERO_PAGE) + { + if (mIns[j - 2].mAddress == mIns[at + 1].mAddress && + mIns[j - 0].mAddress == mIns[at + 1].mAddress + 1 && + mIns[j - 1].mAddress == mIns[j - 3].mAddress + 1) + { + int addr = mIns[j - 3].mAddress; + + while (mIns[j].mLive & LIVE_CPU_REG_A) + { + j++; + if (mIns[j].ReferencesZeroPage(addr) || mIns[j].ReferencesZeroPage(addr + 1)) + return false; + } + + for (int k = j + 1; k < at; k++) + mIns[k].mLive |= mIns[at + 2].mLive & LIVE_CPU_REG_Y; + + mIns[at + 0].mLive |= mIns[j].mLive; + mIns[at + 1].mLive |= mIns[j].mLive; + mIns[at + 2].mLive |= mIns[j].mLive; + + mIns[at + 1].mAddress = addr; + mIns[at + 1].mLive |= LIVE_MEM; + + mIns.Insert(j + 1, mIns[at + 2]); + mIns.Insert(j + 1, mIns[at + 2]); + mIns.Insert(j + 1, mIns[at + 2]); + + mIns.Remove(at + 3); + mIns.Remove(at + 3); + mIns.Remove(at + 3); + + return true; + } + } + + return false; + } + + if (mIns[j].ReferencesYReg()) + return false; + if (mIns[j].ChangesZeroPage(mIns[at + 1].mAddress)) + return false; + if (mIns[j].ChangesZeroPage(mIns[at + 1].mAddress + 1)) + return false; + if (mIns[j].ReferencesZeroPage(mIns[at + 2].mAddress)) + return false; + + j--; + } + + return false; +} + +bool NativeCodeBasicBlock::MoveLoadIndirectBypassYUp(int at) +{ + // ldy #imm + // lda (t0), y + + // move up, and keep A in Y for the interval + + int j = at - 1; + while (j >= 3) + { + if (mIns[j].mType == ASMIT_STA && mIns[j].mMode == ASMIM_ZERO_PAGE && (mIns[j].mAddress == mIns[at + 1].mAddress || mIns[j].mAddress == mIns[at + 1].mAddress + 1)) + { + if (mIns[j - 3].mType == ASMIT_LDA && mIns[j - 3].mMode == ASMIM_ZERO_PAGE && + mIns[j - 2].mType == ASMIT_STA && mIns[j - 2].mMode == ASMIM_ZERO_PAGE && + mIns[j - 1].mType == ASMIT_LDA && mIns[j - 1].mMode == ASMIM_ZERO_PAGE && + mIns[j - 0].mType == ASMIT_STA && mIns[j - 0].mMode == ASMIM_ZERO_PAGE) + { + if (mIns[j - 2].mAddress == mIns[at + 1].mAddress && + mIns[j - 0].mAddress == mIns[at + 1].mAddress + 1 && + mIns[j - 1].mAddress == mIns[j - 3].mAddress + 1) + { + const InterInstruction* iins(mIns[at + 0].mIns); + + mIns[at + 0].mLive |= mIns[j].mLive; + mIns[at + 1].mLive |= mIns[j].mLive; + + mIns[at + 1].mAddress = mIns[j - 3].mAddress; + mIns[at + 1].mLive |= LIVE_MEM; + + mIns.Insert(j + 1, mIns[at + 0]); + mIns.Remove(at + 1); + mIns.Insert(j + 2, mIns[at + 1]); + mIns.Remove(at + 2); + + mIns.Insert(at + 2, NativeCodeInstruction(iins, ASMIT_TYA)); + mIns.Insert(j + 3, NativeCodeInstruction(iins, ASMIT_TAY)); + + for (int k = j + 3; k < at + 3; k++) + mIns[k].mLive |= LIVE_CPU_REG_Y; + + return true; + } + } + + return false; + } + if (mIns[j].mType == ASMIT_STX && mIns[j].mMode == ASMIM_ZERO_PAGE && (mIns[j].mAddress == mIns[at + 1].mAddress || mIns[j].mAddress == mIns[at + 1].mAddress + 1)) + { + if (mIns[j - 3].mType == ASMIT_LDX && mIns[j - 3].mMode == ASMIM_ZERO_PAGE && + mIns[j - 2].mType == ASMIT_STX && mIns[j - 2].mMode == ASMIM_ZERO_PAGE && + mIns[j - 1].mType == ASMIT_LDX && mIns[j - 1].mMode == ASMIM_ZERO_PAGE && + mIns[j - 0].mType == ASMIT_STX && mIns[j - 0].mMode == ASMIM_ZERO_PAGE) + { + if (mIns[j - 2].mAddress == mIns[at + 1].mAddress && + mIns[j - 0].mAddress == mIns[at + 1].mAddress + 1 && + mIns[j - 1].mAddress == mIns[j - 3].mAddress + 1) + { + const InterInstruction* iins(mIns[at + 0].mIns); + + int addr = mIns[j - 3].mAddress; + + while (mIns[j].mLive & LIVE_CPU_REG_A) + { + j++; + if (mIns[j].ReferencesZeroPage(addr) || mIns[j].ReferencesZeroPage(addr + 1)) + return false; + } + + mIns[at + 0].mLive |= mIns[j].mLive; + mIns[at + 1].mLive |= mIns[j].mLive; + + mIns[at + 1].mAddress = addr; + mIns[at + 1].mLive |= LIVE_MEM; + + mIns.Insert(j + 1, mIns[at + 0]); + mIns.Remove(at + 1); + mIns.Insert(j + 2, mIns[at + 1]); + mIns.Remove(at + 2); + + mIns.Insert(at + 2, NativeCodeInstruction(iins, ASMIT_TYA)); + mIns.Insert(j + 3, NativeCodeInstruction(iins, ASMIT_TAY)); + + for (int k = j + 3; k < at + 3; k++) + mIns[k].mLive |= LIVE_CPU_REG_Y; + + return true; + } + } + + return false; + } + + if (mIns[j].ReferencesYReg()) + return false; + if (mIns[j].ChangesZeroPage(mIns[at + 1].mAddress)) + return false; + if (mIns[j].ChangesZeroPage(mIns[at + 1].mAddress + 1)) + return false; + + j--; + } + + return false; +} + +bool NativeCodeBasicBlock::MoveIndirectLoadStoreUp(int at) +{ + int j = at - 1; + while (j > 0) + { + if (mIns[j].mType == ASMIT_STA && mIns[j].mMode == ASMIM_ZERO_PAGE && mIns[j].mAddress == mIns[at].mAddress) + { + if (mIns[j].mLive & LIVE_CPU_REG_Y) + return false; + + mIns[at + 1].mLive |= mIns[j].mLive; + mIns[at + 2].mLive |= mIns[j].mLive; + + mIns[j].mLive |= LIVE_CPU_REG_A; + + mIns.Insert(j + 1, mIns[at + 1]); + mIns.Insert(j + 2, mIns[at + 3]); + mIns[at + 4].mType = ASMIT_NOP; + mIns[at + 4].mMode = ASMIM_IMPLIED; + return true; + } + + if (mIns[j].mMode == ASMIM_INDIRECT_Y && mIns[j].mAddress == mIns[at + 2].mAddress) + { + if (j > 0 && mIns[j - 1].mType == ASMIT_LDY && mIns[j - 1].mMode == ASMIM_IMMEDIATE && mIns[j - 1].mAddress != mIns[at + 1].mAddress) + ; + else + return false; + } + else if (mIns[at + 2].MayBeSameAddress(mIns[j])) + return false; + else if (mIns[j].mType == ASMIT_JSR) + return false; + + if (mIns[j].ChangesZeroPage(mIns[at].mAddress)) + return false; + if (mIns[j].ChangesZeroPage(mIns[at + 2].mAddress)) + return false; + if (mIns[j].ChangesZeroPage(mIns[at + 2].mAddress + 1)) + return false; + + j--; + } + + return false; +} + +bool NativeCodeBasicBlock::MoveLoadStoreOutOfXYRangeUp(int at) +{ + uint32 flags = mIns[at + 1].mFlags | mIns[at + 2].mFlags; + + int j = at - 1; + while (j >= 0) + { + if (mIns[j].MayBeChangedOnAddress(mIns[at + 2])) + return false; + if (mIns[j].ChangesAddress() && mIns[j].SameEffectiveAddress(mIns[at + 1])) + return false; + if (mIns[at + 1].mMode == ASMIM_ABSOLUTE_X && mIns[j].ChangesXReg()) + return false; + if (mIns[at + 1].mMode == ASMIM_ABSOLUTE_Y && mIns[j].ChangesYReg()) + return false; + if (mIns[at + 1].mMode == ASMIM_ZERO_PAGE && mIns[j].ChangesZeroPage(mIns[at + 1].mAddress)) + return false; + if (mIns[at + 2].mMode == ASMIM_ZERO_PAGE && mIns[j].ChangesZeroPage(mIns[at + 2].mAddress)) + return false; + if (mIns[j].mType == ASMIT_JSR) + return false; + if (mIns[j].mFlags & flags & NCIF_VOLATILE) + return false; + + if (mIns[j].mType == ASMIT_LDA) + { + if (j > 0 && (mIns[j - 1].mType == ASMIT_CLC || mIns[j - 1].mType == ASMIT_SEC)) + j--; + mIns.Insert(j, mIns[at + 2]); + mIns.Insert(j, mIns[at + 2]); + int live = 0; + + if (j > 0) + live = mIns[j - 1].mLive; + else + { + if (mEntryRequiredRegs[CPU_REG_X]) + live |= LIVE_CPU_REG_X; + if (mEntryRequiredRegs[CPU_REG_Y]) + live |= LIVE_CPU_REG_Y; + } + + mIns[j].mLive |= live; + mIns[j + 1].mLive |= live; + + mIns.Remove(at + 3); + mIns.Remove(at + 3); + + return true; + } + + j--; + } + + return false; +} + +bool NativeCodeBasicBlock::MoveGenericLoadStoreUp(int at) +{ + int j = at - 1; + while (j >= 0) + { + if (mIns[j].mType == ASMIT_STA && mIns[j].SameEffectiveAddress(mIns[at])) + { + mIns[j].mLive |= LIVE_CPU_REG_A; + mIns.Insert(j + 1, mIns[at + 1]); + mIns[j + 1].mLive |= LIVE_CPU_REG_A; + mIns[j + 1].mLive |= mIns[j].mLive; + mIns[at + 2].mType = ASMIT_NOP; + mIns[at + 2].mMode = ASMIM_IMPLIED; + return true; + } + else if (mIns[j].mType == ASMIT_LDA && mIns[j].SameEffectiveAddress(mIns[at])) + { + while (j < at && !mIns[j + 1].ChangesAccu() && (mIns[j].mLive & LIVE_CPU_REG_A)) + j++; + if (j < at) + { + mIns[j].mLive |= LIVE_CPU_REG_A; + mIns.Insert(j + 1, mIns[at + 1]); + mIns[j + 1].mLive |= LIVE_CPU_REG_A; + mIns[j + 1].mLive |= mIns[j].mLive; + mIns[at + 2].mType = ASMIT_NOP; + mIns[at + 2].mMode = ASMIM_IMPLIED; + return true; + } + else + return false; + } + if (mIns[at].MayBeChangedOnAddress(mIns[j])) + return false; + if (mIns[j].MayBeSameAddress(mIns[at + 1], true)) + return false; + if ((mIns[at + 0].mMode == ASMIM_ABSOLUTE_X || mIns[at + 1].mMode == ASMIM_ABSOLUTE_X) && mIns[j].ChangesXReg()) + return false; + if ((mIns[at + 0].mMode == ASMIM_ABSOLUTE_Y || mIns[at + 1].mMode == ASMIM_ABSOLUTE_Y) && mIns[j].ChangesYReg()) + return false; + if (mIns[at + 0].mMode == ASMIM_INDIRECT_Y && (mIns[j].ChangesYReg() || mIns[j].ChangesZeroPage(mIns[at].mAddress) || mIns[j].ChangesZeroPage(mIns[at].mAddress + 1))) + return false; + if (mIns[at + 1].mMode == ASMIM_INDIRECT_Y && (mIns[j].ChangesYReg() || mIns[j].ChangesZeroPage(mIns[at + 1].mAddress) || mIns[j].ChangesZeroPage(mIns[at + 1].mAddress + 1))) + return false; + + if (mIns[j].mType == ASMIT_JSR) + return false; + + j--; + } + + return false; +} + +bool NativeCodeBasicBlock::MoveAbsoluteLoadStoreUp(int at) +{ + int j = at - 1; + while (j >= 0) + { + if (mIns[j].mType == ASMIT_STA && mIns[j].mMode == ASMIM_ZERO_PAGE && mIns[j].mAddress == mIns[at].mAddress) + { + mIns[j].mLive |= LIVE_CPU_REG_A; + mIns.Insert(j + 1, mIns[at + 1]); + mIns[j + 1].mLive |= LIVE_CPU_REG_A; + mIns[j + 1].mLive |= mIns[j].mLive; + mIns[at + 2].mType = ASMIT_NOP; + mIns[at + 2].mMode = ASMIM_IMPLIED; + return true; + } + else if (mIns[j].mType == ASMIT_LDA && mIns[j].mMode == ASMIM_ZERO_PAGE && mIns[j].mAddress == mIns[at].mAddress) + { + while (j < at && !mIns[j + 1].ChangesAccu() && (mIns[j].mLive & LIVE_CPU_REG_A)) + j++; + if (j < at) + { + mIns[j].mLive |= LIVE_CPU_REG_A; + mIns.Insert(j + 1, mIns[at + 1]); + mIns[j + 1].mLive |= LIVE_CPU_REG_A; + mIns[j + 1].mLive |= mIns[j].mLive; + mIns[at + 2].mType = ASMIT_NOP; + mIns[at + 2].mMode = ASMIM_IMPLIED; + return true; + } + else + return false; + } + if (mIns[j].ChangesZeroPage(mIns[at].mAddress)) + return false; + if (mIns[j].MayBeSameAddress(mIns[at + 1], true)) + return false; + if (mIns[at + 1].mMode == ASMIM_ABSOLUTE_X && mIns[j].ChangesXReg()) + return false; + if (mIns[at + 1].mMode == ASMIM_ABSOLUTE_Y && mIns[j].ChangesYReg()) + return false; + if (mIns[at + 1].mMode == ASMIM_INDIRECT_Y && (mIns[j].ChangesYReg() || mIns[j].ChangesZeroPage(mIns[at + 1].mAddress) || mIns[j].ChangesZeroPage(mIns[at + 1].mAddress + 1))) + return false; + + if (mIns[j].mType == ASMIT_JSR) + return false; + + j--; + } + + return false; +} + +bool NativeCodeBasicBlock::ReplaceZeroPageDown(int at) +{ + int i = at + 2; + while (i < mIns.Size()) + { + if (mIns[i].mType == ASMIT_LDA && mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i].mAddress == mIns[at].mAddress) + { + mIns[i].mLive |= LIVE_CPU_REG_A; + mIns[at + 1].mLive |= mIns[i].mLive; + mIns.Insert(i + 1, mIns[at + 1]); + mIns.Remove(at, 2); + return true; + } + + if (mIns[i].ChangesZeroPage(mIns[at].mAddress)) + return false; + if (mIns[i].ReferencesZeroPage(mIns[at + 1].mAddress)) + return false; + + i++; + } + + return false; +} + +bool NativeCodeBasicBlock::ReplaceZeroPageUp(int at) +{ + int i = at - 1; + while (i >= 0) + { + if ((mIns[i].mType == ASMIT_STA || mIns[i].mType == ASMIT_STX || mIns[i].mType == ASMIT_STY) && mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i].mAddress == mIns[at].mAddress) + { + while (i < at) + { + if (mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i].mAddress == mIns[at].mAddress) + mIns[i].mAddress = mIns[at + 1].mAddress; + i++; + } + + mIns[at + 0].mType = ASMIT_NOP; mIns[at + 0].mMode = ASMIM_IMPLIED; + mIns[at + 1].mType = ASMIT_NOP; mIns[at + 1].mMode = ASMIM_IMPLIED; + + return true; + } + + if (mIns[i].mType == ASMIT_JSR) + return false; + + if (mIns[i].ChangesZeroPage(mIns[at + 1].mAddress)) + return false; + if (mIns[i].UsesZeroPage(mIns[at + 1].mAddress)) + return false; + if (mIns[i].mMode == ASMIM_INDIRECT_Y && (mIns[i].mAddress == mIns[at + 1].mAddress || mIns[i].mAddress + 1 == mIns[at + 1].mAddress)) + return false; + if (mIns[i].mMode == ASMIM_INDIRECT_Y && (mIns[i].mAddress == mIns[at].mAddress || mIns[i].mAddress + 1 == mIns[at].mAddress)) + return false; + + i--; + } + + return false; +} + +bool NativeCodeBasicBlock::MoveZeroPageCrossBlockUp(int at, const NativeCodeInstruction& lins, const NativeCodeInstruction& sins) +{ + if (!mPatched) + { + mPatched = true; + + if (at == -2) + { + for (int i = 0; i < mIns.Size(); i++) + { + if (mIns[i].ChangesZeroPage(lins.mAddress)) + return false; + if (mIns[i].ReferencesZeroPage(sins.mAddress)) + return false; + if (mIns[i].mMode == ASMIM_INDIRECT_Y && (mIns[i].mAddress + 1 == lins.mAddress || mIns[i].mAddress == lins.mAddress)) + return false; + } + + at = mIns.Size(); + } + else + { + if (at == -1) + at = mIns.Size(); + + int i = at; + while (i > 0) + { + i--; + if (mIns[i].mType == ASMIT_STA && mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i].mAddress == lins.mAddress) + { + while (i < at) + { + if (mIns[i].mAddress == lins.mAddress) + mIns[i].mAddress = sins.mAddress; + i++; + } + return true; + } + + if (mIns[i].ChangesZeroPage(lins.mAddress)) + return false; + else if (mIns[i].mMode == ASMIM_ZERO_PAGE && !mIns[i].ChangesAddress() && mIns[i].mAddress == lins.mAddress) + ; + else if (mIns[i].mMode == ASMIM_INDIRECT_Y && (mIns[i].mAddress + 1 == lins.mAddress || mIns[i].mAddress == lins.mAddress)) + return false; + else if (mIns[i].ReferencesZeroPage(sins.mAddress)) + return false; + else if (mIns[i].mType == ASMIT_JSR && mIns[i].ReferencesZeroPage(lins.mAddress)) + return false; + } + } + + int pat = -1; + NativeCodeBasicBlock* eb = nullptr; + + if (mEntryBlocks.Size() == 1 || mEntryBlocks.Size() == 2 && mEntryBlocks[1] == this) + { + eb = mEntryBlocks[0]; + if (!eb->mFalseJump) + ; + else if (eb->mTrueJump == eb || eb->mFalseJump == eb) + pat = -2; + else if (eb->mFalseJump->mPatched && eb->mTrueJump->mPatched) + ; + else if ( + eb->mTrueJump == this && !eb->mFalseJump->mEntryRequiredRegs[lins.mAddress] && !eb->mFalseJump->mEntryRequiredRegs[sins.mAddress] || + eb->mFalseJump == this && !eb->mTrueJump->mEntryRequiredRegs[lins.mAddress] && !eb->mTrueJump->mEntryRequiredRegs[sins.mAddress]) + ; + else + return false; + } + else if (mEntryBlocks.Size() == 2) + { + NativeCodeBasicBlock* eb0 = mEntryBlocks[0]; + NativeCodeBasicBlock* eb1 = mEntryBlocks[1]; + + if (eb0->mEntryBlocks.Size() == 1 && eb0->mEntryBlocks[0] == eb1) + eb = eb0; + else if (eb1->mEntryBlocks.Size() == 1 && eb1->mEntryBlocks[0] == eb0) + eb = eb1; + else + return false; + + if (!eb->mFalseJump) + ; + else if (eb->mTrueJump == eb || eb->mFalseJump == eb) + ; + else if (eb->mFalseJump->mPatched && eb->mTrueJump->mPatched) + ; + else if ( + eb->mTrueJump == this && !eb->mFalseJump->mEntryRequiredRegs[lins.mAddress] && !eb->mFalseJump->mEntryRequiredRegs[sins.mAddress] || + eb->mFalseJump == this && !eb->mTrueJump->mEntryRequiredRegs[lins.mAddress] && !eb->mTrueJump->mEntryRequiredRegs[sins.mAddress]) + ; + else + return false; + + pat = -2; + } + else + return false; + + if (!eb->MoveZeroPageCrossBlockUp(pat, lins, sins)) + return false; + + for(int i=0; imNumEntries == 1 && !mTrueJump->mFalseJump && mTrueJump->mTrueJump == mFalseJump) + { + cblock1 = mTrueJump; + eblock1 = mFalseJump; + } + else if (mFalseJump->mNumEntries == 1 && !mFalseJump->mFalseJump && mFalseJump->mTrueJump == mTrueJump) + { + cblock1 = mFalseJump; + eblock1 = mTrueJump; + } + + if (eblock1 && eblock1->mNumEntries == 2 && eblock1->mTrueJump && eblock1->mFalseJump) + { + if (eblock1->mTrueJump->mNumEntries == 1 && !eblock1->mTrueJump->mFalseJump && eblock1->mTrueJump->mTrueJump == eblock1->mFalseJump) + { + cblock2 = eblock1->mTrueJump; + eblock2 = eblock1->mFalseJump; + } + else if (eblock1->mFalseJump->mNumEntries == 1 && !eblock1->mFalseJump->mFalseJump && eblock1->mFalseJump->mTrueJump == eblock1->mTrueJump) + { + cblock2 = eblock1->mFalseJump; + eblock2 = eblock1->mTrueJump; + } + + if (eblock2 && eblock2->mNumEntries == 2) + { + int sz1 = mIns.Size(), sz2 = eblock1->mIns.Size(); + if (sz1 > 0 && sz2 == 1) + { + if (mIns[sz1 - 1].mType == ASMIT_CPY && mIns[sz1 - 1].mMode == ASMIM_IMMEDIATE && mIns[sz1 - 1].mAddress == 0xff && (mBranch == ASMIT_BCC || mBranch == ASMIT_BCS || mBranch == ASMIT_BEQ || mBranch == ASMIT_BNE) && + eblock1->mIns[0].mType == ASMIT_INY && (eblock1->mBranch == ASMIT_BEQ || eblock1->mBranch == ASMIT_BNE) && !cblock1->ReferencesYReg()) + { + bool check0, check1; + check0 = (mBranch == ASMIT_BCC || mBranch == ASMIT_BNE) == (mTrueJump == cblock1); + check1 = (eblock1->mBranch == ASMIT_BNE) == (eblock1->mTrueJump == cblock2); + + if (check0 == check1) + { + for (int i = 0; i < cblock1->mIns.Size(); i++) + cblock2->mIns.Insert(i, cblock1->mIns[i]); + mBranch = ASMIT_JMP; + mTrueJump = eblock1; + mFalseJump = nullptr; + eblock1->mNumEntries--; + eblock1->mEntryBlocks.RemoveAll(cblock1); + changed = true; + } + } + } + } + } + } + + if (mTrueJump && mTrueJump->MergeDuplicateCondition()) + changed = true; + if (mFalseJump && mFalseJump->MergeDuplicateCondition()) + changed = true; + + } + + return changed; + +} + +bool NativeCodeBasicBlock::ShortcutCrossBlockCondition(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + int sz = mIns.Size(); + if (sz > 0 && mIns[sz - 1].mType == ASMIT_LDA && mIns[sz - 1].mMode == ASMIM_IMMEDIATE && !mFalseJump) + { + if (mTrueJump->mIns.Size() == 2 && mTrueJump->mIns[0].mType == ASMIT_STA && + mTrueJump->mIns[1].mType == ASMIT_ORA && mTrueJump->mIns[1].mMode == ASMIM_IMMEDIATE && mTrueJump->mIns[1].mAddress == 0 && + (mTrueJump->mBranch == ASMIT_BEQ || mTrueJump->mBranch == ASMIT_BNE)) + { + mIns.Push(mTrueJump->mIns[0]); + + mTrueJump->mNumEntries--; + mTrueJump->mEntryBlocks.Remove(mTrueJump->mEntryBlocks.IndexOf(this)); + if (mTrueJump->mBranch == ASMIT_BEQ) + { + if (mIns[sz - 1].mAddress == 0) + mTrueJump = mTrueJump->mTrueJump; + else + mTrueJump = mTrueJump->mFalseJump; + } + else + { + if (mIns[sz - 1].mAddress != 0) + mTrueJump = mTrueJump->mTrueJump; + else + mTrueJump = mTrueJump->mFalseJump; + } + mTrueJump->mNumEntries++; + mTrueJump->mEntryBlocks.Push(this); + changed = true; + } + } + + if (mTrueJump && mTrueJump->ShortcutCrossBlockCondition()) + changed = true; + if (mFalseJump && mFalseJump->ShortcutCrossBlockCondition()) + changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::ShortcutCrossBlockMoves(NativeCodeProcedure* proc) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + if (!mLoopHead) + { + int i = 0; + while (i + 1 < mIns.Size()) + { + if (mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && !(mIns[i + 0].mLive & LIVE_MEM) && + mIns[i + 1].mType == ASMIT_STA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && !(mIns[i + 1].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z))) + { + proc->ResetPatched(); + if (MoveZeroPageCrossBlockUp(i, mIns[i + 0], mIns[i + 1])) + { + changed = true; + mIns.Remove(i, 2); + } + else + i++; + } + else + i++; + } + + CheckLive(); + } + + if (mTrueJump && mTrueJump->ShortcutCrossBlockMoves(proc)) + changed = true; + if (mFalseJump && mFalseJump->ShortcutCrossBlockMoves(proc)) + changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::Check16BitSum(int at, NativeRegisterSum16Info& info) +{ + if (at + 6 < mIns.Size()) + { + if (mIns[at + 0].mType == ASMIT_CLC && + mIns[at + 1].mType == ASMIT_LDA && + (mIns[at + 2].mType == ASMIT_ADC || mIns[at + 2].mType == ASMIT_ORA && mIns[at + 2].mMode == ASMIM_IMMEDIATE && mIns[at + 2].mAddress == 0) && + mIns[at + 3].mType == ASMIT_STA && mIns[at + 3].mMode == ASMIM_ZERO_PAGE && + mIns[at + 4].mType == ASMIT_LDA && + mIns[at + 5].mType == ASMIT_ADC && + mIns[at + 6].mType == ASMIT_STA && mIns[at + 6].mMode == ASMIM_ZERO_PAGE) + { + info.mDstL = &(mIns[at + 3]); + info.mDstH = &(mIns[at + 6]); + + if (mIns[at + 2].mMode == ASMIM_ZERO_PAGE && (mIns[at + 5].mMode == ASMIM_ZERO_PAGE || mIns[at + 5].mMode == ASMIM_IMMEDIATE && mIns[at + 5].mAddress == 0)) + { + if (mIns[at + 1].mMode == ASMIM_IMMEDIATE && mIns[at + 4].mMode == ASMIM_IMMEDIATE) + { + info.mAddress = (mIns[at + 1].mAddress & 0xff) + (mIns[at + 4].mAddress << 8); + info.mLinkerObject = nullptr; + info.mSrcL = &(mIns[at + 2]); + info.mSrcH = &(mIns[at + 5]); + info.mAddL = &(mIns[at + 1]); + info.mAddH = &(mIns[at + 4]); + info.mImmediate = true; + + return true; + } + else if (mIns[at + 1].mMode == ASMIM_IMMEDIATE_ADDRESS && mIns[at + 4].mMode == ASMIM_IMMEDIATE_ADDRESS && mIns[at + 1].mLinkerObject == mIns[at + 4].mLinkerObject && mIns[at + 1].mAddress == mIns[at + 4].mAddress) + { + info.mAddress = mIns[at + 1].mAddress; + info.mLinkerObject = mIns[at + 1].mLinkerObject; + info.mSrcL = &(mIns[at + 2]); + info.mSrcH = &(mIns[at + 5]); + info.mAddL = &(mIns[at + 1]); + info.mAddH = &(mIns[at + 4]); + info.mImmediate = true; + + return true; + } + } + else if (mIns[at + 1].mMode == ASMIM_ZERO_PAGE && (mIns[at + 4].mMode == ASMIM_ZERO_PAGE || mIns[at + 4].mMode == ASMIM_IMMEDIATE && mIns[at + 4].mAddress == 0)) + { + if (mIns[at + 2].mMode == ASMIM_IMMEDIATE && mIns[at + 5].mMode == ASMIM_IMMEDIATE) + { + info.mAddress = (mIns[at + 2].mAddress & 0xff) + (mIns[at + 5].mAddress << 8); + info.mLinkerObject = nullptr; + info.mSrcL = &(mIns[at + 1]); + info.mSrcH = &(mIns[at + 4]); + info.mAddL = &(mIns[at + 2]); + info.mAddH = &(mIns[at + 5]); + info.mImmediate = true; + + return true; + } + else if (mIns[at + 2].mMode == ASMIM_IMMEDIATE_ADDRESS && mIns[at + 5].mMode == ASMIM_IMMEDIATE_ADDRESS && mIns[at + 2].mLinkerObject == mIns[at + 5].mLinkerObject && mIns[at + 2].mAddress == mIns[at + 5].mAddress) + { + info.mAddress = mIns[at + 2].mAddress; + info.mLinkerObject = mIns[at + 2].mLinkerObject; + info.mSrcL = &(mIns[at + 1]); + info.mSrcH = &(mIns[at + 4]); + info.mAddL = &(mIns[at + 2]); + info.mAddH = &(mIns[at + 5]); + info.mImmediate = true; + + return true; + } + } + + if (mIns[at + 1].mMode == ASMIM_ZERO_PAGE && mIns[at + 4].mMode == ASMIM_ZERO_PAGE) + { + info.mSrcL = &(mIns[at + 1]); + info.mSrcH = &(mIns[at + 4]); + info.mAddL = &(mIns[at + 2]); + info.mAddH = &(mIns[at + 5]); + info.mImmediate = false; + + return true; + } + } + else if (mIns[at + 0].mType == ASMIT_LDA && + mIns[at + 1].mType == ASMIT_CLC && + mIns[at + 2].mType == ASMIT_ADC && + mIns[at + 3].mType == ASMIT_STA && mIns[at + 3].mMode == ASMIM_ZERO_PAGE && + mIns[at + 4].mType == ASMIT_LDA && + mIns[at + 5].mType == ASMIT_ADC && + mIns[at + 6].mType == ASMIT_STA && mIns[at + 6].mMode == ASMIM_ZERO_PAGE) + { + info.mDstL = &(mIns[at + 3]); + info.mDstH = &(mIns[at + 6]); + + if (mIns[at + 2].mMode == ASMIM_ZERO_PAGE && mIns[at + 5].mMode == ASMIM_ZERO_PAGE) + { + if (mIns[at + 0].mMode == ASMIM_IMMEDIATE && mIns[at + 4].mMode == ASMIM_IMMEDIATE) + { + info.mAddress = (mIns[at + 0].mAddress & 0xff) + (mIns[at + 4].mAddress << 8); + info.mLinkerObject = nullptr; + info.mSrcL = &(mIns[at + 2]); + info.mSrcH = &(mIns[at + 5]); + info.mAddL = &(mIns[at + 0]); + info.mAddH = &(mIns[at + 4]); + info.mImmediate = true; + + return true; + } + else if (mIns[at + 0].mMode == ASMIM_IMMEDIATE_ADDRESS && mIns[at + 4].mMode == ASMIM_IMMEDIATE_ADDRESS && mIns[at + 0].mLinkerObject == mIns[at + 4].mLinkerObject && mIns[at + 0].mAddress == mIns[at + 4].mAddress) + { + info.mAddress = mIns[at + 0].mAddress; + info.mLinkerObject = mIns[at + 0].mLinkerObject; + info.mSrcL = &(mIns[at + 2]); + info.mSrcH = &(mIns[at + 5]); + info.mAddL = &(mIns[at + 0]); + info.mAddH = &(mIns[at + 4]); + info.mImmediate = true; + + return true; + } + } + else if (mIns[at + 0].mMode == ASMIM_ZERO_PAGE && mIns[at + 4].mMode == ASMIM_ZERO_PAGE) + { + if (mIns[at + 2].mMode == ASMIM_IMMEDIATE && mIns[at + 5].mMode == ASMIM_IMMEDIATE) + { + info.mAddress = (mIns[at + 2].mAddress & 0xff) + (mIns[at + 5].mAddress << 8); + info.mLinkerObject = nullptr; + info.mSrcL = &(mIns[at + 0]); + info.mSrcH = &(mIns[at + 4]); + info.mAddL = &(mIns[at + 2]); + info.mAddH = &(mIns[at + 5]); + info.mImmediate = true; + + return true; + } + else if (mIns[at + 2].mMode == ASMIM_IMMEDIATE_ADDRESS && mIns[at + 5].mMode == ASMIM_IMMEDIATE_ADDRESS && mIns[at + 2].mLinkerObject == mIns[at + 5].mLinkerObject && mIns[at + 2].mAddress == mIns[at + 5].mAddress) + { + info.mAddress = mIns[at + 2].mAddress; + info.mLinkerObject = mIns[at + 2].mLinkerObject; + info.mSrcL = &(mIns[at + 0]); + info.mSrcH = &(mIns[at + 4]); + info.mAddL = &(mIns[at + 2]); + info.mAddH = &(mIns[at + 5]); + info.mImmediate = true; + + return true; + } + } + } + } + + return false; +} + +bool NativeCodeBasicBlock::IsFinalZeroPageUseTail(const NativeCodeBasicBlock* block, int from, int to, bool pair) +{ + if (!mPatched) + { + mPatched = true; + + for (int i = 0; i < mIns.Size(); i++) + { + if (mIns[i].mMode == ASMIM_ZERO_PAGE) + { + if (mIns[i].mAddress == to) + return false; + if (pair && mIns[i].mAddress == to + 1) + return false; + } + else if (mIns[i].mMode == ASMIM_INDIRECT_Y) + { + if (mIns[i].mAddress == to || mIns[i].mAddress + 1 == to) + return false; + if (pair && mIns[i].mAddress == to + 1) + return false; + if (!pair && (mIns[i].mAddress == from || mIns[i].mAddress + 1 == from)) + return false; + } + else if (mIns[i].ReferencesZeroPage(to) || (pair && mIns[i].ReferencesZeroPage(to + 1))) + return false; + else if (mIns[i].mType == ASMIT_JSR) + { + if (mIns[i].mFlags & NCIF_USE_ZP_32_X) + { + if (to >= mIns[i].mParam && to < mIns[i].mParam + 4 || + from >= mIns[i].mParam && from < mIns[i].mParam + 4) + return false; + } + } + } + + if (mTrueJump && !mTrueJump->IsFinalZeroPageUseTail(block, from, to, pair)) + return false; + if (mFalseJump && !mFalseJump->IsFinalZeroPageUseTail(block, from, to, pair)) + return false; + } + + return true; +} + +static bool ZeroPageMayBeChangedOnBlockPathUp(const NativeCodeBasicBlock* block, const NativeCodeBasicBlock* head, int reg) +{ + ExpandingArray blocks; + blocks.Push(block); + int i = 0; + while (i < blocks.Size()) + { + block = blocks[i++]; + if (block != head) + { + if (block->ChangesZeroPage(reg)) + return true; + + for (int j = 0; j < block->mEntryBlocks.Size(); j++) + { + if (!blocks.Contains(block->mEntryBlocks[j])) + blocks.Push(block->mEntryBlocks[j]); + } + } + } + + return false; +} + +bool NativeCodeBasicBlock::IsFinalZeroPageUse(const NativeCodeBasicBlock* block, int at, int from, int to, bool pair, bool fchanged) +{ + if (at == 0 && mVisited) + return false; + + if (!mPatched) + { + if (at != 0 || (mEntryRequiredRegs[from] || mEntryRequiredRegs[to] || (pair && (mEntryRequiredRegs[from + 1] || mEntryRequiredRegs[to + 1])))) + { + mPatched = true; + + if (at == 0) + { + if (mNumEntries > 1) + { + for (int i = 0; i < mEntryBlocks.Size(); i++) + { + if (mEntryBlocks[i] != block && !mEntryBlocks[i]->IsDominatedBy(block)) + return false; + if (!fchanged && (ZeroPageMayBeChangedOnBlockPathUp(mEntryBlocks[i], block, from) || pair && ZeroPageMayBeChangedOnBlockPathUp(mEntryBlocks[i], block, from + 1))) + fchanged = true; + } + } + } + + while (at < mIns.Size()) + { + if (mIns[at].mMode == ASMIM_ZERO_PAGE) + { + if (mIns[at].mAddress == to && (fchanged || mIns[at].ChangesAddress())) + return false; + if (pair && mIns[at].mAddress == to + 1 && (fchanged || mIns[at].ChangesAddress())) + return false; + if (mIns[at].mAddress == from && mIns[at].ChangesAddress()) + fchanged = true; + if (pair && mIns[at].mAddress == from + 1 && mIns[at].ChangesAddress()) + fchanged = true; + } + else if (mIns[at].mMode == ASMIM_INDIRECT_Y) + { + if (!pair && mIns[at].mAddress == to) + return false; + if (mIns[at].mAddress == to && (fchanged || mIns[at].ChangesAddress())) + return false; + if (mIns[at].mAddress + 1 == to) + return false; + if (pair && mIns[at].mAddress == to + 1) + return false; + if (!pair && mIns[at].mAddress == from) + return false; + if (mIns[at].mAddress + 1 == from) + return false; + if (pair && mIns[at].mAddress == from + 1) + return false; + } + else if (mIns[at].mType == ASMIT_JSR) + { + fchanged = true; + + LinkerObject* lo = mIns[at].mLinkerObject; + if (lo) + { + for (int i = 0; i < lo->mNumTemporaries; i++) + { + if (from >= lo->mTemporaries[i] && from < lo->mTemporaries[i] + lo->mTempSizes[i] || + to >= lo->mTemporaries[i] && to < lo->mTemporaries[i] + lo->mTempSizes[i]) + return false; + } + } + + if (mIns[at].ChangesZeroPage(from) || mIns[at].ChangesZeroPage(to)) + return false; + + if (mIns[at].mFlags & NCIF_USE_ZP_32_X) + { + if (to >= mIns[at].mParam && to < mIns[at].mParam + 4 || + from >= mIns[at].mParam && from < mIns[at].mParam + 4) + return false; + } + } + + at++; + } + + if (mTrueJump && !mTrueJump->IsFinalZeroPageUse(block, 0, from, to, pair, fchanged)) + return false; + if (mFalseJump && !mFalseJump->IsFinalZeroPageUse(block, 0, from, to, pair, fchanged)) + return false; + } + else + return IsFinalZeroPageUseTail(block, from, to, pair); + } + + return true; +} + +bool NativeCodeBasicBlock::BackwardFindLiveRange(const NativeCodeBasicBlock* block, int reg, bool pair, ExpandingArray& ranges) +{ + if (mVisited) + return mPatchExit; + + if (!mPatchExit) + { + mPatchExit = true; + + if (mTrueJump && !mTrueJump->ForwardFindLiveRange(block, 0, reg, pair, ranges)) + return false; + if (mFalseJump && !mFalseJump->ForwardFindLiveRange(block, 0, reg, pair, ranges)) + return false; + + CodeRange cr; + cr.mBlock = this; + cr.mEnd = mIns.Size(); + + for (int i = mIns.Size() - 1; i >= 0; i--) + { + if (mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i].mAddress == reg) + { + if (mIns[i].mType == ASMIT_STA || mIns[i].mType == ASMIT_STX || mIns[i].mType == ASMIT_STY) + { + cr.mStart = i; + ranges.Push(cr); + return true; + } + } + } + + cr.mStart = 0; + ranges.Push(cr); + + for (int i = 0; i < mEntryBlocks.Size(); i++) + { + if (!mEntryBlocks[i]->BackwardFindLiveRange(block, reg, pair, ranges)) + return false; + } + } + + return true; +} + +bool NativeCodeBasicBlock::CanReplaceRegInLiveRange(const ExpandingArray& range, int reg, int with, bool pair) +{ + for (int i = 0; i < range.Size(); i++) + { + if (range[i].mStart == 0 && range[i].mBlock->mEntryRequiredRegs[with]) + return false; + if (range[i].mBlock->ReferencesZeroPage(with, range[i].mStart, range[i].mEnd)) + return false; + if (!pair) + { + for (int j = range[i].mStart; j < range[i].mEnd; j++) + { + const NativeCodeInstruction& ins(range[i].mBlock->mIns[j]); + if (ins.mMode == ASMIM_INDIRECT_Y && (ins.mAddress == reg || ins.mAddress + 1 == reg)) + return false; + } + } + } + return true; +} + +bool NativeCodeBasicBlock::ReplaceRegInLiveRange(const ExpandingArray& range, int reg, int with, bool pair) +{ + bool changed = false; + for (int i = 0; i < range.Size(); i++) + { + for (int j = range[i].mStart; j < range[i].mEnd; j++) + { + NativeCodeInstruction& ins(range[i].mBlock->mIns[j]); + if (ins.mMode == ASMIM_ZERO_PAGE || ins.mMode == ASMIM_INDIRECT_Y) + { + if (ins.mAddress == reg) + { + ins.mAddress = with; + changed = true; + } + } + } + if (range[i].mBlock->mEntryRequiredRegs[reg]) + range[i].mBlock->mEntryRequiredRegs += with; + } + return changed; +} + +bool NativeCodeBasicBlock::ForwardFindLiveRange(const NativeCodeBasicBlock* block, int at, int reg, bool pair, ExpandingArray& ranges) +{ + if (!mPatched) + { + mPatched = true; + if (at > 0 || mEntryRequiredRegs[reg] || (pair && mEntryRequiredRegs[reg + 1])) + { + if (at == 0) + { + if (mNumEntries > 1) + { + for (int i = 0; i < mEntryBlocks.Size(); i++) + { + if (mEntryBlocks[i] != block) + { + if (!mEntryBlocks[i]->IsDominatedBy(block)) + return false; + if (!mEntryBlocks[i]->BackwardFindLiveRange(block, reg, pair, ranges)) + return false; + } + } + } + } + + CodeRange cr; + cr.mBlock = this; + cr.mStart = at; + + for (int i = at; i < mIns.Size(); i++) + { + if (mIns[i].mMode == ASMIM_ZERO_PAGE || mIns[i].mMode == ASMIM_INDIRECT_Y) + { + if (mIns[i].mAddress == reg && !(mIns[i].mLive & LIVE_MEM)) + { + cr.mEnd = i + 1; + ranges.Push(cr); + return true; + } + } + + if (mIns[i].mType == ASMIT_JSR) + return false; + } + + cr.mEnd = mIns.Size(); + mPatchExit = true; + ranges.Push(cr); + + if (mTrueJump && !mTrueJump->ForwardFindLiveRange(block, 0, reg, pair, ranges)) + return false; + if (mFalseJump && !mFalseJump->ForwardFindLiveRange(block, 0, reg, pair, ranges)) + return false; + } + } + + return true; +} + + +bool NativeCodeBasicBlock::ReplaceFinalZeroPageUse(NativeCodeProcedure* nproc) +{ +// return false; + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + for (int i = 0; i < mIns.Size(); i++) + { + if (i + 1 < mIns.Size() && + mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_STA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + mIns[i + 0].mAddress >= BC_REG_FPARAMS && mIns[i + 0].mAddress < BC_REG_FPARAMS_END && + mIns[i + 1].mAddress >= BC_REG_TMP) + { + nproc->ResetPatched(); + if (IsFinalZeroPageUse(this, i + 2, mIns[i + 1].mAddress, mIns[i + 0].mAddress, false, false)) + { + nproc->ResetPatched(); + if (ForwardReplaceZeroPage(i + 2, mIns[i + 1].mAddress, mIns[i + 0].mAddress, false)) + changed = true; + + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + changed = true; + } + } + if (i + 3 < mIns.Size() && + mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_STA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + mIns[i + 2].mType == ASMIT_LDA && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && mIns[i + 2].mAddress == mIns[i + 0].mAddress + 1 && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && mIns[i + 3].mAddress == mIns[i + 1].mAddress + 1 && + mIns[i + 0].mAddress >= BC_REG_FPARAMS && mIns[i + 0].mAddress < BC_REG_FPARAMS_END && + mIns[i + 1].mAddress >= BC_REG_TMP) + { + nproc->ResetPatched(); + if (IsFinalZeroPageUse(this, i + 4, mIns[i + 1].mAddress, mIns[i + 0].mAddress, true, false)) + { + nproc->ResetPatched(); + if (ForwardReplaceZeroPage(i + 4, mIns[i + 1].mAddress, mIns[i + 0].mAddress, true)) + changed = true; + nproc->ResetPatched(); + if (ForwardReplaceZeroPage(i + 4, mIns[i + 3].mAddress, mIns[i + 2].mAddress, false)) + changed = true; + + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 3].mType = ASMIT_NOP; mIns[i + 3].mMode = ASMIM_IMPLIED; + + changed = true; + } + } + + if (i + 5 < mIns.Size() && + mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_ADC && + mIns[i + 2].mType == ASMIT_STA && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && + mIns[i + 3].mType == ASMIT_LDA && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && mIns[i + 3].mAddress == mIns[i + 0].mAddress + 1 && + mIns[i + 4].mType == ASMIT_ADC && !mIns[i + 4].MayBeSameAddress(mIns[i + 2]) && + mIns[i + 5].mType == ASMIT_STA && mIns[i + 5].mMode == ASMIM_ZERO_PAGE && mIns[i + 5].mAddress == mIns[i + 2].mAddress + 1 && + mIns[i + 0].mAddress >= BC_REG_FPARAMS && mIns[i + 0].mAddress < BC_REG_FPARAMS_END && + mIns[i + 2].mAddress >= BC_REG_TMP) + { + nproc->ResetPatched(); + if (IsFinalZeroPageUse(this, i + 6, mIns[i + 2].mAddress, mIns[i + 0].mAddress, true, true)) + { + nproc->ResetPatched(); + if (ForwardReplaceZeroPage(i + 6, mIns[i + 2].mAddress, mIns[i + 0].mAddress, true)) + changed = true; + nproc->ResetPatched(); + if (ForwardReplaceZeroPage(i + 6, mIns[i + 5].mAddress, mIns[i + 3].mAddress, false)) + changed = true; + + mIns[i + 2].mAddress = mIns[i + 0].mAddress; + mIns[i + 5].mAddress = mIns[i + 3].mAddress; + + changed = true; + } + } + + if (i + 4 < mIns.Size() && + mIns[i + 0].mType == ASMIT_ADC && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_STA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + mIns[i + 2].mType == ASMIT_LDA && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && mIns[i + 2].mAddress == mIns[i + 0].mAddress + 1 && + mIns[i + 3].mType == ASMIT_ADC && !mIns[i + 3].MayBeSameAddress(mIns[i + 1]) && + mIns[i + 4].mType == ASMIT_STA && mIns[i + 4].mMode == ASMIM_ZERO_PAGE && mIns[i + 4].mAddress == mIns[i + 1].mAddress + 1 && + mIns[i + 0].mAddress >= BC_REG_FPARAMS && mIns[i + 0].mAddress < BC_REG_FPARAMS_END && + mIns[i + 1].mAddress >= BC_REG_TMP) + { + nproc->ResetPatched(); + if (IsFinalZeroPageUse(this, i + 5, mIns[i + 1].mAddress, mIns[i + 0].mAddress, true, true)) + { + nproc->ResetPatched(); + if (ForwardReplaceZeroPage(i + 5, mIns[i + 1].mAddress, mIns[i + 0].mAddress, true)) + changed = true; + nproc->ResetPatched(); + if (ForwardReplaceZeroPage(i + 5, mIns[i + 4].mAddress, mIns[i + 2].mAddress, false)) + changed = true; + + mIns[i + 1].mAddress = mIns[i + 0].mAddress; + mIns[i + 4].mAddress = mIns[i + 2].mAddress; + + changed = true; + } + } + + if (i + 3 < mIns.Size() && + mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + (mIns[i + 1].IsArithmetic() && mIns[i + 1].mMode == ASMIM_IMMEDIATE || mIns[i + 1].IsShift() && mIns[i + 1].mMode == ASMIM_IMPLIED) && + mIns[i + 2].mType == ASMIT_STA && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && + mIns[i + 0].mAddress >= BC_REG_FPARAMS && mIns[i + 0].mAddress < BC_REG_FPARAMS_END && + mIns[i + 2].mAddress >= BC_REG_TMP) + { + nproc->ResetPatched(); + if (IsFinalZeroPageUse(this, i + 3, mIns[i + 2].mAddress, mIns[i + 0].mAddress, false, true)) + { + nproc->ResetPatched(); + if (ForwardReplaceZeroPage(i + 3, mIns[i + 2].mAddress, mIns[i + 0].mAddress, false)) + { + mIns[i + 2].mAddress = mIns[i + 0].mAddress; + changed = true; + } + } + } + } + + if (!changed) + { + for (int i = 0; i < mIns.Size(); i++) + { + if (i + 1 < mIns.Size() && + mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && !(mIns[i + 0].mLive & LIVE_MEM) && + mIns[i + 1].mType == ASMIT_STA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + mIns[i + 0].mAddress >= BC_REG_FPARAMS && mIns[i + 0].mAddress < BC_REG_FPARAMS_END && + mIns[i + 1].mAddress >= BC_REG_TMP) + { + ExpandingArray ranges; + + nproc->ResetPatched(); + if (ForwardFindLiveRange(this, i + 2, mIns[i + 1].mAddress, false, ranges)) + { + if (CanReplaceRegInLiveRange(ranges, mIns[i + 1].mAddress, mIns[i + 0].mAddress, false)) + { +#if 0 + printf("Replace %s, %d, %d, %02x <- %02x)\n", mProc->mIdent->mString, mIndex, i, mIns[i + 1].mAddress, mIns[i + 0].mAddress); + printf("LiveRange %s (%d, %d, %02x)\n", mProc->mIdent->mString, mIndex, i, mIns[i + 1].mAddress); + for (int i = 0; i < ranges.Size(); i++) + { + printf(" %d, %d..%d\n", ranges[i].mBlock->mIndex, ranges[i].mStart, ranges[i].mEnd); + } +#endif + if (ReplaceRegInLiveRange(ranges, mIns[i + 1].mAddress, mIns[i + 0].mAddress, false)) + { + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + changed = true; + } + } + } + } + if (i + 3 < mIns.Size() && + mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && !(mIns[i + 0].mLive & LIVE_MEM) && + mIns[i + 1].mType == ASMIT_STA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + mIns[i + 2].mType == ASMIT_LDA && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && mIns[i + 2].mAddress == mIns[i + 0].mAddress + 1 && !(mIns[i + 2].mLive & LIVE_MEM) && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && mIns[i + 3].mAddress == mIns[i + 1].mAddress + 1 && + mIns[i + 0].mAddress >= BC_REG_FPARAMS && mIns[i + 0].mAddress < BC_REG_FPARAMS_END && + mIns[i + 1].mAddress >= BC_REG_TMP) + { + ExpandingArray ranges1, ranges2; + + nproc->ResetPatched(); + if (ForwardFindLiveRange(this, i + 2, mIns[i + 1].mAddress, true, ranges1)) + { + nproc->ResetPatched(); + if (ForwardFindLiveRange(this, i + 4, mIns[i + 3].mAddress, true, ranges2)) + { + if (CanReplaceRegInLiveRange(ranges1, mIns[i + 1].mAddress, mIns[i + 0].mAddress, true) && + CanReplaceRegInLiveRange(ranges2, mIns[i + 3].mAddress, mIns[i + 2].mAddress, true)) + { +#if 0 + printf("ReplacePair %s, %d\n", mProc->mIdent->mString, mIndex); + printf("LivePairRange %s (%d, %d, %02x)\n", mProc->mIdent->mString, mIndex, i, mIns[i + 1].mAddress); + for (int i = 0; i < ranges1.Size(); i++) + { + printf(" L %d, %d..%d\n", ranges1[i].mBlock->mIndex, ranges1[i].mStart, ranges1[i].mEnd); + } + for (int i = 0; i < ranges2.Size(); i++) + { + printf(" H %d, %d..%d\n", ranges2[i].mBlock->mIndex, ranges2[i].mStart, ranges2[i].mEnd); + } +#endif + + ReplaceRegInLiveRange(ranges1, mIns[i + 1].mAddress, mIns[i + 0].mAddress, true); + ReplaceRegInLiveRange(ranges2, mIns[i + 3].mAddress, mIns[i + 2].mAddress, true); + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 3].mType = ASMIT_NOP; mIns[i + 3].mMode = ASMIM_IMPLIED; + changed = true; + } + } + } + } + +#if 0 + if (i + 3 < mIns.Size() && + mIns[i + 0].mType == ASMIT_STA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_STA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE) + { + int reg1 = mIns[i + 0].mAddress, reg2 = mIns[i + 1].mAddress; + + int j = i + 2; + while (j < mIns.Size() && + !mIns[j].ReferencesZeroPage(reg1) && !mIns[j].ChangesZeroPage(reg1 + 1) && + !mIns[j].ReferencesZeroPage(reg2) && !mIns[j].ChangesZeroPage(reg2 + 1)) + j++; + if (j + 1 < mIns.Size() && + mIns[j + 0].mType == ASMIT_STA && mIns[j + 0].mMode == ASMIM_ZERO_PAGE && + mIns[j + 1].mType == ASMIT_STA && mIns[j + 1].mMode == ASMIM_ZERO_PAGE) + { + int j1 = -1, j2 = -1; + + if (mIns[j + 0].mAddress == reg1 + 1 && mIns[j + 1].mAddress == reg2 + 1) + { + j1 = j + 0; j2 = j + 1; + } + else if (mIns[j + 0].mAddress == reg2 + 1 && mIns[j + 1].mAddress == reg1 + 1) + { + j1 = j + 1; j2 = j + 0; + } + + if (j1 >= 0) + { + ExpandingArray ranges1, ranges2; + + nproc->ResetPatched(); + if (ForwardFindLiveRange(this, j + 2, reg1, true, ranges1)) + { + nproc->ResetPatched(); + if (ForwardFindLiveRange(this, j + 2, reg1 + 1, true, ranges2)) + { + if (CanReplaceRegInLiveRange(ranges1, reg1, reg2, true) && + CanReplaceRegInLiveRange(ranges2, reg1 + 1, reg2 + 1, true)) + { + ReplaceRegInLiveRange(ranges1, reg1, reg2, true); + ReplaceRegInLiveRange(ranges2, reg1 + 1, reg2 + 1, true); + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[j1 ].mType = ASMIT_NOP; mIns[j1 ].mMode = ASMIM_IMPLIED; + changed = true; + } + } + } + + if (!changed) + { + ExpandingArray ranges1, ranges2; + + nproc->ResetPatched(); + if (ForwardFindLiveRange(this, j + 2, reg2, true, ranges1)) + { + nproc->ResetPatched(); + if (ForwardFindLiveRange(this, j + 2, reg2 + 1, true, ranges2)) + { + if (CanReplaceRegInLiveRange(ranges1, reg2, reg1, true) && + CanReplaceRegInLiveRange(ranges2, reg2 + 1, reg1 + 1, true)) + { + ReplaceRegInLiveRange(ranges1, reg2, reg1, true); + ReplaceRegInLiveRange(ranges2, reg2 + 1, reg1 + 1, true); + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[j2 ].mType = ASMIT_NOP; mIns[j2 ].mMode = ASMIM_IMPLIED; + changed = true; + } + } + } + } + } + } + } +#endif + } + } + + if (mTrueJump && mTrueJump->ReplaceFinalZeroPageUse(nproc)) + changed = true; + if (mFalseJump && mFalseJump->ReplaceFinalZeroPageUse(nproc)) + changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::Check16BitSum(const NativeCodeBasicBlock* block, int origin, int at, int reg) +{ + if (!mEntryRequiredRegs.Size()) + return false; + + if (mPatched) + return true; + + if (at == 0) + { + if (!mEntryRequiredRegs[reg]) + return true; + + mPatched = true; + } + + for (int i = at; i < mIns.Size(); i++) + { + if (mIns[i].ReferencesZeroPage(reg)) + { + if (block == this && i == origin) + return true; + if (mIns[i].mType == ASMIT_JSR && mIns[i].mLinkerObject && !mIns[i].mLinkerObject->mProc) + return false; + if (mIns[i].UsesZeroPage(reg)) + return false; + if (mIns[i].ChangesZeroPage(reg)) + return true; + } + } + + if (mTrueJump && !mTrueJump->Check16BitSum(block, origin, 0, reg)) + return false; + if (mFalseJump && !mFalseJump->Check16BitSum(block, origin, 0, reg)) + return false; + + return true; +} + +bool NativeCodeBasicBlock::Simplify16BitSum8BitRange(void) +{ + bool changed = false; + if (!mVisited) + { + mVisited = true; + + for (int i = 0; i + 5 < mIns.Size(); i++) + { + if (mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && + mIns[i + 4].mType == ASMIT_LDA && + mIns[i + 5].mType == ASMIT_ADC && + mIns[i + 1].mMaxVal + mIns[i + 2].mAddress < 256) + { + if (mIns[i + 5].mMode == ASMIM_IMMEDIATE && mIns[i + 5].mAddress == 0) + { + mIns[i + 5].mType = ASMIT_NOP; mIns[i + 5].mMode = ASMIM_IMPLIED; + changed = true; + } + else if (mIns[i + 4].mMode == ASMIM_IMMEDIATE && mIns[i + 4].mAddress == 0) + { + mIns[i + 4].mType = ASMIT_NOP; mIns[i + 4].mMode = ASMIM_IMPLIED; + mIns[i + 5].mType = ASMIT_LDA; + changed = true; + } + } + } + + if (mTrueJump && mTrueJump->Simplify16BitSum8BitRange()) changed = true; + if (mFalseJump && mFalseJump->Simplify16BitSum8BitRange()) changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::EliminateUpper16BitSum(NativeCodeProcedure* nproc) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + CheckLive(); + + for (int i = 0; i < mIns.Size(); i++) + { + if (i + 2 < mIns.Size() && + mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + (mIns[i + 1].mType == ASMIT_ADC || mIns[i + 1].mType == ASMIT_SBC) && + mIns[i + 2].mType == ASMIT_STA && mIns[i + 2].SameEffectiveAddress(mIns[i + 0]) && !(mIns[i + 2].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C | LIVE_CPU_REG_Z)) && + mIns[i + 2].mAddress != BC_REG_LOCALS + 1 && mIns[i + 2].mAddress != BC_REG_STACK + 1) + { + nproc->ResetPatched(); + if (Check16BitSum(this, i, i + 3, mIns[i + 2].mAddress)) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + changed = true; + } + } + else if ((mIns[i + 0].mType == ASMIT_INC || mIns[i + 0].mType == ASMIT_DEC) && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + !(mIns[i + 0].mLive & LIVE_CPU_REG_Z) && + mIns[i + 0].mAddress != BC_REG_LOCALS + 1 && mIns[i + 0].mAddress != BC_REG_STACK + 1) + { + nproc->ResetPatched(); + if (Check16BitSum(this, i, i + 1, mIns[i + 0].mAddress)) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + changed = true; + } + } + } + + if (mTrueJump && mTrueJump->EliminateUpper16BitSum(nproc)) + changed = true; + if (mFalseJump && mFalseJump->EliminateUpper16BitSum(nproc)) + changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::Propagate16BitHighSum(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + for (int i = 0; i + 5 < mIns.Size(); i++) + { + if (mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && !(mIns[i + 0].mLive & LIVE_MEM) && + mIns[i + 1].mType == ASMIT_STA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + mIns[i + 2].mType == ASMIT_CLC && + mIns[i + 3].mType == ASMIT_LDA && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && mIns[i + 3].mAddress == mIns[i + 0].mAddress + 1 && !(mIns[i + 3].mLive & LIVE_MEM) && + mIns[i + 4].mType == ASMIT_ADC && mIns[i + 4].mMode == ASMIM_IMMEDIATE && + mIns[i + 5].mType == ASMIT_STA && mIns[i + 5].mMode == ASMIM_ZERO_PAGE && mIns[i + 5].mAddress == mIns[i + 1].mAddress + 1) + { + bool success = false; + + int taddr = mIns[i + 1].mAddress, saddr = mIns[i + 0].mAddress; + + int j = i + 6; + while (j < mIns.Size()) + { + if (mIns[j].mType == ASMIT_JSR) + break; + else if (mIns[j].ChangesZeroPage(saddr) || mIns[j].ChangesZeroPage(saddr + 1)) + break; + else if (mIns[j].ChangesZeroPage(taddr) || mIns[j].ChangesZeroPage(taddr + 1)) + break; + else if (mIns[j].UsesZeroPage(taddr + 1) && !(mIns[j].mLive & LIVE_MEM)) + { + success = true; + break; + } + j++; + } + + if (success) + { + mIns[i + 5].mAddress = saddr + 1; + for (int k = i + 6; k <= j; k++) + { + if (mIns[k].mMode == ASMIM_ZERO_PAGE) + { + if (mIns[k].mAddress == taddr) + mIns[k].mAddress = saddr; + else if (mIns[k].mAddress == taddr + 1) + mIns[k].mAddress = saddr + 1; + } + else if (mIns[k].mMode == ASMIM_INDIRECT_Y) + { + if (mIns[k].mAddress == taddr) + mIns[k].mAddress = saddr; + } + } + changed = true; + } + } + } + + if (mTrueJump && mTrueJump->Propagate16BitHighSum()) + changed = true; + if (mFalseJump && mFalseJump->Propagate16BitHighSum()) + changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::ForwardReplaceZeroPage(int at, int from, int to, bool pair) +{ + bool changed = false; + + if (!mPatched) + { + mPatched = true; + + for (int i = at; i < mIns.Size(); i++) + { + if (mIns[i].mMode == ASMIM_ZERO_PAGE || mIns[i].mMode == ASMIM_INDIRECT_Y) + { + if (mIns[i].mAddress == from) + { + mIns[i].mAddress = to; + changed = true; + } + } + } + + if (mTrueJump && mTrueJump->ForwardReplaceZeroPage(0, from, to, pair)) + changed = true; + if (mFalseJump && mFalseJump->ForwardReplaceZeroPage(0, from, to, pair)) + changed = true; + + if (mEntryRequiredRegs[from]) + mEntryRequiredRegs += to; + } + + return changed; +} + + + +bool NativeCodeBasicBlock::CanZeroPageCopyUp(int at, int from, int to, bool diamond) +{ + bool full = at == mIns.Size(); + + mPatchChecked = true; + + while (at > 0) + { + at--; + + if (mIns[at].mType == ASMIT_JSR) + return false; + + if (mIns[at].mMode == ASMIM_ZERO_PAGE && mIns[at].mAddress == to) + return false; + + if (mIns[at].mMode == ASMIM_INDIRECT_Y) + { + if (mIns[at].mAddress == from || mIns[at].mAddress == from - 1 || + mIns[at].mAddress == to || mIns[at].mAddress == to - 1) + return false; + } + + if (mIns[at].mMode == ASMIM_ZERO_PAGE && mIns[at].mAddress == from) + { + if (mIns[at].mType == ASMIT_STA || mIns[at].mType == ASMIT_STX || mIns[at].mType == ASMIT_STY) + { + if (!diamond) + return true; + + if (mEntryRequiredRegs[from]) + { + if (mEntryRequiredRegs[to]) + return false; + + int i = 0; + while (i < at && !mIns[i].ReferencesZeroPage(from)) + { + if (mIns[i].ReferencesZeroPage(to)) + return false; + i++; + } + + if (mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && !(mIns[i + 0].mLive & LIVE_MEM) && + mIns[i + 1].mType == ASMIT_STA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && mIns[i + 1].mAddress == to) + { + return mEntryBlocks[0]->CanZeroPageCopyUp(mEntryBlocks[0]->mIns.Size(), from, to, false); + } + + return false; + } + + return mEntryBlocks[0]->CanZeroPageCopyUp(mEntryBlocks[0]->mIns.Size(), from, to, false); + } + } + } + + if (mLoopHead && (mTrueJump != this && mFalseJump != this || !full)) + return false; + + if (mEntryBlocks.Size() == 1) + { + if (mEntryBlocks[0]->mFalseJump) + { + if (this == mEntryBlocks[0]->mTrueJump) + { + if (mEntryBlocks[0]->mFalseJump->mEntryRequiredRegs[from] || mEntryBlocks[0]->mFalseJump->mEntryRequiredRegs[to]) + return false; + } + else + { + if (mEntryBlocks[0]->mTrueJump->mEntryRequiredRegs[from] || mEntryBlocks[0]->mTrueJump->mEntryRequiredRegs[to]) + return false; + } + } + + return mEntryBlocks[0]->CanZeroPageCopyUp(mEntryBlocks[0]->mIns.Size(), from, to, false); + } + else if (mEntryBlocks.Size() == 2) + { + if (mEntryBlocks[0] == this) + return mEntryBlocks[1]->CanZeroPageCopyUp(mEntryBlocks[1]->mIns.Size(), from, to, false); + else if (mEntryBlocks[1] == this) + return mEntryBlocks[0]->CanZeroPageCopyUp(mEntryBlocks[0]->mIns.Size(), from, to, false); + else if ( + mEntryBlocks[0]->mTrueJump == this && mEntryBlocks[0]->mFalseJump == mEntryBlocks[1] && (!mEntryBlocks[1]->mFalseJump || mEntryBlocks[1]->mFalseJump == mEntryBlocks[1] || mEntryBlocks[1]->mTrueJump == mEntryBlocks[1]) || + mEntryBlocks[0]->mFalseJump == this && mEntryBlocks[0]->mTrueJump == mEntryBlocks[1] && (!mEntryBlocks[1]->mFalseJump || mEntryBlocks[1]->mFalseJump == mEntryBlocks[1] || mEntryBlocks[1]->mTrueJump == mEntryBlocks[1])) + return mEntryBlocks[1]->CanZeroPageCopyUp(mEntryBlocks[1]->mIns.Size(), from, to, true); + else if ( + mEntryBlocks[1]->mTrueJump == this && mEntryBlocks[1]->mFalseJump == mEntryBlocks[0] && (!mEntryBlocks[0]->mFalseJump || mEntryBlocks[0]->mFalseJump == mEntryBlocks[0] || mEntryBlocks[0]->mTrueJump == mEntryBlocks[0]) || + mEntryBlocks[1]->mFalseJump == this && mEntryBlocks[1]->mTrueJump == mEntryBlocks[0] && (!mEntryBlocks[0]->mFalseJump || mEntryBlocks[0]->mFalseJump == mEntryBlocks[0] || mEntryBlocks[0]->mTrueJump == mEntryBlocks[0])) + return mEntryBlocks[0]->CanZeroPageCopyUp(mEntryBlocks[0]->mIns.Size(), from, to, true); + } + + return false; +} + +bool NativeCodeBasicBlock::ShortcutZeroPageCopyUp(NativeCodeProcedure* nproc) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + for (int i = 0; i + 1 < mIns.Size(); i++) + { + if (mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && !(mIns[i + 0].mLive & LIVE_MEM) && + mIns[i + 1].mType == ASMIT_STA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && !(mIns[i + 1].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z))) + { + nproc->ResetPatched(); + if (CanZeroPageCopyUp(i, mIns[i + 0].mAddress, mIns[i + 1].mAddress, false)) + { + BackwardReplaceZeroPage(i, mIns[i + 0].mAddress, mIns[i + 1].mAddress, false); + changed = true; + + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + } + } + } + + if (mTrueJump && mTrueJump->ShortcutZeroPageCopyUp(nproc)) + changed = true; + if (mFalseJump && mFalseJump->ShortcutZeroPageCopyUp(nproc)) + changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::IsSameRegisterSource(const NativeCodeInstruction& rins, int nloops) const +{ + for (int i = mIns.Size() - 1; i >= 0; i--) + { + const NativeCodeInstruction& ins(mIns[i]); + if (rins.IsSame(ins)) + return true; +#if 1 + if (rins.mType == ASMIT_LDY && ins.mType == ASMIT_STY && rins.SameEffectiveAddress(ins)) + return true; + if (rins.mType == ASMIT_LDX && ins.mType == ASMIT_STX && rins.SameEffectiveAddress(ins)) + return true; + if (rins.mType == ASMIT_LDA && ins.mType == ASMIT_STA && rins.SameEffectiveAddress(ins)) + return true; +#endif + if (rins.mType == ASMIT_LDY && ins.ChangesYReg()) + return false; + if (rins.mType == ASMIT_LDX && ins.ChangesXReg()) + return false; + if (rins.mType == ASMIT_LDA && ins.ChangesAccu()) + return false; + if (rins.MayBeChangedOnAddress(ins)) + return false; + } + + if (mEntryBlocks.Size() == 0) + return false; + + if (mLoopHead) + { + if (nloops) + nloops--; + else + return false; + } + + for (int i = 0; i < mEntryBlocks.Size(); i++) + if (!mEntryBlocks[i]->IsSameRegisterSource(rins, nloops)) + return false; + return true; +} + +bool NativeCodeBasicBlock::FindInitialLoadA(NativeCodeInstruction *& ins) +{ + for (int i = 0; i < mIns.Size(); i++) + { + if (mIns[i].mType == ASMIT_LDA && !(mIns[i].mLive & LIVE_CPU_REG_Z)) + { + if (mIns[i].mMode == ASMIM_IMMEDIATE || mIns[i].mMode == ASMIM_IMMEDIATE_ADDRESS || + mIns[i].mMode == ASMIM_ZERO_PAGE && !ChangesZeroPage(mIns[i].mAddress, 0, i)) + { + ins = &mIns[i]; + return true; + } + } + if (mIns[i].ChangesAccu()) + return false; + } + return false; +} + +bool NativeCodeBasicBlock::FindInitialLoadX(NativeCodeInstruction *& ins) +{ + for (int i = 0; i < mIns.Size(); i++) + { + if (mIns[i].mType == ASMIT_LDX && !(mIns[i].mLive & LIVE_CPU_REG_Z)) + { + if (mIns[i].mMode == ASMIM_IMMEDIATE || mIns[i].mMode == ASMIM_IMMEDIATE_ADDRESS || + mIns[i].mMode == ASMIM_ZERO_PAGE && !ChangesZeroPage(mIns[i].mAddress, 0, i)) + { + ins = &mIns[i]; + return true; + } + } + if (mIns[i].ChangesXReg()) + return false; + } + return false; +} + +bool NativeCodeBasicBlock::FindInitialLoadY(NativeCodeInstruction *& ins) +{ + for (int i = 0; i < mIns.Size(); i++) + { + if (mIns[i].mType == ASMIT_LDY && !(mIns[i].mLive & LIVE_CPU_REG_Z)) + { + if (mIns[i].mMode == ASMIM_IMMEDIATE || mIns[i].mMode == ASMIM_IMMEDIATE_ADDRESS || + mIns[i].mMode == ASMIM_ZERO_PAGE && !ChangesZeroPage(mIns[i].mAddress, 0, i)) + { + ins = &mIns[i]; + return true; + } + } + if (mIns[i].ChangesYReg()) + return false; + } + return false; +} + +bool NativeCodeBasicBlock::HoistCommonLoads(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + if (mFalseJump && mTrueJump->mNumEntries == 1 && mFalseJump->mNumEntries == 1) + { + NativeCodeInstruction * tins = nullptr, * fins = nullptr; + if (!mExitRequiredRegs[CPU_REG_A] && mTrueJump->FindInitialLoadA(tins) && mFalseJump->FindInitialLoadA(fins) && tins->IsSame(*fins) && !(tins->mFlags & NCIF_VOLATILE)) + { + int i = mIns.Size(); + while (i > 0 && (mIns[i - 1].mLive & LIVE_CPU_REG_Z)) + i--; + if (!ReferencesAccu(i) && (i > 0 || !mEntryRequiredRegs[CPU_REG_Z])) + { + mIns.Insert(i, *tins); + tins->mType = ASMIT_NOP; tins->mMode = ASMIM_IMPLIED; + fins->mType = ASMIT_NOP; fins->mMode = ASMIM_IMPLIED; + changed = true; + } + } + if (!mExitRequiredRegs[CPU_REG_X] && mTrueJump->FindInitialLoadX(tins) && mFalseJump->FindInitialLoadX(fins) && tins->IsSame(*fins) && !(tins->mFlags & NCIF_VOLATILE)) + { + int i = mIns.Size(); + while (i > 0 && (mIns[i - 1].mLive & LIVE_CPU_REG_Z)) + i--; + if (!ReferencesXReg(i) && (i > 0 || !mEntryRequiredRegs[CPU_REG_Z])) + { + mIns.Insert(i, *tins); + tins->mType = ASMIT_NOP; tins->mMode = ASMIM_IMPLIED; + fins->mType = ASMIT_NOP; fins->mMode = ASMIM_IMPLIED; + changed = true; + } + } + if (!mExitRequiredRegs[CPU_REG_Y] && mTrueJump->FindInitialLoadY(tins) && mFalseJump->FindInitialLoadY(fins) && tins->IsSame(*fins) && !(tins->mFlags & NCIF_VOLATILE)) + { + int i = mIns.Size(); + while (i > 0 && (mIns[i - 1].mLive & LIVE_CPU_REG_Z)) + i--; + if (!ReferencesYReg(i) && (i > 0 || !mEntryRequiredRegs[CPU_REG_Z])) + { + mIns.Insert(i, *tins); + tins->mType = ASMIT_NOP; tins->mMode = ASMIM_IMPLIED; + fins->mType = ASMIT_NOP; fins->mMode = ASMIM_IMPLIED; + changed = true; + } + } + + } + + if (mTrueJump && mTrueJump->HoistCommonLoads()) + changed = true; + if (mFalseJump && mFalseJump->HoistCommonLoads()) + changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::BackwardValuePropagation(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + bool achanged = false, xchanged = false, ychanged = false; + + for (int i = 0; i < mIns.Size(); i++) + { + NativeCodeInstruction& ins(mIns[i]); + + if (i + 1 < mIns.Size()) + { + NativeCodeInstruction& nins(mIns[i + 1]); + + if (i + 1 < mIns.Size() && (ins.mType == ASMIT_INC || ins.mType == ASMIT_DEC) && ins.mMode == ASMIM_ZERO_PAGE && !xchanged && nins.mType == ASMIT_LDX && nins.SameEffectiveAddress(ins)) + { + if (!ChangesZeroPage(ins.mAddress, 0, i)) + { + + int j = 0; + while (j < mEntryBlocks.Size() && mEntryBlocks[j]->IsSameRegisterSource(nins, 2)) + j++; + if (j == mEntryBlocks.Size()) + { + if (ins.mType == ASMIT_INC) + ins.mType = ASMIT_INX; + else + ins.mType = ASMIT_DEX; + ins.mMode = ASMIM_IMPLIED; + nins.mType = ASMIT_STX; + changed = true; + } + } + } + } + + if (ins.ChangesYReg()) + ychanged = true; + if (ins.ChangesXReg()) + xchanged = true; + if (ins.ChangesAccu()) + achanged = true; + } + + if (mTrueJump && mTrueJump->BackwardValuePropagation()) + changed = true; + if (mFalseJump && mFalseJump->BackwardValuePropagation()) + changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::PartialBackwardValuePropagation(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + if (!mLoopHead && mEntryBlocks.Size() > 1) + { + bool afail = mEntryRequiredRegs[CPU_REG_A]; + bool xfail = mEntryRequiredRegs[CPU_REG_X]; + bool yfail = mEntryRequiredRegs[CPU_REG_Y]; + + int i = 0; + while (i < mIns.Size() && !(afail && xfail && yfail)) + { + NativeCodeInstruction& ins(mIns[i]); + + if ((!yfail && ins.mType == ASMIT_LDY || !xfail && ins.mType == ASMIT_LDX || !afail && ins.mType == ASMIT_LDA) + && (ins.mMode == ASMIM_IMMEDIATE || ins.mMode == ASMIM_IMMEDIATE_ADDRESS || ins.mMode == ASMIM_ZERO_PAGE) + && !(ins.mLive & LIVE_CPU_REG_Z) && !(ins.mFlags & NCIF_VOLATILE)) + { + if (ins.mMode != ASMIM_ZERO_PAGE || !ChangesZeroPage(ins.mAddress, 0, i)) + { + int j = 0; + while (j < mEntryBlocks.Size() && !mEntryBlocks[j]->IsSameRegisterSource(ins)) + j++; + if (j < mEntryBlocks.Size()) + { + NativeCodeBasicBlock* pblock = mProc->AllocateBlock(); + pblock->mEntryRequiredRegs = mEntryRequiredRegs; + pblock->mExitRequiredRegs = mEntryRequiredRegs; + pblock->mIns.Push(ins); + pblock->Close(ins.mIns, this, nullptr, ASMIT_JMP); + while (j > 0) + { + j--; + NativeCodeBasicBlock* eblock = mEntryBlocks[j]; + mEntryBlocks.Remove(j); + mNumEntries--; + if (eblock->mTrueJump == this) + eblock->mTrueJump = pblock; + else if (eblock->mFalseJump == this) + eblock->mFalseJump = pblock; + pblock->mEntryBlocks.Push(eblock); + pblock->mNumEntries++; + } + j++; + while (j < mEntryBlocks.Size()) + { + if (mEntryBlocks[j]->IsSameRegisterSource(ins)) + j++; + else + { + NativeCodeBasicBlock* eblock = mEntryBlocks[j]; + mEntryBlocks.Remove(j); + mNumEntries--; + if (eblock->mTrueJump == this) + eblock->mTrueJump = pblock; + else if (eblock->mFalseJump == this) + eblock->mFalseJump = pblock; + pblock->mEntryBlocks.Push(eblock); + pblock->mNumEntries++; + } + } + + if (ins.mType == ASMIT_LDA) + afail = true; + if (ins.mType == ASMIT_LDX) + xfail = true; + if (ins.mType == ASMIT_LDY) + yfail = true; + + ins.mType = ASMIT_NOP; + ins.mMode = ASMIM_IMPLIED; + mEntryBlocks.Push(pblock); + mNumEntries++; + changed = true; + } + } + } + + if (ins.ChangesYReg()) + yfail = true; + if (ins.ChangesXReg()) + xfail = true; + if (ins.ChangesAccu()) + afail = true; + + i++; + } + } +#if 1 + for (int i = 0; i < mIns.Size(); i++) + { + if ((mIns[i].mType == ASMIT_LDA || mIns[i].mType == ASMIT_LDX || mIns[i].mType == ASMIT_LDY) && mIns[i].mMode == ASMIM_ABSOLUTE && !(mIns[i].mFlags & NCIF_VOLATILE)) + { + int val = FindImmediateGlobalStore(i, mIns[i]); + if (val >= 0) + { + mIns[i].mMode = ASMIM_IMMEDIATE; + mIns[i].mAddress = val; + changed = true; + } + } + } +#endif + if (mTrueJump && mTrueJump->PartialBackwardValuePropagation()) + changed = true; + if (mFalseJump && mFalseJump->PartialBackwardValuePropagation()) + changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::BackwardReplaceZeroPage(int at, int from, int to, bool diamond) +{ + bool changed = false; + + if (!mPatched) + { + mPatched = true; + + assert(mPatchChecked); + + if (at == mIns.Size()) + mExitRequiredRegs += to; + + bool done = false; + while (at > 0) + { + at--; + if (mIns[at].mMode == ASMIM_ZERO_PAGE && mIns[at].mAddress == from) + { + mIns[at].mAddress = to; + changed = true; + if (mIns[at].mType == ASMIT_STA || mIns[at].mType == ASMIT_STX || mIns[at].mType == ASMIT_STY) + { + done = true; + break; + } + } + } + + mEntryRequiredRegs += to; + + if (diamond) + { + if (mEntryRequiredRegs[from]) + { + int i = 0; + while (i < at && !mIns[i].ReferencesZeroPage(from)) + i++; + + if (i < at) + { + mIns[i].mAddress = to; + changed = true; + } + } + + if (mEntryBlocks[0]->BackwardReplaceZeroPage(mEntryBlocks[0]->mIns.Size(), from, to, false)) + changed = true; + } + else if (!done) + { + if (mEntryBlocks.Size() == 1) + { + if (mEntryBlocks[0]->BackwardReplaceZeroPage(mEntryBlocks[0]->mIns.Size(), from, to, false)) + changed = true; + } + else if (mEntryBlocks.Size() == 2) + { + if (mEntryBlocks[0]->mTrueJump == this && mEntryBlocks[0]->mFalseJump == mEntryBlocks[1] && (!mEntryBlocks[1]->mFalseJump || mEntryBlocks[1]->mFalseJump == mEntryBlocks[1] || mEntryBlocks[1]->mTrueJump == mEntryBlocks[1]) || + mEntryBlocks[0]->mFalseJump == this && mEntryBlocks[0]->mTrueJump == mEntryBlocks[1] && (!mEntryBlocks[1]->mFalseJump || mEntryBlocks[1]->mFalseJump == mEntryBlocks[1] || mEntryBlocks[1]->mTrueJump == mEntryBlocks[1])) + { + if (mEntryBlocks[1]->BackwardReplaceZeroPage(mEntryBlocks[1]->mIns.Size(), from, to, true)) + changed = true; + } + else if (mEntryBlocks[1]->mTrueJump == this && mEntryBlocks[1]->mFalseJump == mEntryBlocks[0] && (!mEntryBlocks[0]->mFalseJump || mEntryBlocks[0]->mFalseJump == mEntryBlocks[0] || mEntryBlocks[0]->mTrueJump == mEntryBlocks[0]) || + mEntryBlocks[1]->mFalseJump == this && mEntryBlocks[1]->mTrueJump == mEntryBlocks[0] && (!mEntryBlocks[0]->mFalseJump || mEntryBlocks[0]->mFalseJump == mEntryBlocks[0] || mEntryBlocks[0]->mTrueJump == mEntryBlocks[0])) + { + if (mEntryBlocks[0]->BackwardReplaceZeroPage(mEntryBlocks[0]->mIns.Size(), from, to, true)) + changed = true; + } + } + } + } + + return changed; +} + +bool NativeRegisterSum16Info::operator==(const NativeRegisterSum16Info& ri) const +{ + if (mSrcL != ri.mSrcL || mSrcH != ri.mSrcH || mAddL != ri.mAddL || mAddH != ri.mAddH || mDstL != ri.mDstL || mDstH != ri.mDstH) + return false; + + if (mImmediate) + return mAddress == ri.mAddress && mLinkerObject == ri.mLinkerObject; + else + return true; +} + +bool NativeRegisterSum16Info::operator!=(const NativeRegisterSum16Info& ri) const +{ + return !(*this == ri); +} + +bool NativeCodeBasicBlock::Propagate16BitSum(const ExpandingArray & cinfo) +{ + bool changed = false; + + if (!mVisited) + { + if (mLoopHead) + mRSumInfos.SetSize(0); + else + { + if (mNumEntered == 0) + mRSumInfos = cinfo; + else + { + int i = 0; + while (i < mRSumInfos.Size()) + { + if (cinfo.Contains(mRSumInfos[i])) + i++; + else + mRSumInfos.Remove(i); + } + } + + mNumEntered++; + if (mNumEntered < mNumEntries) + return false; + } + + mVisited = true; + + for (int i = 0; i < mIns.Size(); i++) + { + NativeRegisterSum16Info info; + + if (Check16BitSum(i, info)) + { + for (int j = 0; j < mRSumInfos.Size(); j++) + { + if (info.mSrcL->mAddress == mRSumInfos[j].mDstL->mAddress && info.mSrcH->mAddress == mRSumInfos[j].mDstH->mAddress) + { + if (info.mImmediate && mRSumInfos[j].mImmediate) + { + if (!info.mLinkerObject && !mRSumInfos[j].mLinkerObject) + { + info.mAddress += mRSumInfos[j].mAddress; + if (info.mSrcL->mType == ASMIT_LDA) + info.mAddL->mType = ASMIT_ADC; + else + info.mSrcL->mType = ASMIT_ADC; + info.mAddL->mAddress = info.mAddress & 0xff; + info.mAddH->mAddress = info.mAddress >> 8; + info.mSrcL->mAddress = mRSumInfos[j].mSrcL->mAddress; + info.mSrcL->mMinVal = mRSumInfos[j].mSrcL->mMinVal; + info.mSrcL->mMaxVal = mRSumInfos[j].mSrcL->mMaxVal; + info.mSrcH->mAddress = mRSumInfos[j].mSrcH->mAddress; + info.mSrcH->mMinVal = mRSumInfos[j].mSrcH->mMinVal; + info.mSrcH->mMaxVal = mRSumInfos[j].mSrcH->mMaxVal; + info.mSrcH->mMode = mRSumInfos[j].mSrcH->mMode; + changed = true; + } + else if (!mRSumInfos[j].mLinkerObject) + { + info.mAddress += mRSumInfos[j].mAddress; + if (info.mSrcL->mType == ASMIT_LDA) + info.mAddL->mType = ASMIT_ADC; + else + info.mSrcL->mType = ASMIT_ADC; + info.mAddL->mAddress = info.mAddress; + info.mAddH->mAddress = info.mAddress; + info.mSrcL->mAddress = mRSumInfos[j].mSrcL->mAddress; + info.mSrcH->mAddress = mRSumInfos[j].mSrcH->mAddress; + info.mSrcH->mMode = mRSumInfos[j].mSrcH->mMode; + changed = true; + } + else if (!info.mLinkerObject) + { + info.mAddress += mRSumInfos[j].mAddress; + info.mLinkerObject = mRSumInfos[j].mLinkerObject; + if (info.mSrcL->mType == ASMIT_LDA) + info.mAddL->mType = ASMIT_ADC; + else + info.mSrcL->mType = ASMIT_ADC; + info.mAddL->mAddress = info.mAddress; + info.mAddL->mLinkerObject = info.mLinkerObject; + info.mAddL->mMode = ASMIM_IMMEDIATE_ADDRESS; + info.mAddL->mFlags = NCIF_LOWER; + info.mAddH->mAddress = info.mAddress; + info.mAddH->mLinkerObject = info.mLinkerObject; + info.mAddH->mMode = ASMIM_IMMEDIATE_ADDRESS; + info.mAddH->mFlags = NCIF_UPPER; + info.mSrcL->mAddress = mRSumInfos[j].mSrcL->mAddress; + info.mSrcH->mAddress = mRSumInfos[j].mSrcH->mAddress; + info.mSrcH->mMode = mRSumInfos[j].mSrcH->mMode; + changed = true; + } + } + } + else if (info.mSrcL->mAddress == mRSumInfos[j].mSrcL->mAddress && info.mSrcH->mAddress == mRSumInfos[j].mSrcH->mAddress) + { + if (info.mAddH->mMode == ASMIM_IMMEDIATE && mRSumInfos[j].mAddH->mMode == ASMIM_IMMEDIATE && + info.mAddL->mMode == ASMIM_ZERO_PAGE && mRSumInfos[j].mAddL->mMode == ASMIM_ZERO_PAGE && info.mAddL->mAddress == mRSumInfos[j].mAddL->mAddress) + { + info.mAddL->mType = ASMIT_NOP; info.mAddL->mMode = ASMIM_IMPLIED; + info.mSrcL->mAddress = mRSumInfos[j].mDstL->mAddress; + info.mSrcH->mAddress = mRSumInfos[j].mDstH->mAddress; + info.mAddH->mAddress -= mRSumInfos[j].mAddH->mAddress; + changed = true; + } + else if (info.mAddL->mMode == ASMIM_IMMEDIATE && info.mAddH->mMode == ASMIM_IMMEDIATE && + mRSumInfos[j].mAddL->mMode == ASMIM_IMMEDIATE && mRSumInfos[j].mAddH->mMode == ASMIM_IMMEDIATE) + { + if (info.mAddress == mRSumInfos[j].mAddress) + { + info.mSrcL->mAddress = mRSumInfos[j].mDstL->mAddress; + info.mSrcL->mMode = mRSumInfos[j].mDstL->mMode; + info.mSrcH->mAddress = mRSumInfos[j].mDstH->mAddress; + info.mSrcH->mMode = mRSumInfos[j].mDstH->mMode; + info.mSrcL->mType = ASMIT_LDA; + info.mSrcH->mType = ASMIT_LDA; + info.mAddL->mType = ASMIT_NOP; info.mAddL->mMode = ASMIM_IMPLIED; + info.mAddH->mType = ASMIT_NOP; info.mAddH->mMode = ASMIM_IMPLIED; + info.mAddress = 0; + changed = true; + } + } + } + } + + if (info.mSrcL->mAddress != info.mDstL->mAddress && (info.mSrcH->mMode == ASMIM_IMMEDIATE || info.mSrcH->mAddress != info.mDstH->mAddress)) + mRSumInfos.Push(info); + } + + int j = 0; + while (j < mRSumInfos.Size()) + { + if (mRSumInfos[j].mDstL != &(mIns[i]) && mRSumInfos[j].mDstH != &(mIns[i])) + { + if ( + mRSumInfos[j].mSrcL->MayBeChangedOnAddress(mIns[i]) || + mRSumInfos[j].mSrcH->MayBeChangedOnAddress(mIns[i]) || + mRSumInfos[j].mAddL->MayBeChangedOnAddress(mIns[i]) || + mRSumInfos[j].mAddH->MayBeChangedOnAddress(mIns[i]) || + mRSumInfos[j].mDstL->MayBeChangedOnAddress(mIns[i]) || + mRSumInfos[j].mDstH->MayBeChangedOnAddress(mIns[i])) + { + mRSumInfos.Remove(j); + } + else + j++; + } + else + j++; + } + } + + if (mTrueJump && mTrueJump->Propagate16BitSum(mRSumInfos)) + changed = true; + if (mFalseJump && mFalseJump->Propagate16BitSum(mRSumInfos)) + changed = true; + } + return changed; +} + +bool NativeCodeBasicBlock::FindAccuExitValue(int& at) +{ + int i = mIns.Size() - 1; + while (i >= 0) + { + if ((mIns[i].mType == ASMIT_LDA || mIns[i].mType == ASMIT_STA) && (mIns[i].mMode == ASMIM_ZERO_PAGE || mIns[i].mMode == ASMIM_ABSOLUTE)) + { + at = i; + i++; + while (i < mIns.Size()) + { + if (mIns[at].MayBeChangedOnAddress(mIns[i])) + return false; + i++; + } + return true; + } + else if (mIns[i].ChangesAccu()) + return false; + i--; + } + return false; +} + +bool NativeCodeBasicBlock::MoveLoadXAbsUpCrossBlock(int at) +{ + int i = at - 1; + while (i >= 0) + { + if ((mIns[i].mType == ASMIT_STA || mIns[i].mType == ASMIT_LDA) && mIns[i].SameEffectiveAddress(mIns[at])) + { + mIns[i].mLive |= LIVE_CPU_REG_A; + i++; + mIns.Insert(i, NativeCodeInstruction(mIns[at].mIns, ASMIT_TAX)); + mIns[at + 1].mType = ASMIT_NOP; + mIns[at + 1].mMode = ASMIM_IMPLIED; + while (i < at + 1) + { + mIns[i].mLive |= LIVE_CPU_REG_X; + i++; + } + + return true; + } + + if (mIns[i].RequiresXReg() || mIns[i].ChangesXReg() || (mIns[i].mLive & LIVE_CPU_REG_X)) + return false; + if (mIns[at].MayBeChangedOnAddress(mIns[i])) + return false; + i--; + } + + if (mEntryBlocks.Size() == 1) + { + NativeCodeBasicBlock* eb = mEntryBlocks[0]; + if (eb->FindAccuExitValue(i) && eb->mIns[i].SameEffectiveAddress(mIns[at])) + { + while (i < eb->mIns.Size()) + { + eb->mIns[i].mLive |= LIVE_CPU_REG_A; + i++; + } + eb->mExitRequiredRegs += CPU_REG_A; + mEntryRequiredRegs += CPU_REG_A; + + mIns.Insert(0, NativeCodeInstruction(mIns[at].mIns, ASMIT_TAX)); + mIns[at + 1].mType = ASMIT_NOP; + mIns[at + 1].mMode = ASMIM_IMPLIED; + i = 0; + while (i < at + 1) + { + mIns[i].mLive |= LIVE_CPU_REG_X; + i++; + } + + return true; + } + } + + return false; +} + + +bool NativeCodeBasicBlock::MoveLoadXUp(int at) +{ + int i = at - 1; + while (i >= 0) + { + if (mIns[i].mType == ASMIT_STA && mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i].mAddress == mIns[at].mAddress) + { + mIns[i].mType = ASMIT_TAX; + mIns[i].mMode = ASMIM_IMPLIED; + mIns[at].mType = ASMIT_NOP; + mIns[at].mMode = ASMIM_IMPLIED; + while (i < at) + { + mIns[i].mLive |= LIVE_CPU_REG_X; + i++; + } + + return true; + } + else if (mIns[i].mType == ASMIT_INC && mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i].mAddress == mIns[at].mAddress) + { + mIns[i].mType = ASMIT_LDX; + mIns[i].mLive |= LIVE_MEM; + mIns[at].mType = ASMIT_INX; + mIns[at].mMode = ASMIM_IMPLIED; + while (i < at) + { + mIns[i].mLive |= LIVE_CPU_REG_X; + i++; + } + + return true; + } + + if (mIns[i].RequiresXReg() || mIns[i].ChangesXReg() || (mIns[i].mLive & LIVE_CPU_REG_X) || mIns[i].UsesZeroPage(mIns[at].mAddress)) + return false; + + i--; + } + + return false; +} + + +bool NativeCodeBasicBlock::MoveStoreXUp(int at) +{ + bool done = false; + + int n = 0, inc = 0;; + int reg = mIns[at].mAddress; + + if (mIns[at].mMode == ASMIM_ZERO_PAGE && !(mIns[at].mLive & LIVE_CPU_REG_X) && at > 0 && (mIns[at - 1].mType == ASMIT_INX || mIns[at - 1].mType == ASMIT_DEX)) + { + bool absa = false; + int k = at - 2; + while (k >= 0 && !mIns[k].ChangesXReg() && !mIns[k].ChangesZeroPage(reg)) + { + if (mIns[k].mMode == ASMIM_ABSOLUTE_X) + absa = true; + k--; + } + if (k >= 0 && absa && mIns[k].mType == ASMIT_LDX && mIns[k].mAddress == reg) + { + if (mIns[at - 1].mType == ASMIT_INX) + mIns[at].mType = ASMIT_INC; + else + mIns[at].mType = ASMIT_DEC; + mIns[at - 1].mType = ASMIT_NOP; + mIns[at - 1].mMode = ASMIM_IMPLIED; + return true; + } + } + + while (at > 0) + { + if (mIns[at - 1].mType == ASMIT_INX) + { + n++; + inc++; + } + else if (mIns[at - 1].mType == ASMIT_DEX) + { + n++; + inc--; + } + else + { + if (mIns[at - 1].mType == ASMIT_LDX || mIns[at - 1].mType == ASMIT_TAX || mIns[at - 1].mType == ASMIT_TXA) + return done; + else if (mIns[at - 1].ChangesXReg() || mIns[at - 1].mType == ASMIT_STX) + return done; + else if (mIns[at + n].mMode == ASMIM_ZERO_PAGE) + { + if ((mIns[at - 1].mMode == ASMIM_ZERO_PAGE || mIns[at - 1].mMode == ASMIM_INDIRECT_Y) && mIns[at - 1].mAddress == reg) + return done; + if (mIns[at - 1].mMode == ASMIM_INDIRECT_Y && mIns[at - 1].mAddress + 1 == reg) + return done; + } + else if (mIns[at - 1].MayBeSameAddress(mIns[at + n], true)) + return done; + + if (mIns[at - 1].mMode == ASMIM_ABSOLUTE_X && inc) + { + if (inc < 0 || !mIns[at - 1].mLinkerObject || mIns[at - 1].mLinkerObject->mSize > 255) + return done; + } + + if (n > 0 && mIns[at + n].mLive & LIVE_CPU_REG_Z) + { + if (mIns[at - 1].ChangesZFlag()) + return done; + mIns[at - 1].mLive |= LIVE_CPU_REG_Z; + } + + int live = mIns[at - 1].mLive; + mIns[at + n].mLive |= LIVE_CPU_REG_X; + + NativeCodeInstruction ins = mIns[at - 1]; + if (ins.mMode == ASMIM_ABSOLUTE_X) + ins.mAddress -= inc; + + if (ins.RequiresCarry()) live |= LIVE_CPU_REG_C; + if (ins.RequiresYReg()) live |= LIVE_CPU_REG_Y; + if (ins.RequiresAccu()) live |= LIVE_CPU_REG_A; + + for (int i = 0; i <= n; i++) + { + mIns[at + i].mLive |= live; + mIns[at - 1 + i] = mIns[at + i]; + } + + mIns[at + n] = ins; + + if (ins.mType != ASMIT_STY) + done = true; + } + at--; + } + + return done; +} + +bool NativeCodeBasicBlock::MoveLoadYUp(int at) +{ + int i = at - 1; + while (i >= 0) + { + if (mIns[i].mType == ASMIT_STA && mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i].mAddress == mIns[at].mAddress) + { + mIns[i].mType = ASMIT_TAY; + mIns[i].mMode = ASMIM_IMPLIED; + mIns[at].mType = ASMIT_NOP; + mIns[at].mMode = ASMIM_IMPLIED; + while (i < at) + { + mIns[i].mLive |= LIVE_CPU_REG_Y; + i++; + } + + return true; + } + else if (mIns[i].mType == ASMIT_INC && mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i].mAddress == mIns[at].mAddress) + { + mIns[i].mType = ASMIT_LDY; + mIns[i].mLive |= LIVE_MEM; + mIns[at].mType = ASMIT_INY; + mIns[at].mMode = ASMIM_IMPLIED; + while (i < at) + { + mIns[i].mLive |= LIVE_CPU_REG_Y; + i++; + } + + return true; + } + + if (mIns[i].RequiresYReg() || mIns[i].ChangesYReg() || (mIns[i].mLive & LIVE_CPU_REG_Y) || mIns[i].UsesZeroPage(mIns[at].mAddress)) + return false; + + i--; + } + + return false; +} + + +bool NativeCodeBasicBlock::MoveStoreYUp(int at) +{ + bool done = false; + + while (at > 0) + { + if (mIns[at - 1].ChangesYReg() || mIns[at - 1].mType == ASMIT_STY) + return done; + if (mIns[at].mMode == ASMIM_ZERO_PAGE) + { + if ((mIns[at - 1].mMode == ASMIM_ZERO_PAGE || mIns[at - 1].mMode == ASMIM_INDIRECT_Y) && mIns[at - 1].mAddress == mIns[at].mAddress) + return done; + if (mIns[at - 1].mMode == ASMIM_INDIRECT_Y && mIns[at - 1].mAddress + 1 == mIns[at].mAddress) + return done; + } + else if (mIns[at - 1].MayBeSameAddress(mIns[at], true)) + return done; + + mIns[at].mLive |= mIns[at - 1].mLive; + mIns[at].mLive |= LIVE_CPU_REG_Y; + + NativeCodeInstruction ins = mIns[at - 1]; + + if (ins.RequiresXReg()) mIns[at].mLive |= LIVE_CPU_REG_X; + if (ins.RequiresAccu()) mIns[at].mLive |= LIVE_CPU_REG_A; + if (ins.RequiresCarry()) mIns[at].mLive |= LIVE_CPU_REG_C; + + mIns[at - 1] = mIns[at]; + mIns[at] = ins; + at--; + + if (ins.mType != ASMIT_STX) + done = true; + } + + return done; +} + +bool NativeCodeBasicBlock::MoveStoreHighByteDown(int at) +{ + int i = at + 4; + while (i + 1 < mIns.Size()) + { + if (mIns[i].mLive & LIVE_CPU_REG_Y) + return false; + if (mIns[i].ChangesZeroPage(mIns[at + 2].mAddress) || mIns[i].ChangesZeroPage(mIns[at + 2].mAddress + 1) || mIns[i].ChangesZeroPage(mIns[at + 3].mAddress)) + return false; + if (mIns[i].UsesZeroPage(mIns[at + 3].mAddress)) + return false; + if (mIns[i].ChangesGlobalMemory()) + return false; + + if (!(mIns[i].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z))) + { + mIns.Insert(i + 1, mIns[at + 3]); mIns[i + 1].mLive |= mIns[i].mLive; + mIns.Insert(i + 1, mIns[at + 2]); mIns[i + 1].mLive |= mIns[i].mLive; + mIns.Insert(i + 1, mIns[at + 1]); mIns[i + 1].mLive |= mIns[i].mLive; + + mIns[at + 1].mType = ASMIT_NOP; mIns[at + 1].mMode = ASMIM_IMPLIED; // LDY + mIns[at + 2].mType = ASMIT_NOP; mIns[at + 2].mMode = ASMIM_IMPLIED; // LDA (x), y + mIns[at + 3].mType = ASMIT_NOP; mIns[at + 3].mMode = ASMIM_IMPLIED; // STA T + + return true; + } + + i++; + } + + return false; +} + +bool NativeCodeBasicBlock::MoveAddHighByteDown(int at) +{ + int i = at + 4; + while (i + 1 < mIns.Size()) + { + if (mIns[i].mLive & LIVE_CPU_REG_C) + return false; + if (mIns[i].ChangesZeroPage(mIns[at + 1].mAddress) || mIns[i].ChangesZeroPage(mIns[at + 3].mAddress)) + return false; + if (mIns[i].UsesZeroPage(mIns[at + 3].mAddress)) + return false; + + if (!(mIns[i].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z))) + { + for (int j = at + 4; j < i + 1; j++) + mIns[j].mLive |= LIVE_CPU_REG_C; + + mIns.Insert(i + 1, mIns[at + 3]); mIns[i + 1].mLive |= mIns[i].mLive; + mIns.Insert(i + 1, mIns[at + 2]); mIns[i + 1].mLive |= mIns[i].mLive; + mIns.Insert(i + 1, mIns[at + 1]); mIns[i + 1].mLive |= mIns[i].mLive; + + mIns[at + 1].mType = ASMIT_NOP; mIns[at + 1].mMode = ASMIM_IMPLIED; // LDA U + mIns[at + 2].mType = ASMIT_NOP; mIns[at + 2].mMode = ASMIM_IMPLIED; // ADC # + mIns[at + 3].mType = ASMIT_NOP; mIns[at + 3].mMode = ASMIM_IMPLIED; // STA T + + return true; + } + + i++; + } + + return false; +} + +bool NativeCodeBasicBlock::MoveLoadImmStoreAbsoluteUp(int at) +{ + int j = at; + while (j > 0) + { + if (mIns[j - 1].mType == ASMIT_LDA && (mIns[j - 1].mMode == ASMIM_IMMEDIATE || mIns[j - 1].mMode == ASMIM_ZERO_PAGE)) + { + if (mIns[j - 1].mMode == mIns[at + 0].mMode && mIns[j - 1].mAddress == mIns[at + 0].mAddress) + { + while (j < at && mIns[j].mType == ASMIT_STA) + j++; + mIns[j - 1].mLive |= LIVE_CPU_REG_A; + + NativeCodeInstruction sins = mIns[at + 1]; + mIns.Remove(at + 1); + if (!(sins.mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z))) + mIns.Remove(at); + + if (sins.ReferencesXReg()) + sins.mLive |= LIVE_CPU_REG_X; + if (sins.ReferencesYReg()) + sins.mLive |= LIVE_CPU_REG_Y; + + mIns.Insert(j, sins); + + return true; + } + else if (mIns[j - 1].mMode == mIns[at + 1].mMode && mIns[j - 1].mAddress == mIns[at + 1].mAddress) + return false; + + j--; + } + else if (mIns[j - 1].mType == ASMIT_STA && mIns[j - 1].mMode == mIns[at + 1].mMode && !(mIns[j - 1].mLinkerObject == mIns[at + 1].mLinkerObject && mIns[j - 1].mAddress == mIns[at + 1].mAddress)) + j--; + else + return false; + } + + return false; +} + + +bool NativeCodeBasicBlock::MoveLoadStoreUp(int at) +{ + int j = at; + while (j > 0 && !((mIns[j - 1].mType == ASMIT_STA || mIns[j - 1].mType == ASMIT_LDA) && mIns[j - 1].mMode == ASMIM_ZERO_PAGE && mIns[j - 1].mAddress == mIns[at].mAddress)) + { + j--; + if ((mIns[j].mMode == ASMIM_ZERO_PAGE || mIns[j].mMode == ASMIM_INDIRECT_Y) && mIns[j].mAddress == mIns[at + 1].mAddress) + return false; + if (mIns[j].mMode == ASMIM_INDIRECT_Y && mIns[j].mAddress + 1 == mIns[at + 1].mAddress) + return false; + if (mIns[j].mMode == ASMIM_ZERO_PAGE && mIns[j].mAddress == mIns[at].mAddress && mIns[j].ChangesAddress()) + return false; + if (mIns[j].mMode == ASMIM_ABSOLUTE_Y && mIns[j].mAddress <= mIns[at + 1].mAddress && mIns[j].mType == ASMIT_LDA && !mIns[j].mLinkerObject) + return false; + if (mIns[j].mType == ASMIT_JSR) + return false; + } + + if (j > 0 && j < at) + { + mIns.Insert(j, mIns[at + 1]); + mIns[j - 1].mLive |= LIVE_CPU_REG_A; + mIns[j].mLive |= LIVE_CPU_REG_A; + mIns[j].mLive |= mIns[j - 1].mLive; + mIns[at + 2].mType = ASMIT_NOP; mIns[at + 2].mMode = ASMIM_IMPLIED; + + return true; + } + + return false; +} + + +// [at + 0] = LSR zp1 and not needed +// [at + 1] = ROR +// [at + 2] = STA zp2 and accu not needed + +bool NativeCodeBasicBlock::ReverseShiftByteOrder(int at) +{ + int start = at; + + while (start > 2 && + mIns[start - 2].mType == mIns[at + 0].mType && mIns[start - 2].mMode == ASMIM_ZERO_PAGE && mIns[start - 2].mAddress == mIns[at + 0].mAddress && + mIns[start - 1].mType == mIns[at + 1].mType && mIns[start - 1].mMode == ASMIM_IMPLIED) + start -= 2; + + if (start >= 5 && + mIns[start - 5].mType == ASMIT_LDA && mIns[start - 5].mMode == ASMIM_ZERO_PAGE && !(mIns[start - 5].mLive & LIVE_MEM) && + mIns[start - 4].mType == mIns[at + 0].mType && mIns[start - 4].mMode == ASMIM_IMPLIED && + mIns[start - 3].mType == ASMIT_STA && mIns[start - 3].mMode == ASMIM_ZERO_PAGE && mIns[start - 3].mAddress == mIns[at + 0].mAddress && + mIns[start - 2].mType == ASMIT_LDA && mIns[start - 2].mMode == ASMIM_ZERO_PAGE && + mIns[start - 1].mType == mIns[at + 1].mType && mIns[start - 1].mMode == ASMIM_IMPLIED) + { + int reg = mIns[start - 5].mAddress; + + mIns[start - 5].mType = mIns[at + 0].mType; mIns[start - 5].mLive |= LIVE_MEM | LIVE_CPU_REG_C; + mIns[start - 4].mType = ASMIT_NOP; mIns[start - 4].mMode = ASMIM_IMPLIED; + mIns[start - 3].mType = ASMIT_NOP; mIns[start - 3].mMode = ASMIM_IMPLIED; + + while (start <= at) + { + mIns[start + 0].mAddress = reg; + start += 2; + } + + return true; + } + + return false; +} + +// [at + 0] LDA zp +// [at + 1] ASL and accu not needed, move down to carry use + +bool NativeCodeBasicBlock::MoveZeroPageSignToCarryDown(int at) +{ + int i = at + 2; + while (i < mIns.Size()) + { + if (mIns[i].ReferencesCarry()) + { + while (i > at + 2 && (mIns[i - 1].mLive & LIVE_CPU_REG_A)) + i--; + if (i > at + 2) + { + mIns.Insert(i + 0, mIns[at + 0]); mIns[i + 0].mLive |= mIns[i - 1].mLive; + mIns.Insert(i + 1, mIns[at + 1]); mIns[i + 1].mLive |= mIns[i - 1].mLive; + mIns[at + 0].mType = ASMIT_NOP; mIns[at + 0].mMode = ASMIM_IMPLIED; + mIns[at + 1].mType = ASMIT_NOP; mIns[at + 1].mMode = ASMIM_IMPLIED; + return true; + } + return false; + } + else if (mIns[i].ChangesCarry()) + return false; + else if (mIns[i].ChangesZeroPage(mIns[at].mAddress)) + return false; + i++; + } + return false; +} + +// [at + 0] ASL zp +// [at + 1] ROL zp +bool NativeCodeBasicBlock::Move16BitShiftUp(int at) +{ + int i = at - 2; + while (i >= 0) + { + if (mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && mIns[i + 0].mAddress == mIns[at + 1].mAddress && + mIns[i + 1].mType == ASMIT_ASL && mIns[i + 1].mMode == ASMIM_IMPLIED && !(mIns[i + 1].mLive & LIVE_CPU_REG_A)) + { + int live = mIns[i + 1].mLive; + mIns[at + 0].mLive |= live; + mIns[at + 1].mLive |= live; + mIns[i + 0] = mIns[at + 0]; + mIns[i + 1] = mIns[at + 1]; + mIns.Remove(at, 2); + return true; + } + if (mIns[at + 0].MayBeChangedOnAddress(mIns[i]) || mIns[at + 1].MayBeChangedOnAddress(mIns[i])) + return false; + i--; + } + + return false; +} + +// [at + 0] CLC +// [at + 1] LDA zp +// [at + 2] ADC # +// [at + 3] STA zp +// [at + 4] LDA zp +// [at + 5] ADC # +// [at + 6] STA zp + +bool NativeCodeBasicBlock::Move16BitAddUp(int at) +{ +// printf("Move16BitAddUp, %s, %d, %d\n", mProc->mIdent->mString, mIndex, at); + + int i = at - 7; + while (i >= 0) + { + if ((mIns[i + 0].mType == ASMIT_TAY || mIns[i + 0].mType == ASMIT_TAX) && + mIns[i + 1].mType == ASMIT_CLC && + mIns[i + 2].mType == ASMIT_ADC && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && mIns[i + 3].mAddress == mIns[at + 1].mAddress && + mIns[i + 4].mType == ASMIT_LDA && + mIns[i + 5].mType == ASMIT_ADC && + mIns[i + 6].mType == ASMIT_STA && mIns[i + 6].mMode == ASMIM_ZERO_PAGE && mIns[i + 6].mAddress == mIns[at + 4].mAddress) + { + if (mIns[i + 2].mMode == ASMIM_IMMEDIATE_ADDRESS && mIns[i + 4].mMode == ASMIM_IMMEDIATE_ADDRESS && + mIns[i + 2].mLinkerObject == mIns[i + 4].mLinkerObject && + mIns[i + 2].mAddress == mIns[i + 4].mAddress && + (mIns[i + 5].mMode != ASMIM_ZERO_PAGE || mIns[i + 5].mAddress != mIns[i + 6].mAddress)) + { + printf("Add shift found, %s, %d, %d -> %d\n", mProc->mIdent->mString, mIndex, at, i); + + const InterInstruction* iins = mIns[at].mIns; + + int addr = mIns[i + 2].mAddress + mIns[at + 2].mAddress + 256 * mIns[at + 5].mAddress; + int lo = mIns[at + 3].mAddress, hi = mIns[at + 6].mAddress; + + mIns.Remove(at, 7); + + int k = i + 7; + + if (mIns[i + 0].mType == ASMIT_TAY) + mIns.Insert(k + 0, NativeCodeInstruction(iins, ASMIT_TYA)); + else + mIns.Insert(k + 0, NativeCodeInstruction(iins, ASMIT_TXA)); + + mIns.Insert(k + 1, NativeCodeInstruction(iins, ASMIT_CLC)); + mIns.Insert(k + 2, NativeCodeInstruction(iins, ASMIT_ADC, mIns[i + 2])); + mIns.Insert(k + 3, NativeCodeInstruction(iins, ASMIT_STA, ASMIM_ZERO_PAGE, lo)); + mIns.Insert(k + 4, NativeCodeInstruction(iins, ASMIT_LDA, mIns[i + 4])); + mIns.Insert(k + 5, NativeCodeInstruction(iins, ASMIT_ADC, mIns[i + 5])); + mIns.Insert(k + 6, NativeCodeInstruction(iins, ASMIT_STA, ASMIM_ZERO_PAGE, hi)); + + mIns[k + 2].mAddress = addr; + mIns[k + 4].mAddress = addr; + + return true; + } + + return false; + } + + if (mIns[i + 6].ChangesZeroPage(mIns[at + 1].mAddress) || + mIns[i + 6].ReferencesZeroPage(mIns[at + 3].mAddress) || + mIns[i + 6].ChangesZeroPage(mIns[at + 4].mAddress) || + mIns[i + 6].ReferencesZeroPage(mIns[at + 6].mAddress)) + { + return false; + } + + i--; + } + + return false; +} + +// [at + 0] ASL zp +// [at + 1] ROL zp +bool NativeCodeBasicBlock::Move16BitShiftDown(int at) +{ + int i = at + 2; + while (i < mIns.Size()) + { + if (mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && (mIns[i + 0].mAddress == mIns[at + 1].mAddress || mIns[i + 0].mAddress == mIns[at + 0].mAddress)) + { + if (mIns[i + 0].mLive & (LIVE_CPU_REG_C | LIVE_CPU_REG_Z | LIVE_MEM)) return false; + + int live = mIns[i + 0].mLive | LIVE_CPU_REG_A; + + mIns.Insert(i + 1, mIns[at]); + mIns.Insert(i + 2, mIns[at + 1]); + + if (mIns[i].mAddress == mIns[at + 0].mAddress) + mIns[i + 1].mMode = ASMIM_IMPLIED; + else + mIns[i + 2].mMode = ASMIM_IMPLIED; + + mIns[i + 1].mLive |= live; + mIns[i + 2].mLive |= live; + mIns.Remove(at, 2); + return true; + } + + if (mIns[i].ReferencesZeroPage(mIns[at + 0].mAddress) || mIns[i].ReferencesZeroPage(mIns[at + 1].mAddress)) + return false; + i++; + } + + return false; +} + + +// Assume [at ] = SHIFT +// Assume [at + 1] = ORA + +bool NativeCodeBasicBlock::FoldShiftORAIntoLoadImmUp(int at) +{ + int ora = mIns[at + 1].mAddress; + int i = at; + + while (i >= 0) + { + if (mIns[i].mType == ASMIT_LDA && mIns[i].mMode == ASMIM_IMMEDIATE) + { + mIns[i].mAddress |= ora; + mIns[at + 1].mType = ASMIT_NOP; + mIns[at + 1].mMode = ASMIM_IMPLIED; + return true; + } + else if ((mIns[i].mType == ASMIT_ROL || mIns[i].mType == ASMIT_ASL) && mIns[i].mMode == ASMIM_IMPLIED) + { + if ((mIns[i].mLive & LIVE_CPU_REG_C) || (ora & 0x01)) + return false; + ora >>= 1; + } + else if ((mIns[i].mType == ASMIT_ROR || mIns[i].mType == ASMIT_LSR) && mIns[i].mMode == ASMIM_IMPLIED) + { + if ((mIns[i].mLive & LIVE_CPU_REG_C) || (ora & 0x80)) + return false; + ora <<= 1; + } + else if (mIns[i].ReferencesAccu()) + return false; + i--; + } + + return false; +} + + +// LDA zp +// op # +// TAX + +bool NativeCodeBasicBlock::MoveLDAopTAXDown(int at) +{ + int addr = mIns[at + 0].mAddress; + + int i = at + 3, di = 0; + while (i < mIns.Size()) + { + if (mIns[i].ReferencesXReg() || mIns[i].ChangesXReg()) + break; + if (mIns[at].MayBeChangedOnAddress(mIns[i])) + break; + if (!(mIns[i].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z))) + di = i + 1; + i++; + } + + if (di > at + 3) + { + if (mIns[di - 1].mType == ASMIT_TAY && + mIns[di - 2].mMode == ASMIM_IMMEDIATE && + mIns[di - 3].mType == ASMIT_LDA) + { + di -= 3; + } + else if (mIns[di - 1].mType == ASMIT_LDY || mIns[di - 1].mType == ASMIT_CLC || mIns[di - 1].mType == ASMIT_SEC) + { + di--; + } + } + + if (di > at + 3) + { + int live = mIns[di - 1].mLive & (LIVE_CPU_REG_Y | LIVE_CPU_REG_C); + mIns[at + 0].mLive |= live; + mIns[at + 1].mLive |= live; + mIns[at + 2].mLive |= live; + mIns.Insert(di + 0, mIns[at + 0]); + mIns.Insert(di + 1, mIns[at + 1]); + mIns.Insert(di + 2, mIns[at + 2]); + mIns.Remove(at, 3); + return true; + } + + return false; +} + +// TAX +// CLC +// ADC #1, 2, 3 +// STA +// +// convert to INX/DEX/STX when X not needed anymore + +bool NativeCodeBasicBlock::MoveTAXADCSTADown(int at) +{ + int n = mIns[at + 2].mAddress; + int addr = mIns[at + 3].mAddress; + + int si = at + 4; + while (si < mIns.Size()) + { + if (!(mIns[si].mLive & LIVE_CPU_REG_X)) + { + if (mIns[si].mLive & LIVE_CPU_REG_Z) + return false; + + mIns[si].mLive |= LIVE_CPU_REG_X; + si++; + + for (int i = 0; i < n; i++) + { + mIns.Insert(si, NativeCodeInstruction(mIns[at].mIns, ASMIT_INX)); + si++; + } + mIns.Insert(si, NativeCodeInstruction(mIns[at].mIns, ASMIT_STX, ASMIM_ZERO_PAGE, addr)); + return true; + } + + if (mIns[si].ChangesXReg()) + return false; + if (mIns[si].ReferencesZeroPage(addr)) + return false; + + si++; + } + + return false; +} + + +// CLC +// LDA zp0 +// ADC #1, 2, 3 +// STA zp1 +// ... +// LDX zp1 +// +// Convert to INX/DEX + +bool NativeCodeBasicBlock::MoveSimpleADCToINCDECDown(int at) +{ + if (at >= 4) + { + int si = at - 4; + + while (si >= 0) + { + if (mIns[si + 3].ReferencesZeroPage(mIns[at].mAddress)) + { + if (mIns[si + 0].mType == ASMIT_CLC && + mIns[si + 1].mType == ASMIT_LDA && mIns[si + 1].mMode == ASMIM_ZERO_PAGE && + mIns[si + 2].mType == ASMIT_ADC && mIns[si + 2].mMode == ASMIM_IMMEDIATE && (mIns[si + 2].mAddress >= 0 && mIns[si + 2].mAddress <= 4) && + mIns[si + 3].mType == ASMIT_STA && !(mIns[si + 3].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C))) + { + int i = si + 4; + while (i < at && !mIns[i].ChangesZeroPage(mIns[si + 1].mAddress)) + i++; + if (i != at) + return false; + + AsmInsType t = mIns[at].mType == ASMIT_LDX ? ASMIT_INX : ASMIT_INY; + + mIns[at].mAddress = mIns[si + 1].mAddress; + for (int i = 0; i < mIns[si + 2].mAddress; i++) + mIns.Insert(at + 1, NativeCodeInstruction(mIns[si].mIns, t)); + + mIns[si + 0].mType = ASMIT_NOP; mIns[si + 0].mMode = ASMIM_IMPLIED; + mIns[si + 1].mType = ASMIT_NOP; mIns[si + 1].mMode = ASMIM_IMPLIED; + mIns[si + 2].mType = ASMIT_NOP; mIns[si + 2].mMode = ASMIM_IMPLIED; + mIns[si + 3].mType = ASMIT_NOP; mIns[si + 3].mMode = ASMIM_IMPLIED; + + return true; + } + else + return false; + } + + si--; + } + + } + + return false; +} + +bool NativeCodeBasicBlock::CombineImmediateADCUp(int at) +{ + int addr = mIns[at].mAddress; + int i = at; + while (i > 3) + { + i--; + if (mIns[i].mType == ASMIT_STA && mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i].mAddress == addr) + { + if (!(mIns[i].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C))) + { + if (mIns[i - 3].mType == ASMIT_CLC && + mIns[i - 2].mType == ASMIT_LDA && mIns[i - 2].mMode == ASMIM_ZERO_PAGE && mIns[i - 2].mAddress == addr && + mIns[i - 1].mType == ASMIT_ADC && (mIns[i - 1].mMode == ASMIM_IMMEDIATE || mIns[i - 1].mMode == ASMIM_IMMEDIATE_ADDRESS)) + { + int val = mIns[at + 1].mAddress; + mIns[at + 1].CopyMode(mIns[i - 1]); + if (mIns[at + 1].mMode == ASMIM_IMMEDIATE_ADDRESS && (mIns[at + 1].mFlags & NCIF_UPPER)) + mIns[at + 1].mAddress += val * 256; + else + mIns[at + 1].mAddress += val; + mIns.Remove(i - 3, 4); + return true; + } + else if (mIns[i - 3].mType == ASMIT_CLC && + mIns[i - 2].mType == ASMIT_LDA && (mIns[i - 2].mMode == ASMIM_IMMEDIATE || mIns[i - 2].mMode == ASMIM_IMMEDIATE_ADDRESS) && + mIns[i - 1].mType == ASMIT_ADC && mIns[i - 1].mMode == ASMIM_ZERO_PAGE && mIns[i - 1].mAddress == addr) + { + int val = mIns[at + 1].mAddress; + mIns[at + 1].CopyMode(mIns[i - 2]); + if (mIns[at + 1].mMode == ASMIM_IMMEDIATE_ADDRESS && (mIns[at + 1].mFlags & NCIF_UPPER)) + mIns[at + 1].mAddress += val * 256; + else + mIns[at + 1].mAddress += val; + mIns.Remove(i - 3, 4); + return true; + } + else if + (mIns[i - 2].mType == ASMIT_CLC && + mIns[i - 1].mType == ASMIT_ADC && (mIns[i - 1].mMode == ASMIM_IMMEDIATE || mIns[i - 1].mMode == ASMIM_IMMEDIATE_ADDRESS)) + { + int val = mIns[at + 1].mAddress; + mIns[at + 1].CopyMode(mIns[i - 1]); + if (mIns[at + 1].mMode == ASMIM_IMMEDIATE_ADDRESS && (mIns[at + 1].mFlags & NCIF_UPPER)) + mIns[at + 1].mAddress += val * 256; + else + mIns[at + 1].mAddress += val; + mIns.Remove(i - 2, 2); + return true; + } + } + + return false; + } + + if (mIns[i].ReferencesZeroPage(addr)) + return false; + } + + return false; +} + +bool NativeCodeBasicBlock::MoveTXALogicTAXDown(int at) +{ + int i = at + 3; + while (i < mIns.Size()) + { + if (mIns[i].ChangesXReg()) + return false; + + if (mIns[i].mType == ASMIT_TXA) + { + if (mIns[i].mLive & LIVE_CPU_REG_X) + return false; + mIns[at + 1].mLive |= mIns[i].mLive; + mIns.Insert(i + 1, mIns[at + 1]); + mIns.Remove(at, 3); + return true; + } + + if (mIns[i].RequiresXReg()) + return false; + + i++; + } + + return false; +} + +bool NativeCodeBasicBlock::MoveTXADCDown(int at) +{ + int i = at + 4; + while (i < mIns.Size()) + { + if (mIns[i].ChangesXReg()) + return false; + if (mIns[i].ReferencesZeroPage(mIns[at + 3].mAddress)) + return false; + + if (!(mIns[i].mLive & (LIVE_CPU_REG_X | LIVE_CPU_REG_Z))) + { + mIns.Insert(i + 1, NativeCodeInstruction(mIns[at + 3].mIns, ASMIT_STX, ASMIM_ZERO_PAGE, mIns[at + 3].mAddress)); + + switch (mIns[at + 2].mAddress) + { + case -2: + case 254: + mIns.Insert(i + 1, NativeCodeInstruction(mIns[at + 2].mIns, ASMIT_DEX)); + case -1: + case 255: + mIns.Insert(i + 1, NativeCodeInstruction(mIns[at + 2].mIns, ASMIT_DEX)); + break; + case 2: + mIns.Insert(i + 1, NativeCodeInstruction(mIns[at + 2].mIns, ASMIT_INX)); + case 1: + mIns.Insert(i + 1, NativeCodeInstruction(mIns[at + 2].mIns, ASMIT_INX)); + break; + } + + while (i > at) + { + mIns[i].mLive |= LIVE_CPU_REG_X; + i--; + } + mIns.Remove(at, 4); + return true; + } + + i++; + } + + return false; +} + +bool NativeCodeBasicBlock::CombineImmediateADCUpX(int at) +{ + int i = at; + while (i > 3) + { + i--; + if (mIns[i].mType == ASMIT_TAX) + { + if (!(mIns[i].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C))) + { + if (mIns[i - 3].mType == ASMIT_CLC && + mIns[i - 2].mType == ASMIT_LDA && + mIns[i - 1].mType == ASMIT_ADC && (mIns[i - 1].mMode == ASMIM_IMMEDIATE || mIns[i - 1].mMode == ASMIM_IMMEDIATE_ADDRESS)) + { + int val = mIns[at + 1].mAddress; + mIns[at + 1].CopyMode(mIns[i - 1]); + if (mIns[at + 1].mFlags & NCIF_UPPER) + mIns[at + 1].mAddress += val * 256; + else + mIns[at + 1].mAddress += val; + mIns[i - 1].mType = ASMIT_NOP; mIns[i - 1].mMode = ASMIM_IMPLIED; + mIns[i - 3].mType = ASMIT_NOP; mIns[i - 3].mMode = ASMIM_IMPLIED; + return true; + } + else if + (mIns[i - 2].mType == ASMIT_CLC && + mIns[i - 1].mType == ASMIT_ADC && (mIns[i - 1].mMode == ASMIM_IMMEDIATE || mIns[i - 1].mMode == ASMIM_IMMEDIATE_ADDRESS)) + { + int val = mIns[at + 1].mAddress; + mIns[at + 1].CopyMode(mIns[i - 1]); + if (mIns[at + 1].mFlags & NCIF_UPPER) + mIns[at + 1].mAddress += val * 256; + else + mIns[at + 1].mAddress += val; + mIns[i - 2].mType = ASMIT_NOP; mIns[i - 2].mMode = ASMIM_IMPLIED; + mIns[i - 1].mType = ASMIT_NOP; mIns[i - 1].mMode = ASMIM_IMPLIED; + return true; + } + } + + return false; + } + + if (mIns[i].ReferencesXReg()) + return false; + } + + return false; +} + +bool NativeCodeBasicBlock::MoveASLMemUp(int at) +{ + int j = at; + while (j > 0 && !mIns[j - 1].SameEffectiveAddress(mIns[at])) + { + if ((mIns[at].mLive & LIVE_CPU_REG_C) && mIns[j - 1].ReferencesCarry()) + return false; + j--; + } + + if (j > 0 && mIns[j - 1].mType == ASMIT_STA && !(mIns[j - 1].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C | LIVE_CPU_REG_Z))) + { + if (mIns[at].mLive & LIVE_CPU_REG_C) + { + for (int i = j - 1; i < at; i++) + mIns[i].mLive |= LIVE_CPU_REG_C; + } + + mIns.Insert(j - 1, NativeCodeInstruction(mIns[at].mIns, mIns[at].mType)); + mIns.Remove(at + 1); + return true; + } + + return false; +} + + +bool NativeCodeBasicBlock::MoveLoadStoreXUp(int at) +{ + int j = at; + while (j > 0 && !((mIns[j - 1].mType == ASMIT_STA || mIns[j - 1].mType == ASMIT_LDA) && mIns[j - 1].mMode == ASMIM_ZERO_PAGE && mIns[j - 1].mAddress == mIns[at].mAddress)) + { + j--; + if ((mIns[j].mMode == ASMIM_ZERO_PAGE || mIns[j].mMode == ASMIM_INDIRECT_Y) && mIns[j].mAddress == mIns[at + 1].mAddress) + return false; + if (mIns[j].mMode == ASMIM_INDIRECT_Y && mIns[j].mAddress + 1 == mIns[at + 1].mAddress) + return false; + if (mIns[j].mMode == ASMIM_ZERO_PAGE && mIns[j].mAddress == mIns[at].mAddress && mIns[j].ChangesAddress()) + return false; + if (mIns[j].mMode == ASMIM_ABSOLUTE_Y && mIns[j].mAddress <= mIns[at + 1].mAddress && mIns[j].mType == ASMIT_LDA && !mIns[j].mLinkerObject) + return false; + if (mIns[j].mType == ASMIT_JSR) + return false; + } + + if (j > 0 && j < at) + { + for (int i = at; i > j; i--) + { + mIns[i] = mIns[i - 1]; + } + mIns[at + 1].mLive |= mIns[j - 1].mLive; + mIns[j - 1].mLive |= LIVE_CPU_REG_A; + mIns[j] = mIns[at + 1]; + mIns[j].mType = ASMIT_STA; + mIns[at + 1].mType = ASMIT_NOP; mIns[at + 1].mMode = ASMIM_IMPLIED; + + return true; + } + + return false; +} + +bool NativeCodeBasicBlock::MoveStaTaxLdaStaDown(int at) +{ + int j = at + 4; + + while (j < mIns.Size()) + { + if (mIns[j].UsesZeroPage(mIns[at + 3].mAddress)) + { + if (mIns[j].mLive & (LIVE_CPU_REG_X | LIVE_MEM)) + return false; + + if (!HasAsmInstructionMode(mIns[j].mType, ASMIM_ABSOLUTE_X)) + return false; + + mIns[j].CopyMode(mIns[at + 2]); + mIns.Insert(j, NativeCodeInstruction(mIns[at].mIns, ASMIT_LDX, ASMIM_ZERO_PAGE, mIns[at + 0].mAddress)); + + mIns.Remove(at + 1); + mIns.Remove(at + 1); + mIns.Remove(at + 1); + + return true; + } + if (mIns[at + 0].MayBeChangedOnAddress(mIns[j])) + return false; + if (mIns[at + 2].MayBeChangedOnAddress(mIns[j])) + return false; + if (mIns[at + 3].MayBeChangedOnAddress(mIns[j])) + return false; + + j++; + } + + return false; +} + +bool NativeCodeBasicBlock::MoveLoadAddImmStoreAbsXUp(int at) +{ + int j = at - 1, top = at; + while (j > 0) + { + if (mIns[j].ChangesXReg()) + break; + if (mIns[j].MayBeSameAddress(mIns[at + 3], true)) + break; + if (mIns[j].MayBeSameAddress(mIns[at + 0], true) && mIns[j].ChangesAddress()) + break; + + if (!(mIns[j - 1].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C | LIVE_CPU_REG_Z))) + top = j; + j--; + } + + while (top < at && mIns[top].mType == ASMIT_STX) + top++; + + if (top < at) + { + int live = 0; + if (top > 0) + live = mIns[top - 1].mLive; + else if (mIns[top].RequiresYReg()) + live |= LIVE_CPU_REG_Y; + mIns[at + 0].mLive |= live; + mIns[at + 1].mLive |= live; + mIns[at + 2].mLive |= live; + mIns[at + 3].mLive |= live; + + mIns.Insert(top, mIns[at + 3]); mIns.Remove(at + 4); + mIns.Insert(top, mIns[at + 3]); mIns.Remove(at + 4); + mIns.Insert(top, mIns[at + 3]); mIns.Remove(at + 4); + mIns.Insert(top, mIns[at + 3]); mIns.Remove(at + 4); + + return true; + } + else + return false; +} + +bool NativeCodeBasicBlock::MoveLoadAddStoreDown(int at) +{ + // at + 0 : LDA + // at + 1 : CLC + // at + 2 : ADC + // at + 3 : STA zp + + int addr = mIns[at + 3].mAddress; + int j = at + 4; + while (j < mIns.Size()) + { + if (mIns[j].mType == ASMIT_ADC && mIns[j].mMode == ASMIM_ZERO_PAGE && mIns[j].mAddress == addr && !(mIns[j].mLive & LIVE_MEM)) + { + if (mIns[at + 0].ReferencesXReg() || mIns[at + 2].ReferencesXReg()) + { + for (int i = at; i < j; i++) + mIns[i].mLive |= LIVE_CPU_REG_X; + } + if (mIns[at + 0].ReferencesYReg() || mIns[at + 2].ReferencesYReg()) + { + for (int i = at; i < j; i++) + mIns[i].mLive |= LIVE_CPU_REG_Y; + } + + mIns.Remove(j); + mIns.Insert(j, mIns[at + 0]); mIns[j].mType = ASMIT_ADC; + mIns.Insert(j + 1, mIns[at + 1]); + mIns.Insert(j + 2, mIns[at + 2]); + + mIns.Remove(at, 4); + return true; + } + + if (mIns[at + 0].MayBeChangedOnAddress(mIns[j]) || mIns[at + 2].MayBeChangedOnAddress(mIns[j])) + return false; + if (mIns[j].ReferencesZeroPage(addr)) + return false; + if (mIns[at + 0].ReferencesXReg() && mIns[j].ChangesXReg()) + return false; + if (mIns[at + 2].ReferencesXReg() && mIns[j].ChangesXReg()) + return false; + if (mIns[at + 0].ReferencesYReg() && mIns[j].ChangesYReg()) + return false; + if (mIns[at + 2].ReferencesYReg() && mIns[j].ChangesYReg()) + return false; + + j++; + } + + return false; +} + +bool NativeCodeBasicBlock::MoveLoadLogicStoreAbsUp(int at) +{ + // at + 0 : LDA zp + // at + 1 : ORA zp / imm + // at + 2 : STA abs / abs,x / abs,y + + int j = at - 1; + while (j >= 0) + { + if (mIns[j].mType == ASMIT_STA && mIns[j].mMode == ASMIM_ZERO_PAGE && mIns[j].mAddress == mIns[at].mAddress) + { + while (j + 1 < mIns.Size() && mIns[j + 1].mType == ASMIT_STA) + j++; + + if (mIns[j].mLive & LIVE_CPU_REG_A) + return false; + + mIns[j].mLive |= LIVE_CPU_REG_A; + for (int i = j + 1; i < at; i++) + mIns[i].mLive |= LIVE_CPU_REG_C; + + mIns[at + 1].mLive |= mIns[j].mLive; + mIns[at + 2].mLive |= mIns[j].mLive; + + mIns.Insert(j + 1, mIns[at + 2]); // STA + mIns.Insert(j + 1, mIns[at + 2]); // ORA + + mIns[at + 2].mType = ASMIT_NOP; mIns[at + 2].mMode = ASMIM_IMPLIED; + mIns[at + 3].mType = ASMIT_NOP; mIns[at + 3].mMode = ASMIM_IMPLIED; + mIns[at + 4].mType = ASMIT_NOP; mIns[at + 4].mMode = ASMIM_IMPLIED; + + return true; + } + + if (mIns[j].ChangesZeroPage(mIns[at + 0].mAddress)) + return false; + if (mIns[at + 1].mMode == ASMIM_ZERO_PAGE && mIns[j].ChangesZeroPage(mIns[at + 1].mAddress)) + return false; + + if (mIns[j].UsesMemoryOf(mIns[at + 2])) + return false; + if (mIns[at + 2].mMode == ASMIM_ABSOLUTE_X && mIns[j].ChangesXReg()) + return false; + if (mIns[at + 2].mMode == ASMIM_ABSOLUTE_Y && mIns[j].ChangesYReg()) + return false; + + j--; + } + + return false; +} + + +bool NativeCodeBasicBlock::MoveLoadOrZPUp(int at) +{ + // at + 0 : LDA zp + // at + 1 : ORA zp + + int j = at - 1; + while (j >= 0) + { + if (mIns[j].mType == ASMIT_STA && mIns[j].mMode == ASMIM_ZERO_PAGE && (mIns[j].mAddress == mIns[at].mAddress || mIns[j].mAddress == mIns[at + 1].mAddress)) + { + if (mIns[j].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z)) + return false; + + if (mIns[at + 1].mAddress == mIns[j].mAddress) + { + mIns[at + 1].mAddress = mIns[at].mAddress; + mIns[at].mAddress = mIns[j].mAddress; + } + mIns[at].mLive |= mIns[at + 1].mLive & LIVE_CPU_REG_Z; + mIns[at + 1].mLive |= mIns[j].mLive | LIVE_CPU_REG_A; + mIns.Insert(j, mIns[at + 1]); + + mIns[at + 2].mType = ASMIT_NOP; mIns[at + 2].mMode = ASMIM_IMPLIED; + return true; + } + + if (mIns[j].ReferencesZeroPage(mIns[at].mAddress)) + return false; + if (mIns[j].ReferencesZeroPage(mIns[at + 1].mAddress)) + return false; + + j--; + } + + return false; + +} + + +bool NativeCodeBasicBlock::MoveImmOpBeforeStore(int at) +{ + // at + 0 : LDA zp + // at + 1 : AND #imm + + int j = at - 1; + while (j >= 0) + { + if (mIns[j].mType == ASMIT_STA && mIns[j].mMode == ASMIM_ZERO_PAGE && mIns[j].mAddress == mIns[at].mAddress) + { + if (mIns[j].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z)) + return false; + + mIns[at + 1].mLive |= mIns[j].mLive; + mIns.Insert(j, mIns[at + 1]); + + mIns[at + 2].mType = ASMIT_NOP; mIns[at + 2].mMode = ASMIM_IMPLIED; + return true; + } + + if (mIns[j].ReferencesZeroPage(mIns[at].mAddress)) + return false; + + j--; + } + + return false; +} + +bool NativeCodeBasicBlock::MoveLoadAddImmStoreUp(int at) +{ + int j = at - 1; + while (j >= 0) + { + if (mIns[j].mType == ASMIT_STA && mIns[j].mMode == ASMIM_ZERO_PAGE && mIns[j].mAddress == mIns[at + 1].mAddress) + { + while (j + 1 < mIns.Size() && mIns[j + 1].mType == ASMIT_STA) + j++; + + if (mIns[j].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C)) + return false; + + mIns[j].mLive |= LIVE_CPU_REG_A; + for (int i = j + 1; i < at; i++) + mIns[i].mLive |= LIVE_CPU_REG_C; + + mIns[at + 0].mLive |= mIns[j].mLive; + mIns[at + 2].mLive |= mIns[j].mLive; + mIns[at + 3].mLive |= mIns[j].mLive; + + mIns.Insert(j + 1, mIns[at + 3]); // STA + mIns.Insert(j + 1, mIns[at + 3]); // ADC + mIns.Insert(j + 1, mIns[at + 2]); // CLC + + mIns[at + 3].mType = ASMIT_NOP; mIns[at + 3].mMode = ASMIM_IMPLIED; + mIns[at + 4].mType = ASMIT_NOP; mIns[at + 4].mMode = ASMIM_IMPLIED; + mIns[at + 5].mType = ASMIT_NOP; mIns[at + 5].mMode = ASMIM_IMPLIED; + mIns[at + 6].mType = ASMIT_NOP; mIns[at + 6].mMode = ASMIM_IMPLIED; + return true; + } + + if (mIns[j].ChangesZeroPage(mIns[at + 1].mAddress)) + return false; + if (mIns[j].UsesMemoryOf(mIns[at + 3])) + return false; + if (mIns[j].ChangesCarry()) + return false; + + j--; + } + + return false; +} + +bool NativeCodeBasicBlock::MoveLoadEorImmStoreUp(int at) +{ + int j = at - 1; + while (j >= 0) + { + if (mIns[j].mType == ASMIT_STA && mIns[j].mMode == ASMIM_ZERO_PAGE && mIns[j].mAddress == mIns[at + 0].mAddress) + { + if (mIns[j].mLive & LIVE_CPU_REG_A) + return false; + + mIns[j].mLive |= LIVE_CPU_REG_A; + + mIns[at + 1].mLive |= mIns[j].mLive; + mIns[at + 2].mLive |= mIns[j].mLive; + + mIns.Insert(j + 1, mIns[at + 2]); // STA + mIns.Insert(j + 1, mIns[at + 2]); // EOR + + mIns[at + 2].mType = ASMIT_NOP; mIns[at + 2].mMode = ASMIM_IMPLIED; + mIns[at + 3].mType = ASMIT_NOP; mIns[at + 3].mMode = ASMIM_IMPLIED; + mIns[at + 4].mType = ASMIT_NOP; mIns[at + 4].mMode = ASMIM_IMPLIED; + return true; + } + else if (mIns[j].IsShift() && mIns[j].mMode == ASMIM_ZERO_PAGE && mIns[j].mAddress == mIns[at + 0].mAddress && mIns[at + 2].mMode == ASMIM_ZERO_PAGE && mIns[j].mAddress == mIns[at + 2].mAddress) + { + if (mIns[j].mLive & LIVE_CPU_REG_A) + return false; + + mIns[j].mLive |= LIVE_CPU_REG_A; + + mIns[at + 0].mLive |= LIVE_CPU_REG_C; + mIns[at + 1].mLive |= mIns[j].mLive; + mIns[at + 2].mLive |= mIns[j].mLive; + + mIns[j].mMode = ASMIM_IMPLIED; + + mIns.Insert(j, mIns[at + 0]); // LDA + + mIns.Insert(j + 2, mIns[at + 3]); // STA + mIns.Insert(j + 2, mIns[at + 3]); // EOR + + mIns[at + 3].mType = ASMIT_NOP; mIns[at + 3].mMode = ASMIM_IMPLIED; + mIns[at + 4].mType = ASMIT_NOP; mIns[at + 4].mMode = ASMIM_IMPLIED; + mIns[at + 5].mType = ASMIT_NOP; mIns[at + 5].mMode = ASMIM_IMPLIED; + return true; + } + + if (mIns[j].ChangesZeroPage(mIns[at + 0].mAddress)) + return false; + if (mIns[j].UsesMemoryOf(mIns[at + 2])) + return false; + + j--; + } + + return false; +} + +bool NativeCodeBasicBlock::MoveCLCLoadAddZPStoreUp(int at) +{ + int j = at - 1; + while (j > 0) + { + if (mIns[j].mType == ASMIT_STA && mIns[j].mMode == ASMIM_ZERO_PAGE && (mIns[j].mAddress == mIns[at + 1].mAddress || mIns[j].mAddress == mIns[at + 2].mAddress)) + { + bool flip = mIns[j].mAddress != mIns[at + 1].mAddress; + + if (mIns[j].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C)) + { + int k = j; + j++; + while (j < mIns.Size() && (mIns[j].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C))) + { + if (mIns[j].ChangesAccu()) + return false; + j++; + } + + while (k < j) + mIns[k++].mLive |= LIVE_CPU_REG_A; + } + + if (mIns[at + 2].mLive & LIVE_CPU_REG_C) + { + for (int i = j + 1; i < at; i++) + mIns[i].mLive |= LIVE_CPU_REG_C; + } + + if (flip) + mIns[at + 2].mAddress = mIns[at + 1].mAddress; + + mIns[at + 0].mLive |= mIns[j].mLive | LIVE_CPU_REG_A; + mIns[at + 2].mLive |= mIns[j].mLive; + mIns[at + 3].mLive |= mIns[j].mLive; + + mIns[j].mLive |= LIVE_CPU_REG_A; + + mIns.Insert(j + 1, mIns[at + 3]); // STA + mIns.Insert(j + 1, mIns[at + 3]); // ADC + mIns.Insert(j + 1, mIns[at + 2]); // CLC + + mIns[at + 3].mType = ASMIT_NOP; mIns[at + 3].mMode = ASMIM_IMPLIED; + mIns[at + 4].mType = ASMIT_NOP; mIns[at + 4].mMode = ASMIM_IMPLIED; + mIns[at + 5].mType = ASMIT_NOP; mIns[at + 5].mMode = ASMIM_IMPLIED; + mIns[at + 6].mType = ASMIT_LDA; + return true; + } + + if (mIns[j].ChangesZeroPage(mIns[at + 1].mAddress)) + return false; + if (mIns[j].ChangesZeroPage(mIns[at + 2].mAddress)) + return false; + if (mIns[j].UsesZeroPage(mIns[at + 3].mAddress)) + return false; + if ((mIns[at + 2].mLive & LIVE_CPU_REG_C) && mIns[j].ChangesCarry()) + return false; + + j--; + } + + return false; +} + +bool NativeCodeBasicBlock::ReverseLoadCommutativeOpUp(int aload, int aop) +{ + int j = aload - 1; + while (j > 0) + { + if (mIns[j].mType == ASMIT_STA && mIns[j].mMode == ASMIM_ZERO_PAGE && mIns[j].mAddress == mIns[aop].mAddress) + { + mIns[aop].CopyModeAndRange(mIns[aload]); + mIns[aop].mLive |= LIVE_MEM; + + mIns[aload].mType = ASMIT_NOP; + mIns[aload].mMode = ASMIM_IMPLIED; + + while (j < aop) + { + mIns[j].mLive |= LIVE_CPU_REG_A; + j++; + } + + j = aload; + while (j < aop) + { + mIns[j].mLive |= mIns[aload - 1].mLive; + j++; + } + + return true; + } + + if (mIns[j].ChangesAccu()) + return false; + if (mIns[j].ChangesZeroPage(mIns[aop].mAddress)) + return false; + + j--; + } + + return false; +} + +bool NativeCodeBasicBlock::MoveTYADCStoreDown(int at) +{ + // [at + 0] TYA + // [at + 1] CLC + // [at + 2] ADC # + // [at + 3] STA + + int offset = mIns[at + 2].mAddress; + int addr = mIns[at + 3].mAddress; + + for (int i = at + 4; i < mIns.Size(); i++) + { + NativeCodeInstruction& ins(mIns[i]); + + if (mIns[i].mType == ASMIT_INY) + offset--; + else if (mIns[i].mType == ASMIT_DEY) + offset++; + else if (mIns[i].ChangesYReg()) + return false; + else if (mIns[i].ReferencesZeroPage(addr)) + return false; + else if (!(mIns[i].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Y | LIVE_CPU_REG_Z))) + { + if (offset < -2 || offset > 2) + return false; + while (offset < 0) + { + mIns.Insert(i, NativeCodeInstruction(mIns[at].mIns, ASMIT_DEY)); + offset++; + i++; + } + while (offset > 0) + { + mIns.Insert(i, NativeCodeInstruction(mIns[at].mIns, ASMIT_INY)); + offset--; + i++; + } + mIns.Insert(i, NativeCodeInstruction(mIns[at].mIns, ASMIT_STY, ASMIM_ZERO_PAGE, addr)); + while (i > at) + { + mIns[i].mLive |= LIVE_CPU_REG_Y; + i--; + } + mIns.Remove(at, 4); + return true; + } + } + + return false; +} + +bool NativeCodeBasicBlock::MoveLDSTXOutOfRange(int at) +{ + bool vol = mIns[at + 1].mFlags & NCIF_VOLATILE; + + int j = at + 2; + while (j < mIns.Size()) + { + if (mIns[at + 1].mMode == ASMIM_ZERO_PAGE) + { + if (mIns[j].ReferencesZeroPage(mIns[at + 1].mAddress)) + return false; + } + else if (mIns[j].MayBeSameAddress(mIns[at + 1])) + return false; + else if (vol && mIns[j].ChangesGlobalMemory()) + return false; + else if (mIns[at + 1].mMode == ASMIM_ABSOLUTE && (mIns[j].mFlags & NCIF_VOLATILE)) + return false; + else if (mIns[j].ReferencesXReg()) + return false; + + if (mIns[j].mType == ASMIT_JSR || mIns[j].mType == ASMIT_RTS) + return false; + + if (!(mIns[j].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z))) + { + mIns.Insert(j + 1, NativeCodeInstruction(mIns[at + 1].mIns, ASMIT_STA, mIns[at + 1])); + mIns.Insert(j + 1, NativeCodeInstruction(mIns[at].mIns, ASMIT_LDA, mIns[at])); + mIns.Remove(at, 2); + + return true; + } + + j++; + } + + j = at; + while (j > 0) + { + j--; + if (!(mIns[j].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z))) + { + mIns.Insert(j + 1, NativeCodeInstruction(mIns[at + 1].mIns, ASMIT_STA, mIns[at + 1])); + mIns.Insert(j + 1, NativeCodeInstruction(mIns[at + 1].mIns, ASMIT_LDA, mIns[at + 1])); + mIns.Remove(at + 2, 2); + + return true; + } + + if (mIns[at + 1].mMode == ASMIM_ZERO_PAGE) + { + if (mIns[j].ReferencesZeroPage(mIns[at + 1].mAddress)) + return false; + } + else if (mIns[j].MayBeSameAddress(mIns[at + 1])) + return false; + else if (vol && mIns[j].ChangesGlobalMemory()) + return false; + else if (mIns[at + 1].mMode == ASMIM_ABSOLUTE && (mIns[j].mFlags & NCIF_VOLATILE)) + return false; + else if (mIns[j].ReferencesXReg()) + return false; + + if (mIns[j].mType == ASMIT_JSR) + return false; + + } + + return false; +} + +bool NativeCodeBasicBlock::MoveShiftZeroPageUp(int at) +{ + int j = at - 1; + while (j >= 0) + { + if (mIns[j].mType == ASMIT_STA && mIns[j].mMode == ASMIM_ZERO_PAGE && mIns[j].mAddress == mIns[at].mAddress) + { + if (mIns[j].mLive & LIVE_CPU_REG_A) + { + if (mIns[j + 1].mType == ASMIT_TAY || mIns[j + 1].mType == ASMIT_TAX) + { + if (mIns[j + 1].mLive & LIVE_CPU_REG_A) + return false; + NativeCodeInstruction ins(mIns[j + 1]); + ins.mLive |= LIVE_CPU_REG_A; + mIns.Remove(j + 1); + mIns.Insert(j, ins); + mIns[j + 1].mLive |= ins.mLive; + j++; + } + else + return false; + } + + NativeCodeInstruction sins(mIns[at]); + mIns.Remove(at); + sins.mMode = ASMIM_IMPLIED; + sins.mLive |= LIVE_CPU_REG_A; + mIns.Insert(j, sins); + mIns[j].mLive |= mIns[j + 1].mLive; + if (sins.mLive & LIVE_CPU_REG_C) + { + for (int i = j; i <= at; i++) + mIns[i].mLive |= LIVE_CPU_REG_C; + } + + return true; + } + + if (mIns[j].ReferencesCarry()) + return false; + if (mIns[j].ReferencesZeroPage(mIns[at].mAddress)) + return false; + j--; + } + + return false; +} + +bool NativeCodeBasicBlock::MoveLoadShiftStoreUp(int at) +{ + int j = at - 1; + while (j > 0) + { + if (mIns[j].mType == ASMIT_STA && mIns[j].mMode == ASMIM_ZERO_PAGE && mIns[j].mAddress == mIns[at + 0].mAddress) + { + if (mIns[j].mLive & LIVE_CPU_REG_C) + return false; + + if (mIns[j].mLive & LIVE_CPU_REG_A) + { + if (mIns[j + 1].mType == ASMIT_STA && !(mIns[j + 1].mLive & LIVE_CPU_REG_A)) + { + NativeCodeInstruction ins = mIns[j + 1]; + mIns[j + 1] = mIns[j]; + mIns[j] = ins; + mIns[j].mLive |= LIVE_CPU_REG_A; + j++; + } + else + return false; + } + + mIns[j].mLive |= LIVE_CPU_REG_A; + + mIns[at + 1].mLive |= mIns[j].mLive; + mIns[at + 2].mLive |= mIns[j].mLive; + + for (int i = j + 1; i < at; i++) + mIns[i].mLive |= LIVE_CPU_REG_C; + + mIns.Insert(j + 1, mIns[at + 2]); // STORE + mIns.Insert(j + 1, mIns[at + 2]); // SHIFT + + mIns[at + 2].mType = ASMIT_NOP; mIns[at + 2].mMode = ASMIM_IMPLIED; + mIns[at + 3].mType = ASMIT_NOP; mIns[at + 3].mMode = ASMIM_IMPLIED; + mIns[at + 4].mType = ASMIT_NOP; mIns[at + 4].mMode = ASMIM_IMPLIED; + return true; + } + + if ((mIns[at + 1].RequiresCarry() || (mIns[at + 2].mLive & LIVE_CPU_REG_C) != 0) && mIns[j].ChangesCarry()) + return false; + if (mIns[j].ChangesZeroPage(mIns[at + 0].mAddress)) + return false; + if (mIns[j].ChangesZeroPage(mIns[at + 2].mAddress)) + return false; + if (mIns[j].UsesZeroPage(mIns[at + 2].mAddress)) + return false; + + j--; + } + + return false; +} + +bool NativeCodeBasicBlock::MoveLoadShiftRotateUp(int at) +{ + int j = at - 1; + while (j > 0) + { + if (mIns[j].mType == ASMIT_STA && mIns[j].mMode == ASMIM_ZERO_PAGE && mIns[j].mAddress == mIns[at + 0].mAddress) + { + if (mIns[j].mLive & LIVE_CPU_REG_C) + return false; + + mIns[at + 1].mLive |= mIns[j].mLive; + mIns[at + 2].mLive |= mIns[j].mLive; + + for (int i = j + 1; i < at; i++) + mIns[i].mLive |= LIVE_CPU_REG_C; + + mIns[j].mLive |= LIVE_CPU_REG_A; + + mIns.Insert(j + 1, mIns[at + 2]); // ROR ZP + mIns.Insert(j + 1, mIns[at + 2]); // LSR + + mIns[at + 2].mType = ASMIT_NOP; mIns[at + 2].mMode = ASMIM_IMPLIED; + mIns[at + 3].mType = ASMIT_NOP; mIns[at + 3].mMode = ASMIM_IMPLIED; + mIns[at + 4].mType = ASMIT_NOP; mIns[at + 4].mMode = ASMIM_IMPLIED; + return true; + } + + if (mIns[j].ChangesZeroPage(mIns[at + 0].mAddress)) + return false; + if (mIns[j].ChangesZeroPage(mIns[at + 2].mAddress)) + return false; + if (mIns[j].UsesZeroPage(mIns[at + 2].mAddress)) + return false; + + j--; + } + + return false; +} + +bool NativeCodeBasicBlock::MoveLoadAddZPStoreUp(int at) +{ + int j = at - 1; + while (j > 0) + { + if (mIns[j].mType == ASMIT_STA && mIns[j].mMode == ASMIM_ZERO_PAGE && mIns[j].mAddress == mIns[at + 0].mAddress) + { + while (j < at && (mIns[j].mLive & LIVE_CPU_REG_A)) + { + if (mIns[j].ChangesAccu()) + return false; + j++; + } + + mIns[at + 1].mLive |= mIns[j].mLive; + mIns[at + 2].mLive |= mIns[j].mLive; + + mIns[j].mLive |= LIVE_CPU_REG_A; + + mIns.Insert(j + 1, mIns[at + 2]); // STA + mIns.Insert(j + 1, mIns[at + 2]); // ADC + + mIns[at + 2].mType = ASMIT_NOP; mIns[at + 2].mMode = ASMIM_IMPLIED; + mIns[at + 3].mType = ASMIT_NOP; mIns[at + 3].mMode = ASMIM_IMPLIED; + mIns[at + 4].mType = ASMIT_NOP; mIns[at + 4].mMode = ASMIM_IMPLIED; + return true; + } + + if (mIns[j].ChangesZeroPage(mIns[at + 0].mAddress)) + return false; + if (mIns[j].ChangesZeroPage(mIns[at + 1].mAddress)) + return false; + if (mIns[j].UsesZeroPage(mIns[at + 2].mAddress)) + return false; + if (mIns[j].ChangesCarry()) + return false; + + j--; + } + + return false; +} + +bool NativeCodeBasicBlock::MoveZpImmTAYDown(int at) +{ + int reg = mIns[at].mAddress; + int j = at + 3; + while (j < mIns.Size()) + { + if (mIns[j].RequiresYReg()) + { + while (j > at + 3 && (mIns[j - 1].mLive & LIVE_CPU_REG_A)) + j--; + if (j > at + 3) + { + mIns.Insert(j + 0, mIns[at + 0]); + mIns.Insert(j + 1, mIns[at + 1]); + mIns.Insert(j + 2, mIns[at + 2]); + + mIns[j + 0].mLive |= mIns[j - 1].mLive; + mIns[j + 1].mLive |= mIns[j - 1].mLive; + mIns[j + 2].mLive |= mIns[j - 1].mLive; + + mIns[at + 0].mType = ASMIT_NOP; mIns[at + 0].mMode = ASMIM_IMPLIED; + mIns[at + 1].mType = ASMIT_NOP; mIns[at + 1].mMode = ASMIM_IMPLIED; + mIns[at + 2].mType = ASMIT_NOP; mIns[at + 2].mMode = ASMIM_IMPLIED; + return true; + } + + return false; + } + + if (mIns[j].ChangesYReg()) + return false; + if (mIns[j].ChangesZeroPage(reg)) + return false; + j++; + } + return false; +} + +bool NativeCodeBasicBlock::MoveCLCLoadAddZPStoreDown(int at) +{ + int j = at + 4; + while (j < mIns.Size()) + { + if (mIns[j].mType == ASMIT_LDA && mIns[j].mMode == ASMIM_ZERO_PAGE && mIns[j].mAddress == mIns[at + 3].mAddress) + { + if (j == at + 4) + return false; + if (mIns[j].mLive & LIVE_CPU_REG_C) + return false; + + mIns.Insert(j, mIns[at + 3]); // STA + mIns.Insert(j, mIns[at + 2]); // ADC + mIns.Insert(j, mIns[at + 1]); // LDA + mIns.Insert(j, mIns[at + 0]); // CLC + + mIns[j + 0].mLive |= mIns[j - 1].mLive; + mIns[j + 1].mLive |= mIns[j - 1].mLive; + mIns[j + 2].mLive |= mIns[j - 1].mLive; + mIns[j + 3].mLive |= mIns[j - 1].mLive; + + mIns[at + 0].mType = ASMIT_NOP; mIns[at + 0].mMode = ASMIM_IMPLIED; + mIns[at + 1].mType = ASMIT_NOP; mIns[at + 1].mMode = ASMIM_IMPLIED; + mIns[at + 2].mType = ASMIT_NOP; mIns[at + 2].mMode = ASMIM_IMPLIED; + mIns[at + 3].mType = ASMIT_NOP; mIns[at + 3].mMode = ASMIM_IMPLIED; + return true; + } + + if (mIns[j].ChangesZeroPage(mIns[at + 1].mAddress)) + return false; + if (mIns[j].ChangesZeroPage(mIns[at + 2].mAddress)) + return false; + if (mIns[j].UsesZeroPage(mIns[at + 3].mAddress)) + return false; + + j++; + } + + return false; +} + +//static bool PeepCheck = false; +//static bool PeepCheck = false; + +bool NativeCodeBasicBlock::ReverseBitfieldForwarding(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + bool cused = mExitRequiredRegs[CPU_REG_C], zused = mExitRequiredRegs[CPU_REG_Z]; + + int aused = 0x00; + + if (mTrueJump && mTrueJump->mEntryRequiredRegs[CPU_REG_A]) + aused = 0xff; + + if (mFalseJump) + { + if (mBranch == ASMIT_BCC || mBranch == ASMIT_BCS) + cused = true; + else if (mBranch == ASMIT_BEQ || mBranch == ASMIT_BNE || mBranch == ASMIT_BPL || mBranch == ASMIT_BMI) + zused = true; + + if (mFalseJump->mEntryRequiredRegs[CPU_REG_A]) + aused = 0xff; + } + + for (int i = mIns.Size() - 1; i >= 0; i--) + { + NativeCodeInstruction& ins(mIns[i]); + switch (ins.mType) + { + case ASMIT_TXA: + case ASMIT_TYA: + case ASMIT_LDA: + aused = 0; + zused = false; + break; + case ASMIT_CLC: + case ASMIT_SEC: + cused = false; + break; + case ASMIT_SBC: + if (!cused && !zused && ins.mMode == ASMIM_IMMEDIATE && i > 0 && mIns[i - 1].mType == ASMIT_SEC) + { + char um = BinMask(aused); + if (ins.mAddress & um) + aused = 0xff; + else if (ins.mAddress) + { + ins.mAddress = 0x00; + changed = true; + } + } + else + aused = 0xff; + cused = true; + zused = false; + break; + case ASMIT_ADC: + if (!cused && !zused && ins.mMode == ASMIM_IMMEDIATE && i > 0 && mIns[i - 1].mType == ASMIT_CLC) + { + char um = BinMask(aused); + if (ins.mAddress & um) + aused = 0xff; + else if (ins.mAddress) + { + ins.mAddress = 0x00; + changed = true; + } + } + else + aused = 0xff; + cused = true; + zused = false; + break; + + case ASMIT_JSR: + cused = true; + zused = false; + aused = 0xff; + break; + case ASMIT_CMP: + cused = false; + zused = false; + aused = 0xff; + break; + case ASMIT_EOR: + if (zused) + aused = 0xff; + else if (ins.mMode == ASMIM_IMMEDIATE) + { + int naddr = ins.mAddress & aused; + if (ins.mAddress != naddr) + { + ins.mAddress = naddr; + changed = true; + } + } + zused = false; + break; + + case ASMIT_STA: + case ASMIT_TAX: + case ASMIT_TAY: + aused = 0xff; + zused = false; + break; + case ASMIT_LSR: + if (ins.mMode == ASMIM_IMPLIED) + { + if (zused) + aused = 0xfe; + else + aused = (aused << 1) & 0xff; + if (cused) + aused |= 1; + } + cused = false; + zused = false; + break; + case ASMIT_ASL: + if (ins.mMode == ASMIM_IMPLIED) + { + if (zused) + aused = 0x7f; + else + aused >>= 1; + if (cused) + aused |= 0x80; + } + cused = false; + zused = false; + break; + case ASMIT_ROR: + if (ins.mMode == ASMIM_IMPLIED) + { + if (zused) + { + aused = 0xfe; + if (cused) + aused |= 1; + cused = true; + } + else if (aused & 0x80) + { + aused = (aused << 1) & 0xff; + if (cused) + aused |= 1; + cused = true; + } + else + { + ins.mType = ASMIT_LSR; + aused = (aused << 1) & 0xff; + if (cused) + aused |= 1; + cused = false; + changed = true; + } + } + else + cused = true; + zused = false; + break; + case ASMIT_ROL: + if (ins.mMode == ASMIM_IMPLIED) + { + if (zused) + { + aused = 0x7f; + if (cused) + aused |= 0x80; + cused = true; + } + else if (aused & 0x01) + { + aused >>= 1; + if (cused) + aused |= 0x80; + cused = true; + } + else + { + ins.mType = ASMIT_ASL; + aused >>= 1; + if (cused) + aused |= 0x80; + cused = false; + changed = true; + } + } + else + cused = true; + zused = false; + break; + case ASMIT_ORA: + if (zused) + aused = 0xff; + else if (ins.mMode == ASMIM_IMMEDIATE) + { + int naddr = ins.mAddress & aused; + if (ins.mAddress != naddr) + { + ins.mAddress = naddr; + changed = true; + } + aused &= ~ins.mAddress; + } + break; + case ASMIT_AND: + if (zused) + aused = 0xff; + else if (ins.mMode == ASMIM_IMMEDIATE) + { + int naddr = ins.mAddress & aused; + if (ins.mAddress != naddr) + { + ins.mAddress = naddr; + changed = true; + } + aused &= ins.mAddress; + } + zused = false; + break; + } + } + + if (mTrueJump && mTrueJump->ReverseBitfieldForwarding()) + changed = true; + if (mFalseJump && mFalseJump->ReverseBitfieldForwarding()) + changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::RemoveLocalUnusedLinkerObjects(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + + for (int i = 0; i < mIns.Size(); i++) + { + NativeCodeInstruction& ins(mIns[i]); + if (ins.mLinkerObject && (ins.mLinkerObject->mFlags & LOBJF_LOCAL_VAR) && !(ins.mLinkerObject->mFlags & LOBJF_LOCAL_USED)) + { + ins.mType = ASMIT_NOP; + ins.mMode = ASMIM_IMPLIED; + changed = true; + } + } + + if (mTrueJump && mTrueJump->RemoveLocalUnusedLinkerObjects()) + changed = true; + if (mFalseJump && mFalseJump->RemoveLocalUnusedLinkerObjects()) + changed = true; + } + + return changed; +} + +void NativeCodeBasicBlock::MarkLocalUsedLinkerObjects(void) +{ + if (!mVisited) + { + mVisited = true; + + for (int i = 0; i < mIns.Size(); i++) + { + NativeCodeInstruction& ins(mIns[i]); + if (ins.mLinkerObject) + { + if (ins.mMode == ASMIM_IMMEDIATE_ADDRESS || ins.UsesAddress()) + ins.mLinkerObject->mFlags |= LOBJF_LOCAL_USED; + } + } + + if (mTrueJump) mTrueJump->MarkLocalUsedLinkerObjects(); + if (mFalseJump) mFalseJump->MarkLocalUsedLinkerObjects(); + } +} + +bool NativeCodeBasicBlock::AbsoluteValueForwarding(const ExpandingArray & npairs) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + ExpandingArray pairs; + if (mNumEntries == 1) + pairs = npairs; + + int ains = -1; + + for (int i = 0; i < mIns.Size(); i++) + { + // Check content of accu + if (mIns[i].mType == ASMIT_LDA) + { + if (mIns[i].mMode == ASMIM_IMMEDIATE || mIns[i].mMode == ASMIM_ZERO_PAGE) + ains = i; + else if (mIns[i].mMode == ASMIM_ABSOLUTE && !(mIns[i].mFlags & NCIF_VOLATILE)) + { + int j = 0; + while (j < pairs.Size() && !pairs[j].mStore.SameEffectiveAddress(mIns[i])) + j++; + if (j < pairs.Size()) + { + mIns[i].CopyMode(pairs[j].mLoad); + ains = i; + changed = true; + } + else + ains = -1; + } + else + ains = -1; + } + else if (mIns[i].ChangesAccu()) + ains = -1; + else if (ains >= 0 && mIns[ains].MayBeChangedOnAddress(mIns[i])) + ains = -1; + + if (ains >= 0 && mIns[i].mType == ASMIT_STA && mIns[i].mMode == ASMIM_ABSOLUTE && !(mIns[i].mFlags & NCIF_VOLATILE)) + { + int j = 0; + while (j < pairs.Size() && !pairs[j].mStore.SameEffectiveAddress(mIns[i])) + j++; + if (j < pairs.Size()) + { + pairs[j].mLoad = mIns[ains]; + pairs[j].mStore = mIns[i]; + } + else + { + NativeCodeLoadStorePair pair; + pair.mLoad = mIns[ains]; + pair.mStore = mIns[i]; + pairs.Push(pair); + } + } + else if (mIns[i].mType == ASMIT_JSR || (mIns[i].mFlags & NCIF_MEMMAP)) + { + pairs.SetSize(0); + ains = -1; + } + else if (mIns[i].ChangesAddress()) + { + int j = 0, k = 0; + while (j < pairs.Size()) + { + if (!pairs[j].mLoad.MayBeChangedOnAddress(mIns[i]) && !pairs[j].mStore.MayBeChangedOnAddress(mIns[i])) + { + if (k != j) + pairs[k] = pairs[j]; + k++; + } + j++; + } + pairs.SetSize(k); + } + } + + if (mTrueJump && mTrueJump->AbsoluteValueForwarding(pairs)) + changed = true; + if (mFalseJump && mFalseJump->AbsoluteValueForwarding(pairs)) + changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::CrossBlockShiftForwarding(const ValueReuseDataSet& data) +{ + bool changed = false; + + if (!mVisited) + { + mNReuseDataSet = data; + + assert(mIndex == 1000 || mNumEntries == mEntryBlocks.Size()); + + if (mLoopHead) + { + mNReuseDataSet.Reset(); + } + else if (mNumEntries > 0) + { + if (mNumEntered > 0) + mNReuseDataSet.Intersect(mReuseDataSet); + + mNumEntered++; + + if (mNumEntered < mNumEntries) + { + mReuseDataSet = mNReuseDataSet; + return false; + } + } + + for (int i = 0; i < mIns.Size(); i++) + { + if (mIns[i].mMode == ASMIM_ZERO_PAGE) + { + int reg = mIns[i].mAddress; + + if (i + 1 < mIns.Size() && + mIns[i].mType == ASMIT_LDA && + mIns[i + 1].IsShift() && mIns[i + 1].mMode == ASMIM_IMPLIED && !(mIns[i + 1].mLive & LIVE_CPU_REG_A)) + { + mNReuseDataSet.mRegs[reg].mBlock = this; + mNReuseDataSet.mRegs[reg].mIndex = i; + } + else if (mNReuseDataSet.mRegs[reg].mBlock) + { + NativeCodeBasicBlock* block = mNReuseDataSet.mRegs[reg].mBlock; + int index = mNReuseDataSet.mRegs[reg].mIndex; + + if (mIns[i].mType == block->mIns[index + 1].mType && !(mIns[i].mLive & (LIVE_CPU_REG_C | LIVE_CPU_REG_Z))) + { + block->mIns[index].mType = mIns[i].mType; + block->mIns[index + 1].mType = ASMIT_NOP; + mIns[i].mType = ASMIT_NOP; + mIns[i].mMode = ASMIM_IMPLIED; + changed = true; + } + + mNReuseDataSet.mRegs[reg].mBlock = nullptr; + } + } + else if (mIns[i].mType == ASMIT_JSR) + { + mNReuseDataSet.ResetCall(mIns[i]); + } + else if (mIns[i].mMode == ASMIM_INDIRECT_Y) + { + int reg = mIns[i].mAddress; + mNReuseDataSet.mRegs[reg].mBlock = nullptr; + mNReuseDataSet.mRegs[reg + 1].mBlock = nullptr; + } + } + + mVisited = true; + + mFReuseDataSet = mNReuseDataSet; + + if (this->mTrueJump && this->mTrueJump->CrossBlockShiftForwarding(mNReuseDataSet)) + changed = true; + if (this->mFalseJump && this->mFalseJump->CrossBlockShiftForwarding(mFReuseDataSet)) + changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::OffsetValueForwarding(const ValueNumberingDataSet& data) +{ + bool changed = false; + + if (!mVisited) + { + mNNumDataSet = data; + + assert(mIndex == 1000 || mNumEntries == mEntryBlocks.Size()); + + if (mLoopHead) + { + mNNumDataSet.Reset(); + } + else if (mNumEntries > 0) + { + if (mNumEntered > 0) + mNNumDataSet.Intersect(mNumDataSet); + + mNumEntered++; + + if (mNumEntered < mNumEntries) + { + mNumDataSet = mNNumDataSet; + return false; + } + } + + mVisited = true; + + int carry = -1; + + for (int i = 0; i < mIns.Size(); i++) + { + NativeCodeInstruction& ins(mIns[i]); + + if (ins.mType == ASMIT_JSR) + { + mNNumDataSet.ResetCall(ins); + carry = -1; + } + else if (ins.mType == ASMIT_LDA && ins.mMode == ASMIM_ZERO_PAGE) + { + int reg = ins.mAddress; + + if (carry != -1 && i + 1 < mIns.Size() && mIns[i + 1].mMode == ASMIM_IMMEDIATE && + (mIns[i + 1].mType == ASMIT_ADC || mIns[i + 1].mType == ASMIT_SBC) && !(mIns[i + 1].mLive & LIVE_CPU_REG_C) && + mNNumDataSet.mRegs[reg].SameBase(mNNumDataSet.mRegs[CPU_REG_A])) + { + ins.mType = ASMIT_NOP; ins.mMode = ASMIM_IMPLIED; + if (mIns[i + 1].mType == ASMIT_ADC) + mIns[i + 1].mAddress = (mIns[i + 1].mAddress + (mNNumDataSet.mRegs[reg].mOffset - mNNumDataSet.mRegs[CPU_REG_A].mOffset)) & 0xff; + else + mIns[i + 1].mAddress = (mIns[i + 1].mAddress - (mNNumDataSet.mRegs[reg].mOffset - mNNumDataSet.mRegs[CPU_REG_A].mOffset)) & 0xff; + changed = true; + } + else + mNNumDataSet.mRegs[CPU_REG_A] = mNNumDataSet.mRegs[reg]; + } + else if (ins.mType == ASMIT_STA && ins.mMode == ASMIM_ZERO_PAGE) + { + int reg = ins.mAddress; + + if (mNNumDataSet.mRegs[reg].SameBase(mNNumDataSet.mRegs[CPU_REG_A])) + { + int d = mNNumDataSet.mRegs[CPU_REG_A].mOffset - mNNumDataSet.mRegs[reg].mOffset; + if (d == 0) + { + ins.mType = ASMIT_NOP; ins.mMode = ASMIM_IMPLIED; + changed = true; + } + else if (d == 1 && !(ins.mLive & (LIVE_CPU_REG_Z | LIVE_CPU_REG_C))) + { + ins.mType = ASMIT_INC; + if (ins.mLive & LIVE_CPU_REG_A) + mIns.Insert(i + 1, NativeCodeInstruction(ins.mIns, ASMIT_LDA, ins)); + changed = true; + } + else if (d == -1 && !(ins.mLive & (LIVE_CPU_REG_Z | LIVE_CPU_REG_C))) + { + ins.mType = ASMIT_DEC; + if (ins.mLive & LIVE_CPU_REG_A) + mIns.Insert(i + 1, NativeCodeInstruction(ins.mIns, ASMIT_LDA, ins)); + changed = true; + } + } + + mNNumDataSet.mRegs[reg] = mNNumDataSet.mRegs[CPU_REG_A]; + } + else if (ins.mType == ASMIT_ADC && ins.mMode == ASMIM_IMMEDIATE && carry != -1) + { + mNNumDataSet.mRegs[CPU_REG_A].mOffset += ins.mAddress + carry; + if (!(ins.mLive & LIVE_CPU_REG_C)) + { + int k = mNNumDataSet.Find(mNNumDataSet.mRegs[CPU_REG_A]); + if (k >= 0) + { + ins.mType = ASMIT_LDA; + ins.mMode = ASMIM_ZERO_PAGE; + ins.mAddress = k; + changed = true; + } + } + } + else if (ins.mType == ASMIT_SBC && ins.mMode == ASMIM_IMMEDIATE && carry != -1) + { + mNNumDataSet.mRegs[CPU_REG_A].mOffset -= ins.mAddress + 1 - carry; + if (!(ins.mLive & LIVE_CPU_REG_C)) + { + int k = mNNumDataSet.Find(mNNumDataSet.mRegs[CPU_REG_A]); + if (k >= 0) + { + ins.mType = ASMIT_LDA; + ins.mMode = ASMIM_ZERO_PAGE; + ins.mAddress = k; + changed = true; + } + } + } + else if (ins.ChangesAccu()) + mNNumDataSet.mRegs[CPU_REG_A].Reset(); + else if (ins.mMode == ASMIM_ZERO_PAGE && ins.ChangesAddress()) + mNNumDataSet.mRegs[ins.mAddress].Reset(); + + if (ins.mType == ASMIT_CLC) + carry = 0; + else if (ins.mType == ASMIT_SEC) + carry = 1; + else if (ins.ChangesCarry()) + carry = -1; + } + + mFNumDataSet = mNNumDataSet; + + if (this->mTrueJump && this->mTrueJump->OffsetValueForwarding(mNNumDataSet)) + changed = true; + if (this->mFalseJump && this->mFalseJump->OffsetValueForwarding(mFNumDataSet)) + changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::CheckLoopIndexXRegisters(NativeCodeBasicBlock* head, int xreg) +{ + for (int i = mIns.Size() - 1; i >= 0; i--) + { + if ((mIns[i].mType == ASMIT_LDX || mIns[i].mType == ASMIT_STX) && mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i].mAddress == xreg) + return true; + else if (mIns[i].ChangesXReg() || mIns[i].ChangesZeroPage(xreg)) + return false; + } + + if (this == head) + return true; + + if (mLoopHead) + return false; + + for (int i = 0; i < mEntryBlocks.Size(); i++) + { + if (!mEntryBlocks[i]->CheckLoopIndexXRegisters(head, xreg)) + return false; + } + + return true; +} + +bool NativeCodeBasicBlock::AbsoluteLocalRegisterValueReuse(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + int ains = -1, xins = -1, yins = -1; + + mIns.Reserve(mIns.Size() + 10); + for (int i = 0; i < mIns.Size(); i++) + { + NativeCodeInstruction& ins(mIns[i]); + + if ((ins.mType == ASMIT_INC || ins.mType == ASMIT_DEC) && i + 1 < mIns.Size() && mIns[i + 1].mType == ins.mType && mIns[i + 1].SameEffectiveAddress(ins)) + { + if (xins >= 0 && ins.SameEffectiveAddress(mIns[xins]) && !(ins.mLive & LIVE_CPU_REG_X)) + { + AsmInsType itype = ins.mType == ASMIT_INC ? ASMIT_INX : ASMIT_DEX; + ins.mType = ASMIT_STX; + mIns.Remove(i + 1, 1); + mIns.Insert(i, NativeCodeInstruction(ins.mIns, itype)); + mIns.Insert(i, NativeCodeInstruction(ins.mIns, itype)); + for (int j = 0; j < i; j++) + mIns[j].mLive |= LIVE_CPU_REG_X; + changed = true; + } + } + + if (ains >= 0 && mIns[ains].MayBeChangedOnAddress(ins)) ains = -1; + if (xins >= 0 && mIns[xins].MayBeChangedOnAddress(ins)) xins = -1; + if (yins >= 0 && mIns[yins].MayBeChangedOnAddress(ins)) yins = -1; + + if (ins.ChangesAccu()) + { + ains = -1; + } + + if (ins.ChangesXReg()) + { + xins = -1; + if (ains >= 0 && mIns[ains].RequiresXReg()) ains = -1; + if (yins >= 0 && mIns[yins].RequiresXReg()) yins = -1; + } + + if (ins.ChangesYReg()) + { + yins = -1; + if (ains >= 0 && mIns[ains].RequiresYReg()) ains = -1; + if (xins >= 0 && mIns[xins].RequiresYReg()) xins = -1; + } + + if (ins.mType == ASMIT_LDA || ins.mType == ASMIT_STA) + ains = i; + else if (ins.mType == ASMIT_LDX || ins.mType == ASMIT_STX) + xins = i; + else if (ins.mType == ASMIT_LDY || ins.mType == ASMIT_STY) + yins = i; + } + + if (mTrueJump && mTrueJump->AbsoluteLocalRegisterValueReuse()) + changed = true; + if (mFalseJump && mFalseJump->AbsoluteLocalRegisterValueReuse()) + changed = true; + } + + return changed; +} + + +bool NativeCodeBasicBlock::CheckLoopIndexYRegisters(NativeCodeBasicBlock* head, int yreg) +{ + for (int i = mIns.Size() - 1; i >= 0; i--) + { + if ((mIns[i].mType == ASMIT_LDY || mIns[i].mType == ASMIT_STY) && mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i].mAddress == yreg) + return true; + else if (mIns[i].ChangesYReg() || mIns[i].ChangesZeroPage(yreg)) + return false; + } + + if (this == head) + return true; + + if (mLoopHead) + return false; + + for (int i = 0; i < mEntryBlocks.Size(); i++) + { + if (!mEntryBlocks[i]->CheckLoopIndexYRegisters(head, yreg)) + return false; + } + + return true; +} + +bool NativeCodeBasicBlock::ReduceIndexXYZeroShuffle(NativeCodeBasicBlock* from, int xreg, int yreg) +{ + bool changed = false; + + if (!mVisited) + { + if (mLoopHead) + { + if (xreg >= 0) + { + for (int i = 0; i < mEntryBlocks.Size(); i++) + { + if (mEntryBlocks[i] != from && !mEntryBlocks[i]->CheckLoopIndexXRegisters(this, xreg)) + { + xreg = -1; + break; + } + } + } + if (yreg >= 0) + { + for (int i = 0; i < mEntryBlocks.Size(); i++) + { + if (mEntryBlocks[i] != from && !mEntryBlocks[i]->CheckLoopIndexYRegisters(this, yreg)) + { + yreg = -1; + break; + } + } + } + } + else if (mNumEntries > 0) + { + if (mNumEntered > 0) + { + if (xreg != mXReg) + xreg = -1; + if (yreg != mYReg) + yreg = -1; + } + + mXReg = xreg; + mYReg = yreg; + + mNumEntered++; + + if (mNumEntered < mNumEntries) + return false; + } + + mVisited = true; + + if (yreg >= 0) + { + int ix = 0, iy = 0; + while (ix < mIns.Size() && !mIns[ix].ReferencesXReg()) + ix++; + while (iy < mIns.Size() && !mIns[iy].ChangesYReg() && mIns[iy].mMode != ASMIM_INDIRECT_Y && mIns[iy].mType != ASMIT_STY) + iy++; + + int ex = ix; + while (ex < mIns.Size() && (mIns[ex].mLive & LIVE_CPU_REG_X)) + ex++; + + if (ix < mIns.Size() && iy == mIns.Size() && mIns[ix].mType == ASMIT_LDX && mIns[ix].mMode == ASMIM_ZERO_PAGE && mIns[ix].mAddress == yreg && + (ex < mIns.Size() || !mExitRequiredRegs[CPU_REG_X]) && (!ChangesXReg(ix + 1, ex) || !ReferencesYReg(ix + 1))) + { + for (int i = 0; i < ix; i++) + mIns[i].mLive |= LIVE_CPU_REG_Y; + + const InterInstruction* iins = nullptr; + + mIns[ix].mType = ASMIT_NOP; mIns[ix].mMode = ASMIM_IMPLIED; + while (ix < mIns.Size() && (mIns[ix].mLive & LIVE_CPU_REG_X)) + { + ix++; + mIns[ix].ReplaceXRegWithYReg(); + changed = true; + } + } + else if (ix < mIns.Size() && mIns[ix].mType == ASMIT_LDX && mIns[ix].mMode == ASMIM_ZERO_PAGE && mIns[ix].mAddress == yreg && ((iy > ix) || mIns[iy].mType == ASMIT_LDY)) + { + for (int i = 0; i < iy; i++) + { + if (mIns[i].mMode == ASMIM_ABSOLUTE_Y) + { + mIns[i].mMode = ASMIM_ABSOLUTE_X; + changed = true; + } + } + if (changed) + { + mIns.Insert(0, NativeCodeInstruction(mIns[ix].mIns, ASMIT_LDX, ASMIM_ZERO_PAGE, yreg)); + mIns.Remove(ix + 1); + } + } + } + + for (int i = 0; i < mIns.Size(); i++) + { + if (i + 1 < mIns.Size() && + mIns[i].mType == ASMIT_LDX && xreg >= 0 && + HasAsmInstructionMode(ASMIT_LDY, mIns[i].mMode) && + !(mIns[i].mLive & LIVE_CPU_REG_Y) && + mIns[i + 1].mMode == ASMIM_ABSOLUTE_X && + HasAsmInstructionMode(mIns[i + 1].mType, ASMIM_ABSOLUTE_Y) && + !(mIns[i].mLive & (LIVE_CPU_REG_X | LIVE_CPU_REG_Y))) + { + int j = i + 2; + while (j < mIns.Size() && !mIns[j].ReferencesXReg()) + j++; + if (j < mIns.Size() && mIns[j].mType == ASMIT_LDX && mIns[j].mMode == ASMIM_ZERO_PAGE && mIns[j].mAddress == xreg) + { + mIns[i].mType = ASMIT_LDY; + mIns[i + 1].mMode = ASMIM_ABSOLUTE_Y; + changed = true; + } + } + + if (mIns[i].mType == ASMIT_LDX && mIns[i].mMode == ASMIM_ZERO_PAGE) + xreg = mIns[i].mAddress; + else if (mIns[i].mType == ASMIT_LDY && mIns[i].mMode == ASMIM_ZERO_PAGE) + yreg = mIns[i].mAddress; + else if (mIns[i].mType == ASMIT_STX && mIns[i].mMode == ASMIM_ZERO_PAGE && xreg == -1) + { + xreg = mIns[i].mAddress; + if (yreg == mIns[i].mAddress) + yreg = -1; + } + else if (mIns[i].mType == ASMIT_STY && mIns[i].mMode == ASMIM_ZERO_PAGE && yreg == -1) + { + yreg = mIns[i].mAddress; + if (xreg == mIns[i].mAddress) + xreg = -1; + } + else if (xreg >=0 && xreg == yreg && mIns[i].mMode == ASMIM_ABSOLUTE_Y && HasAsmInstructionMode(mIns[i].mType, ASMIM_ABSOLUTE_X)) + mIns[i].mMode = ASMIM_ABSOLUTE_X; + else + { + if (mIns[i].ChangesXReg()) + xreg = -1; + if (mIns[i].ChangesYReg()) + yreg = -1; + if (xreg >= 0 && mIns[i].ChangesZeroPage(xreg)) + xreg = -1; + if (yreg >= 0 && mIns[i].ChangesZeroPage(yreg)) + yreg = -1; + } + } + + if (mTrueJump && mTrueJump->ReduceIndexXYZeroShuffle(this, xreg, yreg)) + changed = true; + if (mFalseJump && mFalseJump->ReduceIndexXYZeroShuffle(this, xreg, yreg)) + changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::IndexXYValueForwarding(int xreg, int xoffset, int xvalue, int yreg, int yoffset, int yvalue) +{ + bool changed = false; + + if (!mVisited) + { + if (mLoopHead) + { + xreg = -1; + yreg = -1; + } + else if (mNumEntries > 0) + { + if (mNumEntered > 0) + { + if (xreg != mXReg || xoffset != mXOffset || xvalue != mXValue) + xreg = -1; + if (yreg != mYReg || yoffset != mYOffset || yvalue != mYValue) + yreg = -1; + } + + mXReg = xreg; mXOffset = xoffset; mXValue = xvalue; + mYReg = yreg; mYOffset = yoffset; mYValue = yvalue; + + mNumEntered++; + + if (mNumEntered < mNumEntries) + return false; + } + + for (int i = 0; i < mIns.Size(); i++) + { + if (mIns[i].mType == ASMIT_INY) + yoffset = (yoffset + 1) & 255; + else if (mIns[i].mType == ASMIT_DEY) + yoffset = (yoffset - 1) & 255; + else if (mIns[i].mType == ASMIT_INX) + xoffset = (xoffset + 1) & 255; + else if (mIns[i].mType == ASMIT_DEX) + xoffset = (xoffset - 1) & 255; + else if (mIns[i].mType == ASMIT_LDX) + { + if (mIns[i].mMode == ASMIM_ZERO_PAGE) + { + if (mIns[i].mAddress == xreg) + { + if (xoffset == 0) + { + if (!(mIns[i].mLive & LIVE_CPU_REG_Z)) + { + mIns[i].mType = ASMIT_NOP; mIns[i].mMode = ASMIM_IMPLIED; + changed = true; + } + else if (!(mIns[i].mLive & LIVE_CPU_REG_A)) + { + mIns[i].mType = ASMIT_TXA; mIns[i].mMode = ASMIM_IMPLIED; + changed = true; + } + } + else if (xoffset <= 3) + { + mIns[i].mType = ASMIT_NOP; mIns[i].mMode = ASMIM_IMPLIED; + for (int j = 0; j < xoffset; j++) + mIns.Insert(i + 1, NativeCodeInstruction(mIns[i].mIns, ASMIT_DEX)); + changed = true; + } + else if (xoffset >= 253) + { + mIns[i].mType = ASMIT_NOP; mIns[i].mMode = ASMIM_IMPLIED; + for (int j = xoffset; j < 256; j++) + mIns.Insert(i + 1, NativeCodeInstruction(mIns[i].mIns, ASMIT_INX)); + changed = true; + } + else + xoffset = 0; + } + else if (i + 1 < mIns.Size() && mIns[i].mAddress == yreg && yoffset == 0 && + mIns[i + 1].mType == ASMIT_LDY && mIns[i + 1].mMode == ASMIM_ABSOLUTE_X && !(mIns[i].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_X))) + { + mIns[i] = NativeCodeInstruction(mIns[i + 1].mIns, ASMIT_LDA, mIns[i + 1]); + mIns[i].mMode = ASMIM_ABSOLUTE_Y; + mIns[i + 1].mType = ASMIT_TAY; mIns[i + 1].mMode = ASMIM_IMPLIED; + changed = true; + } + else + { + xreg = mIns[i].mAddress; + xoffset = 0; + xvalue = GlobalValueNumber++; + } + } + else + xreg = -1; + } + else if (mIns[i].mType == ASMIT_LDY) + { + if (mIns[i].mMode == ASMIM_ZERO_PAGE) + { + if (mIns[i].mAddress == yreg) + { + if (yoffset == 0) + { + if (!(mIns[i].mLive & LIVE_CPU_REG_Z)) + { + mIns[i].mType = ASMIT_NOP; mIns[i].mMode = ASMIM_IMPLIED; + changed = true; + } + else if (!(mIns[i].mLive & LIVE_CPU_REG_A)) + { + mIns[i].mType = ASMIT_TYA; mIns[i].mMode = ASMIM_IMPLIED; + changed = true; + } + } + else if (yoffset <= 3) + { + mIns[i].mType = ASMIT_NOP; mIns[i].mMode = ASMIM_IMPLIED; + for (int j = 0; j < yoffset; j++) + mIns.Insert(i + 1, NativeCodeInstruction(mIns[i].mIns, ASMIT_DEY)); + changed = true; + } + else if (yoffset >= 253) + { + mIns[i].mType = ASMIT_NOP; mIns[i].mMode = ASMIM_IMPLIED; + for (int j = yoffset; j < 256; j++) + mIns.Insert(i + 1, NativeCodeInstruction(mIns[i].mIns, ASMIT_INY)); + changed = true; + } + else + yoffset = 0; + } + else + { + yreg = mIns[i].mAddress; + yoffset = 0; + yvalue = GlobalValueNumber++; + } + } + else + yreg = -1; + } + else if (mIns[i].mType == ASMIT_STY && mIns[i].mMode == ASMIM_ZERO_PAGE && yreg == -1) + { + if (xreg >= 0 && mIns[i].ChangesZeroPage(xreg)) + xreg = -1; + yreg = mIns[i].mAddress; + yoffset = 0; + yvalue = GlobalValueNumber++; + } + else if (mIns[i].mType == ASMIT_STX && mIns[i].mMode == ASMIM_ZERO_PAGE && xreg == -1) + { + if (yreg >= 0 && mIns[i].ChangesZeroPage(yreg)) + yreg = -1; + xreg = mIns[i].mAddress; + xoffset = 0; + xvalue = GlobalValueNumber++; + } + else if (i + 2 < mIns.Size() && + mIns[i].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && !(mIns[i + 2].mLive & LIVE_CPU_REG_C)) + { + if (mIns[i + 1].mAddress == yreg) + { + mIns[i + 1].mType = ASMIT_TYA; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mAddress = (mIns[i + 2].mAddress - yoffset) & 255; + changed = true; + } + else if (i + 3 < mIns.Size() && mIns[i + 3].mType == ASMIT_TAY) + { + yreg = mIns[i + 1].mAddress; + yoffset = mIns[i + 2].mAddress; + yvalue = GlobalValueNumber++; + i += 3; + } + else if (mIns[i + 1].mAddress == xreg) + { + mIns[i + 1].mType = ASMIT_TXA; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mAddress = (mIns[i + 2].mAddress - xoffset) & 255; + changed = true; + } + else if (i + 3 < mIns.Size() && mIns[i + 3].mType == ASMIT_TAX) + { + xreg = mIns[i + 1].mAddress; + xoffset = mIns[i + 2].mAddress; + xvalue = GlobalValueNumber++; + i += 3; + } + } + else if (i + 2 < mIns.Size() && + mIns[i].mType == ASMIT_LDA && HasAsmInstructionMode(ASMIT_ADC, mIns[i].mMode) && + mIns[i + 1].mType == ASMIT_CLC && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_ZERO_PAGE) + { + if (yreg == mIns[i + 2].mAddress && yoffset == 0) + { + mIns[i + 2].CopyMode(mIns[i]); + mIns[i].mType = ASMIT_TYA; mIns[i].mMode = ASMIM_IMPLIED; + } + else if (xreg == mIns[i + 2].mAddress && xoffset == 0) + { + mIns[i + 2].CopyMode(mIns[i]); + mIns[i].mType = ASMIT_TXA; mIns[i].mMode = ASMIM_IMPLIED; + } + } + else + { + if (mIns[i].ChangesXReg()) + xreg = -1; + if (mIns[i].ChangesYReg()) + yreg = -1; + if (xreg >= 0 && mIns[i].ChangesZeroPage(xreg)) + xreg = -1; + if (yreg >= 0 && mIns[i].ChangesZeroPage(yreg)) + yreg = -1; + } + } + + mVisited = true; + + if (this->mTrueJump && this->mTrueJump->IndexXYValueForwarding(xreg, xoffset, xvalue, yreg, yoffset, yvalue)) + changed = true; + if (this->mFalseJump && this->mFalseJump->IndexXYValueForwarding(xreg, xoffset, xvalue, yreg, yoffset, yvalue)) + changed = true; + } + + return changed; +} + + + +bool NativeCodeBasicBlock::BitFieldForwarding(const NativeRegisterDataSet& data) +{ + bool changed = false; + + if (!mVisited) + { + mNDataSet = data; + + assert(mIndex == 1000 || mNumEntries == mEntryBlocks.Size()); + + if (mLoopHead) + { + mNDataSet.ResetMask(); + } + else if (mNumEntries > 0) + { + if (mNumEntered > 0) + mNDataSet.IntersectMask(mDataSet); + + mNumEntered++; + + if (mNumEntered < mNumEntries) + { + mDataSet = mNDataSet; + return false; + } + } + + mVisited = true; + + for (int i = 0; i < mIns.Size(); i++) + { + AsmInsType carryop = ASMIT_NOP; + +#if 1 + if (i + 3 < mIns.Size() && + mIns[i + 0].mType == ASMIT_STA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && mIns[i + 1].mAddress != mIns[i + 0].mAddress && + mIns[i + 2].mType == ASMIT_AND && mIns[i + 2].mMode == ASMIM_IMMEDIATE && + mIns[i + 3].mType == ASMIT_ORA && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && mIns[i + 3].mAddress == mIns[i + 0].mAddress && + (mNDataSet[CPU_REG_A].mMask & mIns[i + 2].mAddress) == mIns[i + 2].mAddress && + (mNDataSet[CPU_REG_A].mValue & mIns[i + 2].mAddress) == 0) + { + + mIns[i + 1].mType = ASMIT_EOR; + mIns[i + 2].mAddress = mIns[i + 2].mAddress ^ 0xff; + mIns[i + 3].mType = ASMIT_EOR; + mIns[i + 3].mAddress = mIns[i + 1].mAddress; + changed = true; + } + if (i + 3 < mIns.Size() && + mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_EOR && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && mIns[i + 1].mAddress != mIns[i + 0].mAddress && + mIns[i + 2].mType == ASMIT_AND && mIns[i + 2].mMode == ASMIM_IMMEDIATE && + mIns[i + 3].mType == ASMIT_EOR && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && mIns[i + 3].mAddress == mIns[i + 1].mAddress) + { + int z0 = mNDataSet[mIns[i + 0].mAddress].mMask & ~mNDataSet[mIns[i + 0].mAddress].mValue; + if (!((~z0 & ~mIns[i + 2].mAddress) & 0xff)) + { + mIns[i + 3].CopyMode(mIns[i + 0]); + mIns[i + 3].mType = ASMIT_ORA; + mIns[i + 0].CopyMode(mIns[i + 1]); + mIns[i + 2].mAddress = ~mIns[i + 2].mAddress & 0xff; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + changed = true; + } + } + if (i + 4 < mIns.Size() && + mIns[i + 0].mType == ASMIT_STA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && mIns[i + 1].mAddress != mIns[i + 0].mAddress && + mIns[i + 2].mType == ASMIT_AND && mIns[i + 2].mMode == ASMIM_IMMEDIATE && + mIns[i + 3].mType == ASMIT_CLC && + mIns[i + 4].mType == ASMIT_ADC && mIns[i + 4].mMode == ASMIM_ZERO_PAGE && mIns[i + 4].mAddress == mIns[i + 0].mAddress && + !(mIns[i + 4].mLive & (LIVE_MEM | LIVE_CPU_REG_Z)) + ) + { + uint8 zeroa = mNDataSet[CPU_REG_A].mMask & ~mNDataSet[CPU_REG_A].mValue; + uint8 zeroi = ~mIns[i + 2].mAddress & 255; + + if (zeroi == LowBinMask(zeroi) && (zeroa & zeroi) == zeroi) + { + mIns[i + 0] = mIns[i + 3]; + mIns[i + 0].mLive |= LIVE_CPU_REG_C | LIVE_CPU_REG_A; + mIns[i + 1].mType = ASMIT_ADC; + mIns[i + 1].mLive |= LIVE_CPU_REG_C; + mIns[i + 3].mType = ASMIT_NOP; mIns[i + 3].mMode = ASMIM_IMPLIED; + mIns[i + 4].mType = ASMIT_NOP; mIns[i + 4].mMode = ASMIM_IMPLIED; + changed = true; + } + } + + if (i + 2 < mIns.Size() && + mIns[i + 0].mType == ASMIT_TXA && mIns[i + 1].mType == ASMIT_ORA && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mType == ASMIT_TAX) + { + if (mIns[i + 1].mAddress == 1 && (mNDataSet[CPU_REG_X].mMask & 1)) + { + if (mNDataSet[CPU_REG_X].mValue & 1) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + } + else + { + mIns[i + 0].mType = ASMIT_INX; mIns[i + 0].mLive |= LIVE_CPU_REG_X; + } + + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_TXA; mIns[i + 2].mMode = ASMIM_IMPLIED; + } + else if (mIns[i + 1].mAddress == 2 && (mNDataSet[CPU_REG_X].mMask & 2)) + { + if (mNDataSet[CPU_REG_X].mValue & 2) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + } + else + { + mIns[i + 0].mType = ASMIT_INX; mIns[i + 0].mLive |= LIVE_CPU_REG_X; + mIns[i + 1].mType = ASMIT_INX; mIns[i + 1].mMode = ASMIM_IMPLIED; mIns[i + 1].mLive |= LIVE_CPU_REG_X; + } + + mIns[i + 2].mType = ASMIT_TXA; mIns[i + 2].mMode = ASMIM_IMPLIED; + } + } + + if (i + 2 < mIns.Size() && + mIns[i + 0].mType == ASMIT_TXA && mIns[i + 1].mType == ASMIT_EOR && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mType == ASMIT_TAX) + { + if (mIns[i + 1].mAddress == 1 && (mNDataSet[CPU_REG_X].mMask & 1)) + { + if (mNDataSet[CPU_REG_X].mValue & 1) + { + mIns[i + 0].mType = ASMIT_DEX; mIns[i + 0].mLive |= LIVE_CPU_REG_X; + } + else + { + mIns[i + 0].mType = ASMIT_INX; mIns[i + 0].mLive |= LIVE_CPU_REG_X; + } + + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_TXA; mIns[i + 2].mMode = ASMIM_IMPLIED; + } + } + + if (i + 2 < mIns.Size() && + mIns[i + 0].mType == ASMIT_TYA && mIns[i + 1].mType == ASMIT_EOR && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mType == ASMIT_TAY) + { + if (mIns[i + 1].mAddress == 1 && (mNDataSet[CPU_REG_Y].mMask & 1)) + { + if (mNDataSet[CPU_REG_Y].mValue & 1) + { + mIns[i + 0].mType = ASMIT_DEY; mIns[i + 0].mLive |= LIVE_CPU_REG_Y; + } + else + { + mIns[i + 0].mType = ASMIT_INY; mIns[i + 0].mLive |= LIVE_CPU_REG_Y; + } + + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_TYA; mIns[i + 2].mMode = ASMIM_IMPLIED; + } + } +#endif + if (i + 1 < mIns.Size() && + mIns[i + 0].mType == ASMIT_TXA && !(mIns[i + 0].mLive & LIVE_CPU_REG_X) && + mIns[i + 1].mType == ASMIT_ORA && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 1) + { + if (mNDataSet[CPU_REG_X].mMask & 1) + { + if (mNDataSet[CPU_REG_X].mValue & 1) + { + mIns[i + 1].mAddress = 0; + changed = true; + } + else + { + mIns[i + 0].mType = ASMIT_INX; mIns[i + 0].mLive |= LIVE_CPU_REG_X; + mIns[i + 1].mType = ASMIT_TXA; mIns[i + 1].mMode = ASMIM_IMPLIED; + changed = true; + } + } + + } + + bool skip = false; + if (i + 2 < mIns.Size() && + mIns[i + 0].mType == ASMIT_EOR && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_AND && mIns[i + 1].mMode == ASMIM_IMMEDIATE && + mIns[i + 2].mType == ASMIT_EOR && mIns[i + 2].SameEffectiveAddress(mIns[i + 0])) + { +#if 0 + printf("EORANEOR %s, %d, %d : A:%02x:%02x Z:%02x:%02x I:%02x\n", + mProc->mIdent->mString, mIndex, i, + mNDataSet[CPU_REG_A].mMask, mNDataSet[CPU_REG_A].mValue, + mNDataSet[mIns[i + 0].mAddress].mMask, mNDataSet[mIns[i + 0].mAddress].mValue & mNDataSet[mIns[i + 0].mAddress].mMask, + mIns[i + 1].mAddress); + + if ((mIns[i + 1].mAddress ^ 0xff) == (mNDataSet[mIns[i + 0].mAddress].mMask & ~mNDataSet[mIns[i + 0].mAddress].mValue)) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mAddress ^= 0xff; + mIns[i + 2].mType = ASMIT_ORA; + changed = true; + } + else +#endif + if (mNDataSet[mIns[i + 0].mAddress].mMask == 0) + { + mNDataSet.SetMask(CPU_REG_A, mNDataSet[CPU_REG_A].mMask & mIns[i + 1].mAddress, mNDataSet[CPU_REG_A].mValue); + mNDataSet.ResetMask(CPU_REG_Z); + i += 2; + skip = true; + } + } + + if (!skip) + { + if (mIns[i].BitFieldForwarding(mNDataSet, carryop)) + changed = true; + if (carryop != ASMIT_NOP) + mIns.Insert(i + 1, NativeCodeInstruction(mIns[i].mIns, carryop)); + } + } + + if (mFalseJump) + { + mFDataSet = mNDataSet; + + if (mIns.Size() > 0) + { + const NativeCodeInstruction& lins(mIns[mIns.Size() - 1]); + + if (lins.ChangesAccuAndFlag()) + { + switch (mBranch) + { + case ASMIT_BEQ: + if (mNDataSet[CPU_REG_A].mMask == 0xff) + { + if (mNDataSet[CPU_REG_A].mValue != 0) + { + mTrueJump->RemEntryBlock(this); + mTrueJump = mFalseJump; + mFalseJump = nullptr; + } + else + { + mFalseJump->RemEntryBlock(this); + mFalseJump = nullptr; + } + mBranch = ASMIT_JMP; + changed = true; + } + else + { + mNDataSet.SetMask(CPU_REG_A, 0xff, 0x00); + + if (lins.mMode == ASMIM_ZERO_PAGE && lins.mType == ASMIT_LDA) + { + mNDataSet.SetMask(lins.mAddress, 0xff, 0x00); + } + + if (mFDataSet[CPU_REG_A].mMask == 0xfe) + { + mFDataSet.SetMask(CPU_REG_A, 0xff, 0x01); + } + } + break; + case ASMIT_BNE: + if (mNDataSet[CPU_REG_A].mMask == 0xff) + { + if (mNDataSet[CPU_REG_A].mValue == 0) + { + mTrueJump->RemEntryBlock(this); + mTrueJump = mFalseJump; + mFalseJump = nullptr; + } + else + { + mFalseJump->RemEntryBlock(this); + mFalseJump = nullptr; + } + mBranch = ASMIT_JMP; + changed = true; + } + else + { + mFDataSet.SetMask(CPU_REG_A, 0xff, 0x00); + + if (lins.mMode == ASMIM_ZERO_PAGE && lins.mType == ASMIT_LDA) + { + mFDataSet.SetMask(lins.mAddress, 0xff, 0x00); + } + + if (mNDataSet[CPU_REG_A].mMask == 0xfe) + { + mNDataSet.SetMask(CPU_REG_A, 0xff, 0x01); + } + } + break; + case ASMIT_BPL: + if (mNDataSet[CPU_REG_A].mMask & 0x80) + { + if (mNDataSet[CPU_REG_A].mValue & 0x80) + { + mTrueJump->RemEntryBlock(this); + mTrueJump = mFalseJump; + mFalseJump = nullptr; + } + else + { + mFalseJump->RemEntryBlock(this); + mFalseJump = nullptr; + } + mBranch = ASMIT_JMP; + changed = true; + } + else + { + mNDataSet.SetMask(CPU_REG_A, mNDataSet[CPU_REG_A].mMask | 0x80, mNDataSet[CPU_REG_A].mValue & 0x7f); + mFDataSet.SetMask(CPU_REG_A, mFDataSet[CPU_REG_A].mMask | 0x80, mFDataSet[CPU_REG_A].mValue | 0x80); + + if (lins.mMode == ASMIM_ZERO_PAGE && lins.mType == ASMIT_LDA) + { + mNDataSet.SetMask(lins.mAddress, mNDataSet[lins.mAddress].mMask | 0x80, mNDataSet[lins.mAddress].mValue & 0x7f); + mFDataSet.SetMask(lins.mAddress, mFDataSet[lins.mAddress].mMask | 0x80, mFDataSet[lins.mAddress].mValue | 0x80); + } + } + break; + case ASMIT_BMI: + if (mNDataSet[CPU_REG_A].mMask & 0x80) + { + if (!(mNDataSet[CPU_REG_A].mValue & 0x80)) + { + mTrueJump->RemEntryBlock(this); + mTrueJump = mFalseJump; + mFalseJump = nullptr; + } + else + { + mFalseJump->RemEntryBlock(this); + mFalseJump = nullptr; + } + mBranch = ASMIT_JMP; + changed = true; + } + else + { + mFDataSet.SetMask(CPU_REG_A, mNDataSet[CPU_REG_A].mMask | 0x80, mNDataSet[CPU_REG_A].mValue & 0x7f); + mNDataSet.SetMask(CPU_REG_A, mFDataSet[CPU_REG_A].mMask | 0x80, mFDataSet[CPU_REG_A].mValue | 0x80); + + if (lins.mMode == ASMIM_ZERO_PAGE && lins.mType == ASMIT_LDA) + { + mFDataSet.SetMask(lins.mAddress, mNDataSet[lins.mAddress].mMask | 0x80, mNDataSet[lins.mAddress].mValue & 0x7f); + mNDataSet.SetMask(lins.mAddress, mFDataSet[lins.mAddress].mMask | 0x80, mFDataSet[lins.mAddress].mValue | 0x80); + } + } + break; + } + } + else if (lins.mType == ASMIT_CMP && lins.mMode == ASMIM_IMMEDIATE) + { + switch (mBranch) + { + case ASMIT_BCS: + { + unsigned m = ~BinMask(lins.mAddress - 1) & 0xff; + mFDataSet.SetMask(CPU_REG_A, mFDataSet[CPU_REG_A].mMask | m, mFDataSet[CPU_REG_A].mValue & ~m); + m = ~BinMask(~lins.mAddress & 0xff) & 0xff; + mNDataSet.SetMask(CPU_REG_A, mNDataSet[CPU_REG_A].mMask | m, mNDataSet[CPU_REG_A].mValue | m); + } + break; + case ASMIT_BCC: + { + unsigned m = ~BinMask(lins.mAddress - 1) & 0xff; + mNDataSet.SetMask(CPU_REG_A, mNDataSet[CPU_REG_A].mMask | m, mNDataSet[CPU_REG_A].mValue & ~m); + m = ~BinMask(~lins.mAddress & 0xff) & 0xff; + mFDataSet.SetMask(CPU_REG_A, mFDataSet[CPU_REG_A].mMask | m, mFDataSet[CPU_REG_A].mValue | m); + } + break; + } + } + } + } + + assert(mIndex == 1000 || mNumEntries == mEntryBlocks.Size()); + + if (this->mTrueJump && this->mTrueJump->BitFieldForwarding(mNDataSet)) + changed = true; + if (this->mFalseJump && this->mFalseJump->BitFieldForwarding(mFDataSet)) + changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::GlobalValueForwarding(NativeCodeProcedure* proc, bool final) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + if (mEntryBlocks.Size() == 0) + mDataSet.Reset(); + else + { + if (this == mEntryBlocks[0]->mTrueJump) + mDataSet = mEntryBlocks[0]->mNDataSet; + else + mDataSet = mEntryBlocks[0]->mFDataSet; + + for (int i = 1; i < mEntryBlocks.Size(); i++) + { + if (this == mEntryBlocks[i]->mTrueJump) + mDataSet.Intersect(mEntryBlocks[i]->mNDataSet); + else + mDataSet.Intersect(mEntryBlocks[i]->mFDataSet); + } + } + + for (int i = 0; i < mIns.Size(); i++) + { + AsmInsType carryop; +#if 1 + if (i + 1 < mIns.Size() && + mIns[i].mType == ASMIT_INC && mIns[i].mMode == ASMIM_ABSOLUTE && + mIns[i + 1].mType == ASMIT_LDA && mIns[i].SameEffectiveAddress(mIns[i + 1]) && + mDataSet[CPU_REG_A].mMode == NRDM_ABSOLUTE && + mDataSet[CPU_REG_A].mLinkerObject == mIns[i].mLinkerObject && + mDataSet[CPU_REG_A].mValue == mIns[i].mAddress) + { + mIns[i + 1].mType = ASMIT_STA; + mIns[i + 0].mType = ASMIT_ADC; mIns[i + 0].mMode = ASMIM_IMMEDIATE; mIns[i + 0].mAddress = 1; + mIns.Insert(i, NativeCodeInstruction(mIns[i + 0].mIns, ASMIT_CLC)); + changed = true; + } +#endif + if (mIns[i].ValueForwarding(mDataSet, carryop, true, final, proc->mFastCallBase)) + changed = true; + if (carryop != ASMIT_NOP) + mIns.Insert(i + 1, NativeCodeInstruction(mIns[i].mIns, carryop)); + } + + + if (this->mTrueJump && this->mTrueJump->GlobalValueForwarding(proc, final)) + changed = true; + if (this->mFalseJump && this->mFalseJump->GlobalValueForwarding(proc, final)) + changed = true; + } + + return changed; +} + + +void NativeCodeBasicBlock::ResetModifiedDataSet(NativeRegisterDataSet& data) +{ + // Single block loop + for (int i = 0; i < mIns.Size(); i++) + { + NativeCodeInstruction& ins(mIns[i]); + if (ins.ChangesAccu()) + data.Reset(CPU_REG_A); + if (ins.ChangesXReg()) + data.Reset(CPU_REG_X); + if (ins.ChangesYReg()) + data.Reset(CPU_REG_Y); + + if (ins.mMode == ASMIM_ZERO_PAGE && ins.ChangesAddress()) + data.ResetZeroPage(ins.mAddress); + else if (ins.mMode == ASMIM_ABSOLUTE && ins.ChangesAddress()) + data.ResetAbsolute(ins.mLinkerObject, ins.mAddress); + + if (ins.mType == ASMIT_JSR) + { + data.ResetWorkRegs(); + + if (!(ins.mFlags & NCIF_RUNTIME) && ins.mLinkerObject || (ins.mFlags & NCIF_FEXEC)) + { + if (ins.mLinkerObject && ins.mLinkerObject->mProc) + { + for (int i = BC_REG_TMP; i < BC_REG_TMP + ins.mLinkerObject->mProc->mCallerSavedTemps; i++) + data.ResetZeroPage(i); + } + else + { + for (int i = BC_REG_TMP; i < BC_REG_TMP_SAVED; i++) + data.ResetZeroPage(i); + } + + for (int i = BC_REG_FPARAMS; i < BC_REG_FPARAMS_END; i++) + data.ResetZeroPage(i); + } + } + } +} + +bool NativeCodeBasicBlock::ValueForwarding(NativeCodeProcedure* proc, const NativeRegisterDataSet& data, bool global, bool final) +{ + bool changed = false; + + if (!mVisited) + { + assert(mIndex == 1000 || mNumEntries == mEntryBlocks.Size()); + + mNDataSet = data; + + if (mLoopHead) + { +#if 1 + if (mNumEntries == 2 && (mTrueJump == this || mFalseJump == this)) + { + mNDataSet.Reset(CPU_REG_C); + mNDataSet.Reset(CPU_REG_Z); + + if (final) + { + for (int i = 0; i < mIns.Size(); i++) + { + if ((mIns[i].mType == ASMIT_ASL || mIns[i].mType == ASMIT_LSR) && mIns[i].mMode == ASMIM_ZERO_PAGE && mNDataSet[mIns[i].mAddress].mMode == NRDM_IMMEDIATE && mNDataSet[mIns[i].mAddress].mValue == 0) + { + if (!ChangesZeroPage(mIns[i].mAddress, 0, i - 1) && !ChangesZeroPage(mIns[i].mAddress, i + 1)) + { + mIns[i].mType = ASMIT_CLC; mIns[i].mMode = ASMIM_IMPLIED; + } + } + } + } + + bool loopx = false, loopy = false; + int loopya, loopxa; + + if (mIns.Size() > 2) + { + int sz = mIns.Size(); + + if ((mIns[sz - 2].mType == ASMIT_LDY || mIns[sz - 2].mType == ASMIT_STY) && mIns[sz - 2].mMode == ASMIM_ZERO_PAGE) + { + if (mNDataSet[CPU_REG_Y].mMode == NRDM_ZERO_PAGE && mIns[sz - 2].mAddress == mNDataSet[CPU_REG_Y].mValue || + mNDataSet[CPU_REG_Y].SameData(mNDataSet[mIns[sz - 2].mAddress])) + { + loopya = mIns[sz - 2].mAddress; + loopy = true; + } + } + else if ((mIns[sz - 2].mType == ASMIT_LDX || mIns[sz - 2].mType == ASMIT_STX) && mIns[sz - 2].mMode == ASMIM_ZERO_PAGE) + { + if (mNDataSet[CPU_REG_X].mMode == NRDM_ZERO_PAGE && mIns[sz - 2].mAddress == mNDataSet[CPU_REG_X].mValue || + mNDataSet[CPU_REG_X].SameData(mNDataSet[mIns[sz - 2].mAddress])) + { + loopxa = mIns[sz - 2].mAddress; + loopx = true; + } + } + } + + ResetModifiedDataSet(mNDataSet); + + if (loopy) + { + mNDataSet.Set(CPU_REG_Y, NRDM_ZERO_PAGE, loopya); + } + if (loopx) + { + mNDataSet.Set(CPU_REG_X, NRDM_ZERO_PAGE, loopxa); + } + } + else if (mNumEntries == 2 && global) + { + NativeCodeBasicBlock* lblock = nullptr; + + if (mTrueJump && mFalseJump) + { + if (mTrueJump->mTrueJump == this && mTrueJump->mFalseJump == mFalseJump || + mTrueJump->mFalseJump == this && mTrueJump->mTrueJump == mFalseJump) + { + lblock = mTrueJump; + } + else if (mFalseJump->mTrueJump == this && mFalseJump->mFalseJump == mTrueJump || + mFalseJump->mFalseJump == this && mFalseJump->mTrueJump == mTrueJump) + { + lblock = mFalseJump; + } + } + + if (lblock) + { + mNDataSet.Reset(CPU_REG_C); + mNDataSet.Reset(CPU_REG_Z); + ResetModifiedDataSet(mNDataSet); + lblock->ResetModifiedDataSet(mNDataSet); + } +#if 1 + else if (final) + { + ExpandingArray lblocks; + + if (CollectSingleEntryGenericLoop(lblocks)) + { + mNDataSet.Reset(CPU_REG_C); + mNDataSet.Reset(CPU_REG_Z); + for (int i = 0; i < lblocks.Size(); i++) + lblocks[i]->ResetModifiedDataSet(mNDataSet); + } + else + mNDataSet.Reset(); + } +#endif + else + mNDataSet.Reset(); + } + else if (global && final) + { + ExpandingArray lblocks; + + if (CollectSingleEntryGenericLoop(lblocks)) + { + int k = 0; + for (int i = 0; i < mEntryBlocks.Size(); i++) + if (lblocks.Contains(mEntryBlocks[i])) + k++; + if (k + 1 == mEntryBlocks.Size()) + { + mNDataSet.Reset(CPU_REG_C); + mNDataSet.Reset(CPU_REG_Z); + for (int i = 0; i < lblocks.Size(); i++) + lblocks[i]->ResetModifiedDataSet(mNDataSet); + } + else + mNDataSet.Reset(); + } + else + mNDataSet.Reset(); + } + else +#endif + mNDataSet.Reset(); + } + else if (!global) + { + mNDataSet.Reset(); + } +#if 0 + else if (mNumEntries != 1) + { + ndata.Reset(); + } +#endif + else if (mNumEntries > 0) + { + if (mNumEntered > 0) + mNDataSet.Intersect(mDataSet); + + mNumEntered++; + + if (mNumEntered < mNumEntries) + { + mDataSet = mNDataSet; + return false; + } + } + + mVisited = true; + + for (int i = 0; i < mIns.Size(); i++) + { + AsmInsType carryop; + +#if 1 + if (mIns[i].mMode == ASMIM_INDIRECT_Y && HasAsmInstructionMode(mIns[i].mType, ASMIM_ABSOLUTE_Y) && + !(mIns[i].mType == ASMIT_CMP && (mIns[i].mLive & LIVE_CPU_REG_Y)) && + mNDataSet[CPU_REG_Y].mMode == NRDM_IMMEDIATE && + mNDataSet[mIns[i].mAddress + 1].mMode == NRDM_IMMEDIATE_ADDRESS && (mNDataSet[mIns[i].mAddress + 1].mFlags & NCIF_UPPER) && + mNDataSet[mIns[i].mAddress].mMode != NRDM_IMMEDIATE_ADDRESS) + { + int reg = mIns[i].mAddress; + mIns.Insert(i, NativeCodeInstruction(mIns[i].mIns, ASMIT_LDY, ASMIM_ZERO_PAGE, reg)); + mIns[i + 1].mMode = ASMIM_ABSOLUTE_Y; + mIns[i + 1].mLinkerObject = mNDataSet[reg + 1].mLinkerObject; + mIns[i + 1].mAddress = (mNDataSet[reg + 1].mValue & 0xff00) + mNDataSet[CPU_REG_Y].mValue; + mIns[i + 1].mFlags &= ~NCIF_LOWER; + if (mIns[i + 1].mLive & LIVE_CPU_REG_Y) + { + mIns.Insert(i + 2, NativeCodeInstruction(mIns[i].mIns, ASMIT_LDY, ASMIM_IMMEDIATE, mNDataSet[CPU_REG_Y].mValue)); + mIns.Insert(i + 3, NativeCodeInstruction(mIns[i].mIns, ASMIT_ORA, ASMIM_IMMEDIATE, 0)); + } + changed = true; + } +#endif + // Check load and commutative with current accu value +#if 1 + if (i + 1 < mIns.Size() && mIns[i].mType == ASMIT_LDA && + mIns[i + 1].IsCommutative() && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && mNDataSet[CPU_REG_A].mMode == NRDM_ZERO_PAGE && mNDataSet[CPU_REG_A].mValue == mIns[i + 1].mAddress) + { + mIns[i].mType = mIns[i + 1].mType; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + changed = true; + } +#endif +#if 1 + if (i + 1 < mIns.Size() && + mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_CMP && mIns[i + 1].mMode == ASMIM_IMMEDIATE && + mNDataSet[CPU_REG_A].mMode == NRDM_IMMEDIATE && mNDataSet[CPU_REG_A].mValue == mIns[i + 1].mAddress && + !(mIns[i + 1].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C))) + { + mIns[i + 1].CopyMode(mIns[i + 0]); + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + changed = true; + } +#endif +#if 1 + if (i + 1 < mIns.Size() && + mIns[i + 0].mType == ASMIT_ROR && mIns[i + 0].mMode == ASMIM_IMPLIED && + mIns[i + 1].mType == ASMIT_EOR && mIns[i + 1].mMode == ASMIM_IMMEDIATE && + mNDataSet[CPU_REG_C].mMode == NRDM_IMMEDIATE) + { + mIns[i + 0].mType = ASMIT_LSR; + if (mNDataSet[CPU_REG_C].mValue) + mIns[i + 1].mAddress ^= 0x80; + changed = true; + } +#endif + +#if 1 + if (i + 1 < mIns.Size() && (mIns[i].mType == ASMIT_INC || mIns[i].mType == ASMIT_DEC) && mIns[i].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mMode == ASMIM_ZERO_PAGE && mIns[i + 1].mAddress == mIns[i].mAddress) + { + if (mIns[i + 1].mType == ASMIT_LDY && mNDataSet[CPU_REG_Y].mMode == NRDM_ZERO_PAGE && mNDataSet[CPU_REG_Y].mValue == mIns[i].mAddress) + { + mIns[i].mType = mIns[i].mType == ASMIT_INC ? ASMIT_INY : ASMIT_DEY; mIns[i].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_STY; + changed = true; + } + else if (mIns[i + 1].mType == ASMIT_LDX && mNDataSet[CPU_REG_X].mMode == NRDM_ZERO_PAGE && mNDataSet[CPU_REG_X].mValue == mIns[i].mAddress) + { + mIns[i].mType = mIns[i].mType == ASMIT_INC ? ASMIT_INX : ASMIT_DEX; mIns[i].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_STX; + changed = true; + } + } +#endif + +#if 1 + if (final && + i + 1 < mIns.Size() && mIns[i].mType == ASMIT_LDA && mIns[i].mMode == ASMIM_ABSOLUTE && + mIns[i + 1].IsCommutative() && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && mNDataSet[CPU_REG_Y].mMode == NRDM_ZERO_PAGE && mNDataSet[CPU_REG_Y].mValue == mIns[i + 1].mAddress) + { + mIns[i + 1].CopyMode(mIns[i]); + mIns[i].mType = ASMIT_TYA; mIns[i].mMode = ASMIM_IMPLIED; + changed = true; + } + if (final && + i + 1 < mIns.Size() && mIns[i].mType == ASMIT_LDA && mIns[i].mMode == ASMIM_ABSOLUTE && + mIns[i + 1].IsCommutative() && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && mNDataSet[CPU_REG_X].mMode == NRDM_ZERO_PAGE && mNDataSet[CPU_REG_X].mValue == mIns[i + 1].mAddress) + { + mIns[i + 1].CopyMode(mIns[i]); + mIns[i].mType = ASMIT_TXA; mIns[i].mMode = ASMIM_IMPLIED; + changed = true; + } +#endif + + if (i + 1 < mIns.Size() && mIns[i].mType == ASMIT_LDA && !mIns[i].RequiresYReg() && + mIns[i + 1].mType == ASMIT_LDY && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && mNDataSet[CPU_REG_A].mMode == NRDM_ZERO_PAGE && mNDataSet[CPU_REG_A].mValue == mIns[i + 1].mAddress && !(mIns[i + 1].mLive & LIVE_CPU_REG_Z)) + { + mIns[i + 1] = mIns[i]; + mIns[i].mType = ASMIT_TAY; mIns[i].mMode = ASMIM_IMPLIED; + changed = true; + } + if (i + 1 < mIns.Size() && mIns[i].mType == ASMIT_LDA && !mIns[i].RequiresXReg() && + mIns[i + 1].mType == ASMIT_LDX && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && mNDataSet[CPU_REG_A].mMode == NRDM_ZERO_PAGE && mNDataSet[CPU_REG_A].mValue == mIns[i + 1].mAddress && !(mIns[i + 1].mLive & LIVE_CPU_REG_Z)) + { + mIns[i + 1] = mIns[i]; + mIns[i].mType = ASMIT_TAX; mIns[i].mMode = ASMIM_IMPLIED; + changed = true; + } +#if 1 + if (i + 2 < mIns.Size() && mIns[i].mType == ASMIT_LDA && mIns[i + 1].mType == ASMIT_CLC && + mIns[i + 2].IsCommutative() && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && mNDataSet[CPU_REG_A].mMode == NRDM_ZERO_PAGE && mNDataSet[CPU_REG_A].mValue == mIns[i + 2].mAddress) + { + mIns[i + 2].CopyMode(mIns[i + 0]); + mIns[i].mType = ASMIT_NOP; mIns[i].mMode = ASMIM_IMPLIED; + changed = true; + } +#endif +#if 0 + if (i + 1 < mIns.Size() && mIns[i].mType == ASMIT_LDY && mIns[i].mMode == ASMIM_ZERO_PAGE && + mNDataSet.mRegs[CPU_REG_X].mMode == NRDM_ZERO_PAGE && mNDataSet.mRegs[CPU_REG_X].mValue == mIns[i].mAddress && + mIns[i + 1].mMode == ASMIM_ABSOLUTE_Y && HasAsmInstructionMode(mIns[i + 1].mType, ASMIM_ABSOLUTE_X)) + { + mIns[i + 1].mMode = ASMIM_ABSOLUTE_X; + changed = true; + } +#endif + if (mIns[i].ValueForwarding(mNDataSet, carryop, !global, final, proc->mFastCallBase)) + changed = true; + if (carryop != ASMIT_NOP) + mIns.Insert(i + 1, NativeCodeInstruction(mIns[i].mIns, carryop)); + } + +#if 1 + if (global) + { + NativeCodeBasicBlock* fork = this; + if (!mFalseJump && mTrueJump && mTrueJump->mIns.Size() == 0) + fork = mTrueJump; + + if (fork->mFalseJump) + { + mFDataSet = mNDataSet; + + switch (fork->mBranch) + { + case ASMIT_BCS: + if (mNDataSet[CPU_REG_C].mMode == NRDM_IMMEDIATE) + { + mBranch = ASMIT_JMP; + mTrueJump->mNumEntries--; + mTrueJump->mEntryBlocks.Remove(mTrueJump->mEntryBlocks.IndexOf(this)); + if (!mNDataSet[CPU_REG_C].mValue) + mTrueJump = fork->mFalseJump; + else + mTrueJump = fork->mTrueJump; + mTrueJump->mNumEntries++; + mTrueJump->mEntryBlocks.Push(this); + if (mFalseJump) + { + mFalseJump->mNumEntries--; + mFalseJump->mEntryBlocks.Remove(mFalseJump->mEntryBlocks.IndexOf(this)); + } + mFalseJump = nullptr; + changed = true; + } + else if (mIns.Size() > 0) + { + int n = mIns.Size() - 1; + while (n >= 0 && mIns[n].mType == ASMIT_STA) + n--; + if (n > 0 && mIns[n - 1].mType == ASMIT_CLC && mIns[n].mType == ASMIT_ADC && mIns[n].mMode == ASMIM_IMMEDIATE && mIns[n].mAddress == 1) + { + mDataSet.Set(CPU_REG_A, NRDM_IMMEDIATE, 0); + mDataSet.Set(CPU_REG_Z, NRDM_IMMEDIATE, 0); +#if 1 + if (mFalseJump->mIns.Size() == 0 && mFalseJump->mFalseJump) + { + if (mFalseJump->mIns.Size() == 0) + { + if (mFalseJump->mBranch == ASMIT_BEQ) + { + mFalseJump->mNumEntries--; + mFalseJump->mEntryBlocks.Remove(mFalseJump->mEntryBlocks.IndexOf(this)); + mFalseJump = mFalseJump->mFalseJump; + mFalseJump->mNumEntries++; + mFalseJump->mEntryBlocks.Push(this); + changed = true; + } + else if (mFalseJump->mBranch == ASMIT_BNE) + { + mFalseJump->mNumEntries--; + mFalseJump->mEntryBlocks.Remove(mFalseJump->mEntryBlocks.IndexOf(this)); + mFalseJump = mFalseJump->mTrueJump; + mFalseJump->mNumEntries++; + mFalseJump->mEntryBlocks.Push(this); + changed = true; + } + } + } +#endif + } + else if (final) + { + if (fork->mTrueJump->mIns.Size() > 0 && fork->mTrueJump->mIns[0].IsShift()) + mNDataSet.Set(CPU_REG_C, NRDM_IMMEDIATE, 1); + if (fork->mFalseJump->mIns.Size() > 0 && fork->mFalseJump->mIns[0].IsShift()) + mFDataSet.Set(CPU_REG_C, NRDM_IMMEDIATE, 0); + } + } + break; + case ASMIT_BCC: + if (mNDataSet[CPU_REG_C].mMode == NRDM_IMMEDIATE) + { + mBranch = ASMIT_JMP; + mTrueJump->mNumEntries--; + mTrueJump->mEntryBlocks.Remove(mTrueJump->mEntryBlocks.IndexOf(this)); + if (mNDataSet[CPU_REG_C].mValue) + mTrueJump = fork->mFalseJump; + else + mTrueJump = fork->mTrueJump; + mTrueJump->mNumEntries++; + mTrueJump->mEntryBlocks.Push(this); + if (mFalseJump) + { + mFalseJump->mNumEntries--; + mFalseJump->mEntryBlocks.Remove(mFalseJump->mEntryBlocks.IndexOf(this)); + } + mFalseJump = nullptr; + changed = true; + } + else if (mIns.Size() > 0) + { + int n = mIns.Size() - 1; + while (n >= 0 && mIns[n].mType == ASMIT_STA) + n--; + if (n > 0 && mIns[n - 1].mType == ASMIT_CLC && mIns[n].mType == ASMIT_ADC && mIns[n].mMode == ASMIM_IMMEDIATE && mIns[n].mAddress == 1) + { + mFDataSet.Set(CPU_REG_A, NRDM_IMMEDIATE, 0); + mFDataSet.Set(CPU_REG_A, NRDM_IMMEDIATE, 0); + + if (mTrueJump->mIns.Size() == 0 && mTrueJump->mFalseJump) + { + if (mTrueJump->mBranch == ASMIT_BEQ) + { + mTrueJump->mNumEntries--; + mTrueJump->mEntryBlocks.Remove(mTrueJump->mEntryBlocks.IndexOf(this)); + mTrueJump = mTrueJump->mFalseJump; + mTrueJump->mNumEntries++; + mTrueJump->mEntryBlocks.Push(this); + changed = true; + } + else if (mTrueJump->mBranch == ASMIT_BNE) + { + mTrueJump->mNumEntries--; + mTrueJump->mEntryBlocks.Remove(mTrueJump->mEntryBlocks.IndexOf(this)); + mTrueJump = mTrueJump->mTrueJump; + mTrueJump->mNumEntries++; + mTrueJump->mEntryBlocks.Push(this); + changed = true; + } + } + } + else if (final) + { + if (fork->mTrueJump->mIns.Size() > 0 && fork->mTrueJump->mIns[0].IsShift()) + mNDataSet.Set(CPU_REG_C, NRDM_IMMEDIATE, 0); + if (fork->mFalseJump->mIns.Size() > 0 && fork->mFalseJump->mIns[0].IsShift()) + mFDataSet.Set(CPU_REG_C, NRDM_IMMEDIATE, 1); + } + } + break; + case ASMIT_BNE: + if (mNDataSet[CPU_REG_Z].mMode == NRDM_IMMEDIATE) + { + mBranch = ASMIT_JMP; + mTrueJump->mNumEntries--; + mTrueJump->mEntryBlocks.Remove(mTrueJump->mEntryBlocks.IndexOf(this)); + if (!mNDataSet[CPU_REG_Z].mValue) + mTrueJump = fork->mFalseJump; + else + mTrueJump = fork->mTrueJump; + mTrueJump->mNumEntries++; + mTrueJump->mEntryBlocks.Push(this); + if (mFalseJump) + { + mFalseJump->mNumEntries--; + mFalseJump->mEntryBlocks.Remove(mFalseJump->mEntryBlocks.IndexOf(this)); + } + mFalseJump = nullptr; + changed = true; + } + else if (global && mIns.Size() > 0 && fork == this) + { + NativeCodeInstruction& lins(mIns[mIns.Size() - 1]); + + if (lins.mType == ASMIT_LDY) + { + mFDataSet.Set(CPU_REG_Y, NRDM_IMMEDIATE, 0); + + if (lins.mMode == ASMIM_ZERO_PAGE) + { + mFDataSet.Set(lins.mAddress, NRDM_IMMEDIATE, 0); + } + } + else if (lins.mType == ASMIT_LDX) + { + mFDataSet.Set(CPU_REG_X, NRDM_IMMEDIATE, 0); + + if (lins.mMode == ASMIM_ZERO_PAGE) + { + mFDataSet.Set(lins.mAddress, NRDM_IMMEDIATE, 0); + } + } + else if (lins.mType == ASMIT_LDA) + { + mFDataSet.Set(CPU_REG_A, NRDM_IMMEDIATE, 0); + + if (lins.mMode == ASMIM_ZERO_PAGE) + { + mFDataSet.Set(lins.mAddress, NRDM_IMMEDIATE, 0); + + } + } + else if (lins.mType == ASMIT_CPY) + { + if (lins.mMode == ASMIM_IMMEDIATE) + mFDataSet.Set(CPU_REG_Y, NRDM_IMMEDIATE, lins.mAddress); + else if (lins.mMode == ASMIM_ZERO_PAGE) + { + if (mFDataSet[CPU_REG_Y].mMode == NRDM_IMMEDIATE) + mFDataSet.Set(lins.mAddress, NRDM_IMMEDIATE, mFDataSet[CPU_REG_Y].mValue); + else + mFDataSet.Set(CPU_REG_Y, NRDM_ZERO_PAGE, lins.mAddress); + } + } + else if (lins.mType == ASMIT_CPX) + { + if (lins.mMode == ASMIM_IMMEDIATE) + mFDataSet.Set(CPU_REG_X, NRDM_IMMEDIATE, lins.mAddress); + else if (lins.mMode == ASMIM_ZERO_PAGE) + { + if (mFDataSet[CPU_REG_X].mMode == NRDM_IMMEDIATE) + mFDataSet.Set(lins.mAddress, NRDM_IMMEDIATE, mFDataSet[CPU_REG_X].mValue); + else + mFDataSet.Set(CPU_REG_X, NRDM_ZERO_PAGE, lins.mAddress); + } + } + else if (lins.mType == ASMIT_CMP) + { + if (lins.mMode == ASMIM_IMMEDIATE) + mFDataSet.Set(CPU_REG_A, NRDM_IMMEDIATE, lins.mAddress); + else if (lins.mMode == ASMIM_ZERO_PAGE) + { + if (mFDataSet[CPU_REG_A].mMode == NRDM_IMMEDIATE) + mFDataSet.Set(lins.mAddress, NRDM_IMMEDIATE, mFDataSet[CPU_REG_A].mValue); + else + mFDataSet.Set(CPU_REG_A, NRDM_ZERO_PAGE, lins.mAddress); + } + } + else if (lins.mType == ASMIT_ORA || lins.mType == ASMIT_AND || lins.mType == ASMIT_EOR || lins.mType == ASMIT_ADC || lins.mType == ASMIT_SBC) + { + mFDataSet.Set(CPU_REG_A, NRDM_IMMEDIATE, 0); + } + else if (lins.mType == ASMIT_TXA || lins.mType == ASMIT_TAX) + { + mFDataSet.Set(CPU_REG_A, NRDM_IMMEDIATE, 0); + mFDataSet.Set(CPU_REG_X, NRDM_IMMEDIATE, 0); + } + else if (final && (lins.mType == ASMIT_INX || lins.mType == ASMIT_DEX)) + { + mFDataSet.Set(CPU_REG_X, NRDM_IMMEDIATE, 0); + } + else if (final && (lins.mType == ASMIT_INY || lins.mType == ASMIT_DEY)) + { + mFDataSet.Set(CPU_REG_Y, NRDM_IMMEDIATE, 0); + } + else if ((lins.mType == ASMIT_INC || lins.mType == ASMIT_DEC) && lins.mMode == ASMIM_ZERO_PAGE) + { + mFDataSet.Set(lins.mAddress, NRDM_IMMEDIATE, 0); + } + } + break; + case ASMIT_BEQ: + if (mNDataSet[CPU_REG_Z].mMode == NRDM_IMMEDIATE) + { + mBranch = ASMIT_JMP; + mTrueJump->mNumEntries--; + mTrueJump->mEntryBlocks.Remove(mTrueJump->mEntryBlocks.IndexOf(this)); + if (mNDataSet[CPU_REG_Z].mValue) + mTrueJump = fork->mFalseJump; + else + mTrueJump = fork->mTrueJump; + mTrueJump->mNumEntries++; + mTrueJump->mEntryBlocks.Push(this); + if (mFalseJump) + { + mFalseJump->mNumEntries--; + mFalseJump->mEntryBlocks.Remove(mFalseJump->mEntryBlocks.IndexOf(this)); + } + mFalseJump = nullptr; + changed = true; + } + else if (global && mIns.Size() > 0 && fork == this) + { + NativeCodeInstruction& lins(mIns[mIns.Size() - 1]); + + if (lins.mType == ASMIT_LDY) + { + mNDataSet.Set(CPU_REG_Y, NRDM_IMMEDIATE, 0); + + if (lins.mMode == ASMIM_ZERO_PAGE) + { + mNDataSet.Set(lins.mAddress, NRDM_IMMEDIATE, 0); + } + } + else if (lins.mType == ASMIT_LDX) + { + mNDataSet.Set(CPU_REG_X, NRDM_IMMEDIATE, 0); + + if (lins.mMode == ASMIM_ZERO_PAGE) + { + mNDataSet.Set(lins.mAddress, NRDM_IMMEDIATE, 0); + } + } + else if (lins.mType == ASMIT_LDA) + { + mNDataSet.Set(CPU_REG_A, NRDM_IMMEDIATE, 0); + + if (lins.mMode == ASMIM_ZERO_PAGE) + { + mNDataSet.Set(lins.mAddress, NRDM_IMMEDIATE, 0); + } + } + else if (lins.mType == ASMIT_CPY) + { + if (lins.mMode == ASMIM_IMMEDIATE) + mNDataSet.Set(CPU_REG_Y, NRDM_IMMEDIATE, lins.mAddress); + else if (lins.mMode == ASMIM_ZERO_PAGE) + { + if (mNDataSet[CPU_REG_Y].mMode == NRDM_IMMEDIATE) + mNDataSet.Set(lins.mAddress, NRDM_IMMEDIATE, mNDataSet[CPU_REG_Y].mValue); + else + mNDataSet.Set(CPU_REG_Y, NRDM_ZERO_PAGE, lins.mAddress); + } + } + else if (lins.mType == ASMIT_CPX) + { + if (lins.mMode == ASMIM_IMMEDIATE) + mNDataSet.Set(CPU_REG_X, NRDM_IMMEDIATE, lins.mAddress); + else if (lins.mMode == ASMIM_ZERO_PAGE) + { + if (mNDataSet[CPU_REG_X].mMode == NRDM_IMMEDIATE) + mNDataSet.Set(lins.mAddress, NRDM_IMMEDIATE, mNDataSet[CPU_REG_X].mValue); + else + mNDataSet.Set(CPU_REG_X, NRDM_ZERO_PAGE, lins.mAddress); + } + } + else if (lins.mType == ASMIT_CMP) + { + if (lins.mMode == ASMIM_IMMEDIATE) + mNDataSet.Set(CPU_REG_A, NRDM_IMMEDIATE, lins.mAddress); + else if (lins.mMode == ASMIM_ZERO_PAGE) + { + if (mNDataSet[CPU_REG_A].mMode == NRDM_IMMEDIATE) + mNDataSet.Set(lins.mAddress, NRDM_IMMEDIATE, mNDataSet[CPU_REG_A].mValue); + else + mNDataSet.Set(CPU_REG_A, NRDM_ZERO_PAGE, lins.mAddress); + } + } + else if (lins.mType == ASMIT_ORA || lins.mType == ASMIT_AND || lins.mType == ASMIT_EOR || lins.mType == ASMIT_ADC || lins.mType == ASMIT_SBC) + { + mNDataSet.Set(CPU_REG_A, NRDM_IMMEDIATE, 0); + } + else if (lins.mType == ASMIT_TXA || lins.mType == ASMIT_TAX) + { + mNDataSet.Set(CPU_REG_A, NRDM_IMMEDIATE, 0); + mNDataSet.Set(CPU_REG_X, NRDM_IMMEDIATE, 0); + } + else if (final && (lins.mType == ASMIT_INX || lins.mType == ASMIT_DEX)) + { + mNDataSet.Set(CPU_REG_X, NRDM_IMMEDIATE, 0); + } + else if (final && (lins.mType == ASMIT_INY || lins.mType == ASMIT_DEY)) + { + mNDataSet.Set(CPU_REG_Y, NRDM_IMMEDIATE, 0); + } + else if ((lins.mType == ASMIT_INC || lins.mType == ASMIT_DEC) && lins.mMode == ASMIM_ZERO_PAGE) + { + mNDataSet.Set(lins.mAddress, NRDM_IMMEDIATE, 0); + } + } + break; + case ASMIT_BPL: + if (mNDataSet[CPU_REG_Z].mMode == NRDM_IMMEDIATE) + { + mBranch = ASMIT_JMP; + mTrueJump->mNumEntries--; + mTrueJump->mEntryBlocks.Remove(mTrueJump->mEntryBlocks.IndexOf(this)); + if ((mNDataSet[CPU_REG_Z].mValue & 0x80)) + mTrueJump = fork->mFalseJump; + else + mTrueJump = fork->mTrueJump; + mTrueJump->mNumEntries++; + mTrueJump->mEntryBlocks.Push(this); + if (mFalseJump) + { + mFalseJump->mNumEntries--; + mFalseJump->mEntryBlocks.Remove(mFalseJump->mEntryBlocks.IndexOf(this)); + } + mFalseJump = nullptr; + changed = true; + } + break; + case ASMIT_BMI: + if (mNDataSet[CPU_REG_Z].mMode == NRDM_IMMEDIATE) + { + mBranch = ASMIT_JMP; + mTrueJump->mEntryBlocks.Remove(mTrueJump->mEntryBlocks.IndexOf(this)); + mTrueJump->mNumEntries--; + if (!(mNDataSet[CPU_REG_Z].mValue & 0x80)) + mTrueJump = fork->mFalseJump; + else + mTrueJump = fork->mTrueJump; + mTrueJump->mNumEntries++; + mTrueJump->mEntryBlocks.Push(this); + if (mFalseJump) + { + mFalseJump->mNumEntries--; + mFalseJump->mEntryBlocks.Remove(mFalseJump->mEntryBlocks.IndexOf(this)); + } + mFalseJump = nullptr; + changed = true; + } + break; + } + } + } +#endif + if (global && mTrueJump && mTrueJump->mIns.Size() == 1 && mTrueJump->mTrueJump && !mTrueJump->mFalseJump) + { + if (mTrueJump->mIns[0].mType == ASMIT_LDY && mTrueJump->mIns[0].mMode == ASMIM_ZERO_PAGE) + { + if (mNDataSet[CPU_REG_Y].SameData(mNDataSet[mTrueJump->mIns[0].mAddress])) + { + mTrueJump = mTrueJump->mTrueJump; + changed = true; + } + } + } + if (global && mFalseJump && mFalseJump->mIns.Size() == 1 && mFalseJump->mTrueJump && !mFalseJump->mFalseJump) + { + if (mFalseJump->mIns[0].mType == ASMIT_LDY && mFalseJump->mIns[0].mMode == ASMIM_ZERO_PAGE) + { + if (mFDataSet[CPU_REG_Y].SameData(mFDataSet[mFalseJump->mIns[0].mAddress])) + { + mFalseJump = mFalseJump->mTrueJump; + changed = true; + } + } + } +#if 1 + if (mTrueJump && !mFalseJump && mTrueJump->mIns.Size() == 1 && (mTrueJump->mBranch == ASMIT_BEQ || mTrueJump->mBranch == ASMIT_BNE) && mTrueJump->mIns[0].mMode == ASMIM_IMMEDIATE) + { + if (mTrueJump->mIns[0].mType == ASMIT_CMP) + { + if (mNDataSet[CPU_REG_A].mMode == NRDM_IMMEDIATE) + { + NativeCodeBasicBlock* target; + + if (mNDataSet[CPU_REG_A].mValue == mTrueJump->mIns[0].mAddress) + { + if (mTrueJump->mBranch == ASMIT_BEQ) + target = mTrueJump->mTrueJump; + else + target = mTrueJump->mFalseJump; + } + else + { + if (mTrueJump->mBranch == ASMIT_BNE) + target = mTrueJump->mTrueJump; + else + target = mTrueJump->mFalseJump; + } + + if (target != mTrueJump) + { + mIns.Push(mTrueJump->mIns[0]); + mTrueJump->mEntryBlocks.RemoveAll(this); + mTrueJump->mNumEntries--; + target->mEntryBlocks.Push(this); + target->mNumEntries++; + mTrueJump = target; + changed = true; + } + } + } + else if (mTrueJump->mIns[0].mType == ASMIT_ORA && mTrueJump->mIns[0].mAddress == 0) + { + if (mNDataSet[CPU_REG_A].mMode == NRDM_IMMEDIATE) + { + NativeCodeBasicBlock* target; + + if (mNDataSet[CPU_REG_A].mValue == 0) + { + if (mTrueJump->mBranch == ASMIT_BEQ) + target = mTrueJump->mTrueJump; + else + target = mTrueJump->mFalseJump; + } + else + { + if (mTrueJump->mBranch == ASMIT_BNE) + target = mTrueJump->mTrueJump; + else + target = mTrueJump->mFalseJump; + } + + if (target != mTrueJump) + { + mTrueJump->mEntryBlocks.RemoveAll(this); + mTrueJump->mNumEntries--; + target->mEntryBlocks.Push(this); + target->mNumEntries++; + mTrueJump = target; + changed = true; + } + } + } + } +#endif + assert(mIndex == 1000 || mNumEntries == mEntryBlocks.Size()); + + if (this->mTrueJump && this->mTrueJump->ValueForwarding(proc, mNDataSet, global, final)) + changed = true; + if (this->mFalseJump && this->mFalseJump->ValueForwarding(proc, mFDataSet, global, final)) + changed = true; + + + + } + + return changed; +} + +bool NativeCodeBasicBlock::OptimizeSimpleLoopInvariant(NativeCodeProcedure* proc, bool full) +{ + NativeCodeBasicBlock* lblock = proc->AllocateBlock(); + NativeCodeBasicBlock* eblock = proc->AllocateBlock(); + + NativeCodeBasicBlock* xblock = mTrueJump == this ? mFalseJump : mTrueJump; + + eblock->mBranch = ASMIT_JMP; + eblock->mTrueJump = mFalseJump; + eblock->mFalseJump = nullptr; + + lblock->mBranch = mBranch; + lblock->mTrueJump = lblock; + lblock->mFalseJump = eblock; + + lblock->mEntryRequiredRegs = mEntryRequiredRegs; + lblock->mExitRequiredRegs = mExitRequiredRegs; + + eblock->mEntryRequiredRegs = xblock->mEntryRequiredRegs; + eblock->mExitRequiredRegs = xblock->mEntryRequiredRegs; + + mExitRequiredRegs = mEntryRequiredRegs; + + for (int i = 0; i < mIns.Size(); i++) + lblock->mIns.Push(mIns[i]); + + mIns.SetSize(0); + + mBranch = ASMIT_JMP; + mTrueJump = lblock; + mFalseJump = nullptr; + + mNumEntries -= mEntryBlocks.RemoveAll(this); + + lblock->mEntryBlocks.Push(this); + lblock->mEntryBlocks.Push(lblock); + lblock->mNumEntries = 2; + + eblock->mEntryBlocks.Push(this); + eblock->mNumEntries = 1; + + return lblock->OptimizeSimpleLoopInvariant(proc, this, eblock, full); +} + +bool NativeCodeBasicBlock::OptimizeSingleEntryLoop(NativeCodeProcedure* proc) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + if (mLoopHead && mEntryBlocks.Size() > 1) + { + NativeCodeBasicBlock* pblock = nullptr; + ExpandingArray lblocks, pblocks, eblocks; + + bool addprefix = false; + for(int i=0; iIsDominatedBy(this)) + lblocks.Push(b); + else + { + if (b->mFalseJump) + addprefix = true; + pblocks.Push(b); + } + } + + if (pblocks.Size() < mEntryBlocks.Size()) + { + if (pblocks.Size() > 1 || addprefix) + { + pblock = mProc->AllocateBlock(); + pblock->mEntryRequiredRegs = mEntryRequiredRegs; + pblock->mExitRequiredRegs = mEntryRequiredRegs; + pblock->Close(pblocks[0]->mBranchIns, this, nullptr, ASMIT_JMP); + AddEntryBlock(pblock); + for (int i = 0; i < pblocks.Size(); i++) + { + NativeCodeBasicBlock* b = pblocks[i]; + if (this == b->mTrueJump) + { + b->mTrueJump = pblock; + RemEntryBlock(b); + pblock->AddEntryBlock(b); + } + if (this == b->mFalseJump) + { + b->mFalseJump = pblock; + RemEntryBlock(b); + pblock->AddEntryBlock(b); + } + } + } + else if (pblocks.Size() == 1) + pblock = pblocks[0]; + } + + if (pblock && lblocks.Size() + 1 == mEntryBlocks.Size()) + { + proc->ResetPatched(); + mPatched = true; + + if (!lblocks.Contains(this)) + lblocks.Push(this); + + for (int i = 0; i < lblocks.Size(); i++) + lblocks[i]->mPatched = true; + pblock->mPatched = true; + + int i = 0; + while (i < lblocks.Size()) + { + NativeCodeBasicBlock* block = lblocks[i]; + for(int j=0; jmEntryBlocks.Size(); j++) + { + NativeCodeBasicBlock* cblock = block->mEntryBlocks[j]; + if (!cblock->mPatched) + { + cblock->mPatched = true; + lblocks.Push(cblock); + } + } + i++; + } + + i = 0; + while (i < lblocks.Size()) + { + NativeCodeBasicBlock* b = lblocks[i]; + // !FIXME should be && + if (b->mFalseJump && !(lblocks.Contains(b->mFalseJump) && lblocks.Contains(b->mTrueJump))) + { + eblocks.Push(b); + } + i++; + } + + if (eblocks.Size() > 0) + { + bool aimm = false, ximm = false, yimm = false; + NativeCodeInstruction ains, xins, yins; + + int inserta = -1, insertx = -1, inserty = -1; + if (!pblock->mExitRequiredRegs[CPU_REG_A]) + { + inserta = pblock->mIns.Size(); + } + else + { + int i = pblock->mIns.Size(); + while (i > 0 && (pblock->mIns[i - 1].mLive & (LIVE_CPU_REG_A | LIVE_MEM))) + { + if (pblock->mIns[i - 1].IsPure()) + i--; + else + i = -1; + } + if (i > 0) + inserta = i; + else if (i == 0 && !pblock->mEntryRequiredRegs[CPU_REG_A]) + inserta = 0; + } + if (!pblock->mExitRequiredRegs[CPU_REG_X]) + { + insertx = pblock->mIns.Size(); + } + if (!pblock->mExitRequiredRegs[CPU_REG_Y]) + { + inserty = pblock->mIns.Size(); + } + + if (inserta >= 0 || insertx >= 0 || inserty >= 0) + { + for (int i = 0; i < mIns.Size(); i++) + { + if (mIns[i].mType == ASMIT_LDA && (mIns[i].mMode == ASMIM_IMMEDIATE || mIns[i].mMode == ASMIM_IMMEDIATE_ADDRESS)) + { + aimm = true; + ains = mIns[i]; + } + else if (mIns[i].mType == ASMIT_LDA && mIns[i].mMode == ASMIM_ZERO_PAGE && !this->ChangesZeroPage(mIns[i].mAddress)) + { + int j = 0; + while (j < lblocks.Size() && !lblocks[j]->ChangesZeroPage(mIns[i].mAddress)) + j++; + if (j == lblocks.Size()) + { + aimm = true; + ains = mIns[i]; + } + else + aimm = false; + } + else if (mIns[i].mType == ASMIT_LDX && (mIns[i].mMode == ASMIM_IMMEDIATE || mIns[i].mMode == ASMIM_IMMEDIATE_ADDRESS)) + { + ximm = true; + xins = mIns[i]; + } + else if (mIns[i].mType == ASMIT_LDX && mIns[i].mMode == ASMIM_ZERO_PAGE && !this->ChangesZeroPage(mIns[i].mAddress)) + { + int j = 0; + while (j < lblocks.Size() && !lblocks[j]->ChangesZeroPage(mIns[i].mAddress)) + j++; + if (j == lblocks.Size()) + { + ximm = true; + xins = mIns[i]; + } + else + ximm = false; + } + else if (mIns[i].mType == ASMIT_LDY && (mIns[i].mMode == ASMIM_IMMEDIATE || mIns[i].mMode == ASMIM_IMMEDIATE_ADDRESS)) + { + yimm = true; + yins = mIns[i]; + } + else if (mIns[i].mType == ASMIT_LDY && mIns[i].mMode == ASMIM_ZERO_PAGE && !this->ChangesZeroPage(mIns[i].mAddress)) + { + int j = 0; + while (j < lblocks.Size() && !lblocks[j]->ChangesZeroPage(mIns[i].mAddress)) + j++; + if (j == lblocks.Size()) + { + yimm = true; + yins = mIns[i]; + } + else + yimm = false; + } + else if ( + mIns[i].mType == ASMIT_STA && mIns[i].mMode == ASMIM_ZERO_PAGE && aimm || + mIns[i].mType == ASMIT_STX && mIns[i].mMode == ASMIM_ZERO_PAGE && ximm || + mIns[i].mType == ASMIT_STY && mIns[i].mMode == ASMIM_ZERO_PAGE && yimm) + { + int reg = mIns[i].mAddress; + if (!ReferencedOnPath(this, 0, i, reg) && !ChangedOnPath(this, i + 1, mIns.Size(), reg)) + { + int k = 0; + while (k < lblocks.Size() && !(lblocks[k] != this && lblocks[k]->ChangesZeroPage(reg))) + k++; + if (k == lblocks.Size()) + { + NativeCodeInstruction ins; + if (mIns[i].mType == ASMIT_STA) + ins = ains; + else if (mIns[i].mType == ASMIT_STX) + ins = xins; + else if (mIns[i].mType == ASMIT_STY) + ins = yins; + + if (inserta >= 0) + { + pblock->mIns.Insert(inserta++, NativeCodeInstruction(mIns[i].mIns, ASMIT_LDA, ins)); + pblock->mIns.Insert(inserta++, NativeCodeInstruction(mIns[i].mIns, ASMIT_STA, ASMIM_ZERO_PAGE, reg)); + } + else if (insertx >= 0) + { + pblock->mIns.Insert(insertx++, NativeCodeInstruction(mIns[i].mIns, ASMIT_LDX, ins)); + pblock->mIns.Insert(insertx++, NativeCodeInstruction(mIns[i].mIns, ASMIT_STX, ASMIM_ZERO_PAGE, reg)); + } + else if (inserty >= 0) + { + pblock->mIns.Insert(inserty++, NativeCodeInstruction(mIns[i].mIns, ASMIT_LDY, ins)); + pblock->mIns.Insert(inserty++, NativeCodeInstruction(mIns[i].mIns, ASMIT_STY, ASMIM_ZERO_PAGE, reg)); + } + + mIns[i].mType = ASMIT_NOP; mIns[i].mMode = ASMIM_IMPLIED; + changed = true; + + mEntryRequiredRegs += reg; + mExitRequiredRegs += reg; + pblock->mExitRequiredRegs += reg; + + for (int i = 0; i < lblocks.Size(); i++) + { + lblocks[i]->mEntryRequiredRegs += reg; + lblocks[i]->mExitRequiredRegs += reg; + } + + } + } + } + else + { + if (mIns[i].ChangesAccu()) + aimm = false; + if (mIns[i].ChangesXReg()) + ximm = false; + if (mIns[i].ChangesYReg()) + yimm = false; + } + } + } + + if (!changed) + { + changed = OptimizeSingleEntryLoopInvariant(proc, pblock, eblocks, lblocks); + } + } + } + } + + if (mTrueJump && mTrueJump->OptimizeSingleEntryLoop(proc)) + changed = true; + if (mFalseJump && mFalseJump->OptimizeSingleEntryLoop(proc)) + changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::OptimizeLoopRegisterWrapAround(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + if (mFalseJump) + { + NativeCodeBasicBlock* hblock = nullptr; + + int sz = mIns.Size(); + if (sz > 0) + { + hblock = nullptr; + + if (mTrueJump->mLoopHead) + hblock = mTrueJump; + else if (mFalseJump->mLoopHead) + hblock = mFalseJump; + + if (hblock && hblock->mIns.Size() > 0 && hblock->mNumEntries == 2) + { + NativeCodeBasicBlock* pblock; + if (hblock->mEntryBlocks[0] == this) + pblock = hblock->mEntryBlocks[1]; + else + pblock = hblock->mEntryBlocks[0]; + + if (!pblock->mFalseJump) + { + int k = pblock->mIns.Size(); + while (k > 0 && (pblock->mIns[k - 1].mLive & LIVE_CPU_REG_Z)) + k--; + + if (k > 0 && hblock->mIns[0].mType == ASMIT_TAX && mIns[sz - 1].mType == ASMIT_TXA && !(hblock->mIns[0].mLive & LIVE_CPU_REG_Z)) + { + pblock->mIns.Insert(k, NativeCodeInstruction(hblock->mIns[0].mIns, ASMIT_TAX)); + hblock->mIns.Remove(0); + + pblock->mExitRequiredRegs += CPU_REG_X; + hblock->mEntryRequiredRegs += CPU_REG_X; + mExitRequiredRegs += CPU_REG_X; + changed = true; + } + else if (k > 0 && hblock->mIns[0].mType == ASMIT_TAY && mIns[sz - 1].mType == ASMIT_TYA && !(hblock->mIns[0].mLive & LIVE_CPU_REG_Z)) + { + pblock->mIns.Insert(k, NativeCodeInstruction(hblock->mIns[0].mIns, ASMIT_TAY)); + hblock->mIns.Remove(0); + + pblock->mExitRequiredRegs += CPU_REG_Y; + hblock->mEntryRequiredRegs += CPU_REG_Y; + mExitRequiredRegs += CPU_REG_Y; + changed = true; + } + else if (k > 0 && sz > 1 && hblock->mIns[0].mType == ASMIT_LDA && mIns[sz - 1].mType == ASMIT_CMP && mIns[sz - 2].mType == ASMIT_LDA + && hblock->mIns[0].SameEffectiveAddress(mIns[sz - 2]) && !(hblock->mIns[0].mLive & LIVE_CPU_REG_Z) && !(hblock->mIns[0].mFlags & NCIF_VOLATILE)) + { + pblock->mIns.Insert(k, hblock->mIns[0]); + hblock->mIns.Remove(0); + + pblock->mExitRequiredRegs += CPU_REG_A; + hblock->mEntryRequiredRegs += CPU_REG_A; + mExitRequiredRegs += CPU_REG_A; + changed = true; + } + else + { + int finalA = -1, finalX = -1, finalY = -1; + for (int i = sz - 1; i >= 0; i--) + { + NativeCodeInstruction& ins(mIns[i]); + + if (finalA == -1 && (ins.mType == ASMIT_LDA || ins.mType == ASMIT_STA) && ins.mMode == ASMIM_ZERO_PAGE && !ChangesZeroPage(ins.mAddress, i + 1)) + finalA = ins.mAddress; + else if (finalX == -1 && (ins.mType == ASMIT_LDX || ins.mType == ASMIT_STX) && ins.mMode == ASMIM_ZERO_PAGE && !ChangesZeroPage(ins.mAddress, i + 1)) + finalX = ins.mAddress; + else if (finalY == -1 && (ins.mType == ASMIT_LDY || ins.mType == ASMIT_STY) && ins.mMode == ASMIM_ZERO_PAGE && !ChangesZeroPage(ins.mAddress, i + 1)) + finalY = ins.mAddress; + else if (finalA == -1 && ins.ChangesAccu()) + finalA = -2; + else if (finalX == -1 && ins.ChangesXReg()) + finalX = -2; + else if (finalY == -1 && ins.ChangesYReg()) + finalY = -2; + } + + bool usedA = false, usedX = false, usedY = false; + for (int i = 0; i < hblock->mIns.Size(); i++) + { + NativeCodeInstruction& ins(hblock->mIns[i]); + if (!usedX && ins.mType == ASMIT_LDX && ins.mMode == ASMIM_ZERO_PAGE) + { + if (ins.mAddress == finalA && !mExitRequiredRegs[CPU_REG_X]) + { + int k = sz - 1; + if (mIns[k].mType == ASMIT_CMP || mIns[k].mType == ASMIT_CPX || mIns[k].mType == ASMIT_CPY || mIns[k].mType == ASMIT_INC || mIns[k].mType == ASMIT_DEC || mIns[k].mType == ASMIT_INY || mIns[k].mType == ASMIT_DEY) + k--; + if (!(mIns[k].mLive & LIVE_CPU_REG_X)) + { + pblock->mIns.Push(ins); + mIns.Insert(k + 1, NativeCodeInstruction(ins.mIns, ASMIT_TAX)); + for (int j = k + 1; j < sz + 1; j++) + mIns[j].mLive |= LIVE_CPU_REG_X; + for (int j = 0; j < i; j++) + hblock->mIns[j].mLive |= LIVE_CPU_REG_X; + if (ins.mLive & LIVE_CPU_REG_Z) + { + ins.mType = ASMIT_CPX; + ins.mMode = ASMIM_IMMEDIATE; + ins.mAddress = 0; + } + else + { + ins.mType = ASMIT_NOP; + ins.mMode = ASMIM_IMPLIED; + } + + mExitRequiredRegs += CPU_REG_X; + pblock->mExitRequiredRegs += CPU_REG_X; + hblock->mEntryRequiredRegs += CPU_REG_X; + changed = true; + } + + } + usedX = true; + } + + if (!usedA && ins.ReferencesAccu()) + usedA = true; + if (!usedX && ins.ReferencesXReg()) + usedX = true; + if (!usedY && ins.ReferencesYReg()) + usedY = true; + + if (finalA >= 0 && ins.ChangesZeroPage(finalA)) + finalA = -1; + if (finalX >= 0 && ins.ChangesZeroPage(finalX)) + finalX = -1; + if (finalY >= 0 && ins.ChangesZeroPage(finalY)) + finalY = -1; + } + } + } + } + } + } + + if (mTrueJump && mTrueJump->OptimizeLoopRegisterWrapAround()) + changed = true; + if (mFalseJump && mFalseJump->OptimizeLoopRegisterWrapAround()) + changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::OptimizeLoopCarryOver(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + if (mFalseJump) + { + NativeCodeBasicBlock* hblock = nullptr; + + if (mBranch == ASMIT_BCC && mTrueJump->mLoopHead) + hblock = mTrueJump; + else if (mBranch == ASMIT_BCS && mFalseJump->mLoopHead) + hblock = mFalseJump; + + if (hblock && hblock->mIns.Size() > 0 && hblock->mIns[0].mType == ASMIT_CLC && hblock->mNumEntries == 2) + { + NativeCodeBasicBlock* pblock; + if (hblock->mEntryBlocks[0] == this) + pblock = hblock->mEntryBlocks[1]; + else + pblock = hblock->mEntryBlocks[0]; + + if (!pblock->mFalseJump) + { + pblock->mIns.Push(NativeCodeInstruction(hblock->mIns[0].mIns, ASMIT_CLC)); + hblock->mIns.Remove(0); + + pblock->mExitRequiredRegs += CPU_REG_C; + hblock->mEntryRequiredRegs += CPU_REG_C; + mExitRequiredRegs += CPU_REG_C; + changed = true; + } + } + } + +#if 1 + if (!changed && mLoopHead && mEntryBlocks.Size() == 2) + { + int i = 0; + while (i < mIns.Size() && !mIns[i].ReferencesCarry()) + i++; + if (i < mIns.Size() && mIns[i].mType == ASMIT_CLC) + { + NativeCodeBasicBlock * pblock = nullptr, * eblock = nullptr; + if (mEntryBlocks[0]->mFalseJump && !mEntryBlocks[1]->mFalseJump) + { + eblock = mEntryBlocks[0]; + pblock = mEntryBlocks[1]; + } + else if (!mEntryBlocks[0]->mFalseJump && mEntryBlocks[1]->mFalseJump) + { + eblock = mEntryBlocks[1]; + pblock = mEntryBlocks[0]; + } + + if (eblock && (eblock->mBranch != ASMIT_BCC && eblock->mBranch != ASMIT_BCS) && !eblock->mLoopHead) + { + int j = eblock->mIns.Size() - 1; + while (j >= 0 && !eblock->mIns[j].ReferencesCarry()) + j--; + if (j < 0) + { + if (eblock->mEntryBlocks.Size() == 2) + { + NativeCodeBasicBlock* peblock = nullptr, * deblock = nullptr; + NativeCodeBasicBlock* eblock0 = eblock->mEntryBlocks[0], * eblock1 = eblock->mEntryBlocks[1]; + + if (eblock0->mFalseJump && !eblock1->mFalseJump) + { + if (eblock0->mFalseJump == eblock && eblock0->mTrueJump == eblock1 && eblock0->mBranch == ASMIT_BCS || + eblock0->mTrueJump == eblock && eblock0->mFalseJump == eblock1 && eblock0->mBranch == ASMIT_BCC) + { + peblock = eblock0; + deblock = eblock1; + } + } + else if (eblock1->mFalseJump && !eblock0->mFalseJump) + { + if (eblock1->mFalseJump == eblock && eblock1->mTrueJump == eblock0 && eblock1->mBranch == ASMIT_BCS || + eblock1->mTrueJump == eblock && eblock1->mFalseJump == eblock0 && eblock1->mBranch == ASMIT_BCC) + { + peblock = eblock1; + deblock = eblock0; + } + } + + if (peblock && deblock->mEntryBlocks.Size() == 1) + { + deblock->mIns.Push(mIns[i]); + pblock->mIns.Push(mIns[i]); + mIns.Remove(i); + + for (int k = 0; k < i; k++) + mIns[k].mLive |= LIVE_CPU_REG_C; + for (int k = 0; k < eblock->mIns.Size(); k++) + eblock->mIns[k].mLive |= LIVE_CPU_REG_C; + + deblock->mExitRequiredRegs += CPU_REG_C; + peblock->mExitRequiredRegs += CPU_REG_C; + pblock->mExitRequiredRegs += CPU_REG_C; + eblock->mExitRequiredRegs += CPU_REG_C; + eblock->mEntryRequiredRegs += CPU_REG_C; + mEntryRequiredRegs += CPU_REG_C; + changed = true; + } + } + + } + } + } + } +#endif + if (mTrueJump && mTrueJump->OptimizeLoopCarryOver()) + changed = true; + if (mFalseJump && mFalseJump->OptimizeLoopCarryOver()) + changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::RemoveSimpleLoopUnusedIndex(void) +{ + bool changed = false; + + assert(mEntryRequiredRegs.Size() > 0); + + if (!mVisited) + { + mVisited = true; + + if (mFalseJump && mTrueJump == this) + { + NumberSet required(mFalseJump->mEntryRequiredRegs); + + bool complex = false; + + int k = mIns.Size() - 1; + while (k >= 0) + { + NativeCodeInstruction & ins(mIns[k]); + + if (ins.mType == ASMIT_STA && ins.mMode == ASMIM_ZERO_PAGE && !(ins.mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C | LIVE_CPU_REG_Z)) && !required[ins.mAddress]) + { + if (k > 2 && mIns[k - 1].mMode == ASMIM_IMMEDIATE && mIns[k - 2].mType == ASMIT_LDA && mIns[k - 2].mMode == ASMIM_ZERO_PAGE && mIns[k - 2].mAddress == mIns[k].mAddress) + k -= 2; + else + required += ins.mAddress; + } + else if (ins.mMode == ASMIM_ZERO_PAGE) + required += ins.mAddress; + else if (ins.mMode == ASMIM_INDIRECT_Y) + { + required += ins.mAddress; + required += ins.mAddress + 1; + } + else if (ins.mType == ASMIT_JSR) + { + required += BC_REG_ADDR + 0; + required += BC_REG_ADDR + 1; + for (int i = 0; i < 4; i++) + required += BC_REG_ACCU + i; + for (int i = 0; i < 8; i++) + required += BC_REG_WORK + i; + required += BC_REG_WORK_Y; + + if (!(ins.mFlags & NCIF_RUNTIME) || (ins.mFlags & NCIF_FEXEC)) + { +#if 1 + if (ins.mLinkerObject) + { + LinkerObject* lo = ins.mLinkerObject; + + for (int i = 0; i < 256; i++) + { + if (lo->mZeroPageSet[i]) + required += i; + } + + for (int i = 0; i < ins.mLinkerObject->mNumTemporaries; i++) + { + for (int j = 0; j < ins.mLinkerObject->mTempSizes[i]; j++) + required += ins.mLinkerObject->mTemporaries[i] + j; + } + } + +#else + for (int i = BC_REG_TMP; i < BC_REG_TMP_SAVED; i++) + required += i; + for (int i = BC_REG_FPARAMS; i < BC_REG_FPARAMS_END; i++) + required += i; +#endif + } + +// complex = true; + + } + k--; + } + + if (!complex) + { + for (int i = 0; i < mIns.Size(); i++) + { + if (mIns[i].mType == ASMIT_STA && mIns[i].mMode == ASMIM_ZERO_PAGE && !required[mIns[i].mAddress]) + { + mIns[i].mType = ASMIT_NOP; + mIns[i].mMode = ASMIM_IMPLIED; + changed = true; + } + } + } + } + + if (mTrueJump && mTrueJump->RemoveSimpleLoopUnusedIndex()) + changed = true; + if (mFalseJump && mFalseJump->RemoveSimpleLoopUnusedIndex()) + changed = true; + + } + + return changed; +} + +static bool Is16BitInc(NativeCodeBasicBlock* block, int reg, int at) +{ + if (at + 6 < block->mIns.Size()) + { + if (block->mIns[at + 0].mType == ASMIT_CLC && + block->mIns[at + 1].mType == ASMIT_LDA && block->mIns[at + 1].mMode == ASMIM_ZERO_PAGE && block->mIns[at + 1].mAddress == reg && + block->mIns[at + 2].mType == ASMIT_ADC && block->mIns[at + 2].mMode == ASMIM_IMMEDIATE && block->mIns[at + 2].mAddress == 1 && + block->mIns[at + 3].mType == ASMIT_STA && block->mIns[at + 3].mMode == ASMIM_ZERO_PAGE && block->mIns[at + 3].mAddress == reg && + block->mIns[at + 4].mType == ASMIT_LDA && block->mIns[at + 4].mMode == ASMIM_ZERO_PAGE && block->mIns[at + 4].mAddress == reg + 1 && + block->mIns[at + 5].mType == ASMIT_ADC && block->mIns[at + 5].mMode == ASMIM_IMMEDIATE && block->mIns[at + 5].mAddress == 0 && + block->mIns[at + 6].mType == ASMIT_STA && block->mIns[at + 6].mMode == ASMIM_ZERO_PAGE && block->mIns[at + 6].mAddress == reg + 1 && + !(block->mIns[at + 6].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z | LIVE_CPU_REG_C))) + { + return true; + } + } + + return false; +} + +static bool IsPointerIncAddrs(NativeCodeBasicBlock* block, int reg) +{ + bool changed = false; + bool used = false; + + int i = 0; + while (i < block->mIns.Size()) + { + NativeCodeInstruction ins(block->mIns[i]); + + if (ins.mMode == ASMIM_INDIRECT_Y) + { + if (ins.mAddress == reg) + { + if (changed) + return false; + used = true; + i++; + } + else + return false; + } + else if (Is16BitInc(block, reg, i)) + { + changed = true; + i += 7; + } + else if (ins.ReferencesZeroPage(reg) || ins.ReferencesZeroPage(reg + 1) || ins.ReferencesYReg()) + return false; + else + i++; + } + + return changed; +} + +bool NativeCodeBasicBlock::OptimizeSimpleYIndexedLoop(NativeCodeBasicBlock* prevBlock, NativeCodeBasicBlock* exitBlock) +{ + char regs[256]; + for (int i = 0; i <256; i++) + regs[i] = 0; + + // 1 used + // 2 indexed + // 4 16inc + + int i = 0; + while (i < mIns.Size()) + { + NativeCodeInstruction& ins(mIns[i]); + if (ins.mMode == ASMIM_INDIRECT_Y) + { + if ((regs[ins.mAddress] & 5) || (regs[ins.mAddress + 1] & 5)) + return false; + regs[ins.mAddress] |= 2; + i++; + } + else if (ins.mMode == ASMIM_ZERO_PAGE) + { + if (regs[ins.mAddress] & 1) + i++; + else if (i > 0 && Is16BitInc(this, ins.mAddress, i - 1)) + { + if (exitBlock->mEntryRequiredRegs[ins.mAddress] || exitBlock->mEntryRequiredRegs[ins.mAddress + 1]) + return false; + i += 6; + regs[ins.mAddress] |= 4; + } + else if (regs[ins.mAddress] & 2) + return false; + else + { + regs[ins.mAddress] |= 1; + i++; + } + } + else if (ins.mType == ASMIT_JSR) + return false; + else + i++; + } + + // All indirect Y must be incremented + for (int k = 0; k < 256; k++) + if (regs[k] == 1) + return false; + + bool changed = false; + + for(int i=0; imIns.Push(mIns[1]); + prevBlock->mExitRequiredRegs += CPU_REG_A; + mEntryRequiredRegs += CPU_REG_A; + mIns.Remove(1); + + CheckLive(); + + return true; + } + + if (!mEntryRequiredRegs[CPU_REG_A]) + { + for (int i = 0; i + 3 < mIns.Size(); i++) + { + if (mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && !(mIns[i + 3].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C | LIVE_CPU_REG_Z)) && + !ReferencesZeroPage(mIns[i + 3].mAddress, 0, i + 2) && !ChangesZeroPage(mIns[i + 1].mAddress)) + { + if (!prevBlock) + return OptimizeSimpleLoopInvariant(proc, full); + + prevBlock->mExitRequiredRegs += mIns[i + 3].mAddress; + mEntryRequiredRegs += mIns[i + 3].mAddress; + mExitRequiredRegs += mIns[i + 3].mAddress; + + prevBlock->mIns.Push(mIns[i + 0]); + prevBlock->mIns.Push(mIns[i + 1]); + prevBlock->mIns.Push(mIns[i + 2]); + prevBlock->mIns.Push(mIns[i + 3]); + mIns.Remove(i, 4); + changed = true; + } + } + } + + sz = mIns.Size(); + if (sz >= 3 && + mIns[sz - 3].mType == ASMIT_CLC && + mIns[sz - 2].mType == ASMIT_ADC && mIns[sz - 2].mMode == ASMIM_IMMEDIATE && mIns[sz - 2].mAddress == 1 && + !mIns[sz - 1].ChangesZFlag() && !mIns[sz - 1].ChangesCarry()) + { + if (mBranch == ASMIT_BCC) + { + mBranch = ASMIT_BNE; + mIns[sz - 2].mLive |= LIVE_CPU_REG_Z; + mIns[sz - 1].mLive |= LIVE_CPU_REG_Z; + } + else if (mBranch == ASMIT_BCS) + { + mBranch = ASMIT_BEQ; + mIns[sz - 2].mLive |= LIVE_CPU_REG_Z; + mIns[sz - 1].mLive |= LIVE_CPU_REG_Z; + } + } + +#if 1 + if (full && mEntryRequiredRegs.Size() && !mEntryRequiredRegs[CPU_REG_C] && (mBranch == ASMIT_BCC || mBranch == ASMIT_BCS)) + { + int i = 0; + while (i < mIns.Size() && !mIns[i].ChangesCarry()) + i++; + if (i < mIns.Size() && + mIns[i].mType == ASMIT_CLC && ((mBranch == ASMIT_BCC && mTrueJump == this) || (mBranch == ASMIT_BCS && mFalseJump == this)) || + mIns[i].mType == ASMIT_SEC && ((mBranch == ASMIT_BCS && mTrueJump == this) || (mBranch == ASMIT_BCC && mFalseJump == this))) + { + if (!prevBlock) + return OptimizeSimpleLoopInvariant(proc, full); + + if (mEntryRequiredRegs[CPU_REG_A]) mIns[i].mLive |= LIVE_CPU_REG_A; + if (mEntryRequiredRegs[CPU_REG_X]) mIns[i].mLive |= LIVE_CPU_REG_X; + if (mEntryRequiredRegs[CPU_REG_Y]) mIns[i].mLive |= LIVE_CPU_REG_Y; + + prevBlock->mIns.Push(mIns[i]); + prevBlock->mExitRequiredRegs += CPU_REG_C; + for (int j = 0; j < i; j++) + mIns[j].mLive |= LIVE_CPU_REG_C; + mEntryRequiredRegs += CPU_REG_C; + mIns.Remove(i); + + CheckLive(); + + return true; + } + } +#endif + + for (int i = 0; i + 1 < mIns.Size(); i++) + { + if (mIns[i + 0].mType == ASMIT_LDA && (mIns[i + 0].mMode == ASMIM_IMMEDIATE || mIns[i + 0].mMode == ASMIM_IMMEDIATE_ADDRESS) && + mIns[i + 1].mType == ASMIT_STA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE) + { + int reg = mIns[i + 1].mAddress; + + if (!ReferencesZeroPage(reg, 0, i) && !ChangesZeroPage(reg, i + 2)) + { + if (!mEntryRequiredRegs[CPU_REG_A]) + { + if (!prevBlock) + return OptimizeSimpleLoopInvariant(proc, full); + + prevBlock->mIns.Push(mIns[i + 0]); + prevBlock->mIns.Push(mIns[i + 1]); + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + prevBlock->mExitRequiredRegs += reg; + mEntryRequiredRegs += reg; + mExitRequiredRegs += reg; + changed = true; + } + else if (!mEntryRequiredRegs[CPU_REG_X]) + { + if (!prevBlock) + return OptimizeSimpleLoopInvariant(proc, full); + + prevBlock->mIns.Push(NativeCodeInstruction(mIns[i + 0].mIns, ASMIT_LDX, mIns[i + 0])); + prevBlock->mIns.Push(NativeCodeInstruction(mIns[i + 1].mIns, ASMIT_STX, mIns[i + 1])); + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + prevBlock->mExitRequiredRegs += reg; + mEntryRequiredRegs += reg; + mExitRequiredRegs += reg; + changed = true; + } + } + } + if (mIns[i + 0].mType == ASMIT_STA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_STA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE) + { + int a0 = mIns[i + 0].mAddress, a1 = mIns[i + 1].mAddress; + if (!mEntryRequiredRegs[a0] && !ReferencesZeroPage(a0, i + 2) && !ChangesZeroPage(a1, i + 2)) + { + if (!exitBlock) + return OptimizeSimpleLoopInvariant(proc, full); + + if (exitBlock->mNumEntries == 1 && !exitBlock->mEntryRequiredRegs[CPU_REG_Z]) + { + if (!exitBlock->mEntryRequiredRegs[CPU_REG_A]) + { + mExitRequiredRegs += a1; + exitBlock->mEntryRequiredRegs += a1; + exitBlock->mIns.Insert(0, NativeCodeInstruction(mIns[i + 0].mIns, ASMIT_LDA, mIns[i + 1])); + exitBlock->mIns.Insert(1, NativeCodeInstruction(mIns[i + 0].mIns, ASMIT_STA, mIns[i + 0])); + mIns.Remove(i + 0); + sz--; + changed = true; + } + } + } + else if (!mEntryRequiredRegs[a1] && !ReferencesZeroPage(a1, i + 2) && !ChangesZeroPage(a0, i + 2)) + { + if (!exitBlock) + return OptimizeSimpleLoopInvariant(proc, full); + + if (exitBlock->mNumEntries == 1 && !exitBlock->mEntryRequiredRegs[CPU_REG_Z]) + { + if (!exitBlock->mEntryRequiredRegs[CPU_REG_A]) + { + mExitRequiredRegs += a0; + exitBlock->mEntryRequiredRegs += a0; + exitBlock->mIns.Insert(0, NativeCodeInstruction(mIns[i + 1].mIns, ASMIT_LDA, mIns[i + 0])); + exitBlock->mIns.Insert(1, NativeCodeInstruction(mIns[i + 1].mIns, ASMIT_STA, mIns[i + 1])); + mIns.Remove(i + 1); + sz--; + changed = true; + } + } + } + } + + } + + if (sz >= 3 && mIns[0].mType == ASMIT_LDA && mIns[sz - 2].mType == ASMIT_LDA && mIns[0].SameEffectiveAddress(mIns[sz - 2]) && mIns[sz - 1].mType == ASMIT_CMP) + { + if (!prevBlock) + return OptimizeSimpleLoopInvariant(proc, full); + + prevBlock->mIns.Push(mIns[0]); + mIns.Remove(0); + + mEntryRequiredRegs += CPU_REG_A; + mExitRequiredRegs += CPU_REG_A; + prevBlock->mExitRequiredRegs += CPU_REG_A; + + prevBlock->CheckLive(); + CheckLive(); + + return true; + } + + if (sz >= 2 && mIns[0].mType == ASMIT_LDA && mIns[sz - 1].mType == ASMIT_LDA && mIns[0].SameEffectiveAddress(mIns[sz - 1])) + { + if (!prevBlock) + return OptimizeSimpleLoopInvariant(proc, full); + + prevBlock->mIns.Push(mIns[0]); + mIns.Remove(0); + + mEntryRequiredRegs += CPU_REG_A; + mExitRequiredRegs += CPU_REG_A; + prevBlock->mExitRequiredRegs += CPU_REG_A; + + prevBlock->CheckLive(); + CheckLive(); + + return true; + } + + if (sz >= 3 && mIns[0].mType == ASMIT_LDY && mIns[sz - 2].mType == ASMIT_LDA && mIns[0].SameEffectiveAddress(mIns[sz - 2]) && + mIns[sz - 1].mType == ASMIT_CMP && HasAsmInstructionMode(ASMIT_CPY, mIns[sz - 1].mMode) && !(mIns[sz - 1].mLive & LIVE_CPU_REG_A)) + { + if (!prevBlock) + return OptimizeSimpleLoopInvariant(proc, full); + + mIns[sz - 2].mType = ASMIT_LDY; mIns[sz - 2].mLive |= LIVE_CPU_REG_Y; + mIns[sz - 1].mType = ASMIT_CPY; mIns[sz - 1].mLive |= LIVE_CPU_REG_Y; + + prevBlock->mIns.Push(mIns[0]); + mIns.Remove(0); + + mExitRequiredRegs += CPU_REG_Y; + mEntryRequiredRegs += CPU_REG_Y; + prevBlock->mExitRequiredRegs += CPU_REG_Y; + + prevBlock->CheckLive(); + CheckLive(); + + return true; + } + + if (sz >= 2 && mIns[0].mType == ASMIT_LDY && mIns[sz - 1].mType == ASMIT_LDA && mIns[0].SameEffectiveAddress(mIns[sz - 1]) && !(mIns[sz - 1].mLive & LIVE_CPU_REG_A) && !mExitRequiredRegs[CPU_REG_Y]) + { + if (!prevBlock) + return OptimizeSimpleLoopInvariant(proc, full); + + mIns[sz - 1].mType = ASMIT_LDY; mIns[sz - 1].mLive |= LIVE_CPU_REG_Y; + + prevBlock->mIns.Push(mIns[0]); + mIns.Remove(0); + + mExitRequiredRegs += CPU_REG_Y; + mEntryRequiredRegs += CPU_REG_Y; + prevBlock->mExitRequiredRegs += CPU_REG_Y; + + prevBlock->CheckLive(); + CheckLive(); + + return true; + } + + if (sz >= 4 && mIns[0].mType == ASMIT_TAY && mIns[sz - 3].mType == ASMIT_INX && mIns[sz - 2].mType == ASMIT_TXA && mIns[sz - 1].mType == ASMIT_CPX && !ChangesYReg(1) && !ReferencesXReg(0, sz - 3)) + { + if (!prevBlock) + return OptimizeSimpleLoopInvariant(proc, full); + + for (int i = 0; i < sz; i++) + mIns[i].mLive |= LIVE_CPU_REG_Y; + + mIns[sz - 3].mType = ASMIT_INY; + mIns[sz - 2].mType = ASMIT_TYA; + mIns[sz - 1].mType = ASMIT_CPY; + + if (exitBlock->mEntryRequiredRegs[CPU_REG_X]) + { + exitBlock->mEntryRequiredRegs += CPU_REG_A; + exitBlock->mIns.Insert(0, NativeCodeInstruction(mIns[sz - 2].mIns, ASMIT_TAX)); + } + + prevBlock->mIns.Push(mIns[0]); + mIns.Remove(0); + + mExitRequiredRegs += CPU_REG_Y; + mEntryRequiredRegs += CPU_REG_Y; + prevBlock->mExitRequiredRegs += CPU_REG_Y; + + prevBlock->CheckLive(); + CheckLive(); + + return true; + } + + if (sz >= 5 && full && + mIns[sz - 5].mType == ASMIT_TYA && mIns[sz - 4].mType == ASMIT_CLC && mIns[sz - 3].mType == ASMIT_ADC && mIns[sz - 3].mMode == ASMIM_IMMEDIATE && mIns[sz - 2].mType == ASMIT_TAY && + mIns[sz - 1].mType == ASMIT_CPY && mIns[sz - 1].mMode == ASMIM_IMMEDIATE && mBranch == ASMIT_BNE && + !ChangesYReg(0, sz - 5) && !ChangesCarry(0, sz - 5)) + { + if (pblock) + { + int yend = mIns[sz - 1].mAddress; + int ystart = pblock->RetrieveYValue(pblock->mIns.Size() - 1); + int ystep = mIns[sz - 3].mAddress; + if (ystart >= 0 && yend > ystart && (yend - ystart) % ystep == 0) + { + mIns[sz - 1].mLive |= LIVE_CPU_REG_C; + mBranch = ASMIT_BCC; + changed = true; +#if 0 + prevBlock->mIns.Push(mIns[sz - 4]); + mIns.Remove(sz - 4); + for (int i = 0; i < sz - 4; i++) + mIns[i].mLive |= LIVE_CPU_REG_C; + + mExitRequiredRegs += CPU_REG_C; + mEntryRequiredRegs += CPU_REG_C; + prevBlock->mExitRequiredRegs += CPU_REG_C; + + prevBlock->CheckLive(); + CheckLive(); + + return true; +#endif + } + } + } + + + if (sz >= 3 && mIns[0].mType == ASMIT_LDX && mIns[sz - 2].mType == ASMIT_LDA && mIns[0].SameEffectiveAddress(mIns[sz - 2]) && + mIns[sz - 1].mType == ASMIT_CMP && HasAsmInstructionMode(ASMIT_CPX, mIns[sz - 1].mMode) && !(mIns[sz - 1].mLive & LIVE_CPU_REG_A)) + { + if (!prevBlock) + return OptimizeSimpleLoopInvariant(proc, full); + + mIns[sz - 2].mType = ASMIT_LDX; mIns[sz - 2].mLive |= LIVE_CPU_REG_X; + mIns[sz - 1].mType = ASMIT_CPX; mIns[sz - 1].mLive |= LIVE_CPU_REG_X; + + prevBlock->mIns.Push(mIns[0]); + mIns.Remove(0); + + mExitRequiredRegs += CPU_REG_X; + mEntryRequiredRegs += CPU_REG_X; + prevBlock->mExitRequiredRegs += CPU_REG_X; + + prevBlock->CheckLive(); + CheckLive(); + + return true; + } + + int si = 0, ei = mIns.Size() - 1; + while (si < mIns.Size() && !mIns[si].ReferencesYReg()) + si++; + while (ei > si && !(mIns[ei].ChangesYReg() || mIns[ei].mType == ASMIT_STY)) + ei--; + + if (si < ei && mIns[si].mType == ASMIT_LDY && mIns[ei].mType == ASMIT_STY && mIns[si].mMode == ASMIM_ZERO_PAGE && mIns[ei].mMode == ASMIM_ZERO_PAGE && mIns[si].mAddress == mIns[ei].mAddress) + { + int i = 0; + while (i < si && !mIns[i].ChangesZeroPage(mIns[si].mAddress)) + i++; + + if (i == si) + { + i = ei + 1; + while (i < mIns.Size() && !mIns[i].ChangesZeroPage(mIns[si].mAddress)) + i++; + + if (i == mIns.Size()) + { + if (!prevBlock) + return OptimizeSimpleLoopInvariant(proc, full); + + i = 0; + while (i < si) + { + mIns[i].mLive |= LIVE_CPU_REG_Y; + i++; + } + + i = ei; + while (i < mIns.Size()) + { + mIns[i].mLive |= LIVE_CPU_REG_Y; + i++; + } + + if (prevBlock->mExitRequiredRegs[CPU_REG_C]) + mIns[si].mLive |= LIVE_CPU_REG_C; + prevBlock->mIns.Push(mIns[si]); + mIns.Remove(si); + + mEntryRequiredRegs += CPU_REG_Y; + mExitRequiredRegs += CPU_REG_Y; + + CheckLive(); + prevBlock->CheckLive(); + + return true; + } + } + } + + if (si < ei && mIns[ei].mType == ASMIT_STY && mIns[ei].mMode == ASMIM_ZERO_PAGE) + { + int j = 0; + while (j < mIns.Size() && (j == ei || !(mIns[j].ChangesZeroPage(mIns[ei].mAddress) || mIns[j].UsesZeroPage(mIns[ei].mAddress)))) + j++; + if (j == mIns.Size()) + { + if (!prevBlock) + return OptimizeSimpleLoopInvariant(proc, full); + mExitRequiredRegs += CPU_REG_Y; + exitBlock->mEntryRequiredRegs += CPU_REG_Y; + + exitBlock->mIns.Insert(0, mIns[ei]); + mIns.Remove(ei); + + CheckLive(); + + return true; + } + } + + si = 0, ei = mIns.Size() - 1; + while (si < mIns.Size() && !mIns[si].ReferencesXReg()) + si++; + while (ei > si && !(mIns[ei].ChangesXReg() || mIns[ei].mType == ASMIT_STX)) + ei--; + + if (si < ei && mIns[si].mType == ASMIT_LDX && mIns[ei].mType == ASMIT_STX && mIns[si].mMode == ASMIM_ZERO_PAGE && mIns[ei].mMode == ASMIM_ZERO_PAGE && mIns[si].mAddress == mIns[ei].mAddress) + { + int i = 0; + while (i < si && !mIns[i].ChangesZeroPage(mIns[si].mAddress)) + i++; + + if (i == si) + { + i = ei + 1; + while (i < mIns.Size() && !mIns[i].ChangesZeroPage(mIns[si].mAddress)) + i++; + + if (i == mIns.Size()) + { + if (!prevBlock) + return OptimizeSimpleLoopInvariant(proc, full); + + i = 0; + while (i < si) + { + mIns[i].mLive |= LIVE_CPU_REG_X; + i++; + } + + i = ei; + while (i < mIns.Size()) + { + mIns[i].mLive |= LIVE_CPU_REG_X; + i++; + } + + if (prevBlock->mExitRequiredRegs[CPU_REG_C]) + mIns[si].mLive |= LIVE_CPU_REG_C; + prevBlock->mIns.Push(mIns[si]); + mIns.Remove(si); + + prevBlock->mExitRequiredRegs += CPU_REG_X; + mEntryRequiredRegs += CPU_REG_X; + mExitRequiredRegs += CPU_REG_X; + + CheckLive(); + prevBlock->CheckLive(); + + return true; + } + } + } + + + + si = 0; + ei = mIns.Size() - 1; + while (si < mIns.Size() && !mIns[si].ReferencesXReg()) + si++; + while (ei > si && !mIns[ei].ReferencesXReg()) + ei--; + + if (si < ei && mIns[si].mType == ASMIT_LDX && mIns[ei].mType == ASMIT_STX && mIns[si].mMode == ASMIM_ZERO_PAGE && mIns[ei].mMode == ASMIM_ZERO_PAGE && mIns[si].mAddress == mIns[ei].mAddress) + { + int i = 0; + while (i < si && !mIns[i].ChangesZeroPage(mIns[si].mAddress)) + i++; + + if (i == si) + { + i = ei + 1; + while (i < mIns.Size() && !mIns[i].ChangesZeroPage(mIns[si].mAddress)) + i++; + + if (i == mIns.Size()) + { + if (!prevBlock) + return OptimizeSimpleLoopInvariant(proc, full); + + i = 0; + while (i < si) + { + mIns[i].mLive |= LIVE_CPU_REG_X; + i++; + } + + i = ei; + while (i < mIns.Size()) + { + mIns[i].mLive |= LIVE_CPU_REG_X; + i++; + } + + prevBlock->mIns.Push(mIns[si]); + mIns.Remove(si); + + prevBlock->mExitRequiredRegs += CPU_REG_X; + + mEntryRequiredRegs += CPU_REG_X; + mExitRequiredRegs += CPU_REG_X; + + CheckLive(); + prevBlock->CheckLive(); + + return true; + } + } + } + + if (si + 2 < mIns.Size() && mIns[si].mType == ASMIT_LDX && mIns[si + 2].mType == ASMIT_STX && mIns[si].mMode == ASMIM_ZERO_PAGE && mIns[si + 2].mMode == ASMIM_ZERO_PAGE && mIns[si].mAddress == mIns[si + 2].mAddress && mIns[si + 1].mType == ASMIT_INX) + { + int i = 0; + while (i < si && !mIns[i].ChangesZeroPage(mIns[si].mAddress)) + i++; + + if (i == si) + { + i = si + 3; + while (i < mIns.Size() && !mIns[i].ChangesZeroPage(mIns[si].mAddress) && !mIns[i].ChangesXReg()) + i++; + + if (i == mIns.Size()) + { + if (!prevBlock) + return OptimizeSimpleLoopInvariant(proc, full); + + for(int i=0; imIns.Push(mIns[si]); + mIns.Remove(si); + + prevBlock->mExitRequiredRegs += CPU_REG_X; + + mEntryRequiredRegs += CPU_REG_X; + mExitRequiredRegs += CPU_REG_X; + + CheckLive(); + prevBlock->CheckLive(); + + return true; + } + } + } + + if (si < ei && mIns[si].mType == ASMIT_LDX && mIns[si].mMode == ASMIM_ZERO_PAGE) + { + // Loads X once from zero page and never changes it again + + int i = si; + while (i < ei && !mIns[i + 1].ChangesXReg()) + i++; + + if (i == ei) + { + i = 0; + while (i < si && !mIns[i].ReferencesZeroPage(mIns[si].mAddress)) + i++; + if (i == si) + { + i++; + while (i < mIns.Size() && !mIns[i].ReferencesZeroPage(mIns[si].mAddress)) + i++; + if (i < mIns.Size() && (mIns[i].mType == ASMIT_DEC || mIns[i].mType == ASMIT_INC) && (!(mIns[ei].mLive & LIVE_CPU_REG_Z) || mIns[ei].ChangesAccuAndFlag())) + { + int j = i + 1; + while (j < mIns.Size() && !mIns[j].ReferencesZeroPage(mIns[si].mAddress)) + j++; + if (j == mIns.Size()) + { + // So we have an LDX from ZP, and exactly one INC/DECof this ZP and X never changes in the loop + if (!prevBlock) + return OptimizeSimpleLoopInvariant(proc, full); + + bool needRecheck = mIns[ei].mLive & LIVE_CPU_REG_Z; + + prevBlock->mIns.Push(mIns[si]); + exitBlock->mIns.Insert(0, NativeCodeInstruction(mIns[si].mIns, ASMIT_STX, mIns[si])); + mIns[si].mType = ASMIT_NOP; + mIns[si].mMode = ASMIM_IMPLIED; + + AsmInsType t; + if (mIns[i].mType == ASMIT_DEC) + t = ASMIT_DEX; + else + t = ASMIT_INX; + + if (i < ei) + { + mIns[i].mType = ASMIT_NOP; + mIns[i].mMode = ASMIM_IMPLIED; + + if (needRecheck) + { + mIns[ei].mLive |= LIVE_CPU_REG_A; + mIns.Insert(ei + 1, NativeCodeInstruction(mIns[ei].mIns, ASMIT_ORA, ASMIM_IMMEDIATE, 0)); + } + + mIns.Insert(ei + 1, NativeCodeInstruction(mIns[i].mIns, t)); + } + else + { + mIns[i].mType = t; + mIns[i].mMode = ASMIM_IMPLIED; + } + + for (int i = 0; i < mIns.Size(); i++) + mIns[i].mLive |= LIVE_CPU_REG_X; + + mExitRequiredRegs += CPU_REG_X; + mEntryRequiredRegs += CPU_REG_X; + prevBlock->mExitRequiredRegs += CPU_REG_X; + exitBlock->mEntryRequiredRegs += CPU_REG_X; + + prevBlock->CheckLive(); + CheckLive(); + return true; + } + } + } + } + } + + if (si < ei && mIns[ei].mType == ASMIT_STX && mIns[ei].mMode == ASMIM_ZERO_PAGE) + { + int j = 0; + while (j < mIns.Size() && (j == ei || !(mIns[j].ChangesZeroPage(mIns[ei].mAddress) || mIns[j].UsesZeroPage(mIns[ei].mAddress)))) + j++; + if (j == mIns.Size()) + { + if (!prevBlock) + return OptimizeSimpleLoopInvariant(proc, full); + exitBlock->mIns.Insert(0, mIns[ei]); + mIns.Remove(ei); + + CheckLive(); + + return true; + } + } + + si = 0; + ei = mIns.Size() - 1; + while (si < mIns.Size() && !mIns[si].ReferencesAccu()) + si++; + while (ei > si && !mIns[ei].ReferencesAccu()) + ei--; + + if (si < ei && mIns[si].mType == ASMIT_LDA && mIns[ei].mType == ASMIT_STA && mIns[si].mMode == ASMIM_ZERO_PAGE && mIns[ei].mMode == ASMIM_ZERO_PAGE && mIns[si].mAddress == mIns[ei].mAddress) + { + int i = 0; + while (i < si && !mIns[i].ChangesZeroPage(mIns[si].mAddress)) + i++; + + if (i == si) + { + i = ei + 1; + while (i < mIns.Size() && !mIns[i].ChangesZeroPage(mIns[si].mAddress)) + i++; + + if (i == mIns.Size()) + { + if (!prevBlock) + return OptimizeSimpleLoopInvariant(proc, full); + + i = 0; + while (i < si) + { + mIns[i].mLive |= LIVE_CPU_REG_A; + i++; + } + + i = ei; + while (i < mIns.Size()) + { + mIns[i].mLive |= LIVE_CPU_REG_A; + i++; + } + + prevBlock->mIns.Push(mIns[si]); + mIns.Remove(si); + + mEntryRequiredRegs += CPU_REG_A; + mExitRequiredRegs += CPU_REG_A; + prevBlock->mExitRequiredRegs += CPU_REG_A; + + prevBlock->CheckLive(); + CheckLive(); + + return true; + } + } + } + + ei = mIns.Size() - 1; + while (ei > si && !mIns[ei].ReferencesYReg()) + ei--; + + if (si < ei && mIns[si].mType == ASMIT_TYA && mIns[ei].mType == ASMIT_TAY) + { + int i = 0; + while (i < si && !mIns[i].ChangesYReg()) + i++; + if (i == si) + { + i = ei + 1; + while (i < mIns.Size() && !mIns[i].ChangesAccu()) + i++; + + if (i == mIns.Size()) + { + if (!prevBlock) + return OptimizeSimpleLoopInvariant(proc, full); + + i = 0; + while (i < si) + { + mIns[i].mLive |= LIVE_CPU_REG_A; + i++; + } + + i = ei; + while (i < mIns.Size()) + { + mIns[i].mLive |= LIVE_CPU_REG_A; + i++; + } + + prevBlock->mIns.Push(mIns[si]); + mIns.Remove(si); + + mEntryRequiredRegs += CPU_REG_A; + mExitRequiredRegs += CPU_REG_A; + prevBlock->mExitRequiredRegs += CPU_REG_A; + + prevBlock->CheckLive(); + CheckLive(); + + return true; + } + } + } + + if (si + 2 < ei && + mIns[si + 0].mType == ASMIT_LDA && + mIns[si + 1].mType == ASMIT_CLC && + mIns[si + 2].mType == ASMIT_ADC && + mIns[ei].mType == ASMIT_STA && mIns[si + 2].mMode == ASMIM_ZERO_PAGE && mIns[ei].mMode == ASMIM_ZERO_PAGE && mIns[si + 2].mAddress == mIns[ei].mAddress) + { + int i = 0; + while (i < si && !mIns[i].ChangesZeroPage(mIns[si].mAddress)) + i++; + + if (i == si) + { + i = ei + 1; + while (i < mIns.Size() && !mIns[i].ChangesZeroPage(mIns[si].mAddress)) + i++; + + if (i == mIns.Size()) + { + if (!prevBlock) + return OptimizeSimpleLoopInvariant(proc, full); + + i = 0; + while (i < si) + { + mIns[i].mLive |= LIVE_CPU_REG_A; + i++; + } + + i = ei; + while (i < mIns.Size()) + { + mIns[i].mLive |= LIVE_CPU_REG_A; + i++; + } + + mIns[si + 2].CopyMode(mIns[si + 0]); + + if (mIns[si + 2].RequiresYReg()) + mIns[si + 1].mLive |= LIVE_CPU_REG_Y; + if (mIns[si + 2].RequiresXReg()) + mIns[si + 1].mLive |= LIVE_CPU_REG_X; + + mIns[si + 0].CopyMode(mIns[ei]); + prevBlock->mIns.Push(mIns[si]); + mIns.Remove(si); + + mEntryRequiredRegs += CPU_REG_A; + mExitRequiredRegs += CPU_REG_A; + prevBlock->mExitRequiredRegs += CPU_REG_A; + + prevBlock->CheckLive(); + CheckLive(); + + return true; + } + } + } + + + if (si < ei && mIns[ei].mType == ASMIT_STA && mIns[ei].mMode == ASMIM_ZERO_PAGE) + { + int j = 0; + while (j < mIns.Size() && (j == ei || !(mIns[j].ChangesZeroPage(mIns[ei].mAddress) || mIns[j].UsesZeroPage(mIns[ei].mAddress)))) + j++; + if (j == mIns.Size()) + { + if (!prevBlock) + return OptimizeSimpleLoopInvariant(proc, full); + exitBlock->mIns.Insert(0, mIns[ei]); + mIns.Remove(ei); + + CheckLive(); + + return true; + } + } + + + + if (sz >= 2 && mIns[0].mType == ASMIT_LDY && mIns[0].mMode == ASMIM_ZERO_PAGE) + { + int i = mIns.Size() - 1; + while (i > 0 && !mIns[i].ChangesYReg() && !mIns[i].ChangesZeroPage(mIns[0].mAddress)) + i--; + + if (i > 0 && + (mIns[i].mType == ASMIT_LDY && mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i].mAddress == mIns[0].mAddress) || + (mIns[i].mType == ASMIT_TAY && (mIns[i - 1].mType == ASMIT_LDA || mIns[i - 1].mType == ASMIT_STA) && mIns[i - 1].mMode == ASMIM_ZERO_PAGE && mIns[i - 1].mAddress == mIns[0].mAddress)) + { + if (!prevBlock) + return OptimizeSimpleLoopInvariant(proc, full); + while (i < mIns.Size()) + { + mIns[i].mLive |= LIVE_CPU_REG_Y; + i++; + } + + prevBlock->mIns.Push(mIns[0]); + mIns.Remove(0); + + mEntryRequiredRegs += CPU_REG_Y; + mExitRequiredRegs += CPU_REG_Y; + + prevBlock->CheckLive(); + CheckLive(); + + return true; + } + } + + if (sz >= 2 && + mIns[0].mType == ASMIT_LDX && mIns[0].mMode == ASMIM_ABSOLUTE && + mIns[1].mType == ASMIT_INC && mIns[0].SameEffectiveAddress(mIns[1])) + { + int i = 2; + while (i < mIns.Size() && !mIns[i].ChangesXReg() && !mIns[0].MayBeChangedOnAddress(mIns[i])) + i++; + if (i == mIns.Size()) + { + if (!prevBlock) + return OptimizeSimpleLoopInvariant(proc, full); + for (int i = 0; i < mIns.Size(); i++) + mIns[i].mLive |= LIVE_CPU_REG; + mIns[1].mType = ASMIT_INX; mIns[1].mMode = ASMIM_IMPLIED; + prevBlock->mIns.Push(mIns[0]); + exitBlock->mIns.Insert(0, NativeCodeInstruction(mIns[0].mIns, ASMIT_STX, mIns[0])); + mIns.Remove(0); + prevBlock->mExitRequiredRegs += CPU_REG_X; + mEntryRequiredRegs += CPU_REG_X; + mExitRequiredRegs += CPU_REG_X; + exitBlock->mEntryRequiredRegs += CPU_REG_X; + changed = true; + } + } + + if (sz >= 2 && mIns[0].mType == ASMIT_LDX && mIns[0].mMode == ASMIM_ZERO_PAGE) + { + int i = mIns.Size() - 1; + while (i > 0 && !mIns[i].ChangesXReg() && !mIns[i].ChangesZeroPage(mIns[0].mAddress)) + i--; + + if (i > 0 && + (mIns[i].mType == ASMIT_LDX && mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i].mAddress == mIns[0].mAddress) || + (mIns[i].mType == ASMIT_TAX && (mIns[i - 1].mType == ASMIT_LDA || mIns[i - 1].mType == ASMIT_STA) && mIns[i - 1].mMode == ASMIM_ZERO_PAGE && mIns[i - 1].mAddress == mIns[0].mAddress)) + { + if (!prevBlock) + return OptimizeSimpleLoopInvariant(proc, full); + while (i < mIns.Size()) + { + mIns[i].mLive |= LIVE_CPU_REG_X; + i++; + } + + prevBlock->mIns.Push(mIns[0]); + mIns.Remove(0); + + mEntryRequiredRegs += CPU_REG_X; + mExitRequiredRegs += CPU_REG_X; + prevBlock->mExitRequiredRegs += CPU_REG_X; + + prevBlock->CheckLive(); + CheckLive(); + + return true; + } + } + + if (sz >= 2 && + mIns[0].mType == ASMIT_LDA && mIns[0].mMode == ASMIM_ZERO_PAGE && + mIns[1].mType == ASMIT_STA && mIns[1].mMode == ASMIM_ZERO_PAGE && !(mIns[1].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z))) + { + int i = mIns.Size() - 1; + while (i > 1 && !mIns[i].ChangesZeroPage(mIns[1].mAddress) && !mIns[i].ReferencesZeroPage(mIns[0].mAddress)) + i--; + + if (i > 1) + { + i--; + + if (mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && mIns[i + 0].mAddress == mIns[1].mAddress && + mIns[i + 1].mType == ASMIT_STA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && mIns[i + 1].mAddress == mIns[0].mAddress && !(mIns[1].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z))) + { + if ((!mEntryRequiredRegs[CPU_REG_A] || !mEntryRequiredRegs[CPU_REG_X]) && (!mExitRequiredRegs[CPU_REG_A] || !mExitRequiredRegs[CPU_REG_X]) && !mExitRequiredRegs[CPU_REG_Z]) + { + if (!prevBlock) + return OptimizeSimpleLoopInvariant(proc, full); + + bool live = mIns[0].mLive & LIVE_MEM; + + prevBlock->mIns.Push(mIns[0]); + prevBlock->mIns.Push(mIns[1]); + + if (mEntryRequiredRegs[CPU_REG_A]) + { + prevBlock->mIns[prevBlock->mIns.Size() - 2].mType = ASMIT_LDX; + prevBlock->mIns[prevBlock->mIns.Size() - 2].mLive |= LIVE_CPU_REG_X; + prevBlock->mIns[prevBlock->mIns.Size() - 1].mType = ASMIT_STX; + } + + if (!live) + { + exitBlock->mIns.Insert(0, mIns[i + 0]); + exitBlock->mIns.Insert(1, mIns[i + 1]); + + if (mExitRequiredRegs[CPU_REG_A]) + { + exitBlock->mIns[0].mType = ASMIT_LDX; + exitBlock->mIns[0].mLive |= LIVE_CPU_REG_X; + exitBlock->mIns[1].mType = ASMIT_STX; + } + mIns.Remove(i + 1); + } + + mIns.Remove(0); mIns.Remove(0); + + prevBlock->CheckLive(); + CheckLive(); + + return true; + } + } + } + } + + + int ai = 0; + while (ai < mIns.Size() && !mIns[ai].ChangesAccu()) + ai++; + + if (ai < mIns.Size() && !(mIns[ai].mLive & LIVE_CPU_REG_Z) && !mEntryRequiredRegs[CPU_REG_A]) + { + if (mIns[ai].mType == ASMIT_LDA && mIns[ai].mMode == ASMIM_IMMEDIATE) + { + int i = ai + 1; + while (i < mIns.Size() && !mIns[i].ChangesAccu()) + i++; + if (i == mIns.Size()) + { + if (!prevBlock) + return OptimizeSimpleLoopInvariant(proc, full); + + mEntryRequiredRegs += CPU_REG_A; + for(int i=0; imExitRequiredRegs += CPU_REG_A; + + if (mEntryRequiredRegs[CPU_REG_X]) + mIns[ai].mLive |= LIVE_CPU_REG_X; + if (mEntryRequiredRegs[CPU_REG_Y]) + mIns[ai].mLive |= LIVE_CPU_REG_Y; + prevBlock->mIns.Push(mIns[ai]); + mIns.Remove(ai); + + changed = true; + } + } + else if (mIns[ai].mType == ASMIT_LDA && mIns[ai].mMode == ASMIM_ZERO_PAGE) + { + int i = 0; + while (i < mIns.Size() && (i <= ai || !mIns[i].ChangesAccu()) && !mIns[i].ChangesZeroPage(mIns[ai].mAddress)) + i++; + if (i == mIns.Size()) + { + if (!prevBlock) + return OptimizeSimpleLoopInvariant(proc, full); + + prevBlock->mIns.Push(mIns[ai]); + mIns.Remove(ai); + + mEntryRequiredRegs += CPU_REG_A; + for (int i = 0; i < mIns.Size(); i++) + mIns[i].mLive |= LIVE_CPU_REG_A; + mExitRequiredRegs += CPU_REG_A; + prevBlock->mExitRequiredRegs += CPU_REG_A; + + changed = true; + } + + i = mIns.Size() - 1; + while (i >= 0 && !mIns[i].ChangesAccu() && mIns[i].mType != ASMIT_STA) + i--; + if (i >= 0 && mIns[i].mType == ASMIT_STA && mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i].mAddress == mIns[ai].mAddress) + { + if (!prevBlock) + return OptimizeSimpleLoopInvariant(proc, full); + + prevBlock->mIns.Push(mIns[ai]); + mIns.Remove(ai); + + mEntryRequiredRegs += CPU_REG_A; + for (int i = 0; i < mIns.Size(); i++) + mIns[i].mLive |= LIVE_CPU_REG_A; + mExitRequiredRegs += CPU_REG_A; + prevBlock->mExitRequiredRegs += CPU_REG_A; + + changed = true; + } + } + } + + ai = 0; + while (ai < mIns.Size() && !mIns[ai].ChangesYReg()) + ai++; + + if (ai < mIns.Size()) + { + if (mIns[ai].mType == ASMIT_LDY && mIns[ai].mMode == ASMIM_IMMEDIATE) + { + int i = ai + 1; + while (i < mIns.Size() && !mIns[i].ChangesYReg()) + i++; + if (i == mIns.Size()) + { + if (!prevBlock) + return OptimizeSimpleLoopInvariant(proc, full); + + for (int i = 0; i < mIns.Size(); i++) + mIns[i].mLive |= LIVE_CPU_REG_Y; + + if (mEntryRequiredRegs[CPU_REG_X]) + mIns[ai].mLive |= LIVE_CPU_REG_X; + if (mEntryRequiredRegs[CPU_REG_C]) + mIns[ai].mLive |= LIVE_CPU_REG_C; + + prevBlock->mIns.Push(mIns[ai]); + mIns.Remove(ai); + + mEntryRequiredRegs += CPU_REG_Y; + prevBlock->mExitRequiredRegs += CPU_REG_Y; + + changed = true; + } + } + else if (mIns[ai].mType == ASMIT_LDY && mIns[ai].mMode == ASMIM_ZERO_PAGE && !(mIns[ai].mLive & LIVE_CPU_REG_Z)) + { + int i = 0; + while (i < mIns.Size() && (i == ai || !mIns[i].ChangesYReg())) + i++; + if (i == mIns.Size()) + { + int addr = mIns[ai].mAddress; + bool fail = false, changey = false, changev = false; + + for (int i = 0; i < mIns.Size(); i++) + { + if (mIns[i].mType == ASMIT_LDY) + changey = false; + else if (mIns[i].ReferencesYReg() && changey) + { + fail = true; + break; + } + else if (mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i].mAddress == addr) + { + if (mIns[i].mType == ASMIT_STA || mIns[i].mType == ASMIT_INC || mIns[i].mType == ASMIT_DEC) + { + changey = true; + changev = true; + } + else if (mIns[i].mType != ASMIT_LDA) + { + fail = true; + break; + } + } + } + + if (!fail) + { + if (!prevBlock) + return OptimizeSimpleLoopInvariant(proc, full); + + changed = true; + + prevBlock->mExitRequiredRegs += CPU_REG_Y; + mEntryRequiredRegs += CPU_REG_Y; + mExitRequiredRegs += CPU_REG_Y; + exitBlock->mEntryRequiredRegs += CPU_REG_Y; + + prevBlock->mIns.Push(NativeCodeInstruction(mIns[ai].mIns, ASMIT_LDY, ASMIM_ZERO_PAGE, addr)); + if (changev) + exitBlock->mIns.Push(NativeCodeInstruction(mIns[ai].mIns, ASMIT_STY, ASMIM_ZERO_PAGE, addr)); + for (int i = 0; i < mIns.Size(); i++) + { + mIns[i].mLive |= LIVE_CPU_REG_Y; + + if (mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i].mAddress == addr) + { + if (mIns[i].mType == ASMIT_LDA) + { + mIns[i].mType = ASMIT_TYA; mIns[i].mMode = ASMIM_IMPLIED; + } + else if (mIns[i].mType == ASMIT_STA) + { + mIns[i].mType = ASMIT_TAY; mIns[i].mMode = ASMIM_IMPLIED; + } + else if (mIns[i].mType == ASMIT_LDY) + { + mIns[i].mType = ASMIT_NOP; mIns[i].mMode = ASMIM_IMPLIED; + } + else if (mIns[i].mType == ASMIT_INC) + { + mIns[i].mType = ASMIT_INY; mIns[i].mMode = ASMIM_IMPLIED; + } + else if (mIns[i].mType == ASMIT_DEC) + { + mIns[i].mType = ASMIT_DEY; mIns[i].mMode = ASMIM_IMPLIED; + } + } + } + } + } + } + } + else + { + int i = 0; + while (i < mIns.Size() && !mIns[i].ReferencesYReg()) + i++; + if (i < mIns.Size() && mIns[i].mMode == ASMIM_INDIRECT_Y && IsPointerIncAddrs(this, mIns[i].mAddress)) + { + NativeCodeBasicBlock* oblock = mTrueJump == this ? mFalseJump : mTrueJump; + + int reg = mIns[i].mAddress; + + if (!oblock->mEntryRequiredRegs[CPU_REG_Y] && + !oblock->mEntryRequiredRegs[reg] && + !oblock->mEntryRequiredRegs[reg + 1]) + { + for (int i = 0; i < mIns.Size(); i++) + { + if (mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i].mAddress == reg) + { + if (mIns[i].mType == ASMIT_LDA) + { + mIns[i].mType = ASMIT_TYA; + mIns[i].mMode = ASMIM_IMPLIED; + } + else if (mIns[i].mType == ASMIT_STA) + { + mIns[i].mType = ASMIT_TAY; + mIns[i].mMode = ASMIM_IMPLIED; + } + } + } + changed = true; + } + } + else if (pblock && mIns.Last().mType == ASMIT_DEX && !ChangesXReg(0, mIns.Size() - 1)) + { + NativeCodeBasicBlock* oblock = mTrueJump == this ? mFalseJump : mTrueJump; + int ystart = pblock->RetrieveYValue(); + if (ystart == 0 && !oblock->mEntryRequiredRegs[CPU_REG_Y]) + { + changed = OptimizeSimpleYIndexedLoop(pblock, oblock); + } + } + } + + sz = mIns.Size(); + ai = 0; + while (ai < sz && !mIns[ai].ReferencesYReg()) + ai++; + + if (ai < sz && mIns[ai].mType == ASMIT_LDY && mIns[ai].mMode == ASMIM_ZERO_PAGE && !ChangesZeroPage(mIns[ai].mAddress, 0, ai) && + sz >= 2 && + mIns[sz - 1].mType == ASMIT_CMP && HasAsmInstructionMode(ASMIT_CPY, mIns[sz - 1].mMode) && !(mIns[sz - 1].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Y)) && + mIns[sz - 2].mType == ASMIT_LDA && mIns[sz - 2].mMode == ASMIM_ZERO_PAGE && mIns[sz - 2].mAddress == mIns[ai].mAddress) + { + if (!prevBlock) + return OptimizeSimpleLoopInvariant(proc, full); + + prevBlock->mIns.Push(mIns[ai]); + mIns[ai].mType = ASMIT_NOP; mIns[ai].mMode = ASMIM_IMPLIED; + mIns[sz - 2].mType = ASMIT_LDY; mIns[sz - 2].mLive |= LIVE_CPU_REG_Y; + mIns[sz - 1].mType = ASMIT_CPY; mIns[sz - 1].mLive |= LIVE_CPU_REG_Y; + + for (int i = 0; i < ai; i++) + mIns[i].mLive |= LIVE_CPU_REG_Y; + mExitRequiredRegs += CPU_REG_Y; + mEntryRequiredRegs += CPU_REG_Y; + prevBlock->mExitRequiredRegs += CPU_REG_Y; + + changed = true; + } + +#if 1 + if (mEntryRequiredRegs.Size() && (!mEntryRequiredRegs[CPU_REG_A] || !mEntryRequiredRegs[CPU_REG_X]) && (!mExitRequiredRegs[CPU_REG_A] || !mExitRequiredRegs[CPU_REG_X]) && !mExitRequiredRegs[CPU_REG_Z]) + { + for (int i = 0; i + 1 < mIns.Size(); i++) + { + if (mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_STA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE) + { + int j = 0; + while (j < mIns.Size() && + (j == i + 0 || !mIns[j].ChangesZeroPage(mIns[i + 0].mAddress)) && + (j == i + 1 || !mIns[j].ChangesZeroPage(mIns[i + 1].mAddress))) + j++; + if (j == mIns.Size()) + { + if (!prevBlock) + return OptimizeSimpleLoopInvariant(proc, full); + if (!mEntryRequiredRegs[CPU_REG_A]) + { + assert(mIns[0].mType != ASMIT_STA); + prevBlock->mIns.Push(NativeCodeInstruction(mIns[i + 0].mIns, ASMIT_LDA, ASMIM_ZERO_PAGE, mIns[i + 0].mAddress)); + prevBlock->mIns.Push(NativeCodeInstruction(mIns[i + 1].mIns, ASMIT_STA, ASMIM_ZERO_PAGE, mIns[i + 1].mAddress)); + } + else + { + prevBlock->mIns.Push(NativeCodeInstruction(mIns[i + 0].mIns, ASMIT_LDX, ASMIM_ZERO_PAGE, mIns[i + 0].mAddress)); + prevBlock->mIns.Push(NativeCodeInstruction(mIns[i + 1].mIns, ASMIT_STX, ASMIM_ZERO_PAGE, mIns[i + 1].mAddress)); + } + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + } + else if (j > i && !(mIns[i + 0].mLive & LIVE_MEM) && !(mIns[i + 1].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z)) && !mExitRequiredRegs[mIns[i + 1].mAddress] && !ReferencesZeroPage(mIns[i + 0].mAddress, 0, i)) + { + int j = mIns.Size() - 1; + + while (j >= i + 2 && !mIns[j].ReferencesZeroPage(mIns[i + 0].mAddress) && !mIns[j].ReferencesZeroPage(mIns[i + 1].mAddress)) + j--; + + if (j >= i + 2 && mIns[j].mType == ASMIT_STA && mIns[j].mMode == ASMIM_ZERO_PAGE && mIns[j].mAddress == mIns[i + 0].mAddress) + { + if (!prevBlock) + return OptimizeSimpleLoopInvariant(proc, full); + + mIns[j].mAddress = mIns[i + 1].mAddress; + + if (!mEntryRequiredRegs[CPU_REG_A]) + { + assert(mIns[0].mType != ASMIT_STA); + prevBlock->mIns.Push(NativeCodeInstruction(mIns[i + 0].mIns, ASMIT_LDA, ASMIM_ZERO_PAGE, mIns[i + 0].mAddress)); + prevBlock->mIns.Push(NativeCodeInstruction(mIns[i + 1].mIns, ASMIT_STA, ASMIM_ZERO_PAGE, mIns[i + 1].mAddress)); + } + else + { + prevBlock->mIns.Push(NativeCodeInstruction(mIns[i + 0].mIns, ASMIT_LDX, ASMIM_ZERO_PAGE, mIns[i + 0].mAddress)); + prevBlock->mIns.Push(NativeCodeInstruction(mIns[i + 1].mIns, ASMIT_STX, ASMIM_ZERO_PAGE, mIns[i + 1].mAddress)); + } + + prevBlock->mExitRequiredRegs += mIns[i + 1].mAddress; + mEntryRequiredRegs += mIns[i + 1].mAddress; + mExitRequiredRegs += mIns[i + 1].mAddress; + if (!mExitRequiredRegs[CPU_REG_A]) + { + exitBlock->mIns.Insert(0, NativeCodeInstruction(mIns[i + 0].mIns, ASMIT_LDA, ASMIM_ZERO_PAGE, mIns[i + 1].mAddress)); + exitBlock->mIns.Insert(1, NativeCodeInstruction(mIns[i + 1].mIns, ASMIT_STA, ASMIM_ZERO_PAGE, mIns[i + 0].mAddress)); + } + else + { + exitBlock->mIns.Insert(0, NativeCodeInstruction(mIns[i + 0].mIns, ASMIT_LDX, ASMIM_ZERO_PAGE, mIns[i + 1].mAddress)); + exitBlock->mIns.Insert(1, NativeCodeInstruction(mIns[i + 1].mIns, ASMIT_STX, ASMIM_ZERO_PAGE, mIns[i + 0].mAddress)); + } + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + changed = true; + } + } + } + else if (mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_IMMEDIATE && + mIns[i + 1].mType == ASMIT_STA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE) + { + int j = 0; + while (j < mIns.Size() && + (j == i + 1 || !mIns[j].ChangesZeroPage(mIns[i + 1].mAddress))) + j++; + if (j == mIns.Size()) + { + if (!prevBlock) + return OptimizeSimpleLoopInvariant(proc, full); + + int k = prevBlock->mIns.Size(); + while (k > 0 && (prevBlock->mIns[k - 1].mLive & LIVE_CPU_REG_A)) + k--; + + if (!mEntryRequiredRegs[CPU_REG_A]) + { + assert(mIns[0].mType != ASMIT_STA); + prevBlock->mIns.Insert(k, NativeCodeInstruction(mIns[i + 0].mIns, ASMIT_LDA, ASMIM_IMMEDIATE, mIns[i + 0].mAddress)); + prevBlock->mIns.Insert(k + 1, NativeCodeInstruction(mIns[i + 1].mIns, ASMIT_STA, ASMIM_ZERO_PAGE, mIns[i + 1].mAddress)); + } + else + { + prevBlock->mIns.Insert(k, NativeCodeInstruction(mIns[i + 0].mIns, ASMIT_LDX, ASMIM_IMMEDIATE, mIns[i + 0].mAddress)); + prevBlock->mIns.Insert(k + 1, NativeCodeInstruction(mIns[i + 1].mIns, ASMIT_STX, ASMIM_ZERO_PAGE, mIns[i + 1].mAddress)); + } + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + } + } + } + } +#endif + +#if 1 + if (mEntryRequiredRegs.Size() && (!mEntryRequiredRegs[CPU_REG_A] || !mEntryRequiredRegs[CPU_REG_X]) && (!mExitRequiredRegs[CPU_REG_A] || !mExitRequiredRegs[CPU_REG_X])) + { + for (int i = 0; i + 1 < mIns.Size(); i++) + { + if (mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_STA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + !ChangesZeroPage(mIns[i + 1].mAddress, 0, i) && !ChangesZeroPage(mIns[i + 0].mAddress, 0, i)) + { + int j = mIns.Size() - 1; + while (j >= 0 && !mIns[j].ReferencesZeroPage(mIns[i + 1].mAddress)) + j--; + if (j >= 0 && j + 1 < mIns.Size() && + mIns[j + 0].mType == ASMIT_LDA && mIns[j + 0].mMode == ASMIM_ZERO_PAGE && mIns[j + 0].mAddress == mIns[i + 1].mAddress && + mIns[j + 1].mType == ASMIT_STA && mIns[j + 1].mMode == ASMIM_ZERO_PAGE && mIns[j + 1].mAddress == mIns[i + 0].mAddress) + { + if (!prevBlock) + return OptimizeSimpleLoopInvariant(proc, full); + + if (!mEntryRequiredRegs[CPU_REG_A]) + { + assert(mIns[0].mType != ASMIT_STA); + prevBlock->mIns.Push(NativeCodeInstruction(mIns[i + 0].mIns, ASMIT_LDA, ASMIM_ZERO_PAGE, mIns[i + 0].mAddress)); + prevBlock->mIns.Push(NativeCodeInstruction(mIns[i + 1].mIns, ASMIT_STA, ASMIM_ZERO_PAGE, mIns[i + 1].mAddress)); + } + else + { + prevBlock->mIns.Push(NativeCodeInstruction(mIns[i + 0].mIns, ASMIT_LDX, ASMIM_ZERO_PAGE, mIns[i + 0].mAddress)); + prevBlock->mIns.Push(NativeCodeInstruction(mIns[i + 1].mIns, ASMIT_STX, ASMIM_ZERO_PAGE, mIns[i + 1].mAddress)); + } + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + changed = true; + } + } + } + } +#endif + +#if 1 + for (int i = 0; i < mIns.Size(); i++) + { + if (mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && + mIns[i + 4].mType == ASMIT_LDA && mIns[i + 4].mMode == ASMIM_ZERO_PAGE && mIns[i + 4].mAddress == mIns[i + 1].mAddress + 1 && + mIns[i + 5].mType == ASMIT_ADC && mIns[i + 5].mMode == ASMIM_IMMEDIATE && mIns[i + 5].mAddress == 0 && + mIns[i + 6].mType == ASMIT_STA && mIns[i + 6].mMode == ASMIM_ZERO_PAGE && mIns[i + 6].mAddress == mIns[i + 3].mAddress + 1) + { + int treg = mIns[i + 3].mAddress; + int sreg = mIns[i + 1].mAddress; + int areg = mIns[i + 2].mAddress; + + if (sreg != treg && areg != treg && + !mExitRequiredRegs[treg] && !mExitRequiredRegs[treg + 1] && + !ReferencesZeroPage(treg, 0, i) && !ReferencesZeroPage(treg + 1, 0, i) && + !ChangesZeroPage(treg, i + 7) && !ChangesZeroPage(treg + 1, i + 7) && + !ChangesZeroPage(sreg) && !ChangesZeroPage(sreg + 1)) + { + int j = i + 7; + while (j < mIns.Size() && !mIns[j].ReferencesZeroPage(treg) && !mIns[j].ReferencesZeroPage(treg + 1) && !mIns[j].ChangesZeroPage(areg)) + j++; + + if (j < mIns.Size() && mIns[j].mMode == ASMIM_INDIRECT_Y && mIns[j].mAddress == treg) + { + if (mIns[j - 1].mType == ASMIT_LDY && mIns[j - 1].mMode == ASMIM_IMMEDIATE && !(mIns[j].mLive & LIVE_CPU_REG_Y) && !ReferencesZeroPage(treg, j + 1) && !ReferencesZeroPage(treg + 1, j + 1)) + { + if (!prevBlock) + return OptimizeSimpleLoopInvariant(proc, full); + + prevBlock->mIns.Push(NativeCodeInstruction(mIns[j - 1].mIns, ASMIT_CLC)); + prevBlock->mIns.Push(NativeCodeInstruction(mIns[j - 1].mIns, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg)); + prevBlock->mIns.Push(NativeCodeInstruction(mIns[j - 1].mIns, ASMIT_ADC, ASMIM_IMMEDIATE, mIns[j - 1].mAddress)); + prevBlock->mIns.Push(NativeCodeInstruction(mIns[j - 1].mIns, ASMIT_STA, ASMIM_ZERO_PAGE, treg)); + prevBlock->mIns.Push(NativeCodeInstruction(mIns[j - 1].mIns, ASMIT_LDA, ASMIM_ZERO_PAGE, sreg + 1)); + prevBlock->mIns.Push(NativeCodeInstruction(mIns[j - 1].mIns, ASMIT_ADC, ASMIM_IMMEDIATE, 0)); + prevBlock->mIns.Push(NativeCodeInstruction(mIns[j - 1].mIns, ASMIT_STA, ASMIM_ZERO_PAGE, treg + 1)); + prevBlock->mExitRequiredRegs += treg; + prevBlock->mExitRequiredRegs += treg + 1; + + mIns[j - 1].mMode = ASMIM_ZERO_PAGE; + mIns[j - 1].mAddress = areg; + + mEntryRequiredRegs += treg; + mEntryRequiredRegs += treg + 1; + mExitRequiredRegs += treg; + mExitRequiredRegs += treg + 1; + + mIns[i + 3].mType = ASMIT_NOP; mIns[i + 3].mMode = ASMIM_IMPLIED; + mIns[i + 6].mType = ASMIT_NOP; mIns[i + 6].mMode = ASMIM_IMPLIED; + + changed = true; + } + } + } + } + } +#endif + +#if 1 + sz = mIns.Size(); + if (sz >= 2 && mIns[sz - 2].mType == ASMIT_INX && mIns[sz - 1].mType == ASMIT_CPX && !mEntryRequiredRegs[CPU_REG_Y] && !mExitRequiredRegs[CPU_REG_Y]) + { + int xtoy = -1; + + for (int i = 0; i < sz - 2; i++) + { + if (mIns[i].ChangesXReg()) + { + xtoy = -1; + break; + } + + if (mIns[i].ChangesYReg()) + { + if (xtoy >= 0) + { + xtoy = -1; + break; + } + + if (i >= 3 && + mIns[i - 0].mType == ASMIT_TAY && + mIns[i - 1].mType == ASMIT_ADC && + mIns[i - 2].mType == ASMIT_TXA && + mIns[i - 3].mType == ASMIT_CLC && + !(mIns[i].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C | LIVE_CPU_REG_Z))) + xtoy = i - 3; + else + { + xtoy = -1; + break; + } + } + + + } + + if (xtoy >= 0) + { + int i = 0; + while (i < sz && !mIns[xtoy + 1].MayBeChangedOnAddress(mIns[i])) + i++; + if (i == sz) + { + if (!prevBlock) + return OptimizeSimpleLoopInvariant(proc, full); + + prevBlock->mIns.Push(mIns[xtoy + 0]); + prevBlock->mIns.Push(mIns[xtoy + 1]); + prevBlock->mIns.Push(mIns[xtoy + 2]); + prevBlock->mIns.Push(mIns[xtoy + 3]); + + mIns.Insert(sz - 2, NativeCodeInstruction(mIns[xtoy].mIns, ASMIT_INY)); + + mIns.Remove(xtoy, 4); + for (int i = 0; i < mIns.Size(); i++) + mIns[i].mLive |= LIVE_CPU_REG_Y; + mEntryRequiredRegs += CPU_REG_Y; + mExitRequiredRegs += CPU_REG_Y; + } + } + } +#endif + +#if 1 + sz = mIns.Size(); + if (sz >= 2 && (mBranch == ASMIT_BEQ && mFalseJump == this || mBranch == ASMIT_BNE && mTrueJump == this) && + ((mIns[sz - 2].mType == ASMIT_LDA && mIns[sz - 1].mType == ASMIT_CMP && !mEntryRequiredRegs[CPU_REG_A]) || + (mIns[sz - 2].mType == ASMIT_LDX && mIns[sz - 1].mType == ASMIT_CPX && !mEntryRequiredRegs[CPU_REG_X]) || + (mIns[sz - 2].mType == ASMIT_LDY && mIns[sz - 1].mType == ASMIT_CPY && !mEntryRequiredRegs[CPU_REG_Y])) && + mIns[sz - 2].mMode == ASMIM_ZERO_PAGE && mIns[sz - 1].mMode == ASMIM_ZERO_PAGE) + { + if (ChangesZeroPage(mIns[sz - 1].mAddress) && !ChangesZeroPage(mIns[sz - 2].mAddress)) + { + int a = mIns[sz - 1].mAddress; + mIns[sz - 1].mAddress = mIns[sz - 2].mAddress; + mIns[sz - 2].mAddress = a; + changed = true; + } + } +#endif + + CheckLive(); + + // Special case of 16 bit shift with unused upper byte result + if (mIns.Size() == 3 && mBranch == ASMIT_BNE && mTrueJump == this && !mFalseJump->mEntryRequiredRegs[CPU_REG_A] && + mIns[0].mType == ASMIT_ASL && mIns[0].mMode == ASMIM_ZERO_PAGE && + mIns[1].mType == ASMIT_ROL && mIns[1].mMode == ASMIM_IMPLIED && !(mIns[1].mLive & LIVE_CPU_REG_C) && + mIns[2].mType == ASMIT_DEX) + { + if (!prevBlock) + return OptimizeSimpleLoopInvariant(proc, full); + + prevBlock->mIns.Push(NativeCodeInstruction(mIns[0].mIns, ASMIT_LDA, mIns[0])); + prevBlock->mExitRequiredRegs += CPU_REG_A; + + exitBlock->mEntryRequiredRegs += CPU_REG_A; + exitBlock->mIns.Insert(0, NativeCodeInstruction(mIns[0].mIns, ASMIT_STA, mIns[0])); + + mIns[0].mMode = ASMIM_IMPLIED; + mIns[1].mType = ASMIT_NOP; + + + changed = true; + } + + CheckLive(); + + if (sz >= 2 && (mIns[0].mType == ASMIT_ASL || mIns[0].mType == ASMIT_LSR) && mIns[0].mMode == ASMIM_ZERO_PAGE && + mIns[sz - 1].mType == ASMIT_LDA && mIns[sz - 1].SameEffectiveAddress(mIns[0])) + { + if (!ChangesZeroPage(mIns[0].mAddress, 1, sz - 1) && !ReferencesZeroPage(mIns[0].mAddress, 1, sz - 1) && !ReferencesAccu(0, sz - 1)) + { + if (!prevBlock) + return OptimizeSimpleLoopInvariant(proc, full); + + prevBlock->mIns.Push(NativeCodeInstruction(mIns[0].mIns, ASMIT_LDA, mIns[0])); + prevBlock->mExitRequiredRegs += CPU_REG_A; + + exitBlock->mEntryRequiredRegs += CPU_REG_A; + exitBlock->mIns.Insert(0, NativeCodeInstruction(mIns[0].mIns, ASMIT_STA, mIns[0])); + + mEntryRequiredRegs += CPU_REG_A; + mExitRequiredRegs += CPU_REG_A; + mIns[0].mMode = ASMIM_IMPLIED; + mIns[sz - 1].mType = ASMIT_ORA; mIns[sz - 1].mMode = ASMIM_IMMEDIATE; mIns[sz - 1].mAddress = 0; + + for (int i = 0; i < sz; i++) + mIns[i].mLive |= LIVE_CPU_REG_A; + changed = true; + } + } + + CheckLive(); + + if (sz >= 2 && (mIns[0].mType == ASMIT_DEC || mIns[0].mType == ASMIT_INC) && mIns[0].mMode == ASMIM_ZERO_PAGE && + mIns[1].mType == ASMIT_LDY && mIns[1].SameEffectiveAddress(mIns[0])) + { + int i = 2; + int inc = 0; + while (i < mIns.Size()) + { + if (mIns[i].ChangesYReg()) + { + if (mIns[i].mType == ASMIT_INY) + inc++; + else if (mIns[i].mType == ASMIT_DEY) + inc--; + else if (mIns[i].mType == ASMIT_LDY && mIns[i].SameEffectiveAddress(mIns[0])) + inc = 0; + else + break; + } + else if (mIns[i].ChangesZeroPage(mIns[0].mAddress)) + break; + i++; + } + + if (i == mIns.Size() && inc == 0) + { + if (!prevBlock) + return OptimizeSimpleLoopInvariant(proc, full); + prevBlock->mIns.Push(NativeCodeInstruction(mIns[1].mIns, ASMIT_LDY, mIns[1])); + prevBlock->mExitRequiredRegs += CPU_REG_Y; + mEntryRequiredRegs += CPU_REG_Y; + mExitRequiredRegs += CPU_REG_Y; + mIns[1].mType = ASMIT_STY; + mIns[0].mLive |= LIVE_CPU_REG_Y; + if (mIns[0].mType == ASMIT_DEC) + mIns[0].mType = ASMIT_DEY; + else + mIns[0].mType = ASMIT_INY; + mIns[0].mMode = ASMIM_IMPLIED; + changed = true; + } + } + + CheckLive(); + + if (mIns.Size() > 0 && (mIns.Last().mType == ASMIT_DEX || mIns.Last().mType == ASMIT_DEC || mIns.Last().mType == ASMIT_CPX)) + { + bool yzero = false; + + if (prevBlock) + yzero = prevBlock->RetrieveYValue(prevBlock->mIns.Size() - 1) == 0; + + int nregs = 0; + int iregs[32]; + + for (int i = 0; i + 3 < mIns.Size(); i++) + { + if (mIns[i].mType == ASMIT_LDY && mIns[i].mMode == ASMIM_IMMEDIATE && mIns[i].mAddress == 0) + yzero = true; + else if (mIns[i].ChangesYReg()) + { + nregs = 0; + break; + } + + if (mIns[i].mMode == ASMIM_INDIRECT_Y) + { + if (!yzero) + { + nregs = 0; + break; + } + + int reg = mIns[i].mAddress; + + if (ReferencesZeroPage(mIns[i].mAddress, 0, i) || + ReferencesZeroPage(mIns[i].mAddress + 1, 0, i)) + { + nregs = 0; + break; + } + if (exitBlock->mEntryRequiredRegs[reg] || exitBlock->mEntryRequiredRegs[reg + 1] || exitBlock->mEntryRequiredRegs[CPU_REG_Y]) + { + nregs = 0; + break; + } + + int j = i + 1; + while (j < mIns.Size() && ( + mIns[j].mMode == ASMIM_INDIRECT || + !(mIns[j].ReferencesZeroPage(reg) || mIns[j].ReferencesZeroPage(reg + 1)))) + j++; + + if (j == mIns.Size()) + { + nregs = 0; + break; + } + + int sreg, dreg, offset; + if (Is16BitAddSubImmediate(j - 1, sreg, dreg, offset) && sreg == reg && dreg == sreg && offset == 1) + { + if (ReferencesZeroPage(reg, j + 6) || ReferencesZeroPage(reg + 1, j + 6)) + { + nregs = 0; + break; + } + iregs[nregs++] = sreg; + } + else + { + nregs = 0; + break; + } + } + } + + if (nregs) + { + if (!prevBlock) + return OptimizeSimpleLoopInvariant(proc, full); + + for (int i = 0; i < mIns.Size(); i++) + { + if (mIns[i].mMode == ASMIM_ZERO_PAGE) + { + int j = 0; + while (j < nregs && iregs[j] != mIns[i].mAddress && iregs[j] + 1 != mIns[i].mAddress) + j++; + if (j < nregs) + { + mIns[i].mType = ASMIT_NOP; + mIns[i].mMode = ASMIM_IMPLIED; + } + } + else if (mIns[i].mType == ASMIT_LDY) + { + mIns[i].mType = ASMIT_NOP; + mIns[i].mMode = ASMIM_IMPLIED; + } + + mIns[i].mLive |= LIVE_CPU_REG_Y | LIVE_CPU_REG_A; + } + int j = mIns.Size(); + while (j > 0 && (mIns[j - 1].mLive & LIVE_CPU_REG_Z)) + j--; + mIns.Insert(j, NativeCodeInstruction(mIns[0].mIns, ASMIT_INY)); + prevBlock->mIns.Push(NativeCodeInstruction(mIns[0].mIns, ASMIT_LDY, ASMIM_IMMEDIATE, 0)); + + changed = true; + } + } + + CheckLive(); + + sz = mIns.Size(); + if (sz >= 3 && + mIns[0].mType == ASMIT_STA && mIns[0].mMode == ASMIM_ZERO_PAGE && + mIns[sz - 2].mType == ASMIT_LDA && mIns[sz - 2].mMode == ASMIM_ZERO_PAGE && + !mIns[0].SameEffectiveAddress(mIns[sz - 2]) && mIns[sz - 1].mType == ASMIT_CMP && + !exitBlock->mEntryRequiredRegs[mIns[0].mAddress] && !exitBlock->mEntryRequiredRegs[mIns[sz - 2].mAddress] && + !ChangesZeroPage(mIns[0].mAddress, 1)) + { + int ai = mIns[sz - 2].mAddress; + int aj = mIns[0].mAddress; + + int i = 1; + while (i < sz - 2 && (mIns[i].mMode == ASMIM_ZERO_PAGE || !mIns[i].ReferencesZeroPage(ai))) + i++; + if (i == sz - 2) + { + int i = 0; + while (i < sz - 2 && !mIns[i].ChangesZeroPage(ai)) + i++; + if (!ReferencesZeroPage(aj, i + 1, sz - 2)) + { + int index; + NativeCodeBasicBlock* block = this; + + if (IsExitARegZP(ai, index, block)) + { + if (!prevBlock) + return OptimizeSimpleLoopInvariant(proc, full); + + for (int i = 1; i < sz; i++) + { + if (mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i].mAddress == ai) + mIns[i].mAddress = aj; + } + + prevBlock->mIns.Push(mIns[0]); + mIns.Remove(0); + + mExitRequiredRegs += aj; + mEntryRequiredRegs += aj; + + CheckLive(); + + return true; + } + } + } + + } + + CheckLive(); + + if (sz >= 3 && + mIns[0].mType == ASMIT_TAY && + mIns[sz - 1].mType == ASMIT_CPX && mIns[sz - 2].mType == ASMIT_TXA && mIns[sz - 3].mType == ASMIT_INX && + !mExitRequiredRegs[CPU_REG_Y] && + !ChangesXReg(0, sz - 3) && !ChangesYReg(1, sz - 3)) + { + bool canX = true, canY = false; + + for (int i = 0; i < sz - 3; i++) + { + if (mIns[i].mMode == ASMIM_ABSOLUTE_X && !HasAsmInstructionMode(mIns[i].mType, ASMIM_ABSOLUTE_Y)) + canY = false; + else if (mIns[i].mMode == ASMIM_ABSOLUTE_Y && !HasAsmInstructionMode(mIns[i].mType, ASMIM_ABSOLUTE_X)) + canX = false; + else if (mIns[i].mMode == ASMIM_INDIRECT_Y) + canX = false; + else if (mIns[i].mMode == ASMIM_INDIRECT_X) + canY = false; + } + + if (canX) + { + for (int i = 0; i < sz - 3; i++) + { + mIns[i].mLive |= LIVE_CPU_REG_X; + if (mIns[i].mMode == ASMIM_ABSOLUTE_Y) + mIns[i].mMode = ASMIM_ABSOLUTE_X; + } + return true; + } + } + + CheckLive(); + +#if 1 + // Exchange Y against low byte of pointer + if (!ChangesYReg() && !mEntryRequiredRegs[CPU_REG_A] && !mExitRequiredRegs[CPU_REG_A]) + { + int rind = -1; + for (int i = 0; i < mIns.Size(); i++) + { + if (mIns[i].mMode == ASMIM_INDIRECT_Y) + { + if (rind == -1) + rind = mIns[i].mAddress; + else if (rind != mIns[i].mAddress) + { + rind = -2; + break; + } + } + else if (mIns[i].ReferencesYReg()) + { + rind = -2; + break; + } + } + + if (rind >= 0) + { + bool found = false, ptruse = false; + for (int i = 0; i < mIns.Size(); i++) + { + if (mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i].mAddress == rind) + { + if (i + 2 < mIns.Size() && + mIns[i].mType == ASMIT_LDA && + mIns[i + 1].mType == ASMIT_ADC && mIns[i + 1].mMode == ASMIM_IMMEDIATE && + mIns[i + 2].mType == ASMIT_STA && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && mIns[i + 2].mAddress == rind && !(mIns[i + 2].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z))) + { + found = true; + i += 2; + } + else if (mIns[i].mType == ASMIT_LDA || mIns[i].mType == ASMIT_STA) + { + found = true; + ptruse = true; + } + else + { + rind = -2; + break; + } + } + else if (mIns[i].mMode == ASMIM_INDIRECT_Y) + ; + else if (mIns[i].ReferencesZeroPage(rind)) + { + rind = -2; + break; + } + } + + if (rind >= 0 && found) + { + if (!prevBlock) + return OptimizeSimpleLoopInvariant(proc, full); + + int yentry = pblock->RetrieveYValue(pblock->mIns.Size() - 1); + + // Need to have actual value if needed after loop, otherwise just keep counting from zero + if (ptruse || exitBlock->mEntryRequiredRegs[rind] || exitBlock->mEntryRequiredRegs[rind + 1] || yentry < 0 && exitBlock->mEntryRequiredRegs[CPU_REG_Y]) + { + prevBlock->mIns.Push(NativeCodeInstruction(mBranchIns, ASMIT_LDA, ASMIM_ZERO_PAGE, rind)); + prevBlock->mIns.Push(NativeCodeInstruction(mBranchIns, ASMIT_STY, ASMIM_ZERO_PAGE, rind)); + prevBlock->mIns.Push(NativeCodeInstruction(mBranchIns, ASMIT_TAY)); + + exitBlock->mIns.Push(NativeCodeInstruction(mBranchIns, ASMIT_LDA, ASMIM_ZERO_PAGE, rind)); + exitBlock->mIns.Push(NativeCodeInstruction(mBranchIns, ASMIT_STY, ASMIM_ZERO_PAGE, rind)); + exitBlock->mIns.Push(NativeCodeInstruction(mBranchIns, ASMIT_TAY)); + } + else if (exitBlock->mEntryRequiredRegs[CPU_REG_Y]) + exitBlock->mIns.Push(NativeCodeInstruction(mBranchIns, ASMIT_LDY, ASMIM_IMMEDIATE, yentry)); + + for (int i = 0; i < mIns.Size(); i++) + { + mIns[i].mLive |= LIVE_CPU_REG_Y; + if (mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i].mAddress == rind) + { + if (mIns[i].mType == ASMIT_LDA) + { + mIns[i].mType = ASMIT_TYA; + mIns[i].mMode = ASMIM_IMPLIED; + } + else if (mIns[i].mType == ASMIT_STA) + { + mIns[i].mType = ASMIT_TAY; + mIns[i].mMode = ASMIM_IMPLIED; + } + } + } + + prevBlock->mExitRequiredRegs += CPU_REG_Y; + mEntryRequiredRegs += CPU_REG_Y; + mExitRequiredRegs += CPU_REG_Y; + exitBlock->mEntryRequiredRegs += CPU_REG_Y; + changed = true; + } + } + } +#endif + + if (!changed && !mEntryRequiredRegs[CPU_REG_Y] && !mExitRequiredRegs[CPU_REG_Y] && !mEntryRequiredRegs[CPU_REG_A]) + { + int firsty = 0; + while (firsty + 2 < mIns.Size() && !mIns[firsty].ReferencesYReg()) + firsty++; + if (firsty + 2 < mIns.Size() && mIns[firsty].mType == ASMIT_LDY && mIns[firsty].mMode == ASMIM_IMMEDIATE && mIns[firsty].mAddress == 0 && mIns[firsty + 1].mMode == ASMIM_INDIRECT_Y && !(mIns[firsty + 1].mLive & LIVE_CPU_REG_Y)) + { + int reg = mIns[firsty + 1].mAddress; + int lastz = mIns.Size() - 1; + while (lastz > firsty && !mIns[lastz].ReferencesZeroPage(reg)) + lastz--; + if (lastz > firsty && mIns[lastz].mType == ASMIT_STA && mIns[lastz].mMode == ASMIM_ZERO_PAGE && !ReferencesYReg(lastz)) + { + int i = firsty + 2; + while (i < lastz && !((mIns[i].ReferencesZeroPage(reg) || mIns[i].ChangesYReg()) && (mIns[i].mType != ASMIT_LDY || mIns[i].mMode != ASMIM_ZERO_PAGE || mIns[i].mAddress != reg))) + i++; + + if (i == lastz) + { + if (!prevBlock) + return OptimizeSimpleLoopInvariant(proc, full); + + prevBlock->mIns.Push(NativeCodeInstruction(mBranchIns, ASMIT_LDY, ASMIM_ZERO_PAGE, reg)); + prevBlock->mIns.Push(NativeCodeInstruction(mBranchIns, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + prevBlock->mIns.Push(NativeCodeInstruction(mBranchIns, ASMIT_STA, ASMIM_ZERO_PAGE, reg)); + + mIns[firsty].mType = ASMIT_NOP; mIns[firsty].mMode = ASMIM_IMPLIED; + for (int i = firsty + 2; i < lastz; i++) + { + if (mIns[i].mType == ASMIT_LDY) + { + mIns[i].mType = ASMIT_NOP; mIns[i].mMode = ASMIM_IMPLIED; + } + } + mIns[lastz].mType = ASMIT_TAY; mIns[lastz].mMode = ASMIM_IMPLIED; + for (int i = 0; i < mIns.Size(); i++) + mIns[i].mLive |= LIVE_CPU_REG_Y; + mEntryRequiredRegs += CPU_REG_Y; + mExitRequiredRegs += CPU_REG_Y; + exitBlock->mEntryRequiredRegs += CPU_REG_Y; + exitBlock->mIns.Insert(0, NativeCodeInstruction(mBranchIns, ASMIT_STY, ASMIM_ZERO_PAGE, reg)); + changed = true; + } + } + } + } + + if (!changed && mIns.Size() >= 3) + { + int sz = mIns.Size(); + if (mIns[sz - 3].mType == ASMIT_INC && mIns[sz - 3].mMode == ASMIM_ZERO_PAGE && + mIns[sz - 2].mType == ASMIT_LDX && mIns[sz - 2].mMode == ASMIM_ZERO_PAGE && mIns[sz - 2].mAddress == mIns[sz - 3].mAddress && + mIns[sz - 1].mType == ASMIT_CPX) + { + int reg = mIns[sz - 3].mAddress; + int index; + NativeCodeBasicBlock* xblock = mEntryBlocks[0] == this ? mEntryBlocks[1] : mEntryBlocks[0]; + if (!ReferencesZeroPage(reg, 0, sz - 3) && !ChangesXReg(0, sz - 3) && xblock->IsExitXRegZP(reg, index, xblock)) + { + if (!prevBlock) + return OptimizeSimpleLoopInvariant(proc, full); + + exitBlock->mIns.Insert(0, NativeCodeInstruction(mBranchIns, ASMIT_STX, ASMIM_ZERO_PAGE, reg)); + mIns[sz - 3].mType = ASMIT_NOP; mIns[sz - 3].mMode = ASMIM_IMPLIED; + mIns[sz - 2].mType = ASMIT_INX; mIns[sz - 2].mMode = ASMIM_IMPLIED; + + for (int i = 0; i < mIns.Size(); i++) + mIns[i].mLive |= LIVE_CPU_REG_X; + mEntryRequiredRegs += CPU_REG_X; + mExitRequiredRegs += CPU_REG_X; + exitBlock->mEntryRequiredRegs += CPU_REG_X; + changed = true; + } + } + } + + sz = mIns.Size(); + if (sz >= 5 && mIns[0].mType == ASMIT_TXA && mIns[1].mType == ASMIT_TAY && mIns[sz - 2].mType == ASMIT_INX && mIns[sz - 1].mType == ASMIT_CPX && !exitBlock->mEntryRequiredRegs[CPU_REG_X]) + { + int i = 2; + while (i < sz - 2 && !mIns[i].ChangesXReg() && !mIns[i].ChangesYReg() && + (mIns[i].mMode != ASMIM_ABSOLUTE_X || HasAsmInstructionMode(mIns[i].mType, ASMIM_ABSOLUTE_Y))) + i++; + + if (i == sz - 2) + { + if (!prevBlock) + return OptimizeSimpleLoopInvariant(proc, full); + prevBlock->mIns.Push(NativeCodeInstruction(mBranchIns, ASMIT_TXA)); + prevBlock->mIns.Push(NativeCodeInstruction(mBranchIns, ASMIT_TAY)); + mIns[0].mType = ASMIT_NOP; + mIns[1].mType = ASMIT_TYA; + for (int i = 0; i < sz; i++) + { + mIns[i].ReplaceXRegWithYReg(); + mIns[i].mLive |= LIVE_CPU_REG_Y; + } + mEntryRequiredRegs += CPU_REG_Y; + mExitRequiredRegs += CPU_REG_Y; + changed = true; + } + } + + sz = mIns.Size(); + if (pblock && sz >= 2 && mIns[sz - 2].mType == ASMIT_INY && mIns[sz - 1].mType == ASMIT_DEX && + mBranch == ASMIT_BNE && mTrueJump == this && !mFalseJump->mEntryRequiredRegs[CPU_REG_X] && + !ReferencesXReg(0, mIns.Size() - 1) && !ChangesYReg(0, mIns.Size() - 2)) + { + int xval = pblock->RetrieveXValue(), yval = pblock->RetrieveYValue(); + if (xval > 0 && yval >= 0) + { + mIns[sz - 1].mType = ASMIT_CPY; + mIns[sz - 1].mMode = ASMIM_IMMEDIATE; + mIns[sz - 1].mAddress = yval + xval; + changed = true; + } + } + + // Optimize for single inc/dec zero page variables + + for (int i = 0; i < mIns.Size(); i++) + { + if ((mIns[i].mType == ASMIT_INC || mIns[i].mType == ASMIT_DEC) && mIns[i].mMode == ASMIM_ZERO_PAGE && !(mIns[i].mLive & LIVE_CPU_REG_Z)) + { + int reg = mIns[i].mAddress; + if (!ChangesZeroPage(reg, 0, i) && !ReferencesZeroPage(reg, i + 1) && !exitBlock->mEntryRequiredRegs[reg]) + { + int k = 0; + while (k < i && !mIns[k].ReferencesZeroPage(reg)) + k++; + if (k > 0 && k < i && !ReferencesZeroPage(reg, k + 1, i)) + { + if (mIns[k - 1].mType == ASMIT_CLC && mIns[k].mType == ASMIT_LDA && mIns[k + 1].mType == ASMIT_ADC && !(mIns[k + 1].mLive & LIVE_CPU_REG_C)) + { + if (mIns[k + 1].mMode == ASMIM_IMMEDIATE || + mIns[k + 1].mMode == ASMIM_ZERO_PAGE && !ChangesZeroPage(mIns[k + 1].mAddress)) + { + if (!prevBlock) + return OptimizeSimpleLoopInvariant(proc, full); + prevBlock->mIns.Push(NativeCodeInstruction(mIns[k].mIns, ASMIT_CLC)); + prevBlock->mIns.Push(NativeCodeInstruction(mIns[k].mIns, ASMIT_LDA, ASMIM_ZERO_PAGE, reg)); + prevBlock->mIns.Push(NativeCodeInstruction(mIns[k].mIns, ASMIT_ADC, mIns[k + 1])); + prevBlock->mIns.Push(NativeCodeInstruction(mIns[k].mIns, ASMIT_STA, ASMIM_ZERO_PAGE, reg)); + mIns[k + 1].mType = ASMIT_NOP; + mIns[k + 1].mMode = ASMIM_IMPLIED; + changed = true; + } + } + else if (k > 1 && mIns[k - 2].mType == ASMIT_CLC && mIns[k - 1].mType == ASMIT_LDA && mIns[k].mType == ASMIT_ADC && !(mIns[k].mLive & LIVE_CPU_REG_C)) + { + if (mIns[k - 1].mMode == ASMIM_IMMEDIATE || + mIns[k - 1].mMode == ASMIM_ZERO_PAGE && !ChangesZeroPage(mIns[k - 1].mAddress)) + { + if (!prevBlock) + return OptimizeSimpleLoopInvariant(proc, full); + prevBlock->mIns.Push(NativeCodeInstruction(mIns[k].mIns, ASMIT_CLC)); + prevBlock->mIns.Push(NativeCodeInstruction(mIns[k].mIns, ASMIT_LDA, ASMIM_ZERO_PAGE, reg)); + prevBlock->mIns.Push(NativeCodeInstruction(mIns[k].mIns, ASMIT_ADC, mIns[k - 1])); + prevBlock->mIns.Push(NativeCodeInstruction(mIns[k].mIns, ASMIT_STA, ASMIM_ZERO_PAGE, reg)); + mIns[k - 1].mType = ASMIT_NOP; + mIns[k - 1].mMode = ASMIM_IMPLIED; + mIns[k].mType = ASMIT_LDA; + changed = true; + } + } + } + } + } + } + + ai = mIns.Size() - 1; + while (ai >= 0 && !(mIns[ai].ChangesAccu() || mIns[ai].mType == ASMIT_STA && mIns[ai].mMode == ASMIM_ZERO_PAGE)) + ai--; + if (ai >= 0 && mIns[ai].mType == ASMIT_STA && !ChangesZeroPage(mIns[ai].mAddress, ai + 1)) + { + int reg = mIns[ai].mAddress; + int zi = 0; + while (zi < mIns.Size() && !(mIns[zi].ChangesAccu() || mIns[zi].ReferencesZeroPage(reg))) + zi++; + if (zi < mIns.Size()) + { + if (mIns[zi].IsShift() && mIns[zi].mMode == ASMIM_ZERO_PAGE && !(mIns[zi].mLive & (LIVE_CPU_REG_A | LIVE_MEM))) + { + if (!prevBlock) + return OptimizeSimpleLoopInvariant(proc, full); + + prevBlock->mIns.Push(NativeCodeInstruction(mIns[zi].mIns, ASMIT_LDA, ASMIM_ZERO_PAGE, reg)); + mIns[zi].mMode = ASMIM_IMPLIED; + for (int i = 0; i < zi; i++) + mIns[i].mLive |= LIVE_CPU_REG_A; + for (int i = ai; i < mIns.Size(); i++) + mIns[i].mLive |= LIVE_CPU_REG_A; + mExitRequiredRegs += CPU_REG_A; + mEntryRequiredRegs += CPU_REG_A; + prevBlock->mExitRequiredRegs += CPU_REG_A; + changed = true; + } + } + } + + sz = mIns.Size(); + if (sz >= 4) + { + if (mIns[sz - 1].mType == ASMIT_CMP && + mIns[sz - 2].mType == ASMIT_LDA && (mIns[sz - 2].mMode == ASMIM_ZERO_PAGE || mIns[sz - 2].mMode == ASMIM_ABSOLUTE) && + (mIns[sz - 3].mType == ASMIT_INC || mIns[sz - 3].mType == ASMIT_DEC) && mIns[sz - 3].SameEffectiveAddress(mIns[sz - 2]) && + !(mIns[sz - 1].mLive & LIVE_CPU_REG_A)) + { + if (HasAsmInstructionMode(ASMIT_CPX, mIns[sz - 1].mMode) && !(mIns[sz - 1].mLive & LIVE_CPU_REG_X)) + { + int k = sz - 4; + while (k >= 0 && !mIns[k].ChangesXReg()) + k--; + + if (k >= 0 && mIns[k].mType == ASMIT_LDX && mIns[k].SameEffectiveAddress(mIns[sz - 3])) + { + mIns[sz - 1].mType = ASMIT_CPX; + mIns[sz - 2].mType = ASMIT_STX; + mIns[sz - 3].mType = mIns[sz - 3].mType == ASMIT_DEC ? ASMIT_DEX : ASMIT_INX; + mIns[sz - 3].mMode = ASMIM_IMPLIED; + while (k < sz - 1) + { + mIns[k].mLive |= LIVE_CPU_REG_X; + k++; + } + changed = true; + } + } + } + } + + sz = mIns.Size(); + if (sz >= 5) + { + if (mIns[0].mType == ASMIT_STA && mIns[0].mMode == ASMIM_ZERO_PAGE && + mIns[sz - 4].mType == ASMIT_CLC && + mIns[sz - 3].mType == ASMIT_LDA && mIns[sz - 3].mMode == ASMIM_ZERO_PAGE && mIns[sz - 3].mAddress == mIns[0].mAddress && + mIns[sz - 2].mType == ASMIT_ADC && mIns[sz - 2].mMode == ASMIM_IMMEDIATE && + mIns[sz - 1].mType == ASMIT_CMP && mIns[sz - 1].mMode == ASMIM_IMMEDIATE && mBranch == ASMIT_BNE && + !ReferencesZeroPage(mIns[0].mAddress, 1, sz - 4) && !mFalseJump->mEntryRequiredRegs[mIns[0].mAddress] && pblock) + { + int aexit = mIns[sz - 1].mAddress; + int aimm = pblock->RetrieveAValue(pblock->mIns.Size() - 1, 2); + if (aimm >= 0) + { + if (mIns[1].mType == ASMIT_CLC && + mIns[2].mType == ASMIT_ADC && (mIns[2].mMode == ASMIM_IMMEDIATE || mIns[2].mMode == ASMIM_IMMEDIATE_ADDRESS)) + { + if (!prevBlock) + return OptimizeSimpleLoopInvariant(proc, full); + + prevBlock->mIns.Push(NativeCodeInstruction(mIns[2].mIns, ASMIT_LDA, mIns[2])); + int pz = prevBlock->mIns.Size(); + prevBlock->mIns[pz-1].mAddress += aimm; + mIns[sz - 1].CopyMode(mIns[2]); + mIns[sz - 1].mAddress += aexit; + mIns[2].mMode = ASMIM_IMMEDIATE; + mIns[2].mAddress = 0; + changed = true; + } + } + } + + } + + + sz = mIns.Size(); + if (full && pblock && mBranch == ASMIT_BCC && mTrueJump == this && sz >= 2 && !mExitRequiredRegs[CPU_REG_C]) + { + if (mIns[sz - 2].mType == ASMIT_INX && mIns[sz - 1].mType == ASMIT_CPX && !ChangesXReg(0, sz - 2) && + (mIns[sz - 1].mMode == ASMIM_IMMEDIATE || mIns[sz - 1].mMode == ASMIM_ZERO_PAGE && !ChangesZeroPage(mIns[sz - 1].mAddress)) && pblock->RetrieveXValue() == 0) + { + mIns[sz - 1].mLive |= LIVE_CPU_REG_Z; + mBranch = ASMIT_BNE; + changed = true; + } + else if (mIns[sz - 2].mType == ASMIT_INY && mIns[sz - 1].mType == ASMIT_CPY && !ChangesYReg(0, sz - 2) && + (mIns[sz - 1].mMode == ASMIM_IMMEDIATE || mIns[sz - 1].mMode == ASMIM_ZERO_PAGE && !ChangesZeroPage(mIns[sz - 1].mAddress)) && pblock->RetrieveYValue() == 0) + { + mIns[sz - 1].mLive |= LIVE_CPU_REG_Z; + mBranch = ASMIT_BNE; + changed = true; + } + } + +#if 1 + // Check X and Y same increment + if (sz >= 6) + { + int xi = 0; + while (xi < mIns.Size() && mIns[xi].mType != ASMIT_TXA) + xi++; + int yi = 0; + while (yi < mIns.Size() && mIns[yi].mType != ASMIT_TYA) + yi++; + if (xi + 3 < mIns.Size() && yi + 3 < mIns.Size()) + { + if (mIns[xi + 1].mType == ASMIT_CLC && mIns[xi + 2].mType == ASMIT_ADC && mIns[xi + 2].mMode == ASMIM_IMMEDIATE && mIns[xi + 3].mType == ASMIT_TAX && + mIns[yi + 1].mType == ASMIT_CLC && mIns[yi + 2].mType == ASMIT_ADC && mIns[yi + 2].mMode == ASMIM_IMMEDIATE && mIns[yi + 3].mType == ASMIT_TAY && + mIns[xi + 2].mAddress == mIns[yi + 2].mAddress) + { + if (!ChangesXReg(0, xi) && !ChangesXReg(xi + 4) && !ChangesYReg(0, yi) && !ChangesYReg(yi + 4)) + { + if (pblock) + { + int ystart = pblock->RetrieveYValue(pblock->mIns.Size() - 1); + int xstart = pblock->RetrieveXValue(pblock->mIns.Size() - 1); + + if (ystart >= 0 && xstart >= 0) + { + if (!prevBlock) + return OptimizeSimpleLoopInvariant(proc, full); + + if (xi < yi) + { + mIns[xi + 0].mType = ASMIT_TYA; + if (!ReferencesXReg(0, xi) && !ReferencesXReg(xi + 4) && !exitBlock->mEntryRequiredRegs[CPU_REG_A]) + { + exitBlock->mEntryRequiredRegs += CPU_REG_Y; + mIns[xi + 3].mType = ASMIT_NOP; + exitBlock->mIns.Insert(0, NativeCodeInstruction(mIns[xi + 0].mIns, ASMIT_TYA)); + exitBlock->mIns.Insert(1, NativeCodeInstruction(mIns[xi + 0].mIns, ASMIT_TAX)); + } + } + else + { + mIns[yi + 0].mType = ASMIT_TXA; + if (!ReferencesYReg(0, yi) && !ReferencesYReg(yi + 4) && !exitBlock->mEntryRequiredRegs[CPU_REG_A]) + { + exitBlock->mEntryRequiredRegs += CPU_REG_X; + mIns[yi + 3].mType = ASMIT_NOP; + exitBlock->mIns.Insert(0, NativeCodeInstruction(mIns[yi + 0].mIns, ASMIT_TXA)); + exitBlock->mIns.Insert(1, NativeCodeInstruction(mIns[yi + 0].mIns, ASMIT_TAY)); + } + } + changed = true; + } + } + } + } + } + } +#endif + return changed; +} + +bool NativeCodeBasicBlock::SimpleLoopReversal(NativeCodeProcedure* proc) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + CheckLive(); + + + if (mTrueJump && !mFalseJump && mTrueJump->mTrueJump == mTrueJump && mIns.Size() > 0 && mTrueJump->mIns.Size() > 1 && + mTrueJump->mBranch == ASMIT_BNE) + { + NativeCodeBasicBlock* lb = mTrueJump; + int lbs = lb->mIns.Size(); + + if ((lb->mIns[lbs - 2].mType == ASMIT_ROL || lb->mIns[lbs - 2].mType == ASMIT_ROR) && lb->mIns[lbs - 2].mMode == ASMIM_ZERO_PAGE && + lb->mIns[lbs - 1].mType == ASMIT_DEC && lb->mIns[lbs - 1].mMode == ASMIM_ZERO_PAGE && !(lb->mIns[lbs - 1].mLive & LIVE_CPU_REG_C)) + { + if (!lb->ReferencesZeroPage(lb->mIns[lbs - 1].mAddress, 0, lbs - 1) && !lb->ReferencesZeroPage(lb->mIns[lbs - 2].mAddress, 0, lbs - 2) && + !lb->mFalseJump->mEntryRequiredRegs[lb->mIns[lbs - 1].mAddress]) + { + int lcount = RetrieveZPValue(lb->mIns[lbs - 1].mAddress, mIns.Size() - 1); + int lshift = RetrieveZPValue(lb->mIns[lbs - 2].mAddress, mIns.Size() - 1); + + if (lcount <= 8 && lshift == 0) + { + if (!mExitRequiredRegs[CPU_REG_A]) + { + if (lb->mIns[lbs - 2].mType == ASMIT_ROR) + mIns.Push(NativeCodeInstruction(lb->mIns[lbs - 2].mIns, ASMIT_LDA, ASMIM_IMMEDIATE, 1LL << (lcount - 1))); + else + mIns.Push(NativeCodeInstruction(lb->mIns[lbs - 2].mIns, ASMIT_LDA, ASMIM_IMMEDIATE, 1LL << (8 - lcount))); + + mIns.Push(NativeCodeInstruction(lb->mIns[lbs - 2].mIns, ASMIT_STA, ASMIM_ZERO_PAGE, lb->mIns[lbs - 2].mAddress)); + lb->mBranch = ASMIT_BCC; + lb->mIns[lbs - 1].mType = ASMIT_NOP; lb->mIns[lbs - 1].mMode = ASMIM_IMPLIED; + lb->mIns[lbs - 2].mLive |= LIVE_CPU_REG_C; + changed = true; + } + } + } + } + } + + if (mTrueJump && !mFalseJump && mTrueJump->mTrueJump == mTrueJump && mIns.Size() > 0 && mTrueJump->mIns.Size() > 1 && + mTrueJump->mBranch == ASMIT_BCC && !mExitRequiredRegs[CPU_REG_C]) + { + NativeCodeBasicBlock* lb = mTrueJump; + NativeCodeBasicBlock* eb = mTrueJump->mFalseJump; + + int lbs = lb->mIns.Size(); + + if (lb->mIns[lbs-1].mType == ASMIT_CPX) + { + int li = mIns.Size() - 1; + while (li >= 0 && !mIns[li].ChangesXReg()) + li--; + + if (li >= 0 && lb->mIns[lbs-2].mType == ASMIT_INX && mIns[li].mType == ASMIT_LDX && mIns[li].mMode == ASMIM_IMMEDIATE) + { + if (lb->mIns[lbs - 1].mMode == ASMIM_ZERO_PAGE && mIns[li].mAddress == 0) + { + int a = lb->mIns[lbs - 1].mAddress; + + int i = 0; + while (i + 2 < lbs && !(lb->mIns[i].RequiresXReg() || lb->mIns[i].ChangesZeroPage(a))) + i++; + if (i + 2 == lbs) + { + mIns[li].mMode = ASMIM_ZERO_PAGE; + mIns[li].mAddress = a; + lb->mIns[lbs - 2].mType = ASMIT_DEX; lb->mIns[lbs - 2].mLive |= LIVE_CPU_REG_Z; + lb->mIns[lbs - 1].mType = ASMIT_NOP; lb->mIns[lbs - 1].mMode = ASMIM_IMPLIED; + lb->mBranch = ASMIT_BNE; + eb->mIns.Insert(0, NativeCodeInstruction(lb->mIns[lbs - 1].mIns, ASMIT_LDX, mIns[li])); + changed = true; + + CheckLive(); + + } + } + else if (lb->mIns[lbs - 1].mMode == ASMIM_IMMEDIATE) + { + int finalx = lb->mIns[lbs - 1].mAddress; + int a = lb->mIns[lbs - 1].mAddress - mIns[li].mAddress; + if (a <= 0) + { + finalx = mIns[li].mAddress + 1; + a = 1; + } + + if (lbs == 3 && lb->mIns[0].mType == ASMIT_STA && lb->mIns[0].mMode == ASMIM_ABSOLUTE_X && + ((lb->mIns[0].mLinkerObject && a < 128) || + (!lb->mIns[0].mLinkerObject && !(lb->mIns[0].mFlags & NCIF_VOLATILE) && a <= 40))) + { + lb->mIns[0].mAddress += mIns[li].mAddress; + mIns[li].mAddress = a - 1; + lb->mIns[1].mType = ASMIT_DEX; lb->mIns[1].mLive |= LIVE_CPU_REG_Z; + lb->mIns[2].mType = ASMIT_NOP; lb->mIns[2].mMode = ASMIM_IMPLIED; + lb->mBranch = ASMIT_BPL; + eb->mIns.Insert(0, NativeCodeInstruction(lb->mIns[lbs - 1].mIns, ASMIT_LDX, ASMIM_IMMEDIATE, finalx)); + changed = true; + } + else + { + int i = 0; + while (i + 2 < lbs && !lb->mIns[i].RequiresXReg()) + i++; + if (i + 2 == lbs) + { + mIns[li].mAddress = a; + lb->mIns[lbs - 2].mType = ASMIT_DEX; lb->mIns[lbs - 2].mLive |= LIVE_CPU_REG_Z; + lb->mIns[lbs - 1].mType = ASMIT_NOP; lb->mIns[lbs - 1].mMode = ASMIM_IMPLIED; + lb->mBranch = ASMIT_BNE; + eb->mIns.Insert(0, NativeCodeInstruction(lb->mIns[lbs - 1].mIns, ASMIT_LDX, ASMIM_IMMEDIATE, finalx)); + changed = true; + + CheckLive(); + + } + } + + if (a == 1 && changed) + { + lb->mBranch = ASMIT_JMP; + lb->mTrueJump->mEntryBlocks.RemoveAll(lb); + lb->mTrueJump->mNumEntries--; + lb->mTrueJump = lb->mFalseJump; + lb->mFalseJump = nullptr; + } + } + } + + } + else if (lb->mIns[lbs - 1].mType == ASMIT_CPY) + { + int li = mIns.Size() - 1; + while (li >= 0 && !mIns[li].ReferencesYReg()) + li--; + + if (li >= 0 && lb->mIns[lbs - 2].mType == ASMIT_INY && mIns[li].mType == ASMIT_LDY && mIns[li].mMode == ASMIM_IMMEDIATE) + { + if (lb->mIns[lbs - 1].mMode == ASMIM_ZERO_PAGE) + { + int a = lb->mIns[lbs - 1].mAddress - mIns[li].mAddress; + + int i = 0; + while (i + 2 < lbs && !(lb->mIns[i].RequiresYReg() || lb->mIns[i].ChangesZeroPage(a))) + i++; + if (i + 2 == lbs) + { + mIns[li].mMode = ASMIM_ZERO_PAGE; + mIns[li].mAddress = a; + lb->mIns[lbs - 2].mType = ASMIT_DEY; lb->mIns[lbs - 2].mLive |= LIVE_CPU_REG_Z; + lb->mIns[lbs - 1].mType = ASMIT_NOP; lb->mIns[lbs - 1].mMode = ASMIM_IMPLIED; + lb->mBranch = ASMIT_BNE; + eb->mIns.Insert(0, NativeCodeInstruction(lb->mIns[lbs - 1].mIns, ASMIT_LDY, mIns[li])); + changed = true; + + CheckLive(); + } + } + else if (lb->mIns[lbs - 1].mMode == ASMIM_IMMEDIATE) + { + int finaly = lb->mIns[lbs - 1].mAddress; + int a = lb->mIns[lbs - 1].mAddress - mIns[li].mAddress; + + if (a <= 0) + { + finaly = mIns[li].mAddress + 1; + a = 1; + } + + if (lbs == 3 && lb->mIns[0].mType == ASMIT_STA && lb->mIns[0].mMode == ASMIM_ABSOLUTE_Y && + ((lb->mIns[0].mLinkerObject && a < 128) || + (!lb->mIns[0].mLinkerObject && !(lb->mIns[0].mFlags & NCIF_VOLATILE) && a <= 40))) + { + lb->mIns[0].mAddress += mIns[li].mAddress; + mIns[li].mAddress = a - 1; + lb->mIns[1].mType = ASMIT_DEY; lb->mIns[1].mLive |= LIVE_CPU_REG_Z; + lb->mIns[2].mType = ASMIT_NOP; lb->mIns[2].mMode = ASMIM_IMPLIED; + lb->mBranch = ASMIT_BPL; + eb->mIns.Insert(0, NativeCodeInstruction(lb->mIns[lbs - 1].mIns, ASMIT_LDY, ASMIM_IMMEDIATE, finaly)); + changed = true; + + CheckLive(); + + } + else if (lbs == 3 && lb->mIns[0].mType == ASMIT_STA && lb->mIns[0].mMode == ASMIM_INDIRECT_Y && !(lb->mIns[0].mFlags & NCIF_VOLATILE) && a <= 40 && mIns[li].mAddress == 0) + { + mIns[li].mAddress = a - 1; + lb->mIns[1].mType = ASMIT_DEY; lb->mIns[1].mLive |= LIVE_CPU_REG_Z; + lb->mIns[2].mType = ASMIT_NOP; lb->mIns[2].mMode = ASMIM_IMPLIED; + lb->mBranch = ASMIT_BPL; + eb->mIns.Insert(0, NativeCodeInstruction(lb->mIns[lbs - 1].mIns, ASMIT_LDY, ASMIM_IMMEDIATE, finaly)); + changed = true; + + CheckLive(); + } + else if (lbs == 3 && lb->mIns[0].mType == ASMIT_STA && lb->mIns[0].mMode == ASMIM_INDIRECT_Y && !(lb->mIns[0].mFlags & NCIF_VOLATILE) && a <= 40 && mIns[li].mAddress == 1) + { + mIns[li].mAddress = a; + lb->mIns[1].mType = ASMIT_DEY; lb->mIns[1].mLive |= LIVE_CPU_REG_Z; + lb->mIns[2].mType = ASMIT_NOP; lb->mIns[2].mMode = ASMIM_IMPLIED; + lb->mBranch = ASMIT_BNE; + eb->mIns.Insert(0, NativeCodeInstruction(lb->mIns[lbs - 1].mIns, ASMIT_LDY, ASMIM_IMMEDIATE, finaly)); + changed = true; + + CheckLive(); + } + else + { + int i = 0; + while (i + 2 < lbs && !lb->mIns[i].RequiresYReg()) + i++; + if (i + 2 == lbs) + { + mIns[li].mAddress = a; + lb->mIns[lbs - 2].mType = ASMIT_DEY; lb->mIns[lbs - 2].mLive |= LIVE_CPU_REG_Z; + lb->mIns[lbs - 1].mType = ASMIT_NOP; lb->mIns[lbs - 1].mMode = ASMIM_IMPLIED; + lb->mBranch = ASMIT_BNE; + eb->mIns.Insert(0, NativeCodeInstruction(lb->mIns[lbs - 1].mIns, ASMIT_LDY, ASMIM_IMMEDIATE, finaly)); + changed = true; + + CheckLive(); + + } + } + + if (a == 1 && changed) + { + lb->mBranch = ASMIT_JMP; + lb->mTrueJump->mEntryBlocks.RemoveAll(lb); + lb->mTrueJump->mNumEntries--; + lb->mTrueJump = lb->mFalseJump; + lb->mFalseJump = nullptr; + } + } + } + } + } + + if (mTrueJump && !mFalseJump && mTrueJump->mTrueJump == mTrueJump && mIns.Size() > 0 && mTrueJump->mIns.Size() > 2 && + mTrueJump->mBranch == ASMIT_BCC) + { + NativeCodeBasicBlock* lb = mTrueJump; + int lbs = lb->mIns.Size(); + + if ((lb->mIns[lbs - 3].mType == ASMIT_ROL || lb->mIns[lbs - 3].mType == ASMIT_ROR) && lb->mIns[lbs - 3].mMode == ASMIM_ZERO_PAGE && + lb->mIns[lbs - 2].mType == ASMIT_INY && + lb->mIns[lbs - 1].mType == ASMIT_CPY && lb->mIns[lbs - 1].mMode == ASMIM_IMMEDIATE) + { + if (!lb->ReferencesZeroPage(lb->mIns[lbs - 3].mAddress, 0, lbs - 3)) + { + int ystart = RetrieveYValue(mIns.Size() - 1); + int lcount = lb->mIns[lbs - 1].mAddress - ystart; + int lshift = RetrieveZPValue(lb->mIns[lbs - 3].mAddress, mIns.Size() - 1); + + if (ystart >= 0 && lcount <= 8 && lshift == 0) + { + if (!mExitRequiredRegs[CPU_REG_A]) + { + if (lb->mIns[lbs - 2].mType == ASMIT_ROR) + mIns.Push(NativeCodeInstruction(lb->mIns[lbs - 2].mIns, ASMIT_LDA, ASMIM_IMMEDIATE, 1LL << (lcount - 1))); + else + mIns.Push(NativeCodeInstruction(lb->mIns[lbs - 2].mIns, ASMIT_LDA, ASMIM_IMMEDIATE, 1LL << (8 - lcount))); + + mIns.Push(NativeCodeInstruction(lb->mIns[lbs - 2].mIns, ASMIT_STA, ASMIM_ZERO_PAGE, lb->mIns[lbs - 3].mAddress)); + lb->mBranch = ASMIT_BCC; + lb->mIns[lbs - 1].mType = ASMIT_NOP; lb->mIns[lbs - 1].mMode = ASMIM_IMPLIED; + lb->mIns[lbs - 2].mLive |= LIVE_CPU_REG_C; + lb->mIns[lbs - 3].mLive |= LIVE_CPU_REG_C; + changed = true; + } + } + } + } + } + + + if (mTrueJump && mTrueJump->SimpleLoopReversal(proc)) + changed = true; + if (mFalseJump && mFalseJump->SimpleLoopReversal(proc)) + changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::OptimizeXYSpilling(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + if (mLoopHead && (mTrueJump == this || mFalseJump == this) && mEntryBlocks.Size() == 2) + { + NativeCodeBasicBlock* pblock = mEntryBlocks[0]; + if (pblock == this) + pblock = mEntryBlocks[1]; + NativeCodeBasicBlock* nblock = mTrueJump; + if (nblock == this) + nblock = mFalseJump; + + if (nblock) + { + int ps = pblock->mIns.Size(); + if (nblock->mEntryBlocks.Size() == 1 && !pblock->mFalseJump && ps > 0) + { + if (!ReferencesAccu()) + { + if (!ReferencesXReg() && pblock->mIns.Size() > 0 && pblock->mIns[ps - 1].mType == ASMIT_TAX) + { + nblock->mIns.Insert(0, NativeCodeInstruction(pblock->mIns[ps - 1].mIns, ASMIT_TAX)); + pblock->mIns.Remove(ps - 1); + ps--; + pblock->mExitRequiredRegs += CPU_REG_A; + nblock->mEntryRequiredRegs += CPU_REG_A; + mEntryRequiredRegs += CPU_REG_A; + mExitRequiredRegs += CPU_REG_A; + for (int i = 0; i < mIns.Size(); i++) + mIns[i].mLive |= LIVE_CPU_REG_A; + changed = true; + } + + if (!ReferencesYReg() && pblock->mIns.Size() > 0 && pblock->mIns[ps - 1].mType == ASMIT_TAY) + { + nblock->mIns.Insert(0, NativeCodeInstruction(pblock->mIns[ps - 1].mIns, ASMIT_TAY)); + pblock->mIns.Remove(ps - 1); + ps--; + pblock->mExitRequiredRegs += CPU_REG_A; + nblock->mEntryRequiredRegs += CPU_REG_A; + mEntryRequiredRegs += CPU_REG_A; + mExitRequiredRegs += CPU_REG_A; + for (int i = 0; i < mIns.Size(); i++) + mIns[i].mLive |= LIVE_CPU_REG_A; + changed = true; + } + } + } + } + } + + for (int i = 0; i < mIns.Size(); i++) + { + if (i + 2 < mIns.Size()) + { + if (mIns[i + 0].mType == ASMIT_TAX && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_IMMEDIATE && + mIns[i + 2].mType == ASMIT_STA && mIns[i + 2].mMode == ASMIM_ABSOLUTE && !(mIns[i + 2].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z))) + { + mIns[i + 1].mType = ASMIT_LDX; mIns[i + 1].mLive |= LIVE_CPU_REG_A | LIVE_CPU_REG_X; + mIns[i + 2].mType = ASMIT_STX; mIns[i + 2].mLive |= LIVE_CPU_REG_A; + mIns.Insert(i + 3, NativeCodeInstruction(mIns[i + 0].mIns, ASMIT_TAX)); + mIns.Remove(i); + changed = true; + } + else if (mIns[i + 0].mType == ASMIT_TAY && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_IMMEDIATE && + mIns[i + 2].mType == ASMIT_STA && mIns[i + 2].mMode == ASMIM_ABSOLUTE && !(mIns[i + 2].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z))) + { + mIns[i + 1].mType = ASMIT_LDY; mIns[i + 1].mLive |= LIVE_CPU_REG_A | LIVE_CPU_REG_Y; + mIns[i + 2].mType = ASMIT_STY; mIns[i + 2].mLive |= LIVE_CPU_REG_A; + mIns.Insert(i + 3, NativeCodeInstruction(mIns[i + 0].mIns, ASMIT_TAY)); + mIns.Remove(i); + changed = true; + } + } + + if (i + 1 < mIns.Size()) + { + if (mIns[i + 0].mType == ASMIT_TAX && !mIns[i + 1].ChangesAccu() && !mIns[i + 1].ReferencesXReg() && !(mIns[i + 1].mLive & LIVE_CPU_REG_Z)) + { + mIns[i + 1].mLive |= LIVE_CPU_REG_A; + mIns.Insert(i + 2, NativeCodeInstruction(mIns[i + 0].mIns, ASMIT_TAX)); + mIns.Remove(i); + changed = true; + } + else if (mIns[i + 0].mType == ASMIT_TAY && !mIns[i + 1].ChangesAccu() && !mIns[i + 1].ReferencesYReg() && !(mIns[i + 1].mLive & LIVE_CPU_REG_Z)) + { + mIns[i + 1].mLive |= LIVE_CPU_REG_A; + mIns.Insert(i + 2, NativeCodeInstruction(mIns[i + 0].mIns, ASMIT_TAY)); + mIns.Remove(i); + changed = true; + } + } + } + + if (mTrueJump && mTrueJump->OptimizeXYSpilling()) + changed = true; + if (mFalseJump && mFalseJump->OptimizeXYSpilling()) + changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::OptimizeXYSimpleLoop(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + if (mLoopHead && mTrueJump && mFalseJump && mEntryBlocks.Size() == 2) + { + NativeCodeBasicBlock * pblock, * eblock; + if (mTrueJump == this) + eblock = mFalseJump; + else + eblock = mTrueJump; + + if (mEntryBlocks[0] == this) + pblock = mEntryBlocks[1]; + else + pblock = mEntryBlocks[0]; + + int sz = mIns.Size(); + + if (sz > 2 && mIns[sz - 2].mType == ASMIT_STY && mIns[sz - 2].mMode == ASMIM_ZERO_PAGE && mIns[sz - 1].mType == ASMIT_CPY) + { + int addr = mIns[sz - 2].mAddress; + int psz = pblock->mIns.Size(); + if (psz > 0 && pblock->mIns[psz - 1].mType == ASMIT_STY && pblock->mIns[psz - 1].mMode == ASMIM_ZERO_PAGE && pblock->mIns[psz - 1].mAddress == addr) + { + int i = 0; + while (i + 2 < sz && !mIns[i].ChangesYReg() && !mIns[i].ChangesZeroPage(addr) && (mIns[i].mLive & LIVE_CPU_REG_Y)) + { + if (mIns[i].mType == ASMIT_LDA && mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i].mAddress == addr) + { + mIns[i].mType = ASMIT_TYA; mIns[i].mMode = ASMIM_IMPLIED; + changed = true; + } + else if (mIns[i].mType == ASMIT_LDA && mIns[i + 1].IsCommutative() && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && mIns[i + 1].mAddress == addr) + { + mIns[i + 1].CopyMode(mIns[i]); + mIns[i].mType = ASMIT_TYA; mIns[i].mMode = ASMIM_IMPLIED; + changed = true; + } + + i++; + } + } + } + +#if 1 + if (sz > 2 && mIns[sz - 2].mType == ASMIT_INY && mIns[sz - 1].mType == ASMIT_CPY) + { + int i = 0; + while (i < sz - 2 && !mIns[i].ChangesYReg()) + i++; + + // y register is simple loop increment + if (i == sz - 2) + { + i = 0; + while (i < sz && !mIns[i].ReferencesXReg()) + i++; + if (i < sz && i >= 3 && mIns[i].mType == ASMIT_TAX && mIns[i - 3].mType == ASMIT_TYA && mIns[i - 2].mType == ASMIT_CLC && mIns[i - 1].mType == ASMIT_ADC) + { + int j = i + 4; + while (j < sz && !mIns[j].ChangesXReg()) + j++; + if (j == sz) + { + j = 0; + while (j < sz && !mIns[i - 1].MayBeChangedOnAddress(mIns[j])) + j++; + if (j == sz) + { + for (int j = 0; j < 4; j++) + { + pblock->mIns.Push(mIns[i - 3 + j]); + mIns[i - 3 + j].mType = ASMIT_NOP; mIns[i - 3 + j].mMode = ASMIM_IMPLIED; + } + + for (int j = 0; j < sz; j++) + mIns[j].mLive |= LIVE_CPU_REG_X; + + mIns.Insert(sz - 2, NativeCodeInstruction(mIns[i - 3].mIns, ASMIT_INX)); + sz++; + changed = true; + mEntryRequiredRegs += CPU_REG_X; + mExitRequiredRegs += CPU_REG_X; + pblock->mExitRequiredRegs += CPU_REG_X; + } + } + } + } + else if (sz > 5 && + mIns[0].mType == ASMIT_STY && mIns[0].mMode == ASMIM_ZERO_PAGE && + mIns[sz - 3].mType == ASMIT_LDY && mIns[sz - 3].mMode == ASMIM_ZERO_PAGE && mIns[0].mAddress == mIns[sz - 3].mAddress && + !ChangesZeroPage(mIns[0].mAddress, 1, sz - 3) && !(mIns[sz - 3].mLive & LIVE_CPU_REG_X)) + { + int j = 0; + while (j < sz - 3 && !(mIns[j].ChangesYReg() || mIns[j].mType == ASMIT_LDX && mIns[j].mMode == ASMIM_ZERO_PAGE && mIns[j].mAddress == mIns[0].mAddress)) + j++; + if (mIns[j].mType == ASMIT_LDX && !ChangesXReg(j + 1, sz - 3)) + { + int k = j + 1; + while (k < sz - 3 && mIns[k].CanSwapXYReg()) + k++; + if (k == sz - 3) + { + for (k = j + 1; k < sz - 3; k++) + mIns[k].SwapXYReg(); + + for (k = 0; k < sz; k++) + mIns[k].mLive |= LIVE_CPU_REG_Y; + + mIns[j].mType = ASMIT_NOP; mIns[j].mMode = ASMIM_IMPLIED; + mIns[sz - 3].mType = ASMIT_NOP; mIns[sz - 3].mMode = ASMIM_IMPLIED; + changed = true; + } + } + } + } +#endif + } + + if (mTrueJump && mTrueJump->OptimizeXYSimpleLoop()) + changed = true; + if (mFalseJump && mFalseJump->OptimizeXYSimpleLoop()) + changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::OptimizeSimpleFixLoop(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + if (mLoopHead && mFalseJump && mNumEntries == 2) + { + NativeCodeBasicBlock* lbody = nullptr, * eblock = nullptr, * pblock = nullptr; + AsmInsType bins = mBranch; + + if (mTrueJump == this) + eblock = mFalseJump; + else if (mFalseJump == this) + { + eblock = mTrueJump; + bins = InvertBranchCondition(bins); + } + + if (eblock) + { + if (mEntryBlocks[0] == this) + pblock = mEntryBlocks[1]; + else + pblock = mEntryBlocks[0]; + + int nloop = -1; + int sz = mIns.Size(); + + if (sz > 1 && mIns[sz - 1].mType == ASMIT_DEX && bins == ASMIT_BNE && !ChangesXReg(0, sz - 1)) + { + int xval = pblock->RetrieveXValue(pblock->mIns.Size() - 1); + if (xval == 0) + nloop = 256; + else + nloop = xval; + sz -= 1; + } + else if (sz > 2 && bins == ASMIT_BNE && + mIns[sz - 2].mType == ASMIT_LDA && mIns[sz - 2].mMode == ASMIM_ZERO_PAGE && + mIns[sz - 1].mType == ASMIT_CMP && mIns[sz - 1].mMode == ASMIM_IMMEDIATE_ADDRESS && + mIns[sz - 1].mLinkerObject && (mIns[sz - 1].mFlags & NCIF_LOWER)) + { + int reg = mIns[sz - 2].mAddress; + int k = 0; + while (k < mIns.Size() && !mIns[k].ChangesZeroPage(reg)) + k++; + if (k < mIns.Size() && k >= 3 && !ChangesZeroPage(reg, k + 1)) + { + if (mIns[k - 3].mType == ASMIT_CLC && + mIns[k - 2].mType == ASMIT_LDA && mIns[k - 2].mMode == ASMIM_ZERO_PAGE && mIns[k - 2].mAddress == reg && + mIns[k - 1].mType == ASMIT_ADC && mIns[k - 1].mMode == ASMIM_IMMEDIATE) + { + int j = pblock->mIns.Size() - 1; + while (j >= 0 && !pblock->mIns[j].ChangesZeroPage(reg)) + j--; + if (j > 0 && pblock->mIns[j].mType == ASMIT_STA && pblock->mIns[j - 1].mType == ASMIT_LDA && + pblock->mIns[j - 1].mMode == ASMIM_IMMEDIATE_ADDRESS && + pblock->mIns[j - 1].mLinkerObject == mIns[sz - 1].mLinkerObject) + { + int ni = mIns[sz - 1].mAddress - pblock->mIns[j - 1].mAddress; + if (ni < 256 && ni % mIns[k - 1].mAddress == 0) + { + nloop = ni / mIns[k - 1].mAddress; + sz -= 2; + } + } + } + } + } + + if (nloop > 0) + { + const InterInstruction* ihead = nullptr; + + signed char zpinc[256]; + + // -1 unused + // -2 used low byte + // -3 used high byte + // -4 used + + for (int i = 0; i < 256; i++) + zpinc[i] = -1; + + int ypred = -1, ystart = -1, ymax = -1; + int i = 0; + while (i < sz) + { + if (mIns[i].mType == ASMIT_LDY && mIns[i].mMode == ASMIM_IMMEDIATE) + { + if (ypred == -1) + { + ystart = ypred = mIns[i].mAddress; + ihead = mIns[i].mIns; + } + else if (mIns[i].mAddress == ypred || mIns[i].mAddress == ((ypred + 1) & 255) || mIns[i].mAddress == ((ypred - 1) & 255)) + ypred = mIns[i].mAddress; + else + break; + } + else if (mIns[i].mType == ASMIT_LDY || mIns[i].mType == ASMIT_STY || mIns[i].mMode == ASMIM_ABSOLUTE_Y) + break; + else if (mIns[i].mType == ASMIT_INY) + { + if (ypred == -1) + break; + ypred = (ypred + 1) & 255; + } + else if (mIns[i].mType == ASMIT_DEY) + { + if (ypred == -1) + break; + ypred = (ypred - 1) & 255; + } + else if (mIns[i].mMode == ASMIM_INDIRECT_Y) + { + if ((zpinc[mIns[i].mAddress + 0] == -1 || zpinc[mIns[i].mAddress + 0] == -2) && + (zpinc[mIns[i].mAddress + 1] == -1 || zpinc[mIns[i].mAddress + 1] == -3)) + { + zpinc[mIns[i].mAddress + 0] = -2; + zpinc[mIns[i].mAddress + 1] = -3; + } + else + break; + } + else if (mIns[i].mType == ASMIT_CLC && i + 6 < mIns.Size()) + { + if (mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && mIns[i + 1].mAddress == mIns[i + 3].mAddress && + mIns[i + 4].mType == ASMIT_LDA && mIns[i + 4].mMode == ASMIM_ZERO_PAGE && mIns[i + 4].mAddress == mIns[i + 1].mAddress + 1 && + mIns[i + 5].mType == ASMIT_ADC && mIns[i + 5].mMode == ASMIM_IMMEDIATE && mIns[i + 5].mAddress == 0 && + mIns[i + 6].mType == ASMIT_STA && mIns[i + 6].mMode == ASMIM_ZERO_PAGE && mIns[i + 4].mAddress == mIns[i + 6].mAddress && + !(mIns[i + 6].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C | LIVE_CPU_REG_Z))) + { +// printf("Add %d, %d, %d\n", i, zpinc[mIns[i + 1].mAddress + 0], mIns[i + 2].mAddress); + + if (zpinc[mIns[i + 1].mAddress + 0] == -1) + ; + else if (zpinc[mIns[i + 1].mAddress + 0] == -2) + { + zpinc[mIns[i + 1].mAddress + 0] = mIns[i + 2].mAddress; + i += 6; + } + else + break; + } + } + else if (mIns[i].mMode == ASMIM_ZERO_PAGE) + { + if (zpinc[mIns[i].mAddress + 0] == -1 || zpinc[mIns[i].mAddress + 0] == -4) + zpinc[mIns[i].mAddress + 0] = -4; + else + break; + } + + if (ypred > ymax) + ymax = ypred; + + if (ypred >= 128) + break; + + i++; + } + + if (ystart >= 0 && i == sz) + { + int yinc = -1; + int i = 0; + while (i < 256) + { + if (zpinc[i] >= 0) + { + if (yinc == -1 || yinc == zpinc[i]) + yinc = zpinc[i]; + else + break; + } + else if (zpinc[i] == -2) + break; + i++; + } + + if (i == 256 && yinc > 0 && ymax + (nloop - 1) * yinc < 256) + { + if (eblock->mNumEntries > 1) + { + if (eblock == mTrueJump) + mTrueJump = eblock = BuildSingleEntry(mProc, eblock); + else + mFalseJump = eblock = BuildSingleEntry(mProc, eblock); + } + + pblock->mIns.Push(NativeCodeInstruction(ihead, ASMIT_LDY, ASMIM_IMMEDIATE, ystart)); + ypred = ystart; + + int ei = 0; + i = 0; + while (i < mIns.Size()) + { + if (mIns[i].mType == ASMIT_LDY && mIns[i].mMode == ASMIM_IMMEDIATE) + { + if (mIns[i].mAddress == ypred) + { + mIns[i].mType = ASMIT_NOP; mIns[i].mMode = ASMIM_IMPLIED; + } + else if (mIns[i].mAddress == ((ypred + 1) & 255)) + { + ypred = mIns[i].mAddress; + mIns[i].mType = ASMIT_INY; mIns[i].mMode = ASMIM_IMPLIED; + } + else if (mIns[i].mAddress == ((ypred - 1) & 255)) + { + ypred = mIns[i].mAddress; + mIns[i].mType = ASMIT_DEY; mIns[i].mMode = ASMIM_IMPLIED; + } + i++; + } + else if (mIns[i].mType == ASMIT_INY) + { + ypred = (ypred + 1) & 255; + i++; + } + else if (mIns[i].mType == ASMIT_DEY) + { + ypred = (ypred - 1) & 255; + i++; + } + else if (mIns[i].mType == ASMIT_CLC && i + 6 < mIns.Size() && mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && zpinc[mIns[i + 1].mAddress] >= 0) + { + mIns[i + 2].mAddress = yinc * nloop; + for (int k = 0; k < 7; k++) + eblock->mIns.Insert(ei++, mIns[i + k]); + mIns.Remove(i, 7); + } + else + { + mIns[i].mLive |= LIVE_CPU_REG_Y; + i++; + } + } + + pblock->mExitRequiredRegs += CPU_REG_Y; + mEntryRequiredRegs += CPU_REG_Y; + mExitRequiredRegs += CPU_REG_Y; + + sz = mIns.Size(); + mIns[sz - 1] = NativeCodeInstruction(mIns[sz - 1].mIns, ASMIT_CPY, ASMIM_IMMEDIATE, (ystart + yinc * nloop) & 255); + if (ypred != yinc) + { + mIns.Insert(sz - 1, NativeCodeInstruction(ihead, ASMIT_CLC)); + mIns.Insert(sz - 0, NativeCodeInstruction(ihead, ASMIT_TYA)); + mIns.Insert(sz + 1, NativeCodeInstruction(ihead, ASMIT_ADC, ASMIM_IMMEDIATE, (yinc + ystart - ypred) & 255)); + mIns.Insert(sz + 2, NativeCodeInstruction(ihead, ASMIT_TAY)); + } + + changed = true; + } + } + } + } + } + + if (mTrueJump && mTrueJump->OptimizeSimpleFixLoop()) + changed = true; + if (mFalseJump && mFalseJump->OptimizeSimpleFixLoop()) + changed = true; + } + + return changed; + +} + + + +bool NativeCodeBasicBlock::OptimizeSimpleForLoop(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + if (mLoopHead && mFalseJump && mNumEntries == 2) + { + NativeCodeBasicBlock* lbody = nullptr, * eblock = nullptr, *pblock = nullptr; + + if (!mTrueJump->mFalseJump && mTrueJump->mTrueJump == this) + { + lbody = mTrueJump; + eblock = mFalseJump; + } + else if (!mFalseJump->mFalseJump && mFalseJump->mTrueJump == this) + { + lbody = mFalseJump; + eblock = mTrueJump; + } + + if (lbody) + { + if (mEntryBlocks[0] == lbody) + pblock = mEntryBlocks[1]; + else + pblock = mEntryBlocks[0]; + + if (!pblock->mFalseJump && eblock->mNumEntries == 1) + { + if (mIns.Size() == 2 && mIns[0].mType == ASMIT_LDA && mIns[0].mMode == ASMIM_ZERO_PAGE && mIns[1].mType == ASMIT_CMP && HasAsmInstructionMode(ASMIT_CPY, mIns[1].mMode) && !(mIns[0].mLive & LIVE_CPU_REG_Y) && !lbody->ReferencesYReg()) + { + int reg = mIns[0].mAddress; + bool fail = false; + + for (int i = 0; !fail && i < lbody->mIns.Size(); i++) + { + const NativeCodeInstruction& ins(lbody->mIns[i]); + if (ins.ReferencesZeroPage(reg)) + { + if (ins.mType == ASMIT_LDA || ins.mType == ASMIT_INC || ins.mType == ASMIT_DEC) + ; + else if (ins.mType == ASMIT_LDX) + { + if (i + 1 < lbody->mIns.Size()) + { + if (lbody->mIns[i + 1].mMode == ASMIM_ABSOLUTE_X && !(lbody->mIns[i + 1].mLive & LIVE_CPU_REG_X) && HasAsmInstructionMode(lbody->mIns[i + 1].mType, ASMIM_ABSOLUTE_Y)) + ; + else + fail = true; + } + else + fail = true; + } + else + fail = true; + } + } + + if (!fail) + { + mIns[0].mType = ASMIT_NOP; mIns[0].mMode = ASMIM_IMPLIED; + mIns[1].mType = ASMIT_CPY; mIns[1].mLive |= LIVE_CPU_REG_Y; + mEntryRequiredRegs += CPU_REG_Y; + mExitRequiredRegs += CPU_REG_Y; + + lbody->mEntryRequiredRegs += CPU_REG_Y; + lbody->mExitRequiredRegs += CPU_REG_Y; + + for (int i = 0; i < lbody->mIns.Size(); i++) + { + NativeCodeInstruction& ins(lbody->mIns[i]); + ins.mLive |= LIVE_CPU_REG_Y; + + if (ins.ReferencesZeroPage(reg)) + { + if (ins.mType == ASMIT_LDA) + { + ins.mType = ASMIT_TYA; + ins.mMode = ASMIM_IMPLIED; + } + else if (ins.mType == ASMIT_INC) + { + ins.mType = ASMIT_INY; + ins.mMode = ASMIM_IMPLIED; + } + else if (ins.mType == ASMIT_DEC) + { + ins.mType = ASMIT_DEY; + ins.mMode = ASMIM_IMPLIED; + } + else if (ins.mType == ASMIT_LDX) + { + ins.mType = ASMIT_NOP; ins.mMode = ASMIM_IMPLIED; + lbody->mIns[i + 1].mMode = ASMIM_ABSOLUTE_Y; + } + } + } + + eblock->mEntryRequiredRegs += CPU_REG_Y; + pblock->mExitRequiredRegs += CPU_REG_Y; + + pblock->mIns.Push(NativeCodeInstruction(mIns[0].mIns, ASMIT_LDY, ASMIM_ZERO_PAGE, reg)); + eblock->mIns.Insert(0, NativeCodeInstruction(mIns[0].mIns, ASMIT_STY, ASMIM_ZERO_PAGE, reg)); + + changed = true; + + } + } + else if (mIns.Size() >= 2 && mIns[0].mType == ASMIT_LDY && mIns[0].mMode == ASMIM_ZERO_PAGE) + { + int reg = mIns[0].mAddress; + + int lz = lbody->mIns.Size(); + + int yoffset = 0; + if (lbody->ChangesYReg()) + { + for (int i = 0; i < lbody->mIns.Size(); i++) + { + if (lbody->mIns[i].mType == ASMIT_INY) + yoffset++; + else if (lbody->mIns[i].mType == ASMIT_DEY) + yoffset--; + else if (lbody->mIns[i].ChangesYReg()) + { + yoffset = 1000; + break; + } + } + } + + if (lz > 0 && lbody->mIns[lz - 1].mType == ASMIT_INC && lbody->mIns[lz - 1].mMode == ASMIM_ZERO_PAGE && + lbody->mIns[lz - 1].mAddress == reg && !ReferencesZeroPage(reg, 1) && + !lbody->ReferencesZeroPage(reg, 0, lz - 1) && yoffset >= 0 && yoffset <= 1) + { + pblock->mIns.Push(NativeCodeInstruction(mIns[0].mIns, ASMIT_LDY, ASMIM_ZERO_PAGE, reg)); + eblock->mIns.Insert(0, NativeCodeInstruction(mIns[0].mIns, ASMIT_STY, ASMIM_ZERO_PAGE, reg)); + + mIns[0].mType = ASMIT_NOP; mIns[0].mMode = ASMIM_IMPLIED; + if (yoffset == 0) + { + lbody->mIns[lz - 1].mType = ASMIT_INY; lbody->mIns[lz - 1].mMode = ASMIM_IMPLIED; + } + else + { + lbody->mIns[lz - 1].mType = ASMIT_NOP; lbody->mIns[lz - 1].mMode = ASMIM_IMPLIED; + } + for (int i = 0; i < mIns.Size(); i++) + mIns[i].mLive |= LIVE_CPU_REG_Y; + for (int i = 0; i < lbody->mIns.Size(); i++) + lbody->mIns[i].mLive |= LIVE_CPU_REG_Y; + mExitRequiredRegs += CPU_REG_Y; + mEntryRequiredRegs += CPU_REG_Y; + eblock->mEntryRequiredRegs += CPU_REG_Y; + pblock->mExitRequiredRegs += CPU_REG_Y; + changed = true; + } + } + } + } + } + + if (mTrueJump && mTrueJump->OptimizeSimpleForLoop()) + changed = true; + if (mFalseJump && mFalseJump->OptimizeSimpleForLoop()) + changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::SimplifyLoopExitCondition(void) +{ + bool changed = false; + if (!mVisited) + { + mVisited = true; + + int sz = mIns.Size(); + + if (mLoopHead && mTrueJump == this && mEntryBlocks.Size() == 2) + { + NativeCodeBasicBlock* pblock = mEntryBlocks[0]; + if (pblock == this) + pblock = mEntryBlocks[1]; + + if (sz > 1 && + mIns[sz - 1].mType == ASMIT_CPY && mIns[sz - 1].mMode == ASMIM_IMMEDIATE && + mIns[sz - 2].mType == ASMIT_INY && mBranch == ASMIT_BCC && + !ChangesYReg(0, sz - 2)) + { + int yval = pblock->RetrieveYValue(); + if (yval >= 0 && yval < mIns[sz - 1].mAddress) + { + mBranch = ASMIT_BNE; + mIns[sz - 1].mLive |= LIVE_CPU_REG_Z; + changed = true; + } + } + if (sz > 1 && + mIns[sz - 1].mType == ASMIT_CPX && mIns[sz - 1].mMode == ASMIM_IMMEDIATE && + mIns[sz - 2].mType == ASMIT_INX && mBranch == ASMIT_BCC && + !ChangesXReg(0, sz - 2)) + { + int xval = pblock->RetrieveXValue(); + if (xval >= 0 && xval < mIns[sz - 1].mAddress) + { + mBranch = ASMIT_BNE; + mIns[sz - 1].mLive |= LIVE_CPU_REG_Z; + changed = true; + } + } + } + + if (mTrueJump && mTrueJump->SimplifyLoopExitCondition()) + changed = true; + if (mFalseJump && mFalseJump->SimplifyLoopExitCondition()) + changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::OptimizeSimpleLoop(NativeCodeProcedure * proc, bool full) +{ + if (!mVisited) + { + mVisited = true; + + assert(mLocked || mNumEntries == mEntryBlocks.Size()); + assert(mIns.Size() == 0 || mIns[0].mType != ASMIT_INV); + + bool changed = false; + int sz = mIns.Size(); + + assert(mBranch != ASMIT_JMP || mFalseJump == nullptr); + + CheckLive(); + if (mTrueJump) + mTrueJump->CheckLive(); + if (mFalseJump) + mFalseJump->CheckLive(); + +#if 1 + if (sz > 3 && + mIns[sz - 2].mType == ASMIT_LDA && mIns[sz - 2].mMode == ASMIM_IMMEDIATE && + mIns[sz - 1].mType == ASMIT_CMP && mIns[sz - 1].mMode == ASMIM_ZERO_PAGE && !(mIns[sz - 1].mLive & LIVE_CPU_REG_A)) + { + if (mBranch == ASMIT_BCS && mIns[sz - 2].mAddress < 0xff && !(mIns[sz - 1].mLive & LIVE_CPU_REG_Z)) + { + int val = mIns[sz - 2].mAddress + 1; + mBranch = ASMIT_BCC; + mIns[sz - 2].mMode = ASMIM_ZERO_PAGE; mIns[sz - 2].mAddress = mIns[sz - 1].mAddress; + mIns[sz - 1].mMode = ASMIM_IMMEDIATE; mIns[sz - 1].mAddress = val; + mIns[sz - 2].mLive |= mIns[sz - 1].mLive & LIVE_MEM; + } + else if (mBranch == ASMIT_BCC && mIns[sz - 2].mAddress < 0xff && !(mIns[sz - 1].mLive & LIVE_CPU_REG_Z)) + { + int val = mIns[sz - 2].mAddress + 1; + mBranch = ASMIT_BCS; + mIns[sz - 2].mMode = ASMIM_ZERO_PAGE; mIns[sz - 2].mAddress = mIns[sz - 1].mAddress; + mIns[sz - 1].mMode = ASMIM_IMMEDIATE; mIns[sz - 1].mAddress = val; + mIns[sz - 2].mLive |= mIns[sz - 1].mLive & LIVE_MEM; + } + else if ((mBranch == ASMIT_BEQ || mBranch == ASMIT_BNE) && !(mIns[sz - 1].mLive & LIVE_CPU_REG_C)) + { + int val = mIns[sz - 2].mAddress; + mIns[sz - 2].mMode = ASMIM_ZERO_PAGE; mIns[sz - 2].mAddress = mIns[sz - 1].mAddress; + mIns[sz - 1].mMode = ASMIM_IMMEDIATE; mIns[sz - 1].mAddress = val; + mIns[sz - 2].mLive |= mIns[sz - 1].mLive & LIVE_MEM; + } + } +#endif + if (mFalseJump == this) + { + mBranch = InvertBranchCondition(mBranch); + mFalseJump = mTrueJump; + mTrueJump = this; + } + + if (sz <= 3 && mTrueJump == this) + { + changed = OptimizeSimpleLoopInvariant(proc, nullptr, nullptr, full); + + CheckLive(); + } + else if (sz > 3 && sz < 200 && mNumEntries >= 2 && mTrueJump == this) + { + bool simple = true; + + for(int i=0; i= -1 && yinc <= 1) + { + yinc = 0; + yindex = true; + } + else + yother = true; + } + else if (!yindex && (mIns[i].mType == ASMIT_STY || mIns[i].mType == ASMIT_TYA || mIns[i].mMode == ASMIM_ABSOLUTE_Y || mIns[i].mMode == ASMIM_INDIRECT_Y)) + yother = true; + else if (mIns[i].mType != ASMIT_LDA && mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i].mAddress == zreg) + yother = true; + + if (mIns[i].mType == ASMIT_TAX) + xother = true; + else if (mIns[i].mType == ASMIT_INX) + xinc++; + else if (mIns[i].mType == ASMIT_DEX) + xinc--; + else if (mIns[i].mType == ASMIT_LDX) + { + if (mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i].mAddress == zreg && xinc >= -1 && xinc <= 1) + xindex = true; + else + xother = true; + } + else if (!xindex && (mIns[i].mType == ASMIT_STX || mIns[i].mType == ASMIT_TXA || mIns[i].mMode == ASMIM_ABSOLUTE_X || mIns[i].mMode == ASMIM_INDIRECT_X)) + xother = true; + else if (mIns[i].mType != ASMIT_LDA && mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i].mAddress == zreg) + xother = true; + } + if (yindex && yother) + { + for (int i = 0; i < sz - 3; i++) + { + if (mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i].mAddress == zreg) + ; + else if (mIns[i].mType == ASMIT_TAY && !(mIns[i].mLive & LIVE_CPU_REG_X) && mIns[i + 1].mMode == ASMIM_ABSOLUTE_Y && HasAsmInstructionMode(mIns[i + 1].mType, ASMIM_ABSOLUTE_X) && !(mIns[i + 1].mLive & LIVE_CPU_REG_Y)) + { + mIns[i].mType = ASMIT_TAX; mIns[i].mLive |= LIVE_CPU_REG_X; mIns[i].mLive &= ~LIVE_CPU_REG_Y; + mIns[i + 1].mMode = ASMIM_ABSOLUTE_X; + changed = true; + } + else if (mIns[i].mType == ASMIT_LDY && HasAsmInstructionMode(ASMIT_LDX, mIns[i].mMode) && !(mIns[i].mLive & LIVE_CPU_REG_X)) + { + int j = i + 1; + while (j < mIns.Size() && !mIns[j].ReferencesXReg() && !mIns[j].ReferencesYReg() && !mIns[j].ChangesGlobalMemory()) + j++; + if (j < mIns.Size() && mIns[j].mMode == ASMIM_ABSOLUTE_Y && HasAsmInstructionMode(mIns[j].mType, ASMIM_ABSOLUTE_X) && !(mIns[j].mLive & LIVE_CPU_REG_Y)) + { + mIns[i].mType = ASMIT_LDX; + mIns[j].mMode = ASMIM_ABSOLUTE_X; + while (j > i) + { + j--; + mIns[j].mLive |= LIVE_CPU_REG_X; mIns[j].mLive &= ~LIVE_CPU_REG_Y; + } + changed = true; + } + } + } + } + else if (xindex && xother) + { + bool xswap = true; + for (int i = 0; i < sz - 3; i++) + { + if (mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i].mAddress == zreg) + ; + else if (mIns[i].mType == ASMIT_TAX && !(mIns[i].mLive & LIVE_CPU_REG_Y) && mIns[i + 1].mMode == ASMIM_ABSOLUTE_X && HasAsmInstructionMode(mIns[i + 1].mType, ASMIM_ABSOLUTE_Y) && !(mIns[i + 1].mLive & LIVE_CPU_REG_X)) + ; + else if (mIns[i].mType == ASMIT_LDX && HasAsmInstructionMode(ASMIT_LDY, mIns[i].mMode) && !(mIns[i].mLive & LIVE_CPU_REG_Y)) + { + int j = i + 1; + while (j < mIns.Size() && !mIns[j].ReferencesXReg() && !mIns[j].ReferencesYReg() && !mIns[j].ChangesGlobalMemory()) + j++; + if (j < mIns.Size() && mIns[j].mMode == ASMIM_ABSOLUTE_X && HasAsmInstructionMode(mIns[j].mType, ASMIM_ABSOLUTE_Y) && !(mIns[j].mLive & LIVE_CPU_REG_X)) + ; + else + xswap = false; + } + else if (mIns[i].ChangesXReg()) + xswap = false; + } + + if (xswap) + { + for (int i = 0; i < sz - 3; i++) + { + if (mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i].mAddress == zreg) + ; + else if (mIns[i].mType == ASMIT_TAX && !(mIns[i].mLive & LIVE_CPU_REG_Y) && mIns[i + 1].mMode == ASMIM_ABSOLUTE_X && HasAsmInstructionMode(mIns[i + 1].mType, ASMIM_ABSOLUTE_Y) && !(mIns[i + 1].mLive & LIVE_CPU_REG_X)) + { + mIns[i].mType = ASMIT_TAY; mIns[i].mLive |= LIVE_CPU_REG_Y; mIns[i].mLive &= ~LIVE_CPU_REG_X; + mIns[i + 1].mMode = ASMIM_ABSOLUTE_Y; + changed = true; + } + else if (mIns[i].mType == ASMIT_LDX && HasAsmInstructionMode(ASMIT_LDY, mIns[i].mMode) && !(mIns[i].mLive & LIVE_CPU_REG_Y)) + { + int j = i + 1; + while (j < mIns.Size() && !mIns[j].ReferencesXReg() && !mIns[j].ReferencesYReg() && !mIns[j].ChangesGlobalMemory()) + j++; + if (j < mIns.Size() && mIns[j].mMode == ASMIM_ABSOLUTE_X && HasAsmInstructionMode(mIns[j].mType, ASMIM_ABSOLUTE_Y) && !(mIns[j].mLive & LIVE_CPU_REG_X)) + { + mIns[i].mType = ASMIT_LDY; + mIns[j].mMode = ASMIM_ABSOLUTE_Y; + while (j > i) + { + j--; + mIns[j].mLive |= LIVE_CPU_REG_Y; mIns[j].mLive &= ~LIVE_CPU_REG_X; + } + changed = true; + } + } + } + } + } + + if (!yother) + { + int linc = yinc; + if (mIns[sz - 3].mType == ASMIT_INC) + linc--; + else + linc++; + + const InterInstruction* iins(mIns[sz - 3].mIns); + + NativeCodeBasicBlock* lblock = proc->AllocateBlock(); + NativeCodeBasicBlock* eblock = proc->AllocateBlock(); + + yinc = 0; + for (int i = 0; i + 3 < sz; i++) + { + mIns[i].mLive |= LIVE_CPU_REG_Y; + + if (mIns[i].mType == ASMIT_LDA && mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i].mAddress == zreg) + lblock->mIns.Push(NativeCodeInstruction(mIns[i].mIns, ASMIT_TYA, ASMIM_IMPLIED)); + else if (mIns[i].mType == ASMIT_LDY) + { + if (yinc > 0) + lblock->mIns.Push(NativeCodeInstruction(mIns[i].mIns, ASMIT_DEY)); + else if (yinc < 0) + lblock->mIns.Push(NativeCodeInstruction(mIns[i].mIns, ASMIT_INY)); + yinc = 0; + } + else + { + lblock->mIns.Push(mIns[i]); + if (mIns[i].mType == ASMIT_INY) + yinc++; + else if (mIns[i].mType == ASMIT_DEY) + yinc--; + } + } + + while (linc < 0) + { + lblock->mIns.Push(NativeCodeInstruction(iins, ASMIT_INY, ASMIM_IMPLIED)); + linc++; + } + while (linc > 0) + { + lblock->mIns.Push(NativeCodeInstruction(iins, ASMIT_DEY, ASMIM_IMPLIED)); + linc--; + } + + lblock->mIns.Push(NativeCodeInstruction(iins, ASMIT_CPY, ASMIM_IMMEDIATE, limit)); + lblock->mBranch = mBranch; + lblock->mTrueJump = lblock; + lblock->mFalseJump = eblock; + + eblock->mIns.Push(NativeCodeInstruction(iins, ASMIT_STY, ASMIM_ZERO_PAGE, zreg)); + eblock->mBranch = ASMIT_JMP; + eblock->mTrueJump = mFalseJump; + eblock->mFalseJump = nullptr; + + lblock->mEntryRequiredRegs = mEntryRequiredRegs; + lblock->mExitRequiredRegs = mExitRequiredRegs; + eblock->mEntryRequiredRegs = mExitRequiredRegs; + eblock->mExitRequiredRegs = mExitRequiredRegs; + mExitRequiredRegs = mEntryRequiredRegs; + mExitRequiredRegs += CPU_REG_Y; + lblock->mEntryRequiredRegs += CPU_REG_Y; + lblock->mExitRequiredRegs += CPU_REG_Y; + eblock->mEntryRequiredRegs += CPU_REG_Y; + + mIns.SetSize(0); + mIns.Push(NativeCodeInstruction(iins, ASMIT_LDY, ASMIM_ZERO_PAGE, zreg)); + mBranch = ASMIT_JMP; + mTrueJump = lblock; + mFalseJump = nullptr; + + lblock->OptimizeSimpleLoopInvariant(proc, this, eblock, full); + + lblock->CheckLive(); + + changed = true; + + assert(mIns.Size() == 0 || mIns[0].mType != ASMIT_INV); + } + else if (!xother) + { + int linc = xinc; + if (mIns[sz - 3].mType == ASMIT_INC) + linc--; + else + linc++; + + const InterInstruction* iins(mIns[sz - 3].mIns); + + NativeCodeBasicBlock* lblock = proc->AllocateBlock(); + NativeCodeBasicBlock* eblock = proc->AllocateBlock(); + + xinc = 0; + for (int i = 0; i + 3 < sz; i++) + { + mIns[i].mLive |= LIVE_CPU_REG_X; + + if (mIns[i].mType == ASMIT_LDA && mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i].mAddress == zreg) + lblock->mIns.Push(NativeCodeInstruction(mIns[i].mIns, ASMIT_TXA, ASMIM_IMPLIED)); + else if (mIns[i].mType == ASMIT_LDX) + { + if (xinc > 0) + lblock->mIns.Push(NativeCodeInstruction(mIns[i].mIns, ASMIT_DEX)); + else if (xinc < 0) + lblock->mIns.Push(NativeCodeInstruction(mIns[i].mIns, ASMIT_INX)); + xinc = 0; + } + else + { + lblock->mIns.Push(mIns[i]); + if (mIns[i].mType == ASMIT_INX) + xinc++; + else if (mIns[i].mType == ASMIT_DEX) + xinc--; + } + } + + while (linc < 0) + { + lblock->mIns.Push(NativeCodeInstruction(iins, ASMIT_INX, ASMIM_IMPLIED)); + linc++; + } + while (linc > 0) + { + lblock->mIns.Push(NativeCodeInstruction(iins, ASMIT_DEX, ASMIM_IMPLIED)); + linc--; + } + + lblock->mIns.Push(NativeCodeInstruction(iins, ASMIT_CPX, ASMIM_IMMEDIATE, limit)); + lblock->mBranch = mBranch; + lblock->mTrueJump = lblock; + lblock->mFalseJump = eblock; + + eblock->mIns.Push(NativeCodeInstruction(iins, ASMIT_STX, ASMIM_ZERO_PAGE, zreg)); + eblock->mBranch = ASMIT_JMP; + eblock->mTrueJump = mFalseJump; + eblock->mFalseJump = nullptr; + + lblock->mEntryRequiredRegs = mEntryRequiredRegs; + lblock->mExitRequiredRegs = mExitRequiredRegs; + eblock->mEntryRequiredRegs = mExitRequiredRegs; + eblock->mExitRequiredRegs = mExitRequiredRegs; + mExitRequiredRegs = mEntryRequiredRegs; + mExitRequiredRegs += CPU_REG_X; + lblock->mEntryRequiredRegs += CPU_REG_X; + lblock->mExitRequiredRegs += CPU_REG_X; + eblock->mEntryRequiredRegs += CPU_REG_X; + + mIns.SetSize(0); + mIns.Push(NativeCodeInstruction(iins, ASMIT_LDX, ASMIM_ZERO_PAGE, zreg)); + mBranch = ASMIT_JMP; + mTrueJump = lblock; + mFalseJump = nullptr; + + lblock->CheckLive(); + + lblock->OptimizeSimpleLoopInvariant(proc, this, eblock, full); + + lblock->CheckLive(); + + changed = true; + + assert(mIns.Size() == 0 || mIns[0].mType != ASMIT_INV); + } + } + else if (mIns[sz - 1].mType == ASMIT_DEC && mIns[sz - 1].mMode == ASMIM_ZERO_PAGE && (mBranch == ASMIT_BNE || mBranch == ASMIT_BPL)) + { + // check for usage of Y register + + bool yother = false, yindex = false, xother = false, xindex = false; + int zreg = mIns[sz - 1].mAddress; + int yinc = 0, xinc = 0; + + const InterInstruction* iins(mIns[sz - 1].mIns); + + if (mIns[sz - 1].mLive & LIVE_CPU_REG_Y) + yother = true; + if (mIns[sz - 1].mLive & LIVE_CPU_REG_X) + xother = true; + + for (int i = 0; i < sz - 1; i++) + { + if (mIns[i].mType == ASMIT_TAY) + yother = true; + else if (mIns[i].mType == ASMIT_INY) + yinc++; + else if (mIns[i].mType == ASMIT_DEY) + yinc--; + else if (mIns[i].mType == ASMIT_LDY) + { + if (mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i].mAddress == zreg && yinc >= -1 && yinc <= 1) + { + yinc = 0; + yindex = true; + } + else + yother = true; + } + else if (!yindex && (mIns[i].mType == ASMIT_STY || mIns[i].mType == ASMIT_TYA || mIns[i].mMode == ASMIM_ABSOLUTE_Y || mIns[i].mMode == ASMIM_INDIRECT_Y)) + yother = true; + else if (mIns[i].mType != ASMIT_LDA && mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i].mAddress == zreg) + yother = true; + + if (mIns[i].mType == ASMIT_TAX) + xother = true; + else if (mIns[i].mType == ASMIT_INX) + xinc++; + else if (mIns[i].mType == ASMIT_DEX) + xinc--; + else if (mIns[i].mType == ASMIT_LDX) + { + if (mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i].mAddress == zreg && xinc >= -1 && xinc <= 1) + { + xinc = 0; + xindex = true; + } + else + xother = true; + } + else if (!xindex && (mIns[i].mType == ASMIT_STX || mIns[i].mType == ASMIT_TXA || mIns[i].mMode == ASMIM_ABSOLUTE_X)) + xother = true; + else if (mIns[i].mType != ASMIT_LDA && mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i].mAddress == zreg) + xother = true; + } + + if (!yother) + { + int linc = yinc + 1; + + NativeCodeBasicBlock* lblock = proc->AllocateBlock(); + NativeCodeBasicBlock* eblock = proc->AllocateBlock(); + + yinc = 0; + for (int i = 0; i + 1 < sz; i++) + { + mIns[i].mLive |= LIVE_CPU_REG_Y; + + if (mIns[i].mType == ASMIT_LDA && mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i].mAddress == zreg) + lblock->mIns.Push(NativeCodeInstruction(mIns[i].mIns, ASMIT_TYA, ASMIM_IMPLIED)); + else if (mIns[i].mType == ASMIT_LDY) + { + if (yinc > 0) + lblock->mIns.Push(NativeCodeInstruction(mIns[i].mIns, ASMIT_DEY)); + else if (yinc < 0) + lblock->mIns.Push(NativeCodeInstruction(mIns[i].mIns, ASMIT_INY)); + yinc = 0; + } + else + { + lblock->mIns.Push(mIns[i]); + if (mIns[i].mType == ASMIT_INY) + yinc++; + else if (mIns[i].mType == ASMIT_DEY) + yinc--; + } + } + + if (linc == 0) + { + lblock->mIns.Push(NativeCodeInstruction(iins, ASMIT_CPY, ASMIM_IMMEDIATE, 0)); + } + else + { + while (linc < 0) + { + lblock->mIns.Push(NativeCodeInstruction(iins, ASMIT_INY, ASMIM_IMPLIED)); + linc++; + } + while (linc > 0) + { + lblock->mIns.Push(NativeCodeInstruction(iins, ASMIT_DEY, ASMIM_IMPLIED)); + linc--; + } + } + + lblock->mBranch = mBranch; + lblock->mTrueJump = lblock; + lblock->mFalseJump = eblock; + + eblock->mIns.Push(NativeCodeInstruction(iins, ASMIT_STY, ASMIM_ZERO_PAGE, zreg)); + eblock->mBranch = ASMIT_JMP; + eblock->mTrueJump = mFalseJump; + eblock->mFalseJump = nullptr; + + lblock->mEntryRequiredRegs = mEntryRequiredRegs; + lblock->mExitRequiredRegs = mExitRequiredRegs; + eblock->mEntryRequiredRegs = mExitRequiredRegs; + eblock->mExitRequiredRegs = mExitRequiredRegs; + mExitRequiredRegs = mEntryRequiredRegs; + mExitRequiredRegs += CPU_REG_Y; + lblock->mEntryRequiredRegs += CPU_REG_Y; + lblock->mExitRequiredRegs += CPU_REG_Y; + eblock->mEntryRequiredRegs += CPU_REG_Y; + + mIns.SetSize(0); + mIns.Push(NativeCodeInstruction(iins, ASMIT_LDY, ASMIM_ZERO_PAGE, zreg)); + mBranch = ASMIT_JMP; + mTrueJump = lblock; + mFalseJump = nullptr; + + lblock->OptimizeSimpleLoopInvariant(proc, this, eblock, full); + + lblock->CheckLive(); + + changed = true; + + assert(mIns.Size() == 0 || mIns[0].mType != ASMIT_INV); + } + else if (!xother) + { + int linc = xinc + 1; + + NativeCodeBasicBlock* lblock = proc->AllocateBlock(); + NativeCodeBasicBlock* eblock = proc->AllocateBlock(); + + yinc = 0; + for (int i = 0; i + 1 < sz; i++) + { + mIns[i].mLive |= LIVE_CPU_REG_X; + + if (mIns[i].mType == ASMIT_LDA && mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i].mAddress == zreg) + lblock->mIns.Push(NativeCodeInstruction(mIns[i].mIns, ASMIT_TXA, ASMIM_IMPLIED)); + else if (mIns[i].mType == ASMIT_LDX) + { + if (yinc > 0) + lblock->mIns.Push(NativeCodeInstruction(mIns[i].mIns, ASMIT_DEX)); + else if (yinc < 0) + lblock->mIns.Push(NativeCodeInstruction(mIns[i].mIns, ASMIT_INX)); + yinc = 0; + } + else + { + lblock->mIns.Push(mIns[i]); + if (mIns[i].mType == ASMIT_INX) + yinc++; + else if (mIns[i].mType == ASMIT_DEX) + yinc--; + } + } + + if (linc == 0) + { + lblock->mIns.Push(NativeCodeInstruction(iins, ASMIT_CPX, ASMIM_IMMEDIATE, 0)); + } + else + { + while (linc < 0) + { + lblock->mIns.Push(NativeCodeInstruction(iins, ASMIT_INX, ASMIM_IMPLIED)); + linc++; + } + while (linc > 0) + { + lblock->mIns.Push(NativeCodeInstruction(iins, ASMIT_DEX, ASMIM_IMPLIED)); + linc--; + } + } + + lblock->mBranch = mBranch; + lblock->mTrueJump = lblock; + lblock->mFalseJump = eblock; + + eblock->mIns.Push(NativeCodeInstruction(iins, ASMIT_STX, ASMIM_ZERO_PAGE, zreg)); + eblock->mBranch = ASMIT_JMP; + eblock->mTrueJump = mFalseJump; + eblock->mFalseJump = nullptr; + + lblock->mEntryRequiredRegs = mEntryRequiredRegs; + lblock->mExitRequiredRegs = mExitRequiredRegs; + eblock->mEntryRequiredRegs = mExitRequiredRegs; + eblock->mExitRequiredRegs = mExitRequiredRegs; + mExitRequiredRegs = mEntryRequiredRegs; + mExitRequiredRegs += CPU_REG_X; + lblock->mEntryRequiredRegs += CPU_REG_X; + lblock->mExitRequiredRegs += CPU_REG_X; + eblock->mEntryRequiredRegs += CPU_REG_X; + + mIns.SetSize(0); + mIns.Push(NativeCodeInstruction(iins, ASMIT_LDX, ASMIM_ZERO_PAGE, zreg)); + mBranch = ASMIT_JMP; + mTrueJump = lblock; + mFalseJump = nullptr; + + lblock->OptimizeSimpleLoopInvariant(proc, this, eblock, full); + + lblock->CheckLive(); + + changed = true; + + assert(mIns.Size() == 0 || mIns[0].mType != ASMIT_INV); + } + } + else if (mIns[sz - 3].mType == ASMIT_INC && mIns[sz - 3].mMode == ASMIM_ZERO_PAGE && + mIns[sz - 2].mType == ASMIT_LDA && mIns[sz - 2].mMode == ASMIM_ZERO_PAGE && mIns[sz - 3].mAddress == mIns[sz - 2].mAddress && + mIns[sz - 1].mType == ASMIT_CMP && (mIns[sz - 1].mMode == ASMIM_ZERO_PAGE || mIns[sz - 1].mMode == ASMIM_ABSOLUTE) && !(mIns[sz - 1].mLive & LIVE_CPU_REG_A) && + mBranch == ASMIT_BCC) + { + // check for usage of Y register + + bool yother = false, yindex = false, lchanged = false, xother = false, xindex = false; + NativeCodeInstruction lins = mIns[sz - 1]; + + const InterInstruction* iins(mIns[sz - 1].mIns); + + int zreg = mIns[sz - 3].mAddress; + + if (mIns[sz - 1].mLive & LIVE_CPU_REG_X) + xother = true; + if (mIns[sz - 1].mLive & LIVE_CPU_REG_Y) + yother = true; + + for (int i = 0; i < sz - 3; i++) + { + if (mIns[i].SameEffectiveAddress(lins) && mIns[i].ChangesAddress()) + lchanged = true; + + if (mIns[i].mType == ASMIT_INY || mIns[i].mType == ASMIT_DEY || mIns[i].mType == ASMIT_TAY) + yother = true; + else if (mIns[i].mType == ASMIT_LDY) + { + if (mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i].mAddress == zreg) + yindex = true; + else + yother = true; + } + else if (!yindex && (mIns[i].mType == ASMIT_STY || mIns[i].mType == ASMIT_TYA || mIns[i].mMode == ASMIM_ABSOLUTE_Y || mIns[i].mMode == ASMIM_INDIRECT_Y)) + yother = true; + else if (mIns[i].mType != ASMIT_LDA && mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i].mAddress == zreg) + yother = true; + + if (mIns[i].mType == ASMIT_INX || mIns[i].mType == ASMIT_DEX || mIns[i].mType == ASMIT_TAX) + xother = true; + else if (mIns[i].mType == ASMIT_LDX) + { + if (mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i].mAddress == zreg) + xindex = true; + else + xother = true; + } + else if (mIns[i].mType == ASMIT_LDY && mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i].mAddress == zreg) + xother = true; + else if (!xindex && (mIns[i].mType == ASMIT_STX || mIns[i].mType == ASMIT_TXA || mIns[i].mMode == ASMIM_ABSOLUTE_X || mIns[i].mMode == ASMIM_INDIRECT_X)) + xother = true; + else if (mIns[i].mType != ASMIT_LDA && mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i].mAddress == zreg) + xother = true; + } + + if (!yother && !lchanged) + { + NativeCodeBasicBlock* lblock = proc->AllocateBlock(); + NativeCodeBasicBlock* eblock = proc->AllocateBlock(); + for (int i = 0; i + 3 < sz; i++) + { + mIns[i].mLive |= LIVE_CPU_REG_Y; + + if (mIns[i].mType == ASMIT_LDA && mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i].mAddress == zreg) + lblock->mIns.Push(NativeCodeInstruction(mIns[i].mIns, ASMIT_TYA, ASMIM_IMPLIED)); + else if (mIns[i].mType != ASMIT_LDY) + lblock->mIns.Push(mIns[i]); + } + lblock->mIns.Push(NativeCodeInstruction(iins, ASMIT_INY, ASMIM_IMPLIED)); + lblock->mIns.Push(NativeCodeInstruction(iins, ASMIT_CPY, lins)); + lblock->mBranch = mBranch; + lblock->mTrueJump = lblock; + lblock->mFalseJump = eblock; + + eblock->mIns.Push(NativeCodeInstruction(iins, ASMIT_STY, ASMIM_ZERO_PAGE, zreg)); + eblock->mBranch = ASMIT_JMP; + eblock->mTrueJump = mFalseJump; + eblock->mFalseJump = nullptr; + + lblock->mEntryRequiredRegs = mEntryRequiredRegs; + lblock->mExitRequiredRegs = mExitRequiredRegs; + eblock->mEntryRequiredRegs = mExitRequiredRegs; + eblock->mExitRequiredRegs = mExitRequiredRegs; + mExitRequiredRegs = mEntryRequiredRegs; + mExitRequiredRegs += CPU_REG_Y; + lblock->mEntryRequiredRegs += CPU_REG_Y; + lblock->mExitRequiredRegs += CPU_REG_Y; + eblock->mEntryRequiredRegs += CPU_REG_Y; + + mIns.SetSize(0); + mIns.Push(NativeCodeInstruction(iins, ASMIT_LDY, ASMIM_ZERO_PAGE, zreg)); + mBranch = ASMIT_JMP; + mTrueJump = lblock; + mFalseJump = nullptr; + + lblock->OptimizeSimpleLoopInvariant(proc, this, eblock, full); + + lblock->CheckLive(); + + changed = true; + + assert(mIns.Size() == 0 || mIns[0].mType != ASMIT_INV); + } + else if (!xother && !lchanged) + { + NativeCodeBasicBlock* lblock = proc->AllocateBlock(); + NativeCodeBasicBlock* eblock = proc->AllocateBlock(); + for (int i = 0; i + 3 < sz; i++) + { + mIns[i].mLive |= LIVE_CPU_REG_X; + + if (mIns[i].mType == ASMIT_LDA && mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i].mAddress == zreg) + lblock->mIns.Push(NativeCodeInstruction(mIns[i].mIns, ASMIT_TXA, ASMIM_IMPLIED)); + else if (mIns[i].mType != ASMIT_LDX) + lblock->mIns.Push(mIns[i]); + } + lblock->mIns.Push(NativeCodeInstruction(iins, ASMIT_INX, ASMIM_IMPLIED)); + lblock->mIns.Push(NativeCodeInstruction(iins, ASMIT_CPX, lins)); + lblock->mBranch = mBranch; + lblock->mTrueJump = lblock; + lblock->mFalseJump = eblock; + + eblock->mIns.Push(NativeCodeInstruction(iins, ASMIT_STX, ASMIM_ZERO_PAGE, zreg)); + eblock->mBranch = ASMIT_JMP; + eblock->mTrueJump = mFalseJump; + eblock->mFalseJump = nullptr; + + mIns.SetSize(0); + mIns.Push(NativeCodeInstruction(iins, ASMIT_LDX, ASMIM_ZERO_PAGE, zreg)); + mBranch = ASMIT_JMP; + mTrueJump = lblock; + mFalseJump = nullptr; + + lblock->mEntryRequiredRegs = mEntryRequiredRegs; + lblock->mExitRequiredRegs = mExitRequiredRegs; + eblock->mEntryRequiredRegs = mExitRequiredRegs; + eblock->mExitRequiredRegs = mExitRequiredRegs; + mExitRequiredRegs = mEntryRequiredRegs; + mExitRequiredRegs += CPU_REG_X; + lblock->mEntryRequiredRegs += CPU_REG_X; + lblock->mExitRequiredRegs += CPU_REG_X; + eblock->mEntryRequiredRegs += CPU_REG_X; + + lblock->OptimizeSimpleLoopInvariant(proc, this, eblock, full); + + lblock->CheckLive(); + + changed = true; + + assert(mIns.Size() == 0 || mIns[0].mType != ASMIT_INV); + } + } + } + +#if 1 + assert(mBranch != ASMIT_JMP || mFalseJump == nullptr); + if (!changed) + { + CheckLive(); + changed = OptimizeSimpleLoopInvariant(proc, nullptr, nullptr, full); + CheckLive(); + } + assert(mBranch != ASMIT_JMP || mFalseJump == nullptr); +#endif + } + + assert(mBranch != ASMIT_JMP || mFalseJump == nullptr); + + CheckLive(); + + if (mTrueJump && mTrueJump->OptimizeSimpleLoop(proc, full)) + changed = true; + if (mFalseJump && mFalseJump->OptimizeSimpleLoop(proc, full)) + changed = true; + + assert(mIns.Size() == 0 || mIns[0].mType != ASMIT_INV); + + return changed; + } + + return false; +} + +static bool NativeBlocksArrayEntryRequiresReg(const ExpandingArray& blocks, int reg) +{ + for (int i = 0; i < blocks.Size(); i++) + if (blocks[i]->mEntryRequiredRegs[reg]) + return true; + return false; +} + +bool NativeCodeBasicBlock::OptimizeSingleEntryLoopInvariant(NativeCodeProcedure* proc, NativeCodeBasicBlock* prev, const ExpandingArray& tails, const ExpandingArray& lblocks) +{ + bool changed = false; + + for (int bi = 0; bi < lblocks.Size(); bi++) + { + NativeCodeBasicBlock* block = lblocks[bi]; + for (int i = 0; i + 1 < block->mIns.Size(); i++) + { + if (block->mIns[i].mType == ASMIT_LDA && (block->mIns[i].mMode == ASMIM_IMMEDIATE || block->mIns[i].mMode == ASMIM_IMMEDIATE_ADDRESS) && + block->mIns[i + 1].mType == ASMIT_STA && block->mIns[i + 1].mMode == ASMIM_ZERO_PAGE) + { + int addr = block->mIns[i + 1].mAddress; + if (!mEntryRequiredRegs[addr] && !NativeBlocksArrayEntryRequiresReg(tails, addr) && !ReferencesZeroPage(addr) && + !block->ReferencesZeroPage(addr, 0, i) && !block->ChangesZeroPage(addr, i + 2) && !prev->mExitRequiredRegs[CPU_REG_Z]) + { + int j = 0; + while (j < lblocks.Size() && (j == bi || !lblocks[j]->ReferencesZeroPage(addr))) + j++; + if (j == lblocks.Size()) + { + bool ok = true; + if (!prev->mExitRequiredRegs[CPU_REG_A]) + { + prev->mIns.Push(block->mIns[i]); + prev->mIns.Push(block->mIns[i + 1]); + } + else if (!prev->mExitRequiredRegs[CPU_REG_X]) + { + prev->mIns.Push(NativeCodeInstruction(block->mIns[i].mIns, ASMIT_LDX, block->mIns[i])); + prev->mIns.Push(NativeCodeInstruction(block->mIns[i + 1].mIns, ASMIT_STX, block->mIns[i + 1])); + } + else if (!prev->mExitRequiredRegs[CPU_REG_Y]) + { + prev->mIns.Push(NativeCodeInstruction(block->mIns[i].mIns, ASMIT_LDY, block->mIns[i])); + prev->mIns.Push(NativeCodeInstruction(block->mIns[i + 1].mIns, ASMIT_STY, block->mIns[i + 1])); + } + else + ok = false; + + if (ok) + { + block->mIns.Remove(i + 1); + changed = true; + + prev->mExitRequiredRegs += addr; + mEntryRequiredRegs += addr; + mExitRequiredRegs += addr; + + for (int i = 0; i < lblocks.Size(); i++) + { + lblocks[i]->mEntryRequiredRegs += addr; + lblocks[i]->mExitRequiredRegs += addr; + } + } + } + } + } + } + } + + if (!changed) + { + for (int i = 0; i + 1 < mIns.Size(); i++) + { + if (mIns[i].mType == ASMIT_LDA && mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i + 1].mType == ASMIT_STA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE) + { + if (!ReferencesZeroPage(mIns[i + 1].mAddress, 0, i) && !ChangesZeroPage(mIns[i].mAddress) && !ChangesZeroPage(mIns[i + 1].mAddress, i + 2)) + { + int j = 0; + while (j < lblocks.Size() && !lblocks[j]->ChangesZeroPage(mIns[i].mAddress) && !lblocks[j]->ChangesZeroPage(mIns[i + 1].mAddress)) + j++; + if (j == lblocks.Size()) + { + int addr = mIns[i + 1].mAddress; + + bool ok = true; + if (!prev->mExitRequiredRegs[CPU_REG_A]) + { + prev->mIns.Push(mIns[i]); + prev->mIns.Push(mIns[i + 1]); + } + else if (!prev->mExitRequiredRegs[CPU_REG_X]) + { + prev->mIns.Push(NativeCodeInstruction(mIns[i].mIns, ASMIT_LDX, mIns[i])); + prev->mIns.Push(NativeCodeInstruction(mIns[i + 1].mIns, ASMIT_STX, mIns[i + 1])); + } + else if (!prev->mExitRequiredRegs[CPU_REG_Y]) + { + prev->mIns.Push(NativeCodeInstruction(mIns[i].mIns, ASMIT_LDY, mIns[i])); + prev->mIns.Push(NativeCodeInstruction(mIns[i + 1].mIns, ASMIT_STY, mIns[i + 1])); + } + else + ok = false; + + if (ok) + { + mIns[i + 1].mType = ASMIT_LDA; + mIns.Remove(i); + + prev->mExitRequiredRegs += addr; + mEntryRequiredRegs += addr; + mExitRequiredRegs += addr; + + for (int i = 0; i < lblocks.Size(); i++) + { + lblocks[i]->mEntryRequiredRegs += addr; + lblocks[i]->mExitRequiredRegs += addr; + } + changed = true; + } + } + } + } + + if (!changed && mIns[i].mType == ASMIT_LDA && mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i + 1].mType == ASMIT_STA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE) + { + if (!ReferencesZeroPage(mIns[i + 1].mAddress, 0, i) && !ChangesZeroPage(mIns[i].mAddress, 0, i)) + { + int reg0 = mIns[i].mAddress, reg1 = mIns[i + 1].mAddress; + bool same = true; + + for (int j = 0; j < mEntryBlocks.Size(); j++) + { + NativeCodeBasicBlock* eb = mEntryBlocks[j]; + if (eb != prev) + { + if (!eb->IsSameZPValue(reg0, reg1)) + { + same = false; + break; + } + } + } + + if (same) + { + prev->mIns.Push(mIns[i]); + prev->mIns.Push(mIns[i + 1]); + prev->mExitRequiredRegs += reg1; + mEntryRequiredRegs += reg1; + mIns.Remove(i + 1); + changed = true; + } + } + } + + if (mIns[i].mType == ASMIT_LDA && (mIns[i].mMode == ASMIM_IMMEDIATE || mIns[i].mMode == ASMIM_IMMEDIATE_ADDRESS) && mIns[i + 1].mType == ASMIT_STA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE) + { + int addr = mIns[i + 1].mAddress; + + if (tails.Size() == 1 && !ReferencesZeroPage(addr, 0, i) && !ChangesZeroPage(addr, i + 2)) + { + int j = 0; + while (j < lblocks.Size() && (lblocks[j] == tails[0] || lblocks[j] == this || !lblocks[j]->ChangesZeroPage(addr))) + j++; + if (j == lblocks.Size()) + { + NativeCodeBasicBlock* tail = tails[0]; + + int tz = tail->mIns.Size() - 1; + while (tz >= 0 && !tail->mIns[tz].ReferencesZeroPage(addr)) + tz--; + + if (tz >= 0 && !tail->ChangesZeroPage(addr, 0, tz - 1)) + { + if (tail->mIns[tz].mType == ASMIT_STA && !tail->ChangesAccu(tz + 1) || + tail->mIns[tz].mType == ASMIT_STY && !tail->ChangesYReg(tz + 1) || + tail->mIns[tz].mType == ASMIT_STX && !tail->ChangesXReg(tz + 1)) + { + bool ok = true; + if (!prev->mExitRequiredRegs[CPU_REG_A]) + { + prev->mIns.Push(mIns[i]); + prev->mIns.Push(mIns[i + 1]); + } + else if (!prev->mExitRequiredRegs[CPU_REG_X]) + { + prev->mIns.Push(NativeCodeInstruction(mIns[i].mIns, ASMIT_LDX, mIns[i])); + prev->mIns.Push(NativeCodeInstruction(mIns[i + 1].mIns, ASMIT_STX, mIns[i + 1])); + } + else if (!prev->mExitRequiredRegs[CPU_REG_Y]) + { + prev->mIns.Push(NativeCodeInstruction(mIns[i].mIns, ASMIT_LDY, mIns[i])); + prev->mIns.Push(NativeCodeInstruction(mIns[i + 1].mIns, ASMIT_STY, mIns[i + 1])); + } + else + ok = false; + + if (ok) + { + NativeCodeBasicBlock* exitBlock; + if (lblocks.Contains(tail->mTrueJump)) + exitBlock = tail->mFalseJump; + else + exitBlock = tail->mTrueJump; + + if (exitBlock->mEntryBlocks.Size() > 1) + { + NativeCodeBasicBlock* eb = mProc->AllocateBlock(); + eb->mEntryRequiredRegs = exitBlock->mEntryRequiredRegs; + eb->mExitRequiredRegs = exitBlock->mEntryRequiredRegs; + eb->Close(exitBlock->mBranchIns, exitBlock, nullptr, ASMIT_JMP); + eb->AddEntryBlock(tail); + exitBlock->AddEntryBlock(eb); + exitBlock->RemEntryBlock(tail); + if (tail->mFalseJump == exitBlock) + tail->mFalseJump = eb; + else + tail->mTrueJump = eb; + exitBlock = eb; + } + + mIns.Remove(i + 1); + + prev->mExitRequiredRegs += addr; + mEntryRequiredRegs += addr; + mExitRequiredRegs += addr; + + for (int i = 0; i < lblocks.Size(); i++) + { + lblocks[i]->mEntryRequiredRegs += addr; + lblocks[i]->mExitRequiredRegs += addr; + } + + int live = 0; + if (tail->mIns[tz].mType == ASMIT_STA) + { + live |= LIVE_CPU_REG_A; + tail->mExitRequiredRegs += CPU_REG_A; + exitBlock->mEntryRequiredRegs += CPU_REG_A; + } + else if (tail->mIns[tz].mType == ASMIT_STX) + { + live |= LIVE_CPU_REG_X; + tail->mExitRequiredRegs += CPU_REG_X; + exitBlock->mEntryRequiredRegs += CPU_REG_X; + } + else if (tail->mIns[tz].mType == ASMIT_STY) + { + live |= LIVE_CPU_REG_A; + tail->mExitRequiredRegs += CPU_REG_Y; + exitBlock->mEntryRequiredRegs += CPU_REG_Y; + } + + exitBlock->mIns.Insert(0, tail->mIns[tz]); + tail->mIns.Remove(tz); + + changed = true; + } + } + } + } + } + } + } + + } + + if (!changed) + { + if (mIns.Size() > 0 && mIns[0].mType == ASMIT_LDA && mIns[0].mMode == ASMIM_IMMEDIATE) + { + if (!ChangesAccu(1)) + { + int i = 0; + while (i < lblocks.Size() && !lblocks[i]->ChangesAccu()) + i++; + if (i == lblocks.Size()) + { + prev->mIns.Push(mIns[0]); + mIns.Remove(0); + prev->mExitRequiredRegs += CPU_REG_A; + mEntryRequiredRegs += CPU_REG_A; + mExitRequiredRegs += CPU_REG_A; + + for (int i = 0; i < lblocks.Size(); i++) + { + lblocks[i]->mEntryRequiredRegs += CPU_REG_A; + lblocks[i]->mExitRequiredRegs += CPU_REG_A; + } + changed = true; + } + } + } + if (mIns.Size() > 0 && mIns[0].mType == ASMIT_LDY && mIns[0].mMode == ASMIM_IMMEDIATE) + { + if (!ChangesYReg(1)) + { + int i = 0; + while (i < lblocks.Size() && !lblocks[i]->ChangesYReg()) + i++; + if (i == lblocks.Size()) + { + prev->mIns.Push(mIns[0]); + mIns.Remove(0); + prev->mExitRequiredRegs += CPU_REG_Y; + mEntryRequiredRegs += CPU_REG_Y; + mExitRequiredRegs += CPU_REG_Y; + + for (int i = 0; i < lblocks.Size(); i++) + { + lblocks[i]->mEntryRequiredRegs += CPU_REG_Y; + lblocks[i]->mExitRequiredRegs += CPU_REG_Y; + } + changed = true; + } + } + } + + if (tails.Size() == 1) + { + NativeCodeBasicBlock* tail = tails[0]; + int tz = tail->mIns.Size(); + if (mIns.Size() > 0 && mIns[0].mType == ASMIT_LDY && mIns[0].mMode == ASMIM_ZERO_PAGE && + tz > 0 && (tail->mIns[tz - 1].mType == ASMIT_INC || tail->mIns[tz - 1].mType == ASMIT_DEC) && tail->mIns[tz - 1].SameEffectiveAddress(mIns[0])) + { + if (!ChangesYReg(1) && !ReferencesZeroPage(mIns[0].mAddress, 1) && + !tail->ChangesYReg() && !tail->ReferencesZeroPage(mIns[0].mAddress, 0, tz - 1)) + { + int i = 0; + while (i < lblocks.Size() && (lblocks[i] == tail || (!lblocks[i]->ChangesYReg() && !lblocks[i]->ReferencesZeroPage(mIns[0].mAddress)))) + i++; + if (i == lblocks.Size()) + { + if (lblocks.Contains(tail->mTrueJump)) + { + tail->mFalseJump->mEntryRequiredRegs += CPU_REG_Y; + tail->mFalseJump->mIns.Insert(0, NativeCodeInstruction(mIns[0].mIns, ASMIT_STY, mIns[0])); + } + else + { + tail->mTrueJump->mEntryRequiredRegs += CPU_REG_Y; + tail->mTrueJump->mIns.Insert(0, NativeCodeInstruction(mIns[0].mIns, ASMIT_STY, mIns[0])); + } + + if (tail->mIns[tz - 1].mType == ASMIT_INC) + tail->mIns[tz - 1].mType = ASMIT_INY; + else + tail->mIns[tz - 1].mType = ASMIT_DEY; + tail->mIns[tz - 1].mMode = ASMIM_IMPLIED; + + prev->mIns.Push(mIns[0]); + mIns.Remove(0); + mEntryRequiredRegs += CPU_REG_Y; + mExitRequiredRegs += CPU_REG_Y; + prev->mExitRequiredRegs += CPU_REG_Y; + for (int i = 0; i < lblocks.Size(); i++) + { + lblocks[i]->mEntryRequiredRegs += CPU_REG_Y; + lblocks[i]->mExitRequiredRegs += CPU_REG_Y; + } + changed = true; + } + } + } + } + + if (!changed) + { + if (mIns.Size() > 0 && mIns[0].mType == ASMIT_TXA) + { + if (!ChangesAccu(1) && !ChangesXReg(1)) + { + int i = 0; + while (i < lblocks.Size() && (lblocks[i] == this || (!lblocks[i]->ChangesXReg() && !lblocks[i]->ChangesAccu()))) + i++; + if (i == lblocks.Size()) + { + prev->mIns.Push(mIns[0]); + mIns.Remove(0); + prev->mExitRequiredRegs += CPU_REG_A; + mEntryRequiredRegs += CPU_REG_A; + mExitRequiredRegs += CPU_REG_A; + + for (int i = 0; i < lblocks.Size(); i++) + { + lblocks[i]->mEntryRequiredRegs += CPU_REG_A; + lblocks[i]->mExitRequiredRegs += CPU_REG_A; + } + changed = true; + } + } + } + } + + if (!changed) + { + if (tails.Size() == 1 && mIns.Size() > 0 && mIns[0].mType == ASMIT_LDY && mIns[0].mMode == ASMIM_ZERO_PAGE) + { + NativeCodeBasicBlock* tail = tails[0]; + int k = 1; + while (k < mIns.Size() && !(mIns[k].mMode == ASMIM_ZERO_PAGE && mIns[k].mAddress == mIns[0].mAddress)) + k++; + + if (k < mIns.Size() && !ChangesYReg(1) && + !ReferencesZeroPage(mIns[0].mAddress, 1, k) && + !ReferencesZeroPage(mIns[0].mAddress, k + 1) && + (mIns[k].mType == ASMIT_INC || mIns[k].mType == ASMIT_DEC)) + { + int i = 0; + while (i < lblocks.Size() && (lblocks[i] == this || (!lblocks[i]->ChangesYReg() && !lblocks[i]->ReferencesZeroPage(mIns[0].mAddress)))) + i++; + if (i == lblocks.Size()) + { + AsmInsType it, nit; + + if (mIns[k].mType == ASMIT_INC) + { + it = ASMIT_INY; + nit = ASMIT_DEY; + } + else + { + it = ASMIT_DEY; + nit = ASMIT_INY; + } + + if (lblocks.Contains(tail->mTrueJump)) + { + tail->mFalseJump->mEntryRequiredRegs += CPU_REG_Y; + tail->mFalseJump->mIns.Insert(0, NativeCodeInstruction(mIns[0].mIns, it)); + tail->mFalseJump->mIns.Insert(1, NativeCodeInstruction(mIns[0].mIns, ASMIT_STY, mIns[0])); + } + else + { + tail->mTrueJump->mEntryRequiredRegs += CPU_REG_Y; + tail->mTrueJump->mIns.Insert(0, NativeCodeInstruction(mIns[0].mIns, it)); + tail->mTrueJump->mIns.Insert(1, NativeCodeInstruction(mIns[0].mIns, ASMIT_STY, mIns[0])); + } + + prev->mIns.Push(mIns[0]); + prev->mIns.Push(NativeCodeInstruction(mIns[0].mIns, nit)); + + mIns[0].mType = it; + mIns[0].mMode = ASMIM_IMPLIED; + mIns.Remove(k); + + mEntryRequiredRegs += CPU_REG_Y; + mExitRequiredRegs += CPU_REG_Y; + prev->mExitRequiredRegs += CPU_REG_Y; + for (int i = 0; i < lblocks.Size(); i++) + { + lblocks[i]->mEntryRequiredRegs += CPU_REG_Y; + lblocks[i]->mExitRequiredRegs += CPU_REG_Y; + } + changed = true; + } + } + } + } + } + + if (mEntryBlocks.Size() == 2 && tails.Size() == 1) + { + NativeCodeBasicBlock* tail = tails[0]; + if (tail->mExitRequiredRegs.Size() && !tail->mExitRequiredRegs[CPU_REG_X] && !mEntryRequiredRegs[CPU_REG_X]) + { + int ei = tail->mIns.Size() - 1; + while (ei >= 0 && !tail->mIns[ei].ReferencesXReg()) + ei--; + if (ei >= 0 && tail->mIns[ei].mType == ASMIT_STX && tail->mIns[ei].mMode == ASMIM_ZERO_PAGE) + { + int reg = tail->mIns[ei].mAddress; + + int si = 0; + while (si < mIns.Size() && !mIns[si].ChangesXReg()) + si++; + if (si < mIns.Size() && mIns[si].mType == ASMIT_LDX && mIns[si].SameEffectiveAddress(tail->mIns[ei])) + { + if (!tail->ReferencesZeroPage(reg, ei + 1) && !ReferencesZeroPage(reg, 0, si) && !ReferencesZeroPage(reg, si + 1)) + tail->mIns.Remove(ei); + + prev->mIns.Push(mIns[si]); + mIns.Remove(si); + + for (int i = 0; i < si; i++) + mIns[i].mLive |= LIVE_CPU_REG_X; + for (int i = ei; i < tail->mIns.Size(); i++) + tail->mIns[i].mLive |= LIVE_CPU_REG_X; + mEntryRequiredRegs += CPU_REG_X; + tail->mExitRequiredRegs += CPU_REG_X; + changed = true; + } + } + } + } + + if (!changed) + { + if (tails.Size() == 1) + { + NativeCodeBasicBlock* tail = tails[0]; + int iy = 0; + while (iy < mIns.Size() && !mIns[iy].ReferencesYReg()) + iy++; + + if (iy < mIns.Size() && mIns[iy].mType == ASMIT_LDY && mIns[iy].mMode == ASMIM_ZERO_PAGE && !ChangesZeroPage(mIns[iy].mAddress, 0, iy)) + { + int addr = mIns[iy].mAddress; + if (!ChangesAccu(0, iy)) + { + int tz = tail->mIns.Size(); + if (tz >= 2 && + tail->mIns[tz - 1].mType == ASMIT_CMP && HasAsmInstructionMode(ASMIT_CPY, tail->mIns[tz - 1].mMode) && !(tail->mIns[tz - 1].mLive & LIVE_CPU_REG_A) && + tail->mIns[tz - 2].mType == ASMIT_LDA && tail->mIns[tz - 2].mMode == ASMIM_ZERO_PAGE && tail->mIns[tz - 2].mAddress == addr) + { + prev->mIns.Push(mIns[iy]); + tail->mIns[tz - 1].mType = ASMIT_CPY; tail->mIns[tz - 1].mLive |= LIVE_CPU_REG_Y; + tail->mIns[tz - 2].mType = ASMIT_LDY; tail->mIns[tz - 2].mLive |= LIVE_CPU_REG_Y; + tail->mExitRequiredRegs += CPU_REG_Y; + mEntryRequiredRegs += CPU_REG_Y; + prev->mExitRequiredRegs += CPU_REG_Y; + for (int i = 0; i < iy; i++) + mIns[i].mLive |= LIVE_CPU_REG_Y; + mIns.Remove(iy); + changed = true; + } + } + if (!changed) + { + int tz = tail->mIns.Size() - 1; + while (tz >= 0 && tail->mIns[tz].mType != ASMIT_STY) + tz--; + if (tz >= 0 && tail->mIns[tz].mMode == ASMIM_ZERO_PAGE && tail->mIns[tz].mAddress == addr && !tail->ChangesZeroPage(addr, tz + 1) && !tail->ChangesYReg(tz + 1)) + { + prev->mIns.Push(mIns[iy]); + tail->mExitRequiredRegs += CPU_REG_Y; + mEntryRequiredRegs += CPU_REG_Y; + prev->mExitRequiredRegs += CPU_REG_Y; + for (int i = 0; i < iy; i++) + mIns[i].mLive |= LIVE_CPU_REG_Y; + for (int i = tz; i < tail->mIns.Size(); i++) + tail->mIns[i].mLive |= LIVE_CPU_REG_Y; + mIns.Remove(iy); + changed = true; + } + + } + } + + } + } + + return changed; +} + +bool NativeCodeBasicBlock::OptimizeInnerLoop(NativeCodeProcedure* proc, NativeCodeBasicBlock* head, NativeCodeBasicBlock* tail, ExpandingArray& lblocks) +{ + bool simple = true; + + for (int j = 0; j < lblocks.Size(); j++) + { + NativeCodeBasicBlock* block = lblocks[j]; + for (int i = 0; i < block->mIns.Size(); i++) + { + if (block->mIns[i].mType == ASMIT_JSR) + simple = false; + + } + + block->CheckLive(); + } + + int sz = tail->mIns.Size(); + if (simple && sz >= 3) + { + if (tail->mIns[sz - 3].mType == ASMIT_INC && tail->mIns[sz - 3].mMode == ASMIM_ZERO_PAGE && + tail->mIns[sz - 2].mType == ASMIT_LDA && tail->mIns[sz - 2].mMode == ASMIM_ZERO_PAGE && tail->mIns[sz - 3].mAddress == tail->mIns[sz - 2].mAddress && + tail->mIns[sz - 1].mType == ASMIT_CMP && tail->mIns[sz - 1].mMode == ASMIM_IMMEDIATE && !(tail->mIns[sz - 1].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_X | LIVE_CPU_REG_Y)) && + tail->mBranch == ASMIT_BCC && tail->mTrueJump == head) + { + // check for usage of Y register + + bool yother = false, yindex = false, xother = false, xindex = false; + int zreg = tail->mIns[sz - 3].mAddress; + int limit = tail->mIns[sz - 1].mAddress; + + for (int j = 0; j < lblocks.Size(); j++) + { + NativeCodeBasicBlock* block = lblocks[j]; + + int bz = block == tail ? block->mIns.Size() - 3 : block->mIns.Size(); + + for (int i = 0; i < bz; i++) + { + if (block->mIns[i].mType == ASMIT_INY || block->mIns[i].mType == ASMIT_DEY || block->mIns[i].mType == ASMIT_TAY) + yother = true; + else if (block->mIns[i].mType == ASMIT_LDY) + { + if (block->mIns[i].mMode == ASMIM_ZERO_PAGE && block->mIns[i].mAddress == zreg) + yindex = true; + else + yother = true; + } + else if (!yindex && (block->mIns[i].mType == ASMIT_STY || block->mIns[i].mType == ASMIT_TYA || block->mIns[i].mMode == ASMIM_ABSOLUTE_Y || block->mIns[i].mMode == ASMIM_INDIRECT_Y)) + yother = true; + else if (block->mIns[i].mType == ASMIT_LDX && block->mIns[i].mMode == ASMIM_ZERO_PAGE && block->mIns[i].mAddress == zreg && xother && !yother && !(block->mIns[i].mLive & LIVE_CPU_REG_Y)) + { + if (i + 1 < bz && !(block->mIns[i + 1].mLive & (LIVE_CPU_REG_X | LIVE_CPU_REG_Y)) && block->mIns[i + 1].mMode == ASMIM_ABSOLUTE_X && HasAsmInstructionMode(block->mIns[i + 1].mType, ASMIM_ABSOLUTE_Y)) + { + block->mIns[i].mType = ASMIT_LDY; block->mIns[i].mLive |= LIVE_CPU_REG_Y; + block->mIns[i + 1].mMode = ASMIM_ABSOLUTE_Y; + yindex = true; + } + else + yother = true; + } + else if (block->mIns[i].mType != ASMIT_LDA && block->mIns[i].mMode == ASMIM_ZERO_PAGE && block->mIns[i].mAddress == zreg) + yother = true; + + if (block->mIns[i].mType == ASMIT_INX || block->mIns[i].mType == ASMIT_DEX || block->mIns[i].mType == ASMIT_TAX) + xother = true; + else if (block->mIns[i].mType == ASMIT_LDX) + { + if (block->mIns[i].mMode == ASMIM_ZERO_PAGE && block->mIns[i].mAddress == zreg) + xindex = true; + else + xother = true; + } + else if (!xindex && (block->mIns[i].mType == ASMIT_STX || block->mIns[i].mType == ASMIT_TXA || block->mIns[i].mMode == ASMIM_ABSOLUTE_X || block->mIns[i].mMode == ASMIM_INDIRECT_X)) + xother = true; + else if (block->mIns[i].mType != ASMIT_LDA && block->mIns[i].mMode == ASMIM_ZERO_PAGE && block->mIns[i].mAddress == zreg) + xother = true; + + } + } +#if 1 + if (!yother) + { + NativeCodeBasicBlock* lblock = proc->AllocateBlock(); + NativeCodeBasicBlock* eblock = proc->AllocateBlock(); + + const InterInstruction* iins(tail->mIns[sz - 1].mIns); + + tail->mIns.Remove(sz - 3); + tail->mIns.Remove(sz - 3); + tail->mIns.Remove(sz - 3); + + for (int j = 0; j < lblocks.Size(); j++) + { + NativeCodeBasicBlock* block = lblocks[j]; + + int bz = block->mIns.Size(); + + if (block == head) + { + for (int i = 0; i < bz; i++) + { + if (block->mIns[i].mType == ASMIT_LDA && block->mIns[i].mMode == ASMIM_ZERO_PAGE && block->mIns[i].mAddress == zreg) + lblock->mIns.Push(NativeCodeInstruction(mIns[i].mIns, ASMIT_TYA, ASMIM_IMPLIED)); + else if (block->mIns[i].mType != ASMIT_LDY) + lblock->mIns.Push(block->mIns[i]); + } + + for (int i = 0; i < lblock->mIns.Size(); i++) + lblock->mIns[i].mLive |= LIVE_CPU_REG_Y; + } + else + { + for (int i = 0; i < bz; i++) + { + if (block->mIns[i].mType == ASMIT_LDA && block->mIns[i].mMode == ASMIM_ZERO_PAGE && block->mIns[i].mAddress == zreg) + { + block->mIns[i].mType = ASMIT_TYA; + block->mIns[i].mMode = ASMIM_IMPLIED; + } + else if (block->mIns[i].mType == ASMIT_LDY) + { + block->mIns[i].mType = ASMIT_NOP; block->mIns[i].mMode = ASMIM_IMPLIED; + } + block->mIns[i].mLive |= LIVE_CPU_REG_Y; + } + } + + block->mEntryRequiredRegs += CPU_REG_Y; + block->mExitRequiredRegs += CPU_REG_Y; + } + + tail->mIns.Push(NativeCodeInstruction(iins, ASMIT_INY, ASMIM_IMPLIED)); + tail->mIns.Push(NativeCodeInstruction(iins, ASMIT_CPY, ASMIM_IMMEDIATE, limit)); + + lblock->mBranch = head->mBranch; + lblock->mTrueJump = head->mTrueJump; + lblock->mFalseJump = head->mFalseJump; + + eblock->mIns.Push(NativeCodeInstruction(iins, ASMIT_STY, ASMIM_ZERO_PAGE, zreg)); + eblock->mBranch = ASMIT_JMP; + eblock->mTrueJump = tail->mFalseJump; + eblock->mFalseJump = nullptr; + + tail->mTrueJump = lblock; + tail->mFalseJump = eblock; + + head->mIns.SetSize(0); + head->mIns.Push(NativeCodeInstruction(iins, ASMIT_LDY, ASMIM_ZERO_PAGE, zreg)); + head->mBranch = ASMIT_JMP; + head->mTrueJump = lblock; + head->mFalseJump = nullptr; + + tail->mEntryRequiredRegs += CPU_REG_Y; + tail->mExitRequiredRegs += CPU_REG_Y; + head->mExitRequiredRegs += CPU_REG_Y; + eblock->mEntryRequiredRegs = tail->mExitRequiredRegs; + eblock->mEntryRequiredRegs += CPU_REG_Y; + + lblock->CheckLive(); + + return true; + } + else +#endif + if (!xother) + { + NativeCodeBasicBlock* lblock = proc->AllocateBlock(); + NativeCodeBasicBlock* eblock = proc->AllocateBlock(); + + const InterInstruction* iins(tail->mIns[sz - 1].mIns); + + tail->mIns.Remove(sz - 3); + tail->mIns.Remove(sz - 3); + tail->mIns.Remove(sz - 3); + + for (int j = 0; j < lblocks.Size(); j++) + { + NativeCodeBasicBlock* block = lblocks[j]; + + int bz = block->mIns.Size(); + + if (block == head) + { + for (int i = 0; i < bz; i++) + { + if (block->mIns[i].mType == ASMIT_LDA && block->mIns[i].mMode == ASMIM_ZERO_PAGE && block->mIns[i].mAddress == zreg) + lblock->mIns.Push(NativeCodeInstruction(mIns[i].mIns, ASMIT_TXA, ASMIM_IMPLIED)); + else if (block->mIns[i].mType != ASMIT_LDX) + lblock->mIns.Push(block->mIns[i]); + } + + for (int i = 0; i < lblock->mIns.Size(); i++) + lblock->mIns[i].mLive |= LIVE_CPU_REG_X; + } + else + { + for (int i = 0; i < bz; i++) + { + if (block->mIns[i].mType == ASMIT_LDA && block->mIns[i].mMode == ASMIM_ZERO_PAGE && block->mIns[i].mAddress == zreg) + { + block->mIns[i].mType = ASMIT_TXA; + block->mIns[i].mMode = ASMIM_IMPLIED; + } + else if (block->mIns[i].mType == ASMIT_LDX) + { + block->mIns[i].mType = ASMIT_NOP; block->mIns[i].mMode = ASMIM_IMPLIED; + } + block->mIns[i].mLive |= LIVE_CPU_REG_X; + } + } + + block->mEntryRequiredRegs += CPU_REG_X; + block->mExitRequiredRegs += CPU_REG_X; + } + + tail->mIns.Push(NativeCodeInstruction(iins, ASMIT_INX, ASMIM_IMPLIED)); + tail->mIns.Push(NativeCodeInstruction(iins, ASMIT_CPX, ASMIM_IMMEDIATE, limit)); + + lblock->mBranch = head->mBranch; + lblock->mTrueJump = head->mTrueJump; + lblock->mFalseJump = head->mFalseJump; + lblock->mEntryRequiredRegs = tail->mTrueJump->mEntryRequiredRegs; + + eblock->mIns.Push(NativeCodeInstruction(iins, ASMIT_STX, ASMIM_ZERO_PAGE, zreg)); + eblock->mBranch = ASMIT_JMP; + eblock->mTrueJump = tail->mFalseJump; + eblock->mFalseJump = nullptr; + eblock->mEntryRequiredRegs = tail->mFalseJump->mEntryRequiredRegs; + + tail->mTrueJump = lblock; + tail->mFalseJump = eblock; + + head->mIns.SetSize(0); + head->mIns.Push(NativeCodeInstruction(iins, ASMIT_LDX, ASMIM_ZERO_PAGE, zreg)); + head->mBranch = ASMIT_JMP; + head->mTrueJump = lblock; + head->mFalseJump = nullptr; + + eblock->mEntryRequiredRegs += CPU_REG_X; + tail->mEntryRequiredRegs += CPU_REG_X; + tail->mExitRequiredRegs += CPU_REG_X; + head->mExitRequiredRegs += CPU_REG_X; + + tail->CheckLive(); + head->CheckLive(); + eblock->CheckLive(); + lblock->CheckLive(); + + return true; + } + } + } +#if 1 + else if (head->mEntryBlocks.Size() == 2) + { + bool changed = false; + + NativeCodeBasicBlock* exitBlock, * prefixBlock; + + if (tail->mTrueJump == this) + exitBlock = tail->mFalseJump; + else + exitBlock = tail->mTrueJump; + + if (head->mEntryBlocks[0] == this) + prefixBlock = head->mEntryBlocks[1]; + else + prefixBlock = head->mEntryBlocks[0]; + + if (!prefixBlock->mExitRequiredRegs[CPU_REG_A]) + { + for (int i = 0; i + 1 < head->mIns.Size(); i++) + { + if (head->mIns[i + 0].mType == ASMIT_LDA && head->mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + head->mIns[i + 1].mType == ASMIT_STA && head->mIns[i + 1].mMode == ASMIM_ZERO_PAGE && !(head->mIns[1].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z))) + { + if (!head->ChangesZeroPage(mIns[i + 0].mAddress, 0, i) && !head->ReferencesZeroPage(mIns[i + 1].mAddress, 0, i) && + tail->IsSameZPValue(mIns[i + 0].mAddress, mIns[i + 1].mAddress)) + { + if (prefixBlock->mFalseJump) + prefixBlock = PrefixLoopBlock(prefixBlock); + + prefixBlock->mIns.Push(head->mIns[i + 0]); + prefixBlock->mIns.Push(head->mIns[i + 1]); + prefixBlock->mExitRequiredRegs += head->mIns[i + 1].mAddress; + for (int i = 0; i < lblocks.Size(); i++) + { + lblocks[i]->mExitRequiredRegs += head->mIns[i + 1].mAddress; + lblocks[i]->mEntryRequiredRegs += head->mIns[i + 1].mAddress; + lblocks[i]->mEntryProvidedRegs += head->mIns[i + 1].mAddress; + lblocks[i]->mExitRequiredRegs += head->mIns[i + 0].mAddress; + lblocks[i]->mEntryProvidedRegs += head->mIns[i + 0].mAddress; + } + + head->mIns[i + 0].mType = ASMIT_NOP; head->mIns[i + 0].mMode = ASMIM_IMPLIED; + head->mIns[i + 1].mType = ASMIT_NOP; head->mIns[i + 1].mMode = ASMIM_IMPLIED; + + changed = true; + } + } + } + } + + return changed; + } +#endif + return false; +} + +NativeCodeBasicBlock* NativeCodeBasicBlock::CollectInnerLoop(NativeCodeBasicBlock* head, ExpandingArray& lblocks) +{ + if (mLoopHeadBlock != head) + { + mLoopHeadBlock = head; + lblocks.Push(this); + + if (mTrueJump != head && mFalseJump != head) + { + if (mFalseJump && mTrueJump == this && mEntryBlocks.Size() == 2 && mIns.Size() == 1 && (mIns[0].mType == ASMIT_BIT || mIns[0].mType == ASMIT_LDA && !(mIns[0].mLive & LIVE_CPU_REG_A))) + { + mLoopTailBlock = mFalseJump->CollectInnerLoop(head, lblocks); + } + else + { + if (mTrueJump) + mLoopTailBlock = mTrueJump->CollectInnerLoop(head, lblocks); + + if (mLoopTailBlock && mFalseJump) + { + NativeCodeBasicBlock* tail = mFalseJump->CollectInnerLoop(head, lblocks); + if (tail != mLoopTailBlock) + mLoopTailBlock = nullptr; + } + } + } + else + mLoopTailBlock = this; + } + + return mLoopTailBlock; +} + +bool NativeCodeBasicBlock::OptimizeInnerLoops(NativeCodeProcedure* proc) +{ + bool changed = false; + + if (!mVisited) + { + CheckLive(); + + if (mLoopHead) + { + ExpandingArray lblocks; + + NativeCodeBasicBlock* tail = CollectInnerLoop(this, lblocks); + + if (tail && this != tail) + changed = OptimizeInnerLoop(proc, this, tail, lblocks); + } + + CheckLive(); + + mVisited = true; + + if (mTrueJump && mTrueJump->OptimizeInnerLoops(proc)) + changed = true; + if (mFalseJump && mFalseJump->OptimizeInnerLoops(proc)) + changed = true; + } + + return changed; +} + +NativeCodeBasicBlock* NativeCodeBasicBlock::BuildSingleEntry(NativeCodeProcedure* proc, NativeCodeBasicBlock* block) +{ + if (block->mNumEntries > 1) + { + NativeCodeBasicBlock* nblock = proc->AllocateBlock(); + nblock->mTrueJump = block; + nblock->mEntryRequiredRegs = block->mEntryRequiredRegs; + nblock->mExitRequiredRegs = block->mEntryRequiredRegs; + int k = block->mEntryBlocks.IndexOf(this); + block->mEntryBlocks[k] = nblock; + nblock->mEntryBlocks.Push(this); + nblock->mNumEntries = 1; + return nblock; + } + else + return block; +} + +NativeCodeBasicBlock* NativeCodeBasicBlock::BuildSingleExit(NativeCodeProcedure* proc, NativeCodeBasicBlock* block) +{ + if (block->mFalseJump) + { + NativeCodeBasicBlock* nblock = proc->AllocateBlock(); + if (block->mFalseJump == this) + block->mFalseJump = nblock; + if (block->mTrueJump == this) + block->mTrueJump = nblock; + nblock->mTrueJump = this; + + nblock->mEntryRequiredRegs = this->mEntryRequiredRegs; + nblock->mExitRequiredRegs = this->mEntryRequiredRegs; + + int k = mEntryBlocks.IndexOf(block); + mEntryBlocks[k] = nblock; + nblock->mEntryBlocks.Push(block); + nblock->mNumEntries = 1; + return nblock; + } + else + return block; +} + +int NativeCodeBasicBlock::CorrectXOffset(const InterInstruction * ins, int xoffset, int at) +{ + while (xoffset > 0) + { + mIns.Insert(at, NativeCodeInstruction(ins, ASMIT_DEX)); + at++; + xoffset--; + } + + while (xoffset < 0) + { + mIns.Insert(at, NativeCodeInstruction(ins, ASMIT_INX)); + at++; + xoffset++; + } + + return at; +} + +int NativeCodeBasicBlock::CorrectYOffset(const InterInstruction * ins, int yoffset, int at) +{ + while (yoffset > 0) + { + mIns.Insert(at, NativeCodeInstruction(ins, ASMIT_DEY)); + at++; + yoffset--; + } + + while (yoffset < 0) + { + mIns.Insert(at, NativeCodeInstruction(ins, ASMIT_INY)); + at++; + yoffset++; + } + + return at; +} + + +bool NativeCodeBasicBlock::OptimizeGenericLoop(void) +{ + bool changed = false; + + if (!mVisited) + { + if (mLoopHead) + { + ExpandingArray lblocks; + + mProc->ResetPatched(); + if (CollectSingleEntryGenericLoop(lblocks)) + { + int yreg = -1, xreg = -1, areg = -1; + int zyreg[NUM_REGS], zxreg[NUM_REGS], zareg[NUM_REGS]; + for (int i = 0; i < NUM_REGS; i++) + { + zyreg[i] = 0; + zxreg[i] = 0; + zareg[i] = 0; + } + + if (mEntryRequiredRegs[CPU_REG_Y]) + yreg = -2; + if (mEntryRequiredRegs[CPU_REG_X]) + xreg = -2; + if (mEntryRequiredRegs[CPU_REG_A]) + areg = -2; + + for (int i = 0; i < lblocks.Size(); i++) + { + NativeCodeBasicBlock* block = lblocks[i]; + + int yoffset = 0, xoffset = 0, aoffset = 0; + int yskew = 0, xskew = 0; + + for (int j = 0; j < block->mIns.Size(); j++) + { + const NativeCodeInstruction& ins(block->mIns[j]); + + switch (ins.mType) + { + case ASMIT_LDY: + if (ins.mMode == ASMIM_ZERO_PAGE && (yreg == -1 || yreg == ins.mAddress)) + yreg = ins.mAddress; + else + yreg = -2; + if (ins.mMode == ASMIM_ZERO_PAGE) + { + zxreg[ins.mAddress] = -1; + zareg[ins.mAddress] = -1; + } + yoffset = 0; + yskew = 0; + zareg[CPU_REG_Y] = -1; + break; + case ASMIT_LDX: + if (ins.mMode == ASMIM_ZERO_PAGE && (xreg == -1 || xreg == ins.mAddress)) + xreg = ins.mAddress; + else + xreg = -2; + if (ins.mMode == ASMIM_ZERO_PAGE) + { + zyreg[ins.mAddress] = -1; + zareg[ins.mAddress] = -1; + } + xoffset = 0; + xskew = 0; + zareg[CPU_REG_X] = -1; + break; + case ASMIT_STY: + if (ins.mMode == ASMIM_ZERO_PAGE) + { + zxreg[ins.mAddress] = -1; + zareg[ins.mAddress] = -1; + } + break; + case ASMIT_STX: + if (ins.mMode == ASMIM_ZERO_PAGE) + { + zyreg[ins.mAddress] = -1; + zareg[ins.mAddress] = -1; + } + break; + case ASMIT_INY: + yoffset++; + zareg[CPU_REG_Y] = -1; + break; + case ASMIT_DEY: + yoffset--; + zareg[CPU_REG_Y] = -1; + break; + case ASMIT_INX: + xoffset++; + zareg[CPU_REG_X] = -1; + break; + case ASMIT_DEX: + xoffset--; + zareg[CPU_REG_X] = -1; + break; + case ASMIT_INC: + if (ins.mMode == ASMIM_ZERO_PAGE) + { + if (zxreg[ins.mAddress] >= 0) + { + if (ins.mLive & LIVE_CPU_REG_X) + zxreg[ins.mAddress] = -1; + else + { + zxreg[ins.mAddress] += 3; + xskew++; + } + } + if (zyreg[ins.mAddress] >= 0) + { + if (ins.mLive & LIVE_CPU_REG_Y) + zyreg[ins.mAddress] = -1; + else + { + zyreg[ins.mAddress] += 3; + yskew++; + } + } + + zareg[ins.mAddress] = -1; + } + break; + case ASMIT_DEC: + if (ins.mMode == ASMIM_ZERO_PAGE) + { + if (zxreg[ins.mAddress] >= 0) + { + if (ins.mLive & LIVE_CPU_REG_X) + zxreg[ins.mAddress] = -1; + else + { + zxreg[ins.mAddress] += 3; + xskew--; + } + } + if (zyreg[ins.mAddress] >= 0) + { + if (ins.mLive & LIVE_CPU_REG_Y) + zyreg[ins.mAddress] = -1; + else + { + zyreg[ins.mAddress] += 3; + yskew--; + } + } + + zareg[ins.mAddress] = -1; + } + break; + + case ASMIT_ASL: + case ASMIT_LSR: + case ASMIT_ROL: + case ASMIT_ROR: + if (ins.mMode == ASMIM_ZERO_PAGE) + { + if (zareg[ins.mAddress] >= 0) + { + if (ins.mLive & LIVE_CPU_REG_A) + zareg[ins.mAddress] = -1; + else + zareg[ins.mAddress] += 3; + } + + zxreg[ins.mAddress] = -1; + zyreg[ins.mAddress] = -1; + } + else if (ins.mMode == ASMIM_IMPLIED) + aoffset = 1; + break; + + case ASMIT_LDA: + if (ins.mMode == ASMIM_ZERO_PAGE) + { + if ((areg == -1 || areg == ins.mAddress && aoffset == 0) && !(ins.mLive & LIVE_CPU_REG_Z)) + areg = ins.mAddress; + else + areg = -2; + + if (ins.mAddress == yreg && yoffset != 0) + yreg = -2; + if (ins.mAddress == xreg && xoffset != 0) + xreg = -2; + + if (zxreg[ins.mAddress] >= 0) + zxreg[ins.mAddress]++; + if (zyreg[ins.mAddress] >= 0) + zyreg[ins.mAddress]++; + } + else + areg = -2; + break; + case ASMIT_STA: + if (ins.mMode == ASMIM_ZERO_PAGE) + { + if (areg == ins.mAddress) + aoffset = 0; + + if (ins.mAddress == yreg && (yoffset != 0 || (ins.mLive & LIVE_CPU_REG_Y))) + yreg = -2; + if (ins.mAddress == xreg && (xoffset != 0 || (ins.mLive & LIVE_CPU_REG_X))) + xreg = -2; + + if (zxreg[ins.mAddress] >= 0) + zxreg[ins.mAddress]++; + if (zyreg[ins.mAddress] >= 0) + zyreg[ins.mAddress]++; + } + break; + case ASMIT_TYA: + if (yoffset) + { + yreg = -2; + areg = -2; + } + else if ((areg == -1 || areg == CPU_REG_Y) && !(ins.mLive & LIVE_CPU_REG_Z)) + areg = CPU_REG_Y; + else + areg = -2; + break; + case ASMIT_TXA: + if (xoffset) + { + xreg = -2; + areg = -2; + } + else if ((areg == -1 || areg == CPU_REG_X) && !(ins.mLive & LIVE_CPU_REG_Z)) + areg = CPU_REG_X; + else + areg = -2; + break; + case ASMIT_TAY: + yreg = -2; + zareg[CPU_REG_Y] = -1; + break; + case ASMIT_TAX: + xreg = -2; + zareg[CPU_REG_X] = -1; + break; + case ASMIT_JSR: + yreg = -2; + xreg = -2; + areg = -2; + zareg[CPU_REG_X] = -1; + zareg[CPU_REG_Y] = -1; + break; + case ASMIT_CMP: + if (ins.mMode == ASMIM_INDIRECT_Y && yoffset != 0 && yreg >= 0) + { + yreg = -2; + } + else if (ins.mMode == ASMIM_ZERO_PAGE) + { + zxreg[ins.mAddress] = -1; + zyreg[ins.mAddress] = -1; + zareg[ins.mAddress] = -1; + } + break; + default: + if (ins.mMode == ASMIM_ZERO_PAGE) + { + zxreg[ins.mAddress] = -1; + zyreg[ins.mAddress] = -1; + zareg[ins.mAddress] = -1; + } + if (ins.ChangesAccu()) + aoffset = 1; + break; + } + + if (xreg >= 0 && ins.mMode == ASMIM_ABSOLUTE_X && xskew != xoffset) + zxreg[xreg] = -1; + if (yreg >= 0 && (ins.mMode == ASMIM_ABSOLUTE_Y || ins.mMode == ASMIM_INDIRECT_Y) && yskew != yoffset) + zyreg[yreg] = -1; + + if (!(ins.mLive & LIVE_CPU_REG_Y)) + yoffset = 0; + if (!(ins.mLive & LIVE_CPU_REG_X)) + xoffset = 0; +// if (!(ins.mLive & LIVE_CPU_REG_A)) +// aoffset = 0; + + if (ins.mMode == ASMIM_INDIRECT_Y) + { + zxreg[ins.mAddress] = -1; + zyreg[ins.mAddress] = -1; + zareg[ins.mAddress] = -1; + zxreg[ins.mAddress + 1] = -1; + zyreg[ins.mAddress + 1] = -1; + zareg[ins.mAddress + 1] = -1; + } + } +#if 1 + if (yoffset && block->mExitRequiredRegs[CPU_REG_Y]) + yreg = -2; + if (xoffset && block->mExitRequiredRegs[CPU_REG_X]) + xreg = -2; + if (aoffset) // && block->mExitRequiredRegs[CPU_REG_A]) + areg = -2; +#endif + } + + if (areg >= 0) + { + if (zareg[areg] < 0) + areg = -2; + else + { + zxreg[areg] = -1; + zyreg[areg] = -1; + } + } + + if (xreg != -2) + { + if (xreg == -1) + { + int nx = 0; + for (int i = 0; i < 256; i++) + { + if (i != yreg && zxreg[i] > nx) + { + nx = zxreg[i]; + xreg = i; + } + } + } + else if (zxreg[xreg] < 0) + xreg = -1; + } + + if (yreg != -2) + { + if (yreg == -1) + { + int nx = 0; + for (int i = 0; i < 256; i++) + { + if (i != xreg && zyreg[i] > nx) + { + nx = zyreg[i]; + yreg = i; + } + } + } + else if (zyreg[yreg] < 0) + yreg = -1; + } + + if (yreg >= 0 || xreg >= 0 || areg >= 0) + { + ExpandingArray entries; + ExpandingArray exits; + + for (int i = 0; i < lblocks.Size(); i++) + { + NativeCodeBasicBlock* block = lblocks[i]; + + if (yreg >= 0) + { + block->mEntryRequiredRegs += CPU_REG_Y; + block->mExitRequiredRegs += CPU_REG_Y; + } + if (xreg >= 0) + { + block->mEntryRequiredRegs += CPU_REG_X; + block->mExitRequiredRegs += CPU_REG_X; + } + if (areg >= 0) + { + block->mEntryRequiredRegs += CPU_REG_A; + block->mExitRequiredRegs += CPU_REG_A; + } + + for (int j = 0; j < block->mEntryBlocks.Size(); j++) + { + if (!lblocks.Contains(block->mEntryBlocks[j])) + { + block->mEntryBlocks[j] = block->BuildSingleExit(mProc, block->mEntryBlocks[j]); + if (areg >= 0 && mEntryRequiredRegs[areg]) + { + if (areg == CPU_REG_X) + block->mEntryBlocks[j]->mIns.Push(NativeCodeInstruction(block->mEntryBlocks[j]->mBranchIns, ASMIT_TXA)); + else if (areg == CPU_REG_Y) + block->mEntryBlocks[j]->mIns.Push(NativeCodeInstruction(block->mEntryBlocks[j]->mBranchIns, ASMIT_TYA)); + else + block->mEntryBlocks[j]->mIns.Push(NativeCodeInstruction(block->mEntryBlocks[j]->mBranchIns, ASMIT_LDA, ASMIM_ZERO_PAGE, areg)); + block->mEntryBlocks[j]->mExitRequiredRegs += CPU_REG_A; + } + if (yreg >= 0 && mEntryRequiredRegs[yreg]) + { + block->mEntryBlocks[j]->mIns.Push(NativeCodeInstruction(block->mEntryBlocks[j]->mBranchIns, ASMIT_LDY, ASMIM_ZERO_PAGE, yreg)); + block->mEntryBlocks[j]->mExitRequiredRegs += CPU_REG_Y; + } + if (xreg >= 0 && mEntryRequiredRegs[xreg]) + { + block->mEntryBlocks[j]->mIns.Push(NativeCodeInstruction(block->mEntryBlocks[j]->mBranchIns, ASMIT_LDX, ASMIM_ZERO_PAGE, xreg)); + block->mEntryBlocks[j]->mExitRequiredRegs += CPU_REG_X; + } + } + } + + int yoffset = 0, xoffset = 0, xskew = 0, yskew = 0; + + for (int j = 0; j < block->mIns.Size(); j++) + { + NativeCodeInstruction& ins(block->mIns[j]); + + switch (ins.mType) + { + case ASMIT_LDY: + case ASMIT_STY: + if (ins.mMode == ASMIM_ZERO_PAGE && ins.mAddress == yreg) + { + ins.mType = ASMIT_NOP; + ins.mMode = ASMIM_IMPLIED; + yoffset = 0; + yskew = 0; + } + break; + case ASMIT_LDX: + if (ins.mMode == ASMIM_ZERO_PAGE && ins.mAddress == xreg) + { + if (ins.mLive & LIVE_CPU_REG_Z) + { + if (!(ins.mLive & LIVE_CPU_REG_A)) + { + ins.mType = ASMIT_TXA; + ins.mMode = ASMIM_IMPLIED; + } + else + { + ins.mType = ASMIT_CPX; + ins.mMode = ASMIM_IMMEDIATE; + ins.mAddress = 0; + } + } + else + { + ins.mType = ASMIT_NOP; + ins.mMode = ASMIM_IMPLIED; + } + xoffset = 0; + xskew = 0; + } + break; + case ASMIT_STX: + if (ins.mMode == ASMIM_ZERO_PAGE && ins.mAddress == xreg) + { + ins.mType = ASMIT_NOP; + ins.mMode = ASMIM_IMPLIED; + xoffset = 0; + xskew = 0; + } + break; + case ASMIT_ASL: + case ASMIT_LSR: + case ASMIT_ROL: + case ASMIT_ROR: + if (ins.mMode == ASMIM_ZERO_PAGE && ins.mAddress == areg) + ins.mMode = ASMIM_IMPLIED; + break; + + case ASMIT_INC: + if (ins.mMode == ASMIM_ZERO_PAGE && ins.mAddress == yreg) + { + ins.mType = ASMIT_INY; + ins.mMode = ASMIM_IMPLIED; + yskew += 1; + } + else if (ins.mMode == ASMIM_ZERO_PAGE && ins.mAddress == xreg) + { + ins.mType = ASMIT_INX; + ins.mMode = ASMIM_IMPLIED; + xskew += 1; + } + break; + case ASMIT_DEC: + if (ins.mMode == ASMIM_ZERO_PAGE && ins.mAddress == yreg) + { + ins.mType = ASMIT_DEY; + ins.mMode = ASMIM_IMPLIED; + yskew -= 1; + } + else if (ins.mMode == ASMIM_ZERO_PAGE && ins.mAddress == xreg) + { + ins.mType = ASMIT_DEX; + ins.mMode = ASMIM_IMPLIED; + xskew -= 1; + } + break; + + case ASMIT_LDA: + if (ins.mMode == ASMIM_ZERO_PAGE && ins.mAddress == areg) + { + ins.mType = ASMIT_NOP; + ins.mMode = ASMIM_IMPLIED; + } + else if (ins.mMode == ASMIM_ZERO_PAGE && ins.mAddress == yreg) + { + ins.mType = ASMIT_TYA; + ins.mMode = ASMIM_IMPLIED; + } + else if (ins.mMode == ASMIM_ZERO_PAGE && ins.mAddress == xreg) + { + ins.mType = ASMIT_TXA; + ins.mMode = ASMIM_IMPLIED; + } + break; + case ASMIT_STA: + if (ins.mMode == ASMIM_ZERO_PAGE && ins.mAddress == areg) + { + ins.mType = ASMIT_NOP; + ins.mMode = ASMIM_IMPLIED; + } + else if (ins.mMode == ASMIM_ZERO_PAGE && ins.mAddress == yreg) + { + ins.mType = ASMIT_TAY; + ins.mMode = ASMIM_IMPLIED; + } + else if (ins.mMode == ASMIM_ZERO_PAGE && ins.mAddress == xreg) + { + ins.mType = ASMIT_TAX; + ins.mMode = ASMIM_IMPLIED; + } + break; + + case ASMIT_INY: + yoffset++; + break; + case ASMIT_DEY: + yoffset--; + break; + case ASMIT_INX: + xoffset++; + break; + case ASMIT_DEX: + xoffset--; + break; + + case ASMIT_TXA: + if (areg == CPU_REG_X) + ins.mType = ASMIT_NOP; + break; + case ASMIT_TYA: + if (areg == CPU_REG_Y) + ins.mType = ASMIT_NOP; + break; + } + + if (yoffset && yreg >= 0 && !(ins.mLive & LIVE_CPU_REG_Y)) + { + if (j + 1 == block->mIns.Size() && !block->mExitRequiredRegs[yreg]) + yoffset = 0; + else + { + if (j + 1 == block->mIns.Size() && (ins.mLive & LIVE_CPU_REG_Z) && ins.mType == ASMIT_CMP) + { + printf("oopsie Y\n"); + } + else + { + j = block->CorrectYOffset(ins.mIns, yoffset, j + 1) - 1; + yoffset = 0; + + if (j + 1 == block->mIns.Size() && (ins.mLive & LIVE_CPU_REG_Z) && ins.ChangesAccuAndFlag()) + { + ins.mLive |= LIVE_CPU_REG_A; + block->mIns.Push(NativeCodeInstruction(ins.mIns, ASMIT_ORA, ASMIM_IMMEDIATE, 0)); + } + } + } + } + + if (xoffset && xreg >= 0 && !(ins.mLive & LIVE_CPU_REG_X)) + { + if (j + 1 == block->mIns.Size() && !block->mExitRequiredRegs[xreg]) + xoffset = 0; + else + { + if (j + 1 == block->mIns.Size() && (ins.mLive & LIVE_CPU_REG_Z) && ins.mType == ASMIT_CMP) + { + printf("oopsie X\n"); + } + else + { + j = block->CorrectXOffset(ins.mIns, xoffset, j + 1) - 1; + xoffset = 0; + + if (j + 1 == block->mIns.Size() && (ins.mLive & LIVE_CPU_REG_Z) && ins.ChangesAccuAndFlag()) + { + ins.mLive |= LIVE_CPU_REG_A; + block->mIns.Push(NativeCodeInstruction(ins.mIns, ASMIT_ORA, ASMIM_IMMEDIATE, 0)); + } + } + } + } + + if (yreg >= 0) + ins.mLive |= LIVE_CPU_REG_Y; + if (xreg >= 0) + ins.mLive |= LIVE_CPU_REG_X; + if (areg >= 0) + ins.mLive |= LIVE_CPU_REG_A; + + } + + if (block->mTrueJump) + { + if (!lblocks.Contains(block->mTrueJump)) + { + block->mTrueJump = block->BuildSingleEntry(mProc, block->mTrueJump); + if (areg >= 0 && block->mTrueJump->mEntryRequiredRegs[areg]) + { + if (areg < 256) + block->mTrueJump->mIns.Insert(0, NativeCodeInstruction(block->mBranchIns, ASMIT_STA, ASMIM_ZERO_PAGE, areg)); + block->mExitRequiredRegs += CPU_REG_A; + block->mTrueJump->mEntryRequiredRegs += CPU_REG_A; + } + if (yreg >= 0 && block->mTrueJump->mEntryRequiredRegs[yreg]) + { + int j = block->mTrueJump->CorrectYOffset(block->mBranchIns, yoffset, 0); + block->mTrueJump->mIns.Insert(j, NativeCodeInstruction(block->mBranchIns, ASMIT_STY, ASMIM_ZERO_PAGE, yreg)); + + block->mExitRequiredRegs += CPU_REG_Y; + block->mTrueJump->mEntryRequiredRegs += CPU_REG_Y; + } + if (xreg >= 0 && block->mTrueJump->mEntryRequiredRegs[xreg]) + { + int j = block->mTrueJump->CorrectXOffset(block->mBranchIns, xoffset, 0); + + block->mTrueJump->mIns.Insert(j, NativeCodeInstruction(block->mBranchIns, ASMIT_STX, ASMIM_ZERO_PAGE, xreg)); + block->mExitRequiredRegs += CPU_REG_X; + block->mTrueJump->mEntryRequiredRegs += CPU_REG_X; + } + } + else + { + if (yreg >= 0 && yoffset != 0 && block->mFalseJump->mEntryRequiredRegs[yreg]) + block->mTrueJump->CorrectYOffset(block->mBranchIns, yoffset, 0); + if (xreg >= 0 && xoffset != 0 && block->mFalseJump->mEntryRequiredRegs[xreg]) + block->mTrueJump->CorrectXOffset(block->mBranchIns, xoffset, 0); + } + } + if (block->mFalseJump) + { + if (!lblocks.Contains(block->mFalseJump)) + { + block->mFalseJump = block->BuildSingleEntry(mProc, block->mFalseJump); + if (areg >= 0 && block->mFalseJump->mEntryRequiredRegs[areg]) + { + if (areg < 256) + block->mFalseJump->mIns.Insert(0, NativeCodeInstruction(block->mBranchIns, ASMIT_STA, ASMIM_ZERO_PAGE, areg)); + block->mExitRequiredRegs += CPU_REG_A; + block->mFalseJump->mEntryRequiredRegs += CPU_REG_A; + } + if (yreg >= 0 && block->mFalseJump->mEntryRequiredRegs[yreg]) + { + int j = block->mTrueJump->CorrectYOffset(block->mBranchIns, yoffset, 0); + block->mFalseJump->mIns.Insert(j, NativeCodeInstruction(block->mBranchIns, ASMIT_STY, ASMIM_ZERO_PAGE, yreg)); + block->mExitRequiredRegs += CPU_REG_Y; + block->mFalseJump->mEntryRequiredRegs += CPU_REG_Y; + } + if (xreg >= 0 && block->mFalseJump->mEntryRequiredRegs[xreg]) + { + int j = block->mTrueJump->CorrectXOffset(block->mBranchIns, xoffset, 0); + block->mFalseJump->mIns.Insert(j, NativeCodeInstruction(block->mBranchIns, ASMIT_STX, ASMIM_ZERO_PAGE, xreg)); + block->mExitRequiredRegs += CPU_REG_X; + block->mFalseJump->mEntryRequiredRegs += CPU_REG_X; + } + } + else + { + if (yreg >= 0 && yoffset != 0 && block->mFalseJump->mEntryRequiredRegs[yreg]) + block->mFalseJump->CorrectYOffset(block->mBranchIns, yoffset, 0); + if (xreg >= 0 && xoffset != 0 && block->mFalseJump->mEntryRequiredRegs[xreg]) + block->mFalseJump->CorrectXOffset(block->mBranchIns, xoffset, 0); + } + } + + block->CheckLive(); + } + + CheckLive(); + + changed = true; + } + } + } + + mVisited = true; + + if (mTrueJump && mTrueJump->OptimizeGenericLoop()) + changed = true; + if (mFalseJump && mFalseJump->OptimizeGenericLoop()) + changed = true; + } + + return changed; +} + +void NativeCodeBasicBlock::CollectReachable(ExpandingArray& lblock) +{ + if (!mVisited && !mPatched) + { + lblock.Push(this); + mPatched = true; + + if (mTrueJump) mTrueJump->CollectReachable(lblock); + if (mFalseJump) mFalseJump->CollectReachable(lblock); + } +} + +bool NativeCodeBasicBlock::CollectGenericLoop(ExpandingArray& lblocks) +{ + ExpandingArray rblocks; + + mProc->ResetPatched(); + CollectReachable(rblocks); + + mProc->ResetPatched(); + + bool changed; + do + { + changed = false; + + for (int i = 0; i < rblocks.Size(); i++) + { + NativeCodeBasicBlock* block(rblocks[i]); + + if (!block->mPatched && + (block->mTrueJump && (block->mTrueJump->mPatched || block->mTrueJump == this) || + block->mFalseJump && (block->mFalseJump->mPatched || block->mFalseJump == this))) + { + lblocks.Push(block); + block->mPatched = true; + changed = true; + } + } + + } while (changed); + + return lblocks.Size() > 0; +} + +bool NativeCodeBasicBlock::CollectSingleEntryGenericLoop(ExpandingArray& lblocks) +{ + if (CollectGenericLoop(lblocks)) + { + for (int i = 0; i < lblocks.Size(); i++) + { + NativeCodeBasicBlock* block = lblocks[i]; + + if (block != this) + { + for (int j = 0; j < block->mEntryBlocks.Size(); j++) + if (!lblocks.Contains(block->mEntryBlocks[j])) + return false; + } + } + + return true; + } + else + return false; +} + + +bool NativeCodeBasicBlock::OptimizeFindLoop(NativeCodeProcedure* proc) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + CheckLive(); + + if (mLoopHead && mNumEntries == 2) + { + if (mTrueJump && mFalseJump && mFalseJump->mNumEntries == 1 && mTrueJump->mNumEntries == 1) + { + NativeCodeBasicBlock* succ = nullptr, * body = nullptr; + + if (!mFalseJump->mFalseJump && mFalseJump->mTrueJump == this) + { + succ = mTrueJump; + body = mFalseJump; + } + else if (!mTrueJump->mFalseJump && mTrueJump->mTrueJump == this) + { + succ = mFalseJump; + body = mTrueJump; + } + + if (succ) + { + NativeCodeBasicBlock* pred = mEntryBlocks[0]; + if (pred == body) + pred = mEntryBlocks[1]; + + if (!pred->mFalseJump) + { + if (mIns.Size() > 0 && body->mIns.Size() > 0) + { + int fsz = body->mIns.Size(); + + if (mIns[0].mType == ASMIT_LDY && mIns[0].mMode == ASMIM_ZERO_PAGE && + body->mIns[fsz - 1].mType == ASMIT_INC && body->mIns[fsz - 1].SameEffectiveAddress(mIns[0])) + { + int i = 1; + while (i < mIns.Size() && !mIns[i].ChangesYReg() && !mIns[i].ReferencesZeroPage(mIns[0].mAddress)) + i++; + if (i == mIns.Size()) + { + i = 0; + while (i + 1 < fsz && !body->mIns[i].ChangesYReg() && !body->mIns[i].ReferencesZeroPage(mIns[0].mAddress)) + i++; + if (i + 1 == fsz) + { + pred->mIns.Push(mIns[0]); + succ->mIns.Insert(0, NativeCodeInstruction(mIns[0].mIns, ASMIT_STY, mIns[0])); + body->mIns[fsz - 1].mType = ASMIT_INY; + body->mIns[fsz - 1].mMode = ASMIM_IMPLIED; + mIns.Remove(0); + + body->mEntryRequiredRegs += CPU_REG_Y; + body->mExitRequiredRegs += CPU_REG_Y; + mEntryRequiredRegs += CPU_REG_Y; + mExitRequiredRegs += CPU_REG_Y; + succ->mEntryRequiredRegs += CPU_REG_Y; + + for (int i = 0; i < mIns.Size(); i++) + mIns[i].mLive |= LIVE_CPU_REG_Y; + for (int i = 0; i < fsz; i++) + body->mIns[i].mLive |= LIVE_CPU_REG_Y; + + changed = true; + } + } + } + } + + if (!ReferencesYReg() && body->mIns.Size() > 0 && body->mIns[0].mType == ASMIT_LDY && body->mIns[0].mMode == ASMIM_ZERO_PAGE && !mExitRequiredRegs[CPU_REG_Y]) + { + int incdec = 0; + bool fail = false; + int addr = body->mIns[0].mAddress; + if (!ReferencesZeroPage(addr)) + { + for (int i = 1; i < body->mIns.Size(); i++) + { + const NativeCodeInstruction& bins(body->mIns[i]); + if (bins.mMode == ASMIM_ZERO_PAGE && bins.mAddress == addr) + { + if (bins.mType == ASMIT_INC) + incdec++; + else if (bins.mType == ASMIT_DEC) + incdec--; + else + { + fail = true; + break; + } + } + else if (bins.ReferencesZeroPage(addr)) + fail = true; + else if (bins.ChangesYReg()) + fail = true; + } + + if (!fail) + { + if (pred->mIns.Size() > 0 && (pred->mIns.Last().mLive & LIVE_CPU_REG_Z)) + pred->mIns.Insert(pred->mIns.Size() - 1, NativeCodeInstruction(body->mIns[0].mIns, ASMIT_LDY, ASMIM_ZERO_PAGE, addr)); + else + pred->mIns.Push(NativeCodeInstruction(body->mIns[0].mIns, ASMIT_LDY, ASMIM_ZERO_PAGE, addr)); + body->mIns.Remove(0); + while (incdec > 0) + { + if (body->mIns.Size() > 0 && (body->mIns.Last().mLive & LIVE_CPU_REG_Z)) + body->mIns.Insert(body->mIns.Size() - 1, NativeCodeInstruction(body->mIns[0].mIns, ASMIT_INY)); + else + body->mIns.Push(NativeCodeInstruction(body->mIns[0].mIns, ASMIT_INY)); + incdec--; + } + while (incdec < 0) + { + if (body->mIns.Size() > 0 && (body->mIns.Last().mLive & LIVE_CPU_REG_Z)) + body->mIns.Insert(body->mIns.Size() - 1, NativeCodeInstruction(body->mIns[0].mIns, ASMIT_DEY)); + else + body->mIns.Push(NativeCodeInstruction(body->mIns[0].mIns, ASMIT_DEY)); + incdec++; + } + for (int i = 0; i < body->mIns.Size(); i++) + { + if (body->mIns[i].mMode == ASMIM_ZERO_PAGE && body->mIns[i].mAddress == addr) + { + body->mIns[i].mType = ASMIT_NOP; body->mIns[i].mMode = ASMIM_IMPLIED; + } + } + succ->mIns.Insert(0, NativeCodeInstruction(body->mIns[0].mIns, ASMIT_STY, ASMIM_ZERO_PAGE, addr)); + + body->mEntryRequiredRegs += CPU_REG_Y; + body->mExitRequiredRegs += CPU_REG_Y; + mEntryRequiredRegs += CPU_REG_Y; + mExitRequiredRegs += CPU_REG_Y; + succ->mEntryRequiredRegs += CPU_REG_Y; + + for (int i = 0; i < mIns.Size(); i++) + mIns[i].mLive |= LIVE_CPU_REG_Y; + for (int i = 0; i < body->mIns.Size(); i++) + body->mIns[i].mLive |= LIVE_CPU_REG_Y; + + changed = true; + } + } + } + else if (!ReferencesXReg() && body->mIns.Size() > 0 && body->mIns[0].mType == ASMIT_LDX && body->mIns[0].mMode == ASMIM_ZERO_PAGE && !mExitRequiredRegs[CPU_REG_X]) + { + int incdec = 0; + bool fail = false; + int addr = body->mIns[0].mAddress; + if (!ChangesZeroPage(addr)) + { + bool references = ReferencesZeroPage(addr); + + for (int i = 1; i < body->mIns.Size(); i++) + { + const NativeCodeInstruction& bins(body->mIns[i]); + if (bins.mMode == ASMIM_ZERO_PAGE && bins.mAddress == addr) + { + if (bins.mType == ASMIT_INC) + incdec++; + else if (bins.mType == ASMIT_DEC) + incdec--; + else + { + fail = true; + break; + } + } + else if (bins.ReferencesZeroPage(addr)) + fail = true; + else if (bins.ChangesXReg()) + fail = true; + } + + if (!fail) + { + pred->mIns.Push(NativeCodeInstruction(body->mIns[0].mIns, ASMIT_LDX, ASMIM_ZERO_PAGE, addr)); + body->mIns.Remove(0); + while (incdec > 0) + { + body->mIns.Push(NativeCodeInstruction(body->mIns[0].mIns, ASMIT_INX)); + incdec--; + } + while (incdec < 0) + { + body->mIns.Push(NativeCodeInstruction(body->mIns[0].mIns, ASMIT_DEX)); + incdec++; + + } + + for (int i = 0; i < body->mIns.Size(); i++) + { + if (body->mIns[i].mMode == ASMIM_ZERO_PAGE && body->mIns[i].mAddress == addr) + { + body->mIns[i].mType = ASMIT_NOP; body->mIns[i].mMode = ASMIM_IMPLIED; + } + } + + if (references) + body->mIns.Push(NativeCodeInstruction(body->mIns[0].mIns, ASMIT_STX, ASMIM_ZERO_PAGE, addr)); + else + succ->mIns.Insert(0, NativeCodeInstruction(body->mIns[0].mIns, ASMIT_STX, ASMIM_ZERO_PAGE, addr)); + + body->mEntryRequiredRegs += CPU_REG_X; + body->mExitRequiredRegs += CPU_REG_X; + mEntryRequiredRegs += CPU_REG_X; + mExitRequiredRegs += CPU_REG_X; + succ->mEntryRequiredRegs += CPU_REG_X; + + for (int i = 0; i < mIns.Size(); i++) + mIns[i].mLive |= LIVE_CPU_REG_X; + for (int i = 0; i < body->mIns.Size(); i++) + body->mIns[i].mLive |= LIVE_CPU_REG_X; + + changed = true; + } + } + } + } + } + } + } + + CheckLive(); + + if (mTrueJump && mTrueJump->OptimizeFindLoop(proc)) + changed = true; + if (mFalseJump && mFalseJump->OptimizeFindLoop(proc)) + changed = true; + } + + return changed; +} + +bool NativeCodeBasicBlock::OptimizeSelect(NativeCodeProcedure* proc) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + CheckLive(); + + assert(mBranch != ASMIT_JMP || mFalseJump == nullptr); + + if (mFalseJump && mIns.Size() > 0 && mIns.Last().ChangesAccuAndFlag() && + mTrueJump->mIns.Size() == 1 && mFalseJump->mIns.Size() == 1 && + !mTrueJump->mFalseJump && !mFalseJump->mFalseJump && mTrueJump->mTrueJump == mFalseJump->mTrueJump && + mTrueJump->mIns[0].mType == ASMIT_LDA && mTrueJump->mIns[0].mMode == ASMIM_IMMEDIATE && + mFalseJump->mIns[0].mType == ASMIT_LDA && mFalseJump->mIns[0].mMode == ASMIM_IMMEDIATE) + { + if (mBranch == ASMIT_BNE || mBranch == ASMIT_BEQ) + { + uint8 vt = mTrueJump->mIns[0].mAddress, vf = mFalseJump->mIns[0].mAddress; + mTrueJump = mTrueJump->mTrueJump; + mFalseJump = nullptr; + + if (mBranch == ASMIT_BEQ) + { + char t = vt; vt = vf; vf = t; + } + + mIns[mIns.Size() - 1].mLive |= LIVE_CPU_REG_A; + + mIns.Push(NativeCodeInstruction(mBranchIns, ASMIT_CMP, ASMIM_IMMEDIATE, 1)); + mIns.Push(NativeCodeInstruction(mBranchIns, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + mIns.Push(NativeCodeInstruction(mBranchIns, ASMIT_ADC, ASMIM_IMMEDIATE, 0xff)); + mIns.Push(NativeCodeInstruction(mBranchIns, ASMIT_AND, ASMIM_IMMEDIATE, vt ^ vf)); + mIns.Push(NativeCodeInstruction(mBranchIns, ASMIT_EOR, ASMIM_IMMEDIATE, vt)); + changed = true; + + mBranch = ASMIT_JMP; + } + } + + CheckLive(); + + assert(mBranch != ASMIT_JMP || mFalseJump == nullptr); + + if (mTrueJump && mTrueJump->OptimizeSelect(proc)) + changed = true; + if (mFalseJump && mFalseJump->OptimizeSelect(proc)) + changed = true; + } + + return changed; +} + +static bool CheckBlockCopySequence(const ExpandingArray& ins, int si, int n) +{ + if (si + 2 * n <= ins.Size() && + ins[si + 0].mType == ASMIT_LDA && (ins[si + 0].mMode == ASMIM_ZERO_PAGE || ins[si + 0].mMode == ASMIM_ABSOLUTE) && + ins[si + 1].mType == ASMIT_STA && (ins[si + 1].mMode == ASMIM_ZERO_PAGE || ins[si + 1].mMode == ASMIM_ABSOLUTE)) + { + for (int i = 1; i < n; i++) + { + if (!(ins[si + 2 * i + 0].mType == ASMIT_LDA && ins[si + 2 * i + 0].mMode == ins[si + 0].mMode && ins[si + 2 * i + 0].mAddress == ins[si + 0].mAddress + i && + ins[si + 2 * i + 1].mType == ASMIT_STA && ins[si + 2 * i + 1].mMode == ins[si + 1].mMode && ins[si + 2 * i + 1].mAddress == ins[si + 1].mAddress + i)) + return false; + } + if (ins[si + 2 * n - 1].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z)) + return false; + + return true; + } + else + return false; +} + +void NativeCodeBasicBlock::BranchAccuAlternativeElimination(void) +{ + if (!mVisited) + { + mVisited = true; + if (mTrueJump && mFalseJump && (mBranch == ASMIT_BEQ || mBranch == ASMIT_BNE) && + mTrueJump->mEntryBlocks.Size() == 1 && mFalseJump->mEntryBlocks.Size() == 1 && + mIns.Size() > 0 && mIns[mIns.Size() - 1].ChangesAccuAndFlag() && + !mTrueJump->mFalseJump && !mFalseJump->mFalseJump && mTrueJump->mTrueJump == mFalseJump->mTrueJump && + mTrueJump->mIns.Size() == 1 && mFalseJump->mIns.Size() == 1 && + mTrueJump->mIns[0].mType == ASMIT_LDA && mTrueJump->mIns[0].mMode == ASMIM_IMMEDIATE && + mFalseJump->mIns[0].mType == ASMIT_LDA && mFalseJump->mIns[0].mMode == ASMIM_IMMEDIATE) + { + NativeCodeBasicBlock* eblock = mTrueJump->mTrueJump; + if (eblock->mEntryBlocks.Size() == 2) + { + if (mBranch == ASMIT_BEQ) + { + mTrueJump->mIns[0].mType = ASMIT_EOR; + mFalseJump->mIns[0].mAddress ^= mTrueJump->mIns[0].mAddress; + eblock->mEntryBlocks.RemoveAll(mFalseJump); + eblock->mNumEntries--; + mFalseJump->mTrueJump = mTrueJump; + mTrueJump->mNumEntries++; + mTrueJump->mEntryBlocks.Push(mFalseJump); + mTrueJump->mEntryRequiredRegs += CPU_REG_A; + } + else + { + mFalseJump->mIns[0].mType = ASMIT_EOR; + mTrueJump->mIns[0].mAddress ^= mFalseJump->mIns[0].mAddress; + eblock->mEntryBlocks.RemoveAll(mTrueJump); + eblock->mNumEntries--; + mTrueJump->mTrueJump = mFalseJump; + mFalseJump->mNumEntries++; + mFalseJump->mEntryBlocks.Push(mTrueJump); + mFalseJump->mEntryRequiredRegs += CPU_REG_A; + } + + mIns[mIns.Size() - 1].mLive |= LIVE_CPU_REG_A; + mExitRequiredRegs += CPU_REG_A; + } + } + + if (mTrueJump) mTrueJump->BranchAccuAlternativeElimination(); + if (mFalseJump) mFalseJump->BranchAccuAlternativeElimination(); + } +} + +bool NativeCodeBasicBlock::BlockSizeCopyReduction(NativeCodeProcedure* proc, int& si, int& di) +{ + if ((proc->mCompilerOptions & COPT_OPTIMIZE_CODE_SIZE)) + { + if (si + 1 < mIns.Size() && + mIns[si + 0].mType == ASMIT_LDA && (mIns[si + 0].mMode == ASMIM_ZERO_PAGE || mIns[si + 0].mMode == ASMIM_ABSOLUTE) && + mIns[si + 1].mType == ASMIT_STA && (mIns[si + 1].mMode == ASMIM_ZERO_PAGE || mIns[si + 1].mMode == ASMIM_ABSOLUTE)) + { + int i = 1; + while (si + 2 * i + 1 < mIns.Size() && + mIns[si + 2 * i + 0].mType == ASMIT_LDA && mIns[si + 2 * i + 0].mMode == mIns[si + 0].mMode && mIns[si + 2 * i + 0].mAddress == mIns[si + 0].mAddress + i && + mIns[si + 2 * i + 1].mType == ASMIT_STA && mIns[si + 2 * i + 1].mMode == mIns[si + 1].mMode && mIns[si + 2 * i + 1].mAddress == mIns[si + 1].mAddress + i && + !(mIns[si + 2 * i + 1].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z))) + { + i++; + } + + if (i > 2) + { + if (!(mIns[si + 0].mLive & LIVE_CPU_REG_X)) + { + int k = 1; + while (CheckBlockCopySequence(mIns, si + 2 * k * i, i)) + k++; + + if (k == 1 && i == 4 && mIns[si + 0].mMode == ASMIM_ZERO_PAGE && mIns[si + 1].mMode == ASMIM_ZERO_PAGE) + { + if (mIns[si + 0].mAddress == BC_REG_ACCU) + { + NativeCodeGenerator::Runtime& frt(mProc->mGenerator->ResolveRuntime(Ident::Unique("store32"))); + mIns[di++] = NativeCodeInstruction(mIns[si + 0].mIns, ASMIT_JSR, ASMIM_ABSOLUTE, frt.mOffset, frt.mLinkerObject, NCIF_RUNTIME | NCIF_LOWER | NCIF_UPPER | NCIF_USE_ZP_32_X, mIns[si + 1].mAddress); + si += 8; + return true; + } + else if (mIns[si + 1].mAddress == BC_REG_ACCU) + { + NativeCodeGenerator::Runtime& frt(mProc->mGenerator->ResolveRuntime(Ident::Unique("load32"))); + mIns[di++] = NativeCodeInstruction(mIns[si + 0].mIns, ASMIT_JSR, ASMIM_ABSOLUTE, frt.mOffset, frt.mLinkerObject, NCIF_RUNTIME | NCIF_LOWER | NCIF_UPPER | NCIF_USE_ZP_32_X, mIns[si + 0].mAddress); + si += 8; + return true; + } + } + + int sz = 3 + 4 * k; + for (int j = 0; j < k; j++) + { + NativeCodeInstruction lins = mIns[si + 2 * i * j + 0]; + NativeCodeInstruction sins = mIns[si + 2 * i * j + 1]; + + if (lins.mMode == ASMIM_ZERO_PAGE || (lins.mLinkerObject && (lins.mLinkerObject->mFlags & LOBJF_ZEROPAGE))) + lins.mMode = ASMIM_ZERO_PAGE_X; + else + { + lins.mMode = ASMIM_ABSOLUTE_X; + sz++; + } + if (sins.mMode == ASMIM_ZERO_PAGE || (sins.mLinkerObject && (sins.mLinkerObject->mFlags & LOBJF_ZEROPAGE))) + sins.mMode = ASMIM_ZERO_PAGE_X; + else + { + sins.mMode = ASMIM_ABSOLUTE_X; + sz++; + } + + if (j == 0) + mIns[di++] = NativeCodeInstruction(lins.mIns, ASMIT_LDX, ASMIM_IMMEDIATE, i - 1); + + mIns[di++] = lins; + mIns[di++] = sins; + } + + mIns[di++] = NativeCodeInstruction(mIns[si + 0].mIns, ASMIT_DEX, ASMIM_IMPLIED); + mIns[di++] = NativeCodeInstruction(mIns[si + 0].mIns, ASMIT_BPL, ASMIM_RELATIVE, -sz); + + si += 2 * i * k; + + return true; + } + } + + } + if (si + 2 < mIns.Size() && + mIns[si + 0].mType == ASMIT_LDA && (mIns[si + 0].mMode == ASMIM_ZERO_PAGE || mIns[si + 0].mMode == ASMIM_ABSOLUTE) && + mIns[si + 1].mType == ASMIT_STA && (mIns[si + 1].mMode == ASMIM_ZERO_PAGE || mIns[si + 1].mMode == ASMIM_ABSOLUTE) && + mIns[si + 1].mType == ASMIT_STA && (mIns[si + 1].mMode == ASMIM_ZERO_PAGE || mIns[si + 1].mMode == ASMIM_ABSOLUTE)) + { + int i = 1; + while (si + 3 * i + 2 < mIns.Size() && + mIns[si + 3 * i + 0].mType == ASMIT_LDA && mIns[si + 3 * i + 0].mMode == mIns[si + 0].mMode && mIns[si + 3 * i + 0].mAddress == mIns[si + 0].mAddress + i && + mIns[si + 3 * i + 1].mType == ASMIT_STA && mIns[si + 3 * i + 1].mMode == mIns[si + 1].mMode && mIns[si + 3 * i + 1].mAddress == mIns[si + 1].mAddress + i && + mIns[si + 3 * i + 2].mType == ASMIT_STA && mIns[si + 3 * i + 2].mMode == mIns[si + 2].mMode && mIns[si + 3 * i + 2].mAddress == mIns[si + 2].mAddress + i && + !(mIns[si + 3 * i + 2].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z))) + { + i++; + } + + if (i > 2) + { + if (!(mIns[si + 0].mLive & LIVE_CPU_REG_X)) + { + NativeCodeInstruction lins = mIns[si + 0]; + NativeCodeInstruction sins0 = mIns[si + 1]; + NativeCodeInstruction sins1 = mIns[si + 2]; + + int sz = 9; + if (lins.mMode == ASMIM_ZERO_PAGE) + lins.mMode = ASMIM_ZERO_PAGE_X; + else + { + lins.mMode = ASMIM_ABSOLUTE_X; + sz++; + } + if (sins0.mMode == ASMIM_ZERO_PAGE) + sins0.mMode = ASMIM_ZERO_PAGE_X; + else + { + sins0.mMode = ASMIM_ABSOLUTE_X; + sz++; + } + if (sins1.mMode == ASMIM_ZERO_PAGE) + sins1.mMode = ASMIM_ZERO_PAGE_X; + else + { + sins1.mMode = ASMIM_ABSOLUTE_X; + sz++; + } + + mIns[di++] = NativeCodeInstruction(lins.mIns, ASMIT_LDX, ASMIM_IMMEDIATE, i - 1); + mIns[di++] = lins; + mIns[di++] = sins0; + mIns[di++] = sins1; + mIns[di++] = NativeCodeInstruction(lins.mIns, ASMIT_DEX, ASMIM_IMPLIED); + mIns[di++] = NativeCodeInstruction(lins.mIns, ASMIT_BPL, ASMIM_RELATIVE, -sz); + + si += 3 * i; + + return true; + } + } + + } + +#if 1 + if (si + 5 < mIns.Size() && + mIns[si + 0].mType == ASMIT_LDY && mIns[si + 0].mMode == ASMIM_IMMEDIATE && + mIns[si + 1].mType == ASMIT_STA && mIns[si + 1].mMode == ASMIM_INDIRECT_Y && + !(mIns[si + 1].mLive & LIVE_CPU_REG_C)) + { + int i = 1; + while (si + 2 * i + 1 < mIns.Size() && + mIns[si + 2 * i + 0].mType == ASMIT_LDY && + mIns[si + 2 * i + 0].mMode == ASMIM_IMMEDIATE && + mIns[si + 2 * i + 0].mAddress == mIns[si + 0].mAddress + i && + mIns[si + 2 * i + 1].mType == ASMIT_STA && + mIns[si + 2 * i + 1].mMode == ASMIM_INDIRECT_Y && + mIns[si + 2 * i + 1].mAddress == mIns[si + 1].mAddress) + { + i++; + } + + if (i > 2) + { + int k = mIns[si + 0].mAddress + i - 1; + mIns[di + 0] = NativeCodeInstruction(mIns[si + 0].mIns, ASMIT_LDY, ASMIM_IMMEDIATE, mIns[si + 0].mAddress - 1); + mIns[di + 2] = mIns[si + 1]; + mIns[di + 1] = NativeCodeInstruction(mIns[si + 0].mIns, ASMIT_INY); + mIns[di + 3] = NativeCodeInstruction(mIns[si + 0].mIns, ASMIT_CPY, ASMIM_IMMEDIATE, k); + mIns[di + 4] = NativeCodeInstruction(mIns[si + 0].mIns, ASMIT_BNE, ASMIM_RELATIVE, -7); + di += 5; + si += 2 * i; + if (si == mIns.Size()) + { + mNDataSet.Set(CPU_REG_C, NRDM_IMMEDIATE, 1); + mNDataSet.Set(CPU_REG_Z, NRDM_IMMEDIATE, 0); + } + return true; + } + } +#endif + } + + return false; +} + +// Size reduction violating various assumptions such as no branches in basic blocks +// must be last step before actual assembly + +void NativeCodeBasicBlock::BlockSizeReduction(NativeCodeProcedure* proc, int xenter, int yenter, int center) +{ + if (!mVisited) + { + mVisited = true; + + CheckLive(); + + int i = 0; + int j = 0; + + if (mEntryBlocks.Size() == 1 && + mEntryBlocks[0]->mIns.Size() > 0 && mIns.Size() > 0 && + mIns[0].mType == ASMIT_CMP && mIns[0].mMode == ASMIM_IMMEDIATE && + mEntryBlocks[0]->mIns.Last().mType == ASMIT_CMP && mEntryBlocks[0]->mIns.Last().mMode == ASMIM_IMMEDIATE && + mIns[0].mAddress == mEntryBlocks[0]->mIns.Last().mAddress) + { + // Skip initial compare if same as last of entry block + i++; + } + + while (i < mIns.Size()) + { + if (mIns[i].mType == ASMIT_NOP) + { + i++; + } + else if (i + 6 < mIns.Size() && + mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 1 && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && mIns[i + 3].mAddress == mIns[i + 1].mAddress && + mIns[i + 4].mType == ASMIT_LDA && mIns[i + 4].mMode == ASMIM_ZERO_PAGE && + mIns[i + 5].mType == ASMIT_ADC && mIns[i + 5].mMode == ASMIM_IMMEDIATE && mIns[i + 5].mAddress == 0 && + mIns[i + 6].mType == ASMIT_STA && mIns[i + 6].mMode == ASMIM_ZERO_PAGE && mIns[i + 6].mAddress == mIns[i + 4].mAddress && + !(mIns[i + 6].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C | LIVE_CPU_REG_Z))) + { + mIns[j + 0].mType = ASMIT_INC; mIns[j + 0].mMode = ASMIM_ZERO_PAGE; mIns[j + 0].mAddress = mIns[i + 1].mAddress; + mIns[j + 1].mType = ASMIT_BNE; mIns[j + 1].mMode = ASMIM_RELATIVE; mIns[j + 1].mAddress = 2; + mIns[j + 2].mType = ASMIT_INC; mIns[j + 2].mMode = ASMIM_ZERO_PAGE; mIns[j + 2].mAddress = mIns[i + 4].mAddress; + j += 3; + i += 7; + } + else if (i + 6 < mIns.Size() && + mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 0xff && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && mIns[i + 3].mAddress == mIns[i + 1].mAddress && + mIns[i + 4].mType == ASMIT_LDA && mIns[i + 4].mMode == ASMIM_ZERO_PAGE && + mIns[i + 5].mType == ASMIT_ADC && mIns[i + 5].mMode == ASMIM_IMMEDIATE && mIns[i + 5].mAddress == 0xff && + mIns[i + 6].mType == ASMIT_STA && mIns[i + 6].mMode == ASMIM_ZERO_PAGE && mIns[i + 6].mAddress == mIns[i + 4].mAddress && + !(mIns[i + 6].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C | LIVE_CPU_REG_Z))) + { + mIns[j + 0].mType = ASMIT_LDA; mIns[j + 0].mMode = ASMIM_ZERO_PAGE; mIns[j + 0].mAddress = mIns[i + 1].mAddress; + mIns[j + 1].mType = ASMIT_BNE; mIns[j + 1].mMode = ASMIM_RELATIVE; mIns[j + 1].mAddress = 2; + mIns[j + 2].mType = ASMIT_DEC; mIns[j + 2].mMode = ASMIM_ZERO_PAGE; mIns[j + 2].mAddress = mIns[i + 4].mAddress; + mIns[j + 3].mType = ASMIT_DEC; mIns[j + 3].mMode = ASMIM_ZERO_PAGE; mIns[j + 3].mAddress = mIns[j + 0].mAddress; + j += 4; + i += 7; + } + else if (i + 6 < mIns.Size() && + mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_TXA && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 0xff && + mIns[i + 3].mType == ASMIT_TAX && + mIns[i + 4].mType == ASMIT_LDA && mIns[i + 4].mMode == ASMIM_ZERO_PAGE && + mIns[i + 5].mType == ASMIT_ADC && mIns[i + 5].mMode == ASMIM_IMMEDIATE && mIns[i + 5].mAddress == 0xff && + mIns[i + 6].mType == ASMIT_STA && mIns[i + 6].mMode == ASMIM_ZERO_PAGE && mIns[i + 6].mAddress == mIns[i + 4].mAddress && + !(mIns[i + 6].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C | LIVE_CPU_REG_Z))) + { + mIns[j + 0].mType = ASMIT_TXA; mIns[j + 0].mMode = ASMIM_IMPLIED; + mIns[j + 1].mType = ASMIT_BNE; mIns[j + 1].mMode = ASMIM_RELATIVE; mIns[j + 1].mAddress = 2; + mIns[j + 2].mType = ASMIT_DEC; mIns[j + 2].mMode = ASMIM_ZERO_PAGE; mIns[j + 2].mAddress = mIns[i + 4].mAddress; + mIns[j + 3].mType = ASMIT_DEX; mIns[j + 3].mMode = ASMIM_IMPLIED; + j += 4; + i += 7; + } + else if (i + 6 < mIns.Size() && + mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_ABSOLUTE && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 1 && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].mMode == ASMIM_ABSOLUTE && mIns[i + 3].SameEffectiveAddress(mIns[i + 1]) && + mIns[i + 4].mType == ASMIT_LDA && mIns[i + 4].mMode == ASMIM_ABSOLUTE && + mIns[i + 5].mType == ASMIT_ADC && mIns[i + 5].mMode == ASMIM_IMMEDIATE && mIns[i + 5].mAddress == 0 && + mIns[i + 6].mType == ASMIT_STA && mIns[i + 6].mMode == ASMIM_ABSOLUTE && mIns[i + 6].SameEffectiveAddress(mIns[i + 4]) && + !(mIns[i + 6].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C | LIVE_CPU_REG_Z))) + { + mIns[j + 0].mType = ASMIT_INC; mIns[j + 0].mMode = ASMIM_ABSOLUTE; mIns[j + 0].mAddress = mIns[i + 1].mAddress; mIns[j + 0].mLinkerObject = mIns[i + 1].mLinkerObject; + mIns[j + 1].mType = ASMIT_BNE; mIns[j + 1].mMode = ASMIM_RELATIVE; + if (mIns[i + 4].mLinkerObject->mFlags & LOBJF_ZEROPAGE) + mIns[j + 1].mAddress = 2; + else + mIns[j + 1].mAddress = 3; + mIns[j + 2].mType = ASMIT_INC; mIns[j + 2].mMode = ASMIM_ABSOLUTE; mIns[j + 2].mAddress = mIns[i + 4].mAddress; mIns[j + 2].mLinkerObject = mIns[i + 4].mLinkerObject; + j += 3; + i += 7; + } + else if (i + 6 < mIns.Size() && + mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_ABSOLUTE && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 0xff && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].mMode == ASMIM_ABSOLUTE && mIns[i + 3].SameEffectiveAddress(mIns[i + 1]) && + mIns[i + 4].mType == ASMIT_LDA && mIns[i + 4].mMode == ASMIM_ABSOLUTE && + mIns[i + 5].mType == ASMIT_ADC && mIns[i + 5].mMode == ASMIM_IMMEDIATE && mIns[i + 5].mAddress == 0xff && + mIns[i + 6].mType == ASMIT_STA && mIns[i + 6].mMode == ASMIM_ABSOLUTE && mIns[i + 6].SameEffectiveAddress(mIns[i + 4]) && + !(mIns[i + 6].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C | LIVE_CPU_REG_Z))) + { + mIns[j + 0].mType = ASMIT_LDA; mIns[j + 0].mMode = ASMIM_ABSOLUTE; mIns[j + 0].mAddress = mIns[i + 1].mAddress; mIns[j + 0].mLinkerObject = mIns[i + 1].mLinkerObject; + mIns[j + 1].mType = ASMIT_BNE; mIns[j + 1].mMode = ASMIM_RELATIVE; + if (mIns[i + 4].mLinkerObject->mFlags & LOBJF_ZEROPAGE) + mIns[j + 1].mAddress = 2; + else + mIns[j + 1].mAddress = 3; + mIns[j + 2].mType = ASMIT_DEC; mIns[j + 2].mMode = ASMIM_ABSOLUTE; mIns[j + 2].mAddress = mIns[i + 4].mAddress; mIns[j + 2].mLinkerObject = mIns[i + 4].mLinkerObject; + mIns[j + 3].mType = ASMIT_DEC; mIns[j + 3].mMode = ASMIM_ABSOLUTE; mIns[j + 3].mAddress = mIns[j + 0].mAddress; mIns[j + 3].mLinkerObject = mIns[j + 0].mLinkerObject; + j += 4; + i += 7; + } + else if (i + 6 < mIns.Size() && + mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_ABSOLUTE && + mIns[i + 1].mType == ASMIT_CLC && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 1 && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].mMode == ASMIM_ABSOLUTE && mIns[i + 3].SameEffectiveAddress(mIns[i + 0]) && + mIns[i + 4].mType == ASMIT_LDA && mIns[i + 4].mMode == ASMIM_ABSOLUTE && + mIns[i + 5].mType == ASMIT_ADC && mIns[i + 5].mMode == ASMIM_IMMEDIATE && mIns[i + 5].mAddress == 0 && + mIns[i + 6].mType == ASMIT_STA && mIns[i + 6].mMode == ASMIM_ABSOLUTE && mIns[i + 6].SameEffectiveAddress(mIns[i + 4]) && + !(mIns[i + 6].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C | LIVE_CPU_REG_Z))) + { + mIns[j + 0].mType = ASMIT_INC; mIns[j + 0].mMode = ASMIM_ABSOLUTE; mIns[j + 0].mAddress = mIns[i + 0].mAddress; mIns[j + 0].mLinkerObject = mIns[i + 0].mLinkerObject; + mIns[j + 1].mType = ASMIT_BNE; mIns[j + 1].mMode = ASMIM_RELATIVE; + if (mIns[i + 4].mLinkerObject->mFlags & LOBJF_ZEROPAGE) + mIns[j + 1].mAddress = 2; + else + mIns[j + 1].mAddress = 3; + mIns[j + 2].mType = ASMIT_INC; mIns[j + 2].mMode = ASMIM_ABSOLUTE; mIns[j + 2].mAddress = mIns[i + 4].mAddress; mIns[j + 2].mLinkerObject = mIns[i + 4].mLinkerObject; + j += 3; + i += 7; + } + else if (i + 6 < mIns.Size() && + mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_ABSOLUTE && + mIns[i + 1].mType == ASMIT_CLC && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 0xff && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].mMode == ASMIM_ABSOLUTE && mIns[i + 3].SameEffectiveAddress(mIns[i + 0]) && + mIns[i + 4].mType == ASMIT_LDA && mIns[i + 4].mMode == ASMIM_ABSOLUTE && + mIns[i + 5].mType == ASMIT_ADC && mIns[i + 5].mMode == ASMIM_IMMEDIATE && mIns[i + 5].mAddress == 0xff && + mIns[i + 6].mType == ASMIT_STA && mIns[i + 6].mMode == ASMIM_ABSOLUTE && mIns[i + 6].SameEffectiveAddress(mIns[i + 4]) && + !(mIns[i + 6].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C | LIVE_CPU_REG_Z))) + { + mIns[j + 0].mType = ASMIT_LDA; mIns[j + 0].mMode = ASMIM_ABSOLUTE; mIns[j + 0].mAddress = mIns[i + 0].mAddress; mIns[j + 0].mLinkerObject = mIns[i + 0].mLinkerObject; + mIns[j + 1].mType = ASMIT_BNE; mIns[j + 1].mMode = ASMIM_RELATIVE; + if (mIns[i + 4].mLinkerObject->mFlags & LOBJF_ZEROPAGE) + mIns[j + 1].mAddress = 2; + else + mIns[j + 1].mAddress = 3; + mIns[j + 2].mType = ASMIT_DEC; mIns[j + 2].mMode = ASMIM_ABSOLUTE; mIns[j + 2].mAddress = mIns[i + 4].mAddress; mIns[j + 2].mLinkerObject = mIns[i + 4].mLinkerObject; + mIns[j + 3].mType = ASMIT_DEC; mIns[j + 3].mMode = ASMIM_ABSOLUTE; mIns[j + 3].mAddress = mIns[j + 0].mAddress; mIns[j + 2].mLinkerObject = mIns[j + 0].mLinkerObject; + j += 4; + i += 7; + } + else if (i + 4 < mIns.Size() && + mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_LDA && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 0x01 && + mIns[i + 3].mType == ASMIT_CLC && + mIns[i + 4].mType == ASMIT_ADC && !(mIns[i + 4].mLive & LIVE_CPU_REG_C | LIVE_CPU_REG_Z)) + { + mIns[j + 0] = mIns[i + 0]; mIns[j + 0].mType = ASMIT_SEC; + mIns[j + 1] = mIns[i + 1]; + mIns[j + 2] = mIns[i + 4]; + j += 3; + i += 5; + } + else if (i + 2 < mIns.Size() && + mIns[i + 0].mType == ASMIT_LDX && HasAsmInstructionMode(ASMIT_INC, mIns[i + 0].mMode) && + mIns[i + 1].mType == ASMIT_INX && + mIns[i + 2].mType == ASMIT_STX && mIns[i + 2].SameEffectiveAddress(mIns[i + 0]) && + !(mIns[i + 2].mLive & LIVE_CPU_REG_X)) + { + mIns[j + 0] = mIns[i + 0]; + mIns[j + 0].mType = ASMIT_INC; + j += 1; + i += 3; + } + else if (i + 2 < mIns.Size() && + mIns[i + 0].mType == ASMIT_LDX && HasAsmInstructionMode(ASMIT_DEC, mIns[i + 0].mMode) && + mIns[i + 1].mType == ASMIT_DEX && + mIns[i + 2].mType == ASMIT_STX && mIns[i + 2].SameEffectiveAddress(mIns[i + 0]) && + !(mIns[i + 2].mLive & LIVE_CPU_REG_X)) + { + mIns[j + 0] = mIns[i + 0]; + mIns[j + 0].mType = ASMIT_DEC; + j += 1; + i += 3; + } + else if (i + 2 < mIns.Size() && + mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_ADC && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 0 && + mIns[i + 2].mType == ASMIT_STA && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && mIns[i + 2].mAddress == mIns[i + 0].mAddress && + !(mIns[i + 2].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C | LIVE_CPU_REG_Z))) + { + mIns[j + 0].mType = ASMIT_BCC; mIns[j + 0].mMode = ASMIM_RELATIVE; mIns[j + 0].mAddress = 2; + mIns[j + 1].mType = ASMIT_INC; mIns[j + 1].mMode = ASMIM_ZERO_PAGE; mIns[j + 1].mAddress = mIns[i + 2].mAddress; + j += 2; + i += 3; + } + else if (i + 2 < mIns.Size() && + mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_ABSOLUTE && + mIns[i + 1].mType == ASMIT_ADC && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 0 && + mIns[i + 2].mType == ASMIT_STA && mIns[i + 2].mMode == ASMIM_ABSOLUTE && mIns[i + 2].SameEffectiveAddress(mIns[i + 0]) && + !(mIns[i + 2].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C | LIVE_CPU_REG_Z))) + { + mIns[j + 0].mType = ASMIT_BCC; mIns[j + 0].mMode = ASMIM_RELATIVE; + if (mIns[i + 0].mLinkerObject->mFlags & LOBJF_ZEROPAGE) + mIns[j + 0].mAddress = 2; + else + mIns[j + 0].mAddress = 3; + mIns[j + 1] = mIns[i + 2]; + mIns[j + 1].mType = ASMIT_INC; + j += 2; + i += 3; + } + else if (i + 2 < mIns.Size() && + mIns[i + 0].mType == ASMIT_TXA && + mIns[i + 1].mType == ASMIT_ADC && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 0 && + mIns[i + 2].mType == ASMIT_TAX && + !(mIns[i + 2].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C | LIVE_CPU_REG_Z))) + { + mIns[j + 0].mType = ASMIT_BCC; mIns[j + 0].mMode = ASMIM_RELATIVE; mIns[j + 0].mAddress = 1; + mIns[j + 1].mType = ASMIT_INX; mIns[j + 1].mMode = ASMIM_IMPLIED; + j += 2; + i += 3; + } + else if (i + 2 < mIns.Size() && + mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_SBC && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 0 && + mIns[i + 2].mType == ASMIT_STA && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && mIns[i + 2].mAddress == mIns[i + 0].mAddress && + !(mIns[i + 2].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C | LIVE_CPU_REG_Z))) + { + mIns[j + 0].mType = ASMIT_BCS; mIns[j + 0].mMode = ASMIM_RELATIVE; mIns[j + 0].mAddress = 2; + mIns[j + 1].mType = ASMIT_DEC; mIns[j + 1].mMode = ASMIM_ZERO_PAGE; mIns[j + 1].mAddress = mIns[i + 2].mAddress; + j += 2; + i += 3; + } + else if (i + 2 < mIns.Size() && + mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_ABSOLUTE && + mIns[i + 1].mType == ASMIT_SBC && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 0 && + mIns[i + 2].mType == ASMIT_STA && mIns[i + 2].mMode == ASMIM_ABSOLUTE && mIns[i + 2].SameEffectiveAddress(mIns[i + 0]) && + !(mIns[i + 2].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C | LIVE_CPU_REG_Z))) + { + mIns[j + 0].mType = ASMIT_BCS; mIns[j + 0].mMode = ASMIM_RELATIVE; + if (mIns[i + 0].mLinkerObject->mFlags & LOBJF_ZEROPAGE) + mIns[j + 0].mAddress = 2; + else + mIns[j + 0].mAddress = 3; + mIns[j + 1] = mIns[i + 2]; + mIns[j + 1].mType = ASMIT_DEC; + j += 2; + i += 3; + } + else if (i + 2 < mIns.Size() && + mIns[i + 0].mType == ASMIT_TXA && + mIns[i + 1].mType == ASMIT_SBC && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 0 && + mIns[i + 2].mType == ASMIT_TAX && + !(mIns[i + 2].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C | LIVE_CPU_REG_Z))) + { + mIns[j + 0].mType = ASMIT_BCS; mIns[j + 0].mMode = ASMIM_RELATIVE; mIns[j + 0].mAddress = 1; + mIns[j + 1].mType = ASMIT_DEX; mIns[j + 1].mMode = ASMIM_IMPLIED; + j += 2; + i += 3; + } +#if 1 + else if (i + 3 < mIns.Size() && + mIns[i + 0].mType == ASMIT_ASL && mIns[i + 0].mMode == ASMIM_IMPLIED && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 0 && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 0xff && + mIns[i + 3].mType == ASMIT_EOR && mIns[i + 3].mMode == ASMIM_IMMEDIATE && mIns[i + 3].mAddress == 0xff && + !(mIns[i + 2].mLive & (LIVE_CPU_REG_C | LIVE_CPU_REG_Z))) + { + mIns[j + 0].mType = ASMIT_AND; mIns[j + 0].mMode = ASMIM_IMMEDIATE; mIns[j + 0].mAddress = 0x80; + mIns[j + 1].mType = ASMIT_BPL; mIns[j + 1].mMode = ASMIM_RELATIVE; mIns[j + 1].mAddress = 2; + mIns[j + 2].mType = ASMIT_LDA; mIns[j + 2].mMode = ASMIM_IMMEDIATE; mIns[j + 2].mAddress = 0xff; + j += 3; + i += 4; + } +#endif + else if (i + 3 < mIns.Size() && + mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_LDA && HasAsmInstructionMode(ASMIT_INC, mIns[i + 1].mMode) && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 1 && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].SameEffectiveAddress(mIns[i + 1]) && + !(mIns[i + 3].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C))) + { + mIns[j + 0] = mIns[i + 1]; + mIns[j + 0].mType = ASMIT_INC; + j += 1; + i += 4; + } + else if (i + 3 < mIns.Size() && + mIns[i + 0].mType == ASMIT_SEC && + mIns[i + 1].mType == ASMIT_LDA && HasAsmInstructionMode(ASMIT_DEC, mIns[i + 1].mMode) && + mIns[i + 2].mType == ASMIT_SBC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 1 && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].SameEffectiveAddress(mIns[i + 1]) && + !(mIns[i + 3].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C))) + { + mIns[j + 0] = mIns[i + 1]; + mIns[j + 0].mType = ASMIT_DEC; + j += 1; + i += 4; + } + else if (i + 3 < mIns.Size() && + mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_LDA && HasAsmInstructionMode(ASMIT_INC, mIns[i + 1].mMode) && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 0xff && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].SameEffectiveAddress(mIns[i + 1]) && + !(mIns[i + 3].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C))) + { + mIns[j + 0] = mIns[i + 1]; + mIns[j + 0].mType = ASMIT_DEC; + j += 1; + i += 4; + } + else if (i + 3 < mIns.Size() && + mIns[i + 0].mType == ASMIT_SEC && + mIns[i + 1].mType == ASMIT_LDA && HasAsmInstructionMode(ASMIT_DEC, mIns[i + 1].mMode) && + mIns[i + 2].mType == ASMIT_SBC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 0xff && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].SameEffectiveAddress(mIns[i + 1]) && + !(mIns[i + 3].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C))) + { + mIns[j + 0] = mIns[i + 1]; + mIns[j + 0].mType = ASMIT_INC; + j += 1; + i += 4; + } + + else if (i + 3 < mIns.Size() && + mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_LDA && HasAsmInstructionMode(ASMIT_LDX, mIns[i + 1].mMode) && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 1 && + mIns[i + 3].mType == ASMIT_STA && HasAsmInstructionMode(ASMIT_STX, mIns[i + 3].mMode) && + !(mIns[i + 3].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C | LIVE_CPU_REG_Z | LIVE_CPU_REG_X))) + { + mIns[j + 0] = mIns[i + 1]; + mIns[j + 0].mType = ASMIT_LDX; + mIns[j + 1].mType = ASMIT_INX; mIns[j + 1].mMode = ASMIM_IMPLIED; + mIns[j + 2].mType = ASMIT_STX; mIns[j + 2].CopyMode(mIns[i + 3]); + j += 3; + i += 4; + } + else if (i + 3 < mIns.Size() && + mIns[i + 0].mType == ASMIT_SEC && + mIns[i + 1].mType == ASMIT_LDA && HasAsmInstructionMode(ASMIT_LDX, mIns[i + 1].mMode) && + mIns[i + 2].mType == ASMIT_SBC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 1 && + mIns[i + 3].mType == ASMIT_STA && HasAsmInstructionMode(ASMIT_STX, mIns[i + 3].mMode) && + !(mIns[i + 3].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C | LIVE_CPU_REG_Z | LIVE_CPU_REG_X))) + { + mIns[j + 0] = mIns[i + 1]; + mIns[j + 0].mType = ASMIT_LDX; + mIns[j + 1].mType = ASMIT_DEX; mIns[j + 1].mMode = ASMIM_IMPLIED; + mIns[j + 2].mType = ASMIT_STX; mIns[j + 2].CopyMode(mIns[i + 3]); + j += 3; + i += 4; + } + + else if (i + 3 < mIns.Size() && + mIns[i + 0].mType == ASMIT_LDA && HasAsmInstructionMode(ASMIT_LDX, mIns[i + 0].mMode) && + mIns[i + 1].mType == ASMIT_CLC && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 1 && + mIns[i + 3].mType == ASMIT_STA && HasAsmInstructionMode(ASMIT_STX, mIns[i + 3].mMode) && + !(mIns[i + 3].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C | LIVE_CPU_REG_Z | LIVE_CPU_REG_X))) + { + mIns[j + 0] = mIns[i + 0]; + mIns[j + 0].mType = ASMIT_LDX; + mIns[j + 1].mType = ASMIT_INX; mIns[j + 1].mMode = ASMIM_IMPLIED; + mIns[j + 2].mType = ASMIT_STX; mIns[j + 2].CopyMode(mIns[i + 3]); + j += 3; + i += 4; + } + else if (i + 3 < mIns.Size() && + mIns[i + 0].mType == ASMIT_LDA && HasAsmInstructionMode(ASMIT_LDX, mIns[i + 0].mMode) && + mIns[i + 1].mType == ASMIT_SEC && + mIns[i + 2].mType == ASMIT_SBC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 1 && + mIns[i + 3].mType == ASMIT_STA && HasAsmInstructionMode(ASMIT_STX, mIns[i + 3].mMode) && + !(mIns[i + 3].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C | LIVE_CPU_REG_Z | LIVE_CPU_REG_X))) + { + mIns[j + 0] = mIns[i + 0]; + mIns[j + 0].mType = ASMIT_LDX; + mIns[j + 1].mType = ASMIT_DEX; mIns[j + 1].mMode = ASMIM_IMPLIED; + mIns[j + 2].mType = ASMIT_STX; mIns[j + 2].CopyMode(mIns[i + 3]); + j += 3; + i += 4; + } + else if (i + 3 < mIns.Size() && + mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_LDA && HasAsmInstructionMode(ASMIT_LDX, mIns[i + 1].mMode) && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 0xff && + mIns[i + 3].mType == ASMIT_STA && HasAsmInstructionMode(ASMIT_STX, mIns[i + 3].mMode) && + !(mIns[i + 3].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C | LIVE_CPU_REG_Z | LIVE_CPU_REG_X))) + { + mIns[j + 0] = mIns[i + 1]; + mIns[j + 0].mType = ASMIT_LDX; + mIns[j + 1].mType = ASMIT_DEX; mIns[j + 1].mMode = ASMIM_IMPLIED; + mIns[j + 2].mType = ASMIT_STX; mIns[j + 2].CopyMode(mIns[i + 3]); + j += 3; + i += 4; + } + else if (i + 3 < mIns.Size() && + mIns[i + 0].mType == ASMIT_SEC && + mIns[i + 1].mType == ASMIT_LDA && HasAsmInstructionMode(ASMIT_LDX, mIns[i + 1].mMode) && + mIns[i + 2].mType == ASMIT_SBC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 0xff && + mIns[i + 3].mType == ASMIT_STA && HasAsmInstructionMode(ASMIT_STX, mIns[i + 3].mMode) && + !(mIns[i + 3].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C | LIVE_CPU_REG_Z | LIVE_CPU_REG_X))) + { + mIns[j + 0] = mIns[i + 1]; + mIns[j + 0].mType = ASMIT_LDX; + mIns[j + 1].mType = ASMIT_INX; mIns[j + 1].mMode = ASMIM_IMPLIED; + mIns[j + 2].mType = ASMIT_STX; mIns[j + 2].CopyMode(mIns[i + 3]); + j += 3; + i += 4; + } + + else if (i + 3 < mIns.Size() && + mIns[i + 0].mType == ASMIT_LDA && HasAsmInstructionMode(ASMIT_LDX, mIns[i + 0].mMode) && + mIns[i + 1].mType == ASMIT_CLC && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 2 && + mIns[i + 3].mType == ASMIT_STA && HasAsmInstructionMode(ASMIT_STX, mIns[i + 3].mMode) && + !(mIns[i + 3].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C | LIVE_CPU_REG_Z | LIVE_CPU_REG_X))) + { + mIns[j + 0] = mIns[i + 0]; + mIns[j + 0].mType = ASMIT_LDX; + mIns[j + 1].mType = ASMIT_INX; mIns[j + 1].mMode = ASMIM_IMPLIED; + mIns[j + 2].mType = ASMIT_INX; mIns[j + 2].mMode = ASMIM_IMPLIED; + mIns[j + 3].mType = ASMIT_STX; mIns[j + 3].CopyMode(mIns[i + 3]); + j += 4; + i += 4; + } + else if (i + 3 < mIns.Size() && + mIns[i + 0].mType == ASMIT_LDA && HasAsmInstructionMode(ASMIT_LDX, mIns[i + 0].mMode) && + mIns[i + 1].mType == ASMIT_SEC && + mIns[i + 2].mType == ASMIT_SBC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 2 && + mIns[i + 3].mType == ASMIT_STA && HasAsmInstructionMode(ASMIT_STX, mIns[i + 3].mMode) && + !(mIns[i + 3].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C | LIVE_CPU_REG_Z | LIVE_CPU_REG_X))) + { + mIns[j + 0] = mIns[i + 0]; + mIns[j + 0].mType = ASMIT_LDX; + mIns[j + 1].mType = ASMIT_DEX; mIns[j + 1].mMode = ASMIM_IMPLIED; + mIns[j + 2].mType = ASMIT_DEX; mIns[j + 2].mMode = ASMIM_IMPLIED; + mIns[j + 3].mType = ASMIT_STX; mIns[j + 3].CopyMode(mIns[i + 3]); + j += 4; + i += 4; + } + + else if (i + 3 < mIns.Size() && + mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 2 && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && mIns[i + 1].mAddress == mIns[i + 3].mAddress && + !(mIns[i + 3].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C | LIVE_CPU_REG_Z))) + { + mIns[j + 0].mType = ASMIT_INC; mIns[j + 0].mMode = ASMIM_ZERO_PAGE; mIns[j + 0].mAddress = mIns[i + 1].mAddress; mIns[j + 0].mLinkerObject = nullptr; + mIns[j + 1].mType = ASMIT_INC; mIns[j + 1].mMode = ASMIM_ZERO_PAGE; mIns[j + 1].mAddress = mIns[i + 3].mAddress; mIns[j + 1].mLinkerObject = nullptr; + j += 2; + i += 4; + } + else if (i + 3 < mIns.Size() && + mIns[i + 0].mType == ASMIT_SEC && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + mIns[i + 2].mType == ASMIT_SBC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 2 && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && mIns[i + 1].mAddress == mIns[i + 3].mAddress && + !(mIns[i + 3].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C | LIVE_CPU_REG_Z))) + { + mIns[j + 0].mType = ASMIT_DEC; mIns[j + 0].mMode = ASMIM_ZERO_PAGE; mIns[j + 0].mAddress = mIns[i + 1].mAddress; mIns[j + 0].mLinkerObject = nullptr; + mIns[j + 1].mType = ASMIT_DEC; mIns[j + 1].mMode = ASMIM_ZERO_PAGE; mIns[j + 1].mAddress = mIns[i + 3].mAddress; mIns[j + 1].mLinkerObject = nullptr; + j += 2; + i += 4; + } + + else if (i + 3 < mIns.Size() && + mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_ABSOLUTE && + mIns[i + 1].mType == ASMIT_CLC && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 2 && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].mMode == ASMIM_ABSOLUTE && mIns[i + 0].mAddress == mIns[i + 3].mAddress && + !(mIns[i + 3].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C | LIVE_CPU_REG_Z))) + { + mIns[j + 0].mType = ASMIT_INC; mIns[j + 0].CopyMode(mIns[i + 0]); + mIns[j + 1].mType = ASMIT_INC; mIns[j + 1].CopyMode(mIns[i + 0]); + j += 2; + i += 4; + } + else if (i + 3 < mIns.Size() && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_ABSOLUTE && + mIns[i + 0].mType == ASMIT_SEC && + mIns[i + 2].mType == ASMIT_SBC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 2 && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].mMode == ASMIM_ABSOLUTE && mIns[i + 0].mAddress == mIns[i + 3].mAddress && + !(mIns[i + 3].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C | LIVE_CPU_REG_Z))) + { + mIns[j + 0].mType = ASMIT_DEC; mIns[j + 0].CopyMode(mIns[i + 0]); + mIns[j + 1].mType = ASMIT_DEC; mIns[j + 1].CopyMode(mIns[i + 0]); + j += 2; + i += 4; + } + + else if (i + 1 < mIns.Size() && + mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_ABSOLUTE_X && + mIns[i + 1].mType == ASMIT_TAY && !(mIns[i + 1].mLive & LIVE_CPU_REG_A)) + { + mIns[j] = mIns[i]; + mIns[j].mType = ASMIT_LDY; + mIns[j].mLive |= LIVE_CPU_REG_Y; + j++; + i += 2; + } + else if (i + 1 < mIns.Size() && + mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_ABSOLUTE_Y && + mIns[i + 1].mType == ASMIT_TAX && !(mIns[i + 1].mLive & LIVE_CPU_REG_A)) + { + mIns[j] = mIns[i]; + mIns[j].mType = ASMIT_LDX; + mIns[j].mLive |= LIVE_CPU_REG_Y; + j++; + i += 2; + } + else if (i + 2 < mIns.Size() && + mIns[i + 0].mType == ASMIT_LDY && !(mIns[i + 0].mLive & LIVE_CPU_REG_X) && (mIns[i + 0].mMode == ASMIM_ZERO_PAGE || mIns[i + 0].mMode == ASMIM_ABSOLUTE) && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_ABSOLUTE_Y && + mIns[i + 2].mType == ASMIT_TAY && !(mIns[i + 2].mLive & LIVE_CPU_REG_A)) + { + mIns[j + 0] = mIns[i + 0]; + mIns[j + 1] = mIns[i + 1]; + + mIns[j + 0].mType = ASMIT_LDX; + mIns[j + 0].mLive |= LIVE_CPU_REG_X; + mIns[j + 1].mType = ASMIT_LDY; + mIns[j + 1].mMode = ASMIM_ABSOLUTE_X; + mIns[j + 1].mLive |= LIVE_CPU_REG_Y; + j += 2; + i += 3; + } + else if (i + 2 < mIns.Size() && + mIns[i + 0].mType == ASMIT_LDX && !(mIns[i + 0].mLive & LIVE_CPU_REG_Y) && (mIns[i + 0].mMode == ASMIM_ZERO_PAGE || mIns[i + 0].mMode == ASMIM_ABSOLUTE) && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_ABSOLUTE_X && + mIns[i + 2].mType == ASMIT_TAX && !(mIns[i + 2].mLive & LIVE_CPU_REG_A)) + { + mIns[j + 0] = mIns[i + 0]; + mIns[j + 1] = mIns[i + 1]; + + mIns[j + 0].mType = ASMIT_LDY; + mIns[j + 0].mLive |= LIVE_CPU_REG_Y; + mIns[j + 1].mType = ASMIT_LDX; + mIns[j + 1].mMode = ASMIM_ABSOLUTE_Y; + mIns[j + 1].mLive |= LIVE_CPU_REG_X; + j += 2; + i += 3; + } + else if (i + 5 < mIns.Size() && + mIns[i + 0].ChangesAccuAndFlag() && + mIns[i + 1].mType == ASMIT_CMP && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 0x01 && + mIns[i + 2].mType == ASMIT_LDA && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 0x00 && + mIns[i + 3].mType == ASMIT_ADC && mIns[i + 3].mMode == ASMIM_IMMEDIATE && mIns[i + 3].mAddress == 0xff && + mIns[i + 4].mType == ASMIT_AND && mIns[i + 4].mMode == ASMIM_IMMEDIATE && + mIns[i + 5].mType == ASMIT_EOR && mIns[i + 5].mMode == ASMIM_IMMEDIATE) + { + char veq = mIns[i + 4].mAddress ^ mIns[i + 5].mAddress, vne = mIns[i + 5].mAddress; + + mIns[j + 0] = mIns[i + 0]; + mIns[j + 1].mType = ASMIT_BEQ; mIns[j + 1].mMode = ASMIM_RELATIVE; mIns[j + 1].mAddress = veq != 0 ? 4 : 2; + mIns[j + 2].mType = ASMIT_LDA; mIns[j + 2].mMode = ASMIM_IMMEDIATE; mIns[j + 2].mAddress = vne; mIns[j + 2].mFlags = 0; + j += 3; + if (veq != 0) + { + if (vne) + mIns[j + 0].mType = ASMIT_BNE; + else + mIns[j + 0].mType = ASMIT_BEQ; + mIns[j + 0].mMode = ASMIM_RELATIVE; + mIns[j + 0].mAddress = 2; + mIns[j + 1].mType = ASMIT_LDA; mIns[j + 1].mMode = ASMIM_IMMEDIATE; mIns[j + 1].mAddress = veq; mIns[j + 1].mFlags = 0; + j += 2; + } + i += 6; + } + else if (i + 3 < mIns.Size() && + mIns[i + 0].ChangesAccuAndFlag() && + mIns[i + 1].mType == ASMIT_CMP && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 0x01 && + mIns[i + 2].mType == ASMIT_LDA && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 0x00 && + mIns[i + 3].mType == ASMIT_ROL && mIns[i + 3].mMode == ASMIM_IMPLIED) + { + mIns[j + 0] = mIns[i + 0]; + mIns[j + 1].mType = ASMIT_BEQ; mIns[j + 1].mMode = ASMIM_RELATIVE; mIns[j + 1].mAddress = 2; + mIns[j + 2].mType = ASMIT_LDA; mIns[j + 2].mMode = ASMIM_IMMEDIATE; mIns[j + 2].mAddress = 1; + j += 3; + i += 4; + } +#if 1 + else if (i + 3 < mIns.Size() && + mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_ADC && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 0x01 && + mIns[i + 2].mType == ASMIT_CLC && + mIns[i + 3].mType == ASMIT_ADC) + { + mIns[j + 0] = NativeCodeInstruction(mIns[i + 0].mIns, ASMIT_SEC); + mIns[j + 1] = mIns[i + 3]; + j += 2; + i += 4; + } + else if (i + 4 < mIns.Size() && + mIns[i + 0].mType == ASMIT_SEC && + mIns[i + 1].mType == ASMIT_LDA && + mIns[i + 2].mType == ASMIT_SBC && + mIns[i + 3].mType == ASMIT_SEC && + mIns[i + 4].mType == ASMIT_SBC && mIns[i + 4].mMode == ASMIM_IMMEDIATE && mIns[i + 4].mAddress == 0x01) + { + mIns[j + 0] = NativeCodeInstruction(mIns[i + 0].mIns, ASMIT_CLC); + j += 3; + i += 5; + } +#endif + else if (i + 3 < mIns.Size() && + mIns[i + 0].mType == ASMIT_TAY && + mIns[i + 1].mType == ASMIT_LDA && HasAsmInstructionMode(ASMIT_LDY, mIns[i + 1].mMode) && + mIns[i + 2].mType == ASMIT_STA && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && !(mIns[i + 2].mLive & LIVE_CPU_REG_A) && + mIns[i + 3].mMode == ASMIM_INDIRECT_Y && !(mIns[i + 3].mLive & (LIVE_CPU_REG_Y | LIVE_CPU_REG_Z | LIVE_MEM))) + { + mIns[j + 0] = mIns[i + 2]; + mIns[j + 1] = mIns[i + 1]; mIns[j + 1].mType = ASMIT_LDY; + mIns[j + 2] = mIns[i + 3]; + j += 3; + i += 4; + } + else if (mIns[i + 0].mType == ASMIT_ORA && mIns[i + 0].mMode == ASMIM_IMMEDIATE && mIns[i + 0].mAddress == 0 && !(mIns[i + 0].mLive & LIVE_CPU_REG_X)) + { + mIns[j + 0] = NativeCodeInstruction(mIns[i + 0].mIns, ASMIT_TAX); + i++; + j++; + } + else if (mIns[i + 0].mType == ASMIT_ORA && mIns[i + 0].mMode == ASMIM_IMMEDIATE && mIns[i + 0].mAddress == 0 && !(mIns[i + 0].mLive & LIVE_CPU_REG_Y)) + { + mIns[j + 0] = NativeCodeInstruction(mIns[i + 0].mIns, ASMIT_TAY); + i++; + j++; + } + else if (i + 1 < mIns.Size() && + mIns[i + 0].ChangesZFlag() && mIns[i + 1].mType == ASMIT_LDA && mIns[i + 0].SameEffectiveAddress(mIns[i + 1]) && !(mIns[i + 1].mLive & LIVE_CPU_REG_A) && + !(mIns[i + 0].mFlags & mIns[i + 1].mFlags & NCIF_VOLATILE)) + { + mIns[j++] = mIns[i++]; + i++; + } + else if (BlockSizeCopyReduction(proc, i, j)) + { + + } + else + mIns[j++] = mIns[i++]; + } + mIns.SetSize(j); + +#if 1 + bool yimm = false, ximm = false, aimm = false, afail = false; + int yval = 0, xval = 0, aval = 0; + + if (mNumEntries == 1) + { + if (yenter >= 0) + { + yval = yenter; + yimm = true; + } + if (xenter >= 0) + { + xval = xenter; + ximm = true; + } + } + + for(int i = 0; i < mIns.Size(); i++) + { + if (afail) + aimm = false; + + if (mIns[i].mType == ASMIT_LDA) + { + if (mIns[i].mMode == ASMIM_IMMEDIATE) + { + if (yimm && mIns[i].mAddress == yval) + { + mIns[i].mType = ASMIT_TYA; + mIns[i].mMode = ASMIM_IMPLIED; + } + else if (ximm && mIns[i].mAddress == xval) + { + mIns[i].mType = ASMIT_TXA; + mIns[i].mMode = ASMIM_IMPLIED; + } + + aimm = true; + aval = mIns[i].mAddress; + } + else + aimm = false; + } + else if (mIns[i].mType == ASMIT_TYA) + { + if (yimm) + { + aimm = true; + aval = yval; + } + else + aimm = false; + } + else if (mIns[i].mType == ASMIT_TXA) + { + if (ximm) + { + aimm = true; + aval = xval; + } + else + aimm = false; + } + else if (mIns[i].mType == ASMIT_LDY) + { + if (mIns[i].mMode == ASMIM_IMMEDIATE) + { + if (yimm && mIns[i].mAddress == yval) + { + mIns[i].mType = ASMIT_NOP; + mIns[i].mMode = ASMIM_IMPLIED; + } + else if (yimm && mIns[i].mAddress == ((yval + 1) & 0xff)) + { + yval = mIns[i].mAddress; + mIns[i].mType = ASMIT_INY; + mIns[i].mMode = ASMIM_IMPLIED; + } + else if (yimm && mIns[i].mAddress == ((yval - 1) & 0xff)) + { + yval = mIns[i].mAddress; + mIns[i].mType = ASMIT_DEY; + mIns[i].mMode = ASMIM_IMPLIED; + } + else if (aimm && mIns[i].mAddress == aval) + { + mIns[i].mType = ASMIT_TAY; + mIns[i].mMode = ASMIM_IMPLIED; + yimm = true; + yval = aval; + } + else + { + yimm = true; + yval = mIns[i].mAddress; + } + + } + else + yimm = false; + } + else if (yimm && mIns[i].mType == ASMIT_INY) + { + yval = (yval + 1) & 0xff; + } + else if (yimm && mIns[i].mType == ASMIT_DEY) + { + yval = (yval - 1) & 0xff; + } + else if (mIns[i].mType == ASMIT_TAY) + { + if (aimm) + { + yimm = true; + yval = aval; + } + else + yimm = false; + } + else if (mIns[i].mType == ASMIT_LDX) + { + if (mIns[i].mMode == ASMIM_IMMEDIATE) + { + if (ximm && mIns[i].mAddress == ((xval + 1) & 0xff)) + { + xval = mIns[i].mAddress; + mIns[i].mType = ASMIT_INX; + mIns[i].mMode = ASMIM_IMPLIED; + } + else if (ximm && mIns[i].mAddress == ((xval - 1) & 0xff)) + { + xval = mIns[i].mAddress; + mIns[i].mType = ASMIT_DEX; + mIns[i].mMode = ASMIM_IMPLIED; + } + else if (aimm && mIns[i].mAddress == aval) + { + mIns[i].mType = ASMIT_TAX; + mIns[i].mMode = ASMIM_IMPLIED; + ximm = true; + xval = aval; + } + else + { + ximm = true; + xval = mIns[i].mAddress; + } + + } + else + ximm = false; + } + else if (ximm && mIns[i].mType == ASMIT_INX) + { + xval = (xval + 1) & 0xff; + } + else if (ximm && mIns[i].mType == ASMIT_DEX) + { + xval = (xval - 1) & 0xff; + } + else if (mIns[i].mType == ASMIT_TAX) + { + if (aimm) + { + ximm = true; + xval = aval; + } + else + ximm = false; + } + else if (mIns[i].mType == ASMIT_JSR) + yimm = ximm = aimm = false; + else if (mIns[i].mMode == ASMIM_RELATIVE) + { + if (mIns[i].mAddress < 0) + yimm = ximm = aimm = false; + else + afail = true; + } + else if (mIns[i].ChangesAccu()) + aimm = false; + } +#endif + + // Check for small condition block + if (mFalseJump) + { + NativeCodeBasicBlock* cblock = nullptr, * eblock = nullptr; + + if (mTrueJump == mFalseJump->mTrueJump && !mFalseJump->mFalseJump && mTrueJump->mNumEntries == 2 && mFalseJump->mNumEntries == 1) + { + cblock = mFalseJump; + eblock = mTrueJump; + } + else if (mFalseJump == mTrueJump->mTrueJump && !mTrueJump->mFalseJump && mFalseJump->mNumEntries == 2 && mTrueJump->mNumEntries == 1) + { + cblock = mTrueJump; + eblock = mFalseJump; + } + + if (eblock && cblock->mIns.Size() == 1 && (cblock->mIns[0].mType == ASMIT_INC || cblock->mIns[0].mType == ASMIT_INX || cblock->mIns[0].mType == ASMIT_INY)) + { + int i = 0; + while (i < eblock->mIns.Size() && !eblock->mIns[i].ReferencesCarry()) + i++; + if (i < eblock->mIns.Size() && eblock->mIns[i].mType == ASMIT_CLC) + { + if (mBranch == ASMIT_BCC && eblock == mTrueJump || mBranch == ASMIT_BCS && eblock == mFalseJump) + { + cblock->mIns.Push(NativeCodeInstruction(eblock->mIns[i].mIns, ASMIT_CLC)); + eblock->mIns.Remove(i); + mExitRequiredRegs += CPU_REG_C; + cblock->mExitRequiredRegs += CPU_REG_C; + eblock->mEntryRequiredRegs += CPU_REG_C; + for (int j = 0; j < i; j++) + eblock->mIns[j].mLive |= LIVE_CPU_REG_C; + cblock->mNDataSet.Set(CPU_REG_C, NRDM_IMMEDIATE, 0); + } + } + } + } + + bool carrySet = false, carryClear = false; + if (mEntryBlocks.Size() == 1 && center >= 0) + { + if (center) + carrySet = true; + else + carryClear = true; + } + else + { + if (mEntryBlocks.Size() > 0) + { + carrySet = true; + carryClear = true; + } + + for (int i = 0; i < mEntryBlocks.Size(); i++) + { + if (mEntryBlocks[i]->mBranch == ASMIT_BCC) + { + if (mEntryBlocks[i]->mTrueJump == this) + carrySet = false; + else + carryClear = false; + } + else if (mEntryBlocks[i]->mBranch == ASMIT_BCS) + { + if (mEntryBlocks[i]->mTrueJump == this) + carryClear = false; + else + carrySet = false; + } + + else if (mEntryBlocks[i]->mBranch == ASMIT_BEQ && mEntryBlocks[i]->mTrueJump == this && mEntryBlocks[i]->mIns.Size() > 0 && mEntryBlocks[i]->mIns.Last().mType == ASMIT_CMP) + carryClear = false; + else if (mEntryBlocks[i]->mBranch == ASMIT_BNE && mEntryBlocks[i]->mFalseJump == this && mEntryBlocks[i]->mIns.Size() > 0 && mEntryBlocks[i]->mIns.Last().mType == ASMIT_CMP) + carryClear = false; + else + carryClear = carrySet = false; + } + } + +#if 1 + + i = 0; + j = 0; + int accuVal = 0, accuMask = 0, xregVal = 0, xregMask = 0, yregVal = 0, yregMask = 0; + bool accuFlags = false; + while (i < mIns.Size()) + { + bool skip = false; + switch (mIns[i].mType) + { + case ASMIT_CLC: + if (carryClear) + skip = true; + else if (carrySet && i + 2 < mIns.Size() && mIns[i + 1].mType == ASMIT_LDA && mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress > 0) + { + // convert CLC before const add into SEC and reduce immediate + skip = true; + mIns[i + 2].mAddress--; + } + else if (carrySet && i + 1 < mIns.Size() && mIns[i + 1].mType == ASMIT_ADC && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress > 0) + { + // convert CLC before const add into SEC and reduce immediate + skip = true; + mIns[i + 1].mAddress--; + } + else + { + carryClear = true; + carrySet = false; + } + break; + case ASMIT_SEC: + if (carrySet) + skip = true; + else if (carryClear && i + 1 < mIns.Size() && mIns[i + 1].mType == ASMIT_SBC && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress > 0) + { + // convert SEC before const sub into CLC and reduce immediate + skip = true; + mIns[i + 1].mAddress--; + } + else + { + carryClear = false; + carrySet = true; + } + break; + case ASMIT_ADC: + { + if (mIns[i].mMode == ASMIM_IMMEDIATE) + { + if (carryClear && mIns[i].mAddress == 0 && !(mIns[i].mLive & LIVE_CPU_REG_Z)) + { + skip = true; + } + else if (carryClear && accuMask == 0xff && accuVal == 0x00 && j > 0 && mIns[j - 1].mType == ASMIT_LDA && mIns[j - 1].mMode == ASMIM_IMMEDIATE) + { + mIns[j - 1].mAddress = mIns[i].mAddress; + skip = true; + } + else + { + int amax = ((accuVal & accuMask) | (~accuMask & 255)) + mIns[i].mAddress; + if (!carryClear) + amax++; + if (amax < 256) + { + carryClear = true; + carrySet = false; + accuMask = ~BinMask(amax) & 255; + accuVal = 0; + } + else + { + accuMask = 0; + accuFlags = true; + carryClear = false; + carrySet = false; + } + } + } + else if (mIns[i].mMaxVal < 255) + { + int amax = ((accuVal & accuMask) | (~accuMask & 255)) + mIns[i].mMaxVal; + if (!carryClear) + amax++; + if (amax < 256) + { + carryClear = true; + carrySet = false; + accuMask = ~BinMask(amax) & 255; + accuVal = 0; + } + else + { + accuMask = 0; + accuFlags = true; + carryClear = false; + carrySet = false; + } + } + else + { + accuMask = 0; + accuFlags = true; + carryClear = false; + carrySet = false; + } + } break; + case ASMIT_SBC: + accuMask = 0; + accuFlags = true; + carryClear = false; + carrySet = false; + break; + case ASMIT_CMP: + carryClear = false; + carrySet = false; + accuFlags = false; + break; + case ASMIT_AND: + if (mIns[i].mMode == ASMIM_IMMEDIATE) + { + int clear = mIns[i].mAddress ^ 0xff; + + if (mIns[i].mAddress == 0xff && accuFlags) + skip = true; + else if (!(mIns[i].mLive & LIVE_CPU_REG_Z) && !(clear & (accuMask ^ 0xff)) && !(accuVal & clear)) + skip = true; + else + accuFlags = true; + + accuVal &= ~clear; + accuMask |= clear; + } + else + { + accuMask = 0; + accuFlags = true; + } + break; + case ASMIT_ORA: + if (mIns[i].mMode == ASMIM_IMMEDIATE) + { + int set = mIns[i].mAddress; + + if (mIns[i].mAddress == 0 && accuFlags) + skip = true; + else if (!(mIns[i].mLive & LIVE_CPU_REG_Z) && !(set & (accuMask ^ 0xff)) && !(~accuVal & set)) + skip = true; + else + accuFlags = true; + + accuVal |= set; + accuMask |= set; + } + else + { + accuMask = 0; + accuFlags = true; + } + break; + case ASMIT_LDX: + if (mIns[i].mMode == ASMIM_IMMEDIATE) + { + xregVal = mIns[i].mAddress; + xregMask = 0xff; + } + else + xregMask = 0; + accuFlags = false; + break; + case ASMIT_LDY: + if (mIns[i].mMode == ASMIM_IMMEDIATE) + { + yregVal = mIns[i].mAddress; + yregMask = 0xff; + } + else + yregMask = 0; + accuFlags = false; + break; + + case ASMIT_LDA: + if (mIns[i].mMode == ASMIM_IMMEDIATE) + { + accuVal = mIns[i].mAddress; + accuMask = 0xff; + } + else if (mIns[i].mMode == ASMIM_ABSOLUTE && mIns[i].mLinkerObject && (mIns[i].mLinkerObject->mFlags & LOBJF_CONST) && mIns[i].mLinkerObject->mReferences.Size() == 0) + { + accuVal = mIns[i].mLinkerObject->mData[mIns[i].mAddress]; + accuMask = 0xff; + } +#if 1 + else if ((mIns[i].mMode == ASMIM_ABSOLUTE_X || mIns[i].mMode == ASMIM_ABSOLUTE_Y) && mIns[i].mLinkerObject && (mIns[i].mLinkerObject->mFlags & LOBJF_CONST) && mIns[i].mLinkerObject->mReferences.Size() == 0) + { + int mor = 0; + int mand = 0xff; + int ior = 0; + int iand = 0xff; + + if (mIns[i].mMode == ASMIM_ABSOLUTE_X) + { + ior = xregMask & xregVal; + iand = (~xregMask | xregVal) & 0xff; + } + else if (mIns[i].mMode == ASMIM_ABSOLUTE_Y) + { + ior = yregMask & yregVal; + iand = (~yregMask | yregVal) & 0xff; + } + + int size = mIns[i].mLinkerObject->mSize - mIns[i].mAddress; + if (mIns[i].mLinkerObject->mStripe > 1) + size = mIns[i].mLinkerObject->mStripe - mIns[i].mAddress % mIns[i].mLinkerObject->mStripe; + if (size > 256) + size = 256; + + for (int j = 0; j < size; j++) + { + if ((j & ~iand) == 0 && (j & ior) == ior) + { + mor |= mIns[i].mLinkerObject->mData[mIns[i].mAddress + j]; + mand &= mIns[i].mLinkerObject->mData[mIns[i].mAddress + j]; + } + } + + accuMask = (mand | ~mor) & 0xff; + accuVal = mand; + } +#endif + else + { + accuVal = 0; + accuMask = ~ BinMask(mIns[i].mMaxVal) & 255; + } + accuFlags = true; + break; + case ASMIT_ASL: + if (mIns[i].mMode == ASMIM_IMPLIED) + { + accuVal <<= 1; + accuMask = (accuMask << 1) | 1; + + if (accuMask & 0x100) + { + if (accuVal & 0x100) + { + carryClear = false; + carrySet = true; + } + else + { + carryClear = true; + carrySet = false; + } + } + else + { + carryClear = false; + carrySet = false; + } + + accuMask &= 0xff; + accuVal &= 0xff; + } + else + { + carryClear = false; + carrySet = false; + } + accuFlags = mIns[i].mMode == ASMIM_IMPLIED; + break; + case ASMIT_LSR: + if (mIns[i].mMode == ASMIM_IMPLIED) + { + if (accuMask & 0x01) + { + if (accuVal & 0x01) + { + carryClear = false; + carrySet = true; + } + else + { + carryClear = true; + carrySet = false; + } + } + else + { + carryClear = false; + carrySet = false; + } + + accuVal >>= 1; + accuMask = (accuMask >> 1) | 0x80; + } + else + { + carryClear = false; + carrySet = false; + } + accuFlags = mIns[i].mMode == ASMIM_IMPLIED; + break; + case ASMIT_ROL: + if (mIns[i].mMode == ASMIM_IMPLIED) + { + accuVal <<= 1; + accuMask <<= 1; + + if (accuMask & 0x100) + { + if (accuVal & 0x100) + { + carryClear = false; + carrySet = true; + } + else + { + carryClear = true; + carrySet = false; + } + } + else + { + carryClear = false; + carrySet = false; + } + + accuVal &= 0xff; + accuMask &= 0xff; + } + else + { + carryClear = false; + carrySet = false; + } + accuFlags = mIns[i].mMode == ASMIM_IMPLIED; + break; + case ASMIT_ROR: + if (mIns[i].mMode == ASMIM_IMPLIED) + { + if (accuMask & 0x01) + { + if (accuVal & 0x01) + { + carryClear = false; + carrySet = true; + } + else + { + carryClear = true; + carrySet = false; + } + } + else + { + carryClear = false; + carrySet = false; + } + + accuVal >>= 1; + accuMask >>= 1; + } + else + { + carryClear = false; + carrySet = false; + } + accuFlags = mIns[i].mMode == ASMIM_IMPLIED; + break; + case ASMIT_TAX: + xregMask = accuMask; + xregVal = accuVal; + accuFlags = true; + break; + case ASMIT_TAY: + yregMask = accuMask; + yregVal = accuVal; + accuFlags = true; + break; + case ASMIT_TXA: + accuMask = xregMask; + accuVal = xregVal; + accuFlags = true; + break; + case ASMIT_TYA: + accuMask = yregMask; + accuVal = yregVal; + accuFlags = true; + break; + default: + if (mIns[i].ChangesCarry()) + { + carryClear = false; + carrySet = false; + } + if (mIns[i].ChangesXReg()) + xregMask = 0; + if (mIns[i].ChangesYReg()) + yregMask = 0; + + if (mIns[i].ChangesAccu()) + { + accuMask = 0; + accuFlags = false; + } + else if (mIns[i].ChangesZFlag()) + { + accuFlags = false; + } + } + + if (!skip) + { + if (i != j) + mIns[j] = mIns[i]; + j++; + } + i++; + } + mIns.SetSize(j); + + int cval = -1; + if (carrySet) + cval = 1; + else if (carryClear) + cval = 0; + +#endif + int tcval = cval, fcval = cval; + + if (mBranch == ASMIT_BCC) + { + tcval = 0; fcval = 1; + } + else if (mBranch == ASMIT_BCS) + { + tcval = 1; fcval = 0; + } + + int xtrue = ximm ? xval : -1, xfalse = xtrue; + int ytrue = yimm ? yval : -1, yfalse = ytrue; + + if (mIns.Size() > 0) + { + const NativeCodeInstruction& ins(mIns.Last()); + + if (ins.mMode == ASMIM_IMMEDIATE) + { + if (ins.mType == ASMIT_CPY) + { + if (mBranch == ASMIT_BEQ) + ytrue = ins.mAddress; + else if (mBranch == ASMIT_BNE) + yfalse = ins.mAddress; + } + if (ins.mType == ASMIT_CPX) + { + if (mBranch == ASMIT_BEQ) + xtrue = ins.mAddress; + else if (mBranch == ASMIT_BNE) + xfalse = ins.mAddress; + } + } + } + + + if (mTrueJump) + mTrueJump->BlockSizeReduction(proc, xtrue, ytrue, tcval); + if (mFalseJump) + mFalseJump->BlockSizeReduction(proc, xfalse, yfalse, fcval); + } +} + +bool NativeCodeBasicBlock::ReferencesAccu(int from, int to) const +{ + if (to > mIns.Size()) to = mIns.Size(); + for (int i = from; i < to; i++) + if (mIns[i].ReferencesAccu()) + return true; + return false; +} + +bool NativeCodeBasicBlock::ReferencesYReg(int from, int to) const +{ + if (to > mIns.Size()) to = mIns.Size(); + for (int i = from; i < to; i++) + if (mIns[i].ReferencesYReg()) + return true; + return false; +} + +bool NativeCodeBasicBlock::ReferencesXReg(int from, int to) const +{ + if (to > mIns.Size()) to = mIns.Size(); + for (int i = from; i < to; i++) + if (mIns[i].ReferencesXReg()) + return true; + return false; +} + +bool NativeCodeBasicBlock::ChangesAccu(int from, int to) const +{ + if (to > mIns.Size()) to = mIns.Size(); + for (int i = from; i < to; i++) + if (mIns[i].ChangesAccu()) + return true; + return false; +} + +bool NativeCodeBasicBlock::ChangesYReg(int from, int to) const +{ + if (to > mIns.Size()) to = mIns.Size(); + for (int i = from; i < to; i++) + if (mIns[i].ChangesYReg()) + return true; + return false; +} + +bool NativeCodeBasicBlock::ChangesXReg(int from, int to) const +{ + if (to > mIns.Size()) to = mIns.Size(); + for (int i = from; i < to; i++) + if (mIns[i].ChangesXReg()) + return true; + return false; +} + +bool NativeCodeBasicBlock::ChangesCarry(int from, int to) const +{ + if (to > mIns.Size()) to = mIns.Size(); + for (int i = from; i < to; i++) + if (mIns[i].ChangesCarry()) + return true; + return false; +} + +bool NativeCodeBasicBlock::ChangesZeroPage(int address, int from, int to) const +{ + if (to > mIns.Size()) to = mIns.Size(); + for (int i = from; i < to; i++) + if (mIns[i].ChangesZeroPage(address)) + return true; + return false; +} + +bool NativeCodeBasicBlock::UsesZeroPage(int address, int from, int to) const +{ + if (to > mIns.Size()) to = mIns.Size(); + for (int i = from; i < to; i++) + if (mIns[i].UsesZeroPage(address)) + return true; + return false; +} + +bool NativeCodeBasicBlock::LoadsZeroPage(int address, int from, int to) const +{ + if (to > mIns.Size()) to = mIns.Size(); + for (int i = from; i < to; i++) + if (mIns[i].LoadsZeroPage(address)) + return true; + return false; +} + +bool NativeCodeBasicBlock::ReferencesZeroPage(int address, int from, int to) const +{ + if (to > mIns.Size()) to = mIns.Size(); + for (int i = from; i < to; i++) + if (mIns[i].ReferencesZeroPage(address)) + return true; + return false; +} + +bool NativeCodeBasicBlock::ChangesMemory(const NativeCodeInstruction& ins, int from, int to) const +{ + if (to > mIns.Size()) to = mIns.Size(); + for (int i = from; i < to; i++) + if (ins.MayBeChangedOnAddress(mIns[i])) + return true; + return false; +} + +bool NativeCodeBasicBlock::ReferencesMemory(const NativeCodeInstruction& ins, int from, int to) const +{ + if (to > mIns.Size()) to = mIns.Size(); + for (int i = from; i < to; i++) + if (mIns[i].MayReference(ins)) + return true; + return false; +} + + + +bool NativeCodeBasicBlock::RemoveNops(void) +{ + bool changed = false; + + assert(mIns.Size() == 0 || mIns[0].mType != ASMIT_INV); + + int i = 0; + int j = 0; + while (i < mIns.Size()) + { + if (mIns[i].mType == ASMIT_NOP) + ; + else + { + if (i != j) + mIns[j] = mIns[i]; + j++; + } + i++; + } + if (j != i) + changed = true; + mIns.SetSize(j); + mIns.Reserve(2 * j); + + return changed; +} + +bool NativeCodeBasicBlock::PeepHoleOptimizerShuffle(int pass) +{ + bool changed = false; + +#if 1 +#if 1 + // move load store pairs up to initial store + + for (int i = 2; i + 1 < mIns.Size(); i++) + { + if (mIns[i].mType == ASMIT_LDA && mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i + 1].mType == ASMIT_STA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE) + { + if (MoveLoadStoreUp(i)) + changed = true; + } + } + CheckLive(); + +#endif + +#if 1 + // replace zero page up + + for (int i = 1; i + 1 < mIns.Size(); i++) + { + if (mIns[i].mType == ASMIT_LDA && mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i + 1].mType == ASMIT_STA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && !(mIns[i + 1].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z)) && !(mIns[i + 0].mLive & LIVE_MEM)) + { + if (ReplaceZeroPageUp(i)) + changed = true; + } + } + CheckLive(); + +#endif + + // Move LDX/LDY up to STA of same address +#if 1 + if (pass >= 9) + { + for (int i = 2; i < mIns.Size(); i++) + { + if (mIns[i].mType == ASMIT_LDX && (mIns[i].mMode == ASMIM_ZERO_PAGE || mIns[i].mMode == ASMIM_ABSOLUTE) && !(mIns[i].mLive & LIVE_CPU_REG_Z) && !(mIns[i].mFlags & NCIF_VOLATILE)) + { + if (MoveLDXUp(i)) + changed = true; + } + else if (mIns[i].mType == ASMIT_LDY && (mIns[i].mMode == ASMIM_ZERO_PAGE || mIns[i].mMode == ASMIM_ABSOLUTE) && !(mIns[i].mLive & LIVE_CPU_REG_Z) && !(mIns[i].mFlags & NCIF_VOLATILE)) + { + if (MoveLDYUp(i)) + changed = true; + } + } + CheckLive(); + } +#endif + +#if 1 + // move ORA #imm up a shift chain to an LDA #imm + + for (int i = 1; i + 1 < mIns.Size(); i++) + { + if (mIns[i].IsShift() && mIns[i].mMode == ASMIM_IMPLIED && mIns[i + 1].mType == ASMIT_ORA && mIns[i + 1].mMode == ASMIM_IMMEDIATE && !(mIns[i + 1].mLive & LIVE_CPU_REG_Z)) + { + if (FoldShiftORAIntoLoadImmUp(i)) + changed = true; + } + } + CheckLive(); + +#endif + +#if 1 + for (int i = 0; i + 4 < mIns.Size(); i++) + { + if (mIns[i].mType == ASMIT_LDA && (mIns[i].mMode == ASMIM_ZERO_PAGE || mIns[i].mMode == ASMIM_ABSOLUTE) && + mIns[i + 1].IsLogic() && mIns[i + 1].mMode == ASMIM_IMMEDIATE && + mIns[i + 2].mType == ASMIT_TAX && !(mIns[i + 2].mLive & LIVE_CPU_REG_A)) + { + if (MoveLDAopTAXDown(i)) + changed = true; + } + } + CheckLive(); +#endif + +#if 1 + // move ORA #imm up a shift chain to an LDA #imm + + for (int i = 1; i + 1 < mIns.Size(); i++) + { + if (mIns[i].mType == ASMIT_LDX && mIns[i].mMode == ASMIM_IMMEDIATE && + mIns[i + 1].mType == ASMIT_ORA && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 0) + { + if (MoveLDXBeforeZ(i)) + changed = true; + } + } + CheckLive(); + +#endif + + +#if 1 + // Combine ADC with immediate + + for (int i = 4; i + 1 < mIns.Size(); i++) + { + if (mIns[i].mType == ASMIT_LDA && mIns[i].mMode == ASMIM_ZERO_PAGE && !(mIns[i].mLive & LIVE_MEM) && + mIns[i + 1].mType == ASMIT_ADC && mIns[i + 1].mMode == ASMIM_IMMEDIATE && !(mIns[i + 1].mLive & (LIVE_CPU_REG_C | LIVE_CPU_REG_Z))) + { + if (CombineImmediateADCUp(i)) + changed = true; + } + else if (mIns[i].mType == ASMIT_TXA && !(mIns[i].mLive & LIVE_CPU_REG_X) && + mIns[i + 1].mType == ASMIT_ADC && mIns[i + 1].mMode == ASMIM_IMMEDIATE && !(mIns[i + 1].mLive & (LIVE_CPU_REG_C | LIVE_CPU_REG_Z))) + { + if (CombineImmediateADCUpX(i)) + changed = true; + } + } + + CheckLive(); +#endif + +#if 1 + // Move mask after load up + + for (int i = 2; i + 1 < mIns.Size(); i++) + { + if (mIns[i].mType == ASMIT_LDA && mIns[i].mMode == ASMIM_ZERO_PAGE && !(mIns[i].mLive & LIVE_MEM) && + mIns[i + 1].mType == ASMIT_AND && mIns[i + 1].mMode == ASMIM_IMMEDIATE && !(mIns[i + 1].mLive & LIVE_CPU_REG_Z)) + + { + if (MoveImmOpBeforeStore(i)) + changed = true; + } + } + + CheckLive(); +#endif + +#if 1 + // Move load or zp up + + for (int i = 2; i + 1 < mIns.Size(); i++) + { + if (mIns[i].mType == ASMIT_LDA && mIns[i].mMode == ASMIM_ZERO_PAGE && !(mIns[i].mLive & LIVE_MEM) && + mIns[i + 1].mType == ASMIT_ORA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && !(mIns[i + 1].mLive & LIVE_MEM)) + + { + if (MoveLoadOrZPUp(i)) + changed = true; + } + } + + CheckLive(); +#endif + +#if 1 + // move load store pairs up to initial store + + for (int i = 2; i + 1 < mIns.Size(); i++) + { + if (mIns[i].mType == ASMIT_LDX && mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i + 1].mType == ASMIT_STX && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && !(mIns[i + 1].mLive & (LIVE_CPU_REG_X | LIVE_CPU_REG_Z))) + { + if (MoveLoadStoreXUp(i)) + changed = true; + } + } + CheckLive(); + +#endif + +#if 1 + // move load - store (),y up to initial store + // + + for (int i = 2; i + 2 < mIns.Size(); i++) + { + if (mIns[i].mType == ASMIT_LDA && mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i + 1].mType == ASMIT_LDY && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mType == ASMIT_STA && mIns[i + 2].mMode == ASMIM_INDIRECT_Y) + { + if (MoveIndirectLoadStoreUp(i)) + changed = true; + } + } + CheckLive(); +#endif + +#if 1 + // move load (),y store zp down to potential user + for (int i = 0; i + 2 < mIns.Size(); i++) + { + if (mIns[i + 0].mType == ASMIT_LDY && mIns[i + 0].mMode == ASMIM_IMMEDIATE && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_INDIRECT_Y && !(mIns[i + 1].mFlags & NICT_ZPFLIPPED) && + mIns[i + 2].mType == ASMIT_STA && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && !(mIns[i + 2].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z))) + { + if (MoveLoadIndirectTempStoreUp(i)) + changed = true; + } + else if (mIns[i].mType == ASMIT_LDA && mIns[i].mMode == ASMIM_INDIRECT_Y && mIns[i + 1].mType == ASMIT_STA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && !(mIns[i + 1].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z))) + { + if (MoveIndirectLoadStoreDown(i)) + changed = true; + } +#if 1 + if (pass > 0 && + mIns[i + 0].mType == ASMIT_LDY && mIns[i + 0].mMode == ASMIM_IMMEDIATE && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_INDIRECT_Y && + mIns[i + 2].mType == ASMIT_STA && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && !(mIns[i + 2].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z))) + { + if (MoveIndirectLoadStoreDownY(i)) + changed = true; + } +#endif + } + CheckLive(); + + +#endif + +#if 1 + // move load (),y store zp up to potential user + for (int i = 4; i + 1 < mIns.Size(); i++) + { + if (mIns[i + 0].mType == ASMIT_LDY && mIns[i + 0].mMode == ASMIM_IMMEDIATE && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_INDIRECT_Y && !(mIns[i + 1].mLive & (LIVE_MEM | LIVE_CPU_REG_Y))) + { + if (MoveLoadIndirectBypassYUp(i)) + changed = true; + } + } + CheckLive(); + +#endif + +#if 1 + for (int i = 0; i + 4 < mIns.Size(); i++) + { + if (mIns[i + 0].mType == ASMIT_TYA && mIns[i + 1].mType == ASMIT_CLC && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && !(mIns[i + 3].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C | LIVE_CPU_REG_Z))) + { + if (MoveTYADCStoreDown(i)) + changed = true; + } + } + CheckLive(); +#endif + +#if 1 + // move load - store abs up to initial store + // + + for (int i = 2; i + 1 < mIns.Size(); i++) + { + if (mIns[i].mType == ASMIT_LDA && mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i + 1].mType == ASMIT_STA && + (mIns[i + 1].mMode == ASMIM_ABSOLUTE || mIns[i + 1].mMode == ASMIM_ABSOLUTE_X || mIns[i + 1].mMode == ASMIM_ABSOLUTE_Y || mIns[i + 1].mMode == ASMIM_INDIRECT_Y)) + { + if (MoveAbsoluteLoadStoreUp(i)) + changed = true; + } + } + CheckLive(); + +#endif + +#if 1 + // move load - store abs up to initial store + // + + if (pass == 4) + { + for (int i = 2; i + 1 < mIns.Size(); i++) + { + if (mIns[i].mType == ASMIT_LDA && mIns[i + 1].mType == ASMIT_STA && + (mIns[i + 0].mMode == ASMIM_ABSOLUTE || mIns[i + 0].mMode == ASMIM_ABSOLUTE_X || mIns[i + 0].mMode == ASMIM_ABSOLUTE_Y || mIns[i + 0].mMode == ASMIM_INDIRECT_Y) && + (mIns[i + 1].mMode == ASMIM_ABSOLUTE || mIns[i + 1].mMode == ASMIM_ABSOLUTE_X || mIns[i + 1].mMode == ASMIM_ABSOLUTE_Y || mIns[i + 1].mMode == ASMIM_INDIRECT_Y) && + !(mIns[i + 0].mFlags & NCIF_VOLATILE) && !(mIns[i + 1].mFlags & NCIF_VOLATILE)) + { + if (MoveGenericLoadStoreUp(i)) + changed = true; + } + } + CheckLive(); + } + +#endif + +#if 1 + // move ldx/stx down and convert to accu + // + + for (int i = mIns.Size() - 2; i >= 0; i--) + { + if (mIns[i].mType == ASMIT_LDX && (mIns[i].mMode == ASMIM_IMMEDIATE || mIns[i].mMode == ASMIM_IMMEDIATE_ADDRESS) && + mIns[i + 1].mType == ASMIT_STX && !(mIns[i + 1].mLive & LIVE_CPU_REG_X) && !(mIns[i + 1].mFlags & NCIF_MEMMAP)) + { + if (MoveLDSTXOutOfRange(i)) + changed = true; + } + } + CheckLive(); + +#endif + + +#if 1 + // move load - add # - store up to initial store + // + + for (int i = 2; i + 3 < mIns.Size(); i++) + { + if ( + mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && + mIns[i + 3].mType == ASMIT_STA && (mIns[i + 3].mMode == ASMIM_ZERO_PAGE || mIns[i + 3].mMode == ASMIM_ABSOLUTE) && + (mIns[i + 3].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z)) == 0) + { + if (MoveLoadAddImmStoreUp(i)) + changed = true; + } + } + CheckLive(); + +#endif + + +#if 1 + // move load - add # - store up to initial store + // + + for (int i = 1; i + 2 < mIns.Size(); i++) + { + if ( + mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].IsLogic() && (mIns[i + 1].mMode == ASMIM_IMMEDIATE || mIns[i + 1].mMode == ASMIM_ZERO_PAGE) && + mIns[i + 2].mType == ASMIT_STA && (mIns[i + 2].mMode == ASMIM_ZERO_PAGE || mIns[i + 2].mMode == ASMIM_ABSOLUTE || mIns[i + 2].mMode == ASMIM_ABSOLUTE_X || mIns[i + 2].mMode == ASMIM_ABSOLUTE_Y) && + (mIns[i + 2].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z)) == 0) + { + if (MoveLoadLogicStoreAbsUp(i)) + changed = true; + } + } + CheckLive(); + +#endif + +#if 1 + + // move load - add - store down to usage + + for (int i = 0; i + 4 < mIns.Size(); i++) + { + if ( + mIns[i + 0].mType == ASMIT_LDA && + mIns[i + 1].mType == ASMIT_CLC && + mIns[i + 2].mType == ASMIT_ADC && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && !(mIns[i + 3].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C))) + { + if (MoveLoadAddStoreDown(i)) + changed = true; + } + } + CheckLive(); + +#endif + +#if 1 + // move load - ora/and/eor # - store up to initial store + // + + for (int i = 2; i + 2 < mIns.Size(); i++) + { + if ( + mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + (mIns[i + 1].mType == ASMIT_ORA || mIns[i + 1].mType == ASMIT_AND || mIns[i + 1].mType == ASMIT_EOR) && mIns[i + 1].mMode == ASMIM_IMMEDIATE && + mIns[i + 2].mType == ASMIT_STA && (mIns[i + 2].mMode == ASMIM_ZERO_PAGE || mIns[i + 2].mMode == ASMIM_ABSOLUTE) && + (mIns[i + 2].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z)) == 0) + { + if (MoveLoadEorImmStoreUp(i)) + changed = true; + } + } + CheckLive(); + +#endif + + +#if 1 + // move load - add # - store with absolute,x up as far possible + // + + for (int i = 9; i + 3 < mIns.Size(); i++) + { + if ( + mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_ABSOLUTE_X && + mIns[i + 1].mType == ASMIT_CLC && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].mMode == ASMIM_ABSOLUTE_X && !(mIns[i + 3].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z | LIVE_CPU_REG_C))) + { + if (MoveLoadAddImmStoreAbsXUp(i)) + changed = true; + } + } + CheckLive(); + +#endif + +#if 1 + // move load - add ZP - store up to initial store + // + + for (int i = 2; i + 3 < mIns.Size(); i++) + { + if ( + mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].mMode == ASMIM_ZERO_PAGE) + { + if (MoveCLCLoadAddZPStoreUp(i)) + changed = true; + } +#if 1 + else if ( + mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_ADC && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + mIns[i + 2].mType == ASMIT_STA && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && (mIns[i + 2].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z)) == 0) + { + if (MoveLoadAddZPStoreUp(i)) + changed = true; + } +#endif +#if 1 + else if ( + mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].IsShift() && mIns[i + 1].mMode == ASMIM_IMPLIED && + mIns[i + 2].mType == ASMIT_STA && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && (mIns[i + 2].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z)) == 0) + { + if (MoveLoadShiftStoreUp(i)) + changed = true; + } +#endif +#if 1 + else if ( + mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + (mIns[i + 1].mType == ASMIT_LSR || mIns[i + 1].mType == ASMIT_ASL) && mIns[i + 1].mMode == ASMIM_IMPLIED && + (mIns[i + 2].mType == ASMIT_ROL || mIns[i + 2].mType == ASMIT_ROR) && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && !(mIns[i + 2].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z | LIVE_CPU_REG_C))) + { + if (MoveLoadShiftRotateUp(i)) + changed = true; + } +#endif + } + CheckLive(); + +#endif +#if 1 + for (int i = 0; i < mIns.Size(); i++) + { + if (mIns[i].IsShift() && mIns[i].mMode == ASMIM_ZERO_PAGE) + { + if (MoveShiftZeroPageUp(i)) + { + CheckLive(); + changed = true; + } + } + } + + CheckLive(); +#endif +#if 1 + // move simple add down to consumer + + if (!changed) + { + for (int i = 0; i + 4 < mIns.Size(); i++) + { + if ( + mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && (mIns[i + 3].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z | LIVE_CPU_REG_C)) == 0) + { + if (MoveCLCLoadAddZPStoreDown(i)) + changed = true; + } + } + } + CheckLive(); + +#endif + +#if 1 + for (int i = 0; i + 3 < mIns.Size(); i++) + { + if (mIns[i + 0].mType == ASMIT_TAX && mIns[i + 1].mType == ASMIT_CLC && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress <= 2 && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && !(mIns[i + 3].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C | LIVE_CPU_REG_Z))) + + { + if (MoveTAXADCSTADown(i)) + changed = true; + } + } + + CheckLive(); +#endif + + // Combine and shift + for (int i = 1; i + 2 < mIns.Size(); i++) + { + if (mIns[i + 0].mType == ASMIT_AND && mIns[i + 0].mMode == ASMIM_IMMEDIATE && mIns[i + 0].mAddress == 0x01 && + mIns[i + 1].mType == ASMIT_ORA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + mIns[i + 2].mType == ASMIT_STA && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && mIns[i + 1].mAddress == mIns[i + 2].mAddress && + !(mIns[i + 2].mLive & LIVE_CPU_REG_C)) + { + int j = i - 1; + while (j >= 0 && !mIns[j].ReferencesZeroPage(mIns[i + 1].mAddress)) + j--; + if (j >= 0 && mIns[j].IsShift() && !(mIns[j].mLive & LIVE_CPU_REG_C)) + { + if (mIns[j].mType == ASMIT_ASL) + { + mIns[i + 0].mType = ASMIT_LSR; mIns[i + 0].mMode = ASMIM_IMPLIED; mIns[i + 0].mLive |= LIVE_CPU_REG_C; + mIns[i + 1].mType = ASMIT_ROL; + mIns[i + 2].mType = ASMIT_LDA; + mIns[j + 0].mType = ASMIT_NOP; mIns[j + 0].mMode = ASMIM_IMPLIED; + changed = true; + } + else if (mIns[j].mType == ASMIT_LSR) + { + mIns[i + 0].mType = ASMIT_LSR; mIns[i + 0].mMode = ASMIM_IMPLIED; mIns[i + 0].mLive |= LIVE_CPU_REG_C; + mIns[i + 1].mType = ASMIT_ROR; + mIns[i + 2].mType = ASMIT_LDA; + mIns[j + 0].mType = ASMIT_NOP; mIns[j + 0].mMode = ASMIM_IMPLIED; + changed = true; + } + } + } + } + + CheckLive(); + + int shifti = -1, shiftr = -1; + for (int i = 0; i + 2 < mIns.Size(); i++) + { + if (mIns[i + 0].mType == ASMIT_ASL && mIns[i + 0].mMode == ASMIM_IMPLIED && + mIns[i + 1].mType == ASMIT_STA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + !(mIns[i + 1].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C))) + { + shifti = i; + shiftr = mIns[i + 1].mAddress; + i++; + } + else if (shiftr >= 0) + { + if (shiftr >= 0 && mIns[i].ReferencesZeroPage(shiftr)) + shiftr = -1; + else if ( + mIns[i + 0].mType == ASMIT_AND && mIns[i + 0].mMode == ASMIM_IMMEDIATE && mIns[i + 0].mAddress == 1 && + mIns[i + 1].mType == ASMIT_ORA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && mIns[i + 1].mAddress == shiftr && + !(mIns[i + 1].mLive & (LIVE_MEM | LIVE_CPU_REG_C))) + { + mIns[shifti].mType = ASMIT_NOP; mIns[shifti].mMode = ASMIM_IMPLIED; + mIns[i + 0].mType = ASMIT_LSR; mIns[i + 0].mMode = ASMIM_IMPLIED; mIns[i + 0].mLive |= LIVE_CPU_REG_C; + mIns[i + 1].mType = ASMIT_LDA; mIns[i + 1].mLive |= LIVE_CPU_REG_C; + mIns.Insert(i + 2, NativeCodeInstruction(mIns[i + 0].mIns, ASMIT_ROL)); + shiftr = -1; + changed = true; + } + } + } + + CheckLive(); + +#if 1 + for (int i = 0; i < mIns.Size(); i++) + { + if ((mIns[i + 0].mType == ASMIT_LDX || mIns[i + 0].mType == ASMIT_LDY) && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && !(mIns[i + 0].mLive & LIVE_MEM)) + { + if (MoveSimpleADCToINCDECDown(i)) + changed = true; + } + } + +#endif + + // + // shorten x/y register lifetime + + CheckLive(); +#if 1 + // + // move ldx/y down + + for (int i = 0; i + 1 < mIns.Size(); i++) + { +#if 1 + if (mIns[i].mType == ASMIT_LDY) + { + if (!mIns[i + 1].RequiresYReg() && !mIns[i + 1].ChangesYReg() && !(mIns[i + 1].mLive & LIVE_CPU_REG_Z) && !(mIns[i].mFlags & mIns[i + 1].mFlags & NCIF_VOLATILE)) + { + if (mIns[i].mMode != ASMIM_ABSOLUTE_X || !mIns[i + 1].ChangesXReg()) + { + if (!mIns[i].MayBeChangedOnAddress(mIns[i + 1])) + { + if (mIns[i + 1].SameEffectiveAddress(mIns[i])) + mIns[i + 1].mLive |= LIVE_MEM; + + NativeCodeInstruction ins = mIns[i]; + mIns[i] = mIns[i + 1]; + mIns[i].mLive &= ~LIVE_CPU_REG_Y; + mIns[i + 1] = ins; + mIns[i + 1].mLive |= mIns[i].mLive; + if (mIns[i + 1].mMode == ASMIM_ABSOLUTE_X) + mIns[i].mLive |= LIVE_CPU_REG_X; + } + } + } + } + else if (mIns[i].mType == ASMIT_LDX) + { + if (!mIns[i + 1].RequiresXReg() && !mIns[i + 1].ChangesXReg() && !(mIns[i + 1].mLive & LIVE_CPU_REG_Z) && !(mIns[i].mFlags & mIns[i + 1].mFlags & NCIF_VOLATILE)) + { + if (mIns[i].mMode != ASMIM_ABSOLUTE_Y || !mIns[i + 1].ChangesYReg()) + { + if (!mIns[i].MayBeChangedOnAddress(mIns[i + 1])) + { + if (mIns[i + 1].SameEffectiveAddress(mIns[i])) + mIns[i + 1].mLive |= LIVE_MEM; + + NativeCodeInstruction ins = mIns[i]; + mIns[i] = mIns[i + 1]; + mIns[i].mLive &= ~LIVE_CPU_REG_X; + mIns[i + 1] = ins; + mIns[i + 1].mLive |= mIns[i].mLive; + if (mIns[i + 1].mMode == ASMIM_ABSOLUTE_Y) + mIns[i].mLive |= LIVE_CPU_REG_Y; + } + } + } + } +#endif + } + CheckLive(); + +#endif + +#if 1 + // move stx up + + for (int i = 1; i < mIns.Size(); i++) + { + if (!(mIns[i].mFlags & NCIF_VOLATILE)) + { + if (mIns[i].mType == ASMIT_STX && (mIns[i].mMode == ASMIM_ZERO_PAGE || mIns[i].mMode == ASMIM_ABSOLUTE)) + { + if (MoveStoreXUp(i)) + changed = true; + } + else if (mIns[i].mType == ASMIT_LDX && mIns[i].mMode == ASMIM_ZERO_PAGE && !(mIns[i].mLive & (LIVE_MEM | LIVE_CPU_REG_Z))) + { + if (MoveLoadXUp(i)) + changed = true; + } + else if (mIns[i].mType == ASMIT_STY && (mIns[i].mMode == ASMIM_ZERO_PAGE || mIns[i].mMode == ASMIM_ABSOLUTE)) + { + if (i > 1 || mIns[0].mType != ASMIT_STX) + { + if (MoveStoreYUp(i)) + changed = true; + } + } + else if (mIns[i].mType == ASMIT_LDY && mIns[i].mMode == ASMIM_ZERO_PAGE && !(mIns[i].mLive & (LIVE_MEM | LIVE_CPU_REG_Z))) + { + if (MoveLoadYUp(i)) + changed = true; + } + } + } + CheckLive(); + +#endif + +#if 1 + if (pass > 10) + { + for (int i = 0; i < mIns.Size(); i++) + { + if (!(mIns[i].mFlags & NCIF_VOLATILE)) + { + if (mIns[i].mType == ASMIT_LDX && (mIns[i].mMode == ASMIM_ZERO_PAGE || mIns[i].mMode == ASMIM_ABSOLUTE) && !(mIns[i].mLive & LIVE_CPU_REG_Z)) + { + if (MoveLoadXAbsUpCrossBlock(i)) + changed = true; + } + } + } + CheckLive(); + } +#endif +#if 1 + // move ldy, sty down if live + + for (int i = 0; i + 2 < mIns.Size(); i++) + { + if (mIns[i + 0].mType == ASMIT_LDY && (mIns[i + 0].mMode == ASMIM_ZERO_PAGE || mIns[i + 0].mMode == ASMIM_ABSOLUTE) && + mIns[i + 1].mType == ASMIT_STY && (mIns[i + 1].mMode == ASMIM_ZERO_PAGE || mIns[i + 1].mMode == ASMIM_ABSOLUTE) && + (mIns[i + 2].mLive & LIVE_CPU_REG_Y)) + { + int j = i; + while (j + 2 < mIns.Size() && + !mIns[j + 2].ReferencesYReg() && !(mIns[j + 2].SameEffectiveAddress(mIns[j + 0]) && mIns[j + 2].ChangesAddress()) && !mIns[j + 2].MayBeChangedOnAddress(mIns[j + 1]) && !(mIns[j + 2].mLive & LIVE_CPU_REG_Z)) + { + mIns[j + 0].mLive |= mIns[j + 2].mLive; + mIns[j + 1].mLive |= mIns[j + 2].mLive; + mIns[j + 2].mLive &= ~LIVE_CPU_REG_Y; + mIns.Insert(j, mIns[j + 2]); + mIns.Remove(j + 3); + j++; + } + } + else if (mIns[i + 0].mType == ASMIT_LDX && (mIns[i + 0].mMode == ASMIM_ZERO_PAGE || mIns[i + 0].mMode == ASMIM_ABSOLUTE) && + mIns[i + 1].mType == ASMIT_STX && (mIns[i + 1].mMode == ASMIM_ZERO_PAGE || mIns[i + 1].mMode == ASMIM_ABSOLUTE) && + (mIns[i + 2].mLive & LIVE_CPU_REG_X)) + { + int j = i; + while (j + 2 < mIns.Size() && + !mIns[j + 2].ReferencesXReg() && !(mIns[j + 2].SameEffectiveAddress(mIns[j + 0]) && mIns[j + 2].ChangesAddress()) && !mIns[j + 2].MayBeChangedOnAddress(mIns[j + 1]) && !(mIns[j + 2].mLive & LIVE_CPU_REG_Z)) + { + mIns[j + 0].mLive |= mIns[j + 2].mLive; + mIns[j + 1].mLive |= mIns[j + 2].mLive; + mIns[j + 2].mLive &= ~LIVE_CPU_REG_X; + mIns.Insert(j, mIns[j + 2]); + mIns.Remove(j + 3); + j++; + } + } + } +#endif + +#if 1 + // move clc and sec down + for (int i = 0; i + 1 < mIns.Size(); i++) + { + if ((mIns[i].mType == ASMIT_CLC || mIns[i].mType == ASMIT_SEC) && !mIns[i + 1].RequiresCarry() && !mIns[i + 1].ChangesCarry()) + { + if (i + 2 < mIns.Size() && mIns[i + 1].mType == ASMIT_LDA && (mIns[i + 1].mMode == ASMIM_IMMEDIATE || mIns[i + 1].mMode == ASMIM_IMMEDIATE_ADDRESS || mIns[i + 1].mMode == ASMIM_ZERO_PAGE) && mIns[i + 2].RequiresCarry()) + ; + else if (i + 2 < mIns.Size() && mIns[i + 1].mType == ASMIT_LDY && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mMode == ASMIM_INDIRECT_Y) + ; + else + { + NativeCodeInstruction pins = mIns[i]; + mIns[i] = mIns[i + 1]; + mIns[i + 1] = pins; + mIns[i + 1].mLive |= mIns[i].mLive; + // changed = true; + } + } + } + CheckLive(); + + +#endif + +#if 1 + // move iny/dey/inx/dex down + + for (int i = 0; i + 1 < mIns.Size(); i++) + { + if ((mIns[i].mType == ASMIT_INY || mIns[i].mType == ASMIT_DEY) && !mIns[i + 1].ChangesYReg() && !(mIns[i + 1].mLive & LIVE_CPU_REG_Z)) + { + if (i + 3 == mIns.Size() && mIns[i + 1].mType == ASMIT_INX && mIns[i + 2].mType == ASMIT_CPX) + ; // Avoid bypassing loop iterator + else if (!mIns[i + 1].RequiresYReg()) + { + NativeCodeInstruction pins = mIns[i]; + mIns[i] = mIns[i + 1]; + mIns[i].mLive |= LIVE_CPU_REG_Y; + mIns[i + 1] = pins; + mIns[i + 1].mLive |= mIns[i].mLive; + } + else if (mIns[i + 1].mMode == ASMIM_ABSOLUTE_Y) + { + int cy = RetrieveYValue(i - 1, 2); + if (mIns[i].mType == ASMIT_INY) + { + if (cy == 255) + mIns[i + 1].mMode = ASMIM_ABSOLUTE; + else + mIns[i + 1].mAddress++; + } + else + mIns[i + 1].mAddress--; + NativeCodeInstruction pins = mIns[i]; + mIns[i] = mIns[i + 1]; + mIns[i].mLive |= LIVE_CPU_REG_Y; + mIns[i + 1] = pins; + mIns[i + 1].mLive |= mIns[i].mLive; + } + } + else if ((mIns[i].mType == ASMIT_INX || mIns[i].mType == ASMIT_DEX) && !mIns[i + 1].ChangesXReg() && !(mIns[i + 1].mLive & LIVE_CPU_REG_Z)) + { + if (i + 3 == mIns.Size() && mIns[i + 1].mType == ASMIT_INY && mIns[i + 2].mType == ASMIT_CPY) + ; // Avoid bypassing loop iterator + else if (!mIns[i + 1].RequiresXReg()) + { + NativeCodeInstruction pins = mIns[i]; + mIns[i] = mIns[i + 1]; + mIns[i].mLive |= LIVE_CPU_REG_X; + mIns[i + 1] = pins; + mIns[i + 1].mLive |= mIns[i].mLive; + } + else if (mIns[i + 1].mMode == ASMIM_ABSOLUTE_X) + { + int cx = RetrieveXValue(i - 1, 2); + if (mIns[i].mType == ASMIT_INX) + { + if (cx == 255) + mIns[i + 1].mMode = ASMIM_ABSOLUTE; + else + mIns[i + 1].mAddress++; + } + else + mIns[i + 1].mAddress--; + NativeCodeInstruction pins = mIns[i]; + mIns[i] = mIns[i + 1]; + mIns[i].mLive |= LIVE_CPU_REG_X; + mIns[i + 1] = pins; + mIns[i + 1].mLive |= mIns[i].mLive; + } + } + } + CheckLive(); + +#endif + +#if 1 + // move lda zp/and/tay down + for (int i = 0; i + 2 < mIns.Size(); i++) + { + if (mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && mIns[i + 1].IsLogic() && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mType == ASMIT_TAY && !(mIns[i + 2].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z))) + { + if (MoveZpImmTAYDown(i)) + changed = true; + } + } + CheckLive(); +#endif + +#if 1 + // move tya/clc/adc/tay down + for (int i = 0; i + 5 < mIns.Size(); i++) + { + if (mIns[i + 0].mType == ASMIT_TYA && mIns[i + 1].mType == ASMIT_CLC && mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 3].mType == ASMIT_TAY && !(mIns[i + 3].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C))) + { + if (mIns[i + 4].mType == ASMIT_LDA && (mIns[i + 4].mMode == ASMIM_IMMEDIATE || mIns[i + 4].mMode == ASMIM_IMMEDIATE_ADDRESS || mIns[i + 4].mMode == ASMIM_ZERO_PAGE) && + mIns[i + 5].mType == ASMIT_STA && mIns[i + 5].mMode != ASMIM_INDIRECT_Y && !(mIns[i + 5].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z))) + { + mIns[i + 4].mLive |= LIVE_CPU_REG_Y; + mIns[i + 5].mLive |= LIVE_CPU_REG_Y; + if (mIns[i + 5].mMode == ASMIM_ABSOLUTE_Y) + mIns[i + 5].mAddress += mIns[i + 2].mAddress; + + mIns.Insert(i + 0, mIns[i + 4]); mIns.Remove(i + 5); + mIns.Insert(i + 1, mIns[i + 5]); mIns.Remove(i + 6); + } +#if 1 + else if (i + 6 < mIns.Size() && + mIns[i + 4].mType == ASMIT_LDA && (mIns[i + 4].mMode == ASMIM_IMMEDIATE || mIns[i + 4].mMode == ASMIM_IMMEDIATE_ADDRESS || mIns[i + 4].mMode == ASMIM_ZERO_PAGE) && + mIns[i + 5].mType == ASMIT_STA && mIns[i + 5].mMode != ASMIM_INDIRECT_Y && + mIns[i + 6].mType == ASMIT_STA && mIns[i + 6].mMode != ASMIM_INDIRECT_Y && !(mIns[i + 6].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z))) + { + mIns[i + 4].mLive |= LIVE_CPU_REG_Y; + mIns[i + 5].mLive |= LIVE_CPU_REG_Y; + mIns[i + 6].mLive |= LIVE_CPU_REG_Y; + if (mIns[i + 5].mMode == ASMIM_ABSOLUTE_Y) + mIns[i + 5].mAddress += mIns[i + 2].mAddress; + if (mIns[i + 6].mMode == ASMIM_ABSOLUTE_Y) + mIns[i + 6].mAddress += mIns[i + 2].mAddress; + + mIns.Insert(i + 0, mIns[i + 4]); mIns.Remove(i + 5); + mIns.Insert(i + 1, mIns[i + 5]); mIns.Remove(i + 6); + mIns.Insert(i + 2, mIns[i + 6]); mIns.Remove(i + 7); + } +#endif + } + } + CheckLive(); + + +#endif + +#if 1 + + // reverse "sta t,lda abs,clc,adc t" to "sta t,clc,adc abs,nop" + + for (int i = 1; i + 2 < mIns.Size(); i++) + { + if (mIns[i + 2].IsCommutative() && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && (mIns[i + 1].mType == ASMIT_CLC || mIns[i + 1].mType == ASMIT_SEC) && mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode != ASMIM_ZERO_PAGE) + { + if (ReverseLoadCommutativeOpUp(i, i + 2)) + changed = true; + } + else if (mIns[i + 1].IsCommutative() && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode != ASMIM_ZERO_PAGE) + { + if (ReverseLoadCommutativeOpUp(i, i + 1)) + changed = true; + } + } + CheckLive(); + +#endif + + // shortcut index +#if 1 + for (int i = 0; i + 1 < mIns.Size(); i++) + { + if (mIns[i].mType == ASMIT_TXA && mIns[i + 1].mType == ASMIT_TAY) + { + int j = i + 2, k = i, n = i; + while (j < mIns.Size() && !mIns[j].ChangesXReg() && !mIns[j].ChangesYReg()) + { + if (mIns[j].mMode == ASMIM_ABSOLUTE_Y) + { + assert(HasAsmInstructionMode(mIns[j].mType, ASMIM_ABSOLUTE_X)); + mIns[j].mMode = ASMIM_ABSOLUTE_X; + mIns[j].mFlags |= NICT_INDEXFLIPPED; + n = j; + changed = true; + } + j++; + } + + while (k < n) + { + mIns[k].mLive |= LIVE_CPU_REG_X; + k++; + } + } + else if (mIns[i].mType == ASMIT_TYA && mIns[i + 1].mType == ASMIT_TAX) + { + int j = i + 2, k = i, n = i; + + while (j < mIns.Size() && !mIns[j].ChangesXReg() && !mIns[j].ChangesYReg()) + { + if (mIns[j].mMode == ASMIM_ABSOLUTE_X) + { + assert(HasAsmInstructionMode(mIns[j].mType, ASMIM_ABSOLUTE_Y)); + mIns[j].mMode = ASMIM_ABSOLUTE_Y; + mIns[j].mFlags |= NICT_INDEXFLIPPED; + n = j; + changed = true; + } + j++; + } + + while (k < n) + { + mIns[k].mLive |= LIVE_CPU_REG_Y; + k++; + } + } + } + CheckLive(); + +#endif + +#if 1 + for (int i = 0; i + 2 < mIns.Size(); i++) + { + if (mIns[i + 0].mType == ASMIT_TAX && + mIns[i + 1].mType == ASMIT_LDA && (mIns[i + 1].mMode == ASMIM_ABSOLUTE || mIns[i + 1].mMode == ASMIM_ABSOLUTE_Y || mIns[i + 1].mMode == ASMIM_ZERO_PAGE) && + mIns[i + 2].mType == ASMIT_STA && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && !(mIns[i + 2].mLive & (LIVE_CPU_REG_Z | LIVE_CPU_REG_A))) + { + if (MoveLoadStoreOutOfXYRangeUp(i)) + changed = true; + } + else if ( + mIns[i + 0].mType == ASMIT_TAY && + mIns[i + 1].mType == ASMIT_LDA && (mIns[i + 1].mMode == ASMIM_ABSOLUTE || mIns[i + 1].mMode == ASMIM_ABSOLUTE_X || mIns[i + 1].mMode == ASMIM_ZERO_PAGE) && + mIns[i + 2].mType == ASMIT_STA && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && !(mIns[i + 2].mLive & (LIVE_CPU_REG_Z | LIVE_CPU_REG_A))) + { + if (MoveLoadStoreOutOfXYRangeUp(i)) + changed = true; + } + } + CheckLive(); + +#endif + +#if 1 + for (int i = 0; i < mIns.Size(); i++) + { + if (mIns[i].mType == ASMIT_TAX && !(mIns[i].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C | LIVE_CPU_REG_Z))) + { + if (ReverseReplaceTAX(i)) + changed = true; + } + } + CheckLive(); +#endif + +#if 1 + // Move TAX and TAY down + + for (int i = 0; i + 1 < mIns.Size(); i++) + { + if (mIns[i].mType == ASMIT_TAX) + { + int j = i + 1; + while (j < mIns.Size() && !mIns[j].ReferencesXReg() && !mIns[j].ChangesAccu()) + j++; + if (j < mIns.Size() && mIns[j].mType == ASMIT_CPX && !(mIns[j].mLive & LIVE_CPU_REG_X)) + { + mIns[j].mType = ASMIT_CMP; + while (j > i) + { + j--; + mIns[j].mLive |= LIVE_CPU_REG_A; + } + changed = true; + } + } + else if (mIns[i].mType == ASMIT_TAY) + { + int j = i + 1; + while (j < mIns.Size() && !mIns[j].ReferencesYReg() && !mIns[j].ChangesAccu()) + j++; + if (j < mIns.Size() && mIns[j].mType == ASMIT_CPY && !(mIns[j].mLive & LIVE_CPU_REG_Y)) + { + mIns[j].mType = ASMIT_CMP; + while (j > i) + { + j--; + mIns[j].mLive |= LIVE_CPU_REG_A; + } + changed = true; + } + } + } + CheckLive(); +#endif +#if 1 + int taxPos = -1, tayPos = -1; + for (int i = 0; i < mIns.Size(); i++) + { + if (mIns[i].mType == ASMIT_TAX) + { + if (!(mIns[i].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z))) + taxPos = i; + else + taxPos = -1; + } + else if (mIns[i].mType == ASMIT_TAY) + { + if (!(mIns[i].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z))) + tayPos = i; + else + tayPos = -1; + } + else if (mIns[i].ChangesXReg()) + taxPos = -1; + else if (mIns[i].ChangesYReg()) + tayPos = -1; + else if (mIns[i].mType == ASMIT_TXA) + { + if (taxPos >= 0) + { + if (!(mIns[i].mLive & (LIVE_CPU_REG_X | LIVE_CPU_REG_Z)) && JoinTAXARange(taxPos, i) || JoinTAXARangeLive(taxPos, i)) + changed = true; + taxPos = -1; tayPos = -1; + } + else + taxPos = -1; + + } + else if (mIns[i].mType == ASMIT_TYA) + { + if (tayPos >= 0) + { + if (!(mIns[i].mLive & (LIVE_CPU_REG_Y | LIVE_CPU_REG_Z)) && JoinTAYARange(tayPos, i) || JoinTAYARangeLive(tayPos, i)) + changed = true; + taxPos = -1; tayPos = -1; + } + else + tayPos = -1; + } + else if (mIns[i].RequiresXReg()) + taxPos = -1; + else if (mIns[i].RequiresYReg()) + tayPos = -1; + } + CheckLive(); + +#endif + +#if 1 + for (int i = 0; i + 2 < mIns.Size(); i++) + { + if (mIns[i].mType == ASMIT_TAX && mIns[i + 1].mType == ASMIT_TAY || + mIns[i].mType == ASMIT_TAY && mIns[i + 1].mType == ASMIT_TAX) + { + if (MergeXYSameValue(i + 2)) + changed = true; + } + } + CheckLive(); + +#endif + + +#if 1 + if (pass > 1) + { + // move high byte load down, if low byte is immediately needed afterwards + + for (int i = 0; i + 4 < mIns.Size(); i++) + { + if (mIns[i + 0].mType == ASMIT_STA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_LDY && mIns[i + 1].mMode == ASMIM_IMMEDIATE && + mIns[i + 2].mType == ASMIT_LDA && mIns[i + 2].mMode == ASMIM_INDIRECT_Y && mIns[i + 2].mAddress != mIns[i + 3].mAddress && mIns[i + 2].mAddress + 1 != mIns[i + 3].mAddress && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && mIns[i + 3].mAddress != mIns[i + 0].mAddress && + mIns[i + 4].mType == ASMIT_LDA && mIns[i + 4].mMode == ASMIM_ZERO_PAGE && mIns[i + 4].mAddress == mIns[i + 0].mAddress && !(mIns[i + 4].mLive & LIVE_CPU_REG_Z)) + { + if (MoveStoreHighByteDown(i)) + changed = true; + } + } + CheckLive(); + + for (int i = 0; i + 4 < mIns.Size(); i++) + { + if (mIns[i + 0].mType == ASMIT_STA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && mIns[i + 1].mAddress != mIns[i + 0].mAddress && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && mIns[i + 3].mAddress != mIns[i + 0].mAddress && + mIns[i + 4].mType == ASMIT_LDA && mIns[i + 4].mMode == ASMIM_ZERO_PAGE && mIns[i + 4].mAddress == mIns[i + 0].mAddress && !(mIns[i + 4].mLive & LIVE_CPU_REG_Z)) + { + if (MoveAddHighByteDown(i)) + changed = true; + } + } + + CheckLive(); + } +#endif + +#if 1 + for (int i = 2; i + 1 < mIns.Size(); i++) + { + if (mIns[i + 0].mType == ASMIT_LDA && (mIns[i + 0].mMode == ASMIM_IMMEDIATE || mIns[i + 0].mMode == ASMIM_ZERO_PAGE) && + mIns[i + 1].mType == ASMIT_STA && (mIns[i + 1].mMode == ASMIM_ABSOLUTE || mIns[i + 1].mMode == ASMIM_ABSOLUTE_X || mIns[i + 1].mMode == ASMIM_ABSOLUTE_Y || mIns[i + 1].mMode == ASMIM_ZERO_PAGE) && !(mIns[i + 1].mFlags & NCIF_VOLATILE)) + { + if (MoveLoadImmStoreAbsoluteUp(i + 0)) + changed = true; + } + } + CheckLive(); +#endif + +#if 1 + for (int i = 2; i < mIns.Size(); i++) + { + if ((mIns[i].mType == ASMIT_ASL || mIns[i].mType == ASMIT_LSR) && mIns[i].mMode == ASMIM_ZERO_PAGE && !(mIns[i].mLive & LIVE_CPU_REG_Z)) + { + if (MoveASLMemUp(i)) + changed = true; + } + } + CheckLive(); +#endif + + if (pass > 1) + { +#if 1 + // replace zero page down + + for (int i = 0; i + 1 < mIns.Size(); i++) + { + if (mIns[i].mType == ASMIT_LDA && mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i + 1].mType == ASMIT_STA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && !(mIns[i + 1].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z)) && (mIns[i + 0].mLive & LIVE_MEM)) + { + if (ReplaceZeroPageDown(i)) + changed = true; + } + } + CheckLive(); + +#endif + } +#endif + + CheckLive(); + +#if 1 + for (int i = 0; i + 4 < mIns.Size(); i++) + { + if (mIns[i + 0].mType == ASMIT_LSR && mIns[i + 0].mMode == ASMIM_IMPLIED && + mIns[i + 1].mType == ASMIT_STA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + mIns[i + 2].mType == ASMIT_LDA && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && mIns[i + 1].mAddress != mIns[i + 2].mAddress && + mIns[i + 3].mType == ASMIT_ROR && mIns[i + 3].mMode == ASMIM_IMPLIED) + { + int j = 2; + while (i + 3 + j < mIns.Size() && + mIns[i + 2 + j].mType == ASMIT_LSR && mIns[i + 2 + j].mMode == ASMIM_ZERO_PAGE && mIns[i + 2 + j].mAddress == mIns[i + 1].mAddress && + mIns[i + 3 + j].mType == ASMIT_ROR && mIns[i + 3 + j].mMode == ASMIM_IMPLIED) + { + j += 2; + } + + if (i + 2 + j < mIns.Size() && mIns[i + 2 + j].mType == ASMIT_STA && mIns[i + 2 + j].mMode == ASMIM_ZERO_PAGE && + mIns[i + 2 + j].mAddress == mIns[i + 2].mAddress && !(mIns[i + 2 + j].mLive & LIVE_CPU_REG_A)) + { + int addrl = mIns[i + 2].mAddress, addrh = mIns[i + 1].mAddress; + const InterInstruction* iins(mIns[i + 1].mIns); + + for (int k = 0; k < j; k += 2) + { + mIns[i + k + 0] = NativeCodeInstruction(iins, ASMIT_LSR, ASMIM_IMPLIED); + mIns[i + k + 1] = NativeCodeInstruction(iins, ASMIT_ROR, ASMIM_ZERO_PAGE, addrl); + } + mIns[i + j] = NativeCodeInstruction(iins, ASMIT_STA, ASMIM_ZERO_PAGE, addrh); + mIns[i + j + 1].mType = ASMIT_NOP; mIns[i + j + 1].mMode = ASMIM_IMPLIED; + mIns[i + j + 2].mType = ASMIT_NOP; mIns[i + j + 2].mMode = ASMIM_IMPLIED; + changed = true; + } + } + } + + CheckLive(); +#endif + + // move sta zp, tax, lda abs,x , sta zp down to use +#if 1 + for (int i = 0; i + 4 < mIns.Size(); i++) + { + if (mIns[i + 0].mType == ASMIT_STA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_TAX && + mIns[i + 2].mType == ASMIT_LDA && mIns[i + 2].mMode == ASMIM_ABSOLUTE_X && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && + !(mIns[i + 3].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_X | LIVE_CPU_REG_Z))) + { + if (MoveStaTaxLdaStaDown(i)) + changed = true; + } + } + CheckLive(); + + +#endif + + +#if 1 + for (int i = 0; i + 4 < mIns.Size(); i++) + { + if (mIns[i + 0].mType == ASMIT_TXA && + mIns[i + 1].mType == ASMIT_CLC && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && (mIns[i + 2].mAddress >= -2 && mIns[i + 2].mAddress <= 2 || mIns[i + 2].mAddress >= 254 && mIns[i + 2].mAddress < 256) && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && !(mIns[i + 3].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z | LIVE_CPU_REG_C))) + { + if (MoveTXADCDown(i)) + changed = true; + } + } + + CheckLive(); + +#endif + +#if 1 + for (int i = 0; i + 3 < mIns.Size(); i++) + { + if (mIns[i + 0].mType == ASMIT_TXA && + mIns[i + 1].IsLogic() && mIns[i + 1].mMode == ASMIM_IMMEDIATE && + mIns[i + 2].mType == ASMIT_TAX && !(mIns[i + 2].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z))) + { + if (MoveTXALogicTAXDown(i)) + changed = true; + } + } + + CheckLive(); + +#endif + +#if 1 + for (int i = 0; i + 2 < mIns.Size(); i++) + { + if (mIns[i + 0].mType == ASMIT_LSR && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && !(mIns[i + 0].mLive & LIVE_MEM) && + mIns[i + 1].mType == ASMIT_ROR && mIns[i + 1].mMode == ASMIM_IMPLIED && + mIns[i + 2].mType == ASMIT_STA && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && !(mIns[i + 2].mLive & LIVE_CPU_REG_A)) + { + if (ReverseShiftByteOrder(i)) + changed = true; + } + } + + CheckLive(); + + for (int i = 0; i + 2 < mIns.Size(); i++) + { + if (mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_ASL && mIns[i + 1].mMode == ASMIM_IMPLIED && !(mIns[i + 1].mLive & LIVE_CPU_REG_A)) + { + if (MoveZeroPageSignToCarryDown(i)) + changed = true; + } + } + + CheckLive(); + +#endif + + for (int i = 2; i + 1 < mIns.Size(); i++) + { + if (mIns[i + 0].mType == ASMIT_ASL && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_ROL && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + mIns[i + 0].mAddress != mIns[i + 1].mAddress && !(mIns[i + 1].mLive & LIVE_CPU_REG_C)) + { + if (Move16BitShiftUp(i)) + changed = true; + } + } + + CheckLive(); + + for (int i = 0; i + 2 < mIns.Size(); i++) + { + if (mIns[i + 0].mType == ASMIT_ASL && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_ROL && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + mIns[i + 0].mAddress != mIns[i + 1].mAddress && !(mIns[i + 1].mLive & LIVE_CPU_REG_C)) + { + if (Move16BitShiftDown(i)) + changed = true; + } + else if ( + mIns[i + 0].mType == ASMIT_LSR && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_ROR && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + mIns[i + 0].mAddress != mIns[i + 1].mAddress && !(mIns[i + 1].mLive & LIVE_CPU_REG_C)) + { + if (Move16BitShiftDown(i)) + changed = true; + } + } + + CheckLive(); + +#if 1 + for (int i = 3; i + 2 < mIns.Size(); i++) + { + if (mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_IMMEDIATE && mIns[i + 0].mAddress == 0 && + mIns[i + 1].mType == ASMIT_ROR && mIns[i + 1].mMode == ASMIM_IMPLIED && + mIns[i + 2].mType == ASMIT_ORA && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && !(mIns[i + 2].mLive & (LIVE_CPU_REG_C | LIVE_MEM))) + { + int at = i - 3; + while (at >= 0 && + !(mIns[at + 0].mType == ASMIT_LDA && mIns[at + 0].mMode == ASMIM_ZERO_PAGE && + mIns[at + 1].mType == ASMIT_LSR && mIns[at + 1].mMode == ASMIM_IMPLIED && + mIns[at + 2].mType == ASMIT_STA && mIns[at + 2].mMode == ASMIM_ZERO_PAGE && mIns[at + 2].mAddress == mIns[i + 2].mAddress)) + { + at--; + } + + if (at >= 0 && !ChangesZeroPage(mIns[at + 0].mAddress, at + 2, i) && !ChangesZeroPage(mIns[at + 2].mAddress, at + 3, i)) + { + mIns[i + 0].CopyMode(mIns[at + 0]); + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + changed = true; + } + } + } + + CheckLive(); + +#endif + + +#if 1 + for (int i = 0; i + 1 < mIns.Size(); i++) + { + if (mIns[i + 0].mType == ASMIT_ASL && mIns[i + 0].mMode == ASMIM_IMPLIED && + mIns[i + 1].mType == ASMIT_LSR && mIns[i + 1].mMode == ASMIM_IMPLIED && !(mIns[i + 1].mLive & (LIVE_CPU_REG_Z | LIVE_CPU_REG_C))) + { + int j = 1; + while (i >= j && i + j + 1 < mIns.Size() && + mIns[i - j].mType == ASMIT_ASL && mIns[i - j].mMode == ASMIM_IMPLIED && + mIns[i + 1 + j].mType == ASMIT_LSR && mIns[i + 1 + j].mMode == ASMIM_IMPLIED && !(mIns[i + 1 + j].mLive & (LIVE_CPU_REG_Z | LIVE_CPU_REG_C))) + j++; + mIns[i + 1 - j].mType = ASMIT_AND; + mIns[i + 1 - j].mMode = ASMIM_IMMEDIATE; + mIns[i + 1 - j].mAddress = 0xff >> j; + + while (j > 1) + { + mIns[i + j].mType = ASMIT_NOP; + mIns[i + j].mMode = ASMIM_IMPLIED; + j--; + mIns[i + 1 - j].mType = ASMIT_NOP; + mIns[i + 1 - j].mMode = ASMIM_IMPLIED; + } + mIns[i + 1].mType = ASMIT_NOP; + mIns[i + 1].mMode = ASMIM_IMPLIED; + changed = true; + } + } + + CheckLive(); + +#endif +#if 1 +#if 0 + for (int i = 0; i + 6 < mIns.Size(); i++) + { + if (mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && mIns[i + 3].mAddress != mIns[i + 1].mAddress && + mIns[i + 4].mType == ASMIT_LDA && mIns[i + 4].mMode == ASMIM_ZERO_PAGE && + mIns[i + 5].mType == ASMIT_ADC && mIns[i + 5].mMode == ASMIM_IMMEDIATE && + mIns[i + 6].mType == ASMIT_STA && mIns[i + 6].mMode == ASMIM_ZERO_PAGE && mIns[i + 6].mAddress != mIns[i + 4].mAddress && + !(mIns[i + 6].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C))) + { + if (Move16BitAddUp(i)) + changed = true; + } + } +#endif + +#if 1 + if (pass > 4 && pass < 8) + { + for (int i = 0; i + 1 < mIns.Size(); i++) + { + if ((mIns[i].mType == ASMIT_INC || mIns[i].mType == ASMIT_DEC) && (mIns[i].mMode == ASMIM_ZERO_PAGE || mIns[i].mMode == ASMIM_ABSOLUTE) && !(mIns[i].mLive & LIVE_CPU_REG_Z) && !(mIns[i].mFlags & NCIF_VOLATILE)) + { + if (mIns[i + 1].mType != ASMIT_INC && mIns[i + 1].mType != ASMIT_DEC && mIns[i].MayBeMovedBehind(mIns[i + 1]) && !(mIns[i + 1].mLive & LIVE_CPU_REG_Z)) + { + NativeCodeInstruction ins = mIns[i + 1]; + mIns[i + 1] = mIns[i]; + mIns[i] = ins; + mIns[i + 1].mLive |= ins.mLive; + } + else if (i > 0 && mIns[i - 1].mType == ASMIT_LDA && !mIns[i - 1].MayBeSameAddress(mIns[i])) + { + NativeCodeInstruction ins = mIns[i]; + mIns[i] = mIns[i - 1]; + mIns[i - 1] = ins; + mIns[i].mLive |= ins.mLive; + } + } + } + } +#endif + +#endif + + return changed; +} + +bool NativeCodeBasicBlock::PeepHoleOptimizerIterate1(int i, int pass) +{ + if (mIns[i].mMode == ASMIM_ABSOLUTE && mIns[i].mLinkerObject && (mIns[i].mLinkerObject->mFlags & LOBJF_CONST) && + mIns[i].mLinkerObject->mReferences.Size() == 0 && + mIns[i].mAddress < mIns[i].mLinkerObject->mSize && + !mIns[i].ChangesAddress() && HasAsmInstructionMode(mIns[i].mType, ASMIM_IMMEDIATE)) + { + mIns[i].mMode = ASMIM_IMMEDIATE; + mIns[i].mAddress = mIns[i].mLinkerObject->mData[mIns[i].mAddress]; + mIns[i].mLinkerObject = nullptr; + return true; + } + + if (mIns[i].mType == ASMIT_AND && mIns[i].mMode == ASMIM_IMMEDIATE && mIns[i].mAddress == 0) + { + mIns[i].mType = ASMIT_LDA; + return true; + } + else if (mIns[i].mType == ASMIT_AND && mIns[i].mMode == ASMIM_IMMEDIATE && mIns[i].mAddress == 0xff && (mIns[i].mLive & LIVE_CPU_REG_Z) == 0) + { + mIns[i].mType = ASMIT_NOP; mIns[i].mMode = ASMIM_IMPLIED;; + return true; + } + else if (mIns[i].mType == ASMIT_ORA && mIns[i].mMode == ASMIM_IMMEDIATE && mIns[i].mAddress == 0xff) + { + mIns[i].mType = ASMIT_LDA; + return true; + } + else if (mIns[i].mType == ASMIT_ORA && mIns[i].mMode == ASMIM_IMMEDIATE && mIns[i].mAddress == 0x00 && (mIns[i].mLive & LIVE_CPU_REG_Z) == 0) + { + mIns[i].mType = ASMIT_NOP; mIns[i].mMode = ASMIM_IMPLIED;; + return true; + } + else if (mIns[i].mType == ASMIT_EOR && mIns[i].mMode == ASMIM_IMMEDIATE && mIns[i].mAddress == 0x00 && (mIns[i].mLive & LIVE_CPU_REG_Z) == 0) + { + mIns[i].mType = ASMIT_NOP; mIns[i].mMode = ASMIM_IMPLIED;; + return true; + } + else if (mIns[i].mType == ASMIT_CMP && mIns[i].mMode == ASMIM_IMMEDIATE && mIns[i].mAddress == 0x00 && (mIns[i].mLive & LIVE_CPU_REG_Z) == 0) + { + mIns[i].mType = ASMIT_SEC; mIns[i].mMode = ASMIM_IMPLIED;; + return true; + } + else if (mIns[i].mType == ASMIT_ROR && mIns[i].mMode == ASMIM_IMPLIED && (mIns[i].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z)) == 0) + { + mIns[i].mType = ASMIT_LSR; + return true; + } + else if (mIns[i].mType == ASMIT_ROL && mIns[i].mMode == ASMIM_IMPLIED && (mIns[i].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z)) == 0) + { + mIns[i].mType = ASMIT_ASL; + return true; + } + else if (mIns[i].mType == ASMIT_TAY && !(mIns[i].mLive & LIVE_CPU_REG_Y)) + { + mIns[i].mType = ASMIT_ORA; + mIns[i].mMode = ASMIM_IMMEDIATE; + mIns[i].mAddress = 0; + return true; + } + else if (mIns[i].mType == ASMIT_TAX && !(mIns[i].mLive & LIVE_CPU_REG_X)) + { + mIns[i].mType = ASMIT_ORA; + mIns[i].mMode = ASMIM_IMMEDIATE; + mIns[i].mAddress = 0; + return true; + } + + int apos; + if (mIns[i].mMode == ASMIM_INDIRECT_Y && FindGlobalAddress(i, mIns[i].mAddress, apos)) + { + mIns[i].mMode = ASMIM_ABSOLUTE_Y; + mIns[i].mAddress = mIns[apos].mAddress; + mIns[i].mLinkerObject = mIns[apos].mLinkerObject; + return true; + } + + return false; +} + +bool NativeCodeBasicBlock::PeepHoleOptimizerIterate2(int i, int pass) +{ + if (mIns[i].mType == ASMIT_LDA && mIns[i + 1].mType == ASMIT_LDA && !(mIns[i].mFlags & NCIF_VOLATILE)) + { + mIns[i].mType = ASMIT_NOP; mIns[i].mMode = ASMIM_IMPLIED;; + return true; + } + if (mIns[i].mType == ASMIT_LDA && mIns[i + 1].mType == ASMIT_STA && mIns[i].SameEffectiveAddress(mIns[i + 1]) && !(mIns[i + 1].mFlags & NCIF_VOLATILE)) + { + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED;; + return true; + } + if (mIns[i].mType == ASMIT_STA && mIns[i + 1].mType == ASMIT_LDA && mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && mIns[i].mAddress == mIns[i + 1].mAddress && (mIns[i + 1].mLive & LIVE_CPU_REG_Z) == 0) + { + mIns[i + 0].mLive |= LIVE_CPU_REG_A; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + return true; + } + if (mIns[i].mType == ASMIT_STA && mIns[i + 1].mType == ASMIT_LDA && mIns[i].SameEffectiveAddress(mIns[i + 1]) && !(mIns[i + 1].mFlags & NCIF_VOLATILE) && (mIns[i + 1].mLive & LIVE_CPU_REG_Z) == 0) + { + mIns[i + 0].mLive |= LIVE_CPU_REG_A; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + return true; + } + if (mIns[i].mType == ASMIT_AND && mIns[i + 1].mType == ASMIT_AND && mIns[i].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mMode == ASMIM_IMMEDIATE) + { + mIns[i].mAddress &= mIns[i + 1].mAddress; + mIns[i + 0].mLive |= (mIns[i + 1].mLive & LIVE_CPU_REG_Z); + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + return true; + } + if (mIns[i].mType == ASMIT_ORA && mIns[i + 1].mType == ASMIT_ORA && mIns[i].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mMode == ASMIM_IMMEDIATE) + { + mIns[i].mAddress |= mIns[i + 1].mAddress; + mIns[i + 0].mLive |= (mIns[i + 1].mLive & LIVE_CPU_REG_Z); + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + return true; + } + if (mIns[i].mType == ASMIT_EOR && mIns[i + 1].mType == ASMIT_EOR && mIns[i].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mMode == ASMIM_IMMEDIATE) + { + mIns[i].mAddress ^= mIns[i + 1].mAddress; + mIns[i + 0].mLive |= (mIns[i + 1].mLive & LIVE_CPU_REG_Z); + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + return true; + } + if (mIns[i].mType == ASMIT_LDA && mIns[i + 1].mType == ASMIT_ORA && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 0) + { + mIns[i + 0].mLive |= (mIns[i + 1].mLive & LIVE_CPU_REG_Z); + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + return true; + } + if (mIns[i].mType == ASMIT_LDA && mIns[i + 1].mType == ASMIT_EOR && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 0) + { + mIns[i + 0].mLive |= (mIns[i + 1].mLive & LIVE_CPU_REG_Z); + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + return true; + } + if (mIns[i].mType == ASMIT_LDA && mIns[i + 1].mType == ASMIT_AND && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 0xff) + { + mIns[i + 0].mLive |= (mIns[i + 1].mLive & LIVE_CPU_REG_Z); + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + return true; + } + if (mIns[i].mType == ASMIT_CLC && mIns[i + 1].mType == ASMIT_ROR) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_LSR; + return true; + } + if (mIns[i].mType == ASMIT_CLC && mIns[i + 1].mType == ASMIT_ADC && mIns[i + 1].mMode == ASMIM_IMMEDIATE && !(mIns[i + 1].mLive & LIVE_CPU_REG_A)) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_CMP; mIns[i + 1].mAddress = (-mIns[i + 1].mAddress) & 0xff; + return true; + } + if (mIns[i].mType == ASMIT_SEC && mIns[i + 1].mType == ASMIT_SBC && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 0x80 && !(mIns[i + 1].mLive & LIVE_CPU_REG_C)) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_EOR; + return true; + } + if (mIns[i].mType == ASMIT_CLC && mIns[i + 1].mType == ASMIT_ADC && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 0x80 && !(mIns[i + 1].mLive & LIVE_CPU_REG_C)) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_EOR; + return true; + } + if (mIns[i].mType == ASMIT_LDA && mIns[i].mMode == ASMIM_IMMEDIATE && mIns[i].mAddress == 0 && mIns[i + 1].mType == ASMIT_LSR && mIns[i + 1].mMode == ASMIM_IMPLIED) + { + mIns[i + 1].mType = ASMIT_CLC; + return true; + } + if (mIns[i].mType == ASMIT_CLC && mIns[i + 1].mType == ASMIT_ADC && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 0 && !(mIns[i + 1].mLive & LIVE_CPU_REG_Z)) + { + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + return true; + } + if (mIns[i].mType == ASMIT_SEC && mIns[i + 1].mType == ASMIT_SBC && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 0 && !(mIns[i + 1].mLive & LIVE_CPU_REG_Z)) + { + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + return true; + } + if (mIns[i].mType == ASMIT_SEC && mIns[i + 1].mType == ASMIT_ADC && mIns[i + 1].mMode == ASMIM_IMMEDIATE && !(mIns[i + 1].mLive & LIVE_CPU_REG_C)) + { + mIns[i + 0].mType = ASMIT_CLC; + mIns[i + 1].mAddress++; + return true; + } + if (mIns[i + 0].mType == ASMIT_LDA && mIns[i + 1].mType == ASMIT_ADC && mIns[i + 1].SameEffectiveAddress(mIns[i + 0])) + { + mIns[i + 1].mType = ASMIT_ROL; + mIns[i + 1].mMode = ASMIM_IMPLIED; + return true; + } + if (mIns[i + 1].mType == ASMIT_ORA && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 0 && mIns[i].ChangesFlagToAccu()) + { + mIns[i + 0].mLive |= (mIns[i + 1].mLive & LIVE_CPU_REG_Z); + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + return true; + } + if (mIns[i + 1].mType == ASMIT_CMP && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 0 && mIns[i].ChangesAccuAndFlag() && !(mIns[i + 1].mLive & LIVE_CPU_REG_C)) + { + mIns[i + 0].mLive |= (mIns[i + 1].mLive & LIVE_CPU_REG_Z); + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + return true; + } + if (mIns[i + 1].mType == ASMIT_CMP && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 0 && mIns[i].ChangesAccuAndFlag()) + { + mIns[i + 0].mLive |= (mIns[i + 1].mLive & LIVE_CPU_REG_Z); + mIns[i + 1].mType = ASMIT_SEC; mIns[i + 1].mMode = ASMIM_IMPLIED; + return true; + } + if (mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_IMMEDIATE && mIns[i + 0].mAddress == 0 && + mIns[i + 1].mType == ASMIT_CMP && !(mIns[i + 1].mLive & (LIVE_CPU_REG_C | LIVE_CPU_REG_A))) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_LDA; + return true; + } + if (mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mType == ASMIT_ASL && mIns[i + 1].mMode == ASMIM_IMPLIED) + { + int aval = mIns[i + 0].mAddress << 1; + mIns[i + 0].mAddress = aval & 0xff; + if (aval & 0x100) + mIns[i + 1].mType = ASMIT_SEC; + else + mIns[i + 1].mType = ASMIT_CLC; + mIns[i + 1].mMode = ASMIM_IMPLIED; + return true; + } + if (pass >= 3 && + mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode != ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_CMP && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && !(mIns[i + 1].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C))) + { + NativeCodeInstruction ins(mIns[i + 1]); + mIns[i + 1].CopyMode(mIns[i + 0]); + mIns[i + 0].CopyMode(ins); + if (mIns[i + 1].RequiresXReg()) + mIns[i + 0].mLive |= LIVE_CPU_REG_X; + if (mIns[i + 1].RequiresYReg()) + mIns[i + 0].mLive |= LIVE_CPU_REG_Y; + mIns[i + 0].mLive |= mIns[i + 1].mLive & LIVE_MEM; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_STA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && mIns[i].mAddress == mIns[i + 1].mAddress && + (mIns[i + 1].mType == ASMIT_LSR || mIns[i + 1].mType == ASMIT_ASL || mIns[i + 1].mType == ASMIT_ROL || mIns[i + 1].mType == ASMIT_ROR) && !(mIns[i + 0].mLive & LIVE_CPU_REG_A)) + { + mIns[i + 0].mType = mIns[i + 1].mType; + mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 0].mLive |= LIVE_CPU_REG_A | LIVE_CPU_REG_C; + mIns[i + 1].mType = ASMIT_STA; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_ASL && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_LDY && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && mIns[i + 0].mAddress == mIns[i + 1].mAddress && !(mIns[i + 1].mLive & (LIVE_MEM | LIVE_CPU_REG_A))) + { + mIns[i + 0].mType = ASMIT_LDA; + mIns[i + 0].mLive |= LIVE_CPU_REG_A; + mIns[i + 1].mType = ASMIT_ASL; + mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns.Insert(i + 2, NativeCodeInstruction(mIns[i + 0].mIns, ASMIT_TAY, ASMIM_IMPLIED)); + mIns[i + 2].mLive = mIns[i + 1].mLive; + mIns[i + 1].mLive |= LIVE_CPU_REG_A; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_STA && + mIns[i + 1].mType == ASMIT_LDY && mIns[i + 0].SameEffectiveAddress(mIns[i + 1]) && !(mIns[i + 0].mFlags & mIns[i + 1].mFlags & NCIF_VOLATILE)) + { + mIns[i + 1].mType = ASMIT_TAY; + mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 0].mLive |= LIVE_CPU_REG_A; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_STA && + mIns[i + 1].mType == ASMIT_LDX && mIns[i + 0].SameEffectiveAddress(mIns[i + 1]) && !(mIns[i + 0].mFlags & mIns[i + 1].mFlags & NCIF_VOLATILE)) + { + mIns[i + 1].mType = ASMIT_TAX; + mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 0].mLive |= LIVE_CPU_REG_A; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_TXA && + mIns[i + 1].mType == ASMIT_STA && (mIns[i + 1].mMode == ASMIM_ZERO_PAGE || mIns[i + 1].mMode == ASMIM_ABSOLUTE)) + { + mIns[i + 0].mLive |= LIVE_CPU_REG_X; + mIns[i + 1].mType = ASMIT_STX; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_TYA && + mIns[i + 1].mType == ASMIT_STA && (mIns[i + 1].mMode == ASMIM_ZERO_PAGE || mIns[i + 1].mMode == ASMIM_ABSOLUTE)) + { + mIns[i + 0].mLive |= LIVE_CPU_REG_Y; + mIns[i + 1].mType = ASMIT_STY; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_TAX && + mIns[i + 1].mType == ASMIT_STX && (mIns[i + 1].mMode == ASMIM_ZERO_PAGE || mIns[i + 1].mMode == ASMIM_ABSOLUTE)) + { + mIns[i + 0].mLive |= LIVE_CPU_REG_A; + mIns[i + 1].mType = ASMIT_STA; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_TAY && + mIns[i + 1].mType == ASMIT_STY && (mIns[i + 1].mMode == ASMIM_ZERO_PAGE || mIns[i + 1].mMode == ASMIM_ABSOLUTE)) + { + mIns[i + 0].mLive |= LIVE_CPU_REG_A; + mIns[i + 1].mType = ASMIT_STA; + return true; + } + if (mIns[i + 0].mType == ASMIT_TXA && mIns[i + 1].mType == ASMIT_STX) + { + NativeCodeInstruction ins(mIns[i + 0]); + mIns[i + 0] = mIns[i + 1]; mIns[i + 0].mLive |= LIVE_CPU_REG_X; + mIns[i + 1] = ins; + return true; + } + if (mIns[i + 0].mType == ASMIT_TYA && mIns[i + 1].mType == ASMIT_STY) + { + NativeCodeInstruction ins(mIns[i + 0]); + mIns[i + 0] = mIns[i + 1]; mIns[i + 0].mLive |= LIVE_CPU_REG_Y; + mIns[i + 1] = ins; + return true; + } + if (mIns[i + 0].mType == ASMIT_TAX && mIns[i + 1].mType == ASMIT_STA && !mIns[i + 1].RequiresXReg()) + { + NativeCodeInstruction ins(mIns[i + 0]); + mIns[i + 0] = mIns[i + 1]; mIns[i + 0].mLive |= LIVE_CPU_REG_A; + mIns[i + 1] = ins; + return true; + } + if (mIns[i + 0].mType == ASMIT_TAY && mIns[i + 1].mType == ASMIT_STA && !mIns[i + 1].RequiresYReg()) + { + NativeCodeInstruction ins(mIns[i + 0]); + mIns[i + 0] = mIns[i + 1]; mIns[i + 0].mLive |= LIVE_CPU_REG_A; + mIns[i + 1] = ins; + return true; + } + else if (mIns[i + 0].mType == ASMIT_TAX && mIns[i + 1].mType == ASMIT_CPX && !(mIns[i + 1].mLive & LIVE_CPU_REG_X)) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_CMP; + return true; + } + if (mIns[i + 0].mType == ASMIT_TAY && mIns[i + 1].mType == ASMIT_CPY && !(mIns[i + 1].mLive & LIVE_CPU_REG_Y)) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_CMP; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_LSR && mIns[i + 0].mMode == ASMIM_IMPLIED && + mIns[i + 1].mType == ASMIT_ASL && mIns[i + 1].mMode == ASMIM_IMPLIED && !(mIns[i + 1].mLive & LIVE_CPU_REG_C)) + { + mIns[i + 0].mType = ASMIT_AND; + mIns[i + 0].mMode = ASMIM_IMMEDIATE; + mIns[i + 0].mAddress = 0xfe; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_ROL && mIns[i + 0].mMode == ASMIM_IMPLIED && + mIns[i + 1].mType == ASMIT_LSR && mIns[i + 1].mMode == ASMIM_IMPLIED && !(mIns[i + 1].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z))) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_ROR && mIns[i + 0].mMode == ASMIM_IMPLIED && + mIns[i + 1].mType == ASMIT_ASL && mIns[i + 1].mMode == ASMIM_IMPLIED && !(mIns[i + 1].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z))) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_ROR && mIns[i + 0].mMode == ASMIM_IMPLIED && + mIns[i + 1].mType == ASMIT_ASL && mIns[i + 1].mMode == ASMIM_IMPLIED) + { + mIns[i + 0].mType = ASMIT_AND; + mIns[i + 0].mMode = ASMIM_IMMEDIATE; + mIns[i + 0].mAddress = 0xfe; + mIns[i + 0].mLive |= mIns[i + 1].mLive & LIVE_CPU_REG_C; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + return true; + } + if (mIns[i].mType == ASMIT_LSR && mIns[i].mMode == ASMIM_IMPLIED && mIns[i + 1].mType == ASMIT_AND && mIns[i + 1].mMode == ASMIM_IMMEDIATE && !(mIns[i + 1].mLive & LIVE_CPU_REG_C)) + { + NativeCodeInstruction ins = mIns[i]; + mIns[i] = mIns[i + 1]; + mIns[i + 1] = ins; + + mIns[i].mAddress = (mIns[i].mAddress << 1) & 255; + mIns[i].mLive |= LIVE_CPU_REG_A; + mIns[i + 1].mLive |= mIns[i].mLive & LIVE_CPU_REG_Z; + return true; + } + + if (mIns[i].mType == ASMIT_ASL && mIns[i].mMode == ASMIM_IMPLIED && mIns[i + 1].mType == ASMIT_AND && mIns[i + 1].mMode == ASMIM_IMMEDIATE && !(mIns[i + 1].mLive & LIVE_CPU_REG_C)) + { + NativeCodeInstruction ins = mIns[i]; + mIns[i] = mIns[i + 1]; + mIns[i + 1] = ins; + + mIns[i].mAddress = (mIns[i].mAddress >> 1) & 255; + mIns[i].mLive |= LIVE_CPU_REG_A; + mIns[i + 1].mLive |= mIns[i].mLive & LIVE_CPU_REG_Z; + return true; + } + + if ( + (mIns[i + 0].mType == ASMIT_INX || mIns[i + 0].mType == ASMIT_DEX) && + mIns[i + 1].mType == ASMIT_TXA && !(mIns[i + 1].mLive & LIVE_CPU_REG_A)) + { + mIns[i + 0].mLive |= LIVE_CPU_REG_Z; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + return true; + } + if ( + (mIns[i + 0].mType == ASMIT_INY || mIns[i + 0].mType == ASMIT_DEY) && + mIns[i + 1].mType == ASMIT_TYA && !(mIns[i + 1].mLive & LIVE_CPU_REG_A)) + { + mIns[i + 0].mLive |= LIVE_CPU_REG_Z; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_TXA && + mIns[i + 1].mType == ASMIT_TAX) + { + mIns[i + 0].mLive |= mIns[i + 1].mLive; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_TYA && + mIns[i + 1].mType == ASMIT_TAY) + { + mIns[i + 0].mLive |= mIns[i + 1].mLive; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_TAX && + mIns[i + 1].mType == ASMIT_TXA) + { + mIns[i + 0].mLive |= mIns[i + 1].mLive; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_TAY && + mIns[i + 1].mType == ASMIT_TYA) + { + mIns[i + 0].mLive |= mIns[i + 1].mLive; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_INY && + mIns[i + 1].mType == ASMIT_TYA && !(mIns[i + 1].mLive & LIVE_CPU_REG_A)) + { + mIns[i + 0].mLive |= mIns[i + 1].mLive; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_INX && + mIns[i + 1].mType == ASMIT_TXA && !(mIns[i + 1].mLive & LIVE_CPU_REG_A)) + { + mIns[i + 0].mLive |= mIns[i + 1].mLive; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_INX && + mIns[i + 1].mType == ASMIT_DEX && !(mIns[i + 1].mLive & LIVE_CPU_REG_Z)) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_DEX && + mIns[i + 1].mType == ASMIT_INX && !(mIns[i + 1].mLive & LIVE_CPU_REG_Z)) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_INY && + mIns[i + 1].mType == ASMIT_DEY && !(mIns[i + 1].mLive & LIVE_CPU_REG_Z)) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_DEY && + mIns[i + 1].mType == ASMIT_INY && !(mIns[i + 1].mLive & LIVE_CPU_REG_Z)) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_LDX && mIns[i + 0].mMode == ASMIM_IMMEDIATE && + mIns[i + 1].mType == ASMIT_DEX) + { + mIns[i + 0].mAddress = (mIns[i + 0].mAddress - 1) & 255; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 0].mLive |= mIns[i + 1].mLive; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_LDX && mIns[i + 0].mMode == ASMIM_IMMEDIATE && + mIns[i + 1].mType == ASMIT_INX) + { + mIns[i + 0].mAddress = (mIns[i + 0].mAddress + 1) & 255; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 0].mLive |= mIns[i + 1].mLive; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_LDY && mIns[i + 0].mMode == ASMIM_IMMEDIATE && + mIns[i + 1].mType == ASMIT_DEY) + { + mIns[i + 0].mAddress = (mIns[i + 0].mAddress - 1) & 255; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 0].mLive |= mIns[i + 1].mLive; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_LDY && mIns[i + 0].mMode == ASMIM_IMMEDIATE && + mIns[i + 1].mType == ASMIT_INY) + { + mIns[i + 0].mAddress = (mIns[i + 0].mAddress + 1) & 255; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 0].mLive |= mIns[i + 1].mLive; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_LDA && (mIns[i + 0].mMode == ASMIM_IMMEDIATE || mIns[i + 0].mMode == ASMIM_ZERO_PAGE || mIns[i + 0].mMode == ASMIM_ABSOLUTE || mIns[i + 0].mMode == ASMIM_ABSOLUTE_X || mIns[i + 0].mMode == ASMIM_IMMEDIATE_ADDRESS) && + mIns[i + 1].mType == ASMIT_TAY && !(mIns[i + 1].mLive & LIVE_CPU_REG_A)) + { + mIns[i + 0].mType = ASMIT_LDY; mIns[i + 0].mLive |= mIns[i + 1].mLive; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 0].mLive |= mIns[i + 1].mLive; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_LDA && (mIns[i + 0].mMode == ASMIM_IMMEDIATE || mIns[i + 0].mMode == ASMIM_ZERO_PAGE || mIns[i + 0].mMode == ASMIM_ABSOLUTE || mIns[i + 0].mMode == ASMIM_ABSOLUTE_Y || mIns[i + 0].mMode == ASMIM_IMMEDIATE_ADDRESS) && + mIns[i + 1].mType == ASMIT_TAX && !(mIns[i + 1].mLive & LIVE_CPU_REG_A)) + { + mIns[i + 0].mType = ASMIT_LDX; mIns[i + 0].mLive |= mIns[i + 1].mLive; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 0].mLive |= mIns[i + 1].mLive; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_LDY && (mIns[i + 0].mMode == ASMIM_IMMEDIATE || mIns[i + 0].mMode == ASMIM_ZERO_PAGE || mIns[i + 0].mMode == ASMIM_ABSOLUTE || mIns[i + 0].mMode == ASMIM_ABSOLUTE_X) && + mIns[i + 1].mType == ASMIT_TYA && !(mIns[i + 1].mLive & LIVE_CPU_REG_Y)) + { + mIns[i + 0].mType = ASMIT_LDA; mIns[i + 0].mLive |= mIns[i + 1].mLive; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 0].mLive |= mIns[i + 1].mLive; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_LDX && (mIns[i + 0].mMode == ASMIM_IMMEDIATE || mIns[i + 0].mMode == ASMIM_ZERO_PAGE || mIns[i + 0].mMode == ASMIM_ABSOLUTE || mIns[i + 0].mMode == ASMIM_ABSOLUTE_Y) && + mIns[i + 1].mType == ASMIT_TXA && !(mIns[i + 1].mLive & LIVE_CPU_REG_X)) + { + mIns[i + 0].mType = ASMIT_LDA; mIns[i + 0].mLive |= mIns[i + 1].mLive; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 0].mLive |= mIns[i + 1].mLive; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_LDX && (mIns[i + 0].mMode == ASMIM_IMMEDIATE || mIns[i + 0].mMode == ASMIM_ZERO_PAGE || mIns[i + 0].mMode == ASMIM_ABSOLUTE || mIns[i + 0].mMode == ASMIM_ABSOLUTE_Y) && + mIns[i + 1].mType == ASMIT_TXA && !(mIns[i + 1].mLive & LIVE_CPU_REG_A)) + { + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 0].mLive |= mIns[i + 1].mLive; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_TXA && + mIns[i + 1].mType == ASMIT_CMP && (mIns[i + 1].mMode == ASMIM_IMMEDIATE || mIns[i + 1].mMode == ASMIM_IMMEDIATE_ADDRESS || mIns[i + 1].mMode == ASMIM_ZERO_PAGE || mIns[i + 1].mMode == ASMIM_ABSOLUTE)) + { + mIns[i + 1].mType = ASMIT_CPX; + mIns[i + 0].mLive |= LIVE_CPU_REG_X; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_TYA && + mIns[i + 1].mType == ASMIT_CMP && (mIns[i + 1].mMode == ASMIM_IMMEDIATE || mIns[i + 1].mMode == ASMIM_IMMEDIATE_ADDRESS || mIns[i + 1].mMode == ASMIM_ZERO_PAGE || mIns[i + 1].mMode == ASMIM_ABSOLUTE)) + { + mIns[i + 1].mType = ASMIT_CPY; + mIns[i + 0].mLive |= LIVE_CPU_REG_Y; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_LDX && + mIns[i + 1].mType == ASMIT_STX && !(mIns[i + 1].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_X))) + { + mIns[i + 0].mType = ASMIT_LDA; mIns[i + 0].mLive |= LIVE_CPU_REG_A; + mIns[i + 1].mType = ASMIT_STA; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_LDY && + mIns[i + 1].mType == ASMIT_STY && !(mIns[i + 1].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Y))) + { + mIns[i + 0].mType = ASMIT_LDA; mIns[i + 0].mLive |= LIVE_CPU_REG_A; + mIns[i + 1].mType = ASMIT_STA; + return true; + } + if ( + (mIns[i + 0].mType == ASMIT_LDX || mIns[i + 0].mType == ASMIT_LDY) && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_STA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && !mIns[i + 1].SameEffectiveAddress(mIns[i + 0])) + { + NativeCodeInstruction ins = mIns[i + 0]; + mIns[i + 0] = mIns[i + 1]; mIns[i + 0].mLive |= ins.mLive; + mIns[i + 1] = ins; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_INY && + mIns[i + 1].mMode == ASMIM_ABSOLUTE_Y && !(mIns[i + 1].mLive & LIVE_CPU_REG_Y)) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mAddress++; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_INX && + mIns[i + 1].mMode == ASMIM_ABSOLUTE_X && !(mIns[i + 1].mLive & LIVE_CPU_REG_X)) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mAddress++; + return true; + } +#if 1 + if ( + mIns[i + 0].mType == ASMIT_AND && mIns[i + 0].mMode == ASMIM_IMMEDIATE && mIns[i + 0].mAddress == 1 && + mIns[i + 1].mType == ASMIT_CMP && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 1 && !(mIns[i + 1].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z))) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_LSR; mIns[i + 1].mMode = ASMIM_IMPLIED; + return true; + } +#endif + if ( + mIns[i + 0].mType == ASMIT_ROL && mIns[i + 0].mMode == ASMIM_IMPLIED && + mIns[i + 1].mType == ASMIT_AND && mIns[i + 1].mMode == ASMIM_IMMEDIATE && !(mIns[i + 1].mAddress & 0x01) && !(mIns[i + 1].mLive & LIVE_CPU_REG_C)) + { + mIns[i + 0].mType = ASMIT_ASL; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_ROR && mIns[i + 0].mMode == ASMIM_IMPLIED && + mIns[i + 1].mType == ASMIT_AND && mIns[i + 1].mMode == ASMIM_IMMEDIATE && !(mIns[i + 1].mAddress & 0x80) && !(mIns[i + 1].mLive & LIVE_CPU_REG_C)) + { + mIns[i + 0].mType = ASMIT_LSR; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_ROL && mIns[i + 0].mMode == ASMIM_IMPLIED && + mIns[i + 1].mType == ASMIT_AND && mIns[i + 1].mMode == ASMIM_IMMEDIATE && (mIns[i + 1].mAddress & 0x01) && !(mIns[i + 1].mLive & LIVE_CPU_REG_C)) + { + mIns[i + 0] = mIns[i + 1]; + mIns[i + 0].mLive |= LIVE_CPU_REG_C | LIVE_CPU_REG_A; + mIns[i + 0].mAddress = (mIns[i + 0].mAddress >> 1) & 0x7f; + mIns[i + 1].mType = ASMIT_ROL; mIns[i + 1].mMode = ASMIM_IMPLIED; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_ROR && mIns[i + 0].mMode == ASMIM_IMPLIED && + mIns[i + 1].mType == ASMIT_AND && mIns[i + 1].mMode == ASMIM_IMMEDIATE && (mIns[i + 1].mAddress & 0x80) && !(mIns[i + 1].mLive & LIVE_CPU_REG_C)) + { + mIns[i + 0] = mIns[i + 1]; + mIns[i + 0].mLive |= LIVE_CPU_REG_C | LIVE_CPU_REG_A; + mIns[i + 0].mAddress = (mIns[i + 0].mAddress << 1) & 0xfe; + mIns[i + 1].mType = ASMIT_ROR; mIns[i + 1].mMode = ASMIM_IMPLIED; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_AND && mIns[i + 0].mMode == ASMIM_IMMEDIATE && mIns[i + 0].mAddress == 0x7f && + mIns[i + 1].mType == ASMIT_ASL && mIns[i + 1].mMode == ASMIM_IMPLIED && !(mIns[i + 1].mLive & LIVE_CPU_REG_C)) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_AND && mIns[i + 0].mMode == ASMIM_IMMEDIATE && mIns[i + 0].mAddress == 0xfe && + mIns[i + 1].mType == ASMIT_LSR && mIns[i + 1].mMode == ASMIM_IMPLIED && !(mIns[i + 1].mLive & LIVE_CPU_REG_C)) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && !(mIns[i + 0].mLive & LIVE_MEM) && + mIns[i + 1].IsShift() && mIns[i + 1].mMode == ASMIM_IMPLIED && !(mIns[i + 1].mLive & LIVE_CPU_REG_A)) + { + mIns[i + 0].mType = mIns[i + 1].mType; mIns[i + 0].mLive |= LIVE_CPU_REG_C | LIVE_CPU_REG_Z; + mIns[i + 0].mLive &= ~LIVE_CPU_REG_A; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + return true; + } + if ( + mIns[i + 0].IsShift() && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && mIns[i + 1].mAddress == mIns[i + 0].mAddress && !(mIns[i + 1].mLive & LIVE_MEM)) + { + mIns[i + 1].mType = mIns[i + 0].mType; + mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 1].mLive |= LIVE_CPU_REG_A; + mIns[i + 0].mLive |= LIVE_CPU_REG_A | LIVE_CPU_REG_C; + mIns[i + 0].mType = ASMIT_LDA; + return true; + } + if ( + (mIns[i + 0].mType == ASMIT_INC || mIns[i + 0].mType == ASMIT_DEC) && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].SameEffectiveAddress(mIns[i + 0]) && !(mIns[i + 1].mLive & LIVE_CPU_REG_A)) + { + mIns[i + 0].mLive |= (mIns[i + 1].mLive & LIVE_CPU_REG_Z); + mIns[i + 1].mType = ASMIT_NOP; + mIns[i + 1].mMode = ASMIM_IMPLIED; + return true; + } + if (mIns[i + 0].mType == ASMIT_LDX && mIns[i + 0].mMode == ASMIM_IMMEDIATE_ADDRESS && + mIns[i + 1].mType == ASMIT_INX) + { + if (mIns[i + 0].mFlags & NCIF_UPPER) + mIns[i + 0].mAddress += 0x100; + else + mIns[i + 0].mAddress++; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_STX && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].IsShift() && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && mIns[i + 1].mAddress == mIns[i + 0].mAddress && !(mIns[i + 1].mLive & (LIVE_CPU_REG_A | LIVE_MEM))) + { + mIns[i + 0].mType = ASMIT_TXA; mIns[i + 0].mMode = ASMIM_IMPLIED; mIns[i + 0].mLive |= LIVE_CPU_REG_A; + mIns[i + 1].mMode = ASMIM_IMPLIED; + return true; + } + if (mIns[i + 0].ChangesAccuAndFlag() && mIns[i + 1].mType == ASMIT_TAX && !(mIns[i + 1].mLive & LIVE_CPU_REG_X)) + { + mIns[i + 0].mLive |= (mIns[i + 1].mLive & LIVE_CPU_REG_Z); + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + return true; + } + if (mIns[i + 0].ChangesAccuAndFlag() && mIns[i + 1].mType == ASMIT_TAY && !(mIns[i + 1].mLive & LIVE_CPU_REG_Y)) + { + mIns[i + 0].mLive |= (mIns[i + 1].mLive & LIVE_CPU_REG_Z); + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + return true; + } + if (mIns[i + 0].mType == ASMIT_LDX && mIns[i + 1].mType == ASMIT_CPX && !(mIns[i + 1].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_X))) + { + mIns[i + 0].mType = ASMIT_LDA; mIns[i + 0].mLive |= LIVE_CPU_REG_A; + mIns[i + 1].mType = ASMIT_CMP; + return true; + } + if (mIns[i + 0].mType == ASMIT_LDY && mIns[i + 1].mType == ASMIT_CPY && !(mIns[i + 1].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Y))) + { + mIns[i + 0].mType = ASMIT_LDA; mIns[i + 0].mLive |= LIVE_CPU_REG_A; + mIns[i + 1].mType = ASMIT_CMP; + return true; + } + if (mIns[i + 0].mType == ASMIT_LDX && mIns[i + 1].mType == ASMIT_STX && !(mIns[i + 1].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_X))) + { + mIns[i + 0].mType = ASMIT_LDA; mIns[i + 0].mLive |= LIVE_CPU_REG_A; + mIns[i + 1].mType = ASMIT_STA; + return true; + } + if (mIns[i + 0].mType == ASMIT_LDY && mIns[i + 1].mType == ASMIT_STY && !(mIns[i + 1].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Y))) + { + mIns[i + 0].mType = ASMIT_LDA; mIns[i + 0].mLive |= LIVE_CPU_REG_A; + mIns[i + 1].mType = ASMIT_STA; + return true; + } + if (mIns[i + 0].mType == ASMIT_STX && mIns[i + 1].mType == ASMIT_INC && mIns[i + 0].SameEffectiveAddress(mIns[i + 1]) && !(mIns[i + 1].mLive & LIVE_CPU_REG_X) && !(mIns[i + 0].mFlags & NCIF_VOLATILE)) + { + mIns[i + 0].mType = ASMIT_INX; mIns[i + 0].mMode = ASMIM_IMPLIED; mIns[i + 0].mLive |= LIVE_CPU_REG_X; + mIns[i + 1].mType = ASMIT_STX; + return true; + } + if (mIns[i + 0].mType == ASMIT_STX && mIns[i + 1].mType == ASMIT_DEC && mIns[i + 0].SameEffectiveAddress(mIns[i + 1]) && !(mIns[i + 1].mLive & LIVE_CPU_REG_X) && !(mIns[i + 0].mFlags & NCIF_VOLATILE)) + { + mIns[i + 0].mType = ASMIT_DEX; mIns[i + 0].mMode = ASMIM_IMPLIED; mIns[i + 0].mLive |= LIVE_CPU_REG_X; + mIns[i + 1].mType = ASMIT_STX; + return true; + } + if (mIns[i + 0].mType == ASMIT_STY && mIns[i + 1].mType == ASMIT_INC && mIns[i + 0].SameEffectiveAddress(mIns[i + 1]) && !(mIns[i + 1].mLive & LIVE_CPU_REG_Y) && !(mIns[i + 0].mFlags & NCIF_VOLATILE)) + { + mIns[i + 0].mType = ASMIT_INY; mIns[i + 0].mMode = ASMIM_IMPLIED; mIns[i + 0].mLive |= LIVE_CPU_REG_Y; + mIns[i + 1].mType = ASMIT_STY; + return true; + } + if (mIns[i + 0].mType == ASMIT_STY && mIns[i + 1].mType == ASMIT_DEC && mIns[i + 0].SameEffectiveAddress(mIns[i + 1]) && !(mIns[i + 1].mLive & LIVE_CPU_REG_Y) && !(mIns[i + 0].mFlags & NCIF_VOLATILE)) + { + mIns[i + 0].mType = ASMIT_DEY; mIns[i + 0].mMode = ASMIM_IMPLIED; mIns[i + 0].mLive |= LIVE_CPU_REG_Y; + mIns[i + 1].mType = ASMIT_STY; + return true; + } + if (pass > 10 && + mIns[i + 0].mType == ASMIT_LDA && HasAsmInstructionMode(ASMIT_LDX, mIns[i + 0].mMode) && + mIns[i + 1].mType == ASMIT_CMP && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 0xff && !(mIns[i + 1].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_X | LIVE_CPU_REG_C))) + { + mIns[i + 0].mType = ASMIT_LDX; mIns[i + 0].mLive |= LIVE_CPU_REG_X; + mIns[i + 1].mType = ASMIT_INX; mIns[i + 1].mMode = ASMIM_IMPLIED; + return true; + } + if (pass > 10 && + mIns[i + 0].mType == ASMIT_LDA && HasAsmInstructionMode(ASMIT_LDX, mIns[i + 0].mMode) && + mIns[i + 1].mType == ASMIT_CMP && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 0x01 && !(mIns[i + 1].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_X | LIVE_CPU_REG_C))) + { + mIns[i + 0].mType = ASMIT_LDX; mIns[i + 0].mLive |= LIVE_CPU_REG_X; + mIns[i + 1].mType = ASMIT_DEX; mIns[i + 1].mMode = ASMIM_IMPLIED; + return true; + } + if (pass > 10 && + mIns[i + 0].mType == ASMIT_LDA && HasAsmInstructionMode(ASMIT_LDY, mIns[i + 0].mMode) && + mIns[i + 1].mType == ASMIT_CMP && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 0xff && !(mIns[i + 1].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Y | LIVE_CPU_REG_C))) + { + mIns[i + 0].mType = ASMIT_LDY; mIns[i + 0].mLive |= LIVE_CPU_REG_Y; + mIns[i + 1].mType = ASMIT_INY; mIns[i + 1].mMode = ASMIM_IMPLIED; + return true; + } + if (pass > 10 && + mIns[i + 0].mType == ASMIT_LDA && HasAsmInstructionMode(ASMIT_LDY, mIns[i + 0].mMode) && + mIns[i + 1].mType == ASMIT_CMP && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 0x01 && !(mIns[i + 1].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Y | LIVE_CPU_REG_C))) + { + mIns[i + 0].mType = ASMIT_LDY; mIns[i + 0].mLive |= LIVE_CPU_REG_Y; + mIns[i + 1].mType = ASMIT_DEY; mIns[i + 1].mMode = ASMIM_IMPLIED; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_INC && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && mIns[i + 0].mAddress == mIns[i + 1].mAddress && !(mIns[i + 1].mLive & LIVE_MEM)) + { + if (!(mIns[i + 0].mLive & LIVE_CPU_REG_X)) + { + mIns[i + 0].mType = ASMIT_LDX; mIns[i + 0].mLive |= LIVE_CPU_REG_X; + mIns.Insert(i + 1, NativeCodeInstruction(mIns[i + 1].mIns, ASMIT_INX)); + mIns[i + 2].mType = ASMIT_TXA; mIns[i + 2].mMode = ASMIM_IMPLIED; + return true; + } + else if (!(mIns[i + 0].mLive & LIVE_CPU_REG_Y)) + { + mIns[i + 0].mType = ASMIT_LDY; mIns[i + 0].mLive |= LIVE_CPU_REG_Y; + mIns.Insert(i + 1, NativeCodeInstruction(mIns[i + 1].mIns, ASMIT_INY)); + mIns[i + 2].mType = ASMIT_TYA; mIns[i + 2].mMode = ASMIM_IMPLIED; + return true; + } + else if (!(mIns[i + 0].mLive & LIVE_CPU_REG_C)) + { + mIns.Insert(i + 1, NativeCodeInstruction(mIns[i + 1].mIns, ASMIT_CLC)); + mIns[i + 2].mType = ASMIT_ADC; mIns[i + 2].mMode = ASMIM_IMMEDIATE; mIns[i + 2].mAddress = 1; + return true; + } + } + + if ( + mIns[i + 0].mType == ASMIT_INC && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_LDX && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && mIns[i + 0].mAddress == mIns[i + 1].mAddress && !(mIns[i + 1].mLive & LIVE_MEM)) + { + mIns[i + 0] = mIns[i + 1]; + mIns[i + 1].mType = ASMIT_INX; mIns[i + 1].mMode = ASMIM_IMPLIED; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_DEC && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_LDX && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && mIns[i + 0].mAddress == mIns[i + 1].mAddress && !(mIns[i + 1].mLive & LIVE_MEM)) + { + mIns[i + 0] = mIns[i + 1]; + mIns[i + 1].mType = ASMIT_DEX; mIns[i + 1].mMode = ASMIM_IMPLIED; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_INC && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_LDY && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && mIns[i + 0].mAddress == mIns[i + 1].mAddress && !(mIns[i + 1].mLive & LIVE_MEM)) + { + mIns[i + 0] = mIns[i + 1]; + mIns[i + 1].mType = ASMIT_INY; mIns[i + 1].mMode = ASMIM_IMPLIED; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_DEC && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_LDY && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && mIns[i + 0].mAddress == mIns[i + 1].mAddress && !(mIns[i + 1].mLive & LIVE_MEM)) + { + mIns[i + 0] = mIns[i + 1]; + mIns[i + 1].mType = ASMIT_DEY; mIns[i + 1].mMode = ASMIM_IMPLIED; + return true; + } + + if ((mIns[i + 0].mType == ASMIT_LDX || mIns[i + 0].mType == ASMIT_TAX || mIns[i + 0].mType == ASMIT_INX || mIns[i + 0].mType == ASMIT_DEX) && + mIns[i + 1].mType == ASMIT_CPX && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 0 && !(mIns[i + 1].mLive & LIVE_CPU_REG_C)) + { + mIns[i + 0].mLive |= LIVE_CPU_REG_Z; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + return true; + } + if ((mIns[i + 0].mType == ASMIT_LDY || mIns[i + 0].mType == ASMIT_TAY || mIns[i + 0].mType == ASMIT_INY || mIns[i + 0].mType == ASMIT_DEY) && + mIns[i + 1].mType == ASMIT_CPY && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 0 && !(mIns[i + 1].mLive & LIVE_CPU_REG_C)) + { + mIns[i + 0].mLive |= LIVE_CPU_REG_Z; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_LDY && mIns[i + 0].mMode == ASMIM_ABSOLUTE_X && + mIns[i + 1].mType == ASMIT_STA && mIns[i + 1].mMode == ASMIM_ABSOLUTE_X && + mIns[i + 0].mLinkerObject == mIns[i + 1].mLinkerObject && + mIns[i + 0].mAddress != mIns[i + 1].mAddress && + !(mIns[i + 0].mFlags & NCIF_VOLATILE) && + !(mIns[i + 1].mFlags & NCIF_VOLATILE)) + { + NativeCodeInstruction ins(mIns[i + 0]); + mIns[i + 0] = mIns[i + 1]; + mIns[i + 1] = ins; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_LDX && mIns[i + 0].mMode == ASMIM_ABSOLUTE_Y && + mIns[i + 1].mType == ASMIT_STA && mIns[i + 1].mMode == ASMIM_ABSOLUTE_Y && + mIns[i + 0].mLinkerObject == mIns[i + 1].mLinkerObject && + mIns[i + 0].mAddress != mIns[i + 1].mAddress && + !(mIns[i + 0].mFlags & NCIF_VOLATILE) && + !(mIns[i + 1].mFlags & NCIF_VOLATILE)) + { + NativeCodeInstruction ins(mIns[i + 0]); + mIns[i + 0] = mIns[i + 1]; + mIns[i + 1] = ins; + return true; + } + + // Ensure cannonical mode of immediate + val is val + immediate + if (mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_IMMEDIATE && mIns[i + 1].IsCommutative() && mIns[i + 1].mMode != ASMIM_IMMEDIATE) + { + int v = mIns[i + 0].mAddress; + mIns[i + 0].CopyMode(mIns[i + 1]); + mIns[i + 0].mMinVal = mIns[i + 1].mMinVal; + mIns[i + 0].mMaxVal = mIns[i + 1].mMaxVal; + mIns[i + 1].mMode = ASMIM_IMMEDIATE; mIns[i + 1].mAddress = v; + return true; + } + + +#if 0 + else if ( + mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_IMMEDIATE && + mIns[i + 1].IsCommutative() && mIns[i + 1].mMode == ASMIM_ZERO_PAGE) + { + int val = mIns[i + 0].mAddress; + mIns[i + 0].CopyMode(mIns[i + 1]); + mIns[i + 1].mMode = ASMIM_IMMEDIATE; + mIns[i + 1].mAddress = val; + progress = true; + } +#endif + if (mIns[i + 0].mType == ASMIT_LDY && mIns[i + 0].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mMode == ASMIM_INDIRECT_Y) + { + const NativeCodeInstruction* ains, * iins; + + int sreg = mIns[i + 1].mAddress; + int apos, addr; + uint32 flags; + + if (FindGlobalAddressSumY(i, sreg, true, apos, ains, iins, flags, addr)) + { + if (iins || (flags & LIVE_CPU_REG_Y) || (flags & LIVE_CPU_REG_X)) //!(mIns[i + 1].mLive & LIVE_CPU_REG_X)) + { + RepairLoadYImmediate(i + 1, mIns[i + 0].mAddress); + + if (flags & LIVE_CPU_REG_Y) + { + mIns[i + 1].mMode = ASMIM_ABSOLUTE_Y; + } + else if (flags & LIVE_CPU_REG_X) + { + mIns[i + 1].mMode = ASMIM_ABSOLUTE_X; + } + else + { + mIns[i + 1].mMode = ASMIM_ABSOLUTE_Y; + } + + if (ains->mMode == ASMIM_IMMEDIATE) + { + mIns[i + 1].mLinkerObject = 0; + mIns[i + 1].mAddress = addr + mIns[i + 0].mAddress; + } + else + { + mIns[i + 1].mLinkerObject = ains->mLinkerObject; + mIns[i + 1].mAddress = ains->mAddress + mIns[i + 0].mAddress; + } + + if (!iins) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + if (flags & LIVE_CPU_REG_Y) + { + mIns.Insert(apos, NativeCodeInstruction(mIns[i + 0].mIns, ASMIT_TAY, ASMIM_IMPLIED)); + for (int j = apos; j < i + 2; j++) + mIns[j].mLive |= LIVE_CPU_REG_Y; + } + else + { + PatchGlobalAdressSumYByX(i + 1, sreg, *ains, addr); + mIns.Insert(apos, NativeCodeInstruction(mIns[i + 0].mIns, ASMIT_TAX, ASMIM_IMPLIED)); + for (int j = apos; j < i + 2; j++) + mIns[j].mLive |= LIVE_CPU_REG_X; + } + } + else if (iins->mMode != ASMIM_ZERO_PAGE) + { + mIns[i + 0].mMode = iins->mMode; + mIns[i + 0].mAddress = iins->mAddress; + mIns[i + 0].mLinkerObject = iins->mLinkerObject; + mIns[i + 0].mFlags = iins->mFlags; + + if (!(flags & LIVE_CPU_REG_Y) && (flags & LIVE_CPU_REG_X)) + mIns[i + 0].mType = ASMIT_LDX; + } + else if (iins->mAddress == sreg) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + if (flags & LIVE_CPU_REG_Y) + { + mIns.Insert(apos, NativeCodeInstruction(mIns[i + 0].mIns, ASMIT_LDY, ASMIM_ZERO_PAGE, iins->mAddress)); + mIns[apos].mLive |= LIVE_MEM; + for (int j = apos; j < i + 2; j++) + mIns[j].mLive |= LIVE_CPU_REG_Y; + } + else + { + PatchGlobalAdressSumYByX(i + 1, sreg, *ains, addr); + mIns.Insert(apos, NativeCodeInstruction(mIns[i + 0].mIns, ASMIT_LDX, ASMIM_ZERO_PAGE, iins->mAddress)); + mIns[apos].mLive |= LIVE_MEM; + for (int j = apos; j < i + 2; j++) + mIns[j].mLive |= LIVE_CPU_REG_X; + } + } + else + { + mIns[i + 0].mMode = ASMIM_ZERO_PAGE; + mIns[i + 0].mAddress = iins->mAddress; + + if (!(flags & LIVE_CPU_REG_Y) && (flags & LIVE_CPU_REG_X)) + mIns[i + 0].mType = ASMIT_LDX; + } + + CheckLive(); + + return true; + } + } + } + + CheckLive(); + + if (mIns[i + 0].mType == ASMIT_LDY && mIns[i + 0].mMode == ASMIM_IMMEDIATE && + mIns[i + 1].mMode == ASMIM_INDIRECT_Y) + { + int addr; + if (FindPageStartAddress(i, mIns[i + 1].mAddress, addr)) + { + if (mIns[i + 1].mLive & LIVE_CPU_REG_Y) + { + mIns.Insert(i + 2, mIns[i + 0]); + mIns[i + 2].mLive |= mIns[i + 1].mLive; + } + + int absaddr = addr + mIns[i + 0].mAddress; + + mIns[i + 0].mMode = ASMIM_ZERO_PAGE; + mIns[i + 0].mLive |= LIVE_MEM; + mIns[i + 0].mAddress = mIns[i + 1].mAddress; + + mIns[i + 1].mMode = ASMIM_ABSOLUTE_Y; + mIns[i + 1].mAddress = absaddr; + mIns[i + 1].mLinkerObject = nullptr; + + CheckLive(); + + return true; + } + } + + if ( + mIns[i + 0].mType == ASMIT_LDY && mIns[i + 0].mMode == ASMIM_IMMEDIATE && mIns[i + 0].mAddress == 0 && + mIns[i + 1].mMode == ASMIM_INDIRECT_Y) + { + int sreg = mIns[i + 1].mAddress; + + int apos, breg, ireg; + + if (FindAddressSumY(i, sreg, apos, breg, ireg)) + { + if (PatchAddressSumY(i, sreg, apos, breg, ireg)) + { + CheckLive(); + + return true; + } + } + } + + if ( + mIns[i + 0].mMode == ASMIM_INDIRECT_Y && (mIns[i + 0].mFlags & NCIF_YZERO) && + mIns[i + 1].mMode == ASMIM_INDIRECT_Y && mIns[i + 0].mAddress == mIns[i + 1].mAddress) + { + int sreg = mIns[i + 0].mAddress; + + int apos, breg, ireg; + + if (FindAddressSumY(i, sreg, apos, breg, ireg)) + { + if (!(breg == sreg || ireg == sreg) || !(mIns[i + 1].mLive & LIVE_MEM)) + { + if (breg == sreg || ireg == sreg) + { + mIns[apos + 3].mType = ASMIT_NOP; mIns[apos + 3].mMode = ASMIM_IMPLIED; + mIns[apos + 6].mType = ASMIT_NOP; mIns[apos + 6].mMode = ASMIM_IMPLIED; + } + + RepairLoadYImmediate(i + 1, 0); + + mIns.Insert(i + 0, NativeCodeInstruction(mIns[i + 0].mIns, ASMIT_LDY, ASMIM_ZERO_PAGE, ireg)); + mIns[i + 0].mLive |= LIVE_CPU_REG_Y | LIVE_MEM; + mIns[i + 1].mAddress = breg; mIns[i + 1].mFlags &= ~NCIF_YZERO; + mIns[i + 2].mAddress = breg; mIns[i + 2].mFlags &= ~NCIF_YZERO; + + CheckLive(); + + return true; + } + } + } + + if ( + mIns[i + 0].mType == ASMIT_LDY && mIns[i + 0].mMode == ASMIM_IMMEDIATE && mIns[i + 0].mAddress <= 3 && + mIns[i + 1].mType == ASMIT_STA && mIns[i + 1].mMode == ASMIM_INDIRECT_Y) + { + int apos, breg, ireg; + if (FindAddressSumY(i, mIns[i + 1].mAddress, apos, breg, ireg)) + { + if (breg != mIns[i + 1].mAddress && ireg != mIns[i + 1].mAddress)// || !(mIns[i + 1].mLive & LIVE_MEM)) + { + int yoffset = mIns[i + 0].mAddress; + + if (breg == mIns[i + 1].mAddress) + { + mIns[apos + 3].mType = ASMIT_NOP; mIns[apos + 3].mMode = ASMIM_IMPLIED; + mIns[apos + 6].mType = ASMIT_NOP; mIns[apos + 6].mMode = ASMIM_IMPLIED; + } + + RepairLoadYImmediate(i + 1, 0); + + mIns[i + 0].mMode = ASMIM_ZERO_PAGE; + mIns[i + 0].mAddress = ireg; + mIns[i + 0].mLive |= LIVE_MEM; + mIns[i + 1].mAddress = breg; + mIns[i + 1].mFlags &= ~NCIF_YZERO; + + for (int j = 0; j < yoffset; j++) + { + mIns.Insert(i + 1, NativeCodeInstruction(mIns[i + 0].mIns, ASMIT_INY, ASMIM_IMPLIED)); + mIns[i + 1].mLive = mIns[i + 0].mLive; + } + + CheckLive(); + + return true; + } + } + } + + + return false; +} + +bool NativeCodeBasicBlock::PeepHoleOptimizerIterate3(int i, int pass) +{ + if (mIns[i].mType == ASMIT_LDA && mIns[i + 2].mType == ASMIT_LDA && (mIns[i + 1].mType == ASMIT_CLC || mIns[i + 1].mType == ASMIT_SEC)) + { + mIns[i].mType = ASMIT_NOP; mIns[i].mMode = ASMIM_IMPLIED; + return true; + } + + if (mIns[i + 0].mType == ASMIT_STA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 2].mType == ASMIT_LDA && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && mIns[i + 0].mAddress == mIns[i + 2].mAddress && + mIns[i + 1].mType == ASMIT_INC && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && mIns[i + 0].mAddress == mIns[i + 1].mAddress && (mIns[i + 2].mLive & LIVE_CPU_REG_C) == 0) + { + mIns[i + 0].mType = ASMIT_CLC; + mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 0].mLive |= LIVE_CPU_REG_C | LIVE_CPU_REG_A; + mIns[i + 1].mType = ASMIT_ADC; + mIns[i + 1].mMode = ASMIM_IMMEDIATE; + mIns[i + 1].mAddress = 1; + mIns[i + 1].mLive |= LIVE_CPU_REG_A; + mIns[i + 2].mType = ASMIT_STA; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_STA && + mIns[i + 1].IsShiftOrInc() && mIns[i + 1].mMode != ASMIM_IMPLIED && + mIns[i + 2].mType == ASMIT_LDA && mIns[i + 2].SameEffectiveAddress(mIns[i + 0]) && + !mIns[i + 2].SameEffectiveAddress(mIns[i + 1]) && + !(mIns[i + 2].mMode == ASMIM_INDIRECT_Y && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && (mIns[i + 1].mAddress == mIns[i + 2].mAddress || mIns[i + 1].mAddress == mIns[i + 2].mAddress + 1)) && + !(mIns[i + 2].mLive & LIVE_CPU_REG_Z)) + { + mIns[i + 0].mLive |= LIVE_CPU_REG_A; + mIns[i + 1].mLive |= LIVE_CPU_REG_A; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + return true; + } + + if (mIns[i + 0].mType == ASMIT_STA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode != ASMIM_ZERO_PAGE && + mIns[i + 2].mMode == ASMIM_ZERO_PAGE && mIns[i + 0].mAddress == mIns[i + 2].mAddress && + mIns[i + 2].IsCommutative() && HasAsmInstructionMode(mIns[i + 2].mType, mIns[i + 1].mMode) && + (mIns[i + 2].mLive & LIVE_MEM) == 0) + { + mIns[i + 1].mType = mIns[i + 2].mType; + mIns[i + 1].mLive |= mIns[i + 2].mLive; + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + mIns[i + 0].ChangesAccuAndFlag() && + mIns[i + 1].mType == ASMIT_STA && mIns[i + 2].mType == ASMIT_LDA && !(mIns[i + 2].mFlags & NCIF_VOLATILE) && + mIns[i + 1].SameEffectiveAddress(mIns[i + 2])) + { + mIns[i + 0].mLive |= mIns[i + 2].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z); + mIns[i + 1].mLive |= mIns[i + 2].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z); + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_LDA && + mIns[i + 1].mType == ASMIT_STA && + mIns[i + 2].mType == ASMIT_LDA && !(mIns[i + 2].mFlags & NCIF_VOLATILE) && + mIns[i + 0].SameEffectiveAddress(mIns[i + 2])) + { + mIns[i + 0].mLive |= mIns[i + 2].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z); + mIns[i + 1].mLive |= mIns[i + 2].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z); + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_STA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 2].mType == ASMIT_LDY && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && mIns[i + 2].mAddress == mIns[i + 0].mAddress && + !mIns[i + 1].SameEffectiveAddress(mIns[i + 0]) && !mIns[i + 1].ChangesAccu()) + { + mIns[i + 0].mLive |= LIVE_CPU_REG_A; + mIns[i + 1].mLive |= LIVE_CPU_REG_A; + + mIns[i + 2].mType = ASMIT_TAY; + mIns[i + 2].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 2].mType == ASMIT_TAY && !(mIns[i + 2].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z)) && + !mIns[i + 1].ChangesAccu() && !mIns[i + 1].RequiresAccu() && !mIns[i + 1].RequiresYReg()) + { + mIns[i + 0].mType = ASMIT_LDY; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + if (mIns[i + 1].ChangesYReg()) + { + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + } + mIns[i + 0].mLive |= LIVE_CPU_REG_Y; + mIns[i + 1].mLive |= LIVE_CPU_REG_Y; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_STA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 2].mType == ASMIT_LDA && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && mIns[i + 2].mAddress == mIns[i + 0].mAddress && + mIns[i + 1].mType == ASMIT_DEC && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && mIns[i + 1].mAddress == mIns[i + 0].mAddress && + !(mIns[i + 2].mLive & LIVE_CPU_REG_C)) + { + mIns[i + 0].mType = ASMIT_SEC; mIns[i + 0].mMode = ASMIM_IMPLIED; mIns[i + 0].mLive |= LIVE_CPU_REG_C | LIVE_CPU_REG_A; + mIns[i + 1].mType = ASMIT_SBC; mIns[i + 1].mMode = ASMIM_IMMEDIATE; mIns[i + 1].mAddress = 1; mIns[i + 1].mLive |= LIVE_CPU_REG_A; + if (mIns[i + 2].mLive & LIVE_CPU_REG_Z) + mIns[i + 1].mLive |= LIVE_CPU_REG_Z; + mIns[i + 2].mType = ASMIT_STA; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_IMMEDIATE && mIns[i + 0].mAddress == 0 && + mIns[i + 1].mType == ASMIT_CMP && mIns[i + 1].mMode == ASMIM_IMMEDIATE && + mIns[i + 2].mType == ASMIT_ROR && mIns[i + 2].mMode == ASMIM_IMPLIED) + { + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_CLC; mIns[i + 2].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + mIns[i + 0].ChangesAccuAndFlag() && + mIns[i + 1].mType == ASMIT_STA && + mIns[i + 2].mType == ASMIT_ORA && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 0) + { + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + mIns[i + 0].mLive |= mIns[i + 2].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z); + mIns[i + 1].mLive |= mIns[i + 2].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z); + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_TAX && + mIns[i + 1].mType == ASMIT_LDY && !mIns[i + 1].RequiresXReg() && + mIns[i + 2].mType == ASMIT_ORA && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 0) + { + mIns[i + 1].mLive |= LIVE_CPU_REG_A; + mIns[i + 2] = mIns[i + 0]; + mIns[i + 2].mLive |= LIVE_CPU_REG_Y | LIVE_CPU_REG_Z; + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_TAY && + mIns[i + 1].mType == ASMIT_LDX && !mIns[i + 1].RequiresYReg() && + mIns[i + 2].mType == ASMIT_ORA && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 0) + { + mIns[i + 1].mLive |= LIVE_CPU_REG_A; + mIns[i + 2] = mIns[i + 0]; + mIns[i + 2].mLive |= LIVE_CPU_REG_X | LIVE_CPU_REG_Z; + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + mIns[i + 0].ChangesAccuAndFlag() && (mIns[i + 0].mMode == ASMIM_IMMEDIATE || mIns[i + 0].mMode == ASMIM_ZERO_PAGE) && + mIns[i + 1].mType == ASMIT_LDY && + mIns[i + 2].mType == ASMIT_ORA && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 0) + { + mIns[i + 2] = mIns[i + 0]; + mIns[i + 2].mLive |= LIVE_CPU_REG_Y | LIVE_CPU_REG_Z; + if (mIns[i + 0].RequiresCarry()) + mIns[i + 1].mLive |= LIVE_CPU_REG_C; + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + mIns[i + 0].ChangesAccuAndFlag() && + mIns[i + 1].mType == ASMIT_STA && + mIns[i + 2].mType == ASMIT_CMP && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 0 && !(mIns[i + 2].mLive & LIVE_CPU_REG_C)) + { + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + mIns[i + 0].mLive |= mIns[i + 2].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z); + mIns[i + 1].mLive |= mIns[i + 2].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z); + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_LDA && (mIns[i + 0].mMode == ASMIM_ZERO_PAGE || mIns[i + 0].mMode == ASMIM_ABSOLUTE || mIns[i + 0].mMode == ASMIM_ABSOLUTE_X) && + mIns[i + 1].IsShift() && + mIns[i + 2].mType == ASMIT_STA && mIns[i + 2].SameEffectiveAddress(mIns[i + 0]) && !(mIns[i + 2].mLive & LIVE_CPU_REG_A)) + { + mIns[i + 2].mType = mIns[i + 1].mType; + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_STA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + !mIns[i + 1].UsesZeroPage(mIns[i + 0].mAddress) && !mIns[i + 1].UsesAccu() && + mIns[i + 2].IsShift() && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && mIns[i + 2].mAddress == mIns[i + 0].mAddress && !(mIns[i + 2].mLive & LIVE_CPU_REG_A)) + { + mIns[i + 0] = mIns[i + 1]; + mIns[i + 1] = mIns[i + 2]; + mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 0].mLive |= LIVE_CPU_REG_A; + mIns[i + 1].mLive |= LIVE_CPU_REG_A; + mIns[i + 2].mType = ASMIT_STA; + mIns[i + 2].mLive |= mIns[i + 1].mLive & LIVE_CPU_REG_C; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_STA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_IMMEDIATE && + mIns[i + 2].IsShift() && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && mIns[i + 2].mAddress == mIns[i + 0].mAddress) + { + mIns[i + 0] = mIns[i + 2]; + mIns[i + 2] = mIns[i + 1]; + mIns[i + 1] = mIns[i + 0]; + mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 0].mLive |= LIVE_CPU_REG_A; + mIns[i + 1].mType = ASMIT_STA; + mIns[i + 2].mLive |= mIns[i + 1].mLive & LIVE_CPU_REG_C; + return true; + } + + if ( + mIns[i + 0].mMode != ASMIM_RELATIVE && + mIns[i + 2].mType == ASMIT_LDA && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && !(mIns[i + 2].mLive & LIVE_CPU_REG_A) && + mIns[i + 1].mMode == ASMIM_ZERO_PAGE && mIns[i + 1].mAddress == mIns[i + 2].mAddress && + (mIns[i + 1].mType == ASMIT_DEC || mIns[i + 1].mType == ASMIT_INC)) + { + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + mIns[i + 1].mLive |= LIVE_CPU_REG_Z; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_TAX && + mIns[i + 1].mType == ASMIT_ASL && mIns[i + 1].mMode == ASMIM_IMPLIED && + mIns[i + 2].mType == ASMIT_TXA && !(mIns[i + 2].mLive & (LIVE_CPU_REG_Z | LIVE_CPU_REG_X))) + { + mIns[i + 0].mType = ASMIT_CMP; mIns[i + 0].mMode = ASMIM_IMMEDIATE; mIns[i + 0].mAddress = 0x80; + mIns[i + 0].mLive |= LIVE_CPU_REG_A | LIVE_CPU_REG_C; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_TAX && + mIns[i + 1].mType == ASMIT_INX && + mIns[i + 2].mType == ASMIT_TXA && !(mIns[i + 2].mLive & (LIVE_CPU_REG_X | LIVE_CPU_REG_C))) + { + mIns[i + 0].mType = ASMIT_CLC; + mIns[i + 0].mLive |= LIVE_CPU_REG_C | LIVE_CPU_REG_A; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_ADC; mIns[i + 2].mMode = ASMIM_IMMEDIATE; mIns[i + 2].mAddress = 1; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_TAX && + mIns[i + 1].mType == ASMIT_DEX && + mIns[i + 2].mType == ASMIT_TXA && !(mIns[i + 2].mLive & (LIVE_CPU_REG_X | LIVE_CPU_REG_C))) + { + mIns[i + 0].mType = ASMIT_SEC; + mIns[i + 0].mLive |= LIVE_CPU_REG_C | LIVE_CPU_REG_A; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_SBC; mIns[i + 2].mMode = ASMIM_IMMEDIATE; mIns[i + 2].mAddress = 1; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_TAX && + !mIns[i + 1].ChangesXReg() && !mIns[i + 1].ChangesAccu() && + mIns[i + 2].mType == ASMIT_TXA && !(mIns[i + 2].mLive & LIVE_CPU_REG_Z)) + { + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + mIns[i + 0].mLive |= LIVE_CPU_REG_A; + mIns[i + 1].mLive |= LIVE_CPU_REG_A; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_TAX && + !mIns[i + 1].ChangesXReg() && !mIns[i + 1].UsesAccu() && + mIns[i + 2].mType == ASMIT_STX && !(mIns[i + 2].mLive & LIVE_CPU_REG_X)) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_STA; + mIns[i + 1].mLive |= LIVE_CPU_REG_A; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_TAY && + !mIns[i + 1].ChangesYReg() && !mIns[i + 1].UsesAccu() && + mIns[i + 2].mType == ASMIT_STY && !(mIns[i + 2].mLive & LIVE_CPU_REG_Y)) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_STA; + mIns[i + 1].mLive |= LIVE_CPU_REG_A; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_SEC && + mIns[i + 1].mType == ASMIT_TXA && + mIns[i + 2].mType == ASMIT_SBC && mIns[i + 2].mAddress == 1 && !(mIns[i + 2].mLive & (LIVE_CPU_REG_C | LIVE_CPU_REG_X))) + { + mIns[i + 0].mType = ASMIT_DEX; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_SEC && + mIns[i + 1].mType == ASMIT_TYA && + mIns[i + 2].mType == ASMIT_SBC && mIns[i + 2].mAddress == 1 && !(mIns[i + 2].mLive & (LIVE_CPU_REG_C | LIVE_CPU_REG_Y))) + { + mIns[i + 0].mType = ASMIT_DEY; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_SEC && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_IMMEDIATE && + mIns[i + 2].mType == ASMIT_SBC && mIns[i + 2].mMode == ASMIM_IMMEDIATE) + { + int t = (mIns[i + 2].mAddress ^ 0xff) + mIns[i + 1].mAddress + 1; + + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_LDA; mIns[i + 2].mAddress = t & 0xff; + if (t < 256) + mIns[i + 0].mType = ASMIT_CLC; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_IMMEDIATE && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE) + { + int t = mIns[i + 2].mAddress + mIns[i + 1].mAddress; + + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_LDA; mIns[i + 2].mAddress = t & 0xff; + if (t >= 256) + mIns[i + 0].mType = ASMIT_SEC; + return true; + } + + if (pass > 0 && + mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 0 && + mIns[i + 2].mType == ASMIT_ADC) + { + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_LDA; + return true; + } + + if (pass > 0 && + mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_LDA && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 0) + { + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + mIns[i + 1].mLive |= (mIns[i + 2].mLive & LIVE_CPU_REG_Z); + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_LDA && (mIns[i + 0].mMode == ASMIM_IMMEDIATE || mIns[i + 0].mMode == ASMIM_IMMEDIATE_ADDRESS || mIns[i + 0].mMode == ASMIM_ZERO_PAGE) && + mIns[i + 1].mType == ASMIT_CLC && + mIns[i + 2].mType == ASMIT_ADC) + { + mIns[i + 1] = mIns[i + 0]; + mIns[i + 0].mType = ASMIT_CLC; + mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 0].mLive |= LIVE_CPU_REG_C; + mIns[i + 1].mLive |= LIVE_CPU_REG_C; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_IMMEDIATE_ADDRESS && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && !(mIns[i + 2].mLive & LIVE_CPU_REG_C)) + { + if (mIns[i + 1].mFlags & NCIF_UPPER) + mIns[i + 1].mAddress += 0x0100 * mIns[i + 2].mAddress; + else + mIns[i + 1].mAddress += mIns[i + 2].mAddress; + + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_IMMEDIATE && mIns[i + 0].mAddress == 0 && + mIns[i + 1].mType == ASMIT_ADC && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 0 && + mIns[i + 2].mType == ASMIT_LSR && mIns[i + 2].mMode == ASMIM_IMPLIED) + { + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_SEC && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 0xff && + mIns[i + 2].mType == ASMIT_SBC) + { + mIns[i + 2].mType = ASMIT_EOR; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_EOR && mIns[i + 0].mMode == ASMIM_IMMEDIATE && mIns[i + 0].mAddress == 0x80 && + mIns[i + 1].mType == ASMIT_SEC && + mIns[i + 2].mType == ASMIT_SBC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 0x80 && !(mIns[i + 2].mLive & (LIVE_CPU_REG_C | LIVE_CPU_REG_Z))) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_TYA && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress <= 2 && !(mIns[i + 2].mLive & (LIVE_CPU_REG_C | LIVE_CPU_REG_Y))) + { + int t = mIns[i + 2].mAddress; + mIns[i + 0].mType = ASMIT_INY; mIns[i + 0].mLive |= LIVE_CPU_REG_Y; + if (t > 1) + { + mIns[i + 1].mType = ASMIT_INY; + mIns[i + 1].mLive |= LIVE_CPU_REG_Y; + } + else + { + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + } + mIns[i + 2].mType = ASMIT_TYA; mIns[i + 2].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_TYA && + mIns[i + 1].mType == ASMIT_CLC && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress <= 2 && !(mIns[i + 2].mLive & (LIVE_CPU_REG_C | LIVE_CPU_REG_Y))) + { + int t = mIns[i + 2].mAddress; + mIns[i + 0].mType = ASMIT_INY; mIns[i + 0].mLive |= LIVE_CPU_REG_Y; + if (t > 1) + { + mIns[i + 1].mType = ASMIT_INY; + mIns[i + 1].mLive |= LIVE_CPU_REG_Y; + } + else + { + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + } + mIns[i + 2].mType = ASMIT_TYA; mIns[i + 2].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_ADC && mIns[i + 0].mMode == ASMIM_IMMEDIATE && + mIns[i + 1].mType == ASMIT_CLC && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && !(mIns[i + 2].mLive & LIVE_CPU_REG_C)) + { + mIns[i + 0].mAddress += mIns[i + 2].mAddress; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_SBC && mIns[i + 0].mMode == ASMIM_IMMEDIATE && + mIns[i + 1].mType == ASMIT_SEC && + mIns[i + 2].mType == ASMIT_SBC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && !(mIns[i + 2].mLive & LIVE_CPU_REG_C)) + { + mIns[i + 0].mAddress += mIns[i + 2].mAddress; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_ADC && mIns[i + 0].mMode == ASMIM_IMMEDIATE && mIns[i + 0].mAddress == 0 && + mIns[i + 1].mType == ASMIT_CLC && + mIns[i + 2].mType == ASMIT_ADC) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_EOR && mIns[i + 0].mMode == ASMIM_IMMEDIATE && mIns[i + 0].mAddress == 0x80 && + mIns[i + 1].mType == ASMIT_CLC && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && !(mIns[i + 2].mLive & LIVE_CPU_REG_C)) + { + mIns[i + 2].mAddress ^= 0x80; + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + pass >= 12 && + mIns[i + 0].IsShift() && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode != ASMIM_ABSOLUTE_X && + mIns[i + 2].mType == ASMIT_LDX && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && mIns[i + 2].mAddress == mIns[i + 0].mAddress && !(mIns[i + 2].mLive & LIVE_MEM)) + { + mIns.Insert(i, NativeCodeInstruction(mIns[i].mIns, ASMIT_LDA, mIns[i + 0])); + mIns[i + 1].mMode = ASMIM_IMPLIED; mIns[i + 1].mLive |= LIVE_CPU_REG_A; + mIns[i + 3] = mIns[i + 2]; mIns[i + 3].mLive |= LIVE_CPU_REG_X; + mIns[i + 2].mType = ASMIT_TAX; mIns[i + 2].mMode = ASMIM_IMPLIED; mIns[i + 2].mLive |= LIVE_CPU_REG_X; + return true; + } + + if ( + pass >= 6 && + mIns[i + 0].mType == ASMIT_ASL && mIns[i + 0].mMode == ASMIM_IMPLIED && + mIns[i + 1].mType == ASMIT_CLC && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 1) + { + mIns[i + 0].mType = ASMIT_SEC; mIns[i + 0].mLive |= LIVE_CPU_REG_C; + mIns[i + 1].mType = ASMIT_ROL; mIns[i + 1].mLive |= LIVE_CPU_REG_A; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + return true; + } +#if 1 + if ( + pass >= 6 && + mIns[i + 0].mType == ASMIT_ASL && mIns[i + 0].mMode == ASMIM_IMPLIED && + mIns[i + 1].mType == ASMIT_STA && + mIns[i + 2].mType == ASMIT_INC && mIns[i + 2].SameEffectiveAddress(mIns[i + 1])) + { + mIns[i + 0].mType = ASMIT_SEC; mIns[i + 0].mLive |= LIVE_CPU_REG_C; + mIns[i + 1].mType = ASMIT_ROL; mIns[i + 1].mMode = ASMIM_IMPLIED; mIns[i + 1].mLive |= LIVE_CPU_REG_A; + mIns[i + 2].mType = ASMIT_STA; + return true; + } +#endif + if ( + mIns[i + 0].mType == ASMIT_STA && !(mIns[i + 0].mFlags & NCIF_VOLATILE) && + mIns[i + 2].mType == ASMIT_STA && mIns[i + 0].SameEffectiveAddress(mIns[i + 2]) && + !mIns[i + 1].ChangesAddress() && !mIns[i + 1].ChangesGlobalMemory() && + !mIns[i + 1].ChangesYReg() && !mIns[i + 1].ChangesXReg() && + !(mIns[i + 0].mMode == ASMIM_ZERO_PAGE && mIns[i + 1].UsesZeroPage(mIns[i + 0].mAddress))) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 0].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_IMMEDIATE && + mIns[i + 1].mType == ASMIT_LDX && + mIns[i + 2].mType == ASMIT_STX && !(mIns[i + 2].mLive & (LIVE_CPU_REG_X | LIVE_CPU_REG_Z))) + { + NativeCodeInstruction ins = mIns[i + 0]; + mIns[i + 0] = mIns[i + 1]; + mIns[i + 1] = mIns[i + 2]; + mIns[i + 2] = ins; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_IMMEDIATE && + mIns[i + 1].mType == ASMIT_LDY && + mIns[i + 2].mType == ASMIT_STY && !(mIns[i + 2].mLive & (LIVE_CPU_REG_Y | LIVE_CPU_REG_Z))) + { + NativeCodeInstruction ins = mIns[i + 0]; + mIns[i + 0] = mIns[i + 1]; + mIns[i + 1] = mIns[i + 2]; + mIns[i + 2] = ins; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_LDX && + mIns[i + 2].mType == ASMIT_STX && !(mIns[i + 2].mLive & (LIVE_CPU_REG_X | LIVE_CPU_REG_Z)) && !mIns[i + 2].SameEffectiveAddress(mIns[i + 0])) + { + NativeCodeInstruction ins = mIns[i + 0]; + mIns[i + 0] = mIns[i + 1]; + mIns[i + 1] = mIns[i + 2]; + mIns[i + 2] = ins; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_LDY && + mIns[i + 2].mType == ASMIT_STY && !(mIns[i + 2].mLive & (LIVE_CPU_REG_Y | LIVE_CPU_REG_Z)) && !mIns[i + 2].SameEffectiveAddress(mIns[i + 0])) + { + NativeCodeInstruction ins = mIns[i + 0]; + mIns[i + 0] = mIns[i + 1]; + mIns[i + 1] = mIns[i + 2]; + mIns[i + 2] = ins; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_INC && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + !mIns[i + 1].ChangesZeroPage(mIns[i + 0].mAddress) && !mIns[i + 1].RequiresYReg() && + mIns[i + 2].mType == ASMIT_LDY && mIns[i + 2].SameEffectiveAddress(mIns[i + 0]) && !(mIns[i + 2].mLive & LIVE_MEM)) + { + mIns[i + 0] = mIns[i + 2]; + mIns[i + 2].mType = ASMIT_INY; mIns[i + 2].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_STA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + !mIns[i + 1].ChangesZeroPage(mIns[i + 0].mAddress) && !mIns[i + 1].RequiresYReg() && !mIns[i + 1].ChangesYReg() && + mIns[i + 2].mType == ASMIT_LDY && mIns[i + 2].SameEffectiveAddress(mIns[i + 0]) && !(mIns[i + 2].mLive & LIVE_MEM) && + (!mIns[i + 1].ChangesZFlag() || !(mIns[i + 2].mLive & LIVE_CPU_REG_Z))) + { + mIns[i + 0].mType = ASMIT_TAY; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + mIns[i + 0].mLive |= LIVE_CPU_REG_Y; + mIns[i + 1].mLive |= LIVE_CPU_REG_Y; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_ASL && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_ROL && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && mIns[i + 1].mAddress != mIns[i + 0].mAddress && + mIns[i + 2].mType == ASMIT_LDA && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && mIns[i + 2].mAddress == mIns[i + 0].mAddress && !(mIns[i + 2].mLive & (LIVE_MEM | LIVE_CPU_REG_C | LIVE_CPU_REG_Z))) + { + mIns[i + 0].mType = ASMIT_LDA; mIns[i + 0].mLive |= LIVE_CPU_REG_A; + mIns[i + 2].mType = ASMIT_ROL; mIns[i + 2].mAddress = mIns[i + 1].mAddress; mIns[i + 2].mLive |= LIVE_MEM; + mIns[i + 1].mType = ASMIT_ASL; mIns[i + 1].mMode = ASMIM_IMPLIED; mIns[i + 1].mLive |= LIVE_CPU_REG_A | LIVE_CPU_REG_C; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_LDA && + mIns[i + 1].mType == ASMIT_ROR && mIns[i + 1].mMode == ASMIM_IMPLIED && + mIns[i + 2].mType == ASMIT_AND && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 0x80 && !(mIns[i + 2].mLive & (LIVE_CPU_REG_C | LIVE_CPU_REG_Z))) + { + mIns[i + 0].mMode = ASMIM_IMMEDIATE; mIns[i + 0].mAddress = 0; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_ASL && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_ASL && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && mIns[i + 1].mAddress == mIns[i + 0].mAddress && + mIns[i + 2].mType == ASMIT_ASL && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && mIns[i + 2].mAddress == mIns[i + 0].mAddress && + !(mIns[i + 2].mLive & LIVE_CPU_REG_A)) + { + int addr = mIns[i + 0].mAddress; + + mIns.Insert(i, NativeCodeInstruction(mIns[i + 0].mIns, ASMIT_LDA, ASMIM_ZERO_PAGE, addr)); + mIns.Insert(i + 4, NativeCodeInstruction(mIns[i + 0].mIns, ASMIT_STA, ASMIM_ZERO_PAGE, addr)); + + mIns[i + 0].mLive = mIns[i + 1].mLive | LIVE_CPU_REG_A; + mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 1].mLive |= LIVE_CPU_REG_A; + mIns[i + 2].mMode = ASMIM_IMPLIED; + mIns[i + 2].mLive |= LIVE_CPU_REG_A; + mIns[i + 3].mMode = ASMIM_IMPLIED; + mIns[i + 3].mLive |= LIVE_CPU_REG_A; + mIns[i + 4].mLive = mIns[i + 3].mLive; + + return true; + } +#if 1 + if ( + mIns[i + 0].mType == ASMIT_LDA && (mIns[i + 0].mMode == ASMIM_ZERO_PAGE || mIns[i + 0].mMode == ASMIM_ABSOLUTE) && + mIns[i + 1].IsShiftOrInc() && mIns[i + 1].mMode != ASMIM_IMPLIED && !mIns[i + 0].SameEffectiveAddress(mIns[i + 1]) && + (mIns[i + 2].mType == ASMIT_TAX || mIns[i + 2].mType == ASMIT_TAY) && !(mIns[i + 0].mFlags & mIns[i + 1].mFlags & NCIF_VOLATILE)) + { + NativeCodeInstruction ins = mIns[i + 0]; + mIns[i + 0] = mIns[i + 1]; + mIns[i + 1] = ins; + return true; + } +#endif + if ( + mIns[i + 0].mType == ASMIT_TYA && + !mIns[i + 1].ChangesYReg() && !mIns[i + 1].ChangesAccu() && + mIns[i + 2].mType == ASMIT_TAY && !(mIns[i + 2].mLive & LIVE_CPU_REG_Z)) + { + mIns[i + 0].mLive |= LIVE_CPU_REG_Y; + mIns[i + 1].mLive |= LIVE_CPU_REG_Y; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_TXA && + !mIns[i + 1].ChangesXReg() && !mIns[i + 1].ChangesAccu() && + mIns[i + 2].mType == ASMIT_TAX && !(mIns[i + 2].mLive & LIVE_CPU_REG_Z)) + { + mIns[i + 0].mLive |= LIVE_CPU_REG_X; + mIns[i + 1].mLive |= LIVE_CPU_REG_X; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_TAY && + !mIns[i + 1].ChangesYReg() && !mIns[i + 1].ChangesAccu() && + mIns[i + 2].mType == ASMIT_TYA && !(mIns[i + 2].mLive & LIVE_CPU_REG_Z)) + { + mIns[i + 0].mLive |= LIVE_CPU_REG_A; + mIns[i + 1].mLive |= LIVE_CPU_REG_A; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_TAX && + !mIns[i + 1].ChangesXReg() && !mIns[i + 1].ChangesAccu() && + mIns[i + 2].mType == ASMIT_TXA && !(mIns[i + 2].mLive & LIVE_CPU_REG_Z)) + { + mIns[i + 0].mLive |= LIVE_CPU_REG_A; + mIns[i + 1].mLive |= LIVE_CPU_REG_A; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_TAY && + !mIns[i + 1].ChangesYReg() && !mIns[i + 1].ChangesAccu() && + mIns[i + 2].mType == ASMIT_STY && !(mIns[i + 2].mLive & LIVE_CPU_REG_Y)) + { + mIns[i + 0].mLive |= LIVE_CPU_REG_A; + mIns[i + 1].mLive |= LIVE_CPU_REG_A; + mIns[i + 2].mType = ASMIT_STA; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_TAX && + !mIns[i + 1].ChangesXReg() && !mIns[i + 1].ChangesAccu() && + mIns[i + 2].mType == ASMIT_STX && !(mIns[i + 2].mLive & LIVE_CPU_REG_X)) + { + mIns[i + 0].mLive |= LIVE_CPU_REG_A; + mIns[i + 1].mLive |= LIVE_CPU_REG_A; + mIns[i + 2].mType = ASMIT_STA; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_TAX && + !mIns[i + 1].ChangesXReg() && !mIns[i + 1].ChangesAccu() && mIns[i + 1].ChangesZFlag() && + mIns[i + 2].mType == ASMIT_TXA && (mIns[i + 2].mLive & LIVE_CPU_REG_Z)) + { + mIns[i + 2] = mIns[i + 0]; mIns[i + 2].mLive |= LIVE_CPU_REG_Z; + mIns[i + 1].mLive |= LIVE_CPU_REG_A; + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_TAX && + mIns[i + 1].mType == ASMIT_TAY && + mIns[i + 2].mMode == ASMIM_ABSOLUTE_Y && (mIns[i + 2].mLive & LIVE_CPU_REG_X) && !(mIns[i + 2].mLive & LIVE_CPU_REG_Y) && HasAsmInstructionMode(mIns[i + 2].mType, ASMIM_ABSOLUTE_X)) + { + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mMode = ASMIM_ABSOLUTE_X; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_TAY && + mIns[i + 1].mType == ASMIT_TAX && + mIns[i + 2].mMode == ASMIM_ABSOLUTE_Y && (mIns[i + 2].mLive & LIVE_CPU_REG_X) && !(mIns[i + 2].mLive & LIVE_CPU_REG_Y) && HasAsmInstructionMode(mIns[i + 2].mType, ASMIM_ABSOLUTE_X)) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 2].mMode = ASMIM_ABSOLUTE_X; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_TAX && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_ABSOLUTE_X && + mIns[i + 2].mType == ASMIT_TAX && !(mIns[i + 2].mLive & (LIVE_CPU_REG_Y | LIVE_CPU_REG_A))) + { + mIns[i + 0].mType = ASMIT_TAY; mIns[i + 0].mLive |= LIVE_CPU_REG_Y; + mIns[i + 1].mType = ASMIT_LDX; mIns[i + 1].mMode = ASMIM_ABSOLUTE_Y; mIns[i + 1].mLive |= LIVE_CPU_REG_X; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_TAY && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_ABSOLUTE_Y && + mIns[i + 2].mType == ASMIT_TAY && !(mIns[i + 2].mLive & (LIVE_CPU_REG_X | LIVE_CPU_REG_A))) + { + mIns[i + 0].mType = ASMIT_TAX; mIns[i + 0].mLive |= LIVE_CPU_REG_X; + mIns[i + 1].mType = ASMIT_LDY; mIns[i + 1].mMode = ASMIM_ABSOLUTE_X; mIns[i + 1].mLive |= LIVE_CPU_REG_Y; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + (mIns[i + 0].mType == ASMIT_INX || mIns[i + 0].mType == ASMIT_DEX) && + mIns[i + 1].mType == ASMIT_STX && + mIns[i + 2].mType == ASMIT_TXA && !(mIns[i + 2].mLive & LIVE_CPU_REG_A)) + { + mIns[i + 0].mLive |= mIns[i + 2].mLive & LIVE_CPU_REG_Z; + mIns[i + 1].mLive |= mIns[i + 2].mLive & LIVE_CPU_REG_Z; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + (mIns[i + 0].mType == ASMIT_INY || mIns[i + 0].mType == ASMIT_DEY) && + mIns[i + 1].mType == ASMIT_STY && + mIns[i + 2].mType == ASMIT_TYA && !(mIns[i + 2].mLive & LIVE_CPU_REG_A)) + { + mIns[i + 0].mLive |= mIns[i + 2].mLive & LIVE_CPU_REG_Z; + mIns[i + 1].mLive |= mIns[i + 2].mLive & LIVE_CPU_REG_Z; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_LDA && !(mIns[i + 0].mFlags & NCIF_VOLATILE) && + mIns[i + 1].mType == ASMIT_STA && !mIns[i + 0].MayBeChangedOnAddress(mIns[i + 1], true) && + mIns[i + 2].mType == ASMIT_LDA && mIns[i + 2].SameEffectiveAddress(mIns[i + 0]) && !(mIns[i + 2].mFlags & NCIF_VOLATILE)) + { + mIns[i + 0].mLive |= mIns[i + 2].mLive; + mIns[i + 1].mLive |= mIns[i + 2].mLive; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_LDA && HasAsmInstructionMode(ASMIT_LDX, mIns[i + 0].mMode) && + mIns[i + 1].mType == ASMIT_STA && HasAsmInstructionMode(ASMIT_STX, mIns[i + 1].mMode) && + mIns[i + 2].mType == ASMIT_TAX && !(mIns[i + 2].mLive & LIVE_CPU_REG_A)) + { + mIns[i + 0].mType = ASMIT_LDX; mIns[i + 0].mLive |= LIVE_CPU_REG_X; + mIns[i + 1].mType = ASMIT_STX; mIns[i + 1].mLive |= LIVE_CPU_REG_X; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + if (mIns[i + 2].mLive & LIVE_CPU_REG_Z) + { + mIns[i + 0].mLive |= LIVE_CPU_REG_Z; + mIns[i + 1].mLive |= LIVE_CPU_REG_Z; + } + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_LDA && HasAsmInstructionMode(ASMIT_LDY, mIns[i + 0].mMode) && + mIns[i + 1].mType == ASMIT_STA && HasAsmInstructionMode(ASMIT_STY, mIns[i + 1].mMode) && + mIns[i + 2].mType == ASMIT_TAY && !(mIns[i + 2].mLive & LIVE_CPU_REG_A)) + { + mIns[i + 0].mType = ASMIT_LDY; mIns[i + 0].mLive |= LIVE_CPU_REG_Y; + mIns[i + 1].mType = ASMIT_STY; mIns[i + 1].mLive |= LIVE_CPU_REG_Y; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + if (mIns[i + 2].mLive & LIVE_CPU_REG_Z) + { + mIns[i + 0].mLive |= LIVE_CPU_REG_Z; + mIns[i + 1].mLive |= LIVE_CPU_REG_Z; + } + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_LDA && (mIns[i + 0].mMode == ASMIM_ZERO_PAGE || mIns[i + 0].mMode == ASMIM_ABSOLUTE || mIns[i + 0].mMode == ASMIM_IMMEDIATE) && + mIns[i + 1].mType == ASMIT_STA && (mIns[i + 1].mMode == ASMIM_ZERO_PAGE || mIns[i + 1].mMode == ASMIM_ABSOLUTE) && + mIns[i + 2].mType == ASMIT_LDY && mIns[i + 2].SameEffectiveAddress(mIns[i + 0]) && !(mIns[i + 2].mLive & LIVE_CPU_REG_A) && !(mIns[i + 2].mFlags & NCIF_VOLATILE)) + { + mIns[i + 0].mType = ASMIT_LDY; mIns[i + 0].mLive |= LIVE_CPU_REG_Y; + mIns[i + 1].mType = ASMIT_STY; mIns[i + 1].mLive |= LIVE_CPU_REG_Y; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + if (mIns[i + 2].mLive & LIVE_CPU_REG_Z) + { + mIns[i + 0].mLive |= LIVE_CPU_REG_Z; + mIns[i + 1].mLive |= LIVE_CPU_REG_Z; + } + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_LDA && (mIns[i + 0].mMode == ASMIM_ZERO_PAGE || mIns[i + 0].mMode == ASMIM_ABSOLUTE || mIns[i + 0].mMode == ASMIM_IMMEDIATE) && + mIns[i + 1].mType == ASMIT_STA && (mIns[i + 1].mMode == ASMIM_ZERO_PAGE || mIns[i + 1].mMode == ASMIM_ABSOLUTE) && + mIns[i + 2].mType == ASMIT_LDX && mIns[i + 2].SameEffectiveAddress(mIns[i + 0]) && !(mIns[i + 2].mLive & LIVE_CPU_REG_A) && !(mIns[i + 2].mFlags & NCIF_VOLATILE)) + { + mIns[i + 0].mType = ASMIT_LDX; mIns[i + 0].mLive |= LIVE_CPU_REG_X; + mIns[i + 1].mType = ASMIT_STX; mIns[i + 1].mLive |= LIVE_CPU_REG_X; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + if (mIns[i + 2].mLive & LIVE_CPU_REG_Z) + { + mIns[i + 0].mLive |= LIVE_CPU_REG_Z; + mIns[i + 1].mLive |= LIVE_CPU_REG_Z; + } + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_STA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && !(mIns[i + 0].mLive & LIVE_CPU_REG_Y) && + !mIns[i + 1].UsesZeroPage(mIns[i + 0].mAddress) && !(mIns[i + 1].ChangesYReg()) && + mIns[i + 2].mType == ASMIT_LDY && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && mIns[i + 2].mAddress == mIns[i + 0].mAddress && !(mIns[i + 2].mLive & (LIVE_MEM | LIVE_CPU_REG_Z))) + { + mIns[i + 0].mType = ASMIT_TAY; mIns[i + 0].mMode = ASMIM_IMPLIED; mIns[i + 0].mLive |= LIVE_CPU_REG_Y; + mIns[i + 1].mLive |= LIVE_CPU_REG_Y; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_STA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && !(mIns[i + 0].mLive & LIVE_CPU_REG_X) && + !mIns[i + 1].UsesZeroPage(mIns[i + 0].mAddress) && !(mIns[i + 1].ChangesXReg()) && + mIns[i + 2].mType == ASMIT_LDX && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && mIns[i + 2].mAddress == mIns[i + 0].mAddress && !(mIns[i + 2].mLive & (LIVE_MEM | LIVE_CPU_REG_Z))) + { + mIns[i + 0].mType = ASMIT_TAX; mIns[i + 0].mMode = ASMIM_IMPLIED; mIns[i + 0].mLive |= LIVE_CPU_REG_X; + mIns[i + 1].mLive |= LIVE_CPU_REG_X; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_INC && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && mIns[i + 0].mAddress == mIns[i + 1].mAddress && !(mIns[i + 1].mLive & LIVE_MEM) && + mIns[i + 2].mType == ASMIT_STA && (mIns[i + 2].mMode == ASMIM_ZERO_PAGE || mIns[i + 2].mMode == ASMIM_ABSOLUTE) && !(mIns[i + 2].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Y))) + { + mIns[i + 0].mType = ASMIT_LDY; mIns[i + 0].mLive |= LIVE_CPU_REG_Y; + mIns[i + 1].mType = ASMIT_INY; mIns[i + 1].mMode = ASMIM_IMPLIED; mIns[i + 1].mLive |= LIVE_CPU_REG_Y; + mIns[i + 2].mType = ASMIT_STY; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_STA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_LDA && !mIns[i + 1].SameEffectiveAddress(mIns[i + 0]) && + mIns[i + 2].IsShift() && mIns[i + 2].SameEffectiveAddress(mIns[i + 0])) + { + AsmInsType type = mIns[i + 2].mType; + uint32 live = mIns[i + 2].mLive; + + mIns[i + 2] = mIns[i + 1]; + mIns[i + 1] = mIns[i + 0]; + + mIns[i + 0].mType = type; + mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 0].mLive |= LIVE_CPU_REG_A; + mIns[i + 0].mLive |= live; + mIns[i + 1].mLive |= live; + mIns[i + 2].mLive |= live; + + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_LDA && + mIns[i + 1].mType == ASMIT_STA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && !(mIns[i + 1].mLive & LIVE_CPU_REG_A) && + mIns[i + 2].mType == ASMIT_CPX && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && mIns[i + 1].mAddress == mIns[i + 2].mAddress && !(mIns[i + 2].mLive & LIVE_MEM)) + { + mIns[i + 1] = mIns[i + 0]; + mIns[i + 1].mType = ASMIT_CMP; + mIns[i + 1].mLive |= mIns[i + 2].mLive; + mIns[i + 0].mType = ASMIT_TXA; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 0].mLive |= LIVE_CPU_REG_A; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + if (mIns[i + 1].ReferencesYReg()) mIns[i + 0].mLive |= LIVE_CPU_REG_Y; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_LDA && HasAsmInstructionMode(ASMIT_LDX, mIns[i + 0].mMode) && + mIns[i + 1].mType == ASMIT_TAX && + mIns[i + 2].mType == ASMIT_CMP && HasAsmInstructionMode(ASMIT_CPX, mIns[i + 2].mMode) && !(mIns[i + 2].mLive & LIVE_CPU_REG_A)) + { + mIns[i + 0].mType = ASMIT_LDX; mIns[i + 0].mLive |= LIVE_CPU_REG_X; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_CPX; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_LDA && HasAsmInstructionMode(ASMIT_LDY, mIns[i + 0].mMode) && + mIns[i + 1].mType == ASMIT_TAY && + mIns[i + 2].mType == ASMIT_CMP && HasAsmInstructionMode(ASMIT_CPY, mIns[i + 2].mMode) && !(mIns[i + 2].mLive & LIVE_CPU_REG_A)) + { + mIns[i + 0].mType = ASMIT_LDY; mIns[i + 0].mLive |= LIVE_CPU_REG_Y; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_CPY; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_LDA && !mIns[i + 0].RequiresXReg() && + mIns[i + 1].mType == ASMIT_LDX && + mIns[i + 2].mType == ASMIT_STX && !(mIns[i + 2].mLive & LIVE_CPU_REG_X) && !mIns[i + 0].MayBeChangedOnAddress(mIns[i + 2]) && + !(mIns[i + 0].mFlags & (mIns[i + 1].mFlags | mIns[i + 2].mFlags) & NCIF_VOLATILE)) + { + NativeCodeInstruction ins = mIns[i + 0]; + mIns[i + 0] = mIns[i + 1]; + mIns[i + 1] = mIns[i + 2]; + mIns[i + 2] = ins; + mIns[i + 0].mType = ASMIT_LDA; mIns[i + 0].mLive |= LIVE_CPU_REG_A | mIns[i + 2].mLive; + mIns[i + 1].mType = ASMIT_STA; mIns[i + 1].mLive |= mIns[i + 2].mLive; + if (mIns[i + 2].RequiresYReg()) + { + mIns[i + 0].mLive |= LIVE_CPU_REG_Y; + mIns[i + 1].mLive |= LIVE_CPU_REG_Y; + } + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_LDX && + mIns[i + 1].mType == ASMIT_STX && !(mIns[i + 1].mLive & LIVE_CPU_REG_X) && + mIns[i + 2].mType == ASMIT_STA && !(mIns[i + 2].mLive & LIVE_CPU_REG_A) && + !(mIns[i + 2].mFlags & (mIns[i + 0].mFlags | mIns[i + 1].mFlags) & NCIF_VOLATILE) && + !mIns[i + 0].MayBeChangedOnAddress(mIns[i + 2]) && + !mIns[i + 1].MayBeChangedOnAddress(mIns[i + 2]) && + !mIns[i + 2].MayBeChangedOnAddress(mIns[i + 1])) + { + NativeCodeInstruction ins = mIns[i + 2]; + mIns[i + 2] = mIns[i + 1]; + mIns[i + 1] = mIns[i + 0]; + mIns[i + 0] = ins; + mIns[i + 1].mType = ASMIT_LDA; mIns[i + 1].mLive |= LIVE_CPU_REG_A | mIns[i + 0].mLive; + mIns[i + 2].mType = ASMIT_STA; mIns[i + 2].mLive |= mIns[i + 0].mLive; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_LDX && + mIns[i + 1].mType == ASMIT_STX && + mIns[i + 2].mType == ASMIT_TXA && !(mIns[i + 2].mLive & LIVE_CPU_REG_X)) + { + mIns[i + 0].mType = ASMIT_LDA; mIns[i + 0].mLive |= LIVE_CPU_REG_A | (mIns[i + 2].mLive & LIVE_CPU_REG_Z); + mIns[i + 1].mType = ASMIT_STA; mIns[i + 1].mLive |= LIVE_CPU_REG_A | (mIns[i + 2].mLive & LIVE_CPU_REG_Z); + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_LDY && + mIns[i + 1].mType == ASMIT_STY && + mIns[i + 2].mType == ASMIT_TYA && !(mIns[i + 2].mLive & LIVE_CPU_REG_Y)) + { + mIns[i + 0].mType = ASMIT_LDA; mIns[i + 0].mLive |= LIVE_CPU_REG_A | (mIns[i + 2].mLive & LIVE_CPU_REG_Z); + mIns[i + 1].mType = ASMIT_STA; mIns[i + 1].mLive |= LIVE_CPU_REG_A | (mIns[i + 2].mLive & LIVE_CPU_REG_Z); + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + mIns[i + 2].mType == ASMIT_TXA && !(mIns[i + 2].mLive & LIVE_CPU_REG_A) && + (mIns[i + 0].mType == ASMIT_INX || mIns[i + 0].mType == ASMIT_DEX) && + !mIns[i + 1].ChangesXReg() && !mIns[i + 1].ChangesZFlag()) + { + mIns[i + 0].mLive |= LIVE_CPU_REG_Z; + mIns[i + 1].mLive |= LIVE_CPU_REG_Z; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + mIns[i + 2].mType == ASMIT_TYA && !(mIns[i + 2].mLive & LIVE_CPU_REG_A) && + (mIns[i + 0].mType == ASMIT_INY || mIns[i + 0].mType == ASMIT_DEY) && + !mIns[i + 1].ChangesYReg() && !mIns[i + 1].ChangesZFlag()) + { + mIns[i + 0].mLive |= LIVE_CPU_REG_Z; + mIns[i + 1].mLive |= LIVE_CPU_REG_Z; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_STA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + mIns[i + 2].mType == ASMIT_CMP && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && mIns[i + 0].mAddress == mIns[i + 2].mAddress && + !(mIns[i + 2].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C | LIVE_MEM))) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_CMP; mIns[i + 1].mLive |= LIVE_CPU_REG_Z; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_STA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + mIns[i + 2].mType == ASMIT_CMP && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && mIns[i + 0].mAddress == mIns[i + 2].mAddress && + !(mIns[i + 2].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C))) + { + mIns[i + 0].mLive |= LIVE_CPU_REG_A; + mIns[i + 1].mType = ASMIT_CMP; mIns[i + 1].mLive |= LIVE_CPU_REG_Z; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_ROL && mIns[i + 0].mMode == ASMIM_IMPLIED && + mIns[i + 1].mType == ASMIT_ASL && mIns[i + 1].mMode == ASMIM_IMPLIED && + mIns[i + 2].mType == ASMIT_AND && mIns[i + 2].mMode == ASMIM_IMMEDIATE && !(mIns[i + 2].mAddress & 0x03) && !(mIns[i + 2].mLive & LIVE_CPU_REG_C)) + { + mIns[i + 0].mType = ASMIT_ASL; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_ROR && mIns[i + 0].mMode == ASMIM_IMPLIED && + mIns[i + 1].mType == ASMIT_LSR && mIns[i + 1].mMode == ASMIM_IMPLIED && + mIns[i + 2].mType == ASMIT_AND && mIns[i + 2].mMode == ASMIM_IMMEDIATE && !(mIns[i + 2].mAddress & 0xc0) && !(mIns[i + 2].mLive & LIVE_CPU_REG_C)) + { + mIns[i + 0].mType = ASMIT_LSR; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_AND && mIns[i + 0].mMode == ASMIM_IMMEDIATE && + mIns[i + 1].mType == ASMIT_LSR && mIns[i + 1].mMode == ASMIM_IMPLIED && + mIns[i + 2].mType == ASMIT_AND && mIns[i + 2].mMode == ASMIM_IMMEDIATE) + { + mIns[i + 0].mAddress &= (mIns[i + 2].mAddress << 1) | 1; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_AND && mIns[i + 0].mMode == ASMIM_IMMEDIATE && + mIns[i + 1].mType == ASMIT_ASL && mIns[i + 1].mMode == ASMIM_IMPLIED && + mIns[i + 2].mType == ASMIT_AND && mIns[i + 2].mMode == ASMIM_IMMEDIATE) + { + mIns[i + 0].mAddress &= (mIns[i + 2].mAddress >> 1) | 0x80; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_LSR && mIns[i + 0].mMode == ASMIM_IMPLIED && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 0 && + mIns[i + 2].mType == ASMIT_ROL && mIns[i + 2].mMode == ASMIM_IMPLIED) + { + mIns[i + 1].mType = ASMIT_AND; mIns[i + 1].mAddress = 1; + mIns[i + 1].mLive |= mIns[i + 2].mLive & LIVE_CPU_REG_Z; + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_CLC; mIns[i + 2].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + mIns[i + 0].IsShift() && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + !mIns[i + 1].ReferencesAccu() && (mIns[i + 1].mMode != ASMIM_ZERO_PAGE || mIns[i + 1].mAddress != mIns[i + 0].mAddress) && + mIns[i + 2].mType == ASMIT_LDA && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && mIns[i + 2].mAddress == mIns[i + 0].mAddress && !(mIns[i + 2].mLive & (LIVE_MEM | LIVE_CPU_REG_Z))) + { + NativeCodeInstruction ins = mIns[i + 2]; + mIns.Remove(i + 2); + + mIns[i + 0].mMode = ASMIM_IMPLIED; mIns[i + 0].mLive |= LIVE_CPU_REG_A; + mIns[i + 1].mLive |= LIVE_CPU_REG_A; + mIns.Insert(i, ins); + if (mIns[i + 1].RequiresCarry()) + mIns[i + 0].mLive |= LIVE_CPU_REG_C; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_AND && mIns[i + 0].mMode == ASMIM_IMMEDIATE && (mIns[i + 0].mAddress & 0x3f) == 0x3f && + mIns[i + 1].mType == ASMIT_ASL && mIns[i + 1].mMode == ASMIM_IMPLIED && + mIns[i + 2].mType == ASMIT_ASL && mIns[i + 2].mMode == ASMIM_IMPLIED && !(mIns[i + 2].mLive & LIVE_CPU_REG_C)) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_LSR && mIns[i + 0].mMode == ASMIM_IMPLIED && + mIns[i + 1].mType == ASMIT_AND && mIns[i + 1].mMode == ASMIM_IMMEDIATE && + mIns[i + 2].mType == ASMIT_ASL && mIns[i + 2].mMode == ASMIM_IMPLIED && !(mIns[i + 2].mLive & LIVE_CPU_REG_C)) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mAddress = (mIns[i + 1].mAddress << 1) & 0xff; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_INY && + mIns[i + 1].mType == ASMIT_INY && + mIns[i + 2].mMode == ASMIM_ABSOLUTE_Y && !(mIns[i + 2].mLive & LIVE_CPU_REG_Y)) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mAddress += 2; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_INX && + mIns[i + 1].mType == ASMIT_INX && + mIns[i + 2].mMode == ASMIM_ABSOLUTE_X && !(mIns[i + 2].mLive & LIVE_CPU_REG_X)) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mAddress += 2; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_TXA && !(mIns[i + 0].mLive & LIVE_CPU_REG_X) && + mIns[i + 1].mType == ASMIT_CLC && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 1 && !(mIns[i + 2].mLive & LIVE_CPU_REG_C)) + { + mIns[i + 0].mType = ASMIT_INX; mIns[i + 0].mLive |= LIVE_CPU_REG_X; + mIns[i + 1].mType = ASMIT_TXA; mIns[i + 1].mLive |= LIVE_CPU_REG_Z; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_TXA && !(mIns[i + 0].mLive & LIVE_CPU_REG_X) && + mIns[i + 1].mType == ASMIT_SEC && + mIns[i + 2].mType == ASMIT_SBC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 1 && !(mIns[i + 2].mLive & LIVE_CPU_REG_C)) + { + mIns[i + 0].mType = ASMIT_DEX; mIns[i + 0].mLive |= LIVE_CPU_REG_X; + mIns[i + 1].mType = ASMIT_TXA; mIns[i + 1].mLive |= LIVE_CPU_REG_Z; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_TYA && !(mIns[i + 0].mLive & LIVE_CPU_REG_Y) && + mIns[i + 1].mType == ASMIT_CLC && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 1 && !(mIns[i + 2].mLive & LIVE_CPU_REG_C)) + { + mIns[i + 0].mType = ASMIT_INY; mIns[i + 0].mLive |= LIVE_CPU_REG_Y; + mIns[i + 1].mType = ASMIT_TYA; mIns[i + 1].mLive |= LIVE_CPU_REG_Z; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_TYA && !(mIns[i + 0].mLive & LIVE_CPU_REG_Y) && + mIns[i + 1].mType == ASMIT_SEC && + mIns[i + 2].mType == ASMIT_SBC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 1 && !(mIns[i + 2].mLive & LIVE_CPU_REG_C)) + { + mIns[i + 0].mType = ASMIT_DEY; mIns[i + 0].mLive |= LIVE_CPU_REG_Y; + mIns[i + 1].mType = ASMIT_TYA; mIns[i + 1].mLive |= LIVE_CPU_REG_Z; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_ADC && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 1 && + mIns[i + 2].mType == ASMIT_TAY && !(mIns[i + 2].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C))) + { + mIns[i + 0].mType = ASMIT_TAY; mIns[i + 0].mMode = ASMIM_IMPLIED; mIns[i + 0].mLive |= LIVE_CPU_REG_Y; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_INY; mIns[i + 2].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_SEC && + mIns[i + 1].mType == ASMIT_SBC && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 1 && + mIns[i + 2].mType == ASMIT_TAY && !(mIns[i + 2].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C))) + { + mIns[i + 0].mType = ASMIT_TAY; mIns[i + 0].mMode = ASMIM_IMPLIED; mIns[i + 0].mLive |= LIVE_CPU_REG_Y; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_DEY; mIns[i + 2].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_ADC && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 1 && + mIns[i + 2].mType == ASMIT_TAX && !(mIns[i + 2].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C))) + { + mIns[i + 0].mType = ASMIT_TAX; mIns[i + 0].mMode = ASMIM_IMPLIED; mIns[i + 0].mLive |= LIVE_CPU_REG_X; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_INX; mIns[i + 2].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_SEC && + mIns[i + 1].mType == ASMIT_SBC && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 1 && + mIns[i + 2].mType == ASMIT_TAX && !(mIns[i + 2].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C))) + { + mIns[i + 0].mType = ASMIT_TAX; mIns[i + 0].mMode = ASMIM_IMPLIED; mIns[i + 0].mLive |= LIVE_CPU_REG_X; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_DEX; mIns[i + 2].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_LDA && (mIns[i + 0].mMode == ASMIM_ZERO_PAGE || mIns[i + 0].mMode == ASMIM_ABSOLUTE) && + mIns[i + 1].mType == ASMIT_TAX && + mIns[i + 2].mType == ASMIT_STA && (mIns[i + 2].mMode == ASMIM_ZERO_PAGE || mIns[i + 2].mMode == ASMIM_ABSOLUTE) && + !(mIns[i + 2].mLive & LIVE_CPU_REG_A)) + { + mIns[i + 0].mType = ASMIT_LDX; mIns[i + 0].mLive |= LIVE_CPU_REG_X; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_STX; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_LDA && (mIns[i + 0].mMode == ASMIM_ZERO_PAGE || mIns[i + 0].mMode == ASMIM_ABSOLUTE) && + mIns[i + 1].mType == ASMIT_TAY && + mIns[i + 2].mType == ASMIT_STA && (mIns[i + 2].mMode == ASMIM_ZERO_PAGE || mIns[i + 2].mMode == ASMIM_ABSOLUTE) && + !(mIns[i + 2].mLive & LIVE_CPU_REG_A)) + { + mIns[i + 0].mType = ASMIT_LDY; mIns[i + 0].mLive |= LIVE_CPU_REG_Y; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_STY; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_LDX && (mIns[i + 0].mMode == ASMIM_ZERO_PAGE || mIns[i + 0].mMode == ASMIM_ABSOLUTE) && + (mIns[i + 1].mType == ASMIT_INX || mIns[i + 1].mType == ASMIT_DEX) && + mIns[i + 2].mType == ASMIT_STX && mIns[i + 2].SameEffectiveAddress(mIns[i + 0]) && !(mIns[i + 2].mLive & LIVE_CPU_REG_X)) + { + mIns[i + 2].mType = mIns[i + 1].mType == ASMIT_INX ? ASMIT_INC : ASMIT_DEC; + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_LDY && (mIns[i + 0].mMode == ASMIM_ZERO_PAGE || mIns[i + 0].mMode == ASMIM_ABSOLUTE) && + (mIns[i + 1].mType == ASMIT_INY || mIns[i + 1].mType == ASMIT_DEY) && + mIns[i + 2].mType == ASMIT_STY && mIns[i + 2].SameEffectiveAddress(mIns[i + 0]) && !(mIns[i + 2].mLive & LIVE_CPU_REG_Y)) + { + mIns[i + 2].mType = mIns[i + 1].mType == ASMIT_INY ? ASMIT_INC : ASMIT_DEC; + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + (mIns[i + 0].mType == ASMIT_INC || mIns[i + 0].mType == ASMIT_DEC) && + mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].SameEffectiveAddress(mIns[i + 0]) && !(mIns[i + 1].mLive & LIVE_MEM) && + mIns[i + 2].mType == ASMIT_STA && !(mIns[i + 2].mLive & LIVE_CPU_REG_A) && + (mIns[i + 2].mMode == ASMIM_ZERO_PAGE || mIns[i + 2].mMode == ASMIM_ABSOLUTE) && + (mIns[i + 2].mLive & (LIVE_CPU_REG_X | LIVE_CPU_REG_Y)) != (LIVE_CPU_REG_X | LIVE_CPU_REG_Y)) + { + if (mIns[i + 2].mLive & LIVE_CPU_REG_X) + { + if (mIns[i + 0].mType == ASMIT_INC) + mIns[i + 1].mType = ASMIT_INY; + else + mIns[i + 1].mType = ASMIT_DEY; + + mIns[i + 0].mType = ASMIT_LDY; mIns[i + 0].mLive |= LIVE_CPU_REG_Y; + mIns[i + 1].mMode = ASMIM_IMPLIED; mIns[i + 1].mLive |= LIVE_CPU_REG_Y; + mIns[i + 2].mType = ASMIT_STY; + } + else + { + if (mIns[i + 0].mType == ASMIT_INC) + mIns[i + 1].mType = ASMIT_INX; + else + mIns[i + 1].mType = ASMIT_DEX; + + mIns[i + 0].mType = ASMIT_LDX; mIns[i + 0].mLive |= LIVE_CPU_REG_X; + mIns[i + 1].mMode = ASMIM_IMPLIED; mIns[i + 1].mLive |= LIVE_CPU_REG_X; + mIns[i + 2].mType = ASMIT_STX; + } + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_LDX && + mIns[i + 1].mType == ASMIT_TXA && + mIns[i + 2].mType == ASMIT_CPX && !(mIns[i + 2].mLive & LIVE_CPU_REG_X)) + { + mIns[i + 0].mType = ASMIT_LDA; mIns[i + 0].mLive |= LIVE_CPU_REG_A; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_CMP; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_LDY && + mIns[i + 1].mType == ASMIT_TYA && + mIns[i + 2].mType == ASMIT_CPY && !(mIns[i + 2].mLive & LIVE_CPU_REG_Y)) + { + mIns[i + 0].mType = ASMIT_LDA; mIns[i + 0].mLive |= LIVE_CPU_REG_A; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_CMP; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_ADC && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 0xff && + mIns[i + 2].mType == ASMIT_TAX && !(mIns[i + 2].mLive & (LIVE_CPU_REG_C | LIVE_CPU_REG_A))) + { + mIns[i + 0].mType = ASMIT_TAX; mIns[i + 0].mLive |= LIVE_CPU_REG_X; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_DEX; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_LDA && + mIns[i + 1].mType == ASMIT_STA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + mIns[i + 2].mType == ASMIT_CPY && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && mIns[i + 2].mAddress == mIns[i + 1].mAddress && + !(mIns[i + 2].mLive & (LIVE_CPU_REG_A | LIVE_MEM))) + { + mIns[i + 1].mType = ASMIT_CMP; mIns[i + 1].CopyMode(mIns[i + 0]); mIns[i + 1].mLive |= LIVE_CPU_REG_C | LIVE_CPU_REG_Z; + mIns[i + 0].mType = ASMIT_TYA; mIns[i + 0].mMode = ASMIM_IMPLIED; mIns[i + 0].mLive |= LIVE_CPU_REG_A; + + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_ADC && mIns[i + 0].mMode == ASMIM_IMMEDIATE && + mIns[i + 1].mType == ASMIT_SEC && + mIns[i + 2].mType == ASMIT_SBC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && !(mIns[i + 2].mLive & LIVE_CPU_REG_C)) + { + mIns[i + 0].mAddress = (mIns[i + 0].mAddress - mIns[i + 2].mAddress) & 0xff; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_LDX && (mIns[i + 0].mMode == ASMIM_ZERO_PAGE || mIns[i + 0].mMode == ASMIM_ABSOLUTE || mIns[i + 0].mMode == ASMIM_IMMEDIATE || mIns[i + 0].mMode == ASMIM_IMMEDIATE_ADDRESS) && !(mIns[i + 0].mFlags & NCIF_VOLATILE) && + mIns[i + 1].mType == ASMIT_STX && (mIns[i + 1].mMode == ASMIM_ZERO_PAGE || mIns[i + 1].mMode == ASMIM_ABSOLUTE) && !(mIns[i + 1].mFlags & NCIF_VOLATILE) && + mIns[i + 2].mType == ASMIT_STA && (mIns[i + 2].mMode == ASMIM_ZERO_PAGE || mIns[i + 2].mMode == ASMIM_ABSOLUTE) && !(mIns[i + 2].mFlags & NCIF_VOLATILE) && + !mIns[i + 0].SameEffectiveAddress(mIns[i + 2]) && !mIns[i + 1].SameEffectiveAddress(mIns[i + 2])) + { + NativeCodeInstruction ins(mIns[i + 2]); + mIns.Remove(i + 2); + mIns.Insert(i, ins); + mIns[i + 1].mLive |= ins.mLive & LIVE_CPU_REG_A; + mIns[i + 2].mLive |= ins.mLive & LIVE_CPU_REG_A; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_LDY && (mIns[i + 0].mMode == ASMIM_ZERO_PAGE || mIns[i + 0].mMode == ASMIM_ABSOLUTE || mIns[i + 0].mMode == ASMIM_IMMEDIATE || mIns[i + 0].mMode == ASMIM_IMMEDIATE_ADDRESS) && !(mIns[i + 0].mFlags & NCIF_VOLATILE) && + mIns[i + 1].mType == ASMIT_STY && (mIns[i + 1].mMode == ASMIM_ZERO_PAGE || mIns[i + 1].mMode == ASMIM_ABSOLUTE) && !(mIns[i + 1].mFlags & NCIF_VOLATILE) && + mIns[i + 2].mType == ASMIT_STA && (mIns[i + 2].mMode == ASMIM_ZERO_PAGE || mIns[i + 2].mMode == ASMIM_ABSOLUTE) && !(mIns[i + 2].mFlags & NCIF_VOLATILE) && + !mIns[i + 0].SameEffectiveAddress(mIns[i + 2]) && !mIns[i + 1].SameEffectiveAddress(mIns[i + 2])) + { + NativeCodeInstruction ins(mIns[i + 2]); + mIns.Remove(i + 2); + mIns.Insert(i, ins); + mIns[i + 1].mLive |= ins.mLive & LIVE_CPU_REG_A; + mIns[i + 2].mLive |= ins.mLive & LIVE_CPU_REG_A; + return true; + } + + if ( + mIns[i + 0].ChangesAccuAndFlag() && + (mIns[i + 1].mType == ASMIT_INC || mIns[i + 1].mType == ASMIT_DEC) && + mIns[i + 2].mType == ASMIT_ORA && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 0 && + mIns[i + 1].MayBeMovedBefore(mIns[i + 0])) + { + mIns.Insert(i, mIns[i + 1]); + mIns[i].mLive |= LIVE_CPU_REG_A; + if (mIns[i + 1].RequiresYReg()) + mIns[i].mLive |= LIVE_CPU_REG_Y; + if (mIns[i + 1].RequiresXReg()) + mIns[i].mLive |= LIVE_CPU_REG_X; + mIns[i + 1].mLive |= LIVE_CPU_REG_Z; + mIns.Remove(i + 2); + return true; + } + + if ( + mIns[i + 0].ChangesAccuAndFlag() && mIns[i + 0].mMode == ASMIM_IMPLIED && mIns[i + 0].mType != ASMIT_TXA && + (mIns[i + 1].mType == ASMIT_INX || mIns[i + 1].mType == ASMIT_DEX) && + mIns[i + 2].mType == ASMIT_ORA && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 0) + { + mIns[i + 2] = mIns[i + 0]; + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mLive |= LIVE_CPU_REG_A; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_TXA && + mIns[i + 1].mType == ASMIT_DEX && + mIns[i + 2].mType == ASMIT_ORA && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 0 && !(mIns[i + 2].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C))) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_CPX; mIns[i + 2].mMode = ASMIM_IMMEDIATE; mIns[i + 2].mAddress = 0xff; + return true; + } + + if ( + mIns[i + 0].ChangesAccuAndFlag() && mIns[i + 0].mMode == ASMIM_IMPLIED && mIns[i + 0].mType != ASMIT_TYA && + (mIns[i + 1].mType == ASMIT_INY || mIns[i + 1].mType == ASMIT_DEY) && + mIns[i + 2].mType == ASMIT_ORA && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 0) + { + mIns[i + 2] = mIns[i + 0]; + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mLive |= LIVE_CPU_REG_A; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_TYA && + mIns[i + 1].mType == ASMIT_DEY && + mIns[i + 2].mType == ASMIT_ORA && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 0 && !(mIns[i + 2].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C))) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_CPY; mIns[i + 2].mMode = ASMIM_IMMEDIATE; mIns[i + 2].mAddress = 0xff; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_DEY && + !mIns[i + 1].ReferencesYReg() && + mIns[i + 2].mType == ASMIT_INY && !(mIns[i + 2].mLive & LIVE_CPU_REG_Z)) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_INY && + !mIns[i + 1].ReferencesYReg() && + mIns[i + 2].mType == ASMIT_DEY && !(mIns[i + 2].mLive & LIVE_CPU_REG_Z)) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_DEX && + !mIns[i + 1].ReferencesXReg() && + mIns[i + 2].mType == ASMIT_INX && !(mIns[i + 2].mLive & LIVE_CPU_REG_Z)) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_INX && + !mIns[i + 1].ReferencesXReg() && + mIns[i + 2].mType == ASMIT_DEX && !(mIns[i + 2].mLive & LIVE_CPU_REG_Z)) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_LDA && + mIns[i + 1].mType == ASMIT_STA && + mIns[i + 2].mType == ASMIT_STA && mIns[i + 2].SameEffectiveAddress(mIns[i + 0]) && + !(mIns[i + 0].mMode == ASMIM_INDIRECT_Y && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && (mIns[i + 1].mAddress == mIns[i + 0].mAddress || mIns[i + 1].mAddress + 1 == mIns[i + 0].mAddress))) + { + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_LDA && HasAsmInstructionMode(ASMIT_LDX, mIns[i + 0].mMode) && + mIns[i + 1].mType == ASMIT_CMP && HasAsmInstructionMode(ASMIT_CPX, mIns[i + 1].mMode) && + mIns[i + 2].mType == ASMIT_TAX && !(mIns[i + 2].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z))) + { + mIns[i + 0].mType = ASMIT_LDX; mIns[i + 0].mLive |= LIVE_CPU_REG_X; + mIns[i + 1].mType = ASMIT_CPX; mIns[i + 1].mLive |= LIVE_CPU_REG_X; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_LDA && HasAsmInstructionMode(ASMIT_LDY, mIns[i + 0].mMode) && + mIns[i + 1].mType == ASMIT_CMP && HasAsmInstructionMode(ASMIT_CPY, mIns[i + 1].mMode) && + mIns[i + 2].mType == ASMIT_TAY && !(mIns[i + 2].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z))) + { + mIns[i + 0].mType = ASMIT_LDY; mIns[i + 0].mLive |= LIVE_CPU_REG_Y; + mIns[i + 1].mType = ASMIT_CPY; mIns[i + 1].mLive |= LIVE_CPU_REG_Y; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_STX && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_LDA && !(mIns[i + 1].ReferencesYReg()) && + mIns[i + 2].mType == ASMIT_LDY && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && mIns[i + 2].mAddress == mIns[i + 0].mAddress && !(mIns[i + 2].mLive & LIVE_MEM)) + { + mIns[i + 2] = mIns[i + 1]; + mIns[i + 0].mType = ASMIT_TXA; mIns[i + 0].mMode = ASMIM_IMPLIED; mIns[i + 0].mLive |= LIVE_CPU_REG_A; + mIns[i + 1].mType = ASMIT_TAY; mIns[i + 1].mMode = ASMIM_IMPLIED; mIns[i + 1].mLive |= LIVE_CPU_REG_Y; + if (mIns[i + 2].ReferencesXReg()) + mIns[i + 1].mLive |= LIVE_CPU_REG_X; + mIns[i + 2].mLive |= LIVE_CPU_REG_Y; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_STA && + mIns[i + 1].mType == ASMIT_STA && + mIns[i + 2].IsShift() && mIns[i + 2].SameEffectiveAddress(mIns[i + 0]) && !(mIns[i + 2].mLive & (LIVE_CPU_REG_A | LIVE_MEM))) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mLive |= LIVE_CPU_REG_A; + mIns[i + 2].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_SEC && + mIns[i + 1].mType == ASMIT_SBC && mIns[i + 1].mMode == ASMIM_IMMEDIATE && + mIns[i + 2].mType == ASMIT_CMP && mIns[i + 2].mMode == ASMIM_IMMEDIATE && !(mIns[i + 2].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C))) + { + mIns[i + 2].mAddress = (mIns[i + 2].mAddress + mIns[i + 1].mAddress) & 0xff; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_ADC && mIns[i + 1].mMode == ASMIM_IMMEDIATE && + mIns[i + 2].mType == ASMIT_CMP && mIns[i + 2].mMode == ASMIM_IMMEDIATE && !(mIns[i + 2].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C))) + { + mIns[i + 2].mAddress = (mIns[i + 2].mAddress - mIns[i + 1].mAddress) & 0xff; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + return true; + } + + if (mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_IMMEDIATE && !(mIns[i + 0].mAddress & 1) && + mIns[i + 1].mType == ASMIT_ADC && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 0 && + mIns[i + 2].mType == ASMIT_ORA && mIns[i + 2].mMode == ASMIM_IMMEDIATE && !(mIns[i + 2].mAddress & 1)) + { + mIns[i + 0].mAddress |= mIns[i + 2].mAddress; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + return true; + } + + if (mIns[i + 0].mType == ASMIT_STA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_ASL && mIns[i + 1].mMode == ASMIM_IMPLIED && + mIns[i + 2].mType == ASMIT_ASL && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && mIns[i + 2].mAddress == mIns[i + 0].mAddress && !(mIns[i + 2].mLive & LIVE_MEM)) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mLive |= LIVE_CPU_REG_C; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + return true; + } + + if (mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_ASL && mIns[i + 1].mMode == ASMIM_IMPLIED && + mIns[i + 2].mType == ASMIT_ASL && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && mIns[i + 2].mAddress == mIns[i + 0].mAddress && !(mIns[i + 2].mLive & LIVE_MEM)) + { + mIns[i + 1].mLive |= LIVE_CPU_REG_C; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_LDY && mIns[i + 0].mMode == ASMIM_IMMEDIATE && mIns[i + 0].mAddress <= 1 && + mIns[i + 1].mType == ASMIT_LDA && + mIns[i + 2].mType == ASMIT_STA && mIns[i + 2].mMode == ASMIM_INDIRECT_Y && !(mIns[i + 2].mLive & LIVE_MEM)) + { + int apos, breg, ireg; + if (FindAddressSumY(i, mIns[i + 2].mAddress, apos, breg, ireg)) + { + int yoffset = mIns[i + 0].mAddress; + + if (breg == mIns[i + 2].mAddress || ireg == mIns[i + 2].mAddress) + { + mIns[apos + 3].mType = ASMIT_NOP; mIns[apos + 3].mMode = ASMIM_IMPLIED; + mIns[apos + 6].mType = ASMIT_NOP; mIns[apos + 6].mMode = ASMIM_IMPLIED; + } + + RepairLoadYImmediate(i + 2, yoffset); + + int ypos = i; + if (mIns[i + 1].mMode != ASMIM_INDIRECT_Y && mIns[i + 1].mMode != ASMIM_ABSOLUTE_Y) + { + mIns[i + 0].mMode = ASMIM_ZERO_PAGE; + mIns[i + 0].mAddress = ireg; + mIns[i + 0].mLive |= LIVE_MEM; + mIns[i + 2].mAddress = breg; + } + else + { + mIns.Insert(i + 2, NativeCodeInstruction(mIns[i + 0].mIns, ASMIT_LDY, ASMIM_ZERO_PAGE, ireg)); + mIns[i + 2].mLive = mIns[i + 1].mLive | LIVE_CPU_REG_Y | LIVE_MEM; + ypos = i + 2; + mIns[i + 3].mAddress = breg; + } + + if (yoffset == 1) + { + mIns.Insert(ypos + 1, NativeCodeInstruction(mIns[i + 0].mIns, ASMIT_INY, ASMIM_IMPLIED)); + mIns[ypos + 1].mLive = mIns[ypos].mLive; + } + + CheckLive(); + + return true; + } + } + + if (pass > 12 && + mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_ABSOLUTE_X && + mIns[i + 1].mType == ASMIT_TAX && + mIns[i + 2].mType == ASMIT_LDA && mIns[i + 2].mMode == ASMIM_ABSOLUTE_X && !(mIns[i + 2].mLive & (LIVE_CPU_REG_X | LIVE_CPU_REG_Y))) + { + mIns[i + 0].mType = ASMIT_LDY; mIns[i + 0].mLive |= LIVE_CPU_REG_Y; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mMode = ASMIM_ABSOLUTE_Y; + return true; + } + + if (pass > 12 && + mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_ABSOLUTE_Y && + mIns[i + 1].mType == ASMIT_TAY && + mIns[i + 2].mType == ASMIT_LDA && mIns[i + 2].mMode == ASMIM_ABSOLUTE_Y && !(mIns[i + 2].mLive & (LIVE_CPU_REG_X | LIVE_CPU_REG_Y))) + { + mIns[i + 0].mType = ASMIT_LDX; mIns[i + 0].mLive |= LIVE_CPU_REG_X; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mMode = ASMIM_ABSOLUTE_X; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_LDY && mIns[i + 0].mMode == ASMIM_IMMEDIATE && + mIns[i + 1].mType == ASMIT_LDA && + mIns[i + 2].mType == ASMIT_STA && mIns[i + 2].mMode == ASMIM_INDIRECT_Y && !(mIns[i + 2].mLive & LIVE_CPU_REG_X)) + { + const NativeCodeInstruction* ains; + if (FindImmediateStore(i, mIns[i + 2].mAddress + 1, ains)) + { + mIns.Insert(i + 2, NativeCodeInstruction(mIns[i + 0].mIns, ASMIT_LDX, ASMIM_ZERO_PAGE, mIns[i + 2].mAddress)); + mIns[i + 2].mLive = mIns[i + 1].mLive | LIVE_CPU_REG_X; + mIns[i + 3].mMode = ASMIM_ABSOLUTE_X; + + if (ains->mMode == ASMIM_IMMEDIATE) + mIns[i + 3].mAddress = (ains->mAddress << 8) + mIns[i + 0].mAddress; + else + { + mIns[i + 3].mLinkerObject = ains->mLinkerObject; + mIns[i + 3].mAddress = mIns[i + 0].mAddress + ains->mAddress; + mIns[i + 3].mFlags |= NCIF_UPPER; + mIns[i + 3].mFlags &= ~NCIF_LOWER; + } + + CheckLive(); + + return true; + } + } + + if (mIns[i + 0].mType == ASMIT_TXA && mIns[i + 1].mType == ASMIT_CLC && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && + !(mIns[i + 2].mLive & LIVE_CPU_REG_A)) + { + mIns[i + 0].mType = ASMIT_NOP; + mIns[i + 1].mType = ASMIT_NOP; + mIns[i + 2].mType = ASMIT_CPX; mIns[i + 2].mAddress = (256 - mIns[i + 2].mAddress) & 255; + + return true; + } + + if (mIns[i].mType == ASMIT_LDA && mIns[i].mMode == ASMIM_ABSOLUTE_X && mIns[i].mLinkerObject && mIns[i].mAddress > 0 && mIns[i].mLinkerObject->mSize < 256 && + mIns[i + 1].mType == ASMIT_INX && + mIns[i + 2].mType == ASMIT_ORA && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 0) + { + mIns.Insert(i, NativeCodeInstruction(mIns[i + 1].mIns, ASMIT_INX)); + mIns[i + 1].mAddress--; + mIns.Remove(i + 2); + return true; + } + if (mIns[i].mType == ASMIT_LDA && mIns[i].mMode == ASMIM_ABSOLUTE_Y && mIns[i].mLinkerObject && mIns[i].mAddress > 0 && mIns[i].mLinkerObject->mSize < 256 && + mIns[i + 1].mType == ASMIT_INY && + mIns[i + 2].mType == ASMIT_ORA && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 0) + { + mIns.Insert(i, NativeCodeInstruction(mIns[i + 1].mIns, ASMIT_INY)); + mIns[i + 1].mAddress--; + mIns.Remove(i + 2); + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_LDX && + mIns[i + 1].mType == ASMIT_STX && + mIns[i + 2].mType == ASMIT_STX && !(mIns[i + 2].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_X))) + { + mIns[i + 0].mType = ASMIT_LDA; mIns[i + 0].mLive |= LIVE_CPU_REG_A; + mIns[i + 1].mType = ASMIT_STA; mIns[i + 1].mLive |= LIVE_CPU_REG_A; + mIns[i + 2].mType = ASMIT_STA; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_LDY && + mIns[i + 1].mType == ASMIT_STY && + mIns[i + 2].mType == ASMIT_STY && !(mIns[i + 2].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Y))) + { + mIns[i + 0].mType = ASMIT_LDA; mIns[i + 0].mLive |= LIVE_CPU_REG_A; + mIns[i + 1].mType = ASMIT_STA; mIns[i + 1].mLive |= LIVE_CPU_REG_A; + mIns[i + 2].mType = ASMIT_STA; + return true; + } + + if ( + mIns[i + 0].ChangesAccuAndFlag() && !mIns[i + 0].ReferencesXReg() && + mIns[i + 1].mType == ASMIT_LDX && + mIns[i + 2].mType == ASMIT_ORA && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 0) + { + mIns[i + 2] = mIns[i + 0]; + mIns[i + 2].mLive |= LIVE_CPU_REG_X | LIVE_CPU_REG_Z; + if (mIns[i + 2].ReferencesYReg()) mIns[i + 1].mLive |= LIVE_CPU_REG_Y; + if (mIns[i + 2].ReferencesCarry()) mIns[i + 1].mLive |= LIVE_CPU_REG_C; + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + mIns[i + 0].ChangesAccuAndFlag() && !mIns[i + 0].ReferencesYReg() && + mIns[i + 1].mType == ASMIT_LDY && + mIns[i + 2].mType == ASMIT_ORA && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 0) + { + mIns[i + 2] = mIns[i + 0]; + mIns[i + 2].mLive |= LIVE_CPU_REG_Y | LIVE_CPU_REG_Z; + if (mIns[i + 2].ReferencesXReg()) mIns[i + 1].mLive |= LIVE_CPU_REG_X; + if (mIns[i + 2].ReferencesCarry()) mIns[i + 1].mLive |= LIVE_CPU_REG_C; + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + mIns[i + 0].IsShift() && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + mIns[i + 2].mType == ASMIT_CMP && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && mIns[i + 2].mAddress == mIns[i + 0].mAddress && + !(mIns[i + 2].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C | LIVE_MEM))) + { + mIns[i + 2].mAddress = mIns[i + 1].mAddress; mIns[i + 2].mLive |= LIVE_MEM; + mIns[i + 1].mType = mIns[i + 0].mType; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 0].mType = ASMIT_LDA; mIns[i + 0].mLive |= LIVE_CPU_REG_A; + return true; + } + +#if 1 + if (pass == 4 && + mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_IMMEDIATE && mIns[i + 0].mAddress == 0 && + mIns[i + 1].mType == ASMIT_ROL && mIns[i + 1].mMode == ASMIM_IMPLIED && + mIns[i + 2].mType == ASMIT_STA && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && + !(mIns[i + 2].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C))) + { + mProc->ResetPatched(); + if (CheckBoolBitPropagation(this, i + 3, mIns[i + 2].mAddress)) + { + mIns[i + 1].mType = ASMIT_ROR; + mProc->ResetPatched(); + if (PatchBoolBitPropagation(this, i + 3, mIns[i + 2].mAddress, false)) + return true; + } + } +#endif + + return false; +} + +bool NativeCodeBasicBlock::PeepHoleOptimizerIterate4(int i, int pass) +{ + if (mIns[i + 0].mType == ASMIT_LDA && mIns[i + 3].mType == ASMIT_STA && mIns[i + 0].SameEffectiveAddress(mIns[i + 3]) && + mIns[i + 1].mType == ASMIT_CLC && mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 1 && + HasAsmInstructionMode(ASMIT_INC, mIns[i + 0].mMode) && + (mIns[i + 3].mLive & LIVE_CPU_REG_C) == 0) + { + mIns[i + 0].mType = ASMIT_INC; mIns[i + 0].mLive |= LIVE_MEM; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + mIns[i + 3].mType = ASMIT_LDA; mIns[i + 3].mLive |= LIVE_CPU_REG_A | LIVE_CPU_REG_Z; + return true; + } + + if (mIns[i + 0].mType == ASMIT_LDA && mIns[i + 3].mType == ASMIT_STA && mIns[i + 0].SameEffectiveAddress(mIns[i + 3]) && + mIns[i + 1].mType == ASMIT_SEC && mIns[i + 2].mType == ASMIT_SBC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 1 && + HasAsmInstructionMode(ASMIT_DEC, mIns[i + 0].mMode) && + (mIns[i + 3].mLive & LIVE_CPU_REG_C) == 0) + { + mIns[i + 0].mType = ASMIT_DEC; mIns[i + 0].mLive |= LIVE_MEM; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + mIns[i + 3].mType = ASMIT_LDA; mIns[i + 3].mLive |= LIVE_CPU_REG_A | LIVE_CPU_REG_Z; + return true; + } + if (mIns[i + 1].mType == ASMIT_LDA && mIns[i + 3].mType == ASMIT_STA && mIns[i + 1].SameEffectiveAddress(mIns[i + 3]) && + mIns[i + 0].mType == ASMIT_CLC && mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 1 && + HasAsmInstructionMode(ASMIT_INC, mIns[i + 1].mMode) && + (mIns[i + 3].mLive & LIVE_CPU_REG_C) == 0) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_INC; mIns[i + 1].mLive |= LIVE_MEM; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + mIns[i + 3].mType = ASMIT_LDA; mIns[i + 3].mLive |= LIVE_CPU_REG_A | LIVE_CPU_REG_Z; + return true; + } + if (mIns[i + 1].mType == ASMIT_LDA && mIns[i + 3].mType == ASMIT_STA && mIns[i + 1].SameEffectiveAddress(mIns[i + 3]) && + mIns[i + 0].mType == ASMIT_SEC && mIns[i + 2].mType == ASMIT_SBC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 1 && + HasAsmInstructionMode(ASMIT_DEC, mIns[i + 1].mMode) && + (mIns[i + 3].mLive & LIVE_CPU_REG_C) == 0) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_DEC; mIns[i + 1].mLive |= LIVE_MEM; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + mIns[i + 3].mType = ASMIT_LDA; mIns[i + 3].mLive |= LIVE_CPU_REG_A | LIVE_CPU_REG_Z; + return true; + } + if (mIns[i + 0].mType == ASMIT_LDA && mIns[i + 3].mType == ASMIT_STA && mIns[i + 0].SameEffectiveAddress(mIns[i + 3]) && + mIns[i + 1].mType == ASMIT_CLC && mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && (mIns[i + 2].mAddress & 0xff) == 0xff && + HasAsmInstructionMode(ASMIT_DEC, mIns[i + 0].mMode) && + (mIns[i + 3].mLive & LIVE_CPU_REG_C) == 0) + { + mIns[i + 0].mType = ASMIT_DEC; mIns[i + 0].mLive |= LIVE_MEM; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + mIns[i + 3].mType = ASMIT_LDA; mIns[i + 3].mLive |= LIVE_CPU_REG_A | LIVE_CPU_REG_Z; + return true; + } + if (mIns[i + 0].mType == ASMIT_LDA && mIns[i + 3].mType == ASMIT_STA && mIns[i + 0].SameEffectiveAddress(mIns[i + 3]) && + mIns[i + 1].mType == ASMIT_SEC && mIns[i + 2].mType == ASMIT_SBC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && (mIns[i + 2].mAddress & 0xff) == 0xff && + HasAsmInstructionMode(ASMIT_INC, mIns[i + 0].mMode) && + (mIns[i + 3].mLive & LIVE_CPU_REG_C) == 0) + { + mIns[i + 0].mType = ASMIT_INC; mIns[i + 0].mLive |= LIVE_MEM; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + mIns[i + 3].mType = ASMIT_LDA; mIns[i + 3].mLive |= LIVE_CPU_REG_A | LIVE_CPU_REG_Z; + return true; + } + if (mIns[i + 1].mType == ASMIT_LDA && mIns[i + 3].mType == ASMIT_STA && mIns[i + 1].SameEffectiveAddress(mIns[i + 3]) && + mIns[i + 0].mType == ASMIT_CLC && mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && (mIns[i + 2].mAddress & 0xff) == 0xff && + HasAsmInstructionMode(ASMIT_DEC, mIns[i + 1].mMode) && + (mIns[i + 3].mLive & LIVE_CPU_REG_C) == 0) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_DEC; mIns[i + 1].mLive |= LIVE_MEM; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + mIns[i + 3].mType = ASMIT_LDA; mIns[i + 3].mLive |= LIVE_CPU_REG_A | LIVE_CPU_REG_Z; + return true; + } + if (mIns[i + 1].mType == ASMIT_LDA && mIns[i + 3].mType == ASMIT_STA && mIns[i + 1].SameEffectiveAddress(mIns[i + 3]) && + mIns[i + 0].mType == ASMIT_SEC && mIns[i + 2].mType == ASMIT_SBC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && (mIns[i + 2].mAddress & 0xff) == 0xff && + HasAsmInstructionMode(ASMIT_INC, mIns[i + 1].mMode) && + (mIns[i + 3].mLive & LIVE_CPU_REG_C) == 0) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_INC; mIns[i + 1].mLive |= LIVE_MEM; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + mIns[i + 3].mType = ASMIT_LDA; mIns[i + 3].mLive |= LIVE_CPU_REG_A | LIVE_CPU_REG_Z; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_IMMEDIATE && mIns[i + 0].mAddress == 0 && + mIns[i + 1].mType == ASMIT_ROL && mIns[i + 1].mMode == ASMIM_IMPLIED && + mIns[i + 2].mType == ASMIT_CLC && + mIns[i + 3].mType == ASMIT_ADC) + { + mIns[i + 1].mType = ASMIT_NOP; + mIns[i + 2].mType = ASMIT_NOP; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_ADC && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 1 && + mIns[i + 2].mType == ASMIT_STA && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && + mIns[i + 3].mType == ASMIT_TAX && !(mIns[i + 3].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C))) + { + mIns[i + 0].mType = ASMIT_TAX; mIns[i + 0].mLive |= LIVE_CPU_REG_X; + mIns[i + 1].mType = ASMIT_INX; mIns[i + 1].mMode = ASMIM_IMPLIED; mIns[i + 1].mLive |= LIVE_CPU_REG_X; + mIns[i + 2].mType = ASMIT_STX; mIns[i + 2].mLive |= LIVE_CPU_REG_X; + mIns[i + 3].mType = ASMIT_NOP; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_LDA && + mIns[i + 1].mType == ASMIT_STA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + mIns[i + 2].mType == ASMIT_LDA && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && mIns[i + 2].mAddress != mIns[i + 1].mAddress && + mIns[i + 3].mType == ASMIT_CMP && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && mIns[i + 3].mAddress == mIns[i + 1].mAddress && !(mIns[i + 3].mLive & LIVE_MEM)) + { + mIns[i + 3].CopyMode(mIns[i + 0]); + if (mIns[i + 3].RequiresYReg()) + { + mIns[i + 0].mLive |= LIVE_CPU_REG_Y; + mIns[i + 1].mLive |= LIVE_CPU_REG_Y; + mIns[i + 2].mLive |= LIVE_CPU_REG_Y; + } + else if (mIns[i + 3].RequiresXReg()) + { + mIns[i + 0].mLive |= LIVE_CPU_REG_X; + mIns[i + 1].mLive |= LIVE_CPU_REG_X; + mIns[i + 2].mLive |= LIVE_CPU_REG_X; + } + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_LDA && + mIns[i + 1].IsShift() && mIns[i + 1].mMode == ASMIM_IMPLIED && + mIns[i + 2].mType == mIns[i + 1].mType && mIns[i + 2].SameEffectiveAddress(mIns[i + 0]) && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].SameEffectiveAddress(mIns[i + 0])) + { + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + mIns[i + 1].mLive |= LIVE_CPU_REG_C; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_STA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 2].mType == ASMIT_LDX && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && mIns[i + 2].mAddress == mIns[i + 0].mAddress && + mIns[i + 3].mType == ASMIT_STX && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && + !mIns[i + 1].ChangesZeroPage(mIns[i + 0].mAddress) && + !mIns[i + 1].UsesZeroPage(mIns[i + 3].mAddress) && + !mIns[i + 1].ChangesZeroPage(mIns[i + 3].mAddress)) + { + mIns[i + 0].mLive |= LIVE_CPU_REG_A; + + mIns.Insert(i + 1, NativeCodeInstruction(mIns[i + 0].mIns, ASMIT_STA, ASMIM_ZERO_PAGE, mIns[i + 3].mAddress)); + + mIns[i + 4].mType = ASMIT_NOP; mIns[i + 4].mMode = ASMIM_IMPLIED; + + return true; + } + if ( + mIns[i + 0].mType == ASMIT_STA && + mIns[i + 1].mType == ASMIT_LDA && HasAsmInstructionMode(ASMIT_LDX, mIns[i + 1].mMode) && + mIns[i + 2].mType == ASMIT_STA && HasAsmInstructionMode(ASMIT_STX, mIns[i + 2].mMode) && + mIns[i + 3].mType == ASMIT_LDX && mIns[i + 3].SameEffectiveAddress(mIns[i + 0]) && !(mIns[i + 3].mLive & LIVE_CPU_REG_A)) + { + mIns[i + 0].mLive |= LIVE_CPU_REG_A; + mIns[i + 1].mType = ASMIT_LDX; mIns[i + 1].mLive |= LIVE_CPU_REG_X; + mIns[i + 2].mType = ASMIT_STX; mIns[i + 2].mLive |= LIVE_CPU_REG_A; + mIns[i + 3].mType = ASMIT_TAX; mIns[i + 3].mMode = ASMIM_IMPLIED; + + return true; + } + if ( + mIns[i + 0].mType == ASMIT_STA && + mIns[i + 1].mType == ASMIT_LDA && HasAsmInstructionMode(ASMIT_LDY, mIns[i + 1].mMode) && + mIns[i + 2].mType == ASMIT_STA && HasAsmInstructionMode(ASMIT_STY, mIns[i + 2].mMode) && + mIns[i + 3].mType == ASMIT_LDY && mIns[i + 3].SameEffectiveAddress(mIns[i + 0]) && !(mIns[i + 3].mLive & LIVE_CPU_REG_A)) + { + mIns[i + 0].mLive |= LIVE_CPU_REG_A; + mIns[i + 1].mType = ASMIT_LDY; mIns[i + 1].mLive |= LIVE_CPU_REG_Y; + mIns[i + 2].mType = ASMIT_STY; mIns[i + 2].mLive |= LIVE_CPU_REG_A; + mIns[i + 3].mType = ASMIT_TAY; mIns[i + 3].mMode = ASMIM_IMPLIED; + + return true; + } +#if 1 + if ( + mIns[i + 0].mType == ASMIT_LDY && mIns[i + 0].mMode == ASMIM_ABSOLUTE_X && + mIns[i + 1].mType == ASMIT_TAX && + mIns[i + 2].mType == ASMIT_TYA && + mIns[i + 3].IsCommutative() && mIns[i + 3].mMode == ASMIM_ABSOLUTE_X && !(mIns[i + 3].mLive & (LIVE_CPU_REG_X | LIVE_CPU_REG_Y))) + { + mIns[i + 2] = mIns[i + 0]; + mIns[i + 1].mType = ASMIT_TAY; mIns[i + 1].mLive |= LIVE_CPU_REG_Y; + mIns[i + 2].mType = ASMIT_LDA; + mIns[i + 3].mMode = ASMIM_ABSOLUTE_Y; + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + + return true; + } +#endif +#if 1 + if ( + mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_LDY && mIns[i + 1].mMode == ASMIM_IMMEDIATE && + mIns[i + 2].mType == ASMIT_STY && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && + mIns[i + 3].mType == ASMIT_TAY && !(mIns[i + 3].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z))) + { + mIns[i + 0].mType = ASMIT_LDY; mIns[i + 0].mLive |= LIVE_CPU_REG_Y; + mIns[i + 1].mType = ASMIT_LDA; mIns[i + 1].mLive |= LIVE_CPU_REG_Y; + mIns[i + 2].mType = ASMIT_STA; mIns[i + 2].mLive |= LIVE_CPU_REG_Y; + mIns[i + 3].mType = ASMIT_NOP; mIns[i + 3].mMode = ASMIM_IMPLIED; + + return true; + } +#endif + if ( + mIns[i + 0].mType == ASMIT_STA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_LDY && mIns[i + 1].mMode == ASMIM_IMMEDIATE && + mIns[i + 2].mType == ASMIT_LDA && mIns[i + 2].mMode == ASMIM_INDIRECT_Y && + mIns[i + 3].mType == ASMIT_EOR && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && mIns[i + 3].mAddress == mIns[i + 0].mAddress) + { + mIns[i + 2].mType = mIns[i + 3].mType; + mIns[i + 3].mType = ASMIT_NOP; mIns[i + 3].mMode = ASMIM_IMPLIED; + + return true; + } + +#if 1 + if ( + mIns[i + 0].mType == ASMIT_STA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + mIns[i + 2].mType == ASMIT_LSR && mIns[i + 2].mMode == ASMIM_IMPLIED && + mIns[i + 3].mType == ASMIT_EOR && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && + mIns[i + 3].mAddress == mIns[i + 0].mAddress && !(mIns[i + 3].mLive & LIVE_MEM)) + { + mIns[i + 0].mType = ASMIT_ASL; mIns[i + 0].mMode = ASMIM_IMPLIED; mIns[i + 0].mLive |= LIVE_CPU_REG_A | LIVE_CPU_REG_C; + mIns[i + 1].mType = ASMIT_EOR; mIns[i + 1].mLive |= LIVE_CPU_REG_C; + mIns[i + 2].mType = ASMIT_ROR; + mIns[i + 3].mType = ASMIT_NOP; mIns[i + 3].mMode = ASMIM_IMPLIED; + + return true; + } +#endif + + if ( + mIns[i + 0].mType == ASMIT_LDA && + mIns[i + 1].mType == ASMIT_STA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && !mIns[i + 0].MayBeChangedOnAddress(mIns[i + 1]) && + mIns[i + 2].mType == ASMIT_LDA && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && + mIns[i + 3].mType == ASMIT_ORA && mIns[i + 3].SameEffectiveAddress(mIns[i + 0]) && mIns[i + 3].mMode != ASMIM_IMMEDIATE) + { + mIns[i + 1].mLive |= LIVE_CPU_REG_A; + mIns[i + 2].mType = ASMIT_ORA; mIns[i + 2].mLive |= mIns[i + 3].mLive & LIVE_CPU_REG_Z; + mIns[i + 3].mType = ASMIT_NOP; mIns[i + 3].mMode = ASMIM_IMPLIED; + + return true; + } + if ( + mIns[i + 0].mType == ASMIT_LDA && + mIns[i + 1].mType == ASMIT_EOR && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 0xff && + (mIns[i + 2].mType == ASMIT_SEC || mIns[i + 2].mType == ASMIT_CLC) && + mIns[i + 3].mType == ASMIT_ADC) + { + mIns.Insert(i + 4, mIns[i + 0]); + + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 3].mType = ASMIT_LDA; mIns[i + 3].mLive |= LIVE_CPU_REG_C; + mIns[i + 4].mType = ASMIT_SBC; mIns[i + 4].mLive |= LIVE_CPU_REG_C; + + if (mIns[i + 4].RequiresYReg()) + { + mIns[i + 2].mLive |= LIVE_CPU_REG_Y; + mIns[i + 3].mLive |= LIVE_CPU_REG_Y; + } + if (mIns[i + 4].RequiresXReg()) + { + mIns[i + 2].mLive |= LIVE_CPU_REG_X; + mIns[i + 3].mLive |= LIVE_CPU_REG_X; + } + + return true; + } + if ( + mIns[i + 0].mType == ASMIT_STA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && mIns[i + 0].mAddress != mIns[i + 1].mAddress && + mIns[i + 2].mType == ASMIT_SEC && + mIns[i + 3].mType == ASMIT_SBC && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && mIns[i + 0].mAddress == mIns[i + 3].mAddress && !(mIns[i + 3].mLive & LIVE_MEM)) + { + mIns[i + 0].mType = ASMIT_EOR; + mIns[i + 0].mMode = ASMIM_IMMEDIATE; + mIns[i + 0].mAddress = 0xff; + mIns[i + 0].mLive |= LIVE_CPU_REG_A; + + mIns[i + 3].mType = ASMIT_ADC; + mIns[i + 3].mAddress = mIns[i + 1].mAddress; + + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + + return true; + } + if ( + mIns[i + 0].mType == ASMIT_STA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_SEC && + mIns[i + 2].mType == ASMIT_LDA && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && mIns[i + 0].mAddress != mIns[i + 2].mAddress && + mIns[i + 3].mType == ASMIT_SBC && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && mIns[i + 0].mAddress == mIns[i + 3].mAddress && !(mIns[i + 3].mLive & LIVE_MEM)) + { + mIns[i + 0].mType = ASMIT_EOR; + mIns[i + 0].mMode = ASMIM_IMMEDIATE; + mIns[i + 0].mAddress = 0xff; + mIns[i + 0].mLive |= LIVE_CPU_REG_A; + mIns[i + 1].mLive |= LIVE_CPU_REG_A; + + mIns[i + 3].mType = ASMIT_ADC; + mIns[i + 3].mAddress = mIns[i + 2].mAddress; + + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + + return true; + } + if ( + mIns[i + 0].mType == ASMIT_STA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_LDA && (mIns[i + 1].mMode == ASMIM_ZERO_PAGE && mIns[i + 0].mAddress != mIns[i + 2].mAddress || mIns[i + 1].mMode == ASMIM_ABSOLUTE) && + mIns[i + 2].mType == ASMIT_CLC && + mIns[i + 3].mType == ASMIT_ADC && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && mIns[i + 0].mAddress == mIns[i + 3].mAddress && !(mIns[i + 3].mLive & LIVE_MEM)) + { + mIns[i + 3].CopyMode(mIns[i + 1]); + mIns[i + 3].mType = ASMIT_ADC; + mIns[i + 2].mLive |= LIVE_CPU_REG_A; + + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + + return true; + } + if ( + mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_IMMEDIATE && + mIns[i + 1].mType == ASMIT_ADC && + mIns[i + 2].mType == ASMIT_CLC && + mIns[i + 3].mType == ASMIT_ADC && mIns[i + 3].mMode == ASMIM_IMMEDIATE && !(mIns[i + 3].mLive & LIVE_CPU_REG_C)) + { + mIns[i + 0].mAddress += mIns[i + 3].mAddress; + mIns[i + 1].mLive |= (mIns[i + 3].mLive & LIVE_CPU_REG_Z); + + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + mIns[i + 3].mType = ASMIT_NOP; mIns[i + 3].mMode = ASMIM_IMPLIED; + + return true; + } + if ( + mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_IMMEDIATE && mIns[i + 0].mAddress == 0x00 && + mIns[i + 1].mType == ASMIT_ADC && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 0xff && + mIns[i + 2].mType == ASMIT_AND && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 0x01 && + mIns[i + 3].mType == ASMIT_EOR && mIns[i + 3].mMode == ASMIM_IMMEDIATE && mIns[i + 3].mAddress == 0x01) + { + mIns[i + 1].mAddress = 0x00; + mIns[i + 1].mLive |= (mIns[i + 3].mLive & LIVE_CPU_REG_Z); + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + mIns[i + 3].mType = ASMIT_NOP; mIns[i + 3].mMode = ASMIM_IMPLIED; + + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_ADC && mIns[i + 0].mMode == ASMIM_IMMEDIATE && + mIns[i + 1].mType == ASMIT_EOR && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 0xff && + mIns[i + 2].mType == ASMIT_CLC && + mIns[i + 3].mType == ASMIT_ADC && mIns[i + 3].mMode == ASMIM_IMMEDIATE && !(mIns[i + 3].mLive & LIVE_CPU_REG_C)) + { + mIns[i + 0].mAddress = (mIns[i + 0].mAddress - mIns[i + 3].mAddress) & 0xff; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + mIns[i + 3].mType = ASMIT_NOP; mIns[i + 3].mMode = ASMIM_IMPLIED; + + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_LDA && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 0].SameEffectiveAddress(mIns[i + 3]) && + (mIns[i + 0].mMode == ASMIM_ABSOLUTE || mIns[i + 0].mMode == ASMIM_ZERO_PAGE) && + mIns[i + 1].mType == ASMIT_ASL && mIns[i + 1].mMode == ASMIM_IMPLIED && + mIns[i + 2].mType == ASMIT_ORA && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 1 && !(mIns[i + 3].mLive & LIVE_CPU_REG_A)) + + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_SEC; + mIns[i + 2].mMode = ASMIM_IMPLIED; + mIns[i + 2].mLive |= LIVE_CPU_REG_C; + mIns[i + 3].mType = ASMIT_ROL; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_LDA && + mIns[i + 1].mType == ASMIT_STA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + mIns[i + 3].mMode == ASMIM_ZERO_PAGE && mIns[i + 1].mAddress == mIns[i + 3].mAddress && + mIns[i + 2].mMode == ASMIM_IMPLIED && !(mIns[i + 3].mLive & LIVE_MEM) && + (mIns[i + 2].mType == ASMIT_ASL || mIns[i + 2].mType == ASMIT_LSR || mIns[i + 2].mType == ASMIT_ROL || mIns[i + 2].mType == ASMIT_ROR) && + (mIns[i + 3].mType == ASMIT_ORA || mIns[i + 3].mType == ASMIT_AND || mIns[i + 3].mType == ASMIT_EOR || mIns[i + 3].mType == ASMIT_ADC || mIns[i + 3].mType == ASMIT_SBC)) + { + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 3].mMode = mIns[i + 0].mMode; + mIns[i + 3].mAddress = mIns[i + 0].mAddress; + mIns[i + 3].mLinkerObject = mIns[i + 0].mLinkerObject; + mIns[i + 3].mFlags = mIns[i + 0].mFlags; + + if (mIns[i + 3].mMode == ASMIM_INDIRECT_Y) + { + mIns[i + 0].mLive |= LIVE_MEM; + mIns[i + 3].mLive |= LIVE_MEM; + } + if (mIns[i + 3].RequiresYReg()) + { + mIns[i + 0].mLive |= LIVE_CPU_REG_Y; + mIns[i + 2].mLive |= LIVE_CPU_REG_Y; + } + if (mIns[i + 3].RequiresXReg()) + { + mIns[i + 0].mLive |= LIVE_CPU_REG_X; + mIns[i + 2].mLive |= LIVE_CPU_REG_X; + } + return true; + } + if ( + mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_IMMEDIATE && mIns[i + 0].mAddress == 0 && + mIns[i + 1].mType == ASMIT_ADC && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 0xff && + mIns[i + 2].mType == ASMIT_EOR && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 0xff && + mIns[i + 3].mType == ASMIT_LSR && mIns[i + 3].mMode == ASMIM_IMPLIED && !(mIns[i + 3].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z))) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + mIns[i + 3].mType = ASMIT_NOP; mIns[i + 3].mMode = ASMIM_IMPLIED; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_LDA && HasAsmInstructionMode(ASMIT_LDY, mIns[i + 1].mMode) && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress <= 2 && + mIns[i + 3].mType == ASMIT_TAY && !(mIns[i + 3].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z | LIVE_CPU_REG_C))) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_LDY; mIns[i + 1].mLive |= LIVE_CPU_REG_Y; + mIns[i + 2].mType = ASMIT_INY; mIns[i + 2].mMode = ASMIM_IMPLIED; mIns[i + 2].mLive |= LIVE_CPU_REG_Y; + if (mIns[i + 2].mAddress == 2) + mIns[i + 3].mType = ASMIT_INY; + else + mIns[i + 3].mType = ASMIT_NOP; + mIns[i + 3].mMode = ASMIM_IMPLIED; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_LDA && HasAsmInstructionMode(ASMIT_LDY, mIns[i + 1].mMode) && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress >= 0xfe && + mIns[i + 3].mType == ASMIT_TAY && !(mIns[i + 3].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z | LIVE_CPU_REG_C))) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_LDY; mIns[i + 1].mLive |= LIVE_CPU_REG_Y; + mIns[i + 2].mType = ASMIT_DEY; mIns[i + 2].mMode = ASMIM_IMPLIED; mIns[i + 2].mLive |= LIVE_CPU_REG_Y; + if (mIns[i + 2].mAddress == 0xfe) + mIns[i + 3].mType = ASMIT_DEY; + else + mIns[i + 3].mType = ASMIT_NOP; + mIns[i + 3].mMode = ASMIM_IMPLIED; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_SEC && + mIns[i + 1].mType == ASMIT_LDA && HasAsmInstructionMode(ASMIT_LDY, mIns[i + 1].mMode) && + mIns[i + 2].mType == ASMIT_SBC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress <= 2 && + mIns[i + 3].mType == ASMIT_TAY && !(mIns[i + 3].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z | LIVE_CPU_REG_C))) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_LDY; mIns[i + 1].mLive |= LIVE_CPU_REG_Y; + mIns[i + 2].mType = ASMIT_DEY; mIns[i + 2].mMode = ASMIM_IMPLIED; mIns[i + 2].mLive |= LIVE_CPU_REG_Y; + if (mIns[i + 2].mAddress == 2) + mIns[i + 3].mType = ASMIT_DEY; + else + mIns[i + 3].mType = ASMIT_NOP; + mIns[i + 3].mMode = ASMIM_IMPLIED; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_LDA && HasAsmInstructionMode(ASMIT_LDX, mIns[i + 1].mMode) && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress <= 2 && + mIns[i + 3].mType == ASMIT_TAX && !(mIns[i + 3].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z | LIVE_CPU_REG_C))) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_LDX; mIns[i + 1].mLive |= LIVE_CPU_REG_X; + mIns[i + 2].mType = ASMIT_INX; mIns[i + 2].mMode = ASMIM_IMPLIED; mIns[i + 2].mLive |= LIVE_CPU_REG_X; + if (mIns[i + 2].mAddress == 2) + mIns[i + 3].mType = ASMIT_INX; + else + mIns[i + 3].mType = ASMIT_NOP; + mIns[i + 3].mMode = ASMIM_IMPLIED; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_LDA && HasAsmInstructionMode(ASMIT_LDX, mIns[i + 1].mMode) && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress >= 0xfe && + mIns[i + 3].mType == ASMIT_TAX && !(mIns[i + 3].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z | LIVE_CPU_REG_C))) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_LDX; mIns[i + 1].mLive |= LIVE_CPU_REG_X; + mIns[i + 2].mType = ASMIT_DEX; mIns[i + 2].mMode = ASMIM_IMPLIED; mIns[i + 2].mLive |= LIVE_CPU_REG_X; + if (mIns[i + 2].mAddress == 0xfe) + mIns[i + 3].mType = ASMIT_DEX; + else + mIns[i + 3].mType = ASMIT_NOP; + mIns[i + 3].mMode = ASMIM_IMPLIED; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_SEC && + mIns[i + 1].mType == ASMIT_LDA && HasAsmInstructionMode(ASMIT_LDX, mIns[i + 1].mMode) && + mIns[i + 2].mType == ASMIT_SBC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress <= 2 && + mIns[i + 3].mType == ASMIT_TAX && !(mIns[i + 3].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z | LIVE_CPU_REG_C))) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_LDX; mIns[i + 1].mLive |= LIVE_CPU_REG_X; + mIns[i + 2].mType = ASMIT_DEX; mIns[i + 2].mMode = ASMIM_IMPLIED; mIns[i + 2].mLive |= LIVE_CPU_REG_X; + if (mIns[i + 2].mAddress == 2) + mIns[i + 3].mType = ASMIT_DEX; + else + mIns[i + 3].mType = ASMIT_NOP; + mIns[i + 3].mMode = ASMIM_IMPLIED; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_TXA && + mIns[i + 1].mType == ASMIT_CLC && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress <= 2 && + mIns[i + 3].mType == ASMIT_TAX && !(mIns[i + 3].mLive & LIVE_CPU_REG_C)) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_INX; mIns[i + 1].mMode = ASMIM_IMPLIED; mIns[i + 1].mLive |= LIVE_CPU_REG_X | LIVE_CPU_REG_Z; + if (mIns[i + 2].mAddress == 2) + mIns[i + 2].mType = ASMIT_INX; + else + mIns[i + 2].mType = ASMIT_NOP; + mIns[i + 2].mMode = ASMIM_IMPLIED; mIns[i + 2].mLive |= LIVE_CPU_REG_X; + if (mIns[i + 3].mLive & LIVE_CPU_REG_A) + mIns[i + 3].mType = ASMIT_TXA; + else + mIns[i + 3].mType = ASMIT_NOP; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_TXA && + mIns[i + 1].mType == ASMIT_CLC && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress >= 0xfe && + mIns[i + 3].mType == ASMIT_TAX && !(mIns[i + 3].mLive & LIVE_CPU_REG_C)) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_DEX; mIns[i + 1].mMode = ASMIM_IMPLIED; mIns[i + 1].mLive |= LIVE_CPU_REG_X | LIVE_CPU_REG_Z; + if (mIns[i + 2].mAddress == 0xfe) + mIns[i + 2].mType = ASMIT_DEX; + else + mIns[i + 2].mType = ASMIT_NOP; + mIns[i + 2].mMode = ASMIM_IMPLIED; mIns[i + 2].mLive |= LIVE_CPU_REG_X; + if (mIns[i + 3].mLive & LIVE_CPU_REG_A) + mIns[i + 3].mType = ASMIT_TXA; + else + mIns[i + 3].mType = ASMIT_NOP; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_TXA && + mIns[i + 1].mType == ASMIT_SEC && + mIns[i + 2].mType == ASMIT_SBC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress <= 3 && + mIns[i + 3].mType == ASMIT_TAX && !(mIns[i + 3].mLive & LIVE_CPU_REG_C)) + { + if (mIns[i + 2].mAddress == 3) + mIns[i + 0].mType = ASMIT_DEX; + else + mIns[i + 0].mType = ASMIT_NOP; + mIns[i + 0].mMode = ASMIM_IMPLIED; mIns[i + 0].mLive |= LIVE_CPU_REG_X | LIVE_CPU_REG_Z; + mIns[i + 1].mType = ASMIT_DEX; mIns[i + 1].mMode = ASMIM_IMPLIED; mIns[i + 1].mLive |= LIVE_CPU_REG_X | LIVE_CPU_REG_Z; + if (mIns[i + 2].mAddress >= 2) + mIns[i + 2].mType = ASMIT_DEX; + else + mIns[i + 2].mType = ASMIT_NOP; + mIns[i + 2].mMode = ASMIM_IMPLIED; mIns[i + 2].mLive |= LIVE_CPU_REG_X; + if (mIns[i + 3].mLive & LIVE_CPU_REG_A) + mIns[i + 3].mType = ASMIT_TXA; + else + mIns[i + 3].mType = ASMIT_NOP; + + return true; + } + if ( + mIns[i + 0].mType == ASMIT_TYA && + mIns[i + 1].mType == ASMIT_CLC && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress <= 2 && + mIns[i + 3].mType == ASMIT_TAY && !(mIns[i + 3].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C))) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_INY; mIns[i + 2].mMode = ASMIM_IMPLIED; mIns[i + 2].mLive |= LIVE_CPU_REG_Y | LIVE_CPU_REG_Z; + if (mIns[i + 2].mAddress == 2) + mIns[i + 3].mType = ASMIT_INY; + else + mIns[i + 3].mType = ASMIT_NOP; + mIns[i + 3].mMode = ASMIM_IMPLIED; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_TYA && + mIns[i + 1].mType == ASMIT_CLC && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress >= 0xfe && + mIns[i + 3].mType == ASMIT_TAY && !(mIns[i + 3].mLive & LIVE_CPU_REG_C)) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_DEY; mIns[i + 1].mMode = ASMIM_IMPLIED; mIns[i + 1].mLive |= LIVE_CPU_REG_Y | LIVE_CPU_REG_Z; + if (mIns[i + 2].mAddress == 0xfe) + mIns[i + 2].mType = ASMIT_DEY; + else + mIns[i + 2].mType = ASMIT_NOP; + mIns[i + 2].mMode = ASMIM_IMPLIED; mIns[i + 2].mLive |= LIVE_CPU_REG_Y; + if (mIns[i + 3].mLive & LIVE_CPU_REG_A) + mIns[i + 3].mType = ASMIT_TYA; + else + mIns[i + 3].mType = ASMIT_NOP; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_TYA && + mIns[i + 1].mType == ASMIT_SEC && + mIns[i + 2].mType == ASMIT_SBC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress <= 2 && + mIns[i + 3].mType == ASMIT_TAY && !(mIns[i + 3].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C))) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_DEY; mIns[i + 2].mMode = ASMIM_IMPLIED; mIns[i + 2].mLive |= LIVE_CPU_REG_Y | LIVE_CPU_REG_Z; + if (mIns[i + 2].mAddress == 2) + mIns[i + 3].mType = ASMIT_DEY; + else + mIns[i + 3].mType = ASMIT_NOP; + mIns[i + 3].mMode = ASMIM_IMPLIED; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_TXA && + mIns[i + 1].mType == ASMIT_CLC && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress <= 2 && + mIns[i + 3].mType == ASMIT_STA && (mIns[i + 3].mMode == ASMIM_ZERO_PAGE || mIns[i + 3].mMode == ASMIM_ABSOLUTE) && + !(mIns[i + 3].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_X | LIVE_CPU_REG_C))) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_INX; mIns[i + 1].mMode = ASMIM_IMPLIED; mIns[i + 1].mLive |= LIVE_CPU_REG_X | LIVE_CPU_REG_Z; + if (mIns[i + 2].mAddress == 2) + mIns[i + 2].mType = ASMIT_INX; + else + mIns[i + 2].mType = ASMIT_NOP; + mIns[i + 2].mMode = ASMIM_IMPLIED; mIns[i + 2].mLive |= LIVE_CPU_REG_X; + mIns[i + 3].mType = ASMIT_STX; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_TXA && + mIns[i + 1].mType == ASMIT_SEC && + mIns[i + 2].mType == ASMIT_SBC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress <= 2 && + mIns[i + 3].mType == ASMIT_STA && (mIns[i + 3].mMode == ASMIM_ZERO_PAGE || mIns[i + 3].mMode == ASMIM_ABSOLUTE) && + !(mIns[i + 3].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_X | LIVE_CPU_REG_C))) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_DEX; mIns[i + 1].mMode = ASMIM_IMPLIED; mIns[i + 1].mLive |= LIVE_CPU_REG_X | LIVE_CPU_REG_Z; + if (mIns[i + 2].mAddress == 2) + mIns[i + 2].mType = ASMIT_DEX; + else + mIns[i + 2].mType = ASMIT_NOP; + mIns[i + 2].mMode = ASMIM_IMPLIED; mIns[i + 2].mLive |= LIVE_CPU_REG_X; + mIns[i + 3].mType = ASMIT_STX; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_TYA && + mIns[i + 1].mType == ASMIT_CLC && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress <= 2 && + mIns[i + 3].mType == ASMIT_STA && (mIns[i + 3].mMode == ASMIM_ZERO_PAGE || mIns[i + 3].mMode == ASMIM_ABSOLUTE) && + !(mIns[i + 3].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Y | LIVE_CPU_REG_C))) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_INY; mIns[i + 1].mMode = ASMIM_IMPLIED; mIns[i + 1].mLive |= LIVE_CPU_REG_Y | LIVE_CPU_REG_Z; + if (mIns[i + 2].mAddress == 2) + { + mIns[i + 2].mType = ASMIT_INY; mIns[i + 2].mLive |= LIVE_CPU_REG_Y; + } + else + mIns[i + 2].mType = ASMIT_NOP; + mIns[i + 2].mMode = ASMIM_IMPLIED; + mIns[i + 3].mType = ASMIT_STY; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_TYA && + mIns[i + 1].mType == ASMIT_SEC && + mIns[i + 2].mType == ASMIT_SBC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress <= 2 && + mIns[i + 3].mType == ASMIT_STA && (mIns[i + 3].mMode == ASMIM_ZERO_PAGE || mIns[i + 3].mMode == ASMIM_ABSOLUTE) && + !(mIns[i + 3].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Y | LIVE_CPU_REG_C))) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_DEY; mIns[i + 1].mMode = ASMIM_IMPLIED; mIns[i + 1].mLive |= LIVE_CPU_REG_Y | LIVE_CPU_REG_Z; + if (mIns[i + 2].mAddress == 2) + mIns[i + 2].mType = ASMIT_DEY; + else + mIns[i + 2].mType = ASMIT_NOP; + mIns[i + 2].mMode = ASMIM_IMPLIED; mIns[i + 2].mLive |= LIVE_CPU_REG_Y; + mIns[i + 3].mType = ASMIT_STY; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_TYA && + mIns[i + 1].mType == ASMIT_CLC && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress >= 0xfe && + mIns[i + 3].mType == ASMIT_STA && (mIns[i + 3].mMode == ASMIM_ZERO_PAGE || mIns[i + 3].mMode == ASMIM_ABSOLUTE) && + !(mIns[i + 3].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Y | LIVE_CPU_REG_C))) + { + if (mIns[i + 2].mAddress == 0xfe) + mIns[i + 0].mType = ASMIT_DEY; + else + mIns[i + 0].mType = ASMIT_NOP; + mIns[i + 0].mLive |= LIVE_CPU_REG_Y; + + mIns[i + 1].mType = ASMIT_DEY; mIns[i + 1].mMode = ASMIM_IMPLIED; mIns[i + 1].mLive |= LIVE_CPU_REG_Y | LIVE_CPU_REG_Z; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + mIns[i + 3].mType = ASMIT_STY; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_TXA && + mIns[i + 1].mType == ASMIT_CLC && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress >= 0xfe && + mIns[i + 3].mType == ASMIT_STA && (mIns[i + 3].mMode == ASMIM_ZERO_PAGE || mIns[i + 3].mMode == ASMIM_ABSOLUTE) && + !(mIns[i + 3].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_X | LIVE_CPU_REG_C))) + { + if (mIns[i + 2].mAddress == 0xfe) + mIns[i + 0].mType = ASMIT_DEX; + else + mIns[i + 0].mType = ASMIT_NOP; + mIns[i + 0].mLive |= LIVE_CPU_REG_X; + + mIns[i + 1].mType = ASMIT_DEX; mIns[i + 1].mMode = ASMIM_IMPLIED; mIns[i + 1].mLive |= LIVE_CPU_REG_X | LIVE_CPU_REG_Z; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + mIns[i + 3].mType = ASMIT_STX; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_TAX && + mIns[i + 1].mType == ASMIT_CLC && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 0xff && + mIns[i + 3].mType == ASMIT_STA && HasAsmInstructionMode(ASMIT_STX, mIns[i + 3].mMode) && !(mIns[i + 3].mLive & (LIVE_CPU_REG_C | LIVE_CPU_REG_A | LIVE_CPU_REG_Z))) + { + mIns[i + 1].mType = ASMIT_DEX; mIns[i + 1].mMode = ASMIM_IMPLIED; mIns[i + 1].mLive |= LIVE_CPU_REG_X; + mIns[i + 2] = mIns[i + 3]; mIns[i + 2].mType = ASMIT_STX; mIns[i + 1].mLive |= LIVE_CPU_REG_X; + mIns[i + 3].mType = ASMIT_INX; mIns[i + 3].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_TAX && + mIns[i + 1].mType == ASMIT_CLC && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 0x01 && + mIns[i + 3].mType == ASMIT_STA && HasAsmInstructionMode(ASMIT_STX, mIns[i + 3].mMode) && !(mIns[i + 3].mLive & (LIVE_CPU_REG_C | LIVE_CPU_REG_A | LIVE_CPU_REG_Z))) + { + mIns[i + 1].mType = ASMIT_INX; mIns[i + 1].mMode = ASMIM_IMPLIED; mIns[i + 1].mLive |= LIVE_CPU_REG_X; + mIns[i + 2] = mIns[i + 3]; mIns[i + 2].mType = ASMIT_STX; mIns[i + 1].mLive |= LIVE_CPU_REG_X; + mIns[i + 3].mType = ASMIT_DEX; mIns[i + 3].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_TXA && + mIns[i + 1].mType == ASMIT_CLC && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 0xff && + mIns[i + 3].mType == ASMIT_STA && HasAsmInstructionMode(ASMIT_STX, mIns[i + 3].mMode) && !(mIns[i + 3].mLive & (LIVE_CPU_REG_C | LIVE_CPU_REG_A | LIVE_CPU_REG_Z))) + { + mIns[i + 0].mType = ASMIT_NOP; + mIns[i + 1].mType = ASMIT_DEX; mIns[i + 1].mMode = ASMIM_IMPLIED; mIns[i + 1].mLive |= LIVE_CPU_REG_X; + mIns[i + 2] = mIns[i + 3]; mIns[i + 2].mType = ASMIT_STX; mIns[i + 1].mLive |= LIVE_CPU_REG_X; + mIns[i + 3].mType = ASMIT_INX; mIns[i + 3].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_TXA && + mIns[i + 1].mType == ASMIT_CLC && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 0x01 && + mIns[i + 3].mType == ASMIT_STA && HasAsmInstructionMode(ASMIT_STX, mIns[i + 3].mMode) && !(mIns[i + 3].mLive & (LIVE_CPU_REG_C | LIVE_CPU_REG_A | LIVE_CPU_REG_Z))) + { + mIns[i + 0].mType = ASMIT_NOP; + mIns[i + 1].mType = ASMIT_INX; mIns[i + 1].mMode = ASMIM_IMPLIED; mIns[i + 1].mLive |= LIVE_CPU_REG_X; + mIns[i + 2] = mIns[i + 3]; mIns[i + 2].mType = ASMIT_STX; mIns[i + 1].mLive |= LIVE_CPU_REG_X; + mIns[i + 3].mType = ASMIT_DEX; mIns[i + 3].mMode = ASMIM_IMPLIED; + return true; + } + + + if ( + mIns[i + 0].mType == ASMIT_TAX && + mIns[i + 1].mType == ASMIT_LDA && HasAsmInstructionMode(ASMIT_LDX, mIns[i + 1].mMode) && + mIns[i + 2].mType == ASMIT_STA && HasAsmInstructionMode(ASMIT_STX, mIns[i + 2].mMode) && + mIns[i + 3].mType == ASMIT_TXA && !(mIns[i + 3].mLive & (LIVE_CPU_REG_X | LIVE_CPU_REG_Z))) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_LDX; + mIns[i + 2].mType = ASMIT_STX; mIns[i + 2].mLive |= LIVE_CPU_REG_A; + mIns[i + 3].mType = ASMIT_NOP; mIns[i + 3].mMode = ASMIM_IMPLIED; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_TAY && + mIns[i + 1].mType == ASMIT_LDA && HasAsmInstructionMode(ASMIT_LDY, mIns[i + 1].mMode) && + mIns[i + 2].mType == ASMIT_STA && HasAsmInstructionMode(ASMIT_STY, mIns[i + 2].mMode) && + mIns[i + 3].mType == ASMIT_TYA && !(mIns[i + 3].mLive & (LIVE_CPU_REG_Y | LIVE_CPU_REG_Z))) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_LDY; + mIns[i + 2].mType = ASMIT_STY; mIns[i + 2].mLive |= LIVE_CPU_REG_A; + mIns[i + 3].mType = ASMIT_NOP; mIns[i + 3].mMode = ASMIM_IMPLIED; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_LDA && HasAsmInstructionMode(ASMIT_LDX, mIns[i + 0].mMode) && + mIns[i + 1].mType == ASMIT_LDX && HasAsmInstructionMode(ASMIT_LDA, mIns[i + 1].mMode) && + mIns[i + 2].mType == ASMIT_STA && HasAsmInstructionMode(ASMIT_STX, mIns[i + 2].mMode) && + mIns[i + 3].mType == ASMIT_TXA && !(mIns[i + 3].mLive & (LIVE_CPU_REG_X | LIVE_CPU_REG_Z))) + { + mIns[i + 0].mType = ASMIT_LDX; mIns[i + 0].mLive |= LIVE_CPU_REG_X; + mIns[i + 1].mType = ASMIT_LDA; mIns[i + 1].mLive |= LIVE_CPU_REG_A; + mIns[i + 2].mType = ASMIT_STX; mIns[i + 2].mLive |= LIVE_CPU_REG_A; + mIns[i + 3].mType = ASMIT_NOP; mIns[i + 3].mMode = ASMIM_IMPLIED; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].IsShift() && mIns[i + 1].mMode == ASMIM_IMPLIED && + mIns[i + 2].mType == ASMIT_STA && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && mIns[i + 2].mAddress == mIns[i + 0].mAddress && + mIns[i + 3].mType == ASMIT_ORA && mIns[i + 3].mMode == ASMIM_IMMEDIATE && mIns[i + 3].mAddress == 0x00 && !(mIns[i + 3].mLive & LIVE_CPU_REG_A)) + { + mIns[i + 0].mType = mIns[i + 1].mType; + mIns[i + 0].mLive |= LIVE_MEM | LIVE_CPU_REG_C | LIVE_CPU_REG_Z; + + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + mIns[i + 3].mType = ASMIT_NOP; mIns[i + 3].mMode = ASMIM_IMPLIED; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_LDX && + mIns[i + 1].mType == ASMIT_INX && mIns[i + 2].mType == ASMIT_INX && + mIns[i + 3].mType == ASMIT_STX && mIns[i + 3].SameEffectiveAddress(mIns[i + 0]) && !(mIns[i + 3].mLive & LIVE_CPU_REG_X)) + { + mIns[i + 0].mType = ASMIT_INC; + mIns[i + 3].mType = ASMIT_INC; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + + return true; + } + if ( + mIns[i + 0].mType == ASMIT_LDX && + mIns[i + 1].mType == ASMIT_DEX && mIns[i + 2].mType == ASMIT_DEX && + mIns[i + 3].mType == ASMIT_STX && mIns[i + 3].SameEffectiveAddress(mIns[i + 0]) && !(mIns[i + 3].mLive & LIVE_CPU_REG_X)) + { + mIns[i + 0].mType = ASMIT_DEC; + mIns[i + 3].mType = ASMIT_DEC; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + + return true; + } + if (pass > 10 && + mIns[i + 0].mType == ASMIT_LDA && HasAsmInstructionMode(ASMIT_INC, mIns[i + 0].mMode) && + mIns[i + 1].mType == ASMIT_CLC && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 2 && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].SameEffectiveAddress(mIns[i + 0]) && !(mIns[i + 3].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C))) + { + mIns[i + 0].mType = ASMIT_INC; + mIns[i + 3].mType = ASMIT_INC; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + + return true; + } + if (pass > 10 && + mIns[i + 0].mType == ASMIT_LDA && HasAsmInstructionMode(ASMIT_DEC, mIns[i + 0].mMode) && + mIns[i + 1].mType == ASMIT_SEC && + mIns[i + 2].mType == ASMIT_SBC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 2 && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].SameEffectiveAddress(mIns[i + 0]) && !(mIns[i + 3].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C))) + { + mIns[i + 0].mType = ASMIT_DEC; + mIns[i + 3].mType = ASMIT_DEC; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + + return true; + } + if ( + mIns[i + 0].mType == ASMIT_LDY && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + !(mIns[i + 1].ChangesYReg() || mIns[i + 1].mMode == ASMIM_INDIRECT_Y || mIns[i + 1].RequiresXReg()) && + mIns[i + 2].mType == ASMIT_TYA && + mIns[i + 3].mType == ASMIT_TAX && !(mIns[i + 3].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z | LIVE_CPU_REG_Y))) + { + mIns[i + 0].mType = ASMIT_LDX; + mIns[i + 0].mLive |= LIVE_CPU_REG_X; + mIns[i + 1].ReplaceYRegWithXReg(); + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + mIns[i + 3].mType = ASMIT_NOP; mIns[i + 3].mMode = ASMIM_IMPLIED; + return true; + } + if ( + mIns[i + 0].IsShift() && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_CLC && + mIns[i + 2].mType == ASMIT_LDA && (mIns[i + 2].mMode == ASMIM_IMMEDIATE || mIns[i + 2].mMode == ASMIM_IMMEDIATE_ADDRESS || mIns[i + 2].mMode == ASMIM_ZERO_PAGE && mIns[i + 2].mAddress != mIns[i + 0].mAddress) && + mIns[i + 3].mType == ASMIT_ADC && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && mIns[i + 3].mAddress == mIns[i + 0].mAddress && + !(mIns[i + 3].mLive & LIVE_MEM)) + { + mIns[i + 3] = mIns[i + 2]; + mIns[i + 2] = mIns[i + 1]; + mIns[i + 1].mType = mIns[i + 0].mType; + mIns[i + 0].mType = ASMIT_LDA; + mIns[i + 3].mType = ASMIT_ADC; + + mIns[i + 0].mLive |= LIVE_CPU_REG_A | LIVE_CPU_REG_C; + mIns[i + 1].mLive |= LIVE_CPU_REG_A; + mIns[i + 2].mLive |= LIVE_CPU_REG_A; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_STA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_SEC && + mIns[i + 2].mType == ASMIT_LDA && !mIns[i + 2].SameEffectiveAddress(mIns[i + 0]) && + mIns[i + 3].mType == ASMIT_SBC && mIns[i + 3].SameEffectiveAddress(mIns[i + 0]) && !(mIns[i + 3].mLive & LIVE_MEM)) + { + mIns[i + 0].mType = ASMIT_EOR; + mIns[i + 0].mMode = ASMIM_IMMEDIATE; + mIns[i + 0].mAddress = 0xff; + mIns[i + 0].mLive |= LIVE_CPU_REG_A; + mIns[i + 1].mLive |= LIVE_CPU_REG_A; + mIns[i + 2].mType = ASMIT_ADC; + mIns[i + 3].mType = ASMIT_NOP; mIns[i + 3].mMode = ASMIM_IMPLIED; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_ADC && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress < 4 && + mIns[i + 2].mType == ASMIT_TAX && + mIns[i + 3].mMode == ASMIM_ABSOLUTE_X && !(mIns[i + 3].mLive & (LIVE_CPU_REG_X | LIVE_CPU_REG_C | LIVE_CPU_REG_A))) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 3].mAddress += mIns[i + 1].mAddress; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_ADC && + mIns[i + 2].mType == ASMIT_CLC && + mIns[i + 3].mType == ASMIT_ADC && mIns[i + 3].mMode == ASMIM_IMMEDIATE && mIns[i + 3].mAddress == 1 && !(mIns[i + 3].mLive & LIVE_CPU_REG_C)) + { + mIns[i + 0].mType = ASMIT_SEC; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + mIns[i + 3].mType = ASMIT_NOP; mIns[i + 3].mMode = ASMIM_IMPLIED; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_SEC && + mIns[i + 1].mType == ASMIT_SBC && + mIns[i + 2].mType == ASMIT_SEC && + mIns[i + 3].mType == ASMIT_SBC && mIns[i + 3].mMode == ASMIM_IMMEDIATE && mIns[i + 3].mAddress == 1 && !(mIns[i + 3].mLive & LIVE_CPU_REG_C)) + { + mIns[i + 0].mType = ASMIT_CLC; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + mIns[i + 3].mType = ASMIT_NOP; mIns[i + 3].mMode = ASMIM_IMPLIED; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_SEC && + mIns[i + 1].mType == ASMIT_SBC && mIns[i + 1].mMode == ASMIM_IMMEDIATE && + mIns[i + 2].mType == ASMIT_CLC && + mIns[i + 3].mType == ASMIT_ADC && mIns[i + 3].mMode == ASMIM_IMMEDIATE && !(mIns[i + 3].mLive & LIVE_CPU_REG_C)) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + + mIns[i + 3].mAddress = (mIns[i + 3].mAddress - mIns[i + 1].mAddress) & 0xff; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && + mIns[i + 3].mType == ASMIT_EOR && mIns[i + 3].mMode == ASMIM_IMMEDIATE && mIns[i + 3].mAddress == 0xff && !(mIns[i + 3].mLive & LIVE_CPU_REG_C)) + { + int val = mIns[i + 2].mAddress; + + mIns[i + 0].mType = ASMIT_SEC; + mIns[i + 2].CopyMode(mIns[i + 1]); + mIns[i + 2].mType = ASMIT_SBC; + mIns[i + 1].mMode = ASMIM_IMMEDIATE; + mIns[i + 1].mAddress = -(1 + val) & 255; + + mIns[i + 3].mType = ASMIT_NOP; mIns[i + 3].mMode = ASMIM_IMPLIED; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_LSR && mIns[i + 0].mMode == ASMIM_IMPLIED && + mIns[i + 1].mType == ASMIT_EOR && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 0x40 && + mIns[i + 2].mType == ASMIT_SEC && + mIns[i + 3].mType == ASMIT_SBC && mIns[i + 3].mMode == ASMIM_IMMEDIATE && mIns[i + 3].mAddress == 0x40 && !(mIns[i + 3].mLive & LIVE_CPU_REG_C)) + { + mIns[i + 0].mType = ASMIT_CMP; + mIns[i + 0].mMode = ASMIM_IMMEDIATE; + mIns[i + 0].mAddress = 0x80; + mIns[i + 0].mLive |= LIVE_CPU_REG_C; + mIns[i + 1].mType = ASMIT_ROR; + mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 1].mLive |= mIns[i + 3].mLive; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + mIns[i + 3].mType = ASMIT_NOP; mIns[i + 3].mMode = ASMIM_IMPLIED; + + return true; + } + if ( + mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_IMMEDIATE && mIns[i + 0].mAddress == 0 && + mIns[i + 1].mType == ASMIT_SBC && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 0 && + mIns[i + 2].mType == ASMIT_CLC && + mIns[i + 3].mType == ASMIT_ADC) + { + mIns[i + 0].mAddress = 0xff; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_LDX && mIns[i + 0].mMode == ASMIM_ABSOLUTE_Y && + mIns[i + 1].mType == ASMIT_LDY && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + mIns[i + 2].mType == ASMIT_STA && mIns[i + 2].mMode == ASMIM_ABSOLUTE_Y && mIns[i + 0].mLinkerObject != mIns[i + 2].mLinkerObject && + mIns[i + 3].mType == ASMIT_TXA && !(mIns[i + 3].mLive & (LIVE_CPU_REG_X | LIVE_CPU_REG_Y | LIVE_CPU_REG_Z))) + { + int val = mIns[i + 2].mAddress; + + mIns[i + 3].mType = ASMIT_LDA; + mIns[i + 3].CopyMode(mIns[i + 0]); + + mIns[i + 1].mType = ASMIT_LDX; mIns[i + 1].mLive |= LIVE_CPU_REG_X | LIVE_CPU_REG_Y; + mIns[i + 2].mMode = ASMIM_ABSOLUTE_X; mIns[i + 2].mLive |= LIVE_CPU_REG_Y; + + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + + return true; + } + if ( + mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_STA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + mIns[i + 2].mType == ASMIT_LDA && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && mIns[i + 2].mAddress != mIns[i + 1].mAddress && + mIns[i + 3].mType == ASMIT_LDX && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && mIns[i + 3].mAddress == mIns[i + 0].mAddress) + { + int addr = mIns[i + 0].mAddress; + mIns[i + 0].mAddress = mIns[i + 2].mAddress; + mIns[i + 3].mAddress = mIns[i + 1].mAddress; + mIns[i + 2].mAddress = addr; + + mIns[i + 2].mType = ASMIT_LDX; mIns[i + 2].mLive |= LIVE_CPU_REG_X; + mIns[i + 3].mType = ASMIT_STX; + + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + + return true; + } + if ( + mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_STA && + mIns[i + 2].mType == ASMIT_LDA && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && !mIns[i + 1].SameEffectiveAddress(mIns[i + 2]) && + mIns[i + 3].mType == ASMIT_CMP && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && mIns[i + 3].mAddress == mIns[i + 0].mAddress && !(mIns[i + 3].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C))) + { + mIns[i + 3].mAddress = mIns[i + 2].mAddress; + mIns[i + 1].mLive |= LIVE_CPU_REG_A; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + + return true; + } + if ( + mIns[i + 0].mType == ASMIT_LDA && + mIns[i + 1].mType == ASMIT_ASL && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + mIns[i + 2].mType == ASMIT_ROL && mIns[i + 2].mMode == ASMIM_IMPLIED && HasAsmInstructionMode(ASMIT_ROL, mIns[i + 0].mMode) && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].SameEffectiveAddress(mIns[i + 0]) && !(mIns[i + 3].mLive & LIVE_CPU_REG_A)) + { + + mIns[i + 2].CopyMode(mIns[i + 0]); + + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 3].mType = ASMIT_NOP; mIns[i + 3].mMode = ASMIM_IMPLIED; + + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_ROL && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_ASL && mIns[i + 1].mMode == ASMIM_IMPLIED && + mIns[i + 2].mType == ASMIT_LDA && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && mIns[i + 0].mAddress == mIns[i + 2].mAddress && !(mIns[i + 2].mLive & LIVE_MEM) && + mIns[i + 3].mType == ASMIT_AND && mIns[i + 3].mMode == ASMIM_IMMEDIATE && mIns[i + 3].mAddress == 1) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_ROL; mIns[i + 1].mLive |= LIVE_CPU_REG_A; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_STA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 0 && + mIns[i + 2].mType == ASMIT_ROL && mIns[i + 2].mMode == ASMIM_IMPLIED && + mIns[i + 3].mType == ASMIT_ASL && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && mIns[i + 3].mAddress == mIns[i + 0].mAddress && !(mIns[i + 3].mLive & LIVE_MEM)) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_AND; mIns[i + 1].mAddress = 0x80; + mIns[i + 3].mType = ASMIT_NOP; mIns[i + 3].mMode = ASMIM_IMPLIED; + + return true; + } + +#if 1 + if ( + mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_IMMEDIATE && mIns[i + 0].mAddress == 0 && + mIns[i + 1].mType == ASMIT_ROL && mIns[i + 1].mMode == ASMIM_IMPLIED && + mIns[i + 2].mType == ASMIT_ASL && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && + mIns[i + 3].mType == ASMIT_ORA && mIns[i + 3].SameEffectiveAddress(mIns[i + 2]) && !(mIns[i + 3].mLive & LIVE_MEM)) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + + mIns[i + 2].mType = ASMIT_LDA; mIns[i + 2].mLive |= LIVE_CPU_REG_A | LIVE_CPU_REG_C; + mIns[i + 3].mType = ASMIT_ROL; mIns[i + 3].mMode = ASMIM_IMPLIED; mIns[i + 3].mLive |= LIVE_CPU_REG_A | LIVE_CPU_REG_C; + + return true; + } +#endif + + if (pass >= 6 && + mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].IsShift() && mIns[i + 1].mMode == ASMIM_IMPLIED && + mIns[i + 2].IsShift() && mIns[i + 2].mMode == ASMIM_IMPLIED && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].SameEffectiveAddress(mIns[i + 0]) && !(mIns[i + 3].mLive & LIVE_CPU_REG_A)) + { + + mIns[i + 1].CopyMode(mIns[i + 0]); + mIns[i + 2].CopyMode(mIns[i + 0]); + + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 3].mType = ASMIT_NOP; mIns[i + 3].mMode = ASMIM_IMPLIED; + + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_STA && !(mIns[i + 0].mLive & LIVE_CPU_REG_A) && + !mIns[i + 1].ReferencesAccu() && !mIns[i + 0].MayBeSameAddress(mIns[i + 1]) && + !mIns[i + 2].ReferencesAccu() && !mIns[i + 0].MayBeSameAddress(mIns[i + 2]) && + mIns[i + 3].IsShift() && mIns[i + 3].SameEffectiveAddress(mIns[i + 0])) + { + NativeCodeInstruction ins = mIns[i + 0]; + mIns[i + 0] = mIns[i + 1]; mIns[i + 0].mLive |= LIVE_CPU_REG_A; + mIns[i + 1] = mIns[i + 2]; mIns[i + 1].mLive |= LIVE_CPU_REG_A; + mIns[i + 2] = mIns[i + 3]; mIns[i + 2].mMode = ASMIM_IMPLIED; mIns[i + 2].mLive |= LIVE_CPU_REG_A; + mIns[i + 3] = ins; mIns[i + 3].mLive |= mIns[i + 2].mLive; + + return true; + } + if ( + mIns[i + 0].IsShift() && (mIns[i + 0].mMode == ASMIM_ZERO_PAGE || mIns[i + 0].mMode == ASMIM_ABSOLUTE) && + mIns[i + 3].mType == ASMIT_LDA && mIns[i + 3].SameEffectiveAddress(mIns[i + 0]) && !(mIns[i + 3].mLive & LIVE_MEM) && + !mIns[i + 1].ChangesCarry() && !mIns[i + 2].ChangesCarry() && + !mIns[i + 1].RequiresCarry() && !mIns[i + 2].RequiresCarry() && + !mIns[i + 1].MayBeSameAddress(mIns[i + 0]) && !mIns[i + 2].MayBeSameAddress(mIns[i + 0])) + { + mIns.Insert(i + 4, NativeCodeInstruction(mIns[i + 0].mIns, mIns[i + 0].mType, ASMIM_IMPLIED)); + mIns[i + 1].mLive |= LIVE_CPU_REG_C; + mIns[i + 2].mLive |= LIVE_CPU_REG_C; + mIns[i + 3].mLive |= LIVE_CPU_REG_C; + + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_STX && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_LDX && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + mIns[i + 2].mType == ASMIT_LDA && mIns[i + 2].mMode == ASMIM_ABSOLUTE_X && + mIns[i + 3].mType == ASMIT_LDX && mIns[i + 3].SameEffectiveAddress(mIns[i + 0]) && !(mIns[i + 3].mLive & (LIVE_CPU_REG_Y | LIVE_CPU_REG_Z))) + { + mIns[i + 0].mLive |= LIVE_CPU_REG_X; + mIns[i + 1].mType = ASMIT_LDY; mIns[i + 1].mLive |= LIVE_CPU_REG_Y; + mIns[i + 2].mMode = ASMIM_ABSOLUTE_Y; mIns[i + 2].mLive |= LIVE_CPU_REG_X; + + if (!(mIns[i + 3].mLive & LIVE_MEM)) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + } + mIns[i + 3].mType = ASMIT_NOP; mIns[i + 3].mMode = ASMIM_IMPLIED; + + return true; + } + if ( + mIns[i + 0].mType == ASMIT_STY && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_LDY && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + mIns[i + 2].mType == ASMIT_LDA && mIns[i + 2].mMode == ASMIM_ABSOLUTE_Y && + mIns[i + 3].mType == ASMIT_LDY && mIns[i + 3].SameEffectiveAddress(mIns[i + 0]) && !(mIns[i + 3].mLive & (LIVE_CPU_REG_X | LIVE_CPU_REG_Z))) + { + mIns[i + 0].mLive |= LIVE_CPU_REG_Y; + mIns[i + 1].mType = ASMIT_LDX; mIns[i + 1].mLive |= LIVE_CPU_REG_X; + mIns[i + 2].mMode = ASMIM_ABSOLUTE_X; mIns[i + 2].mLive |= LIVE_CPU_REG_Y; + + if (!(mIns[i + 3].mLive & LIVE_MEM)) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + } + mIns[i + 3].mType = ASMIT_NOP; mIns[i + 3].mMode = ASMIM_IMPLIED; + + return true; + } + if ( + mIns[i + 0].mType == ASMIT_STY && + mIns[i + 1].mType == ASMIT_CLC && + mIns[i + 2].mType == ASMIT_LDA && + mIns[i + 3].mType == ASMIT_ADC && mIns[i + 3].SameEffectiveAddress(mIns[i + 0])) + { + mIns[i + 3].CopyMode(mIns[i + 2]); + mIns[i + 2].mType = ASMIT_TYA; + mIns[i + 2].mMode = ASMIM_IMPLIED; + mIns[i + 0].mLive |= LIVE_CPU_REG_Y; + mIns[i + 1].mLive |= LIVE_CPU_REG_Y; + + return true; + } + if ( + mIns[i + 0].mType == ASMIT_STX && + mIns[i + 1].mType == ASMIT_CLC && + mIns[i + 2].mType == ASMIT_LDA && + mIns[i + 3].mType == ASMIT_ADC && mIns[i + 3].SameEffectiveAddress(mIns[i + 0])) + { + mIns[i + 3].CopyMode(mIns[i + 2]); + mIns[i + 2].mType = ASMIT_TXA; + mIns[i + 2].mMode = ASMIM_IMPLIED; + mIns[i + 0].mLive |= LIVE_CPU_REG_X; + mIns[i + 1].mLive |= LIVE_CPU_REG_X; + + return true; + } + if ( + mIns[i + 0].mType == ASMIT_EOR && mIns[i + 0].mMode == ASMIM_IMMEDIATE && mIns[i + 0].mAddress == 0xff && + mIns[i + 1].mType == ASMIT_CLC && + mIns[i + 2].mType == ASMIT_ADC && + mIns[i + 3].mType == ASMIT_EOR && mIns[i + 3].mMode == ASMIM_IMMEDIATE && mIns[i + 3].mAddress == 0xff && !(mIns[i + 3].mLive & LIVE_CPU_REG_C)) + { + mIns[i + 1].mType = ASMIT_SEC; + mIns[i + 2].mType = ASMIT_SBC; + + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 3].mType = ASMIT_NOP; mIns[i + 3].mMode = ASMIM_IMPLIED; + + return true; + } + if ( + mIns[i + 0].mType == ASMIT_LDX && + mIns[i + 1].mType == ASMIT_DEX && + mIns[i + 2].mType == ASMIT_STX && mIns[i + 2].SameEffectiveAddress(mIns[i + 0]) && HasAsmInstructionMode(ASMIT_DEC, mIns[i + 0].mMode) && + mIns[i + 3].mType == ASMIT_TXA && !(mIns[i + 3].mLive & LIVE_CPU_REG_X)) + { + mIns[i + 0].mType = ASMIT_DEC; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_LDA; mIns[i + 2].mLive = mIns[i + 3].mLive; + mIns[i + 3].mType = ASMIT_NOP; mIns[i + 3].mMode = ASMIM_IMPLIED; + + return true; + } + if ( + mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_IMMEDIATE && + mIns[i + 1].mType == ASMIT_STA && + mIns[i + 2].mType == ASMIT_LDA && + mIns[i + 3].mType == ASMIT_CMP && mIns[i + 3].mMode == ASMIM_IMMEDIATE && mIns[i + 3].mAddress == mIns[i + 0].mAddress && !(mIns[i + 3].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C))) + { + mIns[i + 1].mLive |= LIVE_CPU_REG_A; + mIns[i + 2].mType = ASMIT_CMP; mIns[i + 2].mLive |= LIVE_CPU_REG_Z; + mIns[i + 3].mType = ASMIT_NOP; mIns[i + 3].mMode = ASMIM_IMPLIED; + + return true; + } + if ( + mIns[i + 0].mType == ASMIT_STA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && !(mIns[i + 0].mLive & LIVE_CPU_REG_A) && + !mIns[i + 1].ReferencesZeroPage(mIns[i + 0].mAddress) && !mIns[i + 1].ChangesCarry() && + !mIns[i + 2].ReferencesZeroPage(mIns[i + 0].mAddress) && !mIns[i + 2].ChangesCarry() && + mIns[i + 3].IsShift() && mIns[i + 3].SameEffectiveAddress(mIns[i + 0])) + { + AsmInsType type = mIns[i + 3].mType; + mIns[i + 3] = mIns[i + 2]; + mIns[i + 2] = mIns[i + 1]; + mIns[i + 1] = mIns[i + 0]; + mIns[i + 0] = NativeCodeInstruction(mIns[i + 0].mIns, type); + mIns[i + 1].mLive |= LIVE_CPU_REG_C; + mIns[i + 2].mLive |= LIVE_CPU_REG_C; + mIns[i + 3].mLive |= LIVE_CPU_REG_C; + + return true; + } + if ( + mIns[i + 0].ChangesAccuAndFlag() && + mIns[i + 1].mType == ASMIT_STA && + mIns[i + 2].MayBeMovedBefore(mIns[i + 1]) && mIns[i + 2].MayBeMovedBefore(mIns[i + 0]) && + mIns[i + 3].mType == ASMIT_ORA && mIns[i + 3].mMode == ASMIM_IMMEDIATE && mIns[i + 3].mAddress == 0) + { + NativeCodeInstruction ins(mIns[i + 2]); + mIns.Remove(i + 2); + mIns.Insert(i, ins); + mIns[i + 0].mLive |= LIVE_CPU_REG_A | LIVE_CPU_REG_C | LIVE_CPU_REG_X | LIVE_CPU_REG_Y; + mIns[i + 1].mLive |= mIns[i + 3].mLive; + mIns[i + 2].mLive |= mIns[i + 3].mLive; + mIns[i + 3].mType = ASMIT_NOP; mIns[i + 3].mMode = ASMIM_IMPLIED; + return true; + } + if ( + mIns[i + 0].ChangesAccuAndFlag() && + (mIns[i + 1].mType == ASMIT_LDY && !mIns[i + 0].RequiresYReg() || + mIns[i + 1].mType == ASMIT_LDX && !mIns[i + 0].RequiresXReg()) && + mIns[i + 2].mType == ASMIT_STA && + mIns[i + 3].mType == ASMIT_ORA && mIns[i + 3].mMode == ASMIM_IMMEDIATE && mIns[i + 3].mAddress == 0) + { + NativeCodeInstruction ins(mIns[i + 1]); + mIns.Remove(i + 1); + mIns.Insert(i, ins); + mIns[i + 0].mLive |= LIVE_CPU_REG_A | LIVE_CPU_REG_C | LIVE_CPU_REG_X | LIVE_CPU_REG_Y; + mIns[i + 1].mLive |= mIns[i + 3].mLive | mIns[i + 0].mLive; + mIns[i + 2].mLive |= mIns[i + 3].mLive; + mIns[i + 3].mType = ASMIT_NOP; mIns[i + 3].mMode = ASMIM_IMPLIED; + return true; + } + if ( + mIns[i + 0].mType == ASMIT_STX && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_STA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && mIns[i + 1].mAddress != mIns[i + 0].mAddress && + mIns[i + 2].mType == ASMIT_TXA && + mIns[i + 3].IsCommutative() && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && mIns[i + 3].mAddress == mIns[i + 1].mAddress) + { + mIns[i + 2].mType = ASMIT_NOP; + mIns[i + 1].mLive |= LIVE_CPU_REG_A; + mIns[i + 3].mAddress = mIns[i + 0].mAddress; + return true; + } + + if (mIns[i + 0].mType == ASMIT_LDX && mIns[i + 0].mMode == ASMIM_ABSOLUTE_Y && + mIns[i + 1].mType == ASMIT_TAY && + mIns[i + 2].mType == ASMIT_TXA && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].mMode == ASMIM_ABSOLUTE_Y && !(mIns[i + 3].mLive & (LIVE_CPU_REG_X | LIVE_CPU_REG_Y))) + { + mIns[i + 2] = mIns[i + 0]; mIns[i + 2].mType = ASMIT_LDA; mIns[i + 2].mLive |= LIVE_CPU_REG_A | LIVE_CPU_REG_X; + mIns[i + 1].mType = ASMIT_TAX; + mIns[i + 3].mMode = ASMIM_ABSOLUTE_X; + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + return true; + } + + if (mIns[i + 0].mType == ASMIT_CMP && mIns[i + 0].mMode == ASMIM_IMMEDIATE && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 0 && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 0xff && + mIns[i + 3].mType == ASMIT_LSR && mIns[i + 3].mMode == ASMIM_IMPLIED && !(mIns[i + 3].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z))) + { + int val = mIns[i + 0].mAddress; + mIns[i + 0].mType = ASMIT_EOR; mIns[i + 0].mAddress = 0xff; + mIns[i + 1].mType = ASMIT_CLC; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mAddress = val; + mIns[i + 3].mType = ASMIT_NOP; mIns[i + 3].mMode = ASMIM_IMPLIED; + + return true; + } + + if (mIns[i + 0].mType == ASMIT_STA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + (mIns[i + 1].mType == ASMIT_ASL || mIns[i + 1].mType == ASMIT_LSR) && mIns[i + 1].mMode == ASMIM_IMPLIED && + mIns[i + 2].mType == ASMIT_STA && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && mIns[i + 2].mAddress != mIns[i + 0].mAddress && + mIns[i + 3].mType == mIns[i + 1].mType && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && mIns[i + 3].mAddress == mIns[i + 0].mAddress) + { + mIns[i + 1].mLive |= LIVE_CPU_REG_C; + mIns[i + 2].mLive |= LIVE_CPU_REG_A | LIVE_CPU_REG_C; + mIns[i + 3].mType = ASMIT_STA; + return true; + } + + if (mIns[i + 0].mType == ASMIT_LDY && mIns[i + 2].mType == ASMIT_LDX && mIns[i + 0].SameEffectiveAddress(mIns[i + 2]) && + !mIns[i + 0].MayBeChangedOnAddress(mIns[i + 1]) && !mIns[i + 1].ChangesYReg() && + mIns[i + 1].mMode == ASMIM_ABSOLUTE_Y && mIns[i + 3].mMode == ASMIM_ABSOLUTE_X && !(mIns[i + 2].mLive & LIVE_CPU_REG_Z)) + { + if (!(mIns[i + 3].mLive & LIVE_CPU_REG_Y) && HasAsmInstructionMode(mIns[i + 1].mType, ASMIM_ABSOLUTE_X)) + { + mIns[i + 0].mType = ASMIT_LDX; + mIns[i + 0].mLive |= LIVE_CPU_REG_X; + mIns[i + 1].mLive |= LIVE_CPU_REG_X; + mIns[i + 1].mMode = ASMIM_ABSOLUTE_X; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + return true; + } + else if (!(mIns[i + 3].mLive & LIVE_CPU_REG_X) && HasAsmInstructionMode(mIns[i + 3].mType, ASMIM_ABSOLUTE_Y)) + { + mIns[i + 1].mLive |= LIVE_CPU_REG_Y; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + mIns[i + 3].mMode = ASMIM_ABSOLUTE_Y; + return true; + } + } + + if ( + mIns[i + 0].mType == ASMIT_LDY && mIns[i + 0].mMode == ASMIM_IMMEDIATE && mIns[i + 0].mAddress == 0 && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_INDIRECT_Y && + !mIns[i + 2].ChangesYReg() && (mIns[i + 2].mMode == ASMIM_IMMEDIATE || mIns[i + 2].mMode == ASMIM_ZERO_PAGE && mIns[i + 2].mAddress != mIns[i + 1].mAddress) && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].mMode == ASMIM_INDIRECT_Y && mIns[i + 1].mAddress == mIns[i + 3].mAddress && !(mIns[i + 3].mLive & LIVE_MEM)) + { + int apos, breg, ireg; + if (FindAddressSumY(i, mIns[i + 1].mAddress, apos, breg, ireg)) + { + if (breg == mIns[i + 1].mAddress || ireg == mIns[i + 1].mAddress) + { + mIns[apos + 3].mType = ASMIT_NOP; mIns[apos + 3].mMode = ASMIM_IMPLIED; + mIns[apos + 6].mType = ASMIT_NOP; mIns[apos + 6].mMode = ASMIM_IMPLIED; + } + + RepairLoadYImmediate(i + 3, 0); + + mIns[i + 0].mMode = ASMIM_ZERO_PAGE; + mIns[i + 0].mAddress = ireg; + mIns[i + 0].mLive |= LIVE_MEM; + mIns[i + 1].mAddress = breg; + mIns[i + 3].mAddress = breg; + return true; + } + } + + if (mIns[i + 0].mType == ASMIT_EOR && mIns[i + 0].mMode == ASMIM_IMMEDIATE && mIns[i + 0].mAddress == 0x80 && + mIns[i + 1].mType == ASMIT_CLC && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 0x80 && + (mIns[i + 3].mType == ASMIT_STA || mIns[i + 3].mType == ASMIT_TAY || mIns[i + 3].mType == ASMIT_TAX) && !(mIns[i + 3].mLive & LIVE_CPU_REG_A)) + { + NativeCodeInstruction ins = mIns[i + 3]; + mIns[i + 3] = mIns[i + 2]; + mIns[i + 2] = mIns[i + 1]; + mIns[i + 1] = mIns[i + 0]; + mIns[i + 0] = ins; + return true; + } + + if (mIns[i + 0].mType == ASMIT_AND && mIns[i + 0].mMode == ASMIM_IMMEDIATE && + mIns[i + 1].mType == ASMIT_CMP && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 1 && + mIns[i + 2].mType == ASMIT_LDA && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 0 && + mIns[i + 3].mType == ASMIT_ROR && mIns[i + 3].mMode == ASMIM_IMPLIED) + { + if (mIns[i + 0].mAddress == 0x80) + { + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + mIns[i + 3].mType = ASMIT_NOP; mIns[i + 3].mMode = ASMIM_IMPLIED; + return true; + } + else if (mIns[i + 0].mAddress == 0x40) + { + mIns[i + 1].mType = ASMIT_ASL; mIns[i + 1].mMode = ASMIM_IMPLIED; mIns[i + 1].mLive |= mIns[i + 3].mLive; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + mIns[i + 3].mType = ASMIT_NOP; mIns[i + 3].mMode = ASMIM_IMPLIED; + return true; + } + else if (mIns[i + 0].mAddress == 0x20) + { + mIns[i + 1].mType = ASMIT_ASL; mIns[i + 1].mMode = ASMIM_IMPLIED; mIns[i + 1].mLive |= LIVE_CPU_REG_A; + mIns[i + 2].mType = ASMIT_ASL; mIns[i + 2].mMode = ASMIM_IMPLIED; mIns[i + 2].mLive |= mIns[i + 3].mLive; + mIns[i + 3].mType = ASMIT_NOP; mIns[i + 3].mMode = ASMIM_IMPLIED; + return true; + } + else if (mIns[i + 0].mAddress == 0x01) + { + mIns[i + 1].mType = ASMIT_LSR; mIns[i + 1].mMode = ASMIM_IMPLIED; mIns[i + 1].mLive |= LIVE_CPU_REG_A; + mIns[i + 2].mType = ASMIT_ROR; mIns[i + 2].mMode = ASMIM_IMPLIED; mIns[i + 2].mLive |= mIns[i + 3].mLive; + mIns[i + 3].mType = ASMIT_NOP; mIns[i + 3].mMode = ASMIM_IMPLIED; + return true; + } + } + + if ( + mIns[i + 0].mType == ASMIT_AND && mIns[i + 0].mMode == ASMIM_IMMEDIATE && + mIns[i + 1].mType == ASMIT_ADC && + mIns[i + 2].mType == ASMIT_CLC && + mIns[i + 3].mType == ASMIT_ADC && mIns[i + 3].mMode == ASMIM_IMMEDIATE && + (mIns[i + 0].mAddress & mIns[i + 3].mAddress) == 0 && + !(mIns[i + 3].mLive & LIVE_CPU_REG_C)) + { + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + int mask = mIns[i + 3].mAddress; + mIns[i + 3] = mIns[i + 1]; + mIns[i + 1] = NativeCodeInstruction(mIns[i + 1].mIns, ASMIT_ORA, ASMIM_IMMEDIATE, mask); + return true; + } + + if (pass == 4 && + mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_IMMEDIATE && mIns[i + 0].mAddress == 0 && + mIns[i + 1].mType == ASMIT_ROL && mIns[i + 1].mMode == ASMIM_IMPLIED && + mIns[i + 2].mType == ASMIT_EOR && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 1 && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && + !(mIns[i + 3].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C))) + { + bool flipz = false; + if (mIns[i + 3].mLive & LIVE_CPU_REG_Z) + { + if (i + 4 == mIns.Size() && (mBranch == ASMIT_BEQ || mBranch == ASMIT_BNE) && !mExitRequiredRegs[CPU_REG_Z]) + flipz = true; + } + + if (!(mIns[i + 3].mLive & LIVE_CPU_REG_Z) || flipz) + { + mProc->ResetPatched(); + if (CheckBoolBitPropagation(this, i + 4, mIns[i + 3].mAddress)) + { + mIns[i + 1].mType = ASMIT_ROR; mIns[i + 1].mLive |= mIns[i + 2].mLive; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + if (flipz) + mBranch = InvertBranchCondition(mBranch); + mProc->ResetPatched(); + if (PatchBoolBitPropagation(this, i + 4, mIns[i + 3].mAddress, true)) + return true; + } + } + } + + return false; +} + +bool NativeCodeBasicBlock::PeepHoleOptimizerIterate5(int i, int pass) +{ + if (mIns[i + 0].mType == ASMIT_STA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 3].mType == ASMIT_ADC && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && mIns[i + 0].mAddress == mIns[i + 3].mAddress && + mIns[i + 1].mType == ASMIT_CLC && mIns[i + 2].mType == ASMIT_LDA) + { + // Flip arguments of ADC if second parameter in accu at entry + + mIns[i + 3].CopyMode(mIns[i + 2]); + mIns[i + 2].CopyMode(mIns[i + 0]); + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_STA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 3].mType == ASMIT_SBC && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && mIns[i + 0].mAddress == mIns[i + 3].mAddress && + mIns[i + 1].mType == ASMIT_SEC && mIns[i + 2].mType == ASMIT_LDA) + { + // change sbc to adc to reverse order + + mIns[i + 3].CopyMode(mIns[i + 2]); + mIns[i + 2].mType = ASMIT_EOR; mIns[i + 2].mMode = ASMIM_IMMEDIATE; mIns[i + 2].mAddress = 0xff; + mIns[i + 3].mType = ASMIT_ADC; + + if (mIns[i + 3].ReferencesYReg()) + mIns[i + 2].mLive |= LIVE_CPU_REG_Y; + if (mIns[i + 3].ReferencesXReg()) + mIns[i + 2].mLive |= LIVE_CPU_REG_X; + + mIns[i + 0].mLive |= LIVE_CPU_REG_A; + mIns[i + 1].mLive |= LIVE_CPU_REG_A; + + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_STA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 3].mType == ASMIT_SBC && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && mIns[i + 0].mAddress == mIns[i + 3].mAddress && + mIns[i + 2].mType == ASMIT_SEC && mIns[i + 1].mType == ASMIT_LDA) + { + // change sbc to adc to reverse order + + mIns[i + 3].CopyMode(mIns[i + 1]); + mIns[i + 1].mType = ASMIT_EOR; mIns[i + 1].mMode = ASMIM_IMMEDIATE; mIns[i + 1].mAddress = 0xff; + mIns[i + 3].mType = ASMIT_ADC; + + if (mIns[i + 3].ReferencesYReg()) + { + mIns[i + 1].mLive |= LIVE_CPU_REG_Y; + mIns[i + 2].mLive |= LIVE_CPU_REG_Y; + } + if (mIns[i + 3].ReferencesXReg()) + { + mIns[i + 1].mLive |= LIVE_CPU_REG_X; + mIns[i + 2].mLive |= LIVE_CPU_REG_X; + } + + mIns[i + 0].mLive |= LIVE_CPU_REG_A; + mIns[i + 1].mLive |= LIVE_CPU_REG_A; + + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && (mIns[i + 2].mAddress == 1 || mIns[i + 2].mAddress == 2) && + mIns[i + 3].mType == ASMIT_STA && HasAsmInstructionMode(ASMIT_STY, mIns[i + 3].mMode) && + mIns[i + 4].mType == ASMIT_TAY && !(mIns[i + 4].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C))) + { + mIns[i + 0].mType = ASMIT_NOP; + mIns[i + 1].mType = ASMIT_LDY; mIns[i + 1].mLive |= LIVE_CPU_REG_Y; + mIns[i + 2].mType = ASMIT_INY; mIns[i + 2].mMode = ASMIM_IMPLIED; mIns[i + 2].mLive |= LIVE_CPU_REG_Y; + mIns[i + 3].mType = ASMIT_STY; mIns[i + 3].mLive |= LIVE_CPU_REG_Y; + mIns[i + 4].mType = ASMIT_NOP; + if (mIns[i + 2].mAddress == 2) + { + mIns.Insert(i + 3, mIns[i + 2]); + } + + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && (mIns[i + 2].mAddress == 1 || mIns[i + 2].mAddress == 2) && + mIns[i + 3].mType == ASMIT_CMP && HasAsmInstructionMode(ASMIT_CPY, mIns[i + 3].mMode) && + mIns[i + 4].mType == ASMIT_TAY && !(mIns[i + 4].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z))) + { + mIns[i + 0].mType = ASMIT_NOP; + mIns[i + 1].mType = ASMIT_LDY; mIns[i + 1].mLive |= LIVE_CPU_REG_Y; + mIns[i + 2].mType = ASMIT_INY; mIns[i + 2].mMode = ASMIM_IMPLIED; mIns[i + 2].mLive |= LIVE_CPU_REG_Y; + mIns[i + 3].mType = ASMIT_CPY; mIns[i + 3].mLive |= LIVE_CPU_REG_Y; + mIns[i + 4].mType = ASMIT_NOP; + if (mIns[i + 2].mAddress == 2) + { + mIns.Insert(i + 3, mIns[i + 2]); + } + + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode != ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_STA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + mIns[i + 2].mType == ASMIT_SEC && + mIns[i + 3].mType == ASMIT_LDA && !mIns[i + 3].UsesZeroPage(mIns[i + 1].mAddress) && + mIns[i + 4].mType == ASMIT_SBC && mIns[i + 4].mMode == ASMIM_ZERO_PAGE && mIns[i + 4].mAddress == mIns[i + 1].mAddress && !(mIns[i + 4].mLive & LIVE_MEM)) + { + mIns[i + 4].CopyMode(mIns[i + 0]); + + if (mIns[i + 4].RequiresYReg()) + { + mIns[i + 2].mLive |= LIVE_CPU_REG_Y; + mIns[i + 3].mLive |= LIVE_CPU_REG_Y; + } + if (mIns[i + 4].RequiresXReg()) + { + mIns[i + 2].mLive |= LIVE_CPU_REG_X; + mIns[i + 3].mLive |= LIVE_CPU_REG_X; + } + + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_STA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + !mIns[i + 1].ChangesAccu() && + mIns[i + 2].mType == ASMIT_LDA && + mIns[i + 3].mType == ASMIT_CLC && + mIns[i + 4].mType == ASMIT_ADC && mIns[i + 4].mMode == ASMIM_ZERO_PAGE && mIns[i + 4].mAddress == mIns[i + 0].mAddress) + { + mIns[i + 0].mLive |= LIVE_CPU_REG_A; + mIns[i + 1].mLive |= LIVE_CPU_REG_A; + + mIns[i + 3].mType = ASMIT_ADC; + mIns[i + 3].CopyMode(mIns[i + 2]); + mIns[i + 2].mType = ASMIT_CLC; mIns[i + 2].mMode = ASMIM_IMPLIED; mIns[i + 2].mLive |= LIVE_CPU_REG_C; + mIns[i + 4].mType = ASMIT_NOP; mIns[i + 4].mMode = ASMIM_IMPLIED; + mIns[i + 2].mLive |= mIns[i + 1].mLive; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_STA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 0 && + mIns[i + 2].mType == ASMIT_SBC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 0 && + mIns[i + 3].mType == ASMIT_LSR && mIns[i + 3].mMode == ASMIM_IMPLIED && + mIns[i + 4].mType == ASMIT_ROR && mIns[i + 4].mMode == ASMIM_ZERO_PAGE && mIns[i + 4].mAddress == mIns[i + 0].mAddress && + !(mIns[i + 4].mLive & LIVE_CPU_REG_A)) + { + mIns[i + 0].mType = ASMIT_ROR; mIns[i + 0].mMode = ASMIM_IMPLIED; mIns[i + 0].mLive |= LIVE_CPU_REG_A; + mIns[i + 1].mType = ASMIT_EOR; mIns[i + 1].mAddress = 0x80; mIns[i + 1].mLive |= LIVE_CPU_REG_A | LIVE_CPU_REG_Z; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + mIns[i + 3].mType = ASMIT_NOP; mIns[i + 3].mMode = ASMIM_IMPLIED; + mIns[i + 4].mType = ASMIT_STA; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_SEC && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_IMMEDIATE && + mIns[i + 2].mType == ASMIT_SBC && + mIns[i + 3].mType == ASMIT_CLC && + mIns[i + 4].mType == ASMIT_ADC && mIns[i + 4].mMode == ASMIM_IMMEDIATE && + !(mIns[i + 4].mLive & LIVE_CPU_REG_C)) + { + mIns[i + 1].mAddress = (mIns[i + 1].mAddress + mIns[i + 4].mAddress) & 255; + + mIns[i + 3].mType = ASMIT_NOP; mIns[i + 3].mMode = ASMIM_IMPLIED; + mIns[i + 4].mType = ASMIT_NOP; mIns[i + 4].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_LDY && mIns[i + 0].mMode == ASMIM_IMMEDIATE && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_INDIRECT_Y && + mIns[i + 2].mType == ASMIT_LDY && mIns[i + 2].mMode == ASMIM_IMMEDIATE && + mIns[i + 3].IsCommutative() && mIns[i + 3].mMode == ASMIM_INDIRECT_Y && + mIns[i + 4].mType == ASMIT_LDY && mIns[i + 4].mMode == ASMIM_IMMEDIATE && mIns[i + 4].mAddress == mIns[i + 0].mAddress) + { + int addr = mIns[i + 1].mAddress; + mIns[i + 1].mAddress = mIns[i + 3].mAddress; + mIns[i + 3].mAddress = addr; + mIns[i + 0].mAddress = mIns[i + 2].mAddress; + mIns[i + 2].mAddress = mIns[i + 4].mAddress; + mIns[i + 4].mType = ASMIT_NOP; mIns[i + 4].mMode = ASMIM_IMPLIED; + mIns[i + 3].mLive |= LIVE_CPU_REG_Y; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_LDY && mIns[i + 0].mMode == ASMIM_IMMEDIATE && mIns[i + 0].mAddress == 0 && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_INDIRECT_Y && + !mIns[i + 2].ChangesYReg() && (mIns[i + 2].mMode == ASMIM_IMMEDIATE || mIns[i + 2].mMode == ASMIM_ZERO_PAGE && mIns[i + 2].mAddress != mIns[i + 1].mAddress) && + !mIns[i + 3].ChangesYReg() && (mIns[i + 3].mMode == ASMIM_IMMEDIATE || mIns[i + 3].mMode == ASMIM_ZERO_PAGE && mIns[i + 3].mAddress != mIns[i + 1].mAddress) && + mIns[i + 4].mType == ASMIT_STA && mIns[i + 4].mMode == ASMIM_INDIRECT_Y && mIns[i + 1].mAddress == mIns[i + 4].mAddress && !(mIns[i + 4].mLive & LIVE_MEM)) + { + int apos, breg, ireg; + if (FindAddressSumY(i, mIns[i + 1].mAddress, apos, breg, ireg)) + { + if (breg == mIns[i + 1].mAddress) + { + mIns[apos + 3].mType = ASMIT_NOP; mIns[apos + 3].mMode = ASMIM_IMPLIED; + mIns[apos + 6].mType = ASMIT_NOP; mIns[apos + 6].mMode = ASMIM_IMPLIED; + } + + RepairLoadYImmediate(i + 4, 0); + + mIns[i + 0].mMode = ASMIM_ZERO_PAGE; + mIns[i + 0].mAddress = ireg; + mIns[i + 0].mLive |= LIVE_MEM; + mIns[i + 1].mAddress = breg; + mIns[i + 4].mAddress = breg; + return true; + } + } + + if ( + mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && (mIns[i + 2].mAddress == 1 || mIns[i + 2].mAddress == 2) && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && + mIns[i + 4].mType == ASMIT_STA && mIns[i + 4].mMode == ASMIM_ZERO_PAGE && !(mIns[i + 4].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C))) + { + if (!(mIns[i + 4].mLive & LIVE_CPU_REG_X)) + { + mIns[i + 0].mType = ASMIT_LDX; mIns[i + 0].CopyMode(mIns[i + 1]); mIns[i + 0].mLive |= LIVE_CPU_REG_X; + mIns[i + 1].mType = ASMIT_INX; mIns[i + 1].mMode = ASMIM_IMPLIED; mIns[i + 1].mLive |= LIVE_CPU_REG_X; + if (mIns[i + 2].mAddress == 2) + mIns[i + 2].mType = ASMIT_INX; + else + mIns[i + 2].mType = ASMIT_NOP; + mIns[i + 2].mMode = ASMIM_IMPLIED; mIns[i + 2].mLive |= LIVE_CPU_REG_X; + mIns[i + 3].mType = ASMIT_STX; mIns[i + 3].mLive |= LIVE_CPU_REG_X; + mIns[i + 4].mType = ASMIT_STX; + return true; + } + else if (!(mIns[i + 4].mLive & LIVE_CPU_REG_Y)) + { + mIns[i + 0].mType = ASMIT_LDY; mIns[i + 0].CopyMode(mIns[i + 1]); mIns[i + 0].mLive |= LIVE_CPU_REG_Y; + mIns[i + 1].mType = ASMIT_INY; mIns[i + 1].mMode = ASMIM_IMPLIED; mIns[i + 1].mLive |= LIVE_CPU_REG_Y; + if (mIns[i + 2].mAddress == 2) + mIns[i + 2].mType = ASMIT_INY; + else + mIns[i + 2].mType = ASMIT_NOP; + mIns[i + 2].mMode = ASMIM_IMPLIED; mIns[i + 2].mLive |= LIVE_CPU_REG_Y; + mIns[i + 3].mType = ASMIT_STY; mIns[i + 3].mLive |= LIVE_CPU_REG_Y; + mIns[i + 4].mType = ASMIT_STY; + return true; + } + } + + if ( + mIns[i + 0].mType == ASMIT_SEC && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + mIns[i + 2].mType == ASMIT_SBC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && (mIns[i + 2].mAddress == 1 || mIns[i + 2].mAddress == 2) && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && + mIns[i + 4].mType == ASMIT_STA && mIns[i + 4].mMode == ASMIM_ZERO_PAGE && !(mIns[i + 4].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C))) + { + if (!(mIns[i + 4].mLive & LIVE_CPU_REG_X)) + { + mIns[i + 0].mType = ASMIT_LDX; mIns[i + 0].CopyMode(mIns[i + 1]); mIns[i + 0].mLive |= LIVE_CPU_REG_X; + mIns[i + 1].mType = ASMIT_DEX; mIns[i + 1].mMode = ASMIM_IMPLIED; mIns[i + 1].mLive |= LIVE_CPU_REG_X; + if (mIns[i + 2].mAddress == 2) + mIns[i + 2].mType = ASMIT_DEX; + else + mIns[i + 2].mType = ASMIT_NOP; + mIns[i + 2].mMode = ASMIM_IMPLIED; mIns[i + 2].mLive |= LIVE_CPU_REG_X; + mIns[i + 3].mType = ASMIT_STX; mIns[i + 3].mLive |= LIVE_CPU_REG_X; + mIns[i + 4].mType = ASMIT_STX; + return true; + } + else if (!(mIns[i + 4].mLive & LIVE_CPU_REG_Y)) + { + mIns[i + 0].mType = ASMIT_LDY; mIns[i + 0].CopyMode(mIns[i + 1]); mIns[i + 0].mLive |= LIVE_CPU_REG_Y; + mIns[i + 1].mType = ASMIT_DEY; mIns[i + 1].mMode = ASMIM_IMPLIED; mIns[i + 1].mLive |= LIVE_CPU_REG_Y; + if (mIns[i + 2].mAddress == 2) + mIns[i + 2].mType = ASMIT_DEY; + else + mIns[i + 2].mType = ASMIT_NOP; + mIns[i + 2].mMode = ASMIM_IMPLIED; mIns[i + 2].mLive |= LIVE_CPU_REG_Y; + mIns[i + 3].mType = ASMIT_STY; mIns[i + 3].mLive |= LIVE_CPU_REG_Y; + mIns[i + 4].mType = ASMIT_STY; + return true; + } + } + + if ( + pass > 2 && + mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 1 && + mIns[i + 3].mType == ASMIT_STA && !mIns[i + 3].MayBeSameAddress(mIns[i + 1]) && !mIns[i + 3].RequiresYReg() && + mIns[i + 4].mType == ASMIT_LDY && mIns[i + 4].SameEffectiveAddress(mIns[i + 1]) && !(mIns[i + 4].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C))) + { + mIns[i + 0].mType = ASMIT_NOP; + mIns[i + 1].mType = ASMIT_LDY; mIns[i + 1].mLive |= LIVE_CPU_REG_Y; + mIns[i + 2].mType = ASMIT_INY; mIns[i + 2].mMode = ASMIM_IMPLIED; mIns[i + 2].mLive |= LIVE_CPU_REG_Y; + mIns[i + 3].mType = ASMIT_STY; mIns[i + 3].mLive |= LIVE_CPU_REG_Y; + mIns[i + 4].mType = ASMIT_DEY; mIns[i + 4].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_LDA && + mIns[i + 1].mType == ASMIT_STA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + mIns[i + 2].mType == ASMIT_LDA && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && mIns[i + 3].mAddress != mIns[i + 1].mAddress && + mIns[i + 4].mType == ASMIT_LDA && mIns[i + 4].mMode == ASMIM_ZERO_PAGE && mIns[i + 4].mAddress == mIns[i + 1].mAddress && + + !mIns[i + 0].ReferencesZeroPage(mIns[i + 3].mAddress) && + !mIns[i + 2].ReferencesZeroPage(mIns[i + 1].mAddress) && + !(mIns[i + 0].mFlags & NCIF_VOLATILE) && !(mIns[i + 2].mFlags & NCIF_VOLATILE)) + { + NativeCodeInstruction ins(mIns[i + 0]); + mIns[i + 0] = mIns[i + 2]; + mIns[i + 2] = ins; + mIns[i + 1].mAddress = mIns[i + 3].mAddress; + mIns[i + 3].mAddress = mIns[i + 4].mAddress; + + if (mIns[i + 2].RequiresYReg()) + { + mIns[i + 0].mLive |= LIVE_CPU_REG_Y; + mIns[i + 1].mLive |= LIVE_CPU_REG_Y; + } + if (mIns[i + 2].RequiresXReg()) + { + mIns[i + 0].mLive |= LIVE_CPU_REG_X; + mIns[i + 1].mLive |= LIVE_CPU_REG_X; + } + + mIns[i + 0].mLive |= mIns[i + 2].mLive; + mIns[i + 2].mLive |= mIns[i + 4].mLive; + mIns[i + 3].mLive |= mIns[i + 4].mLive; + + mIns[i + 4].mType = ASMIT_NOP; mIns[i + 4].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_TAY && + mIns[i + 1].mType == ASMIT_TXA && + mIns[i + 2].IsShift() && mIns[i + 2].mMode == ASMIM_IMPLIED && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && + mIns[i + 4].mType == ASMIT_TYA && !(mIns[i + 4].mLive & (LIVE_CPU_REG_Y | LIVE_CPU_REG_Z))) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_STX; mIns[i + 1].CopyMode(mIns[i + 3]); + mIns[i + 3].mType = mIns[i + 2].mType; mIns[i + 3].mLive |= LIVE_CPU_REG_A; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + mIns[i + 4].mType = ASMIT_NOP; mIns[i + 4].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_TAX && + mIns[i + 1].mType == ASMIT_TYA && + mIns[i + 2].IsShift() && mIns[i + 2].mMode == ASMIM_IMPLIED && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && + mIns[i + 4].mType == ASMIT_TXA && !(mIns[i + 4].mLive & (LIVE_CPU_REG_X | LIVE_CPU_REG_Z))) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_STY; mIns[i + 1].CopyMode(mIns[i + 3]); + mIns[i + 3].mType = mIns[i + 2].mType; mIns[i + 3].mLive |= LIVE_CPU_REG_A; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + mIns[i + 4].mType = ASMIT_NOP; mIns[i + 4].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_LDA && (mIns[i + 0].mMode == ASMIM_ZERO_PAGE || mIns[i + 0].mMode == ASMIM_ABSOLUTE) && + (mIns[i + 1].mType == ASMIT_TAY || mIns[i + 1].mType == ASMIT_TAX) && + mIns[i + 2].mType == ASMIT_CLC && + mIns[i + 3].mType == ASMIT_ADC && mIns[i + 3].mMode == ASMIM_IMMEDIATE && (mIns[i + 3].mAddress == 0xff || mIns[i + 3].mAddress == 0x01) && + mIns[i + 4].mType == ASMIT_STA && mIns[i + 0].SameEffectiveAddress(mIns[i + 4]) && !(mIns[i + 4].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C))) + { + if (mIns[i + 1].mType == ASMIT_TAY) + { + mIns[i + 0].mType = ASMIT_LDY; mIns[i + 0].mLive |= LIVE_CPU_REG_Y; + } + else + { + mIns[i + 0].mType = ASMIT_LDX; mIns[i + 0].mLive |= LIVE_CPU_REG_X; + } + + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + mIns[i + 3].mType = ASMIT_NOP; mIns[i + 3].mMode = ASMIM_IMPLIED; + if (mIns[i + 3].mAddress == 0x01) + mIns[i + 4].mType = ASMIT_INC; + else + mIns[i + 4].mType = ASMIT_DEC; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_LDY && (mIns[i + 0].mMode == ASMIM_ZERO_PAGE || mIns[i + 0].mMode == ASMIM_ABSOLUTE) && + mIns[i + 1].mType == ASMIT_TYA && + mIns[i + 2].mType == ASMIT_CLC && + mIns[i + 3].mType == ASMIT_ADC && mIns[i + 3].mMode == ASMIM_IMMEDIATE && (mIns[i + 3].mAddress == 0xff || mIns[i + 3].mAddress == 0x01) && + mIns[i + 4].mType == ASMIT_STA && mIns[i + 0].SameEffectiveAddress(mIns[i + 4]) && !(mIns[i + 4].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C))) + { + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + mIns[i + 3].mType = ASMIT_NOP; mIns[i + 3].mMode = ASMIM_IMPLIED; + if (mIns[i + 3].mAddress == 0x01) + mIns[i + 4].mType = ASMIT_INC; + else + mIns[i + 4].mType = ASMIT_DEC; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_LDX && (mIns[i + 0].mMode == ASMIM_ZERO_PAGE || mIns[i + 0].mMode == ASMIM_ABSOLUTE) && + mIns[i + 1].mType == ASMIT_TXA && + mIns[i + 2].mType == ASMIT_CLC && + mIns[i + 3].mType == ASMIT_ADC && mIns[i + 3].mMode == ASMIM_IMMEDIATE && (mIns[i + 3].mAddress == 0xff || mIns[i + 3].mAddress == 0x01) && + mIns[i + 4].mType == ASMIT_STA && mIns[i + 0].SameEffectiveAddress(mIns[i + 4]) && !(mIns[i + 4].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C))) + { + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + mIns[i + 3].mType = ASMIT_NOP; mIns[i + 3].mMode = ASMIM_IMPLIED; + if (mIns[i + 3].mAddress == 0x01) + mIns[i + 4].mType = ASMIT_INC; + else + mIns[i + 4].mType = ASMIT_DEC; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_TXA && + mIns[i + 1].mType == ASMIT_STA && + mIns[i + 2].mType == ASMIT_CLC && + mIns[i + 3].mType == ASMIT_ADC && mIns[i + 3].mMode == ASMIM_IMMEDIATE && mIns[i + 3].mAddress == 1 && + mIns[i + 4].mType == ASMIT_TAX && !(mIns[i + 4].mLive & LIVE_CPU_REG_C)) + { + mIns[i + 0].mLive |= LIVE_CPU_REG_X; + mIns[i + 1].mLive |= LIVE_CPU_REG_X; + mIns[i + 2].mType = ASMIT_INX; mIns[i + 2].mMode = ASMIM_IMPLIED; mIns[i + 2].mLive |= LIVE_CPU_REG_X | LIVE_CPU_REG_Z; + mIns[i + 3].mType = ASMIT_NOP; mIns[i + 3].mMode = ASMIM_IMPLIED; + mIns[i + 4].mType = ASMIT_TXA; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_TXA && + mIns[i + 1].mType == ASMIT_STA && + mIns[i + 2].mType == ASMIT_SEC && + mIns[i + 3].mType == ASMIT_SBC && mIns[i + 3].mMode == ASMIM_IMMEDIATE && mIns[i + 3].mAddress == 1 && + mIns[i + 4].mType == ASMIT_TAX && !(mIns[i + 4].mLive & LIVE_CPU_REG_C)) + { + mIns[i + 0].mLive |= LIVE_CPU_REG_X; + mIns[i + 1].mLive |= LIVE_CPU_REG_X; + mIns[i + 2].mType = ASMIT_DEX; mIns[i + 2].mMode = ASMIM_IMPLIED; mIns[i + 2].mLive |= LIVE_CPU_REG_X | LIVE_CPU_REG_Z; + mIns[i + 3].mType = ASMIT_NOP; mIns[i + 3].mMode = ASMIM_IMPLIED; + mIns[i + 4].mType = ASMIT_TXA; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_IMMEDIATE && mIns[i + 0].mAddress == 0 && + mIns[i + 1].mType == ASMIT_SBC && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 0 && + mIns[i + 2].mType == ASMIT_LSR && mIns[i + 2].mMode == ASMIM_IMPLIED && + mIns[i + 3].mType == ASMIT_TXA && + mIns[i + 4].mType == ASMIT_ROR && mIns[i + 4].mMode == ASMIM_IMPLIED && !(mIns[i + 4].mLive & LIVE_CPU_REG_C)) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + + mIns[i + 2].mType = ASMIT_TXA; mIns[i + 2].mMode = ASMIM_IMPLIED; mIns[i + 2].mLive |= LIVE_CPU_REG_A | LIVE_CPU_REG_C; + mIns[i + 3].mType = ASMIT_ROR; mIns[i + 3].mMode = ASMIM_IMPLIED; + mIns[i + 4].mType = ASMIT_EOR; mIns[i + 4].mMode = ASMIM_IMMEDIATE; mIns[i + 4].mAddress = 0x80; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_AND && mIns[i + 0].mMode == ASMIM_IMMEDIATE && mIns[i + 0].mAddress == 1 && + mIns[i + 1].mType == ASMIT_STA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + mIns[i + 2].mType == ASMIT_LDA && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && mIns[i + 2].mAddress != mIns[i + 1].mAddress && + mIns[i + 3].mType == ASMIT_ASL && mIns[i + 3].mMode == ASMIM_IMPLIED && + mIns[i + 4].mType == ASMIT_ORA && mIns[i + 4].mMode == ASMIM_ZERO_PAGE && mIns[i + 4].mAddress == mIns[i + 1].mAddress && !(mIns[i + 4].mLive & LIVE_MEM)) + { + mIns[i + 0].mType = ASMIT_LSR; mIns[i + 0].mMode = ASMIM_IMPLIED; mIns[i + 0].mLive |= LIVE_CPU_REG_C; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mLive |= LIVE_CPU_REG_C; + mIns[i + 3].mType = ASMIT_ROL; + mIns[i + 4].mType = ASMIT_NOP; mIns[i + 4].mMode = ASMIM_IMPLIED; + + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_LSR && mIns[i + 0].mMode == ASMIM_IMPLIED && + mIns[i + 1].mType == ASMIT_STA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + mIns[i + 2].mType == ASMIT_LDA && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 0 && + mIns[i + 3].mType == ASMIT_ROR && mIns[i + 3].mMode == ASMIM_IMPLIED && + mIns[i + 4].mType == ASMIT_ORA && mIns[i + 4].mMode == ASMIM_ZERO_PAGE && mIns[i + 4].mAddress == mIns[i + 1].mAddress && !(mIns[i + 4].mLive & (LIVE_MEM | LIVE_CPU_REG_C))) + { + mIns[i + 0] = mIns[i + 1]; mIns[i + 0].mLive |= LIVE_CPU_REG_A; + mIns[i + 1].mType = ASMIT_LSR; mIns[i + 1].mMode = ASMIM_IMPLIED; mIns[i + 1].mLive |= LIVE_CPU_REG_A; + mIns[i + 2].CopyMode(mIns[i + 4]); + mIns[i + 4].mType = ASMIT_NOP; mIns[i + 4].mMode = ASMIM_IMPLIED; + + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_AND && mIns[i + 0].mMode == ASMIM_IMMEDIATE && mIns[i + 0].mAddress == 0x0f && + mIns[i + 1].mType == ASMIT_ASL && mIns[i + 1].mMode == ASMIM_IMPLIED && + mIns[i + 2].mType == ASMIT_ASL && mIns[i + 2].mMode == ASMIM_IMPLIED && + mIns[i + 3].mType == ASMIT_ASL && mIns[i + 3].mMode == ASMIM_IMPLIED && + mIns[i + 4].mType == ASMIT_ASL && mIns[i + 4].mMode == ASMIM_IMPLIED && !(mIns[i + 4].mLive & LIVE_CPU_REG_C)) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_AND && mIns[i + 0].mMode == ASMIM_IMMEDIATE && mIns[i + 0].mAddress == 0xf0 && + mIns[i + 1].mType == ASMIT_LSR && mIns[i + 1].mMode == ASMIM_IMPLIED && + mIns[i + 2].mType == ASMIT_LSR && mIns[i + 2].mMode == ASMIM_IMPLIED && + mIns[i + 3].mType == ASMIT_LSR && mIns[i + 3].mMode == ASMIM_IMPLIED && + mIns[i + 4].mType == ASMIT_LSR && mIns[i + 4].mMode == ASMIM_IMPLIED && !(mIns[i + 4].mLive & LIVE_CPU_REG_C)) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_STA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_ASL && mIns[i + 1].mMode == ASMIM_IMPLIED && + !mIns[i + 2].ChangesAccu() && + mIns[i + 3].mType == ASMIT_LDA && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && mIns[i + 3].mAddress == mIns[i + 0].mAddress && + mIns[i + 4].mType == ASMIT_ASL && mIns[i + 4].mMode == ASMIM_IMPLIED && !(mIns[i + 4].mLive & LIVE_CPU_REG_A)) + { + mIns[i + 2].mLive |= LIVE_CPU_REG_C; + mIns[i + 3].mType = ASMIT_NOP; mIns[i + 3].mMode = ASMIM_IMPLIED; + mIns[i + 4].mType = ASMIT_NOP; mIns[i + 4].mMode = ASMIM_IMPLIED; + + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_AND && mIns[i + 0].mMode == ASMIM_IMMEDIATE && + mIns[i + 1].mType == ASMIT_CLC && + mIns[i + 2].mType == ASMIT_ADC && + mIns[i + 3].mType == ASMIT_CLC && + mIns[i + 4].mType == ASMIT_ADC && mIns[i + 4].mMode == ASMIM_IMMEDIATE && + (mIns[i + 0].mAddress & mIns[i + 4].mAddress) == 0 && + !(mIns[i + 4].mLive & LIVE_CPU_REG_C)) + { + mIns[i + 3] = mIns[i + 1]; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + int mask = mIns[i + 4].mAddress; + mIns[i + 4] = mIns[i + 2]; + mIns[i + 2] = NativeCodeInstruction(mIns[i + 2].mIns, ASMIT_ORA, ASMIM_IMMEDIATE, mask); + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_LDA && HasAsmInstructionMode(ASMIT_LDX, mIns[i + 1].mMode) && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 1 && + mIns[i + 3].mType == ASMIT_CMP && HasAsmInstructionMode(ASMIT_CPX, mIns[i + 3].mMode) && + mIns[i + 4].mType == ASMIT_TAX && !(mIns[i + 4].mLive & LIVE_CPU_REG_A)) + { + mIns[i + 0].mType = ASMIT_NOP; + mIns[i + 1].mType = ASMIT_LDX; mIns[i + 1].mLive |= LIVE_CPU_REG_X; + mIns[i + 2].mType = ASMIT_INX; mIns[i + 2].mMode = ASMIM_IMPLIED; mIns[i + 2].mLive |= LIVE_CPU_REG_X; + mIns[i + 3].mType = ASMIT_CPX; mIns[i + 3].mLive |= LIVE_CPU_REG_X; + mIns[i + 4].mType = ASMIT_NOP; + return true; + } +#if 1 + if ( + mIns[i + 0].mType == ASMIT_STA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].IsShiftRotOrInc() && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && mIns[i + 1].mAddress != mIns[i + 0].mAddress && + mIns[i + 2].IsShiftRotOrInc() && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && mIns[i + 2].mAddress != mIns[i + 0].mAddress && + mIns[i + 3].IsShiftRotOrInc() && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && mIns[i + 3].mAddress != mIns[i + 0].mAddress && + mIns[i + 4].IsShift() && mIns[i + 4].mMode == ASMIM_ZERO_PAGE && mIns[i + 4].mAddress == mIns[i + 0].mAddress && !(mIns[i + 4].mLive & LIVE_CPU_REG_A)) + { + NativeCodeInstruction ins = mIns[i + 0]; + mIns[i + 0] = mIns[i + 1]; mIns[i + 0].mLive |= LIVE_CPU_REG_A; + mIns[i + 1] = mIns[i + 2]; mIns[i + 1].mLive |= LIVE_CPU_REG_A; + mIns[i + 2] = mIns[i + 3]; mIns[i + 2].mLive |= LIVE_CPU_REG_A; + mIns[i + 3] = mIns[i + 4]; mIns[i + 3].mLive |= LIVE_CPU_REG_A; mIns[i + 3].mMode = ASMIM_IMPLIED; + mIns[i + 4] = ins; + return true; + } +#endif + + return false; +} + +bool NativeCodeBasicBlock::PeepHoleOptimizerIterate4b(int i, int pass) +{ + if ( + mIns[i + 0].mType == ASMIT_INY && + mIns[i + 1].mType == ASMIT_TYA && + mIns[i + 2].mType == ASMIT_CLC && + mIns[i + 3].mType == ASMIT_ADC && mIns[i + 3].mMode == ASMIM_IMMEDIATE && !(mIns[i + 3].mLive & (LIVE_CPU_REG_Y | LIVE_CPU_REG_C))) + { + mIns[i + 0].mType = ASMIT_NOP; + mIns[i + 3].mAddress++; + return true; + } + else if ( + mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_ADC && mIns[i + 1].mMode == ASMIM_IMMEDIATE && + mIns[i + 2].mType == ASMIT_TAY && + mIns[i + 3].mType == ASMIT_INY && !(mIns[i + 3].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C))) + { + mIns[i + 1].mAddress++; + mIns[i + 3].mType = ASMIT_NOP; + return true; + } + else if ( + mIns[i + 0].mType == ASMIT_INX && + mIns[i + 1].mType == ASMIT_TXA && + mIns[i + 2].mType == ASMIT_CLC && + mIns[i + 3].mType == ASMIT_ADC && mIns[i + 3].mMode == ASMIM_IMMEDIATE && !(mIns[i + 3].mLive & (LIVE_CPU_REG_X | LIVE_CPU_REG_C))) + { + mIns[i + 0].mType = ASMIT_NOP; + mIns[i + 3].mAddress++; + return true; + } + else if ( + mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_ADC && mIns[i + 1].mMode == ASMIM_IMMEDIATE && + mIns[i + 2].mType == ASMIT_TAX && + mIns[i + 3].mType == ASMIT_INX && !(mIns[i + 3].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C))) + { + mIns[i + 1].mAddress++; + mIns[i + 3].mType = ASMIT_NOP; + return true; + } + else if (pass > 8 && + mIns[i + 0].mType == ASMIT_TYA && + mIns[i + 1].mType == ASMIT_CLC && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && (mIns[i + 2].mAddress & 0xff) < 4 && + mIns[i + 3].mType == ASMIT_TAY && + !(mIns[i + 3].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C))) + { + int n = mIns[i + 2].mAddress & 0xff; + for (int k = 0; k < n; k++) + { + mIns[i + k].mType = ASMIT_INY; mIns[i + k].mMode = ASMIM_IMPLIED; mIns[i + k].mLive |= LIVE_CPU_REG_Y; + } + for (int k = n; k < 4; k++) + { + mIns[i + k].mType = ASMIT_NOP; mIns[i + k].mMode = ASMIM_IMPLIED; + } + return true; + } + else if (pass > 8 && + mIns[i + 0].mType == ASMIT_TXA && + mIns[i + 1].mType == ASMIT_CLC && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && (mIns[i + 2].mAddress & 0xff) < 4 && + mIns[i + 3].mType == ASMIT_TAX && + !(mIns[i + 3].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C))) + { + int n = mIns[i + 2].mAddress & 0xff; + for (int k = 0; k < n; k++) + { + mIns[i + k].mType = ASMIT_INX; mIns[i + k].mMode = ASMIM_IMPLIED; mIns[i + k].mLive |= LIVE_CPU_REG_X; + } + for (int k = n; k < 4; k++) + { + mIns[i + k].mType = ASMIT_NOP; mIns[i + k].mMode = ASMIM_IMPLIED; + } + return true; + } + + return false; +} + +bool NativeCodeBasicBlock::PeepHoleOptimizerIterate5b(int i, int pass) +{ + if ( + mIns[i + 0].mType == ASMIT_TAX && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_IMMEDIATE && + mIns[i + 2].mType == ASMIT_STA && mIns[i + 2].mMode != ASMIM_ABSOLUTE_X && + mIns[i + 3].mType == ASMIT_TXA && + mIns[i + 4].mType == ASMIT_STA && !mIns[i + 2].SameEffectiveAddress(mIns[i + 4]) && !(mIns[i + 4].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z | LIVE_CPU_REG_X))) + { + mIns[i + 0] = mIns[i + 4]; + mIns[i + 3].mType = ASMIT_NOP; mIns[i + 3].mMode = ASMIM_IMPLIED; + mIns[i + 4].mType = ASMIT_NOP; mIns[i + 4].mMode = ASMIM_IMPLIED; + return true; + } + else if ( + mIns[i + 0].mType == ASMIT_LDA && (mIns[i + 0].mMode == ASMIM_ZERO_PAGE || mIns[i + 0].mMode == ASMIM_ABSOLUTE) && + mIns[i + 1].mType == ASMIT_TAX && + mIns[i + 2].mType == ASMIT_CLC && + mIns[i + 3].mType == ASMIT_ADC && mIns[i + 3].mMode == ASMIM_IMMEDIATE && mIns[i + 3].mAddress == 1 && + mIns[i + 4].mType == ASMIT_STA && mIns[i + 0].SameEffectiveAddress(mIns[i + 4]) && !(mIns[i + 4].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z | LIVE_CPU_REG_C))) + { + mIns[i + 0].mType = ASMIT_LDX; mIns[i + 0].mLive |= LIVE_CPU_REG_X; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + mIns[i + 3].mType = ASMIT_NOP; mIns[i + 3].mMode = ASMIM_IMPLIED; + mIns[i + 4].mType = ASMIT_INC; + return true; + } + else if ( + mIns[i + 0].mType == ASMIT_LDA && (mIns[i + 0].mMode == ASMIM_ZERO_PAGE || mIns[i + 0].mMode == ASMIM_ABSOLUTE) && + mIns[i + 1].mType == ASMIT_TAX && + mIns[i + 2].mType == ASMIT_CLC && + mIns[i + 3].mType == ASMIT_ADC && mIns[i + 3].mMode == ASMIM_IMMEDIATE && mIns[i + 3].mAddress == 0xff && + mIns[i + 4].mType == ASMIT_STA && mIns[i + 0].SameEffectiveAddress(mIns[i + 4]) && !(mIns[i + 4].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z | LIVE_CPU_REG_C))) + { + mIns[i + 0].mType = ASMIT_LDX; mIns[i + 0].mLive |= LIVE_CPU_REG_X; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + mIns[i + 3].mType = ASMIT_NOP; mIns[i + 3].mMode = ASMIM_IMPLIED; + mIns[i + 4].mType = ASMIT_DEC; + return true; + } + else if ( + mIns[i + 0].mType == ASMIT_TAX && + mIns[i + 1].mType == ASMIT_LDA && (mIns[i + 1].mMode == ASMIM_ZERO_PAGE || mIns[i + 1].mMode == ASMIM_ABSOLUTE) && + mIns[i + 2].mType == ASMIT_CLC && + mIns[i + 3].mType == ASMIT_ADC && mIns[i + 3].mMode == ASMIM_IMMEDIATE && mIns[i + 3].mAddress == 1 && + mIns[i + 4].mType == ASMIT_STA && (mIns[i + 4].mMode == ASMIM_ZERO_PAGE || mIns[i + 4].mMode == ASMIM_ABSOLUTE) && !(mIns[i + 4].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z | LIVE_CPU_REG_C))) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_LDX; mIns[i + 1].mLive |= LIVE_CPU_REG_X; + mIns[i + 2].mType = ASMIT_INX; mIns[i + 2].mLive |= LIVE_CPU_REG_X; + mIns[i + 3].mType = ASMIT_NOP; mIns[i + 3].mMode = ASMIM_IMPLIED; + mIns[i + 4].mType = ASMIT_STX; mIns[i + 4].mLive |= LIVE_CPU_REG_A; + mIns.Insert(i + 5, NativeCodeInstruction(mIns[i + 0].mIns, ASMIT_TAX)); + return true; + } + else if ( + mIns[i + 0].mType == ASMIT_ASL && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_ASL && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && mIns[i + 1].mAddress == mIns[i + 0].mAddress && + mIns[i + 2].mType == ASMIT_ASL && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && mIns[i + 2].mAddress == mIns[i + 0].mAddress && + mIns[i + 3].mType == ASMIT_ROL && mIns[i + 3].mMode == ASMIM_IMPLIED && + mIns[i + 4].mType == ASMIT_STA && mIns[i + 4].mMode == ASMIM_ZERO_PAGE && mIns[i + 4].mAddress != mIns[i + 0].mAddress && + !(mIns[i + 4].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z))) + { + int a0 = mIns[i + 0].mAddress; + int a1 = mIns[i + 4].mAddress; + + mIns.Insert(i, NativeCodeInstruction(mIns[i + 0].mIns, ASMIT_STA, ASMIM_ZERO_PAGE, a1)); + mIns.Insert(i + 1, NativeCodeInstruction(mIns[i + 0].mIns, ASMIT_LDA, ASMIM_ZERO_PAGE, a0)); + + mIns[i + 2].mMode = ASMIM_IMPLIED; + mIns[i + 3].mMode = ASMIM_IMPLIED; + mIns[i + 4].mMode = ASMIM_IMPLIED; + mIns[i + 5].mMode = ASMIM_ZERO_PAGE; mIns[i + 5].mAddress = a1; + mIns[i + 6].mAddress = a0; + return true; + } + else if ( + mIns[i + 0].mType == ASMIT_AND && mIns[i + 0].mMode == ASMIM_IMMEDIATE && mIns[i + 0].mAddress == 0x80 && + mIns[i + 1].mType == ASMIT_STA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + mIns[i + 2].mType == ASMIT_LDA && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && mIns[i + 2].mAddress != mIns[i + 1].mAddress && + mIns[i + 3].mType == ASMIT_LSR && mIns[i + 3].mMode == ASMIM_IMPLIED && + mIns[i + 4].mType == ASMIT_ORA && mIns[i + 4].mMode == ASMIM_ZERO_PAGE && mIns[i + 4].mAddress == mIns[i + 1].mAddress && !(mIns[i + 4].mLive & (LIVE_CPU_REG_C | LIVE_MEM))) + { + mIns[i + 0].mType = ASMIT_ASL; mIns[i + 0].mMode = ASMIM_IMPLIED; mIns[i + 0].mLive |= LIVE_CPU_REG_C; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mLive |= LIVE_CPU_REG_C; + mIns[i + 3].mType = ASMIT_ROR; + mIns[i + 4].mType = ASMIT_NOP; mIns[i + 4].mMode = ASMIM_IMPLIED; + return true; + } + else if ( + mIns[i + 0].mType == ASMIT_TYA && + mIns[i + 1].mType == ASMIT_TAX && + mIns[i + 2].mType == ASMIT_CLC && + mIns[i + 3].mType == ASMIT_ADC && mIns[i + 3].mMode == ASMIM_IMMEDIATE && mIns[i + 3].mAddress <= 2 && + mIns[i + 4].mType == ASMIT_TAY && !(mIns[i + 4].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C))) + { + mIns[i + 0].mLive |= LIVE_CPU_REG_Y; + mIns[i + 1].mLive |= LIVE_CPU_REG_Y; + mIns[i + 2].mType = ASMIT_INY; mIns[i + 2].mLive |= LIVE_CPU_REG_Y; + mIns[i + 3].mType = ASMIT_NOP; mIns[i + 3].mMode = ASMIM_IMPLIED; + if (mIns[i + 3].mAddress > 1) + mIns[i + 4].mType = ASMIT_INY; + else + mIns[i + 4].mType = ASMIT_NOP; + + return true; + } + else if ( + mIns[i + 0].mType == ASMIT_LDA && + mIns[i + 1].mType == ASMIT_STA && + mIns[i + 2].mType == ASMIT_CLC && + mIns[i + 3].mType == ASMIT_ADC && mIns[i + 3].mMode == ASMIM_IMMEDIATE && mIns[i + 3].mAddress == 1 && + mIns[i + 4].mType == ASMIT_STA && mIns[i + 4].SameEffectiveAddress(mIns[i + 0]) && !(mIns[i + 4].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C)) && + HasAsmInstructionMode(ASMIT_INC, mIns[i + 0].mMode)) + { + mIns[i + 4].mType = ASMIT_INC; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + mIns[i + 3].mType = ASMIT_NOP; mIns[i + 3].mMode = ASMIM_IMPLIED; + + return true; + } + else if ( + mIns[i + 0].mType == ASMIT_LDA && + mIns[i + 1].mType == ASMIT_STA && + mIns[i + 2].mType == ASMIT_CLC && + mIns[i + 3].mType == ASMIT_ADC && mIns[i + 3].mMode == ASMIM_IMMEDIATE && mIns[i + 3].mAddress == 255 && + mIns[i + 4].mType == ASMIT_STA && mIns[i + 4].SameEffectiveAddress(mIns[i + 0]) && !(mIns[i + 4].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C)) && + HasAsmInstructionMode(ASMIT_DEC, mIns[i + 0].mMode)) + { + mIns[i + 4].mType = ASMIT_DEC; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + mIns[i + 3].mType = ASMIT_NOP; mIns[i + 3].mMode = ASMIM_IMPLIED; + + return true; + } + else if ( + pass > 8 && + mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_LDA && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 1 && + mIns[i + 3].mType == ASMIT_CLC && + mIns[i + 4].mType == ASMIT_ADC && + !(mIns[i + 4].mLive & LIVE_CPU_REG_C)) + { + mIns[i + 3].mType = ASMIT_SEC; + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + + return true; + } + else if ( + mIns[i + 0].mType == ASMIT_STA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_ASL && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && mIns[i + 1].mAddress != mIns[i + 0].mAddress && + mIns[i + 2].mType == ASMIT_ROL && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && mIns[i + 2].mAddress != mIns[i + 0].mAddress && + mIns[i + 3].mType == ASMIT_ROL && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && mIns[i + 3].mAddress != mIns[i + 0].mAddress && + mIns[i + 4].mType == ASMIT_ROL && mIns[i + 4].mMode == ASMIM_ZERO_PAGE && mIns[i + 4].mAddress == mIns[i + 0].mAddress && + !(mIns[i + 4].mLive & LIVE_CPU_REG_A)) + { + mIns.Insert(i + 5, mIns[i + 0]); + mIns.Remove(i); + mIns[i + 0].mLive |= LIVE_CPU_REG_A; + mIns[i + 1].mLive |= LIVE_CPU_REG_A; + mIns[i + 2].mLive |= LIVE_CPU_REG_A; + mIns[i + 3].mLive |= LIVE_CPU_REG_A; + mIns[i + 3].mMode = ASMIM_IMPLIED; + mIns[i + 4].mLive |= mIns[i + 3].mLive; + + return true; + } +#if 0 + else if ( + mIns[i + 0].mType == ASMIT_LDY && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_ABSOLUTE_Y && + mIns[i + 2].mType == ASMIT_LDY && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && mIns[i + 2].mAddress != mIns[i + 0].mAddress && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].mMode == ASMIM_ABSOLUTE_Y && + mIns[i + 4].mType == ASMIT_LDY && mIns[i + 4].mMode == ASMIM_ZERO_PAGE && mIns[i + 4].mAddress == mIns[i + 0].mAddress && !(mIns[i + 2].mLive & LIVE_CPU_REG_X)) + { + mIns[i + 1].mLive |= LIVE_CPU_REG_Y; + mIns[i + 2].mType = ASMIT_LDX; mIns[i + 2].mLive |= LIVE_CPU_REG_X; + mIns[i + 3].mMode = ASMIM_ABSOLUTE_X; mIns[i + 3].mLive |= LIVE_CPU_REG_Y; + mIns[i + 4].mType = ASMIT_NOP; mIns[i + 4].mMode = ASMIM_IMPLIED; + return true; + } +#endif + if (pass > 2) + { + if ( + mIns[i + 0].mType == ASMIT_INY && + mIns[i + 1].mType == ASMIT_INY && + mIns[i + 2].mType == ASMIT_INY && + mIns[i + 3].mType == ASMIT_INY && + mIns[i + 4].mType == ASMIT_INY && !(mIns[i + 4].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C))) + { + mIns[i + 0].mType = ASMIT_TYA; mIns[i + 0].mLive |= LIVE_CPU_REG_A; + mIns[i + 1].mType = ASMIT_CLC; mIns[i + 1].mLive |= LIVE_CPU_REG_A | LIVE_CPU_REG_C; + mIns[i + 2].mType = ASMIT_ADC; mIns[i + 2].mMode = ASMIM_IMMEDIATE; mIns[i + 2].mAddress = 5; mIns[i + 2].mLive |= LIVE_CPU_REG_A; + mIns[i + 3].mType = ASMIT_TAY; + mIns[i + 4].mType = ASMIT_NOP; + return true; + } + else if ( + mIns[i + 0].mType == ASMIT_INX && + mIns[i + 1].mType == ASMIT_INX && + mIns[i + 2].mType == ASMIT_INX && + mIns[i + 3].mType == ASMIT_INX && + mIns[i + 4].mType == ASMIT_INX && !(mIns[i + 4].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C))) + { + mIns[i + 0].mType = ASMIT_TXA; mIns[i + 0].mLive |= LIVE_CPU_REG_A; + mIns[i + 1].mType = ASMIT_CLC; mIns[i + 1].mLive |= LIVE_CPU_REG_A | LIVE_CPU_REG_C; + mIns[i + 2].mType = ASMIT_ADC; mIns[i + 2].mMode = ASMIM_IMMEDIATE; mIns[i + 2].mAddress = 5; mIns[i + 2].mLive |= LIVE_CPU_REG_A; + mIns[i + 3].mType = ASMIT_TAX; + mIns[i + 4].mType = ASMIT_NOP; + return true; + } + } + + return false; +} + +bool NativeCodeBasicBlock::PeepHoleOptimizerIterate6(int i, int pass) +{ + if ( + mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_LDY && mIns[i + 1].mMode == ASMIM_IMMEDIATE && + mIns[i + 2].mType == ASMIT_STA && mIns[i + 2].mMode == ASMIM_INDIRECT_Y && !(mIns[i + 2].mFlags & NCIF_VOLATILE) && + mIns[i + 3].mType == ASMIT_TXA && + mIns[i + 4].mType == ASMIT_LDY && mIns[i + 4].mMode == ASMIM_IMMEDIATE && + mIns[i + 5].mType == ASMIT_STA && mIns[i + 5].mMode == ASMIM_INDIRECT_Y && mIns[i + 2].mAddress == mIns[i + 5].mAddress && + !(mIns[i + 5].mFlags & NCIF_VOLATILE) && !(mIns[i + 5].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Y | LIVE_CPU_REG_Z))) + { + mIns[i + 3] = mIns[i + 0]; + mIns[i + 0].mType = ASMIT_TXA; mIns[i + 0].mMode = ASMIM_IMPLIED; + int a = mIns[i + 1].mAddress; mIns[i + 1].mAddress = mIns[i + 4].mAddress; mIns[i + 4].mAddress = a; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_LDA && !mIns[i + 0].RequiresYReg() && + mIns[i + 1].mType == ASMIT_STA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + mIns[i + 2].mType == ASMIT_LDA && !mIns[i + 2].SameEffectiveAddress(mIns[i + 1]) && + mIns[i + 3].mType == ASMIT_LDY && mIns[i + 3].mMode == ASMIM_IMMEDIATE && + mIns[i + 4].mType == ASMIT_STA && mIns[i + 4].mMode == ASMIM_INDIRECT_Y && mIns[i + 4].mAddress != mIns[i + 1].mAddress && mIns[i + 4].mAddress != mIns[i + 1].mAddress + 1 && + mIns[i + 5].mType == ASMIT_LDA && mIns[i + 5].SameEffectiveAddress(mIns[i + 1]) && !(mIns[i + 5].mLive & LIVE_MEM) && + !mIns[i + 0].MayBeChangedOnAddress(mIns[i + 4])) + { + mIns[i + 5] = mIns[i + 0]; + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_AND && mIns[i + 0].mMode == ASMIM_IMMEDIATE && mIns[i + 0].mAddress == 1 && + mIns[i + 1].mType == ASMIT_STA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + mIns[i + 2].mType == ASMIT_LDA && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && + mIns[i + 3].mType == ASMIT_ASL && mIns[i + 3].mMode == ASMIM_IMPLIED && + mIns[i + 4].mType == ASMIT_CLC && + mIns[i + 5].mType == ASMIT_ADC && mIns[i + 5].mMode == ASMIM_ZERO_PAGE && mIns[i + 5].mAddress == mIns[i + 1].mAddress && !(mIns[i + 5].mLive & (LIVE_MEM | LIVE_CPU_REG_C))) + { + mIns[i + 0].mType = ASMIT_LSR; mIns[i + 0].mMode = ASMIM_IMPLIED; mIns[i + 0].mLive |= LIVE_CPU_REG_C; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mLive |= LIVE_CPU_REG_C; + mIns[i + 3].mType = ASMIT_ROL; + mIns[i + 4].mType = ASMIT_NOP; mIns[i + 4].mMode = ASMIM_IMPLIED; + mIns[i + 5].mType = ASMIT_NOP; mIns[i + 5].mMode = ASMIM_IMPLIED; + return true; + } + + if (pass > 0 && + mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].IsShift() && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + mIns[i + 2].IsShift() && mIns[i + 2].mMode == ASMIM_IMPLIED && + mIns[i + 3].IsShift() && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && + mIns[i + 4].IsShift() && mIns[i + 4].mMode == ASMIM_IMPLIED && + mIns[i + 5].mType == ASMIT_STA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 5].mAddress == mIns[i + 0].mAddress && !(mIns[i + 5].mLive & LIVE_CPU_REG_A) && + mIns[i + 5].mAddress != mIns[i + 1].mAddress && mIns[i + 5].mAddress != mIns[i + 3].mAddress) + { + mIns[i + 2].CopyMode(mIns[i + 0]); mIns[i + 2].mLive |= LIVE_MEM; + mIns[i + 4].CopyMode(mIns[i + 0]); mIns[i + 4].mLive |= LIVE_MEM; + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 5].mType = ASMIT_NOP; mIns[i + 5].mMode = ASMIM_IMPLIED; + return true; + } + + if (pass > 0 && + mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_STA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + mIns[i + 2].IsShift() && mIns[i + 2].mMode == ASMIM_IMPLIED && + mIns[i + 3].IsShift() && mIns[i + 3].mMode == ASMIM_IMPLIED && + mIns[i + 4].mType == ASMIT_STA && mIns[i + 4].mMode == ASMIM_ZERO_PAGE && mIns[i + 4].mAddress == mIns[i + 0].mAddress && + mIns[i + 5].mType == ASMIT_LDA && mIns[i + 5].mMode == ASMIM_ZERO_PAGE && mIns[i + 5].mAddress == mIns[i + 1].mAddress && !(mIns[i + 5].mLive & LIVE_MEM)) + { + mIns[i + 2].CopyMode(mIns[i + 0]); mIns[i + 2].mLive |= LIVE_MEM; + mIns[i + 3].CopyMode(mIns[i + 0]); mIns[i + 3].mLive |= LIVE_MEM; + + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 4].mType = ASMIT_NOP; mIns[i + 4].mMode = ASMIM_IMPLIED; + mIns[i + 5].mType = ASMIT_NOP; mIns[i + 5].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_IMMEDIATE && mIns[i + 0].mAddress == 0 && + mIns[i + 1].mType == ASMIT_ROL && mIns[i + 1].mMode == ASMIM_IMPLIED && + mIns[i + 2].mType == ASMIT_STA && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && + mIns[i + 3].mType == ASMIT_LDA && !mIns[i + 3].MayBeSameAddress(mIns[i + 2]) && + mIns[i + 4].mType == ASMIT_ASL && mIns[i + 4].mMode == ASMIM_IMPLIED && + mIns[i + 5].mType == ASMIT_ORA && mIns[i + 5].SameEffectiveAddress(mIns[i + 2]) && !(mIns[i + 5].mLive & LIVE_MEM)) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + mIns[i + 3].mLive |= LIVE_CPU_REG_C; + mIns[i + 4].mType = ASMIT_ROL; + mIns[i + 5].mType = ASMIT_NOP; mIns[i + 5].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_LDA && (mIns[i + 0].mMode == ASMIM_ABSOLUTE || mIns[i + 0].mMode == ASMIM_ABSOLUTE_X || mIns[i + 0].mMode == ASMIM_ABSOLUTE_Y) && + mIns[i + 1].mType == ASMIT_STA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + mIns[i + 2].mType == ASMIT_LDA && (mIns[i + 2].mMode == ASMIM_ABSOLUTE || mIns[i + 2].mMode == ASMIM_ABSOLUTE_X || mIns[i + 2].mMode == ASMIM_ABSOLUTE_Y) && + mIns[i + 3].mType == ASMIT_LSR && mIns[i + 3].mMode == ASMIM_IMPLIED && + mIns[i + 4].mType == ASMIT_STA && mIns[i + 4].mMode == ASMIM_ZERO_PAGE && + mIns[i + 5].mType == ASMIT_ROR && mIns[i + 5].SameEffectiveAddress(mIns[i + 1]) && !(mIns[i + 5].mLive & LIVE_CPU_REG_A)) + { + int livexy = mIns[i + 0].mLive & (LIVE_CPU_REG_X | LIVE_CPU_REG_Y); + + mIns.Insert(i + 5, mIns[i + 0]); + mIns.Insert(i + 7, mIns[i + 1]); + mIns.Remove(i, 2); + + mIns[i + 4].mMode = ASMIM_IMPLIED; mIns[i + 4].mLive |= LIVE_CPU_REG_A; + mIns[i + 5].mLive |= (mIns[i + 4].mLive & (LIVE_CPU_REG_C | LIVE_CPU_REG_Z)); + mIns[i + 0].mLive |= livexy; + mIns[i + 1].mLive |= livexy; + mIns[i + 2].mLive |= LIVE_CPU_REG_C | livexy; + mIns[i + 3].mLive |= LIVE_CPU_REG_C; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_LDA && !(mIns[i + 0].mFlags & NCIF_VOLATILE) && + mIns[i + 1].mType == ASMIT_STA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + mIns[i + 2].mType == ASMIT_LDA && !(mIns[i + 2].mFlags & NCIF_VOLATILE) && !mIns[i + 2].SameEffectiveAddress(mIns[i + 1]) && + mIns[i + 3].IsShift() && mIns[i + 3].mMode == ASMIM_IMPLIED && + mIns[i + 4].IsShift() && mIns[i + 4].SameEffectiveAddress(mIns[i + 1]) && + mIns[i + 5].mType == ASMIT_LDA && mIns[i + 5].SameEffectiveAddress(mIns[i + 1])) + { + mIns[i + 1] = mIns[i + 3]; + mIns[i + 3] = mIns[i + 0]; mIns[i + 3].mLive |= mIns[i + 1].mLive & LIVE_CPU_REG_C; + mIns[i + 0] = mIns[i + 2]; + if (mIns[i + 3].RequiresXReg()) + { + mIns[i + 0].mLive |= LIVE_CPU_REG_X; mIns[i + 1].mLive |= LIVE_CPU_REG_X; + } + if (mIns[i + 3].RequiresYReg()) + { + mIns[i + 0].mLive |= LIVE_CPU_REG_Y; mIns[i + 1].mLive |= LIVE_CPU_REG_Y; + } + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + mIns[i + 4].mMode = ASMIM_IMPLIED; mIns[i + 4].mLive |= LIVE_CPU_REG_A; + mIns[i + 5].mType = ASMIT_STA; + + return true; + } + + +#if 1 + if (pass == 0 && + mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_ADC && mIns[i + 1].mMode == ASMIM_IMMEDIATE_ADDRESS && (mIns[i + 1].mFlags & NCIF_LOWER) && + mIns[i + 2].mType == ASMIT_STA && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && + mIns[i + 3].mType == ASMIT_LDA && mIns[i + 3].mMode == ASMIM_IMMEDIATE_ADDRESS && (mIns[i + 3].mFlags & NCIF_UPPER) && (mIns[i + 3].mLinkerObject == mIns[i + 1].mLinkerObject) && + mIns[i + 4].mType == ASMIT_ADC && mIns[i + 4].mMode == ASMIM_IMMEDIATE && + mIns[i + 5].mType == ASMIT_STA && mIns[i + 5].mMode == ASMIM_ZERO_PAGE && mIns[i + 5].mAddress == mIns[i + 2].mAddress + 1 && + !(mIns[i + 5].mLive & LIVE_CPU_REG_A)) + { + mProc->ResetPatched(); + if (CheckGlobalAddressSumYPointer(this, mIns[i + 2].mAddress, mIns[i + 2].mAddress, i + 6, -1)) + { + assert(mIns[i + 3].mAddress == mIns[i + 1].mAddress); + + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 3].mType = ASMIT_NOP; mIns[i + 3].mMode = ASMIM_IMPLIED; + mIns[i + 4].mType = ASMIT_NOP; mIns[i + 4].mMode = ASMIM_IMPLIED; + mIns[i + 5].mType = ASMIT_NOP; mIns[i + 5].mMode = ASMIM_IMPLIED; + + mProc->ResetPatched(); + if (PatchGlobalAddressSumYPointer(this, mIns[i + 2].mAddress, mIns[i + 2].mAddress, i + 6, -1, mIns[i + 3].mLinkerObject, mIns[i + 3].mAddress + mIns[i + 4].mAddress * 256)) + return true; + } + } +#endif + if (pass == 0 && + mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_ADC && mIns[i + 1].mMode == ASMIM_IMMEDIATE && + mIns[i + 2].mType == ASMIT_STA && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && + mIns[i + 3].mType == ASMIT_LDA && mIns[i + 3].mMode == ASMIM_IMMEDIATE && + mIns[i + 4].mType == ASMIT_ADC && mIns[i + 4].mMode == ASMIM_IMMEDIATE && + mIns[i + 5].mType == ASMIT_STA && mIns[i + 5].mMode == ASMIM_ZERO_PAGE && mIns[i + 5].mAddress == mIns[i + 2].mAddress + 1 && + !(mIns[i + 5].mLive & LIVE_CPU_REG_A)) + { + mProc->ResetPatched(); + if (CheckGlobalAddressSumYPointer(this, mIns[i + 2].mAddress, mIns[i + 2].mAddress, i + 6, -1)) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 3].mType = ASMIT_NOP; mIns[i + 3].mMode = ASMIM_IMPLIED; + mIns[i + 4].mType = ASMIT_NOP; mIns[i + 4].mMode = ASMIM_IMPLIED; + mIns[i + 5].mType = ASMIT_NOP; mIns[i + 5].mMode = ASMIM_IMPLIED; + + mProc->ResetPatched(); + if (PatchGlobalAddressSumYPointer(this, mIns[i + 2].mAddress, mIns[i + 2].mAddress, i + 6, -1, nullptr, mIns[i + 1].mAddress + (mIns[i + 3].mAddress + mIns[i + 4].mAddress) * 256)) + return true; + } + } + else if (pass == 0 && + mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_ADC && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + mIns[i + 2].mType == ASMIT_STA && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && + mIns[i + 3].mType == ASMIT_LDA && mIns[i + 3].mMode == ASMIM_IMMEDIATE && + mIns[i + 4].mType == ASMIT_ADC && mIns[i + 4].mMode == ASMIM_IMMEDIATE && mIns[i + 4].mAddress == 0 && + mIns[i + 5].mType == ASMIT_STA && mIns[i + 5].mMode == ASMIM_ZERO_PAGE && mIns[i + 5].mAddress == mIns[i + 2].mAddress + 1 && + !(mIns[i + 5].mLive & LIVE_CPU_REG_A)) + { + mProc->ResetPatched(); + if (CheckGlobalAddressSumYPointer(this, mIns[i + 2].mAddress, mIns[i + 2].mAddress, i + 6, -1)) + { + mIns[i + 3].mType = ASMIT_NOP; mIns[i + 3].mMode = ASMIM_IMPLIED; + mIns[i + 4].mType = ASMIT_NOP; mIns[i + 4].mMode = ASMIM_IMPLIED; + mIns[i + 5].mType = ASMIT_NOP; mIns[i + 5].mMode = ASMIM_IMPLIED; + + mProc->ResetPatched(); + if (PatchGlobalAddressSumYPointer(this, mIns[i + 2].mAddress, mIns[i + 2].mAddress, i + 6, -1, nullptr, 256 * mIns[i + 3].mAddress)) + return true; + } + } + + if (pass > 8 && + mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_ADC && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 1 && + mIns[i + 2].mType == ASMIT_STA && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && + mIns[i + 3].mType == ASMIT_CLC && + mIns[i + 4].mType == ASMIT_ADC && mIns[i + 4].mMode == ASMIM_IMMEDIATE && mIns[i + 4].mAddress == 1 && + mIns[i + 5].mType == ASMIT_STA && mIns[i + 5].mMode == ASMIM_ZERO_PAGE && + !(mIns[i + 5].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_X | LIVE_CPU_REG_C)) + ) + { + mIns[i + 0].mType = ASMIT_TAX; mIns[i + 0].mLive |= LIVE_CPU_REG_X; + mIns[i + 1].mType = ASMIT_INX; mIns[i + 1].mMode = ASMIM_IMPLIED; mIns[i + 1].mLive |= LIVE_CPU_REG_X; + mIns[i + 2].mType = ASMIT_STX; mIns[i + 2].mLive |= LIVE_CPU_REG_X; + mIns[i + 3].mType = ASMIT_INX; mIns[i + 3].mLive |= LIVE_CPU_REG_X; + mIns[i + 4].mType = ASMIT_NOP; mIns[i + 4].mMode = ASMIM_IMPLIED; + mIns[i + 5].mType = ASMIT_STX; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_TAX && + mIns[i + 1].mType == ASMIT_LSR && mIns[i + 1].mMode == ASMIM_IMPLIED && + !mIns[i + 2].ReferencesCarry() && !mIns[i + 2].ChangesXReg() && + mIns[i + 3].mType == ASMIT_TXA && + mIns[i + 4].mType == ASMIT_AND && mIns[i + 4].mMode == ASMIM_IMMEDIATE && mIns[i + 4].mAddress == 0x01 && + mIns[i + 5].mType == ASMIT_LSR && mIns[i + 5].mMode == ASMIM_IMPLIED) + { + mIns[i + 1].mLive |= LIVE_CPU_REG_C; + mIns[i + 2].mLive |= LIVE_CPU_REG_C; + mIns[i + 3].mType = ASMIT_NOP; mIns[i + 3].mMode = ASMIM_IMPLIED; + mIns[i + 4].mType = ASMIT_LDA; mIns[i + 4].mMode = ASMIM_IMMEDIATE; mIns[i + 4].mAddress = 0x00; mIns[i + 4].mLive |= LIVE_CPU_REG_C; + mIns[i + 5].mType = ASMIT_NOP; mIns[i + 5].mMode = ASMIM_IMPLIED; + + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + (mIns[i + 1].mType == ASMIT_LSR || mIns[i + 1].mType == ASMIT_ROR) && mIns[i + 1].mMode == ASMIM_IMPLIED && + !mIns[i + 2].ReferencesCarry() && !mIns[i + 0].MayBeChangedOnAddress(mIns[i + 2], true) && + mIns[i + 3].mType == ASMIT_LDA && mIns[i + 3].SameEffectiveAddress(mIns[i + 0]) && + mIns[i + 4].mType == ASMIT_AND && mIns[i + 4].mMode == ASMIM_IMMEDIATE && mIns[i + 4].mAddress == 0x01 && + mIns[i + 5].mType == ASMIT_LSR && mIns[i + 5].mMode == ASMIM_IMPLIED) + { + mIns[i + 1].mLive |= LIVE_CPU_REG_C; + mIns[i + 2].mLive |= LIVE_CPU_REG_C; + mIns[i + 3].mType = ASMIT_NOP; mIns[i + 3].mMode = ASMIM_IMPLIED; + mIns[i + 4].mType = ASMIT_LDA; mIns[i + 4].mMode = ASMIM_IMMEDIATE; mIns[i + 4].mAddress = 0x00; mIns[i + 4].mLive |= LIVE_CPU_REG_C; + mIns[i + 5].mType = ASMIT_NOP; mIns[i + 5].mMode = ASMIM_IMPLIED; + + return true; + } + + // 20c7 : b1 10 __ LDA(P3), y; (marr + 0) + // 20c9 : 05 0d __ ORA P0; (sig + 0) + // 20cb : 85 1b __ STA ACCU + 0 + // 20cd : b1 0e __ LDA(P1), y; (arr + 0) + // 20cf : 11 10 __ ORA(P3), y; (marr + 0) + // 20d1 : c5 1b __ CMP ACCU + 0 + + if ( + mIns[i + 0].mType == ASMIT_LDA && + mIns[i + 1].mType == ASMIT_ORA && + mIns[i + 2].mType == ASMIT_STA && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && + mIns[i + 3].mType == ASMIT_LDA && + mIns[i + 4].mType == ASMIT_ORA && mIns[i + 4].SameEffectiveAddress(mIns[i + 0]) && + mIns[i + 5].mType == ASMIT_CMP && mIns[i + 5].mMode == ASMIM_ZERO_PAGE && mIns[i + 5].mAddress == mIns[i + 2].mAddress && + !(mIns[i + 5].mLive & (LIVE_CPU_REG_A | LIVE_MEM))) + { + mIns[i + 0].CopyMode(mIns[i + 3]); + mIns[i + 5].CopyMode(mIns[i + 4]); + mIns[i + 1].mType = ASMIT_EOR; + + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + mIns[i + 3].mType = ASMIT_NOP; mIns[i + 3].mMode = ASMIM_IMPLIED; + + mIns[i + 1].mLive |= (mIns[i + 0].mLive & (LIVE_CPU_REG_X | LIVE_CPU_REG_Y)); + mIns[i + 4].mLive |= (mIns[i + 0].mLive & (LIVE_CPU_REG_X | LIVE_CPU_REG_Y)); + + return true; + } + + // 20d5 : b1 10 __ LDA(P3), y; (marr + 0) + // 20d7 : 11 0e __ ORA(P1), y; (arr + 0) + // 20d9 : 85 1b __ STA ACCU + 0 + // 20db : b1 10 __ LDA(P3), y; (marr + 0) + // 20dd : 05 0d __ ORA P0; (sig + 0) + // 20df : c5 1b __ CMP ACCU + 0 + + if ( + mIns[i + 0].mType == ASMIT_LDA && + mIns[i + 1].mType == ASMIT_ORA && + mIns[i + 2].mType == ASMIT_STA && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && + mIns[i + 3].mType == ASMIT_LDA && mIns[i + 3].SameEffectiveAddress(mIns[i + 0]) && + mIns[i + 4].mType == ASMIT_ORA && + mIns[i + 5].mType == ASMIT_CMP && mIns[i + 5].mMode == ASMIM_ZERO_PAGE && mIns[i + 5].mAddress == mIns[i + 2].mAddress && + !(mIns[i + 5].mLive & (LIVE_CPU_REG_A | LIVE_MEM))) + { + mIns[i + 0].CopyMode(mIns[i + 4]); + mIns[i + 4].CopyMode(mIns[i + 3]); + mIns[i + 5].CopyMode(mIns[i + 3]); + mIns[i + 1].mType = ASMIT_EOR; + + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + mIns[i + 3].mType = ASMIT_NOP; mIns[i + 3].mMode = ASMIM_IMPLIED; + + mIns[i + 1].mLive |= (mIns[i + 0].mLive & (LIVE_CPU_REG_X | LIVE_CPU_REG_Y)); + mIns[i + 4].mLive |= (mIns[i + 0].mLive & (LIVE_CPU_REG_X | LIVE_CPU_REG_Y)); + + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_LSR && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_ROR && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && mIns[i + 1].mAddress != mIns[i + 0].mAddress && + mIns[i + 2].mType == ASMIT_LDA && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && + mIns[i + 3].mType == ASMIT_AND && mIns[i + 3].mMode == ASMIM_IMMEDIATE && mIns[i + 3].mAddress == 0x80 && + mIns[i + 4].mType == ASMIT_ORA && mIns[i + 4].mMode == ASMIM_ZERO_PAGE && mIns[i + 4].mAddress == mIns[i + 0].mAddress && + mIns[i + 5].mType == ASMIT_STA && mIns[i + 5].mMode == ASMIM_ZERO_PAGE && mIns[i + 5].mAddress == mIns[i + 0].mAddress && + !(mIns[i + 5].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z))) + { + mIns.Insert(i + 0, NativeCodeInstruction(mIns[i + 2].mIns, ASMIT_LDA, mIns[i + 2])); + mIns.Insert(i + 1, NativeCodeInstruction(mIns[i + 3].mIns, ASMIT_ASL)); + mIns[i + 2].mType = ASMIT_ROR; + + mIns[i + 4].mType = ASMIT_NOP; mIns[i + 4].mMode = ASMIM_IMPLIED; + mIns[i + 5].mType = ASMIT_NOP; mIns[i + 5].mMode = ASMIM_IMPLIED; + mIns[i + 6].mType = ASMIT_NOP; mIns[i + 6].mMode = ASMIM_IMPLIED; + mIns[i + 7].mType = ASMIT_NOP; mIns[i + 7].mMode = ASMIM_IMPLIED; + return true; + } + + if ( + mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_IMMEDIATE && mIns[i + 0].mAddress == 0 && + mIns[i + 1].mType == ASMIT_ROR && mIns[i + 1].mMode == ASMIM_IMPLIED && + mIns[i + 2].mType == ASMIT_EOR && + mIns[i + 3].mType == ASMIT_ASL && mIns[i + 3].mMode == ASMIM_IMPLIED && + mIns[i + 4].mType == ASMIT_EOR && + mIns[i + 5].mType == ASMIT_ROR && mIns[i + 5].mMode == ASMIM_IMPLIED && + !(mIns[i + 5].mLive & LIVE_CPU_REG_C)) + { + mIns[i + 0].CopyMode(mIns[i + 4]); + + mIns[i + 3].mType = ASMIT_NOP; mIns[i + 3].mMode = ASMIM_IMPLIED; + mIns[i + 4].mType = ASMIT_NOP; mIns[i + 4].mMode = ASMIM_IMPLIED; + mIns[i + 5].mType = ASMIT_NOP; mIns[i + 5].mMode = ASMIM_IMPLIED; + return true; + } + + + return false; +} + +bool NativeCodeBasicBlock::PeepHoleOptimizerIterateN(int i, int pass) +{ + if (i + 5 < mIns.Size() && + mIns[i + 0].mType == ASMIT_INY && mIns[i + 3].mType == ASMIT_DEY && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_INDIRECT_Y && + mIns[i + 4].mType == ASMIT_LDA && mIns[i + 4].mMode == ASMIM_INDIRECT_Y && + !(mIns[i + 5].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z))) + { + if (mIns[i + 5].mType == ASMIT_TAX) + { + if (mIns[i + 2].mMode == ASMIM_ZERO_PAGE && mIns[i + 2].mAddress != mIns[i + 4].mAddress && mIns[i + 2].mAddress != mIns[i + 4].mAddress + 1) + { + mIns[i + 5] = mIns[i + 2]; + mIns[i + 2].mType = ASMIT_TAX; mIns[i + 2].mMode = ASMIM_IMPLIED; + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 2].mLive |= LIVE_CPU_REG_X; + mIns[i + 3].mLive |= LIVE_CPU_REG_X; + mIns[i + 4].mLive |= LIVE_CPU_REG_X; + mIns[i + 5].mLive |= LIVE_CPU_REG_X; + mIns[i + 3].mType = ASMIT_INY; + mIns.Insert(i + 6, NativeCodeInstruction(mIns[i + 0].mIns, ASMIT_DEY)); + return true; + } + } + } + + if (i + 6 < mIns.Size() && pass > 3 && + mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_ADC && mIns[i + 1].mMode == ASMIM_IMMEDIATE_ADDRESS && mIns[i + 1].mLinkerObject && mIns[i + 1].mAddress <= 256 && + mIns[i + 2].mType == ASMIT_STA && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && + mIns[i + 3].mType == ASMIT_LDA && mIns[i + 3].mMode == ASMIM_IMMEDIATE_ADDRESS && mIns[i + 1].mLinkerObject == mIns[i + 3].mLinkerObject && + mIns[i + 4].mType == ASMIT_ADC && mIns[i + 4].mMode == ASMIM_IMMEDIATE && mIns[i + 4].mAddress == 0 && + mIns[i + 5].mType == ASMIT_STA && mIns[i + 5].mMode == ASMIM_ZERO_PAGE && mIns[i + 5].mAddress == mIns[i + 2].mAddress + 1) + { + if (mIns[i + 1].mLinkerObject->mFlags & LOBJF_NEVER_CROSS) + { + mIns[i + 4].mType = ASMIT_NOP; mIns[i + 4].mMode = ASMIM_IMPLIED; + return true; + } + + } + + if (i + 7 < mIns.Size() && pass > 3 && + mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_ADC && mIns[i + 1].mMode == ASMIM_IMMEDIATE && + mIns[i + 2].mType == ASMIT_STA && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && + mIns[i + 3].mType == ASMIT_LDA && + mIns[i + 4].mType == ASMIT_ADC && mIns[i + 4].mMode == ASMIM_IMMEDIATE && + mIns[i + 5].mType == ASMIT_STA && mIns[i + 5].mMode == ASMIM_ZERO_PAGE && mIns[i + 5].mAddress == mIns[i + 2].mAddress + 1 && + mIns[i + 6].mType == ASMIT_LDY && mIns[i + 6].mMode == ASMIM_IMMEDIATE && + mIns[i + 7].mMode == ASMIM_INDIRECT_Y && mIns[i + 7].mAddress == mIns[i + 2].mAddress && !(mIns[i + 7].mLive & LIVE_MEM)) + { + int total = mIns[i + 1].mAddress + mIns[i + 6].mAddress + 256 * mIns[i + 4].mAddress; + if (!(mIns[i + 7].mLive & LIVE_CPU_REG_Y) || !(mIns[i + 7].mLive & LIVE_CPU_REG_Z)) + { + if (mIns[i + 7].mLive & LIVE_CPU_REG_Y) + mIns.Insert(i + 8, NativeCodeInstruction(mIns[i + 0].mIns, ASMIT_LDY, ASMIM_IMMEDIATE, mIns[i + 6].mAddress)); + mIns[i + 6].mAddress = total & 255; + mIns[i + 4].mAddress = total >> 8; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + return true; + } + } + + if (i + 7 < mIns.Size() && + mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_LDA && + mIns[i + 2].mType == ASMIT_ADC && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && + mIns[i + 4].mType == ASMIT_LDA && !mIns[i + 4].SameEffectiveAddress(mIns[i + 3]) && + mIns[i + 5].mType == ASMIT_AND && mIns[i + 5].mMode == ASMIM_IMMEDIATE && + mIns[i + 6].mType == ASMIT_CLC && + mIns[i + 7].mType == ASMIT_ADC && mIns[i + 7].SameEffectiveAddress(mIns[i + 3]) && !(mIns[i + 7].mLive & (LIVE_MEM | LIVE_CPU_REG_C))) + { + NativeCodeInstruction i4(mIns[i + 4]); + NativeCodeInstruction i5(mIns[i + 5]); + NativeCodeInstruction i6(mIns[i + 6]); + + i4.mLive |= LIVE_CPU_REG_C; + i5.mLive |= LIVE_CPU_REG_C; + + mIns.Remove(i + 3, 5); + mIns.Insert(i + 1, i4); + mIns.Insert(i + 2, i5); + mIns[i + 3].mType = ASMIT_ADC; + mIns.Insert(i + 4, i6); + return true; + } + + if (i + 6 < mIns.Size()) + { + if ( + mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_IMMEDIATE_ADDRESS && (mIns[i + 1].mFlags & NCIF_LOWER) && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && + mIns[i + 4].mType == ASMIT_LDA && mIns[i + 4].mMode == ASMIM_IMMEDIATE_ADDRESS && (mIns[i + 4].mFlags & NCIF_UPPER) && mIns[i + 4].mLinkerObject == mIns[i + 1].mLinkerObject && mIns[i + 4].mAddress == mIns[i + 1].mAddress && + mIns[i + 5].mType == ASMIT_ADC && mIns[i + 5].mMode == ASMIM_IMMEDIATE && + mIns[i + 6].mType == ASMIT_STA && mIns[i + 6].mMode == ASMIM_ZERO_PAGE && !(mIns[i + 6].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Y | LIVE_CPU_REG_Z))) + { + mIns[i + 1].mAddress = mIns[i + 4].mAddress = mIns[i + 1].mAddress + mIns[i + 2].mAddress + 256 * mIns[i + 5].mAddress; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + mIns[i + 5].mType = ASMIT_NOP; mIns[i + 5].mMode = ASMIM_IMPLIED; + return true; + } + else if ( + mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_IMMEDIATE_ADDRESS && (mIns[i + 1].mFlags & NCIF_LOWER) && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && + mIns[i + 4].mType == ASMIT_LDA && mIns[i + 4].mMode == ASMIM_IMMEDIATE && + mIns[i + 5].mType == ASMIT_ADC && mIns[i + 5].mMode == ASMIM_IMMEDIATE_ADDRESS && (mIns[i + 5].mFlags & NCIF_UPPER) && mIns[i + 5].mLinkerObject == mIns[i + 1].mLinkerObject && mIns[i + 5].mAddress == mIns[i + 1].mAddress && + mIns[i + 6].mType == ASMIT_STA && mIns[i + 6].mMode == ASMIM_ZERO_PAGE && !(mIns[i + 6].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Y | LIVE_CPU_REG_Z))) + { + mIns[i + 1].mAddress = mIns[i + 5].mAddress = mIns[i + 1].mAddress + mIns[i + 2].mAddress + 256 * mIns[i + 4].mAddress; + mIns[i + 5].mType = ASMIT_LDA; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + mIns[i + 4].mType = ASMIT_NOP; mIns[i + 4].mMode = ASMIM_IMPLIED; + return true; + } + else if ( + mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_ASL && mIns[i + 1].mMode == ASMIM_IMPLIED && + mIns[i + 2].mType == ASMIT_STA && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && + mIns[i + 3].mType == ASMIT_LDA && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && + mIns[i + 4].mType == ASMIT_AND && mIns[i + 4].mMode == ASMIM_IMMEDIATE && mIns[i + 4].mAddress == 1 && + mIns[i + 5].mType == ASMIT_CLC && + mIns[i + 6].mType == ASMIT_ADC && mIns[i + 6].mMode == ASMIM_ZERO_PAGE && mIns[i + 6].mAddress == mIns[i + 2].mAddress && !(mIns[i + 6].mLive & (LIVE_MEM | LIVE_CPU_REG_C))) + { + int addr = mIns[i + 0].mAddress; + mIns[i + 0].mAddress = mIns[i + 3].mAddress; + mIns[i + 3].mAddress = addr; + mIns[i + 1].mType = ASMIT_LSR; mIns[i + 1].mLive |= LIVE_CPU_REG_C; + mIns[i + 3].mLive |= LIVE_CPU_REG_C; + mIns[i + 5].mType = ASMIT_ROL; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + mIns[i + 4].mType = ASMIT_NOP; mIns[i + 4].mMode = ASMIM_IMPLIED; + mIns[i + 6].mType = ASMIT_NOP; mIns[i + 6].mMode = ASMIM_IMPLIED; + return true; + } +#if 1 + if (pass == 0 && + mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_IMMEDIATE_ADDRESS && (mIns[i + 1].mFlags & NCIF_LOWER) && + mIns[i + 2].mType == ASMIT_ADC && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && + mIns[i + 4].mType == ASMIT_LDA && mIns[i + 4].mMode == ASMIM_IMMEDIATE_ADDRESS && (mIns[i + 4].mFlags & NCIF_UPPER) && mIns[i + 4].mLinkerObject == mIns[i + 1].mLinkerObject && mIns[i + 4].mAddress == mIns[i + 1].mAddress && + mIns[i + 5].mType == ASMIT_ADC && mIns[i + 5].mMode == ASMIM_IMMEDIATE && + mIns[i + 6].mType == ASMIT_STA && mIns[i + 6].mMode == ASMIM_ZERO_PAGE && mIns[i + 6].mAddress == mIns[i + 3].mAddress + 1 && + !(mIns[i + 6].mLive & LIVE_CPU_REG_A)) + { + mProc->ResetPatched(); + if (CheckGlobalAddressSumYPointer(this, mIns[i + 3].mAddress, mIns[i + 3].mAddress, i + 7, -1)) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_LDA; + mIns[i + 4].mType = ASMIT_NOP; mIns[i + 4].mMode = ASMIM_IMPLIED; + mIns[i + 5].mType = ASMIT_NOP; mIns[i + 5].mMode = ASMIM_IMPLIED; + mIns[i + 6].mType = ASMIT_NOP; mIns[i + 6].mMode = ASMIM_IMPLIED; + + mProc->ResetPatched(); + if (PatchGlobalAddressSumYPointer(this, mIns[i + 3].mAddress, mIns[i + 3].mAddress, i + 7, -1, mIns[i + 1].mLinkerObject, mIns[i + 1].mAddress + mIns[i + 5].mAddress * 256)) + { + CheckLive(); + return true; + } + } + else if (mIns[i + 2].mMode == ASMIM_ZERO_PAGE && mIns[i + 2].mAddress != mIns[i + 3].mAddress) + { + mProc->ResetPatched(); + if (CheckGlobalAddressSumYPointer(this, mIns[i + 3].mAddress, mIns[i + 2].mAddress, i + 7, -1)) + { + mProc->ResetPatched(); + if (PatchGlobalAddressSumYPointer(this, mIns[i + 3].mAddress, mIns[i + 2].mAddress, i + 7, -1, mIns[i + 1].mLinkerObject, mIns[i + 1].mAddress + mIns[i + 5].mAddress * 256)) + { + CheckLive(); + return true; + } + } + } + } + +#endif +#if 1 + if (pass == 0 && + mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_LDA && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE_ADDRESS && (mIns[i + 2].mFlags & NCIF_LOWER) && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && + mIns[i + 4].mType == ASMIT_LDA && mIns[i + 4].mMode == ASMIM_IMMEDIATE && + mIns[i + 5].mType == ASMIT_ADC && mIns[i + 5].mMode == ASMIM_IMMEDIATE_ADDRESS && (mIns[i + 5].mFlags & NCIF_UPPER) && mIns[i + 5].mLinkerObject == mIns[i + 2].mLinkerObject && mIns[i + 5].mAddress == mIns[i + 2].mAddress && + mIns[i + 6].mType == ASMIT_STA && mIns[i + 6].mMode == ASMIM_ZERO_PAGE && mIns[i + 6].mAddress == mIns[i + 3].mAddress + 1 && + !(mIns[i + 6].mLive & LIVE_CPU_REG_A)) + { + mProc->ResetPatched(); + if (CheckGlobalAddressSumYPointer(this, mIns[i + 3].mAddress, mIns[i + 3].mAddress, i + 7, -1)) + { +// mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + mIns[i + 4].mType = ASMIT_NOP; mIns[i + 4].mMode = ASMIM_IMPLIED; + mIns[i + 5].mType = ASMIT_NOP; mIns[i + 5].mMode = ASMIM_IMPLIED; + mIns[i + 6].mType = ASMIT_NOP; mIns[i + 6].mMode = ASMIM_IMPLIED; + + mProc->ResetPatched(); + if (PatchGlobalAddressSumYPointer(this, mIns[i + 3].mAddress, mIns[i + 3].mAddress, i + 7, -1, mIns[i + 2].mLinkerObject, mIns[i + 2].mAddress + mIns[i + 4].mAddress * 256)) + { + CheckLive(); + return true; + } + } + else if (mIns[i + 1].mMode == ASMIM_ZERO_PAGE && mIns[i + 1].mAddress != mIns[i + 3].mAddress) + { + mProc->ResetPatched(); + if (CheckGlobalAddressSumYPointer(this, mIns[i + 3].mAddress, mIns[i + 1].mAddress, i + 7, -1)) + { + mProc->ResetPatched(); + if (PatchGlobalAddressSumYPointer(this, mIns[i + 3].mAddress, mIns[i + 1].mAddress, i + 7, -1, mIns[i + 2].mLinkerObject, mIns[i + 2].mAddress + mIns[i + 4].mAddress * 256)) + { + CheckLive(); + return true; + } + } + } + } + +#endif +#if 1 + if (pass == 0 && i + 7 < mIns.Size() && + mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_IMMEDIATE_ADDRESS && (mIns[i + 0].mFlags & NCIF_LOWER) && + mIns[i + 1].mType == ASMIT_CLC && + mIns[i + 2].mType == ASMIT_LDY && mIns[i + 2].mMode == ASMIM_IMMEDIATE && + mIns[i + 3].mType == ASMIT_ADC && mIns[i + 3].mMode == ASMIM_INDIRECT_Y && + mIns[i + 4].mType == ASMIT_STA && mIns[i + 4].mMode == ASMIM_ZERO_PAGE && + mIns[i + 5].mType == ASMIT_LDA && mIns[i + 5].mMode == ASMIM_IMMEDIATE_ADDRESS && (mIns[i + 5].mFlags & NCIF_UPPER) && mIns[i + 5].mLinkerObject == mIns[i + 0].mLinkerObject && mIns[i + 5].mAddress == mIns[i + 0].mAddress && + mIns[i + 6].mType == ASMIT_ADC && mIns[i + 6].mMode == ASMIM_IMMEDIATE && + mIns[i + 7].mType == ASMIT_STA && mIns[i + 7].mMode == ASMIM_ZERO_PAGE && mIns[i + 7].mAddress == mIns[i + 4].mAddress + 1 && + !(mIns[i + 7].mLive & LIVE_CPU_REG_A)) + { + mProc->ResetPatched(); + if (CheckGlobalAddressSumYPointer(this, mIns[i + 4].mAddress, mIns[i + 4].mAddress, i + 8, mIns[i + 2].mAddress)) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 3].mType = ASMIT_LDA; + mIns[i + 5].mType = ASMIT_NOP; mIns[i + 5].mMode = ASMIM_IMPLIED; + mIns[i + 6].mType = ASMIT_NOP; mIns[i + 6].mMode = ASMIM_IMPLIED; + mIns[i + 7].mType = ASMIT_NOP; mIns[i + 7].mMode = ASMIM_IMPLIED; + + mProc->ResetPatched(); + if (PatchGlobalAddressSumYPointer(this, mIns[i + 4].mAddress, mIns[i + 4].mAddress, i + 8, mIns[i + 2].mAddress, mIns[i + 0].mLinkerObject, mIns[i + 0].mAddress + mIns[i + 6].mAddress * 256)) + { + CheckLive(); + return true; + } + } + } + +#endif +#if 1 + if (pass == 0 && + mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_IMMEDIATE && + mIns[i + 2].mType == ASMIT_ADC && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && + mIns[i + 4].mType == ASMIT_LDA && mIns[i + 4].mMode == ASMIM_IMMEDIATE && + mIns[i + 5].mType == ASMIT_ADC && mIns[i + 5].mMode == ASMIM_IMMEDIATE && + mIns[i + 6].mType == ASMIT_STA && mIns[i + 6].mMode == ASMIM_ZERO_PAGE && mIns[i + 6].mAddress == mIns[i + 3].mAddress + 1 && + !(mIns[i + 6].mLive & LIVE_CPU_REG_A)) + { + mProc->ResetPatched(); + if (CheckGlobalAddressSumYPointer(this, mIns[i + 3].mAddress, mIns[i + 3].mAddress, i + 7, -1)) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_LDA; + mIns[i + 4].mType = ASMIT_NOP; mIns[i + 4].mMode = ASMIM_IMPLIED; + mIns[i + 5].mType = ASMIT_NOP; mIns[i + 5].mMode = ASMIM_IMPLIED; + mIns[i + 6].mType = ASMIT_NOP; mIns[i + 6].mMode = ASMIM_IMPLIED; + + mProc->ResetPatched(); + if (PatchGlobalAddressSumYPointer(this, mIns[i + 3].mAddress, mIns[i + 3].mAddress, i + 7, -1, nullptr, mIns[i + 1].mAddress + 256 * mIns[i + 4].mAddress + mIns[i + 5].mAddress * 25)) + { + CheckLive(); + return true; + } + } + } + +#endif + +#if 1 + if (mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && mIns[i + 3].mAddress == mIns[i + 2].mAddress && + mIns[i + 4].mType == ASMIT_LDA && mIns[i + 4].mMode == ASMIM_ZERO_PAGE && + mIns[i + 5].mType == ASMIT_ADC && mIns[i + 5].mMode == ASMIM_ZERO_PAGE && mIns[i + 5].mAddress == mIns[i + 2].mAddress + 1 && + mIns[i + 6].mType == ASMIT_STA && mIns[i + 6].mMode == ASMIM_ZERO_PAGE && mIns[i + 6].mAddress == mIns[i + 5].mAddress && + !(mIns[i + 6].mLive & LIVE_CPU_REG_A)) + { + int yval = RetrieveYValue(i); + mProc->ResetPatched(); + if (CheckForwardSumYPointer(this, mIns[i + 3].mAddress, mIns[i + 3].mAddress, mIns[i + 1], i + 7, yval, 0)) + { + mProc->ResetPatched(); + if (PatchForwardSumYPointer(this, mIns[i + 3].mAddress, mIns[i + 3].mAddress, mIns[i + 1], i + 7, yval)) + { + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + mIns[i + 3].mType = ASMIT_NOP; mIns[i + 3].mMode = ASMIM_IMPLIED; + + CheckLive(); + if (mTrueJump) + mTrueJump->CheckLive(); + if (mFalseJump) + mFalseJump->CheckLive(); + + return true; + } + } + } +#endif +#if 1 + if (mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_IMMEDIATE_ADDRESS && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && mIns[i + 2].mAddress != mIns[i + 3].mAddress && + mIns[i + 4].mType == ASMIT_LDA && mIns[i + 4].mMode == ASMIM_IMMEDIATE_ADDRESS && mIns[i + 4].mLinkerObject == mIns[i + 1].mLinkerObject && mIns[i + 4].mAddress == mIns[i + 1].mAddress && + mIns[i + 5].mType == ASMIT_ADC && mIns[i + 5].mMode == ASMIM_ZERO_PAGE && mIns[i + 5].mAddress == mIns[i + 2].mAddress + 1 && + mIns[i + 6].mType == ASMIT_STA && mIns[i + 6].mMode == ASMIM_ZERO_PAGE && mIns[i + 6].mAddress == mIns[i + 3].mAddress + 1 && + !(mIns[i + 6].mLive & LIVE_CPU_REG_A)) + { + int yval = RetrieveYValue(i); + mProc->ResetPatched(); + if (CheckForwardSumYPointer(this, mIns[i + 3].mAddress, mIns[i + 3].mAddress, mIns[i + 2], i + 7, yval, 0)) + { + mProc->ResetPatched(); + if (PatchForwardSumYPointer(this, mIns[i + 3].mAddress, mIns[i + 3].mAddress, mIns[i + 2], i + 7, yval)) + { + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + + CheckLive(); + if (mTrueJump) + mTrueJump->CheckLive(); + if (mFalseJump) + mFalseJump->CheckLive(); + + return true; + } + } + } +#endif +#if 1 + if (pass == 0 && + mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_ADC && mIns[i + 1].mMode == ASMIM_IMMEDIATE_ADDRESS && (mIns[i + 1].mFlags & NCIF_LOWER) && + mIns[i + 2].mType == ASMIT_STA && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && + mIns[i + 3].mType == ASMIT_LDA && mIns[i + 3].mMode == ASMIM_IMMEDIATE_ADDRESS && (mIns[i + 3].mFlags & NCIF_UPPER) && (mIns[i + 3].mLinkerObject == mIns[i + 1].mLinkerObject) && + mIns[i + 4].mType == ASMIT_ADC && mIns[i + 4].mMode == ASMIM_ZERO_PAGE && mIns[i + 4].mAddress == mIns[i + 2].mAddress + 1 && + mIns[i + 5].mType == ASMIT_STA && mIns[i + 5].mMode == ASMIM_ZERO_PAGE && mIns[i + 5].mAddress == mIns[i + 4].mAddress && + !(mIns[i + 5].mLive & LIVE_CPU_REG_A)) + { + int yval = RetrieveYValue(i); + + mProc->ResetPatched(); + if (CheckForwardSumYPointer(this, mIns[i + 2].mAddress, mIns[i + 2].mAddress, mIns[i + 1], i + 6, yval, 0)) + { + mProc->ResetPatched(); + if (PatchForwardSumYPointer(this, mIns[i + 2].mAddress, mIns[i + 2].mAddress, mIns[i + 1], i + 6, yval)) + { + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + CheckLive(); + return true; + } + } + } +#endif + +#if 1 + if (mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && + mIns[i + 4].mType == ASMIT_LDA && mIns[i + 4].mMode == ASMIM_ZERO_PAGE && + mIns[i + 5].mType == ASMIT_ADC && mIns[i + 5].mMode == ASMIM_ZERO_PAGE && + mIns[i + 6].mType == ASMIT_STA && mIns[i + 6].mMode == ASMIM_ZERO_PAGE && mIns[i + 6].mAddress == mIns[i + 3].mAddress + 1 && + !(mIns[i + 6].mLive & LIVE_CPU_REG_A)) + { + int yval = RetrieveYValue(i); + + bool subs1 = mIns[i + 3].mAddress != mIns[i + 1].mAddress; + bool subs2 = mIns[i + 3].mAddress != mIns[i + 2].mAddress; + + if (subs1 && subs2) + { + if (mLoopHead) + { + if (ChangesZeroPage(mIns[i + 1].mAddress)) + subs2 = false; + else if (ChangesZeroPage(mIns[i + 2].mAddress)) + subs1 = false; + } + } + + mProc->ResetPatched(); + if (subs1 && CheckForwardSumYPointer(this, mIns[i + 3].mAddress, mIns[i + 3].mAddress, mIns[i + 1], i + 7, yval, 0)) + { + mProc->ResetPatched(); + if (PatchForwardSumYPointer(this, mIns[i + 3].mAddress, mIns[i + 3].mAddress, mIns[i + 1], i + 7, yval)) + { + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_LDA; + + CheckLive(); + if (mTrueJump) + mTrueJump->CheckLive(); + if (mFalseJump) + mFalseJump->CheckLive(); + + return true; + } + + } + else if (subs2) + { + mProc->ResetPatched(); + if (CheckForwardSumYPointer(this, mIns[i + 3].mAddress, mIns[i + 3].mAddress, mIns[i + 2], i + 7, yval, 0)) + { + mProc->ResetPatched(); + if (PatchForwardSumYPointer(this, mIns[i + 3].mAddress, mIns[i + 3].mAddress, mIns[i + 2], i + 7, yval)) + { + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + + CheckLive(); + if (mTrueJump) + mTrueJump->CheckLive(); + if (mFalseJump) + mFalseJump->CheckLive(); + return true; + } + } + } + } +#endif +#if 1 + if (mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + mIns[i + 2].mType == ASMIT_ADC && (mIns[i + 2].mMode == ASMIM_ZERO_PAGE || mIns[i + 2].mMode == ASMIM_ABSOLUTE) && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && /*mIns[i + 3].mAddress != mIns[i + 1].mAddress && mIns[i + 3].mAddress != mIns[i + 2].mAddress && */ + mIns[i + 4].mType == ASMIT_LDA && mIns[i + 4].mMode == ASMIM_ZERO_PAGE && mIns[i + 4].mAddress == mIns[i + 1].mAddress + 1 && + mIns[i + 5].mType == ASMIT_ADC && mIns[i + 5].mMode == ASMIM_IMMEDIATE && mIns[i + 5].mAddress == 0 && + mIns[i + 6].mType == ASMIT_STA && mIns[i + 6].mMode == ASMIM_ZERO_PAGE && mIns[i + 6].mAddress == mIns[i + 3].mAddress + 1 && + !(mIns[i + 6].mLive & LIVE_CPU_REG_A)) + { + int yval = RetrieveYValue(i); + mProc->ResetPatched(); + if (CheckForwardSumYPointer(this, mIns[i + 3].mAddress, mIns[i + 1].mAddress, mIns[i + 2], i + 7, yval, intmax(3, 255 - mIns[i + 2].mMaxVal))) + { + NativeCodeInstruction inins(mIns[i + 2]); + mProc->ResetPatched(); + if (PatchForwardSumYPointer(this, mIns[i + 3].mAddress, mIns[i + 1].mAddress, inins, i + 7, yval)) + { + if (mIns[i + 3].mAddress == mIns[i + 1].mAddress || mIns[i + 3].mAddress == mIns[i + 2].mAddress) + { + for (int j = 0; j < 7; j++) + { + mIns[i + j].mType = ASMIT_NOP; mIns[i + j].mMode = ASMIM_IMPLIED; + } + } + + CheckLive(); + if (mTrueJump) + mTrueJump->CheckLive(); + if (mFalseJump) + mFalseJump->CheckLive(); + return true; + } + } + } +#endif +#if 1 + if (mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + mIns[i + 2].mType == ASMIT_ADC && (mIns[i + 2].mMode == ASMIM_ZERO_PAGE || mIns[i + 2].mMode == ASMIM_ABSOLUTE) && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && mIns[i + 3].mAddress != mIns[i + 2].mAddress && + mIns[i + 4].mType == ASMIT_LDA && mIns[i + 4].mMode == ASMIM_ZERO_PAGE && + mIns[i + 5].mType == ASMIT_ADC && mIns[i + 5].mMode == ASMIM_IMMEDIATE && mIns[i + 5].mAddress == 0 && + mIns[i + 6].mType == ASMIT_STA && mIns[i + 6].mMode == ASMIM_ZERO_PAGE && mIns[i + 6].mAddress == mIns[i + 3].mAddress + 1 && + !(mIns[i + 6].mLive & LIVE_CPU_REG_A)) + { + int yval = RetrieveYValue(i); + mProc->ResetPatched(); + if (CheckForwardSumYPointer(this, mIns[i + 3].mAddress, mIns[i + 1].mAddress, mIns[i + 2], i + 7, yval, 3)) + { + mProc->ResetPatched(); + if (PatchForwardSumYPointer(this, mIns[i + 3].mAddress, mIns[i + 3].mAddress, mIns[i + 2], i + 7, yval)) + { + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + mIns[i + 5].mType = ASMIT_NOP; mIns[i + 5].mMode = ASMIM_IMPLIED; + + CheckLive(); + if (mTrueJump) + mTrueJump->CheckLive(); + if (mFalseJump) + mFalseJump->CheckLive(); + return true; + } + } + else if (mIns[i + 3].mAddress != mIns[i + 1].mAddress) + { + mProc->ResetPatched(); + if (CheckForwardSumYPointer(this, mIns[i + 3].mAddress, mIns[i + 1].mAddress, mIns[i + 3], i + 7, yval, 3)) + { + mProc->ResetPatched(); + if (PatchForwardSumYPointer(this, mIns[i + 3].mAddress, mIns[i + 1].mAddress, mIns[i + 3], i + 7, yval)) + { + mIns[i + 1].CopyMode(mIns[i + 2]); + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + mIns[i + 4].mType = ASMIT_NOP; mIns[i + 4].mMode = ASMIM_IMPLIED; + mIns[i + 5].mType = ASMIT_NOP; mIns[i + 5].mMode = ASMIM_IMPLIED; + mIns[i + 6].mType = ASMIT_NOP; mIns[i + 6].mMode = ASMIM_IMPLIED; + + CheckLive(); + if (mTrueJump) + mTrueJump->CheckLive(); + if (mFalseJump) + mFalseJump->CheckLive(); + return true; + } + } + } + } +#endif +#if 1 + if ( + mIns[i + 0].mType == ASMIT_STA && (mIns[i + 0].mMode == ASMIM_ZERO_PAGE || mIns[i + 0].mMode == ASMIM_ABSOLUTE) && + mIns[i + 1].mType == ASMIT_CLC && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && mIns[i + 3].mAddress != mIns[i + 0].mAddress && + mIns[i + 4].mType == ASMIT_LDA && mIns[i + 4].mMode == ASMIM_ZERO_PAGE && mIns[i + 4].mAddress == mIns[i + 2].mAddress + 1 && + mIns[i + 5].mType == ASMIT_ADC && mIns[i + 5].mMode == ASMIM_IMMEDIATE && mIns[i + 5].mAddress == 0 && + mIns[i + 6].mType == ASMIT_STA && mIns[i + 6].mMode == ASMIM_ZERO_PAGE && mIns[i + 6].mAddress == mIns[i + 3].mAddress + 1 && + !(mIns[i + 6].mLive & LIVE_CPU_REG_A)) + { + int yval = RetrieveYValue(i); + mProc->ResetPatched(); + if (CheckForwardSumYPointer(this, mIns[i + 3].mAddress, mIns[i + 2].mAddress, mIns[i + 0], i + 7, yval, 3)) + { + mProc->ResetPatched(); + if (PatchForwardSumYPointer(this, mIns[i + 3].mAddress, mIns[i + 2].mAddress, mIns[i + 0], i + 7, yval)) + { + + if (mIns[i + 3].mAddress == mIns[i + 2].mAddress) + { + for (int j = 1; j < 7; j++) + { + mIns[i + j].mType = ASMIT_NOP; mIns[i + j].mMode = ASMIM_IMPLIED; + } + } + + CheckLive(); + if (mTrueJump) + mTrueJump->CheckLive(); + if (mFalseJump) + mFalseJump->CheckLive(); + return true; + } + } + } +#endif +#if 1 + if (mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + mIns[i + 2].mType == ASMIT_ADC && (mIns[i + 2].mMode == ASMIM_ZERO_PAGE || mIns[i + 2].mMode == ASMIM_ABSOLUTE) && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && + mIns[i + 4].mType == ASMIT_LDA && mIns[i + 4].mMode == ASMIM_ZERO_PAGE && mIns[i + 4].mAddress == mIns[i + 1].mAddress + 1 && + mIns[i + 5].mType == ASMIT_ADC && mIns[i + 5].mMode == ASMIM_IMMEDIATE && + mIns[i + 6].mType == ASMIT_STA && mIns[i + 6].mMode == ASMIM_ZERO_PAGE && mIns[i + 6].mAddress == mIns[i + 3].mAddress + 1 && + mIns[i + 2].mAddress != mIns[i + 3].mAddress && + !(mIns[i + 6].mLive & LIVE_CPU_REG_A)) + { + int yval = RetrieveYValue(i); + mProc->ResetPatched(); + if (CheckForwardSumYPointer(this, mIns[i + 3].mAddress, mIns[i + 3].mAddress, mIns[i + 2], i + 7, yval, intmax(3, 255 - mIns[i + 2].mMaxVal))) + { + mProc->ResetPatched(); + if (PatchForwardSumYPointer(this, mIns[i + 3].mAddress, mIns[i + 3].mAddress, mIns[i + 2], i + 7, yval)) + { + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + + CheckLive(); + if (mTrueJump) + mTrueJump->CheckLive(); + if (mFalseJump) + mFalseJump->CheckLive(); + return true; + } + } + } +#endif +#if 1 + if (mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && + mIns[i + 4].mType == ASMIT_LDA && mIns[i + 4].mMode == ASMIM_ZERO_PAGE && mIns[i + 4].mAddress == mIns[i + 1].mAddress + 1 && + mIns[i + 5].mType == ASMIT_ADC && mIns[i + 5].mMode == ASMIM_IMMEDIATE && + mIns[i + 6].mType == ASMIT_STA && mIns[i + 6].mMode == ASMIM_ZERO_PAGE && mIns[i + 6].mAddress == mIns[i + 3].mAddress + 1 && + mIns[i + 3].mAddress != BC_REG_STACK && + !(mIns[i + 6].mLive & LIVE_CPU_REG_A)) + { + int yval = RetrieveYValue(i); + mProc->ResetPatched(); + + if (CheckForwardSumYPointer(this, mIns[i + 3].mAddress, mIns[i + 3].mAddress, mIns[i + 2], i + 7, yval, intmax(3, 255 - mIns[i + 2].mMaxVal))) + { + mProc->ResetPatched(); + if (PatchForwardSumYPointer(this, mIns[i + 3].mAddress, mIns[i + 3].mAddress, mIns[i + 2], i + 7, yval)) + { + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + + CheckLive(); + if (mTrueJump) + mTrueJump->CheckLive(); + if (mFalseJump) + mFalseJump->CheckLive(); + return true; + } + } + } +#endif +#if 1 + if (mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_ADC && mIns[i + 1].mMode == ASMIM_IMMEDIATE && + mIns[i + 2].mType == ASMIT_STA && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && + mIns[i + 3].mType == ASMIT_LDA && + mIns[i + 4].mType == ASMIT_STA && mIns[i + 4].mMode == ASMIM_ZERO_PAGE && + mIns[i + 5].mType == ASMIT_ADC && mIns[i + 5].mMode == ASMIM_IMMEDIATE && + mIns[i + 6].mType == ASMIT_STA && mIns[i + 6].mMode == ASMIM_ZERO_PAGE && mIns[i + 6].mAddress == mIns[i + 2].mAddress + 1 && + mIns[i + 2].mAddress != BC_REG_STACK && + !(mIns[i + 6].mLive & LIVE_CPU_REG_A)) + { + int yval = RetrieveYValue(i); + mProc->ResetPatched(); + + if (CheckForwardSumYPointer(this, mIns[i + 2].mAddress, mIns[i + 2].mAddress, mIns[i + 1], i + 7, yval, 3)) + { + mProc->ResetPatched(); + if (PatchForwardSumYPointer(this, mIns[i + 2].mAddress, mIns[i + 2].mAddress, mIns[i + 1], i + 7, yval)) + { + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + + CheckLive(); + if (mTrueJump) + mTrueJump->CheckLive(); + if (mFalseJump) + mFalseJump->CheckLive(); + return true; + } + } + } +#endif +#if 1 + if ( + mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_ADC && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + mIns[i + 2].mType == ASMIT_STA && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && mIns[i + 2].mAddress != mIns[i + 1].mAddress && + mIns[i + 3].mType == ASMIT_LDA && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && mIns[i + 3].mAddress == mIns[i + 1].mAddress + 1 && !(mIns[i + 3].mLive & LIVE_MEM) && + mIns[i + 4].mType == ASMIT_ADC && mIns[i + 4].mMode == ASMIM_ZERO_PAGE && + mIns[i + 5].mType == ASMIT_STA && mIns[i + 5].mMode == ASMIM_ZERO_PAGE && mIns[i + 5].mAddress == mIns[i + 2].mAddress + 1 && + !(mIns[i + 5].mLive & LIVE_CPU_REG_A)) + { + int yval = RetrieveYValue(i); + mProc->ResetPatched(); + if (CheckForwardSumYPointer(this, mIns[i + 2].mAddress, mIns[i + 1].mAddress, mIns[i + 2], i + 6, yval, 0)) + { + mProc->ResetPatched(); + if (PatchForwardSumYPointer(this, mIns[i + 2].mAddress, mIns[i + 1].mAddress, mIns[i + 2], i + 6, yval)) + { + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 5].mAddress = mIns[i + 3].mAddress; + + CheckLive(); + if (mTrueJump) + mTrueJump->CheckLive(); + if (mFalseJump) + mFalseJump->CheckLive(); + return true; + } + } + } +#endif +#if 1 + if ( + mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_ADC && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + mIns[i + 2].mType == ASMIT_STA && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && mIns[i + 2].mAddress != mIns[i + 1].mAddress && + mIns[i + 3].mType == ASMIT_LDA && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && mIns[i + 3].mAddress == mIns[i + 1].mAddress + 1 && + mIns[i + 4].mType == ASMIT_ADC && mIns[i + 4].mMode == ASMIM_IMMEDIATE && mIns[i + 4].mAddress == 0 && + mIns[i + 5].mType == ASMIT_STA && mIns[i + 5].mMode == ASMIM_ZERO_PAGE && mIns[i + 5].mAddress == mIns[i + 2].mAddress + 1 && + !(mIns[i + 5].mLive & LIVE_CPU_REG_A)) + { + int yval = RetrieveYValue(i); + mProc->ResetPatched(); + if (CheckForwardSumYPointer(this, mIns[i + 2].mAddress, mIns[i + 1].mAddress, mIns[i + 2], i + 6, yval, 3)) + { + mProc->ResetPatched(); + if (PatchForwardSumYPointer(this, mIns[i + 2].mAddress, mIns[i + 1].mAddress, mIns[i + 2], i + 6, yval)) + { + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + + CheckLive(); + if (mTrueJump) + mTrueJump->CheckLive(); + if (mFalseJump) + mFalseJump->CheckLive(); + return true; + } + } + } +#endif +#if 1 + if ( + mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_ADC && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + mIns[i + 2].mType == ASMIT_STA && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && mIns[i + 2].mAddress == mIns[i + 1].mAddress && + mIns[i + 3].mType == ASMIT_LDA && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && mIns[i + 3].mAddress == mIns[i + 1].mAddress + 1 && + mIns[i + 4].mType == ASMIT_ADC && mIns[i + 4].mMode == ASMIM_IMMEDIATE && mIns[i + 4].mAddress == 0 && + mIns[i + 5].mType == ASMIT_STA && mIns[i + 5].mMode == ASMIM_ZERO_PAGE && mIns[i + 5].mAddress == mIns[i + 2].mAddress + 1) + { + int yval = RetrieveYValue(i); + int reg = FindFreeAccu(i); + if (reg >= 0) + { + NativeCodeInstruction iins(mIns[i + 0].mIns, ASMIT_STA, ASMIM_ZERO_PAGE, reg); + + mProc->ResetPatched(); + if (CheckForwardSumYPointer(this, mIns[i + 1].mAddress, mIns[i + 1].mAddress, iins, i + 6, yval, 3)) + { + mIns[i + 0] = iins; + for (int j = 1; j < 6; j++) + { + mIns[i + j].mType = ASMIT_NOP; mIns[i + j].mMode = ASMIM_IMPLIED; + } + + mProc->ResetPatched(); + if (PatchForwardSumYPointer(this, mIns[i + 1].mAddress, mIns[i + 1].mAddress, iins, i + 6, yval)) + { + CheckLive(); + if (mTrueJump) + mTrueJump->CheckLive(); + if (mFalseJump) + mFalseJump->CheckLive(); + return true; + } + } + } + } +#endif +#if 1 + if ( + mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_ADC && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + mIns[i + 2].mType == ASMIT_STA && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && mIns[i + 2].mAddress == mIns[i + 1].mAddress && + mIns[i + 3].mType == ASMIT_LDA && (mIns[i + 3].mMode == ASMIM_ABSOLUTE_X || mIns[i + 3].mMode == ASMIM_ABSOLUTE_Y || mIns[i + 3].mMode == ASMIM_ABSOLUTE) && + mIns[i + 4].mType == ASMIT_ADC && mIns[i + 4].mMode == ASMIM_IMMEDIATE && mIns[i + 4].mAddress == 0 && + mIns[i + 5].mType == ASMIT_STA && mIns[i + 5].mMode == ASMIM_ZERO_PAGE && mIns[i + 5].mAddress == mIns[i + 2].mAddress + 1) + { + int yval = RetrieveYValue(i); + int reg = FindFreeAccu(i); + if (reg >= 0) + { + NativeCodeInstruction iins(mIns[i + 0].mIns, ASMIT_STA, ASMIM_ZERO_PAGE, reg); + + mProc->ResetPatched(); + if (CheckForwardSumYPointer(this, mIns[i + 1].mAddress, mIns[i + 1].mAddress, iins, i + 6, yval, 3)) + { + mIns[i + 0] = iins; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + mIns[i + 4].mType = ASMIT_NOP; mIns[i + 4].mMode = ASMIM_IMPLIED; + + mProc->ResetPatched(); + if (PatchForwardSumYPointer(this, mIns[i + 1].mAddress, mIns[i + 1].mAddress, iins, i + 6, yval)) + { + CheckLive(); + if (mTrueJump) + mTrueJump->CheckLive(); + if (mFalseJump) + mFalseJump->CheckLive(); + return true; + } + } + } + } +#endif +#if 1 + if ( + mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_ADC && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + mIns[i + 2].mType == ASMIT_STA && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && + mIns[i + 3].mType == ASMIT_LDA && (mIns[i + 3].mMode == ASMIM_ABSOLUTE_X || mIns[i + 3].mMode == ASMIM_ABSOLUTE_Y || mIns[i + 3].mMode == ASMIM_ABSOLUTE) && + mIns[i + 4].mType == ASMIT_ADC && mIns[i + 4].mMode == ASMIM_IMMEDIATE && mIns[i + 4].mAddress == 0 && + mIns[i + 5].mType == ASMIT_STA && mIns[i + 5].mMode == ASMIM_ZERO_PAGE && mIns[i + 5].mAddress == mIns[i + 2].mAddress + 1 && + mIns[i + 1].mAddress != mIns[i + 2].mAddress && mIns[i + 1].mAddress != mIns[i + 5].mAddress) + { + int yval = RetrieveYValue(i); + mProc->ResetPatched(); + if (CheckForwardSumYPointer(this, mIns[i + 2].mAddress, mIns[i + 2].mAddress, mIns[i + 1], i + 6, yval, 3)) + { + mProc->ResetPatched(); + if (PatchForwardSumYPointer(this, mIns[i + 2].mAddress, mIns[i + 2].mAddress, mIns[i + 1], i + 6, yval)) + { + mIns[i + 0].mType = ASMIT_NOP; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 4].mType = ASMIT_NOP; mIns[i + 4].mMode = ASMIM_IMPLIED; + + CheckLive(); + if (mTrueJump) + mTrueJump->CheckLive(); + if (mFalseJump) + mFalseJump->CheckLive(); + return true; + } + } + } +#endif +#if 1 + if ( + mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_ADC && (mIns[i + 1].mMode == ASMIM_ABSOLUTE_X || mIns[i + 1].mMode == ASMIM_ABSOLUTE_Y || mIns[i + 1].mMode == ASMIM_ABSOLUTE) && + mIns[i + 2].mType == ASMIT_STA && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && + mIns[i + 3].mType == ASMIT_LDA && (mIns[i + 3].mMode == ASMIM_ABSOLUTE_X || mIns[i + 3].mMode == ASMIM_ABSOLUTE_Y || mIns[i + 3].mMode == ASMIM_ABSOLUTE) && + mIns[i + 4].mType == ASMIT_ADC && mIns[i + 4].mMode == ASMIM_IMMEDIATE && mIns[i + 4].mAddress == 0 && + mIns[i + 5].mType == ASMIT_STA && mIns[i + 5].mMode == ASMIM_ZERO_PAGE && mIns[i + 5].mAddress == mIns[i + 2].mAddress + 1) + { + int yval = RetrieveYValue(i); + int reg = FindFreeAccu(i); + if (reg >= 0) + { + NativeCodeInstruction iins(mIns[i + 0].mIns, ASMIT_STA, ASMIM_ZERO_PAGE, reg); + + mProc->ResetPatched(); + if (CheckForwardSumYPointer(this, mIns[i + 2].mAddress, mIns[i + 2].mAddress, iins, i + 6, yval, 3)) + { + mIns[i + 0] = iins; + mIns[i + 1].mType = ASMIT_LDA; + mIns[i + 4].mType = ASMIT_NOP; mIns[i + 4].mMode = ASMIM_IMPLIED; + + mProc->ResetPatched(); + if (PatchForwardSumYPointer(this, mIns[i + 2].mAddress, mIns[i + 2].mAddress, iins, i + 6, yval)) + { + CheckLive(); + if (mTrueJump) + mTrueJump->CheckLive(); + if (mFalseJump) + mFalseJump->CheckLive(); + return true; + } + } + } + } +#endif +#if 1 + if ( + mLoopHead && + mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_STA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && mIns[i + 1].mAddress != mIns[i + 0].mAddress && + mIns[i + 2].mType == ASMIT_CLC && + mIns[i + 3].mType == ASMIT_LDA && + mIns[i + 4].mType == ASMIT_ADC && + mIns[i + 5].mType == ASMIT_STA && mIns[i + 5].mMode == ASMIM_ZERO_PAGE && mIns[i + 5].mAddress == mIns[i + 1].mAddress + 1) + { + int yval = RetrieveYValue(i); + mProc->ResetPatched(); + if (CheckForwardLowYPointer(this, mIns[i + 1].mAddress, mIns[i + 0].mAddress, i + 6, yval)) + { + mProc->ResetPatched(); + if (PatchForwardLowYPointer(this, mIns[i + 1].mAddress, mIns[i + 0].mAddress, i + 6, yval)) + { + mIns[i + 0].mMode = ASMIM_IMMEDIATE; + mIns[i + 0].mAddress = 0; + + CheckLive(); + if (mTrueJump) + mTrueJump->CheckLive(); + if (mFalseJump) + mFalseJump->CheckLive(); + return true; + } + } + } +#endif + +#if 1 + if ( + mIns[i + 0].mType == ASMIT_LDY && mIns[i + 0].mMode == ASMIM_IMMEDIATE && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_INDIRECT_Y && + mIns[i + 2].mType == ASMIT_CLC && + mIns[i + 3].mType == ASMIT_LDY && mIns[i + 3].mMode == ASMIM_IMMEDIATE && mIns[i + 3].mAddress != mIns[i + 0].mAddress && + mIns[i + 4].mType == ASMIT_ADC && mIns[i + 4].mMode == ASMIM_INDIRECT_Y && !(mIns[i + 4].mLive & LIVE_CPU_REG_Y)) + { + int j = i + 5; + while (j < mIns.Size() && !mIns[j].ChangesYReg()) + j++; + if (j < mIns.Size() && mIns[j].mType == ASMIT_LDY && mIns[j].mMode == ASMIM_IMMEDIATE && mIns[j].mAddress == mIns[i + 0].mAddress) + { + int reg = mIns[i + 1].mAddress; mIns[i + 1].mAddress = mIns[i + 4].mAddress; mIns[i + 4].mAddress = reg; + int yr = mIns[i + 0].mAddress; mIns[i + 0].mAddress = mIns[i + 3].mAddress; mIns[i + 3].mAddress = yr; + mIns[i + 1].mLive |= LIVE_MEM; + mIns[i + 4].mLive |= LIVE_MEM; + mIns[j].mType = ASMIT_NOP; mIns[j].mMode = ASMIM_IMPLIED; + while (j > i) + { + j--; + mIns[j].mLive |= LIVE_CPU_REG_Y; + } + return true; + } + } +#endif + } + +#if 1 + if (i + 7 < mIns.Size() && + mIns[i + 0].mType == ASMIT_STA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_CLC && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && mIns[i + 2].mAddress == mIns[i + 3].mAddress && + mIns[i + 4].mType == ASMIT_LDA && + mIns[i + 5].mType == ASMIT_STA && mIns[i + 5].mMode == ASMIM_ZERO_PAGE && mIns[i + 5].mAddress != mIns[i + 0].mAddress && + mIns[i + 6].mType == ASMIT_ADC && mIns[i + 6].mMode == ASMIM_ZERO_PAGE && mIns[i + 6].mAddress == mIns[i + 2].mAddress + 1 && + mIns[i + 7].mType == ASMIT_STA && mIns[i + 7].mMode == ASMIM_ZERO_PAGE && mIns[i + 6].mAddress == mIns[i + 7].mAddress && + !(mIns[i + 7].mLive & LIVE_CPU_REG_A)) + { + int yval = RetrieveYValue(i); + mProc->ResetPatched(); + if (CheckForwardSumYPointer(this, mIns[i + 2].mAddress, mIns[i + 2].mAddress, mIns[i + 0], i + 8, yval, 0)) + { + mProc->ResetPatched(); + if (PatchForwardSumYPointer(this, mIns[i + 2].mAddress, mIns[i + 2].mAddress, mIns[i + 0], i + 8, yval)) + { + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + mIns[i + 3].mType = ASMIT_NOP; mIns[i + 3].mMode = ASMIM_IMPLIED; + + CheckLive(); + if (mTrueJump) + mTrueJump->CheckLive(); + if (mFalseJump) + mFalseJump->CheckLive(); + + return true; + } + } + } +#endif + +#if 1 + if (pass == 0 && + i + 7 < mIns.Size() && + mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_ADC && mIns[i + 1].mMode == ASMIM_IMMEDIATE_ADDRESS && (mIns[i + 1].mFlags & NCIF_LOWER) && + mIns[i + 2].mType == ASMIT_STA && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && + + mIns[i + 4].mType == ASMIT_LDA && mIns[i + 4].mMode == ASMIM_IMMEDIATE_ADDRESS && (mIns[i + 4].mFlags & NCIF_UPPER) && (mIns[i + 4].mLinkerObject == mIns[i + 1].mLinkerObject) && + mIns[i + 5].mType == ASMIT_ADC && mIns[i + 5].mMode == ASMIM_IMMEDIATE && + mIns[i + 6].mType == ASMIT_STA && mIns[i + 6].mMode == ASMIM_ZERO_PAGE && mIns[i + 6].mAddress == mIns[i + 2].mAddress + 1 && + mIns[i + 7].mType == ASMIT_STA && mIns[i + 7].mMode == ASMIM_ZERO_PAGE && mIns[i + 7].mAddress == mIns[i + 3].mAddress + 1 && + !(mIns[i + 7].mLive & LIVE_CPU_REG_A)) + { + mProc->ResetPatched(); + if (CheckGlobalAddressSumYPointer(this, mIns[i + 2].mAddress, mIns[i + 2].mAddress, i + 8, -1)) + { + NativeCodeInstruction ins(mIns[i + 2]); + mIns[i + 2] = mIns[i + 1]; + mIns[i + 1] = ins; + + mIns[i + 6].mType = ASMIT_NOP; mIns[i + 6].mMode = ASMIM_IMPLIED; + + mProc->ResetPatched(); + if (PatchGlobalAddressSumYPointer(this, mIns[i + 1].mAddress, mIns[i + 1].mAddress, i + 8, -1, mIns[i + 2].mLinkerObject, mIns[i + 2].mAddress + mIns[i + 5].mAddress * 256)) + { + CheckLive(); + return true; + } + } + else + { + mProc->ResetPatched(); + if (CheckGlobalAddressSumYPointer(this, mIns[i + 3].mAddress, mIns[i + 3].mAddress, i + 8, -1)) + { + NativeCodeInstruction ins(mIns[i + 3]); + mIns[i + 3] = mIns[i + 2]; + mIns[i + 2] = mIns[i + 1]; + mIns[i + 1] = ins; + + mIns[i + 7].mType = ASMIT_NOP; mIns[i + 7].mMode = ASMIM_IMPLIED; + + mProc->ResetPatched(); + if (PatchGlobalAddressSumYPointer(this, mIns[i + 1].mAddress, mIns[i + 1].mAddress, i + 8, -1, mIns[i + 2].mLinkerObject, mIns[i + 2].mAddress + mIns[i + 5].mAddress * 256)) + { + CheckLive(); + return true; + } + } + } + } + if (pass == 0 && + i + 8 < mIns.Size() && + mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_IMMEDIATE_ADDRESS && (mIns[i + 1].mFlags & NCIF_LOWER) && + mIns[i + 2].mType == ASMIT_ADC && (mIns[i + 2].mMode == ASMIM_ZERO_PAGE || mIns[i + 2].mMode == ASMIM_ABSOLUTE) && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && + mIns[i + 4].mType == ASMIT_STA && mIns[i + 4].mMode == ASMIM_ZERO_PAGE && + + mIns[i + 5].mType == ASMIT_LDA && mIns[i + 5].mMode == ASMIM_IMMEDIATE_ADDRESS && (mIns[i + 5].mFlags & NCIF_UPPER) && (mIns[i + 5].mLinkerObject == mIns[i + 1].mLinkerObject) && + mIns[i + 6].mType == ASMIT_ADC && mIns[i + 6].mMode == ASMIM_IMMEDIATE && + mIns[i + 7].mType == ASMIT_STA && mIns[i + 7].mMode == ASMIM_ZERO_PAGE && mIns[i + 7].mAddress == mIns[i + 3].mAddress + 1 && + mIns[i + 8].mType == ASMIT_STA && mIns[i + 8].mMode == ASMIM_ZERO_PAGE && mIns[i + 8].mAddress == mIns[i + 4].mAddress + 1 && + !(mIns[i + 8].mLive & LIVE_CPU_REG_A)) + { + mProc->ResetPatched(); + if (CheckGlobalAddressSumYPointer(this, mIns[i + 3].mAddress, mIns[i + 3].mAddress, i + 9, -1)) + { + NativeCodeInstruction ins(mIns[i + 3]); + mIns[i + 3] = mIns[i + 1]; + mIns[i + 1] = mIns[i + 2]; + mIns[i + 2] = ins; + + mIns[i + 1].mType = ASMIT_LDA; + mIns[i + 3].mType = ASMIT_ADC; + + mIns[i + 7].mType = ASMIT_NOP; mIns[i + 7].mMode = ASMIM_IMPLIED; + + mProc->ResetPatched(); + if (PatchGlobalAddressSumYPointer(this, mIns[i + 2].mAddress, mIns[i + 2].mAddress, i + 9, -1, mIns[i + 3].mLinkerObject, mIns[i + 3].mAddress + mIns[i + 6].mAddress * 256)) + { + CheckLive(); + return true; + } + } +#if 0 + else + { + proc->ResetPatched(); + if (CheckGlobalAddressSumYPointer(this, mIns[i + 3].mAddress, mIns[i + 3].mAddress, i + 8, -1)) + { + NativeCodeInstruction ins(mIns[i + 3]); + mIns[i + 3] = mIns[i + 2]; + mIns[i + 2] = mIns[i + 1]; + mIns[i + 1] = ins; + + mIns[i + 7].mType = ASMIT_NOP; mIns[i + 7].mMode = ASMIM_IMPLIED; + + proc->ResetPatched(); + if (PatchGlobalAddressSumYPointer(this, mIns[i + 1].mAddress, mIns[i + 1].mAddress, i + 8, -1, mIns[i + 2].mLinkerObject, mIns[i + 2].mAddress + mIns[i + 5].mAddress * 256)) + progress = true; + } + } +#endif + } +#endif + + + if (i + 5 < mIns.Size() && + mIns[i + 0].mType == ASMIT_LSR && mIns[i + 0].mMode == ASMIM_IMPLIED && + mIns[i + 1].mType == ASMIT_LSR && mIns[i + 1].mMode == ASMIM_IMPLIED && + mIns[i + 2].mType == ASMIT_LSR && mIns[i + 2].mMode == ASMIM_IMPLIED && + mIns[i + 3].mType == ASMIT_LSR && mIns[i + 3].mMode == ASMIM_IMPLIED && + mIns[i + 4].mType == ASMIT_LSR && mIns[i + 4].mMode == ASMIM_IMPLIED && + mIns[i + 5].mType == ASMIT_LSR && mIns[i + 5].mMode == ASMIM_IMPLIED && !(mIns[i + 5].mLive & LIVE_CPU_REG_C)) + { + mIns[i + 0].mType = ASMIT_ASL; mIns[i + 0].mLive |= LIVE_CPU_REG_C; + mIns[i + 1].mType = ASMIT_ROL; mIns[i + 1].mLive |= LIVE_CPU_REG_C; + mIns[i + 2].mType = ASMIT_ROL; + mIns[i + 3].mType = ASMIT_NOP; + mIns[i + 4].mType = ASMIT_NOP; + mIns[i + 5].mType = ASMIT_AND; mIns[i + 5].mMode = ASMIM_IMMEDIATE; mIns[i + 5].mAddress = 3; + return true; + } + + if (i + 6 < mIns.Size() && + mIns[i + 0].mType == ASMIT_STA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_TXA && + mIns[i + 2].mType == ASMIT_LDY && mIns[i + 2].mMode == ASMIM_IMMEDIATE && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].mMode == ASMIM_INDIRECT_Y && + mIns[i + 4].mType == ASMIT_LDA && mIns[i + 4].mMode == ASMIM_ZERO_PAGE && mIns[i + 4].mAddress == mIns[i + 0].mAddress && !(mIns[i + 4].mLive & LIVE_MEM) && + mIns[i + 5].mType == ASMIT_LDY && mIns[i + 5].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress != mIns[i + 5].mAddress && + mIns[i + 6].mType == ASMIT_STA && mIns[i + 6].mMode == ASMIM_INDIRECT_Y && mIns[i + 3].mAddress == mIns[i + 6].mAddress && !(mIns[i + 6].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Y | LIVE_CPU_REG_Z))) + { + int t = mIns[i + 5].mAddress; + mIns[i + 5].mAddress = mIns[i + 2].mAddress; + mIns[i + 2].mAddress = t; + mIns[i + 4] = mIns[i + 1]; + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mLive |= LIVE_CPU_REG_X; + mIns[i + 3].mLive |= LIVE_CPU_REG_X; + return true; + } + + if (i + 9 < mIns.Size() && + mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_ABSOLUTE_X && + mIns[i + 1].mType == ASMIT_LSR && mIns[i + 1].mMode == ASMIM_IMPLIED && + mIns[i + 2].mType == ASMIT_STA && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && + mIns[i + 3].mType == ASMIT_LDA && mIns[i + 3].mMode == ASMIM_ABSOLUTE_X && !mIns[i + 3].SameEffectiveAddress(mIns[i + 0]) && + mIns[i + 4].mType == ASMIT_ROR && mIns[i + 4].mMode == ASMIM_IMPLIED && + mIns[i + 5].mType == ASMIT_LSR && mIns[i + 5].SameEffectiveAddress(mIns[i + 2]) && + mIns[i + 6].mType == ASMIT_ROR && mIns[i + 4].mMode == ASMIM_IMPLIED && + mIns[i + 7].mType == ASMIT_STA && mIns[i + 7].SameEffectiveAddress(mIns[i + 3]) && + mIns[i + 8].mType == ASMIT_LDA && mIns[i + 8].SameEffectiveAddress(mIns[i + 2]) && !(mIns[i + 8].mLive & LIVE_MEM) && + mIns[i + 9].mType == ASMIT_STA && mIns[i + 9].SameEffectiveAddress(mIns[i + 0]) && !(mIns[i + 9].mLive & LIVE_CPU_REG_A)) + { + mIns[i + 0].mType = ASMIT_LSR; mIns[i + 0].mLive |= LIVE_CPU_REG_C; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + mIns[i + 3].mType = ASMIT_ROR; + mIns[i + 4].mType = ASMIT_NOP; mIns[i + 4].mMode = ASMIM_IMPLIED; + mIns[i + 5].CopyMode(mIns[i + 0]); + mIns[i + 6].mType = ASMIT_NOP; mIns[i + 6].mMode = ASMIM_IMPLIED; + mIns[i + 7].mType = ASMIT_ROR; + mIns[i + 8].mType = ASMIT_NOP; mIns[i + 8].mMode = ASMIM_IMPLIED; + mIns[i + 9].mType = ASMIT_NOP; mIns[i + 9].mMode = ASMIM_IMPLIED; + return true; + } + if (i + 9 < mIns.Size() && + mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_ABSOLUTE_X && + mIns[i + 1].mType == ASMIT_ASL && mIns[i + 1].mMode == ASMIM_IMPLIED && + mIns[i + 2].mType == ASMIT_STA && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && + mIns[i + 3].mType == ASMIT_LDA && mIns[i + 3].mMode == ASMIM_ABSOLUTE_X && !mIns[i + 3].SameEffectiveAddress(mIns[i + 0]) && + mIns[i + 4].mType == ASMIT_ROL && mIns[i + 4].mMode == ASMIM_IMPLIED && + mIns[i + 5].mType == ASMIT_ASL && mIns[i + 5].SameEffectiveAddress(mIns[i + 2]) && + mIns[i + 6].mType == ASMIT_ROL && mIns[i + 4].mMode == ASMIM_IMPLIED && + mIns[i + 7].mType == ASMIT_STA && mIns[i + 7].SameEffectiveAddress(mIns[i + 3]) && + mIns[i + 8].mType == ASMIT_LDA && mIns[i + 8].SameEffectiveAddress(mIns[i + 2]) && !(mIns[i + 8].mLive & LIVE_MEM) && + mIns[i + 9].mType == ASMIT_STA && mIns[i + 9].SameEffectiveAddress(mIns[i + 0]) && !(mIns[i + 9].mLive & LIVE_CPU_REG_A)) + { + mIns[i + 0].mType = ASMIT_ASL; mIns[i + 0].mLive |= LIVE_CPU_REG_C; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + mIns[i + 3].mType = ASMIT_ROL; + mIns[i + 4].mType = ASMIT_NOP; mIns[i + 4].mMode = ASMIM_IMPLIED; + mIns[i + 5].CopyMode(mIns[i + 0]); + mIns[i + 6].mType = ASMIT_NOP; mIns[i + 6].mMode = ASMIM_IMPLIED; + mIns[i + 7].mType = ASMIT_ROL; + mIns[i + 8].mType = ASMIT_NOP; mIns[i + 8].mMode = ASMIM_IMPLIED; + mIns[i + 9].mType = ASMIT_NOP; mIns[i + 9].mMode = ASMIM_IMPLIED; + return true; + } + + if (i + 6 < mIns.Size() && + mIns[i + 0].mType == ASMIT_STA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && mIns[i + 1].mAddress != mIns[i + 0].mAddress && + mIns[i + 2].mType == ASMIT_DEY && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].mMode == ASMIM_INDIRECT_Y && mIns[i + 3].mAddress != mIns[i + 0].mAddress && mIns[i + 3].mAddress != mIns[i + 0].mAddress + 1 && + mIns[i + 4].mType == ASMIT_LDA && mIns[i + 4].mMode == ASMIM_ZERO_PAGE && mIns[i + 4].mAddress == mIns[i + 0].mAddress && + mIns[i + 5].mType == ASMIT_INY && + mIns[i + 6].mType == ASMIT_STA && mIns[i + 6].mMode == ASMIM_INDIRECT_Y && mIns[i + 6].mAddress == mIns[i + 3].mAddress && !(mIns[i + 6].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Y)) + ) + { + mIns[i + 0].mLive |= LIVE_CPU_REG_A; + mIns[i + 4] = mIns[i + 1]; + mIns[i + 1] = mIns[i + 3]; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + mIns[i + 3].mType = ASMIT_NOP; mIns[i + 3].mMode = ASMIM_IMPLIED; + mIns[i + 5].mType = ASMIT_DEY; + return true; + } + + if (i + 7 < mIns.Size() && pass > 3 && + mIns[i + 0].mType == ASMIT_LDY && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_ABSOLUTE_Y && + mIns[i + 2].mType == ASMIT_STA && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && + mIns[i + 3].mType == ASMIT_LDY && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && mIns[i + 2].mAddress != mIns[i + 3].mAddress && + mIns[i + 4].mType == ASMIT_LDA && mIns[i + 4].mMode == ASMIM_ABSOLUTE_Y && !(mIns[i + 4].mLive & LIVE_CPU_REG_Y) && + mIns[i + 5].mType == ASMIT_SEC && + mIns[i + 6].mType == ASMIT_SBC && mIns[i + 6].mMode == ASMIM_ZERO_PAGE && mIns[i + 6].mAddress == mIns[i + 2].mAddress && !(mIns[i + 6].mLive & LIVE_MEM) && + !(mIns[i + 1].mFlags & NCIF_VOLATILE) && !(mIns[i + 4].mFlags & NCIF_VOLATILE)) + { + int a0 = mIns[i + 0].mAddress, a1 = mIns[i + 3].mAddress; + + mIns[i + 6].CopyMode(mIns[i + 1]); + mIns[i + 1].CopyMode(mIns[i + 4]); + mIns[i + 0].mAddress = a1; + mIns[i + 3].mAddress = a0; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMMEDIATE; + mIns[i + 4].mType = ASMIT_NOP; mIns[i + 4].mMode = ASMIM_IMMEDIATE; + mIns[i + 5].mLive |= LIVE_CPU_REG_Y; + + return true; + } + + return false; +} + +bool NativeCodeBasicBlock::PeepHoleOptimizerIterate(int pass) +{ + bool changed = false; + + bool progress = false; + do { + progress = false; + + mIns.Reserve(mIns.Size() * 2 + 32); + + if (RemoveNops()) + changed = true; + + CheckLive(); + + // Replace (a & 0x80) != 0 with bpl/bmi + int sz = mIns.Size(); + + if (sz > 1 && + mIns[sz - 2].ChangesAccuAndFlag() && + mIns[sz - 1].mType == ASMIT_AND && mIns[sz - 1].mMode == ASMIM_IMMEDIATE && mIns[sz - 1].mAddress == 0x80 && !(mIns[sz - 1].mLive & LIVE_CPU_REG_A)) + { + if (mBranch == ASMIT_BEQ) + { + mBranch = ASMIT_BPL; + mIns[sz - 2].mLive |= LIVE_CPU_REG_Z; + mIns[sz - 1].mType = ASMIT_NOP; mIns[sz - 1].mMode = ASMIM_IMPLIED; + changed = true; + } + else if (mBranch == ASMIT_BNE) + { + mBranch = ASMIT_BMI; + mIns[sz - 2].mLive |= LIVE_CPU_REG_Z; + mIns[sz - 1].mType = ASMIT_NOP; mIns[sz - 1].mMode = ASMIM_IMPLIED; + changed = true; + } + } + + if (sz > 4 && + mIns[sz - 4].mType == ASMIT_ASL && mIns[sz - 4].mMode == ASMIM_IMPLIED && + mIns[sz - 3].mType == ASMIT_LDA && mIns[sz - 3].mMode == ASMIM_IMMEDIATE && mIns[sz - 3].mAddress == 0 && + mIns[sz - 2].mType == ASMIT_ADC && mIns[sz - 2].mMode == ASMIM_IMMEDIATE && mIns[sz - 2].mAddress == 0xff && + mIns[sz - 1].mType == ASMIT_EOR && mIns[sz - 1].mMode == ASMIM_IMMEDIATE && mIns[sz - 1].mAddress == 0xff && !(mIns[sz - 1].mLive & LIVE_CPU_REG_A)) + { + if (mBranch == ASMIT_BEQ) + { + mBranch = ASMIT_BPL; + mIns[sz - 4].mType = ASMIT_NOP; mIns[sz - 4].mMode = ASMIM_IMPLIED; + mIns[sz - 3].mType = ASMIT_NOP; mIns[sz - 3].mMode = ASMIM_IMPLIED; + mIns[sz - 2].mType = ASMIT_NOP; mIns[sz - 2].mMode = ASMIM_IMPLIED; + mIns[sz - 1].mType = ASMIT_ORA; mIns[sz - 1].mAddress = 0; + changed = true; + } + else if (mBranch == ASMIT_BNE) + { + mBranch = ASMIT_BMI; + mIns[sz - 4].mType = ASMIT_NOP; mIns[sz - 4].mMode = ASMIM_IMPLIED; + mIns[sz - 3].mType = ASMIT_NOP; mIns[sz - 3].mMode = ASMIM_IMPLIED; + mIns[sz - 2].mType = ASMIT_NOP; mIns[sz - 2].mMode = ASMIM_IMPLIED; + mIns[sz - 1].mType = ASMIT_ORA; mIns[sz - 1].mAddress = 0; + changed = true; + } + } + + if (sz > 2 && + mIns[sz - 3].mType == ASMIT_STA && + mIns[sz - 2].mType == ASMIT_LDA && mIns[sz - 2].mMode == ASMIM_IMMEDIATE && + mIns[sz - 1].mType == ASMIT_CMP && mIns[sz - 1].SameEffectiveAddress(mIns[sz - 3]) && !(mIns[sz - 1].mLive & (LIVE_CPU_REG_Z | LIVE_CPU_REG_A))) + { + if (mBranch == ASMIT_BCC && mIns[sz - 2].mAddress < 0xff) + { + mIns[sz - 3].mLive |= LIVE_CPU_REG_A; + mIns[sz - 1].mMode = ASMIM_IMMEDIATE; + mIns[sz - 1].mAddress = mIns[sz - 2].mAddress + 1; + mIns[sz - 2].mType = ASMIT_NOP; mIns[sz - 2].mMode = ASMIM_IMPLIED; + mBranch = ASMIT_BCS; + changed = true; + } + else if (mBranch == ASMIT_BCS && mIns[sz - 2].mAddress < 0xff) + { + mIns[sz - 3].mLive |= LIVE_CPU_REG_A; + mIns[sz - 1].mMode = ASMIM_IMMEDIATE; + mIns[sz - 1].mAddress = mIns[sz - 2].mAddress + 1; + mIns[sz - 2].mType = ASMIT_NOP; mIns[sz - 2].mMode = ASMIM_IMPLIED; + mBranch = ASMIT_BCC; + changed = true; + } + } + + for (int i = 0; i < mIns.Size(); i++) + { + if (PeepHoleOptimizerIterate1(i, pass)) progress = true; + + if (mIns[i + 0].mMode == ASMIM_INDIRECT_Y && (mIns[i + 0].mFlags & NCIF_YZERO)) + { + const NativeCodeInstruction* ains, * iins; + + int sreg = mIns[i + 0].mAddress; + + int apos, breg, ireg, addr; + uint32 flags; + + if (FindAddressSumY(i, sreg, apos, breg, ireg)) + { + if (!(breg == sreg || ireg == sreg) || !(mIns[i + 0].mLive & LIVE_MEM)) + { + if (breg == sreg || ireg == sreg) + { + mIns[apos + 3].mType = ASMIT_NOP; mIns[apos + 3].mMode = ASMIM_IMPLIED; + mIns[apos + 6].mType = ASMIT_NOP; mIns[apos + 6].mMode = ASMIM_IMPLIED; + } + + RepairLoadYImmediate(i, 0); + + mIns.Insert(i + 0, NativeCodeInstruction(mIns[i + 0].mIns, ASMIT_LDY, ASMIM_ZERO_PAGE, ireg)); + mIns[i + 0].mLive |= LIVE_CPU_REG_Y | LIVE_MEM; + + mIns[i + 1].mAddress = breg; + mIns[i + 1].mFlags &= ~NCIF_YZERO; + progress = true; + + CheckLive(); + + } + } + else if (FindGlobalAddressSumY(i, sreg, true, apos, ains, iins, flags, addr)) + { + if (iins || (flags & LIVE_CPU_REG_Y) || (flags & LIVE_CPU_REG_X)) //!(mIns[i + 1].mLive & LIVE_CPU_REG_X)) + { + RepairLoadYImmediate(i, 0); + + if (flags & LIVE_CPU_REG_Y) + { + mIns[i + 0].mMode = ASMIM_ABSOLUTE_Y; + } + else if (flags & LIVE_CPU_REG_X) + { + mIns[i + 0].mMode = ASMIM_ABSOLUTE_X; + } + else + { + mIns[i + 0].mMode = ASMIM_ABSOLUTE_Y; + } + + + if (ains->mMode == ASMIM_IMMEDIATE) + { + mIns[i + 0].mLinkerObject = nullptr; + mIns[i + 0].mAddress = addr; + } + else + { + mIns[i + 0].mLinkerObject = ains->mLinkerObject; + mIns[i + 0].mAddress = ains->mAddress; + } + + mIns[i + 0].mFlags &= ~NCIF_YZERO; + + if (!iins) + { + if (flags & LIVE_CPU_REG_Y) + { + mIns.Insert(apos, NativeCodeInstruction(mIns[i + 0].mIns, ASMIT_TAY, ASMIM_IMPLIED)); + for (int j = apos; j < i + 1; j++) + mIns[j].mLive |= LIVE_CPU_REG_Y; + } + else + { + PatchGlobalAdressSumYByX(i + 1, sreg, *ains, addr); + mIns.Insert(apos, NativeCodeInstruction(mIns[i + 0].mIns, ASMIT_TAX, ASMIM_IMPLIED)); + for (int j = apos; j < i + 2; j++) + mIns[j].mLive |= LIVE_CPU_REG_X; + } + } + else + { + if (iins->mMode != ASMIM_ZERO_PAGE) + mIns.Insert(i + 0, NativeCodeInstruction(mIns[i + 0].mIns, ASMIT_LDY, iins->mMode, iins->mAddress, iins->mLinkerObject, iins->mFlags)); + else if (iins->mAddress == sreg) + { + if (flags & LIVE_CPU_REG_Y) + { + mIns.Insert(apos, NativeCodeInstruction(mIns[i + 0].mIns, ASMIT_LDY, ASMIM_ZERO_PAGE, iins->mAddress)); + mIns[apos].mLive |= LIVE_MEM; + for (int j = apos; j < i + 2; j++) + mIns[j].mLive |= LIVE_CPU_REG_Y; + } + else + { + PatchGlobalAdressSumYByX(i + 1, sreg, *ains, addr); + mIns.Insert(apos, NativeCodeInstruction(mIns[i + 0].mIns, ASMIT_LDX, ASMIM_ZERO_PAGE, iins->mAddress)); + mIns[apos].mLive |= LIVE_MEM; + for (int j = apos; j < i + 2; j++) + mIns[j].mLive |= LIVE_CPU_REG_X; + } + i++; + } + else + mIns.Insert(i + 0, NativeCodeInstruction(mIns[i + 0].mIns, ASMIT_LDY, ASMIM_ZERO_PAGE, iins->mAddress)); + if (i > 0) + mIns[i + 0].mLive |= mIns[i - 1].mLive | LIVE_CPU_REG_Y | LIVE_MEM; + } + + progress = true; + + CheckLive(); + + } + } + else if (FindExternAddressSumY(i, sreg, breg, ireg)) + { + RepairLoadYImmediate(i, 0); + + mIns.Insert(i + 0, NativeCodeInstruction(mIns[i + 0].mIns, ASMIT_LDY, ASMIM_ZERO_PAGE, ireg)); + mIns[i + 0].mLive |= LIVE_CPU_REG_Y | LIVE_MEM; + + mIns[i + 1].mAddress = breg; + mIns[i + 1].mFlags &= ~NCIF_YZERO; + progress = true; + + CheckLive(); + } + else if (FindSharedGlobalAddressSumY(i, sreg, ains, iins)) + { + RepairLoadYImmediate(i, 0); + + mIns.Insert(i + 0, NativeCodeInstruction(mIns[i + 0].mIns, ASMIT_LDY, *iins)); + mIns[i + 0].mLive |= LIVE_CPU_REG_Y | LIVE_MEM; + + mIns[i + 1].mMode = ASMIM_ABSOLUTE_Y; + mIns[i + 1].mLinkerObject = ains->mLinkerObject; + mIns[i + 1].mAddress = ains->mAddress; + mIns[i + 1].mFlags &= ~NCIF_YZERO; + progress = true; + + CheckLive(); + } + else if (!(mIns[i + 0].mLive & LIVE_MEM) && FindLoadAddressSumY(i, sreg, apos, ireg)) + { + RepairLoadYImmediate(i, 0); + PatchLoadAddressSumY(i, sreg, apos, ireg); + + mIns.Insert(i + 0, NativeCodeInstruction(mIns[i + 0].mIns, ASMIT_LDY, ASMIM_ZERO_PAGE, ireg)); + + mIns[i + 1].mFlags &= ~NCIF_YZERO; + progress = true; + + CheckLive(); + } + if (mIns[i + 0].mMode == ASMIM_INDIRECT_Y && (mIns[i + 0].mFlags & NCIF_YZERO) && !(mIns[i + 0].mLive & LIVE_CPU_REG_X)) + { + const NativeCodeInstruction* ains; + if (FindImmediateStore(i, mIns[i].mAddress + 1, ains)) + { + mIns.Insert(i, NativeCodeInstruction(mIns[i + 0].mIns, ASMIT_LDX, ASMIM_ZERO_PAGE, mIns[i].mAddress)); + mIns[i + 1].mMode = ASMIM_ABSOLUTE_X; + + if (ains->mMode == ASMIM_IMMEDIATE) + mIns[i + 1].mAddress = ains->mAddress << 8; + else + { + mIns[i + 1].mLinkerObject = ains->mLinkerObject; + mIns[i + 1].mAddress = ains->mAddress; + mIns[i + 1].mFlags |= NCIF_UPPER; + mIns[i + 1].mFlags &= ~NCIF_LOWER; + } + progress = true; + + CheckLive(); + } + } + + } + + CheckLive(); + + if (i + 1 < mIns.Size() && mIns[i + 0].mType == ASMIT_LDY && mIns[i + 0].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mMode == ASMIM_INDIRECT_Y) + { + const NativeCodeInstruction* ains, * iins; + + int sreg = mIns[i + 1].mAddress; + + int yimm = mIns[i + 0].mAddress; + + + if (FindSharedGlobalAddressSumY(i, sreg, ains, iins)) + { + RepairLoadYImmediate(i + 1, yimm); + + mIns.Insert(i + 1, NativeCodeInstruction(mIns[i + 1].mIns, ASMIT_LDY, *iins)); + mIns[i + 1].mLive |= LIVE_CPU_REG_Y | LIVE_MEM; + + mIns[i + 2].mMode = ASMIM_ABSOLUTE_Y; + mIns[i + 2].mLinkerObject = ains->mLinkerObject; + mIns[i + 2].mAddress = ains->mAddress + yimm; + mIns[i + 2].mFlags &= ~NCIF_YZERO; + progress = true; + + CheckLive(); + } + } + +#if _DEBUG + NativeCodeInstruction h0(mIns[i + 0]); + NativeCodeInstruction h1(mIns[i + 1 < mIns.Size() ? i + 1 : 0]); +#endif + if (i + 1 < mIns.Size() && PeepHoleOptimizerIterate2(i, pass)) progress = true; + CheckLive(); +#if _DEBUG + NativeCodeInstruction i0(mIns[i + 0]); + NativeCodeInstruction i1(mIns[i + 1 < mIns.Size() ? i + 1 : 0]); + NativeCodeInstruction i2(mIns[i + 2 < mIns.Size() ? i + 2 : 0]); +#endif + if (i + 2 < mIns.Size() && PeepHoleOptimizerIterate3(i, pass)) progress = true; + CheckLive(); + if (i + 3 < mIns.Size() && PeepHoleOptimizerIterate4(i, pass)) progress = true; + CheckLive(); + if (i + 4 < mIns.Size() && PeepHoleOptimizerIterate5(i, pass)) progress = true; + CheckLive(); + if (i + 3 < mIns.Size() && PeepHoleOptimizerIterate4b(i, pass)) progress = true; + CheckLive(); + if (i + 4 < mIns.Size() && PeepHoleOptimizerIterate5b(i, pass)) progress = true; + CheckLive(); + + if (mTrueJump) + mTrueJump->CheckLive(); + if (mFalseJump) + mFalseJump->CheckLive(); + +#if 1 + if (pass == 8 && i + 1 < mIns.Size() && (mProc->mCompilerOptions & COPT_OPTIMIZE_BASIC)) + { + if ( + mIns[i + 0].mType == ASMIT_LDA && (mIns[i + 0].mMode == ASMIM_ABSOLUTE_X || mIns[i + 0].mMode == ASMIM_ABSOLUTE_Y) && + !(mIns[i + 0].mFlags & NCIF_VOLATILE) && + mIns[i + 1].mType == ASMIT_STA && mIns[i + 1].mMode == ASMIM_ABSOLUTE && mIns[i + 1].mLinkerObject && + mIns[i + 1].mLinkerObject->mVariable && (mIns[i + 1].mLinkerObject->mFlags & LOBJF_LOCAL_VAR)) + { + mProc->ResetPatched(); + if (CheckSingleUseGlobalLoadStruct(this, mIns[i + 1], i + 2, mIns[i + 0], false, false)) + { + mProc->ResetPatched(); + if (PatchSingleUseGlobalLoadStruct(this, mIns[i + 1], i + 2, mIns[i + 0])) + { + mIns[i + 1].mType = ASMIT_NOP; + mIns[i + 1].mMode = ASMIM_IMPLIED; + changed = true; + } + } + } + } +#endif +#if 1 + if (pass < 10 && i + 1 < mIns.Size()) + { + if ( + mIns[i + 0].mType == ASMIT_LDA && (mIns[i + 0].mMode == ASMIM_ABSOLUTE || mIns[i + 0].mMode == ASMIM_ABSOLUTE_X || mIns[i + 0].mMode == ASMIM_ABSOLUTE_Y || mIns[i + 0].mMode == ASMIM_INDIRECT_Y) && + mIns[i + 1].mType == ASMIT_STA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE) + { + int n = 3; + if (mIns[i + 0].mLinkerObject && (mIns[i + 0].mLinkerObject->mFlags & LOBJF_ZEROPAGE)) + n = 100; + if (mIns[i + 0].mFlags & NCIF_VOLATILE) + n = 1; + if (mIns[i + 1].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z)) + n--; + + if (n > 0 && (mIns[i + 0].mMode != ASMIM_INDIRECT_Y || (mIns[i + 1].mAddress != mIns[i + 0].mAddress && mIns[i + 1].mAddress != mIns[i + 0].mAddress + 1 && pass > 1))) + { + mProc->ResetPatched(); + if (CheckSingleUseGlobalLoad(this, mIns[i + 1].mAddress, i + 2, mIns[i], n)) + { + mProc->ResetPatched(); + if (PatchSingleUseGlobalLoad(this, mIns[i + 1].mAddress, i + 2, mIns[i])) + { + if (mIns[i + 0].mMode == ASMIM_ABSOLUTE_X) + { + mIns[i + 0].mLive |= LIVE_CPU_REG_X; + mIns[i + 1].mLive |= LIVE_CPU_REG_X; + } + else if (mIns[i + 0].mMode == ASMIM_ABSOLUTE_Y || mIns[i + 0].mMode == ASMIM_INDIRECT_Y) + { + mIns[i + 0].mLive |= LIVE_CPU_REG_Y; + mIns[i + 1].mLive |= LIVE_CPU_REG_Y; + } + + if (mIns[i + 0].mFlags & NCIF_VOLATILE) + { + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + } + + progress = true; + } + CheckLive(); + if (mTrueJump) + mTrueJump->CheckLive(); + if (mFalseJump) + mFalseJump->CheckLive(); + } + } + } + else if ( + mIns[i + 0].mType == ASMIT_LDX && mIns[i + 0].mMode == ASMIM_ABSOLUTE && + mIns[i + 1].mType == ASMIT_STX && mIns[i + 1].mMode == ASMIM_ZERO_PAGE) + { + int n = 3; + if (mIns[i + 0].mLinkerObject && (mIns[i + 0].mLinkerObject->mFlags & LOBJF_ZEROPAGE)) + n = 100; + if (mIns[i + 0].mFlags & NCIF_VOLATILE) + n = 1; + if (mIns[i + 1].mLive & (LIVE_CPU_REG_X | LIVE_CPU_REG_Z)) + n--; + + if (n > 0) + { + mProc->ResetPatched(); + if (CheckSingleUseGlobalLoad(this, mIns[i + 1].mAddress, i + 2, mIns[i], n)) + { + mProc->ResetPatched(); + if (PatchSingleUseGlobalLoad(this, mIns[i + 1].mAddress, i + 2, mIns[i])) + progress = true; + CheckLive(); + if (mTrueJump) + mTrueJump->CheckLive(); + if (mFalseJump) + mFalseJump->CheckLive(); + } + } + } + else if ( + mIns[i + 0].mType == ASMIT_STA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_STA && mIns[i + 1].mMode == ASMIM_ABSOLUTE && !(mIns[i + 1].mFlags & NCIF_VOLATILE)) + { + int n = 2; + if (mIns[i + 1].mLinkerObject && (mIns[i + 1].mLinkerObject->mFlags & LOBJF_ZEROPAGE)) + n = 100; + + mProc->ResetPatched(); + if (CheckSingleUseGlobalLoad(this, mIns[i + 0].mAddress, i + 2, mIns[i + 1], n)) + { + mProc->ResetPatched(); + if (PatchSingleUseGlobalLoad(this, mIns[i + 0].mAddress, i + 2, mIns[i + 1])) + progress = true; + CheckLive(); + if (mTrueJump) + mTrueJump->CheckLive(); + if (mFalseJump) + mFalseJump->CheckLive(); + } + } + } +#endif + CheckLive(); + if (mTrueJump) + mTrueJump->CheckLive(); + if (mFalseJump) + mFalseJump->CheckLive(); + +#if 1 + if (i + 5 < mIns.Size() && PeepHoleOptimizerIterate6(i, pass)) progress = true; + CheckLive(); +#endif + +#if 1 + if (i + 3 < mIns.Size() && pass == 0 && + mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_IMMEDIATE && + mIns[i + 1].mType == ASMIT_STA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + mIns[i + 2].mType == ASMIT_LDA && mIns[i + 2].mMode == ASMIM_IMMEDIATE && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && mIns[i + 3].mAddress == mIns[i + 1].mAddress + 1 && + !(mIns[i + 3].mLive & LIVE_CPU_REG_A)) + { + mProc->ResetPatched(); + if (CheckGlobalAddressSumYPointer(this, mIns[i + 1].mAddress, 0, i + 4, -1)) + { + mProc->ResetPatched(); + if (PatchGlobalAddressSumYPointer(this, mIns[i + 1].mAddress, 0, i + 4, -1, nullptr, 256 * mIns[i + 2].mAddress + mIns[i + 0].mAddress)) + progress = true; + } + } + CheckLive(); +#endif + +#if 1 + if (i + 2 < mIns.Size() && pass == 0 && + mIns[i + 0].mType == ASMIT_STA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_IMMEDIATE && + mIns[i + 2].mType == ASMIT_STA && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && mIns[i + 2].mAddress == mIns[i + 0].mAddress + 1 && + !(mIns[i + 2].mLive & LIVE_CPU_REG_A)) + { + mProc->ResetPatched(); + if (CheckGlobalAddressSumYPointer(this, mIns[i + 0].mAddress, mIns[i + 0].mAddress, i + 3, -1)) + { + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + + mProc->ResetPatched(); + if (PatchGlobalAddressSumYPointer(this, mIns[i + 0].mAddress, mIns[i + 0].mAddress, i + 3, -1, nullptr, 256 * mIns[i + 1].mAddress)) + progress = true; + } + } + CheckLive(); +#endif +#if 1 + if (i + 1 < mIns.Size() && + mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_IMMEDIATE_ADDRESS && (mIns[i + 0].mFlags & NCIF_UPPER) && + mIns[i + 1].mType == ASMIT_STA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE) + { + mProc->ResetPatched(); + if (CheckGlobalAddressSumYPointer(this, mIns[i + 1].mAddress - 1, mIns[i + 1].mAddress - 1, i + 2, -1)) + { + mProc->ResetPatched(); + if (PatchGlobalAddressSumYPointer(this, mIns[i + 1].mAddress - 1, mIns[i + 1].mAddress - 1, i + 2, -1, mIns[i + 0].mLinkerObject, mIns[i + 0].mAddress, NCIF_UPPER)) + progress = true; + } + } + CheckLive(); +#endif + + if (PeepHoleOptimizerIterateN(i, pass)) progress = true; + + CheckLive(); +#if 1 + if (i + 1 < mIns.Size() && mIns[i + 0].mType == ASMIT_LDY && mIns[i + 0].mMode == ASMIM_IMMEDIATE && mIns[i + 0].mAddress == 0 && mIns[i + 1].mMode == ASMIM_INDIRECT_Y) + { + int apos, breg; + if (FindDirectAddressSumY(i, mIns[i + 1].mAddress, apos, breg)) + { + if (PatchDirectAddressSumY(i, mIns[i + 1].mAddress, apos, breg)) + progress = true; + } + } +#if 1 + if (i + 1 < mIns.Size() && mIns[i + 0].mType == ASMIT_LDY && mIns[i + 0].mMode == ASMIM_IMMEDIATE && mIns[i + 0].mAddress == 0 && mIns[i + 1].mMode == ASMIM_INDIRECT_Y && !(mIns[i + 1].mLive & (LIVE_MEM | LIVE_CPU_REG_Y))) + { + int apos, breg; + if (FindBypassAddressSumY(i, mIns[i + 1].mAddress, apos, breg)) + { + if (PatchBypassAddressSumY(i, mIns[i + 1].mAddress, apos, breg)) + progress = true; + } + } +#endif +#if 1 + if (i + 1 < mIns.Size() && mIns[i + 0].mType == ASMIT_LDY && mIns[i + 0].mMode == ASMIM_IMMEDIATE && mIns[i + 0].mAddress == 0 && mIns[i + 1].mMode == ASMIM_INDIRECT_Y && !(mIns[i + 1].mLive & (LIVE_MEM | LIVE_CPU_REG_Y))) + { + int apos, yoffset; + if (FindAbsoluteAddressSumY(i, mIns[i + 1].mAddress, apos, yoffset)) + { + if (PatchAbsoluteAddressSumY(i, mIns[i + 1].mAddress, apos)) + { + mIns[i + 0].mAddress = yoffset; + progress = true; + } + } + } +#endif + + +#if 1 + if (mIns[i + 0].mMode == ASMIM_INDIRECT_Y && (mIns[i + 0].mFlags & NCIF_YZERO)) + { + int apos, breg; + if (FindDirectAddressSumY(i, mIns[i].mAddress, apos, breg)) + { + if (PatchDirectAddressSumY(i, mIns[i].mAddress, apos, breg)) + progress = true; + } + } +#endif + +#endif + + +#if 0 + if (i + 13 < mIns.Size()) + { + if (mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 1) + { + printf("CHECK\n"); + } + + if (mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_LDA && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 1 && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && + mIns[i + 4].mType == ASMIT_LDA && + mIns[i + 5].mType == ASMIT_ADC && mIns[i + 5].mMode == ASMIM_IMMEDIATE && mIns[i + 5].mAddress == 0 && + mIns[i + 6].mType == ASMIT_STA && mIns[i + 6].mMode == ASMIM_ZERO_PAGE && + + mIns[i + 7].mType == ASMIT_CLC && + mIns[i + 8].mType == ASMIT_LDA && mIns[i + 8].mMode == ASMIM_ZERO_PAGE && mIns[i + 8].mAddress == mIns[i + 3].mAddress && !(mIns[i + 8].mLive & LIVE_MEM) && + mIns[i + 9].mType == ASMIT_ADC && + mIns[i + 10].mType == ASMIT_STA && + mIns[i + 11].mType == ASMIT_LDA && mIns[i + 11].mMode == ASMIM_ZERO_PAGE && mIns[i + 11].mAddress == mIns[i + 6].mAddress && !(mIns[i + 11].mLive & LIVE_MEM) && + mIns[i + 12].mType == ASMIT_ADC && + mIns[i + 12].mType == ASMIT_STA) + { + mIns[i + 7].CopyMode(mIns[i + 0]); + mIns[i + 11].CopyMode(mIns[i + 4]); + mIns[i + 8].mType = ASMIT_SEC; + + for (int j = 0; j < 7; j++) + { + mIns[i + j].mType = ASMIT_NOP; + mIns[i + j].mMode = ASMIM_IMPLIED; + mIns[i + j + 7].mLive |= mIns[i + 0].mLive; + } + + progress = true; + } + + } +#endif + + +#if 1 + if (i + 7 < mIns.Size() && pass > 0 && + mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_ADC && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + mIns[i + 2].mType == ASMIT_STA && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && mIns[i + 1].mAddress == mIns[i + 2].mAddress && + mIns[i + 3].mType == ASMIT_LDA && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && + mIns[i + 4].mType == ASMIT_ADC && mIns[i + 4].mMode == ASMIM_ZERO_PAGE && + mIns[i + 5].mType == ASMIT_STA && mIns[i + 5].mMode == ASMIM_ZERO_PAGE && mIns[i + 5].mAddress == mIns[i + 2].mAddress + 1 && + mIns[i + 6].mType == ASMIT_LDY && mIns[i + 6].mMode == ASMIM_IMMEDIATE && mIns[i + 6].mAddress == 0 && + mIns[i + 7].mMode == ASMIM_INDIRECT_Y && mIns[i + 7].mAddress == mIns[i + 2].mAddress && + !(mIns[i + 7].mLive & LIVE_MEM)) + { + if (mIns[i + 7].mLive & LIVE_CPU_REG_Y) + mIns.Insert(i + 8, mIns[i + 6]); + + mIns[i + 1] = NativeCodeInstruction(mIns[i + 0].mIns, ASMIT_TAY); mIns[i + 1].mLive |= LIVE_CPU_REG_Y | LIVE_CPU_REG_C; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + mIns[i + 3].mLive |= LIVE_CPU_REG_Y; + mIns[i + 4].mLive |= LIVE_CPU_REG_Y; + mIns[i + 5].mLive |= LIVE_CPU_REG_Y; + mIns[i + 6].mType = ASMIT_NOP; mIns[i + 6].mMode = ASMIM_IMPLIED; + progress = true; + } +#endif + +#if 1 + if (i + 8 < mIns.Size() && pass > 0 && + mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && mIns[i + 2].mAddress == mIns[i + 3].mAddress && + mIns[i + 4].mType == ASMIT_LDA && mIns[i + 4].mMode == ASMIM_ZERO_PAGE && + mIns[i + 5].mType == ASMIT_ADC && mIns[i + 5].mMode == ASMIM_ZERO_PAGE && + mIns[i + 6].mType == ASMIT_STA && mIns[i + 6].mMode == ASMIM_ZERO_PAGE && mIns[i + 6].mAddress == mIns[i + 3].mAddress + 1 && + mIns[i + 7].mType == ASMIT_LDY && mIns[i + 7].mMode == ASMIM_IMMEDIATE && mIns[i + 7].mAddress == 0 && + mIns[i + 8].mMode == ASMIM_INDIRECT_Y && mIns[i + 8].mAddress == mIns[i + 3].mAddress && + !(mIns[i + 8].mLive & LIVE_MEM)) + { + if (mIns[i + 8].mLive & LIVE_CPU_REG_Y) + mIns.Insert(i + 9, mIns[i + 7]); + + mIns[i + 1].mType = ASMIT_LDY; mIns[i + 1].mLive |= LIVE_CPU_REG_Y | LIVE_CPU_REG_C; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + mIns[i + 3].mType = ASMIT_NOP; mIns[i + 3].mMode = ASMIM_IMPLIED; + mIns[i + 4].mLive |= LIVE_CPU_REG_Y; + mIns[i + 5].mLive |= LIVE_CPU_REG_Y; + mIns[i + 6].mLive |= LIVE_CPU_REG_Y; + mIns[i + 7].mType = ASMIT_NOP; mIns[i + 7].mMode = ASMIM_IMPLIED; + progress = true; + } +#endif + CheckLive(); + +#if 1 + if (pass > 1 && mIns[i].mMode == ASMIM_IMMEDIATE_ADDRESS && mIns[i].mLinkerObject && (mIns[i].mFlags & NCIF_LOWER) && !(mIns[i].mAddress & 0xff) && !(mIns[i].mLinkerObject->mAlignment & 0xff)) + { + mIns[i].mMode = ASMIM_IMMEDIATE; + mIns[i].mAddress = 0; + mIns[i].mLinkerObject = nullptr; + mIns[i].mFlags &= ~NCIF_LOWER; + progress = true; + } +#endif + CheckLive(); + if (mTrueJump) + mTrueJump->CheckLive(); + if (mFalseJump) + mFalseJump->CheckLive(); + + } + + if (progress) + changed = true; + + } while (progress); + + CheckLive(); + if (mTrueJump) + mTrueJump->CheckLive(); + if (mFalseJump) + mFalseJump->CheckLive(); + + return changed; +} + +bool NativeCodeBasicBlock::PeepHoleOptimizerExits(int pass) +{ + bool changed = false; + +#if 1 + int sz = mIns.Size(); + + if (sz >= 1 && (mIns[sz - 1].mType == ASMIT_SEC || mIns[sz - 1].mType == ASMIT_CLC) && mExitRequiredRegs[CPU_REG_C]) + { + if (mTrueJump && mTrueJump->mIns.Size() == 1 && mTrueJump->mNumEntries == 1 && mTrueJump->mIns[0].mType == mIns[sz - 1].mType) + { + mTrueJump->mIns[0].mType = ASMIT_NOP; + mTrueJump->mEntryRequiredRegs += CPU_REG_C; + changed = true; + } + if (mFalseJump && mFalseJump->mIns.Size() == 1 && mFalseJump->mNumEntries == 1 && mFalseJump->mIns[0].mType == mIns[sz - 1].mType) + { + mFalseJump->mIns[0].mType = ASMIT_NOP; + mFalseJump->mEntryRequiredRegs += CPU_REG_C; + changed = true; + } + } + + if (sz >= 1 && mIns[sz - 1].mType == ASMIT_SEC) + { + if (mTrueJump && mTrueJump->mIns.Size() == 0) + { + if (mTrueJump->mBranch == ASMIT_BCC) + { + mTrueJump->RemEntryBlock(this); + if (mTrueJump->mFalseJump == mFalseJump) + { + mTrueJump = mFalseJump; + mFalseJump = nullptr; + mBranch = ASMIT_JMP; + } + else + { + mTrueJump->mFalseJump->AddEntryBlock(this); + mTrueJump = mTrueJump->mFalseJump; + } + changed = true; + } + else if (mTrueJump->mBranch == ASMIT_BCS) + { + mTrueJump->RemEntryBlock(this); + if (mTrueJump->mTrueJump == mFalseJump) + { + mTrueJump = mFalseJump; + mFalseJump = nullptr; + mBranch = ASMIT_JMP; + } + else + { + mTrueJump->mTrueJump->AddEntryBlock(this); + mTrueJump = mTrueJump->mTrueJump; + } + changed = true; + } + } + if (mFalseJump && mFalseJump->mIns.Size() == 0) + { + if (mFalseJump->mBranch == ASMIT_BCC) + { + mFalseJump->RemEntryBlock(this); + if (mFalseJump->mFalseJump == mTrueJump) + { + mFalseJump = nullptr; + mBranch = ASMIT_JMP; + } + else + { + mFalseJump->mFalseJump->AddEntryBlock(this); + mFalseJump = mFalseJump->mFalseJump; + } + changed = true; + } + else if (mTrueJump->mBranch == ASMIT_BCS) + { + mFalseJump->RemEntryBlock(this); + if (mFalseJump->mTrueJump == mTrueJump) + { + mFalseJump = nullptr; + mBranch = ASMIT_JMP; + } + else + { + mFalseJump->mTrueJump->AddEntryBlock(this); + mFalseJump = mFalseJump->mTrueJump; + } + changed = true; + } + } + } + if (sz >= 1 && mIns[sz - 1].mType == ASMIT_CLC) + { + if (mTrueJump && mTrueJump->mIns.Size() == 0) + { + if (mTrueJump->mBranch == ASMIT_BCS) + { + mTrueJump->RemEntryBlock(this); + if (mTrueJump->mFalseJump == mFalseJump) + { + mTrueJump = mFalseJump; + mFalseJump = nullptr; + mBranch = ASMIT_JMP; + } + else + { + mTrueJump->mFalseJump->AddEntryBlock(this); + mTrueJump = mTrueJump->mFalseJump; + } + changed = true; + } + else if (mTrueJump->mBranch == ASMIT_BCC) + { + mTrueJump->RemEntryBlock(this); + if (mTrueJump->mTrueJump == mFalseJump) + { + mTrueJump = mFalseJump; + mFalseJump = nullptr; + mBranch = ASMIT_JMP; + } + else + { + mTrueJump->mTrueJump->AddEntryBlock(this); + mTrueJump = mTrueJump->mTrueJump; + } + changed = true; + } + } + if (mFalseJump && mFalseJump->mIns.Size() == 0) + { + if (mFalseJump->mBranch == ASMIT_BCS) + { + mFalseJump->RemEntryBlock(this); + if (mFalseJump->mFalseJump == mTrueJump) + { + mFalseJump = nullptr; + mBranch = ASMIT_JMP; + } + else + { + mFalseJump->mFalseJump->AddEntryBlock(this); + mFalseJump = mFalseJump->mFalseJump; + } + changed = true; + } + else if (mTrueJump->mBranch == ASMIT_BCC) + { + mFalseJump->RemEntryBlock(this); + if (mFalseJump->mTrueJump == mTrueJump) + { + mFalseJump = nullptr; + mBranch = ASMIT_JMP; + } + else + { + mFalseJump->mTrueJump->AddEntryBlock(this); + mFalseJump = mFalseJump->mTrueJump; + } + changed = true; + } + } + } + + if (sz >= 2 && + mIns[sz - 2].mType == ASMIT_LDA && mIns[sz - 2].mMode == ASMIM_IMMEDIATE && mIns[sz - 2].mAddress == 1 && + mIns[sz - 1].mType == ASMIT_SBC && mIns[sz - 1].mMode == ASMIM_IMMEDIATE && mIns[sz - 1].mAddress == 0 && + !(mIns[sz - 1].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C))) + { + if (mBranch == ASMIT_BNE) + { + mBranch = ASMIT_BCS; + mIns[sz - 2].mType = ASMIT_NOP; mIns[sz - 2].mMode = ASMIM_IMPLIED; + mIns[sz - 1].mType = ASMIT_NOP; mIns[sz - 1].mMode = ASMIM_IMPLIED; + changed = true; + } + else if (mBranch == ASMIT_BEQ) + { + mBranch = ASMIT_BCC; + mIns[sz - 2].mType = ASMIT_NOP; mIns[sz - 2].mMode = ASMIM_IMPLIED; + mIns[sz - 1].mType = ASMIT_NOP; mIns[sz - 1].mMode = ASMIM_IMPLIED; + changed = true; + } + } + + if (sz >= 2 && + mIns[sz - 2].mType == ASMIT_LDA && mIns[sz - 2].mMode == ASMIM_IMMEDIATE && mIns[sz - 2].mAddress == 0 && + mIns[sz - 1].mType == ASMIT_CMP) + { + if (mBranch == ASMIT_BNE) + { + if (mTrueJump->mIns.Size() == 0 && mTrueJump->mBranch == ASMIT_BCC) + { + mTrueJump = mTrueJump->mTrueJump; + changed = true; + } + else if (mTrueJump->mIns.Size() == 0 && mTrueJump->mBranch == ASMIT_BCS) + { + mTrueJump = mTrueJump->mFalseJump; + changed = true; + } + } + else if (mBranch == ASMIT_BEQ) + { + if (mFalseJump->mIns.Size() == 0 && mFalseJump->mBranch == ASMIT_BCS) + { + mFalseJump = mFalseJump->mTrueJump; + changed = true; + } + else if (mFalseJump->mIns.Size() == 0 && mFalseJump->mBranch == ASMIT_BCC) + { + mFalseJump = mFalseJump->mFalseJump; + changed = true; + } + } + } + else if (sz >= 2 && + mIns[sz - 2].ChangesAccuAndFlag() && + mIns[sz - 1].mType == ASMIT_CMP && mIns[sz - 1].mMode == ASMIM_IMMEDIATE && mIns[sz - 1].mAddress == 1 && + (mBranch == ASMIT_BCC || mBranch == ASMIT_BCS) && !mExitRequiredRegs[CPU_REG_Z] && !mExitRequiredRegs[CPU_REG_C]) + { + if (mBranch == ASMIT_BCC) + mBranch = ASMIT_BEQ; + else + mBranch = ASMIT_BNE; + + mIns[sz - 1].mType = ASMIT_NOP; mIns[sz - 1].mMode = ASMIM_IMPLIED; + mIns[sz - 2].mLive |= LIVE_CPU_REG_Z; + changed = true; + } + else if (sz >= 2 && + (mIns[sz - 2].ChangesAccuAndFlag() || mIns[sz - 2].mType == ASMIT_TAX || mIns[sz - 2].mType == ASMIT_TAY) && + mIns[sz - 1].mType == ASMIT_CMP && mIns[sz - 1].mMode == ASMIM_IMMEDIATE && mIns[sz - 1].mAddress == 0x80 && + (mBranch == ASMIT_BCC || mBranch == ASMIT_BCS) && !mExitRequiredRegs[CPU_REG_Z] && !mExitRequiredRegs[CPU_REG_C]) + { + if (mBranch == ASMIT_BCC) + mBranch = ASMIT_BPL; + else + mBranch = ASMIT_BMI; + + mIns[sz - 1].mType = ASMIT_NOP; mIns[sz - 1].mMode = ASMIM_IMPLIED; + mIns[sz - 2].mLive |= LIVE_CPU_REG_Z; + changed = true; + } + else if (pass != 28 && sz >= 2 && + mIns[sz - 2].mType == ASMIT_LDA && mIns[sz - 2].mMode == ASMIM_IMMEDIATE && + mIns[sz - 1].mType == ASMIT_CMP && mIns[sz - 1].mMode != ASMIM_IMMEDIATE && !(mIns[sz - 1].mLive & LIVE_CPU_REG_A) && + !mExitRequiredRegs[CPU_REG_C] && !mExitRequiredRegs[CPU_REG_Z]) + { + int v = mIns[sz - 2].mAddress; + if (mBranch == ASMIT_BNE || mBranch == ASMIT_BEQ) + { + mIns[sz - 2].CopyMode(mIns[sz - 1]); + mIns[sz - 1].mMode = ASMIM_IMMEDIATE; mIns[sz - 1].mAddress = v; + changed = true; + } + else if (mBranch == ASMIT_BCC) + { + if (v < 255) + { + mIns[sz - 2].CopyMode(mIns[sz - 1]); + mBranch = ASMIT_BCS; + mIns[sz - 1].mMode = ASMIM_IMMEDIATE; mIns[sz - 1].mAddress = v + 1; + changed = true; + } + } + else if (mBranch == ASMIT_BCS) + { + if (v < 255) + { + mIns[sz - 2].CopyMode(mIns[sz - 1]); + mBranch = ASMIT_BCC; + mIns[sz - 1].mMode = ASMIM_IMMEDIATE; mIns[sz - 1].mAddress = v + 1; + changed = true; + } + } + } +#if 1 + else if (sz >= 2 && + mIns[sz - 2].ChangesAccuAndFlag() && + mIns[sz - 1].mType == ASMIT_ASL && mIns[sz - 1].mMode == ASMIM_IMPLIED && !(mIns[sz - 1].mLive & LIVE_CPU_REG_A) && !mExitRequiredRegs[CPU_REG_Z] && !mExitRequiredRegs[CPU_REG_C]) + + { + if (mBranch == ASMIT_BCC) + { + mBranch = ASMIT_BPL; + mIns.SetSize(sz - 1); + sz -= 1; + mIns[sz - 1].mLive |= LIVE_CPU_REG_Z; + changed = true; + } + else if (mBranch == ASMIT_BCS) + { + mBranch = ASMIT_BMI; + mIns.SetSize(sz - 1); + sz -= 1; + mIns[sz - 1].mLive |= LIVE_CPU_REG_Z; + changed = true; + } + } + else if (sz >= 3 && + mIns[sz - 3].ChangesAccuAndFlag() && + mIns[sz - 2].mType == ASMIT_STA && + mIns[sz - 1].mType == ASMIT_ASL && mIns[sz - 1].mMode == ASMIM_IMPLIED && !(mIns[sz - 1].mLive & LIVE_CPU_REG_A) && !mExitRequiredRegs[CPU_REG_Z] && !mExitRequiredRegs[CPU_REG_C]) + + { + if (mBranch == ASMIT_BCC) + { + mBranch = ASMIT_BPL; + mIns.SetSize(sz - 1); + sz -= 1; + mIns[sz - 2].mLive |= LIVE_CPU_REG_Z; + mIns[sz - 1].mLive |= LIVE_CPU_REG_Z; + changed = true; + } + else if (mBranch == ASMIT_BCS) + { + mBranch = ASMIT_BMI; + mIns.SetSize(sz - 1); + sz -= 1; + mIns[sz - 2].mLive |= LIVE_CPU_REG_Z; + mIns[sz - 1].mLive |= LIVE_CPU_REG_Z; + changed = true; + } + } + else if (sz >= 2 && + mIns[sz - 2].mType == ASMIT_LDA && mIns[sz - 2].mMode == ASMIM_IMMEDIATE && mIns[sz - 2].mAddress == 0 && + (mIns[sz - 1].mType == ASMIT_ROL || mIns[sz - 1].mType == ASMIT_ROR) && mIns[sz - 1].mMode == ASMIM_IMPLIED && !(mIns[sz - 1].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C)) && !mExitRequiredRegs[CPU_REG_Z]) + { + if (mBranch == ASMIT_BNE) + { + mBranch = ASMIT_BCS; + mIns.SetSize(sz - 2); + sz -= 2; + changed = true; + } + else if (mBranch == ASMIT_BEQ) + { + mBranch = ASMIT_BCC; + mIns.SetSize(sz - 2); + sz -= 2; + changed = true; + } + } + else if (sz >= 2 && + mIns[sz - 2].mType == ASMIT_LDA && mIns[sz - 2].mMode == ASMIM_IMMEDIATE && mIns[sz - 2].mAddress == 0 && + (mIns[sz - 1].mType == ASMIT_ROL || mIns[sz - 1].mType == ASMIT_ROR) && mIns[sz - 1].mMode == ASMIM_IMPLIED && !(mIns[sz - 1].mLive & LIVE_CPU_REG_C) && !mExitRequiredRegs[CPU_REG_Z] && + mBranch == ASMIT_BNE && !mTrueJump->mEntryRequiredRegs[CPU_REG_A]) + { + mBranch = ASMIT_BCS; + mIns.SetSize(sz - 1); + sz -= 1; + changed = true; + } + else if (sz >= 2 && + mIns[sz - 2].mType == ASMIT_LDA && mIns[sz - 2].mMode == ASMIM_IMMEDIATE && mIns[sz - 2].mAddress == 0 && + (mIns[sz - 1].mType == ASMIT_ROL || mIns[sz - 1].mType == ASMIT_ROR) && mIns[sz - 1].mMode == ASMIM_IMPLIED && !(mIns[sz - 1].mLive & LIVE_CPU_REG_C) && !mExitRequiredRegs[CPU_REG_Z] && + mBranch == ASMIT_BEQ && !mFalseJump->mEntryRequiredRegs[CPU_REG_A]) + { + mBranch = ASMIT_BCC; + mIns.SetSize(sz - 1); + sz -= 1; + changed = true; + } + else if (sz >= 3 && + mIns[sz - 3].mType == ASMIT_LDA && mIns[sz - 3].mMode == ASMIM_IMMEDIATE && mIns[sz - 3].mAddress == 0 && + mIns[sz - 2].mType == ASMIT_ROL && mIns[sz - 2].mMode == ASMIM_IMPLIED && + mIns[sz - 1].mType == ASMIT_CMP && mIns[sz - 1].mMode == ASMIM_IMMEDIATE && mIns[sz - 1].mAddress == 0x0 && !(mIns[sz - 1].mLive & LIVE_CPU_REG_A) && !mExitRequiredRegs[CPU_REG_Z]) + { + if (mBranch == ASMIT_BNE) + { + mBranch = ASMIT_BCS; + mIns.SetSize(sz - 3); + sz -= 3; + changed = true; + } + else if (mBranch == ASMIT_BEQ) + { + mBranch = ASMIT_BCC; + mIns.SetSize(sz - 3); + sz -= 3; + changed = true; + } + } + else if (sz >= 3 && + mIns[sz - 3].mType == ASMIT_LDA && mIns[sz - 3].mMode == ASMIM_IMMEDIATE && mIns[sz - 3].mAddress == 0x00 && + mIns[sz - 2].mType == ASMIT_ADC && mIns[sz - 2].mMode == ASMIM_IMMEDIATE && mIns[sz - 2].mAddress == 0xff && + mIns[sz - 1].mType == ASMIT_EOR && mIns[sz - 1].mMode == ASMIM_IMMEDIATE && mIns[sz - 1].mAddress == 0xff && !(mIns[sz - 1].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C)) && !mExitRequiredRegs[CPU_REG_Z]) + { + if (mBranch == ASMIT_BNE) + { + mBranch = ASMIT_BCS; + mIns.SetSize(sz - 3); + sz -= 3; + changed = true; + } + else if (mBranch == ASMIT_BEQ) + { + mBranch = ASMIT_BCC; + mIns.SetSize(sz - 3); + sz -= 3; + changed = true; + } + } + else if (sz >= 2 && + mIns[sz - 2].mType == ASMIT_EOR && mIns[sz - 2].mMode == ASMIM_IMMEDIATE && mIns[sz - 2].mAddress == 0x80 && + mIns[sz - 1].mType == ASMIT_CMP && mIns[sz - 1].mMode == ASMIM_IMMEDIATE && mIns[sz - 1].mAddress == 0x80 && !(mIns[sz - 1].mLive & LIVE_CPU_REG_A) && !mExitRequiredRegs[CPU_REG_Z]) + { + if (mBranch == ASMIT_BNE && mTrueJump->mIns.Size() == 0 && mTrueJump->mBranch == ASMIT_BCC) + { + NativeCodeBasicBlock* tblock = mProc->AllocateBlock(); + + tblock->mTrueJump = mTrueJump->mTrueJump; + tblock->mFalseJump = mTrueJump->mFalseJump; + tblock->mBranch = ASMIT_BMI; + tblock->mTrueJump->mNumEntries++; + tblock->mTrueJump->mEntryBlocks.Push(tblock); + tblock->mFalseJump->mNumEntries++; + tblock->mFalseJump->mEntryBlocks.Push(tblock); + + mTrueJump->mEntryBlocks.Remove(mTrueJump->mEntryBlocks.IndexOf(this)); + mTrueJump->mNumEntries--; + mTrueJump = tblock; + tblock->mNumEntries++; + tblock->mEntryBlocks.Push(this); + + mIns[sz - 2].mType = ASMIT_NOP; mIns[sz - 2].mMode = ASMIM_IMPLIED; + mIns[sz - 1].mType = ASMIT_ORA; mIns[sz - 1].mAddress = 0; mIns[sz - 1].mLive |= LIVE_CPU_REG_Z; + + mExitRequiredRegs -= CPU_REG_C; + + CheckLive(); + } + } + else if (sz >= 2 && + mIns[sz - 2].ChangesAccuAndFlag() && + mIns[sz - 1].mType == ASMIT_CMP && mIns[sz - 1].mMode == ASMIM_IMMEDIATE && mIns[sz - 1].mAddress == 0x80 && !mExitRequiredRegs[CPU_REG_Z] && !mExitRequiredRegs[CPU_REG_C]) + { + if (mBranch == ASMIT_BCC) + { + mBranch = ASMIT_BPL; + mIns[sz - 2].mLive |= LIVE_CPU_REG_Z; + mIns[sz - 1].mType = ASMIT_NOP; mIns[sz - 1].mMode = ASMIM_IMPLIED; + + CheckLive(); + } + else if (mBranch == ASMIT_BCS) + { + mBranch = ASMIT_BMI; + mIns[sz - 2].mLive |= LIVE_CPU_REG_Z; + mIns[sz - 1].mType = ASMIT_NOP; mIns[sz - 1].mMode = ASMIM_IMPLIED; + + CheckLive(); + } + } + else if (sz >= 2 && + mIns[sz - 2].mType == ASMIT_CMP && mIns[sz - 2].mMode == ASMIM_IMMEDIATE && mIns[sz - 2].mAddress == 0x80 && !mExitRequiredRegs[CPU_REG_Z] && !mExitRequiredRegs[CPU_REG_C] && + (mIns[sz - 1].mType == ASMIT_TAX || mIns[sz - 1].mType == ASMIT_TAY)) + { + if (mBranch == ASMIT_BCC) + { + mBranch = ASMIT_BPL; + mIns[sz - 2].mType = ASMIT_NOP; mIns[sz - 2].mMode = ASMIM_IMPLIED; + mIns[sz - 1].mLive |= LIVE_CPU_REG_Z; + + CheckLive(); + } + else if (mBranch == ASMIT_BCS) + { + mBranch = ASMIT_BMI; + mIns[sz - 2].mType = ASMIT_NOP; mIns[sz - 2].mMode = ASMIM_IMPLIED; + mIns[sz - 1].mLive |= LIVE_CPU_REG_Z; + + CheckLive(); + } + } + else if (sz >= 2 && + mIns[sz - 2].ChangesXReg() && + mIns[sz - 1].mType == ASMIT_CPX && mIns[sz - 1].mMode == ASMIM_IMMEDIATE && mIns[sz - 1].mAddress == 0x80 && !mExitRequiredRegs[CPU_REG_Z] && !mExitRequiredRegs[CPU_REG_C]) + { + if (mBranch == ASMIT_BCC) + { + mBranch = ASMIT_BPL; + mIns[sz - 2].mLive |= LIVE_CPU_REG_Z; + mIns[sz - 1].mType = ASMIT_NOP; mIns[sz - 1].mMode = ASMIM_IMPLIED; + + CheckLive(); + } + else if (mBranch == ASMIT_BCS) + { + mBranch = ASMIT_BMI; + mIns[sz - 2].mLive |= LIVE_CPU_REG_Z; + mIns[sz - 1].mType = ASMIT_NOP; mIns[sz - 1].mMode = ASMIM_IMPLIED; + + CheckLive(); + } + } + else if (sz >= 2 && + mIns[sz - 2].ChangesYReg() && + mIns[sz - 1].mType == ASMIT_CPY && mIns[sz - 1].mMode == ASMIM_IMMEDIATE && mIns[sz - 1].mAddress == 0x80 && !mExitRequiredRegs[CPU_REG_Z] && !mExitRequiredRegs[CPU_REG_C]) + { + if (mBranch == ASMIT_BCC) + { + mBranch = ASMIT_BPL; + mIns[sz - 2].mLive |= LIVE_CPU_REG_Z; + mIns[sz - 1].mType = ASMIT_NOP; mIns[sz - 1].mMode = ASMIM_IMPLIED; + + CheckLive(); + } + else if (mBranch == ASMIT_BCS) + { + mBranch = ASMIT_BMI; + mIns[sz - 2].mLive |= LIVE_CPU_REG_Z; + mIns[sz - 1].mType = ASMIT_NOP; mIns[sz - 1].mMode = ASMIM_IMPLIED; + + CheckLive(); + } + } + + else if (sz >= 4 && + mIns[sz - 4].mType == ASMIT_EOR && mIns[sz - 4].mMode == ASMIM_IMMEDIATE && mIns[sz - 4].mAddress == 0x80 && + mIns[sz - 3].mType == ASMIT_STA && mIns[sz - 3].mMode == ASMIM_ZERO_PAGE && + mIns[sz - 2].mType == ASMIT_LDA && mIns[sz - 2].mMode == ASMIM_IMMEDIATE && mIns[sz - 2].mAddress == 0x80 && + mIns[sz - 1].mType == ASMIT_CMP && mIns[sz - 1].mMode == ASMIM_ZERO_PAGE && mIns[sz - 1].mAddress == mIns[sz - 3].mAddress && !(mIns[sz - 1].mLive & (LIVE_CPU_REG_A | LIVE_MEM)) && !mExitRequiredRegs[CPU_REG_Z]) + { + if (mBranch == ASMIT_BNE && mTrueJump->mIns.Size() == 0 && mTrueJump->mBranch == ASMIT_BCC) + { + NativeCodeBasicBlock* tblock = mProc->AllocateBlock(); + + tblock->mTrueJump = mTrueJump->mFalseJump; + tblock->mFalseJump = mTrueJump->mTrueJump; + tblock->mBranch = ASMIT_BMI; + tblock->mTrueJump->mNumEntries++; + tblock->mTrueJump->mEntryBlocks.Push(tblock); + tblock->mFalseJump->mNumEntries++; + tblock->mFalseJump->mEntryBlocks.Push(tblock); + + mTrueJump->mEntryBlocks.Remove(mTrueJump->mEntryBlocks.IndexOf(this)); + mTrueJump->mNumEntries--; + mTrueJump = tblock; + tblock->mNumEntries++; + tblock->mEntryBlocks.Push(this); + + mIns[sz - 4].mType = ASMIT_NOP; mIns[sz - 4].mMode = ASMIM_IMPLIED; + mIns[sz - 3].mType = ASMIT_NOP; mIns[sz - 3].mMode = ASMIM_IMPLIED; + mIns[sz - 2].mType = ASMIT_NOP; mIns[sz - 2].mMode = ASMIM_IMPLIED; + mIns[sz - 1].mType = ASMIT_ORA; mIns[sz - 1].mMode = ASMIM_IMMEDIATE; mIns[sz - 1].mAddress = 0; + + mIns[sz - 1].mLive &= ~LIVE_CPU_REG_C; + mIns[sz - 1].mLive |= LIVE_CPU_REG_Z; + mExitRequiredRegs -= CPU_REG_C; + + CheckLive(); + } + } + + else if (sz >= 4 && + mIns[sz - 4].mType == ASMIT_TXA && + mIns[sz - 3].mType == ASMIT_CLC && + mIns[sz - 2].mType == ASMIT_ADC && mIns[sz - 2].mMode == ASMIM_IMMEDIATE && mIns[sz - 2].mAddress == 0xff && + mIns[sz - 1].mType == ASMIT_TAX && !(mIns[sz - 1].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z)) && !mExitRequiredRegs[CPU_REG_C]) + { + if (mBranch == ASMIT_BCC || mBranch == ASMIT_BCS) + { + mIns[sz - 4].mType = ASMIT_DEX; mIns[sz - 4].mLive |= LIVE_CPU_REG_X; + mIns[sz - 3].mType = ASMIT_NOP; mIns[sz - 3].mMode = ASMIM_IMPLIED; + mIns[sz - 2].mType = ASMIT_CPX; mIns[sz - 2].mLive |= LIVE_CPU_REG_X | LIVE_CPU_REG_Z; + mIns[sz - 1].mType = ASMIT_NOP; mIns[sz - 1].mMode = ASMIM_IMPLIED; + + if (mBranch == ASMIT_BCC) + mBranch = ASMIT_BEQ; + else + mBranch = ASMIT_BNE; + + CheckLive(); + } + } + + else if (sz >= 3 && + mIns[sz - 3].mType == ASMIT_LDA && mIns[sz - 3].mMode == ASMIM_IMMEDIATE && mIns[sz - 3].mAddress == 0x00 && + mIns[sz - 2].mType == ASMIT_ROL && mIns[sz - 2].mMode == ASMIM_IMPLIED && + mIns[sz - 1].mType == ASMIT_CMP && mIns[sz - 1].mMode == ASMIM_IMMEDIATE && mIns[sz - 1].mAddress == 0x01 && !(mIns[sz - 1].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z)) && !mExitRequiredRegs[CPU_REG_C]) + { + if (mBranch == ASMIT_BCC || mBranch == ASMIT_BCS) + { + mIns.SetSize(sz - 3); + sz -= 3; + changed = true; + } + } +#if 1 + else if (sz >= 4 && + mIns[sz - 4].mType == ASMIT_EOR && mIns[sz - 4].mMode == ASMIM_IMMEDIATE && mIns[sz - 4].mAddress == 0x80 && + mIns[sz - 3].mType == ASMIT_ROL && mIns[sz - 3].mMode == ASMIM_IMPLIED && + mIns[sz - 2].mType == ASMIT_AND && mIns[sz - 2].mMode == ASMIM_IMMEDIATE && mIns[sz - 2].mAddress == 0x01 && + mIns[sz - 1].mType == ASMIT_SBC && mIns[sz - 1].mMode == ASMIM_IMMEDIATE && mIns[sz - 1].mAddress == 0x00 && !(mIns[sz - 1].mLive & LIVE_CPU_REG_A) && !mExitRequiredRegs[CPU_REG_Z] && !mExitRequiredRegs[CPU_REG_C]) + { + if (mBranch == ASMIT_BEQ) + { + mIns.Remove(sz - 4); + mBranch = ASMIT_BNE; + changed = true; + } + else if (mBranch == ASMIT_BNE) + { + mIns.Remove(sz - 4); + mBranch = ASMIT_BEQ; + changed = true; + } + } +#endif + else if (sz >= 2 && + mIns[sz - 2].mType == ASMIT_LDA && mIns[sz - 2].mMode == ASMIM_IMMEDIATE && mIns[sz - 2].mAddress == 0 && + mIns[sz - 1].mType == ASMIT_SBC && mIns[sz - 1].mMode == ASMIM_IMMEDIATE && mIns[sz - 1].mAddress == 0 && !(mIns[sz - 1].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C)) && !mExitRequiredRegs[CPU_REG_Z]) + { + if (mBranch == ASMIT_BNE) + { + mBranch = ASMIT_BCC; + mIns.SetSize(sz - 2); + changed = true; + } + else if (mBranch == ASMIT_BEQ) + { + mBranch = ASMIT_BCS; + mIns.SetSize(sz - 2); + changed = true; + } + + sz -= 2; + CheckLive(); + } + else if (pass >= 7 && sz >= 2 && + mIns[sz - 2].mType == ASMIT_LDA && mIns[sz - 2].mMode == ASMIM_ZERO_PAGE && !(mIns[sz - 2].mLive & LIVE_MEM) && + mIns[sz - 1].mType == ASMIT_AND && mIns[sz - 1].mMode == ASMIM_IMMEDIATE && mIns[sz - 1].mAddress == 1 && !(mIns[sz - 1].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C)) && !mExitRequiredRegs[CPU_REG_Z]) + { + if (mBranch == ASMIT_BNE) + { + mIns[sz - 2].mType = ASMIT_LSR; mIns[sz - 2].mLive |= LIVE_CPU_REG_C; + mBranch = ASMIT_BCS; + mIns.SetSize(sz - 1); + changed = true; + } + else if (mBranch == ASMIT_BEQ) + { + mIns[sz - 2].mType = ASMIT_LSR; mIns[sz - 2].mLive |= LIVE_CPU_REG_C; + mBranch = ASMIT_BCC; + mIns.SetSize(sz - 1); + changed = true; + } + + sz -= 1; + CheckLive(); + } + else if (pass >= 7 && sz >= 2 && + mIns[sz - 2].mType == ASMIT_AND && mIns[sz - 2].mMode == ASMIM_IMMEDIATE && mIns[sz - 2].mAddress == 1 && + mIns[sz - 1].mType == ASMIT_EOR && mIns[sz - 1].mMode == ASMIM_IMMEDIATE && mIns[sz - 1].mAddress == 1 && + !(mIns[sz - 1].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C)) && !mExitRequiredRegs[CPU_REG_Z]) + { + if (mBranch == ASMIT_BNE) + { + mIns[sz - 2].mType = ASMIT_LSR; mIns[sz - 2].mMode = ASMIM_IMPLIED; mIns[sz - 2].mLive |= LIVE_CPU_REG_C; + mBranch = ASMIT_BCC; + mIns.SetSize(sz - 1); + changed = true; + } + else if (mBranch == ASMIT_BEQ) + { + mIns[sz - 2].mType = ASMIT_LSR; mIns[sz - 2].mMode = ASMIM_IMPLIED; mIns[sz - 2].mLive |= LIVE_CPU_REG_C; + mBranch = ASMIT_BCS; + mIns.SetSize(sz - 1); + changed = true; + } + + sz -= 1; + CheckLive(); + } + + sz = mIns.Size(); + if (sz >= 4 && (mBranch == ASMIT_BCC || mBranch == ASMIT_BCS) && + mIns[sz - 4].mType == ASMIT_LDA && !(mIns[sz - 4].ReferencesXReg()) && + mIns[sz - 3].mType == ASMIT_STA && mIns[sz - 3].mMode == ASMIM_ZERO_PAGE && + mIns[sz - 2].mType == ASMIT_ASL && mIns[sz - 2].mMode == ASMIM_IMPLIED && + mIns[sz - 1].mType == ASMIT_LDX && mIns[sz - 1].mMode == ASMIM_IMMEDIATE && + !(mIns[sz - 1].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z)) && !mExitRequiredRegs[CPU_REG_C]) + { + mIns[sz - 2] = mIns[sz - 3]; mIns[sz - 2].mLive |= LIVE_CPU_REG_X | LIVE_CPU_REG_Z; + mIns[sz - 3] = mIns[sz - 4]; mIns[sz - 3].mLive |= LIVE_CPU_REG_X | LIVE_CPU_REG_Z; + mIns[sz - 4] = mIns[sz - 1]; + mIns[sz - 1].mType = ASMIT_NOP; mIns[sz - 1].mMode = ASMIM_IMPLIED; + mIns[sz - 3].RequiresYReg(); mIns[sz - 4].mLive |= LIVE_CPU_REG_Y; + + if (mBranch == ASMIT_BCC) + mBranch = ASMIT_BPL; + else + mBranch = ASMIT_BMI; + + CheckLive(); + } +#if 1 + sz = mIns.Size(); + if (sz >= 2 && mIns[sz - 1].mType == ASMIT_CPX && mIns[sz - 1].mMode == ASMIM_IMMEDIATE && mIns[sz - 1].mAddress == 0 && !(mIns[sz - 1].mLive & LIVE_CPU_REG_C)) + { + int k = sz - 2; + while (k >= 0 && !(mIns[k].ChangesZFlag() || mIns[k].ChangesXReg())) + k--; + if (k >= 0 && (mIns[k].mType == ASMIT_LDX || mIns[k].mType == ASMIT_INX || mIns[k].mType == ASMIT_DEX || mIns[k].mType == ASMIT_TAX)) + { + while (k < sz) + mIns[k++].mLive |= LIVE_CPU_REG_Z; + mIns.Remove(sz - 1); + changed = true; + } + } + else if (sz >= 2 && mIns[sz - 1].mType == ASMIT_CPY && mIns[sz - 1].mMode == ASMIM_IMMEDIATE && mIns[sz - 1].mAddress == 0 && !(mIns[sz - 1].mLive & LIVE_CPU_REG_C)) + { + int k = sz - 2; + while (k >= 0 && !(mIns[k].ChangesZFlag() || mIns[k].ChangesYReg())) + k--; + if (k >= 0 && (mIns[k].mType == ASMIT_LDY || mIns[k].mType == ASMIT_INY || mIns[k].mType == ASMIT_DEY || mIns[k].mType == ASMIT_TAY)) + { + while (k < sz) + mIns[k++].mLive |= LIVE_CPU_REG_Z; + mIns.Remove(sz - 1); + changed = true; + } + } +#endif + sz = mIns.Size(); + if (sz >= 1 && + mIns[sz - 1].mType == ASMIT_AND && mIns[sz - 1].mMode == ASMIM_IMMEDIATE && mIns[sz - 1].mAddress == 0x80 && !(mIns[sz - 1].mLive & LIVE_CPU_REG_A) && + (mBranch == ASMIT_BEQ || mBranch == ASMIT_BNE)) + { + mIns[sz - 1].mType = ASMIT_ORA; mIns[sz - 1].mAddress = 0x00; mIns[sz - 1].mLive |= LIVE_CPU_REG_Z; + + if (mBranch == ASMIT_BEQ) + mBranch = ASMIT_BPL; + else + mBranch = ASMIT_BMI; + + changed = true; + } + else if (pass >= 7 && sz >= 1 && + mIns[sz - 1].mType == ASMIT_AND && mIns[sz - 1].mMode == ASMIM_IMMEDIATE && mIns[sz - 1].mAddress == 0x01 && !(mIns[sz - 1].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C)) && + (mBranch == ASMIT_BEQ || mBranch == ASMIT_BNE) && !mExitRequiredRegs[CPU_REG_Z]) + { + mIns[sz - 1].mType = ASMIT_LSR; mIns[sz - 1].mLive |= LIVE_CPU_REG_C; mIns[sz - 1].mMode = ASMIM_IMPLIED; + + if (mBranch == ASMIT_BEQ) + mBranch = ASMIT_BCC; + else + mBranch = ASMIT_BCS; + + changed = true; + } +#if 1 + else if (pass >= 7 && sz >= 1 && + mIns[sz - 1].mType == ASMIT_LDA && (mIns[sz - 1].mMode == ASMIM_ABSOLUTE || mIns[sz - 1].mMode == ASMIM_ZERO_PAGE) && !(mIns[sz - 1].mLive & LIVE_CPU_REG_A) && + (mBranch == ASMIT_BPL || mBranch == ASMIT_BMI) && !mExitRequiredRegs[CPU_REG_Z]) + { + mIns[sz - 1].mType = ASMIT_BIT; + + changed = true; + } +#endif + else if (pass >= 7 && sz >= 1 && + mIns[sz - 1].mType == ASMIT_AND && mIns[sz - 1].mMode == ASMIM_IMMEDIATE && mIns[sz - 1].mAddress == 0x40 && !(mIns[sz - 1].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C)) && + (mBranch == ASMIT_BEQ || mBranch == ASMIT_BNE) && !mExitRequiredRegs[CPU_REG_Z]) + { + mIns[sz - 1].mType = ASMIT_ASL; mIns[sz - 1].mMode = ASMIM_IMPLIED; + + if (mBranch == ASMIT_BEQ) + mBranch = ASMIT_BPL; + else + mBranch = ASMIT_BMI; + + changed = true; + } + + if (sz == 1 && mBranch == ASMIT_BNE && mTrueJump == this) + { + if (mIns[0].mType == ASMIT_DEX || mIns[0].mType == ASMIT_INX) + { + mIns[0] = NativeCodeInstruction(mIns[0].mIns, ASMIT_LDX, ASMIM_IMMEDIATE, 0); + changed = true; + } + else if (mIns[0].mType == ASMIT_DEY || mIns[0].mType == ASMIT_INY) + { + mIns[0] = NativeCodeInstruction(mIns[0].mIns, ASMIT_LDY, ASMIM_IMMEDIATE, 0); + changed = true; + } + } + +#endif +#endif + +#if 1 + if (sz >= 4 && (mBranch == ASMIT_BMI || mBranch == ASMIT_BPL) && !mExitRequiredRegs[CPU_REG_Z]) + { + if (mIns[sz - 4].mType == ASMIT_LDA && + (mIns[sz - 3].mType == ASMIT_ASL || mIns[sz - 3].mType == ASMIT_ROL) && mIns[sz - 3].mMode == ASMIM_IMPLIED && + mIns[sz - 2].mType == ASMIT_STA && !mIns[sz - 2].SameEffectiveAddress(mIns[sz - 4]) && + mIns[sz - 1].mType == ASMIT_LDA && mIns[sz - 1].SameEffectiveAddress(mIns[sz - 4])) + { + if (mBranch == ASMIT_BMI) + mBranch = ASMIT_BCS; + else + mBranch = ASMIT_BCC; + mIns[sz - 3].mLive |= LIVE_CPU_REG_C; + mIns[sz - 2].mLive |= LIVE_CPU_REG_C; + mIns[sz - 1].mLive |= LIVE_CPU_REG_C; + changed = true; + } + } + + if (sz >= 4 && (mBranch == ASMIT_BMI || mBranch == ASMIT_BPL) && !mExitRequiredRegs[CPU_REG_Z]) + { + if (mIns[sz - 4].mType == ASMIT_STA && + (mIns[sz - 3].mType == ASMIT_ASL || mIns[sz - 3].mType == ASMIT_ROL) && mIns[sz - 3].mMode == ASMIM_IMPLIED && + mIns[sz - 2].mType == ASMIT_STA && !mIns[sz - 2].SameEffectiveAddress(mIns[sz - 4]) && + mIns[sz - 1].mType == ASMIT_LDA && mIns[sz - 1].SameEffectiveAddress(mIns[sz - 4])) + { + if (mBranch == ASMIT_BMI) + mBranch = ASMIT_BCS; + else + mBranch = ASMIT_BCC; + mIns[sz - 3].mLive |= LIVE_CPU_REG_C; + mIns[sz - 2].mLive |= LIVE_CPU_REG_C; + mIns[sz - 1].mLive |= LIVE_CPU_REG_C; + changed = true; + } + } + +#endif +#if 1 + if (sz >= 3 && (mBranch == ASMIT_BEQ || mBranch == ASMIT_BNE) && !mExitRequiredRegs[CPU_REG_Z] && !mExitRequiredRegs[CPU_REG_C] && !mExitRequiredRegs[CPU_REG_A]) + { + if (mIns[sz - 3].mType == ASMIT_LDA && mIns[sz - 3].mMode == ASMIM_IMMEDIATE && mIns[sz - 3].mAddress == 0 && + mIns[sz - 2].mType == ASMIT_ROL && mIns[sz - 2].mMode == ASMIM_IMPLIED && + mIns[sz - 1].mType == ASMIT_EOR && mIns[sz - 1].mMode == ASMIM_IMMEDIATE && mIns[sz - 1].mAddress == 1) + { + if (mBranch == ASMIT_BEQ) + mBranch = ASMIT_BCS; + else + mBranch = ASMIT_BCC; + + mIns[sz - 3].mType = ASMIT_NOP; mIns[sz - 3].mMode = ASMIM_IMPLIED; + mIns[sz - 2].mType = ASMIT_NOP; mIns[sz - 2].mMode = ASMIM_IMPLIED; + mIns[sz - 1].mType = ASMIT_NOP; mIns[sz - 1].mMode = ASMIM_IMPLIED; + changed = true; + } + } + +#endif + + if (sz >= 1 && mIns[sz - 1].mType == ASMIT_TXA && !(mIns[sz - 1].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C))) + { + mIns[sz - 1].mType = ASMIT_CPX; + mIns[sz - 1].mMode = ASMIM_IMMEDIATE; + mIns[sz - 1].mAddress = 0; + changed = true; + } + if (sz >= 1 && mIns[sz - 1].mType == ASMIT_TYA && !(mIns[sz - 1].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C))) + { + mIns[sz - 1].mType = ASMIT_CPY; + mIns[sz - 1].mMode = ASMIM_IMMEDIATE; + mIns[sz - 1].mAddress = 0; + changed = true; + } + + if (sz >= 4 && (mBranch == ASMIT_BCC || mBranch == ASMIT_BCS) && !mExitRequiredRegs[CPU_REG_C]) + { + if (mIns[sz - 4].mType == ASMIT_TYA && + mIns[sz - 3].mType == ASMIT_CLC && + mIns[sz - 2].mType == ASMIT_ADC && mIns[sz - 2].mMode == ASMIM_IMMEDIATE && mIns[sz - 2].mAddress == 1 && + mIns[sz - 1].mType == ASMIT_TAY) + { + if (mBranch == ASMIT_BCC) + mBranch = ASMIT_BNE; + else + mBranch = ASMIT_BEQ; + mIns[sz - 4].mType = ASMIT_INY; mIns[sz - 4].mLive |= LIVE_CPU_REG_Y; + mIns[sz - 3].mType = ASMIT_NOP; mIns[sz - 3].mMode = ASMIM_IMPLIED; + mIns[sz - 2].mType = ASMIT_NOP; mIns[sz - 2].mMode = ASMIM_IMPLIED; + mIns[sz - 1].mType = ASMIT_TYA; mIns[sz - 1].mLive |= LIVE_CPU_REG_Z; + changed = true; + } + else if (mIns[sz - 4].mType == ASMIT_TXA && + mIns[sz - 3].mType == ASMIT_CLC && + mIns[sz - 2].mType == ASMIT_ADC && mIns[sz - 2].mMode == ASMIM_IMMEDIATE && mIns[sz - 2].mAddress == 1 && + mIns[sz - 1].mType == ASMIT_TAX) + { + if (mBranch == ASMIT_BCC) + mBranch = ASMIT_BNE; + else + mBranch = ASMIT_BEQ; + mIns[sz - 4].mType = ASMIT_INX; mIns[sz - 4].mLive |= LIVE_CPU_REG_X; + mIns[sz - 3].mType = ASMIT_NOP; mIns[sz - 3].mMode = ASMIM_IMPLIED; + mIns[sz - 2].mType = ASMIT_NOP; mIns[sz - 2].mMode = ASMIM_IMPLIED; + mIns[sz - 1].mType = ASMIT_TXA; mIns[sz - 1].mLive |= LIVE_CPU_REG_Z; + changed = true; + } + } + +#if 1 + if (sz >= 3 && (mBranch == ASMIT_BCC || mBranch == ASMIT_BCS) && + mIns[sz - 1].mType == ASMIT_INY && mIns[sz - 2].mType == ASMIT_CPX && mIns[sz - 3].mType == ASMIT_INX && + !(mIns[sz - 1].mLive & LIVE_CPU_REG_Z)) + { + mIns[sz - 1] = mIns[sz - 2]; + mIns[sz - 2] = mIns[sz - 3]; + mIns[sz - 3].mType = ASMIT_INY; + changed = true; + } +#endif +#if 1 + if (sz >= 3 && (mBranch == ASMIT_BCC || mBranch == ASMIT_BCS) && !mExitRequiredRegs[CPU_REG_C]) + { + if (mIns[sz - 3].mType == ASMIT_LDA && !(mIns[sz - 3].ReferencesXReg()) && + mIns[sz - 2].mType == ASMIT_ASL && mIns[sz - 2].mMode == ASMIM_IMPLIED && !(mIns[sz - 2].mLive & LIVE_CPU_REG_A) && + mIns[sz - 1].mType == ASMIT_LDX) + { + mIns[sz - 2] = mIns[sz - 3]; + mIns[sz - 3] = mIns[sz - 1]; + mIns[sz - 1].mType = ASMIT_NOP; mIns[sz - 1].mMode = ASMIM_IMPLIED; + + mIns[sz - 2].mLive |= LIVE_CPU_REG_Z | LIVE_CPU_REG_X; + if (mIns[sz - 2].ReferencesYReg()) mIns[sz - 3].mLive |= LIVE_CPU_REG_Y; + if (mBranch == ASMIT_BCC) + mBranch = ASMIT_BPL; + else + mBranch = ASMIT_BMI; + changed = true; + } + } +#endif + +#if 1 + if (pass == 28 && sz >= 2 && mFalseJump && + mIns[sz - 2].mType == ASMIT_LDA && mIns[sz - 2].mMode != ASMIM_IMMEDIATE && mIns[sz - 2].mMode != ASMIM_ZERO_PAGE && + mIns[sz - 1].mType == ASMIT_CMP && mIns[sz - 1].mMode == ASMIM_IMMEDIATE && !(mIns[sz - 1].mLive & LIVE_CPU_REG_A) && + !mExitRequiredRegs[CPU_REG_C] && !mExitRequiredRegs[CPU_REG_Z]) + { + int v = mIns[sz - 1].mAddress; + if ((mBranch == ASMIT_BEQ || mBranch == ASMIT_BNE) && !(mIns[sz - 1].mLive & LIVE_CPU_REG_C)) + { + mIns[sz - 1].CopyMode(mIns[sz - 2]); mIns[sz - 2].mLive |= mIns[sz - 1].NeedsLive(); + mIns[sz - 2].mMode = ASMIM_IMMEDIATE; + mIns[sz - 2].mAddress = v; + changed = true; + } + else if (mBranch == ASMIT_BCC && !(mIns[sz - 1].mLive & LIVE_CPU_REG_Z) && v > 0) + { + mBranch = ASMIT_BCS; + mIns[sz - 1].CopyMode(mIns[sz - 2]); mIns[sz - 2].mLive |= mIns[sz - 1].NeedsLive(); + mIns[sz - 2].mMode = ASMIM_IMMEDIATE; + mIns[sz - 2].mAddress = v - 1; + changed = true; + } + else if (mBranch == ASMIT_BCS && !(mIns[sz - 1].mLive & LIVE_CPU_REG_Z) && v > 0) + { + mBranch = ASMIT_BCC; + mIns[sz - 1].CopyMode(mIns[sz - 2]); mIns[sz - 2].mLive |= mIns[sz - 1].NeedsLive(); + mIns[sz - 2].mMode = ASMIM_IMMEDIATE; + mIns[sz - 2].mAddress = v - 1; + changed = true; + } + } +#endif + +#if 1 + if (sz > 1 && mFalseJump && + mIns[sz - 2].mType == ASMIT_LDA && mIns[sz - 2].mMode == ASMIM_IMMEDIATE && mIns[sz - 2].mAddress == 0xff && + mIns[sz - 1].mType == ASMIT_CMP && + !mExitRequiredRegs[CPU_REG_C] && !mExitRequiredRegs[CPU_REG_Z]) + { + if (mBranch == ASMIT_BCC) + { + mTrueJump->RemEntryBlock(this); + mTrueJump = mFalseJump; + mFalseJump = nullptr; + mBranch = ASMIT_JMP; + changed = true; + } + else if (mBranch == ASMIT_BCS) + { + mFalseJump->RemEntryBlock(this); + mFalseJump = nullptr; + mBranch = ASMIT_JMP; + changed = true; + } + } +#endif + + if (sz > 0 && mFalseJump && (mIns[sz - 1].mType == ASMIT_CMP || mIns[sz - 1].mType == ASMIT_CPX || mIns[sz - 1].mType == ASMIT_CPY) && + mIns[sz - 1].mMode == ASMIM_IMMEDIATE && mIns[sz - 1].mAddress == 1 && !(mIns[sz - 1].mLive & LIVE_CPU_REG_Z) && + !mExitRequiredRegs[CPU_REG_C] && !mExitRequiredRegs[CPU_REG_Z]) + { + if (mBranch == ASMIT_BCC) + { + mIns[sz - 1].mAddress = 0; + mBranch = ASMIT_BEQ; + mIns[sz - 1].mLive |= LIVE_CPU_REG_Z; + changed = true; + } + else if (mBranch == ASMIT_BCS) + { + mIns[sz - 1].mAddress = 0; + mBranch = ASMIT_BNE; + mIns[sz - 1].mLive |= LIVE_CPU_REG_Z; + changed = true; + } + } +#if 1 + if (sz > 0 && mFalseJump && mIns[sz - 1].mType == ASMIT_CMP && mIns[sz - 1].mMode == ASMIM_IMMEDIATE) + { + if (mBranch == ASMIT_BCC) + { + if (mTrueJump->mIns.Size() == 1 && mTrueJump->mIns[0].mType == ASMIT_CMP && mTrueJump->mIns[0].mMode == ASMIM_IMMEDIATE) + { + if (mTrueJump->mBranch == ASMIT_BCC && mTrueJump->mIns[0].mAddress >= mIns[sz - 1].mAddress && + !mTrueJump->mExitRequiredRegs[CPU_REG_Z] && !mTrueJump->mExitRequiredRegs[CPU_REG_C]) + { + mTrueJump->RemEntryBlock(this); + mTrueJump = mTrueJump->mTrueJump; + mTrueJump->AddEntryBlock(this); + changed = true; + } + else if (mTrueJump->mBranch == ASMIT_BCS && mTrueJump->mIns[0].mAddress >= mIns[sz - 1].mAddress && + !mTrueJump->mExitRequiredRegs[CPU_REG_Z] && !mTrueJump->mExitRequiredRegs[CPU_REG_C]) + { + mTrueJump->RemEntryBlock(this); + mTrueJump = mTrueJump->mFalseJump; + mTrueJump->AddEntryBlock(this); + changed = true; + } + } + } + else if (mBranch == ASMIT_BCS) + { + if (mFalseJump->mIns.Size() == 1 && mFalseJump->mIns[0].mType == ASMIT_CMP && mFalseJump->mIns[0].mMode == ASMIM_IMMEDIATE) + { + if (mFalseJump->mBranch == ASMIT_BCC && mFalseJump->mIns[0].mAddress >= mIns[sz - 1].mAddress && + !mFalseJump->mExitRequiredRegs[CPU_REG_Z] && !mFalseJump->mExitRequiredRegs[CPU_REG_C]) + { + mFalseJump->RemEntryBlock(this); + mFalseJump = mFalseJump->mTrueJump; + mFalseJump->AddEntryBlock(this); + changed = true; + } + else if (mFalseJump->mBranch == ASMIT_BCS && mFalseJump->mIns[0].mAddress >= mIns[sz - 1].mAddress && + !mFalseJump->mExitRequiredRegs[CPU_REG_Z] && !mFalseJump->mExitRequiredRegs[CPU_REG_C]) + { + mFalseJump->RemEntryBlock(this); + mFalseJump = mFalseJump->mFalseJump; + mFalseJump->AddEntryBlock(this); + changed = true; + } + } + } + } +#endif + +#if 1 + if (sz >= 1 && mIns[sz - 1].mType == ASMIT_CMP && mIns[sz - 1].mMode == ASMIM_IMMEDIATE && mIns[sz - 1].mAddress == 0x80 && !(mIns[sz - 1].mLive & LIVE_CPU_REG_Z)) + { + if (mBranch == ASMIT_BCC || mBranch == ASMIT_BCS) + { + AsmInsType ty = ASMIT_INV; + NativeCodeBasicBlock* nblock = nullptr; + int tr; + + if (mTrueJump->mIns.Size() > 0 && mTrueJump->mIns[0].mType == ASMIT_TAY && !(mTrueJump->mIns[0].mLive & LIVE_CPU_REG_A) && !mFalseJump->mEntryRequiredRegs[CPU_REG_A] && !mFalseJump->mEntryRequiredRegs[CPU_REG_Y]) + { + ty = ASMIT_TAY; + tr = CPU_REG_Y; + nblock = mTrueJump; + } + else if (mTrueJump->mIns.Size() > 0 && mTrueJump->mIns[0].mType == ASMIT_TAX && !(mTrueJump->mIns[0].mLive & LIVE_CPU_REG_A) && !mFalseJump->mEntryRequiredRegs[CPU_REG_A] && !mFalseJump->mEntryRequiredRegs[CPU_REG_X]) + { + ty = ASMIT_TAX; + tr = CPU_REG_X; + nblock = mTrueJump; + } + else if (mFalseJump->mIns.Size() > 0 && mFalseJump->mIns[0].mType == ASMIT_TAY && !(mFalseJump->mIns[0].mLive & LIVE_CPU_REG_A) && !mTrueJump->mEntryRequiredRegs[CPU_REG_A] && !mTrueJump->mEntryRequiredRegs[CPU_REG_Y]) + { + ty = ASMIT_TAY; + tr = CPU_REG_Y; + nblock = mFalseJump; + } + else if (mFalseJump->mIns.Size() > 0 && mFalseJump->mIns[0].mType == ASMIT_TAX && !(mFalseJump->mIns[0].mLive & LIVE_CPU_REG_A) && !mTrueJump->mEntryRequiredRegs[CPU_REG_A] && !mTrueJump->mEntryRequiredRegs[CPU_REG_X]) + { + ty = ASMIT_TAX; + tr = CPU_REG_X; + nblock = mFalseJump; + } + + if (nblock) + { + mIns[sz - 1].mType = ty; + mIns[sz - 1].mMode = ASMIM_IMPLIED; + if (tr == CPU_REG_Y) + mIns[sz - 1].mLive |= LIVE_CPU_REG_Y | LIVE_CPU_REG_Z; + else + mIns[sz - 1].mLive |= LIVE_CPU_REG_X | LIVE_CPU_REG_Z; + nblock->mIns.Remove(0); + mExitRequiredRegs += tr; + nblock->mEntryRequiredRegs += tr; + if (mBranch == ASMIT_BCC) + mBranch = ASMIT_BPL; + else + mBranch = ASMIT_BMI; + changed = true; + } + } + } +#endif + +#if 1 + if (sz > 0 && mFalseJump && mIns[sz - 1].ChangesAccuAndFlag() && mBranch == ASMIT_BMI) + { + if (mFalseJump->mIns.Size() == 1 && mFalseJump->mIns[0].mType == ASMIT_CMP && mFalseJump->mBranch == ASMIT_BCS && mFalseJump->mTrueJump == mTrueJump) + { + mTrueJump->RemEntryBlock(this); + mTrueJump = mFalseJump; + mFalseJump = nullptr; + mBranch = ASMIT_JMP; + changed = true; + } + } +#endif + + sz = mIns.Size(); + if (sz >= 4 && mFalseJump && (mBranch == ASMIT_BPL || mBranch == ASMIT_BMI)) + { + if (mIns[sz - 4].mType == ASMIT_TAY && + mIns[sz - 3].mType == ASMIT_ASL && mIns[sz - 3].mMode == ASMIM_IMPLIED && + mIns[sz - 2].mType == ASMIT_TAX && + mIns[sz - 1].mType == ASMIT_TYA && !(mIns[sz - 1].mLive & LIVE_CPU_REG_A)) + { + mIns[sz - 3].mLive |= LIVE_CPU_REG_C; + mIns[sz - 2].mLive |= LIVE_CPU_REG_C; + mIns[sz - 1].mType = ASMIT_NOP; + if (mBranch == ASMIT_BPL) + mBranch = ASMIT_BCC; + else + mBranch = ASMIT_BCS; + changed = true; + } + } + + sz = mIns.Size(); + if (sz > 1 && mFalseJump && (mBranch == ASMIT_BEQ || mBranch == ASMIT_BNE || mBranch == ASMIT_BPL || mBranch == ASMIT_BMI) && mIns[sz - 1].mType == ASMIT_LDA && mIns[sz - 1].mMode == ASMIM_ZERO_PAGE && !(mIns[sz - 1].mLive & LIVE_CPU_REG_A)) + { + int reg = mIns[sz - 1].mAddress; + int k = sz - 2; + while (k >= 0 && !mIns[k].ReferencesZeroPage(reg)) + k--; + + if (k >= 0 && (mIns[k].mType == ASMIT_INC || mIns[k].mType == ASMIT_DEC)) + { + mIns[sz - 1].mType = mIns[k].mType; + mIns[k].mType = ASMIT_NOP; mIns[k].mMode = ASMIM_IMPLIED; + changed = true; + } + } + +#if 1 + if (pass > 15 && sz > 0 && (mIns[sz - 1].mType == ASMIT_CMP || mIns[sz - 1].mType == ASMIT_CPX || mIns[sz - 1].mType == ASMIT_CPY)) + { + if (mTrueJump && mTrueJump->mEntryBlocks.Size() == 1 && mTrueJump->mIns.Size() > 0 && mTrueJump->mIns[0].IsSame(mIns[sz - 1])) + { + mTrueJump->mIns[0].mType = ASMIT_NOP; mTrueJump->mIns[0].mMode = ASMIM_IMPLIED; + if (mTrueJump->mIns[0].mLive & LIVE_CPU_REG_Z) + { + mExitRequiredRegs += CPU_REG_Z; + mTrueJump->mEntryRequiredRegs += CPU_REG_Z; + mIns[sz - 1].mLive |= LIVE_CPU_REG_Z; + } + if (mTrueJump->mIns[0].mLive & LIVE_CPU_REG_C) + { + mExitRequiredRegs += CPU_REG_C; + mTrueJump->mEntryRequiredRegs += CPU_REG_C; + mIns[sz - 1].mLive |= LIVE_CPU_REG_C; + } + changed = true; + } + if (mFalseJump && mFalseJump->mEntryBlocks.Size() == 1 && mFalseJump->mIns.Size() > 0 && mFalseJump->mIns[0].IsSame(mIns[sz - 1])) + { + mFalseJump->mIns[0].mType = ASMIT_NOP; mFalseJump->mIns[0].mMode = ASMIM_IMPLIED; + if (mFalseJump->mIns[0].mLive & LIVE_CPU_REG_Z) + { + mExitRequiredRegs += CPU_REG_Z; + mFalseJump->mEntryRequiredRegs += CPU_REG_Z; + mIns[sz - 1].mLive |= LIVE_CPU_REG_Z; + } + if (mFalseJump->mIns[0].mLive & LIVE_CPU_REG_C) + { + mExitRequiredRegs += CPU_REG_C; + mFalseJump->mEntryRequiredRegs += CPU_REG_C; + mIns[sz - 1].mLive |= LIVE_CPU_REG_C; + } + changed = true; + } + } +#endif + CheckLive(); + if (mTrueJump) + mTrueJump->CheckLive(); + if (mFalseJump) + mFalseJump->CheckLive(); + + if (sz >= 9 && mFalseJump && (mBranch == ASMIT_BEQ || mBranch == ASMIT_BNE || mBranch == ASMIT_BPL || mBranch == ASMIT_BMI) && + mIns[sz - 9].mType == ASMIT_TAY && + mIns[sz - 8].mType == ASMIT_CLC && + mIns[sz - 7].mType == ASMIT_LDA && mIns[sz - 7].mMode == ASMIM_ZERO_PAGE && + mIns[sz - 6].mType == ASMIT_ADC && mIns[sz - 6].mMode == ASMIM_IMMEDIATE && + mIns[sz - 5].mType == ASMIT_STA && mIns[sz - 5].mMode == ASMIM_ZERO_PAGE && mIns[sz - 5].mAddress == mIns[sz - 7].mAddress && + mIns[sz - 4].mType == ASMIT_LDA && mIns[sz - 4].mMode == ASMIM_ZERO_PAGE && + mIns[sz - 3].mType == ASMIT_ADC && mIns[sz - 3].mMode == ASMIM_IMMEDIATE && + mIns[sz - 2].mType == ASMIT_STA && mIns[sz - 2].mMode == ASMIM_ZERO_PAGE && mIns[sz - 2].mAddress == mIns[sz - 4].mAddress && + mIns[sz - 1].mType == ASMIT_TYA && !(mIns[sz - 1].mLive & LIVE_CPU_REG_C)) + { + NativeCodeBasicBlock* ublock = nullptr; + if (mTrueJump->mEntryBlocks.Size() == 1 && !mFalseJump->mEntryRequiredRegs[mIns[sz - 5].mAddress] && !mFalseJump->mEntryRequiredRegs[mIns[sz - 2].mAddress]) + ublock = mTrueJump; + else if (mFalseJump->mEntryBlocks.Size() == 1 && !mTrueJump->mEntryRequiredRegs[mIns[sz - 5].mAddress] && !mTrueJump->mEntryRequiredRegs[mIns[sz - 2].mAddress]) + ublock = mFalseJump; + + if (ublock) + { + mIns[sz - 9].mLive |= LIVE_CPU_REG_Z | LIVE_CPU_REG_A; + mExitProvidedRegs += CPU_REG_Y; + mExitProvidedRegs += CPU_REG_A; + ublock->mEntryRequiredRegs += CPU_REG_Y; + for (int i = 0; i < 8; i++) + ublock->mIns.Insert(i, mIns[sz - 8 + i]); + mIns.Remove(sz - 8, 8); + changed = true; + } + } + + +#if 1 + if (mTrueJump && mFalseJump && !mTrueJump->mFalseJump && !mFalseJump->mFalseJump && mTrueJump->mTrueJump == mFalseJump->mTrueJump && + mTrueJump->mIns.Size() == 1 && mFalseJump->mIns.Size() == 1 && + mTrueJump->mIns[0].mType == ASMIT_LDA && mTrueJump->mIns[0].mMode == ASMIM_IMMEDIATE && + mFalseJump->mIns[0].mType == ASMIT_LDA && mFalseJump->mIns[0].mMode == ASMIM_IMMEDIATE) + { + if (mBranch == ASMIT_BCS && mTrueJump->mIns[0].mAddress == 1 && mFalseJump->mIns[0].mAddress == 0 && !(mExitRequiredRegs[CPU_REG_C])) + { + mIns.Push(NativeCodeInstruction(mBranchIns, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + mIns.Push(NativeCodeInstruction(mBranchIns, ASMIT_ROL, ASMIM_IMPLIED)); + mExitProvidedRegs += CPU_REG_A; + mBranch = ASMIT_JMP; + mTrueJump = mTrueJump->mTrueJump; + mFalseJump = nullptr; + changed = true; + + CheckLive(); + } + else if (mBranch == ASMIT_BCC && mTrueJump->mIns[0].mAddress == 0 && mFalseJump->mIns[0].mAddress == 1 && !(mExitRequiredRegs[CPU_REG_C])) + { + mIns.Push(NativeCodeInstruction(mBranchIns, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + mIns.Push(NativeCodeInstruction(mBranchIns, ASMIT_ROL, ASMIM_IMPLIED)); + mExitProvidedRegs += CPU_REG_A; + mBranch = ASMIT_JMP; + mTrueJump = mTrueJump->mTrueJump; + mFalseJump = nullptr; + changed = true; + + CheckLive(); + } + else if (mBranch == ASMIT_BCS && mTrueJump->mIns[0].mAddress == 0 && mFalseJump->mIns[0].mAddress == 1 && !(mExitRequiredRegs[CPU_REG_C])) + { + mIns.Push(NativeCodeInstruction(mBranchIns, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + mIns.Push(NativeCodeInstruction(mBranchIns, ASMIT_ROL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(mBranchIns, ASMIT_EOR, ASMIM_IMMEDIATE, 1)); + mExitProvidedRegs += CPU_REG_A; + mBranch = ASMIT_JMP; + mTrueJump = mTrueJump->mTrueJump; + mFalseJump = nullptr; + changed = true; + + CheckLive(); + } + else if (mBranch == ASMIT_BCC && mTrueJump->mIns[0].mAddress == 1 && mFalseJump->mIns[0].mAddress == 0 && !(mExitRequiredRegs[CPU_REG_C])) + { + mIns.Push(NativeCodeInstruction(mBranchIns, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + mIns.Push(NativeCodeInstruction(mBranchIns, ASMIT_ROL, ASMIM_IMPLIED)); + mIns.Push(NativeCodeInstruction(mBranchIns, ASMIT_EOR, ASMIM_IMMEDIATE, 1)); + mExitProvidedRegs += CPU_REG_A; + mBranch = ASMIT_JMP; + mTrueJump = mTrueJump->mTrueJump; + mFalseJump = nullptr; + changed = true; + + CheckLive(); + } + else if (mIns.Size() > 0 && mIns.Last().mType == ASMIT_LDA) + { + if (mBranch == ASMIT_BEQ && mTrueJump->mIns[0].mAddress == 1 && mFalseJump->mIns[0].mAddress == 0) + { + mIns.Insert(mIns.Size() - 1, NativeCodeInstruction(mBranchIns, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + mIns[mIns.Size() - 1].mType = ASMIT_CMP; mIns[mIns.Size() - 1].mLive |= LIVE_CPU_REG_C | LIVE_CPU_REG_A; + mIns.Push(NativeCodeInstruction(mBranchIns, ASMIT_ROL, ASMIM_IMPLIED)); + mExitProvidedRegs += CPU_REG_A; + mBranch = ASMIT_JMP; + mTrueJump = mTrueJump->mTrueJump; + mFalseJump = nullptr; + changed = true; + + CheckLive(); + } + else if (mBranch == ASMIT_BNE && mTrueJump->mIns[0].mAddress == 0 && mFalseJump->mIns[0].mAddress == 1) + { + mIns.Insert(mIns.Size() - 1, NativeCodeInstruction(mBranchIns, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + mIns[mIns.Size() - 1].mType = ASMIT_CMP; mIns[mIns.Size() - 1].mLive |= LIVE_CPU_REG_C | LIVE_CPU_REG_A; + mIns.Push(NativeCodeInstruction(mBranchIns, ASMIT_ROL, ASMIM_IMPLIED)); + mExitProvidedRegs += CPU_REG_A; + mBranch = ASMIT_JMP; + mTrueJump = mTrueJump->mTrueJump; + mFalseJump = nullptr; + changed = true; + + CheckLive(); + } + } + } +#endif + CheckLive(); + + if (mTrueJump) + mTrueJump->CheckLive(); + if (mFalseJump) + mFalseJump->CheckLive(); + + assert(mIndex == 1000 || mNumEntries == mEntryBlocks.Size()); + + return changed; +} + +bool NativeCodeBasicBlock::PeepHoleOptimizer(int pass) +{ + if (!mVisited) + { + assert(mBranch != ASMIT_JMP || mFalseJump == nullptr); + assert(mIns.Size() == 0 || mIns[0].mType != ASMIT_INV); + + assert(mIndex == 1000 || mNumEntries == mEntryBlocks.Size()); + + bool changed = RemoveNops(); + + mIns.Reserve(mIns.Size() * 2 + 32); + + mVisited = true; + + CheckLive(); + if (mTrueJump) + mTrueJump->CheckLive(); + if (mFalseJump) + mFalseJump->CheckLive(); + + if (PeepHoleOptimizerShuffle(pass)) + changed = true; + if (PeepHoleOptimizerIterate(pass)) + changed = true; + if (PeepHoleOptimizerExits(pass)) + changed = true; + + if (changed) + RemoveNops(); + + if (this->mTrueJump && this->mTrueJump->PeepHoleOptimizer(pass)) + changed = true; + if (this->mFalseJump && this->mFalseJump->PeepHoleOptimizer(pass)) + changed = true; + + assert(mIns.Size() == 0 || mIns[0].mType != ASMIT_INV); + + return changed; + } + + return false; +} + +void NativeCodeBasicBlock::AddToSuffixTree(NativeCodeMapper& mapper, SuffixTree * tree) +{ + if (!mVisited) + { + mVisited = true; + + if (mSuffixString && mIns.Size() + 50 > mSuffixStringLength) + { + delete[] mSuffixString; + mSuffixString = nullptr; + } + if (!mSuffixString) + { + mSuffixStringLength = mIns.Size() + 100; + mSuffixString = new int[mSuffixStringLength]; + } + + bool rel = false; + for (int i = 0; i < mIns.Size(); i++) + { + if (mIns[i].mMode == ASMIM_RELATIVE) + rel = true; + mSuffixString[i] = mapper.MapInstruction(mIns[i], mProc->mLinkerObject->mSection); + } + mSuffixString[mIns.Size()] = mapper.MapBasicBlock(this); + mSuffixString[mIns.Size() + 1] = -1; + + if (!rel) + tree->AddString(mSuffixString); + + if (mTrueJump) mTrueJump->AddToSuffixTree(mapper, tree); + if (mFalseJump) mFalseJump->AddToSuffixTree(mapper, tree); + } +} + +void NativeCodeBasicBlock::CheckVisited(void) +{ +#if _DEBUG + if (!mPatched) + { + mPatched = true; + assert(mVisited); + + if (mTrueJump) + mTrueJump->CheckVisited(); + if (mFalseJump) + mFalseJump->CheckVisited(); + } + +#endif +} + +void NativeCodeBasicBlock::CheckAsmCode(void) +{ +#if _DEBUG + if (!mVisited) + { + mVisited = true; + + for (int j = 0; j < mIns.Size(); j++) + { + if (mIns[j].mMode == ASMIM_IMMEDIATE_ADDRESS) + assert(HasAsmInstructionMode(mIns[j].mType, ASMIM_IMMEDIATE)); + else + assert(HasAsmInstructionMode(mIns[j].mType, mIns[j].mMode)); +#if CHECK_NULLPTR + if (mIns[j].mMode == ASMIM_ABSOLUTE || mIns[j].mMode == ASMIM_ABSOLUTE_Y || mIns[j].mMode == ASMIM_ABSOLUTE_X) + assert(mIns[j].mLinkerObject != nullptr || mIns[j].mAddress > 0); +#endif + } + + if (mTrueJump) mTrueJump->CheckAsmCode(); + if (mFalseJump) mFalseJump->CheckAsmCode(); + } +#endif +} + + +void NativeCodeBasicBlock::CheckBlocks(bool sequence) +{ +#if _DEBUG + if (!mChecked) + { + mChecked = true; + + assert(this != mProc->mEntryBlock || mNumEntries < 2); + + CheckLive(); + + if (sequence) + { + if (mTrueJump) + assert(mTrueJump->mEntryBlocks.Contains(this)); + if (mFalseJump) + assert(mFalseJump->mEntryBlocks.Contains(this)); + } + + if (mTrueJump) mTrueJump->CheckBlocks(); + if (mFalseJump) mFalseJump->CheckBlocks(); + } +#endif +} + +void NativeCodeBasicBlock::CheckLive(void) +{ +#if _DEBUG + uint32 live = 0; + + if (mFalseJump && mFalseJump->mEntryRequiredRegs.Size() > 0 && mFalseJump->mEntryRequiredRegs[CPU_REG_X] && mExitRequiredRegs.Size() > 0) + { + if (mFalseJump->mIns.Size() > 0 && mFalseJump->mIns[0].RequiresXReg()) + { + assert(mExitRequiredRegs[CPU_REG_X]); + live |= LIVE_CPU_REG_X; + } + } + if (mTrueJump && mTrueJump->mEntryRequiredRegs.Size() > 0 && mTrueJump->mEntryRequiredRegs[CPU_REG_X] && mExitRequiredRegs.Size() > 0) + { + if (mTrueJump->mIns.Size() > 0 && mTrueJump->mIns[0].RequiresXReg()) + { + assert(mExitRequiredRegs[CPU_REG_X]); + live |= LIVE_CPU_REG_X; + } + } + + assert(mBranch == ASMIT_RTS || (mBranch == ASMIT_JMP) == (mFalseJump == nullptr)); + + if (mBranch == ASMIT_BCC || mBranch == ASMIT_BCS) + live |= LIVE_CPU_REG_C; + if (mBranch == ASMIT_BEQ || mBranch == ASMIT_BNE || mBranch == ASMIT_BPL || mBranch == ASMIT_BMI) + live |= LIVE_CPU_REG_Z; + + if (mIns.Size() > 0 && mIns[0].mMode == ASMIM_INDIRECT_Y && mEntryRequiredRegs.Size() > 0) + { + assert(mEntryRequiredRegs[CPU_REG_Y]); + } + + if (mExitRequiredRegs.Size() > 0 && mExitRequiredRegs[CPU_REG_Z]) + { + if (mFalseJump) + live |= LIVE_CPU_REG_Z; + else if (mTrueJump->mEntryRequiredRegs[CPU_REG_Z]) + { + if (mTrueJump->mIns.Size() == 0 || !mTrueJump->mIns[0].ChangesZFlag()) + live |= LIVE_CPU_REG_Z; + } + } + if (mExitRequiredRegs.Size() > 0 && mTrueJump && mTrueJump->mEntryRequiredRegs.Size() > 0 && mExitRequiredRegs[CPU_REG_C] && (mFalseJump || mTrueJump->mEntryRequiredRegs[CPU_REG_C])) + live |= LIVE_CPU_REG_C; + + for (int j = mIns.Size() - 1; j >= 0; j--) + { + assert(mIns[j].mType != ASMIT_INV); + + if (mIns[j].mMode == ASMIM_IMMEDIATE_ADDRESS) + assert(HasAsmInstructionMode(mIns[j].mType, ASMIM_IMMEDIATE)); + else + assert(HasAsmInstructionMode(mIns[j].mType, mIns[j].mMode)); + +#if CHECK_NULLPTR + if (mIns[j].mMode == ASMIM_ABSOLUTE || mIns[j].mMode == ASMIM_ZERO_PAGE || mIns[j].mMode == ASMIM_ABSOLUTE_X || mIns[j].mMode == ASMIM_ABSOLUTE_Y) + assert(mIns[j].mLinkerObject != nullptr || mIns[j].mAddress > 0); +#endif + if (mIns[j].mType != ASMIT_NOP) + { + assert(!(live & ~mIns[j].mLive)); + + if (mIns[j].mType == ASMIT_JSR) + { + assert((mIns[j].mFlags & NCIF_PROVIDE_CPU_REG_X) || (mIns[j].mFlags & NCIF_PRESERVE_CPU_REG_X) || !(live & LIVE_CPU_REG_X)); + assert((mIns[j].mFlags & NCIF_PROVIDE_CPU_REG_Y) || (mIns[j].mFlags & NCIF_PRESERVE_CPU_REG_Y) || !(live & LIVE_CPU_REG_Y)); + if (!(mIns[j].mFlags & NCIF_JSRFLAGS)) + assert(!(live & (LIVE_CPU_REG_C | LIVE_CPU_REG_Z))); + } + + if (mIns[j].ChangesAccu()) live &= ~LIVE_CPU_REG_A; + if (mIns[j].ChangesXReg()) live &= ~LIVE_CPU_REG_X; + if (mIns[j].ChangesYReg()) live &= ~LIVE_CPU_REG_Y; + if (mIns[j].ChangesCarry()) live &= ~LIVE_CPU_REG_C; + if (mIns[j].ChangesZFlag()) live &= ~LIVE_CPU_REG_Z; + + if (mIns[j].RequiresAccu()) live |= LIVE_CPU_REG_A; + if (mIns[j].RequiresXReg()) live |= LIVE_CPU_REG_X; + if (mIns[j].RequiresYReg()) live |= LIVE_CPU_REG_Y; + if (mIns[j].RequiresCarry()) live |= LIVE_CPU_REG_C; + } + } + + if (mEntryRequiredRegs.Size() > 0) + { + if (live & LIVE_CPU_REG_A) + assert(mEntryRegA || mEntryRequiredRegs[CPU_REG_A]); + if (live & LIVE_CPU_REG_X) + assert(mEntryRegX || mEntryRequiredRegs[CPU_REG_X]); + if (live & LIVE_CPU_REG_Y) + assert(mEntryRegY || mEntryRequiredRegs[CPU_REG_Y]); + } +#endif +} + +void NativeCodeBasicBlock::Assemble(void) +{ + if (!mAssembled) + { + mAssembled = true; + + for (int i = 0; i < mIns.Size(); i++) + mIns[i].Assemble(this); + + if (this->mTrueJump) + this->mTrueJump->Assemble(); + if (this->mFalseJump) + this->mFalseJump->Assemble(); + } +} + +void NativeCodeBasicBlock::Close(const InterInstruction* ins, NativeCodeBasicBlock* trueJump, NativeCodeBasicBlock* falseJump, AsmInsType branch) +{ + this->mTrueJump = trueJump; + this->mFalseJump = falseJump; + this->mBranch = branch; + this->mBranchIns = ins; +} + +NativeCodeBasicBlock* NativeCodeBasicBlock::SplitAt(int at) +{ + NativeCodeBasicBlock* eblock = mProc->AllocateBlock(); + eblock->Close(mBranchIns, mTrueJump, mFalseJump, mBranch); + this->mBranch = ASMIT_JMP; + this->mFalseJump = nullptr; + this->mTrueJump = eblock; + for (int i = at; i < mIns.Size(); i++) + eblock->mIns.Push(mIns[i]); + mIns.SetSize(at); + eblock->mNumEntries = 1; + eblock->mEntryBlocks.Push(this); + eblock->mTrueJump->mEntryBlocks.RemoveAll(this); + eblock->mTrueJump->mEntryBlocks.Push(eblock); + if (eblock->mFalseJump) + { + eblock->mFalseJump->mEntryBlocks.RemoveAll(this); + eblock->mFalseJump->mEntryBlocks.Push(eblock); + } + return eblock; +} + +NativeCodeBasicBlock* NativeCodeBasicBlock::BypassEmptyBlocks(void) +{ + if (mBypassed) + return this; + else if (mTrueJump && !mFalseJump && mCode.Size() == 0 && this != mTrueJump) + return mTrueJump->BypassEmptyBlocks(); + else + { + mBypassed = true; + + if (mFalseJump) + mFalseJump = mFalseJump->BypassEmptyBlocks(); + if (mTrueJump) + mTrueJump = mTrueJump->BypassEmptyBlocks(); + + return this; + } +} + +int NativeCodeBasicBlock::LeadsInto(NativeCodeBasicBlock* block, int dist) +{ + if (mPlaced) + return 1000; + else if (this == block) + return 0; + else if (dist < 6) + { + int d0 = mTrueJump ? mTrueJump->LeadsInto(block, dist + 1) : 1000; + int d1 = mFalseJump ? mFalseJump->LeadsInto(block, dist + 1) : 1000; + + int d = (d0 < d1 ? d0 : d1) + mCode.Size(); + + if (d < 100) + return d; + } + + return 1000; +} + +NativeCodeBasicBlock * NativeCodeBasicBlock::PlaceSequence(ExpandingArray& placement, NativeCodeBasicBlock* block) +{ + int size = 0; + do { + block->mPlaced = true; + block->mPlace = placement.Size(); + placement.Push(block); + size += block->mCode.Size(); + block = block->mTrueJump; + } while (block && !block->mFalseJump && !block->mPlaced && size + block->mCode.Size() < 32); + return block; +} + +void NativeCodeBasicBlock::BuildPlacement(ExpandingArray& placement) +{ + if (!mPlaced) + { + if (mTrueJump && mFalseJump && mTrueJump == mFalseJump) + { + mFalseJump = nullptr; + mBranch = ASMIT_JMP; + } + + assert(mBranch != ASMIT_JMP || mIns.Size() > 0 || mTrueJump == this); + + mPlaced = true; + mPlace = placement.Size(); + placement.Push(this); + + if (mFalseJump) + { + if (mFalseJump->mPlaced) + mTrueJump->BuildPlacement(placement); + else if (mTrueJump->mPlaced) + mFalseJump->BuildPlacement(placement); + else if (!mTrueJump->mFalseJump && !mFalseJump->mFalseJump && mTrueJump->mTrueJump == mFalseJump->mTrueJump) + { + if (mTrueJump->mNDataSet[CPU_REG_C].mMode == NRDM_IMMEDIATE || mTrueJump->mNDataSet[CPU_REG_Z].mMode == NRDM_IMMEDIATE) + { + mTrueJump->mPlaced = true; + mTrueJump->mPlace = placement.Size(); + placement.Push(mTrueJump); + mFalseJump->BuildPlacement(placement); + } + else + { + mFalseJump->mPlaced = true; + mFalseJump->mPlace = placement.Size(); + placement.Push(mFalseJump); + + mTrueJump->BuildPlacement(placement); + } + } + else if (mTrueJump->LeadsInto(mFalseJump, 0) < mFalseJump->LeadsInto(mTrueJump, 0)) + { + mTrueJump->BuildPlacement(placement); + mFalseJump->BuildPlacement(placement); + } + else if (mTrueJump->LeadsInto(mFalseJump, 0) > mFalseJump->LeadsInto(mTrueJump, 0)) + { + mFalseJump->BuildPlacement(placement); + mTrueJump->BuildPlacement(placement); + } + else if (mTrueJump->mCode.Size() < 32 && (mTrueJump->mTrueJump && mTrueJump->mTrueJump->mPlaced) || (mTrueJump->mFalseJump && mTrueJump->mFalseJump->mPlaced)) + { + mTrueJump->BuildPlacement(placement); + mFalseJump->BuildPlacement(placement); + } + else if (mFalseJump->mCode.Size() < 32 && (mFalseJump->mTrueJump && mFalseJump->mTrueJump->mPlaced) || (mFalseJump->mFalseJump && mFalseJump->mFalseJump->mPlaced)) + { + mFalseJump->BuildPlacement(placement); + mTrueJump->BuildPlacement(placement); + } + else if (!mTrueJump->mFalseJump && mTrueJump->mTrueJump && mTrueJump->mTrueJump->mFalseJump && + mFalseJump->mFalseJump && mFalseJump->mTrueJump == mTrueJump->mTrueJump && + mTrueJump->mCode.Size() < 20 && mTrueJump->mTrueJump->mCode.Size() < 20 && + mFalseJump->mFalseJump->LeadsInto(mTrueJump->mTrueJump->mTrueJump, 0) < 10 && + mFalseJump->mFalseJump->LeadsInto(mTrueJump->mTrueJump->mFalseJump, 0) < 10) + { + mTrueJump->mPlaced = true; + mTrueJump->mPlace = placement.Size(); + placement.Push(mTrueJump); + + mTrueJump->mTrueJump->mPlaced = true; + mTrueJump->mTrueJump->mPlace = placement.Size(); + placement.Push(mTrueJump->mTrueJump); + + mFalseJump->BuildPlacement(placement); + mTrueJump->mTrueJump->mTrueJump->BuildPlacement(placement); + mTrueJump->mTrueJump->mFalseJump->BuildPlacement(placement); + } + else if (!mTrueJump->mFalseJump && mTrueJump->mTrueJump && mTrueJump->mCode.Size() < 100 && mFalseJump->LeadsInto(mTrueJump->mTrueJump, 0) < 100) + { + NativeCodeBasicBlock * xblock = PlaceSequence(placement, mTrueJump); + mFalseJump->BuildPlacement(placement); + if (xblock) xblock->BuildPlacement(placement); + } + else if (!mFalseJump->mFalseJump && mFalseJump->mTrueJump && mFalseJump->mCode.Size() < 100 && mTrueJump->LeadsInto(mFalseJump->mTrueJump, 0) < 100) + { + NativeCodeBasicBlock * xblock = PlaceSequence(placement, mFalseJump); + mTrueJump->BuildPlacement(placement); + if (xblock) xblock->BuildPlacement(placement); + } + else if (mTrueJump->mIns.Size() == 0 && mTrueJump->mFalseJump == mFalseJump->mFalseJump && mTrueJump->mTrueJump == mFalseJump->mTrueJump) + { + mTrueJump->mPlaced = true; + mTrueJump->mPlace = placement.Size(); + placement.Push(mTrueJump); + + mFalseJump->BuildPlacement(placement); + } + else if (mTrueJump->mIns.Size() == 0 && mTrueJump->mFalseJump == mFalseJump->mTrueJump && mTrueJump->mTrueJump == mFalseJump->mFalseJump) + { + mTrueJump->mPlaced = true; + mTrueJump->mPlace = placement.Size(); + placement.Push(mTrueJump); + + mFalseJump->BuildPlacement(placement); + } + else if ( + !mTrueJump->mFalseJump && mTrueJump->mTrueJump && mTrueJump->mTrueJump->mPlaced && mTrueJump->mCode.Size() < 120 || + mTrueJump->mFalseJump && mTrueJump->mTrueJump && mTrueJump->mFalseJump->mPlaced && mTrueJump->mTrueJump->mPlaced && mTrueJump->mCode.Size() < 120) + { + mTrueJump->BuildPlacement(placement); + mFalseJump->BuildPlacement(placement); + } + else if (!mTrueJump->mFalseJump && mTrueJump->mTrueJump && mFalseJump->mFalseJump && !mTrueJump->mTrueJump->mPlaced && mTrueJump->mTrueJump->mNumEntries > 1 && mTrueJump->mTrueJump->mTrueJump != mTrueJump->mTrueJump) + { + mTrueJump->mPlaced = true; + mTrueJump->mPlace = placement.Size(); + placement.Push(mTrueJump); + + mFalseJump->BuildPlacement(placement); + mTrueJump->mTrueJump->BuildPlacement(placement); + } + else + { + mFalseJump->BuildPlacement(placement); + mTrueJump->BuildPlacement(placement); + } + } + else if (mTrueJump && !mTrueJump->mPlaced) + { + if (mTrueJump->mLoopHead && mTrueJump->mFalseJump) + { + if (!mTrueJump->mFalseJump->mFalseJump && mTrueJump->mFalseJump->mTrueJump == mTrueJump) + { + if (!mTrueJump->mFalseJump->mPlaced && mTrueJump->mFalseJump->mCode.Size() + mTrueJump->mCode.Size() < 100) + { + mTrueJump->mFalseJump->mPlaced = true; + mTrueJump->mFalseJump->mPlace = placement.Size(); + placement.Push(mTrueJump->mFalseJump); + } + } + if (!mTrueJump->mTrueJump->mFalseJump && mTrueJump->mTrueJump->mTrueJump == mTrueJump) + { + if (!mTrueJump->mTrueJump->mPlaced && mTrueJump->mTrueJump->mCode.Size() + mTrueJump->mCode.Size() < 100) + { + mTrueJump->mTrueJump->mPlaced = true; + mTrueJump->mTrueJump->mPlace = placement.Size(); + placement.Push(mTrueJump->mTrueJump); + } + } + if (mTrueJump->mEntryBlocks.Size() == 2 && this == mTrueJump->mEntryBlocks[0] && !mTrueJump->mEntryBlocks[1]->mFalseJump) + { + if (!mTrueJump->mEntryBlocks[1]->mPlaced && mTrueJump->mEntryBlocks[1]->mCode.Size() + mTrueJump->mCode.Size() < 40) + { + mTrueJump->mEntryBlocks[1]->mPlaced = true; + mTrueJump->mEntryBlocks[1]->mPlace = placement.Size(); + placement.Push(mTrueJump->mEntryBlocks[1]); + } + } + } + + mTrueJump->BuildPlacement(placement); + } + } +} + +void NativeCodeBasicBlock::OptimizePlacement(void) +{ + if (!mFalseJump && mTrueJump && !mTrueJump->mFalseJump && mTrueJump->mTrueJump && mTrueJump->mTrueJump->mPlace == mPlace + 1 && mTrueJump->mCode.Size() <= 3) + { + for (int i = 0; i < mTrueJump->mRelocations.Size(); i++) + { + LinkerReference rl = mTrueJump->mRelocations[i]; + rl.mOffset += mCode.Size(); + if (rl.mFlags & LREF_INBLOCK) + { + rl.mRefOffset += mCode.Size(); + rl.mFlags &= ~LREF_INBLOCK; + } + mRelocations.Push(rl); + } + for (int i = 0; i < mTrueJump->mCode.Size(); i++) + mCode.Push(mTrueJump->mCode[i]); + mTrueJump->mEntryBlocks.RemoveAll(this); + mTrueJump->mNumEntries--; + mTrueJump = mTrueJump->mTrueJump; + mTrueJump->mEntryBlocks.Push(this); + mTrueJump->mNumEntries++; + } +} + +void NativeCodeBasicBlock::InitialOffset(int& total) +{ + mAsmFromJump = -1; + + mOffset = total; + total += mCode.Size(); + if (mFalseJump) + { + total += 5; + if (mFalseJump->mPlace != mPlace + 1 && mTrueJump->mPlace != mPlace + 1) + total += 3; + } + else if (mTrueJump) + { + if (mTrueJump->mPlace != mPlace + 1) + total += 3; + } + + mSize = total - mOffset; +} + +bool NativeCodeBasicBlock::CalculateOffset(int& total, bool final) +{ + bool changed = total != mOffset; + mOffset = total; + + total += mCode.Size(); + + if (mFalseJump) + { + if (mFalseJump->mPlace == mPlace + 1) + total += BranchByteSize(mTrueJump, total, mTrueJump->mOffset, final); + else if (mTrueJump->mPlace == mPlace + 1) + total += BranchByteSize(mFalseJump, total, mFalseJump->mOffset, final); + else if (mFalseJump->mPlace < mPlace && mTrueJump->mPlace < mPlace && mPlace - mTrueJump->mPlace < 126 && + mFalseJump->mIns.Size() == 1 && mFalseJump->mIns[0].mType == ASMIT_RTS) + { + total += BranchByteSize(mTrueJump, total, mTrueJump->mOffset, final); + total += JumpByteSize(mFalseJump, total, mFalseJump->mOffset, true, final); + } +#if 1 + else if (final && CheckFinalBranchByteSize(mFalseJump, total, mFalseJump->mOffset) < CheckFinalBranchByteSize(mTrueJump, total, mTrueJump->mOffset)) + { + total += BranchByteSize(mFalseJump, total, mFalseJump->mOffset, final); + total += JumpByteSize(mTrueJump, total, mTrueJump->mOffset, true, final); + } + else if (final && CheckFinalBranchByteSize(mTrueJump, total, mTrueJump->mOffset) < CheckFinalBranchByteSize(mFalseJump, total, mFalseJump->mOffset)) + { + total += BranchByteSize(mTrueJump, total, mTrueJump->mOffset, final); + total += JumpByteSize(mFalseJump, total, mFalseJump->mOffset, true, final); + } +#endif + else if ( + mFalseJump->mPlace > mTrueJump->mPlace && mFalseJump->mPlace < mPlace || + mFalseJump->mPlace < mTrueJump->mPlace && mFalseJump->mPlace > mPlace) + { + total += BranchByteSize(mFalseJump, total, mFalseJump->mOffset, final); + total += JumpByteSize(mTrueJump, total, mTrueJump->mOffset, true, final); + } + else + { + total += BranchByteSize(mTrueJump, total, mTrueJump->mOffset, final); + total += JumpByteSize(mFalseJump, total, mFalseJump->mOffset, true, final); + } + } + else if (mTrueJump) + { + if (mTrueJump->mPlace != mPlace + 1) + { + if (!mTrueJump->mTrueJump && mTrueJump->mCode.Size() < 3) + { + total += mTrueJump->mCode.Size(); + for (int i = 0; i < mTrueJump->mCode.Size(); i++) + mCode.Push(mTrueJump->mCode[i]); + mTrueJump = nullptr; + } + else + total += JumpByteSize(mTrueJump, total, mTrueJump->mOffset, false, final); + } + } + + if (mOffset + mSize != total) + changed = true; + + mSize = total - mOffset; + + return changed; +} + +void NativeCodeBasicBlock::ShortcutJump(int offset) +{ + if (mCode[offset] == 0x4c) + { + int i = 0; + while (i < mRelocations.Size() && mRelocations[i].mOffset != offset + 1) + i++; + if (i < mRelocations.Size()) + { + LinkerReference& ref(mRelocations[i]); + if (ref.mRefObject && ref.mRefObject->mData) + { + LinkerObject* lo = ref.mRefObject; + + if (lo->mData[ref.mRefOffset] == 0x4c || lo->mData[ref.mRefOffset] == 0x6c) + { + int j = 0; + while (j < lo->mReferences.Size() && lo->mReferences[j]->mOffset != ref.mRefOffset + 1) + j++; + if (j < lo->mReferences.Size()) + { + mCode[offset] = lo->mData[ref.mRefOffset]; + ref.mRefObject = lo->mReferences[j]->mRefObject; + ref.mRefOffset = lo->mReferences[j]->mRefOffset; + ref.mFlags = lo->mReferences[j]->mFlags; + } + else + { + mCode[offset] = lo->mData[ref.mRefOffset]; + mCode[offset + 1] = lo->mData[ref.mRefOffset + 1]; + mCode[offset + 2] = lo->mData[ref.mRefOffset + 2]; + mRelocations.Remove(i); + } + } + } + } + } +} + +void NativeCodeBasicBlock::ShortcutTailRecursion() +{ + if (!mVisited) + { + mVisited = true; + if (!mFalseJump && mTrueJump && mTrueJump->mIns.Size() == 1 && mTrueJump->mIns[0].mType == ASMIT_RTS) + { + if (mIns.Size() > 0 && mIns.Last().IsSimpleJSR()) + { + this->mCode[this->mCode.Size() - 3] = 0x4c; + mTrueJump->mNumEntries--; + mTrueJump = nullptr; + ShortcutJump(this->mCode.Size() - 3); + } +#if 0 + else + { + this->mCode.Push(0x60); + mTrueJump->mNumEntries--; + mTrueJump = nullptr; + } +#endif + } + else if (!mFalseJump && !mTrueJump) + { + int ns = mIns.Size(); + if (ns >= 2 && mIns[ns - 1].mType == ASMIT_RTS && mIns[ns - 2].IsSimpleJSR()) + { + this->mCode.Remove(this->mCode.Size() - 1); + this->mCode[this->mCode.Size() - 3] = 0x4c; + ShortcutJump(this->mCode.Size() - 3); + } + } + + if (mTrueJump) mTrueJump->ShortcutTailRecursion(); + if (mFalseJump) mFalseJump->ShortcutTailRecursion(); + } +} + +void NativeCodeBasicBlock::CopyCode(NativeCodeProcedure * proc, uint8* target) +{ + int i; + int next, end; + + end = mOffset + mCode.Size(); + next = mOffset + mSize; + + if (mFalseJump) + { + if (mBranchIns) + PutLocation(mBranchIns->mLocation, false); + + if (mFalseJump->mPlace == mPlace + 1) + end += PutBranch(proc, mTrueJump, mBranch, end, mTrueJump->mOffset); + else if (mTrueJump->mPlace == mPlace + 1) + end += PutBranch(proc, mFalseJump, InvertBranchCondition(mBranch), end, mFalseJump->mOffset); + else if (mFalseJump->mPlace < mPlace && mTrueJump->mPlace < mPlace && mPlace - mTrueJump->mPlace < 126 && + mFalseJump->mIns.Size() == 1 && mFalseJump->mIns[0].mType == ASMIT_RTS) + { + end += PutBranch(proc, mTrueJump, mBranch, end, mTrueJump->mOffset); + end += PutJump(proc, mFalseJump, end, mFalseJump->mOffset, mBranch); + } +#if 1 + else if (CheckFinalBranchByteSize(mFalseJump, end, mFalseJump->mOffset) < CheckFinalBranchByteSize(mTrueJump, end, mTrueJump->mOffset)) + { + end += PutBranch(proc, mFalseJump, InvertBranchCondition(mBranch), end, mFalseJump->mOffset); + end += PutJump(proc, mTrueJump, end, mTrueJump->mOffset, InvertBranchCondition(mBranch)); + } + else if (CheckFinalBranchByteSize(mTrueJump, end, mTrueJump->mOffset) < CheckFinalBranchByteSize(mFalseJump, end, mFalseJump->mOffset)) + { + end += PutBranch(proc, mTrueJump, mBranch, end, mTrueJump->mOffset); + end += PutJump(proc, mFalseJump, end, mFalseJump->mOffset, mBranch); + } +#endif + else if ( + mFalseJump->mPlace > mTrueJump->mPlace && mFalseJump->mPlace < mPlace || + mFalseJump->mPlace < mTrueJump->mPlace && mFalseJump->mPlace > mPlace) + { + end += PutBranch(proc, mFalseJump, InvertBranchCondition(mBranch), end, mFalseJump->mOffset); + end += PutJump(proc, mTrueJump, end, mTrueJump->mOffset, InvertBranchCondition(mBranch)); + } + else + { + end += PutBranch(proc, mTrueJump, mBranch, end, mTrueJump->mOffset); + end += PutJump(proc, mFalseJump, end, mFalseJump->mOffset, mBranch); + } + + if (mBranchIns) + PutLocation(mBranchIns->mLocation, false); + } + else if (mTrueJump) + { + if (mTrueJump->mPlace != mPlace + 1) + { + if (mBranchIns) + PutLocation(mBranchIns->mLocation, false); + + end += PutJump(proc, mTrueJump, end, mTrueJump->mOffset); + + if (mBranchIns) + PutLocation(mBranchIns->mLocation, false); + } + else if (mTrueJump->mIns.Size() == 1 && mTrueJump->mIns[0].mType == ASMIT_RTS && mIns.Size() > 0 && mIns.Last().IsSimpleJSR()) + this->mCode[this->mCode.Size() - 3] = 0x4c; + } + + + assert(end == next); + + for (int i = 0; i < mRelocations.Size(); i++) + { + LinkerReference& rl(mRelocations[i]); + rl.mOffset += mOffset; + if (rl.mFlags & LREF_INBLOCK) + { + rl.mRefOffset += mOffset; + rl.mFlags &= ~LREF_INBLOCK; + } + proc->mRelocations.Push(rl); + } + + for (int i = 0; i < mCodeLocations.Size(); i++) + { + CodeLocation loc(mCodeLocations[i]); + loc.mStart += mOffset; + loc.mEnd += mOffset; + proc->mCodeLocations.Push(loc); + } + + for (int i = 0; i < mCodeOrigins.Size(); i++) + { + CodeLocation loc(mCodeOrigins[i]); + loc.mStart += mOffset; + loc.mEnd += mOffset; + proc->mCodeOrigins.Push(loc); + } + + for (i = 0; i < mCode.Size(); i++) + { + target[i + mOffset] = mCode[i]; + } +} + +bool NativeCodeBasicBlock::CheckAllAccuReturn(void) +{ + if (!mVisited) + { + mVisited = true; + if (mTrueJump) + { + if (!mTrueJump->CheckAllAccuReturn()) + return false; + if (mFalseJump && !mFalseJump->CheckAllAccuReturn()) + return false; + } + else + { + int sz = mIns.Size(); + if (sz >= 2 && + mIns[sz - 1].mType == ASMIT_RTS && + mIns[sz - 2].mType == ASMIT_STA && mIns[sz - 2].mMode == ASMIM_ZERO_PAGE && mIns[sz - 2].mAddress == BC_REG_ACCU) + { + return true; + } + + return false; + } + } + + return true; +} + +void NativeCodeBasicBlock::ReplaceAccuReturn(void) +{ + if (!mVisited) + { + mVisited = true; + if (mTrueJump) + { + mTrueJump->ReplaceAccuReturn(); + if (mFalseJump) + mFalseJump->ReplaceAccuReturn(); + } + else + { + int sz = mIns.Size(); + if (sz >= 2 && + mIns[sz - 1].mType == ASMIT_RTS && + mIns[sz - 2].mType == ASMIT_STA && mIns[sz - 2].mMode == ASMIM_ZERO_PAGE && mIns[sz - 2].mAddress == BC_REG_ACCU) + { + mIns.Remove(sz - 2); + mExitRegA = true; + } + } + } +} + +NativeCodeBasicBlock::NativeCodeBasicBlock(NativeCodeProcedure* proc) +{ + mProc = proc; + mBranch = ASMIT_RTS; + mBranchIns = nullptr; + mTrueJump = mFalseJump = NULL; + mOffset = -1; + mPlaced = false; + mCopied = false; + mKnownShortBranch = false; + mBypassed = false; + mAssembled = false; + mLocked = false; + mDominator = nullptr; + mLoopHeadBlock = nullptr; + mLoopTailBlock = nullptr; + mSuffixString = nullptr; + mSuffixStringLength = 0; + mEntryRegA = false; + mEntryRegX = false; + mEntryRegY = false; + mExitRegA = false; + mExitRegX = false; + mVisited = false; + mLoopHead = false; + mNumEntries = 0; +} + +NativeCodeBasicBlock::~NativeCodeBasicBlock(void) +{ + +} + +NativeCodeProcedure::NativeCodeProcedure(NativeCodeGenerator* generator) + : mGenerator(generator), mSimpleInline(false) +{ + mTempBlocks = 1000; +} + +NativeCodeProcedure::~NativeCodeProcedure(void) +{ + +} + +void NativeCodeProcedure::DisassembleDebug(const char* name) +{ +#if DISASSEMBLE_OPT +#ifdef _WIN32 + FILE* file; + static bool initial = true; + + if (!CheckFunc) + return; + + if (!initial) + { + fopen_s(&file, "r:\\ntivdiss.txt", "a"); + } + else + { + fopen_s(&file, "r:\\ntivdiss.txt", "w"); + initial = false; + } + + if (file) + { + fprintf(file, "--------------------------------------------------------------------\n"); + fprintf(file, "%s : %s:%d\n", name, mLocation.mFileName, mLocation.mLine); + fprintf(file, "\n"); + + ResetVisited(); + mEntryBlock->Disassemble(file); + + fclose(file); + } +#endif +#endif +} + +void NativeCodeProcedure::Disassemble(FILE* file) +{ + fprintf(file, "--------------------------------------------------------------------\n"); + fprintf(file, "%s: %s:%d\n", mIdent->mString, mLocation.mFileName, mLocation.mLine); + + ResetVisited(); + mEntryBlock->Disassemble(file); +} + +void NativeCodeProcedure::AddToSuffixTree(NativeCodeMapper& mapper, SuffixTree* tree) +{ + ResetVisited(); + mEntryBlock->AddToSuffixTree(mapper, tree); +} + +void NativeCodeProcedure::CompressTemporaries(bool singles) +{ + if (mInterProc->mTempSize > 0) + { + ResetVisited(); + + NumberSet used(256), modified(256), pairs(256); + + mEntryBlock->CollectZeroPageUsage(used, modified, pairs); + + uint8 remap[256]; + for (int i = 0; i < 256; i++) + remap[i] = i; + + int tpos = BC_REG_TMP + mInterProc->mFreeCallerSavedTemps; + int spos = BC_REG_TMP_SAVED; + + NumberSet * collisionSet = new NumberSet[NUM_REGS]; + + for (int i = 0; i < NUM_REGS; i++) + collisionSet[i].Reset(NUM_REGS); + + ResetVisited(); + mEntryBlock->BuildCollisionTable(collisionSet); + + +// for (int tsize = 4; tsize > 0; tsize >>= 1) + { + for (int i = 0; i < mInterProc->mTempOffset.Size(); i++) + { + bool tused = false; + + int reg = BC_REG_TMP + mInterProc->mTempOffset[i]; + // if (mInterProc->mLeafProcedure || reg >= BC_REG_TMP_SAVED) + if (reg >= BC_REG_TMP + mInterProc->mFreeCallerSavedTemps) + { + int size = mInterProc->mTempSizes[i]; +// if (size == tsize) + { + int usize = 0; + + for (int j = 0; j < size; j++) + if (used[reg + j]) + usize = j + 1; + + if (usize) + { + int pos = spos; + +#if 1 +// if (mInterProc->mLeafProcedure) + { + if (singles && usize == 1) + { + int k = 0; + while (k < 4 && collisionSet[k + BC_REG_ACCU][reg]) + k++; + if (k < 4) + { + pos = BC_REG_ACCU + k; + for (int i = 0; i < 256; i++) + { + if (collisionSet[reg][i]) + { + collisionSet[pos] += i; + collisionSet[i] += pos; + } + } + } + } + else + { + int k = 0; + while (k < usize && !collisionSet[k + BC_REG_ACCU][k + reg]) + k++; + if (k == usize) + { + pos = BC_REG_ACCU; + for (int i = 0; i < 256; i++) + { + for (int j = 0; j < usize; j++) + { + if (collisionSet[j + reg][i]) + { + collisionSet[j + BC_REG_ACCU] += i; + collisionSet[i] += j + BC_REG_ACCU; + } + } + } + } + } + } +#endif + + if (pos == spos) + { + if (tpos + usize <= BC_REG_TMP + mInterProc->mCallerSavedTemps) + { + pos = tpos; + tpos += usize; + } + else + spos += usize; + mInterProc->mTempOffset[i] = pos - BC_REG_TMP; + mInterProc->mTempSizes[i] = usize; + } + else + { + mInterProc->mTempOffset[i] = 0; + mInterProc->mTempSizes[i] = 0; + } + + for (int j = 0; j < usize; j++) + remap[reg + j] = pos + j; + + } + else + { + mInterProc->mTempOffset[i] = 0; + mInterProc->mTempSizes[i] = 0; + } + } + } + } + } + + delete[] collisionSet; + + mInterProc->mCallerSavedTemps = tpos - BC_REG_TMP; + + ResetVisited(); + mEntryBlock->RemapZeroPage(remap); + + assert(mInterProc->mTempSize >= tpos - BC_REG_TMP); + + if (spos > BC_REG_TMP_SAVED) + mInterProc->mTempSize = spos - BC_REG_TMP; + else + mInterProc->mTempSize = tpos - BC_REG_TMP; + + if (mNoFrame && !used[BC_REG_STACK] && mInterProc->mTempSize <= BC_REG_TMP_SAVED - BC_REG_TMP) + mStackExpand = 0; + } +} + +void NativeCodeProcedure::SaveTempsToStack(int tempSave) +{ + if (mInterProc->mSaveTempsLinkerObject) + { + assert(tempSave <= mInterProc->mSaveTempsLinkerObject->mSize); + + if (tempSave > 3) + { + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_LDX, ASMIM_IMMEDIATE, tempSave - 1)); + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_LDA, ASMIM_ZERO_PAGE_X, BC_REG_TMP_SAVED)); + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_STA, ASMIM_ABSOLUTE_X, 0, mInterProc->mSaveTempsLinkerObject)); + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_DEX, ASMIM_IMPLIED)); + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_BPL, ASMIM_RELATIVE, -8)); + } + else if (tempSave > 0) + { + for (int i = 0; i < tempSave; i++) + { + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP_SAVED + i)); + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_STA, ASMIM_ABSOLUTE, i, mInterProc->mSaveTempsLinkerObject)); + } + } + } +} + +void NativeCodeProcedure::LoadTempsFromStack(int tempSave) +{ + if (mInterProc->mSaveTempsLinkerObject) + { + if (tempSave > 3) + { + mExitBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_LDX, ASMIM_IMMEDIATE, tempSave - 1)); + mExitBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_LDA, ASMIM_ABSOLUTE_X, 0, mInterProc->mSaveTempsLinkerObject)); + mExitBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_STA, ASMIM_ZERO_PAGE_X, BC_REG_TMP_SAVED)); + mExitBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_DEX, ASMIM_IMPLIED)); + mExitBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_BPL, ASMIM_RELATIVE, -8)); + } + else if (tempSave > 0) + { + for (int i = 0; i < tempSave; i++) + { + mExitBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_LDA, ASMIM_ABSOLUTE, i, mInterProc->mSaveTempsLinkerObject)); + mExitBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP_SAVED + i)); + } + } + } +} + + +void NativeCodeProcedure::Compile(InterCodeProcedure* proc) +{ + mInterProc = proc; + mLinkerObject = proc->mLinkerObject; + mIdent = proc->mIdent; + mLocation = proc->mLocation; + mCompilerOptions = proc->mCompilerOptions; + + mInterProc->mLinkerObject->mNativeProc = this; + + CheckFunc = !strcmp(mIdent->mString, "enemies_iterate"); + + int nblocks = proc->mBlocks.Size(); + tblocks = new NativeCodeBasicBlock * [nblocks]; + for (int i = 0; i < nblocks; i++) + tblocks[i] = nullptr; + + mIndex = proc->mID; + mFastCallBase = proc->mFastCallBase; + + int tempSave = proc->mTempSize > BC_REG_TMP_SAVED - BC_REG_TMP && !proc->mSaveTempsLinkerObject && !mInterProc->mInterrupt ? proc->mTempSize - (BC_REG_TMP_SAVED - BC_REG_TMP) : 0; + int commonFrameSize = proc->mCommonFrameSize; + + mStackExpand = tempSave + proc->mLocalSize; + + if (proc->mCallsByteCode || commonFrameSize > 0) + commonFrameSize += 2; + + mFrameOffset = 0; + if (proc->mHasDynamicStack) + mNoFrame = false; + else if (mStackExpand + proc->mCommonFrameSize + proc->mParamVarsSize < 256) + mNoFrame = true; + else + mNoFrame = false; + +#if 0 + if (!(proc->mCompilerOptions & COPT_OPTIMIZE_BASIC) && (proc->mCompilerOptions & COPT_DEBUGINFO)) + mNoFrame = mStackExpand + proc->mCommonFrameSize == 0 && !proc->mHasDynamicStack; +#endif + + if (mNoFrame) + proc->mLinkerObject->mFlags |= LOBJF_NO_FRAME; + + if (mNoFrame) + { + if (mStackExpand > 0) + mFrameOffset = tempSave; + } + else + { + mStackExpand += 2; + } + + if (!proc->mLeafProcedure) + { + if (mNoFrame) + mFrameOffset = commonFrameSize + tempSave; + } + + mEntryBlock = AllocateBlock(); + mEntryBlock->mLocked = true; + mBlocks.Push(mEntryBlock); + + mExitBlock = AllocateBlock(); + mExitBlock->mLocked = true; + mBlocks.Push(mExitBlock); + + // Place a temporary RTS + + uint32 rflags = 0; + switch (proc->mReturnType) + { + case IT_BOOL: + case IT_INT8: + rflags = NCIF_LOWER; + break; + case IT_INT16: + case IT_POINTER: + rflags = NCIF_LOWER | NCIF_UPPER; + break; + case IT_INT32: + case IT_FLOAT: + rflags = NCIF_LOWER | NCIF_UPPER | NCIF_LONG; + break; + + } + + mExitBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_RTS, ASMIM_IMPLIED, 0, nullptr, rflags)); + + mEntryBlock->mTrueJump = CompileBlock(mInterProc, mInterProc->mBlocks[0]); + mEntryBlock->mBranch = ASMIT_JMP; + + bool simpleProcedure = proc->mFastCallProcedure && !proc->mInterrupt && !proc->mDispatchedCall && mNoFrame && mStackExpand == 0 && commonFrameSize == 0 && proc->mTempSize <= BC_REG_TMP_SAVED - BC_REG_TMP && (mGenerator->mCompilerOptions & COPT_NATIVE); + + if (proc->mLeafProcedure && simpleProcedure) + { +#if 1 + if (proc->mParamVars.Size() == 1 && proc->mParamVars[0]->mSize == 1) + { +// printf("A Args %s\n", proc->mIdent->mString); + + proc->mLinkerObject->mFlags |= LOBJF_ARG_REG_A; + proc->mLinkerObject->mTemporaries[0]++; + proc->mLinkerObject->mTempSizes[0]--; + mEntryBlock->mTrueJump->mIns.Insert(0, NativeCodeInstruction(nullptr, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_FPARAMS)); + mEntryBlock->mEntryRegA = true; + } +#endif +#if 1 + else if (proc->mParamVars.Size() == 1 && proc->mParamVars[0]->mSize == 2 && proc->mParamVars[0]->mDeclaration && proc->mParamVars[0]->mDeclaration->mBase->IsIntegerType()) + { +// printf("AX Args %s\n", proc->mIdent->mString); + + proc->mLinkerObject->mFlags |= LOBJF_ARG_REG_A | LOBJF_ARG_REG_X; + proc->mLinkerObject->mTemporaries[0] += 2; + proc->mLinkerObject->mTempSizes[0] -= 2; + mEntryBlock->mTrueJump->mIns.Insert(0, NativeCodeInstruction(nullptr, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_FPARAMS)); + mEntryBlock->mTrueJump->mIns.Insert(1, NativeCodeInstruction(nullptr, ASMIT_STX, ASMIM_ZERO_PAGE, BC_REG_FPARAMS + 1)); + mEntryBlock->mEntryRegA = true; + mEntryBlock->mEntryRegX = true; + } +#endif +#if 1 + if (mExitBlock->mIns[0].mFlags == NCIF_LOWER) + { + mExitBlock->mIns[0].mFlags = NCIF_USE_CPU_REG_A; + mExitBlock->mIns.Insert(0, NativeCodeInstruction(nullptr, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_ACCU)); + mExitBlock->mExitRegA = true; + proc->mLinkerObject->mFlags |= LOBJF_RET_REG_A; + } +#endif + } + + mGenerator->PopulateShortMulTables(); + + Optimize(); +#if 1 + if (simpleProcedure && !(proc->mLinkerObject->mFlags & LOBJF_RET_REG_A) && rflags == NCIF_LOWER) + { + ResetVisited(); + if (mEntryBlock->CheckAllAccuReturn()) + { + proc->mLinkerObject->mFlags |= LOBJF_RET_REG_A; + ResetVisited(); + mEntryBlock->ReplaceAccuReturn(); + } + } +#endif + if (mEntryBlock->mIns.Size() > 0) + { + NativeCodeBasicBlock* eblock = AllocateBlock(); + eblock->mTrueJump = mEntryBlock; + mEntryBlock = eblock; + } + + // Remove temporary RTS + + mExitBlock->mIns.Pop(); + + int frameSpace = tempSave; + + tempSave = proc->mTempSize > BC_REG_TMP_SAVED - BC_REG_TMP ? proc->mTempSize - (BC_REG_TMP_SAVED - BC_REG_TMP) : 0; + + if (!(mGenerator->mCompilerOptions & COPT_NATIVE)) + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_BYTE, ASMIM_IMPLIED, 0xea)); + + bool ignoreExpandCommonFrame = false; + + + if (mInterProc->mInterrupt) + { + if (!mNoFrame || commonFrameSize > 0) + mGenerator->mErrors->Error(mLocation, ERRR_INTERRUPT_TO_COMPLEX, "Function to complex for interrupt"); + + ZeroPageSet zpLocal, zpGlobal; + ResetVisited(); + if (mEntryBlock->CollectZeroPageSet(zpLocal, zpGlobal, true)) + zpLocal |= zpGlobal; + else + mGenerator->mErrors->Error(mLocation, ERRR_INTERRUPT_TO_COMPLEX, "No recursive functions in interrupt"); + + if (proc->mHardwareInterrupt) + { + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_PHA)); + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_TXA)); + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_PHA)); + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_TYA)); + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_PHA)); + } + + bool usesStack = mStackExpand; + + if (zpLocal[BC_REG_STACK]) + { + usesStack = true; + zpLocal -= BC_REG_STACK; + zpLocal -= BC_REG_STACK + 1; + } + + if (usesStack) + { + if (mStackExpand) + { + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_SEC, ASMIM_IMPLIED)); + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_STACK)); + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_SBC, ASMIM_IMMEDIATE, (mStackExpand + commonFrameSize) & 0xff)); + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_STACK)); + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_STACK + 1)); + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_SBC, ASMIM_IMMEDIATE, ((mStackExpand + commonFrameSize) >> 8) + 1)); + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_STACK + 1)); + } + else + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_DEC, ASMIM_ZERO_PAGE, BC_REG_STACK + 1)); + } + + for (int i = 2; i < 256; i++) + { + if (zpLocal[i]) + { + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_LDA, ASMIM_ZERO_PAGE, i)); + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_PHA)); + } + } + + for (int i = 255; i >= 2; i--) + { + if (zpLocal[i]) + { + mExitBlock->mIns.Push(NativeCodeInstruction(mExitBlock->mBranchIns, ASMIT_PLA)); + mExitBlock->mIns.Push(NativeCodeInstruction(mExitBlock->mBranchIns, ASMIT_STA, ASMIM_ZERO_PAGE, i)); + } + } + if (usesStack) + { + if (mStackExpand) + { + mExitBlock->mIns.Push(NativeCodeInstruction(mExitBlock->mBranchIns, ASMIT_CLC, ASMIM_IMPLIED)); + mExitBlock->mIns.Push(NativeCodeInstruction(mExitBlock->mBranchIns, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_STACK)); + mExitBlock->mIns.Push(NativeCodeInstruction(mExitBlock->mBranchIns, ASMIT_ADC, ASMIM_IMMEDIATE, (mStackExpand + commonFrameSize) & 0xff)); + mExitBlock->mIns.Push(NativeCodeInstruction(mExitBlock->mBranchIns, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_STACK)); + mExitBlock->mIns.Push(NativeCodeInstruction(mExitBlock->mBranchIns, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_STACK + 1)); + mExitBlock->mIns.Push(NativeCodeInstruction(mExitBlock->mBranchIns, ASMIT_ADC, ASMIM_IMMEDIATE, ((mStackExpand + commonFrameSize) >> 8) + 1)); + mExitBlock->mIns.Push(NativeCodeInstruction(mExitBlock->mBranchIns, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_STACK + 1)); + } + else + mExitBlock->mIns.Push(NativeCodeInstruction(mExitBlock->mBranchIns, ASMIT_INC, ASMIM_ZERO_PAGE, BC_REG_STACK + 1)); + } + + if (proc->mHardwareInterrupt) + { + mExitBlock->mIns.Push(NativeCodeInstruction(mExitBlock->mBranchIns, ASMIT_PLA)); + mExitBlock->mIns.Push(NativeCodeInstruction(mExitBlock->mBranchIns, ASMIT_TAY)); + mExitBlock->mIns.Push(NativeCodeInstruction(mExitBlock->mBranchIns, ASMIT_PLA)); + mExitBlock->mIns.Push(NativeCodeInstruction(mExitBlock->mBranchIns, ASMIT_TAX)); + mExitBlock->mIns.Push(NativeCodeInstruction(mExitBlock->mBranchIns, ASMIT_PLA)); + } + + // We safe all registers + proc->mLinkerObject->mFlags |= LOBJF_ZEROPAGESET; + } + else + { + if (mNoFrame) + { + if (mStackExpand > 0) + { + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_SEC, ASMIM_IMPLIED)); + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_STACK)); + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_SBC, ASMIM_IMMEDIATE, (mStackExpand + commonFrameSize) & 0xff)); + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_STACK)); + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_BCS, ASMIM_RELATIVE, 2)); + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_DEC, ASMIM_ZERO_PAGE, BC_REG_STACK + 1)); + ignoreExpandCommonFrame = true; + + if (proc->mSaveTempsLinkerObject) + SaveTempsToStack(tempSave); + else if (tempSave) + { + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_LDY, ASMIM_IMMEDIATE, commonFrameSize + tempSave - 1)); + if (tempSave == 1) + { + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP_SAVED)); + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_STA, ASMIM_INDIRECT_Y, BC_REG_STACK)); + } + else if (tempSave == 2) + { + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP_SAVED + 1)); + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_STA, ASMIM_INDIRECT_Y, BC_REG_STACK)); + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_DEY, ASMIM_IMPLIED)); + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP_SAVED)); + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_STA, ASMIM_INDIRECT_Y, BC_REG_STACK)); + + } + else if (commonFrameSize > 0) + { + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_LDX, ASMIM_IMMEDIATE, tempSave - 1)); + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_LDA, ASMIM_ZERO_PAGE_X, BC_REG_TMP_SAVED)); + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_STA, ASMIM_INDIRECT_Y, BC_REG_STACK)); + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_DEY, ASMIM_IMPLIED)); + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_DEX, ASMIM_IMPLIED)); + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_BPL, ASMIM_RELATIVE, -8)); + } + else + { + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_LDA, ASMIM_ABSOLUTE_Y, BC_REG_TMP_SAVED)); + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_STA, ASMIM_INDIRECT_Y, BC_REG_STACK)); + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_DEY, ASMIM_IMPLIED)); + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_BPL, ASMIM_RELATIVE, -8)); + } + } + } + else if (proc->mSaveTempsLinkerObject) + SaveTempsToStack(tempSave); + } + else + { + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_SEC, ASMIM_IMPLIED)); + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_STACK)); + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_SBC, ASMIM_IMMEDIATE, mStackExpand & 0xff)); + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_STACK)); + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_STACK + 1)); + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_SBC, ASMIM_IMMEDIATE, (mStackExpand >> 8) & 0xff)); + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_STACK + 1)); + + if (proc->mSaveTempsLinkerObject) + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_LDY, ASMIM_IMMEDIATE, 0)); + else + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_LDY, ASMIM_IMMEDIATE, tempSave)); + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_LOCALS)); + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_STA, ASMIM_INDIRECT_Y, BC_REG_STACK)); + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_INY, ASMIM_IMPLIED)); + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_LOCALS + 1)); + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_STA, ASMIM_INDIRECT_Y, BC_REG_STACK)); + + if (proc->mSaveTempsLinkerObject) + SaveTempsToStack(tempSave); + else if (tempSave) + { + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_DEY, ASMIM_IMPLIED)); + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_DEY, ASMIM_IMPLIED)); + + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_LDA, ASMIM_ABSOLUTE_Y, BC_REG_TMP_SAVED)); + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_STA, ASMIM_INDIRECT_Y, BC_REG_STACK)); + if (tempSave > 1) + { + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_DEY, ASMIM_IMPLIED)); + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_BPL, ASMIM_RELATIVE, -8)); + } + } + + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_CLC, ASMIM_IMPLIED)); + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_STACK)); + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_ADC, ASMIM_IMMEDIATE, frameSpace + 2)); + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_LOCALS)); + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_STACK + 1)); + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_ADC, ASMIM_IMMEDIATE, 0)); + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_LOCALS + 1)); + } + + if (!proc->mLeafProcedure && commonFrameSize > 0 && !ignoreExpandCommonFrame) + { + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_SEC, ASMIM_IMPLIED)); + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_STACK)); + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_SBC, ASMIM_IMMEDIATE, commonFrameSize & 0xff)); + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_STACK)); + if (commonFrameSize >= 256) + { + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_STACK + 1)); + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_SBC, ASMIM_IMMEDIATE, (commonFrameSize >> 8) & 0xff)); + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_STACK + 1)); + } + else + { + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_BCS, ASMIM_RELATIVE, 2)); + mEntryBlock->mIns.Push(NativeCodeInstruction(nullptr, ASMIT_DEC, ASMIM_ZERO_PAGE, BC_REG_STACK + 1)); + } + + mExitBlock->mIns.Push(NativeCodeInstruction(mExitBlock->mBranchIns, ASMIT_CLC, ASMIM_IMPLIED)); + mExitBlock->mIns.Push(NativeCodeInstruction(mExitBlock->mBranchIns, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_STACK)); + mExitBlock->mIns.Push(NativeCodeInstruction(mExitBlock->mBranchIns, ASMIT_ADC, ASMIM_IMMEDIATE, commonFrameSize & 0xff)); + mExitBlock->mIns.Push(NativeCodeInstruction(mExitBlock->mBranchIns, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_STACK)); + if (commonFrameSize >= 256) + { + mExitBlock->mIns.Push(NativeCodeInstruction(mExitBlock->mBranchIns, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_STACK + 1)); + mExitBlock->mIns.Push(NativeCodeInstruction(mExitBlock->mBranchIns, ASMIT_ADC, ASMIM_IMMEDIATE, (commonFrameSize >> 8) & 0xff)); + mExitBlock->mIns.Push(NativeCodeInstruction(mExitBlock->mBranchIns, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_STACK + 1)); + } + else + { + mExitBlock->mIns.Push(NativeCodeInstruction(mExitBlock->mBranchIns, ASMIT_BCC, ASMIM_RELATIVE, 2)); + mExitBlock->mIns.Push(NativeCodeInstruction(mExitBlock->mBranchIns, ASMIT_INC, ASMIM_ZERO_PAGE, BC_REG_STACK + 1)); + } + } + + if (mNoFrame) + { + if (mStackExpand > 0) + { + if (proc->mSaveTempsLinkerObject) + LoadTempsFromStack(tempSave); + else if (tempSave) + { + mExitBlock->mIns.Push(NativeCodeInstruction(mExitBlock->mBranchIns, ASMIT_LDY, ASMIM_IMMEDIATE, commonFrameSize + tempSave - 1)); + if (tempSave == 1) + { + mExitBlock->mIns.Push(NativeCodeInstruction(mExitBlock->mBranchIns, ASMIT_LDA, ASMIM_INDIRECT_Y, BC_REG_STACK)); + mExitBlock->mIns.Push(NativeCodeInstruction(mExitBlock->mBranchIns, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP_SAVED)); + } + else if (tempSave == 2) + { + mExitBlock->mIns.Push(NativeCodeInstruction(mExitBlock->mBranchIns, ASMIT_LDA, ASMIM_INDIRECT_Y, BC_REG_STACK)); + mExitBlock->mIns.Push(NativeCodeInstruction(mExitBlock->mBranchIns, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP_SAVED + 1)); + mExitBlock->mIns.Push(NativeCodeInstruction(mExitBlock->mBranchIns, ASMIT_DEY, ASMIM_IMPLIED)); + mExitBlock->mIns.Push(NativeCodeInstruction(mExitBlock->mBranchIns, ASMIT_LDA, ASMIM_INDIRECT_Y, BC_REG_STACK)); + mExitBlock->mIns.Push(NativeCodeInstruction(mExitBlock->mBranchIns, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP_SAVED)); + + } + else if (commonFrameSize > 0) + { + mExitBlock->mIns.Push(NativeCodeInstruction(mExitBlock->mBranchIns, ASMIT_LDX, ASMIM_IMMEDIATE, tempSave - 1)); + mExitBlock->mIns.Push(NativeCodeInstruction(mExitBlock->mBranchIns, ASMIT_LDA, ASMIM_INDIRECT_Y, BC_REG_STACK)); + mExitBlock->mIns.Push(NativeCodeInstruction(mExitBlock->mBranchIns, ASMIT_STA, ASMIM_ZERO_PAGE_X, BC_REG_TMP_SAVED)); + mExitBlock->mIns.Push(NativeCodeInstruction(mExitBlock->mBranchIns, ASMIT_DEY, ASMIM_IMPLIED)); + mExitBlock->mIns.Push(NativeCodeInstruction(mExitBlock->mBranchIns, ASMIT_DEX, ASMIM_IMPLIED)); + mExitBlock->mIns.Push(NativeCodeInstruction(mExitBlock->mBranchIns, ASMIT_BPL, ASMIM_RELATIVE, -8)); + } + else + { + mExitBlock->mIns.Push(NativeCodeInstruction(mExitBlock->mBranchIns, ASMIT_LDA, ASMIM_INDIRECT_Y, BC_REG_STACK)); + mExitBlock->mIns.Push(NativeCodeInstruction(mExitBlock->mBranchIns, ASMIT_STA, ASMIM_ABSOLUTE_Y, BC_REG_TMP_SAVED)); + mExitBlock->mIns.Push(NativeCodeInstruction(mExitBlock->mBranchIns, ASMIT_DEY, ASMIM_IMPLIED)); + mExitBlock->mIns.Push(NativeCodeInstruction(mExitBlock->mBranchIns, ASMIT_BPL, ASMIM_RELATIVE, -8)); + } + } + + mExitBlock->mIns.Push(NativeCodeInstruction(mExitBlock->mBranchIns, ASMIT_CLC, ASMIM_IMPLIED)); + mExitBlock->mIns.Push(NativeCodeInstruction(mExitBlock->mBranchIns, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_STACK)); + mExitBlock->mIns.Push(NativeCodeInstruction(mExitBlock->mBranchIns, ASMIT_ADC, ASMIM_IMMEDIATE, (mStackExpand + commonFrameSize) & 0xff)); + mExitBlock->mIns.Push(NativeCodeInstruction(mExitBlock->mBranchIns, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_STACK)); + mExitBlock->mIns.Push(NativeCodeInstruction(mExitBlock->mBranchIns, ASMIT_BCC, ASMIM_RELATIVE, 2)); + mExitBlock->mIns.Push(NativeCodeInstruction(mExitBlock->mBranchIns, ASMIT_INC, ASMIM_ZERO_PAGE, BC_REG_STACK + 1)); + } + else if (proc->mSaveTempsLinkerObject) + LoadTempsFromStack(tempSave); + + } + else + { + if (proc->mSaveTempsLinkerObject) + mExitBlock->mIns.Push(NativeCodeInstruction(mExitBlock->mBranchIns, ASMIT_LDY, ASMIM_IMMEDIATE, 0)); + else + mExitBlock->mIns.Push(NativeCodeInstruction(mExitBlock->mBranchIns, ASMIT_LDY, ASMIM_IMMEDIATE, tempSave)); + mExitBlock->mIns.Push(NativeCodeInstruction(mExitBlock->mBranchIns, ASMIT_LDA, ASMIM_INDIRECT_Y, BC_REG_STACK)); + mExitBlock->mIns.Push(NativeCodeInstruction(mExitBlock->mBranchIns, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_LOCALS)); + mExitBlock->mIns.Push(NativeCodeInstruction(mExitBlock->mBranchIns, ASMIT_INY, ASMIM_IMPLIED)); + mExitBlock->mIns.Push(NativeCodeInstruction(mExitBlock->mBranchIns, ASMIT_LDA, ASMIM_INDIRECT_Y, BC_REG_STACK)); + mExitBlock->mIns.Push(NativeCodeInstruction(mExitBlock->mBranchIns, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_LOCALS + 1)); + + if (proc->mSaveTempsLinkerObject) + LoadTempsFromStack(tempSave); + else if (tempSave) + { + mExitBlock->mIns.Push(NativeCodeInstruction(mExitBlock->mBranchIns, ASMIT_DEY, ASMIM_IMPLIED)); + mExitBlock->mIns.Push(NativeCodeInstruction(mExitBlock->mBranchIns, ASMIT_DEY, ASMIM_IMPLIED)); + + mExitBlock->mIns.Push(NativeCodeInstruction(mExitBlock->mBranchIns, ASMIT_LDA, ASMIM_INDIRECT_Y, BC_REG_STACK)); + mExitBlock->mIns.Push(NativeCodeInstruction(mExitBlock->mBranchIns, ASMIT_STA, ASMIM_ABSOLUTE_Y, BC_REG_TMP_SAVED)); + if (tempSave > 1) + { + mExitBlock->mIns.Push(NativeCodeInstruction(mExitBlock->mBranchIns, ASMIT_DEY, ASMIM_IMPLIED)); + mExitBlock->mIns.Push(NativeCodeInstruction(mExitBlock->mBranchIns, ASMIT_BPL, ASMIM_RELATIVE, -8)); + } + } + + mExitBlock->mIns.Push(NativeCodeInstruction(mExitBlock->mBranchIns, ASMIT_CLC, ASMIM_IMPLIED)); + mExitBlock->mIns.Push(NativeCodeInstruction(mExitBlock->mBranchIns, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_STACK)); + mExitBlock->mIns.Push(NativeCodeInstruction(mExitBlock->mBranchIns, ASMIT_ADC, ASMIM_IMMEDIATE, mStackExpand & 0xff)); + mExitBlock->mIns.Push(NativeCodeInstruction(mExitBlock->mBranchIns, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_STACK)); + mExitBlock->mIns.Push(NativeCodeInstruction(mExitBlock->mBranchIns, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_STACK + 1)); + mExitBlock->mIns.Push(NativeCodeInstruction(mExitBlock->mBranchIns, ASMIT_ADC, ASMIM_IMMEDIATE, (mStackExpand >> 8) & 0xff)); + mExitBlock->mIns.Push(NativeCodeInstruction(mExitBlock->mBranchIns, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_STACK + 1)); + + } + + ZeroPageSet zpLocal, zpGlobal; + ResetVisited(); + if (mEntryBlock->CollectZeroPageSet(zpLocal, zpGlobal, false)) + { + zpLocal |= zpGlobal; + + for (int i = BC_REG_TMP_SAVED; i < BC_REG_TMP_SAVED + tempSave; i++) + zpLocal -= i; + + proc->mLinkerObject->mZeroPageSet = zpLocal; + proc->mLinkerObject->mFlags |= LOBJF_ZEROPAGESET; + } + } + + ResetVisited(); + + bool globalStores = false, globalLoads = false; + mEntryBlock->CheckGlobalAliasing(globalStores, globalLoads); + + if (!globalStores) + proc->mLinkerObject->mFlags |= LOBJF_NO_GLOBAL_STORES; + if (!globalLoads) + proc->mLinkerObject->mFlags |= LOBJF_NO_GLOBAL_LOADS; + + proc->mFramePointer = !mNoFrame; + for (int i = 0; i < proc->mParamVars.Size(); i++) + { + if (proc->mParamVars[i]) + proc->mParamVars[i]->mOffset = i + proc->mLocalSize + 2 + mFrameOffset; + } + for (int i = 0; i < proc->mLocalVars.Size(); i++) + { + if (proc->mLocalVars[i]) + proc->mLocalVars[i]->mOffset += mFrameOffset; + } + + if (proc->mHardwareInterrupt) + mExitBlock->mIns.Push(NativeCodeInstruction(mExitBlock->mBranchIns, ASMIT_RTI, ASMIM_IMPLIED)); + else + mExitBlock->mIns.Push(NativeCodeInstruction(mExitBlock->mBranchIns, ASMIT_RTS, ASMIM_IMPLIED)); + + + if (mExitBlock->mIns.Size() == 1 && rflags == NCIF_LOWER && !mExitBlock->mExitRegA && (mGenerator->mCompilerOptions & COPT_NATIVE)) + { + if (mExitBlock->mEntryBlocks.Size() == 1) + { + NativeCodeBasicBlock* eblock = mExitBlock->mEntryBlocks[0]; + int sz = eblock->mIns.Size(); + while (sz > 0 && !eblock->mIns[sz - 1].ReferencesAccu()) + sz--; + if (sz > 0 && eblock->mIns[sz - 1].mType == ASMIT_STA && eblock->mIns[sz - 1].mMode == ASMIM_ZERO_PAGE && eblock->mIns[sz - 1].mAddress == BC_REG_ACCU) + { + mExitBlock->mExitRegA = true; + eblock->mIns.Remove(sz - 1); + proc->mLinkerObject->mFlags |= LOBJF_RET_REG_A; + } + } + } + + proc->mLinkerObject->mType = LOT_NATIVE_CODE; + + if (!mInterProc->mNoInline) + { + if (mEntryBlock->mIns.Size() == 0 && mExitBlock->mIns.Size() == 1 && !mEntryBlock->mTrueJump->mFalseJump && mEntryBlock->mTrueJump->mTrueJump == mExitBlock) + { + NativeCodeBasicBlock* block = mEntryBlock->mTrueJump; + int sz = block->mIns.Size(); + if (sz < 20) + { + int cost = -6; + for (int i = 0; i < sz; i++) + { + const NativeCodeInstruction& ins(block->mIns[i]); + switch (ins.mType) + { + case ASMIT_LDA: + case ASMIT_LDX: + case ASMIT_LDY: + if (ins.mMode == ASMIM_ZERO_PAGE && ins.mAddress >= BC_REG_FPARAMS && ins.mAddress < BC_REG_FPARAMS_END) + cost -= 2; + else + cost += AsmInsSize(ins.mType, ins.mMode); + break; + case ASMIT_STA: + case ASMIT_STX: + case ASMIT_STY: + if (ins.mMode == ASMIM_ZERO_PAGE && ins.mAddress >= BC_REG_ACCU && ins.mAddress < BC_REG_ACCU + 4) + cost -= 2; + else + cost += AsmInsSize(ins.mType, ins.mMode); + break; + case ASMIT_JSR: + if (ins.mLinkerObject && (ins.mLinkerObject->mFlags & LOBJF_INLINE)) + cost += 1000; + else + cost += 3; + break; + + case ASMIT_TAX: + case ASMIT_TXA: + case ASMIT_TAY: + case ASMIT_TYA: + case ASMIT_ASL: + case ASMIT_LSR: + case ASMIT_ROL: + case ASMIT_ROR: + case ASMIT_CLC: + case ASMIT_SEC: + case ASMIT_INX: + case ASMIT_INY: + case ASMIT_DEX: + case ASMIT_DEY: + case ASMIT_ADC: + case ASMIT_SBC: + case ASMIT_AND: + case ASMIT_ORA: + case ASMIT_EOR: + case ASMIT_BIT: + case ASMIT_CMP: + case ASMIT_CPX: + case ASMIT_CPY: + cost += AsmInsSize(ins.mType, ins.mMode); + break; + + default: + cost += 1000; + break; + } + } + + if (cost <= 0) + { + mSimpleInline = true; + } + } + } + } + + if (mCompilerOptions & COPT_OPTIMIZE_MERGE_CALLS) + { + ResetVisited(); + mEntryBlock->RegisterFunctionCalls(); + } +} + +void NativeCodeProcedure::Assemble(void) +{ + CheckFunc = !strcmp(mIdent->mString, "cwin_edit_char"); + + if (mCompilerOptions & COPT_OPTIMIZE_MERGE_CALLS) + { + ResetVisited(); + mEntryBlock->MergeFunctionCalls(); + } + + mEntryBlock->Assemble(); + + ResetVisited(); + mEntryBlock = mEntryBlock->BypassEmptyBlocks(); + + ResetVisited(); + mEntryBlock->ShortcutTailRecursion(); + + ExpandingArray placement; + + int total; + total = 0; + + mEntryBlock->BuildPlacement(placement); + + for (int i = 0; i < placement.Size(); i++) + placement[i]->OptimizePlacement();; + + for (int i = 0; i < placement.Size(); i++) + placement[i]->InitialOffset(total); + + bool progress; + do { + progress = false; + total = 0; + for (int i = 0; i < placement.Size(); i++) + if (placement[i]->CalculateOffset(total, false)) + progress = true; + } while (progress); + + // final run to recycle jumps + do { + progress = false; + + for (int i = 0; i < placement.Size(); i++) + placement[i]->mAsmFromJump = -1; + + total = 0; + + for (int i = 0; i < placement.Size(); i++) + if (placement[i]->CalculateOffset(total, true)) + progress = true; + } while (progress); + + for (int i = 0; i < placement.Size(); i++) + placement[i]->mAsmFromJump = -1; + + uint8* data = mLinkerObject->AddSpace(total); + + for (int i = 0; i < placement.Size(); i++) + { + LinkerObjectRange range; + char buffer[100]; + if (placement[i]->mLoopHead) + sprintf_s(buffer, "l%d", placement[i]->mIndex); + else + sprintf_s(buffer, "s%d", placement[i]->mIndex); + + range.mIdent = Ident::Unique(buffer); + range.mOffset = placement[i]->mOffset; + range.mSize = placement[i]->mSize; + mLinkerObject->mRanges.Push(range); + placement[i]->CopyCode(this, data); + } + + for (int i = 0; i < mRelocations.Size(); i++) + { + LinkerReference& rl(mRelocations[i]); + rl.mObject = mLinkerObject; + if (!rl.mRefObject) + rl.mRefObject = mLinkerObject; + if (rl.mFlags & LREF_SELFMOD) + { + int offset = rl.mOffset; + int id = rl.mRefOffset >> 1; + NativeCodeBasicBlock* block = mSelfModSources[id].mBlock; + int target = block->mOffset + mSelfModSources[id].mOffset; + target += rl.mRefOffset & 1; + rl.mFlags |= LREF_HIGHBYTE | LREF_LOWBYTE; + rl.mRefOffset = target; + } + mLinkerObject->AddReference(rl); + } + + if (mGenerator->mCompilerOptions & (COPT_DEBUGINFO | COPT_PROFILEINFO)) + { + if (mCodeLocations.Size() > 0) + { + int i = 1, j = 0; + while (i < mCodeLocations.Size()) + { + if (mCodeLocations[j].mEnd == mCodeLocations[i].mStart && + mCodeLocations[j].mLocation.mFileName == mCodeLocations[i].mLocation.mFileName && + mCodeLocations[j].mLocation.mLine == mCodeLocations[i].mLocation.mLine) + mCodeLocations[j].mEnd = mCodeLocations[i].mEnd; + else + mCodeLocations[++j] = mCodeLocations[i]; + + i++; + } + mCodeLocations.SetSize(j + 1); + + mLinkerObject->AddLocations(mCodeLocations); + } + if (mCodeOrigins.Size() > 0) + { + int i = 1, j = 0; + while (i < mCodeOrigins.Size()) + { + if (mCodeOrigins[j].mEnd == mCodeOrigins[i].mStart && + mCodeOrigins[j].mLocation.mFileName == mCodeOrigins[i].mLocation.mFileName && + mCodeOrigins[j].mLocation.mLine == mCodeOrigins[i].mLocation.mLine) + mCodeOrigins[j].mEnd = mCodeOrigins[i].mEnd; + else + mCodeOrigins[++j] = mCodeOrigins[i]; + + i++; + } + mCodeOrigins.SetSize(j + 1); + + mLinkerObject->AddOrigins(mCodeOrigins); + } + } +} + +bool NativeCodeProcedure::MapFastParamsToTemps(void) +{ + NumberSet used(256), modified(256), statics(256), pairs(256); + + ResetVisited(); + mEntryBlock->CollectZeroPageUsage(used, modified, pairs); + + used.Fill(); + + for (int i = BC_REG_TMP; i < 256; i++) + used -= i; + + for (int i = BC_REG_FPARAMS; i < BC_REG_FPARAMS_END; i++) + if (!modified[i]) + statics += i; + + uint8 alias[256]; + for (int i = 0; i < 256; i++) + alias[i] = 0; + + ResetVisited(); + mEntryBlock->FindZeroPageAlias(statics, used, alias, -1); + + for (int i = 1; i < 256; i++) + { + if (used[i] || !alias[i] || + (pairs[i] && (used[i + 1] || alias[i + 1] != alias[i] + 1)) || + (pairs[i - 1] && (used[i - 1] || alias[i - 1] + 1 != alias[i]))) + { + alias[i] = i; + } + } + + NativeCodeBasicBlock* block = mEntryBlock; + while (block && block->mIns.Size() == 0 && !block->mFalseJump && block->mTrueJump != block) + block = block->mTrueJump; + + if (block && block->mNumEntries == 1) + { + used.Clear(); + modified.Clear(); + + NumberSet tpairs(256), aliased(256); + + for (int i = 1; i < 256; i++) + { + if (alias[i] != i) + aliased += alias[i]; + } + + ResetVisited(); + if (block->mTrueJump) + block->mTrueJump->CollectZeroPageUsage(used, modified, tpairs); + if (block->mFalseJump) + block->mFalseJump->CollectZeroPageUsage(used, modified, tpairs); + + for (int i = 0; i + 1 < block->mIns.Size(); i++) + { + if (block->mIns[i + 0].mType == ASMIT_LDA && block->mIns[i + 0].mMode == ASMIM_ZERO_PAGE && block->mIns[i + 0].mAddress >= BC_REG_FPARAMS && block->mIns[i + 0].mAddress < BC_REG_FPARAMS_END && + block->mIns[i + 1].mType == ASMIT_STA && block->mIns[i + 1].mMode == ASMIM_ZERO_PAGE && block->mIns[i + 1].mAddress > BC_REG_TMP && + !used[block->mIns[i + 0].mAddress] && !modified[block->mIns[i + 0].mAddress] && + !aliased[block->mIns[i + 0].mAddress]) + { + if (!block->ReferencesZeroPage(block->mIns[i + 0].mAddress, i + 1) && !block->ReferencesZeroPage(block->mIns[i + 1].mAddress, 0, i)) + { + alias[block->mIns[i + 1].mAddress] = block->mIns[i + 0].mAddress; + aliased += block->mIns[i + 0].mAddress; + } + } + } + + for (int i = 1; i < 255; i++) + { + if ((pairs[i ] && alias[i + 1] != alias[i] + 1) || + (pairs[i - 1] && alias[i - 1] + 1 != alias[i] )) + alias[i] = i; + } + } + + + ResetVisited(); + return mEntryBlock->RemapZeroPage(alias); +} + +void NativeCodeProcedure::RebuildEntry(void) +{ + ResetVisited(); + for (int i = 0; i < mBlocks.Size(); i++) + { + mBlocks[i]->mNumEntries = 0; + mBlocks[i]->mVisiting = false; + mBlocks[i]->mLoopHead = false; + mBlocks[i]->mFromJump = nullptr; + mBlocks[i]->mDominator = nullptr; + mBlocks[i]->mSameBlock = nullptr; + } + + ResetVisited(); + ResetEntryBlocks(); + mEntryBlock->CountEntries(nullptr); + + ResetVisited(); + mEntryBlock->CollectEntryBlocks(nullptr); + + NativeCodeBasicBlock::DominatorStacks stacks; + + mEntryBlock->BuildDominatorTree(nullptr, stacks); +} + +void NativeCodeProcedure::CheckBlocks(bool sequence) +{ +#if _DEBUG + ResetChecked(); + mEntryBlock->CheckBlocks(); +#endif +} + +void NativeCodeProcedure::Optimize(void) +{ +#if 1 + int step = 0; + int cnt = 0; + bool swappedXY = false; + + CheckCase = false; + +#if DISASSEMBLE_OPT + DisassembleDebug("Preoptimize"); +#endif + + CheckBlocks(); + + bool changed, xmapped = false, ymapped = false; + do + { + changed = false; + + RebuildEntry(); + + assert(mEntryBlock->mNumEntries < 2); +#if 1 + if (step > 3) + { + ResetVisited(); + if (mEntryBlock->FindSameBlocks(this)) + { + ResetVisited(); + changed = mEntryBlock->MergeSameBlocks(this); + } + } +#endif + RebuildEntry(); + assert(mEntryBlock->mNumEntries < 2); + +#if _DEBUG + ResetVisited(); + mEntryBlock->CheckAsmCode(); +#endif + + if (step == 4) + { + ResetVisited(); + mEntryBlock->Split16BitLoopCount(this); + } + +#if _DEBUG + ResetVisited(); + mEntryBlock->CheckAsmCode(); +#endif + + if (step >= 6) + { + ResetVisited(); + mEntryBlock->CrossBlockYAliasProgpagation(nullptr); + } + +#if _DEBUG + ResetVisited(); + mEntryBlock->CheckAsmCode(); +#endif + + RebuildEntry(); + assert(mEntryBlock->mNumEntries < 2); + +#if 1 + if (step > 3) + { + NativeRegisterDataSet data; + + bool bchanged; + do { + ResetVisited(); + bchanged = mEntryBlock->BitFieldForwarding(data); + + if (bchanged) + RebuildEntry(); + else + { + ResetPatched(); + mEntryBlock->CheckVisited(); + } + + + } while (bchanged); + } +#endif +#if _DEBUG + ResetVisited(); + mEntryBlock->CheckAsmCode(); +#endif + + if (step >= 3) + { + ResetVisited(); + mEntryBlock->RegisterValueForwarding(); + } + +#if _DEBUG + ResetVisited(); + mEntryBlock->CheckAsmCode(); +#endif + +#if 1 + if (step == 2) + { + BuildDataFlowSets(); + ResetVisited(); + mEntryBlock->ReplaceFinalZeroPageUse(this); + } +#endif + +#if _DEBUG + ResetVisited(); + mEntryBlock->CheckAsmCode(); +#endif + if (step > 1) + { + ResetVisited(); + mEntryBlock->LocalZeroPageValueNumbering(); + } + +#if _DEBUG + ResetVisited(); + mEntryBlock->CheckAsmCode(); +#endif + + if (step == 9 || step == 16) + { + ResetVisited(); + if (mEntryBlock->IndexXYValueForwarding(-1, 0, 0, -1, 0, 0)) + changed = true; + + } + +#if _DEBUG + ResetVisited(); + mEntryBlock->CheckAsmCode(); +#endif + + int t = 0; +#if 1 + do + { + RebuildEntry(); + + BuildDataFlowSets(); + ResetVisited(); + + changed = mEntryBlock->RemoveUnusedResultInstructions(); + + if (step == 0) + { + ResetVisited(); + ExpandingArray cinfo; + + if (mEntryBlock->Propagate16BitSum(cinfo)) + changed = true; + } + + if (!changed) + { + ResetVisited(); + NativeRegisterDataSet data; + + if (mEntryBlock->ValueForwarding(this, data, step > 0, step == 8)) + { + changed = true; + } + else + { +#if 1 + ResetPatched(); + mEntryBlock->CheckVisited(); + + if (step > 1) + { + ResetVisited(); + if (mEntryBlock->GlobalValueForwarding(this, step == 8)) + { + changed = true; + } + } +#endif + } + } + + t++; + + } while (changed && t < 20); +#endif + + BuildDataFlowSets(); + ResetVisited(); + mEntryBlock->RemoveUnusedResultInstructions(); + + if (step == 8 || step == 25) + { + ResetVisited(); + ValueNumberingDataSet data; + if (mEntryBlock->OffsetValueForwarding(data)) + { + changed = true; + + BuildDataFlowSets(); + ResetVisited(); + mEntryBlock->RemoveUnusedResultInstructions(); + } + + ResetVisited(); + ValueReuseDataSet rdata; + if (mEntryBlock->CrossBlockShiftForwarding(rdata)) + { + changed = true; + + BuildDataFlowSets(); + ResetVisited(); + mEntryBlock->RemoveUnusedResultInstructions(); + } + } + + CheckBlocks(); + + if (step == 9) + { + ResetVisited(); + ExpandingArray pairs; + if (mEntryBlock->AbsoluteValueForwarding(pairs)) + { + changed = true; + + BuildDataFlowSets(); + ResetVisited(); + mEntryBlock->RemoveUnusedResultInstructions(); + } + } + + CheckBlocks(); + +#if 1 + ResetVisited(); + if (mEntryBlock->PeepHoleOptimizer(step)) + changed = true; +#endif + + if (step == 2) + { + ResetVisited(); + if (mEntryBlock->ShortcutIndirectLoadStore()) + changed = true; + } + +#if 1 + if (step >= 3) + { + ResetVisited(); + if (mEntryBlock->ReverseBitfieldForwarding()) + changed = true; + } +#endif + + + if (step == 5) + { + ResetVisited(); + if (mEntryBlock->FoldLoopEntry()) + changed = true; + } + +#if 1 + if (step < 7) + { + ResetVisited(); + if (mEntryBlock->OptimizeSelect(this)) + { + changed = true; + } + } + +#if 1 + if (step == 3) + { + ResetVisited(); + if (mEntryBlock->RemoveSimpleLoopUnusedIndex()) + changed = true; + } +#endif + +#if 1 + if (step == 3) + { + ResetVisited(); + if (mEntryBlock->ShortcutPointerAddForward()) + changed = true; + } +#endif + + if (step == 5 && cnt < 10) + { + ResetVisited(); + if (mEntryBlock->RemoveDoubleZPStore()) + changed = true; + } + + if (step == 3 || step == 5 || step == 9) + { + ResetVisited(); + if (mEntryBlock->PropagateCommonSubExpression()) + changed = true; + } + +#if 1 + CheckBlocks(); + + if (step > 0) + { + RebuildEntry(); + + ResetVisited(); + if (mEntryBlock->OptimizeSimpleLoop(this, step > 5)) + changed = true; + + ResetVisited(); + if (mEntryBlock->SimpleLoopReversal(this)) + changed = true; + } +#endif + +#if 1 + CheckBlocks(); + + ResetVisited(); + if (mEntryBlock->MergeBasicBlocks()) + { +// changed = true; + BuildDataFlowSets(); + CheckBlocks(); + } + +#endif + + RebuildEntry(); + + CheckBlocks(); + +#if 1 + if (step == 2) + { + if (MapFastParamsToTemps()) + changed = true; + } +#endif + +#if 1 + if (step > 5 && !changed) + { + ResetVisited(); + if (mEntryBlock->ShortcutCrossBlockMoves(this)) + changed = true; + } +#endif + + if (step == 4 || step == 14) + { + ResetVisited(); + if (mEntryBlock->OptimizeSimpleForLoop()) + changed = true; + + ResetVisited(); + if (mEntryBlock->OptimizeSimpleFixLoop()) + changed = true; + } + + if (step > 6 && !changed) + { + ResetVisited(); + if (mEntryBlock->ShortcutCrossBlockCondition()) + changed = true; + } + + CheckBlocks(); +#if _DEBUG + ResetVisited(); + mEntryBlock->CheckAsmCode(); +#endif + +#if 1 + if (step > 2 && !changed) + { + ResetVisited(); + if (mEntryBlock->JoinTailCodeSequences(step > 4)) + { + changed = true; + BuildDataFlowSets(); + } + + ResetVisited(); + if (mEntryBlock->PropagateSinglePath()) + { + changed = true; + BuildDataFlowSets(); + } + } + + CheckBlocks(); + +#if _DEBUG + ResetVisited(); + mEntryBlock->CheckAsmCode(); +#endif + + if (step > 2 && !changed) + { + ResetVisited(); + if (mEntryBlock->JoinCommonBranchCodeSequences()) + { + changed = true; + BuildDataFlowSets(); + } + } + + CheckBlocks(); + + if (step > 4 && step != 6) + { + ResetVisited(); + if (mEntryBlock->ShortcutBlockExit()) + { + changed = true; + BuildDataFlowSets(); + } + } + + if (step == 1) + { + ResetVisited(); + if (mEntryBlock->CompressSwitchCascade(0x00, 0xff, -1, ASMIT_JMP)) + changed = true; + } + + CheckBlocks(); + + if (step > 3) + { + ResetVisited(); + if (mEntryBlock->JoinCommonHeadSequence()) + changed = true; + } +#if _DEBUG + ResetVisited(); + mEntryBlock->CheckAsmCode(); +#endif + + if (step > 4) + { + ResetVisited(); + if (mEntryBlock->ShortcutORACascade()) + changed = true; + } + + if (step > 6) + { + ResetVisited(); + if (mEntryBlock->JoinEntryLoadStoreZP()) + changed = true; + } + +#endif + +#if _DEBUG + ResetVisited(); + mEntryBlock->CheckAsmCode(); +#endif + + CheckBlocks(); + +#if 1 + if (step == 3 || step == 4 || step == 8 || (step == 15 && cnt > 0)) + { +#if 1 + ResetVisited(); + if (mEntryBlock->OptimizeInnerLoops(this)) + changed = true; +#endif + + CheckBlocks(true); + +#if 1 + ResetVisited(); + if (mEntryBlock->OptimizeFindLoop(this)) + changed = true; +#endif + + CheckBlocks(true); + +#if 1 + ResetVisited(); + if (mEntryBlock->ReduceLocalYPressure()) + changed = true; +#endif + CheckBlocks(true); + +#if 1 + if (step == 4) + { +#if 1 + ResetVisited(); + if (!changed && mEntryBlock->LoopRegisterXYMap()) + changed = true; +#endif + + CheckBlocks(true); + + ResetVisited(); + if (!changed && mEntryBlock->OptimizeGenericLoop()) + changed = true; + } +#endif + + CheckBlocks(true); + +#if 1 + ResetVisited(); + if (!changed && mEntryBlock->ShortcutZeroPageCopyUp(this)) + changed = true; +#endif + + CheckBlocks(true); + +#if 1 + ResetVisited(); + if (!changed && mEntryBlock->CrossBlockXYShortcut()) + changed = true; +#endif + + CheckBlocks(true); + +#if 1 + ResetVisited(); + if (!changed && mEntryBlock->CrossBlockXYPreservation()) + changed = true; +#endif + } +#endif + + CheckBlocks(); +#if _DEBUG + ResetVisited(); + mEntryBlock->CheckAsmCode(); +#endif + + if (step > 4 && !changed) + { + ResetVisited(); + if (mEntryBlock->OptimizeXYSimpleLoop()) + changed = true; + } + + if (step == 3) + { + ResetVisited(); + if (mEntryBlock->EliminateUpper16BitSum(this)) + changed = true; + } + + CheckBlocks(); + + if (step == 3) + { + ResetVisited(); + if (mEntryBlock->CrossBlockIncToZeroShortcut()) + changed = true; + } + + if (step == 12) + { + ResetVisited(); + if (mEntryBlock->RemoveUnusedBitOps()) + changed = true; + } + +#if 1 + if (!changed && (step == 5 || step == 6 || step == 19)) + { + +#if 1 + int xregs[256], yregs[256]; + + for (int i = 0; i < 256; i++) + xregs[i] = yregs[i] = 0; + + for (int i = 0; i < 4; i++) + { + xregs[BC_REG_ACCU + i] = -1; + yregs[BC_REG_ACCU + i] = -1; + xregs[BC_REG_WORK + i] = -1; + yregs[BC_REG_WORK + i] = -1; + } + + if (!mInterProc->mLeafProcedure) + { + for (int i = BC_REG_FPARAMS; i < BC_REG_FPARAMS_END; i++) + { + xregs[i] = -1; + yregs[i] = -1; + } + } + + if (xmapped) + xregs[0] = -1; + if (ymapped) + yregs[0] = -1; + + ResetVisited(); + mEntryBlock->GlobalRegisterXYCheck(xregs, yregs); + + if (xregs[0] >= 0) + { + int j = 1; + for (int i = 0; i < 256; i++) + if (xregs[i] > xregs[j]) + j = i; + if (xregs[j] > 2) + { + ResetVisited(); + mEntryBlock->GlobalRegisterXMap(j); + if (j >= BC_REG_FPARAMS && j < BC_REG_FPARAMS_END && !mEntryBlock->mEntryRegA) + mEntryBlock->mTrueJump->mIns.Insert(0, NativeCodeInstruction(nullptr, ASMIT_LDX, ASMIM_ZERO_PAGE, j)); + changed = true; + xmapped = true; + continue; + } + } + + if (!changed && yregs[0] >= 0) + { + int j = 1; + for (int i = 0; i < 256; i++) + if (yregs[i] > yregs[j]) + j = i; + if (yregs[j] > 2) + { + ResetVisited(); + mEntryBlock->GlobalRegisterYMap(j); + if (j >= BC_REG_FPARAMS && j < BC_REG_FPARAMS_END && !mEntryBlock->mEntryRegA) + mEntryBlock->mTrueJump->mIns.Insert(0, NativeCodeInstruction(nullptr, ASMIT_LDY, ASMIM_ZERO_PAGE, j)); + changed = true; + ymapped = true; + continue; + } + } +#endif + + if (!changed) + { + if (step == 5) + { + ResetVisited(); + mEntryBlock->ReduceLocalXPressure(); + } + else + { + ResetVisited(); + if (mEntryBlock->LocalRegisterXYMap()) + changed = true; + } + } + } +#endif + + CheckBlocks(); + +#if 1 + ResetVisited(); + NativeRegisterDataSet data; + mEntryBlock->BuildEntryDataSet(data); + + ResetVisited(); + if (mEntryBlock->ApplyEntryDataSet()) + changed = true; +#endif + + if (step == 2 && !changed) + { + ResetVisited(); + if (mEntryBlock->CombineSameXY()) + changed = true; + } + +#if _DEBUG + ResetVisited(); + mEntryBlock->CheckAsmCode(); +#endif + +#if 1 + if (step >= 5) + { + if (step == 9) + { + ResetVisited(); + mEntryBlock->ReduceLocalYPressure(); + } + + ResetVisited(); + if (mEntryBlock->MoveAccuTrainsUp()) + changed = true; + + ResetVisited(); + if (mEntryBlock->MoveAccuTrainsDown()) + changed = true; + + ResetVisited(); + if (mEntryBlock->CombineSameXY()) + changed = true; + + ResetVisited(); + if (mEntryBlock->RecycleImmediates()) + changed = true; + + ResetVisited(); + if (mEntryBlock->RecycleLoadStore()) + changed = true; + } +#endif + +#if _DEBUG + ResetVisited(); + mEntryBlock->CheckAsmCode(); +#endif + + if (step == 7 || step == 29) + { + ResetVisited(); + if (mEntryBlock->SinglePathRegisterForward()) + { + BuildDataFlowSets(); + changed = true; + } + + } + + if (step > 7) + { + ResetVisited(); + if (mEntryBlock->SinglePathStoreForward()) + changed = true; + } + + CheckBlocks(); + + if (step == 7 || step == 14) + { + RebuildEntry(); + ResetVisited(); + if (mEntryBlock->CrossBlockRegisterAlias(false, false)) + { + BuildDataFlowSets(); + changed = true; + } + } + + + if (step == 8) + { + ResetVisited(); + if (mEntryBlock->JoinXYCascade()) + changed = true; + } + +#if 1 + if (step == 9 && cnt < 10) + { + CheckBlocks(); + + ResetVisited(); + while (mEntryBlock->OptimizeXYSpilling()) + { + BuildDataFlowSets(); + ResetVisited(); + mEntryBlock->RemoveUnusedResultInstructions(); + + changed = true; + } + + CheckBlocks(); + } +#endif + +#if _DEBUG + ResetVisited(); + mEntryBlock->CheckAsmCode(); +#endif + +#if 1 + if (step == 6) + { + ResetVisited(); + if (mEntryBlock->AlternateXYUsage()) + changed = true; + ResetVisited(); + if (mEntryBlock->OptimizeXYPairUsage()) + changed = true; + ResetVisited(); + if (mEntryBlock->UntangleXYUsage(false)) + changed = true; + } +#endif + + CheckBlocks(); + +#if _DEBUG + ResetVisited(); + mEntryBlock->CheckAsmCode(); +#endif + +#if 1 + ResetVisited(); + if (mEntryBlock->ForwardAccuAddSub()) + changed = true; + +#endif +#if 1 + ResetVisited(); + if (mEntryBlock->ForwardZpYIndex(step >= 5)) + changed = true; +#endif + +#if 1 + ResetVisited(); + if (mEntryBlock->ForwardZpXIndex(step >= 5)) + changed = true; +#endif + + CheckBlocks(); +#if 1 + if (step == 6) + { + ResetVisited(); + if (mEntryBlock->SimplifyDiamond(this)) + changed = true; + + CheckBlocks(); + + ResetVisited(); + if (mEntryBlock->SimplifyLoopEnd(this)) + changed = true; + + CheckBlocks(); + + ResetVisited(); + if (mEntryBlock->JoinDiamondArithmetic()) + changed = true; + + CheckBlocks(); + } + +#endif + + +#if _DEBUG + ResetVisited(); + mEntryBlock->CheckAsmCode(); +#endif + + if (step >= 6) + { + ResetVisited(); + if (mEntryBlock->BypassRegisterConditionBlock()) + changed = true; + } + + if (step == 8) + { + ResetVisited(); + if (mEntryBlock->LoopRegisterWrapAround()) + changed = true; + } + +#if 0 + if (step == 12) + { + ResetVisited(); + if (mEntryBlock->JoinXYCrossBlock()) + changed = true; + } +#endif + if (step >= 9) + { + ResetVisited(); + if (mEntryBlock->EliminateDeadLoops()) + changed = true; + } + + CheckBlocks(); + + if (step == 6 || step == 7) + { + ResetVisited(); + if (mEntryBlock->Propagate16BitHighSum()) + changed = true; + } + + if (step == 8) + { + ResetVisited(); + if (mEntryBlock->SimplifyDiamond(this)) + changed = true; + } + + CheckBlocks(); + +#if 1 + if (step == 8) + { + ResetVisited(); + if (mEntryBlock->CrossBlockXYFlood(this)) + changed = true; + } +#endif + +#if _DEBUG + ResetVisited(); + mEntryBlock->CheckAsmCode(); +#endif + +#if 1 + if (step == 8) + { + ResetVisited(); + if (mEntryBlock->CrossBlockY2XFlood(this)) + changed = true; + } +#endif + + if (step == 8) + { + ResetVisited(); + if (mEntryBlock->CrossBlock16BitFlood(this)) + changed = true; + } + +#if 1 + if (step >= 7) + { + ResetVisited(); + NativeRegisterDataSet data; + mEntryBlock->BuildEntryDataSet(data); + + ResetVisited(); + if (mEntryBlock->ExpandADCToBranch(this)) + { + changed = true; + BuildDataFlowSets(); + } + + if (!changed) + { + ResetVisited(); + if (mEntryBlock->ExpandADCShortCascadeToBranch()) + { + changed = true; + BuildDataFlowSets(); + } + } + } +#endif + +#if _DEBUG + ResetVisited(); + mEntryBlock->CheckAsmCode(); +#endif + +#if 1 + if (step == 9 || step == 26) + { + RebuildEntry(); + ResetVisited(); + if (mEntryBlock->OptimizeSingleEntryLoop(this)) + changed = true; + } +#endif + RebuildEntry(); + +#if _DEBUG + ResetVisited(); + mEntryBlock->CheckAsmCode(); +#endif +#if 1 + if (step == 2 && !changed) + { + ResetVisited(); + if (mEntryBlock->CrossBlockStoreLoadBypass(this)) + changed = true; + } +#endif + +#if 1 + if (step == 10 && (mCompilerOptions & COPT_OPTIMIZE_BASIC)) + { + ResetVisited(); + mEntryBlock->MarkLocalUsedLinkerObjects(); + + ResetVisited(); + if (mEntryBlock->RemoveLocalUnusedLinkerObjects()) + changed = true; + } +#endif + +#endif + + if (step == 8) + { + ResetVisited(); + if (mEntryBlock->ForwardAXYReg()) + changed = true; + } + + if (step == 14) + { + ResetVisited(); + if (mEntryBlock->AbsoluteLocalRegisterValueReuse()) + changed = true; + } + +#if 1 +#if _DEBUG + ResetVisited(); + mEntryBlock->CheckAsmCode(); +#endif + + if (step == 10) + { + if (changed) + swappedXY = false; + else if (!swappedXY) + { + NativeCodeBasicBlock* eblock = mEntryBlock; + for (;;) + { + if (!eblock->mEntryRequiredRegs[CPU_REG_X] && !eblock->mEntryRequiredRegs[CPU_REG_Y]) + { + ResetVisited(); + if (eblock->CanGlobalSwapXY()) + { + ResetVisited(); + changed = eblock->GlobalSwapXY(); + swappedXY = true; + break; + } + } + + if (eblock->mNumEntries <= 1 && eblock->mTrueJump && !eblock->mFalseJump) + eblock = eblock->mTrueJump; + else + break; + } + } + } + +#if _DEBUG + ResetVisited(); + mEntryBlock->CheckAsmCode(); +#endif + + if (step == 11) + { + if (changed && cnt < 20) + swappedXY = false; + else if (!swappedXY) + { + ResetVisited(); + changed = mEntryBlock->LocalSwapXY(); + swappedXY = true; + } + } + +#if 1 + if (step == 13) + { + if (changed && cnt < 20) + swappedXY = false; + else if (!swappedXY) + { + ResetVisited(); + changed = mEntryBlock->ShortSwapXY(); + swappedXY = true; + } + } +#endif + +#endif +#if 1 + if (step == 12) + { + ResetVisited(); + bool cc = mEntryBlock->GlobalLoadStoreForwarding(false, NativeCodeInstruction(), NativeCodeInstruction(), NativeCodeInstruction()); + if (!cc) + { + ResetVisited(); + cc = mEntryBlock->GlobalLoadStoreForwarding(true, NativeCodeInstruction(), NativeCodeInstruction(), NativeCodeInstruction()); + } + if (cc) + { + ResetVisited(); + NativeRegisterDataSet data; + mEntryBlock->BuildEntryDataSet(data); + changed = true; + } + } +#endif + + if (step == 14 && cnt == 0) + { + ResetVisited(); + mEntryBlock->BypassAccuLoadStoreXY(); + changed = true; + } + + if (step == 9) + { + ResetVisited(); + if (mEntryBlock->MoveStoresBehindCondition()) + changed = true; + } + +#if 1 + if (step == 15 && cnt == 0) + { + ResetVisited(); + mEntryBlock->RemoveUnusedResultInstructions(); + + BuildDataFlowSets(); + + ResetVisited(); + if (mEntryBlock->AlternateXXUsage()) + changed = true; + } +#endif + + if (step == 16) + { + ResetVisited(); + if (mEntryBlock->PropagateCrossBlockAccuExpression()) + { + changed = true; + BuildDataFlowSets(); + } + } + + if (step == 16) + { + ResetVisited(); + if (mEntryBlock->JoinConditionSequence()) + changed = true; + } + + if (step == 16) + { + if (mCompilerOptions & COPT_OPTIMIZE_INLINE) + { + ResetVisited(); + if (mEntryBlock->SimpleInlineCalls()) + changed = true; + } + } + + if (step == 17) + { + if (!(mCompilerOptions & COPT_OPTIMIZE_CODE_SIZE)) + { + ResetVisited(); + if (mEntryBlock->Expand16BitLoopBranch()) + changed = true; + + } + } + + if (step == 18) + { + ResetVisited(); + if (mEntryBlock->BackwardValuePropagation()) + { + changed = true; + BuildDataFlowSets(); + } + } + + if (step == 18) + { + ResetVisited(); + if (mEntryBlock->PartialBackwardValuePropagation()) + changed = true; + + if (!changed) + { + ResetVisited(); + if (mEntryBlock->HoistCommonLoads()) + changed = true; + } + + } + + if (step == 19) + { + ResetVisited(); + if (mEntryBlock->MoveStoresBeforeDiamond()) + changed = true; + } + + if (step == 20) + { + ResetVisited(); + if (mEntryBlock->ReduceIndexXYZeroShuffle(nullptr, -1, -1)) + changed = true; + } + + if (step == 21) + { + ResetVisited(); + if (mEntryBlock->OptimizeLoopRegisterWrapAround()) + changed = true; + + ResetVisited(); + if (mEntryBlock->RemoveJumpToBranch()) + changed = true; + + ResetVisited(); + if (mEntryBlock->ReverseLoadAccuToRegXY()) + changed = true; + } + + if (step == 22 && (mCompilerOptions & COPT_OPTIMIZE_BASIC)) + { + ResetVisited(); + if (mEntryBlock->EliminateNonAliasedLocalStores()) + changed = true; + } + + if (step == 22) + { + ResetVisited(); + if (mEntryBlock->MergeDuplicateCondition()) + changed = true; + } + + if (step == 23) + { + ResetVisited(); + if (mEntryBlock->PatchBitBoolConstOrigin()) + changed = true; + } + + if (step == 24) + { + ResetVisited(); + if (mEntryBlock->SortIndirectStores()) + changed = true; + } + + if (step == 25) + { + ResetVisited(); + if (mEntryBlock->Simplify16BitSum8BitRange()) + changed = true; + } + + if (step == 26) + { + ResetVisited(); + if (mEntryBlock->SimplifyLoopExitCondition()) + changed = true; + } + + if (step == 27) + { + ResetVisited(); + if (mEntryBlock->ReverseCrossBlockRegisterUnification()) + changed = true; + } + + if (step == 28) + { + if (cnt < 4) + { + ResetVisited(); + if (mEntryBlock->ReverseZeroPageRecycling()) + changed = true; + } + + ResetVisited(); + if (mEntryBlock->CrossBlockRegZPForward(-1, -1, -1)) + changed = true; + } + + if (step == 29) + { + ResetVisited(); + if (mEntryBlock->SplitSinglePath()) + changed = true; + } + + if (step == 30) + { + ResetVisited(); + if (mEntryBlock->ShortcutBranchSequence()) + changed = true; + } + + +#if _DEBUG + ResetVisited(); + mEntryBlock->CheckAsmCode(); +#endif + + +#if DISASSEMBLE_OPT + char fname[100]; + sprintf_s(fname, "Optimize %d, %d", step, cnt); + DisassembleDebug(fname); +#endif + +#if 1 + if (cnt > 190) + { + printf("Oops %d\n", step); + } +#endif + + if (cnt > 200) + { + changed = false; + mGenerator->mErrors->Error(mLocation, EWARN_OPTIMIZER_LOCKED, "Optimizer locked in infinite loop", mIdent); + } + +#if 1 + if (!changed && step < 31) + { + ResetIndexFlipped(); + + cnt = 0; + step++; + changed = true; + swappedXY = false; + } +#endif + else + cnt++; + + } while (changed); + +#if 1 + ResetVisited(); + mEntryBlock->CombineAlternateLoads(); +#endif + +#if 1 + ResetVisited(); + mEntryBlock->ReduceLocalYPressure(); +#endif + +#if 1 + BuildDataFlowSets(); + CompressTemporaries(false); + BuildDataFlowSets(); + CompressTemporaries(false); + BuildDataFlowSets(); + CompressTemporaries(true); +#endif + +#if DISASSEMBLE_OPT + DisassembleDebug("Post Op 1"); +#endif + + do { + ResetVisited(); + NativeRegisterDataSet data; + mEntryBlock->BuildEntryDataSet(data); + + ResetVisited(); + } while (mEntryBlock->ExpandADCToBranch(this)); + + do { + ResetVisited(); + } while (mEntryBlock->EliminateMicroBlocks()); + +#if DISASSEMBLE_OPT + DisassembleDebug("Post Op 2"); +#endif + + if (mExitBlock && mExitBlock->mIns.Size() == 1 && mExitBlock->mIns[0].mType == ASMIT_RTS && mExitBlock->mEntryBlocks.Size() == 1) + { + const NativeCodeInstruction& rins(mExitBlock->mIns[0]); + NativeCodeBasicBlock* pblock = mExitBlock->mEntryBlocks[0]; + int sz = pblock->mIns.Size(); + + int size = 0, reg = 0; + if (rins.mFlags & NCIF_LONG) + { + if (sz >= 8) + { + if (pblock->mIns[sz - 8].mType == ASMIT_LDA && pblock->mIns[sz - 8].mMode == ASMIM_ZERO_PAGE && + pblock->mIns[sz - 7].mType == ASMIT_STA && pblock->mIns[sz - 7].mMode == ASMIM_ZERO_PAGE && pblock->mIns[sz - 7].mAddress == BC_REG_ACCU && + pblock->mIns[sz - 6].mType == ASMIT_LDA && pblock->mIns[sz - 6].mMode == ASMIM_ZERO_PAGE && pblock->mIns[sz - 6].mAddress == pblock->mIns[sz - 8].mAddress + 1 && + pblock->mIns[sz - 5].mType == ASMIT_STA && pblock->mIns[sz - 5].mMode == ASMIM_ZERO_PAGE && pblock->mIns[sz - 5].mAddress == BC_REG_ACCU + 1 && + pblock->mIns[sz - 4].mType == ASMIT_LDA && pblock->mIns[sz - 4].mMode == ASMIM_ZERO_PAGE && pblock->mIns[sz - 4].mAddress == pblock->mIns[sz - 8].mAddress + 2 && + pblock->mIns[sz - 3].mType == ASMIT_STA && pblock->mIns[sz - 3].mMode == ASMIM_ZERO_PAGE && pblock->mIns[sz - 3].mAddress == BC_REG_ACCU + 2 && + pblock->mIns[sz - 2].mType == ASMIT_LDA && pblock->mIns[sz - 2].mMode == ASMIM_ZERO_PAGE && pblock->mIns[sz - 2].mAddress == pblock->mIns[sz - 8].mAddress + 3 && + pblock->mIns[sz - 1].mType == ASMIT_STA && pblock->mIns[sz - 1].mMode == ASMIM_ZERO_PAGE && pblock->mIns[sz - 1].mAddress == BC_REG_ACCU + 3) + { + reg = pblock->mIns[sz - 8].mAddress; + size = 4; + } + } + } + else if (rins.mFlags & NCIF_UPPER) + { + if (sz >= 4) + { + if (pblock->mIns[sz - 4].mType == ASMIT_LDA && pblock->mIns[sz - 4].mMode == ASMIM_ZERO_PAGE && + pblock->mIns[sz - 3].mType == ASMIT_STA && pblock->mIns[sz - 3].mMode == ASMIM_ZERO_PAGE && pblock->mIns[sz - 3].mAddress == BC_REG_ACCU && + pblock->mIns[sz - 2].mType == ASMIT_LDA && pblock->mIns[sz - 2].mMode == ASMIM_ZERO_PAGE && pblock->mIns[sz - 2].mAddress == pblock->mIns[sz - 4].mAddress + 1 && + pblock->mIns[sz - 1].mType == ASMIT_STA && pblock->mIns[sz - 1].mMode == ASMIM_ZERO_PAGE && pblock->mIns[sz - 1].mAddress == BC_REG_ACCU + 1) + { + reg = pblock->mIns[sz - 4].mAddress; + size = 2; + } + } + } + else + { + if (sz >= 4) + { + if (pblock->mIns[sz - 2].mType == ASMIT_LDA && pblock->mIns[sz - 2].mMode == ASMIM_ZERO_PAGE && + pblock->mIns[sz - 1].mType == ASMIT_STA && pblock->mIns[sz - 1].mMode == ASMIM_ZERO_PAGE && pblock->mIns[sz - 1].mAddress == BC_REG_ACCU) + { + reg = pblock->mIns[sz - 1].mAddress; + size = 2; + } + } + } + + if (size > 0 && (reg < BC_REG_FPARAMS || reg >= BC_REG_FPARAMS_END)) + { + ResetVisited(); + if (mEntryBlock->CanSwapAccuZP(reg, size)) + { + ResetVisited(); + mEntryBlock->SwapAccuZP(reg, size); + } + } + } +#if 1 + cnt = 0; + do + { + RebuildEntry(); + + BuildDataFlowSets(); + ResetVisited(); + changed = mEntryBlock->RemoveUnusedResultInstructions(); + + if (!changed) + { + ResetVisited(); + NativeRegisterDataSet data; + if (mEntryBlock->ValueForwarding(this, data, true, true)) + { + changed = true; + } + else + { +#if 1 + ResetPatched(); + mEntryBlock->CheckVisited(); + + ResetVisited(); + if (mEntryBlock->GlobalValueForwarding(this, true)) + changed = true; +#endif + } + } + + BuildDataFlowSets(); + ResetVisited(); + if (mEntryBlock->RemoveUnusedResultInstructions()) + changed = true; + + CheckBlocks(); + +#if DISASSEMBLE_OPT + DisassembleDebug("Post Op 3a"); +#endif + + if (!changed) + { + ResetVisited(); + changed = mEntryBlock->PropagateSinglePath(); + } + + if (!changed) + { + ResetVisited(); + changed = mEntryBlock->PeepHoleOptimizer(20); + } + +#if DISASSEMBLE_OPT + DisassembleDebug("Post Op 3b"); +#endif + + if (!changed) + { + ResetVisited(); + changed = mEntryBlock->JoinTailCodeSequences(true); + } +#if DISASSEMBLE_OPT + DisassembleDebug("Post Op 3c"); +#endif + + if (!changed) + { + ResetVisited(); + changed = mEntryBlock->UntangleXYUsage(true); + } +#if DISASSEMBLE_OPT + DisassembleDebug("Post Op 3d"); +#endif + + if (!changed) + { + ResetVisited(); + changed = mEntryBlock->JoinCommonBranchCodeSequences(); + } +#if DISASSEMBLE_OPT + DisassembleDebug("Post Op 3e"); +#endif + + cnt++; + } while (changed && cnt < 100); +#endif + +#if DISASSEMBLE_OPT + DisassembleDebug("Post Op 3"); +#endif + + ResetVisited(); + mEntryBlock->OptimizeLoopCarryOver(); + + ResetVisited(); + NativeRegisterDataSet data; + mEntryBlock->ValueForwarding(this, data, true, true); + + ResetVisited(); + mEntryBlock->CrossBlockFlagsForwarding(); + + ResetVisited(); + data.Reset(); + mEntryBlock->PropagateZPAbsolute(data); + + ResetVisited(); + mEntryBlock->PropagateAddGlobalCarry(); + + BuildDataFlowSets(); + ResetVisited(); + mEntryBlock->RemoveUnusedResultInstructions(); + + ResetVisited(); + mEntryBlock->RemoveJumpToBranch(); + + ResetVisited(); + mEntryBlock->MergeSameBranch(); + +#if 1 + CheckBlocks(); + + ResetVisited(); + if (mEntryBlock->MergeBasicBlocks()) + { + BuildDataFlowSets(); + CheckBlocks(); + } + +#endif + + RebuildEntry(); + + if (mCompilerOptions & COPT_OPTIMIZE_SELF_MOD) + { + ResetVisited(); + if (mEntryBlock->CollectSelfModTargets()) + { + ResetVisited(); + NativeRegisterDataSet data; + mEntryBlock->ValueForwarding(this, data, true, true); + + BuildDataFlowSets(); + ResetVisited(); + mEntryBlock->RemoveUnusedResultInstructions(); + + ResetVisited(); + mEntryBlock->PeepHoleOptimizer(299); +#if 0 + ResetVisited(); + mEntryBlock->JoinTailCodeSequences(true); + +#endif + } + } + + ResetVisited(); + if (mEntryBlock->FinalCheckedSizeReduction()) + { + BuildDataFlowSets(); + ResetVisited(); + mEntryBlock->RemoveUnusedResultInstructions(); + CheckBlocks(); + } + + +#if 1 + ResetVisited(); + data.Reset(); + mEntryBlock->BuildEntryDataSet(data); + + ResetVisited(); + if (mEntryBlock->ApplyEntryDataSet()) + changed = true; +#endif +#if DISASSEMBLE_OPT + DisassembleDebug("Post Op 4"); +#endif + + TrimBlocks(); + +#if DISASSEMBLE_OPT + DisassembleDebug("Trimmed"); +#endif + +#if 1 + + RebuildEntry(); + + ResetVisited(); + mEntryBlock->BranchAccuAlternativeElimination(); + + ResetVisited(); + mEntryBlock->BlockSizeReduction(this, -1, -1, -1); + +#if DISASSEMBLE_OPT + DisassembleDebug("Reduced"); +#endif + + ResetVisited(); + mEntryBlock->MergeBasicBlocks(); +#endif + +#endif +} + +void NativeCodeProcedure::BuildDataFlowSets(void) +{ + // + // Build set with local provided/required temporaries + // + ResetVisited(); + mBlocks[0]->BuildLocalRegSets(); + + // + // Build set of globally provided temporaries + // + ResetVisited(); + mBlocks[0]->BuildGlobalProvidedRegSet(NumberSet(NUM_REGS)); + + // + // Build set of globally required temporaries, might need + // multiple iterations until it stabilizes + // + NumberSet totalRequired(NUM_REGS); + + do { + ResetVisited(); + } while (mBlocks[0]->BuildGlobalRequiredRegSet(totalRequired)); +} + +NativeCodeBasicBlock* NativeCodeProcedure::AllocateBlock(void) +{ + NativeCodeBasicBlock* block = new NativeCodeBasicBlock(this); + block->mNoFrame = mNoFrame; + block->mFrameOffset = mFrameOffset; + block->mIndex = mTempBlocks++; + mBlocks.Push(block); + + return block; +} + +void NativeCodeProcedure::TrimBlocks(void) +{ +#if DISASSEMBLE_OPT + if (CheckFunc) return; +#endif + + int n = 0; + int j = 1; + for (int i = 1; i < mBlocks.Size(); i++) + { + NativeCodeBasicBlock* block = mBlocks[i]; + if (block->mNumEntries > 0 || block->mLocked) + { + block->mIndex = j; + mBlocks[j++] = block; + } + else + { + delete block; + n++; + } + } + mBlocks.SetSize(j, false); +} + +NativeCodeBasicBlock* NativeCodeProcedure::CompileBlock(InterCodeProcedure* iproc, InterCodeBasicBlock* sblock) +{ + if (tblocks[sblock->mIndex]) + return tblocks[sblock->mIndex]; + + NativeCodeBasicBlock* block = new NativeCodeBasicBlock(this); + block->mNoFrame = mNoFrame; + block->mFrameOffset = mFrameOffset; + mBlocks.Push(block); + + tblocks[sblock->mIndex] = block; + block->mIndex = sblock->mIndex; + + CompileInterBlock(iproc, sblock, block); + + return block; +} + +void NativeCodeProcedure::ResetEntryBlocks(void) +{ + for (int i = 0; i < mBlocks.Size(); i++) + mBlocks[i]->mEntryBlocks.SetSize(0); +} + +void NativeCodeProcedure::ResetIndexFlipped(void) +{ + for (int i = 0; i < mBlocks.Size(); i++) + { + for (int j = 0; j < mBlocks[i]->mIns.Size(); j++) + mBlocks[i]->mIns[j].mFlags &= ~NICT_INDEXFLIPPED; + } +} + + +void NativeCodeProcedure::ResetPatched(void) +{ + for (int i = 0; i < mBlocks.Size(); i++) + { + NativeCodeBasicBlock* b = mBlocks[i]; + b->mPatched = false; + b->mPatchFail = false; + b->mPatchUsed = false; + b->mPatchChecked = false; + b->mPatchStart = false; + b->mPatchLoop = false; + b->mPatchLoopChanged = false; + b->mPatchExit = false; + } +} + +void NativeCodeProcedure::ResetChecked(void) +{ + for (int i = 0; i < mBlocks.Size(); i++) + { + mBlocks[i]->mChecked = false; + + } +} + +void NativeCodeProcedure::ResetVisited(void) +{ + for (int i = 0; i < mBlocks.Size(); i++) + { +// assert(mBlocks[i]->mIns.Size() > 0 || (mBlocks[i]->mTrueJump != mBlocks[i] && mBlocks[i]->mFalseJump != mBlocks[i])); + + mBlocks[i]->mVisited = false; + mBlocks[i]->mNumEntered = 0; + mBlocks[i]->mLoopHeadBlock = nullptr; + + } +} + + +void NativeCodeProcedure::CompileInterBlock(InterCodeProcedure* iproc, InterCodeBasicBlock* iblock, NativeCodeBasicBlock* block) +{ + // Detect copy loop + if (iblock->mInstructions.Size() == 7) + { + // 0000 (34) LOADI2 R6(I)[-32768..65535] < -R0(P) + 0 {} + // 0001 (34) STOREI2 < -R3(P) + 0 {V(175 'rooms' A)}, R6(IF)[-32768..65535] {} + // 0002 (34) LEAI R0(P) + 0 < -R0(PF) + 0, CI:2 {} + // 0003 (34) LEAI R3(P) + 0 {V(175 'rooms' A)} < -R3(PF) + 0 {V(175 'rooms' A)}, CI : 2 {} + // 0004 (33) BINOP2 R5(I)[0..35] < -R5(IF)[1..36], CI : 1 {} + // 0005 (33) RELOP27 R6(B)[0..1] < -R5(I)[0..35], CI : 0 {} + // 0006 (33) BRANCH < -R6(BF)[0..1] {} + + if (iblock->mTrueJump == iblock && iblock->mEntryBlocks.Size() == 2) + { + if (iblock->mInstructions[0]->mCode == IC_LOAD && + iblock->mInstructions[0]->mSrc[0].mTemp >= 0 && + iblock->mInstructions[1]->mCode == IC_STORE && + iblock->mInstructions[1]->mSrc[0].mTemp == iblock->mInstructions[0]->mDst.mTemp && iblock->mInstructions[1]->mSrc[1].mTemp >= 0 && + iblock->mInstructions[1]->mSrc[0].mType == iblock->mInstructions[0]->mDst.mType && + iblock->mInstructions[2]->mCode == IC_LEA && + iblock->mInstructions[2]->mSrc[1].mTemp == iblock->mInstructions[0]->mSrc[0].mTemp && + iblock->mInstructions[2]->mDst.mTemp == iblock->mInstructions[2]->mSrc[1].mTemp && + iblock->mInstructions[2]->mSrc[0].mTemp < 0 && iblock->mInstructions[2]->mSrc[0].mIntConst == InterTypeSize[iblock->mInstructions[0]->mDst.mType] && + iblock->mInstructions[3]->mCode == IC_LEA && + iblock->mInstructions[3]->mSrc[1].mTemp == iblock->mInstructions[1]->mSrc[1].mTemp && + iblock->mInstructions[3]->mDst.mTemp == iblock->mInstructions[3]->mSrc[1].mTemp && + iblock->mInstructions[3]->mSrc[0].mTemp < 0 && iblock->mInstructions[3]->mSrc[0].mIntConst == InterTypeSize[iblock->mInstructions[0]->mDst.mType] && + iblock->mInstructions[4]->mCode == IC_BINARY_OPERATOR && iblock->mInstructions[4]->mOperator == IA_SUB && + iblock->mInstructions[4]->mDst.mTemp == iblock->mInstructions[4]->mSrc[1].mTemp && + iblock->mInstructions[4]->mSrc[0].mTemp < 0 && iblock->mInstructions[4]->mSrc[0].mIntConst == 1 && + iblock->mInstructions[5]->mCode == IC_RELATIONAL_OPERATOR && iblock->mInstructions[5]->mOperator == IA_CMPGU && + iblock->mInstructions[4]->mDst.mTemp == iblock->mInstructions[5]->mSrc[1].mTemp && + iblock->mInstructions[5]->mSrc[0].mTemp < 0 && iblock->mInstructions[5]->mSrc[0].mIntConst == 0 && + iblock->mInstructions[6]->mCode == IC_BRANCH && + iblock->mInstructions[5]->mDst.mTemp == iblock->mInstructions[6]->mSrc[0].mTemp + ) + { + if (!iblock->mFalseJump->mEntryRequiredTemps[iblock->mInstructions[0]->mSrc[0].mTemp] && + !iblock->mFalseJump->mEntryRequiredTemps[iblock->mInstructions[1]->mSrc[1].mTemp] && + iblock->mInstructions[4]->mSrc[1].IsUByte() && iblock->mInstructions[4]->mSrc[1].mRange.mMaxValue * InterTypeSize[iblock->mInstructions[0]->mDst.mType] < 256) + { + int ireg = BC_REG_TMP + iproc->mTempOffset[iblock->mInstructions[4]->mDst.mTemp]; + int sreg = BC_REG_TMP + iproc->mTempOffset[iblock->mInstructions[0]->mSrc[0].mTemp]; + int dreg = BC_REG_TMP + iproc->mTempOffset[iblock->mInstructions[1]->mSrc[1].mTemp]; + + InterInstruction* ins = iblock->mInstructions[0]; + + int sz = InterTypeSize[iblock->mInstructions[0]->mDst.mType]; + if (sz == 1) + block->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDX, ASMIM_ZERO_PAGE, ireg)); + else + { + block->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, ireg)); + block->mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL)); + if (sz == 4) + block->mIns.Push(NativeCodeInstruction(ins, ASMIT_ASL)); + block->mIns.Push(NativeCodeInstruction(ins, ASMIT_TAX)); + } + block->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDY, ASMIM_IMMEDIATE, 0)); + + NativeCodeBasicBlock* lblock = AllocateBlock(); + + block->Close(ins, lblock, nullptr, ASMIT_JMP); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_INDIRECT_Y, sreg)); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_INDIRECT_Y, dreg)); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_INY)); + lblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_DEX)); + + lblock->Close(ins, lblock, CompileBlock(iproc, iblock->mFalseJump), ASMIT_BNE); + + return; + } + } + } + } + + int i = 0; + while (i < iblock->mInstructions.Size()) + { + const InterInstruction * ins = iblock->mInstructions[i]; + + switch (ins->mCode) + { + case IC_STORE: + if (i + 1 < iblock->mInstructions.Size() && + ins->mSrc[0].mType == IT_INT8 && + ins->mSrc[0].mTemp >= 0 && + ins->mSrc[1].mTemp >= 0 && + iblock->mInstructions[i + 1]->mCode == IC_STORE && + iblock->mInstructions[i + 1]->mSrc[0].mType == IT_INT8 && + iblock->mInstructions[i + 1]->mSrc[0].mTemp == ins->mSrc[0].mTemp && + iblock->mInstructions[i + 1]->mSrc[0].mFinal && + iblock->mInstructions[i + 1]->mSrc[1].mTemp >= 0) + { + block->LoadStoreIndirectPair(iproc, ins, iblock->mInstructions[i + 1]); + i++; + } + else + block->StoreValue(iproc, ins); + break; + case IC_LOAD: + if (i + 1 < iblock->mInstructions.Size() && + iblock->mInstructions[i + 1]->mCode == IC_STORE && + iblock->mInstructions[i + 1]->mSrc[0].mTemp == ins->mDst.mTemp && + iblock->mInstructions[i + 1]->mSrc[0].mFinal) + { + block->LoadStoreIndirectValue(iproc, ins, iblock->mInstructions[i + 1]); + i++; + } + else if (i + 2 < iblock->mInstructions.Size() && + (ins->mDst.mType == IT_INT8 || ins->mDst.mType == IT_INT16 || ins->mDst.mType == IT_INT32) && + iblock->mInstructions[i + 1]->mCode == IC_BINARY_OPERATOR && + iblock->mInstructions[i + 1]->mSrc[0].mTemp == ins->mDst.mTemp && iblock->mInstructions[i + 1]->mSrc[0].mFinal && + iblock->mInstructions[i + 2]->mCode == IC_STORE && + iblock->mInstructions[i + 2]->mSrc[0].mTemp == iblock->mInstructions[i + 1]->mDst.mTemp && + (iblock->mInstructions[i + 2]->mSrc[0].mFinal || iblock->mInstructions[i + 2]->mSrc[0].mTemp != ins->mSrc[0].mTemp) && + block->LoadOpStoreIndirectValue(iproc, ins, iblock->mInstructions[i + 1], 1, iblock->mInstructions[i + 2])) + { + i += 2; + } + else if (i + 2 < iblock->mInstructions.Size() && + (ins->mDst.mType == IT_INT8 || ins->mDst.mType == IT_INT16 || ins->mDst.mType == IT_INT32) && + iblock->mInstructions[i + 1]->mCode == IC_BINARY_OPERATOR && + iblock->mInstructions[i + 1]->mSrc[1].mTemp == ins->mDst.mTemp && iblock->mInstructions[i + 1]->mSrc[1].mFinal && + iblock->mInstructions[i + 2]->mCode == IC_STORE && + iblock->mInstructions[i + 2]->mSrc[0].mTemp == iblock->mInstructions[i + 1]->mDst.mTemp && + (iblock->mInstructions[i + 2]->mSrc[0].mFinal || iblock->mInstructions[i + 2]->mSrc[0].mTemp != ins->mSrc[0].mTemp) && + block->LoadOpStoreIndirectValue(iproc, ins, iblock->mInstructions[i + 1], 0, iblock->mInstructions[i + 2])) + { + i += 2; + } + else if (i + 2 < iblock->mInstructions.Size() && + (ins->mDst.mType == IT_INT8 || ins->mDst.mType == IT_INT16 || ins->mDst.mType == IT_INT32) && + iblock->mInstructions[i + 1]->mCode == IC_UNARY_OPERATOR && + iblock->mInstructions[i + 1]->mSrc[0].mTemp == ins->mDst.mTemp && iblock->mInstructions[i + 1]->mSrc[0].mFinal && + iblock->mInstructions[i + 2]->mCode == IC_STORE && + iblock->mInstructions[i + 2]->mSrc[0].mTemp == iblock->mInstructions[i + 1]->mDst.mTemp && + (iblock->mInstructions[i + 2]->mSrc[0].mFinal || iblock->mInstructions[i + 2]->mSrc[0].mTemp != ins->mSrc[0].mTemp) && + block->LoadUnopStoreIndirectValue(iproc, ins, iblock->mInstructions[i + 1], iblock->mInstructions[i + 2])) + { + i += 2; + } + else if (i + 3 < iblock->mInstructions.Size() && + (ins->mDst.mType == IT_INT8 || ins->mDst.mType == IT_INT16 || ins->mDst.mType == IT_INT32) && + iblock->mInstructions[i + 1]->mCode == IC_LOAD && + iblock->mInstructions[i + 2]->mCode == IC_BINARY_OPERATOR && + iblock->mInstructions[i + 2]->mSrc[0].mTemp == ins->mDst.mTemp && iblock->mInstructions[i + 2]->mSrc[0].mFinal && + iblock->mInstructions[i + 2]->mSrc[1].mTemp == iblock->mInstructions[i + 1]->mDst.mTemp && iblock->mInstructions[i + 2]->mSrc[1].mFinal && + iblock->mInstructions[i + 3]->mCode == IC_STORE && + iblock->mInstructions[i + 3]->mSrc[0].mTemp == iblock->mInstructions[i + 2]->mDst.mTemp && //iblock->mInstructions[i + 3]->mSrc[0].mFinal && + block->LoadLoadOpStoreIndirectValue(iproc, ins, iblock->mInstructions[i + 1], iblock->mInstructions[i + 2], iblock->mInstructions[i + 3])) + { + i += 3; + } + else if (i + 3 < iblock->mInstructions.Size() && + (ins->mDst.mType == IT_INT8 || ins->mDst.mType == IT_INT16 || ins->mDst.mType == IT_INT32) && + iblock->mInstructions[i + 1]->mCode == IC_LOAD && + iblock->mInstructions[i + 2]->mCode == IC_BINARY_OPERATOR && + iblock->mInstructions[i + 2]->mSrc[1].mTemp == ins->mDst.mTemp && iblock->mInstructions[i + 2]->mSrc[1].mFinal && + iblock->mInstructions[i + 2]->mSrc[0].mTemp == iblock->mInstructions[i + 1]->mDst.mTemp && iblock->mInstructions[i + 2]->mSrc[0].mFinal && + iblock->mInstructions[i + 3]->mCode == IC_STORE && + iblock->mInstructions[i + 3]->mSrc[0].mTemp == iblock->mInstructions[i + 2]->mDst.mTemp && //iblock->mInstructions[i + 3]->mSrc[0].mFinal && + block->LoadLoadOpStoreIndirectValue(iproc, iblock->mInstructions[i + 1], ins, iblock->mInstructions[i + 2], iblock->mInstructions[i + 3])) + { + i += 3; + } + else if (i + 1 < iblock->mInstructions.Size() && + InterTypeSize[ins->mDst.mType] >= 2 && + iblock->mInstructions[i + 1]->mCode == IC_BINARY_OPERATOR && + iblock->mInstructions[i + 1]->mSrc[0].mTemp == ins->mDst.mTemp && iblock->mInstructions[i + 1]->mSrc[0].mType == ins->mDst.mType && iblock->mInstructions[i + 1]->mSrc[0].mFinal && + iblock->mInstructions[i + 1]->mSrc[1].mTemp == ins->mDst.mTemp && iblock->mInstructions[i + 1]->mSrc[1].mType == ins->mDst.mType && iblock->mInstructions[i + 1]->mSrc[1].mFinal) + { + block = block->BinaryOperator(iproc, this, iblock->mInstructions[i + 1], ins, ins); + i++; + } + else if (i + 1 < iblock->mInstructions.Size() && + InterTypeSize[ins->mDst.mType] >= 2 && + InterTypeSize[ins->mDst.mType] * ins->mSrc[0].mStride <= 256 && + iblock->mInstructions[i + 1]->mCode == IC_BINARY_OPERATOR && + iblock->mInstructions[i + 1]->mSrc[0].mTemp == ins->mDst.mTemp && iblock->mInstructions[i + 1]->mSrc[0].mType == ins->mDst.mType && iblock->mInstructions[i + 1]->mSrc[0].mFinal) + { + block = block->BinaryOperator(iproc, this, iblock->mInstructions[i + 1], nullptr, ins); + i++; + } + else if (i + 1 < iblock->mInstructions.Size() && + InterTypeSize[ins->mDst.mType] >= 2 && + InterTypeSize[ins->mDst.mType] * ins->mSrc[0].mStride < 256 && + iblock->mInstructions[i + 1]->mCode == IC_BINARY_OPERATOR && + iblock->mInstructions[i + 1]->mSrc[1].mTemp == ins->mDst.mTemp && iblock->mInstructions[i + 1]->mSrc[1].mType == ins->mDst.mType && iblock->mInstructions[i + 1]->mSrc[1].mFinal) + { + block = block->BinaryOperator(iproc, this, iblock->mInstructions[i + 1], ins, nullptr); + i++; + } + else if (i + 2 < iblock->mInstructions.Size() && + InterTypeSize[ins->mDst.mType] >= 2 && + InterTypeSize[ins->mDst.mType] * ins->mSrc[0].mStride <= 256 && + iblock->mInstructions[i + 1]->mCode == IC_LOAD && InterTypeSize[iblock->mInstructions[i + 1]->mDst.mType] >= 2 && + InterTypeSize[iblock->mInstructions[i + 1]->mDst.mType] * iblock->mInstructions[i + 1]->mSrc[0].mStride <= 256 && + iblock->mInstructions[i + 1]->mDst.mTemp != ins->mDst.mTemp && + iblock->mInstructions[i + 2]->mCode == IC_BINARY_OPERATOR && + iblock->mInstructions[i + 2]->mSrc[0].mTemp == iblock->mInstructions[i + 1]->mDst.mTemp && iblock->mInstructions[i + 2]->mSrc[0].mType == iblock->mInstructions[i + 1]->mDst.mType && iblock->mInstructions[i + 2]->mSrc[0].mFinal && + iblock->mInstructions[i + 2]->mSrc[1].mTemp == ins->mDst.mTemp && iblock->mInstructions[i + 2]->mSrc[1].mType == ins->mDst.mType && iblock->mInstructions[i + 2]->mSrc[1].mFinal) + { + block = block->BinaryOperator(iproc, this, iblock->mInstructions[i + 2], ins, iblock->mInstructions[i + 1]); + i += 2; + } + else if (i + 2 < iblock->mInstructions.Size() && + InterTypeSize[ins->mDst.mType] >= 2 && + InterTypeSize[ins->mDst.mType] * ins->mSrc[0].mStride <= 256 && + iblock->mInstructions[i + 1]->mCode == IC_LOAD && InterTypeSize[iblock->mInstructions[i + 1]->mDst.mType] >= 2 && + InterTypeSize[iblock->mInstructions[i + 1]->mDst.mType] * iblock->mInstructions[i + 1]->mSrc[0].mStride <= 256 && + iblock->mInstructions[i + 1]->mDst.mTemp != ins->mDst.mTemp && + iblock->mInstructions[i + 2]->mCode == IC_BINARY_OPERATOR && + iblock->mInstructions[i + 2]->mSrc[1].mTemp == iblock->mInstructions[i + 1]->mDst.mTemp && iblock->mInstructions[i + 2]->mSrc[1].mType == iblock->mInstructions[i + 1]->mDst.mType && iblock->mInstructions[i + 2]->mSrc[1].mFinal && + iblock->mInstructions[i + 2]->mSrc[0].mTemp == ins->mDst.mTemp && iblock->mInstructions[i + 2]->mSrc[0].mType == ins->mDst.mType && iblock->mInstructions[i + 2]->mSrc[0].mFinal) + { + block = block->BinaryOperator(iproc, this, iblock->mInstructions[i + 2], iblock->mInstructions[i + 1], ins); + i += 2; + } + else if (i + 1 < iblock->mInstructions.Size() && + InterTypeSize[ins->mDst.mType] >= 2 && + iblock->mInstructions[i + 1]->mCode == IC_LEA && + iblock->mInstructions[i + 1]->mSrc[1].mTemp == ins->mDst.mTemp && iblock->mInstructions[i + 1]->mSrc[1].mType == ins->mDst.mType && iblock->mInstructions[i + 1]->mSrc[1].mFinal && + !(iblock->mInstructions[i + 1]->mSrc[0].IsUByte() && + iblock->mInstructions[i + 1]->mSrc[0].mTemp >= 0 && + i + 2 < iblock->mInstructions.Size() && + iblock->mInstructions[i + 2]->mCode == IC_LOAD && + iblock->mInstructions[i + 2]->mSrc->mTemp == iblock->mInstructions[i + 1]->mDst.mTemp)) + { + block->LoadEffectiveAddress(iproc, iblock->mInstructions[i + 1], ins, nullptr, false); + i++; + } + else + block->LoadValue(iproc, ins); + break; + case IC_FILL: + block = block->FillValue(iproc, ins, this); + break; + case IC_COPY: + block = block->CopyValue(iproc, ins, this); + break; + case IC_STRCPY: + block = block->StrcpyValue(iproc, ins, this); + break; + + case IC_MALLOC: + block->CallMalloc(iproc, ins, this); + break; + case IC_FREE: + block->CallFree(iproc, ins, this); + break; + case IC_BREAKPOINT: + { + NativeCodeGenerator::Runtime& frt(mGenerator->ResolveRuntime(Ident::Unique("breakpoint"))); + block->mIns.Push(NativeCodeInstruction(ins, ASMIT_JSR, ASMIM_ABSOLUTE, frt.mOffset, frt.mLinkerObject, NCIF_LOWER | NCIF_UPPER | NCIF_VOLATILE | NCIF_BREAKPOINT)); + } break; + + case IC_LOAD_TEMPORARY: + { + if (ins->mSrc[0].mTemp != ins->mDst.mTemp) + { + for (int i = 0; i < InterTypeSize[ins->mDst.mType]; i++) + { + block->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + iproc->mTempOffset[ins->mSrc[0].mTemp] + i)); + block->mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + iproc->mTempOffset[ins->mDst.mTemp] + i)); + } + } + } break; + case IC_BINARY_OPERATOR: + if (i + 1 < iblock->mInstructions.Size() && + ins->mCode == IC_BINARY_OPERATOR && ins->mOperator == IA_ADD && ins->mSrc[1].IsSByte() && ins->mSrc[0].mTemp < 0 && + iblock->mInstructions[i + 1]->mCode == IC_BINARY_OPERATOR && iblock->mInstructions[i + 1]->mOperator == IA_SAR && + iblock->mInstructions[i + 1]->mSrc[1].mTemp == ins->mDst.mTemp && iblock->mInstructions[i + 1]->mSrc[1].mFinal && + iblock->mInstructions[i + 1]->mSrc[0].mTemp < 0 && iblock->mInstructions[i + 1]->mSrc[0].mIntConst <= 4 && + ins->mSrc[0].mIntConst == (1LL << (iblock->mInstructions[i + 1]->mSrc[0].mIntConst - 1))) + { + block->AddAsrSignedByte(iproc, ins, iblock->mInstructions[i + 1]); + i ++; + } + else if (i + 1 < iblock->mInstructions.Size() && + ins->mCode == IC_BINARY_OPERATOR && ins->mOperator == IA_DIVU && + iblock->mInstructions[i + 1]->mOperator == IA_MODU && + ins->mSrc[0].IsEqual(iblock->mInstructions[i + 1]->mSrc[0]) && + ins->mSrc[1].IsEqual(iblock->mInstructions[i + 1]->mSrc[1]) && + ins->mSrc[0].mTemp != ins->mDst.mTemp && ins->mSrc[1].mTemp != ins->mDst.mTemp) + { + block->BinaryDivModPair(iproc, this, ins, iblock->mInstructions[i + 1], false); + i++; + } + else if (i + 1 < iblock->mInstructions.Size() && + ins->mCode == IC_BINARY_OPERATOR && ins->mOperator == IA_MODU && + iblock->mInstructions[i + 1]->mOperator == IA_DIVU && + ins->mSrc[0].IsEqual(iblock->mInstructions[i + 1]->mSrc[0]) && + ins->mSrc[1].IsEqual(iblock->mInstructions[i + 1]->mSrc[1]) && + ins->mSrc[0].mTemp != ins->mDst.mTemp && ins->mSrc[1].mTemp != ins->mDst.mTemp) + { + block->BinaryDivModPair(iproc, this, iblock->mInstructions[i + 1], ins, false); + i++; + } + else if (i + 1 < iblock->mInstructions.Size() && + ins->mCode == IC_BINARY_OPERATOR && ins->mOperator == IA_DIVS && + iblock->mInstructions[i + 1]->mOperator == IA_MODS && + ins->mSrc[0].IsEqual(iblock->mInstructions[i + 1]->mSrc[0]) && + ins->mSrc[1].IsEqual(iblock->mInstructions[i + 1]->mSrc[1]) && + ins->mSrc[0].mTemp != ins->mDst.mTemp && ins->mSrc[1].mTemp != ins->mDst.mTemp) + { + block->BinaryDivModPair(iproc, this, ins, iblock->mInstructions[i + 1], true); + i++; + } + else if (i + 1 < iblock->mInstructions.Size() && + ins->mCode == IC_BINARY_OPERATOR && ins->mOperator == IA_MODS && + iblock->mInstructions[i + 1]->mOperator == IA_DIVS && + ins->mSrc[0].IsEqual(iblock->mInstructions[i + 1]->mSrc[0]) && + ins->mSrc[1].IsEqual(iblock->mInstructions[i + 1]->mSrc[1]) && + ins->mSrc[0].mTemp != ins->mDst.mTemp && ins->mSrc[1].mTemp != ins->mDst.mTemp) + { + block->BinaryDivModPair(iproc, this, iblock->mInstructions[i + 1], ins, true); + i++; + } + else if (i + 1 < iblock->mInstructions.Size() && + ins->mDst.mType == IT_INT16 && + ins->mOperator == IA_AND && + iblock->mInstructions[i + 1]->mCode == IC_BINARY_OPERATOR && + iblock->mInstructions[i + 1]->mOperator == IA_MUL && + ins->mSrc[0].mTemp < 0 && + ins->mDst.mTemp == iblock->mInstructions[i + 1]->mSrc[1].mTemp && + ins->mDst.IsUByte() && + iblock->mInstructions[i + 1]->mSrc[1].mFinal && + iblock->mInstructions[i + 1]->mSrc[0].mTemp < 0 && + iblock->mInstructions[i + 1]->mSrc[0].mIntConst >= 0 && iblock->mInstructions[i + 1]->mSrc[0].mIntConst < 256) + { + block->BinaryAndMulConst(iproc, ins, iblock->mInstructions[i + 1]); + i++; + } + else if (i + 1 < iblock->mInstructions.Size() && + ins->mDst.mType == IT_INT16 && ins->mOperator == IA_SHL && ins->mSrc[1].IsUByte() && ins->mSrc[0].mTemp < 0 && + iblock->mInstructions[i + 1]->mCode == IC_BINARY_OPERATOR && (iblock->mInstructions[i + 1]->mOperator == IA_ADD || iblock->mInstructions[i + 1]->mOperator == IA_SUB) && + iblock->mInstructions[i + 1]->mSrc[1].mTemp == ins->mDst.mTemp && iblock->mInstructions[i + 1]->mSrc[1].mFinal && iblock->mInstructions[i + 1]->mSrc[0].mTemp < 0) + { + block->BinaryShlAddConstPair(iproc, ins, iblock->mInstructions[i + 1]); + i++; + } + else if (i + 1 < iblock->mInstructions.Size() && + ins->mDst.mType == IT_INT16 && ins->mOperator == IA_ADD && ins->mSrc[0].IsUByte() && ins->mSrc[1].IsUByte() && + + iblock->mInstructions[i + 1]->mCode == IC_BINARY_OPERATOR && iblock->mInstructions[i + 1]->mOperator == IA_ADD && + iblock->mInstructions[i + 1]->mSrc[0].mRange.IsRange(0, 1) && iblock->mInstructions[i + 1]->mSrc[0].mFinal && + iblock->mInstructions[i + 1]->mSrc[1].mTemp == ins->mDst.mTemp && iblock->mInstructions[i + 1]->mSrc[1].mFinal) + { + block->BinaryAddCarry(iproc, ins, iblock->mInstructions[i + 1]); + i++; + } + else if (i + 1 < iblock->mInstructions.Size() && + ins->mDst.mType == IT_INT16 && ins->mOperator == IA_ADD && ins->mSrc[0].IsUByte() && ins->mSrc[1].IsUByte() && + + iblock->mInstructions[i + 1]->mCode == IC_BINARY_OPERATOR && iblock->mInstructions[i + 1]->mOperator == IA_ADD && + iblock->mInstructions[i + 1]->mSrc[1].mRange.IsRange(0, 1) && iblock->mInstructions[i + 1]->mSrc[1].mFinal && + iblock->mInstructions[i + 1]->mSrc[0].mTemp == ins->mDst.mTemp && iblock->mInstructions[i + 1]->mSrc[0].mFinal) + { + block->BinaryAddCarry(iproc, ins, iblock->mInstructions[i + 1]); + i++; + } + else + block = block->BinaryOperator(iproc, this, ins, nullptr, nullptr); + break; + case IC_UNARY_OPERATOR: + if (i + 1 < iblock->mInstructions.Size() && ins->mOperator == IA_NEG && iblock->mInstructions[i + 1]->mCode == IC_LEA && iblock->mInstructions[i + 1]->mSrc[0].mTemp == ins->mDst.mTemp && iblock->mInstructions[i + 1]->mSrc[0].mFinal) + { + block->LoadEffectiveAddress(iproc, iblock->mInstructions[i + 1], nullptr, ins, false); + i++; + } + else if (i + 2 < iblock->mInstructions.Size() && ins->mOperator == IA_NEG && + iblock->mInstructions[i + 2]->mCode == IC_LEA && iblock->mInstructions[i + 2]->mSrc[0].mTemp == ins->mDst.mTemp && iblock->mInstructions[i + 2]->mSrc[0].mFinal && + iblock->mInstructions[i + 1]->mCode == IC_LOAD && iblock->mInstructions[i + 2]->mSrc[1].mTemp == iblock->mInstructions[i + 1]->mDst.mTemp && iblock->mInstructions[i + 2]->mSrc[1].mFinal) + { + block->LoadEffectiveAddress(iproc, iblock->mInstructions[i + 2], iblock->mInstructions[i + 1], ins, false); + i+=2; + } + else + block->UnaryOperator(iproc, this, ins); + break; + case IC_CONVERSION_OPERATOR: + if (i + 1 < iblock->mInstructions.Size() && + ins->mOperator == IA_EXT8TO16S && + iblock->mInstructions[i + 1]->mCode == IC_BINARY_OPERATOR && iblock->mInstructions[i + 1]->mOperator == IA_ADD && + (iblock->mInstructions[i + 1]->mSrc[0].mTemp == ins->mDst.mTemp && iblock->mInstructions[i + 1]->mSrc[0].mFinal && iblock->mInstructions[i + 1]->mSrc[1].mTemp < 0 || + iblock->mInstructions[i + 1]->mSrc[1].mTemp == ins->mDst.mTemp && iblock->mInstructions[i + 1]->mSrc[1].mFinal && iblock->mInstructions[i + 1]->mSrc[0].mTemp < 0)) + { + block->SignExtendAddImmediate(iproc, ins, iblock->mInstructions[i + 1]); + i++; + } + else if (i + 1 < iblock->mInstructions.Size() && + (iproc->mCompilerOptions & COPT_OPTIMIZE_AUTO_UNROLL) && + (ins->mOperator == IA_INT2FLOAT || ins->mOperator == IA_UINT2FLOAT) && + ins->mSrc[0].IsSByte() && ins->mSrc[0].mRange.mMaxValue - ins->mSrc[0].mRange.mMinValue < 16 && + iblock->mInstructions[i + 1]->mCode == IC_BINARY_OPERATOR && + (iblock->mInstructions[i + 1]->mSrc[0].mTemp == ins->mDst.mTemp && iblock->mInstructions[i + 1]->mSrc[0].mFinal && iblock->mInstructions[i + 1]->mSrc[1].mTemp < 0 || + iblock->mInstructions[i + 1]->mSrc[1].mTemp == ins->mDst.mTemp && iblock->mInstructions[i + 1]->mSrc[1].mFinal && iblock->mInstructions[i + 1]->mSrc[0].mTemp < 0)) + { + block->BinaryFloatOperatorLookup(iproc, ins, iblock->mInstructions[i + 1]); + i++; + } + else + block->NumericConversion(iproc, this, ins); + break; + case IC_LEA: + { + bool avalid = false; + if (i + 1 < iblock->mInstructions.Size()) + { + if (iblock->mInstructions[i + 1]->mCode == IC_LOAD && iblock->mInstructions[i + 1]->mSrc[0].mTemp == ins->mDst.mTemp) + avalid = true; + else if (iblock->mInstructions[i + 1]->mCode == IC_STORE && iblock->mInstructions[i + 1]->mSrc[1].mTemp == ins->mDst.mTemp) + avalid = true; + else if (iblock->mInstructions[i + 1]->mCode == IC_COPY && (iblock->mInstructions[i + 1]->mSrc[1].mTemp == ins->mDst.mTemp || iblock->mInstructions[i + 1]->mSrc[0].mTemp == ins->mDst.mTemp)) + avalid = true; + } + +#if 1 +#if 1 + if (i + 3 < iblock->mInstructions.Size() && + ins->mSrc[1].mTemp >= 0 && ins->mSrc[0].IsUByte() && ins->mSrc[0].mTemp >= 0 && + iblock->mInstructions[i + 1]->mCode == IC_LEA && iblock->mInstructions[i + 1]->mDst.mTemp != ins->mDst.mTemp && iblock->mInstructions[i + 1]->mSrc[0].mTemp != ins->mDst.mTemp && + iblock->mInstructions[i + 1]->mSrc[0].mTemp == ins->mSrc[0].mTemp && iblock->mInstructions[i + 1]->mSrc[1].mTemp >= 0 && + iblock->mInstructions[i + 2]->mCode == IC_LOAD && iblock->mInstructions[i + 2]->mSrc[0].mTemp == iblock->mInstructions[i + 1]->mDst.mTemp && iblock->mInstructions[i + 2]->mSrc[0].mFinal && + iblock->mInstructions[i + 3]->mCode == IC_STORE && iblock->mInstructions[i + 3]->mSrc[1].mTemp == ins->mDst.mTemp && iblock->mInstructions[i + 3]->mSrc[1].mFinal && + iblock->mInstructions[i + 3]->mSrc[0].mTemp == iblock->mInstructions[i + 2]->mDst.mTemp && iblock->mInstructions[i + 3]->mSrc[0].mFinal && + iblock->mInstructions[i + 2]->mSrc[0].mStride == iblock->mInstructions[i + 3]->mSrc[1].mStride) + { + block->CopyByteIndexedValue(iproc, iblock->mInstructions[i + 1], ins, iblock->mInstructions[i + 2], iblock->mInstructions[i + 3]); + i += 3; + } + else +#endif + if (i + 1 < iblock->mInstructions.Size() && + iblock->mInstructions[i + 1]->mCode == IC_LOAD && iblock->mInstructions[i + 1]->mSrc[0].mTemp == ins->mDst.mTemp && iblock->mInstructions[i + 1]->mSrc[0].mFinal && + ins->mSrc[1].mTemp >= 0 && ins->mSrc[0].IsUByte() && ins->mSrc[0].mTemp >= 0 && + iblock->mInstructions[i + 1]->mSrc[0].mIntConst == 0 && + ins->mSrc[0].mRange.mMaxValue + (InterTypeSize[iblock->mInstructions[i + 1]->mDst.mType] - 1) * iblock->mInstructions[i + 1]->mSrc[0].mStride < 256) + { + block->LoadByteIndexedValue(iproc, ins, iblock->mInstructions[i + 1]); + i++; + } + else if (i + 1 < iblock->mInstructions.Size() && + iblock->mInstructions[i + 1]->mCode == IC_STORE && iblock->mInstructions[i + 1]->mSrc[1].mTemp == ins->mDst.mTemp && iblock->mInstructions[i + 1]->mSrc[1].mFinal && + ins->mSrc[1].mTemp >= 0 && ins->mSrc[0].IsUByte() && ins->mSrc[0].mTemp >= 0 && + iblock->mInstructions[i + 1]->mSrc[1].mIntConst == 0 && (iblock->mInstructions[i + 1]->mSrc[0].mTemp >= 0 || iblock->mInstructions[i + 1]->mSrc[0].mType <= IT_INT32) && + ins->mSrc[0].mRange.mMaxValue + (InterTypeSize[iblock->mInstructions[i + 1]->mSrc[0].mType] - 1) * iblock->mInstructions[i + 1]->mSrc[1].mStride < 256) + { + block->StoreByteIndexedValue(iproc, ins, iblock->mInstructions[i + 1]); + i++; + } + else if (i + 1 < iblock->mInstructions.Size() && + iblock->mInstructions[i + 1]->mCode == IC_STORE && iblock->mInstructions[i + 1]->mSrc[1].mTemp == ins->mDst.mTemp && iblock->mInstructions[i + 1]->mSrc[1].mFinal && + ins->mSrc[1].mTemp >= 0 && ins->mSrc[0].IsUByte() && ins->mSrc[0].mTemp >= 0 && + iblock->mInstructions[i + 1]->mSrc[1].mIntConst + ins->mSrc[0].mRange.mMaxValue + InterTypeSize[iblock->mInstructions[i + 1]->mSrc[0].mType] <= 256 && + (iblock->mInstructions[i + 1]->mSrc[0].mTemp >= 0 || iblock->mInstructions[i + 1]->mSrc[0].mType <= IT_INT32) && + (InterTypeSize[iblock->mInstructions[i + 1]->mSrc[0].mType] == 1 || iblock->mInstructions[i + 1]->mSrc[1].mStride == 1)) + { + block->StoreByteOffsetIndexedValue(iproc, ins, iblock->mInstructions[i + 1]); + i++; + } + else if (i + 1 < iblock->mInstructions.Size() && + iblock->mInstructions[i + 1]->mCode == IC_STORE && iblock->mInstructions[i + 1]->mSrc[1].mTemp == ins->mDst.mTemp && iblock->mInstructions[i + 1]->mSrc[1].mFinal && + ins->mSrc[0].IsUByte() && ins->mSrc[0].mTemp >= 0 && + ins->mSrc[1].mTemp < 0 && ins->mSrc[1].mMemory == IM_GLOBAL) + { + block->StoreAbsoluteByteIndexedValue(iproc, ins, iblock->mInstructions[i + 1]); + i++; + } + else if (i + 1 < iblock->mInstructions.Size() && + iblock->mInstructions[i + 1]->mCode == IC_LOAD && iblock->mInstructions[i + 1]->mSrc[0].mTemp == ins->mDst.mTemp && iblock->mInstructions[i + 1]->mSrc[0].mFinal && + ins->mSrc[0].IsUByte() && ins->mSrc[0].mTemp >= 0 && + ins->mSrc[1].mTemp < 0 && ins->mSrc[1].mMemory == IM_GLOBAL) + { + block->LoadAbsoluteByteIndexedValue(iproc, ins, iblock->mInstructions[i + 1]); + i++; + } + + else if (i + 2 < iblock->mInstructions.Size() && + ins->mSrc[1].mTemp < 0 && ins->mSrc[1].mMemory == IM_GLOBAL && ins->mSrc[1].mLinkerObject->mSize <= 256 && +// ins->mSrc[0].IsUByte() && + iblock->mInstructions[i + 1]->mCode == IC_LEA && iblock->mInstructions[i + 1]->mSrc[1].mTemp == ins->mDst.mTemp && iblock->mInstructions[i + 1]->mSrc[1].mFinal && +// iblock->mInstructions[i + 1]->mSrc[0].IsUByte() && + iblock->mInstructions[i + 2]->mCode == IC_LOAD && iblock->mInstructions[i + 2]->mSrc[0].mTemp == iblock->mInstructions[i + 1]->mDst.mTemp && iblock->mInstructions[i + 2]->mSrc[0].mFinal) + { + block->LoadStoreOpAbsolute2D(iproc, ins, iblock->mInstructions[i + 1], iblock->mInstructions[i + 2]); + i += 2; + } + else if (i + 2 < iblock->mInstructions.Size() && + ins->mSrc[1].mTemp < 0 && ins->mSrc[1].mMemory == IM_GLOBAL && ins->mSrc[1].mLinkerObject->mSize <= 256 && +// ins->mSrc[0].IsUByte() && + iblock->mInstructions[i + 1]->mCode == IC_LEA && iblock->mInstructions[i + 1]->mSrc[1].mTemp == ins->mDst.mTemp && iblock->mInstructions[i + 1]->mSrc[1].mFinal && +// iblock->mInstructions[i + 1]->mSrc[0].IsUByte() && + iblock->mInstructions[i + 2]->mCode == IC_STORE && iblock->mInstructions[i + 2]->mSrc[1].mTemp == iblock->mInstructions[i + 1]->mDst.mTemp && iblock->mInstructions[i + 2]->mSrc[1].mFinal) + { + block->LoadStoreOpAbsolute2D(iproc, ins, iblock->mInstructions[i + 1], iblock->mInstructions[i + 2]); + i += 2; + } + else +#endif + { + bool addrused = false; + if (i + 1 < iblock->mInstructions.Size()) + { + if (iblock->mInstructions[i + 1]->mCode == IC_TYPECAST && iblock->mInstructions[i + 1]->mSrc[0].mTemp == ins->mDst.mTemp && iblock->mInstructions[i + 1]->mSrc[0].mFinal) + addrused = true; + else if (iblock->mInstructions[i + 1]->mCode == IC_STORE && iblock->mInstructions[i + 1]->mSrc[0].mTemp == ins->mDst.mTemp && iblock->mInstructions[i + 1]->mSrc[0].mFinal) + addrused = true; + } + block->LoadEffectiveAddress(iproc, ins, nullptr, nullptr, avalid, addrused); + } + } + break; + case IC_CONSTANT: + block->LoadConstant(iproc, ins); + break; + case IC_DISPATCH: + case IC_CALL: + block->CallFunction(iproc, this, ins); + break; + case IC_CALL_NATIVE: + case IC_ASSEMBLER: + block->CallAssembler(iproc, this, ins); + break; + case IC_PUSH_FRAME: + { + block->mIns.Push(NativeCodeInstruction(ins, ASMIT_SEC, ASMIM_IMPLIED)); + block->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_STACK)); + block->mIns.Push(NativeCodeInstruction(ins, ASMIT_SBC, ASMIM_IMMEDIATE, (ins->mConst.mIntConst + 2) & 0xff)); + block->mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_STACK)); + block->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_STACK + 1)); + block->mIns.Push(NativeCodeInstruction(ins, ASMIT_SBC, ASMIM_IMMEDIATE, ((ins->mConst.mIntConst + 2) >> 8) & 0xff)); + block->mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_STACK + 1)); + } break; + case IC_POP_FRAME: + { + block->mIns.Push(NativeCodeInstruction(ins, ASMIT_CLC, ASMIM_IMPLIED)); + block->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_STACK)); + block->mIns.Push(NativeCodeInstruction(ins, ASMIT_ADC, ASMIM_IMMEDIATE, (ins->mConst.mIntConst + 2) & 0xff)); + block->mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_STACK)); + block->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_STACK + 1)); + block->mIns.Push(NativeCodeInstruction(ins, ASMIT_ADC, ASMIM_IMMEDIATE, ((ins->mConst.mIntConst + 2) >> 8) & 0xff)); + block->mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_STACK + 1)); + } break; + + case IC_RELATIONAL_OPERATOR: + if (i + 1 < iblock->mInstructions.Size() && iblock->mInstructions[i + 1]->mCode == IC_BRANCH && iblock->mInstructions[i + 1]->mSrc[0].mTemp == ins->mDst.mTemp && iblock->mInstructions[i + 1]->mSrc[0].mFinal) + { + block->RelationalOperator(iproc, ins, this, CompileBlock(iproc, iblock->mTrueJump), CompileBlock(iproc, iblock->mFalseJump)); + return; + } + else if (ins->mSrc[0].mType >= IT_INT8 && ins->mSrc[0].mType <= IT_INT32 && ins->mSrc[0].mTemp < 0 && ins->mSrc[0].mIntConst == 0 && ins->mSrc[1].IsInRange(0, 1) && ins->mOperator == IA_CMPNE) + { + block->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + iproc->mTempOffset[ins->mSrc[1].mTemp])); + block->mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + iproc->mTempOffset[ins->mDst.mTemp])); + } + else if (ins->mSrc[1].mType >= IT_INT8 && ins->mSrc[1].mType <= IT_INT32 && ins->mSrc[1].mTemp < 0 && ins->mSrc[1].mIntConst == 0 && ins->mSrc[0].IsInRange(0, 1) && ins->mOperator == IA_CMPNE) + { + block->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + iproc->mTempOffset[ins->mSrc[0].mTemp])); + block->mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + iproc->mTempOffset[ins->mDst.mTemp])); + } + else + { + NativeCodeBasicBlock* tblock, * fblock, * rblock; + + tblock = AllocateBlock(); + fblock = AllocateBlock(); + rblock = AllocateBlock(); + + block->RelationalOperator(iproc, ins, this, tblock, fblock); + + tblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 1)); + tblock->Close(ins, rblock, nullptr, ASMIT_JMP); + fblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + fblock->Close(ins, rblock, nullptr, ASMIT_JMP); + + rblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + iproc->mTempOffset[ins->mDst.mTemp])); + if (InterTypeSize[ins->mDst.mType] > 1) + { + rblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); + rblock->mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + iproc->mTempOffset[ins->mDst.mTemp] + 1)); + } + + block = rblock; + } + break; + + case IC_RETURN_VALUE: + { + if (!mExitBlock->mBranchIns) + mExitBlock->mBranchIns = ins; + + if (ins->mSrc[0].mTemp < 0) + { + if (ins->mSrc[0].mType == IT_FLOAT) + { + union { float f; unsigned int v; } cc; + cc.f = float(ins->mSrc[0].mFloatConst); + + block->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, cc.v & 0xff)); + block->mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 0)); + block->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (cc.v >> 8) & 0xff)); + block->mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 1)); + block->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (cc.v >> 16) & 0xff)); + block->mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 2)); + block->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (cc.v >> 24) & 0xff)); + block->mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 3)); + + mExitBlock->mIns[0].mFlags |= NCIF_LOWER | NCIF_UPPER | NCIF_LONG; + } + else + { + block->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, ins->mSrc[0].mIntConst & 0xff)); + block->mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 0)); + + mExitBlock->mIns[0].mFlags |= NCIF_LOWER; + + if (InterTypeSize[ins->mSrc[0].mType] > 1) + { + block->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (ins->mSrc[0].mIntConst >> 8) & 0xff)); + block->mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 1)); + + mExitBlock->mIns[0].mFlags |= NCIF_UPPER; + + if (InterTypeSize[ins->mSrc[0].mType] > 2) + { + block->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (ins->mSrc[0].mIntConst >> 16) & 0xff)); + block->mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 2)); + block->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_IMMEDIATE, (ins->mSrc[0].mIntConst >> 24) & 0xff)); + block->mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 3)); + + mExitBlock->mIns[0].mFlags |= NCIF_LONG; + } + } + } + } + else + { + block->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + iproc->mTempOffset[ins->mSrc[0].mTemp])); + block->mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU)); + + mExitBlock->mIns[0].mFlags |= NCIF_LOWER; + + if (InterTypeSize[ins->mSrc[0].mType] > 1) + { + block->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + iproc->mTempOffset[ins->mSrc[0].mTemp] + 1)); + block->mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 1)); + + mExitBlock->mIns[0].mFlags |= NCIF_UPPER; + + if (InterTypeSize[ins->mSrc[0].mType] > 2) + { + block->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + iproc->mTempOffset[ins->mSrc[0].mTemp] + 2)); + block->mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 2)); + block->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + iproc->mTempOffset[ins->mSrc[0].mTemp] + 3)); + block->mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 3)); + + mExitBlock->mIns[0].mFlags |= NCIF_LONG; + } + } + } + + block->Close(ins, mExitBlock, nullptr, ASMIT_JMP); + return; + } + + case IC_RETURN: + if (!mExitBlock->mBranchIns) + mExitBlock->mBranchIns = ins; + + block->Close(ins, mExitBlock, nullptr, ASMIT_JMP); + return; + + case IC_TYPECAST: + if (ins->mSrc[0].mTemp != ins->mDst.mTemp) + { + block->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + iproc->mTempOffset[ins->mSrc[0].mTemp])); + block->mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + iproc->mTempOffset[ins->mDst.mTemp])); + if (InterTypeSize[ins->mDst.mType] > 1) + { + block->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + iproc->mTempOffset[ins->mSrc[0].mTemp] + 1)); + block->mIns.Push(NativeCodeInstruction(ins, ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_TMP + iproc->mTempOffset[ins->mDst.mTemp] + 1)); + } + } + break; + + case IC_BRANCH: + if (ins->mSrc[0].mTemp < 0) + { + if (ins->mSrc[0].mIntConst == 0) + block->Close(ins, CompileBlock(iproc, iblock->mFalseJump), nullptr, ASMIT_JMP); + else + block->Close(ins, CompileBlock(iproc, iblock->mTrueJump), nullptr, ASMIT_JMP); + } + else + { + block->mIns.Push(NativeCodeInstruction(ins, ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_TMP + iproc->mTempOffset[ins->mSrc[0].mTemp])); + if (InterTypeSize[ins->mSrc[0].mType] > 1) + block->mIns.Push(NativeCodeInstruction(ins, ASMIT_ORA, ASMIM_ZERO_PAGE, BC_REG_TMP + iproc->mTempOffset[ins->mSrc[0].mTemp] + 1)); + if (InterTypeSize[ins->mSrc[0].mType] > 2) + { + block->mIns.Push(NativeCodeInstruction(ins, ASMIT_ORA, ASMIM_ZERO_PAGE, BC_REG_TMP + iproc->mTempOffset[ins->mSrc[0].mTemp] + 2)); + block->mIns.Push(NativeCodeInstruction(ins, ASMIT_ORA, ASMIM_ZERO_PAGE, BC_REG_TMP + iproc->mTempOffset[ins->mSrc[0].mTemp] + 3)); + } + block->Close(ins, CompileBlock(iproc, iblock->mTrueJump), CompileBlock(iproc, iblock->mFalseJump), ASMIT_BNE); + } + return; + + case IC_UNREACHABLE: + block->Close(ins, mExitBlock, nullptr, ASMIT_JMP); + return; + } + + i++; + } + + block->Close(nullptr, CompileBlock(iproc, iblock->mTrueJump), nullptr, ASMIT_JMP); +} + + +NativeCodeGenerator::NativeCodeGenerator(Errors* errors, Linker* linker, LinkerSection* runtimeSection) + : mErrors(errors), mLinker(linker), mRuntimeSection(runtimeSection), mCompilerOptions(COPT_DEFAULT), mFunctionCalls(nullptr) +{ +} + +NativeCodeGenerator::~NativeCodeGenerator(void) +{ + +} + +void NativeCodeGenerator::CompleteRuntime(void) +{ + PopulateShortMulTables(); +} + + +void NativeCodeGenerator::PopulateShortMulTables(void) +{ + for (int i = 0; i < mMulTables.Size(); i++) + { + const MulTable& m(mMulTables[i]); + if (m.mLinkerLSB->mSize != m.mSize) + { + m.mLinkerLSB->AddSpace(m.mSize); + m.mLinkerMSB->AddSpace(m.mSize); + + for (int j = 0; j < m.mSize; j++) + { + int val = m.mFactor; + switch (m.mOperator) + { + case IA_MUL: + val *= j; + break; + case IA_SHL: + val <<= j; + break; + case IA_SHR: + val = (val & 0xffff) >> j; + break; + case IA_SAR: + val = (int)(short)val >> j; + break; + } + + m.mLinkerLSB->mData[j] = (uint8)(val); + m.mLinkerMSB->mData[j] = (uint8)(val >> 8); + } + } + } + + for (int i = 0; i < mFloatTables.Size(); i++) + { + const FloatTable& f(mFloatTables[i]); + if (f.mLinker[0]->mSize != f.mMaxValue + 1 - f.mMinValue) + { + for (int j = 0; j < 4; j++) + f.mLinker[j]->AddSpace(f.mMaxValue + 1 - f.mMinValue); + } + + for (int j = f.mMinValue; j <= f.mMaxValue; j++) + { + union { + float f; + uint8 u[4]; + } fu; + + switch (f.mOperator) + { + case IA_MUL: + fu.f = f.mConst * float(j); + break; + case IA_ADD: + fu.f = f.mConst * float(j); + break; + case IA_SUB: + if (f.mReverse) + fu.f = f.mConst - float(j); + else + fu.f = float(j) - f.mConst; + break; + case IA_DIVS: + if (f.mReverse) + fu.f = f.mConst / float(j); + else + fu.f = float(j) / f.mConst; + break; + } + + for (int k = 0; k < 4; k++) + f.mLinker[k]->mData[j - f.mMinValue] = fu.u[k]; + } + } +} + +LinkerObject* NativeCodeGenerator::AllocateFloatTable(InterOperator op, bool reverse, int minval, int maxval, float fval, int index) +{ + int i = 0; + while (i < mFloatTables.Size() && + (mFloatTables[i].mOperator != op || + mFloatTables[i].mReverse != reverse || + mFloatTables[i].mConst != fval || + mFloatTables[i].mMinValue != minval)) + i++; + + if (i == mFloatTables.Size()) + { + Location loc; + FloatTable ft; + + char name[60]; + const char* base = ""; + + switch (op) + { + case IA_MUL: + base = "fmul"; + break; + case IA_ADD: + base = "fadd"; + break; + case IA_SUB: + if (reverse) + base = "frsub"; + else + base = "fsub"; + break; + case IA_DIVS: + if (reverse) + base = "frdiv"; + else + base = "fdiv"; + break; + } + + for (int i = 0; i < 4; i++) + { + sprintf_s(name, "__%stab%d_%d_%f", base, i, minval, fval); + ft.mLinker[i] = mLinker->AddObject(loc, Ident::Unique(name), mRuntimeSection, LOT_DATA); + ft.mLinker[i]->mFlags |= LOBJF_CONST; + } + + ft.mOperator = op; + ft.mReverse = reverse; + ft.mConst = fval; + ft.mMinValue = minval; + ft.mMaxValue = maxval; + + mFloatTables.Push(ft); + + return ft.mLinker[index]; + } + else + { + if (maxval > mFloatTables[i].mMaxValue) + mFloatTables[i].mMaxValue = maxval; + + return mFloatTables[i].mLinker[index]; + } +} + +LinkerObject* NativeCodeGenerator::AllocateShortMulTable(InterOperator op, int factor, int size, bool msb) +{ + assert(size > 0); + + int i = 0; + while (i < mMulTables.Size() && (mMulTables[i].mFactor != factor || mMulTables[i].mOperator != op)) + i++; + + if (i == mMulTables.Size()) + { + Location loc; + MulTable mt; + + char name[20]; + const char* base = ""; + + switch (op) + { + case IA_MUL: + base = "mul"; + break; + case IA_SHL: + base = "shl"; + break; + case IA_SHR: + base = "shr"; + break; + case IA_SAR: + base = "sar"; + break; + } + + sprintf_s(name, "__%stab%dL", base, factor); + mt.mLinkerLSB = mLinker->AddObject(loc, Ident::Unique(name), mRuntimeSection, LOT_DATA); + sprintf_s(name, "__%stab%dH", base, factor); + mt.mLinkerMSB = mLinker->AddObject(loc, Ident::Unique(name), mRuntimeSection, LOT_DATA); + + mt.mLinkerLSB->mFlags |= LOBJF_CONST; + mt.mLinkerMSB->mFlags |= LOBJF_CONST; + + mt.mFactor = factor; + mt.mSize = size; + mt.mOperator = op; + + mMulTables.Push(mt); + + return msb ? mt.mLinkerMSB : mt.mLinkerLSB; + } + else + { + if (size > mMulTables[i].mSize) + mMulTables[i].mSize = size; + + return msb ? mMulTables[i].mLinkerMSB : mMulTables[i].mLinkerLSB; + } +} + +NativeCodeGenerator::Runtime& NativeCodeGenerator::ResolveRuntime(const Ident* ident) +{ + int i = 0; + while (i < mRuntime.Size() && mRuntime[i].mIdent != ident) + i++; + Location loc; + if (i == mRuntime.Size() || !mRuntime[i].mLinkerObject) + mErrors->Error(loc, EERR_RUNTIME_CODE, "Undefined runtime function", ident->mString); + return mRuntime[i]; +} + +static inline bool isfparam(const NativeCodeInstruction & cins, const NativeCodeInstruction & ins) +{ + if (cins.mFlags & NCIF_RUNTIME) + { + if (ins.mAddress >= BC_REG_WORK && ins.mAddress < BC_REG_WORK + 8) + return true; + if (ins.mAddress >= BC_REG_ACCU && ins.mAddress < BC_REG_ACCU + 4) + return true; + } + else + { + if (ins.mAddress >= BC_REG_FPARAMS && ins.mAddress < BC_REG_FPARAMS_END) + return true; + } + return false; +} + +void NativeCodeGenerator::RegisterFunctionCall(NativeCodeBasicBlock* block, int at) +{ + LinkerObject* lo = block->mIns[at].mLinkerObject; + if (lo->mIdent)// && !(block->mIns[at].mFlags & NCIF_USE_ZP_32_X)) + { + uint64 fpmask = 0; + + int i = at; + while (i >= 2 && + block->mIns[i - 1].mType == ASMIT_STA && block->mIns[i - 1].mMode == ASMIM_ZERO_PAGE && + isfparam(block->mIns[at], block->mIns[i - 1]) && + block->mIns[i - 2].mType == ASMIT_LDA && (block->mIns[i - 2].mMode == ASMIM_IMMEDIATE || block->mIns[i - 2].mMode == ASMIM_IMMEDIATE_ADDRESS || block->mIns[i - 2].mMode == ASMIM_ZERO_PAGE || block->mIns[i - 2].mMode == ASMIM_ABSOLUTE)) + { + if (block->mIns[i - 2].mMode == ASMIM_ZERO_PAGE && block->mIns[i - 2].mAddress >= BC_REG_FPARAMS && block->mIns[i - 2].mAddress < BC_REG_FPARAMS_END) + { + if (fpmask & (1ull << (block->mIns[i - 2].mAddress - BC_REG_WORK))) + break; + } + fpmask |= 1ull << (block->mIns[i - 1].mAddress - BC_REG_WORK); + + i -= 2; + } + + + if (i < at) + { + // Check conflicting local branch + if (i > 0 && block->mIns[i - 1].mMode == ASMIM_RELATIVE && block->mIns[i - 1].mAddress > 0) + return; + + FunctionCall* ncp = new FunctionCall(); + ncp->mLinkerObject = lo; + ncp->mOffset = block->mIns[at].mAddress; + + ncp->mProxyObject = nullptr; + ncp->mCount = 1; + while (i < at) + { + ncp->mIns[block->mIns[i + 1].mAddress - BC_REG_WORK] = block->mIns[i]; + i += 2; + } + + FunctionCall* cp = mFunctionCalls; + while (cp && (cp->mLinkerObject != ncp->mLinkerObject || cp->mOffset != ncp->mOffset)) + cp = cp->mNext; + if (!cp) + { + ncp->mNext = mFunctionCalls; + mFunctionCalls = ncp; + ncp->mSame = nullptr; + } + else + { + FunctionCall* scp = cp; + while (scp && !scp->IsSame(ncp)) + scp = scp->mSame; + if (!scp) + { + ncp->mSame = cp->mSame; + cp->mSame = ncp; + } + else + { + scp->mCount++; + ncp = scp; + } + } + } + } + +} + +bool NativeCodeGenerator::MergeFunctionCall(NativeCodeBasicBlock* block, int at) +{ + LinkerObject* lo = block->mIns[at].mLinkerObject; + if (lo->mIdent) + { + uint64 fpmask = 0; + + int i = at; + while (i >= 2 && + block->mIns[i - 1].mType == ASMIT_STA && block->mIns[i - 1].mMode == ASMIM_ZERO_PAGE && + isfparam(block->mIns[at], block->mIns[i - 1]) && + block->mIns[i - 2].mType == ASMIT_LDA && (block->mIns[i - 2].mMode == ASMIM_IMMEDIATE || block->mIns[i - 2].mMode == ASMIM_IMMEDIATE_ADDRESS || block->mIns[i - 2].mMode == ASMIM_ZERO_PAGE || block->mIns[i - 2].mMode == ASMIM_ABSOLUTE)) + { + if (block->mIns[i - 2].mMode == ASMIM_ZERO_PAGE && block->mIns[i - 2].mAddress >= BC_REG_FPARAMS && block->mIns[i - 2].mAddress < BC_REG_FPARAMS_END) + { + if (fpmask & (1ull << (block->mIns[i - 2].mAddress - BC_REG_WORK))) + break; + } + fpmask |= 1ull << (block->mIns[i - 1].mAddress - BC_REG_WORK); + + i -= 2; + } + + if (i < at) + { + // Check conflicting local branch + if (i > 0 && block->mIns[i - 1].mMode == ASMIM_RELATIVE && block->mIns[i - 1].mAddress > 0) + return false; + + FunctionCall ncp; + ncp.mLinkerObject = lo; + ncp.mOffset = block->mIns[at].mAddress; + int j = i; + while (i < at) + { + ncp.mIns[block->mIns[i + 1].mAddress - BC_REG_WORK] = block->mIns[i]; + i += 2; + } + + FunctionCall* cp = mFunctionCalls; + while (cp && (cp->mLinkerObject != ncp.mLinkerObject || cp->mOffset != ncp.mOffset)) + cp = cp->mNext; + if (cp) + { + FunctionCall* bcp = nullptr; + int bmatch = 0; + + FunctionCall* scp = cp; + while (scp) + { + if (scp->mProxyObject) + { + int m = ncp.Matches(scp); + if (m > bmatch) + { + bmatch = m; + bcp = scp; + } + } + scp = scp->mSame; + } + + if (bcp) + { + while (j < at) + { + if (bcp->mIns[block->mIns[j + 1].mAddress - BC_REG_WORK].mType != ASMIT_INV) + { + block->mIns[j + 0].mType = ASMIT_NOP; block->mIns[j + 0].mMode = ASMIM_IMPLIED; + block->mIns[j + 1].mType = ASMIT_NOP; block->mIns[j + 1].mMode = ASMIM_IMPLIED; + } + j += 2; + } + block->mIns[j].mLinkerObject = bcp->mProxyObject; + block->mIns[j].mAddress = 0; + return true; + } + } + } + } + return false; +} + +void NativeCodeGenerator::OutlineFunctions(void) +{ + NativeCodeMapper mapper; + + bool progress; + + int k = 0; + + int numOutlines = 0; + do { + progress = false; + + SuffixTree* tree = new SuffixTree(nullptr, 0, nullptr); + + for (int i = 0; i < mProcedures.Size(); i++) + { + if ((mProcedures[i]->mCompilerOptions & COPT_OPTIMIZE_OUTLINE)) + mProcedures[i]->AddToSuffixTree(mapper, tree); + } + tree->AddParents(nullptr); + +#if 0 + FILE* f; + + if (!fopen_s(&f, "r:\\suffix.txt", "w")) + { + tree->Print(f, mapper, 0); + fclose(f); + } +#endif + + SuffixTree* ltree = nullptr; + int lsize = 6; + + tree->LongestMatch(mapper, 0, 0, lsize, ltree); + if (lsize > 6) + { + ExpandingArray segs; + ltree->ReplaceCalls(mapper, segs); + + NativeCodeBasicBlock* block = segs[0].mBlock; + + NativeCodeProcedure* nproc = new NativeCodeProcedure(this); + + NativeCodeBasicBlock* nblock = nproc->AllocateBlock(); + +// printf("Suffix %s,%d:%s\n", block->mProc->mIdent->mString, block->mIndex, block->mProc->mLinkerObject->mSection->mIdent->mString); + + nproc->mLocation = block->mIns[0].mIns ? block->mIns[0].mIns->mLocation : block->mProc->mLocation; + nproc->mCompilerOptions = block->mProc->mCompilerOptions; + nproc->mIdent = Ident::Unique("$outline", numOutlines); + nproc->mLinkerObject = mLinker->AddObject(nproc->mLocation, nproc->mIdent, block->mProc->mLinkerObject->mSection, LOT_NATIVE_CODE); + nproc->mEntryBlock = nblock; + nproc->mInterProc = nullptr; + + bool dojmp = false; + + ltree->ParentCollect(mapper, nblock); + if (nblock->mIns[nblock->mIns.Size() - 1].mType == ASMIT_JSR) + nblock->mIns[nblock->mIns.Size() - 1].mType = ASMIT_JMP; + else if (nblock->mIns[nblock->mIns.Size() - 1].mType == ASMIT_RTS || nblock->mIns[nblock->mIns.Size() - 1].mType == ASMIT_JMP) + dojmp = true; + else + nblock->mIns.Push(NativeCodeInstruction(nblock->mIns[nblock->mIns.Size() - 1].mIns, ASMIT_RTS)); + + segs.Sort([](const SuffixSegment& l, const SuffixSegment& r)->bool { + return l.mBlock == r.mBlock ? l.mStart > r.mStart : ptrdiff_t(l.mBlock) < ptrdiff_t(r.mBlock); + }); + + bool mergeBlocks = false; + + // Check for complete loop block replacement + bool trueLoop = false, falseLoop = false; + + int k = 0; + while (k < segs.Size() && segs[k].mStart == 0 && segs[k].mEnd == segs[k].mBlock->mIns.Size() && segs[k].mBlock->mTrueJump == segs[k].mBlock && segs[k].mBlock->mBranch == segs[0].mBlock->mBranch) + k++; + + if (k == segs.Size()) + trueLoop = true; + else if (k == 0) + { + while (k < segs.Size() && segs[k].mStart == 0 && segs[k].mEnd == segs[k].mBlock->mIns.Size() && segs[k].mBlock->mFalseJump == segs[k].mBlock && segs[k].mBlock->mBranch == segs[0].mBlock->mBranch) + k++; + if (k == segs.Size()) + falseLoop = true; + } + + if (trueLoop || falseLoop) + { + NativeCodeBasicBlock* eblock = nproc->AllocateBlock(); + + if (trueLoop) + { + nblock->mTrueJump = nblock; + nblock->mFalseJump = eblock; + } + else + { + nblock->mTrueJump = eblock; + nblock->mFalseJump = nblock; + } + nblock->mBranch = segs[0].mBlock->mBranch; + + for (int i = 0; i < segs.Size(); i++) + { + SuffixSegment& s(segs[i]); + if (trueLoop) + segs[i].mBlock->mTrueJump = segs[i].mBlock->mFalseJump; + segs[i].mBlock->mFalseJump = nullptr; + segs[i].mBlock->mBranch = ASMIT_JMP; + segs[i].mBlock->mNumEntries = 1; + } + + eblock->mIns.Push(NativeCodeInstruction(nblock->mIns[nblock->mIns.Size() - 1].mIns, ASMIT_RTS)); + if (nblock->mIns[nblock->mIns.Size() - 1].mType == ASMIT_JMP) + nblock->mIns[nblock->mIns.Size() - 1].mType = ASMIT_JSR; + else + nblock->mIns.Remove(nblock->mIns.Size() - 1); + + mergeBlocks = true; + } + else + { + bool falseDetour = false, trueDetour = false; + + int k = 0; + while (k < segs.Size() && + segs[k].mEnd == segs[k].mBlock->mIns.Size() && + segs[k].mBlock->mFalseJump && + !segs[k].mBlock->mFalseJump->mFalseJump && + segs[k].mBlock->mFalseJump->mIns.Size() == 1 && + segs[k].mBlock->mFalseJump->mTrueJump == segs[k].mBlock->mTrueJump && + segs[k].mBlock->mBranch == segs[0].mBlock->mBranch && + segs[k].mBlock->mIns[0].IsSame(segs[0].mBlock->mIns[0])) + k++; + + if (k == segs.Size()) + { + falseDetour = true; + } + + if (trueDetour || falseDetour) + { + NativeCodeBasicBlock* dblock = nproc->AllocateBlock(); + NativeCodeBasicBlock* eblock = nproc->AllocateBlock(); + + dblock->Close(segs[0].mBlock->mBranchIns, eblock, nullptr, ASMIT_JMP); + if (trueDetour) + { + dblock->mIns.Push(segs[0].mBlock->mTrueJump->mIns[0]); + nblock->Close(segs[0].mBlock->mBranchIns, dblock, eblock, segs[0].mBlock->mBranch); + } + else + { + dblock->mIns.Push(segs[0].mBlock->mFalseJump->mIns[0]); + nblock->Close(segs[0].mBlock->mBranchIns, eblock, dblock, segs[0].mBlock->mBranch); + } + + for (int i = 0; i < segs.Size(); i++) + { + SuffixSegment& s(segs[i]); + if (trueDetour) + segs[i].mBlock->mTrueJump = segs[i].mBlock->mFalseJump; + segs[i].mBlock->mFalseJump = nullptr; + segs[i].mBlock->mBranch = ASMIT_JMP; + segs[i].mBlock->mTrueJump->mNumEntries--; + } + + eblock->mIns.Push(NativeCodeInstruction(nblock->mIns[nblock->mIns.Size() - 1].mIns, ASMIT_RTS)); + if (nblock->mIns[nblock->mIns.Size() - 1].mType == ASMIT_JMP) + nblock->mIns[nblock->mIns.Size() - 1].mType = ASMIT_JSR; + else + nblock->mIns.Remove(nblock->mIns.Size() - 1); + + mergeBlocks = true; + } + } + + NativeCodeBasicBlock* pblock = nullptr; + int pstart; + for (int i = 0; i < segs.Size(); i++) + { + SuffixSegment& s(segs[i]); +// printf("Seg %s,%d\n", segs[i].mBlock->mProc->mIdent->mString, segs[i].mBlock->mIndex); + if (s.mBlock != pblock || s.mEnd <= pstart) + { + s.mBlock->mIns.Remove(s.mStart + 1, s.mEnd - s.mStart - 1); + s.mBlock->mIns[s.mStart] = NativeCodeInstruction(s.mBlock->mIns[s.mStart].mIns, dojmp ? ASMIT_JMP : ASMIT_JSR, ASMIM_ABSOLUTE, 0, nproc->mLinkerObject); + pblock = s.mBlock; + pstart = s.mStart; + } + } + + mProcedures.Push(nproc); + + if (mergeBlocks) + { + ExpandingArray procs; + + for (int i = 0; i < segs.Size(); i++) + { + SuffixSegment& s(segs[i]); + NativeCodeProcedure* p = s.mBlock->mProc; + if (!procs.Contains(p)) + { + procs.Push(p); + p->ResetVisited(); + p->mEntryBlock->MergeBasicBlocks(); + } + } + } + + numOutlines++; + progress = true; + } +#if 0 + if (!fopen_s(&f, "r:\\lsuffix.txt", "w")) + { + ltree->ParentPrint(f, mapper); + fclose(f); + } +#endif + delete tree; + mapper.Reset(); + +#if 0 + k++; + if (k == 2) + break; +#endif + } while (progress); +} + +void NativeCodeGenerator::BuildFunctionProxies(void) +{ + FunctionCall* cp = mFunctionCalls; + while (cp) + { + ExpandingArray ocalls; + + FunctionCall* ncp = cp; + while (ncp) + { + if (ncp->mCount == 1) + ocalls.Push(ncp); + ncp = ncp->mSame; + } + + // Loop while we have unmatched calls + while (ocalls.Size() > 1) + { + int i = 0; + while (i < ocalls.Size()) + { + int j = 0; + while (j < ocalls.Size() && (i == j || ocalls[i]->PotentialMatches(ocalls[j]) == 0)) + j++; + if (j == ocalls.Size()) + ocalls.Remove(i); + else + i++; + } + + if (ocalls.Size() > 1) + { + // Find best matching pair + int besti = 0, bestj = 0, bestm = 0; + for (int i = 0; i < ocalls.Size(); i++) + { + for (int j = i + 1; j < ocalls.Size(); j++) + { + int m = ocalls[i]->PotentialMatches(ocalls[j]); + if (m > bestm) + { + besti = i; + bestj = j; + bestm = m; + } + } + } + + FunctionCall* fi = ocalls[besti], * fj = ocalls[bestj]; + + ocalls.Remove(besti); + + for (int i = BC_REG_WORK; i < BC_REG_ACCU + 4; i++) + { + if (fi->mIns[i - BC_REG_WORK].mType != ASMIT_INV) + { + if (!fi->mIns[i - BC_REG_WORK].IsSame(fj->mIns[i - BC_REG_WORK])) + { + fi->mIns[i - BC_REG_WORK].mType = ASMIT_INV; + } + } + } + + int i = 0; + while (i < ocalls.Size()) + { + if (ocalls[i]->Matches(fi) > 0) + { + ocalls.Remove(i); + fi->mCount++; + } + else + i++; + } + } + } + + ncp = cp; + while (ncp) + { + if (ncp->mCount > 1) + { +// printf("RFC %s : %d\n", ncp->mLinkerObject->mIdent->mString, ncp->mCount); + ncp->mProxyObject = mLinker->AddObject(ncp->mLinkerObject->mLocation, ncp->mLinkerObject->mIdent->Mangle("@proxy"), ncp->mLinkerObject->mSection, ncp->mLinkerObject->mType); + + ExpandingArray code; + for (int i = BC_REG_WORK; i < BC_REG_ACCU + 4; i++) + { + NativeCodeInstruction& ins(ncp->mIns[i - BC_REG_WORK]); + if (ins.mType == ASMIT_LDA) + { + switch (ins.mMode) + { + case ASMIM_IMMEDIATE: + code.Push(uint8(AsmInsOpcodes[ASMIT_LDA][ASMIM_IMMEDIATE])); + code.Push(uint8(ins.mAddress)); + break; + case ASMIM_ZERO_PAGE: + code.Push(uint8(AsmInsOpcodes[ASMIT_LDA][ASMIM_ZERO_PAGE])); + if (ins.mLinkerObject) + { + LinkerReference rl; + rl.mObject = ncp->mProxyObject; + rl.mOffset = code.Size(); + + rl.mRefObject = ins.mLinkerObject; + rl.mRefOffset = ins.mAddress; + rl.mFlags = LREF_LOWBYTE; + + ncp->mProxyObject->AddReference(rl); + code.Push(0); + } + else + code.Push(uint8(ins.mAddress)); + break; + case ASMIM_IMMEDIATE_ADDRESS: + code.Push(uint8(AsmInsOpcodes[ASMIT_LDA][ASMIM_IMMEDIATE])); + if (ins.mLinkerObject) + { + LinkerReference rl; + rl.mObject = ncp->mProxyObject; + rl.mOffset = code.Size(); + rl.mFlags = 0; + if (ins.mFlags & NCIF_LOWER) + rl.mFlags |= LREF_LOWBYTE; + if (ins.mFlags & NCIF_UPPER) + rl.mFlags |= LREF_HIGHBYTE; + rl.mRefObject = ins.mLinkerObject; + rl.mRefOffset = ins.mAddress; + + ins.mLinkerObject->mFlags |= LOBJF_NO_CROSS; + + ncp->mProxyObject->AddReference(rl); + code.Push(0); + } + else + code.Push(uint8(ins.mAddress)); + break; + case ASMIM_ABSOLUTE: + if (ins.mLinkerObject && (ins.mLinkerObject->mFlags & LOBJF_ZEROPAGE)) + { + code.Push(uint8(AsmInsOpcodes[ASMIT_LDA][ASMIM_ZERO_PAGE])); + LinkerReference rl; + rl.mObject = ncp->mProxyObject; + rl.mOffset = code.Size(); + + rl.mRefObject = ins.mLinkerObject; + rl.mRefOffset = ins.mAddress; + rl.mFlags = LREF_LOWBYTE; + + ncp->mProxyObject->AddReference(rl); + code.Push(0); + } + else + { + code.Push(uint8(AsmInsOpcodes[ASMIT_LDA][ASMIM_ABSOLUTE])); + if (ins.mLinkerObject) + { + LinkerReference rl; + rl.mObject = ncp->mProxyObject; + rl.mOffset = code.Size(); + rl.mRefObject = ins.mLinkerObject; + rl.mRefOffset = ins.mAddress; + rl.mFlags = LREF_LOWBYTE | LREF_HIGHBYTE; + ncp->mProxyObject->AddReference(rl); + code.Push(0); + code.Push(0); + } + else + { + code.Push(uint8(ins.mAddress & 0xff)); + code.Push(uint8(ins.mAddress >> 8)); + } + } + break; + } + code.Push(uint8(AsmInsOpcodes[ASMIT_STA][ASMIM_ZERO_PAGE])); + code.Push(uint8(i)); + } + } + code.Push(uint8(AsmInsOpcodes[ASMIT_JMP][ASMIM_ABSOLUTE])); + + LinkerReference rl; + rl.mObject = ncp->mProxyObject; + rl.mOffset = code.Size(); + rl.mRefObject = ncp->mLinkerObject; + rl.mRefOffset = ncp->mOffset; + rl.mFlags = LREF_LOWBYTE | LREF_HIGHBYTE; + ncp->mProxyObject->AddReference(rl); + code.Push(0); + code.Push(0); + + ncp->mProxyObject->AddData(&(code[0]), code.Size()); + } + ncp = ncp->mSame; + } + cp = cp->mNext; + } +} + + +void NativeCodeGenerator::RegisterRuntime(const Ident* ident, LinkerObject* object, int offset) +{ + Runtime rt; + rt.mIdent = ident; + rt.mLinkerObject = object; + rt.mOffset = offset; + mRuntime.Push(rt); +} + +bool NativeCodeGenerator::FunctionCall::IsSame(const FunctionCall* fc) const +{ + for (int i = BC_REG_WORK; i < BC_REG_ACCU + 4; i++) + if (!mIns[i - BC_REG_WORK].IsSame(fc->mIns[i - BC_REG_WORK])) + return false; + return true; +} + +int NativeCodeGenerator::FunctionCall::Matches(const FunctionCall* fc) const +{ + int match = 0; + + for (int i = BC_REG_WORK; i < BC_REG_ACCU + 4; i++) + { + if (fc->mIns[i - BC_REG_WORK].mType != ASMIT_INV) + { + if (!mIns[i - BC_REG_WORK].IsSame(fc->mIns[i - BC_REG_WORK])) + return -1; + match++; + } + } + + return match; +} + +int NativeCodeGenerator::FunctionCall::PotentialMatches(const FunctionCall* fc) const +{ + int match = 0; + + for (int i = BC_REG_WORK; i < BC_REG_ACCU + 4; i++) + { + if (fc->mIns[i - BC_REG_WORK].mType != ASMIT_INV) + { + if (mIns[i - BC_REG_WORK].IsSame(fc->mIns[i - BC_REG_WORK])) + match++; + } + } + + return match; +} + diff --git a/oscar64/oscar64/NativeCodeGenerator.h b/oscar64/oscar64/NativeCodeGenerator.h new file mode 100644 index 0000000..2912ec5 --- /dev/null +++ b/oscar64/oscar64/NativeCodeGenerator.h @@ -0,0 +1,1197 @@ +#pragma once + +#include "Assembler.h" +#include "Linker.h" +#include "InterCode.h" + +class NativeCodeProcedure; +class NativeCodeBasicBlock; +class NativeCodeGenerator; +class NativeCodeInstruction; + +class NativeCodeMapper; +class SuffixTree; + +enum NativeRegisterDataMode : uint8 +{ + NRDM_UNKNOWN, + NRDM_IMMEDIATE, + NRDM_IMMEDIATE_ADDRESS, + NRDM_ZERO_PAGE, + NRDM_ABSOLUTE, + NRDM_ABSOLUTE_X, + NRDM_ABSOLUTE_Y, + NRDM_INDIRECT_Y +}; + +struct NativeRegisterData +{ + LinkerObject * mLinkerObject; + int mValue, mUnique; + uint32 mFlags; + NativeRegisterDataMode mMode; + uint8 mMinVal, mMaxVal, mMask; + + NativeRegisterData(void); + + void Set(NativeRegisterDataMode mode, int value = 0, LinkerObject * lo = nullptr, int flags = 0, int minv = 0, int maxv = 255); + void SetMask(int mask, int value); + void Reset(void); + void ResetMask(void); + void ResetAliasing(void); + + bool SameData(const NativeRegisterData& d) const; + bool SameData(const NativeCodeInstruction& ins) const; +}; + +struct NativeRegisterDataSet +{ + const NativeRegisterData& operator[](int reg) const; + + void Set(int reg, NativeRegisterDataMode mode, int value = 0, LinkerObject* lo = nullptr, int flags = 0, int minv = 0, int maxv = 255); + void Set(int reg, const NativeRegisterData& data); + void SetMask(int reg, int mask, int value); + void SetMask(int reg, const NativeRegisterData& data); + void Reset(int reg); + void ResetMask(int reg); + void ResetAliasing(int reg); + + void Reset(void); + void ResetMask(void); + void ResetCall(const NativeCodeInstruction & ins, int fastCallBase); + + void ResetZeroPage(int addr); + void ResetZeroPageRange(int addr, int num); + void ResetAbsolute(LinkerObject * linkerObject, int addr); + void ResetAbsoluteXY(LinkerObject* linkerObject, int addr); + int FindAbsolute(LinkerObject* linkerObject, int addr); + void ResetIndirect(int reg); + void ResetGlobal(void); + void ResetX(void); + void ResetY(void); + void ResetWorkRegs(void); + void ResetWorkMasks(void); + void Intersect(const NativeRegisterDataSet& set); + void IntersectMask(const NativeRegisterDataSet& set); + void ResetAliasing(void); + +protected: + NativeRegisterData mRegs[261]; +}; + +struct ValueNumberingData +{ + uint32 mIndex, mOffset; + + ValueNumberingData(void); + + void Reset(void); + bool SameBase(const ValueNumberingData& d) const; +}; + +struct ValueNumberingDataSet +{ + ValueNumberingData mRegs[261]; + + int Find(const ValueNumberingData & data) const; + + void Reset(void); + void ResetWorkRegs(void); + void ResetCall(const NativeCodeInstruction& ins); + + void Intersect(const ValueNumberingDataSet& set); +}; + +struct ValueReuseData +{ + NativeCodeBasicBlock * mBlock; + int mIndex; + + ValueReuseData(void); + void Reset(void); +}; + +struct ValueReuseDataSet +{ + ValueReuseData mRegs[256]; + + void Reset(void); + void ResetWorkRegs(void); + void ResetCall(const NativeCodeInstruction& ins); + + void Intersect(const ValueReuseDataSet& set); +}; + + + +struct NativeRegisterSum16Info +{ + NativeCodeInstruction * mSrcL, * mSrcH, * mDstL, * mDstH, * mAddL, * mAddH; + + bool mImmediate; + int mAddress; + LinkerObject * mLinkerObject; + + bool operator==(const NativeRegisterSum16Info& ri) const; + bool operator!=(const NativeRegisterSum16Info& ri) const; +}; + +struct NativeSimpleSubExpression +{ + AsmInsType mType; + AsmInsMode mMode; + int mValue; + NativeCodeInstruction * mSrc, * mDst, * mOp; + int mIndex; + + bool MayBeChangedBy(const NativeCodeInstruction& ins); +}; + +struct NativeSimpleSubExpressions +{ + ExpandingArray mExps; + + int FindCommon(const NativeSimpleSubExpression& ex) const; + void Filter(const NativeCodeInstruction& ins); +}; + +struct SelfModReference +{ + NativeCodeBasicBlock * mBlock; + int mOffset; +}; + + +static const uint32 NCIF_LOWER = 0x00000001; +static const uint32 NCIF_UPPER = 0x00000002; +static const uint32 NCIF_RUNTIME = 0x00000004; +static const uint32 NCIF_YZERO = 0x00000008; +static const uint32 NCIF_VOLATILE = 0x00000010; +static const uint32 NCIF_LONG = 0x00000020; +static const uint32 NCIF_FEXEC = 0x00000040; +static const uint32 NCIF_JSRFLAGS = 0x00000080; +static const uint32 NICT_INDEXFLIPPED = 0x00000100; +static const uint32 NICT_ZPFLIPPED = 0x00000200; +static const uint32 NICF_TMPREF = 0x00000400; +static const uint32 NCIF_ALIASING = 0x00000800; + +static const uint32 NCIF_USE_CPU_REG_A = 0x00001000; +static const uint32 NCIF_USE_CPU_REG_X = 0x00002000; +static const uint32 NCIF_USE_CPU_REG_Y = 0x00004000; +static const uint32 NCIF_USE_CPU_REG_C = 0x00008000; + +static const uint32 NCIF_PROVIDE_CPU_REG_A = 0x00010000; +static const uint32 NCIF_PROVIDE_CPU_REG_X = 0x00020000; +static const uint32 NCIF_PROVIDE_CPU_REG_Y = 0x00040000; +static const uint32 NCIF_PROVIDE_CPU_REG_C = 0x00080000; + +static const uint32 NCIF_PRESERVE_CPU_REG_A = 0x00100000; +static const uint32 NCIF_PRESERVE_CPU_REG_X = 0x00200000; +static const uint32 NCIF_PRESERVE_CPU_REG_Y = 0x00400000; +static const uint32 NCIF_BREAKPOINT = 0x00800000; + +// use a 32bit zero page register indexed by X for JSR +static const uint32 NCIF_USE_ZP_32_X = 0x01000000; +static const uint32 NICF_USE_ZP_ADDR = 0x02000000; +static const uint32 NICF_USE_WORKREGS = 0x04000000; + +static const uint32 NCIF_MEMMAP = 0x08000000; +static const uint32 NCIF_SELFMOD = 0x10000000; + +static const uint32 NCIF_IMMADDR_FLAGS = NCIF_LOWER | NCIF_UPPER; + +class NativeCodeInstruction +{ +public: + NativeCodeInstruction(void); + NativeCodeInstruction(const InterInstruction * ins, AsmInsType type, AsmInsMode mode = ASMIM_IMPLIED, int64 address = 0, LinkerObject * linkerObject = nullptr, uint32 flags = NCIF_LOWER | NCIF_UPPER, int param = 0, int minv = 0, int maxv = 255); + NativeCodeInstruction(const InterInstruction* ins, AsmInsType type, const NativeCodeInstruction & addr); + + LinkerObject * mLinkerObject; + const InterInstruction * mIns; + + int mAddress; + uint32 mFlags; + + AsmInsType mType; + AsmInsMode mMode; + uint8 mMinVal, mMaxVal; + uint8 mParam, mLive; + + void Disassemble(FILE* file) const; + void DisassembleAddress(FILE* file) const; + + void CopyMode(const NativeCodeInstruction& ins); + void CopyModeAndRange(const NativeCodeInstruction& ins); + + void Assemble(NativeCodeBasicBlock* block); + void FilterRegUsage(NumberSet& requiredTemps, NumberSet& providedTemps); + bool IsUsedResultInstructions(NumberSet& requiredTemps); + bool BitFieldForwarding(NativeRegisterDataSet& data, AsmInsType& carryop); + bool ValueForwarding(NativeRegisterDataSet& data, AsmInsType & carryop, bool initial, bool final, int fastCallBase); + + void Simulate(NativeRegisterDataSet& data); + bool ApplySimulation(const NativeRegisterDataSet& data); + + bool LoadsAccu(void) const; + bool ChangesAccuAndFlag(void) const; + bool ChangesFlagToAccu(void) const; + bool ChangesAddress(void) const; + bool UsesAddress(void) const; + bool ChangesAccu(void) const; + bool UsesAccu(void) const; + bool ChangesCarry(void) const; + bool ChangesZFlag(void) const; + bool RequiresCarry(void) const; + bool RequiresAccu(void) const; + + bool RequiresYReg(void) const; + bool RequiresXReg(void) const; + + bool ChangesYReg(void) const; + bool ChangesXReg(void) const; + + bool ReferencesCarry(void) const; + bool ReferencesAccu(void) const; + bool ReferencesYReg(void) const; + bool ReferencesXReg(void) const; + + bool ChangesZeroPage(int address) const; + bool UsesZeroPage(int address) const; + bool LoadsZeroPage(int address) const; + bool ReferencesZeroPage(int address) const; + + bool SameLinkerObjectVariableRange(const NativeCodeInstruction& ins, bool sameXY = false) const; + + bool IsPure(void) const; + bool ChangesGlobalMemory(void) const; + bool ReferencesGlobalMemory(void) const; + bool UsesMemoryOf(const NativeCodeInstruction& ins) const; + bool SameEffectiveAddress(const NativeCodeInstruction& ins) const; + bool MayBeChangedOnAddress(const NativeCodeInstruction& ins, bool sameXY = false) const; + bool MayReference(const NativeCodeInstruction& ins, bool sameXY = false) const; + bool MayBeSameAddress(const NativeCodeInstruction& ins, bool sameXY = false) const; + bool IsSame(const NativeCodeInstruction& ins) const; + bool IsSameLS(const NativeCodeInstruction& ins) const; + bool IsCommutative(void) const; + bool IsStore(void) const; + bool IsLoad(void) const; + bool IsLogic(void) const; + bool IsArithmetic(void) const; + bool IsAddSub(void) const; + bool IsShift(void) const; + bool IsShiftOrInc(void) const; + bool IsShiftRotOrInc(void) const; + bool IsSimpleJSR(void) const; + bool MayBeMovedBefore(const NativeCodeInstruction& ins) const; + bool MayBeMovedBehind(const NativeCodeInstruction& ins) const; + + bool ReplaceYRegWithXReg(void); + bool ReplaceXRegWithYReg(void); + + bool CanSwapXYReg(void); + bool SwapXYReg(void); + + void BuildCollisionTable(NumberSet& liveTemps, NumberSet* collisionSets); + + uint32 NeedsLive(void) const; + + uint32 CodeHash(void) const; + bool CodeSame(const NativeCodeInstruction& ins); + +protected: + const char* AddrName(char* buffer) const; +}; + +struct NativeCodeLoadStorePair +{ + NativeCodeInstruction mLoad, mStore; +}; + + +class NativeCodeBasicBlock +{ +public: + NativeCodeBasicBlock(NativeCodeProcedure * proc); + ~NativeCodeBasicBlock(void); + + NativeCodeProcedure * mProc; + ExpandingArray mCode; + ExpandingArray mCodeLocations, mCodeOrigins; + + int mIndex; + + NativeCodeBasicBlock* mTrueJump, * mFalseJump, * mFromJump; + AsmInsType mBranch; + const InterInstruction* mBranchIns; + + ExpandingArray mIns; + ExpandingArray mRelocations; + + ExpandingArray mEntryBlocks; + + int mOffset, mSize, mPlace, mNumEntries, mNumEntered, mFrameOffset, mTemp; + bool mPlaced, mCopied, mKnownShortBranch, mBypassed, mAssembled, mNoFrame, mVisited, mLoopHead, mVisiting, mLocked, mPatched, mPatchFail, mPatchChecked, mPatchUsed, mPatchStart, mPatchLoop, mPatchLoopChanged, mPatchExit; + bool mEntryRegA, mEntryRegX, mEntryRegY, mExitRegA, mExitRegX, mChecked; + NativeCodeBasicBlock * mDominator, * mSameBlock; + + int mAsmFromJump; + + NativeCodeBasicBlock* mLoopHeadBlock, * mLoopTailBlock; + + NativeRegisterDataSet mDataSet, mNDataSet, mFDataSet; + ValueNumberingDataSet mNumDataSet, mNNumDataSet, mFNumDataSet; + ValueReuseDataSet mReuseDataSet, mNReuseDataSet, mFReuseDataSet; + + int mYAlias[256]; + int mYReg, mYOffset, mYValue, mXReg, mXOffset, mXValue; + bool mSameAX, mSameAY; + + ExpandingArray mRSumInfos; + + NativeCodeInstruction mALSIns, mXLSIns, mYLSIns; + + void Disassemble(FILE* file); + void DisassembleBody(FILE* file); + + NativeCodeInstruction DecodeNative(const InterInstruction* ins, LinkerObject * lobj, int& offset) const; + + int PutBranch(NativeCodeProcedure* proc, NativeCodeBasicBlock* target, AsmInsType code, int from, int to); + int PutJump(NativeCodeProcedure* proc, NativeCodeBasicBlock* target, int from, int to, AsmInsType code = ASMIT_INV); + int JumpByteSize(NativeCodeBasicBlock * target, int from, int to, bool second, bool final); + int CheckFinalBranchByteSize(NativeCodeBasicBlock* target, int from, int to) const; + int BranchByteSize(NativeCodeBasicBlock* target, int from, int to, bool final) const; + + NativeCodeBasicBlock* SplitAt(int at); + + NativeCodeBasicBlock* BypassEmptyBlocks(void); + + int LeadsInto(NativeCodeBasicBlock* block, int dist); + NativeCodeBasicBlock* PlaceSequence(ExpandingArray& placement, NativeCodeBasicBlock* block); + void BuildPlacement(ExpandingArray& placement); + void OptimizePlacement(void); + void InitialOffset(int& total); + bool CalculateOffset(int& total, bool final); + + void CopyCode(NativeCodeProcedure* proc, uint8* target); + void Assemble(void); + void Close(const InterInstruction * ins, NativeCodeBasicBlock* trueJump, NativeCodeBasicBlock* falseJump, AsmInsType branch); + + void PrependInstruction(const NativeCodeInstruction& ins); + + void ShortcutTailRecursion(); + void ShortcutJump(int offset); + + bool ReferencesAccu(int from = 0, int to = 65536) const; + bool ReferencesYReg(int from = 0, int to = 65536) const; + bool ReferencesXReg(int from = 0, int to = 65536) const; + + bool ChangesAccu(int from = 0, int to = 65536) const; + bool ChangesYReg(int from = 0, int to = 65536) const; + bool ChangesXReg(int from = 0, int to = 65536) const; + bool ChangesCarry(int from = 0, int to = 65536) const; + + bool ChangesZeroPage(int address, int from = 0, int to = 65536) const; + bool UsesZeroPage(int address, int from = 0, int to = 65536) const; + bool LoadsZeroPage(int address, int from = 0, int to = 65536) const; + bool ReferencesZeroPage(int address, int from = 0, int to = 65536) const; + + bool ChangesMemory(const NativeCodeInstruction& ins, int from = 0, int to = 65536) const; + bool ReferencesMemory(const NativeCodeInstruction& ins, int from = 0, int to = 65536) const; + + bool RemoveNops(void); + bool PeepHoleOptimizer(int pass); + + bool PeepHoleOptimizerShuffle(int pass); + bool PeepHoleOptimizerIterate1(int i, int pass); + bool PeepHoleOptimizerIterate2(int i, int pass); + bool PeepHoleOptimizerIterate3(int i, int pass); + bool PeepHoleOptimizerIterate4(int i, int pass); + bool PeepHoleOptimizerIterate5(int i, int pass); + bool PeepHoleOptimizerIterate4b(int i, int pass); + bool PeepHoleOptimizerIterate5b(int i, int pass); + bool PeepHoleOptimizerIterate6(int i, int pass); + bool PeepHoleOptimizerIterateN(int i, int pass); + + bool PeepHoleOptimizerIterate(int pass); + bool PeepHoleOptimizerExits(int pass); + + void BlockSizeReduction(NativeCodeProcedure* proc, int xenter, int yenter, int center); + bool BlockSizeCopyReduction(NativeCodeProcedure* proc, int & si, int & di); + void BranchAccuAlternativeElimination(void); + + bool OptimizeSimpleYIndexedLoop(NativeCodeBasicBlock* prevBlock, NativeCodeBasicBlock* exitBlock); + bool OptimizeSimpleLoopInvariant(NativeCodeProcedure* proc, bool full); + bool OptimizeSimpleLoopInvariant(NativeCodeProcedure* proc, NativeCodeBasicBlock * prevBlock, NativeCodeBasicBlock* exitBlock, bool full); + bool RemoveSimpleLoopUnusedIndex(void); + bool OptimizeLoopCarryOver(void); + bool OptimizeLoopRegisterWrapAround(void); + + bool OptimizeSingleEntryLoopInvariant(NativeCodeProcedure* proc, NativeCodeBasicBlock* prev, const ExpandingArray& tails, const ExpandingArray& blocks); + bool OptimizeSingleEntryLoop(NativeCodeProcedure* proc); + + bool OptimizeSimpleLoop(NativeCodeProcedure* proc, bool full); + bool OptimizeSimpleForLoop(void); + bool OptimizeSimpleFixLoop(void); + bool SimpleLoopReversal(NativeCodeProcedure* proc); + bool OptimizeInnerLoop(NativeCodeProcedure* proc, NativeCodeBasicBlock* head, NativeCodeBasicBlock* tail, ExpandingArray& blocks); + bool OptimizeXYSimpleLoop(void); + bool SimplifyLoopExitCondition(void); + + bool OptimizeXYSpilling(void); + + bool OptimizeSelect(NativeCodeProcedure* proc); + + bool OptimizeInnerLoops(NativeCodeProcedure* proc); + NativeCodeBasicBlock* CollectInnerLoop(NativeCodeBasicBlock* head, ExpandingArray& lblocks); + + int CorrectXOffset(const InterInstruction * ins, int yoffset, int at); + int CorrectYOffset(const InterInstruction * ins, int yoffset, int at); + bool OptimizeGenericLoop(void); + bool CollectGenericLoop(ExpandingArray& lblocks); + bool CollectSingleEntryGenericLoop(ExpandingArray& lblocks); + void CollectReachable(ExpandingArray& lblock); + + bool OptimizeFindLoop(NativeCodeProcedure* proc); + + NativeCodeBasicBlock* BuildSingleEntry(NativeCodeProcedure* proc, NativeCodeBasicBlock* block); + NativeCodeBasicBlock* BuildSingleExit(NativeCodeProcedure* proc, NativeCodeBasicBlock* block); + + void PutLocation(const Location& loc, bool weak); + void PutOpcode(short opcode); + void PutByte(uint8 code); + void PutWord(uint16 code); + void PutSelfModSource(int id); + + void CheckFrameIndex(const InterInstruction * ins, int & reg, int & index, int size, int treg = 0); + void LoadValueToReg(InterCodeProcedure* proc, const InterInstruction * ins, int reg, const NativeCodeInstruction * ainsl, const NativeCodeInstruction* ainsh); + void LoadConstantToReg(InterCodeProcedure* proc, const InterInstruction* ins, InterType type, int reg); + void LoadConstantToReg(InterCodeProcedure* proc, const InterInstruction* ins, const InterOperand & op, InterType type, int reg, bool checkRange = true); + + void LoadConstant(InterCodeProcedure* proc, const InterInstruction * ins); + void StoreValue(InterCodeProcedure* proc, const InterInstruction * ins); + void LoadValue(InterCodeProcedure* proc, const InterInstruction * ins); + void LoadStoreValue(InterCodeProcedure* proc, const InterInstruction * rins, const InterInstruction * wins); + void LoadStoreIndirectPair(InterCodeProcedure* proc, const InterInstruction* wins0, const InterInstruction* wins1); + bool LoadOpStoreIndirectValue(InterCodeProcedure* proc, const InterInstruction* rins, const InterInstruction* oins, int oindex, const InterInstruction* wins); + bool LoadUnopStoreIndirectValue(InterCodeProcedure* proc, const InterInstruction* rins, const InterInstruction* oins, const InterInstruction* wins); + bool LoadLoadOpStoreIndirectValue(InterCodeProcedure* proc, const InterInstruction* rins1, const InterInstruction* rins0, const InterInstruction* oins, const InterInstruction* wins); + void LoadStoreIndirectValue(InterCodeProcedure* proc, const InterInstruction* rins, const InterInstruction* wins); + void StoreByteOffsetIndexedValue(InterCodeProcedure* proc, const InterInstruction* iins, const InterInstruction* sins); + void StoreAbsoluteByteIndexedValue(InterCodeProcedure* proc, const InterInstruction* iins, const InterInstruction* wins); + void LoadAbsoluteByteIndexedValue(InterCodeProcedure* proc, const InterInstruction* iins, const InterInstruction* rins); + + NativeCodeBasicBlock* BinaryOperator(InterCodeProcedure* proc, NativeCodeProcedure* nproc, const InterInstruction * ins, const InterInstruction* sins1, const InterInstruction* sins0); + void UnaryOperator(InterCodeProcedure* proc, NativeCodeProcedure* nproc, const InterInstruction * ins); + void RelationalOperator(InterCodeProcedure* proc, const InterInstruction * ins, NativeCodeProcedure * nproc, NativeCodeBasicBlock* trueJump, NativeCodeBasicBlock * falseJump); + void LoadEffectiveAddress(InterCodeProcedure* proc, const InterInstruction * ins, const InterInstruction* sins1, const InterInstruction* sins0, bool addrvalid, bool addrused = false); + void LoadStoreOpAbsolute2D(InterCodeProcedure* proc, const InterInstruction* lins1, const InterInstruction* lins2, const InterInstruction* mins); + void SignExtendAddImmediate(InterCodeProcedure* proc, const InterInstruction* xins, const InterInstruction* ains); + void BinaryDivModPair(InterCodeProcedure* proc, NativeCodeProcedure* nproc, const InterInstruction* ins1, const InterInstruction* ins2, bool sign); + void BinaryFloatOperatorLookup(InterCodeProcedure* proc, const InterInstruction* cins, const InterInstruction* ins); + void BinaryAndMulConst(InterCodeProcedure* proc, const InterInstruction* ains, const InterInstruction* mins); + void BinaryAddCarry(InterCodeProcedure* proc, const InterInstruction* ains, const InterInstruction* mins); + void BinaryShlAddConstPair(InterCodeProcedure* proc, const InterInstruction* sins, const InterInstruction* ains); + + void NumericConversion(InterCodeProcedure* proc, NativeCodeProcedure* nproc, const InterInstruction * ins); + NativeCodeBasicBlock * FillValue(InterCodeProcedure* proc, const InterInstruction* ins, NativeCodeProcedure* nproc); + NativeCodeBasicBlock * CopyValue(InterCodeProcedure* proc, const InterInstruction * ins, NativeCodeProcedure* nproc); + NativeCodeBasicBlock * StrcpyValue(InterCodeProcedure* proc, const InterInstruction* ins, NativeCodeProcedure* nproc); + void AddAsrSignedByte(InterCodeProcedure* proc, const InterInstruction* ains, const InterInstruction* sins); + + void CallMalloc(InterCodeProcedure* proc, const InterInstruction* ins, NativeCodeProcedure* nproc); + void CallFree(InterCodeProcedure* proc, const InterInstruction* ins, NativeCodeProcedure* nproc); + + void LoadByteIndexedValue(InterCodeProcedure* proc, const InterInstruction* iins, const InterInstruction* rins); + void StoreByteIndexedValue(InterCodeProcedure* proc, const InterInstruction* iins, const InterInstruction* rins); + void CopyByteIndexedValue(InterCodeProcedure* proc, const InterInstruction* riins, const InterInstruction* wiins, const InterInstruction* rins, const InterInstruction* wins); + + void CallAssembler(InterCodeProcedure* proc, NativeCodeProcedure* nproc, const InterInstruction * ins); + void CallFunction(InterCodeProcedure* proc, NativeCodeProcedure* nproc, const InterInstruction * ins); + + void ShiftRegisterRight( const InterInstruction* ins, int reg, int shift); + void ShiftRegisterLeft(InterCodeProcedure* proc, const InterInstruction* ins, int reg, int shift); + void ShiftRegisterLeftByte(InterCodeProcedure* proc, const InterInstruction* ins, int reg, int shift); + void ShiftRegisterLeftFromByte(InterCodeProcedure* proc, const InterInstruction* ins, int reg, int shift, int max); + int ShortMultiply(InterCodeProcedure* proc, NativeCodeProcedure* nproc, const InterInstruction * ins, const InterInstruction* sins, int index, int mul); + int ShortSignedDivide(InterCodeProcedure* proc, NativeCodeProcedure* nproc, const InterInstruction* ins, const InterInstruction* sins, int mul); + + bool CheckPredAccuStore(int reg); + bool CheckIsInAccu(int reg); + + NumberSet mLocalRequiredRegs, mLocalProvidedRegs; + NumberSet mEntryRequiredRegs, mEntryProvidedRegs; + NumberSet mExitRequiredRegs, mExitProvidedRegs; + NumberSet mNewRequiredRegs; + NumberSet mTempRegs; + + void BuildLocalRegSets(void); + void BuildGlobalProvidedRegSet(NumberSet fromProvidedTemps); + bool BuildGlobalRequiredRegSet(NumberSet& fromRequiredTemps); + bool RemoveUnusedResultInstructions(void); + + void BuildCollisionTable(NumberSet* collisionSets); + + bool IsSame(const NativeCodeBasicBlock* block) const; + bool FindSameBlocks(NativeCodeProcedure* nproc); + bool MergeSameBlocks(NativeCodeProcedure* nproc); + + void CountEntries(NativeCodeBasicBlock* fromJump); + NativeCodeBasicBlock * ForwardAccuBranch(bool eq, bool ne, bool pl, bool mi, int limit); + bool MergeBasicBlocks(void); + bool RemoveJumpToBranch(void); + NativeCodeBasicBlock* SplitBlock(int at); + NativeCodeBasicBlock* CloneBlock(void); + NativeCodeBasicBlock* PrefixLoopBlock(NativeCodeBasicBlock * prefix); + NativeCodeBasicBlock* UniqueEntryBlock(NativeCodeBasicBlock* prefix); + bool ShortcutBranchSequence(void); + + bool RemoveUnusedBitOps(void); + + struct DominatorStacks + { + ExpandingArray< NativeCodeBasicBlock* > d1, d2; + }; + + void BuildDominatorTree(NativeCodeBasicBlock * from, DominatorStacks & stacks); + + bool MoveLoadStoreUp(int at); + bool MoveLoadStoreXUp(int at); + bool MoveLoadImmStoreAbsoluteUp(int at); + + bool MoveIndirectLoadStoreDown(int at); + bool MoveIndirectLoadStoreDownY(int at); + + bool MoveLDXUp(int at); + bool MoveLDYUp(int at); + + bool MoveLDXBeforeZ(int at); + + bool MoveIndirectLoadStoreUp(int at); + bool MoveAbsoluteLoadStoreUp(int at); + bool MoveGenericLoadStoreUp(int at); + bool MoveLoadStoreOutOfXYRangeUp(int at); + bool MoveLoadIndirectTempStoreUp(int at); + bool MoveLoadIndirectBypassYUp(int at); + + bool MoveLoadAddImmStoreAbsXUp(int at); + bool MoveStaTaxLdaStaDown(int at); + + bool MoveLoadAddImmStoreUp(int at); + bool MoveLoadEorImmStoreUp(int at); + bool MoveCLCLoadAddZPStoreUp(int at); + bool MoveLoadAddZPStoreUp(int at); + bool MoveLoadShiftRotateUp(int at); + bool MoveLoadShiftStoreUp(int at); + bool MoveTYADCStoreDown(int at); + bool MoveShiftZeroPageUp(int at); + bool MoveImmOpBeforeStore(int at); + bool MoveLoadOrZPUp(int at); + bool MoveLoadLogicStoreAbsUp(int at); + + bool MoveLoadAddStoreDown(int at); + + bool MoveLDSTXOutOfRange(int at); + + bool MoveZpImmTAYDown(int at); + bool MoveCLCLoadAddZPStoreDown(int at); + bool FindDirectAddressSumY(int at, int reg, int& apos, int& breg); + bool PatchDirectAddressSumY(int at, int reg, int apos, int breg); + bool FindAddressSumY(int at, int reg, int & apos, int& breg, int& ireg); + bool PatchAddressSumY(int at, int reg, int apos, int breg, int ireg); + + bool FindLoadAddressSumY(int at, int reg, int& apos, int& ireg); + bool PatchLoadAddressSumY(int at, int reg, int apos, int ireg); + + bool FindGlobalAddress(int at, int reg, int& apos); + bool FindGlobalAddressSumY(int at, int reg, bool direct, int& apos, const NativeCodeInstruction * & ains, const NativeCodeInstruction*& iins, uint32 & flags, int & addr); + bool FindExternAddressSumY(int at, int reg, int& breg, int& ireg); + bool FindSharedGlobalAddressSumY(int at, int reg, const NativeCodeInstruction*& ains, const NativeCodeInstruction*& iins); + bool FindPageStartAddress(int at, int reg, int& addr); + bool FindBypassAddressSumY(int at, int reg, int& apos, int& breg); + bool PatchBypassAddressSumY(int at, int reg, int apos, int breg); + bool FindAbsoluteAddressSumY(int at, int reg, int& apos, int &offset); + bool PatchAbsoluteAddressSumY(int at, int reg, int apos); + bool MoveStoreXUp(int at); + bool MoveLoadXUp(int at); + bool MoveStoreYUp(int at); + bool MoveLoadYUp(int at); + bool MoveStoreHighByteDown(int at); + bool MoveAddHighByteDown(int at); + bool ReverseLoadCommutativeOpUp(int aload, int aop); + bool ReplaceZeroPageUp(int at); + bool ReplaceZeroPageDown(int at); + bool ReplaceYRegWithXReg(int start, int end); + bool ReplaceXRegWithYReg(int start, int end); + bool MoveASLMemUp(int start); + bool CombineImmediateADCUp(int at); + bool CombineImmediateADCUpX(int at); + bool MoveTXADCDown(int at); + bool MoveTXALogicTAXDown(int at); + bool FoldShiftORAIntoLoadImmUp(int at); + bool ReverseShiftByteOrder(int at); + bool MoveZeroPageSignToCarryDown(int at); + bool Move16BitShiftUp(int at); + bool Move16BitShiftDown(int at); + bool Move16BitAddUp(int at); + + bool FindAccuExitValue(int& at); + bool MoveLoadXAbsUpCrossBlock(int at); + + bool MoveSimpleADCToINCDECDown(int at); + bool MoveTAXADCSTADown(int at); + + bool MoveLDAopTAXDown(int at); + + bool MoveZeroPageCrossBlockUp(int at, const NativeCodeInstruction & lins, const NativeCodeInstruction & sins); + bool ShortcutCrossBlockMoves(NativeCodeProcedure* proc); + bool ShortcutCrossBlockCondition(void); + + bool MergeDuplicateCondition(void); + + bool CanReplaceYRegWithXReg(int start, int end); + bool CanReplaceXRegWithYReg(int start, int end); + + bool ForwardAccuAddSub(void); + bool ForwardZpYIndex(bool full); + bool ForwardZpXIndex(bool full); + bool ForwardAXYReg(void); + + // Join sequences of TXA, CLC, ADC #xx into INX, TXA sequences if possible + bool JoinXYCascade(void); + + bool GlobalLoadStoreForwarding(bool zpage, const NativeCodeInstruction & als, const NativeCodeInstruction & xls, const NativeCodeInstruction & yls); + + bool RegisterValueForwarding(void); + bool CanCombineSameXtoY(int start, int end); + bool CanCombineSameYtoX(int start, int end); + bool CombineSameXY(void); + bool CombineSameXtoY(int xpos, int ypos, int end); + bool CombineSameYtoX(int xpos, int ypos, int end); + + bool FindImmediateStore(int at, int reg, const NativeCodeInstruction*& ains); + int FindImmediateGlobalStore(int at, const NativeCodeInstruction& ins); + + bool JoinXYCrossBlock(void); + bool CanCombineSameXtoYCrossBlock(int from); + bool CanCombineSameYtoXCrossBlock(int from); + bool CombineSameXtoYCrossBlock(int from); + bool CombineSameYtoXCrossBlock(int from); + + bool JoinTAXARange(int from, int to); + bool JoinTAYARange(int from, int to); + bool JoinTAXARangeLive(int from, int to); + bool JoinTAYARangeLive(int from, int to); + bool PatchGlobalAdressSumYByX(int at, int reg, const NativeCodeInstruction& ains, int addr); + bool MergeXYSameValue(int from); + void RepairLoadYImmediate(int at, int val); + int RetrieveCValue(int at, int depth = 0) const; + int RetrieveAValue(int at, int depth = 0) const; + int RetrieveXValue(int at = 65536, int depth = 0) const; + int RetrieveYValue(int at = 65536, int depth = 0) const; + int RetrieveZPValue(int reg, int at, int depth = 0) const; + int FindFreeAccu(int at) const; + bool IsSameZPValue(int reg1, int reg2, int depth = 0) const; + + bool ReverseReplaceTAX(int at); + + bool CanReplaceExitAccuWithX(NativeCodeBasicBlock * target); + bool CanReplaceExitAccuWithY(NativeCodeBasicBlock* target); + void ReplaceExitAccuWithX(NativeCodeBasicBlock* target); + void ReplaceExitAccuWithY(NativeCodeBasicBlock* target); + + bool ReverseLoadAccuToRegXY(void); + + void ResetModifiedDataSet(NativeRegisterDataSet& data); + + bool CrossBlockShiftForwarding(const ValueReuseDataSet & data); + + bool ValueForwarding(NativeCodeProcedure* proc, const NativeRegisterDataSet& data, bool global, bool final); + bool GlobalValueForwarding(NativeCodeProcedure* proc, bool final); + bool BitFieldForwarding(const NativeRegisterDataSet& data); + bool ReverseBitfieldForwarding(void); + bool OffsetValueForwarding(const ValueNumberingDataSet & data); + bool AbsoluteValueForwarding(const ExpandingArray& npairs); + bool IndexXYValueForwarding(int xreg, int xoffset, int xvalue, int yreg, int yoffset, int yvalue); + bool ReduceIndexXYZeroShuffle(NativeCodeBasicBlock* from, int xreg, int yreg); + bool CheckLoopIndexXRegisters(NativeCodeBasicBlock* head, int xreg); + bool CheckLoopIndexYRegisters(NativeCodeBasicBlock* head, int yreg); + bool AbsoluteLocalRegisterValueReuse(void); + + void MarkLocalUsedLinkerObjects(void); + bool RemoveLocalUnusedLinkerObjects(void); + + void CollectEntryBlocks(NativeCodeBasicBlock* block); + + void AddEntryBlock(NativeCodeBasicBlock* block); + void RemEntryBlock(NativeCodeBasicBlock* block); + + NativeCodeBasicBlock * SplitMatchingTails(NativeCodeProcedure* proc); + + NativeCodeBasicBlock* AddDominatorBlock(NativeCodeProcedure* proc, NativeCodeBasicBlock* pblock); + bool JoinTailCodeSequences(bool loops); + bool SameTail(const NativeCodeInstruction& ins) const; + bool HasTailSTA(int& addr, int& index) const; + bool HasTailSTX(int& addr, int& index) const; + bool HasTailSTY(int& addr, int& index) const; + bool HasTailSTAX16(int& addr, int& index0) const; + + bool HasTailSTAInto(int& addr, int& index, NativeCodeBasicBlock* tblock) const; + bool HasTailSTXInto(int& addr, int& index, NativeCodeBasicBlock* tblock) const; + bool HasTailSTYInto(int& addr, int& index, NativeCodeBasicBlock* tblock) const; + + bool HasTailSTAGlobal(NativeCodeInstruction & ins, int& index) const; + bool HasTailAccuReg(int addr) const; + + bool CanMoveStoreUp(int addr, const NativeCodeInstruction& ins, int& at); + bool CanMovePairBefore(int at); + bool JoinCommonHeadSequence(void); + + bool MayBeMovedBeforeBlock(int at); + bool MayBeMovedBeforeBlock(int at, const NativeCodeInstruction & ins); + bool MayBeMovedBeforeBlock(int start, int end); + bool SafeInjectSequenceFromBack(NativeCodeBasicBlock* block, int start, int end); + bool JoinCommonBranchCodeSequences(void); + + bool JoinConditionSequence(void); + + bool CanJoinEntryLoadStoreZP(int saddr, int daddr); + bool DoJoinEntryLoadStoreZP(int saddr, int daddr); + bool JoinEntryLoadStoreZP(void); + + bool IsExitYRegZP(int addr, int& index, NativeCodeBasicBlock * & block); + bool IsExitXRegZP(int addr, int& index, NativeCodeBasicBlock * & block); + bool IsExitARegZP(int addr, int& index, NativeCodeBasicBlock * & block); + bool IsARegZP(int at, int addr, int& index, NativeCodeBasicBlock*& block); + + void MarkLiveBlockChain(int index, NativeCodeBasicBlock* block, uint32 live, uint32 reg); + + bool ShortcutBlockExit(void); + bool PropagateSinglePath(void); + bool SplitSinglePath(void); + bool ShortcutORACascade(void); + + bool CanChangeTailZPStoreToX(int addr, const NativeCodeBasicBlock * nblock, const NativeCodeBasicBlock* fblock = nullptr) const; + void ChangeTailZPStoreToX(int addr); + bool CanChangeTailZPStoreToY(int addr, const NativeCodeBasicBlock* nblock, const NativeCodeBasicBlock* fblock = nullptr) const; + void ChangeTailZPStoreToY(int addr); + + bool CanCrossBlockAShortcut(int addr); + void DoCrossBlockAShortcut(int addr); + + bool CanCrossBlockXShortcut(int addr); + void DoCrossBlockXShortcut(int addr); + + bool CanCrossBlockYShortcut(int addr); + void DoCrossBlockYShortcut(int addr); + + bool CrossBlockXYShortcut(void); + void BypassAccuLoadStoreXY(void); + + bool CrossBlockYAliasProgpagation(const int * yalias); + + bool CrossBlockRegisterAlias(bool sameAX, bool sameAY); + + bool BypassRegisterConditionBlock(void); + bool FoldLoopEntry(void); + bool CombineAlternateLoads(void); + + bool Is16BitImmSum(int at, int & val, int& reg) const; + + bool Check16BitSum(int at, NativeRegisterSum16Info& info); + bool Propagate16BitSum(const ExpandingArray& cinfo); + + bool Propagate16BitHighSum(void); + + bool Check16BitSum(const NativeCodeBasicBlock* block, int origin, int at, int reg); + bool EliminateUpper16BitSum(NativeCodeProcedure* nproc); + bool Simplify16BitSum8BitRange(void); + + struct CodeRange + { + NativeCodeBasicBlock * mBlock; + int mStart, mEnd; + }; + + bool BackwardFindLiveRange(const NativeCodeBasicBlock* block, int reg, bool pair, ExpandingArray& ranges); + bool ForwardFindLiveRange(const NativeCodeBasicBlock* block, int at, int reg, bool pair, ExpandingArray & ranges); + bool CanReplaceRegInLiveRange(const ExpandingArray& range, int reg, int with, bool pair); + bool ReplaceRegInLiveRange(const ExpandingArray& range, int reg, int with, bool pair); + + bool IsFinalZeroPageUse(const NativeCodeBasicBlock* block, int at, int from, int to, bool pair, bool fchanged); + bool IsFinalZeroPageUseTail(const NativeCodeBasicBlock* block, int from, int to, bool pair); + bool ReplaceFinalZeroPageUse(NativeCodeProcedure* nproc); + bool ForwardReplaceZeroPage(int at, int from, int to, bool pair); + + bool CanZeroPageCopyUp(int at, int from, int to, bool diamond); + bool ShortcutZeroPageCopyUp(NativeCodeProcedure* nproc); + bool BackwardReplaceZeroPage(int at, int from, int to, bool diamond); + + bool IsSameRegisterSource(const NativeCodeInstruction& rins, int nloops = 0) const; + bool PartialBackwardValuePropagation(void); + bool BackwardValuePropagation(void); + bool HoistCommonLoads(void); + + bool FindInitialLoadA(NativeCodeInstruction *& ins); + bool FindInitialLoadX(NativeCodeInstruction *& ins); + bool FindInitialLoadY(NativeCodeInstruction *& ins); + + NativeRegisterDataSet mEntryRegisterDataSet; + + void BuildEntryDataSet(const NativeRegisterDataSet& set); + bool ApplyEntryDataSet(void); + + bool CollectZeroPageSet(ZeroPageSet& locals, ZeroPageSet& global, bool ignorefcall); + void CollectZeroPageUsage(NumberSet& used, NumberSet& modified, NumberSet& pairs); + void FindZeroPageAlias(const NumberSet& statics, NumberSet& invalid, uint8* alias, int accu); + bool RemapZeroPage(const uint8* remap); + + void CheckGlobalAliasing(bool& globalStores, bool& globalLoads); + + bool LoopRegisterXYMap(void); + void GlobalRegisterXYCheck(int* xregs, int * yregs); + void GlobalRegisterXMap(int reg); + void GlobalRegisterYMap(int reg); + bool LocalRegisterXYMap(void); + bool ReduceLocalYPressure(void); + bool ReduceLocalXPressure(void); + bool LocalZeroPageValueNumbering(void); + + bool CanReverseCrossBlockChangeAToY(const NativeCodeBasicBlock * block, int depth = 0); + void ReverseCrossBlockChangeAToY(void); + bool CanReverseCrossBlockChangeAToX(const NativeCodeBasicBlock* block, int depth = 0); + void ReverseCrossBlockChangeAToX(void); + + bool ReverseCrossBlockRegisterUnification(void); + bool ReverseZeroPageRecycling(void); + + bool CombineZPPair(int at, int r0, int r1, bool use0, bool use1, bool & swap); + bool RemoveDoubleZPStore(void); + + bool ExpandADCToBranch(NativeCodeProcedure* proc); + bool ExpandADCShortCascadeToBranch(void); + bool Expand16BitLoopBranch(void); + bool SimpleInlineCalls(void); + + bool CompressSwitchCascade(int lower, int upper, int cmp, AsmInsType branch); + + bool Split16BitLoopCount(NativeCodeProcedure* proc); + bool SimplifyDiamond(NativeCodeProcedure* proc); + bool SimplifyLoopEnd(NativeCodeProcedure* proc); + bool CrossBlockStoreLoadBypass(NativeCodeProcedure* proc); + bool EliminateDeadLoops(void); + bool LoopRegisterWrapAround(void); + bool CrossBlockFlagsForwarding(void); + bool MoveStoresBeforeDiamond(void); + bool MoveStoresBehindCondition(void); + bool JoinDiamondArithmetic(void); + + bool SinglePathRegisterForwardY(NativeCodeBasicBlock* path, int yreg); + bool SinglePathRegisterForward(void); + bool SinglePathStoreForward(void); + + bool CrossBlockRegZPForward(int areg, int xreg, int yreg); + + bool CanBytepassLoad(const NativeCodeInstruction& ains, int from = 0) const; + bool CanHoistStore(const NativeCodeInstruction& ains) const; + + bool MoveAccuTrainUp(int at, int end); + bool MoveAccuTrainsUp(void); + bool MoveAccuTrainsDown(void); + bool MoveAccuTrainDown(int end, int start); + + bool MoveImmediateStoreUp(int at); + bool MoveImmediateStoreDown(int at); + bool RecycleImmediates(void); + bool MoveLoadStoreDown(int at); + bool RecycleLoadStore(void); + + void BuildUseChangeSets(int start, int end, unsigned & used, unsigned & changed, uint32 & flags); + bool CanExchangeSegments(int start, int mid, int end); + bool CanSwapInstructions(int at); + + bool CrossBlockXYPreservation(void); + bool CrossBlockIncToZeroShortcut(void); + bool EliminateMicroBlocks(void); + + bool AlternateXYUsage(void); + bool OptimizeXYPairUsage(void); + bool CanGlobalSwapXY(void); + bool GlobalSwapXY(void); + bool LocalSwapXY(void); + bool UntangleXYUsage(bool final); + bool AlternateXXUsage(void); + bool ShortSwapXY(void); + + bool IsSimpleSubExpression(int at, NativeSimpleSubExpression & ex); + bool PropagateCommonSubExpression(void); + + bool PropagateCrossBlockAccuExpression(void); + void CollectAccuDominatedBlocks(ExpandingArray& dblocks); + + bool CanForwardZPMove(int saddr, int daddr, int & index) const; + bool CanForwardLoadStore(const NativeCodeInstruction & lins, const NativeCodeInstruction & sins, int& index) const; + bool Is16BitAddSubImmediate(int at, int& sreg, int &dreg, int& offset) const; + bool CanForward16BitAddSubImmediate(int sreg, int dreg, int offset, int & index) const; + + bool CheckShortcutPointerAddForward(int at); + bool ShortcutPointerAddForward(void); + + bool ShortcutIndirectLoadStore(void); + bool MoveIndirectLoadZeroStoreDown(int at); + bool MoveLoadZeroStoreIndirectUp(int at); + + bool SortIndirectStoreDown(int at); + bool SortIndirectStores(void); + + bool CommonSubExpressionElimination(void); + + bool CheckPatchFailReg(const NativeCodeBasicBlock* block, int reg); + bool CheckPatchFailRegPair(const NativeCodeBasicBlock* block, int reg); + bool CheckPatchFailUse(void); + + bool CheckPatchFailLoop(const NativeCodeBasicBlock* block, const NativeCodeBasicBlock* head, int reg, bool changed); + bool CheckPatchFailLoopPair(const NativeCodeBasicBlock* block, const NativeCodeBasicBlock* head, int reg, bool changed); + + bool JoinSameBranch(NativeCodeBasicBlock* block); + bool MergeSameBranch(void); + + bool FinalCheckedSizeReduction(void); + + bool CheckBoolBitPropagation(const NativeCodeBasicBlock* block, int at, int reg); + bool PatchBoolBitPropagation(const NativeCodeBasicBlock* block, int at, int reg, bool inverse); + + bool CollectRegBoolInstructionsForward(int reg, ExpandingArray& cblocks, ExpandingArray& lins); + bool CollectRegBoolInstructionsBackward(int reg, ExpandingArray& cblocks, ExpandingArray& lins); + + bool CollectCheckRegOriginBlocks(int at, int reg, ExpandingArray& lblocks, ExpandingArray& lins); + bool PatchBitBoolConstOrigin(void); + + // + // reg : base register pair to replace + // index: index register + // at : start position in block + // yval: known y immediate value of -1 if not known + // lobj: linker object addressed + // address: offset into linker object + bool CheckGlobalAddressSumYPointer(const NativeCodeBasicBlock * block, int reg, int index, int at, int yval); + bool PatchGlobalAddressSumYPointer(const NativeCodeBasicBlock* block, int reg, int index, int at, int yval, LinkerObject * lobj, int address, uint32 flags = NCIF_LOWER | NCIF_UPPER); + + // reg : register to replace + // at : start position in block + // ains : instruction loading original data + // cycles : max number of cycles saving + bool CheckSingleUseGlobalLoad(const NativeCodeBasicBlock* block, int reg, int at, const NativeCodeInstruction& ains, int cycles); + bool PatchSingleUseGlobalLoad(const NativeCodeBasicBlock* block, int reg, int at, const NativeCodeInstruction& ains); + + // rins : instruction storing the data + // at : start position in block + // ains : instruction loading original data + // cycles : max number of cycles saving + bool CheckSingleUseGlobalLoadStruct(const NativeCodeBasicBlock* block, const NativeCodeInstruction& rins, int at, const NativeCodeInstruction& ains, bool cleared, bool poisoned); + bool PatchSingleUseGlobalLoadStruct(const NativeCodeBasicBlock* block, const NativeCodeInstruction& rins, int at, const NativeCodeInstruction& ains); + + // reg : base register pair to replace + // base: new base register + // iins : indexing instruction + // at : start position in block + // yval: known y immediate value of -1 if not known + bool CheckForwardSumYPointer(const NativeCodeBasicBlock* block, int reg, int base, const NativeCodeInstruction & iins, int at, int yval, int ymax); + bool PatchForwardSumYPointer(const NativeCodeBasicBlock* block, int reg, int base, const NativeCodeInstruction & iins, int at, int yval); + + // reg : base register pair to replace LSB with zero + // ireg : index register + // at : start position in block + // yval: known y immediate value of -1 if not known + bool CheckForwardLowYPointer(const NativeCodeBasicBlock* block, int reg, int yreg, int at, int yval); + bool PatchForwardLowYPointer(const NativeCodeBasicBlock* block, int reg, int yreg, int at, int yval); + + bool CrossBlock16BitFlood(NativeCodeProcedure* proc); + bool CheckCrossBlock16BitFlood(const NativeCodeBasicBlock* block, int sreg, int dreg, int at, bool rvalid); + bool CheckCrossBlock16BitFloodExit(const NativeCodeBasicBlock* block, int sreg, int dreg, bool rvalid); + bool PatchCrossBlock16BitFlood(const NativeCodeBasicBlock* block, int sreg, int dreg, int at); + bool PatchCrossBlock16BitFloodExit(const NativeCodeBasicBlock* block, int sreg, int dreg); + + bool CrossBlockXYFlood(NativeCodeProcedure * proc); + + bool CheckCrossBlockXFlood(const NativeCodeBasicBlock* block, int reg, int at, bool rvalid); + bool CheckCrossBlockXFloodExit(const NativeCodeBasicBlock* block, int reg, bool rvalid); + bool PatchCrossBlockXFlood(const NativeCodeBasicBlock* block, int reg, int at); + bool PatchCrossBlockXFloodExit(const NativeCodeBasicBlock* block, int reg); + + bool CheckCrossBlockYFlood(const NativeCodeBasicBlock* block, int reg, int at, bool rvalid); + bool CheckCrossBlockYFloodExit(const NativeCodeBasicBlock* block, int reg, bool rvalid); + bool PatchCrossBlockYFlood(const NativeCodeBasicBlock* block, int reg, int at); + bool PatchCrossBlockYFloodExit(const NativeCodeBasicBlock* block, int reg); + + bool CrossBlockY2XFlood(NativeCodeProcedure* proc); + bool CheckCrossBlockY2XFlood(const NativeCodeBasicBlock* block, int at); + bool CheckCrossBlockY2XFloodExit(const NativeCodeBasicBlock* block); + bool PatchCrossBlockY2XFlood(const NativeCodeBasicBlock* block, int at); + bool PatchCrossBlockY2XFloodExit(const NativeCodeBasicBlock* block); + + void PropagateZPAbsolute(const NativeRegisterDataSet& data); + + void PropagateAddGlobalCarry(void); + + int CalcBlockDiffY(void) const; + int CalcBlockDiffX(void) const; + + int SimpleLoopNumIterations(void); + + bool FindSelfModSource(NativeCodeBasicBlock * block, int reg, NativeCodeBasicBlock*& sblock, int & sat); + bool CollectSelfModTargets(void); + + bool EliminateNonAliasedLocalStores(void); + bool CheckNonAliasedLocalStore(int at, const NativeCodeInstruction& sins); + + bool CanSwapAccuZP(int reg, int size); + void SwapAccuZP(int reg, int size); + + void RegisterFunctionCalls(void); + bool MergeFunctionCalls(void); + + bool CheckAllAccuReturn(void); + void ReplaceAccuReturn(void); + + bool IsDominatedBy(const NativeCodeBasicBlock* block) const; + + void CheckLive(void); + void CheckBlocks(bool sequence = false); + void CheckAsmCode(void); + void CheckVisited(void); + + int mSuffixStringLength; + int* mSuffixString; + void AddToSuffixTree(NativeCodeMapper& mapper, SuffixTree * tree); + +}; + +class NativeCodeProcedure +{ + public: + NativeCodeProcedure(NativeCodeGenerator* generator); + ~NativeCodeProcedure(void); + + NativeCodeBasicBlock* mEntryBlock, * mExitBlock; + NativeCodeBasicBlock** tblocks; + + NativeCodeGenerator* mGenerator; + + InterCodeProcedure* mInterProc; + LinkerObject* mLinkerObject; + const Ident* mIdent; + Location mLocation; + uint64 mCompilerOptions; + + int mProgStart, mProgSize, mIndex, mFrameOffset, mStackExpand; + int mFastCallBase; + bool mNoFrame, mSimpleInline; + int mTempBlocks; + + ExpandingArray mRelocations; + ExpandingArray< NativeCodeBasicBlock*> mBlocks; + ExpandingArray mCodeLocations, mCodeOrigins; + ExpandingArray< SelfModReference> mSelfModSources; + + void DisassembleDebug(const char* name); + void Disassemble(FILE* file); + + void Compile(InterCodeProcedure* proc); + void Optimize(void); + void Assemble(void); + + void AddToSuffixTree(NativeCodeMapper& mapper, SuffixTree* tree); + + NativeCodeBasicBlock* CompileBlock(InterCodeProcedure* iproc, InterCodeBasicBlock* block); + NativeCodeBasicBlock* AllocateBlock(void); + + void CompileInterBlock(InterCodeProcedure* iproc, InterCodeBasicBlock* iblock, NativeCodeBasicBlock*block); + + bool MapFastParamsToTemps(void); + void CompressTemporaries(bool singles); + + void BuildDataFlowSets(void); + void ResetEntryBlocks(void); + void ResetChecked(void); + void ResetVisited(void); + void ResetPatched(void); + void RebuildEntry(void); + void ResetIndexFlipped(void); + void CheckBlocks(bool sequence = false); + void TrimBlocks(void); + + void SaveTempsToStack(int tempSave); + void LoadTempsFromStack(int tempSave); +}; + +class NativeCodeGenerator +{ +public: + NativeCodeGenerator(Errors * errors, Linker* linker, LinkerSection * runtimeSection); + ~NativeCodeGenerator(void); + + void RegisterRuntime(const Ident * ident, LinkerObject * object, int offset); + void CompleteRuntime(void); + + uint64 mCompilerOptions; + + struct Runtime + { + const Ident * mIdent; + LinkerObject * mLinkerObject; + int mOffset; + }; + + struct MulTable + { + LinkerObject * mLinkerLSB, * mLinkerMSB; + int mFactor, mSize; + InterOperator mOperator; + }; + + struct FloatTable + { + LinkerObject * mLinker[4]; + float mConst; + int mMinValue, mMaxValue; + InterOperator mOperator; + bool mReverse; + }; + + LinkerObject* AllocateShortMulTable(InterOperator op, int factor, int size, bool msb); + LinkerObject* AllocateFloatTable(InterOperator op, bool reverse, int minval, int maxval, float fval, int index); + void PopulateShortMulTables(void); + + Runtime& ResolveRuntime(const Ident* ident); + + Errors* mErrors; + Linker* mLinker; + LinkerSection* mRuntimeSection; + + ExpandingArray mProcedures; + + ExpandingArray mRuntime; + ExpandingArray mMulTables; + ExpandingArray mFloatTables; + + struct FunctionCall + { + LinkerObject * mLinkerObject, * mProxyObject; + NativeCodeInstruction mIns[64]; + FunctionCall * mNext, * mSame; + int mCount, mOffset; + + bool IsSame(const FunctionCall* fc) const; + int Matches(const FunctionCall* fc) const; + int PotentialMatches(const FunctionCall* fc) const; + }; + + FunctionCall* mFunctionCalls; + + void OutlineFunctions(void); + + void RegisterFunctionCall(NativeCodeBasicBlock* block, int at); + void BuildFunctionProxies(void); + bool MergeFunctionCall(NativeCodeBasicBlock* block, int at); +}; diff --git a/oscar64/oscar64/NativeCodeOutliner.cpp b/oscar64/oscar64/NativeCodeOutliner.cpp new file mode 100644 index 0000000..0069e52 --- /dev/null +++ b/oscar64/oscar64/NativeCodeOutliner.cpp @@ -0,0 +1,379 @@ +#include "NativeCodeOutliner.h" + + +NativeCodeMapper::NativeCodeMapper(void) +{ + for (int i = 0; i < HashSize; i++) + mHash[i] = nullptr; +} + +NativeCodeMapper::~NativeCodeMapper(void) +{ + for (int i = 0; i < HashSize; i++) + { + InsNode* n = mHash[i]; + while (n) + { + InsNode* m = n; + n = n->mNext; + delete m; + } + mHash[i] = nullptr; + } + +} + +void NativeCodeMapper::Reset(void) +{ + mBlocks.SetSize(0); +} + +int NativeCodeMapper::MapBasicBlock(NativeCodeBasicBlock* block) +{ + mBlocks.Push(block); + return -mBlocks.Size(); +} + +int NativeCodeMapper::MapInstruction(const NativeCodeInstruction& ins, LinkerSection* ls) +{ + int hash = ins.CodeHash() % HashSize; + InsNode* n = mHash[hash]; + while (n) + { + if (mIns[n->mIndex].CodeSame(ins) && n->mSection == ls) + return n->mIndex; + n = n->mNext; + } + n = new InsNode(); + n->mIndex = mIns.Size(); + n->mSection = ls; + mIns.Push(ins); + n->mNext = mHash[hash]; + mHash[hash] = n; + return n->mIndex; +} + +SuffixTree::SuffixTree(const int* str, int s, SuffixTree* n) +{ + mSeg = str; + mSize = s; + mNext = n; + mParent = nullptr; + mFirst = nullptr; +} + +SuffixTree::~SuffixTree(void) +{ + delete[] mFirst; +} + +void SuffixTree::AddParents(SuffixTree* parent) +{ + mParent = parent; + if (mFirst) + { + for (int i = 0; i < HashSize; i++) + { + SuffixTree* n = mFirst[i]; + while (n) + { + n->AddParents(this); + n = n->mNext; + } + } + } +} + +void SuffixTree::AddSuffix(const int* str, int s) +{ + int hi = str[0] & (HashSize - 1); + SuffixTree* c = mFirst ? mFirst[hi] : nullptr; + while (c && c->mSeg[0] != str[0]) + c = c->mNext; + + if (c) + { + int k = 1; + while (k < c->mSize && str[k] == c->mSeg[k]) + k++; + if (k == c->mSize) + c->AddSuffix(str + k, s - k); + else + { + SuffixTree * n = new SuffixTree(c->mSeg + k, c->mSize - k, nullptr); + if (c->mFirst) + { + n->mFirst = new SuffixTree * [HashSize]; + for (int i = 0; i < HashSize; i++) + { + n->mFirst[i] = c->mFirst[i]; + c->mFirst[i] = nullptr; + } + } + else + { + c->mFirst = new SuffixTree * [HashSize]; + for (int i = 0; i < HashSize; i++) + c->mFirst[i] = nullptr; + } + c->mFirst[c->mSeg[k] & (HashSize - 1)] = n; + int hk = str[k] & (HashSize - 1); + c->mFirst[hk] = new SuffixTree(str + k, s - k, c->mFirst[hk]); + c->mSize = k; + } + } + else + { + if (!mFirst) + { + mFirst = new SuffixTree * [HashSize]; + for (int i = 0; i < HashSize; i++) + mFirst[i] = nullptr; + } + + mFirst[hi] = new SuffixTree(str, s, mFirst[hi]); + } +} + +void SuffixTree::AddString(const int* str) +{ + int s = 0; + while(str[s] >= 0) + s++; + s++; + + int i = 0; + while (str[i] >= 0) + { + AddSuffix(str + i, s - i); + i++; + } + AddSuffix(str + i, 1); +} + +void SuffixTree::CollectSuffix(NativeCodeMapper& map, int offset, ExpandingArray& segs) +{ + offset += mSize; + if (mFirst) + { + for (int i = 0; i < HashSize; i++) + { + SuffixTree* t = mFirst[i]; + + while (t) + { + t->CollectSuffix(map, offset, segs); + t = t->mNext; + } + } + } + else + { + NativeCodeBasicBlock* block = map.mBlocks[-(mSeg[mSize - 1] + 1)]; + + SuffixSegment seg; + seg.mBlock = block; + seg.mStart = offset; + seg.mEnd = block->mIns.Size() + 1; + segs.Push(seg); + } +} + +int SuffixTree::LongestMatch(NativeCodeMapper& map, int size, int isize, int& msize, SuffixTree*& mtree) +{ + if (mFirst) + { + isize += mSize; + + for (int i = 0; i < mSize; i++) + { + if (mSeg[i] >= 0) + size += AsmInsModeSize[map.mIns[mSeg[i]].mMode]; + } + + assert(size < 10000); + + int cnt = 0; + for (int i = 0; i < HashSize; i++) + { + SuffixTree* t = mFirst[i]; + while (t) + { + cnt += t->LongestMatch(map, size, isize, msize, mtree); + t = t->mNext; + } + } + + if (size >= 6 && (size - 3) * (cnt - 1) > msize) + { + // Second run to cross check for overlaps + ExpandingArray segs; + for (int i = 0; i < HashSize; i++) + { + SuffixTree* t = mFirst[i]; + while (t) + { + t->CollectSuffix(map, 0, segs); + t = t->mNext; + } + } + segs.Sort([](const SuffixSegment& l, const SuffixSegment& r)->bool { + return l.mBlock == r.mBlock ? l.mStart < r.mStart : ptrdiff_t(l.mBlock) < ptrdiff_t(r.mBlock); + }); + + for (int i = 0; i + 1 < segs.Size(); i++) + { + if (segs[i].mBlock == segs[i + 1].mBlock && segs[i].mStart + isize > segs[i + 1].mStart) + cnt--; + } + + if (cnt > 1 && (size - 3) * (cnt - 1) > msize) + { + msize = (size - 3) * (cnt - 1); + mtree = this; + } + } + + return cnt; + } + else + return 1; +} + +void SuffixTree::Print(FILE * file, NativeCodeMapper& map, int depth) +{ + for (int i = 0; i < depth; i++) + fprintf(file, "."); + + for (int i = 0; i < mSize; i++) + { + fprintf(file, "["); + + if (mSeg[i] >= 0) + map.mIns[mSeg[i]].Disassemble(file); + else + { + NativeCodeBasicBlock* block = map.mBlocks[- (mSeg[i] + 1)]; + fprintf(file, "%s,%d", block->mProc->mInterProc->mIdent->mString, block->mIndex); + } + fprintf(file, "]"); + } + fprintf(file, "\n"); + + if (mFirst) + { + for (int i = 0; i < HashSize; i++) + { + SuffixTree* n = mFirst[i]; + while (n) + { + n->Print(file, map, depth + 1); + n = n->mNext; + } + } + } +} + +void SuffixTree::ParentPrint(FILE* file, NativeCodeMapper& map) +{ + if (mParent) + mParent->ParentPrint(file, map); + for (int i = 0; i < mSize; i++) + { + if (mSeg[i] >= 0) + map.mIns[mSeg[i]].Disassemble(file); + else + { + NativeCodeBasicBlock* block = map.mBlocks[-(mSeg[i] + 1)]; + fprintf(file, "%s,%d", block->mProc->mInterProc->mIdent->mString, block->mIndex); + } + fprintf(file, "\n"); + } +} + +int SuffixTree::ParentCodeSize(NativeCodeMapper& map) const +{ + int size = 0; + for (int i = 0; i < mSize; i++) + { + if (mSeg[i] >= 0) + size += AsmInsModeSize[map.mIns[mSeg[i]].mMode]; + } + + if (mParent) + size += mParent->ParentCodeSize(map); + + return size; +} + +void SuffixTree::ParentCollect(NativeCodeMapper& map, NativeCodeBasicBlock* block) +{ + if (mParent) + mParent->ParentCollect(map, block); + for (int i = 0; i < mSize; i++) + block->mIns.Push(map.mIns[mSeg[i]]); +} + +void SuffixTree::ReplaceCalls(NativeCodeMapper& map, ExpandingArray& segs) +{ + if (mFirst) + { + for (int i = 0; i < HashSize; i++) + { + SuffixTree* n = mFirst[i]; + while (n) + { + n->ChildReplaceCalls(map, this, 0, segs); + n = n->mNext; + } + } + } +} + +void SuffixTree::ChildReplaceCalls(NativeCodeMapper& map, SuffixTree* tree, int offset, ExpandingArray& segs) +{ + for (int i = 0; i < mSize; i++) + { + if (mSeg[i] >= 0) + offset ++; + } + + if (mFirst) + { + for (int i = 0; i < HashSize; i++) + { + SuffixTree* n = mFirst[i]; + while (n) + { + n->ChildReplaceCalls(map, tree, offset, segs); + n = n->mNext; + } + } + } + else + { + NativeCodeBasicBlock* block = map.mBlocks[-(mSeg[mSize - 1] + 1)]; + tree->ParentReplaceCalls(map, block, offset, 0, segs); + } +} + +void SuffixTree::ParentReplaceCalls(NativeCodeMapper& map, NativeCodeBasicBlock* block, int offset, int size, ExpandingArray& segs) +{ + for (int i = 0; i < mSize; i++) + { + if (mSeg[i] >= 0) + size ++; + } + if (mParent) + mParent->ParentReplaceCalls(map, block, offset, size, segs); + else + { + int at = block->mIns.Size() - offset - size; + + SuffixSegment seg; + seg.mBlock = block; + seg.mStart = at; + seg.mEnd = at + size; + segs.Push(seg); + } +} diff --git a/oscar64/oscar64/NativeCodeOutliner.h b/oscar64/oscar64/NativeCodeOutliner.h new file mode 100644 index 0000000..7457eb9 --- /dev/null +++ b/oscar64/oscar64/NativeCodeOutliner.h @@ -0,0 +1,66 @@ +#pragma once + +#include "NativeCodeGenerator.h" +#include "Array.h" + +class NativeCodeMapper +{ +public: + NativeCodeMapper(void); + ~NativeCodeMapper(void); + + void Reset(void); + + int MapInstruction(const NativeCodeInstruction& ins, LinkerSection * ls); + int MapBasicBlock(NativeCodeBasicBlock* block); + + ExpandingArray mIns; + ExpandingArray mBlocks; +protected: + static const int HashSize = 256; + + struct InsNode + { + int mIndex; + LinkerSection * mSection; + InsNode* mNext; + }; + + InsNode * mHash[HashSize]; +}; + +struct SuffixSegment +{ + NativeCodeBasicBlock * mBlock; + int mStart, mEnd; +}; + +class SuffixTree +{ +public: + const int * mSeg; + int mSize; + + static const int HashSize = 32; + + SuffixTree* mNext, * mParent, ** mFirst; + + SuffixTree(const int* str, int s, SuffixTree* n); + ~SuffixTree(void); + + void AddParents(SuffixTree* parent); + + void AddSuffix(const int* str, int s); + void AddString(const int* str); + + void Print(FILE* file, NativeCodeMapper & map, int depth); + void ParentPrint(FILE* file, NativeCodeMapper& map); + int LongestMatch(NativeCodeMapper& map, int size, int isize, int & msize, SuffixTree *& mtree); + void CollectSuffix(NativeCodeMapper& map, int offset, ExpandingArray& segs); + int ParentCodeSize(NativeCodeMapper& map) const; + void ParentCollect(NativeCodeMapper& map, NativeCodeBasicBlock * block); + void ReplaceCalls(NativeCodeMapper& map, ExpandingArray & segs); + void ChildReplaceCalls(NativeCodeMapper& map, SuffixTree * tree, int offset, ExpandingArray& segs); + void ParentReplaceCalls(NativeCodeMapper& map, NativeCodeBasicBlock* block, int offset, int size, ExpandingArray& segs); + +}; \ No newline at end of file diff --git a/oscar64/oscar64/NumberSet.cpp b/oscar64/oscar64/NumberSet.cpp new file mode 100644 index 0000000..d820358 --- /dev/null +++ b/oscar64/oscar64/NumberSet.cpp @@ -0,0 +1,338 @@ +#include "NumberSet.h" + +#define VALGRIND 0 + +NumberSet::NumberSet(void) +{ + size = 0; + dwsize = 0; + bits = 0; +} + +NumberSet::NumberSet(int size, bool set) +{ + int i; + + this->size = size; + dwsize = (size + 63) >> 6; + + bits = new uint64[dwsize]; + + if (set) + { + for (i = 0; i < dwsize; i++) + bits[i] = ~0ull; + } + else + { + for (i = 0; i < dwsize; i++) + bits[i] = 0; + } +} + +NumberSet::NumberSet(const NumberSet& set) +{ + int i; + + this->size = set.size; + this->dwsize = set.dwsize; + this->bits = new uint64[dwsize]; + + for (i = 0; i < dwsize; i++) + bits[i] = set.bits[i]; +} + +NumberSet::~NumberSet(void) +{ + delete[] bits; +} + +void NumberSet::Expand(int size, bool set) +{ + if (size > this->size) + { + int ndwsize = (size + 63) >> 6; + if (dwsize != ndwsize) + { + uint64 * nbits = new uint64[ndwsize]; + for (int i = 0; i < dwsize; i++) + nbits[i] = bits[i]; + for (int i = dwsize; i < ndwsize; i++) + nbits[i] = 0; + delete[] bits; + dwsize = ndwsize; + bits = nbits; + } + this->size = size; + } +} + +void NumberSet::Reset(int size, bool set) +{ + int i; + + int ndwsize = (size + 63) >> 6; + if (this->dwsize != ndwsize) + { + delete[] bits; + dwsize = ndwsize; + bits = new uint64[dwsize]; + } + + this->size = size; + + if (set) + { + for (i = 0; i < dwsize; i++) + bits[i] = ~0ull; + } + else + { + for (i = 0; i < dwsize; i++) + bits[i] = 0; + } +} + +void NumberSet::AddRange(int elem, int num) +{ + for (int i = 0; i < num; i++) + *this += elem + i; +} + +void NumberSet::SubRange(int elem, int num) +{ + for (int i = 0; i < num; i++) + *this -= elem + i; +} + +bool NumberSet::RangeClear(int elem, int num) const +{ + for (int i = 0; i < num; i++) + if ((*this)[elem + i]) + return false; + return true; +} + +bool NumberSet::RangeFilled(int elem, int num) const +{ + for (int i = 0; i < num; i++) + if (!(*this)[elem + i]) + return false; + return true; +} + +void NumberSet::Fill(void) +{ + int i; + + for (i = 0; i < dwsize; i++) + bits[i] = ~0ull; +} + +void NumberSet::OrNot(const NumberSet& set) +{ + int i; + + for (i = 0; i < dwsize; i++) + bits[i] |= ~set.bits[i]; +} + +void NumberSet::Clear(void) +{ + int i; + + for (i = 0; i < dwsize; i++) + bits[i] = 0; +} + +NumberSet& NumberSet::operator=(const NumberSet& set) +{ + this->size = set.size; + + if (dwsize != set.dwsize) + { + delete[] bits; + this->dwsize = set.dwsize; + this->bits = new uint64[dwsize]; + } + + int size = dwsize; + const uint64* sbits = set.bits; + uint64* dbits = bits; + + for (int i = 0; i < size; i++) + dbits[i] = sbits[i]; + + return *this; +} + +NumberSet& NumberSet::operator&=(const NumberSet& set) +{ + assert(dwsize == set.dwsize); + + int size = dwsize; + const uint64* sbits = set.bits; + uint64* dbits = bits; + + for (int i = 0; i < size; i++) + dbits[i] &= sbits[i]; + + return *this; +} + +NumberSet& NumberSet::operator|=(const NumberSet& set) +{ + assert(dwsize >= set.dwsize); + + int size = dwsize < set.dwsize ? dwsize : set.dwsize; + const uint64* sbits = set.bits; + uint64* dbits = bits; + + for (int i = 0; i < size; i++) + dbits[i] |= sbits[i]; + + return *this; +} + +NumberSet& NumberSet::operator-=(const NumberSet& set) +{ + assert(dwsize == set.dwsize); + + int i; + + for (i = 0; i < dwsize; i++) + bits[i] &= ~set.bits[i]; + + return *this; +} + +bool NumberSet::operator<=(const NumberSet& set) const +{ + assert(dwsize == set.dwsize); + + int i; + + for (i = 0; i < dwsize; i++) + if (bits[i] & ~set.bits[i]) return false; + + return true; +} + + + +FastNumberSet::FastNumberSet(void) +{ + num = 0; + size = 0; + asize = 0; + buffer = nullptr; +} + +FastNumberSet::FastNumberSet(int size, bool set) +{ + this->size = this->asize = size; + buffer = new uint32[2 * size]; +#if VALGRIND + for (int i = 0; i < 2 * size; i++) + buffer[i] = 0; +#endif + if (set) + { + for (num = 0; num < size; num++) + { + buffer[num] = num; + buffer[num + size] = num; + } + } + else + num = 0; +} + +FastNumberSet::FastNumberSet(const FastNumberSet& set) +{ + int i; + + this->size = this->asize = set.size; + this->num = set.num; + buffer = new uint32[2 * size]; +#if VALGRIND + for (int i = 0; i < 2 * size; i++) + buffer[i] = 0; +#endif + + for (i = 0; i < num; i++) + { + buffer[i] = set.buffer[i]; + buffer[size + buffer[i]] = i; + } +} + +void FastNumberSet::Reset(int size, bool set) +{ + if (size > this->asize) + { + delete[] buffer; + buffer = new uint32[2 * size]; +#if VALGRIND + for (int i = 0; i < 2 * size; i++) + buffer[i] = 0; +#endif + + this->asize = size; + } + + this->size = size; + + if (set) + { + for (num = 0; num < size; num++) + { + buffer[num] = num; + buffer[num + size] = num; + } + } + else + num = 0; +} + + +FastNumberSet::~FastNumberSet(void) +{ + delete[] buffer; +} + +FastNumberSet& FastNumberSet::operator=(const FastNumberSet& set) +{ + if (set.size > this->asize) + { + delete[] buffer; + buffer = new uint32[2 * set.size]; + + this->asize = set.size; + } + + this->size = set.size; + + for (num = 0; num < set.num; num++) + { + buffer[num] = set.buffer[num]; + buffer[buffer[num] + size] = num; + } + + return *this; +} + +void FastNumberSet::Clear(void) +{ + num = 0; +} + +int FastNumberSet::Index(int elem) const +{ + uint32 dw = buffer[size + elem]; + + if (dw < uint32(num) && buffer[dw] == elem) + return dw; + else + return -1; +} diff --git a/oscar64/oscar64/NumberSet.h b/oscar64/oscar64/NumberSet.h new file mode 100644 index 0000000..ab78e41 --- /dev/null +++ b/oscar64/oscar64/NumberSet.h @@ -0,0 +1,144 @@ +#pragma once + +#include + +#include "MachineTypes.h" + +class NumberSet +{ +protected: + uint64 * bits; + int size, dwsize; +public: + NumberSet(void); + NumberSet(int size, bool set = false); + NumberSet(const NumberSet& set); + ~NumberSet(void); + + void Reset(int size, bool set = false); + void Expand(int size, bool set = false); + + NumberSet& operator+=(int elem); + NumberSet& operator-=(int elem); + + bool operator[](int elem) const; + + NumberSet& operator=(const NumberSet& set); + + NumberSet& operator&=(const NumberSet& set); + NumberSet& operator|=(const NumberSet& set); + NumberSet& operator-=(const NumberSet& set); + + bool operator<=(const NumberSet& set) const; + + void OrNot(const NumberSet& set); + + void Clear(void); + void Fill(void); + void AddRange(int elem, int num); + void SubRange(int elem, int num); + bool RangeClear(int elem, int num) const; + bool RangeFilled(int elem, int num) const; + + int Size(void) { return size; } +}; + +inline NumberSet& NumberSet::operator+=(int elem) +{ + assert(elem >= 0 && elem < size); + bits[elem >> 6] |= (1ULL << (elem & 63)); + + return *this; +} + +inline NumberSet& NumberSet::operator-=(int elem) +{ + assert(elem >= 0 && elem < size); + bits[elem >> 6] &= ~(1ULL << (elem & 63)); + + return *this; +} + +inline bool NumberSet::operator[](int elem) const +{ + assert(elem >= 0 && elem < size); + return (bits[elem >> 6] & (1ULL << (elem & 63))) != 0; +} + + +class FastNumberSet +{ +protected: + uint32 * buffer; + int size, num, asize; +public: + FastNumberSet(void); + FastNumberSet(int size, bool set = false); + FastNumberSet(const FastNumberSet& set); + ~FastNumberSet(void); + + void Reset(int size, bool set = false); + + FastNumberSet& operator+=(int elem); + FastNumberSet& operator-=(int elem); + + bool operator[](int elem) const; + + FastNumberSet& operator=(const FastNumberSet& set); + + bool Empty(void) const { return !num; } + void Clear(void); + + int Num(void) const { return num; } + int Element(int i) const; + + int Size(void) const { return size; } + int Index(int elem) const; +}; + +inline bool FastNumberSet::operator[](int elem) const +{ + uint32 dw = buffer[size + elem]; + + return (dw < uint32(num) && buffer[dw] == elem); +} + +inline FastNumberSet& FastNumberSet::operator+=(int elem) +{ + assert(elem < size); + + uint32 dw = buffer[size + elem]; + + if (dw >= uint32(num) || buffer[dw] != elem) + { + assert(num < size); + + buffer[num] = elem; + buffer[size + elem] = num; + num++; + } + + return *this; +} + +inline FastNumberSet& FastNumberSet::operator-=(int elem) +{ + uint32 dw = buffer[size + elem]; + + if (dw < uint32(num) && buffer[dw] == elem) + { + num--; + buffer[dw] = buffer[num]; + buffer[size + buffer[dw]] = dw; + } + + return *this; +} + +inline int FastNumberSet::Element(int i) const +{ + if (i < num) + return buffer[i]; + else + return -1; +} diff --git a/oscar64/oscar64/Parser.cpp b/oscar64/oscar64/Parser.cpp new file mode 100644 index 0000000..d9bfd12 --- /dev/null +++ b/oscar64/oscar64/Parser.cpp @@ -0,0 +1,15774 @@ +#include "Parser.h" +#include +#include "Assembler.h" +#include "MachineTypes.h" +#include "Constexpr.h" +#include "NumberSet.h" + +Parser::Parser(Errors* errors, Scanner* scanner, CompilationUnits* compilationUnits) + : mErrors(errors), mScanner(scanner), mCompilationUnits(compilationUnits), mParent(nullptr) +{ + mGlobals = new DeclarationScope(compilationUnits->mScope, SLEVEL_STATIC); + mScope = mGlobals; + mTemplateScope = nullptr; + + mCodeSection = compilationUnits->mSectionCode; + mDataSection = compilationUnits->mSectionData; + mBSSection = compilationUnits->mSectionBSS; + + mUnrollLoop = 0; + mUnrollLoopPage = false; + mInlineCall = false; + mCompilerOptionSP = 0; + mThisPointer = nullptr; + mFunction = nullptr; + mFunctionType = nullptr; + mLambda = nullptr; + mCaptureScope = nullptr; + mTempVars = nullptr; + + for (int i = 0; i < 256; i++) + mCharMap[i] = i; +} + +Parser::~Parser(void) +{ + +} + +Parser* Parser::Clone(void) +{ + Parser* p = new Parser(mErrors, new Scanner(mErrors, mScanner->mPreprocessor), mCompilationUnits); + + p->mGlobals = mGlobals; + p->mScope = mScope; + p->mCompilerOptions = mCompilerOptions; + p->mCodeSection = mCodeSection; + p->mDataSection = mDataSection; + p->mBSSection = mBSSection; + + return p; +} + +Declaration* Parser::FindBaseMemberFunction(Declaration* dec, Declaration* mdec) +{ + const Ident* ident = mdec->mIdent; + + if (ident->mString[0] == '~') + { + // this is the destructor, need special search + + Declaration * pdec = dec; + Declaration * pmdec = nullptr; + + while (pdec->mBase && !pmdec) + { + pdec = pdec->mBase->mBase; + pmdec = pdec->mScope->Lookup(pdec->mIdent->PreMangle("~")); + } + + return pmdec; + } + else + { + Declaration * pdec = dec; + Declaration * pmdec = nullptr; + + while (pdec->mBase && !pmdec) + { + pdec = pdec->mBase->mBase; + pmdec = pdec->mScope->Lookup(ident); + + while (pmdec && !mdec->mBase->IsDerivedFrom(pmdec->mBase)) + pmdec = pmdec->mNext; + + if (pmdec) + return pmdec; + } + + return pmdec; + } +} + +Declaration * Parser::AddMemberFunction(Declaration* dec, Declaration* mdec) +{ + Declaration* pmdec = FindBaseMemberFunction(dec, mdec); + if (pmdec) + mdec->mBase->mFlags |= pmdec->mBase->mFlags & DTF_VIRTUAL; + + const Ident* dtorident = dec->mIdent->PreMangle("~");; + const Ident* ctorident = dec->mIdent->PreMangle("+");; + + if (mdec->mIdent == ctorident) + { + // Check constructors + Declaration* ctdec = mdec->mBase; + Declaration* tparam = ctdec->mParams->mNext; + + if (!tparam && !dec->mDefaultConstructor) + dec->mDefaultConstructor = mdec; + else if (tparam && !tparam->mNext && tparam->mBase->mType == DT_TYPE_REFERENCE && dec->IsConstSame(tparam->mBase->mBase) && !dec->mCopyConstructor) + dec->mCopyConstructor = mdec; + else if (tparam && !tparam->mNext && tparam->mBase->mType == DT_TYPE_RVALUEREF && dec->IsConstSame(tparam->mBase->mBase) && !dec->mMoveConstructor) + dec->mMoveConstructor = mdec; + } + else if (mdec->mIdent == dtorident && !dec->mDestructor) + { + dec->mDestructor = mdec; + } + + Declaration* gdec = mCompilationUnits->mScope->Insert(mdec->mQualIdent, mdec); + if (gdec) + { + if (gdec->mType == DT_CONST_FUNCTION) + { + Declaration* pcdec = nullptr; + Declaration* pdec = gdec; + + while (pdec && !mdec->mBase->IsSameParams(pdec->mBase)) + { + pcdec = pdec; + pdec = pdec->mNext; + } + + if (!pdec) + pcdec->mNext = mdec; + + dec->mScope->Insert(mdec->mIdent, gdec); + if (dec->mConst) + dec->mConst->mScope->Insert(mdec->mIdent, gdec); + + if (pdec) + return pdec; + } + else + { + mErrors->Error(mdec->mLocation, EERR_DUPLICATE_DEFINITION, "Duplicate struct member declaration", mdec->mIdent); + mErrors->Error(gdec->mLocation, EINFO_ORIGINAL_DEFINITION, "Original definition"); + } + } + else + dec->mScope->Insert(mdec->mIdent, mdec); + + return mdec; +} + +Declaration* Parser::ParseStructDeclaration(uint64 flags, DecType dt, Declaration* ptempl) +{ + const Ident* structName = nullptr; + + Declaration * dec = new Declaration(mScanner->mLocation, dt); + + bool needVTable = false; + + mScanner->NextToken(); + if (mScanner->mToken == TK_IDENT) + { + structName = mScanner->mTokenIdent; + mScanner->NextToken(); + Declaration* edec = mScope->Lookup(structName); + if (edec && edec->mTemplate) + { + mTemplateScope->Insert(structName, dec); + + dec->mIdent = structName; + dec->mQualIdent = mScope->Mangle(structName->Mangle(mTemplateScope->mName->mString)); + dec->mScope = new DeclarationScope(nullptr, SLEVEL_CLASS, dec->mQualIdent); + } + else if (edec && mScanner->mToken != TK_OPEN_BRACE && mScanner->mToken != TK_COLON) + { + dec = edec; + } + else + { + Declaration* pdec = mScope->Insert(structName, dec); + if (pdec) + { + if (pdec->mType == dt && !(pdec->mFlags & DTF_DEFINED)) + { + dec = pdec; + } + else + { + mErrors->Error(mScanner->mLocation, EERR_DUPLICATE_DEFINITION, "Error duplicate struct declaration", structName); + mErrors->Error(pdec->mLocation, EINFO_ORIGINAL_DEFINITION, "Original definition"); + } + } + } + } + else if ((mCompilerOptions & COPT_CPLUSPLUS) && mScope->mLevel < SLEVEL_CLASS) + { + mErrors->Error(mScanner->mLocation, EERR_INVALID_IDENTIFIER, "Invalid unnamed struct on global level"); + } + + if (!dec->mIdent || !dec->mScope) + { + dec->mIdent = structName; + dec->mQualIdent = mScope->Mangle(structName); + dec->mScope = new DeclarationScope(nullptr, SLEVEL_CLASS, dec->mQualIdent); + } + + if (ptempl) + { + ptempl->mBase = dec; + + if (mScanner->mToken == TK_LESS_THAN) + { + int level = 0; + do { + if (mScanner->mToken == TK_LESS_THAN) + level++; + else if (mScanner->mToken == TK_GREATER_THAN) + level--; + mScanner->NextToken(); + } while (level > 0); + } + } + + if ((mCompilerOptions & COPT_CPLUSPLUS) && mScanner->mToken == TK_COLON) + { + mScanner->NextToken(); + uint64 pflags = flags; + + if (ConsumeTokenIf(TK_PUBLIC)) + pflags &= ~(DTF_PRIVATE | DTF_PROTECTED); + else if (ConsumeTokenIf(TK_PROTECTED)) + pflags = (pflags & ~DTF_PRIVATE) | DTF_PROTECTED; + else if (ConsumeTokenIf(TK_PRIVATE)) + pflags |= DTF_PRIVATE | DTF_PROTECTED; + + Declaration* pdec = ParseBaseTypeDeclaration(0, false); + if (pdec) + { + if (pdec->mType == DT_TYPE_STRUCT) + { + Declaration* bcdec = new Declaration(mScanner->mLocation, DT_BASECLASS); + bcdec->mBase = pdec; + bcdec->mFlags = pflags; + + dec->mScope->UseScope(pdec->mScope); + + if (pdec->mVTable) + needVTable = true; + + dec->mSize = pdec->mSize; + + dec->mBase = bcdec; + } + else + mErrors->Error(mScanner->mLocation, ERRO_NOT_A_BASE_CLASS, "Not a base class", dec->mIdent); + } + } + + if (mScanner->mToken == TK_OPEN_BRACE) + { + Declaration* pthis = nullptr; + + DeclarationScope* oscope = mScope; + + if (mCompilerOptions & COPT_CPLUSPLUS) + { + pthis = new Declaration(mScanner->mLocation, DT_TYPE_POINTER); + pthis->mFlags |= DTF_CONST | DTF_DEFINED; + pthis->mBase = dec; + pthis->mSize = 2; + + dec->mScope->mParent = mScope; + mScope = dec->mScope; + } + + mScanner->NextToken(); + if (ConsumeTokenIf(TK_CLOSE_BRACE)) + { + dec->mParams = nullptr; + } + else + { + int bitsleft = 0; + + Declaration* mlast = nullptr; + for (;;) + { + do {} while (ConsumeTokenIf(TK_SEMICOLON)); + + if (ConsumeTokenIf(TK_PUBLIC)) + { + flags &= ~(DTF_PRIVATE | DTF_PROTECTED); + ConsumeToken(TK_COLON); + } + else if (ConsumeTokenIf(TK_PROTECTED)) + { + flags &= ~DTF_PRIVATE; + flags |= DTF_PROTECTED; + ConsumeToken(TK_COLON); + } + else if (ConsumeTokenIf(TK_PRIVATE)) + { + flags |= DTF_PRIVATE | DTF_PROTECTED; + ConsumeToken(TK_COLON); + } + else if (ConsumeTokenIf(TK_FRIEND)) + { + mScope = oscope; + Declaration* fdec = ParseDeclaration(nullptr, true, false, false); + if (fdec->mType == DT_ANON) + fdec = fdec->mBase; + dec->mFriends.Push(fdec); + mScope = dec->mScope; + } + else if (ConsumeTokenIf(TK_CLOSE_BRACE)) + { + break; + } + else if (ConsumeTokenIf(TK_TEMPLATE)) + { + Declaration * tdec = ParseTemplateDeclaration(pthis); + } + else if (ConsumeTokenIf(TK_STATIC_ASSERT)) + { + ParseStaticAssert(); + } + else + { + Declaration* mdec = ParseDeclaration(nullptr, false, false, true, pthis); + if (mdec) + { + mdec->mQualIdent = dec->mScope->Mangle(mdec->mIdent); + + int offset = dec->mSize; + + if (mdec->mBase->mType == DT_TYPE_FUNCTION) + { + mdec->mFlags |= flags & (DTF_PRIVATE | DTF_PROTECTED); + + if (mdec->mBase->mFlags & DTF_VIRTUAL) + needVTable = true; + + mdec->mType = DT_CONST_FUNCTION; + mdec->mSection = mCodeSection; + mdec->mFlags |= DTF_GLOBAL; +// mdec->mBase->mFlags |= DTF_FUNC_THIS; + + if (mCompilerOptions & COPT_NATIVE) + mdec->mFlags |= DTF_NATIVE; + + AddMemberFunction(dec, mdec); + } + else if ((mCompilerOptions & COPT_CPLUSPLUS) && mdec->mType == DT_ANON) + { + mdec->mFlags |= flags & (DTF_PRIVATE | DTF_PROTECTED); + + ConsumeToken(TK_SEMICOLON); + // anon element + } + else + { + while (mdec) + { + mdec->mFlags |= flags & (DTF_PRIVATE | DTF_PROTECTED); + + if (!(mdec->mBase->mFlags & DTF_DEFINED)) + mErrors->Error(mdec->mLocation, EERR_UNDEFINED_OBJECT, "Undefined type used in struct member declaration"); + + if (mdec->mType != DT_VARIABLE) + { + mErrors->Error(mdec->mLocation, EERR_UNDEFINED_OBJECT, "Named structure element expected"); + break; + } + + if (mdec->mFlags & DTF_STATIC) + { + mdec->mFlags |= DTF_GLOBAL; + mdec->mVarIndex = -1; + mdec->mQualIdent = mScope->Mangle(mdec->mIdent); + + if (mdec->mValue && (mdec->mBase->mFlags & DTF_CONST)) + { + if (mdec->mBase->IsNumericType()) + { + if (mdec->mValue->mType == EX_CONSTANT) + { + mdec->mType = mdec->mValue->mDecValue->mType; + mdec->mInteger = mdec->mValue->mDecValue->mInteger; + } + } + else if (dec->mBase->mType == DT_TYPE_POINTER) + { + if (dec->mValue->mType == EX_CONSTANT) + { + if (dec->mValue->mDecValue->mType == DT_CONST_ADDRESS || dec->mValue->mDecValue->mType == DT_CONST_POINTER) + { + mdec->mType = mdec->mValue->mDecValue->mType; + mdec->mInteger = mdec->mValue->mDecValue->mInteger; + } + } + } + } + + Declaration * pdec = mCompilationUnits->mScope->Insert(mdec->mQualIdent, mdec); + + if (pdec) + mdec = pdec; + + Declaration* odec = dec->mScope->Insert(mdec->mIdent, mdec); + if (odec) + { + mErrors->Error(mdec->mLocation, EERR_DUPLICATE_DEFINITION, "Duplicate struct member declaration", mdec->mIdent); + mErrors->Error(odec->mLocation, EINFO_ORIGINAL_DEFINITION, "Original definition"); + } + } + else + { + if (dt == DT_TYPE_UNION) + offset = 0; + + mdec->mType = DT_ELEMENT; + mdec->mOffset = offset; + + if (mdec->mBits) + { + if (mdec->mBits <= 8) + { + if (bitsleft == 0) + { + bitsleft = 8 - mdec->mBits; + offset++; + } + else if (bitsleft >= mdec->mBits) + { + mdec->mOffset--; + mdec->mShift = 8 - bitsleft; + bitsleft -= mdec->mBits; + } + else + { + mdec->mOffset--; + mdec->mShift = 8 - bitsleft; + bitsleft = 8 + bitsleft - mdec->mBits; + offset++; + } + } + else if (mdec->mBits <= 16) + { + if (bitsleft == 0) + { + bitsleft = 16 - mdec->mBits; + offset += 2; + } + else if (bitsleft + 8 >= mdec->mBits) + { + mdec->mOffset--; + mdec->mShift = 8 - bitsleft; + bitsleft = bitsleft + 8 - mdec->mBits; + offset++; + } + else if (mdec->mOffset >= 2) + { + mdec->mOffset -= 2; + mdec->mShift = 16 - bitsleft; + bitsleft = bitsleft + 16 - mdec->mBits; + offset += 2; + } + else + { + mdec->mOffset--; + mdec->mShift = 8 - bitsleft; + bitsleft = bitsleft + 16 - mdec->mBits; + offset += 2; + } + } + else if (mdec->mBits <= 24) + { + if (bitsleft == 0) + { + bitsleft = 24 - mdec->mBits; + offset += 3; + } + else if (bitsleft + 16 >= mdec->mBits) + { + if (mdec->mOffset >= 2) + { + mdec->mOffset -= 2; + mdec->mShift = 16 - bitsleft; + bitsleft = bitsleft + 16 - mdec->mBits; + offset += 2; + } + else + { + mdec->mOffset--; + mdec->mShift = 8 - bitsleft; + bitsleft = bitsleft + 16 - mdec->mBits; + offset += 2; + } + } + else if (mdec->mOffset >= 2) + { + mdec->mOffset -= 2; + mdec->mShift = 16 - bitsleft; + bitsleft = bitsleft + 24 - mdec->mBits; + offset += 3; + } + else + { + mdec->mOffset--; + mdec->mShift = 8 - bitsleft; + bitsleft = bitsleft + 24 - mdec->mBits; + offset += 3; + } + } + else + { + if (bitsleft == 0) + { + bitsleft = 32 - mdec->mBits; + offset += 4; + } + else if (bitsleft + 24 >= mdec->mBits) + { + mdec->mOffset--; + mdec->mShift = 8 - bitsleft; + bitsleft = bitsleft + 24 - mdec->mBits; + offset+=3; + } + else + { + bitsleft = 32 - mdec->mBits; + offset += mdec->mBase->mSize; + } + } + + if (mdec->mBits == 24 && mdec->mOffset > 0 && mdec->mShift == 0) + { + mdec->mOffset--; + mdec->mShift = 8; + } + + if (mdec->mShift == 0 && mdec->mBits == 8 * mdec->mSize) + mdec->mBits = 0; + } + else + { + if (bitsleft && mlast) + { + Declaration* pdec = new Declaration(mdec->mLocation, DT_PADDING); + pdec->mIdent = Ident::Unique("__pad"); + pdec->mBase = TheUnsignedCharTypeDeclaration; + pdec->mOffset = mlast->mOffset; + pdec->mBits = bitsleft; + pdec->mShift = 8 - bitsleft; + pdec->mSize = 1; + bitsleft = 0; + mlast->mNext = pdec; + mlast = pdec; + } + + int alignment = mdec->mBase->mAlignment; + if (alignment == 0) + alignment = 1; + + bitsleft = 0; + if (mdec->mBase->mType == DT_TYPE_ARRAY && mdec->mBase->mBase->IsSimpleType() && mdec->mBase->mBase->mSize > 1) + alignment = mdec->mBase->mBase->mSize; + + offset = (offset + alignment - 1) & ~(alignment - 1); + offset += mdec->mBase->mSize; + + if (alignment > dec->mAlignment) + dec->mAlignment = alignment; + } + + if (offset > dec->mSize) + dec->mSize = offset; + + Declaration* odec = dec->mScope->Insert(mdec->mIdent, mdec); + if (odec) + { + mErrors->Error(mdec->mLocation, EERR_DUPLICATE_DEFINITION, "Duplicate struct member declaration", mdec->mIdent); + mErrors->Error(odec->mLocation, EINFO_ORIGINAL_DEFINITION, "Original definition"); + } + + if (dec->mConst) + { + Declaration* cmdec = mdec->Clone(); + cmdec->mBase = mdec->mBase->ToConstType(); + + dec->mConst->mScope->Insert(cmdec->mIdent, cmdec); + dec->mConst->mSize = dec->mSize; + } + + if (mlast) + mlast->mNext = mdec; + else + dec->mParams = mdec; + mlast = mdec; + } + + mdec = mdec->mNext; + } + + if (mScanner->mToken == TK_SEMICOLON) + mScanner->NextToken(); + else + { + mErrors->Error(mScanner->mLocation, EERR_SYNTAX, "';' expected"); + break; + } + } + } + } + } + + if (bitsleft && mlast) + { + Declaration* pdec = new Declaration(mlast->mLocation, DT_PADDING); + pdec->mIdent = Ident::Unique("__pad"); + pdec->mBase = TheUnsignedCharTypeDeclaration; + pdec->mOffset = mlast->mOffset; + pdec->mBits = bitsleft; + pdec->mShift = 8 - bitsleft; + pdec->mSize = 1; + bitsleft = 0; + mlast->mNext = pdec; + mlast = pdec; + } + + if (mlast) + mlast->mNext = nullptr; + else + dec->mParams = nullptr; + } + + dec->SetDefined(); + if (dec->mConst) + dec->mConst->mSize = dec->mSize; + + if ((mCompilerOptions & COPT_CPLUSPLUS) && dec->mType == DT_TYPE_STRUCT && dec->mIdent) + { + if (needVTable) + { + Declaration* vdec = mCompilationUnits->mVTableScope->Lookup(dec->mQualIdent); + if (!vdec) + { + vdec = new Declaration(dec->mLocation, DT_VTABLE); + + if (dec->mBase) + { + vdec->mBase = dec->mBase->mBase->mVTable; + vdec->mSize = vdec->mBase->mSize; + vdec->mOffset = vdec->mBase->mOffset; + } + else + { + vdec->mOffset = 0; + } + + vdec->mDefaultConstructor = new Declaration(dec->mLocation, DT_CONST_INTEGER); + vdec->mDefaultConstructor->mBase = TheConstCharTypeDeclaration; + vdec->mDefaultConstructor->mSize = 1; + vdec->mDefaultConstructor->mInteger = -1; + + vdec->mClass = dec; + vdec->mScope = new DeclarationScope(nullptr, SLEVEL_CLASS); + vdec->mIdent = dec->mIdent; + vdec->mQualIdent = dec->mQualIdent; + mCompilationUnits->mVTableScope->Insert(vdec->mQualIdent, vdec); + } + + dec->mVTable = vdec; + + if (!dec->mBase) + { + Declaration* mdec = dec->mParams; + while (mdec) + { + mdec->mOffset++; + mdec = mdec->mNext; + } + dec->mSize++; + if (dec->mConst) + dec->mConst->mSize = dec->mSize; + } + + // make sure virtual destructors end up in vtable + AddDefaultConstructors(pthis); + + dec->mScope->Iterate([=](const Ident* ident, Declaration* mdec) + { + if (mdec->mType == DT_CONST_FUNCTION) + { + while (mdec) + { + if (mdec->mBase->mFlags & DTF_VIRTUAL) + { + if (mdec->mFlags & DTF_PURE_VIRTUAL) + dec->mFlags |= DTF_PURE_VIRTUAL; + + Declaration* vpdec = vdec; + Declaration* vmdec; + if (mdec->mIdent->mString[0] == '~') + { + // this is the destructor, need special search + + vmdec = vpdec->mScope->Lookup(vpdec->mIdent->PreMangle("~")); + while (!vmdec && vpdec->mBase) + { + vpdec = vpdec->mBase; + vmdec = vpdec->mScope->Lookup(vpdec->mIdent->PreMangle("~")); + } + } + else + { + vmdec = vpdec->mScope->Lookup(ident); + while (!vmdec && vpdec->mBase) + { + vpdec = vpdec->mBase; + vmdec = vpdec->mScope->Lookup(ident); + } + } + + Declaration* vmpdec = nullptr; + + while (vmdec && !mdec->mBase->IsDerivedFrom(vmdec->mBase)) + { + vmpdec = vmdec; + vmdec = vmdec->mNext; + } + + if (!vmdec) + { + vmdec = mdec->Clone(); + vmdec->mNext = nullptr; + vmdec->mDefaultConstructor = dec->mVTable; + vmdec->mFlags |= DTF_NATIVE; + + if (vmpdec) + vmpdec->mNext = vmdec; + else + vdec->mScope->Insert(ident, vmdec); + } + + mdec->mVTable = vmdec; + } + + mdec = mdec->mNext; + } + } + } + ); + } + else + AddDefaultConstructors(pthis); + + if (pthis->mBase->mConst) + { + pthis->mBase->mConst->mDestructor = pthis->mBase->mDestructor; + pthis->mBase->mConst->mDefaultConstructor = pthis->mBase->mDefaultConstructor; + pthis->mBase->mConst->mCopyConstructor = pthis->mBase->mCopyConstructor; + pthis->mBase->mConst->mMoveConstructor = pthis->mBase->mMoveConstructor; + pthis->mBase->mConst->mVectorConstructor = pthis->mBase->mVectorConstructor; + pthis->mBase->mConst->mVectorDestructor = pthis->mBase->mVectorConstructor; + pthis->mBase->mConst->mVectorCopyConstructor = pthis->mBase->mVectorCopyConstructor; + pthis->mBase->mConst->mVectorMoveConstructor = pthis->mBase->mVectorMoveConstructor; + } + + // Lookup constructors, have same name as class + Declaration* cdec = dec->mScope->Lookup(dec->mIdent->PreMangle("+"), SLEVEL_SCOPE); + while (cdec) + { + if (cdec->mFlags & DTF_DEFINED) + PrependMemberConstructor(pthis, cdec); + cdec = cdec->mNext; + } + + AppendMemberDestructor(pthis); + + } + + mScope = oscope; + } + + if ((flags & ~dec->mFlags) == DTF_CONST) + dec = dec->ToConstType(); + else if ((flags & ~dec->mFlags) == DTF_VOLATILE) + dec = dec->ToVolatileType(); + + return dec; +} + +Declaration* Parser::ParseBaseTypeQualify(bool qualified, Declaration* dec, const Ident*& pident) +{ + while (dec && (dec->mType == DT_NAMESPACE || (qualified && (dec->mType == DT_TYPE_STRUCT || dec->mType == DT_TYPE_TEMPLATE)) && !dec->mTemplate) && ConsumeTokenIf(TK_COLCOLON)) + { + if (ExpectToken(TK_IDENT)) + { + pident = mScanner->mTokenIdent; + if (dec->mType == DT_TYPE_TEMPLATE) + { + Declaration* ndec = new Declaration(mScanner->mLocation, DT_TYPE_TEMPLATE); + ndec->mFlags |= DTF_DEFINED; + ndec->mBase = dec; + ndec->mIdent = pident; + dec = ndec; + } + else + dec = dec->mScope->Lookup(pident); + + mScanner->NextToken(); + } + } + + return dec; +} + +Declaration* Parser::ParseBaseTypeDeclaration(uint64 flags, bool qualified, Declaration* ptempl) +{ + Declaration* dec = nullptr; + const Ident* pident = nullptr; + + switch (mScanner->mToken) + { + case TK_UNSIGNED: + dec = new Declaration(mScanner->mLocation, DT_TYPE_INTEGER); + dec->mFlags = flags | DTF_DEFINED; + mScanner->NextToken(); + if (mScanner->mToken == TK_INT) + { + dec->mSize = 2; + mScanner->NextToken(); + } + else if (mScanner->mToken == TK_SHORT) + { + dec->mSize = 2; + mScanner->NextToken(); + ConsumeTokenIf(TK_INT); + } + else if (mScanner->mToken == TK_LONG) + { + dec->mSize = 4; + mScanner->NextToken(); + ConsumeTokenIf(TK_INT); + } + else if (mScanner->mToken == TK_CHAR) + { + dec->mSize = 1; + mScanner->NextToken(); + } + else + dec->mSize = 2; + + break; + + case TK_SIGNED: + dec = new Declaration(mScanner->mLocation, DT_TYPE_INTEGER); + dec->mFlags = flags | DTF_DEFINED | DTF_SIGNED; + mScanner->NextToken(); + if (mScanner->mToken == TK_INT) + { + dec->mSize = 2; + mScanner->NextToken(); + } + else if (mScanner->mToken == TK_SHORT) + { + dec->mSize = 2; + mScanner->NextToken(); + ConsumeTokenIf(TK_INT); + } + else if (mScanner->mToken == TK_LONG) + { + dec->mSize = 4; + mScanner->NextToken(); + ConsumeTokenIf(TK_INT); + } + else if (mScanner->mToken == TK_CHAR) + { + dec->mSize = 1; + mScanner->NextToken(); + } + else + dec->mSize = 2; + break; + + case TK_CONST: + mScanner->NextToken(); + return ParseBaseTypeDeclaration(flags | DTF_CONST, qualified); + + case TK_VOLATILE: + mScanner->NextToken(); + return ParseBaseTypeDeclaration(flags | DTF_VOLATILE, qualified); + + case TK_MEMMAP: + mScanner->NextToken(); + return ParseBaseTypeDeclaration(flags | DTF_MEMMAP, qualified); + + case TK_TYPENAME: + mScanner->NextToken(); + return ParseBaseTypeDeclaration(flags, qualified); + + case TK_LONG: + dec = new Declaration(mScanner->mLocation, DT_TYPE_INTEGER); + dec->mSize = 4; + mScanner->NextToken(); + if (ConsumeTokenIf(TK_UNSIGNED)) + dec->mFlags = flags | DTF_DEFINED; + else if (ConsumeTokenIf(TK_SIGNED)) + dec->mFlags = flags | DTF_DEFINED | DTF_SIGNED; + else + dec->mFlags = flags | DTF_DEFINED | DTF_SIGNED; + ConsumeTokenIf(TK_INT); + break; + + case TK_SHORT: + dec = new Declaration(mScanner->mLocation, DT_TYPE_INTEGER); + dec->mSize = 2; + mScanner->NextToken(); + if (ConsumeTokenIf(TK_UNSIGNED)) + dec->mFlags = flags | DTF_DEFINED; + else if (ConsumeTokenIf(TK_SIGNED)) + dec->mFlags = flags | DTF_DEFINED | DTF_SIGNED; + else + dec->mFlags = flags | DTF_DEFINED | DTF_SIGNED; + ConsumeTokenIf(TK_INT); + break; + + case TK_INT: + dec = new Declaration(mScanner->mLocation, DT_TYPE_INTEGER); + dec->mSize = 2; + dec->mFlags = flags | DTF_DEFINED | DTF_SIGNED; + mScanner->NextToken(); + break; + + case TK_CHAR: + dec = new Declaration(mScanner->mLocation, DT_TYPE_INTEGER); + dec->mSize = 1; + dec->mFlags = flags | DTF_DEFINED; + mScanner->NextToken(); + break; + + case TK_BOOL: + dec = new Declaration(mScanner->mLocation, DT_TYPE_BOOL); + dec->mSize = 1; + dec->mFlags = flags | DTF_DEFINED; + mScanner->NextToken(); + break; + + case TK_FLOAT: + dec = new Declaration(mScanner->mLocation, DT_TYPE_FLOAT); + dec->mSize = 4; + dec->mFlags = flags | DTF_DEFINED | DTF_SIGNED; + mScanner->NextToken(); + break; + + case TK_VOID: + dec = new Declaration(mScanner->mLocation, DT_TYPE_VOID); + dec->mSize = 0; + dec->mFlags = flags | DTF_DEFINED; + mScanner->NextToken(); + break; + + case TK_AUTO: + dec = new Declaration(mScanner->mLocation, DT_TYPE_AUTO); + dec->mSize = 0; + dec->mFlags = flags | DTF_DEFINED; + mScanner->NextToken(); + break; + + case TK_DECLTYPE: + { + mScanner->NextToken(); + Expression* exp = ParseExpression(true); + dec = exp->mDecType; + } + break; + + case TK_COLCOLON: + case TK_IDENT: + { + DeclarationScope* scope = mScope; + if (mScanner->mToken == TK_COLCOLON) + { + mScanner->NextToken(); + while (scope->mLevel > SLEVEL_STATIC) + scope = scope->mParent; + if (!ExpectToken(TK_IDENT)) + break; + } + + pident = mScanner->mTokenIdent; + mScanner->NextToken(); + + if (mTemplateScope) + { + dec = mTemplateScope->Lookup(pident); + if (dec && mScanner->mToken == TK_LESS_THAN && !dec->mTemplate) + dec = nullptr; + } + + if (!dec && mThisPointer && !mScope->Lookup(pident, SLEVEL_FUNCTION)) + { + int offset; + uint64 flags; + + if (mThisPointer->mType == DT_ARGUMENT) + dec = MemberLookup(mThisPointer->mBase->mBase, mScanner->mTokenIdent, offset, flags); + else if (mThisPointer->mType == DT_TYPE_POINTER) + dec = MemberLookup(mThisPointer->mBase, mScanner->mTokenIdent, offset, flags); + + if (dec && dec->mTemplate) + dec = ParseTemplateExpansion(dec->mTemplate, nullptr); + } + + if (!dec) + { + dec = mScope->Lookup(pident); + if (dec && dec->mType == DT_CONST_FUNCTION && mScope->mLevel == SLEVEL_CLASS) + dec = mScope->mParent->Lookup(pident); + + dec = ParseBaseTypeQualify(qualified, dec, pident); + + if (dec && dec->mTemplate) + dec = ParseTemplateExpansion(dec->mTemplate, nullptr); + } + + dec = ParseBaseTypeQualify(qualified, dec, pident); + + if (dec) + { + if (dec->mType <= DT_TYPE_FUNCTION) + { + if ((flags & ~dec->mFlags) == DTF_CONST) + dec = dec->ToConstType(); + else if ((flags & ~dec->mFlags) == DTF_VOLATILE) + dec = dec->ToVolatileType(); + else if (dec->IsSimpleType() && (flags & ~dec->mFlags)) + { + Declaration* ndec = new Declaration(dec->mLocation, dec->mType); + ndec->mFlags = dec->mFlags | flags; + ndec->mSize = dec->mSize; + ndec->mBase = dec->mBase; + dec = ndec; + } + else if (dec->mType == DT_TYPE_STRUCT && (flags & ~dec->mFlags)) + { + if ((flags & ~dec->mFlags) == DTF_CONST) + dec = dec->ToConstType(); + else if ((flags & ~dec->mFlags) == DTF_VOLATILE) + dec = dec->ToVolatileType(); + else + { + Declaration* ndec = new Declaration(dec->mLocation, dec->mType); + ndec->mFlags = dec->mFlags | flags; + ndec->mSize = dec->mSize; + ndec->mBase = dec->mBase; + ndec->mScope = dec->mScope; + ndec->mParams = dec->mParams; + ndec->mIdent = dec->mIdent; + ndec->mQualIdent = dec->mQualIdent; + dec = ndec; + } + } + } + else if (dec->mType == DT_PACK_TEMPLATE || dec->mType == DT_PACK_TYPE) + { + if ((flags & ~dec->mFlags) == DTF_CONST) + dec = dec->ToConstType(); + } + else + mErrors->Error(mScanner->mLocation, EERR_NOT_A_TYPE, "Identifier is no type", dec->mQualIdent); + } + else + mErrors->Error(mScanner->mLocation, EERR_OBJECT_NOT_FOUND, "Identifier not defined", pident); + + break; + } + + case TK_ENUM: + { + dec = new Declaration(mScanner->mLocation, DT_TYPE_ENUM); + dec->mFlags = flags; + dec->mSize = 1; + dec->mScope = new DeclarationScope(nullptr, SLEVEL_CLASS); + + bool classTemplate = false, baseClass = false; + + mScanner->NextToken(); + + if (mCompilerOptions & COPT_CPLUSPLUS) + { + if (ConsumeTokenIf(TK_CLASS) || ConsumeTokenIf(TK_STRUCT)) + classTemplate = true; + } + + Declaration* odec = nullptr; + + if (mScanner->mToken == TK_IDENT) + { + dec->mIdent = mScanner->mTokenIdent; + dec->mQualIdent = mScope->Mangle(dec->mIdent); + + mScanner->NextToken(); + if (mScanner->mToken != TK_OPEN_BRACE) + odec = mScope->mParent->Lookup(dec->mIdent); + if (!odec) + odec = mScope->Insert(dec->mIdent, dec); + + } + + if (mCompilerOptions & COPT_CPLUSPLUS) + { + if (ConsumeTokenIf(TK_COLON)) + { + Declaration* pdec = ParseBaseTypeDeclaration(0, false); + if (pdec->mType == DT_TYPE_INTEGER) + { + dec->mSize = pdec->mSize; + dec->mFlags |= pdec->mFlags & DTF_SIGNED; + baseClass = true; + } + else + mErrors->Error(pdec->mLocation, EERR_INCOMPATIBLE_TYPES, "Integer base type expected"); + } + } + + int nitem = 0; + if (mScanner->mToken == TK_OPEN_BRACE) + { + if (odec) + { + mErrors->Error(dec->mLocation, EERR_DUPLICATE_DEFINITION, "Duplicate name", dec->mIdent); + mErrors->Error(odec->mLocation, EINFO_ORIGINAL_DEFINITION, "Original definition"); + } + + mScanner->NextToken(); + if (mScanner->mToken == TK_IDENT) + { + int64 minValue = 0, maxValue = 0; + + for (;;) + { + Declaration* cdec = new Declaration(mScanner->mLocation, DT_CONST_INTEGER); + cdec->mBase = dec; + cdec->mSize = 2; + if (mScanner->mToken == TK_IDENT) + { + cdec->mIdent = mScanner->mTokenIdent; + cdec->mQualIdent = mScope->Mangle(cdec->mIdent); + Declaration* odec; + if (classTemplate) + odec = dec->mScope->Insert(cdec->mIdent, cdec); + else + odec = mScope->Insert(cdec->mIdent, cdec); + + if (odec) + { + mErrors->Error(mScanner->mLocation, EERR_DUPLICATE_DEFINITION, "Duplicate declaration", mScanner->mTokenIdent->mString); + mErrors->Error(odec->mLocation, EINFO_ORIGINAL_DEFINITION, "Original definition"); + } + mScanner->NextToken(); + } + if (mScanner->mToken == TK_ASSIGN) + { + mScanner->NextToken(); + Expression* exp = ParseRExpression(); + if (exp->mType == EX_CONSTANT && exp->mDecValue->mType == DT_CONST_INTEGER) + nitem = int(exp->mDecValue->mInteger); + else + mErrors->Error(mScanner->mLocation, EERR_CONSTANT_TYPE, "Integer constant expected"); + } + cdec->mInteger = nitem++; + if (cdec->mInteger < minValue) + minValue = cdec->mInteger; + else if (cdec->mInteger > maxValue) + maxValue = cdec->mInteger; + + cdec->mNext = dec->mParams; + dec->mParams = cdec; + + if (mScanner->mToken == TK_COMMA) + { + mScanner->NextToken(); + if (mScanner->mToken == TK_CLOSE_BRACE) + break; + } + else + break; + } + + dec->mMinValue = minValue; + dec->mMaxValue = maxValue; + + if (minValue < 0) + { + if (baseClass) + { + if (dec->mFlags & DTF_SIGNED) + { + if (minValue < -128 && dec->mSize == 1) + mErrors->Error(mScanner->mLocation, EWARN_INVALID_VALUE_RANGE, "Enum constant out of bounds"); + } + else + mErrors->Error(mScanner->mLocation, EWARN_INVALID_VALUE_RANGE, "Enum constant out of bounds"); + } + else + { + dec->mFlags |= DTF_SIGNED; + if (minValue < -128 || maxValue > 127) + dec->mSize = 2; + } + } + else if (maxValue > 255) + { + if (baseClass) + { + if (dec->mSize == 1) + mErrors->Error(mScanner->mLocation, EWARN_INVALID_VALUE_RANGE, "Enum constant out of bounds"); + } + else + dec->mSize = 2; + } + } + + if (mScanner->mToken == TK_CLOSE_BRACE) + mScanner->NextToken(); + else + mErrors->Error(mScanner->mLocation, EERR_SYNTAX, "'}' expected"); + } + else if (odec) + { + if (odec->mType != DT_TYPE_ENUM) + { + mErrors->Error(dec->mLocation, EERR_DUPLICATE_DEFINITION, "Duplicate name", dec->mIdent); + mErrors->Error(odec->mLocation, EINFO_ORIGINAL_DEFINITION, "Original definition"); + } + } + else + mErrors->Error(mScanner->mLocation, EERR_SYNTAX, "'{' expected"); + + dec->mFlags |= DTF_DEFINED; + break; + } + case TK_STRUCT: + dec = ParseStructDeclaration(flags, DT_TYPE_STRUCT, ptempl); + break; + case TK_CLASS: + dec = ParseStructDeclaration(flags | DTF_PRIVATE | DTF_PROTECTED, DT_TYPE_STRUCT, ptempl); + break; + case TK_UNION: + dec = ParseStructDeclaration(flags, DT_TYPE_UNION, ptempl); + break; + default: + mErrors->Error(mScanner->mLocation, EERR_SYNTAX, "Declaration starts with invalid token", TokenNames[mScanner->mToken]); + mScanner->NextToken(); + } + + if (!dec) + { + dec = new Declaration(mScanner->mLocation, DT_TYPE_VOID); + dec->mSize = 0; + dec->mFlags |= DTF_DEFINED; + } + + if (mScanner->mToken == TK_CONST) + { + dec = dec->ToConstType(); + mScanner->NextToken(); + } + + return dec; + +} + +Declaration* Parser::ParsePostfixDeclaration(bool autoBase) +{ + Declaration* dec; + + if (mScanner->mToken == TK_MUL) + { + mScanner->NextToken(); + + Declaration* ndec = new Declaration(mScanner->mLocation, DT_TYPE_POINTER); + ndec->mSize = 2; + ndec->mFlags |= DTF_DEFINED; + ndec->mCompilerOptions = mCompilerOptions; + + for (;;) + { + if (mScanner->mToken == TK_CONST) + { + ndec->mFlags |= DTF_CONST; + mScanner->NextToken(); + } + else if (mScanner->mToken == TK_VOLATILE) + { + ndec->mFlags |= DTF_VOLATILE; + mScanner->NextToken(); + } + else if (mScanner->mToken == TK_MEMMAP) + { + ndec->mFlags |= DTF_MEMMAP; + mScanner->NextToken(); + } + else + break; + } + + Declaration* dec = ParsePostfixDeclaration(false); + ndec->mBase = dec; + return ndec; + } + else if (mScanner->mToken == TK_BINARY_AND && (mCompilerOptions & COPT_CPLUSPLUS)) + { + mScanner->NextToken(); + + Declaration* ndec = new Declaration(mScanner->mLocation, DT_TYPE_REFERENCE); + ndec->mSize = 2; + ndec->mFlags |= DTF_DEFINED; + + for (;;) + { + if (mScanner->mToken == TK_CONST) + { + ndec->mFlags |= DTF_CONST; + mScanner->NextToken(); + } + else if (mScanner->mToken == TK_VOLATILE) + { + ndec->mFlags |= DTF_VOLATILE; + mScanner->NextToken(); + } + else if (mScanner->mToken == TK_MEMMAP) + { + ndec->mFlags |= DTF_MEMMAP; + mScanner->NextToken(); + } + else + break; + } + + Declaration* dec = ParsePostfixDeclaration(autoBase); + ndec->mBase = dec; + return ndec; + } + else if (mScanner->mToken == TK_LOGICAL_AND && (mCompilerOptions & COPT_CPLUSPLUS)) + { + mScanner->NextToken(); + + Declaration* ndec = new Declaration(mScanner->mLocation, DT_TYPE_RVALUEREF); + ndec->mSize = 2; + ndec->mFlags |= DTF_DEFINED; + + for (;;) + { + if (mScanner->mToken == TK_CONST) + { + ndec->mFlags |= DTF_CONST; + mScanner->NextToken(); + } + else if (mScanner->mToken == TK_VOLATILE) + { + ndec->mFlags |= DTF_VOLATILE; + mScanner->NextToken(); + } + else if (mScanner->mToken == TK_MEMMAP) + { + ndec->mFlags |= DTF_VOLATILE; + mScanner->NextToken(); + } + else + break; + } + + Declaration* dec = ParsePostfixDeclaration(autoBase); + ndec->mBase = dec; + return ndec; + } + else if (ConsumeTokenIf(TK_OPEN_PARENTHESIS)) + { + Declaration* vdec = ParsePostfixDeclaration(false); + if (mScanner->mToken == TK_CLOSE_PARENTHESIS) + mScanner->NextToken(); + else + mErrors->Error(mScanner->mLocation, EERR_SYNTAX, "')' expected"); + dec = vdec; + } + else if (ConsumeTokenIf(TK_ELLIPSIS)) + { + if (mScanner->mToken == TK_IDENT) + { + dec = new Declaration(mScanner->mLocation, DT_PACK_VARIABLE); + dec->mIdent = mScanner->mTokenIdent; + dec->mSection = mBSSection; + dec->mBase = nullptr; + mScanner->NextToken(); + } + else + { + dec = new Declaration(mScanner->mLocation, DT_PACK_ANON); + dec->mIdent = mScanner->mTokenIdent; + dec->mBase = nullptr; + } + } + else if (mScanner->mToken == TK_IDENT) + { + dec = new Declaration(mScanner->mLocation, DT_VARIABLE); + dec->mIdent = mScanner->mTokenIdent; + dec->mSection = mBSSection; + dec->mBase = nullptr; + mScanner->NextToken(); + + if (mScanner->mToken == TK_LESS_THAN && mTemplateScope) + { + Declaration* tdec = mScope->Lookup(dec->mIdent); + if (tdec && tdec->mTemplate) + { + // for now just skip over template stuff + int depth = 0; + do { + if (ConsumeTokenIf(TK_GREATER_THAN)) + depth--; + else if (ConsumeTokenIf(TK_LESS_THAN)) + depth++; + else + mScanner->NextToken(); + } while (depth); + + if (mTemplateScope->mParent) + { + if (mTemplateScope->mParent->mName) + dec->mIdent = dec->mIdent->Mangle(mTemplateScope->mParent->mName->mString); + } + else if (mTemplateScope->mName) + dec->mIdent = dec->mIdent->Mangle(mTemplateScope->mName->mString); + } + } + + dec->mQualIdent = mScope->Mangle(dec->mIdent); + + if (mScanner->mToken == TK_OPEN_PARENTHESIS && mScope->mLevel >= SLEVEL_FUNCTION) + { + // Can't be a function declaration in local context, so it must be an object + // declaration with initializer, so return immediately + + return dec; + } + + while (ConsumeTokenIf(TK_COLCOLON)) + { + if (mScanner->mToken == TK_IDENT) + { + dec->mIdent = mScanner->mTokenIdent; + + char buffer[200]; + strcpy_s(buffer, dec->mQualIdent->mString); + strcat_s(buffer, "::"); + strcat_s(buffer, dec->mIdent->mString); + dec->mQualIdent = Ident::Unique(buffer); + + mScanner->NextToken(); + } + } + } + else if (autoBase && mScanner->mToken == TK_OPEN_BRACKET) + { + dec = new Declaration(mScanner->mLocation, DT_VARIABLE); + dec->mSection = mBSSection; + dec->mBase = nullptr; + + mScanner->NextToken(); + Declaration* sbind = nullptr, * spp = nullptr; + while (mScanner->mToken == TK_IDENT) + { + Declaration* sp = new Declaration(mScanner->mLocation, DT_BINDING); + sp->mBase = dec; + sp->mIdent = mScanner->mTokenIdent; + if (spp) + spp->mNext = sp; + else + sbind = sp; + spp = sp; + mScanner->NextToken(); + if (!ConsumeTokenIf(TK_COMMA)) + break; + } + ConsumeToken(TK_CLOSE_BRACKET); + dec->mParams = sbind; + } + else + { + dec = new Declaration(mScanner->mLocation, DT_ANON); + dec->mBase = nullptr; + } + + for (;;) + { + if (mScanner->mToken == TK_OPEN_BRACKET) + { + Declaration* ndec = new Declaration(mScanner->mLocation, DT_TYPE_ARRAY); + ndec->mSize = 0; + ndec->mFlags = 0; + mScanner->NextToken(); + if (mScanner->mToken != TK_CLOSE_BRACKET) + { + Expression* exp = ParseRExpression(); + if (exp->mType == EX_CONSTANT && exp->mDecType->IsIntegerType()) + { + if (exp->mDecValue->mType == DT_CONST_INTEGER) + ndec->mSize = int(exp->mDecValue->mInteger); + else if (exp->mDecValue->mType == DT_CONST_TEMPLATE) + { + ndec->mSize = 0; + ndec->mTemplate = exp->mDecValue; + } + else + mErrors->Error(exp->mLocation, EERR_CONSTANT_TYPE, "Constant integer expression expected"); + } + else + mErrors->Error(exp->mLocation, EERR_CONSTANT_TYPE, "Constant integer expression expected"); + ndec->mFlags |= DTF_DEFINED; + } + if (mScanner->mToken == TK_CLOSE_BRACKET) + mScanner->NextToken(); + else + mErrors->Error(mScanner->mLocation, EERR_SYNTAX, "']' expected"); + ndec->mBase = dec; + dec = ndec; + } + else if (ConsumeTokenIf(TK_OPEN_PARENTHESIS)) + { + if (mScanner->mToken == TK_CLOSE_PARENTHESIS || mScanner->mToken == TK_ELLIPSIS || IsTypeToken()) + { + mScanner->UngetToken(TK_OPEN_PARENTHESIS); + dec = ParseFunctionDeclaration(dec); + } + else + { + mScanner->UngetToken(TK_OPEN_PARENTHESIS); + return dec; + } + } + else + return dec; + } +} + +Declaration * Parser::ParseFunctionDeclaration(Declaration* bdec) +{ + Declaration* ndec = new Declaration(mScanner->mLocation, DT_TYPE_FUNCTION); + ndec->mSize = 0; + Declaration* pdec = nullptr; + mScanner->NextToken(); + if (mScanner->mToken != TK_CLOSE_PARENTHESIS) + { + int vi = 0; + for (;;) + { + if (mScanner->mToken == TK_ELLIPSIS) + { + ndec->mFlags |= DTF_VARIADIC; + mScanner->NextToken(); + break; + } + + Declaration* bdec = ParseBaseTypeDeclaration(0, true); +// if (bdec->mType == DT_PACK_TEMPLATE) +// ConsumeToken(TK_ELLIPSIS); + + Declaration* adec = ParsePostfixDeclaration(false); + adec = ReverseDeclaration(adec, bdec); + + if (adec->mBase->mType == DT_TYPE_VOID) + { + if (pdec) + mErrors->Error(pdec->mLocation, EERR_WRONG_PARAMETER, "Invalid void argument"); + break; + } + else + { + if (adec->mType != DT_PACK_VARIABLE && !(adec->mBase->mFlags & DTF_DEFINED) && adec->mBase->mType != DT_TYPE_ARRAY && !adec->mBase->mTemplate) + mErrors->Error(adec->mLocation, EERR_UNDEFINED_OBJECT, "Type of argument not defined"); + + if (adec->mType == DT_PACK_VARIABLE || adec->mType == DT_PACK_ANON) + { + adec->mType = DT_PACK_ARGUMENT; + if (adec->mBase) + { + ndec->mParamPack = adec; + + Declaration* atdec = adec->mBase; + + if (atdec->IsReference()) + atdec = atdec->mBase; + if (atdec->mType == DT_PACK_TYPE) + { + atdec = atdec->mParams; + + Declaration* apdec = adec; + while (atdec) + { + apdec = new Declaration(adec->mLocation, DT_ARGUMENT); + if (!adec->mParams) + adec->mParams = apdec; + + if (adec->mBase->IsReference()) + { + if (adec->mBase->mBase->mFlags & DTF_CONST) + apdec->mBase = atdec->ToConstType()->BuildReference(adec->mLocation, adec->mBase->mType); + else + apdec->mBase = atdec->BuildReference(adec->mLocation, adec->mBase->mType); + } + else + apdec->mBase = atdec; + + atdec = atdec->mNext; + apdec->mFlags = DTF_DEFINED; + apdec->mVarIndex = vi; + apdec->mOffset = 0; + + if (apdec->mBase->mType == DT_TYPE_ARRAY) + { + Declaration* ndec = new Declaration(apdec->mBase->mLocation, DT_TYPE_POINTER); + ndec->mBase = apdec->mBase->mBase; + ndec->mSize = 2; + ndec->mFlags |= DTF_DEFINED; + apdec->mBase = ndec; + } + + apdec->mSize = apdec->mBase->mSize; + + vi += apdec->mSize; + if (pdec) + pdec->mNext = apdec; + else + ndec->mParams = apdec; + pdec = apdec; + } + } + else + { + if (pdec) + pdec->mNext = adec; + else + ndec->mParams = adec; + pdec = adec; + } + } + } + else + { + adec->mType = DT_ARGUMENT; + adec->mVarIndex = vi; + adec->mOffset = 0; + if (adec->mBase->mType == DT_TYPE_ARRAY && !adec->mBase->mTemplate) + { + Declaration* ndec = new Declaration(adec->mBase->mLocation, DT_TYPE_POINTER); + ndec->mBase = adec->mBase->mBase; + ndec->mSize = 2; + ndec->mFlags |= DTF_DEFINED; + adec->mBase = ndec; + } + + adec->mSize = adec->mBase->mSize; + + if (((mCompilerOptions & COPT_CPLUSPLUS) || !(mCompilerOptions & COPT_STRICT)) && ConsumeTokenIf(TK_ASSIGN)) + { + adec->mValue = ParseExpression(false); + } + + vi += adec->mSize; + if (pdec) + pdec->mNext = adec; + else + ndec->mParams = adec; + pdec = adec; + } + + if (mScanner->mToken == TK_COMMA) + mScanner->NextToken(); + else + break; + } + } + + if (mScanner->mToken == TK_CLOSE_PARENTHESIS) + mScanner->NextToken(); + else + mErrors->Error(mScanner->mLocation, EERR_SYNTAX, "')' expected"); + } + else + mScanner->NextToken(); + ndec->mBase = bdec; + ndec->mFlags |= DTF_DEFINED; + + return ndec; +} + +Declaration* Parser::ReverseDeclaration(Declaration* odec, Declaration* bdec) +{ + Declaration* cdec = odec->mBase; + + if (bdec) + { + if (odec->mType == DT_TYPE_ARRAY) + { + odec->mSize *= bdec->mSize; + odec->mFlags |= bdec->mFlags & DTF_CONST; + } + else if (odec->mType == DT_VARIABLE || odec->mType == DT_ARGUMENT || odec->mType == DT_ANON) + odec->mSize = bdec->mSize; + odec->mBase = bdec; + } + + if (cdec) + return ReverseDeclaration(cdec, odec); + else + return odec; +} + +Declaration * Parser::CopyConstantInitializer(int offset, Declaration* dtype, Expression* exp) +{ + exp = exp->ConstantDereference(mErrors, mDataSection); + + exp = CoerceExpression(exp, dtype); + + exp = exp->ConstantFold(mErrors, mDataSection); + + Declaration* dec = exp->mDecValue; + + if (exp->mType == EX_CONSTANT) + { + if (!dtype->CanAssign(exp->mDecType)) + { + mErrors->Error(exp->mLocation, EERR_CONSTANT_INITIALIZER, "Incompatible constant initializer"); + } + else + { + if (dec->mType == DT_CONST_FUNCTION && dtype->mType == DT_TYPE_POINTER) + { + // Aggregate entries are linked through mNext, so do not reuse the function + // declaration itself as an initialiser entry. + Declaration* ndec = new Declaration(dec->mLocation, DT_CONST_POINTER); + ndec->mValue = exp; + ndec->mBase = dtype; + dec = ndec; + } + else if (dec->mType == DT_CONST_FLOAT) + { + if (dtype->IsIntegerType() || dtype->mType == DT_TYPE_POINTER) + { + Declaration* ndec = new Declaration(dec->mLocation, DT_CONST_INTEGER); + ndec->mInteger = int(dec->mNumber); + ndec->mBase = dtype; + dec = ndec; + } + else + { + Declaration* ndec = new Declaration(dec->mLocation, DT_CONST_FLOAT); + ndec->mNumber = dec->mNumber; + ndec->mBase = dtype; + dec = ndec; + } + } + else if (dec->mType == DT_CONST_INTEGER) + { + if (dtype->mType == DT_TYPE_FLOAT) + { + Declaration* ndec = new Declaration(dec->mLocation, DT_CONST_FLOAT); + ndec->mNumber = double(dec->mInteger); + ndec->mBase = dtype; + dec = ndec; + } + else + { + Declaration* ndec = new Declaration(dec->mLocation, DT_CONST_INTEGER); + ndec->mInteger = dec->mInteger; + ndec->mBase = dtype; + dec = ndec; + } + } + else if (dec->mType == DT_CONST_DATA) + { + if (dtype->mType == DT_TYPE_POINTER) + { + Declaration* ndec = new Declaration(dec->mLocation, DT_CONST_POINTER); + ndec->mValue = exp; + ndec->mBase = dtype; + dec = ndec; + } + else + { + Declaration* ndec = new Declaration(dec->mLocation, DT_CONST_DATA); + ndec->mSection = dec->mSection; + ndec->mData = dec->mData; + ndec->mSize = dec->mSize; + ndec->mBase = dtype; + dec = ndec; + } + } + else if (dec->mType == DT_CONST_ADDRESS) + { + Declaration* ndec = new Declaration(dec->mLocation, DT_CONST_ADDRESS); + ndec->mInteger = dec->mInteger; + ndec->mBase = dtype; + dec = ndec; + } + + dec->mOffset = offset; + } + } + else if (!dec && exp->mType == EX_CONSTRUCT) + { + dec = new Declaration(exp->mLocation, DT_CONST_CONSTRUCTOR); + dec->mValue = exp; + dec->mBase = exp->mDecType; + dec->mOffset = offset; + } + else if (dec && dtype->mType == DT_TYPE_POINTER && (dec->mType == DT_VARIABLE || dec->mType == DT_VARIABLE_REF) && exp->mDecType->mType == DT_TYPE_ARRAY && (dec->mFlags & DTF_GLOBAL)) + { + if (dtype->CanAssign(exp->mDecType)) + { + Declaration * ndec = new Declaration(dec->mLocation, DT_CONST_POINTER); + ndec->mValue = exp; + ndec->mBase = dtype; + dec = ndec; + dec->mOffset = offset; + } + else + { + dtype->CanAssign(exp->mDecType); + mErrors->Error(exp->mLocation, EERR_CONSTANT_INITIALIZER, "Incompatible constant initializer"); + } + } + else + mErrors->Error(exp->mLocation, EERR_CONSTANT_INITIALIZER, "Constant initializer expected"); + + return dec; +} + +uint8* Parser::ParseStringLiteral(int & msize) +{ + int size = mScanner->mTokenStringSize; + if (size + 1 > msize) + msize = size + 1; + uint8* d = new uint8[msize]; + + int i = 0; + while (i < size) + { + d[i] = mCharMap[mScanner->mTokenString[i]]; + i++; + } + + mScanner->NextToken(); + + while (mScanner->mToken == TK_PREP_PRAGMA) + { + mScanner->NextToken(); + ParsePragma(); + } + + // automatic string concatenation + while (mScanner->mToken == TK_STRING) + { + int s = mScanner->mTokenStringSize; + + if (size + s + 1 > msize) + msize = size + s + 1; + + uint8* nd = new uint8[msize]; + memcpy(nd, d, size); + int i = 0; + while (i < s) + { + nd[i + size] = mCharMap[mScanner->mTokenString[i]]; + i++; + } + size += s; + delete[] d; + d = nd; + mScanner->NextToken(); + + while (mScanner->mToken == TK_PREP_PRAGMA) + { + mScanner->NextToken(); + ParsePragma(); + } + } + + while (size < msize) + d[size++] = 0; + + return d; +} + +Expression* Parser::DefaultInitExpression(Expression* vexp) +{ + Expression* exp = nullptr; + Declaration* dtype = vexp->mDecType; + + if (dtype->mType == DT_TYPE_INTEGER) + { + Expression* rexp = new Expression(mScanner->mLocation, EX_CONSTANT); + rexp->mDecValue = TheZeroIntegerConstDeclaration; + rexp->mDecType = dtype; + + exp = new Expression(rexp->mLocation, EX_INITIALIZATION); + exp->mToken = TK_ASSIGN; + exp->mLeft = vexp; + exp->mRight = rexp; + exp->mDecType = dtype; + } + else + { + mErrors->Error(mScanner->mLocation, EERR_INVALID_INITIALIZER, "Invalid initializer"); + exp = new Expression(mScanner->mLocation, EX_VOID); + } + + return exp; +} + +Expression* Parser::CloneVarInitExpression(Expression* vexp, Expression* iexp, Expression* qexp) +{ + if (!iexp) + return nullptr; + else if (iexp == qexp) + return vexp; + else + { + Expression* nexp = new Expression(iexp->mLocation, iexp->mType); + nexp->mToken = iexp->mToken; + nexp->mConst = iexp->mConst; + nexp->mDecType = iexp->mDecType; + nexp->mDecValue = iexp->mDecValue; + nexp->mLeft = CloneVarInitExpression(vexp, iexp->mLeft, qexp); + nexp->mRight = CloneVarInitExpression(vexp, iexp->mRight, qexp); + return nexp; + } +} + +Expression * Parser::CopyElision(Expression* vexp, Expression* rexp) +{ + if (rexp->mType == EX_CONSTRUCT && rexp->mRight->mType == EX_VARIABLE && (rexp->mRight->mDecValue->mFlags & DTF_TEMPORARY) && vexp->mType == EX_VARIABLE) + { + rexp->ReplaceVariable(rexp->mRight->mDecValue, vexp->mDecValue); + + return rexp; + } + else if (rexp->mType == EX_CALL) + { + Expression* iexp = new Expression(rexp->mLocation, EX_INITIALIZATION); + iexp->mToken = TK_ASSIGN; + iexp->mLeft = vexp; + iexp->mRight = rexp; + iexp->mDecType = vexp->mDecType; + + Expression* dexp = nullptr; + if (vexp->mDecType->mDestructor) + { + Expression* texp = new Expression(mScanner->mLocation, EX_PREFIX); + texp->mToken = TK_BINARY_AND; + texp->mLeft = vexp; + texp->mDecType = new Declaration(mScanner->mLocation, DT_TYPE_POINTER); + texp->mDecType->mFlags |= DTF_CONST | DTF_DEFINED; + texp->mDecType->mBase = vexp->mDecType->ToMutableType(); + texp->mDecType->mSize = 2; + + Expression* cexp = new Expression(mScanner->mLocation, EX_CONSTANT); + cexp->mDecValue = vexp->mDecType->mDestructor; + cexp->mDecType = cexp->mDecValue->mBase; + + dexp = new Expression(mScanner->mLocation, EX_CALL); + dexp->mLeft = cexp; + dexp->mRight = texp; + } + + Expression* nexp = new Expression(mScanner->mLocation, EX_CONSTRUCT); + + nexp->mLeft = new Expression(mScanner->mLocation, EX_LIST); + nexp->mLeft->mLeft = iexp; + nexp->mLeft->mRight = dexp; + + nexp->mRight = vexp; + nexp->mDecType = vexp->mDecType; + + return nexp; + } + + + return nullptr; +} + +Expression* Parser::ParseVarInitExpression(Expression* vexp, bool inner) +{ + Expression* exp = nullptr; + Declaration* dtype = vexp->mDecType; + + if (dtype->mType == DT_TYPE_ARRAY && mScanner->mToken == TK_STRING && dtype->mBase->mType == DT_TYPE_INTEGER && dtype->mBase->mSize == 1) + { + int ds = dtype->mSize; + uint8* d = ParseStringLiteral(ds); + + if (!(dtype->mFlags & DTF_DEFINED)) + { + dtype->mFlags |= DTF_DEFINED; + dtype->mSize = int(ds); + } + + Declaration * dec = new Declaration(mScanner->mLocation, DT_CONST_DATA); + dec->mBase = dtype; + dec->mSize = dtype->mSize; + dec->mSection = mCodeSection; + + dec->mData = d; + + Expression* cexp = new Expression(mScanner->mLocation, EX_CONSTANT); + cexp->mDecValue = dec; + cexp->mDecType = dtype; + + exp = new Expression(mScanner->mLocation, EX_INITIALIZATION); + exp->mToken = TK_ASSIGN; + exp->mLeft = vexp; + exp->mRight = cexp; + exp->mDecType = dtype; + + if (ds > dtype->mSize + 1) + mErrors->Error(mScanner->mLocation, EERR_CONSTANT_INITIALIZER, "String constant is too large for char array"); + } + else if ((dtype->mType == DT_TYPE_STRUCT || dtype->mType == DT_TYPE_UNION) && ConsumeTokenIf(TK_OPEN_BRACE)) + { + NumberSet fset(dtype->mSize * 8); + + bool isconst = true; + Declaration* edec = dtype->mParams; + for(;;) + { + if (ConsumeTokenIf(TK_DOT)) + { + if (mScanner->mToken == TK_IDENT) + { + Declaration * ttype = dtype; + + Declaration* ndec = ttype->mScope->Lookup(mScanner->mTokenIdent, SLEVEL_SCOPE); + while (!ndec && ttype->mBase) + { + ttype = ttype->mBase->mBase; + ndec = ttype->mScope->Lookup(mScanner->mTokenIdent, SLEVEL_SCOPE); + } + + if (ndec) + edec = ndec; + else + mErrors->Error(mScanner->mLocation, EERR_CONSTANT_INITIALIZER, "Struct member not found", mScanner->mTokenIdent); + + mScanner->NextToken(); + ConsumeToken(TK_ASSIGN); + } + else + mErrors->Error(mScanner->mLocation, EERR_CONSTANT_INITIALIZER, "Identifier expected"); + } + + if (!edec) + break; + + if (edec->mSize) + fset += edec->mOffset * 8 + edec->mShift; + + Expression* qexp = new Expression(mScanner->mLocation, EX_QUALIFY); + qexp->mLeft = vexp; + qexp->mDecValue = edec; + qexp->mDecType = edec->mBase; + + Expression* nexp = ParseVarInitExpression(qexp); + if (nexp->mType != EX_INITIALIZATION || nexp->mRight->mType != EX_CONSTANT) + isconst = false; + + exp = nexp->ListAppend(exp); + + if (dtype->mType == DT_TYPE_UNION) + break; + + do { + edec = edec->mNext; + } while (edec && edec->mType != DT_ELEMENT); + + if (!ConsumeTokenIf(TK_COMMA)) + break; + } + ConsumeToken(TK_CLOSE_BRACE); + + if (isconst) + { + Declaration * csdec = new Declaration(mScanner->mLocation, DT_CONST_STRUCT); + csdec->mBase = dtype; + csdec->mSize = dtype->mSize; + csdec->mSection = mCodeSection; + + Declaration* last = nullptr; + + Expression* lexp = exp; + while (lexp) + { + Expression* rexp; + if (lexp->mType == EX_LIST) + { + rexp = lexp->mRight; + lexp = lexp->mLeft; + } + else + { + rexp = lexp; + lexp = nullptr; + } + + Declaration* cidec = CopyConstantInitializer(rexp->mLeft->mDecValue->mOffset, rexp->mDecType, rexp->mRight); + if (cidec) + { + cidec->mBits = rexp->mLeft->mDecValue->mBits; + cidec->mShift = rexp->mLeft->mDecValue->mShift; + } + + if (last) + last->mNext = cidec; + else + csdec->mParams = cidec; + last = cidec; + } + + if (exp) + { + Expression* cexp = new Expression(mScanner->mLocation, EX_CONSTANT); + cexp->mDecValue = csdec; + cexp->mDecType = dtype; + + exp = new Expression(exp->mLocation, EX_INITIALIZATION); + exp->mToken = TK_ASSIGN; + exp->mLeft = vexp; + exp->mRight = cexp; + } + } + else if (dtype->mType == DT_TYPE_STRUCT) + { + edec = dtype->mParams; + while (edec) + { + if (edec->mSize && !fset[edec->mOffset * 8 + edec->mShift]) + { + fset += edec->mOffset * 8 + edec->mShift; + + Expression* qexp = new Expression(mScanner->mLocation, EX_QUALIFY); + qexp->mLeft = vexp; + qexp->mDecValue = edec; + qexp->mDecType = edec->mBase; + + Expression* nexp = DefaultInitExpression(qexp); + exp = nexp->ListAppend(exp); + } + + edec = edec->mNext; + } + } + + if (exp) + exp->mDecType = dtype; + } + else if (dtype->mType == DT_TYPE_ARRAY && (inner || ConsumeTokenIf(TK_OPEN_BRACE))) + { + if (inner && ConsumeTokenIf(TK_OPEN_BRACE)) + inner = false; + + int index = 0, stride = dtype->mBase->mSize, size = dtype->mSize / stride, mindex = 0; + + if (!(dtype->mFlags & DTF_DEFINED)) + size = 0x10000; + + NumberSet fset(size); + bool isconst = true; + + for (;;) + { + int nrep = 1; + + if (ConsumeTokenIf(TK_OPEN_BRACKET)) + { + Expression* istart = ParseRExpression(); + if (istart->mType != EX_CONSTANT || istart->mDecValue->mType != DT_CONST_INTEGER) + mErrors->Error(mScanner->mLocation, EERR_CONSTANT_INITIALIZER, "Constant index expected"); + else + { + index = int(istart->mDecValue->mInteger); + if (index >= size) + mErrors->Error(mScanner->mLocation, EERR_CONSTANT_INITIALIZER, "Constant initializer out of range"); + + if (ConsumeTokenIf(TK_ELLIPSIS)) + { + Expression* iend = ParseRExpression(); + if (iend->mType != EX_CONSTANT || iend->mDecValue->mType != DT_CONST_INTEGER) + mErrors->Error(mScanner->mLocation, EERR_CONSTANT_INITIALIZER, "Constant index expected"); + else + { + nrep = int(iend->mDecValue->mInteger - istart->mDecValue->mInteger + 1); + + if (size + nrep > dtype->mSize) + mErrors->Error(mScanner->mLocation, EERR_CONSTANT_INITIALIZER, "Constant initializer out of range"); + } + } + } + + ConsumeToken(TK_CLOSE_BRACKET); + ConsumeToken(TK_ASSIGN); + } + + if (index > mindex) + mindex = index; + if (index < size) + fset += index; + + Expression* qexp = new Expression(mScanner->mLocation, EX_INDEX); + qexp->mLeft = vexp; + qexp->mRight = new Expression(mScanner->mLocation, EX_CONSTANT); + qexp->mRight->mDecType = TheUnsignedIntTypeDeclaration; + qexp->mRight->mDecValue = new Declaration(mScanner->mLocation, DT_CONST_INTEGER); + qexp->mRight->mDecValue->mSize = 2; + qexp->mRight->mDecValue->mInteger = index; + qexp->mRight->mDecValue->mBase = TheUnsignedIntTypeDeclaration; + qexp->mDecType = dtype->mBase; + + Expression* nexp = ParseVarInitExpression(qexp, true); + if (nexp->mType != EX_INITIALIZATION || nexp->mRight->mType != EX_CONSTANT) + isconst = false; + if (nexp->mRight->mType == EX_PACK) + { + ConsumeToken(TK_ELLIPSIS); + Declaration* pdec = nexp->mRight->mDecValue->mParams; + nexp->mRight = new Expression(mScanner->mLocation, EX_VARIABLE); + nexp->mRight->mDecType = pdec->mBase; + nexp->mRight->mDecValue = pdec; + exp = nexp->ListAppend(exp); + + pdec = pdec->mNext; + while (pdec) + { + index++; + + if (index > mindex) + mindex = index; + if (index < size) + fset += index; + + Expression* qexp = new Expression(mScanner->mLocation, EX_INDEX); + qexp->mLeft = vexp; + qexp->mRight = new Expression(mScanner->mLocation, EX_CONSTANT); + qexp->mRight->mDecType = TheUnsignedIntTypeDeclaration; + qexp->mRight->mDecValue = new Declaration(mScanner->mLocation, DT_CONST_INTEGER); + qexp->mRight->mDecValue->mSize = 2; + qexp->mRight->mDecValue->mInteger = index; + qexp->mRight->mDecValue->mBase = TheUnsignedIntTypeDeclaration; + qexp->mDecType = dtype->mBase; + + nexp = new Expression(mScanner->mLocation, EX_INITIALIZATION); + nexp->mToken = TK_ASSIGN; + nexp->mLeft = qexp; + nexp->mRight = new Expression(mScanner->mLocation, EX_VARIABLE); + nexp->mRight->mDecType = pdec->mBase; + nexp->mRight->mDecValue = pdec; + nexp->mDecType = dtype->mBase; + + exp = nexp->ListAppend(exp); + pdec = pdec->mNext; + } + } + else + exp = nexp->ListAppend(exp); + + index++; + + for (int i = 1; i < nrep; i++) + { + if (index > mindex) + mindex = index; + if (index < size) + fset += index; + + Expression* rqexp = new Expression(mScanner->mLocation, EX_INDEX); + rqexp->mLeft = vexp; + rqexp->mRight = new Expression(mScanner->mLocation, EX_CONSTANT); + rqexp->mRight->mDecType = TheUnsignedIntTypeDeclaration; + rqexp->mRight->mDecValue = new Declaration(mScanner->mLocation, DT_CONST_INTEGER); + rqexp->mRight->mDecValue->mSize = 2; + rqexp->mRight->mDecValue->mInteger = index; + rqexp->mRight->mDecValue->mBase = TheUnsignedIntTypeDeclaration; + rqexp->mDecType = dtype->mBase; + + Expression* rnexp = CloneVarInitExpression(rqexp, nexp, qexp); + exp = rnexp->ListAppend(exp); + + index++; + } + + if (inner && index >= size) + break; + if (!ConsumeTokenIf(TK_COMMA)) + break; + if (inner && mScanner->mToken == TK_OPEN_BRACKET) + { + mScanner->UngetToken(TK_COMMA); + break; + } + if (mScanner->mToken == TK_CLOSE_BRACE) + break; + } + + if (!inner) + { + ConsumeTokenIf(TK_COMMA); + ConsumeToken(TK_CLOSE_BRACE); + } + + + if (!(dtype->mFlags & DTF_DEFINED)) + { + size = mindex + 1; + dtype->mSize = size * dtype->mBase->mSize; + dtype->mFlags |= DTF_DEFINED; + } + + if (isconst) + { + Declaration* csdec = new Declaration(mScanner->mLocation, DT_CONST_STRUCT); + csdec->mBase = dtype; + csdec->mSize = dtype->mSize; + csdec->mSection = mDataSection; + + Declaration* last = nullptr; + + Expression* lexp = exp; + while (lexp) + { + Expression* rexp; + if (lexp->mType == EX_LIST) + { + rexp = lexp->mRight; + lexp = lexp->mLeft; + } + else + { + rexp = lexp; + lexp = nullptr; + } + + Declaration* cidec = CopyConstantInitializer(int(rexp->mLeft->mRight->mDecValue->mInteger) * dtype->Stride(), rexp->mDecType, rexp->mRight); + + if (last) + last->mNext = cidec; + else + csdec->mParams = cidec; + last = cidec; + } + + Expression* cexp = new Expression(mScanner->mLocation, EX_CONSTANT); + cexp->mDecValue = csdec; + cexp->mDecType = dtype; + + exp = new Expression(exp->mLocation, EX_INITIALIZATION); + exp->mToken = TK_ASSIGN; + exp->mLeft = vexp; + exp->mRight = cexp; + } + else + { + for (int i = 0; i < size; i++) + { + if (!fset[i]) + { + fset += i; + + Expression* qexp = new Expression(mScanner->mLocation, EX_INDEX); + qexp->mLeft = vexp; + qexp->mRight = new Expression(mScanner->mLocation, EX_CONSTANT); + qexp->mRight->mDecType = TheUnsignedIntTypeDeclaration; + qexp->mRight->mDecValue = new Declaration(mScanner->mLocation, DT_CONST_INTEGER); + qexp->mRight->mDecValue->mSize = 2; + qexp->mRight->mDecValue->mInteger = i; + qexp->mRight->mDecValue->mBase = TheUnsignedIntTypeDeclaration; + qexp->mDecType = dtype->mBase; + + Expression* nexp = DefaultInitExpression(qexp); + exp = nexp->ListAppend(exp); + } + } + } + + exp->mDecType = dtype; + } + else if (dtype->mType == DT_TYPE_AUTO || dtype->mType == DT_TYPE_REFERENCE) + { + exp = ParseRExpression(); + } + else if (dtype->mDefaultConstructor) + { + Declaration* fcons = dtype->mScope ? dtype->mScope->Lookup(dtype->mIdent->PreMangle("+"), SLEVEL_CLASS) : nullptr; + + if (dtype->mFlags & DTF_PURE_VIRTUAL) + mErrors->Error(mScanner->mLocation, ERRR_INSTANTIATE_ABSTRACT_CLASS, "Cannot instantiate abstract class", dtype->mIdent); + + Declaration* mtype = dtype->ToMutableType(); + + Expression* rexp = ParseRExpression(); + if (rexp->mDecType->IsSame(dtype)) + { + exp = CopyElision(vexp, rexp); + fcons = dtype->mCopyConstructor; + } + + if (!exp) + { + if (fcons) + { + Expression* cexp = new Expression(mScanner->mLocation, EX_CONSTANT); + cexp->mDecValue = fcons; + cexp->mDecType = cexp->mDecValue->mBase; + + Expression* fexp = new Expression(mScanner->mLocation, EX_CALL); + fexp->mLeft = cexp; + fexp->mRight = rexp; + + Expression* texp = new Expression(mScanner->mLocation, EX_PREFIX); + texp->mToken = TK_BINARY_AND; + texp->mLeft = vexp; + texp->mDecType = new Declaration(mScanner->mLocation, DT_TYPE_POINTER); + texp->mDecType->mFlags |= DTF_CONST | DTF_DEFINED; + texp->mDecType->mBase = mtype; + texp->mDecType->mSize = 2; + + if (fexp->mRight) + { + Expression* lexp = new Expression(mScanner->mLocation, EX_LIST); + lexp->mLeft = texp; + lexp->mRight = fexp->mRight; + fexp->mRight = lexp; + } + else + fexp->mRight = texp; + + fexp = ResolveOverloadCall(fexp); + + Expression* dexp = nullptr; + if (dtype->mDestructor) + { + Expression* cexp = new Expression(mScanner->mLocation, EX_CONSTANT); + cexp->mDecValue = dtype->mDestructor; + cexp->mDecType = cexp->mDecValue->mBase; + + dexp = new Expression(mScanner->mLocation, EX_CALL); + dexp->mLeft = cexp; + dexp->mRight = texp; + } + + Expression* nexp = new Expression(mScanner->mLocation, EX_CONSTRUCT); + + nexp->mLeft = new Expression(mScanner->mLocation, EX_LIST); + nexp->mLeft->mLeft = fexp; + nexp->mLeft->mRight = dexp; + + nexp->mRight = vexp; + nexp->mDecType = vexp->mDecType; + + exp = nexp; + } + else + { + rexp = CoerceExpression(rexp, dtype); + exp = new Expression(rexp->mLocation, EX_INITIALIZATION); + exp->mToken = TK_ASSIGN; + exp->mLeft = vexp; + exp->mRight = rexp; + exp->mDecType = dtype; + } + } + } + else + { + Expression* rexp = ParseRExpression(); + rexp = CoerceExpression(rexp, dtype); + exp = new Expression(rexp->mLocation, EX_INITIALIZATION); + exp->mToken = TK_ASSIGN; + exp->mLeft = vexp; + exp->mRight = rexp; + exp->mDecType = dtype; + } + + return exp; +} + +Expression* Parser::ParseConstInitExpression(Declaration* dtype, bool inner) +{ + Expression* exp = nullptr; + Declaration* dec; + + if (dtype->mType == DT_TYPE_AUTO) + { + exp = ParseRExpression(); + } + else if (dtype->mType == DT_TYPE_ARRAY || dtype->mType == DT_TYPE_STRUCT || dtype->mType == DT_TYPE_UNION) + { + if (dtype->mType != DT_TYPE_ARRAY && !(dtype->mFlags & DTF_DEFINED)) + { + if (dtype->mIdent) + mErrors->Error(mScanner->mLocation, EERR_UNDEFINED_OBJECT, "Constant for undefined type", dtype->mIdent); + else + mErrors->Error(mScanner->mLocation, EERR_UNDEFINED_OBJECT, "Constant for undefined anonymous type"); + } + + if (mScanner->mToken == TK_STRING && dtype->mType == DT_TYPE_ARRAY && dtype->mBase->mType == DT_TYPE_INTEGER && dtype->mBase->mSize == 1) + { + int ds = dtype->mSize; + uint8* d = ParseStringLiteral(ds); + + if (!(dtype->mFlags & DTF_DEFINED)) + { + dtype->mFlags |= DTF_DEFINED; + dtype->mSize = int(ds); + } + + dec = new Declaration(mScanner->mLocation, DT_CONST_DATA); + dec->mBase = dtype; + dec->mSize = dtype->mSize; + dec->mSection = mDataSection; + + dec->mData = d; + + if (ds > dtype->mSize + 1) + mErrors->Error(mScanner->mLocation, EERR_CONSTANT_INITIALIZER, "String constant is too large for char array"); + } + else if ((inner && !dtype->mDefaultConstructor) || ConsumeTokenIf(TK_OPEN_BRACE)) + { + if (inner && ConsumeTokenIf(TK_OPEN_BRACE)) + inner = false; + + dec = new Declaration(mScanner->mLocation, DT_CONST_STRUCT); + dec->mBase = dtype; + dec->mSize = dtype->mSize; + dec->mSection = mDataSection; + + Declaration* last = nullptr; + + if (dtype->mType == DT_TYPE_ARRAY) + { + int index = 0, stride = dtype->Stride(), size = 0; + + if (dtype->mFlags & DTF_STRIPED) + dec->mStripe = dtype->mBase->mStripe; + + while (!(dtype->mFlags & DTF_DEFINED) || size < dtype->mSize) + { + int nrep = 1; + + if (ConsumeTokenIf(TK_OPEN_BRACKET)) + { + Expression* istart = ParseRExpression(); + if (istart->mType != EX_CONSTANT || istart->mDecValue->mType != DT_CONST_INTEGER) + mErrors->Error(mScanner->mLocation, EERR_CONSTANT_INITIALIZER, "Constant index expected"); + else + { + index = stride * int(istart->mDecValue->mInteger); + if (index >= dtype->mSize) + { + mErrors->Error(mScanner->mLocation, EERR_CONSTANT_INITIALIZER, "Constant initializer out of range"); + break; + } + + if (ConsumeTokenIf(TK_ELLIPSIS)) + { + Expression* iend = ParseRExpression(); + if (iend->mType != EX_CONSTANT || iend->mDecValue->mType != DT_CONST_INTEGER) + mErrors->Error(mScanner->mLocation, EERR_CONSTANT_INITIALIZER, "Constant index expected"); + else + { + nrep = int(iend->mDecValue->mInteger - istart->mDecValue->mInteger + 1); + + if (size + nrep * dtype->mBase->mSize > dtype->mSize) + { + mErrors->Error(mScanner->mLocation, EERR_CONSTANT_INITIALIZER, "Constant initializer out of range"); + break; + } + } + } + } + + ConsumeToken(TK_CLOSE_BRACKET); + ConsumeToken(TK_ASSIGN); + } + + Expression* texp = ParseConstInitExpression(dtype->mBase, true); + texp = texp->ConstantFold(mErrors, mDataSection); + for (int i = 0; i < nrep; i++) + { + Declaration* cdec = CopyConstantInitializer(index, dtype->mBase, texp); + + if (last) + last->mNext = cdec; + else + dec->mParams = cdec; + last = cdec; + + index += stride; + size += dtype->mBase->mSize; + } + + if (inner && (dtype->mFlags & DTF_DEFINED) && size >= dtype->mSize) + break; + if (!ConsumeTokenIf(TK_COMMA)) + break; + if (inner && mScanner->mToken == TK_OPEN_BRACKET) + { + mScanner->UngetToken(TK_COMMA); + break; + } + if (mScanner->mToken == TK_CLOSE_BRACE) + break; + } + + if (!(dtype->mFlags & DTF_DEFINED)) + { + dtype->mFlags |= DTF_DEFINED; + dtype->mSize = size; + dec->mSize = size; + } + } + else + { + ExpandingArray path; + + Declaration* ttype = dtype; + while (ttype->mBase) + { + path.Push(ttype); + ttype = ttype->mBase->mBase; + } + + Declaration* mdec = ttype->mParams; + for(;;) + { + if (ConsumeTokenIf(TK_DOT)) + { + if (mScanner->mToken == TK_IDENT) + { + ttype = dtype; + + path.SetSize(0); + Declaration* ndec = ttype->mScope->Lookup(mScanner->mTokenIdent, SLEVEL_SCOPE); + while (!ndec && ttype->mBase) + { + path.Push(ttype); + ttype = ttype->mBase->mBase; + ndec = ttype->mScope->Lookup(mScanner->mTokenIdent, SLEVEL_SCOPE); + } + + if (ndec) + mdec = ndec; + else + mErrors->Error(mScanner->mLocation, EERR_CONSTANT_INITIALIZER, "Struct member not found", mScanner->mTokenIdent); + + mScanner->NextToken(); + ConsumeToken(TK_ASSIGN); + } + else + mErrors->Error(mScanner->mLocation, EERR_CONSTANT_INITIALIZER, "Identifier expected"); + } + + if (mdec) + { + Expression* texp = ParseConstInitExpression(mdec->mBase, true); + + Declaration* cdec = CopyConstantInitializer(mdec->mOffset, mdec->mBase, texp); + if (cdec) + { + cdec->mBits = mdec->mBits; + cdec->mShift = mdec->mShift; + + if (last) + last->mNext = cdec; + else + dec->mParams = cdec; + last = cdec; + } + + do { + mdec = mdec->mNext; + } while (mdec && mdec->mType != DT_ELEMENT); + + while (!mdec && path.Size()) + mdec = path.Pop()->mParams; + + if (inner && !mdec) + break; + + if (!ConsumeTokenIf(TK_COMMA)) + break; + } + else if (inner) + break; + else if (!ConsumeTokenIf(TK_COMMA)) + break; + + if (mScanner->mToken == TK_CLOSE_BRACE) + break; + } + } + + if (!inner) + { + ConsumeTokenIf(TK_COMMA); + ConsumeToken(TK_CLOSE_BRACE); + } + + if (last) + last->mNext = nullptr; + else + dec->mParams = nullptr; + } + else + { + exp = ParseRExpression(); + return exp; + } + + exp = new Expression(mScanner->mLocation, EX_CONSTANT); + exp->mDecType = dtype; + exp->mDecValue = dec; + } + else + { + exp = ParseRExpression(); + if (dtype->mType == DT_TYPE_POINTER && exp->mDecType && exp->mDecType->mType == DT_TYPE_ARRAY && exp->mType == EX_CONSTANT) + { + Declaration* ndec = new Declaration(exp->mDecValue->mLocation, DT_CONST_POINTER); + ndec->mBase = dtype; + ndec->mValue = exp; + dec = ndec; + + Expression * nexp = new Expression(mScanner->mLocation, EX_CONSTANT); + nexp->mDecType = new Declaration(dec->mLocation, DT_TYPE_POINTER); + nexp->mDecType->mBase = exp->mDecType->mBase; + nexp->mDecType->mSize = 2; + nexp->mDecType->mStride = exp->mDecType->mStride; + nexp->mDecType->mFlags |= DTF_DEFINED; + nexp->mDecValue = ndec; + exp = nexp; + } + else if (exp->mType == EX_CONSTANT && exp->mDecType != dtype) + { + Expression* nexp = new Expression(mScanner->mLocation, EX_CONSTANT); + nexp->mDecType = dtype; + + if (dtype->mType == DT_TYPE_INTEGER || dtype->mType == DT_TYPE_ENUM || dtype->mType == DT_TYPE_BOOL) + { + Declaration* ndec = new Declaration(exp->mDecValue->mLocation, DT_CONST_INTEGER); + ndec->mBase = dtype; + ndec->mSize = dtype->mSize; + + if (exp->mDecValue->mType == DT_CONST_INTEGER) + ndec->mInteger = exp->mDecValue->mInteger; + else if (exp->mDecValue->mType == DT_CONST_FLOAT) + ndec->mInteger = int64(exp->mDecValue->mNumber); + else + mErrors->Error(mScanner->mLocation, EERR_CONSTANT_INITIALIZER, "Illegal integer constant initializer"); + + nexp->mDecValue = ndec; + } + else if (dtype->mType == DT_TYPE_FLOAT) + { + Declaration* ndec = new Declaration(exp->mDecValue->mLocation, DT_CONST_FLOAT); + ndec->mBase = dtype; + ndec->mSize = dtype->mSize; + + if (exp->mDecValue->mType == DT_CONST_INTEGER) + ndec->mNumber = double(exp->mDecValue->mInteger); + else if (exp->mDecValue->mType == DT_CONST_FLOAT) + ndec->mNumber = exp->mDecValue->mNumber; + else + mErrors->Error(mScanner->mLocation, EERR_CONSTANT_INITIALIZER, "Illegal float constant initializer"); + + nexp->mDecValue = ndec; + } + else if (dtype->mType == DT_TYPE_POINTER) + { + if (exp->mDecValue->mType == DT_CONST_ADDRESS || exp->mDecValue->mType == DT_CONST_POINTER || exp->mDecValue->mType == DT_CONST_ASSEMBLER || exp->mDecValue->mType == DT_LABEL) + ; + else if (exp->mDecValue->mType == DT_CONST_FUNCTION) + { + Declaration* ndec = new Declaration(exp->mDecValue->mLocation, DT_CONST_POINTER); + ndec->mBase = dtype; + ndec->mValue = exp; + dec = ndec; + + Expression* nexp = new Expression(mScanner->mLocation, EX_CONSTANT); + nexp->mDecType = new Declaration(dec->mLocation, DT_TYPE_POINTER); + nexp->mDecType->mBase = exp->mDecType; + nexp->mDecType->mSize = 2; + nexp->mDecType->mFlags |= DTF_DEFINED; + nexp->mDecValue = ndec; + exp = nexp; + } + else if (exp->mDecValue->mType == DT_CONST_INTEGER && exp->mDecValue->mInteger == 0) + { + Expression* nexp = new Expression(mScanner->mLocation, EX_CONSTANT); + nexp->mDecValue = TheNullptrConstDeclaration; + nexp->mDecType = TheNullPointerTypeDeclaration; + exp = nexp; + } + else + mErrors->Error(mScanner->mLocation, EERR_CONSTANT_INITIALIZER, "Illegal pointer constant initializer"); + + nexp = exp; + } + else + { + mErrors->Error(mScanner->mLocation, EERR_CONSTANT_INITIALIZER, "Illegal constant initializer"); + nexp = exp; + } + + exp = nexp; + } + + } + + return exp; +} + +Expression* Parser::BuildMemberInitializer(Expression* vexp) +{ + Declaration* fcons = (vexp->mDecType->mType == DT_TYPE_STRUCT && vexp->mDecType->mScope) ? vexp->mDecType->mScope->Lookup(vexp->mDecType->mIdent->PreMangle("+")) : nullptr; + + if (fcons) + { + Expression* cexp = new Expression(mScanner->mLocation, EX_CONSTANT); + cexp->mDecValue = fcons; + cexp->mDecType = cexp->mDecValue->mBase; + + Expression* fexp = new Expression(mScanner->mLocation, EX_CALL); + fexp->mLeft = cexp; + + mScanner->NextToken(); + if (mScanner->mToken != TK_CLOSE_PARENTHESIS) + { + fexp->mRight = ParseListExpression(false, fcons->mBase->mParams->mNext); + ConsumeToken(TK_CLOSE_PARENTHESIS); + } + else + { + fexp->mRight = nullptr; + mScanner->NextToken(); + } + + Expression* texp = new Expression(mScanner->mLocation, EX_PREFIX); + texp->mToken = TK_BINARY_AND; + texp->mLeft = vexp; + texp->mDecType = vexp->mDecType->BuildPointer(mScanner->mLocation); + + if (fexp->mRight) + { + Expression* lexp = new Expression(mScanner->mLocation, EX_LIST); + lexp->mLeft = texp; + lexp->mRight = fexp->mRight; + fexp->mRight = lexp; + } + else + fexp->mRight = texp; + + fexp = ResolveOverloadCall(fexp); + + return fexp; + } + else if (mScanner->mToken == TK_OPEN_BRACE) + { + return ParseVarInitExpression(vexp); + } + else + { + Expression* nexp = new Expression(mScanner->mLocation, EX_INITIALIZATION); + nexp->mToken = TK_ASSIGN; + nexp->mDecType = vexp->mDecType; + nexp->mLeft = vexp; + ConsumeToken(TK_OPEN_PARENTHESIS); + if (!ConsumeTokenIf(TK_CLOSE_PARENTHESIS)) + { + nexp->mRight = ParseRExpression(); + ConsumeToken(TK_CLOSE_PARENTHESIS); + } + else if (nexp->mDecType->IsIntegerType()) + { + nexp->mRight = new Expression(mScanner->mLocation, EX_CONSTANT); + nexp->mRight->mDecType = nexp->mDecType; + nexp->mRight->mDecValue = new Declaration(mScanner->mLocation, DT_CONST_INTEGER); + nexp->mRight->mDecValue->mBase = nexp->mDecType; + nexp->mRight->mDecValue->mSize = nexp->mDecType->mSize; + nexp->mRight->mDecValue->mInteger = 0; + } + else if (nexp->mDecType->mType == DT_TYPE_FLOAT) + { + nexp->mRight = new Expression(mScanner->mLocation, EX_CONSTANT); + nexp->mRight->mDecType = nexp->mDecType; + nexp->mRight->mDecValue = new Declaration(mScanner->mLocation, DT_CONST_FLOAT); + nexp->mRight->mDecValue->mBase = nexp->mDecType; + nexp->mRight->mDecValue->mSize = nexp->mDecType->mSize; + nexp->mRight->mDecValue->mNumber = 0; + } + else if (nexp->mDecType->mType == DT_TYPE_POINTER) + { + nexp->mRight = new Expression(mScanner->mLocation, EX_CONSTANT); + nexp->mRight->mDecType = nexp->mDecType; + nexp->mRight->mDecValue = TheNullptrConstDeclaration; + } + + return nexp; + } +} + +void Parser::PrependMemberConstructor(Declaration* pthis, Declaration* cfunc) +{ + if (cfunc->mFlags & DTF_COMPLETED) + return; + + cfunc->mFlags |= DTF_COMPLETED; + + Expression* pthisexp = new Expression(pthis->mLocation, EX_VARIABLE); + pthisexp->mDecType = pthis; + pthisexp->mDecValue = cfunc->mBase->mParams; + + Expression* thisexp = new Expression(mScanner->mLocation, EX_PREFIX); + thisexp->mToken = TK_MUL; + thisexp->mDecType = pthis->mBase; + thisexp->mLeft = pthisexp; + + Declaration* bcdec = pthis->mBase->mBase; + while (bcdec) + { + Declaration* mfunc = cfunc->mScope ? cfunc->mScope->Lookup(bcdec->mBase->mIdent) : nullptr; + + if (mfunc) + { + Expression* sexp = new Expression(cfunc->mLocation, EX_SEQUENCE); + sexp->mLeft = mfunc->mValue; + sexp->mRight = cfunc->mValue; + cfunc->mValue = sexp; + } + else if (bcdec->mBase->mDefaultConstructor) + { + Declaration* mdec = bcdec->mBase->mDefaultConstructor; + + Expression* cexp = new Expression(pthis->mLocation, EX_CONSTANT); + cexp->mDecValue = mdec; + cexp->mDecType = cexp->mDecValue->mBase; + + Expression * dexp = new Expression(mScanner->mLocation, EX_CALL); + dexp->mLeft = cexp; + dexp->mRight = pthisexp; + + Expression* sexp = new Expression(mScanner->mLocation, EX_SEQUENCE); + + sexp->mLeft = dexp; + sexp->mRight = cfunc->mValue; + cfunc->mValue = sexp; + } + + bcdec = bcdec->mNext; + } + + Declaration* dec = pthis->mBase->mParams; + if (dec) + { + + dec = dec->Last(); + while (dec) + { + if (dec->mType == DT_ELEMENT) + { + Declaration* mfunc = cfunc->mScope ? cfunc->mScope->Lookup(dec->mIdent) : nullptr; + + if (mfunc) + { + Expression* sexp = new Expression(cfunc->mLocation, EX_SEQUENCE); + sexp->mLeft = mfunc->mValue; + sexp->mRight = cfunc->mValue; + cfunc->mValue = sexp; + } + else + { + Declaration* bdec = dec->mBase; + while (bdec->mType == DT_TYPE_ARRAY) + bdec = bdec->mBase; + + if (bdec->mFlags & DTF_PURE_VIRTUAL) + mErrors->Error(dec->mLocation, ERRR_INSTANTIATE_ABSTRACT_CLASS, "Cannot instantiate abstract class", dec->mIdent); + + if (bdec->mType == DT_TYPE_STRUCT && bdec->mDefaultConstructor || dec->mValue) + { + bdec = bdec->ToMutableType(); + + Declaration* bpdec = bdec->BuildPointer(mScanner->mLocation); + + Expression* qexp = new Expression(pthis->mLocation, EX_QUALIFY); + qexp->mLeft = thisexp; + qexp->mDecValue = dec; + + Expression* dexp; + + if (bdec->mDefaultConstructor) + { + if (dec->mBase->mType == DT_TYPE_STRUCT) + { + qexp->mDecType = bdec; + + if (dec->mValue) + dexp = BuildVariableInit(qexp, dec->mValue); + else + { + Expression* pexp = new Expression(pthis->mLocation, EX_PREFIX); + pexp->mLeft = qexp; + pexp->mToken = TK_BINARY_AND; + pexp->mDecType = bpdec; + + Declaration* mdec = bdec->mDefaultConstructor; + + Expression* cexp = new Expression(pthis->mLocation, EX_CONSTANT); + if (mdec) + { + cexp->mDecValue = mdec; + cexp->mDecType = cexp->mDecValue->mBase; + } + else + mErrors->Error(dec->mLocation, EERR_NO_DEFAULT_CONSTRUCTOR, "No default constructor"); + + dexp = new Expression(mScanner->mLocation, EX_CALL); + dexp->mLeft = cexp; + dexp->mRight = pexp; + } + } + else + { + qexp->mDecType = dec->mBase->mBase; + + Expression* pexp = new Expression(pthis->mLocation, EX_PREFIX); + pexp->mLeft = qexp; + pexp->mToken = TK_BINARY_AND; + pexp->mDecType = bpdec; + + qexp = pexp; + + if (dec->mValue) + { + int offset = 0; + dexp = BuildVariableArrayInit(qexp, bpdec, dec->mValue->mDecValue, 0, offset); +#if 0 + int offset = 0; + dexp = nullptr; + + Declaration* pdec = dec->mValue->mDecValue->mParams; + while (pdec) + { + if (pdec->mType == DT_CONST_CONSTRUCTOR) + { + if (offset != pdec->mOffset) + mErrors->Error(pdec->mLocation, EERR_INVALID_CLASS_INITIALIZER, "Incomplete class initializer list"); + + Declaration* icdec = new Declaration(mScanner->mLocation, DT_CONST_INTEGER); + icdec->mBase = TheUnsignedIntTypeDeclaration; + icdec->mInteger = pdec->mOffset / bdec->mSize; + + Expression* icexp = new Expression(mScanner->mLocation, EX_CONSTANT); + icexp->mDecType = icdec->mBase; + icexp->mDecValue = icdec; + + Expression* mexp = new Expression(mScanner->mLocation, EX_BINARY); + mexp->mToken = TK_ADD; + mexp->mLeft = qexp; + mexp->mRight = icexp; + mexp->mDecType = bpdec; + + Expression* pv = new Expression(pdec->mValue->mLocation, EX_CONSTRUCT); + pv->mLeft = new Expression(pdec->mValue->mLeft->mLocation, EX_LIST); + pv->mLeft->mLeft = new Expression(pdec->mValue->mLeft->mLeft->mLocation, EX_CALL); + pv->mLeft->mLeft->mLeft = pdec->mValue->mLeft->mLeft->mLeft; + pv->mLeft->mLeft->mDecType = pdec->mValue->mLeft->mLeft->mDecType; + pv->mLeft->mLeft->mRight = new Expression(pdec->mValue->mLeft->mLeft->mRight->mLocation, EX_LIST); + pv->mLeft->mLeft->mRight->mRight = pdec->mValue->mLeft->mLeft->mRight->mRight; + pv->mLeft->mLeft->mRight->mLeft = mexp; + + if (dexp) + { + Expression* nexp = new Expression(mScanner->mLocation, EX_LIST); + nexp->mLeft = dexp; + nexp->mRight = pv; + dexp = nexp; + } + else + dexp = pv; + + offset += bdec->mSize; + } + else + mErrors->Error(pdec->mLocation, EERR_INVALID_CLASS_INITIALIZER, "Invalid class initializer"); + + pdec = pdec->mNext; + } +#endif + if (offset != dec->mSize) + mErrors->Error(dec->mLocation, EERR_INVALID_CLASS_INITIALIZER, "Incomplete class initializer list"); + } + else + { + while (qexp->mDecType->mType == DT_TYPE_ARRAY) + { + Expression* iexp = new Expression(pthis->mLocation, EX_PREFIX); + iexp->mToken = TK_MUL; + iexp->mLeft = qexp; + iexp->mDecType = qexp->mDecType->mBase; + qexp = iexp; + } + + qexp->mDecType = bpdec; + + Expression* cexp = new Expression(pthis->mLocation, EX_CONSTANT); + cexp->mDecValue = bdec->mVectorConstructor; + cexp->mDecType = cexp->mDecValue->mBase; + + Declaration* ncdec = new Declaration(mScanner->mLocation, DT_CONST_INTEGER); + ncdec->mBase = TheUnsignedIntTypeDeclaration; + ncdec->mInteger = dec->mSize / bdec->mSize; + + dexp = new Expression(mScanner->mLocation, EX_CALL); + dexp->mLeft = cexp; + dexp->mRight = new Expression(mScanner->mLocation, EX_LIST); + dexp->mRight->mLeft = qexp; + dexp->mRight->mRight = new Expression(mScanner->mLocation, EX_BINARY); + dexp->mRight->mRight->mToken = TK_ADD; + dexp->mRight->mRight->mLeft = qexp; + dexp->mRight->mRight->mDecType = qexp->mDecType; + dexp->mRight->mRight->mRight = new Expression(mScanner->mLocation, EX_CONSTANT); + dexp->mRight->mRight->mRight->mDecType = ncdec->mBase; + dexp->mRight->mRight->mRight->mDecValue = ncdec; + } + } + } + else + { + qexp->mDecType = dec->mBase; + + dexp = new Expression(mScanner->mLocation, EX_INITIALIZATION); + dexp->mToken = TK_ASSIGN; + dexp->mDecType = dec->mBase; + dexp->mLeft = qexp; + dexp->mRight = dec->mValue; + } + + Expression* sexp = new Expression(mScanner->mLocation, EX_SEQUENCE); + + sexp->mLeft = dexp; + sexp->mRight = cfunc->mValue; + cfunc->mValue = sexp; + } + } + } + + dec = dec->mPrev; + } + } + + if (pthis->mBase->mVTable) + { + Expression* sexp = new Expression(cfunc->mLocation, EX_SEQUENCE); + sexp->mLeft = cfunc->mValue; + + Expression* vexp = new Expression(pthis->mLocation, EX_QUALIFY); + vexp->mLeft = thisexp; + vexp->mDecValue = new Declaration(pthis->mLocation, DT_ELEMENT); + vexp->mDecValue->mBase = TheCharTypeDeclaration; + vexp->mDecValue->mOffset = pthis->mBase->mVTable->mOffset; + vexp->mDecValue->mSize = 1; + vexp->mDecType = TheCharTypeDeclaration; + + Expression* nexp = new Expression(cfunc->mLocation, EX_INITIALIZATION); + nexp->mToken = TK_ASSIGN; + nexp->mDecType = TheCharTypeDeclaration; + nexp->mLeft = vexp; + nexp->mRight = new Expression(cfunc->mLocation, EX_CONSTANT); + nexp->mRight->mDecType = TheCharTypeDeclaration; + nexp->mRight->mDecValue = pthis->mBase->mVTable->mDefaultConstructor; + + sexp->mRight = nexp; + cfunc->mValue = sexp; + } +} + +void Parser::BuildMemberConstructor(Declaration* pthis, Declaration* cfunc) +{ + Expression* pthisexp = new Expression(pthis->mLocation, EX_VARIABLE); + pthisexp->mDecType = pthis; + pthisexp->mDecValue = cfunc->mBase->mParams; + + Expression* thisexp = new Expression(mScanner->mLocation, EX_PREFIX); + thisexp->mToken = TK_MUL; + thisexp->mDecType = pthis->mBase; + thisexp->mLeft = pthisexp; + + cfunc->mScope = new DeclarationScope(nullptr, SLEVEL_CLASS); + + DeclarationScope* scope = new DeclarationScope(mScope, SLEVEL_FUNCTION); + mScope = scope; + + Declaration* pdec = cfunc->mBase->mParams; + while (pdec) + { + if (pdec->mIdent) + scope->Insert(pdec->mIdent, pdec); + pdec = pdec->mNext; + } + if (cfunc->mBase->mParamPack) + mScope->Insert(cfunc->mBase->mParamPack->mIdent, cfunc->mBase->mParamPack); + + Declaration* othis = mThisPointer; + mThisPointer = pthis; + + mScanner->NextToken(); + do { + if (ExpectToken(TK_IDENT)) + { + Declaration* dec = pthis->mBase->mScope->Lookup(mScanner->mTokenIdent); + + Declaration* pcdec = pthis->mBase->mBase; + while (pcdec && pcdec->mBase->mIdent != mScanner->mTokenIdent) + pcdec = pcdec->mNext; + + if (pcdec) + { + Expression* qexp = new Expression(mScanner->mLocation, EX_PREFIX); + qexp->mToken = TK_MUL; + qexp->mDecType = pcdec->mBase->ToMutableType(); + qexp->mLeft = pthisexp; + + Declaration* dec = new Declaration(mScanner->mLocation, DT_CONST_CONSTRUCTOR); + dec->mIdent = mScanner->mTokenIdent; + + mScanner->NextToken(); + + dec->mValue = BuildMemberInitializer(qexp); + + cfunc->mScope->Insert(dec->mIdent, dec); + } + else if (dec && dec->mType == DT_ELEMENT) + { + Expression* qexp = new Expression(pthis->mLocation, EX_QUALIFY); + qexp->mLeft = thisexp; + qexp->mDecValue = dec; + qexp->mDecType = dec->mBase->ToMutableType(); + + Declaration* dec = new Declaration(mScanner->mLocation, DT_CONST_CONSTRUCTOR); + dec->mIdent = mScanner->mTokenIdent; + + mScanner->NextToken(); + + dec->mValue = BuildMemberInitializer(qexp); + + cfunc->mScope->Insert(dec->mIdent, dec); + } + else + mErrors->Error(mScanner->mLocation, EERR_OBJECT_NOT_FOUND, "Base class or member not found", mScanner->mTokenIdent); + } + + } while (ConsumeTokenIf(TK_COMMA)); + + mScope = mScope->mParent; + mThisPointer = othis; +} + +void Parser::AddDefaultConstructors(Declaration* pthis) +{ + bool simpleDestructor = true, simpleAssignment = true, simpleConstructor = true, simpleCopy = true, simpleMove = true, hasConstMember = false; + bool inlineDestructor = true; + bool inlineConstructor = true; + bool inlineCopy = true; + bool inlineMove = true; + bool explicitDestructor = false; + bool canMoveConstruct = true; + + const Ident* dtorident = pthis->mBase->mIdent->PreMangle("~");; + const Ident* ctorident = pthis->mBase->mIdent->PreMangle("+");; + const Ident* ctorqident = pthis->mBase->mScope->Mangle(ctorident); + const Ident* cvtorident = pthis->mBase->mIdent->PreMangle("*");; + const Ident* cvtorqident = pthis->mBase->mScope->Mangle(cvtorident); + const Ident* cvmtorident = pthis->mBase->mIdent->PreMangle("*&");; + + const Ident* asgnident = pthis->mBase->mIdent->PreMangle("=");; + const Ident* asgnvident = pthis->mBase->mIdent->PreMangle("=*=");; + const Ident* asgnvmident = pthis->mBase->mIdent->PreMangle("=*&=");; + + + // Extract constructor and destructor from scope + + Declaration* cdec = pthis->mBase->mScope->Lookup(ctorident, SLEVEL_SCOPE); + while (cdec) + { + Declaration* ctdec = cdec->mBase; + Declaration* tparam = ctdec->mParams->mNext; + + if (!tparam) + pthis->mBase->mDefaultConstructor = cdec; + else if (!tparam->mNext && tparam->mBase->mType == DT_TYPE_REFERENCE && pthis->mBase->IsConstSame(tparam->mBase->mBase)) + pthis->mBase->mCopyConstructor = cdec; + else if (!tparam->mNext && tparam->mBase->mType == DT_TYPE_RVALUEREF && pthis->mBase->IsConstSame(tparam->mBase->mBase)) + pthis->mBase->mMoveConstructor = cdec; + + cdec = cdec->mNext; + } + + Declaration* ddec = pthis->mBase->mScope->Lookup(dtorident, SLEVEL_SCOPE); + if (ddec) + { + pthis->mBase->mDestructor = ddec; + explicitDestructor = true; + } + + Declaration* adec = pthis->mBase->mScope->Lookup(Ident::Unique("operator="), SLEVEL_SCOPE); + while (adec) + { + Declaration* atdec = adec->mBase; + Declaration* tparam = atdec->mParams->mNext; + + if (!tparam->mNext && tparam->mBase->mType == DT_TYPE_REFERENCE && pthis->mBase->IsConstSame(tparam->mBase->mBase)) + pthis->mBase->mCopyAssignment = adec; + if (!tparam->mNext && tparam->mBase->mType == DT_TYPE_RVALUEREF && pthis->mBase->IsSame(tparam->mBase->mBase)) + pthis->mBase->mMoveAssignment = adec; + + adec = adec->mNext; + } + + Declaration* bcdec = pthis->mBase->mBase; + while (bcdec) + { + if (bcdec->mBase->mDestructor) + { + if (!(bcdec->mBase->mDestructor->mBase->mFlags & DTF_REQUEST_INLINE)) + inlineConstructor = false; + simpleDestructor = false; + } + if (bcdec->mBase->mDefaultConstructor) + { + simpleConstructor = false; + if (!(bcdec->mBase->mDefaultConstructor->mBase->mFlags & DTF_REQUEST_INLINE)) + inlineConstructor = false; + } + if (bcdec->mBase->mCopyConstructor) + { + simpleCopy = false; + if (!(bcdec->mBase->mCopyConstructor->mBase->mFlags & DTF_REQUEST_INLINE)) + inlineCopy = false; + } + if (bcdec->mBase->mMoveConstructor) + { + simpleMove = false; + if (!(bcdec->mBase->mMoveConstructor->mBase->mFlags & DTF_REQUEST_INLINE)) + inlineMove = false; + } + else if (bcdec->mBase->mDestructor || bcdec->mBase->mDefaultConstructor || bcdec->mBase->mCopyConstructor) + canMoveConstruct = false; + + if (bcdec->mBase->mCopyAssignment) + simpleAssignment = false; + bcdec = bcdec->mNext; + } + + if (pthis->mBase->mVTable) + simpleConstructor = false; + + if (explicitDestructor) + { + simpleCopy = false; + simpleAssignment = false; + canMoveConstruct = false; + } + + Declaration* dec = pthis->mBase->mParams; + while (dec) + { + if (dec->mType == DT_ELEMENT) + { + Declaration* bdec = dec->mBase; + int nitems = 1; + + if (bdec->mFlags & DTF_CONST) + { + hasConstMember = true; + simpleAssignment = false; + } + + while (bdec->mType == DT_TYPE_ARRAY) + bdec = bdec->mBase; + + if (bdec->mFlags & DTF_PURE_VIRTUAL) + mErrors->Error(dec->mLocation, ERRR_INSTANTIATE_ABSTRACT_CLASS, "Cannot instantiate abstract class", dec->mIdent); + + if (bdec->mType == DT_TYPE_STRUCT) + { + if (bdec->mDestructor) + { + simpleDestructor = false; + if (!(bdec->mDestructor->mBase->mFlags & DTF_REQUEST_INLINE)) + inlineDestructor = false; + } + if (bdec->mDefaultConstructor) + { + simpleConstructor = false; + if (!(bdec->mDefaultConstructor->mBase->mFlags & DTF_REQUEST_INLINE)) + inlineConstructor = false; + } + if (bdec->mCopyConstructor) + simpleCopy = false; + if (bdec->mCopyAssignment || bdec->mMoveAssignment) + simpleAssignment = false; + if (bdec->mMoveConstructor) + simpleMove = false; + else if (bdec->mDestructor || bdec->mDefaultConstructor || bdec->mCopyConstructor) + canMoveConstruct = false; + } + if (dec->mValue) + simpleConstructor = false; + } + dec = dec->mNext; + } + + if (!simpleDestructor) + { + if (!pthis->mBase->mDestructor) + { + Declaration* ctdec = new Declaration(mScanner->mLocation, DT_TYPE_FUNCTION); + ctdec->mSize = 0; + Declaration* pdec = nullptr; + ctdec->mBase = TheVoidTypeDeclaration; + ctdec->mFlags |= DTF_DEFINED; + + PrependThisArgument(ctdec, pthis); + + Declaration* cdec = new Declaration(ctdec->mLocation, DT_CONST_FUNCTION); + cdec->mBase = ctdec; + + cdec->mFlags |= cdec->mBase->mFlags & (DTF_CONST | DTF_VOLATILE); + if (inlineDestructor) + cdec->mFlags |= DTF_REQUEST_INLINE; + + cdec->mSection = mCodeSection; + + if (mCompilerOptions & COPT_NATIVE) + cdec->mFlags |= DTF_NATIVE; + + cdec->mIdent = dtorident; + cdec->mQualIdent = pthis->mBase->mScope->Mangle(cdec->mIdent); + + cdec->mCompilerOptions = mCompilerOptions; + cdec->mBase->mCompilerOptions = mCompilerOptions; + + cdec->mVarIndex = -1; + + cdec->mFlags |= DTF_DEFINED; + + cdec->mValue = new Expression(mScanner->mLocation, EX_VOID); + + pthis->mBase->mDestructor = AddMemberFunction(pthis->mBase, cdec); + + } + } + + if (!simpleConstructor) + { + if (!pthis->mBase->mDefaultConstructor) + { + Declaration* ctdec = new Declaration(mScanner->mLocation, DT_TYPE_FUNCTION); + ctdec->mSize = 0; + + ctdec->mBase = TheVoidTypeDeclaration; + ctdec->mFlags |= DTF_DEFINED; + + PrependThisArgument(ctdec, pthis); + + Declaration* cdec = new Declaration(ctdec->mLocation, DT_CONST_FUNCTION); + cdec->mBase = ctdec; + cdec->mIdent = ctorident; + cdec->mQualIdent = ctorqident; + + cdec->mFlags |= cdec->mBase->mFlags & (DTF_CONST | DTF_VOLATILE); + if (inlineConstructor) + cdec->mFlags |= DTF_REQUEST_INLINE; + + cdec->mSection = mCodeSection; + + if (mCompilerOptions & COPT_NATIVE) + cdec->mFlags |= DTF_NATIVE; + + cdec->mCompilerOptions = mCompilerOptions; + cdec->mBase->mCompilerOptions = mCompilerOptions; + + cdec->mVarIndex = -1; + + cdec->mFlags |= DTF_DEFINED; + + cdec->mValue = new Expression(mScanner->mLocation, EX_VOID); + + pthis->mBase->mDefaultConstructor = AddMemberFunction(pthis->mBase, cdec); + } + } + + if (!simpleCopy) + { + if (!pthis->mBase->mCopyConstructor) + { + Declaration* ctdec = new Declaration(mScanner->mLocation, DT_TYPE_FUNCTION); + ctdec->mSize = 0; + ctdec->mBase = TheVoidTypeDeclaration; + ctdec->mFlags |= DTF_DEFINED; + + Declaration* adec = new Declaration(mScanner->mLocation, DT_ARGUMENT); + adec->mVarIndex = 0; + adec->mOffset = 0; + adec->mBase = new Declaration(mScanner->mLocation, DT_TYPE_REFERENCE); + adec->mBase->mSize = 2; + adec->mBase->mBase = pthis->mBase->ToConstType(); + adec->mBase->mFlags |= DTF_CONST | DTF_DEFINED; + adec->mSize = adec->mBase->mSize; + adec->mIdent = adec->mQualIdent = Ident::Unique("_"); + + ctdec->mParams = adec; + + PrependThisArgument(ctdec, pthis); + + Declaration* cdec = new Declaration(ctdec->mLocation, DT_CONST_FUNCTION); + cdec->mBase = ctdec; + + cdec->mFlags |= cdec->mBase->mFlags & (DTF_CONST | DTF_VOLATILE); + if (inlineCopy) + cdec->mFlags |= DTF_REQUEST_INLINE; + + cdec->mSection = mCodeSection; + + if (mCompilerOptions & COPT_NATIVE) + cdec->mFlags |= DTF_NATIVE; + + pthis->mBase->mCopyConstructor = cdec; + + cdec->mIdent = ctorident; + cdec->mQualIdent = ctorqident; + + cdec->mCompilerOptions = mCompilerOptions; + cdec->mBase->mCompilerOptions = mCompilerOptions; + + cdec->mVarIndex = -1; + + if (explicitDestructor) + cdec->mFlags |= DTF_DEPRECATED; + + cdec->mValue = new Expression(mScanner->mLocation, EX_VOID); + + // Now add all the copying + + Expression* pthisexp = new Expression(pthis->mLocation, EX_VARIABLE); + pthisexp->mDecType = pthis; + pthisexp->mDecValue = cdec->mBase->mParams; + + Expression* thisexp = new Expression(pthis->mLocation, EX_PREFIX); + thisexp->mToken = TK_MUL; + thisexp->mDecType = pthis->mBase; + thisexp->mLeft = pthisexp; + + Expression* thatexp = new Expression(pthis->mLocation, EX_VARIABLE); + thatexp->mDecType = adec->mBase; + thatexp->mDecValue = cdec->mBase->mParams->mNext; + + cdec->mScope = new DeclarationScope(nullptr, SLEVEL_CLASS); + + pthis->mBase->mCopyConstructor = AddMemberFunction(pthis->mBase, cdec); + if (pthis->mBase->mCopyConstructor == cdec) + { + // Copy base class + + Declaration* bcdec = pthis->mBase->mBase; + while (bcdec) + { + if (bcdec->mBase->mCopyConstructor) + { + Declaration* mdec = bcdec->mBase->mCopyConstructor; + + Expression* cexp = new Expression(pthis->mLocation, EX_CONSTANT); + cexp->mDecValue = mdec; + cexp->mDecType = cexp->mDecValue->mBase; + + Expression* dexp = new Expression(pthis->mLocation, EX_CALL); + dexp->mLeft = cexp; + dexp->mRight = new Expression(mScanner->mLocation, EX_LIST); + dexp->mRight->mLeft = pthisexp; + dexp->mRight->mRight = thatexp; + + Declaration* mcdec = new Declaration(mScanner->mLocation, DT_CONST_CONSTRUCTOR); + mcdec->mIdent = bcdec->mBase->mIdent; + mcdec->mValue = dexp; + + cdec->mScope->Insert(bcdec->mBase->mIdent, mcdec); + } + else + { + Expression* cexp = new Expression(pthis->mLocation, EX_TYPECAST); + cexp->mLeft = pthisexp; + cexp->mDecType = bcdec->mBase->BuildPointer(pthis->mLocation); + + Expression* cexp2 = new Expression(pthis->mLocation, EX_PREFIX); + cexp2->mToken = TK_MUL; + cexp2->mDecType = bcdec->mBase; + cexp2->mLeft = cexp; + + Expression* dexp = new Expression(pthis->mLocation, EX_INITIALIZATION); + dexp->mToken = TK_ASSIGN; + dexp->mLeft = cexp2; + dexp->mRight = thatexp; + + Declaration* mcdec = new Declaration(mScanner->mLocation, DT_CONST_CONSTRUCTOR); + mcdec->mIdent = bcdec->mBase->mIdent; + mcdec->mValue = dexp; + + cdec->mScope->Insert(bcdec->mBase->mIdent, mcdec); + } + + bcdec = bcdec->mNext; + } + + Declaration* dec = pthis->mBase->mParams; + while (dec) + { + if (dec->mType == DT_ELEMENT) + { + Expression* lexp = new Expression(pthis->mLocation, EX_QUALIFY); + lexp->mLeft = thisexp; + lexp->mDecValue = dec; + lexp->mDecType = dec->mBase; + + Expression* rexp = new Expression(pthis->mLocation, EX_QUALIFY); + rexp->mLeft = thatexp; + rexp->mDecValue = dec; + rexp->mDecType = dec->mBase; + + Expression* mexp; + + Declaration* bdec = dec->mBase; + while (bdec->mType == DT_TYPE_ARRAY) + bdec = bdec->mBase; + + bdec = bdec->ToMutableType(); + + if (bdec->mType == DT_TYPE_STRUCT && bdec->mCopyConstructor) + { + Declaration* bpdec = bdec->BuildPointer(bdec->mLocation); + + if (dec->mBase->mType == DT_TYPE_STRUCT) + { + Expression* pexp = new Expression(pthis->mLocation, EX_PREFIX); + pexp->mLeft = lexp; + pexp->mToken = TK_BINARY_AND; + pexp->mDecType = bpdec; + + Declaration* mdec = dec->mBase->mCopyConstructor; + + Expression* cexp = new Expression(pthis->mLocation, EX_CONSTANT); + cexp->mDecValue = mdec; + cexp->mDecType = cexp->mDecValue->mBase; + + mexp = new Expression(mScanner->mLocation, EX_CALL); + mexp->mLeft = cexp; + mexp->mRight = new Expression(mScanner->mLocation, EX_LIST); + mexp->mRight->mLeft = pexp; + mexp->mRight->mRight = rexp; + } + else + { + Expression* lpexp = new Expression(pthis->mLocation, EX_PREFIX); + lpexp->mLeft = lexp; + lpexp->mToken = TK_BINARY_AND; + lpexp->mDecType = bpdec; + + Expression* rpexp = new Expression(pthis->mLocation, EX_PREFIX); + rpexp->mLeft = rexp; + rpexp->mToken = TK_BINARY_AND; + rpexp->mDecType = bpdec; + + Expression* cexp = new Expression(pthis->mLocation, EX_CONSTANT); + cexp->mDecValue = bdec->mVectorCopyConstructor; + cexp->mDecType = cexp->mDecValue->mBase; + + Declaration* ncdec = new Declaration(mScanner->mLocation, DT_CONST_INTEGER); + ncdec->mBase = TheUnsignedIntTypeDeclaration; + ncdec->mInteger = dec->mSize / bdec->mSize; + + mexp = new Expression(mScanner->mLocation, EX_CALL); + mexp->mLeft = cexp; + mexp->mRight = new Expression(mScanner->mLocation, EX_LIST); + mexp->mRight->mLeft = lpexp; + mexp->mRight->mRight = new Expression(mScanner->mLocation, EX_LIST); + mexp->mRight->mRight->mLeft = rpexp; + mexp->mRight->mRight->mRight = new Expression(mScanner->mLocation, EX_CONSTANT); + mexp->mRight->mRight->mRight->mDecType = ncdec->mBase; + mexp->mRight->mRight->mRight->mDecValue = ncdec; + + } + } + else + { + mexp = new Expression(mScanner->mLocation, EX_INITIALIZATION); + mexp->mToken = TK_ASSIGN; + mexp->mLeft = lexp; + mexp->mRight = rexp; + mexp->mDecType = lexp->mDecType; + } + + Declaration* mcdec = new Declaration(mScanner->mLocation, DT_CONST_CONSTRUCTOR); + mcdec->mIdent = dec->mIdent; + mcdec->mValue = mexp; + + cdec->mScope->Insert(dec->mIdent, mcdec); + } + + dec = dec->mNext; + } + + } + + cdec->mFlags |= DTF_DEFINED; + + } + +#if 1 + if (!pthis->mBase->mMoveConstructor && canMoveConstruct) + { + Declaration* ctdec = new Declaration(mScanner->mLocation, DT_TYPE_FUNCTION); + ctdec->mSize = 0; + ctdec->mBase = TheVoidTypeDeclaration; + ctdec->mFlags |= DTF_DEFINED; + + Declaration* adec = new Declaration(mScanner->mLocation, DT_ARGUMENT); + adec->mVarIndex = 0; + adec->mOffset = 0; + adec->mBase = new Declaration(mScanner->mLocation, DT_TYPE_RVALUEREF); + adec->mBase->mSize = 2; + adec->mBase->mBase = pthis->mBase; + adec->mBase->mFlags |= DTF_DEFINED; + adec->mSize = adec->mBase->mSize; + adec->mIdent = adec->mQualIdent = Ident::Unique("_"); + + ctdec->mParams = adec; + + PrependThisArgument(ctdec, pthis); + + Declaration* cdec = new Declaration(ctdec->mLocation, DT_CONST_FUNCTION); + cdec->mBase = ctdec; + + cdec->mFlags |= cdec->mBase->mFlags & (DTF_CONST | DTF_VOLATILE); + if (inlineCopy) + cdec->mFlags |= DTF_REQUEST_INLINE; + + cdec->mSection = mCodeSection; + + if (mCompilerOptions & COPT_NATIVE) + cdec->mFlags |= DTF_NATIVE; + + pthis->mBase->mMoveConstructor = cdec; + + cdec->mIdent = ctorident; + cdec->mQualIdent = ctorqident; + + cdec->mCompilerOptions = mCompilerOptions; + cdec->mBase->mCompilerOptions = mCompilerOptions; + + cdec->mVarIndex = -1; + + cdec->mValue = new Expression(mScanner->mLocation, EX_VOID); + + // Now add all the moving + + Expression* pthisexp = new Expression(pthis->mLocation, EX_VARIABLE); + pthisexp->mDecType = pthis; + pthisexp->mDecValue = cdec->mBase->mParams; + + Expression* thisexp = new Expression(mScanner->mLocation, EX_PREFIX); + thisexp->mToken = TK_MUL; + thisexp->mDecType = pthis->mBase; + thisexp->mLeft = pthisexp; + + Expression* thatexp = new Expression(pthis->mLocation, EX_VARIABLE); + thatexp->mDecType = adec->mBase; + thatexp->mDecValue = cdec->mBase->mParams->mNext; + + cdec->mScope = new DeclarationScope(nullptr, SLEVEL_CLASS); + + pthis->mBase->mMoveConstructor = AddMemberFunction(pthis->mBase, cdec); + if (pthis->mBase->mMoveConstructor == cdec) + { + // Move base class + + Declaration* bcdec = pthis->mBase->mBase; + while (bcdec) + { + if (bcdec->mBase->mMoveConstructor) + { + Declaration* mdec = bcdec->mBase->mMoveConstructor; + + Expression* cexp = new Expression(pthis->mLocation, EX_CONSTANT); + cexp->mDecValue = mdec; + cexp->mDecType = cexp->mDecValue->mBase; + + Expression* dexp = new Expression(pthis->mLocation, EX_CALL); + dexp->mLeft = cexp; + dexp->mRight = new Expression(mScanner->mLocation, EX_LIST); + dexp->mRight->mLeft = pthisexp; + dexp->mRight->mRight = thatexp; + + Declaration* mcdec = new Declaration(mScanner->mLocation, DT_CONST_CONSTRUCTOR); + mcdec->mIdent = bcdec->mBase->mIdent; + mcdec->mValue = dexp; + + cdec->mScope->Insert(bcdec->mBase->mIdent, mcdec); + } + else + { + Expression* cexp = new Expression(pthis->mLocation, EX_TYPECAST); + cexp->mLeft = pthisexp; + cexp->mDecType = bcdec->mBase->BuildPointer(pthis->mLocation); + + Expression* cexp2 = new Expression(pthis->mLocation, EX_PREFIX); + cexp2->mToken = TK_MUL; + cexp2->mDecType = bcdec->mBase; + cexp2->mLeft = cexp; + + Expression* dexp = new Expression(pthis->mLocation, EX_INITIALIZATION); + dexp->mToken = TK_ASSIGN; + dexp->mLeft = cexp2; + dexp->mRight = thatexp; + + Declaration* mcdec = new Declaration(mScanner->mLocation, DT_CONST_CONSTRUCTOR); + mcdec->mIdent = bcdec->mBase->mIdent; + mcdec->mValue = dexp; + + cdec->mScope->Insert(bcdec->mBase->mIdent, mcdec); + } + + bcdec = bcdec->mNext; + } + + Declaration* dec = pthis->mBase->mParams; + while (dec) + { + if (dec->mType == DT_ELEMENT) + { + Expression* lexp = new Expression(pthis->mLocation, EX_QUALIFY); + lexp->mLeft = thisexp; + lexp->mDecValue = dec; + lexp->mDecType = dec->mBase; + + Expression* rexp = new Expression(pthis->mLocation, EX_QUALIFY); + rexp->mLeft = thatexp; + rexp->mDecValue = dec; + rexp->mDecType = dec->mBase; + + Expression* mexp; + + Declaration* bdec = dec->mBase; + while (bdec->mType == DT_TYPE_ARRAY) + bdec = bdec->mBase; + + bdec = bdec->ToMutableType(); + + if (bdec->mType == DT_TYPE_STRUCT && bdec->mMoveConstructor) + { + Declaration* bpdec = bdec->BuildPointer(bdec->mLocation); + + if (dec->mBase->mType == DT_TYPE_STRUCT) + { + Expression* pexp = new Expression(pthis->mLocation, EX_PREFIX); + pexp->mLeft = lexp; + pexp->mToken = TK_BINARY_AND; + pexp->mDecType = bpdec; + + Declaration* mdec = dec->mBase->mMoveConstructor; + + Expression* cexp = new Expression(pthis->mLocation, EX_CONSTANT); + cexp->mDecValue = mdec; + cexp->mDecType = cexp->mDecValue->mBase; + + mexp = new Expression(mScanner->mLocation, EX_CALL); + mexp->mLeft = cexp; + mexp->mRight = new Expression(mScanner->mLocation, EX_LIST); + mexp->mRight->mLeft = pexp; + mexp->mRight->mRight = rexp; + } + else + { + Expression* lpexp = new Expression(pthis->mLocation, EX_PREFIX); + lpexp->mLeft = lexp; + lpexp->mToken = TK_BINARY_AND; + lpexp->mDecType = bpdec; + + Expression* rpexp = new Expression(pthis->mLocation, EX_PREFIX); + rpexp->mLeft = rexp; + rpexp->mToken = TK_BINARY_AND; + rpexp->mDecType = bpdec; + + Expression* cexp = new Expression(pthis->mLocation, EX_CONSTANT); + cexp->mDecValue = bdec->mVectorMoveConstructor; + cexp->mDecType = cexp->mDecValue->mBase; + + Declaration* ncdec = new Declaration(mScanner->mLocation, DT_CONST_INTEGER); + ncdec->mBase = TheUnsignedIntTypeDeclaration; + ncdec->mInteger = dec->mSize / bdec->mSize; + + mexp = new Expression(mScanner->mLocation, EX_CALL); + mexp->mLeft = cexp; + mexp->mRight = new Expression(mScanner->mLocation, EX_LIST); + mexp->mRight->mLeft = lpexp; + mexp->mRight->mRight = new Expression(mScanner->mLocation, EX_LIST); + mexp->mRight->mRight->mLeft = rpexp; + mexp->mRight->mRight->mRight = new Expression(mScanner->mLocation, EX_CONSTANT); + mexp->mRight->mRight->mRight->mDecType = ncdec->mBase; + mexp->mRight->mRight->mRight->mDecValue = ncdec; + + } + } + else + { + mexp = new Expression(mScanner->mLocation, EX_INITIALIZATION); + mexp->mToken = TK_ASSIGN; + mexp->mLeft = lexp; + mexp->mRight = rexp; + mexp->mDecType = lexp->mDecType; + } + + Declaration* mcdec = new Declaration(mScanner->mLocation, DT_CONST_CONSTRUCTOR); + mcdec->mIdent = mScanner->mTokenIdent; + mcdec->mValue = mexp; + + cdec->mScope->Insert(dec->mIdent, mcdec); + } + + dec = dec->mNext; + } + } + + cdec->mFlags |= DTF_DEFINED; + } +#endif + } + + if (!simpleAssignment) + { + // Copy assignment operator + if (!pthis->mBase->mCopyAssignment) + { + Declaration* ctdec = new Declaration(mScanner->mLocation, DT_TYPE_FUNCTION); + ctdec->mSize = 0; + ctdec->mBase = new Declaration(mScanner->mLocation, DT_TYPE_REFERENCE); + ctdec->mBase->mSize = 2; + ctdec->mBase->mBase = pthis->mBase; + ctdec->mBase->mFlags |= DTF_DEFINED; + ctdec->mFlags |= DTF_DEFINED; + + Declaration* adec = new Declaration(mScanner->mLocation, DT_ARGUMENT); + adec->mVarIndex = 0; + adec->mOffset = 0; + adec->mBase = new Declaration(mScanner->mLocation, DT_TYPE_REFERENCE); + adec->mBase->mSize = 2; + adec->mBase->mBase = pthis->mBase->ToConstType(); + adec->mBase->mFlags |= DTF_CONST | DTF_DEFINED; + adec->mSize = adec->mBase->mSize; + adec->mIdent = adec->mQualIdent = Ident::Unique("_"); + + ctdec->mParams = adec; + + PrependThisArgument(ctdec, pthis); + + Declaration* cdec = new Declaration(ctdec->mLocation, DT_CONST_FUNCTION); + cdec->mBase = ctdec; + + cdec->mFlags |= cdec->mBase->mFlags & (DTF_CONST | DTF_VOLATILE); + + cdec->mSection = mCodeSection; + + if (mCompilerOptions & COPT_NATIVE) + cdec->mFlags |= DTF_NATIVE; + + cdec->mIdent = Ident::Unique("operator="); + cdec->mQualIdent = pthis->mBase->mScope->Mangle(cdec->mIdent); + ctdec->mIdent = cdec->mQualIdent; + + cdec->mCompilerOptions = mCompilerOptions; + cdec->mBase->mCompilerOptions = mCompilerOptions; + + cdec->mVarIndex = -1; + + if (explicitDestructor) + cdec->mFlags |= DTF_DEPRECATED; + + Expression* pthisexp = new Expression(pthis->mLocation, EX_VARIABLE); + pthisexp->mDecType = pthis; + pthisexp->mDecValue = cdec->mBase->mParams; + + Expression* thisexp = new Expression(mScanner->mLocation, EX_PREFIX); + thisexp->mToken = TK_MUL; + thisexp->mDecType = pthis->mBase->ToMutableType(); + thisexp->mLeft = pthisexp; + + cdec->mValue = new Expression(mScanner->mLocation, EX_RETURN); + cdec->mValue->mLeft = thisexp; + cdec->mValue->mDecType = cdec->mBase; + + // Now add all the copying + + Expression* thatexp = new Expression(pthis->mLocation, EX_VARIABLE); + thatexp->mDecType = adec->mBase; + thatexp->mDecValue = cdec->mBase->mParams->mNext; + + cdec->mScope = new DeclarationScope(nullptr, SLEVEL_CLASS); + + pthis->mBase->mCopyAssignment = AddMemberFunction(pthis->mBase, cdec); + if (pthis->mBase->mCopyAssignment == cdec) + { + if (hasConstMember) + { + cdec->mFlags |= DTF_FUNC_DELETED; + ctdec->mFlags |= DTF_FUNC_DELETED; + } + else + { + Declaration* dec = pthis->mBase->mParams; + if (dec) + { + dec = dec->Last(); + while (dec) + { + if (dec->mType == DT_ELEMENT) + { + Expression* lexp = new Expression(pthis->mLocation, EX_QUALIFY); + lexp->mLeft = thisexp; + lexp->mDecValue = dec; + lexp->mDecType = dec->mBase; + + Expression* rexp = new Expression(pthis->mLocation, EX_QUALIFY); + rexp->mLeft = thatexp; + rexp->mDecValue = dec; + rexp->mDecType = dec->mBase; + + Expression* mexp; + + Declaration* bdec = dec->mBase; + while (bdec->mType == DT_TYPE_ARRAY) + bdec = bdec->mBase; + + if (bdec->mType == DT_TYPE_STRUCT && bdec->mCopyAssignment) + { + Declaration* bpdec = bdec->BuildPointer(bdec->mLocation); + + if (dec->mBase->mType == DT_TYPE_STRUCT) + { + Expression* pexp = new Expression(pthis->mLocation, EX_PREFIX); + pexp->mLeft = lexp; + pexp->mToken = TK_BINARY_AND; + pexp->mDecType = bpdec; + + Declaration* mdec = bdec->mCopyAssignment; + + Expression* cexp = new Expression(pthis->mLocation, EX_CONSTANT); + cexp->mDecValue = mdec; + cexp->mDecType = cexp->mDecValue->mBase; + + mexp = new Expression(mScanner->mLocation, EX_CALL); + mexp->mLeft = cexp; + mexp->mRight = new Expression(mScanner->mLocation, EX_LIST); + mexp->mRight->mLeft = pexp; + mexp->mRight->mRight = rexp; + } + else + { + Expression* lpexp = new Expression(pthis->mLocation, EX_PREFIX); + lpexp->mLeft = lexp; + lpexp->mToken = TK_BINARY_AND; + lpexp->mDecType = bpdec; + + Expression* rpexp = new Expression(pthis->mLocation, EX_PREFIX); + rpexp->mLeft = rexp; + rpexp->mToken = TK_BINARY_AND; + rpexp->mDecType = bpdec; + + Expression* cexp = new Expression(pthis->mLocation, EX_CONSTANT); + cexp->mDecValue = bdec->mVectorCopyAssignment; + cexp->mDecType = cexp->mDecValue->mBase; + + Declaration* ncdec = new Declaration(mScanner->mLocation, DT_CONST_INTEGER); + ncdec->mBase = TheUnsignedIntTypeDeclaration; + ncdec->mInteger = dec->mSize / bdec->mSize; + + mexp = new Expression(mScanner->mLocation, EX_CALL); + mexp->mLeft = cexp; + mexp->mRight = new Expression(mScanner->mLocation, EX_LIST); + mexp->mRight->mLeft = lpexp; + mexp->mRight->mRight = new Expression(mScanner->mLocation, EX_LIST); + mexp->mRight->mRight->mLeft = rpexp; + mexp->mRight->mRight->mRight = new Expression(mScanner->mLocation, EX_CONSTANT); + mexp->mRight->mRight->mRight->mDecType = ncdec->mBase; + mexp->mRight->mRight->mRight->mDecValue = ncdec; + } + } + else + { + mexp = new Expression(mScanner->mLocation, EX_ASSIGNMENT); + mexp->mToken = TK_ASSIGN; + mexp->mLeft = lexp; + mexp->mRight = rexp; + mexp->mDecType = lexp->mDecType; + } + + Expression* sexp = new Expression(mScanner->mLocation, EX_SEQUENCE); + sexp->mLeft = mexp; + sexp->mRight = cdec->mValue; + cdec->mValue = sexp; + } + + dec = dec->mPrev; + } + } + } + } + + cdec->mFlags |= DTF_DEFINED; + } + + // Move assignment operator + if (!pthis->mBase->mMoveAssignment && canMoveConstruct) + { + Declaration* ctdec = new Declaration(mScanner->mLocation, DT_TYPE_FUNCTION); + ctdec->mSize = 0; + ctdec->mBase = new Declaration(mScanner->mLocation, DT_TYPE_REFERENCE); + ctdec->mBase->mSize = 2; + ctdec->mBase->mBase = pthis->mBase; + ctdec->mBase->mFlags |= DTF_DEFINED; + ctdec->mFlags |= DTF_DEFINED; + + Declaration* adec = new Declaration(mScanner->mLocation, DT_ARGUMENT); + adec->mVarIndex = 0; + adec->mOffset = 0; + adec->mBase = new Declaration(mScanner->mLocation, DT_TYPE_RVALUEREF); + adec->mBase->mSize = 2; + adec->mBase->mBase = pthis->mBase; + adec->mBase->mFlags |= DTF_DEFINED; + adec->mSize = adec->mBase->mSize; + adec->mIdent = adec->mQualIdent = Ident::Unique("_"); + + ctdec->mParams = adec; + + PrependThisArgument(ctdec, pthis); + + Declaration* cdec = new Declaration(ctdec->mLocation, DT_CONST_FUNCTION); + cdec->mBase = ctdec; + + cdec->mFlags |= cdec->mBase->mFlags & (DTF_CONST | DTF_VOLATILE); + + cdec->mSection = mCodeSection; + + if (mCompilerOptions & COPT_NATIVE) + cdec->mFlags |= DTF_NATIVE; + + cdec->mIdent = Ident::Unique("operator="); + cdec->mQualIdent = pthis->mBase->mScope->Mangle(cdec->mIdent); + ctdec->mIdent = cdec->mQualIdent; + + cdec->mCompilerOptions = mCompilerOptions; + cdec->mBase->mCompilerOptions = mCompilerOptions; + + cdec->mVarIndex = -1; + + if (explicitDestructor) + cdec->mFlags |= DTF_DEPRECATED; + + Expression* pthisexp = new Expression(pthis->mLocation, EX_VARIABLE); + pthisexp->mDecType = pthis; + pthisexp->mDecValue = cdec->mBase->mParams; + + Expression* thisexp = new Expression(mScanner->mLocation, EX_PREFIX); + thisexp->mToken = TK_MUL; + thisexp->mDecType = pthis->mBase->ToMutableType(); + thisexp->mLeft = pthisexp; + + cdec->mValue = new Expression(mScanner->mLocation, EX_RETURN); + cdec->mValue->mLeft = thisexp; + cdec->mValue->mDecType = cdec->mBase; + + // Now add all the copying + + Expression* thatexp = new Expression(pthis->mLocation, EX_VARIABLE); + thatexp->mDecType = adec->mBase; + thatexp->mDecValue = cdec->mBase->mParams->mNext; + + cdec->mScope = new DeclarationScope(nullptr, SLEVEL_CLASS); + + pthis->mBase->mMoveAssignment = AddMemberFunction(pthis->mBase, cdec); + if (pthis->mBase->mMoveAssignment == cdec) + { + if (hasConstMember) + { + cdec->mFlags |= DTF_FUNC_DELETED; + ctdec->mFlags |= DTF_FUNC_DELETED; + } + else + { + Declaration* dec = pthis->mBase->mParams; + if (dec) + { + dec = dec->Last(); + while (dec) + { + if (dec->mType == DT_ELEMENT) + { + Expression* lexp = new Expression(pthis->mLocation, EX_QUALIFY); + lexp->mLeft = thisexp; + lexp->mDecValue = dec; + lexp->mDecType = dec->mBase; + + Expression* rexp = new Expression(pthis->mLocation, EX_QUALIFY); + rexp->mLeft = thatexp; + rexp->mDecValue = dec; + rexp->mDecType = dec->mBase; + + Expression* mexp; + + Declaration* bdec = dec->mBase; + while (bdec->mType == DT_TYPE_ARRAY) + bdec = bdec->mBase; + + if (bdec->mType == DT_TYPE_STRUCT && bdec->mMoveAssignment) + { + Declaration* bpdec = bdec->BuildPointer(bdec->mLocation); + + if (dec->mBase->mType == DT_TYPE_STRUCT) + { + Expression* pexp = new Expression(pthis->mLocation, EX_PREFIX); + pexp->mLeft = lexp; + pexp->mToken = TK_BINARY_AND; + pexp->mDecType = bpdec; + + Declaration* mdec = bdec->mMoveAssignment; + + Expression* cexp = new Expression(pthis->mLocation, EX_CONSTANT); + cexp->mDecValue = mdec; + cexp->mDecType = cexp->mDecValue->mBase; + + mexp = new Expression(mScanner->mLocation, EX_CALL); + mexp->mLeft = cexp; + mexp->mRight = new Expression(mScanner->mLocation, EX_LIST); + mexp->mRight->mLeft = pexp; + mexp->mRight->mRight = rexp; + } + else + { + Expression* lpexp = new Expression(pthis->mLocation, EX_PREFIX); + lpexp->mLeft = lexp; + lpexp->mToken = TK_BINARY_AND; + lpexp->mDecType = bpdec; + + Expression* rpexp = new Expression(pthis->mLocation, EX_PREFIX); + rpexp->mLeft = rexp; + rpexp->mToken = TK_BINARY_AND; + rpexp->mDecType = bpdec; + + Expression* cexp = new Expression(pthis->mLocation, EX_CONSTANT); + cexp->mDecValue = bdec->mVectorMoveAssignment; + cexp->mDecType = cexp->mDecValue->mBase; + + Declaration* ncdec = new Declaration(mScanner->mLocation, DT_CONST_INTEGER); + ncdec->mBase = TheUnsignedIntTypeDeclaration; + ncdec->mInteger = dec->mSize / bdec->mSize; + + mexp = new Expression(mScanner->mLocation, EX_CALL); + mexp->mLeft = cexp; + mexp->mRight = new Expression(mScanner->mLocation, EX_LIST); + mexp->mRight->mLeft = lpexp; + mexp->mRight->mRight = new Expression(mScanner->mLocation, EX_LIST); + mexp->mRight->mRight->mLeft = rpexp; + mexp->mRight->mRight->mRight = new Expression(mScanner->mLocation, EX_CONSTANT); + mexp->mRight->mRight->mRight->mDecType = ncdec->mBase; + mexp->mRight->mRight->mRight->mDecValue = ncdec; + } + } + else + { + mexp = new Expression(mScanner->mLocation, EX_ASSIGNMENT); + mexp->mToken = TK_ASSIGN; + mexp->mLeft = lexp; + mexp->mRight = rexp; + mexp->mDecType = lexp->mDecType; + } + + Expression* sexp = new Expression(mScanner->mLocation, EX_SEQUENCE); + sexp->mLeft = mexp; + sexp->mRight = cdec->mValue; + cdec->mValue = sexp; + } + + dec = dec->mPrev; + } + } + } + } + + cdec->mFlags |= DTF_DEFINED; + } + } + + if (pthis->mBase->mDefaultConstructor && !pthis->mBase->mVectorConstructor) + { + Declaration* ctdec = new Declaration(mScanner->mLocation, DT_TYPE_FUNCTION); + ctdec->mSize = 0; + Declaration* pdec = nullptr; + ctdec->mBase = TheVoidTypeDeclaration; + ctdec->mFlags |= DTF_DEFINED; + + Declaration* vthis = pthis->Clone(); + vthis->mFlags &= ~DTF_CONST; + + Declaration* adec = new Declaration(mScanner->mLocation, DT_ARGUMENT); + adec->mVarIndex = 0; + adec->mOffset = 0; + adec->mBase = pthis; + adec->mSize = adec->mBase->mSize; + adec->mIdent = adec->mQualIdent = Ident::Unique("end"); + + ctdec->mParams = adec; + + PrependThisArgument(ctdec, vthis); + + Declaration* cdec = new Declaration(ctdec->mLocation, DT_CONST_FUNCTION); + cdec->mBase = ctdec; + + cdec->mFlags |= cdec->mBase->mFlags & (DTF_CONST | DTF_VOLATILE); + + cdec->mSection = mCodeSection; + + if (mCompilerOptions & COPT_NATIVE) + cdec->mFlags |= DTF_NATIVE; + + cdec->mIdent = cvtorident; + cdec->mQualIdent = cvtorqident; + + cdec->mCompilerOptions = mCompilerOptions; + cdec->mBase->mCompilerOptions = mCompilerOptions; + + cdec->mVarIndex = -1; + + cdec->mFlags |= DTF_DEFINED; + + pthis->mBase->mVectorConstructor = AddMemberFunction(pthis->mBase, cdec); + if (pthis->mBase->mVectorConstructor == cdec) + { + Expression* pexp = new Expression(mScanner->mLocation, EX_VARIABLE); + pexp->mDecType = vthis; + pexp->mDecValue = ctdec->mParams; + + Expression* aexp = new Expression(mScanner->mLocation, EX_VARIABLE); + aexp->mDecType = pthis; + aexp->mDecValue = adec; + + Expression* iexp = new Expression(mScanner->mLocation, EX_POSTINCDEC); + iexp->mToken = TK_INC; + iexp->mLeft = pexp; + iexp->mDecType = pexp->mDecType; + + Expression* fexp = new Expression(mScanner->mLocation, EX_CONSTANT); + fexp->mDecValue = pthis->mBase->mDefaultConstructor; + fexp->mDecType = fexp->mDecValue->mBase; + + Expression* cexp = new Expression(mScanner->mLocation, EX_CALL); + cexp->mLeft = fexp; + cexp->mRight = iexp; + + Expression* wexp = new Expression(mScanner->mLocation, EX_WHILE); + wexp->mLeft = new Expression(mScanner->mLocation, EX_RELATIONAL); + wexp->mLeft->mToken = TK_NOT_EQUAL; + wexp->mLeft->mLeft = pexp; + wexp->mLeft->mRight = aexp; + wexp->mLeft->mDecType = TheBoolTypeDeclaration; + wexp->mRight = cexp; + + cdec->mValue = wexp; + } + } + + if (pthis->mBase->mDestructor && !pthis->mBase->mVectorDestructor) + { + Declaration* ctdec = new Declaration(mScanner->mLocation, DT_TYPE_FUNCTION); + ctdec->mSize = 0; + Declaration* pdec = nullptr; + ctdec->mBase = TheVoidTypeDeclaration; + ctdec->mFlags |= DTF_DEFINED; + + Declaration* vthis = pthis->Clone(); + vthis->mFlags &= ~DTF_CONST; + + Declaration* adec = new Declaration(mScanner->mLocation, DT_ARGUMENT); + adec->mVarIndex = 0; + adec->mOffset = 0; + adec->mBase = pthis; + adec->mSize = adec->mBase->mSize; + adec->mIdent = adec->mQualIdent = Ident::Unique("end"); + + ctdec->mParams = adec; + + PrependThisArgument(ctdec, vthis); + + Declaration* cdec = new Declaration(ctdec->mLocation, DT_CONST_FUNCTION); + cdec->mBase = ctdec; + + cdec->mFlags |= cdec->mBase->mFlags & (DTF_CONST | DTF_VOLATILE); + + cdec->mSection = mCodeSection; + + if (mCompilerOptions & COPT_NATIVE) + cdec->mFlags |= DTF_NATIVE; + + cdec->mIdent = dtorident; + cdec->mQualIdent = pthis->mBase->mScope->Mangle(cdec->mIdent); + + cdec->mCompilerOptions = mCompilerOptions; + cdec->mBase->mCompilerOptions = mCompilerOptions; + + cdec->mVarIndex = -1; + + cdec->mFlags |= DTF_DEFINED; + + pthis->mBase->mVectorDestructor = AddMemberFunction(pthis->mBase, cdec); + if (pthis->mBase->mVectorDestructor == cdec) + { + Expression* pexp = new Expression(mScanner->mLocation, EX_VARIABLE); + pexp->mDecType = vthis; + pexp->mDecValue = ctdec->mParams; + + Expression* aexp = new Expression(mScanner->mLocation, EX_VARIABLE); + aexp->mDecType = pthis; + aexp->mDecValue = adec; + + Expression* iexp = new Expression(mScanner->mLocation, EX_POSTINCDEC); + iexp->mToken = TK_INC; + iexp->mLeft = pexp; + iexp->mDecType = pexp->mDecType; + + Expression* fexp = new Expression(mScanner->mLocation, EX_CONSTANT); + fexp->mDecValue = pthis->mBase->mDestructor; + fexp->mDecType = fexp->mDecValue->mBase; + + Expression* cexp = new Expression(mScanner->mLocation, EX_CALL); + cexp->mLeft = fexp; + cexp->mRight = iexp; + + Expression* wexp = new Expression(mScanner->mLocation, EX_WHILE); + wexp->mLeft = new Expression(mScanner->mLocation, EX_RELATIONAL); + wexp->mLeft->mToken = TK_NOT_EQUAL; + wexp->mLeft->mLeft = pexp; + wexp->mLeft->mRight = aexp; + wexp->mLeft->mDecType = TheBoolTypeDeclaration; + + wexp->mRight = cexp; + + cdec->mValue = wexp; + } + } + + if (pthis->mBase->mCopyConstructor && !pthis->mBase->mVectorCopyConstructor) + { + Declaration* ctdec = new Declaration(mScanner->mLocation, DT_TYPE_FUNCTION); + ctdec->mSize = 0; + Declaration* pdec = nullptr; + ctdec->mBase = TheVoidTypeDeclaration; + ctdec->mFlags |= DTF_DEFINED; + + Declaration* sdec = new Declaration(mScanner->mLocation, DT_ARGUMENT); + sdec->mVarIndex = 0; + sdec->mOffset = 0; + sdec->mBase = pthis->Clone(); + sdec->mBase->mFlags &= ~DTF_CONST; + sdec->mSize = sdec->mBase->mSize; + sdec->mIdent = sdec->mQualIdent = Ident::Unique("_"); + + Declaration* adec = new Declaration(mScanner->mLocation, DT_ARGUMENT); + adec->mVarIndex = 2; + adec->mOffset = 0; + adec->mBase = TheUnsignedIntTypeDeclaration; + adec->mSize = adec->mBase->mSize; + adec->mIdent = adec->mQualIdent = Ident::Unique("n"); + + sdec->mNext = adec; + ctdec->mParams = sdec; + + Declaration* vthis = pthis->Clone(); + vthis->mFlags &= ~DTF_CONST; + PrependThisArgument(ctdec, vthis); + + Declaration* cdec = new Declaration(ctdec->mLocation, DT_CONST_FUNCTION); + cdec->mBase = ctdec; + + cdec->mFlags |= cdec->mBase->mFlags & (DTF_CONST | DTF_VOLATILE); + + cdec->mSection = mCodeSection; + + if (mCompilerOptions & COPT_NATIVE) + cdec->mFlags |= DTF_NATIVE; + + cdec->mIdent = cvtorident; + cdec->mQualIdent = pthis->mBase->mScope->Mangle(cdec->mIdent); + + cdec->mCompilerOptions = mCompilerOptions; + cdec->mBase->mCompilerOptions = mCompilerOptions; + + cdec->mVarIndex = -1; + + cdec->mFlags |= DTF_DEFINED; + + pthis->mBase->mVectorCopyConstructor = AddMemberFunction(pthis->mBase, cdec); + if (pthis->mBase->mVectorCopyConstructor == cdec) + { + Expression* pexp = new Expression(mScanner->mLocation, EX_VARIABLE); + pexp->mDecType = vthis; + pexp->mDecValue = ctdec->mParams; + + Expression* psexp = new Expression(mScanner->mLocation, EX_VARIABLE); + psexp->mDecType = vthis; + psexp->mDecValue = sdec; + + Expression* aexp = new Expression(mScanner->mLocation, EX_VARIABLE); + aexp->mDecType = TheUnsignedIntTypeDeclaration; + aexp->mDecValue = adec; + + Expression* iexp = new Expression(mScanner->mLocation, EX_POSTINCDEC); + iexp->mToken = TK_INC; + iexp->mLeft = pexp; + iexp->mDecType = pexp->mDecType; + + Expression* isexp = new Expression(mScanner->mLocation, EX_POSTINCDEC); + isexp->mToken = TK_INC; + isexp->mLeft = psexp; + isexp->mDecType = psexp->mDecType; + + Expression* disexp = new Expression(mScanner->mLocation, EX_PREFIX); + disexp->mToken = TK_MUL; + disexp->mLeft = isexp; + disexp->mDecType = vthis->mBase; + + Expression* dexp = new Expression(mScanner->mLocation, EX_POSTINCDEC); + dexp->mToken = TK_DEC; + dexp->mLeft = aexp; + dexp->mDecType = aexp->mDecType; + + Expression* fexp = new Expression(mScanner->mLocation, EX_CONSTANT); + fexp->mDecValue = pthis->mBase->mCopyConstructor; + fexp->mDecType = fexp->mDecValue->mBase; + + Expression* cexp = new Expression(mScanner->mLocation, EX_CALL); + cexp->mLeft = fexp; + cexp->mRight = new Expression(mScanner->mLocation, EX_LIST); + cexp->mRight->mLeft = iexp; + cexp->mRight->mRight = disexp; + + Expression* wexp = new Expression(mScanner->mLocation, EX_WHILE); + wexp->mLeft = aexp; + wexp->mRight = new Expression(mScanner->mLocation, EX_SEQUENCE); + wexp->mRight->mLeft = cexp; + wexp->mRight->mRight = dexp; + + cdec->mValue = wexp; + } + } + + if (pthis->mBase->mMoveConstructor && !pthis->mBase->mVectorMoveConstructor) + { + Declaration* ctdec = new Declaration(mScanner->mLocation, DT_TYPE_FUNCTION); + ctdec->mSize = 0; + Declaration* pdec = nullptr; + ctdec->mBase = TheVoidTypeDeclaration; + ctdec->mFlags |= DTF_DEFINED; + + Declaration* sdec = new Declaration(mScanner->mLocation, DT_ARGUMENT); + sdec->mVarIndex = 0; + sdec->mOffset = 0; + sdec->mBase = pthis->Clone(); + sdec->mBase->mFlags &= ~DTF_CONST; + sdec->mSize = sdec->mBase->mSize; + sdec->mIdent = sdec->mQualIdent = Ident::Unique("_"); + + Declaration* adec = new Declaration(mScanner->mLocation, DT_ARGUMENT); + adec->mVarIndex = 2; + adec->mOffset = 0; + adec->mBase = TheUnsignedIntTypeDeclaration; + adec->mSize = adec->mBase->mSize; + adec->mIdent = adec->mQualIdent = Ident::Unique("n"); + + sdec->mNext = adec; + ctdec->mParams = sdec; + + Declaration* vthis = pthis->Clone(); + vthis->mFlags &= ~DTF_CONST; + PrependThisArgument(ctdec, vthis); + + Declaration* cdec = new Declaration(ctdec->mLocation, DT_CONST_FUNCTION); + cdec->mBase = ctdec; + + cdec->mFlags |= cdec->mBase->mFlags & (DTF_CONST | DTF_VOLATILE); + + cdec->mSection = mCodeSection; + + if (mCompilerOptions & COPT_NATIVE) + cdec->mFlags |= DTF_NATIVE; + + cdec->mIdent = cvmtorident; + cdec->mQualIdent = pthis->mBase->mScope->Mangle(cdec->mIdent); + + cdec->mCompilerOptions = mCompilerOptions; + cdec->mBase->mCompilerOptions = mCompilerOptions; + + cdec->mVarIndex = -1; + + cdec->mFlags |= DTF_DEFINED; + + pthis->mBase->mVectorMoveConstructor = AddMemberFunction(pthis->mBase, cdec); + if (pthis->mBase->mVectorMoveConstructor == cdec) + { + Expression* pexp = new Expression(mScanner->mLocation, EX_VARIABLE); + pexp->mDecType = vthis; + pexp->mDecValue = ctdec->mParams; + + Expression* psexp = new Expression(mScanner->mLocation, EX_VARIABLE); + psexp->mDecType = vthis; + psexp->mDecValue = sdec; + + Expression* aexp = new Expression(mScanner->mLocation, EX_VARIABLE); + aexp->mDecType = TheUnsignedIntTypeDeclaration; + aexp->mDecValue = adec; + + Expression* iexp = new Expression(mScanner->mLocation, EX_POSTINCDEC); + iexp->mToken = TK_INC; + iexp->mLeft = pexp; + iexp->mDecType = pexp->mDecType; + + Expression* isexp = new Expression(mScanner->mLocation, EX_POSTINCDEC); + isexp->mToken = TK_INC; + isexp->mLeft = psexp; + isexp->mDecType = psexp->mDecType; + + Expression* disexp = new Expression(mScanner->mLocation, EX_PREFIX); + disexp->mToken = TK_MUL; + disexp->mLeft = isexp; + disexp->mDecType = vthis->mBase; + + Expression* dexp = new Expression(mScanner->mLocation, EX_POSTINCDEC); + dexp->mToken = TK_DEC; + dexp->mLeft = aexp; + dexp->mDecType = aexp->mDecType; + + Expression* fexp = new Expression(mScanner->mLocation, EX_CONSTANT); + fexp->mDecValue = pthis->mBase->mMoveConstructor; + fexp->mDecType = fexp->mDecValue->mBase; + + Expression* cexp = new Expression(mScanner->mLocation, EX_CALL); + cexp->mLeft = fexp; + cexp->mRight = new Expression(mScanner->mLocation, EX_LIST); + cexp->mRight->mLeft = iexp; + cexp->mRight->mRight = disexp; + + Expression* wexp = new Expression(mScanner->mLocation, EX_WHILE); + wexp->mLeft = aexp; + wexp->mRight = new Expression(mScanner->mLocation, EX_SEQUENCE); + wexp->mRight->mLeft = cexp; + wexp->mRight->mRight = dexp; + + cdec->mValue = wexp; + } + } + + if (pthis->mBase->mCopyAssignment && !pthis->mBase->mVectorCopyAssignment) + { + Declaration* ctdec = new Declaration(mScanner->mLocation, DT_TYPE_FUNCTION); + ctdec->mSize = 0; + Declaration* pdec = nullptr; + ctdec->mBase = TheVoidTypeDeclaration; + ctdec->mFlags |= DTF_DEFINED; + + Declaration* sdec = new Declaration(mScanner->mLocation, DT_ARGUMENT); + sdec->mVarIndex = 0; + sdec->mOffset = 0; + sdec->mBase = pthis->Clone(); + sdec->mBase->mFlags &= ~DTF_CONST; + sdec->mSize = sdec->mBase->mSize; + sdec->mIdent = sdec->mQualIdent = Ident::Unique("_"); + + Declaration* adec = new Declaration(mScanner->mLocation, DT_ARGUMENT); + adec->mVarIndex = 2; + adec->mOffset = 0; + adec->mBase = TheUnsignedIntTypeDeclaration; + adec->mSize = adec->mBase->mSize; + adec->mIdent = adec->mQualIdent = Ident::Unique("n"); + + sdec->mNext = adec; + ctdec->mParams = sdec; + + Declaration* vthis = pthis->Clone(); + vthis->mFlags &= ~DTF_CONST; + PrependThisArgument(ctdec, vthis); + + Declaration* cdec = new Declaration(ctdec->mLocation, DT_CONST_FUNCTION); + cdec->mBase = ctdec; + + cdec->mFlags |= cdec->mBase->mFlags & (DTF_CONST | DTF_VOLATILE); + + cdec->mSection = mCodeSection; + + if (mCompilerOptions & COPT_NATIVE) + cdec->mFlags |= DTF_NATIVE; + + cdec->mIdent = asgnvident; + cdec->mQualIdent = pthis->mBase->mScope->Mangle(cdec->mIdent); + + cdec->mCompilerOptions = mCompilerOptions; + cdec->mBase->mCompilerOptions = mCompilerOptions; + + cdec->mVarIndex = -1; + + cdec->mFlags |= DTF_DEFINED; + + pthis->mBase->mVectorCopyAssignment = AddMemberFunction(pthis->mBase, cdec); + if (pthis->mBase->mVectorCopyAssignment == cdec) + { + Expression* pexp = new Expression(mScanner->mLocation, EX_VARIABLE); + pexp->mDecType = vthis; + pexp->mDecValue = ctdec->mParams; + + Expression* psexp = new Expression(mScanner->mLocation, EX_VARIABLE); + psexp->mDecType = vthis; + psexp->mDecValue = sdec; + + Expression* aexp = new Expression(mScanner->mLocation, EX_VARIABLE); + aexp->mDecType = TheUnsignedIntTypeDeclaration; + aexp->mDecValue = adec; + + Expression* iexp = new Expression(mScanner->mLocation, EX_POSTINCDEC); + iexp->mToken = TK_INC; + iexp->mLeft = pexp; + iexp->mDecType = pexp->mDecType; + + Expression* isexp = new Expression(mScanner->mLocation, EX_POSTINCDEC); + isexp->mToken = TK_INC; + isexp->mLeft = psexp; + isexp->mDecType = psexp->mDecType; + + Expression* disexp = new Expression(mScanner->mLocation, EX_PREFIX); + disexp->mToken = TK_MUL; + disexp->mLeft = isexp; + disexp->mDecType = vthis->mBase; + + Expression* dexp = new Expression(mScanner->mLocation, EX_POSTINCDEC); + dexp->mToken = TK_DEC; + dexp->mLeft = aexp; + dexp->mDecValue = aexp->mDecType; + + Expression* fexp = new Expression(mScanner->mLocation, EX_CONSTANT); + fexp->mDecValue = pthis->mBase->mCopyAssignment; + fexp->mDecType = fexp->mDecValue->mBase; + + Expression* cexp = new Expression(mScanner->mLocation, EX_CALL); + cexp->mLeft = fexp; + cexp->mRight = new Expression(mScanner->mLocation, EX_LIST); + cexp->mRight->mLeft = iexp; + cexp->mRight->mRight = disexp; + + Expression* wexp = new Expression(mScanner->mLocation, EX_WHILE); + wexp->mLeft = aexp; + wexp->mRight = new Expression(mScanner->mLocation, EX_SEQUENCE); + wexp->mRight->mLeft = cexp; + wexp->mRight->mRight = dexp; + + cdec->mValue = wexp; + } + } + + if (pthis->mBase->mMoveAssignment && !pthis->mBase->mVectorMoveAssignment) + { + Declaration* ctdec = new Declaration(mScanner->mLocation, DT_TYPE_FUNCTION); + ctdec->mSize = 0; + Declaration* pdec = nullptr; + ctdec->mBase = TheVoidTypeDeclaration; + ctdec->mFlags |= DTF_DEFINED; + + Declaration* sdec = new Declaration(mScanner->mLocation, DT_ARGUMENT); + sdec->mVarIndex = 0; + sdec->mOffset = 0; + sdec->mBase = pthis->Clone(); + sdec->mBase->mFlags &= ~DTF_CONST; + sdec->mSize = sdec->mBase->mSize; + sdec->mIdent = sdec->mQualIdent = Ident::Unique("_"); + + Declaration* adec = new Declaration(mScanner->mLocation, DT_ARGUMENT); + adec->mVarIndex = 2; + adec->mOffset = 0; + adec->mBase = TheUnsignedIntTypeDeclaration; + adec->mSize = adec->mBase->mSize; + adec->mIdent = adec->mQualIdent = Ident::Unique("n"); + + sdec->mNext = adec; + ctdec->mParams = sdec; + + Declaration* vthis = pthis->Clone(); + vthis->mFlags &= ~DTF_CONST; + PrependThisArgument(ctdec, vthis); + + Declaration* cdec = new Declaration(ctdec->mLocation, DT_CONST_FUNCTION); + cdec->mBase = ctdec; + + cdec->mFlags |= cdec->mBase->mFlags & (DTF_CONST | DTF_VOLATILE); + + cdec->mSection = mCodeSection; + + if (mCompilerOptions & COPT_NATIVE) + cdec->mFlags |= DTF_NATIVE; + + cdec->mIdent = asgnvmident; + cdec->mQualIdent = pthis->mBase->mScope->Mangle(cdec->mIdent); + + cdec->mCompilerOptions = mCompilerOptions; + cdec->mBase->mCompilerOptions = mCompilerOptions; + + cdec->mVarIndex = -1; + + cdec->mFlags |= DTF_DEFINED; + + pthis->mBase->mVectorMoveAssignment = AddMemberFunction(pthis->mBase, cdec); + if (pthis->mBase->mVectorMoveAssignment == cdec) + { + Expression* pexp = new Expression(mScanner->mLocation, EX_VARIABLE); + pexp->mDecType = vthis; + pexp->mDecValue = ctdec->mParams; + + Expression* psexp = new Expression(mScanner->mLocation, EX_VARIABLE); + psexp->mDecType = vthis; + psexp->mDecValue = sdec; + + Expression* aexp = new Expression(mScanner->mLocation, EX_VARIABLE); + aexp->mDecType = TheUnsignedIntTypeDeclaration; + aexp->mDecValue = adec; + + Expression* iexp = new Expression(mScanner->mLocation, EX_POSTINCDEC); + iexp->mToken = TK_INC; + iexp->mLeft = pexp; + iexp->mDecType = pexp->mDecType; + + Expression* isexp = new Expression(mScanner->mLocation, EX_POSTINCDEC); + isexp->mToken = TK_INC; + isexp->mLeft = psexp; + isexp->mDecType = psexp->mDecType; + + Expression* disexp = new Expression(mScanner->mLocation, EX_PREFIX); + disexp->mToken = TK_MUL; + disexp->mLeft = isexp; + disexp->mDecType = vthis->mBase; + + Expression* dexp = new Expression(mScanner->mLocation, EX_POSTINCDEC); + dexp->mToken = TK_DEC; + dexp->mLeft = aexp; + dexp->mDecValue = aexp->mDecType; + + Expression* fexp = new Expression(mScanner->mLocation, EX_CONSTANT); + fexp->mDecValue = pthis->mBase->mMoveAssignment; + fexp->mDecType = fexp->mDecValue->mBase; + + Expression* cexp = new Expression(mScanner->mLocation, EX_CALL); + cexp->mLeft = fexp; + cexp->mRight = new Expression(mScanner->mLocation, EX_LIST); + cexp->mRight->mLeft = iexp; + cexp->mRight->mRight = disexp; + + Expression* wexp = new Expression(mScanner->mLocation, EX_WHILE); + wexp->mLeft = aexp; + wexp->mRight = new Expression(mScanner->mLocation, EX_SEQUENCE); + wexp->mRight->mLeft = cexp; + wexp->mRight->mRight = dexp; + + cdec->mValue = wexp; + } + } +} + +void Parser::AppendMemberDestructor(Declaration* pthis) +{ + if (pthis->mBase->mDestructor) + { + if (pthis->mBase->mDestructor->mFlags & DTF_DEFINED) + { + if (pthis->mBase->mDestructor->mFlags & DTF_COMPLETED) + return; + pthis->mBase->mDestructor->mFlags |= DTF_COMPLETED; + + Expression* pthisexp = new Expression(pthis->mLocation, EX_VARIABLE); + pthisexp->mDecType = pthis; + pthisexp->mDecValue = pthis->mBase->mDestructor->mBase->mParams; + + Expression* thisexp = new Expression(mScanner->mLocation, EX_PREFIX); + thisexp->mToken = TK_MUL; + thisexp->mDecType = pthis->mBase; + thisexp->mLeft = pthisexp; + + Declaration * dec = pthis->mBase->mParams; + if (dec) + { + dec = dec->Last(); + while (dec) + { + if (dec->mType == DT_ELEMENT) + { + Declaration* bdec = dec->mBase; + while (bdec->mType == DT_TYPE_ARRAY) + bdec = bdec->mBase; + + if (bdec->mType == DT_TYPE_STRUCT && bdec->mDestructor) + { + bdec = bdec->ToMutableType(); + + Declaration* bpdec = bdec->BuildPointer(pthis->mLocation); + + Expression* qexp = new Expression(pthis->mLocation, EX_QUALIFY); + qexp->mLeft = thisexp; + qexp->mDecValue = dec; + + Expression* dexp; + + if (dec->mBase->mType == DT_TYPE_STRUCT) + { + qexp->mDecType = bdec; + + Expression* pexp = new Expression(pthis->mLocation, EX_PREFIX); + pexp->mLeft = qexp; + pexp->mToken = TK_BINARY_AND; + pexp->mDecType = bpdec; + + Expression* cexp = new Expression(pthis->mLocation, EX_CONSTANT); + cexp->mDecValue = bdec->mDestructor; + cexp->mDecType = cexp->mDecValue->mBase; + + dexp = new Expression(mScanner->mLocation, EX_CALL); + dexp->mLeft = cexp; + dexp->mRight = pexp; + } + else + { + qexp->mDecType = dec->mBase->mBase; + + Expression* pexp = new Expression(pthis->mLocation, EX_PREFIX); + pexp->mLeft = qexp; + pexp->mToken = TK_BINARY_AND; + pexp->mDecType = bpdec; + + qexp = pexp; + + while (qexp->mDecType->mType == DT_TYPE_ARRAY) + { + Expression* iexp = new Expression(pthis->mLocation, EX_PREFIX); + iexp->mToken = TK_MUL; + iexp->mLeft = qexp; + iexp->mDecType = qexp->mDecType->mBase; + qexp = iexp; + } + + qexp->mDecType = new Declaration(pthis->mLocation, DT_TYPE_POINTER); + qexp->mDecType->mFlags |= DTF_CONST | DTF_DEFINED; + qexp->mDecType->mBase = bdec; + qexp->mDecType->mSize = 2; + + Expression* cexp = new Expression(pthis->mLocation, EX_CONSTANT); + cexp->mDecValue = bdec->mVectorDestructor; + cexp->mDecType = cexp->mDecValue->mBase; + + Declaration* ncdec = new Declaration(mScanner->mLocation, DT_CONST_INTEGER); + ncdec->mBase = TheUnsignedIntTypeDeclaration; + ncdec->mInteger = dec->mSize / bdec->mSize; + + dexp = new Expression(mScanner->mLocation, EX_CALL); + dexp->mLeft = cexp; + dexp->mRight = new Expression(mScanner->mLocation, EX_LIST); + dexp->mRight->mLeft = qexp; + dexp->mRight->mRight = new Expression(mScanner->mLocation, EX_BINARY); + dexp->mRight->mRight->mToken = TK_ADD; + dexp->mRight->mRight->mLeft = qexp; + dexp->mRight->mRight->mDecType = qexp->mDecType; + dexp->mRight->mRight->mRight = new Expression(mScanner->mLocation, EX_CONSTANT); + dexp->mRight->mRight->mRight->mDecType = ncdec->mBase; + dexp->mRight->mRight->mRight->mDecValue = ncdec; + } + + Expression* sexp = new Expression(mScanner->mLocation, EX_SEQUENCE); + + sexp->mLeft = pthis->mBase->mDestructor->mValue; + sexp->mRight = dexp; + + pthis->mBase->mDestructor->mValue = sexp; + } + } + + dec = dec->mPrev; + } + } + + Declaration* bcdec = pthis->mBase->mBase; + while (bcdec) + { + if (bcdec->mBase->mDestructor) + { + Declaration* mdec = bcdec->mBase->mDestructor; + + Expression* cexp = new Expression(pthis->mLocation, EX_CONSTANT); + cexp->mDecValue = mdec; + cexp->mDecType = cexp->mDecValue->mBase; + + Expression* dexp = new Expression(mScanner->mLocation, EX_CALL); + dexp->mLeft = cexp; + dexp->mRight = pthisexp; + + Expression* sexp = new Expression(mScanner->mLocation, EX_SEQUENCE); + + sexp->mLeft = pthis->mBase->mDestructor->mValue; + sexp->mRight = dexp; + pthis->mBase->mDestructor->mValue = sexp; + } + + bcdec = bcdec->mNext; + } + } + } +} + +void Parser::PrependThisArgument(Declaration* fdec, Declaration* pthis) +{ + Declaration* adec = new Declaration(fdec->mLocation, DT_ARGUMENT); + + if (fdec->mBase && fdec->mBase->mType == DT_TYPE_STRUCT) + adec->mVarIndex = 2; + else + adec->mVarIndex = 0; + + adec->mOffset = 0; + adec->mBase = pthis; + adec->mSize = adec->mBase->mSize; + adec->mNext = fdec->mParams; + adec->mIdent = adec->mQualIdent = Ident::Unique("this"); + + Declaration* p = fdec->mParams; + while (p) + { + p->mVarIndex += 2; + p = p->mNext; + } + + fdec->mParams = adec; + fdec->mClass = pthis; + fdec->mFlags |= DTF_FUNC_THIS; +} + +static Expression* ConcatExpression(Expression* e1, Expression* e2) +{ + if (e1) + { + if (e2) + { + Expression* seq = new Expression(e1->mLocation, EX_SEQUENCE); + seq->mLeft = e1; + seq->mRight = e2; + return seq; + } + return e1; + } + else + return e2; +} + +Declaration* Parser::AllocTempVar(Declaration* type) +{ + Declaration* ptemp = nullptr, * vtemp = mTempVars; + while (vtemp && !vtemp->mBase->IsSame(type)) + { + ptemp = vtemp; + vtemp = vtemp->mNext; + } + + if (vtemp) + { + if (ptemp) + ptemp->mNext = vtemp->mNext; + else + mTempVars = vtemp->mNext; + vtemp->mNext = nullptr; + vtemp->mValue = nullptr; + } + else + { + vtemp = new Declaration(mScanner->mLocation, DT_VARIABLE); + vtemp->mBase = type; + vtemp->mSize = type->mSize; + vtemp->mVarIndex = mLocalIndex++; + vtemp->mFlags |= DTF_DEFINED; + } + + return vtemp; +} + +void Parser::FreeTempVar(Declaration* var) +{ + var->mNext = mTempVars; + mTempVars = var; +} + +void Parser::FreeTempVarExp(Expression* exp) +{ + if (exp->mType == EX_SEQUENCE) + { + FreeTempVarExp(exp->mLeft); + FreeTempVarExp(exp->mRight); + } + else if (exp->mType == EX_CALL && exp->mLeft->mType == EX_CONSTANT && + exp->mRight->mType == EX_PREFIX && exp->mRight->mToken == TK_BINARY_AND && exp->mRight->mLeft->mType == EX_VARIABLE) + { + if (exp->mLeft->mDecValue->mIdent->mString[0] == '~' && exp->mRight->mLeft->mDecValue->mIdent == nullptr) + FreeTempVar(exp->mRight->mLeft->mDecValue); + } +} + +Expression* Parser::CleanupExpression(Expression* exp) +{ + if (exp) + { + Expression* xexp = AddFunctionCallRefReturned(exp); + if (xexp) + { + Expression* cexp = new Expression(exp->mLocation, EX_CLEANUP); + cexp->mLeft = exp; + cexp->mRight = xexp; + cexp->mDecType = exp->mDecType; + + FreeTempVarExp(xexp); + return cexp; + } + } + return exp; +} + +Expression* Parser::AddFunctionCallRefReturned(Expression* exp) +{ + Expression* lexp = nullptr, * rexp = nullptr; + + if (exp->mType == EX_PREFIX && exp->mToken == TK_BINARY_AND && exp->mLeft->mType == EX_CALL && exp->mLeft->mDecType->mType != DT_TYPE_REFERENCE && exp->mLeft->mDecType->mType != DT_TYPE_RVALUEREF) + { + lexp = AddFunctionCallRefReturned(exp->mLeft); + + // Returning a value object for pass by address + // add a temporary variable + + //int nindex = mLocalIndex++; + + Declaration* rtdec = exp->mLeft->mDecType; + + Declaration* vdec = AllocTempVar(rtdec); +#if 0 + Declaration* vdec = new Declaration(exp->mLocation, DT_VARIABLE); + + vdec->mVarIndex = nindex; + vdec->mBase = rtdec; + vdec->mSize = rtdec->mSize; +#endif + Expression* vexp = new Expression(exp->mLocation, EX_VARIABLE); + vexp->mDecType = rtdec; + vexp->mDecValue = vdec; + + Expression* pex = new Expression(exp->mLocation, EX_INITIALIZATION); + pex->mToken = TK_ASSIGN; + pex->mLeft = vexp; + pex->mRight = exp->mLeft; + pex->mDecValue = nullptr; + pex->mDecType = vdec->mBase; + + exp->mLeft = pex; + + if (rtdec->mDestructor) + { + Expression* texp = new Expression(mScanner->mLocation, EX_PREFIX); + texp->mToken = TK_BINARY_AND; + texp->mLeft = vexp; + texp->mDecType = new Declaration(mScanner->mLocation, DT_TYPE_POINTER); + texp->mDecType->mFlags |= DTF_CONST | DTF_DEFINED; + texp->mDecType->mBase = vdec->mBase; + texp->mDecType->mSize = 2; + + Expression* cexp = new Expression(mScanner->mLocation, EX_CONSTANT); + cexp->mDecValue = rtdec->mDestructor; + cexp->mDecType = cexp->mDecValue->mBase; + + Expression* dexp = new Expression(mScanner->mLocation, EX_CALL); + dexp->mLeft = cexp; + dexp->mRight = texp; + + rexp = ConcatExpression(rexp, dexp); + } + } + else if (exp->mType == EX_QUALIFY && exp->mLeft->mType == EX_CALL && exp->mLeft->mDecType->mType != DT_TYPE_REFERENCE && exp->mLeft->mDecType->mType != DT_TYPE_RVALUEREF) + { + lexp = AddFunctionCallRefReturned(exp->mLeft); + + // Returning a value object for pass by address + // add a temporary variable + +// int nindex = mLocalIndex++; + + Declaration* rtdec = exp->mLeft->mDecType; + + Declaration* vdec = AllocTempVar(rtdec); +#if 0 + Declaration* vdec = new Declaration(exp->mLocation, DT_VARIABLE); + + vdec->mVarIndex = nindex; + vdec->mBase = rtdec; + vdec->mSize = rtdec->mSize; +#endif + Expression* vexp = new Expression(exp->mLocation, EX_VARIABLE); + vexp->mDecType = rtdec; + vexp->mDecValue = vdec; + + Expression* pex = new Expression(exp->mLocation, EX_INITIALIZATION); + pex->mToken = TK_ASSIGN; + pex->mLeft = vexp; + pex->mRight = exp->mLeft; + pex->mDecValue = nullptr; + pex->mDecType = vdec->mBase; + + exp->mLeft = pex; + + if (rtdec->mDestructor) + { + Expression* texp = new Expression(mScanner->mLocation, EX_PREFIX); + texp->mToken = TK_BINARY_AND; + texp->mLeft = vexp; + texp->mDecType = new Declaration(mScanner->mLocation, DT_TYPE_POINTER); + texp->mDecType->mFlags |= DTF_CONST | DTF_DEFINED; + texp->mDecType->mBase = vdec->mBase; + texp->mDecType->mSize = 2; + + Expression* cexp = new Expression(mScanner->mLocation, EX_CONSTANT); + cexp->mDecValue = rtdec->mDestructor; + cexp->mDecType = cexp->mDecValue->mBase; + + Expression* dexp = new Expression(mScanner->mLocation, EX_CALL); + dexp->mLeft = cexp; + dexp->mRight = texp; + + rexp = ConcatExpression(rexp, dexp); + } + } + else if (exp->mType == EX_CALL) + { + lexp = AddFunctionCallRefReturned(exp->mLeft); + if (exp->mRight) + { + Declaration* pdec = exp->mLeft->mDecType->mParams; + Expression* rex = exp->mRight; + + while (pdec && rex) + { + Expression* pex = rex->mType == EX_LIST ? rex->mLeft : rex; + if (rex->mType == EX_LIST) + rex = rex->mRight; + else + rex = nullptr; + + rexp = ConcatExpression(rexp, AddFunctionCallRefReturned(pex)); + + if (pdec->mBase->IsComplexStruct() && pex->mType == EX_CALL && pex->mDecType->mType == DT_TYPE_STRUCT) + { + Declaration* vdec = AllocTempVar(pex->mDecType); + + Expression* vexp = new Expression(pex->mLocation, EX_VARIABLE); + vexp->mDecType = pex->mDecType; + vexp->mDecValue = vdec; + + Expression* cexp = new Expression(pex->mLocation, pex->mType); + cexp->mDecType = pex->mDecType; + cexp->mDecValue = pex->mDecValue; + cexp->mLeft = pex->mLeft; + cexp->mRight = pex->mRight; + cexp->mToken = pex->mToken; + + pex->mType = EX_INITIALIZATION; + pex->mToken = TK_ASSIGN; + pex->mLeft = vexp; + pex->mRight = cexp; + pex->mDecValue = nullptr; + pex->mDecType = vdec->mBase; + + if (vdec->mBase->mDestructor) + { + Expression* texp = new Expression(mScanner->mLocation, EX_PREFIX); + texp->mToken = TK_BINARY_AND; + texp->mLeft = vexp; + texp->mDecType = new Declaration(mScanner->mLocation, DT_TYPE_POINTER); + texp->mDecType->mFlags |= DTF_CONST | DTF_DEFINED; + texp->mDecType->mBase = vdec->mBase; + texp->mDecType->mSize = 2; + + Expression* cexp = new Expression(mScanner->mLocation, EX_CONSTANT); + cexp->mDecValue = vdec->mBase->mDestructor; + cexp->mDecType = cexp->mDecValue->mBase; + + Expression* dexp = new Expression(mScanner->mLocation, EX_CALL); + dexp->mLeft = cexp; + dexp->mRight = texp; + + rexp = ConcatExpression(rexp, dexp); + } + } + else if ((pdec->mBase->mType == DT_TYPE_REFERENCE || pdec->mBase->mType == DT_TYPE_RVALUEREF) && + (pex->mDecType->mType != DT_TYPE_REFERENCE && pex->mDecType->mType != DT_TYPE_RVALUEREF) && pex->mType == EX_CALL) + { + // Returning a value object for pass as reference + // add a temporary variable + + Declaration* vdec = AllocTempVar(pex->mDecType); + + Expression* vexp = new Expression(pex->mLocation, EX_VARIABLE); + vexp->mDecType = pex->mDecType; + vexp->mDecValue = vdec; + + Expression* cexp = new Expression(pex->mLocation, pex->mType); + cexp->mDecType = pex->mDecType; + cexp->mDecValue = pex->mDecValue; + cexp->mLeft = pex->mLeft; + cexp->mRight = pex->mRight; + cexp->mToken = pex->mToken; + + pex->mType = EX_INITIALIZATION; + pex->mToken = TK_ASSIGN; + pex->mLeft = vexp; + pex->mRight = cexp; + pex->mDecValue = nullptr; + pex->mDecType = vdec->mBase; + + if (vdec->mBase->mDestructor) + { + Expression* texp = new Expression(mScanner->mLocation, EX_PREFIX); + texp->mToken = TK_BINARY_AND; + texp->mLeft = vexp; + texp->mDecType = new Declaration(mScanner->mLocation, DT_TYPE_POINTER); + texp->mDecType->mFlags |= DTF_CONST | DTF_DEFINED; + texp->mDecType->mBase = vdec->mBase; + texp->mDecType->mSize = 2; + + Expression* cexp = new Expression(mScanner->mLocation, EX_CONSTANT); + cexp->mDecValue = vdec->mBase->mDestructor; + cexp->mDecType = cexp->mDecValue->mBase; + + Expression * dexp = new Expression(mScanner->mLocation, EX_CALL); + dexp->mLeft = cexp; + dexp->mRight = texp; + + rexp = ConcatExpression(rexp, dexp); + } + } + else if ((pdec->mBase->mType == DT_TYPE_REFERENCE || pdec->mBase->mType == DT_TYPE_RVALUEREF) && pex->mType == EX_CONSTRUCT && pex->mRight->mType == EX_VARIABLE) + { + // A constructed object is passed by reference + + if (pex->mLeft->mRight) + { + rexp = ConcatExpression(rexp, pex->mLeft->mRight); + pex->mLeft->mRight = nullptr; + } + } + else if ((pdec->mBase->mType == DT_TYPE_REFERENCE || pdec->mBase->mType == DT_TYPE_RVALUEREF) && pdec->mBase->mBase->mType == DT_TYPE_POINTER && pex->mDecType->mType == DT_TYPE_ARRAY) + { + // An array is passed by const ref to a pointer + + if (pdec->mBase->mType == DT_TYPE_REFERENCE && !(pdec->mBase->mBase->mFlags & DTF_CONST)) + mErrors->Error(pex->mLocation, EERR_INCOMPATIBLE_TYPES, "Can't pass constant as non constant reference"); + + Declaration* vdec = AllocTempVar(pdec->mBase->mBase); + + Expression* vexp = new Expression(pex->mLocation, EX_VARIABLE); + vexp->mDecType = pdec->mBase->mBase; + vexp->mDecValue = vdec; + + Expression* cexp = new Expression(pex->mLocation, pex->mType); + cexp->mDecType = pex->mDecType; + cexp->mDecValue = pex->mDecValue; + cexp->mLeft = pex->mLeft; + cexp->mRight = pex->mRight; + cexp->mToken = pex->mToken; + + pex->mType = EX_INITIALIZATION; + pex->mToken = TK_ASSIGN; + pex->mLeft = vexp; + pex->mRight = cexp; + pex->mDecValue = nullptr; + pex->mDecType = vdec->mBase; + } + else if ((pdec->mBase->mType == DT_TYPE_REFERENCE || pdec->mBase->mType == DT_TYPE_RVALUEREF) && pex->mType == EX_CONSTANT) + { + // A simple constant is passed by const ref + if (pex->mDecValue->mType == DT_CONST_INTEGER || pex->mDecValue->mType == DT_CONST_FLOAT || pex->mDecValue->mType == DT_CONST_POINTER || pex->mDecValue->mType == DT_CONST_ADDRESS) + { + if (pdec->mBase->mType == DT_TYPE_REFERENCE && !(pdec->mBase->mBase->mFlags & DTF_CONST)) + mErrors->Error(pex->mLocation, EERR_INCOMPATIBLE_TYPES, "Can't pass constant as non constant reference"); + + Declaration* vdec = AllocTempVar(pdec->mBase->mBase); + + Expression* vexp = new Expression(pex->mLocation, EX_VARIABLE); + vexp->mDecType = pdec->mBase->mBase; + vexp->mDecValue = vdec; + + Expression* cexp = new Expression(pex->mLocation, pex->mType); + cexp->mDecType = pex->mDecType; + cexp->mDecValue = pex->mDecValue; + cexp->mLeft = pex->mLeft; + cexp->mRight = pex->mRight; + cexp->mToken = pex->mToken; + + pex->mType = EX_INITIALIZATION; + pex->mToken = TK_ASSIGN; + pex->mLeft = vexp; + pex->mRight = cexp; + pex->mDecValue = nullptr; + pex->mDecType = vdec->mBase; + } + } + else if ((pdec->mBase->mType == DT_TYPE_REFERENCE && !pex->IsConstRef()) || (pdec->mBase->mType == DT_TYPE_RVALUEREF && !pex->IsLValue())) + { + if (pdec->mBase->mType == DT_TYPE_REFERENCE && !(pdec->mBase->mBase->mFlags & DTF_CONST)) + mErrors->Error(pex->mLocation, EERR_NOT_AN_LVALUE, "Can't pass rvalue as non constant reference"); + + Declaration* vdec = AllocTempVar(pex->mDecType); + + Expression* vexp = new Expression(pex->mLocation, EX_VARIABLE); + vexp->mDecType = pex->mDecType; + vexp->mDecValue = vdec; + + Expression* cexp = new Expression(pex->mLocation, pex->mType); + cexp->mDecType = pex->mDecType; + cexp->mDecValue = pex->mDecValue; + cexp->mLeft = pex->mLeft; + cexp->mRight = pex->mRight; + cexp->mToken = pex->mToken; + + pex->mType = EX_INITIALIZATION; + pex->mToken = TK_ASSIGN; + pex->mLeft = vexp; + pex->mRight = cexp; + pex->mDecValue = nullptr; + pex->mDecType = vdec->mBase; + } + else if (pex->mType == EX_CONSTRUCT && !pdec->IsReference()) + { + pex->mLeft->mRight = nullptr; + } +#if 1 + else if (pex->mType == EX_PREFIX && pex->mToken == TK_BINARY_AND && pex->mLeft->mType == EX_CONSTRUCT) + { + if (pex->mLeft->mRight && pex->mLeft->mRight->mType == EX_VARIABLE && (pex->mLeft->mRight->mDecValue->mFlags & DTF_TEMPORARY)) + { + rexp = ConcatExpression(rexp, pex->mLeft->mLeft->mRight); + pex->mLeft->mLeft->mRight = nullptr; + } + } +#endif + pdec = pdec->mNext; + } + } + } + else if (exp->mType == EX_LOGICAL_AND || exp->mType == EX_LOGICAL_OR) + { + lexp = AddFunctionCallRefReturned(exp->mLeft); + exp->mRight = CleanupExpression(exp->mRight); + } + else + { + if (exp->mLeft) + lexp = AddFunctionCallRefReturned(exp->mLeft); + if (exp->mRight) + rexp = AddFunctionCallRefReturned(exp->mRight); + } + + return ConcatExpression(lexp, rexp); +} + +Expression* Parser::BuildVariableArrayInit(Expression * texp, Declaration* bpdec, Declaration* pdec, int boffset, int & offset) +{ + Expression* lexp = nullptr; + + if (boffset + pdec->mOffset != offset) + mErrors->Error(pdec->mLocation, EERR_INVALID_CLASS_INITIALIZER, "Incomplete class initializer list"); + + if (pdec->mType == DT_CONST_STRUCT) + { + boffset += offset; + + Declaration* edec = pdec->mParams; + while (edec) + { + Expression * iexp = BuildVariableArrayInit(texp, bpdec, edec, boffset, offset); + + if (lexp) + { + Expression* nexp = new Expression(mScanner->mLocation, EX_LIST); + nexp->mLeft = lexp; + nexp->mRight = iexp; + lexp = nexp; + } + else + lexp = iexp; + + edec = edec->mNext; + } + } + else if (pdec->mType == DT_CONST_CONSTRUCTOR) + { + Declaration* icdec = new Declaration(mScanner->mLocation, DT_CONST_INTEGER); + icdec->mBase = TheUnsignedIntTypeDeclaration; + icdec->mInteger = offset / bpdec->mBase->mSize; + + Expression* icexp = new Expression(mScanner->mLocation, EX_CONSTANT); + icexp->mDecType = icdec->mBase; + icexp->mDecValue = icdec; + + Expression* mexp = new Expression(mScanner->mLocation, EX_BINARY); + mexp->mToken = TK_ADD; + mexp->mLeft = texp; + mexp->mRight = icexp; + mexp->mDecType = bpdec; + + Expression* iexp = new Expression(pdec->mValue->mLocation, EX_CONSTRUCT); + iexp->mLeft = new Expression(pdec->mValue->mLeft->mLocation, EX_LIST); + iexp->mLeft->mLeft = new Expression(pdec->mValue->mLeft->mLeft->mLocation, EX_CALL); + iexp->mLeft->mLeft->mLeft = pdec->mValue->mLeft->mLeft->mLeft; + iexp->mLeft->mLeft->mDecType = pdec->mValue->mLeft->mLeft->mDecType; + iexp->mLeft->mLeft->mRight = new Expression(pdec->mValue->mLeft->mLeft->mRight->mLocation, EX_LIST); + iexp->mLeft->mLeft->mRight->mRight = pdec->mValue->mLeft->mLeft->mRight->mRight; + iexp->mLeft->mLeft->mRight->mLeft = mexp; + + lexp = iexp; + + offset += bpdec->mBase->mSize; + } + else + mErrors->Error(pdec->mLocation, EERR_INVALID_CLASS_INITIALIZER, "Invalid class initializer"); + + return lexp; +} + +Expression * Parser::BuildVariableInit(Expression* vexp, Expression* pexp) +{ + Declaration* mtype = vexp->mDecType; + + Declaration* fcons = mtype->mScope ? mtype->mScope->Lookup(mtype->mIdent->PreMangle("+"), SLEVEL_CLASS) : nullptr; + + if (mtype->mFlags & DTF_PURE_VIRTUAL) + mErrors->Error(vexp->mLocation, ERRR_INSTANTIATE_ABSTRACT_CLASS, "Cannot instantiate abstract class", mtype->mIdent); + + if (fcons) + { + Expression* cexp = new Expression(mScanner->mLocation, EX_CONSTANT); + cexp->mDecValue = fcons; + cexp->mDecType = cexp->mDecValue->mBase; + + Expression* fexp = new Expression(mScanner->mLocation, EX_CALL); + fexp->mLeft = cexp; + fexp->mRight = pexp; + + Expression* texp = new Expression(mScanner->mLocation, EX_PREFIX); + texp->mToken = TK_BINARY_AND; + texp->mLeft = vexp; + texp->mDecType = new Declaration(mScanner->mLocation, DT_TYPE_POINTER); + texp->mDecType->mFlags |= DTF_CONST | DTF_DEFINED; + texp->mDecType->mBase = mtype; + texp->mDecType->mSize = 2; + + if (fexp->mRight) + { + Expression* lexp = new Expression(mScanner->mLocation, EX_LIST); + lexp->mLeft = texp; + lexp->mRight = fexp->mRight; + fexp->mRight = lexp; + } + else + fexp->mRight = texp; + + fexp = ResolveOverloadCall(fexp); + + Expression* nexp = new Expression(mScanner->mLocation, EX_CONSTRUCT); + + nexp->mLeft = new Expression(mScanner->mLocation, EX_LIST); + nexp->mLeft->mLeft = fexp; + + nexp->mRight = vexp; + nexp->mDecType = vexp->mDecType; + + return nexp; + } + else if (mtype->CanAssign(pexp->mDecType)) + { + Expression * nexp = new Expression(mScanner->mLocation, EX_INITIALIZATION); + nexp->mToken = TK_ASSIGN; + nexp->mDecType = mtype; + nexp->mLeft = vexp; + nexp->mRight = pexp; + + return nexp; + } + else if (pexp) + mErrors->Error(pexp->mLocation, EERR_INCOMPATIBLE_TYPES, "Can not initialize variable with expression", mtype->mIdent); + + return pexp; +} + +void Parser::ParseVariableInit(Declaration* ndec, Expression* pexp) +{ + Declaration* fcons = ndec->mBase->mScope ? ndec->mBase->mScope->Lookup(ndec->mBase->mIdent->PreMangle("+"), SLEVEL_CLASS) : nullptr; + + if (!pexp) + { + Token ctoken = mScanner->mToken == TK_OPEN_BRACE ? TK_CLOSE_BRACE : TK_CLOSE_PARENTHESIS; + + mScanner->NextToken(); + if (mScanner->mToken != ctoken) + { + pexp = ParseListExpression(false, fcons ? fcons->mBase->mParams->mNext : nullptr); + ConsumeToken(ctoken); + } + else + mScanner->NextToken(); + } + + if (ndec->mBase->mFlags & DTF_PURE_VIRTUAL) + mErrors->Error(ndec->mLocation, ERRR_INSTANTIATE_ABSTRACT_CLASS, "Cannot instantiate abstract class", ndec->mIdent); + + if (pexp && pexp->mType != EX_LIST) + { + if (pexp->mDecType->IsSame(ndec->mBase)) + fcons = ndec->mBase->mCopyConstructor; + } + + if (fcons) + { + Declaration* mtype = ndec->mBase->ToMutableType(); + + Expression* vexp = new Expression(mScanner->mLocation, EX_VARIABLE); + vexp->mDecType = mtype; + vexp->mDecValue = ndec; + + Expression* cexp = new Expression(mScanner->mLocation, EX_CONSTANT); + cexp->mDecValue = fcons; + cexp->mDecType = cexp->mDecValue->mBase; + + Expression* fexp = new Expression(mScanner->mLocation, EX_CALL); + fexp->mLeft = cexp; + fexp->mRight = pexp; + + Expression* texp = new Expression(mScanner->mLocation, EX_PREFIX); + texp->mToken = TK_BINARY_AND; + texp->mLeft = vexp; + texp->mDecType = new Declaration(mScanner->mLocation, DT_TYPE_POINTER); + texp->mDecType->mFlags |= DTF_CONST | DTF_DEFINED; + texp->mDecType->mBase = mtype; + texp->mDecType->mSize = 2; + + if (fexp->mRight) + { + Expression* lexp = new Expression(mScanner->mLocation, EX_LIST); + lexp->mLeft = texp; + lexp->mRight = fexp->mRight; + fexp->mRight = lexp; + } + else + fexp->mRight = texp; + + fexp = ResolveOverloadCall(fexp); + + if ((fexp->mLeft->mDecValue->mFlags & DTF_CONSTEXPR) && ndec->mSection == mBSSection) + ndec->mSection = mDataSection; + + Expression* dexp = nullptr; + if (ndec->mBase->mDestructor) + { + Expression* cexp = new Expression(mScanner->mLocation, EX_CONSTANT); + cexp->mDecValue = ndec->mBase->mDestructor; + cexp->mDecType = cexp->mDecValue->mBase; + + dexp = new Expression(mScanner->mLocation, EX_CALL); + dexp->mLeft = cexp; + dexp->mRight = texp; + } + + Expression* nexp = new Expression(mScanner->mLocation, EX_CONSTRUCT); + + nexp->mLeft = new Expression(mScanner->mLocation, EX_LIST); + nexp->mLeft->mLeft = fexp; + nexp->mLeft->mRight = dexp; + + nexp->mRight = vexp; + nexp->mDecType = vexp->mDecType; + + ndec->mValue = nexp; + } + else if (pexp && pexp->mType != EX_LIST) + { + ndec->mBase = ndec->mBase->DeduceAutoInit(pexp->mDecType); + if (ndec->mBase->CanAssign(pexp->mDecType)) + ndec->mValue = pexp; + else + mErrors->Error(pexp->mLocation, EERR_INCOMPATIBLE_TYPES, "Can not initialize variable with expression", ndec->mIdent); + } + else if (pexp) + mErrors->Error(pexp->mLocation, EERR_INCOMPATIBLE_TYPES, "Can not initialize variable with expression", ndec->mIdent); + else + mErrors->Error(mScanner->mLocation, EERR_INCOMPATIBLE_TYPES, "Can not initialize variable with expression", ndec->mIdent); +} + +void Parser::ExpandStructuredBinding(Declaration* dec) +{ + if (dec->mParams && dec->mParams->mType == DT_BINDING) + { + Declaration* pdec = dec->mBase->NonRefBase(); + if (pdec->mType != DT_TYPE_AUTO) + { + Expression* vex = new Expression(dec->mLocation, EX_VARIABLE); + vex->mDecType = dec->mBase; + vex->mDecValue = dec; + + Declaration* bdec = dec->mParams; + if (pdec->mType == DT_TYPE_STRUCT) + { + Declaration* sdec = pdec->mParams; + while (bdec) + { + if (sdec) + { + if (sdec->mType == DT_ELEMENT) + { + Expression* ex = new Expression(bdec->mLocation, EX_QUALIFY); + ex->mLeft = vex; + ex->mDecType = sdec->mBase; + ex->mDecValue = sdec; + bdec->mValue = ex; + + if (mScope->Insert(bdec->mIdent, bdec)) + mErrors->Error(dec->mLocation, ERRR_INVALID_STRUCT_BINDING_TYPE, "Duplicate struct binding identifier"); + + bdec = bdec->mNext; + } + + sdec = sdec->mNext; + } + else + { + mErrors->Error(dec->mLocation, ERRR_INVALID_STRUCT_BINDING_TYPE, "Too many elements in structured binding"); + break; + } + } + } + else if (pdec->mType == DT_TYPE_ARRAY) + { + int index = 0; + while (bdec) + { + if (index * pdec->mBase->mSize > pdec->mSize) + mErrors->Error(dec->mLocation, ERRR_INVALID_STRUCT_BINDING_TYPE, "Too many elements in structured binding"); + + Expression* ex = new Expression(bdec->mLocation, EX_INDEX); + ex->mLeft = vex; + ex->mRight = new Expression(bdec->mLocation, EX_CONSTANT); + ex->mRight->mDecType = TheSignedIntTypeDeclaration; + ex->mRight->mDecValue = new Declaration(bdec->mLocation, DT_CONST_INTEGER); + ex->mRight->mDecValue->mBase = TheSignedIntTypeDeclaration; + ex->mRight->mDecValue->mSize = 2; + ex->mRight->mDecValue->mInteger = index; + ex->mDecType = pdec->mBase; + bdec->mValue = ex; + index++; + + if (mScope->Insert(bdec->mIdent, bdec)) + mErrors->Error(dec->mLocation, ERRR_INVALID_STRUCT_BINDING_TYPE, "Duplicate struct binding identifier"); + + bdec = bdec->mNext; + } + } + else + mErrors->Error(dec->mLocation, ERRR_INVALID_STRUCT_BINDING_TYPE, "Invalid structured binding type"); + } + } +} + +Declaration* Parser::ParseDeclaration(Declaration * pdec, bool variable, bool expression, bool member, Declaration* pthis, Declaration* ptempl) +{ + bool definingType = false, destructor = false; + uint64 storageFlags = 0, typeFlags = 0; + Declaration* bdec; + + if (pdec) + { + bdec = pdec; + } + else + { + if (mScanner->mToken == TK_TYPEDEF) + { + definingType = true; + variable = false; + mScanner->NextToken(); + } + else if (mScanner->mToken == TK_USING) + { + mScanner->NextToken(); + if (ConsumeTokenIf(TK_NAMESPACE)) + { + Declaration* dec = ParseQualIdent(true); + if (dec) + { + if (dec->mType == DT_NAMESPACE) + { + mScope->UseScope(dec->mScope); + } + else + { + mErrors->Error(mScanner->mLocation, EERR_INCOMPATIBLE_OPERATOR, "Not a namespace"); + mErrors->Error(dec->mLocation, EINFO_ORIGINAL_DEFINITION, "Original definition"); + } + } + + return nullptr; + } + else if (ConsumeTokenIf(TK_ENUM)) + { + Declaration* dec = ParseQualIdent(false); + if (dec) + { + if (dec->mType == DT_TYPE_ENUM) + { + mScope->UseScope(dec->mScope); + } + else + { + mErrors->Error(mScanner->mLocation, EERR_INCOMPATIBLE_OPERATOR, "Not an enum"); + mErrors->Error(dec->mLocation, EINFO_ORIGINAL_DEFINITION, "Original definition"); + } + } + + return nullptr; + } + else + { + Declaration* dec; + + do { + if (mScanner->mToken == TK_IDENT && !mScope->Lookup(mScanner->mTokenIdent, SLEVEL_SCOPE)) + { + const Ident* ident = mScanner->mTokenIdent; + mScanner->NextToken(); + if (ConsumeTokenIf(TK_ASSIGN)) + { + Expression * texp = ParseSimpleExpression(false, true); + if (texp && texp->mType == EX_TYPE) + { + mScope->Insert(ident, texp->mDecType); + dec = texp->mDecType; + continue; + } + } + else + mScanner->UngetToken(TK_IDENT, ident); + } + dec = ParseQualIdent(false); + if (dec) + { + Declaration* pdec = mScope->Insert(dec->mIdent, dec); + if (pdec && pdec != dec) + mErrors->Error(dec->mLocation, EERR_DUPLICATE_DEFINITION, "Duplicate declaration", dec->mIdent); + } + else + break; + + } while (ConsumeTokenIf(TK_COMMA)); + + return dec; + } + } + else + { + for (;;) + { + if (mScanner->mToken == TK_VIRTUAL) + { + storageFlags |= DTF_VIRTUAL; + mScanner->NextToken(); + } + else if (mScanner->mToken == TK_STATIC) + { + storageFlags |= DTF_STATIC; + mScanner->NextToken(); + } + else if (mScanner->mToken == TK_CONSTEXPR) + { + storageFlags |= DTF_CONSTEXPR | DTF_REQUEST_INLINE; + mScanner->NextToken(); + } + else if (mScanner->mToken == TK_EXTERN) + { + storageFlags |= DTF_EXTERN; + mScanner->NextToken(); + } + else if (mScanner->mToken == TK_ZEROPAGE) + { + storageFlags |= DTF_ZEROPAGE; + mScanner->NextToken(); + } + else if (mScanner->mToken == TK_STRIPED) + { + storageFlags |= DTF_STRIPED; + mScanner->NextToken(); + } + else if (mScanner->mToken == TK_NOINLINE) + { + storageFlags |= DTF_PREVENT_INLINE; + mScanner->NextToken(); + } + else if (mScanner->mToken == TK_FORCEINLINE) + { + storageFlags |= DTF_FORCE_INLINE | DTF_REQUEST_INLINE; + mScanner->NextToken(); + } + else if (mScanner->mToken == TK_INLINE) + { + storageFlags |= DTF_REQUEST_INLINE; + mScanner->NextToken(); + } + else if (mScanner->mToken == TK_EXPORT) + { + storageFlags |= DTF_EXPORT; + mScanner->NextToken(); + } + else if (mScanner->mToken == TK_DYNSTACK) + { + storageFlags |= DTF_DYNSTACK; + mScanner->NextToken(); + } + else if (mScanner->mToken == TK_FASTCALL) + { + storageFlags |= DTF_FASTCALL; + mScanner->NextToken(); + } + else if (mScanner->mToken == TK_NATIVE) + { + storageFlags |= DTF_NATIVE; + mScanner->NextToken(); + } + else if (mScanner->mToken == TK_INTERRUPT) + { + storageFlags |= DTF_INTERRUPT | DTF_NATIVE; + mScanner->NextToken(); + } + else if (mScanner->mToken == TK_HWINTERRUPT) + { + storageFlags |= DTF_INTERRUPT | DTF_HWINTERRUPT | DTF_NATIVE; + mScanner->NextToken(); + } + else + break; + } + } + + if ((mCompilerOptions & COPT_CPLUSPLUS) && pthis && mScanner->mToken == TK_BINARY_NOT) + { + // Destructor declaration + mScanner->NextToken(); + if (mScanner->mToken == TK_IDENT) + { + const Ident* ident = mScanner->mTokenIdent; + if (mTemplateScope) + { + Declaration* dec = mTemplateScope->Lookup(ident); + if (dec) + ident = dec->mIdent; + } + + if (ident != pthis->mBase->mIdent) + mErrors->Error(mScanner->mLocation, EERR_INVALID_IDENTIFIER, "Wrong class name for destructor", pthis->mBase->mIdent); + mScanner->NextToken(); + } + else + mErrors->Error(mScanner->mLocation, EERR_SYNTAX, "Identifier expected"); + + Declaration* ctdec = ParseFunctionDeclaration(TheVoidTypeDeclaration); + + if (ctdec->mParams) + mErrors->Error(ctdec->mLocation, EERR_WRONG_PARAMETER, "Destructor can't have parameter"); + + PrependThisArgument(ctdec, pthis); + + Declaration* cdec = new Declaration(ctdec->mLocation, DT_CONST_FUNCTION); + cdec->mBase = ctdec; + + cdec->mFlags |= cdec->mBase->mFlags & (DTF_CONST | DTF_VOLATILE); + ctdec->mFlags |= storageFlags & (DTF_REQUEST_INLINE | DTF_CONSTEXPR | DTF_VIRTUAL); + cdec->mFlags |= storageFlags & (DTF_PREVENT_INLINE); + + cdec->mSection = mCodeSection; + cdec->mBase->mFlags |= typeFlags; + + if (mCompilerOptions & COPT_NATIVE) + cdec->mFlags |= DTF_NATIVE; + + cdec->mIdent = pthis->mBase->mIdent->PreMangle("~"); + cdec->mQualIdent = pthis->mBase->mScope->Mangle(cdec->mIdent); + + if (mScanner->mToken == TK_OPEN_BRACE) + { + cdec->mCompilerOptions = mCompilerOptions; + cdec->mBase->mCompilerOptions = mCompilerOptions; + + cdec->mVarIndex = -1; + + cdec->mValue = ParseFunction(cdec->mBase); + + cdec->mFlags |= DTF_DEFINED | DTF_REQUEST_INLINE; + cdec->mNumVars = mLocalIndex; + } + + return cdec; + } + if ((mCompilerOptions & COPT_CPLUSPLUS) && pthis && mScanner->mToken == TK_OPERATOR) + { + mScanner->NextToken(); + bdec = ParseBaseTypeDeclaration(typeFlags, false); + + Declaration* ctdec = ParseFunctionDeclaration(bdec); + + if (ctdec->mParams) + mErrors->Error(ctdec->mLocation, EERR_WRONG_PARAMETER, "Cast operators can't have parameter"); + + Declaration* bthis = new Declaration(mScanner->mLocation, DT_TYPE_POINTER); + bthis->mFlags |= DTF_CONST | DTF_DEFINED; + if (ConsumeTokenIf(TK_CONST)) + bthis->mBase = pthis->mBase->ToConstType(); + else + bthis->mBase = pthis->mBase; + bthis->mSize = 2; + + PrependThisArgument(ctdec, bthis); + + Declaration* cdec = new Declaration(ctdec->mLocation, DT_CONST_FUNCTION); + cdec->mBase = ctdec; + + cdec->mFlags |= cdec->mBase->mFlags & (DTF_CONST | DTF_VOLATILE); + ctdec->mFlags |= storageFlags & (DTF_REQUEST_INLINE | DTF_CONSTEXPR | DTF_VIRTUAL); + cdec->mFlags |= storageFlags & (DTF_PREVENT_INLINE); + + cdec->mSection = mCodeSection; + cdec->mBase->mFlags |= typeFlags; + + if (mCompilerOptions & COPT_NATIVE) + cdec->mFlags |= DTF_NATIVE; + + cdec->mIdent = Ident::Unique("(cast)"); + cdec->mQualIdent = pthis->mBase->mScope->Mangle(cdec->mIdent); + + if (mScanner->mToken == TK_OPEN_BRACE) + { + cdec->mCompilerOptions = mCompilerOptions; + cdec->mBase->mCompilerOptions = mCompilerOptions; + + cdec->mVarIndex = -1; + + cdec->mValue = ParseFunction(cdec->mBase); + + cdec->mFlags |= DTF_DEFINED | DTF_REQUEST_INLINE; + cdec->mNumVars = mLocalIndex; + } + + return cdec; + } + + bdec = ParseBaseTypeDeclaration(typeFlags, false, ptempl); + } + + Declaration* rdec = nullptr, * ldec = nullptr; + + if (mCompilerOptions & COPT_CPLUSPLUS) + { + if (bdec && pthis && bdec == pthis->mBase && mScanner->mToken == TK_OPEN_PARENTHESIS) + { + Declaration* ctdec = ParseFunctionDeclaration(TheVoidTypeDeclaration); + + Declaration* cdec = new Declaration(ctdec->mLocation, DT_CONST_FUNCTION); + + PrependThisArgument(ctdec, pthis); + + cdec->mBase = ctdec; + + cdec->mFlags |= cdec->mBase->mFlags & (DTF_CONST | DTF_VOLATILE); + cdec->mFlags |= storageFlags & (DTF_INLINE | DTF_CONSTEXPR | DTF_PREVENT_INLINE); + + cdec->mSection = mCodeSection; + cdec->mBase->mFlags |= typeFlags; + + if (mCompilerOptions & COPT_NATIVE) + cdec->mFlags |= DTF_NATIVE; + + Declaration* pdec = pthis->mBase->mScope ? pthis->mBase->mScope->Lookup(pthis->mBase->mIdent->PreMangle("+"), SLEVEL_CLASS) : nullptr; + if (pdec) + { + while (pdec && !cdec->mBase->IsSameParams(pdec->mBase)) + pdec = pdec->mNext; + } + + Declaration* dec = cdec; + + if (pdec) + { + if (!cdec->mBase->IsSame(pdec->mBase)) + mErrors->Error(cdec->mLocation, EERR_DECLARATION_DIFFERS, "Function declaration differs"); + else if (cdec->mFlags & ~pdec->mFlags & (DTF_HWINTERRUPT | DTF_INTERRUPT | DTF_FASTCALL | DTF_NATIVE)) + mErrors->Error(cdec->mLocation, EERR_DECLARATION_DIFFERS, "Function call type declaration differs"); + else + { + // + // Take parameter names from new declaration + // + Declaration* npdec = cdec->mBase->mParams, * ppdec = pdec->mBase->mParams; + while (npdec && ppdec) + { + if (npdec->mIdent) + { + ppdec->mIdent = npdec->mIdent; + ppdec->mQualIdent = npdec->mQualIdent; + } + npdec = npdec->mNext; + ppdec = ppdec->mNext; + } + } + + dec = pdec; + if (!(pdec->mFlags & DTF_DEFINED)) + cdec = pdec; + } + + cdec->mIdent = pthis->mBase->mIdent->PreMangle("+"); + cdec->mQualIdent = pthis->mBase->mScope->Mangle(cdec->mIdent); + + // Initializer list + if (mScanner->mToken == TK_COLON) + { + BuildMemberConstructor(pthis, cdec); + } + + if (mScanner->mToken == TK_OPEN_BRACE) + { + if (cdec->mFlags & DTF_DEFINED) + { + mErrors->Error(mScanner->mLocation, EERR_DUPLICATE_DEFINITION, "Function already has a body"); + mErrors->Error(cdec->mLocation, EINFO_ORIGINAL_DEFINITION, "Original definition"); + } + + cdec->mCompilerOptions = mCompilerOptions; + cdec->mBase->mCompilerOptions = mCompilerOptions; + + cdec->mVarIndex = -1; + + cdec->mValue = ParseFunction(cdec->mBase); + + cdec->mFlags |= DTF_DEFINED | DTF_REQUEST_INLINE; + cdec->mNumVars = mLocalIndex; + + if (ptempl) + PrependMemberConstructor(pthis, cdec); + } + + return dec; + } + + if (bdec && bdec->mType == DT_TYPE_STRUCT && ConsumeTokenIf(TK_COLCOLON)) + { + if (mScanner->mToken == TK_IDENT) + { + if (mScanner->mTokenIdent == bdec->mIdent) + { + mScanner->NextToken(); + + Declaration* ctdec = ParseFunctionDeclaration(TheVoidTypeDeclaration); + + Declaration* bthis = new Declaration(mScanner->mLocation, DT_TYPE_POINTER); + bthis->mFlags |= DTF_CONST | DTF_DEFINED; + if (ConsumeTokenIf(TK_CONST)) + bthis->mBase = bdec->ToConstType(); + else + bthis->mBase = bdec; + bthis->mSize = 2; + + PrependThisArgument(ctdec, bthis); + + Declaration* cdec = bdec->mScope->Lookup(bdec->mIdent->PreMangle("+"), SLEVEL_CLASS); + if (cdec) + { + while (cdec && !cdec->mBase->IsSameParams(ctdec)) + cdec = cdec->mNext; + } + + if (cdec) + { + cdec->mFlags |= storageFlags & (DTF_REQUEST_INLINE | DTF_CONSTEXPR); + + // Initializer list + if (mScanner->mToken == TK_COLON) + { + BuildMemberConstructor(bthis, cdec); + } + + if (mScanner->mToken == TK_OPEN_BRACE) + { + // + // Take parameter names from new declaration + // + Declaration* npdec = ctdec->mParams, * ppdec = cdec->mBase->mParams; + while (npdec && ppdec) + { + if (npdec->mIdent) + { + ppdec->mIdent = npdec->mIdent; + ppdec->mQualIdent = npdec->mQualIdent; + } + npdec = npdec->mNext; + ppdec = ppdec->mNext; + } + + if (cdec->mFlags & DTF_DEFINED) + { + mErrors->Error(mScanner->mLocation, EERR_DUPLICATE_DEFINITION, "Function already has a body"); + mErrors->Error(cdec->mLocation, EINFO_ORIGINAL_DEFINITION, "Original definition"); + } + + cdec->mCompilerOptions = mCompilerOptions; + cdec->mBase->mCompilerOptions = mCompilerOptions; + + cdec->mVarIndex = -1; + + cdec->mValue = ParseFunction(cdec->mBase); + + cdec->mFlags |= DTF_DEFINED; + cdec->mNumVars = mLocalIndex; + + PrependMemberConstructor(bthis, cdec); + } + + return cdec; + } + else + { + mErrors->Error(bdec->mLocation, ERRO_NO_MATCHING_FUNCTION_CALL, "No matching constructor"); + return bdec; + } + } + else + { + Declaration* mdec = bdec->mScope->Lookup(mScanner->mTokenIdent, SLEVEL_CLASS); + if (mdec) + bdec = mdec; + else + mErrors->Error(mScanner->mLocation, EERR_OBJECT_NOT_FOUND, "Member not found", mScanner->mTokenIdent); + mScanner->NextToken(); + } + } + + if (ConsumeTokenIf(TK_BINARY_NOT)) + { + if (mScanner->mToken == TK_IDENT) + { + if (mScanner->mTokenIdent != bdec->mIdent) + mErrors->Error(mScanner->mLocation, EERR_INVALID_IDENTIFIER, "Wrong class name for destructor", bdec->mIdent); + mScanner->NextToken(); + } + else + mErrors->Error(mScanner->mLocation, EERR_SYNTAX, "Identifier expected"); + + Declaration* ctdec = ParseFunctionDeclaration(TheVoidTypeDeclaration); + + if (ctdec->mParams) + mErrors->Error(ctdec->mLocation, EERR_WRONG_PARAMETER, "Destructor can't have parameter"); + + if (bdec->mDestructor) + { + bdec->mDestructor->mFlags |= storageFlags & (DTF_REQUEST_INLINE | DTF_CONSTEXPR); + + Declaration* bthis = new Declaration(mScanner->mLocation, DT_TYPE_POINTER); + bthis->mFlags |= DTF_CONST | DTF_DEFINED; + bthis->mBase = bdec; + bthis->mSize = 2; + + bdec->mDestructor->mCompilerOptions = mCompilerOptions; + bdec->mDestructor->mBase->mCompilerOptions = mCompilerOptions; + + bdec->mDestructor->mVarIndex = -1; + + bdec->mDestructor->mValue = ParseFunction(bdec->mDestructor->mBase); + + bdec->mDestructor->mFlags |= DTF_DEFINED; + bdec->mDestructor->mNumVars = mLocalIndex; + + AppendMemberDestructor(bthis); + } + else + mErrors->Error(ctdec->mLocation, EERR_DUPLICATE_DEFINITION, "Destructor not declared", bdec->mIdent); + + return bdec->mDestructor; + } + else if (ConsumeTokenIf(TK_OPERATOR)) + { + Declaration* tdec = ParseBaseTypeDeclaration(0, true); + + Declaration* ctdec = ParseFunctionDeclaration(tdec); + + if (ctdec->mParams) + mErrors->Error(ctdec->mLocation, EERR_WRONG_PARAMETER, "Cast operators can't have parameter"); + + Declaration* bthis = new Declaration(mScanner->mLocation, DT_TYPE_POINTER); + bthis->mFlags |= DTF_CONST | DTF_DEFINED; + if (ConsumeTokenIf(TK_CONST)) + bthis->mBase = bdec->ToConstType(); + else + bthis->mBase = bdec; + bthis->mSize = 2; + + PrependThisArgument(ctdec, bthis); + + Declaration* fdec = bdec->mScope->Lookup(Ident::Unique("(cast)")); + while (fdec && !fdec->mBase->IsSame(ctdec)) + fdec = fdec->mNext; + + if (fdec) + { + fdec->mCompilerOptions = mCompilerOptions; + fdec->mBase->mCompilerOptions = mCompilerOptions; + + fdec->mVarIndex = -1; + + fdec->mValue = ParseFunction(fdec->mBase); + + fdec->mFlags |= DTF_DEFINED; + fdec->mNumVars = mLocalIndex; + } + else + mErrors->Error(ctdec->mLocation, EERR_DUPLICATE_DEFINITION, "Cast operator not declared", tdec->mIdent); + + return fdec; + } + } + } + + for (;;) + { + Declaration* ndec = ParsePostfixDeclaration(bdec->IsAuto()); + + ndec = ReverseDeclaration(ndec, bdec); + + if (storageFlags & DTF_STRIPED) + ndec = ndec->ToStriped(mErrors); + + Declaration* npdec = ndec; + + if (npdec->mBase->mType == DT_TYPE_POINTER) + npdec = npdec->mBase; + + // Make room for return value pointer on struct return + if (npdec->mBase->mType == DT_TYPE_FUNCTION && npdec->mBase->mBase->IsComplexStruct()) + { + Declaration* pdec = npdec->mBase->mParams; + while (pdec) + { + pdec->mVarIndex += 2; + pdec = pdec->mNext; + } + } + + if (npdec->mBase->mType == DT_TYPE_FUNCTION) + npdec->mBase->mFlags |= storageFlags & (DTF_INTERRUPT | DTF_NATIVE | DTF_FASTCALL | DTF_VIRTUAL); + + if (definingType) + { + if (ndec->mIdent) + { + Declaration* pdec = mScope->Insert(ndec->mIdent, ndec->mBase); + if (pdec && pdec != ndec->mBase) + mErrors->Error(ndec->mLocation, EERR_DUPLICATE_DEFINITION, "Duplicate type declaration", ndec->mIdent); + } + } + else + { + if (ptempl) + ptempl->mBase = ndec; + + if (ptempl && mTemplateScope && ndec->mIdent) + { + if (!strstr(ndec->mQualIdent->mString, mTemplateScope->mName->mString)) + { + ndec->mIdent = ndec->mIdent->Mangle(mTemplateScope->mName->mString); + ndec->mQualIdent = ndec->mQualIdent->Mangle(mTemplateScope->mName->mString); + } + } + + if (ndec->mBase->mType == DT_TYPE_FUNCTION) + { + ndec->mBase->mClass = pthis; + + if (pthis && !(storageFlags & DTF_STATIC)) + { + if (ConsumeTokenIf(TK_CONST)) + PrependThisArgument(ndec->mBase, pthis->mBase->ToConstType()->BuildConstPointer(ndec->mLocation)); + else + PrependThisArgument(ndec->mBase, pthis); + ndec->mBase->mFlags |= DTF_FUNC_THIS; + } + } + + + if (variable) + { + if (storageFlags & DTF_CONSTEXPR) + ndec->mBase = ndec->mBase->ToConstType(); + + ndec->mFlags |= storageFlags; + ndec->mFlags |= ndec->mBase->mFlags & (DTF_CONST | DTF_VOLATILE); + + if (ndec->mBase->mType == DT_TYPE_FUNCTION) + { + ndec->mType = DT_CONST_FUNCTION; + ndec->mSection = mCodeSection; + ndec->mBase->mFlags |= typeFlags; + + if (mCompilerOptions & COPT_NATIVE) + ndec->mFlags |= DTF_NATIVE; + + Declaration* ntdec = FunctionAutoParamsToTemplate(ndec); + if (ntdec) + return ntdec; + } + + if (ndec->mIdent) + { + Declaration* pdec; + + if (storageFlags & DTF_ZEROPAGE) + { + if (ndec->mType == DT_VARIABLE) + ndec->mSection = mCompilationUnits->mSectionZeroPage; + else + mErrors->Error(ndec->mLocation, ERRR_INVALID_STORAGE_TYPE, "Invalid storage type", ndec->mIdent); + } + + if (mScope->mLevel < SLEVEL_FUNCTION && !(storageFlags & DTF_STATIC)) + { + pdec = mCompilationUnits->mScope->Insert(ndec->mQualIdent, ndec); + + if (mScope->Mangle(ndec->mIdent) == ndec->mQualIdent) + { + Declaration* ldec = mScope->Insert(ndec->mIdent, pdec ? pdec : ndec); +#if 0 + if (ldec && ldec->mTemplate && mTemplateScope) + { + ndec->mQualIdent = ndec->mQualIdent->Mangle(mTemplateScope->mName->mString); + } + else +#endif + + if (ldec && ldec != pdec) + { + mErrors->Error(ndec->mLocation, EERR_DUPLICATE_DEFINITION, "Duplicate definition"); + mErrors->Error(ldec->mLocation, EINFO_ORIGINAL_DEFINITION, "Original definition"); + } + } + else if (ptempl && mTemplateScope && pthis) + { + } + else if (!pdec) + mErrors->Error(ndec->mLocation, EERR_OBJECT_NOT_FOUND, "Object not declarared in scope", ndec->mQualIdent); + + } + else if ((storageFlags & DTF_EXTERN) && mScope->mLevel >= SLEVEL_FUNCTION) + { + pdec = mScope->Lookup(ndec->mIdent); + if (pdec) + { + if (!ndec->mBase->IsSame(pdec->mBase)) + { + if (ndec->mBase->mType == DT_TYPE_ARRAY && pdec->mBase->mType == DT_TYPE_ARRAY && ndec->mBase->mBase->IsSame(pdec->mBase->mBase) && ndec->mBase->mSize == 0) + ; + else if (ndec->mBase->mType == DT_TYPE_POINTER && pdec->mBase->mType == DT_TYPE_ARRAY && ndec->mBase->mBase->IsSame(pdec->mBase->mBase)) + ; + else + { + mErrors->Error(ndec->mLocation, EERR_DECLARATION_DIFFERS, "Variable declaration differs", ndec->mIdent); + mErrors->Error(pdec->mLocation, EINFO_ORIGINAL_DEFINITION, "Original definition"); + } + } + ndec = pdec; + } + else + { + mGlobals->mParent->Insert(ndec->mIdent, ndec); + } + + pdec = mScope->Insert(ndec->mIdent, ndec); + } + else + { + pdec = mScope->Insert(ndec->mIdent, ndec); + } + + if (pdec) + { + if (pdec->mType == DT_CONST_FUNCTION && ndec->mBase->mType == DT_TYPE_FUNCTION) + { + if (pdec->mBase->mFlags & DTF_FUNC_THIS) + { + if (!(ndec->mBase->mFlags & DTF_FUNC_THIS)) + { + Declaration* adec = pdec->mBase->mParams->Clone(); + adec->mNext = ndec->mBase->mParams; + + if (ConsumeTokenIf(TK_CONST)) + { + if (!(adec->mBase->mBase->mFlags & DTF_CONST)) + adec->mBase = adec->mBase->mBase->ToConstType()->BuildConstPointer(adec->mLocation); + } + else + { + if (adec->mBase->mBase->mFlags & DTF_CONST) + adec->mBase = adec->mBase->mBase->ToMutableType()->BuildConstPointer(adec->mLocation); + } + + Declaration* p = adec->mBase->mParams; + while (p) + { + p->mVarIndex += 2; + p = p->mNext; + } + + ndec->mBase->mParams = adec; + + ndec->mBase->mFlags |= DTF_FUNC_THIS; + } + } + else if (ndec->mBase->mFlags & DTF_FUNC_THIS) + { + if (pdec->mBase->mParams) + ndec->mBase->mParams = pdec->mBase->mParams->Clone(); + else + ndec->mBase->mParams = nullptr; + ndec->mBase->mFlags &= ~DTF_FUNC_THIS; + } + + if (mCompilerOptions & COPT_CPLUSPLUS) + { + Declaration* pcdec = nullptr; + + while (pdec && !ndec->mBase->IsSameParams(pdec->mBase)) + { + pcdec = pdec; + pdec = pdec->mNext; + } + + if (!pdec) + { + if (!pthis && (ndec->mBase->mFlags & DTF_FUNC_THIS)) + mErrors->Error(ndec->mLocation, EERR_DECLARATION_DIFFERS, "Function declaration differs", ndec->mIdent); + + pcdec->mNext = ndec; + } + } + + if (pdec) + { + if (!ndec->mBase->IsSame(pdec->mBase) && !(ndec->mBase->mBase->mType == DT_TYPE_AUTO && ndec->mBase->IsSameParams(pdec->mBase))) + { + ndec->mBase->IsSameParams(pdec->mBase); + ndec->mBase->IsSame(pdec->mBase); + mErrors->Error(ndec->mLocation, EERR_DECLARATION_DIFFERS, "Function declaration differs", ndec->mIdent); + } + else if (ndec->mFlags & ~pdec->mFlags & (DTF_HWINTERRUPT | DTF_INTERRUPT | DTF_FASTCALL | DTF_NATIVE)) + mErrors->Error(ndec->mLocation, EERR_DECLARATION_DIFFERS, "Function call type declaration differs", ndec->mIdent); + else + { + // + // Take parameter names from new declaration + // + Declaration* npdec = ndec->mBase->mParams, * ppdec = pdec->mBase->mParams; + while (npdec && ppdec) + { + if (npdec->mIdent) + { + ppdec->mIdent = npdec->mIdent; + ppdec->mQualIdent = npdec->mQualIdent; + } + ppdec->mBase = npdec->mBase; + + npdec = npdec->mNext; + ppdec = ppdec->mNext; + } + } + + pdec->mFlags |= ndec->mFlags & (DTF_REQUEST_INLINE | DTF_PREVENT_INLINE | DTF_FORCE_INLINE); + + ndec = pdec; + } + } + else if (ndec->mFlags & DTF_EXTERN) + { + if (!ndec->mBase->IsSame(pdec->mBase)) + { + if (ndec->mBase->mType == DT_TYPE_ARRAY && pdec->mBase->mType == DT_TYPE_ARRAY && ndec->mBase->mBase->IsSame(pdec->mBase->mBase) && ndec->mBase->mSize == 0) + ; + else if (ndec->mBase->mType == DT_TYPE_POINTER && pdec->mBase->mType == DT_TYPE_ARRAY && ndec->mBase->mBase->IsSame(pdec->mBase->mBase)) + ; + else + { + mErrors->Error(ndec->mLocation, EERR_DECLARATION_DIFFERS, "Variable declaration differs", ndec->mIdent); + mErrors->Error(pdec->mLocation, EINFO_ORIGINAL_DEFINITION, "Original definition"); + } + } + + pdec->mFlags |= ndec->mFlags & DTF_ZEROPAGE; + + ndec = pdec; + } + else if (pdec->mFlags & DTF_EXTERN) + { + if (!ndec->mBase->IsSame(pdec->mBase)) + { + if (ndec->mBase->mType == DT_TYPE_ARRAY && pdec->mBase->mType == DT_TYPE_ARRAY && ndec->mBase->mBase->IsSame(pdec->mBase->mBase)) + { + pdec->mBase = ndec->mBase; + } + else if (pdec->mBase->mType == DT_TYPE_POINTER && ndec->mBase->mType == DT_TYPE_ARRAY && ndec->mBase->mBase->IsSame(pdec->mBase->mBase)) + { + pdec->mBase = ndec->mBase; + } + else + mErrors->Error(ndec->mLocation, EERR_DECLARATION_DIFFERS, "Variable declaration differs", ndec->mIdent); + } + else + pdec->mBase = ndec->mBase; + + if (!(ndec->mFlags & DTF_EXTERN)) + { + pdec->mFlags &= ~DTF_EXTERN; + pdec->mSection = ndec->mSection; + pdec->mSize = ndec->mSize; + } + + pdec->mFlags |= ndec->mFlags & DTF_ZEROPAGE; + + ndec = pdec; + } + else if ((mCompilerOptions & COPT_CPLUSPLUS) || mScope->mLevel >= SLEVEL_FUNCTION) + { + mErrors->Error(ndec->mLocation, EERR_DUPLICATE_DEFINITION, "Duplicate variable declaration", ndec->mIdent); + mErrors->Error(pdec->mLocation, EINFO_ORIGINAL_DEFINITION, "Original definition"); + } + else + { + if (!ndec->mBase->IsSame(pdec->mBase)) + { + if (ndec->mBase->mType == DT_TYPE_ARRAY && pdec->mBase->mType == DT_TYPE_ARRAY && ndec->mBase->mBase->IsSame(pdec->mBase->mBase) && pdec->mBase->mSize == 0) + pdec->mBase->mSize = ndec->mBase->mSize; + else if (pdec->mBase->mType == DT_TYPE_POINTER && ndec->mBase->mType == DT_TYPE_ARRAY && ndec->mBase->mBase->IsSame(pdec->mBase->mBase)) + { + pdec->mBase = ndec->mBase; + } + else + { + mErrors->Error(ndec->mLocation, EERR_DECLARATION_DIFFERS, "Variable declaration differs", ndec->mIdent); + mErrors->Error(pdec->mLocation, EINFO_ORIGINAL_DEFINITION, "Original definition"); + } + + } + + pdec->mSection = ndec->mSection; + + pdec->mFlags |= ndec->mFlags & DTF_ZEROPAGE; + + if (pdec->mValue) + { + mErrors->Error(ndec->mLocation, EERR_DUPLICATE_DEFINITION, "Duplicate variable declaration", ndec->mIdent); + mErrors->Error(pdec->mLocation, EINFO_ORIGINAL_DEFINITION, "Original definition"); + } + + ndec = pdec; + + } + } + } + + if (mScope->mLevel < SLEVEL_FUNCTION || (ndec->mFlags & DTF_STATIC)) + { + if (ndec->mSize >= 256) + ndec->mAlignment = ((ndec->mAlignment - 1) | (ndec->mBase->Alignment() - 1)) + 1; + + ndec->mFlags |= DTF_GLOBAL; + ndec->mVarIndex = -1; + } + else + ndec->mVarIndex = mLocalIndex++; + + } + else if (pthis) + { + ndec->mFlags |= storageFlags & (DTF_STATIC | DTF_PREVENT_INLINE | DTF_FORCE_INLINE); + if (storageFlags & DTF_CONSTEXPR) + { + ndec->mFlags |= DTF_CONSTEXPR; + ndec->mBase = ndec->mBase->ToConstType(); + } + } + + ndec->mOffset = 0; + + if (rdec) + ldec->mNext = ndec; + else + rdec = ndec; + ldec = ndec; +// ndec->mNext = nullptr; + + if (member && ConsumeTokenIf(TK_COLON)) + { + Expression* exp = ParseRExpression(); + if (!ndec->mBase->IsIntegerType()) + mErrors->Error(exp->mLocation, EERR_INVALID_BITFIELD, "Invalid bitfield for non integer type"); + else if (exp->mType == EX_CONSTANT && exp->mDecType->IsIntegerType() && exp->mDecValue->mType == DT_CONST_INTEGER) + ndec->mBits = uint8(exp->mDecValue->mInteger); + else + mErrors->Error(exp->mLocation, EERR_CONSTANT_TYPE, "Constant integer expression expected"); + } + + else if (mScanner->mToken == TK_OPEN_PARENTHESIS && (mCompilerOptions & COPT_CPLUSPLUS)) + { + ParseVariableInit(ndec); + } + else if (mScanner->mToken == TK_OPEN_BRACE && (mCompilerOptions & COPT_CPLUSPLUS) && ndec->mType == DT_VARIABLE && !pthis && !(storageFlags & DTF_EXTERN)) + { + ParseVariableInit(ndec); + } + else + { + if (ConsumeTokenIf(TK_ASSIGN)) + { + if (pthis && ndec->mBase->mType == DT_TYPE_FUNCTION && mScanner->mToken == TK_INTEGER && mScanner->mTokenInteger == 0) + { + mScanner->NextToken(); + ndec->mValue = new Expression(mScanner->mLocation, EX_ASSUME); + Declaration * fdec = new Declaration(mScanner->mLocation, DT_CONST_INTEGER); + fdec->mBase = TheBoolTypeDeclaration; + fdec->mInteger = 0; + ndec->mValue->mLeft = new Expression(mScanner->mLocation, EX_CONSTANT); + ndec->mValue->mLeft->mDecValue = fdec; + ndec->mValue->mLeft->mDecType = fdec->mBase; + ndec->mFlags |= DTF_DEFINED | DTF_PURE_VIRTUAL; + } + else + { + if (member || (ndec->mFlags & (DTF_STATIC | DTF_GLOBAL))) + ndec->mValue = ParseConstInitExpression(ndec->mBase); + else + { + Expression* vexp = new Expression(ndec->mLocation, EX_VARIABLE); + vexp->mDecType = ndec->mBase; + vexp->mDecValue = ndec; + + ndec->mValue = ParseVarInitExpression(vexp); + + if (ndec->mBase->mType == DT_TYPE_AUTO) + { + ndec->mBase = ndec->mBase->DeduceAutoInit(ndec->mValue->mDecType); + if (ndec->mValue->mDecType->IsSame(ndec->mBase)) + { + vexp->mDecType = ndec->mBase; + Expression* exp = CopyElision(vexp, ndec->mValue); + if (exp) + ndec->mValue = exp; + } + } + } + + ndec->mBase = ndec->mBase->DeduceAutoInit(ndec->mValue->mDecType); + + if (ndec->mFlags & (DTF_GLOBAL | DTF_STATIC)) + { + if (ndec->mFlags & DTF_ZEROPAGE) + ; + else + ndec->mSection = mDataSection; + } + ndec->mSize = ndec->mBase->mSize; + } + } + + if ((mCompilerOptions & COPT_CPLUSPLUS) && ndec->mType == DT_VARIABLE && !pthis && !(storageFlags & DTF_EXTERN)) + { + if (ndec->mValue && ndec->mValue->mType == EX_CONSTRUCT && ndec->mValue->mRight && ndec->mValue->mRight->mDecValue == ndec) + { + } + else + { + // Find default constructor + + Declaration* bdec = ndec->mBase; + while (bdec && bdec->mType == DT_TYPE_ARRAY) + bdec = bdec->mBase; + + if (bdec->mFlags & DTF_PURE_VIRTUAL) + mErrors->Error(ndec->mLocation, ERRR_INSTANTIATE_ABSTRACT_CLASS, "Cannot instantiate abstract class", ndec->mIdent); + + if (bdec && bdec->mDefaultConstructor) + { + bdec = bdec->ToMutableType(); + + Expression* vexp = new Expression(ndec->mLocation, EX_VARIABLE); + vexp->mDecType = ndec->mBase; + vexp->mDecValue = ndec; + + Expression* texp = new Expression(mScanner->mLocation, EX_PREFIX); + texp->mToken = TK_BINARY_AND; + texp->mLeft = vexp; + texp->mDecType = bdec->BuildPointer(mScanner->mLocation); + + if (ndec->mBase->mType == DT_TYPE_STRUCT) + { + if (ndec->mValue) + { + ParseVariableInit(ndec, ndec->mValue); + } + else + { + Expression* cexp = new Expression(mScanner->mLocation, EX_CONSTANT); + cexp->mDecValue = bdec->mDefaultConstructor; + cexp->mDecType = cexp->mDecValue->mBase; + + Expression* fexp = new Expression(mScanner->mLocation, EX_CALL); + fexp->mLeft = cexp; + fexp->mRight = texp; + + if (bdec->mDefaultConstructor->mFlags & DTF_CONSTEXPR) + ndec->mSection = mDataSection; + + Expression* dexp = nullptr; + if (ndec->mBase->mDestructor) + { + Expression* cexp = new Expression(mScanner->mLocation, EX_CONSTANT); + cexp->mDecValue = ndec->mBase->mDestructor; + cexp->mDecType = cexp->mDecValue->mBase; + + dexp = new Expression(mScanner->mLocation, EX_CALL); + dexp->mLeft = cexp; + dexp->mRight = texp; + } + + Expression* nexp = new Expression(mScanner->mLocation, EX_CONSTRUCT); + + nexp->mLeft = new Expression(mScanner->mLocation, EX_LIST); + nexp->mLeft->mLeft = fexp; + nexp->mLeft->mRight = dexp; + + nexp->mRight = vexp; + nexp->mDecType = vexp->mDecType; + + ndec->mValue = nexp; + } + } + else if (ndec->mValue) + { + Expression* lexp = nullptr; + + Declaration* bpdec = bdec->BuildPointer(mScanner->mLocation); + + if (ndec->mValue->mType == EX_CONSTANT) + { + int offset = 0; + lexp = BuildVariableArrayInit(texp, bpdec, ndec->mValue->mDecValue, 0, offset); + + if (offset != ndec->mSize) + mErrors->Error(pdec->mLocation, EERR_INVALID_CLASS_INITIALIZER, "Incomplete class initializer list"); + + while (texp->mDecType->mBase->mType == DT_TYPE_ARRAY) + { + Expression* iexp = new Expression(vexp->mLocation, EX_PREFIX); + iexp->mToken = TK_MUL; + iexp->mLeft = texp; + iexp->mDecType = texp->mDecType->mBase; + texp = iexp; + } + + texp->mDecType = bpdec; + + //texp->mDecType = bdec->BuildPointer(vexp->mLocation); + + Declaration* ncdec = new Declaration(mScanner->mLocation, DT_CONST_INTEGER); + ncdec->mBase = TheUnsignedIntTypeDeclaration; + ncdec->mInteger = ndec->mSize / bdec->mSize; + + Expression* ncexp = new Expression(mScanner->mLocation, EX_CONSTANT); + ncexp->mDecType = ncdec->mBase; + ncexp->mDecValue = ncdec; + + Expression* dexp = nullptr; + if (bdec->mDestructor) + { + Expression* cexp = new Expression(mScanner->mLocation, EX_CONSTANT); + cexp->mDecValue = bdec->mVectorDestructor; + cexp->mDecType = cexp->mDecValue->mBase; + + dexp = new Expression(mScanner->mLocation, EX_CALL); + dexp->mLeft = cexp; + dexp->mRight = new Expression(mScanner->mLocation, EX_LIST); + dexp->mRight->mLeft = texp; + dexp->mRight->mRight = new Expression(mScanner->mLocation, EX_BINARY); + dexp->mRight->mRight->mToken = TK_ADD; + dexp->mRight->mRight->mLeft = texp; + dexp->mRight->mRight->mDecType = texp->mDecType; + dexp->mRight->mRight->mRight = new Expression(mScanner->mLocation, EX_CONSTANT); + dexp->mRight->mRight->mRight->mDecType = ncdec->mBase; + dexp->mRight->mRight->mRight->mDecValue = ncdec; + } + + Expression* nexp = new Expression(mScanner->mLocation, EX_CONSTRUCT); + + nexp->mLeft = new Expression(mScanner->mLocation, EX_LIST); + nexp->mLeft->mLeft = lexp; + nexp->mLeft->mRight = dexp; + + nexp->mRight = vexp; + nexp->mDecType = vexp->mDecType; + + ndec->mValue = nexp; + ndec->mFlags |= DTF_VAR_ALIASING; + } + else if (member || (ndec->mFlags & (DTF_STATIC | DTF_GLOBAL))) + mErrors->Error(ndec->mValue->mLocation, EERR_INVALID_CLASS_INITIALIZER, "Invalid class initializer list"); + } + else + { + Declaration* bpdec = bdec->BuildPointer(mScanner->mLocation); + + while (texp->mDecType->mBase->mType == DT_TYPE_ARRAY) + { + Expression* iexp = new Expression(vexp->mLocation, EX_PREFIX); + iexp->mToken = TK_MUL; + iexp->mLeft = texp; + iexp->mDecType = texp->mDecType->mBase; + texp = iexp; + } + + texp->mDecType = bpdec; + + Declaration* ncdec = new Declaration(mScanner->mLocation, DT_CONST_INTEGER); + ncdec->mBase = TheUnsignedIntTypeDeclaration; + ncdec->mInteger = ndec->mSize / bdec->mSize; + + Expression* cexp = new Expression(mScanner->mLocation, EX_CONSTANT); + cexp->mDecValue = bdec->mVectorConstructor; + cexp->mDecType = cexp->mDecValue->mBase; + + Expression* fexp = new Expression(mScanner->mLocation, EX_CALL); + fexp->mLeft = cexp; + + fexp->mRight = new Expression(mScanner->mLocation, EX_LIST); + fexp->mRight->mLeft = texp; + fexp->mRight->mRight = new Expression(mScanner->mLocation, EX_BINARY); + fexp->mRight->mRight->mToken = TK_ADD; + fexp->mRight->mRight->mLeft = texp; + fexp->mRight->mRight->mDecType = texp->mDecType; + fexp->mRight->mRight->mRight = new Expression(mScanner->mLocation, EX_CONSTANT); + fexp->mRight->mRight->mRight->mDecType = ncdec->mBase; + fexp->mRight->mRight->mRight->mDecValue = ncdec; + + Expression* dexp = nullptr; + if (bdec->mDestructor) + { + Expression* cexp = new Expression(mScanner->mLocation, EX_CONSTANT); + cexp->mDecValue = bdec->mVectorDestructor; + cexp->mDecType = cexp->mDecValue->mBase; + + dexp = new Expression(mScanner->mLocation, EX_CALL); + dexp->mLeft = cexp; + dexp->mRight = fexp->mRight; + } + + Expression* nexp = new Expression(mScanner->mLocation, EX_CONSTRUCT); + + nexp->mLeft = new Expression(mScanner->mLocation, EX_LIST); + nexp->mLeft->mLeft = fexp; + nexp->mLeft->mRight = dexp; + + nexp->mRight = vexp; + nexp->mDecType = vexp->mDecType; + + ndec->mValue = nexp; + } + } + else if (bdec && bdec->mDestructor) + mErrors->Error(ndec->mLocation, EERR_NO_DEFAULT_CONSTRUCTOR, "No default constructor for class", ndec->mBase->mIdent); + } + } + } + + ExpandStructuredBinding(ndec); + + if (storageFlags & DTF_EXPORT) + { + mCompilationUnits->AddReferenced(ndec); + } + } + + if (mScanner->mToken == TK_COMMA) + mScanner->NextToken(); + else if (mScanner->mToken == TK_OPEN_BRACE) + { + if (ndec->mBase->mType == DT_TYPE_FUNCTION) + { + if (!(ndec->mFlags & DTF_PLACED)) + ndec->mSection = mCodeSection; + + if ((ndec->mFlags & DTF_DEFINED) && !(ndec->mFlags & DTF_REQUEST_INLINE)) + { + mErrors->Error(mScanner->mLocation, EERR_DUPLICATE_DEFINITION, "Duplicate function definition", ndec->mQualIdent); + mErrors->Error(ndec->mLocation, EINFO_ORIGINAL_DEFINITION, "Original definition"); + } + + ndec->mCompilerOptions = mCompilerOptions; + ndec->mBase->mCompilerOptions = mCompilerOptions; + + ndec->mVarIndex = -1; + + mFunction = ndec; + ndec->mValue = ParseFunction(ndec->mBase); + mFunction = nullptr; + + if (pthis && !(storageFlags & DTF_PREVENT_INLINE)) + ndec->mFlags |= DTF_REQUEST_INLINE; + + ndec->mFlags |= DTF_DEFINED; + ndec->mNumVars = mLocalIndex; + + } + return rdec; + } + else + { + if (!expression && mScanner->mToken != TK_SEMICOLON) + mErrors->Error(mScanner->mLocation, ERRR_SEMICOLON_EXPECTED, "Semicolon expected"); + + return rdec; + } + } + + return rdec; +} + +Expression* Parser::ParseDeclarationExpression(Declaration * pdec) +{ + Declaration* dec; + Expression* exp = nullptr, * rexp = nullptr; + + dec = ParseDeclaration(pdec, true, true, false); + if (dec->mType == DT_ANON && dec->mNext == 0) + { + exp = new Expression(dec->mLocation, EX_TYPE); + exp->mDecValue = dec; + exp->mDecType = dec->mBase; + } + else + { + while (dec) + { + Expression* nexp; + + if (dec->mValue && !(dec->mFlags & DTF_GLOBAL)) + { + if (dec->mValue->mType == EX_INITIALIZATION || dec->mValue->mType == EX_LIST) + { + nexp = dec->mValue; + } + else if ((mCompilerOptions & COPT_CPLUSPLUS) && dec->mValue->mType == EX_CONSTRUCT) + { + nexp = dec->mValue; + + Expression* vdec = nexp->mRight; + if (vdec->mDecValue != dec) + { + vdec->mDecValue = dec; + } + } + else if ((mCompilerOptions & COPT_CPLUSPLUS) && dec->mBase->mDestructor) + { + Expression* vexp = new Expression(dec->mLocation, EX_VARIABLE); + vexp->mDecType = dec->mBase; + vexp->mDecValue = dec; + + Expression* texp = new Expression(mScanner->mLocation, EX_PREFIX); + texp->mToken = TK_BINARY_AND; + texp->mLeft = vexp; + texp->mDecType = new Declaration(mScanner->mLocation, DT_TYPE_POINTER); + texp->mDecType->mFlags |= DTF_CONST | DTF_DEFINED; + texp->mDecType->mBase = dec->mBase; + texp->mDecType->mSize = 2; + + Expression* cexp = new Expression(mScanner->mLocation, EX_CONSTANT); + cexp->mDecValue = dec->mBase->mDestructor; + cexp->mDecType = cexp->mDecValue->mBase; + + Expression* dexp = new Expression(mScanner->mLocation, EX_CALL); + dexp->mLeft = cexp; + dexp->mRight = texp; + + Expression* aexp; + + Declaration* fcons = dec->mBase->mScope->Lookup(dec->mBase->mIdent->PreMangle("+")); + + if (fcons && !(dec->mValue->mType == EX_CALL && dec->mValue->mDecType->mType == DT_TYPE_STRUCT)) + { + + Expression* cexp = new Expression(dec->mLocation, EX_CONSTANT); + cexp->mDecValue = fcons; + cexp->mDecType = cexp->mDecValue->mBase; + + aexp = new Expression(mScanner->mLocation, EX_CALL); + aexp->mLeft = cexp; + aexp->mRight = new Expression(dec->mLocation, EX_LIST); + aexp->mRight->mLeft = texp; + aexp->mRight->mRight = dec->mValue; + + ResolveOverloadCall(aexp); + } + else + { + aexp = new Expression(dec->mValue->mLocation, EX_INITIALIZATION); + aexp->mToken = TK_ASSIGN; + aexp->mLeft = new Expression(dec->mLocation, EX_VARIABLE); + aexp->mLeft->mDecValue = dec; + aexp->mLeft->mDecType = dec->mBase; + aexp->mDecType = aexp->mLeft->mDecType; + aexp->mRight = dec->mValue; + } + + nexp = new Expression(mScanner->mLocation, EX_CONSTRUCT); + + nexp->mLeft = new Expression(mScanner->mLocation, EX_LIST); + nexp->mLeft->mLeft = aexp; + nexp->mLeft->mRight = dexp; + nexp->mRight = vexp; + nexp->mDecType = vexp->mDecType; + } + else + { + nexp = new Expression(dec->mValue->mLocation, EX_INITIALIZATION); + nexp->mToken = TK_ASSIGN; + nexp->mLeft = new Expression(dec->mLocation, EX_VARIABLE); + nexp->mLeft->mDecValue = dec; + nexp->mLeft->mDecType = dec->mBase; + nexp->mDecType = nexp->mLeft->mDecType; + + nexp->mRight = dec->mValue; + } + } + else + { + nexp = new Expression(dec->mLocation, EX_VARIABLE); + nexp->mDecValue = dec; + nexp->mDecType = dec->mBase; + } + + if (!exp) + exp = nexp; + else + { + if (!rexp) + { + rexp = new Expression(nexp->mLocation, EX_SEQUENCE); + rexp->mLeft = exp; + exp = rexp; + } + rexp->mRight = new Expression(nexp->mLocation, EX_SEQUENCE); + rexp = rexp->mRight; + rexp->mLeft = nexp; + exp->mDecType = nexp->mDecType; + } + + dec = dec->mNext; + } + } + + return exp; +} + +Declaration* Parser::ParseQualIdent(bool lhs) +{ + Declaration* dec = nullptr; + + if (mScanner->mToken == TK_COLCOLON) + { + mScanner->NextToken(); + if (mScanner->mToken == TK_IDENT) + { + DeclarationScope* scope = mScope; + while (scope->mLevel > SLEVEL_STATIC) + scope = scope->mParent; + dec = scope->Lookup(mScanner->mTokenIdent); + } + else + { + mErrors->Error(mScanner->mLocation, EERR_SYNTAX, "Identifier expected"); + return nullptr; + } + } + else + { + if (mTemplateScope) + { + dec = mTemplateScope->Lookup(mScanner->mTokenIdent); + } + + if (!dec) + dec = mScope->Lookup(mScanner->mTokenIdent); + } + + if (dec) + { + mScanner->NextToken(); + while (ConsumeTokenIf(TK_COLCOLON)) + { + if (mScanner->mToken == TK_IDENT) + { + if (dec->mType == DT_NAMESPACE || dec->mType == DT_TYPE_STRUCT || dec->mType == DT_TYPE_ENUM) + { + Declaration* ndec = dec->mScope->Lookup(mScanner->mTokenIdent, SLEVEL_USING); + + if (!ndec && dec->mType == DT_TYPE_STRUCT && dec->mIdent == mScanner->mTokenIdent) + ndec = dec->mScope->Lookup(mScanner->mTokenIdent->PreMangle("+")); + + if (ndec) + dec = ndec; + else + mErrors->Error(mScanner->mLocation, EERR_OBJECT_NOT_FOUND, "Unknown identifier", mScanner->mTokenIdent); + } + else + mErrors->Error(mScanner->mLocation, EERR_INCOMPATIBLE_OPERATOR, "Not a class or namespace"); + + } + else + mErrors->Error(mScanner->mLocation, EERR_SYNTAX, "Identifier expected"); + + mScanner->NextToken(); + } + } + else + { + const Ident* ident = mScanner->mTokenIdent; + Location loc = mScanner->mLocation; + mScanner->NextToken(); + if (lhs && ConsumeTokenIf(TK_COLON)) + { + dec = new Declaration(loc, DT_CLABEL); + dec->mIdent = ident; + } + else + mErrors->Error(loc, EERR_OBJECT_NOT_FOUND, "Unknown identifier", ident); + } + + return dec; +} + +Expression* Parser::ParseLambdaExpression(void) +{ + Declaration* cdec = new Declaration(mScanner->mLocation, DT_TYPE_STRUCT); + cdec->mIdent = Ident::Unique("lambda", mCompilationUnits->UniqueID()); + cdec->mQualIdent = mScope->Mangle(cdec->mIdent); + cdec->mFlags |= DTF_DEFINED; + + cdec->mScope = new DeclarationScope(nullptr, SLEVEL_CLASS, cdec->mIdent); + + Declaration* olambda = mLambda; + mLambda = cdec; + + Token octoken = mCaptureToken; + mCaptureToken = TK_NONE; + + Declaration* vdec = new Declaration(mScanner->mLocation, DT_VARIABLE); + + Declaration* cpdec = cdec->BuildConstPointer(mScanner->mLocation); + + Declaration* fdec = new Declaration(mScanner->mLocation, DT_CONST_FUNCTION); + fdec->mIdent = Ident::Unique("operator()"); + fdec->mQualIdent = cdec->mScope->Mangle(fdec->mIdent); + fdec->mCompilerOptions = mCompilerOptions; + cdec->mScope->Insert(fdec->mIdent, fdec); + + Declaration* pdec = new Declaration(mScanner->mLocation, DT_ARGUMENT); + pdec->mBase = cpdec; + pdec->mSize = 2; + + Expression* einit = nullptr; + + bool capture = false; + + ConsumeToken(TK_OPEN_BRACKET); + if (!ConsumeTokenIf(TK_CLOSE_BRACKET)) + { + capture = true; + + // Parse capture list list + do { + bool reference = false; + + if (ConsumeTokenIf(TK_BINARY_AND)) + { + if (mScanner->mToken == TK_IDENT) + reference = true; + else + mCaptureToken = TK_BINARY_AND; + } + + if (ConsumeTokenIf(TK_ASSIGN)) + mCaptureToken = TK_ASSIGN; + else if (ConsumeTokenIf(TK_THIS)) + { + Declaration* mdec = new Declaration(mScanner->mLocation, DT_ELEMENT); + mdec->mIdent = Ident::Unique("this"); + mdec->mQualIdent = cdec->mScope->Mangle(mdec->mIdent); + if (cdec->mScope->Insert(mdec->mIdent, mdec)) + mErrors->Error(mdec->mLocation, EERR_DUPLICATE_DEFINITION, "Duplicate lambda capture", mdec->mIdent); + + if (mThisPointer) + { + Expression* iexp = new Expression(mScanner->mLocation, EX_VARIABLE); + iexp->mDecType = mThisPointer->mBase; + iexp->mDecValue = mThisPointer; + + mdec->mBase = iexp->mDecType; + mdec->mValue = iexp; + mdec->mNext = cdec->mParams; + cdec->mParams = mdec; + } + else + { + mErrors->Error(mScanner->mLocation, ERRO_THIS_OUTSIDE_OF_METHOD, "Use of this outside of method"); + mdec->mBase = TheVoidTypeDeclaration; + } + + mdec->mOffset = cdec->mSize; + mdec->mSize = mdec->mBase->mSize; + cdec->mSize += mdec->mSize; + } + else if (mScanner->mToken == TK_IDENT) + { + Declaration* mdec = new Declaration(mScanner->mLocation, DT_ELEMENT); + mdec->mIdent = mScanner->mTokenIdent; + mdec->mQualIdent = cdec->mScope->Mangle(mdec->mIdent); + if (cdec->mScope->Insert(mdec->mIdent, mdec)) + mErrors->Error(mdec->mLocation, EERR_DUPLICATE_DEFINITION, "Duplicate lambda capture", mdec->mIdent); + + Expression* iexp = nullptr; + + mScanner->NextToken(); + if (ConsumeTokenIf(TK_ASSIGN)) + iexp = ParseExpression(false); + else + { + Declaration* mvdec = mScope->Lookup(mdec->mIdent); + if (mvdec && (mvdec->mType == DT_VARIABLE || mvdec->mType == DT_ARGUMENT)) + { + iexp = new Expression(mdec->mLocation, EX_VARIABLE); + iexp->mDecType = mvdec->mBase; + iexp->mDecValue = mvdec; + } + else + mErrors->Error(mdec->mLocation, EERR_INVALID_CAPTURE, "Invalid variable capture", mdec->mIdent); + } + + if (iexp) + { + if (reference) + mdec->mBase = iexp->mDecType->BuildReference(mdec->mLocation); + else + mdec->mBase = iexp->mDecType; + mdec->mValue = iexp; + mdec->mNext = cdec->mParams; + cdec->mParams = mdec; + } + + mdec->mOffset = cdec->mSize; + mdec->mSize = mdec->mBase->mSize; + cdec->mSize += mdec->mSize; + } + + } while (ConsumeTokenIf(TK_COMMA)); + + ConsumeToken(TK_CLOSE_BRACKET); + } + + Declaration* rtype = new Declaration(mScanner->mLocation, DT_TYPE_AUTO); + + if (mScanner->mToken == TK_OPEN_PARENTHESIS) + { + fdec->mBase = ParseFunctionDeclaration(rtype); + } + else + { + ConsumeToken(TK_OPEN_PARENTHESIS); + fdec->mBase = TheConstVoidTypeDeclaration; + } + + if (capture) + PrependThisArgument(fdec->mBase, cpdec); + + fdec->mFlags |= fdec->mBase->mFlags & (DTF_CONST | DTF_VOLATILE); + + fdec->mSection = mCodeSection; + + if (mCompilerOptions & COPT_NATIVE) + fdec->mFlags |= DTF_NATIVE; + + fdec->mCompilerOptions = mCompilerOptions; + fdec->mBase->mCompilerOptions = mCompilerOptions; + + fdec->mVarIndex = -1; + + + int li = mLocalIndex; + Declaration* tvars = mTempVars; + + DeclarationScope * oscope = mCaptureScope; + mCaptureScope = mScope; + + while (mScope->mLevel >= SLEVEL_NAMESPACE) + mScope = mScope->mParent; + + Declaration* tfdec = FunctionAutoParamsToTemplate(fdec); + if (tfdec) + { + fdec = tfdec; + } + else + fdec->mValue = ParseFunction(fdec->mBase); + + mScope = mCaptureScope; + mLambda = olambda; + mCaptureScope = oscope; + mCaptureToken = octoken; + + Declaration* pmdec = cdec->mParams; + while (pmdec) + { + Declaration* fcons = pmdec->mBase->mType == DT_TYPE_STRUCT && pmdec->mBase->mScope ? pmdec->mBase->mScope->Lookup(pmdec->mBase->mIdent->PreMangle("+"), SLEVEL_CLASS) : nullptr; + + Expression* ciexp; + + if (fcons) + { + Expression* vexp = new Expression(pmdec->mLocation, EX_QUALIFY); + vexp->mLeft = new Expression(pmdec->mLocation, EX_VARIABLE); + vexp->mLeft->mDecType = cdec; + vexp->mLeft->mDecValue = vdec; + vexp->mDecValue = pmdec; + vexp->mDecType = pmdec->mBase; + + Expression* cexp = new Expression(mScanner->mLocation, EX_CONSTANT); + cexp->mDecValue = fcons; + cexp->mDecType = cexp->mDecValue->mBase; + + Expression* fexp = new Expression(mScanner->mLocation, EX_CALL); + fexp->mLeft = cexp; + fexp->mRight = pmdec->mValue; + + Expression* texp = new Expression(mScanner->mLocation, EX_PREFIX); + texp->mToken = TK_BINARY_AND; + texp->mLeft = vexp; + texp->mDecType = pmdec->mBase->BuildPointer(mScanner->mLocation); + + Expression* lexp = new Expression(mScanner->mLocation, EX_LIST); + lexp->mLeft = texp; + lexp->mRight = fexp->mRight; + fexp->mRight = lexp; + + fexp = ResolveOverloadCall(fexp); + + Expression* dexp = nullptr; + if (pmdec->mBase->mDestructor) + { + Expression* cexp = new Expression(mScanner->mLocation, EX_CONSTANT); + cexp->mDecValue = pmdec->mBase->mDestructor; + cexp->mDecType = cexp->mDecValue->mBase; + + dexp = new Expression(mScanner->mLocation, EX_CALL); + dexp->mLeft = cexp; + dexp->mRight = texp; + } + + ciexp = new Expression(mScanner->mLocation, EX_CONSTRUCT); + + ciexp->mLeft = new Expression(mScanner->mLocation, EX_LIST); + ciexp->mLeft->mLeft = fexp; + ciexp->mLeft->mRight = dexp; + } + else + { + ciexp = new Expression(pmdec->mLocation, EX_INITIALIZATION); + ciexp->mToken = TK_ASSIGN; + ciexp->mDecType = pmdec->mValue->mDecType; + ciexp->mRight = pmdec->mValue; + ciexp->mLeft = new Expression(pmdec->mLocation, EX_QUALIFY); + ciexp->mLeft->mLeft = new Expression(pmdec->mLocation, EX_VARIABLE); + ciexp->mLeft->mLeft->mDecType = cdec; + ciexp->mLeft->mLeft->mDecValue = vdec; + ciexp->mLeft->mDecValue = pmdec; + ciexp->mLeft->mDecType = pmdec->mBase; + } + + if (einit) + { + Expression* lexp = new Expression(mScanner->mLocation, EX_LIST); + lexp->mLeft = einit; + lexp->mRight = ciexp; + einit = lexp; + } + else + einit = ciexp; + + pmdec = pmdec->mNext; + } + + fdec->mFlags |= DTF_DEFINED | DTF_REQUEST_INLINE; + fdec->mNumVars = mLocalIndex; + + mLocalIndex = li; + mTempVars = tvars; + + vdec->mBase = cdec; + vdec->mVarIndex = mLocalIndex++; + vdec->mSize = cdec->mSize; + vdec->mFlags |= DTF_DEFINED | DTF_CONST; + vdec->mIdent = cdec->mIdent; + + Expression* vexp = new Expression(mScanner->mLocation, EX_VARIABLE); + vexp->mDecType = vdec->mBase; + vexp->mDecValue = vdec; + + + if (einit) + { + Expression* conex = new Expression(mScanner->mLocation, EX_CONSTRUCT); + conex->mRight = vexp; + conex->mDecType = vexp->mDecType; + + conex->mLeft = new Expression(mScanner->mLocation, EX_LIST); + conex->mLeft->mLeft = einit; + conex->mLeft->mRight = nullptr; + + return conex; + } + else + return vexp; +} + +Declaration* Parser::ParseTypeID(bool tid, Declaration* bdec) +{ + Declaration * dec = bdec ? bdec : ParseBaseTypeDeclaration(0, true); + + while (ConsumeTokenIf(TK_MUL)) + dec = dec->BuildPointer(mScanner->mLocation); + while (ConsumeTokenIf(TK_BINARY_AND)) + dec = dec->BuildReference(mScanner->mLocation); + if (tid && mScanner->mToken == TK_OPEN_PARENTHESIS) + dec = ParseFunctionDeclaration(dec); + return dec; +} + +Expression* Parser::ParseCastExpression(Expression* exp) +{ + if (exp->mType == EX_TYPE) + { + if (mScanner->mToken == TK_OPEN_BRACE) + { + if (mFunctionType) + { + Declaration* vdec = new Declaration(mScanner->mLocation, DT_VARIABLE); + vdec->mBase = exp->mDecType; + vdec->mVarIndex = mLocalIndex++; + vdec->mFlags |= DTF_DEFINED; + vdec->mSize = vdec->mBase->mSize; + + Expression* nexp = new Expression(mScanner->mLocation, EX_VARIABLE); + nexp->mDecValue = vdec; + nexp->mDecType = vdec->mBase; + + Expression* iexp = ParseVarInitExpression(nexp); + if (iexp) + { + if (iexp->mType == EX_INITIALIZATION || iexp->mType == EX_CONSTRUCT) + exp = iexp; + else + { + Expression* cexp = new Expression(mScanner->mLocation, EX_CONSTRUCT); + Expression* lexp = new Expression(mScanner->mLocation, EX_LIST); + lexp->mLeft = iexp; + lexp->mRight = nullptr; + cexp->mLeft = lexp; + cexp->mRight = nexp; + cexp->mDecType = vdec->mBase; + + exp = cexp; + } + } + else + exp = nexp; + } + else + { + Declaration* cdec = new Declaration(mScanner->mLocation, DT_VARIABLE); + cdec->mFlags |= DTF_CONST | DTF_STATIC | DTF_GLOBAL; + cdec->mBase = exp->mDecType; + + Expression* cexp = new Expression(mScanner->mLocation, EX_VARIABLE); + cexp->mDecValue = cdec; + + + cdec->mValue = ParseConstInitExpression(cdec->mBase); + cdec->mSection = mDataSection; + cdec->mSize = cdec->mBase->mSize; + cexp->mDecType = cdec->mBase; + + exp = cexp; + } + } + else + { + Expression* nexp = new Expression(mScanner->mLocation, EX_TYPECAST); + nexp->mDecType = exp->mDecType; + if (ConsumeTokenIf(TK_OPEN_PARENTHESIS)) + { + nexp->mLeft = ParseListExpression(false); + ConsumeToken(TK_CLOSE_PARENTHESIS); + + nexp->mLeft = ParseCastExpression(nexp->mLeft); + } + else + nexp->mLeft = ParsePrefixExpression(false); + nexp = CheckOperatorOverload(nexp); + exp = nexp->ConstantFold(mErrors, mDataSection); + if (exp->mType == EX_TYPECAST && exp->mLeft->mType == EX_TYPE) + { + mErrors->Error(nexp->mLocation, EERR_INVALID_VALUE, "Invalid cast of type as a value", exp->mLeft->mDecType->mIdent); + exp = nullptr; + } + } + } + + return exp; +} + +Expression* Parser::ParseIdentExpression(const Location & eloc, Declaration* dec, bool lhs, bool tid) +{ + Expression* exp = nullptr; + + if (dec->mTemplate && mScanner->mToken == TK_LESS_THAN) + { + dec = ParseTemplateExpansion(dec->mTemplate, nullptr); + while (ConsumeTokenIf(TK_COLCOLON)) + { + if (mScanner->mToken == TK_IDENT) + { + if (dec->mType == DT_NAMESPACE || dec->mType == DT_TYPE_STRUCT || dec->mType == DT_TYPE_ENUM) + { + Declaration* ndec = dec->mScope->Lookup(mScanner->mTokenIdent, SLEVEL_USING); + + if (ndec) + dec = ndec; + else + mErrors->Error(mScanner->mLocation, EERR_OBJECT_NOT_FOUND, "Unknown identifier", mScanner->mTokenIdent); + } + else + mErrors->Error(mScanner->mLocation, EERR_INCOMPATIBLE_OPERATOR, "Not a class or namespace"); + + } + else + mErrors->Error(mScanner->mLocation, EERR_SYNTAX, "Identifier expected"); + + mScanner->NextToken(); + } + } + + if (dec->mType == DT_CONST_INTEGER || dec->mType == DT_CONST_FLOAT || dec->mType == DT_CONST_FUNCTION || dec->mType == DT_CONST_ASSEMBLER || dec->mType == DT_LABEL || dec->mType == DT_LABEL_REF) + { + exp = new Expression(eloc, EX_CONSTANT); + exp->mDecValue = dec; + exp->mDecType = dec->mBase; + exp->mConst = true; + } + else if (dec->mType == DT_VARIABLE || dec->mType == DT_ARGUMENT) + { + if (/*(dec->mFlags & DTF_STATIC) &&*/ (dec->mFlags & DTF_CONST) && dec->mValue) + { + if (dec->mBase->IsNumericType()) + { + if (dec->mValue->mType == EX_CONSTANT) + { + exp = dec->mValue->Clone(eloc); + } + } + else if (dec->mBase->mType == DT_TYPE_POINTER) + { + if (dec->mValue->mType == EX_CONSTANT) + { + if (dec->mValue->mDecValue->mType == DT_CONST_ADDRESS || dec->mValue->mDecValue->mType == DT_CONST_POINTER) + exp = dec->mValue->Clone(eloc); + } + } + } + + if (!exp) + { + exp = new Expression(eloc, EX_VARIABLE); + exp->mDecValue = dec; + exp->mDecType = dec->mBase; + } + } + else if (dec->mType == DT_BINDING) + { + return dec->mValue; + } + else if (dec->mType == DT_PACK_ARGUMENT) + { + exp = new Expression(mScanner->mLocation, EX_PACK); + exp->mDecValue = dec; + exp->mDecType = dec->mBase; + } + else if (dec->mType == DT_PACK_TYPE) + { + exp = new Expression(mScanner->mLocation, EX_PACK_TYPE); + exp->mDecValue = nullptr; + exp->mDecType = dec; + } + else if (dec->mType <= DT_TYPE_FUNCTION) + { + if (lhs) + exp = ParseDeclarationExpression(dec); + else + { + exp = new Expression(mScanner->mLocation, EX_TYPE); + exp->mDecValue = nullptr; + exp->mDecType = ParseTypeID(tid, dec); + } + } + else if (dec->mType == DT_CONST_TEMPLATE) + { + exp = new Expression(mScanner->mLocation, EX_CONSTANT); + exp->mDecValue = dec; + exp->mDecType = TheSignedIntTypeDeclaration; + } + else if (dec->mType == DT_PACK_TEMPLATE) + { + exp = new Expression(mScanner->mLocation, EX_PACK_TYPE); + exp->mDecType = dec; + } + else if (dec->mType == DT_ELEMENT) + { + mErrors->Error(mScanner->mLocation, EERR_NON_STATIC_MEMBER, "Non static member access", mScanner->mTokenIdent); + } + else if (dec->mType == DT_CLABEL) + { + exp = new Expression(dec->mLocation, EX_LABEL); + exp->mDecValue = dec; + } + else + { + mErrors->Error(mScanner->mLocation, EERR_INVALID_IDENTIFIER, "Invalid identifier", mScanner->mTokenIdent); + } + + return exp; +} + +Expression* Parser::ParseSimpleExpression(bool lhs, bool tid) +{ + Declaration* dec = nullptr; + Expression* exp = nullptr, * rexp = nullptr; + + Location eloc(mScanner->mLocation); + + switch (mScanner->mToken) + { + case TK_INT: + case TK_SHORT: + case TK_LONG: + case TK_FLOAT: + case TK_CHAR: + case TK_BOOL: + case TK_VOID: + case TK_UNSIGNED: + case TK_SIGNED: + case TK_CONST: + if (lhs) + exp = ParseDeclarationExpression(nullptr); + else + { + exp = new Expression(mScanner->mLocation, EX_TYPE); + exp->mDecValue = nullptr; + exp->mDecType = ParseTypeID(tid); + } + break; + + case TK_VOLATILE: + case TK_STRUCT: + case TK_CLASS: + case TK_UNION: + case TK_ENUM: + case TK_TYPEDEF: + case TK_STATIC: + case TK_AUTO: + case TK_STRIPED: + case TK_DECLTYPE: + case TK_MEMMAP: + case TK_TYPENAME: + case TK_EXTERN: + exp = ParseDeclarationExpression(nullptr); + break; + + case TK_USING: + ParseDeclaration(nullptr, true, true, false); + break; + + case TK_CHARACTER: + dec = new Declaration(mScanner->mLocation, DT_CONST_INTEGER); + dec->mInteger = mCharMap[(unsigned char)mScanner->mTokenInteger]; + dec->mBase = TheUnsignedCharTypeDeclaration; + exp = new Expression(mScanner->mLocation, EX_CONSTANT); + exp->mDecValue = dec; + exp->mDecType = dec->mBase; + + mScanner->NextToken(); + break; + case TK_INTEGER: + dec = new Declaration(mScanner->mLocation, DT_CONST_INTEGER); + dec->mInteger = mScanner->mTokenInteger; + if (dec->mInteger < 32768) + dec->mBase = TheSignedIntTypeDeclaration; + else + dec->mBase = TheUnsignedIntTypeDeclaration; + exp = new Expression(mScanner->mLocation, EX_CONSTANT); + exp->mDecValue = dec; + exp->mDecType = dec->mBase; + + mScanner->NextToken(); + break; + case TK_INTEGERU: + dec = new Declaration(mScanner->mLocation, DT_CONST_INTEGER); + dec->mInteger = mScanner->mTokenInteger; + dec->mBase = TheUnsignedIntTypeDeclaration; + exp = new Expression(mScanner->mLocation, EX_CONSTANT); + exp->mDecValue = dec; + exp->mDecType = dec->mBase; + + mScanner->NextToken(); + break; + case TK_INTEGERL: + dec = new Declaration(mScanner->mLocation, DT_CONST_INTEGER); + dec->mInteger = mScanner->mTokenInteger; + if (dec->mInteger < 0x80000000) + dec->mBase = TheSignedLongTypeDeclaration; + else + dec->mBase = TheUnsignedLongTypeDeclaration; + exp = new Expression(mScanner->mLocation, EX_CONSTANT); + exp->mDecValue = dec; + exp->mDecType = dec->mBase; + + mScanner->NextToken(); + break; + case TK_INTEGERUL: + dec = new Declaration(mScanner->mLocation, DT_CONST_INTEGER); + dec->mInteger = mScanner->mTokenInteger; + dec->mBase = TheUnsignedLongTypeDeclaration; + exp = new Expression(mScanner->mLocation, EX_CONSTANT); + exp->mDecValue = dec; + exp->mDecType = dec->mBase; + + mScanner->NextToken(); + break; + case TK_NUMBER: + dec = new Declaration(mScanner->mLocation, DT_CONST_FLOAT); + dec->mBase = TheFloatTypeDeclaration; + dec->mNumber = mScanner->mTokenNumber; + exp = new Expression(mScanner->mLocation, EX_CONSTANT); + exp->mDecValue = dec; + exp->mDecType = dec->mBase; + + mScanner->NextToken(); + break; + case TK_STRING: + { + dec = new Declaration(mScanner->mLocation, DT_CONST_DATA); + int size = mScanner->mTokenStringSize; + dec->mSize = size + 1; + dec->mVarIndex = -1; + dec->mSection = mCodeSection; + dec->mBase = new Declaration(mScanner->mLocation, DT_TYPE_ARRAY); + dec->mBase->mSize = dec->mSize; + dec->mBase->mBase = TheConstCharTypeDeclaration; + dec->mBase->mFlags |= DTF_DEFINED | DTF_CONST; + uint8* d = new uint8[size + 1]; + dec->mData = d; + + int i = 0; + while (i < size) + { + d[i] = mCharMap[(uint8)mScanner->mTokenString[i]]; + i++; + } + d[size] = 0; + + exp = new Expression(mScanner->mLocation, EX_CONSTANT); + exp->mDecValue = dec; + exp->mDecType = dec->mBase; + + mScanner->NextToken(); + + // automatic string concatenation + while (mScanner->mToken == TK_STRING) + { + int s = mScanner->mTokenStringSize; + uint8* d = new uint8[size + s + 1]; + memcpy(d, dec->mData, size); + int i = 0; + while (i < s) + { + d[size + i] = mCharMap[(uint8)mScanner->mTokenString[i]]; + i++; + } + size += s; + d[size] = 0; + dec->mSize = size + 1; + delete[] dec->mData; + dec->mData = d; + mScanner->NextToken(); + } + break; + } + case TK_TRUE: + dec = new Declaration(mScanner->mLocation, DT_CONST_INTEGER); + dec->mBase = TheBoolTypeDeclaration; + dec->mInteger = 1; + exp = new Expression(mScanner->mLocation, EX_CONSTANT); + exp->mDecValue = dec; + exp->mDecType = dec->mBase; + + mScanner->NextToken(); + break; + case TK_FALSE: + dec = new Declaration(mScanner->mLocation, DT_CONST_INTEGER); + dec->mBase = TheBoolTypeDeclaration; + dec->mInteger = 0; + exp = new Expression(mScanner->mLocation, EX_CONSTANT); + exp->mDecValue = dec; + exp->mDecType = dec->mBase; + + mScanner->NextToken(); + break; + case TK_NULL: + dec = new Declaration(mScanner->mLocation, DT_CONST_ADDRESS); + dec->mBase = TheNullPointerTypeDeclaration; + dec->mInteger = 0; + exp = new Expression(mScanner->mLocation, EX_CONSTANT); + exp->mDecValue = dec; + exp->mDecType = dec->mBase; + + mScanner->NextToken(); + break; + case TK_THIS: + if (mLambda) + { + dec = mLambda->mScope->Lookup(Ident::Unique("this"), SLEVEL_CLASS); + if (dec) + { + Expression* texp = new Expression(mScanner->mLocation, EX_VARIABLE); + texp->mDecType = mThisPointer->mBase; + texp->mDecValue = mThisPointer; + + Expression* dexp = new Expression(mScanner->mLocation, EX_PREFIX); + dexp->mToken = TK_MUL; + dexp->mDecType = texp->mDecType->mBase; + dexp->mLeft = texp; + + dexp = dexp->ConstantFold(mErrors, mDataSection); + + exp = new Expression(mScanner->mLocation, EX_QUALIFY); + exp->mLeft = dexp; + exp->mDecType = dec->mBase; + exp->mDecValue = dec; + } + else + mErrors->Error(mScanner->mLocation, ERRO_THIS_OUTSIDE_OF_METHOD, "Use of this outside of method"); + } + else if (mThisPointer) + { + exp = new Expression(mScanner->mLocation, EX_VARIABLE); + exp->mDecType = mThisPointer->mBase; + exp->mDecValue = mThisPointer; + } + else + mErrors->Error(mScanner->mLocation, ERRO_THIS_OUTSIDE_OF_METHOD, "Use of this outside of method"); + + mScanner->NextToken(); + break; + case TK_COLCOLON: + dec = ParseQualIdent(lhs); + if (dec) + exp = ParseIdentExpression(eloc, dec, lhs); + break; + + case TK_IDENT: + if (mLambda && mCaptureToken != TK_NONE && !mScope->Lookup(mScanner->mTokenIdent, SLEVEL_CLASS) && !mLambda->mScope->Lookup(mScanner->mTokenIdent, SLEVEL_CLASS)) + { + Declaration * mvdec = mCaptureScope->Lookup(mScanner->mTokenIdent, SLEVEL_CLASS); + if (mvdec && mvdec->mType == DT_VARIABLE) + { + Declaration* mdec = new Declaration(mScanner->mLocation, DT_ELEMENT); + mdec->mIdent = mScanner->mTokenIdent; + mdec->mQualIdent = mLambda->mScope->Mangle(mdec->mIdent); + if (mLambda->mScope->Insert(mdec->mIdent, mdec)) + mErrors->Error(mdec->mLocation, EERR_DUPLICATE_DEFINITION, "Duplicate lambda capture", mdec->mIdent); + + Expression* iexp = new Expression(mdec->mLocation, EX_VARIABLE); + iexp->mDecType = mvdec->mBase; + iexp->mDecValue = mvdec; + + if (mCaptureToken == TK_BINARY_AND) + mdec->mBase = iexp->mDecType->BuildReference(mdec->mLocation); + else + mdec->mBase = iexp->mDecType; + mdec->mValue = iexp; + mdec->mNext = mLambda->mParams; + mLambda->mParams = mdec; + + mdec->mOffset = mLambda->mSize; + mdec->mSize = mdec->mBase->mSize; + mLambda->mSize += mdec->mSize; + } + } + + if (mThisPointer && !mScope->Lookup(mScanner->mTokenIdent, SLEVEL_FUNCTION)) + { + int offset; + uint64 flags; + + if (mThisPointer->mType == DT_ARGUMENT) + dec = MemberLookup(mThisPointer->mBase->mBase, mScanner->mTokenIdent, offset, flags); + else if (mThisPointer->mType == DT_TYPE_POINTER) + dec = MemberLookup(mThisPointer->mBase, mScanner->mTokenIdent, offset, flags); + + if (dec) + { + if ((dec->mType == DT_ELEMENT || dec->mType == DT_CONST_FUNCTION) && !(dec->mFlags & DTF_STATIC)) + { + if (mThisPointer->mType == DT_TYPE_POINTER) + { + mErrors->Error(mScanner->mLocation, ERRO_THIS_OUTSIDE_OF_METHOD, "Invalid non static member"); + } + else + { + Expression* texp = new Expression(mScanner->mLocation, EX_VARIABLE); + texp->mDecType = mThisPointer->mBase; + texp->mDecValue = mThisPointer; + + Expression* dexp = new Expression(mScanner->mLocation, EX_PREFIX); + dexp->mToken = TK_MUL; + dexp->mDecType = texp->mDecType->mBase; + dexp->mLeft = texp; + + dexp = dexp->ConstantFold(mErrors, mDataSection); + + exp = ParseQualify(dexp); + } + } + else + mScanner->NextToken(); + } + } + + if (!exp) + { + if (!dec) + dec = ParseQualIdent(lhs); + if (dec) + exp = ParseIdentExpression(eloc, dec, lhs, tid); + } + + break; + case TK_SIZEOF: + mScanner->NextToken(); + + dec = new Declaration(mScanner->mLocation, DT_CONST_INTEGER); + dec->mBase = TheSignedIntTypeDeclaration; + + exp = new Expression(mScanner->mLocation, EX_CONSTANT); + exp->mDecValue = dec; + exp->mDecType = dec->mBase; + + if (ConsumeTokenIf(TK_ELLIPSIS)) + { + rexp = ParseParenthesisExpression(); + if (rexp->mType == EX_PACK) + { + int n = 0; + Declaration* vdec = rexp->mDecValue->mParams; + while (vdec) + { + n++; + vdec = vdec->mNext; + } + dec->mInteger = n; + } + else + mErrors->Error(mScanner->mLocation, EERR_INVALID_PACK_USAGE, "variadic pack expected"); + } + else + { + rexp = ParseParenthesisExpression(); + Declaration* btype; + if (rexp->mDecType->IsReference()) + btype = rexp->mDecType->mBase; + else + btype = rexp->mDecType; + + if (btype->mFlags & DTF_DEFINED) + dec->mInteger = btype->mSize; + else + { + exp->mType = EX_PREFIX; + exp->mToken = TK_SIZEOF; + exp->mLeft = new Expression(mScanner->mLocation, EX_TYPE); + exp->mLeft->mDecType = btype; + exp->mLeft->mDecValue = nullptr; + } + } + + break; + + case TK_OPEN_PARENTHESIS: + mScanner->NextToken(); + exp = ParseListExpression(true); + ConsumeToken(TK_CLOSE_PARENTHESIS); + + if (exp->mType == EX_LIST) + exp->mType = EX_COMMA; + exp = ParseCastExpression(exp); + break; + case TK_OPEN_BRACKET: + if (mCompilerOptions & COPT_CPLUSPLUS) + exp = ParseLambdaExpression(); + else + { + mErrors->Error(mScanner->mLocation, EERR_SYNTAX, "Term starts with invalid token", TokenNames[mScanner->mToken]); + mScanner->NextToken(); + } + break; + case TK_OPEN_BRACE: + if (!lhs) + { + mScanner->NextToken(); + exp = new Expression(mScanner->mLocation, EX_AGGREGATE); + exp->mLeft = ParseListExpression(false); + exp->mDecType = TheConstVoidTypeDeclaration; + ConsumeToken(TK_CLOSE_BRACE); + } + else + { + mErrors->Error(mScanner->mLocation, EERR_SYNTAX, "Term starts with invalid token", TokenNames[mScanner->mToken]); + mScanner->NextToken(); + } + break; + case TK_ASM: + { + + mScanner->NextToken(); + + uint64 aflags = ParseAssemblerFlags(); + + if (mScanner->mToken == TK_OPEN_BRACE) + { + mScanner->NextToken(); + exp = ParseAssembler(); + exp->mDecValue->mFlags |= aflags; + + exp->mDecType = TheSignedLongTypeDeclaration; + if (mScanner->mToken == TK_CLOSE_BRACE) + mScanner->NextToken(); + else + mErrors->Error(mScanner->mLocation, EERR_SYNTAX, "'}' expected"); + } + else + { + mErrors->Error(mScanner->mLocation, EERR_SYNTAX, "'{' expected"); + exp = new Expression(mScanner->mLocation, EX_VOID); + } + } break; + default: + mErrors->Error(mScanner->mLocation, EERR_SYNTAX, "Term starts with invalid token", TokenNames[mScanner->mToken]); + mScanner->NextToken(); + } + + if (!exp) + { + exp = new Expression(mScanner->mLocation, EX_VOID); + exp->mDecType = TheVoidTypeDeclaration; + } + + return exp; +} + +Declaration* Parser::MemberLookup(Declaration* dtype, const Ident* ident, int & offset, uint64& flags) +{ + if ((mCompilerOptions & COPT_CPLUSPLUS) && ident == dtype->mIdent) + return nullptr; + + Declaration* mdec = dtype->mScope ? dtype->mScope->Lookup(ident, SLEVEL_CLASS) : nullptr; + offset = 0; + flags = 0; + + if (!mdec) + { + Declaration* bcdec = dtype->mBase; + + while (bcdec) + { + int noffset; + uint64 nflags; + + Declaration* ndec = MemberLookup(bcdec->mBase, ident, noffset, nflags); + if (ndec) + { + if (mdec) + mErrors->Error(mScanner->mLocation, EERR_DUPLICATE_DEFINITION, "Multiple definitions for member", ident); + else + { + mdec = ndec; + offset = noffset + bcdec->mOffset; + flags = nflags | bcdec->mFlags; + } + } + bcdec = bcdec->mNext; + } + } + else + flags = mdec->mFlags; + + return mdec; +} + +uint64 Parser::ParseAssemblerFlags(void) +{ + uint64 flags = 0; + + if (ConsumeTokenIf(TK_VOLATILE)) + flags |= DTF_VOLATILE; + + if (ConsumeTokenIf(TK_MEMMAP)) + flags |= DTF_MEMMAP; + + if (ConsumeTokenIf(TK_PRESERVES)) + { + ConsumeToken(TK_OPEN_PARENTHESIS); + if (mScanner->mToken == TK_IDENT) + { + char* cp = mScanner->mTokenIdent->mString; + while (*cp) + { + switch (*cp) + { + case 'a': + case 'A': + flags |= DTF_ASM_PRESERVE_A; + break; + case 'x': + case 'X': + flags |= DTF_ASM_PRESERVE_X; + break; + case 'y': + case 'Y': + flags |= DTF_ASM_PRESERVE_Y; + break; + default: + mErrors->Error(mScanner->mLocation, EERR_ASM_INVALID_REGISTER, "Invalid register specified", mScanner->mTokenIdent); + } + cp++; + } + mScanner->NextToken(); + } + ConsumeToken(TK_CLOSE_PARENTHESIS); + } + + return flags; +} + +Expression* Parser::ParseQualify(Expression* exp) +{ + Declaration* dtype = exp->mDecType; + + exp = exp->ConstantFold(mErrors, mDataSection); + + if (dtype->IsReference()) + dtype = dtype->mBase; + + if (dtype->mType == DT_TYPE_STRUCT || dtype->mType == DT_TYPE_UNION) + { + Expression* nexp = new Expression(mScanner->mLocation, EX_QUALIFY); + nexp->mLeft = exp; + + int moffset = 0; + uint64 mflags = 0; + Declaration* mdec = nullptr; + const Ident* ident = nullptr; + bool destructor = false; + + if ((mCompilerOptions & COPT_CPLUSPLUS) && mScanner->mToken == TK_BINARY_NOT) + { + mScanner->NextToken(); + if (mScanner->mToken == TK_IDENT) + { + ident = mScanner->mTokenIdent; + if (mTemplateScope) + { + Declaration* dec = mTemplateScope->Lookup(ident); + if (dec) + ident = dec->mIdent; + } + + ident = ident->PreMangle("~"); + destructor = true; + } + else + mErrors->Error(mScanner->mLocation, EERR_SYNTAX, "Identifier expected"); + } + else if (mScanner->mToken == TK_IDENT) + ident = mScanner->mTokenIdent; + + if (ident) + { + mdec = MemberLookup(dtype, ident, moffset, mflags); + + if (mdec) + { + if (mflags & DTF_PROTECTED) + { + Declaration* tp = mThisPointer; + if (tp && tp->mType == DT_ARGUMENT) + tp = tp->mBase; + if (!(tp && tp->mBase->IsConstSame(dtype))) + { + if (dtype->mFriends.Contains(mFunction)) + ; + else if (mThisPointer && mThisPointer->mBase && mThisPointer->mBase->mBase && dtype->mFriends.Contains(mThisPointer->mBase->mBase->ToMutableType())) + ; + else + mErrors->Error(mScanner->mLocation, EERR_OBJECT_NOT_FOUND, "Struct member identifier not visible", ident); + } + } + + mScanner->NextToken(); + if (mdec->mType == DT_ELEMENT) + { + nexp->mDecValue = mdec; + nexp->mDecType = mdec->mBase; + if (exp->mDecType->mFlags & DTF_CONST) + nexp->mDecType = nexp->mDecType->ToConstType(); + + exp = nexp->ConstantFold(mErrors, mDataSection); + } + else if (mdec->mType == DT_VARIABLE) + { + nexp = new Expression(mScanner->mLocation, EX_VARIABLE); + nexp->mDecValue = mdec; + nexp->mDecType = mdec->mBase; + exp = nexp->ConstantFold(mErrors, mDataSection); + } + else if (mdec->mType == DT_CONST_FUNCTION) + { + if (mdec->mTemplate && mScanner->mToken == TK_LESS_THAN) + mdec = ParseTemplateExpansion(mdec->mTemplate, nullptr); + + if (mdec->mBase->mFlags & DTF_FUNC_THIS) + { + ConsumeToken(TK_OPEN_PARENTHESIS); + + nexp = new Expression(mScanner->mLocation, EX_CALL); + if (mInlineCall) + nexp->mType = EX_INLINE; + mInlineCall = false; + + nexp->mLeft = new Expression(mScanner->mLocation, EX_CONSTANT); + nexp->mLeft->mDecType = mdec->mBase; + nexp->mLeft->mDecValue = mdec; + + nexp->mDecType = mdec->mBase; + if (ConsumeTokenIf(TK_CLOSE_PARENTHESIS)) + nexp->mRight = nullptr; + else + { + nexp->mRight = ParseListExpression(false, mdec->mBase->mParams->mNext); + ConsumeToken(TK_CLOSE_PARENTHESIS); + } + + Expression* texp = new Expression(nexp->mLocation, EX_PREFIX); + texp->mToken = TK_BINARY_AND; + texp->mLeft = exp; + texp->mDecType = new Declaration(nexp->mLocation, DT_TYPE_POINTER); + texp->mDecType->mFlags |= DTF_CONST | DTF_DEFINED; + if (exp->mDecType->IsReference()) + texp->mDecType->mBase = exp->mDecType->mBase; + else + texp->mDecType->mBase = exp->mDecType; + texp->mDecType->mSize = 2; + + if (nexp->mRight) + { + Expression* lexp = new Expression(nexp->mLocation, EX_LIST); + lexp->mLeft = texp; + lexp->mRight = nexp->mRight; + nexp->mRight = lexp; + } + else + nexp->mRight = texp; + + nexp = ResolveOverloadCall(nexp); + + nexp->mDecType = nexp->mLeft->mDecType->mBase; + + if (nexp->mLeft->mDecType->mFlags & DTF_VIRTUAL) + nexp->mType = EX_VCALL; + + exp = nexp; + } + else + { + nexp = new Expression(mScanner->mLocation, EX_CONSTANT); + nexp->mDecValue = mdec; + nexp->mDecType = mdec->mBase; + exp = nexp; + } + } + else if (mdec->mType == DT_CONST_INTEGER || mdec->mType == DT_CONST_FLOAT || mdec->mType == DT_CONST_POINTER || mdec->mType == DT_CONST_ADDRESS) + { + nexp = new Expression(mScanner->mLocation, EX_CONSTANT); + nexp->mDecValue = mdec; + nexp->mDecType = mdec->mBase; + exp = nexp; + } + } + else if (destructor) + { + mScanner->NextToken(); + ConsumeToken(TK_OPEN_PARENTHESIS); + ConsumeToken(TK_CLOSE_PARENTHESIS); + } + else + { + mErrors->Error(mScanner->mLocation, EERR_OBJECT_NOT_FOUND, "Struct member identifier not found", mScanner->mTokenIdent); + mScanner->NextToken(); + } + } + else + mErrors->Error(mScanner->mLocation, EERR_SYNTAX, "Struct member identifier expected"); + + } + else if ((mCompilerOptions & COPT_CPLUSPLUS) && mScanner->mToken == TK_BINARY_NOT) + { + mScanner->NextToken(); + if (mScanner->mToken == TK_IDENT) + { + const Ident * ident = mScanner->mTokenIdent; + if (mTemplateScope) + { + Declaration* dec = mTemplateScope->Lookup(ident); + if (dec == dtype) + { + mScanner->NextToken(); + ConsumeToken(TK_OPEN_PARENTHESIS); + ConsumeToken(TK_CLOSE_PARENTHESIS); + + exp = new Expression(mScanner->mLocation, EX_VOID); + exp->mDecType = TheConstVoidTypeDeclaration; + } + } + else if (ident == dtype->mIdent) + { + mScanner->NextToken(); + ConsumeToken(TK_OPEN_PARENTHESIS); + ConsumeToken(TK_CLOSE_PARENTHESIS); + + exp = new Expression(mScanner->mLocation, EX_VOID); + exp->mDecType = TheConstVoidTypeDeclaration; + } + else + mErrors->Error(mScanner->mLocation, EERR_SYNTAX, "Destructor identifier expected"); + } + else + mErrors->Error(mScanner->mLocation, EERR_SYNTAX, "Identifier expected"); + } + else if (exp->mType == EX_CONSTANT && exp->mDecValue->mType == DT_CONST_ASSEMBLER) + { + if (mScanner->mToken == TK_IDENT) + { + Declaration* ldec = exp->mDecValue->mBase->mScope->Lookup(mScanner->mTokenIdent); + if (ldec) + { + exp->mDecValue = ldec; + } + else + mErrors->Error(mScanner->mLocation, EERR_OBJECT_NOT_FOUND, "Assembler label not found", mScanner->mTokenIdent); + mScanner->NextToken(); + } + else + mErrors->Error(mScanner->mLocation, EERR_SYNTAX, "Identifier expected"); + } + else + mErrors->Error(mScanner->mLocation, EERR_INCOMPATIBLE_OPERATOR, "Struct expected"); + + return exp; +} + +static const int NOOVERLOAD = 0x7fffffff; + +int Parser::OverloadDistance(Declaration* fdec, Expression* pexp) +{ + if (fdec->mTemplate) + { + // this is a constructor, so avoid instantiating a single parameter with the type itself + // as this would only lead to misery and pain and a crashing recursion + if (fdec->mIdent->mString[0] == '+') + { + if (fdec->mBase->mParams && fdec->mBase->mParams->mNext && !fdec->mBase->mParams->mNext->mNext && + fdec->mBase->mParams->mNext->mBase->mType == DT_TYPE_TEMPLATE && + fdec->mBase->mParams->mBase->mBase) + { + if (pexp->mRight && pexp->mRight->mDecType->IsConstSame(fdec->mBase->mParams->mBase->mBase)) + return NOOVERLOAD; + } + } + + Declaration* tdec = new Declaration(mScanner->mLocation, DT_TEMPLATE); + tdec->mScope = new DeclarationScope(nullptr, SLEVEL_TEMPLATE); + if (!tdec->CanResolveTemplate(pexp, fdec)) + return NOOVERLOAD; + } + + Declaration* pdec = fdec->mBase->mParams; + + int dist = 0; + + bool packtemp = false; + while (pexp) + { + Expression* ex = pexp; + if (pexp->mType == EX_LIST) + { + ex = pexp->mLeft; + pexp = pexp->mRight; + } + else + pexp = nullptr; + + if (pdec) + { + Declaration* ptype = pdec->mBase; + Declaration* etype = ex->mDecType; + if (etype->mType == DT_TYPE_REFERENCE || etype->mType == DT_TYPE_RVALUEREF) + etype = etype->mBase; + + if (ptype->mType == DT_PACK_TEMPLATE || ptype->mType == DT_TYPE_REFERENCE && ptype->mBase->mType == DT_PACK_TEMPLATE) + packtemp = true; + + if (ptype->mType == DT_TYPE_TEMPLATE || ptype->mType == DT_PACK_TEMPLATE) + { + dist += 1; + } + else if (ptype->mType == DT_TYPE_REFERENCE && (ptype->mBase->mType == DT_TYPE_TEMPLATE || ptype->mBase->mType == DT_PACK_TEMPLATE)) + { + if (ex->IsLValue()) + dist += 1; + else if (ptype->mBase->mFlags & DTF_CONST) + dist += 1; + else + return NOOVERLOAD; + } + else if (ptype->mType == DT_TYPE_INTEGER && etype->mType == DT_TYPE_INTEGER) + { + if (ptype->mSize == etype->mSize) + { + if ((ptype->mFlags & DTF_SIGNED) == (etype->mFlags & DTF_SIGNED)) + dist += 0; + else + dist += 8; + } + else if (ptype->mSize > etype->mSize) + { + if (ptype->mFlags & DTF_SIGNED) + { + dist += 2; + } + else if (etype->mFlags & DTF_SIGNED) + { + dist += 8; + } + else + dist += 2; + } + else if (ex->mType == EX_CONSTANT && ex->mDecValue->mType == DT_CONST_INTEGER) + { + int64 v = ex->mDecValue->mInteger; + + if (ptype->mFlags & DTF_SIGNED) + { + if (v >= -128 && v <= 127) + dist += 2; + else if (ptype->mSize >= 2 && v >= -32768 && v <= 32767) + dist += 2; + else if (ptype->mSize == 4) + dist += 2; + else + dist += 32; + } + else if (v >= 0) + { + if (v <= 255) + dist += 2; + else if (ptype->mSize >= 2 && v <= 65535) + dist += 2; + else if (ptype->mSize == 4) + dist += 2; + else + dist += 32; + } + else + dist += 32; + } + else + dist += 32; + } + else if (ptype->mType == DT_TYPE_FLOAT && etype->mType == DT_TYPE_FLOAT) + dist += 0; + else if (ptype->mType == DT_TYPE_INTEGER && etype->mType == DT_TYPE_FLOAT) + dist += 16; + else if (ptype->mType == DT_TYPE_INTEGER && etype->mType == DT_TYPE_ENUM) + dist += 32; + else if (ptype->mType == DT_TYPE_FLOAT && etype->mType == DT_TYPE_INTEGER) + dist += 8; + else if (ptype->IsSame(ex->mDecType)) + ; + else if (CanCoerceExpression(ex, ptype)) + { + dist += 512; + if (ptype->mType == DT_TYPE_REFERENCE) + dist += 8; + else if (ptype->mType == DT_TYPE_RVALUEREF) + dist += 4; + } + else if (ptype->mType == DT_TYPE_REFERENCE && ptype->mBase->mType == DT_TYPE_STRUCT && etype->mType == DT_TYPE_STRUCT) + { + if (ex->IsLValue()) + dist += 4; + else if (ptype->mBase->mFlags & DTF_CONST) + dist += 8; + else + return NOOVERLOAD; + + if (ptype->mBase->mTemplate) + { + dist += 16; + } + else + { + int ncast = 0; + Declaration* ext = ex->mDecType; + while (ext && !ext->IsConstSame(ptype->mBase)) + { + ncast++; + ext = ext->mBase; + } + + if (ext) + { + if ((etype->mFlags & DTF_CONST) && !(ptype->mBase->mFlags & DTF_CONST)) + return NOOVERLOAD; + + dist += 32 * ncast; + } + else + return NOOVERLOAD; + } + } + else if (ptype->mType == DT_TYPE_POINTER && etype->mType == DT_TYPE_POINTER && ptype->mBase->IsSame(etype->mBase)) + ; + else if (ptype->mType == DT_TYPE_POINTER && (etype->mType == DT_TYPE_ARRAY || etype->mType == DT_TYPE_POINTER) && ptype->mBase->IsSameMutable(etype->mBase)) + dist += 2; + else if (ptype->mType == DT_TYPE_POINTER && etype->mType == DT_TYPE_FUNCTION && ptype->mBase->IsSame(etype)) + dist += 0; + else if (ptype->mType == DT_TYPE_POINTER && etype->mType == DT_TYPE_POINTER && etype->mBase->mType == DT_TYPE_VOID) + dist += 0; + else if (ptype->IsSubType(etype)) + dist += 256; + else if (ptype->mType == DT_TYPE_RVALUEREF && ptype->mBase->IsSameMutable(etype) && ex->IsRValue()) + { + dist += 2; + } + else if (ptype->mType == DT_TYPE_REFERENCE && ptype->mBase->IsSameMutable(etype)) + { + if (ex->IsLValue()) + dist += 4; + else if (ptype->mBase->mFlags & DTF_CONST) + dist += 8; + else + return NOOVERLOAD; + } + else if (ptype->mType == DT_TYPE_REFERENCE && (ptype->mBase->mFlags & DTF_CONST) && ptype->mBase->CanAssign(etype)) + { + dist += 32; + } + else if (ptype->IsSimpleType() && etype->IsSimpleType() && ptype->IsConstSame(etype)) + dist += 2; + else if (ptype->mType == DT_TYPE_STRUCT && etype->mType == DT_TYPE_STRUCT) + { + int ncast = 0; + Declaration* ext = ex->mDecType; + while (ext && !ext->IsConstSame(ptype)) + { + ncast++; + ext = ext->mBase; + } + + if (ext) + dist += 32 * ncast; + else + return NOOVERLOAD; + } + else + return NOOVERLOAD; + + if (!packtemp) + pdec = pdec->mNext; + } + else if (fdec->mBase->mFlags & DTF_VARIADIC) + { + dist += 1024; + break; + } + else + return NOOVERLOAD; + } + + while (pdec && pdec->mValue) + { + dist += 1024; + pdec = pdec->mNext; + } + + if (pdec) + { + Declaration* ptype = pdec->mBase; + if (ptype->mType == DT_PACK_TEMPLATE || ptype->mType == DT_TYPE_REFERENCE && ptype->mBase->mType == DT_PACK_TEMPLATE) + packtemp = true; + } + + if (pdec && packtemp) + return dist; + + if (pdec) + return NOOVERLOAD; + + return dist; +} + +bool Parser::CanCoerceExpression(Expression* exp, Declaration* type) +{ + Declaration* tdec = exp->mDecType; + while (tdec->mType == DT_TYPE_REFERENCE || tdec->mType == DT_TYPE_RVALUEREF) + tdec = tdec->mBase; + while (type->mType == DT_TYPE_REFERENCE || type->mType == DT_TYPE_RVALUEREF) + type = type->mBase; + + if (tdec->mType == DT_TYPE_STRUCT) + { + Declaration* fexp = tdec->mScope->Lookup(Ident::Unique("(cast)")); + if (fexp) + { + while (fexp && !fexp->mBase->mBase->IsSame(type)) + fexp = fexp->mNext; + if (fexp) + return true; + } + } + + if (type->mType == DT_TYPE_STRUCT) + { + if (!type->IsConstSame(tdec)) + { + Declaration* fcons = type->mScope ? type->mScope->Lookup(type->mIdent->PreMangle("+")) : nullptr; + if (fcons) + { + while (fcons && !(fcons->mBase->mParams && fcons->mBase->mParams->mNext && !fcons->mBase->mParams->mNext->mNext && + (fcons->mBase->mParams->mNext->mBase->mType == DT_TYPE_TEMPLATE || fcons->mBase->mParams->mNext->mBase->CanAssign(tdec)))) + fcons = fcons->mNext; + + if (fcons) + return true; + } + } + } + + return false; +} + +Expression* Parser::CoerceExpression(Expression* exp, Declaration* type) +{ + Declaration* tdec = exp->mDecType; + + while (tdec->mType == DT_TYPE_REFERENCE || tdec->mType == DT_TYPE_RVALUEREF) + tdec = tdec->mBase; + + if (type->mType == DT_TYPE_REFERENCE && type->mBase->mType == DT_TYPE_STRUCT && tdec->mType == DT_TYPE_STRUCT && type->mBase->IsSubType(tdec)) + return exp; + + while (type->mType == DT_TYPE_REFERENCE || type->mType == DT_TYPE_RVALUEREF) + type = type->mBase; + + if (type->mType == DT_TYPE_POINTER && tdec->mType == DT_TYPE_INTEGER && exp->mType == EX_CONSTANT && exp->mDecValue->mInteger == 0) + { + if (mCompilerOptions & COPT_CPLUSPLUS) + mErrors->Error(exp->mLocation, EWARN_NUMERIC_0_USED_AS_NULLPTR, "Numeric 0 used for nullptr"); + Expression* nexp = new Expression(exp->mLocation, EX_CONSTANT); + nexp->mDecType = TheNullPointerTypeDeclaration; + nexp->mDecValue = TheNullptrConstDeclaration; + return nexp; + } + else if (type->mType == DT_TYPE_INTEGER && tdec->mType == DT_TYPE_INTEGER && exp->mType == EX_CONSTANT && (type->mSize != tdec->mSize || (type->mFlags & DTF_SIGNED) != (tdec->mFlags & DTF_SIGNED))) + { + int64 sval = 1ULL << (8 * type->mSize); + int64 v = exp->mDecValue->mInteger; + + if (type->mFlags & DTF_SIGNED) + { + if (v < - (sval >> 1) || v >= (sval >> 1)) + mErrors->Error(exp->mLocation, EWARN_CONSTANT_TRUNCATED, "Integer constant truncated"); + v &= (sval - 1); + if (v & (sval >> 1)) + v -= sval; + } + else + { + if (v < 0 || v >= sval) + mErrors->Error(exp->mLocation, EWARN_CONSTANT_TRUNCATED, "Integer constant truncated"); + v &= (sval - 1); + } + + Expression* ex = new Expression(exp->mLocation, EX_CONSTANT); + Declaration* dec = new Declaration(exp->mLocation, DT_CONST_INTEGER); + dec->mBase = type; + dec->mInteger = v; + ex->mDecValue = dec; + ex->mDecType = type; + return ex; + } + + if (tdec->mType == DT_TYPE_STRUCT) + { + Declaration* fexp = tdec->mScope ? tdec->mScope->Lookup(Ident::Unique("(cast)")) : nullptr; + if (fexp) + { + while (fexp && !fexp->mBase->mBase->IsSame(type)) + fexp = fexp->mNext; + if (!fexp && type->mType == DT_TYPE_INTEGER) + { + fexp = tdec->mScope->Lookup(Ident::Unique("(cast)")); + while (fexp && !fexp->mBase->mBase->IsSame(TheSignedIntTypeDeclaration)) + fexp = fexp->mNext; + } + + if (fexp) + { + Expression* aexp = new Expression(exp->mLocation, EX_PREFIX); + aexp->mToken = TK_BINARY_AND; + aexp->mDecType = tdec->BuildPointer(exp->mLocation); + aexp->mLeft = exp; + + Expression* nexp = new Expression(exp->mLocation, EX_CALL); + nexp->mLeft = new Expression(exp->mLocation, EX_CONSTANT); + nexp->mLeft->mDecType = fexp->mBase; + nexp->mLeft->mDecValue = fexp; + nexp->mRight = aexp; + nexp->mDecType = fexp->mBase->mBase; + + return nexp; + } + } + + if (type->mType == DT_TYPE_POINTER && type->mBase->mType == DT_TYPE_FUNCTION) + { + Declaration* fr = tdec->mScope->Lookup(Ident::Unique("operator()")); + if (fr && !(fr->mBase->mFlags & DTF_FUNC_THIS)) + { + Expression* nexp = new Expression(exp->mLocation, EX_CONSTANT); + nexp->mDecType = fr->mBase; + nexp->mDecValue = fr; + + return nexp; + } + } + } + + if (type->mType == DT_TYPE_STRUCT) + { + if (!type->IsConstSame(tdec)) + { + Declaration* fcons = type->mScope ? type->mScope->Lookup(type->mIdent->PreMangle("+")) : nullptr; + if (fcons) + { +#if 0 + while (fcons && !(fcons->mBase->mParams && fcons->mBase->mParams->mNext && !fcons->mBase->mParams->mNext->mNext && fcons->mBase->mParams->mNext->mBase->CanAssign(tdec))) + fcons = fcons->mNext; + + if (fcons) +#endif + { + Declaration* vdec = AllocTempVar(type->ToMutableType()); +#if 0 + Declaration* vdec = new Declaration(mScanner->mLocation, DT_VARIABLE); + + vdec->mBase = type->ToMutableType(); + vdec->mVarIndex = mLocalIndex++; + vdec->mSize = type->mSize; + vdec->mFlags |= DTF_DEFINED; +#endif + + Expression* vexp = new Expression(mScanner->mLocation, EX_VARIABLE); + vexp->mDecType = vdec->mBase; + vexp->mDecValue = vdec; + + Expression* cexp = new Expression(mScanner->mLocation, EX_CONSTANT); + cexp->mDecValue = fcons; + cexp->mDecType = cexp->mDecValue->mBase; + + Expression* fexp = new Expression(mScanner->mLocation, EX_CALL); + fexp->mLeft = cexp; + fexp->mRight = exp; + + Expression* texp = new Expression(mScanner->mLocation, EX_PREFIX); + texp->mToken = TK_BINARY_AND; + texp->mLeft = vexp; + texp->mDecType = new Declaration(mScanner->mLocation, DT_TYPE_POINTER); + texp->mDecType->mFlags |= DTF_CONST | DTF_DEFINED; + texp->mDecType->mBase = vdec->mBase; + texp->mDecType->mSize = 2; + + Expression* lexp = new Expression(mScanner->mLocation, EX_LIST); + lexp->mLeft = texp; + lexp->mRight = fexp->mRight; + fexp->mRight = lexp; + + fexp = ResolveOverloadCall(fexp); + + Expression* dexp = nullptr; + if (type->mDestructor) + { + Expression* cexp = new Expression(mScanner->mLocation, EX_CONSTANT); + cexp->mDecValue = type->mDestructor; + cexp->mDecType = cexp->mDecValue->mBase; + + dexp = new Expression(mScanner->mLocation, EX_CALL); + dexp->mLeft = cexp; + dexp->mRight = texp; + } + + Expression* nexp = new Expression(mScanner->mLocation, EX_CONSTRUCT); + + nexp->mLeft = new Expression(mScanner->mLocation, EX_LIST); + nexp->mLeft->mLeft = fexp; + nexp->mLeft->mRight = dexp; + + nexp->mRight = vexp; + nexp->mDecType = vexp->mDecType; + + return nexp; + } + } + } + } + +#if 1 + if (type->mType == DT_TYPE_BOOL && tdec->mType == DT_TYPE_INTEGER) + { + Expression* ex; + if (exp->mType == EX_CONSTANT) + { + ex = new Expression(exp->mLocation, EX_CONSTANT); + if (exp->mDecValue->mInteger) + ex->mDecValue = TheTrueConstDeclaration; + else + ex->mDecValue = TheFalseConstDeclaration; + } + else + { + ex = new Expression(exp->mLocation, EX_RELATIONAL); + ex->mToken = TK_NOT_EQUAL; + ex->mLeft = exp; + ex->mRight = new Expression(exp->mLocation, EX_CONSTANT); + ex->mRight->mDecType = TheSignedIntTypeDeclaration; + ex->mRight->mDecValue = TheZeroIntegerConstDeclaration; + } + ex->mDecType = type; + return ex; + } +#endif + + return exp; +} + +void Parser::ExpandFunctionCallTemplate(Expression* exp) +{ + if (exp->mLeft->mDecValue->mTemplate) + { + Declaration* tdec = new Declaration(mScanner->mLocation, DT_TEMPLATE); + tdec->mScope = new DeclarationScope(nullptr, SLEVEL_TEMPLATE); + if (tdec->ResolveTemplate(exp->mRight, exp->mLeft->mDecValue)) + { + exp->mLeft->mDecValue = ParseTemplateExpansion(exp->mLeft->mDecValue->mTemplate, tdec); + exp->mLeft->mDecType = exp->mLeft->mDecValue->mBase; + } + } +} + +void Parser::CompleteFunctionDefaultParams(Expression* exp) +{ + Declaration* fdec = exp->mLeft->mDecValue; + Expression* lexp = exp; + + Declaration* pdec = fdec->mBase->mParams; + Expression* pexp = lexp->mRight; + while (pdec) + { + if (pexp) + { + Expression* exp = pexp; + + if (pexp->mType == EX_LIST) + { + pexp->mLeft = CoerceExpression(pexp->mLeft, pdec->mBase); + lexp = pexp; + pexp = pexp->mRight; + } + else + { + lexp->mRight = CoerceExpression(lexp->mRight, pdec->mBase); + pexp = nullptr; + } + } + else if (pdec->mValue) + { + if (lexp->mRight) + { + Expression* nexp = new Expression(exp->mLocation, EX_LIST); + nexp->mLeft = lexp->mRight; + nexp->mRight = pdec->mValue; + lexp->mRight = nexp; + lexp = nexp; + } + else + lexp->mRight = pdec->mValue; + } + + pdec = pdec->mNext; + } +} + +Expression * Parser::ResolveOverloadCall(Expression* exp, Expression* exp2) +{ + if (exp->mType == EX_CALL && exp->mLeft->mDecValue) + { + if (exp->mRight && FindPackExpression(exp->mRight)) + return exp; + + Declaration* fdec = exp->mLeft->mDecValue; + Declaration* fdec2 = exp2 ? exp2->mLeft->mDecValue : nullptr; + + if (fdec->mType == DT_CONST_FUNCTION && fdec->mNext || fdec2) + { + Declaration* dbest = nullptr; + Expression* pbest = nullptr; + int ibest = NOOVERLOAD, nbest = 0; + + while (fdec) + { + int d = OverloadDistance(fdec, exp->mRight); + if (d == NOOVERLOAD) + ; + else if (d < ibest) + { + dbest = fdec; + pbest = exp->mRight; + ibest = d; + nbest = 1; + } + else if (d == ibest) + nbest++; + fdec = fdec->mNext; + } + + while (fdec2) + { + int d = OverloadDistance(fdec2, exp2->mRight); + if (d == NOOVERLOAD) + ; + else if (d < ibest) + { + dbest = fdec2; + pbest = exp2->mRight; + ibest = d; + nbest = 1; + } + else if (d == ibest) + nbest++; + fdec2 = fdec2->mNext; + } + + if (ibest == NOOVERLOAD) + { +#if _DEBUG + fdec = exp->mLeft->mDecValue; + while (fdec) + { + int d = OverloadDistance(fdec, exp->mRight); + fdec = fdec->mNext; + } +#endif + mErrors->Error(FullLocation(exp->mLocation), ERRO_NO_MATCHING_FUNCTION_CALL, "No matching function call", exp->mLeft->mDecValue->mQualIdent); + } + else if (nbest > 1) + { +#if _DEBUG + fdec = exp->mLeft->mDecValue; + while (fdec) + { + int d = OverloadDistance(fdec, exp->mRight); + fdec = fdec->mNext; + } +#endif + mErrors->Error(FullLocation(exp->mLocation), ERRO_AMBIGUOUS_FUNCTION_CALL, "Ambiguous function call", exp->mLeft->mDecValue->mQualIdent); + } + else + { + assert(dbest); + + exp->mLeft->mDecValue = dbest; + exp->mLeft->mDecType = dbest->mBase; + exp->mDecType = dbest->mBase->mBase; + exp->mRight = pbest; + } + } + + ExpandFunctionCallTemplate(exp); + CompleteFunctionDefaultParams(exp); + } + + return exp; +} + +Expression* Parser::ParseConstruction(Declaration* type) +{ + Token otk = mScanner->mToken == TK_OPEN_PARENTHESIS ? TK_CLOSE_PARENTHESIS : TK_CLOSE_BRACE; + + Expression* pexp = nullptr; + Expression* exp = nullptr; + + Declaration* fcons = type->mScope ? type->mScope->Lookup(type->mIdent->PreMangle("+")) : nullptr; + + mScanner->NextToken(); + if (mScanner->mToken != otk) + { + pexp = ParseListExpression(false, fcons ? fcons->mBase->mParams->mNext : nullptr); + ConsumeToken(otk); + } + else + mScanner->NextToken(); + + if (pexp && pexp->mType != EX_LIST && pexp->mDecType->IsSame(type) && !type->mCopyConstructor) + { + // Simple copy + exp = pexp; + } + else + { + if (fcons) + { + Declaration* tdec = new Declaration(mScanner->mLocation, DT_VARIABLE); + + tdec->mBase = type; + tdec->mVarIndex = mLocalIndex++; + tdec->mSize = type->mSize; + tdec->mFlags |= DTF_DEFINED | DTF_TEMPORARY; + + Expression* vexp = new Expression(mScanner->mLocation, EX_VARIABLE); + vexp->mDecType = type; + vexp->mDecValue = tdec; + + Expression* cexp = new Expression(mScanner->mLocation, EX_CONSTANT); + cexp->mDecValue = fcons; + cexp->mDecType = cexp->mDecValue->mBase; + + Expression* fexp = new Expression(mScanner->mLocation, EX_CALL); + fexp->mLeft = cexp; + + fexp->mRight = pexp; + + Expression* texp = new Expression(mScanner->mLocation, EX_PREFIX); + texp->mToken = TK_BINARY_AND; + texp->mLeft = vexp; + texp->mDecType = new Declaration(mScanner->mLocation, DT_TYPE_POINTER); + texp->mDecType->mFlags |= DTF_CONST | DTF_DEFINED; + texp->mDecType->mBase = type; + texp->mDecType->mSize = 2; + + if (fexp->mRight) + { + Expression* lexp = new Expression(mScanner->mLocation, EX_LIST); + lexp->mLeft = texp; + lexp->mRight = fexp->mRight; + fexp->mRight = lexp; + } + else + fexp->mRight = texp; + + fexp = ResolveOverloadCall(fexp); + + Expression* dexp = nullptr; + if (type->mDestructor) + { + Expression* cexp = new Expression(mScanner->mLocation, EX_CONSTANT); + cexp->mDecValue = type->mDestructor; + cexp->mDecType = cexp->mDecValue->mBase; + + dexp = new Expression(mScanner->mLocation, EX_CALL); + dexp->mLeft = cexp; + dexp->mRight = texp; + } + + Expression* nexp = new Expression(mScanner->mLocation, EX_CONSTRUCT); + + nexp->mLeft = new Expression(mScanner->mLocation, EX_LIST); + nexp->mLeft->mLeft = fexp; + nexp->mLeft->mRight = dexp; + + nexp->mRight = vexp; + nexp->mDecType = vexp->mDecType; + + exp = nexp; + } + else if (pexp) + { + Expression* nexp = new Expression(mScanner->mLocation, EX_TYPECAST); + nexp->mDecType = type; + nexp->mLeft = pexp; + nexp = CheckOperatorOverload(nexp); + exp = nexp->ConstantFold(mErrors, mDataSection); + } + else if (type->IsSimpleType()) + { + Expression* nexp = new Expression(mScanner->mLocation, EX_CONSTANT); + + if (type->IsIntegerType()) + { + nexp->mDecType = type; + nexp->mDecValue = TheZeroIntegerConstDeclaration; + } + else if (type->mType == DT_TYPE_FLOAT) + { + nexp->mDecType = TheFloatTypeDeclaration; + nexp->mDecValue = TheZeroFloatConstDeclaration; + } + else // Pointer + { + nexp->mDecType = TheNullPointerTypeDeclaration; + nexp->mDecValue = TheNullptrConstDeclaration; + } + + exp = nexp; + } + else + { + Declaration* tdec = new Declaration(mScanner->mLocation, DT_VARIABLE); + + tdec->mBase = type; + tdec->mVarIndex = mLocalIndex++; + tdec->mSize = type->mSize; + tdec->mFlags |= DTF_DEFINED | DTF_TEMPORARY; + + Expression* nexp = new Expression(mScanner->mLocation, EX_VARIABLE); + nexp->mDecType = type; + nexp->mDecValue = tdec; + + exp = nexp; + } + } + + return exp; +} + +Expression* Parser::ParsePostfixExpression(bool lhs) +{ + Expression* exp = ParseSimpleExpression(lhs); + + if (exp->mType == EX_LABEL) + return exp; + + for (;;) + { + if (ConsumeTokenIf(TK_COLCOLON)) + { + + } + else if (ConsumeTokenIf(TK_OPEN_BRACKET)) + { + Expression* nexp = new Expression(mScanner->mLocation, EX_INDEX); + nexp->mLeft = exp; + nexp->mRight = ParseExpression(false); + ConsumeToken(TK_CLOSE_BRACKET); + + if (exp->mDecType->ValueType() == DT_TYPE_STRUCT) + { + nexp = CheckOperatorOverload(nexp); + if (nexp->mType == EX_INDEX) + mErrors->Error(mScanner->mLocation, EERR_INVALID_INDEX, "No indexing operator found"); + } + else if (exp->mDecType->NonRefBase()->mType == DT_TYPE_ARRAY || exp->mDecType->NonRefBase()->mType == DT_TYPE_POINTER) + { + if (!nexp->mRight->mDecType->IsIntegerType()) + nexp->mRight = CoerceExpression(nexp->mRight, TheSignedIntTypeDeclaration); + + nexp->mDecType = exp->mDecType->NonRefBase()->mBase; + } + else + mErrors->Error(mScanner->mLocation, EERR_INVALID_INDEX, "Array expected for indexing"); + + if (!nexp->mDecType) + nexp->mDecType = TheVoidTypeDeclaration; + exp = nexp->ConstantFold(mErrors, mDataSection, mCompilationUnits->mLinker); + } + else if (mScanner->mToken == TK_OPEN_BRACE && exp->mType == EX_TYPE) + { + if (exp->mDecType->HasConstructor()) + exp = ParseConstruction(exp->mDecType); + else + exp = ParseCastExpression(exp); + } + else if (mScanner->mToken == TK_OPEN_PARENTHESIS) + { + // Explicit constructor invocation + if (exp->mType == EX_CONSTANT && exp->mDecValue->mType == DT_CONST_FUNCTION && exp->mDecValue->mIdent && exp->mDecValue->mIdent->mString[0] == '+') + { + exp->mType = EX_TYPE; + exp->mDecType = exp->mDecType->mParams->mBase->mBase; + exp->mDecValue = nullptr; + } + + if (exp->mType == EX_TYPE) + { + if (exp->mDecType->mTemplate) + { + mErrors->Error(mScanner->mLocation, EERR_TEMPLATE_PARAMS, "Missing template parameters", exp->mDecType->mQualIdent); + exp->mDecType = TheConstVoidTypeDeclaration; + } + + exp = ParseConstruction(exp->mDecType); + } + else + { + Expression* thisExp = nullptr; + + if (exp->mDecType == nullptr) + ; + else if (exp->mDecType->mType == DT_TYPE_POINTER && exp->mDecType->mBase->mType == DT_TYPE_FUNCTION) + { + } + else if (exp->mDecType->mType == DT_TYPE_FUNCTION) + { + } + else + { + Declaration* tdec = exp->mDecType; + while (tdec->mType == DT_TYPE_REFERENCE || tdec->mType == DT_TYPE_RVALUEREF) + tdec = tdec->mBase; + + if (tdec->mType == DT_TYPE_STRUCT && tdec->mScope) + { + const Ident* opident = Ident::Unique("operator()"); + + Declaration* mdec = tdec->mScope->Lookup(opident); + if (mdec) + { + thisExp = new Expression(exp->mLocation, EX_PREFIX); + thisExp->mToken = TK_BINARY_AND; + thisExp->mLeft = exp; + thisExp->mDecType = tdec->BuildPointer(exp->mLocation); + + exp = new Expression(exp->mLocation, EX_CONSTANT); + exp->mDecValue = mdec; + exp->mDecType = mdec->mBase; + } + else + { + mErrors->Error(mScanner->mLocation, EERR_INCOMPATIBLE_OPERATOR, "Function expected for call"); + exp->mDecType = TheVoidFunctionTypeDeclaration; + } + } + else + { + mErrors->Error(mScanner->mLocation, EERR_INCOMPATIBLE_OPERATOR, "Function expected for call"); + exp->mDecType = TheVoidFunctionTypeDeclaration; + } + + } + + mScanner->NextToken(); + Expression* nexp = new Expression(mScanner->mLocation, EX_CALL); + if (mInlineCall) + nexp->mType = EX_INLINE; + mInlineCall = false; + + nexp->mLeft = exp; + if (mScanner->mToken != TK_CLOSE_PARENTHESIS) + { + nexp->mRight = ParseListExpression(false, exp->mDecType->mParams); + ConsumeToken(TK_CLOSE_PARENTHESIS); + } + else + { + nexp->mRight = nullptr; + mScanner->NextToken(); + } + + bool parentCall = false; + if (exp->mDecType->mFlags & DTF_FUNC_THIS) + { + if (thisExp) + { + if (nexp->mRight) + { + Expression* lexp = new Expression(nexp->mLocation, EX_LIST); + lexp->mLeft = thisExp; + lexp->mRight = nexp->mRight; + nexp->mRight = lexp; + } + else + nexp->mRight = thisExp; + } + else + { + if (mThisPointer && mThisPointer->mType == DT_ARGUMENT) + { + Expression* texp = new Expression(mScanner->mLocation, EX_VARIABLE); + texp->mDecType = mThisPointer->mBase; + texp->mDecValue = mThisPointer; + + if (nexp->mRight) + { + Expression* lexp = new Expression(nexp->mLocation, EX_LIST); + lexp->mLeft = texp; + lexp->mRight = nexp->mRight; + nexp->mRight = lexp; + } + else + nexp->mRight = texp; + + parentCall = true; + } + } + } + + nexp = ResolveOverloadCall(nexp); + Declaration* fdec = nexp->mLeft->mDecType; + if (fdec->mType == DT_TYPE_POINTER) + fdec = fdec->mBase; + + nexp->mDecType = fdec->mBase; + + if (fdec->mFlags & DTF_VIRTUAL) + { + if (parentCall) + exp->mDecType->mFlags |= DTF_STACKCALL; + else + nexp->mType = EX_VCALL; + } + + exp = nexp->ConstantFold(mErrors, mDataSection); + } + } + else if (mScanner->mToken == TK_INC || mScanner->mToken == TK_DEC) + { + Expression* nexp = new Expression(mScanner->mLocation, EX_POSTINCDEC); + nexp->mToken = mScanner->mToken; + nexp->mLeft = exp; + nexp->mDecType = exp->mDecType; + mScanner->NextToken(); + exp = CheckOperatorOverload(nexp); + } + else if (mScanner->mToken == TK_ARROW) + { + mScanner->NextToken(); + if (exp->mDecType->mType != DT_TYPE_ARRAY) + { + while (exp->mDecType->mType != DT_TYPE_POINTER) + { + Expression* dexp = new Expression(mScanner->mLocation, EX_PREFIX); + dexp->mToken = TK_ARROW; + dexp->mDecType = TheVoidPointerTypeDeclaration; + dexp->mLeft = exp; + + Expression* oexp = CheckOperatorOverload(dexp); + if (oexp == dexp) + break; + exp = oexp; + } + } + + if (exp->mDecType->NonRefBase()->mType == DT_TYPE_POINTER || exp->mDecType->NonRefBase()->mType == DT_TYPE_ARRAY) + { + Expression * dexp = new Expression(mScanner->mLocation, EX_PREFIX); + dexp->mToken = TK_MUL; + dexp->mDecType = exp->mDecType->NonRefBase()->mBase; + dexp->mLeft = exp; + + exp = ParseQualify(dexp); +/* + if (dexp->mDecType->mType == DT_TYPE_STRUCT || dexp->mDecType->mType == DT_TYPE_UNION) + { + Expression* nexp = new Expression(mScanner->mLocation, EX_QUALIFY); + nexp->mLeft = dexp; + if (mScanner->mToken == TK_IDENT) + { + Declaration* mdec = dexp->mDecType->mScope->Lookup(mScanner->mTokenIdent); + if (mdec) + { + if (mdec->mType == DT_ELEMENT) + { + nexp->mDecValue = mdec; + nexp->mDecType = mdec->mBase; + if (exp->mDecType->mFlags & DTF_CONST) + nexp->mDecType = nexp->mDecType->ToConstType(); + + exp = nexp->ConstantFold(mErrors); + } + else if (mdec->mType == DT_CONST_FUNCTION) + { + assert(false); + } + } + else + mErrors->Error(mScanner->mLocation, EERR_OBJECT_NOT_FOUND, "Struct member identifier not found", mScanner->mTokenIdent); + mScanner->NextToken(); + } + else + mErrors->Error(mScanner->mLocation, EERR_SYNTAX, "Struct member identifier expected"); + } + else + mErrors->Error(mScanner->mLocation, EERR_INCOMPATIBLE_OPERATOR, "Struct expected"); +*/ + } + else + mErrors->Error(mScanner->mLocation, EERR_INCOMPATIBLE_OPERATOR, "Pointer expected"); + } + else if (mScanner->mToken == TK_DOT) + { + mScanner->NextToken(); + + exp = ParseQualify(exp); + } + else + return exp; + } +} + + +Expression* Parser::ParseNewOperator(void) +{ + Expression* nexp; + + nexp = new Expression(mScanner->mLocation, EX_PREFIX); + nexp->mToken = TK_NEW; + mScanner->NextToken(); + + bool placement = false; + + // Check for placement new + if (ConsumeTokenIf(TK_OPEN_PARENTHESIS)) + { + nexp->mType = EX_TYPECAST; + nexp->mLeft = ParseExpression(false); + + ConsumeToken(TK_CLOSE_PARENTHESIS); + placement = true; + } + + Declaration* dec = ParseBaseTypeDeclaration(0, true); + while (ConsumeTokenIf(TK_MUL)) + dec = dec->BuildPointer(mScanner->mLocation); + + if (dec->mFlags & DTF_PURE_VIRTUAL) + mErrors->Error(dec->mLocation, ERRR_INSTANTIATE_ABSTRACT_CLASS, "Cannot instantiate abstract class", dec->mIdent); + + Declaration* sconst = new Declaration(mScanner->mLocation, DT_CONST_INTEGER); + sconst->mBase = TheUnsignedIntTypeDeclaration; + sconst->mInteger = dec->mSize; + + Expression* sexp = new Expression(mScanner->mLocation, EX_CONSTANT); + sexp->mDecValue = sconst; + sexp->mDecType = TheUnsignedIntTypeDeclaration; + + Declaration* nudec = new Declaration(mScanner->mLocation, DT_CONST_ADDRESS); + nudec->mBase = TheVoidPointerTypeDeclaration; + nudec->mInteger = 0; + Expression* nuexp = new Expression(mScanner->mLocation, EX_CONSTANT); + nuexp->mDecValue = nudec; + nuexp->mDecType = nudec->mBase; + + if (ConsumeTokenIf(TK_OPEN_BRACKET)) + { + Expression* mexp = new Expression(mScanner->mLocation, EX_BINARY); + mexp->mToken = TK_MUL; + mexp->mLeft = sexp; + mexp->mRight = ParseExpression(false); + mexp->mDecType = TheUnsignedIntTypeDeclaration; + ConsumeToken(TK_CLOSE_BRACKET); + + if (!placement) + nexp->mLeft = mexp; + + nexp->mDecType = dec->BuildPointer(mScanner->mLocation); + + if (dec->mVectorConstructor) + { + Declaration* vdec = new Declaration(mScanner->mLocation, DT_VARIABLE); + vdec->mVarIndex = mLocalIndex++; + vdec->mBase = nexp->mDecType; + vdec->mSize = 2; + + Expression* vexp = new Expression(mScanner->mLocation, EX_VARIABLE); + vexp->mDecType = vdec->mBase; + vexp->mDecValue = vdec; + + Expression* iexp = new Expression(mScanner->mLocation, EX_INITIALIZATION); + iexp->mToken = TK_ASSIGN; + iexp->mLeft = vexp; + iexp->mRight = nexp; + iexp->mDecType = nexp->mDecType; + + Expression* csexp = new Expression(mScanner->mLocation, EX_TYPECAST); + csexp->mLeft = vexp; + csexp->mDecType = nexp->mDecType->BuildPointer(mScanner->mLocation); + + Declaration* mdec = dec->mVectorConstructor; + + Expression* pexp = new Expression(mScanner->mLocation, EX_LIST); + pexp->mLeft = vexp; + pexp->mRight = new Expression(mScanner->mLocation, EX_BINARY); + pexp->mRight->mToken = TK_ADD; + pexp->mRight->mDecType = pexp->mLeft->mDecType; + pexp->mRight->mLeft = csexp; + pexp->mRight->mRight = mexp->mRight; + + Expression* cexp = new Expression(mScanner->mLocation, EX_CONSTANT); + cexp->mDecValue = mdec; + cexp->mDecType = cexp->mDecValue->mBase; + + Expression* dexp = new Expression(mScanner->mLocation, EX_CALL); + dexp->mLeft = cexp; + dexp->mRight = pexp; + + Expression* coexp = nexp = new Expression(mScanner->mLocation, EX_CONDITIONAL); + coexp->mLeft = new Expression(mScanner->mLocation, EX_RELATIONAL); + coexp->mLeft->mDecType = TheBoolTypeDeclaration; + coexp->mLeft->mToken = TK_EQUAL; + coexp->mLeft->mLeft = vexp; + coexp->mLeft->mRight = nuexp; + coexp->mRight = new Expression(mScanner->mLocation, EX_LIST); + coexp->mRight->mDecType = vexp->mDecType; + coexp->mRight->mLeft = vexp; + coexp->mRight->mRight = new Expression(mScanner->mLocation, EX_SEQUENCE); + coexp->mRight->mRight->mDecType = vexp->mDecType; + coexp->mRight->mRight->mLeft = dexp; + coexp->mRight->mRight->mRight = vexp; + coexp->mDecType = vexp->mDecType; + + Expression* sexp = new Expression(mScanner->mLocation, EX_SEQUENCE); + sexp->mLeft = iexp; + sexp->mRight = coexp; + sexp->mDecType = coexp->mDecType; + + nexp = sexp; + } + } + else + { + if (!placement) + nexp->mLeft = sexp; + + nexp->mDecType = dec->BuildPointer(mScanner->mLocation); + + if (mScanner->mToken == TK_OPEN_PARENTHESIS || dec->mDefaultConstructor) + { + Declaration* mdec = dec->mDefaultConstructor; + Expression* pexp = nullptr; + + if (ConsumeTokenIf(TK_OPEN_PARENTHESIS)) + { + if (!ConsumeTokenIf(TK_CLOSE_PARENTHESIS)) + { + pexp = ParseListExpression(false); + ConsumeToken(TK_CLOSE_PARENTHESIS); + } + } + + if (dec->mType == DT_TYPE_STRUCT) + { + mdec = dec->mScope ? dec->mScope->Lookup(dec->mIdent->PreMangle("+")) : nullptr; + if (pexp && pexp->mType != EX_LIST) + { + if (pexp->mDecType->IsSame(dec)) + mdec = dec->mCopyConstructor; + else if (pexp->mDecType->mType == DT_TYPE_REFERENCE && pexp->mDecType->mBase->IsConstSame(dec)) + mdec = dec->mCopyConstructor; + else if (pexp->mDecType->mType == DT_TYPE_RVALUEREF && pexp->mDecType->mBase->IsSame(dec)) + { + if (dec->mMoveConstructor) + mdec = dec->mMoveConstructor; + else + mdec = dec->mCopyConstructor; + } + } + } + else + mdec = nullptr; + + if (mdec) + { + Declaration* vdec = new Declaration(mScanner->mLocation, DT_VARIABLE); + vdec->mVarIndex = mLocalIndex++; + vdec->mBase = nexp->mDecType; + vdec->mSize = 2; + + Expression* vexp = new Expression(mScanner->mLocation, EX_VARIABLE); + vexp->mDecType = vdec->mBase; + vexp->mDecValue = vdec; + + Expression* iexp = new Expression(mScanner->mLocation, EX_INITIALIZATION); + iexp->mToken = TK_ASSIGN; + iexp->mLeft = vexp; + iexp->mRight = nexp; + iexp->mDecType = nexp->mDecType; + + Expression* lexp = new Expression(mScanner->mLocation, EX_LIST); + lexp->mLeft = vexp; + lexp->mRight = pexp; + pexp = lexp; + + Expression* cexp = new Expression(mScanner->mLocation, EX_CONSTANT); + cexp->mDecValue = mdec; + cexp->mDecType = cexp->mDecValue->mBase; + + Expression* dexp = new Expression(mScanner->mLocation, EX_CALL); + dexp->mLeft = cexp; + dexp->mRight = pexp; + + dexp = ResolveOverloadCall(dexp); + + Expression* sexp = new Expression(mScanner->mLocation, EX_SEQUENCE); + + sexp->mLeft = dexp; + sexp->mRight = vexp; + sexp->mDecType = vexp->mDecType; + + Expression* coexp = nexp = new Expression(mScanner->mLocation, EX_CONDITIONAL); + coexp->mLeft = new Expression(mScanner->mLocation, EX_RELATIONAL); + coexp->mLeft->mDecType = TheBoolTypeDeclaration; + coexp->mLeft->mToken = TK_EQUAL; + coexp->mLeft->mLeft = vexp; + coexp->mLeft->mRight = nuexp; + coexp->mRight = new Expression(mScanner->mLocation, EX_LIST); + coexp->mRight->mLeft = vexp; + coexp->mRight->mRight = new Expression(mScanner->mLocation, EX_SEQUENCE); + coexp->mRight->mRight->mLeft = dexp; + coexp->mRight->mRight->mRight = vexp; + coexp->mDecType = vexp->mDecType; + + nexp = new Expression(mScanner->mLocation, EX_SEQUENCE); + nexp->mLeft = iexp; + nexp->mRight = coexp; + nexp->mDecType = coexp->mDecType; + } + else if (pexp && pexp->mType != EX_LIST) + { + Expression* dexp = new Expression(mScanner->mLocation, EX_PREFIX); + dexp->mToken = TK_MUL; + dexp->mLeft = nexp; + dexp->mDecType = nexp->mDecType->mBase; + + Expression* iexp = new Expression(mScanner->mLocation, EX_INITIALIZATION); + iexp->mToken = TK_ASSIGN; + iexp->mLeft = dexp; + iexp->mRight = pexp; + iexp->mDecType = nexp->mDecType; + + nexp = new Expression(mScanner->mLocation, EX_PREFIX); + nexp->mToken = TK_BINARY_AND; + nexp->mDecType = iexp->mDecType; + nexp->mLeft = iexp; + } + else if (pexp) + { + mErrors->Error(mScanner->mLocation, ERRO_NO_MATCHING_FUNCTION_CALL, "No matching constructor", dec->mIdent); + } + else if (nexp->mDecType->mBase->IsSimpleType()) + { + Expression* cexp = new Expression(mScanner->mLocation, EX_CONSTANT); + + if (nexp->mDecType->mBase->IsIntegerType()) + { + cexp->mDecType = TheSignedIntTypeDeclaration; + cexp->mDecValue = TheZeroIntegerConstDeclaration; + } + else if (nexp->mDecType->mBase->mType == DT_TYPE_FLOAT) + { + cexp->mDecType = TheFloatTypeDeclaration; + cexp->mDecValue = TheZeroFloatConstDeclaration; + } + else // Pointer + { + cexp->mDecType = TheNullPointerTypeDeclaration; + cexp->mDecValue = TheNullptrConstDeclaration; + } + + Expression* dexp = new Expression(mScanner->mLocation, EX_PREFIX); + dexp->mToken = TK_MUL; + dexp->mLeft = nexp; + dexp->mDecType = nexp->mDecType->mBase; + + Expression* iexp = new Expression(mScanner->mLocation, EX_INITIALIZATION); + iexp->mToken = TK_ASSIGN; + iexp->mLeft = dexp; + iexp->mRight = cexp; + iexp->mDecType = nexp->mDecType; + + nexp = new Expression(mScanner->mLocation, EX_PREFIX); + nexp->mToken = TK_BINARY_AND; + nexp->mDecType = iexp->mDecType; + nexp->mLeft = iexp; + } + } + } + + return nexp; +} + +Expression* Parser::ParsePrefixExpression(bool lhs) +{ + if (mScanner->mToken == TK_SUB || mScanner->mToken == TK_ADD || + mScanner->mToken == TK_BINARY_NOT || mScanner->mToken == TK_LOGICAL_NOT || + mScanner->mToken == TK_MUL || mScanner->mToken == TK_INC || mScanner->mToken == TK_DEC || mScanner->mToken == TK_BINARY_AND || + mScanner->mToken == TK_BANKOF || mScanner->mToken == TK_NEW || mScanner->mToken == TK_DELETE) + { + Expression* nexp; + if (mScanner->mToken == TK_LOGICAL_NOT) + { + mScanner->NextToken(); + nexp = ParsePrefixExpression(false); + nexp = nexp->LogicInvertExpression(); + nexp = CheckOperatorOverload(nexp); + + } + else if (mScanner->mToken == TK_INC || mScanner->mToken == TK_DEC) + { + nexp = new Expression(mScanner->mLocation, EX_PREINCDEC); + nexp->mToken = mScanner->mToken; + mScanner->NextToken(); + nexp->mLeft = ParsePrefixExpression(false);; + nexp->mDecType = nexp->mLeft->mDecType; + nexp = CheckOperatorOverload(nexp); + } + else if (mScanner->mToken == TK_NEW) + { + nexp = ParseNewOperator(); + } + else if (mScanner->mToken == TK_DELETE) + { + bool vdelete = false; + + mScanner->NextToken(); + if (ConsumeTokenIf(TK_OPEN_BRACKET)) + { + ConsumeToken(TK_CLOSE_BRACKET); + vdelete = true; + } + + Declaration* nudec = new Declaration(mScanner->mLocation, DT_CONST_ADDRESS); + nudec->mBase = TheVoidPointerTypeDeclaration; + nudec->mInteger = 0; + Expression* nuexp = new Expression(mScanner->mLocation, EX_CONSTANT); + nuexp->mDecValue = nudec; + nuexp->mDecType = nudec->mBase; + + nexp = new Expression(mScanner->mLocation, EX_PREFIX); + nexp->mToken = TK_DELETE; + nexp->mLeft = ParsePrefixExpression(false); + nexp->mDecType = TheVoidTypeDeclaration; + if (nexp->mLeft->mDecType->mType == DT_TYPE_POINTER) + { + Declaration* dec = nexp->mLeft->mDecType->mBase; + if (dec->mDestructor) + { + Declaration* vdec = new Declaration(mScanner->mLocation, DT_VARIABLE); + vdec->mVarIndex = mLocalIndex++; + vdec->mBase = nexp->mLeft->mDecType; + vdec->mSize = 2; + + Expression* vexp = new Expression(mScanner->mLocation, EX_VARIABLE); + vexp->mDecType = vdec->mBase; + vexp->mDecValue = vdec; + + Expression* iexp = new Expression(mScanner->mLocation, EX_INITIALIZATION); + iexp->mToken = TK_ASSIGN; + iexp->mLeft = vexp; + iexp->mRight = nexp->mLeft; + iexp->mDecType = nexp->mLeft->mDecType; + + Declaration* mdec = dec->mDestructor; + Expression* pexp = vexp; + + if (vdelete) + { + mdec = dec->mVectorDestructor; + + Expression* csexp = new Expression(mScanner->mLocation, EX_TYPECAST); + csexp->mLeft = vexp; + csexp->mDecType = nexp->mLeft->mDecType->BuildPointer(mScanner->mLocation); + + pexp = new Expression(mScanner->mLocation, EX_LIST); + pexp->mLeft = vexp; + pexp->mRight = new Expression(mScanner->mLocation, EX_INDEX); + pexp->mRight->mLeft = csexp; + pexp->mRight->mRight = new Expression(mScanner->mLocation, EX_CONSTANT); + pexp->mRight->mRight->mDecType = TheSignedIntTypeDeclaration; + pexp->mRight->mRight->mDecValue = new Declaration(mScanner->mLocation, DT_CONST_INTEGER); + pexp->mRight->mRight->mDecValue->mBase = TheSignedIntTypeDeclaration; + pexp->mRight->mRight->mDecValue->mInteger = -1; + } + + Expression* cexp = new Expression(mScanner->mLocation, EX_CONSTANT); + cexp->mDecValue = mdec; + cexp->mDecType = cexp->mDecValue->mBase; + + Expression* dexp = new Expression(mScanner->mLocation, EX_CALL); + dexp->mLeft = cexp; + dexp->mRight = pexp; + + if (cexp->mDecValue->mBase->mFlags & DTF_VIRTUAL) + dexp->mType = EX_VCALL; + + Expression* sexp = new Expression(mScanner->mLocation, EX_SEQUENCE); + sexp->mLeft = iexp; + sexp->mRight = new Expression(mScanner->mLocation, EX_IF); + sexp->mRight->mLeft = new Expression(mScanner->mLocation, EX_RELATIONAL); + sexp->mRight->mLeft->mDecType = TheBoolTypeDeclaration; + sexp->mRight->mLeft->mToken = TK_NOT_EQUAL; + sexp->mRight->mLeft->mLeft = vexp; + sexp->mRight->mLeft->mRight = nuexp; + sexp->mRight->mRight = new Expression(mScanner->mLocation, EX_LIST); + sexp->mRight->mRight->mLeft = new Expression(mScanner->mLocation, EX_SEQUENCE); + sexp->mRight->mRight->mLeft->mLeft = dexp; + sexp->mRight->mRight->mLeft->mRight = nexp; + nexp->mLeft = iexp; + + nexp = sexp; + } + } + } + else + { + nexp = new Expression(mScanner->mLocation, EX_PREFIX); + nexp->mToken = mScanner->mToken; + mScanner->NextToken(); + nexp->mLeft = ParsePrefixExpression(false); + Declaration* ttype = nexp->mLeft->mDecType->NonRefBase(); + + if (nexp->mToken == TK_MUL) + { + if (ttype->mType == DT_TYPE_POINTER || ttype->mType == DT_TYPE_ARRAY) + { + // no pointer to function dereferencing + if (ttype->mBase->mType == DT_TYPE_FUNCTION) + return nexp->mLeft; + nexp->mDecType = ttype->mBase; + } + else if ((mCompilerOptions & COPT_CPLUSPLUS) && ttype->mType == DT_TYPE_STRUCT) + { + nexp->mDecType = ttype; + } + else + { + mErrors->Error(nexp->mLocation, EERR_INCOMPATIBLE_OPERATOR, "Pointer or array type expected"); + nexp->mDecType = TheVoidTypeDeclaration; + } + } + else if (nexp->mToken == TK_BINARY_AND) + { + nexp->mDecType = ttype->BuildAddressOfPointer(); + } + else if (nexp->mToken == TK_BANKOF) + { + nexp->mDecType = TheSignedIntTypeDeclaration; + } + else if (nexp->mToken == TK_BINARY_NOT) + { + if (ttype->IsIntegerType() && ttype->mFlags & DTF_SIGNED) + { + if (ttype->mSize == 4) + nexp->mDecType = TheUnsignedLongTypeDeclaration; + else + nexp->mDecType = TheUnsignedIntTypeDeclaration; + } + else + nexp->mDecType = ttype; + } + else + nexp->mDecType = nexp->mLeft->mDecType; + } + nexp = CheckOperatorOverload(nexp); + return nexp->ConstantFold(mErrors, mDataSection, mCompilationUnits->mLinker); + } + else + return ParsePostfixExpression(lhs); +} + +static Declaration* CombinedIntType(Declaration* ld, Declaration* rd) +{ + if (ld->mSize < 2 && rd->mSize < 2) + return TheSignedIntTypeDeclaration; + else if (ld->mSize > rd->mSize) + return ld; + else if (rd->mSize > ld->mSize) + return rd; + else if (!(rd->mFlags & DTF_SIGNED)) + return rd; + else + return ld; +} + +Expression* Parser::ParseMulExpression(bool lhs) +{ + Expression* exp = ParsePrefixExpression(lhs); + + while (mScanner->mToken == TK_MUL || mScanner->mToken == TK_DIV || mScanner->mToken == TK_MOD) + { + Expression* nexp = new Expression(mScanner->mLocation, EX_BINARY); + nexp->mToken = mScanner->mToken; + nexp->mLeft = exp; + mScanner->NextToken(); + + if (ConsumeTokenIf(TK_ELLIPSIS)) + return ParseBinaryFoldExpression(nexp); + + nexp->mRight = ParsePrefixExpression(false); + + if (nexp->mLeft->mDecType->NonRefBase()->mType == DT_TYPE_FLOAT || nexp->mRight->mDecType->NonRefBase()->mType == DT_TYPE_FLOAT) + nexp->mDecType = TheFloatTypeDeclaration; + else if (exp->mDecType->IsIntegerType() && nexp->mRight->mDecType->IsIntegerType()) + nexp->mDecType = CombinedIntType(exp->mDecType, nexp->mRight->mDecType); + else + nexp->mDecType = exp->mDecType; + + exp = CheckOperatorOverload(nexp); + + exp = exp->ConstantFold(mErrors, mDataSection); + } + + return exp; +} + +Expression* Parser::FindPackExpression(Expression* exp) +{ + if (exp->mType == EX_PACK) + return exp; + + if (exp->mLeft) + { + Expression* nexp = FindPackExpression(exp->mLeft); + if (nexp) + return nexp; + } + + if (exp->mRight) + { + Expression* nexp = FindPackExpression(exp->mRight); + if (nexp) + return nexp; + } + + return nullptr; +} + +Expression* Parser::ExpandPackExpression(Expression* exp, Expression* pack, Expression* item) +{ + if (exp == pack) + return item; + + Expression* left = exp->mLeft ? ExpandPackExpression(exp->mLeft, pack, item) : nullptr; + Expression* right = exp->mRight ? ExpandPackExpression(exp->mRight, pack, item) : nullptr; + + if (left == exp->mLeft && right == exp->mRight) + return exp; + + Expression* nexp = new Expression(exp->mLocation, exp->mType); + nexp->mToken = exp->mToken; + nexp->mLeft = left; + nexp->mRight = right; + nexp->mDecValue = exp->mDecValue; + nexp->mDecType = exp->mDecType; + + if (nexp->mType == EX_CALL) + nexp = ResolveOverloadCall(nexp); + + return nexp; +} + +Declaration* Parser::OperatorResultType(Expression* exp) +{ + if (exp->mType == EX_BINARY) + { + Declaration* lt = exp->mLeft->mDecType->NonRefBase(); + Declaration* rt = exp->mRight->mDecType->NonRefBase(); + + switch (exp->mToken) + { + case TK_MUL: + case TK_DIV: + case TK_MOD: + if (lt->mType == DT_TYPE_FLOAT || rt->mType == DT_TYPE_FLOAT) + return TheFloatTypeDeclaration; + else + return lt; + + case TK_ADD: + case TK_SUB: + if (lt->IsIndexed() && rt->IsIntegerType()) + return lt->BuildArrayPointer(); + else if (rt->IsIndexed() && lt->IsIntegerType()) + return rt->BuildArrayPointer(); + else if (lt->mType == DT_TYPE_FLOAT || rt->mType == DT_TYPE_FLOAT) + return TheFloatTypeDeclaration; + else if (lt->IsIndexed() && rt->IsIndexed()) + return TheSignedIntTypeDeclaration; + else + return lt; + + case TK_LEFT_SHIFT: + case TK_RIGHT_SHIFT: + case TK_BINARY_AND: + case TK_BINARY_OR: + case TK_BINARY_XOR: + return lt; + } + } + else if (exp->mType == EX_RELATIONAL || exp->mType == EX_LOGICAL_AND || exp->mType == EX_LOGICAL_OR || exp->mType == EX_LOGICAL_NOT) + return TheBoolTypeDeclaration; + else if (exp->mType == EX_LIST) + return exp->mRight->mDecType; + + return exp->mLeft->mDecType; +} + +Expression* Parser::ParseBinaryFoldExpression(Expression * exp) +{ + if (ConsumeTokenIf(exp->mToken)) + exp->mRight = ParsePrefixExpression(false); + + if (exp->mLeft) + { + Expression* pexp = FindPackExpression(exp->mLeft); + if (pexp) + { + Declaration* dpack = pexp->mDecValue->mParams; + Expression* nexp = exp->mRight; + if (dpack) + { + dpack = dpack->Last(); + if (!nexp) + { + Expression* vexp = new Expression(exp->mLocation, EX_VARIABLE); + vexp->mDecType = dpack->mBase; + vexp->mDecValue = dpack; + nexp = ExpandPackExpression(exp->mLeft, pexp, vexp); + dpack = dpack->mPrev; + } + + while (dpack) + { + Expression* vexp = new Expression(exp->mLocation, EX_VARIABLE); + vexp->mDecType = dpack->mBase; + vexp->mDecValue = dpack; + + Expression* oexp = new Expression(exp->mLocation, exp->mType); + oexp->mToken = exp->mToken; + oexp->mLeft = ExpandPackExpression(exp->mLeft, pexp, vexp); + oexp->mRight = nexp; + oexp->mDecType = OperatorResultType(oexp); + + nexp = CheckOperatorOverload(oexp); + + nexp = nexp->ConstantFold(mErrors, mDataSection); + + dpack = dpack->mPrev; + } + } + + return nexp; + } + } + + if (exp->mRight) + { + Expression* pexp = FindPackExpression(exp->mRight); + if (pexp) + { + Declaration* dpack = pexp->mDecValue->mParams; + Expression* nexp = exp->mLeft; + if (dpack) + { + if (!nexp) + { + Expression* vexp = new Expression(exp->mLocation, EX_VARIABLE); + vexp->mDecType = dpack->mBase; + vexp->mDecValue = dpack; + nexp = ExpandPackExpression(exp->mRight, pexp, vexp); + dpack = dpack->mNext; + } + + while (dpack) + { + Expression* vexp = new Expression(exp->mLocation, EX_VARIABLE); + vexp->mDecType = dpack->mBase; + vexp->mDecValue = dpack; + + Expression* oexp = new Expression(exp->mLocation, exp->mType); + oexp->mToken = exp->mToken; + oexp->mLeft = nexp; + oexp->mRight = ExpandPackExpression(exp->mRight, pexp, vexp); + oexp->mDecType = OperatorResultType(oexp); + + nexp = CheckOperatorOverload(oexp); + + nexp = nexp->ConstantFold(mErrors, mDataSection); + + dpack = dpack->mNext; + } + } + + return nexp; + } + } + + mErrors->Error(exp->mLocation, EERR_INVALID_FOLD_EXPRESSION, "No parameter pack"); + return exp; +} + +Expression* Parser::ParseAddExpression(bool lhs) +{ + Expression* exp = ParseMulExpression(lhs); + + while (mScanner->mToken == TK_ADD || mScanner->mToken == TK_SUB) + { + Expression * nexp = new Expression(mScanner->mLocation, EX_BINARY); + nexp->mToken = mScanner->mToken; + nexp->mLeft = exp; + mScanner->NextToken(); + + if (ConsumeTokenIf(TK_ELLIPSIS)) + return ParseBinaryFoldExpression(nexp); + + nexp->mRight = ParseMulExpression(false); + if (nexp->mLeft->mDecType->mType == DT_TYPE_POINTER && nexp->mRight->mDecType->IsIntegerType()) + { + if (nexp->mLeft->mDecType->mBase->mType == DT_TYPE_VOID) + { + if (mCompilerOptions & COPT_CPLUSPLUS) + mErrors->Error(mScanner->mLocation, ERRR_INVALID_VOID_POINTER_ARITHMETIC, "Invalid arithmetic on void pointer"); + else + mErrors->Error(mScanner->mLocation, EWARN_INVALID_VOID_POINTER_ARITHMETIC, "Invalid arithmetic on void pointer"); + } + nexp->mDecType = nexp->mLeft->mDecType; + } + else if (nexp->mRight->mDecType->mType == DT_TYPE_POINTER && nexp->mLeft->mDecType->IsIntegerType()) + { + if (nexp->mRight->mDecType->mBase->mType == DT_TYPE_VOID) + { + if (mCompilerOptions & COPT_CPLUSPLUS) + mErrors->Error(mScanner->mLocation, ERRR_INVALID_VOID_POINTER_ARITHMETIC, "Invalid arithmetic on void pointer"); + else + mErrors->Error(mScanner->mLocation, EWARN_INVALID_VOID_POINTER_ARITHMETIC, "Invalid arithmetic on void pointer"); + } + nexp->mDecType = nexp->mRight->mDecType; + } + else if (nexp->mLeft->mDecType->mType == DT_TYPE_ARRAY && nexp->mRight->mDecType->IsIntegerType()) + { + Declaration* dec = new Declaration(nexp->mLocation, DT_TYPE_POINTER); + dec->mSize = 2; + dec->mBase = nexp->mLeft->mDecType->mBase; + dec->mStride = nexp->mLeft->mDecType->mStride; + dec->mStripe = nexp->mLeft->mDecType->mStripe; + dec->mFlags |= DTF_DEFINED; + nexp->mDecType = dec; + } + else if (nexp->mRight->mDecType->mType == DT_TYPE_ARRAY && nexp->mLeft->mDecType->IsIntegerType()) + { + Declaration* dec = new Declaration(nexp->mLocation, DT_TYPE_POINTER); + dec->mSize = 2; + dec->mBase = nexp->mRight->mDecType->mBase; + dec->mStride = nexp->mRight->mDecType->mStride; + dec->mStripe = nexp->mRight->mDecType->mStripe; + dec->mFlags |= DTF_DEFINED; + nexp->mDecType = dec; + } + else if (nexp->mLeft->mDecType->mType == DT_TYPE_FLOAT || nexp->mRight->mDecType->mType == DT_TYPE_FLOAT) + nexp->mDecType = TheFloatTypeDeclaration; + else if (nexp->mToken == TK_SUB && + (nexp->mLeft->mDecType->mType == DT_TYPE_POINTER || nexp->mLeft->mDecType->mType == DT_TYPE_ARRAY) && + (nexp->mRight->mDecType->mType == DT_TYPE_POINTER || nexp->mRight->mDecType->mType == DT_TYPE_ARRAY)) + nexp->mDecType = TheSignedIntTypeDeclaration; + else if (exp->mDecType->IsIntegerType() && nexp->mRight->mDecType->IsIntegerType()) + nexp->mDecType = CombinedIntType(exp->mDecType, nexp->mRight->mDecType); + else + nexp->mDecType = exp->mDecType; + + exp = CheckOperatorOverload(nexp); + + exp = exp->ConstantFold(mErrors, mDataSection); + } + + return exp; +} + +Expression* Parser::ParseShiftExpression(bool lhs) +{ + Expression* exp = ParseAddExpression(lhs); + + while (mScanner->mToken == TK_LEFT_SHIFT || mScanner->mToken == TK_RIGHT_SHIFT) + { + Expression* nexp = new Expression(mScanner->mLocation, EX_BINARY); + nexp->mToken = mScanner->mToken; + nexp->mLeft = exp; + mScanner->NextToken(); + + if (ConsumeTokenIf(TK_ELLIPSIS)) + return ParseBinaryFoldExpression(nexp); + + nexp->mRight = ParseAddExpression(false); + nexp->mDecType = exp->mDecType; + + exp = nexp->ConstantFold(mErrors, mDataSection); + + exp = CheckOperatorOverload(exp); + } + + return exp; +} + +Expression* Parser::ParseRelationalExpression(bool lhs) +{ + Expression* exp = ParseShiftExpression(lhs); + + while (mScanner->mToken >= TK_EQUAL && mScanner->mToken <= TK_LESS_EQUAL) + { + Expression* nexp = new Expression(mScanner->mLocation, EX_RELATIONAL); + nexp->mToken = mScanner->mToken; + nexp->mLeft = exp; + mScanner->NextToken(); + + if (ConsumeTokenIf(TK_ELLIPSIS)) + return ParseBinaryFoldExpression(nexp); + + nexp->mRight = ParseShiftExpression(false); + nexp->mDecType = TheBoolTypeDeclaration; + + exp = nexp->ConstantFold(mErrors, mDataSection); + + exp = CheckOperatorOverload(exp); + } + + return exp; +} + +Expression* Parser::ParseBinaryAndExpression(bool lhs) +{ + Expression* exp = ParseRelationalExpression(lhs); + + while (mScanner->mToken == TK_BINARY_AND) + { + Expression* nexp = new Expression(mScanner->mLocation, EX_BINARY); + nexp->mToken = mScanner->mToken; + nexp->mLeft = exp; + mScanner->NextToken(); + nexp->mRight = ParseRelationalExpression(false); + nexp->mDecType = exp->mDecType; + + exp = nexp->ConstantFold(mErrors, mDataSection); + + exp = CheckOperatorOverload(exp); + } + + return exp; +} + +Expression* Parser::ParseBinaryXorExpression(bool lhs) +{ + Expression* exp = ParseBinaryAndExpression(lhs); + + while (mScanner->mToken == TK_BINARY_XOR) + { + Expression* nexp = new Expression(mScanner->mLocation, EX_BINARY); + nexp->mToken = mScanner->mToken; + nexp->mLeft = exp; + mScanner->NextToken(); + + if (ConsumeTokenIf(TK_ELLIPSIS)) + return ParseBinaryFoldExpression(nexp); + + nexp->mRight = ParseBinaryAndExpression(false); + nexp->mDecType = exp->mDecType; + exp = nexp->ConstantFold(mErrors, mDataSection); + + exp = CheckOperatorOverload(exp); + } + + return exp; +} + +Expression* Parser::ParseBinaryOrExpression(bool lhs) +{ + Expression* exp = ParseBinaryXorExpression(lhs); + + while (mScanner->mToken == TK_BINARY_OR) + { + Expression* nexp = new Expression(mScanner->mLocation, EX_BINARY); + nexp->mToken = mScanner->mToken; + nexp->mLeft = exp; + mScanner->NextToken(); + + if (ConsumeTokenIf(TK_ELLIPSIS)) + return ParseBinaryFoldExpression(nexp); + + nexp->mRight = ParseBinaryXorExpression(false); + nexp->mDecType = exp->mDecType; + exp = nexp->ConstantFold(mErrors, mDataSection); + + exp = CheckOperatorOverload(exp); + } + + return exp; +} + +Expression* Parser::ParseLogicAndExpression(bool lhs) +{ + Expression* exp = ParseBinaryOrExpression(lhs); + + while (mScanner->mToken == TK_LOGICAL_AND) + { + Expression* nexp = new Expression(mScanner->mLocation, EX_LOGICAL_AND); + nexp->mToken = mScanner->mToken; + nexp->mLeft = CoerceExpression(exp, TheBoolTypeDeclaration); + mScanner->NextToken(); + + if (ConsumeTokenIf(TK_ELLIPSIS)) + return ParseBinaryFoldExpression(nexp); + + nexp->mRight = CoerceExpression(ParseBinaryOrExpression(false), TheBoolTypeDeclaration); + nexp->mDecType = TheBoolTypeDeclaration; + exp = nexp->ConstantFold(mErrors, mDataSection); + } + + return exp; +} + +Expression* Parser::ParseLogicOrExpression(bool lhs) +{ + Expression* exp = ParseLogicAndExpression(lhs); + + while (mScanner->mToken == TK_LOGICAL_OR) + { + Expression* nexp = new Expression(mScanner->mLocation, EX_LOGICAL_OR); + nexp->mToken = mScanner->mToken; + nexp->mLeft = CoerceExpression(exp, TheBoolTypeDeclaration); + mScanner->NextToken(); + + if (ConsumeTokenIf(TK_ELLIPSIS)) + return ParseBinaryFoldExpression(nexp); + + nexp->mRight = CoerceExpression(ParseLogicAndExpression(false), TheBoolTypeDeclaration); + nexp->mDecType = TheBoolTypeDeclaration; + exp = nexp->ConstantFold(mErrors, mDataSection); + } + + return exp; +} + +Expression* Parser::ParseConditionalExpression(bool lhs) +{ + Expression* exp = ParseLogicOrExpression(lhs); + + if (mScanner->mToken == TK_QUESTIONMARK) + { + Expression* nexp = new Expression(mScanner->mLocation, EX_CONDITIONAL); + nexp->mLeft = CoerceExpression(exp, TheBoolTypeDeclaration); + mScanner->NextToken(); + Expression* texp = new Expression(mScanner->mLocation, EX_SEQUENCE); + nexp->mRight = texp; + + texp->mLeft = ParseLogicOrExpression(false); + ConsumeToken(TK_COLON); + texp->mRight = ParseConditionalExpression(false); + + texp->mDecType = texp->mLeft->mDecType; + nexp->mDecType = texp->mLeft->mDecType; + exp = nexp->ConstantFold(mErrors, mDataSection); + } + + return exp; +} + +Expression* Parser::ParseRExpression(void) +{ + return ParseConditionalExpression(false)->ConstantFold(mErrors, mDataSection)->ConstantDereference(mErrors, mDataSection); +} + +Expression* Parser::ParseParenthesisExpression(void) +{ + if (mScanner->mToken == TK_OPEN_PARENTHESIS) + mScanner->NextToken(); + else + mErrors->Error(mScanner->mLocation, EERR_SYNTAX, "'(' expected"); + + Expression* exp = ParseExpression(true); + + if (mScanner->mToken == TK_CLOSE_PARENTHESIS) + mScanner->NextToken(); + else + mErrors->Error(mScanner->mLocation, EERR_SYNTAX, "')' expected"); + + return exp; +} + +void Parser::ParseStaticAssert(void) +{ + if (ConsumeToken(TK_OPEN_PARENTHESIS)) + { + Expression* exp = ParseExpression(false); + exp = exp->ConstantFold(mErrors, mDataSection)->ConstantDereference(mErrors, mDataSection); + if (exp->mType == EX_CONSTANT && exp->mDecType->IsIntegerType() && exp->mDecValue->mInteger) + { + if (ConsumeTokenIf(TK_COMMA)) + mScanner->NextToken(); + } + else + { + if (ConsumeTokenIf(TK_COMMA) && mScanner->mToken == TK_STRING) + { + mErrors->Error(mScanner->mLocation, EERR_STATIC_ASSERT, "static assertion failed: ", (const char*)mScanner->mTokenString); + mScanner->NextToken(); + } + else + mErrors->Error(mScanner->mLocation, EERR_STATIC_ASSERT, "static assertion failed"); + } + + ConsumeToken(TK_CLOSE_PARENTHESIS); + } +} + +Expression* Parser::CheckOperatorOverload(Expression* exp) +{ + if (mCompilerOptions & COPT_CPLUSPLUS) + { + if (!exp->mLeft || !exp->mLeft->mDecType) + ; + else if (exp->mType == EX_ASSIGNMENT) + { + Declaration* tdec = exp->mLeft->mDecType; + while (tdec->mType == DT_TYPE_REFERENCE || tdec->mType == DT_TYPE_RVALUEREF) + tdec = tdec->mBase; + + if (tdec->mType == DT_TYPE_STRUCT) + { + const Ident* opident = nullptr; + switch (exp->mToken) + { + case TK_ASSIGN: + opident = Ident::Unique("operator="); + break; + case TK_ASSIGN_ADD: + opident = Ident::Unique("operator+="); + break; + case TK_ASSIGN_SUB: + opident = Ident::Unique("operator-="); + break; + case TK_ASSIGN_MUL: + opident = Ident::Unique("operator*="); + break; + case TK_ASSIGN_DIV: + opident = Ident::Unique("operator/="); + break; + case TK_ASSIGN_MOD: + opident = Ident::Unique("operator%="); + break; + case TK_ASSIGN_SHL: + opident = Ident::Unique("operator<<="); + break; + case TK_ASSIGN_SHR: + opident = Ident::Unique("operator>>="); + break; + case TK_ASSIGN_AND: + opident = Ident::Unique("operator&="); + break; + case TK_ASSIGN_XOR: + opident = Ident::Unique("operator^="); + break; + case TK_ASSIGN_OR: + opident = Ident::Unique("operator|="); + break; + } + + if (opident) + { + Expression* nexp2 = nullptr; + + Declaration* mdec2 = mScope->Lookup(opident); + if (mdec2) + { + nexp2 = new Expression(mScanner->mLocation, EX_CALL); + + nexp2->mLeft = new Expression(mScanner->mLocation, EX_CONSTANT); + nexp2->mLeft->mDecType = mdec2->mBase; + nexp2->mLeft->mDecValue = mdec2; + + nexp2->mDecType = mdec2->mBase; + nexp2->mRight = new Expression(mScanner->mLocation, EX_LIST); + nexp2->mRight->mLeft = exp->mLeft; + nexp2->mRight->mRight = exp->mRight; + } + + Declaration* mdec = tdec->mScope ? tdec->mScope->Lookup(opident) : nullptr; + if (mdec) + { + Expression * nexp = new Expression(mScanner->mLocation, EX_CALL); + + nexp->mLeft = new Expression(mScanner->mLocation, EX_CONSTANT); + nexp->mLeft->mDecType = mdec->mBase; + nexp->mLeft->mDecValue = mdec; + + nexp->mDecType = mdec->mBase; + nexp->mRight = exp->mRight; + + Expression* texp = new Expression(nexp->mLocation, EX_PREFIX); + texp->mToken = TK_BINARY_AND; + texp->mLeft = exp->mLeft; + texp->mDecType = new Declaration(nexp->mLocation, DT_TYPE_POINTER); + texp->mDecType->mFlags |= DTF_CONST | DTF_DEFINED; + texp->mDecType->mBase = tdec; + texp->mDecType->mSize = 2; + + Expression* lexp = new Expression(nexp->mLocation, EX_LIST); + lexp->mLeft = texp->ConstantFold(mErrors, mDataSection); + lexp->mRight = nexp->mRight; + nexp->mRight = lexp; + + nexp = ResolveOverloadCall(nexp, nexp2); + nexp->mDecType = nexp->mLeft->mDecType->mBase; + + exp = nexp; + } + } + } + } + else if (exp->mType == EX_INITIALIZATION) + { + Declaration* tdec = exp->mLeft->mDecType; + if (tdec->mType == DT_TYPE_STRUCT && exp->mToken == TK_ASSIGN) + { + Declaration* fcons = tdec->mScope ? tdec->mScope->Lookup(tdec->mIdent->PreMangle("+"), SLEVEL_CLASS) : nullptr; + if (fcons) + { + Declaration* mtype = tdec->ToMutableType(); + + Expression* vexp = exp->mLeft; + + Expression* cexp = new Expression(mScanner->mLocation, EX_CONSTANT); + cexp->mDecValue = fcons; + cexp->mDecType = cexp->mDecValue->mBase; + + Expression* fexp = new Expression(mScanner->mLocation, EX_CALL); + fexp->mLeft = cexp; + fexp->mRight = exp->mRight; + + Expression* texp = new Expression(mScanner->mLocation, EX_PREFIX); + texp->mToken = TK_BINARY_AND; + texp->mLeft = vexp; + texp->mDecType = new Declaration(mScanner->mLocation, DT_TYPE_POINTER); + texp->mDecType->mFlags |= DTF_CONST | DTF_DEFINED; + texp->mDecType->mBase = mtype; + texp->mDecType->mSize = 2; + + if (fexp->mRight) + { + Expression* lexp = new Expression(mScanner->mLocation, EX_LIST); + lexp->mLeft = texp; + lexp->mRight = fexp->mRight; + fexp->mRight = lexp; + } + else + fexp->mRight = texp; + + fexp = ResolveOverloadCall(fexp); + + Expression* dexp = nullptr; + if (tdec->mDestructor) + { + Expression* cexp = new Expression(mScanner->mLocation, EX_CONSTANT); + cexp->mDecValue = tdec->mDestructor; + cexp->mDecType = cexp->mDecValue->mBase; + + dexp = new Expression(mScanner->mLocation, EX_CALL); + dexp->mLeft = cexp; + dexp->mRight = texp; + } + + Expression* nexp = new Expression(mScanner->mLocation, EX_CONSTRUCT); + + nexp->mLeft = new Expression(mScanner->mLocation, EX_LIST); + nexp->mLeft->mLeft = fexp; + nexp->mLeft->mRight = dexp; + + nexp->mRight = vexp; + nexp->mDecType = vexp->mDecType; + + exp = nexp; + } + } + } + else if (exp->mType == EX_INDEX) + { + Declaration* tdec = exp->mLeft->mDecType; + while (tdec->mType == DT_TYPE_REFERENCE || tdec->mType == DT_TYPE_RVALUEREF) + tdec = tdec->mBase; + + if (tdec->mType == DT_TYPE_STRUCT && tdec->mScope) + { + const Ident* opident = Ident::Unique("operator[]"); + + Declaration* mdec = tdec->mScope->Lookup(opident); + if (mdec) + { + Expression* nexp = new Expression(mScanner->mLocation, EX_CALL); + + nexp->mLeft = new Expression(mScanner->mLocation, EX_CONSTANT); + nexp->mLeft->mDecType = mdec->mBase; + nexp->mLeft->mDecValue = mdec; + + nexp->mDecType = mdec->mBase; + nexp->mRight = exp->mRight; + + Expression* texp = new Expression(nexp->mLocation, EX_PREFIX); + texp->mToken = TK_BINARY_AND; + texp->mLeft = exp->mLeft; + texp->mDecType = new Declaration(nexp->mLocation, DT_TYPE_POINTER); + texp->mDecType->mFlags |= DTF_CONST | DTF_DEFINED; + texp->mDecType->mBase = tdec; + texp->mDecType->mSize = 2; + + Expression* lexp = new Expression(nexp->mLocation, EX_LIST); + lexp->mLeft = texp->ConstantFold(mErrors, mDataSection); + lexp->mRight = nexp->mRight; + nexp->mRight = lexp; + + nexp = ResolveOverloadCall(nexp); + nexp->mDecType = nexp->mLeft->mDecType->mBase; + + exp = nexp; + } + } + } + else if (exp->mType == EX_PREINCDEC) + { + const Ident* opident = nullptr; + switch (exp->mToken) + { + case TK_INC: + opident = Ident::Unique("operator++"); + break; + case TK_DEC: + opident = Ident::Unique("operator--"); + break; + } + + if (opident) + { + Declaration* tdec = exp->mLeft->mDecType; + if (tdec->mType == DT_TYPE_STRUCT && tdec->mScope) + { + Declaration* mdec = tdec->mScope->Lookup(opident); + if (mdec) + { + Expression* nexp = new Expression(mScanner->mLocation, EX_CALL); + + nexp->mLeft = new Expression(mScanner->mLocation, EX_CONSTANT); + nexp->mLeft->mDecType = mdec->mBase; + nexp->mLeft->mDecValue = mdec; + + nexp->mDecType = mdec->mBase; + nexp->mRight = exp->mRight; + + Expression* texp = new Expression(nexp->mLocation, EX_PREFIX); + texp->mToken = TK_BINARY_AND; + texp->mLeft = exp->mLeft; + texp->mDecType = new Declaration(nexp->mLocation, DT_TYPE_POINTER); + texp->mDecType->mFlags |= DTF_CONST | DTF_DEFINED; + texp->mDecType->mBase = exp->mLeft->mDecType; + texp->mDecType->mSize = 2; + + nexp->mRight = texp->ConstantFold(mErrors, mDataSection); + + nexp = ResolveOverloadCall(nexp); + nexp->mDecType = nexp->mLeft->mDecType->mBase; + + exp = nexp; + } + } + } + } + else if (exp->mType == EX_POSTINCDEC) + { + const Ident* opident = nullptr; + switch (exp->mToken) + { + case TK_INC: + opident = Ident::Unique("operator++"); + break; + case TK_DEC: + opident = Ident::Unique("operator--"); + break; + } + + if (opident) + { + Declaration* tdec = exp->mLeft->mDecType; + if (tdec->mType == DT_TYPE_STRUCT) + { + Declaration* mdec = tdec->mScope->Lookup(opident); + if (mdec) + { + Expression* nexp = new Expression(mScanner->mLocation, EX_CALL); + + nexp->mLeft = new Expression(mScanner->mLocation, EX_CONSTANT); + nexp->mLeft->mDecType = mdec->mBase; + nexp->mLeft->mDecValue = mdec; + + nexp->mDecType = mdec->mBase; + nexp->mRight = exp->mRight; + + Expression* texp = new Expression(nexp->mLocation, EX_PREFIX); + texp->mToken = TK_BINARY_AND; + texp->mLeft = exp->mLeft; + texp->mDecType = new Declaration(nexp->mLocation, DT_TYPE_POINTER); + texp->mDecType->mFlags |= DTF_CONST | DTF_DEFINED; + texp->mDecType->mBase = exp->mLeft->mDecType; + texp->mDecType->mSize = 2; + + Expression* lexp = new Expression(nexp->mLocation, EX_LIST); + lexp->mLeft = texp->ConstantFold(mErrors, mDataSection); + lexp->mRight = new Expression(nexp->mLocation, EX_CONSTANT); + lexp->mRight->mDecType = TheSignedIntTypeDeclaration; + lexp->mRight->mDecValue = new Declaration(nexp->mLocation, DT_CONST_INTEGER); + lexp->mRight->mDecValue->mBase = TheSignedIntTypeDeclaration; + nexp->mRight = lexp; + + nexp = ResolveOverloadCall(nexp); + nexp->mDecType = nexp->mLeft->mDecType->mBase; + + exp = nexp; + } + } + } + } + else if (exp->mType == EX_BINARY || exp->mType == EX_RELATIONAL) + { + const Ident* opident = nullptr; + switch (exp->mToken) + { + case TK_ADD: + opident = Ident::Unique("operator+"); + break; + case TK_SUB: + opident = Ident::Unique("operator-"); + break; + case TK_MUL: + opident = Ident::Unique("operator*"); + break; + case TK_DIV: + opident = Ident::Unique("operator/"); + break; + case TK_MOD: + opident = Ident::Unique("operator%"); + break; + case TK_BINARY_AND: + opident = Ident::Unique("operator&"); + break; + case TK_BINARY_OR: + opident = Ident::Unique("operator|"); + break; + case TK_BINARY_XOR: + opident = Ident::Unique("operator^"); + break; + case TK_LEFT_SHIFT: + opident = Ident::Unique("operator<<"); + break; + case TK_RIGHT_SHIFT: + opident = Ident::Unique("operator>>"); + break; + case TK_EQUAL: + opident = Ident::Unique("operator=="); + break; + case TK_NOT_EQUAL: + opident = Ident::Unique("operator!="); + break; + case TK_GREATER_THAN: + opident = Ident::Unique("operator>"); + break; + case TK_GREATER_EQUAL: + opident = Ident::Unique("operator>="); + break; + case TK_LESS_THAN: + opident = Ident::Unique("operator<"); + break; + case TK_LESS_EQUAL: + opident = Ident::Unique("operator<="); + break; + } + + if (opident) + { + Expression* nexp2 = nullptr; + + Declaration* mdec2 = mScope->Lookup(opident); + if (mdec2) + { + nexp2 = new Expression(mScanner->mLocation, EX_CALL); + + nexp2->mLeft = new Expression(mScanner->mLocation, EX_CONSTANT); + nexp2->mLeft->mDecType = mdec2->mBase; + nexp2->mLeft->mDecValue = mdec2; + + nexp2->mDecType = mdec2->mBase; + nexp2->mRight = new Expression(mScanner->mLocation, EX_LIST); + nexp2->mRight->mLeft = exp->mLeft; + nexp2->mRight->mRight = exp->mRight; + } + + Declaration* tdec = exp->mLeft->mDecType; + while (tdec->mType == DT_TYPE_REFERENCE || tdec->mType == DT_TYPE_RVALUEREF) + tdec = tdec->mBase; + Declaration* rtdec = exp->mRight->mDecType; + while (rtdec->mType == DT_TYPE_REFERENCE || rtdec->mType == DT_TYPE_RVALUEREF) + rtdec = rtdec->mBase; + + if (tdec->mType == DT_TYPE_STRUCT) + { + if (!mdec2) + { + DeclarationScope* scope = tdec->mScope; + while (scope && scope->mLevel == SLEVEL_CLASS) + scope = scope->mParent; + + if (scope) + { + Declaration* mdec2 = scope->Lookup(opident); + if (mdec2) + { + nexp2 = new Expression(mScanner->mLocation, EX_CALL); + + nexp2->mLeft = new Expression(mScanner->mLocation, EX_CONSTANT); + nexp2->mLeft->mDecType = mdec2->mBase; + nexp2->mLeft->mDecValue = mdec2; + + nexp2->mDecType = mdec2->mBase; + nexp2->mRight = new Expression(mScanner->mLocation, EX_LIST); + nexp2->mRight->mLeft = exp->mLeft; + nexp2->mRight->mRight = exp->mRight; + } + } + } + + Declaration* mdec = tdec->mScope->Lookup(opident, SLEVEL_CLASS); + if (mdec) + { + Expression* nexp = new Expression(mScanner->mLocation, EX_CALL); + + nexp->mLeft = new Expression(mScanner->mLocation, EX_CONSTANT); + nexp->mLeft->mDecType = mdec->mBase; + nexp->mLeft->mDecValue = mdec; + + nexp->mDecType = mdec->mBase; + nexp->mRight = exp->mRight; + + Expression* texp = new Expression(nexp->mLocation, EX_PREFIX); + texp->mToken = TK_BINARY_AND; + texp->mLeft = exp->mLeft; + texp->mDecType = new Declaration(nexp->mLocation, DT_TYPE_POINTER); + texp->mDecType->mFlags |= DTF_CONST | DTF_DEFINED; + texp->mDecType->mBase = tdec; + texp->mDecType->mSize = 2; + + Expression* lexp = new Expression(nexp->mLocation, EX_LIST); + lexp->mLeft = texp->ConstantFold(mErrors, mDataSection); + lexp->mRight = nexp->mRight; + nexp->mRight = lexp; + + nexp = ResolveOverloadCall(nexp, nexp2); + nexp->mDecType = nexp->mLeft->mDecType->mBase; + + exp = nexp; + } + else if (nexp2) + { + exp = ResolveOverloadCall(nexp2); + exp->mDecType = exp->mLeft->mDecType->mBase; + } + } + + if (exp->mType != EX_CALL && !nexp2 && (tdec->mType == DT_TYPE_STRUCT || rtdec->mType == DT_TYPE_STRUCT)) + { + if ((exp->mLeft->mDecType->IsIntegerType() || CanCoerceExpression(exp->mLeft, TheSignedIntTypeDeclaration)) && + (exp->mRight->mDecType->IsIntegerType() || CanCoerceExpression(exp->mRight, TheSignedIntTypeDeclaration))) + { + exp->mLeft = CoerceExpression(exp->mLeft, TheSignedIntTypeDeclaration); + exp->mRight = CoerceExpression(exp->mRight, TheSignedIntTypeDeclaration); + if (exp->mType == EX_BINARY) + exp->mDecType = TheSignedIntTypeDeclaration; + } + } + } + } + else if (exp->mType == EX_PREFIX) + { + const Ident* opident = nullptr; + switch (exp->mToken) + { + case TK_ADD: + opident = Ident::Unique("operator+"); + break; + case TK_SUB: + opident = Ident::Unique("operator-"); + break; + case TK_MUL: + opident = Ident::Unique("operator*"); + break; + case TK_BINARY_NOT: + opident = Ident::Unique("operator~"); + break; + case TK_ARROW: + opident = Ident::Unique("operator->"); + break; + } + + if (opident) + { + Declaration* tdec = exp->mLeft->mDecType; + while (tdec->mType == DT_TYPE_REFERENCE || tdec->mType == DT_TYPE_RVALUEREF) + tdec = tdec->mBase; + + if (tdec->mType == DT_TYPE_STRUCT) + { + Declaration* mdec = tdec->mScope->Lookup(opident); + if (mdec) + { + Expression* nexp = new Expression(mScanner->mLocation, EX_CALL); + + nexp->mLeft = new Expression(mScanner->mLocation, EX_CONSTANT); + nexp->mLeft->mDecType = mdec->mBase; + nexp->mLeft->mDecValue = mdec; + + nexp->mDecType = mdec->mBase; + nexp->mRight = exp->mRight; + + Expression* texp = new Expression(nexp->mLocation, EX_PREFIX); + texp->mToken = TK_BINARY_AND; + texp->mLeft = exp->mLeft; + texp->mDecType = new Declaration(nexp->mLocation, DT_TYPE_POINTER); + texp->mDecType->mFlags |= DTF_CONST | DTF_DEFINED; + texp->mDecType->mBase = tdec; + texp->mDecType->mSize = 2; + + nexp->mRight = texp->ConstantFold(mErrors, mDataSection); + + nexp = ResolveOverloadCall(nexp); + nexp->mDecType = nexp->mLeft->mDecType->mBase; + + exp = nexp; + } + } + } + } + else if (exp->mType == EX_TYPECAST) + { + Declaration* tdec = exp->mLeft->mDecType; + if (exp->mLeft->mDecType) + { + while (tdec->mType == DT_TYPE_REFERENCE || tdec->mType == DT_TYPE_RVALUEREF) + tdec = tdec->mBase; + + if (tdec->mType == DT_TYPE_STRUCT) + { + Declaration* mdec = tdec->mScope->Lookup(Ident::Unique("(cast)")); + while (mdec && !mdec->mBase->mBase->IsSame(exp->mDecType)) + mdec = mdec->mNext; + while (!mdec && tdec->mBase && tdec->mBase->mBase) + { + tdec = tdec->mBase->mBase; + mdec = tdec->mScope->Lookup(Ident::Unique("(cast)")); + while (mdec && !mdec->mBase->mBase->IsSame(exp->mDecType)) + mdec = mdec->mNext; + } + + if (mdec) + { + Expression* nexp = new Expression(mScanner->mLocation, EX_CALL); + + nexp->mLeft = new Expression(mScanner->mLocation, EX_CONSTANT); + nexp->mLeft->mDecType = mdec->mBase; + nexp->mLeft->mDecValue = mdec; + + nexp->mDecType = mdec->mBase; + nexp->mRight = exp->mRight; + + Expression* texp = new Expression(nexp->mLocation, EX_PREFIX); + texp->mToken = TK_BINARY_AND; + texp->mLeft = exp->mLeft; + texp->mDecType = new Declaration(nexp->mLocation, DT_TYPE_POINTER); + texp->mDecType->mFlags |= DTF_CONST | DTF_DEFINED; + texp->mDecType->mBase = tdec; + texp->mDecType->mSize = 2; + + nexp->mRight = texp->ConstantFold(mErrors, mDataSection); + + nexp = ResolveOverloadCall(nexp); + nexp->mDecType = nexp->mLeft->mDecType->mBase; + + exp = nexp; + } + } + } + } + else if (exp->mType == EX_LOGICAL_NOT) + { + const Ident* opident = Ident::Unique("operator!"); + Declaration* tdec = exp->mLeft->mDecType; + while (tdec->mType == DT_TYPE_REFERENCE || tdec->mType == DT_TYPE_RVALUEREF) + tdec = tdec->mBase; + + if (tdec->mType == DT_TYPE_STRUCT) + { + Declaration* mdec = tdec->mScope->Lookup(opident); + if (mdec) + { + Expression* nexp = new Expression(mScanner->mLocation, EX_CALL); + + nexp->mLeft = new Expression(mScanner->mLocation, EX_CONSTANT); + nexp->mLeft->mDecType = mdec->mBase; + nexp->mLeft->mDecValue = mdec; + + nexp->mDecType = mdec->mBase; + nexp->mRight = exp->mRight; + + Expression* texp = new Expression(nexp->mLocation, EX_PREFIX); + texp->mToken = TK_BINARY_AND; + texp->mLeft = exp->mLeft; + texp->mDecType = new Declaration(nexp->mLocation, DT_TYPE_POINTER); + texp->mDecType->mFlags |= DTF_CONST | DTF_DEFINED; + texp->mDecType->mBase = tdec; + texp->mDecType->mSize = 2; + + nexp->mRight = texp->ConstantFold(mErrors, mDataSection); + + nexp = ResolveOverloadCall(nexp); + nexp->mDecType = nexp->mLeft->mDecType->mBase; + + exp = nexp; + } + } + } + } + + return exp; +} + +Expression* Parser::ParseAssignmentExpression(bool lhs) +{ + Expression* exp = ParseConditionalExpression(lhs)->ConstantFold(mErrors, mDataSection); + + if (mScanner->mToken >= TK_ASSIGN && mScanner->mToken <= TK_ASSIGN_OR) + { + Expression* nexp = new Expression(mScanner->mLocation, EX_ASSIGNMENT); + nexp->mToken = mScanner->mToken; + nexp->mLeft = exp; + mScanner->NextToken(); + nexp->mRight = ParseAssignmentExpression(false); + nexp->mDecType = exp->mDecType; + + exp = CheckOperatorOverload(nexp); + + assert(exp->mDecType); + } + + if (lhs) + return exp; + else + return exp->ConstantDereference(mErrors, mDataSection); +} + +Expression* Parser::ParseExpression(bool lhs) +{ + return ParseAssignmentExpression(lhs); +} + +Expression* Parser::ExpandArgumentPack(Expression* exp, Declaration* dec) +{ + Expression* vex = new Expression(exp->mLocation, EX_VARIABLE); + vex->mDecType = dec->mBase; + vex->mDecValue = dec; + + if (dec->mNext) + { + Expression* lex = new Expression(exp->mLocation, EX_LIST); + lex->mToken = TK_COMMA; + lex->mLeft = vex; + lex->mRight = ExpandArgumentPack(exp, dec->mNext); + return lex; + } + else + return vex; +} + +Expression* Parser::ParseListExpression(bool lhs, Declaration* params) +{ + if (ConsumeTokenIf(TK_ELLIPSIS)) + { + ExpressionType et = EX_ERROR; + switch (mScanner->mToken) + { + case TK_MUL: + case TK_DIV: + case TK_MOD: + case TK_ADD: + case TK_SUB: + case TK_LEFT_SHIFT: + case TK_RIGHT_SHIFT: + case TK_BINARY_AND: + case TK_BINARY_OR: + case TK_BINARY_XOR: + et = EX_BINARY; + break; + case TK_EQUAL: + case TK_NOT_EQUAL: + case TK_GREATER_THAN: + case TK_GREATER_EQUAL: + case TK_LESS_THAN: + case TK_LESS_EQUAL: + et = EX_RELATIONAL; + break; + case TK_LOGICAL_AND: + et = EX_LOGICAL_AND; + break; + case TK_LOGICAL_OR: + et = EX_LOGICAL_OR; + break; + case TK_LOGICAL_NOT: + et = EX_LOGICAL_NOT; + break; + case TK_COMMA: + et = EX_LIST; + break; + } + + if (et == EX_ERROR) + mErrors->Error(mScanner->mLocation, EERR_INVALID_FOLD_EXPRESSION, "Invalid operator in fold expression"); + + Expression* nexp = new Expression(mScanner->mLocation, et); + nexp->mToken = mScanner->mToken; + mScanner->NextToken(); + nexp->mRight = ParseExpression(false); + return ParseBinaryFoldExpression(nexp); + } + + Expression* exp; + + if ((mCompilerOptions & COPT_CPLUSPLUS) && mScanner->mToken == TK_OPEN_BRACE && params) + { + exp = new Expression(mScanner->mLocation, EX_TYPE); + exp->mDecType = params->mBase->NonRefBase(); + exp = ParseCastExpression(exp); + } + else + exp = ParseExpression(lhs); + + if (exp->mType == EX_PACK) + { + if (ConsumeTokenIf(TK_ELLIPSIS)) + { + if (exp->mDecValue->mParams) + exp = ExpandArgumentPack(exp, exp->mDecValue->mParams); + else + exp = nullptr; + } + } + + if (ConsumeTokenIf(TK_COMMA)) + { + Expression* nexp = new Expression(mScanner->mLocation, EX_LIST); + nexp->mToken = TK_COMMA; + nexp->mLeft = exp; + + if (ConsumeTokenIf(TK_ELLIPSIS)) + return ParseBinaryFoldExpression(nexp); + + nexp->mRight = ParseListExpression(false, params ? params->mNext : nullptr); + nexp->mDecType = nexp->mRight->mDecType; + exp = nexp; + } + return exp; + +} + +Expression* Parser::ParseFunction(Declaration * dec) +{ + Declaration* othis = mThisPointer; + Declaration* ofunc = mFunctionType; + + if (dec->mFlags & DTF_FUNC_THIS) + mThisPointer = dec->mParams; + else + mThisPointer = dec->mClass; + + mFunctionType = dec; + + DeclarationScope* oscope = mScope; + while (mScope->mLevel == SLEVEL_CLASS) + mScope = mScope->mParent; + + DeclarationScope* scope = new DeclarationScope(mScope, SLEVEL_FUNCTION); + mScope = scope; + + Declaration* pdec = dec->mParams; + while (pdec) + { + if (pdec->mIdent) + scope->Insert(pdec->mIdent, pdec); + pdec = pdec->mNext; + } + if (dec->mParamPack) + mScope->Insert(dec->mParamPack->mIdent, dec->mParamPack); + + int localIndex = mLocalIndex; + mLocalIndex = 0; + mTempVars = nullptr; + + Expression * exp = ParseStatement(); + + if (mCompilerOptions & COPT_CPLUSPLUS) + { + // Add destructors for parameters + Declaration* pdec = dec->mParams; + + while (pdec) + { + if (pdec->mBase->mType == DT_TYPE_STRUCT && pdec->mBase->mDestructor) + { + Expression* vexp = new Expression(pdec->mLocation, EX_VARIABLE); + vexp->mDecType = pdec->mBase; + vexp->mDecValue = pdec; + + Expression* texp = new Expression(mScanner->mLocation, EX_PREFIX); + texp->mToken = TK_BINARY_AND; + texp->mLeft = vexp; + texp->mDecType = new Declaration(mScanner->mLocation, DT_TYPE_POINTER); + texp->mDecType->mFlags |= DTF_CONST | DTF_DEFINED; + texp->mDecType->mBase = pdec->mBase; + texp->mDecType->mSize = 2; + + Expression* cexp = new Expression(mScanner->mLocation, EX_CONSTANT); + cexp->mDecValue = pdec->mBase->mDestructor; + cexp->mDecType = cexp->mDecValue->mBase; + + Expression * dexp = new Expression(mScanner->mLocation, EX_CALL); + dexp->mLeft = cexp; + dexp->mRight = texp; + + Expression* nexp = new Expression(mScanner->mLocation, EX_CONSTRUCT); + + nexp->mLeft = new Expression(mScanner->mLocation, EX_LIST); + nexp->mLeft->mLeft = nullptr; + nexp->mLeft->mRight = dexp; + nexp->mRight = vexp; + nexp->mDecType = vexp->mDecType; + + Expression* sexp = new Expression(mScanner->mLocation, EX_SEQUENCE); + sexp->mLeft = nexp; + sexp->mRight = exp; + exp = sexp; + } + + pdec = pdec->mNext; + } + } + + if (!dec->mBase) + dec->mBase = TheVoidTypeDeclaration; + else if (dec->mBase->mType == DT_TYPE_AUTO) + dec->mBase->mType = DT_TYPE_VOID; + + mScope->End(mScanner->mLocation); + mScope = oscope; + + mThisPointer = othis; + mFunctionType = ofunc; + + return exp; +} + +void Parser::SkipStatement(void) +{ + if (ConsumeTokenIf(TK_OPEN_BRACE)) + { + int cnt = 1; + while (cnt > 0) + { + if (ConsumeTokenIf(TK_OPEN_BRACE)) + cnt++; + else if (ConsumeTokenIf(TK_CLOSE_BRACE)) + cnt--; + else + mScanner->NextToken(); + } + } + else if (ConsumeTokenIf(TK_IF)) + { + SkipStatement(); + if (ConsumeTokenIf(TK_ELSE)) + SkipStatement(); + } + else + { + while (!ConsumeTokenIf(TK_SEMICOLON)) + { + mScanner->NextToken(); + } + } +} + +Expression* Parser::ParseStatement(void) +{ + Expression* exp = nullptr; + + while (mScanner->mToken == TK_PREP_PRAGMA) + { + mScanner->NextToken(); + ParsePragma(); + if (mScanner->mToken == TK_CLOSE_BRACE) + return nullptr; + } + + if (mScanner->mToken == TK_OPEN_BRACE) + { + DeclarationScope* scope = new DeclarationScope(mScope, SLEVEL_LOCAL); + mScope = scope; + + mScanner->NextToken(); + if (mScanner->mToken != TK_CLOSE_BRACE) + { + Expression* sexp = new Expression(mScanner->mLocation, EX_SCOPE); + + Expression* pexp = nullptr; + do + { + Expression* nexp = ParseStatement(); + if (nexp) + { + if (exp) + { + if (!pexp) + { + pexp = new Expression(mScanner->mLocation, EX_SEQUENCE); + pexp->mLeft = exp; + exp = pexp; + } + + pexp->mRight = new Expression(mScanner->mLocation, EX_SEQUENCE); + pexp = pexp->mRight; + pexp->mLeft = nexp; + } + else + exp = nexp; + } + + } while (mScanner->mToken != TK_CLOSE_BRACE && mScanner->mToken != TK_EOF); + + if (mScanner->mToken != TK_CLOSE_BRACE) + mErrors->Error(mScanner->mLocation, EERR_SYNTAX, "'}' expected"); + + exp->mEndLocation = mScanner->mLocation; + sexp->mEndLocation = mScanner->mLocation; + mScope->End(mScanner->mLocation); + mScanner->NextToken(); + + sexp->mLeft = exp; + + exp = sexp; + } + else + { + exp = new Expression(mScanner->mLocation, EX_VOID); + exp->mEndLocation = mScanner->mLocation; + mScanner->NextToken(); + } + + mScope = mScope->mParent; + } + else + { + switch (mScanner->mToken) + { + case TK_IF: + { + mScanner->NextToken(); + + if (ConsumeTokenIf(TK_CONSTEXPR)) + { + exp = CoerceExpression(CleanupExpression(ParseParenthesisExpression()), TheBoolTypeDeclaration)->ConstantFold(mErrors, mDataSection); + if (exp->mType == EX_CONSTANT && exp->mDecValue->mType == DT_CONST_INTEGER) + { + if (exp->mDecValue->mInteger) + { + exp = ParseStatement(); + if (ConsumeTokenIf(TK_ELSE)) + SkipStatement(); + } + else + { + SkipStatement(); + if (ConsumeTokenIf(TK_ELSE)) + exp = ParseStatement(); + else + exp = new Expression(mScanner->mLocation, EX_VOID); + } + } + else + mErrors->Error(exp->mLocation, EERR_INVALID_CONSTEXPR, "Condition is not constant"); + } + else + { + exp = new Expression(mScanner->mLocation, EX_IF); + exp->mLeft = CleanupExpression(CoerceExpression(ParseParenthesisExpression(), TheBoolTypeDeclaration)); + exp->mRight = new Expression(mScanner->mLocation, EX_ELSE); + exp->mRight->mLeft = ParseStatement(); + if (mScanner->mToken == TK_ELSE) + { + mScanner->NextToken(); + exp->mRight->mRight = ParseStatement(); + } + else + exp->mRight->mRight = nullptr; + } + + } break; + case TK_WHILE: + { + mScanner->NextToken(); + + DeclarationScope* scope = new DeclarationScope(mScope, SLEVEL_LOCAL); + mScope = scope; + + exp = new Expression(mScanner->mLocation, EX_WHILE); + exp->mLeft = CleanupExpression(CoerceExpression(ParseParenthesisExpression(), TheBoolTypeDeclaration)); + exp->mRight = ParseStatement(); + + mScope->End(mScanner->mLocation); + mScope = mScope->mParent; + } + break; + case TK_DO: + mScanner->NextToken(); + exp = new Expression(mScanner->mLocation, EX_DO); + exp->mRight = ParseStatement(); + if (mScanner->mToken == TK_WHILE) + { + mScanner->NextToken(); + exp->mLeft = CleanupExpression(CoerceExpression(ParseParenthesisExpression(), TheBoolTypeDeclaration)); + ConsumeToken(TK_SEMICOLON); + } + else + mErrors->Error(mScanner->mLocation, EERR_SYNTAX, "'while' expected"); + break; + case TK_FOR: + mScanner->NextToken(); + if (mScanner->mToken == TK_OPEN_PARENTHESIS) + { + DeclarationScope* scope = new DeclarationScope(mScope, SLEVEL_LOCAL); + mScope = scope; + + mScanner->NextToken(); + exp = new Expression(mScanner->mLocation, EX_FOR); + + int unrollLoop = mUnrollLoop; + bool unrollPage = mUnrollLoopPage; + mUnrollLoop = 0; + mUnrollLoopPage = false; + + Expression* initExp = nullptr, * iterateExp = nullptr, * conditionExp = nullptr, * bodyExp = nullptr, * finalExp = nullptr; + + // Assignment + if (mScanner->mToken != TK_SEMICOLON) + initExp = CleanupExpression(ParseListExpression(true)); + + if ((mCompilerOptions & COPT_CPLUSPLUS) && ConsumeTokenIf(TK_COLON)) + { + Expression* containerExp = ParseExpression(false); + if (initExp->mType == EX_VARIABLE) + { + if (containerExp->mDecType->mType == DT_TYPE_ARRAY) + { + Declaration* iterVarDec = new Declaration(mScanner->mLocation, DT_VARIABLE); + iterVarDec->mBase = containerExp->mDecType->mBase->BuildPointer(mScanner->mLocation); + iterVarDec->mVarIndex = mLocalIndex++; + iterVarDec->mSize = iterVarDec->mBase->mSize; + iterVarDec->mFlags |= DTF_DEFINED; + + Declaration* endVarDec = new Declaration(mScanner->mLocation, DT_VARIABLE); + endVarDec->mBase = iterVarDec->mBase; + endVarDec->mVarIndex = mLocalIndex++; + endVarDec->mSize = endVarDec->mBase->mSize; + endVarDec->mFlags |= DTF_DEFINED; + + Declaration* valueVarDec = initExp->mDecValue; + valueVarDec->mBase = valueVarDec->mBase->DeduceAuto(containerExp->mDecType->mBase); + valueVarDec->mSize = valueVarDec->mBase->mSize; + + ExpandStructuredBinding(valueVarDec); + + initExp = new Expression(mScanner->mLocation, EX_LIST); + initExp->mLeft = new Expression(mScanner->mLocation, EX_INITIALIZATION); + initExp->mLeft->mToken = TK_ASSIGN; + initExp->mLeft->mLeft = new Expression(mScanner->mLocation, EX_VARIABLE); + initExp->mLeft->mLeft->mDecType = iterVarDec->mBase; + initExp->mLeft->mLeft->mDecValue = iterVarDec; + initExp->mLeft->mRight = containerExp; + + initExp->mRight = new Expression(mScanner->mLocation, EX_INITIALIZATION); + initExp->mRight->mToken = TK_ASSIGN; + initExp->mRight->mLeft = new Expression(mScanner->mLocation, EX_VARIABLE); + initExp->mRight->mLeft->mDecType = endVarDec->mBase; + initExp->mRight->mLeft->mDecValue = endVarDec; + initExp->mRight->mRight = new Expression(mScanner->mLocation, EX_BINARY); + initExp->mRight->mRight->mToken = TK_ADD; + initExp->mRight->mRight->mDecType = iterVarDec->mBase; + initExp->mRight->mRight->mLeft = new Expression(mScanner->mLocation, EX_VARIABLE); + initExp->mRight->mRight->mLeft->mDecType = iterVarDec->mBase; + initExp->mRight->mRight->mLeft->mDecValue = iterVarDec; + initExp->mRight->mRight->mRight = new Expression(mScanner->mLocation, EX_CONSTANT); + initExp->mRight->mRight->mRight->mDecType = TheSignedIntTypeDeclaration; + initExp->mRight->mRight->mRight->mDecValue = new Declaration(mScanner->mLocation, DT_CONST_INTEGER); + initExp->mRight->mRight->mRight->mDecValue->mBase = TheSignedIntTypeDeclaration; + initExp->mRight->mRight->mRight->mDecValue->mSize = 2; + initExp->mRight->mRight->mRight->mDecValue->mInteger = containerExp->mDecType->mSize / containerExp->mDecType->mBase->mSize; + + iterateExp = new Expression(mScanner->mLocation, EX_PREINCDEC); + iterateExp->mToken = TK_INC; + iterateExp->mLeft = new Expression(mScanner->mLocation, EX_VARIABLE); + iterateExp->mLeft->mDecType = iterVarDec->mBase; + iterateExp->mLeft->mDecValue = iterVarDec; + + conditionExp = new Expression(mScanner->mLocation, EX_RELATIONAL); + conditionExp->mToken = TK_NOT_EQUAL; + conditionExp->mLeft = new Expression(mScanner->mLocation, EX_VARIABLE); + conditionExp->mLeft->mDecType = iterVarDec->mBase; + conditionExp->mLeft->mDecValue = iterVarDec; + conditionExp->mRight = new Expression(mScanner->mLocation, EX_VARIABLE); + conditionExp->mRight->mDecType = endVarDec->mBase; + conditionExp->mRight->mDecValue = endVarDec; + + if (valueVarDec->mBase->IsReference()) + bodyExp = new Expression(mScanner->mLocation, EX_INITIALIZATION); + else + bodyExp = new Expression(mScanner->mLocation, EX_ASSIGNMENT); + bodyExp->mToken = TK_ASSIGN; + bodyExp->mLeft = new Expression(mScanner->mLocation, EX_VARIABLE); + bodyExp->mLeft->mDecType = valueVarDec->mBase; + bodyExp->mLeft->mDecValue = valueVarDec; + bodyExp->mRight = new Expression(mScanner->mLocation, EX_PREFIX); + bodyExp->mRight->mToken = TK_MUL; + bodyExp->mRight->mDecType = containerExp->mDecType->mBase; + bodyExp->mRight->mLeft = new Expression(mScanner->mLocation, EX_VARIABLE); + bodyExp->mRight->mLeft->mDecType = iterVarDec->mBase; + bodyExp->mRight->mLeft->mDecValue = iterVarDec; + } + else if (containerExp->mDecType->mType == DT_TYPE_STRUCT) + { + Declaration* fbegin = containerExp->mDecType->mScope->Lookup(Ident::Unique("begin")); + Declaration* fend = containerExp->mDecType->mScope->Lookup(Ident::Unique("end")); + + if (fbegin && fend) + { + Expression* cpexp = new Expression(mScanner->mLocation, EX_PREFIX); + cpexp->mToken = TK_BINARY_AND; + cpexp->mDecType = containerExp->mDecType->BuildPointer(mScanner->mLocation); + cpexp->mLeft = containerExp; + + Declaration* cpvar = AllocTempVar(cpexp->mDecType); + + Expression* cexp = new Expression(exp->mLocation, EX_VARIABLE); + cexp->mDecType = cpexp->mDecType; + cexp->mDecValue = cpvar; + + Expression* cinitexp = new Expression(exp->mLocation, EX_INITIALIZATION); + cinitexp->mToken = TK_ASSIGN; + cinitexp->mLeft = cexp; + cinitexp->mRight = cpexp; + cinitexp->mDecType = exp->mDecType; + + Expression* cbegin = new Expression(mScanner->mLocation, EX_CALL); + cbegin->mDecType = fbegin->mBase->mBase; + cbegin->mLeft = new Expression(mScanner->mLocation, EX_CONSTANT); + cbegin->mLeft->mDecType = fbegin->mBase; + cbegin->mLeft->mDecValue = fbegin; + cbegin->mRight = cexp; + + Expression* cend = new Expression(mScanner->mLocation, EX_CALL); + cend->mDecType = fend->mBase->mBase; + cend->mLeft = new Expression(mScanner->mLocation, EX_CONSTANT); + cend->mLeft->mDecType = fend->mBase; + cend->mLeft->mDecValue = fend; + cend->mRight = cexp; + + ResolveOverloadCall(cbegin); + ResolveOverloadCall(cend); + + fbegin = cbegin->mLeft->mDecValue; + fend = cend->mLeft->mDecValue; + + cbegin->mDecType = fbegin->mBase->mBase; + cend->mDecType = fend->mBase->mBase; + + Declaration* iterVarDec = new Declaration(mScanner->mLocation, DT_VARIABLE); + iterVarDec->mBase = fbegin->mBase->mBase->NonRefBase(); + iterVarDec->mVarIndex = mLocalIndex++; + iterVarDec->mSize = iterVarDec->mBase->mSize; + iterVarDec->mFlags |= DTF_DEFINED; + + Declaration* endVarDec = new Declaration(mScanner->mLocation, DT_VARIABLE); + endVarDec->mBase = fend->mBase->mBase->NonRefBase(); + endVarDec->mVarIndex = mLocalIndex++; + endVarDec->mSize = endVarDec->mBase->mSize; + endVarDec->mFlags |= DTF_DEFINED; + + Declaration* valueVarDec = initExp->mDecValue; + + initExp = new Expression(mScanner->mLocation, EX_LIST); + initExp->mLeft = cinitexp; + + Expression* iinitExp; + + iinitExp = new Expression(mScanner->mLocation, EX_LIST); + iinitExp->mLeft = new Expression(mScanner->mLocation, EX_INITIALIZATION); + iinitExp->mLeft->mToken = TK_ASSIGN; + iinitExp->mLeft->mLeft = new Expression(mScanner->mLocation, EX_VARIABLE); + iinitExp->mLeft->mLeft->mDecType = iterVarDec->mBase; + iinitExp->mLeft->mLeft->mDecValue = iterVarDec; + iinitExp->mLeft->mRight = cbegin; + + iinitExp->mRight = new Expression(mScanner->mLocation, EX_INITIALIZATION); + iinitExp->mRight->mToken = TK_ASSIGN; + iinitExp->mRight->mLeft = new Expression(mScanner->mLocation, EX_VARIABLE); + iinitExp->mRight->mLeft->mDecType = endVarDec->mBase; + iinitExp->mRight->mLeft->mDecValue = endVarDec; + iinitExp->mRight->mRight = cend; + + initExp->mRight = iinitExp; + + iterateExp = new Expression(mScanner->mLocation, EX_PREINCDEC); + iterateExp->mToken = TK_INC; + iterateExp->mLeft = new Expression(mScanner->mLocation, EX_VARIABLE); + iterateExp->mLeft->mDecType = iterVarDec->mBase; + iterateExp->mLeft->mDecValue = iterVarDec; + iterateExp = CheckOperatorOverload(iterateExp); + + conditionExp = new Expression(mScanner->mLocation, EX_RELATIONAL); + conditionExp->mToken = TK_NOT_EQUAL; + conditionExp->mLeft = new Expression(mScanner->mLocation, EX_VARIABLE); + conditionExp->mLeft->mDecType = iterVarDec->mBase; + conditionExp->mLeft->mDecValue = iterVarDec; + conditionExp->mRight = new Expression(mScanner->mLocation, EX_VARIABLE); + conditionExp->mRight->mDecType = endVarDec->mBase; + conditionExp->mRight->mDecValue = endVarDec; + conditionExp = CheckOperatorOverload(conditionExp); + + bodyExp = new Expression(mScanner->mLocation, EX_INITIALIZATION); + bodyExp->mToken = TK_ASSIGN; + bodyExp->mLeft = new Expression(mScanner->mLocation, EX_VARIABLE); + + bodyExp->mRight = new Expression(mScanner->mLocation, EX_PREFIX); + bodyExp->mRight->mToken = TK_MUL; + bodyExp->mRight->mDecType = valueVarDec->mBase; + bodyExp->mRight->mLeft = new Expression(mScanner->mLocation, EX_VARIABLE); + bodyExp->mRight->mLeft->mDecType = iterVarDec->mBase; + bodyExp->mRight->mLeft->mDecValue = iterVarDec; + if (iterVarDec->mBase->mType == DT_TYPE_POINTER) + bodyExp->mRight->mDecType = iterVarDec->mBase->mBase; + else + bodyExp->mRight = CheckOperatorOverload(bodyExp->mRight); + + valueVarDec->mBase = valueVarDec->mBase->DeduceAuto(bodyExp->mRight->mDecType); + valueVarDec->mSize = valueVarDec->mBase->mSize; + + ExpandStructuredBinding(valueVarDec); + + bodyExp->mLeft->mDecType = valueVarDec->mBase; + bodyExp->mLeft->mDecValue = valueVarDec; + bodyExp = CheckOperatorOverload(bodyExp); + } + else + mErrors->Error(containerExp->mLocation, EERR_INCOMPATIBLE_TYPES, "Array or container expected"); + } + else + mErrors->Error(containerExp->mLocation, EERR_INCOMPATIBLE_TYPES, "Array or container expected"); + } + else + mErrors->Error(initExp->mLocation, EERR_INCOMPATIBLE_TYPES, "Variable expected"); + } + else + { + if (mScanner->mToken == TK_SEMICOLON) + mScanner->NextToken(); + else + mErrors->Error(mScanner->mLocation, EERR_SYNTAX, "';' expected"); + + // Condition + if (mScanner->mToken != TK_SEMICOLON) + conditionExp = CleanupExpression(CoerceExpression(ParseListExpression(false), TheBoolTypeDeclaration)); + + if (mScanner->mToken == TK_SEMICOLON) + mScanner->NextToken(); + else + mErrors->Error(mScanner->mLocation, EERR_SYNTAX, "';' expected"); + + // Iteration + if (mScanner->mToken != TK_CLOSE_PARENTHESIS) + iterateExp = CleanupExpression(ParseListExpression(false)); + } + + ConsumeToken(TK_CLOSE_PARENTHESIS); + + Expression * innerExp = ParseStatement(); + if (bodyExp) + { + Expression* exp = new Expression(bodyExp->mLocation, EX_SEQUENCE); + exp->mLeft = bodyExp; + exp->mRight = innerExp; + bodyExp = exp; + } + else + bodyExp = innerExp; + + mScope->End(mScanner->mLocation); + + exp->mLeft = new Expression(mScanner->mLocation, EX_SEQUENCE); + exp->mLeft->mLeft = new Expression(mScanner->mLocation, EX_SEQUENCE); + + if (unrollLoop > 1 && initExp && iterateExp && conditionExp) + { + if ((initExp->mType == EX_ASSIGNMENT || initExp->mType == EX_INITIALIZATION) && initExp->mLeft->mType == EX_VARIABLE && initExp->mRight->mType == EX_CONSTANT && + (iterateExp->mType == EX_POSTINCDEC || iterateExp->mType == EX_PREINCDEC || iterateExp->mType == EX_ASSIGNMENT && (iterateExp->mToken == TK_ASSIGN_ADD || iterateExp->mToken == TK_ASSIGN_SUB) && iterateExp->mRight->mType == EX_CONSTANT) && + iterateExp->mLeft->IsSame(initExp->mLeft) && + (conditionExp->mType == EX_RELATIONAL && + (conditionExp->mToken == TK_LESS_THAN || conditionExp->mToken == TK_GREATER_THAN || conditionExp->mToken == TK_LESS_EQUAL || conditionExp->mToken == TK_GREATER_EQUAL) && + conditionExp->mLeft->IsSame(initExp->mLeft) && + conditionExp->mRight->mType == EX_CONSTANT || + conditionExp->IsSame(initExp->mLeft))) + { + if (initExp->mRight->mDecValue->mType == DT_CONST_INTEGER && (conditionExp->mType == EX_VARIABLE || conditionExp->mRight->mDecValue->mType == DT_CONST_INTEGER)) + { + int startValue = int(initExp->mRight->mDecValue->mInteger); + int endValue = 0; + int stepValue = 1; + + if (iterateExp->mType == EX_ASSIGNMENT) + { + if (iterateExp->mToken == TK_ASSIGN_ADD) + stepValue = int(iterateExp->mRight->mDecValue->mInteger); + else + stepValue = - int(iterateExp->mRight->mDecValue->mInteger); + } + else if (iterateExp->mToken == TK_DEC) + stepValue = -1; + + if (conditionExp->mType == EX_RELATIONAL) + { + endValue = int(conditionExp->mRight->mDecValue->mInteger); + + if (conditionExp->mToken == TK_LESS_EQUAL) + { + endValue++; + conditionExp->mToken = TK_LESS_THAN; + } + else if (conditionExp->mToken == TK_GREATER_EQUAL) + { + endValue--; + conditionExp->mToken = TK_GREATER_THAN; + } + } + else if (stepValue > 0 && startValue >= 0) + { + endValue = 1 << (initExp->mLeft->mDecValue->mSize * 8); + } + + if (unrollPage) + { + int numLoops = (endValue - startValue + 255) / 256; + int numIterations = (endValue - startValue) / numLoops; + int stride = (endValue - startValue) / numLoops; + int remain = (endValue - startValue) - numIterations * numLoops; + + // Switch from 3 to 4 to avoid cost of two remains + if (numLoops == 3 && remain == 2 && !((endValue - startValue) & 3)) + { + numLoops = 4; + numIterations = (endValue - startValue) / numLoops; + stride = (endValue - startValue) / numLoops; + remain = (endValue - startValue) - numIterations * numLoops; + } + + Expression* unrollBody = new Expression(mScanner->mLocation, EX_SEQUENCE); + unrollBody->mLeft = bodyExp; + Expression* bexp = unrollBody; + + Expression* iexp = new Expression(mScanner->mLocation, EX_ASSIGNMENT); + iexp->mToken = TK_ASSIGN_ADD; + iexp->mLeft = iterateExp->mLeft; + iexp->mRight = new Expression(mScanner->mLocation, EX_CONSTANT); + + Declaration * idec = new Declaration(mScanner->mLocation, DT_CONST_INTEGER); + idec->mInteger = stride; + idec->mBase = TheSignedIntTypeDeclaration; + iexp->mRight = new Expression(mScanner->mLocation, EX_CONSTANT); + iexp->mRight->mDecValue = idec; + iexp->mRight->mDecType = idec->mBase; + + Expression* dexp = new Expression(mScanner->mLocation, EX_ASSIGNMENT); + dexp->mToken = TK_ASSIGN_SUB; + dexp->mLeft = iterateExp->mLeft; + dexp->mRight = new Expression(mScanner->mLocation, EX_CONSTANT); + + Declaration* ddec = new Declaration(mScanner->mLocation, DT_CONST_INTEGER); + ddec->mInteger = stride * (numLoops - 1); + ddec->mBase = TheSignedIntTypeDeclaration; + dexp->mRight = new Expression(mScanner->mLocation, EX_CONSTANT); + dexp->mRight->mDecValue = ddec; + dexp->mRight->mDecType = ddec->mBase; + + for (int i = 1; i < numLoops; i++) + { + bexp->mRight = new Expression(mScanner->mLocation, EX_SEQUENCE); + bexp = bexp->mRight; + bexp->mLeft = iexp; + bexp->mRight = new Expression(mScanner->mLocation, EX_SEQUENCE); + bexp = bexp->mRight; + bexp->mLeft = bodyExp; + } + bexp->mRight = new Expression(mScanner->mLocation, EX_SEQUENCE); + bexp = bexp->mRight; + bexp->mLeft = dexp; + + if (conditionExp->mType == EX_RELATIONAL) + { + conditionExp->mRight->mDecValue = conditionExp->mRight->mDecValue->Clone(); + } + else + { + conditionExp = new Expression(conditionExp->mLocation, EX_RELATIONAL); + conditionExp->mDecType = TheBoolTypeDeclaration; + conditionExp->mToken = stepValue < 0 ? TK_GREATER_THAN : TK_LESS_THAN; + conditionExp->mLeft = initExp->mLeft; + conditionExp->mRight = new Expression(conditionExp->mLocation, EX_CONSTANT); + conditionExp->mRight->mDecType = conditionExp->mDecType; + conditionExp->mRight->mDecValue = new Declaration(conditionExp->mLocation, DT_CONST_INTEGER); + conditionExp->mRight->mDecValue->mBase = conditionExp->mRight->mDecType; + } + + conditionExp->mRight->mDecValue->mInteger = numIterations; + + if (remain) + { + finalExp = new Expression(mScanner->mLocation, EX_SEQUENCE); + finalExp->mLeft = bodyExp; + Expression* bexp = finalExp; + + for (int i = 1; i < remain; i++) + { + bexp->mRight = new Expression(mScanner->mLocation, EX_SEQUENCE); + bexp = bexp->mRight; + bexp->mLeft = iexp; + bexp->mRight = new Expression(mScanner->mLocation, EX_SEQUENCE); + bexp = bexp->mRight; + bexp->mLeft = bodyExp; + } + } + + bodyExp = unrollBody; + } + else + { + int numSteps = stepValue > 0 ? (endValue - startValue + stepValue - 1) / stepValue : (endValue - startValue + stepValue + 1) / stepValue; + int remain = numSteps % unrollLoop; + endValue -= remain * stepValue; + + if (conditionExp->mType == EX_RELATIONAL) + { + conditionExp->mRight->mDecValue = conditionExp->mRight->mDecValue->Clone(); + } + else + { + conditionExp = new Expression(conditionExp->mLocation, EX_RELATIONAL); + conditionExp->mDecType = TheBoolTypeDeclaration; + conditionExp->mToken = stepValue < 0 ? TK_GREATER_THAN : TK_LESS_THAN; + conditionExp->mLeft = initExp->mLeft; + conditionExp->mRight = new Expression(conditionExp->mLocation, EX_CONSTANT); + conditionExp->mRight->mDecType = conditionExp->mDecType; + conditionExp->mRight->mDecValue = new Declaration(conditionExp->mLocation, DT_CONST_INTEGER); + conditionExp->mRight->mDecValue->mBase = conditionExp->mRight->mDecType; + } + conditionExp->mRight->mDecValue->mInteger = endValue; + + Expression* unrollBody = new Expression(mScanner->mLocation, EX_FORBODY); + unrollBody->mLeft = bodyExp; + Expression* bexp = unrollBody; + if ((endValue - startValue) * stepValue > 0) + { + for (int i = 1; i < unrollLoop; i++) + { + bexp->mRight = new Expression(mScanner->mLocation, EX_SEQUENCE); + bexp = bexp->mRight; + bexp->mLeft = iterateExp; + bexp->mRight = new Expression(mScanner->mLocation, EX_FORBODY); + bexp = bexp->mRight; + bexp->mLeft = bodyExp; + } + + conditionExp->mRight->mDecValue->mInteger = endValue - stepValue * (unrollLoop - 1); + } + + if (remain) + { + finalExp = new Expression(mScanner->mLocation, EX_DO); + finalExp->mLeft = new Expression(mScanner->mLocation, EX_CONSTANT); + finalExp->mLeft->mDecType = TheBoolTypeDeclaration; + finalExp->mLeft->mDecValue = TheFalseConstDeclaration; + + finalExp->mRight = new Expression(mScanner->mLocation, EX_FORBODY); + finalExp->mRight->mLeft = bodyExp; + Expression* bexp = finalExp->mRight; + + for (int i = 1; i < remain; i++) + { + bexp->mRight = new Expression(mScanner->mLocation, EX_SEQUENCE); + bexp = bexp->mRight; + bexp->mLeft = iterateExp; + bexp->mRight = new Expression(mScanner->mLocation, EX_FORBODY); + bexp = bexp->mRight; + bexp->mLeft = bodyExp; + } + } + + bodyExp = unrollBody; + } + } + else + mErrors->Error(exp->mLocation, EWARN_LOOP_UNROLL_IGNORED, "Loop unroll ignored, bounds and step not integer"); + } + else + mErrors->Error(exp->mLocation, EWARN_LOOP_UNROLL_IGNORED, "Loop unroll ignored, bounds and step not const"); + } + + exp->mLeft->mRight = initExp; + exp->mLeft->mLeft->mLeft = conditionExp; + exp->mLeft->mLeft->mRight = iterateExp; + exp->mRight = bodyExp; + + if (finalExp) + { + Expression* nexp = new Expression(mScanner->mLocation, EX_SEQUENCE); + nexp->mLeft = exp; + nexp->mRight = new Expression(mScanner->mLocation, EX_SEQUENCE); + nexp->mRight->mLeft = finalExp; + exp = nexp; + } + + mScope = mScope->mParent; + } + else + mErrors->Error(mScanner->mLocation, EERR_SYNTAX, "'(' expected"); + break; + case TK_SWITCH: + mScanner->NextToken(); + exp = ParseSwitchStatement(); + break; + case TK_RETURN: + mScanner->NextToken(); + exp = new Expression(mScanner->mLocation, EX_RETURN); + if (mScanner->mToken != TK_SEMICOLON) + { + exp->mLeft = ParseRExpression(); + if (mFunctionType && mFunctionType->mBase) + { + if (mFunctionType->mBase->mType == DT_TYPE_AUTO || mFunctionType->mBase->IsReference() && mFunctionType->mBase->mBase->mType == DT_TYPE_AUTO) + { + mFunctionType->mBase = mFunctionType->mBase->DeduceAuto(exp->mLeft->mDecType); + + if (mFunctionType->mBase->IsComplexStruct()) + { + // Make room for value struct return + Declaration* p = mFunctionType->mParams; + while (p) + { + p->mVarIndex += 2; + p = p->mNext; + } + } + } + exp->mLeft = CoerceExpression(exp->mLeft, mFunctionType->mBase); + } + exp->mLeft = CleanupExpression(exp->mLeft); + Expression* rexp = exp->mLeft; + if (rexp->mType == EX_CLEANUP) + rexp = rexp->mLeft; + + if (rexp->mType == EX_CONSTRUCT && mFunctionType && mFunctionType->mBase && mFunctionType->mBase->IsComplexStruct()) + { + Expression* cexp = rexp->mLeft->mLeft; + + rexp->mLeft->mRight = nullptr; + + rexp->mRight->mType = EX_RESULT; + } + } + ConsumeToken(TK_SEMICOLON); + break; + case TK_BREAK: + mScanner->NextToken(); + exp = new Expression(mScanner->mLocation, EX_BREAK); + ConsumeToken(TK_SEMICOLON); + break; + case TK_CONTINUE: + mScanner->NextToken(); + exp = new Expression(mScanner->mLocation, EX_CONTINUE); + ConsumeToken(TK_SEMICOLON); + break; + case TK_SEMICOLON: + exp = new Expression(mScanner->mLocation, EX_VOID); + mScanner->NextToken(); + break; + case TK_ASM: + { + mScanner->NextToken(); + + uint64 aflags = ParseAssemblerFlags(); + + if (mScanner->mToken == TK_OPEN_BRACE) + { + mScanner->NextToken(); + exp = ParseAssembler(); + exp->mDecValue->mFlags |= aflags; + + if (mScanner->mToken == TK_CLOSE_BRACE) + mScanner->NextToken(); + else + mErrors->Error(mScanner->mLocation, EERR_SYNTAX, "'}' expected"); + } + else + { + mErrors->Error(mScanner->mLocation, EERR_SYNTAX, "'{' expected"); + exp = new Expression(mScanner->mLocation, EX_VOID); + } + } break; + case TK_ASSUME: + mScanner->NextToken(); + exp = new Expression(mScanner->mLocation, EX_ASSUME); + exp->mLeft = ParseParenthesisExpression(); + ConsumeToken(TK_SEMICOLON); + break; + case TK_STATIC_ASSERT: + mScanner->NextToken(); + ParseStaticAssert(); + break; + case TK_TYPEDEF: + ParseDeclaration(nullptr, false, false, false); + exp = new Expression(mScanner->mLocation, EX_VOID); + break; + case TK_GOTO: +#if 1 + exp = new Expression(mScanner->mLocation, EX_GOTO); + mScanner->NextToken(); + if (mScanner->mToken == TK_IDENT) + { + Declaration* dl = new Declaration(mScanner->mLocation, DT_CLABEL); + dl->mIdent = mScanner->mTokenIdent; + exp->mDecValue = dl; + mScanner->NextToken(); + } + else + mErrors->Error(mScanner->mLocation, EERR_SYNTAX, "Identifier expected"); +#else + mErrors->Error(mScanner->mLocation, EERR_UNIMPLEMENTED, "'goto' not implemented"); + mScanner->NextToken(); + exp = new Expression(mScanner->mLocation, EX_VOID); +#endif + break; + + default: + exp = CleanupExpression(ParseListExpression(true)); + if (exp->mType != EX_LABEL) + ConsumeToken(TK_SEMICOLON); + break; + } + } + + assert(exp); + + return exp; +} + +Expression* Parser::ParseSwitchStatement(void) +{ + Expression* sexp = new Expression(mScanner->mLocation, EX_SWITCH); + + if (mScanner->mToken == TK_OPEN_PARENTHESIS) + { + mScanner->NextToken(); + sexp->mLeft = CleanupExpression(ParseExpression(true)); + if (mScanner->mToken == TK_CLOSE_PARENTHESIS) + mScanner->NextToken(); + else + mErrors->Error(mScanner->mLocation, EERR_SYNTAX, "')' expected"); + } + else + mErrors->Error(mScanner->mLocation, EERR_SYNTAX, "'(' expected"); + + if (mScanner->mToken == TK_OPEN_BRACE) + { + mScanner->NextToken(); + + if (mScanner->mToken != TK_CLOSE_BRACE) + { + Expression* pcsexp = nullptr; + + Expression* cexp = nullptr; + + Expression* pexp = nullptr; + do + { + if (mScanner->mToken == TK_CASE) + { + mScanner->NextToken(); + cexp = new Expression(mScanner->mLocation, EX_CASE); + pexp = cexp; + cexp->mLeft = ParseRExpression(); + + if (ConsumeTokenIf(TK_ELLIPSIS)) + { + Expression* rexp = new Expression(mScanner->mLocation, EX_LIST); + rexp->mLeft = cexp->mLeft; + rexp->mRight = ParseRExpression(); + cexp->mLeft = rexp; + } + + if (mScanner->mToken == TK_COLON) + mScanner->NextToken(); + else + mErrors->Error(mScanner->mLocation, EERR_SYNTAX, "':' expected"); + + Expression* csexp = new Expression(mScanner->mLocation, EX_SEQUENCE); + csexp->mLeft = cexp; + if (pcsexp) + pcsexp->mRight = csexp; + else + sexp->mRight = csexp; + pcsexp = csexp; + + } + else if (mScanner->mToken == TK_DEFAULT) + { + mScanner->NextToken(); + cexp = new Expression(mScanner->mLocation, EX_DEFAULT); + pexp = cexp; + + if (mScanner->mToken == TK_COLON) + mScanner->NextToken(); + else + mErrors->Error(mScanner->mLocation, EERR_SYNTAX, "':' expected"); + + Expression * csexp = new Expression(mScanner->mLocation, EX_SEQUENCE); + csexp->mLeft = cexp; + if (pcsexp) + pcsexp->mRight = csexp; + else + sexp->mRight = csexp; + pcsexp = csexp; + } + else + { + Expression* nexp = ParseStatement(); + if (cexp) + { + Expression* sexp = new Expression(mScanner->mLocation, EX_SEQUENCE); + sexp->mLeft = nexp; + pexp->mRight = sexp; + pexp = sexp; + } + } + + } while (mScanner->mToken != TK_CLOSE_BRACE && mScanner->mToken != TK_EOF); + } + + if (mScanner->mToken == TK_CLOSE_BRACE) + mScanner->NextToken(); + else + mErrors->Error(mScanner->mLocation, EERR_SYNTAX, "'}' expected"); + } + else + mErrors->Error(mScanner->mLocation, EERR_SYNTAX, "'{' expected"); + + return sexp; +} + +int Parser::ExpansionDistance(Declaration* tdec, Declaration* spec, Declaration* xtdec) +{ + Declaration* ptdec = tdec->mParams; + Declaration* psdec = spec->mParams; + + int cost = 0; + if (tdec->mParser) + cost = 100000; + + while (ptdec && psdec) + { + if (ptdec->mType == DT_CONST_TEMPLATE && psdec->mType == DT_CONST_TEMPLATE) + { + if (ptdec->mBase) + { + if (ptdec->mBase->mType == DT_CONST_INTEGER) + { + if (ptdec->mBase->mInteger != psdec->mBase->mInteger) + return NOOVERLOAD; + } + else if (ptdec->mBase->mType == DT_CONST_TEMPLATE) + { + Declaration* pdec = xtdec->mScope->Insert(ptdec->mBase->mIdent, psdec->mBase); + cost += 100; + } + } + else + { + Declaration* pdec = xtdec->mScope->Insert(ptdec->mIdent, psdec->mBase); + cost += 100; + } + } + else if (ptdec->mType == DT_TYPE_TEMPLATE && psdec->mType == DT_TYPE_TEMPLATE) + { + if (ptdec->mBase) + { + if (!xtdec->ResolveTemplate(psdec->mBase, ptdec->mBase, true, false)) + return NOOVERLOAD; + } + else if (!xtdec->ResolveTemplate(psdec->mBase, ptdec, true, false)) + return NOOVERLOAD; + else + cost += 100; + } + else if (ptdec->mType == DT_PACK_TEMPLATE && psdec->mType == DT_PACK_TEMPLATE) + { + if (ptdec->mBase) + { + + Declaration* tvp = ptdec->mBase->mParams; + Declaration* svp = psdec->mBase->mParams; + + while (tvp && svp && tvp->IsSame(svp)) + { + tvp = tvp->mNext; + svp = svp->mNext; + } + + if (tvp || svp) + return NOOVERLOAD; + } + else if (psdec->mBase) + { + if (!xtdec->ResolveTemplate(psdec->mBase, ptdec, true, false)) + return NOOVERLOAD; + + Declaration* dec = psdec->mBase->mParams; + while (dec) + { + cost += 200; + dec = dec->mNext; + } + } + } + else if (ptdec->mType == DT_PACK_TEMPLATE) + { + if (ptdec->mBase) + { + ptdec = ptdec->mBase->mParams; + continue; + } + else if (psdec->mBase) + { + if (!xtdec->ResolveTemplate(psdec->mBase, ptdec, true, false)) + return NOOVERLOAD; + psdec = psdec->mNext; + cost += 200; + continue; + } + else + { + while (psdec) + { + cost += 200; + psdec = psdec->mNext; + } + + return cost; + } + } + else + return NOOVERLOAD; + + ptdec = ptdec->mNext; + psdec = psdec->mNext; + } + + if (ptdec && ptdec->mType == DT_PACK_TEMPLATE) + { + if (ptdec->mBase) + { + if (ptdec->mBase->mParams) + return NOOVERLOAD; + } + else + cost += 50; + } + else if (ptdec || psdec) + return NOOVERLOAD; + + return cost; +} + +void Parser::ParseTemplateArguments(Declaration* tmpld, Declaration* tdec) +{ + ConsumeToken(TK_LESS_THAN); + + Declaration* ppdec = nullptr; + + if (!ConsumeTokenIf(TK_GREATER_THAN)) + { + Declaration* tparm = tmpld->mParams; + while (tmpld->mNext && !tparm) + { + tmpld = tmpld->mNext; + tparm = tmpld->mParams; + } + + do + { + Expression* exp; + + if (tparm && tparm->mType == DT_TYPE_TEMPLATE) + exp = ParseSimpleExpression(false, true); + else + exp = ParseShiftExpression(false); + + exp = exp->ConstantFold(mErrors, mDataSection, mCompilationUnits->mLinker); + + Declaration* pdec = nullptr; + + if (exp->mType == EX_TYPE) + { + pdec = new Declaration(exp->mLocation, DT_TYPE_TEMPLATE); + pdec->mBase = exp->mDecType; + } + else if (exp->mType == EX_PACK_TYPE) + { + ConsumeToken(TK_ELLIPSIS); + + if (exp->mDecType->mType == DT_PACK_TYPE) + { + Declaration* ptdec = exp->mDecType->mParams; + while (ptdec) + { + pdec = new Declaration(exp->mLocation, DT_TYPE_TEMPLATE); + pdec->mBase = ptdec; + + if (pdec) + { + if (ppdec) + ppdec->mNext = pdec; + else + tdec->mParams = pdec; + ppdec = pdec; + } + + ptdec = ptdec->mNext; + } + continue; + } + else + { + pdec = new Declaration(exp->mLocation, DT_PACK_TEMPLATE); + pdec->mBase = exp->mDecType; + } + } + else if (exp->mType == EX_CONSTANT && exp->mDecValue->mType == DT_CONST_FUNCTION) + { + pdec = new Declaration(exp->mLocation, DT_TYPE_TEMPLATE); + pdec->mBase = exp->mDecValue; + } + else if (exp->mType == EX_CONSTANT && (exp->mDecValue->mType == DT_CONST_INTEGER || exp->mDecValue->mType == DT_CONST_TEMPLATE)) + { + pdec = new Declaration(exp->mLocation, DT_CONST_TEMPLATE); + pdec->mBase = exp->mDecValue; + } + else + mErrors->Error(exp->mLocation, EERR_TEMPLATE_PARAMS, "Template specification expected"); + + if (pdec) + { + if (ppdec) + ppdec->mNext = pdec; + else + tdec->mParams = pdec; + ppdec = pdec; + } + + if (tparm) + tparm = tparm->mNext; + + } while (ConsumeTokenIf(TK_COMMA)); + + ConsumeToken(TK_GREATER_THAN); + + while (tparm && tparm->mValue) + { + Declaration* pdec = nullptr; + Expression* exp = tparm->mValue; + + if (exp->mType == EX_TYPE) + { + pdec = new Declaration(exp->mLocation, DT_TYPE_TEMPLATE); + pdec->mBase = exp->mDecType; + } + else if (exp->mType == EX_CONSTANT && exp->mDecValue->mType == DT_CONST_FUNCTION) + { + pdec = new Declaration(exp->mLocation, DT_TYPE_TEMPLATE); + pdec->mBase = exp->mDecValue; + } + else if (exp->mType == EX_CONSTANT && (exp->mDecValue->mType == DT_CONST_INTEGER || exp->mDecValue->mType == DT_CONST_TEMPLATE)) + { + pdec = new Declaration(exp->mLocation, DT_CONST_TEMPLATE); + pdec->mBase = exp->mDecValue; + } + else + mErrors->Error(exp->mLocation, EERR_TEMPLATE_PARAMS, "Template specification expected"); + + if (pdec) + { + if (ppdec) + ppdec->mNext = pdec; + else + tdec->mParams = pdec; + ppdec = pdec; + } + + if (tparm) + tparm = tparm->mNext; + } + } +} + +Declaration* Parser::ParseTemplateExpansion(Declaration* tmpld, Declaration* expd) +{ + Declaration * tdec = new Declaration(mScanner->mLocation, DT_TEMPLATE); + tdec->mScope = new DeclarationScope(nullptr, SLEVEL_TEMPLATE); + +#if 1 + if (expd) + { + if (tmpld->mBase->mType == DT_TEMPLATE) + { + Parser* p = tmpld->mBase->mParser->Clone(); + p->mParent = this; + + p->mScanner->Replay(tmpld->mBase->mTokens); + + tdec = tmpld->mBase->Clone(); + tdec->mScope = tdec->mScope->Clone(); + tdec->mScope->mName = expd->mScope->mName; + tdec->mScope->mParent = expd->mScope; + + p->ParseTemplateDeclarationBody(tdec, nullptr); + + return tdec; + } + + Declaration* ppdec = nullptr; + Declaration* dec = expd->mParams; + + while (dec) + { + Declaration* epdec = dec->Clone(); + + if (ppdec) + ppdec->mNext = epdec; + else + tdec->mParams = epdec; + ppdec = epdec; + + dec = dec->mNext; + } + } + else + { + ParseTemplateArguments(tmpld, tdec); + } + + Declaration* tmplpd = tmpld; + while (!tmpld->mTokens) + tmpld = tmpld->mNext; + + Declaration* epdec = tdec->mParams; + while (epdec && + epdec->mBase && + epdec->mBase->mType != DT_TYPE_TEMPLATE && + epdec->mBase->mType != DT_CONST_TEMPLATE && + epdec->mBase->mType != DT_PACK_TEMPLATE && + !(epdec->mBase->mType == DT_TYPE_STRUCT && epdec->mBase->mTemplate)) + { + epdec = epdec->mNext; + } + + if (epdec) + { + // Partial template specification + Declaration* bdec = new Declaration(mScanner->mLocation, tmpld->mBase->mType); + tdec->mBase = bdec; + bdec->mTemplate = tdec; + bdec->mBase = tmpld->mBase->mBase; + tdec->mIdent = tmpld->mIdent; + tdec->mQualIdent = tmpld->mQualIdent; + tdec->mScope->mName = tdec->mIdent; + tdec->mNext = tmpld; + bdec->mIdent = tdec->mIdent->Mangle(tdec->MangleIdent()->mString); + + return bdec; + } + + Declaration* pthis = tmpld->mClass; + + int mindist = NOOVERLOAD; + Declaration* etdec = nullptr; + + Declaration* dec = tmpld; + Declaration* mxtdec = nullptr; + while (dec) + { + Declaration* xtdec = new Declaration(mScanner->mLocation, DT_TEMPLATE); + xtdec->mScope = new DeclarationScope(nullptr, SLEVEL_TEMPLATE); + + int dist = ExpansionDistance(dec, tdec, xtdec); + if (dist < mindist) + { + mxtdec = xtdec; + mindist = dist; + etdec = dec; + } + dec = dec->mNext; + } + + if (mindist == 0) + return etdec->mBase; + else if (mindist == NOOVERLOAD) + { + mErrors->Error(FullLocation(tmpld->mLocation), EERR_TEMPLATE_PARAMS, "No matching template parameters", tmpld->mQualIdent); + return tdec; + } + else + tmpld = etdec; + + dec = tdec->mParams; + Declaration * pdec = tmpld->mBase->mTemplate->mParams; + + Declaration * ppdec = nullptr; + while (pdec) + { + Declaration* rdec = mxtdec->mScope->Lookup(pdec->mIdent); + if (pdec->mType == DT_PACK_TEMPLATE) + { + if (!rdec) + { + rdec = new Declaration(tdec->mLocation, DT_PACK_TYPE); + } + else if (!dec) + { + Declaration* packd = new Declaration(tdec->mLocation, DT_PACK_TEMPLATE); + packd->mBase = new Declaration(tdec->mLocation, DT_PACK_TYPE); + if (ppdec) + ppdec->mNext = packd; + else + tdec->mParams = packd; + + ppdec = nullptr; + dec = packd; + } + else if (dec->mType != DT_PACK_TEMPLATE) + { + Declaration* packd = new Declaration(dec->mLocation, DT_PACK_TEMPLATE); + packd->mBase = new Declaration(dec->mLocation, DT_PACK_TYPE); + if (ppdec) + ppdec->mNext = packd; + else if (tdec->mParams && !tdec->mParams->mNext && tdec->mParams->mType == DT_TYPE_TEMPLATE) + ; + else + tdec->mParams = packd; + + ppdec = nullptr; + while (dec) + { + Declaration* ndec = dec->mBase->Clone(); + + if (ppdec) + ppdec->mNext = ndec; + else + packd->mBase->mParams = ndec; + ppdec = ndec; + dec = dec->mNext; + } + + dec = packd; + } + } + else + { + ppdec = dec; + dec = dec->mNext; + } + + if (rdec) + { +// rdec->mIdent = pdec->mIdent; + tdec->mScope->Insert(pdec->mIdent, rdec); + } + + pdec = pdec->mNext; + } + + +#else + if (expd) + { + if (tmpld->mBase->mType == DT_TEMPLATE) + { + Parser* p = tmpld->mBase->mParser->Clone(); + + p->mScanner->Replay(tmpld->mBase->mTokens); + + tdec = tmpld->mBase->Clone(); + tdec->mScope->mName = expd->mScope->mName; + tdec->mScope->mParent = expd->mScope; + + p->ParseTemplateDeclarationBody(tdec, nullptr); + + return tdec; + } + + Declaration* ppdec = nullptr; + Declaration* dec = expd->mParams; + Declaration* pdec = tmpld->mParams; + + while (dec) + { + Declaration* epdec = dec->Clone(); + + tdec->mScope->Insert(epdec->mIdent, epdec->mBase); + epdec->mFlags |= DTF_DEFINED; + + if (ppdec) + ppdec->mNext = epdec; + else + tdec->mParams = epdec; + ppdec = epdec; + + dec = dec->mNext; + pdec = pdec->mNext; + } + + if (pdec) + { + if (ppdec) + ppdec->mNext = pdec; + else + tdec->mParams = pdec; + } + + } + else + { + ConsumeToken(TK_LESS_THAN); + + Declaration* ppdec = nullptr; + Declaration* pdec = tmpld->mParams; + + // Carry over already specialized parameters + while (pdec && (pdec->mType != DT_TYPE_TEMPLATE && pdec->mType != DT_CONST_TEMPLATE)) + { + Declaration* epdec = pdec->Clone(); + + tdec->mScope->Insert(epdec->mIdent, epdec->mBase); + epdec->mFlags |= DTF_DEFINED; + + if (ppdec) + ppdec->mNext = epdec; + else + tdec->mParams = epdec; + ppdec = epdec; + + pdec = pdec->mNext; + } + + // Now to the new parameters + while (pdec) + { + Declaration* epdec = pdec->Clone(); + Expression* exp = ParseShiftExpression(false); + + if (epdec->mType == DT_TYPE_TEMPLATE) + { + if (exp->mType == EX_TYPE) + epdec->mBase = exp->mDecType; + else if (exp->mType == EX_CONSTANT && exp->mDecValue->mType == DT_CONST_FUNCTION) + epdec->mBase = exp->mDecValue; + else + { + mErrors->Error(exp->mLocation, EERR_TEMPLATE_PARAMS, "Type parameter expected", pdec->mIdent); + epdec->mBase = TheVoidTypeDeclaration; + } + } + else if (epdec->mType == DT_CONST_TEMPLATE) + { + if (exp->mType == EX_CONSTANT && (exp->mDecValue->mType == DT_CONST_INTEGER || exp->mDecValue->mType == DT_CONST_TEMPLATE)) + epdec->mBase = exp->mDecValue; + else + mErrors->Error(exp->mLocation, EERR_TEMPLATE_PARAMS, "Const integer parameter expected", pdec->mIdent); + } + + tdec->mScope->Insert(epdec->mIdent, epdec->mBase); + epdec->mFlags |= DTF_DEFINED; + + if (ppdec) + ppdec->mNext = epdec; + else + tdec->mParams = epdec; + ppdec = epdec; + + pdec = pdec->mNext; + if (pdec && !ConsumeTokenIf(TK_COMMA)) + break; + } + + ConsumeToken(TK_GREATER_THAN); + + // Partial template arguments given + if (pdec) + { + if (ppdec) + ppdec->mNext = pdec; + else + tdec->mParams = pdec; + } + } + + while (!tmpld->mTokens) + tmpld = tmpld->mNext; + + Declaration* pthis = tmpld->mClass; + + Declaration* etdec = tmpld->mNext; + while (etdec && !etdec->IsSameParams(tdec)) + etdec = etdec->mNext; +#endif + + Parser* p = tmpld->mParser->Clone(); + p->mParent = this; + + p->mScanner->Replay(tmpld->mTokens); + + tdec->mIdent = tmpld->mIdent; + tdec->mScope->mName = tdec->MangleIdent(); + tdec->mNext = tmpld->mNext; + tmpld->mNext = tdec; + + tdec->mScope->mParent = tmpld->mScope->mParent; + + p->mTemplateScope = tdec->mScope; + if (tmpld->mFlags & DTF_AUTO_TEMPLATE) + { + Declaration * ndec = tmpld->mBase->ExpandTemplate(tdec->mScope); + + Declaration* pdec = tmpld->mBase; + + if (pdec) + { + Declaration* pcdec = nullptr; + + while (pdec && !ndec->mBase->IsSameParams(pdec->mBase)) + { + pcdec = pdec; + pdec = pdec->mNext; + } + + if (!pdec) + pcdec->mNext = ndec; + } + + tdec->mBase = ndec; + ndec->mValue = p->ParseFunction(ndec->mBase); + ndec->mNumVars = mLocalIndex; + ndec->mFlags |= DTF_DEFINED; + + ndec->mIdent = ndec->mIdent->Mangle(tdec->mScope->mName->mString); + ndec->mQualIdent = ndec->mQualIdent->Mangle(tdec->mScope->mName->mString); + } + else + { + Declaration* tpthis = nullptr; + if (tmpld->mClass) + tpthis = tmpld->mClass; + else if (expd && expd->mBase) + tpthis = expd->mBase->BuildConstPointer(tdec->mLocation); +#if 0 + if (tpthis) + { + DeclarationScope* oscope = p->mScope; + tpthis->mBase->mScope->mParent = p->mScope; + p->mScope = tpthis->mBase->mScope; + + tdec->mBase = p->ParseDeclaration(nullptr, true, false, tpthis, tdec); + + p->mScope = oscope; + } + else +#endif + tdec->mBase = p->ParseDeclaration(nullptr, true, false, false, tpthis, tdec); + } + + p->mTemplateScope = nullptr; + + if (tdec->mBase->mType == DT_ANON) + { + tdec->mBase = tdec->mBase->mBase; + mCompilationUnits->mTemplateScope->Insert(tmpld->mQualIdent, tmpld); + + tdec->mBase->mScope->Iterate([=](const Ident* ident, Declaration* mdec) + { + if (mdec->mType == DT_CONST_FUNCTION) + { + while (mdec) + { + if (!(mdec->mFlags & DTF_DEFINED)) + { + Declaration* mpdec = mCompilationUnits->mScope->Lookup(tmpld->mScope->Mangle(mdec->mIdent)); + while (mpdec && !mdec->mBase->IsTemplateSameParams(mpdec->mBase, tdec)) + mpdec = mpdec->mNext; + if (mpdec && mpdec->mTemplate) + { + Declaration* rdec; + if (mdec->mTemplate) + { + Declaration * mtdec = p->ParseTemplateExpansion(mpdec->mTemplate, tdec); + mtdec->mClass = mdec->mTemplate->mClass; + mdec->mTemplate = mtdec; + mdec->mTemplate->mBase = mdec; + rdec = mtdec; + } + else + rdec = p->ParseTemplateExpansion(mpdec->mTemplate, tdec); + if (mdec->mBase->mBase->IsAuto()) + mdec->mBase->mBase = rdec->mBase->mBase; + } + } + + mdec = mdec->mNext; + } + } + }); + } + + return tdec->mBase; +} + +void Parser::CompleteTemplateExpansion(Declaration* tmpld) +{ + tmpld = tmpld->mNext; + while (tmpld) + { + if (tmpld->mBase->mType == DT_TYPE_STRUCT) + { + // now expand the templated members + + tmpld->mBase->mScope->Iterate([=](const Ident* ident, Declaration* mdec) + { + if (mdec->mType == DT_CONST_FUNCTION) + { + while (mdec) + { + Declaration* mpdec = mCompilationUnits->mScope->Lookup(mdec->mQualIdent); + while (mpdec && !mpdec->IsSameParams(mdec->mParams)) + mpdec = mpdec->mNext; + if (mpdec && mpdec->mType == DT_TEMPLATE) + { + ParseTemplateExpansion(mpdec, tmpld); + } + + mdec = mdec->mNext; + } + } + }); + } + } +} + + +Declaration* Parser::FunctionAutoParamsToTemplate(Declaration* fdec) +{ + Declaration* pdec = fdec->mBase->mParams; + while (pdec && !pdec->mBase->IsAuto()) + pdec = pdec->mNext; + + if (pdec) + { + Declaration* tdec = new Declaration(fdec->mLocation, DT_TEMPLATE); + tdec->mParser = this->Clone(); + tdec->mScope = new DeclarationScope(mTemplateScope, SLEVEL_TEMPLATE); + Declaration* ppdec = nullptr; + + int ntp = 0; + pdec = fdec->mBase->mParams; + while (pdec) + { + Declaration* ptdec = nullptr; + + if (pdec->mBase->mType == DT_TYPE_AUTO) + { + ptdec = new Declaration(mScanner->mLocation, DT_TYPE_TEMPLATE); + pdec->mBase = ptdec; + } + else if (pdec->mBase->IsReference() && pdec->mBase->mBase->mType == DT_TYPE_AUTO) + { + ptdec = new Declaration(mScanner->mLocation, DT_TYPE_TEMPLATE); + pdec->mBase->mBase = ptdec; + } + + if (ptdec) + { + ptdec->mIdent = Ident::Unique("T", ntp++); + tdec->mScope->Insert(ptdec->mIdent, ptdec); + ptdec->mFlags |= DTF_DEFINED; + + if (ppdec) + ppdec->mNext = ptdec; + else + tdec->mParams = ptdec; + ppdec = ptdec; + } + + pdec = pdec->mNext; + } + + fdec->mCompilerOptions = mCompilerOptions; + fdec->mBase->mCompilerOptions = mCompilerOptions; + + tdec->mBase = fdec; + fdec->mTemplate = tdec; + + mScanner->BeginRecord(); + + if (ConsumeTokenIf(TK_OPEN_BRACE)) + { + int qdepth = 1; + while (qdepth) + { + if (ConsumeTokenIf(TK_OPEN_BRACE)) + qdepth++; + else if (ConsumeTokenIf(TK_CLOSE_BRACE)) + qdepth--; + else + mScanner->NextToken(); + } + } + + tdec->mTokens = mScanner->CompleteRecord(); + + tdec->mIdent = tdec->mBase->mIdent; + tdec->mQualIdent = tdec->mBase->mQualIdent; + tdec->mScope->mName = tdec->mQualIdent; + tdec->mFlags |= DTF_AUTO_TEMPLATE; + + if (tdec->mQualIdent == mScope->Mangle(tdec->mIdent)) + mScope->Insert(tdec->mIdent, tdec->mBase); + + Declaration* pdec = mCompilationUnits->mScope->Insert(tdec->mQualIdent, tdec->mBase); + if (pdec) + { + Declaration* ppdec = pdec; + while (pdec && !pdec->IsSameTemplate(tdec->mBase)) + { + ppdec = pdec; + pdec = pdec->mNext; + } + + if (!pdec) + { + ppdec->mNext = tdec->mBase; + tdec->mBase->mNext = nullptr; + } + } + + return fdec; + } + else + return nullptr; +} + +Declaration * Parser::ParseTemplateDeclaration(Declaration* pthis) +{ + Declaration* tdec = new Declaration(mScanner->mLocation, DT_TEMPLATE); + tdec->mParser = this->Clone(); + tdec->mScope = new DeclarationScope(mTemplateScope, SLEVEL_TEMPLATE); + tdec->mClass = pthis; + Declaration* ppdec = nullptr; + + ConsumeToken(TK_LESS_THAN); + if (mScanner->mToken != TK_GREATER_THAN) + { + for (;;) + { + Declaration* pdec = nullptr; + + if (ConsumeTokenIf(TK_CLASS) || ConsumeTokenIf(TK_TYPENAME)) + { + if (ConsumeTokenIf(TK_ELLIPSIS)) + { + if (mScanner->mToken == TK_IDENT) + { + pdec = new Declaration(mScanner->mLocation, DT_PACK_TEMPLATE); + pdec->mIdent = mScanner->mTokenIdent; + mScanner->NextToken(); + } + else + mErrors->Error(mScanner->mLocation, EERR_SYNTAX, "Identifier expected"); + } + else if (mScanner->mToken == TK_IDENT) + { + pdec = new Declaration(mScanner->mLocation, DT_TYPE_TEMPLATE); + pdec->mIdent = mScanner->mTokenIdent; + mScanner->NextToken(); + } + else + mErrors->Error(mScanner->mLocation, EERR_SYNTAX, "Identifier expected"); + } + else if (ConsumeTokenIf(TK_INT)) + { + if (mScanner->mToken == TK_IDENT) + { + pdec = new Declaration(mScanner->mLocation, DT_CONST_TEMPLATE); + pdec->mIdent = mScanner->mTokenIdent; + mScanner->NextToken(); + + if (ConsumeTokenIf(TK_ASSIGN)) + { + Expression* exp = ParseShiftExpression(false); + if (exp->mType == EX_CONSTANT && exp->mDecValue->mType == DT_CONST_INTEGER) + pdec->mValue = exp; + else + mErrors->Error(exp->mLocation, EERR_CONSTANT_TYPE, "Constant integer expression expected"); + } + } + else + mErrors->Error(mScanner->mLocation, EERR_SYNTAX, "Identifier expected"); + } + else + mErrors->Error(mScanner->mLocation, EERR_TEMPLATE_PARAMS, "'class' or 'int' expected as template parameter"); + + if (pdec) + { + tdec->mScope->Insert(pdec->mIdent, pdec); + pdec->mFlags |= DTF_DEFINED; + + if (ppdec) + ppdec->mNext = pdec; + else + tdec->mParams = pdec; + ppdec = pdec; + } + + if (!ConsumeTokenIf(TK_COMMA)) + break; + } + } + + ConsumeToken(TK_GREATER_THAN); + + if (ConsumeTokenIf(TK_TEMPLATE)) + { + mTemplateScope = tdec->mScope; + tdec->mBase = ParseTemplateDeclaration(pthis); + mTemplateScope = tdec->mScope->mParent; + tdec->mBase->mBase->mTemplate = tdec; + } + else + ParseTemplateDeclarationBody(tdec, pthis); + + return tdec; +} + +void Parser::ParseTemplateDeclarationBody(Declaration * tdec, Declaration * pthis) +{ + mScanner->BeginRecord(); + if (mScanner->mToken == TK_CLASS || mScanner->mToken == TK_STRUCT) + { + // Class template + Declaration* bdec = new Declaration(mScanner->mLocation, DT_TYPE_STRUCT); + tdec->mBase = bdec; + bdec->mTemplate = tdec; + + mScanner->NextToken(); + if (mScanner->mToken == TK_IDENT) + { + bdec->mIdent = mScanner->mTokenIdent; + bdec->mQualIdent = mScope->Mangle(mScanner->mTokenIdent); + bdec->mScope = new DeclarationScope(nullptr, SLEVEL_CLASS, bdec->mQualIdent); + + mScanner->NextToken(); + if (mScanner->mToken == TK_LESS_THAN) + { + Declaration* ptdec = new Declaration(mScanner->mLocation, DT_TEMPLATE); + ptdec->mScope = new DeclarationScope(nullptr, SLEVEL_TEMPLATE); + + mTemplateScope = tdec->mScope; + ParseTemplateArguments(tdec, ptdec); + + ptdec->mBase = bdec; + ptdec->mParser = tdec->mParser; + tdec = ptdec; + } + + while (mScanner->mToken != TK_SEMICOLON && mScanner->mToken != TK_OPEN_BRACE) + mScanner->NextToken(); + + if (ConsumeTokenIf(TK_OPEN_BRACE)) + { + int qdepth = 1; + while (qdepth) + { + if (ConsumeTokenIf(TK_OPEN_BRACE)) + qdepth++; + else if (ConsumeTokenIf(TK_CLOSE_BRACE)) + qdepth--; + else + mScanner->NextToken(); + } + } + + mTemplateScope = tdec->mScope->mParent; + } + else + mErrors->Error(bdec->mLocation, EERR_FUNCTION_TEMPLATE, "Class template expected"); + } + else + { + // Function template + mTemplateScope = tdec->mScope; + + ConsumeTokenIf(TK_INLINE); + ConsumeTokenIf(TK_NOINLINE); + ConsumeTokenIf(TK_FORCEINLINE); + ConsumeTokenIf(TK_CONSTEXPR); + + Declaration* bdec = ParseBaseTypeDeclaration(0, true); + Declaration* adec = nullptr; + + if (ConsumeTokenIf(TK_COLCOLON)) + { + if (mScanner->mToken == TK_IDENT && mScanner->mTokenIdent == bdec->mTemplate->mIdent) + { + mScanner->NextToken(); + + Declaration* ctdec = ParseFunctionDeclaration(TheVoidTypeDeclaration); + + adec = new Declaration(ctdec->mLocation, DT_CONST_FUNCTION); + + adec->mBase = ctdec; + adec->mIdent = bdec->mTemplate->mIdent->PreMangle("+"); + + char buffer[200]; + strcpy_s(buffer, bdec->mTemplate->mQualIdent->mString); + strcat_s(buffer, "::"); + strcat_s(buffer, adec->mIdent->mString); + adec->mQualIdent = Ident::Unique(buffer); + } + else if (ConsumeTokenIf(TK_BINARY_NOT) && mScanner->mToken == TK_IDENT && mScanner->mTokenIdent == bdec->mTemplate->mIdent) + { + mScanner->NextToken(); + + Declaration* ctdec = ParseFunctionDeclaration(TheVoidTypeDeclaration); + + adec = new Declaration(ctdec->mLocation, DT_CONST_FUNCTION); + + adec->mBase = ctdec; + adec->mIdent = bdec->mTemplate->mIdent->PreMangle("~"); + + char buffer[200]; + strcpy_s(buffer, bdec->mTemplate->mQualIdent->mString); + strcat_s(buffer, "::"); + strcat_s(buffer, adec->mIdent->mString); + adec->mQualIdent = Ident::Unique(buffer); + } + else if (mScanner->mToken == TK_IDENT) + { + bdec = TheVoidTypeDeclaration; + mScanner->NextToken(); + adec = ParsePostfixDeclaration(false); + adec = ReverseDeclaration(adec, bdec); + } + else + mErrors->Error(bdec->mLocation, EERR_FUNCTION_TEMPLATE, "Constructor or destructor expected"); + } + else + { + if (bdec && pthis && bdec == pthis->mBase && mScanner->mToken == TK_OPEN_PARENTHESIS) + { + Declaration* ctdec = ParseFunctionDeclaration(TheVoidTypeDeclaration); + + adec = new Declaration(ctdec->mLocation, DT_CONST_FUNCTION); + + adec->mBase = ctdec; + adec->mIdent = pthis->mBase->mIdent->PreMangle("+"); + adec->mQualIdent = pthis->mBase->mScope->Mangle(adec->mIdent); + + if (ConsumeTokenIf(TK_COLON)) + { + do { + ConsumeToken(TK_IDENT); + if (ConsumeTokenIf(TK_OPEN_PARENTHESIS)) + { + while (mScanner->mToken != TK_OPEN_PARENTHESIS) + mScanner->NextToken(); + mScanner->NextToken(); + } + else if (ConsumeTokenIf(TK_OPEN_BRACE)) + { + int qdepth = 1; + while (qdepth) + { + if (ConsumeTokenIf(TK_OPEN_BRACE)) + qdepth++; + else if (ConsumeTokenIf(TK_CLOSE_BRACE)) + qdepth--; + else + mScanner->NextToken(); + } + } + + } while (ConsumeTokenIf(TK_COMMA)); + } + + } + else + { + adec = ParsePostfixDeclaration(false); + adec = ReverseDeclaration(adec, bdec); + } + } + + mTemplateScope = tdec->mScope->mParent; + + if (adec->mBase->mType == DT_TYPE_FUNCTION) + { + if (pthis) + PrependThisArgument(adec->mBase, pthis); + + if (ConsumeTokenIf(TK_CONST)) + adec->mBase->mFlags |= DTF_CONST; + + adec->mType = DT_CONST_FUNCTION; + adec->mSection = mCodeSection; + + if (mCompilerOptions & COPT_NATIVE) + adec->mFlags |= DTF_NATIVE; + + tdec->mBase = adec; + adec->mTemplate = tdec; + + if (ConsumeTokenIf(TK_COLON)) + { + if (adec->mIdent->mString[0] != '+') + mErrors->Error(bdec->mLocation, EERR_FUNCTION_TEMPLATE, "Function template body for non constructor expected"); + + do { + ConsumeToken(TK_IDENT); + if (ConsumeToken(TK_OPEN_PARENTHESIS)) + { + int qdepth = 1; + while (qdepth) + { + if (ConsumeTokenIf(TK_OPEN_PARENTHESIS)) + qdepth++; + else if (ConsumeTokenIf(TK_CLOSE_PARENTHESIS)) + qdepth--; + else + mScanner->NextToken(); + } + } + } while (ConsumeTokenIf(TK_COMMA)); + } + + if (ConsumeTokenIf(TK_OPEN_BRACE)) + { + int qdepth = 1; + while (qdepth) + { + if (ConsumeTokenIf(TK_OPEN_BRACE)) + qdepth++; + else if (ConsumeTokenIf(TK_CLOSE_BRACE)) + qdepth--; + else + mScanner->NextToken(); + } + } + else if (ConsumeTokenIf(TK_SEMICOLON)) + { + + } + else + { + mErrors->Error(bdec->mLocation, EERR_FUNCTION_TEMPLATE, "Function template body expected"); + adec->mType = DT_TYPE_VOID; + tdec->mBase = adec; + adec->mIdent = adec->mQualIdent = Ident::Unique("_"); + adec->mTemplate = tdec; + } + } + else + { + mErrors->Error(bdec->mLocation, EERR_FUNCTION_TEMPLATE, "Function template expected"); + adec->mType = DT_TYPE_VOID; + tdec->mBase = adec; + adec->mIdent = adec->mQualIdent = Ident::Unique("_"); + adec->mTemplate = tdec; + } + } + + tdec->mTokens = mScanner->CompleteRecord(); + + tdec->mIdent = tdec->mBase->mIdent; + tdec->mQualIdent = tdec->mBase->mQualIdent; + tdec->mScope->mName = tdec->mQualIdent; + + if (tdec->mQualIdent == mScope->Mangle(tdec->mIdent)) + { + Declaration* pdec = mScope->Insert(tdec->mIdent, tdec->mBase); + if (pdec) + { + Declaration* ppdec = pdec; + while (pdec && !pdec->IsSameTemplate(tdec->mBase)) + { + ppdec = pdec; + pdec = pdec->mNext; + } + + if (!pdec) + { + ppdec->mNext = tdec->mBase; + tdec->mBase->mNext = nullptr; + } + else if (pdec->mTemplate) + { + tdec->mNext = pdec->mTemplate->mNext; + pdec->mTemplate->mNext = tdec; + } + } + } + + Declaration* pdec = mCompilationUnits->mScope->Insert(tdec->mQualIdent, tdec->mBase); + if (pdec) + { + Declaration* ppdec = pdec; + while (pdec && !pdec->IsSameTemplate(tdec->mBase)) + { + ppdec = pdec; + pdec = pdec->mNext; + } + + if (!pdec) + { + ppdec->mNext = tdec->mBase; + tdec->mBase->mNext = nullptr; + } + } +} + + +Expression* Parser::ParseAssemblerBaseOperand(Declaration* pcasm, int pcoffset) +{ + Expression* exp = nullptr; + Declaration* dec; + + switch (mScanner->mToken) + { + case TK_SUB: + mScanner->NextToken(); + exp = ParseAssemblerBaseOperand(pcasm, pcoffset); + if (exp->mType == EX_CONSTANT && exp->mDecValue->mType == DT_CONST_INTEGER) + { + dec = new Declaration(mScanner->mLocation, DT_CONST_INTEGER); + dec->mInteger = - exp->mDecValue->mInteger; + if (dec->mInteger < 32768) + dec->mBase = TheSignedIntTypeDeclaration; + else + dec->mBase = TheUnsignedIntTypeDeclaration; + exp = new Expression(mScanner->mLocation, EX_CONSTANT); + exp->mDecValue = dec; + exp->mDecType = dec->mBase; + } + else + mErrors->Error(exp->mLocation, EERR_INCOMPATIBLE_OPERATOR, "Cannot negate expression"); + break; + + case TK_BINARY_NOT: + mScanner->NextToken(); + exp = ParseAssemblerBaseOperand(pcasm, pcoffset); + if (exp->mType == EX_CONSTANT && exp->mDecValue->mType == DT_CONST_INTEGER) + { + dec = new Declaration(mScanner->mLocation, DT_CONST_INTEGER); + dec->mInteger = ~exp->mDecValue->mInteger; + dec->mBase = TheUnsignedIntTypeDeclaration; + exp = new Expression(mScanner->mLocation, EX_CONSTANT); + exp->mDecValue = dec; + exp->mDecType = dec->mBase; + } + else + mErrors->Error(exp->mLocation, EERR_INCOMPATIBLE_OPERATOR, "Cannot negate expression"); + break; + + case TK_MUL: + mScanner->NextToken(); + exp = new Expression(mScanner->mLocation, EX_CONSTANT); + dec = new Declaration(mScanner->mLocation, DT_LABEL); + exp->mDecType = TheUnsignedIntTypeDeclaration; + exp->mDecValue = dec; + dec->mInteger = pcoffset; + dec->mBase = pcasm; + break; + + case TK_INTEGER: + case TK_INTEGERL: + dec = new Declaration(mScanner->mLocation, DT_CONST_INTEGER); + dec->mInteger = mScanner->mTokenInteger; + if (dec->mInteger < 32768) + dec->mBase = TheSignedIntTypeDeclaration; + else + dec->mBase = TheUnsignedIntTypeDeclaration; + exp = new Expression(mScanner->mLocation, EX_CONSTANT); + exp->mDecValue = dec; + exp->mDecType = dec->mBase; + + mScanner->NextToken(); + break; + case TK_INTEGERU: + case TK_INTEGERUL: + dec = new Declaration(mScanner->mLocation, DT_CONST_INTEGER); + dec->mInteger = mScanner->mTokenInteger; + dec->mBase = TheUnsignedIntTypeDeclaration; + exp = new Expression(mScanner->mLocation, EX_CONSTANT); + exp->mDecValue = dec; + exp->mDecType = dec->mBase; + + mScanner->NextToken(); + break; + case TK_CHARACTER: + dec = new Declaration(mScanner->mLocation, DT_CONST_INTEGER); + dec->mInteger = mCharMap[(unsigned char)mScanner->mTokenInteger]; + dec->mBase = TheUnsignedCharTypeDeclaration; + exp = new Expression(mScanner->mLocation, EX_CONSTANT); + exp->mDecValue = dec; + exp->mDecType = dec->mBase; + + mScanner->NextToken(); + break; + + case TK_OPEN_BRACKET: + mScanner->NextToken(); + exp = ParseAssemblerOperand(pcasm, pcoffset); + ConsumeToken(TK_CLOSE_BRACKET); + break; + + case TK_OPEN_PARENTHESIS: + mScanner->NextToken(); + exp = ParseAssemblerOperand(pcasm, pcoffset); + ConsumeToken(TK_CLOSE_PARENTHESIS); + break; + + case TK_IDENT: + dec = mScope->Lookup(mScanner->mTokenIdent); + if (!dec) + { + exp = new Expression(mScanner->mLocation, EX_CONSTANT); + dec = new Declaration(mScanner->mLocation, DT_LABEL); + dec->mIdent = mScanner->mTokenIdent; + dec->mQualIdent = mScanner->mTokenIdent; + exp->mDecType = TheUnsignedIntTypeDeclaration; + mScope->Insert(dec->mIdent, dec); + } + else + { + exp = new Expression(mScanner->mLocation, EX_CONSTANT); + if (dec->mType == DT_ARGUMENT) + { + exp->mDecType = TheUnsignedIntTypeDeclaration; + } + else if (dec->mType == DT_VARIABLE && (dec->mFlags & DTF_CONSTEXPR) && dec->mValue && dec->mValue->mType == EX_CONSTANT) + { + Declaration* dt = dec->mValue->mDecValue; + if (dt->mType == DT_CONST_INTEGER) + { + exp->mDecValue = dt; + exp->mDecType = dt->mBase; + mScanner->NextToken(); + return exp; + } + else if (dt->mType == DT_CONST_ADDRESS) + { + dec = new Declaration(mScanner->mLocation, DT_CONST_INTEGER); + dec->mInteger = dt->mInteger; + dec->mBase = TheUnsignedIntTypeDeclaration; + + exp->mDecValue = dec; + exp->mDecType = TheUnsignedIntTypeDeclaration; + mScanner->NextToken(); + return exp; + } + exp->mDecType = TheUnsignedIntTypeDeclaration; + } + else if (dec->mType == DT_CONST_ASSEMBLER) + { + exp->mDecType = dec->mBase; + } + else if (dec->mType == DT_TYPE_STRUCT) + { + mScanner->NextToken(); + + int offset = 0; + while (dec->mType == DT_TYPE_STRUCT && ConsumeTokenIf(TK_COLCOLON)) + { + if (mScanner->mToken == TK_IDENT) + { + Declaration* edec = dec->mScope->Lookup(mScanner->mTokenIdent); + if (edec) + { + offset += edec->mOffset; + dec = edec->mBase; + } + else + mErrors->Error(mScanner->mLocation, EERR_OBJECT_NOT_FOUND, "Struct member not found", mScanner->mTokenIdent); + + mScanner->NextToken(); + } + else + mErrors->Error(mScanner->mLocation, EERR_INCOMPATIBLE_OPERATOR, "Identifier for qualification expected"); + } + + dec = new Declaration(mScanner->mLocation, DT_CONST_INTEGER); + dec->mInteger = offset; + dec->mBase = TheUnsignedIntTypeDeclaration; + exp = new Expression(mScanner->mLocation, EX_CONSTANT); + exp->mDecValue = dec; + exp->mDecType = dec->mBase; + + return exp; + } + else + exp->mDecType = TheUnsignedIntTypeDeclaration; + } + + exp->mDecValue = dec; + + mScanner->NextToken(); + + while (ConsumeTokenIf(TK_DOT)) + { + if (mScanner->mToken == TK_IDENT) + { + if (exp->mDecValue->mType == DT_CONST_ASSEMBLER) + { + Declaration* ldec = exp->mDecValue->mBase->mScope->Lookup(mScanner->mTokenIdent); + if (ldec) + { + exp->mDecValue = ldec; + exp->mDecType = TheUnsignedIntTypeDeclaration; + } + else if (exp->mDecValue->mFlags & DTF_DEFINED) + mErrors->Error(mScanner->mLocation, EERR_OBJECT_NOT_FOUND, "Assembler label not found", mScanner->mTokenIdent); + else + { + Declaration * ldec = new Declaration(mScanner->mLocation, DT_LABEL); + ldec->mIdent = mScanner->mTokenIdent; + ldec->mQualIdent = mScanner->mTokenIdent; + exp->mDecType = TheUnsignedIntTypeDeclaration; + exp->mDecValue->mBase->mScope->Insert(ldec->mIdent, ldec); + exp->mDecValue = ldec; + } + } + mScanner->NextToken(); + } + else + mErrors->Error(mScanner->mLocation, EERR_INCOMPATIBLE_OPERATOR, "Identifier for qualification expected"); + } + + if (exp->mDecValue->mType == DT_CONST_ASSEMBLER) + { + Declaration* ndec = new Declaration(mScanner->mLocation, DT_LABEL); + ndec->mIdent = exp->mDecValue->mIdent; + ndec->mQualIdent = exp->mDecValue->mQualIdent; + ndec->mBase = exp->mDecValue; + ndec->mInteger = 0; + exp->mDecValue = ndec; + exp->mDecType = TheUnsignedIntTypeDeclaration; + } + + break; + default: + mErrors->Error(mScanner->mLocation, EERR_ASM_INVALD_OPERAND, "Invalid assembler operand"); + } + + if (!exp) + { + exp = new Expression(mScanner->mLocation, EX_VOID); + exp->mDecType = TheVoidTypeDeclaration; + exp->mDecValue = TheConstVoidValueDeclaration; + } + + return exp; +} + +Expression* Parser::ParseAssemblerMulOperand(Declaration* pcasm, int pcoffset) +{ + Expression* exp = ParseAssemblerBaseOperand(pcasm, pcoffset); + while (mScanner->mToken == TK_MUL || mScanner->mToken == TK_DIV || mScanner->mToken == TK_MOD) + { + Expression* nexp = new Expression(mScanner->mLocation, EX_BINARY); + nexp->mToken = mScanner->mToken; + nexp->mLeft = exp; + mScanner->NextToken(); + nexp->mRight = ParseAssemblerBaseOperand(pcasm, pcoffset); + exp = nexp->ConstantFold(mErrors, mDataSection); + } + return exp; +} + +Expression* Parser::ParseAssemblerAddOperand(Declaration* pcasm, int pcoffset) +{ + Expression* exp = ParseAssemblerMulOperand(pcasm, pcoffset); + while (mScanner->mToken == TK_ADD || mScanner->mToken == TK_SUB) + { + Expression* nexp = new Expression(mScanner->mLocation, EX_BINARY); + nexp->mToken = mScanner->mToken; + nexp->mLeft = exp; + mScanner->NextToken(); + nexp->mRight = ParseAssemblerMulOperand(pcasm, pcoffset); + if (!nexp->mLeft->mDecValue || !nexp->mRight->mDecValue) + { + mErrors->Error(mScanner->mLocation, EERR_INCOMPATIBLE_OPERATOR, "Invalid assembler operand"); + } + else if (nexp->mLeft->mDecValue->mType == DT_VARIABLE || nexp->mLeft->mDecValue->mType == DT_ARGUMENT) + { + if (nexp->mRight->mDecValue->mType == DT_CONST_INTEGER) + { + Declaration* ndec = new Declaration(mScanner->mLocation, DT_VARIABLE_REF); + ndec->mBase = nexp->mLeft->mDecValue; + ndec->mOffset = nexp->mToken == TK_SUB ? -int(nexp->mRight->mDecValue->mInteger) : int(nexp->mRight->mDecValue->mInteger); + exp = new Expression(mScanner->mLocation, EX_CONSTANT); + exp->mDecValue = ndec; + } + else + mErrors->Error(mScanner->mLocation, EERR_INCOMPATIBLE_OPERATOR, "Integer offset expected"); + } + else if (nexp->mLeft->mDecValue->mType == DT_VARIABLE_REF) + { + if (nexp->mRight->mDecValue->mType == DT_CONST_INTEGER) + { + Declaration* ndec = new Declaration(mScanner->mLocation, DT_VARIABLE_REF); + ndec->mBase = nexp->mLeft->mDecValue->mBase; + ndec->mOffset = nexp->mLeft->mDecValue->mOffset + (nexp->mToken == TK_SUB ? -int(nexp->mRight->mDecValue->mInteger) : int(nexp->mRight->mDecValue->mInteger)); + exp = new Expression(mScanner->mLocation, EX_CONSTANT); + exp->mDecValue = ndec; + } + else + mErrors->Error(mScanner->mLocation, EERR_INCOMPATIBLE_OPERATOR, "Integer offset expected"); + } + else if (nexp->mLeft->mDecValue->mType == DT_CONST_FUNCTION) + { + if (nexp->mRight->mDecValue->mType == DT_CONST_INTEGER) + { + Declaration* ndec = new Declaration(mScanner->mLocation, DT_FUNCTION_REF); + ndec->mBase = nexp->mLeft->mDecValue; + ndec->mOffset = nexp->mToken == TK_SUB ? -int(nexp->mRight->mDecValue->mInteger) : int(nexp->mRight->mDecValue->mInteger); + exp = new Expression(mScanner->mLocation, EX_CONSTANT); + exp->mDecValue = ndec; + } + else + mErrors->Error(mScanner->mLocation, EERR_INCOMPATIBLE_OPERATOR, "Integer offset expected"); + } + else if (nexp->mLeft->mDecValue->mType == DT_LABEL) + { + if (nexp->mRight->mDecValue->mType == DT_CONST_INTEGER) + { + Declaration* ndec = new Declaration(mScanner->mLocation, DT_LABEL_REF); + ndec->mBase = nexp->mLeft->mDecValue; + ndec->mOffset = nexp->mToken == TK_SUB ? -int(nexp->mRight->mDecValue->mInteger) : int(nexp->mRight->mDecValue->mInteger); + exp = new Expression(mScanner->mLocation, EX_CONSTANT); + exp->mDecValue = ndec; + } + else + mErrors->Error(mScanner->mLocation, EERR_INCOMPATIBLE_OPERATOR, "Integer offset expected"); + } + else + exp = nexp->ConstantFold(mErrors, mDataSection); + } + return exp; +} + +Expression* Parser::ParseAssemblerShiftOperand(Declaration* pcasm, int pcoffset) +{ + Expression* exp = ParseAssemblerAddOperand(pcasm, pcoffset); + while (mScanner->mToken == TK_LEFT_SHIFT || mScanner->mToken == TK_RIGHT_SHIFT) + { + Expression* nexp = new Expression(mScanner->mLocation, EX_BINARY); + nexp->mToken = mScanner->mToken; + nexp->mLeft = exp; + mScanner->NextToken(); + nexp->mRight = ParseAssemblerAddOperand(pcasm, pcoffset); + exp = nexp->ConstantFold(mErrors, mDataSection); + } + return exp; +} + + +Expression* Parser::ParseAssemblerAndOperand(Declaration* pcasm, int pcoffset) +{ + Expression* exp = ParseAssemblerShiftOperand(pcasm, pcoffset); + while (mScanner->mToken == TK_BINARY_AND) + { + Expression* nexp = new Expression(mScanner->mLocation, EX_BINARY); + nexp->mToken = mScanner->mToken; + nexp->mLeft = exp; + mScanner->NextToken(); + nexp->mRight = ParseAssemblerShiftOperand(pcasm, pcoffset); + exp = nexp->ConstantFold(mErrors, mDataSection); + } + return exp; +} + +Expression* Parser::ParseAssemblerOrOperand(Declaration* pcasm, int pcoffset) +{ + Expression* exp = ParseAssemblerAndOperand(pcasm, pcoffset); + while (mScanner->mToken == TK_BINARY_OR) + { + Expression* nexp = new Expression(mScanner->mLocation, EX_BINARY); + nexp->mToken = mScanner->mToken; + nexp->mLeft = exp; + mScanner->NextToken(); + nexp->mRight = ParseAssemblerAndOperand(pcasm, pcoffset); + exp = nexp->ConstantFold(mErrors, mDataSection); + } + return exp; +} + + +Expression* Parser::ParseAssemblerOperand(Declaration* pcasm, int pcoffset) +{ + if (mScanner->mToken == TK_LESS_THAN) + { + mScanner->NextToken(); + Expression* exp = ParseAssemblerOperand(pcasm, pcoffset); + + if (exp->mType == EX_CONSTANT) + { + if (exp->mDecValue->mType == DT_CONST_INTEGER) + { + Declaration* dec = new Declaration(mScanner->mLocation, DT_CONST_INTEGER); + dec->mInteger = exp->mDecValue->mInteger & 0xff; + dec->mBase = TheUnsignedIntTypeDeclaration; + exp->mDecValue = dec; + } + else if (exp->mDecValue->mType == DT_LABEL) + { + Declaration* ndec = new Declaration(mScanner->mLocation, DT_LABEL_REF); + ndec->mBase = exp->mDecValue; + ndec->mOffset = 0; + ndec->mFlags |= DTF_LOWER_BYTE; + exp->mDecValue = ndec; + } + else if (exp->mDecValue->mType == DT_LABEL_REF) + { + Declaration* ndec = new Declaration(mScanner->mLocation, DT_LABEL_REF); + ndec->mBase = exp->mDecValue->mBase; + ndec->mOffset = exp->mDecValue->mOffset; + ndec->mFlags |= DTF_LOWER_BYTE; + exp->mDecValue = ndec; + } + else if (exp->mDecValue->mType == DT_VARIABLE) + { + Declaration* ndec = new Declaration(mScanner->mLocation, DT_VARIABLE_REF); + ndec->mBase = exp->mDecValue; + ndec->mOffset = 0; + ndec->mFlags |= DTF_LOWER_BYTE; + exp->mDecValue = ndec; + } + else if (exp->mDecValue->mType == DT_VARIABLE_REF) + { + Declaration* ndec = new Declaration(mScanner->mLocation, DT_VARIABLE_REF); + ndec->mBase = exp->mDecValue->mBase; + ndec->mOffset = exp->mDecValue->mOffset; + ndec->mFlags |= DTF_LOWER_BYTE; + exp->mDecValue = ndec; + } + else if (exp->mDecValue->mType == DT_CONST_FUNCTION) + { + Declaration* ndec = new Declaration(mScanner->mLocation, DT_FUNCTION_REF); + ndec->mBase = exp->mDecValue; + ndec->mOffset = 0; + ndec->mFlags |= DTF_LOWER_BYTE; + exp->mDecValue = ndec; + } + else if (exp->mDecValue->mType == DT_FUNCTION_REF) + { + Declaration* ndec = new Declaration(mScanner->mLocation, DT_FUNCTION_REF); + ndec->mBase = exp->mDecValue->mBase; + ndec->mOffset = exp->mDecValue->mOffset; + ndec->mFlags |= DTF_LOWER_BYTE; + exp->mDecValue = ndec; + } + else + mErrors->Error(mScanner->mLocation, EERR_INCOMPATIBLE_OPERATOR, "Label or integer value for lower byte operator expected"); + } + else + mErrors->Error(mScanner->mLocation, EERR_INCOMPATIBLE_OPERATOR, "Constant for lower byte operator expected"); + + return exp; + } + else if (mScanner->mToken == TK_GREATER_THAN) + { + mScanner->NextToken(); + Expression* exp = ParseAssemblerOperand(pcasm, pcoffset); + + if (exp->mType == EX_CONSTANT) + { + if (exp->mDecValue->mType == DT_CONST_INTEGER) + { + Declaration* dec = new Declaration(mScanner->mLocation, DT_CONST_INTEGER); + dec->mInteger = (exp->mDecValue->mInteger >> 8) & 0xff; + dec->mBase = TheUnsignedIntTypeDeclaration; + exp->mDecValue = dec; + } + else if (exp->mDecValue->mType == DT_LABEL) + { + Declaration* ndec = new Declaration(mScanner->mLocation, DT_LABEL_REF); + ndec->mBase = exp->mDecValue; + ndec->mOffset = 0; + ndec->mFlags |= DTF_UPPER_BYTE; + exp->mDecValue = ndec; + } + else if (exp->mDecValue->mType == DT_LABEL_REF) + { + Declaration* ndec = new Declaration(mScanner->mLocation, DT_LABEL_REF); + ndec->mBase = exp->mDecValue->mBase; + ndec->mOffset = exp->mDecValue->mOffset; + ndec->mFlags |= DTF_UPPER_BYTE; + exp->mDecValue = ndec; + } + else if (exp->mDecValue->mType == DT_VARIABLE) + { + Declaration* ndec = new Declaration(mScanner->mLocation, DT_VARIABLE_REF); + ndec->mBase = exp->mDecValue; + ndec->mOffset = 0; + ndec->mFlags |= DTF_UPPER_BYTE; + exp->mDecValue = ndec; + } + else if (exp->mDecValue->mType == DT_VARIABLE_REF) + { + Declaration* ndec = new Declaration(mScanner->mLocation, DT_VARIABLE_REF); + ndec->mBase = exp->mDecValue->mBase; + ndec->mOffset = exp->mDecValue->mOffset; + ndec->mFlags |= DTF_UPPER_BYTE; + exp->mDecValue = ndec; + } + else if (exp->mDecValue->mType == DT_CONST_FUNCTION) + { + Declaration* ndec = new Declaration(mScanner->mLocation, DT_FUNCTION_REF); + ndec->mBase = exp->mDecValue; + ndec->mOffset = 0; + ndec->mFlags |= DTF_UPPER_BYTE; + exp->mDecValue = ndec; + } + else if (exp->mDecValue->mType == DT_FUNCTION_REF) + { + Declaration* ndec = new Declaration(mScanner->mLocation, DT_FUNCTION_REF); + ndec->mBase = exp->mDecValue->mBase; + ndec->mOffset = exp->mDecValue->mOffset; + ndec->mFlags |= DTF_UPPER_BYTE; + exp->mDecValue = ndec; + } + else + mErrors->Error(mScanner->mLocation, EERR_INCOMPATIBLE_OPERATOR, "Label or integer value for lower byte operator expected"); + } + else + mErrors->Error(mScanner->mLocation, EERR_INCOMPATIBLE_OPERATOR, "Constant for upper byte operator expected"); + + return exp; + } + else + return ParseAssemblerOrOperand(pcasm, pcoffset); +} + +void Parser::AddAssemblerRegister(const Ident* ident, int value) +{ + Declaration* decaccu = new Declaration(mScanner->mLocation, DT_CONST_INTEGER); + decaccu->mIdent = ident; + decaccu->mQualIdent = ident; + decaccu->mBase = TheUnsignedIntTypeDeclaration; + decaccu->mSize = 2; + decaccu->mInteger = value; + mScope->Insert(decaccu->mIdent, decaccu); +} + +Expression* Parser::ParseAssembler(Declaration* vdasm) +{ + Declaration* dasm; + if (!vdasm) + { + dasm = new Declaration(mScanner->mLocation, DT_TYPE_ASSEMBLER); + dasm->mScope = new DeclarationScope(mScope, SLEVEL_LOCAL); + + vdasm = new Declaration(mScanner->mLocation, DT_CONST_ASSEMBLER); + vdasm->mVarIndex = -1; + vdasm->mSection = mCodeSection; + + vdasm->mBase = dasm; + } + else + dasm = vdasm->mBase; + + + if (!(mCompilerOptions & COPT_OPTIMIZE_ASSEMBLER)) + vdasm->mFlags |= DTF_VOLATILE; + + if (!(vdasm->mFlags & DTF_VOLATILE)) + vdasm->mFlags |= DTF_ASM_PRESERVE_A | DTF_ASM_PRESERVE_X | DTF_ASM_PRESERVE_Y; + + DeclarationScope* scope = dasm->mScope; + mScope = scope; + + mScanner->SetAssemblerMode(true); + + AddAssemblerRegister(Ident::Unique("__tmpy"), BC_REG_WORK_Y); + AddAssemblerRegister(Ident::Unique("__tmp"), BC_REG_WORK); + AddAssemblerRegister(Ident::Unique("__ip"), BC_REG_IP); + AddAssemblerRegister(Ident::Unique("__accu"), BC_REG_ACCU); + AddAssemblerRegister(Ident::Unique("__addr"), BC_REG_ADDR); + AddAssemblerRegister(Ident::Unique("__fp"), BC_REG_LOCALS); + AddAssemblerRegister(Ident::Unique("__sp"), BC_REG_STACK); + AddAssemblerRegister(Ident::Unique("__sregs"), BC_REG_TMP_SAVED); + AddAssemblerRegister(Ident::Unique("__regs"), BC_REG_TMP); + + AddAssemblerRegister(Ident::Unique("accu"), BC_REG_ACCU); + + Declaration* decaccu = new Declaration(mScanner->mLocation, DT_CONST_INTEGER); + decaccu->mIdent = Ident::Unique("accu"); + decaccu->mQualIdent = decaccu->mIdent; + decaccu->mBase = TheUnsignedIntTypeDeclaration; + decaccu->mSize = 2; + decaccu->mInteger = BC_REG_ACCU; + mScope->Insert(decaccu->mIdent, decaccu); + + + Expression* ifirst = new Expression(mScanner->mLocation, EX_ASSEMBLER); + Expression* ilast = ifirst, * ifinal = ifirst; + + bool exitLabel = false; + + ifirst->mDecType = dasm; + ifirst->mDecValue = vdasm; + vdasm->mValue = ifirst; + + int offset = 0; + + while (mScanner->mToken != TK_CLOSE_BRACE && mScanner->mToken != TK_EOF) + { + if (mScanner->mToken == TK_IDENT) + { + AsmInsType ins = FindAsmInstruction(mScanner->mTokenIdent->mString); + if (ins == ASMIT_INV) + { + const Ident* label = mScanner->mTokenIdent; + mScanner->NextToken(); + if (mScanner->mToken != TK_COLON) + mErrors->Error(mScanner->mLocation, EERR_SYNTAX, "':' expected"); + else + mScanner->NextToken(); + + exitLabel = true; + + Declaration* dec = mScope->Lookup(label, SLEVEL_STATIC); + if (dec) + { + if (dec->mType != DT_LABEL || dec->mBase) + mErrors->Error(mScanner->mLocation, EERR_DUPLICATE_DEFINITION, "Duplicate label definition"); + } + else + dec = new Declaration(mScanner->mLocation, DT_LABEL); + + dec->mIdent = label; + dec->mQualIdent = dec->mIdent; + dec->mValue = ilast; + dec->mInteger = offset; + dec->mBase = vdasm; + mScope->Insert(dec->mIdent, dec); + } + else + { + exitLabel = false; + + ilast->mAsmInsType = ins; + mScanner->NextToken(); + if (ins == ASMIT_BYTE) + { + for (;;) + { + ilast->mAsmInsMode = ASMIM_IMMEDIATE; + if (mScanner->mToken == TK_STRING) + { + if (mScanner->mTokenString[0]) + { + int i = 0; + while (mScanner->mTokenString[i]) + { + Declaration* dec = new Declaration(mScanner->mLocation, DT_CONST_INTEGER); + dec->mInteger = mCharMap[mScanner->mTokenString[i]]; + dec->mBase = TheUnsignedCharTypeDeclaration; + Expression* exp = new Expression(mScanner->mLocation, EX_CONSTANT); + exp->mDecValue = dec; + exp->mDecType = dec->mBase; + ilast->mLeft = exp; + + i++; + if (mScanner->mTokenString[i]) + { + offset += AsmInsSize(ilast->mAsmInsType, ilast->mAsmInsMode); + + ifinal = ilast; + + ilast->mRight = new Expression(mScanner->mLocation, EX_ASSEMBLER); + ilast = ilast->mRight; + ilast->mAsmInsType = ASMIT_BYTE; + ilast->mAsmInsMode = ASMIM_IMMEDIATE; + } + } + } + else + mErrors->Error(mScanner->mLocation, EERR_ASM_INVALID_INSTRUCTION, "Invalid assembler token"); + mScanner->NextToken(); + } + else + ilast->mLeft = ParseAssemblerOperand(vdasm, offset); + if (ConsumeTokenIf(TK_COMMA)) + { + offset += AsmInsSize(ilast->mAsmInsType, ilast->mAsmInsMode); + + ifinal = ilast; + + ilast->mRight = new Expression(mScanner->mLocation, EX_ASSEMBLER); + ilast = ilast->mRight; + ilast->mAsmInsType = ASMIT_BYTE; + } + else + break; + } + } + else if (mScanner->mToken == TK_EOL || mScanner->mToken == TK_CLOSE_BRACE || mScanner->mToken == TK_SEMICOLON || mScanner->mToken == TK_COLON) + ilast->mAsmInsMode = ASMIM_IMPLIED; + else if (mScanner->mToken == TK_HASH) + { + ilast->mAsmInsMode = ASMIM_IMMEDIATE; + mScanner->NextToken(); + ilast->mLeft = ParseAssemblerOperand(vdasm, offset); + } + else if (mScanner->mToken == TK_OPEN_PARENTHESIS) + { + mScanner->NextToken(); + ilast->mLeft = ParseAssemblerOperand(vdasm, offset); + if (mScanner->mToken == TK_COMMA) + { + mScanner->NextToken(); + if (mScanner->mToken == TK_IDENT && (!strcmp(mScanner->mTokenIdent->mString, "x") || !strcmp(mScanner->mTokenIdent->mString, "X"))) + { + ilast->mAsmInsMode = ASMIM_INDIRECT_X; + mScanner->NextToken(); + if (mScanner->mToken == TK_CLOSE_PARENTHESIS) + mScanner->NextToken(); + else + mErrors->Error(mScanner->mLocation, EERR_SYNTAX, "')' expected"); + } + else + mErrors->Error(mScanner->mLocation, EERR_SYNTAX, "',x' expected"); + } + else if (mScanner->mToken == TK_CLOSE_PARENTHESIS) + { + mScanner->NextToken(); + if (mScanner->mToken == TK_COMMA) + { + mScanner->NextToken(); + if (mScanner->mToken == TK_IDENT && (!strcmp(mScanner->mTokenIdent->mString, "y") || !strcmp(mScanner->mTokenIdent->mString, "Y"))) + { + ilast->mAsmInsMode = ASMIM_INDIRECT_Y; + mScanner->NextToken(); + } + else + mErrors->Error(mScanner->mLocation, EERR_SYNTAX, "',y' expected"); + } + else + { + ilast->mAsmInsMode = ASMIM_INDIRECT; + } + } + else + mErrors->Error(mScanner->mLocation, EERR_SYNTAX, "',' or ')' expected"); + } + else + { + ilast->mLeft = ParseAssemblerOperand(vdasm, offset); + if (mScanner->mToken == TK_COMMA) + { + mScanner->NextToken(); + if (mScanner->mToken == TK_IDENT && (!strcmp(mScanner->mTokenIdent->mString, "x") || !strcmp(mScanner->mTokenIdent->mString, "X"))) + { + ilast->mAsmInsMode = ASMIM_ABSOLUTE_X; + mScanner->NextToken(); + } + else if (mScanner->mToken == TK_IDENT && (!strcmp(mScanner->mTokenIdent->mString, "y") || !strcmp(mScanner->mTokenIdent->mString, "Y"))) + { + ilast->mAsmInsMode = ASMIM_ABSOLUTE_Y; + mScanner->NextToken(); + } + else + mErrors->Error(mScanner->mLocation, EERR_SYNTAX, "',x' or ',y' expected"); + } + else + { + if (HasAsmInstructionMode(ilast->mAsmInsType, ASMIM_RELATIVE)) + ilast->mAsmInsMode = ASMIM_RELATIVE; + else if ( + HasAsmInstructionMode(ilast->mAsmInsType, ASMIM_IMMEDIATE) && + ilast->mLeft && + ilast->mLeft->mType == EX_CONSTANT && + ilast->mLeft->mDecValue->mType == DT_VARIABLE && + (ilast->mLeft->mDecValue->mFlags & DTF_CONST) && + (ilast->mLeft->mDecValue->mFlags & DTF_STATIC) && + ilast->mLeft->mDecType->IsIntegerType() && + ilast->mLeft->mDecValue->mValue && + ilast->mLeft->mDecValue->mValue->mType == EX_CONSTANT) + { + ilast->mAsmInsMode = ASMIM_IMMEDIATE; + ilast->mLeft = ilast->mLeft->mDecValue->mValue; + } + else + ilast->mAsmInsMode = ASMIM_ABSOLUTE; + } + } + + if (ilast->mLeft && ilast->mLeft->mDecValue) + { + if ((ilast->mLeft->mDecValue->mType == DT_CONST_INTEGER && ilast->mLeft->mDecValue->mInteger >= 0 && ilast->mLeft->mDecValue->mInteger < 256) || + (ilast->mLeft->mDecValue->mType == DT_VARIABLE_REF && !(ilast->mLeft->mDecValue->mBase->mFlags & DTF_GLOBAL)) || + (ilast->mLeft->mDecValue->mType == DT_VARIABLE && !(ilast->mLeft->mDecValue->mFlags & DTF_GLOBAL)) || + (ilast->mLeft->mDecValue->mType == DT_VARIABLE && (ilast->mLeft->mDecValue->mFlags & DTF_ZEROPAGE)) || + ilast->mLeft->mDecValue->mType == DT_ARGUMENT) + { + if (ilast->mAsmInsMode == ASMIM_ABSOLUTE && HasAsmInstructionMode(ilast->mAsmInsType, ASMIM_ZERO_PAGE)) + ilast->mAsmInsMode = ASMIM_ZERO_PAGE; + else if (ilast->mAsmInsMode == ASMIM_ABSOLUTE_X && HasAsmInstructionMode(ilast->mAsmInsType, ASMIM_ZERO_PAGE_X)) + ilast->mAsmInsMode = ASMIM_ZERO_PAGE_X; + else if (ilast->mAsmInsMode == ASMIM_ABSOLUTE_Y && HasAsmInstructionMode(ilast->mAsmInsType, ASMIM_ZERO_PAGE_Y)) + ilast->mAsmInsMode = ASMIM_ZERO_PAGE_Y; + } + } + + offset += AsmInsSize(ilast->mAsmInsType, ilast->mAsmInsMode); + + uint32 flags = AsmInsFlags(ilast->mAsmInsType, ilast->mAsmInsMode); + if (ConsumeTokenIf(TK_COLON)) + { + flags &= ~(ASMIFLG_USES_ACCU | ASMIFLG_USES_XREG | ASMIFLG_USES_YREG | ASMIFLG_CHANGES_ACCU | ASMIFLG_CHANGES_XREG | ASMIFLG_CHANGES_YREG); + + if (mScanner->mToken == TK_IDENT) + { + char* cp = mScanner->mTokenIdent->mString; + while (*cp) + { + switch (*cp) + { + case 'a': + case 'A': + flags |= ASMIFLG_USES_ACCU; + break; + case 'x': + case 'X': + flags |= ASMIFLG_USES_XREG; + break; + case 'y': + case 'Y': + flags |= ASMIFLG_USES_YREG; + break; + default: + mErrors->Error(mScanner->mLocation, EERR_ASM_INVALID_REGISTER, "Invalid register specified", mScanner->mTokenIdent); + } + cp++; + } + mScanner->NextToken(); + } + ConsumeToken(TK_ARROW); + if (mScanner->mToken == TK_IDENT) + { + char* cp = mScanner->mTokenIdent->mString; + while (*cp) + { + switch (*cp) + { + case 'a': + case 'A': + flags |= ASMIFLG_CHANGES_ACCU; + break; + case 'x': + case 'X': + flags |= ASMIFLG_CHANGES_XREG; + break; + case 'y': + case 'Y': + flags |= ASMIFLG_CHANGES_YREG; + break; + default: + mErrors->Error(mScanner->mLocation, EERR_ASM_INVALID_REGISTER, "Invalid register specified", mScanner->mTokenIdent); + } + cp++; + } + mScanner->NextToken(); + } + } + + if (flags & ASMIFLG_CHANGES_ACCU) vdasm->mFlags &= ~DTF_ASM_PRESERVE_A; + if (flags & ASMIFLG_CHANGES_XREG) vdasm->mFlags &= ~DTF_ASM_PRESERVE_X; + if (flags & ASMIFLG_CHANGES_YREG) vdasm->mFlags &= ~DTF_ASM_PRESERVE_Y; + + ifinal = ilast; + + ilast->mRight = new Expression(mScanner->mLocation, EX_ASSEMBLER); + ilast = ilast->mRight; + + if (mScanner->mToken != TK_EOL && mScanner->mToken != TK_CLOSE_BRACE && mScanner->mToken != TK_SEMICOLON) + { + mErrors->Error(mScanner->mLocation, EERR_SYNTAX, "End of line expected"); + } + + while (mScanner->mToken != TK_EOL && mScanner->mToken != TK_EOF && mScanner->mToken != TK_CLOSE_BRACE && mScanner->mToken != TK_SEMICOLON) + mScanner->NextToken(); + } + } + else if (mScanner->mToken == TK_EOL || mScanner->mToken == TK_SEMICOLON) + { + mScanner->NextToken(); + } + else + { + mErrors->Error(mScanner->mLocation, EERR_ASM_INVALID_INSTRUCTION, "Invalid assembler token"); + + while (mScanner->mToken != TK_EOL && mScanner->mToken != TK_CLOSE_BRACE && mScanner->mToken != TK_SEMICOLON && mScanner->mToken != TK_EOF) + mScanner->NextToken(); + } + } + + if ((ifinal->mAsmInsType == ASMIT_RTS || ifinal->mAsmInsType == ASMIT_JMP) && !exitLabel) + { + delete ilast; + ilast = ifinal; + ifinal->mRight = nullptr; + } + else + { + ilast->mAsmInsType = ASMIT_RTS; + ilast->mAsmInsMode = ASMIM_IMPLIED; + } + +#if 0 + offset = 0; + ilast = ifirst; + while (ilast) + { + if (ilast->mLeft && ilast->mLeft->mType == EX_UNDEFINED) + { + Declaration* dec = mScope->Lookup(ilast->mLeft->mDecValue->mIdent); + if (dec) + { + ilast->mLeft->mType = EX_CONSTANT; + ilast->mLeft->mDecValue = dec; + } + else + mErrors->Error(ilast->mLeft->mLocation, "Undefined label", ilast->mLeft->mDecValue->mIdent->mString); + } + offset += AsmInsSize(ilast->mAsmInsType, ilast->mAsmInsMode); + + ilast = ilast->mRight; + } +#endif + + mScope->End(mScanner->mLocation); + mScope = mScope->mParent; + dasm->mSize = offset; + dasm->mScope->mParent = nullptr; + + mScanner->SetAssemblerMode(false); + + vdasm->mFlags |= DTF_DEFINED; + + return ifirst; +} + +bool Parser::ExpectToken(Token token) +{ + if (mScanner->mToken == token) + { + return true; + } + else + { + char buffer[100]; + sprintf_s(buffer, "%s expected", TokenNames[token]); + mErrors->Error(mScanner->mLocation, EERR_SYNTAX, buffer); + return false; + } +} + +bool Parser::ConsumeToken(Token token) +{ + if (mScanner->mToken == token) + { + mScanner->NextToken(); + return true; + } + else + { + char buffer[100]; + sprintf_s(buffer, "%s expected", TokenNames[token]); + mErrors->Error(mScanner->mLocation, EERR_SYNTAX, buffer); + return false; + } +} + +bool Parser::ConsumeTokenIf(Token token) +{ + if (mScanner->mToken == token) + { + mScanner->NextToken(); + return true; + } + else + return false; +} + +bool Parser::ConsumeIdentIf(const char* ident) +{ + if (mScanner->mToken == TK_IDENT && !strcmp(ident, mScanner->mTokenIdent->mString)) + { + mScanner->NextToken(); + return true; + } + else + return false; +} + +bool Parser::IsTypeToken(void) +{ + switch (mScanner->mToken) + { + case TK_INT: + case TK_SHORT: + case TK_LONG: + case TK_FLOAT: + case TK_CHAR: + case TK_BOOL: + case TK_VOID: + case TK_UNSIGNED: + case TK_SIGNED: + case TK_CONST: + case TK_VOLATILE: + case TK_STRUCT: + case TK_CLASS: + case TK_UNION: + case TK_ENUM: + case TK_TYPEDEF: + case TK_STATIC: + case TK_AUTO: + case TK_STRIPED: + case TK_DECLTYPE: + case TK_MEMMAP: + case TK_TYPENAME: + return true; + case TK_IDENT: + { + const Ident* pident = mScanner->mTokenIdent; + + if (mTemplateScope && mTemplateScope->Lookup(pident)) + return true; + + Declaration* dec = nullptr; + + if (mThisPointer && !mScope->Lookup(pident, SLEVEL_FUNCTION)) + { + int offset; + uint64 flags; + + if (mThisPointer->mType == DT_ARGUMENT) + dec = MemberLookup(mThisPointer->mBase->mBase, mScanner->mTokenIdent, offset, flags); + else if (mThisPointer->mType == DT_TYPE_POINTER) + dec = MemberLookup(mThisPointer->mBase, mScanner->mTokenIdent, offset, flags); + } + + if (!dec) + dec = mScope->Lookup(pident); + + if (!dec) + return true; + + if (dec->mType <= DT_TYPE_AUTO || dec->mType == DT_NAMESPACE) + return true; + + + } break; + + } + + return false; +} + +bool Parser::IsIntegerToken(void) +{ + return (mScanner->mToken == TK_INTEGER || mScanner->mToken == TK_INTEGERL || mScanner->mToken == TK_INTEGERU || mScanner->mToken == TK_INTEGERUL); +} + +void Parser::ParsePragma(void) +{ + uint64 op = mScanner->mCompilerOptions; + mScanner->mCompilerOptions &= ~COPT_PETSCII; + + if (mScanner->mToken == TK_IDENT) + { + if (!strcmp(mScanner->mTokenIdent->mString, "message")) + { + mScanner->NextToken(); + ConsumeToken(TK_OPEN_PARENTHESIS); + do { + if (mScanner->mToken == TK_STRING) + { + printf("%s", mScanner->mTokenString); + mScanner->NextToken(); + } + else + { + Expression* exp = ParseRExpression(); + if (exp->mType == EX_CONSTANT) + { + switch (exp->mDecValue->mType) + { + case DT_CONST_INTEGER: + printf("%lld", exp->mDecValue->mInteger); + break; + case DT_CONST_FLOAT: + printf("%g", exp->mDecValue->mNumber); + break; + } + } + } + } while (ConsumeTokenIf(TK_COMMA)); + printf("\n"); + ConsumeToken(TK_CLOSE_PARENTHESIS); + } + else if (!strcmp(mScanner->mTokenIdent->mString, "warning")) + { + mScanner->NextToken(); + ConsumeToken(TK_OPEN_PARENTHESIS); + if (ConsumeIdentIf("disable")) + { + ConsumeToken(TK_COLON); + do { + if (ExpectToken(TK_INTEGER) && mScanner->mTokenInteger < EERR_GENERIC) + { + mErrors->mDisabled += int(mScanner->mTokenInteger); + mScanner->NextToken(); + } + } while (ConsumeTokenIf(TK_COMMA)); + } + else if (ConsumeToken(TK_DEFAULT)) + { + ConsumeToken(TK_COLON); + do { + if (ExpectToken(TK_INTEGER) && mScanner->mTokenInteger < EERR_GENERIC) + { + mErrors->mDisabled -= int(mScanner->mTokenInteger); + mScanner->NextToken(); + } + } while (ConsumeTokenIf(TK_COMMA)); + } + ConsumeToken(TK_CLOSE_PARENTHESIS); + } + else if (!strcmp(mScanner->mTokenIdent->mString, "once")) + { + mScanner->MarkSourceOnce(); + mScanner->NextToken(); + } + else if (!strcmp(mScanner->mTokenIdent->mString, "compile")) + { + mScanner->NextToken(); + ConsumeToken(TK_OPEN_PARENTHESIS); + if (mScanner->mToken == TK_STRING) + { + mCompilationUnits->AddUnit(mScanner->mLocation, (const char *)mScanner->mTokenString, mScanner->mLocation.mFileName); + mScanner->NextToken(); + } + ConsumeToken(TK_CLOSE_PARENTHESIS); + } + else if (!strcmp(mScanner->mTokenIdent->mString, "intrinsic")) + { + mScanner->NextToken(); + ConsumeToken(TK_OPEN_PARENTHESIS); + if (mScanner->mToken == TK_IDENT) + { + Declaration* dec = mGlobals->Lookup(mScanner->mTokenIdent); + if (dec && dec->mType == DT_CONST_FUNCTION) + dec->mFlags |= DTF_INTRINSIC; + else + mErrors->Error(mScanner->mLocation, EERR_OBJECT_NOT_FOUND, "Intrinsic function not found"); + mScanner->NextToken(); + } + ConsumeToken(TK_CLOSE_PARENTHESIS); + } + else if (!strcmp(mScanner->mTokenIdent->mString, "native")) + { + mScanner->NextToken(); + ConsumeToken(TK_OPEN_PARENTHESIS); + if (mScanner->mToken == TK_IDENT) + { + Declaration* dec = mGlobals->Lookup(mScanner->mTokenIdent); + if (!dec) + dec = mScope->Lookup(mScanner->mTokenIdent); + if (dec && dec->mType == DT_CONST_FUNCTION) + dec->mFlags |= DTF_NATIVE; + else + mErrors->Error(mScanner->mLocation, EERR_OBJECT_NOT_FOUND, "Native function not found"); + mScanner->NextToken(); + } + ConsumeToken(TK_CLOSE_PARENTHESIS); + } + else if (!strcmp(mScanner->mTokenIdent->mString, "startup")) + { + if (mCompilationUnits->mStartup) + mErrors->Error(mScanner->mLocation, EERR_DUPLICATE_DEFINITION, "Duplicate startup pragma"); + + mScanner->NextToken(); + ConsumeToken(TK_OPEN_PARENTHESIS); + if (mScanner->mToken == TK_IDENT) + { + Declaration* dec = mGlobals->Lookup(mScanner->mTokenIdent); + if (dec && dec->mType == DT_CONST_ASSEMBLER) + mCompilationUnits->mStartup = dec; + else + mErrors->Error(mScanner->mLocation, EERR_OBJECT_NOT_FOUND, "Startup function not found"); + mScanner->NextToken(); + } + ConsumeToken(TK_CLOSE_PARENTHESIS); + } + else if (!strcmp(mScanner->mTokenIdent->mString, "register")) + { + mScanner->NextToken(); + ConsumeToken(TK_OPEN_PARENTHESIS); + if (mScanner->mToken == TK_IDENT) + { + const Ident* reg = mScanner->mTokenIdent; + int index = 0; + mScanner->NextToken(); + + ConsumeToken(TK_COMMA); + Expression* exp = ParseRExpression(); + if (exp->mType == EX_CONSTANT && exp->mDecValue->mType == DT_CONST_INTEGER && exp->mDecValue->mInteger >= 2 && exp->mDecValue->mInteger < 0x100) + index = int(exp->mDecValue->mInteger); + else + mErrors->Error(mScanner->mLocation, EERR_PRAGMA_PARAMETER, "Integer number for start expected"); + + if (!strcmp(reg->mString, "__tmpy")) + BC_REG_WORK_Y = index; + else if (!strcmp(reg->mString, "__tmp")) + BC_REG_WORK = index; + else if (!strcmp(reg->mString, "__ip")) + BC_REG_IP = index; + else if (!strcmp(reg->mString, "__accu")) + BC_REG_ACCU = index; + else if (!strcmp(reg->mString, "__addr")) + BC_REG_ADDR = index; + else if (!strcmp(reg->mString, "__sp")) + BC_REG_STACK = index; + else if (!strcmp(reg->mString, "__fp")) + BC_REG_LOCALS = index; + else if (!strcmp(reg->mString, "__sregs")) + BC_REG_TMP_SAVED = index; + else if (!strcmp(reg->mString, "__regs")) + BC_REG_TMP = index; + else + mErrors->Error(mScanner->mLocation, EERR_PRAGMA_PARAMETER, "Unknown register name"); + } + else + mErrors->Error(mScanner->mLocation, EERR_PRAGMA_PARAMETER, "Register name expected"); + ConsumeToken(TK_CLOSE_PARENTHESIS); + } + else if (!strcmp(mScanner->mTokenIdent->mString, "bytecode")) + { + mScanner->NextToken(); + ConsumeToken(TK_OPEN_PARENTHESIS); + Expression* exp = ParseAssemblerOperand(nullptr, 0); + + ConsumeToken(TK_COMMA); + if (mScanner->mToken == TK_IDENT) + { + Declaration* dec = mGlobals->Lookup(mScanner->mTokenIdent); + if (dec && dec->mType == DT_CONST_ASSEMBLER) + { + mScanner->NextToken(); + if (ConsumeTokenIf(TK_DOT)) + { + if (mScanner->mToken == TK_IDENT) + { + Declaration* ndec = dec->mBase->mScope->Lookup(mScanner->mTokenIdent); + if (ndec) + dec = ndec; + else + mErrors->Error(mScanner->mLocation, EERR_OBJECT_NOT_FOUND, "Label not found in assembler code"); + mScanner->NextToken(); + } + else + mErrors->Error(mScanner->mLocation, EERR_SYNTAX, "Identifier expected"); + } + + if (exp->mType == EX_CONSTANT && exp->mDecValue->mType == DT_CONST_INTEGER && exp->mDecValue->mInteger >= 0 && exp->mDecValue->mInteger < 256) + { + if (mCompilationUnits->mByteCodes[exp->mDecValue->mInteger]) + mErrors->Error(mScanner->mLocation, EERR_DUPLICATE_DEFINITION, "Duplicate bytecode function"); + + mCompilationUnits->mByteCodes[exp->mDecValue->mInteger] = dec; + } + else + mErrors->Error(exp->mLocation, EERR_CONSTANT_TYPE, "Numeric value for byte code expected"); + } + else + { + mErrors->Error(mScanner->mLocation, EERR_OBJECT_NOT_FOUND, "Bytecode function not found"); + mScanner->NextToken(); + } + + } + ConsumeToken(TK_CLOSE_PARENTHESIS); + } + else if (!strcmp(mScanner->mTokenIdent->mString, "runtime")) + { + mScanner->NextToken(); + ConsumeToken(TK_OPEN_PARENTHESIS); + const Ident* rtident = nullptr; + + if (mScanner->mToken == TK_IDENT) + { + rtident = mScanner->mTokenIdent; + mScanner->NextToken(); + } + else + mErrors->Error(mScanner->mLocation, EERR_SYNTAX, "Identifier expected"); + + ConsumeToken(TK_COMMA); + if (mScanner->mToken == TK_IDENT) + { + Declaration* dec = mGlobals->Lookup(mScanner->mTokenIdent); + if (dec && dec->mType == DT_CONST_ASSEMBLER) + { + mScanner->NextToken(); + if (ConsumeTokenIf(TK_DOT)) + { + if (mScanner->mToken == TK_IDENT) + { + Declaration* ndec = dec->mBase->mScope->Lookup(mScanner->mTokenIdent); + if (ndec) + dec = ndec; + else + mErrors->Error(mScanner->mLocation, EERR_OBJECT_NOT_FOUND, "Label not found in assembler code"); + mScanner->NextToken(); + } + else + mErrors->Error(mScanner->mLocation, EERR_SYNTAX, "Identifier expected"); + } + + if (rtident) + mCompilationUnits->mRuntimeScope->Insert(rtident, dec); + } + else if (dec && dec->mType == DT_VARIABLE && (dec->mFlags & DTF_GLOBAL)) + { + if (rtident) + mCompilationUnits->mRuntimeScope->Insert(rtident, dec); + mScanner->NextToken(); + } + else + { + mErrors->Error(mScanner->mLocation, EERR_OBJECT_NOT_FOUND, "Runtime function not found", rtident); + mScanner->NextToken(); + } + + } + ConsumeToken(TK_CLOSE_PARENTHESIS); + } + else if (!strcmp(mScanner->mTokenIdent->mString, "stacksize")) + { + mScanner->NextToken(); + ConsumeToken(TK_OPEN_PARENTHESIS); + if (IsIntegerToken()) + { + mCompilationUnits->mSectionStack->mSize = int(mScanner->mTokenInteger); + mScanner->NextToken(); + } + else + mErrors->Error(mScanner->mLocation, EERR_PRAGMA_PARAMETER, "Stack size expected"); + + ConsumeToken(TK_CLOSE_PARENTHESIS); + } + else if (!strcmp(mScanner->mTokenIdent->mString, "heapsize")) + { + mScanner->NextToken(); + ConsumeToken(TK_OPEN_PARENTHESIS); + if (IsIntegerToken()) + { + mCompilationUnits->mSectionHeap->mSize = int(mScanner->mTokenInteger); + mScanner->NextToken(); + } + else + mErrors->Error(mScanner->mLocation, EERR_PRAGMA_PARAMETER, "Heap size expected"); + + ConsumeToken(TK_CLOSE_PARENTHESIS); + } + else if (!strcmp(mScanner->mTokenIdent->mString, "charmap")) + { + mScanner->NextToken(); + ConsumeToken(TK_OPEN_PARENTHESIS); + if (IsIntegerToken()) + { + int cindex = int(mScanner->mTokenInteger); + mScanner->NextToken(); + ConsumeToken(TK_COMMA); + + if (IsIntegerToken()) + { + int ccode = int(mScanner->mTokenInteger); + int ccount = 1; + mScanner->NextToken(); + if (ConsumeTokenIf(TK_COMMA)) + { + if (IsIntegerToken()) + { + ccount = int(mScanner->mTokenInteger); + mScanner->NextToken(); + } + else + mErrors->Error(mScanner->mLocation, EERR_PRAGMA_PARAMETER, "Character run expected"); + } + + for (int i = 0; i < ccount; i++) + { + mCharMap[cindex] = ccode; + cindex = (cindex + 1) & 255; + ccode = (ccode + 1) & 255; + } + } + else + mErrors->Error(mScanner->mLocation, EERR_PRAGMA_PARAMETER, "Character code expected"); + } + else + mErrors->Error(mScanner->mLocation, EERR_PRAGMA_PARAMETER, "Character index expected"); + + ConsumeToken(TK_CLOSE_PARENTHESIS); + } + else if (!strcmp(mScanner->mTokenIdent->mString, "region")) + { + mScanner->NextToken(); + ConsumeToken(TK_OPEN_PARENTHESIS); + + if (mScanner->mToken == TK_IDENT) + { + const Ident* regionIdent = mScanner->mTokenIdent; + mScanner->NextToken(); + + Expression* exp; + int start = 0, end = 0, flags = 0; + uint64 bank = 0; + + ConsumeToken(TK_COMMA); + + exp = ParseRExpression(); + if (exp->mType == EX_CONSTANT && (exp->mDecValue->mType == DT_CONST_INTEGER || exp->mDecValue->mType == DT_CONST_ADDRESS)) + start = int(exp->mDecValue->mInteger); + else + mErrors->Error(mScanner->mLocation, EERR_PRAGMA_PARAMETER, "Integer number for start expected"); + + ConsumeToken(TK_COMMA); + + exp = ParseRExpression(); + if (exp->mType == EX_CONSTANT && (exp->mDecValue->mType == DT_CONST_INTEGER || exp->mDecValue->mType == DT_CONST_ADDRESS)) + end = int(exp->mDecValue->mInteger); + else + mErrors->Error(mScanner->mLocation, EERR_PRAGMA_PARAMETER, "Integer number for end expected"); + + ConsumeToken(TK_COMMA); + + if (mScanner->mToken != TK_COMMA) + { + exp = ParseRExpression(); + if (exp->mType == EX_CONSTANT && exp->mDecValue->mType == DT_CONST_INTEGER) + flags = int(exp->mDecValue->mInteger); + else + mErrors->Error(mScanner->mLocation, EERR_PRAGMA_PARAMETER, "Integer number for flags expected"); + } + + ConsumeToken(TK_COMMA); + + Declaration* inlay = nullptr; + + if (mScanner->mToken != TK_COMMA) + { + if (mScanner->mToken == TK_OPEN_BRACE) + { + do + { + mScanner->NextToken(); + + exp = ParseRExpression(); + if (exp->mType == EX_CONSTANT && exp->mDecValue->mType == DT_CONST_INTEGER) + bank |= 1ULL << exp->mDecValue->mInteger; + else + mErrors->Error(mScanner->mLocation, EERR_PRAGMA_PARAMETER, "Integer number for bank expected"); + + } while (mScanner->mToken == TK_COMMA); + + ConsumeToken(TK_CLOSE_BRACE); + } + else + { + exp = ParseExpression(true); + if (exp->mType == EX_VARIABLE && exp->mDecType->mType == DT_TYPE_ARRAY && exp->mDecType->mSize == 0 && + exp->mDecType->mBase->mType == DT_TYPE_INTEGER && exp->mDecType->mBase->mSize == 1) + { + exp->mDecValue->mFlags |= DTF_DEFINED | DTF_BANK_INLAY; + exp->mDecValue->mSection = mCodeSection; + exp->mDecType->mFlags |= DTF_DEFINED; + inlay = exp->mDecValue; + } + else + { + exp = exp->ConstantFold(mErrors, mDataSection, mCompilationUnits->mLinker); + if (exp->mType == EX_CONSTANT && exp->mDecValue->mType == DT_CONST_INTEGER) + bank = 1ULL << exp->mDecValue->mInteger; + else + mErrors->Error(mScanner->mLocation, EERR_PRAGMA_PARAMETER, "Integer number for bank expected"); + } + } + } + + LinkerRegion* rgn = mCompilationUnits->mLinker->FindRegion(regionIdent); + if (!rgn) + { + rgn = mCompilationUnits->mLinker->AddRegion(regionIdent, start, end); + rgn->mFlags = flags; + rgn->mCartridgeBanks = bank; + } + else if (rgn->mStart != start || rgn->mEnd != end || rgn->mFlags != flags || rgn->mCartridgeBanks != bank) + mErrors->Error(mScanner->mLocation, EERR_PRAGMA_PARAMETER, "Conflicting linker region definition"); + + if (inlay) + { + inlay->mInlayRegion = rgn; + } + + ConsumeToken(TK_COMMA); + ConsumeToken(TK_OPEN_BRACE); + if (!ConsumeTokenIf(TK_CLOSE_BRACE)) + { + do { + if (mScanner->mToken == TK_IDENT) + { + LinkerSection* lsec = mCompilationUnits->mLinker->FindSection(mScanner->mTokenIdent); + if (lsec) + { + if (!rgn->mSections.Contains(lsec)) + rgn->mSections.Push(lsec); + } + else + mErrors->Error(mScanner->mLocation, EERR_PRAGMA_PARAMETER, "Section name not defined"); + mScanner->NextToken(); + } + else + mErrors->Error(mScanner->mLocation, EERR_PRAGMA_PARAMETER, "Section name expected"); + } while (ConsumeTokenIf(TK_COMMA)); + ConsumeToken(TK_CLOSE_BRACE); + } + + if (ConsumeTokenIf(TK_COMMA)) + { + exp = ParseRExpression(); + if (exp->mType == EX_CONSTANT && exp->mDecValue->mType == DT_CONST_INTEGER) + rgn->mReloc = int(exp->mDecValue->mInteger - rgn->mStart); + else + mErrors->Error(mScanner->mLocation, EERR_PRAGMA_PARAMETER, "Integer number for bank expected"); + } + } + else + mErrors->Error(mScanner->mLocation, EERR_PRAGMA_PARAMETER, "Region name expected"); + + ConsumeToken(TK_CLOSE_PARENTHESIS); + } + else if (!strcmp(mScanner->mTokenIdent->mString, "overlay")) + { + mScanner->NextToken(); + ConsumeToken(TK_OPEN_PARENTHESIS); + + if (mScanner->mToken == TK_IDENT) + { + const Ident* overlayIdent = mScanner->mTokenIdent; + mScanner->NextToken(); + + int bank = 0; + + ConsumeToken(TK_COMMA); + + Expression* exp; + + exp = ParseRExpression(); + if (exp->mType == EX_CONSTANT && exp->mDecValue->mType == DT_CONST_INTEGER) + bank = int(exp->mDecValue->mInteger); + else + mErrors->Error(mScanner->mLocation, EERR_PRAGMA_PARAMETER, "Integer number for bank expected"); + + LinkerOverlay* lovl = mCompilationUnits->mLinker->AddOverlay(mScanner->mLocation, overlayIdent, bank); + + while (ConsumeTokenIf(TK_COMMA)) + { + if (mScanner->mToken == TK_IDENT) + { + if (!strcmp(mScanner->mTokenIdent->mString, "lzo")) + lovl->mCompressed = true; + else + mErrors->Error(mScanner->mLocation, EERR_PRAGMA_PARAMETER, "Overlay type specifier expected"); + mScanner->NextToken(); + } + else + mErrors->Error(mScanner->mLocation, EERR_PRAGMA_PARAMETER, "Overlay type specifier expected"); + } + } + + ConsumeToken(TK_CLOSE_PARENTHESIS); + } + else if (!strcmp(mScanner->mTokenIdent->mString, "section")) + { + mScanner->NextToken(); + ConsumeToken(TK_OPEN_PARENTHESIS); + + if (mScanner->mToken == TK_IDENT) + { + const Ident* sectionIdent = mScanner->mTokenIdent; + mScanner->NextToken(); + + int flags = 0; + Expression* exp; + Declaration* dstart = nullptr, * dend = nullptr; + LinkerSectionType type = LST_DATA; + + ConsumeToken(TK_COMMA); + + exp = ParseRExpression(); + if (exp->mType == EX_CONSTANT && exp->mDecValue->mType == DT_CONST_INTEGER) + flags = int(exp->mDecValue->mInteger); + else + mErrors->Error(mScanner->mLocation, EERR_PRAGMA_PARAMETER, "Integer number for flags expected"); + + if (ConsumeTokenIf(TK_COMMA)) + { + if (mScanner->mToken != TK_COMMA) + { + exp = ParseExpression(false); + if (exp->mDecValue && exp->mDecValue->mType == DT_VARIABLE) + dstart = exp->mDecValue; + } + + if (ConsumeTokenIf(TK_COMMA)) + { + if (mScanner->mToken != TK_COMMA) + { + exp = ParseExpression(false); + if (exp->mDecValue && exp->mDecValue->mType == DT_VARIABLE) + dend = exp->mDecValue; + } + + if (ConsumeTokenIf(TK_COMMA)) + { + if (mScanner->mToken == TK_IDENT) + { + if (!strcmp(mScanner->mTokenIdent->mString, "bss")) + type = LST_BSS; + else if (!strcmp(mScanner->mTokenIdent->mString, "data")) + type = LST_DATA; + else if (!strcmp(mScanner->mTokenIdent->mString, "packed")) + { + type = LST_DATA; + flags |= LSECF_PACKED; + } + else + mErrors->Error(mScanner->mLocation, EERR_PRAGMA_PARAMETER, "Unknown section type"); + } + else + mErrors->Error(mScanner->mLocation, EERR_PRAGMA_PARAMETER, "Identifier expected"); + + mScanner->NextToken(); + + } + } + } + + LinkerSection* lsec = mCompilationUnits->mLinker->FindSection(sectionIdent); + if (!lsec) + lsec = mCompilationUnits->mLinker->AddSection(sectionIdent, type); + + lsec->mFlags |= flags; + + if (dstart) + { + dstart->mSection = lsec; + dstart->mFlags |= DTF_SECTION_START; + } + if (dend) + { + dend->mSection = lsec; + dend->mFlags |= DTF_SECTION_END; + } + } + else + mErrors->Error(mScanner->mLocation, EERR_PRAGMA_PARAMETER, "Section name expected"); + + ConsumeToken(TK_CLOSE_PARENTHESIS); + } + else if (!strcmp(mScanner->mTokenIdent->mString, "place")) + { + mScanner->NextToken(); + ConsumeToken(TK_OPEN_PARENTHESIS); + + if (mScanner->mToken == TK_IDENT) + { + const Ident* sectionIdent = mScanner->mTokenIdent; + mScanner->NextToken(); + LinkerSection* lsec = mCompilationUnits->mLinker->FindSection(sectionIdent); + if (lsec) + { + while (ConsumeTokenIf(TK_COMMA)) + { + if (mScanner->mToken == TK_IDENT) + { + Declaration* dec = mGlobals->Lookup(mScanner->mTokenIdent); + if (dec && (dec->mType == DT_VARIABLE && (dec->mFlags & DTF_GLOBAL) || dec->mType == DT_CONST_FUNCTION)) + { + mScanner->NextToken(); + + dec->mFlags |= DTF_PLACED; + dec->mSection = lsec; + if (dec->mLinkerObject) + dec->mLinkerObject->MoveToSection(lsec); + } + else + { + mErrors->Error(mScanner->mLocation, EERR_OBJECT_NOT_FOUND, "Variable not found"); + mScanner->NextToken(); + } + } + else + mErrors->Error(mScanner->mLocation, EERR_PRAGMA_PARAMETER, "Variable name expected"); + } + } + else + mErrors->Error(mScanner->mLocation, EERR_PRAGMA_PARAMETER, "Section not defined"); + } + else + mErrors->Error(mScanner->mLocation, EERR_PRAGMA_PARAMETER, "Section name expected"); + + ConsumeToken(TK_CLOSE_PARENTHESIS); + } + else if (!strcmp(mScanner->mTokenIdent->mString, "code")) + { + mScanner->NextToken(); + ConsumeToken(TK_OPEN_PARENTHESIS); + + if (mScanner->mToken == TK_IDENT) + { + const Ident* sectionIdent = mScanner->mTokenIdent; + mScanner->NextToken(); + LinkerSection* lsec = mCompilationUnits->mLinker->FindSection(sectionIdent); + if (lsec) + { + mCodeSection = lsec; + } + else + mErrors->Error(mScanner->mLocation, EERR_PRAGMA_PARAMETER, "Section not defined"); + } + else + mErrors->Error(mScanner->mLocation, EERR_PRAGMA_PARAMETER, "Section name expected"); + + ConsumeToken(TK_CLOSE_PARENTHESIS); + } + else if (!strcmp(mScanner->mTokenIdent->mString, "data")) + { + mScanner->NextToken(); + ConsumeToken(TK_OPEN_PARENTHESIS); + + if (mScanner->mToken == TK_IDENT) + { + const Ident* sectionIdent = mScanner->mTokenIdent; + mScanner->NextToken(); + LinkerSection* lsec = mCompilationUnits->mLinker->FindSection(sectionIdent); + if (lsec) + { + mDataSection = lsec; + } + else + mErrors->Error(mScanner->mLocation, EERR_PRAGMA_PARAMETER, "Section not defined"); + } + else + mErrors->Error(mScanner->mLocation, EERR_PRAGMA_PARAMETER, "Section name expected"); + + ConsumeToken(TK_CLOSE_PARENTHESIS); + } + else if (!strcmp(mScanner->mTokenIdent->mString, "bss")) + { + mScanner->NextToken(); + ConsumeToken(TK_OPEN_PARENTHESIS); + + if (mScanner->mToken == TK_IDENT) + { + const Ident* sectionIdent = mScanner->mTokenIdent; + mScanner->NextToken(); + LinkerSection* lsec = mCompilationUnits->mLinker->FindSection(sectionIdent); + if (lsec) + { + mBSSection = lsec; + } + else + mErrors->Error(mScanner->mLocation, EERR_PRAGMA_PARAMETER, "Section not defined"); + } + else + mErrors->Error(mScanner->mLocation, EERR_PRAGMA_PARAMETER, "Section name expected"); + + ConsumeToken(TK_CLOSE_PARENTHESIS); + } + else if (!strcmp(mScanner->mTokenIdent->mString, "nocross")) + { + mScanner->NextToken(); + ConsumeToken(TK_OPEN_PARENTHESIS); + + if (mScanner->mToken == TK_IDENT) + { + Declaration* dec = mGlobals->Lookup(mScanner->mTokenIdent); + if (dec && dec->mType == DT_VARIABLE && (dec->mFlags & DTF_GLOBAL)) + dec->mFlags |= DTF_NO_PAGE_CROSS; + else if (dec && dec->mType == DT_CONST_FUNCTION) + dec->mFlags |= DTF_NO_PAGE_CROSS; + else + mErrors->Error(mScanner->mLocation, EERR_OBJECT_NOT_FOUND, "Variable not found"); + mScanner->NextToken(); + } + else + mErrors->Error(mScanner->mLocation, EERR_PRAGMA_PARAMETER, "Variable name expected"); + + ConsumeToken(TK_CLOSE_PARENTHESIS); + } + else if (!strcmp(mScanner->mTokenIdent->mString, "align")) + { + mScanner->NextToken(); + ConsumeToken(TK_OPEN_PARENTHESIS); + + if (mScanner->mToken == TK_IDENT) + { + Declaration* dec = mGlobals->Lookup(mScanner->mTokenIdent); + if (dec && dec->mType == DT_VARIABLE && (dec->mFlags & DTF_GLOBAL)) + { + mScanner->NextToken(); + ConsumeToken(TK_COMMA); + + Expression * exp = ParseRExpression(); + if (exp->mType == EX_CONSTANT && exp->mDecValue->mType == DT_CONST_INTEGER) + { + dec->mAlignment = int(exp->mDecValue->mInteger); + } + else + mErrors->Error(mScanner->mLocation, EERR_PRAGMA_PARAMETER, "Integer number for alignment expected"); + } + else if (dec && dec->mType == DT_CONST_FUNCTION) + { + mScanner->NextToken(); + ConsumeToken(TK_COMMA); + + Expression* exp = ParseRExpression(); + if (exp->mType == EX_CONSTANT && exp->mDecValue->mType == DT_CONST_INTEGER) + { + dec->mAlignment = int(exp->mDecValue->mInteger); + } + else + mErrors->Error(mScanner->mLocation, EERR_PRAGMA_PARAMETER, "Integer number for alignment expected"); + } + else + { + mErrors->Error(mScanner->mLocation, EERR_OBJECT_NOT_FOUND, "Variable not found"); + mScanner->NextToken(); + } + } + else + mErrors->Error(mScanner->mLocation, EERR_PRAGMA_PARAMETER, "Variable name expected"); + + ConsumeToken(TK_CLOSE_PARENTHESIS); + } + else if (!strcmp(mScanner->mTokenIdent->mString, "zeropage")) + { + mScanner->NextToken(); + ConsumeToken(TK_OPEN_PARENTHESIS); + + if (mScanner->mToken == TK_IDENT) + { + Declaration* dec = mGlobals->Lookup(mScanner->mTokenIdent); + if (dec && dec->mType == DT_VARIABLE && (dec->mFlags & DTF_GLOBAL)) + { + mScanner->NextToken(); + + dec->mFlags |= DTF_ZEROPAGE; + dec->mSection = mCompilationUnits->mSectionZeroPage; + if (dec->mLinkerObject) + { + dec->mLinkerObject->MoveToSection(dec->mSection); + dec->mLinkerObject->mFlags |= LOBJF_ZEROPAGE; + } + } + else + { + mErrors->Error(mScanner->mLocation, EERR_OBJECT_NOT_FOUND, "Variable not found"); + mScanner->NextToken(); + } + } + else + mErrors->Error(mScanner->mLocation, EERR_PRAGMA_PARAMETER, "Variable name expected"); + + ConsumeToken(TK_CLOSE_PARENTHESIS); + } + else if (!strcmp(mScanner->mTokenIdent->mString, "reference")) + { + mScanner->NextToken(); + ConsumeToken(TK_OPEN_PARENTHESIS); + + if (mScanner->mToken == TK_IDENT) + { + Declaration* dec = mGlobals->Lookup(mScanner->mTokenIdent); + if (dec && (dec->mType == DT_CONST_FUNCTION || (dec->mType == DT_VARIABLE && (dec->mFlags & DTF_GLOBAL)))) + { + mCompilationUnits->AddReferenced(dec); + mScanner->NextToken(); + } + else + { + mErrors->Error(mScanner->mLocation, EERR_OBJECT_NOT_FOUND, "Variable not found"); + mScanner->NextToken(); + } + } + else + mErrors->Error(mScanner->mLocation, EERR_PRAGMA_PARAMETER, "Variable name expected"); + + ConsumeToken(TK_CLOSE_PARENTHESIS); + } + else if (!strcmp(mScanner->mTokenIdent->mString, "unroll")) + { + mScanner->NextToken(); + ConsumeToken(TK_OPEN_PARENTHESIS); + + mUnrollLoopPage = false; + + if (IsIntegerToken()) + { + mUnrollLoop = int(mScanner->mTokenInteger); + mScanner->NextToken(); + } + else if (mScanner->mToken == TK_IDENT && !strcmp(mScanner->mTokenIdent->mString, "full")) + { + mUnrollLoop = 0x10000; + mScanner->NextToken(); + } + else if (mScanner->mToken == TK_IDENT && !strcmp(mScanner->mTokenIdent->mString, "page")) + { + mUnrollLoop = 0x10000; + mUnrollLoopPage = true; + mScanner->NextToken(); + } + else + mErrors->Error(mScanner->mLocation, EERR_PRAGMA_PARAMETER, "Integer literal expected"); + + ConsumeToken(TK_CLOSE_PARENTHESIS); + } + else if (!strcmp(mScanner->mTokenIdent->mString, "callinline")) + { + mScanner->NextToken(); + ConsumeToken(TK_OPEN_PARENTHESIS); + ConsumeToken(TK_CLOSE_PARENTHESIS); + mInlineCall = true; + } + else if (ConsumeIdentIf("optimize")) + { + ConsumeToken(TK_OPEN_PARENTHESIS); + if (!ConsumeTokenIf(TK_CLOSE_PARENTHESIS)) + { + do { + if (ConsumeIdentIf("push")) + { + if (mCompilerOptionSP < 32) + mCompilerOptionStack[mCompilerOptionSP++] = mCompilerOptions; + else + mErrors->Error(mScanner->mLocation, ERRR_STACK_OVERFLOW, "Stack overflow"); + } + else if (ConsumeIdentIf("pop")) + { + if (mCompilerOptionSP > 0) + mCompilerOptions = mCompilerOptionStack[--mCompilerOptionSP]; + else + mErrors->Error(mScanner->mLocation, ERRR_STACK_OVERFLOW, "Stack underflow"); + } + else if (IsIntegerToken()) + { + mCompilerOptions &= ~(COPT_OPTIMIZE_ALL); + switch (mScanner->mTokenInteger) + { + case 0: + break; + case 1: + mCompilerOptions |= COPT_OPTIMIZE_DEFAULT; + break; + case 2: + mCompilerOptions |= COPT_OPTIMIZE_SPEED; + break; + case 3: + mCompilerOptions |= COPT_OPTIMIZE_ALL; + break; + default: + mErrors->Error(mScanner->mLocation, ERRR_INVALID_NUMBER, "Invalid number"); + } + mScanner->NextToken(); + } + else if (ConsumeIdentIf("asm")) + mCompilerOptions |= COPT_OPTIMIZE_ASSEMBLER; + else if (ConsumeIdentIf("noasm")) + mCompilerOptions &= ~COPT_OPTIMIZE_ASSEMBLER; + else if (ConsumeIdentIf("size")) + mCompilerOptions |= COPT_OPTIMIZE_SIZE; + else if (ConsumeIdentIf("speed")) + mCompilerOptions &= ~COPT_OPTIMIZE_SIZE; + else if (ConsumeIdentIf("noinline")) + mCompilerOptions &= ~(COPT_OPTIMIZE_INLINE | COPT_OPTIMIZE_AUTO_INLINE | COPT_OPTIMIZE_AUTO_INLINE_ALL); + else if (ConsumeTokenIf(TK_INLINE)) + mCompilerOptions |= COPT_OPTIMIZE_INLINE; + else if (ConsumeIdentIf("autoinline")) + mCompilerOptions |= COPT_OPTIMIZE_INLINE | COPT_OPTIMIZE_AUTO_INLINE; + else if (ConsumeIdentIf("maxinline")) + mCompilerOptions |= COPT_OPTIMIZE_INLINE | COPT_OPTIMIZE_AUTO_INLINE | COPT_OPTIMIZE_AUTO_INLINE_ALL; + else if (ConsumeIdentIf("constparams")) + mCompilerOptions |= COPT_OPTIMIZE_CONST_PARAMS; + else if (ConsumeIdentIf("noconstparams")) + mCompilerOptions &= ~COPT_OPTIMIZE_CONST_PARAMS; + else if (ConsumeIdentIf("outline")) + mCompilerOptions |= COPT_OPTIMIZE_OUTLINE; + else if (ConsumeIdentIf("nooutline")) + mCompilerOptions &= ~COPT_OPTIMIZE_OUTLINE; + else if (ConsumeIdentIf("selfmod")) + mCompilerOptions |= COPT_OPTIMIZE_SELF_MOD; + else if (ConsumeIdentIf("noselfmod")) + mCompilerOptions &= ~COPT_OPTIMIZE_SELF_MOD; + else + mErrors->Error(mScanner->mLocation, EERR_INVALID_IDENTIFIER, "Invalid option"); + + } while (ConsumeTokenIf(TK_COMMA)); + + ConsumeToken(TK_CLOSE_PARENTHESIS); + } + } + else + { + mScanner->NextToken(); + if (ConsumeTokenIf(TK_OPEN_PARENTHESIS)) + { + while (mScanner->mToken != TK_CLOSE_PARENTHESIS && mScanner->mToken != TK_EOF) + mScanner->NextToken(); + ConsumeToken(TK_CLOSE_PARENTHESIS); + } + mErrors->Error(mScanner->mLocation, EWARN_UNKNOWN_PRAGMA, "Unknown pragma, ignored", mScanner->mTokenIdent); + } + } + else + mErrors->Error(mScanner->mLocation, EERR_SYNTAX, "Invalid pragma directive"); + + mScanner->mCompilerOptions = op; +} + +void Parser::ParseNamespace(void) +{ + if (mScanner->mToken == TK_IDENT) + { + const Ident* ident = mScanner->mTokenIdent; + + mScanner->NextToken(); + + if (mScanner->mToken == TK_OPEN_BRACE) + { + Declaration* ns = mScope->Lookup(ident); + if (ns) + { + if (ns->mType != DT_NAMESPACE) + { + mErrors->Error(mScanner->mLocation, ERRO_NOT_A_NAMESPACE, "Not a namespace", ident); + ns = nullptr; + } + } + + if (!ns) + { + ns = new Declaration(mScanner->mLocation, DT_NAMESPACE); + ns->mScope = new DeclarationScope(mScope, SLEVEL_NAMESPACE, ident); + ns->mIdent = ident; + ns->mQualIdent = mScope->Mangle(ident); + + mScope->Insert(ident, ns); + } + + mScanner->NextToken(); + + DeclarationScope* outerScope = mScope; + mScope = ns->mScope; + + while (mScanner->mToken != TK_EOF && mScanner->mToken != TK_CLOSE_BRACE) + { + if (mScanner->mToken == TK_PREP_PRAGMA) + { + mScanner->NextToken(); + ParsePragma(); + } + else if (mScanner->mToken == TK_SEMICOLON) + mScanner->NextToken(); + else if (mScanner->mToken == TK_TEMPLATE) + { + mScanner->NextToken(); + ParseTemplateDeclaration(nullptr); + } + else if (mScanner->mToken == TK_NAMESPACE) + { + mScanner->NextToken(); + ParseNamespace(); + } + else if (mScanner->mToken == TK_STATIC_ASSERT) + { + mScanner->NextToken(); + ParseStaticAssert(); + } + else + ParseDeclaration(nullptr, true, false, false); + } + + ConsumeToken(TK_CLOSE_BRACE); + + mScope = outerScope; + } + else + { + ConsumeToken(TK_ASSIGN); + Declaration* dec = ParseQualIdent(false); + if (dec) + { + if (dec->mType == DT_NAMESPACE) + { + Declaration* ns = mScope->Insert(ident, dec); + if (ns) + { + mErrors->Error(mScanner->mLocation, EERR_DUPLICATE_DEFINITION, "Duplicate definition", ident); + mErrors->Error(ns->mLocation, EINFO_ORIGINAL_DEFINITION, "Original definition"); + } + } + else + mErrors->Error(mScanner->mLocation, ERRO_NOT_A_NAMESPACE, "Not a namespace", ident); + } + ConsumeToken(TK_SEMICOLON); + } + } + else + { + // Anonymous namespace + } +} + +Location Parser::FullLocation(const Location& loc) +{ + if (mParent) + return mParent->FullLocation(Location(loc, &mParent->mScanner->mLocation)); + else + return loc; +} + +void Parser::Parse(void) +{ + mLocalIndex = 0; + + while (mScanner->mToken != TK_EOF) + { + if (mScanner->mToken == TK_PREP_PRAGMA) + { + mScanner->NextToken(); + ParsePragma(); + } + else if (mScanner->mToken == TK_ASM) + { + mScanner->NextToken(); + if (mScanner->mToken == TK_IDENT) + { + const Ident* ident = mScanner->mTokenIdent; + mScanner->NextToken(); + + Declaration* vdasm = mScope->Lookup(ident); + if (vdasm) + { + if (vdasm->mType != DT_CONST_ASSEMBLER || (vdasm->mFlags & DTF_DEFINED)) + { + mErrors->Error(mScanner->mLocation, EERR_DUPLICATE_DEFINITION, "Duplicate definition", ident); + mErrors->Error(vdasm->mLocation, EINFO_ORIGINAL_DEFINITION, "Original definition"); + } + } + else + { + Declaration * dasm = new Declaration(mScanner->mLocation, DT_TYPE_ASSEMBLER); + dasm->mScope = new DeclarationScope(mScope, SLEVEL_LOCAL); + + vdasm = new Declaration(mScanner->mLocation, DT_CONST_ASSEMBLER); + vdasm->mVarIndex = -1; + vdasm->mSection = mCodeSection; + vdasm->mBase = dasm; + vdasm->mIdent = ident; + vdasm->mQualIdent = ident; + mScope->Insert(ident, vdasm); + } + + vdasm->mFlags |= ParseAssemblerFlags(); + + if (mScanner->mToken == TK_OPEN_BRACE) + { + mScanner->NextToken(); + + Expression* exp = ParseAssembler(vdasm); + + if (mScanner->mToken == TK_CLOSE_BRACE) + mScanner->NextToken(); + else + mErrors->Error(mScanner->mLocation, EERR_SYNTAX, "'}' expected"); + } + } + else + mErrors->Error(mScanner->mLocation, EERR_SYNTAX, "Identifier expected"); + } + else if (mScanner->mToken == TK_SEMICOLON) + mScanner->NextToken(); + else if (mScanner->mToken == TK_TEMPLATE) + { + mScanner->NextToken(); + ParseTemplateDeclaration(nullptr); + } + else if (mScanner->mToken == TK_NAMESPACE) + { + mScanner->NextToken(); + ParseNamespace(); + } + else if (mScanner->mToken == TK_STATIC_ASSERT) + { + mScanner->NextToken(); + ParseStaticAssert(); + } + else + ParseDeclaration(nullptr, true, false, false); + } +} diff --git a/oscar64/oscar64/Parser.h b/oscar64/oscar64/Parser.h new file mode 100644 index 0000000..b73e9cf --- /dev/null +++ b/oscar64/oscar64/Parser.h @@ -0,0 +1,173 @@ +#pragma once + +#include "Scanner.h" +#include "Declaration.h" +#include "CompilationUnits.h" + +class Parser +{ +public: + Parser(Errors * errors, Scanner* scanner, CompilationUnits * compilationUnits); + ~Parser(void); + + Parser* Clone(void); + Parser * mParent; + + DeclarationScope * mGlobals, * mScope, * mTemplateScope, * mCaptureScope; + int mLocalIndex; + CompilationUnits * mCompilationUnits; + Declaration * mThisPointer, * mFunctionType, * mFunction, * mLambda, * mTempVars; + Token mCaptureToken; + + LinkerSection * mCodeSection, * mDataSection, * mBSSection; + + uint64 mCompilerOptions; + uint64 mCompilerOptionStack[32]; + int mCompilerOptionSP; + + Location FullLocation(const Location& loc); + + void Parse(void); +protected: + bool ExpectToken(Token token); + bool ConsumeToken(Token token); + bool ConsumeTokenIf(Token token); + bool ConsumeIdentIf(const char* ident); + bool IsIntegerToken(void); + bool IsTypeToken(void); + + uint8 mCharMap[256]; + int mUnrollLoop; + bool mUnrollLoopPage; + bool mInlineCall; + + Declaration* AllocTempVar(Declaration* type); + void FreeTempVar(Declaration* var); + void FreeTempVarExp(Expression* exp); + + uint8* ParseStringLiteral(int & msize); + + void ParseNamespace(void); + + void ParsePragma(void); + + Declaration * ParseFunctionDeclaration(Declaration* bdec); + void PrependThisArgument(Declaration* fdec, Declaration * pthis); + void AppendMemberDestructor(Declaration* pthis); + void BuildMemberConstructor(Declaration* pthis, Declaration* cfunc); + Expression* BuildMemberInitializer(Expression* vexp); + void PrependMemberConstructor(Declaration* pthis, Declaration* cfunc); + + void AddDefaultConstructors(Declaration* pthis); + + Expression* BuildVariableArrayInit(Expression* texp, Declaration* bpdec, Declaration* pdec, int boffset, int & offset); + Expression * BuildVariableInit(Expression* vexp, Expression* pexp); + void ParseVariableInit(Declaration* ndec, Expression * pexp = nullptr); + Declaration* AddMemberFunction(Declaration* dec, Declaration* mdec); + Declaration* FindBaseMemberFunction(Declaration* dec, Declaration* mdec); + + Expression * AddFunctionCallRefReturned(Expression * exp); + Expression* CleanupExpression(Expression* exp); + + void ExpandStructuredBinding(Declaration* dec); + + Declaration* ParseBaseTypeQualify(bool qualified, Declaration* dec, const Ident *& pident); + Declaration* ParseBaseTypeDeclaration(uint64 flags, bool qualified, Declaration* ptempl = nullptr); + Declaration* ParseDeclaration(Declaration* pdec, bool variable, bool expression, bool member, Declaration * pthis = nullptr, Declaration * ptempl = nullptr); + Declaration* ParseStructDeclaration(uint64 flags, DecType dt, Declaration* ptempl = nullptr); + + Declaration* CopyConstantInitializer(int offset, Declaration* dtype, Expression* exp); + Expression* ParseConstInitExpression(Declaration* dtype, bool inner = false); + Expression* ParseDeclarationExpression(Declaration* pdec); + Expression* DefaultInitExpression(Expression* vexp); + Expression* ParseVarInitExpression(Expression* lexp, bool inner = false); + Expression* CloneVarInitExpression(Expression* vexp, Expression* iexp, Expression * qexp); + Expression* CopyElision(Expression* vexp, Expression* rexp); + + Declaration* ParsePostfixDeclaration(bool autoBase); + Declaration* ReverseDeclaration(Declaration* odec, Declaration* bdec); + + Expression* ParseFunction(Declaration* dec); + Expression* ParseAssembler(Declaration * vdassm = nullptr); + + uint64 ParseAssemblerFlags(void); + + Expression* ParseAssemblerBaseOperand(Declaration* pcasm, int pcoffset); + Expression* ParseAssemblerMulOperand(Declaration* pcasm, int pcoffset); + Expression* ParseAssemblerAddOperand(Declaration* pcasm, int pcoffset); + Expression* ParseAssemblerShiftOperand(Declaration* pcasm, int pcoffset); + Expression* ParseAssemblerAndOperand(Declaration* pcasm, int pcoffset); + Expression* ParseAssemblerOrOperand(Declaration* pcasm, int pcoffset); + Expression* ParseAssemblerOperand(Declaration * pcasm, int pcoffset); + + Expression* CheckOperatorOverload(Expression* exp); + + void AddAssemblerRegister(const Ident* ident, int value); + + Declaration* ParseQualIdent(bool lhs); + + void SkipStatement(void); + Expression* ParseStatement(void); + Expression* ParseSwitchStatement(void); + + Declaration* MemberLookup(Declaration* dtype, const Ident * ident, int& offset, uint64 & flags); + + Expression* ParseQualify(Expression * exp); + + int OverloadDistance(Declaration* pdec, Expression* pexp); + Expression * ResolveOverloadCall(Expression* exp, Expression * exp2 = nullptr); + Expression* CoerceExpression(Expression* exp, Declaration* type); + bool CanCoerceExpression(Expression* exp, Declaration* type); + void CompleteFunctionDefaultParams(Expression* exp); + void ExpandFunctionCallTemplate(Expression* exp); + + Declaration * ParseTemplateDeclaration(Declaration* pthis); + void ParseTemplateDeclarationBody(Declaration* tdec, Declaration* pthis); + Declaration* FunctionAutoParamsToTemplate(Declaration* fdec); + + int ExpansionDistance(Declaration* tdec, Declaration* spec, Declaration* xtdec); + void ParseTemplateArguments(Declaration* tmpld, Declaration* tdec); + Declaration* ParseTemplateExpansion(Declaration* tmpld, Declaration* expd); + void CompleteTemplateExpansion(Declaration* tmpld); + + Expression* ParseNewOperator(void); + Expression* ParseLambdaExpression(void); + + Declaration* OperatorResultType(Expression* exp); + + Expression* FindPackExpression(Expression* exp); + Expression* ExpandPackExpression(Expression* exp, Expression* pack, Expression* item); + Expression* ParseBinaryFoldExpression(Expression * exp); + + Declaration* ParseTypeID(bool tid, Declaration * bdec = nullptr); + + Expression* ParseConstruction(Declaration* type); + + Expression* ParseCastExpression(Expression* exp); + Expression* ParseIdentExpression(const Location & eloc, Declaration* dec, bool lhs, bool tid = false); + Expression* ParseSimpleExpression(bool lhs, bool tid = false); + Expression* ParsePrefixExpression(bool lhs); + Expression* ParsePostfixExpression(bool lhs); + Expression* ParseMulExpression(bool lhs); + Expression* ParseAddExpression(bool lhs); + Expression* ParseShiftExpression(bool lhs); + Expression* ParseRelationalExpression(bool lhs); + Expression* ParseBinaryAndExpression(bool lhs); + Expression* ParseBinaryXorExpression(bool lhs); + Expression* ParseBinaryOrExpression(bool lhs); + Expression* ParseLogicAndExpression(bool lhs); + Expression* ParseLogicOrExpression(bool lhs); + Expression* ParseConditionalExpression(bool lhs); + Expression* ParseAssignmentExpression(bool lhs); + Expression* ParseExpression(bool lhs); + Expression* ParseRExpression(void); + + Expression* ExpandArgumentPack(Expression * exp, Declaration* dec); + Expression* ParseListExpression(bool lhs, Declaration * params = nullptr); + + Expression* ParseParenthesisExpression(void); + void ParseStaticAssert(void); + + Errors* mErrors; + Scanner* mScanner; +}; diff --git a/oscar64/oscar64/Preprocessor.cpp b/oscar64/oscar64/Preprocessor.cpp new file mode 100644 index 0000000..3b8b5b3 --- /dev/null +++ b/oscar64/oscar64/Preprocessor.cpp @@ -0,0 +1,1081 @@ +#include "Preprocessor.h" +#include +#include +#include + +SourcePath::SourcePath(const char* path) +{ + strcpy_s(mPathName, path); + char* p = mPathName; + while (*p) + { + if (*p == '\\') + *p = '/'; + p++; + } +} + +SourcePath::~SourcePath(void) +{ + +} + +bool SourceFile::ReadLineLZO(char* line, ptrdiff_t limit) +{ + if (mPos > 256) + { + memmove(mBuffer, mBuffer + mPos - 256, mFill + 256 - mPos); + mFill -= mPos - 256; + mPos = 256; + } + + assert(mFill >= 0 && mFill - mPos < 384 && mPos <= mFill); + + int c; + while (mLimit && mFill < 384 && (c = ReadChar()) >= 0) + { + mLimit--; + mBuffer[mFill++] = c; + } + + if (mPos < mFill) + { + int pi = 0; + while (pi < 127 && mPos < mFill) + { + int bi = pi, bj = 0; + for (int i = 1; i <= (mPos < 255 ? mPos : 255); i++) + { + int j = 0; + while (j < 127 && mPos + j < mFill && mBuffer[mPos - i + j] == mBuffer[mPos + j]) + j++; + + if (j > bj) + { + bi = i; + bj = j; + } + } + + if (bj >= 4) + { + if (pi > 0) + { + sprintf_s(line, limit, "0x%02x, ", pi); + + for (int i = 0; i < pi; i++) + { + char buffer[16]; + sprintf_s(buffer, 16, "0x%02x, ", (unsigned char)mBuffer[mPos - pi + i]); + + strcat_s(line, limit, buffer); + } + + return true; + } + else + { + sprintf_s(line, limit, "0x%02x, 0x%02x, ", 128 + bj, bi); + mPos += bj; + + if (mFill == mPos) + strcat_s(line, limit, "0x00, "); + + return true; + } + } + else + { + mPos++; + pi++; + } + } + + sprintf_s(line, limit, "0x%02x, ", pi); + + for (int i = 0; i < pi; i++) + { + char buffer[16]; + sprintf_s(buffer, 16, "0x%02x, ", (unsigned char)mBuffer[mPos - pi + i]); + + strcat_s(line, limit, buffer); + } + + if (mFill == mPos) + strcat_s(line, limit, "0x00, "); + + return true; + } + + return false; +} + +bool SourceFile::ReadLineRLE(char* line, ptrdiff_t limit) +{ + assert(mFill >= 0 && mFill < 256); + + int c; + while (mLimit && mFill < 256 && (c = ReadChar()) >= 0) + { + mLimit--; + mBuffer[mFill++] = c; + } + + assert(mFill >= 0 && mFill <= 256); + + if (mFill) + { + if (mFill >= 3 && mBuffer[0] == mBuffer[1] && mBuffer[1] == mBuffer[2]) + { + int cnt = 1; + while (cnt < 64 && cnt < mFill && mBuffer[cnt] == mBuffer[cnt - 1]) + cnt++; + + if (cnt <= 8 && cnt < mFill) + { + int rcnt = 1; + int rep = 1; + while (rcnt < 16 && cnt + rcnt < mFill && rep < 3) + { + if (mBuffer[cnt + rcnt] == mBuffer[cnt + rcnt - 1]) + rep++; + else + rep = 1; + rcnt++; + } + if (cnt + rcnt < mFill && rep >= 3) + rcnt -= rep; + + if (rcnt > 0) + { + sprintf_s(line, limit, "0x%02x, 0x%02x, ", 0x80 + ((cnt - 1) << 4) + (rcnt - 1), (unsigned char)mBuffer[0]); + + assert(mFill >= 0 && mFill <= 256); + + for (int i = 0; i < rcnt; i++) + { + char buffer[16]; + sprintf_s(buffer, 16, "0x%02x, ", (unsigned char)mBuffer[cnt + i]); + + assert(mFill >= 0 && mFill <= 256); + + strcat_s(line, limit, buffer); + + assert(mFill >= 0 && mFill <= 256); + } + + assert(mFill >= cnt + rcnt); + assert(mFill >= 0 && mFill <= 256); + + memmove(mBuffer, mBuffer + cnt + rcnt, mFill - cnt - rcnt); + mFill -= cnt + rcnt; + + assert(mFill >= 0 && mFill < 256); + } + else + { + sprintf_s(line, limit, "0x%02x, 0x%02x, ", 0x00 + (cnt - 1), (unsigned char)mBuffer[0]); + memmove(mBuffer, mBuffer + cnt, mFill - cnt); + mFill -= cnt; + + assert(mFill >= 0 && mFill < 256); + } + } + else + { + sprintf_s(line, limit, "0x%02x, 0x%02x, ", 0x00 + (cnt - 1), (unsigned char)mBuffer[0]); + memmove(mBuffer, mBuffer + cnt, mFill - cnt); + mFill -= cnt; + + assert(mFill >= 0 && mFill < 256); + } + + if (mFill == 0) + strcat_s(line, limit, "0x00, "); + + return true; + } + else + { + int cnt = 1; + int rep = 1; + while (cnt < 64 && cnt < mFill && rep < 3) + { + if (mBuffer[cnt] == mBuffer[cnt - 1]) + rep++; + else + rep = 1; + cnt++; + } + if (cnt < mFill && rep >= 3) + cnt -= rep; + + sprintf_s(line, limit, "0x%02x, ", 0x40 + (cnt - 1)); + + for (int i = 0; i < cnt; i++) + { + char buffer[16]; + sprintf_s(buffer, 16, "0x%02x, ", (unsigned char)mBuffer[i]); + strcat_s(line, limit, buffer); + } + + memmove(mBuffer, mBuffer + cnt, mFill - cnt); + mFill -= cnt; + + assert(mFill >= 0 && mFill < 256); + + if (mFill == 0) + strcat_s(line, limit, "0x00, "); + + return true; + } + } + + return false; +} + +int SourceFile::ReadChar(void) +{ + if (mMemData) + { + if (mMemPos < mMemSize) + return mMemData[mMemPos++]; + else + return -1; + } + else + return fgetc(mFile); +} + +bool SourceFile::ReadLine(char* line, ptrdiff_t limit) +{ + if (mFile) + { + switch (mMode) + { + case SFM_TEXT: + if (fgets(line, int(limit), mFile)) + return true; + break; + case SFM_BINARY: + if (mLimit) + { + mLimit--; + + int c = ReadChar(); + if (c >= 0) + { + sprintf_s(line, limit, "0x%02x, ", c); + return true; + } + } + break; + case SFM_BINARY_WORD: + if (mLimit >= 2) + { + mLimit -= 2; + + int c = ReadChar(); + if (c >= 0) + { + int d = ReadChar(); + if (d >= 0) + sprintf_s(line, limit, "0x%04x, ", c + 256 * d); + return true; + } + } + break; + + case SFM_BINARY_RLE: + if (ReadLineRLE(line, limit)) + return true; + break; + case SFM_BINARY_LZO: + if (ReadLineLZO(line, limit)) + return true; + break; + } + + fclose(mFile); + mFile = nullptr; + } + + return false; +} + +struct CTMHeader +{ + uint8 mID[3]; + uint8 mVersion; +}; + +struct CTMHeader8 +{ + uint8 mDispMode; + uint8 mColorMethod; + uint8 mFlags; + uint8 mColors[7]; +}; + +struct CTMHeader9 +{ + uint8 mDispMode; + uint8 mColorMethod; + uint8 mFlags; + uint8 mFgridWidth[2], mFGridHeight[2]; + char mFGridConfig; + uint8 mColors[7]; +}; + +struct CTTHeader9 +{ + uint8 mDispMode; + uint8 mColorMethod; + uint8 mFlags; + uint8 mFgridWidth[2], mFGridHeight[2]; + char mFGridConfig; + uint8 mColors[6]; +}; + +#if 0 +#pragma pack(push, 1) +struct SPDHeader5 +{ + uint8 mID[3]; + uint8 mVersion[1]; + uint8 mFlags; + uint16 mNumSprites, mNumTiles; + uint8 mNumSpriteAnmis, mNumTileAnims; + uint8 mTileWidth, mTileHeight; + uint8 mColors[3]; + int16 mSpriteOverlayDist, mTileOverlayDist; +}; +#pragma pack(pop) + +void SourceFile::ReadSpritePad(Errors* errors, SourceFileDecoder decoder) +{ + SPDHeader5 spdHeader; + + fread(&spdHeader, sizeof(SPDHeader5), 1, mFile); + + if (decoder == SFD_SPD_SPRITES) + { + mLimit = 64 * spdHeader.mNumSprites; + return; + } + + mLimit = 0; +} +#endif + +#pragma pack(push, 1) +// 0.0 1.0 5.0 SPD file format information +// - SPD SPD 3 bytes "SPD" +// - 01 04 version number of spritepad (1 = 2.0) +// - - byte flags +// - byte word number of sprites +// - byte word number of animations +// - - byte number of sprite animations +// - - byte number of tile animations +// - - byte tile width +// - - byte tile height +// byte byte byte color transparent +// byte byte byte color multicolor 1 +// byte byte byte color multicolor 2 +// list of [ +// [63] [63] [63] 63 bytes of sprite data +// byte byte byte color byte. Bits: 0-3 color, 4 overlay, 7 multicolor/singlecolor +// ] +// - [4] - bytes xx = "00", "00", "01", "00" added at the end of file (SpritePad animation info) + +struct SPDHeader +{ + uint8 mID[3]; + uint8 mVersion[1]; // 1=1.0 5=5.0 +}; + +struct SPDHeader1 +{ + uint8 mNumSprites; // -1 + uint8 mNumSpriteAnmis; // -1 + uint8 mColors[3]; +}; + +struct SPDHeader3 +{ + uint8 mFlags; + uint16 mNumSprites, mNumTiles; + uint8 mNumSpriteAnmis, mNumTileAnims; + uint8 mTileWidth, mTileHeight; + uint8 mColors[3]; +}; + +struct SPDHeader5 +{ + uint8 mFlags; + uint16 mNumSprites, mNumTiles; + uint8 mNumSpriteAnmis, mNumTileAnims; + uint8 mTileWidth, mTileHeight; + uint8 mColors[3]; + int16 mSpriteOverlayDist, mTileOverlayDist; +}; + +#pragma pack(pop) + +void SourceFile::ReadSpritePad(Errors* errors, const Location& location, SourceFileDecoder decoder) +{ + SPDHeader spdHeader; + fread(&spdHeader, sizeof(SPDHeader), 1, mFile); + + if (spdHeader.mID[0] == 'S' && spdHeader.mID[1] == 'P' && spdHeader.mID[2] == 'D') + { + int numSprites = 0; + int numTiles = 0, tileSize = 0; + + switch (spdHeader.mVersion[0]) + { + case 5: + { + SPDHeader5 spdHeader5; + fread(&spdHeader5, sizeof(SPDHeader5), 1, mFile); + numSprites = spdHeader5.mNumSprites; + numTiles = spdHeader5.mNumTiles; + tileSize = spdHeader5.mTileHeight * spdHeader5.mTileWidth; + break; + } + case 3: + { + SPDHeader3 spdHeader3; + fread(&spdHeader3, sizeof(SPDHeader3), 1, mFile); + numSprites = spdHeader3.mNumSprites; + numTiles = spdHeader3.mNumTiles; + tileSize = spdHeader3.mTileHeight * spdHeader3.mTileWidth; + break; + } + case 1: + { + SPDHeader1 spdHeader1; + fread(&spdHeader1, sizeof(SPDHeader1), 1, mFile); + numSprites = spdHeader1.mNumSprites + 1; + break; + } + default: + errors->Error(location, EERR_UNIMPLEMENTED, "SPD file version not recognized"); + } + + + if (decoder == SFD_SPD_SPRITES) + { + mLimit = 64 * numSprites; + return; + } + else if (decoder == SFD_SPD_TILES) + { + fseek(mFile, 64 * numSprites, SEEK_CUR); + mMemSize = numTiles * tileSize; + mLimit = mMemSize; + mMemPos = 0; + mMemData = new uint8[mMemSize]; + for (int i = 0; i < mMemSize; i++) + { + int16 d; + fread(&d, 2, 1, mFile); + mMemData[i] = uint8(d); + } + return; + } + } + else + errors->Error(location, EERR_UNIMPLEMENTED, "SPD file format not recognized"); + + mLimit = 0; +} + +void SourceFile::ReadCharPad(Errors* errors, const Location& location, SourceFileDecoder decoder) +{ + CTMHeader ctmHeader; + CTMHeader8 ctmHeader8; + CTMHeader9 ctmHeader9; + CTTHeader9 cttHeader9; + uint16 ctmMarker, numChars, numTiles; + char tileWidth, tileHeight; + + fread(&ctmHeader, sizeof(CTMHeader), 1, mFile); + switch (ctmHeader.mVersion) + { + case 8: + fread(&ctmHeader8, sizeof(CTMHeader8), 1, mFile); + break; + case 9: + if (ctmHeader.mID[2] == 'T') + { + fread(&cttHeader9, sizeof(CTTHeader9), 1, mFile); + ctmHeader8.mDispMode = cttHeader9.mDispMode; + ctmHeader8.mColorMethod = cttHeader9.mColorMethod; + ctmHeader8.mFlags = cttHeader9.mFlags; + } + else + { + fread(&ctmHeader9, sizeof(CTMHeader9), 1, mFile); + ctmHeader8.mDispMode = ctmHeader9.mDispMode; + ctmHeader8.mColorMethod = ctmHeader9.mColorMethod; + ctmHeader8.mFlags = ctmHeader9.mFlags; + } + + break; + } + + + fread(&ctmMarker, 2, 1, mFile); + fread(&numChars, 2, 1, mFile); + numChars++; + + if (decoder == SFD_CTM_CHARS) + { + mLimit = 8 * numChars; + return; + } + + // Skip chars + fseek(mFile, 8 * numChars, SEEK_CUR); + + fread(&ctmMarker, 2, 1, mFile); + + if (decoder == SFD_CTM_CHAR_ATTRIB_1 || decoder == SFD_CTM_CHAR_ATTRIB_2 || decoder == SFD_CTM_CHAR_MATERIAL) + { + mMemSize = numChars; + mLimit = mMemSize; + mMemPos = 0; + mMemData = new uint8[mMemSize]; + } + + if (decoder == SFD_CTM_CHAR_MATERIAL) + { + fread(mMemData, 1, mMemSize, mFile); + return; + } + else if (decoder == SFD_CTM_CHAR_ATTRIB_1) + { + for (int i = 0; i < mMemSize; i++) + { + uint8 fd; + fread(&fd, 1, 1, mFile); + mMemData[i] = fd << 4; + } + + if (ctmHeader8.mColorMethod == 0) + return; + } + else + { + // Skip material + fseek(mFile, 1 * numChars, SEEK_CUR); + } + + if (ctmHeader8.mColorMethod == 2) + { + fread(&ctmMarker, 2, 1, mFile); + + if (decoder == SFD_CTM_CHAR_ATTRIB_1 || decoder == SFD_CTM_CHAR_ATTRIB_2) + { + if (ctmHeader8.mDispMode == 4) + { + for (int i = 0; i < mMemSize; i++) + { + uint8 fd[3]; + fread(fd, 1, 3, mFile); + if (decoder == SFD_CTM_CHAR_ATTRIB_1) + mMemData[i] |= fd[0]; + else + mMemData[i] = (fd[2] << 4) | fd[1]; + } + } + else + { + for (int i = 0; i < mMemSize; i++) + { + uint8 fd[3]; + fread(fd, 1, 1, mFile); + mMemData[i] |= fd[0]; + } + } + + + return; + } + else + { + // Skip colors + if (ctmHeader8.mDispMode == 3) + fseek(mFile, 2 * numChars, SEEK_CUR); + else if (ctmHeader8.mDispMode == 4) + fseek(mFile, 3 * numChars, SEEK_CUR); + else + fseek(mFile, numChars, SEEK_CUR); + } + } + + if (ctmHeader8.mFlags & 1) + { + fread(&ctmMarker, 2, 1, mFile); + + fread(&numTiles, 2, 1, mFile); + numTiles++; + + fread(&tileWidth, 1, 1, mFile); + fread(&tileHeight, 1, 1, mFile); + + if (decoder == SFD_CTM_TILES_16) + { + mLimit = 2 * numTiles * tileWidth * tileHeight; + return; + } + else if (decoder == SFD_CTM_TILES_8) + { + mMemSize = numTiles * tileWidth * tileHeight; + mLimit = mMemSize; + mMemPos = 0; + mMemData = new uint8[mMemSize]; + for (int i = 0; i < mMemSize; i++) + { + int16 d; + fread(&d, 2, 1, mFile); + mMemData[i] = uint8(d); + } + return; + } + else if (decoder == SFD_CTM_TILES_8_SW) + { + mMemSize = numTiles * tileWidth * tileHeight; + mLimit = mMemSize; + mMemPos = 0; + mMemData = new uint8[mMemSize]; + for (int j = 0; j < numTiles; j++) + { + for (int i = 0; i < tileWidth * tileHeight; i++) + { + int16 d; + fread(&d, 2, 1, mFile); + mMemData[j + i * numTiles] = uint8(d); + } + } + return; + } + else + fseek(mFile, 2 * numTiles * tileWidth * tileHeight, SEEK_CUR); + + if (ctmHeader8.mColorMethod == 1) + { + fread(&ctmMarker, 2, 1, mFile); + if (decoder == SFD_CTM_CHAR_ATTRIB_1 || decoder == SFD_CTM_CHAR_ATTRIB_2) + { + mMemSize = numTiles; + mLimit = mMemSize; + mMemPos = 0; + mMemData = new uint8[mMemSize]; + + if (ctmHeader8.mDispMode == 4) + { + for (int i = 0; i < mMemSize; i++) + { + uint8 fd[3]; + fread(fd, 1, 3, mFile); + if (decoder == SFD_CTM_CHAR_ATTRIB_1) + mMemData[i] = fd[0]; + else + mMemData[i] = (fd[2] << 4) | fd[1]; + } + } + else + { + for (int i = 0; i < mMemSize; i++) + { + uint8 fd[3]; + fread(fd, 1, 1, mFile); + mMemData[i] = fd[0]; + } + } + + return; + } + else + { + // Skip colors + if (ctmHeader8.mDispMode == 3) + fseek(mFile, 2 * numTiles, SEEK_CUR); + else if (ctmHeader8.mDispMode == 4) + fseek(mFile, 3 * numTiles, SEEK_CUR); + else + fseek(mFile, numTiles, SEEK_CUR); + } + } + + // Skip tags + fread(&ctmMarker, 2, 1, mFile); + fseek(mFile, 1 * numTiles, SEEK_CUR); + + // Skip tile names + fread(&ctmMarker, 2, 1, mFile); + for (int i = 0; i < numTiles; i++) + { + while (fgetc(mFile) > 0) + ; + } + } + + fread(&ctmMarker, 2, 1, mFile); + + int16 mapWidth, mapHeight; + fread(&mapWidth, 2, 1, mFile); + fread(&mapHeight, 2, 1, mFile); + + if (decoder == SFD_CTM_MAP_16) + { + mLimit = 2 * mapWidth * mapHeight; + return; + } + else if (decoder == SFD_CTM_MAP_8) + { + mMemSize = mapWidth * mapHeight; + mLimit = mMemSize; + mMemPos = 0; + mMemData = new uint8[mMemSize]; + for (int i = 0; i < mMemSize; i++) + { + int16 d; + fread(&d, 2, 1, mFile); + mMemData[i] = uint8(d); + } + return; + } + else + fseek(mFile, 2 * mapWidth * mapHeight, SEEK_CUR); + + mLimit = 0; +} + +void SourceFile::Limit(Errors* errors, const Location& location, SourceFileDecoder decoder, int skip, int limit) +{ + switch (decoder) + { + case SFD_CTM_CHARS: + case SFD_CTM_CHAR_ATTRIB_1: + case SFD_CTM_CHAR_ATTRIB_2: + case SFD_CTM_TILES_8: + case SFD_CTM_TILES_8_SW: + case SFD_CTM_TILES_16: + case SFD_CTM_MAP_8: + case SFD_CTM_MAP_16: + ReadCharPad(errors, location, decoder); + break; + + case SFD_SPD_SPRITES: + case SFD_SPD_TILES: + ReadSpritePad(errors, location, decoder); + break; + + default: + mLimit = limit; + if (mFile) + fseek(mFile, skip, SEEK_SET); + } +} + +SourceFile::SourceFile(void) + : mFile(nullptr), mFileName{ 0 }, mStack(nullptr), mMemData(nullptr) +{ + +} + +SourceFile::~SourceFile(void) +{ + if (mFile) + { + fclose(mFile); + mFile = nullptr; + } + + if (mMemData) + { + delete[] mMemData; + } +} + +bool SourceFile::Open(const char* name, const char* path, SourceFileMode mode) +{ + char fname[220]; + + if (strlen(name) + strlen(path) > 200) + return false; + + strcpy_s(fname, path); + ptrdiff_t n = strlen(fname); + + if (n > 0 && fname[n - 1] != '/') + { + fname[n++] = '/'; + fname[n] = 0; + } + + strcat_s(fname + n, sizeof(fname) - n, name); + + if (!fopen_s(&mFile, fname, "rb")) + { + _fullpath(mFileName, fname, sizeof(mFileName)); + char* p = mFileName; + while (*p) + { + if (*p == '\\') + *p = '/'; + p++; + } + strcpy_s(mLocationFileName, mFileName); + mMode = mode; + mLimit = 0x10000; + mFill = 0; + mPos = 0; + + return true; + } + + return false; +} + +void SourceFile::Close(void) +{ + if (mFile) + { + fclose(mFile); + mFile = nullptr; + } +} + +bool SourceFile::PushSource(void) +{ + SourceStack* stack = new SourceStack(); + stack->mUp = mStack; + mStack = stack; + stack->mFilePos = ftell(mFile); + stack->mLocation = mLocation; + return true; +} + +bool SourceFile::PopSource(void) +{ + SourceStack* stack = mStack; + if (stack) + { + mLocation = stack->mLocation; + fseek(mFile, stack->mFilePos, SEEK_SET); + mStack = mStack->mUp; + return true; + } + else + return false; +} + +bool SourceFile::DropSource(void) +{ + SourceStack* stack = mStack; + if (stack) + { + mStack = mStack->mUp; + return true; + } + else + return false; +} + +bool Preprocessor::NextLine(void) +{ + ptrdiff_t s = 0; + while (s < 32768 && mSource->ReadLine(mLine + s, 32768 - s)) + { + mLocation.mLine++; + + s = strlen(mLine); + while (s > 0 && (mLine[s - 1] == '\n' || mLine[s - 1] == '\r')) + s--; + if (s == 0 || mLine[s - 1] != '\\') + return true; + s--; + } + + return false; +} + +bool Preprocessor::EmbedData(const char* reason, const char* name, bool local, int skip, int limit, SourceFileMode mode, SourceFileDecoder decoder) +{ + if (strlen(name) > 200) + { + mErrors->Error(mLocation, EERR_FILE_NOT_FOUND, "Binary file path exceeds max path length"); + return false; + } + + if (mSource) + mSource->mLocation = mLocation; + + SourceFile* source = new SourceFile(); + + bool ok = false; + + if (source->Open(name, "", mode)) + ok = true; + + if (!ok && local && mSource) + { + char lpath[220]; + strcpy_s(lpath, mSource->mFileName); + ptrdiff_t i = strlen(lpath); + while (i > 0 && lpath[i - 1] != '/') + i--; + lpath[i] = 0; + + if (source->Open(name, lpath, mode)) + ok = true; + } + + SourcePath* p = mPaths; + while (!ok && p) + { + if (source->Open(name, p->mPathName, mode)) + ok = true; + else + p = p->mNext; + } + + if (ok) + { + if (mCompilerOptions & COPT_VERBOSE) + printf("%s \"%s\"\n", reason, source->mFileName); + + source->Limit(mErrors, mLocation, decoder, skip, limit); + + source->mUp = mSource; + mSource = source; + mLocation.mFileName = mSource->mLocationFileName; + mLocation.mLine = 0; + mLine[0] = 0; + + return true; + } + else + { + delete source; + return false; + } +} + +bool Preprocessor::OpenSource(const char * reason, const char* name, bool local) +{ + if (strlen(name) > 200) + { + mErrors->Error(mLocation, EERR_FILE_NOT_FOUND, "Source file path exceeds max path length"); + return false; + } + + if (mSource) + mSource->mLocation = mLocation; + + SourceFile * source = new SourceFile(); + + bool ok = false; + + if (source->Open(name, "")) + ok = true; + + if (!ok && local && mSource) + { + char lpath[220]; + strcpy_s(lpath, mSource->mFileName); + ptrdiff_t i = strlen(lpath); + while (i > 0 && lpath[i - 1] != '/') + i--; + lpath[i] = 0; + + if (source->Open(name, lpath)) + ok = true; + } + + SourcePath* p = mPaths; + while (!ok && p) + { + if (source->Open(name, p->mPathName)) + ok = true; + else + p = p->mNext; + } + + if (ok) + { + if (mCompilerOptions & COPT_VERBOSE) + printf("%s \"%s\"\n", reason, source->mFileName); + + source->mUp = mSource; + mSource = source; + mLocation.mFileName = mSource->mLocationFileName; + mLocation.mLine = 0; + mLine[0] = 0; + + return true; + } + else + return false; +} + +bool Preprocessor::CloseSource(void) +{ + if (mSource) + { + mSource = mSource->mUp; + if (mSource) + { + mLocation = mSource->mLocation; + mLine[0] = 0; + return true; + } + } + + return false; +} + +bool Preprocessor::PushSource(void) +{ + mSource->mLocation = mLocation; + return mSource->PushSource(); +} + +bool Preprocessor::PopSource(void) +{ + if (mSource->PopSource()) + { + mLocation = mSource->mLocation; + return true; + } + else + return false; + +} + +bool Preprocessor::DropSource(void) +{ + return mSource->DropSource(); +} + +Preprocessor::Preprocessor(Errors* errors) + : mSource(nullptr), mSourceList(nullptr), mPaths(nullptr), mErrors(errors), mCompilerOptions(COPT_DEFAULT) +{ + +} + +Preprocessor::~Preprocessor(void) +{ +} + +void Preprocessor::AddPath(const char* path) +{ + SourcePath* sp = new SourcePath(path); + sp->mNext = mPaths; + mPaths = sp; +} + diff --git a/oscar64/oscar64/Preprocessor.h b/oscar64/oscar64/Preprocessor.h new file mode 100644 index 0000000..bf10d48 --- /dev/null +++ b/oscar64/oscar64/Preprocessor.h @@ -0,0 +1,120 @@ +#pragma once + +#include "Errors.h" +#include +#include "MachineTypes.h" +#include "CompilerTypes.h" + +class SourceStack +{ +public: + SourceStack* mUp; + + int mFilePos; + Location mLocation; +}; + +enum SourceFileMode +{ + SFM_TEXT, + SFM_BINARY, + SFM_BINARY_WORD, + SFM_BINARY_RLE, + SFM_BINARY_LZO +}; + +enum SourceFileDecoder +{ + SFD_NONE, + SFD_CTM_CHARS, + SFD_CTM_CHAR_MATERIAL, + SFD_CTM_CHAR_ATTRIB_1, + SFD_CTM_CHAR_ATTRIB_2, + SFD_CTM_TILES_8, + SFD_CTM_TILES_8_SW, + SFD_CTM_TILES_16, + SFD_CTM_MAP_8, + SFD_CTM_MAP_16, + SFD_SPD_SPRITES, + SFD_SPD_TILES, +}; + +class SourceFile +{ +public: + char mFileName[MAXPATHLEN], mLocationFileName[MAXPATHLEN]; + + SourceFile * mUp, * mNext; + Location mLocation; + SourceStack * mStack; + SourceFileMode mMode; + int mLimit; + + uint8 mBuffer[512]; + int mFill, mPos, mMemPos, mMemSize; + uint8 * mMemData; + + bool ReadLine(char* line, ptrdiff_t limit); + + bool ReadLineRLE(char* line, ptrdiff_t limit); + bool ReadLineLZO(char* line, ptrdiff_t limit); + + SourceFile(void); + ~SourceFile(void); + + bool Open(const char* name, const char * path, SourceFileMode mode = SFM_TEXT); + void Close(void); + + void Limit(Errors * errors, const Location & location, SourceFileDecoder decoder, int skip, int limit); + + bool PushSource(void); + bool PopSource(void); + bool DropSource(void); + +protected: + FILE* mFile; + + void ReadCharPad(Errors* errors, const Location& location, SourceFileDecoder decoder); + void ReadSpritePad(Errors* errors, const Location& location, SourceFileDecoder decoder); + int ReadChar(void); +}; + +class SourcePath +{ +public: + char mPathName[MAXPATHLEN]; + + SourcePath* mNext; + + SourcePath(const char* path); + ~SourcePath(void); +}; + +class Preprocessor +{ +public: + char mLine[32768]; + + Location mLocation; + Errors* mErrors; + + SourceFile* mSource, * mSourceList; + SourcePath* mPaths; + + uint64 mCompilerOptions; + + void AddPath(const char* path); + bool NextLine(void); + + bool OpenSource(const char* reason, const char* name, bool local); + bool CloseSource(void); + + bool PushSource(void); + bool PopSource(void); + bool DropSource(void); + + bool EmbedData(const char* reason, const char* name, bool local, int skip, int limit, SourceFileMode mode, SourceFileDecoder decoder); + + Preprocessor(Errors * errors); + ~Preprocessor(void); +}; diff --git a/oscar64/oscar64/Scanner.cpp b/oscar64/oscar64/Scanner.cpp new file mode 100644 index 0000000..1ba3cbd --- /dev/null +++ b/oscar64/oscar64/Scanner.cpp @@ -0,0 +1,3063 @@ +#include "Scanner.h" +#include +#include +#include +#include +#include +#include "MachineTypes.h" + + +const char* TokenNames[] = +{ + "tk_none", + "tk_eof", + "tk_error", + "tk_eols", + + "'if'", + "'else'", + "'while'", + "'do'", + "'for'", + "'void'", + "'int'", + "'char'", + "'float'", + "'unsigned'", + "'signed'", + "'switch'", + "'case'", + "'default'", + "'break'", + "'return'", + "'goto'", + "'short'", + "'long'", + "'continue'", + "'integer'", + "'integeru'", + "'integerl'", + "'integerul'", + "'bool'", + "'const'", + "'volatile'", + "'typedef'", + "'struct'", + "'union'", + "'enum'", + "'sizeof'", + "'__bankof'", + "'static'", + "'auto'", + "'extern'", + "'inline'", + "'__assume'", + "'static_assert'", + + "__asm", + "__interrupt", + "__hwinterrupt", + "__native", + "__fastcall", + "__export", + "__zeropage", + "__noinline", + "__forceinline", + "__striped", + "__dynstack", + "__preserves", + "__memmap", + + "number", + "char", + "string literal", + "identifier", + "'true'", + "'false'", + "'nullptr'", + + "'+'", + "'-'", + "'*'", + "'/'", + "'%'", + + "'<<'", + "'>>'", + + "'++'", + "'--'", + + "'!'", + "'&&'", + "'||'", + + "'~'", + "'&'", + "'|'", + "'^'", + + "'=='", + "'!='", + "'>'", + "'>='", + "'<'", + "'<='", + + "'='", + "'+='", + "'-='", + "'*='", + "'/='", + "'%='", + "'<<='", + "'>>='", + "'&='", + "'^='", + "'|='", + + "'->'", + "'#'", + "'$'", + + "'('", + "')'", + + "'{'", + "'}'", + + "'['", + "']'", + + "'.'", + "'..'", + "'...'", + "','", + "';'", + "':'", + "'?'", + + "embedded", + + "'#define'", + "'#undef'", + "'#include'", + "'#if'", + "'#elif'", + "'#else'", + "'#endif'", + "'#ifdef'", + "'#ifndef'", + "'#error'", + "'#warning'", + "'#pragma'", + "'#line'", + + "'#assign'", + "'#repeat'", + "'#until'", + "'#embed'", + "'#for'", + "'##'", + "'#string'", + + "'namespace'", + "'using'", + "'this'", + "'::'", + "'class'", + "'public'", + "'protected'", + "'private'", + "'new'", + "'delete'", + "'virtual'", + "'operator'", + "'template'", + "'friend'", + "'constexpr'", + "'typename'", + "'decltype'", +}; + + +Macro::Macro(const Ident* ident, MacroDict * scope) + : mIdent(ident), mString(nullptr), mBuffer(nullptr), mSize(0), mNumArguments(-1), mScope(scope), mVariadic(false), mAssign(false) +{ + +} + +Macro::~Macro(void) +{ + delete[] mBuffer; +} + +void Macro::SetString(const char* str) +{ + ptrdiff_t s = strlen(str); + SetString(str, s); +} + +void Macro::SetString(const char* str, ptrdiff_t length) +{ + while (*str != 0 && *str <= ' ') + { + length--; + str++; + } + while (length > 0 && str[length - 1] <= ' ') + length--; + + if (length + 2 > mSize) + { + delete[] mBuffer; + mSize = (length + 9) & ~7; + mBuffer = new char[mSize]; + } + + if (length > 0) + memcpy(mBuffer, str, length); + + mBuffer[length] = ' '; + mBuffer[length + 1] = 0; + mString = mBuffer; +} + + +void Macro::AddArgument(const Ident * ident) +{ + mArguments[mNumArguments++] = ident; +} + +MacroDict::MacroDict(void) +{ + mHashSize = 0; + mHashFill = 0; + mHash = nullptr; +} + +MacroDict::~MacroDict(void) +{ + delete[] mHash; +} + +void MacroDict::Insert(Macro * macro) +{ + if (!mHash) + { + mHashSize = 16; + mHashFill = 0; + mHash = new MacroPtr[mHashSize]; + for (int i = 0; i < mHashSize; i++) + mHash[i] = nullptr; + } + + int hm = mHashSize - 1; + int hi = macro->mIdent->mHash & hm; + + while (mHash[hi]) + { + if (macro->mIdent == mHash[hi]->mIdent) + { + mHash[hi] = macro; + return; + } + + hi = (hi + 1) & hm; + } + + mHash[hi] = macro; + mHashFill++; + + if (2 * mHashFill >= mHashSize) + { + int size = mHashSize; + Macro ** entries = mHash; + mHashSize *= 2; + mHashFill = 0; + mHash = new MacroPtr[mHashSize]; + for (int i = 0; i < mHashSize; i++) + mHash[i] = nullptr; + + for (int i = 0; i < size; i++) + { + if (entries[i] && entries[i]->mIdent) + Insert(entries[i]); + } + delete[] entries; + } +} + +void MacroDict::Remove(const Ident* ident) +{ + if (mHashSize > 0) + { + int hm = mHashSize - 1; + int hi = ident->mHash & hm; + + while (mHash[hi]) + { + if (ident == mHash[hi]->mIdent) + { + mHash[hi]->mIdent = nullptr; + return; + } + + hi = (hi + 1) & hm; + } + } +} + +Macro* MacroDict::Lookup(const Ident* ident) +{ + if (mHashSize > 0) + { + int hm = mHashSize - 1; + int hi = ident->mHash & hm; + + while (mHash[hi]) + { + if (ident == mHash[hi]->mIdent) + return mHash[hi]; + hi = (hi + 1) & hm; + } + } + + return nullptr; +} + + +TokenSequence::TokenSequence(Scanner* scanner) + : mNext(nullptr), mLocation(scanner->mLocation), mToken(scanner->mToken), + mTokenIdent(scanner->mTokenIdent), + mTokenInteger(scanner->mTokenInteger), mTokenNumber(scanner->mTokenNumber), + mTokenString(nullptr) +{ + if (mToken == TK_STRING) + { + uint8 * str = new uint8[scanner->mTokenStringSize + 1]; + memcpy(str, scanner->mTokenString, scanner->mTokenStringSize + 1); + mTokenInteger = scanner->mTokenStringSize; + mTokenString = str; + } +} + +TokenSequence::~TokenSequence(void) +{ + delete[] mTokenString; +} + +Scanner::Scanner(Errors* errors, Preprocessor* preprocessor) + : mErrors(errors), mPreprocessor(preprocessor) +{ + mOffset = 0; + mLine = mPreprocessor->mLine; + mPrepCondFalse = 0; + mPrepCondDepth = 0; + mPrepCondExit = 0; + mAssemblerMode = false; + mPreprocessorMode = false; + mMacroExpansion = nullptr; + mMacroExpansionDepth = 0; + + mDefines = new MacroDict(); + mDefineArguments = nullptr; + mToken = TK_NONE; + mUngetToken = TK_NONE; + mReplay = nullptr; + mRecord = mRecordLast = mRecordPrev = nullptr; + + mOnceDict = new MacroDict(); + + mFileMacro = new Macro(Ident::Unique("__FILE__"), nullptr); + mDefines->Insert(mFileMacro); + + mLineMacro = new Macro(Ident::Unique("__LINE__"), nullptr); + mDefines->Insert(mLineMacro); + + NextChar(); + + assert(sizeof(TokenNames) == NUM_TOKENS * sizeof(char*)); +} + +Scanner::~Scanner(void) +{ + delete mDefines; +} + + +void Scanner::BeginRecord(void) +{ + mRecord = mRecordLast = mRecordPrev = new TokenSequence(this); +} + +TokenSequence* Scanner::CompleteRecord(void) +{ + TokenSequence* seq = mRecord; + mRecord = mRecordLast = mRecordPrev = nullptr; + return seq; +} + +const TokenSequence* Scanner::Replay(const TokenSequence* replay) +{ + const TokenSequence* seq = mReplay; + mReplay = replay; + NextToken(); + return seq; +} + +const char* Scanner::TokenName(Token token) const +{ + return TokenNames[token]; +} + + +void Scanner::SetAssemblerMode(bool mode) +{ + mAssemblerMode = mode; +} + +static bool IsIdentChar(char c) +{ + if (c >= 'a' && c <= 'z') + return true; + else if (c >= 'A' && c <= 'Z') + return true; + else if (c >= '0' && c <= '9') + return true; + else if (c == '_') + return true; + else + return false; +} + +static inline bool IsWhitespace(char ch) +{ + return ch <= 32; +} + +static inline bool IsLineBreak(char ch) +{ + return ch == '\n' || ch == '\r'; +} + +static inline bool IsAlpha(char ch) +{ + return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch == '_'; +} + +static inline bool IsAlphaNumeric(char ch) +{ + return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch == '_' || (ch >= '0' && ch <= '9'); +} + + +static inline bool IsNumeric(char ch) +{ + return ch >= '0' && ch <= '9'; +} + + +static inline bool IsHex(char ch) +{ + return (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F'); +} + +static inline int HexValue(char ch) +{ + if (ch >= '0' && ch <= '9') + return ch - '0'; + else if (ch >= 'a' && ch <= 'f') + return ch - 'a' + 10; + else if (ch >= 'A' && ch <= 'F') + return ch - 'A' + 10; + else + return 0; +} + +void Scanner::AddMacro(const Ident* ident, const char* value) +{ + Macro* macro = new Macro(ident, nullptr); + macro->SetString(value); + mDefines->Insert(macro); +} + +void Scanner::MarkSourceOnce(void) +{ + const Ident* fident = Ident::Unique(mPreprocessor->mSource->mFileName); + + Macro* macro = new Macro(fident, nullptr); + mOnceDict->Insert(macro); +} + +bool Scanner::IsIntegerToken(void) const +{ + return mToken == TK_INTEGER || mToken == TK_INTEGERU || mToken == TK_INTEGERL || mToken == TK_INTEGERUL; +} + +void Scanner::UngetToken(Token token) +{ + mUngetToken = mToken; + mToken = token; + if (mRecord) + mRecordLast = mRecordPrev; +} + +void Scanner::UngetToken(Token token, const Ident* ident) +{ + mUngetToken = mToken; + mToken = token; + mTokenIdent = ident; + if (mRecord) + mRecordLast = mRecordPrev; +} + +void Scanner::NextToken(void) +{ + if (mUngetToken) + { + mToken = mUngetToken; + mUngetToken = TK_NONE; + } + else if (mReplay) + { + mLocation = mReplay->mLocation; + mToken = mReplay->mToken; + + mTokenIdent = mReplay->mTokenIdent; + mTokenNumber = mReplay->mTokenNumber; + mTokenInteger = mReplay->mTokenInteger; + if (mReplay->mTokenString) + { + mTokenStringSize = (int)mReplay->mTokenInteger; + memcpy(mTokenString, mReplay->mTokenString, mTokenStringSize + 1); + } + + mReplay = mReplay->mNext; + } + else + NextPreToken(); + + if (mRecord) + { + mRecordPrev = mRecordLast; + mRecordLast->mNext = new TokenSequence(this); + mRecordLast = mRecordLast->mNext; + } +} + +void Scanner::NextPreToken(void) +{ + for (;;) + { + if (mPrepCondFalse > 0 || mPrepCondExit) + NextSkipRawToken(); + else + NextRawToken(); + + if (mToken == TK_PREP_ENDIF) + { + if (mPrepCondFalse > 0) + mPrepCondFalse--; + else if (mPrepCondExit) + mPrepCondExit = false; + else if (mPrepCondDepth > 0) + mPrepCondDepth--; + else + mErrors->Error(mLocation, EERR_INVALID_PREPROCESSOR, "Unexpected #endif"); + } + else if (mToken == TK_PREP_ELSE) + { + if (mPrepCondExit) + { + + } + else if (mPrepCondFalse == 1) + { + mPrepCondFalse = 0; + mPrepCondDepth++; + } + else if (mPrepCondFalse > 1) + { + } + else if (mPrepCondDepth > 0) + { + mPrepCondExit = true; + } + else + mErrors->Error(mLocation, EERR_INVALID_PREPROCESSOR, "Unexpected #else"); + } + else if (mToken == TK_PREP_ELIF) + { + if (mPrepCondExit) + { + + } + else if (mPrepCondFalse == 1) + { + mPreprocessorMode = true; + mPrepCondFalse = 0; + + NextPreToken(); + int64 v = PrepParseConditional(); + if (v) + { + mPrepCondFalse = 0; + mPrepCondDepth++; + } + else + mPrepCondFalse++; + + mPreprocessorMode = false; + if (mToken != TK_EOL) + mErrors->Error(mLocation, ERRR_PREPROCESSOR, "End of line expected"); + } + else if (mPrepCondFalse > 1) + { + } + else if (mPrepCondDepth > 0) + { + mPrepCondExit = true; + } + else + mErrors->Error(mLocation, EERR_INVALID_PREPROCESSOR, "Unexpected #else"); + } + else if (mToken == TK_EOF) + { + if (!mPreprocessor->CloseSource()) + return; + mToken = TK_NONE; + mOffset = 0; + } + else if (mPrepCondFalse > 0 || mPrepCondExit) + { + if (mToken == TK_PREP_IFDEF || mToken == TK_PREP_IFNDEF || mToken == TK_PREP_IF) + mPrepCondFalse++; + } + else if (mToken == TK_PREP_INCLUDE) + { + // Make sure include file names are not petsciid + uint64 op = mCompilerOptions; + mCompilerOptions &= ~COPT_PETSCII; + NextRawToken(); + mCompilerOptions = op; + + if (mToken == TK_STRING) + { + if (!mPreprocessor->OpenSource("Including", (const char *)mTokenString, true)) + mErrors->Error(mLocation, EERR_FILE_NOT_FOUND, "Could not open source file", (const char*)mTokenString); + else if (mOnceDict->Lookup(Ident::Unique(mPreprocessor->mSource->mFileName))) + mPreprocessor->CloseSource(); + } + else if (mToken == TK_LESS_THAN) + { + mOffset--; + StringToken('>', 'a'); + if (!mPreprocessor->OpenSource("Including", (const char*)mTokenString, false)) + mErrors->Error(mLocation, EERR_FILE_NOT_FOUND, "Could not open source file", (const char*)mTokenString); + else if (mOnceDict->Lookup(Ident::Unique(mPreprocessor->mSource->mFileName))) + mPreprocessor->CloseSource(); + } + } + else if (mToken == TK_PREP_LINE) + { + mPreprocessorMode = true; + NextPreToken(); + int l = mLocation.mLine; + int64 v = PrepParseConditional(); + if (mLocation.mLine == l && mToken == TK_STRING) + { + strcpy_s(mPreprocessor->mSource->mLocationFileName, (const char*)mTokenString); + NextRawToken(); + } + mPreprocessor->mLocation.mLine = int(v) + mLocation.mLine - l; + mPreprocessorMode = false; + } + else if (mToken == TK_PREP_FOR) + { + NextRawToken(); + if (mToken == TK_OPEN_PARENTHESIS) + { + NextRawToken(); + Macro* macro = new Macro(Ident::Unique("@for"), nullptr); + if (mToken == TK_IDENT) + { + const Ident* loopindex = mTokenIdent; + NextRawToken(); + + if (mToken == TK_COMMA) + { + mPreprocessorMode = true; + NextPreToken(); + int64 loopCount = PrepParseConditional(); + mPreprocessorMode = false; + + if (mToken == TK_CLOSE_PARENTHESIS) + { + int slen = mOffset; + bool quote = false; + while (mLine[slen] && (quote || mLine[slen] != '/' || mLine[slen + 1] != '/')) + { + if (mLine[slen] == '"') + quote = !quote; + slen++; + } + + macro->SetString(mLine + mOffset, slen - mOffset); + + mOffset = slen; + while (mLine[mOffset]) + mOffset++; + + if (loopCount > 0) + { + MacroExpansion* ex = new MacroExpansion(); + MacroDict* scope = mDefineArguments; + mDefineArguments = new MacroDict(); + + Macro* arg = new Macro(loopindex, scope); + mDefineArguments->Insert(arg); + + arg->SetString("0"); + + ex->mLine = mLine; + ex->mOffset = mOffset; + ex->mLink = mMacroExpansion; + ex->mChar = mTokenChar; + ex->mLoopCount = 0; + ex->mLoopIndex = arg; + ex->mLoopLimit = loopCount; + + mMacroExpansion = ex; + mMacroExpansionDepth++; + if (mMacroExpansionDepth > 1024) + mErrors->Error(mLocation, EFATAL_MACRO_EXPANSION_DEPTH, "Maximum macro expansion depth exceeded", mTokenIdent); + mLine = macro->mString; + mOffset = 0; + } + } + else + mErrors->Error(mLocation, EERR_INVALID_PREPROCESSOR, "')' expected in defined parameter list"); + } + else + mErrors->Error(mLocation, EERR_INVALID_PREPROCESSOR, "',' expected"); + + } + else + mErrors->Error(mLocation, EERR_INVALID_PREPROCESSOR, "'loop index variable expected"); + } + else + mErrors->Error(mLocation, EERR_INVALID_PREPROCESSOR, "'('(' expected"); + + } + else if (mToken == TK_PREP_DEFINE) + { + NextRawToken(); + if (mToken == TK_IDENT || mToken >= TK_IF && mToken <= TK_ASM || mToken == TK_TRUE || mToken == TK_FALSE) + { + if (mToken != TK_IDENT) + mTokenIdent = Ident::Unique(TokenNames[mToken]); + Macro* macro = new Macro(mTokenIdent, nullptr); + + if (mTokenChar == '(') + { + macro->mNumArguments = 0; + + NextRawToken(); + + if (mTokenChar == ')') + { + NextRawToken(); + } + else + { + // Macro with argument + do + { + NextRawToken(); + if (mToken == TK_IDENT) + { + macro->AddArgument(mTokenIdent); + NextRawToken(); + if (mToken == TK_ELLIPSIS) + { + macro->mVariadic = true; + NextRawToken(); + break; + } + } + else if (mToken == TK_ELLIPSIS) + { + macro->AddArgument(Ident::Unique("__VA_ARGS__")); + macro->mVariadic = true; + NextRawToken(); + break; + } + else + mErrors->Error(mLocation, EERR_INVALID_PREPROCESSOR, "Invalid define argument"); + + } while (mToken == TK_COMMA); + } + + if (mToken == TK_CLOSE_PARENTHESIS) + { + // No need to goto next token, mOffset is already behind it + } + else + mErrors->Error(mLocation, EERR_INVALID_PREPROCESSOR, "')' expected in defined parameter list"); + } + + int slen = mOffset; + bool quote = false; + while (mLine[slen] && (quote || mLine[slen] != '/' || mLine[slen + 1] != '/')) + { + if (mLine[slen] == '"') + quote = !quote; + slen++; + } + + macro->SetString(mLine + mOffset, slen - mOffset); + mDefines->Insert(macro); + mOffset = slen; + while (mLine[mOffset]) + mOffset++; + } + } + else if (mToken == TK_PREP_ERROR) + { + NextRawToken(); + + if (mToken == TK_STRING) + { + mErrors->Error(mLocation, ERRR_PREPROCESSOR, (const char*)mTokenString); + } + else + { + mErrors->Error(mLocation, EERR_INVALID_PREPROCESSOR, "Missing or invalid error message"); + } + exit(EXIT_FAILURE); + } + else if (mToken == TK_PREP_WARN) + { + NextRawToken(); + + if (mToken == TK_STRING) + { + this->Warning((const char*)mTokenString); + } + else + { + this->Warning("Missing or invalid warning message"); + } + } + else if (mToken == TK_PREP_IDENT) + { + if (mTokenIdent->mString[0]) + { + Macro* def = nullptr; + if (mDefineArguments) + def = mDefineArguments->Lookup(mTokenIdent); + if (!def) + def = mDefines->Lookup(mTokenIdent); + + if (def) + { + if (def->mNumArguments == -1) + { + mToken = TK_STRING; + int i = 0; + while ((mTokenString[i] = def->mString[i])) + i++; + mTokenStringSize = i; + return; + } + else + mErrors->Error(mLocation, EERR_INVALID_PREPROCESSOR, "Invalid preprocessor command", mTokenIdent); + } + else + mErrors->Error(mLocation, EERR_INVALID_PREPROCESSOR, "Invalid preprocessor command", mTokenIdent); + } + else + { + mToken = TK_HASH; + return; + } + } + else if (mToken == TK_PREP_UNDEF) + { + NextRawToken(); + if (mToken == TK_IDENT) + { + mDefines->Remove(mTokenIdent); + } + + } + else if (mToken == TK_PREP_IFDEF) + { + NextRawToken(); + if (mToken == TK_IDENT) + { + Macro * def = mDefines->Lookup(mTokenIdent); + if (def) + mPrepCondDepth++; + else + mPrepCondFalse++; + } + } + else if (mToken == TK_PREP_IFNDEF) + { + NextRawToken(); + if (mToken == TK_IDENT) + { + Macro * def = mDefines->Lookup(mTokenIdent); + if (!def) + mPrepCondDepth++; + else + mPrepCondFalse++; + } + } + else if (mToken == TK_PREP_IF) + { + mPreprocessorMode = true; + NextPreToken(); + int64 v = PrepParseConditional(); + if (v) + mPrepCondDepth++; + else + mPrepCondFalse++; + mPreprocessorMode = false; + if (mToken != TK_EOL) + mErrors->Error(mLocation, ERRR_PREPROCESSOR, "End of line expected"); + } + else if (mToken == TK_PREP_ASSIGN) + { + mPreprocessorMode = true; + NextRawToken(); + if (mToken == TK_IDENT) + { + const Ident* ident = mTokenIdent; + + NextPreToken(); + + int64 v = PrepParseConditional(); + Macro* macro = mDefines->Lookup(ident); + if (!macro) + { + macro = new Macro(ident, nullptr); + mDefines->Insert(macro); + } + char buffer[20]; + sprintf_s(buffer, "%d", int(v)); + macro->SetString(buffer); + macro->mAssign = true; + mPreprocessorMode = false; + if (mToken != TK_EOL) + mErrors->Error(mLocation, ERRR_PREPROCESSOR, "End of line expected"); + } + } + else if (mToken == TK_PREP_REPEAT) + { + mPreprocessor->PushSource(); + } + else if (mToken == TK_PREP_UNTIL) + { + mPreprocessorMode = true; + NextPreToken(); + int64 v = PrepParseConditional(); + if (mToken != TK_EOL) + mErrors->Error(mLocation, ERRR_PREPROCESSOR, "End of line expected"); + + if (v) + mPreprocessor->DropSource(); + else + { + mPreprocessor->PopSource(); + mPreprocessor->PushSource(); + mPreprocessor->NextLine(); + mOffset = 0; + NextChar(); + } + mPreprocessorMode = false; + } + else if (mToken == TK_PREP_EMBED) + { + int limit = 65536, skip = 0; + SourceFileMode mode = SFM_BINARY; + SourceFileDecoder decoder = SFD_NONE; + + uint64 op = mCompilerOptions; + mCompilerOptions &= ~COPT_PETSCII; + + NextPreToken(); + if (IsIntegerToken()) + { + limit = int(mTokenInteger); + NextPreToken(); + + if (IsIntegerToken()) + { + skip = int(mTokenInteger); + NextPreToken(); + } + } + + while (mToken == TK_IDENT) + { + if (!strcmp(mTokenIdent->mString, "rle")) + mode = SFM_BINARY_RLE; + else if (!strcmp(mTokenIdent->mString, "lzo")) + mode = SFM_BINARY_LZO; + else if (!strcmp(mTokenIdent->mString, "word")) + mode = SFM_BINARY_WORD; + else if (!strcmp(mTokenIdent->mString, "ctm_chars")) + decoder = SFD_CTM_CHARS; + else if (!strcmp(mTokenIdent->mString, "ctm_material")) + decoder = SFD_CTM_CHAR_MATERIAL; + else if (!strcmp(mTokenIdent->mString, "ctm_attr1")) + decoder = SFD_CTM_CHAR_ATTRIB_1; + else if (!strcmp(mTokenIdent->mString, "ctm_attr2")) + decoder = SFD_CTM_CHAR_ATTRIB_2; + else if (!strcmp(mTokenIdent->mString, "ctm_tiles8")) + decoder = SFD_CTM_TILES_8; + else if (!strcmp(mTokenIdent->mString, "ctm_tiles8sw")) + decoder = SFD_CTM_TILES_8_SW; + else if (!strcmp(mTokenIdent->mString, "ctm_tiles16")) + decoder = SFD_CTM_TILES_16; + else if (!strcmp(mTokenIdent->mString, "ctm_map8")) + decoder = SFD_CTM_MAP_8; + else if (!strcmp(mTokenIdent->mString, "ctm_map16")) + decoder = SFD_CTM_MAP_16; + else if (!strcmp(mTokenIdent->mString, "spd_sprites")) + decoder = SFD_SPD_SPRITES; + else if (!strcmp(mTokenIdent->mString, "spd_tiles")) + decoder = SFD_SPD_TILES; + else + mErrors->Error(mLocation, EERR_FILE_NOT_FOUND, "Invalid embed compression mode", mTokenIdent); + + NextPreToken(); + } + + if (mToken == TK_STRING) + { + if (!mPreprocessor->EmbedData("Embedding", (const char*)mTokenString, true, skip, limit, mode, decoder)) + mErrors->Error(mLocation, EERR_FILE_NOT_FOUND, "Could not open source file", (const char*)mTokenString); + } + else if (mToken == TK_LESS_THAN) + { + mOffset--; + StringToken('>', 'a'); + if (!mPreprocessor->EmbedData("Embedding", (const char*)mTokenString, false, skip, limit, mode, decoder)) + mErrors->Error(mLocation, EERR_FILE_NOT_FOUND, "Could not open source file", (const char*)mTokenString); + } + + mCompilerOptions = op; + } + else if (mToken == TK_IDENT) + { + if (mTokenChar == '#' && mLine[mOffset++] == '#') + { + char tkistr[1024]; + + Macro* def = nullptr; + if (mDefineArguments) + def = mDefineArguments->Lookup(mTokenIdent); + if (!def) + { + def = mDefines->Lookup(mTokenIdent); + if (def && !def->mAssign) + def = nullptr; + } + + if (def) + strcpy_s(tkistr, def->mString); + else + strcpy_s(tkistr, mTokenIdent->mString); + + size_t i = strlen(tkistr); + while (i > 0 && tkistr[i - 1] == ' ') i--; + tkistr[i] = 0; + + do { + char tkprep[200]; + int n = 0; + while (NextChar() && IsAlphaNumeric(mTokenChar)) + tkprep[n++] = mTokenChar; + tkprep[n] = 0; + mTokenIdent = Ident::Unique(tkprep); + Macro* def = nullptr; + if (mDefineArguments) + def = mDefineArguments->Lookup(mTokenIdent); + if (!def) + { + def = mDefines->Lookup(mTokenIdent); + if (def && !def->mAssign) + def = nullptr; + } + + if (def) + strcat_s(tkistr, def->mString); + else + strcat_s(tkistr, mTokenIdent->mString); + + size_t i = strlen(tkistr); + while (i > 0 && tkistr[i - 1] == ' ') i--; + tkistr[i] = 0; + + } while (mTokenChar == '#' && mLine[mOffset++] == '#'); + mTokenIdent = Ident::Unique(tkistr); + } + + Macro* def = nullptr; + if (mDefineArguments) + def = mDefineArguments->Lookup(mTokenIdent); + if (!def) + def = mDefines->Lookup(mTokenIdent); + + if (def) + { + MacroExpansion* ex = new MacroExpansion(); + ex->mDefinedArguments = mDefineArguments; + + if (def->mNumArguments == 0) + { + NextRawToken(); + if (mToken != TK_OPEN_PARENTHESIS) + mErrors->Error(mLocation, EERR_INVALID_PREPROCESSOR, "Missing '(' for macro expansion"); + else + NextRawToken(); + if (mToken != TK_CLOSE_PARENTHESIS) + mErrors->Error(mLocation, EERR_INVALID_PREPROCESSOR, "Missing ')' for macro expansion"); + } + else if (def->mNumArguments > 0) + { + MacroDict* scope = mDefineArguments; + + mDefineArguments = new MacroDict(); + NextRawToken(); + if (mToken == TK_OPEN_PARENTHESIS) + { + mOffset--; + for (int i = 0; i < def->mNumArguments; i++) + { + int offset = mOffset; + int level = 0; + char quoteChar = 0; // 0=not in quote, '"'=double-quote, '\''=single-quote + char argbuf[4096]; + int arglen = 0; + bool argEof = false; + + for (;;) + { + // fetch next line if we've reached end of current line + while (!mLine[offset]) + { + if (!mPreprocessor->NextLine()) + { + argEof = true; + break; + } + offset = 0; + } + + char ch = mLine[offset]; + if (!ch) + break; + + bool isLastArg = def->mVariadic && i + 1 == def->mNumArguments; + if (!quoteChar && level == 0 && (!isLastArg && ch == ',')) + break; + if (!quoteChar && level == 0 && ch == ')') + break; + + if (quoteChar) + { + // inside a quoted string/char: skip escaped characters + if (ch == '\\' && mLine[offset + 1]) + { + if (arglen < (int)sizeof(argbuf) - 1) + argbuf[arglen++] = ch; + offset++; + ch = mLine[offset]; + } + else if (ch == quoteChar) + quoteChar = 0; + } + else if (ch == '"' || ch == '\'') + quoteChar = ch; + else if (ch == '(' || ch == '{') + level++; + else if (ch == ')' || ch == '}') + { + if (level == 0) + mErrors->Error(mLocation, EERR_INVALID_PREPROCESSOR, "Unbalanced closing bracket in macro argument"); + else + level--; + } + + if (arglen >= (int)sizeof(argbuf) - 1) + { + mErrors->Error(mLocation, EERR_INVALID_PREPROCESSOR, "Macro argument expansion exceeds buffer limit"); + break; + } + argbuf[arglen++] = ch; + offset++; + } + argbuf[arglen] = 0; + + if (argEof) + mErrors->Error(mLocation, EERR_INVALID_PREPROCESSOR, "Unexpected end of file in macro argument"); + else if (level != 0) + mErrors->Error(mLocation, EERR_INVALID_PREPROCESSOR, "Unbalanced brackets in macro argument"); + + // trim leading/trailing whitespace from argument + int astart = 0; + while (astart < arglen && isspace(argbuf[astart])) astart++; + int aend = arglen; + while (aend > astart && isspace(argbuf[aend - 1])) aend--; + argbuf[aend] = 0; + + Macro* arg = new Macro(def->mArguments[i], scope); + arg->SetString(argbuf + astart); + mDefineArguments->Insert(arg); + + if (def->mVariadic && i + 1 == def->mNumArguments) + { + Macro* vaarg = new Macro(Ident::Unique("__VA_ARGS__"), scope); + vaarg->SetString(argbuf + astart); + mDefineArguments->Insert(vaarg); + } + + mOffset = offset; + + if (i + 1 != def->mNumArguments) + { + if (mLine[mOffset] == ',') + mOffset++; + else if (def->mVariadic && i + 2 == def->mNumArguments && mLine[mOffset] == ')') + ; + else + mErrors->Error(mLocation, EERR_INVALID_PREPROCESSOR, "Invalid define expansion argument"); + } + else + { + if (mLine[mOffset] == ')') + mOffset++; + else + mErrors->Error(mLocation, EERR_INVALID_PREPROCESSOR, "Invalid define expansion closing argument"); + } + } + } + else + mErrors->Error(mLocation, EERR_INVALID_PREPROCESSOR, "Missing arguments for macro expansion"); + NextChar(); + } + else + mDefineArguments = def->mScope; + + if (def == mFileMacro) + { + char fileName[256]; + sprintf_s(fileName, "\"%s\"", mPreprocessor->mSource->mFileName); + def->SetString(fileName); + } + else if (def == mLineMacro) + { + char lineNumber[16]; + sprintf_s(lineNumber, "%d", mPreprocessor->mLocation.mLine); + def->SetString(lineNumber); + } + + ex->mLine = mLine; + ex->mOffset = mOffset; + ex->mLink = mMacroExpansion; + ex->mChar = mTokenChar; + + mMacroExpansion = ex; + mMacroExpansionDepth++; + if (mMacroExpansionDepth > 1024) + mErrors->Error(mLocation, EFATAL_MACRO_EXPANSION_DEPTH, "Maximum macro expansion depth exceeded", mTokenIdent); + mLine = def->mString; + mOffset = 0; + NextChar(); + } + else + { + while (mTokenChar == ' ') + NextChar(); + + if (mTokenChar == '#' && mLine[mOffset] == '#') + { + char tkbase[256]; + strcpy_s(tkbase, mTokenIdent->mString); + + do { + mOffset++; + NextChar(); + + ptrdiff_t n = 0; + char tkident[256]; + while (IsIdentChar(mTokenChar)) + { + if (n < 255) + tkident[n++] = mTokenChar; + NextChar(); + } + tkident[n] = 0; + + const Ident* ntkident = Ident::Unique(tkident); + + Macro* def = nullptr; + if (mDefineArguments) + def = mDefineArguments->Lookup(ntkident); + if (!def) + def = mDefines->Lookup(ntkident); + + if (def) + strcat_s(tkbase, def->mString); + else + strcat_s(tkbase, tkident); + + n = strlen(tkbase); + while (n > 0 && tkbase[n - 1] == ' ') + n--; + tkbase[n] = 0; + + while (mTokenChar == ' ') + NextChar(); + + } while (mTokenChar == '#' && mLine[mOffset] == '#'); + + ptrdiff_t n = strlen(tkbase); + char* str = new char[n + 1]; + strcpy_s(str, n + 1, tkbase); + + MacroExpansion* ex = new MacroExpansion(); + ex->mDefinedArguments = mDefineArguments; + + ex->mLine = mLine; + ex->mOffset = mOffset; + ex->mLink = mMacroExpansion; + ex->mChar = mTokenChar; + + mMacroExpansion = ex; + mMacroExpansionDepth++; + if (mMacroExpansionDepth > 1024) + mErrors->Error(mLocation, EFATAL_MACRO_EXPANSION_DEPTH, "Maximum macro expansion depth exceeded", mTokenIdent); + mLine = str; + mOffset = 0; + NextChar(); + } + else + return; + } + } + else + return; + } +} + +void Scanner::NextSkipRawToken(void) +{ + while (mToken != TK_EOF) + { + mToken = TK_ERROR; + + if (mOffset > 1 || !IsWhitespace(mTokenChar)) + mStartOfLine = false; + + while (IsWhitespace(mTokenChar)) + { + if (mTokenChar == '\n') + { + mToken = TK_EOL; + NextChar(); + return; + } + if (!NextChar()) + { + mToken = TK_EOF; + return; + } + } + + mLocation = mPreprocessor->mLocation; + mLocation.mColumn = mOffset; + + if (mTokenChar == '#') + { + if (!(mAssemblerMode || mPrepCondFalse) || mOffset == 1 || mStartOfLine) + { + int n = 0; + char tkprep[128]; + tkprep[0] = 0; + + while (NextChar() && IsAlpha(mTokenChar)) + { + if (n < 127) + tkprep[n++] = mTokenChar; + } + tkprep[n] = 0; + + if (n == 0 && mTokenChar == '#') + { + NextChar(); + mToken = TK_PREP_CONCAT; + } + else if (!strcmp(tkprep, "define")) + mToken = TK_PREP_DEFINE; + else if (!strcmp(tkprep, "error")) + mToken = TK_PREP_ERROR; + else if (!strcmp(tkprep, "warning")) + mToken = TK_PREP_WARN; + else if (!strcmp(tkprep, "undef")) + mToken = TK_PREP_UNDEF; + else if (!strcmp(tkprep, "include")) + mToken = TK_PREP_INCLUDE; + else if (!strcmp(tkprep, "if")) + mToken = TK_PREP_IF; + else if (!strcmp(tkprep, "ifdef")) + mToken = TK_PREP_IFDEF; + else if (!strcmp(tkprep, "ifndef")) + mToken = TK_PREP_IFNDEF; + else if (!strcmp(tkprep, "elif")) + mToken = TK_PREP_ELIF; + else if (!strcmp(tkprep, "else")) + mToken = TK_PREP_ELSE; + else if (!strcmp(tkprep, "endif")) + mToken = TK_PREP_ENDIF; + else if (!strcmp(tkprep, "pragma")) + mToken = TK_PREP_PRAGMA; + else if (!strcmp(tkprep, "line")) + mToken = TK_PREP_LINE; + else if (!strcmp(tkprep, "assign")) + mToken = TK_PREP_ASSIGN; + else if (!strcmp(tkprep, "repeat")) + mToken = TK_PREP_REPEAT; + else if (!strcmp(tkprep, "until")) + mToken = TK_PREP_UNTIL; + else if (!strcmp(tkprep, "embed")) + mToken = TK_PREP_EMBED; + else if (!strcmp(tkprep, "for")) + mToken = TK_PREP_FOR; + else + { + mToken = TK_PREP_IDENT; + mTokenIdent = Ident::Unique(tkprep); + } + } + else + { + NextChar(); + mToken = TK_HASH; + } + + return; + } + + while (mTokenChar != '\n') + { + if (!NextChar()) + { + mToken = TK_EOF; + return; + } + } + } +} + +void Scanner::NextRawToken(void) +{ + if (mUngetToken) + { + mToken = mUngetToken; + mUngetToken = TK_NONE; + } + else if (mToken != TK_EOF) + { + mToken = TK_ERROR; + + if (mOffset > 1 || !IsWhitespace(mTokenChar)) + mStartOfLine = false; + + while (IsWhitespace(mTokenChar)) + { + if ((mAssemblerMode || mPreprocessorMode) && mTokenChar == '\n') + { + mToken = TK_EOL; + NextChar(); + return; + } + if (!NextChar()) + { + mToken = TK_EOF; + return; + } + } + + mLocation = mPreprocessor->mLocation; + mLocation.mColumn = mOffset; + + switch (mTokenChar) + { + case '+': + mToken = TK_ADD; + NextChar(); + if (mTokenChar == '+') + { + NextChar(); + mToken = TK_INC; + } + else if (mTokenChar == '=') + { + NextChar(); + mToken = TK_ASSIGN_ADD; + } + break; + case '-': + mToken = TK_SUB; + NextChar(); + if (mTokenChar == '-') + { + NextChar(); + mToken = TK_DEC; + } + else if (mTokenChar == '=') + { + NextChar(); + mToken = TK_ASSIGN_SUB; + } + else if (mTokenChar == '>') + { + NextChar(); + mToken = TK_ARROW; + } + break; + case '*': + mToken = TK_MUL; + NextChar(); + if (mTokenChar == '=') + { + NextChar(); + mToken = TK_ASSIGN_MUL; + } + break; + case '/': + mToken = TK_DIV; + NextChar(); + if (mTokenChar == '*') + { + bool first = true; + while (first || mTokenChar != '/') + { + if (mTokenChar == '*') + first = false; + else + first = true; + if (!NextChar()) + { + mToken = TK_ERROR; + Error("Multiline comment not closed"); + return; + } + } + NextChar(); + NextPreToken(); + } + else if (mTokenChar == '/') + { + NextChar(); + while (!IsLineBreak(mTokenChar) && NextChar()) + ; + NextPreToken(); + } + else if (mTokenChar == '=') + { + NextChar(); + mToken = TK_ASSIGN_DIV; + } + break; + case '%': + NextChar(); + if (mAssemblerMode && mTokenChar >= '0' && mTokenChar <= '1') + { + int n = 0; + int64 mant = 0; + while (mTokenChar >= '0' && mTokenChar <= '1') + { + mant = mant * 2 + (mTokenChar - '0'); + NextChar(); + n++; + } + + if (n == 0) + mErrors->Error(mLocation, EERR_SYNTAX, "Missing digits in hex constant"); + + mToken = TK_INTEGER; + mTokenInteger = mant; + } + else + { + mToken = TK_MOD; + if (mTokenChar == '=') + { + NextChar(); + mToken = TK_ASSIGN_MOD; + } + } + break; + case '~': + mToken = TK_BINARY_NOT; + NextChar(); + break; + case '^': + mToken = TK_BINARY_XOR; + NextChar(); + if (mTokenChar == '=') + { + NextChar(); + mToken = TK_ASSIGN_XOR; + } + break; + + case '&': + mToken = TK_BINARY_AND; + NextChar(); + if (mTokenChar == '&') + { + NextChar(); + mToken = TK_LOGICAL_AND; + } + else if (mTokenChar == '=') + { + NextChar(); + mToken = TK_ASSIGN_AND; + } + break; + case '|': + mToken = TK_BINARY_OR; + NextChar(); + if (mTokenChar == '|') + { + NextChar(); + mToken = TK_LOGICAL_OR; + } + else if (mTokenChar == '=') + { + NextChar(); + mToken = TK_ASSIGN_OR; + } + break; + + case '(': + mToken = TK_OPEN_PARENTHESIS; + NextChar(); + break; + case ')': + mToken = TK_CLOSE_PARENTHESIS; + NextChar(); + break; + case '{': + mToken = TK_OPEN_BRACE; + NextChar(); + break; + case '}': + mToken = TK_CLOSE_BRACE; + NextChar(); + break; + case '[': + mToken = TK_OPEN_BRACKET; + NextChar(); + break; + case ']': + mToken = TK_CLOSE_BRACKET; + NextChar(); + break; + + case '.': + mToken = TK_DOT; + NextChar(); + if (mTokenChar == '.') + { + NextChar(); + mToken = TK_DOTDOT; + if (mTokenChar == '.') + { + NextChar(); + mToken = TK_ELLIPSIS; + } + } + break; + case ',': + mToken = TK_COMMA; + NextChar(); + break; + case ';': + mToken = TK_SEMICOLON; + NextChar(); + break; + case ':': + mToken = TK_COLON; + NextChar(); + if (mTokenChar == ':' && (mCompilerOptions & COPT_CPLUSPLUS)) + { + mToken = TK_COLCOLON; + NextChar(); + } + break; + case '?': + mToken = TK_QUESTIONMARK; + NextChar(); + break; + + case '=': + mToken = TK_ASSIGN; + NextChar(); + if (mTokenChar == '=') + { + mToken = TK_EQUAL; + NextChar(); + } + + break; + case '!': + mToken = TK_LOGICAL_NOT; + NextChar(); + if (mTokenChar == '=') + { + mToken = TK_NOT_EQUAL; + NextChar(); + } + break; + case '<': + mToken = TK_LESS_THAN; + NextChar(); + if (mTokenChar == '=') + { + NextChar(); + mToken = TK_LESS_EQUAL; + } + else if (mTokenChar == '<') + { + NextChar(); + mToken = TK_LEFT_SHIFT; + if (mTokenChar == '=') + { + NextChar(); + mToken = TK_ASSIGN_SHL; + } + } + break; + case '>': + mToken = TK_GREATER_THAN; + NextChar(); + if (mTokenChar == '=') + { + NextChar(); + mToken = TK_GREATER_EQUAL; + } + else if (mTokenChar == '>') + { + mToken = TK_RIGHT_SHIFT; + NextChar(); + if (mTokenChar == '=') + { + NextChar(); + mToken = TK_ASSIGN_SHR; + } + } + break; + + case '\'': + if (mCompilerOptions & COPT_PETSCII) + CharToken('p'); + else + CharToken('a'); + break; + case '"': + if (mCompilerOptions & COPT_PETSCII) + StringToken(mTokenChar, 'p'); + else + StringToken(mTokenChar, 'a'); + break; + + case '#': + { + if (!(mAssemblerMode || mPrepCondFalse) || mOffset == 1 || mStartOfLine) + { + int n = 0; + char tkprep[128]; + tkprep[0] = 0; + + while (NextChar() && IsAlpha(mTokenChar)) + { + if (n < 127) + tkprep[n++] = mTokenChar; + } + tkprep[n] = 0; + + if (n == 0 && mTokenChar == '#') + { + NextChar(); + mToken = TK_PREP_CONCAT; + } + else if (!strcmp(tkprep, "define")) + mToken = TK_PREP_DEFINE; + else if (!strcmp(tkprep, "error")) + mToken = TK_PREP_ERROR; + else if (!strcmp(tkprep, "warning")) + mToken = TK_PREP_WARN; + else if (!strcmp(tkprep, "undef")) + mToken = TK_PREP_UNDEF; + else if (!strcmp(tkprep, "include")) + mToken = TK_PREP_INCLUDE; + else if (!strcmp(tkprep, "if")) + mToken = TK_PREP_IF; + else if (!strcmp(tkprep, "ifdef")) + mToken = TK_PREP_IFDEF; + else if (!strcmp(tkprep, "ifndef")) + mToken = TK_PREP_IFNDEF; + else if (!strcmp(tkprep, "elif")) + mToken = TK_PREP_ELIF; + else if (!strcmp(tkprep, "else")) + mToken = TK_PREP_ELSE; + else if (!strcmp(tkprep, "endif")) + mToken = TK_PREP_ENDIF; + else if (!strcmp(tkprep, "pragma")) + mToken = TK_PREP_PRAGMA; + else if (!strcmp(tkprep, "line")) + mToken = TK_PREP_LINE; + else if (!strcmp(tkprep, "assign")) + mToken = TK_PREP_ASSIGN; + else if (!strcmp(tkprep, "repeat")) + mToken = TK_PREP_REPEAT; + else if (!strcmp(tkprep, "until")) + mToken = TK_PREP_UNTIL; + else if (!strcmp(tkprep, "embed")) + mToken = TK_PREP_EMBED; + else if (!strcmp(tkprep, "for")) + mToken = TK_PREP_FOR; + else + { + mToken = TK_PREP_IDENT; + mTokenIdent = Ident::Unique(tkprep); + } + } + else + { + NextChar(); + mToken = TK_HASH; + } + break; + } + + case '$': + + if (mAssemblerMode) + { + int n = 0; + int64 mant = 0; + while (NextChar()) + { + if (mTokenChar >= '0' && mTokenChar <= '9') + mant = mant * 16 + (int)mTokenChar - (int)'0'; + else if (mTokenChar >= 'a' && mTokenChar <= 'f') + mant = mant * 16 + 10 + (int)mTokenChar - (int)'a'; + else if (mTokenChar >= 'A' && mTokenChar <= 'F') + mant = mant * 16 + 10 + (int)mTokenChar - (int)'A'; + else + break; + n++; + } + + if (n == 0) + mErrors->Error(mLocation, EERR_SYNTAX, "Missing digits in hex constant"); + + if (mTokenChar == 'L' || mTokenChar == 'l') + { + NextChar(); + mToken = TK_INTEGERUL; + } + else + mToken = TK_INTEGERU; + mTokenInteger = mant; + } + else + { + NextChar(); + mToken = TK_DOLLAR; + } + break; + + default: + if (mTokenChar >= '0' && mTokenChar <= '9') + { + ParseNumberToken(); + } + else if (mTokenChar >= 'A' && mTokenChar <= 'Z' || mTokenChar >= 'a' && mTokenChar <= 'z' || mTokenChar == '_') + { + int n = 0; + char tkident[256]; + tkident[0] = 0; + + for (;;) + { + if (IsIdentChar(mTokenChar)) + { + if (n < 255) + tkident[n++] = mTokenChar; + NextChar(); + } + else + break; + } + if (n == 1) + { + if (mTokenChar == '"') + { + StringToken(mTokenChar, tkident[0]); + break; + } + else if (mTokenChar == '\'') + { + CharToken(tkident[0]); + break; + } + } + tkident[n] = 0; + if (n == 256) + Error("Identifier exceeds max character limit"); + + if (!strcmp(tkident, "true")) + mToken = TK_TRUE; + else if (!strcmp(tkident, "false")) + mToken = TK_FALSE; + else if (!strcmp(tkident, "nullptr")) + mToken = TK_NULL; + else if (!strcmp(tkident, "int")) + mToken = TK_INT; + else if (!strcmp(tkident, "float")) + mToken = TK_FLOAT; + else if (!strcmp(tkident, "bool") || !strcmp(tkident, "_Bool")) + mToken = TK_BOOL; + else if (!strcmp(tkident, "char")) + mToken = TK_CHAR; + else if (!strcmp(tkident, "short")) + mToken = TK_SHORT; + else if (!strcmp(tkident, "long")) + mToken = TK_LONG; + else if (!strcmp(tkident, "unsigned")) + mToken = TK_UNSIGNED; + else if (!strcmp(tkident, "signed")) + mToken = TK_SIGNED; + else if (!strcmp(tkident, "const")) + mToken = TK_CONST; + else if (!strcmp(tkident, "volatile")) + mToken = TK_VOLATILE; + else if (!strcmp(tkident, "if")) + mToken = TK_IF; + else if (!strcmp(tkident, "else")) + mToken = TK_ELSE; + else if (!strcmp(tkident, "while")) + mToken = TK_WHILE; + else if (!strcmp(tkident, "do")) + mToken = TK_DO; + else if (!strcmp(tkident, "for")) + mToken = TK_FOR; + else if (!strcmp(tkident, "switch")) + mToken = TK_SWITCH; + else if (!strcmp(tkident, "case")) + mToken = TK_CASE; + else if (!strcmp(tkident, "default")) + mToken = TK_DEFAULT; + else if (!strcmp(tkident, "break")) + mToken = TK_BREAK; + else if (!strcmp(tkident, "continue")) + mToken = TK_CONTINUE; + else if (!strcmp(tkident, "return")) + mToken = TK_RETURN; + else if (!strcmp(tkident, "goto")) + mToken = TK_GOTO; + else if (!strcmp(tkident, "void")) + mToken = TK_VOID; + else if (!strcmp(tkident, "struct")) + mToken = TK_STRUCT; + else if (!strcmp(tkident, "union")) + mToken = TK_UNION; + else if (!strcmp(tkident, "enum")) + mToken = TK_ENUM; + else if (!strcmp(tkident, "sizeof")) + mToken = TK_SIZEOF; + else if (!strcmp(tkident, "__bankof")) + mToken = TK_BANKOF; + else if (!strcmp(tkident, "typedef")) + mToken = TK_TYPEDEF; + else if (!strcmp(tkident, "static")) + mToken = TK_STATIC; + else if (!strcmp(tkident, "auto")) + mToken = TK_AUTO; + else if (!strcmp(tkident, "extern")) + mToken = TK_EXTERN; + else if (!strcmp(tkident, "inline")) + mToken = TK_INLINE; + else if (!strcmp(tkident, "__asm")) + mToken = TK_ASM; + else if (!strcmp(tkident, "__assume")) + mToken = TK_ASSUME; + else if (!strcmp(tkident, "static_assert")) + mToken = TK_STATIC_ASSERT; + else if (!strcmp(tkident, "__interrupt")) + mToken = TK_INTERRUPT; + else if (!strcmp(tkident, "__hwinterrupt")) + mToken = TK_HWINTERRUPT; + else if (!strcmp(tkident, "__native")) + mToken = TK_NATIVE; + else if (!strcmp(tkident, "__fastcall")) + mToken = TK_FASTCALL; + else if (!strcmp(tkident, "__export")) + mToken = TK_EXPORT; + else if (!strcmp(tkident, "__zeropage")) + mToken = TK_ZEROPAGE; + else if (!strcmp(tkident, "__noinline")) + mToken = TK_NOINLINE; + else if (!strcmp(tkident, "__forceinline")) + mToken = TK_FORCEINLINE; + else if (!strcmp(tkident, "__striped")) + mToken = TK_STRIPED; + else if (!strcmp(tkident, "__dynstack")) + mToken = TK_DYNSTACK; + else if (!strcmp(tkident, "__preserves")) + mToken = TK_PRESERVES; + else if (!strcmp(tkident, "__memmap")) + mToken = TK_MEMMAP; + else if ((mCompilerOptions & COPT_CPLUSPLUS) && !strcmp(tkident, "namespace")) + mToken = TK_NAMESPACE; + else if ((mCompilerOptions & COPT_CPLUSPLUS) && !strcmp(tkident, "using")) + mToken = TK_USING; + else if ((mCompilerOptions & COPT_CPLUSPLUS) && !strcmp(tkident, "this")) + mToken = TK_THIS; + else if ((mCompilerOptions & COPT_CPLUSPLUS) && !strcmp(tkident, "class")) + mToken = TK_CLASS; + else if ((mCompilerOptions & COPT_CPLUSPLUS) && !strcmp(tkident, "public")) + mToken = TK_PUBLIC; + else if ((mCompilerOptions & COPT_CPLUSPLUS) && !strcmp(tkident, "protected")) + mToken = TK_PROTECTED; + else if ((mCompilerOptions & COPT_CPLUSPLUS) && !strcmp(tkident, "private")) + mToken = TK_PRIVATE; + else if ((mCompilerOptions & COPT_CPLUSPLUS) && !strcmp(tkident, "new")) + mToken = TK_NEW; + else if ((mCompilerOptions & COPT_CPLUSPLUS) && !strcmp(tkident, "delete")) + mToken = TK_DELETE; + else if ((mCompilerOptions & COPT_CPLUSPLUS) && !strcmp(tkident, "virtual")) + mToken = TK_VIRTUAL; + else if ((mCompilerOptions & COPT_CPLUSPLUS) && !strcmp(tkident, "template")) + mToken = TK_TEMPLATE; + else if ((mCompilerOptions & COPT_CPLUSPLUS) && !strcmp(tkident, "friend")) + mToken = TK_FRIEND; + else if ((mCompilerOptions & COPT_CPLUSPLUS) && !strcmp(tkident, "constexpr")) + mToken = TK_CONSTEXPR; + else if ((mCompilerOptions & COPT_CPLUSPLUS) && !strcmp(tkident, "typename")) + mToken = TK_TYPENAME; + else if ((mCompilerOptions & COPT_CPLUSPLUS) && !strcmp(tkident, "decltype")) + mToken = TK_DECLTYPE; + else if ((mCompilerOptions & COPT_CPLUSPLUS) && !strcmp(tkident, "operator")) + { + NextRawToken(); + switch (mToken) + { + case TK_ASSIGN: + mTokenIdent = Ident::Unique("operator="); + break; + + case TK_ASSIGN_ADD: + mTokenIdent = Ident::Unique("operator+="); + break; + case TK_ASSIGN_SUB: + mTokenIdent = Ident::Unique("operator-="); + break; + case TK_ASSIGN_MUL: + mTokenIdent = Ident::Unique("operator*="); + break; + case TK_ASSIGN_DIV: + mTokenIdent = Ident::Unique("operator/="); + break; + case TK_ASSIGN_MOD: + mTokenIdent = Ident::Unique("operator%="); + break; + case TK_ASSIGN_SHL: + mTokenIdent = Ident::Unique("operator<<="); + break; + case TK_ASSIGN_SHR: + mTokenIdent = Ident::Unique("operator>>="); + break; + case TK_ASSIGN_AND: + mTokenIdent = Ident::Unique("operator&="); + break; + case TK_ASSIGN_XOR: + mTokenIdent = Ident::Unique("operator^="); + break; + case TK_ASSIGN_OR: + mTokenIdent = Ident::Unique("operator|="); + break; + + case TK_ADD: + mTokenIdent = Ident::Unique("operator+"); + break; + case TK_SUB: + mTokenIdent = Ident::Unique("operator-"); + break; + case TK_MUL: + mTokenIdent = Ident::Unique("operator*"); + break; + case TK_DIV: + mTokenIdent = Ident::Unique("operator/"); + break; + case TK_MOD: + mTokenIdent = Ident::Unique("operator%"); + break; + + case TK_BINARY_AND: + mTokenIdent = Ident::Unique("operator&"); + break; + case TK_BINARY_OR: + mTokenIdent = Ident::Unique("operator|"); + break; + case TK_BINARY_XOR: + mTokenIdent = Ident::Unique("operator^"); + break; + case TK_LOGICAL_NOT: + mTokenIdent = Ident::Unique("operator!"); + break; + + case TK_LEFT_SHIFT: + mTokenIdent = Ident::Unique("operator<<"); + break; + case TK_RIGHT_SHIFT: + mTokenIdent = Ident::Unique("operator>>"); + break; + + case TK_EQUAL: + mTokenIdent = Ident::Unique("operator=="); + break; + case TK_NOT_EQUAL: + mTokenIdent = Ident::Unique("operator!="); + break; + case TK_GREATER_THAN: + mTokenIdent = Ident::Unique("operator>"); + break; + case TK_GREATER_EQUAL: + mTokenIdent = Ident::Unique("operator>="); + break; + case TK_LESS_THAN: + mTokenIdent = Ident::Unique("operator<"); + break; + case TK_LESS_EQUAL: + mTokenIdent = Ident::Unique("operator<="); + break; + + case TK_INC: + mTokenIdent = Ident::Unique("operator++"); + break; + case TK_DEC: + mTokenIdent = Ident::Unique("operator--"); + break; + + case TK_OPEN_BRACKET: + NextRawToken(); + if (mToken != TK_CLOSE_BRACKET) + mErrors->Error(mLocation, EERR_INVALID_OPERATOR, "']' expected"); + mTokenIdent = Ident::Unique("operator[]"); + break; + + case TK_OPEN_PARENTHESIS: + NextRawToken(); + if (mToken != TK_CLOSE_PARENTHESIS) + mErrors->Error(mLocation, EERR_INVALID_OPERATOR, "')' expected"); + mTokenIdent = Ident::Unique("operator()"); + break; + + case TK_ARROW: + mTokenIdent = Ident::Unique("operator->"); + break; + + case TK_NEW: + mTokenIdent = Ident::Unique("operator-new"); + break; + case TK_DELETE: + mTokenIdent = Ident::Unique("operator-delete"); + break; + + default: + // dirty little hack to implement token preview, got to fix + // this with an infinite preview sequence at one point + mUngetToken = mToken; + mToken = TK_OPERATOR; + return; + } + + mToken = TK_IDENT; + } + else + { + mToken = TK_IDENT; + mTokenIdent = Ident::Unique(tkident); + } + } + else + { + NextChar(); + } + break; + } + } + else + { + mToken = TK_EOF; + } + +} + +void Scanner::Warning(const char* error) +{ + mErrors->Error(mLocation, EWARN_SYNTAX, error); +} + +void Scanner::Error(const char* error) +{ + mErrors->Error(mLocation, EERR_SYNTAX, error); +} +static uint8 p2smap[] = { 0x00, 0x20, 0x00, 0x40, 0x00, 0x60, 0x40, 0x60 }; + +static inline uint8 p2s(uint8 ch) +{ + return (ch & 0x1f) | p2smap[ch >> 5]; +} + +static inline uint8 transchar(char mode, uint8 ch) +{ + switch (mode) + { + default: + case 'a': + return ch; + case 'p': + if (ch >= 'a' && ch <= 'z') + return ch ^ 0x20; + else if (ch >= 'A' && ch <= 'Z') + return ch ^ 0x80; + else + return ch; + break; + case 'P': + if (ch >= 'A' && ch <= 'Z' || ch >= 'a' && ch <= 'z') + return (ch ^ 0x20) & 0xdf; + else + return ch; + break; + case 's': + if (ch >= 'A' && ch <= 'Z' || ch >= 'a' && ch <= 'z') + return p2s(ch ^ 0x20); + else + return p2s(ch); + break; + case 'S': + if (ch >= 'A' && ch <= 'Z' || ch >= 'a' && ch <= 'z') + return p2s((ch ^ 0x20) & 0xdf); + else + return p2s(ch); + break; + } +} + +void Scanner::StringToken(char terminator, char mode) +{ + switch (mode) + { + case 'a': + case 'p': + case 'P': + case 's': + case 'S': + break; + default: + Error("Invalid string literal mode"); + } + + int n = 0; + + while (mLine[mOffset] && mLine[mOffset] != terminator && mLine[mOffset] != '\n') + { + char ch = mLine[mOffset++]; + + if (ch == '\\' && mLine[mOffset]) + { + ch = mLine[mOffset++]; + switch (ch) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + { + mTokenChar = ch - '0'; + if (mLine[mOffset] >= '0' && mLine[mOffset] <= '7') + { + mTokenChar = mTokenChar * 8 + mLine[mOffset ++ ] - '0'; + if (mLine[mOffset] >= '0' && mLine[mOffset] <= '7') + { + mTokenChar = mTokenChar * 8 + mLine[mOffset++] - '0'; + } + } + break; + } + case 'n': + mTokenChar = '\n'; + break; + case 'r': + mTokenChar = '\r'; + break; + case 't': + mTokenChar = '\t'; + break; + case 'f': + mTokenChar = '\f'; + break; + case 'b': + mTokenChar = '\b'; + break; + case 'v': + mTokenChar = '\v'; + break; + case 'x': + { + char c0 = mLine[mOffset++]; + char c1 = mLine[mOffset++]; + + if (IsHex(c0) && IsHex(c1)) + mTokenChar = transchar(mode, 16 * HexValue(c0) + HexValue(c1)); + else + mErrors->Error(mLocation, EERR_SYNTAX, "Invalid hex escape code"); + } + break; + case 'y': + { + char c0 = mLine[mOffset++]; + char c1 = mLine[mOffset++]; + + if (IsHex(c0) && IsHex(c1)) + mTokenChar = 16 * HexValue(c0) + HexValue(c1); + else + mErrors->Error(mLocation, EERR_SYNTAX, "Invalid hex escape code"); + } + break; + default: + mTokenChar = transchar(mode, ch); + break; + } + } + else + mTokenChar = transchar(mode, ch); + + mTokenString[n++] = mTokenChar; + } + mTokenStringSize = n; + mTokenString[n] = 0; + + if (mLine[mOffset] && mLine[mOffset] == terminator) + { + mToken = TK_STRING; + mOffset++; + NextChar(); + } + else + { + NextChar(); + Error("String constant not terminated"); + mToken = TK_ERROR; + } + +} + +void Scanner::CharToken(char mode) +{ + switch (mode) + { + case 'a': + case 'p': + case 'P': + case 's': + case 'S': + break; + default: + Error("Invalid string literal mode"); + } + + int n = 0; + + uint8 ch = mLine[mOffset++]; + + if (ch == '\\' && mLine[mOffset]) + { + ch = mLine[mOffset++]; + switch (ch) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + { + mTokenChar = ch - '0'; + if (mLine[mOffset] >= '0' && mLine[mOffset] <= '7') + { + mTokenChar = mTokenChar * 8 + mLine[mOffset++] - '0'; + if (mLine[mOffset] >= '0' && mLine[mOffset] <= '7') + { + mTokenChar = mTokenChar * 8 + mLine[mOffset++] - '0'; + } + } + break; + } + case 'n': + mTokenChar = '\n'; + break; + case 'r': + mTokenChar = '\r'; + break; + case 't': + mTokenChar = '\t'; + break; + case 'f': + mTokenChar = '\f'; + break; + case 'b': + mTokenChar = '\b'; + break; + case 'v': + mTokenChar = '\v'; + break; + case 'x': + { + char c0 = mLine[mOffset++]; + char c1 = mLine[mOffset++]; + + if (IsHex(c0) && IsHex(c1)) + mTokenChar = transchar(mode, 16 * HexValue(c0) + HexValue(c1)); + else + mErrors->Error(mLocation, EERR_SYNTAX, "Invalid hex escape code"); + } + break; + case 'y': + { + char c0 = mLine[mOffset++]; + char c1 = mLine[mOffset++]; + + if (IsHex(c0) && IsHex(c1)) + mTokenChar = 16 * HexValue(c0) + HexValue(c1); + else + mErrors->Error(mLocation, EERR_SYNTAX, "Invalid hex escape code"); + } + break; + default: + mTokenChar = transchar(mode, ch); + } + } + else + mTokenChar = transchar(mode, ch); + + mTokenInteger = mTokenChar; + assert(mTokenInteger >= 0); + + if (mLine[mOffset] && mLine[mOffset] == '\'') + { + mToken = TK_CHARACTER; + mOffset++; + NextChar(); + } + else + { + NextChar(); + Error("Character constant not terminated"); + mToken = TK_ERROR; + } +} + +bool Scanner::NextChar(void) +{ + while (!(mLine[mOffset])) + { + if (mMacroExpansion) + { + if (mMacroExpansion->mLoopIndex) + { + mMacroExpansion->mLoopCount++; + if (mMacroExpansion->mLoopCount < mMacroExpansion->mLoopLimit) + { + char buffer[20]; + sprintf_s(buffer, "%d", int(mMacroExpansion->mLoopCount)); + mMacroExpansion->mLoopIndex->SetString(buffer); + mTokenChar = mMacroExpansion->mChar; + mOffset = 0; + + return true; + } + else + mMacroExpansion->mChar = ' '; + } + + MacroExpansion* mac = mMacroExpansion->mLink; +// delete mDefineArguments; + + mLine = mMacroExpansion->mLine; + mOffset = mMacroExpansion->mOffset; + mTokenChar = mMacroExpansion->mChar; + mDefineArguments = mMacroExpansion->mDefinedArguments; + + delete mMacroExpansion; + mMacroExpansion = mac; + mMacroExpansionDepth--; + + return true; + } + else if (mPreprocessor->NextLine()) + { + mOffset = 0; + mStartOfLine = true; + } + else + { + mTokenChar = 0; + return false; + } + } + + mTokenChar = mLine[mOffset++]; + return true; +} + +void Scanner::ParseNumberToken(void) +{ + uint64 mant = (int)mTokenChar - (int)'0'; + + NextChar(); + + if (mant == 0 && (mTokenChar == 'x' || mTokenChar == 'X')) + { + int n = 0; + while (NextChar()) + { + if (mTokenChar >= '0' && mTokenChar <= '9') + mant = mant * 16 + (int)mTokenChar - (int)'0'; + else if (mTokenChar >= 'a' && mTokenChar <= 'f') + mant = mant * 16 + 10 + (int)mTokenChar - (int)'a'; + else if (mTokenChar >= 'A' && mTokenChar <= 'F') + mant = mant * 16 + 10 + (int)mTokenChar - (int)'A'; + else + break; + n++; + } + + if (n == 0) + Error("Missing digits in hex constant"); + + if (mTokenChar == 'U' || mTokenChar == 'u') + { + NextChar(); + if (mTokenChar == 'L' || mTokenChar == 'l') + { + NextChar(); + mToken = TK_INTEGERUL; + } + else if (mant < 0x10000) + mToken = TK_INTEGERU; + else + mToken = TK_INTEGERUL; + } + else + { + if (mTokenChar == 'L' || mTokenChar == 'l') + { + NextChar(); + if (mTokenChar == 'U' || mTokenChar == 'u') + { + NextChar(); + mToken = TK_INTEGERUL; + } + else if (mant <= 0x7fffffff) + mToken = TK_INTEGERL; + else + mToken = TK_INTEGERUL; + } + else if (mant < 0x8000) + mToken = TK_INTEGER; + else if (mant < 0x10000) + mToken = TK_INTEGERU; + else if (mant < 0x80000000) + mToken = TK_INTEGERL; + else + mToken = TK_INTEGERUL; + } + + mTokenInteger = mant; + } + else if (mant == 0 && (mTokenChar == 'b' || mTokenChar == 'B')) + { + int n = 0; + while (NextChar()) + { + if (mTokenChar >= '0' && mTokenChar <= '1') + mant = mant * 2 + (int)mTokenChar - (int)'0'; + else + break; + n++; + } + + if (n == 0) + Error("Missing digits in binary constant"); + + if (mTokenChar == 'U' || mTokenChar == 'u') + { + NextChar(); + if (mTokenChar == 'L' || mTokenChar == 'l') + { + NextChar(); + mToken = TK_INTEGERUL; + } + else if (mant < 0x10000) + mToken = TK_INTEGERU; + else + mToken = TK_INTEGERUL; + } + else + { + if (mTokenChar == 'L' || mTokenChar == 'l') + { + NextChar(); + if (mTokenChar == 'U' || mTokenChar == 'u') + { + NextChar(); + mToken = TK_INTEGERUL; + } + else + mToken = TK_INTEGERL; + } + else if (mant < 0x8000) + mToken = TK_INTEGER; + else if (mant < 0x10000) + mToken = TK_INTEGERU; + else if (mant < 0x80000000) + mToken = TK_INTEGERL; + else + mToken = TK_INTEGERUL; + } + mTokenInteger = mant; + } + else + { + int64 moctal = 0; + bool zerostart = mant == 0; + + int n = 0; + if (mTokenChar >= '0' && mTokenChar <= '9') + { + mant = mant * 10 + (int)mTokenChar - (int)'0'; + moctal = moctal * 8 + (int)mTokenChar - (int)'0'; + while (NextChar()) + { + if (mTokenChar >= '0' && mTokenChar <= '9') + { + mant = mant * 10 + (int)mTokenChar - (int)'0'; + moctal = moctal * 8 + (int)mTokenChar - (int)'0'; + } + else + break; + n++; + } + } + + if (mTokenChar != '.') + { + if (zerostart) + mant = moctal; + + if (mTokenChar == 'U' || mTokenChar == 'u') + { + NextChar(); + if (mTokenChar == 'L' || mTokenChar == 'l') + { + NextChar(); + mToken = TK_INTEGERUL; + } + else if (mant < 0x10000) + mToken = TK_INTEGERU; + else + mToken = TK_INTEGERUL; + } + else + { + if (mTokenChar == 'L' || mTokenChar == 'l') + { + NextChar(); + if (mTokenChar == 'U' || mTokenChar == 'u') + { + NextChar(); + mToken = TK_INTEGERUL; + } + else + mToken = TK_INTEGERL; + } + else if (mant < 0x8000) + mToken = TK_INTEGER; + else + mToken = TK_INTEGERL; + } + mTokenInteger = mant; + } + else + { + double facc = double(mant), fract = 1.0; + + NextChar(); + + while (mTokenChar >= '0' && mTokenChar <= '9') + { + facc *= 10; + fract *= 10; + facc += (int)mTokenChar - (int)'0'; + NextChar(); + } + + facc /= fract; + + if (mTokenChar == 'e' || mTokenChar == 'E') + { + NextChar(); + bool sign = false; + if (mTokenChar == '+') + NextChar(); + else if (mTokenChar == '-') + { + NextChar(); + sign = true; + } + + int expval = 0; + + while (mTokenChar >= '0' && mTokenChar <= '9') + { + expval = expval * 10 + (int)mTokenChar - (int)'0'; + NextChar(); + } + + if (expval > 600) + expval = 600; + + double fexp = pow(10.0, expval); + + if (sign) + facc /= fexp; + else + facc *= fexp; + } + + mTokenNumber = facc; + mToken = TK_NUMBER; + + if (mTokenChar == 'f' || mTokenChar == 'F') + NextChar(); + } + } +} + + +int64 Scanner::PrepParseSimple(bool skip) +{ + int64 v = 0; + + switch (mToken) + { + case TK_CHARACTER: + case TK_INTEGER: + case TK_INTEGERU: + case TK_INTEGERL: + case TK_INTEGERUL: + v = mTokenInteger; + NextPreToken(); + break; + case TK_SUB: + NextPreToken(); + v = -PrepParseSimple(skip); + break; + case TK_LOGICAL_NOT: + NextPreToken(); + v = !PrepParseSimple(skip); + break; + case TK_BINARY_NOT: + NextPreToken(); + v = ~PrepParseSimple(skip); + break; + case TK_OPEN_PARENTHESIS: + NextPreToken(); + v = PrepParseConditional(skip); + if (mToken == TK_CLOSE_PARENTHESIS) + NextPreToken(); + else + mErrors->Error(mLocation, ERRR_PREPROCESSOR, "')' expected"); + break; + case TK_IDENT: + if (strcmp(mTokenIdent->mString, "defined") == 0) + { + bool parenthesis = false; + + NextRawToken(); + if (mToken == TK_OPEN_PARENTHESIS) + { + NextRawToken(); + parenthesis = true; + } + + if (mToken == TK_IDENT) + { + Macro* def = nullptr; + if (mDefineArguments) + def = mDefineArguments->Lookup(mTokenIdent); + if (!def) + def = mDefines->Lookup(mTokenIdent); + if (def) + v = 1; + else + v = 0; + NextPreToken(); + } + else + mErrors->Error(mLocation, ERRR_PREPROCESSOR, "Identifier expected"); + + if (parenthesis) + { + if (mToken == TK_CLOSE_PARENTHESIS) + NextPreToken(); + else + mErrors->Error(mLocation, ERRR_PREPROCESSOR, "')' expected"); + } + } + else if (skip) + { + NextPreToken(); + v = 0; + } + else + { + mErrors->Error(mLocation, EWARN_EXPAND_UNDEFINED_MACRO_IDENT, "Expand undefined identifier in condition to 0", mTokenIdent); + NextPreToken(); + v = 0; + } + break; + default: + mErrors->Error(mLocation, ERRR_PREPROCESSOR, "Invalid preprocessor token", TokenName(mToken)); + if (mToken != TK_EOL) + NextPreToken(); + } + + return v; +} + +int64 Scanner::PrepParseMul(bool skip) +{ + int64 v = PrepParseSimple(skip); + int64 u; + for (;;) + { + switch (mToken) + { + case TK_MUL: + NextPreToken(); + v *= PrepParseSimple(skip); + break; + case TK_DIV: + NextPreToken(); + u = PrepParseSimple(skip); + if (skip) + ; + else if (u == 0) + mErrors->Error(mLocation, ERRR_PREPROCESSOR, "Division by zero"); + else + v /= u; + break; + case TK_MOD: + u = PrepParseSimple(skip); + if (skip) + ; + else if (u == 0) + mErrors->Error(mLocation, ERRR_PREPROCESSOR, "Division by zero"); + else + v %= u; + break; + default: + return v; + } + } +} + +int64 Scanner::PrepParseAdd(bool skip) +{ + int64 v = PrepParseMul(skip); + for (;;) + { + switch (mToken) + { + case TK_ADD: + NextPreToken(); + v += PrepParseMul(skip); + break; + case TK_SUB: + NextPreToken(); + v -= PrepParseMul(skip); + break; + default: + return v; + } + } +} + +int64 Scanner::PrepParseShift(bool skip) +{ + int64 v = PrepParseAdd(skip); + for (;;) + { + switch (mToken) + { + case TK_LEFT_SHIFT: + NextPreToken(); + v <<= PrepParseAdd(skip); + break; + case TK_RIGHT_SHIFT: + NextPreToken(); + v >>= PrepParseAdd(skip); + break; + default: + return v; + } + } +} + +int64 Scanner::PrepParseRel(bool skip) +{ + int64 v = PrepParseShift(skip); + for (;;) + { + switch (mToken) + { + case TK_LESS_THAN: + NextPreToken(); + v = v < PrepParseShift(skip); + break; + case TK_GREATER_THAN: + NextPreToken(); + v = v > PrepParseShift(skip); + break; + case TK_LESS_EQUAL: + NextPreToken(); + v = v <= PrepParseShift(skip); + break; + case TK_GREATER_EQUAL: + NextPreToken(); + v = v >= PrepParseShift(skip); + break; + case TK_EQUAL: + NextPreToken(); + v = v == PrepParseShift(skip); + break; + case TK_NOT_EQUAL: + NextPreToken(); + v = v != PrepParseShift(skip); + break; + default: + return v; + } + } + +} + +int64 Scanner::PrepParseBinaryAnd(bool skip) +{ + int64 v = PrepParseRel(skip); + while (mToken == TK_BINARY_AND) + { + NextPreToken(); + v &= PrepParseRel(skip); + } + return v; +} + +int64 Scanner::PrepParseBinaryXor(bool skip) +{ + int64 v = PrepParseBinaryAnd(skip); + while (mToken == TK_BINARY_XOR) + { + NextPreToken(); + v ^= PrepParseBinaryAnd(skip); + } + return v; +} + +int64 Scanner::PrepParseBinaryOr(bool skip) +{ + int64 v = PrepParseBinaryXor(skip); + while (mToken == TK_BINARY_OR) + { + NextPreToken(); + v |= PrepParseBinaryXor(skip); + } + return v; +} + +int64 Scanner::PrepParseLogicalAnd(bool skip) +{ + int64 v = PrepParseBinaryOr(skip); + while (mToken == TK_LOGICAL_AND) + { + NextPreToken(); + if (!PrepParseBinaryOr(skip || !v)) + v = 0; + } + return v; +} + +int64 Scanner::PrepParseLogicalOr(bool skip) +{ + int64 v = PrepParseLogicalAnd(skip); + while (mToken == TK_LOGICAL_OR) + { + NextPreToken(); + if (PrepParseLogicalAnd(skip || v)) + v = 1; + } + return v; +} + +int64 Scanner::PrepParseConditional(bool skip) +{ + int64 v = PrepParseLogicalOr(skip); + if (mToken == TK_QUESTIONMARK) + { + NextPreToken(); + int64 vt = PrepParseConditional(skip || v); + if (mToken == TK_COLON) + NextPreToken(); + else + mErrors->Error(mLocation, ERRR_PREPROCESSOR, "':' expected"); + int64 vf = PrepParseConditional(skip || !v); + if (v) + v = vt; + else + v = vf; + } + + return v; +} diff --git a/oscar64/oscar64/Scanner.h b/oscar64/oscar64/Scanner.h new file mode 100644 index 0000000..cd56d89 --- /dev/null +++ b/oscar64/oscar64/Scanner.h @@ -0,0 +1,345 @@ +#pragma once + +#include "Ident.h" +#include "Errors.h" +#include "Preprocessor.h" + +enum Token : uint8 +{ + TK_NONE, + TK_EOF, + TK_ERROR, + TK_EOL, + + TK_IF, + TK_ELSE, + TK_WHILE, + TK_DO, + TK_FOR, + TK_VOID, + TK_INT, + TK_CHAR, + TK_FLOAT, + TK_UNSIGNED, + TK_SIGNED, + TK_SWITCH, + TK_CASE, + TK_DEFAULT, + TK_BREAK, + TK_RETURN, + TK_GOTO, + TK_SHORT, + TK_LONG, + TK_CONTINUE, + TK_INTEGER, + TK_INTEGERU, + TK_INTEGERL, + TK_INTEGERUL, + TK_BOOL, + TK_CONST, + TK_VOLATILE, + TK_TYPEDEF, + TK_STRUCT, + TK_UNION, + TK_ENUM, + TK_SIZEOF, + TK_BANKOF, + TK_STATIC, + TK_AUTO, + TK_EXTERN, + TK_INLINE, + TK_ASSUME, + TK_STATIC_ASSERT, + + TK_ASM, + TK_INTERRUPT, + TK_HWINTERRUPT, + TK_NATIVE, + TK_FASTCALL, + TK_EXPORT, + TK_ZEROPAGE, + TK_NOINLINE, + TK_FORCEINLINE, + TK_STRIPED, + TK_DYNSTACK, + TK_PRESERVES, + TK_MEMMAP, + + TK_NUMBER, + TK_CHARACTER, + TK_STRING, + TK_IDENT, + TK_TRUE, + TK_FALSE, + TK_NULL, + + TK_ADD, + TK_SUB, + TK_MUL, + TK_DIV, + TK_MOD, + + TK_LEFT_SHIFT, + TK_RIGHT_SHIFT, + + TK_INC, + TK_DEC, + + TK_LOGICAL_NOT, + TK_LOGICAL_AND, + TK_LOGICAL_OR, + + TK_BINARY_NOT, + TK_BINARY_AND, + TK_BINARY_OR, + TK_BINARY_XOR, + + TK_EQUAL, + TK_NOT_EQUAL, + TK_GREATER_THAN, + TK_GREATER_EQUAL, + TK_LESS_THAN, + TK_LESS_EQUAL, + + TK_ASSIGN, + TK_ASSIGN_ADD, + TK_ASSIGN_SUB, + TK_ASSIGN_MUL, + TK_ASSIGN_DIV, + TK_ASSIGN_MOD, + TK_ASSIGN_SHL, + TK_ASSIGN_SHR, + TK_ASSIGN_AND, + TK_ASSIGN_XOR, + TK_ASSIGN_OR, + + TK_ARROW, + TK_HASH, + TK_DOLLAR, + + TK_OPEN_PARENTHESIS, + TK_CLOSE_PARENTHESIS, + + TK_OPEN_BRACE, + TK_CLOSE_BRACE, + + TK_OPEN_BRACKET, + TK_CLOSE_BRACKET, + + TK_DOT, + TK_DOTDOT, + TK_ELLIPSIS, + TK_COMMA, + TK_SEMICOLON, + TK_COLON, + TK_QUESTIONMARK, + + TK_EMBEDDED, + + TK_PREP_DEFINE, + TK_PREP_UNDEF, + TK_PREP_INCLUDE, + TK_PREP_IF, + TK_PREP_ELIF, + TK_PREP_ELSE, + TK_PREP_ENDIF, + TK_PREP_IFDEF, + TK_PREP_IFNDEF, + TK_PREP_ERROR, + TK_PREP_WARN, + TK_PREP_PRAGMA, + TK_PREP_LINE, + + TK_PREP_ASSIGN, + TK_PREP_REPEAT, + TK_PREP_UNTIL, + TK_PREP_EMBED, + TK_PREP_FOR, + + TK_PREP_CONCAT, + TK_PREP_IDENT, + + TK_NAMESPACE, + TK_USING, + TK_THIS, + TK_COLCOLON, + TK_CLASS, + TK_PUBLIC, + TK_PROTECTED, + TK_PRIVATE, + TK_NEW, + TK_DELETE, + TK_VIRTUAL, + TK_OPERATOR, + TK_TEMPLATE, + TK_FRIEND, + TK_CONSTEXPR, + TK_TYPENAME, + TK_DECLTYPE, + + NUM_TOKENS +}; + +extern const char* TokenNames[]; + +class MacroDict; + +class Macro +{ +public: + Macro(const Ident* ident, MacroDict* scope); + ~Macro(void); + + void SetString(const char* str); + void SetString(const char* str, ptrdiff_t length); + void AddArgument(const Ident * ident); + + const Ident * mIdent; + const char * mString; + char * mBuffer; + int mSize; + int mNumArguments; + const Ident * mArguments[32]; + MacroDict * mScope; + bool mVariadic, mAssign; +}; + +typedef Macro* MacroPtr; + +class MacroDict +{ +public: + MacroDict(void); + ~MacroDict(void); + + void Insert(Macro * macro); + void Remove(const Ident* ident); + Macro* Lookup(const Ident* ident); + +protected: + MacroPtr * mHash; + int mHashSize, mHashFill; + MacroDict * mParent; +}; + +class Scanner; + +struct TokenSequence +{ + TokenSequence * mNext; + Location mLocation; + Token mToken; + + const Ident * mTokenIdent; + const uint8 * mTokenString; + double mTokenNumber; + int64 mTokenInteger; + + TokenSequence(Scanner* scanner); + ~TokenSequence(void); +}; + +class Scanner +{ +public: + Scanner(Errors * errors, Preprocessor * preprocessor); + ~Scanner(void); + + const char* TokenName(Token token) const; + + void NextToken(void); + void UngetToken(Token token); + void UngetToken(Token token, const Ident * ident); + + void BeginRecord(void); + TokenSequence* CompleteRecord(void); + + const TokenSequence* Replay(const TokenSequence * sequence); + + void Warning(const char * error); + void Error(const char * error); + + Errors* mErrors; + Preprocessor * mPreprocessor; + + int mPrepCondFalse, mPrepCondDepth; + bool mPrepCondExit; + + int mOffset; + const char * mLine; + bool mStartOfLine; + + const Ident * mTokenIdent; + uint8 mTokenString[1024], mTokenChar; + int mTokenStringSize; + + uint8 * mTokenEmbed; + int mTokenEmbedSize; + + Token mUndoToken; + Token mToken; + double mTokenNumber; + int64 mTokenInteger; + + Location mLocation; + + void SetAssemblerMode(bool mode); + + bool mAssemblerMode; + bool mPreprocessorMode; + + uint64 mCompilerOptions; + + bool IsIntegerToken(void) const; + + void AddMacro(const Ident* ident, const char* value); + void MarkSourceOnce(void); +protected: + void NextSkipRawToken(void); + void NextRawToken(void); + void NextPreToken(void); + + struct MacroExpansion + { + MacroExpansion * mLink; + const char * mLine; + int mOffset; + char mChar; + Macro * mLoopIndex; + int64 mLoopCount, mLoopLimit; + MacroDict* mDefinedArguments; + + MacroExpansion(void) + : mLink(nullptr), mLine(nullptr), mOffset(0), mChar(0), mLoopIndex(nullptr), mLoopCount(0), mLoopLimit(0), mDefinedArguments(nullptr) + {} + + } * mMacroExpansion; + + int mMacroExpansionDepth; + + MacroDict* mDefines, * mDefineArguments, * mOnceDict; + Macro* mFileMacro, * mLineMacro; + + Token mUngetToken; + + const TokenSequence* mReplay; + TokenSequence* mRecord, * mRecordLast, * mRecordPrev; + + void StringToken(char terminator, char mode); + void CharToken(char mode); + bool NextChar(void); + void ParseNumberToken(void); + + int64 PrepParseSimple(bool skip); + int64 PrepParseMul(bool skip); + int64 PrepParseAdd(bool skip); + int64 PrepParseShift(bool skip); + int64 PrepParseRel(bool skip); + int64 PrepParseBinaryAnd(bool skip); + int64 PrepParseBinaryXor(bool skip); + int64 PrepParseBinaryOr(bool skip); + int64 PrepParseLogicalAnd(bool skip); + int64 PrepParseLogicalOr(bool skip); + int64 PrepParseConditional(bool skip = false); + +}; diff --git a/oscar64/oscar64/oscar64.cpp b/oscar64/oscar64/oscar64.cpp new file mode 100644 index 0000000..7aebb7d --- /dev/null +++ b/oscar64/oscar64/oscar64.cpp @@ -0,0 +1,977 @@ +#include +#ifdef _WIN32 +#include +#else +#include +#endif +#ifdef __APPLE__ +#include +#endif +#ifdef __FreeBSD__ + #include + #include +#endif +#include "Compiler.h" +#include "DiskImage.h" +#include + +#ifdef _WIN64 +#include +#include +#endif + +#ifdef _WIN32 +bool GetProductAndVersion(char* strProductName, char* strProductVersion) +{ + // get the filename of the executable containing the version resource + TCHAR szFilename[MAX_PATH + 1] = { 0 }; + if (GetModuleFileName(NULL, szFilename, MAX_PATH) == 0) + { + return false; + } + + // allocate a block of memory for the version info + DWORD dummy; + DWORD dwSize = GetFileVersionInfoSize(szFilename, &dummy); + if (dwSize == 0) + { + return false; + } + + BYTE* data = new BYTE[dwSize]; + + // load the version info + if (!GetFileVersionInfo(szFilename, NULL, dwSize, data)) + { + return false; + } + + // get the name and version strings + LPVOID pvProductName = NULL; + unsigned int iProductNameLen = 0; + LPVOID pvProductVersion = NULL; + unsigned int iProductVersionLen = 0; + + // replace "040904e4" with the language ID of your resources + if (!VerQueryValueA(&data[0], "\\StringFileInfo\\000904b0\\ProductName", &pvProductName, &iProductNameLen) || + !VerQueryValueA(&data[0], "\\StringFileInfo\\000904b0\\ProductVersion", &pvProductVersion, &iProductVersionLen)) + { + return false; + } + + + strcpy_s(strProductName, 100, (LPCSTR)pvProductName); + strcpy_s(strProductVersion, 100, (LPCSTR)pvProductVersion); + + return true; +} +#endif + +void writeHelp(void) +{ + printf("-v : verbose output for diagnostics\n"); + printf("-v2 : more verbose output\n"); + printf("-h : print help\n"); + printf("-i : additional include paths\n"); + printf("-ii : set default include path\n"); + printf("-o : optional output file name\n"); + printf("-rt : alternative runtime library, replaces the crt.c(or empty for none)\n"); + printf("-e : execute the result in the integrated emulator\n"); + printf("-ep : execute and profile the result in the integrated emulator\n"); + printf("-bc : create byte code for all functions\n"); + printf("-n : create pure native code for all functions(now default)\n"); + printf("-d : define a symbol(e.g.NOFLOAT or NOLONG to avoid float / long code in printf)\n"); + printf("-D : define a symbol in a gcc compliant way(e.g. - D NAME = VALUE)\n"); + printf("-O1 or -O : default optimizations\n"); + printf("-O0 : disable optimizations\n"); + printf("-O2 : more aggressive speed optimizations including auto inline of small functions\n"); + printf("-O3 : aggressive optimization for speed\n"); + printf("-Os : optimize for size\n"); + printf("-Oi : enable auto inline of small functions(part of O2 / O3)\n"); + printf("-Oa : optimize inline assembler(part of O2 / O3)\n"); + printf("-Oa : optimize inline assembler(part of O2 / O3)\n"); + printf("-Oz : enable auto placement of global variables in zero page(part of O3)\n"); + printf("-Op : optimize constant parameters\n"); + printf("-Oo : optimize size using \"outliner\" (extract repeated code sequences into functions)\n"); + printf("-Ox : optimize pointer arithmetic by blocking shorter arrays to not cross page boundaries\n"); + printf("-g : create source level debug info and add source line numbers to asm listing\n"); + printf("-gp : create source level debug info and add source line numbers to asm listing and static profile data\n"); + printf("-tf : target format, may be prg, crt or bin\n"); + printf("-tm : target machine\n"); + printf("-d64 : create a d64 disk image\n"); + printf("-f : add a binary file to the disk image\n"); + printf("-fz : add a compressed binary file to the disk image\n"); + printf("-fi : sector skip for data files on disk image\n"); + printf("-xz : extended zero page usage, more zero page space, but no return to basic\n"); + printf("-cid : cartridge type ID, used by vice emulator\n"); + printf("-csub : cartridge sub type\n"); + printf("-cname : cartridge name\n"); + printf("-pp : compile in C++ mode\n"); + printf("-strict : use strict ANSI C parsing(no C++ goodies)\n"); + printf("-psci : use PETSCII encoding for all strings without prefix\n"); + printf("-rmp : generate error files: .error.map and .error.asm when linker fails\n"); +} + +int main2(int argc, const char** argv) +{ + InitDeclarations(); + InitAssembler(); + + if (argc > 1) + { + char basePath[200], crtPath[200], includePath[200], targetPath[200], diskPath[200]; + char strProductName[100], strProductVersion[200]; + int dataFileInterleave = 10; + +#ifdef _WIN32 + GetProductAndVersion(strProductName, strProductVersion); + + DWORD length = ::GetModuleFileNameA(NULL, basePath, sizeof(basePath)); + +#else + strcpy(strProductName, "oscar64"); + strcpy(strProductVersion, "1.32.272"); + +#ifdef __APPLE__ + uint32_t length = sizeof(basePath); + + _NSGetExecutablePath(basePath, &length); + length = strlen(basePath); +#else +#ifdef __FreeBSD__ + size_t length = 200; + int oid[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1}; + if (sysctl((const int *)&oid[0], 4, basePath, &length, NULL, 0) < 0) { + length = 0; + } +#else + int length = readlink("/proc/self/exe", basePath, sizeof(basePath)); + + // strcpy(basePath, argv[0]); + // int length = strlen(basePath); +#endif +#endif +#endif + while (length > 0 && basePath[length - 1] != '/' && basePath[length - 1] != '\\') + length--; + + if (length > 0) + { + length--; + while (length > 0 && basePath[length - 1] != '/' && basePath[length - 1] != '\\') + length--; + } + + basePath[length] = 0; + + Compiler* compiler = new Compiler(); + + compiler->mCompilerOptions |= COPT_NATIVE; + + Location loc; + + GrowingArray dataFiles(nullptr); + GrowingArray dataFileCompressed(false); + + bool emulate = false, profile = false, customCRT = false, asserts = false, iorange = false, showHelp = false, hasSources = false; + int trace = 0; + + compiler->mPreprocessor->AddPath(basePath); + strcpy_s(includePath, basePath); + strcat_s(includePath, "include"); + + targetPath[0] = 0; + diskPath[0] = 0; + + char targetFormat[20]; + strcpy_s(targetFormat, "prg"); + + char targetMachine[20]; + strcpy_s(targetMachine, "c64"); + + compiler->AddDefine(Ident::Unique("__OSCAR64C__"), "1"); + compiler->AddDefine(Ident::Unique("__STDC__"), "1"); + compiler->AddDefine(Ident::Unique("__STDC_VERSION__"), "199901L"); + + bool defining = false; + + for (int i = 1; i < argc; i++) + { + const char* arg = argv[i]; + if (defining) + { + defining = false; + + char def[100]; + int i = 0; + while (arg[i] && arg[i] != '=') + { + def[i] = arg[i]; + i++; + } + def[i] = 0; + if (arg[i] == '=') + compiler->AddDefine(Ident::Unique(def), _strdup(arg + i + 1)); + else + compiler->AddDefine(Ident::Unique(def), ""); + } + else if (arg[0] == '-') + { + if (arg[1] == 'i' && arg[2] == '=') + { + compiler->mPreprocessor->AddPath(arg + 3); + } + else if (arg[1] == 'i' && arg[2] == 'i' && arg[3] == '=') + { + strcpy_s(includePath, arg + 4); + } + else if (arg[1] == 'f' && arg[2] == '=') + { + dataFiles.Push(arg + 3); + dataFileCompressed.Push(false); + } + else if (arg[1] == 'f' && arg[2] == 'z' && arg[3] == '=') + { + dataFiles.Push(arg + 4); + dataFileCompressed.Push(true); + } + else if (arg[1] == 'f' && arg[2] == 'i' && arg[3] == '=') + { + dataFileInterleave = atoi(arg + 4); + } + else if (arg[1] == 'o' && arg[2] == '=') + { + strcpy_s(targetPath, arg + 3); + } + else if (arg[1] == 'r' && arg[2] == 't' && arg[3] == '=') + { + strcpy_s(crtPath, arg + 4); + customCRT = true; + } + else if (arg[1] == 'd' && arg[2] == '6' && arg[3] == '4' && arg[4] == '=') + { + strcpy_s(diskPath, arg + 5); + } + else if (arg[1] == 't' && arg[2] == 'f' && arg[3] == '=') + { + strcpy_s(targetFormat, arg + 4); + } + else if (arg[1] == 't' && arg[2] == 'm' && arg[3] == '=') + { + strcpy_s(targetMachine, arg + 4); + } + else if (arg[1] == 'c' && arg[2] == 'i' && arg[3] == 'd' && arg[4] == '=') + { + char cid[10]; + strcpy_s(cid, arg + 5); + compiler->mCartridgeID = atoi(cid); + } + else if (arg[1] == 'c' && arg[2] == 's' && arg[3] == 'u' && arg[4] == 'b' && arg[5] == '=') + { + char cid[10]; + strcpy_s(cid, arg + 6); + compiler->mCartridgeSubType = atoi(cid); + } + else if (arg[1] == 'c' && arg[2] == 'n' && arg[3] == 'a' && arg[4] == 'm' && arg[5] == 'e' && arg[6] == '=') + { + strcpy_s(compiler->mCartridgeName, arg + 7); + } + else if (arg[1] == 'n' && arg[2] == 0) + { + compiler->mCompilerOptions |= COPT_NATIVE; + } + else if (arg[1] == 'b' && arg[2] == 'c' && arg[3] == 0) + { + compiler->mCompilerOptions &= ~COPT_NATIVE; + } + else if (arg[1] == 'p' && arg[2] == 's' && arg[3] == 'c' && arg[4] == 'i' && arg[5] == 0) + { + compiler->mCompilerOptions |= COPT_PETSCII; + compiler->AddDefine(Ident::Unique("__PETSCII__"), "1"); + } + else if (arg[1] == 'O') + { + if (arg[2] == '0' && !arg[3]) + compiler->mCompilerOptions &= ~(COPT_OPTIMIZE_ALL); + else if (arg[2] == '1' && !arg[3] || arg[2] == 0) + compiler->mCompilerOptions |= COPT_OPTIMIZE_DEFAULT; + else if (arg[2] == '2' && !arg[3]) + compiler->mCompilerOptions |= COPT_OPTIMIZE_SPEED; + else if (arg[2] == '3' && !arg[3]) + compiler->mCompilerOptions |= COPT_OPTIMIZE_ALL; + else if (arg[2] == 's' && !arg[3]) + compiler->mCompilerOptions |= COPT_OPTIMIZE_SIZE; + else if (arg[2] == 'a' && !arg[3]) + compiler->mCompilerOptions |= COPT_OPTIMIZE_ASSEMBLER; + else if (arg[2] == 'i' && !arg[3]) + compiler->mCompilerOptions |= COPT_OPTIMIZE_AUTO_INLINE; + else if (arg[2] == 'i' && arg[3] == 'i' && !arg[4]) + compiler->mCompilerOptions |= COPT_OPTIMIZE_AUTO_INLINE | COPT_OPTIMIZE_AUTO_INLINE_ALL; + else if (arg[2] == 'z' && !arg[3]) + compiler->mCompilerOptions |= COPT_OPTIMIZE_AUTO_ZEROPAGE; + else if (arg[2] == 'p' && !arg[3]) + compiler->mCompilerOptions |= COPT_OPTIMIZE_CONST_PARAMS; + else if (arg[2] == 'g' && !arg[3]) + compiler->mCompilerOptions |= COPT_OPTIMIZE_GLOBAL; + else if (arg[2] == 'm' && !arg[3]) + compiler->mCompilerOptions |= COPT_OPTIMIZE_MERGE_CALLS; + else if (arg[2] == 'o' && !arg[3]) + compiler->mCompilerOptions |= COPT_OPTIMIZE_OUTLINE; + else if (arg[2] == 'x' && !arg[3]) + compiler->mCompilerOptions |= COPT_OPTIMIZE_PAGE_CROSSING; + else if (arg[2] == 'M' && !arg[3]) + compiler->mCompilerOptions |= COPT_OPTIMIZE_SELF_MOD; + else + compiler->mErrors->Error(loc, EERR_COMMAND_LINE, "Invalid command line argument", arg); + } + else if (arg[1] == 'e') + { + emulate = true; + int i = 2; + while (arg[i]) + { + if (arg[i] == 'p') + profile = true; + else if (arg[i] == 't') + trace = 2; + else if (arg[i] == 'b') + trace = 1; + else if (arg[i] == 'a') + asserts = true; + else if (arg[i] == 'i') + iorange = true; + i++; + } + } + else if (arg[1] == 'D' && !arg[2]) + { + defining = true; + } + else if (arg[1] == 'd' || arg[1] == 'D') + { + char def[100]; + int i = 2; + while (arg[i] && arg[i] != '=') + { + def[i - 2] = arg[i]; + i++; + } + def[i - 2] = 0; + if (arg[i] == '=') + compiler->AddDefine(Ident::Unique(def), _strdup(arg + i + 1)); + else + compiler->AddDefine(Ident::Unique(def), ""); + } + else if (arg[1] == 'g' && !arg[2]) + { + compiler->mCompilerOptions |= COPT_DEBUGINFO; + } + else if (arg[1] == 'g' && arg[2] == 'p' && !arg[3]) + { + compiler->mCompilerOptions |= COPT_DEBUGINFO | COPT_PROFILEINFO; + } + else if (arg[1] == 'h' && !arg[2]) + { + showHelp = true; + } + else if (arg[1] == 'v') + { + compiler->mCompilerOptions |= COPT_VERBOSE; + if (arg[2] == '1') + ; + else if (arg[2] == '2') + compiler->mCompilerOptions |= COPT_VERBOSE2; + else if (arg[2] == '3') + compiler->mCompilerOptions |= COPT_VERBOSE2 | COPT_VERBOSE3; + else if (arg[2]) + compiler->mErrors->Error(loc, EERR_COMMAND_LINE, "Invalid command line argument", arg); + } + else if (arg[1] == 'x' && arg[2] == 'z' && !arg[3]) + { + compiler->mCompilerOptions |= COPT_EXTENDED_ZERO_PAGE; + } + else if (arg[1] == 'p' && arg[2] == 'p' && !arg[3]) + { + compiler->mCompilerOptions |= COPT_CPLUSPLUS; + compiler->AddDefine(Ident::Unique("__cplusplus"), "1"); + compiler->AddDefine(Ident::Unique("__cplusplus__"), "1"); + } + else if (arg[1] == 's' && arg[2] == 't' && arg[3] == 'r' && arg[4] == 'i' && arg[5] == 'c' && arg[6] == 't' && !arg[7]) + { + compiler->mCompilerOptions |= COPT_STRICT; + compiler->AddDefine(Ident::Unique("__strict__"), "1"); + } + else if (arg[1] == 'r' && arg[2] == 'm' && arg[3] == 'p' && !arg[4]) + { + compiler->mCompilerOptions |= COPT_ERROR_FILES; + } + else + compiler->mErrors->Error(loc, EERR_COMMAND_LINE, "Invalid command line argument", arg); + } + else + { + if (!targetPath[0]) + strcpy_s(targetPath, argv[i]); + + ptrdiff_t n = strlen(argv[i]); + if (n > 4 && argv[i][n - 4] == '.' && argv[i][n - 3] == 'c' && argv[i][n - 2] == 'p' && argv[i][n - 1] == 'p') + { + compiler->mCompilerOptions |= COPT_CPLUSPLUS; + compiler->AddDefine(Ident::Unique("__cplusplus"), "1"); + compiler->AddDefine(Ident::Unique("__cplusplus__"), "1"); + } + + compiler->mCompilationUnits->AddUnit(loc, argv[i], nullptr); + + hasSources = true; + } + } + + if (compiler->mCompilerOptions & COPT_NATIVE) + { + compiler->AddDefine(Ident::Unique("OSCAR_NATIVE_ALL"), "1"); + } + + + // REMOVE ME + // compiler->mCompilerOptions |= COPT_OPTIMIZE_GLOBAL; + // REMOVE ME + + char basicStart[10]; + strcpy_s(basicStart, "0x0801"); + + if (!strcmp(targetMachine, "c64")) + { + compiler->mTargetMachine = TMACH_C64; + compiler->AddDefine(Ident::Unique("__C64__"), "1"); + compiler->AddDefine(Ident::Unique("__CBM__"), "1"); + } + else if (!strcmp(targetMachine, "c128")) + { + strcpy_s(basicStart, "0x1c01"); + compiler->mTargetMachine = TMACH_C128; + compiler->AddDefine(Ident::Unique("__C128__"), "1"); + compiler->AddDefine(Ident::Unique("__CBM__"), "1"); + } + else if (!strcmp(targetMachine, "c128b")) + { + strcpy_s(basicStart, "0x1c01"); + compiler->mTargetMachine = TMACH_C128B; + compiler->AddDefine(Ident::Unique("__C128B__"), "1"); + compiler->AddDefine(Ident::Unique("__CBM__"), "1"); + } + else if (!strcmp(targetMachine, "c128e")) + { + strcpy_s(basicStart, "0x1c01"); + compiler->mTargetMachine = TMACH_C128E; + compiler->AddDefine(Ident::Unique("__C128E__"), "1"); + compiler->AddDefine(Ident::Unique("__CBM__"), "1"); + } + else if (!strcmp(targetMachine, "vic20")) + { + strcpy_s(basicStart, "0x1001"); + compiler->mTargetMachine = TMACH_VIC20; + compiler->AddDefine(Ident::Unique("__VIC20__"), "1"); + compiler->AddDefine(Ident::Unique("__CBM__"), "1"); + } + else if (!strcmp(targetMachine, "vic20+3")) + { + strcpy_s(basicStart, "0x0401"); + compiler->mTargetMachine = TMACH_VIC20_3K; + compiler->AddDefine(Ident::Unique("__VIC20__"), "1"); + compiler->AddDefine(Ident::Unique("__CBM__"), "1"); + } + else if (!strcmp(targetMachine, "vic20+8")) + { + strcpy_s(basicStart, "0x1201"); + compiler->mTargetMachine = TMACH_VIC20_8K; + compiler->AddDefine(Ident::Unique("__VIC20__"), "1"); + compiler->AddDefine(Ident::Unique("__CBM__"), "1"); + } + else if (!strcmp(targetMachine, "vic20+16")) + { + strcpy_s(basicStart, "0x1201"); + compiler->mTargetMachine = TMACH_VIC20_16K; + compiler->AddDefine(Ident::Unique("__VIC20__"), "1"); + compiler->AddDefine(Ident::Unique("__CBM__"), "1"); + } + else if (!strcmp(targetMachine, "vic20+24")) + { + strcpy_s(basicStart, "0x1201"); + compiler->mTargetMachine = TMACH_VIC20_24K; + compiler->AddDefine(Ident::Unique("__VIC20__"), "1"); + compiler->AddDefine(Ident::Unique("__CBM__"), "1"); + } + else if (!strcmp(targetMachine, "pet")) + { + strcpy_s(basicStart, "0x0401"); + compiler->mTargetMachine = TMACH_PET_8K; + compiler->AddDefine(Ident::Unique("__CBMPET__"), "1"); + compiler->AddDefine(Ident::Unique("__CBM__"), "1"); + } + else if (!strcmp(targetMachine, "pet16")) + { + strcpy_s(basicStart, "0x0401"); + compiler->mTargetMachine = TMACH_PET_16K; + compiler->AddDefine(Ident::Unique("__CBMPET__"), "1"); + compiler->AddDefine(Ident::Unique("__CBM__"), "1"); + } + else if (!strcmp(targetMachine, "pet32")) + { + strcpy_s(basicStart, "0x0401"); + compiler->mTargetMachine = TMACH_PET_32K; + compiler->AddDefine(Ident::Unique("__CBMPET__"), "1"); + compiler->AddDefine(Ident::Unique("__CBM__"), "1"); + } + else if (!strcmp(targetMachine, "plus4")) + { + strcpy_s(basicStart, "0x1001"); + compiler->mTargetMachine = TMACH_PLUS4; + compiler->AddDefine(Ident::Unique("__PLUS4__"), "1"); + compiler->AddDefine(Ident::Unique("__CBM__"), "1"); + } + else if (!strcmp(targetMachine, "mega65")) + { + strcpy_s(basicStart, "0x2001"); + compiler->mTargetMachine = TMACH_MEGA65; + compiler->AddDefine(Ident::Unique("__MEGA65__"), "1"); + } + else if (!strcmp(targetMachine, "x16")) + { + strcpy_s(basicStart, "0x0801"); + compiler->mTargetMachine = TMACH_X16; + compiler->AddDefine(Ident::Unique("__X16__"), "1"); + } + else if (!strcmp(targetMachine, "nes")) + { + compiler->mTargetMachine = TMACH_NES; + } + else if (!strcmp(targetMachine, "nes_nrom_h")) + { + compiler->mTargetMachine = TMACH_NES_NROM_H; + } + else if (!strcmp(targetMachine, "nes_nrom_v")) + { + compiler->mTargetMachine = TMACH_NES_NROM_V; + } + else if (!strcmp(targetMachine, "nes_mmc1")) + { + compiler->mTargetMachine = TMACH_NES_MMC1; + } + else if (!strcmp(targetMachine, "nes_mmc3")) + { + compiler->mTargetMachine = TMACH_NES_MMC3; + } + else if (!strcmp(targetMachine, "atari")) + { + compiler->mTargetMachine = TMACH_ATARI; + compiler->AddDefine(Ident::Unique("__ATARI__"), "1"); + } + else + compiler->mErrors->Error(loc, EERR_COMMAND_LINE, "Invalid target machine option", targetMachine); + + + if (compiler->mTargetMachine >= TMACH_NES && compiler->mTargetMachine <= TMACH_NES_MMC3) + { + compiler->mCompilerOptions |= COPT_TARGET_NES; + compiler->mCompilerOptions |= COPT_EXTENDED_ZERO_PAGE; + compiler->mCompilerOptions |= COPT_NATIVE; + compiler->AddDefine(Ident::Unique("OSCAR_TARGET_NES"), "1"); + switch (compiler->mTargetMachine) + { + default: + case TMACH_NES: + case TMACH_NES_NROM_H: + case TMACH_NES_NROM_V: + break; + case TMACH_NES_MMC1: + compiler->AddDefine(Ident::Unique("__NES_MMC1__"), "1"); + break; + case TMACH_NES_MMC3: + compiler->AddDefine(Ident::Unique("__NES_MMC3__"), "1"); + break; + } + compiler->AddDefine(Ident::Unique("__NES__"), "1"); + } + else if (!strcmp(targetFormat, "prg")) + { + compiler->mCompilerOptions |= COPT_TARGET_PRG; + compiler->AddDefine(Ident::Unique("OSCAR_TARGET_PRG"), "1"); + compiler->AddDefine(Ident::Unique("OSCAR_BASIC_START"), basicStart); + } + else if (!strcmp(targetFormat, "crt")) + { + compiler->mCompilerOptions |= COPT_TARGET_CRT_EASYFLASH; + compiler->mCartridgeID = 0x0020; + + compiler->AddDefine(Ident::Unique("OSCAR_TARGET_CRT_EASYFLASH"), "1"); + } + else if (!strcmp(targetFormat, "crt16")) + { + compiler->mCompilerOptions |= COPT_TARGET_CRT16; + compiler->AddDefine(Ident::Unique("OSCAR_TARGET_CRT16"), "1"); + } + else if (!strcmp(targetFormat, "crt8")) + { + compiler->mCompilerOptions |= COPT_TARGET_CRT8; + compiler->AddDefine(Ident::Unique("OSCAR_TARGET_CRT8"), "1"); + } + else if (!strcmp(targetFormat, "bin")) + { + compiler->mCompilerOptions |= COPT_TARGET_BIN; + compiler->AddDefine(Ident::Unique("OSCAR_TARGET_BIN"), "1"); + } + else if (!strcmp(targetFormat, "lzo")) + { + compiler->mCompilerOptions |= COPT_TARGET_LZO; + compiler->AddDefine(Ident::Unique("OSCAR_TARGET_LZO"), "1"); + compiler->AddDefine(Ident::Unique("OSCAR_BASIC_START"), basicStart); + } + else + compiler->mErrors->Error(loc, EERR_COMMAND_LINE, "Invalid target format option", targetFormat); + + if (showHelp) + writeHelp(); + + strcpy_s(compiler->mVersion, strProductVersion); + + if (compiler->mCompilerOptions & COPT_VERBOSE) + { + printf("Starting %s %s\n", strProductName, strProductVersion); + } + DiskImage* d64; + + if (diskPath[0] != '\0') + d64 = new DiskImage(diskPath); + else + d64 = nullptr; + + if (compiler->mErrors->mErrorCount == 0 && (customCRT || hasSources)) + { + compiler->RemoveErrorFile(targetPath); + + { + char dstring[100], tstring[100]; + time_t now = time(NULL); + struct tm t; +#ifdef _WIN32 + localtime_s(&t, &now); +#else + localtime_r(&now, &t); +#endif + + strftime(dstring, sizeof(tstring) - 1, "\"%b %d %Y\"", &t); + strftime(tstring, sizeof(dstring) - 1, "\"%H:%M:%S\"", &t); + + compiler->AddDefine(Ident::Unique("__DATE__"), _strdup(dstring)); + compiler->AddDefine(Ident::Unique("__TIME__"), _strdup(tstring)); + } + + if (compiler->mCompilerOptions & COPT_OPTIMIZE_CODE_SIZE) + compiler->AddDefine(Ident::Unique("OSCAR_OPTIMIZE_SIZE"), "1"); + + + // Add runtime module + + compiler->mPreprocessor->AddPath(includePath); + + if (hasSources && !customCRT) + { + FILE* crtFile; + char crtFileNamePath[FILENAME_MAX]; + crtFileNamePath[FILENAME_MAX - 1] = '\0'; + strcpy_s(crtFileNamePath, includePath); + strcat_s(crtFileNamePath, "/crt.h"); + + if (fopen_s(&crtFile, crtFileNamePath, "r")) + { + strcpy_s(crtFileNamePath, basePath); + strcat_s(crtFileNamePath, "include/oscar64/crt.h"); + + if (!fopen_s(&crtFile, crtFileNamePath, "r")) { + strcpy_s(includePath, basePath); + strcat_s(includePath, "include/oscar64/"); + compiler->mPreprocessor->AddPath(includePath); + } + else + { + printf("Could not locate Oscar64 includes under %s\n", basePath); + return 20; + } + } + fclose(crtFile); + + strcpy_s(crtPath, includePath); + strcat_s(crtPath, "/crt.c"); + } + + if (crtPath[0] != '\0') + compiler->mCompilationUnits->AddUnit(loc, crtPath, nullptr); + + if (compiler->mCompilerOptions & COPT_TARGET_LZO) + { + compiler->BuildLZO(targetPath); + } + else if (compiler->ParseSource() && compiler->GenerateCode()) + { + compiler->WriteOutputFile(targetPath, d64); + + + if (emulate) + compiler->ExecuteCode(profile, trace, asserts, iorange); + } + else if (compiler->mCompilerOptions & COPT_ERROR_FILES) + { + compiler->WriteErrorFile(targetPath); + } + } + + if (compiler->mErrors->mErrorCount == 0 && d64 != nullptr) + { + for (int i = 0; i < dataFiles.Size(); i++) + { + if (!d64->WriteFile(dataFiles[i], dataFileCompressed[i], dataFileInterleave)) + { + printf("Could not embed disk file %s\n", dataFiles[i]); + return 20; + } + } + + if (!d64->WriteImage(diskPath)) + { + printf("Could not write disk image %s\n", diskPath); + return 20; + } + } + + if (compiler->mErrors->mErrorCount != 0) + return 20; + } + else + { + printf("oscar64 [-h] {-i=includePath} [-o=output.prg] [-rt=runtime.c] [-tf=target] [-tm=machine] [-e] [-n] [-g] [-O(0|1|2|3)] [-pp] {-dSYMBOL[=value]} [-v] [-d64=diskname] {-f[z]=file.xxx} {source.c}\n"); + + return 0; + } + + return 0; +} + + +#ifdef WIN32 +#ifndef _DEBUG +int seh_filter(unsigned int code, struct _EXCEPTION_POINTERS* info) +{ + +#ifdef _WIN64 + +#pragma warning( push ) +#pragma warning( disable : 4996) + + char buff[256]; + switch (info->ExceptionRecord->ExceptionCode) + { + case EXCEPTION_ACCESS_VIOLATION: + sprintf(buff, "Addressing Error[Permissions do not allow this access]"); + break; + + case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: + sprintf(buff, "Addressing Error[Array Bounds Exceeded]"); + break; + + case EXCEPTION_BREAKPOINT: + sprintf(buff, "Breakpoint Instruction Encountered"); + break; + + case EXCEPTION_DATATYPE_MISALIGNMENT: + sprintf(buff, "Addressing Error[Data Access Misaligned]"); + break; + + case EXCEPTION_FLT_DENORMAL_OPERAND: + sprintf(buff, "Arithmetic Exception[Float Denormal Operand]"); + break; + + case EXCEPTION_FLT_DIVIDE_BY_ZERO: + sprintf(buff, "Arithmetic Exception[Float Divide-by-zero]"); + break; + + case EXCEPTION_FLT_INEXACT_RESULT: + sprintf(buff, "Arithmetic Exception[Float Inexact Result]"); + break; + + case EXCEPTION_FLT_INVALID_OPERATION: + sprintf(buff, "Arithmetic Exception[Float Invalid Operation]"); + break; + + case EXCEPTION_FLT_OVERFLOW: + sprintf(buff, "Arithmetic Exception[Float Overflow]"); + break; + + case EXCEPTION_FLT_STACK_CHECK: + sprintf(buff, "Arithmetic Exception[Float Stack Check]"); + break; + + case EXCEPTION_FLT_UNDERFLOW: + sprintf(buff, "Arithmetic Exception[Float Underflow]"); + break; + + case EXCEPTION_ILLEGAL_INSTRUCTION: + sprintf(buff, "Illegal Instruction"); + break; + + case EXCEPTION_IN_PAGE_ERROR: + sprintf(buff, "Memory Paging Error"); + break; + + case EXCEPTION_INT_DIVIDE_BY_ZERO: + sprintf(buff, "Arithmetic Exception[Integer Divide-by-zero]"); + break; + + case EXCEPTION_INT_OVERFLOW: + sprintf(buff, "Arithmetic Exception[Integer Overflow]"); + break; + + case EXCEPTION_INVALID_DISPOSITION: + sprintf(buff, "Exception Handler Invalid Disposition"); + break; + + case EXCEPTION_NONCONTINUABLE_EXCEPTION: + sprintf(buff, "Thread Attempted To Continue When Not Allowed"); + break; + + case EXCEPTION_PRIV_INSTRUCTION: + sprintf(buff, "Illegal Instruction[Privileged Op code]"); + break; + + case EXCEPTION_SINGLE_STEP: + sprintf(buff, "Single Step Executed"); + break; + + case EXCEPTION_STACK_OVERFLOW: + sprintf(buff, "Stack Fault[overfolow]"); + break; + + default: + sprintf(buff, "Unknown code 0x%-8.8lx", info->ExceptionRecord->ExceptionCode); + break; + } + + printf("%s at location 0x%016llx\n", buff, info->ContextRecord->Rip); + + DWORD err; + HANDLE hProcess; + ULONG64 buffer[(sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR) + sizeof(ULONG64) - 1) / sizeof(ULONG64) + 20]; + PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)buffer; + IMAGEHLP_LINE64 line = { sizeof(IMAGEHLP_LINE64) }; + DWORD64 displacement; + DWORD loffset; + + SymSetOptions(SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS); + hProcess = GetCurrentProcess(); + + BOOL printSymbols = SymInitialize(hProcess, NULL, TRUE); + if (!printSymbols) + { + // SymInitialize failed + err = GetLastError(); + printf("SymInitialize returned error : %d\n", err); + return EXCEPTION_EXECUTE_HANDLER; + } + + STACKFRAME64 stackFrame = { 0 }; + CONTEXT context = *info->ContextRecord; + stackFrame.AddrPC.Offset = info->ContextRecord->Rip; + stackFrame.AddrPC.Segment = 0; + stackFrame.AddrPC.Mode = AddrModeFlat; + stackFrame.AddrFrame.Offset = info->ContextRecord->Rbp; + stackFrame.AddrFrame.Segment = 0; + stackFrame.AddrFrame.Mode = AddrModeFlat; + stackFrame.AddrFrame.Offset = info->ContextRecord->Rsp; + stackFrame.AddrStack.Mode = AddrModeFlat; + stackFrame.AddrStack.Segment = 0; + + while (StackWalk64(IMAGE_FILE_MACHINE_AMD64, + GetCurrentProcess(), + GetCurrentThread(), + &stackFrame, + &context, + NULL, /* PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine */ + SymFunctionTableAccess64, + SymGetModuleBase64, + NULL /* PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress */ + )) + { + if (true) + { + printf("...StackFrame\n"); + printf("......PC address 0x%016llx\n", stackFrame.AddrPC.Offset); + printf("......Module address 0x%016llx\n", SymGetModuleBase64(GetCurrentProcess(), stackFrame.AddrPC.Offset)); + printf("......Return address 0x%016llx\n", stackFrame.AddrReturn.Offset); + printf("......Frame address 0x%016llx\n", stackFrame.AddrFrame.Offset); + printf("......Stack address 0x%016llx\n", stackFrame.AddrStack.Offset); + printf("......Param[0] 0x%016llx\n", stackFrame.Params[0]); + printf("......Param[1] 0x%016llx\n", stackFrame.Params[1]); + printf("......Param[2] 0x%016llx\n", stackFrame.Params[2]); + printf("......Param[3] 0x%016llx\n", stackFrame.Params[3]); + } + pSymbol->MaxNameLen = MAX_SYM_NAME; + pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO);; + + if (SymFromAddr(hProcess, stackFrame.AddrPC.Offset, &displacement, pSymbol)) + { + if (SymGetLineFromAddr64(hProcess, stackFrame.AddrPC.Offset, &loffset, &line)) + { + printf("...Procedure %s:0x%-8.8lx LINE %s.%d\n", pSymbol->Name, (DWORD)displacement, line.FileName, line.LineNumber); + } + else + { + printf("...Procedure %s:0x%-8.8lx\n", pSymbol->Name, (DWORD)displacement); + } + } + else + { + err = GetLastError(); + printf("Call to SymEnumSymbolsForAddr failed, error = %d (0x%-8.8x)\n", err, err); + } + } + printf("...Returning EXCEPTION_EXECUTE_HANDLER\n"); + +#pragma warning( pop ) + +// printf("oscar64 crashed. %08x %08llx", info->ExceptionRecord->ExceptionCode, (uint64)(info->ExceptionRecord->ExceptionAddress)); +#else + printf("oscar64 crashed. %08x %08x", info->ExceptionRecord->ExceptionCode, (uint32)(info->ExceptionRecord->ExceptionAddress)); +#endif + return EXCEPTION_EXECUTE_HANDLER; +} +#endif +#endif + +int main(int argc, const char** argv) +{ +#if 1 +#ifdef _WIN32 +#ifndef __GNUC__ +#ifndef _DEBUG + __try + { +#endif +#endif +#endif +#endif + return main2(argc, argv); + +#if 1 +#ifdef _WIN32 +#ifndef __GNUC__ +#ifndef _DEBUG +} + __except (seh_filter(GetExceptionCode(), GetExceptionInformation())) + { + return 30; + } +#endif +#endif +#endif +#endif +} diff --git a/oscar64/oscar64/oscar64.rc b/oscar64/oscar64/oscar64.rc new file mode 100644 index 0000000..a77b502 --- /dev/null +++ b/oscar64/oscar64/oscar64.rc @@ -0,0 +1,110 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (Neutral) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_NEUTRAL +#pragma code_page(1252) + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,32,272,0 + PRODUCTVERSION 1,32,272,0 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "000904b0" + BEGIN + VALUE "CompanyName", "oscar64" + VALUE "FileDescription", "oscar64 compiler" + VALUE "FileVersion", "1.32.272.0" + VALUE "InternalName", "oscar64.exe" + VALUE "LegalCopyright", "Copyright (C) 2021" + VALUE "OriginalFilename", "oscar64.exe" + VALUE "ProductName", "oscar64" + VALUE "ProductVersion", "1.32.272.0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x9, 1200 + END +END + +#endif // English (Neutral) resources +///////////////////////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////////////////////// +// German (Germany) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_DEU) +LANGUAGE LANG_GERMAN, SUBLANG_GERMAN +#pragma code_page(1252) + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + +#endif // German (Germany) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/oscar64/oscar64/oscar64.vcxproj b/oscar64/oscar64/oscar64.vcxproj new file mode 100644 index 0000000..f972dc0 --- /dev/null +++ b/oscar64/oscar64/oscar64.vcxproj @@ -0,0 +1,235 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + Win32Proj + {1dbc623e-6109-41fe-b1bb-9b43fc984f7d} + oscar64 + 10.0 + + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + false + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + false + + + + + + + + + + + + + + + + + + + + + true + + + false + + + true + $(SolutionDir)\$(Configuration)$(Platform)\ + $(Configuration)$(Platform)\ + + + false + $(SolutionDir)\$(Configuration)$(Platform)\ + $(Configuration)$(Platform)\ + + + + Level3 + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;version.lib;%(AdditionalDependencies) + 16000000 + + + + + Level3 + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + false + + + Console + true + true + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;version.lib;%(AdditionalDependencies) + 16000000 + true + false + + + copy $(TargetPath) $(SolutionDir)bin + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;version.lib;%(AdditionalDependencies) + 160000000 + + + + + Level3 + true + true + false + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + AnySuitable + false + Speed + true + StreamingSIMDExtensions2 + ProgramDatabase + Caret + true + + + Console + true + true + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;version.lib;dbghelp.lib;%(AdditionalDependencies) + 16000000 + + + copy $(TargetPath) $(SolutionDir)bin + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/oscar64/oscar64/oscar64.vcxproj.filters b/oscar64/oscar64/oscar64.vcxproj.filters new file mode 100644 index 0000000..371cbf1 --- /dev/null +++ b/oscar64/oscar64/oscar64.vcxproj.filters @@ -0,0 +1,185 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/oscar64/oscar64/resource.h b/oscar64/oscar64/resource.h new file mode 100644 index 0000000..f85f7a5 --- /dev/null +++ b/oscar64/oscar64/resource.h @@ -0,0 +1,14 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by oscar64.rc + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/oscar64/oscar64setup/oscar64setup.vdproj b/oscar64/oscar64setup/oscar64setup.vdproj new file mode 100644 index 0000000..15637e4 --- /dev/null +++ b/oscar64/oscar64setup/oscar64setup.vdproj @@ -0,0 +1,6811 @@ +"DeployProject" +{ +"VSVersion" = "3:800" +"ProjectType" = "8:{978C614F-708E-4E1A-B201-565925725DBA}" +"IsWebType" = "8:FALSE" +"ProjectName" = "8:oscar64setup" +"LanguageId" = "3:1033" +"CodePage" = "3:1252" +"UILanguageId" = "3:1033" +"SccProjectName" = "8:" +"SccLocalPath" = "8:" +"SccAuxPath" = "8:" +"SccProvider" = "8:" +"BackwardsCompatibleGUIDGeneration" = "8:TRUE" + "Hierarchy" + { + "Entry" + { + "MsmKey" = "8:_0045AD9C29CB49C39F9322ABDE701C11" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_007FA3120B09491581F435FEE187EC9F" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_035D8376B0CC41EA90DBB294D667A617" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_04ABABC55200450383686DD782DD1548" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_05BBBE09BC7047B6AB2F10A2E9032E37" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_06971480436546E1B12DDC7EC9F704FD" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_071415C872DB4A94B28137BD7FE1F669" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_071CF34574024DBD95D2203D6E4279D0" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_0819C3D33D0942EF9E319C754E6C43D8" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_08F70A83B36D428A9F0C9EFC69787FF1" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_0CAC2197F4C34B33AFF5A3D0A7C78454" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_0CC2E11595B34DF0A003DF534A5AADF5" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_0D15550C6DED4A3DB1301B4EC13A4320" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_0D8B657E4A954DBFAF14055CDFFB384C" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_0EE9160CA4D44278A3929B0A4E0789F8" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_1088B8AF518A4841A419B723AEB6C2BA" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_11003A1822704117BBF4B206A133EAD9" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_12ACD00110E34BF9A3C06E00A16449D9" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_12C998C755E643038CDF5D9F56983ECA" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_1328E18B9638422795616BABEAAFA600" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_1393003825B9451084DB314DD4267AD5" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_16B8661A949341E3866A400D7B7FFC03" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_179A529C9A50404D8053B1204DBEE943" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_19321EF0356047C9A6AEE738ECDEB9C3" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_1A605A19ADAD413CB754523D4FE7C32F" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_1AAB1F90101E40B6937A64FD4BF9D469" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_1EB5CE4736F1417ABD479DFD92E19FD2" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_1ED10FB93DDA4801BF72003E21B2CE55" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_1F88FA4F35F043B3ABFCB552FCEA5CDD" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_216D2A5FECF74C96A4964A74DFEB8552" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_21C809DE4B19458FB97988B235805A95" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_222FC9C47F124FE08B8D66C56A07DD94" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_23211A34D7544532ABFD8D17B647717F" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_24F2466589154E2DAA108C2EDB3BB432" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_26E38AB4F85C4C9DB284803C4A3473F3" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_28CED61D2559486D87F70EC821B89525" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_29ED5D9B606D45DA80CFF329A7643DC8" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_2C39AA436BFE4300AB4738C879E6E4F4" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_2CC169DCBB0B4CC0BFE38AE63B285359" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_2CE4E742BFAC456C99ECA1DF0BFC5F34" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_2D828D0247F144CDB0112B2AD4004C2C" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_2DB9B0614EAC452A96802454D1BE5ACA" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_315C94A7D90D4CF5BF95F3B57482CADA" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_317711E6E48744A18655469B4C53767E" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_3277DE1463544F67B7E7390175F8A9CF" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_33E047EAEFFB45778C59472F157161DA" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_343F58F80DF84D40AE23457288C5D7D5" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_379EE3C17FEC4C5EA79D07668CD05FC4" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_37F135A207264D5984B4B81EC44A4337" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_38759DBF58B3475AA934C0DA70FB5B81" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_3A3E6E45463E4839BDB96B1C7097DA8A" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_3AFDC86156F04AABB8D82218C17005F2" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_3C29D5D111D24946BEF556FBAB178506" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_3FFD08277B804985BDF072C0C1877287" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_41A6A127243E4EC2A1E1E2171993C5C1" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_41AAEE312C4C4C5E80FFF8C311D70F2C" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_41C57237B9DA453DB84BBC1F1BE29E49" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_438F2450C997431898912DA2D4DFCEAE" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_43B55A11B25F4F13A9AE7E36B1AD2897" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_44A56A2AD0C84900ACDDA218E57165D8" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_47A877D439EE429BAB64C52FEF69EDA4" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_492AD1ED992D44F1A65903A5232C0426" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_494DB63135144419A8E0E6037E1B3237" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_4A1182DE6E79476B9FE3EAE072252A90" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_4B8FC526E6CC47FC8321D0191BFDBEDC" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_4CAC4169D7CC4A2099042927E32B2184" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_4D4981B8F95A4704A9E34C066DBD7563" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_4E9503F4F8F343739E4685C2066B3AD0" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_539501D7FC4941B8AEE943A2D5C74703" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_542D20FAC6B34E318FA36AD50BF3EEBE" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_557F7BC3AD794C4A93D5DFDA00024E1B" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_5832EB4DC2134FF0BB7872666547CA0B" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_585B41257840451B8AF01582B8B032D1" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_5A2F25CDE50B4AFAB0E0D5A3E787ABDD" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_5ACC41BF427E47EBB9A4A66BD117FF80" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_5C4D0F37C6784D198C3199B0610F60E5" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_6094F07357CC41E39D165B0A81E5AD4C" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_610F946CE11C4867B87A581F0FFD9030" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_636E67C8267143AA9FC128EB0AF85FC9" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_63A827ECF1BC48CE96DB82B554FC323D" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_64C12019C1114C3282B32CD6B866C20A" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_65286F9E8B2F496B95A50DA63A1F0D5B" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_652D33F4F54F4974AA6C4CD23A323FD1" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_67C8F824E02B4211B315AB32AB7ABBB1" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_67F949223D154D92A9EC79A2A9CD1C97" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_683E44581EBB4154841523B526E1B4B4" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_6EE9325B01F74FFF8146A6AD7FE71A31" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_7127D91089DA4162BB8DC8EAB0C107F0" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_71E85695DAA74E2297BA826A3D8A6B37" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_72FE541170AB418781C6087271FD3D74" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_7393641DDDD742BCA845F192181E1294" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_749F54DFBD4D404DA9C2E2D5BA7CDDBF" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_74E75A13AC624648B03EB53735D70DE0" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_76FEB96DC82F4EC681034B0A54620EAB" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_7789738FE8854CE488C5248434E47211" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_79985361F09A43299E258E1A8E5ED934" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_7A9E700FC438425580655F7C1BC07FD3" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_7AC94D519AF047EF853BD602790627C0" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_7B3ADAAB82E944EDBA72636B19969B4A" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_7B4E6DFBFF944508821C3B54AE7E3D5E" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_7DC360D088E84BB5AA7B338C464439A9" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_7DD72563FCD1431C95C845C0C4370BC6" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_7E6F12A2B7A647199FCBA7CBDF4E94D2" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_7EA67552E0B34B9BA70152AD9E369EA6" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_7FF22AC441314566AA43E02CA040F4BC" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_8004350ED41848E29D306A0A86DDA90E" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_814F6BFC9788449DB259F99386D39AFD" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_82C11E2A96FE49E399EEEAC2FEE9DCDC" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_85AF458C41734A099F879A01E6CEB35F" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_85CB18843FC0454FB3451EC86725D782" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_8827B6B07A1C4B32B08DF784E090381D" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_89C3C9640CD142CCB3192AF3B15FC771" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_8B258D238AC44D43A7DC654AFE0B9D8B" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_8CCBAD6654AC4B3F829EB04F37BE3EB2" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_8CE64311B70C454D866A7E0098455EB5" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_8E57961EDF0840A7A08877E8ED4826BC" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_8EBEED55D3F142769DE0A0014758447D" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_90171C7FE48D426E8664B043F5E75D6B" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_94AD9BFFA9D04AA0B6BC6EEA1D9A93ED" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_95B2F42C70F1469C851D5751CC82319C" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_963E94CDB4DF4300B48C537010EDF36F" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_97FBAA1E30F140628447208D520BC191" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_9893C97816744FFC9EC442940C79E58B" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_98968728BFD9473FBCFD4E6A3B113A1F" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_98B942589E984AAD381F87F18DDB79D3" + "OwnerKey" = "8:_FB2E467BC172457785F4279BB0BFE8B6" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_9B2E1BB3CD154CEFB214378A64B0B00C" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_9D0D7A63D6C848CD85489D6E7C43AFAD" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_9D4472D86BDF48E387059DCB67990AA9" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_9D5C03E361EC4766AF0204219047B6C8" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_9E8E4DCF8AD041A9A9EA2716F82D36AF" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_9EF4DA2EABDB4E3B864CCDEC851B7037" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_A14BC161099E420286E6A1595596D0F0" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_A16BC2645A14DC0435B7119153B32DE8" + "OwnerKey" = "8:_FB2E467BC172457785F4279BB0BFE8B6" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_A18504BA88CE40128ED44BD1854F0A33" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_A27837E885F24200AC5CD5D9F8A0C2A4" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_A284F8E444A340B986AAE126C7E5A387" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_A32310DEFD4E41118CD7D36A8614C0D4" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_A32AAE4931564357A3222F04C8D99C2F" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_A566398810C1458E8E063A81FA88D46F" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_A62BFFC051304130B85007261BC97749" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_A7796DF7A31E4292ADF6BECCF2604081" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_A8820F3446B9408E88692E85817C1B07" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_A8F4F0F1A9F94DD2BE7D6582EF9F58A8" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_AB5120799C684496B5E81A4212BD54D4" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_AB90D0DCB4E044348BCCD9054215278F" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_AE023AEA6C444140B19CD627DDC2CB87" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_AEE4BA494D48426089DE7D870C4F399B" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_B037CA4170834093B03F7067F7E2D5A5" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_B0A61EA0BB804AEAB25D6C4B6F4C57E6" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_B275D22D5B444CC0A8E44E63010B9759" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_B2F1B217D45A434DBA8EC21095F4D717" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_B4265CBF352343D2867DBCCE67D9F493" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_B59AF91A39B749DF9E6B95CD35C5C6AC" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_B61CD152136A6E3CE5FF69D29E7A3F7B" + "OwnerKey" = "8:_FB2E467BC172457785F4279BB0BFE8B6" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_B85448D515F0487FB6845119AE346F1D" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_BA92710604A2499F8577F52758F3F529" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_BAE9C5C261D54D06B5CBE68CDFA3809F" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_BBECB7DB41D84A34B2837C5E7EC7FAD4" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_BC5DE43AE7DE49AAA858A5CD2B985416" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_BC6636B5CB534552BC23A19BCF4DD252" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_BFAE73A1432845AB83CAA44F3A7EC9C3" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_C239EFB1C3A646809AD2740ABDE747B0" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_C30DFFA5DE3D4FEC982E5EB08F63C55E" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_C3C6C74DFAB942AA8BFC15885A2D9BD0" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_C433B7556E0448C6A98F8357DEA96507" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_C43DB5F721EA4B4A81507E9702E8FD4B" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_C4B1FC7B672E4E538E92FD06D90AE808" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_C6B8A6C354494A50BBFD7EE6B2FB39B6" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_C6CAE25C0BCA43E58CF2062C2700F12F" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_C72E9CB8EC154F9BA499FAC968B83A93" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_C94FC3629B574FD3B4B86DADD8D77812" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_CA8FE59769D748A5BF04ABC3F9154DBA" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_CACD2FA2FFF3488AB6C5A0E5980B7805" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_CC473A7B399D4B31A9DC8C94F7771EF7" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_CCB9E57C55E9405DA3EBBF4E6131004A" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_CD97BB2D0A3349FD86EE0058561FB8A7" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_CDBE2B8738C843CBB146767FBEE9A828" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_CEB3122F595C457EA809182C29CB8B2A" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_D0E45B48D76B4407B0BDE4378C1DB2C7" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_D16004065A954CC7A91F72185792B436" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_D1EF53E7B029443F8EE2E8174F8201ED" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_D2CCF52F2336415EAD896A2C5410D5E8" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_D34AB6F4EE6C4E18A8C6D44CC8E23CB7" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_D37E98655388482FB2C18CD585A68AE7" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_D38EB85FE8E3492DA4BD8E6E4F7CEBEB" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_D52B954DFBCE4EB0932433DE7FCE9090" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_D651A6B3342C4D1ABC7FE873E253A1C5" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_D76DA8C7D3964C93ABE193A7B53A0A9B" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_D7FC57E3C7D54299AD14AE1BFBB2103C" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_D82F590F7B264CCEBE12A9427BD4A6C9" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_D9089340F2BF445B85880C886B30D7B7" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_DA28A07E7836459C99161100D7C102B8" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_DBCD7B3D63C34E31A882799F3C82F0E9" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_DE1058FFA9E149D1909C819592A12273" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_DEADBEA270134B77800770802B21859C" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_E00658F953AC4735A6ED77408A74163D" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_E11BDCFFB9944A1F893FE4EC86ACCD6C" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_E218D776D9014F99BE2B046AEF2D6E8B" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_E34D6FBF8D3F40F3B7617DC34EFB31F0" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_E3FF6A8B315C439DA54DE18DB39FB3AD" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_E4138658F1F544CF86255EDC1659936D" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_E4F56F827D7644939B1EB5307A1DB263" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_E5405BA024FB44FB8A71DC51B3B35428" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_E54AFD61BAD0414C9C5B2EFF31DA01FA" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_E58D098F58734A09BB721C2DB6E1D8A3" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_E671F62994E148F1AD835BC530525D2C" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_E87F2F9F79424FBBBD071EF633A81BB5" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_EA3C0BCB01F2639DFA2E37EC8436E5F6" + "OwnerKey" = "8:_FB2E467BC172457785F4279BB0BFE8B6" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_EC599856E7AE44F4A2F51AA76A4D044C" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_ED20DE3FDC8A400296E92AE0D37D8665" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_ED5FED616EA64689AB793E5D8BD3F13D" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_ED872D39D58443D590B7C80604BC0FF4" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_EDDED74E9AC24D9C8C7811F3A20ACA53" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_EF27132EAC3147A492E72EC29DD9A16D" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_F06F7114D6184A03962BE1C8AA697FEF" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_F1B639D383C846C2A7CCF8031E524A76" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_F35970F9D8FA46B09F36D7E9DE5532CA" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_F472CF5FA6CF43649B4CD8E5F204DA65" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_F715B9FA6E0048388F451AB4670FD1EB" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_F8F279E2317342BB8F6FD09FE8FE4EF5" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_FB2E467BC172457785F4279BB0BFE8B6" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_FCF3696486DC430EA7BFACA8BE9731E7" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_FD0B1CB15474472987407696041DEE5B" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_FDDF38BFE6BA48C88ED8CB7EADD94159" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + } + "Configurations" + { + "Debug" + { + "DisplayName" = "8:Debug" + "IsDebugOnly" = "11:TRUE" + "IsReleaseOnly" = "11:FALSE" + "OutputFilename" = "8:Debug\\oscar64setup.msi" + "PackageFilesAs" = "3:2" + "PackageFileSize" = "3:-2147483648" + "CabType" = "3:1" + "Compression" = "3:2" + "SignOutput" = "11:FALSE" + "CertificateFile" = "8:" + "PrivateKeyFile" = "8:" + "TimeStampServer" = "8:" + "InstallerBootstrapper" = "3:2" + "BootstrapperCfg:{63ACBE69-63AA-4F98-B2B6-99F9E24495F2}" + { + "Enabled" = "11:TRUE" + "PromptEnabled" = "11:TRUE" + "PrerequisitesLocation" = "2:1" + "Url" = "8:" + "ComponentsUrl" = "8:" + "Items" + { + "{EDC2488A-8267-493A-A98E-7D9C3B36CDF3}:.NETFramework,Version=v4.7.2" + { + "Name" = "8:Microsoft .NET Framework 4.7.2 (x86 and x64)" + "ProductCode" = "8:.NETFramework,Version=v4.7.2" + } + } + } + } + "Release" + { + "DisplayName" = "8:Release" + "IsDebugOnly" = "11:FALSE" + "IsReleaseOnly" = "11:TRUE" + "OutputFilename" = "8:Release\\oscar64setup.msi" + "PackageFilesAs" = "3:2" + "PackageFileSize" = "3:-2147483648" + "CabType" = "3:1" + "Compression" = "3:2" + "SignOutput" = "11:FALSE" + "CertificateFile" = "8:" + "PrivateKeyFile" = "8:" + "TimeStampServer" = "8:" + "InstallerBootstrapper" = "3:2" + "BootstrapperCfg:{63ACBE69-63AA-4F98-B2B6-99F9E24495F2}" + { + "Enabled" = "11:TRUE" + "PromptEnabled" = "11:TRUE" + "PrerequisitesLocation" = "2:1" + "Url" = "8:" + "ComponentsUrl" = "8:" + "Items" + { + "{EDC2488A-8267-493A-A98E-7D9C3B36CDF3}:.NETFramework,Version=v4.7.2" + { + "Name" = "8:Microsoft .NET Framework 4.7.2 (x86 and x64)" + "ProductCode" = "8:.NETFramework,Version=v4.7.2" + } + } + } + } + } + "Deployable" + { + "CustomAction" + { + } + "DefaultFeature" + { + "Name" = "8:DefaultFeature" + "Title" = "8:" + "Description" = "8:" + } + "ExternalPersistence" + { + "LaunchCondition" + { + } + } + "File" + { + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_0045AD9C29CB49C39F9322ABDE701C11" + { + "SourcePath" = "8:..\\include\\c64\\kernalio.c" + "TargetName" = "8:kernalio.c" + "Tag" = "8:" + "Folder" = "8:_247D4CAD3CB843B3A8A4DC2D90F47C28" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_007FA3120B09491581F435FEE187EC9F" + { + "SourcePath" = "8:..\\samples\\hiresmc\\func3d.c" + "TargetName" = "8:func3d.c" + "Tag" = "8:" + "Folder" = "8:_3E74C69B2EF64E938EA60950128C5A74" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_035D8376B0CC41EA90DBB294D667A617" + { + "SourcePath" = "8:..\\samples\\rasterirq\\make.bat" + "TargetName" = "8:make.bat" + "Tag" = "8:" + "Folder" = "8:_E925E579BC89433389A5D5B048CAA36A" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_04ABABC55200450383686DD782DD1548" + { + "SourcePath" = "8:..\\samples\\games\\lander.c" + "TargetName" = "8:lander.c" + "Tag" = "8:" + "Folder" = "8:_BC04C0DDE264410096618981E4E890DA" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_05BBBE09BC7047B6AB2F10A2E9032E37" + { + "SourcePath" = "8:..\\samples\\kernalio\\hiresread.c" + "TargetName" = "8:hiresread.c" + "Tag" = "8:" + "Folder" = "8:_35EAA89057B0488590EC66D0D27D657A" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_06971480436546E1B12DDC7EC9F704FD" + { + "SourcePath" = "8:..\\samples\\resources\\mouse.spd" + "TargetName" = "8:mouse.spd" + "Tag" = "8:" + "Folder" = "8:_758C6547998745659548D0656D380FEA" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_071415C872DB4A94B28137BD7FE1F669" + { + "SourcePath" = "8:..\\include\\opp\\slab.h" + "TargetName" = "8:slab.h" + "Tag" = "8:" + "Folder" = "8:_D76A1802AFD04C48A519C9F8700E53CC" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_071CF34574024DBD95D2203D6E4279D0" + { + "SourcePath" = "8:..\\include\\opp\\iostream.h" + "TargetName" = "8:iostream.h" + "Tag" = "8:" + "Folder" = "8:_D76A1802AFD04C48A519C9F8700E53CC" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_0819C3D33D0942EF9E319C754E6C43D8" + { + "SourcePath" = "8:..\\include\\gfx\\bitmap.h" + "TargetName" = "8:bitmap.h" + "Tag" = "8:" + "Folder" = "8:_E0C7A8689A4144D0842BCEDA85A07B5D" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_08F70A83B36D428A9F0C9EFC69787FF1" + { + "SourcePath" = "8:..\\samples\\resources\\connect4sprites.bin" + "TargetName" = "8:connect4sprites.bin" + "Tag" = "8:" + "Folder" = "8:_758C6547998745659548D0656D380FEA" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_0CAC2197F4C34B33AFF5A3D0A7C78454" + { + "SourcePath" = "8:..\\samples\\kernalio\\hiresfload.c" + "TargetName" = "8:hiresfload.c" + "Tag" = "8:" + "Folder" = "8:_35EAA89057B0488590EC66D0D27D657A" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_0CC2E11595B34DF0A003DF534A5AADF5" + { + "SourcePath" = "8:..\\include\\audio\\sidfx.h" + "TargetName" = "8:sidfx.h" + "Tag" = "8:" + "Folder" = "8:_A4C8078BC7A74CCABB1050E80BC79950" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_0D15550C6DED4A3DB1301B4EC13A4320" + { + "SourcePath" = "8:..\\include\\vic20\\vic.h" + "TargetName" = "8:vic.h" + "Tag" = "8:" + "Folder" = "8:_042C172681BD4CC89F4C8AD83B406959" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_0D8B657E4A954DBFAF14055CDFFB384C" + { + "SourcePath" = "8:..\\include\\crt.c" + "TargetName" = "8:crt.c" + "Tag" = "8:" + "Folder" = "8:_7C0D28C244F14A21B5F72213BBE59B6F" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_0EE9160CA4D44278A3929B0A4E0789F8" + { + "SourcePath" = "8:..\\include\\opp\\vector.h" + "TargetName" = "8:vector.h" + "Tag" = "8:" + "Folder" = "8:_D76A1802AFD04C48A519C9F8700E53CC" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_1088B8AF518A4841A419B723AEB6C2BA" + { + "SourcePath" = "8:..\\samples\\fractals\\mbmulti3d.c" + "TargetName" = "8:mbmulti3d.c" + "Tag" = "8:" + "Folder" = "8:_BDDE04AB523C4059AABE326807998F7A" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_11003A1822704117BBF4B206A133EAD9" + { + "SourcePath" = "8:..\\include\\conio.h" + "TargetName" = "8:conio.h" + "Tag" = "8:" + "Folder" = "8:_7C0D28C244F14A21B5F72213BBE59B6F" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_12ACD00110E34BF9A3C06E00A16449D9" + { + "SourcePath" = "8:..\\include\\opp\\string.cpp" + "TargetName" = "8:string.cpp" + "Tag" = "8:" + "Folder" = "8:_D76A1802AFD04C48A519C9F8700E53CC" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_12C998C755E643038CDF5D9F56983ECA" + { + "SourcePath" = "8:..\\include\\fixmath.h" + "TargetName" = "8:fixmath.h" + "Tag" = "8:" + "Folder" = "8:_7C0D28C244F14A21B5F72213BBE59B6F" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_1328E18B9638422795616BABEAAFA600" + { + "SourcePath" = "8:..\\include\\opp\\functional.h" + "TargetName" = "8:functional.h" + "Tag" = "8:" + "Folder" = "8:_D76A1802AFD04C48A519C9F8700E53CC" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_1393003825B9451084DB314DD4267AD5" + { + "SourcePath" = "8:..\\samples\\memmap\\allmem.c" + "TargetName" = "8:allmem.c" + "Tag" = "8:" + "Folder" = "8:_A62A71A6A08941C5964B90112D87731F" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_16B8661A949341E3866A400D7B7FFC03" + { + "SourcePath" = "8:..\\samples\\fractals\\mbzoom.c" + "TargetName" = "8:mbzoom.c" + "Tag" = "8:" + "Folder" = "8:_BDDE04AB523C4059AABE326807998F7A" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_179A529C9A50404D8053B1204DBEE943" + { + "SourcePath" = "8:..\\samples\\hires\\splitscreen.c" + "TargetName" = "8:splitscreen.c" + "Tag" = "8:" + "Folder" = "8:_82FDD682B0334DE1B89A843D32862B85" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_19321EF0356047C9A6AEE738ECDEB9C3" + { + "SourcePath" = "8:..\\samples\\sprites\\sprmux32.c" + "TargetName" = "8:sprmux32.c" + "Tag" = "8:" + "Folder" = "8:_A891BBCA276B4516852A6BFD884353E3" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_1A605A19ADAD413CB754523D4FE7C32F" + { + "SourcePath" = "8:..\\include\\opp\\sstream.cpp" + "TargetName" = "8:sstream.cpp" + "Tag" = "8:" + "Folder" = "8:_D76A1802AFD04C48A519C9F8700E53CC" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_1AAB1F90101E40B6937A64FD4BF9D469" + { + "SourcePath" = "8:..\\include\\setjmp.c" + "TargetName" = "8:setjmp.c" + "Tag" = "8:" + "Folder" = "8:_7C0D28C244F14A21B5F72213BBE59B6F" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_1EB5CE4736F1417ABD479DFD92E19FD2" + { + "SourcePath" = "8:..\\include\\c64\\reu.c" + "TargetName" = "8:reu.c" + "Tag" = "8:" + "Folder" = "8:_247D4CAD3CB843B3A8A4DC2D90F47C28" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_1ED10FB93DDA4801BF72003E21B2CE55" + { + "SourcePath" = "8:..\\samples\\games\\missile.c" + "TargetName" = "8:missile.c" + "Tag" = "8:" + "Folder" = "8:_BC04C0DDE264410096618981E4E890DA" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_1F88FA4F35F043B3ABFCB552FCEA5CDD" + { + "SourcePath" = "8:..\\include\\c64\\rasterirq.c" + "TargetName" = "8:rasterirq.c" + "Tag" = "8:" + "Folder" = "8:_247D4CAD3CB843B3A8A4DC2D90F47C28" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_216D2A5FECF74C96A4964A74DFEB8552" + { + "SourcePath" = "8:..\\include\\stdbool.h" + "TargetName" = "8:stdbool.h" + "Tag" = "8:" + "Folder" = "8:_7C0D28C244F14A21B5F72213BBE59B6F" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_21C809DE4B19458FB97988B235805A95" + { + "SourcePath" = "8:..\\samples\\scrolling\\cgrid8way.c" + "TargetName" = "8:cgrid8way.c" + "Tag" = "8:" + "Folder" = "8:_AB905F99C1D249A080D01FB8FC118C9E" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_222FC9C47F124FE08B8D66C56A07DD94" + { + "SourcePath" = "8:..\\samples\\hires\\fractaltree.c" + "TargetName" = "8:fractaltree.c" + "Tag" = "8:" + "Folder" = "8:_82FDD682B0334DE1B89A843D32862B85" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_23211A34D7544532ABFD8D17B647717F" + { + "SourcePath" = "8:..\\samples\\memmap\\easyflashlow.c" + "TargetName" = "8:easyflashlow.c" + "Tag" = "8:" + "Folder" = "8:_A62A71A6A08941C5964B90112D87731F" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_24F2466589154E2DAA108C2EDB3BB432" + { + "SourcePath" = "8:..\\include\\oscar.c" + "TargetName" = "8:oscar.c" + "Tag" = "8:" + "Folder" = "8:_7C0D28C244F14A21B5F72213BBE59B6F" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_26E38AB4F85C4C9DB284803C4A3473F3" + { + "SourcePath" = "8:..\\include\\new" + "TargetName" = "8:new" + "Tag" = "8:" + "Folder" = "8:_7C0D28C244F14A21B5F72213BBE59B6F" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_28CED61D2559486D87F70EC821B89525" + { + "SourcePath" = "8:..\\include\\iso646.h" + "TargetName" = "8:iso646.h" + "Tag" = "8:" + "Folder" = "8:_7C0D28C244F14A21B5F72213BBE59B6F" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_29ED5D9B606D45DA80CFF329A7643DC8" + { + "SourcePath" = "8:..\\samples\\kernalio\\diskdir.c" + "TargetName" = "8:diskdir.c" + "Tag" = "8:" + "Folder" = "8:_35EAA89057B0488590EC66D0D27D657A" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_2C39AA436BFE4300AB4738C879E6E4F4" + { + "SourcePath" = "8:..\\samples\\hiresmc\\polygon.c" + "TargetName" = "8:polygon.c" + "Tag" = "8:" + "Folder" = "8:_3E74C69B2EF64E938EA60950128C5A74" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_2CC169DCBB0B4CC0BFE38AE63B285359" + { + "SourcePath" = "8:..\\include\\nes\\mmc1.c" + "TargetName" = "8:mmc1.c" + "Tag" = "8:" + "Folder" = "8:_9F35D1516FB34C30A2C5C1CA12501231" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_2CE4E742BFAC456C99ECA1DF0BFC5F34" + { + "SourcePath" = "8:..\\include\\c64\\flossiec.c" + "TargetName" = "8:flossiec.c" + "Tag" = "8:" + "Folder" = "8:_247D4CAD3CB843B3A8A4DC2D90F47C28" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_2D828D0247F144CDB0112B2AD4004C2C" + { + "SourcePath" = "8:..\\samples\\scrolling\\tunnel.c" + "TargetName" = "8:tunnel.c" + "Tag" = "8:" + "Folder" = "8:_AB905F99C1D249A080D01FB8FC118C9E" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_2DB9B0614EAC452A96802454D1BE5ACA" + { + "SourcePath" = "8:..\\include\\opp\\numeric.h" + "TargetName" = "8:numeric.h" + "Tag" = "8:" + "Folder" = "8:_D76A1802AFD04C48A519C9F8700E53CC" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_315C94A7D90D4CF5BF95F3B57482CADA" + { + "SourcePath" = "8:..\\include\\c128\\bank1.h" + "TargetName" = "8:bank1.h" + "Tag" = "8:" + "Folder" = "8:_0DAA950D275540D381CEA1427C33C8AA" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_317711E6E48744A18655469B4C53767E" + { + "SourcePath" = "8:..\\include\\math.c" + "TargetName" = "8:math.c" + "Tag" = "8:" + "Folder" = "8:_7C0D28C244F14A21B5F72213BBE59B6F" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_3277DE1463544F67B7E7390175F8A9CF" + { + "SourcePath" = "8:..\\samples\\rasterirq\\autocrawler.c" + "TargetName" = "8:autocrawler.c" + "Tag" = "8:" + "Folder" = "8:_E925E579BC89433389A5D5B048CAA36A" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_33E047EAEFFB45778C59472F157161DA" + { + "SourcePath" = "8:..\\include\\c64\\iecbus.h" + "TargetName" = "8:iecbus.h" + "Tag" = "8:" + "Folder" = "8:_247D4CAD3CB843B3A8A4DC2D90F47C28" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_343F58F80DF84D40AE23457288C5D7D5" + { + "SourcePath" = "8:..\\include\\c64\\keyboard.c" + "TargetName" = "8:keyboard.c" + "Tag" = "8:" + "Folder" = "8:_247D4CAD3CB843B3A8A4DC2D90F47C28" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_379EE3C17FEC4C5EA79D07668CD05FC4" + { + "SourcePath" = "8:..\\samples\\memmap\\easyflash.c" + "TargetName" = "8:easyflash.c" + "Tag" = "8:" + "Folder" = "8:_A62A71A6A08941C5964B90112D87731F" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_37F135A207264D5984B4B81EC44A4337" + { + "SourcePath" = "8:..\\samples\\hiresmc\\make.bat" + "TargetName" = "8:make.bat" + "Tag" = "8:" + "Folder" = "8:_3E74C69B2EF64E938EA60950128C5A74" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_38759DBF58B3475AA934C0DA70FB5B81" + { + "SourcePath" = "8:..\\samples\\hires\\polygon.c" + "TargetName" = "8:polygon.c" + "Tag" = "8:" + "Folder" = "8:_82FDD682B0334DE1B89A843D32862B85" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_3A3E6E45463E4839BDB96B1C7097DA8A" + { + "SourcePath" = "8:..\\samples\\kernalio\\hireswrite.c" + "TargetName" = "8:hireswrite.c" + "Tag" = "8:" + "Folder" = "8:_35EAA89057B0488590EC66D0D27D657A" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_3AFDC86156F04AABB8D82218C17005F2" + { + "SourcePath" = "8:..\\include\\c64\\vic.h" + "TargetName" = "8:vic.h" + "Tag" = "8:" + "Folder" = "8:_247D4CAD3CB843B3A8A4DC2D90F47C28" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_3C29D5D111D24946BEF556FBAB178506" + { + "SourcePath" = "8:..\\include\\c64\\cia.c" + "TargetName" = "8:cia.c" + "Tag" = "8:" + "Folder" = "8:_247D4CAD3CB843B3A8A4DC2D90F47C28" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_3FFD08277B804985BDF072C0C1877287" + { + "SourcePath" = "8:..\\include\\assert.c" + "TargetName" = "8:assert.c" + "Tag" = "8:" + "Folder" = "8:_7C0D28C244F14A21B5F72213BBE59B6F" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_41A6A127243E4EC2A1E1E2171993C5C1" + { + "SourcePath" = "8:..\\samples\\scrolling\\grid2d.c" + "TargetName" = "8:grid2d.c" + "Tag" = "8:" + "Folder" = "8:_AB905F99C1D249A080D01FB8FC118C9E" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_41AAEE312C4C4C5E80FFF8C311D70F2C" + { + "SourcePath" = "8:..\\samples\\rasterirq\\openborder.c" + "TargetName" = "8:openborder.c" + "Tag" = "8:" + "Folder" = "8:_E925E579BC89433389A5D5B048CAA36A" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_41C57237B9DA453DB84BBC1F1BE29E49" + { + "SourcePath" = "8:..\\samples\\rasterirq\\colorbars.c" + "TargetName" = "8:colorbars.c" + "Tag" = "8:" + "Folder" = "8:_E925E579BC89433389A5D5B048CAA36A" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_438F2450C997431898912DA2D4DFCEAE" + { + "SourcePath" = "8:..\\samples\\rasterirq\\movingbars.c" + "TargetName" = "8:movingbars.c" + "Tag" = "8:" + "Folder" = "8:_E925E579BC89433389A5D5B048CAA36A" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_43B55A11B25F4F13A9AE7E36B1AD2897" + { + "SourcePath" = "8:..\\samples\\resources\\blumba2.bin" + "TargetName" = "8:blumba2.bin" + "Tag" = "8:" + "Folder" = "8:_758C6547998745659548D0656D380FEA" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_44A56A2AD0C84900ACDDA218E57165D8" + { + "SourcePath" = "8:..\\samples\\fractals\\mbmulti.c" + "TargetName" = "8:mbmulti.c" + "Tag" = "8:" + "Folder" = "8:_BDDE04AB523C4059AABE326807998F7A" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_47A877D439EE429BAB64C52FEF69EDA4" + { + "SourcePath" = "8:..\\samples\\memmap\\largemem.c" + "TargetName" = "8:largemem.c" + "Tag" = "8:" + "Folder" = "8:_A62A71A6A08941C5964B90112D87731F" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_492AD1ED992D44F1A65903A5232C0426" + { + "SourcePath" = "8:..\\include\\nes\\mmc3.h" + "TargetName" = "8:mmc3.h" + "Tag" = "8:" + "Folder" = "8:_9F35D1516FB34C30A2C5C1CA12501231" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_494DB63135144419A8E0E6037E1B3237" + { + "SourcePath" = "8:..\\include\\c128\\bank1.c" + "TargetName" = "8:bank1.c" + "Tag" = "8:" + "Folder" = "8:_0DAA950D275540D381CEA1427C33C8AA" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_4A1182DE6E79476B9FE3EAE072252A90" + { + "SourcePath" = "8:..\\samples\\resources\\connect4chars.bin" + "TargetName" = "8:connect4chars.bin" + "Tag" = "8:" + "Folder" = "8:_758C6547998745659548D0656D380FEA" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_4B8FC526E6CC47FC8321D0191BFDBEDC" + { + "SourcePath" = "8:..\\samples\\resources\\scifiglyph.bin" + "TargetName" = "8:scifiglyph.bin" + "Tag" = "8:" + "Folder" = "8:_758C6547998745659548D0656D380FEA" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_4CAC4169D7CC4A2099042927E32B2184" + { + "SourcePath" = "8:..\\samples\\resources\\hscrollshmupsprites.bin" + "TargetName" = "8:hscrollshmupsprites.bin" + "Tag" = "8:" + "Folder" = "8:_758C6547998745659548D0656D380FEA" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_4D4981B8F95A4704A9E34C066DBD7563" + { + "SourcePath" = "8:..\\samples\\particles\\fireworks_ptr.c" + "TargetName" = "8:fireworks_ptr.c" + "Tag" = "8:" + "Folder" = "8:_8E34FB2717EA4D6583CFF4927C713690" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_4E9503F4F8F343739E4685C2066B3AD0" + { + "SourcePath" = "8:..\\include\\plus4\\ted.c" + "TargetName" = "8:ted.c" + "Tag" = "8:" + "Folder" = "8:_176312291F834CD18F492496EBCF9B7F" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_539501D7FC4941B8AEE943A2D5C74703" + { + "SourcePath" = "8:..\\samples\\games\\snake.c" + "TargetName" = "8:snake.c" + "Tag" = "8:" + "Folder" = "8:_BC04C0DDE264410096618981E4E890DA" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_542D20FAC6B34E318FA36AD50BF3EEBE" + { + "SourcePath" = "8:..\\include\\string.h" + "TargetName" = "8:string.h" + "Tag" = "8:" + "Folder" = "8:_7C0D28C244F14A21B5F72213BBE59B6F" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_557F7BC3AD794C4A93D5DFDA00024E1B" + { + "SourcePath" = "8:..\\samples\\memmap\\charsetexpand.c" + "TargetName" = "8:charsetexpand.c" + "Tag" = "8:" + "Folder" = "8:_A62A71A6A08941C5964B90112D87731F" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_5832EB4DC2134FF0BB7872666547CA0B" + { + "SourcePath" = "8:..\\samples\\sprites\\make.bat" + "TargetName" = "8:make.bat" + "Tag" = "8:" + "Folder" = "8:_A891BBCA276B4516852A6BFD884353E3" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_585B41257840451B8AF01582B8B032D1" + { + "SourcePath" = "8:..\\include\\petscii.h" + "TargetName" = "8:petscii.h" + "Tag" = "8:" + "Folder" = "8:_7C0D28C244F14A21B5F72213BBE59B6F" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_5A2F25CDE50B4AFAB0E0D5A3E787ABDD" + { + "SourcePath" = "8:..\\include\\c128\\mmu.h" + "TargetName" = "8:mmu.h" + "Tag" = "8:" + "Folder" = "8:_0DAA950D275540D381CEA1427C33C8AA" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_5ACC41BF427E47EBB9A4A66BD117FF80" + { + "SourcePath" = "8:..\\include\\gfx\\vector3d.h" + "TargetName" = "8:vector3d.h" + "Tag" = "8:" + "Folder" = "8:_E0C7A8689A4144D0842BCEDA85A07B5D" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_5C4D0F37C6784D198C3199B0610F60E5" + { + "SourcePath" = "8:..\\samples\\memmap\\easyflashcall.cpp" + "TargetName" = "8:easyflashcall.cpp" + "Tag" = "8:" + "Folder" = "8:_A62A71A6A08941C5964B90112D87731F" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_6094F07357CC41E39D165B0A81E5AD4C" + { + "SourcePath" = "8:..\\samples\\sprites\\joycontrol.c" + "TargetName" = "8:joycontrol.c" + "Tag" = "8:" + "Folder" = "8:_A891BBCA276B4516852A6BFD884353E3" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_610F946CE11C4867B87A581F0FFD9030" + { + "SourcePath" = "8:..\\samples\\resources\\ballsprite.bin" + "TargetName" = "8:ballsprite.bin" + "Tag" = "8:" + "Folder" = "8:_758C6547998745659548D0656D380FEA" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_636E67C8267143AA9FC128EB0AF85FC9" + { + "SourcePath" = "8:..\\include\\c64\\kernalio.h" + "TargetName" = "8:kernalio.h" + "Tag" = "8:" + "Folder" = "8:_247D4CAD3CB843B3A8A4DC2D90F47C28" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_63A827ECF1BC48CE96DB82B554FC323D" + { + "SourcePath" = "8:..\\include\\opp\\iostream.cpp" + "TargetName" = "8:iostream.cpp" + "Tag" = "8:" + "Folder" = "8:_D76A1802AFD04C48A519C9F8700E53CC" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_64C12019C1114C3282B32CD6B866C20A" + { + "SourcePath" = "8:..\\include\\c64\\reu.h" + "TargetName" = "8:reu.h" + "Tag" = "8:" + "Folder" = "8:_247D4CAD3CB843B3A8A4DC2D90F47C28" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_65286F9E8B2F496B95A50DA63A1F0D5B" + { + "SourcePath" = "8:..\\samples\\resources\\maze3dchars.bin" + "TargetName" = "8:maze3dchars.bin" + "Tag" = "8:" + "Folder" = "8:_758C6547998745659548D0656D380FEA" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_652D33F4F54F4974AA6C4CD23A323FD1" + { + "SourcePath" = "8:..\\samples\\resources\\hscrollshmuptiles.bin" + "TargetName" = "8:hscrollshmuptiles.bin" + "Tag" = "8:" + "Folder" = "8:_758C6547998745659548D0656D380FEA" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_67C8F824E02B4211B315AB32AB7ABBB1" + { + "SourcePath" = "8:..\\samples\\games\\breakout.c" + "TargetName" = "8:breakout.c" + "Tag" = "8:" + "Folder" = "8:_BC04C0DDE264410096618981E4E890DA" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_67F949223D154D92A9EC79A2A9CD1C97" + { + "SourcePath" = "8:..\\samples\\hires\\cube3d.c" + "TargetName" = "8:cube3d.c" + "Tag" = "8:" + "Folder" = "8:_82FDD682B0334DE1B89A843D32862B85" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_683E44581EBB4154841523B526E1B4B4" + { + "SourcePath" = "8:..\\samples\\memmap\\make.bat" + "TargetName" = "8:make.bat" + "Tag" = "8:" + "Folder" = "8:_A62A71A6A08941C5964B90112D87731F" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_6EE9325B01F74FFF8146A6AD7FE71A31" + { + "SourcePath" = "8:..\\include\\opp\\string.h" + "TargetName" = "8:string.h" + "Tag" = "8:" + "Folder" = "8:_D76A1802AFD04C48A519C9F8700E53CC" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_7127D91089DA4162BB8DC8EAB0C107F0" + { + "SourcePath" = "8:..\\include\\c64\\vic.c" + "TargetName" = "8:vic.c" + "Tag" = "8:" + "Folder" = "8:_247D4CAD3CB843B3A8A4DC2D90F47C28" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_71E85695DAA74E2297BA826A3D8A6B37" + { + "SourcePath" = "8:..\\samples\\resources\\digitsprites.bin" + "TargetName" = "8:digitsprites.bin" + "Tag" = "8:" + "Folder" = "8:_758C6547998745659548D0656D380FEA" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_72FE541170AB418781C6087271FD3D74" + { + "SourcePath" = "8:..\\include\\c64\\asm6502.c" + "TargetName" = "8:asm6502.c" + "Tag" = "8:" + "Folder" = "8:_247D4CAD3CB843B3A8A4DC2D90F47C28" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_7393641DDDD742BCA845F192181E1294" + { + "SourcePath" = "8:..\\samples\\games\\hscrollshmup.c" + "TargetName" = "8:hscrollshmup.c" + "Tag" = "8:" + "Folder" = "8:_BC04C0DDE264410096618981E4E890DA" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_749F54DFBD4D404DA9C2E2D5BA7CDDBF" + { + "SourcePath" = "8:..\\samples\\resources\\breakoutchars.bin" + "TargetName" = "8:breakoutchars.bin" + "Tag" = "8:" + "Folder" = "8:_758C6547998745659548D0656D380FEA" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_74E75A13AC624648B03EB53735D70DE0" + { + "SourcePath" = "8:..\\include\\opp\\span.h" + "TargetName" = "8:span.h" + "Tag" = "8:" + "Folder" = "8:_D76A1802AFD04C48A519C9F8700E53CC" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_76FEB96DC82F4EC681034B0A54620EAB" + { + "SourcePath" = "8:..\\include\\c128\\vdc.c" + "TargetName" = "8:vdc.c" + "Tag" = "8:" + "Folder" = "8:_0DAA950D275540D381CEA1427C33C8AA" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_7789738FE8854CE488C5248434E47211" + { + "SourcePath" = "8:..\\include\\opp\\ifstream.h" + "TargetName" = "8:ifstream.h" + "Tag" = "8:" + "Folder" = "8:_D76A1802AFD04C48A519C9F8700E53CC" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_79985361F09A43299E258E1A8E5ED934" + { + "SourcePath" = "8:..\\include\\gfx\\mcbitmap.c" + "TargetName" = "8:mcbitmap.c" + "Tag" = "8:" + "Folder" = "8:_E0C7A8689A4144D0842BCEDA85A07B5D" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_7A9E700FC438425580655F7C1BC07FD3" + { + "SourcePath" = "8:..\\samples\\rasterirq\\textcrawler.c" + "TargetName" = "8:textcrawler.c" + "Tag" = "8:" + "Folder" = "8:_E925E579BC89433389A5D5B048CAA36A" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_7AC94D519AF047EF853BD602790627C0" + { + "SourcePath" = "8:..\\include\\opp\\hashmap.h" + "TargetName" = "8:hashmap.h" + "Tag" = "8:" + "Folder" = "8:_D76A1802AFD04C48A519C9F8700E53CC" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_7B3ADAAB82E944EDBA72636B19969B4A" + { + "SourcePath" = "8:..\\include\\c64\\memmap.c" + "TargetName" = "8:memmap.c" + "Tag" = "8:" + "Folder" = "8:_247D4CAD3CB843B3A8A4DC2D90F47C28" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_7B4E6DFBFF944508821C3B54AE7E3D5E" + { + "SourcePath" = "8:..\\samples\\stdio\\helloworld.c" + "TargetName" = "8:helloworld.c" + "Tag" = "8:" + "Folder" = "8:_D1203ABC2E8B4CE188F6B34DC62EB03B" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_7DC360D088E84BB5AA7B338C464439A9" + { + "SourcePath" = "8:..\\samples\\games\\make.bat" + "TargetName" = "8:make.bat" + "Tag" = "8:" + "Folder" = "8:_BC04C0DDE264410096618981E4E890DA" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_7DD72563FCD1431C95C845C0C4370BC6" + { + "SourcePath" = "8:..\\include\\cx16\\vera.c" + "TargetName" = "8:vera.c" + "Tag" = "8:" + "Folder" = "8:_CF51FF683B01444CB933CD9573FBBDA9" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_7E6F12A2B7A647199FCBA7CBDF4E94D2" + { + "SourcePath" = "8:..\\samples\\kernalio\\filewrite.c" + "TargetName" = "8:filewrite.c" + "Tag" = "8:" + "Folder" = "8:_35EAA89057B0488590EC66D0D27D657A" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_7EA67552E0B34B9BA70152AD9E369EA6" + { + "SourcePath" = "8:..\\include\\c64\\joystick.h" + "TargetName" = "8:joystick.h" + "Tag" = "8:" + "Folder" = "8:_247D4CAD3CB843B3A8A4DC2D90F47C28" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_7FF22AC441314566AA43E02CA040F4BC" + { + "SourcePath" = "8:..\\samples\\fractals\\make.bat" + "TargetName" = "8:make.bat" + "Tag" = "8:" + "Folder" = "8:_BDDE04AB523C4059AABE326807998F7A" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_8004350ED41848E29D306A0A86DDA90E" + { + "SourcePath" = "8:..\\include\\nes\\nes.h" + "TargetName" = "8:nes.h" + "Tag" = "8:" + "Folder" = "8:_9F35D1516FB34C30A2C5C1CA12501231" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_814F6BFC9788449DB259F99386D39AFD" + { + "SourcePath" = "8:..\\include\\audio\\sidfx.c" + "TargetName" = "8:sidfx.c" + "Tag" = "8:" + "Folder" = "8:_A4C8078BC7A74CCABB1050E80BC79950" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_82C11E2A96FE49E399EEEAC2FEE9DCDC" + { + "SourcePath" = "8:..\\include\\opp\\list.h" + "TargetName" = "8:list.h" + "Tag" = "8:" + "Folder" = "8:_D76A1802AFD04C48A519C9F8700E53CC" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_85AF458C41734A099F879A01E6CEB35F" + { + "SourcePath" = "8:..\\include\\opp\\ofstream.cpp" + "TargetName" = "8:ofstream.cpp" + "Tag" = "8:" + "Folder" = "8:_D76A1802AFD04C48A519C9F8700E53CC" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_85CB18843FC0454FB3451EC86725D782" + { + "SourcePath" = "8:..\\include\\opp\\optional.h" + "TargetName" = "8:optional.h" + "Tag" = "8:" + "Folder" = "8:_D76A1802AFD04C48A519C9F8700E53CC" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_8827B6B07A1C4B32B08DF784E090381D" + { + "SourcePath" = "8:..\\samples\\memmap\\tsr.c" + "TargetName" = "8:tsr.c" + "Tag" = "8:" + "Folder" = "8:_A62A71A6A08941C5964B90112D87731F" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_89C3C9640CD142CCB3192AF3B15FC771" + { + "SourcePath" = "8:..\\samples\\kernalio\\make.bat" + "TargetName" = "8:make.bat" + "Tag" = "8:" + "Folder" = "8:_35EAA89057B0488590EC66D0D27D657A" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_8B258D238AC44D43A7DC654AFE0B9D8B" + { + "SourcePath" = "8:..\\include\\time.h" + "TargetName" = "8:time.h" + "Tag" = "8:" + "Folder" = "8:_7C0D28C244F14A21B5F72213BBE59B6F" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_8CCBAD6654AC4B3F829EB04F37BE3EB2" + { + "SourcePath" = "8:..\\samples\\memmap\\charsetlo.c" + "TargetName" = "8:charsetlo.c" + "Tag" = "8:" + "Folder" = "8:_A62A71A6A08941C5964B90112D87731F" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_8CE64311B70C454D866A7E0098455EB5" + { + "SourcePath" = "8:..\\samples\\sprites\\sprmux64.c" + "TargetName" = "8:sprmux64.c" + "Tag" = "8:" + "Folder" = "8:_A891BBCA276B4516852A6BFD884353E3" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_8E57961EDF0840A7A08877E8ED4826BC" + { + "SourcePath" = "8:..\\include\\c64\\mouse.h" + "TargetName" = "8:mouse.h" + "Tag" = "8:" + "Folder" = "8:_247D4CAD3CB843B3A8A4DC2D90F47C28" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_8EBEED55D3F142769DE0A0014758447D" + { + "SourcePath" = "8:..\\samples\\particles\\fireworks_hires.c" + "TargetName" = "8:fireworks_hires.c" + "Tag" = "8:" + "Folder" = "8:_8E34FB2717EA4D6583CFF4927C713690" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_90171C7FE48D426E8664B043F5E75D6B" + { + "SourcePath" = "8:..\\include\\c128\\mmu.c" + "TargetName" = "8:mmu.c" + "Tag" = "8:" + "Folder" = "8:_0DAA950D275540D381CEA1427C33C8AA" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_94AD9BFFA9D04AA0B6BC6EEA1D9A93ED" + { + "SourcePath" = "8:..\\bin\\oscar64.bat" + "TargetName" = "8:oscar64.bat" + "Tag" = "8:" + "Folder" = "8:_964EE09114664F92821A713CD4DD047B" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_95B2F42C70F1469C851D5751CC82319C" + { + "SourcePath" = "8:..\\samples\\resources\\missilechars.bin" + "TargetName" = "8:missilechars.bin" + "Tag" = "8:" + "Folder" = "8:_758C6547998745659548D0656D380FEA" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_963E94CDB4DF4300B48C537010EDF36F" + { + "SourcePath" = "8:..\\samples\\hires\\bitblit.c" + "TargetName" = "8:bitblit.c" + "Tag" = "8:" + "Folder" = "8:_82FDD682B0334DE1B89A843D32862B85" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_97FBAA1E30F140628447208D520BC191" + { + "SourcePath" = "8:..\\include\\gfx\\tinyfont.h" + "TargetName" = "8:tinyfont.h" + "Tag" = "8:" + "Folder" = "8:_E0C7A8689A4144D0842BCEDA85A07B5D" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_9893C97816744FFC9EC442940C79E58B" + { + "SourcePath" = "8:..\\include\\c64\\easyflash.h" + "TargetName" = "8:easyflash.h" + "Tag" = "8:" + "Folder" = "8:_247D4CAD3CB843B3A8A4DC2D90F47C28" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_98968728BFD9473FBCFD4E6A3B113A1F" + { + "SourcePath" = "8:..\\samples\\fractals\\mbhires.c" + "TargetName" = "8:mbhires.c" + "Tag" = "8:" + "Folder" = "8:_BDDE04AB523C4059AABE326807998F7A" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_98B942589E984AAD381F87F18DDB79D3" + { + "SourcePath" = "8:ucrtbased.dll" + "TargetName" = "8:ucrtbased.dll" + "Tag" = "8:" + "Folder" = "8:_607E75AF0E2A4CB9908C4C39DF8FE6E4" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:TRUE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_9B2E1BB3CD154CEFB214378A64B0B00C" + { + "SourcePath" = "8:..\\include\\plus4\\ted.h" + "TargetName" = "8:ted.h" + "Tag" = "8:" + "Folder" = "8:_176312291F834CD18F492496EBCF9B7F" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_9D0D7A63D6C848CD85489D6E7C43AFAD" + { + "SourcePath" = "8:..\\include\\stdio.h" + "TargetName" = "8:stdio.h" + "Tag" = "8:" + "Folder" = "8:_7C0D28C244F14A21B5F72213BBE59B6F" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_9D4472D86BDF48E387059DCB67990AA9" + { + "SourcePath" = "8:..\\samples\\resources\\breakoutsprites.bin" + "TargetName" = "8:breakoutsprites.bin" + "Tag" = "8:" + "Folder" = "8:_758C6547998745659548D0656D380FEA" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_9D5C03E361EC4766AF0204219047B6C8" + { + "SourcePath" = "8:..\\include\\c64\\sid.c" + "TargetName" = "8:sid.c" + "Tag" = "8:" + "Folder" = "8:_247D4CAD3CB843B3A8A4DC2D90F47C28" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_9E8E4DCF8AD041A9A9EA2716F82D36AF" + { + "SourcePath" = "8:..\\samples\\memmap\\overlaylzo.c" + "TargetName" = "8:overlaylzo.c" + "Tag" = "8:" + "Folder" = "8:_A62A71A6A08941C5964B90112D87731F" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_9EF4DA2EABDB4E3B864CCDEC851B7037" + { + "SourcePath" = "8:..\\include\\c64\\cia.h" + "TargetName" = "8:cia.h" + "Tag" = "8:" + "Folder" = "8:_247D4CAD3CB843B3A8A4DC2D90F47C28" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_A14BC161099E420286E6A1595596D0F0" + { + "SourcePath" = "8:..\\include\\opp\\ofstream.h" + "TargetName" = "8:ofstream.h" + "Tag" = "8:" + "Folder" = "8:_D76A1802AFD04C48A519C9F8700E53CC" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_A16BC2645A14DC0435B7119153B32DE8" + { + "SourcePath" = "8:VCRUNTIME140D.dll" + "TargetName" = "8:VCRUNTIME140D.dll" + "Tag" = "8:" + "Folder" = "8:_607E75AF0E2A4CB9908C4C39DF8FE6E4" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:TRUE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_A18504BA88CE40128ED44BD1854F0A33" + { + "SourcePath" = "8:..\\samples\\memmap\\easyflashreloc.c" + "TargetName" = "8:easyflashreloc.c" + "Tag" = "8:" + "Folder" = "8:_A62A71A6A08941C5964B90112D87731F" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_A27837E885F24200AC5CD5D9F8A0C2A4" + { + "SourcePath" = "8:..\\include\\conio.c" + "TargetName" = "8:conio.c" + "Tag" = "8:" + "Folder" = "8:_7C0D28C244F14A21B5F72213BBE59B6F" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_A284F8E444A340B986AAE126C7E5A387" + { + "SourcePath" = "8:..\\samples\\resources\\charset.bin" + "TargetName" = "8:charset.bin" + "Tag" = "8:" + "Folder" = "8:_758C6547998745659548D0656D380FEA" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_A32310DEFD4E41118CD7D36A8614C0D4" + { + "SourcePath" = "8:..\\include\\limits.h" + "TargetName" = "8:limits.h" + "Tag" = "8:" + "Folder" = "8:_7C0D28C244F14A21B5F72213BBE59B6F" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_A32AAE4931564357A3222F04C8D99C2F" + { + "SourcePath" = "8:..\\include\\c64\\keyboard.h" + "TargetName" = "8:keyboard.h" + "Tag" = "8:" + "Folder" = "8:_247D4CAD3CB843B3A8A4DC2D90F47C28" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_A566398810C1458E8E063A81FA88D46F" + { + "SourcePath" = "8:..\\include\\stddef.h" + "TargetName" = "8:stddef.h" + "Tag" = "8:" + "Folder" = "8:_7C0D28C244F14A21B5F72213BBE59B6F" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_A62BFFC051304130B85007261BC97749" + { + "SourcePath" = "8:..\\include\\opp\\sstream.h" + "TargetName" = "8:sstream.h" + "Tag" = "8:" + "Folder" = "8:_D76A1802AFD04C48A519C9F8700E53CC" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_A7796DF7A31E4292ADF6BECCF2604081" + { + "SourcePath" = "8:..\\include\\inttypes.c" + "TargetName" = "8:inttypes.c" + "Tag" = "8:" + "Folder" = "8:_7C0D28C244F14A21B5F72213BBE59B6F" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_A8820F3446B9408E88692E85817C1B07" + { + "SourcePath" = "8:..\\samples\\make.bat" + "TargetName" = "8:make.bat" + "Tag" = "8:" + "Folder" = "8:_7377423C9BC34CA8BF0B30F59C279053" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_A8F4F0F1A9F94DD2BE7D6582EF9F58A8" + { + "SourcePath" = "8:..\\include\\c64\\sprites.h" + "TargetName" = "8:sprites.h" + "Tag" = "8:" + "Folder" = "8:_247D4CAD3CB843B3A8A4DC2D90F47C28" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_AB5120799C684496B5E81A4212BD54D4" + { + "SourcePath" = "8:..\\samples\\memmap\\easyflashshared.c" + "TargetName" = "8:easyflashshared.c" + "Tag" = "8:" + "Folder" = "8:_A62A71A6A08941C5964B90112D87731F" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_AB90D0DCB4E044348BCCD9054215278F" + { + "SourcePath" = "8:..\\samples\\memmap\\charsetcopy.c" + "TargetName" = "8:charsetcopy.c" + "Tag" = "8:" + "Folder" = "8:_A62A71A6A08941C5964B90112D87731F" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_AE023AEA6C444140B19CD627DDC2CB87" + { + "SourcePath" = "8:..\\include\\cx16\\vera.h" + "TargetName" = "8:vera.h" + "Tag" = "8:" + "Folder" = "8:_CF51FF683B01444CB933CD9573FBBDA9" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_AEE4BA494D48426089DE7D870C4F399B" + { + "SourcePath" = "8:..\\samples\\scrolling\\make.bat" + "TargetName" = "8:make.bat" + "Tag" = "8:" + "Folder" = "8:_AB905F99C1D249A080D01FB8FC118C9E" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_B037CA4170834093B03F7067F7E2D5A5" + { + "SourcePath" = "8:..\\samples\\kernalio\\charwrite.c" + "TargetName" = "8:charwrite.c" + "Tag" = "8:" + "Folder" = "8:_35EAA89057B0488590EC66D0D27D657A" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_B0A61EA0BB804AEAB25D6C4B6F4C57E6" + { + "SourcePath" = "8:..\\include\\c64\\iecbus.c" + "TargetName" = "8:iecbus.c" + "Tag" = "8:" + "Folder" = "8:_247D4CAD3CB843B3A8A4DC2D90F47C28" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_B275D22D5B444CC0A8E44E63010B9759" + { + "SourcePath" = "8:..\\include\\nes\\nes.c" + "TargetName" = "8:nes.c" + "Tag" = "8:" + "Folder" = "8:_9F35D1516FB34C30A2C5C1CA12501231" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_B2F1B217D45A434DBA8EC21095F4D717" + { + "SourcePath" = "8:..\\samples\\memmap\\charsetload.c" + "TargetName" = "8:charsetload.c" + "Tag" = "8:" + "Folder" = "8:_A62A71A6A08941C5964B90112D87731F" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_B4265CBF352343D2867DBCCE67D9F493" + { + "SourcePath" = "8:..\\include\\c64\\joystick.c" + "TargetName" = "8:joystick.c" + "Tag" = "8:" + "Folder" = "8:_247D4CAD3CB843B3A8A4DC2D90F47C28" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_B59AF91A39B749DF9E6B95CD35C5C6AC" + { + "SourcePath" = "8:..\\samples\\resources\\hscrollshmupmap.bin" + "TargetName" = "8:hscrollshmupmap.bin" + "Tag" = "8:" + "Folder" = "8:_758C6547998745659548D0656D380FEA" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_B61CD152136A6E3CE5FF69D29E7A3F7B" + { + "SourcePath" = "8:VCRUNTIME140_1D.dll" + "TargetName" = "8:VCRUNTIME140_1D.dll" + "Tag" = "8:" + "Folder" = "8:_607E75AF0E2A4CB9908C4C39DF8FE6E4" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:TRUE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_B85448D515F0487FB6845119AE346F1D" + { + "SourcePath" = "8:..\\samples\\resources\\landersprites.bin" + "TargetName" = "8:landersprites.bin" + "Tag" = "8:" + "Folder" = "8:_758C6547998745659548D0656D380FEA" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_BA92710604A2499F8577F52758F3F529" + { + "SourcePath" = "8:..\\include\\fixmath.c" + "TargetName" = "8:fixmath.c" + "Tag" = "8:" + "Folder" = "8:_7C0D28C244F14A21B5F72213BBE59B6F" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_BAE9C5C261D54D06B5CBE68CDFA3809F" + { + "SourcePath" = "8:..\\include\\setjmp.h" + "TargetName" = "8:setjmp.h" + "Tag" = "8:" + "Folder" = "8:_7C0D28C244F14A21B5F72213BBE59B6F" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_BBECB7DB41D84A34B2837C5E7EC7FAD4" + { + "SourcePath" = "8:..\\samples\\hires\\make.bat" + "TargetName" = "8:make.bat" + "Tag" = "8:" + "Folder" = "8:_82FDD682B0334DE1B89A843D32862B85" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_BC5DE43AE7DE49AAA858A5CD2B985416" + { + "SourcePath" = "8:..\\samples\\hiresmc\\floodfill.c" + "TargetName" = "8:floodfill.c" + "Tag" = "8:" + "Folder" = "8:_3E74C69B2EF64E938EA60950128C5A74" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_BC6636B5CB534552BC23A19BCF4DD252" + { + "SourcePath" = "8:..\\include\\c64\\sprites.c" + "TargetName" = "8:sprites.c" + "Tag" = "8:" + "Folder" = "8:_247D4CAD3CB843B3A8A4DC2D90F47C28" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_BFAE73A1432845AB83CAA44F3A7EC9C3" + { + "SourcePath" = "8:..\\samples\\fractals\\mbfixed.c" + "TargetName" = "8:mbfixed.c" + "Tag" = "8:" + "Folder" = "8:_BDDE04AB523C4059AABE326807998F7A" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_C239EFB1C3A646809AD2740ABDE747B0" + { + "SourcePath" = "8:..\\include\\c64\\types.h" + "TargetName" = "8:types.h" + "Tag" = "8:" + "Folder" = "8:_247D4CAD3CB843B3A8A4DC2D90F47C28" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_C30DFFA5DE3D4FEC982E5EB08F63C55E" + { + "SourcePath" = "8:..\\include\\c64\\flossiec.h" + "TargetName" = "8:flossiec.h" + "Tag" = "8:" + "Folder" = "8:_247D4CAD3CB843B3A8A4DC2D90F47C28" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_C3C6C74DFAB942AA8BFC15885A2D9BD0" + { + "SourcePath" = "8:..\\include\\c64\\asm6502.h" + "TargetName" = "8:asm6502.h" + "Tag" = "8:" + "Folder" = "8:_247D4CAD3CB843B3A8A4DC2D90F47C28" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_C433B7556E0448C6A98F8357DEA96507" + { + "SourcePath" = "8:..\\include\\vic20\\vic.c" + "TargetName" = "8:vic.c" + "Tag" = "8:" + "Folder" = "8:_042C172681BD4CC89F4C8AD83B406959" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_C43DB5F721EA4B4A81507E9702E8FD4B" + { + "SourcePath" = "8:..\\samples\\fractals\\mbtext.c" + "TargetName" = "8:mbtext.c" + "Tag" = "8:" + "Folder" = "8:_BDDE04AB523C4059AABE326807998F7A" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_C4B1FC7B672E4E538E92FD06D90AE808" + { + "SourcePath" = "8:..\\samples\\sprites\\creditroll.c" + "TargetName" = "8:creditroll.c" + "Tag" = "8:" + "Folder" = "8:_A891BBCA276B4516852A6BFD884353E3" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_C6B8A6C354494A50BBFD7EE6B2FB39B6" + { + "SourcePath" = "8:..\\samples\\scrolling\\bigfont.c" + "TargetName" = "8:bigfont.c" + "Tag" = "8:" + "Folder" = "8:_AB905F99C1D249A080D01FB8FC118C9E" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_C6CAE25C0BCA43E58CF2062C2700F12F" + { + "SourcePath" = "8:..\\include\\inttypes.h" + "TargetName" = "8:inttypes.h" + "Tag" = "8:" + "Folder" = "8:_7C0D28C244F14A21B5F72213BBE59B6F" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_C72E9CB8EC154F9BA499FAC968B83A93" + { + "SourcePath" = "8:..\\include\\c64\\rasterirq.h" + "TargetName" = "8:rasterirq.h" + "Tag" = "8:" + "Folder" = "8:_247D4CAD3CB843B3A8A4DC2D90F47C28" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_C94FC3629B574FD3B4B86DADD8D77812" + { + "SourcePath" = "8:..\\include\\stdarg.h" + "TargetName" = "8:stdarg.h" + "Tag" = "8:" + "Folder" = "8:_7C0D28C244F14A21B5F72213BBE59B6F" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_CA8FE59769D748A5BF04ABC3F9154DBA" + { + "SourcePath" = "8:..\\include\\oscar.h" + "TargetName" = "8:oscar.h" + "Tag" = "8:" + "Folder" = "8:_7C0D28C244F14A21B5F72213BBE59B6F" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_CACD2FA2FFF3488AB6C5A0E5980B7805" + { + "SourcePath" = "8:..\\samples\\memmap\\magicdesk.c" + "TargetName" = "8:magicdesk.c" + "Tag" = "8:" + "Folder" = "8:_A62A71A6A08941C5964B90112D87731F" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_CC473A7B399D4B31A9DC8C94F7771EF7" + { + "SourcePath" = "8:..\\samples\\memmap\\charsethi.c" + "TargetName" = "8:charsethi.c" + "Tag" = "8:" + "Folder" = "8:_A62A71A6A08941C5964B90112D87731F" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_CCB9E57C55E9405DA3EBBF4E6131004A" + { + "SourcePath" = "8:..\\include\\c64\\sid.h" + "TargetName" = "8:sid.h" + "Tag" = "8:" + "Folder" = "8:_247D4CAD3CB843B3A8A4DC2D90F47C28" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_CD97BB2D0A3349FD86EE0058561FB8A7" + { + "SourcePath" = "8:..\\include\\c64\\charwin.c" + "TargetName" = "8:charwin.c" + "Tag" = "8:" + "Folder" = "8:_247D4CAD3CB843B3A8A4DC2D90F47C28" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_CDBE2B8738C843CBB146767FBEE9A828" + { + "SourcePath" = "8:..\\include\\nes\\mmc1.h" + "TargetName" = "8:mmc1.h" + "Tag" = "8:" + "Folder" = "8:_9F35D1516FB34C30A2C5C1CA12501231" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_CEB3122F595C457EA809182C29CB8B2A" + { + "SourcePath" = "8:..\\include\\opp\\boundint.h" + "TargetName" = "8:boundint.h" + "Tag" = "8:" + "Folder" = "8:_D76A1802AFD04C48A519C9F8700E53CC" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_D0E45B48D76B4407B0BDE4378C1DB2C7" + { + "SourcePath" = "8:..\\include\\stdlib.h" + "TargetName" = "8:stdlib.h" + "Tag" = "8:" + "Folder" = "8:_7C0D28C244F14A21B5F72213BBE59B6F" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_D16004065A954CC7A91F72185792B436" + { + "SourcePath" = "8:..\\include\\gfx\\vector3d.c" + "TargetName" = "8:vector3d.c" + "Tag" = "8:" + "Folder" = "8:_E0C7A8689A4144D0842BCEDA85A07B5D" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_D1EF53E7B029443F8EE2E8174F8201ED" + { + "SourcePath" = "8:..\\include\\gfx\\tinyfont.c" + "TargetName" = "8:tinyfont.c" + "Tag" = "8:" + "Folder" = "8:_E0C7A8689A4144D0842BCEDA85A07B5D" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_D2CCF52F2336415EAD896A2C5410D5E8" + { + "SourcePath" = "8:..\\samples\\resources\\hscrollshmupchars.bin" + "TargetName" = "8:hscrollshmupchars.bin" + "Tag" = "8:" + "Folder" = "8:_758C6547998745659548D0656D380FEA" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_D34AB6F4EE6C4E18A8C6D44CC8E23CB7" + { + "SourcePath" = "8:..\\samples\\games\\maze3d.c" + "TargetName" = "8:maze3d.c" + "Tag" = "8:" + "Folder" = "8:_BC04C0DDE264410096618981E4E890DA" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_D37E98655388482FB2C18CD585A68AE7" + { + "SourcePath" = "8:..\\samples\\hires\\qsort.c" + "TargetName" = "8:qsort.c" + "Tag" = "8:" + "Folder" = "8:_82FDD682B0334DE1B89A843D32862B85" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_D38EB85FE8E3492DA4BD8E6E4F7CEBEB" + { + "SourcePath" = "8:..\\include\\opp\\utility.h" + "TargetName" = "8:utility.h" + "Tag" = "8:" + "Folder" = "8:_D76A1802AFD04C48A519C9F8700E53CC" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_D52B954DFBCE4EB0932433DE7FCE9090" + { + "SourcePath" = "8:..\\samples\\hiresmc\\paint.c" + "TargetName" = "8:paint.c" + "Tag" = "8:" + "Folder" = "8:_3E74C69B2EF64E938EA60950128C5A74" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_D651A6B3342C4D1ABC7FE873E253A1C5" + { + "SourcePath" = "8:..\\include\\opp\\ifstream.cpp" + "TargetName" = "8:ifstream.cpp" + "Tag" = "8:" + "Folder" = "8:_D76A1802AFD04C48A519C9F8700E53CC" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_D76DA8C7D3964C93ABE193A7B53A0A9B" + { + "SourcePath" = "8:..\\samples\\stdio\\make.bat" + "TargetName" = "8:make.bat" + "Tag" = "8:" + "Folder" = "8:_D1203ABC2E8B4CE188F6B34DC62EB03B" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_D7FC57E3C7D54299AD14AE1BFBB2103C" + { + "SourcePath" = "8:..\\samples\\hires\\lines.c" + "TargetName" = "8:lines.c" + "Tag" = "8:" + "Folder" = "8:_82FDD682B0334DE1B89A843D32862B85" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_D82F590F7B264CCEBE12A9427BD4A6C9" + { + "SourcePath" = "8:..\\include\\ctype.h" + "TargetName" = "8:ctype.h" + "Tag" = "8:" + "Folder" = "8:_7C0D28C244F14A21B5F72213BBE59B6F" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_D9089340F2BF445B85880C886B30D7B7" + { + "SourcePath" = "8:..\\include\\c64\\mouse.c" + "TargetName" = "8:mouse.c" + "Tag" = "8:" + "Folder" = "8:_247D4CAD3CB843B3A8A4DC2D90F47C28" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_DA28A07E7836459C99161100D7C102B8" + { + "SourcePath" = "8:..\\include\\c64\\memmap.h" + "TargetName" = "8:memmap.h" + "Tag" = "8:" + "Folder" = "8:_247D4CAD3CB843B3A8A4DC2D90F47C28" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_DBCD7B3D63C34E31A882799F3C82F0E9" + { + "SourcePath" = "8:..\\include\\opp\\iterator.h" + "TargetName" = "8:iterator.h" + "Tag" = "8:" + "Folder" = "8:_D76A1802AFD04C48A519C9F8700E53CC" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_DE1058FFA9E149D1909C819592A12273" + { + "SourcePath" = "8:..\\samples\\particles\\fireworks_stripe.c" + "TargetName" = "8:fireworks_stripe.c" + "Tag" = "8:" + "Folder" = "8:_8E34FB2717EA4D6583CFF4927C713690" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_DEADBEA270134B77800770802B21859C" + { + "SourcePath" = "8:..\\samples\\games\\connectfour.c" + "TargetName" = "8:connectfour.c" + "Tag" = "8:" + "Folder" = "8:_BC04C0DDE264410096618981E4E890DA" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_E00658F953AC4735A6ED77408A74163D" + { + "SourcePath" = "8:..\\include\\c128\\vdc.h" + "TargetName" = "8:vdc.h" + "Tag" = "8:" + "Folder" = "8:_0DAA950D275540D381CEA1427C33C8AA" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_E11BDCFFB9944A1F893FE4EC86ACCD6C" + { + "SourcePath" = "8:..\\include\\opp\\algorithm.h" + "TargetName" = "8:algorithm.h" + "Tag" = "8:" + "Folder" = "8:_D76A1802AFD04C48A519C9F8700E53CC" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_E218D776D9014F99BE2B046AEF2D6E8B" + { + "SourcePath" = "8:..\\include\\stdlib.c" + "TargetName" = "8:stdlib.c" + "Tag" = "8:" + "Folder" = "8:_7C0D28C244F14A21B5F72213BBE59B6F" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_E34D6FBF8D3F40F3B7617DC34EFB31F0" + { + "SourcePath" = "8:..\\samples\\sprites\\multiplexer.c" + "TargetName" = "8:multiplexer.c" + "Tag" = "8:" + "Folder" = "8:_A891BBCA276B4516852A6BFD884353E3" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_E3FF6A8B315C439DA54DE18DB39FB3AD" + { + "SourcePath" = "8:..\\samples\\memmap\\overlay.c" + "TargetName" = "8:overlay.c" + "Tag" = "8:" + "Folder" = "8:_A62A71A6A08941C5964B90112D87731F" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_E4138658F1F544CF86255EDC1659936D" + { + "SourcePath" = "8:..\\samples\\kernalio\\charread.c" + "TargetName" = "8:charread.c" + "Tag" = "8:" + "Folder" = "8:_35EAA89057B0488590EC66D0D27D657A" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_E4F56F827D7644939B1EB5307A1DB263" + { + "SourcePath" = "8:..\\include\\string.c" + "TargetName" = "8:string.c" + "Tag" = "8:" + "Folder" = "8:_7C0D28C244F14A21B5F72213BBE59B6F" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_E5405BA024FB44FB8A71DC51B3B35428" + { + "SourcePath" = "8:..\\include\\stdint.h" + "TargetName" = "8:stdint.h" + "Tag" = "8:" + "Folder" = "8:_7C0D28C244F14A21B5F72213BBE59B6F" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_E54AFD61BAD0414C9C5B2EFF31DA01FA" + { + "SourcePath" = "8:..\\include\\assert.h" + "TargetName" = "8:assert.h" + "Tag" = "8:" + "Folder" = "8:_7C0D28C244F14A21B5F72213BBE59B6F" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_E58D098F58734A09BB721C2DB6E1D8A3" + { + "SourcePath" = "8:..\\samples\\hires\\func3d.c" + "TargetName" = "8:func3d.c" + "Tag" = "8:" + "Folder" = "8:_82FDD682B0334DE1B89A843D32862B85" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_E671F62994E148F1AD835BC530525D2C" + { + "SourcePath" = "8:..\\include\\crt.h" + "TargetName" = "8:crt.h" + "Tag" = "8:" + "Folder" = "8:_7C0D28C244F14A21B5F72213BBE59B6F" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_E87F2F9F79424FBBBD071EF633A81BB5" + { + "SourcePath" = "8:..\\include\\math.h" + "TargetName" = "8:math.h" + "Tag" = "8:" + "Folder" = "8:_7C0D28C244F14A21B5F72213BBE59B6F" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_EA3C0BCB01F2639DFA2E37EC8436E5F6" + { + "SourcePath" = "8:VERSION.dll" + "TargetName" = "8:VERSION.dll" + "Tag" = "8:" + "Folder" = "8:_607E75AF0E2A4CB9908C4C39DF8FE6E4" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:TRUE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_EC599856E7AE44F4A2F51AA76A4D044C" + { + "SourcePath" = "8:..\\include\\opp\\bidxlist.h" + "TargetName" = "8:bidxlist.h" + "Tag" = "8:" + "Folder" = "8:_D76A1802AFD04C48A519C9F8700E53CC" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_ED20DE3FDC8A400296E92AE0D37D8665" + { + "SourcePath" = "8:..\\include\\opp\\static_vector.h" + "TargetName" = "8:static_vector.h" + "Tag" = "8:" + "Folder" = "8:_D76A1802AFD04C48A519C9F8700E53CC" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_ED5FED616EA64689AB793E5D8BD3F13D" + { + "SourcePath" = "8:..\\include\\nes\\mmc3.c" + "TargetName" = "8:mmc3.c" + "Tag" = "8:" + "Folder" = "8:_9F35D1516FB34C30A2C5C1CA12501231" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_ED872D39D58443D590B7C80604BC0FF4" + { + "SourcePath" = "8:..\\samples\\kernalio\\fileread.c" + "TargetName" = "8:fileread.c" + "Tag" = "8:" + "Folder" = "8:_35EAA89057B0488590EC66D0D27D657A" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_EDDED74E9AC24D9C8C7811F3A20ACA53" + { + "SourcePath" = "8:..\\include\\stdio.c" + "TargetName" = "8:stdio.c" + "Tag" = "8:" + "Folder" = "8:_7C0D28C244F14A21B5F72213BBE59B6F" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_EF27132EAC3147A492E72EC29DD9A16D" + { + "SourcePath" = "8:..\\include\\gfx\\mcbitmap.h" + "TargetName" = "8:mcbitmap.h" + "Tag" = "8:" + "Folder" = "8:_E0C7A8689A4144D0842BCEDA85A07B5D" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_F06F7114D6184A03962BE1C8AA697FEF" + { + "SourcePath" = "8:..\\samples\\scrolling\\colorram.c" + "TargetName" = "8:colorram.c" + "Tag" = "8:" + "Folder" = "8:_AB905F99C1D249A080D01FB8FC118C9E" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_F1B639D383C846C2A7CCF8031E524A76" + { + "SourcePath" = "8:..\\samples\\resources\\missilesprites.bin" + "TargetName" = "8:missilesprites.bin" + "Tag" = "8:" + "Folder" = "8:_758C6547998745659548D0656D380FEA" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_F35970F9D8FA46B09F36D7E9DE5532CA" + { + "SourcePath" = "8:..\\include\\c64\\charwin.h" + "TargetName" = "8:charwin.h" + "Tag" = "8:" + "Folder" = "8:_247D4CAD3CB843B3A8A4DC2D90F47C28" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_F472CF5FA6CF43649B4CD8E5F204DA65" + { + "SourcePath" = "8:..\\include\\opp\\array.h" + "TargetName" = "8:array.h" + "Tag" = "8:" + "Folder" = "8:_D76A1802AFD04C48A519C9F8700E53CC" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_F715B9FA6E0048388F451AB4670FD1EB" + { + "SourcePath" = "8:..\\samples\\resources\\friendlybear.bin" + "TargetName" = "8:friendlybear.bin" + "Tag" = "8:" + "Folder" = "8:_758C6547998745659548D0656D380FEA" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_F8F279E2317342BB8F6FD09FE8FE4EF5" + { + "SourcePath" = "8:..\\include\\time.c" + "TargetName" = "8:time.c" + "Tag" = "8:" + "Folder" = "8:_7C0D28C244F14A21B5F72213BBE59B6F" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_FCF3696486DC430EA7BFACA8BE9731E7" + { + "SourcePath" = "8:..\\include\\ctype.c" + "TargetName" = "8:ctype.c" + "Tag" = "8:" + "Folder" = "8:_7C0D28C244F14A21B5F72213BBE59B6F" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_FD0B1CB15474472987407696041DEE5B" + { + "SourcePath" = "8:..\\samples\\particles\\make.bat" + "TargetName" = "8:make.bat" + "Tag" = "8:" + "Folder" = "8:_8E34FB2717EA4D6583CFF4927C713690" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_FDDF38BFE6BA48C88ED8CB7EADD94159" + { + "SourcePath" = "8:..\\include\\gfx\\bitmap.c" + "TargetName" = "8:bitmap.c" + "Tag" = "8:" + "Folder" = "8:_E0C7A8689A4144D0842BCEDA85A07B5D" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + } + "FileType" + { + } + "Folder" + { + "{1525181F-901A-416C-8A58-119130FE478E}:_1DA98350CBBD4BE0882910D585A91075" + { + "Name" = "8:#1916" + "AlwaysCreate" = "11:FALSE" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Property" = "8:DesktopFolder" + "Folders" + { + } + } + "{1525181F-901A-416C-8A58-119130FE478E}:_53E064FC52FA45EEB9D4EE3FEE7BED54" + { + "Name" = "8:#1919" + "AlwaysCreate" = "11:FALSE" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Property" = "8:ProgramMenuFolder" + "Folders" + { + } + } + "{1525181F-901A-416C-8A58-119130FE478E}:_83ED789BBD1946E98C723F58E42A70DB" + { + "Name" = "8:#1918" + "AlwaysCreate" = "11:FALSE" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Property" = "8:PersonalFolder" + "Folders" + { + "{9EF0B969-E518-4E46-987F-47570745A589}:_06D8A6B4C98F47E6B2B3C0C4E113E3D9" + { + "Name" = "8:oscar64" + "AlwaysCreate" = "11:FALSE" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Property" = "8:_22C99E3A9CCD42FEA45EF6D0EC6BF209" + "Folders" + { + "{9EF0B969-E518-4E46-987F-47570745A589}:_7377423C9BC34CA8BF0B30F59C279053" + { + "Name" = "8:samples" + "AlwaysCreate" = "11:FALSE" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Property" = "8:_D04348C622664C45A9E3C48F4DE8806E" + "Folders" + { + "{9EF0B969-E518-4E46-987F-47570745A589}:_35EAA89057B0488590EC66D0D27D657A" + { + "Name" = "8:kernalio" + "AlwaysCreate" = "11:FALSE" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Property" = "8:_7A2674E58ECB4124873F9D52AF821715" + "Folders" + { + } + } + "{9EF0B969-E518-4E46-987F-47570745A589}:_3E74C69B2EF64E938EA60950128C5A74" + { + "Name" = "8:hiresmc" + "AlwaysCreate" = "11:FALSE" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Property" = "8:_2C07A88E15EF43EE9F73E4D4B611EA5A" + "Folders" + { + } + } + "{9EF0B969-E518-4E46-987F-47570745A589}:_758C6547998745659548D0656D380FEA" + { + "Name" = "8:resources" + "AlwaysCreate" = "11:FALSE" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Property" = "8:_7952B74313E4429690121DD36755AB11" + "Folders" + { + } + } + "{9EF0B969-E518-4E46-987F-47570745A589}:_82FDD682B0334DE1B89A843D32862B85" + { + "Name" = "8:hires" + "AlwaysCreate" = "11:FALSE" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Property" = "8:_205D9EC486B644E6A2EB32677AD312E5" + "Folders" + { + } + } + "{9EF0B969-E518-4E46-987F-47570745A589}:_8E34FB2717EA4D6583CFF4927C713690" + { + "Name" = "8:particles" + "AlwaysCreate" = "11:FALSE" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Property" = "8:_647005059279431AB02852A772FCCE4D" + "Folders" + { + } + } + "{9EF0B969-E518-4E46-987F-47570745A589}:_A62A71A6A08941C5964B90112D87731F" + { + "Name" = "8:memmap" + "AlwaysCreate" = "11:FALSE" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Property" = "8:_76673EF1E5E34C58AD58E3AFE57CE034" + "Folders" + { + } + } + "{9EF0B969-E518-4E46-987F-47570745A589}:_A891BBCA276B4516852A6BFD884353E3" + { + "Name" = "8:sprites" + "AlwaysCreate" = "11:FALSE" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Property" = "8:_C6220364E13B4D8292B2622DD2C00937" + "Folders" + { + } + } + "{9EF0B969-E518-4E46-987F-47570745A589}:_AB905F99C1D249A080D01FB8FC118C9E" + { + "Name" = "8:scrolling" + "AlwaysCreate" = "11:FALSE" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Property" = "8:_0E38AE25502048969B0438595134ED6E" + "Folders" + { + } + } + "{9EF0B969-E518-4E46-987F-47570745A589}:_BC04C0DDE264410096618981E4E890DA" + { + "Name" = "8:games" + "AlwaysCreate" = "11:FALSE" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Property" = "8:_2FC18ABE685C4EC1A2E232741074C931" + "Folders" + { + } + } + "{9EF0B969-E518-4E46-987F-47570745A589}:_BDDE04AB523C4059AABE326807998F7A" + { + "Name" = "8:fractals" + "AlwaysCreate" = "11:FALSE" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Property" = "8:_6690A4A2351145369394F37F4BED6717" + "Folders" + { + } + } + "{9EF0B969-E518-4E46-987F-47570745A589}:_D1203ABC2E8B4CE188F6B34DC62EB03B" + { + "Name" = "8:stdio" + "AlwaysCreate" = "11:FALSE" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Property" = "8:_3A46A653D56A47BDB4DCB0A9D11A077C" + "Folders" + { + } + } + "{9EF0B969-E518-4E46-987F-47570745A589}:_E925E579BC89433389A5D5B048CAA36A" + { + "Name" = "8:rasterirq" + "AlwaysCreate" = "11:FALSE" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Property" = "8:_1E69B82C7B074C2398A67D0271B4C2D9" + "Folders" + { + } + } + } + } + "{9EF0B969-E518-4E46-987F-47570745A589}:_964EE09114664F92821A713CD4DD047B" + { + "Name" = "8:bin" + "AlwaysCreate" = "11:FALSE" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Property" = "8:_F3D0C704B65348008AA46A143C359B59" + "Folders" + { + } + } + } + } + } + } + "{3C67513D-01DD-4637-8A68-80971EB9504F}:_C95D3F098F884652A04D707B55B980EE" + { + "DefaultLocation" = "8:[ProgramFiles64Folder][ProductName]" + "Name" = "8:#1925" + "AlwaysCreate" = "11:FALSE" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Property" = "8:TARGETDIR" + "Folders" + { + "{9EF0B969-E518-4E46-987F-47570745A589}:_607E75AF0E2A4CB9908C4C39DF8FE6E4" + { + "Name" = "8:bin" + "AlwaysCreate" = "11:FALSE" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Property" = "8:_210EBA1A0EC145268D1720AECA0F9358" + "Folders" + { + } + } + "{9EF0B969-E518-4E46-987F-47570745A589}:_7C0D28C244F14A21B5F72213BBE59B6F" + { + "Name" = "8:include" + "AlwaysCreate" = "11:FALSE" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Property" = "8:_6AA93D53766B4EE0953D25E157222875" + "Folders" + { + "{9EF0B969-E518-4E46-987F-47570745A589}:_042C172681BD4CC89F4C8AD83B406959" + { + "Name" = "8:vic20" + "AlwaysCreate" = "11:FALSE" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Property" = "8:_EE2F6B12EB07400C8843B7F4C5B49C52" + "Folders" + { + } + } + "{9EF0B969-E518-4E46-987F-47570745A589}:_0DAA950D275540D381CEA1427C33C8AA" + { + "Name" = "8:c128" + "AlwaysCreate" = "11:FALSE" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Property" = "8:_A41957B106C34BD6B545B6AF90430469" + "Folders" + { + } + } + "{9EF0B969-E518-4E46-987F-47570745A589}:_176312291F834CD18F492496EBCF9B7F" + { + "Name" = "8:plus4" + "AlwaysCreate" = "11:FALSE" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Property" = "8:_3E40B4176706486A92CFA4B195216DBF" + "Folders" + { + } + } + "{9EF0B969-E518-4E46-987F-47570745A589}:_247D4CAD3CB843B3A8A4DC2D90F47C28" + { + "Name" = "8:c64" + "AlwaysCreate" = "11:FALSE" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Property" = "8:_6E28C9C364374358A6470A35DBE3C7C3" + "Folders" + { + } + } + "{9EF0B969-E518-4E46-987F-47570745A589}:_9F35D1516FB34C30A2C5C1CA12501231" + { + "Name" = "8:nes" + "AlwaysCreate" = "11:FALSE" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Property" = "8:_2D1C556D213E4762AB26C7A8451860A2" + "Folders" + { + } + } + "{9EF0B969-E518-4E46-987F-47570745A589}:_A4C8078BC7A74CCABB1050E80BC79950" + { + "Name" = "8:audio" + "AlwaysCreate" = "11:FALSE" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Property" = "8:_D9D33FD9864B40ECADCC2496B1BA18F8" + "Folders" + { + } + } + "{9EF0B969-E518-4E46-987F-47570745A589}:_CF51FF683B01444CB933CD9573FBBDA9" + { + "Name" = "8:cx16" + "AlwaysCreate" = "11:FALSE" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Property" = "8:_E9B59201726C4801B40E69CE1E618E78" + "Folders" + { + } + } + "{9EF0B969-E518-4E46-987F-47570745A589}:_D76A1802AFD04C48A519C9F8700E53CC" + { + "Name" = "8:opp" + "AlwaysCreate" = "11:FALSE" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Property" = "8:_36F6D5A5C23349D190D89E25E1F78C22" + "Folders" + { + } + } + "{9EF0B969-E518-4E46-987F-47570745A589}:_E0C7A8689A4144D0842BCEDA85A07B5D" + { + "Name" = "8:gfx" + "AlwaysCreate" = "11:FALSE" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Property" = "8:_1113C161CA574BEF91886C572F021DFA" + "Folders" + { + } + } + } + } + } + } + } + "LaunchCondition" + { + } + "Locator" + { + } + "MsiBootstrapper" + { + "LangId" = "3:1033" + "RequiresElevation" = "11:FALSE" + } + "Product" + { + "Name" = "8:Microsoft Visual Studio" + "ProductName" = "8:oscar64" + "ProductCode" = "8:{4E48E034-B689-4FCA-9B5A-DDFF4806155C}" + "PackageCode" = "8:{83435F3D-F50D-450E-90FB-B351EC154573}" + "UpgradeCode" = "8:{9AB61EFF-ACAC-4079-9950-8D96615CD4EF}" + "AspNetVersion" = "8:2.0.50727.0" + "RestartWWWService" = "11:FALSE" + "RemovePreviousVersions" = "11:TRUE" + "DetectNewerInstalledVersion" = "11:TRUE" + "InstallAllUsers" = "11:FALSE" + "ProductVersion" = "8:1.32.272" + "Manufacturer" = "8:oscar64" + "ARPHELPTELEPHONE" = "8:" + "ARPHELPLINK" = "8:" + "Title" = "8:oscar64setup" + "Subject" = "8:" + "ARPCONTACT" = "8:drmortalwombat" + "Keywords" = "8:" + "ARPCOMMENTS" = "8:" + "ARPURLINFOABOUT" = "8:" + "ARPPRODUCTICON" = "8:" + "ARPIconIndex" = "3:0" + "SearchPath" = "8:" + "UseSystemSearchPath" = "11:TRUE" + "TargetPlatform" = "3:1" + "PreBuildEvent" = "8:" + "PostBuildEvent" = "8:" + "RunPostBuildEvent" = "3:0" + } + "Registry" + { + "HKLM" + { + "Keys" + { + "{60EA8692-D2D5-43EB-80DC-7906BF13D6EF}:_BE8B749DF81B42A2BE3DD63972B124BE" + { + "Name" = "8:Software" + "Condition" = "8:" + "AlwaysCreate" = "11:FALSE" + "DeleteAtUninstall" = "11:FALSE" + "Transitive" = "11:FALSE" + "Keys" + { + "{60EA8692-D2D5-43EB-80DC-7906BF13D6EF}:_555EF5AC0E714F4EB07C07C1C75F6D88" + { + "Name" = "8:[Manufacturer]" + "Condition" = "8:" + "AlwaysCreate" = "11:FALSE" + "DeleteAtUninstall" = "11:FALSE" + "Transitive" = "11:FALSE" + "Keys" + { + } + "Values" + { + } + } + } + "Values" + { + } + } + } + } + "HKCU" + { + "Keys" + { + "{60EA8692-D2D5-43EB-80DC-7906BF13D6EF}:_4BC94361FDFD4603AE2DB6796FBBA0AB" + { + "Name" = "8:Software" + "Condition" = "8:" + "AlwaysCreate" = "11:FALSE" + "DeleteAtUninstall" = "11:FALSE" + "Transitive" = "11:FALSE" + "Keys" + { + "{60EA8692-D2D5-43EB-80DC-7906BF13D6EF}:_DFF27B28C7D04B1B8FF6CB69EF030F6F" + { + "Name" = "8:[Manufacturer]" + "Condition" = "8:" + "AlwaysCreate" = "11:FALSE" + "DeleteAtUninstall" = "11:FALSE" + "Transitive" = "11:FALSE" + "Keys" + { + } + "Values" + { + } + } + } + "Values" + { + } + } + } + } + "HKCR" + { + "Keys" + { + } + } + "HKU" + { + "Keys" + { + } + } + "HKPU" + { + "Keys" + { + } + } + } + "Sequences" + { + } + "Shortcut" + { + } + "UserInterface" + { + "{2479F3F5-0309-486D-8047-8187E2CE5BA0}:_059C525532334A4EA2ABF6065A1A1FEE" + { + "UseDynamicProperties" = "11:FALSE" + "IsDependency" = "11:FALSE" + "SourcePath" = "8:\\VsdBasicDialogs.wim" + } + "{DF760B10-853B-4699-99F2-AFF7185B4A62}:_3161DB92793C449A872E2C604DEC6726" + { + "Name" = "8:#1900" + "Sequence" = "3:2" + "Attributes" = "3:1" + "Dialogs" + { + "{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_22A815F54AFC4CBD9D81C79DB17BB57D" + { + "Sequence" = "3:300" + "DisplayName" = "8:Confirm Installation" + "UseDynamicProperties" = "11:TRUE" + "IsDependency" = "11:FALSE" + "SourcePath" = "8:\\VsdAdminConfirmDlg.wid" + "Properties" + { + "BannerBitmap" + { + "Name" = "8:BannerBitmap" + "DisplayName" = "8:#1001" + "Description" = "8:#1101" + "Type" = "3:8" + "ContextData" = "8:Bitmap" + "Attributes" = "3:4" + "Setting" = "3:1" + "UsePlugInResources" = "11:TRUE" + } + } + } + "{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_41DF5ED83EA54AF29742B64EDBFE2801" + { + "Sequence" = "3:200" + "DisplayName" = "8:Installation Folder" + "UseDynamicProperties" = "11:TRUE" + "IsDependency" = "11:FALSE" + "SourcePath" = "8:\\VsdAdminFolderDlg.wid" + "Properties" + { + "BannerBitmap" + { + "Name" = "8:BannerBitmap" + "DisplayName" = "8:#1001" + "Description" = "8:#1101" + "Type" = "3:8" + "ContextData" = "8:Bitmap" + "Attributes" = "3:4" + "Setting" = "3:1" + "UsePlugInResources" = "11:TRUE" + } + } + } + "{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_CB85A9956DDD4404B2D7FFF372F38144" + { + "Sequence" = "3:100" + "DisplayName" = "8:Welcome" + "UseDynamicProperties" = "11:TRUE" + "IsDependency" = "11:FALSE" + "SourcePath" = "8:\\VsdAdminWelcomeDlg.wid" + "Properties" + { + "BannerBitmap" + { + "Name" = "8:BannerBitmap" + "DisplayName" = "8:#1001" + "Description" = "8:#1101" + "Type" = "3:8" + "ContextData" = "8:Bitmap" + "Attributes" = "3:4" + "Setting" = "3:1" + "UsePlugInResources" = "11:TRUE" + } + "CopyrightWarning" + { + "Name" = "8:CopyrightWarning" + "DisplayName" = "8:#1002" + "Description" = "8:#1102" + "Type" = "3:3" + "ContextData" = "8:" + "Attributes" = "3:0" + "Setting" = "3:1" + "Value" = "8:#1202" + "DefaultValue" = "8:#1202" + "UsePlugInResources" = "11:TRUE" + } + "Welcome" + { + "Name" = "8:Welcome" + "DisplayName" = "8:#1003" + "Description" = "8:#1103" + "Type" = "3:3" + "ContextData" = "8:" + "Attributes" = "3:0" + "Setting" = "3:1" + "Value" = "8:#1203" + "DefaultValue" = "8:#1203" + "UsePlugInResources" = "11:TRUE" + } + } + } + } + } + "{DF760B10-853B-4699-99F2-AFF7185B4A62}:_6BA6256C7E96485A9C5360AB352FB97D" + { + "Name" = "8:#1901" + "Sequence" = "3:2" + "Attributes" = "3:2" + "Dialogs" + { + "{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_C921198DBC0B415D931D0E64BDB86CB3" + { + "Sequence" = "3:100" + "DisplayName" = "8:Progress" + "UseDynamicProperties" = "11:TRUE" + "IsDependency" = "11:FALSE" + "SourcePath" = "8:\\VsdAdminProgressDlg.wid" + "Properties" + { + "BannerBitmap" + { + "Name" = "8:BannerBitmap" + "DisplayName" = "8:#1001" + "Description" = "8:#1101" + "Type" = "3:8" + "ContextData" = "8:Bitmap" + "Attributes" = "3:4" + "Setting" = "3:1" + "UsePlugInResources" = "11:TRUE" + } + "ShowProgress" + { + "Name" = "8:ShowProgress" + "DisplayName" = "8:#1009" + "Description" = "8:#1109" + "Type" = "3:5" + "ContextData" = "8:1;True=1;False=0" + "Attributes" = "3:0" + "Setting" = "3:0" + "Value" = "3:1" + "DefaultValue" = "3:1" + "UsePlugInResources" = "11:TRUE" + } + } + } + } + } + "{DF760B10-853B-4699-99F2-AFF7185B4A62}:_96B7FABC141B460D99F4554568634198" + { + "Name" = "8:#1902" + "Sequence" = "3:2" + "Attributes" = "3:3" + "Dialogs" + { + "{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_3C16AF72B1844B8283F7CC09E9A5689C" + { + "Sequence" = "3:100" + "DisplayName" = "8:Finished" + "UseDynamicProperties" = "11:TRUE" + "IsDependency" = "11:FALSE" + "SourcePath" = "8:\\VsdAdminFinishedDlg.wid" + "Properties" + { + "BannerBitmap" + { + "Name" = "8:BannerBitmap" + "DisplayName" = "8:#1001" + "Description" = "8:#1101" + "Type" = "3:8" + "ContextData" = "8:Bitmap" + "Attributes" = "3:4" + "Setting" = "3:1" + "UsePlugInResources" = "11:TRUE" + } + } + } + } + } + "{DF760B10-853B-4699-99F2-AFF7185B4A62}:_A17BD471F1A843CBB743A8F25DFA53ED" + { + "Name" = "8:#1902" + "Sequence" = "3:1" + "Attributes" = "3:3" + "Dialogs" + { + "{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_EFE3AD407A394F1FB24D87B275E06829" + { + "Sequence" = "3:100" + "DisplayName" = "8:Finished" + "UseDynamicProperties" = "11:TRUE" + "IsDependency" = "11:FALSE" + "SourcePath" = "8:\\VsdFinishedDlg.wid" + "Properties" + { + "BannerBitmap" + { + "Name" = "8:BannerBitmap" + "DisplayName" = "8:#1001" + "Description" = "8:#1101" + "Type" = "3:8" + "ContextData" = "8:Bitmap" + "Attributes" = "3:4" + "Setting" = "3:1" + "UsePlugInResources" = "11:TRUE" + } + "UpdateText" + { + "Name" = "8:UpdateText" + "DisplayName" = "8:#1058" + "Description" = "8:#1158" + "Type" = "3:15" + "ContextData" = "8:" + "Attributes" = "3:0" + "Setting" = "3:1" + "Value" = "8:#1258" + "DefaultValue" = "8:#1258" + "UsePlugInResources" = "11:TRUE" + } + } + } + } + } + "{DF760B10-853B-4699-99F2-AFF7185B4A62}:_B78CA3EC175C40DBAB3EE6CF6B2CDF21" + { + "Name" = "8:#1901" + "Sequence" = "3:1" + "Attributes" = "3:2" + "Dialogs" + { + "{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_CF3BFA457E25443CA029CFA4AC8B1362" + { + "Sequence" = "3:100" + "DisplayName" = "8:Progress" + "UseDynamicProperties" = "11:TRUE" + "IsDependency" = "11:FALSE" + "SourcePath" = "8:\\VsdProgressDlg.wid" + "Properties" + { + "BannerBitmap" + { + "Name" = "8:BannerBitmap" + "DisplayName" = "8:#1001" + "Description" = "8:#1101" + "Type" = "3:8" + "ContextData" = "8:Bitmap" + "Attributes" = "3:4" + "Setting" = "3:1" + "UsePlugInResources" = "11:TRUE" + } + "ShowProgress" + { + "Name" = "8:ShowProgress" + "DisplayName" = "8:#1009" + "Description" = "8:#1109" + "Type" = "3:5" + "ContextData" = "8:1;True=1;False=0" + "Attributes" = "3:0" + "Setting" = "3:0" + "Value" = "3:1" + "DefaultValue" = "3:1" + "UsePlugInResources" = "11:TRUE" + } + } + } + } + } + "{2479F3F5-0309-486D-8047-8187E2CE5BA0}:_EB8F638EBC0F47928718AE6304D131C4" + { + "UseDynamicProperties" = "11:FALSE" + "IsDependency" = "11:FALSE" + "SourcePath" = "8:\\VsdUserInterface.wim" + } + "{DF760B10-853B-4699-99F2-AFF7185B4A62}:_F269F9CF8EA34A0DBFEB26DDF0903B03" + { + "Name" = "8:#1900" + "Sequence" = "3:1" + "Attributes" = "3:1" + "Dialogs" + { + "{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_B9F8FBF597A4408CA849022BDFEF03A5" + { + "Sequence" = "3:200" + "DisplayName" = "8:Installation Folder" + "UseDynamicProperties" = "11:TRUE" + "IsDependency" = "11:FALSE" + "SourcePath" = "8:\\VsdFolderDlg.wid" + "Properties" + { + "BannerBitmap" + { + "Name" = "8:BannerBitmap" + "DisplayName" = "8:#1001" + "Description" = "8:#1101" + "Type" = "3:8" + "ContextData" = "8:Bitmap" + "Attributes" = "3:4" + "Setting" = "3:1" + "UsePlugInResources" = "11:TRUE" + } + "InstallAllUsersVisible" + { + "Name" = "8:InstallAllUsersVisible" + "DisplayName" = "8:#1059" + "Description" = "8:#1159" + "Type" = "3:5" + "ContextData" = "8:1;True=1;False=0" + "Attributes" = "3:0" + "Setting" = "3:0" + "Value" = "3:1" + "DefaultValue" = "3:1" + "UsePlugInResources" = "11:TRUE" + } + } + } + "{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_DD1D9320821E4A04B1BE2A87B2C8C642" + { + "Sequence" = "3:300" + "DisplayName" = "8:Confirm Installation" + "UseDynamicProperties" = "11:TRUE" + "IsDependency" = "11:FALSE" + "SourcePath" = "8:\\VsdConfirmDlg.wid" + "Properties" + { + "BannerBitmap" + { + "Name" = "8:BannerBitmap" + "DisplayName" = "8:#1001" + "Description" = "8:#1101" + "Type" = "3:8" + "ContextData" = "8:Bitmap" + "Attributes" = "3:4" + "Setting" = "3:1" + "UsePlugInResources" = "11:TRUE" + } + } + } + "{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_F921F483DB2249D88F2CEAEED72C355B" + { + "Sequence" = "3:100" + "DisplayName" = "8:Welcome" + "UseDynamicProperties" = "11:TRUE" + "IsDependency" = "11:FALSE" + "SourcePath" = "8:\\VsdWelcomeDlg.wid" + "Properties" + { + "BannerBitmap" + { + "Name" = "8:BannerBitmap" + "DisplayName" = "8:#1001" + "Description" = "8:#1101" + "Type" = "3:8" + "ContextData" = "8:Bitmap" + "Attributes" = "3:4" + "Setting" = "3:1" + "UsePlugInResources" = "11:TRUE" + } + "CopyrightWarning" + { + "Name" = "8:CopyrightWarning" + "DisplayName" = "8:#1002" + "Description" = "8:#1102" + "Type" = "3:3" + "ContextData" = "8:" + "Attributes" = "3:0" + "Setting" = "3:1" + "Value" = "8:#1202" + "DefaultValue" = "8:#1202" + "UsePlugInResources" = "11:TRUE" + } + "Welcome" + { + "Name" = "8:Welcome" + "DisplayName" = "8:#1003" + "Description" = "8:#1103" + "Type" = "3:3" + "ContextData" = "8:" + "Attributes" = "3:0" + "Setting" = "3:1" + "Value" = "8:#1203" + "DefaultValue" = "8:#1203" + "UsePlugInResources" = "11:TRUE" + } + } + } + } + } + } + "MergeModule" + { + } + "ProjectOutput" + { + "{5259A561-127C-4D43-A0A1-72F10C7B3BF8}:_FB2E467BC172457785F4279BB0BFE8B6" + { + "SourcePath" = "8:..\\Releasex64\\oscar64.exe" + "TargetName" = "8:" + "Tag" = "8:" + "Folder" = "8:_607E75AF0E2A4CB9908C4C39DF8FE6E4" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + "ProjectOutputGroupRegister" = "3:1" + "OutputConfiguration" = "8:" + "OutputGroupCanonicalName" = "8:Built" + "OutputProjectGuid" = "8:{1DBC623E-6109-41FE-B1BB-9B43FC984F7D}" + "ShowKeyOutput" = "11:TRUE" + "ExcludeFilters" + { + } + } + } + } +} diff --git a/oscar64/samples/build.sh b/oscar64/samples/build.sh new file mode 100755 index 0000000..1f93031 --- /dev/null +++ b/oscar64/samples/build.sh @@ -0,0 +1,24 @@ +#!/bin/sh + +( cd fractals ; ./build.sh ) + +( cd games ; ./build.sh ) + +( cd hires ; ./build.sh ) + +( cd hiresmc ; ./build.sh ) + +( cd particles ; ./build.sh ) + +( cd kernalio ; ./build.sh ) + +( cd memmap ; ./build.sh ) + +( cd rasterirq ; ./build.sh ) + +( cd scrolling ; ./build.sh ) + +( cd sprites ; ./build.sh ) + +( cd stdio ; ./build.sh ) + diff --git a/oscar64/samples/fractals/build.sh b/oscar64/samples/fractals/build.sh new file mode 100755 index 0000000..8cc01b6 --- /dev/null +++ b/oscar64/samples/fractals/build.sh @@ -0,0 +1,8 @@ +#!/bin/sh +../../bin/oscar64 mbtext.c -n +../../bin/oscar64 mbhires.c -n +../../bin/oscar64 mbmulti.c -n +../../bin/oscar64 mbmulti3d.c -n +../../bin/oscar64 mbfixed.c -n -O3 +../../bin/oscar64 mbzoom.c -n -O3 + diff --git a/oscar64/samples/fractals/make.bat b/oscar64/samples/fractals/make.bat new file mode 100644 index 0000000..730f50e --- /dev/null +++ b/oscar64/samples/fractals/make.bat @@ -0,0 +1,6 @@ +call ..\..\bin\oscar64 mbtext.c -n +call ..\..\bin\oscar64 mbhires.c -n +call ..\..\bin\oscar64 mbmulti.c -n +call ..\..\bin\oscar64 mbmulti3d.c -n +call ..\..\bin\oscar64 mbfixed.c -n -O3 +call ..\..\bin\oscar64 mbzoom.c -n -O3 diff --git a/oscar64/samples/fractals/makefile b/oscar64/samples/fractals/makefile new file mode 100644 index 0000000..f30655a --- /dev/null +++ b/oscar64/samples/fractals/makefile @@ -0,0 +1,14 @@ +%.prg: %.c + @echo "Compiling sample file" $< + @$(OSCAR64_CC) $(OSCAR64_CFLAGS) $< + +all: mbtext.prg mbtext.prg mbhires.prg mbmulti.prg mbmulti3d.prg mbfixed.prg mbzoom.prg + +mbfixed.prg: mbfixed.c + @$(OSCAR64_CC) $(OSCAR64_CFLAGS) -O3 $< + +mbzoom.prg: mbzoom.c + @$(OSCAR64_CC) $(OSCAR64_CFLAGS) -O3 $< + +clean: + @$(RM) *.asm *.int *.lbl *.map *.prg diff --git a/oscar64/samples/fractals/mbfixed.c b/oscar64/samples/fractals/mbfixed.c new file mode 100644 index 0000000..22944a2 --- /dev/null +++ b/oscar64/samples/fractals/mbfixed.c @@ -0,0 +1,100 @@ +#include +#include +#include +#include +#include + +// Address of hires buffer and color buffers +#define Screen ((char *)0xe000) +#define Color ((char *)0xc800) +#define Color2 ((char *)0xd800) + +// Bit patterns for eight different color pairs +char colors[] = { + 0xff, 0xff, + 0xee, 0xbb, + 0xaa, 0xaa, + 0x88, 0x22, + + 0x44, 0x11, + 0x55, 0x55, + 0xdd, 0x77, + 0x33, 0xcc +}; + +int main(void) +{ + // Install the IRQ trampoline + mmap_trampoline(); + + // Turn of the kernal ROM + mmap_set(MMAP_NO_ROM); + + // Switch VIC into multicolor bitmap mode + vic_setmode(VICM_HIRES_MC, Color, Screen); + + // Clear the screen and set the colors + vic.color_back = 0x00; + + memset(Screen, 0, 8000); + memset(Color, 0x27, 1000); + memset(Color2, 0x03, 1000); + + // Loop over all pixels + int py, px; + + for(py=0; py<100; py++) + { + for(px=0; px<160; px++) + { + // Value in the complex plane + +// int xz = (int)(((float)px * (3.5 / 160.0) - 2.5) * 4096 + 0.5); +// int yz = (int)(((float)py * (2.4 / 100.0) - 1.2) * 4096 + 0.5); + + int xz = lmul8f8s(px, (int)((3.5 / 160.0) * 4096 * 256)) - (int)(2.5 * 4096); + int yz = lmul8f8s(py, (int)((2.4 / 100.0) * 4096 * 256)) - (int)(1.2 * 4096); + + // Iterate up to 32 times + int x = 0, y = 0; + int i; + for(i=0; i<32; i++) + { + unsigned long xq = lsqr4f12s(x), yq = lsqr4f12s(y); + + if (xq + yq >= 0x04000000UL) break; + + int xt = (int)(xq >> 12) - (int)(yq >> 12) + xz; + y = 2 * lmul4f12s(x, y) + yz; + x = xt; + } + + if (i < 32) + { + // Position on screen + char * dp = Screen + 320 * (py >> 2) + 2 * (py & 3) + 2 * (px & ~3); + + // Mask of pixels to change + char mask = 0xc0 >> (2 * (px & 3)); + + // Get the two color patterns for upper and lower half + char c0 = colors[2 * (i & 7)], c1 = colors[2 * (i & 7) + 1]; + + // Put the pixels into the image + dp[0] |= c0 & mask; + dp[1] |= c1 & mask; + } + } + } + + // Re-enable the kernal + mmap_set(MMAP_NO_BASIC); + + // Wait for key press + getch(); + + // Restore VIC state + vic_setmode(VICM_TEXT, (char *)0x0400, (char *)0x1000); + + return 0; +} diff --git a/oscar64/samples/fractals/mbhires.c b/oscar64/samples/fractals/mbhires.c new file mode 100644 index 0000000..0e6bd1e --- /dev/null +++ b/oscar64/samples/fractals/mbhires.c @@ -0,0 +1,65 @@ +#include +#include +#include +#include + +// Address of hires buffer and color buffer +#define Screen ((char *)0xe000) +#define Color ((char *)0xc800) + +int main(void) +{ + // Install the IRQ trampoline + mmap_trampoline(); + + // Turn of the kernal ROM + mmap_set(MMAP_NO_ROM); + + // Switch VIC into hires mode + vic_setmode(VICM_HIRES, Color, Screen); + + // Clear the screen + memset(Screen, 0, 8000); + memset(Color, 0x10, 1000); + + // Loop over all pixels + int py, px; + + for(py=0; py<200; py++) + { + for(px=0; px<320; px++) + { + // Value in the complex plane + + float xz = (float)px * (3.5 / 320.0)- 2.5; + float yz = (float)py * (2.0 / 200.0) - 1.0; + + // Iterate up to 32 times + float x = 0.0, y = 0.0; + int i; + for(i=0; i<32; i++) + { + if (x * x + y * y > 4.0) break; + + float xt = x * x - y * y + xz; + y = 2 * x * y + yz; + x = xt; + } + + // Set a pixel if exceeds bound in less than 32 iterations + if (i < 32) + Screen[320 * (py >> 3) + (py & 7) + (px & ~7)] |= 0x80 >> (px & 7); + } + } + + // Re-enable the kernal + mmap_set(MMAP_NO_BASIC); + + // Wait for key press + getch(); + + // Restore VIC state + vic_setmode(VICM_TEXT, (char *)0x0400, (char *)0x1000); + + return 0; +} diff --git a/oscar64/samples/fractals/mbmulti.c b/oscar64/samples/fractals/mbmulti.c new file mode 100644 index 0000000..ec0bdcd --- /dev/null +++ b/oscar64/samples/fractals/mbmulti.c @@ -0,0 +1,94 @@ +#include +#include +#include +#include + +// Address of hires buffer and color buffers +#define Screen ((char *)0xe000) +#define Color ((char *)0xc800) +#define Color2 ((char *)0xd800) + +// Bit patterns for eight different color pairs +char colors[] = { + 0xff, 0xff, + 0xee, 0xbb, + 0xaa, 0xaa, + 0x88, 0x22, + + 0x44, 0x11, + 0x55, 0x55, + 0xdd, 0x77, + 0x33, 0xcc +}; + +int main(void) +{ + // Install the IRQ trampoline + mmap_trampoline(); + + // Turn of the kernal ROM + mmap_set(MMAP_NO_ROM); + + // Switch VIC into multicolor bitmap mode + vic_setmode(VICM_HIRES_MC, Color, Screen); + + // Clear the screen and set the colors + vic.color_back = 0x00; + + memset(Screen, 0, 8000); + memset(Color, 0x27, 1000); + memset(Color2, 0x03, 1000); + + // Loop over all pixels + int py, px; + + for(py=0; py<100; py++) + { + for(px=0; px<160; px++) + { + // Value in the complex plane + + float xz = (float)px * (3.5 / 160.0)- 2.5; + float yz = (float)py * (2.4 / 100.0) - 1.2; + + // Iterate up to 32 times + float x = 0.0, y = 0.0; + int i; + for(i=0; i<32; i++) + { + if (x * x + y * y > 4.0) break; + + float xt = x * x - y * y + xz; + y = 2 * x * y + yz; + x = xt; + } + + if (i < 32) + { + // Position on screen + char * dp = Screen + 320 * (py >> 2) + 2 * (py & 3) + 2 * (px & ~3); + + // Mask of pixels to change + char mask = 0xc0 >> (2 * (px & 3)); + + // Get the two color patterns for upper and lower half + char c0 = colors[2 * (i & 7)], c1 = colors[2 * (i & 7) + 1]; + + // Put the pixels into the image + dp[0] |= c0 & mask; + dp[1] |= c1 & mask; + } + } + } + + // Re-enable the kernal + mmap_set(MMAP_NO_BASIC); + + // Wait for key press + getch(); + + // Restore VIC state + vic_setmode(VICM_TEXT, (char *)0x0400, (char *)0x1000); + + return 0; +} diff --git a/oscar64/samples/fractals/mbmulti3d.c b/oscar64/samples/fractals/mbmulti3d.c new file mode 100644 index 0000000..76b200e --- /dev/null +++ b/oscar64/samples/fractals/mbmulti3d.c @@ -0,0 +1,183 @@ +#include +#include +#include +#include +#include + +// Address of hires buffer and color buffers +#define Screen ((char *)0xe000) +#define Color1 ((char *)0xc800) +#define Color2 ((char *)0xd800) + +// Bit patterns for two different color pairs and eight shades +byte colors[2][17] = +{ + {0x00, + 0x44, 0x44, 0x55, 0x55, 0xdd, 0xdd, 0xff, 0xff, + 0x88, 0x88, 0xaa, 0xaa, 0xee, 0xee, 0xff, 0xff, + }, + {0x00, + 0x00, 0x11, 0x11, 0x55, 0x55, 0x77, 0x77, 0xff, + 0x00, 0x22, 0x22, 0xaa, 0xaa, 0xbb, 0xbb, 0xff, + } +}; + +// Fill a vertical line x from py to ty with color c +void VLine(int x, int py, int ty, char c) +{ + // Clip boundaries + if (py < 0) + py = 0; + if (ty > 100) + ty = 100; + + // Check if there are pixel to draw + if (py < ty) + { + // Calculate top address and mask + char mask = 0xc0 >> (2 * (x & 3)); + char * dp = Screen + 320 * (py >> 2) + 2 * (py & 3) + 2 * (x & ~3); + + // Get the two color patterns + char c0 = colors[0][c] & mask, c1 = colors[1][c] & mask; + + // Invert mask to cover the unchanged portion + mask = ~mask; + + // Loop over all pixels + char h = ty - py; + while (h) + { + // Apply color to memory + dp[0] = (dp[0] & mask) | c0; + dp[1] = (dp[1] & mask) | c1; + + // Two pixel lines down + dp += 2; + if (!((int)dp & 7)) + dp += 312; + + h--; + } + } +} + +// Iterate up to 32 iterations and return a smoothed height +float iter(float xz, float yz) +{ + float x = 0.0, y = 0.0, r; + int i; + for(i=0; i<32; i++) + { + r = x * x + y * y; + if (r > 64.0) break; + + float xt = x * x - y * y + xz; + y = 2 * x * y + yz; + x = xt; + } + + + if (i == 32) + return 32; + else + return i - log(log(r)/log(64.0))/log(2.0); +} + +// Calculate light with given new and old heights +int light(float hl, float hu, float h) +{ + float dx = h - hl, dz = h - hu, dy = 0.1; + + float dd = sqrt(dx * dx + dy * dy + dz * dz); + int ni = (int)floor((-2 * dx + dy + dz) / dd * 0.408 * 8); + if (ni < 0) ni = 0; else if (ni > 7) ni = 7; + + return ni; +} + +int main(void) +{ + // Install the IRQ trampoline + mmap_trampoline(); + + // Turn of the kernal ROM + mmap_set(MMAP_NO_ROM); + + // Switch VIC into multicolor bitmap mode + vic_setmode(VICM_HIRES_MC, Color1, Screen); + + // Clear the screen and set the colors + vic.color_back = 0x00; + vic.color_border = 0x00; + + memset(Screen, 0, 8000); + memset(Color1, 0x26, 1000); + memset(Color2, 0x0f, 1000); + + // Height of previous row, needed for lighting + float hl[200]; + + // Rotation of complex plane + float w = -0.7; + float co = cos(w), si = sin(w); + + // Loop from left to right + for(int x=-1; x<160; x+= 1) + { + // Loop from far to nead + int py = 20; + float hu = 0; + for(int y=1; y<200; y+= 1) + { + // Inverse 3D projection + float fz = 2.0 / (float)y; + float fx = (float)(x - 80) * fz / 100.0; + + float mz = fz * 100.0 - 3.0, mx = fx * 100.0; + + // Rotation of the plane + float rx = mx * co - mz * si, rz = mx * si + mz * co; + float dp = iter(rx, rz); + float v = 2 * dp; + if (v < 1.0) v = 1.0; + + float fy = 5.0 * pow(2.0, - v * 0.4); + + // Calculate light + int ni = light(hl[y], hu, fy); + + // Update left column + hl[y] = fy; + hu = fy; + + // Forward 3D projection + int ty = 20 + y / 2 + (int)(floor(fy / fz)); + + + // color of pixel + int c; + if (dp != 32) + c = 1 + ni + 8 * ((int)floor(dp) & 1); + else + c = 0; + + // Draw line if not dummy left row + if (x >= 0) + VLine(x, py, ty, c); + + py = ty; + } + } + + // Re-enable the kernal + mmap_set(MMAP_NO_BASIC); + + // Wait for key press + getch(); + + // Restore VIC state + vic_setmode(VICM_TEXT, (char *)0x0400, (char *)0x1000); + + return 0; +} diff --git a/oscar64/samples/fractals/mbmulti3d.png b/oscar64/samples/fractals/mbmulti3d.png new file mode 100644 index 0000000..9f1b8d7 Binary files /dev/null and b/oscar64/samples/fractals/mbmulti3d.png differ diff --git a/oscar64/samples/fractals/mbtext.c b/oscar64/samples/fractals/mbtext.c new file mode 100644 index 0000000..5eb15c7 --- /dev/null +++ b/oscar64/samples/fractals/mbtext.c @@ -0,0 +1,36 @@ +#include + +#define Screen ((char *)0x0400) +#define Color ((char *)0xd800) + +int main(void) +{ + memset(Screen, 160, 1024); + + int py, px; + + for(py=0; py<25; py++) + { + for(px=0; px<40; px++) + { + float xz = (float)px * (3.5 / 40.0)- 2.5; + float yz = (float)py * (2.0 / 25.0) - 1.0; + + float x = 0.0, y = 0.0; + int i; + for(i=0; i<=14; i++) + { + if (x * x + y * y > 4.0) break; + + float xt = x * x - y * y + xz; + y = 2 * x * y + yz; + x = xt; + } + i--; + + Color[py * 40 + px] = i; + } + } + + return 0; +} diff --git a/oscar64/samples/fractals/mbzoom.c b/oscar64/samples/fractals/mbzoom.c new file mode 100644 index 0000000..9725389 --- /dev/null +++ b/oscar64/samples/fractals/mbzoom.c @@ -0,0 +1,312 @@ +#include +#include +#include + +#define Screen ((char *)0x0400) +#define Color ((char *)0xd800) + +// Lookup table for squares from 0..255 +__striped unsigned sqb[256]; + +// Shift byte right and left by 4 +char shlb4[256], shrb4[256]; + +#pragma align(sqb, 256); + +// Square an unsigned int into an unsigned long +inline unsigned long ssquare(unsigned x) +{ + // Split into low byte and highbyte, so we have x = a + 0x100 * b + + unsigned a = x & 0xff; + unsigned b = x >> 8; + + // So now we calculate (a + 0x100 * b)² + // Result will be a² + 0x100 * 2 * a * b + 0x10000 * b² + // with 2 * a * b == (a + b)² - a² - b² + + // We can cover all cases with the square table except if a + b >= 0x100 + // in this case we have abp := a + b - 0x100 + // (abp + 0x100)² == abp² + 2 * 0x100 * abp + 0x10000 + + // Get squares of the bytes and the sum of the bytes + unsigned a2 = sqb[a], b2 = sqb[b]; + unsigned apb = a + b; + + // First approximation approximation + // a² + 0x10000 * b² + unsigned long sum = a2 + ((unsigned long)b2 << 16); + + // Check if a + b >= 0x100 + if (apb & 0xff00) + { + apb &= 0xff; + sum += 0x1000000UL; + sum += (unsigned long)apb << 17; + sum += (unsigned long)sqb[apb] << 8; + } + else + { + apb &= 0xff; + sum += (unsigned long)sqb[apb] << 8; + } + + // Now w have a² + 0x1000 * b² + (a + b)² + + sum -= (unsigned long)a2 << 8; + sum -= (unsigned long)b2 << 8; + + // And finally the complete result + return sum; +} + +// Signed square of x +inline long sq(int x) +{ + if (x < 0) + x = -x; + return ssquare(x); +} + +// Colors to fill in the different levels +static const char colors[32] = { + VCOL_BLUE, + VCOL_LT_BLUE, + VCOL_WHITE, + VCOL_LT_GREEN, + VCOL_GREEN, + VCOL_YELLOW, + VCOL_ORANGE, + VCOL_RED, + VCOL_PURPLE, + + VCOL_BLUE, + VCOL_BLUE, + VCOL_LT_BLUE, + VCOL_LT_BLUE, + VCOL_WHITE, + VCOL_WHITE, + VCOL_LT_GREEN, + VCOL_LT_GREEN, + VCOL_GREEN, + VCOL_GREEN, + VCOL_YELLOW, + VCOL_YELLOW, + VCOL_ORANGE, + VCOL_ORANGE, + VCOL_RED, + VCOL_RED, + VCOL_PURPLE, + VCOL_PURPLE, + + VCOL_LT_GREY, + VCOL_LT_GREY, + VCOL_MED_GREY, + VCOL_MED_GREY, + VCOL_DARK_GREY, +}; + +inline int shr12(long l) +{ + char b3 = (l >> 24) & 0xff; + char b2 = (l >> 16) & 0xff; + char b1 = (l >> 8) & 0xff; + char a0 = shrb4[b1] | shlb4[b2]; + char a1 = shrb4[b2] | shlb4[b3]; + return a0 | (a1 << 8); +} + +// Return color for a given coordinate in the complex plane using +// 12.4bit fixed numbers using m'=m²+b + +inline char fcolor(int xz, int yz) +{ + // Start value for iteration is the offset value itself + + int x = xz, y = yz; + + // Iterate up to 32 steps + + for(int i=0; i<32; i++) + { + // Build squares of real and imaginary component + long xx = sq(x), yy = sq(y), xxyy = sq(x + y); + + long xxpyy = xx + yy; + + // Use squares to check for exit condition of sure + // to progress towards infinity + if (xxpyy >= 4L * 4096 * 4096) return colors[i]; + + // Next iteration values using complex arithmetic + // Mx' = Mx² - My² + Bx + // My' = 2 * Mx * My + By = (Mx + My)² - Mx² - My² + By + x = shr12(xx - yy + 2048) + xz; + y = shr12(xxyy - xxpyy + 2048) + yz; + } + + // More than maximum number of iterations, so assume progress + // towards zero + + return VCOL_BLACK; +} + +// Fill a row with color +void fill_row(char py, int cix, int yz, int cis) +{ + int xz = cix; + for(int px=0; px<40; px++) + { + Color[py * 40 + px] = fcolor(xz, yz); + xz += cis; + } +} + +// Fill a column with color +void fill_column(char px, int xz, int ciy, int cis) +{ + int yz = ciy; + for(int py=0; py<25; py++) + { + Color[py * 40 + px] = fcolor(xz, yz); + yz += cis; + } +} + +// Fill the complete image +void fill_image(int cix, int ciy, int cis) +{ + int yz = ciy; + for(int py=0; py<25; py++) + { + fill_row(py, cix, yz, cis); + yz += cis; + } +} + +// Scroll screen to the left +void scroll_left(void) +{ + for(char x=0; x<39; x++) + { + #pragma unroll(full) + for(char y=0; y<25; y++) + { + Color[y * 40 + x] = Color[y * 40 + x + 1]; + } + } +} + +// Scroll screen to the right +void scroll_right(void) +{ + for(signed char x=38; x>=0; x--) + { + #pragma unroll(full) + for(char y=0; y<25; y++) + { + Color[y * 40 + x + 1] = Color[y * 40 + x]; + } + } +} + +// Scroll screen up +void scroll_up(void) +{ + for(char x=0; x<40; x++) + { + #pragma unroll(full) + for(char y=0; y<24; y++) + { + Color[y * 40 + x] = Color[(y + 1) * 40 + x]; + } + } +} + +// Scroll screen down +void scroll_down(void) +{ + for(char x=0; x<40; x++) + { + #pragma unroll(full) + for(char y=0; y<24; y++) + { + Color[(24 - y) * 40 + x] = Color[(23 - y) * 40 + x]; + } + } +} + +int main(void) +{ + // Initialize square table + for(unsigned i=0; i<256; i++) + { + sqb[i] = i * i; + shlb4[i] = i << 4; + shrb4[i] = i >> 4; + } + + // Clear screen + memset(Screen, 160, 1024); + + // Start coordinates in float + float cx = -0.4; + float cy = 0.0; + float cw = 3.2; + + // Convert to top, left and step in 12.4 fixed point + int cix = (int)((cx - 0.5 * cw) * 4096); + int ciy = (int)((cy - 12.0 * cw / 40.0) * 4096); + int cis = (int)(cw / 40.0 * 4096); + + // Initial image + fill_image(cix, ciy, cis); + + for(;;) + { + // Wait for keypress + char ch = getch(); + + switch (ch) + { + case 'S': + ciy += cis; + scroll_up(); + fill_row(24, cix, ciy + 24 * cis, cis); + break; + case 'W': + ciy -= cis; + scroll_down(); + fill_row(0, cix, ciy, cis); + break; + case 'A': + cix -= cis; + scroll_right(); + fill_column(0, cix, ciy, cis); + break; + case 'D': + cix += cis; + scroll_left(); + fill_column(39, cix + 39 * cis, ciy, cis); + break; + case '+': + cix += 20 * cis; + ciy += 12 * cis; + cis = cis * 2 / 3; + cix -= 20 * cis; + ciy -= 12 * cis; + fill_image(cix, ciy, cis); + break; + case '-': + cix += 20 * cis; + ciy += 12 * cis; + cis = cis * 3 / 2; + cix -= 20 * cis; + ciy -= 12 * cis; + fill_image(cix, ciy, cis); + break; + } + } + + return 0; +} diff --git a/oscar64/samples/games/breakout.c b/oscar64/samples/games/breakout.c new file mode 100644 index 0000000..ace2326 --- /dev/null +++ b/oscar64/samples/games/breakout.c @@ -0,0 +1,664 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Character set +char charset[2048] = { + #embed "../resources/breakoutchars.bin" +}; + +char spriteset[2048] = { + #embed "../resources/breakoutsprites.bin" +}; + +byte * const Screen = (byte *)0xc800; +byte * const Font = (byte *)0xd000; +byte * const Color = (byte *)0xd800; +byte * const Sprites = (byte *)0xd800; + +unsigned flashingBricks[32]; +char numFlashingBricks[4]; + +// 0123456789012345678901234567890123456789 +const char StatusText[] = s" Score: 000000 High: 000000 Lives: 0 "; + +void status_init(void) +{ + for(char i=0; i<40; i++) + { + if (StatusText[i] != ' ') + { + Screen[i] = StatusText[i]; + Color[i] = 7; + } + } + +} + +// Increment the score from a given digit on +void score_inc(char digit, unsigned val) +{ + // Lowest digit to increment + char at = 13 - digit; + + // Loop while there is still score to account for + while (val) + { + // Increment one character + char ch = Screen[at] + val % 10; + + // Remove low digit from number + val /= 10; + + // Check overflow + if (ch > '9') + { + ch -= 10; + val++; + } + + Screen[at] = ch; + + // Next higher character + at --; + } +} + +// Reset score and update high score +void score_reset(void) +{ + // Find first digit, where score and highscore differ + char i = 0; + while (i < 6 && Screen[i + 8] == Screen[i + 22]) + i++; + + // Check if new score is higher + if (i < 6 && Screen[i + 8] > Screen[i + 22]) + { + // If so, copy to highscore + while (i < 6) + { + Screen[i + 22] = Screen[i + 8]; + i++; + } + } + + // Clear score + for(char i=0; i<6; i++) + Screen[i + 8] = '0'; +} + +void brick_put(char x, char y, char b, char c) +{ + char * cp = Color + 40 * y + x; + + cp[ 0] = c; + cp[ 1] = c; + cp[ 2] = c; + cp[40] = c; + cp[41] = c; + cp[42] = c; + cp[81] = 15; + cp[82] = 15; + cp[83] = 15; + + char * sp = Screen + 40 * y + x; + + sp[ 0] = b | 0; + sp[ 1] = b | 1; + sp[ 2] = b | 2; + + sp[40] = b | 3; + sp[41] = b | 4; + sp[42] = b | 5; + sp[43] = 103 - (x & 1) - 2 * (y & 1); + + sp[81] = 101 - (x & 1) + 2 * (y & 1); + sp[82] = 100 + (x & 1) + 2 * (y & 1); + sp[83] = 101 - (x & 1) + 2 * (y & 1); +} + +void brick_init(void) +{ + for(char y=0; y<25; y++) + { + for(char x=0; x<40; x++) + { + Screen[40 * y + x] = 96 + (x & 1) + 2 * (y & 1);// + 4 * (x == 0 || y == 0); + } + } + + status_init(); + + for(char y=0; y<8; y++) + { + for(char x=0; x<12; x++) + { + brick_put(3 * x + 2, 2 * y + 1, 128 + (x / 4) * 8, 8 + y); + } + } +} + +void brick_hit(char x, char y) +{ + __assume(x < 40 && y < 25); + + char c = Screen[40 * y + x + 0]; + + if (c >= 128) + { + c &= 7; + + if (c >= 3) + { + y --; + c -= 3; + } + + x -= c; + + unsigned bi = 256 * y + x; + char i = 0, n = numFlashingBricks[3]; + while (i < n && flashingBricks[i] != bi) + i++; + + if (i == n) + { + flashingBricks[i] = bi; + numFlashingBricks[3]++; + + char * cp = Color + 40 * y + x; + + cp[ 0] = 9; + cp[ 1] = 9; + cp[ 2] = 9; + cp[40] = 9; + cp[41] = 9; + cp[42] = 9; + + score_inc(0, 5); + } + } +} + +void brick_animate(void) +{ + char n = numFlashingBricks[0]; + + for(char i=0; i> 8; + + char * cp = Color + 40 * y + x; + + cp[ 0] = 15; + cp[ 1] = 15; + cp[ 2] = 15; + cp[40] = 15; + cp[41] = 15; + cp[42] = 15; + + char * sp = Screen + 40 * (y - 1) + (x - 1); + + char ch = 96 + (x & 1) + 2 * (y & 1); + if (sp[ 0] >= 128) + sp[41] = ch | 4; + else + sp[41] = ch; + + if (sp[ 1] >= 128) + sp[42] = (ch ^ 1) | 4; + else + sp[42] = (ch ^ 1); + + if (sp[ 2] >= 128) + sp[43] = ch | 4; + else + sp[43] = ch; + + if (sp[40] >= 128) + sp[81] = (ch ^ 2) | 4; + else + sp[81] = (ch ^ 2); + + sp[82] = (ch ^ 3); + sp[83] = (ch ^ 2); + + if (sp[84] < 128) + sp[84] = (ch ^ 3); + + if (sp[122] < 128) + sp[122] = (ch ^ 1); + if (sp[123] < 128) + sp[123] = ch; + if (sp[124] < 128) + sp[124] = (ch ^ 1); + } + + for(char i=0; i<3; i++) + numFlashingBricks[i] = numFlashingBricks[i + 1] - n; + numFlashingBricks[3] = numFlashingBricks[2]; + + for(char i=0; i> 6) +#define BALL_COORD(x, f) (((x) << 6) + f) + +void ball_init(Ball * ball, char index, int sx, int sy, int vx, int vy) +{ + ball->index = index; + ball->active = true; + ball->sx = sx; + ball->sy = sy; + ball->vx = vx; + ball->vy = vy; + + int ix = BALL_INT(ball->sx) + 24, iy = BALL_INT(ball->sy) + 50; + + spr_set(2 * index + 2, true, ix, iy, 97, 0, false, false, false); + spr_set(2 * index + 3, true, ix, iy, 96, 15, true, false, false); +} + +void ball_lost(Ball * ball) +{ + ball->active = false; + + spr_show(2 * ball->index + 2, false); + spr_show(2 * ball->index + 3, false); + + return; +} + +#define COL_00 1 +#define COL_01 2 +#define COL_10 4 +#define COL_11 8 + +int paddlex, paddlevx; + +char sound_index; +unsigned sound_freq; + +void sound_trigger(unsigned freq) +{ + sound_freq = freq; + if (sound_index == 0) + sound_index = 3; + else if (sound_index > 6) + sound_index = 1; +} + +void sound_loop(void) +{ + switch (sound_index) + { + case 0: + break; + case 1: + case 18: + sid.voices[0].ctrl = 0; + sid.voices[0].attdec = 0; + sid.voices[0].susrel = 0; + sound_index++; + break; + case 2: + case 19: + sid.voices[0].ctrl = SID_CTRL_TEST; + sound_index++; + break; + case 3: + sid.voices[0].freq = sound_freq; + sid.voices[0].attdec = SID_ATK_2 | SID_DKY_6; + sid.voices[0].susrel = SID_DKY_300 | 0xf0; + sid.voices[0].pwm = 0x800; + sid.voices[0].ctrl = SID_CTRL_RECT | SID_CTRL_GATE | SID_CTRL_NOISE; + sound_index++; + break; + case 4: + case 5: + sound_index++; + break; + case 6: + sound_index++; + sid.voices[0].ctrl = SID_CTRL_RECT; + break; + case 20: + sound_index = 0; + break; + default: + sound_index++; + } +} + +void ball_loop(Ball * ball) +{ + if (!ball->active) + return; + + ball->sx += ball->vx; + ball->sy += ball->vy; + + bool mirrorX = false, mirrorY = false; + + int ix = BALL_INT(ball->sx), iy = BALL_INT(ball->sy); + int px = BALL_INT(paddlex); + + if (ix + 6 > 320 || ix < 0) + { + mirrorX = true; + sound_trigger(NOTE_D(6)); + } + + if (iy < 0) + { + mirrorY = true; + sound_trigger(NOTE_D(6)); + } + else if (iy >= 200) + { + ball_lost(ball); + return; + } + + if (iy + 6 > 190 && iy < 190) + { + if (ix + 5 >= px && ix < px + 48) + { + mirrorY = true; + if (ix < px && ball->vx > 0) + { + mirrorX = true; + } + else if (ix + 6 >= px + 48 && ball->vx < 0) + { + mirrorX = true; + } + else + { + ball->vx = (ball->vx * 6 + paddlevx + 4) >> 3; + } + + sound_trigger(NOTE_D(7)); + } + } + + + int x0 = ix >> 3; + int y0 = iy >> 3; + int x1 = (ix + 5) >> 3; + int y1 = (iy + 5) >> 3; + + + if (x0 >= 0 && x1 < 40 && y0 >= 0 && y1 < 24) + { + char col = 0; + bool hit = false; + + if (Screen[40 * y0 + x0] >= 128) col |= COL_00; + if (Screen[40 * y0 + x1] >= 128) col |= COL_01; + if (Screen[40 * y1 + x0] >= 128) col |= COL_10; + if (Screen[40 * y1 + x1] >= 128) col |= COL_11; + + if (ball->vx < 0 && ((col & (COL_00 | COL_01)) == COL_00) || ((col & (COL_10 | COL_11)) == COL_10)) + { + mirrorX = true; + hit = true; + } + else if (ball->vx > 0 && ((col & (COL_00 | COL_01)) == COL_01) || ((col & (COL_10 | COL_11)) == COL_11)) + { + mirrorX = true; + hit = true; + } + + if (ball->vy < 0 && ((col & (COL_00 | COL_10)) == COL_00) || ((col & (COL_01 | COL_11)) == COL_01)) + { + mirrorY = true; + hit = true; + } + else if (ball->vy > 0 && ((col & (COL_00 | COL_10)) == COL_10) || ((col & (COL_01 | COL_11)) == COL_11)) + { + mirrorY = true; + hit = true; + } + + if (col) + { + brick_hit(x0, y0); + brick_hit(x1, y0); + brick_hit(x0, y1); + brick_hit(x1, y1); + } + + if (hit) + { + sound_trigger(NOTE_D(8)); + } + } + + if (mirrorY) + { + ball->vy = - ball->vy; + ball->sy += ball->vy; + } + if (mirrorX) + { + ball->vx = - ball->vx; + ball->sx += ball->vx; + } +} + +void ball_move(Ball * ball) +{ + int ix = BALL_INT(ball->sx) + 24, iy = BALL_INT(ball->sy) + 50; + + spr_move(2 * ball->index + 2, ix, iy); + spr_move(2 * ball->index + 3, ix, iy); +} + +enum GameState +{ + GS_READY, // Getting ready + + GS_BALL_LOCKED, // The ball is locked on the paddle + GS_PLAYING, // Playing the game + GS_BALL_DROPPED, // The last ball has been dropped + + GS_GAME_OVER +}; + +struct Game +{ + GameState state; + Ball balls[3]; + char count; + +} TheGame; + +void paddle_init(void) +{ + paddlevx = 0; + paddlex = BALL_COORD(160, 0); + + spr_set(0, true, 24 + BALL_INT(paddlex), 240, 99, 0, false, true, false); + spr_set(1, true, 24 + BALL_INT(paddlex), 240, 98, 15, true, true, false); +} + +void paddle_control(void) +{ + joy_poll(0); + + if (joyx[0] == 0) + { + if (paddlevx < 0) + paddlevx = (paddlevx + 1) >> 1; + else + paddlevx >>= 1; + } + else + { + paddlevx += joyx[0] * 8; + + if (paddlevx >= 256) + paddlevx = 256; + else if (paddlevx < -256) + paddlevx = -256; + } + + paddlex += paddlevx; + if (paddlex < BALL_COORD(-4, 0) || paddlex > BALL_COORD(320 - 48 + 4, 0)) + { + paddlevx = -paddlevx; + paddlex += paddlevx; + } +} + +void paddle_move(void) +{ + spr_move(0, 24 + BALL_INT(paddlex), 240); + spr_move(1, 24 + BALL_INT(paddlex), 240); +} + +void game_state(GameState state) +{ + // Set new state + TheGame.state = state; + + switch (state) + { + case GS_READY: + brick_init(); + paddle_init(); + TheGame.count = 120; + break; + + case GS_BALL_LOCKED: + ball_init(TheGame.balls + 0, 0, paddlex + BALL_COORD(22, 0), BALL_COORD(184, 0), BALL_COORD(0, 0), BALL_COORD(0, 0)); + ball_init(TheGame.balls + 1, 1, paddlex + BALL_COORD(22, 0), BALL_COORD(184, 0), BALL_COORD(0, 0), BALL_COORD(0, 0)); + ball_init(TheGame.balls + 2, 2, paddlex + BALL_COORD(22, 0), BALL_COORD(184, 0), BALL_COORD(0, 0), BALL_COORD(0, 0)); + break; + + case GS_PLAYING: + TheGame.balls[0].vy = BALL_COORD(-2, 32); + TheGame.balls[0].vx = paddlevx >> 2; + TheGame.balls[1].vy = BALL_COORD(-2, 16); + TheGame.balls[1].vx = (paddlevx >> 2) - 16; + TheGame.balls[2].vy = BALL_COORD(-2, 16); + TheGame.balls[2].vx = (paddlevx >> 2) + 16; + break; + + case GS_BALL_DROPPED: + TheGame.count = 60; + break; + + case GS_GAME_OVER: + TheGame.count = 120; + break; + } +} + +void game_loop() +{ + switch (TheGame.state) + { + case GS_READY: + paddle_control(); + if (!--TheGame.count) + game_state(GS_BALL_LOCKED); + break; + case GS_BALL_LOCKED: + paddle_control(); + TheGame.balls[0].sx = paddlex + BALL_COORD(22, 0); + TheGame.balls[1].sx = paddlex + BALL_COORD(22, 0); + TheGame.balls[2].sx = paddlex + BALL_COORD(22, 0); + if (joyb[0]) + game_state(GS_PLAYING); + break; + case GS_PLAYING: + paddle_control(); +// vic.color_border++; + for(char i=0; i<3; i++) + ball_loop(TheGame.balls + i); + for(char i=0; i<3; i++) + ball_loop(TheGame.balls + i); +// vic.color_border--; + if (!(TheGame.balls[0].active || TheGame.balls[1].active || TheGame.balls[2].active)) + game_state(GS_BALL_DROPPED); + break; + case GS_BALL_DROPPED: + paddle_control(); + if (!--TheGame.count) + game_state(GS_BALL_LOCKED); + break; + case GS_GAME_OVER: + if (!--TheGame.count) + game_state(GS_READY); + break; + } +} + +int main(void) +{ + mmap_trampoline(); + + // Install character set + mmap_set(MMAP_RAM); + memcpy(Font, charset, 2048); + memcpy(Sprites, spriteset, 256); + mmap_set(MMAP_NO_BASIC); + + // Switch screen + vic_setmode(VICM_TEXT_MC, Screen, Font); + + spr_init(Screen); + + // Change colors + vic.color_border = VCOL_BLACK; + vic.color_back = VCOL_BLACK; + vic.color_back1 = VCOL_WHITE; + vic.color_back2 = VCOL_DARK_GREY; + + vic.spr_mcolor0 = VCOL_DARK_GREY; + vic.spr_mcolor1 = VCOL_WHITE; + + memset(Screen, 96, 1000); + memset(Color, 15, 1000); + + game_state(GS_READY); + + sid.fmodevol = 15; + + for(;;) + { + brick_animate(); + game_loop(); + sound_loop(); + vic_waitFrame(); + + for(char j=0; j<3; j++) + ball_move(TheGame.balls + j); + paddle_move(); + + + } + + return 0; +} diff --git a/oscar64/samples/games/breakout.png b/oscar64/samples/games/breakout.png new file mode 100644 index 0000000..ccb2c59 Binary files /dev/null and b/oscar64/samples/games/breakout.png differ diff --git a/oscar64/samples/games/build.sh b/oscar64/samples/games/build.sh new file mode 100755 index 0000000..56b0821 --- /dev/null +++ b/oscar64/samples/games/build.sh @@ -0,0 +1,9 @@ +#!/bin/sh +../../bin/oscar64 snake.c +../../bin/oscar64 lander.c -n +../../bin/oscar64 maze3d.c -n +../../bin/oscar64 missile.c -O3 -n +../../bin/oscar64 breakout.c -n +../../bin/oscar64 connectfour.c -n +../../bin/oscar64 hscrollshmup.c -O2 -n + diff --git a/oscar64/samples/games/connectfour.c b/oscar64/samples/games/connectfour.c new file mode 100644 index 0000000..c7e9bc8 --- /dev/null +++ b/oscar64/samples/games/connectfour.c @@ -0,0 +1,819 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Include charset and sprite resources + +char charset[2048] = { + #embed "../resources/connect4chars.bin" +}; + +char spriteset[2048] = { + #embed "../resources/connect4sprites.bin" +}; + +// Address ov video data + +byte * const Screen = (byte *)0xc800; +byte * const Font = (byte *)0xd000; +byte * const Color = (byte *)0xd800; +byte * const Sprites = (byte *)0xd800; + +// Values for player 1 pieces and player 2 pieces. +// The values 1 and 5 are selected so that the sum of the +// values in a row of four will add up to a unique value +// in the range of 0..24 for ranking + +#define PLAYER1_INC 1 +#define PLAYER2_INC 5 + +// Current state of the board + +struct Board +{ + char fcols[48]; // Pieces in the 6x7 grid using 48 to have a multiplier of 8 + char ffree[7]; // Number of free places in each column +} board; + +enum GameState +{ + GS_READY, // Getting ready + + GS_PLAYER_MOVE, // The players move + GS_COMPUTER_MOVE, // The computers move + + GS_PLAYER_WIN, // The player wins + GS_COMPUTER_WIN, // The computer wins + GS_GAME_OVER // No more moves +}; + +// Draw the board into the CharWin using the small +// character set + +void board_draw(CharWin * cwin) +{ + for(char y=0; y<6; y++) + { + for(char x=0; x<7; x++) + { + if (board.fcols[8 * y + x] == PLAYER1_INC) + cwin_putat_char_raw(cwin, x, y, 82, 10); + else if (board.fcols[8 * y + x] == PLAYER2_INC) + cwin_putat_char_raw(cwin, x, y, 82, 15); + else + cwin_putat_char_raw(cwin, x, y, 82, 8); + } + + // Right most column + + cwin_putat_char_raw(cwin, 7, y, 83, 8); + } +} + +// Draw a big piece or place with the given color, the +// color 0 denotes an empty place. Empty places use different +// tiles to allow sprite priority to obscure the falling pieces + +void board_draw_item(CharWin * cwin, char x, char y, char c) +{ + char cx = x * 3 + 1, cy = y * 3 + 2; + + if (c) + { + cwin_putat_char_raw(cwin, cx + 0, cy + 0, 73, c); + cwin_putat_char_raw(cwin, cx + 1, cy + 0, 74, c); + cwin_putat_char_raw(cwin, cx + 2, cy + 0, 75, c); + + cwin_putat_char_raw(cwin, cx + 0, cy + 1, 76, c); + cwin_putat_char_raw(cwin, cx + 1, cy + 1, 77, c); + cwin_putat_char_raw(cwin, cx + 2, cy + 1, 78, c); + + cwin_putat_char_raw(cwin, cx + 0, cy + 2, 79, c); + cwin_putat_char_raw(cwin, cx + 1, cy + 2, 80, c); + cwin_putat_char_raw(cwin, cx + 2, cy + 2, 81, c); + } + else + { + cwin_putat_char_raw(cwin, cx + 0, cy + 0, 64, 14); + cwin_putat_char_raw(cwin, cx + 1, cy + 0, 65, 14); + cwin_putat_char_raw(cwin, cx + 2, cy + 0, 66, 14); + + cwin_putat_char_raw(cwin, cx + 0, cy + 1, 67, 14); + cwin_putat_char_raw(cwin, cx + 1, cy + 1, 68, 14); + cwin_putat_char_raw(cwin, cx + 2, cy + 1, 69, 14); + + cwin_putat_char_raw(cwin, cx + 0, cy + 2, 70, 14); + cwin_putat_char_raw(cwin, cx + 1, cy + 2, 71, 14); + cwin_putat_char_raw(cwin, cx + 2, cy + 2, 72, 14); + } +} + +// Draw the big board + +void board_draw_main(CharWin * cwin) +{ + for(char y=0; y<6; y++) + { + for(char x=0; x<7; x++) + { + if (board.fcols[8 * y + x] == PLAYER1_INC) + board_draw_item(cwin, x, y, 10); + else if (board.fcols[8 * y + x] == PLAYER2_INC) + board_draw_item(cwin, x, y, 15); + else + board_draw_item(cwin, x, y, 0); + } + } +} + +// Draw the decoration of the big board + +void board_init_main(CharWin * cwin) +{ + // Frame around the board + + cwin_putat_char_raw(cwin, 0, 1, 84, 14); + cwin_fill_rect_raw(cwin, 1, 1, 21, 1, 85, 14); + cwin_putat_char_raw(cwin, 22, 1, 86, 14); + cwin_fill_rect_raw(cwin, 0, 2, 1, 18, 87, 14); + cwin_fill_rect_raw(cwin, 22, 2, 1, 18, 88, 14); + cwin_putat_char_raw(cwin, 0, 20, 89, 14); + cwin_fill_rect_raw(cwin, 1, 20, 21, 1, 90, 14); + cwin_putat_char_raw(cwin, 22, 20, 91, 14); + + // Fields in the board + + for(char x=0; x<7; x++) + cwin_putat_char_raw(cwin, 3 * x + 2, 0, '1' + x, 1); + + board_draw_main(cwin); +} + +// Play animation of a dropped piece + +void item_drop_anim(CharWin * cwin, char x, char y, char c) +{ + // Initial position + + int ix = (cwin->sx + 3 * x + 1) * 8 + 24, iy = (cwin->sy + 2) * 8 + 20; + + // Show sprite at start position + spr_set(0, true, ix, iy, 97, c, true, false, false); + + // Set sprite priority to be behind background + vic.spr_priority = 0x01; + + // Speed and target position, using four fractional bits + int vy = 0, aiy = iy * 16, ty = ((cwin->sy + y * 3 + 2) * 8 + 50) * 16; + + // Bounce three times back when reaching bottom + char bounce = 3; + + // Loop through the bounces + while (bounce > 0) + { + // Let gravity do its thing of accelerating + vy += 3; + + // Add vertical velocity to position + aiy += vy; + + // Reached the bottom yet? + if (aiy > ty) + { + // Reflect position + aiy = 2 * ty - aiy; + + // Reflect speed + vy = - vy * 5 >> 4; + + // One bounce down + bounce--; + } + + // Move sprite and wait for display + spr_move(0, ix, aiy >> 4); + vic_waitFrame(); + } +} + +// Play column selection animation + +void column_select_anim(CharWin * cwin, char x0, char x1, char c) +{ + // Start position + int ix = (cwin->sx + 3 * x0 + 1) * 8 + 24, iy = (cwin->sy + 2) * 8 + 20; + + // Target position + int tx = (cwin->sx + 3 * x1 + 1) * 8 + 24; + + // Show sprite at start position + spr_set(0, true, ix, iy, 97, c, true, false, false); + + // Set sprite priority to be behind background + vic.spr_priority = 0x01; + + // Not at target position already? + if (ix != tx) + { + // Eight frames for movement + for(int i=1; i<=8; i++) + { + vic_waitFrame(); + // Move sprite along path + spr_move(0, (ix * (8 - i) + tx * i) >> 3, iy); + } + } +} + +// Initialize the board to empty + +void board_init(void) +{ + // Column has six free slots + for(char x=0; x<7; x++) + board.ffree[x] = 6; + + // Clear all slots + for(char i=0; i<48; i++) + board.fcols[i] = 0; +} + +// Drop one piece of player p down column x + +inline bool board_drop(char x, bool p) +{ + // Check for free space + if (board.ffree[x]) + { + // Fill one slot + char y = --(board.ffree[x]); + board.fcols[8 * y + x] = p ? PLAYER2_INC : PLAYER1_INC; + + // Success + return true; + } + return false; +} + +// Remove the top piece from column x + +inline void board_undrop(char x) +{ + // Free one slot + char y = (board.ffree[x])++; + board.fcols[8 * y + x] = 0x00; +} + +// Score for rows with 0, 1, 2, and 3 elements of same color + +static const int score[5] = {0, 1, 8, 128, 10000}; + +// Score index is calculated (25 * rv + 5 * p1 + p2) with rv the +// value range of the row, p1 and p2 number of pieces from player 1 or 2 + +int fscore[125]; + +// Indices of the board positions of four slots of each of the 69 +// 4 slot rows in the board. The fifth value is the value range +// of the row + +char frows[5][69]; + +#pragma align(fscore, 256) + +// Tables for opening library for computer move 1, 2, 3 and 4 + +const char open1 = 3; +const char open2[4 * 7] = +{ + 4, 3, 3, 3, 4, 3, 3, 3, 5, 3, 3, 4, 3, 3, 3, 3, 2, 3, 2, 2, 2, 4, 2, 4, 3, 2, 4, 2 +}; + +const char open3[4 * 7 * 7] = { + 0, 5, 2, 2, 5, 3, 2, 5, 4, 2, 4, 3, 3, 5, 2, 2, 3, 2, 3, 2, 2, 4, 5, 2, 4, 4, 3, 2, + 3, 3, 3, 3, 3, 3, 4, 1, 3, 2, 5, 3, 2, 1, 2, 5, 2, 2, 4, 1, 4, 5, 4, 2, 4, 3, 3, 5, + 4, 1, 3, 4, 4, 4, 3, 2, 1, 3, 2, 3, 2, 3, 5, 5, 1, 5, 4, 3, 3, 3, 4, 3, 3, 5, 3, 3, + 3, 5, 2, 3, 4, 1, 3, 5, 4, 3, 1, 4, 3, 5, 2, 2, 3, 2, 3, 2, 2, 2, 1, 3, 2, 3, 2, 3, + 3, 1, 3, 2, 2, 3, 3, 2, 1, 3, 2, 2, 2, 2, 3, 3, 3, 2, 1, 3, 3, 3, 3, 1, 3, 3, 2, 3, + 2, 3, 3, 3, 3, 3, 3, 3, 2, 1, 2, 3, 4, 3, 2, 4, 3, 2, 2, 2, 2, 1, 0, 2, 4, 3, 4, 4, + 4, 4, 1, 2, 5, 2, 2, 2, 2, 3, 2, 4, 6, 5, 4, 4, 4, 4, 3, 2, 4, 3, 2, 3, 4, 5, 4, 3, +}; + +const char open4[4 * 7 * 7 * 7] = { + 2, 5, 2, 2, 5, 3, 2, 2, 2, 6, 2, 2, 2, 2, 0, 4, 4, 3, 2, 3, 3, 5, 5, 5, 5, 5, 1, 1, + 2, 2, 6, 2, 2, 2, 2, 0, 4, 4, 4, 3, 5, 4, 1, 5, 1, 1, 1, 1, 1, 0, 4, 2, 4, 3, 4, 5, + 5, 1, 4, 5, 5, 3, 2, 3, 3, 1, 1, 1, 1, 3, 5, 5, 4, 5, 5, 4, 4, 3, 3, 3, 1, 3, 3, 3, + 3, 3, 3, 1, 3, 3, 3, 5, 4, 2, 5, 3, 1, 4, 0, 3, 6, 2, 3, 3, 3, 3, 3, 1, 1, 1, 1, 3, + 3, 3, 3, 2, 3, 3, 3, 2, 1, 6, 2, 4, 5, 2, 3, 3, 3, 2, 3, 3, 3, 3, 1, 5, 5, 3, 3, 3, + 3, 3, 0, 2, 3, 3, 3, 0, 5, 2, 2, 5, 1, 2, 4, 4, 2, 4, 5, 4, 2, 2, 2, 3, 2, 2, 2, 2, + 2, 5, 5, 2, 2, 1, 2, 4, 3, 4, 4, 3, 3, 4, 1, 3, 2, 1, 4, 4, 1, 4, 5, 4, 4, 4, 2, 4, + 0, 3, 3, 4, 3, 3, 3, 3, 3, 3, 1, 3, 5, 3, 3, 3, 3, 2, 2, 4, 3, 4, 3, 4, 4, 3, 3, 4, + 3, 3, 2, 3, 3, 5, 6, 3, 5, 4, 5, 5, 3, 3, 3, 3, 2, 3, 3, 3, 5, 0, 5, 3, 4, 4, 3, 2, + 3, 3, 3, 1, 3, 3, 3, 3, 1, 5, 5, 3, 3, 3, 3, 3, 2, 1, 4, 3, 1, 3, 3, 3, 4, 3, 3, 3, + 2, 2, 3, 2, 5, 5, 4, 2, 5, 3, 1, 4, 2, 1, 0, 2, 4, 4, 3, 2, 4, 5, 4, 2, 5, 3, 1, 4, + 3, 3, 0, 2, 3, 3, 3, 4, 2, 4, 4, 2, 2, 4, 3, 3, 3, 4, 6, 3, 3, 2, 5, 3, 1, 4, 2, 1, + 2, 2, 3, 2, 2, 4, 6, 0, 4, 2, 4, 3, 4, 5, 5, 1, 4, 5, 5, 3, 2, 3, 3, 1, 1, 1, 1, 3, + 5, 5, 4, 5, 5, 4, 4, 3, 3, 3, 1, 3, 3, 3, 3, 3, 3, 1, 3, 3, 3, 5, 4, 2, 5, 3, 1, 4, + 2, 2, 6, 2, 2, 2, 2, 4, 4, 3, 4, 4, 4, 3, 4, 1, 3, 5, 3, 3, 3, 2, 2, 6, 2, 2, 2, 2, + 5, 1, 3, 3, 5, 4, 3, 2, 2, 6, 2, 2, 2, 2, 4, 1, 3, 5, 3, 1, 3, 3, 3, 1, 1, 1, 1, 3, + 6, 2, 3, 1, 4, 5, 4, 3, 3, 3, 2, 3, 3, 3, 1, 2, 3, 1, 4, 1, 1, 3, 3, 3, 4, 3, 2, 3, + 1, 3, 1, 1, 4, 2, 1, 3, 3, 3, 2, 3, 3, 3, 4, 4, 2, 4, 5, 4, 2, 4, 1, 6, 4, 4, 4, 2, + 1, 3, 2, 4, 3, 1, 1, 4, 4, 4, 4, 4, 4, 2, 3, 3, 4, 4, 3, 3, 4, 3, 5, 3, 1, 3, 1, 3, + 5, 2, 2, 5, 4, 3, 5, 3, 3, 3, 1, 3, 5, 3, 3, 1, 4, 4, 5, 3, 3, 3, 1, 3, 2, 2, 2, 3, + 3, 3, 4, 4, 3, 3, 4, 3, 5, 3, 3, 5, 5, 3, 5, 4, 2, 5, 5, 3, 5, 3, 3, 3, 1, 1, 5, 3, + 3, 3, 3, 1, 3, 3, 3, 5, 1, 1, 3, 4, 3, 3, 1, 3, 1, 1, 4, 2, 1, 5, 5, 2, 1, 4, 1, 1, + 5, 4, 2, 5, 5, 3, 5, 3, 3, 2, 3, 5, 5, 1, 3, 3, 3, 5, 3, 3, 3, 5, 4, 2, 5, 3, 1, 4, + 2, 1, 1, 4, 3, 4, 4, 3, 3, 3, 2, 3, 3, 3, 5, 3, 2, 5, 4, 3, 3, 3, 3, 3, 1, 1, 5, 3, + 3, 3, 3, 5, 3, 3, 3, 4, 3, 2, 2, 3, 1, 6, 0, 3, 6, 2, 3, 3, 3, 3, 3, 1, 1, 1, 1, 3, + 3, 3, 3, 2, 3, 3, 3, 2, 1, 6, 2, 4, 5, 2, 3, 3, 3, 2, 3, 3, 3, 3, 1, 5, 5, 3, 3, 3, + 3, 3, 0, 2, 3, 3, 3, 3, 3, 1, 1, 1, 1, 3, 6, 2, 3, 1, 4, 5, 4, 3, 3, 3, 2, 3, 3, 3, + 1, 2, 3, 1, 4, 1, 1, 3, 3, 3, 4, 3, 2, 3, 1, 3, 1, 1, 4, 2, 1, 3, 3, 3, 2, 3, 3, 3, + 3, 3, 3, 4, 3, 3, 3, 2, 3, 1, 3, 0, 1, 1, 3, 3, 3, 3, 4, 3, 3, 3, 3, 3, 3, 2, 2, 3, + 4, 4, 4, 2, 2, 3, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 3, 2, 2, 2, 2, + 1, 3, 2, 4, 3, 1, 1, 0, 1, 2, 2, 4, 5, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 3, 3, 2, 2, 3, 3, 2, 3, 3, 2, 2, 3, 2, 3, 1, 3, 4, 3, 3, 3, 1, 1, 4, 4, 2, 4, 2, + 3, 4, 3, 4, 3, 4, 4, 3, 2, 3, 2, 3, 3, 3, 1, 2, 2, 2, 4, 4, 2, 3, 4, 4, 4, 3, 5, 3, + 3, 2, 4, 4, 3, 3, 3, 3, 1, 5, 5, 3, 3, 3, 1, 3, 1, 1, 4, 2, 1, 3, 1, 1, 3, 3, 1, 3, + 2, 3, 3, 2, 2, 3, 3, 3, 4, 4, 4, 3, 5, 3, 3, 3, 1, 2, 2, 5, 3, 3, 1, 3, 5, 3, 3, 3, + 1, 3, 3, 3, 4, 3, 3, 3, 3, 1, 1, 2, 1, 3, 0, 1, 3, 3, 4, 3, 3, 2, 3, 3, 2, 2, 3, 2, + 3, 2, 4, 4, 3, 3, 3, 3, 1, 3, 5, 3, 3, 3, 3, 3, 3, 2, 3, 3, 6, 0, 2, 1, 2, 3, 4, 4, + 4, 1, 3, 3, 4, 4, 2, 4, 3, 2, 4, 3, 3, 4, 3, 3, 2, 4, 4, 4, 3, 3, 2, 1, 4, 3, 3, 3, + 4, 2, 1, 4, 5, 4, 2, 4, 2, 1, 2, 3, 4, 4, 2, 2, 1, 2, 1, 2, 2, 1, 1, 3, 3, 3, 5, 1, + 2, 5, 3, 2, 5, 3, 3, 2, 3, 1, 1, 1, 6, 2, 1, 4, 4, 1, 4, 6, 5, 2, 2, 1, 6, 6, 2, 4, + 2, 2, 1, 2, 5, 4, 2, 4, 3, 2, 4, 3, 3, 4, 4, 3, 3, 3, 3, 1, 1, 1, 0, 3, 3, 3, 3, 3, + 1, 0, 2, 2, 4, 5, 2, 1, 0, 3, 1, 3, 1, 3, 1, 0, 2, 5, 2, 2, 5, 1, 0, 3, 2, 2, 5, 2, + 2, 2, 1, 4, 4, 4, 2, 2, 4, 0, 2, 4, 2, 4, 5, 4, 2, 1, 5, 4, 1, 2, 1, 2, 2, 2, 4, 2, + 5, 2, 1, 5, 4, 2, 1, 2, 4, 2, 4, 6, 2, 4, 4, 2, 2, 2, 5, 4, 4, 4, 1, 4, 2, 3, 6, 5, + 1, 4, 4, 1, 4, 6, 5, 3, 5, 3, 5, 3, 6, 5, 2, 1, 2, 4, 4, 6, 5, 3, 3, 3, 3, 3, 6, 5, + 5, 5, 3, 3, 3, 3, 2, 2, 3, 3, 2, 4, 3, 2, 4, 2, 1, 4, 5, 4, 2, 2, 4, 0, 0, 5, 4, 2, + 1, 0, 2, 5, 2, 2, 5, 4, 0, 5, 5, 5, 3, 4, 3, 3, 1, 4, 3, 1, 2, 5, 1, 3, 3, 3, 5, 5, + 2, 2, 5, 4, 5, 4, 4, 2, 2, 3, 4, 5, 4, 2, 2, 2, 1, 2, 5, 4, 2, 3, 3, 3, 2, 5, 4, 3, + 3, 2, 2, 2, 4, 3, 3, 2, 3, 3, 2, 4, 3, 2, 4, 2, 2, 3, 3, 5, 2, 2, 2, 3, 4, 5, 4, 6, +}; + +// Init the ai tables +void ai_init(void) +{ + // Loop over 5 value ranges, and 0 to 4 pieces of each player + // in a row + + for(char k=0; k<5; k++) + { + for(char i=0; i<5; i++) + { + for(char j=0; j<5; j++) + { + if (i && j) + fscore[25 * k + PLAYER2_INC * i + j] = 0; + else if (i == 4 || j == 4) + fscore[25 * k + PLAYER2_INC * i + j] = score[i] - score[j]; + else + fscore[25 * k + PLAYER2_INC * i + j] = (score[i] - score[j]) * (1 + k); + } + } + } + + // Create the indices of all 69 rows + + char p = 0; + + // Vertical rows + for(char x=0; x<7; x++) + { + for(char y=0; y<3; y++) + { + frows[0][p] = x + 8 * y; + frows[1][p] = x + 8 * y + 8; + frows[2][p] = x + 8 * y + 16; + frows[3][p] = x + 8 * y + 24; + frows[4][p] = 25 * (y + 1); + p++; + } + } + + for(char x=0; x<4; x++) + { + // Horizontal rows + for(char y=0; y<6; y++) + { + frows[0][p] = x + 8 * y; + frows[1][p] = x + 8 * y + 1; + frows[2][p] = x + 8 * y + 2; + frows[3][p] = x + 8 * y + 3; + frows[4][p] = y > 0 ? 25 * (y - 1) : 0; + p++; + } + + // Diagonal down rows + for(char y=0; y<3; y++) + { + frows[0][p] = x + 8 * y; + frows[1][p] = x + 8 * y + 9; + frows[2][p] = x + 8 * y + 18; + frows[3][p] = x + 8 * y + 27; + frows[4][p] = 25 * (y + 1); + p++; + } + + // Diagonal up rows + for(char y=3; y<6; y++) + { + frows[0][p] = x + 8 * y; + frows[1][p] = x + 8 * y - 7; + frows[2][p] = x + 8 * y - 14; + frows[3][p] = x + 8 * y - 21; + frows[4][p] = 25 * (y - 2); + p++; + } + } +} + +// Evaluate the current board state for the player + +int board_eval(bool player) +{ + int score = 0; + + // Loop through all rows + for(char r=0; r<69; r++) + { + // Build score index for row + char sum = + board.fcols[frows[0][r]] + + board.fcols[frows[1][r]] + + board.fcols[frows[2][r]] + + board.fcols[frows[3][r]] + + frows[4][r]; + + // Help compiler to avoid two byte pointer arithmetic + __assume(sum < 128); + + // Add score to board score + score += fscore[sum]; + } + + // Check if there was a row with four winning pieces and + // normalize to +/- 32000 then + + if (score < -8192) + score = -32000; + else if (score >= 8192) + score = 32000; + + // Invert score for other player + + return player ? score : -score; +} + +CharWin cwt, cws; +CharWin cw, cwm; + +char buff[20]; + +static const char optx[7] = {3, 2, 4, 1, 5, 0, 6}; + +// Find a good move for the player using alpha/beta pruning +// see: https://en.wikipedia.org/wiki/Alpha-beta_pruning + +int board_check(char level, int alpha, int beta, bool player) +{ + int best = alpha; + bool checked = false; + + // Current score to cutof early if clear win condition + int val = board_eval(player) + level; + + // Best move + char zx = 0; + + // Check for end of search + if (level < 5 && val < 5000 && val > -5000) + { + // Update current check display at level 3 + if (level == 3) + { + board_draw(&cwt); + itoa(val, buff, 10); + cwin_fill_rect(&cwt, 0, 6, 7, 1, ' ', 1); + cwin_putat_string(&cwt, 0, 6, buff, 1); + } + + // Check all seven columns + for(char i=0; i<7; i++) + { + // Start check in the center, due to higher value of + // pieces there + + char ix = optx[i]; + + // Try to drop a piece + + if (board_drop(ix, player)) + { + // We still have a move + checked = true; + + // Check score of position + int score = - board_check(level + 1, -beta, -best, !player); + + // Better than what we have so far + if (score > best) + { + // Remember + best = score; + zx = ix; + + // Update best move if on level zero + if (level == 0) + { + board_draw(&cw); + itoa(best, buff, 10); + cwin_fill_rect(&cw, 0, 6, 7, 1, ' ', 1); + cwin_putat_string(&cw, 0, 6, buff, 1); + } + + // Check for beta pruning + if (best > beta) + { + board_undrop(ix); + break; + } + } + + // Undo move + board_undrop(ix); + } + } + } + + // Return new position if first move level + if (level == 0) + return zx; + else if (checked) + return best; + else + return val; +} + +// Return a move from the opening library if available + +char board_opening(char step, char * moves) +{ + switch (step) + { + case 1: + return open1; + case 2: + if (moves[0] < 4) + return open2[moves[0] * 7 + moves[1]]; + else + return 6 - open2[(6 - moves[0]) * 7 + (6 - moves[1])]; + + case 3: + if (moves[0] < 4) + return open3[moves[0] * 49 + moves[1] * 7 + moves[2]]; + else + return 6 - open3[(6 - moves[0]) * 49 + (6 - moves[1]) * 7 + (6 - moves[2])]; + + case 4: + if (moves[0] < 4) + return open4[moves[0] * 343 + moves[1] * 49 + moves[2] * 7 + moves[3]]; + else + return 6 - open4[(6 - moves[0]) * 343 + (6 - moves[1]) * 49 + (6 - moves[2]) * 7 + (6 - moves[3])]; + + default: + return 0xff; + } +} + +// Current state of the game +struct Game +{ + GameState state; // State + char count; // Auto continue counter + char posx; // Column osition of new piece + char step; // Number of player moves + char moves[21]; // Pieces placed by player + +} TheGame; + +// Set new game state + +void game_state(GameState state) +{ + // Set new state + TheGame.state = state; + + // Clear status line + + cwin_fill_rect(&cws, 0, 0, 40, 1, ' ', 1); + + switch (state) + { + case GS_READY: + // Start play in one second + TheGame.count = 60; + TheGame.step = 0; + + // Clear the board + board_init(); + board_init_main(&cwm); + break; + + case GS_PLAYER_MOVE: + // Place selection sprite in center + TheGame.posx = 3; + column_select_anim(&cwm, 3, 3, 7); + cwin_putat_string(&cws, 0, 0, P"PLAYER MOVE", 7); + break; + + case GS_COMPUTER_MOVE: + cwin_putat_string(&cws, 0, 0, P"COMPUTER MOVE", 2); + break; + + case GS_PLAYER_WIN: + cwin_putat_string(&cws, 0, 0, P"PLAYER WINS", 7); + + // Continue in 4 seconds + TheGame.count = 240; + break; + + case GS_COMPUTER_WIN: + cwin_putat_string(&cws, 0, 0, P"COMPUTER WINS", 2); + + // Continue in 4 seconds + TheGame.count = 240; + break; + + case GS_GAME_OVER: + cwin_putat_string(&cws, 0, 0, P"NO MORE MOVES", 1); + + // Continue in 4 seconds + TheGame.count = 240; + break; + } +} + +// Main game loop + +void game_loop() +{ + char bx = 0xff, cx; + + switch (TheGame.state) + { + case GS_READY: + if (!--TheGame.count) + game_state(GS_PLAYER_MOVE); + break; + + case GS_PLAYER_MOVE: + // Current selection + cx = TheGame.posx; + + // Check for key pressed + if (kbhit()) + { + // Get key + char ch = getch(); + + // Numbers 1 to 7 drop immediate + if (ch >= '1' && ch <= '7') + bx = cx = ch - '1'; + } + else + { + // Check Joystick + joy_poll(0); + + // Move cursor + if (joyx[0] < 0 && cx > 0) + cx--; + else if (joyx[0] > 0 && cx < 6) + cx++; + else if(joyb[0]) + bx = cx; + } + + // Move selection piece + column_select_anim(&cwm, TheGame.posx, cx, 7); + TheGame.posx = cx; + + if (bx != 0xff) + { + // Drop piece into board + if (board_drop(bx, true)) + { + // Remember move + TheGame.moves[TheGame.step++] = bx; + + // Play drop animation + item_drop_anim(&cwm, bx, board.ffree[bx], 7); + + // Show new state of the board + board_draw_main(&cwm); + spr_show(0, false); + + // Check for win condition + if (board_eval(true) == 32000) + game_state(GS_PLAYER_WIN); + else + game_state(GS_COMPUTER_MOVE); + } + } + break; + + case GS_COMPUTER_MOVE: + // Check for opening move or calculate next move + bx = board_opening(TheGame.step, TheGame.moves); + if (bx == 0xff) + bx = board_check(0, -32767, 32767, false); + + // Drop piece into board + if (board_drop(bx, false)) + { + // Play drop animation + item_drop_anim(&cwm, bx, board.ffree[bx], 2); + + // Show new state of the board + board_draw_main(&cwm); + spr_show(0, false); + + // Check for loss or draw condition + if (board_eval(false) == 32000) + game_state(GS_COMPUTER_WIN); + else if (TheGame.step == 21) + game_state(GS_GAME_OVER); + else + game_state(GS_PLAYER_MOVE); + } + break; + + case GS_PLAYER_WIN: + case GS_COMPUTER_WIN: + case GS_GAME_OVER: + if (!--TheGame.count) + game_state(GS_READY); + break; + } +} + + +int main(void) +{ + mmap_trampoline(); + + // Install character set + mmap_set(MMAP_RAM); + memcpy(Font, charset, 2048); + memcpy(Sprites, spriteset, 256); + mmap_set(MMAP_NO_BASIC); + + // Switch screen + vic_setmode(VICM_TEXT_MC, Screen, Font); + + spr_init(Screen); + + // Change colors + vic.color_border = VCOL_BLUE; + vic.color_back = VCOL_BLACK; + vic.color_back1 = VCOL_BLUE; + vic.color_back2 = VCOL_WHITE; + + vic.spr_mcolor0 = VCOL_DARK_GREY; + vic.spr_mcolor1 = VCOL_WHITE; + + // Clear screen + memset(Screen, 96, 1000); + memset(Color, 15, 1000); + + // Prepare char wins + cwin_init(&cws, Screen, 5, 0, 30, 1); + cwin_init(&cwm, Screen, 2, 3, 28, 19); + cwin_init(&cw, Screen, 30, 6, 8, 7); + cwin_init(&cwt, Screen, 30, 15, 8, 7); + + // Init AI + ai_init(); + + game_state(GS_READY); + + for(;;) + { + game_loop(); + vic_waitFrame(); + } + + + return 0; +} diff --git a/oscar64/samples/games/connectfour.png b/oscar64/samples/games/connectfour.png new file mode 100644 index 0000000..daab7e0 Binary files /dev/null and b/oscar64/samples/games/connectfour.png differ diff --git a/oscar64/samples/games/hscrollshmup.c b/oscar64/samples/games/hscrollshmup.c new file mode 100644 index 0000000..236494d --- /dev/null +++ b/oscar64/samples/games/hscrollshmup.c @@ -0,0 +1,1182 @@ +#include +#include +#include +#include +#include +#include +#include