diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..1a6397284 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,15 @@ +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 4 +trim_trailing_whitespace = true +insert_final_newline = true +guidelines = 80, 120 + +[*.{c,h}] +cpp_new_line_before_open_brace_block=same_line +cpp_new_line_before_open_brace_function=new_line +cpp_space_before_function_open_parenthesis=insert +cpp_new_line_before_else=false diff --git a/.github/checks/lastline.sh b/.github/checks/lastline.sh index d80d2fb57..d243a01e1 100755 --- a/.github/checks/lastline.sh +++ b/.github/checks/lastline.sh @@ -9,7 +9,7 @@ nl=' ' nl=$'\n' r1="${nl}$" -FILES=`find $CHECK_PATH -type f \( -name \*.inc -o -name Makefile -o -name \*.cfg -o -name \*.\[chs\] -o -name \*.mac -o -name \*.asm -o -name \*.sgml \) -print | while read f; do +FILES=`find $CHECK_PATH -type f -size +0 \( -name \*.inc -o -name Makefile -o -name \*.cfg -o -name \*.\[chs\] -o -name \*.mac -o -name \*.asm -o -name \*.sgml \) -print | while read f; do t=$(tail -c2 $f; printf x) [[ ${t%x} =~ $r1 ]] || echo "$f" done` diff --git a/.github/checks/spaces.sh b/.github/checks/spaces.sh index 945e9acc3..e231f6c2d 100755 --- a/.github/checks/spaces.sh +++ b/.github/checks/spaces.sh @@ -5,7 +5,7 @@ CHECK_PATH=. cd $SCRIPT_PATH/../../ -FILES=`find $CHECK_PATH -type f \( -name \*.inc -o -name Makefile -o -name \*.cfg -o -name \*.\[chs\] -o -name \*.mac -o -name \*.asm -o -name \*.sgml \) -print | grep -v "libwrk/" | grep -v "testwrk/" | xargs grep -l ' $'` +FILES=`find $CHECK_PATH -type f \( -name \*.inc -o -name Makefile -o -name \*.cfg -o -name \*.\[chs\] -o -name \*.mac -o -name \*.asm -o -name \*.sgml \) -print | grep -v "test/" | grep -v "libwrk/" | grep -v "testwrk/" | xargs grep -l ' $'` cd $OLDCWD diff --git a/.github/checks/tabs.sh b/.github/checks/tabs.sh index 1c32def17..80dac3f2d 100755 --- a/.github/checks/tabs.sh +++ b/.github/checks/tabs.sh @@ -5,7 +5,7 @@ CHECK_PATH=. cd $SCRIPT_PATH/../../ -FILES=`find $CHECK_PATH -type f \( \( -name \*.inc -a \! -name Makefile.inc \) -o -name \*.cfg -o -name \*.\[chs\] -o -name \*.mac -o -name \*.asm -o -name \*.sgml \) -print | grep -v "libwrk/" | grep -v "testwrk/" | xargs grep -l $'\t'` +FILES=`find $CHECK_PATH -type f \( \( -name \*.inc -a \! -name Makefile.inc \) -o -name \*.cfg -o -name \*.\[chs\] -o -name \*.mac -o -name \*.asm -o -name \*.sgml \) -print | grep -v "test/" | grep -v "libwrk/" | grep -v "testwrk/" | xargs grep -l $'\t'` cd $OLDCWD diff --git a/.github/workflows/build-on-pull-request.yml b/.github/workflows/build-on-pull-request.yml index 55be5db1e..7b762844b 100644 --- a/.github/workflows/build-on-pull-request.yml +++ b/.github/workflows/build-on-pull-request.yml @@ -19,7 +19,7 @@ jobs: - shell: bash run: git config --global core.autocrlf input - name: Checkout Source - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Do some simple style checks shell: bash @@ -35,7 +35,7 @@ jobs: run: make -j2 lib QUIET=1 - name: Run the regression tests. shell: bash - run: make test QUIET=1 + run: make -j2 test QUIET=1 - name: Test that the samples can be built. run: make -C samples platforms - name: Test that the targettest programs can be built. @@ -44,7 +44,7 @@ jobs: shell: bash run: make -j2 doc - name: Upload a documents snapshot. - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: docs path: ./html @@ -54,7 +54,7 @@ jobs: make -j2 bin USER_CFLAGS=-Werror CROSS_COMPILE=x86_64-w64-mingw32- build_windows: - name: Build (Windows) + name: Build and Test (Windows) runs-on: windows-latest steps: @@ -62,13 +62,31 @@ jobs: run: git config --global core.autocrlf input - name: Checkout Source - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Add msbuild to PATH - uses: microsoft/setup-msbuild@v1.1 + uses: microsoft/setup-msbuild@v2 - - name: Build app (debug) - run: msbuild src\cc65.sln -t:rebuild -property:Configuration=Debug + - name: Build app (x86 debug) + run: msbuild src\cc65.sln -t:rebuild -property:Configuration=Debug -property:Platform=Win32 - - name: Build app (release) - run: msbuild src\cc65.sln -t:rebuild -property:Configuration=Release + - name: Build app (x86 release) + run: msbuild src\cc65.sln -t:rebuild -property:Configuration=Release -property:Platform=Win32 + + - name: Build app (x64 release) + run: msbuild src\cc65.sln -t:rebuild -property:Configuration=Debug -property:Platform=x64 + + - name: Build app (x64 release) + run: msbuild src\cc65.sln -t:rebuild -property:Configuration=Release -property:Platform=x64 + + - name: Build utils (MinGW) + shell: cmd + run: make -j2 util SHELL=cmd + + - name: Build the platform libraries (make lib) + shell: cmd + run: make -j2 lib QUIET=1 SHELL=cmd + + - name: Run the regression tests (make test) + shell: cmd + run: make -j2 test QUIET=1 SHELL=cmd diff --git a/.github/workflows/snapshot-on-push-master.yml b/.github/workflows/snapshot-on-push-master.yml index 2aedb0e25..42794f10b 100644 --- a/.github/workflows/snapshot-on-push-master.yml +++ b/.github/workflows/snapshot-on-push-master.yml @@ -18,10 +18,10 @@ jobs: run: git config --global core.autocrlf input - name: Checkout Source - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Add msbuild to PATH - uses: microsoft/setup-msbuild@v1.1 + uses: microsoft/setup-msbuild@v2 - name: Build app (debug) run: msbuild src\cc65.sln -t:rebuild -property:Configuration=Debug @@ -44,7 +44,7 @@ jobs: - shell: bash run: git config --global core.autocrlf input - name: Checkout Source - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Do some simple style checks shell: bash @@ -59,7 +59,7 @@ jobs: run: make -j2 lib QUIET=1 - name: Run the regression tests. shell: bash - run: make test QUIET=1 + run: make -j2 test QUIET=1 - name: Test that the samples can be built. shell: bash run: make -j2 samples @@ -86,18 +86,18 @@ jobs: mv cc65.zip cc65-snapshot-win32.zip - name: Upload a 32-bit Snapshot Zip - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: cc65-snapshot-win32 path: cc65-snapshot-win32.zip - name: Upload a 64-bit Snapshot Zip - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: cc65-snapshot-win64 path: cc65-snapshot-win64.zip - name: Get the online documents repo. - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: repository: cc65/doc # this token will expire, if it does, generate a new one as decribed in https://github.com/cc65/cc65/issues/2065 @@ -120,7 +120,7 @@ jobs: - name: Package offline documents. run: 7z a cc65-snapshot-docs.zip ./html/*.* - name: Upload a Documents Snapshot Zip - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: cc65-snapshot-docs path: cc65-snapshot-docs.zip @@ -135,6 +135,15 @@ jobs: port: ${{ secrets.SSH_PORT }} user: ${{ secrets.SSH_USER }} key: ${{ secrets.SSH_KEY }} + - name: Upload 64-bit Windows snapshot to sourceforge + uses: nogsantos/scp-deploy@master + with: + src: cc65-snapshot-win64.zip + host: ${{ secrets.SSH_HOST }} + remote: ${{ secrets.SSH_DIR }} + port: ${{ secrets.SSH_PORT }} + user: ${{ secrets.SSH_USER }} + key: ${{ secrets.SSH_KEY }} - name: Upload documents snapshot to sourceforge uses: nogsantos/scp-deploy@master with: diff --git a/.github/workflows/windows-test-scheduled.yml b/.github/workflows/windows-test-scheduled.yml index 451b37f79..fa22473f4 100644 --- a/.github/workflows/windows-test-scheduled.yml +++ b/.github/workflows/windows-test-scheduled.yml @@ -30,7 +30,7 @@ jobs: run: mkdir ~/.cache-sha - name: Cache SHA - uses: actions/cache@v3 + uses: actions/cache@v4 id: check-sha with: path: ~/.cache-sha @@ -43,11 +43,11 @@ jobs: - name: Checkout source if: steps.check-sha.outputs.cache-hit != 'true' - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Add msbuild to PATH if: steps.check-sha.outputs.cache-hit != 'true' - uses: microsoft/setup-msbuild@v1.1 + uses: microsoft/setup-msbuild@v2 - name: Build app (MSVC debug) if: steps.check-sha.outputs.cache-hit != 'true' @@ -70,7 +70,7 @@ jobs: - name: Run the regression tests (make test) if: steps.check-sha.outputs.cache-hit != 'true' shell: cmd - run: make test QUIET=1 SHELL=cmd + run: make -j2 test QUIET=1 SHELL=cmd - name: Test that the samples can be built (make samples) if: steps.check-sha.outputs.cache-hit != 'true' diff --git a/Contributing.md b/Contributing.md index 3b355373c..10d687424 100644 --- a/Contributing.md +++ b/Contributing.md @@ -1,186 +1,321 @@ -This document contains all kinds of information that you should know if you want to contribute to the cc65 project. Before you start, please read all of it. If something is not clear to you, please ask - this document is an ongoing effort and may well be incomplete. +Contributing to cc65 +==================== -(''Note:'' The word "must" indicates a requirement. The word "should" indicates a recomendation.) +This document contains all kinds of information that you +should know if you want to contribute to the cc65 project. +Before you start, please read all of it. If something is not +clear to you, please ask - this document is an ongoing effort +and may well be incomplete. -*this is work in progress and is constantly updated - if in doubt, please ask* +Also, before you put a lot of work into implementing +something you want to contribute, please get in touch with +one of the developers and ask if what you are going to do is +actually wanted and has a chance of being merged. Perhaps +someone else is already working on it, or perhaps what you +have in mind is not how we'd expect it to be - talking to us +before you start might save you a lot of work in those cases. -# generally +(''Note:'' The word "must" indicates a requirement. The word + "should" indicates a recomendation.) -* You must obey these rules when contributing new code or documentation to cc65. We are well aware that not all existing code may respect all rules outlined here - but this is no reason for you not to respect them. -* One commit/patch/PR per issue. Do not mix several things unless they are very closely related. -* Sometimes when you make a PR, it may break completely unrelated tests. However, any PR is expected to merge cleanly with no failures. That means in practise that you are expected to fix/update the failing tests if required - for example this might be needed if you make changes to the compiler that changes the format of error- or warning messages. In that case you might have to update some reference files in the testbench. Obviously still check if that is actually the right thing to do ;) +*this is work in progress and is constantly updated - if in +doubt, please ask* + +# Generally + +* You must obey these rules when contributing new code or + documentation to cc65. We are well aware that not all + existing code may respect all rules outlined here - but this + is no reason for you not to respect them. +* One commit/patch/PR per issue. Do not mix several things + unless they are very closely related. +* Sometimes when you make a PR, it may break completely + unrelated tests. However, any PR is expected to merge + cleanly with no failures. That means in practise that you + are expected to fix/update the failing tests if required - + for example this might be needed if you make changes to the + compiler that changes the format of error- or warning + messages. In that case you might have to update some + reference files in the testbench. Obviously still check if + that is actually the right thing to do. ;) # Codestyle rules -## All Sources +## All sources ### Line endings -All files must only contain Unix style 'LF' line endings. Please configure your editors accordingly. +All files must only contain Unix style 'LF' line endings. +Please configure your editors accordingly. ### TABs and spaces -This is an ongoing controversial topic - everyone knows that. However, the following is how we do it :) +This is an ongoing controversial topic - everyone knows +that. However, the following is how we do it :) * TAB characters must be expanded to spaces. -* 4 spaces per indention level (rather than 8) are preferred, especially if there are many different levels. +* 4 spaces per indention level (rather than 8) are + preferred, especially if there are many different levels. * No extra spaces at the end of lines. -* All text files must end with new-line characters. Don't leave the last line "dangling". +* All text files must end with new-line characters. Don't + leave the last line "dangling". -The (bash) scripts used to check the above rules can be found in ```.github/check```. You can also run all checks using ```make check```. +The (bash) scripts used to check the above rules can be +found in ```.github/check```. You can also run all checks +using ```make check```. -### Identifiers and Symbol names +### Identifiers and symbol names -The C Standard defines certain identifiers and symbol names, which we can not use -in our code. Since it is not always obvious which parts of the library code will -actually end up in a linked program, the following applies to ALL of the library. +The C Standard defines certain identifiers and symbol names, +which we can not use in our code. Since it is not always +obvious which parts of the library code will actually end up +in a linked program, the following applies to ALL of the +library. -Any non standard identifier/symbol/function that is exported from source files, -or appears in header files: +Any non standard identifier/symbol/function that is exported +from source files, or appears in header files: -* must not be in the "_symbol" form in C, or "__symbol" form in assembly. -* must start with (at least) two (C Code) or three (assembly code) underscores, unless the symbol appears in a non standard header file. +* must not be in the "_symbol" form in C, or "__symbol" form + in assembly, +* must start with (at least) two (C Code) or three (assembly + code) underscores, unless the symbol appears in a non + standard header file. -This is likely more than the standard dictates us to do - but it is certainly -standard compliant - and easy to remember. +This is likely more than the standard dictates us to do - +but it is certainly standard compliant - and easy to +remember. -Also see the discussion in https://github.com/cc65/cc65/issues/1796 +Also see the discussion in +https://github.com/cc65/cc65/issues/1796 -### misc +### Miscellaneous -* 80 characters is the desired maximum width of files. But, it isn't a "strong" rule; sometimes, you will want to type longer lines, in order to keep the parts of expressions or comments together on the same line. +* 80 characters is the desired maximum width of files. But, + it isn't a "strong" rule; sometimes, you will want to type + longer lines, in order to keep the parts of expressions or + comments together on the same line. * You should avoid typing non-ASCII characters. -* If you change "normal" source code into comments, then you must add a comment about why that code is a comment. -* When you want to create a comment from several lines of code, you should use preprocessor lines, instead of ```/* */``` or "```;```". Example: -
+* If you change "normal" source code into comments, then you
+  must add a comment about why that code is a comment.
+* When you want to create a comment from several lines of
+  code, you should use preprocessor lines, instead of ```/*
+  */``` or "```;```".  Example:
+
+~~~C
 #if 0
-    one ();
-    two ();
-    three = two () + one ();
+  one (); two ();
+  three = two () + one ();
 #endif
-
+~~~ + * You should type upper case characters for hex values. -* When you type zero-page addresses in hexadecimal, you should type two hex characters (after the hex prefix). When you type non-zero-page addresses in hex, you should type four hex characters. -* When you type lists of addresses, it is a good idea to sort them in ascending numerical order. That makes it easier for readers to build mental pictures of where things are in an address space. And, it is easier to see how big the variables and buffers are. Example: -
+* When you type zero-page addresses in hexadecimal, you
+  should type two hex characters (after the hex prefix).
+  When you type non-zero-page addresses in hex, you should
+  type four hex characters.
+* When you type lists of addresses, it is a good idea to
+  sort them in ascending numerical order.  That makes it
+  easier for readers to build mental pictures of where things
+  are in an address space.  And, it is easier to see how big
+  the variables and buffers are.  Example:
+
+~~~asm
 xCoord := $0703
-yCoord := $0705        ; (this address implies that xCoord is 16 bits)
-cmdbuf := $0706        ; (this address implies that yCoord is 8 bits)
-cmdlen := $0786        ; (this address implies that cmdbuf is 128 bytes)
+yCoord := $0705 ; (this address implies that xCoord is 16 bits)
+cmdbuf := $0706 ; (this address implies that yCoord is 8 bits)
+cmdlen := $0786 ; (this address implies that cmdbuf is 128 bytes)
 color  := $0787
-
+~~~ -## C Sources +## C sources -The following is still very incomplete - if in doubt please look at existing sourcefiles and adapt to the existing style +The following is still very incomplete - if in doubt please +look at existing sourcefiles and adapt to the existing style. -* Your files should generally obey the C89 standard, with a few C99 things (this is a bit similar to what cc65 itself supports). The exceptions are: - * use stdint.h for variables that require a certain bit size - * In printf-style functions use the PRIX64 (and similar) macros to deal with 64bit values (from inttypes.h) -This list is not necessarily complete - if in doubt, please ask. +Your files should generally obey the C89 standard, with a +few C99 things (this is a bit similar to what cc65 itself +supports). The exceptions are: + +* Use stdint.h for variables that require a certain bit size +* In printf-style functions use the PRIX64 (and similar) + macros to deal with 64bit values (from inttypes.h) This + list is not necessarily complete - if in doubt, please ask. * We generally have a "no warnings" policy - * Warnings must not be hidden by using typecasts - fix the code instead +* Warnings must not be hidden by using typecasts - fix the + code instead * The normal indentation width should be four spaces. -* You must use ANSI C comments (```/* */```); you must not use C++ comments (```//```). -* When you add functions to an existing file, you should separate them by the same number of blank lines that separate the functions that already are in that file. -* All function declarations must be followed by a comment block that tells at least briefly what the function does, what the parameters are, and what is returned. This comment must sit between the declaration and the function body, like this: -
+* You must use ANSI C comments (```/* */```); you must not
+  use C++ comments (```//```).
+* When you add functions to an existing file, you should
+  separate them by the same number of blank lines that
+  separate the functions that already are in that file.
+* All function declarations must be followed by a comment
+  block that tells at least briefly what the function does,
+  what the parameters are, and what is returned. This comment
+  must sit between the declaration and the function body, like
+  this:
+
+~~~C
 int foo(int bar)
 /* Add 1 to bar, takes bar and returns the result */
 {
     return bar + 1;
 }
-
-* When a function's argument list wraps around to a next line, you should indent that next line by either the normal width or enough spaces to align it with the arguments on the previous line. -* All declarations in a block must be at the beginning of that block. -* You should put a blank line between a list of local variable declarations and the first line of code. -* Always use curly braces even for single statements after ```if```, and the single statement should go into a new line. -* Use "cuddling" braces, ie the opening brace goes in the same line as the ```if```: -
+~~~
+
+* When a function's argument list wraps around to a next
+  line, you should indent that next line by either the
+  normal width or enough spaces to align it with the arguments
+  on the previous line.
+* All declarations in a block must be at the beginning of
+  that block.
+* You should put a blank line between a list of local
+  variable declarations and the first line of code.
+* Always use curly braces even for single statements after
+  ```if```, and the single statement should go into a new
+  line.
+* Use "cuddling" braces, ie the opening brace goes in the
+  same line as the ```if```:
+
+~~~C
 if (foo > 42) {
     bar = 23;
 }
-
-* Should the ```if``` statement be followed by an empty conditional block, there should be a comment telling why this is the case -
-if (check()) {
-    /* nothing happened, do nothing */
-}
-
-* You must separate function names and parameter/argument lists by one space. -* When declaring/defining pointers, you must put the asterisk (```*```) next to the data type, with a space between it and the variable's name. Examples: -
-    int* namedPtr[5];
-    char* nextLine (FILE* f);
-
+~~~ + +* Should the ```if``` statement be followed by an empty + conditional block, there should be a comment telling why + this is the case: + +~~~C +if (check()) { /* nothing happened, do nothing */ } +~~~ + +* You must separate function names and parameter/argument + lists by one space. +* When declaring/defining pointers, you must put the + asterisk (```*```) next to the data type, with a space + between it and the variable's name. Examples: + +~~~C +int* namedPtr[5]; +char* nextLine (FILE* f); +~~~ ### Header files -Headers that belong to the standard library (libc) must conform with the C standard. That means: -* all non standard functions, or functions that only exist in a certain standard, should be in #ifdefs - * the same is true for macros or typedefs -
-#if __CC65_STD__ == __CC65_STD_C99__
-/* stuff that only exists in C99 here */
-#endif
-#if __CC65_STD__ == __CC65_STD_CC65__
-/* non standard stuff here */
-#endif
-
-You can refer to Annex B of the ISO C99 standard ([here](https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf) is the draft). +* All Headers should start with a copyright/license banner +* Function prototypes must be a single line, not contain the redundant + "extern" keyword, and followed by a brief comment that explains what + the function does, and separated from the next prototype by a blank + line: -## Assembly Sources +~~~C +void __fastcall__ cclear (unsigned char length); +/* Clear part of a line (write length spaces). */ -* Op-code mnemonics must have lower-case letters. The names of instruction macroes may have upper-case letters. -* Op-codes must use their official and commonly used mnemonics, ie bcc and bcs and not bgt and blt -* Hexadecimal number constants should be used except where decimal or binary numbers make much more sense in that constant's context. +~~~ + +Headers that belong to the standard library (libc) must +conform with the C standard. That means: + +* All non standard functions, or functions that only exist + in a certain standard, should be in #ifdefs +* The same is true for macros or typedefs. + You can refer to Annex B of the ISO C99 standard + ([here](https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf) + is the draft). Example: + +~~~C +#if __CC65_STD__ == __CC65_STD_C99__ /* stuff that only exists in C99 here */ +#endif +#if __CC65_STD__ == __CC65_STD_CC65__ /* non standard stuff here */ +#endif +~~~ + +## Assembly sources + +* Opcode mnemonics must have lower-case letters. The names + of instruction macroes may have upper-case letters. +* Opcodes must use their official and commonly used + mnemonics, ie 'bcc' and 'bcs' and not 'bgt' and 'blt'. +* Hexadecimal number constants should be used except where + decimal or binary numbers make much more sense in that + constant's context. * Hexadecimal letters should be upper-case. -* When you set two registers or two memory locations to an immediate 16-bit zero, you should use the expressions ```#<$0000``` and ```#>$0000``` (they make it obvious where you are putting the lower and upper bytes). -* If a function is declared to return a char-sized value, it actually must return an integer-sized value. (When cc65 promotes a returned value, it sometimes assumes that the value already is an integer.) This must be done in one of the following ways: -
+* When you set two registers or two memory locations to an
+  immediate 16-bit zero, you should use the expressions
+  ```#<$0000``` and ```#>$0000``` (they make it obvious where
+  you are putting the lower and upper bytes).
+* If a function is declared to return a char-sized value, it
+  actually must return an integer-sized value.  (When cc65
+  promotes a returned value, it sometimes assumes that the value
+  already is an integer.)
+  This must be done in one of the following ways:
+
+~~~asm
     lda #RETURN_VALUE
-    ldx #0 ; return value is char
-
-or, if the value is 0, you can use: -
+    ldx #0 ; Promote char return value
+
+    ; If the value is 0, you can use:
     lda #RETURN_VALUE
     .assert RETURN_VALUE = 0
     tax
-
-sometimes jumping to return0 could save a byte: -
+
+    ; Sometimes jumping to 'return 0' could save a byte:
     .assert RETURN_VALUE = 0
     jmp return 0
-
-* Functions, that are intended for a platform's system library, should be optimized as much as possible. -* Sometimes, there must be a trade-off between size and speed. If you think that a library function won't be used often, then you should make it small. Otherwise, you should make it fast. -* Comments that are put on the right side of instructions must be aligned (start in the same character columns). -* Assembly source fields (label, operation, operand, comment) should start ''after'' character columns that are multiples of eight (such as 1, 9, 17, 33, and 41). +~~~ + +* Functions, that are intended for a platform's system + library, should be optimized as much as possible. +* Sometimes, there must be a trade-off between size and + speed. If you think that a library function won't be used + often, then you should make it small. Otherwise, you should + make it fast. +* Comments that are put on the right side of instructions + must be aligned (start in the same character columns). +* Assembly source fields (label, operation, operand, + comment) should start ''after'' character columns that are + multiples of eight (such as 1, 9, 17, 33, and 41). -## LinuxDoc Sources +## LinuxDoc sources * TAB characters must be expanded to spaces. -* All text files must end with new-line characters. Don't leave the last line "dangling". +* All text files must end with new-line characters. Don't + leave the last line "dangling". * 80 characters is the desired maximum width of files. * You should avoid typing non-ASCII characters. * You should put blank lines between LinuxDoc sections: - * Three blank lines between `````` sections. - * Two blank lines between `````` sections. - * One blank line between other sections. +* Three blank lines between `````` sections. +* Two blank lines between `````` sections. +* One blank line between other sections. # Library implementation rules -* By default the toolchain must output a "standard" binary for the platform, no emulator formats, no extra headers used by tools. If the resulting binaries can not be run as is on emulators or eg flash cartridges, the process of converting them to something that can be used with these should be documented in the user manual. -* Generally every function should live in a seperate source file - unless the functions are so closely related that splitting makes no sense. -* Source files should not contain commented out code - if they do, there should be a comment that explains why that commented out code exists. +* By default the toolchain must output a "standard" binary + for the platform, no emulator formats, no extra headers + used by tools. If the resulting binaries can not be run as + is on emulators or eg flash cartridges, the process of + converting them to something that can be used with these + should be documented in the user manual. +* Generally every function should live in a seperate source + file - unless the functions are so closely related that + splitting makes no sense. +* Source files should not contain commented out code - if + they do, there should be a comment that explains why that + commented out code exists. # Makefile rules -* Makefiles must generally work on both *nix (ba)sh and windows cmd.exe. -* Makefiles must not use external tools that are not provided by the cc65 toolchain itself. +* Makefiles must generally work on both *nix (ba)sh and + windows cmd.exe. +* Makefiles must not use external tools that are not + provided by the cc65 toolchain itself. -The only exception to the above are actions that are exclusive to the github actions - those may rely on bash and/or linux tools. +The only exception to the above are actions that are exclusive +to the github actions - those may rely on bash and/or linux tools. # Documentation rules @@ -190,107 +325,42 @@ The only exception to the above are actions that are exclusive to the github act ## Wiki -* The Wiki is strictly for additional information that does not fit into the regular user manual (LinuxDoc). The wiki must not duplicate any information that is present in the user manual +* The Wiki is strictly for additional information that does + not fit into the regular user manual (LinuxDoc). The wiki + must not duplicate any information that is present in the + user manual. -# Roadmap / TODOs / open Ends +# Roadmap / TODOs / open ends ## Documentation -* the printf family of function does not completely implement all printf modifiers and does not behave as expected in some cases - all this should be documented in detail +* The printf() family of functions does not completely + implement all printf() modifiers and does not behave as + expected in some cases - all this should be documented in + detail. ## Compiler -* We need a way that makes it possible to feed arbitrary assembler code into the optimzer, so we can have proper tests for it +* We need a way that makes it possible to feed arbitrary + assembler code into the optimzer, so we can have proper + tests for it. ### Floating point support -The first step is implementing the datatype "float" as IEEE 754 floats. Help welcomed! +The first step is implementing the datatype "float" as IEEE +754 floats. Help welcomed! -* WIP compiler/library changes are here: https://github.com/cc65/cc65/pull/1777 +* WIP compiler/library changes are here: + https://github.com/cc65/cc65/pull/1777 ## Library -### name clashes in the library - -see "Identifiers and Symbol names" above - not all identifiers have been checked -and renamed yet. The following is a list of those that still might need to be -fixed: - -``` -common - -__argc libsrc/runtime/callmain.s libsrc/cbm610/mainargs.s libsrc/cx16/mainargs.s libsrc/plus4/mainargs.s libsrc/lynx/mainargs.s libsrc/c16/mainargs.s libsrc/geos-common/system/mainargs.s libsrc/sim6502/mainargs.s libsrc/c128/mainargs.s libsrc/vic20/mainargs.s libsrc/nes/mainargs.s libsrc/atari/getargs.s libsrc/apple2/mainargs.s libsrc/cbm510/mainargs.s libsrc/telestrat/mainargs.s libsrc/c64/mainargs.s libsrc/pet/mainargs.s libsrc/atmos/mainargs.s -__argv libsrc/runtime/callmain.s libsrc/cbm610/mainargs.s libsrc/cx16/mainargs.s libsrc/plus4/mainargs.s libsrc/lynx/mainargs.s libsrc/c16/mainargs.s libsrc/geos-common/system/mainargs.s libsrc/sim6502/mainargs.s libsrc/c128/mainargs.s libsrc/vic20/mainargs.s libsrc/nes/mainargs.s libsrc/atari/getargs.s libsrc/apple2/mainargs.s libsrc/cbm510/mainargs.s libsrc/telestrat/mainargs.s libsrc/c64/mainargs.s libsrc/pet/mainargs.s libsrc/atmos/mainargs.s -__cos libsrc/common/sincos.s -__ctypeidx libsrc/common/ctype.s libsrc/common/ctypemask.s libsrc/geos-common/system/ctype.s libsrc/atari/ctype.s libsrc/cbm/ctype.s libsrc/atmos/ctype.s asminc/ctype_common.inc -__cwd libsrc/common/getcwd.s libsrc/common/_cwd.s libsrc/atari/initcwd.s libsrc/apple2/initcwd.s libsrc/apple2/initcwd.s libsrc/telestrat/initcwd.s libsrc/cbm/initcwd.s -__cwd_buf_size libsrc/common/_cwd.s -__envcount libsrc/common/searchenv.s libsrc/common/_environ.s libsrc/common/putenv.s libsrc/common/getenv.s -__environ libsrc/common/searchenv.s libsrc/common/_environ.s libsrc/common/putenv.s libsrc/common/getenv.s -__envsize libsrc/common/_environ.s libsrc/common/putenv.s -__fdesc libsrc/common/_fdesc.s libsrc/common/fopen.s -__filetab libsrc/common/_fdesc.s libsrc/common/_file.s asminc/_file.inc -__fopen libsrc/common/fopen.s libsrc/common/_fopen.s -__printf libsrc/common/vsnprintf.s libsrc/common/_printf.s libsrc/common/vfprintf.s libsrc/conio/vcprintf.s libsrc/pce/_printf.s -__scanf libsrc/common/_scanf.inc libsrc/common/vsscanf.s libsrc/conio/vcscanf.s -__sin libsrc/common/sincos.s -__sys libsrc/common/_sys.s libsrc/apple2/_sys.s -__sys_oserrlist libsrc/common/stroserr.s libsrc/geos-common/system/oserrlist.s libsrc/atari/oserrlist.s libsrc/apple2/oserrlist.s libsrc/cbm/oserrlist.s libsrc/atmos/oserrlist.s -__syschdir libsrc/common/chdir.s libsrc/atari/syschdir.s libsrc/apple2/syschdir.s libsrc/telestrat/syschdir.s libsrc/cbm/syschdir.s -__sysmkdir libsrc/common/mkdir.s libsrc/atari/sysmkdir.s libsrc/apple2/sysmkdir.s libsrc/telestrat/sysmkdir.s -__sysremove libsrc/common/remove.s libsrc/geos-common/file/sysremove.s libsrc/atari/sysremove.s libsrc/atari/sysrmdir.s libsrc/apple2/sysremove.s libsrc/apple2/sysrmdir.s libsrc/telestrat/sysremove.s libsrc/cbm/sysremove.s -__sysrename libsrc/common/rename.s libsrc/geos-common/file/sysrename.s libsrc/atari/sysrename.s libsrc/apple2/sysrename.s libsrc/cbm/sysrename.s -__sysrmdir libsrc/common/rmdir.s libsrc/atari/sysrmdir.s libsrc/apple2/sysrmdir.s -__sysuname libsrc/common/uname.s libsrc/cbm610/sysuname.s libsrc/cx16/sysuname.s libsrc/plus4/sysuname.s libsrc/lynx/sysuname.s libsrc/c16/sysuname.s libsrc/geos-common/system/sysuname.s libsrc/c128/sysuname.s libsrc/creativision/sysuname.s libsrc/vic20/sysuname.s libsrc/nes/sysuname.s libsrc/atari/sysuname.s libsrc/apple2/sysuname.s libsrc/cbm510/sysuname.s libsrc/telestrat/sysuname.s libsrc/c64/sysuname.s libsrc/pet/sysuname.s libsrc/atari5200/sysuname.s libsrc/atmos/sysuname.s - -apple2 - -__auxtype libsrc/apple2/open.s -__datetime libsrc/apple2/open.s -__dos_type libsrc/apple2/dioopen.s libsrc/apple2/curdevice.s libsrc/apple2/mainargs.s libsrc/apple2/settime.s libsrc/apple2/getdevice.s libsrc/apple2/dosdetect.s libsrc/apple2/irq.s libsrc/apple2/open.s libsrc/apple2/mli.s libsrc/apple2/getres.s -__filetype libsrc/apple2/open.s libsrc/apple2/exehdr.s - - -atari - -__defdev libsrc/atari/posixdirent.s libsrc/atari/ucase_fn.s libsrc/atari/getdefdev.s -__dos_type libsrc/atari/getargs.s libsrc/atari/exec.s libsrc/atari/settime.s libsrc/atari/syschdir.s libsrc/atari/dosdetect.s libsrc/atari/is_cmdline_dos.s libsrc/atari/sysrmdir.s libsrc/atari/gettime.s libsrc/atari/lseek.s libsrc/atari/getres.s libsrc/atari/getdefdev.s -__do_oserror libsrc/atari/posixdirent.s libsrc/atari/do_oserr.s libsrc/atari/serref.s libsrc/atari/read.s libsrc/atari/write.s libsrc/atari/close.s -__getcolor libsrc/atari/setcolor.s -__getdefdev libsrc/atari/getdefdev.s -__graphics libsrc/atari/graphics.s -__inviocb libsrc/atari/serref.s libsrc/atari/ser/atrrdev.s libsrc/atari/inviocb.s libsrc/atari/read.s libsrc/atari/write.s libsrc/atari/lseek.s libsrc/atari/close.s -__is_cmdline_dos libsrc/atari/is_cmdline_dos.s libsrc/atari/doesclrscr.s -__rest_vecs libsrc/atari/savevec.s -__rwsetup libsrc/atari/rwcommon.s libsrc/atari/read.s libsrc/atari/write.s -__save_vecs libsrc/atari/savevec.s -__scroll libsrc/atari/scroll.s -__setcolor libsrc/atari/setcolor.s -__setcolor_low libsrc/atari/setcolor.s -__sio_call libsrc/atari/diowritev.s libsrc/atari/diopncls.s libsrc/atari/siocall.s libsrc/atari/diowrite.s libsrc/atari/dioread.s - - -cbm - -__cbm_filetype libsrc/cbm/cbm_filetype.s asminc/cbm_filetype.in -__dirread libsrc/cbm/dir.inc libsrc/cbm/dir.s -__dirread1 libsrc/cbm/dir.inc libsrc/cbm/dir.s - - -lynx - -__iodat libsrc/lynx/lynx-cart.s libsrc/lynx/bootldr.s libsrc/lynx/extzp.s libsrc/lynx/crt0.s libsrc/lynx/extzp.inc -__iodir libsrc/lynx/extzp.s libsrc/lynx/crt0.s libsrc/lynx/extzp.inc -__sprsys libsrc/lynx/tgi/lynx-160-102-16.s libsrc/lynx/extzp.s libsrc/lynx/crt0.s libsrc/lynx/extzp.inc -__viddma libsrc/lynx/tgi/lynx-160-102-16.s libsrc/lynx/extzp.s libsrc/lynx/crt0.s libsrc/lynx/extzp.inc - - -pce - -__nmi libsrc/pce/irq.s libsrc/pce/crt0.s -``` +Some name clashes need to be resolved. Please see the +[detailed list of name clashes](libsrc/NameClashes.md). ## Test suite -* specific tests to check the optimizer (rather than the codegenerator) are needed. -* we need more specific tests to check standard conformance of the library headers +* Specific tests to check the optimizer (rather than the code + generator) are needed. +* We need more specific tests to check standard conformance + of the library headers. diff --git a/README.md b/README.md index e0e2c24e7..e3f1ab30f 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,62 @@ -# About cc65 +The cc65 cross-compiler suite +============================= -cc65 is a complete cross development package for 65(C)02 systems, including -a powerful macro assembler, a C compiler, linker, archiver and several -other tools. cc65 has C and runtime library support for many of the old 6502 machines. -For details look at the [Website](https://cc65.github.io). +cc65 is a complete cross-development package for 65(C)02 systems, +including a powerful macro assembler, a C compiler, linker, archiver, +simulator and several other tools. cc65 has C and runtime library +support for many of the old 6502 machines. For details look at +the [cc65 web site](https://cc65.github.io): + +| Company / People | Machine / Environment | +|-------------------------|-------------------------------------| +| Apple | Apple II | +| | Apple IIe enhanced | +| Atari | Atari 400/800 | +| | Atari 2600 | +| | Atari 5200 | +| | Atari 7800 | +| | Atari XL | +| | Lynx | +| Tangerine | Oric Atmos | +| Eureka | Oric Telestrat | +| Acorn | BBC series | +| Commodore | C128 | +| | C16 | +| | C64 | +| | CBM 510/610 | +| | PET | +| | Plus/4 | +| | VIC-20 | +| VTech | CreatiVision | +| Commander X16 Community | Commander X16 | +| Bit Corporation | Gamate | +| Berkeley Softworks | GEOS (Apple/CBM) | +| LUnix Team | LUnix (C64) | +| Nintendo | Nintendo Entertainment System (NES) | +| Ohio Scientific | OSI C1P | +| MOS Technology, Inc. | KIM-1 | +| NEC | PC Engine (PCE) | +| Dr. Jozo Dujmović | Picocomputer (RP6502) | +| Watara | Watura/QuickShot Supervision | +| Synertek | SYM-1 | + +A generic configuration to adapt cc65 to new targets is also around. ## People -Core team members: +cc65 is originally based on the "Small C" compiler by Ron Cain and +enhanced by James E. Hendrix. + +### Project founders + +* John R. Dunning: [original implementation](https://public.websites.umich.edu/~archive/atari/8bit/Languages/Cc65/) + of the C compiler and runtime library, Atari hosted. +* Ullrich von Bassewitz: + * moved Dunning's code to modern systems, + * rewrote most parts of the compiler, + * rewrote all of the runtime library. + +### Core team members * [Christian Groessler](https://github.com/groessler): Atari, Atari5200, and CreatiVision library Maintainer * [dqh](https://github.com/dqh-au): GHA help @@ -15,7 +64,7 @@ Core team members: * [groepaz](https://github.com/mrdudz): CBM library, Project Maintainer * [Oliver Schmidt](https://github.com/oliverschmidt): Apple II library Maintainer -External contributors: +### External contributors * [acqn](https://github.com/acqn): various compiler fixes * [jedeoric](https://github.com/jedeoric): Telestrat target @@ -24,29 +73,35 @@ External contributors: * [Stephan Mühlstrasser](https://github.com/smuehlst): osic1p target * [Wayne Parham](https://github.com/WayneParham): Sym-1 target * [Dave Plummer](https://github.com/davepl): KIM-1 target +* [rumbledethumps](https://github.com/rumbledethumps): Picocomputer target *(The above list is incomplete, if you feel left out - please speak up or add yourself in a PR)* -For a complete list look at the [full team list](https://github.com/orgs/cc65/teams) or the list of [all contributors](https://github.com/cc65/cc65/graphs/contributors) +For a complete list look at the [full team list](https://github.com/orgs/cc65/teams) +or the list of [all contributors](https://github.com/cc65/cc65/graphs/contributors). # Contact -For general discussion, questions, etc subscribe to the [mailing list](https://cc65.github.io/mailing-lists.html) or use the [github discussions](https://github.com/cc65/cc65/discussions). +For general discussion, questions, etc subscribe to the +[mailing list](https://cc65.github.io/mailing-lists.html) +or use the [github discussions](https://github.com/cc65/cc65/discussions). -Some of us may also be around on IRC [#cc65](https://web.libera.chat/#cc65) on libera.chat +Some of us may also be around on IRC [#cc65](https://web.libera.chat/#cc65) on libera.chat. # Documentation -* The main [Documentation](https://cc65.github.io/doc) for users and developers - -* Info on [Contributing](Contributing.md) to the CC65 project. Please read this before working on something you want to contribute, and before reporting bugs. - -* The [Wiki](https://github.com/cc65/wiki/wiki) contains some extra info that does not fit into the regular documentation. +* The main [Documentation](https://cc65.github.io/doc) for users and + developers. +* Info on [Contributing](Contributing.md) to the CC65 project. Please + read this before working on something you want to contribute, and + before reporting bugs. +* The [Wiki](https://github.com/cc65/wiki/wiki) contains some extra info + that does not fit into the regular documentation. # Downloads -* [Windows Snapshot](https://sourceforge.net/projects/cc65/files/cc65-snapshot-win32.zip) - -* [Linux Snapshot DEB and RPM](https://software.opensuse.org//download.html?project=home%3Astrik&package=cc65) +* [Windows 64bit Snapshot](https://sourceforge.net/projects/cc65/files/cc65-snapshot-win64.zip) +* [Windows 32bit Snapshot](https://sourceforge.net/projects/cc65/files/cc65-snapshot-win32.zip) +* [Linux Snapshot DEB and RPM](https://software.opensuse.org/download.html?project=home%3Astrik&package=cc65) [![Snapshot Build](https://github.com/cc65/cc65/actions/workflows/snapshot-on-push-master.yml/badge.svg?branch=master)](https://github.com/cc65/cc65/actions/workflows/snapshot-on-push-master.yml) diff --git a/asminc/apple2.inc b/asminc/apple2.inc index 528c463a1..fb4a1b2f0 100644 --- a/asminc/apple2.inc +++ b/asminc/apple2.inc @@ -25,6 +25,12 @@ BRKVec := $03F0 ; Break vector SOFTEV := $03F2 ; Vector for warm start PWREDUP := $03F4 ; This must be = EOR #$A5 of SOFTEV+1 +;----------------------------------------------------------------------------- +; 80 column firmware + +OURCH := $057B ; Cursor horizontal position +OURCV := $05FB ; Cursor vertical position + ;----------------------------------------------------------------------------- ; Hardware diff --git a/asminc/cpu.mac b/asminc/cpu.mac index 31170fbed..084a42119 100644 --- a/asminc/cpu.mac +++ b/asminc/cpu.mac @@ -15,7 +15,7 @@ CPU_ISET_4510 = $0400 CPU_NONE = CPU_ISET_NONE CPU_6502 = CPU_ISET_6502 CPU_6502X = CPU_ISET_6502|CPU_ISET_6502X -CPU_6502DTV = CPU_ISET_6502|CPU_ISET_6502X|CPU_ISET_6502DTV +CPU_6502DTV = CPU_ISET_6502|CPU_ISET_6502DTV CPU_65SC02 = CPU_ISET_6502|CPU_ISET_65SC02 CPU_65C02 = CPU_ISET_6502|CPU_ISET_65SC02|CPU_ISET_65C02 CPU_65816 = CPU_ISET_6502|CPU_ISET_65SC02|CPU_ISET_65816 diff --git a/asminc/generic.mac b/asminc/generic.mac index bc6f5924e..5e5b210f5 100644 --- a/asminc/generic.mac +++ b/asminc/generic.mac @@ -31,10 +31,8 @@ ; bgt - jump if unsigned greater .macro bgt Arg - .local L - beq L + beq *+4 bcs Arg -L: .endmacro ; ble - jump if unsigned less or equal diff --git a/asminc/lynx.inc b/asminc/lynx.inc index 403d15d07..150bee6d1 100644 --- a/asminc/lynx.inc +++ b/asminc/lynx.inc @@ -79,19 +79,91 @@ MATHL = $FC6D MATHK = $FC6E MATHJ = $FC6F -; Suzy Misc +; Suzy sprite engine + +SPRCTL0 = $FC80 +; Sprite bits-per-pixel definitions +BPP_MASK = %11000000 ; Mask for settings bits per pixel +BPP_1 = %00000000 +BPP_2 = %01000000 +BPP_3 = %10000000 +BPP_4 = %11000000 +; More sprite control 0 bit definitions +HFLIP = %00100000 +VFLIP = %00010000 +; Sprite types - redefined to reflect the reality caused by the shadow error +TYPE_SHADOW = %00000111 +TYPE_XOR = %00000110 +TYPE_NONCOLL = %00000101 ; Non-colliding +TYPE_NORMAL = %00000100 +TYPE_BOUNDARY = %00000011 +TYPE_BSHADOW = %00000010 ; Background shadow +TYPE_BACKNONCOLL = %00000001 ; Background non-colliding +TYPE_BACKGROUND = %00000000 -SPRCTL0 = $FC80 SPRCTL1 = $FC81 -SPRCOLL = $FC82 -SPRINIT = $FC83 -SUZYHREV = $FC88 -SUZYSREV = $FC89 -SUZYBUSEN = $FC90 -SPRGO = $FC91 -SPRSYS = $FC92 -JOYSTICK = $FCB0 +LITERAL = %10000000 +PACKED = %00000000 +ALGO3 = %01000000 ; Broken, do not set this bit! +; Sprite reload mask definitions +RELOAD_MASK = %00110000 +RENONE = %00000000 ; Reload nothing +REHV = %00010000 ; Reload hsize, vsize +REHVS = %00100000 ; Reload hsize, vsize, stretch +REHVST = %00110000 ; Reload hsize, vsize, stretch, tilt +; More sprite control 1 bit definitions +REUSEPAL = %00001000 +SKIP = %00000100 +DRAWUP = %00000010 +DRAWLEFT = %00000001 + +SPRCOLL = $FC82 +SPRINIT = $FC83 +SUZYHREV = $FC88 +SUZYSREV = $FC89 +SUZYBUSEN = $FC90 + +SPRGO = $FC91 +; SPRGO bit definitions +SPRITE_GO = %00000001 ; sprite process start bit +EVER_ON = %00000100 ; everon detector enable + +SPRSYS = $FC92 +; SPRSYS bit definitions for write operations +SIGNMATH = %10000000 ; signed math +ACCUMULATE = %01000000 ; accumulate multiplication results +NO_COLLIDE = %00100000 ; do not collide with any sprites (also SPRCOLL bit definition) +VSTRETCH = %00010000 ; stretch v +LEFTHAND = %00001000 +CLR_UNSAFE = %00000100 ; unsafe access reset +SPRITESTOP = %00000010 ; request to stop sprite process +; SPRSYS bit definitions for read operations +MATHWORKING = %10000000 ; math operation in progress +MATHWARNING = %01000000 ; accumulator overflow on multiple or divide by zero +MATHCARRY = %00100000 ; last carry bit +VSTRETCHING = %00010000 +LEFTHANDED = %00001000 +UNSAFE_ACCESS = %00000100 ; unsafe access performed +SPRITETOSTOP = %00000010 ; requested to stop +SPRITEWORKING = %00000001 ; sprite process is active + +JOYSTICK = $FCB0 +; JOYSTICK bit definitions +JOYPAD_UP = %10000000 +JOYPAD_DOWN = %01000000 +JOYPAD_LEFT = %00100000 +JOYPAD_RIGHT = %00010000 +BUTTON_OPTION1 = %00001000 +BUTTON_OPTION2 = %00000100 +BUTTON_INNER = %00000010 +BUTTON_OUTER = %00000001 + SWITCHES = $FCB1 +; SWITCHES bit definitions +CART1_IO_INACTIVE = %00000100 +CART0_IO_INACTIVE = %00000010 +BUTTON_PAUSE = %00000001 + RCART0 = $FCB2 RCART1 = $FCB3 LEDS = $FCC0 @@ -99,181 +171,290 @@ PARSTATUS = $FCC2 PARDATA = $FCC3 HOWIE = $FCC4 - -; *** +; ; *** Mikey Addresses ; *** -; Mikey Timers +; Mikey timers -TIMER0 = $FD00 -TIMER1 = $FD04 -TIMER2 = $FD08 -TIMER3 = $FD0C -TIMER4 = $FD10 -TIMER5 = $FD14 -TIMER6 = $FD18 -TIMER7 = $FD1C -HTIMER = $FD00 ; horizontal line timer (timer 0) -VTIMER = $FD08 ; vertical blank timer (timer 2) -STIMER = $FD1C ; sound timer (timer 7) +; Logical timer names +TIMER0 = $FD00 +TIMER1 = $FD04 +TIMER2 = $FD08 +TIMER3 = $FD0C +TIMER4 = $FD10 +TIMER5 = $FD14 +TIMER6 = $FD18 +TIMER7 = $FD1C +HTIMER = TIMER0 ; horizontal line timer (timer 0) +VTIMER = TIMER2 ; vertical blank timer (timer 2) +STIMER = TIMER7 ; sound timer (timer 7) -HTIMBKUP = $FD00 ; horizontal line timer (timer 0) -HTIMCTLA = $FD01 -HTIMCNT = $FD02 -HTIMCTLB = $FD03 -VTIMBKUP = $FD08 ; vertical blank timer (timer 2) -VTIMCTLA = $FD09 -VTIMCNT = $FD0A -VTIMCTLB = $FD0B -BAUDBKUP = $FD10 ; serial timer (timer 4) -STIMBKUP = $FD1C ; sound timer (timer 7) -STIMCTLA = $FD1D -STIMCNT = $FD1E -STIMCTLB = $FD1F +HTIMBKUP = $FD00 ; horizontal line timer (timer 0) +HTIMCTLA = $FD01 +HTIMCNT = $FD02 +HTIMCTLB = $FD03 +VTIMBKUP = $FD08 ; vertical blank timer (timer 2) +VTIMCTLA = $FD09 +VTIMCNT = $FD0A +VTIMCTLB = $FD0B +BAUDBKUP = $FD10 ; serial timer (timer 4) +STIMBKUP = $FD1C ; sound timer (timer 7) +STIMCTLA = $FD1D +STIMCNT = $FD1E +STIMCTLB = $FD1F -TIM0BKUP = $FD00 -TIM0CTLA = $FD01 -TIM0CNT = $FD02 -TIM0CTLB = $FD03 -TIM1BKUP = $FD04 -TIM1CTLA = $FD05 -TIM1CNT = $FD06 -TIM1CTLB = $FD07 -TIM2BKUP = $FD08 -TIM2CTLA = $FD09 -TIM2CNT = $FD0A -TIM2CTLB = $FD0B -TIM3BKUP = $FD0C -TIM3CTLA = $FD0D -TIM3CNT = $FD0E -TIM3CTLB = $FD0F -TIM4BKUP = $FD10 -TIM4CTLA = $FD11 -TIM4CNT = $FD12 -TIM4CTLB = $FD13 -TIM5BKUP = $FD14 -TIM5CTLA = $FD15 -TIM5CNT = $FD16 -TIM5CTLB = $FD17 -TIM6BKUP = $FD18 -TIM6CTLA = $FD19 -TIM6CNT = $FD1A -TIM6CTLB = $FD1B -TIM7BKUP = $FD1C -TIM7CTLA = $FD1D -TIM7CNT = $FD1E -TIM7CTLB = $FD1F +TIM0BKUP = $FD00 +TIM0CTLA = $FD01 +TIM0CNT = $FD02 +TIM0CTLB = $FD03 +TIM1BKUP = $FD04 +TIM1CTLA = $FD05 +TIM1CNT = $FD06 +TIM1CTLB = $FD07 +TIM2BKUP = $FD08 +TIM2CTLA = $FD09 +TIM2CNT = $FD0A +TIM2CTLB = $FD0B +TIM3BKUP = $FD0C +TIM3CTLA = $FD0D +TIM3CNT = $FD0E +TIM3CTLB = $FD0F +TIM4BKUP = $FD10 +TIM4CTLA = $FD11 +TIM4CNT = $FD12 +TIM4CTLB = $FD13 +TIM5BKUP = $FD14 +TIM5CTLA = $FD15 +TIM5CNT = $FD16 +TIM5CTLB = $FD17 +TIM6BKUP = $FD18 +TIM6CTLA = $FD19 +TIM6CNT = $FD1A +TIM6CTLB = $FD1B +TIM7BKUP = $FD1C +TIM7CTLA = $FD1D +TIM7CNT = $FD1E +TIM7CTLB = $FD1F + +; Timer offsets +TIM_BACKUP = 0 +TIM_CONTROLA = 1 +TIM_COUNT = 2 +TIM_CONTROLB = 3 + +; TIM_CONTROLA control bits +ENABLE_INT = %10000000 +RESET_DONE = %01000000 +ENABLE_RELOAD = %00010000 +ENABLE_COUNT = %00001000 +AUD_CLOCK_MASK = %00000111 +; Clock settings +AUD_LINKING = %00000111 +AUD_64 = %00000110 +AUD_32 = %00000101 +AUD_16 = %00000100 +AUD_8 = %00000011 +AUD_4 = %00000010 +AUD_2 = %00000001 +AUD_1 = %00000000 + +; TIM_CONTROLB control bits +TIMER_DONE = %00001000 +LAST_CLOCK = %00000100 +BORROW_IN = %00000010 +BORROW_OUT = %00000001 ; Mikey Audio -AUDIO0 = $FD20 ; audio channel 0 -AUDIO1 = $FD28 ; audio channel 1 -AUDIO2 = $FD30 ; audio channel 2 -AUDIO3 = $FD38 ; audio channel 3 +AUDIO0 = $FD20 ; audio channel 0 +AUDIO1 = $FD28 ; audio channel 1 +AUDIO2 = $FD30 ; audio channel 2 +AUDIO3 = $FD38 ; audio channel 3 -AUD0VOL = $FD20 -AUD0FEED = $FD21 -AUD0OUT = $FD22 -AUD0SHIFT = $FD23 -AUD0BKUP = $FD24 -AUD0CTLA = $FD25 -AUD0CNT = $FD26 -AUD0CTLB = $FD27 -AUD1VOL = $FD28 -AUD1FEED = $FD29 -AUD1OUT = $FD2A -AUD1SHIFT = $FD2B -AUD1BKUP = $FD2C -AUD1CTLA = $FD2D -AUD1CNT = $FD2E -AUD1CTLB = $FD2F -AUD2VOL = $FD30 -AUD2FEED = $FD31 -AUD2OUT = $FD32 -AUD2SHIFT = $FD33 -AUD2BKUP = $FD34 -AUD2CTLA = $FD35 -AUD2CNT = $FD36 -AUD2CTLB = $FD37 -AUD3VOL = $FD38 -AUD3FEED = $FD39 -AUD3OUT = $FD3A -AUD3SHIFT = $FD3B -AUD3BKUP = $FD3C -AUD3CTLA = $FD3D -AUD3CNT = $FD3E -AUD3CTLB = $FD3F -MSTEREO = $FD50 +AUD0VOL = $FD20 +AUD0FEED = $FD21 +AUD0OUT = $FD22 +AUD0SHIFT = $FD23 +AUD0BKUP = $FD24 +AUD0CTLA = $FD25 +AUD0CNT = $FD26 +AUD0CTLB = $FD27 +AUD1VOL = $FD28 +AUD1FEED = $FD29 +AUD1OUT = $FD2A +AUD1SHIFT = $FD2B +AUD1BKUP = $FD2C +AUD1CTLA = $FD2D +AUD1CNT = $FD2E +AUD1CTLB = $FD2F +AUD2VOL = $FD30 +AUD2FEED = $FD31 +AUD2OUT = $FD32 +AUD2SHIFT = $FD33 +AUD2BKUP = $FD34 +AUD2CTLA = $FD35 +AUD2CNT = $FD36 +AUD2CTLB = $FD37 +AUD3VOL = $FD38 +AUD3FEED = $FD39 +AUD3OUT = $FD3A +AUD3SHIFT = $FD3B +AUD3BKUP = $FD3C +AUD3CTLA = $FD3D +AUD3CNT = $FD3E +AUD3CTLB = $FD3F -; Mikey Misc +; AUD_CONTROL bits are almost identical to TIM_CONTROLA bits. +; See TIM_CONTROLA above for the other definitions +FEEDBACK_7 = %10000000 +ENABLE_INTEGRATE = %00100000 + +; Stereo control registers follow +; Stereo capability does not exist in all Lynxes +; Left and right may be reversed, and if so will be corrected in a later +; release +ATTENREG0 = $FD40 ; Stereo attenuation registers +ATTENREG1 = $FD41 +ATTENREG2 = $FD42 +ATTENREG3 = $FD43 + +MPAN = $FD44 +MSTEREO = $FD50 +; Bit definitions for MPAN and MSTEREO registers +LEFT_ATTENMASK = %11110000 +RIGHT_ATTENMASK = %00001111 +LEFT3_SELECT = %10000000 +LEFT2_SELECT = %01000000 +LEFT1_SELECT = %00100000 +LEFT0_SELECT = %00010000 +RIGHT3_SELECT = %00001000 +RIGHT2_SELECT = %00000100 +RIGHT1_SELECT = %00000010 +RIGHT0_SELECT = %00000001 + +; Mikey interrupts + +INTRST = $FD80 +INTSET = $FD81 ; Interrupt bits in INTRST and INTSET -TIMER0_INTERRUPT = $01 -TIMER1_INTERRUPT = $02 -TIMER2_INTERRUPT = $04 -TIMER3_INTERRUPT = $08 -TIMER4_INTERRUPT = $10 -TIMER5_INTERRUPT = $20 -TIMER6_INTERRUPT = $40 -TIMER7_INTERRUPT = $80 +TIMER0_INTERRUPT = %00000001 +TIMER1_INTERRUPT = %00000010 +TIMER2_INTERRUPT = %00000100 +TIMER3_INTERRUPT = %00001000 +TIMER4_INTERRUPT = %00010000 +TIMER5_INTERRUPT = %00100000 +TIMER6_INTERRUPT = %01000000 +TIMER7_INTERRUPT = %10000000 -HBL_INTERRUPT = TIMER0_INTERRUPT -VBL_INTERRUPT = TIMER2_INTERRUPT +HBL_INTERRUPT = TIMER0_INTERRUPT +VBL_INTERRUPT = TIMER2_INTERRUPT SERIAL_INTERRUPT = TIMER4_INTERRUPT -SND_INTERRUPT = TIMER7_INTERRUPT +SND_INTERRUPT = TIMER7_INTERRUPT -INTRST = $FD80 -INTSET = $FD81 -MAGRDY0 = $FD84 -MAGRDY1 = $FD85 -AUDIN = $FD86 -SYSCTL1 = $FD87 -MIKEYHREV = $FD88 -MIKEYSREV = $FD89 -IODIR = $FD8A -IODAT = $FD8B -TxIntEnable = %10000000 -RxIntEnable = %01000000 -TxParEnable = %00010000 -ResetErr = %00001000 -TxOpenColl = %00000100 -TxBreak = %00000010 -ParEven = %00000001 -TxReady = %10000000 -RxReady = %01000000 -TxEmpty = %00100000 -RxParityErr = %00010000 -RxOverrun = %00001000 -RxFrameErr = %00000100 -RxBreak = %00000010 -ParityBit = %00000001 -SERCTL = $FD8C -SERDAT = $FD8D -SDONEACK = $FD90 -CPUSLEEP = $FD91 -DISPCTL = $FD92 -PBKUP = $FD93 -DISPADRL = $FD94 -DISPADRH = $FD95 -MTEST0 = $FD9C -MTEST1 = $FD9D -MTEST2 = $FD9E -PALETTE = $FDA0 ; hardware rgb palette -GCOLMAP = $FDA0 ; hardware rgb palette (green) -RBCOLMAP = $FDB0 ; hardware rgb palette (red-blue) +MAGRDY0 = $FD84 +MAGRDY1 = $FD85 +AUDIN = $FD86 +SYSCTL1 = $FD87 +; SYSCTL1 bit definitions +POWERON = %00000010 +CART_ADDR_STROBE = %00000001 +MIKEYHREV = $FD88 +MIKEYSREV = $FD89 -; *** -; *** Misc Hardware + 6502 vectors -; *** +IODIR = $FD8A +IODAT = $FD8B +; IODIR and IODAT bit definitions +AUDIN_BIT = %00010000 ; Note that there is also the address AUDIN +READ_ENABLE = %00010000 ; Same bit for AUDIN_BIT +RESTLESS = %00001000 +NOEXP = %00000100 ; If set, redeye is not connected +CART_ADDR_DATA = %00000010 +CART_POWER_OFF = %00000010 ; Same bit for CART_ADDR_DATA +EXTERNAL_POWER = %00000001 -MAPCTL = $FFF9 -VECTORS = $FFFB -INTVECTL = $FFFE -INTVECTH = $FFFF -RSTVECTL = $FFFC -RSTVECTH = $FFFD -NMIVECTL = $FFFA -NMIVECTH = $FFFB +SERCTL = $FD8C +; SERCTL bit definitions for write operations +TXINTEN = %10000000 +RXINTEN = %01000000 +PAREN = %00010000 +RESETERR = %00001000 +TXOPEN = %00000100 +TXBRK = %00000010 +PAREVEN = %00000001 +; SERCTL bit definitions for read operations +TXRDY = %10000000 +RXRDY = %01000000 +TXEMPTY = %00100000 +PARERR = %00010000 +OVERRUN = %00001000 +FRAMERR = %00000100 +RXBRK = %00000010 +PARBIT = %00000001 +SERDAT = $FD8D +SDONEACK = $FD90 +CPUSLEEP = $FD91 +DISPCTL = $FD92 +; DISPCTL bit definitions +DISP_COLOR = %10000000 ; must be set to 1 +DISP_FOURBIT = %01000000 ; must be set to 1 +DISP_FLIP = %00100000 +DMA_ENABLE = %00010000 ; must be set to 1 + +PBKUP = $FD93 +DISPADRL = $FD94 +DISPADRH = $FD95 + +MTEST0 = $FD9C +; MTEST0 bit definitions +AT_CNT16 = %10000000 +AT_TEST = %01000000 +XCLKEN = %00100000 +UART_TURBO = %00010000 +ROM_SEL = %00001000 +ROM_TEST = %00000100 +M_TEST = %00000010 +CPU_TEST = %00000001 + +MTEST1 = $FD9D +; MTEST1 bit definitions +P_CNT16 = %01000000 +REF_CNT16 = %00100000 +VID_TRIG = %00010000 +REF_TRIG = %00001000 +VID_DMA_DIS = %00000100 +REF_FAST = %00000010 +REF_DIS = %00000001 + +MTEST2 = $FD9E +; MTEST2 bit definitions +V_STROBE = %00010000 +V_ZERO = %00001000 +H_120 = %00000100 +H_ZERO = %00000010 +V_BLANKEF = %00000001 + +PALETTE = $FDA0 ; hardware rgb palette +GCOLMAP = $FDA0 ; hardware rgb palette (green) +RBCOLMAP = $FDB0 ; hardware rgb palette (red-blue) + +; Memory mapping control and 6502 vectors + +MAPCTL = $FFF9 +; MAPCTL bit definitions +TURBO_DISABLE = %10000000 +VECTOR_SPACE = %00001000 ; 1 maps RAM into specified space +ROM_SPACE = %00000100 +MIKEY_SPACE = %00000010 +SUZY_SPACE = %00000001 + +VECTORS = $FFFB +INTVECTL = $FFFE +INTVECTH = $FFFF +RSTVECTL = $FFFC +RSTVECTH = $FFFD +NMIVECTL = $FFFA +NMIVECTH = $FFFB diff --git a/asminc/rp6502.inc b/asminc/rp6502.inc new file mode 100644 index 000000000..816712abb --- /dev/null +++ b/asminc/rp6502.inc @@ -0,0 +1,98 @@ +; Picocomputer 6502 general defines + +; RIA UART +RIA_READY := $FFE0 ; TX=$80 RX=$40 +RIA_TX := $FFE1 +RIA_RX := $FFE2 + +; VSYNC from PIX VGA +RIA_VSYNC := $FFE3 + +; RIA XRAM portal 0 +RIA_RW0 := $FFE4 +RIA_STEP0 := $FFE5 +RIA_ADDR0 := $FFE6 + +; RIA XRAM portal 1 +RIA_RW1 := $FFE8 +RIA_STEP1 := $FFE9 +RIA_ADDR1 := $FFEA + +; RIA OS fastcall +RIA_XSTACK := $FFEC +RIA_ERRNO := $FFED +RIA_OP := $FFEF +RIA_IRQ := $FFF0 +RIA_SPIN := $FFF1 +RIA_BUSY := $FFF2 ; Bit $80 +RIA_A := $FFF4 +RIA_X := $FFF6 +RIA_SREG := $FFF8 + +; RIA OS operation numbers +RIA_OP_EXIT := $FF +RIA_OP_ZXSTACK := $00 +RIA_OP_XREG := $01 +RIA_OP_PHI2 := $02 +RIA_OP_CODEPAGE := $03 +RIA_OP_LRAND := $04 +RIA_OP_STDIN_OPT := $05 +RIA_OP_CLOCK := $0F +RIA_OP_CLOCK_GETRES := $10 +RIA_OP_CLOCK_GETTIME := $11 +RIA_OP_CLOCK_SETTIME := $12 +RIA_OP_CLOCK_GETTIMEZONE := $13 +RIA_OP_OPEN := $14 +RIA_OP_CLOSE := $15 +RIA_OP_READ_XSTACK := $16 +RIA_OP_READ_XRAM := $17 +RIA_OP_WRITE_XSTACK := $18 +RIA_OP_WRITE_XRAM := $19 +RIA_OP_LSEEK := $1A +RIA_OP_UNLINK := $1B +RIA_OP_RENAME := $1C + +; 6522 VIA +VIA := $FFD0 ; VIA base address +VIA_PB := VIA+$0 ; Port register B +VIA_PA1 := VIA+$1 ; Port register A +VIA_PRB := VIA+$0 ; *** Deprecated *** +VIA_PRA := VIA+$1 ; *** Deprecated *** +VIA_DDRB := VIA+$2 ; Data direction register B +VIA_DDRA := VIA+$3 ; Data direction register A +VIA_T1CL := VIA+$4 ; Timer 1, low byte +VIA_T1CH := VIA+$5 ; Timer 1, high byte +VIA_T1LL := VIA+$6 ; Timer 1 latch, low byte +VIA_T1LH := VIA+$7 ; Timer 1 latch, high byte +VIA_T2CL := VIA+$8 ; Timer 2, low byte +VIA_T2CH := VIA+$9 ; Timer 2, high byte +VIA_SR := VIA+$A ; Shift register +VIA_CR := VIA+$B ; Auxiliary control register +VIA_PCR := VIA+$C ; Peripheral control register +VIA_IFR := VIA+$D ; Interrupt flag register +VIA_IER := VIA+$E ; Interrupt enable register +VIA_PA2 := VIA+$F ; Port register A w/o handshake + +; Values in ___oserror are the union of these FatFs errors and errno.inc +.enum + FR_OK = 32 ; Succeeded + FR_DISK_ERR ; A hard error occurred in the low level disk I/O layer + FR_INT_ERR ; Assertion failed + FR_NOT_READY ; The physical drive cannot work + FR_NO_FILE ; Could not find the file + FR_NO_PATH ; Could not find the path + FR_INVALID_NAME ; The path name format is invalid + FR_DENIED ; Access denied due to prohibited access or directory full + FR_EXIST ; Access denied due to prohibited access + FR_INVALID_OBJECT ; The file/directory object is invalid + FR_WRITE_PROTECTED ; The physical drive is write protected + FR_INVALID_DRIVE ; The logical drive number is invalid + FR_NOT_ENABLED ; The volume has no work area + FR_NO_FILESYSTEM ; There is no valid FAT volume + FR_MKFS_ABORTED ; The f_mkfs() aborted due to any problem + FR_TIMEOUT ; Could not get a grant to access the volume within defined period + FR_LOCKED ; The operation is rejected according to the file sharing policy + FR_NOT_ENOUGH_CORE ; LFN working buffer could not be allocated + FR_TOO_MANY_OPEN_FILES ; Number of open files > FF_FS_LOCK + FR_INVALID_PARAMETER ; Given parameter is invalid +.endenum diff --git a/asminc/sim65.inc b/asminc/sim65.inc new file mode 100644 index 000000000..b8ea264b7 --- /dev/null +++ b/asminc/sim65.inc @@ -0,0 +1,75 @@ + +; ******************************************************************************* +; ** ** +; ** sim65.inc : assembler definitions for the sim6502 and sim65c02 targets. ** +; ** ** +; ** Sidney Cadot, January 2025 ** +; ** ** +; ******************************************************************************* + + ; The '_peripherals' symbol is defined in the linker configuration + ; file to correspond to the first address in the periperal memory + ; aparture. + ; + ; We use it here as a base address for all peripheral addresses. + + .import _peripherals + +; ************************************************************** +; ** ** +; ** Define assembler symbols for the "counter" peripheral. ** +; ** ** +; ************************************************************** + +peripheral_counter_base := _peripherals + 0 + +peripheral_counter_latch := peripheral_counter_base + 0 +peripheral_counter_select := peripheral_counter_base + 1 +peripheral_counter_value := peripheral_counter_base + 2 + +; Values for the peripheral_counter_select register. + +COUNTER_SELECT_CLOCKCYCLE_COUNTER = $00 +COUNTER_SELECT_INSTRUCTION_COUNTER = $01 +COUNTER_SELECT_IRQ_COUNTER = $02 +COUNTER_SELECT_NMI_COUNTER = $03 +COUNTER_SELECT_WALLCLOCK_TIME = $80 +COUNTER_SELECT_WALLCLOCK_TIME_SPLIT = $81 + +; ******************************************************************** +; ** ** +; ** Define assembler symbols for the "sim65 control" peripheral. ** +; ** ** +; ******************************************************************** + +peripheral_sim65_base := _peripherals + 10 + +peripheral_sim65_cpu_mode := peripheral_sim65_base + 0 +peripheral_sim65_trace_mode := peripheral_sim65_base + 1 + +; Values for the peripheral_sim65_cpu_mode register. + +SIM65_CPU_MODE_6502 = $00 +SIM65_CPU_MODE_65C02 = $01 +SIM65_CPU_MODE_6502X = $02 + +; Bitfield values for the peripheral_sim65_trace_mode field. + +SIM65_TRACE_MODE_FIELD_INSTR_COUNTER = $40 +SIM65_TRACE_MODE_FIELD_CLOCK_COUNTER = $20 +SIM65_TRACE_MODE_FIELD_PC = $10 +SIM65_TRACE_MODE_FIELD_INSTR_BYTES = $08 +SIM65_TRACE_MODE_FIELD_INSTR_ASSEMBLY = $04 +SIM65_TRACE_MODE_FIELD_CPU_REGISTERS = $02 +SIM65_TRACE_MODE_FIELD_CC65_SP = $01 + +; Values for the peripheral_sim65_trace_mode field that fully disable / enable tracing. + +SIM65_TRACE_MODE_DISABLE = $00 +SIM65_TRACE_MODE_ENABLE_FULL = $7F + +; ************************ +; ** ** +; ** End of sim65.inc ** +; ** ** +; ************************ diff --git a/asminc/stat.inc b/asminc/stat.inc new file mode 100644 index 000000000..e5248f06d --- /dev/null +++ b/asminc/stat.inc @@ -0,0 +1,64 @@ +;**************************************************************************** +;* * +;* stat.inc * +;* * +;* Stat struct * +;* * +;* * +;* * +;*(C) 2023 Colin Leroy-Mira * +;* * +;* * +;*This software is provided 'as-is', without any expressed 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. * +;* * +;**************************************************************************** + + .include "time.inc" + +;------------------------------------------------------------------------------ +; st_mode values + +S_IFDIR = $01 +S_IFREG = $02 + +;------------------------------------------------------------------------------ +; struct stat + +.struct stat + st_dev .dword + st_ino .dword + st_mode .byte + st_nlink .dword + st_uid .byte + st_gid .byte + st_size .dword + st_atim .tag timespec + st_ctim .tag timespec + st_mtim .tag timespec + .ifdef __APPLE2__ + st_access .byte + st_type .byte + st_auxtype .word + st_storagetype .byte + st_blocks .word + st_mod_date .word + st_mod_time .word + st_create_date .word + st_create_time .word + .endif +.endstruct diff --git a/asminc/statvfs.inc b/asminc/statvfs.inc new file mode 100644 index 000000000..8674b045d --- /dev/null +++ b/asminc/statvfs.inc @@ -0,0 +1,46 @@ +;**************************************************************************** +;* * +;* statvfs.inc * +;* * +;* Statvfs struct * +;* * +;* * +;* * +;*(C) 2023 Colin Leroy-Mira * +;* * +;* * +;*This software is provided 'as-is', without any expressed 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. * +;* * +;**************************************************************************** + +;------------------------------------------------------------------------------ +; struct statvfs + +.struct statvfs + f_bsize .dword + f_frsize .dword + f_blocks .dword + f_bfree .dword + f_bavail .dword + f_files .dword + f_ffree .dword + f_favail .dword + f_fsid .dword + f_flag .dword + f_namemax .dword +.endstruct diff --git a/asminc/telestrat.inc b/asminc/telestrat.inc index c57bd3de8..bbfabdf40 100644 --- a/asminc/telestrat.inc +++ b/asminc/telestrat.inc @@ -257,8 +257,11 @@ XBINDX = $28 ; Convert a number into hex and displays on chan XDECIM = $29 XHEXA = $2A ; Convert a number into hex +XMAINARGS = $2C ; Only available for Orix + XEDT = $2D ; Launch editor XINSER = $2E +XGETARGV = $2E ; Only available for Orix XSCELG = $2F ; Search a line in editor mode XOPEN = $30 ; Only in Orix diff --git a/asminc/time.inc b/asminc/time.inc index 6064b4ba3..7c1ab3177 100644 --- a/asminc/time.inc +++ b/asminc/time.inc @@ -66,3 +66,9 @@ .global _clock_settime .global _localtime .global _mktime + + +;------------------------------------------------------------------------------ +; Constants + +CLOCK_REALTIME = 0 diff --git a/cfg/apple2-hgr.cfg b/cfg/apple2-hgr.cfg index cfe577e00..402e53b34 100644 --- a/cfg/apple2-hgr.cfg +++ b/cfg/apple2-hgr.cfg @@ -17,6 +17,7 @@ MEMORY { MAIN: file = %O, define = yes, start = %S, size = __HIMEM__ - %S; BSS: file = "", start = __ONCE_RUN__, size = __HIMEM__ - __STACKSIZE__ - __ONCE_RUN__; LC: file = "", define = yes, start = __LCADDR__, size = __LCSIZE__; + LOW: file = "", define = yes, start = $0800, size = %S - $0800; } SEGMENTS { ZEROPAGE: load = ZP, type = zp; @@ -27,10 +28,11 @@ SEGMENTS { CODE: load = MAIN, type = ro start = $4000; RODATA: load = MAIN, type = ro; DATA: load = MAIN, type = rw; - INIT: load = MAIN, type = rw; + INIT: load = MAIN, type = rw; # uninitialized, but reserves output space ONCE: load = MAIN, type = ro, define = yes; LC: load = MAIN, run = LC, type = ro, optional = yes; BSS: load = BSS, type = bss, define = yes; + LOWBSS: load = LOW, type = bss, optional = yes; } FEATURES { CONDES: type = constructor, diff --git a/cfg/apple2-overlay.cfg b/cfg/apple2-overlay.cfg index a0b7678c1..512b19c16 100644 --- a/cfg/apple2-overlay.cfg +++ b/cfg/apple2-overlay.cfg @@ -25,6 +25,7 @@ MEMORY { MAIN: file = %O, define = yes, start = %S + __OVERLAYSIZE__, size = __HIMEM__ - __OVERLAYSIZE__ - %S; BSS: file = "", start = __ONCE_RUN__, size = __HIMEM__ - __STACKSIZE__ - __ONCE_RUN__; LC: file = "", define = yes, start = __LCADDR__, size = __LCSIZE__; + LOW: file = "", define = yes, start = $0800, size = %S - $0800; OVL1: file = "%O.1", start = %S, size = __OVERLAYSIZE__; OVL2: file = "%O.2", start = %S, size = __OVERLAYSIZE__; OVL3: file = "%O.3", start = %S, size = __OVERLAYSIZE__; @@ -43,10 +44,11 @@ SEGMENTS { CODE: load = MAIN, type = ro; RODATA: load = MAIN, type = ro; DATA: load = MAIN, type = rw; - INIT: load = MAIN, type = rw; + INIT: load = MAIN, type = rw; # uninitialized, but reserves output space ONCE: load = MAIN, type = ro, define = yes; LC: load = MAIN, run = LC, type = ro, optional = yes; BSS: load = BSS, type = bss, define = yes; + LOWBSS: load = LOW, type = bss, optional = yes; OVERLAY1: load = OVL1, type = ro, define = yes, optional = yes; OVERLAY2: load = OVL2, type = ro, define = yes, optional = yes; OVERLAY3: load = OVL3, type = ro, define = yes, optional = yes; diff --git a/cfg/apple2-system.cfg b/cfg/apple2-system.cfg index 0170feb93..8720ae800 100644 --- a/cfg/apple2-system.cfg +++ b/cfg/apple2-system.cfg @@ -13,6 +13,7 @@ MEMORY { MAIN: file = %O, define = yes, start = $2000, size = $BF00 - $2000; BSS: file = "", start = __ONCE_RUN__, size = $BF00 - __STACKSIZE__ - __ONCE_RUN__; LC: file = "", define = yes, start = __LCADDR__, size = __LCSIZE__; + LOW: file = "", define = yes, start = $0800, size = $2000 - $0800; } SEGMENTS { ZEROPAGE: load = ZP, type = zp; @@ -22,10 +23,11 @@ SEGMENTS { CODE: load = MAIN, type = ro; RODATA: load = MAIN, type = ro; DATA: load = MAIN, type = rw; - INIT: load = MAIN, type = rw; + INIT: load = MAIN, type = rw; # uninitialized, but reserves output space ONCE: load = MAIN, type = ro, define = yes; LC: load = MAIN, run = LC, type = ro, optional = yes; BSS: load = BSS, type = bss, define = yes; + LOWBSS: load = LOW, type = bss, optional = yes; } FEATURES { CONDES: type = constructor, diff --git a/cfg/apple2.cfg b/cfg/apple2.cfg index a6809cf89..c33bdee2d 100644 --- a/cfg/apple2.cfg +++ b/cfg/apple2.cfg @@ -17,6 +17,7 @@ MEMORY { MAIN: file = %O, define = yes, start = %S, size = __HIMEM__ - %S; BSS: file = "", start = __ONCE_RUN__, size = __HIMEM__ - __STACKSIZE__ - __ONCE_RUN__; LC: file = "", define = yes, start = __LCADDR__, size = __LCSIZE__; + LOW: file = "", define = yes, start = $0800, size = %S - $0800; } SEGMENTS { ZEROPAGE: load = ZP, type = zp; @@ -26,10 +27,11 @@ SEGMENTS { CODE: load = MAIN, type = ro; RODATA: load = MAIN, type = ro; DATA: load = MAIN, type = rw; - INIT: load = MAIN, type = rw; + INIT: load = MAIN, type = rw; # uninitialized, but reserves output space ONCE: load = MAIN, type = ro, define = yes; LC: load = MAIN, run = LC, type = ro, optional = yes; BSS: load = BSS, type = bss, define = yes; + LOWBSS: load = LOW, type = bss, optional = yes; } FEATURES { CONDES: type = constructor, diff --git a/cfg/apple2enh-hgr.cfg b/cfg/apple2enh-hgr.cfg index cfe577e00..402e53b34 100644 --- a/cfg/apple2enh-hgr.cfg +++ b/cfg/apple2enh-hgr.cfg @@ -17,6 +17,7 @@ MEMORY { MAIN: file = %O, define = yes, start = %S, size = __HIMEM__ - %S; BSS: file = "", start = __ONCE_RUN__, size = __HIMEM__ - __STACKSIZE__ - __ONCE_RUN__; LC: file = "", define = yes, start = __LCADDR__, size = __LCSIZE__; + LOW: file = "", define = yes, start = $0800, size = %S - $0800; } SEGMENTS { ZEROPAGE: load = ZP, type = zp; @@ -27,10 +28,11 @@ SEGMENTS { CODE: load = MAIN, type = ro start = $4000; RODATA: load = MAIN, type = ro; DATA: load = MAIN, type = rw; - INIT: load = MAIN, type = rw; + INIT: load = MAIN, type = rw; # uninitialized, but reserves output space ONCE: load = MAIN, type = ro, define = yes; LC: load = MAIN, run = LC, type = ro, optional = yes; BSS: load = BSS, type = bss, define = yes; + LOWBSS: load = LOW, type = bss, optional = yes; } FEATURES { CONDES: type = constructor, diff --git a/cfg/apple2enh-overlay.cfg b/cfg/apple2enh-overlay.cfg index a0b7678c1..512b19c16 100644 --- a/cfg/apple2enh-overlay.cfg +++ b/cfg/apple2enh-overlay.cfg @@ -25,6 +25,7 @@ MEMORY { MAIN: file = %O, define = yes, start = %S + __OVERLAYSIZE__, size = __HIMEM__ - __OVERLAYSIZE__ - %S; BSS: file = "", start = __ONCE_RUN__, size = __HIMEM__ - __STACKSIZE__ - __ONCE_RUN__; LC: file = "", define = yes, start = __LCADDR__, size = __LCSIZE__; + LOW: file = "", define = yes, start = $0800, size = %S - $0800; OVL1: file = "%O.1", start = %S, size = __OVERLAYSIZE__; OVL2: file = "%O.2", start = %S, size = __OVERLAYSIZE__; OVL3: file = "%O.3", start = %S, size = __OVERLAYSIZE__; @@ -43,10 +44,11 @@ SEGMENTS { CODE: load = MAIN, type = ro; RODATA: load = MAIN, type = ro; DATA: load = MAIN, type = rw; - INIT: load = MAIN, type = rw; + INIT: load = MAIN, type = rw; # uninitialized, but reserves output space ONCE: load = MAIN, type = ro, define = yes; LC: load = MAIN, run = LC, type = ro, optional = yes; BSS: load = BSS, type = bss, define = yes; + LOWBSS: load = LOW, type = bss, optional = yes; OVERLAY1: load = OVL1, type = ro, define = yes, optional = yes; OVERLAY2: load = OVL2, type = ro, define = yes, optional = yes; OVERLAY3: load = OVL3, type = ro, define = yes, optional = yes; diff --git a/cfg/apple2enh-system.cfg b/cfg/apple2enh-system.cfg index 0170feb93..8720ae800 100644 --- a/cfg/apple2enh-system.cfg +++ b/cfg/apple2enh-system.cfg @@ -13,6 +13,7 @@ MEMORY { MAIN: file = %O, define = yes, start = $2000, size = $BF00 - $2000; BSS: file = "", start = __ONCE_RUN__, size = $BF00 - __STACKSIZE__ - __ONCE_RUN__; LC: file = "", define = yes, start = __LCADDR__, size = __LCSIZE__; + LOW: file = "", define = yes, start = $0800, size = $2000 - $0800; } SEGMENTS { ZEROPAGE: load = ZP, type = zp; @@ -22,10 +23,11 @@ SEGMENTS { CODE: load = MAIN, type = ro; RODATA: load = MAIN, type = ro; DATA: load = MAIN, type = rw; - INIT: load = MAIN, type = rw; + INIT: load = MAIN, type = rw; # uninitialized, but reserves output space ONCE: load = MAIN, type = ro, define = yes; LC: load = MAIN, run = LC, type = ro, optional = yes; BSS: load = BSS, type = bss, define = yes; + LOWBSS: load = LOW, type = bss, optional = yes; } FEATURES { CONDES: type = constructor, diff --git a/cfg/apple2enh.cfg b/cfg/apple2enh.cfg index a6809cf89..c33bdee2d 100644 --- a/cfg/apple2enh.cfg +++ b/cfg/apple2enh.cfg @@ -17,6 +17,7 @@ MEMORY { MAIN: file = %O, define = yes, start = %S, size = __HIMEM__ - %S; BSS: file = "", start = __ONCE_RUN__, size = __HIMEM__ - __STACKSIZE__ - __ONCE_RUN__; LC: file = "", define = yes, start = __LCADDR__, size = __LCSIZE__; + LOW: file = "", define = yes, start = $0800, size = %S - $0800; } SEGMENTS { ZEROPAGE: load = ZP, type = zp; @@ -26,10 +27,11 @@ SEGMENTS { CODE: load = MAIN, type = ro; RODATA: load = MAIN, type = ro; DATA: load = MAIN, type = rw; - INIT: load = MAIN, type = rw; + INIT: load = MAIN, type = rw; # uninitialized, but reserves output space ONCE: load = MAIN, type = ro, define = yes; LC: load = MAIN, run = LC, type = ro, optional = yes; BSS: load = BSS, type = bss, define = yes; + LOWBSS: load = LOW, type = bss, optional = yes; } FEATURES { CONDES: type = constructor, diff --git a/cfg/atari-asm-xex.cfg b/cfg/atari-asm-xex.cfg index f0a6291db..fddf95dd4 100644 --- a/cfg/atari-asm-xex.cfg +++ b/cfg/atari-asm-xex.cfg @@ -20,5 +20,6 @@ SEGMENTS { CODE: load = MAIN, type = rw, define = yes; RODATA: load = MAIN, type = ro optional = yes; DATA: load = MAIN, type = rw optional = yes; + INIT: load = MAIN, type = bss, optional = yes, define = yes; BSS: load = MAIN, type = bss, optional = yes, define = yes; } diff --git a/cfg/atari-asm.cfg b/cfg/atari-asm.cfg index 6fc1c2caa..f824eb264 100644 --- a/cfg/atari-asm.cfg +++ b/cfg/atari-asm.cfg @@ -25,6 +25,7 @@ SEGMENTS { CODE: load = MAIN, type = rw, define = yes; RODATA: load = MAIN, type = ro optional = yes; DATA: load = MAIN, type = rw optional = yes; + INIT: load = MAIN, type = bss, optional = yes, define = yes; BSS: load = MAIN, type = bss, optional = yes, define = yes; AUTOSTRT: load = TRAILER, type = ro, optional = yes; } diff --git a/cfg/atari-cassette.cfg b/cfg/atari-cassette.cfg index 13b34cc73..1d7d02f93 100644 --- a/cfg/atari-cassette.cfg +++ b/cfg/atari-cassette.cfg @@ -22,8 +22,8 @@ SEGMENTS { CODE: load = MAIN, type = ro, define = yes; RODATA: load = MAIN, type = ro, optional = yes; DATA: load = MAIN, type = rw, optional = yes; + INIT: load = MAIN, type = bss, define = yes, optional = yes; BSS: load = MAIN, type = bss, define = yes, optional = yes; - INIT: load = MAIN, type = bss, optional = yes; } FEATURES { CONDES: type = constructor, diff --git a/cfg/atari-overlay.cfg b/cfg/atari-overlay.cfg index 9311a1b22..1dde26331 100644 --- a/cfg/atari-overlay.cfg +++ b/cfg/atari-overlay.cfg @@ -52,7 +52,7 @@ SEGMENTS { CODE: load = MAIN, type = ro, define = yes; RODATA: load = MAIN, type = ro; DATA: load = MAIN, type = rw; - INIT: load = MAIN, type = rw, optional = yes; + INIT: load = MAIN, type = bss, define = yes, optional = yes; BSS: load = MAIN, type = bss, define = yes; AUTOSTRT: load = TRAILER, type = ro; OVERLAY1: load = OVL1, type = ro, define = yes, optional = yes; diff --git a/cfg/atari-xex.cfg b/cfg/atari-xex.cfg index cabde3708..1ab60af2e 100644 --- a/cfg/atari-xex.cfg +++ b/cfg/atari-xex.cfg @@ -36,7 +36,7 @@ SEGMENTS { CODE: load = MAIN, type = ro, define = yes; RODATA: load = MAIN, type = ro; DATA: load = MAIN, type = rw; - INIT: load = MAIN, type = rw, optional = yes; + INIT: load = MAIN, type = bss, define = yes, optional = yes; BSS: load = MAIN, type = bss, define = yes; } FEATURES { diff --git a/cfg/atari.cfg b/cfg/atari.cfg index 106c75e63..0af121de3 100644 --- a/cfg/atari.cfg +++ b/cfg/atari.cfg @@ -40,7 +40,7 @@ SEGMENTS { CODE: load = MAIN, type = ro, define = yes; RODATA: load = MAIN, type = ro; DATA: load = MAIN, type = rw; - INIT: load = MAIN, type = rw, optional = yes; + INIT: load = MAIN, type = bss, define = yes, optional = yes; BSS: load = MAIN, type = bss, define = yes; AUTOSTRT: load = TRAILER, type = ro; } diff --git a/cfg/atari5200.cfg b/cfg/atari5200.cfg index e8f6d44a5..a487bdcab 100644 --- a/cfg/atari5200.cfg +++ b/cfg/atari5200.cfg @@ -15,7 +15,7 @@ MEMORY { SEGMENTS { ZEROPAGE: load = ZP, type = zp, optional = yes; EXTZP: load = ZP, type = zp, optional = yes; - DLIST: load = ROM , type = ro, define = yes, optional = yes; + DLIST: load = ROM, type = ro, define = yes, optional = yes; STARTUP: load = ROM, type = ro, define = yes, optional = yes; LOWCODE: load = ROM, type = ro, define = yes, optional = yes; ONCE: load = ROM, type = ro, optional = yes; diff --git a/cfg/atarixl-largehimem.cfg b/cfg/atarixl-largehimem.cfg index 38fb68db9..6df9b3f80 100644 --- a/cfg/atarixl-largehimem.cfg +++ b/cfg/atarixl-largehimem.cfg @@ -67,7 +67,7 @@ SEGMENTS { CODE: load = MAIN, type = ro, define = yes; RODATA: load = MAIN, type = ro; DATA: load = MAIN, type = rw; - INIT: load = MAIN, type = rw, optional = yes; + INIT: load = MAIN, type = bss, define = yes, optional = yes; BSS: load = MAIN, type = bss, define = yes; AUTOSTRT: load = TRAILER, type = ro; } diff --git a/cfg/atarixl-overlay.cfg b/cfg/atarixl-overlay.cfg index 339228ea0..5cfdcdbc5 100644 --- a/cfg/atarixl-overlay.cfg +++ b/cfg/atarixl-overlay.cfg @@ -78,7 +78,7 @@ SEGMENTS { CODE: load = MAIN, type = ro, define = yes; RODATA: load = MAIN, type = ro; DATA: load = MAIN, type = rw; - INIT: load = MAIN, type = rw, optional = yes; + INIT: load = MAIN, type = bss, define = yes, optional = yes; BSS: load = MAIN, type = bss, define = yes; AUTOSTRT: load = TRAILER, type = ro; diff --git a/cfg/atarixl-xex.cfg b/cfg/atarixl-xex.cfg index 1b76855d0..a0a4a971d 100644 --- a/cfg/atarixl-xex.cfg +++ b/cfg/atarixl-xex.cfg @@ -58,7 +58,7 @@ SEGMENTS { CODE: load = MAIN, type = ro, define = yes; RODATA: load = MAIN, type = ro; DATA: load = MAIN, type = rw; - INIT: load = MAIN, type = rw, optional = yes; + INIT: load = MAIN, type = bss, define = yes, optional = yes; BSS: load = MAIN, type = bss, define = yes; SRPREPHDR: load = UNUSED, type = ro; SRPREPTRL: load = UNUSED, type = ro; diff --git a/cfg/atarixl.cfg b/cfg/atarixl.cfg index cece23555..5e7199cd3 100644 --- a/cfg/atarixl.cfg +++ b/cfg/atarixl.cfg @@ -65,7 +65,7 @@ SEGMENTS { CODE: load = MAIN, type = ro, define = yes; RODATA: load = MAIN, type = ro; DATA: load = MAIN, type = rw; - INIT: load = MAIN, type = rw, optional = yes; + INIT: load = MAIN, type = bss, define = yes, optional = yes; BSS: load = MAIN, type = bss, define = yes; AUTOSTRT: load = TRAILER, type = ro; } diff --git a/cfg/atmos.cfg b/cfg/atmos.cfg index 35f184f4f..4c370903a 100644 --- a/cfg/atmos.cfg +++ b/cfg/atmos.cfg @@ -23,7 +23,7 @@ SEGMENTS { CODE: load = MAIN, type = ro; RODATA: load = MAIN, type = ro; DATA: load = MAIN, type = rw; - INIT: load = MAIN, type = rw; + INIT: load = MAIN, type = rw; # uninitialized, but reserves output space ONCE: load = MAIN, type = ro, define = yes; BASTAIL: load = MAIN, type = ro, optional = yes; BSS: load = BSS, type = bss, define = yes; diff --git a/cfg/c16-asm.cfg b/cfg/c16-asm.cfg new file mode 100644 index 000000000..8cb839304 --- /dev/null +++ b/cfg/c16-asm.cfg @@ -0,0 +1,20 @@ +FEATURES { + STARTADDRESS: default = $1001; +} +SYMBOLS { + __LOADADDR__: type = import; +} +MEMORY { + ZP: file = "", start = $0002, size = $001A; + LOADADDR: file = %O, start = %S - 2, size = $0002; + MAIN: file = %O, start = %S, size = $3000 - %S; +} +SEGMENTS { + ZEROPAGE: load = ZP, type = zp, optional = yes; + LOADADDR: load = LOADADDR, type = ro; + EXEHDR: load = MAIN, type = ro, optional = yes; + CODE: load = MAIN, type = ro; + RODATA: load = MAIN, type = ro, optional = yes; + DATA: load = MAIN, type = rw, optional = yes; + BSS: load = MAIN, type = bss, define = yes; +} diff --git a/cfg/c64-overlay.cfg b/cfg/c64-overlay.cfg index 0f42434ad..ae760b30c 100644 --- a/cfg/c64-overlay.cfg +++ b/cfg/c64-overlay.cfg @@ -44,7 +44,7 @@ SEGMENTS { CODE: load = MAIN, type = ro; RODATA: load = MAIN, type = ro; DATA: load = MAIN, type = rw; - INIT: load = MAIN, type = rw; + INIT: load = MAIN, type = rw; # uninitialized, but reserves output space ONCE: load = MAIN, type = ro, define = yes; BSS: load = BSS, type = bss, define = yes; OVL1ADDR: load = OVL1ADDR, type = ro; diff --git a/cfg/c64.cfg b/cfg/c64.cfg index 5bd8d8240..59cadd46b 100644 --- a/cfg/c64.cfg +++ b/cfg/c64.cfg @@ -23,7 +23,7 @@ SEGMENTS { CODE: load = MAIN, type = ro; RODATA: load = MAIN, type = ro; DATA: load = MAIN, type = rw; - INIT: load = MAIN, type = rw; + INIT: load = MAIN, type = rw; # uninitialized, but reserves output space ONCE: load = MAIN, type = ro, define = yes; BSS: load = BSS, type = bss, define = yes; } diff --git a/cfg/creativision.cfg b/cfg/creativision.cfg index 2eb9ac427..f3b7ac0f4 100644 --- a/cfg/creativision.cfg +++ b/cfg/creativision.cfg @@ -11,10 +11,10 @@ SEGMENTS { ZP: load = ZP, type = zp, optional = yes; VECTORS: load = ROM, run = RAM, type = rw, define = yes; DATA: load = ROM, run = RAM, type = rw, define = yes, start = $0204; + INIT: load = RAM, type = bss, optional = yes; BSS: load = RAM, type = bss, define = yes; ONCE: load = ROM, type = ro, optional = yes; CODE: load = ROM, type = ro; - INIT: load = ROM, type = ro; RODATA: load = ROM, type = ro; AUDIO: load = ROM, type = ro, optional = yes, start = $BF00; SETUP: load = ROM, type = ro, start = $BFE8; diff --git a/cfg/cx16-bank.cfg b/cfg/cx16-bank.cfg index d3c2c02ae..264b6fcba 100644 --- a/cfg/cx16-bank.cfg +++ b/cfg/cx16-bank.cfg @@ -57,7 +57,7 @@ SEGMENTS { CODE: load = MAIN, type = ro; RODATA: load = MAIN, type = ro; DATA: load = MAIN, type = rw; - INIT: load = MAIN, type = rw, optional = yes; + INIT: load = MAIN, type = rw, optional = yes; # uninitialized, but reserves output space ONCE: load = MAIN, type = ro, define = yes; BSS: load = BSS, type = bss, define = yes; BRAM01ADDR: load = BRAM01ADDR, type = ro, optional = yes; diff --git a/cfg/cx16.cfg b/cfg/cx16.cfg index 4b6025fb6..a160a6718 100644 --- a/cfg/cx16.cfg +++ b/cfg/cx16.cfg @@ -24,7 +24,7 @@ SEGMENTS { CODE: load = MAIN, type = ro; RODATA: load = MAIN, type = ro; DATA: load = MAIN, type = rw; - INIT: load = MAIN, type = rw, optional = yes; + INIT: load = MAIN, type = rw, optional = yes; # uninitialized, but reserves output space ONCE: load = MAIN, type = ro, define = yes; BSS: load = BSS, type = bss, define = yes; } diff --git a/cfg/kim1-mtu60k.cfg b/cfg/kim1-mtu60k.cfg new file mode 100644 index 000000000..4f24a4bf4 --- /dev/null +++ b/cfg/kim1-mtu60k.cfg @@ -0,0 +1,41 @@ +# kim1-mtu60k.cfg (4k) +# +# for expanded KIM-1 w/ K-1008 Graphics and 60K RAM +# +# ld65 --config kim1-mtu60k.cfg -o .bin .o + +FEATURES { + STARTADDRESS: default = $2000; + CONDES: segment = STARTUP, + type = constructor, + label = __CONSTRUCTOR_TABLE__, + count = __CONSTRUCTOR_COUNT__; + CONDES: segment = STARTUP, + type = destructor, + label = __DESTRUCTOR_TABLE__, + count = __DESTRUCTOR_COUNT__; +} + +SYMBOLS { + __STACKSIZE__: type = weak, value = $0080; # 128 byte program stack + __STARTADDRESS__: type = export, value = %S; +} + +MEMORY { + ZP: file = %O, define = yes, start = $0000, size = $00EE; + CPUSTACK: file = "", define = yes, start = $0100, size = $0100; + RAM: file = %O, define = yes, start = %S, size = $E000 - %S - __STACKSIZE__; + MAINROM: file = "", define = yes, start = $E000, size = $1000; + TOP: file = "", define = yes, start = $F000, size = $1000; +} + +SEGMENTS { + ZEROPAGE: load = ZP, type = zp, define = yes; + STARTUP: load = RAM, type = ro, define = yes; + CODE: load = RAM, type = ro, define = yes; + RODATA: load = RAM, type = ro, define = yes; + ONCE: load = RAM, type = ro, define = yes; + DATA: load = RAM, type = rw, define = yes; + BSS: load = RAM, type = bss, define = yes; +} + diff --git a/cfg/kim1-mtuE000.cfg b/cfg/kim1-mtuE000.cfg new file mode 100644 index 000000000..5f93cc13f --- /dev/null +++ b/cfg/kim1-mtuE000.cfg @@ -0,0 +1,41 @@ +# kim1-mtu60k.cfg (4k) +# +# for expanded KIM-1 w/ K-1008 Graphics and 60K RAM +# +# ld65 --config kim1-mtu60k.cfg -o .bin .o + +FEATURES { + STARTADDRESS: default = $E000; + CONDES: segment = STARTUP, + type = constructor, + label = __CONSTRUCTOR_TABLE__, + count = __CONSTRUCTOR_COUNT__; + CONDES: segment = STARTUP, + type = destructor, + label = __DESTRUCTOR_TABLE__, + count = __DESTRUCTOR_COUNT__; +} + +SYMBOLS { + __STACKSIZE__: type = weak, value = $0080; # 128 byte program stack + __STARTADDRESS__: type = export, value = %S; +} + +MEMORY { + ZP: file = %O, define = yes, start = $0000, size = $00EE; + CPUSTACK: file = "", define = yes, start = $0100, size = $0100; + RAM: file = %O, define = yes, start = $2000, size = $E000 - $2000 - __STACKSIZE__; + MAINROM: file = "", define = yes, start = $E000, size = $1000; + TOP: file = "", define = yes, start = $F000, size = $1000; +} + +SEGMENTS { + ZEROPAGE: load = ZP, type = zp, define = yes; + STARTUP: load = RAM, type = ro, define = yes; + CODE: load = RAM, type = ro, define = yes; + RODATA: load = RAM, type = ro, define = yes; + ONCE: load = RAM, type = ro, define = yes; + DATA: load = RAM, type = rw, define = yes; + BSS: load = RAM, type = bss, define = yes; +} + diff --git a/cfg/lynx-uploader.cfg b/cfg/lynx-uploader.cfg index 476b3c5de..62269de90 100644 --- a/cfg/lynx-uploader.cfg +++ b/cfg/lynx-uploader.cfg @@ -5,16 +5,17 @@ SYMBOLS { __BANK1BLOCKSIZE__: type = weak, value = $0000; # bank 1 block size __EXEHDR__: type = import; __BOOTLDR__: type = import; - __DEFDIR__: type = import; __UPLOADER__: type = import; + __UPLOADERSIZE__: type = export, value = $61; + __HEADERSIZE__: type = export, value = 64; } MEMORY { ZP: file = "", define = yes, start = $0000, size = $0100; - HEADER: file = %O, start = $0000, size = $0040; + HEADER: file = %O, start = $0000, size = __HEADERSIZE__; BOOT: file = %O, start = $0200, size = __STARTOFDIRECTORY__; - DIR: file = %O, start = $0000, size = 8; - MAIN: file = %O, define = yes, start = $0200, size = $BD38 - __STACKSIZE__; - UPLDR: file = %O, define = yes, start = $BFDC, size = $005C; + DIR: file = %O, start = $0000, size = 16; + MAIN: file = %O, define = yes, start = $0200, size = $C038 - __UPLOADERSIZE__ - $200 - __STACKSIZE__; + UPLOAD: file = %O, define = yes, start = $C038 - __UPLOADERSIZE__, size = $0061; } SEGMENTS { ZEROPAGE: load = ZP, type = zp; @@ -30,8 +31,8 @@ SEGMENTS { RODATA: load = MAIN, type = ro, define = yes; DATA: load = MAIN, type = rw, define = yes; BSS: load = MAIN, type = bss, define = yes; - UPCODE: load = UPLDR, type = ro, define = yes; - UPDATA: load = UPLDR, type = rw, define = yes; + UPCODE: load = UPLOAD, type = ro, define = yes; + UPDATA: load = UPLOAD, type = rw, define = yes; } FEATURES { CONDES: type = constructor, diff --git a/cfg/plus4-asm.cfg b/cfg/plus4-asm.cfg new file mode 100644 index 000000000..df47ba06e --- /dev/null +++ b/cfg/plus4-asm.cfg @@ -0,0 +1,20 @@ +FEATURES { + STARTADDRESS: default = $1001; +} +SYMBOLS { + __LOADADDR__: type = import; +} +MEMORY { + ZP: file = "", start = $0002, size = $001A; + LOADADDR: file = %O, start = %S - 2, size = $0002; + MAIN: file = %O, start = %S, size = $FD00 - %S; +} +SEGMENTS { + ZEROPAGE: load = ZP, type = zp, optional = yes; + LOADADDR: load = LOADADDR, type = ro; + EXEHDR: load = MAIN, type = ro, optional = yes; + CODE: load = MAIN, type = ro; + RODATA: load = MAIN, type = ro, optional = yes; + DATA: load = MAIN, type = rw, optional = yes; + BSS: load = MAIN, type = bss, define = yes; +} diff --git a/cfg/rp6502.cfg b/cfg/rp6502.cfg new file mode 100644 index 000000000..ed40e467d --- /dev/null +++ b/cfg/rp6502.cfg @@ -0,0 +1,34 @@ +SYMBOLS { + __STARTUP__: type = import; + __STACKSIZE__: type = weak, value = $0800; +} +MEMORY { + ZP: file = "", define = yes, start = $0000, size = $0100; + CPUSTACK: file = "", start = $0100, size = $0100; + RAM: file = %O, define = yes, start = $0200, size = $FD00 - __STACKSIZE__; +} +SEGMENTS { + ZEROPAGE: load = ZP, type = zp; + STARTUP: load = RAM, type = ro; + LOWCODE: load = RAM, type = ro, optional = yes; + ONCE: load = RAM, type = ro, optional = yes; + CODE: load = RAM, type = ro; + RODATA: load = RAM, type = ro; + DATA: load = RAM, type = rw, define = yes; + BSS: load = RAM, type = bss, define = yes; +} +FEATURES { + CONDES: type = constructor, + label = __CONSTRUCTOR_TABLE__, + count = __CONSTRUCTOR_COUNT__, + segment = ONCE; + CONDES: type = destructor, + label = __DESTRUCTOR_TABLE__, + count = __DESTRUCTOR_COUNT__, + segment = RODATA; + CONDES: type = interruptor, + label = __INTERRUPTOR_TABLE__, + count = __INTERRUPTOR_COUNT__, + segment = RODATA, + import = __CALLIRQ__; +} diff --git a/cfg/sim6502.cfg b/cfg/sim6502.cfg index 39c33581c..72098f750 100644 --- a/cfg/sim6502.cfg +++ b/cfg/sim6502.cfg @@ -1,12 +1,15 @@ SYMBOLS { __EXEHDR__: type = import; __STACKSIZE__: type = weak, value = $0800; # 2k stack + _peripherals: type = export, value = $FFC0; } + MEMORY { ZP: file = "", start = $0000, size = $0100; HEADER: file = %O, start = $0000, size = $000C; - MAIN: file = %O, define = yes, start = $0200, size = $FDF0 - __STACKSIZE__; + MAIN: file = %O, define = yes, start = $0200, size = $FFC0 - $0200 - __STACKSIZE__; } + SEGMENTS { ZEROPAGE: load = ZP, type = zp; EXEHDR: load = HEADER, type = ro; @@ -18,6 +21,7 @@ SEGMENTS { DATA: load = MAIN, type = rw; BSS: load = MAIN, type = bss, define = yes; } + FEATURES { CONDES: type = constructor, label = __CONSTRUCTOR_TABLE__, diff --git a/cfg/sim65c02.cfg b/cfg/sim65c02.cfg index 39c33581c..72098f750 100644 --- a/cfg/sim65c02.cfg +++ b/cfg/sim65c02.cfg @@ -1,12 +1,15 @@ SYMBOLS { __EXEHDR__: type = import; __STACKSIZE__: type = weak, value = $0800; # 2k stack + _peripherals: type = export, value = $FFC0; } + MEMORY { ZP: file = "", start = $0000, size = $0100; HEADER: file = %O, start = $0000, size = $000C; - MAIN: file = %O, define = yes, start = $0200, size = $FDF0 - __STACKSIZE__; + MAIN: file = %O, define = yes, start = $0200, size = $FFC0 - $0200 - __STACKSIZE__; } + SEGMENTS { ZEROPAGE: load = ZP, type = zp; EXEHDR: load = HEADER, type = ro; @@ -18,6 +21,7 @@ SEGMENTS { DATA: load = MAIN, type = rw; BSS: load = MAIN, type = bss, define = yes; } + FEATURES { CONDES: type = constructor, label = __CONSTRUCTOR_TABLE__, diff --git a/cfg/telestrat.cfg b/cfg/telestrat.cfg index bd720fb8d..52b982eef 100644 --- a/cfg/telestrat.cfg +++ b/cfg/telestrat.cfg @@ -22,7 +22,7 @@ SEGMENTS { CODE: load = MAIN, type = ro; RODATA: load = MAIN, type = ro; DATA: load = MAIN, type = rw; - INIT: load = MAIN, type = rw; + INIT: load = MAIN, type = rw; # uninitialized, but reserves output space ONCE: load = MAIN, type = ro, define = yes; BASTAIL: load = MAIN, type = ro, optional = yes; BSS: load = BSS, type = bss, define = yes; diff --git a/cfg/vic20-asm-32k.cfg b/cfg/vic20-asm-32k.cfg new file mode 100644 index 000000000..3d0341e71 --- /dev/null +++ b/cfg/vic20-asm-32k.cfg @@ -0,0 +1,22 @@ +# Assembly program configuration for expanded VICs (>= +8K). + +FEATURES { + STARTADDRESS: default = $1201; +} +SYMBOLS { + __LOADADDR__: type = import; +} +MEMORY { + ZP: file = "", start = $0002, size = $001A, define = yes; + LOADADDR: file = %O, start = %S - 2, size = $0002; + MAIN: file = %O, start = %S, size = $8000 - %S; +} +SEGMENTS { + ZEROPAGE: load = ZP, type = zp, optional = yes; + LOADADDR: load = LOADADDR, type = ro; + EXEHDR: load = MAIN, type = ro, optional = yes; + CODE: load = MAIN, type = ro; + RODATA: load = MAIN, type = ro; + DATA: load = MAIN, type = rw; + BSS: load = MAIN, type = bss, optional = yes, define = yes; +} diff --git a/cfg/vic20-asm-3k.cfg b/cfg/vic20-asm-3k.cfg new file mode 100644 index 000000000..6ef06957e --- /dev/null +++ b/cfg/vic20-asm-3k.cfg @@ -0,0 +1,22 @@ +# Assembly program configuration for expanded VICs (+3K only). + +FEATURES { + STARTADDRESS: default = $0401; +} +SYMBOLS { + __LOADADDR__: type = import; +} +MEMORY { + ZP: file = "", start = $0002, size = $001A, define = yes; + LOADADDR: file = %O, start = %S - 2, size = $0002; + MAIN: file = %O, start = %S, size = $1E00 - %S; +} +SEGMENTS { + ZEROPAGE: load = ZP, type = zp, optional = yes; + LOADADDR: load = LOADADDR, type = ro; + EXEHDR: load = MAIN, type = ro, optional = yes; + CODE: load = MAIN, type = ro; + RODATA: load = MAIN, type = ro; + DATA: load = MAIN, type = rw; + BSS: load = MAIN, type = bss, optional = yes, define = yes; +} diff --git a/cfg/vic20-asm.cfg b/cfg/vic20-asm.cfg index 286a7f95c..531d3f010 100644 --- a/cfg/vic20-asm.cfg +++ b/cfg/vic20-asm.cfg @@ -1,3 +1,5 @@ +# Assembly program configuration for unexpanded VICs. + FEATURES { STARTADDRESS: default = $1001; } @@ -7,11 +9,12 @@ SYMBOLS { MEMORY { ZP: file = "", start = $0002, size = $001A, define = yes; LOADADDR: file = %O, start = %S - 2, size = $0002; - MAIN: file = %O, start = %S, size = $0DF3 - %S; + MAIN: file = %O, start = %S, size = $1E00 - %S; } SEGMENTS { ZEROPAGE: load = ZP, type = zp, optional = yes; LOADADDR: load = LOADADDR, type = ro; + EXEHDR: load = MAIN, type = ro, optional = yes; CODE: load = MAIN, type = ro; RODATA: load = MAIN, type = ro; DATA: load = MAIN, type = rw; diff --git a/doc/apple2.sgml b/doc/apple2.sgml index 63b40c6f8..9cff996b7 100644 --- a/doc/apple2.sgml +++ b/doc/apple2.sgml @@ -62,7 +62,7 @@ Special locations:

While running

+ + + cl65 -t apple2 myprog.c apple2-integer-basic-compat.o + + + Linker configurations

@@ -289,9 +310,9 @@ the memory from $800 to $1FFF can be added to the heap by calling ProDOS 8 requires for every open file a page-aligned 1 KB I/O buffer. By default these buffers are allocated by the cc65 runtime system on the heap using -. While this is +generally the best solution it means quite some overhead for (especially rather +small) cc65 programs which do open files but don't make use of the heap otherwise. The apple2 package comes with the alternative ProDOS 8 I/O buffer allocation module Apple ][ specific functions

-The functions listed below are special for the Apple ][. See -the for declaration and +The functions and variables listed below are special for the Apple ][. +See the for declaration and usage. _auxtype _dos_type _filetype +_datetime +allow_lowercase +beep +dir_entry_count +get_tv get_ostype +gmtime_dt +mktime_dt rebootafterexit ser_apple2_slot tgi_apple2_mix +Apple IIgs specific functions in accelerator.h

+ +In addition to those, the for declaration and +usage. + + +detect_iigs +get_iigs_speed +set_iigs_speed + + + Hardware access

There's currently no support for direct hardware access. This does not mean @@ -427,17 +469,47 @@ The names in the parentheses denote the symbols to be used for static linking of - Driver for the Apple II Super Serial Card. Supports up to 19200 baud, - requires hardware flow control (RTS/CTS) and does interrupt driven receives. + Driver for the Apple II Super Serial Card. + The SSC is an extension card for the II, II+, IIe; the Apple //c and //c+ have + the same hardware and firmware integrated. + It supports up to 9600 baud, supports no flow control and hardware flow control + (RTS/CTS) and does interrupt driven receives. Speeds faster than 9600 baud + aren't reachable because the ROM and ProDOS IRQ handlers are too slow. + Software flow control (XON/XOFF) is not supported. + Note that because of the peculiarities of the 6551 chip transmits are not interrupt driven, and the transceiver blocks if the receiver asserts flow control because of a full buffer. + Note that using the driver at SER_BAUD_115200 will disable IRQs. It will be up + to the users to use the serial port, either by re-enabling IRQs themselves, + or by directly poll-reading the ACIA DATA register without the help of ser_get(). + The driver defaults to slot 2. Call + Driver for the Apple IIgs serial ports (printer and modem). + It supports up to 9600 baud, supports no flow control and hardware flow control + (RTS/CTS) and does interrupt driven receives. Speeds faster than 9600 baud + aren't reachable because the ROM and ProDOS IRQ handlers are too slow. + Software flow control (XON/XOFF) is not supported. + Note that transmits are not interrupt driven, and the transceiver blocks if + the receiver asserts flow control because of a full buffer. + + The driver defaults to opening the modem port. Calling

@@ -544,6 +616,28 @@ program. See the discussion of the . +ProDOS date/time manipulation

+ + +The readdir and stat function return ProDOS timestamps in their file +creation/modification time attributes. You can convert them to more portable +time representations using either: + + + + DIO

@@ -603,7 +697,17 @@ url="ca65.html" name="assembler manual">. The header file + + extern struct datetime _datetime; + + Example diff --git a/doc/apple2enh.sgml b/doc/apple2enh.sgml index 15ceed04f..094ddd93e 100644 --- a/doc/apple2enh.sgml +++ b/doc/apple2enh.sgml @@ -21,7 +21,8 @@ as it comes with the cc65 C compiler. It describes the memory layout, enhanced Apple //e specific header files, available drivers, and any pitfalls specific to that platform. -Please note that enhanced Apple //e specific functions are just mentioned +Please note that this target requires a 65C02 or 65816 CPU, +enhanced Apple //e specific functions are just mentioned here, they are described in detail in the separate . Even functions marked as "platform dependent" may be available on more than one platform. Please see the function reference for @@ -62,7 +63,7 @@ Special locations:

While running . While this is +generally the best solution it means quite some overhead for (especially rather +small) cc65 programs which do open files but don't make use of the heap otherwise. The apple2enh package comes with the alternative ProDOS 8 I/O buffer allocation module Enhanced Apple //e specific functions

-The functions listed below are special for the enhanced Apple //e. See -the for declaration and +The functions and variables listed below are special for the Apple ][. +See the for declaration and usage. _auxtype _dos_type _filetype +_datetime +beep +dir_entry_count +get_tv get_ostype +gmtime_dt +mktime_dt rebootafterexit ser_apple2_slot tgi_apple2_mix @@ -338,6 +345,20 @@ usage. +Apple IIgs specific functions in accelerator.h

+ +In addition to those, the for declaration and +usage. + + +detect_iigs +get_iigs_speed +set_iigs_speed + + + Hardware access

There's currently no support for direct hardware access. This does not mean @@ -427,17 +448,47 @@ The names in the parentheses denote the symbols to be used for static linking of - Driver for the Apple II Super Serial Card. Supports up to 19200 baud, - requires hardware flow control (RTS/CTS) and does interrupt driven receives. + Driver for the Apple II Super Serial Card. + The SSC is an extension card for the II, II+, IIe; the Apple //c and //c+ have + the same hardware and firmware integrated. + It supports up to 9600 baud, supports no flow control and hardware flow control + (RTS/CTS) and does interrupt driven receives. Speeds faster than 9600 baud + aren't reachable because the ROM and ProDOS IRQ handlers are too slow. + Software flow control (XON/XOFF) is not supported. + Note that because of the peculiarities of the 6551 chip transmits are not interrupt driven, and the transceiver blocks if the receiver asserts flow control because of a full buffer. + Note that using the driver at SER_BAUD_115200 will disable IRQs. It will be up + to the users to use the serial port, either by re-enabling IRQs themselves, + or by directly poll-reading the ACIA DATA register without the help of ser_get(). + The driver defaults to slot 2. Call + Driver for the Apple IIgs serial ports (printer and modem). + It supports up to 9600 baud, supports no flow control and hardware flow control + (RTS/CTS) and does interrupt driven receives. Speeds faster than 9600 baud + aren't reachable because the ROM and ProDOS IRQ handlers are too slow. + Software flow control (XON/XOFF) is not supported. + Note that transmits are not interrupt driven, and the transceiver blocks if + the receiver asserts flow control because of a full buffer. + + The driver defaults to opening the modem port. Calling

@@ -549,6 +600,28 @@ program. See the discussion of the . +ProDOS date/time manipulation

+ + +The readdir and stat function return ProDOS timestamps in their file +creation/modification time attributes. You can convert them to more portable +time representations using either: + + + + DIO

@@ -593,7 +666,7 @@ url="ca65.html" name="assembler manual">. auxiliary type. Therefore, some additional mechanism for specifying the file types is needed. - Specifying the File Type and Auxiliary Type + Specifying the File Type, Auxiliary Type and creation date There are two global variables provided that allow the file type and auxiliary type to be specified before a call to . that can be used to set these variables. It is included in + + extern struct datetime _datetime; + + + Example A text file cannot be created with just the diff --git a/doc/atari.sgml b/doc/atari.sgml index 3057cd8a6..060bc8ad4 100644 --- a/doc/atari.sgml +++ b/doc/atari.sgml @@ -412,8 +412,9 @@ Please mind that ANTIC has memory alignment requirements for "player missile graphics"-data, font data, display lists and screen memory. Creation of a special linker configuration with appropriate aligned segments and switching to that segment in the c-code is usually necessary. A more memory -hungry solution consists in using the " function in conjunction with copying your data to the +allocated memory. Character mapping

diff --git a/doc/atmos.sgml b/doc/atmos.sgml index cef7770e4..e330d9517 100644 --- a/doc/atmos.sgml +++ b/doc/atmos.sgml @@ -1,15 +1,15 @@

-Oric Atmos-specific information for cc65 +<title>Tangerine Oric Atmos-specific information for cc65 <author> <url url="mailto:uz@cc65.org" name="Ullrich von Bassewitz">,<newline> -<url url="mailto:polluks@sdf.lonestar.org" name="Stefan A. Haubenthal">,<newline> +<url url="mailto:polluks@sdf.org" name="Stefan A. Haubenthal">,<newline> <url url="mailto:greg.king5@verizon.net" name="Greg King"> <abstract> -An overview over the Atmos runtime system as it is implemented for the cc65 C -compiler. +An overview over the Oric Atmos runtime system as it is implemented for the cc65 +C compiler. This target is not Oric-1 compatible. </abstract> <!-- Table of contents --> diff --git a/doc/ca65.sgml b/doc/ca65.sgml index b4ef3e188..80224a84e 100644 --- a/doc/ca65.sgml +++ b/doc/ca65.sgml @@ -120,7 +120,7 @@ Long options: --list-bytes n Maximum number of bytes per listing line --memory-model model Set the memory model --pagelength n Set the page length for the listing - --relax-checks Relax some checks (see docs) + --relax-checks Disables some error checks --smart Enable smart mode --target sys Set the target system --verbose Increase verbosity @@ -265,14 +265,17 @@ Here is a description of all the command line options: <label id="option--relax-checks"> <tag><tt>--relax-checks</tt></tag> - Relax some checks done by the assembler. This will allow code that is an + Disables some error checks done by the assembler. This will allow code that is an error in most cases and flagged as such by the assembler, but can be valid in special situations. - Examples are: + Disabled checks are: <itemize> -<item>Short branches between two different segments. -<item>Byte sized address loads where the address is not a zeropage address. +<item>Address vs. fragment size: a byte sized load from an non-zeropage + address is truncated instead of producing an error. +<item>Indirect jump on page boundary: <tt>jmp (label)</tt> on a label that + resides on a page boundary (<tt>$xxFF</tt>) fetches the second byte from the + wrong address on 6502 CPUs, now allowed instead of producing an error. </itemize> @@ -826,49 +829,40 @@ names like "Loop". Here is an example: bne @Loop ; ERROR: Unknown identifier! </verb></tscreen> + <sect1>Unnamed labels<p> -If you really want to write messy code, there are also unnamed labels. These -labels do not have a name (you guessed that already, didn't you?). A colon is -used to mark the absence of the name. +If you really want to write messy code, there are also unnamed labels. To define +an unnamed label, use sole <tt>:</tt>. -Unnamed labels may be accessed by using the colon plus several minus or plus -characters as a label designator. Using the '-' characters will create a back -reference (use the n'th label backwards), using '+' will create a forward -reference (use the n'th label in forward direction). An example will help to -understand this: +To reference an unnamed label, use <tt>:</tt> with several <tt>-</tt> or <tt>+</tt> characters. +The <tt>-</tt> characters will create a back reference (n'th label backwards), +the <tt>+</tt> will create a forward reference (n'th label in forward direction). +As an alternative, angle brackets <tt><</tt> and <tt>></tt> may be used +instead of <tt>-</tt> and <tt>+</tt> with the same meaning. + +Example: <tscreen><verb> - : lda (ptr1),y ; #1 - cmp (ptr2),y - bne :+ ; -> #2 - tax - beq :+++ ; -> #4 - iny - bne :- ; -> #1 - inc ptr1+1 - inc ptr2+1 - bne :- ; -> #1 - - : bcs :+ ; #2 -> #3 - ldx #$FF - rts - - : ldx #$01 ; #3 - : rts ; #4 + cpy #0 + beq :++ + : + sta $2007 + dey + bne :- + : + rts </verb></tscreen> -As you can see from the example, unnamed labels will make even short -sections of code hard to understand, because you have to count labels -to find branch targets (this is the reason why I for my part do -prefer the "cheap" local labels). Nevertheless, unnamed labels are -convenient in some situations, so it's your decision. +Unnamed labels may make even short sections of code hard to understand, because +you have to count labels to find branch targets. It's better to prefer the +"cheap" local labels. Nevertheless, unnamed labels are convenient in some +situations, so it's up to your discretion. <em/Note:/ <ref id="scopes" name="Scopes"> organize named symbols, not unnamed ones, so scopes don't have an effect on unnamed labels. - <sect1>Using macros to define labels and constants<p> While there are drawbacks with this approach, it may be handy in a few rare @@ -1063,7 +1057,7 @@ The namespace token (<tt/::/) is used to access other scopes: .endscope ... - lda foo::bar ; Access foo in scope bar + lda #foo::bar ; Access bar in scope foo </verb></tscreen> The only way to deny access to a scope from the outside is to declare a scope @@ -1367,17 +1361,22 @@ writable. Reading this pseudo variable will give the assembler version according to the following formula: - VER_MAJOR*$100 + VER_MINOR*$10 + <tt>(VER_MAJOR * 0x100) + VER_MINOR</tt> - It may be used to encode the assembler version or check the assembler for - special features not available with older versions. + The upper 8 bits are the major-, the lower 8 bits are the minor version. Example: - Version 2.14 of the assembler will return $2E0 as numerical constant when - reading the pseudo variable <tt/.VERSION/. - + For example, version 47.11 of the assembler would have this macro defined as + <tt/0x2f0b/. + Note: until 2.19 this pseudo variable was defined as <tt>(VER_MAJOR * 0x100) + VER_MINOR * 0x10</tt> - + which resulted in broken values starting at version 2.16 of the assembler. For + this reason the value of this pseudo variable is considered purely informal - you should + not use it to check for a specific assembler version and use different code + according to the detected version - please update your code to work with the + recent version of the assembler instead (There is very little reason to not use + the most recent version - and even less to support older versions in your code). <sect>Pseudo functions<label id="pseudo-functions"><p> @@ -2859,6 +2858,26 @@ See: <tt><ref id=".ASCIIZ" name=".ASCIIZ"></tt>,<tt><ref id=".CHARMAP" name=".CH overridden. When using this feature, you may also get into trouble if later versions of the assembler define new keywords starting with a dot. + <tag><tt>line_continuations</tt><label id="line_continuations"></tag> + + Switch on or off line continuations using the backslash character + before a newline. The option is off by default. + Note: Line continuations do not work in a comment. A backslash at the + end of a comment is treated as part of the comment and does not trigger + line continuation. + + Example: + + <tscreen><verb> + .feature line_continuations + ; Allow line continuations + + lda \ + #$20 ; This is legal now + </verb></tscreen> + + For backward compatibility reasons, the <tt>.LINECONT +</tt> control command + is also supported and enables the same feature. + <tag><tt>long_jsr_jmp_rts</tt><label id="long_jsr_jmp_rts"></tag> Affects 65816 mode only. @@ -3363,26 +3382,6 @@ See: <tt><ref id=".ASCIIZ" name=".ASCIIZ"></tt>,<tt><ref id=".CHARMAP" name=".CH the feature in more detail. -<sect1><tt>.LINECONT</tt><label id=".LINECONT"><p> - - Switch on or off line continuations using the backslash character - before a newline. The option is off by default. - Note: Line continuations do not work in a comment. A backslash at the - end of a comment is treated as part of the comment and does not trigger - line continuation. - The command can be followed by a '+' or '-' character to switch the - option on or off respectively. - - Example: - - <tscreen><verb> - .linecont + ; Allow line continuations - - lda \ - #$20 ; This is legal now - </verb></tscreen> - - <sect1><tt>.LIST</tt><label id=".LIST"><p> Enable output to the listing. The command can be followed by a boolean @@ -3896,7 +3895,7 @@ See: <tt><ref id=".ASCIIZ" name=".ASCIIZ"></tt>,<tt><ref id=".BYTE" name=".BYTE" Reserve storage. The command is followed by one or two constant expressions. The first one is mandatory and defines, how many bytes of - storage should be defined. The second, optional expression must by a + storage should be defined. The second, optional expression must be a constant byte value that will be used as value of the data. If there is no fill value given, the linker will use the value defined in the linker configuration file (default: zero). @@ -4078,7 +4077,9 @@ See: <tt><ref id=".ASCIIZ" name=".ASCIIZ"></tt>,<tt><ref id=".BYTE" name=".BYTE" <sect1><tt>.TAG</tt><label id=".TAG"><p> - Allocate space for a struct or union. + Allocate space for a struct or union. This is equivalent to + <tt><ref id=".RES" name=".RES"></tt> with the + <tt><ref id=".SIZEOF" name=".SIZEOF"></tt> of a struct. Example: @@ -4092,6 +4093,7 @@ See: <tt><ref id=".ASCIIZ" name=".ASCIIZ"></tt>,<tt><ref id=".BYTE" name=".BYTE" .tag Point ; Allocate 4 bytes </verb></tscreen> + See: <ref id="structs" name=""Structs and unions""> <sect1><tt>.UNDEF, .UNDEFINE</tt><label id=".UNDEFINE"><p> @@ -4481,9 +4483,9 @@ different: <item> Macros defined with <tt><ref id=".DEFINE" name=".DEFINE"></tt> may not span more than a line. You may use line continuation (see <tt><ref - id=".LINECONT" name=".LINECONT"></tt>) to spread the definition over - more than one line for increased readability, but the macro itself - may not contain an end-of-line token. + id="line_continuations" name="line_continuations"></tt>) to spread the + definition over more than one line for increased readability, but the + macro itself may not contain an end-of-line token. <item> Macros defined with <tt><ref id=".DEFINE" name=".DEFINE"></tt> share the name space with classic macros, but they are detected and replaced @@ -4857,10 +4859,15 @@ compiler, depending on the target system selected: Structs and unions are special forms of <ref id="scopes" name="scopes">. They are, to some degree, comparable to their C counterparts. Both have a list of -members. Each member allocates storage, and optionally may have a name whose -value, in the case of a struct, usually is the storage offset from the -beginning, and in the case of a union, doesn't change, and usually is zero. +members. Each member allocates storage, and optionally may have a name. +Each named member has a constant value equal to the storage offset from the +beginning of the structure. In the case of a union, all members are placed at +the same offset, typically 0. + +Each named member also has a storage size which can be accessed with the +<tt><ref id=".SIZEOF" name=".SIZEOF"></tt> operator. The struct or union itself +also has a <tt/.SIZEOF/ indicating its total storage size. <sect1>Declaration<p> @@ -4887,8 +4894,9 @@ A struct or union may not necessarily have a name. If it is anonymous, no local scope is opened; the identifiers used to name the members are placed into the current scope instead. -A struct may contain unnamed members and definitions of local structs/unions. -The storage allocators may contain a multiplier, as in the example below: +Storage allocators may contain a multiplier. A struct may also contain members +and definitions of local structs/unions. Example: + <tscreen><verb> .struct Circle .struct Point @@ -4897,7 +4905,8 @@ The storage allocators may contain a multiplier, as in the example below: Radius .word .endstruct </verb></tscreen> -The size of the Circle struct is 6 (three words). + +In this example the size of the Circle struct is 6 (three words). <sect1>The storage allocator keywords<p> @@ -4907,7 +4916,7 @@ The size of the Circle struct is 6 (three words). <tag/.BYTE, .RES/ Allocates multiples of 1 byte. <tt/.RES/ requires an operand. - <tag/.DBYTE, .WORD, .ADDR/ + <tag/.DBYT, .WORD, .ADDR/ Allocates multiples of 2 bytes. <tag/.FARADDR/ @@ -4916,6 +4925,15 @@ The size of the Circle struct is 6 (three words). <tag/.DWORD/ Allocates multiples of 4 bytes. + <tag/.TAG/ + Allocates a previously defined struct. + + <tag/.STRUCT, .UNION/ + Begins a nested .struct or .union definition, and allocates it. + Note that its member offset values will begin at 0, unless this nested + structure is anonymous, in which case they will instead become members of + the enclosing scope. + </descrip> @@ -4960,13 +4978,54 @@ name=".TAG"> directive. C: .tag Circle </verb></tscreen> -Currently, members are just offsets from the start of the struct or union. To +Members are just offsets from the start of the struct or union. To access a field of a struct, the member offset must be added to the address of the struct variable itself: <tscreen><verb> - lda C+Circle::Radius ; Load circle radius into A + lda C + Circle::Radius ; Load circle radius + lda C + Circle::Origin + Point::ycoord ; Load circle origin.ycoord </verb></tscreen> -That may change in a future version of the assembler. + +Nested structures or unions are treated differently depending on whether they +are anonymous. If named, a new structure definition is created within the +enclosing scope, with its offsets beginning at 0. If anonymous, the members of +the new structure are added to the enclosing scope instead, with offsets +continuing through that scope. Example: + +<tscreen><verb> + .struct Object + id .byte ; Object::id = 0 + target .struct Point ; Object::target = 1 + xcoord .word ; Object::Point::xcoord = 0 + ycoord .word ; Object::Point::ycoord = 2 + .endstruct + cost .struct ; Object::cost = 5 + price .word ; Object::price = 5 + tax .word ; Object::tax = 7 + .endstruct + .struct + radius .word ; Object::radius = 9 + .endstruct + .endstruct + +O: .tag Object + lda O + Object::target + Object::Point::ycoord ; Named struct + lda O + Object::tax ; Anonymous + lda O + Object::radius ; Anonymous + + ; Be careful not to use a named nested structure without also adding the + ; offset to the nested structure itself. + lda O + Object::Point::ycoord ; Incorrect! + lda O + Object::target + Object::Point::ycoord ; Correct +</verb></tscreen> + +In this example, the first nested structure is named "Point", and its member +offsets begin at 0. On the other hand, the two anonymous structures simply +continue to add members to the enclosing "Object" structure. + +Note that an anonymous structure does not need a member name, since all of its +members become part of the enclosing structure. The "cost" member in the +example is redundantly the same offset as its first member "price". <sect1>Limitations<p> diff --git a/doc/cc65.sgml b/doc/cc65.sgml index 4303edb48..6793603d5 100644 --- a/doc/cc65.sgml +++ b/doc/cc65.sgml @@ -61,7 +61,7 @@ Short options: -Os Inline some standard functions -T Include source as comment -V Print the compiler version number - -W warning[,...] Suppress warnings + -W [-+]warning[,...] Control warnings ('-' disables, '+' enables) -d Debug mode -g Add debug info to object file -h Help (this text) @@ -84,8 +84,9 @@ Long options: --create-full-dep name Create a full make dependency file --data-name seg Set the name of the DATA segment --debug Debug mode + --debug-tables name Write symbol table debug info to a file --debug-info Add debug info to object file - --debug-opt name Configure optimizations with a file + --debug-opt name Debug optimization steps --debug-opt-output Debug output of each optimization step --dep-target target Use this dependency target --disable-opt name Disable an optimization step @@ -807,6 +808,9 @@ and the one defined by the ISO standard: <itemize> <item> The datatypes "float" and "double" are not available. + Floating point constants may be used, though they will have to be + converted and stored into integer values. + Floating point arithmetic expressions are not supported. <p> <item> C Functions may pass and return structs (or unions) by value, but only of 1, 2 or 4 byte sizes. @@ -820,6 +824,11 @@ and the one defined by the ISO standard: as it sounds, since the 6502 has so few registers that it isn't possible to keep values in registers anyway. <p> +<item> In <tt/cc65/ mode, <tt/main()/ cannot be called recursively. If this + is necessary, the program must be compiled in <tt/c89/ or <tt/c99/ mode + using the <tt><ref id="option--standard" name="--standard"></tt> + command line option. + <p> </itemize> There may be some more minor differences I'm currently not aware of. The @@ -834,21 +843,21 @@ This cc65 version has some extensions to the ISO C standard. <itemize> -<item> The compiler allows to insert assembler statements into the output - file. The syntax is +<item> The compiler allows to insert inline assembler code in the form of the + <tt/asm/ expression into the output file. The syntax is <tscreen><verb> - asm [optional volatile] (<string literal>[, optional parameters]) ; + asm [optional volatile] (<string literal>[, optional parameters]) </verb></tscreen> or <tscreen><verb> - __asm__ [optional volatile] (<string literal>[, optional parameters]) ; + __asm__ [optional volatile] (<string literal>[, optional parameters]) </verb></tscreen> The first form is in the user namespace; and, is disabled if the <tt/-A/ switch is given. - There is a whole section covering inline assembler statements, + There is a whole section covering the inline assembler, <ref id="inline-asm" name="see there">. <p> @@ -1005,6 +1014,13 @@ This cc65 version has some extensions to the ISO C standard. <tt/_Static_assert/ is also available as the macro <tt/static_assert/ in <tt/assert.h/. + Note: The string literal as the message in the <tt/_Static_assert/ + declaration is not subject to string literal translation (see + <tt/<ref id="pragma-charmap" name="#pragma charmap()">/) and will + always be in the host encoding. On the other hand, any character or + string literals present in the condition expression of the + <tt/_Static_assert/ declaration will be translated as usual. + <item> cc65 supports bit-fields of any integral type that is int-sized or smaller, and enumerated types with those types as their underlying type. (Only <tt/int/, <tt/signed int/, and <tt/unsigned int/ are @@ -1037,6 +1053,16 @@ This cc65 version has some extensions to the ISO C standard. unsigned char foo = 0b101; // sets it to 5 </verb></tscreen> +<item> The character escape '\e', a GCC C extension, is accepted. + In ASCII this is the escape character 0x1B, which may be + remapped in other character sets via a #pragma charmap. + It can be disabled with the <tt><ref id="option--standard" + name="--standard"></tt> option. + + <tscreen><verb> + unsigned char foo = '\e'; // sets it to 0x1B or equivalent + </verb></tscreen> + </itemize> <p> @@ -1263,6 +1289,12 @@ If the first parameter is <tt/push/, the old value is saved onto a stack before changing it. The value may later be restored by using the <tt/pop/ parameter with the <tt/#pragma/. +For all pragma names that contain hyphens, the same name using underlines +instead of the hyphens is available as an alternative. While the former +resembles the corresponding command line option and is more orthogonal, the +latter may be more compatible with external tools that rewrite the token +sequences of the input. + <sect1><tt>#pragma allow-eager-inline ([push,] on|off)</tt><label id="pragma-allow-eager-inline"><p> @@ -1314,7 +1346,9 @@ parameter with the <tt/#pragma/. <sect1><tt>#pragma charmap (<index>, <code>)</tt><label id="pragma-charmap"><p> - Each literal string and each literal character in the source is translated + Each literal string and each literal character in the preprocessed source, + except when used in an <tt/asm/ expression as the inline assembler code or + in a <tt/_Static_assert/ declaration as the failure message, is translated by use of a translation table. That translation table is preset when the compiler is started, depending on the target system; for example, to map ISO-8859-1 characters into PETSCII if the target is a Commodore machine. @@ -1346,7 +1380,7 @@ parameter with the <tt/#pragma/. Example: <tscreen><verb> /* Use a space wherever an 'a' occurs in ISO-8859-1 source */ - #pragma charmap (0x61, 0x20); + #pragma charmap (0x61, 0x20) </verb></tscreen> @@ -1601,13 +1635,13 @@ parameter with the <tt/#pragma/. This pragma sets a wrapper for functions, often used for trampolines. - The name is a function returning <tt/void/, and taking no parameters. + The <tt/name/ is a wrapper function returning <tt/void/, and taking no parameters. It must preserve the CPU's <tt/A/ and <tt/X/ registers if it wraps any <tt/__fastcall__/ functions that have parameters. It must preserve the <tt/Y/ register if it wraps any variadic functions (they have "<tt/.../" in their prototypes). - The identifier is an 8-bit number that's set into <tt/tmp4/. If the identifier + The <tt/identifier/ is an 8-bit number that's set into <tt/tmp4/. If the <tt/identifier/ is "bank", then ca65's <tt><url url="ca65.html#.BANK" name=".bank"></tt> function will be used to determine the number from the bank attribute defined in the linker config, see <url url="ld65.html#MEMORY" name="Other MEMORY area attributes">. Note that @@ -1617,6 +1651,11 @@ parameter with the <tt/#pragma/. The address of a wrapped function is passed in <tt/ptr4/. The wrapper can call that function by using "<tt/jsr callptr4/". + All functions ever declared or defined when this pragma is in effect will be wrapped + when they are called explicitly by their names later in the same translation unit. + Invocation of these functions in any other ways, for example, that via a function + pointer or in inline assembly code, will not be wrapped. + This feature is useful, for example, with banked memory, to switch banks automatically to where a wrapped function resides, and then to restore the previous bank when it returns. @@ -1711,23 +1750,23 @@ bloated code and a slowdown. <sect>Inline assembler<label id="inline-asm"><p> -The compiler allows to insert assembler statements into the output file. The -syntax is +The compiler allows to insert inline assembler code in the form of the <tt/asm/ +expression into the output file. The syntax is <tscreen><verb> - asm [optional volatile] (<string literal>[, optional parameters]) ; + asm [optional volatile] (<string literal>[, optional parameters]) </verb></tscreen> or <tscreen><verb> - __asm__ [optional volatile] (<string literal>[, optional parameters]) ; + __asm__ [optional volatile] (<string literal>[, optional parameters]) </verb></tscreen> <p> The first form is in the user namespace; and, is disabled by <tt><ref id="option--standard" name="--standard"></tt> if the argument is not <tt/cc65/. -The <tt/asm/ statement can be used only inside a function. Please note that -the result of an inline assembler expression is always of type <tt/void/. +The <tt/asm/ expression can be used only inside a function. The result of an +<tt/asm/ expression is always of type <tt/void/. The contents of the string literal are preparsed by the compiler; and, inserted into the generated assembly output, so that it can be processed further by @@ -1738,15 +1777,15 @@ even if the ca65 assembler (which is used to translate the generated assembler code) would accept them. The built-in inline assembler is not a replacement for the full-blown macro assembler which comes with the compiler. -Note: Inline assembler statements are subject to all optimizations done by the -compiler. There currently is no way to protect an inline assembler statement +Note: Inline assembler expressions are subject to all optimizations done by the +compiler. There currently is no way to protect an inline assembler expression -- alone -- from being moved or removed completely by the optimizer. If in doubt, check the generated assembler output; or, disable optimizations (for that function). As a shortcut, you can put the <tt/volatile/ qualifier in your <tt/asm/ -statements. It will disable optimization for the functions in which those -<tt/asm volatile/ statements sit. The effect is the same as though you put +expressions. It will disable optimization for the functions in which those +<tt/asm volatile/ expressions sit. The effect is the same as though you put <tt/#pragma optimize(push, off)/ above those functions, and <tt/#pragma optimize(pop)/ below those functions. @@ -1754,6 +1793,13 @@ The string literal may contain format specifiers from the following list. For each format specifier, an argument is expected which is inserted instead of the format specifier, before passing the assembly code line to the backend. +Note: The string literal as the inline assembler code itself in the <tt/asm/ +expression is not subject to string literal translation (see +<tt/<ref id="pragma-charmap" name="#pragma charmap()">/) and will always +be in the host encoding. On the other hand, all character and string literals +as the arguments for replacing the format specifiers will be translated as +usual. + <itemize> <item><tt/%b/ - Numerical 8-bit value <item><tt/%w/ - Numerical 16-bit value @@ -1841,7 +1887,7 @@ Arrays also can be accessed: <p> Note: Do not embed the assembler labels that are used as names of global -variables or functions into your <tt/asm/ statements. Code such as this: +variables or functions into your <tt/asm/ expressions. Code such as this: <tscreen><verb> int foo; diff --git a/doc/chrcvt65.sgml b/doc/chrcvt65.sgml index a631a0004..00909e12f 100644 --- a/doc/chrcvt65.sgml +++ b/doc/chrcvt65.sgml @@ -19,7 +19,7 @@ the native format. chrcvt65 is a vector font converter. It is able to convert a "BGI Stroked Font" to a compact TGI native vector font. See the function <url -url="funcref.html#tgi_load_vectorfont" name="tgi_load_vectorfont"> for usage. +url="tgi.html#tgi_load_vectorfont" name="tgi_load_vectorfont"> for usage. diff --git a/doc/cl65.sgml b/doc/cl65.sgml index 24d2f5927..f48e1353c 100644 --- a/doc/cl65.sgml +++ b/doc/cl65.sgml @@ -261,6 +261,9 @@ different options for different files on the command line. As an example. translates main.c with full optimization and module.c with less optimization and debug info enabled. +Note that the target system (-t , --target) must be specified before any file +unless using the default target of c64 + The type of an input file is derived from its extension: <itemize> diff --git a/doc/customizing.sgml b/doc/customizing.sgml index e18bcf86c..58631eb3c 100644 --- a/doc/customizing.sgml +++ b/doc/customizing.sgml @@ -140,7 +140,7 @@ FEATURES { SYMBOLS { # Define the stack size for the application - __STACKSIZE__: value = $0200, weak = yes; + __STACKSIZE__: value = $0200, type = weak; } </code></tscreen> diff --git a/doc/cx16.sgml b/doc/cx16.sgml index 78a51206b..a718e52fa 100644 --- a/doc/cx16.sgml +++ b/doc/cx16.sgml @@ -243,6 +243,12 @@ point to <tt/cx320p1.tgi (cx320p1_tgi)/. a way that's compatible with some of the other color drivers). </descrip><p> +<descrip> + <tag><tt/cx640p1.tgi (cx640p1_tgi)/</tag> + This driver features a resolution of 640 across and 480 down with 2 colors, + black and white. +</descrip><p> + <sect1>Extended memory drivers<p> diff --git a/doc/da65.sgml b/doc/da65.sgml index 3a01ce4f5..94fbfbd29 100644 --- a/doc/da65.sgml +++ b/doc/da65.sgml @@ -184,7 +184,7 @@ Here is a description of all the command line options: <label id="option--mnemonic-column"> <tag><tt>--mnemonic-column n</tt></tag> - Specifies the column where a mnemonic or pseudo instrcuction is output. + Specifies the column where a mnemonic or pseudo instruction is output. <label id="option--pagelength"> @@ -255,7 +255,7 @@ disassembler may be told to recognize either the 65SC02 or 65C02 CPUs. The latter understands the same opcodes as the former, plus 16 additional bit manipulation and bit test-and-branch commands. Using 6502x as CPU the illegal opcodes of 6502 CPU are detected and displayed. 6502dtv setting recognizes the -emulated CPU instructons of the C64DTV device. +emulated CPU instructions of the C64DTV device. When disassembling 4510 code, due to handling of 16-bit wide branches, da65 @@ -283,7 +283,7 @@ Some instructions may generate labels in the first pass, while most other instructions do not generate labels, but use them if they are available. Among others, the branch and jump instructions will generate labels for the target of the branch in the first pass. External labels (taken from the info file) -have precedence over internally generated ones, They must be valid identifiers +have precedence over internally generated ones. They must be valid identifiers as specified for the ca65 assembler. Internal labels (generated by the disassembler) have the form <tt/Labcd/, where <tt/abcd/ is the hexadecimal address of the label in upper case letters. You should probably avoid using @@ -304,7 +304,7 @@ name="next section"> for more information. The info file contains lists of specifications grouped together. Each group directive has an identifying token and an attribute list enclosed in curly braces. Attributes have a name followed by a value. The syntax of the value -depends on the type of the attribute. String attributes are places in double +depends on the type of the attribute. String attributes are placed in double quotes, numeric attributes may be specified as decimal numbers or hexadecimal with a leading dollar sign. There are also attributes where the attribute value is a keyword; in this case, the keyword is given as-is (without quotes or @@ -317,8 +317,8 @@ anything). Each attribute is terminated by a semicolon. <sect1>Comments<p> -Comments start with a hash mark (<tt/#/) or a double slash (<tt>//</tt>); -and, extend from the position of the mark to the end of the current line. +Comments start with a hash mark (<tt/#/) or a double slash (<tt>//</tt>) +and extend from the position of the mark to the end of the current line. Hash marks or double slashes inside of strings will <em/not/ start a comment, of course. @@ -359,20 +359,20 @@ following attributes are recognized: <tag><tt/HEXOFFS/</tag> - The attribute is followed by a boolean value. If true, offsets to labels are + This attribute is followed by a boolean value. If true, offsets to labels are output in hex, otherwise they're output in decimal notation. The default is false. The attribute may be changed on the command line using the <tt><ref id="option--hexoffs" name="--hexoffs"></tt> option. <tag><tt/INPUTNAME/</tag> - The attribute is followed by a string value, which gives the name of the + This attribute is followed by a string value, which gives the name of the input file to read. If it is present, the disassembler does not accept an input file name on the command line. <tag><tt/INPUTOFFS/</tag> - The attribute is followed by a numerical value that gives an offset into + This attribute is followed by a numerical value that gives an offset into the input file which is skipped before reading data. The attribute may be used to skip headers or unwanted code sections in the input file. @@ -412,7 +412,7 @@ following attributes are recognized: <label id="OUTPUTNAME"> <tag><tt/OUTPUTNAME/</tag> - The attribute is followed by string value, which gives the name of the + This attribute is followed by string value, which gives the name of the output file to write. If it is present, specification of an output file on the command line using the <tt><ref id="option-o" name="-o"></tt> option is not allowed. @@ -433,8 +433,8 @@ following attributes are recognized: This attribute may be used instead of the <tt><ref id="option--start-addr" name="--start-addr"></tt> option on the command line. It takes a numerical parameter. The default for the start address is $10000 minus the size of - the input file (this assumes that the input file is a ROM that contains the - reset and irq vectors). + the input file. (This assumes that the input file is a ROM that contains the + reset and irq vectors.) <tag><tt/TEXTCOLUMN/</tag> @@ -468,7 +468,7 @@ following attributes are recognized: <tag><tt>NAME</tt></tag> This is a convenience attribute. It takes a string argument and will cause the disassembler to define a label for the start of the range with the - given name. So a separate <tt><ref id="infofile-label" name="LABEL"></tt> + given name so a separate <tt><ref id="infofile-label" name="LABEL"></tt> directive is not needed. <tag><tt>START</tt></tag> @@ -509,8 +509,8 @@ following attributes are recognized: <tag><tt>SKIP</tt></tag> The range is simply ignored when generating the output file. Please note that this means that reassembling the output file will <em/not/ generate - the original file, not only because the missing piece in between, but also - because the following code will be located on wrong addresses. Output + the original file, not only because of the missing piece in between, but + also because the following code will be located on wrong addresses. Output generated with <tt/SKIP/ ranges will need manual rework. <tag><tt>TEXTTABLE</tt></tag> @@ -529,7 +529,7 @@ following attributes are recognized: <tag><tt>ADDRMODE</tt></tag> When disassembling 65816 code, this specifies the M and X flag states - for this range. It's a string argument of the form "mx", capital letters + for this range. It's a string argument of the form "mx". Capital letters mean the flag is enabled. </descrip> @@ -561,9 +561,9 @@ code. The following attributes are recognized: <tag><tt>NAME</tt></tag> The attribute is followed by a string value which gives the name of the - label. Empty names are allowed, in this case the disassembler will create - an unnamed label (see the assembler docs for more information about unnamed - labels). + label. Empty names are allowed; in this case the disassembler will create + an unnamed label. (See the assembler docs for more information about unnamed + labels.) <tag><tt>SIZE</tt></tag> This attribute is optional and may be used to specify the size of the data @@ -595,15 +595,18 @@ disassembled code. The following attributes are recognized: <descrip> <tag><tt>START</tt></tag> - Followed by a numerical value. Specifies the start address of the segment. + This attribute is followed by a numerical value which specifies the start + address of the segment. <tag><tt>END</tt></tag> - Followed by a numerical value. Specifies the end address of the segment. The - end address is the last address that is a part of the segment. + This attribute is followed by a numerical value which specifies the end + address of the segment. The end address is the last address that is a part of + the segment. <tag><tt>NAME</tt></tag> - The attribute is followed by a string value which gives the name of the + This attribute is followed by a string value which gives the name of the segment. + </descrip> All attributes are mandatory. Segments must not overlap. The disassembler will @@ -635,10 +638,11 @@ The following attributes are recognized: <descrip> <tag><tt>FILE</tt></tag> - Followed by a string value. Specifies the name of the file to read. + This attribute is followed by a string value. It specifies the name of the + file to read. <tag><tt>COMMENTSTART</tt></tag> - The optional attribute is followed by a character constant. It specifies the + This optional attribute is followed by a character constant. It specifies the character that starts a comment. The default value is a semicolon. This value is ignored if <tt/IGNOREUNKNOWN/ is true. diff --git a/doc/funcref.sgml b/doc/funcref.sgml index 2a6d77adc..e534b47be 100644 --- a/doc/funcref.sgml +++ b/doc/funcref.sgml @@ -71,18 +71,21 @@ function. <item><ref id="detect_c64dtv" name="detect_c64dtv"> <item><ref id="detect_c65" name="detect_c65"> <item><ref id="detect_chameleon" name="detect_chameleon"> +<item><ref id="detect_iigs" name="detect_iigs"> <item><ref id="detect_scpu" name="detect_scpu"> <item><ref id="detect_turbomaster" name="detect_turbomaster"> <item><ref id="get_c128_speed" name="get_c128_speed"> <item><ref id="get_c64dtv_speed" name="get_c64dtv_speed"> <item><ref id="get_c65_speed" name="get_c65_speed"> <item><ref id="get_chameleon_speed" name="get_chameleon_speed"> +<item><ref id="get_iigs_speed" name="get_iigs_speed"> <item><ref id="get_scpu_speed" name="get_scpu_speed"> <item><ref id="get_turbomaster_speed" name="get_turbomaster_speed"> <item><ref id="set_c128_speed" name="set_c128_speed"> <item><ref id="set_c64dtv_speed" name="set_c64dtv_speed"> <item><ref id="set_c65_speed" name="set_c65_speed"> <item><ref id="set_chameleon_speed" name="set_chameleon_speed"> +<item><ref id="set_iigs_speed" name="set_iigs_speed"> <item><ref id="set_scpu_speed" name="set_scpu_speed"> <item><ref id="set_turbomaster_speed" name="set_turbomaster_speed"> </itemize> @@ -92,7 +95,13 @@ function. <itemize> <item>_dos_type +<item>allow_lowercase +<item><ref id="beep" name="beep"> +<item><ref id="dir_entry_count" name="dir_entry_count"> +<item><ref id="get_tv" name="get_tv"> <item><ref id="get_ostype" name="get_ostype"> +<item><ref id="gmtime_dt" name="gmtime_dt"> +<item><ref id="mktime_dt" name="mktime_dt"> <item>rebootafterexit </itemize> @@ -101,7 +110,12 @@ function. <itemize> <item>_dos_type +<item><ref id="beep" name="beep"> +<item><ref id="dir_entry_count" name="dir_entry_count"> +<item><ref id="get_tv" name="get_tv"> <item><ref id="get_ostype" name="get_ostype"> +<item><ref id="gmtime_dt" name="gmtime_dt"> +<item><ref id="mktime_dt" name="mktime_dt"> <item>rebootafterexit <item><ref id="videomode" name="videomode"> </itemize> @@ -128,7 +142,7 @@ function. <!-- <item><ref id="_setcolor_low" name="_setcolor_low"> --> <item><ref id="_sound" name="_sound"> <item><ref id="get_ostype" name="get_ostype"> -<!-- <item><ref id="get_tv" name="get_tv"> --> +<item><ref id="get_tv" name="get_tv"> </itemize> (incomplete) @@ -145,6 +159,7 @@ function. <item><ref id="atmos_tick" name="atmos_tick"> <item><ref id="atmos_tock" name="atmos_tock"> <item><ref id="atmos_zap" name="atmos_zap"> +<item><ref id="waitvsync" name="waitvsync"> </itemize> @@ -214,7 +229,7 @@ function. <!-- <item><ref id="cbm_readdir" name="cbm_readdir"> --> <!-- <item><ref id="cbm_save" name="cbm_save"> --> <!-- <item><ref id="cbm_write" name="cbm_write"> --> -<!-- <item><ref id="get_tv" name="get_tv"> --> +<item><ref id="get_tv" name="get_tv"> <item><ref id="kbrepeat" name="kbrepeat"> <item><ref id="waitvsync" name="waitvsync"> </itemize> @@ -334,9 +349,11 @@ function. <itemize> <!-- <item><ref id="get_numbanks" name="get_numbanks"> --> -<!-- <item><ref id="get_ostype" name="get_ostype"> --> -<!-- <item><ref id="get_tv" name="get_tv"> --> +<item><ref id="get_ostype" name="get_ostype"> +<item><ref id="get_tv" name="get_tv"> <!-- <item><ref id="set_tv" name="set_tv"> --> +<!-- <item><ref id="vera_layer_enable" name="vera_layer_enable"> --> +<!-- <item><ref id="vera_sprites_enable" name="vera_sprites_enable"> --> <item><ref id="videomode" name="videomode"> <!-- <item><ref id="vpeek" name="vpeek"> --> <!-- <item><ref id="vpoke" name="vpoke"> --> @@ -428,7 +445,7 @@ see also <tt>testcode/lib/em-test.c</tt> and <tt>samples/multidemo.c</tt>. <sect1><tt/gamate.h/<label id="gamate.h"><p> <itemize> -<!-- <item><ref id="get_tv" name="get_tv"> --> +<item><ref id="get_tv" name="get_tv"> <item><ref id="waitvsync" name="waitvsync"> </itemize> @@ -440,6 +457,16 @@ see also <tt>testcode/lib/em-test.c</tt> and <tt>samples/multidemo.c</tt>. <url url="geos.html" name="GEOS API">. +<sect1><tt/inet.h/<label id="inet.h"><p> + +<itemize> +<item><ref id="htonl" name="htonl"> +<item><ref id="htons" name="htons"> +<item><ref id="ntohl" name="ntohl"> +<item><ref id="ntohs" name="ntohs"> +</itemize> + + <sect1><tt/joystick.h/<label id="joystick.h"><p> <itemize> @@ -452,6 +479,18 @@ see also <tt>testcode/lib/em-test.c</tt> and <tt>samples/multidemo.c</tt>. </itemize> +<sect1><tt/kim1.h/<label id="kim1.h"><p> + +<itemize> +<!-- <item><ref id="getkey" name="getkey"> --> +<!-- <item><ref id="scandisplay" name="scandisplay"> --> +<item><ref id="loadt" name="loadt"> +<item><ref id="dumpt" name="dumpt"> +</itemize> + +(incomplete) + + <sect1><tt/locale.h/<label id="locale.h"><p> <itemize> @@ -482,6 +521,14 @@ see also <tt>testcode/lib/em-test.c</tt> and <tt>samples/multidemo.c</tt>. </itemize> +<sect1><tt/lzsa.h/<label id="lzsa.h"><p> + +<itemize> +<item><ref id="decompress_lzsa1" name="decompress_lzsa1"> +<item><ref id="decompress_lzsa2" name="decompress_lzsa2"> +</itemize> + + <sect1><tt/modload.h/<label id="modload.h"><p> <itemize> @@ -513,7 +560,7 @@ see also <tt>testcode/lib/em-test.c</tt> and <tt>samples/multidemo.c</tt>. <sect1><tt/nes.h/<label id="nes.h"><p> <itemize> -<!-- <item><ref id="get_tv" name="get_tv"> --> +<item><ref id="get_tv" name="get_tv"> <item><ref id="waitvsync" name="waitvsync"> </itemize> @@ -531,7 +578,7 @@ It does not declare any functions. <sect1><tt/pce.h/<label id="pce.h"><p> <itemize> -<!-- <item><ref id="get_tv" name="get_tv"> --> +<item><ref id="get_tv" name="get_tv"> <item><ref id="waitvsync" name="waitvsync"> </itemize> @@ -701,7 +748,7 @@ communication, see also <tt>testcode/lib/ser-test.c</tt>. <item><ref id="ltoa" name="ltoa"> <item><ref id="malloc" name="malloc"> <item><ref id="perror" name="perror"> -<!-- <item><ref id="posix_memalign" name="posix_memalign"> --> +<item><ref id="posix_memalign" name="posix_memalign"> <!-- <item><ref id="putenv" name="putenv"> --> <item><ref id="qsort" name="qsort"> <item><ref id="rand" name="rand"> @@ -747,6 +794,7 @@ communication, see also <tt>testcode/lib/ser-test.c</tt>. <item><ref id="strqtok" name="strqtok"> <item><ref id="strrchr" name="strrchr"> <item><ref id="strspn" name="strspn"> +<item><ref id="strcasestr" name="strcasestr"> <item><ref id="strstr" name="strstr"> <item><ref id="strtok" name="strtok"> <item><ref id="strxfrm" name="strxfrm"> @@ -822,6 +870,20 @@ communication, see also <tt>testcode/lib/ser-test.c</tt>. (incomplete) +<sect1><tt/stat.h/<label id="sys/stat.h"><p> + +<itemize> +<item><ref id="stat" name="stat"> +</itemize> + + +<sect1><tt/statvfs.h/<label id="sys/statvfs.h"><p> + +<itemize> +<item><ref id="statvfs" name="statvfs"> +</itemize> + + <sect1><tt/vic20.h/<label id="vic20.h"><p> (incomplete) @@ -839,6 +901,13 @@ communication, see also <tt>testcode/lib/ser-test.c</tt>. (incomplete) +<sect1><tt/zx02.h/<label id="zx02.h"><p> + +<itemize> +<item><ref id="decompress_zx02" name="decompress_zx02"> +</itemize> + + <sect>Alphabetical function reference<p> <sect1>_DE_ISDIR<label id="_DE_ISDIR"><p> @@ -1726,16 +1795,17 @@ used in presence of a prototype. <descrip> <tag/Function/Beep sound. <tag/Header/<tt/<ref id="sym1.h" name="sym1.h">/ +<tag/Header/<tt/<ref id="apple2.h" name="apple2.h">/ <tag/Declaration/<tt/void beep(void);/ <tag/Description/<tt/beep/ makes a brief tone. <tag/Notes/<itemize> -<item>The function is specific to the Sym-1. +<item>The function is specific to the Sym-1 and Apple2 platforms. </itemize> <tag/Availability/cc65 <tag/See also/ <ref id="fdisp" name="fdisp">, <ref id="loadt" name="loadt">, -<ref id="dumpt" name="dumpt">, +<ref id="dumpt" name="dumpt"> <tag/Example/None. </descrip> </quote> @@ -1880,7 +1950,7 @@ be used in presence of a prototype. <ref id="_swap" name="_swap">, <ref id="memcpy" name="memcpy">, <ref id="memmove" name="memmove">, -<ref id="memset" name="memset">, +<ref id="memset" name="memset"> <tag/Example/None. </descrip> </quote> @@ -1953,7 +2023,7 @@ sent a command to TALK and a secondary address if it needs one. </itemize> <tag/Availability/cc65 <tag/See also/ -<ref id="cbm_k_talk" name="cbm_k_talk">, +<ref id="cbm_k_talk" name="cbm_k_talk"> <tag/Example/None. </descrip> </quote> @@ -2355,8 +2425,8 @@ function, in order to provide input from the keyboard. <tag/See also/ <ref id="cbm_k_getin" name="cbm_k_getin">, <ref id="cbm_k_udtim" name="cbm_k_udtim">, -<ref id="cgetc" name="cgetc">, -<!-- <ref id="getc" name="getc"> --> +<ref id="cgetc" name="cgetc"> +<!-- <ref id="getc" name="getc">, --> <!-- <ref id="getchar" name="getchar"> --> <tag/Example/None. </descrip> @@ -2383,7 +2453,7 @@ the address must first be ORed with $60. <tag/Availability/cc65 <tag/See also/ <ref id="cbm_k_listen" name="cbm_k_listen"> -<tag/Exampe/None. +<tag/Example/None. </descrip> </quote> @@ -2827,6 +2897,79 @@ setting the time may not work. See also the platform-specific information. </quote> +<sect1>gmtime_dt<label id="gmtime_dt"><p> + +<quote> +<descrip> +<tag/Function/Converts a ProDOS date to a struct tm. +<tag/Header/<tt/<ref id="apple2.h" name="apple2.h">/ +<tag/Declaration/<tt/struct tm* __fastcall__ gmtime_dt (const struct datetime* dt);/ +<tag/Description/The <tt/gmtime_dt/ function converts the given +proDOS date/time to a struct tm. On error, NULL is returned and <tt/errno/ is set +to an error code describing the reason for the failure. +<tag/Notes/<itemize> +<item>The function is only available as fastcall function, so it may only +be used in presence of a prototype. +<item>This function is only available on Apple II. +<item>On Apple II, you can't stat() an opened file. stat() before opening. +</itemize> +<tag/Availability/cc65 +<tag/Example/ +<verb> + +#include <stdio.h> +#include <time.h> +#include <sys/stat.h> +int main(void) +{ + struct stat st; + struct tm* tm; + if (stat ("/disk/file", &st) == 0) { + tm = gmtime_dt (&st.st_ctime); + if (tm) + printf ("File created on %s\n", asctime(tm)); + } +} +</verb> +</descrip> +</quote> + + +<sect1>mktime_dt<label id="mktime_dt"><p> + +<quote> +<descrip> +<tag/Function/Converts a ProDOS date to a time_t. +<tag/Header/<tt/<ref id="apple2.h" name="apple2.h">/ +<tag/Declaration/<tt/time_t __fastcall__ mktime_dt (const struct datetime* dt);/ +<tag/Description/The <tt/mktime_dt/ function parses the given +proDOS date/time and returns a time_t timestamp. On error, 0 is returned, +and errno is set. +<tag/Notes/<itemize> +<item>The function is only available as fastcall function, so it may only +be used in presence of a prototype. +<item>This function is only available on Apple II. +</itemize> +<tag/Availability/cc65 +<tag/Example/ +<verb> + +#include <stdio.h> +#include <time.h> +#include <sys/stat.h> +int main(void) +{ + struct stat st; + if (stat ("/disk/file", &st) == 0) { + printf ("File created on %s\n", + localtime (mktime_dt (&st.st_ctime))); + } +} +</verb> +</descrip> +</quote> + + <sect1>clrscr<label id="clrscr"><p> <quote> @@ -3253,6 +3396,70 @@ used in presence of a prototype. <tag/Description/<tt/decompress_lz4/ uncompresses a LZ4-compressed buffer. <tag/Notes/<itemize> <item>Use LZ4_compress_HC with compression level 16 for best compression. +<item>Your program will need to know the uncompressed size of the buffer as +there is no end-of-stream marker. +<item>LZ4 is the biggest and second-slowest decompressor shipped in cc65 runtime. +It is also the least efficient compression algorithm. +</itemize> +<tag/Availability/cc65 +<tag/Example/None. +</descrip> +</quote> + + +<sect1>decompress_lzsa1<label id="decompress_lzsa1"><p> + +<quote> +<descrip> +<tag/Function/Uncompress a LZSA buffer with format 1. +<tag/Header/<tt/<ref id="lzsa.h" name="lzsa.h">/ +<tag/Declaration/<tt/void decompress_lzsa1 (const unsigned char* src, unsigned char* const dst);/ +<tag/Description/<tt/decompress_lz4/ uncompresses a LZSA buffer with format 1. +<tag/Notes/<itemize> +<item>Use <tt/lzsa -f 1 -r input.bin output.lzsa1/ to compress your input. +<item>The project and compressor can be found at <url url="https://github.com/emmanuel-marty/lzsa"> +<item>LZSA1 is the fastest decompressor shipped in cc65 runtime, but data is less +compressed than with LZSA2 and ZX02. +</itemize> +<tag/Availability/cc65 +<tag/Example/None. +</descrip> +</quote> + + +<sect1>decompress_lzsa2<label id="decompress_lzsa2"><p> + +<quote> +<descrip> +<tag/Function/Uncompress a LZSA buffer with format 2. +<tag/Header/<tt/<ref id="lzsa.h" name="lzsa.h">/ +<tag/Declaration/<tt/void decompress_lzsa2 (const unsigned char* src, unsigned char* const dst);/ +<tag/Description/<tt/decompress_lz4/ uncompresses a LZSA buffer with format 2. +<tag/Notes/<itemize> +<item>Use <tt/lzsa -f 2 -r input.bin output.lzsa2/ to compress your input. +<item>The project and compressor can be found at <url url="https://github.com/emmanuel-marty/lzsa"> +<item>LZSA2 is the second fastest decompressor shipped in cc65 runtime, but data is less +compressed than with ZX02. +</itemize> +<tag/Availability/cc65 +<tag/Example/None. +</descrip> +</quote> + + +<sect1>decompress_zx02<label id="decompress_zx02"><p> + +<quote> +<descrip> +<tag/Function/Uncompress a ZX02 buffer. +<tag/Header/<tt/<ref id="zx02.h" name="zx02.h">/ +<tag/Declaration/<tt/void decompress_zx02 (const unsigned char* src, unsigned char* const dst);/ +<tag/Description/<tt/decompress_zx02/ uncompresses a ZX02 buffer with format 2. +<tag/Notes/<itemize> +<item>Use <tt/zx02 input.bin output.zx02/ to compress your input. +<item>The project and compressor can be found at <url url="https://github.com/dmsc/zx02"> +<item>ZX02 is the slowest decompressor shipped with cc65 runtime, but is also the +smallest and has the best compression ratio. </itemize> <tag/Availability/cc65 <tag/Example/None. @@ -3274,7 +3481,7 @@ used in presence of a prototype. <tag/Availability/cc65 (not all platforms) <tag/See also/ <ref id="get_c128_speed" name="get_c128_speed">, -<ref id="set_c128_speed" name="set_c128_speed">, +<ref id="set_c128_speed" name="set_c128_speed"> <tag/Example/None. </descrip> </quote> @@ -3314,7 +3521,7 @@ used in presence of a prototype. <tag/Availability/cc65 (not all platforms) <tag/See also/ <ref id="get_c65_speed" name="get_c65_speed">, -<ref id="set_c65_speed" name="set_c65_speed">, +<ref id="set_c65_speed" name="set_c65_speed"> <tag/Example/None. </descrip> </quote> @@ -3340,6 +3547,26 @@ used in presence of a prototype. </quote> +<sect1>detect_iigs<label id="detect_iigs"><p> + +<quote> +<descrip> +<tag/Function/Check whether we are running on an Apple IIgs.. +<tag/Header/<tt/<ref id="accelerator.h" name="accelerator.h">/ +<tag/Declaration/<tt/unsigned char detect_iigs (void);/ +<tag/Description/The function returns a 1 if running on an Apple IIgs. +<tag/Notes/<itemize> +<item>The function is specific to the Apple2 and Apple2enh platforms. +</itemize> +<tag/Availability/cc65 (not all platforms) +<tag/See also/ +<ref id="get_iigs_speed" name="get_iigs_speed">, +<ref id="set_iigs_speed" name="set_iigs_speed">, +<tag/Example/None. +</descrip> +</quote> + + <sect1>detect_scpu<label id="detect_scpu"><p> <quote> @@ -3380,6 +3607,25 @@ used in presence of a prototype. </quote> +<sect1>dir_entry_count<label id="dir_entry_count"><p> + +<quote> +<descrip> +<tag/Function/Returns the number of entries in the directory. +<tag/Header/<tt/<ref id="apple2.h" name="apple2.h">/ +<tag/Declaration/<tt/unsigned int __fastcall__ dir_entry_count(DIR *dir);/ +<tag/Description/<tt/dir_entry_count/ is machine dependent and does not exist for +all supported targets. If it exists, it returns the number of active +(non-deleted) files and directories in the directory. +<tag/Notes/<itemize> +<item>The function does not exist on all platforms. +</itemize> +<tag/Availability/cc65 (not all platforms) +<tag/Example/None. +</descrip> +</quote> + + <sect1>div<label id="div"><p> <quote> @@ -3440,11 +3686,11 @@ int main(void) <quote> <descrip> <tag/Function/Dump memory to tape. -<tag/Header/<tt/<ref id="sym1.h" name="sym1.h">/ +<tag/Header/<tt/<ref id="sym1.h" name="sym1.h">/, <tt/<ref id="kim1.h" name="kim1.h">/ <tag/Declaration/<tt/int __fastcall__ dumpt (unsigned char id, const void* start, const void* end);/ <tag/Description/<tt/dumpt/ saves memory onto data tape. <tag/Notes/<itemize> -<item>The function is specific to the Sym-1. +<item>The function is specific to the Sym-1 and KIM-1. <item>The return value is status. Non-zero status indicates an error. <item>The function is only available as fastcall function, so it may only be used in presence of a prototype. @@ -3453,7 +3699,7 @@ be used in presence of a prototype. <tag/See also/ <ref id="beep" name="beep">, <ref id="fdisp" name="fdisp">, -<ref id="loadt" name="loadt">, +<ref id="loadt" name="loadt"> <tag/Example/None. </descrip> </quote> @@ -3833,7 +4079,7 @@ switching the CPU into double clock mode. <tag/See also/ <ref id="beep" name="beep">, <ref id="loadt" name="loadt">, -<ref id="dumpt" name="dumpt">, +<ref id="dumpt" name="dumpt"> <tag/Example/None. </descrip> </quote> @@ -3943,13 +4189,39 @@ be used in presence of a prototype. </quote> +<sect1>get_tv<label id="get_tv"><p> + +<quote> +<descrip> +<tag/Function/The function returns the system's vertical blank frequency. +<tag/Header/<tt/<ref id="apple2.h" name="apple2.h">, +<ref id="atari.h" name="atari.h">, <ref id="cbm.h" name="cbm.h">, +<ref id="cx16.h" name="cx16.h">, <ref id="gamate.h" name="gamate.h">, +<ref id="nes.h" name="nes.h">, <ref id="pce.h" name="pce.h">/ +<tag/Declaration/<tt/unsigned char get_tv (void);/ +<tag/Description/<tt/get_tv/ is machine dependent and does not exist for +all supported targets. If it exists, it returns a number that identifies the +frequency at which the screen vertical blank happens (either 50 or 60Hz), +if possible. +<tag/Notes/<itemize> +<item>The function does not exist on all platforms. +<item>Return TV_NTSC for 60Hz systems, TV_PAL for 50Hz systems, or +TV_OTHER if the scan frequency can not be determined. +</itemize> +<tag/Availability/cc65 (not all platforms) +<tag/Example/None. +</descrip> +</quote> + + <sect1>get_ostype<label id="get_ostype"><p> <quote> <descrip> <tag/Function/The function returns the operating system, the program runs on. <tag/Header/<tt/<ref id="apple2.h" name="apple2.h">, -<ref id="atari.h" name="atari.h">, <ref id="c64.h" name="c64.h">/ +<ref id="atari.h" name="atari.h">, <ref id="c64.h" name="c64.h">, +<ref id="cx16.h" name="cx16.h">/ <tag/Declaration/<tt/unsigned char get_ostype (void);/ <tag/Description/<tt/get_ostype/ is machine dependent and does not exist for all supported targets. If it exists, it returns a number that identifies the @@ -4053,6 +4325,27 @@ header files define constants that can be used to check the return code. </quote> +<sect1>get_iigs_speed<label id="get_iigs_speed"><p> + +<quote> +<descrip> +<tag/Function/Get the current speed of the Apple IIgs. +<tag/Header/<tt/<ref id="accelerator.h" name="accelerator.h">/ +<tag/Declaration/<tt/unsigned char get_iigs_speed (void);/ +<tag/Description/The function returns the current speed of the Apple IIgs. +<tag/Notes/<itemize> +<item>The function is specific to the Apple2 and Apple2enh platforms. +<item>See the accelerator.h header for the speed definitions. +</itemize> +<tag/Availability/cc65 (not all platforms) +<tag/See also/ +<ref id="detect_iigs" name="detect_iigs">, +<ref id="set_iigs_speed" name="set_iigs_speed">, +<tag/Example/None. +</descrip> +</quote> + + <sect1>get_scpu_speed<label id="get_scpu_speed"><p> <quote> @@ -4388,6 +4681,45 @@ to undefined behaviour. </descrip> </quote> +<sect1>htonl<label id="htonl"><p> + +<quote> +<descrip> +<tag/Function/Swaps byte order in a 32 bit word. +<tag/Header/<tt/<ref id="inet.h" name="arpa/inet.h">/ +<tag/Declaration/<tt/int htonl(val)/ +<tag/Description/Converts a 32 bit word from from network byte order +(big endian) to little endian (or vice-versa). +<tag/Notes/<itemize> +<item>The function is only available as fastcall function, so it may only +be used in presence of a prototype. +</itemize> +<tag/See also/ +<ref id="ntohl" name="ntohl"> +<tag/Availability/cc65 +</descrip> +</quote> + + +<sect1>htons<label id="htons"><p> + +<quote> +<descrip> +<tag/Function/Swaps byte order in a 16 bit word. +<tag/Header/<tt/<ref id="inet.h" name="arpa/inet.h">/ +<tag/Declaration/<tt/int htons(val)/ +<tag/Description/Converts a 16 bit word from from network byte order +(big endian) to little endian (or vice-versa) by swapping both its bytes. +<tag/Notes/<itemize> +<item>The function is only available as fastcall function, so it may only +be used in presence of a prototype. +</itemize> +<tag/See also/ +<ref id="ntohs" name="ntohs"> +<tag/Availability/cc65 +</descrip> +</quote> + <sect1>isalnum<label id="isalnum"><p> @@ -5084,11 +5416,11 @@ used in presence of a prototype. <quote> <descrip> <tag/Function/Load memory from tape. -<tag/Header/<tt/<ref id="sym1.h" name="sym1.h">/ +<tag/Header/<tt/<ref id="sym1.h" name="sym1.h">/, <tt/<ref id="kim1.h" name="kim1.h">/ <tag/Declaration/<tt/int __fastcall__ loadt (unsigned char id);/ <tag/Description/<tt/loadt/ loads memory from data tape. <tag/Notes/<itemize> -<item>The function is specific to the Sym-1. +<item>The function is specific to the Sym-1 and KIM-1. <item>The return value is status. Non-zero status indicates an error. <item>The function is only available as fastcall function, so it may only be used in presence of a prototype. @@ -5097,7 +5429,7 @@ be used in presence of a prototype. <tag/See also/ <ref id="beep" name="beep">, <ref id="fdisp" name="fdisp">, -<ref id="dumpt" name="dumpt">, +<ref id="dumpt" name="dumpt"> <tag/Example/None. </descrip> </quote> @@ -5757,6 +6089,44 @@ memory allocated for the driver. </descrip> </quote> +<sect1>ntohl<label id="ntohl"><p> + +<quote> +<descrip> +<tag/Function/Swaps byte order in a 32 bit word. +<tag/Header/<tt/<ref id="inet.h" name="arpa/inet.h">/ +<tag/Declaration/<tt/int __fastcall__ ntohl (int val);/ +<tag/Description/Converts a 32 bit word from from host byte order (little endian) +to big endian (or vice-versa). +<tag/Notes/<itemize> +<item>The function is only available as fastcall function, so it may only +be used in presence of a prototype. +</itemize> +<tag/See also/ +<ref id="htonl" name="htonl"> +<tag/Availability/cc65 +</descrip> +</quote> + +<sect1>ntohs<label id="ntohs"><p> + +<quote> +<descrip> +<tag/Function/Swaps byte order in a 16 bit word. +<tag/Header/<tt/<ref id="inet.h" name="arpa/inet.h">/ +<tag/Declaration/<tt/int __fastcall__ ntohs (int val);/ +<tag/Description/Converts a 16 bit word from from host byte order (little endian) +to big endian (or vice-versa) by swapping both its bytes. +<tag/Notes/<itemize> +<item>The function is only available as fastcall function, so it may only +be used in presence of a prototype. +</itemize> +<tag/See also/ +<ref id="htons" name="htons"> +<tag/Availability/cc65 +</descrip> +</quote> + <sect1>offsetof<label id="offsetof"><p> <quote> @@ -5959,6 +6329,32 @@ be used in presence of a prototype. </quote> +<sect1>posix_memalign<label id="posix_memalign"><p> + +<quote> +<descrip> +<tag/Function/Allocate aligned dynamic memory. +<tag/Header/<tt/<ref id="stdlib.h" name="stdlib.h">/ +<tag/Declaration/<tt/int __fastcall__ posix_memalign (void** memptr, size_t alignment, size_t size);/ +<tag/Description/Allocate a block of memory with the given "size", which is aligned to a +memory address that is a multiple of "alignment". "alignment" <em/must not/ be +zero, and <em/must/ be a power of two; otherwise, this function will return +EINVAL. The function returns ENOMEM if not enough memory is available +to satisfy the request. "memptr" must point to a variable; that variable +will return the address of the allocated memory. Use free() to release that +allocated block. +<tag/Notes/<itemize> +<item>The function is only available as fastcall function, so it may only +be used in presence of a prototype. +</itemize> +<tag/Availability/POSIX 1003.1 +<tag/See also/ +<ref id="free" name="free"> +<tag/Example/None. +</descrip> +</quote> + + <sect1>psg_delay<label id="psg_delay"><p> <quote> @@ -6127,6 +6523,9 @@ be used in presence of a prototype. <item>The returned pointer may point to a statically allocated instance of <tt/struct dirent/, so it may get overwritten by subsequent calls to <tt/readdir/. +<item>On the Apple II platform, the d_ctime and d_mtime returned are in the +ProDOS format. You can convert them to more portable time representations using +the ProDOS datetime conversion functions. <item>On several platforms, namely the CBMs and the Atari, the disk drives get confused when opening/closing files between directory reads. So for example a program that reads the list of files on a disk, and after each call to @@ -6606,7 +7005,8 @@ be used in presence of a prototype. <tag/Function/Uninstall the currently loaded driver but do not unload it. <tag/Header/<tt/<ref id="serial.h" name="serial.h">/ <tag/Declaration/<tt/unsigned char ser_uninstall (void);/ -<tag/Description/Uninstall the currently loaded driver but do not unload it. +<tag/Description/Uninstall the currently loaded driver but do not unload it. This +function returns SER_ERR_NO_DRIVER if no driver was installed, 0 otherwise. <tag/Availability/cc65 <tag/See also/Other serial functions. <tag/Example/None. @@ -6790,6 +7190,30 @@ clean-up when exiting the program. </quote> +<sect1>set_iigs_speed<label id="set_iigs_speed"><p> + +<quote> +<descrip> +<tag/Function/Set the current speed of the Apple IIgs. +<tag/Header/<tt/<ref id="accelerator.h" name="accelerator.h">/ +<tag/Declaration/<tt/unsigned char __fastcall__ set_iigs_speed (unsigned char speed);/ +<tag/Description/The function sets the speed of the Apple IIgs CPU (and returns +the new speed). +<tag/Notes/<itemize> +<item>The function is specific to the Apple2 and Apple2enh platforms. +<item>See the accelerator.h header for the speed definitions. +<item>Accepted parameters are SPEED_SLOW and SPEED_FAST (all other values are +considered SPEED_FAST). +</itemize> +<tag/Availability/cc65 (not all platforms) +<tag/See also/ +<ref id="detect_iigs" name="detect_iigs">, +<ref id="get_iigs_speed" name="get_iigs_speed">, +<tag/Example/None. +</descrip> +</quote> + + <sect1>set_scpu_speed<label id="set_scpu_speed"><p> <quote> @@ -6972,6 +7396,85 @@ be used in presence of a prototype. </quote> +<sect1>stat<label id="stat"><p> + +<quote> +<descrip> +<tag/Function/Get file status. +<tag/Header/<tt/<ref id="sys/stat.h" name="sys/stat.h">/ +<tag/Declaration/<tt/int __fastcall__ stat (const char* pathname, struct stat* statbuf);/ +<tag/Description/<tt/stat/ gets information for the file with the given name. On success, +zero is returned. On error, -1 is returned and <tt/errno/ is set to an error +code describing the reason for the failure. +<tag/Notes/<itemize> +<item>The function is only available as fastcall function, so it may only +be used in presence of a prototype. +<item>On the Apple II platform, the st_ctim, st_mtim and st_atim members are left +to zero, for size and performance reasons. The ProDOS creation and modification dates +are returned in the ProDOS format in st_ctime and st_mtime. The access date does +not exist. You can convert them to POSIX-style time representations using +the <url url="apple2.html#ss9.3" name="ProDOS datetime conversion functions">. +</itemize> +<tag/Availability/POSIX 1003.1 +<tag/See also/ +<ref id="statvfs" name="statvfs"> +<tag/Example/ +<verb> +#include <sys/stat.h> + +#define FILENAME "helloworld" +struct stat stbuf; +if (stat (FILENAME, &stbuf) == 0) { + printf ("%s size is %lu bytes (created on %s)\n", FILENAME, stbuf.st_size, +#ifndef __APPLE2__ + localtime (&stbuf.st_ctim.tv_sec) +#else + localtime (mktime_dt (&stbuf.st_ctime)) +#endif + ); +} else { + printf ("There was a problem stat'ing %s: %d\n", FILENAME, errno); +} +</verb> +</descrip> +</quote> + + +<sect1>statvfs<label id="statvfs"><p> + +<quote> +<descrip> +<tag/Function/Get filesystem statistics. +<tag/Header/<tt/<ref id="sys/statvfs.h" name="sys/statvfs.h">/ +<tag/Declaration/<tt/int __fastcall__ statvfs (const char* pathname, struct statvfs* buf);/ +<tag/Description/<tt/statvfs/ gets information for the filesystem on which the given file +resides. On success, +zero is returned. On error, -1 is returned and <tt/errno/ is set to an error +code describing the reason for the failure. +<tag/Notes/<itemize> +<item>The function is only available as fastcall function, so it may only +be used in presence of a prototype. +<item>The function requires an absolute pathname. +</itemize> +<tag/Availability/POSIX 1003.1 +<tag/See also/ +<ref id="stat" name="stat"> +<tag/Example/ +<verb> +#include <sys/statvfs.h> + +#define FILENAME "/disk/helloworld" +struct statvfs stvbuf; +if (statvfs (FILENAME, &stvbuf) == 0) { + printf ("%s filesystem has %u blocks of %u size, %u of them free.\n", FILENAME, stvbuf.f_blocks, stvbuf.f_bsize, stvbuf.f_bfree); +} else { + printf ("There was a problem statvfs'ing %s: %d\n", FILENAME, errno); +} +</verb> +</descrip> +</quote> + + <sect1>strcasecmp<label id="strcasecmp"><p> <quote> @@ -7552,22 +8055,47 @@ be used in presence of a prototype. </quote> -<sect1>strstr<label id="strstr"><p> +<sect1>strcasestr<label id="strcasestr"><p> <quote> <descrip> -<tag/Function/Find a substring. +<tag/Function/Find a substring, case-insensitive. <tag/Header/<tt/<ref id="string.h" name="string.h">/ -<tag/Declaration/<tt/char* __fastcall__ strstr (const char* str, const char* substr);/ -<tag/Description/<tt/strstr/ searches for the first occurrence of the string -<tt/substr/ within <tt/str/. If found, it returns a pointer to the copy, -otherwise it returns <tt/NULL/. +<tag/Declaration/<tt/char* __fastcall__ strcasestr (const char* str, const char* substr);/ +<tag/Description/<tt/strcasestr/ searches for the first occurrence of the string +<tt/substr/ within <tt/str/. If found, it returns a pointer to the start of the +match in <tt/str/, otherwise it returns <tt/NULL/. <tag/Notes/<itemize> <item>The function is only available as fastcall function, so it may only be used in presence of a prototype. </itemize> <tag/Availability/ISO 9899 <tag/See also/ +<ref id="strstr" name="strstr">, +<ref id="strcspn" name="strcspn">, +<ref id="strspn" name="strspn"> +<tag/Example/None. +</descrip> +</quote> + + +<sect1>strstr<label id="strstr"><p> + +<quote> +<descrip> +<tag/Function/Find a substring, case-sensitive. +<tag/Header/<tt/<ref id="string.h" name="string.h">/ +<tag/Declaration/<tt/char* __fastcall__ strstr (const char* str, const char* substr);/ +<tag/Description/<tt/strstr/ searches for the first occurrence of the string +<tt/substr/ within <tt/str/. If found, it returns a pointer to the start of the +match in <tt/str/, otherwise it returns <tt/NULL/. +<tag/Notes/<itemize> +<item>The function is only available as fastcall function, so it may only +be used in presence of a prototype. +</itemize> +<tag/Availability/ISO 9899 +<tag/See also/ +<ref id="strcasestr" name="strcasestr">, <ref id="strcspn" name="strcspn">, <ref id="strspn" name="strspn"> <tag/Example/None. @@ -7984,6 +8512,8 @@ only in the presence of a prototype. <descrip> <tag/Function/Wait until the start of the next video frame. <tag/Header/<tt/ +<ref id="apple2enh.h" name="apple2enh.h">, +<ref id="atmos.h" name="atmos.h">, <ref id="cbm.h" name="cbm.h">, <ref id="gamate.h" name="gamate.h">, <ref id="nes.h" name="nes.h">, @@ -7991,6 +8521,7 @@ only in the presence of a prototype. <tag/Declaration/<tt/void waitvsync (void);/ <tag/Description/Wait for vertical sync, to reduce flickering. <tag/Availability/Platforms served by the headers above +(Atmos requires the VSync hack) <tag/Example/None. </descrip> </quote> diff --git a/doc/grc65.sgml b/doc/grc65.sgml index c03815ade..d256c31e6 100644 --- a/doc/grc65.sgml +++ b/doc/grc65.sgml @@ -270,7 +270,7 @@ required for the correct process of GEOS sequential application building. <p>Large GEOS applications typically don't fit in one piece in their designated memory area. They are therefore split into overlays which are loaded into memory on demand. The individual overlays are stored as records of a VLIR (Variable -Length Index Record) file. When GEOS starts a VLIR overlay appliation it loads +Length Index Record) file. When GEOS starts a VLIR overlay application it loads record number 0 which is supposed to contain the main program. The record numbers starting with 1 are to be used for the actual overlays. diff --git a/doc/index.sgml b/doc/index.sgml index 727364028..92df5e018 100644 --- a/doc/index.sgml +++ b/doc/index.sgml @@ -175,6 +175,9 @@ <tag><htmlurl url="plus4.html" name="plus4.html"></tag> Topics specific to the Commodore Plus/4. + <tag><htmlurl url="rp6502.html" name="rp6502.html"></tag> + Topics specific to the Picocomputer 6502. + <tag><htmlurl url="supervision.html" name="supervision.html"></tag> Topics specific to the Watara Supervision Console. diff --git a/doc/ld65.sgml b/doc/ld65.sgml index 1ad04b395..b889645f5 100644 --- a/doc/ld65.sgml +++ b/doc/ld65.sgml @@ -1180,6 +1180,202 @@ The ZPSAVE segment contains the original values of the zeropage locations used by the ZEROPAGE segment. It is placed in its own segment because it must not be initialized. +<sect>Debug Info<p> + +The debug info and the API mirrors closely the items available in the sources +used to build an executable. To use the API efficiently, it is necessary to +understand from which blocks the information is built. + +<itemize> +<item> Libraries +<item> Lines +<item> Modules +<item> Scopes +<item> Segments +<item> Source files +<item> Spans +<item> Symbols +<item> Types +</itemize> + +Each item of each type has something like a primary index called an 'id'. +The ids can be thought of as array indices, so looking up something by its +id is fast. Invalid ids are marked with the special value CC65_INV_ID. +Data passed back for an item may contain ids of other objects. A scope for +example contains the id of the parent scope (or CC65_INV_ID if there is no +parent scope). Most API functions use ids to lookup related objects. + + +<sect1>Libraries<p> + +This information comes from the linker and is currently used in only one +place:To mark the origin of a module. The information available for a library +is its name including the path. + +<itemize> +<item> Library id +<item> Name and path of library +</itemize> + + +<sect1>Lines<p> + +A line is a location in a source file. It is module dependent, which means +that if two modules use the same source file, each one has its own line +information for this file. While the assembler has also column information, +it is dropped early because it would generate much more data. A line may have +one or more spans attached if code or data is generated. + +<itemize> +<item> Line id +<item> Id of the source file, the line is from +<item> The line number in the file (starting with 1) +<item> The type of the line: Assembler/C source or macro +<item> A count for recursive macros if the line comes from a macro +</itemize> + + +<sect1>Modules<p> + +A module is actually an object file. It is generated from one or more source +files and may come from a library. The assembler generates a main scope for +symbols declared outside user generated scopes. The main scope has an empty name. + +<itemize> +<item> Module id +<item> The name of the module including the path +<item> The id of the main source file (the one specified on the command line) +<item> The id of the library the module comes from, or CC65_INV_ID +<item> The id of the main scope for this module +</itemize> + + +<sect1>Scopes<p> + +Each module has a main scope where all symbols live, that are specified outside +other scopes. Additional nested scopes may be specified in the sources. So scopes +have a one to many relation: Each scope (with the exception of the main scope) has +exactly one parent and may have several child scopes. Scopes may not cross modules. + +<itemize> +<item> Scope id +<item> The name of the scope (may be empty) +<item> The type of the scope: Module, .SCOPE or .PROC, .STRUCT and .ENUM +<item> The size of the scope (the size of the span for the active segment) +<item> The id of the parent scope (CC65_INV_ID in case of the main scope) +<item> The id of the attached symbol for .PROC scopes +<item> The id of the module where the scope comes from +</itemize> + + +<sect1>Segment Info<p> + +<itemize> +<item> Segment id +<item> The name of the segment +<item> The start address of the segment +<item> The size of the segment +<item> The name of the output file, this segment was written to (may be empty) +<item> The offset of the segment in the output file (only if name not empty) +<item> The bank number of the segment's memory area +</itemize> + +It is also possible to retrieve the spans for sections (a section is the part of a +segment that comes from one module). Since the main scope covers a whole module, and +the main scope has spans assigned (if not empty), the spans for the main scope of a +module are also the spans for the sections in the segments. + + +<sect1>Source files<p> + +Modules are generated from source files. Since some source files are used several times +when generating a list of modules (header files for example), the linker will merge +duplicates to reduce redundant information. Source files are considered identical if the +full name including the path is identical, and the size and time of last modification +matches. Please note that there may be still duplicates if files are accessed using +different paths. + +<itemize> +<item> Source file id +<item> The name of the source file including the path +<item> The size of the file at the time when it was read +<item> The time of last modification at the time when the file was read +</itemize> + + +<sect1>Spans<p> + +A span is a small part of a segment. It has a start address and a size. Spans are used +to record sizes of other objects. Line infos and scopes may have spans attached, so it +is possible to lookup which data was generated for these items. + +<itemize> +<item> Span id +<item> The start address of the span. This is an absolute address +<item> The end address of the span. This is inclusive which means if start==end then => size==1 +<item> The id of the segment were the span is located +<item> The type of the data in the span (optional, maybe NULL) +<item> The number of line infos available for this span +<item> The number of scope infos available for this span +</itemize> + +The last two fields will save a call to cc65_line_byspan or cc65_scope_byspan by providing +information about the number of items that can be retrieved by these calls. + + +<sect1>Symbols<p> + +<itemize> +<item> Symbol id +<item> The name of the symbol +<item> The type of the symbol, which may be label, equate or import +<item> The size of the symbol (size of attached code or data). Only for labels. Zero if unknown +<item> The value of the symbol. For an import, this is taken from the corresponding export +<item> The id of the corresponding export. Only valid for imports, CC65_INV_ID for other symbols +<item> The segment id if the symbol is segment based. For an import, taken from the export +<item> The id of the scope this symbols was defined in +<item> The id of the parent symbol. This is only set for cheap locals and CC65_INV_ID otherwise +</itemize> + +Beware: Even for an import, the id of the corresponding export may be CC65_INV_ID. +This happens if the module with the export has no debug information. So make sure +that your application can handle it. + + +<sect1>Types<p> + +A type is somewhat special. You cannot retrieve data about it in a similar way as with the other +items. Instead you have to call a special routine that parses the type data and returns it +in a set of data structures that can be processed by a C or C++ program. + +The type information is language independent and doesn't encode things like 'const' or +'volatile'. Instead it defines a set of simple data types and a few ways to aggregate +them (arrays, structs and unions). + +Type information is currently generated by the assembler for storage allocating commands +like .BYTE or .WORD. For example, the assembler code + +<tscreen><verb> +foo: .byte $01, $02, $03 +</verb></tscreen> + +will assign the symbol foo a size of 3, but will also generate a span with a size of 3 +bytes and a type ARRAY[3] OF BYTE. +Evaluating the type of a span allows a debugger to display the data in the same way as it +was defined in the assembler source. + +<table> +<tabular ca="clc"> +<bf/Assembler Command/| <bf/Generated Type Information/@<hline> +.ADDR| ARRAY OF LITTLE ENDIAN POINTER WITH SIZE 2 TO VOID@ +.BYTE| ARRAY OF UNSIGNED WITH SIZE 1@ +.DBYT| ARRAY OF BIG ENDIAN UNSIGNED WITH SIZE 2@ +.DWORD| ARRAY OF LITTLE ENDIAN UNSIGNED WITH SIZE 4@ +.FARADDR| ARRAY OF LITTLE ENDIAN POINTER WITH SIZE 3 TO VOID@ +.WORD| ARRAY OF LITTLE ENDIAN UNSIGNED WITH SIZE 2 +</tabular> +</table> + <sect>Copyright<p> diff --git a/doc/rp6502.sgml b/doc/rp6502.sgml new file mode 100644 index 000000000..201f0a06b --- /dev/null +++ b/doc/rp6502.sgml @@ -0,0 +1,96 @@ +<!doctype linuxdoc system> + +<article> +<title>Picocomputer 6502 - specific information for cc65 +<author><url url="mailto:uz@cc65.org" name="Ullrich von Bassewitz"> + +<abstract> +An overview over the Picocomputer 6502 and its interfaces to the cc65 C +compiler. +</abstract> + +<!-- Table of contents --> +<toc> + +<!-- Begin the document --> + +<sect>Overview<p> + +The Picocomputer 6502 is a modern W65C02S computer with a custom operating +system designed to be POSIX-like. The reference design includes a W65C02S, +W65C22S, RP6502-RIA, and optionally a RP6502-VGA. Peripheral devices like +keyboards, mice, and flash storage are connected by USB to the RP6502-RIA. +Audio is generated by the RP6502-RIA. Video is generated by the RP6502-VGA. + + + +<sect>Binary format<p> + +The standard binary output format generated by the linker for the RP6502 target +is a plain machine language program without any prefix or postfix. + +The RP6502 Integrated Development Environment, based on Visual Studio Code, +will convert the cc65 binary output into RP6502 ROM files that can be loaded +directly from the RP6502 monitor or installed on the RIA to be loaded at boot. + + + +<sect>Memory layout<p> + +<descrip> + <tag/Stack/ + The C run-time stack is located at $FEFF, and grows downward. + + <tag/Heap/ + The C heap is located at the end of the program, and grows toward the C + run-time stack. + + <tag/RAM/ + RAM is located at $0000 - $FEFF. Default binaries load and + start at $0200. + + <tag/ROM/ + The RP6502 is designed with no ROM in the 6502 address space. + + <tag/VIA/ + A Versatile Interface Adapter (6522) is 16 registers located + at $FFD0. + + <tag/RIA/ + The RP6502 Interface Adapter is 32 registers located at $FFE0. + + <tag/User/ + User I/O expansion is from $FF00 to $FFCF. + +</descrip><p> + + + +<sect>Platform-specific header files<p> + +Programs containing RP6502-specific code may use the <tt/rp6502.h/ or +<tt/rp6502.inc/ include files. + + + +<sect>License<p> + +This software is provided "as-is", without any expressed 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: +<enum> +<item> 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. +<item> Altered source versions must be plainly marked as such, and must not + be misrepresented as being the original software. +<item> This notice may not be removed or altered from any source + distribution. +</enum> + +</article> diff --git a/doc/sim65.sgml b/doc/sim65.sgml index b1e5afbdc..9c3764e1d 100644 --- a/doc/sim65.sgml +++ b/doc/sim65.sgml @@ -40,6 +40,8 @@ The simulator is called as follows: Long options: --help Help (this text) --cycles Print amount of executed CPU cycles + --cpu <type> Override CPU type (6502, 65C02, 6502X) + --trace Enable CPU trace --verbose Increase verbosity --version Print the simulator version number </verb></tscreen> @@ -49,7 +51,7 @@ which is limited to an 8-bit result 0-255. An error in sim65, like bad arguments or an internal problem will exit with <tt/1/. -A timeout from <tt/-x/ will exist with <tt/2/. +A timeout from <tt/-x/ will exit with <tt/2/. <sect1>Command line options in detail<p> @@ -70,6 +72,17 @@ Here is a description of all the command line options: count. + <tag><tt>--cpu <type></tt></tag> + + Specify the CPU type to use while executing the program. This CPU type + is normally determined from the program file header, but it can be useful + to override it. + + <tag><tt>--trace</tt></tag> + + Print a single line of information for each instruction or interrupt that + is executed by the CPU to stdout. + <tag><tt>-v, --verbose</tt></tag> Increase the simulator verbosity. @@ -115,37 +128,78 @@ PVExit ($01) <sect>Creating a Test in C<p> -For a C test compiled and linked with <tt/--target sim6502/ the +For a C test linked with <tt/--target sim6502/ and the <tt/sim6502.lib/ library, command line arguments to <tt/sim65/ will be passed to <tt/main/, and the return value from <tt/main/ will become sim65's exit code. -The <tt/exit/ function may also be used to terminate with an exit code. +The <tt/stdlib.h/ <tt/exit/ function may also be used to terminate with an exit code. -Exit codes are limited to 8 bits. +Exit codes are limited to an unsigned 8 bit value. (E.g. returning -1 will give an exit code of 255.) The standard C library high level file input and output is functional. A sim65 application can be written like a command line application, -providing arguments to <tt/main/ and using the <tt/stdio.h/ interfaces. +providing command line arguments to <tt/main/ and using the <tt/stdio.h/ interfaces +to interact with the console or access files. Internally, file input and output is provided at a lower level by -a set of built-in paravirtualization functions (<ref id="paravirt-internal" name="see below">). +a set of built-in paravirtualization functions (see <ref id="paravirt-internal" name="below">). +Example: + +<tscreen><verb> +#include <stdio.h> +int main() +{ + printf("Hello!\n"); + return 5; +} + +// Build and run: +// cl65 -t sim6502 -o example.prg example.c +// sim65 example.prg + +// Build and run, separate steps: +// cc65 -t sim6502 -o example.s example.c +// ca65 -t sim6502 -o example.o example.s +// ld65 -t sim6502 -o example.prg example.o sim6502.lib +// sim65 example.prg +</verb></tscreen> <sect>Creating a Test in Assembly<p> -Assembly tests may similarly be assembled and linked with -<tt/--target sim6502/ or <tt/--target sim65c02/. -Define and export <tt/_main/ as an entry point, +Though a C test may also link with assembly code, +a pure assembly test can also be created. + +Link with <tt/--target sim6502/ or <tt/--target sim65c02/ and the corresponding library, +define and export <tt/_main/ as an entry point, and the sim65 library provides two ways to return an 8-bit exit code: <itemize> <item>Return from <tt/_main/ with the exit code in <tt/A/. -<item><tt/jmp exit/ with the code in <tt/A/. +<item><tt/jmp exit/ with the code in <tt/A/. (<tt/.import exit/ from the sim65 library.) </itemize> -The binary file has a 12 byte header: +Example: + +<tscreen><verb> +.export _main +_main: + lda #5 + rts + +; Build and run: +; cl65 -t sim6502 -o example.prg example.s +; sim65 example.prg + +; Build and run, separate steps: +; ca65 -t sim6502 -o example.o example.s +; ld65 -t sim6502 -o example.prg example.o sim6502.lib +; sim65 example.prg +</verb></tscreen> + +Internally, the binary program file has a 12 byte header provided by the library: <itemize> @@ -182,8 +236,204 @@ These use cc65 calling conventions, and are intended for use with the sim65 targ <item><tt/IRQ/ and <tt/NMI/ events will not be generated, though <tt/BRK/ can be used if the IRQ vector at <tt/$FFFE/ is manually prepared by the test code. +<item>The <tt/sim6502/ or <tt/sim65c02/ targets provide a default configuration, +but if customization is needed <tt/sim6502.cfg/ or <tt/sim65c02.cfg/ might be used as a template. + </itemize> +<sect>Counter peripheral + +<p>The sim65 simulator supports a memory-mapped counter peripheral that manages +a number of 64-bit counters that are continuously updated as the simulator is +running. For each counter, it also provides a 64 bit "latching" register. + +<p>The functionality of the counter peripheral is accessible through 3 registers: + +<itemize> +<item><tt>PERIPHERALS_COUNTER_LATCH</tt> ($FFC0, write-only) +<item><tt>PERIPHERALS_COUNTER_SELECT</tt> ($FFC1, read/write) +<item><tt>PERIPHERALS_COUNTER_VALUE</tt> ($FFC2..$FFC9, read-only) +</itemize> + +<p>These three registers are used as follows. + +<p>When a program explicitly requests a "counter latch" operation by writing any value +to the <tt>PERIPHERALS_COUNTER_LATCH</tt> address ($FFC0), all live registers are simultaneously +copied to the latch registers. They will keep their newly latched values until another latch +operation is requested. + +<p>The <tt>PERIPHERALS_COUNTER_SELECT</tt> address ($FFC1) register holds an 8-bit value that +specifies which 64-bit latch register is currently readable through the <tt>PERIPHERALS_COUNTER_VALUE</tt> +address range. Six values are currently defined: + +<itemize> +<item>$00: latched clock cycle counter selected. +<item>$01: latched CPU instruction counter selected. +<item>$02: latched IRQ interrupt counter selected. +<item>$03: latched NMI interrupt counter selected. +<item>$80: latched wallclock time (nanoseconds) selected. +<item>$81: latched wallclock time (split: seconds, nanoseconds) selected. +</itemize> + +<p>Values $00 to $03 provide access to the latched (frozen) value of their respective live +counters at the time of the last write to <tt>PERIPHERALS_COUNTER_LATCH</tt>. + +<p>When <tt>PERIPHERALS_COUNTER_SELECT</tt> equals $80, the <tt>PERIPHERALS_COUNTER_VALUE</tt> +will be a 64-bit value corresponding to the number of nanoseconds elapsed since the Unix epoch +(Midnight, Jan 1st, 1970 UTC), at the time of the last latch operation. + +<p>When <tt>PERIPHERALS_COUNTER_SELECT</tt> equals $81, the high 32 bits of <tt>PERIPHERALS_COUNTER_VALUE</tt> +will be a 32-bit value corresponding to the number of seconds elapsed since the Unix epoch (Midnight, Jan 1st, +1970 UTC), at the time of the last latch operation. The low 32 bits of +<tt>PERIPHERALS_COUNTER_VALUE</tt> will hold the nanoseconds since the start of that second. + +<p>The two different wallclock-time latch registers will always refer to precisely the same time instant. +For some applications, the single 64-bit value measured in nanoseconds will be more convenient, while +for other applications, the split 32/32 bits representation with separate second and nanosecond +values will be more convenient. + +<p>Note that the time elapsed since the Unix epoch is an approximation, as the implementation depends on the +way POSIX defines time-since-the-epoch. Unfortunately, POSIX incorrectly assumes that all days are precisely +86400 seconds long, which is not true in case of leap seconds. The way this inconsistency is resolved is +system dependent. + +<p>On reset, <tt>PERIPHERALS_COUNTER_SELECT</tt> is initialized to zero. If the <tt>PERIPHERALS_COUNTER_SELECT</tt> +register holds a value other than one of the six values described above, all <tt>PERIPHERALS_COUNTER_VALUE</tt> +bytes will read as zero. + +<p>The <tt>PERIPHERALS_COUNTER_VALUE</tt> addresses ($FFC2..$FFC9) are used to read to currently +selected 64-bit latch register value. Address $FFC2 holds the least significant byte (LSB), +while address $FFC9 holds the most significant byte (MSB). + +<p>On reset, all latch registers are reset to zero. Reading any of the <tt>PERIPHERALS_COUNTER_VALUE</tt> +bytes before the first write to <tt>PERIPHERALS_COUNTER_LATCH</tt> will yield zero. + +Example: + +<tscreen><verb> +/* This example uses the peripheral support in sim65.h */ + +#include <stdio.h> +#include <sim65.h> + +static void print_current_counters(void) +{ + peripherals.counter.latch = 0; /* latch values */ + + peripherals.counter.select = COUNTER_SELECT_CLOCKCYCLE_COUNTER; + printf("clock cycles ............... : %08lx %08lx\n", peripherals.counter.value32[1], peripherals.counter.value32[0]); + peripherals.counter.select = COUNTER_SELECT_INSTRUCTION_COUNTER; + printf("instructions ............... : %08lx %08lx\n", peripherals.counter.value32[1], peripherals.counter.value32[0]); + peripherals.counter.select = COUNTER_SELECT_WALLCLOCK_TIME; + printf("wallclock time ............. : %08lx %08lx\n", peripherals.counter.value32[1], peripherals.counter.value32[0]); + peripherals.counter.select = COUNTER_SELECT_WALLCLOCK_TIME_SPLIT; + printf("wallclock time, split ...... : %08lx %08lx\n", peripherals.counter.value32[1], peripherals.counter.value32[0]); + printf("\n"); +} + +int main(void) +{ + print_current_counters(); + print_current_counters(); + return 0; +} +</verb></tscreen> + +<sect>SIM65 control peripheral + +<p>The sim65 simulator supports a memory-mapped peripheral that allows control +of the simulator behavior itself. + +<p>The sim65 control peripheral interface consists of 2 registers: + +<itemize> +<item><tt>PERIPHERALS_SIMCONTROL_CPUMODE</tt> ($FFCA, read/write) +<item><tt>PERIPHERALS_SIMCONTROL_TRACEMODE</tt> ($FFCB, read/write) +</itemize> + +<p>Address <tt>PERIPHERALS_SIMCONTROL_CPUMODE</tt> allows access to the currently active CPU mode. + +<p>Possible values are CPU_6502 (0), CPU_65C02 (1), and CPU_6502X (2). For specialized applications, +it may be useful to switch CPU models at runtime; this is supported by writing 0, 1, or 2 to this address. +Writing any other value will be ignored. + +<p>Address <tt>PERIPHERALS_SIMCONTROL_TRACEMODE</tt> allows inspection and control of the currently active +CPU tracing mode. + +<p>A value of 0 means tracing is disabled; a value of $7F fully enables tracing. The 7 +lower bits of the value actually provide control over which fields are printed; see below +for an explanation of the seven fields. + +<p>Having the ability to enable/disable tracing on the fly can be a useful debugging aid. For example, +it can be used to enable tracing for short fragments of code. Consider the following example: + +<tscreen><verb> +/* This example uses the TRACE_ON and TRACE_OFF macros defined in sim65.h */ + +#include <stdio.h> +#include <sim65.h> + +unsigned x; + +int main(void) +{ + TRACE_ON(); + + x = 0x1234; /* We want to see what happens here. */ + + TRACE_OFF(); + + return 0; +} +</verb></tscreen> + +<p>This small test program, when compiled with optimizations enabled (-O), produces the output trace below: + +<tscreen><verb> +70 232 022E A2 12 ldx #$12 A=7F X=00 Y=04 S=FD Flags=nvdizC SP=FFBC +71 234 0230 A9 34 lda #$34 A=7F X=12 Y=04 S=FD Flags=nvdizC SP=FFBC +72 236 0232 8D C8 02 sta $02C8 A=34 X=12 Y=04 S=FD Flags=nvdizC SP=FFBC +73 240 0235 8E C9 02 stx $02C9 A=34 X=12 Y=04 S=FD Flags=nvdizC SP=FFBC +74 244 0238 A9 00 lda #$00 A=34 X=12 Y=04 S=FD Flags=nvdizC SP=FFBC +75 246 023A 8D CB FF sta $FFCB A=00 X=12 Y=04 S=FD Flags=nvdiZC SP=FFBC +</verb></tscreen> + +<p>The example output shows the full trace format, consisting of the following seven fields: + +<itemize> +<item>The first field is an instruction counter. We see here that the assignment '<tt>x = 0x1234;</tt>' +starts at the 70th CPU instruction since the start of the simulator, and takes four 6502 instructions. +The two instructions that follow correspond to the execution of the <tt>TRACE_OFF</tt>' macro +that disables tracing. +<item>The second field shows the clock cycles since the start of the program. Here we see that the +first four instructions take 12 clock cycles in total (262 - 250 = 12). +<item>The third field shows the program counter as a four-digit, i.e., the PC register. Its 16-bit + value is displayed as a 4-digit hecadecimal number. +<item>The fourth field shows one to three hexadecimal byte values that make up the instruction. +<item>The fifth field shows the instruction in human-readable assembly language. +<item>The sixth field shows the CPU registers before execution of the instruction. The A, X, Y, and + S registers are each shown as a single byte value. The six status bits of the CPU are shown in + the order NVDIZC (Negative, Overflow, Decimal, Interrupt, Zero, Carry). They are displayed as + a capital letter if the flag is set, or a small letter if the flag is unset. +<item>The seventh and last field shows the software stack pointer SP as used by CC65 programs that + conform to the CC65 conventions. +</itemize> + +<p>Writing a specific value to <tt>PERIPHERALS_SIMCONTROL_TRACEMODE</tt> will control which of these +seven fields are displayed. The following values are defined to denote the seven fields: + +<itemize> +<item>TRACE_FIELD_INSTR_COUNTER = 0x40 +<item>TRACE_FIELD_CLOCK_COUNTER = 0x20 +<item>TRACE_FIELD_PC = 0x10 +<item>TRACE_FIELD_INSTR_BYTES = 0x08 +<item>TRACE_FIELD_INSTR_ASSEMBLY = 0x04 +<item>TRACE_FIELD_CPU_REGISTERS = 0x02 +<item>TRACE_FIELD_CC65_SP = 0x01 +</itemize> + +<p>For example, writing the value $16 to <tt>PERIPHERALS_SIMCONTROL_TRACEMODE</tt> will only display +the program counter, instruction assembly, and CPU registers fields. <sect>Copyright<p> diff --git a/doc/smc.sgml b/doc/smc.sgml index 71de20208..ed9b1ba20 100644 --- a/doc/smc.sgml +++ b/doc/smc.sgml @@ -104,7 +104,7 @@ placeholder has two advantages: <item> The code is better documented. It is clearly visible that the given value is about to be changed. <item> When examining an (initial) disassembly (e.g. in a debugger), these - placegolders can be better identified: They are fixed and, you may + placeholders can be better identified: They are fixed and, you may notice that below, quite eye catching defined. </enum> diff --git a/doc/tgi.sgml b/doc/tgi.sgml index 29acd8ce6..3b013664f 100644 --- a/doc/tgi.sgml +++ b/doc/tgi.sgml @@ -477,10 +477,10 @@ be used in presence of a prototype. <quote> <descrip> -<tag/Function/Get number of horisontal pixels on the screen. +<tag/Function/Get number of horizontal pixels on the screen. <tag/Header/<tt/<ref id="tgi.h" name="tgi.h">/ <tag/Declaration/<tt/unsigned tgi_getxres (void);/ -<tag/Description/Get number of horisontal pixels on the screen. +<tag/Description/Get number of horizontal pixels on the screen. This is same as tgi_maxx()+1. <tag/Availability/cc65 <tag/See also/Other tgi functions. diff --git a/include/_mikey.h b/include/_mikey.h index b9b7f6906..6e16e98ca 100644 --- a/include/_mikey.h +++ b/include/_mikey.h @@ -27,7 +27,7 @@ #ifndef __MIKEY_H #define __MIKEY_H -/* timer structure */ +/* Timer structure */ typedef struct _mikey_timer { unsigned char reload; unsigned char control; @@ -39,7 +39,7 @@ typedef struct _mikey_all_timers { struct _mikey_timer timer[8]; } _mikey_all_timers; -/* audio channel structure */ +/* Audio channel structure */ typedef struct _mikey_audio { unsigned char volume; unsigned char feedback; @@ -53,54 +53,212 @@ typedef struct _mikey_audio { /* Define a structure with the mikey register offsets */ struct __mikey { - struct _mikey_timer timer0; // 0xFD00 - struct _mikey_timer timer1; // 0xFD04 - struct _mikey_timer timer2; // 0xFD08 - struct _mikey_timer timer3; // 0xFD0C - struct _mikey_timer timer4; // 0xFD10 - struct _mikey_timer timer5; // 0xFD14 - struct _mikey_timer timer6; // 0xFD18 - struct _mikey_timer timer7; // 0xFD1C - struct _mikey_audio channel_a; // 0xFD20 - struct _mikey_audio channel_b; // 0xFD28 - struct _mikey_audio channel_c; // 0xFD30 - struct _mikey_audio channel_d; // 0xFD38 - unsigned char attena; // 0xFD40 ?? not yet allocated? - unsigned char attenb; // 0xFD41 | - unsigned char attenc; // 0xFD42 | - unsigned char attend; // 0xFD43 | - unsigned char panning; // 0xFD44 | - unsigned char unused0[11]; // 0xFD45 - 0xFD4F not used - unsigned char mstereo; // 0xFD50 stereo control bits - unsigned char unused1[47]; // 0xFD51 - 0xFD7F not used - unsigned char intrst; // 0xFD80 interrupt poll 0 - unsigned char intset; // 0xFD81 interrupt poll 1 - unsigned char unused2[2]; // 0xFD82 - 0xFD83 not used - unsigned char magrdy0; // 0xFD84 mag tape channel0 ready bit - unsigned char magrdy1; // 0xFD85 mag tape channel1 ready bit - unsigned char audin; // 0xFD86 audio in - unsigned char sysctl1; // 0xFD87 control bits - unsigned char mikeyrev; // 0xFD88 mikey hardware rev - unsigned char mikeysrev; // 0xFD89 mikey software rev - unsigned char iodir; // 0xFD8A parallel i/o data dir - unsigned char iodat; // 0xFD8B parallel data - unsigned char serctl; // 0xFD8C serial control register - unsigned char serdat; // 0xFD8D serial data - unsigned char unused3[2]; // 0xFD8E - 0xFD8F not used - unsigned char sdoneack; // 0xFD90 suzy done acknowledge - unsigned char cpusleep; // 0xFD91 cpu bus request disable - unsigned char dispctl; // 0xFD92 video bus request enable, viddma - unsigned char pkbkup; // 0xFD93 magic 'P' count - unsigned char *scrbase; // 0xFD94 start address of video display - unsigned char unused4[6]; // 0xFD96 - 0xFD9B not used - unsigned char mtest0; // 0xFD9C - unsigned char mtest1; // 0xFD9D - unsigned char mtest2; // 0xFD9E - unsigned char unused5; // 0xFD9F not used - unsigned char palette[32]; // 0xFDA0 - 0xFDBF palette 32 bytes - // 0xFDC0 - 0xFDFF not used + struct _mikey_timer timer0; /* 0xFD00 */ + struct _mikey_timer timer1; /* 0xFD04 */ + struct _mikey_timer timer2; /* 0xFD08 */ + struct _mikey_timer timer3; /* 0xFD0C */ + struct _mikey_timer timer4; /* 0xFD10 */ + struct _mikey_timer timer5; /* 0xFD14 */ + struct _mikey_timer timer6; /* 0xFD18 */ + struct _mikey_timer timer7; /* 0xFD1C */ + struct _mikey_audio channel_a; /* 0xFD20 */ + struct _mikey_audio channel_b; /* 0xFD28 */ + struct _mikey_audio channel_c; /* 0xFD30 */ + struct _mikey_audio channel_d; /* 0xFD38 */ + unsigned char attena; /* 0xFD40 ?? not yet allocated? */ + unsigned char attenb; /* 0xFD41 | */ + unsigned char attenc; /* 0xFD42 | */ + unsigned char attend; /* 0xFD43 | */ + unsigned char panning; /* 0xFD44 | */ + unsigned char unused0[11]; /* 0xFD45 - 0xFD4F not used */ + unsigned char mstereo; /* 0xFD50 stereo control bits */ + unsigned char unused1[47]; /* 0xFD51 - 0xFD7F not used */ + unsigned char intrst; /* 0xFD80 interrupt poll 0 */ + unsigned char intset; /* 0xFD81 interrupt poll 1 */ + unsigned char unused2[2]; /* 0xFD82 - 0xFD83 not used */ + unsigned char magrdy0; /* 0xFD84 mag tape channel0 ready bit */ + unsigned char magrdy1; /* 0xFD85 mag tape channel1 ready bit */ + unsigned char audin; /* 0xFD86 audio in */ + unsigned char sysctl1; /* 0xFD87 control bits */ + unsigned char mikeyrev; /* 0xFD88 mikey hardware rev */ + unsigned char mikeysrev; /* 0xFD89 mikey software rev */ + unsigned char iodir; /* 0xFD8A parallel i/o data dir */ + unsigned char iodat; /* 0xFD8B parallel data */ + unsigned char serctl; /* 0xFD8C serial control register */ + unsigned char serdat; /* 0xFD8D serial data */ + unsigned char unused3[2]; /* 0xFD8E - 0xFD8F not used */ + unsigned char sdoneack; /* 0xFD90 suzy done acknowledge */ + unsigned char cpusleep; /* 0xFD91 cpu bus request disable */ + unsigned char dispctl; /* 0xFD92 video bus request enable, viddma */ + unsigned char pkbkup; /* 0xFD93 magic 'P' count */ + unsigned char *scrbase; /* 0xFD94 start address of video display */ + unsigned char unused4[6]; /* 0xFD96 - 0xFD9B not used */ + unsigned char mtest0; /* 0xFD9C */ + unsigned char mtest1; /* 0xFD9D */ + unsigned char mtest2; /* 0xFD9E */ + unsigned char unused5; /* 0xFD9F not used */ + unsigned char palette[32]; /* 0xFDA0 - 0xFDBF palette 32 bytes */ + unsigned char unused6[64]; /* 0xFDC0 - 0xFDFF not used */ + unsigned char bootrom[504]; /* 0xFE00 - 0xFFD8 boot rom */ + unsigned char reserved; /* 0xFFD8 reserved for future hardware */ + unsigned char mapctl; /* 0xFFF9 map control register */ + struct { + unsigned char *nmi; /* 0xFFFA NMI vector */ + unsigned char *reset; /* 0xFFFB reset vector */ + unsigned char *irq; /* 0xFFFC IRQ vector */ + } vectors; }; +/* TIM_CONTROLA control bit definitions */ +enum { + ENABLE_INT = 0x80, + RESET_DONE = 0x40, + ENABLE_RELOAD = 0x10, + ENABLE_COUNT = 0x08 +}; + +/* AUD_CONTROL control bit definitions */ +enum { + FEEDBACK_7 = 0x80, + ENABLE_INTEGRATE = 0x20 +}; + +/* Audio and timer clock settings for source period */ +enum { + AUD_LINKING = 0x07, + AUD_64 = 0x06, + AUD_32 = 0x05, + AUD_16 = 0x04, + AUD_8 = 0x03, + AUD_4 = 0x02, + AUD_2 = 0x01, + AUD_1 = 0x00 +}; + +/* TIM_CONTROLB control bit definitions */ +enum { + TIMER_DONE = 0x08, + LAST_CLOCK = 0x04, + BORROW_IN = 0x02, + BORROW_OUT = 0x01 +}; + +/* MPAN and MSTEREO registers bit definitions */ +enum { + LEFT3_SELECT = 0x80, + LEFT2_SELECT = 0x40, + LEFT1_SELECT = 0x20, + LEFT0_SELECT = 0x10, + RIGHT3_SELECT = 0x08, + RIGHT2_SELECT = 0x04, + RIGHT1_SELECT = 0x02, + RIGHT0_SELECT = 0x01, + LEFT_ATTENMASK = 0xF0, + RIGHT_ATTENMASK = 0x0F +}; + +/* Interrupt Reset and Set bit definitions */ +enum { + TIMER7_INT = 0x80, + TIMER6_INT = 0x40, + TIMER5_INT = 0x20, + TIMER4_INT = 0x10, + TIMER3_INT = 0x08, + TIMER2_INT = 0x04, + TIMER1_INT = 0x02, + TIMER0_INT = 0x01, + SERIAL_INT = TIMER4_INT, + VERTICAL_INT = TIMER2_INT, + HORIZONTAL_INT = TIMER0_INT +}; + +/* SYSCTL1 bit definitions */ +enum { + POWERON = 0x02, + CART_ADDR_STROBE = 0x01 +}; + +/* IODIR and IODAT bit definitions */ +enum { + AUDIN_BIT = 0x10, /* different from AUDIN address */ + READ_ENABLE = 0x10, /* same bit for AUDIN_BIT */ + RESTLESS = 0x08, + NOEXP = 0x04, /* if set, redeye is not connected */ + CART_ADDR_DATA = 0x02, + CART_POWER_OFF = 0x02, /* same bit for CART_ADDR_DATA */ + EXTERNAL_POWER = 0x01 +}; + +/* SERCTL bit definitions for write operations */ +enum { + TXINTEN = 0x80, + RXINTEN = 0x40, + PAREN = 0x10, + RESETERR = 0x08, + TXOPEN = 0x04, + TXBRK = 0x02, + PAREVEN = 0x01 +}; + +/* SERCTL bit definitions for read operations */ +enum { + TXRDY = 0x80, + RXRDY = 0x40, + TXEMPTY = 0x20, + PARERR = 0x10, + OVERRUN = 0x08, + FRAMERR = 0x04, + RXBRK = 0x02, + PARBIT = 0x01 +}; + +/* DISPCTL bit definitions */ +enum { + DISP_COLOR = 0x08, /* must be set to 1 */ + DISP_FOURBIT = 0x04, /* must be set to 1 */ + DISP_FLIP = 0x02, + DMA_ENABLE = 0x01 /* must be set to 1 */ +}; + +/* MTEST0 bit definitions */ +enum { + AT_CNT16 = 0x80, + AT_TEST = 0x40, + XCLKEN = 0x20, + UART_TURBO = 0x10, + ROM_SEL = 0x08, + ROM_TEST = 0x04, + M_TEST = 0x02, + CPU_TEST = 0x01 +}; + +/* MTEST1 bit definitions */ +enum { + P_CNT16 = 0x40, + REF_CNT16 = 0x20, + VID_TRIG = 0x10, + REF_TRIG = 0x08, + VID_DMA_DIS = 0x04, + REF_FAST = 0x02, + REF_DIS = 0x01 +}; + +/* MTEST2 bit definitions */ +enum { + V_STROBE = 0x10, + V_ZERO = 0x08, + H_120 = 0x04, + H_ZERO = 0x02, + V_BLANKEF = 0x01 +}; + +/* MAPCTL bit definitions */ +enum { + TURBO_DISABLE = 0x80, + VECTOR_SPACE = 0x08, + ROM_SPACE = 0x04, + MIKEY_SPACE = 0x02, + SUZY_SPACE = 0x01 +}; #endif - diff --git a/include/_suzy.h b/include/_suzy.h index 8ab7f68dd..50845c958 100644 --- a/include/_suzy.h +++ b/include/_suzy.h @@ -24,75 +24,65 @@ /* */ /*****************************************************************************/ - #ifndef __SUZY_H #define __SUZY_H -/* Joypad $FCB0 */ -#define JOYPAD_RIGHT 0x10 -#define JOYPAD_LEFT 0x20 -#define JOYPAD_DOWN 0x40 -#define JOYPAD_UP 0x80 -#define BUTTON_OPTION1 0x08 -#define BUTTON_OPTION2 0x04 -#define BUTTON_INNER 0x02 -#define BUTTON_OUTER 0x01 +/* JOYSTICK bit definitions */ +enum { + JOYPAD_RIGHT = 0x10, + JOYPAD_LEFT = 0x20, + JOYPAD_DOWN = 0x40, + JOYPAD_UP = 0x80, + BUTTON_OPTION1 = 0x08, + BUTTON_OPTION2 = 0x04, + BUTTON_INNER = 0x02, + BUTTON_OUTER = 0x01 +}; -/* Switches $FCB1 */ -#define BUTTON_PAUSE 0x01 +/* SWITCHES bit definitions */ +enum { + CART1_IO_INACTIVE = 0x04, + CART0_IO_INACTIVE = 0x02, + BUTTON_PAUSE = 0x01 +}; +/* SPRCTL0 bit definitions */ +enum { + BPP_4 = 0xC0, + BPP_3 = 0x80, + BPP_2 = 0x40, + BPP_1 = 0x00, + HFLIP = 0x20, + VFLIP = 0x10, + TYPE_SHADOW = 0x07, + TYPE_XOR = 0x06, + TYPE_NONCOLL = 0x05, + TYPE_NORMAL = 0x04, + TYPE_BOUNDARY = 0x03, + TYPE_BSHADOW = 0x02, + TYPE_BACKNONCOLL = 0x01, + TYPE_BACKGROUND = 0x00 +}; -/* Hardware Math */ -#define FACTOR_A *(unsigned int *) 0xFC54 -#define FACTOR_B *(unsigned int *) 0xFC52 -#define PRODUCT0 *(unsigned int *) 0xFC60 -#define PRODUCT1 *(unsigned int *) 0xFC62 -#define PRODUCT *(long *) 0xFC60 +/* SPRCTL1 bit definitions */ +enum { + LITERAL = 0x80, + PACKED = 0x00, + ALGO3 = 0x40, + RENONE = 0x00, + REHV = 0x10, + REHVS = 0x20, + REHVST = 0x30, + REUSEPAL = 0x08, + SKIP = 0x04, + DRAWUP = 0x02, + DRAWLEFT = 0x01 +}; -#define DIVIDEND0 *(unsigned int *) 0xFC60 -#define DIVIDEND1 *(unsigned int *) 0xFC62 -#define DIVIDEND *(long *) 0xFC60 -#define DIVISOR *(unsigned int *) 0xFC56 -#define QUOTIENT0 *(unsigned int *) 0xFC52 -#define QUOTIENT1 *(unsigned int *) 0xFC54 -#define QUOTIENT *(long *) 0xFC52 -#define REMAINDER0 *(unsigned int *) 0xFC6C -#define REMAINDER1 *(unsigned int *) 0xFC6E -#define REMAINDER *(long *) 0xFC6C +/* Sprite control block (SCB) definitions */ - -/* Sprite control block (SCB) defines */ - -/* SPRCTL0 $FC80 */ -#define BPP_4 0xC0 -#define BPP_3 0x80 -#define BPP_2 0x40 -#define BPP_1 0x00 -#define HFLIP 0x20 -#define VFLIP 0x10 -#define TYPE_SHADOW 0x07 -#define TYPE_XOR 0x06 -#define TYPE_NONCOLL 0x05 -#define TYPE_NORMAL 0x04 -#define TYPE_BOUNDARY 0x03 -#define TYPE_BSHADOW 0x02 -#define TYPE_BACKNONCOLL 0x01 -#define TYPE_BACKGROUND 0x00 - -/* SPRCTL1 $FC81 */ -#define LITERAL 0x80 -#define PACKED 0x00 -#define ALGO3 0x40 -#define RENONE 0x00 -#define REHV 0x10 -#define REHVS 0x20 -#define REHVST 0x30 -#define REUSEPAL 0x08 -#define SKIP 0x04 -#define DRAWUP 0x02 -#define DRAWLEFT 0x01 - -typedef struct SCB_REHVST_PAL { // SCB with all attributes +/* SCB with all attributes */ +typedef struct SCB_REHVST_PAL { unsigned char sprctl0; unsigned char sprctl1; unsigned char sprcoll; @@ -107,7 +97,8 @@ typedef struct SCB_REHVST_PAL { // SCB with all attributes unsigned char penpal[8]; } SCB_REHVST_PAL; -typedef struct SCB_REHVST { // SCB without pallette +/* SCB without pallette */ +typedef struct SCB_REHVST { unsigned char sprctl0; unsigned char sprctl1; unsigned char sprcoll; @@ -121,7 +112,8 @@ typedef struct SCB_REHVST { // SCB without pallette unsigned int tilt; } SCB_REHVST; -typedef struct SCB_REHV { // SCB without stretch/tilt +/* SCB without stretch/tilt */ +typedef struct SCB_REHV { unsigned char sprctl0; unsigned char sprctl1; unsigned char sprcoll; @@ -133,7 +125,8 @@ typedef struct SCB_REHV { // SCB without stretch/tilt unsigned int vsize; } SCB_REHV; -typedef struct SCB_REHV_PAL { // SCB without str/tilt, w/ penpal +/* SCB without stretch/tilt, with penpal */ +typedef struct SCB_REHV_PAL { unsigned char sprctl0; unsigned char sprctl1; unsigned char sprcoll; @@ -146,7 +139,8 @@ typedef struct SCB_REHV_PAL { // SCB without str/tilt, w/ penpal unsigned char penpal[8]; } SCB_REHV_PAL; -typedef struct SCB_REHVS { // SCB w/o tilt & penpal +/* SCB without tilt/penpal */ +typedef struct SCB_REHVS { unsigned char sprctl0; unsigned char sprctl1; unsigned char sprcoll; @@ -159,7 +153,8 @@ typedef struct SCB_REHVS { // SCB w/o tilt & penpal unsigned int stretch; } SCB_REHVS; -typedef struct SCB_REHVS_PAL { // SCB w/o tilt w/penpal +/* SCB without tilt, with penpal */ +typedef struct SCB_REHVS_PAL { unsigned char sprctl0; unsigned char sprctl1; unsigned char sprcoll; @@ -173,7 +168,8 @@ typedef struct SCB_REHVS_PAL { // SCB w/o tilt w/penpal unsigned char penpal[8]; } SCB_REHVS_PAL; -typedef struct SCB_RENONE { // SCB w/o size/stretch/tilt/pal +/* SCB without size/stretch/tilt/penpal */ +typedef struct SCB_RENONE { unsigned char sprctl0; unsigned char sprctl1; unsigned char sprcoll; @@ -183,7 +179,8 @@ typedef struct SCB_RENONE { // SCB w/o size/stretch/tilt/pal signed int vpos; } SCB_RENONE; -typedef struct SCB_RENONE_PAL { // SCB w/o size/str/tilt w/penpal +/* SCB without size/str/tilt, with penpal */ +typedef struct SCB_RENONE_PAL { unsigned char sprctl0; unsigned char sprctl1; unsigned char sprcoll; @@ -210,30 +207,124 @@ typedef struct PENPAL_1 { unsigned char penpal[1]; } PENPAL_1; -/* Misc system defines */ +/* SPRGO bit definitions */ +enum { + SPRITE_GO = 0x01, /* sprite process start bit */ + EVER_ON = 0x04 /* everon detector enable */ +}; -/* SPRGO $FC91 */ -#define EVER_ON 0x04 -#define SPRITE_GO 0x01 +/* SPRSYS bit definitions for write operations */ +enum { + SIGNMATH = 0x80, /* signed math */ + ACCUMULATE = 0x40, /* accumulate multiplication results */ + NO_COLLIDE = 0x20, /* do not collide with any sprites (also SPRCOLL bit definition) */ + VSTRETCH = 0x10, /* stretch v */ + LEFTHAND = 0x08, + CLR_UNSAFE = 0x04, /* unsafe access reset */ + SPRITESTOP = 0x02 /* request to stop sprite process */ +}; -/* SPRSYS (write) $FC92 */ -#define SIGNMATH 0x80 -#define ACCUMULATE 0x40 -#define NO_COLLIDE 0x20 -#define VSTRETCH 0x10 -#define LEFTHAND 0x08 -#define CLR_UNSAFE 0x04 -#define SPRITESTOP 0x02 +/* SPRSYS bit definitions for read operations */ +enum { + MATHWORKING = 0x80, /* math operation in progress */ + MATHWARNING = 0x40, /* accumulator overflow on multiple or divide by zero */ + MATHCARRY = 0x20, /* last carry bit */ + VSTRETCHING = 0x10, + LEFTHANDED = 0x08, + UNSAFE_ACCESS = 0x04, /* unsafe access performed */ + SPRITETOSTOP = 0x02, /* requested to stop */ + SPRITEWORKING = 0x01 /* sprite process is active */ +}; -/* SPRSYS (read) $FC92 */ -#define MATHWORKING 0x80 -#define MATHWARNING 0x40 -#define MATHCARRY 0x20 -#define VSTRETCHING 0x10 -#define LEFTHANDED 0x08 -#define UNSAFE_ACCESS 0x04 -#define SPRITETOSTOP 0x02 -#define SPRITEWORKING 0x01 +/* Suzy hardware registers */ +struct __suzy { + unsigned char *tmpadr; /* 0xFC00 Temporary address */ + unsigned int tiltacc; /* 0xFC02 Tilt accumulator */ + unsigned int hoff; /* 0xFC04 Offset to H edge of screen */ + unsigned int voff; /* 0xFC06 Offset to V edge of screen */ + unsigned char *sprbase; /* 0xFC08 Base address of sprite */ + unsigned char *colbase; /* 0xFC0A Base address of collision buffer */ + unsigned char *vidadr; /* 0xFC0C Current vid buffer address */ + unsigned char *coladr; /* 0xFC0E Current col buffer address */ + unsigned char *scbnext; /* 0xFC10 Address of next SCB */ + unsigned char *sprdline; /* 0xFC12 start of sprite data line address */ + unsigned int hposstrt; /* 0xFC14 start hpos */ + unsigned int vposstrt; /* 0xFC16 start vpos */ + unsigned int sprhsize; /* 0xFC18 sprite h size */ + unsigned int sprvsize; /* 0xFC1A sprite v size */ + unsigned int stretchl; /* 0xFC1C H size adder */ + unsigned int tilt; /* 0xFC1E H pos adder */ + unsigned int sprdoff; /* 0xFC20 offset to next sprite data line */ + unsigned int sprvpos; /* 0xFC22 current vpos */ + unsigned int colloff; /* 0xFC24 offset to collision depository */ + unsigned int vsizeacc; /* 0xFC26 vertical size accumulator */ + unsigned int hsizeoff; /* 0xFC28 horizontal size offset */ + unsigned int vsizeoff; /* 0xFC2A vertical size offset */ + unsigned char *scbaddr; /* 0xFC2C address of current SCB */ + unsigned char *procaddr; /* 0xFC2E address of current spr data proc */ + unsigned char unused0[32]; /* 0xFC30 - 0xFC4F reserved/unused */ + unsigned char unused1[2]; /* 0xFC50 - 0xFC51 do not use */ + unsigned char mathd; /* 0xFC52 */ + unsigned char mathc; /* 0xFC53 */ + unsigned char mathb; /* 0xFC54 */ + unsigned char matha; /* 0xFC55 write starts a multiply operation */ + unsigned char mathp; /* 0xFC56 */ + unsigned char mathn; /* 0xFC57 */ + unsigned char unused2[8]; /* 0xFC58 - 0xFC5F do not use */ + unsigned char mathh; /* 0xFC60 */ + unsigned char mathg; /* 0xFC61 */ + unsigned char mathf; /* 0xFC62 */ + unsigned char mathe; /* 0xFC63 write starts a divide operation */ + unsigned char unused3[8]; /* 0xFC64 - 0xFC6B do not use */ + unsigned char mathm; /* 0xFC6C */ + unsigned char mathl; /* 0xFC6D */ + unsigned char mathk; /* 0xFC6E */ + unsigned char mathj; /* 0xFC6F */ + unsigned char unused4[16]; /* 0xFC70 - 0xFC7F do not use */ + unsigned char sprctl0; /* 0xFC80 sprite control bits 0 */ + unsigned char sprctl1; /* 0xFC81 sprite control bits 1 */ + unsigned char sprcoll; /* 0xFC82 sprite collision number */ + unsigned char sprinit; /* 0xFC83 sprite initialization bits */ + unsigned char unused5[4]; /* 0xFC84 - 0xFC87 unused */ + unsigned char suzyhrev; /* 0xFC88 suzy hardware rev */ + unsigned char suzysrev; /* 0xFC89 suzy software rev */ + unsigned char unused6[6]; /* 0xFC8A - 0xFC8F unused */ + unsigned char suzybusen; /* 0xFC90 suzy bus enable */ + unsigned char sprgo; /* 0xFC91 sprite process start bit */ + unsigned char sprsys; /* 0xFC92 sprite system control bits */ + unsigned char unused7[29]; /* 0xFC93 - 0xFCAF unused */ + unsigned char joystick; /* 0xFCB0 joystick and buttons */ + unsigned char switches; /* 0xFCB1 other switches */ + unsigned char cart0; /* 0xFCB2 cart0 r/w */ + unsigned char cart1; /* 0xFCB3 cart1 r/w */ + unsigned char unused8[8]; /* 0xFCB4 - 0xFCBF unused */ + unsigned char leds; /* 0xFCC0 leds */ + unsigned char unused9; /* 0xFCC1 unused */ + unsigned char parstat; /* 0xFCC2 parallel port status */ + unsigned char pardata; /* 0xFCC3 parallel port data */ + unsigned char howie; /* 0xFCC4 howie (?) */ + /* 0xFCC5 - 0xFCFF unused */ +}; + +/* Hardware math registers */ +#define FACTOR_A *(unsigned int *) 0xFC54 +#define FACTOR_B *(unsigned int *) 0xFC52 +#define PRODUCT0 *(unsigned int *) 0xFC60 +#define PRODUCT1 *(unsigned int *) 0xFC62 +#define PRODUCT *(long *) 0xFC60 + +#define DIVIDEND0 *(unsigned int *) 0xFC60 +#define DIVIDEND1 *(unsigned int *) 0xFC62 +#define DIVIDEND *(long *) 0xFC60 +#define DIVISOR *(unsigned int *) 0xFC56 +#define QUOTIENT0 *(unsigned int *) 0xFC52 +#define QUOTIENT1 *(unsigned int *) 0xFC54 +#define QUOTIENT *(long *) 0xFC52 +#define REMAINDER0 *(unsigned int *) 0xFC6C +#define REMAINDER1 *(unsigned int *) 0xFC6E +#define REMAINDER *(long *) 0xFC6C + +/* Deprecated definitions */ /* MAPCTL $FFF9 */ #define HIGHSPEED 0x80 @@ -242,77 +333,4 @@ typedef struct PENPAL_1 { #define MIKEYSPACE 0x02 #define SUZYSPACE 0x01 - -/* Suzy Hardware Registers */ -struct __suzy { - unsigned int tmpadr; // 0xFC00 Temporary address - unsigned int tiltacc; // 0xFC02 Tilt accumulator - unsigned int hoff; // 0xFC04 Offset to H edge of screen - unsigned int voff; // 0xFC06 Offset to V edge of screen - unsigned char *sprbase; // 0xFC08 Base address of sprite - unsigned char *colbase; // 0xFC0A Base address of collision buffer - unsigned char *vidadr; // 0xFC0C Current vid buffer address - unsigned char *coladr; // 0xFC0E Current col buffer address - unsigned char *scbnext; // 0xFC10 Address of next SCB - unsigned char *sprdline; // 0xFC12 start of sprite data line address - unsigned char *hposstrt; // 0xFC14 start hpos - unsigned char *vposstrt; // 0xFC16 start vpos - unsigned char *sprhsize; // 0xFC18 sprite h size - unsigned char *sprvsize; // 0xFC1A sprite v size - unsigned int stretchl; // 0xFC1C H size adder - unsigned int tilt; // 0xFC1E H pos adder - unsigned int sprdoff; // 0xFC20 offset to next sprite data line - unsigned int sprvpos; // 0xFC22 current vpos - unsigned int colloff; // 0xFC24 offset to collision depository - unsigned int vsizeacc; // 0xFC26 vertical size accumulator - unsigned int hsizeoff; // 0xFC28 horizontal size offset - unsigned int vsizeoff; // 0xFC2A vertical size offset - unsigned char *scbaddr; // 0xFC2C address of current SCB - unsigned char *procaddr; // 0xFC2E address of current spr data proc - unsigned char unused0[32]; // 0xFC30 - 0xFC4F reserved/unused - unsigned char unused1[2]; // 0xFC50 - 0xFC51 do not use - unsigned char mathd; // 0xFC52 - unsigned char mathc; // 0xFC53 - unsigned char mathb; // 0xFC54 - unsigned char matha; // 0xFC55 - unsigned char mathp; // 0xFC56 - unsigned char mathn; // 0xFC57 - unsigned char unused2[8]; // 0xFC58 - 0xFC5F do not use - unsigned char mathh; // 0xFC60 - unsigned char mathg; // 0xFC61 - unsigned char mathf; // 0xFC62 - unsigned char mathe; // 0xFC63 - unsigned char unused3[8]; // 0xFC64 - 0xFC6B do not use - unsigned char mathm; // 0xFC6C - unsigned char mathl; // 0xFC6D - unsigned char mathk; // 0xFC6E - unsigned char mathj; // 0xFC6F - unsigned char unused4[16]; // 0xFC70 - 0xFC7F do not use - unsigned char sprctl0; // 0xFC80 sprite control bits 0 - unsigned char sprctl1; // 0xFC81 sprite control bits 1 - unsigned char sprcoll; // 0xFC82 sprite collision number - unsigned char sprinit; // 0xFC83 sprite initialization bits - unsigned char unused5[4]; // 0xFC84 - 0xFC87 unused - unsigned char suzyhrev; // 0xFC88 suzy hardware rev - unsigned char suzysrev; // 0xFC89 suzy software rev - unsigned char unused6[6]; // 0xFC8A - 0xFC8F unused - unsigned char suzybusen; // 0xFC90 suzy bus enable - unsigned char sprgo; // 0xFC91 sprite process start bit - unsigned char sprsys; // 0xFC92 sprite system control bits - unsigned char unused7[29]; // 0xFC93 - 0xFCAF unused - unsigned char joystick; // 0xFCB0 joystick and buttons - unsigned char switches; // 0xFCB1 other switches - unsigned char cart0; // 0xFCB2 cart0 r/w - unsigned char cart1; // 0xFCB3 cart1 r/w - unsigned char unused8[8]; // 0xFCB4 - 0xFCBF unused - unsigned char leds; // 0xFCC0 leds - unsigned char unused9; // 0xFCC1 unused - unsigned char parstat; // 0xFCC2 parallel port status - unsigned char pardata; // 0xFCC3 parallel port data - unsigned char howie; // 0xFCC4 howie (?) - // 0xFCC5 - 0xFCFF unused -}; - - #endif - diff --git a/include/accelerator.h b/include/accelerator.h index b5d8d0194..0137a7fed 100644 --- a/include/accelerator.h +++ b/include/accelerator.h @@ -304,6 +304,36 @@ unsigned char detect_turbomaster (void); * 0x01 : C64 Turbo Master cartridge present */ +unsigned char __fastcall__ set_iigs_speed (unsigned char speed); + +/* Set the speed of the Apple IIgs CPU. + * + * Possible values: + * SPEED_SLOW : 1 Mhz mode + * SPEED_FAST : Fast mode (2.8MHz or more, depending on the presence of + * an accelerator) + * + * Any other value will be interpreted as SPEED_FAST. + */ + +unsigned char get_iigs_speed (void); + +/* Get the speed of the Apple IIgs CPU. + * + * Possible return values: + * SPEED_SLOW : 1 Mhz mode + * SPEED_FAST : Fast mode (2.8MHz or more, depending on the presence of + * an accelerator) + */ + +unsigned char detect_iigs (void); + +/* Check whether we are running on an Apple IIgs. + * + * Possible return values: + * 0x00 : No + * 0x01 : Yes + */ + /* End of accelerator.h */ #endif - diff --git a/include/apple2.h b/include/apple2.h index 9f644bc97..9f7526f59 100644 --- a/include/apple2.h +++ b/include/apple2.h @@ -41,6 +41,7 @@ # error This module may only be used when compiling for the Apple ][! #endif +#include <time.h> #include <apple2_filetype.h> @@ -121,6 +122,11 @@ #define APPLE_IIGS1 0x81 /* Apple IIgs (ROM 1) */ #define APPLE_IIGS3 0x83 /* Apple IIgs (ROM 3) */ +/* Return codes for get_tv() */ +#define TV_NTSC 0 +#define TV_PAL 1 +#define TV_OTHER 2 + extern unsigned char _dos_type; /* Valid _dos_type values: ** @@ -142,6 +148,27 @@ extern unsigned char _dos_type; ** ProDOS 8 2.4.x - 0x24 */ +/* struct stat.st_mode values */ +#define S_IFDIR 0x01 +#define S_IFREG 0x02 +#define S_IFBLK 0xFF +#define S_IFCHR 0xFF +#define S_IFIFO 0xFF +#define S_IFLNK 0xFF +#define S_IFSOCK 0xFF + +struct datetime { + struct { + unsigned day :5; + unsigned mon :4; + unsigned year :7; + } date; + struct { + unsigned char min; + unsigned char hour; + } time; +}; + /*****************************************************************************/ @@ -151,20 +178,10 @@ extern unsigned char _dos_type; /* The file stream implementation and the POSIX I/O functions will use the -** following struct to set the date and time stamp on files. This specificially +** following struct to set the date and time stamp on files. This specifically ** applies to the open and fopen functions. */ -extern struct { - struct { - unsigned day :5; - unsigned mon :4; - unsigned year :7; - } createdate; /* Current date: 0 */ - struct { - unsigned char min; - unsigned char hour; - } createtime; /* Current time: 0 */ -} _datetime; +extern struct datetime _datetime; /* The addresses of the static drivers */ #if !defined(__APPLE2ENH__) @@ -172,6 +189,7 @@ extern void a2_auxmem_emd[]; extern void a2_stdjoy_joy[]; /* Referred to by joy_static_stddrv[] */ extern void a2_stdmou_mou[]; /* Referred to by mouse_static_stddrv[] */ extern void a2_ssc_ser[]; /* Referred to by ser_static_stddrv[] */ +extern void a2_gs_ser[]; /* IIgs serial driver */ extern void a2_hi_tgi[]; /* Referred to by tgi_static_stddrv[] */ extern void a2_lo_tgi[]; #endif @@ -184,6 +202,12 @@ extern void a2_lo_tgi[]; +void beep (void); +/* Beep beep. */ + +unsigned char get_tv (void); +/* Get the machine vblank frequency. Returns one of the TV_xxx codes. */ + unsigned char get_ostype (void); /* Get the machine type. Returns one of the APPLE_xxx codes. */ @@ -210,6 +234,27 @@ void rebootafterexit (void); #define _cpeekcolor() COLOR_WHITE #define _cpeekrevers() 0 +struct tm* __fastcall__ gmtime_dt (const struct datetime* dt); +/* Converts a ProDOS date/time structure to a struct tm */ + +time_t __fastcall__ mktime_dt (const struct datetime* dt); +/* Converts a ProDOS date/time structure to a time_t UNIX timestamp */ + +typedef struct DIR DIR; + +unsigned int __fastcall__ dir_entry_count(DIR *dir); +/* Returns the number of active files in a ProDOS directory */ + +#if !defined(__APPLE2ENH__) +unsigned char __fastcall__ allow_lowercase (unsigned char onoff); +/* If onoff is 0, lowercase characters printed to the screen via STDIO and +** CONIO are forced to uppercase. If onoff is 1, lowercase characters are +** printed to the screen untouched. By default lowercase characters are +** forced to uppercase because a stock Apple ][+ doesn't support lowercase +** display. The function returns the old lowercase setting. +*/ +#endif + /* End of apple2.h */ diff --git a/include/apple2enh.h b/include/apple2enh.h index bfe5cdb18..864d24986 100644 --- a/include/apple2enh.h +++ b/include/apple2enh.h @@ -82,8 +82,8 @@ #define CH_F10 0xB0 /* Video modes */ -#define VIDEOMODE_40x24 0x0011 -#define VIDEOMODE_80x24 0x0012 +#define VIDEOMODE_40x24 0x15 +#define VIDEOMODE_80x24 0x00 #define VIDEOMODE_40COL VIDEOMODE_40x24 #define VIDEOMODE_80COL VIDEOMODE_80x24 @@ -100,6 +100,7 @@ extern void a2e_auxmem_emd[]; extern void a2e_stdjoy_joy[]; /* Referred to by joy_static_stddrv[] */ extern void a2e_stdmou_mou[]; /* Referred to by mouse_static_stddrv[] */ extern void a2e_ssc_ser[]; /* Referred to by ser_static_stddrv[] */ +extern void a2e_gs_ser[]; /* IIgs serial driver */ extern void a2e_hi_tgi[]; /* Referred to by tgi_static_stddrv[] */ extern void a2e_lo_tgi[]; diff --git a/libsrc/apple2/closedir.c b/include/arpa/inet.h similarity index 76% rename from libsrc/apple2/closedir.c rename to include/arpa/inet.h index d37d15bba..cd353a2bb 100644 --- a/libsrc/apple2/closedir.c +++ b/include/arpa/inet.h @@ -1,12 +1,12 @@ /*****************************************************************************/ /* */ -/* closedir.c */ +/* arpa/inet.h */ /* */ -/* Close a directory */ +/* Endianness utilities for cc65 */ /* */ /* */ /* */ -/* (C) 2005 Oliver Schmidt, <ol.sc@web.de> */ +/* (C) 2023 Colin Leroy-Mira, <colin@colino.net> */ /* */ /* */ /* This software is provided 'as-is', without any expressed or implied */ @@ -30,10 +30,8 @@ -#include <stdlib.h> -#include <fcntl.h> -#include <dirent.h> -#include "dir.h" +#ifndef _ARPA_INET_H +#define _ARPA_INET_H @@ -43,15 +41,27 @@ -int __fastcall__ closedir (DIR* dir) -{ - int result; +#if (__OPT_i__ < 200) +int __fastcall__ ntohs (int val); +int __fastcall__ htons (int val); +#else - /* Cleanup directory file */ - result = close (dir->fd); +#define ntohs(x) \ + ( \ + __AX__=(x), \ + asm("sta tmp1"), \ + asm("txa"), \ + asm("ldx tmp1"), \ + __AX__ \ + ) +#define htons(x) ntohs(x) - /* Cleanup DIR */ - free (dir); +#endif - return result; -} +long __fastcall__ ntohl (long val); +long __fastcall__ htonl (long val); + + + +/* End of arpa/inet.h */ +#endif diff --git a/include/atari.h b/include/atari.h index 04cacab33..0af109264 100644 --- a/include/atari.h +++ b/include/atari.h @@ -220,17 +220,17 @@ /* Color register functions */ /*****************************************************************************/ -extern void __fastcall__ _setcolor (unsigned char color_reg, unsigned char hue, unsigned char luminance); -extern void __fastcall__ _setcolor_low (unsigned char color_reg, unsigned char color_value); -extern unsigned char __fastcall__ _getcolor (unsigned char color_reg); +void __fastcall__ _setcolor (unsigned char color_reg, unsigned char hue, unsigned char luminance); +void __fastcall__ _setcolor_low (unsigned char color_reg, unsigned char color_value); +unsigned char __fastcall__ _getcolor (unsigned char color_reg); /*****************************************************************************/ /* Other screen functions */ /*****************************************************************************/ -extern void waitvsync (void); /* wait for start of next frame */ -extern int __fastcall__ _graphics (unsigned char mode); /* mode value same as in BASIC */ -extern void __fastcall__ _scroll (signed char numlines); +void waitvsync (void); /* wait for start of next frame */ +int __fastcall__ _graphics (unsigned char mode); /* mode value same as in BASIC */ +void __fastcall__ _scroll (signed char numlines); /* numlines > 0 scrolls up */ /* numlines < 0 scrolls down */ @@ -239,18 +239,18 @@ extern void __fastcall__ _scroll (signed char numlines); /* Sound function */ /*****************************************************************************/ -extern void __fastcall__ _sound (unsigned char voice, unsigned char frequency, unsigned char distortion, unsigned char volume); +void __fastcall__ _sound (unsigned char voice, unsigned char frequency, unsigned char distortion, unsigned char volume); /*****************************************************************************/ /* Misc. functions */ /*****************************************************************************/ -extern unsigned char get_ostype(void); /* get ROM version */ -extern unsigned char get_tv(void); /* get TV system */ -extern void _save_vecs(void); /* save system vectors */ -extern void _rest_vecs(void); /* restore system vectors */ -extern char *_getdefdev(void); /* get default floppy device */ -extern unsigned char _is_cmdline_dos(void); /* does DOS support command lines */ +unsigned char get_ostype(void); /* get ROM version */ +unsigned char get_tv(void); /* get TV system */ +void _save_vecs(void); /* save system vectors */ +void _rest_vecs(void); /* restore system vectors */ +char *_getdefdev(void); /* get default floppy device */ +unsigned char _is_cmdline_dos(void); /* does DOS support command lines */ /*****************************************************************************/ diff --git a/include/atari5200.h b/include/atari5200.h index ff176c15b..9a0399d0e 100644 --- a/include/atari5200.h +++ b/include/atari5200.h @@ -94,7 +94,7 @@ extern void atr5200std_joy[]; /* referred to by joy_static_stddrv[] */ #define _bordercolor(color) 0 /* wait for start of next frame */ -extern void waitvsync (void); +void waitvsync (void); /* end of atari5200.h */ #endif diff --git a/include/atari7800.h b/include/atari7800.h index 3cbeedb8b..b289bb41e 100644 --- a/include/atari7800.h +++ b/include/atari7800.h @@ -52,7 +52,7 @@ /* No support for dynamically loadable drivers */ #define DYN_DRV 0 -extern unsigned char get_tv(void); /* get TV system */ +unsigned char get_tv(void); /* get TV system */ #include <_tia.h> #define TIA (*(struct __tia*)0x0000) diff --git a/include/atmos.h b/include/atmos.h index 38d423c46..460a0010f 100644 --- a/include/atmos.h +++ b/include/atmos.h @@ -169,6 +169,9 @@ void atmos_tock (void); void atmos_zap (void); /* Raygun sound effect */ +void waitvsync (void); +/* Wait for start of next frame */ + /* End of atmos.h */ diff --git a/include/cbm264.h b/include/cbm264.h index 4951df518..ab634b721 100644 --- a/include/cbm264.h +++ b/include/cbm264.h @@ -97,7 +97,7 @@ #define COLOR_WHITE (BCOLOR_WHITE | CATTR_LUMA7) #define COLOR_RED (BCOLOR_RED | CATTR_LUMA4) #define COLOR_CYAN (BCOLOR_CYAN | CATTR_LUMA7) -#define COLOR_PURPLE (BCOLOR_VIOLET | CATTR_LUMA7) +#define COLOR_PURPLE (BCOLOR_LIGHTVIOLET | CATTR_LUMA7) #define COLOR_GREEN (BCOLOR_GREEN | CATTR_LUMA7) #define COLOR_BLUE (BCOLOR_BLUE | CATTR_LUMA7) #define COLOR_YELLOW (BCOLOR_YELLOW | CATTR_LUMA7) diff --git a/include/cx16.h b/include/cx16.h index 66f21843e..6a3705631 100644 --- a/include/cx16.h +++ b/include/cx16.h @@ -3,7 +3,7 @@ /* cx16.h */ /* */ /* CX16 system-specific definitions */ -/* For prerelease 39 */ +/* For prerelease 43 */ /* */ /* */ /* This software is provided "as-is", without any expressed or implied */ @@ -176,6 +176,11 @@ enum { #define VIDEOMODE_40x15 0x04 #define VIDEOMODE_20x30 0x05 #define VIDEOMODE_20x15 0x06 +#define VIDEOMODE_22x23 0x07 +#define VIDEOMODE_64x50 0x08 +#define VIDEOMODE_64x25 0x09 +#define VIDEOMODE_32x50 0x0A +#define VIDEOMODE_32x25 0x0B #define VIDEOMODE_80COL VIDEOMODE_80x60 #define VIDEOMODE_40COL VIDEOMODE_40x30 #define VIDEOMODE_320x240 0x80 @@ -256,6 +261,42 @@ struct __vera { unsigned char vstart; /* Vertical start position */ unsigned char vstop; /* Vertical stop position */ }; + struct { /* Visible when DCSEL flag = 2 */ + unsigned char fxctrl; + unsigned char fxtilebase; + unsigned char fxmapbase; + unsigned char fxmult; + }; + struct { /* Visible when DCSEL flag = 3 */ + unsigned char fxxincrl; + unsigned char fxxincrh; + unsigned char fxyincrl; + unsigned char fxyincrh; + }; + struct { /* Visible when DCSEL flag = 4 */ + unsigned char fxxposl; + unsigned char fxxposh; + unsigned char fxyposl; + unsigned char fxyposh; + }; + struct { /* Visible when DCSEL flag = 5 */ + unsigned char fxxposs; + unsigned char fxyposs; + unsigned char fxpolyfilll; + unsigned char fxpolyfillh; + }; + struct { /* Visible when DCSEL flag = 6 */ + unsigned char fxcachel; + unsigned char fxcachem; + unsigned char fxcacheh; + unsigned char fxcacheu; + }; + struct { /* Visible when DCSEL flag = 63 */ + unsigned char dcver0; + unsigned char dcver1; + unsigned char dcver2; + unsigned char dcver3; + }; } display; struct { unsigned char config; /* Layer map geometry */ diff --git a/include/dirent.h b/include/dirent.h index 124c7f224..60982ba05 100644 --- a/include/dirent.h +++ b/include/dirent.h @@ -33,6 +33,8 @@ #ifndef _DIRENT_H #define _DIRENT_H +#include <target.h> + /*****************************************************************************/ @@ -46,31 +48,15 @@ typedef struct DIR DIR; #if defined(__APPLE2__) struct dirent { - char d_name[16]; - unsigned d_ino; - unsigned d_blocks; - unsigned long d_size; - unsigned char d_type; - struct { - unsigned day :5; - unsigned mon :4; - unsigned year :7; - } d_cdate; - struct { - unsigned char min; - unsigned char hour; - } d_ctime; - unsigned char d_access; - unsigned d_auxtype; - struct { - unsigned day :5; - unsigned mon :4; - unsigned year :7; - } d_mdate; - struct { - unsigned char min; - unsigned char hour; - } d_mtime; + char d_name[16]; + unsigned d_ino; + unsigned d_blocks; + unsigned long d_size; + unsigned char d_type; + struct datetime d_ctime; + unsigned char d_access; + unsigned d_auxtype; + struct datetime d_mtime; }; #define _DE_ISREG(t) ((t) != 0x0F) @@ -161,7 +147,5 @@ void __fastcall__ seekdir (DIR* dir, long offs); void __fastcall__ rewinddir (DIR* dir); - - /* End of dirent.h */ #endif diff --git a/include/lynx.h b/include/lynx.h index 41dc5acb3..259b3da71 100644 --- a/include/lynx.h +++ b/include/lynx.h @@ -31,27 +31,15 @@ /* */ /*****************************************************************************/ - - #ifndef _LYNX_H #define _LYNX_H - - /* Check for errors */ #if !defined(__LYNX__) # error This module may only be used when compiling for the Lynx game console! #endif - - -/*****************************************************************************/ -/* Data */ -/*****************************************************************************/ - - - -/* Color defines */ +/* Color definitions */ #define COLOR_TRANSPARENT 0x00 #define COLOR_BLACK 0x01 #define COLOR_RED 0x02 @@ -88,6 +76,56 @@ #define TGI_COLOR_LIGHTBLUE COLOR_LIGHTBLUE #define TGI_COLOR_WHITE COLOR_WHITE +/* No support for dynamically loadable drivers */ +#define DYN_DRV 0 + +/* Addresses of static drivers */ +extern void lynx_stdjoy_joy[]; /* Referred to by joy_static_stddrv[] */ +extern void lynx_comlynx_ser[]; /* Referred to by ser_static_stddrv[] */ +extern void lynx_160_102_16_tgi[]; /* Referred to by tgi_static_stddrv[] */ + +/* Sound support */ +void lynx_snd_init (void); /* Initialize the sound driver */ +void lynx_snd_pause (void); /* Pause sound */ +void lynx_snd_continue (void); /* Continue sound after pause */ +void __fastcall__ lynx_snd_play (unsigned char channel, unsigned char *music); /* Play tune on channel */ +void lynx_snd_stop (void); /* Stop sound on all channels */ +void __fastcall__ lynx_snd_stop_channel (unsigned char channel); /* Stop sound on all channels */ +unsigned char lynx_snd_active(void); /* Show which channels are active */ + +/* Cartridge access */ +void __fastcall__ lynx_load (int file_number); /* Load a file into RAM using a zero-based index */ +void __fastcall__ lynx_exec (int file_number); /* Load a file into ram and execute it */ + +/* EEPROM access */ +unsigned __fastcall__ lynx_eeprom_read (unsigned char cell); /* Read a 16 bit word from the given address */ +unsigned __fastcall__ lynx_eeprom_write (unsigned char cell, unsigned val); /* Write the word at the given address */ +void __fastcall__ lynx_eeprom_erase (unsigned char cell); /* Clear the word at the given address */ +unsigned __fastcall__ lynx_eeread (unsigned cell); /* Read a 16 bit word from the given address 93C46, 93C66 or 93C86 */ +unsigned __fastcall__ lynx_eewrite (unsigned cell, unsigned val); /* Write the word at the given address 93C46, 93C66 or 93C86 */ + +/* TGI extras */ +#define tgi_sprite(spr) tgi_ioctl(0, spr) +#define tgi_flip() tgi_ioctl(1, (void*)0) +#define tgi_setbgcolor(bgcol) tgi_ioctl(2, (void*)(bgcol)) +#define tgi_setframerate(rate) tgi_ioctl(3, (void*)(rate)) +#define tgi_busy() tgi_ioctl(4, (void*)0) +#define tgi_updatedisplay() tgi_ioctl(4, (void*)1) +#define tgi_setcollisiondetection(active) tgi_ioctl(5, (void*)(active)) + +/* Hardware definitions */ +#include <_mikey.h> +#define MIKEY (*(struct __mikey *)0xFD00) + +#define _MIKEY_TIMERS (*(struct _mikey_all_timers *) 0xFD00) /* mikey_timers[8] */ +#define _HBL_TIMER (*(struct _mikey_timer *) 0xFD00) /* timer0 (HBL) */ +#define _VBL_TIMER (*(struct _mikey_timer *) 0xFD08) /* timer2 (VBL) */ +#define _UART_TIMER (*(struct _mikey_timer *) 0xFD14) /* timer4 (UART) */ +#define _VIDDMA (*(unsigned int *) 0xFD92) /* DISPCTL/VIDDMA */ + +#include <_suzy.h> +#define SUZY (*(volatile struct __suzy*)0xFC00) + /* Masks for joy_read */ #define JOY_UP_MASK 0x80 #define JOY_DOWN_MASK 0x40 @@ -102,118 +140,5 @@ #define JOY_BTN_A(v) ((v) & JOY_BTN_A_MASK) #define JOY_BTN_B(v) ((v) & JOY_BTN_B_MASK) -/* No support for dynamically loadable drivers */ -#define DYN_DRV 0 - - - -/*****************************************************************************/ -/* Variables */ -/*****************************************************************************/ - - - -/* The addresses of the static drivers */ -extern void lynx_stdjoy_joy[]; /* Referred to by joy_static_stddrv[] */ -extern void lynx_comlynx_ser[]; /* Referred to by ser_static_stddrv[] */ -extern void lynx_160_102_16_tgi[]; /* Referred to by tgi_static_stddrv[] */ - - - -/*****************************************************************************/ -/* Sound support */ -/*****************************************************************************/ - - - -void lynx_snd_init (void); -/* Initialize the sound driver */ - -void lynx_snd_pause (void); -/* Pause sound */ - -void lynx_snd_continue (void); -/* Continue sound after pause */ - -void __fastcall__ lynx_snd_play (unsigned char channel, unsigned char *music); -/* Play tune on channel */ - -void lynx_snd_stop (void); -/* Stop sound on all channels */ - -void __fastcall__ lynx_snd_stop_channel (unsigned char channel); -/* Stop sound on all channels */ - -unsigned char lynx_snd_active(void); -/* Show which channels are active */ - - - -/*****************************************************************************/ -/* Accessing the cart */ -/*****************************************************************************/ - - - -void __fastcall__ lynx_load (int fileno); -/* Load a file into ram. The first entry is fileno=0. */ - -void __fastcall__ lynx_exec (int fileno); -/* Load a file into ram and execute it. */ - - - -/*****************************************************************************/ -/* Accessing the EEPROM */ -/*****************************************************************************/ - - - -unsigned __fastcall__ lynx_eeprom_read (unsigned char cell); -/* Read a 16 bit word from the given address */ - -unsigned __fastcall__ lynx_eeprom_write (unsigned char cell, unsigned val); -/* Write the word at the given address */ - -void __fastcall__ lynx_eeprom_erase (unsigned char cell); -/* Clear the word at the given address */ - -unsigned __fastcall__ lynx_eeread (unsigned cell); -/* Read a 16 bit word from the given address 93C46 93C66 or 93C86*/ - -unsigned __fastcall__ lynx_eewrite (unsigned cell, unsigned val); -/* Write the word at the given address 93C46 93C66 or 93C86*/ - - - -/*****************************************************************************/ -/* TGI extras */ -/*****************************************************************************/ - - - -#define tgi_sprite(spr) tgi_ioctl(0, spr) -#define tgi_flip() tgi_ioctl(1, (void*)0) -#define tgi_setbgcolor(bgcol) tgi_ioctl(2, (void*)(bgcol)) -#define tgi_setframerate(rate) tgi_ioctl(3, (void*)(rate)) -#define tgi_busy() tgi_ioctl(4, (void*)0) -#define tgi_updatedisplay() tgi_ioctl(4, (void*)1) -#define tgi_setcollisiondetection(active) tgi_ioctl(5, (void*)(active)) - -/* Define Hardware */ -#include <_mikey.h> -#define MIKEY (*(struct __mikey *)0xFD00) - -#define _MIKEY_TIMERS (*(struct _mikey_all_timers *) 0xFD00) // mikey_timers[8] -#define _HBL_TIMER (*(struct _mikey_timer *) 0xFD00) // timer0 (HBL) -#define _VBL_TIMER (*(struct _mikey_timer *) 0xFD08) // timer2 (VBL) -#define _UART_TIMER (*(struct _mikey_timer *) 0xFD14) // timer4 (UART) -#define _VIDDMA (*(unsigned int *) 0xFD92) // dispctl/viddma - -#include <_suzy.h> -#define SUZY (*(struct __suzy*)0xFC00) - - - /* End of lynx.h */ #endif diff --git a/include/lzsa.h b/include/lzsa.h new file mode 100644 index 000000000..9b0eb82e5 --- /dev/null +++ b/include/lzsa.h @@ -0,0 +1,56 @@ +/*****************************************************************************/ +/* */ +/* lzsa.h */ +/* */ +/* Decompression routine for the 'lzsa' format */ +/* */ +/* */ +/* */ +/* (C) 2022 John Brandwood */ +/* */ +/* */ +/* Boost license: */ +/* Distributed under the Boost Software License, Version 1.0. */ +/* Boost Software License - Version 1.0 - August 17th, 2003 */ +/* */ +/* Permission is hereby granted, free of charge, to any person or */ +/* organization */ +/* obtaining a copy of the software and accompanying documentation covered by*/ +/* this license (the "Software") to use, reproduce, display, distribute, */ +/* execute, and transmit the Software, and to prepare derivative works of the*/ +/* Software, and to permit third-parties to whom the Software is furnished to*/ +/* do so, all subject to the following: */ +/* */ +/* The copyright notices in the Software and this entire statement, including*/ +/* the above license grant, this restriction and the following disclaimer, */ +/* must be included in all copies of the Software, in whole or in part, and */ +/* all derivative works of the Software, unless such copies or derivative */ +/* works are solely in the form of machine-executable object code generated */ +/* by a source language processor. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR*/ +/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */ +/* FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT */ +/* SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE */ +/* FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR */ +/* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE */ +/* USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*****************************************************************************/ + + + +#ifndef _LZSA_H +#define _LZSA_H + +void __fastcall__ decompress_lzsa1 (const unsigned char* src, unsigned char* const dst); +/* Decompresses the source buffer into the destination buffer. +** compress with lzsa -r -f 1 input.bin output.lzsa1 + */ + +void __fastcall__ decompress_lzsa2 (const unsigned char* src, unsigned char* const dst); +/* Decompresses the source buffer into the destination buffer. +** compress with lzsa -r -f 2 input.bin output.lzsa2 + */ + +/* end of lzsa.h */ +#endif diff --git a/include/rp6502.h b/include/rp6502.h new file mode 100644 index 000000000..7deeebc4c --- /dev/null +++ b/include/rp6502.h @@ -0,0 +1,266 @@ +/*****************************************************************************/ +/* */ +/* rp6502.h */ +/* */ +/* Picocomputer 6502 */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed 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. */ +/* */ +/*****************************************************************************/ + +#ifndef _RP6502_H +#define _RP6502_H + +/* RP6502 VIA $FFD0-$FFDF */ + +#include <_6522.h> +#define VIA (*(volatile struct __6522 *)0xFFD0) + +/* RP6502 RIA $FFE0-$FFF9 */ + +struct __RP6502 +{ + const unsigned char ready; + unsigned char tx; + const unsigned char rx; + const unsigned char vsync; + unsigned char rw0; + unsigned char step0; + unsigned int addr0; + unsigned char rw1; + unsigned char step1; + unsigned int addr1; + unsigned char xstack; + unsigned int errno; + unsigned char op; + unsigned char irq; + const unsigned char spin; + const unsigned char busy; + const unsigned char lda; + unsigned char a; + const unsigned char ldx; + unsigned char x; + const unsigned char rts; + unsigned int sreg; +}; +#define RIA (*(volatile struct __RP6502 *)0xFFE0) + +#define RIA_READY_TX_BIT 0x80 +#define RIA_READY_RX_BIT 0x40 +#define RIA_BUSY_BIT 0x80 + +/* XSTACK helpers */ + +void __fastcall__ ria_push_long (unsigned long val); +void __fastcall__ ria_push_int (unsigned int val); +#define ria_push_char(v) RIA.xstack = v + +long ria_pop_long (void); +int ria_pop_int (void); +#define ria_pop_char() RIA.xstack + +/* Set the RIA fastcall register */ + +void __fastcall__ ria_set_axsreg (unsigned long axsreg); +void __fastcall__ ria_set_ax (unsigned int ax); +#define ria_set_a(v) RIA.a = v + +/* Run an OS operation */ + +int __fastcall__ ria_call_int (unsigned char op); +long __fastcall__ ria_call_long (unsigned char op); + +/* These run _mappederrno() on error */ + +int __fastcall__ ria_call_int_errno (unsigned char op); +long __fastcall__ ria_call_long_errno (unsigned char op); + +/* OS operation numbers */ + +#define RIA_OP_EXIT 0xFF +#define RIA_OP_ZXSTACK 0x00 +#define RIA_OP_XREG 0x01 +#define RIA_OP_PHI2 0x02 +#define RIA_OP_CODEPAGE 0x03 +#define RIA_OP_LRAND 0x04 +#define RIA_OP_STDIN_OPT 0x05 +#define RIA_OP_CLOCK 0x0F +#define RIA_OP_CLOCK_GETRES 0x10 +#define RIA_OP_CLOCK_GETTIME 0x11 +#define RIA_OP_CLOCK_SETTIME 0x12 +#define RIA_OP_CLOCK_GETTIMEZONE 0x13 +#define RIA_OP_OPEN 0x14 +#define RIA_OP_CLOSE 0x15 +#define RIA_OP_READ_XSTACK 0x16 +#define RIA_OP_READ_XRAM 0x17 +#define RIA_OP_WRITE_XSTACK 0x18 +#define RIA_OP_WRITE_XRAM 0x19 +#define RIA_OP_LSEEK 0x1A +#define RIA_OP_UNLINK 0x1B +#define RIA_OP_RENAME 0x1C + +/* C API for the operating system. */ + +int __cdecl__ xregn (char device, char channel, unsigned char address, unsigned count, + ...); +int __cdecl__ xreg (char device, char channel, unsigned char address, ...); +int phi2 (void); +int codepage (void); +long lrand (void); +int __fastcall__ stdin_opt (unsigned long ctrl_bits, unsigned char str_length); +int __fastcall__ read_xstack (void* buf, unsigned count, int fildes); +int __fastcall__ read_xram (unsigned buf, unsigned count, int fildes); +int __fastcall__ write_xstack (const void* buf, unsigned count, int fildes); +int __fastcall__ write_xram (unsigned buf, unsigned count, int fildes); + +/* XREG location helpers */ + +#define xreg_ria_keyboard(...) xreg(0, 0, 0, __VA_ARGS__) +#define xreg_ria_mouse(...) xreg(0, 0, 1, __VA_ARGS__) +#define xreg_vga_canvas(...) xreg(1, 0, 0, __VA_ARGS__) +#define xreg_vga_mode(...) xreg(1, 0, 1, __VA_ARGS__) + +/* XRAM structure helpers */ + +#define xram0_struct_set(addr, type, member, val) \ + RIA.addr0 = (unsigned)(&((type *)0)->member) + (unsigned)addr; \ + switch (sizeof(((type *)0)->member)) \ + { \ + case 1: \ + RIA.rw0 = val; \ + break; \ + case 2: \ + RIA.step0 = 1; \ + RIA.rw0 = val & 0xff; \ + RIA.rw0 = (val >> 8) & 0xff; \ + break; \ + case 4: \ + RIA.step0 = 1; \ + RIA.rw0 = (unsigned long)val & 0xff; \ + RIA.rw0 = ((unsigned long)val >> 8) & 0xff; \ + RIA.rw0 = ((unsigned long)val >> 16) & 0xff; \ + RIA.rw0 = ((unsigned long)val >> 24) & 0xff; \ + break; \ + } + +#define xram1_struct_set(addr, type, member, val) \ + RIA.addr1 = (unsigned)(&((type *)0)->member) + (unsigned)addr; \ + switch (sizeof(((type *)0)->member)) \ + { \ + case 1: \ + RIA.rw1 = val; \ + break; \ + case 2: \ + RIA.step1 = 1; \ + RIA.rw1 = val & 0xff; \ + RIA.rw1 = (val >> 8) & 0xff; \ + break; \ + case 4: \ + RIA.step1 = 1; \ + RIA.rw1 = (unsigned long)val & 0xff; \ + RIA.rw1 = ((unsigned long)val >> 8) & 0xff; \ + RIA.rw1 = ((unsigned long)val >> 16) & 0xff; \ + RIA.rw1 = ((unsigned long)val >> 24) & 0xff; \ + break; \ + } + +typedef struct +{ + unsigned char x_wrap; // bool + unsigned char y_wrap; // bool + int x_pos_px; + int y_pos_px; + int width_chars; + int height_chars; + unsigned xram_data_ptr; + unsigned xram_palette_ptr; + unsigned xram_font_ptr; +} vga_mode1_config_t; + +typedef struct +{ + unsigned char x_wrap; // bool + unsigned char y_wrap; // bool + int x_pos_px; + int y_pos_px; + int width_tiles; + int height_tiles; + unsigned xram_data_ptr; + unsigned xram_palette_ptr; + unsigned xram_tile_ptr; +} vga_mode2_config_t; + +typedef struct +{ + unsigned char x_wrap; // bool + unsigned char y_wrap; // bool + int x_pos_px; + int y_pos_px; + int width_px; + int height_px; + unsigned xram_data_ptr; + unsigned xram_palette_ptr; +} vga_mode3_config_t; + +typedef struct +{ + int x_pos_px; + int y_pos_px; + unsigned xram_sprite_ptr; + unsigned char log_size; + unsigned char has_opacity_metadata; // bool +} vga_mode4_sprite_t; + +typedef struct +{ + int transform[6]; + int x_pos_px; + int y_pos_px; + unsigned xram_sprite_ptr; + unsigned char log_size; + unsigned char has_opacity_metadata; // bool +} vga_mode4_asprite_t; + +/* Values in __oserror are the union of these FatFs errors and errno.h */ + +typedef enum +{ + FR_OK = 32, /* Succeeded */ + FR_DISK_ERR, /* A hard error occurred in the low level disk I/O layer */ + FR_INT_ERR, /* Assertion failed */ + FR_NOT_READY, /* The physical drive cannot work */ + FR_NO_FILE, /* Could not find the file */ + FR_NO_PATH, /* Could not find the path */ + FR_INVALID_NAME, /* The path name format is invalid */ + FR_DENIED, /* Access denied due to prohibited access or directory full */ + FR_EXIST, /* Access denied due to prohibited access */ + FR_INVALID_OBJECT, /* The file/directory object is invalid */ + FR_WRITE_PROTECTED, /* The physical drive is write protected */ + FR_INVALID_DRIVE, /* The logical drive number is invalid */ + FR_NOT_ENABLED, /* The volume has no work area */ + FR_NO_FILESYSTEM, /* There is no valid FAT volume */ + FR_MKFS_ABORTED, /* The f_mkfs() aborted due to any problem */ + FR_TIMEOUT, /* Could not get a grant to access the volume within defined period */ + FR_LOCKED, /* The operation is rejected according to the file sharing policy */ + FR_NOT_ENOUGH_CORE, /* LFN working buffer could not be allocated */ + FR_TOO_MANY_OPEN_FILES, /* Number of open files > FF_FS_LOCK */ + FR_INVALID_PARAMETER /* Given parameter is invalid */ +} FRESULT; + +#endif /* _RP6502_H */ diff --git a/include/sim65.h b/include/sim65.h new file mode 100644 index 000000000..ef59955ff --- /dev/null +++ b/include/sim65.h @@ -0,0 +1,136 @@ +/*****************************************************************************/ +/* */ +/* sim65.h */ +/* */ +/* Definitions for the sim6502 and sim65c02 targets */ +/* */ +/* */ +/* */ +/* (C) 2025 Sidney Cadot */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed 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. */ +/* */ +/*****************************************************************************/ + +#ifndef _SIM65_H +#define _SIM65_H + +/* Check that we include this file while compiling to a compatible target. */ +#if !defined(__SIM6502__) && !defined(__SIM65C02__) +# error This module may only be used when compiling for the sim6502 or sim65c02 targets! +#endif + +#include <stdint.h> + +/* The sim65 targets (sim6502, sim65c02) have a peripheral memory aperture located at + * address range 0xFFC0 .. 0xFFDF. Currently, the following peripherals are located + * inside that memory apeture: + * + * $FFC0 .. $FFC9 "counter" peripheral + * $FFCA .. $FFCB "sim65 control" peripheral + * $FFCC .. $FFDF (currently unused) + * + * The "peripherals" structure below corresponds to the register layout of the currently + * defined peripherals in this memory range. Combined with the fact that the sim6502 and + * sim65c02 linker configuration files define the "peripherals" symbol to be fixed at + * address $FFC0, this provides easy-to-use and efficient access to the peripheral registers. + * + * After including "sim65.h", it is possible for a C program to do things like: + * + * { + * peripherals.counter.latch = 0; + * peripherals.sim65.cpu_mode = SIM65_CPU_MODE_6502X; + * peripherals.sim65.trace_mode = SIM65_TRACE_MODE_ENABLE_FULL; + * } + * + * Note that "peripherals" variable is declared volatile. This instructs a C compiler to + * forego optimizations on memory accesses to the variable. However, CC65 currently ignores + * the volatile attribute. Fortunately, it is not smart with respect to optimizing + * memory accesses, so accessing the "peripherals" fields works fine in practice. + */ + +extern volatile struct { + struct { + uint8_t latch; + uint8_t select; + union { + uint8_t value [8]; /* Access value as eight separate bytes. */ + uint16_t value16 [4]; /* Access value as four 16-bit words. */ + uint32_t value32 [2]; /* Access value as two 32-bit long words. */ + }; + } counter; + struct { + uint8_t cpu_mode; + uint8_t trace_mode; + } sim65; +} peripherals; + +/* Values for the peripherals.counter.select field. */ +#define COUNTER_SELECT_CLOCKCYCLE_COUNTER 0x00 +#define COUNTER_SELECT_INSTRUCTION_COUNTER 0x01 +#define COUNTER_SELECT_IRQ_COUNTER 0x02 +#define COUNTER_SELECT_NMI_COUNTER 0x03 +#define COUNTER_SELECT_WALLCLOCK_TIME 0x80 +#define COUNTER_SELECT_WALLCLOCK_TIME_SPLIT 0x81 + +/* Values for the peripherals.sim65.cpu_mode field. */ +#define SIM65_CPU_MODE_6502 0x00 +#define SIM65_CPU_MODE_65C02 0x01 +#define SIM65_CPU_MODE_6502X 0x02 + +/* Bitfield values for the peripherals.sim65.trace_mode field. */ +#define SIM65_TRACE_MODE_FIELD_INSTR_COUNTER 0x40 +#define SIM65_TRACE_MODE_FIELD_CLOCK_COUNTER 0x20 +#define SIM65_TRACE_MODE_FIELD_PC 0x10 +#define SIM65_TRACE_MODE_FIELD_INSTR_BYTES 0x08 +#define SIM65_TRACE_MODE_FIELD_INSTR_ASSEMBLY 0x04 +#define SIM65_TRACE_MODE_FIELD_CPU_REGISTERS 0x02 +#define SIM65_TRACE_MODE_FIELD_CC65_SP 0x01 + +/* Values for the peripherals.sim65.trace_mode field that fully disable / enable tracing. */ +#define SIM65_TRACE_MODE_DISABLE 0x00 +#define SIM65_TRACE_MODE_ENABLE_FULL 0x7F + +/* Convenience macros to enable / disable tracing at runtime. */ +#define TRACE_ON() do peripherals.sim65.trace_mode = SIM65_TRACE_MODE_ENABLE_FULL; while(0) +#define TRACE_OFF() do peripherals.sim65.trace_mode = SIM65_TRACE_MODE_DISABLE; while(0) + +/* Convenience macro to query the CPU mode at runtime. */ +#define GET_CPU_MODE() peripherals.sim65.cpu_mode + +/* Convenience macro to set the CPU mode at runtime. + * + * Use SIM65_CPU_MODE_6502, SIM65_CPU_MODE_65C02, or SIM65_CPU_MODE_6502 as argument. + * + * Important Note: + * + * When running in a program compiled for the "sim6502" target, it is safe to switch to + * 65C02 or 6502X mode, since the runtime library will only use plain 6502 opcodes, and + * those work the same in 65C02 and 6502X mode. + * + * However, when running in a program compiled for the "sim65c02" target, it is NOT safe + * to switch to 6502 or 6502X mode, since many routines in the runtime library use + * 65C02-specific opcodes, and these will not work as expected when the CPU is switched + * to 6502 or 6502X mode. When such an instruction is encountered, the program will + * exhibit undefined behavior. + */ +#define SET_CPU_MODE(mode) do peripherals.sim65.cpu_mode = mode; while(0) + +/* End of sim65.h */ +#endif diff --git a/include/stdint.h b/include/stdint.h index 5d6f04769..6d51565e0 100644 --- a/include/stdint.h +++ b/include/stdint.h @@ -52,15 +52,15 @@ typedef unsigned char uint8_t; typedef unsigned uint16_t; typedef unsigned long uint32_t; -#define INT8_MIN ((int8_t) 0x80) -#define INT8_MAX ((int8_t) 0x7F) -#define INT16_MIN ((int16_t) 0x8000) -#define INT16_MAX ((int16_t) 0x7FFF) -#define INT32_MIN ((int32_t) 0x80000000) -#define INT32_MAX ((int32_t) 0x7FFFFFFF) -#define UINT8_MAX ((uint8_t) 0xFF) -#define UINT16_MAX ((uint16_t) 0xFFFF) -#define UINT32_MAX ((uint32_t) 0xFFFFFFFF) +#define INT8_MIN -128 +#define INT8_MAX 127 +#define INT16_MIN (-32767 - 1) +#define INT16_MAX 32767 +#define INT32_MIN (-2147483647L - 1L) +#define INT32_MAX 2147483647L +#define UINT8_MAX 255 +#define UINT16_MAX 65535U +#define UINT32_MAX 4294967295UL /* Minimum-width integer types */ typedef signed char int_least8_t; @@ -70,15 +70,15 @@ typedef unsigned char uint_least8_t; typedef unsigned uint_least16_t; typedef unsigned long uint_least32_t; -#define INT_LEAST8_MIN ((int_least8_t) 0x80) -#define INT_LEAST8_MAX ((int_least8_t) 0x7F) -#define INT_LEAST16_MIN ((int_least16_t) 0x8000) -#define INT_LEAST16_MAX ((int_least16_t) 0x7FFF) -#define INT_LEAST32_MIN ((int_least32_t) 0x80000000) -#define INT_LEAST32_MAX ((int_least32_t) 0x7FFFFFFF) -#define UINT_LEAST8_MAX ((uint_least8_t) 0xFF) -#define UINT_LEAST16_MAX ((uint_least16_t) 0xFFFF) -#define UINT_LEAST32_MAX ((uint_least32_t) 0xFFFFFFFF) +#define INT_LEAST8_MIN INT8_MIN +#define INT_LEAST8_MAX INT8_MAX +#define INT_LEAST16_MIN INT16_MIN +#define INT_LEAST16_MAX INT16_MAX +#define INT_LEAST32_MIN INT32_MIN +#define INT_LEAST32_MAX INT32_MAX +#define UINT_LEAST8_MAX UINT8_MAX +#define UINT_LEAST16_MAX UINT16_MAX +#define UINT_LEAST32_MAX UINT32_MAX /* Fastest minimum-width integer types */ typedef signed char int_fast8_t; @@ -88,40 +88,40 @@ typedef unsigned char uint_fast8_t; typedef unsigned uint_fast16_t; typedef unsigned long uint_fast32_t; -#define INT_FAST8_MIN ((int_fast8_t) 0x80) -#define INT_FAST8_MAX ((int_fast8_t) 0x7F) -#define INT_FAST16_MIN ((int_fast16_t) 0x8000) -#define INT_FAST16_MAX ((int_fast16_t) 0x7FFF) -#define INT_FAST32_MIN ((int_fast32_t) 0x80000000) -#define INT_FAST32_MAX ((int_fast32_t) 0x7FFFFFFF) -#define UINT_FAST8_MAX ((uint_fast8_t) 0xFF) -#define UINT_FAST16_MAX ((uint_fast16_t) 0xFFFF) -#define UINT_FAST32_MAX ((uint_fast32_t) 0xFFFFFFFF) +#define INT_FAST8_MIN INT8_MIN +#define INT_FAST8_MAX INT8_MAX +#define INT_FAST16_MIN INT16_MIN +#define INT_FAST16_MAX INT16_MAX +#define INT_FAST32_MIN INT32_MIN +#define INT_FAST32_MAX INT32_MAX +#define UINT_FAST8_MAX UINT8_MAX +#define UINT_FAST16_MAX UINT16_MAX +#define UINT_FAST32_MAX UINT32_MAX /* Integer types capable of holding object pointers */ typedef int intptr_t; typedef unsigned uintptr_t; -#define INTPTR_MIN ((intptr_t)0x8000) -#define INTPTR_MAX ((intptr_t)0x7FFF) -#define UINTPTR_MAX ((uintptr_t) 0xFFFF) +#define INTPTR_MIN INT16_MIN +#define INTPTR_MAX INT16_MAX +#define UINTPTR_MAX UINT16_MAX /* Greatest width integer types */ typedef long intmax_t; typedef unsigned long uintmax_t; -#define INTMAX_MIN ((intmax_t) 0x80000000) -#define INTMAX_MAX ((intmax_t) 0x7FFFFFFF) -#define UINTMAX_MAX ((uintmax_t) 0xFFFFFFFF) +#define INTMAX_MIN INT32_MIN +#define INTMAX_MAX INT32_MAX +#define UINTMAX_MAX UINT32_MAX /* Limits of other integer types */ -#define PTRDIFF_MIN ((int) 0x8000) -#define PTRDIFF_MAX ((int) 0x7FFF) +#define PTRDIFF_MIN INT16_MIN +#define PTRDIFF_MAX INT16_MAX -#define SIG_ATOMIC_MIN ((unsigned char) 0x00) -#define SIG_ATOMIC_MAX ((unsigned char) 0xFF) +#define SIG_ATOMIC_MIN 0 +#define SIG_ATOMIC_MAX UINT8_MAX -#define SIZE_MAX 0xFFFF +#define SIZE_MAX UINT16_MAX /* Macros for minimum width integer constants */ #define INT8_C(c) c diff --git a/include/stdio.h b/include/stdio.h index 012b8e2ba..35ebd7784 100644 --- a/include/stdio.h +++ b/include/stdio.h @@ -86,6 +86,10 @@ extern FILE* stderr; # define FILENAME_MAX (80+1) #elif defined(__TELESTRAT__) # define FILENAME_MAX (50+1) +#elif defined(__SIM6502__) +# define FILENAME_MAX (1024+1) +#elif defined(__SIM65C02__) +# define FILENAME_MAX (1024+1) #else # define FILENAME_MAX (16+1) #endif diff --git a/include/string.h b/include/string.h index abaf80e7d..3b7ece1d9 100644 --- a/include/string.h +++ b/include/string.h @@ -81,6 +81,7 @@ void __fastcall__ bzero (void* ptr, size_t n); /* BSD */ char* __fastcall__ strdup (const char* s); /* SYSV/BSD */ int __fastcall__ stricmp (const char* s1, const char* s2); /* DOS/Windows */ int __fastcall__ strcasecmp (const char* s1, const char* s2); /* Same for Unix */ +char* __fastcall__ strcasestr (const char* str, const char* substr); int __fastcall__ strnicmp (const char* s1, const char* s2, size_t count); /* DOS/Windows */ int __fastcall__ strncasecmp (const char* s1, const char* s2, size_t count); /* Same for Unix */ size_t __fastcall__ strnlen (const char* s, size_t maxlen); /* POSIX.1-2008 */ @@ -89,6 +90,7 @@ char* __fastcall__ strlower (char* s); char* __fastcall__ strupr (char* s); char* __fastcall__ strupper (char* s); char* __fastcall__ strqtok (char* s1, const char* s2); +char* __fastcall__ stpcpy (char* dest, const char* src); #endif const char* __fastcall__ __stroserror (unsigned char errcode); diff --git a/include/sys/stat.h b/include/sys/stat.h index d8fc09c75..0e1589d52 100644 --- a/include/sys/stat.h +++ b/include/sys/stat.h @@ -2,7 +2,7 @@ /* */ /* stat.h */ /* */ -/* Constants for the mode argument of open and creat */ +/* stat(2) definition */ /* */ /* */ /* */ @@ -11,6 +11,9 @@ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ +/* (C) 2023 Colin Leroy-Mira */ +/* EMail: colin@colino.net */ +/* */ /* */ /* This software is provided 'as-is', without any expressed or implied */ /* warranty. In no event will the authors be held liable for any damages */ @@ -36,6 +39,10 @@ #ifndef _STAT_H #define _STAT_H +#include <time.h> +#include <target.h> +#include <sys/types.h> + /*****************************************************************************/ @@ -47,6 +54,30 @@ #define S_IREAD 0x01 #define S_IWRITE 0x02 +#define S_IFMT 0x03 + +struct stat { + dev_t st_dev; + ino_t st_ino; + mode_t st_mode; + nlink_t st_nlink; + uid_t st_uid; + gid_t st_gid; + off_t st_size; + struct timespec st_atim; + struct timespec st_ctim; + struct timespec st_mtim; + #ifdef __APPLE2__ + unsigned char st_access; + unsigned char st_type; + unsigned int st_auxtype; + unsigned char st_storagetype; + unsigned int st_blocks; + struct datetime st_mtime; + struct datetime st_ctime; + #endif +}; + /*****************************************************************************/ @@ -55,5 +86,9 @@ +int __fastcall__ stat (const char* pathname, struct stat* statbuf); + + + /* End of stat.h */ #endif diff --git a/libsrc/apple2/dir.h b/include/sys/statvfs.h similarity index 69% rename from libsrc/apple2/dir.h rename to include/sys/statvfs.h index 369080c47..d9edc2f23 100644 --- a/libsrc/apple2/dir.h +++ b/include/sys/statvfs.h @@ -1,12 +1,13 @@ /*****************************************************************************/ /* */ -/* dir.h */ +/* statvfs.h */ /* */ -/* Apple ][ system specific DIR */ +/* statvfs(3) definition */ /* */ /* */ /* */ -/* (C) 2005 Oliver Schmidt, <ol.sc@web.de> */ +/* (C) 2023 Colin Leroy-Mira */ +/* EMail: colin@colino.net */ /* */ /* */ /* This software is provided 'as-is', without any expressed or implied */ @@ -30,8 +31,10 @@ -#ifndef _DIR_H -#define _DIR_H +#ifndef _STATVFS_H +#define _STATVFS_H + +#include <sys/types.h> @@ -41,22 +44,31 @@ -struct DIR { - int fd; - unsigned char entry_length; - unsigned char entries_per_block; - unsigned char current_entry; - union { - unsigned char bytes[512]; - struct { - unsigned prev_block; - unsigned next_block; - unsigned char entries[1]; - } content; - } block; +struct statvfs { + unsigned long f_bsize; + unsigned long f_frsize; + fsblkcnt_t f_blocks; + fsblkcnt_t f_bfree; + fsblkcnt_t f_bavail; + fsfilcnt_t f_files; + fsfilcnt_t f_ffree; + fsfilcnt_t f_favail; + unsigned long f_fsid; + unsigned long f_flag; + unsigned long f_namemax; }; -/* End of dir.h */ +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +int __fastcall__ statvfs (const char* pathname, struct statvfs* buf); + + + +/* End of statvfs.h */ #endif diff --git a/include/sys/types.h b/include/sys/types.h index e75dd7d46..89b91c5b4 100644 --- a/include/sys/types.h +++ b/include/sys/types.h @@ -50,6 +50,46 @@ typedef long int off_t; #endif +#ifndef _HAVE_dev_t +#define _HAVE_dev_t +typedef unsigned long int dev_t; +#endif + +#ifndef _HAVE_ino_t +#define _HAVE_ino_t +typedef unsigned long int ino_t; +#endif + +#ifndef _HAVE_nlink_t +#define _HAVE_nlink_t +typedef unsigned long int nlink_t; +#endif + +#ifndef _HAVE_uid_t +#define _HAVE_uid_t +typedef unsigned char uid_t; +#endif + +#ifndef _HAVE_gid_t +#define _HAVE_gid_t +typedef unsigned char gid_t; +#endif + +#ifndef _HAVE_mode_t +#define _HAVE_mode_t +typedef unsigned char mode_t; +#endif + +#ifndef _HAVE_fsblkcnt_t +#define _HAVE_fsblkcnt_t +typedef unsigned long int fsblkcnt_t; +#endif + +#ifndef _HAVE_fsfilcnt_t +#define _HAVE_fsfilcnt_t +typedef unsigned long int fsfilcnt_t; +#endif + /*****************************************************************************/ @@ -60,6 +100,3 @@ typedef long int off_t; /* End of types.h */ #endif - - - diff --git a/include/time.h b/include/time.h index bfc2ac435..5eb6f144a 100644 --- a/include/time.h +++ b/include/time.h @@ -37,6 +37,11 @@ #define _TIME_H +/* Forward declaration for target.h */ +typedef unsigned long time_t; +typedef unsigned long clock_t; + + /* NULL pointer */ #ifndef NULL @@ -49,9 +54,6 @@ typedef unsigned size_t; #endif -typedef unsigned long time_t; -typedef unsigned long clock_t; - /* Structure for broken down time */ struct tm { int tm_sec; @@ -84,6 +86,8 @@ struct tm { # define CLOCKS_PER_SEC 135 /* FIXME */ #elif defined(__GEOS__) # define CLOCKS_PER_SEC 1 +#elif defined (__RP6502__) +# define CLOCKS_PER_SEC 100 #elif defined(__TELESTRAT__) # define CLOCKS_PER_SEC 10 #elif defined(__ATARI__) || defined (__LYNX__) diff --git a/include/zx02.h b/include/zx02.h new file mode 100644 index 000000000..b3e711d56 --- /dev/null +++ b/include/zx02.h @@ -0,0 +1,44 @@ +/*****************************************************************************/ +/* */ +/* zx02.h */ +/* */ +/* Decompression routine for the 'zx02' format */ +/* */ +/* */ +/* */ +/* (C) 2022 DMSC */ +/* */ +/* */ +/* MIT license: */ +/* Permission is hereby granted, free of charge, to any person obtaining a */ +/* copy of this software and associated documentation files (the “Software”),*/ +/* to deal in the Software without restriction, including without limitation */ +/* the rights to use, copy, modify, merge, publish, distribute, sublicense, */ +/* and/or sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be included */ +/* in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS */ +/* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN */ +/* NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, */ +/* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR */ +/* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE */ +/* USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*****************************************************************************/ + + + +#ifndef _ZX02_H +#define _ZX02_H + +void __fastcall__ decompress_zx02 (const unsigned char* src, unsigned char* const dst); +/* Decompresses the source buffer into the destination buffer. +** compress with zx02 input.bin output.zx02 + */ + + +/* end of zx02.h */ +#endif diff --git a/libsrc/Makefile b/libsrc/Makefile index 0873d019f..8a4f11414 100644 --- a/libsrc/Makefile +++ b/libsrc/Makefile @@ -33,6 +33,7 @@ TARGETS = apple2 \ none \ osic1p \ pce \ + rp6502 \ sim6502 \ sim65c02 \ supervision \ @@ -131,8 +132,8 @@ $(TARGETS): | ../lib else # TARGET -CA65FLAGS = -CC65FLAGS = -Or -W error +CA65FLAGS = -g +CC65FLAGS = -g -Or -W error EXTZP = cbm510 \ cbm610 \ diff --git a/libsrc/NameClashes.md b/libsrc/NameClashes.md new file mode 100644 index 000000000..ef2105602 --- /dev/null +++ b/libsrc/NameClashes.md @@ -0,0 +1,380 @@ +List of cc65 library name clashes +================================= + +The following is a list of identifiers that might need +to be fixed, sorted by directory and identifier: + +# common + +## \_\_argc + +* libsrc/runtime/callmain.s +* libsrc/cbm610/mainargs.s +* libsrc/cx16/mainargs.s +* libsrc/plus4/mainargs.s +* libsrc/lynx/mainargs.s +* libsrc/c16/mainargs.s +* libsrc/geos-common/system/mainargs.s +* libsrc/sim6502/mainargs.s +* libsrc/c128/mainargs.s +* libsrc/vic20/mainargs.s +* libsrc/nes/mainargs.s +* libsrc/atari/getargs.s +* libsrc/apple2/mainargs.s +* libsrc/cbm510/mainargs.s +* libsrc/telestrat/mainargs.s +* libsrc/c64/mainargs.s +* libsrc/pet/mainargs.s +* libsrc/atmos/mainargs.s + +## \_\_argv + +* libsrc/runtime/callmain.s +* libsrc/cbm610/mainargs.s +* libsrc/cx16/mainargs.s +* libsrc/plus4/mainargs.s +* libsrc/lynx/mainargs.s +* libsrc/c16/mainargs.s +* libsrc/geos-common/system/mainargs.s +* libsrc/sim6502/mainargs.s +* libsrc/c128/mainargs.s +* libsrc/vic20/mainargs.s +* libsrc/nes/mainargs.s +* libsrc/atari/getargs.s +* libsrc/apple2/mainargs.s +* libsrc/cbm510/mainargs.s +* libsrc/telestrat/mainargs.s +* libsrc/c64/mainargs.s +* libsrc/pet/mainargs.s +* libsrc/atmos/mainargs.s + +## \_\_cos + +* libsrc/common/sincos.s + +## \_\_ctypeidx + +* libsrc/common/ctype.s +* libsrc/common/ctypemask.s +* libsrc/geos-common/system/ctype.s +* libsrc/atari/ctype.s +* libsrc/cbm/ctype.s +* libsrc/atmos/ctype.s +* asminc/ctype\_common.inc + +## \_\_cwd + +* libsrc/common/getcwd.s +* libsrc/common/_cwd.s +* libsrc/atari/initcwd.s +* libsrc/apple2/initcwd.s +* libsrc/apple2/initcwd.s +* libsrc/telestrat/initcwd.s +* libsrc/cbm/initcwd.s + +## \_\_cwd\_buf\_size + +* libsrc/common/_cwd.s + +## \_\_envcount + +* libsrc/common/searchenv.s +* libsrc/common/_environ.s +* libsrc/common/putenv.s +* libsrc/common/getenv.s + +## \_\_environ + +* libsrc/common/searchenv.s +* libsrc/common/_environ.s +* libsrc/common/putenv.s +* libsrc/common/getenv.s + +## \_\_envsize + +* libsrc/common/_environ.s +* libsrc/common/putenv.s + +## \_\_fdesc + +* libsrc/common/_fdesc.s +* libsrc/common/fopen.s + +## \_\_filetab + +* libsrc/common/_fdesc.s +* libsrc/common/_file.s +* asminc/_file.inc + +## \_\_fopen + +* libsrc/common/fopen.s +* libsrc/common/_fopen.s + +## \_\_printf + +* libsrc/common/vsnprintf.s +* libsrc/common/_printf.s +* libsrc/common/vfprintf.s +* libsrc/conio/vcprintf.s +* libsrc/pce/_printf.s + +## \_\_scanf + +* libsrc/common/_scanf.inc +* libsrc/common/vsscanf.s +* libsrc/conio/vcscanf.s + +## \_\_sin + +* libsrc/common/sincos.s + +## \_\_sys + +* libsrc/common/_sys.s +* libsrc/apple2/_sys.s + +## \_\_sys\_oserrlist + +* libsrc/common/stroserr.s +* libsrc/geos-common/system/oserrlist.s +* libsrc/atari/oserrlist.s +* libsrc/apple2/oserrlist.s +* libsrc/cbm/oserrlist.s +* libsrc/atmos/oserrlist.s + +## \_\_syschdir + +* libsrc/common/chdir.s +* libsrc/atari/syschdir.s +* libsrc/apple2/syschdir.s +* libsrc/telestrat/syschdir.s +* libsrc/cbm/syschdir.s + +## \_\_sysmkdir + +* libsrc/common/mkdir.s +* libsrc/atari/sysmkdir.s +* libsrc/apple2/sysmkdir.s +* libsrc/telestrat/sysmkdir.s + +## \_\_sysremove + +* libsrc/common/remove.s +* libsrc/geos-common/file/sysremove.s +* libsrc/atari/sysremove.s +* libsrc/atari/sysrmdir.s +* libsrc/apple2/sysremove.s +* libsrc/apple2/sysrmdir.s +* libsrc/telestrat/sysremove.s +* libsrc/cbm/sysremove.s + +## \_\_sysrename + +* libsrc/common/rename.s +* libsrc/geos-common/file/sysrename.s +* libsrc/atari/sysrename.s +* libsrc/apple2/sysrename.s +* libsrc/cbm/sysrename.s + +## \_\_sysrmdir + +* libsrc/common/rmdir.s +* libsrc/atari/sysrmdir.s +* libsrc/apple2/sysrmdir.s + +\_\_sysuname + +* libsrc/common/uname.s +* libsrc/cbm610/sysuname.s +* libsrc/cx16/sysuname.s +* libsrc/plus4/sysuname.s +* libsrc/lynx/sysuname.s +* libsrc/c16/sysuname.s +* libsrc/geos-common/system/sysuname.s +* libsrc/c128/sysuname.s +* libsrc/creativision/sysuname.s +* libsrc/vic20/sysuname.s +* libsrc/nes/sysuname.s +* libsrc/atari/sysuname.s +* libsrc/apple2/sysuname.s +* libsrc/cbm510/sysuname.s +* libsrc/telestrat/sysuname.s +* libsrc/c64/sysuname.s +* libsrc/pet/sysuname.s +* libsrc/atari5200/sysuname.s +* libsrc/atmos/sysuname.s + +# apple2 + +## \_\_auxtype + +* libsrc/apple2/open.s + +## \_\_datetime + +* libsrc/apple2/open.s + +## \_\_dos\_type + +* libsrc/apple2/dioopen.s +* libsrc/apple2/curdevice.s +* libsrc/apple2/mainargs.s +* libsrc/apple2/settime.s +* libsrc/apple2/getdevice.s +* libsrc/apple2/dosdetect.s +* libsrc/apple2/irq.s +* libsrc/apple2/open.s +* libsrc/apple2/mli.s +* libsrc/apple2/getres.s + +## \_\_filetype + +* libsrc/apple2/open.s +* libsrc/apple2/exehdr.s + +## atari + +## \_\_defdev + +* libsrc/atari/posixdirent.s +* libsrc/atari/ucase\_fn.s +* libsrc/atari/getdefdev.s + +## \_\_dos\_type + +* libsrc/atari/getargs.s +* libsrc/atari/exec.s +* libsrc/atari/settime.s +* libsrc/atari/syschdir.s +* libsrc/atari/dosdetect.s +* libsrc/atari/is\_cmdline\_dos.s +* libsrc/atari/sysrmdir.s +* libsrc/atari/gettime.s +* libsrc/atari/lseek.s +* libsrc/atari/getres.s +* libsrc/atari/getdefdev.s + +## \_\_do\_oserror + +* libsrc/atari/posixdirent.s +* libsrc/atari/do\_oserr.s +* libsrc/atari/serref.s +* libsrc/atari/read.s +* libsrc/atari/write.s +* libsrc/atari/close.s + +## \_\_getcolor + +* libsrc/atari/setcolor.s + +## \_\_getdefdev + +* libsrc/atari/getdefdev.s + +## \_\_graphics + +* libsrc/atari/graphics.s + +## \_\_inviocb + +* libsrc/atari/serref.s +* libsrc/atari/ser/atrrdev.s +* libsrc/atari/inviocb.s +* libsrc/atari/read.s +* libsrc/atari/write.s +* libsrc/atari/lseek.s +* libsrc/atari/close.s + +## \_\_is\_cmdline\_dos + +* libsrc/atari/is\_cmdline\_dos.s +* libsrc/atari/doesclrscr.s + +## \_\_rest\_vecs + +* libsrc/atari/savevec.s + +## \_\_rwsetup + +* libsrc/atari/rwcommon.s +* libsrc/atari/read.s +* libsrc/atari/write.s + +## \_\_save\_vecs + +* libsrc/atari/savevec.s + +## \_\_scroll + +* libsrc/atari/scroll.s + +## \_\_setcolor + +* libsrc/atari/setcolor.s + +## \_\_setcolor\_low + +* libsrc/atari/setcolor.s + +## \_\_sio\_call + +* libsrc/atari/diowritev.s +* libsrc/atari/diopncls.s +* libsrc/atari/siocall.s +* libsrc/atari/diowrite.s +* libsrc/atari/dioread.s + +# cbm + +## \_\_cbm\_filetype + +* libsrc/cbm/cbm\_filetype.s +* asminc/cbm\_filetype.in + +## \_\_dirread + +* libsrc/cbm/dir.inc +* libsrc/cbm/dir.s + +## \_\_dirread1 + +* libsrc/cbm/dir.inc +* libsrc/cbm/dir.s + +# lynx + +## \_\_iodat + +* libsrc/lynx/lynx-cart.s +* libsrc/lynx/bootldr.s +* libsrc/lynx/extzp.s +* libsrc/lynx/crt0.s +* libsrc/lynx/extzp.inc + +## \_\_iodir + +* libsrc/lynx/extzp.s +* libsrc/lynx/crt0.s +* libsrc/lynx/extzp.inc + +## \_\_sprsys + +* libsrc/lynx/tgi/lynx-160-102-16.s +* libsrc/lynx/extzp.s +* libsrc/lynx/crt0.s +* libsrc/lynx/extzp.inc + +## \_\_viddma + +* libsrc/lynx/tgi/lynx-160-102-16.s +* libsrc/lynx/extzp.s +* libsrc/lynx/crt0.s +* libsrc/lynx/extzp.inc + +# pce + +## \_\_nmi + +* libsrc/pce/irq.s +* libsrc/pce/crt0.s diff --git a/libsrc/apple2/allow_lowercase.s b/libsrc/apple2/allow_lowercase.s new file mode 100644 index 000000000..648276b4c --- /dev/null +++ b/libsrc/apple2/allow_lowercase.s @@ -0,0 +1,23 @@ +; +; Oliver Schmidt, 2024-08-06 +; +; unsigned char __fastcall__ allow_lowercase (unsigned char onoff); +; + + .export _allow_lowercase + .import uppercasemask, return0, return1 + +_allow_lowercase: + tax + lda values,x + ldx uppercasemask + sta uppercasemask + cpx #$FF + beq :+ + jmp return0 +: jmp return1 + + .rodata + +values: .byte $DF ; Force uppercase + .byte $FF ; Keep lowercase diff --git a/libsrc/apple2/beep.s b/libsrc/apple2/beep.s new file mode 100644 index 000000000..84d132a3a --- /dev/null +++ b/libsrc/apple2/beep.s @@ -0,0 +1,20 @@ +; +; Colin Leroy-Mira, 2024 +; +; void beep(void) +; + + .export _beep + + .include "apple2.inc" + + .segment "LOWCODE" + +_beep: + ; Switch in ROM and call BELL + bit $C082 + jsr $FF3A ; BELL + + ; Switch in LC bank 2 for R/O and return + bit $C080 + rts diff --git a/libsrc/apple2/callmain.s b/libsrc/apple2/callmain.s new file mode 100644 index 000000000..71a8b5611 --- /dev/null +++ b/libsrc/apple2/callmain.s @@ -0,0 +1,75 @@ +; +; Ullrich von Bassewitz, 2003-03-07 +; +; Push arguments and call main() +; + + + .export callmain, _exit + .export __argc, __argv + + .import _main, pushax, done, donelib + .import zpsave, rvsave, reset + + .include "zeropage.inc" + .include "apple2.inc" + + +;--------------------------------------------------------------------------- +; Setup the stack for main(), then jump to it + +callmain: + lda __argc + ldx __argc+1 + jsr pushax ; Push argc + + lda __argv + ldx __argv+1 + jsr pushax ; Push argv + + ldy #4 ; Argument size + jsr _main + + ; Avoid a re-entrance of donelib. This is also the exit() entry. +_exit: ldx #<exit + lda #>exit + jsr reset ; Setup RESET vector + + ; Switch in LC bank 2 for R/O in case it was switched out by a RESET. + bit $C080 + + ; Call the module destructors. + jsr donelib + + ; Switch in ROM. + bit $C082 + + ; Restore the original RESET vector. +exit: ldx #$02 +: lda rvsave,x + sta SOFTEV,x + dex + bpl :- + + ; Copy back the zero-page stuff. + ldx #zpspace-1 +: lda zpsave,x + sta sp,x + dex + bpl :- + + ; ProDOS TechRefMan, chapter 5.2.1: + ; "System programs should set the stack pointer to $FF at the + ; warm-start entry point." + ldx #$FF + txs ; Re-init stack pointer + + ; We're done + jmp done + +;--------------------------------------------------------------------------- +; Data + +.data +__argc: .word 0 +__argv: .addr 0 diff --git a/libsrc/apple2/cgetc.s b/libsrc/apple2/cgetc.s index ecce9f9de..d2ebe5db8 100644 --- a/libsrc/apple2/cgetc.s +++ b/libsrc/apple2/cgetc.s @@ -9,6 +9,7 @@ .export _cgetc .import cursor, putchardirect + .include "zeropage.inc" .include "apple2.inc" _cgetc: @@ -22,7 +23,7 @@ _cgetc: .else lda #' ' | $40 ; Blank, flashing .endif - jsr putchardirect ; Returns old character in X + jsr putchardirect ; Saves old character in tmp3 ; Wait for keyboard strobe. : inc RNDL ; Increment random counter low @@ -37,7 +38,7 @@ _cgetc: ; Restore old character. pha - txa + lda tmp3 jsr putchardirect pla diff --git a/libsrc/apple2/closedir.s b/libsrc/apple2/closedir.s new file mode 100644 index 000000000..1f176092b --- /dev/null +++ b/libsrc/apple2/closedir.s @@ -0,0 +1,35 @@ +; +; Colin Leroy-Mira <colin@colino.net>, 2024 +; +; int __fastcall__ closedir (DIR *dir) +; + + .export _closedir, closedir_ptr1 + + .import _close + .import _free + .import pushax, popax, pushptr1, swapstk + + .importzp ptr1 + + .include "apple2.inc" + .include "dir.inc" + .include "errno.inc" + .include "fcntl.inc" + .include "zeropage.inc" + +_closedir: + sta ptr1 + stx ptr1+1 +closedir_ptr1: + ; Close fd + jsr pushptr1 ; Backup ptr1 + ldy #$00 + lda (ptr1),y ; Get fd + ldx #$00 + jsr _close + jsr swapstk ; Store result, pop ptr1 + + ; Free dir structure + jsr _free + jmp popax ; Return result diff --git a/libsrc/apple2/cpeekc.s b/libsrc/apple2/cpeekc.s index a547f7867..a6a082eab 100644 --- a/libsrc/apple2/cpeekc.s +++ b/libsrc/apple2/cpeekc.s @@ -11,18 +11,23 @@ _cpeekc: ldy CH .ifdef __APPLE2ENH__ + sec ; Assume main memory bit RD80VID ; In 80 column mode? bpl peek ; No, just go ahead - tya + lda OURCH lsr ; Div by 2 tay bcs peek ; Odd cols are in main memory + php + sei ; No valid MSLOT et al. in aux memory bit HISCR ; Assume SET80COL .endif peek: lda (BASL),Y ; Get character .ifdef __APPLE2ENH__ - bit LOWSCR ; Doesn't hurt in 40 column mode - .endif + bcs :+ ; In main memory + bit LOWSCR + plp +: .endif eor #$80 ; Invert high bit - ldx #$00 + ldx #>$0000 rts diff --git a/libsrc/apple2/cputc.s b/libsrc/apple2/cputc.s index 30383fcfe..fdf799357 100644 --- a/libsrc/apple2/cputc.s +++ b/libsrc/apple2/cputc.s @@ -11,9 +11,15 @@ .export _cputcxy, _cputc .export cputdirect, newline, putchar, putchardirect .import gotoxy, VTABZ + .ifndef __APPLE2ENH__ + .import uppercasemask + .endif + .include "zeropage.inc" .include "apple2.inc" + .macpack cpu + .segment "ONCE" .ifdef __APPLE2ENH__ @@ -33,7 +39,7 @@ _cputcxy: pla ; Restore C and run into _cputc _cputc: - cmp #$0D ; Test for \r = carrage return + cmp #$0D ; Test for \r = carriage return beq left cmp #$0A ; Test for \n = line feed beq newline @@ -41,19 +47,34 @@ _cputc: .ifndef __APPLE2ENH__ cmp #$E0 ; Test for lowercase bcc cputdirect - and #$DF ; Convert to uppercase + and uppercasemask .endif cputdirect: jsr putchar + .ifdef __APPLE2ENH__ + bit RD80VID ; In 80 column mode? + bpl :+ + inc OURCH ; Bump to next column + lda OURCH + bra check ; Must leave CH alone +: .endif inc CH ; Bump to next column lda CH - cmp WNDWDTH - bcc :+ +check: cmp WNDWDTH + bcc done jsr newline -left: lda #$00 ; Goto left edge of screen +left: + .ifdef __APPLE2ENH__ + stz CH ; Goto left edge of screen + bit RD80VID ; In 80 column mode? + bpl done + stz OURCH ; Goto left edge of screen + .else + lda #$00 ; Goto left edge of screen sta CH -: rts + .endif +done: rts newline: inc CV ; Bump to next line @@ -77,22 +98,27 @@ putchar: mask: and INVFLG ; Apply normal, inverse, flash putchardirect: - pha + tax ldy CH .ifdef __APPLE2ENH__ + sec ; Assume main memory bit RD80VID ; In 80 column mode? bpl put ; No, just go ahead - tya + lda OURCH lsr ; Div by 2 tay bcs put ; Odd cols go in main memory + php + sei ; No valid MSLOT et al. in aux memory bit HISCR ; Assume SET80COL .endif put: lda (BASL),Y ; Get current character - tax ; Return old character for _cgetc - pla + sta tmp3 ; Save old character for _cgetc + txa sta (BASL),Y .ifdef __APPLE2ENH__ - bit LOWSCR ; Doesn't hurt in 40 column mode - .endif + bcs :+ ; In main memory + bit LOWSCR + plp +: .endif rts diff --git a/libsrc/apple2/crt0.s b/libsrc/apple2/crt0.s index 60a8516d1..628303687 100644 --- a/libsrc/apple2/crt0.s +++ b/libsrc/apple2/crt0.s @@ -4,11 +4,13 @@ ; Startup code for cc65 (Apple2 version) ; - .export _exit, done, return + .export done, return + .export zpsave, rvsave, reset .export __STARTUP__ : absolute = 1 ; Mark as startup - .import initlib, donelib + .import initlib, _exit .import zerobss, callmain + .import bltu2 .import __ONCE_LOAD__, __ONCE_SIZE__ ; Linker generated .import __LC_START__, __LC_LAST__ ; Linker generated @@ -33,41 +35,7 @@ jsr zerobss ; Push the command-line arguments; and, call main(). - jsr callmain - - ; Avoid a re-entrance of donelib. This is also the exit() entry. -_exit: ldx #<exit - lda #>exit - jsr reset ; Setup RESET vector - - ; Switch in ROM, in case it wasn't already switched in by a RESET. - bit $C082 - - ; Call the module destructors. - jsr donelib - - ; Restore the original RESET vector. -exit: ldx #$02 -: lda rvsave,x - sta SOFTEV,x - dex - bpl :- - - ; Copy back the zero-page stuff. - ldx #zpspace-1 -: lda zpsave,x - sta sp,x - dex - bpl :- - - ; ProDOS TechRefMan, chapter 5.2.1: - ; "System programs should set the stack pointer to $FF at the - ; warm-start entry point." - ldx #$FF - txs ; Re-init stack pointer - - ; We're done - jmp done + jmp callmain ; ------------------------------------------------------------------------ @@ -126,6 +94,7 @@ basic: lda HIMEM ; Call the module constructors. jsr initlib + ; Copy the LC segment to its destination ; Switch in LC bank 2 for W/O. bit $C081 bit $C081 @@ -153,7 +122,7 @@ basic: lda HIMEM ; Call into Applesoft Block Transfer Up -- which handles zero- ; sized blocks well -- to move the content of the LC memory area. - jsr $D39A ; BLTU2 + jsr bltu2 ; Switch in LC bank 2 for R/O and return. bit $C080 diff --git a/libsrc/apple2/curdevice.s b/libsrc/apple2/curdevice.s index 9781b8ad0..a450c605f 100644 --- a/libsrc/apple2/curdevice.s +++ b/libsrc/apple2/curdevice.s @@ -23,5 +23,5 @@ _getcurrentdevice: bne :+ lda #$FF ; INVALID_DEVICE -: ldx #$00 +: ldx #>$0000 rts diff --git a/libsrc/apple2/detect_iigs.s b/libsrc/apple2/detect_iigs.s new file mode 100644 index 000000000..f82a464ac --- /dev/null +++ b/libsrc/apple2/detect_iigs.s @@ -0,0 +1,17 @@ +; +; Colin Leroy-Mira <colin@colino.net>, 2024 +; +; void __fastcall__ detect_iigs(void) +; + + .export _detect_iigs + .import ostype, return0, return1 + + .include "apple2.inc" + + ; Returns 1 if running on IIgs, 0 otherwise +_detect_iigs: + lda ostype + bpl :+ + jmp return1 +: jmp return0 diff --git a/libsrc/apple2/diocommon.s b/libsrc/apple2/diocommon.s index b18f0e6ef..6870ebb51 100644 --- a/libsrc/apple2/diocommon.s +++ b/libsrc/apple2/diocommon.s @@ -31,5 +31,5 @@ diocommon: dioepilog: ; Return success or error sta ___oserror - ldx #$00 + ldx #>$0000 rts diff --git a/libsrc/apple2/dir.inc b/libsrc/apple2/dir.inc new file mode 100644 index 000000000..545ae003b --- /dev/null +++ b/libsrc/apple2/dir.inc @@ -0,0 +1,15 @@ +.struct DIR + FD .word + ENTRY_LENGTH .byte + ENTRIES_PER_BLOCK .byte + FILE_COUNT .word + CURRENT_ENTRY .byte + .union + BYTES .byte 512 + .struct CONTENT + PREV_BLOCK .word + NEXT_BLOCK .word + ENTRIES .byte + .endstruct + .endunion +.endstruct diff --git a/libsrc/apple2/dir_entry_count.s b/libsrc/apple2/dir_entry_count.s new file mode 100644 index 000000000..6a80bef28 --- /dev/null +++ b/libsrc/apple2/dir_entry_count.s @@ -0,0 +1,24 @@ +; +; Colin Leroy-Mira <colin@colino.net>, 2024 +; +; unsigned int __fastcall__ dir_entry_count(DIR *dir); +; + + .export _dir_entry_count + + .importzp ptr1 + + .include "apple2.inc" + .include "dir.inc" + +.proc _dir_entry_count + sta ptr1 + stx ptr1+1 + + ldy #DIR::FILE_COUNT + 1 + lda (ptr1),y + tax + dey + lda (ptr1),y + rts +.endproc diff --git a/libsrc/apple2/exec.s b/libsrc/apple2/exec.s index d5cbf8788..ec90f19bb 100644 --- a/libsrc/apple2/exec.s +++ b/libsrc/apple2/exec.s @@ -5,8 +5,8 @@ ; .export _exec - .import pushname, popname - .import popax, done, _exit + .import mli_file_info_direct + .import pushname, popname, popax, done, _exit .include "zeropage.inc" .include "errno.inc" @@ -17,13 +17,12 @@ typerr: lda #$4A ; "Incompatible file format" ; Cleanup name -oserr: jsr popname ; Preserves A - ; Set ___oserror - jmp ___mappederrno +mlierr: jsr popname +oserr: jmp ___mappederrno _exec: - ; Save cmdline + ; Store cmdline sta ptr4 stx ptr4+1 @@ -32,6 +31,9 @@ _exec: jsr pushname bne oserr + jsr mli_file_info_direct + bcs mlierr + ; ProDOS TechRefMan, chapter 5.1.5.1: ; "The complete or partial pathname of the system program ; is stored at $280, starting with a length byte." @@ -46,18 +48,6 @@ _exec: dey bpl :- - ; Set pushed name - lda sp - ldx sp+1 - sta mliparam + MLI::INFO::PATHNAME - stx mliparam + MLI::INFO::PATHNAME+1 - - ; Get file_type and aux_type - lda #GET_INFO_CALL - ldx #GET_INFO_COUNT - jsr callmli - bcs oserr - ; If we get here the program file at least exists so we copy ; the loader stub right now and patch it later to set params ldx #size - 1 @@ -121,35 +111,9 @@ setbuf: lda #$00 ; Low byte dex dex - ; Set I/O buffer - sta mliparam + MLI::OPEN::IO_BUFFER - stx mliparam + MLI::OPEN::IO_BUFFER+1 - - ; PATHNAME already set - .assert MLI::OPEN::PATHNAME = MLI::INFO::PATHNAME, error - - ; Lower file level to avoid program file - ; being closed by C library shutdown code - ldx LEVEL - stx level - beq :+ - dec LEVEL - - ; Open file -: lda #OPEN_CALL - ldx #OPEN_COUNT - jsr callmli - - ; Restore file level - ldx level - stx LEVEL - bcc :+ - jmp oserr - - ; Get and save fd -: lda mliparam + MLI::OPEN::REF_NUM - sta read_ref - sta close_ref + ; Set OPEN MLI call I/O buffer parameter + sta io_buffer + stx io_buffer+1 .ifdef __APPLE2ENH__ ; Calling the 80 column firmware needs the ROM switched @@ -194,14 +158,25 @@ setbuf: lda #$00 ; Low byte ; Initiate C library shutdown jmp _exit - .bss - -level : .res 1 - .rodata +source: + ; Open program file + ; PATHNAME parameter is already set (we reuse + ; the copy at $0280); IO_BUFFER has been setup + ; before shutting down the C library + jsr $BF00 + .byte OPEN_CALL + .word open_param + bcs error + + ; Copy REF_NUM to MLI READ and CLOSE parameters + lda open_ref + sta read_ref + sta close_ref + ; Read whole program file -source: jsr $BF00 + jsr $BF00 .byte READ_CALL .word read_param bcs error @@ -213,8 +188,6 @@ source: jsr $BF00 bcs error ; Check for cmdline handling - lda $0100 ; Valid cmdline? - beq jump ; No, jump to program right away ldx file_type ; SYS file? bne system ; Yes, check for startup filename @@ -256,6 +229,14 @@ jump: jmp (data_buffer) file_type = * - source + target .byte $00 +open_param = * - source + target + .byte $03 ; PARAM_COUNT + .addr $0280 ; PATHNAME +io_buffer = * - source + target + .addr $0000 ; IO_BUFFER +open_ref = * - source + target + .byte $00 ; REF_NUM + read_param = * - source + target .byte $04 ; PARAM_COUNT read_ref = * - source + target @@ -287,4 +268,8 @@ size = * - source target = DOSWARM - size + ; Make sure that the loader isn't too big, and + ; fits in $300-$3D0 + .assert target >= $300, error + dosvec: jmp quit diff --git a/libsrc/apple2/extra/integer-basic-compat.s b/libsrc/apple2/extra/integer-basic-compat.s new file mode 100644 index 000000000..c4dc8d8ef --- /dev/null +++ b/libsrc/apple2/extra/integer-basic-compat.s @@ -0,0 +1,33 @@ +; +; Colin Leroy-Mira, 06.03.2025 +; +; Copy the LC segment from the end of the binary to the Language Card +; using _memcpy. This allows running apple2 programs on the original +; Integer ROM Apple ][. +; + + .export bltu2 + + .import _memcpy, pushax + .import __ONCE_LOAD__, __ONCE_SIZE__ ; Linker generated + .import __LC_START__, __LC_LAST__ ; Linker generated + + .segment "ONCE" + +bltu2: + ; Get the destination start address. + lda #<__LC_START__ + ldx #>__LC_START__ + jsr pushax + + ; Get the source start address. + lda #<(__ONCE_LOAD__ + __ONCE_SIZE__) + ldx #>(__ONCE_LOAD__ + __ONCE_SIZE__) + jsr pushax + + ; Set the length + lda #<(__LC_LAST__ - __LC_START__) + ldx #>(__LC_LAST__ - __LC_START__) + + ; And do the copy + jmp _memcpy diff --git a/libsrc/apple2/filename.s b/libsrc/apple2/filename.s index aaef6ec2d..0d4b6bedd 100644 --- a/libsrc/apple2/filename.s +++ b/libsrc/apple2/filename.s @@ -8,6 +8,7 @@ .import subysp, addysp, decsp1 .include "zeropage.inc" + .include "apple2.inc" .include "mli.inc" pushname: @@ -15,7 +16,7 @@ pushname: stx ptr1+1 ; Alloc pathname buffer - ldy #64+1 ; Max pathname length + zero + ldy #FILENAME_MAX jsr subysp ; Check for full pathname @@ -71,14 +72,14 @@ copy: lda (ptr1),y sta (sp),y beq setlen iny - cpy #64+1 ; Max pathname length + zero + cpy #FILENAME_MAX bcc copy ; Load oserror code lda #$40 ; "Invalid pathname" ; Free pathname buffer -addsp65:ldy #64+1 +addsp65:ldy #FILENAME_MAX bne addsp ; Branch always ; Alloc and set length byte @@ -93,5 +94,5 @@ setlen: tya popname: ; Cleanup stack - ldy #1 + 64+1 ; Length byte + max pathname length + zero -addsp: jmp addysp ; Preserves A + ldy #1 + FILENAME_MAX +addsp: jmp addysp ; Preserves A and X diff --git a/libsrc/apple2/get_iigs_speed.s b/libsrc/apple2/get_iigs_speed.s new file mode 100644 index 000000000..b960516fa --- /dev/null +++ b/libsrc/apple2/get_iigs_speed.s @@ -0,0 +1,22 @@ +; +; Colin Leroy-Mira <colin@colino.net>, 2024 +; +; unsigned char __fastcall__ get_iigs_speed(void) +; + + .export _get_iigs_speed + .import ostype, return0 + + .include "apple2.inc" + .include "accelerator.inc" + +_get_iigs_speed: + lda ostype ; Return SLOW if not IIgs + bpl :+ + lda CYAREG ; Check current setting + bpl :+ + lda #SPEED_FAST + ldx #>$0000 + rts + .assert SPEED_SLOW = 0, error +: jmp return0 ; SPEED_SLOW diff --git a/libsrc/apple2/get_ostype.s b/libsrc/apple2/get_ostype.s index a1b1eb5be..ea9ff25cc 100644 --- a/libsrc/apple2/get_ostype.s +++ b/libsrc/apple2/get_ostype.s @@ -5,7 +5,7 @@ ; .constructor initostype, 9 - .export _get_ostype + .export _get_ostype, ostype ; Identify machine according to: ; Apple II Miscellaneous TechNote #7, Apple II Family Identification diff --git a/libsrc/apple2/get_tv.s b/libsrc/apple2/get_tv.s new file mode 100644 index 000000000..830cc4ac1 --- /dev/null +++ b/libsrc/apple2/get_tv.s @@ -0,0 +1,189 @@ +; +; Colin Leroy-Mira <colin@colino.net>, 2025 +; +; unsigned char __fastcall__ get_tv(void) +; + .export _get_tv + + .import _set_iigs_speed, _get_iigs_speed + .import ostype + + .constructor calibrate_tv, 2 + + .include "accelerator.inc" + .include "apple2.inc" + .include "get_tv.inc" + + .segment "ONCE" + +; Cycle wasters +waste_72: + jsr waste_36 +waste_36: + jsr waste_12 +waste_24: + jsr waste_12 +waste_12: + rts + +.proc calibrate_tv + lda ostype + bmi iigs + cmp #$20 + bcc iip + cmp #$40 + bcc iie + +iic: jmp calibrate_iic +iigs: jmp calibrate_iigs +iie: jmp calibrate_iie +iip: rts ; Keep TV::OTHER. +.endproc + + +; Magic numbers +WASTE_LOOP_CYCLES = 92 ; The wait loop total cycles +NTSC_LOOP_COUNT = 17030/WASTE_LOOP_CYCLES ; How many loops expected on NTSC +PAL_LOOP_COUNT = 20280/WASTE_LOOP_CYCLES ; How many loops expected on PAL +STOP_PTRIG = 16500/WASTE_LOOP_CYCLES ; Stop PTRIG at 16.5ms + +; Carry set at enter: wait for VBL + +; Carry clear at enter: wait for VBL - +; Increments X every 92 cycles. +.proc count_until_vbl_bit + lda #$10 ; BPL + bcc :+ + lda #$30 ; BMI +: sta sign + + ; Wait for VBLsign change with 92 cycles loops. + ; Hit PTRIG repeatedly so that accelerators will slow down. + ; But stop hitting PTRIG after 16.5ms cycles, so that on the //c, + ; the VBLINT will not be reset right before we get it. 16.5ms + ; is a good value because it's far enough from 17ms for NTSC + ; models, and close enough to 20.2ms for PAL models that accelerators + ; will stay slow until there. (5ms usually). + +: cpx #STOP_PTRIG ; 2 - see if we spent 16.5ms already + bcs notrig ; 4 / 5 - if so, stop hitting PTRIG + sta PTRIG ; 8 - otherwise hit it + bcc count ; 11 +notrig: + nop ; 7 - keep cycle count constant when not + nop ; 9 - hitting PTRIG + nop ; 11 +count: + inx ; 13 + jsr waste_72 ; 85 + bit RDVBLBAR ; 89 - Wait for VBL change +sign: + bpl :- ; 92 - patched with bpl/bmi + rts +.endproc + +.proc calibrate_iic + php + sei + + sta IOUDISOFF + lda RDVBLMSK + pha ; Back up for cleanup + + bit ENVBL + bit PTRIG ; Reset VBL interrupt flag +: bit RDVBLBAR ; Wait for one VBL + bpl :- + + bit PTRIG ; Reset VBL interrupt flag again + ldx #$00 + clc + jsr count_until_vbl_bit + + pla ; Cleanup + asl + bcs :+ ; VBL interrupts were already enabled + bit DISVBL +: sta IOUDISON ; IIc Tech Ref Man: The firmware normally leaves IOUDIS on. + + plp + jmp calibrate_done +.endproc + +.proc calibrate_iie +: bit RDVBLBAR ; Wait for bit 7 to be off (VBL start) + bmi :- +: bit RDVBLBAR ; Wait for bit 7 to be on (VBL end) + bpl :- + + ; Wait and count during a full cycle + ldx #$00 + sec + jsr count_until_vbl_bit + clc + jsr count_until_vbl_bit + + jmp calibrate_done +.endproc + +.proc calibrate_iigs + ; Backup speed and slow down + jsr _get_iigs_speed + pha + lda #SPEED_SLOW + jsr _set_iigs_speed + + ; The same as IIe, but reverted, because... something? +: bit RDVBLBAR ; Wait for bit 7 to be on (VBL start) + bpl :- +: bit RDVBLBAR ; Wait for bit 7 to be off (VBL end) + bmi :- + + ; Wait and count during a full cycle + ldx #$00 + clc + jsr count_until_vbl_bit + sec + jsr count_until_vbl_bit + + jsr calibrate_done + + ; Restore user speed + pla + jmp _set_iigs_speed +.endproc + +.proc calibrate_done + ; Consider X +/- 3 to be valid, + ; anything else is unknown. + + lda #TV::NTSC + cpx #NTSC_LOOP_COUNT-3 + bcc unexpected + cpx #NTSC_LOOP_COUNT+3 + bcc matched + + lda #TV::PAL + cpx #PAL_LOOP_COUNT-3 + bcc unexpected + cpx #PAL_LOOP_COUNT+3 + bcs unexpected + +matched: + sta tv + +unexpected: + rts +.endproc + + .code + +; The only thing remaining from that code after init +.proc _get_tv + lda tv + ldx #>$0000 + rts +.endproc + + .segment "INIT" + +tv: .byte TV::OTHER diff --git a/libsrc/apple2/getdevice.s b/libsrc/apple2/getdevice.s index 0c674cad0..f3b0d5a86 100644 --- a/libsrc/apple2/getdevice.s +++ b/libsrc/apple2/getdevice.s @@ -30,5 +30,5 @@ next: inx bne next done: txa - ldx #$00 + ldx #>$0000 rts diff --git a/libsrc/apple2/gettime.s b/libsrc/apple2/gettime.s index 7467b0189..829aaab3b 100644 --- a/libsrc/apple2/gettime.s +++ b/libsrc/apple2/gettime.s @@ -4,7 +4,8 @@ ; int __fastcall__ clock_gettime (clockid_t clk_id, struct timespec *tp); ; - .import pushax, steaxspidx, incsp1, incsp3, return0 + .import pushax, incsp1, incsp3, steaxspidx, return0 + .import _mktime_dt .include "time.inc" .include "zeropage.inc" @@ -29,42 +30,12 @@ _clock_gettime: jsr callmli bcs oserr - ; Get date - lda DATELO+1 - lsr - php ; Save month msb - cmp #70 ; Year < 70? - bcs :+ ; No, leave alone - adc #100 ; Move 19xx to 20xx -: sta TM + tm::tm_year - lda DATELO - tax ; Save day - plp ; Restore month msb - ror - lsr - lsr - lsr - lsr - beq erange ; [1..12] allows for validity check - tay - dey ; Move [1..12] to [0..11] - sty TM + tm::tm_mon - txa ; Restore day - and #%00011111 - sta TM + tm::tm_mday + ; Convert DATELO/TIMELO to time_t + lda #<DATELO + ldx #>DATELO + jsr _mktime_dt - ; Get time - lda TIMELO+1 - sta TM + tm::tm_hour - lda TIMELO - sta TM + tm::tm_min - - ; Make time_t - lda #<TM - ldx #>TM - jsr _mktime - - ; Store tv_sec + ; Store ldy #timespec::tv_sec jsr steaxspidx @@ -74,21 +45,8 @@ _clock_gettime: ; Return success jmp return0 - ; Load errno code -erange: lda #ERANGE - - ; Cleanup stack - jsr incsp3 ; Preserves A - - ; Set __errno - jmp ___directerrno - ; Cleanup stack oserr: jsr incsp3 ; Preserves A ; Set ___oserror jmp ___mappederrno - - .bss - -TM: .tag tm diff --git a/libsrc/apple2/gmtime_dt.s b/libsrc/apple2/gmtime_dt.s new file mode 100644 index 000000000..a0b8e9f4d --- /dev/null +++ b/libsrc/apple2/gmtime_dt.s @@ -0,0 +1,73 @@ +; +; Oliver Schmidt, 14.08.2018 +; Colin Leroy-Mira, 2023 <colin@colino.net> +; +; struct tm * __fastcall__ gmtime_dt(const struct datetime *dt) +; + + .export _gmtime_dt, tm_buf + + .include "time.inc" + .include "zeropage.inc" + .include "errno.inc" + .include "mli.inc" + + ; Convert ProDOS date/time to a struct tm + ; source date address in AX + ; on stack: + ; destination struct + +_gmtime_dt: + sta ptr1 + stx ptr1+1 + + ; Get time + ldy #$03 + lda (ptr1),y + sta tm_buf + tm::tm_hour + dey + lda (ptr1),y + sta tm_buf + tm::tm_min + + ; Get date + dey + lda (ptr1),y + lsr + php ; Save month msb + cmp #70 ; Year < 70? + bcs :+ ; No, leave alone + adc #100 ; Move 19xx to 20xx +: sta tm_buf + tm::tm_year + + dey + lda (ptr1),y + tax ; Save day + plp ; Restore month msb + ror + lsr + lsr + lsr + lsr + beq erange ; [1..12] allows for validity check + tay + dey ; Move [1..12] to [0..11] + sty tm_buf + tm::tm_mon + txa ; Restore day + and #%00011111 + sta tm_buf + tm::tm_mday + + lda #<tm_buf ; Return pointer to tm_buf + ldx #>tm_buf + rts + + ; Load errno code and return NULL +erange: lda #ERANGE + sta ___errno + lda #$00 + tax + rts + + .bss + +tm_buf: + .tag tm diff --git a/libsrc/apple2/gotoxy.s b/libsrc/apple2/gotoxy.s index 6755af8d8..2a0261eba 100644 --- a/libsrc/apple2/gotoxy.s +++ b/libsrc/apple2/gotoxy.s @@ -22,4 +22,9 @@ _gotoxy: _gotox: sta CH ; Store X + .ifdef __APPLE2ENH__ + bit RD80VID ; In 80 column mode? + bpl :+ + sta OURCH ; Store X +: .endif rts diff --git a/libsrc/apple2/initcwd.s b/libsrc/apple2/initcwd.s index 7a12bc52a..03e13bcfb 100644 --- a/libsrc/apple2/initcwd.s +++ b/libsrc/apple2/initcwd.s @@ -3,22 +3,37 @@ ; .export initcwd - .import __cwd + .import __cwd, __dos_type .include "zeropage.inc" + .include "apple2.inc" .include "mli.inc" initcwd: - ; Set static prefix buffer - lda #<__cwd - ldx #>__cwd - sta mliparam + MLI::PREFIX::PATHNAME - stx mliparam + MLI::PREFIX::PATHNAME+1 + ; Check for ProDOS 8 + lda __dos_type + beq done - ; Get current working directory - lda #GET_PREFIX_CALL - ldx #PREFIX_COUNT - jsr callmli + ; Save random counter + lda RNDL + pha + lda RNDH + pha + + ; Call MLI + ; We're not using mli.s' callmli because its + ; mliparam is in BSS and this will be called + ; before LC code is moved to the Language Card. + + jsr $BF00 ; MLI call entry point + .byte GET_PREFIX_CALL ; MLI command + .addr mli_parameters ; MLI parameter + + ; Restore random counter + pla + sta RNDH + pla + sta RNDL ; Check for null prefix ldx __cwd @@ -39,3 +54,9 @@ initcwd: sta __cwd,x done: rts + + .rodata + +mli_parameters: + .byte $01 ; Number of parameters + .addr __cwd ; Address of parameter diff --git a/libsrc/apple2/joy/a2.stdjoy.s b/libsrc/apple2/joy/a2.stdjoy.s index 11be52eb4..b5e7a311b 100644 --- a/libsrc/apple2/joy/a2.stdjoy.s +++ b/libsrc/apple2/joy/a2.stdjoy.s @@ -92,7 +92,7 @@ COUNT: bvc noiic ; Not $4x dex ; Only one joystick for the //c noiic: txa ; Number of joysticks we support - ldx #$00 + ldx #>$0000 rts ; READ routine. Read a particular joystick passed in A. @@ -170,5 +170,5 @@ nogs2: lda #$00 ; 0 0 0 0 0 0 0 0 ; Finalize eor #%00010100 ; BTN_2 BTN_1 DOWN UP RIGHT LEFT 0 0 - ldx #$00 + ldx #>$0000 rts diff --git a/libsrc/apple2/lc-copy-applesoft.s b/libsrc/apple2/lc-copy-applesoft.s new file mode 100644 index 000000000..70a04431d --- /dev/null +++ b/libsrc/apple2/lc-copy-applesoft.s @@ -0,0 +1,9 @@ +; +; Oliver Schmidt, 15.09.2009 +; +; Copy the LC segment from the end of the binary to the Language Card +; using AppleSoft's BLTU2 routine. +; + .export bltu2 + +bltu2 := $D39A diff --git a/libsrc/apple2/mktime_dt.s b/libsrc/apple2/mktime_dt.s new file mode 100644 index 000000000..415f52b9e --- /dev/null +++ b/libsrc/apple2/mktime_dt.s @@ -0,0 +1,37 @@ +; +; Oliver Schmidt, 14.08.2018 +; Colin Leroy-Mira, 2023 <colin@colino.net> +; +; time_t __fastcall__ mktime_dt(const struct datetime *dt) +; + + .import steaxspidx, pushax, incsp2, _gmtime_dt + .import tm_buf + .export _mktime_dt + + .include "time.inc" + .include "zeropage.inc" + .include "errno.inc" + .include "mli.inc" + + ; Convert ProDOS date/time to UNIX timestamp + ; source date address in AX + +_mktime_dt: + ; Convert to internal tm + jsr _gmtime_dt + cpx #$00 + bne :+ + cmp #$00 + beq err + + ; Make time_t +: lda #<tm_buf + ldx #>tm_buf + jmp _mktime + +err: lda #$00 + tax + sta sreg + sta sreg+1 + rts diff --git a/libsrc/apple2/mli.inc b/libsrc/apple2/mli.inc index 42363d9c9..382a071b0 100644 --- a/libsrc/apple2/mli.inc +++ b/libsrc/apple2/mli.inc @@ -83,8 +83,8 @@ EOF_COUNT = 2 AUX_TYPE .word STORAGE_TYPE .byte BLOCKS .word - MODE_DATE .word - MODE_TIME .word + MOD_DATE .word + MOD_TIME .word CREATE_DATE .word CREATE_TIME .word .endstruct @@ -139,3 +139,6 @@ LEVEL := $BF94 ; File level: used in open, flush, close MACHID := $BF98 ; Machine identification PFIXPTR := $BF9A ; If = 0, no prefix active KVERSION:= $BFFF ; Kernel version number + +; Max filename length +FILENAME_MAX = 64+1 diff --git a/libsrc/apple2/mli_file_info.s b/libsrc/apple2/mli_file_info.s new file mode 100644 index 000000000..16e01c07f --- /dev/null +++ b/libsrc/apple2/mli_file_info.s @@ -0,0 +1,33 @@ +; +; Colin Leroy-Mira, 2023 <colin@colino.net> +; + + .export mli_file_info + .import pushname, popname, mli_file_info_direct + .import popax + .include "zeropage.inc" + .include "errno.inc" + .include "mli.inc" + + ; Calls ProDOS MLI GET_FILE_INFO on the filename + ; stored as C string in AX at top of stack + ; Returns with carry set on error, and sets errno +mli_file_info: + ; Get pathname + jsr popax + jsr pushname + bne oserr + + jsr mli_file_info_direct + php ; Save return status + + jsr popname ; Preserves A + + plp + bcs oserr + rts + +oserr: + jsr ___mappederrno + sec + rts diff --git a/libsrc/apple2/mli_file_info_direct.s b/libsrc/apple2/mli_file_info_direct.s new file mode 100644 index 000000000..c15ebc28f --- /dev/null +++ b/libsrc/apple2/mli_file_info_direct.s @@ -0,0 +1,22 @@ +; +; Colin Leroy-Mira, 2023 <colin@colino.net> +; + + .export mli_file_info_direct + .include "zeropage.inc" + .include "mli.inc" + + ; Calls ProDOS MLI GET_FILE_INFO on the ProDOS style + ; filename stored on top of stack + ; Returns with carry set on error, and sets errno +mli_file_info_direct: + ; Set pushed name + lda sp + ldx sp+1 + sta mliparam + MLI::INFO::PATHNAME + stx mliparam + MLI::INFO::PATHNAME+1 + + ; Get file information + lda #GET_INFO_CALL + ldx #GET_INFO_COUNT + jmp callmli diff --git a/libsrc/apple2/mou/a2.stdmou.s b/libsrc/apple2/mou/a2.stdmou.s index c54c09d34..e48abbb41 100644 --- a/libsrc/apple2/mou/a2.stdmou.s +++ b/libsrc/apple2/mou/a2.stdmou.s @@ -155,6 +155,7 @@ next: inc ptr1+1 ; Disable interrupts now because setting the slot number makes ; the IRQ handler (maybe called due to some non-mouse IRQ) try ; calling the firmware which isn't correctly set up yet + php sei ; Convert to and save slot number @@ -211,7 +212,7 @@ next: inc ptr1+1 common: jsr firmware ; Enable interrupts and return success - cli + plp lda #<MOUSE_ERR_OK ldx #>MOUSE_ERR_OK rts @@ -220,6 +221,7 @@ common: jsr firmware ; No return code required (the driver is removed from memory on return). UNINSTALL: ; Hide cursor + php sei jsr CHIDE @@ -249,7 +251,8 @@ SETBOX: ; Apple II Mouse TechNote #1, Interrupt Environment with the Mouse: ; "Disable interrupts before placing position information in the ; screen holes." -: sei +: php + sei ; Set low clamp lda (ptr1),y @@ -298,6 +301,7 @@ GETBOX: ; the screen). No return code required. MOVE: ldy slot + php sei ; Set y @@ -328,9 +332,10 @@ MOVE: ; no special action is required besides hiding the mouse cursor. ; No return code required. HIDE: + php sei jsr CHIDE - cli + plp rts ; SHOW: Is called to show the mouse cursor. The mouse kernel manages a @@ -339,15 +344,16 @@ HIDE: ; no special action is required besides enabling the mouse cursor. ; No return code required. SHOW: + php sei jsr CSHOW - cli + plp rts ; BUTTONS: Return the button mask in A/X. BUTTONS: lda info + MOUSE_INFO::BUTTONS - ldx #$00 + ldx #>$0000 rts ; POS: Return the mouse position in the MOUSE_POS struct pointed to by ptr1. @@ -360,12 +366,13 @@ POS: ; struct pointed to by ptr1. No return code required. INFO: ldy #.sizeof(MOUSE_INFO)-1 -copy: sei +copy: php + sei : lda info,y sta (ptr1),y dey bpl :- - cli + plp rts ; IOCTL: Driver defined entry point. The wrapper will pass a pointer to ioctl diff --git a/libsrc/apple2/open.s b/libsrc/apple2/open.s index 68c203cd6..7ece7f18d 100644 --- a/libsrc/apple2/open.s +++ b/libsrc/apple2/open.s @@ -18,6 +18,7 @@ .include "fcntl.inc" .include "mli.inc" .include "filedes.inc" + .include "time.inc" .segment "ONCE" @@ -208,7 +209,7 @@ done: lda tmp1 ; Restore fd jsr popname ; Preserves A ; Return success - ldx #$00 + ldx #>$0000 stx ___oserror rts diff --git a/libsrc/apple2/opendir.c b/libsrc/apple2/opendir.c deleted file mode 100644 index 1144d8511..000000000 --- a/libsrc/apple2/opendir.c +++ /dev/null @@ -1,112 +0,0 @@ -/*****************************************************************************/ -/* */ -/* opendir.h */ -/* */ -/* Open a directory */ -/* */ -/* */ -/* */ -/* (C) 2005 Oliver Schmidt, <ol.sc@web.de> */ -/* */ -/* */ -/* This software is provided 'as-is', without any expressed 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. */ -/* */ -/*****************************************************************************/ - - - -#include <stddef.h> -#include <stdlib.h> -#include <stdio.h> -#include <unistd.h> -#include <fcntl.h> -#include <errno.h> -#include <dirent.h> -#include "dir.h" - - - -/*****************************************************************************/ -/* Data */ -/*****************************************************************************/ - - - -extern char _cwd[FILENAME_MAX]; - - - -/*****************************************************************************/ -/* Code */ -/*****************************************************************************/ - - - -DIR* __fastcall__ opendir (register const char* name) -{ - register DIR* dir; - - /* Alloc DIR */ - if ((dir = malloc (sizeof (*dir))) == NULL) { - - /* May not have been done by malloc() */ - _directerrno (ENOMEM); - - /* Return failure */ - return NULL; - } - - /* Interpret dot as current working directory */ - if (*name == '.') { - name = _cwd; - } - - /* Open directory file */ - if ((dir->fd = open (name, O_RDONLY)) != -1) { - - /* Read directory key block */ - if (read (dir->fd, - dir->block.bytes, - sizeof (dir->block)) == sizeof (dir->block)) { - - /* Get directory entry infos from directory header */ - dir->entry_length = dir->block.bytes[0x23]; - dir->entries_per_block = dir->block.bytes[0x24]; - - /* Skip directory header entry */ - dir->current_entry = 1; - - /* Return success */ - return dir; - } - - /* EOF: Most probably no directory file at all */ - if (_oserror == 0) { - _directerrno (EINVAL); - } - - /* Cleanup directory file */ - close (dir->fd); - } - - /* Cleanup DIR */ - free (dir); - - /* Return failure */ - return NULL; -} diff --git a/libsrc/apple2/opendir.s b/libsrc/apple2/opendir.s new file mode 100644 index 000000000..317be2755 --- /dev/null +++ b/libsrc/apple2/opendir.s @@ -0,0 +1,159 @@ +; +; Colin Leroy-Mira <colin@colino.net>, 2024 +; +; DIR* __fastcall__ opendir (register const char* name) +; + + .export _opendir, read_dir_block_ptr1 + + .import closedir_ptr1 + .import _open, _read, _close + .import _malloc + .import ___directerrno + + .import ___oserror, __cwd + + .import pushptr1, popptr1 + .import pushax, pusha0 + .import return0, returnFFFF + + .importzp ptr1 + + .include "apple2.inc" + .include "dir.inc" + .include "errno.inc" + .include "fcntl.inc" + .include "zeropage.inc" + +.proc _opendir + sta ptr1 + stx ptr1+1 + + ldy #$00 + lda (ptr1),y + cmp #'.' + bne :+ + + lda #<__cwd + ldx #>__cwd + sta ptr1 + stx ptr1+1 + +: ; Open directory + jsr pushptr1 + lda #O_RDONLY + jsr pusha0 + + ldy #$04 + jsr _open + + cmp #$FF ; Did we succeed? + beq @return_null + pha ; Yes - Push fd for backup + + ; malloc the dir struct + lda #<.sizeof(DIR) + ldx #>.sizeof(DIR) + jsr _malloc + bne :+ + + ; We failed to allocate + pla ; Get fd back + ldx #$00 + jsr _close ; Close it + + lda #ENOMEM ; Set error + jsr ___directerrno +@return_null: + jmp return0 + +: ; Store dir struct to pointer + sta ptr1 + stx ptr1+1 + + ; Save fd to dir struct + lda #$00 + ldy #DIR::FD + 1 + sta (ptr1),y + + dey + pla ; Get fd back + sta (ptr1),y + + jsr read_dir_block_ptr1 + bcc @read_ok + + ; Close directory, free it + jsr closedir_ptr1 + jmp return0 ; Return NULL + +@read_ok: + ; Read succeeded, populate dir struct + + ; Get file_count to entry_length from block + ldy #$26 + DIR::BYTES +: lda (ptr1),y + pha + dey + cpy #$23 + DIR::BYTES - 1 + bne :- + + ; Set entry_length to file_count in struct + ldy #DIR::ENTRY_LENGTH +: pla + sta (ptr1),y + iny + cpy #DIR::CURRENT_ENTRY + bne :- + + ; Skip directory header entry + lda #$01 + sta (ptr1),y + + ; Return pointer to dir struct + lda ptr1 + ldx ptr1+1 + rts +.endproc + +; Read a directory for the DIR* pointer in ptr1 +; Return with carry clear on success +read_dir_block_ptr1: + ; Push ptr1, read will destroy it + jsr pushptr1 + + ldy #DIR::FD + lda (ptr1),y + + jsr pusha0 ; Push fd for read + lda #<DIR::BYTES + clc + adc ptr1 + pha + lda #>DIR::BYTES + adc ptr1+1 + tax + pla + jsr pushax ; Push dir->block.bytes for read + + lda #<.sizeof(DIR::BYTES) + ldx #>.sizeof(DIR::BYTES) + + jsr _read ; Read directory block + cpx #>.sizeof(DIR::BYTES) + bne @read_err + cmp #<.sizeof(DIR::BYTES) + beq @read_ok + +@read_err: + ; Read failed, exit + lda ___oserror + bne :+ + lda #EINVAL + jsr ___directerrno +: sec + bcs @out +@read_ok: + clc +@out: + jmp popptr1 diff --git a/libsrc/apple2/oserror.s b/libsrc/apple2/oserror.s index 5f523340f..db4f146cd 100644 --- a/libsrc/apple2/oserror.s +++ b/libsrc/apple2/oserror.s @@ -23,7 +23,7 @@ ___osmaperrno: ; Found the code : lda ErrTab-1,x - ldx #$00 ; High byte always zero + ldx #>$0000 rts .rodata diff --git a/libsrc/apple2/readdir.c b/libsrc/apple2/readdir.c deleted file mode 100644 index 8acfbbe8b..000000000 --- a/libsrc/apple2/readdir.c +++ /dev/null @@ -1,90 +0,0 @@ -/*****************************************************************************/ -/* */ -/* readdir.c */ -/* */ -/* Read directory entry */ -/* */ -/* */ -/* */ -/* (C) 2005 Oliver Schmidt, <ol.sc@web.de> */ -/* */ -/* */ -/* This software is provided 'as-is', without any expressed 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. */ -/* */ -/*****************************************************************************/ - - - -#include <stddef.h> -#include <unistd.h> -#include <dirent.h> -#include "dir.h" - - - -/*****************************************************************************/ -/* Code */ -/*****************************************************************************/ - - - -struct dirent* __fastcall__ readdir (register DIR* dir) -{ - register unsigned char* entry; - - /* Search for the next active directory entry */ - do { - - /* Read next directory block if necessary */ - if (dir->current_entry == dir->entries_per_block) { - if (read (dir->fd, - dir->block.bytes, - sizeof (dir->block)) != sizeof (dir->block)) { - - /* Just return failure as read() has */ - /* set errno if (and only if) no EOF */ - return NULL; - } - - /* Start with first entry in next block */ - dir->current_entry = 0; - } - - /* Compute pointer to current entry */ - entry = dir->block.content.entries + - dir->current_entry * dir->entry_length; - - /* Switch to next entry */ - ++dir->current_entry; - } while (entry[0x00] == 0); - - /* Move creation date/time to allow for next step below */ - *(unsigned long*)&entry[0x1A] = *(unsigned long*)&entry[0x18]; - - /* Feature unsigned long access to EOF by extension from 3 to 4 bytes */ - entry[0x18] = 0; - - /* Move file type to allow for next step below */ - entry[0x19] = entry[0x10]; - - /* Zero-terminate file name */ - entry[0x01 + (entry[0x00] & 0x0F)] = 0; - - /* Return success */ - return (struct dirent*)&entry[0x01]; -} diff --git a/libsrc/apple2/readdir.s b/libsrc/apple2/readdir.s new file mode 100644 index 000000000..e748055e1 --- /dev/null +++ b/libsrc/apple2/readdir.s @@ -0,0 +1,113 @@ +; +; Colin Leroy-Mira <colin@colino.net>, 2024 +; +; struct dirent * __fastcall__ readdir (DIR *dir) +; + .export _readdir + .import read_dir_block_ptr1 + + .import incax1, return0 + .import tosaddax, tosumula0, incaxy + .import pushax, pusha0, pushptr1, popptr1 + .importzp ptr1, ptr4 + + .include "dir.inc" + +.proc _readdir + sta ptr1 + stx ptr1+1 + +@next_entry: + ; Do we need to read the next directory block? + ldy #DIR::CURRENT_ENTRY + lda (ptr1),y + ldy #DIR::ENTRIES_PER_BLOCK + cmp (ptr1),y + bne @read_entry ; We don't + + jsr read_dir_block_ptr1 + bcc @read_ok + + ; We had a read error + jmp return0 + +@read_ok: + ldy #DIR::CURRENT_ENTRY + lda #$00 + sta (ptr1),y + +@read_entry: + ; Compute pointer to current entry: + ; entry = dir->block.content.entries + + ; dir->current_entry * dir->entry_length + + jsr pushptr1 ; Backup ptr1 + lda ptr1 + ldx ptr1+1 + ldy #DIR::BYTES + DIR::CONTENT::ENTRIES + jsr incaxy + jsr pushax + ldy #DIR::CURRENT_ENTRY + lda (ptr1),y + jsr pusha0 + ldy #DIR::ENTRY_LENGTH + lda (ptr1),y + jsr tosumula0 + jsr tosaddax + ; Store pointer to current entry + sta ptr4 + stx ptr4+1 + jsr popptr1 + + ; Switch to next entry + ldy #DIR::CURRENT_ENTRY + lda (ptr1),y + clc + adc #1 + sta (ptr1),y + + ; Check if entry[0] == 0 + ldy #$00 + lda (ptr4),y + beq @next_entry ; Yes, skip entry + + ; Move creation date/time to allow for next step below + ; 18-19-1A-1B => 1A-1B-1C-1D + ldy #$1B +: lda (ptr4),y + iny + iny + sta (ptr4),y + dey + dey + dey + cpy #$17 + bne :- + + ; Feature unsigned long access to EOF by extension from 3 to 4 bytes + ; entry[0x18] = 0 + iny + lda #$00 + sta (ptr4),y + + ; Move file type to allow for next step below + ; entry[0x19] = entry[0x10] + ldy #$10 + lda (ptr4),y + ldy #$19 + sta (ptr4),y + + ; Zero-terminate file name + ldy #$00 + lda (ptr4),y + and #$0F + tay + iny + lda #$00 + sta (ptr4),y + + ; Return pointer to entry+1 + lda ptr4 + ldx ptr4+1 + jmp incax1 +.endproc diff --git a/libsrc/apple2/revers.s b/libsrc/apple2/revers.s index 83fcb2ae7..86dfd0d19 100644 --- a/libsrc/apple2/revers.s +++ b/libsrc/apple2/revers.s @@ -18,5 +18,5 @@ normal: dex ; $00->$FF, $40->$3F stx INVFLG ; Save new flag value bmi :+ ; Jump if current value is $FF (normal) lda #$01 ; Return "inverse" -: ldx #$00 +: ldx #>$0000 rts diff --git a/libsrc/apple2/rewinddir.s b/libsrc/apple2/rewinddir.s new file mode 100644 index 000000000..fd3e5738f --- /dev/null +++ b/libsrc/apple2/rewinddir.s @@ -0,0 +1,70 @@ +; +; Colin Leroy-Mira <colin@colino.net>, 2024 +; +; void __fastcall__ rewinddir (DIR* dir) +; + .export _rewinddir + .import read_dir_block_ptr1 + + .import pusha, pusha0, pushax + .import pushptr1, popptr1 + .import incaxy + .import _lseek, _memset + + .importzp ptr1, sreg + + .include "dir.inc" + .include "stdio.inc" + +.proc _rewinddir + sta ptr1 + stx ptr1+1 + jsr pushptr1 ; Backup ptr1, destroyed by _lseek + + ; Rewind directory file + ldy #DIR::FD + lda (ptr1),y + jsr pusha0 ; Push dir->fd + + tya ; Y = 0 here + jsr pusha0 + jsr pusha0 ; Push 0L + + lda #SEEK_SET ; X = 0 here + jsr _lseek + + ora sreg ; Check lseek returned 0L + ora sreg+1 + bne @rewind_err + txa + bne @rewind_err + + jsr popptr1 ; Restore ptr1 + + ; Read directory key block + jsr read_dir_block_ptr1 + bcs @rewind_err + + ; Skip directory header entry + lda #$01 + ldy #DIR::CURRENT_ENTRY + sta (ptr1),y + rts + +@rewind_err: + jsr popptr1 ; Restore ptr1 + + ; Assert that no subsequent readdir() finds an active entry + lda ptr1 + ldx ptr1+1 + ldy #DIR::BYTES + DIR::CONTENT::ENTRIES + jsr incaxy + jsr pushax + + lda #$00 + jsr pusha + + lda #<.sizeof(DIR::BYTES) + ldx #>.sizeof(DIR::BYTES) + jmp _memset +.endproc diff --git a/libsrc/apple2/ser/a2.gs.s b/libsrc/apple2/ser/a2.gs.s new file mode 100644 index 000000000..3ad899fc2 --- /dev/null +++ b/libsrc/apple2/ser/a2.gs.s @@ -0,0 +1,752 @@ +; +; Serial driver for the Apple IIgs Zilog Z8530. +; +; Colin Leroy-Mira <colin@colino.net>, 2023 +; +; This software is licensed under the same license as cc65, +; the zlib license (see LICENSE file). +; +; Documentation from http://www.applelogic.org/files/Z8530UM.pdf (pages +; referred to where applicable) +; and https://gswv.apple2.org.za/a2zine/Utils/Z8530_SCCsamples_info.txt + + + + .setcpu "65816" + + .include "zeropage.inc" + .include "ser-kernel.inc" + .include "ser-error.inc" + + .macpack module + +; ------------------------------------------------------------------------ +; Header. Includes jump table + + .ifdef __APPLE2ENH__ + module_header _a2e_gs_ser + .else + module_header _a2_gs_ser + .endif + + ; Driver signature + .byte $73, $65, $72 ; "ser" + .byte SER_API_VERSION ; Serial API version number + + ; Library reference + .addr $0000 + + ; Jump table + .addr SER_INSTALL + .addr SER_UNINSTALL + .addr SER_OPEN + .addr SER_CLOSE + .addr SER_GET + .addr SER_PUT + .addr SER_STATUS + .addr SER_IOCTL + .addr SER_IRQ + +;---------------------------------------------------------------------------- +; Global variables + + .bss + +RecvHead: .res 1 ; Head of receive buffer +RecvTail: .res 1 ; Tail of receive buffer +RecvFreeCnt: .res 1 ; Number of bytes in receive buffer +SendHead: .res 1 ; Head of send buffer +SendTail: .res 1 ; Tail of send buffer +SendFreeCnt: .res 1 ; Number of bytes in send buffer + +Stopped: .res 1 ; Flow-stopped flag +RtsOff: .res 1 +HSType: .res 1 ; Flow-control type + +RecvBuf: .res 256 ; Receive buffers: 256 bytes +SendBuf: .res 256 ; Send buffers: 256 bytes + +CurClockSource: .res 1 ; Whether to use BRG or RTxC for clock + + .data + +Opened: .byte $00 ; 1 when opened +Channel: .byte $00 ; Channel B by default +CurChanIrqFlags:.byte $00 + +SerFlagOrig: .byte $00 + +RxBitTable: .byte %00000000 ; SER_BITS_5, in WR_RX_CTRL (WR3) + .byte %10000000 ; SER_BITS_6 (Ref page 5-7) + .byte %01000000 ; SER_BITS_7 + .byte %11000000 ; SER_BITS_8 + +TxBitTable: .byte %00000000 ; SER_BITS_5, in WR_TX_CTRL (WR5) + .byte %01000000 ; SER_BITS_6 (Ref page 5-9) + .byte %00100000 ; SER_BITS_7 + .byte %01100000 ; SER_BITS_8 + + .rodata + +ClockMultiplier:.byte %01000000 ; Clock x16 (300-57600bps, WR4, ref page 5-8) + .byte %10000000 ; Clock x32 (115200bps, ref page 5-8) + +ClockSource: .byte %01010000 ; Use baud rate generator (ch. B) (WR11, page 5-17) + .byte %00000000 ; Use RTxC (115200bps) (ch. B) + .byte %11010000 ; Use baud rate generator (ch. A) + .byte %10000000 ; Use RTxC (115200bps) (ch. A) + +BrgEnabled: .byte %00000001 ; Baud rate generator on (WR14, page 5-19) + .byte %00000000 ; BRG Off + +ChanIrqFlags: .byte %00000101 ; ANDed (RX/special IRQ, ch. B) (page 5-25) + .byte %00101000 ; ANDed (RX/special IRQ, ch. A) + +ChanIrqMask: .byte %00000111 ; Ch. B IRQ flags mask + .byte %00111000 ; Ch. A IRQ flags mask + +BaudTable: ; bit7 = 1 means setting is invalid + ; Indexes cc65 RS232 SER_BAUD enum + ; into WR12/13 register values + ; (Ref page 5-18 and 5-19) + .word $FFFF ; SER_BAUD_45_5 + .word $FFFF ; SER_BAUD_50 + .word $FFFF ; SER_BAUD_75 + .word $FFFF ; SER_BAUD_110 + .word $FFFF ; SER_BAUD_134_5 + .word $FFFF ; SER_BAUD_150 + .word $017E ; SER_BAUD_300 + .word $FFFF ; SER_BAUD_600 + .word $005E ; SER_BAUD_1200 + .word $FFFF ; SER_BAUD_1800 + .word $002E ; SER_BAUD_2400 + .word $FFFF ; SER_BAUD_3600 + .word $0016 ; SER_BAUD_4800 + .word $FFFF ; SER_BAUD_7200 + .word $000A ; SER_BAUD_9600 + .word $0004 ; SER_BAUD_19200 + .word $0001 ; SER_BAUD_38400 + .word $0000 ; SER_BAUD_57600 + .word $0000 ; SER_BAUD_115200 (constant unused at that speed) + .word $FFFF ; SER_BAUD_230400 + +; About the speed selection: either we use the baud rate generator: +; - Load the time constants from BaudTable into WR12/WR13 +; - Setup the TX/RX clock source to BRG (ClockSource into WR11) +; - Setup the clock multiplier (WR4) +; - Enable the baud rate generator (WR14) +; In this case, the baud rate will be: +; rate = crystal_clock/(2+BRG_time_constant))/(2*clock_multiplier) +; Example: (3686400/(2+0x0004)) / (2*16) = 19200 bps +; +; Or we don't use the baud rate generator: +; - Setup the TX/RX clock source to RTxC +; - Setup the clock multiplier +; - Disable the baud rate generator +; - WR12 and 13 are ignored +; In this case, the baud rate will be: +; rate = crystal_clock/clock_multiplier +; Example: 3686400/32 = 115200 bps + +StopTable: .byte %00000100 ; SER_STOP_1, in WR_TX_RX_CTRL (WR4) + .byte %00001100 ; SER_STOP_2 (Ref page 5-8) + +ParityTable: .byte %00000000 ; SER_PAR_NONE, in WR_TX_RX_CTRL (WR4) + .byte %00000001 ; SER_PAR_ODD (Ref page 5-8) + .byte %00000011 ; SER_PAR_EVEN + .byte $FF ; SER_PAR_MARK + .byte $FF ; SER_PAR_SPACE + +; ------------------------------------------------------------------------ +; Addresses + +SCCAREG := $C039 +SCCBREG := $C038 +SCCADATA := $C03B +SCCBDATA := $C03A + +; We're supposed to get SerFlag's address using GetAddr on ROMs 1 and 3. +; (https://archive.org/details/IIgs_2523018_SCC_Access, page 9) +; But, it's the same value as on ROM0. As we don't expect a ROM 4 anytime +; soon with a different value, let's keep it simple. + +SER_FLAG := $E10104 + +; ------------------------------------------------------------------------ +; Channels + +CHANNEL_B = 0 +CHANNEL_A = 1 + +; ------------------------------------------------------------------------ +; Write registers, read registers, and values that interest us + +WR_INIT_CTRL = 0 +RR_INIT_STATUS = 0 +INIT_CTRL_CLEAR_EIRQ = %00010000 +INIT_CTRL_CLEAR_ERR = %00110000 +INIT_STATUS_READY = %00000100 +INIT_STATUS_RTS = %00100000 + +WR_TX_RX_MODE_CTRL = 1 +TX_RX_MODE_OFF = %00000000 +TX_RX_MODE_RXIRQ = %00010001 + +WR_RX_CTRL = 3 ; (Ref page 5-7) +RR_RX_STATUS = 9 ; Corresponding status register +RX_CTRL_ON = %00000001 ; ORed, Rx enabled +RX_CTRL_OFF = %11111110 ; ANDed,Rx disabled + +WR_TX_RX_CTRL = 4 +RR_TX_RX_STATUS = 4 + +WR_TX_CTRL = 5 ; (Ref page 5-9) +RR_TX_STATUS = 5 ; Corresponding status register +TX_CTRL_ON = %00001000 ; ORed, Tx enabled +TX_CTRL_OFF = %11110111 ; ANDed,Tx disabled +TX_DTR_ON = %01111111 ; ANDed,DTR ON (high) +TX_DTR_OFF = %10000000 ; ORed, DTR OFF +TX_RTS_ON = %00000010 ; ORed, RTS ON (low) +TX_RTS_OFF = %11111101 ; ANDed, RTS OFF + +WR_MASTER_IRQ_RST = 9 ; (Ref page 5-14) +MASTER_IRQ_SHUTDOWN = %00000010 ; STA'd +MASTER_IRQ_MIE_RST = %00001010 ; STA'd +MASTER_IRQ_SET = %00011001 ; STA'd + +WR_CLOCK_CTRL = 11 ; (Ref page 5-17) + +WR_BAUDL_CTRL = 12 ; (Ref page 5-18) +WR_BAUDH_CTRL = 13 ; (Ref page 5-19) + +WR_MISC_CTRL = 14 ; (Ref page 5-19) + +WR_IRQ_CTRL = 15 ; (Ref page 5-20) +IRQ_CLEANUP_EIRQ = %00001000 + +RR_SPEC_COND_STATUS = 1 ; (Ref page 5-23) +SPEC_COND_FRAMING_ERR = %01000000 +SPEC_COND_OVERRUN_ERR = %00100000 + +RR_IRQ_STATUS = 2 ; (Ref page 5-24) +IRQ_MASQ = %01110000 ; ANDed +IRQ_RX = %00100000 +IRQ_SPECIAL = %01100000 + +RR_INTR_PENDING_STATUS = 3 ; (Ref page 5-25) +INTR_IS_RX = %00100100 ; ANDed (RX IRQ, channel A or B) + + .code + +; Read register value to A. +; Input: X as channel +; Y as register +; Output: A +readSSCReg: + cpx #0 + bne ReadAreg + sty SCCBREG + lda SCCBREG + rts +ReadAreg: + sty SCCAREG + lda SCCAREG + rts + +; Write value of A to a register. +; Input: X as channel +; Y as register +writeSCCReg: + cpx #0 + bne WriteAreg + sty SCCBREG + sta SCCBREG + rts +WriteAreg: + sty SCCAREG + sta SCCAREG + rts + +;---------------------------------------------------------------------------- +; SER_INSTALL: Is called after the driver is loaded into memory. If possible, +; check if the hardware is present. Must return an SER_ERR_xx code in a/x. +; +; Since we don't have to manage the IRQ vector on the Apple II, this is +; actually the same as: +; +; SER_UNINSTALL: Is called before the driver is removed from memory. +; No return code required (the driver is removed from memory on return). +; +; and: +; +; SER_CLOSE: Close the port and disable interrupts. Called without parameters. +; Must return an SER_ERR_xx code in a/x. + +SER_INSTALL: +SER_UNINSTALL: +SER_CLOSE: + ; Check if this is a IIgs (Apple II Miscellaneous TechNote #7, + ; Apple II Family Identification) + sec + bit $C082 + jsr $FE1F + bit $C080 + + bcc IIgs + + lda #SER_ERR_NO_DEVICE ; Not a IIgs + ldx #>$0000 + rts + +IIgs: + ldx Opened ; Check for open port + beq :+ + + ldx Channel + + php ; Deactivate interrupts + sei ; if enabled + + ldy #WR_MASTER_IRQ_RST + lda #MASTER_IRQ_SHUTDOWN + jsr writeSCCReg + + ldy #WR_TX_RX_MODE_CTRL + lda #TX_RX_MODE_OFF + jsr writeSCCReg + + ; Reset SerFlag to what it was + lda SerFlagOrig + sta SER_FLAG + + lda SCCBDATA + + ; Clear external interrupts (twice) + ldy #WR_INIT_CTRL + lda #INIT_CTRL_CLEAR_EIRQ + jsr writeSCCReg + jsr writeSCCReg + + ; Reset MIE for firmware use + ldy #WR_MASTER_IRQ_RST + lda #MASTER_IRQ_MIE_RST + jsr writeSCCReg + + ldx #$00 + stx Opened ; Mark port as closed + + plp ; Reenable interrupts if needed +: txa ; Promote char return value + rts + +getClockSource: + .assert SER_PARAMS::BAUDRATE = 0, error + lda (ptr1) ; Baudrate index - cc65 value + cmp #SER_BAUD_115200 + lda #$00 + adc #$00 + sta CurClockSource ; 0 = BRG, 1 = RTxC + rts + +;---------------------------------------------------------------------------- +; SER_OPEN: A pointer to a ser_params structure is passed in ptr1. +; Must return an SER_ERR_xx code in a/x. + +SER_OPEN: + php ; Deactivate interrupts + sei ; if enabled + + ; Check if the handshake setting is valid + ldy #SER_PARAMS::HANDSHAKE ; Handshake + lda (ptr1),y + cmp #SER_HS_SW ; Not supported + beq InvParam + + sta HSType ; Store flow control type + + ; Initialize buffers + ldy #$00 + sty Stopped + sty RecvHead + sty RecvTail + sty SendHead + sty SendTail + dey ; Y = 255 + sty RecvFreeCnt + sty SendFreeCnt + + ldx Channel + + ldy #RR_INIT_STATUS ; Hit rr0 once to sync up + jsr readSSCReg + + ldy #WR_MISC_CTRL ; WR14: Turn everything off + lda #$00 + jsr writeSCCReg + + jsr getClockSource ; Should we use BRG or RTxC? + + ldy #SER_PARAMS::STOPBITS ; WR4 setup: clock mult., stop & parity + lda (ptr1),y ; Stop bits + tay + lda StopTable,y ; Get value + + pha + ldy #SER_PARAMS::PARITY + lda (ptr1),y ; Parity bits + tay + pla + ora ParityTable,y ; Get value + bmi InvParam + + ldy CurClockSource ; Clock multiplier + ora ClockMultiplier,y + + ldy #WR_TX_RX_CTRL + jsr writeSCCReg ; End of WR4 setup + + ldy CurClockSource ; WR11 setup: clock source + cpx #CHANNEL_B + beq SetClock + iny ; Shift to get correct ClockSource val + iny ; depending on our channel + +SetClock: + lda ClockSource,y + ldy #WR_CLOCK_CTRL + jsr writeSCCReg ; End of WR11 setup + + lda ChanIrqFlags,x ; Store which IRQ bits we'll check + sta CurChanIrqFlags + +SetBaud: + .assert SER_PARAMS::BAUDRATE = 0, error + lda (ptr1) ; Baudrate index - cc65 value + asl + tay + + lda BaudTable,y ; Get low byte of register value + bpl BaudOK ; Verify baudrate is supported + +InvParam: + lda #SER_ERR_INIT_FAILED + ldy #$00 ; Mark port closed + bra SetupOut + +BaudOK: + phy ; WR12 setup: BRG time constant, low byte + ldy #WR_BAUDL_CTRL ; Setting WR12 & 13 is useless if we're using + jsr writeSCCReg ; RTxC, but doing it anyway makes code smaller + ply + + iny + lda BaudTable,y ; WR13 setup: BRG time constant, high byte + ldy #WR_BAUDH_CTRL + jsr writeSCCReg + + ldy CurClockSource ; WR14 setup: BRG enabling + lda BrgEnabled,y + ldy #WR_MISC_CTRL ; Time to turn this thing on + jsr writeSCCReg + + ldy #SER_PARAMS::DATABITS ; WR3 setup: RX data bits + lda (ptr1),y + tay + lda RxBitTable,y + ora #RX_CTRL_ON ; and turn receiver on + + phy + ldy #WR_RX_CTRL + jsr writeSCCReg ; End of WR3 setup + ply + + lda TxBitTable,y ; WR5 setup: TX data bits + ora #TX_CTRL_ON ; and turn transmitter on + and #TX_DTR_ON ; and turn DTR on + + sta RtsOff ; Save value for flow control + + ora #TX_RTS_ON ; and turn RTS on + + ldy #WR_TX_CTRL + jsr writeSCCReg ; End of WR5 setup + + ldy #WR_IRQ_CTRL ; WR15 setup: IRQ + lda #IRQ_CLEANUP_EIRQ + jsr writeSCCReg + + ldy #WR_INIT_CTRL ; WR0 setup: clear existing IRQs + lda #INIT_CTRL_CLEAR_EIRQ + jsr writeSCCReg ; Clear (write twice) + jsr writeSCCReg + + ldy #WR_TX_RX_MODE_CTRL ; WR1 setup: Activate RX IRQ + lda #TX_RX_MODE_RXIRQ + jsr writeSCCReg + + lda SCCBREG ; WR9 setup: Activate master IRQ + ldy #WR_MASTER_IRQ_RST + lda #MASTER_IRQ_SET + jsr writeSCCReg + + lda SER_FLAG ; Get SerFlag's current value + sta SerFlagOrig ; and save it + + ora ChanIrqMask,x ; Tell firmware which channel IRQs we want + sta SER_FLAG + + ldy #$01 ; Mark port opened + lda #SER_ERR_OK + +SetupOut: + plp ; Reenable interrupts if needed + ldx #>$0000 + sty Opened + rts + +;---------------------------------------------------------------------------- +; SER_GET: Will fetch a character from the receive buffer and store it into the +; variable pointed to by ptr1. If no data is available, SER_ERR_NO_DATA is +; returned. + +SER_GET: + ldx Channel + + lda RecvFreeCnt ; Check for buffer empty + cmp #$FF + beq NoData + + ldy Stopped ; Check for flow stopped + beq :+ + cmp #63 ; Enough free? + bcc :+ + stz Stopped ; Release flow control + + lda RtsOff + ora #TX_RTS_ON + + ldy #WR_TX_CTRL + jsr writeSCCReg + +: ldy RecvHead ; Get byte from buffer + lda RecvBuf,y + inc RecvHead + inc RecvFreeCnt + sta (ptr1) + lda #SER_ERR_OK + .assert SER_ERR_OK = 0, error + tax + rts +NoData: + lda #SER_ERR_NO_DATA + ldx #>$0000 + rts + +;---------------------------------------------------------------------------- +; SER_PUT: Output character in A. +; Must return an SER_ERR_xx code in a/x. + +SER_PUT: + ldx Channel + + ldy SendFreeCnt ; Anything to send first? + iny ; Y = $FF? + beq :+ + pha + lda #$00 ; TryHard = false + jsr TryToSend + pla + +: ldy SendFreeCnt ; Do we have room to store byte? + bne :+ + lda #SER_ERR_OVERFLOW + ldx #>$0000 + rts + +: ldy SendTail ; Put byte into send buffer & send + sta SendBuf,y + inc SendTail + dec SendFreeCnt + lda #$FF ; TryHard = true + jsr TryToSend + lda #SER_ERR_OK + .assert SER_ERR_OK = 0, error + tax ; Promote char return value + rts + +;---------------------------------------------------------------------------- +; SER_STATUS: Return the status in the variable pointed to by ptr1. +; Must return an SER_ERR_xx code in a/x. +; We provide the read register 0, containing interesting info like +; INIT_STATUS_READY (hardware handshake status) or INIT_STATUS_RTS +; (ready to send). + +SER_STATUS: + ldx Channel + ldy #RR_INIT_STATUS + jsr readSSCReg + ldx #$00 + sta (ptr1) + .assert SER_ERR_OK = 0, error + txa + rts + +;---------------------------------------------------------------------------- +; SER_IOCTL: Driver defined entry point. The wrapper will pass a pointer to ioctl +; specific data in ptr1, and the ioctl code in A. +; Sets communication channel A or B (A = 1, B = 0) +; Must return an SER_ERR_xx code in a/x. + +SER_IOCTL: + ora ptr1+1 ; Check data msb and code to be 0 + bne :+ + + ldx ptr1 ; Check data lsb to be 0 or 1 + bmi :+ + cpx #$02 + bcs :+ + + stx Channel + .assert SER_ERR_OK = 0, error + tax ; Promote char return value + rts + +: lda #SER_ERR_INV_IOCTL + ldx #>$0000 + rts + +;---------------------------------------------------------------------------- +; SER_IRQ: Called from the builtin runtime IRQ handler as a subroutine. All +; registers are already saved, no parameters are passed, but the carry flag +; is clear on entry. The routine must return with carry set if the interrupt +; was handled, otherwise with carry clear. + +SER_IRQ: + ldy #RR_INTR_PENDING_STATUS ; IRQ status is always in A reg + sty SCCAREG + lda SCCAREG + + and CurChanIrqFlags ; Is this ours? + beq Done + + and #INTR_IS_RX ; Is this an RX irq? + beq CheckSpecial + + ldx Channel + beq ReadBdata + lda SCCADATA + bra ReadDone +ReadBdata: + lda SCCBDATA ; Get byte +ReadDone: + ldx RecvFreeCnt ; Check if we have free space left + beq Flow ; Jump if no space in receive buffer + ldy RecvTail ; Load buffer pointer + sta RecvBuf,y ; Store received byte in buffer + inc RecvTail ; Increment buffer pointer + dec RecvFreeCnt ; Decrement free space counter + cpx #33 + bcc Flow ; Assert flow control if buffer space low + rts ; Interrupt handled (carry already set) + +CheckSpecial: + ; Always check IRQ special flags from Channel B (Ref page 5-24) + ldy #RR_IRQ_STATUS + sty SCCBREG + lda SCCBREG + + and #IRQ_MASQ + cmp #IRQ_SPECIAL + beq Special + + ; Clear exint + ldx Channel + ldy #WR_INIT_CTRL + lda #INIT_CTRL_CLEAR_EIRQ + jsr writeSCCReg + + sec + rts + +Flow: lda HSType ; Don't touch if no flow control + beq IRQDone + + ldx Channel ; Assert flow control if buffer space too low + ldy #WR_TX_CTRL + lda RtsOff + jsr writeSCCReg + + sta Stopped +IRQDone:sec ; Interrupt handled +Done: rts + +Special:ldx Channel + ldy #RR_SPEC_COND_STATUS + jsr readSSCReg + + tax + and #SPEC_COND_FRAMING_ERR + bne BadChar + txa + and #SPEC_COND_OVERRUN_ERR + beq BadChar + + ldy #WR_INIT_CTRL + lda #INIT_CTRL_CLEAR_ERR + jsr writeSCCReg + + sec + rts + +BadChar: + cpx #CHANNEL_B + beq BadCharB + lda SCCADATA + bra BadCharDone +BadCharB: + lda SCCBDATA ; Remove char in error +BadCharDone: + sec + rts + +;---------------------------------------------------------------------------- +; Try to send a byte. Internal routine. A = TryHard, X = Channel + +TryToSend: + sta tmp1 ; Remember tryHard flag +Again: lda SendFreeCnt ; Anything to send? + cmp #$FF + beq Quit ; No + + lda Stopped ; Check for flow stopped + bne Quit ; Bail out if it is + +Wait: + ldy #RR_INIT_STATUS + jsr readSSCReg ; Check that we're ready to send + tay + and #INIT_STATUS_READY + beq NotReady + + tya + and #INIT_STATUS_RTS ; Ready to send + bne Send + +NotReady: + bit tmp1 ; Keep trying if must try hard + bmi Wait +Quit: rts + +Send: ldy SendHead ; Send byte + lda SendBuf,y + + cpx #CHANNEL_B + beq WriteBdata + sta SCCADATA + bra WriteDone +WriteBdata: + sta SCCBDATA +WriteDone: + inc SendHead + inc SendFreeCnt + jmp Again ; Continue flushing TX buffer diff --git a/libsrc/apple2/ser/a2.ssc.s b/libsrc/apple2/ser/a2.ssc.s index 6ad9c3825..488a32540 100644 --- a/libsrc/apple2/ser/a2.ssc.s +++ b/libsrc/apple2/ser/a2.ssc.s @@ -26,6 +26,7 @@ .include "ser-error.inc" .macpack module + .macpack cpu ; ------------------------------------------------------------------------ ; Header. Includes jump table @@ -37,8 +38,8 @@ .endif ; Driver signature - .byte $73, $65, $72 ; "ser" - .byte SER_API_VERSION ; Serial API version number + .byte $73, $65, $72 ; "ser" + .byte SER_API_VERSION ; Serial API version number ; Library reference .addr $0000 @@ -57,13 +58,19 @@ ;---------------------------------------------------------------------------- ; I/O definitions +.if (.cpu .bitand CPU_ISET_65C02) +ACIA := $C088 +.else Offset = $8F ; Move 6502 false read out of I/O to page $BF +ACIA := $C088-Offset +.endif -ACIA = $C088-Offset -ACIA_DATA = ACIA+0 ; Data register -ACIA_STATUS = ACIA+1 ; Status register -ACIA_CMD = ACIA+2 ; Command register -ACIA_CTRL = ACIA+3 ; Control register +ACIA_DATA := ACIA+0 ; Data register +ACIA_STATUS := ACIA+1 ; Status register +ACIA_CMD := ACIA+2 ; Command register +ACIA_CTRL := ACIA+3 ; Control register + +SLTROMSEL := $C02D ; For Apple IIgs slot verification ;---------------------------------------------------------------------------- ; Global variables @@ -72,16 +79,18 @@ ACIA_CTRL = ACIA+3 ; Control register RecvHead: .res 1 ; Head of receive buffer RecvTail: .res 1 ; Tail of receive buffer -RecvFreeCnt: .res 1 ; Number of bytes in receive buffer +RecvFreeCnt: .res 1 ; Number of free bytes in receive buffer SendHead: .res 1 ; Head of send buffer SendTail: .res 1 ; Tail of send buffer -SendFreeCnt: .res 1 ; Number of bytes in send buffer +SendFreeCnt: .res 1 ; Number of free bytes in send buffer Stopped: .res 1 ; Flow-stopped flag -RtsOff: .res 1 ; +RtsOff: .res 1 ; Cached value of command register with + ; flow stopped +HSType: .res 1 ; Flow-control type -RecvBuf: .res 256 ; Receive buffers: 256 bytes -SendBuf: .res 256 ; Send buffers: 256 bytes +RecvBuf: .res 256 ; Receive buffer: 256 bytes +SendBuf: .res 256 ; Send buffer: 256 bytes Index: .res 1 ; I/O register index @@ -91,8 +100,9 @@ Slot: .byte $02 ; Default to SSC in slot 2 .rodata - ; Tables used to translate RS232 params into register values -BaudTable: ; bit7 = 1 means setting is invalid +BaudTable: ; Table used to translate RS232 baudrate param + ; into control register value + ; bit7 = 1 means setting is invalid .byte $FF ; SER_BAUD_45_5 .byte $01 ; SER_BAUD_50 .byte $02 ; SER_BAUD_75 @@ -111,32 +121,44 @@ BaudTable: ; bit7 = 1 means setting is invalid .byte $0F ; SER_BAUD_19200 .byte $FF ; SER_BAUD_38400 .byte $FF ; SER_BAUD_57600 - .byte $FF ; SER_BAUD_115200 + .byte $00 ; SER_BAUD_115200 .byte $FF ; SER_BAUD_230400 -BitTable: + +BitTable: ; Table used to translate RS232 databits param + ; into control register value .byte $60 ; SER_BITS_5 .byte $40 ; SER_BITS_6 .byte $20 ; SER_BITS_7 .byte $00 ; SER_BITS_8 -StopTable: + +StopTable: ; Table used to translate RS232 stopbits param + ; into control register value .byte $00 ; SER_STOP_1 .byte $80 ; SER_STOP_2 -ParityTable: + +ParityTable: ; Table used to translate RS232 parity param + ; into command register value .byte $00 ; SER_PAR_NONE .byte $20 ; SER_PAR_ODD .byte $60 ; SER_PAR_EVEN .byte $A0 ; SER_PAR_MARK .byte $E0 ; SER_PAR_SPACE -IdOfsTable: + +IdOfsTable: ; Table of bytes positions, used to check four + ; specific bytes on the slot's firmware to make + ; sure this is a serial card. .byte $05 ; Pascal 1.0 ID byte .byte $07 ; Pascal 1.0 ID byte .byte $0B ; Pascal 1.1 generic signature byte .byte $0C ; Device signature byte -IdValTable: - .byte $38 ; Fixed - .byte $18 ; Fixed - .byte $01 ; Fixed - .byte $31 ; Serial or parallel I/O card type 1 + +IdValTable: ; Table of expected values for the four checked + ; bytes + .byte $38 ; ID Byte 0 (from Pascal 1.0), fixed + .byte $18 ; ID Byte 1 (from Pascal 1.0), fixed + .byte $01 ; Generic signature for Pascal 1.1, fixed + .byte $31 ; Device signature byte (serial or + ; parallel I/O card type 1) IdTableLen = * - IdValTable @@ -163,12 +185,10 @@ SER_CLOSE: ldx Index ; Check for open port beq :+ - ; Deactivate DTR and disable 6551 interrupts - lda #%00001010 + lda #%00001010 ; Deactivate DTR and disable 6551 interrupts sta ACIA_CMD,x - ; Done, return an error code -: lda #SER_ERR_OK +: lda #SER_ERR_OK ; Done, return an error code .assert SER_ERR_OK = 0, error tax stx Index ; Mark port as closed @@ -177,104 +197,155 @@ SER_CLOSE: ;---------------------------------------------------------------------------- ; SER_OPEN: A pointer to a ser_params structure is passed in ptr1. ; Must return an SER_ERR_xx code in a/x. +; Note: Hardware checks are done in SER_OPEN instead of SER_INSTALL, +; because they depend on the selected slot, and we can't select the slot +; before SER_INSTALL. SER_OPEN: - ldx #<$C000 + ; Check if this is a IIgs (Apple II Miscellaneous TechNote #7, + ; Apple II Family Identification) + sec + bit $C082 + jsr $FE1F + bit $C080 + + bcs NotIIgs + + ; We're on a IIgs. For every slot N, either bit N of $C02D is + ; 0 for the internal ROM, or 1 for "Your Card". Let's make sure + ; that slot N's bit is set to 1, otherwise, that can't be an SSC. + + ldy Slot + lda SLTROMSEL +: lsr + dey + bpl :- ; Shift until slot's bit ends in carry + bcc NoDev + +NotIIgs:ldx #<$C000 stx ptr2 lda #>$C000 ora Slot sta ptr2+1 - ; Check Pascal 1.1 Firmware Protocol ID bytes -: ldy IdOfsTable,x +: ldy IdOfsTable,x ; Check Pascal 1.1 Firmware Protocol ID bytes lda IdValTable,x cmp (ptr2),y - bne NoDevice + bne NoDev inx cpx #IdTableLen bcc :- - ; Convert slot to I/O register index - lda Slot + lda Slot ; Convert slot to I/O register index asl asl asl asl - adc #Offset ; Assume carry to be clear +.if .not (.cpu .bitand CPU_ISET_65C02) + adc #Offset ; Assume carry to be clear +.endif tax - ; Check if the handshake setting is valid - ldy #SER_PARAMS::HANDSHAKE ; Handshake - lda (ptr1),y - cmp #SER_HS_HW ; This is all we support - bne InvParam + ; Check that this works like an ACIA 6551 is expected to work - ; Initialize buffers - ldy #$00 + lda ACIA_STATUS,x ; Save current values in what we expect to be + sta tmp1 ; the ACIA status register + lda ACIA_CMD,x ; and command register. So we can restore them + sta tmp2 ; if this isn't a 6551. + + ldy #%00000010 ; Disable TX/RX, disable IRQ +: tya + sta ACIA_CMD,x + cmp ACIA_CMD,x ; Verify what we stored is there + bne NotAcia + iny ; Enable TX/RX, disable IRQ + cpy #%00000100 + bne :- + sta ACIA_STATUS,x ; Reset ACIA + lda ACIA_CMD,x ; Check that RX/TX is disabled + lsr + bcc AciaOK + +NotAcia:lda tmp2 ; Restore original values + sta ACIA_CMD,x + lda tmp1 + sta ACIA_STATUS,x + +NoDev: lda #SER_ERR_NO_DEVICE + bne Out + + ; Check if the handshake setting is valid +AciaOK: ldy #SER_PARAMS::HANDSHAKE + lda (ptr1),y + cmp #SER_HS_SW ; Not supported + bne HandshakeOK + + lda #SER_ERR_INIT_FAILED + bne Out + +HandshakeOK: + sta HSType ; Store flow control type + + ldy #$00 ; Initialize buffers sty Stopped sty RecvHead sty RecvTail sty SendHead sty SendTail - dey ; Y = 255 + dey ; Y = 255 sty RecvFreeCnt sty SendFreeCnt ; Set the value for the control register, which contains stop bits, ; word length and the baud rate. ldy #SER_PARAMS::BAUDRATE - lda (ptr1),y ; Baudrate index + lda (ptr1),y ; Baudrate index tay - lda BaudTable,y ; Get 6551 value - bmi InvBaud ; Branch if rate not supported - sta tmp1 + lda BaudTable,y ; Get 6551 value + sta tmp2 ; Backup for IRQ setting + bpl BaudOK ; Check that baudrate is supported - ldy #SER_PARAMS::DATABITS ; Databits - lda (ptr1),y + lda #SER_ERR_BAUD_UNAVAIL + bne Out + +BaudOK: sta tmp1 + ldy #SER_PARAMS::DATABITS + lda (ptr1),y ; Databits index tay - lda BitTable,y + lda BitTable,y ; Get 6551 value ora tmp1 sta tmp1 - ldy #SER_PARAMS::STOPBITS ; Stopbits - lda (ptr1),y + ldy #SER_PARAMS::STOPBITS + lda (ptr1),y ; Stopbits index tay - lda StopTable,y + lda StopTable,y ; Get 6551 value ora tmp1 - ora #%00010000 ; Receiver clock source = baudrate + ora #%00010000 ; Set receiver clock source = baudrate sta ACIA_CTRL,x ; Set the value for the command register. We remember the base value ; in RtsOff, since we will have to manipulate ACIA_CMD often. - ldy #SER_PARAMS::PARITY ; Parity - lda (ptr1),y + ldy #SER_PARAMS::PARITY + lda (ptr1),y ; Parity index tay - lda ParityTable,y - ora #%00000001 ; DTR active - sta RtsOff - ora #%00001000 ; Enable receive interrupts - sta ACIA_CMD,x + lda ParityTable,y ; Get 6551 value + + ora #%00000001 ; Set DTR active + sta RtsOff ; Store value to easily handle flow control later + + ora #%00001010 ; Disable interrupts and set RTS low + + ldy tmp2 ; Don't enable IRQs if 115200bps + beq :+ + and #%11111101 ; Enable receive IRQs +: sta ACIA_CMD,x ; Done - stx Index ; Mark port as open + stx Index ; Mark port as open lda #SER_ERR_OK - .assert SER_ERR_OK = 0, error - tax - rts - - ; Device (hardware) not found -NoDevice:lda #SER_ERR_NO_DEVICE - ldx #0 ; return value is char - rts - - ; Invalid parameter -InvParam:lda #SER_ERR_INIT_FAILED - ldx #0 ; return value is char - rts - - ; Baud rate not available -InvBaud:lda #SER_ERR_BAUD_UNAVAIL - ldx #0 ; return value is char +Out: + ldx #>$0000 rts ;---------------------------------------------------------------------------- @@ -284,38 +355,38 @@ InvBaud:lda #SER_ERR_BAUD_UNAVAIL SER_GET: ldx Index - ldy SendFreeCnt ; Send data if necessary - iny ; Y == $FF? - beq :+ - lda #$00 ; TryHard = false - jsr TryToSend - ; Check for buffer empty -: lda RecvFreeCnt ; (25) + lda RecvFreeCnt ; Check for buffer empty cmp #$FF bne :+ lda #SER_ERR_NO_DATA - ldx #0 ; return value is char + ldx #>$0000 rts - ; Check for flow stopped & enough free: release flow control -: ldy Stopped ; (34) +: ldy Stopped ; Check for flow stopped beq :+ - cmp #63 + cmp #63 ; Enough free? bcc :+ +.if (.cpu .bitand CPU_ISET_65C02) + stz Stopped ; Release flow control +.else lda #$00 sta Stopped +.endif lda RtsOff ora #%00001000 sta ACIA_CMD,x - ; Get byte from buffer -: ldy RecvHead ; (41) +: ldy RecvHead ; Get byte from buffer lda RecvBuf,y inc RecvHead inc RecvFreeCnt - ldx #$00 ; (59) + ldx #$00 +.if (.cpu .bitand CPU_ISET_65C02) + sta (ptr1) ; Store it for caller +.else sta (ptr1,x) +.endif txa ; Return code = 0 rts @@ -326,28 +397,26 @@ SER_GET: SER_PUT: ldx Index - ; Try to send - ldy SendFreeCnt - iny ; Y = $FF? + ldy SendFreeCnt ; Anything to send first? + cpy #$FF ; No beq :+ pha lda #$00 ; TryHard = false - jsr TryToSend + jsr TryToSend ; Try to flush send buffer pla - ; Put byte into send buffer & send -: ldy SendFreeCnt + ldy SendFreeCnt ; Reload SendFreeCnt after TryToSend bne :+ lda #SER_ERR_OVERFLOW - ldx #0 ; return value is char + ldx #>$0000 rts -: ldy SendTail +: ldy SendTail ; Put byte into send buffer sta SendBuf,y inc SendTail dec SendFreeCnt lda #$FF ; TryHard = true - jsr TryToSend + jsr TryToSend ; Flush send buffer lda #SER_ERR_OK .assert SER_ERR_OK = 0, error tax @@ -369,26 +438,25 @@ SER_STATUS: ;---------------------------------------------------------------------------- ; SER_IOCTL: Driver defined entry point. The wrapper will pass a pointer to ioctl ; specific data in ptr1, and the ioctl code in A. +; The ioctl data is the slot number to open. ; Must return an SER_ERR_xx code in a/x. SER_IOCTL: - ; Check data msb and code to be 0 - ora ptr1+1 + ora ptr1+1 ; Check data msb and code to be 0 bne :+ - ; Check data lsb to be [1..7] - ldx ptr1 + ldx ptr1 ; Check data lsb to be [1..7] beq :+ cpx #7+1 bcs :+ - stx Slot + stx Slot ; Store slot .assert SER_ERR_OK = 0, error tax rts : lda #SER_ERR_INV_IOCTL - ldx #0 ; return value is char + ldx #>$0000 rts ;---------------------------------------------------------------------------- @@ -404,22 +472,24 @@ SER_IRQ: and #$08 beq Done ; Jump if no ACIA interrupt lda ACIA_DATA,x ; Get byte from ACIA - ldy RecvFreeCnt ; Check if we have free space left + ldx RecvFreeCnt ; Check if we have free space left beq Flow ; Jump if no space in receive buffer ldy RecvTail ; Load buffer pointer sta RecvBuf,y ; Store received byte in buffer inc RecvTail ; Increment buffer pointer dec RecvFreeCnt ; Decrement free space counter - ldy RecvFreeCnt ; Check for buffer space low - cpy #33 + cpx #33 ; Check for buffer space low bcc Flow ; Assert flow control if buffer space low rts ; Interrupt handled (carry already set) - ; Assert flow control if buffer space too low -Flow: lda RtsOff +Flow: lda HSType ; Don't touch if no flow control + beq IRQDone + + ldx Index ; Assert flow control if buffer space too low + lda RtsOff sta ACIA_CMD,x sta Stopped - sec ; Interrupt handled +IRQDone:sec ; Interrupt handled Done: rts ;---------------------------------------------------------------------------- @@ -427,26 +497,24 @@ Done: rts TryToSend: sta tmp1 ; Remember tryHard flag -Again: lda SendFreeCnt - cmp #$FF - beq Quit ; Bail out +NextByte: + lda SendFreeCnt ; Is there anything to send? This can happen if + cmp #$FF ; we got interrupted by RX while sending, and + beq Quit ; flow control was asserted. - ; Check for flow stopped - lda Stopped - bne Quit ; Bail out +Again: lda Stopped ; Is flow stopped? + bne Quit ; Yes, Bail out - ; Check that ACIA is ready to send - lda ACIA_STATUS,x + lda ACIA_STATUS,x ; Check that ACIA is ready to send and #$10 - bne Send + bne Send ; It is! bit tmp1 ; Keep trying if must try hard bmi Again Quit: rts - ; Send byte and try again -Send: ldy SendHead +Send: ldy SendHead ; Get first byte to send lda SendBuf,y - sta ACIA_DATA,x + sta ACIA_DATA,x ; Send it inc SendHead inc SendFreeCnt - jmp Again + bne NextByte ; And try next one diff --git a/libsrc/apple2/set_iigs_speed.s b/libsrc/apple2/set_iigs_speed.s new file mode 100644 index 000000000..f13e8ab6a --- /dev/null +++ b/libsrc/apple2/set_iigs_speed.s @@ -0,0 +1,29 @@ +; +; Colin Leroy-Mira <colin@colino.net>, 2024 +; +; unsigned char __fastcall__ detect_iigs(unsigned char speed) +; + + .export _set_iigs_speed + .import ostype, return0 + + .include "apple2.inc" + .include "accelerator.inc" + +_set_iigs_speed: + tax ; Keep parameter + lda ostype ; Return if not IIgs + bmi :+ + jmp return0 + +: lda CYAREG + cpx #SPEED_SLOW + beq :+ + ora #%10000000 + bne set_speed +: and #%01111111 +set_speed: + sta CYAREG + txa + ldx #>$0000 + rts diff --git a/libsrc/apple2/sleep.s b/libsrc/apple2/sleep.s new file mode 100644 index 000000000..43873d9f4 --- /dev/null +++ b/libsrc/apple2/sleep.s @@ -0,0 +1,54 @@ +; +; Colin Leroy-Mira <colin@colino.net>, 2024 +; +; void __fastcall__ sleep(unsigned s) +; +; + + .export _sleep + .import _get_iigs_speed + .import _set_iigs_speed + .import WAIT + .importzp tmp1 + + .include "accelerator.inc" + + ; This functions uses the Apple2 WAIT ROM routine to waste a certain + ; amount of cycles and returns approximately after the numbers of + ; seconds passed in AX. + ; + ; It takes 1023730 cycles when called with AX=1 (1,0007s), + ; 10236364 cycles when called with AX=10 (10,006 seconds), + ; 306064298 cycles with AX=300 (299.2 seconds). + ; + ; Caveat: IRQs firing during calls to sleep will make the sleep longer + ; by the amount of cycles it takes to handle the IRQ. + ; +_sleep: + stx tmp1 ; High byte of s in X + tay ; Low byte in A + ora tmp1 + bne :+ + rts +: jsr _get_iigs_speed ; Save current CPU speed + pha + lda #SPEED_SLOW ; Down to 1MHz for consistency around WAIT + jsr _set_iigs_speed +sleep_1s: + ldx #$0A ; Loop 10 times +sleep_100ms: + lda #$C7 ; Sleep about 99ms + jsr WAIT + lda #$0D ; About 1ms + jsr WAIT + dex + bne sleep_100ms + dey + bne sleep_1s + dec tmp1 + bmi done + dey ; Down to #$FF + bne sleep_1s +done: + pla ; Restore CPU speed + jmp _set_iigs_speed diff --git a/libsrc/apple2/stat.s b/libsrc/apple2/stat.s new file mode 100644 index 000000000..e0564ae0c --- /dev/null +++ b/libsrc/apple2/stat.s @@ -0,0 +1,129 @@ +; +; Colin Leroy-Mira, 2023 <colin@colino.net> +; +; int __fastcall__ stat(const char *pathname, struct stat *statbuf); +; + + .export _stat + .import __errno, _open,_close + .import mli_file_info + .import popax, pushax, pusha0, incsp2 + .include "zeropage.inc" + .include "errno.inc" + .include "fcntl.inc" + .include "filedes.inc" + .include "mli.inc" + .include "stat.inc" + +_stat: + ; Store statbuf pointer + sta ptr4 + sta stbuf + stx ptr4+1 + stx stbuf+1 + + ; Clear statbuf + lda #$00 + ldy #.sizeof(stat)-1 +: sta (ptr4),y + dey + bpl :- + + ; Reset errno + sta ___errno + + ; Store pathname + jsr popax + jsr pushax ; Push it back for mli_file_info + jsr pushax ; and for open + + jsr mli_file_info + + bcc got_info + jmp incsp2 ; Drop filename copy for open + +got_info: + ; st_dev + lda DEVNUM + lsr ; Shift right to cc65 representation + lsr + lsr + lsr + ldy #stat::st_dev + sta (ptr4),y + + ; st_mode (S_IFDIR/S_IFREG only) + lda mliparam + MLI::INFO::FILE_TYPE + ldy #stat::st_mode + cmp #$0f + bne is_reg + lda #S_IFDIR + bne set_st_mode + +is_reg: lda #S_IFREG + +set_st_mode: + sta (ptr4),y + + ; st_access through st_create_time + ldx #MLI::INFO::ACCESS + ldy #stat::st_access +: lda mliparam,x + sta (ptr4),y + inx + iny + cpy #stat::st_create_time + .sizeof(stat::st_create_time) + bne :- + + ; st_size + lda #O_RDONLY + jsr pusha0 + ldy #$04 + jsr _open + cmp #$FF + beq done + pha ; Save file descriptor for closing + + ; Get ProDOS's REF_NUM from file descriptor + jsr getfd + ; Get file information + sta mliparam + MLI::EOF::REF_NUM + lda #GET_EOF_CALL + ldx #EOF_COUNT + jsr callmli + bcs eoferr + + ; Get struct stat in ptr4 back, open destroyed it + lda stbuf + ldx stbuf+1 + sta ptr4 + stx ptr4+1 + + ; Store size + ldy #stat::st_size + lda mliparam + MLI::EOF::EOF + sta (ptr4),y + lda mliparam + MLI::EOF::EOF+1 + iny + sta (ptr4),y + lda mliparam + MLI::EOF::EOF+2 + iny + sta (ptr4),y + + ; Close file +eoferr: + pla + ldx #$00 + jsr _close + + ; Set return value if we had an error + lda ___errno + beq done + lda #$FF +done: + tax ; Promote char return value + rts + + .bss + +stbuf: .res 2 diff --git a/libsrc/apple2/statvfs.s b/libsrc/apple2/statvfs.s new file mode 100644 index 000000000..8fcf46af8 --- /dev/null +++ b/libsrc/apple2/statvfs.s @@ -0,0 +1,123 @@ +; +; Colin Leroy-Mira, 2023 <colin@colino.net> +; +; int __fastcall__ statvfs(const char *pathname, struct statvfs *statvfsbuf); +; + + .export _statvfs + .import _dio_query_sectsize + .import mli_file_info, pushax, popax, popptr1, pushptr1 + .include "zeropage.inc" + .include "apple2.inc" + .include "errno.inc" + .include "mli.inc" + .include "statvfs.inc" + +_statvfs: + ; Store statbuf + sta ptr4 + stx ptr4+1 + + ; Clear statbuf + lda #$00 + ldy #.sizeof(statvfs)-1 +: sta (ptr4),y + dey + bpl :- + + ; Store pathname, keeping only volume name + jsr popptr1 + ldy #$00 + sty vol_sep + lda (ptr1),y + cmp #'/' ; Is the path absolute? + beq :+ + lda #EINVAL + jmp ___directerrno + +: iny + lda (ptr1),y + beq :+ ; End of string, no other / + cpy #FILENAME_MAX + beq :+ ; Max filename length reached + cmp #'/' + bne :- ; Not a slash, keep looking + sty vol_sep ; Register '/' index + lda #$00 + sta (ptr1),y ; Cut pathname at first slash +: jsr pushptr1 + + jsr mli_file_info + + php + ldy vol_sep ; Put slash back in pathname + lda #'/' + sta (ptr1),y + plp + + bcc got_info + + jmp ___mappederrno + +got_info: + ; f_fsid + lda DEVNUM + lsr ; Shift right to cc65 representation + lsr + lsr + lsr + ldy #statvfs::f_fsid + sta (ptr4),y + + ; total number of blocks + lda mliparam + MLI::INFO::AUX_TYPE + ldy #statvfs::f_blocks + sta (ptr4),y + lda mliparam + MLI::INFO::AUX_TYPE+1 + iny + sta (ptr4),y + + ; blocks free & avail + sec + lda mliparam + MLI::INFO::AUX_TYPE + sbc mliparam + MLI::INFO::BLOCKS + ldy #statvfs::f_bfree + sta (ptr4),y + ldy #statvfs::f_bavail + sta (ptr4),y + + lda mliparam + MLI::INFO::AUX_TYPE+1 + sbc mliparam + MLI::INFO::BLOCKS+1 + iny + sta (ptr4),y + ldy #statvfs::f_bfree+1 + sta (ptr4),y + + ; block sizes + jsr _dio_query_sectsize + ; low bytes + ldy #statvfs::f_bsize + sta (ptr4),y + ldy #statvfs::f_frsize + sta (ptr4),y + ; f_frsize high byte + iny + txa + sta (ptr4),y + ; f_bsize high byte + ldy #statvfs::f_bsize+1 + sta (ptr4),y + + ; f_namemax + lda #FILENAME_MAX + ldy #statvfs::f_namemax + sta (ptr4),y + + lda #$00 + sta ___errno + tax + rts + + .bss + +vol_sep:.res 1 diff --git a/libsrc/apple2/syschdir.s b/libsrc/apple2/syschdir.s index 8afc3af0b..b3f81e2a5 100644 --- a/libsrc/apple2/syschdir.s +++ b/libsrc/apple2/syschdir.s @@ -29,7 +29,8 @@ __syschdir: bcs cleanup ; Update current working directory - jsr initcwd ; Returns with A = 0 + jsr initcwd + lda #$00 ; Cleanup name cleanup:jsr popname ; Preserves A diff --git a/libsrc/apple2/targetutil/convert.c b/libsrc/apple2/targetutil/convert.c index ea9273fc3..52dffa745 100644 --- a/libsrc/apple2/targetutil/convert.c +++ b/libsrc/apple2/targetutil/convert.c @@ -108,7 +108,7 @@ static unsigned get_dir_entry(char* p_name) } /* Field header_pointer directly follows field last_mod */ - cur_addr = *(unsigned*)(&dirent->d_mtime.hour + 1); + cur_addr = *(unsigned*)(&dirent->d_mtime.time.hour + 1); dhandle = dio_open(getcurrentdevice()); if (!dhandle) { diff --git a/libsrc/apple2/tgi/a2.hi.s b/libsrc/apple2/tgi/a2.hi.s index aeb24f6be..1d5bdb68b 100644 --- a/libsrc/apple2/tgi/a2.hi.s +++ b/libsrc/apple2/tgi/a2.hi.s @@ -342,7 +342,7 @@ GETPIXEL: lda #$03 ; 3 (white) : bcc :+ adc #$03 ; += 4 (black -> black2, white -> white2) -: ldx #$00 +: ldx #>$0000 bit $C080 ; Switch in LC bank 2 for R/O rts diff --git a/libsrc/apple2/tgi/a2.lo.s b/libsrc/apple2/tgi/a2.lo.s index 6d1c6aa4a..8b00c5d20 100644 --- a/libsrc/apple2/tgi/a2.lo.s +++ b/libsrc/apple2/tgi/a2.lo.s @@ -321,7 +321,7 @@ GETPIXEL: jsr SCRN tax lda COL2TGI,x - ldx #$00 + ldx #>$0000 bit $C080 ; Switch in LC bank 2 for R/O rts diff --git a/libsrc/apple2/uppercasemask.s b/libsrc/apple2/uppercasemask.s new file mode 100644 index 000000000..8b993bb1e --- /dev/null +++ b/libsrc/apple2/uppercasemask.s @@ -0,0 +1,9 @@ +; +; Oliver Schmidt, 2024-08-06 +; + + .export uppercasemask + + .data + +uppercasemask: .byte $DF ; Convert to uppercase diff --git a/libsrc/apple2/videomode.s b/libsrc/apple2/videomode.s index 13151a48a..414105b18 100644 --- a/libsrc/apple2/videomode.s +++ b/libsrc/apple2/videomode.s @@ -6,7 +6,6 @@ .ifdef __APPLE2ENH__ .export _videomode - .import COUT .include "apple2.inc" @@ -17,52 +16,25 @@ _videomode: bit RD80VID php - ; If we are in 80 column mode then the 80 column firmware is - ; known to be active so we can just print the ctrl-char code - ; (even if this only means staying in the current videomode) - bpl :+ - jsr COUT - bra done - - ; If we are in 40 column mode and want to set 40 column mode - ; then we explicitly do nothing as we neither know about the - ; current state of the 80 column firmware nor want to fix it -: cmp #$11 ; Ctrl-char code for 40 cols - beq done - - ; If we are in 40 column mode and want to set 80 column mode - ; then we first presume the 80 column firmware being already - ; active and print the ctrl-char code (this causes a garbage - ; char to be printed on the screen if isn't already active) - jsr COUT - - ; If we successfully switched to 80 column mode then the 80 - ; column firmware was in fact already active and we're done - bit RD80VID - bmi done - - ; The 80 column firmware isn't already active so we need to - ; initialize it - causing the screen to be cleared and thus - ; the garbage char printed above to be erased (but for some - ; reason the cursor horizontal position not to be zeroed) - stz CH - ; Initializing the 80 column firmware needs the ROM switched ; in, otherwise it would copy the F8 ROM to the LC (@ $CEF4) bit $C082 - ; Initialize 80 column firmware - jsr $C300 ; PR#3 + ; Call 80 column firmware with ctrl-char code + jsr $C300 ; Switch in LC bank 2 for R/O bit $C080 + ; Switch in alternate charset again + sta SETALTCHAR + ; Return ctrl-char code for setting previous ; videomode using the saved videomode flag -done: lda #$11 ; Ctrl-char code for 40 cols + lda #$15 ; Ctrl-char code for 40 cols plp bpl :+ - inc a ; Ctrl-char code for 80 cols + lda #$00 ; Ctrl-char code for 80 cols : rts ; X was preserved all the way .endif ; __APPLE2ENH__ diff --git a/libsrc/apple2/wait.s b/libsrc/apple2/wait.s new file mode 100644 index 000000000..3b569215b --- /dev/null +++ b/libsrc/apple2/wait.s @@ -0,0 +1,20 @@ +; +; Colin Leroy-Mira, 2024 +; +; WAIT routine +; + + .export WAIT + + .include "apple2.inc" + + .segment "LOWCODE" + +WAIT: + ; Switch in ROM and call WAIT + bit $C082 + jsr $FCA8 ; Vector to WAIT routine + + ; Switch in LC bank 2 for R/O and return + bit $C080 + rts diff --git a/libsrc/apple2/waitvsync.s b/libsrc/apple2/waitvsync.s index a4ab5ebb3..486b93a53 100644 --- a/libsrc/apple2/waitvsync.s +++ b/libsrc/apple2/waitvsync.s @@ -5,21 +5,11 @@ ; .ifdef __APPLE2ENH__ - .constructor initvsync .export _waitvsync - .import _get_ostype + .import ostype .include "apple2.inc" - .segment "ONCE" - -initvsync: - jsr _get_ostype - sta ostype - rts - - .code - _waitvsync: bit ostype bmi iigs ; $8x @@ -39,7 +29,8 @@ iigs: bit RDVBLBAR rts ; Apple IIc TechNote #9, Detecting VBL -iic: sei +iic: php + sei sta IOUDISOFF lda RDVBLMSK bit ENVBL @@ -50,11 +41,7 @@ iic: sei bcs :+ ; VBL interrupts were already enabled bit DISVBL : sta IOUDISON ; IIc Tech Ref Man: The firmware normally leaves IOUDIS on. - cli + plp rts - .segment "INIT" - -ostype: .res 1 - .endif ; __APPLE2ENH__ diff --git a/libsrc/apple2/wherex.s b/libsrc/apple2/wherex.s index bd717a22b..4d4f856f0 100644 --- a/libsrc/apple2/wherex.s +++ b/libsrc/apple2/wherex.s @@ -10,5 +10,10 @@ _wherex: lda CH - ldx #$00 + .ifdef __APPLE2ENH__ + bit RD80VID ; In 80 column mode? + bpl :+ + lda OURCH +: .endif + ldx #>$0000 rts diff --git a/libsrc/apple2/wherey.s b/libsrc/apple2/wherey.s index daacaaba7..a3843f606 100644 --- a/libsrc/apple2/wherey.s +++ b/libsrc/apple2/wherey.s @@ -12,5 +12,5 @@ _wherey: lda CV sec sbc WNDTOP - ldx #$00 + ldx #>$0000 rts diff --git a/libsrc/apple2/write.s b/libsrc/apple2/write.s index 7b50d0705..5fb51cca6 100644 --- a/libsrc/apple2/write.s +++ b/libsrc/apple2/write.s @@ -7,6 +7,9 @@ .export _write .import rwprolog, rwcommon, rwepilog .import COUT + .ifndef __APPLE2ENH__ + .import uppercasemask + .endif .include "zeropage.inc" .include "errno.inc" @@ -84,7 +87,7 @@ next: lda (ptr1),y .ifndef __APPLE2ENH__ cmp #$E0 ; Test for lowercase bcc output - and #$DF ; Convert to uppercase + and uppercasemask .endif output: jsr COUT ; Preserves X and Y diff --git a/libsrc/atari/cashdr.s b/libsrc/atari/cashdr.s index 99aefe68f..c0d64a56b 100644 --- a/libsrc/atari/cashdr.s +++ b/libsrc/atari/cashdr.s @@ -10,10 +10,10 @@ .include "atari.inc" - .import __BSS_RUN__, __STARTADDRESS__, _cas_init + .import __INIT_RUN__, __STARTADDRESS__, _cas_init .export _cas_hdr -.assert ((__BSS_RUN__ - __STARTADDRESS__ + 127) / 128) < $101, error, "File to big to load from cassette" +.assert ((__INIT_RUN__ - __STARTADDRESS__ + 127) / 128) < $101, error, "File to big to load from cassette" ; for a description of the cassette header, see De Re Atari, appendix C @@ -22,7 +22,7 @@ _cas_hdr: .byte 0 ; ignored - .byte <((__BSS_RUN__ - __STARTADDRESS__ + 127) / 128) ; # of 128-byte records to read + .byte <((__INIT_RUN__ - __STARTADDRESS__ + 127) / 128) ; # of 128-byte records to read .word __STARTADDRESS__ ; load address .word _cas_init ; init address @@ -31,6 +31,8 @@ _cas_hdr: ldy #80 sta (SAVMSC),y .endif + lda #$3c ; motor off + sta PACTL clc rts diff --git a/libsrc/atari/crt0.s b/libsrc/atari/crt0.s index 381aa699f..50e3ca7b6 100644 --- a/libsrc/atari/crt0.s +++ b/libsrc/atari/crt0.s @@ -204,6 +204,10 @@ APPMHI_save: .res 2 ; ------------------------------------------------------------------------ +.segment "INIT" ; have at least one (empty) segment of INIT, exehdr.s needs its definition + +; ------------------------------------------------------------------------ + .segment "LOWCODE" ; have at least one (empty) segment of LOWCODE, so that the next line works even if the program doesn't make use of this segment .assert (__LOWCODE_RUN__ + __LOWCODE_SIZE__ <= $4000 || __LOWCODE_RUN__ > $7FFF || __LOWCODE_SIZE__ = 0), warning, "'lowcode area' reaches into $4000..$7FFF bank memory window" ; check for LOWBSS_SIZE = 0 not needed since the only file which uses LOWBSS (irq.s) also uses LOWCODE diff --git a/libsrc/atari/exehdr.s b/libsrc/atari/exehdr.s index 7abb7c1ac..1ac9a0fbe 100644 --- a/libsrc/atari/exehdr.s +++ b/libsrc/atari/exehdr.s @@ -1,11 +1,15 @@ ; This file defines the EXE header and main chunk load header for Atari executables .export __EXEHDR__: absolute = 1 - .import __MAIN_START__, __BSS_LOAD__ + .import __MAIN_START__, __INIT_LOAD__ .segment "EXEHDR" .word $FFFF .segment "MAINHDR" .word __MAIN_START__ - .word __BSS_LOAD__ - 1 + .word __INIT_LOAD__ - 1 + +; Define the INIT segment so that __INIT_LOAD__ from above '.import' is always defined. +; The segment is normally present when linking a C program, but not necessarily when linking an assembler program. +.segment "INIT" diff --git a/libsrc/atari/getdevice.s b/libsrc/atari/getdevice.s index e0e700436..b98d3ad5f 100644 --- a/libsrc/atari/getdevice.s +++ b/libsrc/atari/getdevice.s @@ -50,7 +50,7 @@ check_device: lda #SIO_STAT sta DCOMND ; set command into DCB lda #%01000000 ; direction value, "receive data" - sta DSTATS ; set data flow directon + sta DSTATS ; set data flow direction lda #15 sta DTIMLO ; value got from DOS source lda #4 diff --git a/libsrc/atari/graphics.s b/libsrc/atari/graphics.s index 1f7844c39..8b4e8034d 100644 --- a/libsrc/atari/graphics.s +++ b/libsrc/atari/graphics.s @@ -23,7 +23,7 @@ .code -; set new grapics mode +; set new graphics mode ; gets new mode in A ; returns handle or -1 on error ; uses tmp1, tmp2, tmp3, tmp4 (in subroutines) diff --git a/libsrc/atari/mou/atrtt.s b/libsrc/atari/mou/atrtt.s index b1e53935e..f7c56e9f2 100644 --- a/libsrc/atari/mou/atrtt.s +++ b/libsrc/atari/mou/atrtt.s @@ -374,7 +374,7 @@ IRQ: ; The touch pad is read thru the paddle potentiometers. The possible ; values are 1..228. Since the maximum value is less than the X ; dimension we have to "stretch" this value. In order to use only -; divisions by powers of two, we use the following appoximation: +; divisions by powers of two, we use the following approximation: ; 320/227 = 1.4096 ; 1+1/2-1/8+1/32 = 1.4062 ; For Y we subtract 1/8 of it to get in the YMax ballpark. @@ -385,7 +385,7 @@ IRQ: ; X - ldx PADDL0 ; get X postion + ldx PADDL0 ; get X position dex ; decrement, since it's 1-based stx XPos txa @@ -445,7 +445,7 @@ IRQ: ; Y - ldx PADDL1 ; get Y postion + ldx PADDL1 ; get Y position dex ; decrement, since it's 1-based stx YPos lda #228 diff --git a/libsrc/atari/open.s b/libsrc/atari/open.s index ed3e40b2f..e7e55c54c 100644 --- a/libsrc/atari/open.s +++ b/libsrc/atari/open.s @@ -19,7 +19,7 @@ .import findfreeiocb .import incsp4 .import ldaxysp,addysp - .import ___oserror + .import ___oserror, returnFFFF .ifdef UCASE_FILENAME .import ucase_fn .endif @@ -39,9 +39,7 @@ parmok: jsr findfreeiocb lda #<EMFILE ; "too many open files" seterr: jsr ___directerrno jsr incsp4 ; clean up stack - lda #$FF - tax - rts ; return -1 + jmp returnFFFF ; process the mode argument diff --git a/libsrc/atari/read.s b/libsrc/atari/read.s index 5078b321d..b50bd2856 100644 --- a/libsrc/atari/read.s +++ b/libsrc/atari/read.s @@ -33,7 +33,7 @@ done: lda ICBLL,x ; buf len lo lda ICBLH,x ; get buf len hi tax ; to X okdone: lda #0 - sta ___oserror ; clear system dependend error code + sta ___oserror ; clear system dependent error code pla ; get buf len lo rts @@ -147,6 +147,7 @@ icbll_copy: sta dataptr+1 lda ICBLL,x sta copylen + beq copied ; length = 0 if EOF pha ; remember for return value ldy #0 ldx index @@ -159,7 +160,7 @@ copy: lda linebuf,x bne copy pla ; length - pha ; save length to return at okdone +copied: pha ; save length to return at okdone clc adc index diff --git a/libsrc/atari/siocall.s b/libsrc/atari/siocall.s index 38cbb35d5..b1c19a2d0 100644 --- a/libsrc/atari/siocall.s +++ b/libsrc/atari/siocall.s @@ -23,7 +23,7 @@ .proc __sio_call sta DCOMND ; set command into DCB - stx DSTATS ; set data flow directon + stx DSTATS ; set data flow direction jsr popax ; get buffer address sta DBUFLO ; set buffer address into DCB stx DBUFHI diff --git a/libsrc/atari/write.s b/libsrc/atari/write.s index 2ddb88ee3..b745e5211 100644 --- a/libsrc/atari/write.s +++ b/libsrc/atari/write.s @@ -21,7 +21,7 @@ write9: lda ICBLH,x ; buf len high tax lda #0 - sta ___oserror ; clear system dependend error code + sta ___oserror ; clear system dependent error code pla rts diff --git a/libsrc/atari5200/clrscr.s b/libsrc/atari5200/clrscr.s index 041f34a67..d92419b9e 100644 --- a/libsrc/atari5200/clrscr.s +++ b/libsrc/atari5200/clrscr.s @@ -7,8 +7,9 @@ .export _clrscr .include "atari5200.inc" .importzp ptr1 + .importzp screen_width, screen_height -SCRSIZE = 480 ; 20x24: size of default conio atari5200 screen +SCRSIZE = screen_width * screen_height _clrscr:lda SAVMSC ; screen memory sta ptr1 diff --git a/libsrc/atari5200/conioscreen.s b/libsrc/atari5200/conioscreen.s index 8c78fd44f..0c77b14c7 100644 --- a/libsrc/atari5200/conioscreen.s +++ b/libsrc/atari5200/conioscreen.s @@ -7,6 +7,7 @@ SCREEN_BUF_SIZE = 20 * 24 SCREEN_BUF = $4000 - SCREEN_BUF_SIZE + .import _clrscr .export screen_setup .export screen_width, screen_height .export conio_color @@ -26,24 +27,10 @@ screen_setup: lda #>SCREEN_BUF sta SAVMSC+1 - ; initialize cursor position - lda #0 - sta COLCRS_5200 - sta ROWCRS_5200 - ; clear screen buffer - ldy #<(SCREEN_BUF_SIZE-1) - ldx #>(SCREEN_BUF_SIZE-1) -clrscr: sta (SAVMSC),y - dey - cpy #$FF - bne clrscr - dex - cpx #$FF - bne clrscr + jsr _clrscr ; set default colors - lda #GTIA_COLOR_WHITE sta COLOR0 lda #GTIA_COLOR_LIGHTRED @@ -55,7 +42,6 @@ clrscr: sta (SAVMSC),y sta COLOR4 ; background ; set display list - lda #<dlist sta SDLSTL lda #>dlist @@ -82,7 +68,7 @@ dlist: .repeat 3 .byte DL_CHR20x8x2 .endrepeat - .byte DL_JVB + .byte DL_JVB .word dlist ; end of display list diff --git a/libsrc/atari5200/extra/conioscreen-20x12.s b/libsrc/atari5200/extra/conioscreen-20x12.s index aeb11cb43..c2aa0a65f 100644 --- a/libsrc/atari5200/extra/conioscreen-20x12.s +++ b/libsrc/atari5200/extra/conioscreen-20x12.s @@ -7,6 +7,7 @@ SCREEN_BUF_SIZE = 20 * 12 SCREEN_BUF = $4000 - SCREEN_BUF_SIZE + .import _clrscr .export screen_setup .export screen_width, screen_height .export conio_color @@ -26,24 +27,10 @@ screen_setup: lda #>SCREEN_BUF sta SAVMSC+1 - ; initialize cursor position - lda #0 - sta COLCRS_5200 - sta ROWCRS_5200 - ; clear screen buffer - ldy #<(SCREEN_BUF_SIZE-1) - ldx #>(SCREEN_BUF_SIZE-1) -clrscr: sta (SAVMSC),y - dey - cpy #$FF - bne clrscr - dex - cpx #$FF - bne clrscr + jsr _clrscr ; set default colors - lda #GTIA_COLOR_WHITE sta COLOR0 lda #GTIA_COLOR_LIGHTRED @@ -55,7 +42,6 @@ clrscr: sta (SAVMSC),y sta COLOR4 ; background ; set display list - lda #<dlist sta SDLSTL lda #>dlist @@ -82,7 +68,7 @@ dlist: .repeat 3 .byte DL_CHR20x16x2 .endrepeat - .byte DL_JVB + .byte DL_JVB .word dlist ; end of display list diff --git a/libsrc/atari7800/joy/atari7800-stdjoy.s b/libsrc/atari7800/joy/atari7800-stdjoy.s index 59f656ada..c24e87e29 100644 --- a/libsrc/atari7800/joy/atari7800-stdjoy.s +++ b/libsrc/atari7800/joy/atari7800-stdjoy.s @@ -53,13 +53,20 @@ JOY_COUNT = 2 ; Number of joysticks we support ; Must return an JOY_ERR_xx code in a/x. ; +PB2 = $04 ; Joystick 0 +PB4 = $10 ; Joystick 1 + INSTALL: ; Assume 7800 2-button controller, can change ; to 2600 1-button later - lda #$14 - sta CTLSWB ; enable 2-button 7800 controller 1: set pin 6 to output + lda #(PB2 | PB4) + ; enable 2-button 7800 controllers on both ports + ; by setting PB2 and PB4 to output + sta CTLSWB + ; enable 2-button 7800 controllers by setting + ; the outputs to 0; (INPT4 and INPT5) high ldy #$00 - sty SWCHB ; enable 2-button 7800 controller 2: pull pin 6 (INPT4) high + sty SWCHB reset: lda #JOY_ERR_OK @@ -88,6 +95,28 @@ COUNT: ; ------------------------------------------------------------------------ ; READ: Read a particular joystick passed in A for 2 fire buttons. +readdualbuttons0: + ldy #0 ; ........ + bit INPT0 ; Check for right button + bpl L1 + ldy #2 ; ......2. +L1: bit INPT1 ; Check for left button + bpl L2 + iny ; ......21 +L2: tya + rts + +readdualbuttons1: + ldy #0 ; ........ + bit INPT2 ; Check for right button + bpl L1 + ldy #2 ; ......2. +L3: bit INPT3 ; Check for left button + bpl L2 + iny ; ......21 +L4: tya + rts + readbuttons: ; Y has joystick of interest 0/1 ; return value: @@ -97,42 +126,48 @@ readbuttons: ; $03: both buttons ; preserves X tya - beq L5 + beq readbuttons0 +readbuttons1: ; Joystick 1 processing - ; 7800 joystick 1 buttons - ldy #0 ; ........ - bit INPT2 ; Check for right button - bpl L1 - ldy #2 ; ......2. -L1: bit INPT3 ;Check for left button - bpl L2 - iny ; ......21 -L2: tya - bne L4 ; 7800 mode joystick worked - ; 2600 Joystick 1 + ; Start by checking for single button 2600 joystick bit INPT5 - bmi L4 -L3: iny ; .......1 - lda #0 ; Fallback to 2600 joystick mode - sta CTLSWB -L4: tya ; ......21 + bpl singlebtn1detected + jmp readdualbuttons1 +singlebtn1detected: + ; Single button joystick detected but could be dual + jsr readdualbuttons1 + bne L5 ; It was a dual button press + ; It was a single button press + bit INPT5 + bmi L5 + iny ; .......1 + lda #PB4 ; Joystick 1 is a single button unit + clc + adc SWCHB + sta SWCHB ; Cut power from the dual button circuit +L5: tya ; ......21 rts -L5: ; Joystick 0 processing - ; 7800 joystick 0 buttons - ldy #0 ; ........ - bit INPT0 ; Check for right button - bpl L6 - ldy #2 ; ......2. -L6: bit INPT1 ;Check for left button - bpl L7 - iny ; ......21 -L7: tya - bne L4 ; 7800 mode joystick worked - ; 2600 Joystick 0 +readbuttons0: + ; Joystick 0 processing + ; Start by checking for single button 2600 joystick bit INPT4 - bmi L4 - bpl L3 + bpl singlebtn0detected + jmp readdualbuttons0 +singlebtn0detected: + ; Single button joystick detected but could be dual + jsr readdualbuttons0 + bne L6 ; It was a dual button press + ; It was a single button press + bit INPT4 + bmi L6 + iny ; .......1 + lda #PB2 ; Joystick 0 is a single button unit + clc + adc SWCHB + sta SWCHB ; Cut power from the dual button circuit +L6: tya ; ......21 + rts READ: tay ; Store joystick 0/1 in Y diff --git a/libsrc/atmos/ser/atmos-acia.s b/libsrc/atmos/ser/atmos-acia.s index f679125d1..1509803d2 100644 --- a/libsrc/atmos/ser/atmos-acia.s +++ b/libsrc/atmos/ser/atmos-acia.s @@ -227,14 +227,8 @@ InvBaud:lda #<SER_ERR_BAUD_UNAVAIL ; returned. SER_GET: - ldy SendFreeCnt ; Send data if necessary - iny ; Y == $FF? - beq :+ - lda #$00 ; TryHard = false - jsr TryToSend - ; Check for buffer empty -: lda RecvFreeCnt ; (25) + lda RecvFreeCnt ; (25) cmp #$FF bne :+ lda #SER_ERR_NO_DATA @@ -269,20 +263,21 @@ SER_GET: SER_PUT: ; Try to send ldy SendFreeCnt - iny ; Y = $FF? + cpy #$FF ; Nothing to flush beq :+ pha lda #$00 ; TryHard = false jsr TryToSend pla - ; Put byte into send buffer & send -: ldy SendFreeCnt + ; Reload SendFreeCnt after TryToSend + ldy SendFreeCnt bne :+ lda #SER_ERR_OVERFLOW ldx #0 ; return value is char rts + ; Put byte into send buffer & send : ldy SendTail sta SendBuf,y inc SendTail @@ -329,19 +324,19 @@ SER_IRQ: and #$08 beq Done ; Jump if no ACIA interrupt lda ACIA::DATA,x ; Get byte from ACIA - ldy RecvFreeCnt ; Check if we have free space left + ldx RecvFreeCnt ; Check if we have free space left beq Flow ; Jump if no space in receive buffer ldy RecvTail ; Load buffer pointer sta RecvBuf,y ; Store received byte in buffer inc RecvTail ; Increment buffer pointer dec RecvFreeCnt ; Decrement free space counter - ldy RecvFreeCnt ; Check for buffer space low - cpy #33 + cpx #33 bcc Flow ; Assert flow control if buffer space low rts ; Interrupt handled (carry already set) ; Assert flow control if buffer space too low -Flow: lda RtsOff +Flow: ldx Index ; Reload port + lda RtsOff sta ACIA::CMD,x sta Stopped sec ; Interrupt handled @@ -352,12 +347,13 @@ Done: rts TryToSend: sta tmp1 ; Remember tryHard flag -Again: lda SendFreeCnt +NextByte: + lda SendFreeCnt cmp #$FF beq Quit ; Bail out ; Check for flow stopped - lda Stopped +Again: lda Stopped bne Quit ; Bail out ; Check that ACIA is ready to send @@ -374,4 +370,4 @@ Send: ldy SendHead sta ACIA::DATA inc SendHead inc SendFreeCnt - jmp Again + jmp NextByte diff --git a/libsrc/atmos/waitvsync.s b/libsrc/atmos/waitvsync.s new file mode 100644 index 000000000..85e50a795 --- /dev/null +++ b/libsrc/atmos/waitvsync.s @@ -0,0 +1,18 @@ +; +; Written by Stefan Haubenthal <polluks@sdf.org>, requires VSync hack +; +; void waitvsync (void); +; + + .export _waitvsync + + .include "atmos.inc" + +.proc _waitvsync + + lda #%00010000 ; CB1 +wait: and VIA::PRA2 + bne wait + rts + +.endproc diff --git a/libsrc/c128/break.s b/libsrc/c128/break.s index 092ca3469..450183dc3 100644 --- a/libsrc/c128/break.s +++ b/libsrc/c128/break.s @@ -125,11 +125,10 @@ uservec: jmp $FFFF ; Patched at runtime .data -; Old break vector preceeded by a jump opcode +; Old break vector preceded by a jump opcode brk_old: jmp $0000 -; Indirect vectors preceeded by a jump opcode +; Indirect vectors preceded by a jump opcode brk_ind: jmp $0000 - diff --git a/libsrc/c128/ser/c128-swlink.s b/libsrc/c128/ser/c128-swlink.s index 7d36eb5bc..796dc3b3b 100644 --- a/libsrc/c128/ser/c128-swlink.s +++ b/libsrc/c128/ser/c128-swlink.s @@ -134,7 +134,7 @@ ParityTable: ; Interrupt stub that is copied into low RAM. The startup code uses a special ; memory configuration with just kernal and I/O enabled (anything else is RAM). ; The NMI handler in ROM will switch back to a configuration where just the -; low 16K RAM are accessible. So we have to copy a smal piece of code into +; low 16K RAM are accessible. So we have to copy a small piece of code into ; low RAM that enables the cc65 configuration and then jumps to the real NMI ; handler. @@ -296,7 +296,7 @@ SER_CLOSE: lda #%00001010 sta ACIA_CMD -; Initalize buffers. Returns zero in a +; Initialize buffers. Returns zero in a jsr InitBuffers @@ -314,15 +314,10 @@ SER_CLOSE: ; SER_GET: - ldx SendFreeCnt ; Send data if necessary - inx ; X == $FF? - beq @L1 - lda #$00 - jsr TryToSend ; Check for buffer empty -@L1: lda RecvFreeCnt ; (25) + lda RecvFreeCnt ; (25) cmp #$ff bne @L2 lda #SER_ERR_NO_DATA @@ -362,21 +357,23 @@ SER_PUT: ; Try to send ldx SendFreeCnt - inx ; X = $ff? + cpx #$FF ; Nothing to flush beq @L2 pha lda #$00 jsr TryToSend pla -; Put byte into send buffer & send +; Reload SendFreeCnt after TryToSend -@L2: ldx SendFreeCnt - bne @L3 + ldx SendFreeCnt + bne @L2 lda #SER_ERR_OVERFLOW ; X is already zero rts -@L3: ldx SendTail +; Put byte into send buffer & send + +@L2: ldx SendTail sta SendBuf,x inc SendTail dec SendFreeCnt @@ -466,25 +463,25 @@ NmiHandler: sta tmp1 ; Remember tryHard flag @L0: lda SendFreeCnt cmp #$ff - beq @L3 ; Bail out + beq @L2 ; Bail out ; Check for flow stopped @L1: lda Stopped - bne @L3 ; Bail out + bne @L2 ; Bail out ; Check that swiftlink is ready to send -@L2: lda ACIA_STATUS + lda ACIA_STATUS and #$10 - bne @L4 + bne @L3 bit tmp1 ;keep trying if must try hard - bmi @L0 -@L3: rts + bmi @L1 +@L2: rts ; Send byte and try again -@L4: ldx SendHead +@L3: ldx SendHead lda SendBuf,x sta ACIA_DATA inc SendHead diff --git a/libsrc/c128/tgi/c128-vdc.s b/libsrc/c128/tgi/c128-vdc.s index f48b530f6..edbdd6cd8 100644 --- a/libsrc/c128/tgi/c128-vdc.s +++ b/libsrc/c128/tgi/c128-vdc.s @@ -268,7 +268,7 @@ INIT: @L1: ldx #$FF stx BITMASK -; Remeber current color value +; Remember current color value ldx #VDC_COLORS jsr VDCReadReg sta OLDCOLOR diff --git a/libsrc/c128/tgi/c128-vdc2.s b/libsrc/c128/tgi/c128-vdc2.s index 4b7b17c57..6b75ee712 100644 --- a/libsrc/c128/tgi/c128-vdc2.s +++ b/libsrc/c128/tgi/c128-vdc2.s @@ -277,7 +277,7 @@ INIT: @L1: ldx #$FF stx BITMASK -; Remeber current color value +; Remember current color value ldx #VDC_COLORS jsr VDCReadReg sta OLDCOLOR diff --git a/libsrc/c128/waitvsync.s b/libsrc/c128/waitvsync.s index e4bbbf7c9..573f574a7 100644 --- a/libsrc/c128/waitvsync.s +++ b/libsrc/c128/waitvsync.s @@ -23,8 +23,8 @@ _waitvsync: @c80: ;FIXME: do we have to switch banks? + lda #$20 @l3: - lda VDC_INDEX - and #$20 + and VDC_INDEX beq @l3 rts diff --git a/libsrc/c64/emd/c64-rrr.s b/libsrc/c64/emd/c64-rrr.s index 4d175cd32..8632e5e64 100644 --- a/libsrc/c64/emd/c64-rrr.s +++ b/libsrc/c64/emd/c64-rrr.s @@ -212,8 +212,8 @@ MAP: cmp pagecount bcs return_null sta curpage - lda #<dummy ; load .A/.X with adress of data for COPYFROM-call (which expects the - ldx #>dummy ; adress in .A/.X) + lda #<dummy ; load .A/.X with address of data for COPYFROM-call (which expects the + ldx #>dummy ; address in .A/.X) jsr COPYFROM bcs return_win ; function returns pointer to window (returns always with carry set!) @@ -224,8 +224,8 @@ COMMIT: lda curpage cmp pagecount bcs return - lda #<dummy ; load .A/.X with adress of data for COPYTO-call (which expects the - ldx #>dummy ; adress in .A/.X) + lda #<dummy ; load .A/.X with address of data for COPYTO-call (which expects the + ldx #>dummy ; address in .A/.X) ;---------------------------------------------------------------------------------------- ;void __fastcall__ em_copyto (struct em_copy *copy_data); @@ -324,7 +324,7 @@ get_struct_data: ;read and process the values from the em_copy struct passed to as parameters rameter to the ;functions em_copyto and em_copyfrom - sta aux ;store adress of struct (passed in .A/.X) into a zp pointer + sta aux ;store address of struct (passed in .A/.X) into a zp pointer stx aux+1 ldy #0 ;index 0 @@ -347,7 +347,7 @@ get_struct_data: lsr lsr ;shift into bits 3 and 4 ora #$23 ;set bit 5 (select ram) and 1+2 (game/exrom setting for ULTIMAX-mode) - tax ;.X has now the value to write into $de00 to acess rr-ram at desired 16k-bank + tax ;.X has now the value to write into $de00 to access rr-ram at desired 16k-bank iny iny ;skip unused byte lda (aux),y ;read length lo-byte @@ -357,7 +357,7 @@ get_struct_data: iny lda (aux),y ;length hi-byte adc c64_ram+1 - sta len+1 ;tmp2: length, tmp3 contains end adress of transfer in c64-ram. + sta len+1 ;tmp2: length, tmp3 contains end address of transfer in c64-ram. rts ;55 bytes diff --git a/libsrc/c64/joy/c64-hitjoy.s b/libsrc/c64/joy/c64-hitjoy.s index a9d454fd0..c57f45780 100644 --- a/libsrc/c64/joy/c64-hitjoy.s +++ b/libsrc/c64/joy/c64-hitjoy.s @@ -102,7 +102,7 @@ readadapter: lda #%00010001 sta $dd0e ; control register a ; timer: start - ; continous + ; continuous ; forced load ; serial port: input @@ -110,7 +110,7 @@ readadapter: lda #%01010001 sta $dc0e ; control register a ; timer: start - ; continous + ; continuous ; forced load ; serial port: output diff --git a/libsrc/c64/ser/c64-swlink.s b/libsrc/c64/ser/c64-swlink.s index 81c9916a6..2c06bb39b 100644 --- a/libsrc/c64/ser/c64-swlink.s +++ b/libsrc/c64/ser/c64-swlink.s @@ -270,7 +270,7 @@ SER_CLOSE: lda #%00001010 sta ACIA_CMD -; Initalize buffers. Returns zero in a +; Initialize buffers. Returns zero in a jsr InitBuffers @@ -288,15 +288,10 @@ SER_CLOSE: ; SER_GET: - ldx SendFreeCnt ; Send data if necessary - inx ; X == $FF? - beq @L1 - lda #$00 - jsr TryToSend ; Check for buffer empty -@L1: lda RecvFreeCnt ; (25) + lda RecvFreeCnt ; (25) cmp #$ff bne @L2 lda #SER_ERR_NO_DATA @@ -336,21 +331,23 @@ SER_PUT: ; Try to send ldx SendFreeCnt - inx ; X = $ff? + cpx #$FF ; Nothing to flush beq @L2 pha lda #$00 jsr TryToSend pla -; Put byte into send buffer & send +; Reload SendFreeCnt after TryToSend -@L2: ldx SendFreeCnt - bne @L3 + ldx SendFreeCnt + bne @L2 lda #SER_ERR_OVERFLOW ; X is already zero rts -@L3: ldx SendTail +; Put byte into send buffer & send + +@L2: ldx SendTail sta SendBuf,x inc SendTail dec SendFreeCnt @@ -443,25 +440,25 @@ NmiHandler: sta tmp1 ; Remember tryHard flag @L0: lda SendFreeCnt cmp #$ff - beq @L3 ; Bail out + beq @L2 ; Bail out ; Check for flow stopped @L1: lda Stopped - bne @L3 ; Bail out + bne @L2 ; Bail out ; Check that swiftlink is ready to send -@L2: lda ACIA_STATUS + lda ACIA_STATUS and #$10 - bne @L4 + bne @L3 bit tmp1 ;keep trying if must try hard - bmi @L0 -@L3: rts + bmi @L1 +@L2: rts ; Send byte and try again -@L4: ldx SendHead +@L3: ldx SendHead lda SendBuf,x sta ACIA_DATA inc SendHead diff --git a/libsrc/cbm/cbm_read.s b/libsrc/cbm/cbm_read.s index 8a9939eca..c449567b1 100644 --- a/libsrc/cbm/cbm_read.s +++ b/libsrc/cbm/cbm_read.s @@ -24,7 +24,7 @@ ; /* the kernal routine BASIN sets ST to EOF if the end of file ; ** is reached the first time, then we have store tmp. ; ** every subsequent call returns EOF and READ ERROR in ST, then -; ** we have to exit the loop here immediatly. +; ** we have to exit the loop here immediately. ; */ ; if (cbm_k_readst() & 0xBF) break; ; @@ -40,7 +40,7 @@ .export _cbm_read .importzp ptr1, ptr2, ptr3, tmp1 - .import popax, popa + .import popax, popa, returnFFFF .import ___oserror @@ -107,7 +107,4 @@ _cbm_read: ; CHKIN failed @E1: sta ___oserror - lda #$FF - tax - rts ; return -1 - + jmp returnFFFF diff --git a/libsrc/cbm/cbm_write.s b/libsrc/cbm/cbm_write.s index 18c6f4684..a4ecfbe3f 100644 --- a/libsrc/cbm/cbm_write.s +++ b/libsrc/cbm/cbm_write.s @@ -32,7 +32,7 @@ .export _cbm_write .importzp ptr1, ptr2, ptr3 - .import popax, popa + .import popax, popa, returnFFFF .import ___oserror @@ -88,7 +88,4 @@ _cbm_write: ; Error entry, error code is in A @E2: sta ___oserror - lda #$FF - tax - rts ; return -1 - + jmp returnFFFF diff --git a/libsrc/cbm/exec.c b/libsrc/cbm/exec.c index b9c1bdc96..49ab53553 100644 --- a/libsrc/cbm/exec.c +++ b/libsrc/cbm/exec.c @@ -89,7 +89,7 @@ int __fastcall__ exec (const char* progname, const char* cmdline) } utoa (dv, basic.unit, 10); - /* Tape files can be openned only once; skip this test for the Datasette. */ + /* Tape files can be opened only once; skip this test for the Datasette. */ if (dv != 1) { /* Don't try to run a program that can't be found. */ fd = open (progname, O_RDONLY); diff --git a/libsrc/cbm/getdevice.s b/libsrc/cbm/getdevice.s index 08ba6d5f8..b68e716b4 100644 --- a/libsrc/cbm/getdevice.s +++ b/libsrc/cbm/getdevice.s @@ -54,7 +54,7 @@ next: inx lda ST -; Either the Kernal calls above were successfull or there was +; Either the Kernal calls above were successful or there was ; already a cmdchannel to the device open - which is a pretty ; good indication of its existence ;-) diff --git a/libsrc/cbm/loadaddr.s b/libsrc/cbm/loadaddr.s index 0675dd67d..4329f201f 100644 --- a/libsrc/cbm/loadaddr.s +++ b/libsrc/cbm/loadaddr.s @@ -2,7 +2,7 @@ ; Ullrich von Bassewitz, 2010-11-13 ; ; This module supplies the load address that is expected by Commodore -; machines in the first two bytes of an excutable disk file. +; machines in the first two bytes of an executable disk file. ; diff --git a/libsrc/cbm510/joy/cbm510-std.s b/libsrc/cbm510/joy/cbm510-std.s index f7cbb2cdc..640b0d2a8 100644 --- a/libsrc/cbm510/joy/cbm510-std.s +++ b/libsrc/cbm510/joy/cbm510-std.s @@ -117,7 +117,7 @@ READ: ldx #$0F ; Switch to the system bank lsr tmp1 lsr tmp1 -; Mask the relavant bits, get the push button bit +; Mask the relevant bits, get the push button bit @L2: asl a ; push button bit into carry lda tmp1 diff --git a/libsrc/cbm510/ser/cbm510-std.s b/libsrc/cbm510/ser/cbm510-std.s index cc58c1233..800007492 100644 --- a/libsrc/cbm510/ser/cbm510-std.s +++ b/libsrc/cbm510/ser/cbm510-std.s @@ -244,15 +244,10 @@ InvBaud: ; SER_GET: - ldx SendFreeCnt ; Send data if necessary - inx ; X == $FF? - beq @L1 - lda #$00 - jsr TryToSend ; Check for buffer empty -@L1: lda RecvFreeCnt + lda RecvFreeCnt cmp #$ff bne @L2 lda #SER_ERR_NO_DATA @@ -292,21 +287,23 @@ SER_PUT: ; Try to send ldx SendFreeCnt - inx ; X = $ff? + cpx #$FF ; Nothing to flush beq @L2 pha lda #$00 jsr TryToSend pla -; Put byte into send buffer & send +; Reload SendFreeCnt after TryToSend -@L2: ldx SendFreeCnt - bne @L3 + ldx SendFreeCnt + bne @L2 lda #SER_ERR_OVERFLOW ; X is already zero rts -@L3: ldx SendTail +; Put byte into send buffer & send + +@L2: ldx SendTail sta SendBuf,x inc SendTail dec SendFreeCnt @@ -395,31 +392,31 @@ SER_IRQ: sta IndReg ; Switch to the system bank @L0: lda SendFreeCnt cmp #$ff - beq @L3 ; Bail out + beq @L2 ; Bail out ; Check for flow stopped @L1: lda Stopped - bne @L3 ; Bail out + bne @L2 ; Bail out ; Check that swiftlink is ready to send -@L2: ldy #ACIA::STATUS + ldy #ACIA::STATUS lda (acia),y and #$10 - bne @L4 + bne @L3 bit tmp1 ; Keep trying if must try hard - bmi @L0 + bmi @L1 ; Switch back the bank and return -@L3: lda ExecReg +@L2: lda ExecReg sta IndReg rts ; Send byte and try again -@L4: ldx SendHead +@L3: ldx SendHead lda SendBuf,x ldy #ACIA::DATA sta (acia),y diff --git a/libsrc/cbm610/ser/cbm610-std.s b/libsrc/cbm610/ser/cbm610-std.s index f7ddde935..45b18eadf 100644 --- a/libsrc/cbm610/ser/cbm610-std.s +++ b/libsrc/cbm610/ser/cbm610-std.s @@ -245,15 +245,10 @@ InvBaud: ; SER_GET: - ldx SendFreeCnt ; Send data if necessary - inx ; X == $FF? - beq @L1 - lda #$00 - jsr TryToSend ; Check for buffer empty -@L1: lda RecvFreeCnt + lda RecvFreeCnt cmp #$ff bne @L2 lda #SER_ERR_NO_DATA @@ -293,21 +288,23 @@ SER_PUT: ; Try to send ldx SendFreeCnt - inx ; X = $ff? + cpx #$ff ; Nothing to flush beq @L2 pha lda #$00 jsr TryToSend pla -; Put byte into send buffer & send +; Reload SendFreeCnt after TryToSend -@L2: ldx SendFreeCnt - bne @L3 + ldx SendFreeCnt + bne @L2 lda #SER_ERR_OVERFLOW ; X is already zero rts -@L3: ldx SendTail +; Put byte into send buffer & send + +@L2: ldx SendTail sta SendBuf,x inc SendTail dec SendFreeCnt @@ -395,31 +392,31 @@ SER_IRQ: sta IndReg ; Switch to the system bank @L0: lda SendFreeCnt cmp #$ff - beq @L3 ; Bail out + beq @L2 ; Bail out ; Check for flow stopped @L1: lda Stopped - bne @L3 ; Bail out + bne @L2 ; Bail out ; Check that swiftlink is ready to send -@L2: ldy #ACIA::STATUS + ldy #ACIA::STATUS lda (acia),y and #$10 - bne @L4 + bne @L3 bit tmp1 ; Keep trying if must try hard - bmi @L0 + bmi @L1 ; Switch back the bank and return -@L3: lda ExecReg +@L2: lda ExecReg sta IndReg rts ; Send byte and try again -@L4: ldx SendHead +@L3: ldx SendHead lda SendBuf,x ldy #ACIA::DATA sta (acia),y diff --git a/libsrc/common/_printf.s b/libsrc/common/_printf.s index a0074583e..d7eeb072d 100644 --- a/libsrc/common/_printf.s +++ b/libsrc/common/_printf.s @@ -13,6 +13,7 @@ .import _strlower, _strlen .macpack generic + .macpack cpu ; ---------------------------------------------------------------------------- ; We will store variables into the register bank in the zeropage. Define @@ -37,7 +38,11 @@ FCount = ptr2 GetFormatChar: ldy #0 + .if (.cpu .bitand ::CPU_ISET_65SC02) + lda (Format) + .else lda (Format),y + .endif IncFormatPtr: inc Format bne @L1 @@ -110,7 +115,11 @@ GetIntArg: lda (ArgList),y tax dey + .if (.cpu .bitand ::CPU_ISET_65SC02) + lda (ArgList) + .else lda (ArgList),y + .endif rts ; ---------------------------------------------------------------------------- @@ -135,9 +144,9 @@ ReadInt: pha ; Save digit value lda ptr1 ldx ptr1+1 - asl ptr1 + asl a rol ptr1+1 ; * 2 - asl ptr1 + asl a rol ptr1+1 ; * 4, assume carry clear adc ptr1 sta ptr1 @@ -265,10 +274,16 @@ Save: lda regbank,y ; Initialize the output counter in the output descriptor to zero lda #0 + .if (.cpu .bitand ::CPU_ISET_65SC02) + sta (OutData) + ldy #$01 + sta (OutData),y + .else tay sta (OutData),y iny sta (OutData),y + .endif ; Get the output function from the output descriptor and remember it @@ -338,7 +353,11 @@ MainLoop: sta (sp),y dey lda FCount + .if (.cpu .bitand ::CPU_ISET_65SC02) + sta (sp) + .else sta (sp),y + .endif jsr CallOutFunc ; Call the output function ; We're back from out(), or we didn't call it. Check for end of string. @@ -551,10 +570,16 @@ CheckCount: jsr GetIntArg sta ptr1 stx ptr1+1 ; Get user supplied pointer + .if (.cpu .bitand ::CPU_ISET_65SC02) + lda (OutData) ; Low byte of OutData->ccount + sta (ptr1) + ldy #1 + .else ldy #0 lda (OutData),y ; Low byte of OutData->ccount sta (ptr1),y iny + .endif lda (OutData),y ; High byte of OutData->ccount sta (ptr1),y jmp MainLoop ; Done diff --git a/libsrc/common/_time_t_to_tm.s b/libsrc/common/_time_t_to_tm.s new file mode 100644 index 000000000..9bcf84184 --- /dev/null +++ b/libsrc/common/_time_t_to_tm.s @@ -0,0 +1,129 @@ +; +; Colin Leroy-Mira, 2024 +; +; struct tm* __fastcall__ _time_t_to_tm (const time_t t) +; +; Helper to gmtime and localtime. Breaks down a number of +; seconds since Jan 1, 1970 into days, hours and seconds, +; so that each of them fits in 16 bits; passes the +; result to _mktime which fixes all values in the struct, +; and returns a pointer to the struct to callers. +; + + .export __time_t_to_tm + .import udiv32, _mktime + .importzp sreg, tmp3, ptr1, ptr2, ptr3, ptr4 + + .include "time.inc" + + .macpack cpu + +__time_t_to_tm: + ; Divide number of seconds since epoch, in ptr1:sreg, + ; by 86400 to get the number of days since epoch, and + ; the number of seconds today in the remainder. + + ; Load t as dividend (sreg is already set by the caller) + sta ptr1 + stx ptr1+1 + + ; Load 86400 as divisor + lda #$80 + sta ptr3 + lda #$51 + sta ptr3+1 + lda #$01 + sta ptr4 + lda #$00 + sta ptr4+1 + + ; Clear TM buf while we have zero in A + ldx #.sizeof(tm)-1 +: sta TM,x + dex + bpl :- + + ; Divide t/86400 + jsr udiv32 + + ; Store the quotient (the number of full days), and increment + ; by one as epoch starts at day 1. + clc + lda ptr1 + adc #1 + sta TM + tm::tm_mday + lda ptr1+1 + adc #0 + sta TM + tm::tm_mday+1 + + ; Now divide the number of remaining seconds by 3600, + ; to get the number of hours, and the seconds in the + ; current hour, in neat 16-bit integers. + + ; Load the previous division's remainder (in ptr2:tmp3:tmp4) + ; as dividend + lda ptr2 + sta ptr1 + lda ptr2+1 + sta ptr1+1 + lda tmp3 + sta sreg + ; We ignore the high byte stored in tmp4 because it will be + ; zero. We'll zero sreg+1 right below, when we'll have + ; a convenient zero already in A. + + ; Load divisor + lda #<3600 + sta ptr3 + lda #>3600 + sta ptr3+1 + + ; Zero the two high bytes of the divisor and the high byte + ; of the dividend. + .if .cpu .bitand CPU_ISET_65SC02 + stz ptr4 + stz ptr4+1 + stz sreg+1 + .else + lda #$00 + sta ptr4 + sta ptr4+1 + sta sreg+1 + .endif + + ; Do the division + jsr udiv32 + + ; Store year + lda #70 + sta TM + tm::tm_year + + ; Store hours (the quotient of the last division) + lda ptr1 + sta TM + tm::tm_hour + lda ptr1+1 + sta TM + tm::tm_hour+1 + + ; Store seconds (the remainder of the last division) + lda ptr2 + sta TM + tm::tm_sec + lda ptr2+1 + sta TM + tm::tm_sec+1 + + ; The rest of the struct tm fields are zero. mktime + ; will take care of shifting extra seconds to minutes, + ; and extra days to months and years. + + ; Call mktime + lda #<TM + ldx #>TM + jsr _mktime + + ; And return our pointer + lda #<TM + ldx #>TM + rts + + .bss + +TM: .tag tm diff --git a/libsrc/common/asctime.s b/libsrc/common/asctime.s new file mode 100644 index 000000000..efcf34b41 --- /dev/null +++ b/libsrc/common/asctime.s @@ -0,0 +1,81 @@ +; +; Colin Leroy-Mira, 2024 +; +; char* __fastcall__ asctime (const struct tm* timep) +; + + .export _asctime + .import _strftime, pushax + .importzp ptr1 + .include "time.inc" + + .macpack cpu + +; ------------------------------------------------------------------------ +; Special values + +; We need to be able to store up to 38 bytes: +; 1234567890123456789012345678901234567 +; "Wednesday September ..1 00:00:00 1970" +MAX_BUF_LEN = 38 + +; ------------------------------------------------------------------------ +; Code + +_asctime: + ; Backup timep + .if (.cpu .bitand ::CPU_ISET_65SC02) + pha + phx + .else + sta ptr1 + stx ptr1+1 + .endif + + ; Push buf + lda #<buf + ldx #>buf + jsr pushax + + ; Push sizeof(buf) + lda #<MAX_BUF_LEN + ldx #>MAX_BUF_LEN + jsr pushax + + ; Push format string + lda #<fmt + ldx #>fmt + jsr pushax + + ; Restore timep + .if (.cpu .bitand ::CPU_ISET_65SC02) + plx + pla + .else + lda ptr1 + ldx ptr1+1 + .endif + + ; Call formatter + jsr _strftime + + ; Check return status + bne :+ + cpx #$00 + bne :+ + rts + +: lda #<buf + ldx #>buf + rts + + .data + +fmt: .byte '%' + .byte 'c' + .byte $0A + .byte $00 + + .bss + +buf: .res MAX_BUF_LEN diff --git a/libsrc/common/checkferror.s b/libsrc/common/checkferror.s new file mode 100644 index 000000000..736fa3ccd --- /dev/null +++ b/libsrc/common/checkferror.s @@ -0,0 +1,24 @@ +; +; Colin Leroy-Mira, 2024 +; +; Helper to check for file opened, not eof, not ferror +; Expects file pointer in ptr1, +; Returns with Z flag set if everything is OK, +; Destroys A, X, Y, +; Sets file flags in A +; + + .export checkferror + .importzp ptr1 + + .include "_file.inc" + +checkferror: + ldy #_FILE::f_flags + lda (ptr1),y + tax + and #(_FOPEN|_FERROR|_FEOF); Check for file open, error/eof + tay + txa + cpy #_FOPEN + rts diff --git a/libsrc/common/ctype.s b/libsrc/common/ctype.s index 220ad79c1..d51b1bf2b 100644 --- a/libsrc/common/ctype.s +++ b/libsrc/common/ctype.s @@ -7,7 +7,7 @@ ; ; See "LICENSE" file for legal information. ; -; Character specification table, matching serveral consoles. +; Character specification table, matching several consoles. ; .include "ctypetable.inc" diff --git a/libsrc/common/divt.s b/libsrc/common/divt.s index 7f2b4e1bb..52b6efd04 100644 --- a/libsrc/common/divt.s +++ b/libsrc/common/divt.s @@ -3,7 +3,7 @@ ; 2002-10-22, Greg King ; ; This signed-division function returns both the quotient and the remainder, -; in this structure: +; in this structure: (quotient in sreg, remainder in AX) ; ; typedef struct { ; int rem, quot; diff --git a/libsrc/common/fclose.s b/libsrc/common/fclose.s index 2368bf9f6..f6c57841e 100644 --- a/libsrc/common/fclose.s +++ b/libsrc/common/fclose.s @@ -7,7 +7,7 @@ .export _fclose - .import _close + .import _close, ___directerrno .importzp ptr1 .include "errno.inc" @@ -31,10 +31,7 @@ ; File is not open lda #EINVAL - jsr ___seterrno - lda #$FF ; Return -1 - tax - rts + jmp ___directerrno ; File is open. Reset the flags and close the file. @@ -47,4 +44,3 @@ jmp _close ; Will set errno and return an error flag .endproc - diff --git a/libsrc/common/fgetc.c b/libsrc/common/fgetc.c deleted file mode 100644 index b4ba18d73..000000000 --- a/libsrc/common/fgetc.c +++ /dev/null @@ -1,58 +0,0 @@ -/* -** fgetc.c -** -** (C) Copyright 1998, 2002 Ullrich von Bassewitz (uz@cc65.org) -** -*/ - - - -#include <stdio.h> -#include <unistd.h> -#include "_file.h" - - - -/*****************************************************************************/ -/* Code */ -/*****************************************************************************/ - - - -int __fastcall__ fgetc (register FILE* f) -{ - unsigned char c; - - /* Check if the file is open or if there is an error condition */ - if ((f->f_flags & _FOPEN) == 0 || (f->f_flags & (_FERROR | _FEOF)) != 0) { - return EOF; - } - - /* If we have a pushed back character, return it */ - if (f->f_flags & _FPUSHBACK) { - f->f_flags &= ~_FPUSHBACK; - return f->f_pushback; - } - - /* Read one byte */ - switch (read (f->f_fd, &c, 1)) { - - case -1: - /* Error */ - f->f_flags |= _FERROR; - return EOF; - - case 0: - /* EOF */ - f->f_flags |= _FEOF; - return EOF; - - default: - /* Char read */ - return c; - - } -} - - - diff --git a/libsrc/common/fgetc.s b/libsrc/common/fgetc.s new file mode 100644 index 000000000..34d4df3aa --- /dev/null +++ b/libsrc/common/fgetc.s @@ -0,0 +1,94 @@ +; +; Colin Leroy-Mira, 2024 +; +; int __fastcall__ fgetc (register FILE* f) +; + + .export _fgetc + .import _read, checkferror + .import pusha0, pushax, popptr1, incsp2, returnFFFF + .importzp ptr1 + + .include "stdio.inc" + .include "_file.inc" + + .macpack cpu + +_fgetc: + sta ptr1 + stx ptr1+1 + jsr pushax ; Backup our ptr + + jsr checkferror + bne ret_eof + + .if (.cpu .bitand ::CPU_ISET_65SC02) + bit #_FPUSHBACK ; Check for pushed back char + beq do_read + .else + tax + and #_FPUSHBACK ; Check for pushed back char + beq do_read + txa + .endif + + and #<(~_FPUSHBACK) ; Reset flag + sta (ptr1),y + + .assert _FILE::f_pushback = _FILE::f_flags+1, error + iny + jsr incsp2 ; Drop our ptr copy + lda (ptr1),y ; Return pushed back char + ldx #$00 + rts + +do_read: + ; Push _read parameters + ldy #_FILE::f_fd + lda (ptr1),y + jsr pusha0 + + lda #<c + ldx #>c + jsr pushax + + lda #$01 + ldx #$00 + + ; Read + jsr _read + + ; Check for errors + cmp #$00 + beq set_feof + + cmp #<(-1) + beq set_ferror + + jsr incsp2 + ; Return char + ldx #$00 + lda c + rts + +ret_eof: + jsr incsp2 + jmp returnFFFF + +set_ferror: + lda #_FERROR + bne set_err +set_feof: + lda #_FEOF +set_err: + pha + jsr popptr1 + pla + ldy #_FILE::f_flags + ora (ptr1),y + sta (ptr1),y + jmp returnFFFF + + .bss + +c: .res 1 diff --git a/libsrc/common/fgets.c b/libsrc/common/fgets.c deleted file mode 100644 index 21a991fd6..000000000 --- a/libsrc/common/fgets.c +++ /dev/null @@ -1,68 +0,0 @@ -/* -** Ullrich von Bassewitz, 11.08.1998 -** -** char* fgets (char* s, int size, FILE* f); -*/ - - - -#include <stdio.h> -#include <errno.h> -#include "_file.h" - - - -/*****************************************************************************/ -/* Code */ -/*****************************************************************************/ - - - -char* __fastcall__ fgets (char* s, unsigned size, register FILE* f) -{ - register char* p = s; - unsigned i; - int c; - - if (size == 0) { - /* Invalid size */ - return (char*) _seterrno (EINVAL); - } - - /* Read input */ - i = 0; - while (--size) { - - /* Get next character */ - if ((c = fgetc (f)) == EOF) { - /* Error or EOF */ - if ((f->f_flags & _FERROR) != 0 || i == 0) { - /* ERROR or EOF on first char */ - *p = '\0'; - return 0; - } else { - /* EOF with data already read */ - break; - } - } - - /* One char more */ - *p = c; - ++p; - ++i; - - /* Stop at end of line */ - if ((char)c == '\n') { - break; - } - } - - /* Terminate the string */ - *p = '\0'; - - /* Done */ - return s; -} - - - diff --git a/libsrc/common/fgets.s b/libsrc/common/fgets.s new file mode 100644 index 000000000..c25664f19 --- /dev/null +++ b/libsrc/common/fgets.s @@ -0,0 +1,119 @@ +; +; Colin Leroy-Mira, 2024 +; +; char* __fastcall__ fgets (char* s, unsigned size, register FILE* f) +; + + .export _fgets + .import _fgetc, popptr1, pushptr1, popax, pushax, return0, ___errno + .importzp ptr1, ptr4 + + .feature string_escapes + + .include "errno.inc" + .include "stdio.inc" + .include "_file.inc" + + .macpack cpu + +terminate_ptr: + lda #$00 + tax + .if (.cpu .bitand ::CPU_ISET_65SC02) + sta (ptr4) + .else + tay + sta (ptr4),y + .endif + rts + +_fgets: + sta ptr1 + stx ptr1+1 + + jsr popax + sta size + stx size+1 + + jsr popax + sta ptr4 + stx ptr4+1 + sta buf + stx buf+1 + + .if (.cpu .bitand ::CPU_ISET_65SC02) + stz didread + .else + lda #$00 ; We have read nothing yet + sta didread + .endif + + ; Check size + lda size + ora size+1 + bne read_loop + lda #EINVAL + sta ___errno + jmp return0 + +read_loop: + lda size ; Dec size + bne :+ + dec size+1 +: dec size + + bne :+ ; Check bound + ldx size+1 + beq done + +: jsr pushptr1 ; Push ptr1 for backup and load it to AX for fgetc + jsr _fgetc ; Read a char + + pha + jsr popptr1 ; Get ptr1 back + pla + + cpx #<EOF + beq got_eof + + ldy #$01 + sty didread ; We read at least one char + + .if (.cpu .bitand ::CPU_ISET_65SC02) + sta (ptr4) + .else + dey + sta (ptr4),y + .endif + + inc ptr4 + bne :+ + inc ptr4+1 + +: cmp #'\n' ; #'\n' should get translated properly + beq done + bne read_loop + +got_eof: + lda didread + beq stopped_at_first_char + ldy #_FILE::f_flags + lda (ptr1),y + and #_FERROR + bne stopped_at_first_char + +done: + jsr terminate_ptr + ldx #>buf + lda #<buf + rts + +stopped_at_first_char: + jmp terminate_ptr + + .bss + +c: .res 1 +buf: .res 2 +size: .res 2 +didread:.res 1 diff --git a/libsrc/common/fmisc.s b/libsrc/common/fmisc.s index 90b48e8a6..d189db8a8 100644 --- a/libsrc/common/fmisc.s +++ b/libsrc/common/fmisc.s @@ -5,7 +5,7 @@ ; .export _clearerr, _feof, _ferror, _fileno, _fflush - .import return0 + .import return0, ___directerrno .importzp ptr1 .include "_file.inc" @@ -78,10 +78,7 @@ err: rts ; If the file is not valid, fileno must set errno and return -1 error: lda #<EBADF - jsr ___seterrno - lda #$FF - tax - rts + jmp ___directerrno .endproc ; @@ -89,5 +86,3 @@ error: lda #<EBADF ; _fflush = return0 - - diff --git a/libsrc/common/fputc.c b/libsrc/common/fputc.c deleted file mode 100644 index b623949d3..000000000 --- a/libsrc/common/fputc.c +++ /dev/null @@ -1,41 +0,0 @@ -/* -** fputc.c -** -** Ullrich von Bassewitz, 02.06.1998 -*/ - - - -#include <stdio.h> -#include <unistd.h> -#include "_file.h" - - - -/*****************************************************************************/ -/* Code */ -/*****************************************************************************/ - - - -int __fastcall__ fputc (int c, register FILE* f) -{ - /* Check if the file is open or if there is an error condition */ - if ((f->f_flags & _FOPEN) == 0 || (f->f_flags & (_FERROR | _FEOF)) != 0) { - goto ReturnEOF; - } - - /* Write the byte */ - if (write (f->f_fd, &c, 1) != 1) { - /* Error */ - f->f_flags |= _FERROR; -ReturnEOF: - return EOF; - } - - /* Return the byte written */ - return c & 0xFF; -} - - - diff --git a/libsrc/common/fputc.s b/libsrc/common/fputc.s new file mode 100644 index 000000000..358723538 --- /dev/null +++ b/libsrc/common/fputc.s @@ -0,0 +1,66 @@ +; +; Colin Leroy-Mira, 2024 +; +; int __fastcall__ fputc (int c, FILE* f); +; + + .export _fputc + .importzp ptr1 + .import _write, checkferror + .import pushax, pusha0, popax, incsp2 + .import pushptr1, popptr1, returnFFFF + + .include "stdio.inc" + .include "_file.inc" + +_fputc: + sta ptr1 + stx ptr1+1 + + jsr popax ; Get char, as we'll have + sta c ; to return it anyway + stx c+1 + + jsr checkferror + bne ret_eof + + jsr pushptr1 ; Backup fp pointer + + ; Push _write parameters + ldy #_FILE::f_fd + lda (ptr1),y + jsr pusha0 + + lda #<c + ldx #>c + jsr pushax + + lda #$01 + ldx #$00 + + ; Write + jsr _write + + ; Check for errors + cmp #$01 + bne set_ferror + + ; Return char + lda c + ldx #$00 + jmp incsp2 ; Drop fp pointer copy + +ret_eof: + jmp returnFFFF + +set_ferror: + jsr popptr1 + lda #_FERROR + ldy #_FILE::f_flags + ora (ptr1),y + sta (ptr1),y + jmp returnFFFF + + .bss + +c: .res 2 diff --git a/libsrc/common/fputs.c b/libsrc/common/fputs.c deleted file mode 100644 index be476a3f0..000000000 --- a/libsrc/common/fputs.c +++ /dev/null @@ -1,28 +0,0 @@ -/* -** int fputs (const char* s, FILE* f); -** -** Ullrich von Bassewitz, 11.08.1998 -*/ - - - -#include <stdio.h> -#include <string.h> -#include <unistd.h> -#include "_file.h" - - - -int __fastcall__ fputs (const char* s, register FILE* f) -{ - /* Check if the file is open or if there is an error condition */ - if ((f->f_flags & _FOPEN) == 0 || (f->f_flags & (_FERROR | _FEOF)) != 0) { - return EOF; - } - - /* Write the string */ - return write (f->f_fd, s, strlen (s)); -} - - - diff --git a/libsrc/common/fputs.s b/libsrc/common/fputs.s new file mode 100644 index 000000000..b79a4707f --- /dev/null +++ b/libsrc/common/fputs.s @@ -0,0 +1,36 @@ +; +; Colin Leroy-Mira, 2024 +; +; int __fastcall__ fputs (const char* s, register FILE* f) +; + + .export _fputs + .importzp ptr1, ptr2 + .import _write, _strlen, checkferror + .import swapstk, pushax, returnFFFF + + .include "stdio.inc" + .include "_file.inc" + +_fputs: + sta ptr1 + stx ptr1+1 + + jsr checkferror + bne ret_eof + + ; Push _write parameters + ldy #_FILE::f_fd + lda (ptr1),y + ldx #$00 + jsr swapstk ; Push fd, get s + + jsr pushax ; Push s + + jsr _strlen ; Get length + + ; Write + jmp _write + +ret_eof: + jmp returnFFFF diff --git a/libsrc/common/fread.s b/libsrc/common/fread.s index 647a7f2c8..b39b9d748 100644 --- a/libsrc/common/fread.s +++ b/libsrc/common/fread.s @@ -20,6 +20,7 @@ .include "_file.inc" .macpack generic + .macpack cpu ; ------------------------------------------------------------------------ ; Code @@ -47,13 +48,21 @@ ldy #_FILE::f_flags lda (file),y + .if (.cpu .bitand ::CPU_ISET_65SC02) + bit #_FOPEN ; Is the file open? + .else and #_FOPEN ; Is the file open? + .endif beq @L1 ; Branch if no ; Check if the stream is in an error state + .if (.cpu .bitand ::CPU_ISET_65SC02) + bit #_FERROR + .else lda (file),y ; get file->f_flags again and #_FERROR + .endif beq @L2 ; File not open or in error state @@ -65,11 +74,19 @@ ; Remember if we have a pushed back character and reset the flag. -@L2: tax ; X = 0 +@L2: .if (.cpu .bitand ::CPU_ISET_65SC02) + ldx #$00 + bit #_FPUSHBACK + .else + tax ; X = 0 lda (file),y and #_FPUSHBACK + .endif beq @L3 + + .if (.not .cpu .bitand ::CPU_ISET_65SC02) lda (file),y + .endif and #<~_FPUSHBACK sta (file),y ; file->f_flags &= ~_FPUSHBACK; inx ; X = 1 @@ -118,12 +135,20 @@ ; Copy the buffer pointer into ptr1, and increment the pointer value passed ; to read() by one, so read() starts to store data at buf+1. + .if (.cpu .bitand ::CPU_ISET_65SC02) + lda (sp) + sta ptr1 + add #1 + sta (sp) + ldy #1 + .else ldy #0 lda (sp),y sta ptr1 add #1 sta (sp),y iny + .endif lda (sp),y sta ptr1+1 adc #0 @@ -134,8 +159,12 @@ ldy #_FILE::f_pushback lda (file),y + .if (.cpu .bitand ::CPU_ISET_65SC02) + sta (ptr1) ; *buf = file->f_pushback; + .else ldy #0 sta (ptr1),y ; *buf = file->f_pushback; + .endif ; Restore the low byte of count and decrement count by one. This may result ; in count being zero, so check for that. @@ -210,4 +239,3 @@ .bss save: .res 2 pb: .res 1 - diff --git a/libsrc/common/fwrite.s b/libsrc/common/fwrite.s index 861feb120..e7151da95 100644 --- a/libsrc/common/fwrite.s +++ b/libsrc/common/fwrite.s @@ -8,7 +8,7 @@ .export _fwrite .import _write - .import pushax, incsp6, addysp, ldaxysp, pushwysp, return0 + .import pushax, pusha0, incsp6, addysp, ldaxysp, pushwysp, return0 .import tosumulax, tosudivax .importzp ptr1 @@ -16,6 +16,7 @@ .include "errno.inc" .include "_file.inc" + .macpack cpu ; ------------------------------------------------------------------------ ; Code @@ -33,7 +34,11 @@ ldy #_FILE::f_flags lda (ptr1),y + .if (.cpu .bitand ::CPU_ISET_65SC02) + bit #_FOPEN + .else and #_FOPEN ; Is the file open? + .endif bne @L2 ; Branch if yes ; File not open @@ -45,7 +50,9 @@ ; Check if the stream is in an error state -@L2: lda (ptr1),y ; get file->f_flags again +@L2: .if (.not .cpu .bitand ::CPU_ISET_65SC02) + lda (ptr1),y ; get file->f_flags again + .endif and #_FERROR bne @L1 @@ -53,8 +60,7 @@ ldy #_FILE::f_fd lda (ptr1),y - ldx #$00 - jsr pushax ; file->f_fd + jsr pusha0 ; file->f_fd ldy #9 jsr pushwysp ; buf @@ -123,4 +129,3 @@ .bss file: .res 2 - diff --git a/libsrc/common/gets.c b/libsrc/common/gets.c deleted file mode 100644 index 2936c70de..000000000 --- a/libsrc/common/gets.c +++ /dev/null @@ -1,63 +0,0 @@ -/* -** gets.c -** -** Ullrich von Bassewitz, 11.08.1998 -*/ - - - -#include <stdio.h> -#include "_file.h" - - - -/*****************************************************************************/ -/* Code */ -/*****************************************************************************/ - - - -char* __fastcall__ gets (char* s) -{ - register char* p = s; - int c; - unsigned i = 0; - - while (1) { - - /* Get next character */ - if ((c = fgetc (stdin)) == EOF) { - /* Error or EOF */ - *p = '\0'; - if (stdin->f_flags & _FERROR) { - /* ERROR */ - return 0; - } else { - /* EOF */ - if (i) { - return s; - } else { - return 0; - } - } - } - - /* One char more. Newline ends the input */ - if ((char) c == '\n') { - *p = '\0'; - break; - } else { - *p = c; - ++p; - ++i; - } - - } - - /* Done */ - return s; -} - - - - diff --git a/libsrc/common/gets.s b/libsrc/common/gets.s new file mode 100644 index 000000000..dfaf2def3 --- /dev/null +++ b/libsrc/common/gets.s @@ -0,0 +1,47 @@ +; +; Colin Leroy-Mira, 2024 +; +; char* __fastcall__ gets (char* s) +; + + .export _gets + .import _fgets, _stdin, popax, pushax + .importzp ptr4 + +_gets: + ; Push buffer + sta ptr4 + stx ptr4+1 + jsr pushax + + ; Push size (there's no limit!) + lda #$FF + tax + jsr pushax + + lda _stdin + ldx _stdin+1 + + jsr _fgets + + ; Check return value + bne :+ + cpx #$00 + bne :+ + rts + +: ; At least one byte written. + jsr pushax ; Store returned pointer + + ; Remove \n if there is one. + lda ptr4 ; _fgets returns with ptr4 at + bne :+ ; end of buffer + dec ptr4+1 +: dec ptr4 + lda (ptr4),y ; _fgets returns with Y=0 + cmp #$0A + bne :+ + tya + sta (ptr4),y ; Set terminator over \n + +: jmp popax diff --git a/libsrc/common/gmtime.s b/libsrc/common/gmtime.s new file mode 100644 index 000000000..288b285eb --- /dev/null +++ b/libsrc/common/gmtime.s @@ -0,0 +1,20 @@ +; +; Colin Leroy-Mira, 2024 +; +; struct tm* __fastcall__ gmtime (const time_t* timep); +; + + .export _gmtime + .import __time_t_to_tm + .import ldeaxi + +_gmtime: + cpx #$00 ; Check for null pointer + bne :+ + cmp #$00 + beq no_pointer +: jsr ldeaxi ; Load value from pointer + jmp __time_t_to_tm ; Convert it + +no_pointer: + rts ; A/X already set diff --git a/libsrc/common/localtime.s b/libsrc/common/localtime.s new file mode 100644 index 000000000..279442c9d --- /dev/null +++ b/libsrc/common/localtime.s @@ -0,0 +1,29 @@ +; +; Colin Leroy-Mira, 2024 +; +; struct tm* __fastcall__ localtime (const time_t* timep); +; + + .export _localtime + .import __time_t_to_tm, __tz + .import ldeaxi, tosaddeax, pusheax + .importzp sreg + +_localtime: + cpx #$00 ; Check for null pointer + bne :+ + cmp #$00 + beq no_pointer +: jsr ldeaxi ; Load value + jsr pusheax ; Push it + lda __tz+1+3 + sta sreg+1 + lda __tz+1+2 + sta sreg + ldx __tz+1+1 + lda __tz+1 + jsr tosaddeax ; Add _tz.timezone + jmp __time_t_to_tm ; Convert to struct tm + +no_pointer: + rts ; A/X already set diff --git a/libsrc/common/lz4.s b/libsrc/common/lz4.s index 4702137bc..c53841897 100644 --- a/libsrc/common/lz4.s +++ b/libsrc/common/lz4.s @@ -6,6 +6,60 @@ ; Almost 7 times faster, uses no RAM (vs 14 bytes BSS), and takes 1/4 the space ; vs the official C source. ; +; +; C implementation was: + +; void decompress_lz4 (unsigned char *in, unsigned char *out, const int outlen) { +; unsigned char token, tmp; +; unsigned int offset; +; unsigned char *end = out+outlen; +; unsigned char *copysrc; +; +; while (out < end) { +; token = *in++; +; offset = token >> 4; +; +; token &= 0x0f; +; token += 4; // Minmatch +; +; if (offset == 15) { +; moreliterals: +; tmp = *in++; +; offset += tmp; +; if (tmp == 255) +; goto moreliterals; +; } +; +; if (offset) { +; memcpy(out, in, offset); +; out += offset; +; in += offset; +; } +; +; if (out >= end) { +; return; +; } +; +; offset = (*in); +; in++; +; offset += (*in)<<8; +; in++; +; +; copysrc = out - offset; +; offset = token; +; +; if (token == 19) { +; morematches: +; tmp = *in++; +; offset += tmp; +; if (tmp == 255) +; goto morematches; +; } +; +; memcpy(out, copysrc, offset); +; out += offset; +; } +; } .importzp sp, sreg, regsave, regbank .importzp tmp1, tmp2, tmp3, tmp4, ptr1, ptr2, ptr3, ptr4 @@ -14,12 +68,11 @@ .export _decompress_lz4 out = regsave -written = regsave + 2 +end = regsave + 2 tmp = tmp1 token = tmp2 offset = ptr3 in = sreg -outlen = ptr4 ; --------------------------------------------------------------- ; void decompress_lz4 (const u8 *in, u8 * const out, const u16 outlen) @@ -29,37 +82,43 @@ outlen = ptr4 .proc _decompress_lz4: near - sta outlen - stx outlen+1 + sta tmp + stx tmp+1 +; +; end = out + outlen; +; jsr popax sta out - stx out+1 + clc + adc tmp + sta end + txa + sta out+1 + adc tmp+1 + sta end+1 jsr popax sta in stx in+1 ; -; written = 0; +; while (out < end) { ; - lda #$00 - sta written -; -; while (written < outlen) { -; - jmp L0046 + jmp check_len ; ; token = *in++; ; -L0004: ldy #$00 +get_token: + ldy #$00 lda (in),y - sta token + tay ; Backup token to Y inc in - bne L000A + bne :+ inc in+1 -L000A: + +: ; ; offset = token >> 4; ; @@ -74,7 +133,7 @@ L000A: ; token &= 0xf; ; token += 4; // Minmatch ; - lda token + tya ; Get token back from Y and #$0F clc adc #$04 @@ -84,7 +143,8 @@ L000A: ; lda offset cmp #$0F -L0013: bne L001A +moreliterals: + bne check_offset_not_zero ; ; tmp = *in++; ; @@ -93,9 +153,10 @@ L0013: bne L001A sta tmp inc in - bne L0017 + bne :+ inc in+1 -L0017: + +: ; ; offset += tmp; ; @@ -113,24 +174,20 @@ L0017: ; ; goto moreliterals; ; - jmp L0013 + jmp moreliterals ; ; if (offset) { ; -L001A: lda offset +check_offset_not_zero: + lda offset ora offset+1 - beq L001C + beq check_end ; -; memcpy(&out[written], in, offset); +; memcpy(out, in, offset); ; lda out - clc - adc written sta ptr2 - lda out+1 - adc written+1 - tax - lda ptr2 + ldx out+1 stx ptr2+1 jsr pushax lda in @@ -140,15 +197,15 @@ L001A: lda offset ; ldy #$00 - not needed as pushax zeroes Y jsr memcpy_upwards ; -; written += offset; +; out += offset; +; memcpy returned a pointer to out ; - lda offset clc - adc written - sta written - lda offset+1 - adc written+1 - sta written+1 + adc offset + sta out + txa + adc offset+1 + sta out+1 ; ; in += offset; ; @@ -160,21 +217,23 @@ L001A: lda offset adc in+1 sta in+1 ; -; if (written >= outlen) +; if (out >= end) ; -L001C: lda written - cmp outlen - lda written+1 - sbc outlen+1 +check_end: + lda out + cmp end + lda out+1 + sbc end+1 ; ; return; ; - bcc L0047 + bcc end_not_reached rts ; ; memcpy(&offset, in, 2); ; -L0047: ldy #$00 +end_not_reached: + ldy #$00 lda (in),y sta offset iny @@ -187,23 +246,18 @@ L0047: ldy #$00 clc adc in sta in - bcc L002F + bcc :+ inc in+1 + +: ; -; copysrc = out + written - offset; +; copysrc = out - offset; ; -L002F: lda out - clc - adc written - tay - lda out+1 - adc written+1 - tax - tya + lda out sec sbc offset sta ptr1 - txa + lda out+1 sbc offset+1 sta ptr1+1 ; @@ -217,7 +271,8 @@ L002F: lda out ; if (token == 19) { ; cmp #$13 -L0045: bne L003C +morematches: + bne token_not_19 ; ; tmp = *in++; ; @@ -226,9 +281,9 @@ L0045: bne L003C sta tmp inc in - bne L0039 + bne :+ inc in+1 -L0039: +: ; ; offset += tmp; ; @@ -246,41 +301,36 @@ L0039: ; ; goto morematches; ; - jmp L0045 + jmp morematches ; -; memcpy(&out[written], copysrc, offset); +; memcpy(out, copysrc, offset); ; -L003C: lda out - clc - adc written +token_not_19: + lda out sta ptr2 - lda out+1 - adc written+1 - tax - lda ptr2 + ldx out+1 stx ptr2+1 jsr pushax jsr memcpy_upwards ; -; written += offset; +; out += offset; ; - lda offset clc - adc written - sta written - lda offset+1 - adc written+1 -L0046: sta written+1 + adc offset + sta out + txa + adc offset+1 + sta out+1 ; 0 on the first loop iteration +check_len: ; -; while (written < outlen) { +; while (out < end) { ; - lda written - cmp outlen - lda written+1 - sbc outlen+1 - jcc L0004 + lda out + cmp end + lda out+1 + sbc end+1 + jcc get_token rts .endproc - diff --git a/libsrc/common/lzsa1.s b/libsrc/common/lzsa1.s new file mode 100644 index 000000000..bbff728f7 --- /dev/null +++ b/libsrc/common/lzsa1.s @@ -0,0 +1,207 @@ +; void __fastcall__ decompress_lzsa1(const void *src, void *dest) +; +; NMOS 6502 decompressor for data stored in Emmanuel Marty's LZSA1 format. +; +; Compress with: +; lzsa -r -f 1 input.bin output.lzsa1 +; +; Copyright John Brandwood 2021. +; +; Distributed under the Boost Software License, Version 1.0. +; Boost Software License - Version 1.0 - August 17th, 2003 +; +; Permission is hereby granted, free of charge, to any person or organization +; obtaining a copy of the software and accompanying documentation covered by +; this license (the "Software") to use, reproduce, display, distribute, +; execute, and transmit the Software, and to prepare derivative works of the +; Software, and to permit third-parties to whom the Software is furnished to +; do so, all subject to the following: +; +; The copyright notices in the Software and this entire statement, including +; the above license grant, this restriction and the following disclaimer, +; must be included in all copies of the Software, in whole or in part, and +; all derivative works of the Software, unless such copies or derivative +; works are solely in the form of machine-executable object code generated by +; a source language processor. +; +; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +; FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +; SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +; FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +; ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +; DEALINGS IN THE SOFTWARE. + + .export _decompress_lzsa1 + + .import popax + .importzp ptr1, ptr2, ptr3, ptr4, tmp1, tmp2, tmp3 + +lzsa_cmdbuf = tmp1 ; 1 byte. +lzsa_winptr = ptr1 ; 1 word. +lzsa_srcptr = ptr2 ; 1 word. +lzsa_dstptr = ptr3 ; 1 word. + +lzsa_offset = lzsa_winptr + +.proc _decompress_lzsa1 + sta lzsa_dstptr + stx lzsa_dstptr+1 + jsr popax + sta lzsa_srcptr + stx lzsa_srcptr+1 + +lzsa1_unpack: ldy #0 ; Initialize source index. + ldx #0 ; Initialize hi-byte of length. + + ; + ; Copy bytes from compressed source data. + ; + ; N.B. X=0 is expected and guaranteed when we get here. + ; + +cp_length: lda (lzsa_srcptr),y + inc lzsa_srcptr + bne cp_skip0 + inc lzsa_srcptr+1 + +cp_skip0: sta lzsa_cmdbuf ; Preserve this for later. + and #$70 ; Extract literal length. + lsr ; Set CC before ... + beq lz_offset ; Skip directly to match? + + lsr ; Get 3-bit literal length. + lsr + lsr + cmp #$07 ; Extended length? + bcc cp_got_len + + jsr get_length ; X=0, CS from CMP, returns CC. + stx cp_npages+1 ; Hi-byte of length. + +cp_got_len: tax ; Lo-byte of length. + +cp_byte: lda (lzsa_srcptr),y ; CC throughout the execution of + sta (lzsa_dstptr),y ; of this .cp_page loop. + inc lzsa_srcptr + bne cp_skip1 + inc lzsa_srcptr+1 +cp_skip1: inc lzsa_dstptr + bne cp_skip2 + inc lzsa_dstptr+1 +cp_skip2: dex + bne cp_byte +cp_npages: lda #0 ; Any full pages left to copy? + beq lz_offset + + dec cp_npages+1 ; Unlikely, so can be slow. + bcc cp_byte ; Always true! + + ; + ; Copy bytes from decompressed window. + ; + ; Longer but faster. + ; + ; N.B. X=0 is expected and guaranteed when we get here. + ; + +lz_offset: lda (lzsa_srcptr),y ; Get offset-lo. + inc lzsa_srcptr + bne offset_lo + inc lzsa_srcptr+1 + +offset_lo: sta lzsa_offset + + lda #$FF ; Get offset-hi. + bit lzsa_cmdbuf + bpl offset_hi + + lda (lzsa_srcptr),y + inc lzsa_srcptr + bne offset_hi + inc lzsa_srcptr+1 + +offset_hi: sta lzsa_offset+1 + +lz_length: lda lzsa_cmdbuf ; X=0 from previous loop. + and #$0F + adc #$03 ; Always CC from .cp_page loop. + cmp #$12 ; Extended length? + bcc got_lz_len + + jsr get_length ; X=0, CS from CMP, returns CC. + +got_lz_len: inx ; Hi-byte of length+256. + + eor #$FF ; Negate the lo-byte of length + tay + eor #$FF + +get_lz_dst: adc lzsa_dstptr ; Calc address of partial page. + sta lzsa_dstptr ; Always CC from previous CMP. + iny + bcs get_lz_win + beq get_lz_win ; Is lo-byte of length zero? + dec lzsa_dstptr+1 + +get_lz_win: clc ; Calc address of match. + adc lzsa_offset ; N.B. Offset is negative! + sta lzsa_winptr + lda lzsa_dstptr+1 + adc lzsa_offset+1 + sta lzsa_winptr+1 + +lz_byte: lda (lzsa_winptr),y + sta (lzsa_dstptr),y + iny + bne lz_byte + inc lzsa_dstptr+1 + dex ; Any full pages left to copy? + bne lz_more + + jmp cp_length ; Loop around to the beginning. + +lz_more: inc lzsa_winptr+1 ; Unlikely, so can be slow. + bne lz_byte ; Always true! + + ; + ; Get 16-bit length in X:A register pair, return with CC. + ; + ; N.B. X=0 is expected and guaranteed when we get here. + ; + +get_length: clc ; Add on the next byte to get + adc (lzsa_srcptr),y ; the length. + inc lzsa_srcptr + bne skip_inc + inc lzsa_srcptr+1 + +skip_inc: bcc got_length ; No overflow means done. + clc ; MUST return CC! + tax ; Preserve overflow value. + +extra_byte: jsr get_byte ; So rare, this can be slow! + pha + txa ; Overflow to 256 or 257? + beq extra_word + +check_length: pla ; Length-lo. + bne got_length ; Check for zero. + dex ; Do one less page loop if so. +got_length: rts + +extra_word: jsr get_byte ; So rare, this can be slow! + tax + bne check_length ; Length-hi == 0 at EOF. + +finished: pla ; Length-lo. + pla ; Decompression completed, pop + pla ; return address. + rts + +get_byte: lda (lzsa_srcptr),y ; Subroutine version for when + inc lzsa_srcptr ; inlining isn't advantageous. + bne got_byte + inc lzsa_srcptr+1 ; Inc & test for bank overflow. +got_byte: rts +.endproc diff --git a/libsrc/common/lzsa2.s b/libsrc/common/lzsa2.s new file mode 100644 index 000000000..e851970e1 --- /dev/null +++ b/libsrc/common/lzsa2.s @@ -0,0 +1,308 @@ +; void __fastcall__ decompress_lzsa2(const void *src, void *dest) +; +; NMOS 6502 decompressor for data stored in Emmanuel Marty's LZSA2 format. +; +; Compress with: +; lzsa -r -f 2 input.bin output.lzsa2 +; +; Copyright John Brandwood 2021. +; +; Distributed under the Boost Software License, Version 1.0. +; Boost Software License - Version 1.0 - August 17th, 2003 +; +; Permission is hereby granted, free of charge, to any person or organization +; obtaining a copy of the software and accompanying documentation covered by +; this license (the "Software") to use, reproduce, display, distribute, +; execute, and transmit the Software, and to prepare derivative works of the +; Software, and to permit third-parties to whom the Software is furnished to +; do so, all subject to the following: +; +; The copyright notices in the Software and this entire statement, including +; the above license grant, this restriction and the following disclaimer, +; must be included in all copies of the Software, in whole or in part, and +; all derivative works of the Software, unless such copies or derivative +; works are solely in the form of machine-executable object code generated by +; a source language processor. +; +; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +; FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +; SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +; FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +; ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +; DEALINGS IN THE SOFTWARE. + + .export _decompress_lzsa2 + + .import popax + .importzp ptr1, ptr2, ptr3, ptr4, tmp1, tmp2, tmp3 + +lzsa_length = lzsa_winptr ; 1 word. + +lzsa_cmdbuf = tmp1 ; 1 byte. +lzsa_nibflg = tmp2 ; 1 byte. +lzsa_nibble = tmp3 ; 1 byte. +lzsa_offset = ptr1 ; 1 word. +lzsa_winptr = ptr2 ; 1 word. +lzsa_srcptr = ptr3 ; 1 word. +lzsa_dstptr = ptr4 ; 1 word. + +.proc _decompress_lzsa2 + sta lzsa_dstptr + stx lzsa_dstptr+1 + jsr popax + sta lzsa_srcptr + stx lzsa_srcptr+1 + +lzsa2_unpack: + ldx #$00 ; Hi-byte of length or offset. + ldy #$00 ; Initialize source index. + sty lzsa_nibflg ; Initialize nibble buffer. + + ; + ; Copy bytes from compressed source data. + ; + ; N.B. X=0 is expected and guaranteed when we get here. + ; + +cp_length: + lda (lzsa_srcptr),y + inc lzsa_srcptr + bne cp_skip0 + inc lzsa_srcptr+1 + +cp_skip0: + sta lzsa_cmdbuf ; Preserve this for later. + and #$18 ; Extract literal length. + beq lz_offset ; Skip directly to match? + + lsr ; Get 2-bit literal length. + lsr + lsr + cmp #$03 ; Extended length? + bcc cp_got_len + + jsr get_length ; X=0 for literals, returns CC. + stx cp_npages+1 ; Hi-byte of length. + +cp_got_len: + tax ; Lo-byte of length. + +cp_byte: + lda (lzsa_srcptr),y ; CC throughout the execution of + sta (lzsa_dstptr),y ; of this .cp_page loop. + inc lzsa_srcptr + bne cp_skip1 + inc lzsa_srcptr+1 +cp_skip1: + inc lzsa_dstptr + bne cp_skip2 + inc lzsa_dstptr+1 +cp_skip2: + dex + bne cp_byte +cp_npages: + lda #0 ; Any full pages left to copy? + beq lz_offset + + dec cp_npages+1 ; Unlikely, so can be slow + bcc cp_byte ; Always true! + + ; + ; Copy bytes from decompressed window. + ; + ; N.B. X=0 is expected and guaranteed when we get here. + ; + ; xyz + ; =========================== + ; 00z 5-bit offset + ; 01z 9-bit offset + ; 10z 13-bit offset + ; 110 16-bit offset + ; 111 repeat offset + ; + +lz_offset: + lda lzsa_cmdbuf + asl + bcs get_13_16_rep + +get_5_9_bits: + dex ; X=$FF for a 5-bit offset. + asl + bcs get_9_bits ; Fall through if 5-bit. + +get_13_bits: + asl ; Both 5-bit and 13-bit read + php ; a nibble. + jsr get_nibble + plp + rol ; Shift into position, clr C. + eor #$E1 + cpx #$00 ; X=$FF for a 5-bit offset. + bne set_offset + sbc #2 ; 13-bit offset from $FE00. + bne set_hi_8 ; Always NZ from previous SBC. + +get_9_bits: + asl ; X=$FF if CC, X=$FE if CS. + bcc get_lo_8 + dex + bcs get_lo_8 ; Always CS from previous BCC. + +get_13_16_rep: + asl + bcc get_13_bits ; Shares code with 5-bit path. + +get_16_rep: + bmi lz_length ; Repeat previous offset. + +get_16_bits: + jsr get_byte ; Get hi-byte of offset. + +set_hi_8: + tax + +get_lo_8: + lda (lzsa_srcptr),y ; Get lo-byte of offset. + inc lzsa_srcptr + bne set_offset + inc lzsa_srcptr+1 + +set_offset: + sta lzsa_offset ; Save new offset. + stx lzsa_offset+1 + +lz_length: + ldx #1 ; Hi-byte of length+256. + + lda lzsa_cmdbuf + and #$07 + clc + adc #$02 + cmp #$09 ; Extended length? + bcc got_lz_len + + jsr get_length ; X=1 for match, returns CC. + inx ; Hi-byte of length+256. + +got_lz_len: + eor #$FF ; Negate the lo-byte of length. + tay + eor #$FF + +get_lz_dst: + adc lzsa_dstptr ; Calc address of partial page. + sta lzsa_dstptr ; Always CC from previous CMP. + iny + bcs get_lz_win + beq get_lz_win ; Is lo-byte of length zero? + dec lzsa_dstptr+1 + +get_lz_win: + clc ; Calc address of match. + adc lzsa_offset ; N.B. Offset is negative! + sta lzsa_winptr + lda lzsa_dstptr+1 + adc lzsa_offset+1 + sta lzsa_winptr+1 + +lz_byte: + lda (lzsa_winptr),y + sta (lzsa_dstptr),y + iny + bne lz_byte + inc lzsa_dstptr+1 + dex ; Any full pages left to copy? + bne lz_more + + jmp cp_length ; Loop around to the beginning. + +lz_more: + inc lzsa_winptr+1 ; Unlikely, so can be slow. + bne lz_byte ; Always true! + + ; + ; Lookup tables to differentiate literal and match lengths. + ; + +nibl_len_tbl: + .byte 3 ; 0+3 (for literal). + .byte 9 ; 2+7 (for match). + +byte_len_tbl: + .byte 18 - 1 ; 0+3+15 - CS (for literal). + .byte 24 - 1 ; 2+7+15 - CS (for match). + + ; + ; Get 16-bit length in X:A register pair, return with CC. + ; + +get_length: + jsr get_nibble + cmp #$0F ; Extended length? + bcs byte_length + adc nibl_len_tbl,x ; Always CC from previous CMP. + +got_length: + ldx #$00 ; Set hi-byte of 4 & 8 bit + rts ; lengths. + +byte_length: + jsr get_byte ; So rare, this can be slow! + adc byte_len_tbl,x ; Always CS from previous CMP + bcc got_length + beq finished + +word_length: + clc ; MUST return CC! + jsr get_byte ; So rare, this can be slow! + pha + jsr get_byte ; So rare, this can be slow! + tax + pla + bne got_word ; Check for zero lo-byte. + dex ; Do one less page loop if so. +got_word: + rts + +get_byte: + lda (lzsa_srcptr),y ; Subroutine version for when + inc lzsa_srcptr ; inlining isn't advantageous. + bne got_byte + inc lzsa_srcptr+1 +got_byte: + rts + +finished: + pla ; Decompression completed, pop + pla ; return address. + rts + + ; + ; Get a nibble value from compressed data in A. + ; + +get_nibble: + lsr lzsa_nibflg ; Is there a nibble waiting? + lda lzsa_nibble ; Extract the lo-nibble. + bcs got_nibble + + inc lzsa_nibflg ; Reset the flag. + + lda (lzsa_srcptr),y + inc lzsa_srcptr + bne set_nibble + inc lzsa_srcptr+1 + +set_nibble: + sta lzsa_nibble ; Preserve for next time. + lsr ; Extract the hi-nibble. + lsr + lsr + lsr + +got_nibble: + and #$0F + rts +.endproc diff --git a/libsrc/common/malloc.s b/libsrc/common/malloc.s index 6872f1f2e..72c4aedaa 100644 --- a/libsrc/common/malloc.s +++ b/libsrc/common/malloc.s @@ -131,6 +131,7 @@ _malloc: sta ptr1 bcc @L1 inc ptr1+1 + beq OutOfHeapSpace ; if high byte's 0, we overflowed! @L1: ldx ptr1+1 bne @L2 cmp #HEAP_MIN_BLOCKSIZE+1 @@ -336,4 +337,3 @@ RetUserPtr: bcc @L9 inx @L9: rts - diff --git a/libsrc/common/mktime.c b/libsrc/common/mktime.c deleted file mode 100644 index 275589dbb..000000000 --- a/libsrc/common/mktime.c +++ /dev/null @@ -1,192 +0,0 @@ -/*****************************************************************************/ -/* */ -/* mktime.c */ -/* */ -/* Make calendar time from broken down time and cleanup */ -/* */ -/* */ -/* */ -/* (C) 2002 Ullrich von Bassewitz */ -/* Wacholderweg 14 */ -/* D-70597 Stuttgart */ -/* EMail: uz@musoftware.de */ -/* */ -/* */ -/* This software is provided 'as-is', without any expressed 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. */ -/* */ -/*****************************************************************************/ - - - -#include <limits.h> -#include <stdlib.h> -#include <time.h> - - - -/*****************************************************************************/ -/* Data */ -/*****************************************************************************/ - - - -#define JANUARY 0 -#define FEBRUARY 1 -#define DECEMBER 11 -#define JAN_1_1970 4 /* 1/1/1970 is a thursday */ - - - -static const unsigned char MonthLength [] = { - 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 -}; -static const unsigned MonthDays [] = { - 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 -}; - - - -/*****************************************************************************/ -/* Code */ -/*****************************************************************************/ - - - -static unsigned char __fastcall__ IsLeapYear (unsigned Year) -/* Returns 1 if the given year is a leap year */ -{ - return (((Year % 4) == 0) && ((Year % 100) != 0 || (Year % 400) == 0)); -} - - - -time_t __fastcall__ mktime (register struct tm* TM) -/* Make a time in seconds since 1/1/1970 from the broken down time in TM. -** A call to mktime does also correct the time in TM to contain correct -** values. -*/ -{ - register div_t D; - int Max; - unsigned DayCount; - - /* Check if TM is valid */ - if (TM == 0) { - /* Invalid data */ - goto Error; - } - - /* Adjust seconds. */ - D = div (TM->tm_sec, 60); - TM->tm_sec = D.rem; - - /* Adjust minutes */ - if (TM->tm_min + D.quot < 0) { - goto Error; - } - TM->tm_min += D.quot; - D = div (TM->tm_min, 60); - TM->tm_min = D.rem; - - /* Adjust hours */ - if (TM->tm_hour + D.quot < 0) { - goto Error; - } - TM->tm_hour += D.quot; - D = div (TM->tm_hour, 24); - TM->tm_hour = D.rem; - - /* Adjust days */ - if (TM->tm_mday + D.quot < 0) { - goto Error; - } - TM->tm_mday += D.quot; - - /* Adjust month and year. This is an iterative process, since changing - ** the month will change the allowed days for this month. - */ - while (1) { - - /* Make sure, month is in the range 0..11 */ - D = div (TM->tm_mon, 12); - TM->tm_mon = D.rem; - if (TM->tm_year + D.quot < 0) { - goto Error; - } - TM->tm_year += D.quot; - - /* Now check if mday is in the correct range, if not, correct month - ** and eventually year and repeat the process. - */ - if (TM->tm_mon == FEBRUARY && IsLeapYear (TM->tm_year + 1900)) { - Max = 29; - } else { - Max = MonthLength[TM->tm_mon]; - } - if (TM->tm_mday > Max) { - /* Must correct month and eventually, year */ - if (TM->tm_mon == DECEMBER) { - TM->tm_mon = JANUARY; - ++TM->tm_year; - } else { - ++TM->tm_mon; - } - TM->tm_mday -= Max; - } else { - /* Done */ - break; - } - } - - /* Ok, all time/date fields are now correct. Calculate the days in this - ** year. - */ - TM->tm_yday = MonthDays[TM->tm_mon] + TM->tm_mday - 1; - if (TM->tm_mon > FEBRUARY && IsLeapYear (TM->tm_year + 1900)) { - ++TM->tm_yday; - } - - /* Calculate days since 1/1/1970. In the complete epoch (1/1/1970 to - ** somewhere in 2038) all years dividable by 4 are leap years, so - ** dividing by 4 gives the days that must be added cause of leap years. - ** (and the last leap year before 1970 was 1968) - */ - DayCount = ((unsigned) (TM->tm_year-70)) * 365U + - (((unsigned) (TM->tm_year-(68+1))) / 4) + - TM->tm_yday; - - /* Calculate the weekday */ - TM->tm_wday = (JAN_1_1970 + DayCount) % 7; - - /* No (US) daylight saving (for now) */ - TM->tm_isdst = 0; - - /* Return seconds since 1970 */ - return DayCount * 86400UL + - ((unsigned) TM->tm_hour) * 3600UL + - ((unsigned) TM->tm_min) * 60U + - ((unsigned) TM->tm_sec) - - _tz.timezone; - -Error: - /* Error exit */ - return (time_t) -1L; -} - - - diff --git a/libsrc/common/mktime.s b/libsrc/common/mktime.s new file mode 100644 index 000000000..e84cee040 --- /dev/null +++ b/libsrc/common/mktime.s @@ -0,0 +1,476 @@ +; +; Colin Leroy-Mira, 2024 +; +; time_t __fastcall__ mktime (register struct tm* TM) +; +; Converts a struct tm to a time_t timestamp, making sure +; day, month, year, hour, minute and seconds are in the +; correct range. +; + + .export _mktime + .import __tz + .import pushax, pusha0, pusheax + .import shrax2, _div, tosumulax, tosumodax, tossubeax, tosaddeax, tosumuleax + .importzp ptr2, tmp3, sreg + + .include "time.inc" + +; ------------------------------------------------------------------------ +; Special values + +FEBRUARY = 1 +MARCH = 2 +JAN_1_1970 = 4 +N_SEC = 60 +N_MIN = 60 +N_HOUR = 24 +N_MON = 12 +N_DAY_YEAR = 365 +; ------------------------------------------------------------------------ +; Helpers + + ; Helper to shift overflows from one field to the next + ; Current field in Y, divisor in A + ; Keeps remainder in current field, and adds the quotient + ; to the next one +adjust_field: + pha ; Push divisor + iny ; Point to high byte of current field + lda (ptr2),y + tax + dey + sty tmp3 ; Store current field (_div will mess with + lda (ptr2),y ; tmp1 and tmp2) + jsr pushax + pla ; Load divisor + ldx #$00 + + jsr _div + + ldy tmp3 ; Store remainder in current field + sta (ptr2),y + iny + txa + sta (ptr2),y + + lda sreg ; Add quotient to next field + iny + clc + adc (ptr2),y + sta (ptr2),y + iny + lda sreg+1 + adc (ptr2),y + sta (ptr2),y + rts + + ; Returns 1 in A if the given year is a leap year. Expects a year + ; from 0 to 206, without 1900 added. +is_leap_year: + cmp #$00 ; Y 0 (1900) is not a leap year + beq not_leap + cmp #$C8 ; Y 200 (2100) is not a leap year + beq not_leap + and #$03 ; Year % 4 == 0 means leap year + bne not_leap + lda #$01 ; Return 1 + rts +not_leap: + lda #$00 ; Return 0 + rts + + ; Returns the number of days in the current month/year in A +get_days_in_month: + ldy #tm::tm_mon + lda (ptr2),y + tax + lda months_len,x + cpx #FEBRUARY + beq :+ + rts +: tax + ldy #tm::tm_year ; Adjust for leap years + lda (ptr2),y + jsr is_leap_year + beq :+ + inx +: txa + rts + + ; Add AX to counter +addaxcounter: + clc + adc Counter + sta Counter ; Store in Counter + txa + adc Counter+1 + sta Counter+1 + rts + + ; Helpers for long chain of arithmetic on day counter. + ; Reload Counter and push it on the stack +load_and_push_counter: + lda Counter+3 + sta sreg+1 + lda Counter+2 + sta sreg + lda Counter + ldx Counter+1 + jsr pusheax + rts + + ; Store result in AX:sreg to Counter +store_counter: + sta Counter + stx Counter+1 + lda sreg + sta Counter+2 + lda sreg+1 + sta Counter+3 + rts + +; ------------------------------------------------------------------------ +; Code + +_mktime: + sta ptr2 ; Store struct to ptr2, which arithmetic + stx ptr2+1 ; functions won't touch + + ; Check pointer validity + ora ptr2+1 + bne :+ + lda #$FF + tax + sta sreg + sta sreg+1 + rts + + ; Adjust seconds +: ldy #tm::tm_sec + lda #N_SEC + jsr adjust_field + + ; Adjust minutes + ldy #tm::tm_min + lda #N_MIN + jsr adjust_field + + ; Adjust hours + ldy #tm::tm_hour + lda #N_HOUR + jsr adjust_field + + ;Shift one year as long as tm_mday is more than a year + ldy #tm::tm_year + lda (ptr2),y + +dec_by_year: + jsr is_leap_year ; Compute max numbers of days in year + clc + adc #<N_DAY_YEAR ; No care about carry, + sta Max ; 365+1 doesn't overflow low byte + + ldy #tm::tm_mday+1 ; Do we have more days in store? + lda (ptr2),y + cmp #>N_DAY_YEAR + beq :+ ; High byte equal, check low byte + bcs do_year_dec ; High byte greater, decrement + bcc dec_by_month ; Low byte lower, we're done +: dey + lda (ptr2),y + cmp Max + bcc dec_by_month + beq dec_by_month + +do_year_dec: + ; Decrement days + ldy #tm::tm_mday + lda (ptr2),y + sbc Max ; Carry already set + sta (ptr2),y + iny + lda (ptr2),y + sbc #>N_DAY_YEAR + sta (ptr2),y + + ; Increment year + ldy #tm::tm_year + lda (ptr2),y + clc + adc #1 + sta (ptr2),y ; No carry possible here either + bcc dec_by_year ; bra, go check next year + +dec_by_month: + ; We're done decrementing days by full years, now do it + ; month per month. + ldy #tm::tm_mon + lda #N_MON + jsr adjust_field + + ; Get max day for this month + jsr get_days_in_month + sta Max + + ; So, do we have more days than this month? + ldy #tm::tm_mday+1 + lda (ptr2),y + bne do_month_dec ; High byte not zero, sure we do + dey + lda (ptr2),y + cmp Max + bcc calc_tm_yday ; No + beq calc_tm_yday + +do_month_dec: + ; Decrement days + ldy #tm::tm_mday + lda (ptr2),y + sec + sbc Max + sta (ptr2),y + iny + lda (ptr2),y + sbc #$00 + sta (ptr2),y + + ; Increment month + ldy #tm::tm_mon + lda (ptr2),y + clc + adc #1 + sta (ptr2),y + + bne dec_by_month ; Check next month + +calc_tm_yday: + ; We finished decrementing tm_mday and have put it in the correct + ; year/month range. Now compute the day of the year. + ldy #tm::tm_mday ; Get current day of month + lda (ptr2),y + sta Counter ; Store it in Counter + + lda #$00 ; Init counter high bytes + sta Counter+1 + sta Counter+2 + sta Counter+3 + + ldy #tm::tm_mon ; Get current month + lda (ptr2),y + asl + tax + clc + lda yday_by_month,x ; Get yday for this month's start + adc Counter ; Add it to counter + sta Counter + inx + lda yday_by_month,x + adc Counter+1 + sta Counter+1 + + ldy #tm::tm_year ; Adjust for leap years (if after feb) + lda (ptr2),y + jsr is_leap_year + beq dec_counter + ldy #tm::tm_mon ; Leap year, get current month + lda (ptr2),y + cmp #MARCH + bcs store_yday + +dec_counter: + lda Counter ; Decrease counter by one (yday starts at 0), + bne :+ ; unless we're after february in a leap year + dec Counter+1 +: dec Counter + +store_yday: + ldy #tm::tm_yday ; Store tm_yday + lda Counter + sta (ptr2),y + iny + lda Counter+1 + sta (ptr2),y + + ; Now calculate total day count since epoch with the formula: + ; ((unsigned) (TM->tm_year-70)) * 365U + (number of days per year since 1970) + ; (((unsigned) (TM->tm_year-(68+1))) / 4) + (one extra day per leap year since 1970) + ; TM->tm_yday (number of days in this year) + + ldy #tm::tm_year ; Get full years + lda (ptr2),y + sec + sbc #70 + ldx #0 + jsr pushax + lda #<N_DAY_YEAR + ldx #>N_DAY_YEAR + + jsr tosumulax + jsr addaxcounter + + ; Add one day per leap year + ldy #tm::tm_year ; Get full years + lda (ptr2),y + sec + sbc #69 + ldx #0 + jsr shrax2 ; Divide by 4 + + jsr addaxcounter + + ; Handle the 2100 exception (which was considered leap by "Add one day + ; per leap year" just before) + ldy #tm::tm_year ; Get full years + lda (ptr2),y + cmp #201 + bcc finish_calc ; <= 200, nothing to do + + lda Counter + bne :+ + dec Counter+1 +: dec Counter + +finish_calc: + ; Now we can compute the weekday. + lda Counter + clc + adc #JAN_1_1970 + pha + lda Counter+1 + adc #0 + tax + pla + jsr pushax + + lda #7 ; Modulo 7 + ldx #0 + jsr tosumodax + + ldy #tm::tm_wday ; Store tm_wday + sta (ptr2),y + iny + txa + sta (ptr2),y + + ; DST + lda #$00 ; Store tm_isdst + ldy #tm::tm_isdst + sta (ptr2),y + iny + sta (ptr2),y + + ; Our struct tm is all fixed and every field calculated. + ; We can finally count seconds according to this formula: + ; seconds = (full days since epoch) * 86400UL + + ; ((unsigned) TM->tm_hour) * 3600UL + + ; ((unsigned) TM->tm_min) * 60U + + ; ((unsigned) TM->tm_sec) - + ; _tz.timezone; + + ; We already have the number of days since epoch in our counter, + ; from just before when we computed tm_wday. Reuse it. + jsr load_and_push_counter + lda #$00 ; Multiply by 86400 + sta sreg+1 + lda #$01 + sta sreg + lda #$80 + ldx #$51 + jsr tosumuleax + jsr store_counter ; Store into counter + + ; Push counter to add 3600 * hours to it + jsr load_and_push_counter + + ldx #$00 ; Load hours + stx sreg + stx sreg+1 + ldy #tm::tm_hour + lda (ptr2),y + jsr pusheax ; Push + ldx #$00 ; Load 3600 + stx sreg + stx sreg+1 + lda #<3600 + ldx #>3600 + jsr tosumuleax ; Multiply (pops the pushed hours) + jsr tosaddeax ; Add to counter (pops the pushed counter) + jsr store_counter ; Store counter + + ; Push counter to add 60 * min to it + jsr load_and_push_counter + + ldy #tm::tm_min ; Load minutes + lda (ptr2),y + jsr pusha0 ; Push + lda #N_MIN + ldx #0 + stx sreg + stx sreg+1 + jsr tosumulax ; Multiply + jsr tosaddeax ; Add to pushed counter + jsr store_counter ; Store + + ; Add seconds + jsr load_and_push_counter + + ldy #tm::tm_sec ; Load seconds + lda (ptr2),y + ldx #0 + stx sreg + stx sreg+1 + jsr tosaddeax ; Simple addition there + + ; No need to store/load/push the counter here, simply to push it + ; for the last subtraction + jsr pusheax + + ; Subtract timezone + lda __tz+1+3 + sta sreg+1 + lda __tz+1+2 + sta sreg + ldx __tz+1+1 + lda __tz+1 + jsr tossubeax + + ; And we're done! + rts + + .data + +months_len: + .byte 31 + .byte 28 + .byte 31 + .byte 30 + .byte 31 + .byte 30 + .byte 31 + .byte 31 + .byte 30 + .byte 31 + .byte 30 + .byte 31 + +yday_by_month: + .word 0 + .word 31 + .word 59 + .word 90 + .word 120 + .word 151 + .word 181 + .word 212 + .word 243 + .word 273 + .word 304 + .word 334 + + + .bss + +Max: .res 1 ; We won't need a high byte +Counter: + .res 4 diff --git a/libsrc/common/ntohl.s b/libsrc/common/ntohl.s new file mode 100644 index 000000000..77adba253 --- /dev/null +++ b/libsrc/common/ntohl.s @@ -0,0 +1,32 @@ +; +; Colin Leroy-Mira <colin@colino.net>, 2023-09-06 +; +; int __fastcall__ ntohl (long val); +; + +.export _ntohl, _htonl +.import popa +.importzp tmp1, tmp2, sreg + +_htonl := _ntohl + +_ntohl: + ; The parts of our 32 bit word + ; are in sreg+1, sreg, X, A. + + ; Save A and X + stx tmp1 + sta tmp2 + + ; Invert high word + lda sreg+1 + ldx sreg + + ; Invert low word + ldy tmp1 + sty sreg + + ldy tmp2 + sty sreg+1 + + rts diff --git a/libsrc/common/ntohs.s b/libsrc/common/ntohs.s new file mode 100644 index 000000000..042ddb005 --- /dev/null +++ b/libsrc/common/ntohs.s @@ -0,0 +1,16 @@ +; +; Colin Leroy-Mira <colin@colino.net>, 2023-09-06 +; +; int __fastcall__ ntohs (int val); +; + +.export _ntohs, _htons +.importzp tmp1 + +_htons := _ntohs + +_ntohs: + sta tmp1 + txa + ldx tmp1 + rts diff --git a/libsrc/common/pmemalign.c b/libsrc/common/pmemalign.c index 52adb240d..4499084d1 100644 --- a/libsrc/common/pmemalign.c +++ b/libsrc/common/pmemalign.c @@ -50,7 +50,6 @@ */ - int __fastcall__ posix_memalign (void** memptr, size_t alignment, size_t size) /* Allocate a block of memory with the given "size", which is aligned to a ** memory address that is a multiple of "alignment". "alignment" MUST NOT be @@ -64,20 +63,27 @@ int __fastcall__ posix_memalign (void** memptr, size_t alignment, size_t size) size_t rawsize; size_t uppersize; size_t lowersize; + char err; register struct usedblock* b; /* points to raw Block */ register struct usedblock* u; /* points to User block */ register struct usedblock* p; /* Points to upper block */ /* Handle requests for zero-sized blocks */ if (size == 0) { +err_einval: + err = EINVAL; +err_out: *memptr = NULL; - return EINVAL; + return err; } - /* Test alignment: is it a power of two? There must be only one bit set. */ - if (alignment == 0 || (alignment & (alignment - 1)) != 0) { - *memptr = NULL; - return EINVAL; + /* Test alignment: is it a power of two? There must be one and only one bit set. */ + if (alignment == 0) { + goto err_einval; + } + + if (alignment & (alignment - 1)) { + goto err_einval; } /* Augment the block size up to the alignment, and allocate memory. @@ -90,8 +96,8 @@ int __fastcall__ posix_memalign (void** memptr, size_t alignment, size_t size) /* Handle out-of-memory */ if (b == NULL) { - *memptr = NULL; - return ENOMEM; + err = ENOMEM; + goto err_out; } /* Create (and return) a new pointer that points to the user-visible diff --git a/libsrc/common/putenv.s b/libsrc/common/putenv.s index 5febcc71e..13f0e7dc4 100644 --- a/libsrc/common/putenv.s +++ b/libsrc/common/putenv.s @@ -10,7 +10,7 @@ .import _malloc, _free .import searchenv, copyenvptr .import __environ, __envcount, __envsize - .import return0 + .import return0, ___directerrno .import ptr1:zp, ptr2:zp, ptr3:zp, tmp1:zp .include "errno.inc" @@ -169,10 +169,7 @@ addentry: ; Error entries nomem: lda #ENOMEM -error: jsr ___seterrno - lda #$FF ; Return -1 - tax - rts +error: jmp ___directerrno .endproc @@ -184,5 +181,3 @@ error: jsr ___seterrno name: .addr 0 ; Pointer to name newsize: .byte 0 ; New environment size - - diff --git a/libsrc/common/realloc.c b/libsrc/common/realloc.c deleted file mode 100644 index eeb1eeea5..000000000 --- a/libsrc/common/realloc.c +++ /dev/null @@ -1,112 +0,0 @@ -/*****************************************************************************/ -/* */ -/* realloc.c */ -/* */ -/* Change the size of an allocated memory block */ -/* */ -/* */ -/* */ -/* (C) 1998-2004 Ullrich von Bassewitz */ -/* Wacholderweg 14 */ -/* D-70597 Stuttgart */ -/* EMail: uz@musoftware.de */ -/* */ -/* */ -/* This software is provided 'as-is', without any expressed 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. */ -/* */ -/*****************************************************************************/ - - - -#include <stdlib.h> -#include <string.h> -#include <_heap.h> - - - -void* __fastcall__ realloc (void* block, register size_t size) -{ - register struct usedblock* b; - struct usedblock* newblock; - unsigned oldsize; - unsigned newhptr; - - /* Check the block parameter */ - if (!block) { - /* Block is NULL, same as malloc */ - return malloc (size); - } - - /* Check the size parameter */ - if (size == 0) { - /* Block is not NULL, but size is: free the block */ - free (block); - return 0; - } - - /* Make the internal used size from the given size */ - size += HEAP_ADMIN_SPACE; - if (size < sizeof (struct freeblock)) { - size = sizeof (struct freeblock); - } - - /* The word below the user block contains a pointer to the start of the - ** raw memory block. The first word of this raw memory block is the full - ** size of the block. Get a pointer to the real block, get the old block - ** size. - */ - b = (((struct usedblock*) block) - 1)->start; - oldsize = b->size; - - /* Is the block at the current heap top? */ - if (((unsigned) b) + oldsize == ((unsigned) __heapptr)) { - /* Check if we've enough memory at the heap top */ - newhptr = ((unsigned) __heapptr) - oldsize + size; - if (newhptr <= ((unsigned) __heapend)) { - /* Ok, there's space enough */ - __heapptr = (unsigned*) newhptr; - b->size = size; - b->start = b; - return block; - } - } - - /* The given block was not located on top of the heap, or there's no - ** room left. Try to allocate a new block and copy the data. - */ - if (newblock = malloc (size)) { - - /* Adjust the old size to the user visible portion */ - oldsize -= HEAP_ADMIN_SPACE; - - /* If the new block is larger than the old one, copy the old - ** data only - */ - if (size > oldsize) { - size = oldsize; - } - - /* Copy the block data */ - memcpy (newblock, block, size); - free (block); - } - return newblock; -} - - - diff --git a/libsrc/common/realloc.s b/libsrc/common/realloc.s new file mode 100644 index 000000000..176871dd5 --- /dev/null +++ b/libsrc/common/realloc.s @@ -0,0 +1,213 @@ +; +; Colin Leroy-Mira, 2024 +; +; void* __fastcall__ realloc (void* block, register size_t size) +; + + .importzp ptr1, ptr2, ptr3, ptr4, tmp1, tmp2, tmp3, tmp4, sp + .import _malloc, _memcpy, _free + .import pushax, popptr1, return0 + .import incsp2, decsp2 + .export _realloc + + .include "_heap.inc" + + .macpack generic + +;---------------------------------------------------------------------------- +; Aliases for clarity + +block = ptr1 +size = ptr2 +ublock = ptr3 +oldsize = ptr4 +newblock = tmp1 ; (and tmp2) +orgblock = tmp3 ; (and tmp4) + +;---------------------------------------------------------------------------- +; Code + +_realloc: + sta size ; Store size + stx size+1 + + jsr popptr1 ; Pop block + + lda block+1 ; Is block null? + tax + ora block + bne :+ + + lda size ; Block is null, just malloc + ldx size+1 + jmp _malloc + +: lda size ; Is size 0? + ora size+1 + bne :+ + + lda block ; It is: free block (high byte already in X) + jsr _free + jmp return0 + +: clc ; Add internal used size + lda size + adc #HEAP_ADMIN_SPACE + sta size + bcc :+ + inc size+1 + bne :+ + + lda #$00 ; Size high byte now 0: We overflowed! + tax + rts + +: ldx size+1 ; Should we round size up? + bne :+ + cmp #.sizeof (freeblock) + bcs :+ + + lda #.sizeof (freeblock) + sta size ; (we presuppose that sizeof (freeblock) is < 256) + +: lda block ; Get pointer to raw memory block + sta orgblock ; Store original pointer + sec + sbc #.sizeof(usedblock) + sta ublock + lda block+1 + sta orgblock+1 ; Finish storing original pointer + sbc #0 + sta ublock+1 ; We have our usedblock struct + + ; Get block start + ldy #usedblock::start+1 + lda (ublock),y + tax ; Backup ublock high + dey + lda (ublock),y + + sta ublock ; Store ublock + stx ublock+1 + + ; Remember oldsize + ldy #usedblock::size+1 + lda (ublock),y + sta oldsize+1 + dey + lda (ublock),y + sta oldsize + + clc ; Is the block at heap top? + adc ublock + tay + lda ublock+1 + adc oldsize+1 + cmp ___heapptr+1 + bne must_malloc_new + cpy ___heapptr + bne must_malloc_new + + tya ; Put ___heapptr back in A + sec ; Check if we have enough memory at heap top + sbc oldsize ; Subtract oldsize + sta newblock + lda ___heapptr+1 + sbc oldsize+1 + sta newblock+1 + clc + lda newblock ; And add size + adc size + sta newblock + lda newblock+1 + adc size+1 + sta newblock+1 + bcs must_malloc_new ; If we have a carry there we overflowed + + cmp ___heapend+1 + bne :+ + lda newblock + cmp ___heapend +: bcc :+ + bne must_malloc_new + +: lda newblock ; There is enough space + sta ___heapptr ; Update heapptr + lda newblock+1 + sta ___heapptr+1 + + ldy #usedblock::start+1 + lda ublock+1 + sta (ublock),y ; Update block start + dey + lda ublock + sta (ublock),y + dey + + .assert usedblock::size = usedblock::start-2, error + lda size+1 + sta (ublock),y ; Update block size + dey + lda size + sta (ublock),y + + lda orgblock ; Return original block + ldx orgblock+1 + rts + +must_malloc_new: ; The block is not at heap top, or too big + lda size+1 + pha ; Backup new size (at this point the only ptr + tax ; we'll need after malloc). tmp* are safe + lda size ; from malloc, memcpy and free. + pha + jsr _malloc + + cmp #$00 ; Did malloc succeed? + bne :+ + cpx #$00 + bne :+ + pla ; Pop size backup and return NULL + pla + txa ; X already 0 + rts ; No + +: sta newblock ; Yes, store newblock + stx newblock+1 + jsr pushax ; Push newblock for memcpy + + lda orgblock ; Push orgblock for memcpy + ldx orgblock+1 + jsr pushax + + sec ; Remove admin space from oldsize + lda oldsize + sbc #<HEAP_ADMIN_SPACE + sta oldsize + lda oldsize+1 + sbc #>HEAP_ADMIN_SPACE + sta oldsize+1 + + pla ; Restore new size to AX + tay + pla + tax + tya + + cmp oldsize ; Find the smallest size + bcc :+ + cpx oldsize+1 + bcc :+ + + lda oldsize + ldx oldsize+1 + +: jsr _memcpy ; And copy data + + lda orgblock ; Free old block + ldx orgblock+1 + jsr _free + + lda newblock ; Return new block + ldx newblock+1 + rts diff --git a/libsrc/common/rewind.c b/libsrc/common/rewind.c deleted file mode 100644 index 333230b74..000000000 --- a/libsrc/common/rewind.c +++ /dev/null @@ -1,25 +0,0 @@ -/* -** rewind.c -** -** Christian Groessler, 07-Aug-2000 -*/ - - - -#include <stdio.h> - - - -/*****************************************************************************/ -/* Code */ -/*****************************************************************************/ - - - -void __fastcall__ rewind (FILE* f) -{ - fseek(f, 0L, SEEK_SET); - clearerr(f); -} - - diff --git a/libsrc/common/rewind.s b/libsrc/common/rewind.s new file mode 100644 index 000000000..e7013505c --- /dev/null +++ b/libsrc/common/rewind.s @@ -0,0 +1,35 @@ +; +; Colin Leroy-Mira <colin@colino.net> +; +; void __fastcall__ rewind (FILE* f) +; /* Rewind a file */ +; + + .export _rewind + + .import _fseek, _clearerr + .import pushax, pushl0, popax + + .include "stdio.inc" + + +; ------------------------------------------------------------------------ +; Code + +.proc _rewind + + ; Push f twice (once for fseek, once for clearerr later) + jsr pushax + jsr pushax + + ; Push offset (long) zero + jsr pushl0 + + lda #SEEK_SET + jsr _fseek + + ; Clear error + jsr popax + jmp _clearerr + +.endproc diff --git a/libsrc/common/stpcpy.s b/libsrc/common/stpcpy.s new file mode 100644 index 000000000..c8a10db94 --- /dev/null +++ b/libsrc/common/stpcpy.s @@ -0,0 +1,22 @@ +; +; Colin Leroy-Mira, 4 Sept. 2024 +; +; char* stpcpy (char* dest, const char* src); +; + + .export _stpcpy + .import _strcpy + + .importzp tmp1, ptr2 + +_stpcpy: + jsr _strcpy + + ldx ptr2+1 ; Load dest pointer's last high byte + tya ; Get the last offset strcpy wrote to + + clc + adc ptr2 ; Add to low byte value + bcc :+ + inx +: rts ; Return pointer to dest's terminator diff --git a/libsrc/common/strcasestr.s b/libsrc/common/strcasestr.s new file mode 100644 index 000000000..58364f419 --- /dev/null +++ b/libsrc/common/strcasestr.s @@ -0,0 +1,95 @@ +; +; Ullrich von Bassewitz, 11.12.1998 +; +; char* strcasestr (const char* haystack, const char* needle); +; + + .export _strcasestr + .import popptr1, return0, tolowerdirect + .importzp ptr1, ptr2, ptr3, ptr4, tmp1, tmp2, tmp3, tmp4 + .include "ctype.inc" + + .segment "LOWCODE" + +_strcasestr: + sta ptr2 ; Save needle + stx ptr2+1 + sta ptr4 ; Setup temp copy for later + + jsr popptr1 ; Get haystack to ptr1 + +; If needle is empty, return haystack + + ; ldy #$00 Y=0 guaranteed by popptr1 + lda (ptr2),y ; Get first byte of needle + beq @Found ; Needle is empty --> we're done + +; Search for the beginning of the string (this is not an optimal search +; strategy [in fact, it's pretty dumb], but it's simple to implement). + + jsr tolowerdirect ; Lowercase + sta tmp1 ; Save start of needle +@L1: lda (ptr1),y ; Get next char from haystack + beq @NotFound ; Jump if end + + jsr tolowerdirect ; Lowercase + cmp tmp1 ; Start of needle found? + beq @L2 ; Jump if so + iny ; Next char + bne @L1 + inc ptr1+1 ; Bump high byte + bne @L1 ; Branch always + +; We found the start of needle in haystack + +@L2: tya ; Get offset + clc + adc ptr1 + sta ptr1 ; Make ptr1 point to start + bcc @L3 + inc ptr1+1 + +; ptr1 points to the start of needle in haystack now. Setup temporary pointers for the +; search. The low byte of ptr4 is already set. + +@L3: sta ptr3 + lda ptr1+1 + sta ptr3+1 + lda ptr2+1 + sta ptr4+1 + ldy #1 ; First char is identical, so start on second + +; Do the compare + +@L4: lda (ptr4),y ; Get char from needle + beq @Found ; Jump if end of needle (-> found) + + jsr tolowerdirect ; Lowercase + sta tmp2 + + lda (ptr3),y ; Compare with haystack + + jsr tolowerdirect ; Lowercase + cmp tmp2 + bne @L5 ; Jump if not equal + iny ; Next char + bne @L4 + inc ptr3+1 + inc ptr4+1 ; Bump hi byte of pointers + bne @L4 ; Next char (branch always) + +; The strings did not compare equal, search next start of needle + +@L5: ldy #1 ; Start after this char + bne @L1 ; Branch always + +; We found the start of needle + +@Found: lda ptr1 + ldx ptr1+1 + rts + +; We reached end of haystack without finding needle + +@NotFound: + jmp return0 ; return NULL diff --git a/libsrc/common/strcpy.s b/libsrc/common/strcpy.s index 77b39fe76..9a100f540 100644 --- a/libsrc/common/strcpy.s +++ b/libsrc/common/strcpy.s @@ -25,6 +25,9 @@ L1: lda (ptr1),y inc ptr2+1 bne L1 -L9: lda ptr2 ; X still contains high byte - rts +L9: lda ptr2 ; X still contains dest's original high byte + ; On exit, we want AX to be dest (as this is what strcpy returns). + ; We also want (ptr2),y to still point to dest's terminator, as this + ; is used by stpcpy(). + rts diff --git a/libsrc/common/strcspn.s b/libsrc/common/strcspn.s index 4bb01479a..418bf6ac2 100644 --- a/libsrc/common/strcspn.s +++ b/libsrc/common/strcspn.s @@ -7,13 +7,13 @@ .export _strcspn .import popptr1, _strlen - .importzp ptr1, ptr2, tmp1, tmp2 + .importzp ptr1, ptr4, tmp1, tmp2 _strcspn: - jsr _strlen ; get length in a/x and transfer s2 to ptr2 + jsr _strlen ; get length in a/x and transfer s2 to ptr4 ; Note: It does not make sense to ; have more than 255 test chars, so - ; we don't support a high byte here! (ptr2+1 is + ; we don't support a high byte here! (ptr4+1 is ; also unchanged in strlen then (important!)) ; -> the original implementation also ; ignored this case @@ -38,7 +38,7 @@ checkNext: iny check: cpy tmp1 ; compare with length of test character string beq endOfTestChars - cmp (ptr2),y ; found matching char? + cmp (ptr4),y ; found matching char? bne checkNext leave: txa ; restore position of finding diff --git a/libsrc/common/strdup.s b/libsrc/common/strdup.s index 3ab07bda1..94f2cd338 100644 --- a/libsrc/common/strdup.s +++ b/libsrc/common/strdup.s @@ -1,85 +1,62 @@ ; ; Ullrich von Bassewitz, 18.07.2000 +; Colin Leroy-Mira, 05.01.2024 ; ; char* __fastcall__ strdup (const char* S); ; -; Note: The code knowns which zero page locations are used by malloc. +; Note: The code knowns which zero page locations are used by malloc, +; memcpy and strlen. ; - .importzp sp, tmp1, ptr4 - .import pushax, decsp4, incsp4 - .import _strlen, _malloc, _memcpy + .importzp ptr2, ptr3, ptr4, tmp1, tmp2, tmp3 + .import _strlen_ptr4, _malloc, _memcpy, pushax .export _strdup .macpack cpu - .macpack generic _strdup: + ; Get length (and store source in ptr4) + sta ptr4 + stx ptr4+1 + stx tmp1 ; Backup high byte, which + jsr _strlen_ptr4 ; strlen may increment -; Since we need some place to store the intermediate results, allocate a -; stack frame. To make this somewhat more efficient, create the stackframe -; as needed for the final call to the memcpy function. - - pha ; decsp will destroy A (but not X) - jsr decsp4 ; Target/source - -; Store the pointer into the source slot - - ldy #1 - txa - sta (sp),y - pla -.if (.cpu .bitand CPU_ISET_65SC02) - sta (sp) + ; Add null byte for terminator +.if (.cpu .bitand ::CPU_ISET_65SC02) + inc a .else - dey - sta (sp),y + clc + adc #1 .endif - -; Get length of S (which is still in a/x) - - jsr _strlen - -; Calculate strlen(S)+1 (the space needed) - - add #1 - bcc @L1 + bne :+ inx -; Save the space we're about to allocate in ptr4 - -@L1: sta ptr4 - stx ptr4+1 - -; Allocate memory. _malloc will not use ptr4 + ; Store length +: sta tmp2 + stx tmp3 + ; Allocate memory jsr _malloc -; Store the result into the target stack slot - - ldy #2 - sta (sp),y ; Store low byte - sta tmp1 - txa ; Get high byte - iny - sta (sp),y ; Store high byte - -; Check for a NULL pointer - - ora tmp1 + ; Check for NULL + bne :+ + cpx #$00 beq OutOfMemory -; Copy the string. memcpy will return the target string which is exactly -; what we need here. It will also drop the allocated stack frame. + ; Push dest +: jsr pushax + ; Push source lda ptr4 - ldx ptr4+1 ; Load size - jmp _memcpy ; Copy string, drop stackframe + ldx tmp1 + jsr pushax -; Out of memory, return NULL (A = 0) + ; Push length + lda tmp2 + ldx tmp3 + + ; Copy and return the dest pointer + jmp _memcpy OutOfMemory: - tax - jmp incsp4 ; Drop stack frame - - + rts diff --git a/libsrc/common/strftime.c b/libsrc/common/strftime.c index 38ea4850e..2f3cadc2e 100644 --- a/libsrc/common/strftime.c +++ b/libsrc/common/strftime.c @@ -40,7 +40,7 @@ /* Use static local variables for speed */ -#pragma static-locals (on); +#pragma static-locals (on) diff --git a/libsrc/common/strlen.s b/libsrc/common/strlen.s index 8d5bc20fc..b28bffe7a 100644 --- a/libsrc/common/strlen.s +++ b/libsrc/common/strlen.s @@ -2,19 +2,20 @@ ; Ullrich von Bassewitz, 31.05.1998 ; ; Note: strspn & strcspn call internally this function and rely on -; the usage of only ptr2 here! Keep in mind when appling changes +; the usage of only ptr4 here! Keep in mind when applying changes ; and check the other implementations too! ; ; size_t __fastcall__ strlen (const char* s); ; - .export _strlen - .importzp ptr2 + .export _strlen, _strlen_ptr4 + .importzp ptr4 .macpack cpu _strlen: - sta ptr2 ; Save s - stx ptr2+1 + sta ptr4 ; Save s + stx ptr4+1 +_strlen_ptr4: .if (.cpu .bitand ::CPU_ISET_HUC6280) clx cly @@ -27,11 +28,11 @@ _strlen: .endif .endif -L1: lda (ptr2),y +L1: lda (ptr4),y beq L9 iny bne L1 - inc ptr2+1 + inc ptr4+1 inx bne L1 diff --git a/libsrc/common/strspn.s b/libsrc/common/strspn.s index 6fda716be..7e3f707d1 100644 --- a/libsrc/common/strspn.s +++ b/libsrc/common/strspn.s @@ -7,13 +7,13 @@ .export _strspn .import popptr1, _strlen - .importzp ptr1, ptr2, tmp1, tmp2 + .importzp ptr1, ptr4, tmp1, tmp2 _strspn: - jsr _strlen ; get length in a/x and transfer s2 to ptr2 + jsr _strlen ; get length in a/x and transfer s2 to ptr4 ; Note: It does not make sense to ; have more than 255 test chars, so - ; we don't support a high byte here! (ptr2+1 is + ; we don't support a high byte here! (ptr4+1 is ; also unchanged in strlen then (important!)) ; -> the original implementation also ; ignored this case @@ -38,7 +38,7 @@ checkNext: iny check: cpy tmp1 ; compare with length of test character string beq leave - cmp (ptr2),y ; found matching char? + cmp (ptr4),y ; found matching char? bne checkNext foundTestChar: diff --git a/libsrc/common/strstr.s b/libsrc/common/strstr.s index 84f633245..691e5ba5c 100644 --- a/libsrc/common/strstr.s +++ b/libsrc/common/strstr.s @@ -82,14 +82,3 @@ _strstr: lda #$00 ; return NULL tax rts - - - - - - - - - - - diff --git a/libsrc/common/time.s b/libsrc/common/time.s index 40b470f5b..4092e71c6 100644 --- a/libsrc/common/time.s +++ b/libsrc/common/time.s @@ -6,7 +6,7 @@ .export _time - .import decsp1, ldeaxi + .import pusha, ldeaxi .importzp ptr1, sreg, tmp1, tmp2 .include "time.inc" @@ -22,54 +22,50 @@ ; Get the time (machine dependent) - jsr decsp1 + .assert timespec::tv_sec = 0, error + lda #CLOCK_REALTIME + jsr pusha lda #<time ldx #>time jsr _clock_gettime - sta tmp2 - lda #<time - ldx #>time - .assert timespec::tv_sec = 0, error - jsr ldeaxi - sta tmp1 ; Save low byte of result + +; _clock_gettime returns 0 on success and -1 on error. Check that. + + inx ; Did _clock_gettime return -1? + bne @L2 ; Jump if not + +; We had an error so invalidate time. A contains $FF. + + ldy #3 +@L1: sta time,y + dey + bpl @L1 ; Restore timep and check if it is NULL - pla +@L2: pla sta ptr1+1 pla sta ptr1 ; Restore timep ora ptr1+1 ; timep == 0? - beq @L1 + beq @L4 ; timep is not NULL, store the result there ldy #3 - lda sreg+1 +@L3: lda time,y sta (ptr1),y dey - lda sreg - sta (ptr1),y - dey - txa - sta (ptr1),y - dey - lda tmp1 - sta (ptr1),y + bpl @L3 -; If the result is != 0, return -1 +; Load the final result. -@L1: lda tmp2 - beq @L2 - - tax - sta sreg +@L4: lda time+3 sta sreg+1 - rts - -; Reload the low byte of the result and return - -@L2: lda tmp1 + lda time+2 + sta sreg + ldx time+1 + lda time rts .endproc diff --git a/libsrc/common/tolower.s b/libsrc/common/tolower.s index 828be1cb1..9c143f1ce 100644 --- a/libsrc/common/tolower.s +++ b/libsrc/common/tolower.s @@ -10,19 +10,20 @@ ; int tolower (int c); ; - .export _tolower + .export _tolower, tolowerdirect .include "ctype.inc" .import ctypemaskdirect _tolower: cpx #$00 ; out of range? - bne @L2 ; if so, return the argument unchanged - tay ; save char + bne out ; if so, return the argument unchanged +tolowerdirect: + pha ; save char jsr ctypemaskdirect ; get character classification and #CT_UPPER ; upper case char? beq @L1 ; jump if no - tya ; restore char + pla ; restore char adc #<('a'-'A') ; make lower case char (ctypemaskdirect ensures carry clear) rts -@L1: tya ; restore char -@L2: rts +@L1: pla ; restore char +out: rts diff --git a/libsrc/common/ungetc.s b/libsrc/common/ungetc.s index 7e8c1f94f..c661600af 100644 --- a/libsrc/common/ungetc.s +++ b/libsrc/common/ungetc.s @@ -62,10 +62,6 @@ ; File is not open or the character is invalid error: lda #EINVAL - jsr ___seterrno - lda #$FF ; Return -1 - tax - rts + jmp ___directerrno .endproc - diff --git a/libsrc/common/vsprintf.s b/libsrc/common/vsprintf.s index b4cb9c419..65a961955 100644 --- a/libsrc/common/vsprintf.s +++ b/libsrc/common/vsprintf.s @@ -30,7 +30,7 @@ _vsprintf: ldy #2 jsr staxysp -; Contine by jumping to vsnprintf, which expects ap on the CPU stack and will +; Continue by jumping to vsnprintf, which expects ap on the CPU stack and will ; cleanup the C stack jmp vsnprintf diff --git a/libsrc/common/zx02.s b/libsrc/common/zx02.s new file mode 100644 index 000000000..02256a768 --- /dev/null +++ b/libsrc/common/zx02.s @@ -0,0 +1,148 @@ +; void __fastcall__ decompress_zx02(const void *src, void *dest) +; +; De-compressor for ZX02 files +; +; Compress with: +; zx02 input.bin output.zx0 +; +; (c) 2022 DMSC +; Code under MIT license, see LICENSE file. + + .export _decompress_zx02 + + .import popax + .importzp ptr1, ptr2, ptr3, tmp1, tmp2 + +offset_hi = tmp1 +ZX0_src = ptr1 +ZX0_dst = ptr2 +bitr = tmp2 +pntr = ptr3 + +.proc _decompress_zx02 + sta ZX0_dst + stx ZX0_dst+1 + + jsr popax + sta ZX0_src + stx ZX0_src+1 + + ; Init values + lda #$80 + sta bitr + ldy #$FF + sty pntr + iny + sty offset_hi ; Y = 0 at end of init + +; Decode literal: Copy next N bytes from compressed file +; Elias(length) byte[1] byte[2] ... byte[N] +decode_literal: + ldx #$01 + jsr get_elias + +cop0: + lda (ZX0_src), y + inc ZX0_src + bne :+ + inc ZX0_src+1 + +: sta (ZX0_dst),y + inc ZX0_dst + bne :+ + inc ZX0_dst+1 + +: dex + bne cop0 + + asl bitr + bcs dzx0s_new_offset + + ; Copy from last offset (repeat N bytes from last offset) + ; Elias(length) + inx + jsr get_elias + +dzx0s_copy: + lda ZX0_dst+1 + sbc offset_hi ; C=0 from get_elias + sta pntr+1 + +cop1: + ldy ZX0_dst + lda (pntr), y + ldy #0 + sta (ZX0_dst),y + inc ZX0_dst + bne :+ + inc ZX0_dst+1 + inc pntr+1 +: dex + bne cop1 + + asl bitr + bcc decode_literal + +; Copy from new offset (repeat N bytes from new offset) +; Elias(MSB(offset)) LSB(offset) Elias(length-1) +dzx0s_new_offset: + ; Read elias code for high part of offset + inx + jsr get_elias + beq exit ; Read a 0, signals the end + + ; Decrease and divide by 2 + dex + txa + lsr + sta offset_hi + + ; Get low part of offset, a literal 7 bits + lda (ZX0_src), y + inc ZX0_src + bne :+ + inc ZX0_src+1 + +: ; Divide by 2 + ror + eor #$ff + sta pntr + + ; And get the copy length. + ; Start elias reading with the bit already in carry: + ldx #1 + jsr elias_skip1 + + inx + bcc dzx0s_copy + +; Read an elias-gamma interlaced code. +elias_get: + ; Read next data bit to result + asl bitr + rol + tax + +get_elias: + ; Get one bit + asl bitr + bne elias_skip1 + + ; Read new bit from stream + lda (ZX0_src), y + inc ZX0_src + bne :+ + inc ZX0_src+1 + +: ; sec ; not needed, C=1 guaranteed from last bit + rol + sta bitr + +elias_skip1: + txa + bcs elias_get + + ; Got ending bit, stop reading +exit: + rts +.endproc diff --git a/libsrc/conio/cputs.s b/libsrc/conio/cputs.s index 41191a0b0..62e757b84 100644 --- a/libsrc/conio/cputs.s +++ b/libsrc/conio/cputs.s @@ -8,28 +8,46 @@ .export _cputsxy, _cputs .import gotoxy, _cputc .importzp ptr1, tmp1 + .macpack cpu _cputsxy: sta ptr1 ; Save s for later stx ptr1+1 jsr gotoxy ; Set cursor, pop x and y +.if (.cpu .bitand CPU_ISET_65SC02) + bra L0 ; Same as cputs... +.else jmp L0 ; Same as cputs... +.endif _cputs: sta ptr1 ; Save s stx ptr1+1 -L0: ldy #0 -L1: lda (ptr1),y - beq L9 ; Jump if done + +.if (.cpu .bitand CPU_ISET_65SC02) + +L0: lda (ptr1) ; (5) + beq L9 ; (7) Jump if done + jsr _cputc ; (13) Output char, advance cursor + inc ptr1 ; (18) Bump low byte + bne L0 ; (20) Next char + inc ptr1+1 ; (25) Bump high byte + bne L0 + +.else + +L0: ldy #0 ; (2) +L1: lda (ptr1),y ; (7) + beq L9 ; (9) Jump if done iny - sty tmp1 ; Save offset - jsr _cputc ; Output char, advance cursor - ldy tmp1 ; Get offset - bne L1 ; Next char - inc ptr1+1 ; Bump high byte + sty tmp1 ; (14) Save offset + jsr _cputc ; (20) Output char, advance cursor + ldy tmp1 ; (23) Get offset + bne L1 ; (25) Next char + inc ptr1+1 ; (30) Bump high byte bne L1 +.endif + ; Done L9: rts - - diff --git a/libsrc/conio/vcprintf.s b/libsrc/conio/vcprintf.s index 084efe089..595a2d2c5 100644 --- a/libsrc/conio/vcprintf.s +++ b/libsrc/conio/vcprintf.s @@ -10,7 +10,7 @@ .importzp sp, ptr1, ptr2, ptr3, tmp1 .macpack generic - + .macpack cpu .data @@ -74,21 +74,40 @@ out: jsr popax ; count ; Loop outputting characters +.if (.cpu .bitand CPU_ISET_65SC02) + @L1: dec outdesc+6 beq @L4 -@L2: ldy tmp1 - lda (ptr1),y - iny - bne @L3 - inc ptr1+1 -@L3: sty tmp1 - jsr _cputc - jmp @L1 +@L2: lda (ptr1) ; (5) + inc ptr1 ; (10) + bne @L3 ; (12) + inc ptr1+1 ; (17) +@L3: jsr _cputc ; (23) + bra @L1 ; (26) @L4: dec outdesc+7 bne @L2 rts +.else + +@L1: dec outdesc+6 + beq @L4 +@L2: ldy tmp1 ; (3) + lda (ptr1),y ; (8) + iny ; (10) + bne @L3 ; (12) + inc ptr1+1 ; (17) +@L3: sty tmp1 ; (20) + jsr _cputc ; (26) + jmp @L1 ; (32) + +@L4: dec outdesc+7 + bne @L2 + rts + +.endif + ; ---------------------------------------------------------------------------- ; vcprintf - formatted console i/o ; diff --git a/libsrc/creativision/cputc.s b/libsrc/creativision/cputc.s index 437b738b2..4389234c1 100644 --- a/libsrc/creativision/cputc.s +++ b/libsrc/creativision/cputc.s @@ -95,12 +95,6 @@ IS_UPPER: BAD_CHAR: jmp plot -;----------------------------------------------------------------------------- -; Initialize the conio subsystem. "INIT" segment is nothing special on the -; Creativision, it is part of the "ROM" memory. - -.segment "INIT" - initconio: lda #$0 sta SCREEN_PTR diff --git a/libsrc/cx16/getdevice.s b/libsrc/cx16/getdevice.s index 5f2e74af4..70f883db0 100644 --- a/libsrc/cx16/getdevice.s +++ b/libsrc/cx16/getdevice.s @@ -56,7 +56,7 @@ next: inx lda STATUS -; Either the Kernal calls above were successfull, or there was +; Either the Kernal calls above were successful, or there was ; already a cmdchannel to the device open -- which is a pretty ; good indication of its existence. ;-) diff --git a/libsrc/cx16/tgi/cx320p1.s b/libsrc/cx16/tgi/cx320p1.s index 2fcd9adf3..f42d91c33 100644 --- a/libsrc/cx16/tgi/cx320p1.s +++ b/libsrc/cx16/tgi/cx320p1.s @@ -91,7 +91,7 @@ Y2 := ptr4 .bss -; The colors are indicies into a TGI palette. The TGI palette is indicies into +; The colors are indices into a TGI palette. The TGI palette is indices into ; VERA's palette. Vera's palette is a table of Red, Green, and Blue levels. ; The first 16 RGB elements mimic the Commodore 64's colors. @@ -267,7 +267,7 @@ GETPALETTE: ; GETDEFPALETTE: Return the default palette for the driver in .XA. All ; drivers should return something reasonable here, even drivers that don't ; support palettes, otherwise the caller has no way to determine the colors -; of the (not changable) palette. +; of the (not changeable) palette. ; ; Must set an error code: NO (all drivers must have a default palette) diff --git a/libsrc/cx16/tgi/cx640p1.s b/libsrc/cx16/tgi/cx640p1.s new file mode 100644 index 000000000..815c4ab67 --- /dev/null +++ b/libsrc/cx16/tgi/cx640p1.s @@ -0,0 +1,675 @@ +; +; Graphics driver for the 640 pixels across, 480 pixels down, 2 color mode +; on the Commander X16 +; +; 2024-06-11, Scott Hutter +; Based on code by Greg King +; + + .include "zeropage.inc" + + .include "tgi-kernel.inc" + .include "tgi-error.inc" + + .include "cbm_kernal.inc" + .include "cx16.inc" + + .macpack generic + .macpack module + + +; ------------------------------------------------------------------------ +; Header. Includes jump table and constants. + + module_header _cx640p1_tgi ; 640 pixels across, 1 pixel per bit + +; First part of the header is a structure that has a signature, +; and defines the capabilities of the driver. + + .byte $74, $67, $69 ; ASCII "tgi" + .byte TGI_API_VERSION ; TGI API version number + .addr $0000 ; Library reference + .word 640 ; X resolution + .word 480 ; Y resolution + .byte 2 ; Number of drawing colors + .byte 0 ; Number of screens available + .byte 8 ; System font X size + .byte 8 ; System font Y size + .word $0100 ; Aspect ratio (based on VGA display) + .byte 0 ; TGI driver flags + +; Next, comes the jump table. Currently, all entries must be valid, +; and may point to an RTS for test versions (function not implemented). + + .addr INSTALL + .addr UNINSTALL + .addr INIT + .addr DONE + .addr GETERROR + .addr CONTROL + .addr CLEAR + .addr SETVIEWPAGE + .addr SETDRAWPAGE + .addr SETCOLOR + .addr SETPALETTE + .addr GETPALETTE + .addr GETDEFPALETTE + .addr SETPIXEL + .addr GETPIXEL + .addr LINE + .addr BAR + .addr TEXTSTYLE + .addr OUTTEXT + + +; ------------------------------------------------------------------------ +; Constant + + + +; ------------------------------------------------------------------------ +; Data. + +; Variables mapped to the zero page segment variables. Some of these are +; used for passing parameters to the driver. + +X1 = ptr1 +Y1 = ptr2 +X2 = ptr3 +Y2 = ptr4 + +ADDR = tmp1 ; ADDR+1,2,3 + +TEMP = tmp3 +TEMP2 = tmp4 ; HORLINE +TEMP3 = sreg ; HORLINE + + +; Absolute variables used in the code + +.bss + +; The colors are indices into a TGI palette. The TGI palette is indices into +; VERA's palette. Vera's palette is a table of Red, Green, and Blue levels. +; The first 16 RGB elements mimic the Commodore 64's colors. + +SCRBASE: .res 1 ; High byte of screen base +BITMASK: .res 1 ; $00 = clear, $FF = set pixels + +defpalette: .res 2 +palette: .res 2 + +color: .res 1 ; Stroke and fill index +text_mode: .res 1 ; Old text mode + +tempX: .res 2 +tempY: .res 2 +ERR2: .res 1 +ERR: .res 1 +SY: .res 1 +SX: .res 1 +DY: .res 1 +DX: .res 1 +CURRENT_Y: .res 2 +CURRENT_X: .res 2 + +.data + +ERROR: .byte TGI_ERR_OK ; Error code + + +; Constants and tables + +.rodata + +veracolors: +col_black: .byte %00000000, %00000000 +col_white: .byte %11111111, %00001111 +col_red: .byte %00000000, %00001000 +col_cyan: .byte %11111110, %00001010 +col_purple: .byte %01001100, %00001100 +col_green: .byte %11000101, %00000000 +col_blue: .byte %00001010, %00000000 +col_yellow: .byte %11100111, %00001110 +col_orange: .byte %10000101, %00001101 +col_brown: .byte %01000000, %00000110 +col_lred: .byte %01110111, %00001111 +col_gray1: .byte %00110011, %00000011 +col_gray2: .byte %01110111, %00000111 +col_lgreen: .byte %11110110, %00001010 +col_lblue: .byte %10001111, %00000000 +col_gray3: .byte %10111011, %00001011 + +; Bit masks for setting pixels +bitMasks1: + .byte %10000000, %01000000, %00100000, %00010000 + .byte %00001000, %00000100, %00000010, %00000001 +bitMasks2: + .byte %01111111, %10111111, %11011111, %11101111 + .byte %11110111, %11111011, %11111101, %11111110 + + +.code + +; ------------------------------------------------------------------------ +; INSTALL routine. Is called after the driver is loaded into memory. May +; initialize anything that has to be done just once. Is probably empty +; most of the time. +; +; Must set an error code: NO + +INSTALL: +; Create the default palette. + lda #$00 + sta defpalette + lda #$01 + sta defpalette+1 + + ; Fall through. + +; ------------------------------------------------------------------------ +; UNINSTALL routine. Is called before the driver is removed from memory. May +; clean up anything done by INSTALL, but is probably empty most of the time. +; +; Must set an error code: NO + +UNINSTALL: + rts + +; ------------------------------------------------------------------------ +; INIT: Changes an already installed device from text mode to graphics +; mode. +; Note that INIT/DONE may be called multiple times while the driver +; is loaded, while INSTALL is called only once; so, any code that is needed +; to initiate variables and so on must go here. Setting the palette is not +; needed because that is called by the graphics kernel later. +; The graphics kernel never will call INIT when a graphics mode already is +; active, so there is no need to protect against that. +; +; Must set an error code: YES + +INIT: stz ERROR ; #TGI_ERR_OK + +; Save the current text mode. + + sec + jsr SCREEN_MODE + sta text_mode + +; Switch into (640 x 480 x 2 bpp) graphics mode. + + lda #%00000000 ; DCSEL = 0, VRAM port 1 + sta VERA::CTRL + lda #%00100001 ; Disable sprites, layer 1 enable, VGA + sta VERA::DISP::VIDEO + lda #%00000100 ; Bitmap mode enable + sta VERA::L1::CONFIG + lda #%00000001 ; Tile width 640 + sta VERA::L1::TILE_BASE + rts + +; ------------------------------------------------------------------------ +; DONE: Will be called to switch the graphics device back into text mode. +; The graphics kernel never will call DONE when no graphics mode is active, +; so there is no need to protect against that. +; +; Must set an error code: NO + +DONE: + jsr CINT + lda text_mode + clc + jmp SCREEN_MODE + +; ------------------------------------------------------------------------ +; GETERROR: Return the error code in .A, and clear it. + +GETERROR: + lda ERROR + stz ERROR + rts + +; ------------------------------------------------------------------------ +; CONTROL: Platform-/driver-specific entry point. +; +; Must set an error code: YES + +CONTROL: + lda #TGI_ERR_INV_FUNC + sta ERROR + rts + +; ------------------------------------------------------------------------ +; CLEAR: Clear the screen. +; +; Must set an error code: NO + +CLEAR: + .scope inner + + ; set up DCSEL=2 + lda #(2 << 1) + sta VERA::CTRL + + ; set cache writes + lda #$40 + tsb VERA::DISP::VIDEO ; VERA_FX_CTRL when DCSEL=2 + + ; set FX cache to all zeroes + lda #(6 << 1) + sta VERA::CTRL + + lda #$00 + sta VERA::DISP::VIDEO + sta VERA::DISP::HSCALE + sta VERA::DISP::VSCALE + sta VERA::DISP::FRAME + + stz VERA::CTRL + ; set address and increment for bitmap area + stz VERA::ADDR + stz VERA::ADDR + 1 + lda #$30 ; increment +4 + sta VERA::ADDR + 2 + + ldy #$F0 +@blank_outer: + ldx #$0A +@blank_loop: + + .repeat 8 + stz VERA::DATA0 + .endrep + + dex + bne @blank_loop + dey + bne @blank_outer + + ; set up DCSEL=2 + lda #(2 << 1) + sta VERA::CTRL + + ; set FX off (cache write bit 1 -> 0) + stz VERA::DISP::VIDEO ; VERA_FX_CTRL when DCSEL=2 + stz VERA::CTRL + + .endscope + rts + + +; ------------------------------------------------------------------------ +; SETVIEWPAGE: Set the visible page. Called with the new page in .A (0..n-1). +; The page number already is checked to be valid by the graphics kernel. +; +; Must set an error code: NO (will be called only if page OK) + +SETVIEWPAGE: + + ; Fall through. + +; ------------------------------------------------------------------------ +; SETDRAWPAGE: Set the drawable page. Called with the new page in .A (0..n-1). +; The page number already is checked to be valid by the graphics kernel. +; +; Must set an error code: NO (will be called only if page OK) + +SETDRAWPAGE: + rts + +; ------------------------------------------------------------------------ +; SETPALETTE: Set the palette (not available with all drivers/hardware). +; A pointer to the palette is passed in ptr1. Must set an error if palettes +; are not supported +; +; Must set an error code: YES + +SETPALETTE: + stz ERROR ; #TGI_ERR_OK + ldy #$01 ; Palette size of 2 colors +@L1: lda (ptr1),y ; Copy the palette + sta palette,y + dey + bpl @L1 + + ; set background color from palette color 0 + lda #$00 + sta VERA::ADDR + lda #$FA + sta VERA::ADDR+1 + lda #$01 + sta VERA::ADDR+2 ; write color RAM @ $1FA00 + + lda palette + asl + tay + lda veracolors,y + sta VERA::DATA0 + + inc VERA::ADDR ; $1FA01 + + lda palette + asl + tay + iny ; second byte of color + lda veracolors,y + sta VERA::DATA0 + + ; set foreground color from palette color 1 + inc VERA::ADDR ; $1FA02 + + lda palette+1 + asl + tay + lda veracolors,y + sta VERA::DATA0 + + inc VERA::ADDR ; $1FA03 + + lda palette+1 + asl + tay + iny ; second byte of color + lda veracolors,y + sta VERA::DATA0 + rts + +; ------------------------------------------------------------------------ +; SETCOLOR: Set the drawing color (in .A). The new color already is checked +; to be in a valid range (0..maxcolor). +; +; Must set an error code: NO (will be called only if color OK) + +SETCOLOR: + tax + beq @L1 + lda #$FF +@L1: sta BITMASK + stx color + rts + +; ------------------------------------------------------------------------ +; GETPALETTE: Return the current palette in .XA. Even drivers that cannot +; set the palette should return the default palette here, so there's no +; way for this function to fail. +; +; Must set an error code: NO + +GETPALETTE: + lda #<palette + ldx #>palette + rts + +; ------------------------------------------------------------------------ +; GETDEFPALETTE: Return the default palette for the driver in .XA. All +; drivers should return something reasonable here, even drivers that don't +; support palettes, otherwise the caller has no way to determine the colors +; of the (not changeable) palette. +; +; Must set an error code: NO (all drivers must have a default palette) + +GETDEFPALETTE: + lda #<defpalette + ldx #>defpalette + rts + +; ------------------------------------------------------------------------ +; SETPIXEL: Draw one pixel at X1/Y1 = ptr1/ptr2 with the current drawing +; color. The co-ordinates passed to this function never are outside the +; visible screen area, so there is no need for clipping inside this function. +; +; Must set an error code: NO + +SETPIXEL: + jsr CALC + + stx TEMP + + lda ADDR + ldy ADDR+1 + ldx #$00 + + sta VERA::ADDR + sty VERA::ADDR + 1 + stx VERA::ADDR + 2 + + ldx TEMP + + lda BITMASK + beq @ahead + + ; if BITMASK = $00, white is line color + ; Set the bit in the byte at VERA_DATA0 + lda VERA::DATA0 ; Load the byte at memory address + ora bitMasks1,X ; OR with the bit mask + sta VERA::DATA0 ; Store back the modified byte + rts + +@ahead: + ; if BITMASK = $FF, black is line color + lda VERA::DATA0 ; Load the byte at memory address + and bitMasks2,X ; OR with the bit mask + sta VERA::DATA0 ; Store back the modified byte + rts + +; ------------------------------------------------------------------------ +; GETPIXEL: Read the color value of a pixel, and return it in .XA. The +; co-ordinates passed to this function never are outside the visible screen +; area, so there is no need for clipping inside this function. + +GETPIXEL: + jsr CALC + + stx TEMP + + lda ADDR + ldy ADDR+1 + ldx #$00 + + sta VERA::ADDR + sty VERA::ADDR + 1 + stx VERA::ADDR + 2 + + ldx TEMP + lda VERA::DATA0 ; Load the byte at memory address + and bitMasks1,X + + bne @ahead + + ldx #$00 + lda #$00 + rts + +@ahead: + ldx #$00 + lda #$01 + rts + +; ------------------------------------------------------------------------ +; BAR: Draw a filled rectangle with the corners X1/Y1, X2/Y2, where +; X1/Y1 = ptr1/ptr2 and X2/Y2 = ptr3/ptr4, using the current drawing color. +; Contrary to most other functions, the graphics kernel will sort and clip +; the co-ordinates before calling the driver; so on entry, the following +; conditions are valid: +; X1 <= X2 +; Y1 <= Y2 +; (X1 >= 0) && (X1 < XRES) +; (X2 >= 0) && (X2 < XRES) +; (Y1 >= 0) && (Y1 < YRES) +; (Y2 >= 0) && (Y2 < YRES) +; +; Must set an error code: NO + +BAR: + ; Initialize tempY with Y1 + lda Y1 + sta tempY + lda Y1+1 + sta tempY+1 + +@outer_loop: + ; Compare tempY with Y2 + lda tempY+1 + cmp Y2+1 + bcc @outer_continue ; If tempY high byte < Y2 high byte, continue + bne @outer_end ; If tempY high byte > Y2 high byte, end + lda tempY + cmp Y2 + bcc @outer_continue ; If tempY low byte < Y2 low byte, continue + beq @outer_end ; If tempY low byte = Y2 low byte, end + +@outer_continue: + ; Initialize tempX with X1 + lda X1 + sta tempX + lda X1+1 + sta tempX+1 + +@inner_loop: + ; Compare tempX with X2 + lda tempX+1 + cmp X2+1 + bcc @inner_continue ; If tempX high byte < X2 high byte, continue + bne @inner_end ; If tempX high byte > X2 high byte, end + lda tempX + cmp X2 + bcc @inner_continue ; If tempX low byte < X2 low byte, continue + +@inner_end: + ; Increment tempY + inc tempY + bne @outer_loop ; If no overflow, continue outer loop + inc tempY+1 ; If overflow, increment high byte + +@inner_continue: + ; Call setpixel(tempX, tempY) + lda X1 + pha + lda X1+1 + pha + lda Y1 + pha + lda Y1+1 + pha + + lda tempX + ldx tempX+1 + sta X1 + stx X1+1 + + lda tempY + ldx tempY+1 + sta Y1 + stx Y1+1 + + jsr SETPIXEL + + pla + sta Y1+1 + pla + sta Y1 + pla + sta X1+1 + pla + sta X1 + + ; Increment tempX + inc tempX + bne @inner_loop_check ; If no overflow, continue + inc tempX+1 ; If overflow, increment high byte + +@inner_loop_check: + ; Compare tempX with X2 again after increment + lda tempX+1 + cmp X2+1 + bcc @inner_continue ; If tempX high byte < X2 high byte, continue + bne @outer_increment ; If tempX high byte > X2 high byte, increment tempY + lda tempX + cmp X2 + bcc @inner_continue ; If tempX low byte < X2 low byte, continue + +@outer_increment: + ; Increment tempY + inc tempY + bne @outer_loop ; If no overflow, continue outer loop + inc tempY+1 ; If overflow, increment high byte + +@outer_end: + jmp @done + +@done: + rts + +; ------------------------------------------------------------------------ +; TEXTSTYLE: Set the style used when calling OUTTEXT. Text scaling in X and Y +; directions are passed in .X and .Y, the text direction is passed in .A. +; +; Must set an error code: NO + +TEXTSTYLE: + rts + +; ------------------------------------------------------------------------ +; OUTTEXT: Output text at X/Y = ptr1/ptr2 using the current color and the +; current text style. The text to output is given as a zero-terminated +; string with address in ptr3. +; +; Must set an error code: NO + +OUTTEXT: + rts + + +; ------------------------------------------------------------------------ +; Calculate all variables to plot the pixel at X1/Y1. +;------------------------ +;< X1,Y1 - pixel +;> ADDR - address of card +;> X - bit number (X1 & 7) +CALC: + lda Y1+1 + sta ADDR+1 + lda Y1 + asl + rol ADDR+1 + asl + rol ADDR+1 ; Y*4 + clc + adc Y1 + sta ADDR + lda Y1+1 + adc ADDR+1 + sta ADDR+1 ; Y*4+Y=Y*5 + lda ADDR + asl + rol ADDR+1 + asl + rol ADDR+1 + asl + rol ADDR+1 + asl + rol ADDR+1 + sta ADDR ; Y*5*16=Y*80 + lda X1+1 + sta TEMP + lda X1 + lsr TEMP + ror + lsr TEMP + ror + lsr TEMP + ror + clc + adc ADDR + sta ADDR + lda ADDR+1 ; ADDR = Y*80+x/8 + adc TEMP + sta ADDR+1 + lda ADDR+1 + lda X1 + and #7 + tax + rts + + +.include "../../tgi/tgidrv_line.inc" diff --git a/libsrc/cx16/videomode.s b/libsrc/cx16/videomode.s index 998316858..46f461fca 100644 --- a/libsrc/cx16/videomode.s +++ b/libsrc/cx16/videomode.s @@ -9,6 +9,13 @@ ; #define VIDEOMODE_40x15 0x04 ; #define VIDEOMODE_20x30 0x05 ; #define VIDEOMODE_20x15 0x06 +; #define VIDEOMODE_22x23 0x07 +; #define VIDEOMODE_64x50 0x08 +; #define VIDEOMODE_64x25 0x09 +; #define VIDEOMODE_32x50 0x0A +; #define VIDEOMODE_32x25 0x0B +; #define VIDEOMODE_80COL VIDEOMODE_80x60 +; #define VIDEOMODE_40COL VIDEOMODE_40x30 ; #define VIDEOMODE_320x240 0x80 ; #define VIDEOMODE_SWAP (-1) ; diff --git a/libsrc/cx16/waitvsync.s b/libsrc/cx16/waitvsync.s index d75a7a735..e0d9f9768 100644 --- a/libsrc/cx16/waitvsync.s +++ b/libsrc/cx16/waitvsync.s @@ -6,7 +6,7 @@ ; ; VERA's vertical sync causes IRQs which increment the jiffy timer. ; -; Updated by ZeroByteOrg to use Kernal API RDTIM to retreive the TIMER variable +; Updated by ZeroByteOrg to use Kernal API RDTIM to retrieve the TIMER variable ; .export _waitvsync diff --git a/libsrc/dbg/dbgdump.s b/libsrc/dbg/dbgdump.s index 8ab646d21..bc6a4bd40 100644 --- a/libsrc/dbg/dbgdump.s +++ b/libsrc/dbg/dbgdump.s @@ -1,7 +1,7 @@ ; ; Ullrich von Bassewitz, 11.08.1998 ; -; char* __cdecl__ DbgMemDump (unsigend Addr, char* Buf, unsigned char Length); +; char* __cdecl__ DbgMemDump (unsigned Addr, char* Buf, unsigned char Length); ; .export _DbgMemDump diff --git a/libsrc/geos-cbm/system/get_ostype.s b/libsrc/geos-cbm/system/get_ostype.s index 6e6731952..9ba8b7982 100644 --- a/libsrc/geos-cbm/system/get_ostype.s +++ b/libsrc/geos-cbm/system/get_ostype.s @@ -82,8 +82,8 @@ tvmode: ; PAL/NTSC check here, result in A pha lda #IO_IN ; enable access to I/O sta CPU_DATA - bit rasreg - bpl tvmode ; wait for rasterline 127<x<256 +: bit rasreg + bpl :- ; wait for rasterline 127<x<256 lda #24 ; (rasterline now >=256!) modelp: cmp rasreg ; wait for rasterline = 24 (or 280 on PAL) diff --git a/libsrc/geos-common/const.inc b/libsrc/geos-common/const.inc index c0294e01d..ab45a19a2 100644 --- a/libsrc/geos-common/const.inc +++ b/libsrc/geos-common/const.inc @@ -1,5 +1,5 @@ ; -;GeosConst - various system constans sorted by function +;GeosConst - various system constants sorted by function ;reassembled by Maciej 'YTM/Elysium' Witkowiak ;4-2-99, 18-3-99 diff --git a/libsrc/geos-common/graph/bitotherclip.s b/libsrc/geos-common/graph/bitotherclip.s index 020139da8..fba00d966 100644 --- a/libsrc/geos-common/graph/bitotherclip.s +++ b/libsrc/geos-common/graph/bitotherclip.s @@ -6,7 +6,7 @@ ; void BitOtherClip (void *proc1, void* proc2, char skipl, char skipr, int skipy, ; struct iconpic *myGfx); -; both proc1, proc2 should be: char __fastcall something (void); +; both proc1, proc2 should be: char foo (void); ; proc1 is called before reading a byte (.A returns next data) ; proc2 is called before reading each byte which is not pattern (code >219) diff --git a/libsrc/geos-common/symbols.txt b/libsrc/geos-common/symbols.txt index d1893e376..b82954ec6 100644 --- a/libsrc/geos-common/symbols.txt +++ b/libsrc/geos-common/symbols.txt @@ -26,7 +26,7 @@ cardDataPntr $2c $2c $60 Word This is a pointer to the actual card graphics dat CPU_DATA $01 n/a n/a Word Address of 6510 data register that controls the hardware memory ... CPU_DDR $00 n/a n/a Byte Address of 6510 data direction register. curDevice $ba $ba n/a Byte This holds the current serial device number. -curDirHead $8200 $8200 $fa80 256 | 39 Bytes For CBM, it is the buffer containing header infomation ... +curDirHead $8200 $8200 $fa80 256 | 39 Bytes For CBM, it is the buffer containing header information ... curDrive $8489 $8489 $f60d Byte Holds the device number of the currently active disk drive. curEnable n/a $1300 $0951 Byte This is an image of the C64 mobenble register. curHeight $29 $29 $021b Byte Used to hold the card height in pixels of the current font in ... @@ -48,7 +48,7 @@ dblClickCount $8515 $8515 $0258 Byte Used to determine when an icon is double c devTabHi n/a n/a $fae7 4 Bytes For the Apple, these are the high and low bytes of the four ... devTabLo n/a n/a $faeb 4 Bytes For the Apple, these are the high and low bytes of the four ... devUnitTab n/a n/a $faef 4 Bytes The ProDos unit numbers of the four possible devices are kept ... -dir2Head $8900 $8900 n/a 256 Bytes This is the 2nd directory header block used for larger cpacity ... +dir2Head $8900 $8900 n/a 256 Bytes This is the 2nd directory header block used for larger capacity ... dirBlkno n/a n/a $f620 Word Block number of the key block of the directory containing ... dirEntryBuf $8400 $8400 $fa59 256 | 39 Bytes Buffer used to build a file's directory entry. dirPtr n/a n/a $f622 Word Pointer into diskBlkBuf for this file's entry. @@ -74,7 +74,7 @@ fileWritten $8498 $8498 $f61a Byte Flag indicating if a if the currently open f firstBoot $88c5 $88c5 $0281 Byte This flag is changed from 0 to $FF when the deskTop comes up ... fontData $850c $850c n/a(?) 9 Bytes Buffer for saving the user active font table when going into ... fontTable $26 $26 n/a(?) 8 Bytes fontTable is a label for the beginning of variables for the ... -grcntr12 $d016 $d016 n/a Byte Graphics control reqister #2. +grcntr12 $d016 $d016 n/a Byte Graphics control register #2. grcntrl1 $d011 $d011 n/a Byte Graphics control register #1. grirq $d019 $d019 n/a Byte Graphics chip interrupt register. grirqen $d01a $d01a n/a Byte Graphics chip interrupt enable register. @@ -89,7 +89,7 @@ intBotVector $849f $849f $0204 Word Vector to routine to call after the operati interleave $848c $848c n/a Byte Variable used by BlkAlloc routine as the desired interleave when ... intSource n/a n/a $02c6 Byte Byte to indicate where interrupts are coming from on the Apple. intTopVector $849d $849d $0202 Word Vector to routine to call before the operating system interrupt ... -invertBuffer n/a $1ced n/a 80 Bytes Buffer area used to speed up the 80 colunn InvertLine routine. +invertBuffer n/a $1ced n/a 80 Bytes Buffer area used to speed up the 80 column InvertLine routine. irqvec $0314 $0314 n/a Word IRQ vector. isGEOS $848b $848b n/a Byte Flag to indicate whether the current disk is a GEOS disk. keyData $8504 $8504 $0245 Byte Holds the ASCII value of the current last key that was pressed. @@ -152,7 +152,7 @@ msbxpos $d010 $d010 n/a Byte Most significant bits for x positions of sprites. msePicPtr $31 $31 n/a Word Pointer to the mouse graphics data. nationality $c010 $c010 $e00d Byte Byte to hold nationality of Kernal. nmivec $0318 $0318 n/a Word NMI vector. -noEraseSprites n/a n/a $0240 Byte Flag to stop routine TempHideMouse fron erasing sprites #2 ... +noEraseSprites n/a n/a $0240 Byte Flag to stop routine TempHideMouse from erasing sprites #2 ... numDrives $848d $848d $f60e Byte Set to number of drives on the system. obj0Pointer $8ff8 $8ff8 n/a Byte Pointer to the picture data for sprite 0. obj1Pointer $8ff9 $8ff9 n/a Byte Pointer to the picture data for sprite 1. @@ -169,7 +169,7 @@ PrntDiskName $8476 $8476 n/a 18 Bytes Disk name that current printer driver is o PrntFileName $8465 $8465 $08ac 17 | 16 Bytes Name of the current printer driver. ramBase $88c7 $88c7 n/a 4 Bytes RAM bank for each disk drive to use if the drive type is either ... ramExpSize $88c3 $88c3 n/a Byte Byte for number or RAM banks available in RAM expansion unit. -random $850a $850a $024c Word Variable incremented each interrupt to generate a randon nunber. +random $850a $850a $024c Word Variable incremented each interrupt to generate a randon number. rasreg $d012 $d012 n/a Byte Raster register. RecoverVector $84b1 $84b1 $0216 Word Pointer to routine that is called to recover the background ... reqXpos0 n/a n/a $0800 Word This variable corresponds to the Commodore VIC chip register ... @@ -193,7 +193,7 @@ shiftBuf n/a $1b45 $70 7 Bytes Buffer for shifting/doubling sprites. Located in shiftOutBuf n/a $1b4c $78 7 Bytes Buffer for shifting/doubling/oring sprites. Located in back ... sizeFlags n/a $1b53 $db1c Byte Height of sprite. softOnes n/a $1c2d $d000 192 Bytes Buffer used for putting sprite bitmaps up on screen without ... -softZeros n/a $1b6d $d0e0 192 Bytes Buffer used for putting sprite bitnaps up on screen without ... +softZeros n/a $1b6d $d0e0 192 Bytes Buffer used for putting sprite bitmaps up on screen without ... spr0pic $8a00 $8a00 n/a 64 Bytes This is where the graphics data for sprite 0 is kept on ... spr1pic $8a40 $8a40 n/a 64 Bytes This is where the graphics data for sprite 1 is kept on ... spr2pic $8a80 $8a80 n/a 64 Bytes This is where the graphics data for sprite 2 is kept on ... @@ -353,8 +353,8 @@ i_Rectangle $c19f $c19f $fe3c Inline Rectangle. ImprintLine n/a n/a $ff8f Imprint horizontal line to background buffer. ImprintRectangle $c250 $c250 $fe4e Imprint rectangular area to background buffer. InfoCard n/a n/a $670f Get I/O card attributes. -InitCard n/a n/a $6700 Intialize I/O card. -InitForDialog n/a n/a $ff4a Internal pre-dialog box intialization. +InitCard n/a n/a $6700 Initialize I/O card. +InitForDialog n/a n/a $ff4a Internal pre-dialog box initialization. InitForIO $c25c $c25c n/a Prepare CBM system for I/O across serial bus. InitForPrint $7900 $7900 $6000 Initialize printer (once per document). InitMouse $fe80 $fe80 $f000 Initialize input device. diff --git a/libsrc/kim1/scandisplay.s b/libsrc/kim1/scandisplay.s index 0f46a5de4..768adb2b9 100644 --- a/libsrc/kim1/scandisplay.s +++ b/libsrc/kim1/scandisplay.s @@ -15,7 +15,6 @@ sta $FA ; Middle display data jsr popa sta $FB ; Leftmost display data - jsr SCANDS - rts + jmp SCANDS .endproc diff --git a/libsrc/kim1/tapeio.s b/libsrc/kim1/tapeio.s index 4a16d6b9c..4af2b3aa2 100644 --- a/libsrc/kim1/tapeio.s +++ b/libsrc/kim1/tapeio.s @@ -16,7 +16,7 @@ sta ID ; Tape record ID to P1L jsr LOADT ; Read data from tape bcs error - jmp return0 ; Return 0 if sucessful + jmp return0 ; Return 0 if successful error: jmp return1 ; or 1 if not .endproc @@ -33,7 +33,7 @@ error: jmp return1 ; or 1 if not ldx #$00 jsr DUMPT ; Write data to tape bcs error - jmp return0 ; Return 0 if sucessful + jmp return0 ; Return 0 if successful error: jmp return1 ; or 1 if not .endproc diff --git a/libsrc/lynx/bootldr.s b/libsrc/lynx/bootldr.s index ddc24faed..a0320886f 100644 --- a/libsrc/lynx/bootldr.s +++ b/libsrc/lynx/bootldr.s @@ -15,7 +15,7 @@ .segment "BOOTLDR" ;********************************** ; Here is the bootloader in plaintext -; The idea is to make the smalles possible encrypted loader as decryption +; The idea is to make the smallest possible encrypted loader as decryption ; is very slow. The minimum size is 49 bytes plus a zero byte. ;********************************** ; EXE = $fb68 diff --git a/libsrc/lynx/crt0.s b/libsrc/lynx/crt0.s index 238a2c99d..030f523e9 100644 --- a/libsrc/lynx/crt0.s +++ b/libsrc/lynx/crt0.s @@ -68,7 +68,7 @@ MikeyInitData: .byte $9e,$18,$68,$1f,$00,$00,$00,$00,$00,$ff,$1a,$1b,$04,$0d,$2 ; Disable the TX/RX IRQ; set to 8E1. - lda #%00011101 + lda #PAREN|RESETERR|TXOPEN|PAREVEN ; #%00011101 sta SERCTL ; Clear all pending interrupts. diff --git a/libsrc/lynx/eeprom66.s b/libsrc/lynx/eeprom66.s index 6511cf8af..44b365f03 100644 --- a/libsrc/lynx/eeprom66.s +++ b/libsrc/lynx/eeprom66.s @@ -40,7 +40,7 @@ EE_C_WRITE = $14 EE_C_READ = $18 EE_C_ERASE = $1C EE_C_EWEN = $13 -EE_C_EWEN2 = $FF ;; C0 schould be enough +EE_C_EWEN2 = $FF ;; C0 should be enough EE_C_EWDS = $10 EE_C_EWDS2 = $00 diff --git a/libsrc/lynx/eeprom86.s b/libsrc/lynx/eeprom86.s index 73b342fae..8b829c5e8 100644 --- a/libsrc/lynx/eeprom86.s +++ b/libsrc/lynx/eeprom86.s @@ -41,7 +41,7 @@ EE_C_WRITE = $14 EE_C_READ = $18 EE_C_ERASE = $1C EE_C_EWEN = $13 -EE_C_EWEN2 = $FF ;; C0 schould be enough +EE_C_EWEN2 = $FF ;; C0 should be enough EE_C_EWDS = $10 EE_C_EWDS2 = $00 diff --git a/libsrc/lynx/extzp.inc b/libsrc/lynx/extzp.inc index 2b0f68701..7103ff5b6 100644 --- a/libsrc/lynx/extzp.inc +++ b/libsrc/lynx/extzp.inc @@ -12,10 +12,6 @@ .global __iodir: zp .global __viddma: zp .global __sprsys: zp - .global _abc_score_ptr0: zp - .global _abc_score_ptr1: zp - .global _abc_score_ptr2: zp - .global _abc_score_ptr3: zp .global _FileEntry: zp .global _FileStartBlock: zp .global _FileBlockOffset: zp @@ -25,6 +21,3 @@ .global _FileCurrBlock: zp .global _FileBlockByte: zp .global _FileDestPtr: zp - - - diff --git a/libsrc/lynx/extzp.s b/libsrc/lynx/extzp.s index f22cfaefb..df53c3d9a 100644 --- a/libsrc/lynx/extzp.s +++ b/libsrc/lynx/extzp.s @@ -16,13 +16,6 @@ __iodir: .res 1 __viddma: .res 1 __sprsys: .res 1 -; ------------------------------------------------------------------------ -; sound effect pointers for multitimbral Lynx music hardware -_abc_score_ptr0: .res 2 -_abc_score_ptr1: .res 2 -_abc_score_ptr2: .res 2 -_abc_score_ptr3: .res 2 - ; ------------------------------------------------------------------------ ; Filesystem variables needed for reading stuff from the Lynx cart _FileEntry: ; The file directory entry is 8 bytes @@ -35,4 +28,3 @@ _FileFileLen: .res 2 _FileCurrBlock: .res 1 _FileBlockByte: .res 2 _FileDestPtr: .res 2 - diff --git a/libsrc/lynx/lseek.s b/libsrc/lynx/lseek.s index 4b4f94d7c..04d816945 100644 --- a/libsrc/lynx/lseek.s +++ b/libsrc/lynx/lseek.s @@ -18,7 +18,7 @@ .import ldeaxysp, decsp2, pushax, incsp8 .import tosandeax,decax1,tosdiveax,axlong,ldaxysp .import lynxskip0, lynxblock,tosasreax - .import __BLOCKSIZE__ + .import __BANK0BLOCKSIZE__ .importzp _FileCurrBlock .segment "CODE" @@ -32,15 +32,15 @@ jsr ldeaxysp jsr pusheax ldx #$00 - lda #<(__BLOCKSIZE__/1024 + 9) + lda #<(__BANK0BLOCKSIZE__/1024 + 9) jsr tosasreax sta _FileCurrBlock jsr lynxblock ldy #$05 jsr ldeaxysp jsr pusheax - lda #<(__BLOCKSIZE__-1) - ldx #>(__BLOCKSIZE__-1) + lda #<(__BANK0BLOCKSIZE__-1) + ldx #>(__BANK0BLOCKSIZE__-1) jsr axlong jsr tosandeax eor #$FF diff --git a/libsrc/lynx/lynx-cart.s b/libsrc/lynx/lynx-cart.s index 94edff677..d1f3e33eb 100644 --- a/libsrc/lynx/lynx-cart.s +++ b/libsrc/lynx/lynx-cart.s @@ -17,7 +17,7 @@ .include "extzp.inc" .export lynxskip0, lynxread0 .export lynxblock - .import __BLOCKSIZE__ + .import __BANK0BLOCKSIZE__ .code @@ -88,7 +88,7 @@ lynxblock: lda __iodat sta IODAT stz _FileBlockByte - lda #<($100-(>__BLOCKSIZE__)) + lda #<($100-(>__BANK0BLOCKSIZE__)) sta _FileBlockByte+1 ply plx diff --git a/libsrc/lynx/ser/lynx-comlynx.s b/libsrc/lynx/ser/lynx-comlynx.s index 8aa3c838e..c4ae3d5b6 100644 --- a/libsrc/lynx/ser/lynx-comlynx.s +++ b/libsrc/lynx/ser/lynx-comlynx.s @@ -73,7 +73,12 @@ SER_UNINSTALL: ; Must return an SER_ERR_xx code in a/x. SER_CLOSE: - ; Disable interrupts + ; Disable interrupts and stop timer 4 (serial) + lda #TXOPEN|RESETERR + sta SERCTL + stz TIM4CTLA ; Disable count and no reload + stz SerialStat ; Reset status + ; Done, return an error code lda #SER_ERR_OK .assert SER_ERR_OK = 0, error @@ -108,17 +113,17 @@ SER_OPEN: stz TxPtrIn stz TxPtrOut - ; clock = 8 * 15625 - lda #%00011000 - sta TIM4CTLA ldy #SER_PARAMS::BAUDRATE lda (ptr1),y + ; Source period is 1 us + ldy #%00011000 ; ENABLE_RELOAD|ENABLE_COUNT|AUD_1 + ldx #1 cmp #SER_BAUD_62500 beq setbaudrate - ldx #2 + ldx #3 cmp #SER_BAUD_31250 beq setbaudrate @@ -134,6 +139,10 @@ SER_OPEN: cmp #SER_BAUD_2400 beq setbaudrate + ldx #68 + cmp #SER_BAUD_1800 + beq setbaudrate + ldx #103 cmp #SER_BAUD_1200 beq setbaudrate @@ -142,65 +151,22 @@ SER_OPEN: cmp #SER_BAUD_600 beq setbaudrate - ; clock = 6 * 15625 - ldx #%00011010 - stx TIM4CTLA + ; Source period is 8 us + ldy #%00011011 ; ENABLE_RELOAD|ENABLE_COUNT|AUD_8 - ldx #12 - cmp #SER_BAUD_7200 - beq setbaudrate - - ldx #25 - cmp #SER_BAUD_3600 - beq setbaudrate - - ldx #207 - stx TIM4BKUP - - ; clock = 4 * 15625 - ldx #%00011100 + ldx #51 cmp #SER_BAUD_300 - beq setprescaler - - ; clock = 6 * 15625 - ldx #%00011110 - cmp #SER_BAUD_150 - beq setprescaler - - ; clock = 1 * 15625 - ldx #%00011111 - stx TIM4CTLA - cmp #SER_BAUD_75 - beq baudsuccess - - ldx #141 - cmp #SER_BAUD_110 - beq setbaudrate - - ; clock = 2 * 15625 - ldx #%00011010 - stx TIM4CTLA - ldx #68 - cmp #SER_BAUD_1800 - beq setbaudrate - - ; clock = 6 * 15625 - ldx #%00011110 - stx TIM4CTLA - ldx #231 - cmp #SER_BAUD_134_5 beq setbaudrate lda #SER_ERR_BAUD_UNAVAIL ldx #0 ; return value is char rts -setprescaler: - stx TIM4CTLA - bra baudsuccess + setbaudrate: + sty TIM4CTLA stx TIM4BKUP -baudsuccess: - ldx #TxOpenColl|ParEven + + ldx #TXOPEN|PAREVEN stx contrl ldy #SER_PARAMS::DATABITS ; Databits lda (ptr1),y @@ -218,15 +184,15 @@ baudsuccess: beq checkhs cmp #SER_PAR_SPACE bne @L0 - ldx #TxOpenColl + ldx #TXOPEN stx contrl bra checkhs @L0: - ldx #TxParEnable|TxOpenColl|ParEven + ldx #PAREN|TXOPEN|PAREVEN stx contrl cmp #SER_PAR_EVEN beq checkhs - ldx #TxParEnable|TxOpenColl + ldx #PAREN|TXOPEN stx contrl checkhs: ldx contrl @@ -234,15 +200,27 @@ checkhs: ldy #SER_PARAMS::HANDSHAKE ; Handshake lda (ptr1),y cmp #SER_HS_NONE + beq redeye_ok + cmp #SER_HS_SW ; Software handshake will check for connected redeye bne invparameter + + lda IODAT + and #NOEXP ; Check if redeye bit flag is unset + beq redeye_ok + lda #SER_ERR_NO_DEVICE ; ComLynx cable is not inserted + ldx #0 + rts + +redeye_ok: lda SERDAT lda contrl - ora #RxIntEnable|ResetErr + ora #RXINTEN|RESETERR ; Turn on interrupts for receive sta SERCTL lda #SER_ERR_OK .assert SER_ERR_OK = 0, error tax rts + invparameter: lda #SER_ERR_INIT_FAILED ldx #0 ; return value is char @@ -264,8 +242,8 @@ GetByte: ldy RxPtrOut lda RxBuffer,y inc RxPtrOut + sta (ptr1) ldx #$00 - sta (ptr1,x) txa ; Return code = 0 rts @@ -279,24 +257,26 @@ SER_PUT: ina cmp TxPtrOut bne PutByte + lda #SER_ERR_OVERFLOW ldx #0 ; return value is char rts + PutByte: ldy TxPtrIn txa sta TxBuffer,y inc TxPtrIn - bit TxDone - bmi @L1 + bit TxDone ; Check bit 7 of TxDone (TXINTEN) + bmi @L1 ; Was TXINTEN already set? php sei - lda contrl - ora #TxIntEnable|ResetErr - sta SERCTL ; Allow TX-IRQ to hang RX-IRQ + lda contrl ; contrl does not include RXINTEN setting + ora #TXINTEN|RESETERR + sta SERCTL ; Allow TX-IRQ to hang RX-IRQ (no receive while transmitting) sta TxDone - plp + plp ; Restore processor and interrupt enable @L1: lda #SER_ERR_OK .assert SER_ERR_OK = 0, error @@ -308,9 +288,9 @@ PutByte: ; Must return an SER_ERR_xx code in a/x. SER_STATUS: - ldy SerialStat + lda SerialStat + sta (ptr1) ldx #$00 - sta (ptr1,x) txa ; Return code = 0 rts @@ -342,48 +322,56 @@ SER_IRQ: @L0: bit TxDone bmi @tx_irq ; Transmit in progress - ldx SERDAT - lda SERCTL - and #RxParityErr|RxOverrun|RxFrameErr|RxBreak - beq @rx_irq + + ldx SERDAT ; Read received data + lda contrl + and #PAREN ; Parity enabled implies SER_PAR_EVEN or SER_PAR_ODD + tay + ora #OVERRUN|FRAMERR|RXBRK + and SERCTL ; Check presence of relevant error flags in SERCTL + + beq @rx_irq ; No errors so far + tsb SerialStat ; Save error condition - bit #RxBreak + bit #RXBRK ; Check for break signal beq @noBreak + stz TxPtrIn ; Break received - drop buffers stz TxPtrOut stz RxPtrIn stz RxPtrOut @noBreak: - lda contrl - ora #RxIntEnable|ResetErr - sta SERCTL - lda #$10 - sta INTRST - bra @IRQexit + bra @exit0 + @rx_irq: + tya + bne @2 ; Parity was enabled so no marker bit check needed + lda contrl - ora #RxIntEnable|ResetErr - sta SERCTL + eor SERCTL ; Should match current parity bit + and #PARBIT ; Check for mark or space value + bne @exit0 + +@2: txa ldx RxPtrIn sta RxBuffer,x txa inx -@cont0: cpx RxPtrOut beq @1 stx RxPtrIn - lda #SERIAL_INTERRUPT - sta INTRST bra @IRQexit @1: sta RxPtrIn lda #$80 tsb SerialStat + bra @exit0 + @tx_irq: - ldx TxPtrOut ; Has all bytes been sent? + ldx TxPtrOut ; Have all bytes been sent? cpx TxPtrIn beq @allSent @@ -393,24 +381,24 @@ SER_IRQ: @exit1: lda contrl - ora #TxIntEnable|ResetErr + ora #TXINTEN|RESETERR sta SERCTL - lda #SERIAL_INTERRUPT - sta INTRST bra @IRQexit @allSent: lda SERCTL ; All bytes sent - bit #TxEmpty + bit #TXEMPTY beq @exit1 bvs @exit1 stz TxDone + +@exit0: lda contrl - ora #RxIntEnable|ResetErr + ora #RXINTEN|RESETERR ; Re-enable receive interrupt sta SERCTL +@IRQexit: lda #SERIAL_INTERRUPT sta INTRST -@IRQexit: clc rts diff --git a/libsrc/lynx/tgi/lynx-160-102-16.s b/libsrc/lynx/tgi/lynx-160-102-16.s index c35b6a5aa..00a2059aa 100644 --- a/libsrc/lynx/tgi/lynx-160-102-16.s +++ b/libsrc/lynx/tgi/lynx-160-102-16.s @@ -43,7 +43,7 @@ libref: .addr $0000 ; Library reference ; to an RTS for test versions (function not implemented). A future version may ; allow for emulation: In this case the vector will be zero. Emulation means ; that the graphics kernel will emulate the function by using lower level -; primitives - for example ploting a line by using calls to SETPIXEL. +; primitives - for example plotting a line by using calls to SETPIXEL. .addr INSTALL .addr UNINSTALL @@ -258,7 +258,7 @@ GETERROR: ; ; The TGI lacks a way to draw sprites. As that functionality is vital to ; Lynx games we borrow this CONTROL function to implement the still -; missing tgi_draw_sprite funtion. To use this in your C-program +; missing tgi_draw_sprite function. To use this in your C-program ; do a #define tgi_draw_sprite(spr) tgi_ioctl(0, spr) ; ; To do a flip-screen call tgi_ioctl(1, 0) diff --git a/libsrc/lynx/uploader.s b/libsrc/lynx/uploader.s index f16a1721a..5ce21b489 100644 --- a/libsrc/lynx/uploader.s +++ b/libsrc/lynx/uploader.s @@ -33,21 +33,21 @@ loop1: cont1: jsr read_byte sta (load_ptr2),y - sta PALETTE ; feedback ;-) + sta PALETTE + 1 ; feedback ;-) iny bne loop1 inc load_ptr2+1 bra loop1 read_byte: - bit SERCTL + bit SERCTL ; Check for RXRDY ($40) bvc read_byte lda SERDAT rts _UpLoaderIRQ: lda INTSET - and #$10 + and #SERIAL_INTERRUPT bne @L0 clc rts @@ -69,6 +69,8 @@ again: ; last action : clear interrupt ; exit: + lda #SERIAL_INTERRUPT + sta INTRST clc rts diff --git a/libsrc/nes/ppubuf.s b/libsrc/nes/ppubuf.s index f08fc1a71..0de6d1980 100644 --- a/libsrc/nes/ppubuf.s +++ b/libsrc/nes/ppubuf.s @@ -68,7 +68,7 @@ ; ------------------------------------------------------------------------ ; Flush PPU-Memory write buffer -; called from vblank interupt +; called from vblank interrupt .proc ppubuf_flush diff --git a/libsrc/pce/vga.s b/libsrc/pce/vga.s index 630fbe8db..067647cb8 100644 --- a/libsrc/pce/vga.s +++ b/libsrc/pce/vga.s @@ -3,9 +3,9 @@ .export _pce_font -; The character tiles use only two colors from each pallette. Color zero -; comes from pallette zero; color one is different in each pallette. The -; color of a character is set by choosing one of the 16 pallettes. +; The character tiles use only two colors from each palette. Color zero +; comes from palette zero; color one is different in each palette. The +; color of a character is set by choosing one of the 16 palettes. .rodata diff --git a/libsrc/pet/cbm_load.c b/libsrc/pet/cbm_load.c index 1823f6537..2c98d9581 100644 --- a/libsrc/pet/cbm_load.c +++ b/libsrc/pet/cbm_load.c @@ -22,14 +22,14 @@ unsigned int __fastcall__ cbm_load (const char* name, unsigned char device, void unsigned int size = 0; if (cbm_open (1, device, CBM_READ, name) != 0) { - /* Can't load from a file that can't be openned. */ + /* Can't load from a file that can't be opened. */ return 0; } /* Get the file's load address. */ if (cbm_read (1, &load, sizeof load) != sizeof load) { /* Either the file wasn't found, or it was too short. (Note: - ** the computer openned a file even if the drive couldn't open one.) + ** the computer opened a file even if the drive couldn't open one.) */ cbm_close (1); return 0; diff --git a/libsrc/pet/waitvsync.s b/libsrc/pet/waitvsync.s index 39b562e43..d74f76c9f 100644 --- a/libsrc/pet/waitvsync.s +++ b/libsrc/pet/waitvsync.s @@ -9,8 +9,7 @@ .include "pet.inc" _waitvsync: -@l1: - lda VIA_PB - and #%00100000 - bne @l1 + lda #%00100000 +: and VIA_PB + bne :- rts diff --git a/libsrc/plus4/ser/plus4-stdser.s b/libsrc/plus4/ser/plus4-stdser.s index fbdc61b2e..77445c7a2 100644 --- a/libsrc/plus4/ser/plus4-stdser.s +++ b/libsrc/plus4/ser/plus4-stdser.s @@ -252,15 +252,10 @@ InvBaud: ; SER_GET: - ldx SendFreeCnt ; Send data if necessary - inx ; X == $FF? - beq @L1 - lda #$00 - jsr TryToSend ; Check for buffer empty -@L1: lda RecvFreeCnt ; (25) + lda RecvFreeCnt ; (25) cmp #$ff bne @L2 lda #SER_ERR_NO_DATA @@ -300,21 +295,23 @@ SER_PUT: ; Try to send ldx SendFreeCnt - inx ; X = $ff? + cpx #$ff ; Nothing to flush beq @L2 pha lda #$00 jsr TryToSend pla -; Put byte into send buffer & send +; Reload SendFreeCnt after TryToSend -@L2: ldx SendFreeCnt - bne @L3 + ldx SendFreeCnt + bne @L2 lda #SER_ERR_OVERFLOW ; X is already zero rts -@L3: ldx SendTail +; Put byte into send buffer & send + +@L2: ldx SendTail sta SendBuf,x inc SendTail dec SendFreeCnt @@ -387,25 +384,25 @@ SER_IRQ: sta tmp1 ; Remember tryHard flag @L0: lda SendFreeCnt cmp #$ff - beq @L3 ; Bail out + beq @L2 ; Bail out ; Check for flow stopped @L1: lda Stopped - bne @L3 ; Bail out + bne @L2 ; Bail out ; Check that swiftlink is ready to send -@L2: lda ACIA_STATUS + lda ACIA_STATUS and #$10 - bne @L4 + bne @L3 bit tmp1 ;keep trying if must try hard - bmi @L0 -@L3: rts + bmi @L1 +@L2: rts ; Send byte and try again -@L4: ldx SendHead +@L3: ldx SendHead lda SendBuf,x sta ACIA_DATA inc SendHead diff --git a/libsrc/rp6502/clock.c b/libsrc/rp6502/clock.c new file mode 100644 index 000000000..f8756f553 --- /dev/null +++ b/libsrc/rp6502/clock.c @@ -0,0 +1,7 @@ +#include <rp6502.h> +#include <time.h> + +clock_t __fastcall__ clock (void) +{ + return ria_call_long (RIA_OP_CLOCK); +} diff --git a/libsrc/rp6502/close.c b/libsrc/rp6502/close.c new file mode 100644 index 000000000..dd7e38115 --- /dev/null +++ b/libsrc/rp6502/close.c @@ -0,0 +1,8 @@ +#include <rp6502.h> +#include <errno.h> + +int __fastcall__ close (int fd) +{ + ria_set_ax (fd); + return ria_call_int_errno (RIA_OP_CLOSE); +} diff --git a/libsrc/rp6502/codepage.c b/libsrc/rp6502/codepage.c new file mode 100644 index 000000000..e28726f04 --- /dev/null +++ b/libsrc/rp6502/codepage.c @@ -0,0 +1,6 @@ +#include <rp6502.h> + +int __fastcall__ codepage (void) +{ + return ria_call_int (RIA_OP_CODEPAGE); +} diff --git a/libsrc/rp6502/crt0.s b/libsrc/rp6502/crt0.s new file mode 100644 index 000000000..165ecf0a2 --- /dev/null +++ b/libsrc/rp6502/crt0.s @@ -0,0 +1,51 @@ +; +; 2023, Rumbledethumps +; +; crt0.s + +.export _init, _exit +.import _main + +.export __STARTUP__ : absolute = 1 +.import __RAM_START__, __RAM_SIZE__ + +.import copydata, zerobss, initlib, donelib + +.include "rp6502.inc" +.include "zeropage.inc" + +.segment "STARTUP" + +; Essential 6502 startup the CPU doesn't do +_init: + ldx #$FF + txs + cld + +; Set cc65 argument stack pointer + lda #<(__RAM_START__ + __RAM_SIZE__) + sta sp + lda #>(__RAM_START__ + __RAM_SIZE__) + sta sp+1 + +; Initialize memory storage + jsr zerobss ; Clear BSS segment + jsr copydata ; Initialize DATA segment + jsr initlib ; Run constructors + +; Call main() + jsr _main + +; Back from main() also the _exit entry +; Stack the exit value in case destructors call OS +_exit: + phx + pha + jsr donelib ; Run destructors + pla + sta RIA_A + plx + stx RIA_X + lda #$FF ; exit() + sta RIA_OP + stp diff --git a/libsrc/rp6502/getres.c b/libsrc/rp6502/getres.c new file mode 100644 index 000000000..394c32e76 --- /dev/null +++ b/libsrc/rp6502/getres.c @@ -0,0 +1,10 @@ +#include <rp6502.h> +#include <time.h> + +extern int __clock_gettimespec (struct timespec* ts, unsigned char op); + +int clock_getres (clockid_t clock_id, struct timespec* res) +{ + ria_set_ax (clock_id); + return __clock_gettimespec (res, RIA_OP_CLOCK_GETRES); +} diff --git a/libsrc/rp6502/gettime.c b/libsrc/rp6502/gettime.c new file mode 100644 index 000000000..ee63c31ec --- /dev/null +++ b/libsrc/rp6502/gettime.c @@ -0,0 +1,12 @@ +#include <rp6502.h> +#include <time.h> + +extern int __clock_gettimespec (struct timespec* ts, unsigned char op); + +int clock_gettime (clockid_t clock_id, struct timespec* tp) +{ + (void)clock_id; + /* time.s doesn't set the stack value for clock_id (bug?) */ + ria_set_ax (CLOCK_REALTIME); + return __clock_gettimespec (tp, RIA_OP_CLOCK_GETTIME); +} diff --git a/libsrc/rp6502/gettimespec.c b/libsrc/rp6502/gettimespec.c new file mode 100644 index 000000000..4dc3a0db3 --- /dev/null +++ b/libsrc/rp6502/gettimespec.c @@ -0,0 +1,13 @@ +#include <rp6502.h> +#include <time.h> + +int __clock_gettimespec (struct timespec* ts, unsigned char op) +/* Internal method shared by clock_getres and clock_gettime. */ +{ + int ax = ria_call_int_errno (op); + if (ax >= 0) { + ts->tv_sec = ria_pop_long (); + ts->tv_nsec = ria_pop_long (); + } + return ax; +} diff --git a/libsrc/rp6502/gettimezone.c b/libsrc/rp6502/gettimezone.c new file mode 100644 index 000000000..f3cb2a061 --- /dev/null +++ b/libsrc/rp6502/gettimezone.c @@ -0,0 +1,16 @@ +#include <rp6502.h> +#include <time.h> + +int clock_gettimezone (clockid_t clock_id, struct _timezone* tz) +{ + int ax; + ria_set_ax (clock_id); + ax = ria_call_int_errno (RIA_OP_CLOCK_GETTIMEZONE); + if (ax >= 0) { + char i; + for (i = 0; i < sizeof (struct _timezone); i++) { + ((char*)tz)[i] = ria_pop_char (); + } + } + return ax; +} diff --git a/libsrc/rp6502/initenv.s b/libsrc/rp6502/initenv.s new file mode 100644 index 000000000..180b25c67 --- /dev/null +++ b/libsrc/rp6502/initenv.s @@ -0,0 +1,14 @@ +; +; 2023, Rumbledethumps +; + +.constructor initenv, 24 +.import __environ, __envcount, __envsize + +.segment "ONCE" + +.proc initenv + + rts + +.endproc diff --git a/libsrc/rp6502/irq.s b/libsrc/rp6502/irq.s new file mode 100644 index 000000000..d7d2e6ec5 --- /dev/null +++ b/libsrc/rp6502/irq.s @@ -0,0 +1,50 @@ +; +; 2023, Rumbledethumps +; +; Enables the C IRQ tools + +.export initirq, doneirq +.import callirq, _exit + +.include "rp6502.inc" + +.segment "ONCE" + +initirq: + lda #<handler + ldx #>handler + sei + sta $FFFE + stx $FFFF + cli + rts + +.code + +doneirq: + sei + rts + +.segment "LOWCODE" + +handler: + cld + phx + tsx + pha + inx + inx + lda $100,X + and #$10 + bne break + phy + jsr callirq + ply + pla + plx + rti + +break: + lda #$FF + sta RIA_A + jmp _exit diff --git a/libsrc/rp6502/lrand.c b/libsrc/rp6502/lrand.c new file mode 100644 index 000000000..6434425df --- /dev/null +++ b/libsrc/rp6502/lrand.c @@ -0,0 +1,6 @@ +#include <rp6502.h> + +long __fastcall__ lrand (void) +{ + return ria_call_long (RIA_OP_LRAND); +} diff --git a/libsrc/rp6502/lseek.c b/libsrc/rp6502/lseek.c new file mode 100644 index 000000000..29506612c --- /dev/null +++ b/libsrc/rp6502/lseek.c @@ -0,0 +1,11 @@ +#include <rp6502.h> +#include <unistd.h> + +off_t __fastcall__ lseek (int fd, off_t offset, int whence) +{ + /* Modified argument order for short stacking offset */ + ria_push_long (offset); + ria_push_char (whence); + ria_set_ax (fd); + return ria_call_long_errno (RIA_OP_LSEEK); +} diff --git a/libsrc/rp6502/mainargs.s b/libsrc/rp6502/mainargs.s new file mode 100644 index 000000000..152020022 --- /dev/null +++ b/libsrc/rp6502/mainargs.s @@ -0,0 +1,15 @@ +; +; 2023, Rumbledethumps +; +; No arguments + +.constructor initmainargs, 24 +.import __argc, __argv + +.segment "ONCE" + +.proc initmainargs + + rts + +.endproc diff --git a/libsrc/rp6502/open.c b/libsrc/rp6502/open.c new file mode 100644 index 000000000..ab3a374a2 --- /dev/null +++ b/libsrc/rp6502/open.c @@ -0,0 +1,16 @@ +#include <rp6502.h> +#include <errno.h> +#include <string.h> + +int __cdecl__ open (const char* name, int flags, ...) +{ + size_t namelen = strlen (name); + if (namelen > 255) { + return _mappederrno (EINVAL); + } + while (namelen) { + ria_push_char (name[--namelen]); + } + ria_set_ax (flags); + return ria_call_int_errno (RIA_OP_OPEN); +} diff --git a/libsrc/rp6502/oserrlist.s b/libsrc/rp6502/oserrlist.s new file mode 100644 index 000000000..ef322e293 --- /dev/null +++ b/libsrc/rp6502/oserrlist.s @@ -0,0 +1,86 @@ +; +; 2002-07-18, Ullrich von Bassewitz +; 2022, ChaN +; 2023, Rumbledethumps +; +; Defines the platform-specific error list. +; +; The table is built as a list of entries: +; +; .byte entrylen +; .byte errorcode +; .asciiz errormsg +; +; and, terminated by an entry with length zero that is returned if the +; error code could not be found. +; + +.export __sys_oserrlist + +.include "rp6502.inc" +.include "errno.inc" + +;---------------------------------------------------------------------------- +; Macros used to generate the list (may get moved to an include file?) + +; Regular entry +.macro sys_oserr_entry code, msg + .local Start, End +Start: + .byte End - Start + .byte code + .asciiz msg +End: +.endmacro + +; Sentinel entry +.macro sys_oserr_sentinel msg + .byte 0 ; Length is always zero + .byte 0 ; Code is unused + .asciiz msg +.endmacro + +;---------------------------------------------------------------------------- +; The error message table + +.rodata +__sys_oserrlist: + + sys_oserr_entry ENOENT , "No such file or directory" + sys_oserr_entry ENOMEM , "Out of memory" + sys_oserr_entry EACCES , "Permission denied" + sys_oserr_entry ENODEV , "No such device" + sys_oserr_entry EMFILE , "Too many open files" + sys_oserr_entry EBUSY , "Device or resource busy" + sys_oserr_entry EINVAL , "Invalid argument" + sys_oserr_entry ENOSPC , "No space left on device" + sys_oserr_entry EEXIST , "File exists" + sys_oserr_entry EAGAIN , "Try again" + sys_oserr_entry EIO , "I/O error" + sys_oserr_entry EINTR , "Interrupted system call" + sys_oserr_entry ENOSYS , "Function not implemented" + sys_oserr_entry ESPIPE , "Illegal seek" + sys_oserr_entry ERANGE , "Range error" + sys_oserr_entry EBADF , "Bad file number" + sys_oserr_entry ENOEXEC , "Exec format error" + sys_oserr_entry EUNKNOWN , "Unknown OS specific error" + sys_oserr_entry FR_DISK_ERR , "A hard error occurred in the low level disk I/O layer" + sys_oserr_entry FR_INT_ERR , "Assertion failed" + sys_oserr_entry FR_NOT_READY , "The physical drive cannot work" + sys_oserr_entry FR_NO_FILE , "Could not find the file" + sys_oserr_entry FR_NO_PATH , "Could not find the path" + sys_oserr_entry FR_INVALID_NAME , "The path name format is invalid" + sys_oserr_entry FR_DENIED , "Access denied due to prohibited access or directory full" + sys_oserr_entry FR_EXIST , "Access denied due to prohibited access" + sys_oserr_entry FR_INVALID_OBJECT , "The file/directory object is invalid" + sys_oserr_entry FR_WRITE_PROTECTED , "The physical drive is write protected" + sys_oserr_entry FR_INVALID_DRIVE , "The logical drive number is invalid" + sys_oserr_entry FR_NOT_ENABLED , "The volume has no work area" + sys_oserr_entry FR_NO_FILESYSTEM , "There is no valid FAT volume" + sys_oserr_entry FR_MKFS_ABORTED , "The f_mkfs() aborted due to any problem" + sys_oserr_entry FR_TIMEOUT , "Could not get a grant to access the volume within defined period" + sys_oserr_entry FR_LOCKED , "The operation is rejected according to the file sharing policy" + sys_oserr_entry FR_NOT_ENOUGH_CORE , "LFN working buffer could not be allocated" + sys_oserr_entry FR_TOO_MANY_OPEN_FILES , "Number of open files > FF_FS_LOCK" + sys_oserr_entry FR_INVALID_PARAMETER , "Given parameter is invalid" + sys_oserr_sentinel "Unknown error" diff --git a/libsrc/rp6502/oserror.s b/libsrc/rp6502/oserror.s new file mode 100644 index 000000000..706bcfa65 --- /dev/null +++ b/libsrc/rp6502/oserror.s @@ -0,0 +1,66 @@ +; +; 2000-05-17, Ullrich von Bassewitz +; 2022, ChaN +; 2023, Rumbledethumps +; +; int __fastcall__ __osmaperrno (unsigned char oserror); +; +; RP6502 will respond with a union of CC65 and FatFs errnos. +; This will map FatFs errors into the CC65 range for portable code. + +EFATFS_START := 32 + +.include "rp6502.inc" +.include "errno.inc" + +.code + +___osmaperrno: + cmp #EFATFS_START + bmi @L2 + + ldx #ErrTabSize +@L1: + cmp ErrTab-2,x ; Search for the error code + beq @L3 ; Jump if found + dex + dex + bne @L1 ; Next entry + +; Code not found, return EUNKNOWN + lda #<EUNKNOWN + ldx #>EUNKNOWN +@L2: + rts + +; Found the code +@L3: + lda ErrTab-1,x + ldx #$00 ; High byte always zero + rts + +.rodata + +ErrTab: + + .byte FR_DISK_ERR , EIO ; A hard error occurred in the low level disk I/O layer +; .byte FR_INT_ERR , EUNKNOWN ; Assertion failed + .byte FR_NOT_READY , EBUSY ; The physical drive cannot work + .byte FR_NO_FILE , ENOENT ; Could not find the file + .byte FR_NO_PATH , ENOENT ; Could not find the path + .byte FR_INVALID_NAME , EINVAL ; The path name format is invalid + .byte FR_DENIED , EACCES ; Access denied due to prohibited access or directory full + .byte FR_EXIST , EEXIST ; Access denied due to prohibited access + .byte FR_INVALID_OBJECT , EINVAL ; The file/directory object is invalid + .byte FR_WRITE_PROTECTED , EACCES ; The physical drive is write protected + .byte FR_INVALID_DRIVE , ENODEV ; The logical drive number is invalid +; .byte FR_NOT_ENABLED , EUNKNOWN ; The volume has no work area +; .byte FR_NO_FILESYSTEM , EUNKNOWN ; There is no valid FAT volume +; .byte FR_MKFS_ABORTED , EUNKNOWN ; The f_mkfs() aborted due to any problem +; .byte FR_TIMEOUT , EUNKNOWN ; Could not get a grant to access the volume within defined period + .byte FR_LOCKED , EBUSY ; The operation is rejected according to the file sharing policy + .byte FR_NOT_ENOUGH_CORE , ENOMEM ; LFN working buffer could not be allocated + .byte FR_TOO_MANY_OPEN_FILES , EMFILE ; Number of open files > FF_FS_LOCK + .byte FR_INVALID_PARAMETER , EINVAL ; Given parameter is invalid + +ErrTabSize = (* - ErrTab) diff --git a/libsrc/rp6502/phi2.c b/libsrc/rp6502/phi2.c new file mode 100644 index 000000000..1275e256e --- /dev/null +++ b/libsrc/rp6502/phi2.c @@ -0,0 +1,6 @@ +#include <rp6502.h> + +int __fastcall__ phi2 (void) +{ + return ria_call_int (RIA_OP_PHI2); +} diff --git a/libsrc/rp6502/randomize.c b/libsrc/rp6502/randomize.c new file mode 100644 index 000000000..569387d14 --- /dev/null +++ b/libsrc/rp6502/randomize.c @@ -0,0 +1,7 @@ +#include <rp6502.h> +#include <stdlib.h> + +void _randomize (void) +{ + srand (ria_call_int (RIA_OP_LRAND)); +} diff --git a/libsrc/rp6502/read.c b/libsrc/rp6502/read.c new file mode 100644 index 000000000..87a9bbf7d --- /dev/null +++ b/libsrc/rp6502/read.c @@ -0,0 +1,20 @@ +#include <rp6502.h> +#include <unistd.h> + +int __fastcall__ read (int fildes, void* buf, unsigned count) +{ + int total = 0; + while (count) { + unsigned blockcount = (count > 512) ? 512 : count; + int bytes_read = read_xstack (&((char*)buf)[total], blockcount, fildes); + if (bytes_read < 0) { + return bytes_read; + } + total += bytes_read; + count -= bytes_read; + if (bytes_read < blockcount) { + break; + } + } + return total; +} diff --git a/libsrc/rp6502/read_xram.c b/libsrc/rp6502/read_xram.c new file mode 100644 index 000000000..f88a036ee --- /dev/null +++ b/libsrc/rp6502/read_xram.c @@ -0,0 +1,9 @@ +#include <rp6502.h> + +int __fastcall__ read_xram (unsigned buf, unsigned count, int fildes) +{ + ria_push_int (buf); + ria_push_int (count); + ria_set_ax (fildes); + return ria_call_int_errno (RIA_OP_READ_XRAM); +} diff --git a/libsrc/rp6502/read_xstack.c b/libsrc/rp6502/read_xstack.c new file mode 100644 index 000000000..70eebad2f --- /dev/null +++ b/libsrc/rp6502/read_xstack.c @@ -0,0 +1,13 @@ +#include <rp6502.h> + +int __fastcall__ read_xstack (void* buf, unsigned count, int fildes) +{ + int i, ax; + ria_push_int (count); + ria_set_ax (fildes); + ax = ria_call_int_errno (RIA_OP_READ_XSTACK); + for (i = 0; i < ax; i++) { + ((char*)buf)[i] = ria_pop_char (); + } + return ax; +} diff --git a/libsrc/rp6502/ria.s b/libsrc/rp6502/ria.s new file mode 100644 index 000000000..a1b53efb1 --- /dev/null +++ b/libsrc/rp6502/ria.s @@ -0,0 +1,89 @@ +; +; 2023, Rumbledethumps +; +; Helpers for building API shims + +.include "rp6502.inc" + +.export _ria_push_long, _ria_push_int +.export _ria_pop_long, _ria_pop_int +.export _ria_set_axsreg, _ria_set_ax +.export _ria_call_int, _ria_call_long +.export _ria_call_int_errno, _ria_call_long_errno + +.importzp sp, sreg +.import ___mappederrno, incsp1 + +.code + +; void __fastcall__ ria_push_long(unsigned long val); +_ria_push_long: + ldy sreg+1 + sty RIA_XSTACK + ldy sreg + sty RIA_XSTACK +; void __fastcall__ ria_push_int(unsigned int val); +_ria_push_int: + stx RIA_XSTACK + sta RIA_XSTACK + rts + +; long __fastcall__ ria_pop_long(void); +_ria_pop_long: + jsr _ria_pop_int + ldy RIA_XSTACK + sty sreg + ldy RIA_XSTACK + sty sreg+1 + rts + +; int __fastcall__ ria_pop_int(void); +_ria_pop_int: + lda RIA_XSTACK + ldx RIA_XSTACK + rts + +; void __fastcall__ ria_set_axsreg(unsigned long axsreg); +_ria_set_axsreg: + ldy sreg + sty RIA_SREG + ldy sreg+1 + sty RIA_SREG+1 +; void __fastcall__ ria_set_ax(unsigned int ax); +_ria_set_ax: + stx RIA_X + sta RIA_A + rts + +; int __fastcall__ ria_call_int(unsigned char op); +_ria_call_int: + sta RIA_OP + jmp RIA_SPIN + +; long __fastcall__ ria_call_long(unsigned char op); +_ria_call_long: + sta RIA_OP + jsr RIA_SPIN + ldy RIA_SREG + sty sreg + ldy RIA_SREG+1 + sty sreg+1 + rts + +; int __fastcall__ ria_call_int_errno(unsigned char op); +_ria_call_int_errno: + sta RIA_OP + jsr RIA_SPIN + ldx RIA_X + bmi ERROR + rts + +; long __fastcall__ ria_call_long_errno(unsigned char op); +_ria_call_long_errno: + jsr _ria_call_long + bmi ERROR + rts + +ERROR: + lda RIA_ERRNO + jmp ___mappederrno diff --git a/libsrc/rp6502/settime.c b/libsrc/rp6502/settime.c new file mode 100644 index 000000000..1ba1d2e3e --- /dev/null +++ b/libsrc/rp6502/settime.c @@ -0,0 +1,10 @@ +#include <rp6502.h> +#include <time.h> + +int clock_settime (clockid_t clock_id, const struct timespec* tp) +{ + ria_set_ax (clock_id); + ria_push_long (tp->tv_nsec); + ria_push_long (tp->tv_sec); + return ria_call_int_errno (RIA_OP_CLOCK_SETTIME); +} diff --git a/libsrc/rp6502/stdin_opt.c b/libsrc/rp6502/stdin_opt.c new file mode 100644 index 000000000..3a9ccfde4 --- /dev/null +++ b/libsrc/rp6502/stdin_opt.c @@ -0,0 +1,9 @@ + +#include <rp6502.h> + +int __fastcall__ stdin_opt (unsigned long ctrl_bits, unsigned char str_length) +{ + ria_push_long (ctrl_bits); + ria_set_a (str_length); + return ria_call_int_errno (RIA_OP_STDIN_OPT); +} diff --git a/libsrc/rp6502/sysremove.c b/libsrc/rp6502/sysremove.c new file mode 100644 index 000000000..d8c1ced98 --- /dev/null +++ b/libsrc/rp6502/sysremove.c @@ -0,0 +1,16 @@ +#include <rp6502.h> +#include <errno.h> +#include <string.h> + +unsigned char __fastcall__ _sysremove (const char* name) +{ + size_t namelen; + namelen = strlen (name); + if (namelen > 255) { + return _mappederrno (EINVAL); + } + while (namelen) { + ria_push_char (name[--namelen]); + } + return ria_call_int_errno (RIA_OP_UNLINK); +} diff --git a/libsrc/rp6502/sysrename.c b/libsrc/rp6502/sysrename.c new file mode 100644 index 000000000..46bdd8b31 --- /dev/null +++ b/libsrc/rp6502/sysrename.c @@ -0,0 +1,21 @@ +#include <rp6502.h> +#include <errno.h> +#include <string.h> + +unsigned char __fastcall__ _sysrename (const char* oldpath, const char* newpath) +{ + size_t oldpathlen, newpathlen; + oldpathlen = strlen (oldpath); + newpathlen = strlen (newpath); + if (oldpathlen + newpathlen > 510) { + return _mappederrno (EINVAL); + } + while (oldpathlen) { + ria_push_char (oldpath[--oldpathlen]); + } + ria_push_char (0); + while (newpathlen) { + ria_push_char (newpath[--newpathlen]); + } + return ria_call_int_errno (RIA_OP_RENAME); +} diff --git a/libsrc/rp6502/write.c b/libsrc/rp6502/write.c new file mode 100644 index 000000000..23877b6e8 --- /dev/null +++ b/libsrc/rp6502/write.c @@ -0,0 +1,20 @@ +#include <rp6502.h> +#include <unistd.h> + +int __fastcall__ write (int fildes, const void* buf, unsigned count) +{ + int ax, total = 0; + while (count) { + int blockcount = (count > 512) ? 512 : count; + ax = write_xstack (&((char*)buf)[total], blockcount, fildes); + if (ax < 0) { + return ax; + } + total += ax; + count -= ax; + if (ax < blockcount) { + break; + } + } + return total; +} diff --git a/libsrc/rp6502/write_xram.c b/libsrc/rp6502/write_xram.c new file mode 100644 index 000000000..31a553813 --- /dev/null +++ b/libsrc/rp6502/write_xram.c @@ -0,0 +1,9 @@ +#include <rp6502.h> + +int __fastcall__ write_xram (unsigned buf, unsigned count, int fildes) +{ + ria_push_int (buf); + ria_push_int (count); + ria_set_ax (fildes); + return ria_call_int_errno (RIA_OP_WRITE_XRAM); +} diff --git a/libsrc/rp6502/write_xstack.c b/libsrc/rp6502/write_xstack.c new file mode 100644 index 000000000..29285a87e --- /dev/null +++ b/libsrc/rp6502/write_xstack.c @@ -0,0 +1,15 @@ +#include <rp6502.h> +#include <errno.h> + +int __fastcall__ write_xstack (const void* buf, unsigned count, int fildes) +{ + unsigned i; + if (count > 512) { + return _mappederrno (EINVAL); + } + for (i = count; i;) { + ria_push_char (((char*)buf)[--i]); + } + ria_set_ax (fildes); + return ria_call_int_errno (RIA_OP_WRITE_XSTACK); +} diff --git a/libsrc/rp6502/xreg.s b/libsrc/rp6502/xreg.s new file mode 100644 index 000000000..40d4a6705 --- /dev/null +++ b/libsrc/rp6502/xreg.s @@ -0,0 +1,37 @@ +; +; 2023, Rumbledethumps +; +; CC65 will promote variadic char arguments to int. It will not demote longs. +; int __cdecl__ xreg(char device, char channel, unsigned char address, ...); + +.export _xreg +.importzp sp +.import addysp, _ria_call_int_errno + +.include "rp6502.inc" + +.code + +.proc _xreg + + ; save variadic size in X + tya + tax + +@copy: ; copy stack + dey + lda (sp),y + sta RIA_XSTACK + tya + bne @copy + + ; recover variadic size and move sp + txa + tay + jsr addysp + + ; run RIA operation + lda #RIA_OP_XREG + jmp _ria_call_int_errno + +.endproc diff --git a/libsrc/rp6502/xregn.c b/libsrc/rp6502/xregn.c new file mode 100644 index 000000000..ec040be20 --- /dev/null +++ b/libsrc/rp6502/xregn.c @@ -0,0 +1,19 @@ +#include <rp6502.h> +#include <stdarg.h> + +int __cdecl__ xregn (char device, char channel, unsigned char address, unsigned count, + ...) +{ + va_list args; + va_start (args, count); + RIA.xstack = device; + RIA.xstack = channel; + RIA.xstack = address; + while (count--) { + unsigned v = va_arg (args, unsigned); + RIA.xstack = v >> 8; + RIA.xstack = v; + } + va_end (args); + return ria_call_int_errno (RIA_OP_XREG); +} diff --git a/libsrc/runtime/aslax7.s b/libsrc/runtime/aslax7.s new file mode 100644 index 000000000..533ee55e6 --- /dev/null +++ b/libsrc/runtime/aslax7.s @@ -0,0 +1,21 @@ +; +; Miloslaw Smyk, 2024 +; +; CC65 runtime: Scale the primary register by 128, unsigned +; + + .export shlax7, aslax7 + +aslax7: +shlax7: ; XXXXXXXL AAAAAAAl + tay + txa + lsr ; XXXXXXXL -> 0XXXXXXX, L->C + tya + ror ; AAAAAAAl -> LAAAAAAA, l->C + tax + lda #$00 ; LAAAAAAA 00000000 + ror ; LAAAAAAA l0000000 + rts + + ; 10 bytes, 16 cycles + rts diff --git a/libsrc/runtime/asrax7.s b/libsrc/runtime/asrax7.s new file mode 100644 index 000000000..3c9cce681 --- /dev/null +++ b/libsrc/runtime/asrax7.s @@ -0,0 +1,18 @@ +; +; Miloslaw Smyk, 2024 +; +; CC65 runtime: Scale the primary register by 128, signed +; + + .export asrax7 + +asrax7: ; HXXXXXXL hAAAAAAl + asl ; AAAAAAA0, h->C + txa + rol ; XXXXXXLh, H->C + ldx #$00 ; 00000000 XXXXXXLh + bcc :+ + dex ; 11111111 XXXXXXLh if C +: rts + + ; 12 cycles max, 9 bytes diff --git a/libsrc/runtime/callirq.s b/libsrc/runtime/callirq.s index 74a12c4db..2096e5c3b 100644 --- a/libsrc/runtime/callirq.s +++ b/libsrc/runtime/callirq.s @@ -12,7 +12,7 @@ ; ; 2. Reentrancy. The condes routines must use self modyfiying code, which ; means it is not reentrant. An IRQ using condes, that interrupts -; another use of condes will cause unpredicatble behaviour. The current +; another use of condes will cause unpredictable behaviour. The current ; code avoids this by using locking mechanisms, but it's complex and ; has a size and performance penalty. ; diff --git a/libsrc/runtime/lshelp.s b/libsrc/runtime/lshelp.s index 8a2fca139..ce1648d28 100644 --- a/libsrc/runtime/lshelp.s +++ b/libsrc/runtime/lshelp.s @@ -39,7 +39,7 @@ poplsargs: adc #$00 sta sreg+1 -L1: lda ptr4+1 ; Is the right operand nagative? +L1: lda ptr4+1 ; Is the right operand negative? sta tmp2 ; Remember the sign for later bpl L2 ; Jump if not diff --git a/libsrc/runtime/ludiv.s b/libsrc/runtime/ludiv.s index e2e27371e..77335d8f5 100644 --- a/libsrc/runtime/ludiv.s +++ b/libsrc/runtime/ludiv.s @@ -22,7 +22,7 @@ tosudiv0ax: .endif tosudiveax: - jsr getlop ; Get the paramameters + jsr getlop ; Get the parameters jsr udiv32 ; Do the division lda ptr1 ; Result is in ptr1:sreg ldx ptr1+1 diff --git a/libsrc/runtime/lumod.s b/libsrc/runtime/lumod.s index 09909c0c9..eb6176b35 100644 --- a/libsrc/runtime/lumod.s +++ b/libsrc/runtime/lumod.s @@ -22,7 +22,7 @@ tosumod0ax: .endif tosumodeax: - jsr getlop ; Get the paramameters + jsr getlop ; Get the parameters jsr udiv32 ; Do the division lda tmp3 ; Remainder is in ptr2:tmp3:tmp4 sta sreg diff --git a/libsrc/runtime/popptr1.s b/libsrc/runtime/popptr1.s index 1d04330ab..b54bb9eb3 100644 --- a/libsrc/runtime/popptr1.s +++ b/libsrc/runtime/popptr1.s @@ -8,12 +8,18 @@ .import incsp2 .importzp sp, ptr1 + .macpack cpu + .proc popptr1 ; 14 bytes (four usages = at least 2 bytes saved) ldy #1 lda (sp),y ; get hi byte sta ptr1+1 ; into ptr hi - dey ; no optimization for 65C02 here to have Y=0 at exit! + dey ; dey even for for 65C02 here to have Y=0 at exit! +.if (.cpu .bitand ::CPU_ISET_65SC02) + lda (sp) ; get lo byte +.else lda (sp),y ; get lo byte +.endif sta ptr1 ; to ptr lo jmp incsp2 .endproc diff --git a/libsrc/runtime/pushax.s b/libsrc/runtime/pushax.s index ac181b994..27ddf641d 100644 --- a/libsrc/runtime/pushax.s +++ b/libsrc/runtime/pushax.s @@ -7,6 +7,8 @@ .export push0, pusha0, pushax .importzp sp + .macpack cpu + push0: lda #0 pusha0: ldx #0 @@ -29,7 +31,11 @@ pusha0: ldx #0 sta (sp),y ; (27) pla ; (31) dey ; (33) +.if (.cpu .bitand ::CPU_ISET_65SC02) + sta (sp) ; (37) +.else sta (sp),y ; (38) - rts ; (44) +.endif + rts ; (44/43) .endproc diff --git a/libsrc/runtime/pushptr1.s b/libsrc/runtime/pushptr1.s new file mode 100644 index 000000000..ca934d6c1 --- /dev/null +++ b/libsrc/runtime/pushptr1.s @@ -0,0 +1,14 @@ +; +; Colin Leroy-Mira, 2024 +; +; CC65 runtime: Push ptr1 to stack. +; A/X destroyed (set to ptr1) + + .export pushptr1 + .import pushax + .importzp ptr1 + +pushptr1: + lda ptr1 + ldx ptr1+1 + jmp pushax diff --git a/libsrc/runtime/returnFFFF.s b/libsrc/runtime/returnFFFF.s new file mode 100644 index 000000000..41180651b --- /dev/null +++ b/libsrc/runtime/returnFFFF.s @@ -0,0 +1,15 @@ +; +; Ullrich von Bassewitz, 25.10.2000 +; +; CC65 runtime: Return -1 in a/x +; + + .export returnFFFF + +.proc returnFFFF + + lda #$FF + tax + rts + +.endproc diff --git a/libsrc/runtime/shrax7.s b/libsrc/runtime/shrax7.s new file mode 100644 index 000000000..31712ca72 --- /dev/null +++ b/libsrc/runtime/shrax7.s @@ -0,0 +1,18 @@ +; +; Miloslaw Smyk, 2024 +; +; CC65 runtime: Scale the primary register by 128, unsigned +; + + .export shrax7 + +shrax7: ; HXXXXXXL hAAAAAAl + asl ; AAAAAAA0, h->C + txa + rol ; XXXXXXLh, H->C + ldx #$00 ; 00000000 XXXXXXLh + bcc :+ + inx ; 0000000H XXXXXXLh if C +: rts + + ; 12 cycles max, 9 bytes diff --git a/libsrc/serial/ser-kernel.s b/libsrc/serial/ser-kernel.s index b6c57a3b5..1514bcf77 100644 --- a/libsrc/serial/ser-kernel.s +++ b/libsrc/serial/ser-kernel.s @@ -7,6 +7,7 @@ .import return0, ser_libref .importzp ptr1 .interruptor ser_irq, 29 ; Export as high priority IRQ handler + .destructor _ser_uninstall .include "ser-kernel.inc" .include "ser-error.inc" @@ -44,7 +45,16 @@ ser_sig: .byte $73, $65, $72, SER_API_VERSION ; "ser", version _ser_install: - sta _ser_drv + ldy _ser_drv ; Check no driver is installed + bne ErrInstalled + ldy _ser_drv+1 + beq :+ +ErrInstalled: + ldx #$00 + lda #SER_ERR_INSTALLED + rts + +: sta _ser_drv sta ptr1 stx _ser_drv+1 stx ptr1+1 @@ -107,7 +117,14 @@ copy: lda (ptr1),y ; */ _ser_uninstall: - jsr ser_uninstall ; Call driver routine + ldx _ser_drv ; Check a driver is installed + bne :+ + ldx _ser_drv+1 + bne :+ + lda #SER_ERR_NO_DRIVER + rts + +: jsr ser_uninstall ; Call driver routine lda #$60 ; RTS opcode sta ser_irq ; Disable IRQ entry point @@ -117,5 +134,6 @@ _ser_clear_ptr: ; External entry point sta _ser_drv sta _ser_drv+1 ; Clear the driver pointer +done: tax rts ; Return zero diff --git a/libsrc/sim6502/exehdr.s b/libsrc/sim6502/exehdr.s index 09d099da5..6487e5b0c 100644 --- a/libsrc/sim6502/exehdr.s +++ b/libsrc/sim6502/exehdr.s @@ -9,11 +9,21 @@ .import __MAIN_START__ .import startup + .macpack cpu + .segment "EXEHDR" .byte $73, $69, $6D, $36, $35 ; 'sim65' .byte 2 ; header version - .byte .defined(__SIM65C02__) ; CPU type +.if (.cpu .bitand ::CPU_ISET_6502X) + .byte 2 +.elseif (.cpu .bitand ::CPU_ISET_65C02) + .byte 1 +.elseif (.cpu .bitand ::CPU_ISET_6502) + .byte 0 +.else + .error Unknown CPU type. +.endif .byte sp ; sp address .addr __MAIN_START__ ; load address .addr startup ; reset address diff --git a/libsrc/sim6502/paravirt.s b/libsrc/sim6502/paravirt.s index 0d8e528b1..b9fc38de5 100644 --- a/libsrc/sim6502/paravirt.s +++ b/libsrc/sim6502/paravirt.s @@ -7,11 +7,17 @@ ; int __fastcall__ write (int fd, const void* buf, unsigned count); ; - .export exit, args, _open, _close, _read, _write + .export exit, args, _open, _close, _read, _write, _lseek + .export __sysremove, ___osmaperrno +_lseek := $FFF1 +__sysremove := $FFF2 +___osmaperrno := $FFF3 _open := $FFF4 _close := $FFF5 _read := $FFF6 _write := $FFF7 args := $FFF8 exit := $FFF9 + + ; $FFFA-FFFF are hardware vectors, extend before not after! diff --git a/libsrc/sym1/tapeio.s b/libsrc/sym1/tapeio.s index 078ea7abd..f94e1336c 100644 --- a/libsrc/sym1/tapeio.s +++ b/libsrc/sym1/tapeio.s @@ -21,7 +21,7 @@ ldy #$80 jsr LOADT ; Read data from tape bcs error - jmp return0 ; Return 0 if sucessful + jmp return0 ; Return 0 if successful error: jmp return1 ; or 1 if not .endproc @@ -40,7 +40,7 @@ error: jmp return1 ; or 1 if not ldy #$80 jsr DUMPT ; Write data to tape bcs error - jmp return0 ; Return 0 if sucessful + jmp return0 ; Return 0 if successful error: jmp return1 ; or 1 if not .endproc diff --git a/libsrc/telestrat/write.s b/libsrc/telestrat/write.s index 37a896696..8509aa159 100644 --- a/libsrc/telestrat/write.s +++ b/libsrc/telestrat/write.s @@ -65,7 +65,7 @@ L2: ldy #0 cpx #$0A ; check for \n bne L3 BRK_TELEMON XWR0 ; macro send char to screen (channel 0 in telemon terms) - lda #$0D ; return to the beggining of the line + lda #$0D ; return to the beginning of the line BRK_TELEMON XWR0 ; macro diff --git a/libsrc/tgi/tgi_clippedline.s b/libsrc/tgi/tgi_clippedline.s index b0f1dd456..319bb7152 100644 --- a/libsrc/tgi/tgi_clippedline.s +++ b/libsrc/tgi/tgi_clippedline.s @@ -188,7 +188,7 @@ tgi_clip_sign: .res 1 ldx tgi_clip_dx+1 jsr udiv32by16r16 -; Check the sign of the final result and negate it if nessary +; Check the sign of the final result and negate it if necessary done: bit tmp1 jmi negax @@ -228,7 +228,7 @@ done: bit tmp1 ldx tgi_clip_dy+1 jsr udiv32by16r16 -; Check the sign of the final result and negate it if nessary +; Check the sign of the final result and negate it if necessary jmp muldiv_dydx::done @@ -279,7 +279,7 @@ L1: lda tgi_clip_o1 ; We must clip. If we haven't already done so, calculate dx/dy. -L2: lda tgi_clip_d ; Deltas alreay calculated? +L2: lda tgi_clip_d ; Deltas already calculated? bne HaveDeltas ; Jump if yes inc tgi_clip_d jsr calcdeltas diff --git a/libsrc/tgi/tgi_init.s b/libsrc/tgi/tgi_init.s index c8fd355d5..79651cbba 100644 --- a/libsrc/tgi/tgi_init.s +++ b/libsrc/tgi/tgi_init.s @@ -63,7 +63,7 @@ lda #<$100 ldx #>$100 jsr pushax ; Width scale = 1.0 - jsr pushax ; Heigh scale = 1.0 + jsr pushax ; Height scale = 1.0 jsr pusha ; Text direction = TGI_TEXT_HORIZONTAL jmp _tgi_settextstyle ; A = Font = TGI_FONT_BITMAP diff --git a/libsrc/vic20/joy/vic20-stdjoy.s b/libsrc/vic20/joy/vic20-stdjoy.s index b3de8766c..22ec0b638 100644 --- a/libsrc/vic20/joy/vic20-stdjoy.s +++ b/libsrc/vic20/joy/vic20-stdjoy.s @@ -82,7 +82,7 @@ COUNT: ; ------------------------------------------------------------------------ ; READ: Read a particular joystick passed in A. -; The current implemenation will ignore the joystick number because we do only +; The current implementation will ignore the joystick number because we do only ; have one joystick READ: lda #$7F ; mask for VIA2 JOYBIT: sw3 diff --git a/libsrc/zlib/crc32.s b/libsrc/zlib/crc32.s index 41b5fe9db..f80b1f437 100644 --- a/libsrc/zlib/crc32.s +++ b/libsrc/zlib/crc32.s @@ -1,6 +1,7 @@ ; ; 2001-11-14, Piotr Fusik ; 2018-05-20, Christian Kruger +; 2025-05-14, Piotr Fusik ; ; unsigned long __fastcall__ crc32 (unsigned long crc, ; const unsigned char* buf, @@ -9,7 +10,7 @@ .export _crc32 - .import compleax, incsp2, incsp4, popptr1, popeax + .import compleax, incsp4, popptr1, popeax .importzp sreg, ptr1, ptr2, tmp1, tmp2 POLYNOMIAL = $EDB88320 @@ -58,15 +59,12 @@ make_table: inx bne @L1 inc table_initialised -RET: rts _crc32: -; ptr2 = (len & 0xff) == 0 ? len : len + 0x100; - tay - beq @L1 +; ptr2 = len + 0x100 inx -@L1: sta ptr2 + sta ptr2 stx ptr2+1 ; ptr1 = buf jsr popptr1 @@ -78,20 +76,15 @@ _crc32: bne @dont_make jsr make_table @dont_make: -; eax = crc - jsr popeax -; if (len == 0) return crc; - ldy ptr2 - bne @L2 - ldy ptr2+1 - beq RET -@L2: ; eax = ~crc + jsr popeax jsr compleax stx tmp2 ldy #0 +@L1: cpy ptr2 + beq @low_end ; crc = (crc >> 8) ^ table[(crc & 0xff) ^ *p++]; -@L3: eor (ptr1),y +@L2: eor (ptr1),y tax lda table_0,x eor tmp2 @@ -106,12 +99,12 @@ _crc32: sta sreg+1 lda tmp1 iny - bne @L4 + bne @L1 inc ptr1+1 -@L4: dec ptr2 - bne @L3 + jmp @L1 +@low_end: dec ptr2+1 - bne @L3 + bne @L2 ldx tmp2 jmp compleax diff --git a/samples/Makefile b/samples/Makefile index 0e5292306..3680f542c 100644 --- a/samples/Makefile +++ b/samples/Makefile @@ -562,6 +562,11 @@ install: $(INSTALL) -d $(DESTDIR)$(samplesdir)/cbm $(INSTALL) -d $(DESTDIR)$(samplesdir)/gamate $(INSTALL) -d $(DESTDIR)$(samplesdir)/supervision + $(INSTALL) -d $(DESTDIR)$(samplesdir)/disasm + $(INSTALL) -d $(DESTDIR)$(samplesdir)/kim1 + $(INSTALL) -d $(DESTDIR)$(samplesdir)/lynx + $(INSTALL) -d $(DESTDIR)$(samplesdir)/sim65 + $(INSTALL) -d $(DESTDIR)$(samplesdir)/sym1 $(INSTALL) -m0644 *.* $(DESTDIR)$(samplesdir) $(INSTALL) -m0644 readme.txt $(DESTDIR)$(samplesdir) $(INSTALL) -m0644 Makefile $(DESTDIR)$(samplesdir) @@ -573,6 +578,11 @@ install: $(INSTALL) -m0644 cbm/*.* $(DESTDIR)$(samplesdir)/cbm $(INSTALL) -m0644 gamate/*.* $(DESTDIR)$(samplesdir)/gamate $(INSTALL) -m0644 supervision/*.* $(DESTDIR)$(samplesdir)/supervision + $(INSTALL) -m0644 disasm/*.* $(DESTDIR)$(samplesdir)/disasm + $(INSTALL) -m0644 kim1/*.* $(DESTDIR)$(samplesdir)/kim1 + $(INSTALL) -m0644 lynx/*.* $(DESTDIR)$(samplesdir)/lynx + $(INSTALL) -m0644 sim65/*.* $(DESTDIR)$(samplesdir)/sim65 + $(INSTALL) -m0644 sym1/*.* $(DESTDIR)$(samplesdir)/sym1 # -------------------------------------------------------------------------- # Packaging rules diff --git a/samples/cbm/Makefile b/samples/cbm/Makefile index 03387a061..4b89722d2 100644 --- a/samples/cbm/Makefile +++ b/samples/cbm/Makefile @@ -80,17 +80,19 @@ ifneq ($(filter disk samples.%,$(MAKECMDGOALS)),) C1541 ?= c1541 endif -DISK_c64 = samples.d64 +DISK_$(SYS) = samples.d64 EXELIST_c64 = \ fire \ plasma \ - nachtm + nachtm \ + hello EXELIST_c128 = \ fire \ plasma \ - nachtm + nachtm \ + hello EXELIST_cbm510 = \ fire \ @@ -101,16 +103,17 @@ EXELIST_cbm610 = \ nachtm EXELIST_plus4 = \ - plasma + plasma \ + hello EXELIST_c16 = \ - notavailable + hello EXELIST_pet = \ notavailable EXELIST_vic20 = \ - notavailable + hello ifneq ($(EXELIST_$(SYS)),) samples: $(EXELIST_$(SYS)) @@ -135,6 +138,15 @@ plasma: plasma.c $(CL) -t $(SYS) -O -o plasma -m plasma.map plasma.c nachtm: nachtm.c $(CL) -t $(SYS) -O -o nachtm -m nachtm.map nachtm.c +hello: hello-asm.s + # Use separate assembler ... + $(AS) -t $(SYS) hello-asm.s + # ... and linker commands ... + $(LD) -C $(SYS)-asm.cfg -o hello -m hello-asm.map -u __EXEHDR__ hello-asm.o $(SYS).lib + @$(DEL) hello-asm.o 2>$(NULLDEV) + # ... or compile & link utility +# $(CL) -C $(SYS)-asm.cfg -o hello -m hello-asm.map -u __EXEHDR__ hello-asm.s + # -------------------------------------------------------------------------- # Rule to make a CBM disk with all samples. Needs the c1541 program that comes diff --git a/samples/cbm/fire.c b/samples/cbm/fire.c index 40eff0707..1eae086da 100644 --- a/samples/cbm/fire.c +++ b/samples/cbm/fire.c @@ -56,7 +56,7 @@ /* Use static local variables for speed */ -#pragma static-locals (1); +#pragma static-locals (1) diff --git a/samples/cbm/hello-asm.s b/samples/cbm/hello-asm.s new file mode 100644 index 000000000..689dcc06b --- /dev/null +++ b/samples/cbm/hello-asm.s @@ -0,0 +1,15 @@ +; +; Sample assembly program for Commodore machines +; + + .include "cbm_kernal.inc" + + ldx #$00 +: lda text,x + beq out + jsr CHROUT + inx + bne :- +out: rts + +text: .asciiz "hello world!" diff --git a/samples/cbm/plasma.c b/samples/cbm/plasma.c index f48d6dc77..5c0b901f7 100644 --- a/samples/cbm/plasma.c +++ b/samples/cbm/plasma.c @@ -51,7 +51,7 @@ /* Use static local variables for speed */ -#pragma static-locals (1); +#pragma static-locals (1) static const unsigned char sinustable[0x100] = { diff --git a/samples/checkversion.c b/samples/checkversion.c index 1d3494a87..f2a9d4a49 100644 --- a/samples/checkversion.c +++ b/samples/checkversion.c @@ -10,11 +10,11 @@ #include <stdio.h> #include <stdlib.h> -#if ((__CC65__ & 0xff00) > 3) || ((__CC65__ & 0x000f) > 0) +#if ((__CC65__ >> 8) > 3) || ((__CC65__ & 0x000f) > 0) /* compiler version is 2.19-git or higher */ # define VER_MAJOR ((__CC65__ >> 8) & 0xff) # define VER_MINOR (__CC65__ & 0xff) -#elif ((__CC65__ & 0xff00) == 3) +#elif ((__CC65__ >> 8) == 3) /* broken values in version 2.16 - 2.19-git before the bug was fixed */ # define VER_MAJOR 2 # define VER_MINOR (((__CC65__ >> 4) & 0x0f) + 16) diff --git a/samples/geos/Makefile b/samples/geos/Makefile index 03f6b8cdc..578927760 100644 --- a/samples/geos/Makefile +++ b/samples/geos/Makefile @@ -3,6 +3,9 @@ # var. to build for another target system. SYS ?= geos-cbm +# Comes with the VICE emulator, see http://vice-emu.sourceforge.net/ +C1541 ?= c1541 + # If SYS was given on the commandline, redirect "c64" to "geos-cbm" and # "apple2enh" to "geos-apple" ifeq ($(origin SYS),command line) @@ -82,11 +85,11 @@ samples: $(EXELIST_$(SYS)) $(foreach dir,$(DIRLIST),$(SUBDIR_recipe)) define samples-geos -c1541 -attach $(0).d64 -geoswrite $(1); +$(C1541) -attach $(0).d64 -geoswrite $(1); endef samples-geos: $(EXELIST_$(SYS)) - c1541 -format "$@,01" d64 $@.d64 + $(C1541) -format "$@,01" d64 $@.d64 $(foreach tool,$(EXELIST_$(SYS)),$(call samples-geos,$(tool))) else samples: @@ -103,7 +106,7 @@ bitmap.c: logo.pcx bitmap-demo.cvt: bitmap.c bitmap-demores.grc bitmap-demo.c $(CL) -t $(SYS) -O -o $@ -m bitmap-demo.map bitmap-demores.grc bitmap-demo.c - + filesel.cvt: fileselres.grc filesel.c $(CL) -t $(SYS) -O -o $@ -m filesel.map fileselres.grc filesel.c @@ -134,7 +137,7 @@ vector-demo.cvt: vector-demores.grc vector-demo.c yesno.cvt: yesnores.grc yesno.c $(CL) -t $(SYS) -O -o $@ -m yesno.map yesnores.grc yesno.c - + clean: @$(DEL) overlay-demores.h 2>$(NULLDEV) @$(DEL) bitmap.c 2>$(NULLDEV) diff --git a/samples/geos/overlay-demo.c b/samples/geos/overlay-demo.c index 73ab0e3c0..e3e92e6c2 100644 --- a/samples/geos/overlay-demo.c +++ b/samples/geos/overlay-demo.c @@ -27,7 +27,7 @@ void show(char *name) ** rather place the all the code of certain source files into the overlay by ** compiling them with --code-name OVERLAY1. */ -#pragma code-name(push, "OVERLAY1"); +#pragma code-name(push, "OVERLAY1") void foo(void) { @@ -39,27 +39,27 @@ void foo(void) show("One"); } -#pragma code-name(pop); +#pragma code-name(pop) -#pragma code-name(push, "OVERLAY2"); +#pragma code-name(push, "OVERLAY2") void bar(void) { show("Two"); } -#pragma code-name(pop); +#pragma code-name(pop) -#pragma code-name(push, "OVERLAY3"); +#pragma code-name(push, "OVERLAY3") void foobar (void) { show("Three"); } -#pragma code-name(pop); +#pragma code-name(pop) void main(int /*argc*/, char *argv[]) diff --git a/samples/kim1/Makefile b/samples/kim1/Makefile index 74c415fdc..08bb2a780 100644 --- a/samples/kim1/Makefile +++ b/samples/kim1/Makefile @@ -31,9 +31,12 @@ else LD := $(if $(wildcard ../../bin/ld65*),../../bin/ld65,ld65) endif -EXELIST_kim1 = \ - kimHello.bin \ - kimSieve.bin +EXELIST_kim1 = \ + kimHello.bin \ + kimSieve.bin \ + kimLife.bin \ + kimTest.bin \ + kimGFX.bin ifneq ($(EXELIST_$(SYS)),) samples: $(EXELIST_$(SYS)) @@ -50,13 +53,65 @@ else @echo > $(NULLDEV) endif +subs.o: subs.asm + $(AS) subs.asm -o subs.o + +ramfont.o: ramfont.asm + $(AS) ramfont.asm -o ramfont.o + +kimLife.bin: kimLife.c + $(CL) -t kim1 -C kim1-60k.cfg -Oi -o kimLife.bin kimLife.c + +kimTest.bin: kimTest.c + $(CL) -t kim1 -C kim1-60k.cfg -Oi -o kimTest.bin kimTest.c + +kimGFX.bin: kimGFX.c subs.o ramfont.o + $(CL) -t kim1 --listing kimGFX.lst -C kim1-mtuE000.cfg -o kimGFX.bin kimGFX.c subs.o ramfont.o -Ln kimgfx.lbl + kimSieve.bin: kimSieve.c $(CL) -t kim1 -C kim1-60k.cfg -O -o kimSieve.bin kimSieve.c kimHello.bin: kimHello.c $(CL) -t kim1 -O -o kimHello.bin kimHello.c +# To build an intel-format file for the CORSHAM SD card reader + +kimLife.hex: kimLife.bin + srec_cat kimLife.bin -binary -offset 0x2000 -o kimLife.hex -Intel -address-length=2 + +kimTest.hex: kimTest.bin + srec_cat kimTest.bin -binary -offset 0x2000 -o kimTest.hex -Intel -address-length=2 + +kimGFX.hex: kimGFX.bin ramfont.o + srec_cat kimGFX.bin -binary -offset 0x2000 -o kimGFX.hex -Intel -address-length=2 + +# To build a paper tape file for uploading to the KIM-1 via terminal + +kimLife.ptp: kimLife.bin + srec_cat kimLife.bin -binary -offset 0x2000 -o kimLife.ptp -MOS_Technologies + +kimGFX.ptp: kimGFX.bin + srec_cat kimGFX.bin -binary -offset 0x2000 -o kimGFX.ptp -MOS_Technologies + +kimTest.ptp: kimTest.bin + srec_cat kimTest.bin -binary -offset 0x2000 -o kimTest.ptp -MOS_Technologies + clean: @$(DEL) kimSieve.bin 2>$(NULLDEV) @$(DEL) kimHello.bin 2>$(NULLDEV) + @$(DEL) kimLife.bin 2>$(NULLDEV) + @$(DEL) kimLife.ptp 2>$(NULLDEV) + @$(DEL) kimLife.hex 2>$(NULLDEV) + @$(DEL) kimTest.bin 2>$(NULLDEV) + @$(DEL) kimTest.ptp 2>$(NULLDEV) + @$(DEL) kimTest.hex 2>$(NULLDEV) + @$(DEL) kimGFX.bin 2>$(NULLDEV) + @$(DEL) kimGFX.ptp 2>$(NULLDEV) + @$(DEL) kimGFX.hex 2>$(NULLDEV) + @$(DEL) kimgfx.lbl 2>$(NULLDEV) + @$(DEL) kimGFX.lst 2>$(NULLDEV) + @$(DEL) subs.o 2>$(NULLDEV) + @$(DEL) ramfont.o 2>$(NULLDEV) + + diff --git a/samples/kim1/font.rom b/samples/kim1/font.rom new file mode 100644 index 000000000..e69de29bb diff --git a/samples/kim1/kimGFX.c b/samples/kim1/kimGFX.c new file mode 100644 index 000000000..45daafa1e --- /dev/null +++ b/samples/kim1/kimGFX.c @@ -0,0 +1,290 @@ +// -------------------------------------------------------------------------- +// Simple Graphics Test for KIM-1 with MTU Visible Memory Board +// +// Assumes the MTU Visible Memory Board mapped at 0xA000 for 8K of video RAM +// +// davepl@davepl.com +// -------------------------------------------------------------------------- + +#include <stdio.h> // For printf +#include <stdlib.h> // For rand, srand +#include <string.h> // For memcpy +#include <ctype.h> + +typedef unsigned char byte; + +extern void ClearScreen(void); // In subs.asm +extern void ScrollScreen(void); +extern void DrawCircle(void); +extern void SetPixel(void); +extern void ClearPixel(void); +extern void DrawChar(void); +extern void Demo(void); +extern void __fastcall__ Delay(byte loops); +extern void __fastcall__ DrawLine(byte bSet); +extern byte __fastcall__ AscToPet(byte in); +extern byte __fastcall__ PetToAsc(byte in); +extern byte __fastcall__ ReverseBits(byte in); +extern void __fastcall__ CharOut(byte asci_char); +extern byte __fastcall__ getch(); +extern unsigned char font8x8_basic[256][8]; + +extern int x1cord; +extern int y1cord; +extern int x2cord; +extern int y2cord; +extern int cursorX; +extern int cursorY; + +// If in zeropage: +// +// #pragma zpsym("x1cord") +// #pragma zpsym("x2cord") +// #pragma zpsym("y1cord") +// #pragma zpsym("y2cord") + +// Screen memory is placed at A000-BFFF, 320x200 pixels, mapped right to left within each horizontal byte + +byte * screen = (byte *) 0xA000; + +// Cursor position + +#define SCREEN_WIDTH 320 +#define SCREEN_HEIGHT 200 +#define CHARWIDTH 8 +#define CHARHEIGHT 8 +#define BYTESPERROW (SCREEN_WIDTH / 8) +#define BYTESPERCHARROW (BYTESPERROW * 8) +#define CHARSPERROW (SCREEN_WIDTH / CHARWIDTH) +#define ROWSPERCOLUMN (SCREEN_HEIGHT / CHARHEIGHT) + +// SETPIXEL +// +// 0 <= x < 320 +// 0 <= y < 200 +// +// Draws a pixel on the screen in white or black at pixel pos x, y + +void SETPIXEL(int x, int y, byte b) +{ + x1cord = x; + y1cord = y; + + if (b) + SetPixel(); + else + ClearPixel(); +} + +// DRAWPIXEL +// +// 0 <= x < 320 +// 0 <= y < 200 +// +// Turns on a screen pixel at pixel pos x,y + +void DRAWPIXEL(int x, int y) +{ + x1cord = x; + y1cord = y; + SetPixel(); +} + +int c; + +void DrawText(char * psz) +{ + while (*psz) + { + while (cursorX >= CHARSPERROW) + { + cursorX -= CHARSPERROW; + cursorY += 1; + } + + // If we've gone off the bottom of the screen, we scroll the screen and back up to the last line again + + if (cursorY >= ROWSPERCOLUMN) + { + cursorY = ROWSPERCOLUMN - 1; + ScrollScreen(); + } + + // If we output a newline we advanced the cursor down one line and reset it to the left + + if (*psz == 0x0A) + { + cursorX = 0; + cursorY++; + psz++; + } + else + { + c = *psz; + + __asm__ ("ldx %v", cursorX); + __asm__ ("ldy %v", cursorY); + __asm__ ("lda %v", c); + DrawChar(); + cursorX++; + psz++; + } + } +} + +void DrawTextAt(int x, int y, char * psz) +{ + cursorX = x; + cursorY = y; + DrawText(psz); +} + +// Something like Bresenham's algorithm for drawing a line +/* +void DrawLine(int x0, int y0, int x1, int y1, byte val) +{ + int dx = abs(x1 - x0), sx = x0 < x1 ? 1 : -1; + int dy = abs(y1 - y0), sy = y0 < y1 ? 1 : -1; + int err = (dx > dy ? dx : -dy) / 2, e2; + + while (1) + { + SETPIXEL(x0, y0, val); + + if (x0 == x1 && y0 == y1) + break; + + e2 = err; + + if (e2 > -dx) + { + err -= dy; + x0 += sx; + } + if (e2 < dy) + { + err += dx; + y0 += sy; + } + } +} +*/ + +// DrawCircle +// +// Draw a circle without sin, cos, or floating point! + +void DrawCircleC(int x0, int y0, int radius, byte) +{ + x1cord = x0; + y1cord = y0; + y2cord = radius; + DrawCircle(); +} + +void DrawLineC(int x1, int y1, int x2, int y2, byte bSet) +{ + x1cord = x1; + y1cord = y1; + x2cord = x2; + y2cord = y2; + DrawLine(bSet); +} + +// MirrorFont +// +// RAM font is backwards left-right relative to the way memory is laid out on the KIM-1, so we swap all the +// bytes in place by reversing the order of the bits in every byte + +void MirrorFont() +{ + int c; + byte * pb = (byte *) font8x8_basic; + + for (c = 0; c < 128 * 8; c++) + pb[c] = ReverseBits(pb[c]); +} + +// DrawScreenMoire +// +// Draws a moire pattern on the screen without clearing it first + +void DrawMoire(int left, int top, int right, int bottom, byte pixel) +{ + int x, y; + + for (x = left; x < right; x += 6) + DrawLineC(x, top, right - x + left, bottom, pixel); + + for (y = top; y < bottom; y += 6) + DrawLineC(left, y, right, bottom - y + top, pixel); +} + +void DrawScreenMoire(int left, int top, int right, int bottom) +{ + int x, y; + + DrawLineC(left, top, right, top, 1); + DrawLineC(left, bottom, right, bottom, 1); + DrawLineC(left, top, left, bottom, 1); + DrawLineC(right, top, right, bottom, 1); + + left++; top++; right--; bottom--; + + for (x = left; x < right; x += 6) + DrawLineC(x, top, right - x + left, bottom, 1); + for (y = top; y < bottom; y += 6) + DrawLineC(left, y, right, bottom - y + top, 1); + for (x = left; x < right; x += 6) + DrawLineC(x, top, right - x + left, bottom, 0); + for (y = top; y < bottom; y += 6) + DrawLineC(left, y, right, bottom - y + top, 0); + +} + +int main (void) +{ + + int i; + int c = 0; + + Demo(); + + CharOut('R'); + CharOut('E'); + CharOut('A'); + CharOut('D'); + CharOut('Y'); + CharOut('.'); + CharOut('\n'); + + + while(1) + { + c = toupper(getch()); + if (c != EOF) + CharOut(c); + } + + // Clear the screen memory + while(1) + { + Demo(); + DrawScreenMoire(0,30, 319, 199); + Delay(10); + + Demo(); + for (i = 5; i < 80; i+=5) + { + DrawCircleC(SCREEN_WIDTH/2, SCREEN_HEIGHT/2 + 20, i, 1); + DrawCircleC(SCREEN_WIDTH/4, SCREEN_HEIGHT/2 + 20, i, 1); + DrawCircleC(SCREEN_WIDTH*3/4, SCREEN_HEIGHT/2 + 20, i, 1); + } + + Delay(10); + + } + + printf("Done, exiting...\r\n"); + return 0; +} diff --git a/samples/kim1/kimLife.c b/samples/kim1/kimLife.c new file mode 100644 index 000000000..fb1696b9e --- /dev/null +++ b/samples/kim1/kimLife.c @@ -0,0 +1,144 @@ +// -------------------------------------------------------------------------- +// Conway's Game of Life for KIM-1 +// +// Assumes the MTU Visible Memory Board mapped at 0x8000 for 8K of video RAM +// +// Dave Plummer on a rainy Thursday +// +// davepl@davepl.com +// -------------------------------------------------------------------------- + +#include <stdio.h> // For printf +#include <stdlib.h> // For rand, srand +#include <string.h> // For memcpy + +typedef unsigned char byte; + +// World size + +#define WIDTH 320 +#define HEIGHT 200 +#define NUMBITS 64000 +#define NUMBYTES 8000 +#define DENSITY 50 + +// Screen memory is placed at 8000, our world copy at A000, and they use the same layout so +// that we can memcpy from one to the other without translating + +byte * world = (byte *) 0x8000; +byte * new_world = (byte *) 0xA000; + +// BITARRAY +// +// Access individual bits in a block of memory + +// Access to the screen bitmap + +byte GETBIT(byte *p, int n) +{ + return (p[n >> 3] & (1 << (n & 7))) ? 1 : 0; +} + +void SETBIT(byte *p, int n) +{ + p[n >> 3] |= (1 << (n & 7)); +} + +void CLRBIT(byte *p, int n) +{ + p[n >> 3] &= ~(1 << (n & 7)); +} + +void SETPIXEL(byte * p, int x, int y, byte b) +{ + if (b) + SETBIT(p, y * WIDTH + x); + else + CLRBIT(p, y * WIDTH + x); +} + +byte GETPIXEL(byte *p, int x, int y) +{ + return GETBIT(p, y * WIDTH + x); +} + +// RandomFillWorld +// +// Populates the initial world with random cells + +void RandomFillWorld() +{ + int x, y; + + // I need a better way to see the RNG or it'll be the same game every time! + srand(0); + for (x = 0; x < WIDTH; x++) + { + for (y = 0; y < HEIGHT; y++) + { + byte b = ((rand() % 100) < DENSITY) ? 1 : 0; + SETPIXEL(world, x, y, b); + } + } +} + +// CountNeighbors +// +// Count the number of live cells around the given spot, excluding the actual spot specified + +int CountNeighbors(int x, int y) +{ + int i, j, nx, ny, count = 0; + + for (j = -1; j <= 1; j++) + { + for (i = -1; i <= 1; i++) + { + if (i != 0 || j != 0) + { + nx = (x + i + WIDTH) % WIDTH; + ny = (y + j + HEIGHT) % HEIGHT; + count += GETPIXEL(world, nx, ny) ? 1 : 0; + } + } + } + return count; +} + +// UpdateWorld +// +// Applies the rules of Conway's Game of Life to the cells + +void UpdateWorld() +{ + int x, y; + + for (y = 0; y < HEIGHT; y++) + { + for (x = 0; x < WIDTH; x++) + { + int neighbors = CountNeighbors(x, y); + if (GETPIXEL(world, x, y)) + SETPIXEL(new_world, x, y, (neighbors == 2 || neighbors == 3)); + else + SETPIXEL(new_world, x, y, (neighbors == 3)); + } + } +} + +int main (void) +{ + printf("\r\nStarting Conway's Game of Life: Randomizing World...\r\n"); + RandomFillWorld(); + printf("World Ready, Running!\r\n"); + + for (;;) + { + UpdateWorld(); + printf("["); + memcpy(world, new_world, NUMBYTES); + printf("]"); + } + + return 0; +} diff --git a/samples/kim1/kimTest.c b/samples/kim1/kimTest.c new file mode 100644 index 000000000..273aeb584 --- /dev/null +++ b/samples/kim1/kimTest.c @@ -0,0 +1,262 @@ +// -------------------------------------------------------------------------- +// Diagnostics Test for KIM-1 +// +// Dave Plummer +// davepl@davepl.com +// +// Memory test examples by Michael Barr +// +// -------------------------------------------------------------------------- + +#include <stdio.h> // For printf +#include <stdlib.h> // For rand, srand +#include <string.h> // For memcpy + +typedef unsigned char byte; + +// RepeatChar +// +// Outputs a given character N times + +void RepeatChar(char c, size_t count) +{ + while (count--) + putc(c, stdout); +} + +/********************************************************************** + * + * Function: memTestDataBus() + * + * Description: Test the data bus wiring in a memory region by + * performing a walking 1's test at a fixed address + * within that region. The address (and hence the + * memory region) is selected by the caller. + * + * Returns: 0 if the test succeeds. + * A non-zero result is the first pattern that failed. + * + **********************************************************************/ + +byte memTestDataBus(volatile byte * address) +{ + byte pattern; + + // Perform a walking 1's test at the given address. + + for (pattern = 1; pattern != 0; pattern <<= 1) + { + // Write the test pattern. + *address = pattern; + + // Read it back and check it + if (*address != pattern) + { + printf("\r\nmemTestDataBus: FAILED at %04x with pattern %02x\r\n", address, pattern); + return (pattern); + } + } + + return (0); +} + +/********************************************************************** + * + * Function: memTestAddressBus() + * + * Description: Test the address bus wiring in a memory region by + * performing a walking 1's test on the relevant bits + * of the address and checking for aliasing. This test + * will find single-bit address failures such as stuck + * -high, stuck-low, and shorted pins. The base address + * and size of the region are selected by the caller. + * + * Notes: For best results, the selected base address should + * have enough LSB 0's to guarantee single address bit + * changes. For example, to test a 64-Kbyte region, + * select a base address on a 64-Kbyte boundary. Also, + * select the region size as a power-of-two--if at all + * possible. + * + * Returns: NULL if the test succeeds. + * A non-zero result is the first address at which an + * aliasing problem was uncovered. By examining the + * contents of memory, it may be possible to gather + * additional information about the problem. + * + **********************************************************************/ + +byte * memTestAddressBus(volatile byte * baseAddress, unsigned long nBytes) +{ + unsigned long addressMask = (nBytes/sizeof(byte) - 1); + unsigned long offset; + unsigned long testOffset; + + byte pattern = (byte) 0xAAAAAAAA; + byte antipattern = (byte) 0x55555555; + + + //Write the default pattern at each of the power-of-two offsets. + + for (offset = 1; (offset & addressMask) != 0; offset <<= 1) + { + baseAddress[offset] = pattern; + } + + // Check for address bits stuck high. + + testOffset = 0; + baseAddress[testOffset] = antipattern; + + for (offset = 1; (offset & addressMask) != 0; offset <<= 1) + { + if (baseAddress[offset] != pattern) + { + printf("\r\nmemTestAddressBus: FAILED at %04x with pattern %02x\r\n", baseAddress+offset, pattern); + return ((byte *) &baseAddress[offset]); + } + if (offset % 1024 == 0) + printf("."); + } + + baseAddress[testOffset] = pattern; + + + // Check for address bits stuck low or shorted. + + for (testOffset = 1; (testOffset & addressMask) != 0; testOffset <<= 1) + { + baseAddress[testOffset] = antipattern; + + if (baseAddress[0] != pattern) + { + return ((byte *) &baseAddress[testOffset]); + } + + for (offset = 1; (offset & addressMask) != 0; offset <<= 1) + { + if ((baseAddress[offset] != pattern) && (offset != testOffset)) + { + printf("\r\nmemTestAddressBus: FAILED at %04x with pattern %02x\r\n", baseAddress+offset, pattern); + return ((byte *) &baseAddress[testOffset]); + } + } + baseAddress[testOffset] = pattern; + } + return (NULL); +} + +/********************************************************************** + * + * Function: memTestDevice() + * + * Description: Test the integrity of a physical memory device by + * performing an increment/decrement test over the + * entire region. In the process every storage bit + * in the device is tested as a zero and a one. The + * base address and the size of the region are + * selected by the caller. + * + * Returns: NULL if the test succeeds. + * + * A non-zero result is the first address at which an + * incorrect value was read back. By examining the + * contents of memory, it may be possible to gather + * additional information about the problem. + * + **********************************************************************/ + +byte * memTestDevice(volatile byte * baseAddress, unsigned long nBytes) +{ + unsigned long offset; + unsigned long nWords = nBytes / sizeof(byte); + + byte pattern; + byte antipattern; + + + // Fill memory with a known pattern. + + for (pattern = 1, offset = 0; offset < nWords; pattern++, offset++) + baseAddress[offset] = pattern; + + // Check each location and invert it for the second pass. + + for (pattern = 1, offset = 0; offset < nWords; pattern++, offset++) + { + if (offset % 1024 == 0) + printf("%04X ", (int) &baseAddress[offset]); + + if (baseAddress[offset] != pattern) + { + printf("\r\nmemTestDevice: FAILED at %04x with pattern %02x\r\n", (int) &baseAddress[offset], pattern); + return ((byte *) &baseAddress[offset]); + } + + antipattern = ~pattern; + baseAddress[offset] = antipattern; + + } + + // Check each location for the inverted pattern and zero it. + + for (pattern = 1, offset = 0; offset < nWords; pattern++, offset++) + { + if (offset % 1024 == 0) + printf("%04X ", (int) &baseAddress[offset]); + + antipattern = ~pattern; + if (baseAddress[offset] != antipattern) + { + printf("\r\nmemTestDevice: FAILED at %04x with antipattern %02x\r\n", (int) &baseAddress[offset], pattern); + return ((byte *) &baseAddress[offset]); + } + } + + return (NULL); +} + +// TestMemory +// +// Run all three memory tests + +byte TestMemory(byte * startAddress, unsigned long size) +{ + if ((memTestDataBus(startAddress) != 0) || + (memTestAddressBus(startAddress, size) != NULL) || + (memTestDevice(startAddress, size) != NULL)) + { + return (-1); + } + else + { + return (0); + } +} + +int main (void) +{ + printf("\r\nTesting KIM-1...\r\n"); + RepeatChar('-', 39); + + printf("\r\nTesting RIOT RAM: 1780-17BF\r\n"); + if (TestMemory((byte *)0x1780, 0x17BF - 0x1780)) + return 0; + + printf("\r\nTesting RIOT RAM: 17C0-17E6\r\n"); + if (TestMemory((byte *)0x17C0, 0x17E6 - 0x17C0)) + return 0; + + printf("\r\nTesting Memory: 0400-13FF\r\n"); + if (TestMemory((byte *)0x0400, 0x13FF - 0x0400)) + return 0; + + printf("\r\nTesting Memory: 4000-DFFF\r\n"); + if (TestMemory((byte *)0x4000, 0xDFFF - 0x4000)) + return 0; + + printf("\r\nPASS!\r\n"); + return 1; +} + + diff --git a/samples/kim1/ramfont.asm b/samples/kim1/ramfont.asm new file mode 100644 index 000000000..ac0b2c9cd --- /dev/null +++ b/samples/kim1/ramfont.asm @@ -0,0 +1,272 @@ +;----------------------------------------------------------------------------------- +; KIMGFX: Simple pixel graphics for the MOS/Commodore KIM-1 +;----------------------------------------------------------------------------------- +; (c) Plummer's Software Ltd, 04/25/2023 Created +; David Plummer +;----------------------------------------------------------------------------------- +; +; File: ramfont.s +; Magnetic OCR (check number style) Font data +; +;----------------------------------------------------------------------------------- + +.segment "CODE" +.export _font8x8_basic + +_font8x8_basic: + .byte $1c, $22, $4a, $56, $4c, $20, $1e, $00 ; PETSCII code 0 + .byte $3c, $24, $24, $7e, $62, $62, $62, $00 ; PETSCII code 1 + .byte $78, $44, $44, $7c, $62, $62, $7e, $00 ; PETSCII code 2 + .byte $7e, $42, $40, $60, $60, $62, $7e, $00 ; PETSCII code 3 + .byte $7c, $46, $42, $62, $62, $66, $7c, $00 ; PETSCII code 4 + .byte $7e, $40, $40, $7c, $60, $60, $7e, $00 ; PETSCII code 5 + .byte $7e, $40, $40, $7e, $60, $60, $60, $00 ; PETSCII code 6 + .byte $7e, $42, $40, $6e, $62, $62, $7e, $00 ; PETSCII code 7 + .byte $42, $42, $42, $7e, $62, $62, $62, $00 ; PETSCII code 8 + .byte $08, $08, $08, $0c, $0c, $0c, $0c, $00 ; PETSCII code 9 + .byte $04, $04, $04, $06, $06, $46, $7e, $00 ; PETSCII code 10 + .byte $42, $44, $48, $7c, $62, $62, $62, $00 ; PETSCII code 11 + .byte $40, $40, $40, $60, $60, $60, $7e, $00 ; PETSCII code 12 + .byte $7e, $4a, $4a, $6a, $6a, $6a, $6a, $00 ; PETSCII code 13 + .byte $7e, $42, $42, $62, $62, $62, $62, $00 ; PETSCII code 14 + .byte $7e, $46, $42, $42, $42, $42, $7e, $00 ; PETSCII code 15 + .byte $7e, $42, $42, $7e, $60, $60, $60, $00 ; PETSCII code 16 + .byte $7e, $42, $42, $42, $4a, $4e, $7e, $00 ; PETSCII code 17 + .byte $7c, $44, $44, $7c, $62, $62, $62, $00 ; PETSCII code 18 + .byte $7e, $42, $40, $7e, $06, $46, $7e, $00 ; PETSCII code 19 + .byte $3e, $10, $10, $18, $18, $18, $18, $00 ; PETSCII code 20 + .byte $42, $42, $42, $62, $62, $62, $7e, $00 ; PETSCII code 21 + .byte $62, $62, $62, $66, $24, $24, $3c, $00 ; PETSCII code 22 + .byte $4a, $4a, $4a, $6a, $6a, $6a, $7e, $00 ; PETSCII code 23 + .byte $42, $42, $66, $18, $66, $62, $62, $00 ; PETSCII code 24 + .byte $22, $22, $22, $3e, $18, $18, $18, $00 ; PETSCII code 25 + .byte $7e, $42, $06, $18, $60, $62, $7e, $00 ; PETSCII code 26 + .byte $3c, $20, $20, $20, $20, $20, $3c, $00 ; PETSCII code 27 + .byte $00, $40, $20, $10, $08, $04, $02, $00 ; PETSCII code 28 + .byte $3c, $04, $04, $04, $04, $04, $3c, $00 ; PETSCII code 29 + .byte $00, $08, $1c, $2a, $08, $08, $14, $14 ; PETSCII code 30 + .byte $00, $00, $10, $20, $7f, $20, $10, $00 ; PETSCII code 31 + .byte $00, $00, $00, $00, $00, $00, $00, $00 ; PETSCII code 32 + .byte $08, $08, $08, $0c, $0c, $00, $0c, $00 ; PETSCII code 33 + .byte $6c, $24, $6c, $00, $00, $00, $00, $00 ; PETSCII code 34 + .byte $24, $24, $7e, $24, $7e, $24, $24, $00 ; PETSCII code 35 + .byte $08, $3e, $20, $3e, $06, $3e, $08, $00 ; PETSCII code 36 + .byte $00, $62, $64, $08, $10, $26, $46, $00 ; PETSCII code 37 + .byte $3c, $20, $24, $7e, $64, $64, $7c, $00 ; PETSCII code 38 + .byte $1c, $18, $10, $00, $00, $00, $00, $00 ; PETSCII code 39 + .byte $04, $08, $10, $10, $10, $08, $04, $00 ; PETSCII code 40 + .byte $20, $10, $08, $08, $08, $10, $20, $00 ; PETSCII code 41 + .byte $08, $2a, $1c, $3e, $1c, $2a, $08, $00 ; PETSCII code 42 + .byte $00, $08, $08, $3e, $08, $08, $00, $00 ; PETSCII code 43 + .byte $00, $00, $00, $00, $00, $18, $18, $08 ; PETSCII code 44 + .byte $00, $00, $00, $7e, $00, $00, $00, $00 ; PETSCII code 45 + .byte $00, $00, $00, $00, $00, $18, $18, $00 ; PETSCII code 46 + .byte $00, $02, $04, $08, $10, $20, $40, $00 ; PETSCII code 47 + .byte $7e, $62, $52, $4a, $46, $46, $7e, $00 ; PETSCII code 48 + .byte $18, $08, $08, $18, $18, $1a, $3e, $00 ; PETSCII code 49 + .byte $7e, $42, $02, $7e, $60, $60, $7e, $00 ; PETSCII code 50 + .byte $7c, $44, $04, $1e, $06, $46, $7e, $00 ; PETSCII code 51 + .byte $44, $44, $44, $44, $7e, $0c, $0c, $00 ; PETSCII code 52 + .byte $7e, $40, $7e, $06, $06, $46, $7e, $00 ; PETSCII code 53 + .byte $7e, $42, $40, $7e, $46, $46, $7e, $00 ; PETSCII code 54 + .byte $7e, $02, $02, $06, $06, $06, $06, $00 ; PETSCII code 55 + .byte $3c, $24, $24, $7e, $46, $46, $7e, $00 ; PETSCII code 56 + .byte $7e, $42, $42, $7e, $06, $06, $06, $00 ; PETSCII code 57 + .byte $00, $00, $18, $00, $00, $18, $00, $00 ; PETSCII code 58 + .byte $00, $00, $18, $00, $00, $18, $18, $08 ; PETSCII code 59 + .byte $0e, $18, $30, $60, $30, $18, $0e, $00 ; PETSCII code 60 + .byte $00, $00, $7e, $00, $7e, $00, $00, $00 ; PETSCII code 61 + .byte $70, $18, $0c, $06, $0c, $18, $70, $00 ; PETSCII code 62 + .byte $7e, $02, $02, $7e, $60, $00, $60, $00 ; PETSCII code 63 + .byte $00, $00, $00, $00, $ff, $00, $00, $00 ; PETSCII code 64 + .byte $08, $1c, $3e, $7f, $7f, $1c, $3e, $00 ; PETSCII code 65 + .byte $10, $10, $10, $10, $10, $10, $10, $10 ; PETSCII code 66 + .byte $00, $00, $00, $ff, $00, $00, $00, $00 ; PETSCII code 67 + .byte $00, $00, $ff, $00, $00, $00, $00, $00 ; PETSCII code 68 + .byte $00, $ff, $00, $00, $00, $00, $00, $00 ; PETSCII code 69 + .byte $00, $00, $00, $00, $00, $ff, $00, $00 ; PETSCII code 70 + .byte $20, $20, $20, $20, $20, $20, $20, $20 ; PETSCII code 71 + .byte $04, $04, $04, $04, $04, $04, $04, $04 ; PETSCII code 72 + .byte $00, $00, $00, $00, $e0, $10, $08, $08 ; PETSCII code 73 + .byte $08, $08, $08, $04, $03, $00, $00, $00 ; PETSCII code 74 + .byte $08, $08, $08, $10, $e0, $00, $00, $00 ; PETSCII code 75 + .byte $80, $80, $80, $80, $80, $80, $80, $ff ; PETSCII code 76 + .byte $80, $40, $20, $10, $08, $04, $02, $01 ; PETSCII code 77 + .byte $01, $02, $04, $08, $10, $20, $40, $80 ; PETSCII code 78 + .byte $ff, $80, $80, $80, $80, $80, $80, $80 ; PETSCII code 79 + .byte $ff, $01, $01, $01, $01, $01, $01, $01 ; PETSCII code 80 + .byte $00, $3c, $7e, $7e, $7e, $7e, $3c, $00 ; PETSCII code 81 + .byte $00, $00, $00, $00, $00, $00, $ff, $00 ; PETSCII code 82 + .byte $36, $7f, $7f, $7f, $3e, $1c, $08, $00 ; PETSCII code 83 + .byte $40, $40, $40, $40, $40, $40, $40, $40 ; PETSCII code 84 + .byte $00, $00, $00, $00, $03, $04, $08, $08 ; PETSCII code 85 + .byte $81, $42, $24, $18, $18, $24, $42, $81 ; PETSCII code 86 + .byte $00, $3c, $42, $42, $42, $42, $3c, $00 ; PETSCII code 87 + .byte $08, $1c, $2a, $77, $2a, $08, $08, $00 ; PETSCII code 88 + .byte $02, $02, $02, $02, $02, $02, $02, $02 ; PETSCII code 89 + .byte $08, $1c, $3e, $7f, $3e, $1c, $08, $00 ; PETSCII code 90 + .byte $08, $08, $08, $08, $ff, $08, $08, $08 ; PETSCII code 91 + .byte $a0, $50, $a0, $50, $a0, $50, $a0, $50 ; PETSCII code 92 + .byte $08, $08, $08, $08, $08, $08, $08, $08 ; PETSCII code 93 + .byte $00, $00, $01, $3e, $54, $14, $14, $00 ; PETSCII code 94 + .byte $ff, $7f, $3f, $1f, $0f, $07, $03, $01 ; PETSCII code 95 + .byte $00, $00, $00, $00, $00, $00, $00, $00 ; PETSCII code 96 + .byte $f0, $f0, $f0, $f0, $f0, $f0, $f0, $f0 ; PETSCII code 97 + .byte $00, $00, $00, $00, $ff, $ff, $ff, $ff ; PETSCII code 98 + .byte $ff, $00, $00, $00, $00, $00, $00, $00 ; PETSCII code 99 + .byte $00, $00, $00, $00, $00, $00, $00, $ff ; PETSCII code 100 + .byte $80, $80, $80, $80, $80, $80, $80, $80 ; PETSCII code 101 + .byte $aa, $55, $aa, $55, $aa, $55, $aa, $55 ; PETSCII code 102 + .byte $01, $01, $01, $01, $01, $01, $01, $01 ; PETSCII code 103 + .byte $00, $00, $00, $00, $aa, $55, $aa, $55 ; PETSCII code 104 + .byte $ff, $fe, $fc, $f8, $f0, $e0, $c0, $80 ; PETSCII code 105 + .byte $03, $03, $03, $03, $03, $03, $03, $03 ; PETSCII code 106 + .byte $08, $08, $08, $08, $0f, $08, $08, $08 ; PETSCII code 107 + .byte $00, $00, $00, $00, $0f, $0f, $0f, $0f ; PETSCII code 108 + .byte $08, $08, $08, $08, $0f, $00, $00, $00 ; PETSCII code 109 + .byte $00, $00, $00, $00, $f8, $08, $08, $08 ; PETSCII code 110 + .byte $00, $00, $00, $00, $00, $00, $ff, $ff ; PETSCII code 111 + .byte $00, $00, $00, $00, $0f, $08, $08, $08 ; PETSCII code 112 + .byte $08, $08, $08, $08, $ff, $00, $00, $00 ; PETSCII code 113 + .byte $00, $00, $00, $00, $ff, $08, $08, $08 ; PETSCII code 114 + .byte $08, $08, $08, $08, $f8, $08, $08, $08 ; PETSCII code 115 + .byte $c0, $c0, $c0, $c0, $c0, $c0, $c0, $c0 ; PETSCII code 116 + .byte $e0, $e0, $e0, $e0, $e0, $e0, $e0, $e0 ; PETSCII code 117 + .byte $07, $07, $07, $07, $07, $07, $07, $07 ; PETSCII code 118 + .byte $ff, $ff, $00, $00, $00, $00, $00, $00 ; PETSCII code 119 + .byte $ff, $ff, $ff, $00, $00, $00, $00, $00 ; PETSCII code 120 + .byte $00, $00, $00, $00, $00, $ff, $ff, $ff ; PETSCII code 121 + .byte $01, $01, $01, $01, $01, $01, $01, $ff ; PETSCII code 122 + .byte $00, $00, $00, $00, $f0, $f0, $f0, $f0 ; PETSCII code 123 + .byte $0f, $0f, $0f, $0f, $00, $00, $00, $00 ; PETSCII code 124 + .byte $08, $08, $08, $08, $f8, $00, $00, $00 ; PETSCII code 125 + .byte $f0, $f0, $f0, $f0, $00, $00, $00, $00 ; PETSCII code 126 + .byte $f0, $f0, $f0, $f0, $0f, $0f, $0f, $0f ; PETSCII code 127 + .byte $1c, $22, $4a, $56, $4c, $20, $1e, $00 ; PETSCII code 128 + .byte $00, $00, $3c, $04, $7c, $64, $7c, $00 ; PETSCII code 129 + .byte $40, $40, $7e, $42, $62, $62, $7e, $00 ; PETSCII code 130 + .byte $00, $00, $7e, $42, $60, $62, $7e, $00 ; PETSCII code 131 + .byte $02, $02, $7e, $42, $62, $62, $7e, $00 ; PETSCII code 132 + .byte $00, $00, $7e, $42, $7e, $60, $7e, $00 ; PETSCII code 133 + .byte $1e, $12, $10, $7c, $18, $18, $18, $00 ; PETSCII code 134 + .byte $00, $00, $7e, $42, $62, $7e, $02, $7e ; PETSCII code 135 + .byte $40, $40, $7e, $42, $62, $62, $62, $00 ; PETSCII code 136 + .byte $18, $00, $10, $10, $18, $18, $18, $00 ; PETSCII code 137 + .byte $0c, $00, $08, $0c, $0c, $0c, $44, $7c ; PETSCII code 138 + .byte $40, $40, $44, $48, $78, $64, $64, $00 ; PETSCII code 139 + .byte $10, $10, $10, $10, $18, $18, $18, $00 ; PETSCII code 140 + .byte $00, $00, $7f, $49, $6d, $6d, $6d, $00 ; PETSCII code 141 + .byte $00, $00, $7e, $42, $62, $62, $62, $00 ; PETSCII code 142 + .byte $00, $00, $7e, $42, $62, $62, $7e, $00 ; PETSCII code 143 + .byte $00, $00, $7e, $42, $62, $7e, $40, $40 ; PETSCII code 144 + .byte $00, $00, $7e, $42, $46, $7e, $02, $02 ; PETSCII code 145 + .byte $00, $00, $7e, $40, $60, $60, $60, $00 ; PETSCII code 146 + .byte $00, $00, $7e, $40, $7e, $06, $7e, $00 ; PETSCII code 147 + .byte $10, $10, $7c, $10, $18, $18, $18, $00 ; PETSCII code 148 + .byte $00, $00, $42, $42, $62, $62, $7e, $00 ; PETSCII code 149 + .byte $00, $00, $62, $62, $66, $24, $3c, $00 ; PETSCII code 150 + .byte $00, $00, $49, $49, $6d, $6d, $7f, $00 ; PETSCII code 151 + .byte $00, $00, $42, $42, $3c, $62, $62, $00 ; PETSCII code 152 + .byte $00, $00, $62, $62, $42, $7e, $02, $7e ; PETSCII code 153 + .byte $00, $00, $7e, $06, $18, $60, $7e, $00 ; PETSCII code 154 + .byte $3c, $20, $20, $20, $20, $20, $3c, $00 ; PETSCII code 155 + .byte $00, $40, $20, $10, $08, $04, $02, $00 ; PETSCII code 156 + .byte $3c, $04, $04, $04, $04, $04, $3c, $00 ; PETSCII code 157 + .byte $00, $08, $1c, $2a, $08, $08, $14, $14 ; PETSCII code 158 + .byte $00, $00, $10, $20, $7f, $20, $10, $00 ; PETSCII code 159 + .byte $00, $00, $00, $00, $00, $00, $00, $00 ; PETSCII code 160 + .byte $08, $08, $08, $0c, $0c, $00, $0c, $00 ; PETSCII code 161 + .byte $6c, $24, $6c, $00, $00, $00, $00, $00 ; PETSCII code 162 + .byte $24, $24, $7e, $24, $7e, $24, $24, $00 ; PETSCII code 163 + .byte $08, $3e, $20, $3e, $06, $3e, $08, $00 ; PETSCII code 164 + .byte $00, $62, $64, $08, $10, $26, $46, $00 ; PETSCII code 165 + .byte $3c, $20, $24, $7e, $64, $64, $7c, $00 ; PETSCII code 166 + .byte $1c, $18, $10, $00, $00, $00, $00, $00 ; PETSCII code 167 + .byte $04, $08, $10, $10, $10, $08, $04, $00 ; PETSCII code 168 + .byte $20, $10, $08, $08, $08, $10, $20, $00 ; PETSCII code 169 + .byte $08, $2a, $1c, $3e, $1c, $2a, $08, $00 ; PETSCII code 170 + .byte $00, $08, $08, $3e, $08, $08, $00, $00 ; PETSCII code 171 + .byte $00, $00, $00, $00, $00, $18, $18, $08 ; PETSCII code 172 + .byte $00, $00, $00, $7e, $00, $00, $00, $00 ; PETSCII code 173 + .byte $00, $00, $00, $00, $00, $18, $18, $00 ; PETSCII code 174 + .byte $00, $02, $04, $08, $10, $20, $40, $00 ; PETSCII code 175 + .byte $7e, $62, $52, $4a, $46, $46, $7e, $00 ; PETSCII code 176 + .byte $38, $08, $08, $18, $18, $1a, $3e, $00 ; PETSCII code 177 + .byte $7e, $42, $02, $7e, $60, $60, $7e, $00 ; PETSCII code 178 + .byte $7c, $44, $04, $1e, $06, $46, $7e, $00 ; PETSCII code 179 + .byte $44, $44, $44, $44, $7e, $0c, $0c, $00 ; PETSCII code 180 + .byte $7e, $40, $7e, $06, $06, $46, $7e, $00 ; PETSCII code 181 + .byte $7e, $42, $40, $7e, $46, $46, $7e, $00 ; PETSCII code 182 + .byte $7e, $02, $02, $06, $06, $06, $06, $00 ; PETSCII code 183 + .byte $3c, $24, $24, $7e, $46, $46, $7e, $00 ; PETSCII code 184 + .byte $7e, $42, $42, $7e, $06, $06, $06, $00 ; PETSCII code 185 + .byte $00, $00, $18, $00, $00, $18, $00, $00 ; PETSCII code 186 + .byte $00, $00, $18, $00, $00, $18, $18, $08 ; PETSCII code 187 + .byte $0e, $18, $30, $60, $30, $18, $0e, $00 ; PETSCII code 188 + .byte $00, $00, $7e, $00, $7e, $00, $00, $00 ; PETSCII code 189 + .byte $70, $18, $0c, $06, $0c, $18, $70, $00 ; PETSCII code 190 + .byte $7e, $02, $02, $7e, $60, $00, $60, $00 ; PETSCII code 191 + .byte $00, $00, $00, $00, $ff, $00, $00, $00 ; PETSCII code 192 + .byte $3c, $24, $24, $7e, $62, $62, $62, $00 ; PETSCII code 193 + .byte $78, $44, $44, $7c, $62, $62, $7e, $00 ; PETSCII code 194 + .byte $7e, $42, $40, $60, $60, $62, $7e, $00 ; PETSCII code 195 + .byte $7c, $46, $42, $62, $62, $66, $7c, $00 ; PETSCII code 196 + .byte $7e, $40, $40, $78, $60, $60, $7e, $00 ; PETSCII code 197 + .byte $7e, $40, $40, $7e, $60, $60, $60, $00 ; PETSCII code 198 + .byte $7e, $42, $40, $6e, $62, $62, $7e, $00 ; PETSCII code 199 + .byte $42, $42, $42, $7e, $62, $62, $62, $00 ; PETSCII code 200 + .byte $08, $08, $08, $0c, $0c, $0c, $0c, $00 ; PETSCII code 201 + .byte $04, $04, $04, $06, $06, $46, $7e, $00 ; PETSCII code 202 + .byte $42, $44, $48, $7c, $62, $62, $62, $00 ; PETSCII code 203 + .byte $40, $40, $40, $60, $60, $60, $7e, $00 ; PETSCII code 204 + .byte $7e, $4a, $4a, $6a, $6a, $6a, $6a, $00 ; PETSCII code 205 + .byte $7e, $42, $42, $62, $62, $62, $62, $00 ; PETSCII code 206 + .byte $7e, $46, $42, $42, $42, $42, $7e, $00 ; PETSCII code 207 + .byte $7e, $42, $42, $7e, $60, $60, $60, $00 ; PETSCII code 208 + .byte $7e, $42, $42, $42, $4a, $4e, $7e, $00 ; PETSCII code 209 + .byte $7c, $44, $44, $7c, $62, $62, $62, $00 ; PETSCII code 210 + .byte $7e, $42, $40, $7e, $06, $46, $7e, $00 ; PETSCII code 211 + .byte $3e, $10, $10, $18, $18, $18, $18, $00 ; PETSCII code 212 + .byte $42, $42, $42, $62, $62, $62, $7e, $00 ; PETSCII code 213 + .byte $62, $62, $62, $66, $24, $24, $3c, $00 ; PETSCII code 214 + .byte $4a, $4a, $4a, $6a, $6a, $6a, $7e, $00 ; PETSCII code 215 + .byte $42, $42, $66, $3c, $66, $62, $62, $00 ; PETSCII code 216 + .byte $22, $22, $22, $3e, $18, $18, $18, $00 ; PETSCII code 217 + .byte $7e, $42, $06, $18, $60, $62, $7e, $00 ; PETSCII code 218 + .byte $08, $08, $08, $08, $ff, $08, $08, $08 ; PETSCII code 219 + .byte $a0, $50, $a0, $50, $a0, $50, $a0, $50 ; PETSCII code 220 + .byte $08, $08, $08, $08, $08, $08, $08, $08 ; PETSCII code 221 + .byte $cc, $cc, $33, $33, $cc, $cc, $33, $33 ; PETSCII code 222 + .byte $cc, $66, $33, $99, $cc, $66, $33, $99 ; PETSCII code 223 + .byte $00, $00, $00, $00, $00, $00, $00, $00 ; PETSCII code 224 + .byte $f0, $f0, $f0, $f0, $f0, $f0, $f0, $f0 ; PETSCII code 225 + .byte $00, $00, $00, $00, $ff, $ff, $ff, $ff ; PETSCII code 226 + .byte $ff, $00, $00, $00, $00, $00, $00, $00 ; PETSCII code 227 + .byte $00, $00, $00, $00, $00, $00, $00, $ff ; PETSCII code 228 + .byte $80, $80, $80, $80, $80, $80, $80, $80 ; PETSCII code 229 + .byte $aa, $55, $aa, $55, $aa, $55, $aa, $55 ; PETSCII code 230 + .byte $01, $01, $01, $01, $01, $01, $01, $01 ; PETSCII code 231 + .byte $00, $00, $00, $00, $aa, $55, $aa, $55 ; PETSCII code 232 + .byte $99, $33, $66, $cc, $99, $33, $66, $cc ; PETSCII code 233 + .byte $03, $03, $03, $03, $03, $03, $03, $03 ; PETSCII code 234 + .byte $08, $08, $08, $08, $0f, $08, $08, $08 ; PETSCII code 235 + .byte $00, $00, $00, $00, $0f, $0f, $0f, $0f ; PETSCII code 236 + .byte $08, $08, $08, $08, $0f, $00, $00, $00 ; PETSCII code 237 + .byte $00, $00, $00, $00, $f8, $08, $08, $08 ; PETSCII code 238 + .byte $00, $00, $00, $00, $00, $00, $ff, $ff ; PETSCII code 239 + .byte $00, $00, $00, $00, $0f, $08, $08, $08 ; PETSCII code 240 + .byte $08, $08, $08, $08, $ff, $00, $00, $00 ; PETSCII code 241 + .byte $00, $00, $00, $00, $ff, $08, $08, $08 ; PETSCII code 242 + .byte $08, $08, $08, $08, $f8, $08, $08, $08 ; PETSCII code 243 + .byte $c0, $c0, $c0, $c0, $c0, $c0, $c0, $c0 ; PETSCII code 244 + .byte $e0, $e0, $e0, $e0, $e0, $e0, $e0, $e0 ; PETSCII code 245 + .byte $07, $07, $07, $07, $07, $07, $07, $07 ; PETSCII code 246 + .byte $ff, $ff, $00, $00, $00, $00, $00, $00 ; PETSCII code 247 + .byte $ff, $ff, $ff, $00, $00, $00, $00, $00 ; PETSCII code 248 + .byte $00, $00, $00, $00, $00, $ff, $ff, $ff ; PETSCII code 249 + .byte $01, $02, $44, $48, $50, $60, $40, $00 ; PETSCII code 250 + .byte $00, $00, $00, $00, $f0, $f0, $f0, $f0 ; PETSCII code 251 + .byte $0f, $0f, $0f, $0f, $00, $00, $00, $00 ; PETSCII code 252 + .byte $08, $08, $08, $08, $f8, $00, $00, $00 ; PETSCII code 253 + .byte $f0, $f0, $f0, $f0, $00, $00, $00, $00 ; PETSCII code 254 + .byte $f0, $f0, $f0, $f0, $0f, $0f, $0f, $0f ; PETSCII code 255 diff --git a/samples/kim1/subs.asm b/samples/kim1/subs.asm new file mode 100644 index 000000000..5b525749e --- /dev/null +++ b/samples/kim1/subs.asm @@ -0,0 +1,1140 @@ +;----------------------------------------------------------------------------------- +; KIMGFX: Simple pixel graphics for the MOS/Commodore KIM-1 +;----------------------------------------------------------------------------------- +; (c) Plummer's Software Ltd, 04/25/2023 Created +; David Plummer +;----------------------------------------------------------------------------------- +; +; File: subs.asm Assembly language subroutines for KIMGFX +; +;----------------------------------------------------------------------------------- + + +.SETCPU "6502" + +.export _ClearScreen +.export _ScrollScreen +.export _SetPixel +.export _ClearPixel +.export _DrawCircle +.export _DrawLine +.export _AscToPet +.export _ReverseBits +.export _DrawChar +.export _CharOut +.export _Demo +.export _Delay +.export _getch + + +.import _font8x8_basic + +; This is the assumed location of the MTU visible memory board's 8K of memory. You can adjust this +; constant to refelct other locations as needed. + +SCREEN = $A000 + +; Note that even though these constants are defined here and respected, there are still going to be +; logic assumptions in GetPixelAddress that assume a 320x200 screen. If you change these, you'll +; need to adjust GetPixelAddress to match. + +SCREEN_WIDTH = 320 +SCREEN_HEIGHT = 200 +SCREEN_BYTES = SCREEN_WIDTH * SCREEN_HEIGHT / 8 +CHARWIDTH = 8 +CHARHEIGHT = 8 +BYTESPERROW = (SCREEN_WIDTH / 8) +BYTESPERCHARROW = (BYTESPERROW * 8) +CHARSPERROW = (SCREEN_WIDTH / CHARWIDTH) +ROWSPERCOLUMN = (SCREEN_HEIGHT / CHARHEIGHT) +LASTROW = SCREEN + SCREEN_BYTES - BYTESPERCHARROW + +.segment "ZEROPAGE" + +btpt: .res 1 + +dest: +dest_lo: .res 1 +dest_hi: .res 1 + +src: +src_lo: .res 1 +src_hi: .res 1 + +adp1: +adp1_lo: .res 1 +adp1_hi: .res 1 + +adp2: +adp2_lo: .res 1 +adp2_hi: .res 1 + +scroll_src: +scroll_src_lo: .res 1 +scroll_src_hi: .res 1 + +scroll_dest: +scroll_dest_lo: .res 1 +scroll_dest_hi: .res 1 + + +.segment "DATA" + +; Arguments for graphics functions + +_x1cord: .res 2 +_x2cord: .res 2 +_y1cord: .res 2 +_y2cord: .res 2 +_cursorX: .res 1 +_cursorY: .res 1 + +; Linedraw + +dx: .res 2 +dy: .res 2 +e2: .res 2 +sx: .res 1 +sy: .res 1 +dltemp: .res 2 +pixel: .res 1 + +; DrawCircle + +xval: .res 2 ; These could move to zeropage for perf, but presume we +yval: .res 2 ; we want to minimize the amount we grow zero page use +err: .res 2 +temp: .res 2 +tempa: .res 1 +tempx: .res 1 +tempy: .res 1 +temp2: .res 2 +x0: .res 2 +y0: .res 2 + +; CharOut + +tempstr: .res 2 + +.export _x1cord ; Make sure these show up on the C side as zero page +.export _x2cord +.export _y1cord +.export _y2cord +.export _cursorX +.export _cursorY + +.segment "CODE" + +;----------------------------------------------------------------------------------- +; GetPixelAddress - Calculate the address of a pixel in the video memory +;----------------------------------------------------------------------------------- +; Based on MTU PIXADR code +;----------------------------------------------------------------------------------- +; In: _x1cord (16-bit) +; _y1cord (16-bit) +; Out: adp1 (16-bit) Address of pixel to set +;----------------------------------------------------------------------------------- + +_GetPixelAddress: + lda _x1cord ; compute bit address first + sta adp1 ; also transfer x1cord to adp1 + and #$07 ; + which is simply the low 3 bits of x + sta btpt + lda _x1cord+1 ; finish transferring x1cord to adp1 + sta adp1+1 + lsr adp1+1 ; double shift adp1 right 3 to get + ror adp1 ; int(xcord/8 ) + lsr adp1+1 + ror adp1 + lsr adp1+1 + ror adp1 + sec ; and temporary storage + lda _y1cord + sta adp2 + sta temp + lda #0 + sbc _y1cord+1 + sta adp2+1 + sta temp+1 + asl adp2 ; compute 40*(y1cord) + rol adp2+1 ; 2*(y1cord) + asl adp2 + rol adp2+1 ; 4*(y1cord) + lda adp2 ; add in temporary save of (y1cord) + clc ; to make 5*(y1cord) + adc temp + sta adp2 + lda adp2+1 + adc temp+1 + sta adp2+1 ; 5*(y1cord) + asl adp2 ; 10*(1cord) + rol adp2+1 + asl adp2 ; 20#(y1cord) + rol adp2+1 + asl adp2 ; 40*(y1cord) + rol adp2+1 + lda adp2 ; add in int(x1cord/8) computed earlier + clc + adc adp1 + sta adp1 + lda adp2+1 + adc adp1+1 + adc #>SCREEN ; add in vmorg*256 + sta adp1+1 ; final result + rts ; return + +;----------------------------------------------------------------------------------- +; Mask tables for individual pixel subroutines +; +; MSKTB1 is a table of 1 bits corresponding to bit numbers +; MSKTB2 is a table of 0 bits corresponding to bit numbers +;----------------------------------------------------------------------------------- + +msktb1: .byte $80,$40,$20,$10,$08,$04,$02,$01 +msktb2: .byte $7F,$BF,$DF,$EF,$F7,$FB,$FD,$FE + +_Delay: pha + sta temp + txa + pha + tya + pha + +@loopa: ldx #$ff +@loopx: ldy #$ff +@loopy: dey + bne @loopy + dex + bne @loopx + dec temp + bne @loopa + + pla + tay + pla + tax + pla + rts + +;----------------------------------------------------------------------------------- +; SetPixel - Set a pixel in the video memory +;----------------------------------------------------------------------------------- +; x - _x1cord (16-bit) +; y - _y1cord (16-bit) +;----------------------------------------------------------------------------------- +; Mask tables for individual pixel subroutines +;----------------------------------------------------------------------------------- + +_SetPixel: jsr _GetPixelAddress + ldy btpt ; get bit number in y + lda msktb1,y ; get a byte with that bit =1, others =0 + ldy #0 + ora (adp1),y ; combine the bit with the addressed vm + sta (adp1),y ; byte + rts + +;----------------------------------------------------------------------------------- +; ClearPixel - Clears a pixel in the video memory +;----------------------------------------------------------------------------------- +; x - _x1cord (16-bit) +; y - _y1cord (16-bit) +;----------------------------------------------------------------------------------- + +_ClearPixel: jsr _GetPixelAddress + ldy btpt ; get bit number in y + lda msktb2,y ; get a byte with that bit =0, others =1 + ldy #0 + and (adp1),y ; remove the bit from the addressed vm + sta (adp1),y ; byte + rts + +;----------------------------------------------------------------------------------- +; ClearScreen - Clears the entire video memory (and thus the screen) +;----------------------------------------------------------------------------------- + +_ClearScreen: + lda #$00 + + ldx #<SCREEN + stx dest_lo + ldx #>SCREEN + stx dest_hi + + ldy #0 +: sta (dest), y ; Loop unwound by a factor of 8, which means our iny before the branchh + iny ; will still work as it's on a page crossing boundary. + sta (dest), y ; This will avoid most of the overhead of the branch. + iny + sta (dest), y + iny + sta (dest), y + iny + sta (dest), y + iny + sta (dest), y + iny + sta (dest), y + iny + sta (dest), y + iny + bne :- + + inc dest_hi + ldx dest_hi + cpx #>SCREEN + $20 + bne :- + + rts + +;----------------------------------------------------------------------------------- +; ScrollScreen - Scrolls the entire video memory (and thus the screen) up one row +;----------------------------------------------------------------------------------- + +BYTES_TO_MOVE = SCREEN_BYTES - BYTESPERCHARROW +PAGES_TO_MOVE = BYTES_TO_MOVE / 256 + +_ScrollScreen: + pha + tya + pha + txa + pha + + ; Load the source (A140) and destination (A000) addresses + lda #<(SCREEN+BYTESPERCHARROW) + sta scroll_src_lo + lda #>(SCREEN+BYTESPERCHARROW) + sta scroll_src_hi + lda #<SCREEN + sta scroll_dest_lo + lda #>SCREEN + sta scroll_dest_hi + + ldx #PAGES_TO_MOVE +@outerLoop: + ldy #0 +@innerLoop: ; + ; I could do this faster in self-modifying code (avoiding the zero page overhead) but then it + ; couldn't go into ROM + + lda (scroll_src),y ; I've unwound the loop to do 8 bytes at a time. Since we're doing full pages + sta (scroll_dest),y ; as long as we unwind the loop to do 8 bytes at a time, we know we'll still + iny ; do the final increment on a page boundary. + lda (scroll_src),y + sta (scroll_dest),y + iny + lda (scroll_src),y + sta (scroll_dest),y + iny + lda (scroll_src),y + sta (scroll_dest),y + iny + lda (scroll_src),y + sta (scroll_dest),y + iny + lda (scroll_src),y + sta (scroll_dest),y + iny + lda (scroll_src),y + sta (scroll_dest),y + iny + lda (scroll_src),y + sta (scroll_dest),y + iny + bne @innerLoop ; If Y overflows, it will be 0, so won't branch + inc scroll_src_hi + inc scroll_dest_hi + dex + bne @outerLoop + + ; Clear the last line + lda #<LASTROW + sta scroll_dest_lo + lda #>LASTROW + sta scroll_dest_hi + lda #$00 + ldy #0 +fullPageLoop: + sta (scroll_dest_lo),y + iny + bne fullPageLoop + inc scroll_dest_hi +partialPageLoop: + sta (scroll_dest_lo),y + iny + cpy #BYTESPERCHARROW - 256 ; Only clear up to the 64th byte (256 + 64 == 320) + bne partialPageLoop + + pla + tax + pla + tay + pla + rts + +;----------------------------------------------------------------------------------- +; DrawCircle - Draws a circle in video memory of a given radius at a given coord +;----------------------------------------------------------------------------------- +; x - _x1cord (16-bit) +; y - _y1cord (16-bit) +; radius - _y2cord (16-bit) +;----------------------------------------------------------------------------------- +; Implements the midpoint circle algorithm without floating point or trig functions +;----------------------------------------------------------------------------------- +; int x = radius; +; int y = 0; +; int err = 0; +; +; while (x >= y) +; { +; SETPIXEL(x0 + x, y0 + y, val); +; SETPIXEL(x0 + y, y0 + x, val); +; SETPIXEL(x0 - y, y0 + x, val); +; SETPIXEL(x0 - x, y0 + y, val); +; SETPIXEL(x0 - x, y0 - y, val); +; SETPIXEL(x0 - y, y0 - x, val); +; SETPIXEL(x0 + y, y0 - x, val); +; SETPIXEL(x0 + x, y0 - y, val); +; +; y++; +; err += 1 + 2 * y; +; if (2 * (err - x) + 1 > 0) { +; x--; +; err += 1 - 2 * x; +; } +; } +;----------------------------------------------------------------------------------- + +_DrawCircle: lda _x1cord ; x0 = _x1cord + sta x0 + lda _x1cord+1 + sta x0+1 + lda _y1cord ; y0 = _y1cord + sta y0 + lda _y1cord+1 + sta y0+1 + + lda _y2cord ; x = radius + sta xval + lda _y2cord+1 + sta xval+1 + + lda #$0 ; yval = 0; + sta yval + sta yval+1 + sta err ; err = 0; + sta err+1 +circleloop: + lda xval+1 ; if (xval < yval) we're done; + sec + cmp yval+1 + bcc doneCircle ; if high byteof yval is greater, we can draw + bne doCircle ; it not greater and not equal, is less, so done + lda xval ; in other cases we need to compare the LSB next + cmp yval + bcs doCircle ; if it's less, but MSB was equal, we go draw + +doneCircle: rts + +doCircle: lda x0 ; Draw the first of 8 symmetric quadrant copies + clc + adc yval + sta _x1cord + lda x0+1 + adc yval+1 + sta _x1cord+1 + lda y0 + sec + sbc xval + sta _y1cord + lda y0+1 + sbc xval+1 + sta _y1cord+1 + jsr _SetPixel ; SETPIXEL(x0 + y, y0 - x, val); + + lda x0 + sec + sbc yval + sta _x1cord + lda x0+1 + sbc yval+1 + sta _x1cord+1 + lda y0 + sec + sbc xval + sta _y1cord + lda y0+1 + sbc xval+1 + sta _y1cord+1 + jsr _SetPixel ; SETPIXEL(x0 - y, y0 - x, val); + + lda x0 + sec + sbc xval + sta _x1cord + lda x0+1 + sbc xval+1 + sta _x1cord+1 + lda y0 + sec + sbc yval + sta _y1cord + lda y0+1 + sbc yval+1 + sta _y1cord+1 + jsr _SetPixel ; SETPIXEL(x0 - x, y0 - y, val); + + lda x0 + sec + sbc xval + sta _x1cord + lda x0+1 + sbc xval+1 + sta _x1cord+1 + lda y0 + clc + adc yval + sta _y1cord + lda y0+1 + adc yval+1 + sta _y1cord+1 + jsr _SetPixel ; SETPIXEL(x0 - x, y0 + y, val); + + lda x0 + clc + adc yval + sta _x1cord + lda x0+1 + adc yval+1 + sta _x1cord+1 + lda y0 + clc + adc xval + sta _y1cord + lda y0+1 + adc xval+1 + sta _y1cord+1 + jsr _SetPixel ; SETPIXEL(x0 + y, y0 + x, val); + + lda x0 + clc + adc xval + sta _x1cord + lda x0+1 + adc xval+1 + sta _x1cord+1 + lda y0 + clc + adc yval + sta _y1cord + lda y0+1 + adc yval+1 + sta _y1cord+1 + jsr _SetPixel ; SETPIXEL(x0 + x, y0 + y, val); + + lda x0 + clc + adc xval + sta _x1cord + lda x0+1 + adc xval+1 + sta _x1cord+1 + lda y0 + sec + sbc yval + sta _y1cord + lda y0+1 + sbc yval+1 + sta _y1cord+1 + jsr _SetPixel ; SETPIXEL(x0 + x, y0 - y, val); + + lda x0 + sec + sbc yval + sta _x1cord + lda x0+1 + sbc yval+1 + sta _x1cord+1 + lda y0 + clc + adc xval + sta _y1cord + lda y0+1 + adc xval+1 + sta _y1cord+1 + jsr _SetPixel ; SETPIXEL(x0 - y, y0 + x, val); + + inc yval ; yval++; + bne :+ + inc yval+1 + +: lda yval ; temp = 2 * yval + 1; + asl + sta temp + lda yval+1 + rol + sta temp+1 + inc temp + bne :+ + inc temp+1 +: + lda err ; err += temp + clc + adc temp + sta err + lda err+1 + adc temp+1 + sta err+1 + ; if (2 * (err - xval) + 1 > 0) then dec xval + lda err ; temp = err-xval + sec + sbc xval + sta temp + lda err+1 + sbc xval+1 + sta temp+1 + + asl temp ; temp = 2*(err-xval)+1 + rol temp+1 + inc temp + bne :+ + inc temp+1 +: + lda temp+1 ; if (temp > 0) we'll dec xval + bmi doneLoop ; less than zero, so no dec + bne decxval ; if not zero, go ahead and dec + + lda temp ; MSB is zero so now check the LSB + beq doneLoop ; both bytes are zero, so no dec + +decxval: lda xval ; xval-- + bne :+ + dec xval+1 +: dec xval + +updateerr: lda xval ; temp = xval * 2 + asl + sta temp + lda xval+1 + rol + sta temp+1 + + lda #1 ; temp2 == 1-temp == 1-(xval*2) + sec + sbc temp + sta temp2 + lda #0 + sbc temp+1 + sta temp2+1 + + lda err ; err += 1-(xval*2) + clc + adc temp2 + sta err + lda err+1 + adc temp2+1 + sta err+1 + +doneLoop: jmp circleloop + +;----------------------------------------------------------------------------------- +; Character set translation tables +;----------------------------------------------------------------------------------- + +ascToPetTable: .byte $00,$01,$02,$03,$04,$05,$06,$07,$14,$20,$0d,$11,$93,$0a,$0e,$0f + .byte $10,$0b,$12,$13,$08,$15,$16,$17,$18,$19,$1a,$1b,$1c,$1d,$1e,$1f + .byte $20,$21,$22,$23,$24,$25,$26,$27,$28,$29,$2a,$2b,$2c,$2d,$2e,$2f + .byte $30,$31,$32,$33,$34,$35,$36,$37,$38,$39,$3a,$3b,$3c,$3d,$3e,$3f + .byte $40,$c1,$c2,$c3,$c4,$c5,$c6,$c7,$c8,$c9,$ca,$cb,$cc,$cd,$ce,$cf + .byte $d0,$d1,$d2,$d3,$d4,$d5,$d6,$d7,$d8,$d9,$da,$5b,$5c,$5d,$5e,$5f + .byte $c0,$41,$42,$43,$44,$45,$46,$47,$48,$49,$4a,$4b,$4c,$4d,$4e,$4f + .byte $50,$51,$52,$53,$54,$55,$56,$57,$58,$59,$5a,$db,$dc,$dd,$de,$df + .byte $80,$81,$82,$83,$84,$85,$86,$87,$88,$89,$8a,$8b,$8c,$8d,$8e,$8f + .byte $90,$91,$92,$0c,$94,$95,$96,$97,$98,$99,$9a,$9b,$9c,$9d,$9e,$9f + .byte $a0,$a1,$a2,$a3,$a4,$a5,$a6,$a7,$a8,$a9,$aa,$ab,$ac,$ad,$ae,$af + .byte $b0,$b1,$b2,$b3,$b4,$b5,$b6,$b7,$b8,$b9,$ba,$bb,$bc,$bd,$be,$bf + .byte $60,$61,$62,$63,$64,$65,$66,$67,$68,$69,$6a,$6b,$6c,$6d,$6e,$6f + .byte $70,$71,$72,$73,$74,$75,$76,$77,$78,$79,$7a,$7b,$7c,$7d,$7e,$7f + .byte $e0,$e1,$e2,$e3,$e4,$e5,$e6,$e7,$e8,$e9,$ea,$eb,$ec,$ed,$ee,$ef + .byte $f0,$f1,$f2,$f3,$f4,$f5,$f6,$f7,$f8,$f9,$fa,$fb,$fc,$fd,$fe,$ff + +; PETSCI to Ascii lookup table - not current used, so commented out, but can be used to map fonts +; +; + petToAscTable: .byte $00,$01,$02,$03,$04,$05,$06,$07,$14,$09,$0d,$11,$93,$0a,$0e,$0f + .byte $10,$0b,$12,$13,$08,$15,$16,$17,$18,$19,$1a,$1b,$1c,$1d,$1e,$1f + .byte $20,$21,$22,$23,$24,$25,$26,$27,$28,$29,$2a,$2b,$2c,$2d,$2e,$2f + .byte $30,$31,$32,$33,$34,$35,$36,$37,$38,$39,$3a,$3b,$3c,$3d,$3e,$3f + .byte $40,$61,$62,$63,$64,$65,$66,$67,$68,$69,$6a,$6b,$6c,$6d,$6e,$6f + .byte $70,$71,$72,$73,$74,$75,$76,$77,$78,$79,$7a,$5b,$5c,$5d,$5e,$5f + .byte $c0,$c1,$c2,$c3,$c4,$c5,$c6,$c7,$c8,$c9,$ca,$cb,$cc,$cd,$ce,$cf + .byte $d0,$d1,$d2,$d3,$d4,$d5,$d6,$d7,$d8,$d9,$da,$db,$dc,$dd,$de,$df + .byte $80,$81,$82,$83,$84,$85,$86,$87,$88,$89,$8a,$8b,$8c,$8d,$8e,$8f + .byte $90,$91,$92,$0c,$94,$95,$96,$97,$98,$99,$9a,$9b,$9c,$9d,$9e,$9f + .byte $a0,$a1,$a2,$a3,$a4,$a5,$a6,$a7,$a8,$a9,$aa,$ab,$ac,$ad,$ae,$af + .byte $b0,$b1,$b2,$b3,$b4,$b5,$b6,$b7,$b8,$b9,$ba,$bb,$bc,$bd,$be,$bf + .byte $60,$41,$42,$43,$44,$45,$46,$47,$48,$49,$4a,$4b,$4c,$4d,$4e,$4f + .byte $50,$51,$52,$53,$54,$55,$56,$57,$58,$59,$5a,$7b,$7c,$7d,$7e,$7f + .byte $a0,$a1,$a2,$a3,$a4,$a5,$a6,$a7,$a8,$a9,$aa,$ab,$ac,$ad,$ae,$af + .byte $b0,$b1,$b2,$b3,$b4,$b5,$b6,$b7,$b8,$b9,$ba,$bb,$bc,$bd,$be,$bf + +;----------------------------------------------------------------------------------- +; PetToAsc - Convert a PETSCII character to ASCII +;----------------------------------------------------------------------------------- +; A - Character to convert +;----------------------------------------------------------------------------------- + +_AscToPet: tay + lda ascToPetTable, y + rts + +_PetToAsc: tay + lda petToAscTable, Y + rts + +;----------------------------------------------------------------------------------- +; ReverseBits - Reverse the bits in a byte +;----------------------------------------------------------------------------------- +; A = octet to be reversed +;----------------------------------------------------------------------------------- + +_ReverseBits: + ldx #8 ; set counter to 8 (for 8 bits) + lda #0 ; initialize A to 0 + sta temp ; clear result byte (accumulator for the reversed octet) +: asl ; shift leftmost bit of input into carry + lda temp ; load the temporary result byte into A + ror ; rotate carry into leftmost bit of result + sta temp ; store the updated result back into memory + dex ; decrement counter + bne :- ; repeat until all bits are processed + lda temp ; load the final reversed byte into A + rts ; return with result in A + +;----------------------------------------------------------------------------------- +; LoadFont - Makes sure the font data is ready to use. This usually requires +; reversing the bits so that they match the bit order of the screen +;----------------------------------------------------------------------------------- + +_LoadFont: ldx #3 + lda #<_font8x8_basic + sta adp1_lo + lda #>_font8x8_basic + sta adp1_hi + ldy #0 +@loop: lda (adp1), y + jsr _ReverseBits + sta (adp1), y + iny + bne @loop + + inc adp1_lo + bne :+ + inc adp1_hi +: dex + bne @loop + rts + +ScreenLineAddresses: + .word SCREEN + 0 * BYTESPERCHARROW, SCREEN + 1 * BYTESPERCHARROW + .word SCREEN + 2 * BYTESPERCHARROW, SCREEN + 3 * BYTESPERCHARROW + .word SCREEN + 4 * BYTESPERCHARROW, SCREEN + 5 * BYTESPERCHARROW + .word SCREEN + 6 * BYTESPERCHARROW, SCREEN + 7 * BYTESPERCHARROW + .word SCREEN + 8 * BYTESPERCHARROW, SCREEN + 9 * BYTESPERCHARROW + .word SCREEN + 10 * BYTESPERCHARROW, SCREEN + 11 * BYTESPERCHARROW + .word SCREEN + 12 * BYTESPERCHARROW, SCREEN + 13 * BYTESPERCHARROW + .word SCREEN + 14 * BYTESPERCHARROW, SCREEN + 15 * BYTESPERCHARROW + .word SCREEN + 16 * BYTESPERCHARROW, SCREEN + 17 * BYTESPERCHARROW + .word SCREEN + 18 * BYTESPERCHARROW, SCREEN + 19 * BYTESPERCHARROW + .word SCREEN + 20 * BYTESPERCHARROW, SCREEN + 21 * BYTESPERCHARROW + .word SCREEN + 22 * BYTESPERCHARROW, SCREEN + 23 * BYTESPERCHARROW + .word SCREEN + 24 * BYTESPERCHARROW + .assert( (* - ScreenLineAddresses) = ROWSPERCOLUMN * 2), error + +;----------------------------------------------------------------------------------- +; DrawChar - Draws an ASCII character at char location x, y +;----------------------------------------------------------------------------------- +; 0 <= x < 40 +; 0 <= y < 25 +; Preserves all registers, but its not very threadsafe or reentrant +;----------------------------------------------------------------------------------- + +_DrawChar: sty tempy + stx tempx + sta tempa + + tya ; Get the address in screen memory where this + asl ; character X/Y cursor pos should be drawn + tay + txa + clc + adc ScreenLineAddresses, y + sta dest_lo + lda ScreenLineAddresses+1, y + adc #0 + sta dest_hi + + lda #0 ; Get the address in font memory where this + sta src_hi ; Petscii chracter lives (after conversion from + lda tempa ; ascii) + + sty temp2 + jsr _AscToPet + ldy temp2 + + asl + rol src_hi + asl + rol src_hi + asl + rol src_hi + clc + adc #<_font8x8_basic ; Add the base address of the font table to the offset + sta src_lo + lda src_hi + adc #>_font8x8_basic + sta src_hi + + ldy #0 ; opy the character def to the screen, one byte at a time + ldx #0 +: lda (src), y ; Copy this byte from the character def to the screen target + sta (dest, x) + lda dest_lo ; Advance to the next "scanline", or pixel row, down + clc + adc #<BYTESPERROW + sta dest_lo + lda dest_hi + adc #>BYTESPERROW + sta dest_hi + + iny + cpy #8 + bne :- + + ldy tempy + ldx tempx + lda tempa + rts + +;----------------------------------------------------------------------------------- +; CursorOn - Turns on the text cursor and draws it at the current cursor pos +;----------------------------------------------------------------------------------- + +CursorOn: ldx _cursorX + ldy _cursorY + lda #'_' + jsr _DrawChar + rts + +CursorOff: ldx _cursorX + ldy _cursorY + lda #' ' + jsr _DrawChar + rts + +;----------------------------------------------------------------------------------- +; DrawText - Draws an ASCII char in A at the current cursor pos, saves all regs +;----------------------------------------------------------------------------------- + +_CharOut: sta temp + lda #0 + sta temp+1 + txa + pha + tya + pha + + ldx #<temp + ldy #>temp + jsr _DrawText + + pla + tay + pla + tax + rts + +;----------------------------------------------------------------------------------- +; Backspace - Erase the current character and move back one position. Does not +; move back up to previous line +;----------------------------------------------------------------------------------- + +Backspace: lda _cursorX + beq colzero + jsr CursorOff + dec _cursorX + jsr CursorOn +colzero: rts + +;----------------------------------------------------------------------------------- +; DrawText - Draws an ASCII string at the current cursor position +;----------------------------------------------------------------------------------- +; XY - Pointer to the string to draw, stops on NUL or 255 chars later +;----------------------------------------------------------------------------------- + +_DrawText: stx adp1_lo + sty adp1_hi + jsr CursorOff + + ldy #0 +checkHWrap: lda _cursorX + cmp #CHARSPERROW + bcc checkVWrap + lda #0 + sta _cursorX + inc _cursorY + +checkVWrap: lda _cursorY + cmp #ROWSPERCOLUMN + bcc loadChar + jsr _ScrollScreen + lda #ROWSPERCOLUMN-1 + sta _cursorY + +loadChar: lda (adp1), y + beq doneText + + cmp #$0a + bne :+ + + lda #0 ; Back to the left edge + sta _cursorX + inc _cursorY ; Advance to the next line + iny + bne checkHWrap + +: sty temp + ldx _cursorX + ldy _cursorY + jsr _DrawChar + ldy temp + inc _cursorX + iny + bne checkHWrap + +doneText: jsr CursorOn + rts + +demoText1: .byte " *** COMMODORE KIM-1 SHELL V0.1 ***", $0A, $0A + .byte " 60K RAM SYSTEM. 49152 BYTES FREE.", $0A, $0A, $00 +readyText: .byte $0A,"READY.", $0A, 00 + +_Demo: jsr _ClearScreen + lda #0 + sta _cursorX + sta _cursorY + ldx #<demoText1 + ldy #>demoText1 + jsr _DrawText + rts + +_Ready: ldx #<readyText + ldy #>readyText + jsr _DrawText + rts + + +;----------------------------------------------------------------------------------- +; DrawLine - Draws a line between two points +;----------------------------------------------------------------------------------- +; _x1cord (16-bit) +; _y1cord ( 8-bit) +; _x2cord (16-bit) +; _y2cord ( 8-bit) +;----------------------------------------------------------------------------------- +; Implements something like Bresenham's algorithm for drawing a line: +;----------------------------------------------------------------------------------- +; void DrawLine(int x0, int y0, int x1, int y1, byte val) +; { +; int dx = abs(_x2cord - _x1cord), sx = _x1cord < _x2cord ? 1 : -1; +; int dy = abs(_y2cord - _y1cord), sy = _y1cord < _y2cord ? 1 : -1; +; int err = (dx > dy ? dx : -dy) / 2, e2; +; +; while (1) +; { +; SETPIXEL(_x1cord, _y1cord, val); +; +; if (_x1cord == _x2cord && _y1cord == _y2cord) +; break; +; +; e2 = err; +; +; if (e2 > -dx) +; { +; err -= dy; +; _x1cord += sx; +; } +; if (e2 < dy) +; { +; err += dx; +; _y1cord += sy; +; } +; } +; } +;----------------------------------------------------------------------------------- + +_DrawLine: sta pixel + + ldx #$01 ; positive x-step for now + stx sx + + ; Calculate dx = (x2cord - X1cord) and see if its positive or not + + lda _x2cord ; Calculate dx = (x2cord - X1cord) + sec + sbc _x1cord + sta dx + lda _x2cord+1 + sbc _x1cord+1 + sta dx+1 + bpl calcdy ; dx is positive (dx >= 0), so we're good + + ; dx was negative (dx < 0), so we set sx to -1 and get the absolute + ; value by subtracting the other direction + + ldx #$FF ; negative x-step + stx sx + lda _x1cord ; Calculate dx = (x2cord - X1cord) + sec + sbc _x2cord + sta dx + lda _x1cord+1 + sbc _x2cord+1 + sta dx+1 + + ; Calculate dy = (y2cord - y1cord) and see if its positive or not + +calcdy: ldx #$01 ; positive y-step for now + stx sy + lda _y2cord + sec + sbc _y1cord + sta dy + bcs positivedy ; If y2cord > y1cord, then dy is positive and we're good + + ; dy was negative (dy < 0), so we set sy to -1 and get the absolute value + + ldx #$FF ; negative y-step + stx sy + lda _y1cord + sec + sbc _y2cord + sta dy + + ; Now we have dx and dy, so we can calculate err, but first we need + ; to see if dx > dy or not + +positivedy: lda dx+1 ; Check if dx > dy (both are always positive now) + bne dxgt ; If MSB of dx is greater than zero, then dx > dy since dy is 8-bits + lda dy + cmp dx + bcs dygte + +dxgt: lda dx ; We found dx>dy so set err = dx / 2 + sta err + lda dx+1 + lsr + sta err+1 ; err = dx/2 + ror err + jmp loop + +dygte: lda #0 ; we found dx <= dy so set err = -dy / 2 + sec + sbc dy ; else err = -dy / 2 + ror + ora #$80 + sta err + lda #$FF + sta err+1 + + ; Now we have dx, dy, and err, so we can start drawing pixels + +loop: lda pixel + beq clearpixel + jsr _SetPixel ; Plot the current _x1cord, _y1cord + jmp next +clearpixel: jsr _ClearPixel ; Clear the current _x1cord, _y1cord + +next: lda _x1cord ; if (_x1cord == _x2cord && _y1cord == _y2cord) then we rts + cmp _x2cord + bne noteq + lda _y1cord + cmp _y2cord + bne noteq + lda _x1cord+1 + cmp _x2cord+1 + bne noteq + + rts + +noteq: lda err ; e2 = err + sta e2 + lda err+1 + sta e2+1 + + ; Check the two update conditions for x and y, and update if needed + + lda e2 ; if (e2 > -dx) is the same as if (e2 + dx > 0), so we test that because its easier + clc ; If its true then we dec err and inc _x1cord + adc dx + sta temp + lda e2+1 + adc dx+1 + bmi doneupdatex ; If result is negative, then e2 + dx < 0, so we don't dec err or inc _x1cord + bne stepx ; If MSB is non-zero, then e2 + dx > 0, so we DO dec err and inc _x1cord + lda temp ; If result is zero in MSB, then we check the LSB here + beq doneupdatex ; If LSB is zero, then we don't dec err or inc _x1cord + ; We already know e2 + dx > 0, so LSB can't be negative +stepx: lda sx + bmi decx +incxval: inc _x1cord ; _x1cord += 1 because sx == 1 + bne updatexerr + inc _x1cord+1 + jmp updatexerr + +decx: lda _x1cord ; _x1cord += 1 because sx == 1 + sec + sbc #1 + sta _x1cord + lda _x1cord+1 + sbc #0 + sta _x1cord+1 + +updatexerr: lda err ; err -= dy + sec + sbc dy + sta err + lda err+1 + sbc #0 + sta err+1 + +doneupdatex: lda e2+1 ; if (e2 < dy) then we inc err and inc _y1cord + bmi updateerry ; If MSB is negative, then e2 < dy, so we inc err and inc _y1cord + bne noupdatey ; If the MSB of e2 is set and positive, then we know e2 > dy, so we don't inc err or inc _y1cord + lda e2 + sec + sbc dy + beq noupdatey ; e2 - dy == 0 so we don't inc err or inc _y1cord + bcs noupdatey ; if e2 was large enough that carry never cleared, then e2 > dy do no update + +updateerry: lda err ; err += dx + clc + adc dx + sta err + lda err+1 + adc dx+1 + sta err+1 + +stepy: lda _y1cord + clc + adc sy + sta _y1cord + +noupdatey: jmp loop + +_getch: jsr $1E5A ; Get character using Monitor ROM call + and #$7F ; Clear top bit + cmp #$0D ; Check for '\r' + bne gotch ; ...if CR character + lda #$0A ; Replace with '\n' +gotch: rts diff --git a/samples/lynx/mandelbrot.c b/samples/lynx/mandelbrot.c index ce49fbf7a..27ac7d340 100644 --- a/samples/lynx/mandelbrot.c +++ b/samples/lynx/mandelbrot.c @@ -26,7 +26,7 @@ #define divfp(_a,_b) ((((signed long)_a)<<fpshift)/(_b)) /* Use static local variables for speed */ -#pragma static-locals (1); +#pragma static-locals (1) diff --git a/samples/mandelbrot.c b/samples/mandelbrot.c index 519f3d823..f4e7b83a8 100644 --- a/samples/mandelbrot.c +++ b/samples/mandelbrot.c @@ -41,7 +41,7 @@ #endif /* Use static local variables for speed */ -#pragma static-locals (1); +#pragma static-locals (1) diff --git a/samples/multidemo.c b/samples/multidemo.c index 02bb6fcac..435c1b985 100644 --- a/samples/multidemo.c +++ b/samples/multidemo.c @@ -64,34 +64,34 @@ void log (char *msg) ** rather place all the code of certain source files into the overlay by ** compiling them with --code-name OVERLAY1. */ -#pragma code-name (push, "OVERLAY1"); +#pragma code-name (push, "OVERLAY1") void foo (void) { log ("Calling main from overlay 1"); } -#pragma code-name (pop); +#pragma code-name (pop) -#pragma code-name (push, "OVERLAY2"); +#pragma code-name (push, "OVERLAY2") void bar (void) { log ("Calling main from overlay 2"); } -#pragma code-name (pop); +#pragma code-name (pop) -#pragma code-name (push, "OVERLAY3"); +#pragma code-name (push, "OVERLAY3") void foobar (void) { log ("Calling main from overlay 3"); } -#pragma code-name(pop); +#pragma code-name(pop) unsigned char loademdriver (void) diff --git a/samples/overlaydemo.c b/samples/overlaydemo.c index dde6b70ab..a37b06d4f 100644 --- a/samples/overlaydemo.c +++ b/samples/overlaydemo.c @@ -39,7 +39,7 @@ void log (char *msg) ** rather place all the code of certain source files into the overlay by ** compiling them with --code-name OVERLAY1. */ -#pragma code-name (push, "OVERLAY1"); +#pragma code-name (push, "OVERLAY1") void foo (void) { @@ -51,27 +51,27 @@ void foo (void) log ("Calling main from overlay 1"); } -#pragma code-name (pop); +#pragma code-name (pop) -#pragma code-name (push, "OVERLAY2"); +#pragma code-name (push, "OVERLAY2") void bar (void) { log ("Calling main from overlay 2"); } -#pragma code-name (pop); +#pragma code-name (pop) -#pragma code-name (push, "OVERLAY3"); +#pragma code-name (push, "OVERLAY3") void foobar (void) { log ("Calling main from overlay 3"); } -#pragma code-name(pop); +#pragma code-name(pop) unsigned char loadfile (char *name, void *addr, void *size) diff --git a/samples/sieve.c b/samples/sieve.c index 8d0619888..7c3b9cd75 100644 --- a/samples/sieve.c +++ b/samples/sieve.c @@ -38,7 +38,7 @@ static unsigned char Sieve[COUNT]; -#pragma static-locals(1); +#pragma static-locals(1) diff --git a/samples/sim65/Makefile b/samples/sim65/Makefile new file mode 100644 index 000000000..865594736 --- /dev/null +++ b/samples/sim65/Makefile @@ -0,0 +1,61 @@ + +# Run 'make SYS=<target>'; or, set a SYS env. +# var. to build for another target system. +SYS ?= sim6502 + +# Just the usual way to find out if we're +# using cmd.exe to execute make rules. +ifneq ($(shell echo),) + CMD_EXE = 1 +endif + +ifdef CMD_EXE + NULLDEV = nul: + DEL = -del /f + RMDIR = rmdir /s /q +else + NULLDEV = /dev/null + DEL = $(RM) + RMDIR = $(RM) -r +endif + +ifdef CC65_HOME + AS = $(CC65_HOME)/bin/ca65 + CC = $(CC65_HOME)/bin/cc65 + CL = $(CC65_HOME)/bin/cl65 + LD = $(CC65_HOME)/bin/ld65 +else + AS := $(if $(wildcard ../../bin/ca65*),../../bin/ca65,ca65) + CC := $(if $(wildcard ../../bin/cc65*),../../bin/cc65,cc65) + CL := $(if $(wildcard ../../bin/cl65*),../../bin/cl65,cl65) + LD := $(if $(wildcard ../../bin/ld65*),../../bin/ld65,ld65) +endif + +EXELIST_sim6502 = \ + cpumode_example.bin \ + timer_example.bin \ + trace_example.bin + +ifneq ($(EXELIST_$(SYS)),) +samples: $(EXELIST_$(SYS)) +else +samples: notavailable +endif + +# empty target used to skip systems that will not work with any program in this dir +notavailable: +ifeq ($(MAKELEVEL),0) + @echo "info: sim65 tests not available for" $(SYS) +else +# suppress the "nothing to be done for 'samples' message + @echo > $(NULLDEV) +endif + +.SUFFIXES: +.SUFFIXES: .c .bin + +%.bin : %.c + $(CL) -t $(SYS) -Oris -m $*.map -o $@ $< + +clean: + @$(DEL) *.o *.map *.bin 2>$(NULLDEV) diff --git a/samples/sim65/cpumode_example.c b/samples/sim65/cpumode_example.c new file mode 100644 index 000000000..6cc2d0c16 --- /dev/null +++ b/samples/sim65/cpumode_example.c @@ -0,0 +1,104 @@ +/* + * Sim65 cpu-mode switching example. + * + * Description + * ----------- + * + * We can inspect and manipulate the CPU model that sim65 emulates at runtime. + * + * Sim65 always runs in one of three modes: + * + * - 6502 mode: the 151 documented opcodes are supported; if the processor encounters + * one of the 105 undocumented opcodes, the simulator ends with an + * 'illegal opcode' message. + * - 65C02 mode: the 105 undocumented opcodes now have well-defined behavior. Some + * do useful things, while all others are now defined as NOPs. + * - 6502X mode: the 105 undocumented opcodes don't have documented behavior, but + * they /do/ have behavior on a real 6502. This behavior has been + * figured out, and is deterministic (with minor exceptions). + * In this mode, sim65 mimics the behavior of a real 6502 when + * it encounters an undocumented opcode, rather than terminating. + * + * In the example below, we first switch to 6502X mode and execute a small + * assembly code fragment, then repeat this in 65C02 mode. + * + * The code fragment is designed to distinguish between a 6502 and a 65C02 + * processor based on the behavior of the ADC function in decimal mode. + * + * Important Note: + * + * When running in a program compiled for the "sim6502" target, it is safe to switch to + * 65C02 or 6502X mode, since the runtime library will only use plain 6502 opcodes, and + * those work the same in 65C02 and 6502X mode. + * + * However, when running in a program compiled for the "sim65c02" target, it is NOT safe + * to switch to 6502 or 6502X mode, since many routines in the runtime library use + * 65C02-specific opcodes, and these will not work as expected when the CPU is switched + * to 6502 or 6502X mode. When such an instruction is encountered, the program will + * exhibit undefined behavior. + * + * For this reason, this program will only work when compiled for the "sim6502" target. + * + * Running the example + * ------------------- + * + * cl65 -t sim6502 -O cpumode_example.c -o cpumode_example.prg + * sim65 cpumode_example.prg + * + */ + +#include <stdio.h> +#include <stdbool.h> +#include <sim65.h> + +static bool __fastcall__ is_65c02(void) +{ + /* This assembly routine loads 0 into AX on a 6502 (also on a 6502 on which decimal + * mode is not implemented), and 1 on a 65C02. + * + * Note: this implementation triggers a "control reaches end of non-void function" + * warning that can be safely ignored. While no return statement is present, the + * return value is correctly loaded into AX by the assembly code. + */ + __asm__("sed"); + __asm__("ldx #0"); + __asm__("txa"); + __asm__("sbc #155"); + __asm__("asl"); + __asm__("rol"); + __asm__("and #1"); + __asm__("cld"); +} + +int main(void) +{ + printf("CPU mode at startup ....... : %u\n", GET_CPU_MODE()); + printf("Is 65C02? ................. : %s\n", is_65c02() ? "YES" : "NO"); + + printf("\n"); + + printf("Switching to 6502 mode ....\n"); + SET_CPU_MODE(SIM65_CPU_MODE_6502); + printf("Current CPU mode .......... : %u\n", GET_CPU_MODE()); + printf("Is 65C02? ................. : %s\n", is_65c02() ? "YES" : "NO"); + + printf("\n"); + + printf("Switching to 65C02 mode ...\n"); + SET_CPU_MODE(SIM65_CPU_MODE_65C02); + printf("Current CPU mode .......... : %u\n", GET_CPU_MODE()); + printf("Is 65C02? ................. : %s\n", is_65c02() ? "YES" : "NO"); + + printf("\n"); + + printf("Switching to 6502X mode ...\n"); + SET_CPU_MODE(SIM65_CPU_MODE_6502X); + printf("Current CPU mode .......... : %u\n", GET_CPU_MODE()); + printf("Is 65C02? ................. : %s\n", is_65c02() ? "YES" : "NO"); + + printf("\n"); + + printf("Bye!\n"); + + return 0; +} diff --git a/samples/sim65/timer_example.c b/samples/sim65/timer_example.c new file mode 100644 index 000000000..8262bdff8 --- /dev/null +++ b/samples/sim65/timer_example.c @@ -0,0 +1,117 @@ +/* + * Sim65 timer example. + * + * Description + * ----------- + * + * This example tests the clock cycle counter feature of sim65. + * + * The function 'timestamp' obtains the lower 32-bits of the clock cycle counter. + * + * The function 'calc_sum_terms' calculates the sum of a range of integers + * starting at zero. It simply iterates over all terms, which means that its + * runtime is a linear function of its input value. + * + * In the main function, we first derive an 'offset' value by getting two timestamp + * values, with nothing happening in between. Ideally this should yield a 0 clock + * cycle duration, but due to the overhead of calling the 'timestamp' function, + * and the 'timestamp' function itself, the difference between these timestamp + * will be non-zero. We store this value in the 'overhead' variable, and subtract + * this value in later measurements. + * + * Next, we measure the duration of calling the function 'calc_sum_terms' with two + * input values, 0, and 1. The duration includes storing the result in the 'result' + * variable. + * + * Extrapolating from these two measurements, and assuming that the runtime of + * calling 'calc_sum_terms' and storing its result scales linearly with its argument, + * we can predict the duration of a call to 'calc_sum_terms' with a much larger + * argument (max_terms = 10000). + * + * Finally, we actually measure the duration with max_terms = 10000. If the + * duration measured is equal to the predicted value, we exit successfully. If not, + * we exit with failure. + * + * Running the example + * ------------------- + * + * cl65 -t sim6502 -O timer_example.c -o timer_example.prg + * sim65 timer_example.prg + * + */ + +#include <stdio.h> +#include <sim65.h> + +static uint32_t timestamp(void) +{ + peripherals.counter.select = COUNTER_SELECT_CLOCKCYCLE_COUNTER; + peripherals.counter.latch = 0; + return peripherals.counter.value32[0]; +} + +static unsigned long calc_sum_terms(unsigned max_term) +/* A function with a runtime that scales linearly with its argument. */ +{ + unsigned k; + unsigned long sum = 0; + for (k = 0; k <= max_term; ++k) + { + sum += k; + } + return sum; +} + +int main(void) +{ + unsigned max_term; + unsigned long result; + uint32_t t1, t2, overhead; + int32_t d0, d1, duration; + int32_t predicted_duration; + + /* Calibration measurement of zero clock cycles, to determine the overhead. */ + + overhead = 0; + t1 = timestamp(); + t2 = timestamp() - overhead; + overhead = (t2 - t1); + + /* Calculate call duration (including assignment of result) for argument value 0. */ + + max_term = 0; + t1 = timestamp(); + result = calc_sum_terms(max_term); + t2 = timestamp(); + d0 = (t2 - t1) - overhead; + printf("max_term = %u -> result = %lu; duration = %ld\n", max_term, result, d0); + + /* Calculate call duration (including assignment of result) for argument value 1. */ + + max_term = 1; + t1 = timestamp(); + result = calc_sum_terms(max_term); + t2 = timestamp(); + d1 = (t2 - t1) - overhead; + printf("max_term = %u -> result = %lu; duration = %ld\n", max_term, result, d1); + + /* Predict runtime for a much bigger argument value, 10000. */ + + max_term = 10000; + predicted_duration = d0 + max_term * (d1 - d0); + + printf("predicted duration for max_term = %u: %ld\n", max_term, predicted_duration); + + /* Do the actual measurement for max_term = 10000. + * Note: equality between the prediction and the measurement is only achieved if we compile with -O. + */ + + t1 = timestamp(); + result = calc_sum_terms(max_term); + t2 = timestamp(); + duration = (t2 - t1) - overhead; + printf("max_term = %u -> result = %lu; duration = %ld\n", max_term, result, duration); + + + return 0; +} diff --git a/samples/sim65/trace_example.c b/samples/sim65/trace_example.c new file mode 100644 index 000000000..cd4ec87d7 --- /dev/null +++ b/samples/sim65/trace_example.c @@ -0,0 +1,40 @@ +/* + * Sim65 trace functionailty example. + * + * Description + * ----------- + * + * The easiest way to use tracing in sim65 is to pass the '--trace' option + * to sim65 while starting a program. + * + * However, it is also possiblke to enable and disable the trace functionality + * at runtime, from within the C code itself. This can be useful to produce + * runtime traces of small code fragments for debugging purposes. + * + * In this example, We use the TRACE_ON and TRACE_OFF macros provided in sim65.h + * to trace what the CPU is doing during a single statement: the assignment of + * a constant to a global variable. + * + * Running the example + * ------------------- + * + * cl65 -t sim6502 -O trace_example.c -o trace_example.prg + * sim65 trace_example.prg + * + * Compiling and running the program like this will produce a trace of six 6502 instructions. + * The first four instructions correspond to the 'x = 0x1234' assignment statement. + * The last two instructions (ending in a store to address $FFCB) disable the trace facility. + * + */ + +#include <sim65.h> + +unsigned x; + +int main(void) +{ + TRACE_ON(); + x = 0x1234; + TRACE_OFF(); + return 0; +} diff --git a/src/ar65.vcxproj b/src/ar65.vcxproj index 27d12dadc..0fd788e06 100644 --- a/src/ar65.vcxproj +++ b/src/ar65.vcxproj @@ -5,10 +5,18 @@ <Configuration>Debug</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> <ProjectConfiguration Include="Release|Win32"> <Configuration>Release</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> </ItemGroup> <PropertyGroup Label="Globals"> <ProjectGuid>{5E8C19C6-B167-440C-8BEF-3CBF109CDB49}</ProjectGuid> @@ -21,15 +29,22 @@ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <UseDebugLibraries>true</UseDebugLibraries> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <UseDebugLibraries>true</UseDebugLibraries> + </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> <UseDebugLibraries>false</UseDebugLibraries> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <UseDebugLibraries>false</UseDebugLibraries> + </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <ImportGroup Label="ExtensionSettings"> </ImportGroup> <PropertyGroup Label="UserMacros" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> </PropertyGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> @@ -40,6 +55,15 @@ <SubSystem>Console</SubSystem> </Link> </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <PreprocessorDefinitions>_CONSOLE;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <DisableSpecificWarnings>4267;%(DisableSpecificWarnings)</DisableSpecificWarnings> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + </Link> + </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ClCompile> <PreprocessorDefinitions>_CONSOLE;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> @@ -48,6 +72,15 @@ <SubSystem>Console</SubSystem> </Link> </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <PreprocessorDefinitions>_CONSOLE;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <DisableSpecificWarnings>4267;%(DisableSpecificWarnings)</DisableSpecificWarnings> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + </Link> + </ItemDefinitionGroup> <ItemGroup> <ClInclude Include="ar65\add.h" /> <ClInclude Include="ar65\del.h" /> diff --git a/src/ca65.vcxproj b/src/ca65.vcxproj index 3cc6019f2..d8fd39303 100644 --- a/src/ca65.vcxproj +++ b/src/ca65.vcxproj @@ -5,10 +5,18 @@ <Configuration>Debug</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> <ProjectConfiguration Include="Release|Win32"> <Configuration>Release</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> </ItemGroup> <PropertyGroup Label="Globals"> <ProjectGuid>{D28CB737-E6CA-49C4-8CE9-FF05F86DD4EC}</ProjectGuid> @@ -21,15 +29,22 @@ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <UseDebugLibraries>true</UseDebugLibraries> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <UseDebugLibraries>true</UseDebugLibraries> + </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> <UseDebugLibraries>false</UseDebugLibraries> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <UseDebugLibraries>false</UseDebugLibraries> + </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <ImportGroup Label="ExtensionSettings"> </ImportGroup> <PropertyGroup Label="UserMacros" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> </PropertyGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> @@ -40,6 +55,15 @@ <SubSystem>Console</SubSystem> </Link> </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <PreprocessorDefinitions>_CONSOLE;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <DisableSpecificWarnings>4244;4267;%(DisableSpecificWarnings)</DisableSpecificWarnings> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + </Link> + </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ClCompile> <PreprocessorDefinitions>_CONSOLE;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> @@ -48,6 +72,15 @@ <SubSystem>Console</SubSystem> </Link> </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <PreprocessorDefinitions>_CONSOLE;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <DisableSpecificWarnings>4244;4267;%(DisableSpecificWarnings)</DisableSpecificWarnings> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + </Link> + </ItemDefinitionGroup> <ItemGroup> <ClInclude Include="ca65\anonname.h" /> <ClInclude Include="ca65\asserts.h" /> diff --git a/src/ca65/condasm.c b/src/ca65/condasm.c index 6198f4017..f872ec9ed 100644 --- a/src/ca65/condasm.c +++ b/src/ca65/condasm.c @@ -133,24 +133,26 @@ static void SetIfCond (IfDesc* ID, int C) -static void ElseClause (IfDesc* ID, const char* Directive) -/* Enter an .ELSE clause */ +static int ElseClause (IfDesc* ID, const char* Directive) +/* Enter an .ELSE clause. Return true if this was ok, zero on errors. */ { /* Check if we have an open .IF - otherwise .ELSE is not allowed */ if (ID == 0) { Error ("Unexpected %s", Directive); - return; + return 0; } /* Check for a duplicate else, then remember that we had one */ if (ID->Flags & ifElse) { /* We already had a .ELSE ! */ Error ("Duplicate .ELSE"); + return 0; } ID->Flags |= ifElse; /* Condition is inverted now */ ID->Flags ^= ifCond; + return 1; } @@ -226,46 +228,52 @@ void DoConditionals (void) D = GetCurrentIf (); /* Allow an .ELSE */ - ElseClause (D, ".ELSE"); + if (ElseClause (D, ".ELSE")) { + /* Remember the data for the .ELSE */ + if (D) { + ReleaseFullLineInfo (&D->LineInfos); + GetFullLineInfo (&D->LineInfos); + D->Name = ".ELSE"; + } - /* Remember the data for the .ELSE */ - if (D) { - ReleaseFullLineInfo (&D->LineInfos); - GetFullLineInfo (&D->LineInfos); - D->Name = ".ELSE"; + /* Calculate the new overall condition */ + CalcOverallIfCond (); + + /* Skip .ELSE */ + NextTok (); + ExpectSep (); + } else { + /* Problem with .ELSE, ignore remainder of line */ + SkipUntilSep (); } - - /* Calculate the new overall condition */ - CalcOverallIfCond (); - - /* Skip .ELSE */ - NextTok (); - ExpectSep (); break; case TOK_ELSEIF: D = GetCurrentIf (); /* Handle as if there was an .ELSE first */ - ElseClause (D, ".ELSEIF"); + if (ElseClause (D, ".ELSEIF")) { + /* Calculate the new overall if condition */ + CalcOverallIfCond (); - /* Calculate the new overall if condition */ - CalcOverallIfCond (); + /* Allocate and prepare a new descriptor */ + D = AllocIf (".ELSEIF", 0); + NextTok (); - /* Allocate and prepare a new descriptor */ - D = AllocIf (".ELSEIF", 0); - NextTok (); + /* Ignore the new condition if we are inside a false .ELSE + ** branch. This way we won't get any errors about undefined + ** symbols or similar... + */ + if (IfCond) { + SetIfCond (D, ConstExpression ()); + ExpectSep (); + } - /* Ignore the new condition if we are inside a false .ELSE - ** branch. This way we won't get any errors about undefined - ** symbols or similar... - */ - if (IfCond) { - SetIfCond (D, ConstExpression ()); - ExpectSep (); + /* Get the new overall condition */ + CalcOverallIfCond (); + } else { + /* Problem with .ELSEIF, ignore remainder of line */ + SkipUntilSep (); } - - /* Get the new overall condition */ - CalcOverallIfCond (); break; case TOK_ENDIF: diff --git a/src/ca65/feature.c b/src/ca65/feature.c index 9f5ca5876..6a38900b5 100644 --- a/src/ca65/feature.c +++ b/src/ca65/feature.c @@ -67,6 +67,7 @@ static const char* const FeatureKeys[FEAT_COUNT] = { "bracket_as_indirect", "string_escapes", "long_jsr_jmp_rts", + "line_continuations", }; @@ -121,6 +122,7 @@ void SetFeature (feature_t Feature, unsigned char On) case FEAT_BRACKET_AS_INDIRECT: BracketAsIndirect = On; break; case FEAT_STRING_ESCAPES: StringEscapes = On; break; case FEAT_LONG_JSR_JMP_RTS: LongJsrJmpRts = On; break; + case FEAT_LINE_CONTINUATIONS: LineCont = On; break; default: break; } } diff --git a/src/ca65/feature.h b/src/ca65/feature.h index 8eeb62e6f..4d307f74d 100644 --- a/src/ca65/feature.h +++ b/src/ca65/feature.h @@ -69,6 +69,7 @@ typedef enum { FEAT_BRACKET_AS_INDIRECT, FEAT_STRING_ESCAPES, FEAT_LONG_JSR_JMP_RTS, + FEAT_LINE_CONTINUATIONS, /* Special value: Number of features available */ FEAT_COUNT diff --git a/src/ca65/instr.c b/src/ca65/instr.c index 89162c3c6..da6bd6e44 100644 --- a/src/ca65/instr.c +++ b/src/ca65/instr.c @@ -1618,7 +1618,7 @@ static void PutJMP (const InsDesc* Ins) if (EvalEA (Ins, &A)) { /* Check for indirect addressing */ - if ((A.AddrModeBit & AM65_ABS_IND) && (CPU < CPU_65SC02)) { + if ((A.AddrModeBit & AM65_ABS_IND) && (CPU < CPU_65SC02) && (RelaxChecks == 0)) { /* Compare the low byte of the expression to 0xFF to check for ** a page cross. Be sure to use a copy of the expression otherwise @@ -1631,7 +1631,7 @@ static void PutJMP (const InsDesc* Ins) unsigned Msg = GetStringId ("\"jmp (abs)\" across page border"); /* Generate the assertion */ - AddAssertion (E, ASSERT_ACT_WARN, Msg); + AddAssertion (E, ASSERT_ACT_ERROR, Msg); } /* No error, output code */ diff --git a/src/ca65/istack.c b/src/ca65/istack.c index 7a95e7e8c..979c09740 100644 --- a/src/ca65/istack.c +++ b/src/ca65/istack.c @@ -156,3 +156,29 @@ void CheckInputStack (void) Error ("Open %s", IStack->Desc); } } + + + +InputStack RetrieveInputStack (void) +/* Retrieve the current input stack. This will also clear it. Used when +** including a file. The current input stack is stored together with the old +** input file and restored when the file is closed. + */ +{ + /* We do not touch the counter so input sources are counted across + ** includes. + */ + InputStack S = IStack; + IStack = 0; + return S; +} + + + +void RestoreInputStack (InputStack S) +/* Restore an old input stack that was retrieved by RetrieveInputStack(). */ +{ + CHECK (IStack == 0); + IStack = S; +} + diff --git a/src/ca65/istack.h b/src/ca65/istack.h index aa37bab14..28c413d39 100644 --- a/src/ca65/istack.h +++ b/src/ca65/istack.h @@ -38,6 +38,17 @@ +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Opaque pointer to an input stack */ +typedef void* InputStack; + + + /*****************************************************************************/ /* Code */ /*****************************************************************************/ @@ -63,6 +74,15 @@ void CheckInputStack (void); ** stuff on the input stack. */ +InputStack RetrieveInputStack (void); +/* Retrieve the current input stack. This will also clear it. Used when +** including a file. The current input stack is stored together with the old +** input file and restored when the file is closed. + */ + +void RestoreInputStack (InputStack S); +/* Restore an old input stack that was retrieved by RetrieveInputStack(). */ + /* End of istack.h */ diff --git a/src/ca65/main.c b/src/ca65/main.c index fedbb0d4b..f3100162a 100644 --- a/src/ca65/main.c +++ b/src/ca65/main.c @@ -346,6 +346,10 @@ static void SetSys (const char* Sys) NewSymbol ("__KIM1__", 1); break; + case TGT_RP6502: + NewSymbol ("__RP6502__", 1); + break; + default: AbEnd ("Invalid target name: '%s'", Sys); @@ -703,6 +707,24 @@ static void OneLine (void) NextTok (); } + /* Handle @-style unnamed labels */ + if (CurTok.Tok == TOK_ULABEL) { + if (CurTok.IVal != 0) { + Error ("Invalid unnamed label definition"); + } + ULabDef (); + NextTok (); + + /* Skip the colon. If NoColonLabels is enabled, allow labels without + ** a colon if there is no whitespace before the identifier. + */ + if (CurTok.Tok == TOK_COLON) { + NextTok (); + } else if (CurTok.WS || !NoColonLabels) { + Error ("':' expected"); + } + } + /* If the first token on the line is an identifier, check for a macro or ** an instruction. */ diff --git a/src/ca65/scanner.c b/src/ca65/scanner.c index 185100025..8b910e672 100644 --- a/src/ca65/scanner.c +++ b/src/ca65/scanner.c @@ -46,6 +46,7 @@ #include "check.h" #include "filestat.h" #include "fname.h" +#include "tgttrans.h" #include "xmalloc.h" /* ca65 */ @@ -113,6 +114,7 @@ struct CharSource { token_t Tok; /* Last token */ int C; /* Last character */ int SkipN; /* For '\r\n' line endings, skip '\n\ if next */ + InputStack IStack; /* Saved input stack */ const CharSourceFunctions* Func; /* Pointer to function table */ union { InputFile File; /* File data */ @@ -321,6 +323,9 @@ static void UseCharSource (CharSource* S) S->Tok = CurTok.Tok; S->C = C; + /* Remember the current input stack */ + S->IStack = RetrieveInputStack (); + /* Use the new input source */ S->Next = Source; Source = S; @@ -347,7 +352,10 @@ static void DoneCharSource (void) /* Restore the old token */ CurTok.Tok = Source->Tok; - C = Source->C; + C = Source->C; + + /* Restore the old input source */ + RestoreInputStack (Source->IStack); /* Remember the last stacked input source */ S = Source->Next; @@ -781,14 +789,33 @@ static void ReadIdent (void) static void ReadStringConst (int StringTerm) /* Read a string constant into SVal. */ { + int NeedNext; + /* Skip the leading string terminator */ NextChar (); /* Read the string */ while (1) { + int Cooked = 1; + NeedNext = 1; + + if (StringTerm == 0 && SB_GetLen(&CurTok.SVal) == 1) { + if (C == '\'') { + break; + } + else if (MissingCharTerm) { + NeedNext = 0; + break; + } + else { + Error ("Illegal character constant"); + } + } + if (C == StringTerm) { break; } + if (C == '\n' || C == EOF) { Error ("Newline in string constant"); break; @@ -801,20 +828,74 @@ static void ReadStringConst (int StringTerm) case EOF: Error ("Unterminated escape sequence in string constant"); break; - case '\\': - case '\'': - case '"': + case '?': + C = '\?'; break; - case 't': - C = '\x09'; + case 'a': + C = '\a'; + break; + case 'b': + C = '\b'; + break; + case 'e': + C = '\x1B'; /* see comments in cc65/scanner.c */ + break; + case 'f': + C = '\f'; break; case 'r': - C = '\x0D'; + C = '\r'; break; case 'n': - C = '\x0A'; + C = '\n'; break; + case 't': + C = '\t'; + break; + case 'v': + C = '\v'; + break; + case '\\': + C = '\\'; /* unnecessary but more readable */ + break; + case '\'': + C = '\''; /* unnecessary but more readable */ + if (StringTerm == 0) { + /* special case used by character constants + ** when LooseStringTerm not set. this will + ** cause '\' to be a valid character constant + */ + C = '\\'; + NeedNext = 0; + } + break; + case '\"': + C = '\"'; /* unnecessary but more readable */ + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + { /* brace needed for scoping */ + int Count = 1; + int Final = DigitVal(C); + Cooked = 0; + NextChar (); + while (IsODigit (C) && Count++ < 3) { + Final = (Final << 3) | DigitVal(C); + NextChar(); + } + if (C >= 256) + Error ("Octal character constant out of range"); + } + break; + case 'X': case 'x': + Cooked = 0; NextChar (); if (IsXDigit (C)) { char high_nibble = DigitVal (C) << 4; @@ -832,14 +913,19 @@ static void ReadStringConst (int StringTerm) } /* Append the char to the string */ - SB_AppendChar (&CurTok.SVal, C); + SB_AppendCharCooked (&CurTok.SVal, C, Cooked); - /* Skip the character */ - NextChar (); + if (NeedNext) { + /* Skip the character */ + NextChar (); + NeedNext = 1; + } } - /* Skip the trailing terminator */ - NextChar (); + if (NeedNext) { + /* Skip the trailing terminator */ + NextChar (); + } /* Terminate the string */ SB_Terminate (&CurTok.SVal); @@ -1124,17 +1210,33 @@ Again: /* Local symbol? */ if (C == LocalStart) { - /* Read the identifier. */ - ReadIdent (); + NextChar (); - /* Start character alone is not enough */ - if (SB_GetLen (&CurTok.SVal) == 1) { - Error ("Invalid cheap local symbol"); - goto Again; + if (IsIdChar (C)) { + /* Read a local identifier */ + CurTok.Tok = TOK_LOCAL_IDENT; + SB_AppendChar (&CurTok.SVal, LocalStart); + ReadIdent (); + } else { + /* Read an unnamed label */ + CurTok.IVal = 0; + CurTok.Tok = TOK_ULABEL; + + if (C == '-' || C == '<') { + int PrevC = C; + do { + --CurTok.IVal; + NextChar (); + } while (C == PrevC); + } else if (C == '+' || C == '>') { + int PrevC = C; + do { + ++CurTok.IVal; + NextChar (); + } while (C == PrevC); + } } - /* A local identifier */ - CurTok.Tok = TOK_LOCAL_IDENT; return; } @@ -1314,22 +1416,30 @@ CharAgain: break; case '-': + case '<': + { + int PrevC = C; CurTok.IVal = 0; do { --CurTok.IVal; NextChar (); - } while (C == '-'); + } while (C == PrevC); CurTok.Tok = TOK_ULABEL; break; + } case '+': + case '>': + { + int PrevC = C; CurTok.IVal = 0; do { ++CurTok.IVal; NextChar (); - } while (C == '+'); + } while (C == PrevC); CurTok.Tok = TOK_ULABEL; break; + } case '=': NextChar (); @@ -1434,12 +1544,13 @@ CharAgain: return; case '\'': - /* Hack: If we allow ' as terminating character for strings, read - ** the following stuff as a string, and check for a one character - ** string later. - */ if (LooseStringTerm) { + /* Hack: If we allow ' as terminating character for strings, read + ** the following stuff as a string, and check for a one character + ** string later. + */ ReadStringConst ('\''); + TgtTranslateStrBuf(&CurTok.SVal); if (SB_GetLen (&CurTok.SVal) == 1) { CurTok.IVal = SB_AtUnchecked (&CurTok.SVal, 0); CurTok.Tok = TOK_CHARCON; @@ -1447,22 +1558,17 @@ CharAgain: CurTok.Tok = TOK_STRCON; } } else { - /* Always a character constant */ - NextChar (); - if (C == EOF || IsControl (C)) { + /* Always a character constant + ** Hack: Pass 0 to ReadStringConst for special handling. + */ + ReadStringConst(0); + TgtTranslateStrBuf(&CurTok.SVal); + if (SB_GetLen(&CurTok.SVal) != 1) { Error ("Illegal character constant"); goto CharAgain; } - CurTok.IVal = C; + CurTok.IVal = SB_AtUnchecked (&CurTok.SVal, 0); CurTok.Tok = TOK_CHARCON; - NextChar (); - if (C != '\'') { - if (!MissingCharTerm) { - Error ("Illegal character constant"); - } - } else { - NextChar (); - } } return; @@ -1497,7 +1603,7 @@ CharAgain: /* In case of the main file, do not close it, but return EOF. */ if (Source && Source->Next) { DoneCharSource (); - goto Again; + goto Restart; } else { CurTok.Tok = TOK_EOF; } diff --git a/src/ca65/token.h b/src/ca65/token.h index b8bbb6d6e..8f935f7a1 100644 --- a/src/ca65/token.h +++ b/src/ca65/token.h @@ -71,7 +71,7 @@ typedef enum token_t { TOK_REG, /* Sweet16 R.. register (in sweet16 mode) */ TOK_ASSIGN, /* := */ - TOK_ULABEL, /* :++ or :-- */ + TOK_ULABEL, /* An unnamed label */ TOK_EQ, /* = */ TOK_NE, /* <> */ diff --git a/src/ca65/ulabel.c b/src/ca65/ulabel.c index 1127c3743..19bec0671 100644 --- a/src/ca65/ulabel.c +++ b/src/ca65/ulabel.c @@ -107,8 +107,12 @@ ExprNode* ULabRef (int Which) int Index; ULabel* L; - /* Which can never be 0 */ - PRECONDITION (Which != 0); + /* Which should not be 0 */ + if (Which == 0) { + Error ("Invalid unnamed label reference"); + /* We must return something valid */ + return GenCurrentPC(); + } /* Get the index of the referenced label */ if (Which > 0) { diff --git a/src/cc65.props b/src/cc65.props index ef5a37fea..19fb0b646 100644 --- a/src/cc65.props +++ b/src/cc65.props @@ -21,6 +21,7 @@ <TreatWarningAsError>true</TreatWarningAsError> <PreprocessorDefinitions>_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;_WIN32_WINNT=0x0601;WINVER=0x0601;NTDDI_VERSION=0x06010000;%(PreprocessorDefinitions)</PreprocessorDefinitions> <AdditionalIncludeDirectories Condition="$(MSBuildProjectName) != 'common'">common</AdditionalIncludeDirectories> + <AdditionalOptions>/utf-8 %(AdditionalOptions)</AdditionalOptions> </ClCompile> <Link> <AdditionalDependencies Condition="$(MSBuildProjectName) != 'common'">$(IntDir)..\..\common\$(Configuration)\common.lib</AdditionalDependencies> @@ -42,13 +43,13 @@ <!-- Release settings. --> <PropertyGroup Condition="'$(Configuration)'=='Release'"> <LinkIncremental>false</LinkIncremental> - <WholeProgramOptimization>true</WholeProgramOptimization> </PropertyGroup> <ItemDefinitionGroup Condition="'$(Configuration)'=='Release'"> <ClCompile> <RuntimeLibrary>MultiThreaded</RuntimeLibrary> <FunctionLevelLinking>true</FunctionLevelLinking> <Optimization>MaxSpeed</Optimization> + <WholeProgramOptimization>true</WholeProgramOptimization> <BufferSecurityCheck>false</BufferSecurityCheck> <ControlFlowGuard>false</ControlFlowGuard> </ClCompile> @@ -57,6 +58,7 @@ <EnableCOMDATFolding>true</EnableCOMDATFolding> <OptimizeReferences>true</OptimizeReferences> <LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration> + <AdditionalOptions>/EMITTOOLVERSIONINFO:NO /NOVCFEATURE /NOCOFFGRPINFO %(AdditionalOptions)</AdditionalOptions> </Link> </ItemDefinitionGroup> </Project> diff --git a/src/cc65.sln b/src/cc65.sln index 4ae2816ad..8e31ef909 100644 --- a/src/cc65.sln +++ b/src/cc65.sln @@ -1,6 +1,8 @@  -Microsoft Visual Studio Solution File, Format Version 11.00 -# Visual C++ Express 2010 +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.6.33829.357 +MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "common", "common.vcxproj", "{71DC1F68-BFC4-478C-8655-C8E9C9654D2B}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cc65", "cc65.vcxproj", "{B17EDBD5-DC04-4970-9CBD-56A98B6A3FCA}" @@ -66,61 +68,115 @@ EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 Release|Win32 = Release|Win32 + Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {71DC1F68-BFC4-478C-8655-C8E9C9654D2B}.Debug|Win32.ActiveCfg = Debug|Win32 {71DC1F68-BFC4-478C-8655-C8E9C9654D2B}.Debug|Win32.Build.0 = Debug|Win32 + {71DC1F68-BFC4-478C-8655-C8E9C9654D2B}.Debug|x64.ActiveCfg = Debug|x64 + {71DC1F68-BFC4-478C-8655-C8E9C9654D2B}.Debug|x64.Build.0 = Debug|x64 {71DC1F68-BFC4-478C-8655-C8E9C9654D2B}.Release|Win32.ActiveCfg = Release|Win32 {71DC1F68-BFC4-478C-8655-C8E9C9654D2B}.Release|Win32.Build.0 = Release|Win32 + {71DC1F68-BFC4-478C-8655-C8E9C9654D2B}.Release|x64.ActiveCfg = Release|x64 + {71DC1F68-BFC4-478C-8655-C8E9C9654D2B}.Release|x64.Build.0 = Release|x64 {B17EDBD5-DC04-4970-9CBD-56A98B6A3FCA}.Debug|Win32.ActiveCfg = Debug|Win32 {B17EDBD5-DC04-4970-9CBD-56A98B6A3FCA}.Debug|Win32.Build.0 = Debug|Win32 + {B17EDBD5-DC04-4970-9CBD-56A98B6A3FCA}.Debug|x64.ActiveCfg = Debug|x64 + {B17EDBD5-DC04-4970-9CBD-56A98B6A3FCA}.Debug|x64.Build.0 = Debug|x64 {B17EDBD5-DC04-4970-9CBD-56A98B6A3FCA}.Release|Win32.ActiveCfg = Release|Win32 {B17EDBD5-DC04-4970-9CBD-56A98B6A3FCA}.Release|Win32.Build.0 = Release|Win32 + {B17EDBD5-DC04-4970-9CBD-56A98B6A3FCA}.Release|x64.ActiveCfg = Release|x64 + {B17EDBD5-DC04-4970-9CBD-56A98B6A3FCA}.Release|x64.Build.0 = Release|x64 {D28CB737-E6CA-49C4-8CE9-FF05F86DD4EC}.Debug|Win32.ActiveCfg = Debug|Win32 {D28CB737-E6CA-49C4-8CE9-FF05F86DD4EC}.Debug|Win32.Build.0 = Debug|Win32 + {D28CB737-E6CA-49C4-8CE9-FF05F86DD4EC}.Debug|x64.ActiveCfg = Debug|x64 + {D28CB737-E6CA-49C4-8CE9-FF05F86DD4EC}.Debug|x64.Build.0 = Debug|x64 {D28CB737-E6CA-49C4-8CE9-FF05F86DD4EC}.Release|Win32.ActiveCfg = Release|Win32 {D28CB737-E6CA-49C4-8CE9-FF05F86DD4EC}.Release|Win32.Build.0 = Release|Win32 + {D28CB737-E6CA-49C4-8CE9-FF05F86DD4EC}.Release|x64.ActiveCfg = Release|x64 + {D28CB737-E6CA-49C4-8CE9-FF05F86DD4EC}.Release|x64.Build.0 = Release|x64 {5E8C19C6-B167-440C-8BEF-3CBF109CDB49}.Debug|Win32.ActiveCfg = Debug|Win32 {5E8C19C6-B167-440C-8BEF-3CBF109CDB49}.Debug|Win32.Build.0 = Debug|Win32 + {5E8C19C6-B167-440C-8BEF-3CBF109CDB49}.Debug|x64.ActiveCfg = Debug|x64 + {5E8C19C6-B167-440C-8BEF-3CBF109CDB49}.Debug|x64.Build.0 = Debug|x64 {5E8C19C6-B167-440C-8BEF-3CBF109CDB49}.Release|Win32.ActiveCfg = Release|Win32 {5E8C19C6-B167-440C-8BEF-3CBF109CDB49}.Release|Win32.Build.0 = Release|Win32 + {5E8C19C6-B167-440C-8BEF-3CBF109CDB49}.Release|x64.ActiveCfg = Release|x64 + {5E8C19C6-B167-440C-8BEF-3CBF109CDB49}.Release|x64.Build.0 = Release|x64 {26C749A0-814C-47A2-9D36-AE92AE932FE4}.Debug|Win32.ActiveCfg = Debug|Win32 {26C749A0-814C-47A2-9D36-AE92AE932FE4}.Debug|Win32.Build.0 = Debug|Win32 + {26C749A0-814C-47A2-9D36-AE92AE932FE4}.Debug|x64.ActiveCfg = Debug|x64 + {26C749A0-814C-47A2-9D36-AE92AE932FE4}.Debug|x64.Build.0 = Debug|x64 {26C749A0-814C-47A2-9D36-AE92AE932FE4}.Release|Win32.ActiveCfg = Release|Win32 {26C749A0-814C-47A2-9D36-AE92AE932FE4}.Release|Win32.Build.0 = Release|Win32 + {26C749A0-814C-47A2-9D36-AE92AE932FE4}.Release|x64.ActiveCfg = Release|x64 + {26C749A0-814C-47A2-9D36-AE92AE932FE4}.Release|x64.Build.0 = Release|x64 {F657912F-050A-488B-B203-50ED5715CDD7}.Debug|Win32.ActiveCfg = Debug|Win32 {F657912F-050A-488B-B203-50ED5715CDD7}.Debug|Win32.Build.0 = Debug|Win32 + {F657912F-050A-488B-B203-50ED5715CDD7}.Debug|x64.ActiveCfg = Debug|x64 + {F657912F-050A-488B-B203-50ED5715CDD7}.Debug|x64.Build.0 = Debug|x64 {F657912F-050A-488B-B203-50ED5715CDD7}.Release|Win32.ActiveCfg = Release|Win32 {F657912F-050A-488B-B203-50ED5715CDD7}.Release|Win32.Build.0 = Release|Win32 + {F657912F-050A-488B-B203-50ED5715CDD7}.Release|x64.ActiveCfg = Release|x64 + {F657912F-050A-488B-B203-50ED5715CDD7}.Release|x64.Build.0 = Release|x64 {0BCFB793-2B25-40E2-B265-75848824AC4C}.Debug|Win32.ActiveCfg = Debug|Win32 {0BCFB793-2B25-40E2-B265-75848824AC4C}.Debug|Win32.Build.0 = Debug|Win32 + {0BCFB793-2B25-40E2-B265-75848824AC4C}.Debug|x64.ActiveCfg = Debug|x64 + {0BCFB793-2B25-40E2-B265-75848824AC4C}.Debug|x64.Build.0 = Debug|x64 {0BCFB793-2B25-40E2-B265-75848824AC4C}.Release|Win32.ActiveCfg = Release|Win32 {0BCFB793-2B25-40E2-B265-75848824AC4C}.Release|Win32.Build.0 = Release|Win32 + {0BCFB793-2B25-40E2-B265-75848824AC4C}.Release|x64.ActiveCfg = Release|x64 + {0BCFB793-2B25-40E2-B265-75848824AC4C}.Release|x64.Build.0 = Release|x64 {F5DB5D1A-05BC-48FE-B346-4E96DD522AA2}.Debug|Win32.ActiveCfg = Debug|Win32 {F5DB5D1A-05BC-48FE-B346-4E96DD522AA2}.Debug|Win32.Build.0 = Debug|Win32 + {F5DB5D1A-05BC-48FE-B346-4E96DD522AA2}.Debug|x64.ActiveCfg = Debug|x64 + {F5DB5D1A-05BC-48FE-B346-4E96DD522AA2}.Debug|x64.Build.0 = Debug|x64 {F5DB5D1A-05BC-48FE-B346-4E96DD522AA2}.Release|Win32.ActiveCfg = Release|Win32 {F5DB5D1A-05BC-48FE-B346-4E96DD522AA2}.Release|Win32.Build.0 = Release|Win32 + {F5DB5D1A-05BC-48FE-B346-4E96DD522AA2}.Release|x64.ActiveCfg = Release|x64 + {F5DB5D1A-05BC-48FE-B346-4E96DD522AA2}.Release|x64.Build.0 = Release|x64 {E0FD0AB3-3BEE-496F-8108-A8E0F8933F39}.Debug|Win32.ActiveCfg = Debug|Win32 {E0FD0AB3-3BEE-496F-8108-A8E0F8933F39}.Debug|Win32.Build.0 = Debug|Win32 + {E0FD0AB3-3BEE-496F-8108-A8E0F8933F39}.Debug|x64.ActiveCfg = Debug|x64 + {E0FD0AB3-3BEE-496F-8108-A8E0F8933F39}.Debug|x64.Build.0 = Debug|x64 {E0FD0AB3-3BEE-496F-8108-A8E0F8933F39}.Release|Win32.ActiveCfg = Release|Win32 {E0FD0AB3-3BEE-496F-8108-A8E0F8933F39}.Release|Win32.Build.0 = Release|Win32 + {E0FD0AB3-3BEE-496F-8108-A8E0F8933F39}.Release|x64.ActiveCfg = Release|x64 + {E0FD0AB3-3BEE-496F-8108-A8E0F8933F39}.Release|x64.Build.0 = Release|x64 {FF8576C2-1253-44FE-A51B-D9AE35F3CEAD}.Debug|Win32.ActiveCfg = Debug|Win32 {FF8576C2-1253-44FE-A51B-D9AE35F3CEAD}.Debug|Win32.Build.0 = Debug|Win32 + {FF8576C2-1253-44FE-A51B-D9AE35F3CEAD}.Debug|x64.ActiveCfg = Debug|x64 + {FF8576C2-1253-44FE-A51B-D9AE35F3CEAD}.Debug|x64.Build.0 = Debug|x64 {FF8576C2-1253-44FE-A51B-D9AE35F3CEAD}.Release|Win32.ActiveCfg = Release|Win32 {FF8576C2-1253-44FE-A51B-D9AE35F3CEAD}.Release|Win32.Build.0 = Release|Win32 + {FF8576C2-1253-44FE-A51B-D9AE35F3CEAD}.Release|x64.ActiveCfg = Release|x64 + {FF8576C2-1253-44FE-A51B-D9AE35F3CEAD}.Release|x64.Build.0 = Release|x64 {4388D1AF-C7EA-4AD4-8E80-CA1FB7BF76BF}.Debug|Win32.ActiveCfg = Debug|Win32 {4388D1AF-C7EA-4AD4-8E80-CA1FB7BF76BF}.Debug|Win32.Build.0 = Debug|Win32 + {4388D1AF-C7EA-4AD4-8E80-CA1FB7BF76BF}.Debug|x64.ActiveCfg = Debug|x64 + {4388D1AF-C7EA-4AD4-8E80-CA1FB7BF76BF}.Debug|x64.Build.0 = Debug|x64 {4388D1AF-C7EA-4AD4-8E80-CA1FB7BF76BF}.Release|Win32.ActiveCfg = Release|Win32 {4388D1AF-C7EA-4AD4-8E80-CA1FB7BF76BF}.Release|Win32.Build.0 = Release|Win32 + {4388D1AF-C7EA-4AD4-8E80-CA1FB7BF76BF}.Release|x64.ActiveCfg = Release|x64 + {4388D1AF-C7EA-4AD4-8E80-CA1FB7BF76BF}.Release|x64.Build.0 = Release|x64 {002A366E-2863-46A8-BDDE-DDF534AAEC73}.Debug|Win32.ActiveCfg = Debug|Win32 {002A366E-2863-46A8-BDDE-DDF534AAEC73}.Debug|Win32.Build.0 = Debug|Win32 + {002A366E-2863-46A8-BDDE-DDF534AAEC73}.Debug|x64.ActiveCfg = Debug|x64 + {002A366E-2863-46A8-BDDE-DDF534AAEC73}.Debug|x64.Build.0 = Debug|x64 {002A366E-2863-46A8-BDDE-DDF534AAEC73}.Release|Win32.ActiveCfg = Release|Win32 {002A366E-2863-46A8-BDDE-DDF534AAEC73}.Release|Win32.Build.0 = Release|Win32 + {002A366E-2863-46A8-BDDE-DDF534AAEC73}.Release|x64.ActiveCfg = Release|x64 + {002A366E-2863-46A8-BDDE-DDF534AAEC73}.Release|x64.Build.0 = Release|x64 {1C7A3FEF-DD0B-4B10-BC33-C3BE29BF67CC}.Debug|Win32.ActiveCfg = Debug|Win32 {1C7A3FEF-DD0B-4B10-BC33-C3BE29BF67CC}.Debug|Win32.Build.0 = Debug|Win32 + {1C7A3FEF-DD0B-4B10-BC33-C3BE29BF67CC}.Debug|x64.ActiveCfg = Debug|x64 + {1C7A3FEF-DD0B-4B10-BC33-C3BE29BF67CC}.Debug|x64.Build.0 = Debug|x64 {1C7A3FEF-DD0B-4B10-BC33-C3BE29BF67CC}.Release|Win32.ActiveCfg = Release|Win32 {1C7A3FEF-DD0B-4B10-BC33-C3BE29BF67CC}.Release|Win32.Build.0 = Release|Win32 + {1C7A3FEF-DD0B-4B10-BC33-C3BE29BF67CC}.Release|x64.ActiveCfg = Release|x64 + {1C7A3FEF-DD0B-4B10-BC33-C3BE29BF67CC}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/cc65.vcxproj b/src/cc65.vcxproj index 7e2e8ea8c..75ee92276 100644 --- a/src/cc65.vcxproj +++ b/src/cc65.vcxproj @@ -5,10 +5,18 @@ <Configuration>Debug</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> <ProjectConfiguration Include="Release|Win32"> <Configuration>Release</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> </ItemGroup> <PropertyGroup Label="Globals"> <ProjectGuid>{B17EDBD5-DC04-4970-9CBD-56A98B6A3FCA}</ProjectGuid> @@ -21,15 +29,22 @@ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <UseDebugLibraries>true</UseDebugLibraries> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <UseDebugLibraries>true</UseDebugLibraries> + </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> <UseDebugLibraries>false</UseDebugLibraries> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <UseDebugLibraries>false</UseDebugLibraries> + </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <ImportGroup Label="ExtensionSettings"> </ImportGroup> <PropertyGroup Label="UserMacros" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> </PropertyGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> @@ -40,6 +55,15 @@ <SubSystem>Console</SubSystem> </Link> </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <PreprocessorDefinitions>_CONSOLE;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <DisableSpecificWarnings>4244;4267;%(DisableSpecificWarnings)</DisableSpecificWarnings> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + </Link> + </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ClCompile> <PreprocessorDefinitions>_CONSOLE;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> @@ -48,6 +72,15 @@ <SubSystem>Console</SubSystem> </Link> </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <PreprocessorDefinitions>_CONSOLE;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <DisableSpecificWarnings>4244;4267;%(DisableSpecificWarnings)</DisableSpecificWarnings> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + </Link> + </ItemDefinitionGroup> <ItemGroup> <ClInclude Include="cc65\anonname.h" /> <ClInclude Include="cc65\asmcode.h" /> @@ -64,12 +97,13 @@ <ClInclude Include="cc65\codeseg.h" /> <ClInclude Include="cc65\compile.h" /> <ClInclude Include="cc65\coptadd.h" /> + <ClInclude Include="cc65\coptbool.h" /> <ClInclude Include="cc65\coptc02.h" /> <ClInclude Include="cc65\coptcmp.h" /> <ClInclude Include="cc65\coptind.h" /> <ClInclude Include="cc65\coptjmp.h" /> + <ClInclude Include="cc65\coptlong.h" /> <ClInclude Include="cc65\coptmisc.h" /> - <ClInclude Include="cc65\coptneg.h" /> <ClInclude Include="cc65\coptptrload.h" /> <ClInclude Include="cc65\coptptrstore.h" /> <ClInclude Include="cc65\coptpush.h" /> @@ -79,6 +113,7 @@ <ClInclude Include="cc65\coptstore.h" /> <ClInclude Include="cc65\coptsub.h" /> <ClInclude Include="cc65\copttest.h" /> + <ClInclude Include="cc65\coptunary.h" /> <ClInclude Include="cc65\dataseg.h" /> <ClInclude Include="cc65\datatype.h" /> <ClInclude Include="cc65\declare.h" /> @@ -110,6 +145,7 @@ <ClInclude Include="cc65\scanner.h" /> <ClInclude Include="cc65\scanstrbuf.h" /> <ClInclude Include="cc65\segments.h" /> + <ClInclude Include="cc65\seqpoint.h" /> <ClInclude Include="cc65\shiftexpr.h" /> <ClInclude Include="cc65\stackptr.h" /> <ClInclude Include="cc65\standard.h" /> @@ -143,12 +179,13 @@ <ClCompile Include="cc65\codeseg.c" /> <ClCompile Include="cc65\compile.c" /> <ClCompile Include="cc65\coptadd.c" /> + <ClCompile Include="cc65\coptbool.c" /> <ClCompile Include="cc65\coptc02.c" /> <ClCompile Include="cc65\coptcmp.c" /> <ClCompile Include="cc65\coptind.c" /> <ClCompile Include="cc65\coptjmp.c" /> + <ClCompile Include="cc65\coptlong.c" /> <ClCompile Include="cc65\coptmisc.c" /> - <ClCompile Include="cc65\coptneg.c" /> <ClCompile Include="cc65\coptptrload.c" /> <ClCompile Include="cc65\coptptrstore.c" /> <ClCompile Include="cc65\coptpush.c" /> @@ -158,6 +195,7 @@ <ClCompile Include="cc65\coptstore.c" /> <ClCompile Include="cc65\coptsub.c" /> <ClCompile Include="cc65\copttest.c" /> + <ClCompile Include="cc65\coptunary.c" /> <ClCompile Include="cc65\dataseg.c" /> <ClCompile Include="cc65\datatype.c" /> <ClCompile Include="cc65\declare.c" /> @@ -190,6 +228,7 @@ <ClCompile Include="cc65\scanner.c" /> <ClCompile Include="cc65\scanstrbuf.c" /> <ClCompile Include="cc65\segments.c" /> + <ClCompile Include="cc65\seqpoint.c" /> <ClCompile Include="cc65\shiftexpr.c" /> <ClCompile Include="cc65\stackptr.c" /> <ClCompile Include="cc65\standard.c" /> diff --git a/src/cc65/asmlabel.c b/src/cc65/asmlabel.c index 7d5db75e6..a07607048 100644 --- a/src/cc65/asmlabel.c +++ b/src/cc65/asmlabel.c @@ -52,7 +52,7 @@ -static struct Segments* CurrentFunctionSegment; +static struct SegContext* CurrentFunctionSegment; @@ -62,7 +62,7 @@ static struct Segments* CurrentFunctionSegment; -void UseLabelPoolFromSegments (struct Segments* Seg) +void UseLabelPoolFromSegments (struct SegContext* Seg) /* Use the info in segments for generating new label numbers */ { CurrentFunctionSegment = Seg; diff --git a/src/cc65/asmlabel.h b/src/cc65/asmlabel.h index dbfe2f443..7e8039cc4 100644 --- a/src/cc65/asmlabel.h +++ b/src/cc65/asmlabel.h @@ -44,7 +44,7 @@ -struct Segments; +struct SegContext; @@ -54,7 +54,7 @@ struct Segments; -void UseLabelPoolFromSegments (struct Segments* Seg); +void UseLabelPoolFromSegments (struct SegContext* Seg); /* Use the info in segments for generating new label numbers */ unsigned GetLocalLabel (void); diff --git a/src/cc65/asmstmt.c b/src/cc65/asmstmt.c index d182140fd..8521f0a81 100644 --- a/src/cc65/asmstmt.c +++ b/src/cc65/asmstmt.c @@ -80,9 +80,9 @@ static void AsmErrorSkip (void) -static SymEntry* AsmGetSym (unsigned Arg, unsigned Type) -/* Find the symbol with the name currently in NextTok. The symbol must be of -** the given type. On errors, NULL is returned. +static SymEntry* AsmGetSym (unsigned Arg, int OnStack) +/* Find the symbol with the name currently in NextTok. The symbol must be on +** the stack if OnStack is true. On errors, NULL is returned. */ { SymEntry* Sym; @@ -102,7 +102,7 @@ static SymEntry* AsmGetSym (unsigned Arg, unsigned Type) /* Did we find a symbol with this name? */ if (Sym == 0) { - Error ("Undefined symbol '%s' for argument %u", CurTok.Ident, Arg); + Error ("Undeclared symbol '%s' for argument %u", CurTok.Ident, Arg); AsmErrorSkip (); return 0; } @@ -110,8 +110,8 @@ static SymEntry* AsmGetSym (unsigned Arg, unsigned Type) /* We found the symbol - skip the name token */ NextToken (); - /* Check if we have a global symbol */ - if ((Sym->Flags & Type) != Type) { + /* Check if the symbol is on the stack */ + if ((Sym->Flags & SC_STORAGEMASK) != SC_AUTO ? OnStack : !OnStack) { Error ("Type of argument %u differs from format specifier", Arg); AsmErrorSkip (); return 0; @@ -218,23 +218,24 @@ static void ParseGVarArg (StrBuf* T, unsigned Arg) */ { /* Parse the symbol name parameter and check the type */ - SymEntry* Sym = AsmGetSym (Arg, SC_STATIC); + SymEntry* Sym = AsmGetSym (Arg, 0); if (Sym == 0) { /* Some sort of error */ return; } - /* Check for external linkage */ - if (Sym->Flags & (SC_EXTERN | SC_STORAGE | SC_FUNC)) { - /* External linkage or a function */ + /* Get the correct asm name */ + if ((Sym->Flags & SC_TYPEMASK) == SC_FUNC || SymIsGlobal (Sym)) { + /* External or internal linkage or a function */ SB_AppendChar (T, '_'); SB_AppendStr (T, Sym->Name); - } else if (Sym->Flags & SC_REGISTER) { + } else if ((Sym->Flags & SC_STORAGEMASK) == SC_REGISTER) { + /* Register variable */ char Buf[32]; xsprintf (Buf, sizeof (Buf), "regbank+%d", Sym->V.R.RegOffs); SB_AppendStr (T, Buf); } else { - /* Static variable */ + /* Local static variable */ SB_AppendStr (T, LocalDataLabelName (Sym->V.L.Label)); } } @@ -248,7 +249,7 @@ static void ParseLVarArg (StrBuf* T, unsigned Arg) char Buf [16]; /* Parse the symbol name parameter and check the type */ - SymEntry* Sym = AsmGetSym (Arg, SC_AUTO); + SymEntry* Sym = AsmGetSym (Arg, 1); if (Sym == 0) { /* Some sort of error */ return; @@ -417,6 +418,9 @@ void AsmStatement (void) ** a string literal in parenthesis. */ { + /* Prevent from translating the inline code string literal in asm */ + NoCharMap = 1; + /* Skip the ASM */ NextToken (); @@ -431,9 +435,15 @@ void AsmStatement (void) /* Need left parenthesis */ if (!ConsumeLParen ()) { + NoCharMap = 0; return; } + /* We have got the inline code string untranslated, now reenable string + ** literal translation for string arguments (if any). + */ + NoCharMap = 0; + /* String literal */ if (CurTok.Tok != TOK_SCONST) { diff --git a/src/cc65/assignment.c b/src/cc65/assignment.c index 30e2f0fca..a66c495da 100644 --- a/src/cc65/assignment.c +++ b/src/cc65/assignment.c @@ -44,6 +44,7 @@ #include "scanner.h" #include "stackptr.h" #include "stdnames.h" +#include "symentry.h" #include "typecmp.h" #include "typeconv.h" @@ -82,6 +83,8 @@ static void CopyStruct (ExprDesc* LExpr, ExprDesc* RExpr) if (TypeCmp (ltype, RExpr->Type).C < TC_STRICT_COMPATIBLE) { TypeCompatibilityDiagnostic (ltype, RExpr->Type, 1, "Incompatible types in assignment to '%s' from '%s'"); + } else if (SymHasConstMember (ltype->A.S)) { + Error ("Assignment to read only variable"); } /* Do we copy the value directly using the primary? */ @@ -90,7 +93,7 @@ static void CopyStruct (ExprDesc* LExpr, ExprDesc* RExpr) /* Check if the value of the rhs is not in the primary yet */ if (!ED_IsLocPrimary (RExpr)) { /* Just load the value into the primary as the replacement type. */ - LoadExpr (TypeOf (stype) | CF_FORCECHAR, RExpr); + LoadExpr (CG_TypeOf (stype) | CF_FORCECHAR, RExpr); } /* Store it into the location referred in the primary */ @@ -101,10 +104,16 @@ static void CopyStruct (ExprDesc* LExpr, ExprDesc* RExpr) } else { - /* The rhs cannot happen to be loaded in the primary as it is too big */ + /* Load the address of rhs into the primary */ if (!ED_IsLocExpr (RExpr)) { ED_AddrExpr (RExpr); LoadExpr (CF_NONE, RExpr); + } else if (RExpr->IVal != 0) { + /* We have an expression in the primary plus a constant + ** offset. Adjust the value in the primary accordingly. + */ + g_inc (CF_PTR | CF_CONST, RExpr->IVal); + RExpr->IVal = 0; } /* Push the address of the rhs as the source of memcpy */ @@ -145,8 +154,8 @@ void DoIncDecBitField (ExprDesc* Expr, long Val, unsigned KeepResult) ChunkType = GetBitFieldChunkType (Expr->Type); /* Determine code generator flags */ - Flags = TypeOf (Expr->Type) | CF_FORCECHAR; - ChunkFlags = TypeOf (ChunkType); + Flags = CG_TypeOf (Expr->Type) | CF_FORCECHAR; + ChunkFlags = CG_TypeOf (ChunkType); if ((ChunkFlags & CF_TYPEMASK) == CF_CHAR) { ChunkFlags |= CF_FORCECHAR; } @@ -232,8 +241,8 @@ static void OpAssignBitField (const GenDesc* Gen, ExprDesc* Expr, const char* Op ChunkType = GetBitFieldChunkType (Expr->Type); /* Determine code generator flags */ - Flags = TypeOf (Expr->Type) | CF_FORCECHAR; - ChunkFlags = TypeOf (ChunkType); + Flags = CG_TypeOf (Expr->Type) | CF_FORCECHAR; + ChunkFlags = CG_TypeOf (ChunkType); if ((ChunkFlags & CF_TYPEMASK) == CF_CHAR) { ChunkFlags |= CF_FORCECHAR; } @@ -358,7 +367,7 @@ static void OpAssignBitField (const GenDesc* Gen, ExprDesc* Expr, const char* Op unsigned AdjustedFlags = Flags; if (Expr->Type->A.B.Width < INT_BITS || IsSignSigned (Expr->Type)) { AdjustedFlags = (Flags & ~CF_UNSIGNED) | CF_CONST; - AdjustedFlags = g_typeadjust (AdjustedFlags, TypeOf (Expr2.Type) | CF_CONST); + AdjustedFlags = g_typeadjust (AdjustedFlags, CG_TypeOf (Expr2.Type) | CF_CONST); } Gen->Func (g_typeadjust (Flags, AdjustedFlags) | CF_CONST, Expr2.IVal); } else { @@ -381,11 +390,11 @@ static void OpAssignBitField (const GenDesc* Gen, ExprDesc* Expr, const char* Op unsigned AdjustedFlags = Flags; if (Expr->Type->A.B.Width < INT_BITS || IsSignSigned (Expr->Type)) { AdjustedFlags = (Flags & ~CF_UNSIGNED) | CF_CONST; - AdjustedFlags = g_typeadjust (AdjustedFlags, TypeOf (Expr2.Type) | CF_CONST); + AdjustedFlags = g_typeadjust (AdjustedFlags, CG_TypeOf (Expr2.Type) | CF_CONST); } Gen->Func (g_typeadjust (Flags, AdjustedFlags), 0); } else { - Gen->Func (g_typeadjust (Flags, TypeOf (Expr2.Type)), 0); + Gen->Func (g_typeadjust (Flags, CG_TypeOf (Expr2.Type)), 0); } } else { @@ -452,7 +461,7 @@ static void OpAssignArithmetic (const GenDesc* Gen, ExprDesc* Expr, const char* Expr2.Flags |= Expr->Flags & E_MASK_KEEP_SUBEXPR; /* Determine code generator flags */ - Flags = TypeOf (Expr->Type); + Flags = CG_TypeOf (Expr->Type); /* Determine the type of the lhs */ MustScale = Gen != 0 && (Gen->Func == g_add || Gen->Func == g_sub) && @@ -572,7 +581,7 @@ static void OpAssignArithmetic (const GenDesc* Gen, ExprDesc* Expr, const char* if (MustScale) { /* lhs is a pointer, scale rhs */ - g_scale (TypeOf (Expr2.Type), CheckedSizeOf (Expr->Type+1)); + g_scale (CG_TypeOf (Expr2.Type), CheckedSizeOf (Expr->Type+1)); } /* If the lhs is character sized, the operation may be later done @@ -583,7 +592,7 @@ static void OpAssignArithmetic (const GenDesc* Gen, ExprDesc* Expr, const char* } /* Adjust the types of the operands if needed */ - Gen->Func (g_typeadjust (Flags, TypeOf (Expr2.Type)), 0); + Gen->Func (g_typeadjust (Flags, CG_TypeOf (Expr2.Type)), 0); } } @@ -627,7 +636,7 @@ void OpAssign (const GenDesc* Gen, ExprDesc* Expr, const char* Op) } } else if (IsQualConst (ltype)) { /* Check for assignment to const */ - Error ("Assignment to const"); + Error ("Assignment to const variable"); } } @@ -686,7 +695,7 @@ void OpAddSubAssign (const GenDesc* Gen, ExprDesc *Expr, const char* Op) Error ("Invalid lvalue in assignment"); } else if (IsQualConst (Expr->Type)) { /* The left side must not be const qualified */ - Error ("Assignment to const"); + Error ("Assignment to const variable"); } } @@ -715,8 +724,8 @@ void OpAddSubAssign (const GenDesc* Gen, ExprDesc *Expr, const char* Op) } /* Setup the code generator flags */ - lflags |= TypeOf (Expr->Type) | GlobalModeFlags (Expr) | CF_FORCECHAR; - rflags |= TypeOf (Expr2.Type) | CF_FORCECHAR; + lflags |= CG_TypeOf (Expr->Type) | CG_AddrModeFlags (Expr) | CF_FORCECHAR; + rflags |= CG_TypeOf (Expr2.Type) | CF_FORCECHAR; if (ED_IsConstAbs (&Expr2)) { /* The resulting value is a constant */ @@ -736,7 +745,7 @@ void OpAddSubAssign (const GenDesc* Gen, ExprDesc *Expr, const char* Op) if (MustScale) { /* lhs is a pointer, scale rhs */ - g_scale (TypeOf (Expr2.Type), CheckedSizeOf (Indirect (Expr->Type))); + g_scale (CG_TypeOf (Expr2.Type), CheckedSizeOf (Indirect (Expr->Type))); } } diff --git a/src/cc65/codeent.c b/src/cc65/codeent.c index 928815934..a870ee981 100644 --- a/src/cc65/codeent.c +++ b/src/cc65/codeent.c @@ -2445,6 +2445,24 @@ static char* RegContentDesc (const RegContents* RC, char* Buf) sprintf (B, "Y:%02X ", RC->RegY); } B += 5; + if (RegValIsUnknown (RC->Ptr1Lo)) { + strcpy (B, "P1L:XX "); + } else { + sprintf (B, "P1L:%02X ", RC->Ptr1Lo); + } + B += 7; + if (RegValIsUnknown (RC->Ptr1Hi)) { + strcpy (B, "P1H:XX "); + } else { + sprintf (B, "P1H:%02X ", RC->Ptr1Hi); + } + B += 7; + if (RegValIsUnknown (RC->Tmp1)) { + strcpy (B, "T1:XX "); + } else { + sprintf (B, "T1:%02X ", RC->Tmp1); + } + B += 6; if (PStatesAreUnknown (RC->PFlags, PSTATE_C)) { strcpy (B, "~"); } else { diff --git a/src/cc65/codegen.c b/src/cc65/codegen.c index 480cc32ea..e55a318d6 100644 --- a/src/cc65/codegen.c +++ b/src/cc65/codegen.c @@ -507,34 +507,39 @@ void g_enter (unsigned flags, unsigned argsize) -void g_leave (void) +void g_leave (int DoCleanup) /* Function epilogue */ { - /* How many bytes of locals do we have to drop? */ - unsigned ToDrop = (unsigned) -StackPtr; + /* In the main function in cc65 mode nothing has to be dropped because + ** the program is terminated anyway. + */ + if (DoCleanup) { + /* How many bytes of locals do we have to drop? */ + unsigned ToDrop = (unsigned) -StackPtr; - /* If we didn't have a variable argument list, don't call leave */ - if (funcargs >= 0) { + /* If we didn't have a variable argument list, don't call leave */ + if (funcargs >= 0) { - /* Drop stackframe if needed */ - g_drop (ToDrop + funcargs); + /* Drop stackframe if needed */ + g_drop (ToDrop + funcargs); - } else if (StackPtr != 0) { + } else if (StackPtr != 0) { + + /* We've a stack frame to drop */ + if (ToDrop > 255) { + g_drop (ToDrop); /* Inlines the code */ + AddCodeLine ("jsr leave"); + } else { + AddCodeLine ("ldy #$%02X", ToDrop); + AddCodeLine ("jsr leavey"); + } - /* We've a stack frame to drop */ - if (ToDrop > 255) { - g_drop (ToDrop); /* Inlines the code */ - AddCodeLine ("jsr leave"); } else { - AddCodeLine ("ldy #$%02X", ToDrop); - AddCodeLine ("jsr leavey"); + + /* Nothing to drop */ + AddCodeLine ("jsr leave"); + } - - } else { - - /* Nothing to drop */ - AddCodeLine ("jsr leave"); - } /* Add the final rts */ @@ -705,7 +710,6 @@ void g_getimmed (unsigned Flags, uintptr_t Val, long Offs) /* Load a constant into the primary register */ { unsigned char B1, B2, B3, B4; - unsigned Done; if ((Flags & CF_CONST) != 0) { @@ -731,40 +735,15 @@ void g_getimmed (unsigned Flags, uintptr_t Val, long Offs) B3 = (unsigned char) (Val >> 16); B4 = (unsigned char) (Val >> 24); - /* Remember which bytes are done */ - Done = 0; - - /* Load the value */ - AddCodeLine ("ldx #$%02X", B2); - Done |= 0x02; - if (B2 == B3) { - AddCodeLine ("stx sreg"); - Done |= 0x04; - } - if (B2 == B4) { - AddCodeLine ("stx sreg+1"); - Done |= 0x08; - } - if ((Done & 0x04) == 0 && B1 != B3) { - AddCodeLine ("lda #$%02X", B3); - AddCodeLine ("sta sreg"); - Done |= 0x04; - } - if ((Done & 0x08) == 0 && B1 != B4) { - AddCodeLine ("lda #$%02X", B4); - AddCodeLine ("sta sreg+1"); - Done |= 0x08; - } + /* Load the value. Don't be too smart here and let + * the optimizer do its job. + */ + AddCodeLine ("lda #$%02X", B4); + AddCodeLine ("sta sreg+1"); + AddCodeLine ("lda #$%02X", B3); + AddCodeLine ("sta sreg"); AddCodeLine ("lda #$%02X", B1); - Done |= 0x01; - if ((Done & 0x04) == 0) { - CHECK (B1 == B3); - AddCodeLine ("sta sreg"); - } - if ((Done & 0x08) == 0) { - CHECK (B1 == B4); - AddCodeLine ("sta sreg+1"); - } + AddCodeLine ("ldx #$%02X", B2); break; default: @@ -1510,7 +1489,7 @@ unsigned g_typeadjust (unsigned lhs, unsigned rhs) ** both operands are converted to unsigned long int. */ if ((ltype == CF_LONG && rtype == CF_INT && (rhs & CF_UNSIGNED)) || - (rtype == CF_LONG && ltype == CF_INT && (rhs & CF_UNSIGNED))) { + (rtype == CF_LONG && ltype == CF_INT && (lhs & CF_UNSIGNED))) { /* long can represent all unsigneds, so we are in the first sub-case. */ return const_flag | CF_LONG; } @@ -2000,25 +1979,55 @@ void g_subeqstatic (unsigned flags, uintptr_t label, long offs, case CF_INT: if (flags & CF_CONST) { - AddCodeLine ("lda %s", lbuf); - AddCodeLine ("sec"); - AddCodeLine ("sbc #$%02X", (unsigned char)val); - AddCodeLine ("sta %s", lbuf); - if (val < 0x100) { - unsigned L = GetLocalLabel (); - AddCodeLine ("bcs %s", LocalLabelName (L)); - AddCodeLine ("dec %s+1", lbuf); - g_defcodelabel (L); + if (val == 1) { + unsigned L = GetLocalLabel(); if ((flags & CF_NOKEEP) == 0) { - AddCodeLine ("ldx %s+1", lbuf); + if ((CPUIsets[CPU] & CPU_ISET_65SC02) != 0) { + AddCodeLine ("lda %s", lbuf); + AddCodeLine ("bne %s", LocalLabelName (L)); + AddCodeLine ("dec %s+1", lbuf); + g_defcodelabel (L); + AddCodeLine ("dea"); + AddCodeLine ("sta %s", lbuf); + AddCodeLine ("ldx %s+1", lbuf); + } else { + AddCodeLine ("ldx %s", lbuf); + AddCodeLine ("bne %s", LocalLabelName (L)); + AddCodeLine ("dec %s+1", lbuf); + g_defcodelabel (L); + AddCodeLine ("dex"); + AddCodeLine ("stx %s", lbuf); + AddCodeLine ("txa"); + AddCodeLine ("ldx %s+1", lbuf); + } + } else { + AddCodeLine ("lda %s", lbuf); + AddCodeLine ("bne %s", LocalLabelName (L)); + AddCodeLine ("dec %s+1", lbuf); + g_defcodelabel (L); + AddCodeLine ("dec %s", lbuf); } } else { - AddCodeLine ("lda %s+1", lbuf); - AddCodeLine ("sbc #$%02X", (unsigned char)(val >> 8)); - AddCodeLine ("sta %s+1", lbuf); - if ((flags & CF_NOKEEP) == 0) { - AddCodeLine ("tax"); - AddCodeLine ("lda %s", lbuf); + AddCodeLine ("lda %s", lbuf); + AddCodeLine ("sec"); + AddCodeLine ("sbc #$%02X", (unsigned char)val); + AddCodeLine ("sta %s", lbuf); + if (val < 0x100) { + unsigned L = GetLocalLabel (); + AddCodeLine ("bcs %s", LocalLabelName (L)); + AddCodeLine ("dec %s+1", lbuf); + g_defcodelabel (L); + if ((flags & CF_NOKEEP) == 0) { + AddCodeLine ("ldx %s+1", lbuf); + } + } else { + AddCodeLine ("lda %s+1", lbuf); + AddCodeLine ("sbc #$%02X", (unsigned char)(val >> 8)); + AddCodeLine ("sta %s+1", lbuf); + if ((flags & CF_NOKEEP) == 0) { + AddCodeLine ("tax"); + AddCodeLine ("lda %s", lbuf); + } } } } else { @@ -2726,7 +2735,12 @@ void g_mul (unsigned flags, unsigned long val) if (flags & CF_FORCECHAR) { /* Handle some special cases */ switch (val) { - + case 0: + AddCodeLine ("lda #$00"); + return; + case 1: + /* Nothing to do */ + return; case 3: AddCodeLine ("sta tmp1"); AddCodeLine ("asl a"); @@ -2764,6 +2778,13 @@ void g_mul (unsigned flags, unsigned long val) case CF_INT: switch (val) { + case 0: + AddCodeLine ("lda #$00"); + AddCodeLine ("tax"); + return; + case 1: + /* Nothing to do */ + return; case 3: AddCodeLine ("jsr mulax3"); return; @@ -3244,6 +3265,14 @@ void g_asr (unsigned flags, unsigned long val) } val -= 8; } + if (val == 7) { + if (flags & CF_UNSIGNED) { + AddCodeLine ("jsr shrax7"); + } else { + AddCodeLine ("jsr asrax7"); + } + val = 0; + } if (val >= 4) { if (flags & CF_UNSIGNED) { AddCodeLine ("jsr shrax4"); @@ -3386,6 +3415,14 @@ void g_asl (unsigned flags, unsigned long val) AddCodeLine ("lda #$00"); val -= 8; } + if (val == 7) { + if (flags & CF_UNSIGNED) { + AddCodeLine ("jsr shlax7"); + } else { + AddCodeLine ("jsr aslax7"); + } + val = 0; + } if (val >= 4) { if (flags & CF_UNSIGNED) { AddCodeLine ("jsr shlax4"); @@ -4187,10 +4224,14 @@ void g_gt (unsigned flags, unsigned long val) */ g_ne (flags, val); } else if (val < 0xFFFF) { - /* Use >= instead of > because the former gives better - ** code on the 6502 than the latter. - */ - g_ge (flags, val+1); + if (val == 0xFF) { + AddCodeLine ("cpx #$00"); + } else { + /* Use >= instead of > because the former gives better + ** code on the 6502 than the latter. + */ + g_ge (flags, val+1); + } } else { /* Never true */ Warning ("Condition is never true"); @@ -4217,6 +4258,8 @@ void g_gt (unsigned flags, unsigned long val) ** is easier to optimize. */ g_ne (flags, val); + } else if (val == 0xFF) { + AddCodeLine ("cpx #$00"); } else if (val < 0xFFFFFFFF) { /* Use >= instead of > because the former gives better ** code on the 6502 than the latter. @@ -4229,7 +4272,9 @@ void g_gt (unsigned flags, unsigned long val) } } else { /* Signed compare */ - if ((long) val < 0x7FFFFFFF) { + if (val == 0xFF) { + AddCodeLine ("cpx #$00"); + } else if ((long) val < 0x7FFFFFFF) { g_ge (flags, val+1); } else { /* Never true */ diff --git a/src/cc65/codegen.h b/src/cc65/codegen.h index 8e04b45e4..734c95372 100644 --- a/src/cc65/codegen.h +++ b/src/cc65/codegen.h @@ -247,7 +247,7 @@ void g_scale (unsigned flags, long val); void g_enter (unsigned flags, unsigned argsize); /* Function prologue */ -void g_leave (void); +void g_leave (int DoCleanup); /* Function epilogue */ diff --git a/src/cc65/codeinfo.c b/src/cc65/codeinfo.c index 88f8a5138..435794613 100644 --- a/src/cc65/codeinfo.c +++ b/src/cc65/codeinfo.c @@ -99,6 +99,7 @@ static const FuncInfo FuncInfoTable[] = { { "aslax2", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, { "aslax3", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, { "aslax4", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "aslax7", REG_AX, PSTATE_ALL | REG_AXY }, { "aslaxy", REG_AXY, PSTATE_ALL | REG_AXY | REG_TMP1 }, { "asleax1", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, { "asleax2", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, @@ -108,6 +109,7 @@ static const FuncInfo FuncInfoTable[] = { { "asrax2", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, { "asrax3", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, { "asrax4", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "asrax7", REG_AX, PSTATE_ALL | REG_AX }, { "asraxy", REG_AXY, PSTATE_ALL | REG_AXY | REG_TMP1 }, { "asreax1", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, { "asreax2", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, @@ -245,6 +247,7 @@ static const FuncInfo FuncInfoTable[] = { { "shlax2", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, { "shlax3", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, { "shlax4", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "shlax7", REG_AX, PSTATE_ALL | REG_AXY }, { "shlaxy", REG_AXY, PSTATE_ALL | REG_AXY | REG_TMP1 }, { "shleax1", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, { "shleax2", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, @@ -254,6 +257,7 @@ static const FuncInfo FuncInfoTable[] = { { "shrax2", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, { "shrax3", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, { "shrax4", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "shrax7", REG_AX, PSTATE_ALL | REG_AX }, { "shraxy", REG_AXY, PSTATE_ALL | REG_AXY | REG_TMP1 }, { "shreax1", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, { "shreax2", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, @@ -556,11 +560,8 @@ fncls_t GetFuncInfo (const char* Name, unsigned int* Use, unsigned int* Chg) *Use = REG_NONE; } - /* Will destroy all registers */ - *Chg = REG_ALL; - - /* and will destroy all processor flags */ - *Chg |= PSTATE_ALL; + /* Will destroy all registers and processor flags */ + *Chg = (REG_ALL | PSTATE_ALL); /* Done */ return FNCLS_GLOBAL; @@ -573,8 +574,7 @@ fncls_t GetFuncInfo (const char* Name, unsigned int* Use, unsigned int* Chg) ** are used mostly in inline assembly anyway. */ *Use = REG_ALL; - *Chg = REG_ALL; - *Chg |= PSTATE_ALL; + *Chg = (REG_ALL | PSTATE_ALL); return FNCLS_NUMERIC; } else { @@ -601,8 +601,7 @@ fncls_t GetFuncInfo (const char* Name, unsigned int* Use, unsigned int* Chg) fprintf (stderr, "No info about internal function '%s'\n", Name); } *Use = REG_ALL; - *Chg = REG_ALL; - *Chg |= PSTATE_ALL; + *Chg = (REG_ALL | PSTATE_ALL); } return FNCLS_BUILTIN; } @@ -611,8 +610,7 @@ fncls_t GetFuncInfo (const char* Name, unsigned int* Use, unsigned int* Chg) ** registers and processor flags are changed */ *Use = REG_EAXY; - *Chg = REG_ALL; - *Chg |= PSTATE_ALL; + *Chg = (REG_ALL | PSTATE_ALL); return FNCLS_UNKNOWN; } @@ -895,6 +893,14 @@ int RegEAXUsed (struct CodeSeg* S, unsigned Index) +int LoadFlagsUsed (struct CodeSeg* S, unsigned Index) +/* Check if one of the flags set by a register load (Z and N) are used. */ +{ + return (GetRegInfo (S, Index, PSTATE_ZN) & PSTATE_ZN) != 0; +} + + + unsigned GetKnownReg (unsigned Use, const RegContents* RC) /* Return the register or zero page location from the set in Use, thats ** contents are known. If Use does not contain any register, or if the diff --git a/src/cc65/codeinfo.h b/src/cc65/codeinfo.h index 14ef54d8f..c57908dad 100644 --- a/src/cc65/codeinfo.h +++ b/src/cc65/codeinfo.h @@ -201,6 +201,9 @@ int RegAXUsed (struct CodeSeg* S, unsigned Index); int RegEAXUsed (struct CodeSeg* S, unsigned Index); /* Check if any of the four bytes in EAX are used. */ +int LoadFlagsUsed (struct CodeSeg* S, unsigned Index); +/* Check if one of the flags set by a register load (Z and N) are used. */ + unsigned GetKnownReg (unsigned Use, const struct RegContents* RC); /* Return the register or zero page location from the set in Use, thats ** contents are known. If Use does not contain any register, or if the diff --git a/src/cc65/codeopt.c b/src/cc65/codeopt.c index 208ada134..a716ad431 100644 --- a/src/cc65/codeopt.c +++ b/src/cc65/codeopt.c @@ -52,12 +52,13 @@ #include "codeinfo.h" #include "codeopt.h" #include "coptadd.h" +#include "coptbool.h" #include "coptc02.h" #include "coptcmp.h" #include "coptind.h" #include "coptjmp.h" +#include "coptlong.h" #include "coptmisc.h" -#include "coptneg.h" #include "coptptrload.h" #include "coptptrstore.h" #include "coptpush.h" @@ -67,6 +68,7 @@ #include "coptstore.h" #include "coptsub.h" #include "copttest.h" +#include "coptunary.h" #include "error.h" #include "global.h" #include "output.h" @@ -115,7 +117,12 @@ static OptFunc DOptBNegAX1 = { OptBNegAX1, "OptBNegAX1", 100, 0, static OptFunc DOptBNegAX2 = { OptBNegAX2, "OptBNegAX2", 100, 0, 0, 0, 0, 0 }; static OptFunc DOptBNegAX3 = { OptBNegAX3, "OptBNegAX3", 100, 0, 0, 0, 0, 0 }; static OptFunc DOptBNegAX4 = { OptBNegAX4, "OptBNegAX4", 100, 0, 0, 0, 0, 0 }; +static OptFunc DOptBinOps = { OptBinOps, "OptBinOps", 0, 0, 0, 0, 0, 0 }; +static OptFunc DOptBoolCmp = { OptBoolCmp, "OptBoolCmp", 100, 0, 0, 0, 0, 0 }; static OptFunc DOptBoolTrans = { OptBoolTrans, "OptBoolTrans", 100, 0, 0, 0, 0, 0 }; +static OptFunc DOptBoolUnary1 = { OptBoolUnary1, "OptBoolUnary1", 40, 0, 0, 0, 0, 0 }; +static OptFunc DOptBoolUnary2 = { OptBoolUnary2, "OptBoolUnary2", 40, 0, 0, 0, 0, 0 }; +static OptFunc DOptBoolUnary3 = { OptBoolUnary3, "OptBoolUnary3", 40, 0, 0, 0, 0, 0 }; static OptFunc DOptBranchDist = { OptBranchDist, "OptBranchDist", 0, 0, 0, 0, 0, 0 }; static OptFunc DOptBranchDist2 = { OptBranchDist2, "OptBranchDist2", 0, 0, 0, 0, 0, 0 }; static OptFunc DOptCmp1 = { OptCmp1, "OptCmp1", 42, 0, 0, 0, 0, 0 }; @@ -123,13 +130,14 @@ static OptFunc DOptCmp2 = { OptCmp2, "OptCmp2", 85, 0, static OptFunc DOptCmp3 = { OptCmp3, "OptCmp3", 75, 0, 0, 0, 0, 0 }; static OptFunc DOptCmp4 = { OptCmp4, "OptCmp4", 75, 0, 0, 0, 0, 0 }; static OptFunc DOptCmp5 = { OptCmp5, "OptCmp5", 100, 0, 0, 0, 0, 0 }; -static OptFunc DOptCmp6 = { OptCmp6, "OptCmp6", 100, 0, 0, 0, 0, 0 }; static OptFunc DOptCmp7 = { OptCmp7, "OptCmp7", 85, 0, 0, 0, 0, 0 }; static OptFunc DOptCmp8 = { OptCmp8, "OptCmp8", 50, 0, 0, 0, 0, 0 }; static OptFunc DOptCmp9 = { OptCmp9, "OptCmp9", 85, 0, 0, 0, 0, 0 }; static OptFunc DOptComplAX1 = { OptComplAX1, "OptComplAX1", 65, 0, 0, 0, 0, 0 }; -static OptFunc DOptCondBranches1= { OptCondBranches1,"OptCondBranches1", 80, 0, 0, 0, 0, 0 }; -static OptFunc DOptCondBranches2= { OptCondBranches2,"OptCondBranches2", 0, 0, 0, 0, 0, 0 }; +static OptFunc DOptCondBranch1 = { OptCondBranch1, "OptCondBranch1", 80, 0, 0, 0, 0, 0 }; +static OptFunc DOptCondBranch2 = { OptCondBranch2, "OptCondBranch2", 40, 0, 0, 0, 0, 0 }; +static OptFunc DOptCondBranch3 = { OptCondBranch3, "OptCondBranch3", 40, 0, 0, 0, 0, 0 }; +static OptFunc DOptCondBranchC = { OptCondBranchC, "OptCondBranchC", 0, 0, 0, 0, 0, 0 }; static OptFunc DOptDeadCode = { OptDeadCode, "OptDeadCode", 100, 0, 0, 0, 0, 0 }; static OptFunc DOptDeadJumps = { OptDeadJumps, "OptDeadJumps", 100, 0, 0, 0, 0, 0 }; static OptFunc DOptDecouple = { OptDecouple, "OptDecouple", 100, 0, 0, 0, 0, 0 }; @@ -144,6 +152,8 @@ static OptFunc DOptJumpTarget3 = { OptJumpTarget3, "OptJumpTarget3", 100, 0, static OptFunc DOptLoad1 = { OptLoad1, "OptLoad1", 100, 0, 0, 0, 0, 0 }; static OptFunc DOptLoad2 = { OptLoad2, "OptLoad2", 200, 0, 0, 0, 0, 0 }; static OptFunc DOptLoad3 = { OptLoad3, "OptLoad3", 0, 0, 0, 0, 0, 0 }; +static OptFunc DOptLongAssign = { OptLongAssign, "OptLongAssign", 100, 0, 0, 0, 0, 0 }; +static OptFunc DOptLongCopy = { OptLongCopy, "OptLongCopy", 100, 0, 0, 0, 0, 0 }; static OptFunc DOptNegAX1 = { OptNegAX1, "OptNegAX1", 165, 0, 0, 0, 0, 0 }; static OptFunc DOptNegAX2 = { OptNegAX2, "OptNegAX2", 200, 0, 0, 0, 0, 0 }; static OptFunc DOptPrecalc = { OptPrecalc, "OptPrecalc", 100, 0, 0, 0, 0, 0 }; @@ -170,6 +180,7 @@ static OptFunc DOptPush1 = { OptPush1, "OptPush1", 65, 0, static OptFunc DOptPush2 = { OptPush2, "OptPush2", 50, 0, 0, 0, 0, 0 }; static OptFunc DOptPushPop1 = { OptPushPop1, "OptPushPop1", 0, 0, 0, 0, 0, 0 }; static OptFunc DOptPushPop2 = { OptPushPop2, "OptPushPop2", 0, 0, 0, 0, 0, 0 }; +static OptFunc DOptPushPop3 = { OptPushPop3, "OptPushPop3", 0, 0, 0, 0, 0, 0 }; static OptFunc DOptRTS = { OptRTS, "OptRTS", 100, 0, 0, 0, 0, 0 }; static OptFunc DOptRTSJumps1 = { OptRTSJumps1, "OptRTSJumps1", 100, 0, 0, 0, 0, 0 }; static OptFunc DOptRTSJumps2 = { OptRTSJumps2, "OptRTSJumps2", 100, 0, 0, 0, 0, 0 }; @@ -191,6 +202,7 @@ static OptFunc DOptStore3 = { OptStore3, "OptStore3", 120, 0, static OptFunc DOptStore4 = { OptStore4, "OptStore4", 50, 0, 0, 0, 0, 0 }; static OptFunc DOptStore5 = { OptStore5, "OptStore5", 100, 0, 0, 0, 0, 0 }; static OptFunc DOptStoreLoad = { OptStoreLoad, "OptStoreLoad", 0, 0, 0, 0, 0, 0 }; +static OptFunc DOptLoadStoreLoad= { OptLoadStoreLoad,"OptLoadStoreLoad", 0, 0, 0, 0, 0, 0 }; static OptFunc DOptSub1 = { OptSub1, "OptSub1", 100, 0, 0, 0, 0, 0 }; static OptFunc DOptSub2 = { OptSub2, "OptSub2", 100, 0, 0, 0, 0, 0 }; static OptFunc DOptSub3 = { OptSub3, "OptSub3", 100, 0, 0, 0, 0, 0 }; @@ -221,7 +233,12 @@ static OptFunc* OptFuncs[] = { &DOptBNegAX2, &DOptBNegAX3, &DOptBNegAX4, + &DOptBinOps, + &DOptBoolCmp, &DOptBoolTrans, + &DOptBoolUnary1, + &DOptBoolUnary2, + &DOptBoolUnary3, &DOptBranchDist, &DOptBranchDist2, &DOptCmp1, @@ -229,13 +246,14 @@ static OptFunc* OptFuncs[] = { &DOptCmp3, &DOptCmp4, &DOptCmp5, - &DOptCmp6, &DOptCmp7, &DOptCmp8, &DOptCmp9, &DOptComplAX1, - &DOptCondBranches1, - &DOptCondBranches2, + &DOptCondBranch1, + &DOptCondBranch2, + &DOptCondBranch3, + &DOptCondBranchC, &DOptDeadCode, &DOptDeadJumps, &DOptDecouple, @@ -250,6 +268,8 @@ static OptFunc* OptFuncs[] = { &DOptLoad1, &DOptLoad2, &DOptLoad3, + &DOptLongAssign, + &DOptLongCopy, &DOptNegAX1, &DOptNegAX2, &DOptPrecalc, @@ -275,6 +295,8 @@ static OptFunc* OptFuncs[] = { &DOptPush1, &DOptPush2, &DOptPushPop1, + &DOptPushPop2, + &DOptPushPop3, &DOptRTS, &DOptRTSJumps1, &DOptRTSJumps2, @@ -296,6 +318,7 @@ static OptFunc* OptFuncs[] = { &DOptStore4, &DOptStore5, &DOptStoreLoad, + &DOptLoadStoreLoad, &DOptSub1, &DOptSub2, &DOptSub3, @@ -612,10 +635,6 @@ static unsigned RunOptGroup1 (CodeSeg* S) Changes += RunOptFunc (S, &DOptPtrLoad15, 1); Changes += RunOptFunc (S, &DOptPtrLoad16, 1); Changes += RunOptFunc (S, &DOptPtrLoad17, 1); - Changes += RunOptFunc (S, &DOptBNegAX1, 1); - Changes += RunOptFunc (S, &DOptBNegAX2, 1); - Changes += RunOptFunc (S, &DOptBNegAX3, 1); - Changes += RunOptFunc (S, &DOptBNegAX4, 1); Changes += RunOptFunc (S, &DOptAdd1, 1); Changes += RunOptFunc (S, &DOptAdd2, 1); Changes += RunOptFunc (S, &DOptAdd4, 1); @@ -623,6 +642,7 @@ static unsigned RunOptGroup1 (CodeSeg* S) Changes += RunOptFunc (S, &DOptAdd6, 1); Changes += RunOptFunc (S, &DOptSub1, 1); Changes += RunOptFunc (S, &DOptSub3, 1); + Changes += RunOptFunc (S, &DOptLongAssign, 1); Changes += RunOptFunc (S, &DOptStore4, 1); Changes += RunOptFunc (S, &DOptStore5, 1); Changes += RunOptFunc (S, &DOptShift1, 1); @@ -632,6 +652,7 @@ static unsigned RunOptGroup1 (CodeSeg* S) Changes += RunOptFunc (S, &DOptStore1, 1); Changes += RunOptFunc (S, &DOptStore2, 5); Changes += RunOptFunc (S, &DOptStore3, 5); + Changes += RunOptFunc (S, &DOptLongCopy, 1); /* Return the number of changes */ return Changes; @@ -668,11 +689,15 @@ static unsigned RunOptGroup3 (CodeSeg* S) do { C = 0; - C += RunOptFunc (S, &DOptBNegA1, 1); - C += RunOptFunc (S, &DOptBNegA2, 1); C += RunOptFunc (S, &DOptNegAX1, 1); C += RunOptFunc (S, &DOptNegAX2, 1); - C += RunOptFunc (S, &DOptStackOps, 3); + C += RunOptFunc (S, &DOptStackOps, 3); /* Before OptBoolUnary1 */ + C += RunOptFunc (S, &DOptCmp8, 1); /* Before OptBoolUnary1 */ + C += RunOptFunc (S, &DOptBoolUnary1, 3); + C += RunOptFunc (S, &DOptBoolUnary2, 3); + C += RunOptFunc (S, &DOptBoolUnary3, 1); + C += RunOptFunc (S, &DOptBNegA1, 1); + C += RunOptFunc (S, &DOptBNegAX1, 1); /* After OptBoolUnary2 */ C += RunOptFunc (S, &DOptShift1, 1); C += RunOptFunc (S, &DOptShift4, 1); C += RunOptFunc (S, &DOptComplAX1, 1); @@ -684,28 +709,35 @@ static unsigned RunOptGroup3 (CodeSeg* S) C += RunOptFunc (S, &DOptJumpCascades, 1); C += RunOptFunc (S, &DOptDeadJumps, 1); C += RunOptFunc (S, &DOptDeadCode, 1); - C += RunOptFunc (S, &DOptBoolTrans, 1); C += RunOptFunc (S, &DOptJumpTarget1, 1); C += RunOptFunc (S, &DOptJumpTarget2, 1); - C += RunOptFunc (S, &DOptCondBranches1, 1); - C += RunOptFunc (S, &DOptCondBranches2, 1); + C += RunOptFunc (S, &DOptCondBranch1, 1); + C += RunOptFunc (S, &DOptCondBranch2, 1); + C += RunOptFunc (S, &DOptCondBranch3, 1); + C += RunOptFunc (S, &DOptCondBranchC, 1); C += RunOptFunc (S, &DOptRTSJumps1, 1); + C += RunOptFunc (S, &DOptBoolCmp, 1); + C += RunOptFunc (S, &DOptBoolTrans, 1); + C += RunOptFunc (S, &DOptBNegA2, 1); /* After OptCondBranch's */ + C += RunOptFunc (S, &DOptBNegAX2, 1); /* After OptCondBranch's */ + C += RunOptFunc (S, &DOptBNegAX3, 1); /* After OptCondBranch's */ + C += RunOptFunc (S, &DOptBNegAX4, 1); /* After OptCondBranch's */ C += RunOptFunc (S, &DOptCmp1, 1); C += RunOptFunc (S, &DOptCmp2, 1); - C += RunOptFunc (S, &DOptCmp8, 1); /* Must run before OptCmp3 */ + C += RunOptFunc (S, &DOptCmp8, 1); /* Must run before OptCmp3 */ C += RunOptFunc (S, &DOptCmp3, 1); C += RunOptFunc (S, &DOptCmp4, 1); C += RunOptFunc (S, &DOptCmp5, 1); - C += RunOptFunc (S, &DOptCmp6, 1); C += RunOptFunc (S, &DOptCmp7, 1); C += RunOptFunc (S, &DOptCmp9, 1); C += RunOptFunc (S, &DOptTest1, 1); C += RunOptFunc (S, &DOptLoad1, 1); - C += RunOptFunc (S, &DOptJumpTarget3, 1); /* After OptCondBranches2 */ + C += RunOptFunc (S, &DOptJumpTarget3, 1); /* After OptCondBranches2 */ C += RunOptFunc (S, &DOptUnusedLoads, 1); C += RunOptFunc (S, &DOptUnusedStores, 1); C += RunOptFunc (S, &DOptDupLoads, 1); C += RunOptFunc (S, &DOptStoreLoad, 1); + C += RunOptFunc (S, &DOptLoadStoreLoad, 1); C += RunOptFunc (S, &DOptTransfers1, 1); C += RunOptFunc (S, &DOptTransfers3, 1); C += RunOptFunc (S, &DOptTransfers4, 1); @@ -713,9 +745,11 @@ static unsigned RunOptGroup3 (CodeSeg* S) C += RunOptFunc (S, &DOptStore5, 1); C += RunOptFunc (S, &DOptPushPop1, 1); C += RunOptFunc (S, &DOptPushPop2, 1); + C += RunOptFunc (S, &DOptPushPop3, 1); C += RunOptFunc (S, &DOptPrecalc, 1); C += RunOptFunc (S, &DOptShiftBack, 1); C += RunOptFunc (S, &DOptSignExtended, 1); + C += RunOptFunc (S, &DOptBinOps, 1); Changes += C; @@ -822,6 +856,7 @@ static unsigned RunOptGroup7 (CodeSeg* S) Changes += RunOptFunc (S, &DOptUnusedStores, 1); Changes += RunOptFunc (S, &DOptJumpTarget1, 5); Changes += RunOptFunc (S, &DOptStore5, 1); + Changes += RunOptFunc (S, &DOptTransfers1, 1); } C = RunOptFunc (S, &DOptSize2, 1); diff --git a/src/cc65/codeoptutil.c b/src/cc65/codeoptutil.c index 173d5185f..43b1dee22 100644 --- a/src/cc65/codeoptutil.c +++ b/src/cc65/codeoptutil.c @@ -1128,8 +1128,10 @@ void AddOpHigh (StackOpData* D, opc_t OPC, LoadInfo* LI, int KeepResult) InsertEntry (D, X, D->IP++); } - /* In both cases, we can remove the load */ - LI->X.Flags |= LI_REMOVE; + /* If this is the right hand side, we can remove the load. */ + if (LI == &D->Rhs) { + LI->X.Flags |= LI_REMOVE; + } } else { /* opc zphi */ diff --git a/src/cc65/codeseg.c b/src/cc65/codeseg.c index 9f1bf4cc5..f4970b586 100644 --- a/src/cc65/codeseg.c +++ b/src/cc65/codeseg.c @@ -1471,7 +1471,7 @@ void CS_Output (CodeSeg* S) /* Add line debug info */ if (DebugInfo) { WriteOutput ("\t.dbg\tline, \"%s\", %u\n", - GetInputName (LI), GetInputLine (LI)); + GetActualFileName (LI), GetActualLineNum (LI)); } } /* Output the code */ diff --git a/src/cc65/compile.c b/src/cc65/compile.c index 73380f3df..e08a829e6 100644 --- a/src/cc65/compile.c +++ b/src/cc65/compile.c @@ -65,6 +65,7 @@ #include "preproc.h" #include "standard.h" #include "staticassert.h" +#include "typecmp.h" #include "symtab.h" @@ -78,7 +79,6 @@ static void Parse (void) /* Top level parser routine. */ { - int comma; SymEntry* Sym; FuncDesc* FuncDef = 0; @@ -88,14 +88,18 @@ static void Parse (void) /* Fill up the next token with a bogus semicolon and start the tokenizer */ NextTok.Tok = TOK_SEMI; NextToken (); + NextToken (); /* Parse until end of input */ while (CurTok.Tok != TOK_CEOF) { DeclSpec Spec; + int Comma; + int NeedClean = 0; /* Check for empty statements */ if (CurTok.Tok == TOK_SEMI) { + /* TODO: warn on this if we have a pedantic mode */ NextToken (); continue; } @@ -110,28 +114,20 @@ static void Parse (void) continue; } - /* Check for a #pragma */ - if (CurTok.Tok == TOK_PRAGMA) { - DoPragma (); - continue; - } - /* Check for a _Static_assert */ if (CurTok.Tok == TOK_STATIC_ASSERT) { ParseStaticAssert (); continue; } - /* Read variable defs and functions */ - ParseDeclSpec (&Spec, TS_DEFAULT_TYPE_INT, SC_EXTERN | SC_STATIC); + /* Read the declaration specifier */ + ParseDeclSpec (&Spec, TS_DEFAULT_TYPE_INT | TS_FUNCTION_SPEC, SC_NONE); /* Don't accept illegal storage classes */ - if ((Spec.StorageClass & SC_TYPEMASK) == 0) { - if ((Spec.StorageClass & SC_AUTO) != 0 || - (Spec.StorageClass & SC_REGISTER) != 0) { - Error ("Illegal storage class"); - Spec.StorageClass = SC_EXTERN | SC_STATIC; - } + if ((Spec.StorageClass & SC_STORAGEMASK) == SC_AUTO || + (Spec.StorageClass & SC_STORAGEMASK) == SC_REGISTER) { + Error ("Illegal storage class"); + Spec.StorageClass &= ~SC_STORAGEMASK; } /* Check if this is only a type declaration */ @@ -141,44 +137,59 @@ static void Parse (void) continue; } + /* If we haven't got a type specifier yet, something must be wrong */ + if ((Spec.Flags & DS_TYPE_MASK) == DS_NONE) { + /* Avoid extra errors if it was a failed type specifier */ + if ((Spec.Flags & DS_EXTRA_TYPE) == 0) { + Error ("Declaration specifier expected"); + } + NeedClean = -1; + goto EndOfDecl; + } + /* Read declarations for this type */ - Sym = 0; - comma = 0; + Comma = 0; while (1) { Declarator Decl; - /* Read the next declaration */ - ParseDecl (&Spec, &Decl, DM_NEED_IDENT); + Sym = 0; - /* Check if we must reserve storage for the variable. We do this, - ** - ** - if it is not a typedef or function, - ** - if we don't had a storage class given ("int i") - ** - if the storage class is explicitly specified as static, - ** - or if there is an initialization. - ** - ** This means that "extern int i;" will not get storage allocated. - */ - if ((Decl.StorageClass & SC_FUNC) != SC_FUNC && + /* Read the next declaration */ + NeedClean = ParseDecl (&Spec, &Decl, DM_IDENT_OR_EMPTY); + + /* Bail out if there are errors */ + if (NeedClean <= 0) { + break; + } + + /* The symbol is now visible in the file scope */ + if ((Decl.StorageClass & SC_TYPEMASK) != SC_FUNC && (Decl.StorageClass & SC_TYPEMASK) != SC_TYPEDEF) { - if ((Spec.Flags & DS_DEF_STORAGE) != 0 || - (Decl.StorageClass & (SC_EXTERN|SC_STATIC)) == SC_STATIC || - ((Decl.StorageClass & SC_EXTERN) != 0 && + /* Check if we must reserve storage for the variable. We do this, + ** + ** - if it is not a typedef or function, + ** - if we don't had a storage class given ("int i") + ** - if the storage class is explicitly specified as static, + ** - or if there is an initialization. + ** + ** This means that "extern int i;" will not get storage allocated + ** in this translation unit. + */ + if ((Decl.StorageClass & SC_STORAGEMASK) == SC_NONE || + (Decl.StorageClass & SC_STORAGEMASK) == SC_STATIC || + ((Decl.StorageClass & SC_STORAGEMASK) == SC_EXTERN && CurTok.Tok == TOK_ASSIGN)) { - /* We will allocate storage */ - Decl.StorageClass |= SC_STORAGE; - } else { - /* It's a declaration */ - Decl.StorageClass |= SC_DECL; + /* We will allocate storage in this translation unit */ + Decl.StorageClass |= SC_TU_STORAGE; } } /* If this is a function declarator that is not followed by a comma ** or semicolon, it must be followed by a function body. */ - if ((Decl.StorageClass & SC_FUNC) != 0) { - if (CurTok.Tok != TOK_COMMA && CurTok.Tok != TOK_SEMI) { + if ((Decl.StorageClass & SC_TYPEMASK) == SC_FUNC) { + if (CurTok.Tok == TOK_LCURLY) { /* A definition */ Decl.StorageClass |= SC_DEF; @@ -191,7 +202,13 @@ static void Parse (void) } } else { /* Just a declaration */ - Decl.StorageClass |= SC_DECL; + FuncDef = GetFuncDesc (Decl.Type); + if ((FuncDef->Flags & (FD_EMPTY | FD_OLDSTYLE)) == FD_OLDSTYLE) { + /* A parameter list without types is only allowed in a + ** function definition. + */ + Error ("Parameter names without types in function declaration"); + } } } @@ -202,7 +219,7 @@ static void Parse (void) SymUseAttr (Sym, &Decl); /* Reserve storage for the variable if we need to */ - if (Decl.StorageClass & SC_STORAGE) { + if (Decl.StorageClass & SC_TU_STORAGE) { /* Get the size of the variable */ unsigned Size = SizeOf (Decl.Type); @@ -252,17 +269,19 @@ static void Parse (void) /* Parse the initialization */ ParseInit (Sym->Type); + } else { /* This is a declaration */ if (IsTypeVoid (Decl.Type)) { /* We cannot declare variables of type void */ Error ("Illegal type for variable '%s'", Decl.Ident); - Sym->Flags &= ~(SC_STORAGE | SC_DEF); + Sym->Flags |= SC_DEF; } else if (Size == 0 && SymIsDef (Sym) && !IsEmptiableObjectType (Decl.Type)) { /* Size is unknown. Is it an array? */ if (!IsTypeArray (Decl.Type)) { Error ("Variable '%s' has unknown size", Decl.Ident); + Sym->Flags |= SC_DEF; } } else { /* Check for enum forward declaration. @@ -286,11 +305,14 @@ static void Parse (void) */ const char* bssName = GetSegName (SEG_BSS); - if (Sym->V.BssName && strcmp (Sym->V.BssName, bssName) != 0) { - Error ("Global variable '%s' already was defined in the '%s' segment.", - Sym->Name, Sym->V.BssName); + if (Sym->V.BssName != 0) { + if (strcmp (Sym->V.BssName, bssName) != 0) { + Error ("Global variable '%s' already was defined in the '%s' segment", + Sym->Name, Sym->V.BssName); + } + } else { + Sym->V.BssName = xstrdup (bssName); } - Sym->V.BssName = xstrdup (bssName); /* This is to make the automatical zeropage setting of the symbol ** work right. @@ -300,45 +322,63 @@ static void Parse (void) } /* Make the symbol zeropage according to the segment address size */ - if ((Sym->Flags & SC_STATIC) != 0) { - if (GetSegAddrSize (GetSegName (CS->CurDSeg)) == ADDR_SIZE_ZP) { - Sym->Flags |= SC_ZEROPAGE; + if ((Sym->Flags & SC_TYPEMASK) == SC_NONE) { + if (SymIsGlobal (Sym) || + (Sym->Flags & SC_STORAGEMASK) == SC_STATIC || + (Sym->Flags & SC_STORAGEMASK) == SC_REGISTER) { + if (GetSegAddrSize (GetSegName (CS->CurDSeg)) == ADDR_SIZE_ZP) { + Sym->Flags |= SC_ZEROPAGE; + } } } } /* Check for end of declaration list */ - if (CurTok.Tok == TOK_COMMA) { - NextToken (); - comma = 1; - } else { + if (CurTok.Tok != TOK_COMMA) { break; } + Comma = 1; + Spec.Flags |= DS_NO_EMPTY_DECL; + NextToken (); + } + + /* Finish the declaration */ + if (Sym && IsTypeFunc (Sym->Type) && CurTok.Tok == TOK_LCURLY) { + /* A function definition is not terminated with a semicolon */ + if (IsTypeFunc (Spec.Type) && TypeCmp (Sym->Type, Spec.Type).C >= TC_EQUAL) { + /* ISO C: The type category in a function definition cannot be + ** inherited from a typedef. + */ + Error ("Function cannot be defined with a typedef"); + } else if (Comma) { + /* ISO C: A function definition cannot shall its return type + ** specifier with other declarators. + */ + Error ("';' expected after top level declarator"); + } + + /* Parse the function body anyways */ + NeedClean = 0; + NewFunc (Sym, FuncDef); + + /* Make sure we aren't omitting any work */ + CheckDeferredOpAllDone (); + } else if (NeedClean > 0) { + /* Must be followed by a semicolon */ + if (CurTok.Tok != TOK_SEMI) { + Error ("',' or ';' expected after top level declarator"); + NeedClean = -1; + } else { + NextToken (); + NeedClean = 0; + } } - /* Function declaration? */ - if (Sym && IsTypeFunc (Sym->Type)) { - - /* Function */ - if (!comma) { - if (CurTok.Tok == TOK_SEMI) { - /* Prototype only */ - NextToken (); - } else { - /* Parse the function body */ - NewFunc (Sym, FuncDef); - - /* Make sure we aren't omitting any work */ - CheckDeferredOpAllDone (); - } - } - - } else { - - /* Must be followed by a semicolon */ - ConsumeSemi (); - +EndOfDecl: + /* Try some smart error recovery */ + if (NeedClean < 0) { + SmartErrorSkip (1); } } @@ -476,7 +516,10 @@ void Compile (const char* FileName) ** global variables. */ for (Entry = GetGlobalSymTab ()->SymHead; Entry; Entry = Entry->NextSym) { - if ((Entry->Flags & (SC_STORAGE | SC_DEF | SC_STATIC)) == (SC_STORAGE | SC_STATIC)) { + /* Is it a global (with or without static) tentative declaration of + ** an uninitialized variable? + */ + if ((Entry->Flags & (SC_TU_STORAGE | SC_DEF)) == SC_TU_STORAGE) { /* Assembly definition of uninitialized global variable */ SymEntry* TagSym = GetESUTagSym (Entry->Type); unsigned Size = SizeOf (Entry->Type); @@ -505,12 +548,22 @@ void Compile (const char* FileName) /* Mark as defined; so that it will be exported, not imported */ Entry->Flags |= SC_DEF; - } else { + } else if (!IsTypeArray (Entry->Type)) { /* Tentative declared variable is still of incomplete type */ - Error ("Definition of '%s' has type '%s' that is never completed", + Error ("Definition of '%s' never has its type '%s' completed", Entry->Name, GetFullTypeName (Entry->Type)); } + } else if (!SymIsDef (Entry) && (Entry->Flags & SC_TYPEMASK) == SC_FUNC) { + /* Check for undefined functions */ + if ((Entry->Flags & SC_STORAGEMASK) == SC_STATIC && SymIsRef (Entry)) { + Warning ("Static function '%s' used but never defined", + Entry->Name); + } else if ((Entry->Flags & SC_INLINE) != 0) { + Warning ("Inline function '%s' %s but never defined", + Entry->Name, + SymIsRef (Entry) ? "used" : "declared"); + } } } diff --git a/src/cc65/coptbool.c b/src/cc65/coptbool.c new file mode 100644 index 000000000..3a3b3fa7c --- /dev/null +++ b/src/cc65/coptbool.c @@ -0,0 +1,942 @@ +/*****************************************************************************/ +/* */ +/* coptbool.c */ +/* */ +/* Optimize boolean sequences */ +/* */ +/* */ +/* */ +/* (C) 2001-2012, Ullrich von Bassewitz */ +/* Roemerstrasse 52 */ +/* D-70794 Filderstadt */ +/* EMail: uz@cc65.org */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed 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. */ +/* */ +/*****************************************************************************/ + + + +/* cc65 */ +#include "codeent.h" +#include "codeinfo.h" +#include "error.h" +#include "coptbool.h" + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Table used to invert a condition, indexed by condition */ +static const unsigned char CmpInvertTab[] = { + CMP_NE, CMP_EQ, + CMP_LE, CMP_LT, CMP_GE, CMP_GT, + CMP_ULE, CMP_ULT, CMP_UGE, CMP_UGT +}; + + + +/*****************************************************************************/ +/* Helper functions */ +/*****************************************************************************/ + + + +static void ReplaceBranchCond (CodeSeg* S, unsigned I, cmp_t Cond) +/* Helper function for the replacement of routines that return a boolean +** followed by a conditional jump. Instead of the boolean value, the condition +** codes are evaluated directly. +** I is the index of the conditional branch, the sequence is already checked +** to be correct. +*/ +{ + CodeEntry* N; + CodeLabel* L; + + /* Get the entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Replace the conditional branch */ + switch (Cond) { + + case CMP_EQ: + CE_ReplaceOPC (E, OP65_JEQ); + break; + + case CMP_NE: + CE_ReplaceOPC (E, OP65_JNE); + break; + + case CMP_GT: + /* Replace by + ** beq @L + ** jpl Target + ** @L: ... + */ + if ((N = CS_GetNextEntry (S, I)) == 0) { + /* No such entry */ + Internal ("Invalid program flow"); + } + L = CS_GenLabel (S, N); + N = NewCodeEntry (OP65_BEQ, AM65_BRA, L->Name, L, E->LI); + CS_InsertEntry (S, N, I); + CE_ReplaceOPC (E, OP65_JPL); + break; + + case CMP_GE: + CE_ReplaceOPC (E, OP65_JPL); + break; + + case CMP_LT: + CE_ReplaceOPC (E, OP65_JMI); + break; + + case CMP_LE: + /* Replace by + ** jmi Target + ** jeq Target + */ + CE_ReplaceOPC (E, OP65_JMI); + L = E->JumpTo; + N = NewCodeEntry (OP65_JEQ, AM65_BRA, L->Name, L, E->LI); + CS_InsertEntry (S, N, I+1); + break; + + case CMP_UGT: + /* Replace by + ** beq @L + ** jcs Target + ** @L: ... + */ + if ((N = CS_GetNextEntry (S, I)) == 0) { + /* No such entry */ + Internal ("Invalid program flow"); + } + L = CS_GenLabel (S, N); + N = NewCodeEntry (OP65_BEQ, AM65_BRA, L->Name, L, E->LI); + CS_InsertEntry (S, N, I); + CE_ReplaceOPC (E, OP65_JCS); + break; + + case CMP_UGE: + CE_ReplaceOPC (E, OP65_JCS); + break; + + case CMP_ULT: + CE_ReplaceOPC (E, OP65_JCC); + break; + + case CMP_ULE: + /* Replace by + ** jcc Target + ** jeq Target + */ + CE_ReplaceOPC (E, OP65_JCC); + L = E->JumpTo; + N = NewCodeEntry (OP65_JEQ, AM65_BRA, L->Name, L, E->LI); + CS_InsertEntry (S, N, I+1); + break; + + default: + Internal ("Unknown jump condition: %d", Cond); + + } + +} + + + +/*****************************************************************************/ +/* Optimize bool comparison and transformer subroutines with branches */ +/*****************************************************************************/ + + + +unsigned OptBoolCmp (CodeSeg* S) +/* Search for calls to compare subroutines followed by a conditional branch +** and replace them by cheaper versions, since the branch means that the +** boolean value returned by these routines is not needed (we may also check +** that explicitly, but for the current code generator it is always true). +*/ +{ + unsigned Changes = 0; + + /* Walk over the entries */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + CodeEntry* N; + cmp_t Cond; + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Check for the sequence */ + if (E->OPC == OP65_JSR && + (Cond = FindTosCmpCond (E->Arg)) != CMP_INV && + (N = CS_GetNextEntry (S, I)) != 0 && + (N->Info & OF_ZBRA) != 0 && + !CE_HasLabel (N)) { + + /* The tos... functions will return a boolean value in a/x and + ** the Z flag says if this value is zero or not. We will call + ** a cheaper subroutine instead, one that does not return a + ** boolean value but only valid flags. Note: jeq jumps if + ** the condition is not met, jne jumps if the condition is met. + ** Invert the code if we jump on condition not met. + */ + if (GetBranchCond (N->OPC) == BC_EQ) { + /* Jumps if condition false, invert condition */ + Cond = CmpInvertTab [Cond]; + } + + /* Replace the subroutine call. */ + E = NewCodeEntry (OP65_JSR, AM65_ABS, "tosicmp", 0, E->LI); + CS_InsertEntry (S, E, I+1); + CS_DelEntry (S, I); + + /* Replace the conditional branch */ + ReplaceBranchCond (S, I+1, Cond); + + /* Remember, we had changes */ + ++Changes; + + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + + + +unsigned OptBoolTrans (CodeSeg* S) +/* Try to remove the call to boolean transformer routines where the call is +** not really needed and change following branch condition accordingly. +*/ +{ + unsigned Changes = 0; + + /* Walk over the entries */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + CodeEntry* N; + cmp_t Cond; + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Check for a boolean transformer */ + if (E->OPC == OP65_JSR && + (Cond = FindBoolCmpCond (E->Arg)) != CMP_INV && + (N = CS_GetNextEntry (S, I)) != 0 && + (N->Info & OF_ZBRA) != 0 && + (GetRegInfo (S, I + 2, PSTATE_Z) & PSTATE_Z) == 0) { + + /* Make the boolean transformer unnecessary by changing the + ** the conditional jump to evaluate the condition flags that + ** are set after the compare directly. Note: jeq jumps if + ** the condition is not met, jne jumps if the condition is met. + ** Invert the code if we jump on condition not met. + */ + if (GetBranchCond (N->OPC) == BC_EQ) { + /* Jumps if condition false, invert condition */ + Cond = CmpInvertTab [Cond]; + } + + /* Check if we can replace the code by something better */ + ReplaceBranchCond (S, I+1, Cond); + + /* Remove the call to the bool transformer */ + CS_DelEntry (S, I); + + /* Remember, we had changes */ + ++Changes; + + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + + + +unsigned OptBoolUnary (CodeSeg* S) +/* Try to remove the call to a bcastax/bnegax routines where the call is +** not really needed and change following branch condition accordingly. +*/ +{ + unsigned Changes = 0; + + /* Walk over the entries */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + CodeEntry* N; + cmp_t Cond; + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Check for a boolean transformer */ + if (E->OPC == OP65_JSR && + (Cond = FindBoolCmpCond (E->Arg)) != CMP_INV && + (N = CS_GetNextEntry (S, I)) != 0 && + (N->Info & OF_ZBRA) != 0) { + + /* Make the boolean transformer unnecessary by changing the + ** the conditional jump to evaluate the condition flags that + ** are set after the compare directly. Note: jeq jumps if + ** the condition is not met, jne jumps if the condition is met. + ** Invert the code if we jump on condition not met. + */ + if (GetBranchCond (N->OPC) == BC_EQ) { + /* Jumps if condition false, invert condition */ + Cond = CmpInvertTab [Cond]; + } + + /* Check if we can replace the code by something better */ + ReplaceBranchCond (S, I+1, Cond); + + /* Remove the call to the bool transformer */ + CS_DelEntry (S, I); + + /* Remember, we had changes */ + ++Changes; + + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + + + +/*****************************************************************************/ +/* Remove calls to the boolean cast/negation subroutines */ +/*****************************************************************************/ + + + +unsigned OptBoolUnary1 (CodeSeg* S) +/* Search for and remove cmp #0/bcastax/boolne following a bcastax/bnegax. +** Or search for and remove cmp #1/bnegax/booleq following a bcastax/bnegax +** and invert the bcastax/bnegax. +*/ +{ + unsigned Changes = 0; + int Neg = 0; + + /* Walk over the entries */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + CodeEntry* L[2]; + + /* Get next entry */ + L[0] = CS_GetEntry (S, I); + + /* Check for the sequence. + ** We allow the first entry to have labels. + */ + if (L[0]->OPC == OP65_JSR && + (L[1] = CS_GetNextEntry (S, I)) != 0 && + !CE_HasLabel (L[1])) { + if (strcmp (L[0]->Arg, "bnegax") == 0) { + Neg = 1; + } else if (strcmp (L[0]->Arg, "bcastax") == 0) { + Neg = 0; + } else { + /* Next entry */ + ++I; + continue; + } + if ((L[1]->OPC == OP65_CMP && CE_IsKnownImm (L[1], 0x0)) || + CE_IsCallTo (L[1], "boolne") || + CE_IsCallTo (L[1], "bcastax")) { + /* Delete the entry no longer needed. */ + CS_DelEntry (S, I + 1); + + /* Remember, we had changes */ + ++Changes; + + /* We are still at this index */ + continue; + + } else if ((L[1]->OPC == OP65_CMP && CE_IsKnownImm (L[1], 0x1)) || + CE_IsCallTo (L[1], "booleq") || + CE_IsCallTo (L[1], "bnegax")) { + /* Invert the previous bool conversion */ + CE_SetArg (L[0], Neg ? "bcastax" : "bnegax"); + + /* Delete the entry no longer needed */ + CS_DelEntry (S, I + 1); + + /* Remember, we had changes */ + ++Changes; + + /* We are still at this index */ + continue; + } + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + + + +unsigned OptBoolUnary2 (CodeSeg* S) +/* Search for and remove cmp #0/bcastax/boolne following a boolean transformer. +** Or search for and remove cmp #1/bnegax/booleq following a boolean transformer +** and invert the boolean transformer. +*/ +{ + unsigned Changes = 0; + cmp_t Cond; + char Buf[16]; + + /* Walk over the entries */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + CodeEntry* L[2]; + + /* Get next entry */ + L[0] = CS_GetEntry (S, I); + + /* Check for the sequence. + ** We allow the first entry to have labels. + */ + if (L[0]->OPC == OP65_JSR && + (L[1] = CS_GetNextEntry (S, I)) != 0 && + !CE_HasLabel (L[1]) && + (Cond = FindBoolCmpCond (L[0]->Arg)) != CMP_INV) { + if ((L[1]->OPC == OP65_CMP && CE_IsKnownImm (L[1], 0x0)) || + CE_IsCallTo (L[1], "boolne") || + CE_IsCallTo (L[1], "bcastax")) { + /* Delete the entry no longer needed */ + CS_DelEntry (S, I + 1); + + /* Remember, we had changes */ + ++Changes; + + /* We are still at this index */ + continue; + + } else if ((L[1]->OPC == OP65_CMP && CE_IsKnownImm (L[1], 0x1)) || + CE_IsCallTo (L[1], "booleq") || + CE_IsCallTo (L[1], "bnegax")) { + /* Invert the bool conversion */ + if (GetBoolCmpSuffix (Buf, GetNegatedCond (Cond)) == 0) { + Internal ("No inverted boolean transformer for: %s", L[0]->Arg); + } + CE_SetArg (L[0], Buf); + + /* Delete the entry no longer needed */ + CS_DelEntry (S, I + 1); + + /* Remember, we had changes */ + ++Changes; + + /* We are still at this index */ + continue; + } + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + + + +unsigned OptBoolUnary3 (CodeSeg* S) +/* If A == 0, replace bcastax/bnegax with +** +** cpx #0 +** jsr boolne/booleq +** +** Or if X == 0, replace bcastax/bnegax with +** +** cmp #0 +** jsr boolne/booleq +** +*/ +{ + unsigned Changes = 0; + opc_t Op = OP65_COUNT; + const char* Sub = 0; + + /* Walk over the entries */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + CodeEntry* E; + CodeEntry* X; + + /* Get next entry */ + E = CS_GetEntry (S, I); + + /* Check for the sequence */ + if (!CE_HasLabel (E)) { + /* Choose the right subroutine */ + if (CE_IsCallTo (E, "bnegax")) { + Sub = "booleq"; + } else if (CE_IsCallTo (E, "bcastax")) { + Sub = "boolne"; + } + /* Choose the right opcode */ + if (RegValIsKnown (E->RI->In.RegA) && E->RI->In.RegA == 0) { + Op = OP65_CPX; + } else if (RegValIsKnown (E->RI->In.RegX) && E->RI->In.RegX == 0) { + Op = OP65_CMP; + } + /* Replace the sequence if all requirements are met*/ + if (Op != OP65_COUNT && Sub != 0) { + /* Replace bcastax/bnegax with boolne/booleq */ + CE_SetArg (E, Sub); + + /* Insert the compare */ + X = NewCodeEntry (Op, AM65_IMM, "$00", 0, E->LI); + CS_InsertEntry (S, X, I); + + /* Remember, we had changes */ + ++Changes; + + /* Correct the index */ + ++I; + } + + /* Reset the choices */ + Op = OP65_COUNT; + Sub = 0; + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + + + +/*****************************************************************************/ +/* bnega optimizations */ +/*****************************************************************************/ + + + +unsigned OptBNegA1 (CodeSeg* S) +/* Check for +** +** ldx #$00 +** lda .. +** jsr bnega +** +** Remove the ldx if the lda does not use it. +*/ +{ + unsigned Changes = 0; + + /* Walk over the entries */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + CodeEntry* L[2]; + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Check for a ldx */ + if (E->OPC == OP65_LDX && + E->AM == AM65_IMM && + CE_HasNumArg (E) && + E->Num == 0 && + CS_GetEntries (S, L, I+1, 2) && + L[0]->OPC == OP65_LDA && + (L[0]->Use & REG_X) == 0 && + !CE_HasLabel (L[0]) && + CE_IsCallTo (L[1], "bnega") && + !CE_HasLabel (L[1])) { + + /* Remove the ldx instruction */ + CS_DelEntry (S, I); + + /* Remember, we had changes */ + ++Changes; + + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + + + +unsigned OptBNegA2 (CodeSeg* S) +/* Check for +** +** lda .. +** jsr bnega +** jeq/jne .. +** +** Adjust the conditional branch and remove the call to the subroutine. +*/ +{ + unsigned Changes = 0; + + /* Walk over the entries */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + CodeEntry* L[2]; + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Check for the sequence */ + if ((E->OPC == OP65_ADC || + E->OPC == OP65_AND || + E->OPC == OP65_DEA || + E->OPC == OP65_EOR || + E->OPC == OP65_INA || + E->OPC == OP65_LDA || + E->OPC == OP65_ORA || + E->OPC == OP65_PLA || + E->OPC == OP65_SBC || + E->OPC == OP65_TXA || + E->OPC == OP65_TYA) && + CS_GetEntries (S, L, I+1, 2) && + CE_IsCallTo (L[0], "bnega") && + !CE_HasLabel (L[0]) && + (L[1]->Info & OF_ZBRA) != 0 && + !CE_HasLabel (L[1]) && + (GetRegInfo (S, I + 3, PSTATE_Z) & PSTATE_Z) == 0) { + + /* Invert the branch */ + CE_ReplaceOPC (L[1], GetInverseBranch (L[1]->OPC)); + + /* Delete the subroutine call */ + CS_DelEntry (S, I+1); + + /* Remember, we had changes */ + ++Changes; + + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + + + +/*****************************************************************************/ +/* bnegax optimizations */ +/*****************************************************************************/ + + + +unsigned OptBNegAX1 (CodeSeg* S) +/* On a call to bnegax, if X is zero, the result depends only on the value in +** A, so change the call to a call to bnega. This will get further optimized +** later if possible. +*/ +{ + unsigned Changes = 0; + unsigned I; + + /* Walk over the entries */ + I = 0; + while (I < CS_GetEntryCount (S)) { + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Check if this is a call to bnegax, and if X is known and zero */ + if (E->RI->In.RegX == 0 && CE_IsCallTo (E, "bnegax")) { + + CodeEntry* X = NewCodeEntry (OP65_JSR, AM65_ABS, "bnega", 0, E->LI); + CS_InsertEntry (S, X, I+1); + CS_DelEntry (S, I); + + /* We had changes */ + ++Changes; + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + + + +unsigned OptBNegAX2 (CodeSeg* S) +/* Search for the sequence: +** +** ldy #xx +** jsr ldaxysp +** jsr bnegax +** jne/jeq ... +** +** and replace it by +** +** ldy #xx +** lda (sp),y +** dey +** ora (sp),y +** jeq/jne ... +*/ +{ + unsigned Changes = 0; + + /* Walk over the entries */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + CodeEntry* L[4]; + + /* Get next entry */ + L[0] = CS_GetEntry (S, I); + + /* Check for the sequence */ + if (L[0]->OPC == OP65_LDY && + CE_IsConstImm (L[0]) && + !CS_RangeHasLabel (S, I+1, 3) && + CS_GetEntries (S, L+1, I+1, 3) && + CE_IsCallTo (L[1], "ldaxysp") && + CE_IsCallTo (L[2], "bnegax") && + (L[3]->Info & OF_ZBRA) != 0 && + (GetRegInfo (S, I + 4, PSTATE_Z) & PSTATE_Z) == 0) { + + CodeEntry* X; + + /* lda (sp),y */ + X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "sp", 0, L[1]->LI); + CS_InsertEntry (S, X, I+1); + + /* dey */ + X = NewCodeEntry (OP65_DEY, AM65_IMP, 0, 0, L[1]->LI); + CS_InsertEntry (S, X, I+2); + + /* ora (sp),y */ + X = NewCodeEntry (OP65_ORA, AM65_ZP_INDY, "sp", 0, L[1]->LI); + CS_InsertEntry (S, X, I+3); + + /* Invert the branch */ + CE_ReplaceOPC (L[3], GetInverseBranch (L[3]->OPC)); + + /* Delete the entries no longer needed. */ + CS_DelEntries (S, I+4, 2); + + /* Remember, we had changes */ + ++Changes; + + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + + + +unsigned OptBNegAX3 (CodeSeg* S) +/* Search for the sequence: +** +** lda xx +** ldx yy +** jsr bnegax +** jne/jeq ... +** +** and replace it by +** +** lda xx +** ora xx+1 +** jeq/jne ... +*/ +{ + unsigned Changes = 0; + + /* Walk over the entries */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + CodeEntry* L[3]; + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Check for the sequence */ + if (E->OPC == OP65_LDA && + CS_GetEntries (S, L, I+1, 3) && + L[0]->OPC == OP65_LDX && + !CE_HasLabel (L[0]) && + CE_IsCallTo (L[1], "bnegax") && + !CE_HasLabel (L[1]) && + (L[2]->Info & OF_ZBRA) != 0 && + !CE_HasLabel (L[2]) && + (GetRegInfo (S, I + 4, PSTATE_Z) & PSTATE_Z) == 0) { + + /* ldx --> ora */ + CE_ReplaceOPC (L[0], OP65_ORA); + + /* Invert the branch */ + CE_ReplaceOPC (L[2], GetInverseBranch (L[2]->OPC)); + + /* Delete the subroutine call */ + CS_DelEntry (S, I+2); + + /* Remember, we had changes */ + ++Changes; + + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + + + +unsigned OptBNegAX4 (CodeSeg* S) +/* Search for the sequence: +** +** jsr xxx +** jsr bnega(x) +** jeq/jne ... +** +** and replace it by: +** +** jsr xxx +** <boolean test> +** jne/jeq ... +*/ +{ + unsigned Changes = 0; + + /* Walk over the entries */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + CodeEntry* L[2]; + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Check for the sequence */ + if (E->OPC == OP65_JSR && + CS_GetEntries (S, L, I+1, 2) && + L[0]->OPC == OP65_JSR && + strncmp (L[0]->Arg,"bnega",5) == 0 && + !CE_HasLabel (L[0]) && + (L[1]->Info & OF_ZBRA) != 0 && + !CE_HasLabel (L[1]) && + (GetRegInfo (S, I + 3, PSTATE_Z) & PSTATE_Z) == 0) { + + CodeEntry* X; + + /* Check if we're calling bnega or bnegax */ + int ByteSized = (strcmp (L[0]->Arg, "bnega") == 0); + + /* Insert apropriate test code */ + if (ByteSized) { + /* Test bytes */ + X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, L[0]->LI); + CS_InsertEntry (S, X, I+2); + } else { + /* Test words */ + X = NewCodeEntry (OP65_STX, AM65_ZP, "tmp1", 0, L[0]->LI); + CS_InsertEntry (S, X, I+2); + X = NewCodeEntry (OP65_ORA, AM65_ZP, "tmp1", 0, L[0]->LI); + CS_InsertEntry (S, X, I+3); + } + + /* Delete the subroutine call */ + CS_DelEntry (S, I+1); + + /* Invert the branch */ + CE_ReplaceOPC (L[1], GetInverseBranch (L[1]->OPC)); + + /* Remember, we had changes */ + ++Changes; + + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} diff --git a/src/cc65/coptneg.h b/src/cc65/coptbool.h similarity index 77% rename from src/cc65/coptneg.h rename to src/cc65/coptbool.h index 844d8b886..19554482e 100644 --- a/src/cc65/coptneg.h +++ b/src/cc65/coptbool.h @@ -1,8 +1,8 @@ /*****************************************************************************/ /* */ -/* coptneg.h */ +/* coptbool.h */ /* */ -/* Optimize negation sequences */ +/* Optimize boolean sequences */ /* */ /* */ /* */ @@ -33,8 +33,8 @@ -#ifndef COPTNEG_H -#define COPTNEG_H +#ifndef COPTBOOL_H +#define COPTBOOL_H @@ -43,6 +43,56 @@ +/*****************************************************************************/ +/* Optimize bool comparison and transformer subroutines */ +/*****************************************************************************/ + + + +unsigned OptBoolCmp (CodeSeg* S); +/* Search for calls to compare subroutines followed by a conditional branch +** and replace them by cheaper versions, since the branch means that the +** boolean value returned by these routines is not needed (we may also check +** that explicitly, but for the current code generator it is always true). +*/ + +unsigned OptBoolTrans (CodeSeg* S); +/* Try to remove the call to boolean transformer routines where the call is +** not really needed and change following branch condition accordingly. +*/ + + + +/*****************************************************************************/ +/* Remove calls to the boolean cast/negation subroutines */ +/*****************************************************************************/ + + + +unsigned OptBoolUnary1 (CodeSeg* S); +/* Search for and remove bcastax adjacent to bnegax */ + +unsigned OptBoolUnary2 (CodeSeg* S); +/* Search for and remove bcastax/bnegax following a boolean transformer. +** Invert the boolean transformer if it is bnegax to be removed. +*/ + +unsigned OptBoolUnary3 (CodeSeg* S); +/* Replace bcastax/bnegax with +** +** cpx #0 +** jsr boolne/booleq +** +** if A == 0, or replace bcastax/bnegax with +** +** cmp #0 +** jsr boolne/booleq +** +** if X == 0. +*/ + + + /*****************************************************************************/ /* bnega optimizations */ /*****************************************************************************/ @@ -132,54 +182,6 @@ unsigned OptBNegAX4 (CodeSeg* S); -/*****************************************************************************/ -/* negax optimizations */ -/*****************************************************************************/ - - - -unsigned OptNegAX1 (CodeSeg* S); -/* Search for a call to negax and replace it by -** -** eor #$FF -** clc -** adc #$01 -** -** if X isn't used later. -*/ - -unsigned OptNegAX2 (CodeSeg* S); -/* Search for a call to negax and replace it by -** -** ldx #$FF -** eor #$FF -** clc -** adc #$01 -** bcc L1 -** inx -** L1: -** -** if X is known and zero on entry. -*/ - - - -/*****************************************************************************/ -/* complax optimizations */ -/*****************************************************************************/ - - - -unsigned OptComplAX1 (CodeSeg* S); -/* Search for a call to complax and replace it by -** -** eor #$FF -** -** if X isn't used later. -*/ - - - -/* End of coptneg.h */ +/* End of coptbool.h */ #endif diff --git a/src/cc65/coptcmp.c b/src/cc65/coptcmp.c index 92401a858..2970b363b 100644 --- a/src/cc65/coptcmp.c +++ b/src/cc65/coptcmp.c @@ -43,131 +43,12 @@ -/*****************************************************************************/ -/* Data */ -/*****************************************************************************/ - - - -/* Table used to invert a condition, indexed by condition */ -static const unsigned char CmpInvertTab [] = { - CMP_NE, CMP_EQ, - CMP_LE, CMP_LT, CMP_GE, CMP_GT, - CMP_ULE, CMP_ULT, CMP_UGE, CMP_UGT -}; - - - /*****************************************************************************/ /* Helper functions */ /*****************************************************************************/ -static void ReplaceCmp (CodeSeg* S, unsigned I, cmp_t Cond) -/* Helper function for the replacement of routines that return a boolean -** followed by a conditional jump. Instead of the boolean value, the condition -** codes are evaluated directly. -** I is the index of the conditional branch, the sequence is already checked -** to be correct. -*/ -{ - CodeEntry* N; - CodeLabel* L; - - /* Get the entry */ - CodeEntry* E = CS_GetEntry (S, I); - - /* Replace the conditional branch */ - switch (Cond) { - - case CMP_EQ: - CE_ReplaceOPC (E, OP65_JEQ); - break; - - case CMP_NE: - CE_ReplaceOPC (E, OP65_JNE); - break; - - case CMP_GT: - /* Replace by - ** beq @L - ** jpl Target - ** @L: ... - */ - if ((N = CS_GetNextEntry (S, I)) == 0) { - /* No such entry */ - Internal ("Invalid program flow"); - } - L = CS_GenLabel (S, N); - N = NewCodeEntry (OP65_BEQ, AM65_BRA, L->Name, L, E->LI); - CS_InsertEntry (S, N, I); - CE_ReplaceOPC (E, OP65_JPL); - break; - - case CMP_GE: - CE_ReplaceOPC (E, OP65_JPL); - break; - - case CMP_LT: - CE_ReplaceOPC (E, OP65_JMI); - break; - - case CMP_LE: - /* Replace by - ** jmi Target - ** jeq Target - */ - CE_ReplaceOPC (E, OP65_JMI); - L = E->JumpTo; - N = NewCodeEntry (OP65_JEQ, AM65_BRA, L->Name, L, E->LI); - CS_InsertEntry (S, N, I+1); - break; - - case CMP_UGT: - /* Replace by - ** beq @L - ** jcs Target - ** @L: ... - */ - if ((N = CS_GetNextEntry (S, I)) == 0) { - /* No such entry */ - Internal ("Invalid program flow"); - } - L = CS_GenLabel (S, N); - N = NewCodeEntry (OP65_BEQ, AM65_BRA, L->Name, L, E->LI); - CS_InsertEntry (S, N, I); - CE_ReplaceOPC (E, OP65_JCS); - break; - - case CMP_UGE: - CE_ReplaceOPC (E, OP65_JCS); - break; - - case CMP_ULT: - CE_ReplaceOPC (E, OP65_JCC); - break; - - case CMP_ULE: - /* Replace by - ** jcc Target - ** jeq Target - */ - CE_ReplaceOPC (E, OP65_JCC); - L = E->JumpTo; - N = NewCodeEntry (OP65_JEQ, AM65_BRA, L->Name, L, E->LI); - CS_InsertEntry (S, N, I+1); - break; - - default: - Internal ("Unknown jump condition: %d", Cond); - - } - -} - - - static int IsImmCmp16 (CodeEntry** L) /* Check if the instructions at L are an immediate compare of a/x: ** @@ -205,68 +86,6 @@ static int GetCmpRegVal (const CodeEntry* E) -/*****************************************************************************/ -/* Remove calls to the bool transformer subroutines */ -/*****************************************************************************/ - - - -unsigned OptBoolTrans (CodeSeg* S) -/* Try to remove the call to boolean transformer routines where the call is -** not really needed. -*/ -{ - unsigned Changes = 0; - - /* Walk over the entries */ - unsigned I = 0; - while (I < CS_GetEntryCount (S)) { - - CodeEntry* N; - cmp_t Cond; - - /* Get next entry */ - CodeEntry* E = CS_GetEntry (S, I); - - /* Check for a boolean transformer */ - if (E->OPC == OP65_JSR && - (Cond = FindBoolCmpCond (E->Arg)) != CMP_INV && - (N = CS_GetNextEntry (S, I)) != 0 && - (N->Info & OF_ZBRA) != 0) { - - /* Make the boolean transformer unnecessary by changing the - ** the conditional jump to evaluate the condition flags that - ** are set after the compare directly. Note: jeq jumps if - ** the condition is not met, jne jumps if the condition is met. - ** Invert the code if we jump on condition not met. - */ - if (GetBranchCond (N->OPC) == BC_EQ) { - /* Jumps if condition false, invert condition */ - Cond = CmpInvertTab [Cond]; - } - - /* Check if we can replace the code by something better */ - ReplaceCmp (S, I+1, Cond); - - /* Remove the call to the bool transformer */ - CS_DelEntry (S, I); - - /* Remember, we had changes */ - ++Changes; - - } - - /* Next entry */ - ++I; - - } - - /* Return the number of changes made */ - return Changes; -} - - - /*****************************************************************************/ /* Optimizations for compares */ /*****************************************************************************/ @@ -684,68 +503,6 @@ unsigned OptCmp5 (CodeSeg* S) -unsigned OptCmp6 (CodeSeg* S) -/* Search for calls to compare subroutines followed by a conditional branch -** and replace them by cheaper versions, since the branch means that the -** boolean value returned by these routines is not needed (we may also check -** that explicitly, but for the current code generator it is always true). -*/ -{ - unsigned Changes = 0; - - /* Walk over the entries */ - unsigned I = 0; - while (I < CS_GetEntryCount (S)) { - - CodeEntry* N; - cmp_t Cond; - - /* Get next entry */ - CodeEntry* E = CS_GetEntry (S, I); - - /* Check for the sequence */ - if (E->OPC == OP65_JSR && - (Cond = FindTosCmpCond (E->Arg)) != CMP_INV && - (N = CS_GetNextEntry (S, I)) != 0 && - (N->Info & OF_ZBRA) != 0 && - !CE_HasLabel (N)) { - - /* The tos... functions will return a boolean value in a/x and - ** the Z flag says if this value is zero or not. We will call - ** a cheaper subroutine instead, one that does not return a - ** boolean value but only valid flags. Note: jeq jumps if - ** the condition is not met, jne jumps if the condition is met. - ** Invert the code if we jump on condition not met. - */ - if (GetBranchCond (N->OPC) == BC_EQ) { - /* Jumps if condition false, invert condition */ - Cond = CmpInvertTab [Cond]; - } - - /* Replace the subroutine call. */ - E = NewCodeEntry (OP65_JSR, AM65_ABS, "tosicmp", 0, E->LI); - CS_InsertEntry (S, E, I+1); - CS_DelEntry (S, I); - - /* Replace the conditional branch */ - ReplaceCmp (S, I+1, Cond); - - /* Remember, we had changes */ - ++Changes; - - } - - /* Next entry */ - ++I; - - } - - /* Return the number of changes made */ - return Changes; -} - - - unsigned OptCmp7 (CodeSeg* S) /* Search for a sequence ldx/txa/branch and remove the txa if A is not ** used later. diff --git a/src/cc65/coptcmp.h b/src/cc65/coptcmp.h index 0cdcf2d3d..dd188f7fc 100644 --- a/src/cc65/coptcmp.h +++ b/src/cc65/coptcmp.h @@ -43,19 +43,6 @@ -/*****************************************************************************/ -/* Remove calls to the bool transformer subroutines */ -/*****************************************************************************/ - - - -unsigned OptBoolTrans (CodeSeg* S); -/* Try to remove the call to boolean transformer routines where the call is -** not really needed. -*/ - - - /*****************************************************************************/ /* Optimizations for compares */ /*****************************************************************************/ @@ -136,13 +123,6 @@ unsigned OptCmp5 (CodeSeg* S); ** jne/jeq L2 */ -unsigned OptCmp6 (CodeSeg* S); -/* Search for calls to compare subroutines followed by a conditional branch -** and replace them by cheaper versions, since the branch means that the -** boolean value returned by these routines is not needed (we may also check -** that explicitly, but for the current code generator it is always true). -*/ - unsigned OptCmp7 (CodeSeg* S); /* Search for a sequence ldx/txa/branch and remove the txa if A is not ** used later. diff --git a/src/cc65/coptind.c b/src/cc65/coptind.c index a080cfb20..52c47481e 100644 --- a/src/cc65/coptind.c +++ b/src/cc65/coptind.c @@ -151,7 +151,9 @@ static short ZPRegVal (unsigned short Use, const RegContents* RC) unsigned OptUnusedLoads (CodeSeg* S) -/* Remove loads of registers where the value loaded is not used later. */ +/* Remove loads of or operations with registers where the value loaded or +** produced is not used later. +*/ { unsigned Changes = 0; @@ -164,17 +166,24 @@ unsigned OptUnusedLoads (CodeSeg* S) /* Get next entry */ CodeEntry* E = CS_GetEntry (S, I); - /* Check if it's a register load or transfer insn */ - if ((E->Info & (OF_LOAD | OF_XFR | OF_REG_INCDEC)) != 0 && - (N = CS_GetNextEntry (S, I)) != 0 && - !CE_UseLoadFlags (N)) { + /* Check if this is one of the instruction we can operate on */ + int IsOp = (E->Info & (OF_LOAD | OF_XFR | OF_REG_INCDEC)) != 0 || + E->OPC == OP65_AND || + E->OPC == OP65_EOR || + E->OPC == OP65_ORA; + + /* Check for the necessary preconditions */ + if (IsOp && (N = CS_GetNextEntry (S, I)) != 0 && !LoadFlagsUsed (S, I+1)) { /* Check which sort of load or transfer it is */ unsigned R; switch (E->OPC) { + case OP65_AND: case OP65_DEA: + case OP65_EOR: case OP65_INA: case OP65_LDA: + case OP65_ORA: case OP65_TXA: case OP65_TYA: R = REG_A; break; case OP65_DEX: @@ -581,6 +590,60 @@ unsigned OptStoreLoad (CodeSeg* S) +unsigned OptLoadStoreLoad (CodeSeg* S) +/* Search for the sequence +** +** ld. xx +** st. yy +** ld. xx +** +** and remove the useless load. +*/ +{ + unsigned Changes = 0; + + /* Walk over the entries */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + CodeEntry* L[3]; + + /* Get next entry */ + L[0] = CS_GetEntry (S, I); + + /* Check for the sequence */ + if ((L[0]->OPC == OP65_LDA || + L[0]->OPC == OP65_LDX || + L[0]->OPC == OP65_LDY) && + (L[0]->AM == AM65_ABS || L[0]->AM == AM65_ZP) && + !CS_RangeHasLabel (S, I+1, 3) && + CS_GetEntries (S, L+1, I+1, 2) && + (L[1]->OPC == OP65_STA || + L[1]->OPC == OP65_STX || + L[1]->OPC == OP65_STY) && + L[2]->OPC == L[0]->OPC && + L[2]->AM == L[0]->AM && + strcmp (L[0]->Arg, L[2]->Arg) == 0) { + + /* Remove the second load */ + CS_DelEntries (S, I+2, 1); + + /* Remember, we had changes */ + ++Changes; + + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + + + unsigned OptTransfers1 (CodeSeg* S) /* Remove transfers from one register to another and back */ { @@ -1288,6 +1351,97 @@ unsigned OptPushPop2 (CodeSeg* S) +unsigned OptPushPop3 (CodeSeg* S) +/* Remove a pha/pla sequence where the contents of A are known */ +{ + unsigned Changes = 0; + unsigned Pha = 0; /* Index of PHA insn */ + unsigned Pla = 0; /* Index of PLA insn */ + CodeEntry* PhaEntry = 0; /* Pointer to PHA */ + + enum { + Searching, + FoundPha, + FoundPla + } State = Searching; + + /* Walk over the entries. Look for a PHA instruction where the contents + ** of A is known followed by a PLA later. + */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Get the input registers */ + const RegInfo* RI = E->RI; + + + const char* Arg; + CodeEntry* X; + switch (State) { + + case Searching: + if (E->OPC == OP65_PHA && RegValIsKnown (RI->In.RegA)) { + /* Found start of sequence */ + Pha = I; + PhaEntry = E; + State = FoundPha; + } + break; + + case FoundPha: + /* Check for several things that abort the sequence: + ** - End of the basic block + ** - Another PHA or any other stack manipulating instruction + ** If we find something that aborts the sequence, start over + ** searching for the next PHA. + */ + if (CE_HasLabel (E)) { + /* Switch back to searching at this instruction */ + State = Searching; + continue; + } + if (E->OPC == OP65_PHA) { + /* Start over at this instruction */ + State = Searching; + continue; + } + if (E->OPC == OP65_PHP || E->OPC == OP65_PLP || E->OPC == OP65_TXS) { + /* Start over at the next instruction */ + State = Searching; + } else if (E->OPC == OP65_PLA) { + /* Switch state. This will also switch to the next insn + ** which is ok. + */ + Pla = I; + State = FoundPla; + } + break; + + case FoundPla: + /* We found the sequence we were looking for. Replace it. */ + Arg = MakeHexArg (PhaEntry->RI->In.RegA); + X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI); + CS_InsertEntry (S, X, Pla + 1); + CS_DelEntry (S, Pla); + CS_DelEntry (S, Pha); + ++Changes; + State = Searching; + break; + } + + /* Next entry */ + ++I; + } + + /* Return the number of changes made */ + return Changes; +} + + + unsigned OptPrecalc (CodeSeg* S) /* Replace immediate operations with the accu where the current contents are ** known by a load of the final value. diff --git a/src/cc65/coptind.h b/src/cc65/coptind.h index c7ecf4194..3493543a4 100644 --- a/src/cc65/coptind.h +++ b/src/cc65/coptind.h @@ -50,7 +50,9 @@ unsigned OptUnusedLoads (CodeSeg* S); -/* Remove loads of registers where the value loaded is not used later. */ +/* Remove loads of or operations with registers where the value loaded or +** produced is not used later. +*/ unsigned OptUnusedStores (CodeSeg* S); /* Remove stores into zero page registers that aren't used later */ @@ -64,6 +66,9 @@ unsigned OptDupLoads (CodeSeg* S); unsigned OptStoreLoad (CodeSeg* S); /* Remove a store followed by a load from the same location. */ +unsigned OptLoadStoreLoad (CodeSeg* S); +/* Remove a load, store followed by a reload of the same location. */ + unsigned OptTransfers1 (CodeSeg* S); /* Remove transfers from one register to another and back */ @@ -88,6 +93,9 @@ unsigned OptPushPop1 (CodeSeg* S); unsigned OptPushPop2 (CodeSeg* S); /* Remove a PHP/PLP sequence were no processor flags changed inside */ +unsigned OptPushPop3 (CodeSeg* S); +/* Remove a pha/pla sequence where the contents of A are known */ + unsigned OptPrecalc (CodeSeg* S); /* Replace immediate operations with the accu where the current contents are ** known by a load of the final value. diff --git a/src/cc65/coptjmp.c b/src/cc65/coptjmp.c index 9dd4a29c5..e0b53ad91 100644 --- a/src/cc65/coptjmp.c +++ b/src/cc65/coptjmp.c @@ -898,17 +898,13 @@ unsigned OptJumpTarget3 (CodeSeg* S) -unsigned OptCondBranches1 (CodeSeg* S) -/* Performs several optimization steps: -** +unsigned OptCondBranch1 (CodeSeg* S) +/* Performs some optimization steps: ** - If an immediate load of a register is followed by a conditional jump that ** is never taken because the load of the register sets the flags in such a ** manner, remove the conditional branch. ** - If the conditional branch is always taken because of the register load, ** replace it by a jmp. -** - If a conditional branch jumps around an unconditional branch, remove the -** conditional branch and make the jump a conditional branch with the -** inverse condition of the first one. */ { unsigned Changes = 0; @@ -918,7 +914,6 @@ unsigned OptCondBranches1 (CodeSeg* S) while (I < CS_GetEntryCount (S)) { CodeEntry* N; - CodeLabel* L; /* Get next entry */ CodeEntry* E = CS_GetEntry (S, I); @@ -960,6 +955,35 @@ unsigned OptCondBranches1 (CodeSeg* S) } + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + + + +unsigned OptCondBranch2 (CodeSeg* S) +/* If a conditional branch jumps around an unconditional branch, remove the +** conditional branch and make the jump a conditional branch with the inverse +** condition of the first one. +*/ +{ + unsigned Changes = 0; + + /* Walk over the entries */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + CodeEntry* N; + CodeLabel* L; + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + if ((E->Info & OF_CBRA) != 0 && /* It's a conditional branch */ (L = E->JumpTo) != 0 && /* ..referencing a local label */ (N = CS_GetNextEntry (S, I)) != 0 && /* There is a following entry */ @@ -991,7 +1015,51 @@ unsigned OptCondBranches1 (CodeSeg* S) -unsigned OptCondBranches2 (CodeSeg* S) +unsigned OptCondBranch3 (CodeSeg* S) +/* If the conditional branch is always taken because it follows an inverse +** conditional branch, replace it by a jmp. +*/ +{ + unsigned Changes = 0; + + /* Walk over the entries */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + CodeEntry* N; + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Check if it's a conditional branch */ + if ((E->Info & OF_CBRA) != 0 && /* It's a conditional branch */ + (N = CS_GetNextEntry (S, I)) != 0 && /* There is a following entry */ + (N->Info & OF_CBRA) != 0 && /* ..which is a conditional branch */ + !CE_HasLabel (N)) { /* ..and does not have a label */ + + /* Check if the branches conditions are inverse of each other */ + if (GetInverseCond (GetBranchCond (N->OPC)) == GetBranchCond (E->OPC)) { + /* The branch is always taken, replace it by a jump */ + CE_ReplaceOPC (N, OP65_JMP); + + /* Remember, we had changes */ + ++Changes; + } + + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + + + +unsigned OptCondBranchC (CodeSeg* S) /* If on entry to a "rol a" instruction the accu is zero, and a beq/bne follows, ** we can remove the rol and branch on the state of the carry flag. */ diff --git a/src/cc65/coptjmp.h b/src/cc65/coptjmp.h index 8df53415d..194117729 100644 --- a/src/cc65/coptjmp.h +++ b/src/cc65/coptjmp.h @@ -101,13 +101,27 @@ unsigned OptJumpTarget3 (CodeSeg* S); ** done. */ -unsigned OptCondBranches1 (CodeSeg* S); -/* If an immidiate load of a register is followed by a conditional jump that -** is never taken because the load of the register sets the flags in such a -** manner, remove the conditional branch. +unsigned OptCondBranch1 (CodeSeg* S); +/* Performs some optimization steps: +** - If an immediate load of a register is followed by a conditional jump that +** is never taken because the load of the register sets the flags in such a +** manner, remove the conditional branch. +** - If the conditional branch is always taken because of the register load, +** replace it by a jmp. */ -unsigned OptCondBranches2 (CodeSeg* S); +unsigned OptCondBranch2 (CodeSeg* S); +/* If a conditional branch jumps around an unconditional branch, remove the +** conditional branch and make the jump a conditional branch with the inverse +** condition of the first one. +*/ + +unsigned OptCondBranch3 (CodeSeg* S); +/* If the conditional branch is always taken because it follows an inverse +** conditional branch, replace it by a jmp. +*/ + +unsigned OptCondBranchC (CodeSeg* S); /* If on entry to a "rol a" instruction the accu is zero, and a beq/bne follows, ** we can remove the rol and branch on the state of the carry. */ diff --git a/src/cc65/coptlong.c b/src/cc65/coptlong.c new file mode 100644 index 000000000..23c30875a --- /dev/null +++ b/src/cc65/coptlong.c @@ -0,0 +1,239 @@ +/*****************************************************************************/ +/* */ +/* coptlong.c */ +/* */ +/* Long integers optimizations */ +/* */ +/* */ +/* */ +/* (C) 2001-2009, Ullrich von Bassewitz */ +/* Roemerstrasse 52 */ +/* D-70794 Filderstadt */ +/* EMail: uz@cc65.org */ +/* (C) 2023, Colin Leroy-Mira <colin@colino.net */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed 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. */ +/* */ +/*****************************************************************************/ + + + +/* common */ +#include "cpu.h" + +/* cc65 */ +#include "codeent.h" +#include "coptind.h" +#include "codeinfo.h" +#include "codeopt.h" +#include "error.h" + + +/*****************************************************************************/ +/* Remove unused loads and stores */ +/*****************************************************************************/ + +unsigned OptLongAssign (CodeSeg* S) +/* Simplify long assignments. +** Recognize +** lda #IMM 0 +** sta sreg+1 1 +** lda #IMM 2 +** sta sreg 3 +** lda #IMM 4 +** ldx #IMM 5 +** sta YYY 6 +** stx YYY+1 7 +** ldy sreg 8 +** sty YYY+2 9 +** ldy sreg+1 10 +** sty YYY+3 11 +** and simplify, if not used right after and no branching occurs, to +** lda XXX+3 +** sta YYY+3 +** lda XXX+2 +** sta YYY+2 +** ldx XXX +** lda XXX+1 +** sta YYY +** stx YYY+1 +*/ +{ + unsigned Changes = 0; + + /* Walk over the entries */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + CodeEntry* L[13]; + + /* Get next entry */ + L[0] = CS_GetEntry (S, I); + + if (CS_GetEntries (S, L+1, I+1, 12)) { + CodeEntry* N; + if (/* Check the opcode sequence */ + L[0]->OPC == OP65_LDA && + L[1]->OPC == OP65_STA && + L[2]->OPC == OP65_LDA && + L[3]->OPC == OP65_STA && + L[4]->OPC == OP65_LDA && + L[5]->OPC == OP65_LDX && + L[6]->OPC == OP65_STA && + L[7]->OPC == OP65_STX && + L[8]->OPC == OP65_LDY && + L[9]->OPC == OP65_STY && + L[10]->OPC == OP65_LDY && + L[11]->OPC == OP65_STY && + /* Check the arguments match */ + L[0]->AM == AM65_IMM && + !strcmp (L[1]->Arg, "sreg+1") && + L[2]->AM == AM65_IMM && + !strcmp (L[3]->Arg, "sreg") && + L[4]->AM == AM65_IMM && + L[5]->AM == AM65_IMM && + !strncmp(L[7]->Arg, L[6]->Arg, strlen(L[6]->Arg)) && + !strcmp(L[7]->Arg + strlen(L[6]->Arg), "+1") && + !strcmp (L[8]->Arg, "sreg") && + !strncmp(L[9]->Arg, L[6]->Arg, strlen(L[6]->Arg)) && + !strcmp(L[9]->Arg + strlen(L[6]->Arg), "+2") && + !strcmp (L[10]->Arg, "sreg+1") && + !strncmp(L[11]->Arg, L[6]->Arg, strlen(L[6]->Arg)) && + !strcmp(L[11]->Arg + strlen(L[6]->Arg), "+3") && + /* Check there's nothing more */ + !RegXUsed (S, I+12) && + !CS_RangeHasLabel(S, I, 12)) { + + N = NewCodeEntry (OP65_STA, L[11]->AM, L[11]->Arg, 0, L[11]->LI); + CS_DelEntry (S, I+1); + CS_InsertEntry (S, N, I+1); + + N = NewCodeEntry (OP65_STA, L[9]->AM, L[9]->Arg, 0, L[9]->LI); + CS_DelEntry (S, I+3); + CS_InsertEntry (S, N, I+3); + + CS_DelEntries (S, I+8, 4); + + /* Remember, we had changes */ + ++Changes; + } + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + +unsigned OptLongCopy (CodeSeg* S) +/* Simplify long copies. +** Recognize +** lda XXX+3 0 +** sta sreg+1 1 +** lda XXX+2 2 +** sta sreg 3 +** ldx XXX+1 4 +** lda XXX 5 +** sta YYY 6 +** stx YYY+1 7 +** ldy sreg 8 +** sty YYY+2 9 +** ldy sreg+1 10 +** sty YYY+3 11 +** and simplify, if not used right after and no branching occurs, to +** lda XXX+3 +** sta YYY+3 +** lda XXX+2 +** sta YYY+2 +** ldx XXX +** lda XXX+1 +** sta YYY +** stx YYY+1 +*/ +{ + unsigned Changes = 0; + + /* Walk over the entries */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + CodeEntry* L[13]; + + /* Get next entry */ + L[0] = CS_GetEntry (S, I); + + if (CS_GetEntries (S, L+1, I+1, 12)) { + CodeEntry *N; + if (L[0]->OPC == OP65_LDA && + !strncmp(L[0]->Arg, L[5]->Arg, strlen(L[5]->Arg)) && + !strcmp(L[0]->Arg + strlen(L[5]->Arg), "+3") && + L[1]->OPC == OP65_STA && + !strcmp (L[1]->Arg, "sreg+1") && + L[2]->OPC == OP65_LDA && + !strncmp(L[2]->Arg, L[5]->Arg, strlen(L[5]->Arg)) && + !strcmp(L[2]->Arg + strlen(L[5]->Arg), "+2") && + L[3]->OPC == OP65_STA && + !strcmp (L[3]->Arg, "sreg") && + L[4]->OPC == OP65_LDX && + !strncmp(L[4]->Arg, L[5]->Arg, strlen(L[5]->Arg)) && + !strcmp(L[4]->Arg + strlen(L[5]->Arg), "+1") && + L[5]->OPC == OP65_LDA && + L[6]->OPC == OP65_STA && + L[7]->OPC == OP65_STX && + !strncmp(L[7]->Arg, L[6]->Arg, strlen(L[6]->Arg)) && + !strcmp(L[7]->Arg + strlen(L[6]->Arg), "+1") && + L[8]->OPC == OP65_LDY && + !strcmp (L[8]->Arg, "sreg") && + L[9]->OPC == OP65_STY && + !strncmp(L[9]->Arg, L[6]->Arg, strlen(L[6]->Arg)) && + !strcmp(L[9]->Arg + strlen(L[6]->Arg), "+2") && + L[10]->OPC == OP65_LDY && + !strcmp (L[10]->Arg, "sreg+1") && + L[11]->OPC == OP65_STY && + !strncmp(L[11]->Arg, L[6]->Arg, strlen(L[6]->Arg)) && + !strcmp(L[11]->Arg + strlen(L[6]->Arg), "+3") && + !RegXUsed (S, I+11) && + !CS_RangeHasLabel(S, I, 12)) { + + N = NewCodeEntry (OP65_STA, L[11]->AM, L[11]->Arg, 0, L[11]->LI); + CS_DelEntry (S, I+1); + CS_InsertEntry (S, N, I+1); + + N = NewCodeEntry (OP65_STA, L[9]->AM, L[9]->Arg, 0, L[9]->LI); + CS_DelEntry (S, I+3); + CS_InsertEntry (S, N, I+3); + + CS_DelEntries (S, I+8, 4); + + /* Remember, we had changes */ + ++Changes; + } + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} diff --git a/libsrc/common/localtime.c b/src/cc65/coptlong.h similarity index 77% rename from libsrc/common/localtime.c rename to src/cc65/coptlong.h index 48931ea62..c0233c299 100644 --- a/libsrc/common/localtime.c +++ b/src/cc65/coptlong.h @@ -1,15 +1,16 @@ /*****************************************************************************/ /* */ -/* localtime.c */ +/* coptlong.h */ /* */ -/* Convert calendar time into broken down local time */ +/* Long integers optimizations */ /* */ /* */ /* */ -/* (C) 2002 Ullrich von Bassewitz */ -/* Wacholderweg 14 */ -/* D-70597 Stuttgart */ -/* EMail: uz@musoftware.de */ +/* (C) 2001-2009, Ullrich von Bassewitz */ +/* Roemerstrasse 52 */ +/* D-70794 Filderstadt */ +/* EMail: uz@cc65.org */ +/* (C) 2023, Colin Leroy-Mira <colin@colino.net */ /* */ /* */ /* This software is provided 'as-is', without any expressed or implied */ @@ -33,7 +34,13 @@ -#include <time.h> +#ifndef COPTLONG_H +#define COPTLONG_H + + + +/* cc65 */ +#include "codeseg.h" @@ -43,18 +50,14 @@ -struct tm* __fastcall__ localtime (const time_t* timep) -{ - time_t t; +unsigned OptLongAssign (CodeSeg* S); +/* Simplify long assigns. */ - /* Check for a valid time spec */ - if (timep == 0) { - return 0; - } +unsigned OptLongCopy (CodeSeg* S); +/* Simplify long copy. */ - /* Get the time and correct for the time zone offset */ - t = *timep + _tz.timezone; - /* Use gmtime for conversion */ - return gmtime (&t); -} + +/* End of coptind.h */ + +#endif diff --git a/src/cc65/coptmisc.c b/src/cc65/coptmisc.c index 523fbf17c..e48d469a1 100644 --- a/src/cc65/coptmisc.c +++ b/src/cc65/coptmisc.c @@ -733,3 +733,172 @@ unsigned OptLoad2 (CodeSeg* S) /* Return the number of changes made */ return Changes; } + + + +unsigned OptBinOps (CodeSeg* S) +/* Search for an AND/EOR/ORA where the value of A or the operand is known and +** replace it by something simpler. +*/ +{ + unsigned Changes = 0; + + /* Walk over the entries */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Get a pointer to the input registers of the insn */ + const RegContents* In = &E->RI->In; + + /* Check for AND/EOR/ORA and a known value in A */ + int Delete = 0; + CodeEntry* X = 0; + switch (E->OPC) { + + case OP65_AND: + if (In->RegA == 0x00) { + /* Zero AND anything gives zero. The instruction can be + ** replaced by an immediate load of zero. + */ + X = NewCodeEntry (OP65_LDA, AM65_IMM, "$00", 0, E->LI); + } else if (In->RegA == 0xFF) { + /* 0xFF AND anything equals the operand. The instruction + ** can be replaced by a simple load of the operand. + */ + X = NewCodeEntry (OP65_LDA, E->AM, E->Arg, 0, E->LI); + } else if (E->AM == AM65_ZP) { + short Operand = -1; + switch (GetKnownReg (E->Use & REG_ZP, In)) { + case REG_TMP1: Operand = In->Tmp1; break; + case REG_PTR1_LO: Operand = In->Ptr1Lo; break; + case REG_PTR1_HI: Operand = In->Ptr1Hi; break; + case REG_SREG_LO: Operand = In->SRegLo; break; + case REG_SREG_HI: Operand = In->SRegHi; break; + } + if (Operand == 0x00) { + /* AND with zero gives zero. The instruction can be + ** replaced by an immediate load of zero. + */ + X = NewCodeEntry (OP65_LDA, AM65_IMM, "$00", 0, E->LI); + } else if (Operand == 0xFF) { + /* AND with 0xFF is a no-op besides setting the flags. + ** The instruction can be removed if the flags aren't + ** used later. + */ + if (!LoadFlagsUsed (S, I+1)) { + Delete = 1; + } + } else if (Operand >= 0) { + /* The instruction can be replaced by an immediate + ** AND. + */ + const char* Arg = MakeHexArg (Operand); + X = NewCodeEntry (OP65_AND, AM65_IMM, Arg, 0, E->LI); + } + } + break; + + case OP65_EOR: + if (In->RegA == 0x00) { + /* Zero EOR anything equals the operand. The instruction + ** can be replaced by a simple load. + */ + X = NewCodeEntry (OP65_LDA, E->AM, E->Arg, 0, E->LI); + } else if (E->AM == AM65_ZP) { + short Operand = -1; + switch (GetKnownReg (E->Use & REG_ZP, In)) { + case REG_TMP1: Operand = In->Tmp1; break; + case REG_PTR1_LO: Operand = In->Ptr1Lo; break; + case REG_PTR1_HI: Operand = In->Ptr1Hi; break; + case REG_SREG_LO: Operand = In->SRegLo; break; + case REG_SREG_HI: Operand = In->SRegHi; break; + } + if (Operand == 0x00) { + /* EOR with 0x00 is a no-op besides setting the flags. + ** The instruction can be removed if the flags aren't + ** used later. + */ + if (!LoadFlagsUsed (S, I+1)) { + Delete = 1; + } + } else if (Operand >= 0) { + /* The instruction can be replaced by an immediate + ** EOR. + */ + const char* Arg = MakeHexArg (Operand); + X = NewCodeEntry (OP65_EOR, AM65_IMM, Arg, 0, E->LI); + } + } + break; + + case OP65_ORA: + if (In->RegA == 0x00) { + /* ORA with 0x00 is a no-op. The instruction can be + ** replaced by a simple load. + */ + X = NewCodeEntry (OP65_LDA, E->AM, E->Arg, 0, E->LI); + } else if (In->RegA == 0xFF) { + /* ORA with 0xFF gives 0xFF. The instruction can be replaced + ** by an immediate load of 0xFF. + */ + X = NewCodeEntry (OP65_LDA, AM65_IMM, "$FF", 0, E->LI); + } else if (E->AM == AM65_ZP) { + short Operand = -1; + switch (GetKnownReg (E->Use & REG_ZP, In)) { + case REG_TMP1: Operand = In->Tmp1; break; + case REG_PTR1_LO: Operand = In->Ptr1Lo; break; + case REG_PTR1_HI: Operand = In->Ptr1Hi; break; + case REG_SREG_LO: Operand = In->SRegLo; break; + case REG_SREG_HI: Operand = In->SRegHi; break; + } + if (Operand == 0x00) { + /* ORA with 0x00 is a no-op besides setting the flags. + ** The instruction can be removed if the flags aren't + ** used later. + */ + if (!LoadFlagsUsed (S, I+1)) { + Delete = 1; + } + } else if (Operand == 0xFF) { + /* ORA with 0xFF results in 0xFF. The instruction can + ** be replaced by a simple load. + */ + X = NewCodeEntry (OP65_LDA, AM65_IMM, "$FF", 0, E->LI); + } else if (Operand >= 0) { + /* The instruction can be replaced by an immediate + ** ORA. + */ + const char* Arg = MakeHexArg (Operand); + X = NewCodeEntry (OP65_ORA, AM65_IMM, Arg, 0, E->LI); + } + } + break; + + default: + break; + + } + + /* If we must delete the instruction, do that. If we have a replacement + ** entry, place it and remove the old one. + */ + if (X) { + CS_InsertEntry (S, X, I+1); + Delete = 1; + } + if (Delete) { + CS_DelEntry (S, I); + ++Changes; + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} diff --git a/src/cc65/coptmisc.h b/src/cc65/coptmisc.h index 89242351c..418b61e94 100644 --- a/src/cc65/coptmisc.h +++ b/src/cc65/coptmisc.h @@ -107,6 +107,12 @@ unsigned OptLoad1 (CodeSeg* S); unsigned OptLoad2 (CodeSeg* S); /* Replace calls to ldaxysp by inline code */ +unsigned OptBinOps (CodeSeg* S); +/* Search for an AND/EOR/ORA where the value of A or the operand is known and +** replace it by something simpler. +*/ + + /* End of coptmisc.h */ diff --git a/src/cc65/coptneg.c b/src/cc65/coptneg.c deleted file mode 100644 index 27171c68d..000000000 --- a/src/cc65/coptneg.c +++ /dev/null @@ -1,607 +0,0 @@ -/*****************************************************************************/ -/* */ -/* coptneg.c */ -/* */ -/* Optimize negation sequences */ -/* */ -/* */ -/* */ -/* (C) 2001-2012, Ullrich von Bassewitz */ -/* Roemerstrasse 52 */ -/* D-70794 Filderstadt */ -/* EMail: uz@cc65.org */ -/* */ -/* */ -/* This software is provided 'as-is', without any expressed 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. */ -/* */ -/*****************************************************************************/ - - - -/* cc65 */ -#include "codeent.h" -#include "codeinfo.h" -#include "coptneg.h" - - - -/*****************************************************************************/ -/* bnega optimizations */ -/*****************************************************************************/ - - - -unsigned OptBNegA1 (CodeSeg* S) -/* Check for -** -** ldx #$00 -** lda .. -** jsr bnega -** -** Remove the ldx if the lda does not use it. -*/ -{ - unsigned Changes = 0; - - /* Walk over the entries */ - unsigned I = 0; - while (I < CS_GetEntryCount (S)) { - - CodeEntry* L[2]; - - /* Get next entry */ - CodeEntry* E = CS_GetEntry (S, I); - - /* Check for a ldx */ - if (E->OPC == OP65_LDX && - E->AM == AM65_IMM && - CE_HasNumArg (E) && - E->Num == 0 && - CS_GetEntries (S, L, I+1, 2) && - L[0]->OPC == OP65_LDA && - (L[0]->Use & REG_X) == 0 && - !CE_HasLabel (L[0]) && - CE_IsCallTo (L[1], "bnega") && - !CE_HasLabel (L[1])) { - - /* Remove the ldx instruction */ - CS_DelEntry (S, I); - - /* Remember, we had changes */ - ++Changes; - - } - - /* Next entry */ - ++I; - - } - - /* Return the number of changes made */ - return Changes; -} - - - -unsigned OptBNegA2 (CodeSeg* S) -/* Check for -** -** lda .. -** jsr bnega -** jeq/jne .. -** -** Adjust the conditional branch and remove the call to the subroutine. -*/ -{ - unsigned Changes = 0; - - /* Walk over the entries */ - unsigned I = 0; - while (I < CS_GetEntryCount (S)) { - - CodeEntry* L[2]; - - /* Get next entry */ - CodeEntry* E = CS_GetEntry (S, I); - - /* Check for the sequence */ - if ((E->OPC == OP65_ADC || - E->OPC == OP65_AND || - E->OPC == OP65_DEA || - E->OPC == OP65_EOR || - E->OPC == OP65_INA || - E->OPC == OP65_LDA || - E->OPC == OP65_ORA || - E->OPC == OP65_PLA || - E->OPC == OP65_SBC || - E->OPC == OP65_TXA || - E->OPC == OP65_TYA) && - CS_GetEntries (S, L, I+1, 2) && - CE_IsCallTo (L[0], "bnega") && - !CE_HasLabel (L[0]) && - (L[1]->Info & OF_ZBRA) != 0 && - !CE_HasLabel (L[1])) { - - /* Invert the branch */ - CE_ReplaceOPC (L[1], GetInverseBranch (L[1]->OPC)); - - /* Delete the subroutine call */ - CS_DelEntry (S, I+1); - - /* Remember, we had changes */ - ++Changes; - - } - - /* Next entry */ - ++I; - - } - - /* Return the number of changes made */ - return Changes; -} - - - -/*****************************************************************************/ -/* bnegax optimizations */ -/*****************************************************************************/ - - - -unsigned OptBNegAX1 (CodeSeg* S) -/* On a call to bnegax, if X is zero, the result depends only on the value in -** A, so change the call to a call to bnega. This will get further optimized -** later if possible. -*/ -{ - unsigned Changes = 0; - unsigned I; - - /* Walk over the entries */ - I = 0; - while (I < CS_GetEntryCount (S)) { - - /* Get next entry */ - CodeEntry* E = CS_GetEntry (S, I); - - /* Check if this is a call to bnegax, and if X is known and zero */ - if (E->RI->In.RegX == 0 && CE_IsCallTo (E, "bnegax")) { - - CodeEntry* X = NewCodeEntry (OP65_JSR, AM65_ABS, "bnega", 0, E->LI); - CS_InsertEntry (S, X, I+1); - CS_DelEntry (S, I); - - /* We had changes */ - ++Changes; - } - - /* Next entry */ - ++I; - - } - - /* Return the number of changes made */ - return Changes; -} - - - -unsigned OptBNegAX2 (CodeSeg* S) -/* Search for the sequence: -** -** ldy #xx -** jsr ldaxysp -** jsr bnegax -** jne/jeq ... -** -** and replace it by -** -** ldy #xx -** lda (sp),y -** dey -** ora (sp),y -** jeq/jne ... -*/ -{ - unsigned Changes = 0; - - /* Walk over the entries */ - unsigned I = 0; - while (I < CS_GetEntryCount (S)) { - - CodeEntry* L[4]; - - /* Get next entry */ - L[0] = CS_GetEntry (S, I); - - /* Check for the sequence */ - if (L[0]->OPC == OP65_LDY && - CE_IsConstImm (L[0]) && - !CS_RangeHasLabel (S, I+1, 3) && - CS_GetEntries (S, L+1, I+1, 3) && - CE_IsCallTo (L[1], "ldaxysp") && - CE_IsCallTo (L[2], "bnegax") && - (L[3]->Info & OF_ZBRA) != 0) { - - CodeEntry* X; - - /* lda (sp),y */ - X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "sp", 0, L[1]->LI); - CS_InsertEntry (S, X, I+1); - - /* dey */ - X = NewCodeEntry (OP65_DEY, AM65_IMP, 0, 0, L[1]->LI); - CS_InsertEntry (S, X, I+2); - - /* ora (sp),y */ - X = NewCodeEntry (OP65_ORA, AM65_ZP_INDY, "sp", 0, L[1]->LI); - CS_InsertEntry (S, X, I+3); - - /* Invert the branch */ - CE_ReplaceOPC (L[3], GetInverseBranch (L[3]->OPC)); - - /* Delete the entries no longer needed. */ - CS_DelEntries (S, I+4, 2); - - /* Remember, we had changes */ - ++Changes; - - } - - /* Next entry */ - ++I; - - } - - /* Return the number of changes made */ - return Changes; -} - - - -unsigned OptBNegAX3 (CodeSeg* S) -/* Search for the sequence: -** -** lda xx -** ldx yy -** jsr bnegax -** jne/jeq ... -** -** and replace it by -** -** lda xx -** ora xx+1 -** jeq/jne ... -*/ -{ - unsigned Changes = 0; - - /* Walk over the entries */ - unsigned I = 0; - while (I < CS_GetEntryCount (S)) { - - CodeEntry* L[3]; - - /* Get next entry */ - CodeEntry* E = CS_GetEntry (S, I); - - /* Check for the sequence */ - if (E->OPC == OP65_LDA && - CS_GetEntries (S, L, I+1, 3) && - L[0]->OPC == OP65_LDX && - !CE_HasLabel (L[0]) && - CE_IsCallTo (L[1], "bnegax") && - !CE_HasLabel (L[1]) && - (L[2]->Info & OF_ZBRA) != 0 && - !CE_HasLabel (L[2])) { - - /* ldx --> ora */ - CE_ReplaceOPC (L[0], OP65_ORA); - - /* Invert the branch */ - CE_ReplaceOPC (L[2], GetInverseBranch (L[2]->OPC)); - - /* Delete the subroutine call */ - CS_DelEntry (S, I+2); - - /* Remember, we had changes */ - ++Changes; - - } - - /* Next entry */ - ++I; - - } - - /* Return the number of changes made */ - return Changes; -} - - - -unsigned OptBNegAX4 (CodeSeg* S) -/* Search for the sequence: -** -** jsr xxx -** jsr bnega(x) -** jeq/jne ... -** -** and replace it by: -** -** jsr xxx -** <boolean test> -** jne/jeq ... -*/ -{ - unsigned Changes = 0; - - /* Walk over the entries */ - unsigned I = 0; - while (I < CS_GetEntryCount (S)) { - - CodeEntry* L[2]; - - /* Get next entry */ - CodeEntry* E = CS_GetEntry (S, I); - - /* Check for the sequence */ - if (E->OPC == OP65_JSR && - CS_GetEntries (S, L, I+1, 2) && - L[0]->OPC == OP65_JSR && - strncmp (L[0]->Arg,"bnega",5) == 0 && - !CE_HasLabel (L[0]) && - (L[1]->Info & OF_ZBRA) != 0 && - !CE_HasLabel (L[1])) { - - CodeEntry* X; - - /* Check if we're calling bnega or bnegax */ - int ByteSized = (strcmp (L[0]->Arg, "bnega") == 0); - - /* Insert apropriate test code */ - if (ByteSized) { - /* Test bytes */ - X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, L[0]->LI); - CS_InsertEntry (S, X, I+2); - } else { - /* Test words */ - X = NewCodeEntry (OP65_STX, AM65_ZP, "tmp1", 0, L[0]->LI); - CS_InsertEntry (S, X, I+2); - X = NewCodeEntry (OP65_ORA, AM65_ZP, "tmp1", 0, L[0]->LI); - CS_InsertEntry (S, X, I+3); - } - - /* Delete the subroutine call */ - CS_DelEntry (S, I+1); - - /* Invert the branch */ - CE_ReplaceOPC (L[1], GetInverseBranch (L[1]->OPC)); - - /* Remember, we had changes */ - ++Changes; - - } - - /* Next entry */ - ++I; - - } - - /* Return the number of changes made */ - return Changes; -} - - - -/*****************************************************************************/ -/* negax optimizations */ -/*****************************************************************************/ - - - -unsigned OptNegAX1 (CodeSeg* S) -/* Search for a call to negax and replace it by -** -** eor #$FF -** clc -** adc #$01 -** -** if X isn't used later. -*/ -{ - unsigned Changes = 0; - unsigned I; - - /* Walk over the entries */ - I = 0; - while (I < CS_GetEntryCount (S)) { - - /* Get next entry */ - CodeEntry* E = CS_GetEntry (S, I); - - /* Check if this is a call to negax, and if X isn't used later */ - if (CE_IsCallTo (E, "negax") && !RegXUsed (S, I+1)) { - - CodeEntry* X; - - /* Add replacement code behind */ - X = NewCodeEntry (OP65_EOR, AM65_IMM, "$FF", 0, E->LI); - CS_InsertEntry (S, X, I+1); - - X = NewCodeEntry (OP65_CLC, AM65_IMP, 0, 0, E->LI); - CS_InsertEntry (S, X, I+2); - - X = NewCodeEntry (OP65_ADC, AM65_IMM, "$01", 0, E->LI); - CS_InsertEntry (S, X, I+3); - - /* Delete the call to negax */ - CS_DelEntry (S, I); - - /* Skip the generated code */ - I += 2; - - /* We had changes */ - ++Changes; - } - - /* Next entry */ - ++I; - - } - - /* Return the number of changes made */ - return Changes; -} - - - -unsigned OptNegAX2 (CodeSeg* S) -/* Search for a call to negax and replace it by -** -** ldx #$FF -** eor #$FF -** clc -** adc #$01 -** bcc L1 -** inx -** L1: -** -** if X is known and zero on entry. -*/ -{ - unsigned Changes = 0; - unsigned I; - - /* Walk over the entries */ - I = 0; - while (I < CS_GetEntryCount (S)) { - - CodeEntry* P; - - /* Get next entry */ - CodeEntry* E = CS_GetEntry (S, I); - - /* Check if this is a call to negax, and if X is known and zero */ - if (E->RI->In.RegX == 0 && - CE_IsCallTo (E, "negax") && - (P = CS_GetNextEntry (S, I)) != 0) { - - CodeEntry* X; - CodeLabel* L; - - /* Add replacement code behind */ - - /* ldx #$FF */ - X = NewCodeEntry (OP65_LDX, AM65_IMM, "$FF", 0, E->LI); - CS_InsertEntry (S, X, I+1); - - /* eor #$FF */ - X = NewCodeEntry (OP65_EOR, AM65_IMM, "$FF", 0, E->LI); - CS_InsertEntry (S, X, I+2); - - /* clc */ - X = NewCodeEntry (OP65_CLC, AM65_IMP, 0, 0, E->LI); - CS_InsertEntry (S, X, I+3); - - /* adc #$01 */ - X = NewCodeEntry (OP65_ADC, AM65_IMM, "$01", 0, E->LI); - CS_InsertEntry (S, X, I+4); - - /* Get the label attached to the insn following the call */ - L = CS_GenLabel (S, P); - - /* bcc L */ - X = NewCodeEntry (OP65_BCC, AM65_BRA, L->Name, L, E->LI); - CS_InsertEntry (S, X, I+5); - - /* inx */ - X = NewCodeEntry (OP65_INX, AM65_IMP, 0, 0, E->LI); - CS_InsertEntry (S, X, I+6); - - /* Delete the call to negax */ - CS_DelEntry (S, I); - - /* Skip the generated code */ - I += 5; - - /* We had changes */ - ++Changes; - } - - /* Next entry */ - ++I; - - } - - /* Return the number of changes made */ - return Changes; -} - - - -/*****************************************************************************/ -/* complax optimizations */ -/*****************************************************************************/ - - - -unsigned OptComplAX1 (CodeSeg* S) -/* Search for a call to complax and replace it by -** -** eor #$FF -** -** if X isn't used later. -*/ -{ - unsigned Changes = 0; - unsigned I; - - /* Walk over the entries */ - I = 0; - while (I < CS_GetEntryCount (S)) { - - /* Get next entry */ - CodeEntry* E = CS_GetEntry (S, I); - - /* Check if this is a call to negax, and if X isn't used later */ - if (CE_IsCallTo (E, "complax") && !RegXUsed (S, I+1)) { - - CodeEntry* X; - - /* Add replacement code behind */ - X = NewCodeEntry (OP65_EOR, AM65_IMM, "$FF", 0, E->LI); - CS_InsertEntry (S, X, I+1); - - /* Delete the call to negax */ - CS_DelEntry (S, I); - - /* We had changes */ - ++Changes; - } - - /* Next entry */ - ++I; - - } - - /* Return the number of changes made */ - return Changes; -} diff --git a/src/cc65/coptstop.c b/src/cc65/coptstop.c index ae40a55e9..402f16b97 100644 --- a/src/cc65/coptstop.c +++ b/src/cc65/coptstop.c @@ -1105,6 +1105,62 @@ static unsigned Opt_tosxorax (StackOpData* D) +static unsigned Opt_a_tosbitwise (StackOpData* D, opc_t OPC) +/* Optimize the tosandax/tosorax/tosxorax sequence. */ +{ + CodeEntry* X; + + + /* Inline the bitwise operation */ + D->IP = D->OpIndex+1; + + /* Backup lhs if necessary */ + if ((D->Rhs.A.Flags & LI_DIRECT) == 0) { + if ((D->Lhs.A.Flags & (LI_DIRECT | LI_RELOAD_Y)) == LI_DIRECT) { + /* Just reload lhs */ + X = NewCodeEntry (OPC, D->Lhs.A.LoadEntry->AM, D->Lhs.A.LoadEntry->Arg, 0, D->OpEntry->LI); + InsertEntry (D, X, D->IP++); + } else { + /* Backup lhs */ + X = NewCodeEntry (OP65_STA, AM65_ZP, D->ZPLo, 0, D->PushEntry->LI); + InsertEntry (D, X, D->PushIndex+1); + /* Add code for low operand */ + X = NewCodeEntry (OPC, AM65_ZP, D->ZPLo, 0, D->OpEntry->LI); + InsertEntry (D, X, D->IP++); + } + } else { + /* Add code for low operand */ + X = NewCodeEntry (OPC, D->Rhs.A.LoadEntry->AM, D->Rhs.A.LoadEntry->Arg, 0, D->OpEntry->LI); + InsertEntry (D, X, D->IP++); + + /* Rhs load entries may be removed */ + D->Rhs.A.Flags |= LI_REMOVE; + } + + /* Do high-byte operation only when its result is used */ + if ((GetRegInfo (D->Code, D->IP, REG_X) & REG_X) != 0) { + /* Replace the high-byte load with 0 for EOR, or just leave it alone */ + if (OPC == OP65_EOR) { + X = NewCodeEntry (OP65_LDX, AM65_IMM, MakeHexArg (0), 0, D->Rhs.X.ChgEntry->LI); + InsertEntry (D, X, D->IP++); + D->Rhs.X.Flags |= LI_REMOVE; + } else { + D->Rhs.X.Flags |= LI_DONT_REMOVE; + } + } else { + /* Rhs load entries may be removed */ + D->Rhs.X.Flags |= LI_REMOVE; + } + + /* Remove the push and the call to the tossubax function */ + RemoveRemainders (D); + + /* We changed the sequence */ + return 1; +} + + + static unsigned Opt_a_toscmpbool (StackOpData* D, const char* BoolTransformer) /* Optimize the TOS compare sequence with a bool transformer */ { @@ -1177,6 +1233,14 @@ static unsigned Opt_a_toscmpbool (StackOpData* D, const char* BoolTransformer) +static unsigned Opt_a_tosand (StackOpData* D) +/* Optimize the tosandax sequence. */ +{ + return Opt_a_tosbitwise (D, OP65_AND); +} + + + static unsigned Opt_a_toseq (StackOpData* D) /* Optimize the toseqax sequence */ { @@ -1292,6 +1356,66 @@ static unsigned Opt_a_tosne (StackOpData* D) +static unsigned Opt_a_tosor (StackOpData* D) +/* Optimize the tosorax sequence. */ +{ + return Opt_a_tosbitwise (D, OP65_ORA); +} + + + +static unsigned Opt_a_tossub (StackOpData* D) +/* Optimize the tossubax sequence. */ +{ + CodeEntry* X; + + + /* Inline the sbc */ + D->IP = D->OpIndex+1; + + /* Must be true because of OP_RHS_LOAD */ + CHECK ((D->Rhs.A.Flags & D->Rhs.X.Flags & LI_DIRECT) != 0); + + /* sec */ + X = NewCodeEntry (OP65_SEC, AM65_IMP, 0, 0, D->OpEntry->LI); + InsertEntry (D, X, D->IP++); + + /* Add code for low operand */ + AddOpLow (D, OP65_SBC, &D->Rhs); + + /* Do sign-extension as high-byte operation only when its result is used */ + if ((GetRegInfo (D->Code, D->IP, REG_X) & REG_X) != 0) { + CodeLabel* L; + CodeEntry* N; + + X = NewCodeEntry (OP65_LDX, AM65_IMM, MakeHexArg (0), 0, D->OpEntry->LI); + InsertEntry (D, X, D->IP++); + + /* Add sign extension - N is unused now */ + N = CS_GetEntry (D->Code, D->IP); + CHECK (N != 0); + L = CS_GenLabel (D->Code, N); + + X = NewCodeEntry (OP65_BCS, AM65_BRA, L->Name, L, D->OpEntry->LI); + InsertEntry (D, X, D->IP++); + + X = NewCodeEntry (OP65_DEX, AM65_IMP, 0, 0, D->OpEntry->LI); + InsertEntry (D, X, D->IP++); + } + + /* Rhs load entries must be removed */ + D->Rhs.X.Flags |= LI_REMOVE; + D->Rhs.A.Flags |= LI_REMOVE; + + /* Remove the push and the call to the tossubax function */ + RemoveRemainders (D); + + /* We changed the sequence */ + return 1; +} + + + static unsigned Opt_a_tosuge (StackOpData* D) /* Optimize the tosgeax and tosugeax sequences */ { @@ -1324,6 +1448,14 @@ static unsigned Opt_a_tosult (StackOpData* D) +static unsigned Opt_a_tosxor (StackOpData* D) +/* Optimize the tosxorax sequence. */ +{ + return Opt_a_tosbitwise (D, OP65_EOR); +} + + + /*****************************************************************************/ /* Code */ /*****************************************************************************/ @@ -1356,6 +1488,7 @@ static const OptFuncDesc FuncTable[] = { }; static const OptFuncDesc FuncRegATable[] = { + { "tosandax", Opt_a_tosand, REG_NONE, OP_RHS_REMOVE_DIRECT | OP_RHS_LOAD_DIRECT }, { "toseqax", Opt_a_toseq, REG_NONE, OP_NONE }, { "tosgeax", Opt_a_tosuge, REG_NONE, OP_NONE }, { "tosgtax", Opt_a_tosugt, REG_NONE, OP_NONE }, @@ -1363,10 +1496,13 @@ static const OptFuncDesc FuncRegATable[] = { { "tosleax", Opt_a_tosule, REG_NONE, OP_NONE }, { "tosltax", Opt_a_tosult, REG_NONE, OP_NONE }, { "tosneax", Opt_a_tosne, REG_NONE, OP_NONE }, + { "tosorax", Opt_a_tosor, REG_NONE, OP_RHS_REMOVE_DIRECT | OP_RHS_LOAD_DIRECT }, + { "tossubax", Opt_a_tossub, REG_NONE, OP_RHS_REMOVE_DIRECT | OP_RHS_LOAD_DIRECT }, { "tosugeax", Opt_a_tosuge, REG_NONE, OP_NONE }, { "tosugtax", Opt_a_tosugt, REG_NONE, OP_NONE }, { "tosuleax", Opt_a_tosule, REG_NONE, OP_NONE }, { "tosultax", Opt_a_tosult, REG_NONE, OP_NONE }, + { "tosxorax", Opt_a_tosxor, REG_NONE, OP_RHS_REMOVE_DIRECT | OP_RHS_LOAD_DIRECT }, }; #define FUNC_COUNT(Table) (sizeof(Table) / sizeof(Table[0])) diff --git a/src/cc65/coptunary.c b/src/cc65/coptunary.c new file mode 100644 index 000000000..4d92f9d4a --- /dev/null +++ b/src/cc65/coptunary.c @@ -0,0 +1,236 @@ +/*****************************************************************************/ +/* */ +/* coptunary.c */ +/* */ +/* Optimize bitwise unary sequences */ +/* */ +/* */ +/* */ +/* (C) 2001-2012, Ullrich von Bassewitz */ +/* Roemerstrasse 52 */ +/* D-70794 Filderstadt */ +/* EMail: uz@cc65.org */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed 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. */ +/* */ +/*****************************************************************************/ + + + +/* cc65 */ +#include "codeent.h" +#include "codeinfo.h" +#include "coptbool.h" + + + +/*****************************************************************************/ +/* negax optimizations */ +/*****************************************************************************/ + + + +unsigned OptNegAX1 (CodeSeg* S) +/* Search for a call to negax and replace it by +** +** eor #$FF +** clc +** adc #$01 +** +** if X isn't used later. +*/ +{ + unsigned Changes = 0; + unsigned I; + + /* Walk over the entries */ + I = 0; + while (I < CS_GetEntryCount (S)) { + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Check if this is a call to negax, and if X isn't used later */ + if (CE_IsCallTo (E, "negax") && !RegXUsed (S, I+1)) { + + CodeEntry* X; + + /* Add replacement code behind */ + X = NewCodeEntry (OP65_EOR, AM65_IMM, "$FF", 0, E->LI); + CS_InsertEntry (S, X, I+1); + + X = NewCodeEntry (OP65_CLC, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, I+2); + + X = NewCodeEntry (OP65_ADC, AM65_IMM, "$01", 0, E->LI); + CS_InsertEntry (S, X, I+3); + + /* Delete the call to negax */ + CS_DelEntry (S, I); + + /* Skip the generated code */ + I += 2; + + /* We had changes */ + ++Changes; + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + + + +unsigned OptNegAX2 (CodeSeg* S) +/* Search for a call to negax and replace it by +** +** ldx #$FF +** eor #$FF +** clc +** adc #$01 +** bcc L1 +** inx +** L1: +** +** if X is known and zero on entry. +*/ +{ + unsigned Changes = 0; + unsigned I; + + /* Walk over the entries */ + I = 0; + while (I < CS_GetEntryCount (S)) { + + CodeEntry* P; + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Check if this is a call to negax, and if X is known and zero */ + if (E->RI->In.RegX == 0 && + CE_IsCallTo (E, "negax") && + (P = CS_GetNextEntry (S, I)) != 0) { + + CodeEntry* X; + CodeLabel* L; + + /* Add replacement code behind */ + + /* ldx #$FF */ + X = NewCodeEntry (OP65_LDX, AM65_IMM, "$FF", 0, E->LI); + CS_InsertEntry (S, X, I+1); + + /* eor #$FF */ + X = NewCodeEntry (OP65_EOR, AM65_IMM, "$FF", 0, E->LI); + CS_InsertEntry (S, X, I+2); + + /* clc */ + X = NewCodeEntry (OP65_CLC, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, I+3); + + /* adc #$01 */ + X = NewCodeEntry (OP65_ADC, AM65_IMM, "$01", 0, E->LI); + CS_InsertEntry (S, X, I+4); + + /* Get the label attached to the insn following the call */ + L = CS_GenLabel (S, P); + + /* bcc L */ + X = NewCodeEntry (OP65_BCC, AM65_BRA, L->Name, L, E->LI); + CS_InsertEntry (S, X, I+5); + + /* inx */ + X = NewCodeEntry (OP65_INX, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, I+6); + + /* Delete the call to negax */ + CS_DelEntry (S, I); + + /* Skip the generated code */ + I += 5; + + /* We had changes */ + ++Changes; + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + + + +/*****************************************************************************/ +/* complax optimizations */ +/*****************************************************************************/ + + + +unsigned OptComplAX1 (CodeSeg* S) +/* Search for a call to complax and replace it by +** +** eor #$FF +** +** if X isn't used later. +*/ +{ + unsigned Changes = 0; + unsigned I; + + /* Walk over the entries */ + I = 0; + while (I < CS_GetEntryCount (S)) { + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Check if this is a call to negax, and if X isn't used later */ + if (CE_IsCallTo (E, "complax") && !RegXUsed (S, I+1)) { + + CodeEntry* X; + + /* Add replacement code behind */ + X = NewCodeEntry (OP65_EOR, AM65_IMM, "$FF", 0, E->LI); + CS_InsertEntry (S, X, I+1); + + /* Delete the call to negax */ + CS_DelEntry (S, I); + + /* We had changes */ + ++Changes; + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} diff --git a/libsrc/common/gmtime.c b/src/cc65/coptunary.h similarity index 64% rename from libsrc/common/gmtime.c rename to src/cc65/coptunary.h index 85e9de3d0..a7fd6d7b4 100644 --- a/libsrc/common/gmtime.c +++ b/src/cc65/coptunary.h @@ -1,15 +1,15 @@ /*****************************************************************************/ /* */ -/* gmtime.c */ +/* coptunary.h */ /* */ -/* Convert calendar time into broken down time in UTC */ +/* Optimize bitwise unary sequences */ /* */ /* */ /* */ -/* (C) 2002 Ullrich von Bassewitz */ -/* Wacholderweg 14 */ -/* D-70597 Stuttgart */ -/* EMail: uz@musoftware.de */ +/* (C) 2001-2012, Ullrich von Bassewitz */ +/* Roemerstrasse 52 */ +/* D-70794 Filderstadt */ +/* EMail: uz@cc65.org */ /* */ /* */ /* This software is provided 'as-is', without any expressed or implied */ @@ -33,41 +33,64 @@ -#include <time.h> +#ifndef COPTUNARY_H +#define COPTUNARY_H + + + +/* cc65 */ +#include "codeseg.h" /*****************************************************************************/ -/* Code */ +/* negax optimizations */ /*****************************************************************************/ -struct tm* __fastcall__ gmtime (const time_t* timep) -{ - static struct tm timebuf; - time_t t; +unsigned OptNegAX1 (CodeSeg* S); +/* Search for a call to negax and replace it by +** +** eor #$FF +** clc +** adc #$01 +** +** if X isn't used later. +*/ - /* Check the argument */ - if (timep == 0 || (long) (t = *timep) < 0) { - /* Invalid arg */ - return 0; - } +unsigned OptNegAX2 (CodeSeg* S); +/* Search for a call to negax and replace it by +** +** ldx #$FF +** eor #$FF +** clc +** adc #$01 +** bcc L1 +** inx +** L1: +** +** if X is known and zero on entry. +*/ - /* Since our ints are just 16 bits, split the given time into seconds, - ** hours and days. Each of the values will fit in a 16 bit variable. - ** The mktime routine will then do the rest. - */ - timebuf.tm_sec = t % 3600; - timebuf.tm_min = 0; - timebuf.tm_hour = (t / 3600) % 24; - timebuf.tm_mday = (t / (3600UL * 24UL)) + 1; - timebuf.tm_mon = 0; - timebuf.tm_year = 70; /* Base value is 1/1/1970 */ - /* Call mktime to do the final conversion */ - mktime (&timebuf); - /* Return the result */ - return &timebuf; -} +/*****************************************************************************/ +/* complax optimizations */ +/*****************************************************************************/ + + + +unsigned OptComplAX1 (CodeSeg* S); +/* Search for a call to complax and replace it by +** +** eor #$FF +** +** if X isn't used later. +*/ + + + +/* End of coptunary.h */ + +#endif diff --git a/src/cc65/datatype.c b/src/cc65/datatype.c index caa41a7a4..4d6cb25a5 100644 --- a/src/cc65/datatype.c +++ b/src/cc65/datatype.c @@ -63,6 +63,8 @@ const Type type_char[] = { TYPE(T_CHAR), TYPE(T_END) }; const Type type_schar[] = { TYPE(T_SCHAR), TYPE(T_END) }; const Type type_uchar[] = { TYPE(T_UCHAR), TYPE(T_END) }; +const Type type_short[] = { TYPE(T_SHORT), TYPE(T_END) }; +const Type type_ushort[] = { TYPE(T_USHORT), TYPE(T_END) }; const Type type_int[] = { TYPE(T_INT), TYPE(T_END) }; const Type type_uint[] = { TYPE(T_UINT), TYPE(T_END) }; const Type type_long[] = { TYPE(T_LONG), TYPE(T_END) }; @@ -214,7 +216,7 @@ unsigned BitSizeOf (const Type* T) unsigned SizeOf (const Type* T) /* Compute size (in bytes) of object represented by type array */ { - switch (GetUnqualTypeCode (T)) { + switch (GetUnderlyingTypeCode (T)) { case T_VOID: /* A void variable is a cc65 extension. @@ -315,7 +317,7 @@ unsigned CheckedSizeOf (const Type* T) { unsigned Size = SizeOf (T); if (Size == 0) { - if (HasUnknownSize (T + 1)) { + if (HasUnknownSize (T)) { Error ("Size of type '%s' is unknown", GetFullTypeName (T)); } else { Error ("Size of type '%s' is 0", GetFullTypeName (T)); @@ -366,7 +368,7 @@ static unsigned GetMinimalTypeSizeByBitWidth (unsigned BitWidth) -TypeCode GetUnqualTypeCode (const Type* Type) +TypeCode GetUnderlyingTypeCode (const Type* Type) /* Get the type code of the unqualified underlying type of Type. ** Return GetUnqualRawTypeCode (Type) if Type is not scalar. */ @@ -495,8 +497,8 @@ Type* NewPointerTo (const Type* T) Type* NewBitFieldOf (const Type* T, unsigned BitOffs, unsigned BitWidth) -/* Return a type string that is "T : BitWidth" aligned on BitOffs. The type -** string is allocated on the heap and may be freed after use. +/* Return a type string that is "unqualified T : BitWidth" aligned on BitOffs. +** The type string is allocated on the heap and may be freed after use. */ { Type* P; @@ -537,7 +539,7 @@ const Type* AddressOf (const Type* T) Type* P = TypeAlloc (Size + 1); /* Create the return type... */ - P[0].C = T_PTR | (T[0].C & T_QUAL_ADDRSIZE) | T_QUAL_CONST; + P[0].C = T_PTR | (T[0].C & T_QUAL_ADDRSIZE); memcpy (P+1, T, Size * sizeof (Type)); /* ...and return it */ @@ -670,6 +672,10 @@ const Type* ArithmeticConvert (const Type* lhst, const Type* rhst) ** floating point types are not (yet) supported. ** The integral promotions are performed on both operands. */ + if (IsClassFloat(lhst) || IsClassFloat(rhst)) { + Error ("Floating point arithmetic not supported."); + return type_long; + } lhst = IntPromotion (lhst); rhst = IntPromotion (rhst); @@ -719,19 +725,21 @@ const Type* ArithmeticConvert (const Type* lhst, const Type* rhst) const Type* GetSignedType (const Type* T) /* Get signed counterpart of the integral type */ { - switch (GetUnqualTypeCode (T) & T_MASK_RANK) { + switch (GetUnderlyingTypeCode (T) & T_MASK_RANK) { case T_RANK_CHAR: return type_schar; - case T_RANK_INT: case T_RANK_SHORT: + return type_short; + + case T_RANK_INT: return type_int; case T_RANK_LONG: return type_long; default: - Internal ("Unknown type code: %lX", GetUnqualTypeCode (T)); + Internal ("Unknown type code: %lX", GetUnderlyingTypeCode (T)); return T; } } @@ -741,19 +749,21 @@ const Type* GetSignedType (const Type* T) const Type* GetUnsignedType (const Type* T) /* Get unsigned counterpart of the integral type */ { - switch (GetUnqualTypeCode (T) & T_MASK_RANK) { + switch (GetUnderlyingTypeCode (T) & T_MASK_RANK) { case T_RANK_CHAR: return type_uchar; - case T_RANK_INT: case T_RANK_SHORT: + return type_ushort; + + case T_RANK_INT: return type_uint; case T_RANK_LONG: return type_ulong; default: - Internal ("Unknown type code: %lX", GetUnqualTypeCode (T)); + Internal ("Unknown type code: %lX", GetUnderlyingTypeCode (T)); return T; } } @@ -812,6 +822,14 @@ const Type* GetStructReplacementType (const Type* SType) +const Type* GetBitFieldDeclType (const Type* Type) +/* Get the original integer type used to declare the bit-field */ +{ + return Type + 1; +} + + + const Type* GetBitFieldChunkType (const Type* Type) /* Get the type needed to operate on the byte chunk containing the bit-field */ { @@ -860,6 +878,16 @@ int IsTypeFragBitField (const Type* T) +#if !defined(HAVE_INLINE) +int IsTypeFuncLike (const Type* T) +/* Return true if this is a function or a function pointer */ +{ + return IsTypeFunc (T) || IsTypeFuncPtr (T); +} +#endif + + + int IsObjectType (const Type* T) /* Return true if this is a fully described object type */ { @@ -919,6 +947,14 @@ int IsAggregateType (const Type* T) +int IsDerivedDeclaratorType (const Type* T) +/* Return true if this is an array, function or pointer type */ +{ + return IsTypeArray (T) || IsTypeFunc (T) || IsTypePtr (T); +} + + + int IsRelationType (const Type* T) /* Return true if this is an arithmetic, array or pointer type */ { @@ -953,6 +989,35 @@ int IsIncompleteESUType (const Type* T) +int IsAnonESUType (const Type* T) +/* Return true if this is an anonymous ESU type */ +{ + SymEntry* TagSym = GetESUTagSym (T); + + return TagSym != 0 && SymHasAnonName (TagSym); +} + + + +int IsAnonStructClass (const Type* T) +/* Return true if this is an anonymous struct or union type */ +{ + return IsClassStruct (T) && IsAnonESUType (T); +} + + + +int IsPassByRefType (const Type* T) +/* Return true if this is a large struct/union type that doesn't fit in the +** primary. This returns false for the void value extension type since it is +** not passable at all. +*/ +{ + return IsClassStruct (T) && GetStructReplacementType (T) == T; +} + + + int IsEmptiableObjectType (const Type* T) /* Return true if this is a struct/union/void type that can have zero size */ { @@ -975,7 +1040,11 @@ int HasUnknownSize (const Type* T) int TypeHasAttrData (const Type* T) /* Return true if the given type has attribute data */ { - return IsClassStruct (T) || IsTypeArray (T) || IsClassFunc (T); + return IsClassStruct (T) || + IsTypeArray (T) || + IsClassFunc (T) || + IsTypeVoid (T) || + IsTypeBitField (T); } @@ -1219,7 +1288,7 @@ void SetESUTagSym (Type* T, struct SymEntry* S) const char* GetBasicTypeName (const Type* T) /* Return a const name string of the basic type. -** Return "type" for unknown basic types. +** Return "<type>" for unknown basic types. */ { switch (GetRawTypeRank (T)) { @@ -1269,7 +1338,7 @@ const char* GetBasicTypeName (const Type* T) } } } - return "type"; + return "<type>"; } @@ -1305,6 +1374,65 @@ const char* GetFullTypeName (const Type* T) +static void GetParameterList (StrBuf* ParamList, StrBuf* Buf, const FuncDesc* D, int Detailed) +{ + /* First argument */ + const SymEntry* Param = D->SymTab->SymHead; + unsigned I; + + if ((D->Flags & FD_OLDSTYLE) == 0) { + /* ANSI style */ + for (I = 0; I < D->ParamCount; ++I) { + CHECK (Param != 0 && (Param->Flags & SC_PARAM) != 0); + if (I > 0) { + SB_AppendStr (ParamList, ", "); + } + if (Detailed) { + if (SymIsRegVar (Param)) { + SB_AppendStr (ParamList, "register "); + } + if (!SymHasAnonName (Param)) { + SB_AppendStr (Buf, Param->Name); + } + } + SB_AppendStr (ParamList, SB_GetConstBuf (GetFullTypeNameBuf (Buf, Param->Type))); + SB_Clear (Buf); + /* Next argument */ + Param = Param->NextSym; + } + if ((D->Flags & FD_VARIADIC) == 0) { + if (D->ParamCount == 0 && (D->Flags & FD_EMPTY) == 0) { + SB_AppendStr (ParamList, "void"); + } + } else { + if (D->ParamCount > 0) { + SB_AppendStr (ParamList, ", ..."); + } else { + SB_AppendStr (ParamList, "..."); + } + } + } else { + /* K&R style */ + if (Detailed) { + for (I = 0; I < D->ParamCount; ++I) { + CHECK (Param != 0 && (Param->Flags & SC_PARAM) != 0); + if (I > 0) { + SB_AppendStr (ParamList, ", "); + } + if (!SymHasAnonName (Param)) { + SB_AppendStr (ParamList, Param->Name); + } + /* Next argument */ + Param = Param->NextSym; + } + } + SB_Clear (Buf); + } + SB_Terminate (ParamList); +} + + + static struct StrBuf* GetFullTypeNameWestEast (struct StrBuf* West, struct StrBuf* East, const Type* T) /* Return the name string of the given type split into a western part and an ** eastern part. @@ -1344,34 +1472,12 @@ static struct StrBuf* GetFullTypeNameWestEast (struct StrBuf* West, struct StrBu } else if (IsTypeFunc (T)) { - FuncDesc* D = GetFuncDesc (T); - struct StrBuf ParamList = AUTO_STRBUF_INITIALIZER; + int QualCount = 0; + struct StrBuf ParamList = AUTO_STRBUF_INITIALIZER; + const FuncDesc* D = GetFuncDesc (T); - /* First argument */ - SymEntry* Param = D->SymTab->SymHead; - unsigned I; - for (I = 0; I < D->ParamCount; ++I) { - CHECK (Param != 0 && (Param->Flags & SC_PARAM) != 0); - if (I > 0) { - SB_AppendStr (&ParamList, ", "); - } - SB_AppendStr (&ParamList, SB_GetConstBuf (GetFullTypeNameBuf (&Buf, Param->Type))); - SB_Clear (&Buf); - /* Next argument */ - Param = Param->NextSym; - } - if ((D->Flags & FD_VARIADIC) == 0) { - if (D->ParamCount == 0 && (D->Flags & FD_EMPTY) == 0) { - SB_AppendStr (&ParamList, "void"); - } - } else { - if (D->ParamCount > 0) { - SB_AppendStr (&ParamList, ", ..."); - } else { - SB_AppendStr (&ParamList, "..."); - } - } - SB_Terminate (&ParamList); + /* Get the parameter list string */ + GetParameterList (&ParamList, &Buf, D, 0); /* Join the existing West and East together */ if (!SB_IsEmpty (East)) { @@ -1380,13 +1486,27 @@ static struct StrBuf* GetFullTypeNameWestEast (struct StrBuf* West, struct StrBu SB_Clear (East); } + /* Add qualifiers */ + if ((GetQualifier (T) & ~T_QUAL_NEAR) != T_QUAL_NONE) { + QualCount = GetQualifierTypeCodeNameBuf (&Buf, T->C, T_QUAL_NEAR); + if (QualCount > 0) { + SB_AppendChar (&Buf, ' '); + } + } + if (SB_IsEmpty (West)) { - /* Just use the param list */ - SB_Printf (West, "(%s)", SB_GetConstBuf (&ParamList)); + /* Use no parentheses */ + SB_Terminate (&Buf); + + /* Append the param list to the West */ + SB_Printf (West, "%s(%s)", SB_GetConstBuf (&Buf), SB_GetConstBuf (&ParamList)); } else { - /* Append the param list to the existing West */ - SB_Printf (&Buf, "(%s)(%s)", SB_GetConstBuf (West), SB_GetConstBuf (&ParamList)); - SB_Printf (West, "%s", SB_GetConstBuf (&Buf)); + /* Append the existing West */ + SB_Append (&Buf, West); + SB_Terminate (&Buf); + + /* Append the param list to the West */ + SB_Printf (West, "(%s)(%s)", SB_GetConstBuf (&Buf), SB_GetConstBuf (&ParamList)); } SB_Done (&ParamList); @@ -1429,7 +1549,7 @@ static struct StrBuf* GetFullTypeNameWestEast (struct StrBuf* West, struct StrBu if (!IsTypeBitField (T)) { SB_AppendStr (&Buf, GetTagSymName (T)); } else { - SB_AppendStr (&Buf, GetBasicTypeName (T + 1)); + SB_AppendStr (&Buf, GetBasicTypeName (GetBitFieldDeclType (T))); } if (!SB_IsEmpty (West)) { @@ -1549,37 +1669,8 @@ void PrintFuncSig (FILE* F, const char* Name, const Type* T) /* Get the function descriptor used in definition */ const FuncDesc* D = GetFuncDefinitionDesc (T); - /* Get the parameter list string. Start from the first parameter */ - SymEntry* Param = D->SymTab->SymHead; - unsigned I; - for (I = 0; I < D->ParamCount; ++I) { - CHECK (Param != 0 && (Param->Flags & SC_PARAM) != 0); - if (I > 0) { - SB_AppendStr (&ParamList, ", "); - } - if (SymIsRegVar (Param)) { - SB_AppendStr (&ParamList, "register "); - } - if (!SymHasAnonName (Param)) { - SB_AppendStr (&Buf, Param->Name); - } - SB_AppendStr (&ParamList, SB_GetConstBuf (GetFullTypeNameBuf (&Buf, Param->Type))); - SB_Clear (&Buf); - /* Next argument */ - Param = Param->NextSym; - } - if ((D->Flags & FD_VARIADIC) == 0) { - if (D->ParamCount == 0 && (D->Flags & FD_EMPTY) == 0) { - SB_AppendStr (&ParamList, "void"); - } - } else { - if (D->ParamCount > 0) { - SB_AppendStr (&ParamList, ", ..."); - } else { - SB_AppendStr (&ParamList, "..."); - } - } - SB_Terminate (&ParamList); + /* Get the parameter list string */ + GetParameterList (&ParamList, &Buf, D, 1); /* Get the function qualifiers */ if (GetQualifierTypeCodeNameBuf (&Buf, T->C, T_QUAL_NONE) > 0) { @@ -1590,16 +1681,44 @@ void PrintFuncSig (FILE* F, const char* Name, const Type* T) /* Get the signature string without the return type */ SB_Printf (&West, "%s%s (%s)", SB_GetConstBuf (&Buf), Name, SB_GetConstBuf (&ParamList)); - SB_Done (&Buf); - SB_Done (&ParamList); /* Complete with the return type */ GetFullTypeNameWestEast (&West, &East, GetFuncReturnType (T)); SB_Append (&West, &East); + + /* Check if the function is defined in K&R style */ + if ((D->Flags & FD_OLDSTYLE) != 0 && D->ParamCount > 0) { + /* First argument */ + const SymEntry* Param = D->SymTab->SymHead; + unsigned I; + + SB_Clear (&ParamList); + SB_Clear (&Buf); + for (I = 0; I < D->ParamCount; ++I) { + CHECK (Param != 0 && (Param->Flags & SC_PARAM) != 0); + SB_AppendChar (&ParamList, ' '); + if (SymIsRegVar (Param)) { + SB_AppendStr (&ParamList, "register "); + } + if (!SymHasAnonName (Param)) { + SB_AppendStr (&Buf, Param->Name); + } + SB_AppendStr (&ParamList, SB_GetConstBuf (GetFullTypeNameBuf (&Buf, Param->Type))); + SB_AppendChar (&ParamList, ';'); + SB_Clear (&Buf); + + /* Next argument */ + Param = Param->NextSym; + } + SB_Append (&West, &ParamList); + } + SB_Terminate (&West); /* Output */ fprintf (F, "%s", SB_GetConstBuf (&West)); + SB_Done (&ParamList); + SB_Done (&Buf); SB_Done (&East); SB_Done (&West); } diff --git a/src/cc65/datatype.h b/src/cc65/datatype.h index eebd3abd8..8446fb914 100644 --- a/src/cc65/datatype.h +++ b/src/cc65/datatype.h @@ -215,6 +215,8 @@ struct Type { extern const Type type_char[]; extern const Type type_schar[]; extern const Type type_uchar[]; +extern const Type type_short[]; +extern const Type type_ushort[]; extern const Type type_int[]; extern const Type type_uint[]; extern const Type type_long[]; @@ -326,7 +328,7 @@ INLINE TypeCode GetQualifier (const Type* T) # define GetQualifier(T) ((T)->C & T_MASK_QUAL) #endif -TypeCode GetUnqualTypeCode (const Type* Type); +TypeCode GetUnderlyingTypeCode (const Type* Type); /* Get the type code of the unqualified underlying type of Type. ** Return GetUnqualRawTypeCode (Type) if Type is not scalar. */ @@ -357,30 +359,30 @@ INLINE TypeCode GetTypeClass (const Type* T) INLINE TypeCode GetTypeRank (const Type* T) /* Get the type rank of a type */ { - return (GetUnqualTypeCode (T) & T_MASK_RANK); + return (GetUnderlyingTypeCode (T) & T_MASK_RANK); } #else -# define GetTypeRank(T) (GetUnqualTypeCode (T) & T_MASK_RANK) +# define GetTypeRank(T) (GetUnderlyingTypeCode (T) & T_MASK_RANK) #endif #if defined(HAVE_INLINE) INLINE TypeCode GetSignedness (const Type* T) /* Get the signedness of a type */ { - return (GetUnqualTypeCode (T) & T_MASK_SIGN); + return (GetUnderlyingTypeCode (T) & T_MASK_SIGN); } #else -# define GetSignedness(T) (GetUnqualTypeCode (T) & T_MASK_SIGN) +# define GetSignedness(T) (GetUnderlyingTypeCode (T) & T_MASK_SIGN) #endif #if defined(HAVE_INLINE) INLINE TypeCode GetSizeModifier (const Type* T) /* Get the size modifier of a type */ { - return (GetUnqualTypeCode (T) & T_MASK_SIZE); + return (GetUnderlyingTypeCode (T) & T_MASK_SIZE); } #else -# define GetSizeModifier(T) (GetUnqualTypeCode (T) & T_MASK_SIZE) +# define GetSizeModifier(T) (GetUnderlyingTypeCode (T) & T_MASK_SIZE) #endif #if defined(HAVE_INLINE) @@ -433,8 +435,8 @@ Type* NewPointerTo (const Type* T); */ Type* NewBitFieldOf (const Type* T, unsigned BitOffs, unsigned BitWidth); -/* Return a type string that is "T : BitWidth" aligned on BitOffs. The type -** string is allocated on the heap and may be freed after use. +/* Return a type string that is "unqualified T : BitWidth" aligned on BitOffs. +** The type string is allocated on the heap and may be freed after use. */ const Type* AddressOf (const Type* T); @@ -482,6 +484,9 @@ const Type* GetUnderlyingType (const Type* Type); const Type* GetStructReplacementType (const Type* SType); /* Get a replacement type for passing a struct/union by value in the primary */ +const Type* GetBitFieldDeclType (const Type* Type); +/* Get the original integer type used to declare the bit-field */ + const Type* GetBitFieldChunkType (const Type* Type); /* Get the type needed to operate on the byte chunk containing the bit-field */ @@ -690,6 +695,17 @@ INLINE int IsTypeFuncPtr (const Type* T) # define IsTypeFuncPtr(T) (IsTypePtr (T) && IsTypeFunc (T+1)) #endif +#if defined(HAVE_INLINE) +INLINE int IsTypeFuncLike (const Type* T) +/* Return true if this is a function or a function pointer */ +{ + return IsTypeFunc (T) || IsTypeFuncPtr (T); +} +#else +int IsTypeFuncLike (const Type* T); +/* Return true if this is a function or a function pointer */ +#endif + #if defined(HAVE_INLINE) INLINE int IsClassInt (const Type* T) /* Return true if this is an integer type */ @@ -761,6 +777,9 @@ int IsDerivedType (const Type* T); int IsAggregateType (const Type* T); /* Return true if this is an array or struct type */ +int IsDerivedDeclaratorType (const Type* T); +/* Return true if this is an array, function or pointer type */ + int IsRelationType (const Type* T); /* Return true if this is an arithmetic, array or pointer type */ @@ -773,6 +792,18 @@ int IsESUType (const Type* T); int IsIncompleteESUType (const Type* T); /* Return true if this is an incomplete ESU type */ +int IsAnonESUType (const Type* T); +/* Return true if this is an anonymous ESU type */ + +int IsAnonStructClass (const Type* T); +/* Return true if this is an anonymous struct or union type */ + +int IsPassByRefType (const Type* T); +/* Return true if this is a large struct/union type that doesn't fit in the +** primary. This returns false for the void value extension type since it is +** not passable at all. +*/ + int IsEmptiableObjectType (const Type* T); /* Return true if this is a struct/union/void type that can have zero size */ @@ -1024,7 +1055,7 @@ void SetESUTagSym (Type* T, struct SymEntry* S); const char* GetBasicTypeName (const Type* T); /* Return a const name string of the basic type. -** Return "type" for unknown basic types. +** Return "<type>" for unknown basic types. */ const char* GetFullTypeName (const Type* T); diff --git a/src/cc65/declare.c b/src/cc65/declare.c index 59eb555c4..e9f519ebe 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -61,7 +61,6 @@ #include "standard.h" #include "staticassert.h" #include "symtab.h" -#include "wrappedcall.h" #include "typeconv.h" @@ -72,17 +71,33 @@ -static void ParseTypeSpec (DeclSpec* D, typespec_t TSFlags, int* SignednessSpecified); -/* Parse a type specifier */ +static SymEntry* ParseEnumSpec (const char* Name, unsigned* DSFlags); +/* Parse an enum specifier */ + +static SymEntry* ParseUnionSpec (const char* Name, unsigned* DSFlags); +/* Parse a union specifier */ + +static SymEntry* ParseStructSpec (const char* Name, unsigned* DSFlags); +/* Parse a struct specifier */ /*****************************************************************************/ -/* Internal functions */ +/* Type specification parser */ /*****************************************************************************/ +static void InitDeclSpec (DeclSpec* Spec) +/* Initialize the DeclSpec struct for use */ +{ + Spec->StorageClass = 0; + Spec->Type[0].C = T_END; + Spec->Flags = 0; +} + + + static unsigned ParseOneStorageClass (void) /* Parse and return a storage class specifier */ { @@ -92,7 +107,7 @@ static unsigned ParseOneStorageClass (void) switch (CurTok.Tok) { case TOK_EXTERN: - StorageClass = SC_EXTERN | SC_STATIC; + StorageClass = SC_EXTERN; NextToken (); break; @@ -102,7 +117,7 @@ static unsigned ParseOneStorageClass (void) break; case TOK_REGISTER: - StorageClass = SC_REGISTER | SC_STATIC; + StorageClass = SC_REGISTER; NextToken (); break; @@ -125,9 +140,37 @@ static unsigned ParseOneStorageClass (void) -static int ParseStorageClass (DeclSpec* D) +static unsigned ParseOneFuncSpec (void) +/* Parse and return a function specifier */ +{ + unsigned FuncSpec = 0; + + /* Check the function specifier given */ + switch (CurTok.Tok) { + + case TOK_INLINE: + FuncSpec = SC_INLINE; + NextToken (); + break; + + case TOK_NORETURN: + FuncSpec = SC_NORETURN; + NextToken (); + break; + + default: + break; + } + + return FuncSpec; +} + + + +static int ParseStorageClass (DeclSpec* Spec) /* Parse storage class specifiers. Return true if a specifier is read even if -** it was duplicated or disallowed. */ +** it was duplicated or disallowed. +*/ { /* Check the storage class given */ unsigned StorageClass = ParseOneStorageClass (); @@ -137,9 +180,9 @@ static int ParseStorageClass (DeclSpec* D) } while (StorageClass != 0) { - if (D->StorageClass == 0) { - D->StorageClass = StorageClass; - } else if (D->StorageClass == StorageClass) { + if ((Spec->StorageClass & SC_STORAGEMASK) == 0) { + Spec->StorageClass |= StorageClass; + } else if ((Spec->StorageClass & SC_STORAGEMASK) == StorageClass) { Warning ("Duplicate storage class specifier"); } else { Error ("Conflicting storage class specifier"); @@ -152,6 +195,31 @@ static int ParseStorageClass (DeclSpec* D) +static int ParseFuncSpecClass (DeclSpec* Spec) +/* Parse function specifiers. Return true if a specifier is read even if it +** was duplicated or disallowed. +*/ +{ + /* Check the function specifiers given */ + unsigned FuncSpec = ParseOneFuncSpec (); + + if (FuncSpec == 0) { + return 0; + } + + while (FuncSpec != 0) { + if ((Spec->StorageClass & FuncSpec) != 0) { + Warning ("Duplicate function specifier"); + } + Spec->StorageClass |= FuncSpec; + FuncSpec = ParseOneFuncSpec (); + } + + return 1; +} + + + static void DuplicateQualifier (const char* Name) /* Print an error message */ { @@ -304,7 +372,8 @@ static void OptionalSpecifiers (DeclSpec* Spec, TypeCode* Qualifiers, typespec_t */ { TypeCode Q = T_QUAL_NONE; - int Continue; + int HasStorageClass; + int HasFuncSpec; do { /* There may be type qualifiers *before* any storage class specifiers */ @@ -312,11 +381,17 @@ static void OptionalSpecifiers (DeclSpec* Spec, TypeCode* Qualifiers, typespec_t *Qualifiers |= Q; /* Parse storage class specifiers anyway then check */ - Continue = ParseStorageClass (Spec); - if (Continue && (TSFlags & (TS_STORAGE_CLASS_SPEC | TS_FUNCTION_SPEC)) == 0) { + HasStorageClass = ParseStorageClass (Spec); + if (HasStorageClass && (TSFlags & TS_STORAGE_CLASS_SPEC) == 0) { Error ("Unexpected storage class specified"); } - } while (Continue || Q != T_QUAL_NONE); + + /* Parse function specifiers anyway then check */ + HasFuncSpec = ParseFuncSpecClass (Spec); + if (HasFuncSpec && (TSFlags & TS_FUNCTION_SPEC) == 0) { + Error ("Unexpected function specifiers"); + } + } while (Q != T_QUAL_NONE || HasStorageClass || HasFuncSpec); } @@ -332,30 +407,1166 @@ static void OptionalInt (void) -static void OptionalSigned (int* SignednessSpecified) +static void OptionalSigned (DeclSpec* Spec) /* Eat an optional "signed" token */ { if (CurTok.Tok == TOK_SIGNED) { /* Skip it */ NextToken (); - if (SignednessSpecified != NULL) { - *SignednessSpecified = 1; + if (Spec != NULL) { + Spec->Flags |= DS_EXPLICIT_SIGNEDNESS; } } } -void InitDeclSpec (DeclSpec* D) -/* Initialize the DeclSpec struct for use */ +static void UseDefaultType (DeclSpec* Spec, typespec_t TSFlags) +/* Use the default type for the type specifier */ { - D->StorageClass = 0; - D->Type[0].C = T_END; - D->Flags = 0; + if ((TSFlags & TS_MASK_DEFAULT_TYPE) == TS_DEFAULT_TYPE_NONE) { + Spec->Flags = (Spec->Flags & ~DS_TYPE_MASK) | DS_NONE; + Spec->Type[0].C = T_INT; + Spec->Type[1].C = T_END; + } else { + Spec->Flags = (Spec->Flags & ~DS_TYPE_MASK) | DS_DEF_TYPE; + Spec->Type[0].C = T_INT; + Spec->Type[1].C = T_END; + } } +static void ParseTypeSpec (DeclSpec* Spec, typespec_t TSFlags) +/* Parse a type specifier. Store whether one of "signed" or "unsigned" was +** specified, so bit-fields of unspecified signedness can be treated as +** unsigned; without special handling, it would be treated as signed. +*/ +{ + ident Ident; + SymEntry* TagEntry; + TypeCode Qualifiers = T_QUAL_NONE; + + /* Assume we have an explicitly specified type */ + Spec->Flags = (Spec->Flags & ~DS_TYPE_MASK) | DS_EXPLICIT_TYPE; + + /* Read storage specifiers and/or type qualifiers if we have any */ + OptionalSpecifiers (Spec, &Qualifiers, TSFlags); + + /* Look at the data type */ + switch (CurTok.Tok) { + + case TOK_VOID: + NextToken (); + Spec->Type[0].C = T_VOID; + Spec->Type[0].A.U = 0; + Spec->Type[1].C = T_END; + break; + + case TOK_CHAR: + NextToken (); + Spec->Type[0].C = T_CHAR; + Spec->Type[1].C = T_END; + break; + + case TOK_LONG: + NextToken (); + if (CurTok.Tok == TOK_UNSIGNED) { + Spec->Flags |= DS_EXPLICIT_SIGNEDNESS; + NextToken (); + OptionalInt (); + Spec->Type[0].C = T_ULONG; + Spec->Type[1].C = T_END; + } else { + OptionalSigned (Spec); + OptionalInt (); + Spec->Type[0].C = T_LONG; + Spec->Type[1].C = T_END; + } + break; + + case TOK_SHORT: + NextToken (); + if (CurTok.Tok == TOK_UNSIGNED) { + Spec->Flags |= DS_EXPLICIT_SIGNEDNESS; + NextToken (); + OptionalInt (); + Spec->Type[0].C = T_USHORT; + Spec->Type[1].C = T_END; + } else { + OptionalSigned (Spec); + OptionalInt (); + Spec->Type[0].C = T_SHORT; + Spec->Type[1].C = T_END; + } + break; + + case TOK_INT: + NextToken (); + Spec->Type[0].C = T_INT; + Spec->Type[1].C = T_END; + break; + + case TOK_SIGNED: + Spec->Flags |= DS_EXPLICIT_SIGNEDNESS; + NextToken (); + switch (CurTok.Tok) { + + case TOK_CHAR: + NextToken (); + Spec->Type[0].C = T_SCHAR; + Spec->Type[1].C = T_END; + break; + + case TOK_SHORT: + NextToken (); + OptionalInt (); + Spec->Type[0].C = T_SHORT; + Spec->Type[1].C = T_END; + break; + + case TOK_LONG: + NextToken (); + OptionalInt (); + Spec->Type[0].C = T_LONG; + Spec->Type[1].C = T_END; + break; + + case TOK_INT: + NextToken (); + /* FALL THROUGH */ + + default: + Spec->Type[0].C = T_INT; + Spec->Type[1].C = T_END; + break; + } + break; + + case TOK_UNSIGNED: + Spec->Flags |= DS_EXPLICIT_SIGNEDNESS; + NextToken (); + switch (CurTok.Tok) { + + case TOK_CHAR: + NextToken (); + Spec->Type[0].C = T_UCHAR; + Spec->Type[1].C = T_END; + break; + + case TOK_SHORT: + NextToken (); + OptionalInt (); + Spec->Type[0].C = T_USHORT; + Spec->Type[1].C = T_END; + break; + + case TOK_LONG: + NextToken (); + OptionalInt (); + Spec->Type[0].C = T_ULONG; + Spec->Type[1].C = T_END; + break; + + case TOK_INT: + NextToken (); + /* FALL THROUGH */ + + default: + Spec->Type[0].C = T_UINT; + Spec->Type[1].C = T_END; + break; + } + break; + + case TOK_FLOAT: + NextToken (); + Spec->Type[0].C = T_FLOAT; + Spec->Type[1].C = T_END; + break; + + case TOK_DOUBLE: + NextToken (); + Spec->Type[0].C = T_DOUBLE; + Spec->Type[1].C = T_END; + break; + + case TOK_UNION: + NextToken (); + /* Remember we have an extra type decl */ + Spec->Flags |= DS_EXTRA_TYPE; + /* Check for tag name */ + if (CurTok.Tok == TOK_IDENT) { + strcpy (Ident, CurTok.Ident); + NextToken (); + } else if (CurTok.Tok == TOK_LCURLY) { + AnonName (Ident, "union"); + } else { + Error ("Tag name identifier or '{' expected"); + UseDefaultType (Spec, TS_DEFAULT_TYPE_NONE); + break; + } + /* Declare the union in the current scope */ + TagEntry = ParseUnionSpec (Ident, &Spec->Flags); + /* Encode the union entry into the type */ + Spec->Type[0].C = T_UNION; + SetESUTagSym (Spec->Type, TagEntry); + Spec->Type[1].C = T_END; + break; + + case TOK_STRUCT: + NextToken (); + /* Remember we have an extra type decl */ + Spec->Flags |= DS_EXTRA_TYPE; + /* Check for tag name */ + if (CurTok.Tok == TOK_IDENT) { + strcpy (Ident, CurTok.Ident); + NextToken (); + } else if (CurTok.Tok == TOK_LCURLY) { + AnonName (Ident, "struct"); + } else { + Error ("Tag name identifier or '{' expected"); + UseDefaultType (Spec, TS_DEFAULT_TYPE_NONE); + break; + } + /* Declare the struct in the current scope */ + TagEntry = ParseStructSpec (Ident, &Spec->Flags); + /* Encode the struct entry into the type */ + Spec->Type[0].C = T_STRUCT; + SetESUTagSym (Spec->Type, TagEntry); + Spec->Type[1].C = T_END; + break; + + case TOK_ENUM: + NextToken (); + /* Remember we have an extra type decl */ + Spec->Flags |= DS_EXTRA_TYPE; + /* Check for tag name */ + if (CurTok.Tok == TOK_IDENT) { + strcpy (Ident, CurTok.Ident); + NextToken (); + } else if (CurTok.Tok == TOK_LCURLY) { + AnonName (Ident, "enum"); + } else { + Error ("Tag name identifier or '{' expected"); + UseDefaultType (Spec, TS_DEFAULT_TYPE_NONE); + break; + } + /* Parse the enum decl */ + TagEntry = ParseEnumSpec (Ident, &Spec->Flags); + /* Encode the enum entry into the type */ + Spec->Type[0].C |= T_ENUM; + SetESUTagSym (Spec->Type, TagEntry); + Spec->Type[1].C = T_END; + /* The signedness of enums is determined by the type, so say this is specified to avoid + ** the int -> unsigned int handling for plain int bit-fields in AddBitField. + */ + Spec->Flags |= DS_EXPLICIT_SIGNEDNESS; + break; + + case TOK_IDENT: + /* This could be a label */ + if (NextTok.Tok != TOK_COLON || GetLexicalLevel () == LEX_LEVEL_STRUCT) { + TagEntry = FindSym (CurTok.Ident); + if (TagEntry && SymIsTypeDef (TagEntry)) { + /* It's a typedef */ + NextToken (); + TypeCopy (Spec->Type, TagEntry->Type); + /* If it's a typedef, we should actually use whether the signedness was + ** specified on the typedef, but that information has been lost. Treat the + ** signedness as being specified to work around the ICE in #1267. + ** Unforunately, this will cause plain int bit-fields defined via typedefs + ** to be treated as signed rather than unsigned. + */ + Spec->Flags |= DS_EXPLICIT_SIGNEDNESS; + break; + } else if ((TSFlags & TS_MASK_DEFAULT_TYPE) == TS_DEFAULT_TYPE_NONE) { + /* Treat this identifier as an unknown type */ + Error ("Unknown type name '%s'", CurTok.Ident); + TypeCopy (Spec->Type, type_int); + NextToken (); + break; + } + } else { + /* This is a label. Use the default type flag to end the loop + ** in DeclareLocals. The type code used here doesn't matter as + ** long as it has no qualifiers. + */ + UseDefaultType (Spec, TS_DEFAULT_TYPE_INT); + break; + } + /* FALL THROUGH */ + + default: + UseDefaultType (Spec, TSFlags); + break; + } + + /* There may also be specifiers/qualifiers *after* the initial type */ + OptionalSpecifiers (Spec, &Qualifiers, TSFlags); + Spec->Type[0].C |= Qualifiers; +} + + + +/*****************************************************************************/ +/* Enum/struct/union parser */ +/*****************************************************************************/ + + + +static SymEntry* ForwardESU (const char* Name, unsigned Flags, unsigned* DSFlags) +/* Handle an enum, struct or union forward declaration */ +{ + /* Try to find an enum/struct/union with the given name. If there is none, + ** insert a forward declaration into the current lexical level. + */ + SymEntry* TagEntry = FindTagSym (Name); + if (TagEntry == 0) { + if ((Flags & SC_TYPEMASK) != SC_ENUM) { + TagEntry = AddStructSym (Name, Flags, 0, 0, DSFlags); + } else { + TagEntry = AddEnumSym (Name, Flags, 0, 0, DSFlags); + } + } else if ((TagEntry->Flags & SC_TYPEMASK) != (Flags & SC_TYPEMASK)) { + /* Already defined, but not the same type class */ + Error ("Symbol '%s' is already different kind", Name); + } + return TagEntry; +} + + + +static const Type* GetEnumeratorType (long Min, unsigned long Max, int Signed) +/* GitHub #1093 - We use unsigned types to save spaces whenever possible. +** If both the signed and unsigned integer types of the same minimum size +** capable of representing all values of the enum, we prefer the unsigned +** one. +** Return 0 if impossible to represent Min and Max as the same integer type. +*/ +{ + const Type* Underlying = type_int; /* default type */ + + /* Change the underlying type if necessary */ + if (Min < 0 || Signed) { + /* We can't use unsigned types if there are any negative values */ + if (Max > (unsigned long)INT32_MAX) { + /* No way to represent both Min and Max as the same integer type */ + Underlying = 0; + } else if (Min < INT16_MIN || Max > (unsigned long)INT16_MAX) { + Underlying = type_long; + } else if (Min < INT8_MIN || Max > (unsigned long)INT8_MAX) { + Underlying = type_int; + } else { + Underlying = type_schar; + } + } else { + if (Max > UINT16_MAX) { + Underlying = type_ulong; + } else if (Max > UINT8_MAX) { + Underlying = type_uint; + } else { + Underlying = type_uchar; + } + } + + return Underlying; +} + + + +static SymEntry* ParseEnumSpec (const char* Name, unsigned* DSFlags) +/* Parse an enum specifier */ +{ + SymTable* FieldTab; + long EnumVal; + int IsSigned; + int IsIncremented; + ident Ident; + long MinConstant = 0; + unsigned long MaxConstant = 0; + const Type* NewType = 0; /* new member type */ + const Type* MemberType = type_int; /* default member type */ + unsigned Flags = 0; + unsigned PrevErrorCount = ErrorCount; + + + if (CurTok.Tok != TOK_LCURLY) { + /* Just a forward definition */ + return ForwardESU (Name, SC_ENUM, DSFlags); + } + + /* Add a forward declaration for the enum tag in the current lexical level */ + AddEnumSym (Name, 0, 0, 0, DSFlags); + + /* Skip the opening curly brace */ + NextToken (); + + /* Read the enum tags */ + EnumVal = -1L; + while (CurTok.Tok != TOK_RCURLY) { + + /* We expect an identifier */ + if (CurTok.Tok != TOK_IDENT) { + Error ("Identifier expected for enumerator declarator"); + /* Avoid excessive errors */ + NextToken (); + continue; + } + + /* Remember the identifier and skip it */ + strcpy (Ident, CurTok.Ident); + NextToken (); + + /* Check for an assigned value */ + if (CurTok.Tok == TOK_ASSIGN) { + + NextToken (); + ExprDesc Expr = NoCodeConstAbsIntExpr (hie1); + EnumVal = Expr.IVal; + MemberType = Expr.Type; + IsSigned = IsSignSigned (MemberType); + IsIncremented = 0; + + } else { + + /* Defaulted with the same signedness as the previous member's */ + IsSigned = IsSignSigned (MemberType) && + (unsigned long)EnumVal != GetIntegerTypeMax (MemberType); + + /* Enumerate by adding one to the previous value */ + EnumVal = (long)(((unsigned long)EnumVal + 1UL) & 0xFFFFFFFFUL); + + if (GetUnqualRawTypeCode (MemberType) == T_ULONG && EnumVal == 0) { + /* Error since the new value cannot be represented in the + ** largest unsigned integer type supported by cc65 for enum. + */ + Error ("Enumerator '%s' overflows the range of '%s'", + Ident, + GetBasicTypeName (type_ulong)); + } + + IsIncremented = 1; + } + + /* Track down the min/max values and evaluate the type of EnumVal + ** using GetEnumeratorType in a tricky way. + */ + if (!IsSigned || EnumVal >= 0) { + if ((unsigned long)EnumVal > MaxConstant) { + MaxConstant = (unsigned long)EnumVal; + } + NewType = GetEnumeratorType (0, EnumVal, IsSigned); + } else { + if (EnumVal < MinConstant) { + MinConstant = EnumVal; + } + NewType = GetEnumeratorType (EnumVal, 0, 1); + } + + /* GetEnumeratorType above should never fail, but just in case */ + if (NewType == 0) { + Internal ("Unexpected failure with GetEnumeratorType: %lx", EnumVal); + NewType = type_ulong; + } else if (SizeOf (NewType) < SizeOf (type_int)) { + /* Integer constants are not shorter than int */ + NewType = type_int; + } + + /* Warn if the incremented value exceeds the range of the previous + ** type. + */ + if (PrevErrorCount == ErrorCount && + IsIncremented && + (!IsSigned || EnumVal >= 0) && + NewType->C != GetUnqualRawTypeCode (MemberType)) { + /* The possible overflow here can only be when EnumVal > 0 */ + Warning ("Enumerator '%s' (value = %lu) implies type '%s'", + Ident, + (unsigned long)EnumVal, + GetBasicTypeName (NewType)); + } + + /* Warn if the value exceeds range of 'int' in standard mode */ + if (IS_Get (&Standard) != STD_CC65 && NewType->C != T_INT) { + if (!IsSigned || EnumVal >= 0) { + Warning ("ISO C restricts enumerator values to range of 'int'\n" + "\tEnumerator '%s' (value = %lu) is too large", + Ident, + (unsigned long)EnumVal); + } else { + Warning ("ISO C restricts enumerator values to range of 'int'\n" + "\tEnumerator '%s' (value = %ld) is too small", + Ident, + EnumVal); + } + } + + /* Add an entry of the enumerator to the symbol table */ + AddConstSym (Ident, NewType, SC_DEF | SC_ENUMERATOR, EnumVal); + + /* Use this type for following members */ + MemberType = NewType; + + /* Check for end of definition */ + if (CurTok.Tok != TOK_COMMA) { + break; + } + NextToken (); + } + ConsumeRCurly (); + + /* Check if there have been any members. Error if none */ + if (NewType == 0) { + Error ("Empty enum is invalid"); + } + + /* This evaluates the underlying type of the whole enum */ + MemberType = GetEnumeratorType (MinConstant, MaxConstant, 0); + if (MemberType == 0) { + /* It is very likely that the program is wrong */ + Error ("Enumeration values cannot be represented all as 'long'\n" + "\tMin enumerator value = %ld, Max enumerator value = %lu", + MinConstant, MaxConstant); + + /* Avoid more errors */ + MemberType = type_long; + } + + FieldTab = GetSymTab (); + + /* Return a fictitious symbol if errors occurred during parsing */ + if (PrevErrorCount != ErrorCount) { + Flags |= SC_FICTITIOUS; + } + + return AddEnumSym (Name, SC_DEF | Flags, MemberType, FieldTab, DSFlags); +} + + + +static int ParseFieldWidth (Declarator* D) +/* Parse an optional field width. Returns -1 if no field width is specified, +** otherwise the width of the field. +*/ +{ + ExprDesc Expr; + + if (CurTok.Tok != TOK_COLON) { + /* No bit-field declaration */ + return -1; + } + + if (!IsClassInt (D->Type)) { + /* Only integer types may be used for bit-fields */ + Error ("Bit-field has invalid type '%s', must be integral", + GetBasicTypeName (D->Type)); + + /* Avoid a diagnostic storm by giving the bit-field the widest valid + ** signed type, and continuing to parse. + */ + D->Type[0].C = T_LONG; + } + + if (IsTypeEnum (D->Type) && IsIncompleteESUType (D->Type)) { + /* If the type is an enum, it must be complete */ + Error ("Bit-field has incomplete type '%s'", + GetFullTypeName (D->Type)); + + /* Avoid a diagnostic storm */ + D->Type[0].C = T_LONG; + } + + /* We currently support integral types up to long */ + if (SizeOf (D->Type) > SizeOf (type_ulong)) { + /* Only long-sized or smaller types may be used for bit-fields, for now */ + Error ("cc65 currently supports only long-sized and smaller bit-field types"); + + /* Avoid a diagnostic storm */ + D->Type[0].C = T_LONG; + } + + /* Read the width */ + NextToken (); + Expr = NoCodeConstAbsIntExpr (hie1); + + if (Expr.IVal < 0) { + Error ("Negative width in bit-field"); + return -1; + } + if (Expr.IVal > (long)(SizeOf (D->Type) * CHAR_BITS)) { + Error ("Width of bit-field exceeds its type"); + return -1; + } + if (Expr.IVal == 0 && D->Ident[0] != '\0') { + Error ("Zero width for named bit-field"); + return -1; + } + + /* Return the field width */ + return (int) Expr.IVal; +} + + + +static unsigned PadWithBitField (unsigned StructSize, unsigned BitOffs) +/* Pad the current struct with an anonymous bit-field aligned to the next byte. +** Return how many bits are used to pad. +*/ +{ + /* MSVC complains about unary negation of unsigned, + ** so it has been rewritten as subtraction. + */ + unsigned PaddingBits = (0 - BitOffs) % CHAR_BITS; + + /* We need an anonymous name */ + ident Ident; + AnonName (Ident, "bit-field"); + + /* Add an anonymous bit-field that aligns to the next + ** byte. + */ + AddBitField (Ident, type_uchar, StructSize, BitOffs, PaddingBits, + /*SignednessSpecified=*/1); + + return PaddingBits; +} + + + +static unsigned AliasAnonStructFields (const Declarator* D, SymEntry* Anon) +/* Create alias fields from an anon union/struct in the current lexical level. +** The function returns the count of created aliases. +*/ +{ + unsigned Count = 0; + SymEntry* Field; + SymEntry* Alias; + + /* Get the symbol table containing the fields. If it is empty, there has + ** been an error before, so bail out. + */ + SymTable* Tab = GetESUTagSym (D->Type)->V.S.SymTab; + if (Tab == 0) { + /* Incomplete definition - has been flagged before */ + return 0; + } + + /* Get a pointer to the list of symbols. Then walk the list adding copies + ** of the embedded struct to the current level. + */ + Field = Tab->SymHead; + while (Field) { + + /* Enter an alias of this symbol */ + if (!IsAnonName (Field->Name)) { + Alias = AddLocalSym (Field->Name, Field->Type, SC_STRUCTFIELD|SC_ALIAS, 0); + Alias->V.A.Field = Field; + Alias->V.A.Offs = Anon->V.Offs + Field->V.Offs; + ++Count; + } + + /* Currently, there can not be any attributes, but if there will be + ** some in the future, we want to know this. + */ + CHECK (Field->Attr == 0); + + /* Next entry */ + Field = Field->NextSym; + } + + /* Return the count of created aliases */ + return Count; +} + + + +static SymEntry* ParseUnionSpec (const char* Name, unsigned* DSFlags) +/* Parse a union specifier */ +{ + + unsigned UnionSize; + unsigned FieldSize; + int FieldWidth; /* Width in bits, -1 if not a bit-field */ + SymTable* FieldTab; + SymEntry* UnionTagEntry; + SymEntry* Field; + unsigned Flags = 0; + unsigned PrevErrorCount = ErrorCount; + + + if (CurTok.Tok != TOK_LCURLY) { + /* Just a forward declaration */ + return ForwardESU (Name, SC_UNION, DSFlags); + } + + /* Add a forward declaration for the union tag in the current lexical level */ + UnionTagEntry = AddStructSym (Name, SC_UNION, 0, 0, DSFlags); + + UnionTagEntry->V.S.ACount = 0; + + /* Skip the curly brace */ + NextToken (); + + /* Enter a new lexical level for the struct */ + EnterStructLevel (); + + /* Parse union fields */ + UnionSize = 0; + while (CurTok.Tok != TOK_RCURLY && CurTok.Tok != TOK_CEOF) { + + /* Get the type of the entry */ + DeclSpec Spec; + int NeedClean = 0; + + /* Check for extra semicolons */ + if (CurTok.Tok == TOK_SEMI) { + /* TODO: warn on this if we have a pedantic mode */ + NextToken (); + continue; + } + + /* Check for a _Static_assert */ + if (CurTok.Tok == TOK_STATIC_ASSERT) { + ParseStaticAssert (); + continue; + } + + InitDeclSpec (&Spec); + ParseTypeSpec (&Spec, TS_DEFAULT_TYPE_NONE); + + /* Check if this is only a type declaration */ + if (CurTok.Tok == TOK_SEMI && + !(IS_Get (&Standard) >= STD_CC65 && IsAnonStructClass (Spec.Type))) { + CheckEmptyDecl (&Spec); + NextToken (); + continue; + } + + /* If we haven't got a type specifier yet, something must be wrong */ + if ((Spec.Flags & DS_TYPE_MASK) == DS_NONE) { + /* Avoid extra errors if it was a failed type specifier */ + if ((Spec.Flags & DS_EXTRA_TYPE) == 0) { + Error ("Declaration specifier expected"); + } + NeedClean = -1; + goto EndOfDecl; + } + + /* Allow anonymous bit-fields */ + Spec.Flags |= DS_ALLOW_BITFIELD; + + /* Read fields with this type */ + while (1) { + + Declarator Decl; + + /* Get type and name of the struct field */ + NeedClean = ParseDecl (&Spec, &Decl, DM_IDENT_OR_EMPTY); + + /* Bail out if there are errors */ + if (NeedClean <= 0) { + break; + } + + /* Check for a bit-field declaration */ + FieldWidth = ParseFieldWidth (&Decl); + + /* Check for fields without names */ + if (Decl.Ident[0] == '\0') { + if (FieldWidth < 0) { + /* In cc65 mode, we allow anonymous structs/unions within + ** a union. + */ + AnonFieldName (Decl.Ident, GetBasicTypeName (Decl.Type), UnionTagEntry->V.S.ACount); + + /* Ignore CVR qualifiers */ + if (IsQualConst (Decl.Type) || IsQualVolatile (Decl.Type) || IsQualRestrict (Decl.Type)) { + Warning ("Anonymous %s qualifiers are ignored", GetBasicTypeName (Decl.Type)); + Decl.Type[0].C &= ~T_QUAL_CVR; + } + } else if (FieldWidth > 0) { + /* A bit-field without a name will get an anonymous one */ + AnonName (Decl.Ident, "bit-field"); + } + } else if (IsIncompleteType (Decl.Type)) { + Error ("Field '%s' has incomplete type '%s'", + Decl.Ident, + GetFullTypeName (Decl.Type)); + } + + /* Check for const types */ + if (IsQualConst (Decl.Type)) { + Flags |= SC_HAVECONST; + } + + /* Ignore zero sized bit fields in a union */ + if (FieldWidth == 0) { + goto NextMember; + } + + /* Handle sizes */ + FieldSize = SizeOf (Decl.Type); + if (FieldSize > UnionSize) { + UnionSize = FieldSize; + } + + /* Add a field entry to the table */ + if (FieldWidth > 0) { + /* For a union, allocate space for the type specified by the + ** bit-field. + */ + AddBitField (Decl.Ident, Decl.Type, 0, 0, FieldWidth, + (Spec.Flags & DS_EXPLICIT_SIGNEDNESS) != 0); + } else if (Decl.Ident[0] != '\0') { + /* Add the new field to the table */ + Field = AddLocalSym (Decl.Ident, Decl.Type, SC_STRUCTFIELD, 0); + + /* Check the new field for certain kinds of members */ + if (IsClassStruct (Decl.Type)) { + SymEntry* TagEntry = GetESUTagSym (Decl.Type); + + /* Alias the fields of the anonymous member on the current level */ + if (IsAnonName (Decl.Ident)) { + Field->V.A.ANumber = UnionTagEntry->V.S.ACount++; + AliasAnonStructFields (&Decl, Field); + } + + /* Check if the field itself has a flexible array member */ + if (TagEntry && SymHasFlexibleArrayMember (TagEntry)) { + Field->Flags |= SC_HAVEFAM; + Flags |= SC_HAVEFAM; + } + + /* Check if the field itself has a const member */ + if (TagEntry && SymHasConstMember (TagEntry)) { + Field->Flags |= SC_HAVECONST; + Flags |= SC_HAVECONST; + } + } + } + +NextMember: + /* Check for end of declaration list */ + if (CurTok.Tok != TOK_COMMA) { + break; + } + Spec.Flags |= DS_NO_EMPTY_DECL; + NextToken (); + } + +EndOfDecl: + if (NeedClean > 0) { + /* Must be followed by a semicolon */ + if (ConsumeSemi ()) { + NeedClean = 0; + } else { + NeedClean = -1; + } + } + + /* Try some smart error recovery */ + if (NeedClean < 0) { + SmartErrorSkip (1); + } + } + + /* Skip the closing brace */ + NextToken (); + + /* Remember the symbol table and leave the struct level */ + FieldTab = GetFieldSymTab (); + LeaveStructLevel (); + + /* Return a fictitious symbol if errors occurred during parsing */ + if (PrevErrorCount != ErrorCount) { + Flags |= SC_FICTITIOUS; + } + + /* Make a real entry from the forward decl and return it */ + return AddStructSym (Name, SC_UNION | SC_DEF | Flags, UnionSize, FieldTab, DSFlags); +} + + + +static SymEntry* ParseStructSpec (const char* Name, unsigned* DSFlags) +/* Parse a struct specifier */ +{ + + unsigned StructSize; + int FlexibleMember; + unsigned BitOffs; /* Bit offset for bit-fields */ + int FieldWidth; /* Width in bits, -1 if not a bit-field */ + SymTable* FieldTab; + SymEntry* StructTagEntry; + SymEntry* Field; + unsigned Flags = 0; + unsigned PrevErrorCount = ErrorCount; + + + if (CurTok.Tok != TOK_LCURLY) { + /* Just a forward declaration */ + return ForwardESU (Name, SC_STRUCT, DSFlags); + } + + /* Add a forward declaration for the struct tag in the current lexical level */ + StructTagEntry = AddStructSym (Name, SC_STRUCT, 0, 0, DSFlags); + + StructTagEntry->V.S.ACount = 0; + + /* Skip the curly brace */ + NextToken (); + + /* Enter a new lexical level for the struct */ + EnterStructLevel (); + + /* Parse struct fields */ + FlexibleMember = 0; + StructSize = 0; + BitOffs = 0; + while (CurTok.Tok != TOK_RCURLY && CurTok.Tok != TOK_CEOF) { + + /* Get the type of the entry */ + DeclSpec Spec; + int NeedClean = 0; + + /* Check for extra semicolons */ + if (CurTok.Tok == TOK_SEMI) { + /* TODO: warn on this if we have a pedantic mode */ + NextToken (); + continue; + } + + /* Check for a _Static_assert */ + if (CurTok.Tok == TOK_STATIC_ASSERT) { + ParseStaticAssert (); + continue; + } + + InitDeclSpec (&Spec); + ParseTypeSpec (&Spec, TS_DEFAULT_TYPE_NONE); + + /* Check if this is only a type declaration */ + if (CurTok.Tok == TOK_SEMI && + !(IS_Get (&Standard) >= STD_CC65 && IsAnonStructClass (Spec.Type))) { + CheckEmptyDecl (&Spec); + NextToken (); + continue; + } + + /* If we haven't got a type specifier yet, something must be wrong */ + if ((Spec.Flags & DS_TYPE_MASK) == DS_NONE) { + /* Avoid extra errors if it was a failed type specifier */ + if ((Spec.Flags & DS_EXTRA_TYPE) == 0) { + Error ("Declaration specifier expected"); + } + NeedClean = -1; + goto EndOfDecl; + } + + /* Allow anonymous bit-fields */ + Spec.Flags |= DS_ALLOW_BITFIELD; + + /* Read fields with this type */ + while (1) { + + Declarator Decl; + + /* If we had a flexible array member before, no other fields can + ** follow. + */ + if (FlexibleMember) { + Error ("Flexible array member must be last field"); + FlexibleMember = 0; /* Avoid further errors */ + } + + /* Get type and name of the struct field */ + NeedClean = ParseDecl (&Spec, &Decl, DM_IDENT_OR_EMPTY); + + /* Bail out if there are errors */ + if (NeedClean <= 0) { + break; + } + + /* Check for a bit-field declaration */ + FieldWidth = ParseFieldWidth (&Decl); + + /* If this is not a bit field, or the bit field is too large for + ** the remainder of the allocated unit, or we have a bit field + ** with width zero, align the struct to the next member by adding + ** a member with an anonymous name. + */ + if (BitOffs > 0) { + if (FieldWidth <= 0 || + (BitOffs + FieldWidth) > CHAR_BITS * SizeOf (Decl.Type)) { + /* Add an anonymous bit-field that aligns to the next + ** byte. + */ + unsigned PaddingBits = PadWithBitField (StructSize, BitOffs); + + /* No bits left */ + StructSize += (BitOffs + PaddingBits) / CHAR_BITS; + BitOffs = 0; + } + } + + /* Check for fields without names */ + if (Decl.Ident[0] == '\0') { + if (FieldWidth < 0) { + /* In cc65 mode, we allow anonymous structs/unions within + ** a struct. + */ + AnonFieldName (Decl.Ident, GetBasicTypeName (Decl.Type), StructTagEntry->V.S.ACount); + + /* Ignore CVR qualifiers */ + if (IsQualConst (Decl.Type) || IsQualVolatile (Decl.Type) || IsQualRestrict (Decl.Type)) { + Warning ("Anonymous %s qualifiers are ignored", GetBasicTypeName (Decl.Type)); + Decl.Type[0].C &= ~T_QUAL_CVR; + } + } else if (FieldWidth > 0) { + /* A bit-field without a name will get an anonymous one */ + AnonName (Decl.Ident, "bit-field"); + } + } else { + /* Check if this field is a flexible array member, and + ** calculate the size of the field. + */ + if (IsTypeArray (Decl.Type) && GetElementCount (Decl.Type) == UNSPECIFIED) { + /* Array with unspecified size */ + if (StructSize == 0) { + Error ("Flexible array member cannot be first struct field"); + } + FlexibleMember = 1; + Flags |= SC_HAVEFAM; + + /* Assume zero for size calculations */ + SetElementCount (Decl.Type, FLEXIBLE); + } + + if (IsIncompleteType (Decl.Type)) { + Error ("Field '%s' has incomplete type '%s'", + Decl.Ident, + GetFullTypeName (Decl.Type)); + } + } + + /* Check for const types */ + if (IsQualConst (Decl.Type)) { + Flags |= SC_HAVECONST; + } + + /* Apart from the above, a bit field with width 0 is not processed + ** further. + */ + if (FieldWidth == 0) { + goto NextMember; + } + + /* Add a field entry to the table */ + if (FieldWidth > 0) { + /* Full bytes have already been added to the StructSize, + ** which is passed to the offset of AddBitField. BitOffs + ** is always within a char, which simplifies handling the + ** bit-field as a char type in expressions. + */ + CHECK (BitOffs < CHAR_BITS); + AddBitField (Decl.Ident, Decl.Type, StructSize, BitOffs, FieldWidth, + (Spec.Flags & DS_EXPLICIT_SIGNEDNESS) != 0); + BitOffs += FieldWidth; + CHECK (BitOffs <= CHAR_BITS * SizeOf (Decl.Type)); + /* Add any full bytes to the struct size */ + StructSize += BitOffs / CHAR_BITS; + BitOffs %= CHAR_BITS; + } else if (Decl.Ident[0] != '\0') { + /* Add the new field to the table */ + Field = AddLocalSym (Decl.Ident, Decl.Type, SC_STRUCTFIELD, StructSize); + + /* Check the new field for certain kinds of members */ + if (IsClassStruct (Decl.Type)) { + SymEntry* TagEntry = GetESUTagSym (Decl.Type); + + /* Alias the fields of the anonymous member on the current level */ + if (IsAnonName (Decl.Ident)) { + Field->V.A.ANumber = StructTagEntry->V.S.ACount++; + AliasAnonStructFields (&Decl, Field); + } + + /* Check if the field itself has a flexible array member */ + if (TagEntry && SymHasFlexibleArrayMember (TagEntry)) { + Field->Flags |= SC_HAVEFAM; + Flags |= SC_HAVEFAM; + Error ("Invalid use of struct with flexible array member"); + } + + /* Check if the field itself has a const member */ + if (TagEntry && SymHasConstMember (TagEntry)) { + Field->Flags |= SC_HAVECONST; + Flags |= SC_HAVECONST; + } + } + + if (!FlexibleMember) { + StructSize += SizeOf (Decl.Type); + } + } + +NextMember: + /* Check for end of declaration list */ + if (CurTok.Tok != TOK_COMMA) { + break; + } + Spec.Flags |= DS_NO_EMPTY_DECL; + NextToken (); + } + +EndOfDecl: + if (NeedClean > 0) { + /* Must be followed by a semicolon */ + if (ConsumeSemi ()) { + NeedClean = 0; + } else { + NeedClean = -1; + } + } + + /* Try some smart error recovery */ + if (NeedClean < 0) { + SmartErrorSkip (1); + } + } + + if (BitOffs > 0) { + /* If we have bits from bit-fields left, pad the struct to next byte */ + unsigned PaddingBits = PadWithBitField (StructSize, BitOffs); + + /* No bits left */ + StructSize += (BitOffs + PaddingBits) / CHAR_BITS; + } + + /* Skip the closing brace */ + NextToken (); + + /* Remember the symbol table and leave the struct level */ + FieldTab = GetFieldSymTab (); + LeaveStructLevel (); + + /* Return a fictitious symbol if errors occurred during parsing */ + if (PrevErrorCount != ErrorCount) { + Flags |= SC_FICTITIOUS; + } + + /* Make a real entry from the forward decl and return it */ + return AddStructSym (Name, SC_STRUCT | SC_DEF | Flags, StructSize, FieldTab, DSFlags); +} + + + +/*****************************************************************************/ +/* Declarator parser */ +/*****************************************************************************/ + + + static void InitDeclarator (Declarator* D) /* Initialize the Declarator struct for use */ { @@ -486,6 +1697,31 @@ static void FixQualifiers (Type* DataType) T[0].C |= CodeAddrSizeQualifier (); } + } else { + + /* If we have remaining qualifiers, flag them as invalid */ + Q = T[0].C; + + if (Q & T_QUAL_NEAR) { + Error ("Invalid '__near__' qualifier"); + Q &= ~T_QUAL_NEAR; + } + if (Q & T_QUAL_FAR) { + Error ("Invalid '__far__' qualifier"); + Q &= ~T_QUAL_FAR; + } + if (Q & T_QUAL_FASTCALL) { + Error ("Invalid '__fastcall__' qualifier"); + Q &= ~T_QUAL_FASTCALL; + } + if (Q & T_QUAL_CDECL) { + Error ("Invalid '__cdecl__' qualifier"); + Q &= ~T_QUAL_CDECL; + } + + /* Clear the invalid qualifiers */ + T[0].C &= Q; + } ++T; } @@ -493,27 +1729,73 @@ static void FixQualifiers (Type* DataType) -static void CheckArrayElementType (Type* DataType) -/* Check if data type consists of arrays of incomplete element types */ +static void FixFunctionReturnType (Type* T) +/* Check if the data type consists of any functions returning forbidden return +** types and remove qualifiers from the return types if they are not void. +*/ { - Type* T = DataType; + while (T->C != T_END) { + if (IsTypeFunc (T)) { + ++T; + /* Functions may not return functions or arrays */ + if (IsTypeFunc (T)) { + Error ("Functions are not allowed to return functions"); + } else if (IsTypeArray (T)) { + Error ("Functions are not allowed to return arrays"); + } + + /* The return type must not be qualified */ + if ((GetQualifier (T) & T_QUAL_CVR) != T_QUAL_NONE) { + /* We are stricter than the standard here */ + if (GetRawTypeRank (T) == T_RANK_VOID) { + /* A qualified void type is always an error */ + Error ("Function definition has qualified void return type"); + } else { + /* For others, qualifiers are ignored */ + Warning ("Type qualifiers ignored on function return type"); + T[0].C &= ~T_QUAL_CVR; + } + } + } else { + ++T; + } + } +} + + + +static void CheckArrayElementType (const Type* T) +/* Check recursively if type consists of arrays of forbidden element types */ +{ while (T->C != T_END) { if (IsTypeArray (T)) { + /* If the array is multi-dimensional, keep going until we get the + ** true element type. + */ ++T; - if (IsIncompleteESUType (T)) { - /* We cannot have an array of incomplete elements */ - Error ("Array of incomplete element type '%s'", GetFullTypeName (T)); - } else if (SizeOf (T) == 0) { - /* If the array is multi-dimensional, try to get the true - ** element type. - */ - if (IsTypeArray (T)) { - continue; + if (SizeOf (T) == 0) { + if (IsTypeArray (T) || IsIncompleteESUType (T)) { + /* We cannot have an array of incomplete elements */ + if (!IsTypeArray (T) || GetElementCount (T) == UNSPECIFIED) { + Error ("Array of incomplete element type '%s'", + GetFullTypeName (T)); + return; + } + } else if (!IsTypeVoid (T) || IS_Get (&Standard) != STD_CC65) { + /* We could support certain 0-size element types as an extension */ + Error ("Array of 0-size element type '%s'", + GetFullTypeName (T)); + return; } - /* We could support certain 0-size element types as an extension */ - if (!IsTypeVoid (T) || IS_Get (&Standard) != STD_CC65) { - Error ("Array of 0-size element type '%s'", GetFullTypeName (T)); + } else { + /* Elements cannot contain flexible array members themselves */ + if (IsClassStruct (T)) { + SymEntry* TagEntry = GetESUTagSym (T); + if (TagEntry && SymHasFlexibleArrayMember (TagEntry)) { + Error ("Invalid use of struct with flexible array member"); + return; + } } } } else { @@ -524,1040 +1806,15 @@ static void CheckArrayElementType (Type* DataType) -static SymEntry* ForwardESU (const char* Name, unsigned Flags, unsigned* DSFlags) -/* Handle an enum, struct or union forward declaration */ -{ - /* Try to find an enum/struct/union with the given name. If there is none, - ** insert a forward declaration into the current lexical level. - */ - SymEntry* TagEntry = FindTagSym (Name); - if (TagEntry == 0) { - if ((Flags & SC_ESUTYPEMASK) != SC_ENUM) { - TagEntry = AddStructSym (Name, Flags, 0, 0, DSFlags); - } else { - TagEntry = AddEnumSym (Name, Flags, 0, 0, DSFlags); - } - } else if ((TagEntry->Flags & SC_TYPEMASK) != (Flags & SC_ESUTYPEMASK)) { - /* Already defined, but not the same type class */ - Error ("Symbol '%s' is already different kind", Name); - } - return TagEntry; -} - - - -static const Type* GetEnumeratorType (long Min, unsigned long Max, int Signed) -/* GitHub #1093 - We use unsigned types to save spaces whenever possible. -** If both the signed and unsigned integer types of the same minimum size -** capable of representing all values of the enum, we prefer the unsigned -** one. -** Return 0 if impossible to represent Min and Max as the same integer type. -*/ -{ - const Type* Underlying = type_int; /* default type */ - - /* Change the underlying type if necessary */ - if (Min < 0 || Signed) { - /* We can't use unsigned types if there are any negative values */ - if (Max > (unsigned long)INT32_MAX) { - /* No way to represent both Min and Max as the same integer type */ - Underlying = 0; - } else if (Min < INT16_MIN || Max > (unsigned long)INT16_MAX) { - Underlying = type_long; - } else if (Min < INT8_MIN || Max > (unsigned long)INT8_MAX) { - Underlying = type_int; - } else { - Underlying = type_schar; - } - } else { - if (Max > UINT16_MAX) { - Underlying = type_ulong; - } else if (Max > UINT8_MAX) { - Underlying = type_uint; - } else { - Underlying = type_uchar; - } - } - - return Underlying; -} - - - -static SymEntry* ParseEnumSpec (const char* Name, unsigned* DSFlags) -/* Process an enum specifier */ -{ - SymTable* FieldTab; - long EnumVal; - int IsSigned; - int IsIncremented; - ident Ident; - long MinConstant = 0; - unsigned long MaxConstant = 0; - const Type* NewType = 0; /* new member type */ - const Type* MemberType = type_int; /* default member type */ - unsigned Flags = 0; - unsigned PrevErrorCount = ErrorCount; - - - if (CurTok.Tok != TOK_LCURLY) { - /* Just a forward definition */ - return ForwardESU (Name, SC_ENUM, DSFlags); - } - - /* Add a forward declaration for the enum tag in the current lexical level */ - AddEnumSym (Name, 0, 0, 0, DSFlags); - - /* Skip the opening curly brace */ - NextToken (); - - /* Read the enum tags */ - EnumVal = -1L; - while (CurTok.Tok != TOK_RCURLY) { - - /* We expect an identifier */ - if (CurTok.Tok != TOK_IDENT) { - Error ("Identifier expected for enumerator declarator"); - /* Avoid excessive errors */ - NextToken (); - continue; - } - - /* Remember the identifier and skip it */ - strcpy (Ident, CurTok.Ident); - NextToken (); - - /* Check for an assigned value */ - if (CurTok.Tok == TOK_ASSIGN) { - - NextToken (); - ExprDesc Expr = NoCodeConstAbsIntExpr (hie1); - EnumVal = Expr.IVal; - MemberType = Expr.Type; - IsSigned = IsSignSigned (MemberType); - IsIncremented = 0; - - } else { - - /* Defaulted with the same signedness as the previous member's */ - IsSigned = IsSignSigned (MemberType) && - (unsigned long)EnumVal != GetIntegerTypeMax (MemberType); - - /* Enumerate by adding one to the previous value */ - EnumVal = (long)(((unsigned long)EnumVal + 1UL) & 0xFFFFFFFFUL); - - if (GetUnqualRawTypeCode (MemberType) == T_ULONG && EnumVal == 0) { - /* Error since the new value cannot be represented in the - ** largest unsigned integer type supported by cc65 for enum. - */ - Error ("Enumerator '%s' overflows the range of '%s'", - Ident, - GetBasicTypeName (type_ulong)); - } - - IsIncremented = 1; - } - - /* Track down the min/max values and evaluate the type of EnumVal - ** using GetEnumeratorType in a tricky way. - */ - if (!IsSigned || EnumVal >= 0) { - if ((unsigned long)EnumVal > MaxConstant) { - MaxConstant = (unsigned long)EnumVal; - } - NewType = GetEnumeratorType (0, EnumVal, IsSigned); - } else { - if (EnumVal < MinConstant) { - MinConstant = EnumVal; - } - NewType = GetEnumeratorType (EnumVal, 0, 1); - } - - /* GetEnumeratorType above should never fail, but just in case */ - if (NewType == 0) { - Internal ("Unexpected failure with GetEnumeratorType: %lx", EnumVal); - NewType = type_ulong; - } else if (SizeOf (NewType) < SizeOf (type_int)) { - /* Integer constants are not shorter than int */ - NewType = type_int; - } - - /* Warn if the incremented value exceeds the range of the previous - ** type. - */ - if (PrevErrorCount == ErrorCount && - IsIncremented && - (!IsSigned || EnumVal >= 0) && - NewType->C != GetUnqualRawTypeCode (MemberType)) { - /* The possible overflow here can only be when EnumVal > 0 */ - Warning ("Enumerator '%s' (value = %lu) implies type '%s'", - Ident, - (unsigned long)EnumVal, - GetBasicTypeName (NewType)); - } - - /* Warn if the value exceeds range of 'int' in standard mode */ - if (IS_Get (&Standard) != STD_CC65 && NewType->C != T_INT) { - if (!IsSigned || EnumVal >= 0) { - Warning ("ISO C restricts enumerator values to range of 'int'\n" - "\tEnumerator '%s' (value = %lu) is too large", - Ident, - (unsigned long)EnumVal); - } else { - Warning ("ISO C restricts enumerator values to range of 'int'\n" - "\tEnumerator '%s' (value = %ld) is too small", - Ident, - EnumVal); - } - } - - /* Add an entry of the enumerator to the symbol table */ - AddConstSym (Ident, NewType, SC_ENUMERATOR | SC_CONST, EnumVal); - - /* Use this type for following members */ - MemberType = NewType; - - /* Check for end of definition */ - if (CurTok.Tok != TOK_COMMA) { - break; - } - NextToken (); - } - ConsumeRCurly (); - - /* Check if there have been any members. Error if none */ - if (NewType == 0) { - Error ("Empty enum is invalid"); - } - - /* This evaluates the underlying type of the whole enum */ - MemberType = GetEnumeratorType (MinConstant, MaxConstant, 0); - if (MemberType == 0) { - /* It is very likely that the program is wrong */ - Error ("Enumeration values cannot be represented all as 'long'\n" - "\tMin enumerator value = %ld, Max enumerator value = %lu", - MinConstant, MaxConstant); - - /* Avoid more errors */ - MemberType = type_long; - } - - FieldTab = GetSymTab (); - - /* Return a fictitious symbol if errors occurred during parsing */ - if (PrevErrorCount != ErrorCount) { - Flags |= SC_FICTITIOUS; - } - - return AddEnumSym (Name, Flags, MemberType, FieldTab, DSFlags); -} - - - -static int ParseFieldWidth (Declarator* D) -/* Parse an optional field width. Returns -1 if no field width is specified, -** otherwise the width of the field. -*/ -{ - if (CurTok.Tok != TOK_COLON) { - /* No bit-field declaration */ - return -1; - } - - if (!IsClassInt (D->Type)) { - /* Only integer types may be used for bit-fields */ - Error ("Bit-field has invalid type '%s', must be integral", - GetBasicTypeName (D->Type)); - - /* Avoid a diagnostic storm by giving the bit-field the widest valid - ** signed type, and continuing to parse. - */ - D->Type[0].C = T_INT; - } - - /* We currently support integral types up to long */ - if (SizeOf (D->Type) > SizeOf (type_ulong)) { - /* Only long-sized or smaller types may be used for bit-fields, for now */ - Error ("cc65 currently supports only long-sized and smaller bit-field types"); - - /* Avoid a diagnostic storm */ - D->Type[0].C = T_INT; - } - - /* Read the width */ - NextToken (); - ExprDesc Expr = NoCodeConstAbsIntExpr (hie1); - - if (Expr.IVal < 0) { - Error ("Negative width in bit-field"); - return -1; - } - if (Expr.IVal > (long)(SizeOf (D->Type) * CHAR_BITS)) { - Error ("Width of bit-field exceeds its type"); - return -1; - } - if (Expr.IVal == 0 && D->Ident[0] != '\0') { - Error ("Zero width for named bit-field"); - return -1; - } - - /* Return the field width */ - return (int) Expr.IVal; -} - - - -static unsigned PadWithBitField (unsigned StructSize, unsigned BitOffs) -/* Pad the current struct with an anonymous bit-field aligned to the next byte. -** Return how many bits are used to pad. -*/ -{ - /* MSVC complains about unary negation of unsigned, - ** so it has been rewritten as subtraction. - */ - unsigned PaddingBits = (0 - BitOffs) % CHAR_BITS; - - /* We need an anonymous name */ - ident Ident; - AnonName (Ident, "bit-field"); - - /* Add an anonymous bit-field that aligns to the next - ** byte. - */ - AddBitField (Ident, type_uchar, StructSize, BitOffs, PaddingBits, - /*SignednessSpecified=*/1); - - return PaddingBits; -} - - - -static unsigned AliasAnonStructFields (const Declarator* D, SymEntry* Anon) -/* Create alias fields from an anon union/struct in the current lexical level. -** The function returns the count of created aliases. -*/ -{ - unsigned Count = 0; - SymEntry* Field; - SymEntry* Alias; - - /* Get the symbol table containing the fields. If it is empty, there has - ** been an error before, so bail out. - */ - SymTable* Tab = GetESUTagSym (D->Type)->V.S.SymTab; - if (Tab == 0) { - /* Incomplete definition - has been flagged before */ - return 0; - } - - /* Get a pointer to the list of symbols. Then walk the list adding copies - ** of the embedded struct to the current level. - */ - Field = Tab->SymHead; - while (Field) { - - /* Enter an alias of this symbol */ - if (!IsAnonName (Field->Name)) { - Alias = AddLocalSym (Field->Name, Field->Type, SC_STRUCTFIELD|SC_ALIAS, 0); - Alias->V.A.Field = Field; - Alias->V.A.Offs = Anon->V.Offs + Field->V.Offs; - ++Count; - } - - /* Currently, there can not be any attributes, but if there will be - ** some in the future, we want to know this. - */ - CHECK (Field->Attr == 0); - - /* Next entry */ - Field = Field->NextSym; - } - - /* Return the count of created aliases */ - return Count; -} - - - -static SymEntry* ParseUnionSpec (const char* Name, unsigned* DSFlags) -/* Parse a union specifier */ -{ - - unsigned UnionSize; - unsigned FieldSize; - int FieldWidth; /* Width in bits, -1 if not a bit-field */ - SymTable* FieldTab; - SymEntry* UnionTagEntry; - SymEntry* Field; - unsigned Flags = 0; - unsigned PrevErrorCount = ErrorCount; - - - if (CurTok.Tok != TOK_LCURLY) { - /* Just a forward declaration */ - return ForwardESU (Name, SC_UNION, DSFlags); - } - - /* Add a forward declaration for the union tag in the current lexical level */ - UnionTagEntry = AddStructSym (Name, SC_UNION, 0, 0, DSFlags); - - UnionTagEntry->V.S.ACount = 0; - - /* Skip the curly brace */ - NextToken (); - - /* Enter a new lexical level for the struct */ - EnterStructLevel (); - - /* Parse union fields */ - UnionSize = 0; - while (CurTok.Tok != TOK_RCURLY) { - - /* Get the type of the entry */ - DeclSpec Spec; - int SignednessSpecified = 0; - - /* Check for a _Static_assert */ - if (CurTok.Tok == TOK_STATIC_ASSERT) { - ParseStaticAssert (); - continue; - } - - InitDeclSpec (&Spec); - ParseTypeSpec (&Spec, TS_DEFAULT_TYPE_NONE, &SignednessSpecified); - - /* Read fields with this type */ - while (1) { - - Declarator Decl; - - /* Get type and name of the struct field */ - ParseDecl (&Spec, &Decl, DM_ACCEPT_IDENT); - - /* Check for a bit-field declaration */ - FieldWidth = ParseFieldWidth (&Decl); - - /* Ignore zero sized bit fields in a union */ - if (FieldWidth == 0) { - goto NextMember; - } - - /* Check for fields without a name */ - if (Decl.Ident[0] == '\0') { - /* In cc65 mode, we allow anonymous structs/unions within - ** a union. - */ - if (IS_Get (&Standard) >= STD_CC65 && IsClassStruct (Decl.Type)) { - /* This is an anonymous struct or union. Copy the fields - ** into the current level. - */ - AnonFieldName (Decl.Ident, "field", UnionTagEntry->V.S.ACount); - } else { - /* A non bit-field without a name is legal but useless */ - Warning ("Declaration does not declare anything"); - } - } - - /* Check for incomplete types including 'void' */ - if (IsIncompleteType (Decl.Type)) { - Error ("Field '%s' has incomplete type '%s'", - Decl.Ident, - GetFullTypeName (Decl.Type)); - } - - /* Handle sizes */ - FieldSize = SizeOf (Decl.Type); - if (FieldSize > UnionSize) { - UnionSize = FieldSize; - } - - /* Add a field entry to the table. */ - if (FieldWidth > 0) { - /* For a union, allocate space for the type specified by the - ** bit-field. - */ - AddBitField (Decl.Ident, Decl.Type, 0, 0, FieldWidth, - SignednessSpecified); - } else if (Decl.Ident[0] != '\0') { - Field = AddLocalSym (Decl.Ident, Decl.Type, SC_STRUCTFIELD, 0); - if (IsAnonName (Decl.Ident)) { - Field->V.A.ANumber = UnionTagEntry->V.S.ACount++; - AliasAnonStructFields (&Decl, Field); - } - - /* Check if the field itself has a flexible array member */ - if (IsClassStruct (Decl.Type)) { - SymEntry* TagEntry = GetESUTagSym (Decl.Type); - if (TagEntry && SymHasFlexibleArrayMember (TagEntry)) { - Field->Flags |= SC_HAVEFAM; - Flags |= SC_HAVEFAM; - } - } - } - -NextMember: if (CurTok.Tok != TOK_COMMA) { - break; - } - NextToken (); - } - ConsumeSemi (); - } - - /* Skip the closing brace */ - NextToken (); - - /* Remember the symbol table and leave the struct level */ - FieldTab = GetFieldSymTab (); - LeaveStructLevel (); - - /* Return a fictitious symbol if errors occurred during parsing */ - if (PrevErrorCount != ErrorCount) { - Flags |= SC_FICTITIOUS; - } - - /* Empty union is not supported now */ - if (UnionSize == 0) { - Error ("Empty union type '%s' is not supported", Name); - } - - /* Make a real entry from the forward decl and return it */ - return AddStructSym (Name, SC_UNION | SC_DEF | Flags, UnionSize, FieldTab, DSFlags); -} - - - -static SymEntry* ParseStructSpec (const char* Name, unsigned* DSFlags) -/* Parse a struct specifier */ -{ - - unsigned StructSize; - int FlexibleMember; - unsigned BitOffs; /* Bit offset for bit-fields */ - int FieldWidth; /* Width in bits, -1 if not a bit-field */ - SymTable* FieldTab; - SymEntry* StructTagEntry; - SymEntry* Field; - unsigned Flags = 0; - unsigned PrevErrorCount = ErrorCount; - - - if (CurTok.Tok != TOK_LCURLY) { - /* Just a forward declaration */ - return ForwardESU (Name, SC_STRUCT, DSFlags); - } - - /* Add a forward declaration for the struct tag in the current lexical level */ - StructTagEntry = AddStructSym (Name, SC_STRUCT, 0, 0, DSFlags); - - StructTagEntry->V.S.ACount = 0; - - /* Skip the curly brace */ - NextToken (); - - /* Enter a new lexical level for the struct */ - EnterStructLevel (); - - /* Parse struct fields */ - FlexibleMember = 0; - StructSize = 0; - BitOffs = 0; - while (CurTok.Tok != TOK_RCURLY) { - - /* Get the type of the entry */ - DeclSpec Spec; - int SignednessSpecified = 0; - - /* Check for a _Static_assert */ - if (CurTok.Tok == TOK_STATIC_ASSERT) { - ParseStaticAssert (); - continue; - } - - InitDeclSpec (&Spec); - ParseTypeSpec (&Spec, TS_DEFAULT_TYPE_NONE, &SignednessSpecified); - - /* Read fields with this type */ - while (1) { - - Declarator Decl; - - /* If we had a flexible array member before, no other fields can - ** follow. - */ - if (FlexibleMember) { - Error ("Flexible array member must be last field"); - FlexibleMember = 0; /* Avoid further errors */ - } - - /* Get type and name of the struct field */ - ParseDecl (&Spec, &Decl, DM_ACCEPT_IDENT); - - /* Check for a bit-field declaration */ - FieldWidth = ParseFieldWidth (&Decl); - - /* If this is not a bit field, or the bit field is too large for - ** the remainder of the allocated unit, or we have a bit field - ** with width zero, align the struct to the next member by adding - ** a member with an anonymous name. - */ - if (BitOffs > 0) { - if (FieldWidth <= 0 || - (BitOffs + FieldWidth) > CHAR_BITS * SizeOf (Decl.Type)) { - /* Add an anonymous bit-field that aligns to the next - ** byte. - */ - unsigned PaddingBits = PadWithBitField (StructSize, BitOffs); - - /* No bits left */ - StructSize += (BitOffs + PaddingBits) / CHAR_BITS; - BitOffs = 0; - } - } - - /* Apart from the above, a bit field with width 0 is not processed - ** further. - */ - if (FieldWidth == 0) { - goto NextMember; - } - - /* Check if this field is a flexible array member, and - ** calculate the size of the field. - */ - if (IsTypeArray (Decl.Type) && GetElementCount (Decl.Type) == UNSPECIFIED) { - /* Array with unspecified size */ - if (StructSize == 0) { - Error ("Flexible array member cannot be first struct field"); - } - FlexibleMember = 1; - Flags |= SC_HAVEFAM; - - /* Assume zero for size calculations */ - SetElementCount (Decl.Type, FLEXIBLE); - } - - /* Check for fields without names */ - if (Decl.Ident[0] == '\0') { - if (FieldWidth < 0) { - /* In cc65 mode, we allow anonymous structs/unions within - ** a struct. - */ - if (IS_Get (&Standard) >= STD_CC65 && IsClassStruct (Decl.Type)) { - - /* This is an anonymous struct or union. Copy the - ** fields into the current level. - */ - AnonFieldName (Decl.Ident, "field", StructTagEntry->V.S.ACount); - } else { - /* A non bit-field without a name is legal but useless */ - Warning ("Declaration does not declare anything"); - } - } else { - /* A bit-field without a name will get an anonymous one */ - AnonName (Decl.Ident, "bit-field"); - } - } - - /* Check for incomplete types including 'void' */ - if (IsIncompleteType (Decl.Type)) { - Error ("Field '%s' has incomplete type '%s'", - Decl.Ident, - GetFullTypeName (Decl.Type)); - } - - /* Add a field entry to the table */ - if (FieldWidth > 0) { - /* Full bytes have already been added to the StructSize, - ** which is passed to the offset of AddBitField. BitOffs - ** is always within a char, which simplifies handling the - ** bit-field as a char type in expressions. - */ - CHECK (BitOffs < CHAR_BITS); - AddBitField (Decl.Ident, Decl.Type, StructSize, BitOffs, - FieldWidth, SignednessSpecified); - BitOffs += FieldWidth; - CHECK (BitOffs <= CHAR_BITS * SizeOf (Decl.Type)); - /* Add any full bytes to the struct size. */ - StructSize += BitOffs / CHAR_BITS; - BitOffs %= CHAR_BITS; - } else if (Decl.Ident[0] != '\0') { - Field = AddLocalSym (Decl.Ident, Decl.Type, SC_STRUCTFIELD, StructSize); - if (IsAnonName (Decl.Ident)) { - Field->V.A.ANumber = StructTagEntry->V.S.ACount++; - AliasAnonStructFields (&Decl, Field); - } - - /* Check if the field itself has a flexible array member */ - if (IsClassStruct (Decl.Type)) { - SymEntry* TagEntry = GetESUTagSym (Decl.Type); - if (TagEntry && SymHasFlexibleArrayMember (TagEntry)) { - Field->Flags |= SC_HAVEFAM; - Flags |= SC_HAVEFAM; - } - } - - if (!FlexibleMember) { - StructSize += SizeOf (Decl.Type); - } - } - -NextMember: if (CurTok.Tok != TOK_COMMA) { - break; - } - NextToken (); - } - ConsumeSemi (); - } - - if (BitOffs > 0) { - /* If we have bits from bit-fields left, pad the struct to next byte */ - unsigned PaddingBits = PadWithBitField (StructSize, BitOffs); - - /* No bits left */ - StructSize += (BitOffs + PaddingBits) / CHAR_BITS; - } - - /* Skip the closing brace */ - NextToken (); - - /* Remember the symbol table and leave the struct level */ - FieldTab = GetFieldSymTab (); - LeaveStructLevel (); - - /* Return a fictitious symbol if errors occurred during parsing */ - if (PrevErrorCount != ErrorCount) { - Flags |= SC_FICTITIOUS; - } - - /* Empty struct is not supported now */ - if (StructSize == 0) { - Error ("Empty struct type '%s' is not supported", Name); - } - - /* Make a real entry from the forward decl and return it */ - return AddStructSym (Name, SC_STRUCT | SC_DEF | Flags, StructSize, FieldTab, DSFlags); -} - - - -static void ParseTypeSpec (DeclSpec* D, typespec_t TSFlags, int* SignednessSpecified) -/* Parse a type specifier. Store whether one of "signed" or "unsigned" was -** specified, so bit-fields of unspecified signedness can be treated as -** unsigned; without special handling, it would be treated as signed. -*/ -{ - ident Ident; - SymEntry* TagEntry; - TypeCode Qualifiers = T_QUAL_NONE; - - if (SignednessSpecified != NULL) { - *SignednessSpecified = 0; - } - - /* Assume we have an explicit type */ - D->Flags &= ~DS_DEF_TYPE; - - /* Read storage specifiers and/or type qualifiers if we have any */ - OptionalSpecifiers (D, &Qualifiers, TSFlags); - - /* Look at the data type */ - switch (CurTok.Tok) { - - case TOK_VOID: - NextToken (); - D->Type[0].C = T_VOID; - D->Type[0].A.U = 0; - D->Type[1].C = T_END; - break; - - case TOK_CHAR: - NextToken (); - D->Type[0].C = T_CHAR; - D->Type[1].C = T_END; - break; - - case TOK_LONG: - NextToken (); - if (CurTok.Tok == TOK_UNSIGNED) { - if (SignednessSpecified != NULL) { - *SignednessSpecified = 1; - } - NextToken (); - OptionalInt (); - D->Type[0].C = T_ULONG; - D->Type[1].C = T_END; - } else { - OptionalSigned (SignednessSpecified); - OptionalInt (); - D->Type[0].C = T_LONG; - D->Type[1].C = T_END; - } - break; - - case TOK_SHORT: - NextToken (); - if (CurTok.Tok == TOK_UNSIGNED) { - if (SignednessSpecified != NULL) { - *SignednessSpecified = 1; - } - NextToken (); - OptionalInt (); - D->Type[0].C = T_USHORT; - D->Type[1].C = T_END; - } else { - OptionalSigned (SignednessSpecified); - OptionalInt (); - D->Type[0].C = T_SHORT; - D->Type[1].C = T_END; - } - break; - - case TOK_INT: - NextToken (); - D->Type[0].C = T_INT; - D->Type[1].C = T_END; - break; - - case TOK_SIGNED: - if (SignednessSpecified != NULL) { - *SignednessSpecified = 1; - } - NextToken (); - switch (CurTok.Tok) { - - case TOK_CHAR: - NextToken (); - D->Type[0].C = T_SCHAR; - D->Type[1].C = T_END; - break; - - case TOK_SHORT: - NextToken (); - OptionalInt (); - D->Type[0].C = T_SHORT; - D->Type[1].C = T_END; - break; - - case TOK_LONG: - NextToken (); - OptionalInt (); - D->Type[0].C = T_LONG; - D->Type[1].C = T_END; - break; - - case TOK_INT: - NextToken (); - /* FALL THROUGH */ - - default: - D->Type[0].C = T_INT; - D->Type[1].C = T_END; - break; - } - break; - - case TOK_UNSIGNED: - if (SignednessSpecified != NULL) { - *SignednessSpecified = 1; - } - NextToken (); - switch (CurTok.Tok) { - - case TOK_CHAR: - NextToken (); - D->Type[0].C = T_UCHAR; - D->Type[1].C = T_END; - break; - - case TOK_SHORT: - NextToken (); - OptionalInt (); - D->Type[0].C = T_USHORT; - D->Type[1].C = T_END; - break; - - case TOK_LONG: - NextToken (); - OptionalInt (); - D->Type[0].C = T_ULONG; - D->Type[1].C = T_END; - break; - - case TOK_INT: - NextToken (); - /* FALL THROUGH */ - - default: - D->Type[0].C = T_UINT; - D->Type[1].C = T_END; - break; - } - break; - - case TOK_FLOAT: - NextToken (); - D->Type[0].C = T_FLOAT; - D->Type[1].C = T_END; - break; - - case TOK_DOUBLE: - NextToken (); - D->Type[0].C = T_DOUBLE; - D->Type[1].C = T_END; - break; - - case TOK_UNION: - NextToken (); - /* */ - if (CurTok.Tok == TOK_IDENT) { - strcpy (Ident, CurTok.Ident); - NextToken (); - } else { - AnonName (Ident, "union"); - } - /* Remember we have an extra type decl */ - D->Flags |= DS_EXTRA_TYPE; - /* Declare the union in the current scope */ - TagEntry = ParseUnionSpec (Ident, &D->Flags); - /* Encode the union entry into the type */ - D->Type[0].C = T_UNION; - SetESUTagSym (D->Type, TagEntry); - D->Type[1].C = T_END; - break; - - case TOK_STRUCT: - NextToken (); - /* */ - if (CurTok.Tok == TOK_IDENT) { - strcpy (Ident, CurTok.Ident); - NextToken (); - } else { - AnonName (Ident, "struct"); - } - /* Remember we have an extra type decl */ - D->Flags |= DS_EXTRA_TYPE; - /* Declare the struct in the current scope */ - TagEntry = ParseStructSpec (Ident, &D->Flags); - /* Encode the struct entry into the type */ - D->Type[0].C = T_STRUCT; - SetESUTagSym (D->Type, TagEntry); - D->Type[1].C = T_END; - break; - - case TOK_ENUM: - NextToken (); - /* Named enum */ - if (CurTok.Tok == TOK_IDENT) { - strcpy (Ident, CurTok.Ident); - NextToken (); - } else { - if (CurTok.Tok != TOK_LCURLY) { - Error ("Identifier expected"); - } - AnonName (Ident, "enum"); - } - /* Remember we have an extra type decl */ - D->Flags |= DS_EXTRA_TYPE; - /* Parse the enum decl */ - TagEntry = ParseEnumSpec (Ident, &D->Flags); - /* Encode the enum entry into the type */ - D->Type[0].C |= T_ENUM; - SetESUTagSym (D->Type, TagEntry); - D->Type[1].C = T_END; - /* The signedness of enums is determined by the type, so say this is specified to avoid - ** the int -> unsigned int handling for plain int bit-fields in AddBitField. - */ - if (SignednessSpecified) { - *SignednessSpecified = 1; - } - break; - - case TOK_IDENT: - /* This could be a label */ - if (NextTok.Tok != TOK_COLON || GetLexicalLevel () == LEX_LEVEL_STRUCT) { - TagEntry = FindSym (CurTok.Ident); - if (TagEntry && SymIsTypeDef (TagEntry)) { - /* It's a typedef */ - NextToken (); - TypeCopy (D->Type, TagEntry->Type); - /* If it's a typedef, we should actually use whether the signedness was - ** specified on the typedef, but that information has been lost. Treat the - ** signedness as being specified to work around the ICE in #1267. - ** Unforunately, this will cause plain int bit-fields defined via typedefs - ** to be treated as signed rather than unsigned. - */ - if (SignednessSpecified) { - *SignednessSpecified = 1; - } - break; - } - } else { - /* This is a label. Use the default type flag to end the loop - ** in DeclareLocals. The type code used here doesn't matter as - ** long as it has no qualifiers. - */ - D->Flags |= DS_DEF_TYPE; - D->Type[0].C = T_INT; - D->Type[1].C = T_END; - break; - } - /* FALL THROUGH */ - - default: - if ((TSFlags & TS_MASK_DEFAULT_TYPE) != TS_DEFAULT_TYPE_INT) { - Error ("Type expected"); - D->Type[0].C = T_INT; - D->Type[1].C = T_END; - } else { - D->Flags |= DS_DEF_TYPE; - D->Type[0].C = T_INT; - D->Type[1].C = T_END; - } - break; - } - - /* There may also be specifiers/qualifiers *after* the initial type */ - OptionalSpecifiers (D, &Qualifiers, TSFlags); - D->Type[0].C |= Qualifiers; -} - - - -static const Type* ParamTypeCvt (Type* T) -/* If T is an array or a function, convert it to a pointer else do nothing. -** Return the resulting type. -*/ -{ - Type* Tmp = 0; - - if (IsTypeArray (T)) { - Tmp = ArrayToPtr (T); - } else if (IsTypeFunc (T)) { - Tmp = NewPointerTo (T); - } - - if (Tmp != 0) { - /* Do several fixes on qualifiers */ - FixQualifiers (Tmp); - - /* Replace the type */ - TypeCopy (T, Tmp); - TypeFree (Tmp); - } - - return T; -} - - - static void ParseOldStyleParamList (FuncDesc* F) /* Parse an old-style (K&R) parameter list */ { - /* Some fix point tokens that are used for error recovery */ - static const token_t TokenList[] = { TOK_COMMA, TOK_RPAREN, TOK_SEMI }; + if (CurTok.Tok == TOK_RPAREN) { + return; + } /* Parse params */ - while (CurTok.Tok != TOK_RPAREN) { + while (1) { /* List of identifiers expected */ if (CurTok.Tok == TOK_IDENT) { @@ -1573,29 +1830,38 @@ static void ParseOldStyleParamList (FuncDesc* F) } else { /* Not a parameter name */ - Error ("Identifier expected"); + Error ("Identifier expected for parameter name"); /* Try some smart error recovery */ - SkipTokens (TokenList, sizeof(TokenList) / sizeof(TokenList[0])); + if (SmartErrorSkip (0) < 0) { + break; + } } /* Check for more parameters */ - if (CurTok.Tok == TOK_COMMA) { - NextToken (); - } else { + if (CurTok.Tok != TOK_COMMA) { break; } + NextToken (); + + } +} + + + +static void ParseOldStyleParamDeclList (FuncDesc* F attribute ((unused))) +/* Parse an old-style (K&R) function declarator declaration list */ +{ + if (CurTok.Tok == TOK_SEMI) { + /* No parameter declaration list */ + return; } - /* Skip right paren. We must explicitly check for one here, since some of - ** the breaks above bail out without checking. - */ - ConsumeRParen (); - /* An optional list of type specifications follows */ - while (CurTok.Tok != TOK_LCURLY) { + while (CurTok.Tok != TOK_LCURLY && CurTok.Tok != TOK_CEOF) { DeclSpec Spec; + int NeedClean; /* Read the declaration specifier */ ParseDeclSpec (&Spec, TS_DEFAULT_TYPE_NONE, SC_AUTO); @@ -1603,36 +1869,51 @@ static void ParseOldStyleParamList (FuncDesc* F) /* We accept only auto and register as storage class specifiers, but ** we ignore all this, since we use auto anyway. */ - if ((Spec.StorageClass & SC_AUTO) == 0 && - (Spec.StorageClass & SC_REGISTER) == 0) { + if ((Spec.StorageClass & SC_STORAGEMASK) != SC_AUTO && + (Spec.StorageClass & SC_STORAGEMASK) != SC_REGISTER) { Error ("Illegal storage class"); } + /* If we haven't got a type specifier yet, something must be wrong */ + if ((Spec.Flags & DS_TYPE_MASK) == DS_NONE) { + /* Avoid extra errors if it was a failed type specifier */ + if ((Spec.Flags & DS_EXTRA_TYPE) == 0) { + Error ("Declaration specifier expected"); + } + NeedClean = -1; + goto EndOfDecl; + } + /* Parse a comma separated variable list */ while (1) { - Declarator Decl; + Declarator Decl; /* Read the parameter */ - ParseDecl (&Spec, &Decl, DM_NEED_IDENT); + NeedClean = ParseDecl (&Spec, &Decl, DM_IDENT_OR_EMPTY); + + /* Bail out if there are errors */ + if (NeedClean <= 0) { + break; + } /* Warn about new local type declaration */ - if ((Spec.Flags & DS_NEW_TYPE_DECL) != 0) { + if ((Spec.Flags & DS_NEW_TYPE_DECL) != 0 && !IsAnonESUType (Spec.Type)) { Warning ("'%s' will be invisible out of this function", GetFullTypeName (Spec.Type)); } if (Decl.Ident[0] != '\0') { - /* We have a name given. Search for the symbol */ SymEntry* Param = FindLocalSym (Decl.Ident); + if (Param) { /* Check if we already changed the type for this - ** parameter + ** parameter. */ if (Param->Flags & SC_DEFTYPE) { /* Found it, change the default type to the one given */ - SymChangeType (Param, ParamTypeCvt (Decl.Type)); + SymChangeType (Param, PtrConversion (Decl.Type)); /* Reset the "default type" flag */ Param->Flags &= ~SC_DEFTYPE; } else { @@ -1640,20 +1921,40 @@ static void ParseOldStyleParamList (FuncDesc* F) Error ("Redefinition for parameter '%s'", Param->Name); } } else { - Error ("Unknown identifier: '%s'", Decl.Ident); + Error ("Unknown parameter '%s'", Decl.Ident); + } + + /* Initialization is not allowed */ + if (CurTok.Tok == TOK_ASSIGN) { + Error ("Parameter '%s' cannot be initialized", Decl.Ident); + + /* Try some smart error recovery */ + SmartErrorSkip (0); } } - if (CurTok.Tok == TOK_COMMA) { - NextToken (); - } else { + /* Check for more declarators */ + if (CurTok.Tok != TOK_COMMA) { break; } + NextToken (); } - /* Variable list must be semicolon terminated */ - ConsumeSemi (); +EndOfDecl: + if (NeedClean > 0) { + /* Must be followed by a semicolon */ + if (ConsumeSemi ()) { + NeedClean = 0; + } else { + NeedClean = -1; + } + } + + /* Try some smart error recovery */ + if (NeedClean < 0) { + SmartErrorSkip (1); + } } } @@ -1662,12 +1963,17 @@ static void ParseOldStyleParamList (FuncDesc* F) static void ParseAnsiParamList (FuncDesc* F) /* Parse a new-style (ANSI) parameter list */ { - /* Parse params */ - while (CurTok.Tok != TOK_RPAREN) { + if (CurTok.Tok == TOK_RPAREN) { + return; + } - DeclSpec Spec; - Declarator Decl; - SymEntry* Param; + /* Parse params */ + while (1) { + + DeclSpec Spec; + Declarator Decl; + SymEntry* Param; + unsigned PrevErrorCount = ErrorCount; /* Allow an ellipsis as last parameter */ if (CurTok.Tok == TOK_ELLIPSIS) { @@ -1680,17 +1986,22 @@ static void ParseAnsiParamList (FuncDesc* F) ParseDeclSpec (&Spec, TS_DEFAULT_TYPE_NONE, SC_AUTO); /* We accept only auto and register as storage class specifiers */ - if ((Spec.StorageClass & SC_AUTO) == SC_AUTO) { - Spec.StorageClass = SC_AUTO | SC_PARAM | SC_DEF; - } else if ((Spec.StorageClass & SC_REGISTER) == SC_REGISTER) { - Spec.StorageClass = SC_REGISTER | SC_STATIC | SC_PARAM | SC_DEF; + if ((Spec.StorageClass & SC_STORAGEMASK) == SC_REGISTER) { + Spec.StorageClass = SC_REGISTER | SC_PARAM | SC_DEF; } else { - Error ("Illegal storage class"); + if ((Spec.StorageClass & SC_STORAGEMASK) != SC_AUTO) { + Error ("Illegal storage class"); + } Spec.StorageClass = SC_AUTO | SC_PARAM | SC_DEF; } + /* Type must be specified */ + if ((Spec.Flags & DS_TYPE_MASK) == DS_NONE) { + Error ("Declaration specifier or '...' expected"); + } + /* Warn about new local type declaration */ - if ((Spec.Flags & DS_NEW_TYPE_DECL) != 0) { + if ((Spec.Flags & DS_NEW_TYPE_DECL) != 0 && !IsAnonESUType (Spec.Type)) { Warning ("'%s' will be invisible out of this function", GetFullTypeName (Spec.Type)); } @@ -1698,7 +2009,7 @@ static void ParseAnsiParamList (FuncDesc* F) /* Allow parameters without a name, but remember if we had some to ** eventually print an error message later. */ - ParseDecl (&Spec, &Decl, DM_ACCEPT_IDENT); + ParseDecl (&Spec, &Decl, DM_ACCEPT_PARAM_IDENT); if (Decl.Ident[0] == '\0') { /* Unnamed symbol. Generate a name that is not user accessible, @@ -1715,7 +2026,7 @@ static void ParseAnsiParamList (FuncDesc* F) ParseAttribute (&Decl); /* Create a symbol table entry */ - Param = AddLocalSym (Decl.Ident, ParamTypeCvt (Decl.Type), Decl.StorageClass, 0); + Param = AddLocalSym (Decl.Ident, PtrConversion (Decl.Type), Decl.StorageClass, 0); /* Add attributes if we have any */ SymUseAttr (Param, &Decl); @@ -1730,38 +2041,32 @@ static void ParseAnsiParamList (FuncDesc* F) /* Count arguments */ ++F->ParamCount; - /* Check for more parameters */ - if (CurTok.Tok == TOK_COMMA) { - NextToken (); - } else { + if (PrevErrorCount != ErrorCount) { + /* Try some smart error recovery */ + if (SmartErrorSkip (0) < 0) { + break; + } + } + + /* Check for end of parameter type list */ + if (CurTok.Tok != TOK_COMMA) { break; } + NextToken (); } - - /* Skip right paren. We must explicitly check for one here, since some of - ** the breaks above bail out without checking. - */ - ConsumeRParen (); } -static FuncDesc* ParseFuncDecl (void) -/* Parse the argument list of a function with the enclosing parentheses */ +static void ParseFuncDecl (Declarator* D, declmode_t Mode, TypeCode Qualifiers) +/* Parse the argument list of a function with the closing parenthesis */ { - SymEntry* Sym; - SymEntry* WrappedCall; - unsigned int WrappedCallData; - /* Create a new function descriptor */ FuncDesc* F = NewFuncDesc (); /* Enter a new lexical level */ EnterFunctionLevel (); - /* Skip the opening paren */ - NextToken (); - /* Check for several special parameter lists */ if (CurTok.Tok == TOK_RPAREN) { /* Parameter list is empty (K&R-style) */ @@ -1770,19 +2075,23 @@ static FuncDesc* ParseFuncDecl (void) /* Parameter list declared as void */ NextToken (); F->Flags |= FD_VOID_PARAM; - } else if (CurTok.Tok == TOK_IDENT && + } else if (Mode != DM_NO_IDENT && + CurTok.Tok == TOK_IDENT && (NextTok.Tok == TOK_COMMA || NextTok.Tok == TOK_RPAREN)) { /* If the identifier is a typedef, we have a new-style parameter list; ** if it's some other identifier, it's an old-style parameter list. + ** Note: Non-empty Old-style (K&R) parameter list is not allowed in + ** type names. */ - Sym = FindSym (CurTok.Ident); + SymEntry* Sym = FindSym (CurTok.Ident); if (Sym == 0 || !SymIsTypeDef (Sym)) { - /* Old-style (K&R) function. */ + /* Old-style (K&R) function */ F->Flags |= FD_OLDSTYLE; } } /* Parse params */ + PushLexicalLevel (LEX_LEVEL_PARAM_LIST); if ((F->Flags & FD_OLDSTYLE) == 0) { /* New-style function */ ParseAnsiParamList (F); @@ -1791,135 +2100,182 @@ static FuncDesc* ParseFuncDecl (void) ParseOldStyleParamList (F); } + if (!ConsumeRParen ()) { + /* Try some smart error recovery */ + SimpleErrorSkip (); + NextToken (); + } else if (Mode == DM_IDENT_OR_EMPTY && (F->Flags & FD_OLDSTYLE) != 0) { + /* Parameter declaration list is only allowed in function definitions */ + ParseOldStyleParamDeclList (F); + } + + PopLexicalLevel (); + /* Remember the last function parameter. We need it later for several ** purposes, for example when passing stuff to fastcall functions. Since ** more symbols are added to the table, it is easier if we remember it ** now, since it is currently the last entry in the symbol table. */ - F->LastParam = GetSymTab()->SymTail; + F->LastParam = GetSymTab ()->SymTail; + + /* Leave the lexical level remembering the symbol tables */ + RememberFunctionLevel (F); /* It is allowed to use incomplete types in function prototypes, so we ** won't always get to know the parameter sizes here and may do that later. */ F->Flags |= FD_INCOMPLETE_PARAM; - /* Leave the lexical level remembering the symbol tables */ - RememberFunctionLevel (F); - - /* Did we have a WrappedCall for this function? */ - GetWrappedCall((void **) &WrappedCall, &WrappedCallData); - if (WrappedCall) { - F->WrappedCall = WrappedCall; - F->WrappedCallData = WrappedCallData; + /* We cannot specify fastcall for variadic functions */ + if ((F->Flags & FD_VARIADIC) && (Qualifiers & T_QUAL_FASTCALL)) { + Error ("Variadic functions cannot be __fastcall__"); + Qualifiers &= ~T_QUAL_FASTCALL; } - /* Return the function descriptor */ - return F; + /* Add the function type. Be sure to bounds check the type buffer */ + NeedTypeSpace (D, 1); + D->Type[D->Index].C = T_FUNC | Qualifiers; + D->Type[D->Index].A.F = F; + ++D->Index; } -static void DirectDecl (const DeclSpec* Spec, Declarator* D, declmode_t Mode) +static void DirectDecl (DeclSpec* Spec, Declarator* D, TypeCode* RemQ, declmode_t Mode) /* Recursively process direct declarators. Build a type array in reverse order. */ { /* Read optional function or pointer qualifiers that modify the identifier - ** or token to the right. For convenience, we allow a calling convention - ** also for pointers here. If it's a pointer-to-function, the qualifier - ** later will be transfered to the function itself. If it's a pointer to - ** something else, it will be flagged as an error. + ** or token to the right. */ - TypeCode Qualifiers = OptionalQualifiers (T_QUAL_NONE, T_QUAL_ADDRSIZE | T_QUAL_CCONV); + TypeCode Qualifiers = *RemQ | OptionalQualifiers (*RemQ, T_QUAL_ADDRSIZE | T_QUAL_CCONV); /* Pointer to something */ if (CurTok.Tok == TOK_STAR) { + /* Qualifiers on the pointer itself */ + TypeCode Q = T_QUAL_NONE; /* Skip the star */ NextToken (); - /* Allow const, restrict, and volatile qualifiers */ - Qualifiers |= OptionalQualifiers (Qualifiers, T_QUAL_CVR); + /* A pointer type cannot be used as an empty declaration */ + if (Mode == DM_IDENT_OR_EMPTY) { + Spec->Flags |= DS_NO_EMPTY_DECL; + } - /* Parse the type that the pointer points to */ - DirectDecl (Spec, D, Mode); + /* Allow const, restrict, and volatile qualifiers */ + Q |= OptionalQualifiers (Qualifiers, T_QUAL_CVR); + + /* For convenience, we allow a calling convention also for pointers + ** here. If it's a pointer-to-function, the qualifier later will be + ** transfered to the function itself. If it's a pointer to something + ** else, it will be flagged as an error. + */ + *RemQ = T_QUAL_NONE; + + /* Parse the type that derives from the pointer */ + DirectDecl (Spec, D, RemQ, Mode); /* Add the type */ - AddTypeCodeToDeclarator (D, T_PTR | Qualifiers); + AddTypeCodeToDeclarator (D, T_PTR | Q | *RemQ); + + /* Return the calling convention and address size specifiers on the + ** pointee type. + */ + *RemQ = Qualifiers; return; } if (CurTok.Tok == TOK_LPAREN) { - NextToken (); - DirectDecl (Spec, D, Mode); - ConsumeRParen (); - } else { - /* Things depend on Mode now: - ** - Mode == DM_NEED_IDENT means: - ** we *must* have a type and a variable identifer. - ** - Mode == DM_NO_IDENT means: - ** we must have a type but no variable identifer - ** (if there is one, it's not read). - ** - Mode == DM_ACCEPT_IDENT means: - ** we *may* have an identifier. If there is an identifier, - ** it is read, but it is no error, if there is none. + int Nested = 0; + SymEntry* Entry; + + /* An empty declaration cannot contain parentheses where an identifier + ** would show up if it were a non-empty declaration. */ - if (Mode == DM_NO_IDENT) { - D->Ident[0] = '\0'; - } else if (CurTok.Tok == TOK_IDENT) { - strcpy (D->Ident, CurTok.Ident); - NextToken (); + if (Mode == DM_IDENT_OR_EMPTY) { + Spec->Flags |= DS_NO_EMPTY_DECL; + } + + /* Skip the opening paren */ + NextToken (); + + /* We have to disambiguate the meanings of 'type (identifier' when + ** the identifier can be a typedef'ed parameter type specifier or + ** a declarator enclosed in parentheses in some cases. + */ + if (Mode == DM_IDENT_OR_EMPTY || /* If we are in a declaration... */ + CurTok.Tok == TOK_LPAREN || /* or the next token is one more paren... */ + CurTok.Tok == TOK_STAR || /* or a '*' ... */ + (CurTok.Tok == TOK_IDENT && /* or an identifier that... */ + ((Entry = FindSym (CurTok.Ident)) == 0 || /* is not a typedef. */ + !SymIsTypeDef (Entry)))) { + Nested = 1; } else { - if (Mode == DM_NEED_IDENT) { - /* Some fix point tokens that are used for error recovery */ - static const token_t TokenList[] = { TOK_COMMA, TOK_SEMI }; + /* Check for qualifiers */ + TypeCode Q = OptionalQualifiers (T_QUAL_NONE, T_QUAL_ADDRSIZE | T_QUAL_CCONV); - Error ("Identifier expected"); - - /* Try some smart error recovery */ - SkipTokens (TokenList, sizeof(TokenList) / sizeof(TokenList[0])); + if (Q != T_QUAL_NONE) { + Qualifiers |= Q; + Nested = 1; + } + } + + if (Nested) { + /* Parse the direct declarator in parentheses */ + DirectDecl (Spec, D, &Qualifiers, Mode); + ConsumeRParen (); + } else { + /* This is a parameter type list in parentheses */ + ParseFuncDecl (D, Mode, Qualifiers); + + /* Qualifiers now used */ + Qualifiers = T_QUAL_NONE; + } + + } else if (CurTok.Tok == TOK_IDENT) { + if (Mode == DM_NO_IDENT) { + Error ("Unexpected identifier in type name"); + } + strcpy (D->Ident, CurTok.Ident); + NextToken (); + } else { + D->Ident[0] = '\0'; + if (CurTok.Tok != TOK_LBRACK && + ((Spec->Flags & DS_ALLOW_BITFIELD) == 0 || CurTok.Tok != TOK_COLON)) { + if ((Spec->Flags & DS_TYPE_MASK) == DS_DEF_TYPE) { + Error ("Declaration specifier or identifier expected"); + } else if ((Spec->Flags & DS_NO_EMPTY_DECL) != 0) { + Error ("Identifier expected"); } - D->Ident[0] = '\0'; } } while (CurTok.Tok == TOK_LBRACK || CurTok.Tok == TOK_LPAREN) { if (CurTok.Tok == TOK_LPAREN) { + /* Skip the opening paren */ + NextToken (); /* Function declarator */ - FuncDesc* F; - SymEntry* PrevEntry; - - /* Parse the function declarator */ - F = ParseFuncDecl (); - - /* We cannot specify fastcall for variadic functions */ - if ((F->Flags & FD_VARIADIC) && (Qualifiers & T_QUAL_FASTCALL)) { - Error ("Variadic functions cannot be __fastcall__"); - Qualifiers &= ~T_QUAL_FASTCALL; - } - - /* Was there a previous entry? If so, copy WrappedCall info from it */ - PrevEntry = FindGlobalSym (D->Ident); - if (PrevEntry && PrevEntry->Flags & SC_FUNC) { - FuncDesc* D = GetFuncDesc (PrevEntry->Type); - if (D->WrappedCall && !F->WrappedCall) { - F->WrappedCall = D->WrappedCall; - F->WrappedCallData = D->WrappedCallData; - } - } - - /* Add the function type. Be sure to bounds check the type buffer */ - NeedTypeSpace (D, 1); - D->Type[D->Index].C = T_FUNC | Qualifiers; - D->Type[D->Index].A.F = F; - ++D->Index; + ParseFuncDecl (D, Mode, Qualifiers); /* Qualifiers now used */ Qualifiers = T_QUAL_NONE; - } else { /* Array declarator */ long Size = UNSPECIFIED; + /* An array type cannot be used as an empty declaration */ + if (Mode == DM_IDENT_OR_EMPTY) { + Spec->Flags |= DS_NO_EMPTY_DECL; + if (D->Ident[0] == '\0') { + if ((Spec->Flags & DS_TYPE_MASK) != DS_NONE) { + Error ("Identifier or ';' expected after declaration specifiers"); + } else { + Error ("Identifier expected"); + } + } + } + /* We cannot have any qualifiers for an array */ if (Qualifiers != T_QUAL_NONE) { Error ("Invalid qualifiers for array"); @@ -1954,44 +2310,53 @@ static void DirectDecl (const DeclSpec* Spec, Declarator* D, declmode_t Mode) } } - /* If we have remaining qualifiers, flag them as invalid */ - if (Qualifiers & T_QUAL_NEAR) { - Error ("Invalid '__near__' qualifier"); - } - if (Qualifiers & T_QUAL_FAR) { - Error ("Invalid '__far__' qualifier"); - } - if (Qualifiers & T_QUAL_FASTCALL) { - Error ("Invalid '__fastcall__' qualifier"); - } - if (Qualifiers & T_QUAL_CDECL) { - Error ("Invalid '__cdecl__' qualifier"); - } + *RemQ = Qualifiers; } /*****************************************************************************/ -/* code */ +/* Code */ /*****************************************************************************/ Type* ParseType (Type* T) -/* Parse a complete type specification */ +/* Parse a complete type specification in parentheses */ { DeclSpec Spec; Declarator Decl; + int NeedClean = -1; + + /* Skip the left paren */ + NextToken (); /* Get a type without a default */ InitDeclSpec (&Spec); - ParseTypeSpec (&Spec, TS_DEFAULT_TYPE_NONE, NULL); + ParseTypeSpec (&Spec, TS_DEFAULT_TYPE_NONE); - /* Parse additional declarators */ - ParseDecl (&Spec, &Decl, DM_NO_IDENT); + /* Only parse further if there is a type specifier */ + if ((Spec.Flags & DS_TYPE_MASK) != DS_NONE) { + /* Parse additional declarators */ + NeedClean = ParseDecl (&Spec, &Decl, DM_NO_IDENT); - /* Copy the type to the target buffer */ - TypeCopy (T, Decl.Type); + /* Copy the type to the target buffer */ + TypeCopy (T, Decl.Type); + } else { + /* Fail-safe */ + TypeCopy (T, type_int); + } + + /* Try some smart error recovery */ + if (NeedClean < 0) { + SimpleErrorSkip (); + } + + /* Closing paren */ + if (!ConsumeRParen ()) { + SimpleErrorSkip (); + NextToken (); + } /* Return a pointer to the target buffer */ return T; @@ -1999,145 +2364,224 @@ Type* ParseType (Type* T) -void ParseDecl (const DeclSpec* Spec, Declarator* D, declmode_t Mode) -/* Parse a variable, type or function declarator */ +int ParseDecl (DeclSpec* Spec, Declarator* D, declmode_t Mode) +/* Parse a variable, type or function declarator. Return -1 if this stops at +** an unpaired right parenthesis/bracket/curly brace. Return 0 if this stops +** after consuming a semicolon or closing curly brace, or reaching an EOF. +** Return 1 otherwise. +*/ { + TypeCode Q = T_QUAL_NONE; + /* Used to check if we have any errors during parsing this */ unsigned PrevErrorCount = ErrorCount; + /* If there is no explicit type specifier, an optional identifier becomes + ** required. + */ + if (Mode == DM_IDENT_OR_EMPTY && + (Spec->Flags & DS_TYPE_MASK) == DS_DEF_TYPE) { + Spec->Flags |= DS_NO_EMPTY_DECL; + } + /* Initialize the Declarator struct */ InitDeclarator (D); /* Get additional derivation of the declarator and the identifier */ - DirectDecl (Spec, D, Mode); + DirectDecl (Spec, D, &Q, Mode); /* Add the base type */ NeedTypeSpace (D, TypeLen (Spec->Type) + 1); /* Bounds check */ TypeCopy (D->Type + D->Index, Spec->Type); + D->Type[D->Index].C |= Q; /* Use the storage class from the declspec */ D->StorageClass = Spec->StorageClass; + /* If we have a function, add a special symbol type */ + if (Mode != DM_ACCEPT_PARAM_IDENT && + IsTypeFunc (D->Type) && + (D->StorageClass & SC_TYPEMASK) == SC_NONE) { + D->StorageClass |= SC_FUNC; + } + /* Do several fixes on qualifiers */ FixQualifiers (D->Type); - /* Check if the data type consists of any arrays of forbidden types */ - CheckArrayElementType (D->Type); + /* Check if the data type consists of any functions returning forbidden return + ** types and remove qualifiers from the return types if they are not void. + */ + FixFunctionReturnType (D->Type); - /* If we have a function, add a special storage class */ - if (IsTypeFunc (D->Type)) { - D->StorageClass |= SC_FUNC; - } + /* Check recursively if the data type consists of arrays of forbidden types */ + CheckArrayElementType (D->Type); /* Parse attributes for this declarator */ ParseAttribute (D); - /* Check several things for function or function pointer types */ - if (IsTypeFunc (D->Type) || IsTypeFuncPtr (D->Type)) { - - /* A function. Check the return type */ - Type* RetType = GetFuncReturnTypeModifiable (D->Type); - - /* Functions may not return functions or arrays */ - if (IsTypeFunc (RetType)) { - Error ("Functions are not allowed to return functions"); - } else if (IsTypeArray (RetType)) { - Error ("Functions are not allowed to return arrays"); - } - - /* The return type must not be qualified */ - if (GetQualifier (RetType) != T_QUAL_NONE && RetType[1].C == T_END) { - - if (GetRawTypeRank (RetType) == T_RANK_VOID) { - /* A qualified void type is always an error */ - Error ("function definition has qualified void return type"); - } else { - /* For others, qualifiers are ignored */ - Warning ("type qualifiers ignored on function return type"); - RetType[0].C = GetUnqualRawTypeCode (RetType); - } - } - - /* Warn about an implicit int return in the function */ - if ((Spec->Flags & DS_DEF_TYPE) != 0 && - RetType[0].C == T_INT && RetType[1].C == T_END) { - /* Function has an implicit int return. Output a warning if we don't - ** have the C89 standard enabled explicitly. + /* Check a few things for the instance (rather than the type) */ + if (D->Ident[0] != '\0') { + /* Check a few pre-C99 things */ + if ((Spec->Flags & DS_TYPE_MASK) == DS_DEF_TYPE && IsRankInt (Spec->Type)) { + /* If the standard was not set explicitly to C89, print a warning + ** for typedefs with implicit int type specifier. */ if (IS_Get (&Standard) >= STD_C99) { - Warning ("Implicit 'int' return type is an obsolete feature"); + if ((D->StorageClass & SC_TYPEMASK) != SC_TYPEDEF) { + Warning ("Implicit 'int' type specifier is an obsolete feature"); + } else { + Warning ("Type specifier defaults to 'int' in typedef of '%s'", + D->Ident); + Note ("Implicit 'int' type specifier is an obsolete feature"); + } } - GetFuncDesc (D->Type)->Flags |= FD_OLDSTYLE_INTRET; } - } + /* Check other things depending on the "kind" of the instance */ + if ((D->StorageClass & SC_TYPEMASK) == SC_FUNC) { + /* Special handling for main() */ + if (strcmp (D->Ident, "main") == 0) { + /* main() cannot be a fastcall function */ + if (IsQualFastcall (D->Type)) { + Error ("'main' cannot be declared __fastcall__"); + } - /* For anthing that is not a function or typedef, check for an implicit - ** int declaration. - */ - if ((D->StorageClass & SC_FUNC) != SC_FUNC && - (D->StorageClass & SC_TYPEMASK) != SC_TYPEDEF) { - /* If the standard was not set explicitly to C89, print a warning - ** for variables with implicit int type. - */ - if ((Spec->Flags & DS_DEF_TYPE) != 0 && IS_Get (&Standard) >= STD_C99) { - Warning ("Implicit 'int' is an obsolete feature"); + /* main() cannot be an inline function */ + if ((D->StorageClass & SC_INLINE) == SC_INLINE) { + Error ("'main' cannot be declared inline"); + D->StorageClass &= ~SC_INLINE; + } + + /* Check return type */ + if (GetUnqualRawTypeCode (GetFuncReturnType (D->Type)) != T_INT) { + /* If cc65 extensions aren't enabled, don't allow a main function + ** that doesn't return an int. + */ + if (IS_Get (&Standard) != STD_CC65) { + Error ("'main' must always return an int"); + } + } + } + } else if (Mode != DM_ACCEPT_PARAM_IDENT && + (D->StorageClass & SC_INLINE) == SC_INLINE) { + /* 'inline' is only allowed on functions */ + Error ("'inline' on non-function declaration"); + D->StorageClass &= ~SC_INLINE; } } - if (!IsTypeFunc (D->Type) && !IsTypeVoid (D->Type)) { - /* Check the size of the generated type */ + /* Check the size of the declared type */ + if (IsObjectType (D->Type)) { unsigned Size = SizeOf (D->Type); + if (Size >= 0x10000) { if (D->Ident[0] != '\0') { - Error ("Size of '%s' is invalid (0x%06X)", D->Ident, Size); + Error ("Size of '%s' is too large (0x%06X)", D->Ident, Size); } else { - Error ("Invalid size in declaration (0x%06X)", Size); + Error ("Size in declaration is too large (0x%06X)", Size); } } } + /* An empty declaration must be terminated with a semicolon */ + if (PrevErrorCount == ErrorCount && + Mode == DM_IDENT_OR_EMPTY && + D->Ident[0] == '\0' && + CurTok.Tok != TOK_SEMI && + ((Spec->Flags & DS_ALLOW_BITFIELD) == 0 || CurTok.Tok != TOK_COLON)) { + Error ("Identifier or ';' expected after declaration specifiers"); + } + if (PrevErrorCount != ErrorCount) { - /* Make the declaration fictitious if is is not parsed correctly */ - D->StorageClass |= SC_FICTITIOUS; - - if (Mode == DM_NEED_IDENT && D->Ident[0] == '\0') { + if ((Spec->Flags & DS_TYPE_MASK) != DS_DEF_TYPE && + (Spec->Flags & DS_NO_EMPTY_DECL) != 0 && + D->Ident[0] == '\0') { /* Use a fictitious name for the identifier if it is missing */ - AnonName (D->Ident, "global"); + const char* Level = ""; + + switch (GetLexicalLevel ()) { + case LEX_LEVEL_GLOBAL: + Level = "global"; + break; + case LEX_LEVEL_FUNCTION: + case LEX_LEVEL_BLOCK: + Level = "local"; + break; + case LEX_LEVEL_STRUCT: + Level = "field"; + break; + default: + Level = "unknown"; + break; + } + AnonName (D->Ident, Level); + + /* Make the declarator fictitious */ + D->StorageClass |= SC_FICTITIOUS; + } + + /* Try some smart error recovery */ + if (Mode == DM_NO_IDENT) { + return SimpleErrorSkip (); + } else if (CurTok.Tok != TOK_LCURLY || !IsTypeFunc (D->Type)) { + /* Skip to the end of the whole declaration if it is not part of a + ** parameter list. + */ + return SmartErrorSkip (Mode == DM_IDENT_OR_EMPTY); } } + + return 1; } -void ParseDeclSpec (DeclSpec* D, typespec_t TSFlags, unsigned DefStorage) +void ParseDeclSpec (DeclSpec* Spec, typespec_t TSFlags, unsigned DefStorage) /* Parse a declaration specification */ { /* Initialize the DeclSpec struct */ - InitDeclSpec (D); + InitDeclSpec (Spec); /* Assume we're using an explicit storage class */ - D->Flags &= ~DS_DEF_STORAGE; + Spec->Flags &= ~DS_DEF_STORAGE; /* Parse the type specifiers */ - ParseTypeSpec (D, TSFlags | TS_STORAGE_CLASS_SPEC | TS_FUNCTION_SPEC, NULL); + ParseTypeSpec (Spec, TSFlags | TS_STORAGE_CLASS_SPEC); /* If no explicit storage class is given, use the default */ - if (D->StorageClass == 0) { - D->Flags |= DS_DEF_STORAGE; - D->StorageClass = DefStorage; + if ((Spec->StorageClass & SC_STORAGEMASK) == 0) { + Spec->Flags |= DS_DEF_STORAGE; + Spec->StorageClass |= DefStorage; } } -void CheckEmptyDecl (const DeclSpec* D) +void CheckEmptyDecl (const DeclSpec* Spec) /* Called after an empty type declaration (that is, a type declaration without ** a variable). Checks if the declaration does really make sense and issues a ** warning if not. */ { - if ((D->Flags & DS_EXTRA_TYPE) == 0) { + if ((Spec->StorageClass & SC_INLINE) == SC_INLINE) { + Error ("'inline' on empty declaration"); + } else if ((Spec->Flags & DS_TYPE_MASK) == DS_NONE) { + /* No declaration at all */ + } else if ((Spec->Flags & DS_EXTRA_TYPE) == 0) { + /* Empty declaration of basic types */ Warning ("Useless declaration"); + } else if (IsAnonStructClass (Spec->Type)) { + /* This could be that the user made a wrong attempt to declare an + ** anonymous struct/union field outside a struct/union. + */ + Warning ("Unnamed %s that defines no instances", GetBasicTypeName (Spec->Type)); + } else if (GetLexicalLevel () == LEX_LEVEL_STRUCT) { + /* This could be that the user made a wrong attempt to declare an + ** anonymous struct/union field inside a struct/union. Perhaps just + ** paranoid since it is not so uncommon to do forward declarations. + */ + if (!IsTypeEnum (Spec->Type) || ((Spec->Flags & DS_NEW_TYPE_DEF) == 0)) { + Warning ("Declaration defines no instances"); + } } } diff --git a/src/cc65/declare.h b/src/cc65/declare.h index ee9e1fc63..4cfc48c68 100644 --- a/src/cc65/declare.h +++ b/src/cc65/declare.h @@ -65,17 +65,26 @@ enum typespec_t { TS_DEFAULT_TYPE_AUTO = 0x02, /* C23 type inference with auto */ /* Whether to allow certain kinds of specifiers */ - TS_STORAGE_CLASS_SPEC = 0x04, /* Allow storage storage class specifiers */ - TS_FUNCTION_SPEC = 0x08, /* Allow function specifiers */ + TS_STORAGE_CLASS_SPEC = 0x04, /* Allow storage class specifiers */ + TS_FUNCTION_SPEC = 0x08, /* Allow function specifiers */ }; /* Masks for the Flags field in DeclSpec */ +#define DS_NONE 0x0000U /* Nothing specified or used */ #define DS_DEF_STORAGE 0x0001U /* Default storage class used */ -#define DS_DEF_TYPE 0x0002U /* Default type used */ -#define DS_EXTRA_TYPE 0x0004U /* Extra type declared */ +#define DS_EXPLICIT_TYPE 0x0002U /* Type specified */ +#define DS_DEF_TYPE 0x0004U /* Implicit type used */ +#define DS_AUTO_TYPE 0x0006U /* C23 auto type used */ +#define DS_TYPE_MASK 0x0006U /* Mask for type of spec decl */ +#define DS_EXTRA_TYPE 0x0008U /* ESU type in declaration */ #define DS_NEW_TYPE_DECL 0x0010U /* New type declared */ #define DS_NEW_TYPE_DEF 0x0020U /* New type defined */ #define DS_NEW_TYPE (DS_NEW_TYPE_DECL | DS_NEW_TYPE_DEF) +#define DS_EXPLICIT_SIGNEDNESS 0x0040U /* Signedness specified */ +#define DS_NO_EMPTY_DECL 0x0100U /* Disallow empty declaration */ +#define DS_ALLOW_BITFIELD 0x0200U /* Allow anonymous bit-fields */ + + /* Result of ParseDeclSpec */ typedef struct DeclSpec DeclSpec; @@ -97,11 +106,24 @@ struct Declarator { unsigned Index; /* Used to build Type */ }; -/* Modes for ParseDecl */ +/* Modes for ParseDecl: +** - DM_IDENT_OR_EMPTY means: +** we *may* have an identifier, or none. If it is the latter case, +** the type specifier must be used for an empty declaration, +** or it is an error. +** - DM_NO_IDENT means: +** we must have a type but no variable identifer +** (if there is one, it's not read). +** Note: this is used for type names. +** - DM_ACCEPT_PARAM_IDENT means: +** we *may* have an identifier. If there is an identifier, +** it is read, but it is no error, if there is none. +** Note: this is used for function parameter type lists. +*/ typedef enum { - DM_NEED_IDENT, /* We must have an identifier */ - DM_NO_IDENT, /* We won't read an identifier */ - DM_ACCEPT_IDENT, /* We will accept an id if there is one */ + DM_IDENT_OR_EMPTY, + DM_NO_IDENT, + DM_ACCEPT_PARAM_IDENT, } declmode_t; @@ -112,19 +134,20 @@ typedef enum { -void InitDeclSpec (DeclSpec* D); -/* Initialize the DeclSpec struct for use */ - Type* ParseType (Type* Type); -/* Parse a complete type specification */ +/* Parse a complete type specification in parentheses */ -void ParseDecl (const DeclSpec* Spec, Declarator* D, declmode_t Mode); -/* Parse a variable, type or function declarator */ +int ParseDecl (DeclSpec* Spec, Declarator* D, declmode_t Mode); +/* Parse a variable, type or function declarator. Return -1 if this stops at +** an unpaired right parenthesis/bracket/curly brace. Return 0 if this stops +** after consuming a semicolon or closing curly brace, or reaching an EOF. +** Return 1 otherwise. +*/ -void ParseDeclSpec (DeclSpec* D, typespec_t TSFlags, unsigned DefStorage); +void ParseDeclSpec (DeclSpec* Spec, typespec_t TSFlags, unsigned DefStorage); /* Parse a declaration specification */ -void CheckEmptyDecl (const DeclSpec* D); +void CheckEmptyDecl (const DeclSpec* Spec); /* Called after an empty type declaration (that is, a type declaration without ** a variable). Checks if the declaration does really make sense and issues a ** warning if not. diff --git a/src/cc65/error.c b/src/cc65/error.c index 39b067825..db0debf8c 100644 --- a/src/cc65/error.c +++ b/src/cc65/error.c @@ -59,8 +59,12 @@ /* Count of errors/warnings */ -unsigned ErrorCount = 0; -unsigned WarningCount = 0; +unsigned PPErrorCount = 0; /* Pre-parser errors */ +unsigned PPWarningCount = 0; /* Pre-parser warnings */ +unsigned ErrorCount = 0; /* Errors occurred in parser and later translation phases */ +unsigned WarningCount = 0; /* Warnings occurred in parser and later translation phases */ +unsigned RecentLineNo = 0; +unsigned RecentErrorCount = 0; /* Warning and error options */ IntStack WarnEnable = INTSTACK(1); /* Enable warnings */ @@ -116,13 +120,46 @@ Collection DiagnosticStrBufs; +void PrintFileInclusionInfo (const LineInfo* LI) +/* Print hierarchy of file inclusion */ +{ + if (LI->IncFiles != 0) { + unsigned FileCount = CollCount (LI->IncFiles); + if (FileCount > 0) { + const char* Str = "In file included from %s:%u%c\n"; + + while (FileCount-- > 0) { + LineInfoFile* LIF = CollAtUnchecked (LI->IncFiles, FileCount); + char C = FileCount > 0 ? ',' : ':'; + + fprintf (stderr, Str, LIF->Name, LIF->LineNum, C); + Str = " from %s:%u%c\n"; + } + } + } +} + + + +static LineInfo* GetDiagnosticLI (void) +/* Get the line info where the diagnostic info refers to */ +{ + if (CurTok.LI) { + return CurTok.LI; + } else { + return GetCurLineInfo (); + } +} + + + static const char* GetDiagnosticFileName (void) /* Get the source file name where the diagnostic info refers to */ { if (CurTok.LI) { - return GetInputName (CurTok.LI); + return GetPresumedFileName (CurTok.LI); } else { - return GetCurrentFilename (); + return GetCurrentFileName (); } } @@ -132,7 +169,7 @@ static unsigned GetDiagnosticLineNum (void) /* Get the source line number where the diagnostic info refers to */ { if (CurTok.LI) { - return GetInputLine (CurTok.LI); + return GetPresumedLineNum (CurTok.LI); } else { return GetCurrentLineNum (); } @@ -195,41 +232,61 @@ void Internal (const char* Format, ...) -static void IntError (const char* Filename, unsigned LineNo, const char* Msg, va_list ap) +static void IntError (errcat_t EC, LineInfo* LI, const char* Msg, va_list ap) /* Print an error message - internal function */ { - fprintf (stderr, "%s:%u: Error: ", Filename, LineNo); + unsigned LineNo = GetPresumedLineNum (LI); + + /* Print file inclusion if appropriate */ + if (HasFileInclusionChanged (LI)) { + PrintFileInclusionInfo (LI); + } + RememberCheckedLI (LI); + + fprintf (stderr, "%s:%u: Error: ", GetPresumedFileName (LI), LineNo); vfprintf (stderr, Msg, ap); fprintf (stderr, "\n"); if (Line) { Print (stderr, 1, "Input: %.*s\n", (int) SB_GetLen (Line), SB_GetConstBuf (Line)); } - ++ErrorCount; - if (ErrorCount > 20) { + + if (EC != EC_PP) { + ++ErrorCount; + } else { + ++PPErrorCount; + } + if (RecentLineNo != LineNo) { + RecentLineNo = LineNo; + RecentErrorCount = 0; + } else { + ++RecentErrorCount; + } + + if (RecentErrorCount > 20 || GetTotalErrors () > 200) { Fatal ("Too many errors"); } } +void LIError (errcat_t EC, LineInfo* LI, const char* Format, ...) +/* Print an error message with the line info given explicitly */ +{ + va_list ap; + va_start (ap, Format); + IntError (EC, LI, Format, ap); + va_end (ap); +} + + + void Error (const char* Format, ...) /* Print an error message */ { va_list ap; va_start (ap, Format); - IntError (GetDiagnosticFileName (), GetDiagnosticLineNum (), Format, ap); - va_end (ap); -} - - - -void LIError (const LineInfo* LI, const char* Format, ...) -/* Print an error message with the line info given explicitly */ -{ - va_list ap; - va_start (ap, Format); - IntError (GetInputName (LI), GetInputLine (LI), Format, ap); + IntError (EC_PARSER, GetDiagnosticLI (), Format, ap); va_end (ap); } @@ -240,7 +297,7 @@ void PPError (const char* Format, ...) { va_list ap; va_start (ap, Format); - IntError (GetCurrentFilename(), GetCurrentLineNum(), Format, ap); + IntError (EC_PP, GetCurLineInfo (), Format, ap); va_end (ap); } @@ -252,47 +309,60 @@ void PPError (const char* Format, ...) -static void IntWarning (const char* Filename, unsigned LineNo, const char* Msg, va_list ap) +static void IntWarning (errcat_t EC, LineInfo* LI, const char* Msg, va_list ap) /* Print a warning message - internal function */ { if (IS_Get (&WarningsAreErrors)) { /* Treat the warning as an error */ - IntError (Filename, LineNo, Msg, ap); + IntError (EC, LI, Msg, ap); } else if (IS_Get (&WarnEnable)) { - fprintf (stderr, "%s:%u: Warning: ", Filename, LineNo); + unsigned LineNo = GetPresumedLineNum (LI); + + /* Print file inclusion if appropriate */ + if (HasFileInclusionChanged (LI)) { + PrintFileInclusionInfo (LI); + } + RememberCheckedLI (LI); + + fprintf (stderr, "%s:%u: Warning: ", GetPresumedFileName (LI), LineNo); vfprintf (stderr, Msg, ap); fprintf (stderr, "\n"); if (Line) { Print (stderr, 1, "Input: %.*s\n", (int) SB_GetLen (Line), SB_GetConstBuf (Line)); } - ++WarningCount; + + if (EC != EC_PP) { + ++WarningCount; + } else { + ++PPWarningCount; + } } } +void LIWarning (errcat_t EC, LineInfo* LI, const char* Format, ...) +/* Print a warning message with the line info given explicitly */ +{ + va_list ap; + va_start (ap, Format); + IntWarning (EC, LI, Format, ap); + va_end (ap); +} + + + void Warning (const char* Format, ...) /* Print a warning message */ { va_list ap; va_start (ap, Format); - IntWarning (GetDiagnosticFileName (), GetDiagnosticLineNum (), Format, ap); - va_end (ap); -} - - - -void LIWarning (const LineInfo* LI, const char* Format, ...) -/* Print a warning message with the line info given explicitly */ -{ - va_list ap; - va_start (ap, Format); - IntWarning (GetInputName (LI), GetInputLine (LI), Format, ap); + IntWarning (EC_PARSER, GetDiagnosticLI (), Format, ap); va_end (ap); } @@ -303,12 +373,24 @@ void PPWarning (const char* Format, ...) { va_list ap; va_start (ap, Format); - IntWarning (GetCurrentFilename(), GetCurrentLineNum(), Format, ap); + IntWarning (EC_PP, GetCurLineInfo (), Format, ap); va_end (ap); } +void UnreachableCodeWarning (void) +/* Print a warning about unreachable code at the current location if these +** warnings are enabled. +*/ +{ + if (IS_Get (&WarnUnreachableCode)) { + Warning ("Unreachable code"); + } +} + + + IntStack* FindWarning (const char* Name) /* Search for a warning in the WarnMap table and return a pointer to the ** intstack that holds its state. Return NULL if there is no such warning. @@ -344,33 +426,33 @@ void ListWarnings (FILE* F) -static void IntNote (const char* Filename, unsigned LineNo, const char* Msg, va_list ap) +static void IntNote (const LineInfo* LI, const char* Msg, va_list ap) /* Print a note message - internal function */ { - fprintf (stderr, "%s:%u: Note: ", Filename, LineNo); + fprintf (stderr, "%s:%u: Note: ", GetPresumedFileName (LI), GetPresumedLineNum (LI)); vfprintf (stderr, Msg, ap); fprintf (stderr, "\n"); } -void Note (const char* Format, ...) -/* Print a note message */ -{ - va_list ap; - va_start (ap, Format); - IntNote (GetDiagnosticFileName (), GetDiagnosticLineNum (), Format, ap); - va_end (ap); -} - - - void LINote (const LineInfo* LI, const char* Format, ...) /* Print a note message with the line info given explicitly */ { va_list ap; va_start (ap, Format); - IntNote (GetInputName (LI), GetInputLine (LI), Format, ap); + IntNote (LI, Format, ap); + va_end (ap); +} + + + +void Note (const char* Format, ...) +/* Print a note message */ +{ + va_list ap; + va_start (ap, Format); + IntNote (GetDiagnosticLI (), Format, ap); va_end (ap); } @@ -381,23 +463,41 @@ void PPNote (const char* Format, ...) { va_list ap; va_start (ap, Format); - IntNote (GetCurrentFilename(), GetCurrentLineNum(), Format, ap); + IntNote (GetDiagnosticLI (), Format, ap); va_end (ap); } /*****************************************************************************/ -/* Code */ +/* Error summary */ /*****************************************************************************/ +unsigned GetTotalErrors (void) +/* Get total count of errors of all categories */ +{ + return PPErrorCount + ErrorCount; +} + + + +unsigned GetTotalWarnings (void) +/* Get total count of warnings of all categories */ +{ + return PPWarningCount + WarningCount; +} + + + void ErrorReport (void) /* Report errors (called at end of compile) */ { - unsigned int V = (ErrorCount != 0 ? 0 : 1); - Print (stdout, V, "%u errors and %u warnings generated.\n", ErrorCount, WarningCount); + unsigned TotalErrors = GetTotalErrors (); + unsigned TotalWarnings = GetTotalWarnings (); + unsigned int V = (TotalErrors != 0 ? 0 : 1); + Print (stdout, V, "%u errors and %u warnings generated.\n", TotalErrors, TotalWarnings); } @@ -418,23 +518,12 @@ void InitDiagnosticStrBufs (void) void DoneDiagnosticStrBufs (void) /* Done with tracked string buffers used for diagnostics */ -{ - ClearDiagnosticStrBufs (); - DoneCollection (&DiagnosticStrBufs); -} - - - -void ClearDiagnosticStrBufs (void) -/* Free all tracked string buffers */ { unsigned I; - for (I = 0; I < CollCount (&DiagnosticStrBufs); ++I) { SB_Done (CollAtUnchecked (&DiagnosticStrBufs, I)); } - - CollDeleteAll (&DiagnosticStrBufs); + DoneCollection (&DiagnosticStrBufs); } diff --git a/src/cc65/error.h b/src/cc65/error.h index 83be8c782..b3cdc49ab 100644 --- a/src/cc65/error.h +++ b/src/cc65/error.h @@ -55,9 +55,20 @@ +/* Error categories */ +typedef enum errcat_t errcat_t; +enum errcat_t { + EC_PP, /* Pre-parser phases */ + EC_PARSER, /* Parser and later phases */ +}; + + + /* Count of errors/warnings */ -extern unsigned ErrorCount; -extern unsigned WarningCount; +extern unsigned PPErrorCount; /* Pre-parser errors */ +extern unsigned PPWarningCount; /* Pre-parser warnings */ +extern unsigned ErrorCount; /* Errors occurred in parser and later translation phases */ +extern unsigned WarningCount; /* Warnings occurred in parser and later translation phases */ /* Warning and error options */ extern IntStack WarnEnable; /* Enable warnings */ @@ -89,6 +100,9 @@ struct StrBuf; +void PrintFileInclusionInfo (const LineInfo* LI); +/* Print hierarchy of file inclusion */ + void Fatal (const char* Format, ...) attribute ((noreturn, format (printf, 1, 2))); /* Print a message about a fatal error and die */ @@ -98,7 +112,7 @@ void Internal (const char* Format, ...) attribute ((noreturn, format (printf, 1, void Error (const char* Format, ...) attribute ((format (printf, 1, 2))); /* Print an error message */ -void LIError (const LineInfo* LI, const char* Format, ...) attribute ((format (printf, 2, 3))); +void LIError (errcat_t EC, LineInfo* LI, const char* Format, ...) attribute ((format (printf, 3, 4))); /* Print an error message with the line info given explicitly */ void PPError (const char* Format, ...) attribute ((format (printf, 1, 2))); @@ -107,12 +121,17 @@ void PPError (const char* Format, ...) attribute ((format (printf, 1, 2))); void Warning (const char* Format, ...) attribute ((format (printf, 1, 2))); /* Print a warning message */ -void LIWarning (const LineInfo* LI, const char* Format, ...) attribute ((format (printf, 2, 3))); +void LIWarning (errcat_t EC, LineInfo* LI, const char* Format, ...) attribute ((format (printf, 3, 4))); /* Print a warning message with the line info given explicitly */ void PPWarning (const char* Format, ...) attribute ((format (printf, 1, 2))); /* Print a warning message. For use within the preprocessor */ +void UnreachableCodeWarning (void); +/* Print a warning about unreachable code at the current location if these +** warnings are enabled. +*/ + IntStack* FindWarning (const char* Name); /* Search for a warning in the WarnMap table and return a pointer to the ** intstack that holds its state. Return NULL if there is no such warning. @@ -130,6 +149,12 @@ void LINote (const LineInfo* LI, const char* Format, ...) attribute ((format (pr void PPNote (const char* Format, ...) attribute ((format (printf, 1, 2))); /* Print a note message. For use within the preprocessor */ +unsigned GetTotalErrors (void); +/* Get total count of errors of all categories */ + +unsigned GetTotalWarnings (void); +/* Get total count of warnings of all categories */ + void ErrorReport (void); /* Report errors (called at end of compile) */ @@ -139,9 +164,6 @@ void InitDiagnosticStrBufs (void); void DoneDiagnosticStrBufs (void); /* Done with tracked string buffers used for diagnostics */ -void ClearDiagnosticStrBufs (void); -/* Free all tracked string buffers */ - struct StrBuf* NewDiagnosticStrBuf (void); /* Get a new tracked string buffer */ diff --git a/src/cc65/expr.c b/src/cc65/expr.c index 9460569ed..2939ab1cc 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -31,6 +31,7 @@ #include "macrotab.h" #include "preproc.h" #include "scanner.h" +#include "seqpoint.h" #include "shiftexpr.h" #include "stackptr.h" #include "standard.h" @@ -80,7 +81,7 @@ static void PostDec (ExprDesc* Expr); -unsigned GlobalModeFlags (const ExprDesc* Expr) +unsigned CG_AddrModeFlags (const ExprDesc* Expr) /* Return the addressing mode flags for the given expression */ { switch (ED_GetLoc (Expr)) { @@ -95,7 +96,7 @@ unsigned GlobalModeFlags (const ExprDesc* Expr) case E_LOC_LITERAL: return CF_LITERAL; case E_LOC_CODE: return CF_CODE; default: - Internal ("GlobalModeFlags: Invalid location flags value: 0x%04X", Expr->Flags); + Internal ("CG_AddrModeFlags: Invalid location flags value: 0x%04X", Expr->Flags); /* NOTREACHED */ return 0; } @@ -103,32 +104,32 @@ unsigned GlobalModeFlags (const ExprDesc* Expr) -static unsigned TypeOfBySize (unsigned Size) +static unsigned CG_TypeOfBySize (unsigned Size) /* Get the code generator replacement type of the object by its size */ { - unsigned NewType; + unsigned CG_Type; /* If the size is less than or equal to that of a a long, we will copy ** the struct using the primary register, otherwise we use memcpy. */ switch (Size) { - case 1: NewType = CF_CHAR; break; - case 2: NewType = CF_INT; break; + case 1: CG_Type = CF_CHAR; break; + case 2: CG_Type = CF_INT; break; case 3: /* FALLTHROUGH */ - case 4: NewType = CF_LONG; break; - default: NewType = CF_NONE; break; + case 4: CG_Type = CF_LONG; break; + default: CG_Type = CF_NONE; break; } - return NewType; + return CG_Type; } -unsigned TypeOf (const Type* T) +unsigned CG_TypeOf (const Type* T) /* Get the code generator base type of the object */ { - unsigned NewType; + unsigned CG_Type; - switch (GetUnqualTypeCode (T)) { + switch (GetUnderlyingTypeCode (T)) { case T_SCHAR: return CF_CHAR; @@ -163,9 +164,9 @@ unsigned TypeOf (const Type* T) case T_STRUCT: case T_UNION: - NewType = TypeOfBySize (SizeOf (T)); - if (NewType != CF_NONE) { - return NewType; + CG_Type = CG_TypeOfBySize (SizeOf (T)); + if (CG_Type != CF_NONE) { + return CG_Type; } /* Address of ... */ return CF_INT | CF_UNSIGNED; @@ -184,10 +185,10 @@ unsigned TypeOf (const Type* T) -unsigned FuncTypeOf (const Type* T) -/* Get the code generator flag for calling the function */ +unsigned CG_CallFlags (const Type* T) +/* Get the code generator flags for calling the function */ { - if (GetUnqualTypeCode (T) == T_FUNC) { + if (GetUnderlyingTypeCode (T) == T_FUNC) { return (T->A.F->Flags & FD_VARIADIC) ? 0 : CF_FIXARGC; } else { Error ("Illegal function type %04lX", T->C); @@ -254,7 +255,7 @@ static unsigned typeadjust (ExprDesc* lhs, const ExprDesc* rhs, int NoPush) const Type* rhst = rhs->Type; /* Generate type adjustment code if needed */ - ltype = TypeOf (lhst); + ltype = CG_TypeOf (lhst); if (ED_IsConstAbsInt (lhs) && ltype == CF_INT && lhs->IVal >= 0 && lhs->IVal < 256) { /* If the lhs is a int constant that fits in an unsigned char, use unsigned char. ** g_typeadjust will either promote this to int or unsigned int as appropriate @@ -269,7 +270,7 @@ static unsigned typeadjust (ExprDesc* lhs, const ExprDesc* rhs, int NoPush) /* Value is in primary register*/ ltype |= CF_PRIMARY; } - rtype = TypeOf (rhst); + rtype = CG_TypeOf (rhst); if (ED_IsConstAbsInt (rhs) && rtype == CF_INT && rhs->IVal >= 0 && rhs->IVal < 256) { rtype = CF_CHAR | CF_UNSIGNED; } @@ -290,7 +291,7 @@ static unsigned typeadjust (ExprDesc* lhs, const ExprDesc* rhs, int NoPush) void LimitExprValue (ExprDesc* Expr, int WarnOverflow) /* Limit the constant value of the expression to the range of its type */ { - switch (GetUnqualTypeCode (Expr->Type)) { + switch (GetUnderlyingTypeCode (Expr->Type)) { case T_INT: case T_SHORT: if (WarnOverflow && ((Expr->IVal < -0x8000) || (Expr->IVal > 0x7FFF))) { @@ -493,7 +494,7 @@ static void DoInc (ExprDesc* Expr, unsigned KeepResult) } /* Get the flags */ - Flags = TypeOf (Expr->Type) | GlobalModeFlags (Expr) | CF_FORCECHAR | CF_CONST; + Flags = CG_TypeOf (Expr->Type) | CG_AddrModeFlags (Expr) | CF_FORCECHAR | CF_CONST; if (KeepResult != OA_NEED_NEW) { /* No need to get the result */ Flags |= CF_NOKEEP; @@ -580,7 +581,7 @@ static void DoDec (ExprDesc* Expr, unsigned KeepResult) } /* Get the flags */ - Flags = TypeOf (Expr->Type) | GlobalModeFlags (Expr) | CF_FORCECHAR | CF_CONST; + Flags = CG_TypeOf (Expr->Type) | CG_AddrModeFlags (Expr) | CF_FORCECHAR | CF_CONST; if (KeepResult != OA_NEED_NEW) { /* No need to get the result */ Flags |= CF_NOKEEP; @@ -677,6 +678,7 @@ void DoDeferred (unsigned Flags, ExprDesc* Expr) int I; unsigned Size = 0; int Count = GetDeferredOpCount (); + unsigned StmtFlags = GetSQPFlags (); /* Nothing to be done */ if (Count <= 0) { @@ -684,26 +686,38 @@ void DoDeferred (unsigned Flags, ExprDesc* Expr) } /* Backup some regs/processor flags around the inc/dec */ - if ((Flags & SQP_KEEP_TEST) != 0 && ED_NeedsTest (Expr)) { + if ((StmtFlags & SQP_KEEP_TEST) != 0 || + ((Flags & SQP_KEEP_TEST) != 0 && ED_NeedsTest (Expr))) { /* Sufficient to add a pair of PHP/PLP for all cases */ AddCodeLine ("php"); } - /* Backup the content of EAX around the inc/dec */ - if ((Flags & SQP_KEEP_EAX) != 0 && ED_NeedsPrimary (Expr)) { - /* Get the size */ - Size = CheckedSizeOf (Expr->Type); + if ((Flags & SQP_MASK_EAX) != 0 && ED_NeedsPrimary (Expr)) { + Size = SizeOf (Expr->Type); + } - if (Size < 2) { - AddCodeLine ("pha"); - } else if (Size < 3) { - AddCodeLine ("sta regsave"); - AddCodeLine ("stx regsave+1"); - } else { - AddCodeLine ("jsr saveeax"); + /* Get the size of the backup */ + if ((StmtFlags & SQP_MASK_EAX) != 0) { + switch (StmtFlags & SQP_MASK_EAX) { + case SQP_KEEP_A: if (Size < 1) Size = 1; break; + case SQP_KEEP_AX: if (Size < 2) Size = 2; break; + case SQP_KEEP_EAX: if (Size < 4) Size = 4; break; + default: ; } } + /* Backup the content of EAX around the inc/dec */ + if (Size == 1) { + AddCodeLine ("pha"); + } else if (Size == 2) { + AddCodeLine ("sta regsave"); + AddCodeLine ("stx regsave+1"); + } else if (Size == 3 || Size == 4) { + AddCodeLine("jsr saveeax"); + } else if (Size > 4) { + Error ("Unsupported deferred operand size: %u", Size); + } + for (I = 0; I < Count; ++I) { DeferredOp* Op = CollAtUnchecked (&DeferredOps, I); switch (Op->OpType) { @@ -721,19 +735,18 @@ void DoDeferred (unsigned Flags, ExprDesc* Expr) CollDeleteAll (&DeferredOps); /* Restore the content of EAX around the inc/dec */ - if ((Flags & SQP_KEEP_EAX) != 0 && ED_NeedsPrimary (Expr)) { - if (Size < 2) { - AddCodeLine ("pla"); - } else if (Size < 3) { - AddCodeLine ("lda regsave"); - AddCodeLine ("ldx regsave+1"); - } else { - AddCodeLine ("jsr resteax"); - } + if (Size == 1) { + AddCodeLine ("pla"); + } else if (Size == 2) { + AddCodeLine ("lda regsave"); + AddCodeLine ("ldx regsave+1"); + } else if (Size == 3 || Size == 4) { + AddCodeLine ("jsr resteax"); } /* Restore the regs/processor flags around the inc/dec */ - if ((Flags & SQP_KEEP_TEST) != 0 && ED_NeedsTest (Expr)) { + if ((StmtFlags & SQP_KEEP_TEST) != 0 || + ((Flags & SQP_KEEP_TEST) != 0 && ED_NeedsTest (Expr))) { /* Sufficient to pop the processor flags */ AddCodeLine ("plp"); } @@ -873,7 +886,7 @@ static unsigned FunctionArgList (FuncDesc* Func, int IsFastcall, ExprDesc* ED) /* Handle struct/union specially */ if (IsClassStruct (Expr.Type)) { /* Use the replacement type */ - Flags |= TypeOf (GetStructReplacementType (Expr.Type)); + Flags |= CG_TypeOf (GetStructReplacementType (Expr.Type)); /* Load the value into the primary if it is not already there */ LoadExpr (Flags, &Expr); @@ -882,7 +895,7 @@ static unsigned FunctionArgList (FuncDesc* Func, int IsFastcall, ExprDesc* ED) LoadExpr (CF_NONE, &Expr); /* Use the type of the argument for the push */ - Flags |= TypeOf (Expr.Type); + Flags |= CG_TypeOf (Expr.Type); } /* If this is a fastcall function, don't push the last argument */ @@ -1035,16 +1048,15 @@ static void FunctionCall (ExprDesc* Expr) /* Parse the argument list and pass them to the called function */ ArgSize = FunctionArgList (Func, IsFastcall, Expr); + if (ArgSize > 0xFF && (Func->Flags & FD_VARIADIC) != 0) { + Error ("Total size of all arguments passed to a variadic function cannot exceed 255 bytes"); + } + /* We need the closing paren here */ ConsumeRParen (); /* Special handling for function pointers */ if (IsFuncPtr) { - - if (Func->WrappedCall) { - Warning ("Calling a wrapped function via a pointer, wrapped-call will not be used"); - } - /* If the function is not a fastcall function, load the pointer to ** the function into the primary. */ @@ -1069,7 +1081,7 @@ static void FunctionCall (ExprDesc* Expr) } /* Call the function */ - g_callind (FuncTypeOf (Expr->Type+1), ArgSize, PtrOffs); + g_callind (CG_CallFlags (Expr->Type+1), ArgSize, PtrOffs); } else { @@ -1093,18 +1105,18 @@ static void FunctionCall (ExprDesc* Expr) } else { /* Normal function */ - if (Func->WrappedCall) { + if (Expr->Sym && Expr->Sym->V.F.WrappedCall) { char tmp[64]; StrBuf S = AUTO_STRBUF_INITIALIZER; - if (Func->WrappedCallData == WRAPPED_CALL_USE_BANK) { + if (Expr->Sym->V.F.WrappedCallData == WRAPPED_CALL_USE_BANK) { /* Store the bank attribute in tmp4 */ SB_AppendStr (&S, "ldy #<.bank(_"); SB_AppendStr (&S, (const char*) Expr->Name); SB_AppendChar (&S, ')'); } else { /* Store the WrappedCall data in tmp4 */ - sprintf(tmp, "ldy #%u", Func->WrappedCallData); + sprintf(tmp, "ldy #%u", Expr->Sym->V.F.WrappedCallData); SB_AppendStr (&S, tmp); } g_asmcode (&S); @@ -1137,9 +1149,9 @@ static void FunctionCall (ExprDesc* Expr) SB_Done (&S); - g_call (FuncTypeOf (Expr->Type), Func->WrappedCall->Name, ArgSize); + g_call (CG_CallFlags (Expr->Type), Expr->Sym->V.F.WrappedCall->Name, ArgSize); } else { - g_call (FuncTypeOf (Expr->Type), (const char*) Expr->Name, ArgSize); + g_call (CG_CallFlags (Expr->Type), (const char*) Expr->Name, ArgSize); } } @@ -1207,20 +1219,22 @@ static void Primary (ExprDesc* E) /* Is the symbol known? */ if (Sym) { - /* We found the symbol - skip the name token */ - NextToken (); - /* Check for illegal symbol types */ - CHECK ((Sym->Flags & SC_LABEL) != SC_LABEL); - if (Sym->Flags & SC_ESUTYPEMASK) { + CHECK ((Sym->Flags & SC_TYPEMASK) != SC_LABEL); + if ((Sym->Flags & SC_TYPEMASK) == SC_TYPEDEF) { /* Cannot use type symbols */ Error ("Variable identifier expected"); /* Assume an int type to make E valid */ E->Flags = E_LOC_STACK | E_RTYPE_LVAL; E->Type = type_int; + /* Skip the erroneous token */ + NextToken (); break; } + /* Skip the name token */ + NextToken (); + /* Mark the symbol as referenced */ Sym->Flags |= SC_REF; @@ -1232,7 +1246,7 @@ static void Primary (ExprDesc* E) /* Enum or some other numeric constant */ E->Flags = E_LOC_NONE | E_RTYPE_RVAL; E->IVal = Sym->V.ConstVal; - } else if ((Sym->Flags & SC_AUTO) == SC_AUTO) { + } else if ((Sym->Flags & SC_STORAGEMASK) == SC_AUTO) { /* Local variable. If this is a parameter for a variadic ** function, we have to add some address calculations, and the ** address is not const. @@ -1246,26 +1260,25 @@ static void Primary (ExprDesc* E) E->Flags = E_LOC_STACK | E_RTYPE_LVAL; E->IVal = Sym->V.Offs; } - } else if ((Sym->Flags & SC_FUNC) == SC_FUNC) { + } else if ((Sym->Flags & SC_TYPEMASK) == SC_FUNC) { /* Function */ E->Flags = E_LOC_GLOBAL | E_RTYPE_LVAL; E->Name = (uintptr_t) Sym->Name; - } else if ((Sym->Flags & SC_REGISTER) == SC_REGISTER) { + } else if ((Sym->Flags & SC_STORAGEMASK) == SC_REGISTER) { /* Register variable, zero page based */ E->Flags = E_LOC_REGISTER | E_RTYPE_LVAL; E->Name = Sym->V.R.RegOffs; - } else if ((Sym->Flags & SC_STATIC) == SC_STATIC) { - /* Static variable */ - if (Sym->Flags & (SC_EXTERN | SC_STORAGE | SC_DECL)) { - E->Flags = E_LOC_GLOBAL | E_RTYPE_LVAL; - E->Name = (uintptr_t) Sym->Name; - } else { - E->Flags = E_LOC_STATIC | E_RTYPE_LVAL; - E->Name = Sym->V.L.Label; - } - } else { + } else if (SymIsGlobal (Sym)) { + /* Global variable */ + E->Flags = E_LOC_GLOBAL | E_RTYPE_LVAL; + E->Name = (uintptr_t) Sym->Name; + } else if ((Sym->Flags & SC_STORAGEMASK) == SC_STATIC) { /* Local static variable */ E->Flags = E_LOC_STATIC | E_RTYPE_LVAL; + E->Name = Sym->V.L.Label; + } else { + /* Other */ + E->Flags = E_LOC_STATIC | E_RTYPE_LVAL; E->Name = Sym->V.Offs; } @@ -1275,7 +1288,23 @@ static void Primary (ExprDesc* E) ** rvalue, too, because we cannot store anything in a function. ** So fix the flags depending on the type. */ - if (IsTypeArray (E->Type) || IsTypeFunc (E->Type)) { + if (IsTypeArray (E->Type)) { + ED_AddrExpr (E); + } else if (IsTypeFunc (E->Type)) { + /* In cc65 mode we cannot call or take the address of + ** main(). + */ + if (IS_Get (&Standard) == STD_CC65 && + strcmp (Sym->Name, "main") == 0) { + /* Adjust the error message depending on a call or an + ** address operation. + */ + if (CurTok.Tok == TOK_LPAREN) { + Error ("'main' must not be called recursively"); + } else { + Error ("The address of 'main' cannot be taken"); + } + } ED_AddrExpr (E); } @@ -1299,18 +1328,19 @@ static void Primary (ExprDesc* E) } else { Warning ("Call to undeclared function '%s'", Ident); } - Sym = AddGlobalSym (Ident, GetImplicitFuncType(), SC_EXTERN | SC_REF | SC_FUNC); + Sym = AddGlobalSym (Ident, GetImplicitFuncType(), SC_REF | SC_FUNC); E->Type = Sym->Type; E->Flags = E_LOC_GLOBAL | E_RTYPE_RVAL; E->Name = (uintptr_t) Sym->Name; } else { /* Undeclared Variable */ + Error ("Undeclared identifier '%s'", Ident); Sym = AddLocalSym (Ident, type_int, SC_AUTO | SC_REF, 0); E->Flags = E_LOC_STACK | E_RTYPE_LVAL; E->Type = type_int; - Error ("Undefined symbol: '%s'", Ident); } + E->Sym = Sym; } break; @@ -1319,8 +1349,6 @@ static void Primary (ExprDesc* E) /* String literal */ if ((Flags & E_EVAL_UNEVAL) != E_EVAL_UNEVAL) { E->V.LVal = UseLiteral (CurTok.SVal); - /* Translate into target charset */ - TranslateLiteral (E->V.LVal); } else { E->V.LVal = CurTok.SVal; } @@ -1358,6 +1386,7 @@ static void Primary (ExprDesc* E) case TOK_A: /* Register pseudo variable */ + SetSQPFlags (SQP_KEEP_A); E->Type = type_uchar; E->Flags = E_LOC_PRIMARY | E_RTYPE_LVAL; NextToken (); @@ -1365,6 +1394,7 @@ static void Primary (ExprDesc* E) case TOK_AX: /* Register pseudo variable */ + SetSQPFlags (SQP_KEEP_AX); E->Type = type_uint; E->Flags = E_LOC_PRIMARY | E_RTYPE_LVAL; NextToken (); @@ -1372,6 +1402,7 @@ static void Primary (ExprDesc* E) case TOK_EAX: /* Register pseudo variable */ + SetSQPFlags (SQP_KEEP_EAX); E->Type = type_ulong; E->Flags = E_LOC_PRIMARY | E_RTYPE_LVAL; NextToken (); @@ -1393,28 +1424,12 @@ static void Primary (ExprDesc* E) break; } else { /* Let's see if this is a C99-style declaration */ - DeclSpec Spec; - InitDeclSpec (&Spec); - ParseDeclSpec (&Spec, TS_DEFAULT_TYPE_INT, SC_AUTO); - - if (Spec.Type->C != T_END) { + DeclSpec Spec; + ParseDeclSpec (&Spec, TS_DEFAULT_TYPE_NONE | TS_FUNCTION_SPEC, SC_AUTO); + if ((Spec.Flags & DS_TYPE_MASK) != DS_NONE) { Error ("Mixed declarations and code are not supported in cc65"); - while (CurTok.Tok != TOK_SEMI) { - Declarator Decl; - - /* Parse one declaration */ - ParseDecl (&Spec, &Decl, DM_ACCEPT_IDENT); - if (CurTok.Tok == TOK_ASSIGN) { - NextToken (); - ParseInit (Decl.Type); - } - if (CurTok.Tok == TOK_COMMA) { - NextToken (); - } else { - break; - } - } + SmartErrorSkip (0); } else { Error ("Expression expected"); E->Flags |= E_EVAL_MAYBE_UNUSED; @@ -1439,7 +1454,7 @@ static void StructRef (ExprDesc* Expr) /* Skip the token and check for an identifier */ NextToken (); if (CurTok.Tok != TOK_IDENT) { - Error ("Identifier expected"); + Error ("Identifier expected for %s member", GetBasicTypeName (Expr->Type)); /* Make the expression an integer at address zero */ ED_MakeConstAbs (Expr, 0, type_int); return; @@ -1601,9 +1616,9 @@ static void hie11 (ExprDesc *Expr) break; case TOK_LPAREN: - /* Function call. */ - if (!IsTypeFunc (Expr->Type) && !IsTypeFuncPtr (Expr->Type)) { - /* Not a function */ + /* Function call */ + if (!IsTypeFuncLike (Expr->Type)) { + /* Not a function or function pointer */ Error ("Illegal function call"); /* Force the type to be a implicitly defined function, one ** returning an int and taking any number of arguments. @@ -1665,7 +1680,7 @@ void Store (ExprDesc* Expr, const Type* StoreType) } /* Prepare the code generator flags */ - Flags = TypeOf (StoreType) | GlobalModeFlags (Expr); + Flags = CG_TypeOf (StoreType) | CG_AddrModeFlags (Expr); /* Do the store depending on the location */ switch (ED_GetLoc (Expr)) { @@ -1793,7 +1808,7 @@ static void PostInc (ExprDesc* Expr) } /* Get the data type */ - Flags = TypeOf (Expr->Type); + Flags = CG_TypeOf (Expr->Type); /* We are allowed by the C standard to defer the inc operation until after ** the expression is used, so that we don't need to save and reload @@ -1854,7 +1869,7 @@ static void PostDec (ExprDesc* Expr) } /* Get the data type */ - Flags = TypeOf (Expr->Type); + Flags = CG_TypeOf (Expr->Type); /* Emit smaller code if a char variable is at a constant location */ if ((Flags & CF_TYPEMASK) == CF_CHAR && ED_IsLocConst (Expr) && !IsTypeBitField (Expr->Type)) { @@ -1901,31 +1916,46 @@ static void UnaryOp (ExprDesc* Expr) /* Get the expression */ hie10 (Expr); - /* We can only handle integer types */ - if (!IsClassInt (Expr->Type)) { - Error ("Argument must have integer type"); - ED_MakeConstAbsInt (Expr, 1); - } - /* Check for a constant numeric expression */ if (ED_IsConstAbs (Expr)) { - /* Value is numeric */ - switch (Tok) { - case TOK_MINUS: Expr->IVal = -Expr->IVal; break; - case TOK_PLUS: break; - case TOK_COMP: Expr->IVal = ~Expr->IVal; break; - default: Internal ("Unexpected token: %d", Tok); + + if (IsClassFloat (Expr->Type)) { + switch (Tok) { + case TOK_MINUS: Expr->V.FVal = FP_D_Sub(FP_D_Make(0.0),Expr->V.FVal); break; + case TOK_PLUS: break; + case TOK_COMP: Error ("Unary ~ operator not valid for floating point constant"); break; + default: Internal ("Unexpected token: %d", Tok); + } + } else { + if (!IsClassInt (Expr->Type)) { + Error ("Constant argument must have integer or float type"); + ED_MakeConstAbsInt (Expr, 1); + } + + /* Value is numeric */ + switch (Tok) { + case TOK_MINUS: Expr->IVal = -Expr->IVal; break; + case TOK_PLUS: break; + case TOK_COMP: Expr->IVal = ~Expr->IVal; break; + default: Internal ("Unexpected token: %d", Tok); + } + + /* Adjust the type of the expression */ + Expr->Type = IntPromotion (Expr->Type); + + /* Limit the calculated value to the range of its type */ + LimitExprValue (Expr, 1); } - /* Adjust the type of the expression */ - Expr->Type = IntPromotion (Expr->Type); - - /* Limit the calculated value to the range of its type */ - LimitExprValue (Expr, 1); - } else { unsigned Flags; + /* If not constant, we can only handle integer types */ + if (!IsClassInt (Expr->Type)) { + Error ("Non-constant argument must have integer type"); + ED_MakeConstAbsInt (Expr, 1); + } + /* Value is not constant */ LoadExpr (CF_NONE, Expr); @@ -1934,7 +1964,7 @@ static void UnaryOp (ExprDesc* Expr) TypeConversion (Expr, Expr->Type); /* Get code generation flags */ - Flags = TypeOf (Expr->Type); + Flags = CG_TypeOf (Expr->Type); /* Handle the operation */ switch (Tok) { @@ -1978,13 +2008,13 @@ void hie10 (ExprDesc* Expr) if (ED_IsConstAbs (Expr)) { /* Constant numeric expression */ Expr->IVal = !Expr->IVal; - } else if (ED_IsAddrExpr (Expr)) { + } else if (ED_IsEntityAddr (Expr)) { /* Address != NULL, so !Address == 0 */ ED_MakeConstBool (Expr, 0); } else { /* Not constant, load into the primary */ LoadExpr (CF_NONE, Expr); - g_bneg (TypeOf (Expr->Type)); + g_bneg (CG_TypeOf (Expr->Type)); ED_FinalizeRValLoad (Expr); ED_TestDone (Expr); /* bneg will set cc */ } @@ -2002,7 +2032,7 @@ void hie10 (ExprDesc* Expr) ** of dereference operators is legal, since the result will ** always be converted to "pointer to function". */ - if (IsTypeFuncPtr (Expr->Type) || IsTypeFunc (Expr->Type)) { + if (IsTypeFuncLike (Expr->Type)) { /* Expression not storable */ ED_MarkExprAsRVal (Expr); } else { @@ -2056,9 +2086,7 @@ void hie10 (ExprDesc* Expr) NextToken (); if (TypeSpecAhead ()) { Type T[MAXTYPELEN]; - NextToken (); Size = ExprCheckedSizeOf (ParseType (T)); - ConsumeRParen (); } else { /* Remember the output queue pointer */ CodeMark Mark; @@ -2141,7 +2169,7 @@ static void hie_internal (const GenDesc* Ops, /* List of generators */ /* Get the lhs on stack */ GetCodePos (&Mark1); - ltype = TypeOf (Expr->Type); + ltype = CG_TypeOf (Expr->Type); lconst = ED_IsConstAbs (Expr); if (lconst) { /* Constant value */ @@ -2255,7 +2283,7 @@ static void hie_internal (const GenDesc* Ops, /* List of generators */ ** operation because this allows for better code. */ unsigned rtype = ltype | CF_CONST; - ltype = TypeOf (Expr2.Type); /* Expr2 is now left */ + ltype = CG_TypeOf (Expr2.Type); /* Expr2 is now left */ type = CF_CONST; if ((Gen->Flags & GEN_NOPUSH) == 0) { g_push (ltype, 0); @@ -2279,7 +2307,7 @@ static void hie_internal (const GenDesc* Ops, /* List of generators */ ** expects the lhs in the primary, remove the push of the primary ** now. */ - unsigned rtype = TypeOf (Expr2.Type); + unsigned rtype = CG_TypeOf (Expr2.Type); type = 0; if (rconst) { /* As above, but for the RHS. */ @@ -2355,7 +2383,7 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */ /* Get the lhs on stack */ GetCodePos (&Mark1); - ltype = TypeOf (Expr->Type); + ltype = CG_TypeOf (Expr->Type); if (ED_IsConstAbs (Expr)) { /* Numeric constant value */ GetCodePos (&Mark2); @@ -2434,8 +2462,8 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */ } /* Check for numeric constant operands */ - if ((ED_IsAddrExpr (Expr) && ED_IsNullPtr (&Expr2)) || - (ED_IsNullPtr (Expr) && ED_IsAddrExpr (&Expr2))) { + if ((ED_IsEntityAddr (Expr) && ED_IsNullPtr (&Expr2)) || + (ED_IsNullPtr (Expr) && ED_IsEntityAddr (&Expr2))) { /* Object addresses are inequal to null pointer */ Expr->IVal = (Tok != TOK_EQ); @@ -2466,8 +2494,8 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */ pop (ltype); } - } else if (ED_IsAddrExpr (Expr) && - ED_IsAddrExpr (&Expr2) && + } else if (ED_IsEntityAddr (Expr) && + ED_IsEntityAddr (&Expr2) && Expr->Sym == Expr2.Sym) { /* Evaluate the result for static addresses */ @@ -2667,7 +2695,7 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */ } } else { - unsigned rtype = TypeOf (Expr2.Type) | (flags & CF_CONST); + unsigned rtype = CG_TypeOf (Expr2.Type) | (flags & CF_CONST); flags |= g_typeadjust (ltype, rtype); } @@ -2887,7 +2915,7 @@ static void parseadd (ExprDesc* Expr, int DoArrayRef) g_addaddr_local (flags, Expr->IVal); } else { /* Static address */ - g_addaddr_static (flags | GlobalModeFlags (Expr), Expr->Name, Expr->IVal); + g_addaddr_static (flags | CG_AddrModeFlags (Expr), Expr->Name, Expr->IVal); } } else { /* Lhs is not numeric. Load it. */ @@ -2905,7 +2933,7 @@ static void parseadd (ExprDesc* Expr, int DoArrayRef) g_addaddr_local (flags, Expr2.IVal); } else { /* Static address */ - g_addaddr_static (flags | GlobalModeFlags (&Expr2), Expr2.Name, Expr2.IVal); + g_addaddr_static (flags | CG_AddrModeFlags (&Expr2), Expr2.Name, Expr2.IVal); } } @@ -2932,7 +2960,7 @@ static void parseadd (ExprDesc* Expr, int DoArrayRef) flags |= CF_CONST; } else { /* Constant address label */ - flags |= GlobalModeFlags (Expr); + flags |= CG_AddrModeFlags (Expr); } /* Check for pointer arithmetic */ @@ -2968,15 +2996,16 @@ static void parseadd (ExprDesc* Expr, int DoArrayRef) } if (!AddDone) { - if (ED_IsLocQuasiConst (&Expr2) && - rscale == 1 && + if (ED_IsLocQuasiConst (&Expr2) && + !IsTypeBitField (Expr2.Type) && + rscale == 1 && CheckedSizeOf (rhst) == SIZEOF_CHAR) { /* Change the order back */ RemoveCode (&Mark); /* Load lhs */ LoadExpr (CF_NONE, Expr); /* Use new flags */ - flags = CF_CHAR | GlobalModeFlags (&Expr2); + flags = CF_CHAR | CG_AddrModeFlags (&Expr2); /* Add the variable */ if (ED_IsLocStack (&Expr2)) { g_addlocal (flags, Expr2.IVal); @@ -2998,7 +3027,7 @@ static void parseadd (ExprDesc* Expr, int DoArrayRef) ** not a numeric constant, and the scale factor is not one ** (no scaling), we must take the long way over the stack. */ - g_push (TypeOf (Expr2.Type), 0); /* rhs --> stack */ + g_push (CG_TypeOf (Expr2.Type), 0); /* rhs --> stack */ LoadExpr (CF_NONE, Expr); g_scale (CF_PTR, lscale); g_add (CF_PTR, 0); @@ -3014,7 +3043,7 @@ static void parseadd (ExprDesc* Expr, int DoArrayRef) /* Left hand side is not constant. Get the value onto the stack. */ LoadExpr (CF_NONE, Expr); /* --> primary register */ GetCodePos (&Mark); - flags = TypeOf (Expr->Type); /* default codegen type */ + flags = CG_TypeOf (Expr->Type); /* default codegen type */ g_push (flags, 0); /* --> stack */ /* Evaluate the rhs */ @@ -3078,7 +3107,7 @@ static void parseadd (ExprDesc* Expr, int DoArrayRef) g_push (CF_PTR, 0); /* --> stack */ LoadExpr (CF_NONE, &Expr2); /* Load rhs into primary register */ } else { - g_tosint (TypeOf (lhst)); /* Make sure TOS is int */ + g_tosint (CG_TypeOf (lhst)); /* Make sure TOS is int */ LoadExpr (CF_NONE, &Expr2); /* Load rhs into primary register */ if (lscale != 1) { g_swap (CF_INT); /* Swap TOS and primary */ @@ -3182,7 +3211,7 @@ static void parsesub (ExprDesc* Expr) Expr2.Flags |= Expr->Flags & E_MASK_KEEP_SUBEXPR; /* lhs cannot be function or pointer to function */ - if (IsTypeFunc (Expr->Type) || IsTypeFuncPtr (Expr->Type)) { + if (IsTypeFuncLike (Expr->Type)) { Error ("Invalid left operand for binary operator '-'"); /* Make it pointer to char to avoid further errors */ Expr->Type = type_uchar; @@ -3199,15 +3228,15 @@ static void parsesub (ExprDesc* Expr) /* Remember the output queue position, then bring the value onto the stack */ GetCodePos (&Mark1); - LoadExpr (CF_NONE, Expr); /* --> primary register */ + LoadExpr (CF_NONE, Expr); /* --> primary register */ GetCodePos (&Mark2); - g_push (TypeOf (lhst), 0); /* --> stack */ + g_push (CG_TypeOf (lhst), 0); /* --> stack */ /* Parse the right hand side */ MarkedExprWithCheck (hie9, &Expr2); /* rhs cannot be function or pointer to function */ - if (IsTypeFunc (Expr2.Type) || IsTypeFuncPtr (Expr2.Type)) { + if (IsTypeFuncLike (Expr2.Type)) { Error ("Invalid right operand for binary operator '-'"); /* Make it pointer to char to avoid further errors */ Expr2.Type = type_uchar; @@ -3261,7 +3290,7 @@ static void parsesub (ExprDesc* Expr) /* The right hand side is constant. Check left hand side. */ if (ED_IsQuasiConst (Expr)) { /* We can't do all 'ptr1 - ptr2' constantly at the moment */ - if (Expr->Sym == Expr2.Sym) { + if (ED_GetLoc (Expr) == ED_GetLoc (&Expr2) && Expr->Sym == Expr2.Sym) { Expr->IVal = (Expr->IVal - Expr2.IVal) / rscale; /* Get rid of unneeded flags etc. */ ED_MakeConstAbsInt (Expr, Expr->IVal); @@ -3358,7 +3387,7 @@ static void parsesub (ExprDesc* Expr) } /* Load rhs into the primary */ LoadExpr (CF_NONE, &Expr2); - g_scale (TypeOf (rhst), rscale); + g_scale (CG_TypeOf (rhst), rscale); /* Generate code for the sub (the & is a hack here) */ g_sub (flags & ~CF_CONST, 0); } @@ -3395,7 +3424,7 @@ static void parsesub (ExprDesc* Expr) } /* Load rhs into the primary */ LoadExpr (CF_NONE, &Expr2); - g_scale (TypeOf (rhst), rscale); + g_scale (CG_TypeOf (rhst), rscale); /* Generate code for the sub (the & is a hack here) */ g_sub (flags & ~CF_CONST, 0); } @@ -3932,10 +3961,10 @@ static void hieQuest (ExprDesc* Expr) NextToken (); /* Parse second expression. Remember for later if it is a NULL pointer - ** expression, then load it into the primary. + ** constant expression, then load it into the primary. */ ExprWithCheck (hie0, &Expr2); - Expr2IsNULL = ED_IsNullPtr (&Expr2); + Expr2IsNULL = ED_IsNullPtrConstant (&Expr2); if (!IsTypeVoid (Expr2.Type) && ED_YetToLoad (&Expr2) && (!ConstantCond || !ED_IsConst (&Expr2))) { @@ -3979,10 +4008,10 @@ static void hieQuest (ExprDesc* Expr) ConsumeColon (); /* Parse third expression. Remember for later if it is a NULL pointer - ** expression, then load it into the primary. + ** constant expression, then load it into the primary. */ ExprWithCheck (hieQuest, &Expr3); - Expr3IsNULL = ED_IsNullPtr (&Expr3); + Expr3IsNULL = ED_IsNullPtrConstant (&Expr3); if (!IsTypeVoid (Expr3.Type) && ED_YetToLoad (&Expr3) && (!ConstantCond || !ED_IsConst (&Expr3))) { @@ -4068,9 +4097,10 @@ static void hieQuest (ExprDesc* Expr) /* Avoid further errors */ ResultType = NewPointerTo (type_void); } else { - /* Result has the composite type */ + /* Result has the properly qualified composite type */ ResultType = TypeDup (Expr2.Type); TypeComposition (ResultType, Expr3.Type); + ResultType[1].C |= GetQualifier (Indirect (Expr3.Type)); } } } else if (IsClassPtr (Expr2.Type) && Expr3IsNULL) { @@ -4292,8 +4322,13 @@ ExprDesc NoCodeConstExpr (void (*Func) (ExprDesc*)) if (!ED_IsConst (&Expr) || !ED_CodeRangeIsEmpty (&Expr)) { Error ("Constant expression expected"); /* To avoid any compiler errors, make the expression a valid const */ - Expr.Flags &= E_MASK_RTYPE | E_MASK_KEEP_RESULT; + Expr.Flags &= E_MASK_RTYPE | E_MASK_KEEP_MAKE; Expr.Flags |= E_LOC_NONE; + + /* Remove any non-constant code generated */ + if (!ED_CodeRangeIsEmpty (&Expr)) { + RemoveCodeRange (&Expr.Start, &Expr.End); + } } /* Return by value */ @@ -4318,6 +4353,11 @@ ExprDesc NoCodeConstAbsIntExpr (void (*Func) (ExprDesc*)) Error ("Constant integer expression expected"); /* To avoid any compiler errors, make the expression a valid const */ ED_MakeConstAbsInt (&Expr, 1); + + /* Remove any non-constant code generated */ + if (!ED_CodeRangeIsEmpty (&Expr)) { + RemoveCodeRange (&Expr.Start, &Expr.End); + } } /* Return by value */ diff --git a/src/cc65/expr.h b/src/cc65/expr.h index 5644fb82d..5e0b4e845 100644 --- a/src/cc65/expr.h +++ b/src/cc65/expr.h @@ -23,11 +23,6 @@ -#define SQP_KEEP_NONE 0x00 -#define SQP_KEEP_TEST 0x01U -#define SQP_KEEP_EAX 0x02U -#define SQP_KEEP_EXPR 0x03U /* SQP_KEEP_TEST | SQP_KEEP_EAX */ - /* Generator attributes */ #define GEN_NOPUSH 0x01 /* Don't push lhs */ #define GEN_COMM 0x02 /* Operator is commutative */ @@ -48,14 +43,14 @@ typedef struct GenDesc { -unsigned GlobalModeFlags (const ExprDesc* Expr); +unsigned CG_AddrModeFlags (const ExprDesc* Expr); /* Return the addressing mode flags for the given expression */ -unsigned TypeOf (const Type* T); +unsigned CG_TypeOf (const Type* T); /* Get the code generator base type of the object */ -unsigned FuncTypeOf (const Type* T); -/* Get the code generator flag for calling the function */ +unsigned CG_CallFlags (const Type* T); +/* Get the code generator flags for calling the function */ void ExprWithCheck (void (*Func) (ExprDesc*), ExprDesc* Expr); /* Call an expression function with checks. */ diff --git a/src/cc65/exprdesc.c b/src/cc65/exprdesc.c index 5924ab6cf..08be1091d 100644 --- a/src/cc65/exprdesc.c +++ b/src/cc65/exprdesc.c @@ -167,6 +167,17 @@ int ED_IsLocQuasiConst (const ExprDesc* Expr) +int ED_IsLocZP (const ExprDesc* Expr) +/* Return true if the expression is in a location on a zeropage */ +{ + return ED_IsLocRegister (Expr) || + (ED_IsLocConst (Expr) && + Expr->Sym != 0 && + (Expr->Sym->Flags & SC_ZEROPAGE) != 0); +} + + + #if !defined(HAVE_INLINE) int ED_IsLocPrimaryOrExpr (const ExprDesc* Expr) /* Return true if the expression is E_LOC_PRIMARY or E_LOC_EXPR */ @@ -244,7 +255,7 @@ int ED_IsConstTrue (const ExprDesc* Expr) { /* Non-zero arithmetics and objects addresses are boolean true */ return (ED_IsConstAbsInt (Expr) && Expr->IVal != 0) || - (ED_IsAddrExpr (Expr)); + ED_IsEntityAddr (Expr); } @@ -302,13 +313,59 @@ int ED_IsQuasiConstAddr (const ExprDesc* Expr) -int ED_IsNullPtr (const ExprDesc* Expr) -/* Return true if the given expression is a NULL pointer constant */ + +int ED_IsStackAddr (const ExprDesc* Expr) +/* Return true if the expression denotes a fixed address on stack */ { - return (Expr->Flags & (E_MASK_LOC|E_MASK_RTYPE)) == - (E_LOC_NONE|E_RTYPE_RVAL) && - Expr->IVal == 0 && - IsClassInt (Expr->Type); + return ED_IsAddrExpr (Expr) && ED_IsLocStack (Expr); +} + + + +int ED_IsZPInd (const ExprDesc* Expr) +/* Return true if the expression is located on the zeropage */ +{ + return ED_IsIndExpr (Expr) && ED_IsLocZP (Expr); +} + + + +int ED_IsNullPtr (const ExprDesc* Expr) +/* Return true if the given expression is a null pointer. +** Note: A null pointer constant converted to a pointer type is a null pointer. +*/ +{ + return ED_IsConstAbs (Expr) && + Expr->IVal == 0 && + (IsClassInt (Expr->Type) || IsTypePtr (Expr->Type)); +} + + + +int ED_IsNullPtrConstant (const ExprDesc* Expr) +/* Return true if the given expression is a null pointer constant. +** Note: An integer constant expression with value 0, or such an +** expression cast to void* is a null pointer constant. However, a +** null pointer constant converted to a pointer type is just a null +** pointer, not necessarily a constant in ISO C. +*/ +{ + return ED_IsConstAbs (Expr) && + Expr->IVal == 0 && + (IsClassInt (Expr->Type) || + (IsTypePtr (Expr->Type) && IsTypeVoid (Expr->Type + 1) && + GetQualifier (Expr->Type + 1) == T_QUAL_NONE)); +} + + + +int ED_IsEntityAddr (const ExprDesc* Expr) +/* Return true if the expression denotes the address of an object or function. +*/ +{ + return ED_IsAddrExpr (Expr) && + Expr->Sym != 0 && + (IsClassPtr (Expr->Type) || IsTypeFunc (Expr->Type)); } @@ -390,7 +447,7 @@ ExprDesc* ED_FinalizeRValLoad (ExprDesc* Expr) -ExprDesc* ED_AddrExpr (ExprDesc* Expr) +void ED_AddrExpr (ExprDesc* Expr) /* Take address of Expr. The result is always an rvalue */ { switch (Expr->Flags & E_MASK_LOC) { @@ -419,12 +476,11 @@ ExprDesc* ED_AddrExpr (ExprDesc* Expr) } break; } - return Expr; } -ExprDesc* ED_IndExpr (ExprDesc* Expr) +void ED_IndExpr (ExprDesc* Expr) /* Dereference Expr */ { switch (Expr->Flags & E_MASK_LOC) { @@ -458,7 +514,6 @@ ExprDesc* ED_IndExpr (ExprDesc* Expr) } break; } - return Expr; } diff --git a/src/cc65/exprdesc.h b/src/cc65/exprdesc.h index 2ef8b617f..d1ff99445 100644 --- a/src/cc65/exprdesc.h +++ b/src/cc65/exprdesc.h @@ -98,7 +98,7 @@ enum { E_LOC_NONE = 0x0000, /* Pure rvalue with no storage */ E_LOC_ABS = 0x0001, /* Absolute numeric addressed variable */ E_LOC_GLOBAL = 0x0002, /* Global variable */ - E_LOC_STATIC = 0x0004, /* Static variable */ + E_LOC_STATIC = 0x0004, /* Local static variable */ E_LOC_REGISTER = 0x0008, /* Register variable */ E_LOC_STACK = 0x0010, /* Value on the stack */ E_LOC_PRIMARY = 0x0020, /* Temporary in primary register */ @@ -369,6 +369,9 @@ int ED_IsLocQuasiConst (const ExprDesc* Expr); */ #endif +int ED_IsLocZP (const ExprDesc* Expr); +/* Return true if the expression is in a location on a zeropage */ + #if defined(HAVE_INLINE) INLINE int ED_IsLocPrimaryOrExpr (const ExprDesc* Expr) /* Return true if the expression is E_LOC_PRIMARY or E_LOC_EXPR */ @@ -572,8 +575,28 @@ int ED_IsQuasiConstAddr (const ExprDesc* Expr); ** This can be a constant address or a stack variable address. */ +int ED_IsStackAddr (const ExprDesc* Expr); +/* Return true if the expression denotes a fixed address on stack */ + +int ED_IsZPInd (const ExprDesc* Expr); +/* Return true if the expression is located on the zeropage */ + int ED_IsNullPtr (const ExprDesc* Expr); -/* Return true if the given expression is a NULL pointer constant */ +/* Return true if the given expression is a null pointer. +** Note: A null pointer constant converted to a pointer type is a null pointer. +*/ + +int ED_IsNullPtrConstant (const ExprDesc* Expr); +/* Return true if the given expression is a null pointer constant. +** Note: An integer constant expression with value 0, or such an +** expression cast to void* is a null pointer constant. However, a +** null pointer constant converted to a pointer type is just a null +** pointer, not necessarily a constant in ISO C. +*/ + +int ED_IsEntityAddr (const ExprDesc* Expr); +/* Return true if the expression denotes the address of an object or function. +*/ int ED_IsBool (const ExprDesc* Expr); /* Return true if the expression can be treated as a boolean, that is, it can @@ -628,10 +651,10 @@ INLINE void ED_MarkExprAsRVal (ExprDesc* Expr) # define ED_MarkExprAsRVal(Expr) do { (Expr)->Flags &= ~E_RTYPE_LVAL; } while (0) #endif -ExprDesc* ED_AddrExpr (ExprDesc* Expr); +void ED_AddrExpr (ExprDesc* Expr); /* Take address of Expr */ -ExprDesc* ED_IndExpr (ExprDesc* Expr); +void ED_IndExpr (ExprDesc* Expr); /* Dereference Expr */ #if defined(HAVE_INLINE) diff --git a/src/cc65/funcdesc.c b/src/cc65/funcdesc.c index 2291b35ee..de881167d 100644 --- a/src/cc65/funcdesc.c +++ b/src/cc65/funcdesc.c @@ -61,8 +61,6 @@ FuncDesc* NewFuncDesc (void) F->ParamSize = 0; F->LastParam = 0; F->FuncDef = 0; - F->WrappedCall = 0; - F->WrappedCallData = 0; /* Return the new struct */ return F; diff --git a/src/cc65/funcdesc.h b/src/cc65/funcdesc.h index e065c7602..8d21d3080 100644 --- a/src/cc65/funcdesc.h +++ b/src/cc65/funcdesc.h @@ -70,8 +70,6 @@ struct FuncDesc { unsigned ParamSize; /* Size of the parameters */ struct SymEntry* LastParam; /* Pointer to last parameter */ struct FuncDesc* FuncDef; /* Descriptor used in definition */ - struct SymEntry* WrappedCall; /* Pointer to the WrappedCall */ - unsigned int WrappedCallData; /* The WrappedCall's user data */ }; diff --git a/src/cc65/function.c b/src/cc65/function.c index 38a8f45aa..3123079d3 100644 --- a/src/cc65/function.c +++ b/src/cc65/function.c @@ -450,7 +450,6 @@ void NewFunc (SymEntry* Func, FuncDesc* D) /* Parse argument declarations and function body. */ { int ParamComplete; /* If all paramemters have complete types */ - int C99MainFunc = 0;/* Flag for C99 main function returning int */ SymEntry* Param; const Type* RType; /* Real type used for struct parameters */ const Type* ReturnType; /* Return type */ @@ -466,11 +465,15 @@ void NewFunc (SymEntry* Func, FuncDesc* D) /* Check return type */ ReturnType = F_GetReturnType (CurrentFunc); - if (IsIncompleteESUType (ReturnType)) { + if (!IsTypeArray (ReturnType) && !IsTypeFunc (ReturnType)) { /* There are already diagnostics on returning arrays or functions */ - if (!IsTypeArray (ReturnType) && !IsTypeFunc (ReturnType)) { + if (IsIncompleteESUType (ReturnType)) { Error ("Function has incomplete return type '%s'", GetFullTypeName (ReturnType)); + } else if (IsPassByRefType (ReturnType)) { + /* Handle struct/union specially */ + Error ("Function return type '%s' of size %u is unsupported", + GetFullTypeName (ReturnType), SizeOf (ReturnType)); } } @@ -509,18 +512,6 @@ void NewFunc (SymEntry* Func, FuncDesc* D) /* Mark this as the main function */ CurrentFunc->Flags |= FF_IS_MAIN; - /* Main cannot be a fastcall function */ - if (IsQualFastcall (Func->Type)) { - Error ("'main' cannot be declared as __fastcall__"); - } - - /* If cc65 extensions aren't enabled, don't allow a main function that - ** doesn't return an int. - */ - if (IS_Get (&Standard) != STD_CC65 && ReturnType[0].C != T_INT) { - Error ("'main' must always return an int"); - } - /* Add a forced import of a symbol that is contained in the startup ** code. This will force the startup code to be linked in. */ @@ -536,18 +527,10 @@ void NewFunc (SymEntry* Func, FuncDesc* D) /* The start-up code doesn't fast-call main(). */ Func->Type->C |= T_QUAL_CDECL; } - - /* Determine if this is a main function in a C99 environment that - ** returns an int. - */ - if (GetUnqualRawTypeCode (ReturnType) == T_INT && - IS_Get (&Standard) == STD_C99) { - C99MainFunc = 1; - } } /* Allocate code and data segments for this function */ - Func->V.F.Seg = PushSegments (Func); + Func->V.F.Seg = PushSegContext (Func); /* Use the info in the segments for generating new local labels */ UseLabelPoolFromSegments (Func->V.F.Seg); @@ -565,15 +548,15 @@ void NewFunc (SymEntry* Func, FuncDesc* D) /* Generate the push */ /* Handle struct/union specially */ if (IsClassStruct (D->LastParam->Type)) { - Flags = TypeOf (GetStructReplacementType (D->LastParam->Type)) | CF_FORCECHAR; + Flags = CG_TypeOf (GetStructReplacementType (D->LastParam->Type)) | CF_FORCECHAR; } else { - Flags = TypeOf (D->LastParam->Type) | CF_FORCECHAR; + Flags = CG_TypeOf (D->LastParam->Type) | CF_FORCECHAR; } g_push (Flags, 0); } /* Generate function entry code if needed */ - g_enter (FuncTypeOf (Func->Type), F_GetParamSize (CurrentFunc)); + g_enter (CG_CallFlags (Func->Type), F_GetParamSize (CurrentFunc)); /* If stack checking code is requested, emit a call to the helper routine */ if (IS_Get (&CheckStack)) { @@ -648,31 +631,34 @@ void NewFunc (SymEntry* Func, FuncDesc* D) AnyStatement (0); } - /* If this is not a void function, and not the main function in a C99 - ** environment returning int, output a warning if we didn't see a return - ** statement. - */ - if (!F_HasVoidReturn (CurrentFunc) && !F_HasReturn (CurrentFunc) && !C99MainFunc && IS_Get (&WarnReturnType)) { - Warning ("Control reaches end of non-void function [-Wreturn-type]"); - } - - /* If this is the main function in a C99 environment returning an int, let - ** it always return zero. Note: Actual return statements jump to the return - ** label defined below. - ** The code is removed by the optimizer if unused. - */ - if (C99MainFunc) { - g_getimmed (CF_INT | CF_CONST, 0, 0); + /* Check if this function is missing a return value */ + if (!F_HasVoidReturn (CurrentFunc) && !F_HasReturn (CurrentFunc)) { + /* If this is the main function in a C99 environment returning an int, + ** let it always return zero. Otherwise output a warning. + */ + if (F_IsMainFunc (CurrentFunc) && + IS_Get (&Standard) >= STD_C99 && + GetUnqualRawTypeCode (ReturnType) == T_INT) { + g_getimmed (CF_INT | CF_CONST, 0, 0); + } else if (IS_Get (&WarnReturnType)) { + Warning ("Control reaches end of non-void function [-Wreturn-type]"); + } } /* Output the function exit code label */ g_defcodelabel (F_GetRetLab (CurrentFunc)); - /* Restore the register variables */ - F_RestoreRegVars (CurrentFunc); + /* Restore the register variables (not necessary for the main function in + ** cc65 mode) + */ + int CleanupOnExit = (IS_Get (&Standard) != STD_CC65) || + !F_IsMainFunc (CurrentFunc); + if (CleanupOnExit) { + F_RestoreRegVars (CurrentFunc); + } /* Generate the exit code */ - g_leave (); + g_leave (CleanupOnExit); /* Emit references to imports/exports */ EmitExternals (); @@ -684,9 +670,6 @@ void NewFunc (SymEntry* Func, FuncDesc* D) /* Leave the lexical level */ LeaveFunctionLevel (); - /* Eat the closing brace */ - ConsumeRCurly (); - /* Restore the old literal pool, remembering the one for the function */ Func->V.F.LitPool = PopLiteralPool (); @@ -696,7 +679,13 @@ void NewFunc (SymEntry* Func, FuncDesc* D) } /* Switch back to the old segments */ - PopSegments (); + PopSegContext (); + + /* Eat the closing brace after we've done everything with the function + ** definition. This way we won't have troubles with pragmas right after + ** the closing brace. + */ + ConsumeRCurly(); /* Reset the current function pointer */ FreeFunction (CurrentFunc); diff --git a/src/cc65/goto.c b/src/cc65/goto.c index 7d3ff1a6a..e96ad6c4c 100644 --- a/src/cc65/goto.c +++ b/src/cc65/goto.c @@ -43,6 +43,7 @@ #include "expr.h" #include "loadexpr.h" #include "scanner.h" +#include "seqpoint.h" #include "standard.h" #include "symtab.h" #include "goto.h" @@ -91,7 +92,7 @@ void GotoStatement (void) /* Find array size */ if (!IsTypeArray (arr->Type) || SizeOf (arr->Type) == 0 || - !(arr->Flags & SC_STATIC) || + (arr->Flags & SC_STORAGEMASK) != SC_STATIC || SizeOf (GetElementType(arr->Type)) != 2) { Error ("Expected a static array"); } else if (GetElementCount (arr->Type) > 127) { diff --git a/src/cc65/initdata.c b/src/cc65/initdata.c index 619fe4897..addc7421b 100644 --- a/src/cc65/initdata.c +++ b/src/cc65/initdata.c @@ -58,6 +58,7 @@ #include "litpool.h" #include "pragma.h" #include "scanner.h" +#include "seqpoint.h" #include "shift.h" #include "standard.h" #include "symtab.h" @@ -168,12 +169,12 @@ static void DefineData (ExprDesc* Expr) case E_LOC_NONE: /* Immediate numeric value with no storage */ - g_defdata (CF_IMM | TypeOf (Expr->Type) | CF_CONST, Expr->IVal, 0); + g_defdata (CF_IMM | CG_TypeOf (Expr->Type) | CF_CONST, Expr->IVal, 0); break; case E_LOC_ABS: /* Absolute numeric address */ - g_defdata (CF_ABSOLUTE | TypeOf (Expr->Type) | CF_CONST, Expr->IVal, 0); + g_defdata (CF_ABSOLUTE | CG_TypeOf (Expr->Type) | CF_CONST, Expr->IVal, 0); break; case E_LOC_GLOBAL: @@ -244,9 +245,6 @@ static void DefineBitFieldData (StructInitData* SI) static void DefineStrData (Literal* Lit, unsigned Count) { - /* Translate into target charset */ - TranslateLiteral (Lit); - /* Output the data */ g_defbytes (GetLiteralStr (Lit), Count); } @@ -304,6 +302,13 @@ static unsigned ParsePointerInit (const Type* T) /* Optional opening brace */ unsigned BraceCount = OpeningCurlyBraces (0); + /* We warn if an initializer for a scalar contains braces, because this is + ** quite unusual and often a sign for some problem in the input. + */ + if (BraceCount > 0) { + Warning ("Braces around scalar initializer"); + } + /* Expression */ ExprDesc ED = NoCodeConstExpr (hie1); TypeConversion (&ED, T); @@ -343,8 +348,8 @@ static unsigned ParseArrayInit (Type* T, int* Braces, int AllowFlexibleMembers) /* Char array initialized by string constant */ int NeedParen; - /* If we initializer is enclosed in brackets, remember this fact and - ** skip the opening bracket. + /* If the initializer is enclosed in curly braces, remember this fact + ** and skip the opening one. */ NeedParen = (CurTok.Tok == TOK_LCURLY); if (NeedParen) { @@ -377,7 +382,9 @@ static unsigned ParseArrayInit (Type* T, int* Braces, int AllowFlexibleMembers) } else { - /* Arrays can be initialized without a pair of curly braces */ + /* An array can be initialized without a pair of enclosing curly braces + ** if it is itself a member of a struct/union or an element of an array. + */ if (*Braces == 0 || CurTok.Tok == TOK_LCURLY) { /* Consume the opening curly brace */ HasCurly = ConsumeLCurly (); @@ -387,14 +394,22 @@ static unsigned ParseArrayInit (Type* T, int* Braces, int AllowFlexibleMembers) /* Initialize the array members */ Count = 0; while (CurTok.Tok != TOK_RCURLY) { - /* Flexible array members may not be initialized within - ** an array (because the size of each element may differ - ** otherwise). + /* Flexible array members cannot be initialized within an array. + ** (Otherwise the size of each element may differ.) */ ParseInitInternal (ElementType, Braces, 0); ++Count; - if (CurTok.Tok != TOK_COMMA) + if (CurTok.Tok != TOK_COMMA) { break; + } + + if (!HasCurly && ElementCount > 0 && Count >= ElementCount) { + /* If the array is initialized without enclosing curly braces, + ** it only accepts how many elements initializers up to its + ** count of elements, leaving any following initializers out. + */ + break; + } NextToken (); } @@ -489,9 +504,9 @@ static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers) if (HasCurly) { Error ("Excess elements in %s initializer", GetBasicTypeName (T)); - SkipInitializer (HasCurly); + SkipInitializer (0); } - return SI.Offs; + break; } /* Check for special members that don't consume the initializer */ @@ -503,18 +518,20 @@ static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers) /* This may be an anonymous bit-field, in which case it doesn't ** have an initializer. */ - if (SymIsBitField (TagSym) && (IsAnonName (TagSym->Name))) { - /* Account for the data and output it if we have at least a full - ** byte. We may have more if there was storage unit overlap, for - ** example two consecutive 7 bit fields. Those would be packed - ** into 2 bytes. - */ - SI.ValBits += TagSym->Type->A.B.Width; - CHECK (SI.ValBits <= CHAR_BIT * sizeof(SI.BitVal)); - /* TODO: Generalize this so any type can be used. */ - CHECK (SI.ValBits <= LONG_BITS); - while (SI.ValBits >= CHAR_BITS) { - DefineBitFieldData (&SI); + if (SymIsBitField (TagSym) && IsAnonName (TagSym->Name)) { + if (!IsTypeUnion (T)) { + /* Account for the data and output it if we have at least a full + ** byte. We may have more if there was storage unit overlap, for + ** example two consecutive 7 bit fields. Those would be packed + ** into 2 bytes. + */ + SI.ValBits += TagSym->Type->A.B.Width; + CHECK (SI.ValBits <= CHAR_BIT * sizeof(SI.BitVal)); + /* TODO: Generalize this so any type can be used. */ + CHECK (SI.ValBits <= LONG_BITS); + while (SI.ValBits >= CHAR_BITS) { + DefineBitFieldData (&SI); + } } /* Avoid consuming the comma if any */ goto NextMember; @@ -620,15 +637,15 @@ static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers) /* Skip the comma next round */ SkipComma = 1; -NextMember: - /* Next member. For unions, only the first one can be initialized */ + /* For unions, only the first named member can be initialized */ if (IsTypeUnion (T)) { - /* Union */ TagSym = 0; - } else { - /* Struct */ - TagSym = TagSym->NextSym; + continue; } + +NextMember: + /* Next member */ + TagSym = TagSym->NextSym; } if (HasCurly) { @@ -669,7 +686,7 @@ static unsigned ParseVoidInit (Type* T) Size = 0; do { ExprDesc Expr = NoCodeConstExpr (hie1); - switch (GetUnqualTypeCode (&Expr.Type[0])) { + switch (GetUnderlyingTypeCode (&Expr.Type[0])) { case T_SCHAR: case T_UCHAR: @@ -737,7 +754,7 @@ static unsigned ParseVoidInit (Type* T) static unsigned ParseInitInternal (Type* T, int *Braces, int AllowFlexibleMembers) /* Parse initialization of variables. Return the number of data bytes. */ { - switch (GetUnqualTypeCode (T)) { + switch (GetUnderlyingTypeCode (T)) { case T_SCHAR: case T_UCHAR: diff --git a/src/cc65/input.c b/src/cc65/input.c index 89c471687..fcf7f32f3 100644 --- a/src/cc65/input.c +++ b/src/cc65/input.c @@ -91,10 +91,11 @@ struct IFile { /* Struct that describes an active input file */ typedef struct AFile AFile; struct AFile { - unsigned Line; /* Line number for this file */ + unsigned LineNum; /* Actual line number for this file */ FILE* F; /* Input file stream */ IFile* Input; /* Points to corresponding IFile */ int SearchPath; /* True if we've added a path for this file */ + unsigned LineOffs; /* Offset to presumed line number for this file */ char* PName; /* Presumed name of the file */ PPIfStack IfStack; /* PP #if stack */ int MissingNL; /* Last input line was missing a newline */ @@ -111,6 +112,7 @@ static Collection* CurrentInputStack; /* Counter for the __COUNTER__ macro */ static unsigned MainFileCounter; +LineInfo* PrevDiagnosticLI; @@ -163,10 +165,11 @@ static AFile* NewAFile (IFile* IF, FILE* F) AFile* AF = (AFile*) xmalloc (sizeof (AFile)); /* Initialize the fields */ - AF->Line = 0; - AF->F = F; - AF->Input = IF; - AF->PName = 0; + AF->LineNum = 0; + AF->F = F; + AF->Input = IF; + AF->LineOffs = 0; + AF->PName = 0; AF->IfStack.Index = -1; AF->MissingNL = 0; @@ -285,7 +288,7 @@ void OpenMainFile (const char* Name) /* Update the line infos, so we have a valid line info even at start of ** the main file before the first line is read. */ - UpdateLineInfo (MainFile->Input, MainFile->Line, Line); + UpdateCurrentLineInfo (Line); /* Initialize the __COUNTER__ counter */ MainFileCounter = 0; @@ -553,7 +556,7 @@ int NextLine (void) if (!Input->MissingNL || SB_NotEmpty (Line)) { /* Accept files without a newline at the end */ - ++Input->Line; + ++Input->LineNum; /* Assume no new line */ Input->MissingNL = 1; @@ -569,7 +572,7 @@ int NextLine (void) if (C == '\n') { /* We got a new line */ - ++Input->Line; + ++Input->LineNum; /* If the \n is preceeded by a \r, remove the \r, so we can read ** DOS/Windows files under *nix. @@ -605,7 +608,7 @@ int NextLine (void) InitLine (Line); /* Create line information for this line */ - UpdateLineInfo (Input->Input, Input->Line, Line); + UpdateCurrentLineInfo (Line); /* Done */ return C != EOF || SB_NotEmpty (Line); @@ -645,15 +648,145 @@ int PreprocessNextLine (void) -const char* GetInputFile (const struct IFile* IF) -/* Return a filename from an IFile struct */ +static LineInfoFile* NewLineInfoFile (const AFile* AF) +{ + const char* Name = AF->PName == 0 ? AF->Input->Name : AF->PName; + unsigned Len = strlen (Name); + + /* Allocate memory for the file info and the file name */ + LineInfoFile* LIF = xmalloc (sizeof (LineInfoFile) + Len); + + /* Copy info */ + LIF->InputFile = AF->Input; + LIF->LineNum = AF->LineNum + AF->LineOffs; + memcpy (LIF->Name, Name, Len + 1); + + return LIF; +} + + + +void GetFileInclusionInfo (struct LineInfo* LI) +/* Get info about source file inclusion for LineInfo struct */ +{ + unsigned FileCount = CollCount (&AFiles); + + CHECK (FileCount > 0); + + /* Get the correct index */ + --FileCount; + + if (LI->IncFiles != 0) { + FreeFileInclusionInfo (LI); + } + LI->IncFiles = 0; + + if (LI->File != 0) { + xfree (LI->File); + } + + /* Copy info from the AFile */ + LI->File = NewLineInfoFile (CollAtUnchecked (&AFiles, FileCount)); + + /* Remember the actual line number */ + LI->ActualLineNum = ((AFile*)CollAtUnchecked (&AFiles, FileCount))->LineNum; + + if (FileCount > 0) { + /* The file is included from another */ + + /* Always use a new collection */ + LI->IncFiles = NewCollection (); + + while (FileCount-- > 0) { + /* Copy info from the AFile */ + LineInfoFile* LIF = NewLineInfoFile (CollAtUnchecked (&AFiles, FileCount)); + + /* Add this file */ + CollAppend (LI->IncFiles, LIF); + } + } +} + + + +void FreeFileInclusionInfo (struct LineInfo* LI) +/* Free info about source file inclusion for LineInfo struct */ +{ + if (LI->File != 0) { + xfree (LI->File); + LI->File = 0; + } + + if (LI->IncFiles != 0) { + unsigned I; + for (I = 0; I < CollCount (LI->IncFiles); ++I) { + CollAtUnchecked (LI->IncFiles, I); + } + FreeCollection (LI->IncFiles); + LI->IncFiles = 0; + } +} + + + +static int IsDifferentLineInfoFile (const LineInfoFile* Lhs, const LineInfoFile* Rhs) +/* Return true if the two files are different */ +{ + /* If the input files are the same but their presumed names are different, + ** we still consider the files same. + */ + return Lhs->InputFile != Rhs->InputFile || Lhs->LineNum != Rhs->LineNum; +} + + + +int HasFileInclusionChanged (const struct LineInfo* LI) +/* Return true if file inclusion has changed from last time */ +{ + if (LI->File != 0) { + LineInfo* PrevLI = GetPrevCheckedLI (); + + if (LI == PrevLI) { + return 0; + } + + if (PrevLI == 0) { + return 1; + } + + if (LI->IncFiles != 0) { + unsigned I; + + if (PrevLI->IncFiles == 0 || + CollCount (LI->IncFiles) != CollCount (PrevLI->IncFiles)) { + return 1; + } + + for (I = 0; I < CollCount (LI->IncFiles); ++I) { + /* If this refers to a different file, then the inclusion has changed */ + if (IsDifferentLineInfoFile (CollAtUnchecked (LI->IncFiles, I), + CollAtUnchecked (PrevLI->IncFiles, I))) { + return 1; + } + } + } + } + + /* Unchanged */ + return 0; +} + + + +const char* GetInputFileName (const struct IFile* IF) +/* Return the name of the file from an IFile struct */ { return IF->Name; } -const char* GetCurrentFilename (void) +const char* GetCurrentFileName (void) /* Return the name of the current input file */ { unsigned AFileCount = CollCount (&AFiles); @@ -674,7 +807,7 @@ unsigned GetCurrentLineNum (void) unsigned AFileCount = CollCount (&AFiles); if (AFileCount > 0) { const AFile* AF = CollLast (&AFiles); - return AF->Line; + return AF->LineNum + AF->LineOffs; } else { /* No open file */ return 0; @@ -684,18 +817,18 @@ unsigned GetCurrentLineNum (void) void SetCurrentLineNum (unsigned LineNum) -/* Set the line number in the current input file */ +/* Set the presumed line number in the current input file */ { unsigned AFileCount = CollCount (&AFiles); if (AFileCount > 0) { AFile* AF = CollLast (&AFiles); - AF->Line = LineNum; + AF->LineOffs = LineNum - AF->LineNum; } } -void SetCurrentFilename (const char* Name) +void SetCurrentFileName (const char* Name) /* Set the presumed name of the current input file */ { unsigned AFileCount = CollCount (&AFiles); diff --git a/src/cc65/input.h b/src/cc65/input.h index 9457bdf9b..9a5a76949 100644 --- a/src/cc65/input.h +++ b/src/cc65/input.h @@ -52,6 +52,10 @@ +/* Forwards */ +struct IFile; +struct LineInfo; + /* An enum that describes different types of input files. The members are ** choosen so that it is possible to combine them to bitsets */ @@ -61,9 +65,6 @@ typedef enum { IT_USRINC = 0x04, /* User include file (using "") */ } InputType; -/* Forward for an IFile structure */ -struct IFile; - /* The current input line */ extern StrBuf* Line; @@ -125,10 +126,19 @@ int PreprocessNextLine (void); ** main file. */ -const char* GetInputFile (const struct IFile* IF); -/* Return a filename from an IFile struct */ +void GetFileInclusionInfo (struct LineInfo* LI); +/* Get info about source file inclusion for LineInfo struct */ -const char* GetCurrentFilename (void); +void FreeFileInclusionInfo (struct LineInfo* LI); +/* Free info about source file inclusion for LineInfo struct */ + +int HasFileInclusionChanged (const struct LineInfo* LI); +/* Return true if file inclusion has changed from last time */ + +const char* GetInputFileName (const struct IFile* IF); +/* Return the name of the file from an IFile struct */ + +const char* GetCurrentFileName (void); /* Return the name of the current input file */ unsigned GetCurrentLineNum (void); @@ -137,7 +147,7 @@ unsigned GetCurrentLineNum (void); void SetCurrentLineNum (unsigned LineNum); /* Set the line number in the current input file */ -void SetCurrentFilename (const char* Name); +void SetCurrentFileName (const char* Name); /* Set the presumed name of the current input file */ unsigned GetCurrentCounter (void); diff --git a/src/cc65/lineinfo.c b/src/cc65/lineinfo.c index f5c2e2689..8639d27e9 100644 --- a/src/cc65/lineinfo.c +++ b/src/cc65/lineinfo.c @@ -56,6 +56,9 @@ /* Global pointer to line information for the current line */ static LineInfo* CurLineInfo = 0; +/* Global pointer to previously checked line information about file inclusion hierarchy */ +static LineInfo* PrevCheckedLI = 0; + /*****************************************************************************/ @@ -64,7 +67,7 @@ static LineInfo* CurLineInfo = 0; -static LineInfo* NewLineInfo (struct IFile* F, unsigned LineNum, const StrBuf* Line) +static LineInfo* NewLineInfo (const StrBuf* Line) /* Create and return a new line info. Ref count will be 1. */ { unsigned Len; @@ -87,8 +90,9 @@ static LineInfo* NewLineInfo (struct IFile* F, unsigned LineNum, const StrBuf* L /* Initialize the fields */ LI->RefCount = 1; - LI->InputFile = F; - LI->LineNum = LineNum; + LI->File = 0; + LI->IncFiles = 0; + GetFileInclusionInfo (LI); /* Copy the line, replacing tabs by spaces in the given line since tabs ** will give rather arbitrary results when used in the output later, and @@ -117,6 +121,7 @@ static LineInfo* NewLineInfo (struct IFile* F, unsigned LineNum, const StrBuf* L static void FreeLineInfo (LineInfo* LI) /* Free a LineInfo structure */ { + FreeFileInclusionInfo (LI); xfree (LI); } @@ -156,8 +161,8 @@ LineInfo* GetCurLineInfo (void) -void UpdateLineInfo (struct IFile* F, unsigned LineNum, const StrBuf* Line) -/* Update the line info - called if a new line is read */ +void UpdateCurrentLineInfo (const StrBuf* Line) +/* Update the current line info - called if a new line is read */ { /* If a current line info exists, release it */ if (CurLineInfo) { @@ -172,23 +177,60 @@ void UpdateLineInfo (struct IFile* F, unsigned LineNum, const StrBuf* Line) } /* Create a new line info */ - CurLineInfo = NewLineInfo (F, LineNum, Line); + CurLineInfo = NewLineInfo (Line); } -const char* GetInputName (const LineInfo* LI) -/* Return the file name from a line info */ +void RememberCheckedLI (LineInfo* LI) +/* Remember the latest checked line info struct */ +{ + if (PrevCheckedLI != LI) { + if (PrevCheckedLI != 0) { + ReleaseLineInfo (PrevCheckedLI); + } + PrevCheckedLI = UseLineInfo (LI); + } +} + + + +LineInfo* GetPrevCheckedLI (void) +/* Get the latest checked line info struct */ +{ + return PrevCheckedLI; +} + + + +const char* GetPresumedFileName (const LineInfo* LI) +/* Return the presumed file name from a line info */ { PRECONDITION (LI != 0); - return GetInputFile (LI->InputFile); + return LI->File->Name; } -unsigned GetInputLine (const LineInfo* LI) -/* Return the line number from a line info */ +unsigned GetPresumedLineNum (const LineInfo* LI) +/* Return the presumed line number from a line info */ { PRECONDITION (LI != 0); - return LI->LineNum; + return LI->File->LineNum; +} + + + +const char* GetActualFileName (const struct LineInfo* LI) +/* Return the actual name of the source file from a line info struct */ +{ + return LI->File != 0 ? GetInputFileName (LI->File->InputFile) : "<out of filescope>"; +} + + + +unsigned GetActualLineNum (const struct LineInfo* LI) +/* Return the actual line number of the source file from a line info struct */ +{ + return LI->ActualLineNum; } diff --git a/src/cc65/lineinfo.h b/src/cc65/lineinfo.h index f365b4f01..02e77cd9c 100644 --- a/src/cc65/lineinfo.h +++ b/src/cc65/lineinfo.h @@ -60,15 +60,24 @@ struct IFile; +/* Struct that describes an input file for line info */ +typedef struct LineInfoFile LineInfoFile; +struct LineInfoFile { + struct IFile* InputFile; /* Points to corresponding IFile */ + unsigned LineNum; /* Presumed line number for this file */ + char Name[1]; /* Presumed name of the file */ +}; + /* The text for the actual line is allocated at the end of the structure, so ** the size of the structure varies. */ typedef struct LineInfo LineInfo; struct LineInfo { - unsigned RefCount; /* Reference counter */ - struct IFile* InputFile; /* Input file for this line */ - unsigned LineNum; /* Line number */ - char Line[1]; /* Source code line */ + unsigned RefCount; /* Reference counter */ + LineInfoFile* File; /* Presumed input files for this line */ + unsigned ActualLineNum; /* Actual line number for this file */ + struct Collection* IncFiles; /* Presumed inclusion input files */ + char Line[1]; /* Text of source code line */ }; @@ -92,14 +101,26 @@ LineInfo* GetCurLineInfo (void); ** increased, use UseLineInfo for that purpose. */ -void UpdateLineInfo (struct IFile* F, unsigned LineNum, const StrBuf* Line); -/* Update the line info - called if a new line is read */ +void UpdateCurrentLineInfo (const StrBuf* Line); +/* Update the current line info - called if a new line is read */ -const char* GetInputName (const LineInfo* LI); -/* Return the file name from a line info */ +void RememberCheckedLI (struct LineInfo* LI); +/* Remember the latest checked line info struct */ -unsigned GetInputLine (const LineInfo* LI); -/* Return the line number from a line info */ +LineInfo* GetPrevCheckedLI (void); +/* Get the latest checked line info struct */ + +const char* GetPresumedFileName (const LineInfo* LI); +/* Return the presumed file name from a line info */ + +unsigned GetPresumedLineNum (const LineInfo* LI); +/* Return the presumed line number from a line info */ + +const char* GetActualFileName (const struct LineInfo* LI); +/* Return the actual name of the source file from a line info struct */ + +unsigned GetActualLineNum (const struct LineInfo* LI); +/* Return the actual line number of the source file from a line info struct */ diff --git a/src/cc65/litpool.c b/src/cc65/litpool.c index d741f87d0..6d8827c60 100644 --- a/src/cc65/litpool.c +++ b/src/cc65/litpool.c @@ -92,7 +92,7 @@ static Collection LPStack = STATIC_COLLECTION_INITIALIZER; -static Literal* NewLiteral (const void* Buf, unsigned Len) +static Literal* NewLiteral (const StrBuf* S) /* Create a new literal and return it */ { /* Allocate memory */ @@ -103,7 +103,7 @@ static Literal* NewLiteral (const void* Buf, unsigned Len) L->RefCount = 0; L->Output = 0; SB_Init (&L->Data); - SB_AppendBuf (&L->Data, Buf, Len); + SB_Append (&L->Data, S); /* Return the new literal */ return L; @@ -160,9 +160,20 @@ void ReleaseLiteral (Literal* L) void TranslateLiteral (Literal* L) -/* Translate a literal into the target charset. */ +/* Translate a literal into the target charset */ { - TgtTranslateBuf (SB_GetBuf (&L->Data), SB_GetLen (&L->Data)); + TgtTranslateStrBuf (&L->Data); +} + + + +void ConcatLiteral (Literal* L, const Literal* Appended) +/* Concatenate string literals */ +{ + if (SB_GetLen (&L->Data) > 0 && SB_LookAtLast (&L->Data) == '\0') { + SB_Drop (&L->Data, 1); + } + SB_Append (&L->Data, &Appended->Data); } @@ -457,18 +468,18 @@ void OutputGlobalLiteralPool (void) Literal* AddLiteral (const char* S) /* Add a literal string to the literal pool. Return the literal. */ { - return AddLiteralBuf (S, strlen (S) + 1); + StrBuf SB; + SB_InitFromString(&SB, S); + return AddLiteralStr(&SB); } -Literal* AddLiteralBuf (const void* Buf, unsigned Len) -/* Add a buffer containing a literal string to the literal pool. Return the -** literal. -*/ +Literal* AddLiteralStr (const StrBuf* S) +/* Add a literal string to the literal pool. Return the literal. */ { /* Create a new literal */ - Literal* L = NewLiteral (Buf, Len); + Literal* L = NewLiteral (S); /* Add the literal to the correct pool */ if (IS_Get (&WritableStrings)) { @@ -480,11 +491,3 @@ Literal* AddLiteralBuf (const void* Buf, unsigned Len) /* Return the new literal */ return L; } - - - -Literal* AddLiteralStr (const StrBuf* S) -/* Add a literal string to the literal pool. Return the literal. */ -{ - return AddLiteralBuf (SB_GetConstBuf (S), SB_GetLen (S)); -} diff --git a/src/cc65/litpool.h b/src/cc65/litpool.h index 78f432138..959924e94 100644 --- a/src/cc65/litpool.h +++ b/src/cc65/litpool.h @@ -75,7 +75,10 @@ void ReleaseLiteral (Literal* L); /* Decrement the reference counter for the literal */ void TranslateLiteral (Literal* L); -/* Translate a literal into the target charset. */ +/* Translate a literal into the target charset */ + +void ConcatLiteral (Literal* L, const Literal* Appended); +/* Concatenate string literals */ unsigned GetLiteralLabel (const Literal* L); /* Return the asm label for a literal */ @@ -122,11 +125,6 @@ void OutputGlobalLiteralPool (void); Literal* AddLiteral (const char* S); /* Add a literal string to the literal pool. Return the literal. */ -Literal* AddLiteralBuf (const void* Buf, unsigned Len); -/* Add a buffer containing a literal string to the literal pool. Return the -** literal. -*/ - Literal* AddLiteralStr (const StrBuf* S); /* Add a literal string to the literal pool. Return the literal. */ diff --git a/src/cc65/loadexpr.c b/src/cc65/loadexpr.c index c5ac43f78..d06ca9437 100644 --- a/src/cc65/loadexpr.c +++ b/src/cc65/loadexpr.c @@ -133,10 +133,10 @@ void LoadExpr (unsigned Flags, struct ExprDesc* Expr) AdjustBitField = 1; /* Flags we need operate on the whole bit-field, without CF_FORCECHAR. */ - BitFieldFullWidthFlags = Flags | TypeOf (Expr->Type); + BitFieldFullWidthFlags = Flags | CG_TypeOf (Expr->Type); /* Flags we need operate on the whole chunk containing the bit-field. */ - Flags |= TypeOf (GetBitFieldChunkType (Expr->Type)); + Flags |= CG_TypeOf (GetBitFieldChunkType (Expr->Type)); /* If we're adjusting, then only load a char (not an int) and do only char ops; ** We will clear the high byte in the adjustment. CF_FORCECHAR does nothing if @@ -151,7 +151,7 @@ void LoadExpr (unsigned Flags, struct ExprDesc* Expr) if (IsIncompleteESUType (Expr->Type)) { return; } - Flags |= TypeOf (Expr->Type); + Flags |= CG_TypeOf (Expr->Type); } } @@ -175,7 +175,7 @@ void LoadExpr (unsigned Flags, struct ExprDesc* Expr) case E_LOC_NONE: /* Immediate number constant */ - g_getimmed (Flags | CF_IMM | TypeOf (Expr->Type) | CF_CONST, Expr->IVal, 0); + g_getimmed (Flags | CF_IMM | CG_TypeOf (Expr->Type) | CF_CONST, Expr->IVal, 0); break; case E_LOC_ABS: diff --git a/src/cc65/locals.c b/src/cc65/locals.c index 68ac00e62..08e41918e 100644 --- a/src/cc65/locals.c +++ b/src/cc65/locals.c @@ -49,6 +49,7 @@ #include "initdata.h" #include "loadexpr.h" #include "locals.h" +#include "seqpoint.h" #include "stackptr.h" #include "standard.h" #include "staticassert.h" @@ -110,15 +111,12 @@ static void ParseRegisterDecl (Declarator* Decl, int Reg) /* Get the size of the variable */ unsigned Size = SizeOf (Decl->Type); - /* Save the current contents of the register variable on stack */ - F_AllocLocalSpace (CurrentFunc); - g_save_regvars (Reg, Size); - - /* Add the symbol to the symbol table. We do that now, because for register - ** variables the current stack pointer is implicitly used as location for - ** the save area. + /* Check if this is the main function and we are in cc65 mode. If so, we + ** won't save the old contents of the register variables since in cc65 + ** mode main() may not be called recursively. */ - Sym = AddLocalSym (Decl->Ident, Decl->Type, Decl->StorageClass, Reg); + int SaveRegVars = (IS_Get (&Standard) != STD_CC65) || + !F_IsMainFunc (CurrentFunc); /* Check for an optional initialization */ if (CurTok.Tok == TOK_ASSIGN) { @@ -126,6 +124,25 @@ static void ParseRegisterDecl (Declarator* Decl, int Reg) /* Skip the '=' */ NextToken (); + /* If the register variable is initialized, the initialization code may + ** access other already declared variables. This means that we have to + ** allocate them now. + */ + F_AllocLocalSpace (CurrentFunc); + + /* Save the current contents of the register variable on stack. This is + ** not necessary for the main function. + */ + if (SaveRegVars) { + g_save_regvars (Reg, Size); + } + + /* Add the symbol to the symbol table. We do that now, because for + ** register variables the current stack pointer is implicitly used + ** as location for the save area (maybe unused in case of main()). + */ + Sym = AddLocalSym (Decl->Ident, Decl->Type, Decl->StorageClass, Reg); + /* Special handling for compound types */ if (IsCompound) { @@ -164,7 +181,7 @@ static void ParseRegisterDecl (Declarator* Decl, int Reg) LoadExpr (CF_NONE, &Expr); /* Store the value into the variable */ - g_putstatic (CF_REGVAR | TypeOf (Sym->Type), Reg, 0); + g_putstatic (CF_REGVAR | CG_TypeOf (Sym->Type), Reg, 0); /* This has to be done at sequence point */ DoDeferred (SQP_KEEP_NONE, &Expr); @@ -172,6 +189,21 @@ static void ParseRegisterDecl (Declarator* Decl, int Reg) /* Mark the variable as referenced */ Sym->Flags |= SC_REF; + } else { + + /* Save the current contents of the register variable on stack. This is + ** not necessary for the main function. + */ + if (SaveRegVars) { + F_AllocLocalSpace (CurrentFunc); + g_save_regvars (Reg, Size); + } + + /* Add the symbol to the symbol table. We do that now, because for + ** register variables the current stack pointer is implicitly used + ** as location for the save area (maybe unused in case of main()). + */ + Sym = AddLocalSym (Decl->Ident, Decl->Type, Decl->StorageClass, Reg); } /* Cannot allocate a variable of unknown size */ @@ -274,7 +306,7 @@ static void ParseAutoDecl (Declarator* Decl) } /* Push the value */ - g_push (Flags | TypeOf (Sym->Type), Expr.IVal); + g_push (Flags | CG_TypeOf (Sym->Type), Expr.IVal); /* This has to be done at sequence point */ DoDeferred (SQP_KEEP_NONE, &Expr); @@ -303,7 +335,7 @@ static void ParseAutoDecl (Declarator* Decl) /* Static local variables. */ - Decl->StorageClass = (Decl->StorageClass & ~SC_AUTO) | SC_STATIC; + Decl->StorageClass = (Decl->StorageClass & ~SC_STORAGEMASK) | SC_STATIC; /* Generate a label, but don't define it */ DataLabel = GetLocalDataLabel (); @@ -353,7 +385,7 @@ static void ParseAutoDecl (Declarator* Decl) LoadExpr (CF_NONE, &Expr); /* Store the value into the variable */ - g_putstatic (CF_STATIC | TypeOf (Sym->Type), DataLabel, 0); + g_putstatic (CF_STATIC | CG_TypeOf (Sym->Type), DataLabel, 0); /* This has to be done at sequence point */ DoDeferred (SQP_KEEP_NONE, &Expr); @@ -440,33 +472,37 @@ static void ParseStaticDecl (Declarator* Decl) -static void ParseOneDecl (const DeclSpec* Spec) +static int ParseOneDecl (DeclSpec* Spec) /* Parse one variable declarator. */ { - Declarator Decl; /* Declarator data structure */ + Declarator Decl; /* Declarator data structure */ + int NeedClean; /* Read the declarator */ - ParseDecl (Spec, &Decl, DM_NEED_IDENT); + NeedClean = ParseDecl (Spec, &Decl, DM_IDENT_OR_EMPTY); - /* Check if there are any non-extern storage classes set for function - ** declarations. Function can only be declared inside functions with the - ** 'extern' storage class specifier or no storage class specifier at all. + /* Check if there are explicitly specified non-external storage classes + ** for function declarations. */ - if ((Decl.StorageClass & SC_FUNC) == SC_FUNC) { - - /* Check if there are explicitly specified non-external storage classes */ + if ((Decl.StorageClass & SC_TYPEMASK) == SC_FUNC) { + /* Function can only be declared inside functions with the 'extern' + ** storage class specifier or no storage class specifier at all. + ** Note: this declaration is always checked for compatibility with + ** other declarations of the same symbol, but does not necessarily + ** make the symbol globally visible. This is tricky. + */ if ((Spec->Flags & DS_DEF_STORAGE) != DS_DEF_STORAGE && - (Decl.StorageClass & SC_EXTERN) == 0 && + (Decl.StorageClass & SC_STORAGEMASK) != SC_EXTERN && (Decl.StorageClass & SC_STORAGEMASK) != 0) { Error ("Illegal storage class on function"); } /* The default storage class could be wrong. Just clear them */ Decl.StorageClass &= ~SC_STORAGEMASK; - - /* This is always an extern declaration */ - Decl.StorageClass |= SC_DECL | SC_EXTERN; + } else if ((Decl.StorageClass & SC_STORAGEMASK) != SC_EXTERN) { + /* If the symbol is not marked as external, it will be defined now */ + Decl.StorageClass |= SC_DEF; } /* If we don't have a name, this was flagged as an error earlier. @@ -476,12 +512,6 @@ static void ParseOneDecl (const DeclSpec* Spec) AnonName (Decl.Ident, "param"); } - /* If the symbol is not marked as external, it will be defined now */ - if ((Decl.StorageClass & SC_DECL) == 0 && - (Decl.StorageClass & SC_EXTERN) == 0) { - Decl.StorageClass |= SC_DEF; - } - /* Handle anything that needs storage (no functions, no typdefs) */ if ((Decl.StorageClass & SC_DEF) == SC_DEF && (Decl.StorageClass & SC_TYPEMASK) != SC_TYPEDEF) { @@ -490,20 +520,20 @@ static void ParseOneDecl (const DeclSpec* Spec) ** convert the declaration to "auto" if this is not possible. */ int Reg = 0; /* Initialize to avoid gcc complains */ - if ((Decl.StorageClass & SC_REGISTER) != 0 && + if ((Decl.StorageClass & SC_STORAGEMASK) == SC_REGISTER && (Reg = F_AllocRegVar (CurrentFunc, Decl.Type)) < 0) { /* No space for this register variable, convert to auto */ - Decl.StorageClass = (Decl.StorageClass & ~SC_REGISTER) | SC_AUTO; + Decl.StorageClass = (Decl.StorageClass & ~SC_STORAGEMASK) | SC_AUTO; } /* Check the variable type */ - if ((Decl.StorageClass & SC_REGISTER) == SC_REGISTER) { + if ((Decl.StorageClass & SC_STORAGEMASK) == SC_REGISTER) { /* Register variable */ ParseRegisterDecl (&Decl, Reg); - } else if ((Decl.StorageClass & SC_AUTO) == SC_AUTO) { + } else if ((Decl.StorageClass & SC_STORAGEMASK) == SC_AUTO) { /* Auto variable */ ParseAutoDecl (&Decl); - } else if ((Decl.StorageClass & SC_STATIC) == SC_STATIC) { + } else if ((Decl.StorageClass & SC_STORAGEMASK) == SC_STATIC) { /* Static variable */ ParseStaticDecl (&Decl); } else { @@ -512,7 +542,7 @@ static void ParseOneDecl (const DeclSpec* Spec) } else { - if ((Decl.StorageClass & SC_EXTERN) == SC_EXTERN) { + if ((Decl.StorageClass & SC_STORAGEMASK) == SC_EXTERN) { /* External identifier - may not get initialized */ if (CurTok.Tok == TOK_ASSIGN) { Error ("Cannot initialize extern variable '%s'", Decl.Ident); @@ -522,8 +552,8 @@ static void ParseOneDecl (const DeclSpec* Spec) } } - if ((Decl.StorageClass & SC_EXTERN) == SC_EXTERN || - (Decl.StorageClass & SC_FUNC) == SC_FUNC) { + if ((Decl.StorageClass & SC_STORAGEMASK) == SC_EXTERN || + (Decl.StorageClass & SC_TYPEMASK) == SC_FUNC) { /* Add the global symbol to both of the global and local symbol ** tables. */ @@ -537,6 +567,8 @@ static void ParseOneDecl (const DeclSpec* Spec) /* Make sure we aren't missing some work */ CheckDeferredOpAllDone (); + + return NeedClean; } @@ -550,17 +582,12 @@ void DeclareLocals (void) /* A place to store info about potential initializations of auto variables */ CollAppend (&CurrentFunc->LocalsBlockStack, 0); - /* Loop until we don't find any more variables */ + /* Loop until we don't find any more variables. EOF is handled in the loop + ** as well. + */ while (1) { - - /* Check variable declarations. We need to distinguish between a - ** default int type and the end of variable declarations. So we - ** will do the following: If there is no explicit storage class - ** specifier *and* no explicit type given, *and* no type qualifiers - ** have been read, it is assumed that we have reached the end of - ** declarations. - */ DeclSpec Spec; + int NeedClean; /* Check for a _Static_assert */ if (CurTok.Tok == TOK_STATIC_ASSERT) { @@ -568,10 +595,18 @@ void DeclareLocals (void) continue; } - ParseDeclSpec (&Spec, TS_DEFAULT_TYPE_INT, SC_AUTO); - if ((Spec.Flags & DS_DEF_STORAGE) != 0 && /* No storage spec */ - (Spec.Flags & DS_DEF_TYPE) != 0 && /* No type given */ - GetQualifier (Spec.Type) == T_QUAL_NONE) { /* No type qualifier */ + /* Read the declaration specifier */ + ParseDeclSpec (&Spec, TS_DEFAULT_TYPE_INT | TS_FUNCTION_SPEC, SC_AUTO); + + /* Check variable declarations. We need distinguish between a default + ** int type and the end of variable declarations. So we will do the + ** following: If there is no explicit storage class specifier *and* no + ** explicit type given, *and* no type qualifiers have been read, it is + ** assumed that we have reached the end of declarations. + */ + if ((Spec.Flags & DS_DEF_STORAGE) != 0 && /* No storage spec */ + (Spec.Flags & DS_TYPE_MASK) == DS_DEF_TYPE && /* No type given */ + GetQualifier (Spec.Type) == T_QUAL_NONE) { /* No type qualifier */ break; } @@ -583,11 +618,24 @@ void DeclareLocals (void) continue; } + /* If we haven't got a type specifier yet, something must be wrong */ + if ((Spec.Flags & DS_TYPE_MASK) == DS_NONE) { + /* Avoid extra errors if it was a failed type specifier */ + if ((Spec.Flags & DS_EXTRA_TYPE) == 0) { + Error ("Declaration specifier expected"); + } + NeedClean = -1; + goto EndOfDecl; + } + /* Parse a comma separated variable list */ while (1) { - /* Parse one declaration */ - ParseOneDecl (&Spec); + /* Parse one declarator */ + NeedClean = ParseOneDecl (&Spec); + if (NeedClean <= 0) { + break; + } /* Check if there is more */ if (CurTok.Tok == TOK_COMMA) { @@ -599,8 +647,20 @@ void DeclareLocals (void) } } - /* A semicolon must follow */ - ConsumeSemi (); + if (NeedClean > 0) { + /* Must be followed by a semicolon */ + if (ConsumeSemi ()) { + NeedClean = 0; + } else { + NeedClean = -1; + } + } + +EndOfDecl: + /* Try some smart error recovery */ + if (NeedClean < 0) { + SmartErrorSkip (1); + } } /* Be sure to allocate any reserved space for locals */ diff --git a/src/cc65/main.c b/src/cc65/main.c index f800ac43e..47435757c 100644 --- a/src/cc65/main.c +++ b/src/cc65/main.c @@ -91,7 +91,7 @@ static void Usage (void) " -Os\t\t\t\tInline some standard functions\n" " -T\t\t\t\tInclude source as comment\n" " -V\t\t\t\tPrint the compiler version number\n" - " -W warning[,...]\t\tSuppress warnings\n" + " -W [-+]warning[,...]\t\tControl warnings ('-' disables, '+' enables)\n" " -d\t\t\t\tDebug mode\n" " -g\t\t\t\tAdd debug info to object file\n" " -h\t\t\t\tHelp (this text)\n" @@ -303,6 +303,10 @@ static void SetSys (const char* Sys) DefineNumericMacro ("__KIM1__", 1); break; + case TGT_RP6502: + DefineNumericMacro ("__RP6502__", 1); + break; + default: AbEnd ("Unknown target system '%s'", Sys); } @@ -1085,7 +1089,7 @@ int main (int argc, char* argv[]) Compile (InputFile); /* Create the output file if we didn't had any errors */ - if (PreprocessOnly == 0 && (ErrorCount == 0 || Debug)) { + if (PreprocessOnly == 0 && (GetTotalErrors () == 0 || Debug)) { /* Emit literals, do cleanup and optimizations */ FinishCompile (); @@ -1111,5 +1115,5 @@ int main (int argc, char* argv[]) DoneSegAddrSizes (); /* Return an apropriate exit code */ - return (ErrorCount > 0)? EXIT_FAILURE : EXIT_SUCCESS; + return (GetTotalErrors () > 0)? EXIT_FAILURE : EXIT_SUCCESS; } diff --git a/src/cc65/ppexpr.c b/src/cc65/ppexpr.c index 8d8c0b65d..b7382697c 100644 --- a/src/cc65/ppexpr.c +++ b/src/cc65/ppexpr.c @@ -55,7 +55,6 @@ static int PPEvaluationFailed = 0; -static void PPhie0 (PPExpr* Expr); static void PPhie1 (PPExpr* Expr); @@ -138,7 +137,7 @@ static void PPhiePrimary (PPExpr* Expr) ** recursively. */ NextToken (); - PPhie0 (Expr); + PPhie1 (Expr); ConsumeRParen (); break; @@ -263,6 +262,7 @@ void PPhie10 (PPExpr* Expr) NextToken (); PPhie10 (Expr); Expr->IVal = !Expr->IVal; + Expr->Flags &= ~PPEXPR_UNSIGNED; /* Result is signed */ break; case TOK_CEOF: @@ -424,10 +424,10 @@ static void PPhie_compare (const token_t* Ops, /* List of generators */ } } } - } - /* The result is signed */ - Expr->Flags &= ~PPEXPR_UNSIGNED; + /* The result is signed */ + Expr->Flags &= ~PPEXPR_UNSIGNED; + } } @@ -711,7 +711,7 @@ static void PPhieQuest (PPExpr* Expr) /* Parse second expression */ PPExprInit (&Expr2); - PPhie0 (&Expr2); + PPhie1 (&Expr2); /* Skip the colon */ ConsumeColon (); @@ -726,7 +726,7 @@ static void PPhieQuest (PPExpr* Expr) PPhieQuest (&Expr3); /* Set the result */ - Expr->IVal = Expr->IVal ? Expr2.IVal != 0 : Expr3.IVal != 0; + Expr->IVal = Expr->IVal ? Expr2.IVal : Expr3.IVal; /* Restore evaluation as before */ PPEvaluationEnabled = PPEvaluationEnabledPrev; @@ -809,23 +809,6 @@ static void PPhie1 (PPExpr* Expr) -static void PPhie0 (PPExpr* Expr) -/* Handle the comma "," operator */ -{ - PPhie1 (Expr); - - while (CurTok.Tok == TOK_COMMA) { - /* Skip the comma */ - NextToken (); - /* Reset the expression */ - PPExprInit (Expr); - /* Use the next operand as the value instead */ - PPhie1 (Expr); - } -} - - - void ParsePPExprInLine (PPExpr* Expr) /* Parse a line for PP expression */ { @@ -836,7 +819,7 @@ void ParsePPExprInLine (PPExpr* Expr) /* Parse */ PPExprInit (Expr); - PPhie0 (Expr); + PPhie1 (Expr); /* If the evaluation fails, the result is always zero */ if (PPEvaluationFailed) { diff --git a/src/cc65/pragma.c b/src/cc65/pragma.c index 83ed362c8..ee71b42d8 100644 --- a/src/cc65/pragma.c +++ b/src/cc65/pragma.c @@ -41,12 +41,11 @@ #include "chartype.h" #include "segnames.h" #include "tgttrans.h" +#include "xmalloc.h" /* cc65 */ #include "codegen.h" #include "error.h" -#include "expr.h" -#include "funcdesc.h" #include "global.h" #include "litpool.h" #include "scanner.h" @@ -58,7 +57,7 @@ /*****************************************************************************/ -/* data */ +/* Data */ /*****************************************************************************/ @@ -69,70 +68,67 @@ typedef enum { PRAGMA_ALIGN, PRAGMA_ALLOW_EAGER_INLINE, PRAGMA_BSS_NAME, - PRAGMA_BSSSEG, /* obsolete */ PRAGMA_CHARMAP, PRAGMA_CHECK_STACK, - PRAGMA_CHECKSTACK, /* obsolete */ PRAGMA_CODE_NAME, - PRAGMA_CODESEG, /* obsolete */ PRAGMA_CODESIZE, PRAGMA_DATA_NAME, - PRAGMA_DATASEG, /* obsolete */ PRAGMA_INLINE_STDFUNCS, PRAGMA_LOCAL_STRINGS, PRAGMA_MESSAGE, PRAGMA_OPTIMIZE, PRAGMA_REGISTER_VARS, PRAGMA_REGVARADDR, - PRAGMA_REGVARS, /* obsolete */ PRAGMA_RODATA_NAME, - PRAGMA_RODATASEG, /* obsolete */ PRAGMA_SIGNED_CHARS, - PRAGMA_SIGNEDCHARS, /* obsolete */ PRAGMA_STATIC_LOCALS, - PRAGMA_STATICLOCALS, /* obsolete */ PRAGMA_WARN, PRAGMA_WRAPPED_CALL, PRAGMA_WRITABLE_STRINGS, PRAGMA_ZPSYM, - PRAGMA_COUNT } pragma_t; /* Pragma table */ static const struct Pragma { const char* Key; /* Keyword */ pragma_t Tok; /* Token */ -} Pragmas[PRAGMA_COUNT] = { +} Pragmas[] = { { "align", PRAGMA_ALIGN }, { "allow-eager-inline", PRAGMA_ALLOW_EAGER_INLINE }, + { "allow_eager_inline", PRAGMA_ALLOW_EAGER_INLINE }, { "bss-name", PRAGMA_BSS_NAME }, - { "bssseg", PRAGMA_BSSSEG }, /* obsolete */ + { "bss_name", PRAGMA_BSS_NAME }, { "charmap", PRAGMA_CHARMAP }, { "check-stack", PRAGMA_CHECK_STACK }, - { "checkstack", PRAGMA_CHECKSTACK }, /* obsolete */ + { "check_stack", PRAGMA_CHECK_STACK }, { "code-name", PRAGMA_CODE_NAME }, - { "codeseg", PRAGMA_CODESEG }, /* obsolete */ + { "code_name", PRAGMA_CODE_NAME }, { "codesize", PRAGMA_CODESIZE }, { "data-name", PRAGMA_DATA_NAME }, - { "dataseg", PRAGMA_DATASEG }, /* obsolete */ + { "data_name", PRAGMA_DATA_NAME }, { "inline-stdfuncs", PRAGMA_INLINE_STDFUNCS }, + { "inline_stdfuncs", PRAGMA_INLINE_STDFUNCS }, { "local-strings", PRAGMA_LOCAL_STRINGS }, + { "local_strings", PRAGMA_LOCAL_STRINGS }, { "message", PRAGMA_MESSAGE }, { "optimize", PRAGMA_OPTIMIZE }, { "register-vars", PRAGMA_REGISTER_VARS }, + { "register_vars", PRAGMA_REGISTER_VARS }, { "regvaraddr", PRAGMA_REGVARADDR }, - { "regvars", PRAGMA_REGVARS }, /* obsolete */ { "rodata-name", PRAGMA_RODATA_NAME }, - { "rodataseg", PRAGMA_RODATASEG }, /* obsolete */ + { "rodata_name", PRAGMA_RODATA_NAME }, { "signed-chars", PRAGMA_SIGNED_CHARS }, - { "signedchars", PRAGMA_SIGNEDCHARS }, /* obsolete */ + { "signed_chars", PRAGMA_SIGNED_CHARS }, { "static-locals", PRAGMA_STATIC_LOCALS }, - { "staticlocals", PRAGMA_STATICLOCALS }, /* obsolete */ + { "static_locals", PRAGMA_STATIC_LOCALS }, { "warn", PRAGMA_WARN }, { "wrapped-call", PRAGMA_WRAPPED_CALL }, + { "wrapped_call", PRAGMA_WRAPPED_CALL }, { "writable-strings", PRAGMA_WRITABLE_STRINGS }, + { "writable_strings", PRAGMA_WRITABLE_STRINGS }, { "zpsym", PRAGMA_ZPSYM }, }; +#define PRAGMA_COUNT (sizeof (Pragmas) / sizeof (Pragmas[0])) /* Result of ParsePushPop */ typedef enum { @@ -142,6 +138,21 @@ typedef enum { PP_ERROR, } PushPopResult; +/* Effective scope of the pragma. +** This talks about how far the pragma has effects on whenever it shows up, +** even in the middle of an expression, statement or something. +*/ +typedef enum { + PES_NONE, + PES_IMM, /* No way back */ + PES_EXPR, /* Current expression/declarator */ + PES_STMT, /* Current statement/declaration */ + PES_SCOPE, /* Current scope */ + PES_FUNC, /* Current function */ + PES_FILE, /* Current file */ + PES_ALL, /* All */ +} pragma_scope_t; + /*****************************************************************************/ @@ -339,7 +350,7 @@ static void PushInt (IntStack* S, long Val) -static int BoolKeyword (StrBuf* Ident) +static int IsBoolKeyword (StrBuf* Ident) /* Check if the identifier in Ident is a keyword for a boolean value. Currently ** accepted are true/false/on/off. */ @@ -364,17 +375,88 @@ static int BoolKeyword (StrBuf* Ident) +static void ApplyPragma (int PushPop, IntStack* Stack, long Val) +/* Apply a pragma immediately */ +{ + if (PushPop > 0) { + /* Push the new value */ + PushInt (Stack, Val); + } else if (PushPop < 0) { + /* Pop the old value */ + PopInt (Stack); + } else { + /* Set the new value */ + IS_Set (Stack, Val); + } +} + + + +static void ApplySegNamePragma (pragma_t Token, int PushPop, const char* Name, unsigned char AddrSize) +/* Process a segname pragma */ +{ + segment_t Seg = SEG_CODE; + + switch (Token) { + case PRAGMA_CODE_NAME: + Seg = SEG_CODE; + break; + + case PRAGMA_RODATA_NAME: + Seg = SEG_RODATA; + break; + + case PRAGMA_DATA_NAME: + Seg = SEG_DATA; + break; + + case PRAGMA_BSS_NAME: + Seg = SEG_BSS; + break; + + default: + Internal ("Unknown segment name pragma: %02X", Token); + break; + } + + /* Set the new name */ + if (PushPop > 0) { + PushSegName (Seg, Name); + } else if (PushPop < 0) { + PopSegName (Seg); + } else { + SetSegName (Seg, Name); + } + + /* Set the optional address size for the segment if valid */ + if (PushPop >= 0 && AddrSize != ADDR_SIZE_INVALID) { + SetSegAddrSize (Name, AddrSize); + } + + /* BSS variables are output at the end of the compilation. Don't + ** bother to change their segment, now. + */ + if (Seg != SEG_BSS) { + g_segname (Seg); + } +} + + + /*****************************************************************************/ /* Pragma handling functions */ /*****************************************************************************/ -static void StringPragma (StrBuf* B, void (*Func) (const char*)) +static void StringPragma (pragma_scope_t Scope, StrBuf* B, void (*Func) (const char*)) /* Handle a pragma that expects a string parameter */ { StrBuf S = AUTO_STRBUF_INITIALIZER; + /* Only PES_IMM is supported */ + CHECK (Scope == PES_IMM); + /* We expect a string here */ if (GetString (B, &S)) { /* Call the given function with the string argument */ @@ -387,14 +469,17 @@ static void StringPragma (StrBuf* B, void (*Func) (const char*)) -static void SegNamePragma (StrBuf* B, segment_t Seg) +static void SegNamePragma (pragma_scope_t Scope, pragma_t Token, StrBuf* B) /* Handle a pragma that expects a segment name parameter */ { const char* Name; unsigned char AddrSize = ADDR_SIZE_INVALID; StrBuf S = AUTO_STRBUF_INITIALIZER; StrBuf A = AUTO_STRBUF_INITIALIZER; - int Push = 0; + int PushPop = 0; + + /* Unused at the moment */ + (void)Scope; /* Check for the "push" or "pop" keywords */ switch (ParsePushPop (B)) { @@ -403,19 +488,12 @@ static void SegNamePragma (StrBuf* B, segment_t Seg) break; case PP_PUSH: - Push = 1; + PushPop = 1; break; case PP_POP: /* Pop the old value and output it */ - PopSegName (Seg); - - /* BSS variables are output at the end of the compilation. Don't - ** bother to change their segment, now. - */ - if (Seg != SEG_BSS) { - g_segname (Seg); - } + ApplySegNamePragma (Token, -1, 0, 0); /* Done */ goto ExitPoint; @@ -454,27 +532,14 @@ static void SegNamePragma (StrBuf* B, segment_t Seg) /* Get the address size for the segment */ AddrSize = AddrSizeFromStr (SB_GetConstBuf (&A)); - /* Set the address size for the segment if valid */ - if (AddrSize != ADDR_SIZE_INVALID) { - SetSegAddrSize (Name, AddrSize); - } else { - Warning ("Invalid address size for segment!"); + /* Check the address size for the segment */ + if (AddrSize == ADDR_SIZE_INVALID) { + Warning ("Invalid address size for segment"); } } /* Set the new name and optionally address size */ - if (Push) { - PushSegName (Seg, Name); - } else { - SetSegName (Seg, Name); - } - - /* BSS variables are output at the end of the compilation. Don't - ** bother to change their segment, now. - */ - if (Seg != SEG_BSS) { - g_segname (Seg); - } + ApplySegNamePragma (Token, PushPop, Name, AddrSize); } else { @@ -484,13 +549,15 @@ static void SegNamePragma (StrBuf* B, segment_t Seg) } ExitPoint: + /* Call the string buf destructor */ SB_Done (&S); SB_Done (&A); } -static void WrappedCallPragma (StrBuf* B) + +static void WrappedCallPragma (pragma_scope_t Scope, StrBuf* B) /* Handle the wrapped-call pragma */ { StrBuf S = AUTO_STRBUF_INITIALIZER; @@ -498,6 +565,9 @@ static void WrappedCallPragma (StrBuf* B) long Val; SymEntry *Entry; + /* Only PES_IMM is supported */ + CHECK (Scope == PES_IMM); + /* Check for the "push" or "pop" keywords */ switch (ParsePushPop (B)) { @@ -553,7 +623,7 @@ static void WrappedCallPragma (StrBuf* B) Entry = FindSym(Name); /* Check if the name is valid */ - if (Entry && (Entry->Flags & SC_FUNC) == SC_FUNC) { + if (Entry && (Entry->Flags & SC_TYPEMASK) == SC_FUNC) { PushWrappedCall(Entry, (unsigned int) Val); Entry->Flags |= SC_REF; @@ -573,11 +643,14 @@ ExitPoint: -static void CharMapPragma (StrBuf* B) +static void CharMapPragma (pragma_scope_t Scope, StrBuf* B) /* Change the character map */ { long Index, C; + /* Only PES_IMM is supported */ + CHECK (Scope == PES_IMM); + /* Read the character index */ if (!GetNumber (B, &Index)) { return; @@ -619,7 +692,7 @@ static void CharMapPragma (StrBuf* B) -static void WarnPragma (StrBuf* B) +static void WarnPragma (pragma_scope_t Scope, StrBuf* B) /* Enable/disable warnings */ { long Val; @@ -627,6 +700,10 @@ static void WarnPragma (StrBuf* B) /* A warning name must follow */ IntStack* S = GetWarning (B); + + /* Only PES_IMM is supported */ + CHECK (Scope == PES_IMM); + if (S == 0) { return; } @@ -680,48 +757,47 @@ static void WarnPragma (StrBuf* B) -static void FlagPragma (StrBuf* B, IntStack* Stack) +static void FlagPragma (pragma_scope_t Scope, pragma_t Token, StrBuf* B, IntStack* Stack) /* Handle a pragma that expects a boolean parameter */ { StrBuf Ident = AUTO_STRBUF_INITIALIZER; long Val; - int Push; + int PushPop = 0; + /* Unused at the moment */ + (void)Scope; + (void)Token; /* Try to read an identifier */ int IsIdent = SB_GetSym (B, &Ident, 0); /* Check if we have a first argument named "pop" */ if (IsIdent && SB_CompareStr (&Ident, "pop") == 0) { - PopInt (Stack); + /* Pop the old value and bail out */ + ApplyPragma (-1, Stack, 0); + /* No other arguments allowed */ return; } /* Check if we have a first argument named "push" */ if (IsIdent && SB_CompareStr (&Ident, "push") == 0) { - Push = 1; + PushPop = 1; if (!GetComma (B)) { goto ExitPoint; } IsIdent = SB_GetSym (B, &Ident, 0); - } else { - Push = 0; } /* Boolean argument follows */ if (IsIdent) { - Val = BoolKeyword (&Ident); + Val = IsBoolKeyword (&Ident); } else if (!GetNumber (B, &Val)) { goto ExitPoint; } - /* Set/push the new value */ - if (Push) { - PushInt (Stack, Val); - } else { - IS_Set (Stack, Val); - } + /* Add this pragma and apply it whenever appropriately */ + ApplyPragma (PushPop, Stack, Val); ExitPoint: /* Free the identifier */ @@ -730,12 +806,16 @@ ExitPoint: -static void IntPragma (StrBuf* B, IntStack* Stack, long Low, long High) +static void IntPragma (pragma_scope_t Scope, pragma_t Token, StrBuf* B, IntStack* Stack, long Low, long High) /* Handle a pragma that expects an int parameter */ { long Val; int Push; + /* Unused at the moment */ + (void)Scope; + (void)Token; + /* Check for the "push" or "pop" keywords */ switch (ParsePushPop (B)) { @@ -749,7 +829,7 @@ static void IntPragma (StrBuf* B, IntStack* Stack, long Low, long High) case PP_POP: /* Pop the old value and bail out */ - PopInt (Stack); + ApplyPragma (-1, Stack, 0); return; case PP_ERROR: @@ -772,31 +852,32 @@ static void IntPragma (StrBuf* B, IntStack* Stack, long Low, long High) return; } - /* Set/push the new value */ - if (Push) { - PushInt (Stack, Val); - } else { - IS_Set (Stack, Val); - } + /* Add this pragma and apply it whenever appropriately */ + ApplyPragma (Push, Stack, Val); } -static void MakeMessage (const char* Message) +static void NoteMessagePragma (const char* Message) +/* Wrapper for printf-like Note() function protected from user-provided format +** specifiers. +*/ { Note ("%s", Message); } -static void ParsePragma (void) -/* Parse the contents of the _Pragma statement */ +static void ParsePragmaString (void) +/* Parse the contents of _Pragma */ { pragma_t Pragma; StrBuf Ident = AUTO_STRBUF_INITIALIZER; /* Create a string buffer from the string literal */ StrBuf B = AUTO_STRBUF_INITIALIZER; + + SB_Append (&B, GetLiteralStrBuf (CurTok.SVal)); /* Skip the string token */ @@ -837,111 +918,106 @@ static void ParsePragma (void) switch (Pragma) { case PRAGMA_ALIGN: - IntPragma (&B, &DataAlignment, 1, 4096); + /* TODO: PES_EXPR (PES_DECL) */ + IntPragma (PES_STMT, Pragma, &B, &DataAlignment, 1, 4096); break; case PRAGMA_ALLOW_EAGER_INLINE: - FlagPragma (&B, &EagerlyInlineFuncs); + FlagPragma (PES_STMT, Pragma, &B, &EagerlyInlineFuncs); break; - case PRAGMA_BSSSEG: - Warning ("#pragma bssseg is obsolete, please use #pragma bss-name instead"); - /* FALLTHROUGH */ case PRAGMA_BSS_NAME: - SegNamePragma (&B, SEG_BSS); + /* TODO: PES_STMT or even PES_EXPR (PES_DECL) maybe? */ + SegNamePragma (PES_FUNC, PRAGMA_BSS_NAME, &B); break; case PRAGMA_CHARMAP: - CharMapPragma (&B); + CharMapPragma (PES_IMM, &B); break; - case PRAGMA_CHECKSTACK: - Warning ("#pragma checkstack is obsolete, please use #pragma check-stack instead"); - /* FALLTHROUGH */ case PRAGMA_CHECK_STACK: - FlagPragma (&B, &CheckStack); + /* TODO: PES_SCOPE maybe? */ + FlagPragma (PES_FUNC, Pragma, &B, &CheckStack); break; - case PRAGMA_CODESEG: - Warning ("#pragma codeseg is obsolete, please use #pragma code-name instead"); - /* FALLTHROUGH */ case PRAGMA_CODE_NAME: - SegNamePragma (&B, SEG_CODE); + /* PES_FUNC is the only sensible option so far */ + SegNamePragma (PES_FUNC, PRAGMA_CODE_NAME, &B); break; case PRAGMA_CODESIZE: - IntPragma (&B, &CodeSizeFactor, 10, 1000); + /* PES_EXPR would be optimization nightmare */ + IntPragma (PES_STMT, Pragma, &B, &CodeSizeFactor, 10, 1000); break; - case PRAGMA_DATASEG: - Warning ("#pragma dataseg is obsolete, please use #pragma data-name instead"); - /* FALLTHROUGH */ case PRAGMA_DATA_NAME: - SegNamePragma (&B, SEG_DATA); + /* TODO: PES_STMT or even PES_EXPR (PES_DECL) maybe? */ + SegNamePragma (PES_FUNC, PRAGMA_DATA_NAME, &B); break; case PRAGMA_INLINE_STDFUNCS: - FlagPragma (&B, &InlineStdFuncs); + /* TODO: PES_EXPR maybe? */ + FlagPragma (PES_STMT, Pragma, &B, &InlineStdFuncs); break; case PRAGMA_LOCAL_STRINGS: - FlagPragma (&B, &LocalStrings); + /* TODO: PES_STMT or even PES_EXPR */ + FlagPragma (PES_FUNC, Pragma, &B, &LocalStrings); break; case PRAGMA_MESSAGE: - StringPragma (&B, MakeMessage); + /* PES_IMM is the only sensible option */ + StringPragma (PES_IMM, &B, NoteMessagePragma); break; case PRAGMA_OPTIMIZE: - FlagPragma (&B, &Optimize); + /* TODO: PES_STMT or even PES_EXPR maybe? */ + FlagPragma (PES_STMT, Pragma, &B, &Optimize); break; case PRAGMA_REGVARADDR: - FlagPragma (&B, &AllowRegVarAddr); + /* TODO: PES_STMT or even PES_EXPR maybe? */ + FlagPragma (PES_FUNC, Pragma, &B, &AllowRegVarAddr); break; - case PRAGMA_REGVARS: - Warning ("#pragma regvars is obsolete, please use #pragma register-vars instead"); - /* FALLTHROUGH */ case PRAGMA_REGISTER_VARS: - FlagPragma (&B, &EnableRegVars); + /* TODO: PES_STMT or even PES_EXPR (PES_DECL) maybe? */ + FlagPragma (PES_FUNC, Pragma, &B, &EnableRegVars); break; - case PRAGMA_RODATASEG: - Warning ("#pragma rodataseg is obsolete, please use #pragma rodata-name instead"); - /* FALLTHROUGH */ case PRAGMA_RODATA_NAME: - SegNamePragma (&B, SEG_RODATA); + /* TODO: PES_STMT or even PES_EXPR maybe? */ + SegNamePragma (PES_FUNC, PRAGMA_RODATA_NAME, &B); break; - case PRAGMA_SIGNEDCHARS: - Warning ("#pragma signedchars is obsolete, please use #pragma signed-chars instead"); - /* FALLTHROUGH */ case PRAGMA_SIGNED_CHARS: - FlagPragma (&B, &SignedChars); + /* TODO: PES_STMT or even PES_EXPR maybe? */ + FlagPragma (PES_FUNC, Pragma, &B, &SignedChars); break; - case PRAGMA_STATICLOCALS: - Warning ("#pragma staticlocals is obsolete, please use #pragma static-locals instead"); - /* FALLTHROUGH */ case PRAGMA_STATIC_LOCALS: - FlagPragma (&B, &StaticLocals); + /* TODO: PES_STMT or even PES_EXPR (PES_DECL) maybe? */ + FlagPragma (PES_FUNC, Pragma, &B, &StaticLocals); break; case PRAGMA_WRAPPED_CALL: - WrappedCallPragma(&B); + /* PES_IMM is the only sensible option */ + WrappedCallPragma (PES_IMM, &B); break; case PRAGMA_WARN: - WarnPragma (&B); + /* PES_IMM is the only sensible option */ + WarnPragma (PES_IMM, &B); break; case PRAGMA_WRITABLE_STRINGS: - FlagPragma (&B, &WritableStrings); + /* TODO: PES_STMT or even PES_EXPR maybe? */ + FlagPragma (PES_FUNC, Pragma, &B, &WritableStrings); break; case PRAGMA_ZPSYM: - StringPragma (&B, MakeZPSym); + /* PES_IMM is the only sensible option */ + StringPragma (PES_IMM, &B, MakeZPSym); break; default: @@ -975,20 +1051,31 @@ ExitPoint: -void DoPragma (void) -/* Handle pragmas. These come always in form of the new C99 _Pragma() operator. */ +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +void ConsumePragma (void) +/* Parse a pragma. The pragma comes always in the form of the new C99 _Pragma() +** operator. +*/ { - /* Skip the token itself */ + /* Skip the _Pragma token */ NextToken (); + /* Prevent from translating string literals in _Pragma */ + ++InPragmaParser; + /* We expect an opening paren */ if (!ConsumeLParen ()) { + --InPragmaParser; return; } /* String literal */ if (CurTok.Tok != TOK_SCONST) { - /* Print a diagnostic */ Error ("String literal expected"); @@ -996,13 +1083,13 @@ void DoPragma (void) ** enclosing paren, or a semicolon. */ PragmaErrorSkip (); - } else { - - /* Parse the _Pragma statement */ - ParsePragma (); + /* Parse the pragma */ + ParsePragmaString (); } + --InPragmaParser; + /* Closing paren needed */ ConsumeRParen (); } diff --git a/src/cc65/pragma.h b/src/cc65/pragma.h index d1b94fa23..55e907453 100644 --- a/src/cc65/pragma.h +++ b/src/cc65/pragma.h @@ -44,8 +44,10 @@ -void DoPragma (void); -/* Handle pragmas. These come always in form of the new C99 _Pragma() operator. */ +void ConsumePragma (void); +/* Parse a pragma. The pragma comes always in the form of the new C99 _Pragma() +** operator. +*/ diff --git a/src/cc65/preproc.c b/src/cc65/preproc.c index 0a9b94bf2..5cdec3142 100644 --- a/src/cc65/preproc.c +++ b/src/cc65/preproc.c @@ -842,7 +842,7 @@ static void AddPreLine (StrBuf* Str) SB_AppendChar (Str, '\n'); } SB_Printf (&Comment, "#line %u \"%s\"\n", - GetCurrentLineNum () - ContinuedLines, GetCurrentFilename ()); + GetCurrentLineNum () - ContinuedLines, GetCurrentFileName ()); SB_Append (Str, &Comment); } else { /* Output new lines */ @@ -2640,6 +2640,19 @@ static void DoDefine (void) goto Error_Handler; } NextChar (); + + } else { + + /* Object like macro. Check ISO/IEC 9899:1999 (E) 6.10.3p3: + ** "There shall be white-space between the identifier and the + ** replacement list in the definition of an object-like macro." + ** Note: C89 doesn't have this constraint. + ** Note: if there is no replacement list, a space is not required. + */ + if (Std == STD_C99 && !IsSpace (CurC) && CurC != 0) { + PPWarning ("ISO C99 requires whitespace after the macro name"); + } + } /* Remove whitespace and comments from the line, store the preprocessed @@ -2812,11 +2825,30 @@ static void DoInclude (void) InputType IT; StrBuf Filename = AUTO_STRBUF_INITIALIZER; - /* Macro-replace a single line with special support for <filename> */ - SB_Clear (MLine); - PreprocessDirective (Line, MLine, MSM_TOK_HEADER); + /* Skip whitespace so the input pointer points to the argument */ + SkipWhitespace (0); - /* Read from the processed line */ + /* We may have three forms of the #include directive: + ** + ** - # include "q-char-sequence" new-line + ** - # include <h-char-sequence> new-line + ** - # include pp-tokens new-line + ** + ** The former two are processed as is while the latter is preprocessed and + ** must then resemble one of the first two forms. + */ + if (CurC == '"' || CurC == '<') { + /* Copy the argument part over to MLine */ + unsigned Start = SB_GetIndex (Line); + unsigned Length = SB_GetLen (Line) - Start; + SB_Slice (MLine, Line, Start, Length); + } else { + /* Macro-replace a single line with special support for <filename> */ + SB_Clear (MLine); + PreprocessDirective (Line, MLine, MSM_TOK_HEADER); + } + + /* Read from the copied/preprocessed line */ SB_Reset (MLine); MLine = InitLine (MLine); @@ -2894,7 +2926,7 @@ static unsigned GetLineDirectiveNum (void) /* Ensure the buffer is terminated with a '\0' */ SB_Terminate (&Buf); - if (SkipWhitespace (0) != 0 || CurC == '\0') { + if (SB_GetLen (&Buf) > 0) { const char* Str = SB_GetConstBuf (&Buf); if (Str[0] == '\0') { PPWarning ("#line directive interprets number as decimal, not octal"); @@ -2910,9 +2942,10 @@ static unsigned GetLineDirectiveNum (void) } } } else { - PPError ("#line directive requires a simple decimal digit sequence"); + PPError ("#line directive requires a decimal digit sequence"); ClearLine (); } + SkipWhitespace (0); /* Done with the buffer */ SB_Done (&Buf); @@ -2943,7 +2976,7 @@ static void DoLine (void) StrBuf Filename = AUTO_STRBUF_INITIALIZER; if (SB_GetString (Line, &Filename)) { SB_Terminate (&Filename); - SetCurrentFilename (SB_GetConstBuf (&Filename)); + SetCurrentFileName (SB_GetConstBuf (&Filename)); } else { PPError ("Invalid filename for #line directive"); LineNum = 0; @@ -3220,7 +3253,7 @@ void HandleSpecialMacro (Macro* M, const char* Name) } else if (strcmp (Name, "__FILE__") == 0) { /* Replace __FILE__ with the current filename */ StrBuf B = AUTO_STRBUF_INITIALIZER; - SB_InitFromString (&B, GetCurrentFilename ()); + SB_InitFromString (&B, GetCurrentFileName ()); SB_Clear (&M->Replacement); Stringize (&B, &M->Replacement); SB_Done (&B); @@ -3332,7 +3365,7 @@ void Preprocess (void) PLine = InitLine (PLine); if (Verbosity > 1 && SB_NotEmpty (Line)) { - printf ("%s:%u: %.*s\n", GetCurrentFilename (), GetCurrentLineNum (), + printf ("%s:%u: %.*s\n", GetCurrentFileName (), GetCurrentLineNum (), (int) SB_GetLen (Line), SB_GetConstBuf (Line)); } diff --git a/src/cc65/scanner.c b/src/cc65/scanner.c index ede77cb2c..f0ff664fd 100644 --- a/src/cc65/scanner.c +++ b/src/cc65/scanner.c @@ -56,6 +56,7 @@ #include "ident.h" #include "input.h" #include "litpool.h" +#include "pragma.h" #include "preproc.h" #include "scanner.h" #include "standard.h" @@ -69,9 +70,12 @@ -Token CurTok; /* The current token */ -Token NextTok; /* The next token */ -int PPParserRunning; /* Is tokenizer used by the preprocessor */ +static Token SavedTok; /* Saved token */ +Token CurTok; /* The current token */ +Token NextTok; /* The next token */ +int PPParserRunning; /* Is tokenizer used by the preprocessor */ +int NoCharMap; /* Disable literal translation */ +unsigned InPragmaParser; /* Depth of pragma parser calling */ @@ -159,6 +163,12 @@ static const struct Keyword { typedef uint32_t scan_t; +/* ParseChar return values */ +typedef struct { + int Val; + int Cooked; +} parsedchar_t; + /*****************************************************************************/ /* code */ /*****************************************************************************/ @@ -322,13 +332,16 @@ static void SetTok (int tok) -static int ParseChar (void) -/* Parse a character. Converts escape chars into character codes. */ +static parsedchar_t ParseChar (void) +/* Parse a character token. Converts escape chars into character codes. */ { + parsedchar_t Result; int C; int HadError; int Count; + Result.Cooked = 1; + /* Check for escape chars */ if (CurC == '\\') { NextChar (); @@ -342,6 +355,14 @@ static int ParseChar (void) case 'b': C = '\b'; break; + case 'e': + if (IS_Get(&Standard) != STD_CC65) { + goto IllegalEscape; + } + /* we'd like to use \e here, but */ + /* not all build systems support it */ + C = '\x1B'; + break; case 'f': C = '\f'; break; @@ -369,6 +390,7 @@ static int ParseChar (void) case 'x': case 'X': /* Hex character constant */ + Result.Cooked = 0; if (!IsXDigit (NextC)) { Error ("\\x used with no following hex digits"); C = ' '; @@ -397,6 +419,7 @@ static int ParseChar (void) case '6': case '7': /* Octal constant */ + Result.Cooked = 0; Count = 1; C = HexVal (CurC); while (IsODigit (NextC) && Count++ < 3) { @@ -407,6 +430,7 @@ static int ParseChar (void) Error ("Octal character constant out of range"); break; default: +IllegalEscape: C = CurC; Error ("Illegal escaped character: 0x%02X", CurC); break; @@ -419,15 +443,20 @@ static int ParseChar (void) NextChar (); /* Do correct sign extension */ - return SignExtendChar (C); + Result.Val = SignExtendChar(C); + if (Result.Cooked) { + Result.Cooked = Result.Val; + } + + return Result; } static void CharConst (void) -/* Parse a character constant. */ +/* Parse a character constant token */ { - int C; + parsedchar_t C; if (CurC == 'L') { /* Wide character constant */ @@ -453,7 +482,8 @@ static void CharConst (void) } /* Translate into target charset */ - NextTok.IVal = SignExtendChar (TgtTranslateChar (C)); + NextTok.IVal = SignExtendChar (C.Val); + NextTok.Cooked = C.Cooked; /* Character constants have type int */ NextTok.Type = type_int; @@ -462,51 +492,46 @@ static void CharConst (void) static void StringConst (void) -/* Parse a quoted string */ +/* Parse a quoted string token */ { + /* result from ParseChar */ + parsedchar_t ParsedChar; + /* String buffer */ StrBuf S = AUTO_STRBUF_INITIALIZER; /* Assume next token is a string constant */ NextTok.Tok = TOK_SCONST; - /* Concatenate strings. If at least one of the concenated strings is a wide - ** character literal, the whole string is a wide char literal, otherwise - ** it's a normal string literal. - */ - while (1) { + /* Check if this is a normal or a wide char string */ + if (CurC == 'L' && NextC == '\"') { + /* Wide character literal */ + NextTok.Tok = TOK_WCSCONST; + NextChar (); + NextChar (); + } else if (CurC == '\"') { + /* Skip the quote char */ + NextChar (); + } else { + /* No string */ + goto ExitPoint; + } - /* Check if this is a normal or a wide char string */ - if (CurC == 'L' && NextC == '\"') { - /* Wide character literal */ - NextTok.Tok = TOK_WCSCONST; - NextChar (); - NextChar (); - } else if (CurC == '\"') { - /* Skip the quote char */ - NextChar (); - } else { - /* No string */ + /* Read until end of string */ + while (CurC != '\"') { + if (CurC == '\0') { + Error ("Unexpected newline"); break; } - - /* Read until end of string */ - while (CurC != '\"') { - if (CurC == '\0') { - Error ("Unexpected newline"); - break; - } - SB_AppendChar (&S, ParseChar ()); - } - - /* Skip closing quote char if there was one */ - NextChar (); - - /* Skip white space, read new input */ - SkipWhite (); - + ParsedChar = ParseChar (); + SB_AppendCharCooked(&S, ParsedChar.Val, ParsedChar.Cooked); } + /* Skip closing quote char if there was one */ + NextChar (); + +ExitPoint: + /* Terminate the string */ SB_AppendChar (&S, '\0'); @@ -520,7 +545,7 @@ static void StringConst (void) static void NumericConst (void) -/* Parse a numeric constant */ +/* Parse a numeric constant token */ { unsigned Base; /* Temporary number base according to prefix */ unsigned Index; @@ -694,6 +719,7 @@ static void NumericConst (void) /* Set the value and the token */ NextTok.IVal = IVal; + NextTok.Cooked = 0; NextTok.Tok = TOK_ICONST; } else { @@ -704,20 +730,21 @@ static void NumericConst (void) /* Check for a fractional part and read it */ if (SB_Peek (&Src) == '.') { - Double Scale; + Double Scale, ScaleDigit; /* Skip the dot */ SB_Skip (&Src); /* Read fractional digits */ - Scale = FP_D_Make (1.0); + ScaleDigit = FP_D_Div (FP_D_Make (1.0), FP_D_FromInt (Base)); + Scale = ScaleDigit; while (IsXDigit (SB_Peek (&Src)) && (DigitVal = HexVal (SB_Peek (&Src))) < Base) { /* Get the value of this digit */ - Double FracVal = FP_D_Div (FP_D_FromInt (DigitVal * Base), Scale); + Double FracVal = FP_D_Mul (FP_D_FromInt (DigitVal), Scale); /* Add it to the float value */ FVal = FP_D_Add (FVal, FracVal); - /* Scale base */ - Scale = FP_D_Mul (Scale, FP_D_FromInt (DigitVal)); + /* Adjust Scale for next digit */ + Scale = FP_D_Mul (Scale, ScaleDigit); /* Skip the digit */ SB_Skip (&Src); } @@ -729,12 +756,15 @@ static void NumericConst (void) unsigned Digits; unsigned Exp; + int Sign; /* Skip the exponent notifier */ SB_Skip (&Src); /* Read an optional sign */ + Sign = 0; if (SB_Peek (&Src) == '-') { + Sign = 1; SB_Skip (&Src); } else if (SB_Peek (&Src) == '+') { SB_Skip (&Src); @@ -764,9 +794,11 @@ static void NumericConst (void) Warning ("Floating constant exponent is too large"); } - /* Scale the exponent and adjust the value accordingly */ + /* Scale the exponent and adjust the value accordingly. + ** Decimal exponents are base 10, hexadecimal exponents are base 2 (C99). + */ if (Exp) { - FVal = FP_D_Mul (FVal, FP_D_Make (pow (10, Exp))); + FVal = FP_D_Mul (FVal, FP_D_Make (pow ((Base == 16) ? 2.0 : 10.0, (Sign ? -1.0 : 1.0) * Exp))); } } @@ -794,39 +826,54 @@ static void NumericConst (void) -void NextToken (void) +static void GetNextInputToken (void) /* Get next token from input stream */ { ident token; - /* We have to skip white space here before shifting tokens, since the - ** tokens and the current line info is invalid at startup and will get - ** initialized by reading the first time from the file. Remember if - ** we were at end of input and handle that later. - */ - int GotEOF = (SkipWhite() == 0); + if (!NoCharMap && !InPragmaParser) { + /* Translate string and character literals into target charset */ + if (NextTok.Tok == TOK_SCONST || NextTok.Tok == TOK_WCSCONST) { + TranslateLiteral (NextTok.SVal); + } else if (NextTok.Tok == TOK_CCONST || NextTok.Tok == TOK_WCCONST) { + if (NextTok.Cooked) { + NextTok.IVal = SignExtendChar (TgtTranslateChar (NextTok.IVal)); + } + else { + NextTok.IVal = SignExtendChar (NextTok.IVal); + } + } + } /* Current token is the lookahead token */ if (CurTok.LI) { ReleaseLineInfo (CurTok.LI); } + + /* Get the current token */ CurTok = NextTok; - /* When reading the first time from the file, the line info in NextTok, - ** which was copied to CurTok is invalid. Since the information from - ** the token is used for error messages, we must make it valid. - */ - if (CurTok.LI == 0) { - CurTok.LI = UseLineInfo (GetCurLineInfo ()); - } + if (SavedTok.Tok == TOK_INVALID) { + /* We have to skip white space here before shifting tokens, since the + ** tokens and the current line info is invalid at startup and will get + ** initialized by reading the first time from the file. Remember if we + ** were at end of input and handle that later. + */ + int GotEOF = (SkipWhite () == 0); - /* Remember the starting position of the next token */ - NextTok.LI = UseLineInfo (GetCurLineInfo ()); + /* Remember the starting position of the next token */ + NextTok.LI = UseLineInfo (GetCurLineInfo ()); - /* Now handle end of input. */ - if (GotEOF) { - /* End of file reached */ - NextTok.Tok = TOK_CEOF; + /* Now handle end of input */ + if (GotEOF) { + /* End of file reached */ + NextTok.Tok = TOK_CEOF; + return; + } + } else { + /* Just use the saved token */ + NextTok = SavedTok; + SavedTok.Tok = TOK_INVALID; return; } @@ -853,7 +900,8 @@ void NextToken (void) if (!PPParserRunning) { /* Check for a keyword */ - if ((NextTok.Tok = FindKey (token)) != TOK_IDENT) { + NextTok.Tok = FindKey (token); + if (NextTok.Tok != TOK_IDENT) { /* Reserved word found */ return; } @@ -1111,7 +1159,90 @@ void NextToken (void) UnknownChar (CurC); } +} + + +void NextToken (void) +/* Get next non-pragma token from input stream consuming any pragmas +** encountered. Adjacent string literal tokens will be concatenated. +*/ +{ + /* Used for string literal concatenation */ + Token PrevTok; + + /* When reading the first time from the file, the line info in NextTok, + ** which will be copied to CurTok is invalid. Since the information from + ** the token is used for error messages, we must make it valid. + */ + if (NextTok.LI == 0) { + NextTok.LI = UseLineInfo (GetCurLineInfo ()); + } + + PrevTok.Tok = TOK_INVALID; + while (1) { + /* Read the next token from the file */ + GetNextInputToken (); + + /* Consume all pragmas at hand, including those nested in a _Pragma() */ + if (CurTok.Tok == TOK_PRAGMA) { + /* Repeated and/or nested _Pragma()'s will be handled recursively */ + ConsumePragma (); + } + + /* Check for string concatenation */ + if (CurTok.Tok == TOK_SCONST || CurTok.Tok == TOK_WCSCONST) { + if (PrevTok.Tok == TOK_SCONST || PrevTok.Tok == TOK_WCSCONST) { + /* Warn on non-ISO behavior */ + if (InPragmaParser) { + Warning ("Concatenated string literals in _Pragma operation"); + } + + /* Concatenate strings */ + ConcatLiteral (PrevTok.SVal, CurTok.SVal); + + /* If at least one of the concatenated strings is a wide + ** character literal, the whole string is a wide char + ** literal, otherwise it is a normal string literal. + */ + if (CurTok.Tok == TOK_WCSCONST) { + PrevTok.Tok = TOK_WCSCONST; + PrevTok.Type = CurTok.Type; + } + } + + if (NextTok.Tok == TOK_SCONST || + NextTok.Tok == TOK_WCSCONST || + NextTok.Tok == TOK_PRAGMA) { + /* Remember current string literal token */ + if (PrevTok.Tok == TOK_INVALID) { + PrevTok = CurTok; + PrevTok.LI = UseLineInfo (PrevTok.LI); + } + + /* Keep looping */ + continue; + } + } + + break; + } + + /* Use the concatenated string literal token if there is one */ + if (PrevTok.Tok == TOK_SCONST || PrevTok.Tok == TOK_WCSCONST) { + if (CurTok.Tok != TOK_SCONST && CurTok.Tok != TOK_WCSCONST) { + /* Push back the incoming tokens */ + SavedTok = NextTok; + NextTok = CurTok; + } else { + /* The last string literal token can be just replaced */ + if (CurTok.LI) { + ReleaseLineInfo (CurTok.LI); + } + } + /* Replace the current token with the concatenated string literal */ + CurTok = PrevTok; + } } @@ -1140,6 +1271,212 @@ void SkipTokens (const token_t* TokenList, unsigned TokenCount) +static void OpenBrace (Collection* C, token_t Tok) +/* Consume an opening parenthesis/bracket/curly brace and remember that */ +{ + switch (Tok) { + case TOK_LPAREN: Tok = TOK_RPAREN; break; + case TOK_LBRACK: Tok = TOK_RBRACK; break; + case TOK_LCURLY: Tok = TOK_RCURLY; break; + default: Internal ("Unexpected opening token: %02X", (unsigned)Tok); + } + CollAppend (C, (void*)Tok); + NextToken (); +} + + + +static void PopBrace (Collection* C) +/* Close the latest open parenthesis/bracket/curly brace */ +{ + if (CollCount (C) > 0) { + CollPop (C); + } +} + + + +static int CloseBrace (Collection* C, token_t Tok) +/* Consume a closing parenthesis/bracket/curly brace if it is matched with an +** opening one to close and return 0, or bail out and return -1 if it is not +** matched. +*/ +{ + if (CollCount (C) > 0) { + token_t LastTok = (token_t)(intptr_t)CollLast (C); + if (LastTok == Tok) { + CollPop (C); + NextToken (); + return 0; + } + } + + return -1; +} + + + +int SmartErrorSkip (int TillEnd) +/* Try some smart error recovery. +** +** - If TillEnd == 0: +** Skip tokens until a comma or closing curly brace that is not enclosed in +** an open parenthesis/bracket/curly brace, or until a semicolon, EOF or +** unpaired right parenthesis/bracket/curly brace is reached. The closing +** curly brace is consumed in the former case. +** +** - If TillEnd != 0: +** Skip tokens until a right curly brace or semicolon is reached and consumed +** while there are no open parentheses/brackets/curly braces, or until an EOF +** is reached anytime. Any open parenthesis/bracket/curly brace is considered +** to be closed by consuming a right parenthesis/bracket/curly brace even if +** they didn't match. +** +** - Return -1: +** If this exits at a semicolon or unpaired right parenthesis/bracket/curly +** brace while there are still open parentheses/brackets/curly braces. +** +** - Return 0: +** If this exits as soon as it reaches an EOF; +** Or if this exits right after consuming a semicolon or right curly brace +** while there are no open parentheses/brackets/curly braces. +** +** - Return 1: +** If this exits at a non-EOF without consuming it. +*/ +{ + Collection C = AUTO_COLLECTION_INITIALIZER; + int Res = 0; + + /* Some fix point tokens that are used for error recovery */ + static const token_t TokenList[] = { TOK_COMMA, TOK_SEMI, + TOK_LPAREN, TOK_RPAREN, TOK_LBRACK, TOK_RBRACK, TOK_LCURLY, TOK_RCURLY }; + + while (CurTok.Tok != TOK_CEOF) { + SkipTokens (TokenList, sizeof (TokenList) / sizeof (TokenList[0])); + + switch (CurTok.Tok) { + case TOK_LPAREN: + case TOK_LBRACK: + case TOK_LCURLY: + OpenBrace (&C, CurTok.Tok); + break; + + case TOK_RPAREN: + case TOK_RBRACK: + if (CloseBrace (&C, CurTok.Tok) < 0) { + if (!TillEnd) { + Res = -1; + goto ExitPoint; + } + PopBrace (&C); + NextToken (); + } + break; + + case TOK_RCURLY: + if (CloseBrace (&C, CurTok.Tok) < 0) { + if (!TillEnd) { + Res = -1; + goto ExitPoint; + } + PopBrace (&C); + NextToken (); + } + if (CollCount (&C) == 0) { + /* We consider this as a terminator as well */ + Res = 0; + goto ExitPoint; + } + break; + + case TOK_COMMA: + if (CollCount (&C) == 0 && !TillEnd) { + Res = 1; + goto ExitPoint; + } + NextToken (); + break; + + case TOK_SEMI: + if (CollCount (&C) == 0) { + if (TillEnd) { + NextToken (); + Res = 0; + } else { + Res = 1; + } + goto ExitPoint; + } + NextToken (); + break; + + case TOK_CEOF: + /* We cannot consume this */ + Res = 0; + goto ExitPoint; + + default: + Internal ("Unexpected token: %02X", (unsigned)CurTok.Tok); + } + } + +ExitPoint: + DoneCollection (&C); + return Res; +} + + + +int SimpleErrorSkip (void) +/* Skip tokens until an EOF or unpaired right parenthesis/bracket/curly brace +** is reached. Return 0 If this exits at an EOF. Otherwise return -1. +*/ +{ + Collection C = AUTO_COLLECTION_INITIALIZER; + int Res = 0; + + /* Some fix point tokens that are used for error recovery */ + static const token_t TokenList[] = { + TOK_LPAREN, TOK_RPAREN, TOK_LBRACK, TOK_RBRACK, TOK_LCURLY, TOK_RCURLY }; + + while (CurTok.Tok != TOK_CEOF) { + SkipTokens (TokenList, sizeof (TokenList) / sizeof (TokenList[0])); + + switch (CurTok.Tok) { + case TOK_LPAREN: + case TOK_LBRACK: + case TOK_LCURLY: + OpenBrace (&C, CurTok.Tok); + break; + + case TOK_RPAREN: + case TOK_RBRACK: + case TOK_RCURLY: + if (CloseBrace (&C, CurTok.Tok) < 0) { + /* Found a terminator */ + Res = -1; + goto ExitPoint; + } + break; + + case TOK_CEOF: + /* We cannot go any farther */ + Res = 0; + goto ExitPoint; + + default: + Internal ("Unexpected token: %02X", (unsigned)CurTok.Tok); + } + } + +ExitPoint: + DoneCollection (&C); + return Res; +} + + + int Consume (token_t Token, const char* ErrorMsg) /* Eat token if it is the next in the input stream, otherwise print an error ** message. Returns true if the token was found and false otherwise. diff --git a/src/cc65/scanner.h b/src/cc65/scanner.h index 338ad6a65..a8b8a8ab2 100644 --- a/src/cc65/scanner.h +++ b/src/cc65/scanner.h @@ -76,6 +76,7 @@ typedef enum token_t { /* Function specifiers */ TOK_INLINE, + TOK_NORETURN, TOK_FASTCALL, TOK_CDECL, @@ -212,6 +213,7 @@ typedef struct Token Token; struct Token { token_t Tok; /* The token itself */ long IVal; /* The integer attribute */ + int Cooked; /* The "cooked" flag for char constants */ Double FVal; /* The float attribute */ struct Literal* SVal; /* String literal is any */ ident Ident; /* Identifier if IDENT */ @@ -219,9 +221,11 @@ struct Token { const Type* Type; /* Type if integer or float constant */ }; -extern Token CurTok; /* The current token */ -extern Token NextTok; /* The next token */ -extern int PPParserRunning; /* Is tokenizer used by the preprocessor */ +extern Token CurTok; /* The current token */ +extern Token NextTok; /* The next token */ +extern int PPParserRunning; /* Is tokenizer used by the preprocessor */ +extern int NoCharMap; /* Disable literal translation */ +extern unsigned InPragmaParser; /* Depth of pragma parser calling */ @@ -299,13 +303,49 @@ void CopyPPNumber (StrBuf* Target); /* Copy a pp-number from the input to Target */ void NextToken (void); -/* Get next token from input stream */ +/* Get next non-pragma token from input stream consuming any pragmas +** encountered. Adjacent string literal tokens will be concatenated. +*/ void SkipTokens (const token_t* TokenList, unsigned TokenCount); /* Skip tokens until we reach TOK_CEOF or a token in the given token list. ** This routine is used for error recovery. */ +int SmartErrorSkip (int TillEnd); +/* Try some smart error recovery. +** +** - If TillEnd == 0: +** Skip tokens until a comma or closing curly brace that is not enclosed in +** an open parenthesis/bracket/curly brace, or until a semicolon, EOF or +** unpaired right parenthesis/bracket/curly brace is reached. The closing +** curly brace is consumed in the former case. +** +** - If TillEnd != 0: +** Skip tokens until a right curly brace or semicolon is reached and consumed +** while there are no open parentheses/brackets/curly braces, or until an EOF +** is reached anytime. Any open parenthesis/bracket/curly brace is considered +** to be closed by consuming a right parenthesis/bracket/curly brace even if +** they didn't match. +** +** - Return -1: +** If this exits at a semicolon or unpaired right parenthesis/bracket/curly +** brace while there are still open parentheses/brackets/curly braces. +** +** - Return 0: +** If this exits as soon as it reaches an EOF; +** Or if this exits right after consuming a semicolon or right curly brace +** while there are no open parentheses/brackets/curly braces. +** +** - Return 1: +** If this exits at a non-EOF without consuming it. +*/ + +int SimpleErrorSkip (void); +/* Skip tokens until an EOF or unpaired right parenthesis/bracket/curly brace +** is reached. Return 0 If this exits at an EOF. Otherwise return -1. +*/ + int Consume (token_t Token, const char* ErrorMsg); /* Eat token if it is the next in the input stream, otherwise print an error ** message. Returns true if the token was found and false otherwise. diff --git a/src/cc65/segments.c b/src/cc65/segments.c index 8283b9da5..764ae29ea 100644 --- a/src/cc65/segments.c +++ b/src/cc65/segments.c @@ -69,11 +69,11 @@ typedef struct { } SegAddrSize_t; -/* Pointer to the current segment list. Output goes here. */ -Segments* CS = 0; +/* Pointer to the current segment context. Output goes here. */ +SegContext* CS = 0; -/* Pointer to the global segment list */ -Segments* GS = 0; +/* Pointer to the global segment context */ +SegContext* GS = 0; /* Actual names for the segments */ static StrStack SegmentNames[SEG_COUNT]; @@ -86,12 +86,12 @@ static Collection SegmentAddrSizes; ** maximum stack depth is 2, so there is not really a need for a better ** implementation. */ -static Collection SegmentStack = STATIC_COLLECTION_INITIALIZER; +static Collection SegContextStack = STATIC_COLLECTION_INITIALIZER; /*****************************************************************************/ -/* Code */ +/* Segment name and address size */ /*****************************************************************************/ @@ -226,11 +226,17 @@ const char* GetSegName (segment_t Seg) -static Segments* NewSegments (SymEntry* Func) -/* Initialize a Segments structure (set all fields to NULL) */ +/*****************************************************************************/ +/* Segment context */ +/*****************************************************************************/ + + + +static SegContext* NewSegContext (SymEntry* Func) +/* Initialize a SegContext structure (set all fields to NULL) */ { /* Allocate memory */ - Segments* S = xmalloc (sizeof (Segments)); + SegContext* S = xmalloc (sizeof (SegContext)); /* Initialize the fields */ S->Text = NewTextSeg (Func); @@ -248,14 +254,14 @@ static Segments* NewSegments (SymEntry* Func) -Segments* PushSegments (SymEntry* Func) -/* Make the new segment list current but remember the old one */ +SegContext* PushSegContext (SymEntry* Func) +/* Make the new segment context current but remember the old one */ { /* Push the current pointer onto the stack */ - CollAppend (&SegmentStack, CS); + CollAppend (&SegContextStack, CS); - /* Create a new Segments structure */ - CS = NewSegments (Func); + /* Create a new SegContext structure */ + CS = NewSegContext (Func); /* Return the new struct */ return CS; @@ -263,14 +269,14 @@ Segments* PushSegments (SymEntry* Func) -void PopSegments (void) -/* Pop the old segment list (make it current) */ +void PopSegContext (void) +/* Pop the old segment context (make it current) */ { /* Must have something on the stack */ - PRECONDITION (CollCount (&SegmentStack) > 0); + PRECONDITION (CollCount (&SegContextStack) > 0); /* Pop the last segment and set it as current */ - CS = CollPop (&SegmentStack); + CS = CollPop (&SegContextStack); } @@ -278,13 +284,13 @@ void PopSegments (void) void CreateGlobalSegments (void) /* Create the global segments and remember them in GS */ { - GS = PushSegments (0); + GS = PushSegContext (0); } void UseDataSeg (segment_t DSeg) -/* For the current segment list, use the data segment DSeg */ +/* For the current segment context, use the data segment DSeg */ { /* Check the input */ PRECONDITION (CS && DSeg != SEG_CODE); @@ -372,7 +378,7 @@ void RemoveGlobalCode (void) -void OutputSegments (const Segments* S) +void OutputSegments (const SegContext* S) /* Output the given segments to the output file */ { /* Output the function prologue if the segments came from a function */ diff --git a/src/cc65/segments.h b/src/cc65/segments.h index 91d702df6..d7c9e3c11 100644 --- a/src/cc65/segments.h +++ b/src/cc65/segments.h @@ -78,8 +78,8 @@ typedef enum segment_t { } segment_t; /* A list of all segments used when generating code */ -typedef struct Segments Segments; -struct Segments { +typedef struct SegContext SegContext; +struct SegContext { struct TextSeg* Text; /* Text segment */ struct CodeSeg* Code; /* Code segment */ struct DataSeg* Data; /* Data segment */ @@ -90,11 +90,11 @@ struct Segments { unsigned NextDataLabel; /* Number to generate unique data labels */ }; -/* Pointer to the current segment list. Output goes here. */ -extern Segments* CS; +/* Pointer to the current segment context. Output goes here. */ +extern SegContext* CS; -/* Pointer to the global segment list */ -extern Segments* GS; +/* Pointer to the global segment context */ +extern SegContext* GS; @@ -132,17 +132,17 @@ void PopSegName (segment_t Seg); const char* GetSegName (segment_t Seg); /* Get the name of the given segment */ -Segments* PushSegments (struct SymEntry* Func); -/* Make the new segment list current but remember the old one */ +SegContext* PushSegContext (struct SymEntry* Func); +/* Make the new segment context current but remember the old one */ -void PopSegments (void); -/* Pop the old segment list (make it current) */ +void PopSegContext (void); +/* Pop the old segment context (make it current) */ void CreateGlobalSegments (void); /* Create the global segments and remember them in GS */ void UseDataSeg (segment_t DSeg); -/* For the current segment list, use the data segment DSeg */ +/* For the current segment context, use the data segment DSeg */ struct DataSeg* GetDataSeg (void); /* Return the current data segment */ @@ -165,7 +165,7 @@ int HaveGlobalCode (void); void RemoveGlobalCode (void); /* Remove all code from the global code segment. Used for error recovery. */ -void OutputSegments (const Segments* S); +void OutputSegments (const SegContext* S); /* Output the given segments to the output file */ diff --git a/libsrc/common/asctime.c b/src/cc65/seqpoint.c similarity index 72% rename from libsrc/common/asctime.c rename to src/cc65/seqpoint.c index b46f29128..a6d992113 100644 --- a/libsrc/common/asctime.c +++ b/src/cc65/seqpoint.c @@ -1,15 +1,12 @@ /*****************************************************************************/ /* */ -/* asctime.c */ +/* seqpoint.h */ /* */ -/* Convert a broken down time into a string */ +/* Stuff involved in sequence points */ /* */ /* */ /* */ -/* (C) 2002 Ullrich von Bassewitz */ -/* Wacholderweg 14 */ -/* D-70597 Stuttgart */ -/* EMail: uz@musoftware.de */ +/* Copyright 2022 The cc65 Authors */ /* */ /* */ /* This software is provided 'as-is', without any expressed or implied */ @@ -33,27 +30,38 @@ -#include <stdio.h> -#include <time.h> +/* cc65 */ +#include "seqpoint.h" /*****************************************************************************/ -/* Code */ +/* data */ /*****************************************************************************/ -/* - CAUTION: we need to reserve enough space to be able to hold the maximum - length string: - 1234567890123456789012345678901234567 - "Wednesday September ..1 00:00:00 1970" -*/ -char* __fastcall__ asctime (const struct tm* timep) +/* Remeber if __A__, __AX__ and __EAX__ are being used */ +unsigned PendingSqpFlags = SQP_KEEP_NONE; + + + +/*****************************************************************************/ +/* code */ +/*****************************************************************************/ + + + +void SetSQPFlags (unsigned Flags) +/* Set the SQP_KEEP_* flags for the deferred operations in the statement */ { - static char buf[38]; - - /* Format into given buffer and return the result */ - return strftime (buf, sizeof (buf), "%c\n", timep)? buf : 0; + PendingSqpFlags = Flags; +} + + + +unsigned GetSQPFlags (void) +/* Get the SQP_KEEP_* flags for the deferred operations in the statement */ +{ + return PendingSqpFlags; } diff --git a/libsrc/apple2/rewinddir.c b/src/cc65/seqpoint.h similarity index 69% rename from libsrc/apple2/rewinddir.c rename to src/cc65/seqpoint.h index 3329852d8..07ff0d315 100644 --- a/libsrc/apple2/rewinddir.c +++ b/src/cc65/seqpoint.h @@ -1,12 +1,12 @@ /*****************************************************************************/ /* */ -/* rewinddir.c */ +/* seqpoint.h */ /* */ -/* Reset directory stream */ +/* Stuff involved in sequence points */ /* */ /* */ /* */ -/* (C) 2005 Oliver Schmidt, <ol.sc@web.de> */ +/* Copyright 2022 The cc65 Authors */ /* */ /* */ /* This software is provided 'as-is', without any expressed or implied */ @@ -30,40 +30,41 @@ -#include <stdio.h> -#include <unistd.h> -#include <string.h> -#include <dirent.h> -#include "dir.h" +#ifndef SEQPOINT_H +#define SEQPOINT_H /*****************************************************************************/ -/* Code */ +/* data */ /*****************************************************************************/ -void __fastcall__ rewinddir (register DIR* dir) -{ - /* Rewind directory file */ - if (lseek (dir->fd, 0, SEEK_SET)) { +#define SQP_KEEP_NONE 0x00U +#define SQP_KEEP_A 0x01U +#define SQP_KEEP_AX 0x03U +#define SQP_KEEP_EAX 0x07U +#define SQP_MASK_EAX 0x07U +#define SQP_KEEP_TEST 0x10U +#define SQP_KEEP_EXPR 0x17U /* SQP_KEEP_TEST | SQP_KEEP_EAX */ - /* Read directory key block */ - if (read (dir->fd, - dir->block.bytes, - sizeof (dir->block)) == sizeof (dir->block)) { - /* Skip directory header entry */ - dir->current_entry = 1; - /* Return success */ - return; - } - } +/*****************************************************************************/ +/* code */ +/*****************************************************************************/ - /* Assert that no subsequent readdir() finds an active entry */ - memset (dir->block.bytes, 0, sizeof (dir->block)); - /* Return failure */ -} + +void SetSQPFlags (unsigned Flags); +/* Set the SQP_KEEP_* flags for the deferred operations in the statement */ + +unsigned GetSQPFlags (void); +/* Get the SQP_KEEP_* flags for the deferred operations in the statement */ + + + +/* End of seqpoint.h */ + +#endif diff --git a/src/cc65/shiftexpr.c b/src/cc65/shiftexpr.c index 1224bfecb..e6fed87e8 100644 --- a/src/cc65/shiftexpr.c +++ b/src/cc65/shiftexpr.c @@ -95,14 +95,14 @@ void ShiftExpr (struct ExprDesc* Expr) ResultType = IntPromotion (Expr->Type); /* Prepare the code generator flags */ - GenFlags = TypeOf (ResultType); + GenFlags = CG_TypeOf (ResultType); /* Calculate the number of bits the lhs operand has */ ExprBits = SizeOf (ResultType) * 8; /* Get the lhs on stack */ GetCodePos (&Mark1); - ltype = TypeOf (Expr->Type); + ltype = CG_TypeOf (Expr->Type); lconst = ED_IsConstAbs (Expr); if (lconst) { /* Constant value */ diff --git a/src/cc65/staticassert.c b/src/cc65/staticassert.c index 1bf8dd4c5..9df9af7da 100644 --- a/src/cc65/staticassert.c +++ b/src/cc65/staticassert.c @@ -45,7 +45,7 @@ -void ParseStaticAssert () +void ParseStaticAssert (void) { /* ** static_assert-declaration ::= @@ -53,44 +53,50 @@ void ParseStaticAssert () ** _Static_assert ( constant-expression , string-literal ) ; */ ExprDesc Expr; - int failed; + unsigned PrevErrorCount = ErrorCount; + int failed = 0; /* Skip the _Static_assert token itself */ CHECK (CurTok.Tok == TOK_STATIC_ASSERT); NextToken (); /* We expect an opening paren */ - if (!ConsumeLParen ()) { - return; + if (ConsumeLParen ()) { + /* Parse assertion condition */ + Expr = NoCodeConstAbsIntExpr (hie1); + failed = !Expr.IVal; } - /* Parse assertion condition */ - Expr = NoCodeConstAbsIntExpr (hie1); - failed = !Expr.IVal; + if (PrevErrorCount != ErrorCount) { + goto ExitPoint; + } /* If there is a comma, we also have an error message. The message is optional because we ** support the C2X syntax with only an expression. */ if (CurTok.Tok == TOK_COMMA) { - /* Skip the comma. */ + /* Prevent from translating the message string literal */ + NoCharMap = 1; + + /* Skip the comma and get the next token */ NextToken (); + /* Reenable string literal translation */ + NoCharMap = 0; + /* String literal */ if (CurTok.Tok != TOK_SCONST) { Error ("String literal expected for static_assert message"); - return; - } + } else { + /* Issue an error including the message if the static_assert failed. */ + if (failed) { + Error ("static_assert failed '%s'", GetLiteralStr (CurTok.SVal)); + } - /* Issue an error including the message if the static_assert failed. */ - if (failed) { - Error ("static_assert failed '%s'", GetLiteralStr (CurTok.SVal)); - } - - /* Consume the string constant, now that we don't need it anymore. - ** This should never fail since we checked the token type above. - */ - if (!Consume (TOK_SCONST, "String literal expected")) { - return; + /* Consume the string constant, now that we don't need it anymore. + ** This should never fail since we checked the token type above. + */ + Consume (TOK_SCONST, "String literal expected"); } } else { /* No message. */ @@ -99,7 +105,24 @@ void ParseStaticAssert () } } - /* Closing paren and semi-colon needed */ - ConsumeRParen (); - ConsumeSemi (); + /* The assertion failure error is not a syntax error */ + if (failed) { + ++PrevErrorCount; + } + + if (PrevErrorCount == ErrorCount) { + /* Closing paren needed */ + ConsumeRParen (); + } + + if (PrevErrorCount == ErrorCount) { + /* Must be followed by a semicolon */ + ConsumeSemi (); + } + +ExitPoint: + /* Try some smart error recovery */ + if (PrevErrorCount != ErrorCount) { + SmartErrorSkip (1); + } } diff --git a/src/cc65/stdfunc.c b/src/cc65/stdfunc.c index d22c73dcf..2889a176e 100644 --- a/src/cc65/stdfunc.c +++ b/src/cc65/stdfunc.c @@ -50,6 +50,7 @@ #include "litpool.h" #include "loadexpr.h" #include "scanner.h" +#include "seqpoint.h" #include "stackptr.h" #include "stdfunc.h" #include "stdnames.h" @@ -184,7 +185,7 @@ static void ParseArg (ArgDesc* Arg, const Type* Type, ExprDesc* Expr) GetCodePos (&Arg->End); /* Use the type of the argument for the push */ - Arg->Flags |= TypeOf (Arg->Expr.Type); + Arg->Flags |= CG_TypeOf (Arg->Expr.Type); /* Propagate from subexpressions */ Expr->Flags |= Arg->Expr.Flags & E_MASK_VIRAL; @@ -279,13 +280,11 @@ static void StdFunc_memcpy (FuncDesc* F attribute ((unused)), ExprDesc* Expr) ** generated, and emit better code. */ if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 && - ((ED_IsRVal (&Arg2.Expr) && ED_IsLocConst (&Arg2.Expr)) || - (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr))) && - ((ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) || - (ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr)))) { + (ED_IsConstAddr (&Arg2.Expr) || ED_IsZPInd (&Arg2.Expr)) && + (ED_IsConstAddr (&Arg1.Expr) || ED_IsZPInd (&Arg1.Expr))) { - int Reg1 = ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr); - int Reg2 = ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr); + int Reg1 = ED_IsZPInd (&Arg1.Expr); + int Reg2 = ED_IsZPInd (&Arg2.Expr); /* Drop the generated code */ RemoveCode (&Arg1.Expr.Start); @@ -341,9 +340,9 @@ static void StdFunc_memcpy (FuncDesc* F attribute ((unused)), ExprDesc* Expr) } if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 && - ED_IsRVal (&Arg2.Expr) && ED_IsLocConst (&Arg2.Expr) && - ED_IsRVal (&Arg1.Expr) && ED_IsLocStack (&Arg1.Expr) && - (Arg1.Expr.IVal - StackPtr) + Arg3.Expr.IVal < 256) { + ED_IsConstAddr (&Arg2.Expr) && + ED_IsStackAddr (&Arg1.Expr) && + ED_GetStackOffs (&Arg1.Expr, Arg3.Expr.IVal) < 256) { /* It is possible to just use one index register even if the stack ** offset is not zero, by adjusting the offset to the constant @@ -352,7 +351,7 @@ static void StdFunc_memcpy (FuncDesc* F attribute ((unused)), ExprDesc* Expr) ** than 256. Register space is zero page, which means that the ** address calculation could overflow in the linker. */ - int AllowOneIndex = !ED_IsLocRegister (&Arg2.Expr) && + int AllowOneIndex = !ED_IsLocZP (&Arg2.Expr) && !(ED_IsLocNone (&Arg2.Expr) && Arg2.Expr.IVal < 256); /* Calculate the real stack offset */ @@ -419,9 +418,9 @@ static void StdFunc_memcpy (FuncDesc* F attribute ((unused)), ExprDesc* Expr) } if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 && - ED_IsRVal (&Arg2.Expr) && ED_IsLocStack (&Arg2.Expr) && - (Arg2.Expr.IVal - StackPtr) + Arg3.Expr.IVal < 256 && - ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) { + ED_IsStackAddr (&Arg2.Expr) && + ED_GetStackOffs (&Arg2.Expr, Arg3.Expr.IVal) < 256 && + ED_IsConstAddr (&Arg1.Expr)) { /* It is possible to just use one index register even if the stack ** offset is not zero, by adjusting the offset to the constant @@ -430,7 +429,7 @@ static void StdFunc_memcpy (FuncDesc* F attribute ((unused)), ExprDesc* Expr) ** than 256. Register space is zero page, which means that the ** address calculation could overflow in the linker. */ - int AllowOneIndex = !ED_IsLocRegister (&Arg1.Expr) && + int AllowOneIndex = !ED_IsLocZP (&Arg1.Expr) && !(ED_IsLocNone (&Arg1.Expr) && Arg1.Expr.IVal < 256); /* Calculate the real stack offset */ @@ -496,8 +495,8 @@ static void StdFunc_memcpy (FuncDesc* F attribute ((unused)), ExprDesc* Expr) goto ExitPoint; } - if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 && - ED_IsRVal (&Arg2.Expr) && ED_IsLocStack (&Arg2.Expr) && + if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 && + ED_IsStackAddr (&Arg2.Expr) && (Offs = ED_GetStackOffs (&Arg2.Expr, 0)) == 0) { /* Drop the generated code but leave the load of the first argument*/ @@ -636,10 +635,9 @@ static void StdFunc_memset (FuncDesc* F attribute ((unused)), ExprDesc* Expr) */ if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 && ED_IsConstAbsInt (&Arg2.Expr) && - ((ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) || - (ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr)))) { + (ED_IsConstAddr (&Arg1.Expr) || ED_IsZPInd (&Arg1.Expr))) { - int Reg = ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr); + int Reg = ED_IsZPInd (&Arg1.Expr); /* Drop the generated code */ RemoveCode (&Arg1.Expr.Start); @@ -688,8 +686,8 @@ static void StdFunc_memset (FuncDesc* F attribute ((unused)), ExprDesc* Expr) if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 && ED_IsConstAbsInt (&Arg2.Expr) && - ED_IsRVal (&Arg1.Expr) && ED_IsLocStack (&Arg1.Expr) && - (Arg1.Expr.IVal - StackPtr) + Arg3.Expr.IVal < 256) { + ED_IsStackAddr (&Arg1.Expr) && + ED_GetStackOffs (&Arg1.Expr, Arg3.Expr.IVal) < 256) { /* Calculate the real stack offset */ int Offs = ED_GetStackOffs (&Arg1.Expr, 0); @@ -835,7 +833,7 @@ static void StdFunc_strcmp (FuncDesc* F attribute ((unused)), ExprDesc* Expr) */ if (ED_IsLocLiteral (&Arg2.Expr) && IS_Get (&WritableStrings) == 0 && - GetLiteralSize (Arg2.Expr.V.LVal) == 1 && + GetLiteralSize (Arg2.Expr.V.LVal) >= 1 && GetLiteralStr (Arg2.Expr.V.LVal)[0] == '\0') { /* Drop the generated code so we have the first argument in the @@ -849,7 +847,7 @@ static void StdFunc_strcmp (FuncDesc* F attribute ((unused)), ExprDesc* Expr) /* We do now have Arg1 in the primary. Load the first character from ** this string and cast to int. This is the function result. */ - IsArray = IsTypeArray (Arg1.Type) && ED_IsRVal (&Arg1.Expr); + IsArray = IsTypeArray (Arg1.Type) && ED_IsAddrExpr (&Arg1.Expr); if (IsArray && ED_IsLocStack (&Arg1.Expr) && (Offs = ED_GetStackOffs (&Arg1.Expr, 0) < 256)) { /* Drop the generated code */ @@ -877,22 +875,20 @@ static void StdFunc_strcmp (FuncDesc* F attribute ((unused)), ExprDesc* Expr) } } else if ((IS_Get (&CodeSizeFactor) >= 165) && - ((ED_IsRVal (&Arg2.Expr) && ED_IsLocConst (&Arg2.Expr)) || - (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr))) && - ((ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) || - (ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr))) && + (ED_IsConstAddr (&Arg2.Expr) || ED_IsZPInd (&Arg2.Expr)) && + (ED_IsConstAddr (&Arg1.Expr) || ED_IsZPInd (&Arg1.Expr)) && (IS_Get (&EagerlyInlineFuncs) || (ECount1 > 0 && ECount1 < 256))) { unsigned Entry, Loop, Fin; /* Labels */ const char* Load; const char* Compare; - if (ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr)) { + if (ED_IsZPInd (&Arg1.Expr)) { Load = "lda (%s),y"; } else { Load = "lda %s,y"; } - if (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr)) { + if (ED_IsZPInd (&Arg2.Expr)) { Compare = "cmp (%s),y"; } else { Compare = "cmp %s,y"; @@ -923,14 +919,13 @@ static void StdFunc_strcmp (FuncDesc* F attribute ((unused)), ExprDesc* Expr) g_defcodelabel (Fin); } else if ((IS_Get (&CodeSizeFactor) > 190) && - ((ED_IsRVal (&Arg2.Expr) && ED_IsLocConst (&Arg2.Expr)) || - (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr))) && + (ED_IsConstAddr (&Arg2.Expr) || ED_IsZPInd (&Arg2.Expr)) && (IS_Get (&EagerlyInlineFuncs) || (ECount1 > 0 && ECount1 < 256))) { unsigned Entry, Loop, Fin; /* Labels */ const char* Compare; - if (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr)) { + if (ED_IsZPInd (&Arg2.Expr)) { Compare = "cmp (%s),y"; } else { Compare = "cmp %s,y"; @@ -1027,21 +1022,19 @@ static void StdFunc_strcpy (FuncDesc* F attribute ((unused)), ExprDesc* Expr) ** be generated. If such a situation is detected, throw away the ** generated, and emit better code. */ - if (((ED_IsRVal (&Arg2.Expr) && ED_IsLocConst (&Arg2.Expr)) || - (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr))) && - ((ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) || - (ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr))) && + if ((ED_IsConstAddr (&Arg2.Expr) || ED_IsZPInd (&Arg2.Expr)) && + (ED_IsConstAddr (&Arg1.Expr) || ED_IsZPInd (&Arg1.Expr)) && (IS_Get (&EagerlyInlineFuncs) || (ECount != UNSPECIFIED && ECount < 256))) { const char* Load; const char* Store; - if (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr)) { + if (ED_IsZPInd (&Arg2.Expr)) { Load = "lda (%s),y"; } else { Load = "lda %s,y"; } - if (ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr)) { + if (ED_IsZPInd (&Arg1.Expr)) { Store = "sta (%s),y"; } else { Store = "sta %s,y"; @@ -1068,9 +1061,9 @@ static void StdFunc_strcpy (FuncDesc* F attribute ((unused)), ExprDesc* Expr) goto ExitPoint; } - if (ED_IsRVal (&Arg2.Expr) && ED_IsLocStack (&Arg2.Expr) && + if (ED_IsStackAddr (&Arg2.Expr) && StackPtr >= -255 && - ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) { + ED_IsConstAddr (&Arg1.Expr)) { /* It is possible to just use one index register even if the stack ** offset is not zero, by adjusting the offset to the constant @@ -1079,7 +1072,7 @@ static void StdFunc_strcpy (FuncDesc* F attribute ((unused)), ExprDesc* Expr) ** than 256. Register space is zero page, which means that the ** address calculation could overflow in the linker. */ - int AllowOneIndex = !ED_IsLocRegister (&Arg1.Expr) && + int AllowOneIndex = !ED_IsLocZP (&Arg1.Expr) && !(ED_IsLocNone (&Arg1.Expr) && Arg1.Expr.IVal < 256); /* Calculate the real stack offset */ @@ -1115,8 +1108,8 @@ static void StdFunc_strcpy (FuncDesc* F attribute ((unused)), ExprDesc* Expr) goto ExitPoint; } - if (ED_IsRVal (&Arg2.Expr) && ED_IsLocConst (&Arg2.Expr) && - ED_IsRVal (&Arg1.Expr) && ED_IsLocStack (&Arg1.Expr) && + if (ED_IsConstAddr (&Arg2.Expr) && + ED_IsStackAddr (&Arg1.Expr) && StackPtr >= -255) { /* It is possible to just use one index register even if the stack @@ -1126,7 +1119,7 @@ static void StdFunc_strcpy (FuncDesc* F attribute ((unused)), ExprDesc* Expr) ** than 256. Register space is zero page, which means that the ** address calculation could overflow in the linker. */ - int AllowOneIndex = !ED_IsLocRegister (&Arg2.Expr) && + int AllowOneIndex = !ED_IsLocZP (&Arg2.Expr) && !(ED_IsLocNone (&Arg2.Expr) && Arg2.Expr.IVal < 256); /* Calculate the real stack offset */ @@ -1233,9 +1226,13 @@ static void StdFunc_strlen (FuncDesc* F attribute ((unused)), ExprDesc* Expr) ** at runtime. */ if (ED_IsLocLiteral (&Arg) && IS_Get (&WritableStrings) == 0) { + /* Get the length of the C string within the string literal. + ** Note: Keep in mind that the literal could contain '\0' in it. + */ + size_t Len = strnlen (GetLiteralStr (Arg.V.LVal), GetLiteralSize (Arg.V.LVal) - 1); /* Constant string literal */ - ED_MakeConstAbs (Expr, GetLiteralSize (Arg.V.LVal) - 1, type_size_t); + ED_MakeConstAbs (Expr, Len, type_size_t); /* We don't need the literal any longer */ ReleaseLiteral (Arg.V.LVal); @@ -1275,7 +1272,7 @@ static void StdFunc_strlen (FuncDesc* F attribute ((unused)), ExprDesc* Expr) ** completely within the reach of a byte sized index register. */ if (ED_IsLocStack (&Arg) && IsArray && IsByteIndex && - (Arg.IVal - StackPtr) + ECount < 256) { + ED_GetStackOffs (&Arg, ECount) < 256) { /* Calculate the true stack offset */ int Offs = ED_GetStackOffs (&Arg, 0); @@ -1304,7 +1301,7 @@ static void StdFunc_strlen (FuncDesc* F attribute ((unused)), ExprDesc* Expr) ** get inlined if requested on the command line, since we cannot know how ** big the buffer actually is, so inlining is not always safe. */ - if (ED_IsLocRegister (&Arg) && ED_IsLVal (&Arg) && IsPtr && + if (ED_IsZPInd (&Arg) && IsPtr && IS_Get (&EagerlyInlineFuncs)) { /* Generate the strlen code */ diff --git a/src/cc65/stmt.c b/src/cc65/stmt.c index 7355e88a8..e28b844b5 100644 --- a/src/cc65/stmt.c +++ b/src/cc65/stmt.c @@ -56,6 +56,7 @@ #include "loop.h" #include "pragma.h" #include "scanner.h" +#include "seqpoint.h" #include "stackptr.h" #include "stmt.h" #include "swstmt.h" @@ -187,8 +188,8 @@ static int IfStatement (void) /* If the if expression was always true, the code in the else branch ** is never executed. Output a warning if this is the case. */ - if (TestResult == TESTEXPR_TRUE && IS_Get (&WarnUnreachableCode)) { - Warning ("Unreachable code"); + if (TestResult == TESTEXPR_TRUE) { + UnreachableCodeWarning (); } /* Define the target for the first test */ @@ -310,7 +311,6 @@ static void ReturnStatement (void) /* Handle the 'return' statement */ { ExprDesc Expr; - const Type* ReturnType; ED_Init (&Expr); NextToken (); @@ -326,31 +326,19 @@ static void ReturnStatement (void) if (F_HasVoidReturn (CurrentFunc)) { Error ("Returning a value in function with return type 'void'"); } else { - /* Check the return type first */ - ReturnType = F_GetReturnType (CurrentFunc); - if (IsIncompleteESUType (ReturnType)) { - /* Avoid excess errors */ - if (ErrorCount == 0) { - Error ("Returning a value in function with incomplete return type"); - } + const Type* ReturnType = F_GetReturnType (CurrentFunc); + + /* Convert the return value to the type of the function result */ + TypeConversion (&Expr, ReturnType); + + /* Load the value into the primary */ + if (IsClassStruct (Expr.Type)) { + /* Handle struct/union specially */ + LoadExpr (CG_TypeOf (GetStructReplacementType (ReturnType)), &Expr); } else { - /* Convert the return value to the type of the function result */ - TypeConversion (&Expr, ReturnType); - /* Load the value into the primary */ - if (IsClassStruct (Expr.Type)) { - /* Handle struct/union specially */ - ReturnType = GetStructReplacementType (Expr.Type); - if (ReturnType == Expr.Type) { - Error ("Returning '%s' of this size by value is not supported", GetFullTypeName (Expr.Type)); - } - LoadExpr (TypeOf (ReturnType), &Expr); - - } else { - /* Load the value into the primary */ - LoadExpr (CF_NONE, &Expr); - } + LoadExpr (CF_NONE, &Expr); } /* Append deferred inc/dec at sequence point */ @@ -674,6 +662,8 @@ int AnyStatement (int* PendingToken) ** NULL, the function will skip the token. */ { + int GotBreak = 0; + /* Assume no pending token */ if (PendingToken) { *PendingToken = 0; @@ -689,7 +679,8 @@ int AnyStatement (int* PendingToken) switch (CurTok.Tok) { case TOK_IF: - return IfStatement (); + GotBreak = IfStatement (); + break; case TOK_SWITCH: SwitchStatement (); @@ -710,25 +701,25 @@ int AnyStatement (int* PendingToken) case TOK_GOTO: GotoStatement (); CheckSemi (PendingToken); - return 1; + GotBreak = 1; + break; case TOK_RETURN: ReturnStatement (); CheckSemi (PendingToken); - return 1; + GotBreak = 1; + break; case TOK_BREAK: BreakStatement (); CheckSemi (PendingToken); - return 1; + GotBreak = 1; + break; case TOK_CONTINUE: ContinueStatement (); CheckSemi (PendingToken); - return 1; - - case TOK_PRAGMA: - DoPragma (); + GotBreak = 1; break; case TOK_SEMI: @@ -737,12 +728,17 @@ int AnyStatement (int* PendingToken) break; case TOK_LCURLY: - return CompoundStatement (PendingToken); + GotBreak = CompoundStatement (PendingToken); + break; default: /* Simple statement */ Statement (PendingToken); break; } - return 0; + + /* Reset SQP flags */ + SetSQPFlags (SQP_KEEP_NONE); + + return GotBreak; } diff --git a/src/cc65/swstmt.c b/src/cc65/swstmt.c index 0466ddf4a..fc213c9a1 100644 --- a/src/cc65/swstmt.c +++ b/src/cc65/swstmt.c @@ -64,7 +64,7 @@ typedef struct SwitchCtrl SwitchCtrl; struct SwitchCtrl { Collection* Nodes; /* CaseNode tree */ - TypeCode ExprType; /* Basic switch expression type */ + const Type* ExprType; /* Switch controlling expression type */ unsigned Depth; /* Number of bytes the selector type has */ unsigned DefaultLabel; /* Label for the default branch */ @@ -133,7 +133,7 @@ void SwitchStatement (void) /* Setup the control structure, save the old and activate the new one */ SwitchData.Nodes = NewCollection (); - SwitchData.ExprType = GetUnqualTypeCode (&SwitchExpr.Type[0]); + SwitchData.ExprType = SwitchExpr.Type; SwitchData.Depth = SizeOf (SwitchExpr.Type); SwitchData.DefaultLabel = 0; OldSwitch = Switch; @@ -152,7 +152,7 @@ void SwitchStatement (void) /* Check if we had any labels */ if (CollCount (SwitchData.Nodes) == 0 && SwitchData.DefaultLabel == 0) { - Warning ("No case labels"); + Warning ("No reachable case labels for switch"); } /* If the last statement did not have a break, we may have an open @@ -209,62 +209,64 @@ void CaseLabel (void) /* Handle a case label */ { ExprDesc CaseExpr; /* Case label expression */ - long Val; /* Case label value */ - unsigned CodeLabel; /* Code label for this case */ /* Skip the "case" token */ NextToken (); /* Read the selector expression */ CaseExpr = NoCodeConstAbsIntExpr (hie1); - Val = CaseExpr.IVal; /* Now check if we're inside a switch statement */ if (Switch != 0) { /* Check the range of the expression */ - switch (Switch->ExprType) { + const Type* CaseT = CaseExpr.Type; + long CaseVal = CaseExpr.IVal; + int OutOfRange = 0; + const char* DiagMsg = 0; - case T_SCHAR: - /* Signed char */ - if (Val < -128 || Val > 127) { - Error ("Range error"); - } - break; + CaseExpr.Type = IntPromotion (Switch->ExprType); + LimitExprValue (&CaseExpr, 1); - case T_UCHAR: - if (Val < 0 || Val > 255) { - Error ("Range error"); - } - break; - - case T_SHORT: - case T_INT: - if (Val < -32768 || Val > 32767) { - Error ("Range error"); - } - break; - - case T_USHORT: - case T_UINT: - if (Val < 0 || Val > 65535) { - Error ("Range error"); - } - break; - - case T_LONG: - case T_ULONG: - break; - - default: - Internal ("Invalid type: %06lX", Switch->ExprType); + if (CaseVal != CaseExpr.IVal || + (IsSignSigned (CaseT) != IsSignSigned (CaseExpr.Type) && + (IsSignSigned (CaseT) ? CaseVal < 0 : CaseExpr.IVal < 0))) { + Warning (IsSignSigned (CaseT) ? + IsSignSigned (CaseExpr.Type) ? + "Case value is implicitly converted (%ld to %ld)" : + "Case value is implicitly converted (%ld to %lu)" : + IsSignSigned (CaseExpr.Type) ? + "Case value is implicitly converted (%lu to %ld)" : + "Case value is implicitly converted (%lu to %lu)", + CaseVal, CaseExpr.IVal); } - /* Insert the case selector into the selector table */ - CodeLabel = InsertCaseValue (Switch->Nodes, Val, Switch->Depth); + /* Check the range of the expression */ + if (IsSignSigned (CaseExpr.Type)) { + if (CaseExpr.IVal < GetIntegerTypeMin (Switch->ExprType)) { + DiagMsg = "Case value (%ld) out of range for switch condition type"; + OutOfRange = 1; + } else if (IsSignSigned (Switch->ExprType) ? + CaseExpr.IVal > (long)GetIntegerTypeMax (Switch->ExprType) : + SizeOf (CaseExpr.Type) > SizeOf (Switch->ExprType) && + (unsigned long)CaseExpr.IVal > GetIntegerTypeMax (Switch->ExprType)) { + DiagMsg = "Case value (%ld) out of range for switch condition type"; + OutOfRange = 1; + } + } else if ((unsigned long)CaseExpr.IVal > GetIntegerTypeMax (Switch->ExprType)) { + DiagMsg = "Case value (%lu) out of range for switch condition type"; + OutOfRange = 1; + } - /* Define this label */ - g_defcodelabel (CodeLabel); + if (OutOfRange == 0) { + /* Insert the case selector into the selector table */ + unsigned CodeLabel = InsertCaseValue (Switch->Nodes, CaseExpr.IVal, Switch->Depth); + + /* Define this label */ + g_defcodelabel (CodeLabel); + } else { + Warning (DiagMsg, CaseExpr.IVal); + } } else { diff --git a/src/cc65/symentry.c b/src/cc65/symentry.c index 30ebe7dd8..56f6ffbb0 100644 --- a/src/cc65/symentry.c +++ b/src/cc65/symentry.c @@ -88,7 +88,7 @@ void FreeSymEntry (SymEntry* E) TypeFree (E->Type); xfree (E->AsmName); - if (E->Flags & SC_LABEL) { + if ((E->Flags & SC_TYPEMASK) == SC_LABEL) { for (i = 0; i < CollCount (E->V.L.DefsOrRefs); i++) { xfree (CollAt (E->V.L.DefsOrRefs, i)); } @@ -109,21 +109,17 @@ void DumpSymEntry (FILE* F, const SymEntry* E) unsigned Val; } SCFlagTable; - static SCFlagTable ESUTypes[] = { - { "SC_TYPEDEF", SC_TYPEDEF }, - { "SC_UNION", SC_UNION }, - { "SC_STRUCT", SC_STRUCT }, - { "SC_ENUM", SC_ENUM }, - }; - static SCFlagTable Types[] = { - { "SC_BITFIELD", SC_BITFIELD }, - { "SC_STRUCTFIELD", SC_STRUCTFIELD }, - { "SC_ENUMERATOR", SC_ENUMERATOR }, - { "SC_CONST", SC_CONST }, + { "SC_NONE", SC_NONE }, + { "SC_STRUCT", SC_STRUCT }, + { "SC_UNION", SC_UNION }, + { "SC_ENUM", SC_ENUM }, { "SC_LABEL", SC_LABEL }, - { "SC_PARAM", SC_PARAM }, + { "SC_BITFIELD", SC_BITFIELD }, + { "SC_TYPEDEF", SC_TYPEDEF }, + { "SC_ENUMERATOR", SC_ENUMERATOR }, { "SC_FUNC", SC_FUNC }, + { "SC_ARRAY", SC_ARRAY }, }; static SCFlagTable Storages[] = { @@ -131,11 +127,31 @@ void DumpSymEntry (FILE* F, const SymEntry* E) { "SC_REGISTER", SC_REGISTER }, { "SC_STATIC", SC_STATIC }, { "SC_EXTERN", SC_EXTERN }, - { "SC_STORAGE", SC_STORAGE }, + }; + + static SCFlagTable Properties[] = { + { "SC_CONST", SC_CONST }, + { "SC_STRUCTFIELD", SC_STRUCTFIELD }, + { "SC_PARAM", SC_PARAM }, + { "SC_DEFTYPE", SC_DEFTYPE }, { "SC_ZEROPAGE", SC_ZEROPAGE }, - { "SC_DECL", SC_DECL }, + { "SC_HAVEALIGN", SC_HAVEALIGN }, + { "SC_HAVEATTR", SC_HAVEATTR }, + { "SC_TU_STORAGE", SC_TU_STORAGE }, + { "SC_ASSIGN_INIT", SC_ASSIGN_INIT }, + { "SC_ALIAS", SC_ALIAS }, + { "SC_FICTITIOUS", SC_FICTITIOUS }, + { "SC_HAVEFAM", SC_HAVEFAM }, + { "SC_HAVECONST", SC_HAVECONST }, + }; + + static SCFlagTable Status[] = { { "SC_DEF", SC_DEF }, { "SC_REF", SC_REF }, + { "SC_GOTO", SC_GOTO }, + { "SC_GOTO_IND", SC_GOTO_IND }, + { "SC_LOCALSCOPE", SC_LOCALSCOPE }, + { "SC_NOINLINEDEF", SC_NOINLINEDEF }, }; unsigned I; @@ -152,28 +168,38 @@ void DumpSymEntry (FILE* F, const SymEntry* E) /* Print the flags */ SymFlags = E->Flags; fprintf (F, " Flags:"); - /* Enum, struct, union and typedefs */ - if ((SymFlags & SC_ESUTYPEMASK) != 0) { - for (I = 0; I < sizeof (ESUTypes) / sizeof (ESUTypes[0]); ++I) { - if ((SymFlags & SC_ESUTYPEMASK) == ESUTypes[I].Val) { - SymFlags &= ~SC_ESUTYPEMASK; - fprintf (F, " %s", ESUTypes[I].Name); + /* Symbol types */ + if ((SymFlags & SC_TYPEMASK) != 0) { + for (I = 0; I < sizeof (Types) / sizeof (Types[0]); ++I) { + if ((SymFlags & SC_TYPEMASK) == Types[I].Val) { + SymFlags &= ~SC_TYPEMASK; + fprintf (F, " %s", Types[I].Name); break; } } } - /* Other type flags */ - for (I = 0; I < sizeof (Types) / sizeof (Types[0]) && SymFlags != 0; ++I) { - if ((SymFlags & Types[I].Val) == Types[I].Val) { - SymFlags &= ~Types[I].Val; - fprintf (F, " %s", Types[I].Name); + /* Storage classes */ + if ((SymFlags & SC_STORAGEMASK) != 0) { + for (I = 0; I < sizeof (Storages) / sizeof (Storages[0]); ++I) { + if ((SymFlags & SC_STORAGEMASK) == Storages[I].Val) { + SymFlags &= ~SC_STORAGEMASK; + fprintf (F, " %s", Storages[I].Name); + break; + } } } - /* Storage flags */ - for (I = 0; I < sizeof (Storages) / sizeof (Storages[0]) && SymFlags != 0; ++I) { - if ((SymFlags & Storages[I].Val) == Storages[I].Val) { - SymFlags &= ~Storages[I].Val; - fprintf (F, " %s", Storages[I].Name); + /* Special property flags */ + for (I = 0; I < sizeof (Properties) / sizeof (Properties[0]) && SymFlags != 0; ++I) { + if ((SymFlags & Properties[I].Val) == Properties[I].Val) { + SymFlags &= ~Properties[I].Val; + fprintf (F, " %s", Properties[I].Name); + } + } + /* Status flags */ + for (I = 0; I < sizeof (Status) / sizeof (Status[0]) && SymFlags != 0; ++I) { + if ((SymFlags & Status[I].Val) == Status[I].Val) { + SymFlags &= ~Status[I].Val; + fprintf (F, " %s", Status[I].Name); } } if (SymFlags != 0) { @@ -199,9 +225,10 @@ int SymIsOutputFunc (const SymEntry* Sym) /* Symbol must be a function which is defined and either extern or ** static and referenced. */ - return IsTypeFunc (Sym->Type) && - SymIsDef (Sym) && - (Sym->Flags & (SC_REF | SC_EXTERN)); + return IsTypeFunc (Sym->Type) && + SymIsDef (Sym) && + ((Sym->Flags & SC_REF) || + (Sym->Flags & SC_STORAGEMASK) != SC_STATIC); } @@ -272,7 +299,7 @@ void SymCvtRegVarToAuto (SymEntry* Sym) /* Convert a register variable to an auto variable */ { /* Change the storage class */ - Sym->Flags = (Sym->Flags & ~(SC_REGISTER | SC_STATIC | SC_EXTERN)) | SC_AUTO; + Sym->Flags = (Sym->Flags & ~SC_STORAGEMASK) | SC_AUTO; /* Transfer the stack offset from register save area to actual offset */ Sym->V.Offs = Sym->V.R.SaveOffs; diff --git a/src/cc65/symentry.h b/src/cc65/symentry.h index 5ebd30a75..7871b9ade 100644 --- a/src/cc65/symentry.h +++ b/src/cc65/symentry.h @@ -56,7 +56,7 @@ -struct Segments; +struct SegContext; struct LiteralPool; struct CodeEntry; @@ -68,46 +68,92 @@ struct CodeEntry; -/* Storage classes and flags */ +/* Symbol types and flags */ #define SC_NONE 0x0000U /* Nothing */ -#define SC_STRUCT 0x0001U /* Struct */ -#define SC_UNION 0x0002U /* Union */ -#define SC_ENUM 0x0003U /* Enum */ -#define SC_TYPEDEF 0x0004U /* Typedef */ -#define SC_ESUTYPEMASK 0x0007U /* Mask for above types */ -#define SC_ENUMERATOR 0x0008U /* An enumerator */ -#define SC_BITFIELD 0x0010U /* A bit-field inside a struct or union */ -#define SC_TYPEMASK 0x001FU /* Mask for above types */ -#define SC_FUNC 0x0020U /* A function */ -#define SC_LABEL 0x0040U /* A goto code label */ -#define SC_CONST 0x0080U /* A numeric constant with a type */ -#define SC_PARAM 0x0100U /* A function parameter */ -#define SC_DEFTYPE 0x0200U /* Parameter has default type (=int, old style) */ -#define SC_STRUCTFIELD 0x0400U /* Struct or union field */ +/* Types of symbols */ +#define SC_STRUCT 0x0001U /* Struct tag */ +#define SC_UNION 0x0002U /* Union tag */ +#define SC_ENUM 0x0003U /* Enum tag */ +#define SC_LABEL 0x0004U /* A goto code label */ +#define SC_BITFIELD 0x0005U /* A bit-field inside a struct or union */ +#define SC_TYPEDEF 0x0006U /* A typedef */ +#define SC_ENUMERATOR 0x0007U /* An enumerator */ -#define SC_ZEROPAGE 0x0800U /* Symbol marked as zeropage */ +/* Note: These symbol types might be checked as bit-flags occasionally. +** So don't share their unique bits with other symbol types. +*/ +#define SC_FUNC 0x0008U /* A function */ +#define SC_ARRAY 0x0010U /* UNUSED: An array */ +#define SC_TYPEMASK 0x001FU /* Mask for symbol types all above */ -#define SC_DEF 0x1000U /* Symbol is defined */ -#define SC_REF 0x2000U /* Symbol is referenced */ -#define SC_DECL 0x4000U /* Symbol is declared in global scope */ -#define SC_STORAGE 0x8000U /* Symbol with associated storage */ +/* Additional property of the symbols */ +#define SC_CONST 0x0020U /* A numeric constant with a type */ +#define SC_STRUCTFIELD 0x0040U /* A struct or union field */ +#define SC_PARAM 0x0080U /* A function parameter */ +#define SC_DEFTYPE 0x0100U /* An old-style parameter with default type (=int) */ -#define SC_AUTO 0x010000U /* Auto variable */ -#define SC_REGISTER 0x020000U /* Register variable */ -#define SC_STATIC 0x040000U /* Static - not to be confused with other *_STATIC */ -#define SC_EXTERN 0x080000U /* Extern linkage */ -#define SC_STORAGEMASK 0x0F0000U /* Storage type mask */ +/* Address property of the symbol */ +#define SC_ZEROPAGE 0x0200U /* Symbol marked as on zeropage */ -#define SC_HAVEATTR 0x100000U /* Symbol has attributes */ +/* Additional attributes of the symbol */ +#define SC_HAVEALIGN 0x0400U /* UNUSED: Symbol has special alignment */ +#define SC_HAVEATTR 0x0800U /* Symbol has attributes */ -#define SC_GOTO 0x200000U -#define SC_SPADJUSTMENT 0x400000U -#define SC_GOTO_IND 0x800000U /* Indirect goto */ +/* Special property of declaration */ +#define SC_TU_STORAGE 0x1000U /* Symbol has allocated storage in the TU */ +#define SC_ASSIGN_INIT 0x2000U /* Symbol is to be initialized with assignment code */ -#define SC_ALIAS 0x01000000U /* Alias of global or anonymous field */ -#define SC_FICTITIOUS 0x02000000U /* Symbol is fictitious (for error recovery) */ -#define SC_HAVEFAM 0x04000000U /* Type has a Flexible Array Member */ +#define SC_ALIAS 0x4000U /* Symbol is an alias */ +#define SC_FICTITIOUS 0x8000U /* Symbol is fictitious (for error recovery) */ +#define SC_HAVEFAM 0x010000U /* Struct/union has a Flexible Array Member */ +#define SC_HAVECONST 0x020000U /* Struct/union has a const member */ + +/* Status of the symbol */ +#define SC_DEF 0x040000U /* Symbol is defined */ +#define SC_REF 0x080000U /* Symbol is referenced */ +#define SC_GOTO 0x100000U /* Symbol is destination of a goto */ +#define SC_GOTO_IND 0x200000U /* Symbol is destination of an indirect goto */ +#define SC_LOCALSCOPE 0x400000U /* Symbol is invisible in file scope */ +#define SC_NOINLINEDEF 0x800000U /* Symbol may never have an inline definition */ + +/* To figure out the linkage of an object or function symbol Sym: +** - external linkage: +** SymIsGlobal (Sym) && (Sym->Flags & SC_STORAGEMASK) != SC_STATIC +** - internal linkage: +** SymIsGlobal (Sym) && (Sym->Flags & SC_STORAGEMASK) == SC_STATIC +** - no linkage: +** !SymIsGlobal (Sym) +** +** To figure out the storage class of a symbol by its SC_ flags: +** +** - no explicit storage class specifiers (in file scope): +** (flags & SC_STORAGEMASK) == SC_NONE +** - no explicit storage class specifiers (in block scope): +** (flags & SC_STORAGEMASK) == SC_AUTO +** - extern: +** (flags & SC_STORAGEMASK) == SC_EXTERN +** - static: +** (flags & SC_STORAGEMASK) == SC_STATIC +** - auto: +** (flags & SC_STORAGEMASK) == SC_AUTO +** - register: +** (flags & SC_STORAGEMASK) == SC_REGISTER +** - typedef (per ISO C): +** (flags & SC_TYPEMASK) == SC_TYPEDEF +** +** Note: SC_TYPEDEF can be also used as a flag. +*/ +#define SC_AUTO 0x01000000U /* Auto storage class */ +#define SC_REGISTER 0x02000000U /* Register storage class */ +#define SC_STATIC 0x03000000U /* Static storage class */ +#define SC_EXTERN 0x04000000U /* Extern storage class */ +#define SC_THREAD 0x08000000U /* UNSUPPORTED: Thread-local storage class */ +#define SC_STORAGEMASK 0x0F000000U /* Storage type mask */ + +/* Function specifiers */ +#define SC_INLINE 0x10000000U /* Inline function */ +#define SC_NORETURN 0x20000000U /* Noreturn function */ @@ -156,8 +202,10 @@ struct SymEntry { /* Data for functions */ struct { - struct Segments* Seg; /* Segments for this function */ + struct SegContext* Seg; /* SegContext for this function */ struct LiteralPool* LitPool; /* Literal pool for this function */ + struct SymEntry* WrappedCall; /* Pointer to the WrappedCall */ + unsigned int WrappedCallData; /* The WrappedCall's user data */ } F; /* Label name for static symbols */ @@ -211,14 +259,37 @@ void FreeSymEntry (SymEntry* E); void DumpSymEntry (FILE* F, const SymEntry* E); /* Dump the given symbol table entry to the file in readable form */ +int SymIsOutputFunc (const SymEntry* Sym); +/* Return true if this is a function that must be output */ + +#if defined(HAVE_INLINE) +INLINE int SymIsArray (const SymEntry* Sym) +/* Return true if the given entry is an array entry */ +{ + return ((Sym->Flags & SC_TYPEMASK) == SC_ARRAY); +} +#else +# define SymIsArray(Sym) (((Sym)->Flags & SC_TYPEMASK) == SC_ARRAY) +#endif + #if defined(HAVE_INLINE) INLINE int SymIsBitField (const SymEntry* Sym) /* Return true if the given entry is a bit-field entry */ { - return ((Sym->Flags & SC_BITFIELD) == SC_BITFIELD); + return ((Sym->Flags & SC_TYPEMASK) == SC_BITFIELD); } #else -# define SymIsBitField(Sym) (((Sym)->Flags & SC_BITFIELD) == SC_BITFIELD) +# define SymIsBitField(Sym) (((Sym)->Flags & SC_TYPEMASK) == SC_BITFIELD) +#endif + +#if defined(HAVE_INLINE) +INLINE int SymIsLabel (const SymEntry* Sym) +/* Return true if the given entry is a label entry */ +{ + return ((Sym)->Flags & SC_TYPEMASK) == SC_LABEL; +} +#else +# define SymIsLabel(Sym) (((Sym)->Flags & SC_TYPEMASK) == SC_LABEL) #endif #if defined(HAVE_INLINE) @@ -254,17 +325,13 @@ INLINE int SymIsRef (const SymEntry* Sym) #if defined(HAVE_INLINE) INLINE int SymIsRegVar (const SymEntry* Sym) /* Return true if the given entry is a register variable */ -/* ### HACK! Fix the ugly type flags! */ { - return ((Sym->Flags & (SC_REGISTER | SC_TYPEMASK)) == SC_REGISTER); + return ((Sym->Flags & (SC_STORAGEMASK | SC_TYPEMASK)) == (SC_REGISTER | SC_NONE)); } #else -# define SymIsRegVar(Sym) (((Sym)->Flags & (SC_REGISTER | SC_TYPEMASK)) == SC_REGISTER) +# define SymIsRegVar(Sym) (((Sym)->Flags & (SC_STORAGEMASK | SC_TYPEMASK)) == (SC_REGISTER | SC_NONE)) #endif -int SymIsOutputFunc (const SymEntry* Sym); -/* Return true if this is a function that must be output */ - #if defined(HAVE_INLINE) INLINE int SymHasFlexibleArrayMember (const SymEntry* Sym) /* Return true if the given entry has a flexible array member */ @@ -275,6 +342,16 @@ INLINE int SymHasFlexibleArrayMember (const SymEntry* Sym) # define SymHasFlexibleArrayMember(Sym) (((Sym)->Flags & SC_HAVEFAM) == SC_HAVEFAM) #endif +#if defined(HAVE_INLINE) +INLINE int SymHasConstMember (const SymEntry* Sym) +/* Return true if the given entry has a const member */ +{ + return ((Sym->Flags & SC_HAVECONST) == SC_HAVECONST); +} +#else +# define SymHasConstMember(Sym) (((Sym)->Flags & SC_HAVECONST) == SC_HAVECONST) +#endif + #if defined(HAVE_INLINE) INLINE const char* SymGetAsmName (const SymEntry* Sym) /* Return the assembler label name for the symbol (beware: may be NULL!) */ @@ -282,7 +359,7 @@ INLINE const char* SymGetAsmName (const SymEntry* Sym) return Sym->AsmName; } #else -# define SymGetAsmName(Sym) ((Sym)->AsmName) +# define SymGetAsmName(Sym) ((Sym)->AsmName) #endif const DeclAttr* SymGetAttr (const SymEntry* Sym, DeclAttrType AttrType); diff --git a/src/cc65/symtab.c b/src/cc65/symtab.c index a2bbf13dd..07723699f 100644 --- a/src/cc65/symtab.c +++ b/src/cc65/symtab.c @@ -61,6 +61,7 @@ #include "symentry.h" #include "typecmp.h" #include "typeconv.h" +#include "wrappedcall.h" #include "symtab.h" @@ -164,22 +165,23 @@ static void CheckSymTable (SymTable* Tab) /* Ignore typedef entries */ if (!SymIsTypeDef (Entry)) { - /* Check if the symbol is one with storage, and it if it was - ** defined but not used. - */ - if (((Flags & SC_AUTO) || (Flags & SC_STATIC)) && (Flags & SC_EXTERN) == 0) { + /* Check if the symbol has non-external linkage and is defined but not used */ + if (!SymIsGlobal (Entry) || (Flags & SC_STORAGEMASK) == SC_STATIC) { if (SymIsDef (Entry) && !SymIsRef (Entry) && !SymHasAttr (Entry, atUnused)) { if (Flags & SC_PARAM) { - if (IS_Get (&WarnUnusedParam)) { + if (IS_Get (&WarnUnusedParam) && + !IsAnonName (Entry->Name)) { Warning ("Parameter '%s' is never used", Entry->Name); } - } else if (Flags & SC_FUNC) { + } else if ((Flags & SC_TYPEMASK) == SC_FUNC) { if (IS_Get (&WarnUnusedFunc)) { Warning ("Function '%s' is defined but never used", Entry->Name); } - } else { - if (IS_Get (&WarnUnusedVar)) { + } else if ((Flags & SC_TYPEMASK) == SC_NONE) { + if (IS_Get (&WarnUnusedVar) && + !IsAnonName (Entry->Name) && + (Flags & SC_CONST) != SC_CONST) { Warning ("Variable '%s' is defined but never used", Entry->Name); } } @@ -187,7 +189,7 @@ static void CheckSymTable (SymTable* Tab) } /* If the entry is a label, check if it was defined in the function */ - if (Flags & SC_LABEL) { + if ((Flags & SC_TYPEMASK) == SC_LABEL) { if (!SymIsDef (Entry)) { /* Undefined label */ Error ("Undefined label: '%s'", Entry->Name); @@ -556,8 +558,10 @@ static SymEntry* FindSymInTable (const SymTable* T, const char* Name, unsigned H -static SymEntry* FindSymInTree (const SymTable* Tab, const char* Name) -/* Find the symbol with the given name in the table tree that starts with T */ +static SymEntry* FindVisibleSymInTree (const SymTable* Tab, const char* Name) +/* Find the visible symbol with the given name in the table tree that starts +** with Tab. +*/ { /* Get the hash over the name */ unsigned Hash = HashStr (Name); @@ -573,7 +577,7 @@ static SymEntry* FindSymInTree (const SymTable* Tab, const char* Name) } /* Bail out if we found it */ - if (E != 0) { + if (E != 0 && (Tab != SymTab0 || (E->Flags & SC_LOCALSCOPE) == 0)) { return E; } @@ -588,9 +592,9 @@ static SymEntry* FindSymInTree (const SymTable* Tab, const char* Name) SymEntry* FindSym (const char* Name) -/* Find the symbol with the given name */ +/* Find with the given name the symbol visible in the current scope */ { - return FindSymInTree (SymTab, Name); + return FindVisibleSymInTree (SymTab, Name); } @@ -612,9 +616,9 @@ SymEntry* FindLocalSym (const char* Name) SymEntry* FindTagSym (const char* Name) -/* Find the symbol with the given name in the tag table */ +/* Find with the given name the tag symbol visible in the current scope */ { - return FindSymInTree (TagTab, Name); + return FindVisibleSymInTree (TagTab, Name); } @@ -715,7 +719,7 @@ static int HandleSymRedefinition (SymEntry* Sym, const Type* T, unsigned Flags) Sym = 0; } - } else if ((Sym->Flags & SC_FUNC) == SC_FUNC) { + } else if ((Sym->Flags & SC_TYPEMASK) == SC_FUNC) { /* In case of a function, use the new type descriptor, since it ** contains pointers to the new symbol tables that are needed if @@ -740,7 +744,7 @@ static int HandleSymRedefinition (SymEntry* Sym, const Type* T, unsigned Flags) /* Refine the existing composite prototype with this new ** one. */ - RefineFuncDesc (Sym->Type, T); + TypeComposition (Sym->Type, T); } } @@ -793,8 +797,6 @@ static int HandleSymRedefinition (SymEntry* Sym, const Type* T, unsigned Flags) */ Error ("Redeclaration of enumerator constant '%s'", Sym->Name); Sym = 0; - } else if (Flags & SC_STRUCTFIELD) { - Error ("Duplicate member '%s'", Sym->Name); } } } @@ -864,7 +866,6 @@ SymEntry* AddEnumSym (const char* Name, unsigned Flags, const Type* Type, SymTab } else { TagEntry->V.E.SymTab = Tab; TagEntry->V.E.Type = Type; - TagEntry->Flags &= ~SC_DECL; TagEntry->Flags |= SC_DEF; /* Remember this is the first definition of this type */ @@ -921,14 +922,8 @@ SymEntry* AddStructSym (const char* Name, unsigned Flags, unsigned Size, SymTabl /* SCType must be struct or union */ PRECONDITION (SCType == SC_STRUCT || SCType == SC_UNION); - if ((Flags & SC_FICTITIOUS) == 0) { - /* Do we have an entry with this name already? */ - TagEntry = FindSymInTable (CurTagTab, Name, HashStr (Name)); - } else { - /* Add a fictitious symbol in the fail-safe table */ - TagEntry = 0; - CurTagTab = FailSafeTab; - } + /* Do we have an entry with this name already? */ + TagEntry = FindSymInTable (CurTagTab, Name, HashStr (Name)); if (TagEntry) { @@ -956,6 +951,21 @@ SymEntry* AddStructSym (const char* Name, unsigned Flags, unsigned Size, SymTabl if (DSFlags != 0) { *DSFlags |= DS_NEW_TYPE_DEF; } + + if ((Flags & SC_FICTITIOUS) == SC_FICTITIOUS) { + /* Add a fictitious symbol in the fail-safe table */ + TagEntry = 0; + } else if (Size == 0) { + /* Empty struct is not supported now */ + if (!IsAnonName (Name)) { + Error ("Empty %s type '%s' is not supported", + SCType == SC_STRUCT ? "struct" : "union", Name); + } else { + Error ("Empty %s type is not supported", + SCType == SC_STRUCT ? "struct" : "union"); + } + TagEntry = 0; + } } } @@ -998,6 +1008,7 @@ SymEntry* AddBitField (const char* Name, const Type* T, unsigned Offs, { /* Do we have an entry with this name already? */ SymEntry* Entry = FindSymInTable (FieldTab, Name, HashStr (Name)); + if (Entry) { /* We have a symbol with this name already */ @@ -1009,7 +1020,6 @@ SymEntry* AddBitField (const char* Name, const Type* T, unsigned Offs, Entry = NewSymEntry (Name, SC_BITFIELD); /* Set the symbol attributes. Bit-fields are always integral types. */ - Entry->Type = NewBitFieldOf (T, BitOffs, BitWidth); Entry->V.Offs = Offs; if (!SignednessSpecified) { @@ -1020,13 +1030,12 @@ SymEntry* AddBitField (const char* Name, const Type* T, unsigned Offs, ** is controlled by `--signed-chars`. In bit-fields, however, we perform the same ** `char -> unsigned char` adjustment that is performed with other integral types. */ - CHECK ((Entry->Type->C & T_MASK_SIGN) == T_SIGN_SIGNED || - IsRankChar (Entry->Type)); - Entry->Type[0].C &= ~T_MASK_SIGN; - Entry->Type[0].C |= T_SIGN_UNSIGNED; - Entry->Type[1].C &= ~T_MASK_SIGN; - Entry->Type[1].C |= T_SIGN_UNSIGNED; + CHECK (IsSignSigned (T) || IsRankChar (T)); + Entry->Type = NewBitFieldOf (GetUnsignedType (T), BitOffs, BitWidth); + } else { + Entry->Type = NewBitFieldOf (T, BitOffs, BitWidth); } + Entry->Type[0].C |= GetQualifier (T) & T_MASK_QUAL; /* Add the entry to the symbol table */ AddSymEntry (FieldTab, Entry); @@ -1044,8 +1053,9 @@ SymEntry* AddConstSym (const char* Name, const Type* T, unsigned Flags, long Val { /* Do we have an entry with this name already? */ SymEntry* Entry = FindSymInTable (SymTab, Name, HashStr (Name)); + if (Entry) { - if ((Entry->Flags & SC_CONST) != SC_CONST) { + if ((Entry->Flags & SC_TYPEMASK) != (Flags & SC_TYPEMASK)) { Error ("Symbol '%s' is already different kind", Name); } else { Error ("Multiple definition for constant '%s'", Name); @@ -1054,7 +1064,7 @@ SymEntry* AddConstSym (const char* Name, const Type* T, unsigned Flags, long Val } /* Create a new entry */ - Entry = NewSymEntry (Name, Flags); + Entry = NewSymEntry (Name, Flags | SC_CONST); /* We only have integer constants for now */ Entry->Type = TypeDup (T); @@ -1114,6 +1124,7 @@ SymEntry* AddLabelSym (const char* Name, unsigned Flags) /* Do we have an entry with this name already? */ SymEntry* Entry = FindSymInTable (LabelTab, Name, HashStr (Name)); + if (Entry) { if (SymIsDef (Entry) && (Flags & SC_DEF) != 0) { @@ -1164,7 +1175,7 @@ SymEntry* AddLabelSym (const char* Name, unsigned Flags) /* Optimizer will need the information about the value of SP adjustment ** later, so let's preserve it. */ - E = NewSymEntry (LocalDataLabelName (DOR->LateSP_Label), SC_SPADJUSTMENT); + E = NewSymEntry (LocalDataLabelName (DOR->LateSP_Label), 0); E->V.SPAdjustment = StackPtr - DOR->StackPtr; AddSymEntry (SPAdjustTab, E); } @@ -1223,31 +1234,40 @@ SymEntry* AddLocalSym (const char* Name, const Type* T, unsigned Flags, int Offs ident Ident; /* Do we have an entry with this name already? */ - SymEntry* Entry = FindSymInTable (Tab, Name, HashStr (Name)); + SymEntry* Entry; + + /* HACK: only allows to add parameter symbols in a parameter list */ + if ((Flags & SC_PARAM) == 0 && GetLexicalLevel () == LEX_LEVEL_PARAM_LIST) { + return 0; + } + + Entry = FindSymInTable (Tab, Name, HashStr (Name)); + if (Entry) { + if ((Flags & SC_STRUCTFIELD) == 0) { + while (Entry && (Entry->Flags & SC_ALIAS) == SC_ALIAS) { + /* Get the aliased entry */ + Entry = Entry->V.A.Field; + } + } /* We have a symbol with this name already */ if (HandleSymRedefinition (Entry, T, Flags)) { Entry = 0; - } else if ((Flags & SC_ESUTYPEMASK) != SC_TYPEDEF) { + } else if ((Flags & SC_TYPEMASK) != SC_TYPEDEF) { /* Redefinitions are not allowed */ if (SymIsDef (Entry) && (Flags & SC_DEF) == SC_DEF) { Error ("Multiple definition of '%s'", Entry->Name); Entry = 0; - } else if ((Flags & (SC_AUTO | SC_REGISTER)) != 0 && - (Entry->Flags & SC_EXTERN) != 0) { - /* Check for local storage class conflict */ - Error ("Declaration of '%s' with no linkage follows extern declaration", - Name); + } else if ((Flags & SC_STRUCTFIELD) != 0) { + Error ("Duplicate member '%s'", Entry->Name); Entry = 0; - } else { - /* If a static declaration follows a non-static declaration, - ** then it is an error. - */ - if ((Flags & SC_DEF) && - (Flags & SC_EXTERN) == 0 && - (Entry->Flags & SC_EXTERN) != 0) { - Error ("Static declaration of '%s' follows extern declaration", Name); + } else if (Entry->Owner == SymTab0) { + if ((Flags & SC_STORAGEMASK) == SC_AUTO || + (Flags & SC_STORAGEMASK) == SC_REGISTER || + (Flags & SC_STORAGEMASK) == SC_STATIC) { + Error ("Declaration of '%s' with no linkage follows extern declaration", + Name); Entry = 0; } } @@ -1272,20 +1292,20 @@ SymEntry* AddLocalSym (const char* Name, const Type* T, unsigned Flags, int Offs Entry->Type = TypeDup (T); if ((Flags & SC_STRUCTFIELD) == SC_STRUCTFIELD || - (Flags & SC_ESUTYPEMASK) == SC_TYPEDEF) { + (Flags & SC_TYPEMASK) == SC_TYPEDEF) { if ((Flags & SC_ALIAS) != SC_ALIAS) { Entry->V.Offs = Offs; } - } else if ((Flags & SC_AUTO) == SC_AUTO) { + } else if ((Flags & SC_STORAGEMASK) == SC_AUTO) { Entry->V.Offs = Offs; - } else if ((Flags & SC_REGISTER) == SC_REGISTER) { + } else if ((Flags & SC_STORAGEMASK) == SC_REGISTER) { Entry->V.R.RegOffs = Offs; Entry->V.R.SaveOffs = StackPtr; - } else if ((Flags & SC_EXTERN) == SC_EXTERN || - (Flags & SC_FUNC) == SC_FUNC) { + } else if ((Flags & SC_STORAGEMASK) == SC_EXTERN || + (Flags & SC_TYPEMASK) == SC_FUNC) { Entry->V.L.Label = Offs; SymSetAsmName (Entry); - } else if ((Flags & SC_STATIC) == SC_STATIC) { + } else if ((Flags & SC_STORAGEMASK) == SC_STATIC) { /* Generate the assembler name from the data label number */ Entry->V.L.Label = Offs; Entry->AsmName = xstrdup (LocalDataLabelName (Entry->V.L.Label)); @@ -1330,46 +1350,70 @@ SymEntry* AddGlobalSym (const char* Name, const Type* T, unsigned Flags) Entry = FindGlobalSym (Name); } + /* Do we have a symbol with this name already? */ if (Entry) { - /* We have a symbol with this name already */ + /* Check if the symbol refers to some different type of things */ if (HandleSymRedefinition (Entry, T, Flags)) { Entry = 0; - } else if ((Entry->Flags & (SC_AUTO | SC_REGISTER)) != 0) { - /* Check for local storage class conflict */ + } else if (Entry->Owner != SymTab0) { + /* The previous declaration has no linkage. The current declaration + ** has either external or internal linkage. Either way it is an + ** error since the two declarations would be referring to different + ** objects with the same identifier. + */ Error ("Extern declaration of '%s' follows declaration with no linkage", Name); Entry = 0; - } else if ((Flags & SC_ESUTYPEMASK) != SC_TYPEDEF) { - /* If a static declaration follows a non-static declaration, then the result is undefined. - ** Most compilers choose to either give an error at compile time, - ** or remove the extern property for a link time error if used. + } else if ((Flags & SC_TYPEMASK) != SC_TYPEDEF) { + /* If we are adding the symbol in the file scope, it is now + ** visible there. */ - if (SymTab == SymTab0 && - (Flags & SC_EXTERN) == 0 && - (Entry->Flags & SC_EXTERN) != 0) { - Error ("Static declaration of '%s' follows non-static declaration", Name); - } else if ((Flags & SC_EXTERN) != 0 && - (Entry->Owner == SymTab0 || (Entry->Flags & SC_DEF) != 0) && - (Entry->Flags & SC_EXTERN) == 0) { - /* It is OK if a global extern declaration follows a global - ** non-static declaration, but an error if either of them is - ** local, as the two would be referring to different objects. - ** It is an error as well if a global non-static declaration - ** follows a global static declaration. + if (SymTab == SymTab0) { + Entry->Flags &= ~SC_LOCALSCOPE; + } + + /* The C standard specifies that the result is undefined if the + ** same thing has both internal and external linkage. Most + ** compilers choose to either give an error at compile time, or + ** remove the external linkage for a link time error if used + ** outside the current translation unit. We choose to give an + ** error at compile time in this case. + */ + if ((Entry->Flags & SC_STORAGEMASK) != SC_STATIC) { + /* The previous declaration is a non-static declaration of an + ** object or function that has external linkage. */ - if (Entry->Owner == SymTab0) { - if ((Flags & SC_STORAGE) == 0) { - /* Linkage must be unchanged. - ** The C standard specifies that a later extern declaration will be ignored, - ** and will use the previous linkage instead. Giving a warning for this case. - */ - Flags &= ~SC_EXTERN; - Warning ("Extern declaration of '%s' follows static declaration, extern ignored", Name); - } else { - Error ("Non-static declaration of '%s' follows static declaration", Name); - } - } else { - Error ("Extern declaration of '%s' follows static declaration", Name); + if ((Flags & SC_STORAGEMASK) == SC_STATIC) { + /* It is a static declaration of an object or function that + ** has internal linkage. Conflicted wih the previous one. + */ + Error ("Static declaration of '%s' follows non-static declaration", + Name); + Entry = 0; + } + } else if ((Flags & SC_STORAGEMASK) != SC_STATIC) { + /* The previous declaration is a static declaration of an + ** object or function that has internal linkage. + */ + if ((Flags & SC_STORAGEMASK) == SC_EXTERN || + (Flags & SC_TYPEMASK) == SC_FUNC) { + /* The C standard specifies that an extern declaration + ** shall keep the previously declared internal linkage + ** unchanged. For a function declaration with no storage + ** class specifiers, it is treated as if with 'extern'. + ** We give a warning although it is not required by the + ** standard. + */ + Flags &= ~SC_STORAGEMASK; + Warning ("Extern declaration of '%s' follows static declaration", + Name); + } else if ((Flags & SC_STORAGEMASK) == SC_NONE) { + /* It is a non-extern-or-static declaration of an object in + ** file scope that has external linkage. Conflicted wih the + ** previous one. + */ + Error ("Non-static declaration of '%s' follows static declaration", + Name); Entry = 0; } } @@ -1387,6 +1431,12 @@ SymEntry* AddGlobalSym (const char* Name, const Type* T, unsigned Flags) } if (Entry == 0) { + /* Hide the symbol in the file scope if we are declaring it in a + ** local scope. + */ + if (Tab == SymTab0 && SymTab != SymTab0) { + Flags |= SC_LOCALSCOPE; + } /* Create a new entry */ Entry = NewSymEntry (Name, Flags); @@ -1394,23 +1444,42 @@ SymEntry* AddGlobalSym (const char* Name, const Type* T, unsigned Flags) /* Set the symbol attributes */ Entry->Type = TypeDup (T); - /* If this is a function, clear additional fields */ - if (IsTypeFunc (T)) { - Entry->V.F.Seg = 0; - } - /* Add the assembler name of the symbol */ SymSetAsmName (Entry); /* Add the entry to the symbol table */ AddSymEntry (Tab, Entry); + } + /* If this is a function, do we wrap calls to it? */ + if (IsTypeFunc (Entry->Type)) { + SymEntry* WrappedCall; + unsigned int WrappedCallData; + + /* Always use the latest wrapper data for it */ + GetWrappedCall ((void**)&WrappedCall, &WrappedCallData); + if (WrappedCall) { + Entry->V.F.WrappedCall = WrappedCall; + Entry->V.F.WrappedCallData = WrappedCallData; + } + + /* A files cope function declaration with the 'extern' storage + ** class or without the 'inline' specifier ensures that the + ** function definition (if any) is a non-inline definition. + */ + if (SymTab == SymTab0 && + ((Flags & SC_STORAGEMASK) == SC_EXTERN || + (Flags & SC_INLINE) == 0)) { + Entry->Flags |= SC_NOINLINEDEF; + } } /* Add an alias of the global symbol to the local symbol table */ if (Tab == SymTab0 && SymTab != SymTab0 && Entry->Owner != SymTab && Alias == 0) { Alias = AddLocalSym (Name, T, SC_ALIAS, 0); - Alias->V.A.Field = Entry; + if (Alias != 0) { + Alias->V.A.Field = Entry; + } } /* Return the entry */ @@ -1457,14 +1526,22 @@ SymTable* GetLabelSymTab (void) -int SymIsLocal (SymEntry* Sym) -/* Return true if the symbol is defined in the highest lexical level */ +int SymIsLocal (const SymEntry* Sym) +/* Return true if the symbol is declared in the highest lexical level */ { return (Sym->Owner == SymTab || Sym->Owner == TagTab); } +int SymIsGlobal (const SymEntry* Sym) +/* Return true if the symbol is declared in the file scope level */ +{ + return (Sym->Owner == SymTab0 || Sym->Owner == TagTab0); +} + + + void MakeZPSym (const char* Name) /* Mark the given symbol as zero page symbol */ { @@ -1475,7 +1552,7 @@ void MakeZPSym (const char* Name) if (Entry) { Entry->Flags |= SC_ZEROPAGE; } else { - Error ("Undefined symbol: '%s'", Name); + Error ("Undeclared symbol: '%s'", Name); } } @@ -1524,12 +1601,14 @@ void EmitExternals (void) Entry = SymTab->SymHead; while (Entry) { unsigned Flags = Entry->Flags; - if (Flags & SC_EXTERN) { + if (Entry->Owner == SymTab0 && + (Flags & SC_STORAGEMASK) != SC_STATIC && + ((Flags & SC_TYPEMASK) == SC_FUNC || (Flags & SC_TYPEMASK) == SC_NONE)) { /* Only defined or referenced externs */ if (SymIsRef (Entry) && !SymIsDef (Entry)) { /* An import */ g_defimport (Entry->Name, Flags & SC_ZEROPAGE); - } else if (SymIsDef (Entry)) { + } else if (SymIsDef (Entry) && ((Flags & SC_NOINLINEDEF) || (Flags & SC_INLINE) == 0)) { /* An export */ g_defexport (Entry->Name, Flags & SC_ZEROPAGE); } @@ -1558,18 +1637,18 @@ void EmitDebugInfo (void) } Sym = SymTab->SymHead; while (Sym) { - if ((Sym->Flags & (SC_CONST | SC_TYPEMASK)) == 0) { - if (Sym->Flags & SC_AUTO) { + if ((Sym->Flags & SC_TYPEMASK) == 0) { + if ((Sym->Flags & SC_STORAGEMASK) == SC_AUTO) { AddTextLine ("%s, \"%s\", \"00\", auto, %d", Head, Sym->Name, Sym->V.Offs); - } else if (Sym->Flags & SC_REGISTER) { + } else if ((Sym->Flags & SC_STORAGEMASK) == SC_REGISTER) { AddTextLine ("%s, \"%s\", \"00\", register, \"regbank\", %d", Head, Sym->Name, Sym->V.R.RegOffs); } else if (SymIsRef (Sym) && !SymIsDef (Sym)) { AddTextLine ("%s, \"%s\", \"00\", %s, \"%s\"", Head, Sym->Name, - (Sym->Flags & SC_EXTERN)? "extern" : "static", + (Sym->Flags & SC_STORAGEMASK) != SC_STATIC ? "extern" : "static", Sym->AsmName); } } diff --git a/src/cc65/symtab.h b/src/cc65/symtab.h index b711fe606..236bc090a 100644 --- a/src/cc65/symtab.h +++ b/src/cc65/symtab.h @@ -78,6 +78,7 @@ struct LexicalLevel { #define LEX_LEVEL_FUNCTION 2U #define LEX_LEVEL_BLOCK 3U #define LEX_LEVEL_STRUCT 4U +#define LEX_LEVEL_PARAM_LIST 5U /* HACK for error recovery */ /* Forwards */ struct FuncDesc; @@ -141,7 +142,7 @@ void LeaveStructLevel (void); SymEntry* FindSym (const char* Name); -/* Find the symbol with the given name */ +/* Find with the given name the symbol visible in the current scope */ SymEntry* FindGlobalSym (const char* Name); /* Find the symbol with the given name in the global symbol table only */ @@ -150,7 +151,7 @@ SymEntry* FindLocalSym (const char* Name); /* Find the symbol with the given name in the current symbol table only */ SymEntry* FindTagSym (const char* Name); -/* Find the symbol with the given name in the tag table */ +/* Find with the given name the tag symbol visible in the current scope */ SymEntry FindStructField (const Type* TypeArray, const char* Name); /* Find a struct/union field in the fields list. @@ -210,8 +211,11 @@ SymTable* GetFieldSymTab (void); SymTable* GetLabelSymTab (void); /* Return the label symbol table */ -int SymIsLocal (SymEntry* Sym); -/* Return true if the symbol is defined in the highest lexical level */ +int SymIsLocal (const SymEntry* Sym); +/* Return true if the symbol is declared in the highest lexical level */ + +int SymIsGlobal (const SymEntry* Sym); +/* Return true if the symbol is declared in the file scope level */ void MakeZPSym (const char* Name); /* Mark the given symbol as zero page symbol */ diff --git a/src/cc65/testexpr.c b/src/cc65/testexpr.c index bad8b95f1..ced563d01 100644 --- a/src/cc65/testexpr.c +++ b/src/cc65/testexpr.c @@ -39,6 +39,7 @@ #include "expr.h" #include "loadexpr.h" #include "scanner.h" +#include "seqpoint.h" #include "testexpr.h" @@ -75,7 +76,7 @@ unsigned Test (unsigned Label, int Invert) /* Constant rvalue */ if (!Invert && Expr.IVal == 0) { g_jump (Label); - Warning ("Unreachable code"); + UnreachableCodeWarning (); } else if (Invert && Expr.IVal != 0) { g_jump (Label); } diff --git a/src/cc65/typecmp.c b/src/cc65/typecmp.c index c3239652f..a09c80304 100644 --- a/src/cc65/typecmp.c +++ b/src/cc65/typecmp.c @@ -69,6 +69,7 @@ static int EqualFuncParams (const FuncDesc* F1, const FuncDesc* F2) /* Get the symbol types */ const Type* Type1 = Sym1->Type; const Type* Type2 = Sym2->Type; + typecmp_t CmpResult; /* If either of both functions is old style, apply the default ** promotions to the parameter type. @@ -84,9 +85,10 @@ static int EqualFuncParams (const FuncDesc* F1, const FuncDesc* F2) } } - /* Compare this field */ - if (TypeCmp (Type1, Type2).C < TC_EQUAL) { - /* Field types not equal */ + /* Compare types of this parameter */ + CmpResult = TypeCmp (Type1, Type2); + if (CmpResult.C < TC_EQUAL || (CmpResult.F & TCF_MASK_PARAM_DIFF) != 0) { + /* The types are not compatible */ return 0; } @@ -141,7 +143,7 @@ static void SetResult (typecmp_t* Result, typecmpcode_t Val) static typecmp_t* CmpQuals (const Type* lhst, const Type* rhst, typecmp_t* Result) -/* Copare the types regarding thier qualifiers. Return the Result */ +/* Compare the types regarding their qualifiers. Return via pointer *Result */ { TypeCode LeftQual, RightQual; @@ -249,22 +251,32 @@ static void DoCompare (const Type* lhs, const Type* rhs, typecmp_t* Result) /* Compare two types. Determine, where they differ */ - while (lhs->C != T_END) { - - /* Check if the end of the type string is reached */ - if (rhs->C == T_END) { - /* End of comparison reached */ - break; - } - + while (lhs->C != T_END && rhs->C != T_END) { /* Compare qualifiers */ if (CmpQuals (lhs, rhs, Result)->C == TC_INCOMPATIBLE) { + /* No need to compare further */ return; } /* Get the ranks of the left and right hands */ - LeftRank = (GetUnqualTypeCode (lhs) & T_MASK_RANK); - RightRank = (GetUnqualTypeCode (rhs) & T_MASK_RANK); + LeftRank = (GetUnderlyingTypeCode (lhs) & T_MASK_RANK); + RightRank = (GetUnderlyingTypeCode (rhs) & T_MASK_RANK); + + /* Bit-fields are considered compatible if they have the same + ** signedness, bit-offset and bit-width. + */ + if (IsTypeBitField (lhs) || IsTypeBitField (rhs)) { + if (!IsTypeBitField (lhs) || + !IsTypeBitField (rhs) || + lhs->A.B.Offs != rhs->A.B.Offs || + lhs->A.B.Width != rhs->A.B.Width) { + /* Incompatible */ + goto Incompatible; + } + if (LeftRank != RightRank) { + SetResult (Result, TC_STRICT_COMPATIBLE); + } + } /* If one side is a pointer and the other side is an array, both are ** compatible. @@ -280,56 +292,35 @@ static void DoCompare (const Type* lhs, const Type* rhs, typecmp_t* Result) } } - /* Bit-fields are considered compatible if they have the same - ** signedness, bit-offset and bit-width. - */ - if (IsTypeBitField (lhs) || IsTypeBitField (rhs)) { - if (!IsTypeBitField (lhs) || - !IsTypeBitField (rhs) || - lhs->A.B.Offs != rhs->A.B.Offs || - lhs->A.B.Width != rhs->A.B.Width) { - SetResult (Result, TC_INCOMPATIBLE); - } - if (LeftRank != RightRank) { - SetResult (Result, TC_STRICT_COMPATIBLE); - } - } - /* If the ranks are different, the types are incompatible */ if (LeftRank != RightRank) { - SetResult (Result, TC_INCOMPATIBLE); - return; + goto Incompatible; } /* Enums must be handled specially */ if ((IsTypeEnum (lhs) || IsTypeEnum (rhs))) { /* Compare the tag types */ - Sym1 = IsTypeEnum (lhs) ? GetESUTagSym (lhs) : 0; - Sym2 = IsTypeEnum (rhs) ? GetESUTagSym (rhs) : 0; + Sym1 = GetESUTagSym (lhs); + Sym2 = GetESUTagSym (rhs); + /* For the two to be identical, they must be declared in the same + ** scope and have the same name. + */ if (Sym1 != Sym2) { if (Sym1 == 0 || Sym2 == 0) { - /* Only one is an enum. So they can't be identical */ SetResult (Result, TC_STRICT_COMPATIBLE); - - } else { - /* For the two to be identical, they must be in the same - ** scope and have the same name. + } else if (Sym1->Owner != Sym2->Owner || + strcmp (Sym1->Name, Sym2->Name) != 0) { + /* If any one of the two is incomplete, we can't guess + ** their underlying types and have to assume that they + ** be incompatible. */ - if (Sym1->Owner != Sym2->Owner || - strcmp (Sym1->Name, Sym2->Name) != 0) { - - /* If any one of the two is incomplete, we can't guess - ** their underlying types and have to assume that they - ** be incompatible. - */ - if (SizeOf (lhs) == 0 || SizeOf (rhs) == 0) { - SetResult (Result, TC_INCOMPATIBLE); - return; - } + if (SizeOf (lhs) == 0 || SizeOf (rhs) == 0) { + goto Incompatible; } + SetResult (Result, TC_STRICT_COMPATIBLE); } } @@ -353,10 +344,10 @@ static void DoCompare (const Type* lhs, const Type* rhs, typecmp_t* Result) case T_RANK_PTR: ++Result->Indirections; if (Result->Indirections == 1) { - if ((GetUnqualTypeCode (lhs + 1) & T_MASK_RANK) == T_RANK_VOID) { + if ((GetUnderlyingTypeCode (lhs + 1) & T_MASK_RANK) == T_RANK_VOID) { Result->F |= TCF_VOID_PTR_ON_LEFT; } - if ((GetUnqualTypeCode (rhs + 1) & T_MASK_RANK) == T_RANK_VOID) { + if ((GetUnderlyingTypeCode (rhs + 1) & T_MASK_RANK) == T_RANK_VOID) { Result->F |= TCF_VOID_PTR_ON_RIGHT; } } else { @@ -383,15 +374,13 @@ static void DoCompare (const Type* lhs, const Type* rhs, typecmp_t* Result) /* Check the remaining flags */ if ((F1->Flags & ~FD_IGNORE) != (F2->Flags & ~FD_IGNORE)) { /* Flags differ */ - SetResult (Result, TC_INCOMPATIBLE); - return; + goto Incompatible; } /* Compare the parameter lists */ if (EqualFuncParams (F1, F2) == 0) { /* Parameter list is not identical */ - SetResult (Result, TC_INCOMPATIBLE); - return; + goto Incompatible; } } @@ -406,8 +395,7 @@ static void DoCompare (const Type* lhs, const Type* rhs, typecmp_t* Result) if (LeftCount != UNSPECIFIED && RightCount != UNSPECIFIED) { /* Member count given but different */ - SetResult (Result, TC_INCOMPATIBLE); - return; + goto Incompatible; } /* We take into account which side is more specified */ @@ -436,8 +424,7 @@ static void DoCompare (const Type* lhs, const Type* rhs, typecmp_t* Result) /* This shouldn't happen in the current code base, but ** we still handle this case to be future-proof. */ - SetResult (Result, TC_INCOMPATIBLE); - return; + goto Incompatible; } } @@ -453,9 +440,11 @@ static void DoCompare (const Type* lhs, const Type* rhs, typecmp_t* Result) /* Check if lhs and rhs both reached ends */ if (lhs->C == T_END && rhs->C == T_END) { SetResult (Result, TC_IDENTICAL); - } else { - SetResult (Result, TC_INCOMPATIBLE); + return; } + +Incompatible: + SetResult (Result, TC_INCOMPATIBLE); } @@ -483,7 +472,10 @@ typecmp_t TypeCmp (const Type* lhs, const Type* rhs) void TypeCompatibilityDiagnostic (const Type* NewType, const Type* OldType, int IsError, const char* Msg) -/* Print error or warning message about type compatibility with proper type names */ +/* Print error or warning message about type compatibility with proper type +** names. The format string shall contain two '%s' specifiers for the names of +** the two types. +*/ { StrBuf NewTypeName = STATIC_STRBUF_INITIALIZER; StrBuf OldTypeName = STATIC_STRBUF_INITIALIZER; diff --git a/src/cc65/typecmp.h b/src/cc65/typecmp.h index 367df5245..43acc7ea5 100644 --- a/src/cc65/typecmp.h +++ b/src/cc65/typecmp.h @@ -68,15 +68,17 @@ typedef enum { TCF_VOID_PTR_ON_LEFT = 0x01, /* lhs is a void pointer */ TCF_VOID_PTR_ON_RIGHT = 0x02, /* rhs is a void pointer */ TCF_MASK_VOID_PTR = TCF_VOID_PTR_ON_LEFT | TCF_VOID_PTR_ON_RIGHT, - TCF_QUAL_DIFF = 0x04, /* CVR qualifiers differ in a way that doesn't matter */ + TCF_QUAL_DIFF = 0x04, /* lhs doesn't have all of CVR qualifiers of rhs */ TCF_QUAL_IMPLICIT = 0x08, /* CVR qualifiers of lhs are stricter than those of rhs */ - TCF_PTR_QUAL_DIFF = 0x10, /* CVR qualifiers of pointers differ */ - TCF_PTR_QUAL_IMPLICIT = 0x20, /* CVR qualifiers of pointers are stricter on lhs than those on rhs */ - TCF_MASK_C_QUAL_DIFF = 0x3C, /* All C Standard qualifiers */ + TCF_MASK_CVR_DIFF = 0x0C, /* All CVR qualifiers */ + TCF_PTR_QUAL_DIFF = 0x10, /* lhs pointee doesn't have all of CVR qualifiers of rhs pointee */ + TCF_PTR_QUAL_IMPLICIT = 0x20, /* CVR qualifiers of pointees are stricter on lhs than those on rhs */ + TCF_MASK_PTR_QUAL_DIFF = 0x30, /* All CVR qualifiers of pointees */ TCF_ADDRSIZE_QUAL_DIFF = 0x40, /* Address size qualifiers differ */ TCF_CCONV_QUAL_DIFF = 0x80, /* Function calling conventions differ. Unused now */ TCF_INCOMPATIBLE_QUAL = TCF_ADDRSIZE_QUAL_DIFF | TCF_CCONV_QUAL_DIFF, - TCF_MASK_QUAL = TCF_MASK_C_QUAL_DIFF | TCF_INCOMPATIBLE_QUAL, + TCF_MASK_PARAM_DIFF = TCF_MASK_PTR_QUAL_DIFF | TCF_INCOMPATIBLE_QUAL, + TCF_MASK_QUAL = TCF_MASK_CVR_DIFF | TCF_MASK_PTR_QUAL_DIFF | TCF_INCOMPATIBLE_QUAL, } typecmpflag_t; typedef struct { @@ -97,7 +99,10 @@ typecmp_t TypeCmp (const Type* lhs, const Type* rhs); /* Compare two types and return the result */ void TypeCompatibilityDiagnostic (const Type* NewType, const Type* OldType, int IsError, const char* Msg); -/* Print error or warning message about type compatibility with proper type names */ +/* Print error or warning message about type compatibility with proper type +** names. The format string shall contain two '%s' specifiers for the names of +** the two types. +*/ diff --git a/src/cc65/typeconv.c b/src/cc65/typeconv.c index 6bdb45b5f..6e9fad69a 100644 --- a/src/cc65/typeconv.c +++ b/src/cc65/typeconv.c @@ -111,7 +111,7 @@ static void DoConversion (ExprDesc* Expr, const Type* NewType, int Explicit) LoadExpr (CF_NONE, Expr); /* Emit typecast code */ - g_typecast (TypeOf (NewType), TypeOf (OldType)); + g_typecast (CG_TypeOf (NewType), CG_TypeOf (OldType)); /* Value is now in primary and an rvalue */ ED_FinalizeRValLoad (Expr); @@ -123,6 +123,17 @@ static void DoConversion (ExprDesc* Expr, const Type* NewType, int Explicit) ** to handle sign extension correctly. */ + /* If this is a floating point constant, convert to integer, + ** and warn if precision is discarded. + */ + if (IsClassFloat (OldType) && IsClassInt (NewType)) { + long IVal = (long)Expr->V.FVal.V; + if ((Expr->V.FVal.V != FP_D_FromInt(IVal).V) && !Explicit) { + Warning ("Floating point constant (%f) converted to integer loses precision (%ld)",Expr->V.FVal.V,IVal); + } + Expr->IVal = IVal; + } + /* Check if the new datatype will have a smaller range. If it ** has a larger range, things are OK, since the value is ** internally already represented by a long. @@ -167,7 +178,7 @@ static void DoConversion (ExprDesc* Expr, const Type* NewType, int Explicit) LoadExpr (CF_NONE, Expr); /* Emit typecast code. */ - g_typecast (TypeOf (NewType), TypeOf (OldType)); + g_typecast (CG_TypeOf (NewType), CG_TypeOf (OldType)); /* Value is now an rvalue in the primary */ ED_FinalizeRValLoad (Expr); @@ -310,15 +321,9 @@ void TypeCast (ExprDesc* Expr) { Type NewType[MAXTYPELEN]; - /* Skip the left paren */ - NextToken (); - - /* Read the type */ + /* Read the type enclosed in parentheses */ ParseType (NewType); - /* Closing paren */ - ConsumeRParen (); - /* Read the expression we have to cast */ hie10 (Expr); @@ -422,10 +427,6 @@ void TypeComposition (Type* lhs, const Type* rhs) ** type or this fails with a critical check. */ { - FuncDesc* F1; - FuncDesc* F2; - long LeftCount, RightCount; - /* Compose two types */ while (lhs->C != T_END) { @@ -435,13 +436,13 @@ void TypeComposition (Type* lhs, const Type* rhs) } /* Check for sanity */ - CHECK (GetUnqualTypeCode (lhs) == GetUnqualTypeCode (rhs)); + CHECK (GetUnderlyingTypeCode (lhs) == GetUnderlyingTypeCode (rhs)); /* Check for special type elements */ if (IsTypeFunc (lhs)) { /* Compose the function descriptors */ - F1 = GetFuncDesc (lhs); - F2 = GetFuncDesc (rhs); + FuncDesc* F1 = GetFuncDesc (lhs); + FuncDesc* F2 = GetFuncDesc (rhs); /* If F1 has an empty parameter list (which does also mean, it is ** not a function definition, because the flag is reset in this @@ -465,8 +466,8 @@ void TypeComposition (Type* lhs, const Type* rhs) } } else if (IsTypeArray (lhs)) { /* Check member count */ - LeftCount = GetElementCount (lhs); - RightCount = GetElementCount (rhs); + long LeftCount = GetElementCount (lhs); + long RightCount = GetElementCount (rhs); /* Set composite type if it is requested */ if (LeftCount != UNSPECIFIED) { @@ -474,41 +475,10 @@ void TypeComposition (Type* lhs, const Type* rhs) } else if (RightCount != UNSPECIFIED) { SetElementCount (lhs, RightCount); } - } else { - /* Combine the qualifiers */ - if (IsClassPtr (lhs)) { - ++lhs; - ++rhs; - lhs->C |= GetQualifier (rhs); - } } /* Next type string element */ ++lhs; ++rhs; } - - return; -} - - - -FuncDesc* RefineFuncDesc (Type* OldType, const Type* NewType) -/* Refine the existing function descriptor with a new one */ -{ - FuncDesc* Old = GetFuncDesc (OldType); - FuncDesc* New = GetFuncDesc (NewType); - - CHECK (Old != 0 && New != 0); - - if ((New->Flags & FD_EMPTY) == 0) { - if ((Old->Flags & FD_EMPTY) == 0) { - TypeComposition (OldType, NewType); - } else { - TypeCopy (OldType, NewType); - Old->Flags &= ~FD_EMPTY; - } - } - - return Old; } diff --git a/src/cc65/typeconv.h b/src/cc65/typeconv.h index 839c5e43e..1d79a446c 100644 --- a/src/cc65/typeconv.h +++ b/src/cc65/typeconv.h @@ -63,9 +63,6 @@ void TypeComposition (Type* lhs, const Type* rhs); ** type or this fails with a critical check. */ -FuncDesc* RefineFuncDesc (Type* OldType, const Type* NewType); -/* Refine the existing function descriptor with a new one */ - /* End of typeconv.h */ diff --git a/src/chrcvt65.vcxproj b/src/chrcvt65.vcxproj index 1e5c753b5..d44380c4e 100644 --- a/src/chrcvt65.vcxproj +++ b/src/chrcvt65.vcxproj @@ -5,10 +5,18 @@ <Configuration>Debug</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> <ProjectConfiguration Include="Release|Win32"> <Configuration>Release</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> </ItemGroup> <PropertyGroup Label="Globals"> <ProjectGuid>{1C7A3FEF-DD0B-4B10-BC33-C3BE29BF67CC}</ProjectGuid> @@ -21,15 +29,22 @@ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <UseDebugLibraries>true</UseDebugLibraries> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <UseDebugLibraries>true</UseDebugLibraries> + </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> <UseDebugLibraries>false</UseDebugLibraries> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <UseDebugLibraries>false</UseDebugLibraries> + </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <ImportGroup Label="ExtensionSettings"> </ImportGroup> <PropertyGroup Label="UserMacros" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> </PropertyGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> @@ -40,6 +55,15 @@ <SubSystem>Console</SubSystem> </Link> </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <PreprocessorDefinitions>_CONSOLE;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <DisableSpecificWarnings>4244;%(DisableSpecificWarnings)</DisableSpecificWarnings> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + </Link> + </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ClCompile> <PreprocessorDefinitions>_CONSOLE;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> @@ -48,6 +72,15 @@ <SubSystem>Console</SubSystem> </Link> </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <PreprocessorDefinitions>_CONSOLE;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <DisableSpecificWarnings>4244;%(DisableSpecificWarnings)</DisableSpecificWarnings> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + </Link> + </ItemDefinitionGroup> <ItemGroup> <ClCompile Include="chrcvt65\error.c" /> <ClCompile Include="chrcvt65\main.c" /> diff --git a/src/cl65.vcxproj b/src/cl65.vcxproj index 67b7eb087..688926557 100644 --- a/src/cl65.vcxproj +++ b/src/cl65.vcxproj @@ -5,10 +5,18 @@ <Configuration>Debug</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> <ProjectConfiguration Include="Release|Win32"> <Configuration>Release</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> </ItemGroup> <PropertyGroup Label="Globals"> <ProjectGuid>{F657912F-050A-488B-B203-50ED5715CDD7}</ProjectGuid> @@ -21,15 +29,22 @@ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <UseDebugLibraries>true</UseDebugLibraries> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <UseDebugLibraries>true</UseDebugLibraries> + </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> <UseDebugLibraries>false</UseDebugLibraries> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <UseDebugLibraries>false</UseDebugLibraries> + </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <ImportGroup Label="ExtensionSettings"> </ImportGroup> <PropertyGroup Label="UserMacros" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> </PropertyGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> @@ -40,6 +55,15 @@ <SubSystem>Console</SubSystem> </Link> </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <PreprocessorDefinitions>_CONSOLE;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <DisableSpecificWarnings>4244;4267;%(DisableSpecificWarnings)</DisableSpecificWarnings> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + </Link> + </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ClCompile> <PreprocessorDefinitions>_CONSOLE;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> @@ -48,6 +72,15 @@ <SubSystem>Console</SubSystem> </Link> </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <PreprocessorDefinitions>_CONSOLE;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <DisableSpecificWarnings>4244;4267;%(DisableSpecificWarnings)</DisableSpecificWarnings> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + </Link> + </ItemDefinitionGroup> <ItemGroup> <ClCompile Include="cl65\error.c" /> <ClCompile Include="cl65\global.c" /> diff --git a/src/cl65/main.c b/src/cl65/main.c index 553fb9ca6..31d805181 100644 --- a/src/cl65/main.c +++ b/src/cl65/main.c @@ -1305,6 +1305,9 @@ static void OptStaticLocals (const char* Opt attribute ((unused)), static void OptTarget (const char* Opt attribute ((unused)), const char* Arg) /* Set the target system */ { + if (FirstInput) { + Error ("Target must be specified before input files"); + } Target = FindTarget (Arg); if (Target == TGT_UNKNOWN) { Error ("No such target system: '%s'", Arg); @@ -1610,7 +1613,7 @@ int main (int argc, char* argv []) } /* Determine the file type by the extension */ - switch (GetFileType (Arg)) { + switch (GetTypeOfFile (Arg)) { case FILETYPE_C: /* Compile the file */ diff --git a/src/co65.vcxproj b/src/co65.vcxproj index 9f5959d89..fa6a1e315 100644 --- a/src/co65.vcxproj +++ b/src/co65.vcxproj @@ -5,10 +5,18 @@ <Configuration>Debug</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> <ProjectConfiguration Include="Release|Win32"> <Configuration>Release</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> </ItemGroup> <PropertyGroup Label="Globals"> <ProjectGuid>{F5DB5D1A-05BC-48FE-B346-4E96DD522AA2}</ProjectGuid> @@ -21,15 +29,22 @@ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <UseDebugLibraries>true</UseDebugLibraries> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <UseDebugLibraries>true</UseDebugLibraries> + </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> <UseDebugLibraries>false</UseDebugLibraries> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <UseDebugLibraries>false</UseDebugLibraries> + </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <ImportGroup Label="ExtensionSettings"> </ImportGroup> <PropertyGroup Label="UserMacros" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> </PropertyGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> @@ -40,6 +55,14 @@ <SubSystem>Console</SubSystem> </Link> </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <PreprocessorDefinitions>_CONSOLE;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + </Link> + </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ClCompile> <PreprocessorDefinitions>_CONSOLE;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> @@ -48,6 +71,14 @@ <SubSystem>Console</SubSystem> </Link> </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <PreprocessorDefinitions>_CONSOLE;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + </Link> + </ItemDefinitionGroup> <ItemGroup> <ClCompile Include="co65\convert.c" /> <ClCompile Include="co65\error.c" /> diff --git a/src/common.vcxproj b/src/common.vcxproj index df99fc4a9..6098c98a0 100644 --- a/src/common.vcxproj +++ b/src/common.vcxproj @@ -5,10 +5,18 @@ <Configuration>Debug</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> <ProjectConfiguration Include="Release|Win32"> <Configuration>Release</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> </ItemGroup> <PropertyGroup Label="Globals"> <ProjectGuid>{71DC1F68-BFC4-478C-8655-C8E9C9654D2B}</ProjectGuid> @@ -22,16 +30,25 @@ <ConfigurationType>StaticLibrary</ConfigurationType> <UseDebugLibraries>true</UseDebugLibraries> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <ConfigurationType>StaticLibrary</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> <ConfigurationType>StaticLibrary</ConfigurationType> <UseDebugLibraries>false</UseDebugLibraries> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <ConfigurationType>StaticLibrary</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <ImportGroup Label="ExtensionSettings"> </ImportGroup> <PropertyGroup Label="UserMacros" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> </PropertyGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> @@ -39,11 +56,23 @@ <PreprocessorDefinitions>_LIB;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> </ClCompile> </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <PreprocessorDefinitions>_LIB;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <DisableSpecificWarnings>4244;4267;%(DisableSpecificWarnings)</DisableSpecificWarnings> + </ClCompile> + </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ClCompile> <PreprocessorDefinitions>_LIB;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> </ClCompile> </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <PreprocessorDefinitions>_LIB;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <DisableSpecificWarnings>4244;4267;%(DisableSpecificWarnings)</DisableSpecificWarnings> + </ClCompile> + </ItemDefinitionGroup> <ItemGroup> <ClInclude Include="common\abend.h" /> <ClInclude Include="common\addrsize.h" /> diff --git a/src/common/filetype.c b/src/common/filetype.c index a5bac640d..ae8b636dc 100644 --- a/src/common/filetype.c +++ b/src/common/filetype.c @@ -92,8 +92,7 @@ static const FileId TypeTable[] = { /*****************************************************************************/ - -FILETYPE GetFileType (const char* Name) +FILETYPE GetTypeOfFile (const char* Name) /* Determine the type of the given file by looking at the name. If the file ** type could not be determined, the function returns FILETYPE_UNKOWN. */ diff --git a/src/common/filetype.h b/src/common/filetype.h index f4beae73a..0738078ba 100644 --- a/src/common/filetype.h +++ b/src/common/filetype.h @@ -63,7 +63,7 @@ typedef enum { -FILETYPE GetFileType (const char* Name); +FILETYPE GetTypeOfFile (const char* Name); /* Determine the type of the given file by looking at the name. If the file ** type could not be determined, the function returns FILETYPE_UNKOWN. */ diff --git a/src/common/strbuf.c b/src/common/strbuf.c index 79419f1c4..c5b27ee63 100644 --- a/src/common/strbuf.c +++ b/src/common/strbuf.c @@ -82,6 +82,7 @@ StrBuf* SB_InitFromString (StrBuf* B, const char* S) B->Len = strlen (S); B->Index = 0; B->Buf = (char*) S; + B->Cooked = (char*) S; return B; } @@ -92,6 +93,7 @@ void SB_Done (StrBuf* B) { if (B->Allocated) { xfree (B->Buf); + xfree (B->Cooked); } } @@ -146,10 +148,12 @@ void SB_Realloc (StrBuf* B, unsigned NewSize) */ if (B->Allocated) { /* Just reallocate the block */ - B->Buf = xrealloc (B->Buf, NewAllocated); + B->Buf = xrealloc (B->Buf, NewAllocated); + B->Cooked = xrealloc (B->Cooked, NewAllocated); } else { /* Allocate a new block and copy */ - B->Buf = memcpy (xmalloc (NewAllocated), B->Buf, B->Len); + B->Buf = memcpy (xmalloc (NewAllocated), B->Buf, B->Len); + B->Cooked = memcpy (xmalloc (NewAllocated), B->Cooked, B->Len); } /* Remember the new block size */ @@ -178,10 +182,12 @@ static void SB_CheapRealloc (StrBuf* B, unsigned NewSize) /* Free the old buffer if there is one */ if (B->Allocated) { xfree (B->Buf); + xfree (B->Cooked); } /* Allocate a fresh block */ - B->Buf = xmalloc (NewAllocated); + B->Buf = xmalloc (NewAllocated); + B->Cooked = xmalloc (NewAllocated); /* Remember the new block size */ B->Allocated = NewAllocated; @@ -222,6 +228,7 @@ void SB_Terminate (StrBuf* B) SB_Realloc (B, NewLen); } B->Buf[B->Len] = '\0'; + B->Cooked[B->Len] = '\0'; } @@ -234,6 +241,22 @@ void SB_CopyBuf (StrBuf* Target, const char* Buf, unsigned Size) SB_CheapRealloc (Target, Size); } memcpy (Target->Buf, Buf, Size); + memcpy (Target->Cooked, Buf, Size); /* nothing raw */ + } + Target->Len = Size; +} + + + +void SB_CopyBufCooked (StrBuf* Target, const char* Buf, const char* Cooked, unsigned Size) +/* Copy Buf and Cooked to Target, discarding the old contents of Target */ +{ + if (Size) { + if (Target->Allocated < Size) { + SB_CheapRealloc (Target, Size); + } + memcpy (Target->Buf, Buf, Size); + memcpy (Target->Cooked, Cooked, Size); } Target->Len = Size; } @@ -254,7 +277,7 @@ void SB_CopyStr (StrBuf* Target, const char* S) void SB_Copy (StrBuf* Target, const StrBuf* Source) /* Copy Source to Target, discarding the old contents of Target */ { - SB_CopyBuf (Target, Source->Buf, Source->Len); + SB_CopyBufCooked (Target, Source->Buf, Source->Cooked, Source->Len); Target->Index = Source->Index; } #endif @@ -269,6 +292,21 @@ void SB_AppendChar (StrBuf* B, int C) SB_Realloc (B, NewLen); } B->Buf[B->Len] = (char) C; + B->Cooked[B->Len] = (char) C; + B->Len = NewLen; +} + + + +void SB_AppendCharCooked (StrBuf* B, int C, int Cooked) +/* Append a character to a string buffer */ +{ + unsigned NewLen = B->Len + 1; + if (NewLen > B->Allocated) { + SB_Realloc (B, NewLen); + } + B->Buf[B->Len] = (char) C; + B->Cooked[B->Len] = (char) (Cooked ? C : 0); B->Len = NewLen; } @@ -282,6 +320,7 @@ void SB_AppendBuf (StrBuf* B, const char* S, unsigned Size) SB_Realloc (B, NewLen); } memcpy (B->Buf + B->Len, S, Size); + memcpy (B->Cooked + B->Len, S, Size); B->Len = NewLen; } @@ -301,7 +340,13 @@ void SB_AppendStr (StrBuf* B, const char* S) void SB_Append (StrBuf* Target, const StrBuf* Source) /* Append the contents of Source to Target */ { - SB_AppendBuf (Target, Source->Buf, Source->Len); + unsigned NewLen = Target->Len + Source->Len; + if (NewLen > Target->Allocated) { + SB_Realloc (Target, NewLen); + } + memcpy (Target->Buf + Target->Len, Source->Buf, Source->Len); + memcpy (Target->Cooked + Target->Len, Source->Cooked, Source->Len); + Target->Len = NewLen; } #endif diff --git a/src/common/strbuf.h b/src/common/strbuf.h index e0602a6c8..372b1be0b 100644 --- a/src/common/strbuf.h +++ b/src/common/strbuf.h @@ -53,10 +53,17 @@ /*****************************************************************************/ +/* We want to track whether a character is "raw" or not. */ +/* "raw" characters should NOT be translated when translating a string. */ +/* We do this by keeping a second array parallel to "Buf" called "Cooked". */ +/* Think of "cooked" as the inverse of "raw". */ +/* If Cooked[n] is 0, then the character is raw and should not be translated. */ +/* This was done to keep LIT_STR_BUFFER sane. */ typedef struct StrBuf StrBuf; struct StrBuf { char* Buf; /* Pointer to buffer */ + char* Cooked; /* Pointer to cooked buffer */ unsigned Len; /* Length of the string */ unsigned Index; /* Used for reading (Get and friends) */ unsigned Allocated; /* Size of allocated memory */ @@ -66,13 +73,13 @@ struct StrBuf { extern const StrBuf EmptyStrBuf; /* Initializer for static string bufs */ -#define STATIC_STRBUF_INITIALIZER { 0, 0, 0, 0 } +#define STATIC_STRBUF_INITIALIZER { 0, 0, 0, 0, 0 } /* Initializer for auto string bufs */ -#define AUTO_STRBUF_INITIALIZER { 0, 0, 0, 0 } +#define AUTO_STRBUF_INITIALIZER { 0, 0, 0, 0, 0 } /* Initialize with a string literal (beware: evaluates str twice!) */ -#define LIT_STRBUF_INITIALIZER(str) { (char*)str, sizeof(str)-1, 0, 0 } +#define LIT_STRBUF_INITIALIZER(str) { (char*)str, (char *)str, sizeof(str)-1, 0, 0 } @@ -164,6 +171,16 @@ INLINE char* SB_GetBuf (StrBuf* B) # define SB_GetBuf(B) (B)->Buf #endif +#if defined(HAVE_INLINE) +INLINE char* SB_GetCooked (StrBuf* B) +/* Return a cooked pointer */ +{ + return B->Cooked; +} +#else +# define SB_GetCooked(B) (B)->Cooked +#endif + #if defined(HAVE_INLINE) INLINE char SB_At (const StrBuf* B, unsigned Index) /* Get a character from the buffer */ @@ -310,6 +327,9 @@ void SB_Terminate (StrBuf* B); void SB_CopyBuf (StrBuf* Target, const char* Buf, unsigned Size); /* Copy Buf to Target, discarding the old contents of Target */ +void SB_CopyBufCooked (StrBuf* Target, const char* Buf, const char *Cooked, unsigned Size); +/* Copy Buf and Cooked to Target, discarding the old contents of Target */ + #if defined(HAVE_INLINE) INLINE void SB_CopyStr (StrBuf* Target, const char* S) /* Copy S to Target, discarding the old contents of Target */ @@ -325,7 +345,7 @@ void SB_CopyStr (StrBuf* Target, const char* S); INLINE void SB_Copy (StrBuf* Target, const StrBuf* Source) /* Copy Source to Target, discarding the old contents of Target */ { - SB_CopyBuf (Target, Source->Buf, Source->Len); + SB_CopyBufCooked (Target, Source->Buf, Source->Cooked, Source->Len); Target->Index = Source->Index; } #else @@ -336,6 +356,9 @@ void SB_Copy (StrBuf* Target, const StrBuf* Source); void SB_AppendChar (StrBuf* B, int C); /* Append a character to a string buffer */ +void SB_AppendCharCooked (StrBuf* B, int C, int Cooked); +/* Append a character to a string buffer, raw if Cooked == 0 */ + void SB_AppendBuf (StrBuf* B, const char* S, unsigned Size); /* Append a character buffer to the end of the string buffer */ @@ -354,7 +377,13 @@ void SB_AppendStr (StrBuf* B, const char* S); INLINE void SB_Append (StrBuf* Target, const StrBuf* Source) /* Append the contents of Source to Target */ { - SB_AppendBuf (Target, Source->Buf, Source->Len); + unsigned NewLen = Target->Len + Source->Len; + if (NewLen > Target->Allocated) { + SB_Realloc (Target, NewLen); + } + memcpy (Target->Buf + Target->Len, Source->Buf, Source->Len); + memcpy (Target->Cooked + Target->Len, Source->Cooked, Source->Len); + Target->Len = NewLen; } #else void SB_Append (StrBuf* Target, const StrBuf* Source); diff --git a/src/common/target.c b/src/common/target.c index ad62990bd..b50478e16 100644 --- a/src/common/target.c +++ b/src/common/target.c @@ -173,6 +173,7 @@ static const TargetEntry TargetMap[] = { { "pce", TGT_PCENGINE }, { "pet", TGT_PET }, { "plus4", TGT_PLUS4 }, + { "rp6502", TGT_RP6502 }, { "sim6502", TGT_SIM6502 }, { "sim65c02", TGT_SIM65C02 }, { "supervision", TGT_SUPERVISION }, @@ -221,6 +222,7 @@ static const TargetProperties PropertyTable[TGT_COUNT] = { { "cx16", CPU_65C02, BINFMT_BINARY, CTPET }, { "sym1", CPU_6502, BINFMT_BINARY, CTNone }, { "kim1", CPU_6502, BINFMT_BINARY, CTNone }, + { "rp6502", CPU_65C02, BINFMT_BINARY, CTNone }, }; /* Target system */ diff --git a/src/common/target.h b/src/common/target.h index 0cec74b6e..730b8211e 100644 --- a/src/common/target.h +++ b/src/common/target.h @@ -88,6 +88,7 @@ typedef enum { TGT_CX16, TGT_SYM1, TGT_KIM1, + TGT_RP6502, TGT_COUNT /* Number of target systems */ } target_t; diff --git a/src/common/tgttrans.c b/src/common/tgttrans.c index 3310eab81..3ac41f09e 100644 --- a/src/common/tgttrans.c +++ b/src/common/tgttrans.c @@ -121,7 +121,19 @@ void TgtTranslateStrBuf (StrBuf* Buf) ** system character set. */ { - TgtTranslateBuf (SB_GetBuf (Buf), SB_GetLen (Buf)); + unsigned char* B = (unsigned char*)SB_GetBuf(Buf); + unsigned char* Cooked = (unsigned char*)SB_GetCooked(Buf); + unsigned Len = SB_GetLen(Buf); + + /* Translate */ + while (Len--) { + if (*Cooked) { + *B = Tab[*B]; + } + /* else { *B = *B; } */ + ++B; + ++Cooked; + } } @@ -129,7 +141,7 @@ void TgtTranslateStrBuf (StrBuf* Buf) void TgtTranslateSet (unsigned Index, unsigned char C) /* Set the translation code for the given character */ { - CHECK (Index < sizeof (Tab)); + CHECK (Index < (sizeof (Tab) / sizeof(Tab[0]))); Tab[Index] = C; } diff --git a/src/common/xsprintf.c b/src/common/xsprintf.c index 556e4f359..aa3183752 100644 --- a/src/common/xsprintf.c +++ b/src/common/xsprintf.c @@ -352,8 +352,8 @@ static void StoreOffset (PrintfCtrl* P) /* Store the current output offset (%n format spec) */ { switch (P->LengthMod) { - case lmChar: *va_arg (P->ap, int*) = P->BufFill; break; - case lmShort: *va_arg (P->ap, int*) = P->BufFill; break; + case lmChar: *va_arg (P->ap, char*) = (char)P->BufFill; break; + case lmShort: *va_arg (P->ap, short*) = (short)P->BufFill; break; case lmInt: *va_arg (P->ap, int*) = P->BufFill; break; case lmLong: *va_arg (P->ap, long*) = P->BufFill; break; case lmIntMax: *va_arg (P->ap, intmax_t*) = P->BufFill; break; diff --git a/src/da65.vcxproj b/src/da65.vcxproj index a40daf1d6..9d2b0ded6 100644 --- a/src/da65.vcxproj +++ b/src/da65.vcxproj @@ -5,10 +5,18 @@ <Configuration>Debug</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> <ProjectConfiguration Include="Release|Win32"> <Configuration>Release</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> </ItemGroup> <PropertyGroup Label="Globals"> <ProjectGuid>{0BCFB793-2B25-40E2-B265-75848824AC4C}</ProjectGuid> @@ -21,15 +29,22 @@ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <UseDebugLibraries>true</UseDebugLibraries> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <UseDebugLibraries>true</UseDebugLibraries> + </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> <UseDebugLibraries>false</UseDebugLibraries> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <UseDebugLibraries>false</UseDebugLibraries> + </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <ImportGroup Label="ExtensionSettings"> </ImportGroup> <PropertyGroup Label="UserMacros" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> </PropertyGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> @@ -40,6 +55,15 @@ <SubSystem>Console</SubSystem> </Link> </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <PreprocessorDefinitions>_CONSOLE;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <DisableSpecificWarnings>4244;4267;%(DisableSpecificWarnings)</DisableSpecificWarnings> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + </Link> + </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ClCompile> <PreprocessorDefinitions>_CONSOLE;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> @@ -48,6 +72,15 @@ <SubSystem>Console</SubSystem> </Link> </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <PreprocessorDefinitions>_CONSOLE;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <DisableSpecificWarnings>4244;4267;%(DisableSpecificWarnings)</DisableSpecificWarnings> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + </Link> + </ItemDefinitionGroup> <ItemGroup> <ClCompile Include="da65\asminc.c" /> <ClCompile Include="da65\attrtab.c" /> diff --git a/src/da65/attrtab.c b/src/da65/attrtab.c index b70e017a1..e35530afd 100644 --- a/src/da65/attrtab.c +++ b/src/da65/attrtab.c @@ -149,7 +149,7 @@ unsigned GetGranularity (attr_t Style) case atSkip: default: - Internal ("GetGraularity called for style = %d", Style); + Internal ("GetGranularity called for style = %d", Style); return 0; } } diff --git a/src/dbginfo/dbginfo.c b/src/dbginfo/dbginfo.c index fee41012c..1f693e513 100644 --- a/src/dbginfo/dbginfo.c +++ b/src/dbginfo/dbginfo.c @@ -127,6 +127,7 @@ typedef enum { TOK_ABSOLUTE = TOK_FIRST_KEYWORD, /* ABSOLUTE keyword */ TOK_ADDRSIZE, /* ADDRSIZE keyword */ TOK_AUTO, /* AUTO keyword */ + TOK_BANK, /* BANK keyword */ TOK_COUNT, /* COUNT keyword */ TOK_CSYM, /* CSYM keyword */ TOK_DEF, /* DEF keyword */ @@ -347,6 +348,7 @@ struct SegInfo { cc65_size Size; /* Size of segment */ char* OutputName; /* Name of output file */ unsigned long OutputOffs; /* Offset in output file */ + unsigned Bank; /* Bank number of memory area */ char Name[1]; /* Name of segment */ }; @@ -1618,7 +1620,8 @@ static int CompareScopeInfoByName (const void* L, const void* R) static SegInfo* NewSegInfo (const StrBuf* Name, unsigned Id, cc65_addr Start, cc65_addr Size, - const StrBuf* OutputName, unsigned long OutputOffs) + const StrBuf* OutputName, unsigned long OutputOffs, + unsigned Bank) /* Create a new SegInfo struct and return it */ { /* Allocate memory */ @@ -1628,6 +1631,7 @@ static SegInfo* NewSegInfo (const StrBuf* Name, unsigned Id, S->Id = Id; S->Start = Start; S->Size = Size; + S->Bank = Bank; if (SB_GetLen (OutputName) > 0) { /* Output file given */ S->OutputName = SB_StrDup (OutputName); @@ -1676,6 +1680,7 @@ static void CopySegInfo (cc65_segmentdata* D, const SegInfo* S) D->segment_size = S->Size; D->output_name = S->OutputName; D->output_offs = S->OutputOffs; + D->segment_bank = S->Bank; } @@ -2528,6 +2533,7 @@ static void NextToken (InputData* D) { "abs", TOK_ABSOLUTE }, { "addrsize", TOK_ADDRSIZE }, { "auto", TOK_AUTO }, + { "bank", TOK_BANK }, { "count", TOK_COUNT }, { "csym", TOK_CSYM }, { "def", TOK_DEF }, @@ -3838,6 +3844,7 @@ static void ParseSegment (InputData* D) StrBuf Name = STRBUF_INITIALIZER; StrBuf OutputName = STRBUF_INITIALIZER; unsigned long OutputOffs = 0; + unsigned Bank = 0; SegInfo* S; enum { ibNone = 0x000, @@ -3850,6 +3857,7 @@ static void ParseSegment (InputData* D) ibSize = 0x020, ibStart = 0x040, ibType = 0x080, + ibBank = 0x100, ibRequired = ibId | ibName | ibStart | ibSize | ibAddrSize | ibType, } InfoBits = ibNone; @@ -3863,10 +3871,11 @@ static void ParseSegment (InputData* D) Token Tok; /* Something we know? */ - if (D->Tok != TOK_ADDRSIZE && D->Tok != TOK_ID && - D->Tok != TOK_NAME && D->Tok != TOK_OUTPUTNAME && - D->Tok != TOK_OUTPUTOFFS && D->Tok != TOK_SIZE && - D->Tok != TOK_START && D->Tok != TOK_TYPE) { + if (D->Tok != TOK_ADDRSIZE && D->Tok != TOK_BANK && + D->Tok != TOK_ID && D->Tok != TOK_NAME && + D->Tok != TOK_OUTPUTNAME && D->Tok != TOK_OUTPUTOFFS && + D->Tok != TOK_SIZE && D->Tok != TOK_START && + D->Tok != TOK_TYPE) { /* Try smart error recovery */ if (D->Tok == TOK_IDENT || TokenIsKeyword (D->Tok)) { @@ -3892,6 +3901,15 @@ static void ParseSegment (InputData* D) InfoBits |= ibAddrSize; break; + case TOK_BANK: + if (!IntConstFollows (D)) { + goto ErrorExit; + } + Bank = D->IVal; + InfoBits |= ibBank; + NextToken (D); + break; + case TOK_ID: if (!IntConstFollows (D)) { goto ErrorExit; @@ -3992,7 +4010,7 @@ static void ParseSegment (InputData* D) } /* Create the segment info and remember it */ - S = NewSegInfo (&Name, Id, Start, Size, &OutputName, OutputOffs); + S = NewSegInfo (&Name, Id, Start, Size, &OutputName, OutputOffs, Bank); CollReplaceExpand (&D->Info->SegInfoById, S, Id); CollAppend (&D->Info->SegInfoByName, S); diff --git a/src/dbginfo/dbginfo.h b/src/dbginfo/dbginfo.h index 95aae837c..07914f238 100644 --- a/src/dbginfo/dbginfo.h +++ b/src/dbginfo/dbginfo.h @@ -521,6 +521,7 @@ struct cc65_segmentdata { cc65_size segment_size; /* Size of segment */ const char* output_name; /* Output file this seg was written to */ unsigned long output_offs; /* Offset of this seg in output file */ + unsigned segment_bank; }; typedef struct cc65_segmentinfo cc65_segmentinfo; diff --git a/src/dbginfo/dbgsh.c b/src/dbginfo/dbgsh.c index ba5d83849..6a95db2af 100644 --- a/src/dbginfo/dbgsh.c +++ b/src/dbginfo/dbgsh.c @@ -721,7 +721,7 @@ static void PrintSegmentHeader (void) /* Output a header for a list of segments */ { /* Header */ - PrintLine (" id name address size output file offs"); + PrintLine (" id name address size output file offs bank"); PrintSeparator (); } @@ -741,6 +741,7 @@ static void PrintSegments (const cc65_segmentinfo* S) PrintSize (D->segment_size, 7); Print ("%-16s", D->output_name? D->output_name : ""); PrintSize (D->output_offs, 6); + PrintId (D->segment_bank, 8); NewLine (); } } diff --git a/src/grc65.vcxproj b/src/grc65.vcxproj index fbd44fa3e..742003899 100644 --- a/src/grc65.vcxproj +++ b/src/grc65.vcxproj @@ -5,10 +5,18 @@ <Configuration>Debug</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> <ProjectConfiguration Include="Release|Win32"> <Configuration>Release</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> </ItemGroup> <PropertyGroup Label="Globals"> <ProjectGuid>{E0FD0AB3-3BEE-496F-8108-A8E0F8933F39}</ProjectGuid> @@ -21,15 +29,22 @@ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <UseDebugLibraries>true</UseDebugLibraries> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <UseDebugLibraries>true</UseDebugLibraries> + </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> <UseDebugLibraries>false</UseDebugLibraries> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <UseDebugLibraries>false</UseDebugLibraries> + </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <ImportGroup Label="ExtensionSettings"> </ImportGroup> <PropertyGroup Label="UserMacros" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> </PropertyGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> @@ -40,6 +55,15 @@ <SubSystem>Console</SubSystem> </Link> </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <PreprocessorDefinitions>_CONSOLE;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <DisableSpecificWarnings>4267;%(DisableSpecificWarnings)</DisableSpecificWarnings> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + </Link> + </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ClCompile> <PreprocessorDefinitions>_CONSOLE;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> @@ -48,6 +72,15 @@ <SubSystem>Console</SubSystem> </Link> </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <PreprocessorDefinitions>_CONSOLE;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <DisableSpecificWarnings>4267;%(DisableSpecificWarnings)</DisableSpecificWarnings> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + </Link> + </ItemDefinitionGroup> <ItemGroup> <ClCompile Include="grc65\main.c" /> </ItemGroup> diff --git a/src/grc65/main.c b/src/grc65/main.c index 7d31bfc52..5ef9e9645 100644 --- a/src/grc65/main.c +++ b/src/grc65/main.c @@ -231,9 +231,11 @@ static int findToken (const char * const *tokenTbl, const char *token) /* takes as input table of tokens and token, returns position in table or -1 if not found */ int i; - for (i = 0; tokenTbl[i][0]; i++) { - if (strcmp (tokenTbl[i], token) == 0) { - return i; + if (token != NULL) { + for (i = 0; tokenTbl[i][0]; i++) { + if (strcmp (tokenTbl[i], token) == 0) { + return i; + } } } diff --git a/src/ld65.vcxproj b/src/ld65.vcxproj index 9e4b08621..e2f06b198 100644 --- a/src/ld65.vcxproj +++ b/src/ld65.vcxproj @@ -5,10 +5,18 @@ <Configuration>Debug</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> <ProjectConfiguration Include="Release|Win32"> <Configuration>Release</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> </ItemGroup> <PropertyGroup Label="Globals"> <ProjectGuid>{26C749A0-814C-47A2-9D36-AE92AE932FE4}</ProjectGuid> @@ -21,15 +29,22 @@ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <UseDebugLibraries>true</UseDebugLibraries> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <UseDebugLibraries>true</UseDebugLibraries> + </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> <UseDebugLibraries>false</UseDebugLibraries> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <UseDebugLibraries>false</UseDebugLibraries> + </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <ImportGroup Label="ExtensionSettings"> </ImportGroup> <PropertyGroup Label="UserMacros" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> </PropertyGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> @@ -38,6 +53,19 @@ </ClCompile> <Link> <SubSystem>Console</SubSystem> + <MapFileName>ld65.map</MapFileName> + <MapExports>true</MapExports> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <PreprocessorDefinitions>_CONSOLE;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <DisableSpecificWarnings>4244;4267;%(DisableSpecificWarnings)</DisableSpecificWarnings> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <MapFileName>ld65.map</MapFileName> + <MapExports>true</MapExports> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> @@ -48,6 +76,15 @@ <SubSystem>Console</SubSystem> </Link> </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <PreprocessorDefinitions>_CONSOLE;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <DisableSpecificWarnings>4244;4267;%(DisableSpecificWarnings)</DisableSpecificWarnings> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + </Link> + </ItemDefinitionGroup> <ItemGroup> <ClInclude Include="ld65\asserts.h" /> <ClInclude Include="ld65\bin.h" /> @@ -118,4 +155,4 @@ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <ImportGroup Label="ExtensionTargets"> </ImportGroup> -</Project> +</Project> \ No newline at end of file diff --git a/src/ld65/config.c b/src/ld65/config.c index 6c1f6ad4c..947302e98 100644 --- a/src/ld65/config.c +++ b/src/ld65/config.c @@ -889,6 +889,7 @@ static void ParseO65 (void) CfgOptionalAssign (); /* Check which attribute was given */ + CfgSymbol* Sym; switch (AttrTok) { case CFGTOK_EXPORT: @@ -896,8 +897,11 @@ static void ParseO65 (void) AttrFlags |= atExport; /* We expect an identifier */ CfgAssureIdent (); - /* Remember it as an export for later */ - NewCfgSymbol (CfgSymO65Export, GetStrBufId (&CfgSVal)); + /* Remember it as an export for later. We do not support o65 + * output for the 65816, so the address size is always 16 bit. + */ + Sym = NewCfgSymbol (CfgSymO65Export, GetStrBufId (&CfgSVal)); + Sym->AddrSize = ADDR_SIZE_ABS; /* Eat the identifier token */ CfgNextTok (); break; @@ -907,8 +911,11 @@ static void ParseO65 (void) AttrFlags |= atImport; /* We expect an identifier */ CfgAssureIdent (); - /* Remember it as an import for later */ - NewCfgSymbol (CfgSymO65Import, GetStrBufId (&CfgSVal)); + /* Remember it as an import for later. We do not support o65 + * output for the 65816, so the address size is always 16 bit. + */ + Sym = NewCfgSymbol (CfgSymO65Import, GetStrBufId (&CfgSVal)); + Sym->AddrSize = ADDR_SIZE_ABS; /* Eat the identifier token */ CfgNextTok (); break; diff --git a/src/ld65/exports.c b/src/ld65/exports.c index 9149f54d1..9fa0e4019 100644 --- a/src/ld65/exports.c +++ b/src/ld65/exports.c @@ -808,6 +808,15 @@ static int CmpExpName (const void* K1, const void* K2) +static int CmpExpValue (const void* K1, const void* K2) +/* Compare function for qsort */ +{ + long Diff = GetExportVal (*(Export**)K1) - GetExportVal (*(Export**)K2); + return Diff < 0? -1 : Diff > 0? 1 : 0; +} + + + static void CreateExportPool (void) /* Create an array with pointer to all exports */ { @@ -880,19 +889,25 @@ static char GetAddrSizeCode (unsigned char AddrSize) -void PrintExportMapByName (FILE* F) -/* Print an export map, sorted by symbol name, to the given file */ +static void PrintExportMap (Export** Pool, unsigned Count, FILE* F) +/* Print an export map to the given file */ { unsigned I; - unsigned Count; /* Print all exports */ - Count = 0; - for (I = 0; I < ExpCount; ++I) { - const Export* E = ExpPool [I]; + unsigned Col = 0; + for (I = 0; I < Count; ++I) { + const Export* E = Pool [I]; - /* Print unreferenced symbols only if explictly requested */ - if (VerboseMap || E->ImpCount > 0 || SYM_IS_CONDES (E->Type)) { + /* Print unreferenced symbols only if explictly requested. If Expr is + ** NULL, the export is undefined. This happens for imports that don't + ** have a matching export, but if we have one of those, we don't come + ** here. It does also happen for imports that where satisfied from + ** elsewhere, like o65 imports defined in the linker config. + ** So ignore exports here that have an invalid Expr. + */ + if (E->Expr != 0 && + (VerboseMap || E->ImpCount > 0 || SYM_IS_CONDES (E->Type))) { fprintf (F, "%-25s %06lX %c%c%c%c ", GetString (E->Name), @@ -901,8 +916,8 @@ void PrintExportMapByName (FILE* F) SYM_IS_LABEL (E->Type)? 'L' : 'E', GetAddrSizeCode ((unsigned char) E->AddrSize), SYM_IS_CONDES (E->Type)? 'I' : ' '); - if (++Count == 2) { - Count = 0; + if (++Col == 2) { + Col = 0; fprintf (F, "\n"); } } @@ -912,13 +927,10 @@ void PrintExportMapByName (FILE* F) -static int CmpExpValue (const void* I1, const void* I2) -/* Compare function for qsort */ +void PrintExportMapByName (FILE* F) +/* Print an export map, sorted by symbol name, to the given file */ { - long V1 = GetExportVal (ExpPool [*(unsigned *)I1]); - long V2 = GetExportVal (ExpPool [*(unsigned *)I2]); - - return V1 < V2 ? -1 : V1 == V2 ? 0 : 1; + PrintExportMap (ExpPool, ExpCount, F); } @@ -926,43 +938,16 @@ static int CmpExpValue (const void* I1, const void* I2) void PrintExportMapByValue (FILE* F) /* Print an export map, sorted by symbol value, to the given file */ { - unsigned I; - unsigned Count; - unsigned *ExpValXlat; + /* Create a new pool that is sorted by value */ + Export** Pool = xmalloc (ExpCount * sizeof (Export*)); + memcpy (Pool, ExpPool, ExpCount * sizeof (Export*)); + qsort (Pool, ExpCount, sizeof (Export*), CmpExpValue); - /* Create a translation table where the symbols are sorted by value. */ - ExpValXlat = xmalloc (ExpCount * sizeof (unsigned)); - for (I = 0; I < ExpCount; ++I) { - /* Initialize table with current sort order. */ - ExpValXlat [I] = I; - } + /* Print the exports */ + PrintExportMap (Pool, ExpCount, F); - /* Sort them by value */ - qsort (ExpValXlat, ExpCount, sizeof (unsigned), CmpExpValue); - - /* Print all exports */ - Count = 0; - for (I = 0; I < ExpCount; ++I) { - const Export* E = ExpPool [ExpValXlat [I]]; - - /* Print unreferenced symbols only if explictly requested */ - if (VerboseMap || E->ImpCount > 0 || SYM_IS_CONDES (E->Type)) { - fprintf (F, - "%-25s %06lX %c%c%c%c ", - GetString (E->Name), - GetExportVal (E), - E->ImpCount? 'R' : ' ', - SYM_IS_LABEL (E->Type)? 'L' : 'E', - GetAddrSizeCode ((unsigned char) E->AddrSize), - SYM_IS_CONDES (E->Type)? 'I' : ' '); - if (++Count == 2) { - Count = 0; - fprintf (F, "\n"); - } - } - } - fprintf (F, "\n"); - xfree (ExpValXlat); + /* Free the allocated buffer */ + xfree (Pool); } diff --git a/src/ld65/main.c b/src/ld65/main.c index 70e9c84d1..5632f4961 100644 --- a/src/ld65/main.c +++ b/src/ld65/main.c @@ -189,7 +189,7 @@ static void LinkFile (const char* Name, FILETYPE Type) /* If we don't know the file type, determine it from the extension */ if (Type == FILETYPE_UNKNOWN) { - Type = GetFileType (Name); + Type = GetTypeOfFile (Name); } /* For known file types, search the file in the directory list */ diff --git a/src/ld65/segments.c b/src/ld65/segments.c index 255b8ccd1..35d178774 100644 --- a/src/ld65/segments.c +++ b/src/ld65/segments.c @@ -638,6 +638,13 @@ void PrintDbgSegments (FILE* F) fprintf (F, ",oname=\"%s\",ooffs=%lu", S->OutputName, S->OutputOffs); } + if (S->MemArea) { + if (S->MemArea->BankExpr) { + if (IsConstExpr (S->MemArea->BankExpr)) { + fprintf (F, ",bank=%lu", GetExprVal(S->MemArea->BankExpr)); + } + } + } fputc ('\n', F); } } diff --git a/src/msbuild.cmd b/src/msbuild.cmd index 2e1821f0a..092bfb578 100644 --- a/src/msbuild.cmd +++ b/src/msbuild.cmd @@ -1,18 +1,23 @@ @echo off +setlocal -if exist "C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\Common7\Tools\VsDevCmd.bat" goto vs2017 -if exist "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\Tools\VsDevCmd.bat" goto vs2019 +where msbuild.exe 1>nul 2>&1 && goto :ready -echo Error: VsDevCmd.bat not found! -goto:eof +set VSWHERE_PATH=%ProgramFiles%\Microsoft Visual Studio\Installer\vswhere.exe +if not exist "%VSWHERE_PATH%" set VSWHERE_PATH=%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe +if not exist "%VSWHERE_PATH%" goto :error +for /f "usebackq delims=#" %%a in (`"%VSWHERE_PATH%" -latest -property installationPath`) do set VSDEVCMD_PATH=%%a\Common7\Tools\VsDevCmd.bat +if not exist "%VSDEVCMD_PATH%" goto :error +set VSCMD_SKIP_SENDTELEMETRY=1 +call "%VSDEVCMD_PATH%" -no_logo -startdir=none -:vs2017 -call "C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\Common7\Tools\VsDevCmd.bat" -goto run +where msbuild.exe 1>nul 2>&1 && goto :ready -:vs2019 -call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\Tools\VsDevCmd.bat" -goto run +:error + +echo Error: Can't find MSBuild. +exit /b 1 + +:ready -:run msbuild.exe %* diff --git a/src/od65.vcxproj b/src/od65.vcxproj index 1a1527067..409b5038e 100644 --- a/src/od65.vcxproj +++ b/src/od65.vcxproj @@ -5,10 +5,18 @@ <Configuration>Debug</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> <ProjectConfiguration Include="Release|Win32"> <Configuration>Release</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> </ItemGroup> <PropertyGroup Label="Globals"> <ProjectGuid>{FF8576C2-1253-44FE-A51B-D9AE35F3CEAD}</ProjectGuid> @@ -21,15 +29,22 @@ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <UseDebugLibraries>true</UseDebugLibraries> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <UseDebugLibraries>true</UseDebugLibraries> + </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> <UseDebugLibraries>false</UseDebugLibraries> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <UseDebugLibraries>false</UseDebugLibraries> + </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <ImportGroup Label="ExtensionSettings"> </ImportGroup> <PropertyGroup Label="UserMacros" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> </PropertyGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> @@ -40,6 +55,15 @@ <SubSystem>Console</SubSystem> </Link> </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <PreprocessorDefinitions>_CONSOLE;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <DisableSpecificWarnings>4244;4267;%(DisableSpecificWarnings)</DisableSpecificWarnings> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + </Link> + </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ClCompile> <PreprocessorDefinitions>_CONSOLE;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> @@ -48,6 +72,15 @@ <SubSystem>Console</SubSystem> </Link> </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <PreprocessorDefinitions>_CONSOLE;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <DisableSpecificWarnings>4244;4267;%(DisableSpecificWarnings)</DisableSpecificWarnings> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + </Link> + </ItemDefinitionGroup> <ItemGroup> <ClCompile Include="od65\dump.c" /> <ClCompile Include="od65\error.c" /> diff --git a/src/sim65.vcxproj b/src/sim65.vcxproj index 97fc3855a..f1fe6bdd2 100644 --- a/src/sim65.vcxproj +++ b/src/sim65.vcxproj @@ -5,10 +5,18 @@ <Configuration>Debug</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> <ProjectConfiguration Include="Release|Win32"> <Configuration>Release</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> </ItemGroup> <PropertyGroup Label="Globals"> <ProjectGuid>{002A366E-2863-46A8-BDDE-DDF534AAEC73}</ProjectGuid> @@ -21,15 +29,22 @@ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <UseDebugLibraries>true</UseDebugLibraries> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <UseDebugLibraries>true</UseDebugLibraries> + </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> <UseDebugLibraries>false</UseDebugLibraries> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <UseDebugLibraries>false</UseDebugLibraries> + </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <ImportGroup Label="ExtensionSettings"> </ImportGroup> <PropertyGroup Label="UserMacros" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> </PropertyGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> @@ -40,6 +55,15 @@ <SubSystem>Console</SubSystem> </Link> </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <PreprocessorDefinitions>_CONSOLE;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <DisableSpecificWarnings>4267;%(DisableSpecificWarnings)</DisableSpecificWarnings> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + </Link> + </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ClCompile> <PreprocessorDefinitions>_CONSOLE;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> @@ -48,11 +72,22 @@ <SubSystem>Console</SubSystem> </Link> </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <PreprocessorDefinitions>_CONSOLE;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <DisableSpecificWarnings>4267;%(DisableSpecificWarnings)</DisableSpecificWarnings> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + </Link> + </ItemDefinitionGroup> <ItemGroup> <ClInclude Include="sim65\6502.h" /> <ClInclude Include="sim65\error.h" /> <ClInclude Include="sim65\memory.h" /> <ClInclude Include="sim65\paravirt.h" /> + <ClInclude Include="sim65\peripherals.h" /> + <ClInclude Include="sim65\trace.h" /> </ItemGroup> <ItemGroup> <ClCompile Include="sim65\6502.c" /> @@ -60,8 +95,10 @@ <ClCompile Include="sim65\main.c" /> <ClCompile Include="sim65\memory.c" /> <ClCompile Include="sim65\paravirt.c" /> + <ClCompile Include="sim65\peripherals.c" /> + <ClCompile Include="sim65\trace.c" /> </ItemGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <ImportGroup Label="ExtensionTargets"> </ImportGroup> -</Project> \ No newline at end of file +</Project> diff --git a/src/sim65/6502.c b/src/sim65/6502.c index 9d2c93da8..a9aa299ff 100644 --- a/src/sim65/6502.c +++ b/src/sim65/6502.c @@ -11,7 +11,13 @@ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ -/* Mar-2017, Christian Krueger, added support for 65SC02 */ +/* Mar-2017, Christian Krueger, added support for 65SC02. */ +/* Dec-2023, Carlo Bramini, rewritten for better maintenance and added */ +/* support for undocumented opcodes for 6502. */ +/* Dec-2024, Sidney Cadot, fixed 65C02 ADC/SBC (decimal mode); implemented */ +/* 65C02 missing instructions; fixed 6502X illegal instructions; */ +/* fixed cycle count for all 6502(X) / 65C02 opcodes; */ +/* achieved full 65x02 test-suite compliance. */ /* */ /* This software is provided 'as-is', without any expressed or implied */ /* warranty. In no event will the authors be held liable for any damages */ @@ -33,18 +39,350 @@ /*****************************************************************************/ /* Known bugs and limitations of the 65C02 simulation: - * support currently only on the level of 65SC02: - BBRx, BBSx, RMBx, SMBx, WAI, and STP are unsupported - * BCD flag handling equals 6502 (unchecked if bug is simulated or wrong for - 6502) -*/ + * the WAI ($CB) and STP ($DB) instructions are unsupported. + */ + +#include <stdint.h> +#include <stdbool.h> #include "memory.h" +#include "peripherals.h" #include "error.h" -#include "6502.h" #include "paravirt.h" +#include "trace.h" + +#include "6502.h" + +/* + + 6502 opcode map: + + x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE xF +0x BRK ORA --- SLO NOP ORA ASL SLO PHP ORA ASL ANC NOP ORA ASL SLO + inx inx zp zp zp zp imm acc imm abs abs abs abs + +1x BPL ORA --- SLO NOP ORA ASL SLO CLC ORA NOP SLO NOP ORA ASL SLO + rel iny iny zpx zpx zpx zpy aby aby abx abx abx abx + +2x JSR AND --- RLA BIT AND ROL RLA PLP AND ROL ANC BIT AND ROL RLA + abs inx inx zp zp zp zp imm acc imm abs abs abs abs + +3x BMI AND --- RLA NOP AND ROL RLA SEC AND NOP RLA NOP AND ROL RLA + rel iny iny zpx zpx zpx zpy aby aby abx abx abx abx + +4x RTI EOR --- SRE NOP EOR LSR SRE PHA EOR LSR ASR JMP EOR LSR SRE + inx inx zp zp zp zp imm acc imm abs abs abs abs + +5x BVC EOR --- SRE NOP EOR LSR SRE CLI EOR NOP SRE NOP EOR LSR SRE + rel iny iny zpx zpx zpx zpx aby aby abx abx abx abx + +6x RTS ADC --- RRA NOP ADC ROR RRA PLA ADC ROR ARR JMP ADC ROR RRA + inx inx zp zp zp zp imm acc imm ind abs abs abs + +7x BVS ADC --- RRA NOP ADC ROR RRA SEI ADC NOP RRA NOP ADC ROR RRA + rel iny iny zpx zpx zpx zpx aby aby abx abx abx abx + +8x NOP STA NOP SAX STY STA STX SAX DEY NOP TXA ANE STY STA STX SAX + imm inx imm inx zp zp zp zp imm imm abs abs abs abs + +9x BCC STA --- SHA STY STA STX SAX TYA STA TXS TAS SHY STA SHX SHA + rel iny iny zpx zpx zpy zpy aby aby abx abx aby aby + +Ax LDY LDA LDX LAX LDY LDA LDX LAX TAY LDA TAX LXA LDY LDA LDX LAX + imm inx imm inx zp zp zp zp imm imm abs abs abs abs + +Bx BCS LDA --- LAX LDY LDA LDX LAX CLV LDA TSX LAS LDY LDA LDX LAX + rel iny iny zpx zpx zpy zpy aby aby abx abx aby aby + +Cx CPY CMP NOP DCP CPY CMP DEC DCP INY CMP DEX SBX CPY CMP DEC DCP + imm inx imm inx zp zp zp zp imm imm abs abs abs abs + +Dx BNE CMP --- DCP NOP CMP DEC DCP CLD CMP NOP DCP NOP CMP DEC DCP + rel iny iny zpx zpx zpx zpx aby zpx aby abx abx abx abx + +Ex CPX SBC NOP ISC CPX SBC INC ISC INX SBC NOP SBC CPX SBC INC ISC + imm inx imm inx zp zp zp zp imm imm abs abs abs abs + +Fx BEQ SBC --- ISC NOP SBC INC ISC SED SBC NOP ISC NOP SBC INC ISC + rel iny iny zpx zpx zpx zpx aby zpx aby abx abx abx abx + +--- = CPU JAM/HALT + +*/ + +/* + +65xx ILLEGAL INSTRUCTIONS +* SLO: shift left the contents of a memory location and then OR the result with + the accumulator. + +Address mode | opcode | cycles | N V B D I Z C +-------------+--------+--------+ FLAGS: X . . . . X X +SLO abs | 0Fh | 6 | +SLO abs,X | 1Fh | 7 | +SLO abs,Y | 1Bh | 7 | +SLO zp | 07h | 5 | +SLO zp,X | 17h | 6 | +SLO (zp,X) | 03h | 8 | +SLO (zp),Y | 13h | 8 | +-------------+--------+--------+ + + +* RLA: rotate left the contents of a memory location and then AND the result with + the accumulator. + +Address mode | opcode | cycles | N V B D I Z C +-------------+--------+--------+ FLAGS: X . . . . X X +RLA abs | 2Fh | 6 | +RLA abs,X | 3Fh | 7 | +RLA abs,Y | 3Bh | 7 | +RLA zp | 27h | 5 | +RLA zp,X | 37h | 6 | +RLA (zp,X) | 23h | 8 | +RLA (zp),Y | 33h | 8 | +-------------+--------+--------+ + + +* SRE: shift right the contents of a memory location and then X-OR the result + with the accumulator. + +Address mode | opcode | cycles | N V B D I Z C +-------------+--------+--------+ FLAGS: X . . . . X X +SRE abs | 4Fh | 6 | +SRE abs,X | 5Fh | 7 | +SRE abs,Y | 5Bh | 7 | +SRE zp | 47h | 5 | +SRE zp,X | 57h | 6 | +SRE (zp,X) | 43h | 8 | +SRE (zp),Y | 53h | 8 | +-------------+--------+--------+ + + +* RRA: rotate right the contents of a memory location and then adds with carry + the result with the accumulator. + +Address mode | opcode | cycles | N V B D I Z C +-------------+--------+--------+ FLAGS: X X . . . X X +RRA abs | 6Fh | 6 | +RRA abs,X | 7Fh | 7 | +RRA abs,Y | 7Bh | 7 | +RRA zp | 67h | 5 | +RRA zp,X | 77h | 6 | +RRA (zp,X) | 63h | 8 | +RRA (zp),Y | 73h | 8 | +-------------+--------+--------+ + + +* SAX: calculate AND between the A and X registers (without changing the + contents of the registers) and stores the result in memory. + Flags into P register are not modified. + +Address mode | opcode | cycles | N V B D I Z C +-------------+--------+--------+ FLAGS: . . . . . . . +SAX abs | 8Fh | 4 | +SAX zp | 87h | 3 | +SAX zp,Y | 97h | 4 | +SAX (zp,X) | 83h | 6 | +-------------+--------+--------+ + + +* LAX: loads both the accumulator and the X register with the content of a memory + location. + +Address mode | opcode | cycles | N V B D I Z C +-------------+--------+--------+ FLAGS: X . . . . X . +LAX abs | AFh | 4 | +LAX abs,Y | BFh | 4* | * = adds +1 if page cross is detected. +LAX zp | A7h | 3 | +LAX zp,Y | B7h | 4 | +LAX (zp,X) | A3h | 6 | +LAX (zp),Y | B3h | 5* | +-------------+--------+--------+ + + +* DCP: decrements the contents of a memory location and then compares the result + with the accumulator. + +Address mode | opcode | cycles | N V B D I Z C +-------------+--------+--------+ FLAGS: X . . . . X X +DCP abs | CFh | 6 | +DCP abs,X | DFh | 7 | +DCP abs,Y | DBh | 7 | +DCP zp | C7h | 5 | +DCP zp,X | D7h | 6 | +DCP (zp,X) | C3h | 8 | +DCP (zp),Y | D3h | 8 | +-------------+--------+--------+ + + +* ISC: increments the contents of a memory location and then subtract with carry + the result from the accumulator. + +Address mode | opcode | cycles | N V B D I Z C +-------------+--------+--------+ FLAGS: X X . . . X X +ISC abs | EFh | 6 | +ISC abs,X | FFh | 7 | +ISC abs,Y | FBh | 7 | +ISC zp | E7h | 5 | +ISC zp,X | F7h | 6 | +ISC (zp,X) | E3h | 8 | +ISC (zp),Y | F3h | 8 | +-------------+--------+--------+ + + +* ASR: calculates the AND between the accumulator and an immediate value and then + shift right the result. + +Address mode | opcode | cycles | N V B D I Z C +-------------+--------+--------+ FLAGS: X . . . . X X +ASR #imm | 4Bh | 2 | +-------------+--------+--------+ + + +* ARR: calculates the AND between the accumulator and an immediate value and then + rotate right the result. + +Address mode | opcode | cycles | N V B D I Z C +-------------+--------+--------+ FLAGS: X . . . . X X +ARR #imm | 6Bh | 2 | +-------------+--------+--------+ + + +* ANE: calculates the OR of the accumulator with an unstable constant, then it does + an AND with the X register and an immediate value. + The unstable constant varies with temperature, the production batch and + maybe other factors. Experimental measures assume its value to 0xEF. + +Address mode | opcode | cycles | N V B D I Z C +-------------+--------+--------+ FLAGS: X . . . . X . +ANE #imm | 8Bh | 2 | +-------------+--------+--------+ + + +* LXA: calculates the OR of the accumulator with an unstable constant, then it does + an AND with an immediate value. The result is copied into the X register and + the accumulator. + The unstable constant varies with temperature, the production batch and + maybe other factors. Experimental measures assume its value to 0xEE. + +Address mode | opcode | cycles | N V B D I Z C +-------------+--------+--------+ FLAGS: X . . . . X . +LXA #imm | ABh | 2 | +-------------+--------+--------+ + + +* SBX: calculates the AND of the accumulator with the X register and the subtracts + an immediate value. + +Address mode | opcode | cycles | N V B D I Z C +-------------+--------+--------+ FLAGS: X . . . . X X +SBX #imm | CBh | 2 | +-------------+--------+--------+ + + +* NOP: No-Operation. + +Address mode | opcode | cycles | N V B D I Z C +-------------+--------+--------+ FLAGS: . . . . . . . +NOP | 1Ah | 2 | +NOP | 3Ah | 2 | * = adds +1 if page cross is detected. +NOP | 5Ah | 2 | +NOP | 7Ah | 2 | +NOP | DAh | 2 | +NOP | FAh | 2 | +NOP #imm | 80h | 2 | +NOP #imm | 82h | 2 | +NOP #imm | 89h | 2 | +NOP #imm | C2h | 2 | +NOP #imm | E2h | 2 | +NOP zp | 04h | 3 | +NOP zp,x | 14h | 4 | +NOP zp,x | 34h | 4 | +NOP zp | 44h | 3 | +NOP zp,x | 54h | 4 | +NOP zp | 64h | 3 | +NOP zp,x | 74h | 4 | +NOP zp,x | D4h | 4 | +NOP zp,x | F4h | 4 | +NOP abs | 0Ch | 4 | +NOP abs,x | 1Ch | 4* | +NOP abs,x | 3Ch | 4* | +NOP abs,x | 5Ch | 4* | +NOP abs,x | 7Ch | 4* | +NOP abs,x | DCh | 4* | +NOP abs,x | FCh | 4* | +-------------+--------+--------+ + + +* TAS: calculates the AND of the accumulator with the X register and stores the result + into the stack pointer. Then, it calculates the AND of the result with the + high byte of the memory pointer plus 1 and it stores the final result in memory. + +Address mode | opcode | cycles | N V B D I Z C +-------------+--------+--------+ FLAGS: . . . . . . . +TAS abs,y | 9Bh | 5 | +-------------+--------+--------+ + + +* SHY: calculates the AND of the Y register with the high byte of the memory pointer + plus 1 and it stores the final result in memory. + +Address mode | opcode | cycles | N V B D I Z C +-------------+--------+--------+ FLAGS: . . . . . . . +SHY abs,x | 9Ch | 5 | +-------------+--------+--------+ + + +* SHX: calculates the AND of the X register with the high byte of the memory pointer + plus 1 and it stores the final result in memory. + +Address mode | opcode | cycles | N V B D I Z C +-------------+--------+--------+ FLAGS: . . . . . . . +SHX abs,y | 9Eh | 5 | +-------------+--------+--------+ + + +* SHA: calculates the AND of the accumulator with the X register with the high byte + of the memory pointer plus 1 and it stores the final result in memory. + +Address mode | opcode | cycles | N V B D I Z C +-------------+--------+--------+ FLAGS: . . . . . . . +SHX abs,y | 9Fh | 5 | +SHX (zp),y | 93h | 6 | +-------------+--------+--------+ + + +* ANC: calculates the AND of the accumulator with an immediate value and then + updates the status of N and Z bits of the status register. + The N flag is also copied into the Carry flag. + +Address mode | opcode | cycles | N V B D I Z C +-------------+--------+--------+ FLAGS: X . . . . X X +ANC #imm | 0Bh | 2 | +ANC #imm | 2Bh | 2 | +-------------+--------+--------+ + + +* LAS: calculates the AND of a memory location with the contents of the +stack pointer register and it stores the result in the accumulator, the X +register, and the stack pointer. + +Address mode | opcode | cycles | N V B D I Z C +-------------+--------+--------+ FLAGS: X . . . . X . +LAS abs,y | BBh | 4* | +-------------+--------+--------+ * = adds +1 if page cross is detected. + + +* SBC: alias of the official SBC opcode. + +Address mode | opcode | cycles | N V B D I Z C +-------------+--------+--------+ FLAGS: X X . . . X X +SBC #imm | EBh | 2 | +-------------+--------+--------+ + +*/ /*****************************************************************************/ /* Data */ @@ -59,16 +397,17 @@ CPUType CPU; typedef void (*OPFunc) (void); /* The CPU registers */ -static CPURegs Regs; +CPURegs Regs; /* Cycles for the current insn */ static unsigned Cycles; /* NMI request active */ -static unsigned HaveNMIRequest; +static bool HaveNMIRequest; /* IRQ request active */ -static unsigned HaveIRQRequest; +static bool HaveIRQRequest; + /*****************************************************************************/ @@ -100,7 +439,6 @@ static unsigned HaveIRQRequest; */ #define TEST_ZF(v) SET_ZF (((v) & 0xFF) == 0) #define TEST_SF(v) SET_SF (((v) & 0x80) != 0) -#define TEST_CF(v) SET_CF (((v) & 0xFF00) != 0) /* Program counter halves */ #define PCL (Regs.PC & 0xFF) @@ -113,238 +451,711 @@ static unsigned HaveIRQRequest; /* Test for page cross */ #define PAGE_CROSS(addr,offs) ((((addr) & 0xFF) + offs) >= 0x100) -/* #imm */ -#define AC_OP_IMM(op) \ - Cycles = 2; \ - Regs.AC = Regs.AC op MemReadByte (Regs.PC+1); \ - TEST_ZF (Regs.AC); \ - TEST_SF (Regs.AC); \ - Regs.PC += 2 +/* Address operators */ /* zp */ -#define AC_OP_ZP(op) \ - Cycles = 3; \ - Regs.AC = Regs.AC op MemReadByte (MemReadByte (Regs.PC+1)); \ - TEST_ZF (Regs.AC); \ - TEST_SF (Regs.AC); \ +#define ADR_ZP(ad) \ + ad = MemReadByte (Regs.PC+1); \ Regs.PC += 2 /* zp,x */ -#define AC_OP_ZPX(op) \ - unsigned char ZPAddr; \ - Cycles = 4; \ - ZPAddr = MemReadByte (Regs.PC+1) + Regs.XR; \ - Regs.AC = Regs.AC op MemReadByte (ZPAddr); \ - TEST_ZF (Regs.AC); \ - TEST_SF (Regs.AC); \ +#define ADR_ZPX(ad) \ + ad = (MemReadByte (Regs.PC+1) + Regs.XR) & 0xFF; \ Regs.PC += 2 /* zp,y */ -#define AC_OP_ZPY(op) \ - unsigned char ZPAddr; \ - Cycles = 4; \ - ZPAddr = MemReadByte (Regs.PC+1) + Regs.YR; \ - Regs.AC = Regs.AC op MemReadByte (ZPAddr); \ - TEST_ZF (Regs.AC); \ - TEST_SF (Regs.AC); \ +#define ADR_ZPY(ad) \ + ad = (MemReadByte (Regs.PC+1) + Regs.YR) & 0xFF; \ Regs.PC += 2 /* abs */ -#define AC_OP_ABS(op) \ - unsigned Addr; \ - Cycles = 4; \ - Addr = MemReadWord (Regs.PC+1); \ - Regs.AC = Regs.AC op MemReadByte (Addr); \ - TEST_ZF (Regs.AC); \ - TEST_SF (Regs.AC); \ +#define ADR_ABS(ad) \ + ad = MemReadWord (Regs.PC+1); \ Regs.PC += 3 /* abs,x */ -#define AC_OP_ABSX(op) \ - unsigned Addr; \ - Cycles = 4; \ - Addr = MemReadWord (Regs.PC+1); \ - if (PAGE_CROSS (Addr, Regs.XR)) { \ +#define ADR_ABSX(ad) \ + ad = MemReadWord (Regs.PC+1); \ + if (PAGE_CROSS (ad, Regs.XR)) { \ ++Cycles; \ } \ - Regs.AC = Regs.AC op MemReadByte (Addr + Regs.XR); \ - TEST_ZF (Regs.AC); \ - TEST_SF (Regs.AC); \ + ad += Regs.XR; \ Regs.PC += 3 /* abs,y */ -#define AC_OP_ABSY(op) \ - unsigned Addr; \ - Cycles = 4; \ - Addr = MemReadWord (Regs.PC+1); \ - if (PAGE_CROSS (Addr, Regs.YR)) { \ +#define ADR_ABSY(ad) \ + ad = MemReadWord (Regs.PC+1); \ + if (PAGE_CROSS (ad, Regs.YR)) { \ ++Cycles; \ } \ - Regs.AC = Regs.AC op MemReadByte (Addr + Regs.YR); \ - TEST_ZF (Regs.AC); \ - TEST_SF (Regs.AC); \ + ad += Regs.YR; \ Regs.PC += 3 /* (zp,x) */ -#define AC_OP_ZPXIND(op) \ - unsigned char ZPAddr; \ - unsigned Addr; \ - Cycles = 6; \ - ZPAddr = MemReadByte (Regs.PC+1) + Regs.XR; \ - Addr = MemReadZPWord (ZPAddr); \ - Regs.AC = Regs.AC op MemReadByte (Addr); \ - TEST_ZF (Regs.AC); \ - TEST_SF (Regs.AC); \ +#define ADR_ZPXIND(ad) \ + ad = (MemReadByte (Regs.PC+1) + Regs.XR) & 0xFF; \ + ad = MemReadZPWord (ad); \ Regs.PC += 2 /* (zp),y */ -#define AC_OP_ZPINDY(op) \ - unsigned char ZPAddr; \ - unsigned Addr; \ - Cycles = 5; \ - ZPAddr = MemReadByte (Regs.PC+1); \ - Addr = MemReadZPWord (ZPAddr); \ - if (PAGE_CROSS (Addr, Regs.YR)) { \ +#define ADR_ZPINDY(ad) \ + ad = MemReadZPWord (MemReadByte (Regs.PC+1)); \ + if (PAGE_CROSS (ad, Regs.YR)) { \ ++Cycles; \ } \ - Addr += Regs.YR; \ - Regs.AC = Regs.AC op MemReadByte (Addr); \ - TEST_ZF (Regs.AC); \ - TEST_SF (Regs.AC); \ + ad += Regs.YR; \ Regs.PC += 2 /* (zp) */ -#define AC_OP_ZPIND(op) \ - unsigned char ZPAddr; \ - unsigned Addr; \ - Cycles = 5; \ - ZPAddr = MemReadByte (Regs.PC+1); \ - Addr = MemReadZPWord (ZPAddr); \ - Regs.AC = Regs.AC op MemReadByte (Addr); \ - TEST_ZF (Regs.AC); \ - TEST_SF (Regs.AC); \ +#define ADR_ZPIND(ad) \ + ad = MemReadZPWord (MemReadByte (Regs.PC+1)); \ Regs.PC += 2 -/* ADC */ -#define ADC(v) \ +/* Address operators (no penalty on page cross) */ + +/* abs,x - no penalty */ +#define ADR_ABSX_NP(ad) \ + ad = MemReadWord (Regs.PC+1); \ + ad += Regs.XR; \ + Regs.PC += 3 + +/* abs,y - no penalty */ +#define ADR_ABSY_NP(ad) \ + ad = MemReadWord (Regs.PC+1); \ + ad += Regs.YR; \ + Regs.PC += 3 + +/* (zp),y - no penalty */ +#define ADR_ZPINDY_NP(ad) \ + ad = MemReadZPWord (MemReadByte (Regs.PC+1)); \ + ad += Regs.YR; \ + Regs.PC += 2 + + + +/* Memory operators */ + +/* #imm */ +#define MEM_AD_OP_IMM(op) \ + op = MemReadByte (Regs.PC+1); \ + Regs.PC += 2 + +/* zp / zp,x / zp,y / abs / abs,x / abs,y / (zp,x) / (zp),y / (zp) */ +#define MEM_AD_OP(mode, ad, op) \ + ADR_##mode(ad); \ + op = MemReadByte (ad) + +/* ALU opcode helpers */ + +/* Execution cycles for ALU opcodes */ +#define ALU_CY_ZP 3 +#define ALU_CY_ZPX 4 +#define ALU_CY_ZPY 4 +#define ALU_CY_ABS 4 +#define ALU_CY_ABSX 4 +#define ALU_CY_ABSY 4 +#define ALU_CY_ZPXIND 6 +#define ALU_CY_ZPINDY 5 +#define ALU_CY_ZPIND 5 + +/* #imm */ +#define ALU_OP_IMM(op) \ + uint8_t immediate; \ + MEM_AD_OP_IMM(immediate); \ + Cycles = 2; \ + op (immediate) + +/* zp / zp,x / zp,y / abs / abs,x / abs,y / (zp,x) / (zp),y / (zp) */ +#define ALU_OP(mode, op) \ + unsigned address, operand; \ + Cycles = ALU_CY_##mode; \ + MEM_AD_OP (mode, address, operand); \ + op (operand) + +/* Store opcode helpers */ + +/* Execution cycles for store opcodes */ +#define STO_CY_ZP 3 +#define STO_CY_ZPX 4 +#define STO_CY_ZPY 4 +#define STO_CY_ABS 4 +#define STO_CY_ABSX 5 +#define STO_CY_ABSY 5 +#define STO_CY_ZPXIND 6 +#define STO_CY_ZPINDY 6 +#define STO_CY_ZPIND 5 + +#define STO_CY_ABSX_NP STO_CY_ABSX +#define STO_CY_ABSY_NP STO_CY_ABSY +#define STO_CY_ZPINDY_NP STO_CY_ZPINDY + +/* zp / zp,x / zp,y / abs / abs,x / abs,y / (zp,x) / (zp),y / (zp) */ +#define STO_OP(mode, op) \ + unsigned address; \ + Cycles = STO_CY_##mode; \ + ADR_##mode (address); \ + MemWriteByte(address, op) + +/* Read-Modify-Write opcode helpers */ + +/* Execution cycles for R-M-W opcodes */ +#define RMW_CY_ZP 5 +#define RMW_CY_ZPX 6 +#define RMW_CY_ABS 6 +#define RMW_CY_ABSX 7 +#define RMW_CY_ABSY 7 +#define RMW_CY_ZPXIND 8 +#define RMW_CY_ZPINDY 8 + +#define RMW_CY_ABSX_NP RMW_CY_ABSX +#define RMW_CY_ABSY_NP RMW_CY_ABSY +#define RMW_CY_ZPINDY_NP RMW_CY_ZPINDY + +/* zp / zp,x / zp,y / abs / abs,x / abs,y / (zp,x) / (zp),y / (zp) */ +#define MEM_OP(mode, op) \ + unsigned address, operand; \ + Cycles = RMW_CY_##mode; \ + MEM_AD_OP (mode, address, operand); \ + op (operand); \ + MemWriteByte (address, (unsigned char)operand) + +/* 2 x Read-Modify-Write opcode helpers (illegal opcodes) */ + +/* Execution cycles for 2 x R-M-W opcodes */ +#define RMW2_CY_ZP 5 +#define RMW2_CY_ZPX 6 +#define RMW2_CY_ABS 6 +#define RMW2_CY_ABSX 7 +#define RMW2_CY_ABSY 7 +#define RMW2_CY_ZPXIND 8 +#define RMW2_CY_ZPINDY 8 + +#define RMW2_CY_ZPINDY_NP RMW2_CY_ZPINDY +#define RMW2_CY_ABSY_NP RMW2_CY_ABSY +#define RMW2_CY_ABSX_NP RMW2_CY_ABSX + +/* zp / zp,x / zp,y / abs / abs,x / abs,y / (zp,x) / (zp),y */ +#define ILLx2_OP(mode, op) \ + unsigned address; \ + unsigned operand; \ + Cycles = RMW2_CY_##mode; \ + MEM_AD_OP (mode, address, operand); \ + op (operand); \ + MemWriteByte (address, (unsigned char)operand) + +/* AC opcode helpers */ + +/* #imm */ +#define AC_OP_IMM(op) \ + unsigned char immediate; \ + MEM_AD_OP_IMM(immediate); \ + Cycles = 2; \ + Regs.AC = Regs.AC op immediate; \ + TEST_ZF (Regs.AC); \ + TEST_SF (Regs.AC) + +/* zp / zp,x / zp,y / abs / abs,x / abs,y / (zp,x) / (zp),y / (zp) */ +#define AC_OP(mode, op) \ + unsigned address; \ + unsigned operand; \ + Cycles = ALU_CY_##mode; \ + MEM_AD_OP(mode, address, operand); \ + Regs.AC = Regs.AC op operand; \ + TEST_ZF (Regs.AC); \ + TEST_SF (Regs.AC) + + +/* ADC, binary mode (6502 and 65C02) */ +#define ADC_BINARY_MODE(v) \ do { \ - unsigned old = Regs.AC; \ - unsigned rhs = (v & 0xFF); \ - if (GET_DF ()) { \ - unsigned lo; \ - int res; \ - lo = (old & 0x0F) + (rhs & 0x0F) + GET_CF (); \ - if (lo >= 0x0A) { \ - lo = ((lo + 0x06) & 0x0F) + 0x10; \ - } \ - Regs.AC = (old & 0xF0) + (rhs & 0xF0) + lo; \ - res = (signed char)(old & 0xF0) + \ - (signed char)(rhs & 0xF0) + \ - (signed char)lo; \ - TEST_ZF (old + rhs + GET_CF ()); \ - TEST_SF (Regs.AC); \ - if (Regs.AC >= 0xA0) { \ - Regs.AC += 0x60; \ - } \ - TEST_CF (Regs.AC); \ - SET_OF ((res < -128) || (res > 127)); \ - if (CPU != CPU_6502) { \ - ++Cycles; \ - } \ + const uint8_t op = v; \ + const uint8_t OldAC = Regs.AC; \ + bool carry = GET_CF(); \ + Regs.AC = OldAC + op + carry; \ + const bool NV = Regs.AC >= 0x80; \ + carry = OldAC + op + carry >= 0x100; \ + SET_SF(NV); \ + SET_OF(((OldAC >= 0x80) ^ NV) & ((op >= 0x80) ^ NV)); \ + SET_ZF(Regs.AC == 0); \ + SET_CF(carry); \ + } while (0) + +/* ADC, decimal mode (6502 behavior) */ +#define ADC_DECIMAL_MODE_6502(v) \ + do { \ + const uint8_t op = v; \ + const uint8_t OldAC = Regs.AC; \ + bool carry = GET_CF(); \ + const uint8_t binary_result = OldAC + op + carry; \ + uint8_t low_nibble = (OldAC & 15) + (op & 15) + carry; \ + if ((carry = low_nibble > 9)) \ + low_nibble = (low_nibble - 10) & 15; \ + uint8_t high_nibble = (OldAC >> 4) + (op >> 4) + carry; \ + const bool NV = (high_nibble & 8) != 0; \ + if ((carry = high_nibble > 9)) \ + high_nibble = (high_nibble - 10) & 15; \ + Regs.AC = (high_nibble << 4) | low_nibble; \ + SET_SF(NV); \ + SET_OF(((OldAC >= 0x80) ^ NV) & ((op >= 0x80) ^ NV)); \ + SET_ZF(binary_result == 0); \ + SET_CF(carry); \ + } while (0) + +/* ADC, decimal mode (65C02 behavior) */ +#define ADC_DECIMAL_MODE_65C02(v) \ + do { \ + const uint8_t op = v; \ + const uint8_t OldAC = Regs.AC; \ + const bool OldCF = GET_CF(); \ + bool carry = OldCF; \ + uint8_t low_nibble = (OldAC & 15) + (op & 15) + carry; \ + if ((carry = low_nibble > 9)) \ + low_nibble = (low_nibble - 10) & 15; \ + uint8_t high_nibble = (OldAC >> 4) + (op >> 4) + carry; \ + const bool PrematureSF = (high_nibble & 8) != 0; \ + if ((carry = high_nibble > 9)) \ + high_nibble = (high_nibble - 10) & 15; \ + Regs.AC = (high_nibble << 4) | low_nibble; \ + const bool NewZF = Regs.AC == 0; \ + const bool NewSF = Regs.AC >= 0x80; \ + const bool NewOF = ((OldAC >= 0x80) ^ PrematureSF) & \ + ((op >= 0x80) ^ PrematureSF); \ + SET_SF(NewSF); \ + SET_OF(NewOF); \ + SET_ZF(NewZF); \ + SET_CF(carry); \ + ++Cycles; \ + } while (0) + +/* ADC, 6502 version */ +#define ADC_6502(v) \ + do { \ + if (GET_DF()) { \ + ADC_DECIMAL_MODE_6502(v); \ } else { \ - Regs.AC += rhs + GET_CF (); \ - TEST_ZF (Regs.AC); \ - TEST_SF (Regs.AC); \ - TEST_CF (Regs.AC); \ - SET_OF (!((old ^ rhs) & 0x80) && \ - ((old ^ Regs.AC) & 0x80)); \ - Regs.AC &= 0xFF; \ + ADC_BINARY_MODE(v); \ + } \ + } while (0) + +/* ADC, 65C02 version */ +#define ADC_65C02(v) \ + do { \ + if (GET_DF()) { \ + ADC_DECIMAL_MODE_65C02(v); \ + } else { \ + ADC_BINARY_MODE(v); \ } \ } while (0) /* branches */ #define BRANCH(cond) \ - Cycles = 2; \ - if (cond) { \ - signed char Offs; \ - unsigned char OldPCH; \ - ++Cycles; \ - Offs = (signed char) MemReadByte (Regs.PC+1); \ - OldPCH = PCH; \ - Regs.PC += 2 + (int) Offs; \ - if (PCH != OldPCH) { \ + do { \ + Cycles = 2; \ + if (cond) { \ + int8_t Offs; \ + uint8_t OldPCH; \ ++Cycles; \ + Offs = MemReadByte (Regs.PC+1); \ + Regs.PC += 2; \ + OldPCH = PCH; \ + Regs.PC = (Regs.PC + (int) Offs) & 0xFFFF; \ + if (PCH != OldPCH) { \ + ++Cycles; \ + } \ + } else { \ + Regs.PC += 2; \ } \ - } else { \ - Regs.PC += 2; \ - } + } while (0) /* compares */ -#define CMP(v1, v2) \ +#define COMPARE(v1, v2) \ do { \ unsigned Result = v1 - v2; \ - TEST_ZF (Result & 0xFF); \ + TEST_ZF (Result); \ TEST_SF (Result); \ SET_CF (Result <= 0xFF); \ } while (0) +#define CPX(operand) \ + COMPARE (Regs.XR, operand) + +#define CPY(operand) \ + COMPARE (Regs.YR, operand) + +#define CMP(operand) \ + COMPARE (Regs.AC, operand) /* ROL */ #define ROL(Val) \ - Val <<= 1; \ - if (GET_CF ()) { \ - Val |= 0x01; \ - } \ - TEST_ZF (Val); \ - TEST_SF (Val); \ - TEST_CF (Val) + do { \ + const bool ShiftOut = (Val & 0x80) != 0; \ + Val <<= 1; \ + if (GET_CF ()) { \ + Val |= 0x01; \ + } \ + TEST_ZF (Val); \ + TEST_SF (Val); \ + SET_CF (ShiftOut); \ + } while (0) /* ROR */ #define ROR(Val) \ - if (GET_CF ()) { \ - Val |= 0x100; \ - } \ + do { \ + const bool ShiftOut = (Val & 0x01) != 0; \ + Val >>= 1; \ + if (GET_CF ()) { \ + Val |= 0x80; \ + } \ + TEST_ZF (Val); \ + TEST_SF (Val); \ + SET_CF (ShiftOut); \ + } while (0) + +/* ASL */ +#define ASL(Val) \ + SET_CF (Val & 0x80); \ + Val = (Val << 1) & 0xFF; \ + TEST_ZF (Val); \ + TEST_SF (Val) + +/* LSR */ +#define LSR(Val) \ SET_CF (Val & 0x01); \ Val >>= 1; \ TEST_ZF (Val); \ TEST_SF (Val) -/* SBC */ -#define SBC(v) \ +/* INC */ +#define INC(Val) \ + Val = (Val + 1) & 0xFF; \ + TEST_ZF (Val); \ + TEST_SF (Val) + +/* DEC */ +#define DEC(Val) \ + Val = (Val - 1) & 0xFF; \ + TEST_ZF (Val); \ + TEST_SF (Val) + +/* SLO */ +#define SLO(Val) \ + Val <<= 1; \ + SET_CF (Val & 0x100); \ + Regs.AC |= Val; \ + Regs.AC &= 0xFF; \ + TEST_ZF (Regs.AC); \ + TEST_SF (Regs.AC) + +/* RLA */ +#define RLA(Val) \ + Val <<= 1; \ + if (GET_CF ()) { \ + Val |= 0x01; \ + } \ + SET_CF (Val & 0x100); \ + Regs.AC &= Val; \ + TEST_ZF (Regs.AC); \ + TEST_SF (Regs.AC) + +/* SRE */ +#define SRE(Val) \ + SET_CF (Val & 0x01); \ + Val >>= 1; \ + Regs.AC ^= Val; \ + TEST_ZF (Regs.AC); \ + TEST_SF (Regs.AC) + +/* RRA */ +#define RRA(Val) \ + if (GET_CF ()) { \ + Val |= 0x100; \ + } \ + SET_CF (Val & 0x01); \ + Val >>= 1; \ + ADC_6502 (Val) + +/* BIT */ +#define BIT(Val) \ + SET_SF (Val & 0x80); \ + SET_OF (Val & 0x40); \ + SET_ZF ((Val & Regs.AC) == 0) + +/* BITIMM */ +/* The BIT instruction with immediate mode addressing only sets + the zero flag; the sign and overflow flags are not changed. */ +#define BITIMM(Val) \ + SET_ZF ((Val & Regs.AC) == 0) + +/* LDA */ +#define LDA(Val) \ + Regs.AC = Val; \ + TEST_SF (Val); \ + TEST_ZF (Val) + +/* LDX */ +#define LDX(Val) \ + Regs.XR = Val; \ + TEST_SF (Val); \ + TEST_ZF (Val) + +/* LDY */ +#define LDY(Val) \ + Regs.YR = Val; \ + TEST_SF (Val); \ + TEST_ZF (Val) + +/* LAX */ +#define LAX(Val) \ + Regs.AC = Val; \ + Regs.XR = Val; \ + TEST_SF (Val); \ + TEST_ZF (Val) + +/* TSB */ +#define TSB(Val) \ + SET_ZF ((Val & Regs.AC) == 0); \ + Val |= Regs.AC + +/* TRB */ +#define TRB(Val) \ + SET_ZF ((Val & Regs.AC) == 0); \ + Val &= ~Regs.AC + +/* DCP */ +#define DCP(Val) \ + Val = (Val - 1) & 0xFF; \ + COMPARE (Regs.AC, Val) + +/* ISC */ +#define ISC(Val) \ + Val = (Val + 1) & 0xFF; \ + SBC_6502(Val) + +/* ASR */ +#define ASR(Val) \ + Regs.AC &= Val; \ + LSR(Regs.AC) + +/* ARR */ +#define ARR(Val) \ do { \ - unsigned old = Regs.AC; \ - unsigned rhs = (v & 0xFF); \ + unsigned tmp = Regs.AC & Val; \ + Val = tmp >> 1; \ + if (GET_CF ()) { \ + Val |= 0x80; \ + } \ if (GET_DF ()) { \ - unsigned lo; \ - int res; \ - lo = (old & 0x0F) - (rhs & 0x0F) + GET_CF () - 1; \ - if (lo & 0x80) { \ - lo = ((lo - 0x06) & 0x0F) - 0x10; \ + SET_SF (GET_CF ()); \ + TEST_ZF (Val); \ + SET_OF ((Val ^ tmp) & 0x40); \ + if (((tmp & 0x0f) + (tmp & 0x01)) > 0x05) { \ + Val = (Val & 0xf0) | ((Val + 0x06) & 0x0f); \ } \ - Regs.AC = (old & 0xF0) - (rhs & 0xF0) + lo; \ - if (Regs.AC & 0x80) { \ - Regs.AC -= 0x60; \ + if (((tmp & 0xf0) + (tmp & 0x10)) > 0x50) { \ + Val = (Val & 0x0f) | ((Val + 0x60) & 0xf0); \ + SET_CF(1); \ + } else { \ + SET_CF(0); \ } \ - res = Regs.AC - rhs + (!GET_CF ()); \ - TEST_ZF (res); \ - TEST_SF (res); \ - SET_CF (res <= 0xFF); \ - SET_OF (((old^rhs) & (old^res) & 0x80)); \ - if (CPU != CPU_6502) { \ + if (CPU == CPU_65C02) { \ ++Cycles; \ } \ } else { \ - Regs.AC -= rhs + (!GET_CF ()); \ - TEST_ZF (Regs.AC); \ - TEST_SF (Regs.AC); \ - SET_CF (Regs.AC <= 0xFF); \ - SET_OF (((old^rhs) & (old^Regs.AC) & 0x80)); \ - Regs.AC &= 0xFF; \ + TEST_SF (Val); \ + TEST_ZF (Val); \ + SET_CF (Val & 0x40); \ + SET_OF ((Val & 0x40) ^ ((Val & 0x20) << 1)); \ + } \ + Regs.AC = Val; \ + } while (0) + +/* ANE */ +/* An "unstable" illegal opcode that depends on a "constant" value that isn't + * really constant. It varies between machines, with temperature, and so on. + * Original sim65 behavior was to use the constant 0xEF here. To get behavior + * in line with the 65x02 testsuite, we now use the value 0xEE instead, + * which is also a reasonable choice that can be observed in practice. + */ +#define ANE(Val) \ + Val = (Regs.AC | 0xEE) & Regs.XR & Val; \ + Regs.AC = Val; \ + TEST_SF (Val); \ + TEST_ZF (Val) + +/* LXA */ +#define LXA(Val) \ + Val = (Regs.AC | 0xEE) & Val; \ + Regs.AC = Val; \ + Regs.XR = Val; \ + TEST_SF (Val); \ + TEST_ZF (Val) + +/* SBX */ +#define SBX(Val) \ + do { \ + unsigned tmp = (Regs.AC & Regs.XR) - (Val); \ + SET_CF (tmp < 0x100); \ + tmp &= 0xFF; \ + Regs.XR = tmp; \ + TEST_SF (tmp); \ + TEST_ZF (tmp); \ + } while (0) + +/* NOP */ +#define NOP(Val) \ + (void)Val + +/* TAS */ +#define TAS(Val) \ + Val = Regs.AC & Regs.XR; \ + Regs.SP = Val; \ + Val &= (address >> 8) + 1 + +/* SHA */ +#define SHA(Val) \ + Val = Regs.AC & Regs.XR & ((address >> 8) + 1) + +/* ANC */ +#define ANC(Val) \ + Val = Regs.AC & Val; \ + Regs.AC = Val; \ + SET_CF (Val & 0x80); \ + TEST_SF (Val); \ + TEST_ZF (Val) + + +/* LAS */ +#define LAS(Val) \ + Val = Regs.SP & Val; \ + Regs.AC = Val; \ + Regs.XR = Val; \ + Regs.SP = Val; \ + TEST_SF (Val); \ + TEST_ZF (Val) + +/* SBC, binary mode (6502 and 65C02) */ +#define SBC_BINARY_MODE(v) \ + do { \ + const uint8_t op = v; \ + const uint8_t OldAC = Regs.AC; \ + const bool borrow = !GET_CF(); \ + Regs.AC = OldAC - op - borrow; \ + const bool NV = Regs.AC >= 0x80; \ + SET_SF(NV); \ + SET_OF(((OldAC >= 0x80) ^ NV) & ((op < 0x80) ^ NV)); \ + SET_ZF(Regs.AC == 0); \ + SET_CF(OldAC >= op + borrow); \ + } while (0) + +/* SBC, decimal mode (6502 behavior) */ +#define SBC_DECIMAL_MODE_6502(v) \ + do { \ + const uint8_t op = v; \ + const uint8_t OldAC = Regs.AC; \ + bool borrow = !GET_CF(); \ + const uint8_t binary_result = OldAC - op - borrow; \ + const bool NV = binary_result >= 0x80; \ + uint8_t low_nibble = (OldAC & 15) - (op & 15) - borrow; \ + if ((borrow = low_nibble >= 0x80)) \ + low_nibble = (low_nibble + 10) & 15; \ + uint8_t high_nibble = (OldAC >> 4) - (op >> 4) - borrow;\ + if ((borrow = high_nibble >= 0x80)) \ + high_nibble = (high_nibble + 10) & 15; \ + Regs.AC = (high_nibble << 4) | low_nibble; \ + SET_SF(NV); \ + SET_OF(((OldAC >= 0x80) ^ NV) & ((op < 0x80) ^ NV)); \ + SET_ZF(binary_result == 0); \ + SET_CF(!borrow); \ + } while (0) + +/* SBC, decimal mode (65C02 behavior) */ +#define SBC_DECIMAL_MODE_65C02(v) \ + do { \ + const uint8_t op = v; \ + const uint8_t OldAC = Regs.AC; \ + bool borrow = !GET_CF(); \ + uint8_t low_nibble = (OldAC & 15) - (op & 15) - borrow; \ + if ((borrow = low_nibble >= 0x80)) \ + low_nibble += 10; \ + const bool low_nibble_still_negative = \ + (low_nibble >= 0x80); \ + low_nibble &= 15; \ + uint8_t high_nibble = (OldAC >> 4) - (op >> 4) - borrow;\ + const bool PN = (high_nibble & 8) != 0; \ + if ((borrow = high_nibble >= 0x80)) \ + high_nibble += 10; \ + high_nibble -= low_nibble_still_negative; \ + high_nibble &= 15; \ + Regs.AC = (high_nibble << 4) | low_nibble; \ + SET_SF(Regs.AC >= 0x80); \ + SET_OF(((OldAC >= 0x80) ^ PN) & ((op < 0x80) ^ PN)); \ + SET_ZF(Regs.AC == 0x00); \ + SET_CF(!borrow); \ + ++Cycles; \ + } while (0) + +/* SBC, 6502 version */ +#define SBC_6502(v) \ + do { \ + if (GET_DF()) { \ + SBC_DECIMAL_MODE_6502(v); \ + } else { \ + SBC_BINARY_MODE(v); \ + } \ + } while (0) + +/* SBC, 65C02 version */ +#define SBC_65C02(v) \ + do { \ + if (GET_DF()) { \ + SBC_DECIMAL_MODE_65C02(v); \ + } else { \ + SBC_BINARY_MODE(v); \ } \ } while (0) +/* Set/reset a specific bit in a zero-page byte. This macro + * macro is used to implement the 65C02 RMBx and SMBx instructions. + */ +#define ZP_BITOP(bitnr, bitval) \ + do { \ + const uint8_t zp_address = MemReadByte (Regs.PC + 1); \ + uint8_t zp_value = MemReadByte (zp_address); \ + if (bitval) { \ + zp_value |= (1 << bitnr); \ + } else { \ + zp_value &= ~(1 << bitnr); \ + } \ + MemWriteByte (zp_address, zp_value); \ + Regs.PC += 2; \ + Cycles = 5; \ + } while (0) + +/* Branch depending on the state of a specific bit of a zero page + * address. This macro is used to implement the 65C02 BBRx and + * BBSx instructions. + */ +#define ZP_BIT_BRANCH(bitnr, bitval) \ + do { \ + const uint8_t zp_address = MemReadByte (Regs.PC + 1); \ + const uint8_t zp_value = MemReadByte (zp_address); \ + const int8_t displacement = MemReadByte (Regs.PC + 2); \ + if (((zp_value & (1 << bitnr)) != 0) == bitval) { \ + Regs.PC += 3; \ + uint8_t OldPCH = PCH; \ + Regs.PC += displacement; \ + Cycles = 6; \ + if (PCH != OldPCH) { \ + Cycles += 1; \ + } \ + } else { \ + Regs.PC += 3; \ + Cycles = 5; \ + } \ + } while (0) + /*****************************************************************************/ /* Opcode handling functions */ /*****************************************************************************/ @@ -368,7 +1179,7 @@ static void OPC_6502_00 (void) PUSH (PCL); PUSH (Regs.SR); SET_IF (1); - if (CPU != CPU_6502) + if (CPU == CPU_65C02) { SET_DF (0); } @@ -380,22 +1191,35 @@ static void OPC_6502_00 (void) static void OPC_6502_01 (void) /* Opcode $01: ORA (ind,x) */ { - AC_OP_ZPXIND (|); + AC_OP (ZPXIND, |); } -static void OPC_65SC02_04 (void) +static void OPC_6502X_03 (void) +/* Opcode $03: SLO (zp,x) */ +{ + ILLx2_OP (ZPXIND, SLO); +} + + + +/* Aliases of opcode $04 */ +#define OPC_6502X_44 OPC_6502X_04 +#define OPC_6502X_64 OPC_6502X_04 + +static void OPC_6502X_04 (void) +/* Opcode $04: NOP zp */ +{ + ALU_OP (ZP, NOP); +} + + + +static void OPC_65C02_04 (void) /* Opcode $04: TSB zp */ { - unsigned char ZPAddr; - unsigned char Val; - Cycles = 5; - ZPAddr = MemReadByte (Regs.PC+1); - Val = MemReadByte (ZPAddr); - SET_ZF ((Val & Regs.AC) == 0); - MemWriteByte (ZPAddr, (unsigned char)(Val | Regs.AC)); - Regs.PC += 2; + MEM_OP (ZP, TSB); } @@ -403,7 +1227,7 @@ static void OPC_65SC02_04 (void) static void OPC_6502_05 (void) /* Opcode $05: ORA zp */ { - AC_OP_ZP (|); + AC_OP (ZP, |); } @@ -411,16 +1235,23 @@ static void OPC_6502_05 (void) static void OPC_6502_06 (void) /* Opcode $06: ASL zp */ { - unsigned char ZPAddr; - unsigned Val; - Cycles = 5; - ZPAddr = MemReadByte (Regs.PC+1); - Val = MemReadByte (ZPAddr) << 1; - MemWriteByte (ZPAddr, (unsigned char) Val); - TEST_ZF (Val & 0xFF); - TEST_SF (Val); - SET_CF (Val & 0x100); - Regs.PC += 2; + MEM_OP (ZP, ASL); +} + + + +static void OPC_6502X_07 (void) +/* Opcode $07: SLO zp */ +{ + ILLx2_OP (ZP, SLO); +} + + + +static void OPC_65C02_07 (void) +/* Opcode $07: RMB0 zp */ +{ + ZP_BITOP(0, 0); } @@ -447,27 +1278,35 @@ static void OPC_6502_0A (void) /* Opcode $0A: ASL a */ { Cycles = 2; - Regs.AC <<= 1; - TEST_ZF (Regs.AC & 0xFF); - TEST_SF (Regs.AC); - SET_CF (Regs.AC & 0x100); - Regs.AC &= 0xFF; + ASL(Regs.AC); Regs.PC += 1; } -static void OPC_65SC02_0C (void) +/* Aliases of opcode $0B */ +#define OPC_6502X_2B OPC_6502X_0B + +static void OPC_6502X_0B (void) +/* Opcode $0B: ANC #imm */ +{ + ALU_OP_IMM (ANC); +} + + + +static void OPC_6502X_0C (void) +/* Opcode $0C: NOP abs */ +{ + ALU_OP (ABS, NOP); +} + + + +static void OPC_65C02_0C (void) /* Opcode $0C: TSB abs */ { - unsigned Addr; - unsigned char Val; - Cycles = 6; - Addr = MemReadWord (Regs.PC+1); - Val = MemReadByte (Addr); - SET_ZF ((Val & Regs.AC) == 0); - MemWriteByte (Addr, (unsigned char) (Val | Regs.AC)); - Regs.PC += 3; + MEM_OP (ABS, TSB); } @@ -475,24 +1314,31 @@ static void OPC_65SC02_0C (void) static void OPC_6502_0D (void) /* Opcode $0D: ORA abs */ { - AC_OP_ABS (|); + AC_OP (ABS, |); } static void OPC_6502_0E (void) -/* Opcode $0E: ALS abs */ +/* Opcode $0E: ASL abs */ { - unsigned Addr; - unsigned Val; - Cycles = 6; - Addr = MemReadWord (Regs.PC+1); - Val = MemReadByte (Addr) << 1; - MemWriteByte (Addr, (unsigned char) Val); - TEST_ZF (Val & 0xFF); - TEST_SF (Val); - SET_CF (Val & 0x100); - Regs.PC += 3; + MEM_OP (ABS, ASL); +} + + + +static void OPC_6502X_0F (void) +/* Opcode $0F: SLO abs */ +{ + ILLx2_OP (ABS, SLO); +} + + + +static void OPC_65C02_0F (void) +/* Opcode $0F: BBR0 zp, rel */ +{ + ZP_BIT_BRANCH (0, 0); } @@ -508,30 +1354,46 @@ static void OPC_6502_10 (void) static void OPC_6502_11 (void) /* Opcode $11: ORA (zp),y */ { - AC_OP_ZPINDY (|); + AC_OP (ZPINDY, |); } -static void OPC_65SC02_12 (void) +static void OPC_65C02_12 (void) /* Opcode $12: ORA (zp) */ { - AC_OP_ZPIND (|); + AC_OP (ZPIND, |); } -static void OPC_65SC02_14 (void) +static void OPC_6502X_13 (void) +/* Opcode $03: SLO (zp),y */ +{ + ILLx2_OP (ZPINDY_NP, SLO); +} + + + +/* Aliases of opcode $14 */ +#define OPC_6502X_34 OPC_6502X_14 +#define OPC_6502X_54 OPC_6502X_14 +#define OPC_6502X_74 OPC_6502X_14 +#define OPC_6502X_D4 OPC_6502X_14 +#define OPC_6502X_F4 OPC_6502X_14 + +static void OPC_6502X_14 (void) +/* Opcode $04: NOP zp,x */ +{ + ALU_OP (ZPX, NOP); +} + + + +static void OPC_65C02_14 (void) /* Opcode $14: TRB zp */ { - unsigned char ZPAddr; - unsigned char Val; - Cycles = 5; - ZPAddr = MemReadByte (Regs.PC+1); - Val = MemReadByte (ZPAddr); - SET_ZF ((Val & Regs.AC) == 0); - MemWriteByte (ZPAddr, (unsigned char)(Val & ~Regs.AC)); - Regs.PC += 2; + MEM_OP (ZP, TRB); } @@ -539,7 +1401,7 @@ static void OPC_65SC02_14 (void) static void OPC_6502_15 (void) /* Opcode $15: ORA zp,x */ { - AC_OP_ZPX (|); + AC_OP (ZPX, |); } @@ -547,16 +1409,23 @@ static void OPC_6502_15 (void) static void OPC_6502_16 (void) /* Opcode $16: ASL zp,x */ { - unsigned char ZPAddr; - unsigned Val; - Cycles = 6; - ZPAddr = MemReadByte (Regs.PC+1) + Regs.XR; - Val = MemReadByte (ZPAddr) << 1; - MemWriteByte (ZPAddr, (unsigned char) Val); - TEST_ZF (Val & 0xFF); - TEST_SF (Val); - SET_CF (Val & 0x100); - Regs.PC += 2; + MEM_OP (ZPX, ASL); +} + + + +static void OPC_6502X_17 (void) +/* Opcode $17: SLO zp,x */ +{ + ILLx2_OP (ZPX, SLO); +} + + + +static void OPC_65C02_17 (void) +/* Opcode $17: RMB1 zp */ +{ + ZP_BITOP(1, 0); } @@ -574,34 +1443,48 @@ static void OPC_6502_18 (void) static void OPC_6502_19 (void) /* Opcode $19: ORA abs,y */ { - AC_OP_ABSY (|); + AC_OP (ABSY, |); } -static void OPC_65SC02_1A (void) +static void OPC_65C02_1A (void) /* Opcode $1A: INC a */ { Cycles = 2; - Regs.AC = (Regs.AC + 1) & 0xFF; - TEST_ZF (Regs.AC); - TEST_SF (Regs.AC); + INC(Regs.AC); Regs.PC += 1; } -static void OPC_65SC02_1C (void) +static void OPC_6502X_1B (void) +/* Opcode $1B: SLO abs,y */ +{ + ILLx2_OP (ABSY_NP, SLO); +} + + + +/* Aliases of opcode $1C */ +#define OPC_6502X_3C OPC_6502X_1C +#define OPC_6502X_5C OPC_6502X_1C +#define OPC_6502X_7C OPC_6502X_1C +#define OPC_6502X_DC OPC_6502X_1C +#define OPC_6502X_FC OPC_6502X_1C + +static void OPC_6502X_1C (void) +/* Opcode $1C: NOP abs,x */ +{ + ALU_OP (ABSX, NOP); +} + + + +static void OPC_65C02_1C (void) /* Opcode $1C: TRB abs */ { - unsigned Addr; - unsigned char Val; - Cycles = 6; - Addr = MemReadWord (Regs.PC+1); - Val = MemReadByte (Addr); - SET_ZF ((Val & Regs.AC) == 0); - MemWriteByte (Addr, (unsigned char) (Val & ~Regs.AC)); - Regs.PC += 3; + MEM_OP (ABS, TRB); } @@ -609,7 +1492,7 @@ static void OPC_65SC02_1C (void) static void OPC_6502_1D (void) /* Opcode $1D: ORA abs,x */ { - AC_OP_ABSX (|); + AC_OP (ABSX, |); } @@ -617,18 +1500,32 @@ static void OPC_6502_1D (void) static void OPC_6502_1E (void) /* Opcode $1E: ASL abs,x */ { - unsigned Addr; - unsigned Val; - Cycles = 7; - Addr = MemReadWord (Regs.PC+1) + Regs.XR; - if (CPU != CPU_6502 && !PAGE_CROSS (Addr, Regs.XR)) - --Cycles; - Val = MemReadByte (Addr) << 1; - MemWriteByte (Addr, (unsigned char) Val); - TEST_ZF (Val & 0xFF); - TEST_SF (Val); - SET_CF (Val & 0x100); - Regs.PC += 3; + MEM_OP (ABSX_NP, ASL); +} + + + +static void OPC_65C02_1E (void) +/* Opcode $1E: ASL abs,x */ +{ + MEM_OP (ABSX, ASL); + --Cycles; +} + + + +static void OPC_6502X_1F (void) +/* Opcode $1F: SLO abs,x */ +{ + ILLx2_OP (ABSX_NP, SLO); +} + + + +static void OPC_65C02_1F (void) +/* Opcode $1F: BBR1 zp, rel */ +{ + ZP_BIT_BRANCH (1, 0); } @@ -636,13 +1533,35 @@ static void OPC_6502_1E (void) static void OPC_6502_20 (void) /* Opcode $20: JSR */ { - unsigned Addr; + /* The obvious way to implement JSR for the 6502 is to (a) read the target address, + * and then (b) push the return address minus one. Or do (b) first, then (a). + * + * However, there is a non-obvious case where this conflicts with the actual order + * of operations that the 6502 does, which is: + * + * (a) Load the LSB of the target address. + * (b) Push the MSB of the return address, minus one. + * (c) Push the LSB of the return address, minus one. + * (d) Load the MSB of the target address. + * + * This can make a difference in a pretty esoteric case, if the JSR target is located, + * wholly or in part, inside the stack page (!). This won't happen in normal code + * but it can happen in specifically constructed examples. + * + * To deal with this, we load the LSB and MSB of the target address separately, + * with the pushing of the return address sandwiched in between, to mimic + * the order of the bus operations on a real 6502. + */ + Cycles = 6; - Addr = MemReadWord (Regs.PC+1); - Regs.PC += 2; + Regs.PC += 1; + uint8_t AddrLo = MemReadByte(Regs.PC); + Regs.PC += 1; PUSH (PCH); PUSH (PCL); - Regs.PC = Addr; + uint8_t AddrHi = MemReadByte(Regs.PC); + + Regs.PC = AddrLo + (AddrHi << 8); ParaVirtHooks (&Regs); } @@ -652,23 +1571,23 @@ static void OPC_6502_20 (void) static void OPC_6502_21 (void) /* Opcode $21: AND (zp,x) */ { - AC_OP_ZPXIND (&); + AC_OP (ZPXIND, &); +} + + + +static void OPC_6502X_23 (void) +/* Opcode $23: RLA (zp,x) */ +{ + ILLx2_OP (ZPXIND, RLA); } static void OPC_6502_24 (void) -/* Opcode $24: BIT zp */ { - unsigned char ZPAddr; - unsigned char Val; - Cycles = 3; - ZPAddr = MemReadByte (Regs.PC+1); - Val = MemReadByte (ZPAddr); - SET_SF (Val & 0x80); - SET_OF (Val & 0x40); - SET_ZF ((Val & Regs.AC) == 0); - Regs.PC += 2; +/* Opcode $24: BIT zp */ + ALU_OP (ZP, BIT); } @@ -676,7 +1595,7 @@ static void OPC_6502_24 (void) static void OPC_6502_25 (void) /* Opcode $25: AND zp */ { - AC_OP_ZP (&); + AC_OP (ZP, &); } @@ -684,14 +1603,23 @@ static void OPC_6502_25 (void) static void OPC_6502_26 (void) /* Opcode $26: ROL zp */ { - unsigned char ZPAddr; - unsigned Val; - Cycles = 5; - ZPAddr = MemReadByte (Regs.PC+1); - Val = MemReadByte (ZPAddr); - ROL (Val); - MemWriteByte (ZPAddr, Val); - Regs.PC += 2; + MEM_OP (ZP, ROL); +} + + + +static void OPC_6502X_27 (void) +/* Opcode $27: RLA zp */ +{ + ILLx2_OP (ZP, RLA); +} + + + +static void OPC_65C02_27 (void) +/* Opcode $27: RMB2 zp */ +{ + ZP_BITOP(2, 0); } @@ -730,15 +1658,7 @@ static void OPC_6502_2A (void) static void OPC_6502_2C (void) /* Opcode $2C: BIT abs */ { - unsigned Addr; - unsigned char Val; - Cycles = 4; - Addr = MemReadWord (Regs.PC+1); - Val = MemReadByte (Addr); - SET_SF (Val & 0x80); - SET_OF (Val & 0x40); - SET_ZF ((Val & Regs.AC) == 0); - Regs.PC += 3; + ALU_OP (ABS, BIT); } @@ -746,7 +1666,7 @@ static void OPC_6502_2C (void) static void OPC_6502_2D (void) /* Opcode $2D: AND abs */ { - AC_OP_ABS (&); + AC_OP (ABS, &); } @@ -754,14 +1674,23 @@ static void OPC_6502_2D (void) static void OPC_6502_2E (void) /* Opcode $2E: ROL abs */ { - unsigned Addr; - unsigned Val; - Cycles = 6; - Addr = MemReadWord (Regs.PC+1); - Val = MemReadByte (Addr); - ROL (Val); - MemWriteByte (Addr, Val); - Regs.PC += 3; + MEM_OP (ABS, ROL); +} + + + +static void OPC_6502X_2F (void) +/* Opcode $2F: RLA abs */ +{ + ILLx2_OP (ABS, RLA); +} + + + +static void OPC_65C02_2F (void) +/* Opcode $2F: BBR2 zp, rel */ +{ + ZP_BIT_BRANCH (2, 0); } @@ -777,31 +1706,31 @@ static void OPC_6502_30 (void) static void OPC_6502_31 (void) /* Opcode $31: AND (zp),y */ { - AC_OP_ZPINDY (&); + AC_OP (ZPINDY, &); } -static void OPC_65SC02_32 (void) +static void OPC_65C02_32 (void) /* Opcode $32: AND (zp) */ { - AC_OP_ZPIND (&); + AC_OP (ZPIND, &); } -static void OPC_65SC02_34 (void) +static void OPC_6502X_33 (void) +/* Opcode $33: RLA (zp),y */ +{ + ILLx2_OP (ZPINDY_NP, RLA); +} + + + +static void OPC_65C02_34 (void) /* Opcode $34: BIT zp,x */ { - unsigned char ZPAddr; - unsigned char Val; - Cycles = 4; - ZPAddr = MemReadByte (Regs.PC+1) + Regs.XR; - Val = MemReadByte (ZPAddr); - SET_SF (Val & 0x80); - SET_OF (Val & 0x40); - SET_ZF ((Val & Regs.AC) == 0); - Regs.PC += 2; + ALU_OP (ZPX, BIT); } @@ -809,7 +1738,7 @@ static void OPC_65SC02_34 (void) static void OPC_6502_35 (void) /* Opcode $35: AND zp,x */ { - AC_OP_ZPX (&); + AC_OP (ZPX, &); } @@ -817,14 +1746,23 @@ static void OPC_6502_35 (void) static void OPC_6502_36 (void) /* Opcode $36: ROL zp,x */ { - unsigned char ZPAddr; - unsigned Val; - Cycles = 6; - ZPAddr = MemReadByte (Regs.PC+1) + Regs.XR; - Val = MemReadByte (ZPAddr); - ROL (Val); - MemWriteByte (ZPAddr, Val); - Regs.PC += 2; + MEM_OP (ZPX, ROL); +} + + + +static void OPC_6502X_37 (void) +/* Opcode $37: RLA zp,x */ +{ + ILLx2_OP (ZPX, RLA); +} + + + +static void OPC_65C02_37 (void) +/* Opcode $37: RMB3 zp */ +{ + ZP_BITOP(3, 0); } @@ -842,37 +1780,33 @@ static void OPC_6502_38 (void) static void OPC_6502_39 (void) /* Opcode $39: AND abs,y */ { - AC_OP_ABSY (&); + AC_OP (ABSY, &); } -static void OPC_65SC02_3A (void) +static void OPC_65C02_3A (void) /* Opcode $3A: DEC a */ { Cycles = 2; - Regs.AC = (Regs.AC - 1) & 0xFF; - TEST_ZF (Regs.AC); - TEST_SF (Regs.AC); + DEC (Regs.AC); Regs.PC += 1; } -static void OPC_65SC02_3C (void) +static void OPC_6502X_3B (void) +/* Opcode $3B: RLA abs,y */ +{ + ILLx2_OP (ABSY_NP, RLA); +} + + + +static void OPC_65C02_3C (void) /* Opcode $3C: BIT abs,x */ { - unsigned Addr; - unsigned char Val; - Cycles = 4; - Addr = MemReadWord (Regs.PC+1); - if (PAGE_CROSS (Addr, Regs.XR)) - ++Cycles; - Val = MemReadByte (Addr + Regs.XR); - SET_SF (Val & 0x80); - SET_OF (Val & 0x40); - SET_ZF ((Val & Regs.AC) == 0); - Regs.PC += 3; + ALU_OP (ABSX, BIT); } @@ -880,7 +1814,7 @@ static void OPC_65SC02_3C (void) static void OPC_6502_3D (void) /* Opcode $3D: AND abs,x */ { - AC_OP_ABSX (&); + AC_OP (ABSX, &); } @@ -888,16 +1822,32 @@ static void OPC_6502_3D (void) static void OPC_6502_3E (void) /* Opcode $3E: ROL abs,x */ { - unsigned Addr; - unsigned Val; - Cycles = 7; - Addr = MemReadWord (Regs.PC+1) + Regs.XR; - if (CPU != CPU_6502 && !PAGE_CROSS (Addr, Regs.XR)) - --Cycles; - Val = MemReadByte (Addr); - ROL (Val); - MemWriteByte (Addr, Val); - Regs.PC += 2; + MEM_OP (ABSX_NP, ROL); +} + + + +static void OPC_65C02_3E (void) +/* Opcode $3E: ROL abs,x */ +{ + MEM_OP (ABSX, ROL); + --Cycles; +} + + + +static void OPC_6502X_3F (void) +/* Opcode $3F: RLA abs,x */ +{ + ILLx2_OP (ABSX_NP, RLA); +} + + + +static void OPC_65C02_3F (void) +/* Opcode $3F: BBR3 zp, rel */ +{ + ZP_BIT_BRANCH (3, 0); } @@ -918,16 +1868,15 @@ static void OPC_6502_40 (void) static void OPC_6502_41 (void) /* Opcode $41: EOR (zp,x) */ { - AC_OP_ZPXIND (^); + AC_OP (ZPXIND, ^); } -static void OPC_65C02_44 (void) -/* Opcode $44: 'zp' 3 cycle NOP */ +static void OPC_6502X_43 (void) +/* Opcode $43: SRE (zp,x) */ { - Cycles = 3; - Regs.PC += 2; + ILLx2_OP (ZPXIND, SRE); } @@ -935,7 +1884,7 @@ static void OPC_65C02_44 (void) static void OPC_6502_45 (void) /* Opcode $45: EOR zp */ { - AC_OP_ZP (^); + AC_OP (ZP, ^); } @@ -943,17 +1892,23 @@ static void OPC_6502_45 (void) static void OPC_6502_46 (void) /* Opcode $46: LSR zp */ { - unsigned char ZPAddr; - unsigned char Val; - Cycles = 5; - ZPAddr = MemReadByte (Regs.PC+1); - Val = MemReadByte (ZPAddr); - SET_CF (Val & 0x01); - Val >>= 1; - MemWriteByte (ZPAddr, Val); - TEST_ZF (Val); - TEST_SF (Val); - Regs.PC += 2; + MEM_OP (ZP, LSR); +} + + + +static void OPC_6502X_47 (void) +/* Opcode $47: SRE zp */ +{ + ILLx2_OP (ZP, SRE); +} + + + +static void OPC_65C02_47 (void) +/* Opcode $47: RMB4 zp */ +{ + ZP_BITOP(4, 0); } @@ -980,15 +1935,20 @@ static void OPC_6502_4A (void) /* Opcode $4A: LSR a */ { Cycles = 2; - SET_CF (Regs.AC & 0x01); - Regs.AC >>= 1; - TEST_ZF (Regs.AC); - TEST_SF (Regs.AC); + LSR (Regs.AC); Regs.PC += 1; } +static void OPC_6502X_4B (void) +/* Opcode $4B: ASR imm */ +{ + ALU_OP_IMM (ASR); +} + + + static void OPC_6502_4C (void) /* Opcode $4C: JMP abs */ { @@ -1003,7 +1963,7 @@ static void OPC_6502_4C (void) static void OPC_6502_4D (void) /* Opcode $4D: EOR abs */ { - AC_OP_ABS (^); + AC_OP (ABS, ^); } @@ -1011,17 +1971,23 @@ static void OPC_6502_4D (void) static void OPC_6502_4E (void) /* Opcode $4E: LSR abs */ { - unsigned Addr; - unsigned char Val; - Cycles = 6; - Addr = MemReadWord (Regs.PC+1); - Val = MemReadByte (Addr); - SET_CF (Val & 0x01); - Val >>= 1; - MemWriteByte (Addr, Val); - TEST_ZF (Val); - TEST_SF (Val); - Regs.PC += 3; + MEM_OP (ABS, LSR); +} + + + +static void OPC_6502X_4F (void) +/* Opcode $4F: SRE abs */ +{ + ILLx2_OP (ABS, SRE); +} + + + +static void OPC_65C02_4F (void) +/* Opcode $4F: BBR4 zp, rel */ +{ + ZP_BIT_BRANCH (4, 0); } @@ -1037,15 +2003,23 @@ static void OPC_6502_50 (void) static void OPC_6502_51 (void) /* Opcode $51: EOR (zp),y */ { - AC_OP_ZPINDY (^); + AC_OP (ZPINDY, ^); } -static void OPC_65SC02_52 (void) +static void OPC_65C02_52 (void) /* Opcode $52: EOR (zp) */ { - AC_OP_ZPIND (^); + AC_OP (ZPIND, ^); +} + + + +static void OPC_6502X_53 (void) +/* Opcode $43: SRE (zp),y */ +{ + ILLx2_OP (ZPINDY_NP, SRE); } @@ -1053,7 +2027,7 @@ static void OPC_65SC02_52 (void) static void OPC_6502_55 (void) /* Opcode $55: EOR zp,x */ { - AC_OP_ZPX (^); + AC_OP (ZPX, ^); } @@ -1061,17 +2035,23 @@ static void OPC_6502_55 (void) static void OPC_6502_56 (void) /* Opcode $56: LSR zp,x */ { - unsigned char ZPAddr; - unsigned char Val; - Cycles = 6; - ZPAddr = MemReadByte (Regs.PC+1) + Regs.XR; - Val = MemReadByte (ZPAddr); - SET_CF (Val & 0x01); - Val >>= 1; - MemWriteByte (ZPAddr, Val); - TEST_ZF (Val); - TEST_SF (Val); - Regs.PC += 2; + MEM_OP (ZPX, LSR); +} + + + +static void OPC_6502X_57 (void) +/* Opcode $57: SRE zp,x */ +{ + ILLx2_OP (ZPX, SRE); +} + + + +static void OPC_65C02_57 (void) +/* Opcode $57: RMB5 zp */ +{ + ZP_BITOP(5, 0); } @@ -1089,12 +2069,12 @@ static void OPC_6502_58 (void) static void OPC_6502_59 (void) /* Opcode $59: EOR abs,y */ { - AC_OP_ABSY (^); + AC_OP (ABSY, ^); } -static void OPC_65SC02_5A (void) +static void OPC_65C02_5A (void) /* Opcode $5A: PHY */ { Cycles = 3; @@ -1104,9 +2084,25 @@ static void OPC_65SC02_5A (void) +static void OPC_6502X_5B (void) +/* Opcode $5B: SRE abs,y */ +{ + ILLx2_OP (ABSY_NP, SRE); +} + + + static void OPC_65C02_5C (void) /* Opcode $5C: 'Absolute' 8 cycle NOP */ { + /* This instruction takes 8 cycles, as per the following sources: + * + * - http://www.6502.org/tutorials/65c02opcodes.html + * - Tests on a WDC 65C02 in hardware. + * + * The 65x02 testsuite however claims that this instruction takes 4 cycles. + * See issue: https://github.com/SingleStepTests/65x02/issues/12 + */ Cycles = 8; Regs.PC += 3; } @@ -1116,7 +2112,7 @@ static void OPC_65C02_5C (void) static void OPC_6502_5D (void) /* Opcode $5D: EOR abs,x */ { - AC_OP_ABSX (^); + AC_OP (ABSX, ^); } @@ -1124,19 +2120,32 @@ static void OPC_6502_5D (void) static void OPC_6502_5E (void) /* Opcode $5E: LSR abs,x */ { - unsigned Addr; - unsigned char Val; - Cycles = 7; - Addr = MemReadWord (Regs.PC+1) + Regs.XR; - if (CPU != CPU_6502 && !PAGE_CROSS (Addr, Regs.XR)) - --Cycles; - Val = MemReadByte (Addr); - SET_CF (Val & 0x01); - Val >>= 1; - MemWriteByte (Addr, Val); - TEST_ZF (Val); - TEST_SF (Val); - Regs.PC += 3; + MEM_OP (ABSX_NP, LSR); +} + + + +static void OPC_65C02_5E (void) +/* Opcode $5E: LSR abs,x */ +{ + MEM_OP (ABSX, LSR); + --Cycles; +} + + + +static void OPC_6502X_5F (void) +/* Opcode $5F: SRE abs,x */ +{ + ILLx2_OP (ABSX_NP, SRE); +} + + + +static void OPC_65C02_5F (void) +/* Opcode $5F: BBR5 zp, rel */ +{ + ZP_BIT_BRANCH (5, 0); } @@ -1155,25 +2164,31 @@ static void OPC_6502_60 (void) static void OPC_6502_61 (void) /* Opcode $61: ADC (zp,x) */ { - unsigned char ZPAddr; - unsigned Addr; - Cycles = 6; - ZPAddr = MemReadByte (Regs.PC+1) + Regs.XR; - Addr = MemReadZPWord (ZPAddr); - ADC (MemReadByte (Addr)); - Regs.PC += 2; + ALU_OP (ZPXIND, ADC_6502); } -static void OPC_65SC02_64 (void) +static void OPC_65C02_61 (void) +/* Opcode $61: ADC (zp,x) */ +{ + ALU_OP (ZPXIND, ADC_65C02); +} + + + +static void OPC_6502X_63 (void) +/* Opcode $63: RRA (zp,x) */ +{ + ILLx2_OP (ZPXIND, RRA); +} + + + +static void OPC_65C02_64 (void) /* Opcode $64: STZ zp */ { - unsigned char ZPAddr; - Cycles = 3; - ZPAddr = MemReadByte (Regs.PC+1); - MemWriteByte (ZPAddr, 0); - Regs.PC += 2; + STO_OP (ZP, 0); } @@ -1181,11 +2196,15 @@ static void OPC_65SC02_64 (void) static void OPC_6502_65 (void) /* Opcode $65: ADC zp */ { - unsigned char ZPAddr; - Cycles = 3; - ZPAddr = MemReadByte (Regs.PC+1); - ADC (MemReadByte (ZPAddr)); - Regs.PC += 2; + ALU_OP (ZP, ADC_6502); +} + + + +static void OPC_65C02_65 (void) +/* Opcode $65: ADC zp */ +{ + ALU_OP (ZP, ADC_65C02); } @@ -1193,14 +2212,23 @@ static void OPC_6502_65 (void) static void OPC_6502_66 (void) /* Opcode $66: ROR zp */ { - unsigned char ZPAddr; - unsigned Val; - Cycles = 5; - ZPAddr = MemReadByte (Regs.PC+1); - Val = MemReadByte (ZPAddr); - ROR (Val); - MemWriteByte (ZPAddr, Val); - Regs.PC += 2; + MEM_OP (ZP, ROR); +} + + + +static void OPC_6502X_67 (void) +/* Opcode $67: RRA zp */ +{ + ILLx2_OP (ZP, RRA); +} + + + +static void OPC_65C02_67 (void) +/* Opcode $67: RMB6 zp */ +{ + ZP_BITOP(6, 0); } @@ -1220,9 +2248,15 @@ static void OPC_6502_68 (void) static void OPC_6502_69 (void) /* Opcode $69: ADC #imm */ { - Cycles = 2; - ADC (MemReadByte (Regs.PC+1)); - Regs.PC += 2; + ALU_OP_IMM (ADC_6502); +} + + + +static void OPC_65C02_69 (void) +/* Opcode $69: ADC #imm */ +{ + ALU_OP_IMM (ADC_65C02); } @@ -1237,6 +2271,14 @@ static void OPC_6502_6A (void) +static void OPC_6502X_6B (void) +/* Opcode $6B: ARR imm */ +{ + ALU_OP_IMM (ARR); +} + + + static void OPC_6502_6C (void) /* Opcode $6C: JMP (ind) */ { @@ -1244,25 +2286,17 @@ static void OPC_6502_6C (void) PC = Regs.PC; Lo = MemReadWord (PC+1); - if (CPU == CPU_6502) - { - /* Emulate the 6502 bug */ - Cycles = 5; - Regs.PC = MemReadByte (Lo); - Hi = (Lo & 0xFF00) | ((Lo + 1) & 0xFF); - Regs.PC |= (MemReadByte (Hi) << 8); + /* Emulate the buggy 6502 behavior */ + Cycles = 5; + Regs.PC = MemReadByte (Lo); + Hi = (Lo & 0xFF00) | ((Lo + 1) & 0xFF); + Regs.PC |= (MemReadByte (Hi) << 8); - /* Output a warning if the bug is triggered */ - if (Hi != Lo + 1) - { - Warning ("6502 indirect jump bug triggered at $%04X, ind addr = $%04X", - PC, Lo); - } - } - else + /* Output a warning if the bug is triggered */ + if (Hi != Lo + 1) { - Cycles = 6; - Regs.PC = MemReadWord(Lo); + Warning ("6502 indirect jump bug triggered at $%04X, ind addr = $%04X", + PC, Lo); } ParaVirtHooks (&Regs); @@ -1273,8 +2307,8 @@ static void OPC_6502_6C (void) static void OPC_65C02_6C (void) /* Opcode $6C: JMP (ind) */ { - /* 6502 bug fixed here */ - Cycles = 5; + /* The 6502 bug is fixed on the 65C02, at the cost of an extra cycle. */ + Cycles = 6; Regs.PC = MemReadWord (MemReadWord (Regs.PC+1)); ParaVirtHooks (&Regs); @@ -1285,11 +2319,15 @@ static void OPC_65C02_6C (void) static void OPC_6502_6D (void) /* Opcode $6D: ADC abs */ { - unsigned Addr; - Cycles = 4; - Addr = MemReadWord (Regs.PC+1); - ADC (MemReadByte (Addr)); - Regs.PC += 3; + ALU_OP (ABS, ADC_6502); +} + + + +static void OPC_65C02_6D (void) +/* Opcode $6D: ADC abs */ +{ + ALU_OP (ABS, ADC_65C02); } @@ -1297,14 +2335,23 @@ static void OPC_6502_6D (void) static void OPC_6502_6E (void) /* Opcode $6E: ROR abs */ { - unsigned Addr; - unsigned Val; - Cycles = 6; - Addr = MemReadWord (Regs.PC+1); - Val = MemReadByte (Addr); - ROR (Val); - MemWriteByte (Addr, Val); - Regs.PC += 3; + MEM_OP (ABS, ROR); +} + + + +static void OPC_6502X_6F (void) +/* Opcode $6F: RRA abs */ +{ + ILLx2_OP (ABS, RRA); +} + + + +static void OPC_65C02_6F (void) +/* Opcode $6F: BBR6 zp, rel */ +{ + ZP_BIT_BRANCH (6, 0); } @@ -1320,42 +2367,39 @@ static void OPC_6502_70 (void) static void OPC_6502_71 (void) /* Opcode $71: ADC (zp),y */ { - unsigned char ZPAddr; - unsigned Addr; - Cycles = 5; - ZPAddr = MemReadByte (Regs.PC+1); - Addr = MemReadZPWord (ZPAddr); - if (PAGE_CROSS (Addr, Regs.YR)) { - ++Cycles; - } - ADC (MemReadByte (Addr + Regs.YR)); - Regs.PC += 2; + ALU_OP (ZPINDY, ADC_6502); } -static void OPC_65SC02_72 (void) +static void OPC_65C02_71 (void) +/* Opcode $71: ADC (zp),y */ +{ + ALU_OP (ZPINDY, ADC_65C02); +} + + + +static void OPC_65C02_72 (void) /* Opcode $72: ADC (zp) */ { - unsigned char ZPAddr; - unsigned Addr; - Cycles = 5; - ZPAddr = MemReadByte (Regs.PC+1); - Addr = MemReadZPWord (ZPAddr); - ADC (MemReadByte (Addr)); - Regs.PC += 2; + ALU_OP (ZPIND, ADC_65C02); } -static void OPC_65SC02_74 (void) +static void OPC_6502X_73 (void) +/* Opcode $73: RRA (zp),y */ +{ + ILLx2_OP (ZPINDY_NP, RRA); +} + + + +static void OPC_65C02_74 (void) /* Opcode $74: STZ zp,x */ { - unsigned char ZPAddr; - Cycles = 4; - ZPAddr = MemReadByte (Regs.PC+1) + Regs.XR; - MemWriteByte (ZPAddr, 0); - Regs.PC += 2; + STO_OP (ZPX, 0); } @@ -1363,11 +2407,15 @@ static void OPC_65SC02_74 (void) static void OPC_6502_75 (void) /* Opcode $75: ADC zp,x */ { - unsigned char ZPAddr; - Cycles = 4; - ZPAddr = MemReadByte (Regs.PC+1) + Regs.XR; - ADC (MemReadByte (ZPAddr)); - Regs.PC += 2; + ALU_OP (ZPX, ADC_6502); +} + + + +static void OPC_65C02_75 (void) +/* Opcode $75: ADC zp,x */ +{ + ALU_OP (ZPX, ADC_65C02); } @@ -1375,14 +2423,23 @@ static void OPC_6502_75 (void) static void OPC_6502_76 (void) /* Opcode $76: ROR zp,x */ { - unsigned char ZPAddr; - unsigned Val; - Cycles = 6; - ZPAddr = MemReadByte (Regs.PC+1) + Regs.XR; - Val = MemReadByte (ZPAddr); - ROR (Val); - MemWriteByte (ZPAddr, Val); - Regs.PC += 2; + MEM_OP (ZPX, ROR); +} + + + +static void OPC_6502X_77 (void) +/* Opcode $77: RRA zp,x */ +{ + ILLx2_OP (ZPX, RRA); +} + + + +static void OPC_65C02_77 (void) +/* Opcode $77: RMB7 zp */ +{ + ZP_BITOP(7, 0); } @@ -1400,19 +2457,20 @@ static void OPC_6502_78 (void) static void OPC_6502_79 (void) /* Opcode $79: ADC abs,y */ { - unsigned Addr; - Cycles = 4; - Addr = MemReadWord (Regs.PC+1); - if (PAGE_CROSS (Addr, Regs.YR)) { - ++Cycles; - } - ADC (MemReadByte (Addr + Regs.YR)); - Regs.PC += 3; + ALU_OP (ABSY, ADC_6502); } -static void OPC_65SC02_7A (void) +static void OPC_65C02_79 (void) +/* Opcode $79: ADC abs,y */ +{ + ALU_OP (ABSY, ADC_65C02); +} + + + +static void OPC_65C02_7A (void) /* Opcode $7A: PLY */ { Cycles = 4; @@ -1424,7 +2482,15 @@ static void OPC_65SC02_7A (void) -static void OPC_65SC02_7C (void) +static void OPC_6502X_7B (void) +/* Opcode $7B: RRA abs,y */ +{ + ILLx2_OP (ABSY_NP, RRA); +} + + + +static void OPC_65C02_7C (void) /* Opcode $7C: JMP (ind,X) */ { unsigned PC, Adr; @@ -1441,14 +2507,15 @@ static void OPC_65SC02_7C (void) static void OPC_6502_7D (void) /* Opcode $7D: ADC abs,x */ { - unsigned Addr; - Cycles = 4; - Addr = MemReadWord (Regs.PC+1); - if (PAGE_CROSS (Addr, Regs.XR)) { - ++Cycles; - } - ADC (MemReadByte (Addr + Regs.XR)); - Regs.PC += 3; + ALU_OP (ABSX, ADC_6502); +} + + + +static void OPC_65C02_7D (void) +/* Opcode $7D: ADC abs,x */ +{ + ALU_OP (ABSX, ADC_65C02); } @@ -1456,21 +2523,51 @@ static void OPC_6502_7D (void) static void OPC_6502_7E (void) /* Opcode $7E: ROR abs,x */ { - unsigned Addr; - unsigned Val; - Cycles = 7; - Addr = MemReadWord (Regs.PC+1) + Regs.XR; - if (CPU != CPU_6502 && !PAGE_CROSS (Addr, Regs.XR)) - --Cycles; - Val = MemReadByte (Addr); - ROR (Val); - MemWriteByte (Addr, Val); - Regs.PC += 3; + MEM_OP (ABSX_NP, ROR); } -static void OPC_65SC02_80 (void) +static void OPC_65C02_7E (void) +/* Opcode $7E: ROR abs,x */ +{ + MEM_OP (ABSX, ROR); + --Cycles; +} + + + +static void OPC_6502X_7F (void) +/* Opcode $7F: RRA abs,x */ +{ + ILLx2_OP (ABSX_NP, RRA); +} + + + +static void OPC_65C02_7F (void) +/* Opcode $7F: BBR7 zp, rel */ +{ + ZP_BIT_BRANCH (7, 0); +} + + + +/* Aliases of opcode $80 */ +#define OPC_6502X_82 OPC_6502X_80 +#define OPC_6502X_C2 OPC_6502X_80 +#define OPC_6502X_E2 OPC_6502X_80 +#define OPC_6502X_89 OPC_6502X_80 + +static void OPC_6502X_80 (void) +/* Opcode $80: NOP imm */ +{ + ALU_OP_IMM (NOP); +} + + + +static void OPC_65C02_80 (void) /* Opcode $80: BRA */ { BRANCH (1); @@ -1481,13 +2578,15 @@ static void OPC_65SC02_80 (void) static void OPC_6502_81 (void) /* Opcode $81: STA (zp,x) */ { - unsigned char ZPAddr; - unsigned Addr; - Cycles = 6; - ZPAddr = MemReadByte (Regs.PC+1) + Regs.XR; - Addr = MemReadZPWord (ZPAddr); - MemWriteByte (Addr, Regs.AC); - Regs.PC += 2; + STO_OP (ZPXIND, Regs.AC); +} + + + +static void OPC_6502X_83 (void) +/* Opcode $83: SAX (zp,x) */ +{ + STO_OP (ZPXIND, Regs.AC & Regs.XR); } @@ -1495,11 +2594,7 @@ static void OPC_6502_81 (void) static void OPC_6502_84 (void) /* Opcode $84: STY zp */ { - unsigned char ZPAddr; - Cycles = 3; - ZPAddr = MemReadByte (Regs.PC+1); - MemWriteByte (ZPAddr, Regs.YR); - Regs.PC += 2; + STO_OP (ZP, Regs.YR); } @@ -1507,11 +2602,7 @@ static void OPC_6502_84 (void) static void OPC_6502_85 (void) /* Opcode $85: STA zp */ { - unsigned char ZPAddr; - Cycles = 3; - ZPAddr = MemReadByte (Regs.PC+1); - MemWriteByte (ZPAddr, Regs.AC); - Regs.PC += 2; + STO_OP (ZP, Regs.AC); } @@ -1519,11 +2610,23 @@ static void OPC_6502_85 (void) static void OPC_6502_86 (void) /* Opcode $86: STX zp */ { - unsigned char ZPAddr; - Cycles = 3; - ZPAddr = MemReadByte (Regs.PC+1); - MemWriteByte (ZPAddr, Regs.XR); - Regs.PC += 2; + STO_OP (ZP, Regs.XR); +} + + + +static void OPC_6502X_87 (void) +/* Opcode $87: SAX zp */ +{ + STO_OP (ZP, Regs.AC & Regs.XR); +} + + + +static void OPC_65C02_87 (void) +/* Opcode $87: SMB0 zp */ +{ + ZP_BITOP(0, 1); } @@ -1532,24 +2635,18 @@ static void OPC_6502_88 (void) /* Opcode $88: DEY */ { Cycles = 2; - Regs.YR = (Regs.YR - 1) & 0xFF; - TEST_ZF (Regs.YR); - TEST_SF (Regs.YR); + DEC (Regs.YR); Regs.PC += 1; } -static void OPC_65SC02_89 (void) +static void OPC_65C02_89 (void) /* Opcode $89: BIT #imm */ { - unsigned char Val; - Cycles = 2; - Val = MemReadByte (Regs.PC+1); - SET_SF (Val & 0x80); - SET_OF (Val & 0x40); - SET_ZF ((Val & Regs.AC) == 0); - Regs.PC += 2; + /* Note: BIT #imm behaves differently from BIT with other addressing modes, + * hence the different 'op' argument to the macro. */ + ALU_OP_IMM (BITIMM); } @@ -1566,14 +2663,18 @@ static void OPC_6502_8A (void) +static void OPC_6502X_8B (void) +/* Opcode $8B: ANE imm */ +{ + ALU_OP_IMM (ANE); +} + + + static void OPC_6502_8C (void) /* Opcode $8C: STY abs */ { - unsigned Addr; - Cycles = 4; - Addr = MemReadWord (Regs.PC+1); - MemWriteByte (Addr, Regs.YR); - Regs.PC += 3; + STO_OP (ABS, Regs.YR); } @@ -1581,11 +2682,7 @@ static void OPC_6502_8C (void) static void OPC_6502_8D (void) /* Opcode $8D: STA abs */ { - unsigned Addr; - Cycles = 4; - Addr = MemReadWord (Regs.PC+1); - MemWriteByte (Addr, Regs.AC); - Regs.PC += 3; + STO_OP (ABS, Regs.AC); } @@ -1593,11 +2690,23 @@ static void OPC_6502_8D (void) static void OPC_6502_8E (void) /* Opcode $8E: STX abs */ { - unsigned Addr; - Cycles = 4; - Addr = MemReadWord (Regs.PC+1); - MemWriteByte (Addr, Regs.XR); - Regs.PC += 3; + STO_OP (ABS, Regs.XR); +} + + + +static void OPC_6502X_8F (void) +/* Opcode $8F: SAX abs */ +{ + STO_OP (ABS, Regs.AC & Regs.XR); +} + + + +static void OPC_65C02_8F (void) +/* Opcode $8F: BBS0 zp, rel */ +{ + ZP_BIT_BRANCH (0, 1); } @@ -1613,27 +2722,36 @@ static void OPC_6502_90 (void) static void OPC_6502_91 (void) /* Opcode $91: sta (zp),y */ { - unsigned char ZPAddr; - unsigned Addr; - Cycles = 6; - ZPAddr = MemReadByte (Regs.PC+1); - Addr = MemReadZPWord (ZPAddr) + Regs.YR; - MemWriteByte (Addr, Regs.AC); - Regs.PC += 2; + STO_OP (ZPINDY_NP, Regs.AC); } -static void OPC_65SC02_92 (void) +static void OPC_65C02_92 (void) /* Opcode $92: sta (zp) */ { - unsigned char ZPAddr; - unsigned Addr; - Cycles = 5; - ZPAddr = MemReadByte (Regs.PC+1); - Addr = MemReadZPWord (ZPAddr); - MemWriteByte (Addr, Regs.AC); - Regs.PC += 2; + STO_OP (ZPIND, Regs.AC); +} + + + +static void OPC_6502X_93 (void) +/* Opcode $93: SHA (zp),y */ +{ + ++Regs.PC; + uint8_t zp_ptr_lo = MemReadByte(Regs.PC); + ++Regs.PC; + uint8_t zp_ptr_hi = zp_ptr_lo + 1; + uint8_t baselo = MemReadByte(zp_ptr_lo); + uint8_t basehi = MemReadByte(zp_ptr_hi); + uint8_t basehi_incremented = basehi + 1; + uint8_t write_value = Regs.AC & Regs.XR & basehi_incremented; + uint8_t write_address_lo = (baselo + Regs.YR); + bool pagecross = (baselo + Regs.YR) > 0xff; + uint8_t write_address_hi = pagecross ? write_value : basehi; + uint16_t write_address = write_address_lo + (write_address_hi << 8); + MemWriteByte(write_address, write_value); + Cycles=6; } @@ -1641,11 +2759,7 @@ static void OPC_65SC02_92 (void) static void OPC_6502_94 (void) /* Opcode $94: STY zp,x */ { - unsigned char ZPAddr; - Cycles = 4; - ZPAddr = MemReadByte (Regs.PC+1) + Regs.XR; - MemWriteByte (ZPAddr, Regs.YR); - Regs.PC += 2; + STO_OP (ZPX, Regs.YR); } @@ -1653,11 +2767,7 @@ static void OPC_6502_94 (void) static void OPC_6502_95 (void) /* Opcode $95: STA zp,x */ { - unsigned char ZPAddr; - Cycles = 4; - ZPAddr = MemReadByte (Regs.PC+1) + Regs.XR; - MemWriteByte (ZPAddr, Regs.AC); - Regs.PC += 2; + STO_OP (ZPX, Regs.AC); } @@ -1665,11 +2775,23 @@ static void OPC_6502_95 (void) static void OPC_6502_96 (void) /* Opcode $96: stx zp,y */ { - unsigned char ZPAddr; - Cycles = 4; - ZPAddr = MemReadByte (Regs.PC+1) + Regs.YR; - MemWriteByte (ZPAddr, Regs.XR); - Regs.PC += 2; + STO_OP (ZPY, Regs.XR); +} + + + +static void OPC_6502X_97 (void) +/* Opcode $97: SAX zp,y */ +{ + STO_OP (ZPY, Regs.AC & Regs.XR); +} + + + +static void OPC_65C02_97 (void) +/* Opcode $97: SMB1 zp */ +{ + ZP_BITOP(1, 1); } @@ -1689,11 +2811,7 @@ static void OPC_6502_98 (void) static void OPC_6502_99 (void) /* Opcode $99: STA abs,y */ { - unsigned Addr; - Cycles = 5; - Addr = MemReadWord (Regs.PC+1) + Regs.YR; - MemWriteByte (Addr, Regs.AC); - Regs.PC += 3; + STO_OP (ABSY_NP, Regs.AC); } @@ -1708,14 +2826,51 @@ static void OPC_6502_9A (void) -static void OPC_65SC02_9C (void) +static void OPC_6502X_9B (void) +/* Opcode $9B: TAS abs,y */ +{ + ++Regs.PC; + uint8_t baselo = MemReadByte(Regs.PC); + ++Regs.PC; + uint8_t basehi = MemReadByte(Regs.PC); + ++Regs.PC; + uint8_t basehi_incremented = basehi + 1; + uint8_t write_value = Regs.AC & Regs.XR & basehi_incremented; + uint8_t write_address_lo = (baselo + Regs.YR); + bool pagecross = (baselo + Regs.YR) > 0xff; + uint8_t write_address_hi = pagecross ? write_value : basehi; + uint16_t write_address = write_address_lo + (write_address_hi << 8); + MemWriteByte(write_address, write_value); + Regs.SP = Regs.AC & Regs.XR; + Cycles=5; +} + + + +static void OPC_6502X_9C (void) +/* Opcode $9D: SHY abs,x */ +{ + ++Regs.PC; + uint8_t baselo = MemReadByte(Regs.PC); + ++Regs.PC; + uint8_t basehi = MemReadByte(Regs.PC); + ++Regs.PC; + uint8_t basehi_incremented = basehi + 1; + uint8_t write_value = Regs.YR & basehi_incremented; + uint8_t write_address_lo = (baselo + Regs.XR); + bool pagecross = (baselo + Regs.XR) > 0xff; + uint8_t write_address_hi = pagecross ? write_value : basehi; + uint16_t write_address = write_address_lo + (write_address_hi << 8); + MemWriteByte(write_address, write_value); + Cycles=5; +} + + + +static void OPC_65C02_9C (void) /* Opcode $9C: STZ abs */ { - unsigned Addr; - Cycles = 4; - Addr = MemReadWord (Regs.PC+1); - MemWriteByte (Addr, 0); - Regs.PC += 3; + STO_OP (ABS, 0); } @@ -1723,23 +2878,63 @@ static void OPC_65SC02_9C (void) static void OPC_6502_9D (void) /* Opcode $9D: STA abs,x */ { - unsigned Addr; - Cycles = 5; - Addr = MemReadWord (Regs.PC+1) + Regs.XR; - MemWriteByte (Addr, Regs.AC); - Regs.PC += 3; + STO_OP (ABSX_NP, Regs.AC); } -static void OPC_65SC02_9E (void) +static void OPC_6502X_9E (void) +/* Opcode $9E: SHX abs,x */ +{ + ++Regs.PC; + uint8_t baselo = MemReadByte(Regs.PC); + ++Regs.PC; + uint8_t basehi = MemReadByte(Regs.PC); + ++Regs.PC; + uint8_t basehi_incremented = basehi + 1; + uint8_t write_value = Regs.XR & basehi_incremented; + uint8_t write_address_lo = (baselo + Regs.YR); + bool pagecross = (baselo + Regs.YR) > 0xff; + uint8_t write_address_hi = pagecross ? write_value : basehi; + uint16_t write_address = write_address_lo + (write_address_hi << 8); + MemWriteByte(write_address, write_value); + Cycles=5; +} + + + +static void OPC_65C02_9E (void) /* Opcode $9E: STZ abs,x */ { - unsigned Addr; - Cycles = 5; - Addr = MemReadWord (Regs.PC+1) + Regs.XR; - MemWriteByte (Addr, 0); - Regs.PC += 3; + STO_OP (ABSX_NP, 0); +} + + + +static void OPC_6502X_9F (void) +/* Opcode $9F: SHA abs,y */ +{ + ++Regs.PC; + uint8_t baselo = MemReadByte(Regs.PC); + ++Regs.PC; + uint8_t basehi = MemReadByte(Regs.PC); + ++Regs.PC; + uint8_t basehi_incremented = basehi + 1; + uint8_t write_value = Regs.AC & Regs.XR & basehi_incremented; + uint8_t write_address_lo = (baselo + Regs.YR); + bool pagecross = (baselo + Regs.YR) > 0xff; + uint8_t write_address_hi = pagecross ? write_value : basehi; + uint16_t write_address = write_address_lo + (write_address_hi << 8); + MemWriteByte(write_address, write_value); + Cycles=5; +} + + + +static void OPC_65C02_9F (void) +/* Opcode $9F: BBS1 zp, rel */ +{ + ZP_BIT_BRANCH (1, 1); } @@ -1747,11 +2942,7 @@ static void OPC_65SC02_9E (void) static void OPC_6502_A0 (void) /* Opcode $A0: LDY #imm */ { - Cycles = 2; - Regs.YR = MemReadByte (Regs.PC+1); - TEST_ZF (Regs.YR); - TEST_SF (Regs.YR); - Regs.PC += 2; + ALU_OP_IMM (LDY); } @@ -1759,15 +2950,7 @@ static void OPC_6502_A0 (void) static void OPC_6502_A1 (void) /* Opcode $A1: LDA (zp,x) */ { - unsigned char ZPAddr; - unsigned Addr; - Cycles = 6; - ZPAddr = MemReadByte (Regs.PC+1) + Regs.XR; - Addr = MemReadZPWord (ZPAddr); - Regs.AC = MemReadByte (Addr); - TEST_ZF (Regs.AC); - TEST_SF (Regs.AC); - Regs.PC += 2; + ALU_OP (ZPXIND, LDA); } @@ -1775,11 +2958,15 @@ static void OPC_6502_A1 (void) static void OPC_6502_A2 (void) /* Opcode $A2: LDX #imm */ { - Cycles = 2; - Regs.XR = MemReadByte (Regs.PC+1); - TEST_ZF (Regs.XR); - TEST_SF (Regs.XR); - Regs.PC += 2; + ALU_OP_IMM (LDX); +} + + + +static void OPC_6502X_A3 (void) +/* Opcode $A3: LAX (zp,x) */ +{ + ALU_OP (ZPXIND, LAX); } @@ -1787,13 +2974,7 @@ static void OPC_6502_A2 (void) static void OPC_6502_A4 (void) /* Opcode $A4: LDY zp */ { - unsigned char ZPAddr; - Cycles = 3; - ZPAddr = MemReadByte (Regs.PC+1); - Regs.YR = MemReadByte (ZPAddr); - TEST_ZF (Regs.YR); - TEST_SF (Regs.YR); - Regs.PC += 2; + ALU_OP (ZP, LDY); } @@ -1801,13 +2982,7 @@ static void OPC_6502_A4 (void) static void OPC_6502_A5 (void) /* Opcode $A5: LDA zp */ { - unsigned char ZPAddr; - Cycles = 3; - ZPAddr = MemReadByte (Regs.PC+1); - Regs.AC = MemReadByte (ZPAddr); - TEST_ZF (Regs.AC); - TEST_SF (Regs.AC); - Regs.PC += 2; + ALU_OP (ZP, LDA); } @@ -1815,13 +2990,23 @@ static void OPC_6502_A5 (void) static void OPC_6502_A6 (void) /* Opcode $A6: LDX zp */ { - unsigned char ZPAddr; - Cycles = 3; - ZPAddr = MemReadByte (Regs.PC+1); - Regs.XR = MemReadByte (ZPAddr); - TEST_ZF (Regs.XR); - TEST_SF (Regs.XR); - Regs.PC += 2; + ALU_OP (ZP, LDX); +} + + + +static void OPC_6502X_A7 (void) +/* Opcode $A7: LAX zp */ +{ + ALU_OP (ZP, LAX); +} + + + +static void OPC_65C02_A7 (void) +/* Opcode $A7: SMB2 zp */ +{ + ZP_BITOP(2, 1); } @@ -1841,11 +3026,7 @@ static void OPC_6502_A8 (void) static void OPC_6502_A9 (void) /* Opcode $A9: LDA #imm */ { - Cycles = 2; - Regs.AC = MemReadByte (Regs.PC+1); - TEST_ZF (Regs.AC); - TEST_SF (Regs.AC); - Regs.PC += 2; + ALU_OP_IMM (LDA); } @@ -1862,16 +3043,18 @@ static void OPC_6502_AA (void) +static void OPC_6502X_AB (void) +/* Opcode $AB: LXA imm */ +{ + ALU_OP_IMM (LXA); +} + + + static void OPC_6502_AC (void) /* Opcode $Regs.AC: LDY abs */ { - unsigned Addr; - Cycles = 4; - Addr = MemReadWord (Regs.PC+1); - Regs.YR = MemReadByte (Addr); - TEST_ZF (Regs.YR); - TEST_SF (Regs.YR); - Regs.PC += 3; + ALU_OP (ABS, LDY); } @@ -1879,13 +3062,7 @@ static void OPC_6502_AC (void) static void OPC_6502_AD (void) /* Opcode $AD: LDA abs */ { - unsigned Addr; - Cycles = 4; - Addr = MemReadWord (Regs.PC+1); - Regs.AC = MemReadByte (Addr); - TEST_ZF (Regs.AC); - TEST_SF (Regs.AC); - Regs.PC += 3; + ALU_OP (ABS, LDA); } @@ -1893,13 +3070,23 @@ static void OPC_6502_AD (void) static void OPC_6502_AE (void) /* Opcode $AE: LDX abs */ { - unsigned Addr; - Cycles = 4; - Addr = MemReadWord (Regs.PC+1); - Regs.XR = MemReadByte (Addr); - TEST_ZF (Regs.XR); - TEST_SF (Regs.XR); - Regs.PC += 3; + ALU_OP (ABS, LDX); +} + + + +static void OPC_6502X_AF (void) +/* Opcode $AF: LAX abs */ +{ + ALU_OP (ABS, LAX); +} + + + +static void OPC_65C02_AF (void) +/* Opcode $AF: BBS2 zp, rel */ +{ + ZP_BIT_BRANCH (2, 1); } @@ -1915,34 +3102,23 @@ static void OPC_6502_B0 (void) static void OPC_6502_B1 (void) /* Opcode $B1: LDA (zp),y */ { - unsigned char ZPAddr; - unsigned Addr; - Cycles = 5; - ZPAddr = MemReadByte (Regs.PC+1); - Addr = MemReadZPWord (ZPAddr); - if (PAGE_CROSS (Addr, Regs.YR)) { - ++Cycles; - } - Regs.AC = MemReadByte (Addr + Regs.YR); - TEST_ZF (Regs.AC); - TEST_SF (Regs.AC); - Regs.PC += 2; + ALU_OP (ZPINDY, LDA); } -static void OPC_65SC02_B2 (void) +static void OPC_65C02_B2 (void) /* Opcode $B2: LDA (zp) */ { - unsigned char ZPAddr; - unsigned Addr; - Cycles = 5; - ZPAddr = MemReadByte (Regs.PC+1); - Addr = MemReadZPWord (ZPAddr); - Regs.AC = MemReadByte (Addr); - TEST_ZF (Regs.AC); - TEST_SF (Regs.AC); - Regs.PC += 2; + ALU_OP (ZPIND, LDA); +} + + + +static void OPC_6502X_B3 (void) +/* Opcode $B3: LAX (zp),y */ +{ + ALU_OP (ZPINDY, LAX); } @@ -1950,13 +3126,7 @@ static void OPC_65SC02_B2 (void) static void OPC_6502_B4 (void) /* Opcode $B4: LDY zp,x */ { - unsigned char ZPAddr; - Cycles = 4; - ZPAddr = MemReadByte (Regs.PC+1) + Regs.XR; - Regs.YR = MemReadByte (ZPAddr); - TEST_ZF (Regs.YR); - TEST_SF (Regs.YR); - Regs.PC += 2; + ALU_OP (ZPX, LDY); } @@ -1964,13 +3134,7 @@ static void OPC_6502_B4 (void) static void OPC_6502_B5 (void) /* Opcode $B5: LDA zp,x */ { - unsigned char ZPAddr; - Cycles = 4; - ZPAddr = MemReadByte (Regs.PC+1) + Regs.XR; - Regs.AC = MemReadByte (ZPAddr); - TEST_ZF (Regs.AC); - TEST_SF (Regs.AC); - Regs.PC += 2; + ALU_OP (ZPX, LDA); } @@ -1978,13 +3142,23 @@ static void OPC_6502_B5 (void) static void OPC_6502_B6 (void) /* Opcode $B6: LDX zp,y */ { - unsigned char ZPAddr; - Cycles = 4; - ZPAddr = MemReadByte (Regs.PC+1) + Regs.YR; - Regs.XR = MemReadByte (ZPAddr); - TEST_ZF (Regs.XR); - TEST_SF (Regs.XR); - Regs.PC += 2; + ALU_OP (ZPY, LDX); +} + + + +static void OPC_6502X_B7 (void) +/* Opcode $B7: LAX zp,y */ +{ + ALU_OP (ZPY, LAX); +} + + + +static void OPC_65C02_B7 (void) +/* Opcode $B7: SMB3 zp */ +{ + ZP_BITOP(3, 1); } @@ -2002,16 +3176,7 @@ static void OPC_6502_B8 (void) static void OPC_6502_B9 (void) /* Opcode $B9: LDA abs,y */ { - unsigned Addr; - Cycles = 4; - Addr = MemReadWord (Regs.PC+1); - if (PAGE_CROSS (Addr, Regs.YR)) { - ++Cycles; - } - Regs.AC = MemReadByte (Addr + Regs.YR); - TEST_ZF (Regs.AC); - TEST_SF (Regs.AC); - Regs.PC += 3; + ALU_OP (ABSY, LDA); } @@ -2028,19 +3193,18 @@ static void OPC_6502_BA (void) +static void OPC_6502X_BB (void) +/* Opcode $BB: LAS abs,y */ +{ + ALU_OP (ABSY, LAS); +} + + + static void OPC_6502_BC (void) /* Opcode $BC: LDY abs,x */ { - unsigned Addr; - Cycles = 4; - Addr = MemReadWord (Regs.PC+1); - if (PAGE_CROSS (Addr, Regs.XR)) { - ++Cycles; - } - Regs.YR = MemReadByte (Addr + Regs.XR); - TEST_ZF (Regs.YR); - TEST_SF (Regs.YR); - Regs.PC += 3; + ALU_OP (ABSX, LDY); } @@ -2048,16 +3212,7 @@ static void OPC_6502_BC (void) static void OPC_6502_BD (void) /* Opcode $BD: LDA abs,x */ { - unsigned Addr; - Cycles = 4; - Addr = MemReadWord (Regs.PC+1); - if (PAGE_CROSS (Addr, Regs.XR)) { - ++Cycles; - } - Regs.AC = MemReadByte (Addr + Regs.XR); - TEST_ZF (Regs.AC); - TEST_SF (Regs.AC); - Regs.PC += 3; + ALU_OP (ABSX, LDA); } @@ -2065,16 +3220,23 @@ static void OPC_6502_BD (void) static void OPC_6502_BE (void) /* Opcode $BE: LDX abs,y */ { - unsigned Addr; - Cycles = 4; - Addr = MemReadWord (Regs.PC+1); - if (PAGE_CROSS (Addr, Regs.YR)) { - ++Cycles; - } - Regs.XR = MemReadByte (Addr + Regs.YR); - TEST_ZF (Regs.XR); - TEST_SF (Regs.XR); - Regs.PC += 3; + ALU_OP (ABSY, LDX); +} + + + +static void OPC_6502X_BF (void) +/* Opcode $BF: LAX abs,y */ +{ + ALU_OP (ABSY, LAX); +} + + + +static void OPC_65C02_BF (void) +/* Opcode $BF: BBS3 zp, rel */ +{ + ZP_BIT_BRANCH (3, 1); } @@ -2082,9 +3244,7 @@ static void OPC_6502_BE (void) static void OPC_6502_C0 (void) /* Opcode $C0: CPY #imm */ { - Cycles = 2; - CMP (Regs.YR, MemReadByte (Regs.PC+1)); - Regs.PC += 2; + ALU_OP_IMM (CPY); } @@ -2092,13 +3252,15 @@ static void OPC_6502_C0 (void) static void OPC_6502_C1 (void) /* Opcode $C1: CMP (zp,x) */ { - unsigned char ZPAddr; - unsigned Addr; - Cycles = 6; - ZPAddr = MemReadByte (Regs.PC+1) + Regs.XR; - Addr = MemReadZPWord (ZPAddr); - CMP (Regs.AC, MemReadByte (Addr)); - Regs.PC += 2; + ALU_OP (ZPXIND, CMP); +} + + + +static void OPC_6502X_C3 (void) +/* Opcode $C3: DCP (zp,x) */ +{ + MEM_OP (ZPXIND, DCP); } @@ -2106,11 +3268,7 @@ static void OPC_6502_C1 (void) static void OPC_6502_C4 (void) /* Opcode $C4: CPY zp */ { - unsigned char ZPAddr; - Cycles = 3; - ZPAddr = MemReadByte (Regs.PC+1); - CMP (Regs.YR, MemReadByte (ZPAddr)); - Regs.PC += 2; + ALU_OP (ZP, CPY); } @@ -2118,11 +3276,7 @@ static void OPC_6502_C4 (void) static void OPC_6502_C5 (void) /* Opcode $C5: CMP zp */ { - unsigned char ZPAddr; - Cycles = 3; - ZPAddr = MemReadByte (Regs.PC+1); - CMP (Regs.AC, MemReadByte (ZPAddr)); - Regs.PC += 2; + ALU_OP (ZP, CMP); } @@ -2130,15 +3284,23 @@ static void OPC_6502_C5 (void) static void OPC_6502_C6 (void) /* Opcode $C6: DEC zp */ { - unsigned char ZPAddr; - unsigned char Val; - Cycles = 5; - ZPAddr = MemReadByte (Regs.PC+1); - Val = MemReadByte (ZPAddr) - 1; - MemWriteByte (ZPAddr, Val); - TEST_ZF (Val); - TEST_SF (Val); - Regs.PC += 2; + MEM_OP (ZP, DEC); +} + + + +static void OPC_6502X_C7 (void) +/* Opcode $C7: DCP zp */ +{ + MEM_OP (ZP, DCP); +} + + + +static void OPC_65C02_C7 (void) +/* Opcode $C7: SMB4 zp */ +{ + ZP_BITOP(4, 1); } @@ -2147,9 +3309,7 @@ static void OPC_6502_C8 (void) /* Opcode $C8: INY */ { Cycles = 2; - Regs.YR = (Regs.YR + 1) & 0xFF; - TEST_ZF (Regs.YR); - TEST_SF (Regs.YR); + INC(Regs.YR); Regs.PC += 1; } @@ -2158,9 +3318,7 @@ static void OPC_6502_C8 (void) static void OPC_6502_C9 (void) /* Opcode $C9: CMP #imm */ { - Cycles = 2; - CMP (Regs.AC, MemReadByte (Regs.PC+1)); - Regs.PC += 2; + ALU_OP_IMM (CMP); } @@ -2169,22 +3327,24 @@ static void OPC_6502_CA (void) /* Opcode $CA: DEX */ { Cycles = 2; - Regs.XR = (Regs.XR - 1) & 0xFF; - TEST_ZF (Regs.XR); - TEST_SF (Regs.XR); + DEC (Regs.XR); Regs.PC += 1; } +static void OPC_6502X_CB (void) +/* Opcode $CB: SBX imm */ +{ + ALU_OP_IMM (SBX); +} + + + static void OPC_6502_CC (void) /* Opcode $CC: CPY abs */ { - unsigned Addr; - Cycles = 4; - Addr = MemReadWord (Regs.PC+1); - CMP (Regs.YR, MemReadByte (Addr)); - Regs.PC += 3; + ALU_OP (ABS, CPY); } @@ -2192,11 +3352,7 @@ static void OPC_6502_CC (void) static void OPC_6502_CD (void) /* Opcode $CD: CMP abs */ { - unsigned Addr; - Cycles = 4; - Addr = MemReadWord (Regs.PC+1); - CMP (Regs.AC, MemReadByte (Addr)); - Regs.PC += 3; + ALU_OP (ABS, CMP); } @@ -2204,15 +3360,23 @@ static void OPC_6502_CD (void) static void OPC_6502_CE (void) /* Opcode $CE: DEC abs */ { - unsigned Addr; - unsigned char Val; - Cycles = 6; - Addr = MemReadWord (Regs.PC+1); - Val = MemReadByte (Addr) - 1; - MemWriteByte (Addr, Val); - TEST_ZF (Val); - TEST_SF (Val); - Regs.PC += 3; + MEM_OP (ABS, DEC); +} + + + +static void OPC_6502X_CF (void) +/* Opcode $CF: DCP abs */ +{ + MEM_OP (ABS, DCP); +} + + + +static void OPC_65C02_CF (void) +/* Opcode $CF: BBS4 zp, rel */ +{ + ZP_BIT_BRANCH (4, 1); } @@ -2228,30 +3392,23 @@ static void OPC_6502_D0 (void) static void OPC_6502_D1 (void) /* Opcode $D1: CMP (zp),y */ { - unsigned ZPAddr; - unsigned Addr; - Cycles = 5; - ZPAddr = MemReadByte (Regs.PC+1); - Addr = MemReadWord (ZPAddr); - if (PAGE_CROSS (Addr, Regs.YR)) { - ++Cycles; - } - CMP (Regs.AC, MemReadByte (Addr + Regs.YR)); - Regs.PC += 2; + ALU_OP (ZPINDY, CMP); } -static void OPC_65SC02_D2 (void) +static void OPC_65C02_D2 (void) /* Opcode $D2: CMP (zp) */ { - unsigned ZPAddr; - unsigned Addr; - Cycles = 5; - ZPAddr = MemReadByte (Regs.PC+1); - Addr = MemReadWord (ZPAddr); - CMP (Regs.AC, MemReadByte (Addr)); - Regs.PC += 2; + ALU_OP (ZPIND, CMP); +} + + + +static void OPC_6502X_D3 (void) +/* Opcode $D3: DCP (zp),y */ +{ + MEM_OP (ZPINDY_NP, DCP); } @@ -2259,11 +3416,7 @@ static void OPC_65SC02_D2 (void) static void OPC_6502_D5 (void) /* Opcode $D5: CMP zp,x */ { - unsigned char ZPAddr; - Cycles = 4; - ZPAddr = MemReadByte (Regs.PC+1) + Regs.XR; - CMP (Regs.AC, MemReadByte (ZPAddr)); - Regs.PC += 2; + ALU_OP (ZPX, CMP); } @@ -2271,15 +3424,23 @@ static void OPC_6502_D5 (void) static void OPC_6502_D6 (void) /* Opcode $D6: DEC zp,x */ { - unsigned char ZPAddr; - unsigned char Val; - Cycles = 6; - ZPAddr = MemReadByte (Regs.PC+1) + Regs.XR; - Val = MemReadByte (ZPAddr) - 1; - MemWriteByte (ZPAddr, Val); - TEST_ZF (Val); - TEST_SF (Val); - Regs.PC += 2; + MEM_OP (ZPX, DEC); +} + + + +static void OPC_6502X_D7 (void) +/* Opcode $D7: DCP zp,x */ +{ + MEM_OP (ZPX, DCP); +} + + + +static void OPC_65C02_D7 (void) +/* Opcode $D7: SMB5 zp */ +{ + ZP_BITOP(5, 1); } @@ -2297,19 +3458,12 @@ static void OPC_6502_D8 (void) static void OPC_6502_D9 (void) /* Opcode $D9: CMP abs,y */ { - unsigned Addr; - Cycles = 4; - Addr = MemReadWord (Regs.PC+1); - if (PAGE_CROSS (Addr, Regs.YR)) { - ++Cycles; - } - CMP (Regs.AC, MemReadByte (Addr + Regs.YR)); - Regs.PC += 3; + ALU_OP (ABSY, CMP); } -static void OPC_65SC02_DA (void) +static void OPC_65C02_DA (void) /* Opcode $DA: PHX */ { Cycles = 3; @@ -2319,17 +3473,18 @@ static void OPC_65SC02_DA (void) +static void OPC_6502X_DB (void) +/* Opcode $DB: DCP abs,y */ +{ + MEM_OP (ABSY_NP, DCP); +} + + + static void OPC_6502_DD (void) /* Opcode $DD: CMP abs,x */ { - unsigned Addr; - Cycles = 4; - Addr = MemReadWord (Regs.PC+1); - if (PAGE_CROSS (Addr, Regs.XR)) { - ++Cycles; - } - CMP (Regs.AC, MemReadByte (Addr + Regs.XR)); - Regs.PC += 3; + ALU_OP (ABSX, CMP); } @@ -2337,15 +3492,23 @@ static void OPC_6502_DD (void) static void OPC_6502_DE (void) /* Opcode $DE: DEC abs,x */ { - unsigned Addr; - unsigned char Val; - Cycles = 7; - Addr = MemReadWord (Regs.PC+1) + Regs.XR; - Val = MemReadByte (Addr) - 1; - MemWriteByte (Addr, Val); - TEST_ZF (Val); - TEST_SF (Val); - Regs.PC += 3; + MEM_OP (ABSX_NP, DEC); +} + + + +static void OPC_6502X_DF (void) +/* Opcode $DF: DCP abs,x */ +{ + MEM_OP (ABSX_NP, DCP); +} + + + +static void OPC_65C02_DF (void) +/* Opcode $DF: BBS5 zp, rel */ +{ + ZP_BIT_BRANCH (5, 1); } @@ -2353,9 +3516,7 @@ static void OPC_6502_DE (void) static void OPC_6502_E0 (void) /* Opcode $E0: CPX #imm */ { - Cycles = 2; - CMP (Regs.XR, MemReadByte (Regs.PC+1)); - Regs.PC += 2; + ALU_OP_IMM (CPX); } @@ -2363,13 +3524,23 @@ static void OPC_6502_E0 (void) static void OPC_6502_E1 (void) /* Opcode $E1: SBC (zp,x) */ { - unsigned char ZPAddr; - unsigned Addr; - Cycles = 6; - ZPAddr = MemReadByte (Regs.PC+1) + Regs.XR; - Addr = MemReadZPWord (ZPAddr); - SBC (MemReadByte (Addr)); - Regs.PC += 2; + ALU_OP (ZPXIND, SBC_6502); +} + + + +static void OPC_65C02_E1 (void) +/* Opcode $E1: SBC (zp,x) */ +{ + ALU_OP (ZPXIND, SBC_65C02); +} + + + +static void OPC_6502X_E3 (void) +/* Opcode $E3: ISC (zp,x) */ +{ + MEM_OP (ZPXIND, ISC); } @@ -2377,11 +3548,7 @@ static void OPC_6502_E1 (void) static void OPC_6502_E4 (void) /* Opcode $E4: CPX zp */ { - unsigned char ZPAddr; - Cycles = 3; - ZPAddr = MemReadByte (Regs.PC+1); - CMP (Regs.XR, MemReadByte (ZPAddr)); - Regs.PC += 2; + ALU_OP (ZP, CPX); } @@ -2389,11 +3556,15 @@ static void OPC_6502_E4 (void) static void OPC_6502_E5 (void) /* Opcode $E5: SBC zp */ { - unsigned char ZPAddr; - Cycles = 3; - ZPAddr = MemReadByte (Regs.PC+1); - SBC (MemReadByte (ZPAddr)); - Regs.PC += 2; + ALU_OP (ZP, SBC_6502); +} + + + +static void OPC_65C02_E5 (void) +/* Opcode $E5: SBC zp */ +{ + ALU_OP (ZP, SBC_65C02); } @@ -2401,15 +3572,23 @@ static void OPC_6502_E5 (void) static void OPC_6502_E6 (void) /* Opcode $E6: INC zp */ { - unsigned char ZPAddr; - unsigned char Val; - Cycles = 5; - ZPAddr = MemReadByte (Regs.PC+1); - Val = MemReadByte (ZPAddr) + 1; - MemWriteByte (ZPAddr, Val); - TEST_ZF (Val); - TEST_SF (Val); - Regs.PC += 2; + MEM_OP (ZP, INC); +} + + + +static void OPC_6502X_E7 (void) +/* Opcode $E7: ISC zp */ +{ + MEM_OP (ZP, ISC); +} + + + +static void OPC_65C02_E7 (void) +/* Opcode $E7: SMB6 zp */ +{ + ZP_BITOP(6, 1); } @@ -2418,24 +3597,37 @@ static void OPC_6502_E8 (void) /* Opcode $E8: INX */ { Cycles = 2; - Regs.XR = (Regs.XR + 1) & 0xFF; - TEST_ZF (Regs.XR); - TEST_SF (Regs.XR); + INC (Regs.XR); Regs.PC += 1; } +/* Aliases of opcode $E9 */ +#define OPC_6502X_EB OPC_6502_E9 + static void OPC_6502_E9 (void) /* Opcode $E9: SBC #imm */ { - Cycles = 2; - SBC (MemReadByte (Regs.PC+1)); - Regs.PC += 2; + ALU_OP_IMM (SBC_6502); } +static void OPC_65C02_E9 (void) +/* Opcode $E9: SBC #imm */ +{ + ALU_OP_IMM (SBC_65C02); +} + +/* Aliases of opcode $EA */ +#define OPC_6502X_1A OPC_6502_EA +#define OPC_6502X_3A OPC_6502_EA +#define OPC_6502X_5A OPC_6502_EA +#define OPC_6502X_7A OPC_6502_EA +#define OPC_6502X_DA OPC_6502_EA +#define OPC_6502X_FA OPC_6502_EA + static void OPC_6502_EA (void) /* Opcode $EA: NOP */ { @@ -2485,11 +3677,7 @@ static void OPC_65C02_NOP34 (void) static void OPC_6502_EC (void) /* Opcode $EC: CPX abs */ { - unsigned Addr; - Cycles = 4; - Addr = MemReadWord (Regs.PC+1); - CMP (Regs.XR, MemReadByte (Addr)); - Regs.PC += 3; + ALU_OP (ABS, CPX); } @@ -2497,27 +3685,38 @@ static void OPC_6502_EC (void) static void OPC_6502_ED (void) /* Opcode $ED: SBC abs */ { - unsigned Addr; - Cycles = 4; - Addr = MemReadWord (Regs.PC+1); - SBC (MemReadByte (Addr)); - Regs.PC += 3; + ALU_OP (ABS, SBC_6502); } +static void OPC_65C02_ED (void) +/* Opcode $ED: SBC abs */ +{ + ALU_OP (ABS, SBC_65C02); +} + + static void OPC_6502_EE (void) /* Opcode $EE: INC abs */ { - unsigned Addr; - unsigned char Val; - Cycles = 6; - Addr = MemReadWord (Regs.PC+1); - Val = MemReadByte (Addr) + 1; - MemWriteByte (Addr, Val); - TEST_ZF (Val); - TEST_SF (Val); - Regs.PC += 3; + MEM_OP (ABS, INC); +} + + + +static void OPC_6502X_EF (void) +/* Opcode $EF: ISC abs */ +{ + MEM_OP (ABS, ISC); +} + + + +static void OPC_65C02_EF (void) +/* Opcode $EF: BBS6 zp, rel */ +{ + ZP_BIT_BRANCH (6, 1); } @@ -2533,30 +3732,31 @@ static void OPC_6502_F0 (void) static void OPC_6502_F1 (void) /* Opcode $F1: SBC (zp),y */ { - unsigned char ZPAddr; - unsigned Addr; - Cycles = 5; - ZPAddr = MemReadByte (Regs.PC+1); - Addr = MemReadZPWord (ZPAddr); - if (PAGE_CROSS (Addr, Regs.YR)) { - ++Cycles; - } - SBC (MemReadByte (Addr + Regs.YR)); - Regs.PC += 2; + ALU_OP (ZPINDY, SBC_6502); } -static void OPC_65SC02_F2 (void) +static void OPC_65C02_F1 (void) +/* Opcode $F1: SBC (zp),y */ +{ + ALU_OP (ZPINDY, SBC_65C02); +} + + + +static void OPC_65C02_F2 (void) /* Opcode $F2: SBC (zp) */ { - unsigned char ZPAddr; - unsigned Addr; - Cycles = 5; - ZPAddr = MemReadByte (Regs.PC+1); - Addr = MemReadZPWord (ZPAddr); - SBC (MemReadByte (Addr)); - Regs.PC += 2; + ALU_OP (ZPIND, SBC_65C02); +} + + + +static void OPC_6502X_F3 (void) +/* Opcode $F3: ISC (zp),y */ +{ + MEM_OP (ZPINDY_NP, ISC); } @@ -2564,11 +3764,15 @@ static void OPC_65SC02_F2 (void) static void OPC_6502_F5 (void) /* Opcode $F5: SBC zp,x */ { - unsigned char ZPAddr; - Cycles = 4; - ZPAddr = MemReadByte (Regs.PC+1) + Regs.XR; - SBC (MemReadByte (ZPAddr)); - Regs.PC += 2; + ALU_OP (ZPX, SBC_6502); +} + + + +static void OPC_65C02_F5 (void) +/* Opcode $F5: SBC zp,x */ +{ + ALU_OP (ZPX, SBC_65C02); } @@ -2576,15 +3780,23 @@ static void OPC_6502_F5 (void) static void OPC_6502_F6 (void) /* Opcode $F6: INC zp,x */ { - unsigned char ZPAddr; - unsigned char Val; - Cycles = 6; - ZPAddr = MemReadByte (Regs.PC+1) + Regs.XR; - Val = MemReadByte (ZPAddr) + 1; - MemWriteByte (ZPAddr, Val); - TEST_ZF (Val); - TEST_SF (Val); - Regs.PC += 2; + MEM_OP (ZPX, INC); +} + + + +static void OPC_6502X_F7 (void) +/* Opcode $F7: ISC zp,x */ +{ + MEM_OP (ZPX, ISC); +} + + + +static void OPC_65C02_F7 (void) +/* Opcode $F7: SMB7 zp */ +{ + ZP_BITOP(7, 1); } @@ -2602,19 +3814,20 @@ static void OPC_6502_F8 (void) static void OPC_6502_F9 (void) /* Opcode $F9: SBC abs,y */ { - unsigned Addr; - Cycles = 4; - Addr = MemReadWord (Regs.PC+1); - if (PAGE_CROSS (Addr, Regs.YR)) { - ++Cycles; - } - SBC (MemReadByte (Addr + Regs.YR)); - Regs.PC += 3; + ALU_OP (ABSY, SBC_6502); } -static void OPC_65SC02_FA (void) +static void OPC_65C02_F9 (void) +/* Opcode $F9: SBC abs,y */ +{ + ALU_OP (ABSY, SBC_65C02); +} + + + +static void OPC_65C02_FA (void) /* Opcode $7A: PLX */ { Cycles = 4; @@ -2626,17 +3839,26 @@ static void OPC_65SC02_FA (void) +static void OPC_6502X_FB (void) +/* Opcode $FB: ISC abs,y */ +{ + MEM_OP (ABSY_NP, ISC); +} + + + static void OPC_6502_FD (void) /* Opcode $FD: SBC abs,x */ { - unsigned Addr; - Cycles = 4; - Addr = MemReadWord (Regs.PC+1); - if (PAGE_CROSS (Addr, Regs.XR)) { - ++Cycles; - } - SBC (MemReadByte (Addr + Regs.XR)); - Regs.PC += 3; + ALU_OP (ABSX, SBC_6502); +} + + + +static void OPC_65C02_FD (void) +/* Opcode $FD: SBC abs,x */ +{ + ALU_OP (ABSX, SBC_65C02); } @@ -2644,15 +3866,23 @@ static void OPC_6502_FD (void) static void OPC_6502_FE (void) /* Opcode $FE: INC abs,x */ { - unsigned Addr; - unsigned char Val; - Cycles = 7; - Addr = MemReadWord (Regs.PC+1) + Regs.XR; - Val = MemReadByte (Addr) + 1; - MemWriteByte (Addr, Val); - TEST_ZF (Val); - TEST_SF (Val); - Regs.PC += 3; + MEM_OP (ABSX_NP, INC); +} + + + +static void OPC_6502X_FF (void) +/* Opcode $FF: ISC abs,x */ +{ + MEM_OP (ABSX_NP, ISC); +} + + + +static void OPC_65C02_FF (void) +/* Opcode $FF: BBS7 zp, rel */ +{ + ZP_BIT_BRANCH (7, 1); } @@ -2925,40 +4155,302 @@ static const OPFunc OP6502Table[256] = { +/* Opcode handler table for the 6502X */ +static const OPFunc OP6502XTable[256] = { + OPC_6502_00, + OPC_6502_01, + OPC_Illegal, + OPC_6502X_03, + OPC_6502X_04, + OPC_6502_05, + OPC_6502_06, + OPC_6502X_07, + OPC_6502_08, + OPC_6502_09, + OPC_6502_0A, + OPC_6502X_0B, + OPC_6502X_0C, + OPC_6502_0D, + OPC_6502_0E, + OPC_6502X_0F, + OPC_6502_10, + OPC_6502_11, + OPC_Illegal, + OPC_6502X_13, + OPC_6502X_14, + OPC_6502_15, + OPC_6502_16, + OPC_6502X_17, + OPC_6502_18, + OPC_6502_19, + OPC_6502X_1A, + OPC_6502X_1B, + OPC_6502X_1C, + OPC_6502_1D, + OPC_6502_1E, + OPC_6502X_1F, + OPC_6502_20, + OPC_6502_21, + OPC_Illegal, + OPC_6502X_23, + OPC_6502_24, + OPC_6502_25, + OPC_6502_26, + OPC_6502X_27, + OPC_6502_28, + OPC_6502_29, + OPC_6502_2A, + OPC_6502X_2B, + OPC_6502_2C, + OPC_6502_2D, + OPC_6502_2E, + OPC_6502X_2F, + OPC_6502_30, + OPC_6502_31, + OPC_Illegal, + OPC_6502X_33, + OPC_6502X_34, + OPC_6502_35, + OPC_6502_36, + OPC_6502X_37, + OPC_6502_38, + OPC_6502_39, + OPC_6502X_3A, + OPC_6502X_3B, + OPC_6502X_3C, + OPC_6502_3D, + OPC_6502_3E, + OPC_6502X_3F, + OPC_6502_40, + OPC_6502_41, + OPC_Illegal, + OPC_6502X_43, + OPC_6502X_44, + OPC_6502_45, + OPC_6502_46, + OPC_6502X_47, + OPC_6502_48, + OPC_6502_49, + OPC_6502_4A, + OPC_6502X_4B, + OPC_6502_4C, + OPC_6502_4D, + OPC_6502_4E, + OPC_6502X_4F, + OPC_6502_50, + OPC_6502_51, + OPC_Illegal, + OPC_6502X_53, + OPC_6502X_54, + OPC_6502_55, + OPC_6502_56, + OPC_6502X_57, + OPC_6502_58, + OPC_6502_59, + OPC_6502X_5A, + OPC_6502X_5B, + OPC_6502X_5C, + OPC_6502_5D, + OPC_6502_5E, + OPC_6502X_5F, + OPC_6502_60, + OPC_6502_61, + OPC_Illegal, + OPC_6502X_63, + OPC_6502X_64, + OPC_6502_65, + OPC_6502_66, + OPC_6502X_67, + OPC_6502_68, + OPC_6502_69, + OPC_6502_6A, + OPC_6502X_6B, + OPC_6502_6C, + OPC_6502_6D, + OPC_6502_6E, + OPC_6502X_6F, + OPC_6502_70, + OPC_6502_71, + OPC_Illegal, + OPC_6502X_73, + OPC_6502X_74, + OPC_6502_75, + OPC_6502_76, + OPC_6502X_77, + OPC_6502_78, + OPC_6502_79, + OPC_6502X_7A, + OPC_6502X_7B, + OPC_6502X_7C, + OPC_6502_7D, + OPC_6502_7E, + OPC_6502X_7F, + OPC_6502X_80, + OPC_6502_81, + OPC_6502X_82, + OPC_6502X_83, + OPC_6502_84, + OPC_6502_85, + OPC_6502_86, + OPC_6502X_87, + OPC_6502_88, + OPC_6502X_89, + OPC_6502_8A, + OPC_6502X_8B, + OPC_6502_8C, + OPC_6502_8D, + OPC_6502_8E, + OPC_6502X_8F, + OPC_6502_90, + OPC_6502_91, + OPC_Illegal, + OPC_6502X_93, + OPC_6502_94, + OPC_6502_95, + OPC_6502_96, + OPC_6502X_97, + OPC_6502_98, + OPC_6502_99, + OPC_6502_9A, + OPC_6502X_9B, + OPC_6502X_9C, + OPC_6502_9D, + OPC_6502X_9E, + OPC_6502X_9F, + OPC_6502_A0, + OPC_6502_A1, + OPC_6502_A2, + OPC_6502X_A3, + OPC_6502_A4, + OPC_6502_A5, + OPC_6502_A6, + OPC_6502X_A7, + OPC_6502_A8, + OPC_6502_A9, + OPC_6502_AA, + OPC_6502X_AB, + OPC_6502_AC, + OPC_6502_AD, + OPC_6502_AE, + OPC_6502X_AF, + OPC_6502_B0, + OPC_6502_B1, + OPC_Illegal, + OPC_6502X_B3, + OPC_6502_B4, + OPC_6502_B5, + OPC_6502_B6, + OPC_6502X_B7, + OPC_6502_B8, + OPC_6502_B9, + OPC_6502_BA, + OPC_6502X_BB, + OPC_6502_BC, + OPC_6502_BD, + OPC_6502_BE, + OPC_6502X_BF, + OPC_6502_C0, + OPC_6502_C1, + OPC_6502X_C2, + OPC_6502X_C3, + OPC_6502_C4, + OPC_6502_C5, + OPC_6502_C6, + OPC_6502X_C7, + OPC_6502_C8, + OPC_6502_C9, + OPC_6502_CA, + OPC_6502X_CB, + OPC_6502_CC, + OPC_6502_CD, + OPC_6502_CE, + OPC_6502X_CF, + OPC_6502_D0, + OPC_6502_D1, + OPC_Illegal, + OPC_6502X_D3, + OPC_6502X_D4, + OPC_6502_D5, + OPC_6502_D6, + OPC_6502X_D7, + OPC_6502_D8, + OPC_6502_D9, + OPC_6502X_DA, + OPC_6502X_DB, + OPC_6502X_DC, + OPC_6502_DD, + OPC_6502_DE, + OPC_6502X_DF, + OPC_6502_E0, + OPC_6502_E1, + OPC_6502X_E2, + OPC_6502X_E3, + OPC_6502_E4, + OPC_6502_E5, + OPC_6502_E6, + OPC_6502X_E7, + OPC_6502_E8, + OPC_6502_E9, + OPC_6502_EA, + OPC_6502X_EB, + OPC_6502_EC, + OPC_6502_ED, + OPC_6502_EE, + OPC_6502X_EF, + OPC_6502_F0, + OPC_6502_F1, + OPC_Illegal, + OPC_6502X_F3, + OPC_6502X_F4, + OPC_6502_F5, + OPC_6502_F6, + OPC_6502X_F7, + OPC_6502_F8, + OPC_6502_F9, + OPC_6502X_FA, + OPC_6502X_FB, + OPC_6502X_FC, + OPC_6502_FD, + OPC_6502_FE, + OPC_6502X_FF +}; + + + /* Opcode handler table for the 65C02 */ static const OPFunc OP65C02Table[256] = { OPC_6502_00, OPC_6502_01, OPC_65C02_NOP22, // $02 OPC_65C02_NOP11, // $03 - OPC_65SC02_04, + OPC_65C02_04, OPC_6502_05, OPC_6502_06, - OPC_Illegal, // $07: RMB0 currently unsupported + OPC_65C02_07, OPC_6502_08, OPC_6502_09, OPC_6502_0A, OPC_65C02_NOP11, // $0B - OPC_65SC02_0C, + OPC_65C02_0C, OPC_6502_0D, OPC_6502_0E, - OPC_Illegal, // $0F: BBR0 currently unsupported + OPC_65C02_0F, OPC_6502_10, OPC_6502_11, - OPC_65SC02_12, + OPC_65C02_12, OPC_65C02_NOP11, // $13 - OPC_65SC02_14, + OPC_65C02_14, OPC_6502_15, OPC_6502_16, - OPC_Illegal, // $17: RMB1 currently unsupported + OPC_65C02_17, OPC_6502_18, OPC_6502_19, - OPC_65SC02_1A, + OPC_65C02_1A, OPC_65C02_NOP11, // $1B - OPC_65SC02_1C, + OPC_65C02_1C, OPC_6502_1D, - OPC_6502_1E, - OPC_Illegal, // $1F: BBR1 currently unsupported + OPC_65C02_1E, + OPC_65C02_1F, OPC_6502_20, OPC_6502_21, OPC_65C02_NOP22, // $22 @@ -2966,7 +4458,7 @@ static const OPFunc OP65C02Table[256] = { OPC_6502_24, OPC_6502_25, OPC_6502_26, - OPC_Illegal, // $27: RMB2 currently unsupported + OPC_65C02_27, OPC_6502_28, OPC_6502_29, OPC_6502_2A, @@ -2974,31 +4466,31 @@ static const OPFunc OP65C02Table[256] = { OPC_6502_2C, OPC_6502_2D, OPC_6502_2E, - OPC_Illegal, // $2F: BBR2 currently unsupported + OPC_65C02_2F, OPC_6502_30, OPC_6502_31, - OPC_65SC02_32, + OPC_65C02_32, OPC_65C02_NOP11, // $33 - OPC_65SC02_34, + OPC_65C02_34, OPC_6502_35, OPC_6502_36, - OPC_Illegal, // $37: RMB3 currently unsupported + OPC_65C02_37, OPC_6502_38, OPC_6502_39, - OPC_65SC02_3A, + OPC_65C02_3A, OPC_65C02_NOP11, // $3B - OPC_65SC02_3C, + OPC_65C02_3C, OPC_6502_3D, - OPC_6502_3E, - OPC_Illegal, // $3F: BBR3 currently unsupported + OPC_65C02_3E, + OPC_65C02_3F, OPC_6502_40, OPC_6502_41, OPC_65C02_NOP22, // $42 OPC_65C02_NOP11, // $43 - OPC_65C02_44, // $44 + OPC_6502X_44, // $44 OPC_6502_45, OPC_6502_46, - OPC_Illegal, // $47: RMB4 currently unsupported + OPC_65C02_47, OPC_6502_48, OPC_6502_49, OPC_6502_4A, @@ -3006,87 +4498,87 @@ static const OPFunc OP65C02Table[256] = { OPC_6502_4C, OPC_6502_4D, OPC_6502_4E, - OPC_Illegal, // $4F: BBR4 currently unsupported + OPC_65C02_4F, OPC_6502_50, OPC_6502_51, - OPC_65SC02_52, + OPC_65C02_52, OPC_65C02_NOP11, // $53 OPC_65C02_NOP24, // $54 OPC_6502_55, OPC_6502_56, - OPC_Illegal, // $57: RMB5 currently unsupported + OPC_65C02_57, OPC_6502_58, OPC_6502_59, - OPC_65SC02_5A, + OPC_65C02_5A, OPC_65C02_NOP11, // $5B OPC_65C02_5C, OPC_6502_5D, - OPC_6502_5E, - OPC_Illegal, // $5F: BBR5 currently unsupported + OPC_65C02_5E, + OPC_65C02_5F, OPC_6502_60, - OPC_6502_61, + OPC_65C02_61, OPC_65C02_NOP22, // $62 OPC_65C02_NOP11, // $63 - OPC_65SC02_64, - OPC_6502_65, + OPC_65C02_64, + OPC_65C02_65, OPC_6502_66, - OPC_Illegal, // $67: RMB6 currently unsupported + OPC_65C02_67, OPC_6502_68, - OPC_6502_69, + OPC_65C02_69, OPC_6502_6A, OPC_65C02_NOP11, // $6B OPC_65C02_6C, - OPC_6502_6D, + OPC_65C02_6D, OPC_6502_6E, - OPC_Illegal, // $6F: BBR6 currently unsupported + OPC_65C02_6F, OPC_6502_70, - OPC_6502_71, - OPC_65SC02_72, + OPC_65C02_71, + OPC_65C02_72, OPC_65C02_NOP11, // $73 - OPC_65SC02_74, - OPC_6502_75, + OPC_65C02_74, + OPC_65C02_75, OPC_6502_76, - OPC_Illegal, // $77: RMB7 currently unsupported + OPC_65C02_77, OPC_6502_78, - OPC_6502_79, - OPC_65SC02_7A, + OPC_65C02_79, + OPC_65C02_7A, OPC_65C02_NOP11, // $7B - OPC_65SC02_7C, - OPC_6502_7D, - OPC_6502_7E, - OPC_Illegal, // $7F: BBR7 currently unsupported - OPC_65SC02_80, + OPC_65C02_7C, + OPC_65C02_7D, + OPC_65C02_7E, + OPC_65C02_7F, + OPC_65C02_80, OPC_6502_81, OPC_65C02_NOP22, // $82 OPC_65C02_NOP11, // $83 OPC_6502_84, OPC_6502_85, OPC_6502_86, - OPC_Illegal, // $87: SMB0 currently unsupported + OPC_65C02_87, OPC_6502_88, - OPC_65SC02_89, + OPC_65C02_89, OPC_6502_8A, OPC_65C02_NOP11, // $8B OPC_6502_8C, OPC_6502_8D, OPC_6502_8E, - OPC_Illegal, // $8F: BBS0 currently unsupported + OPC_65C02_8F, OPC_6502_90, OPC_6502_91, - OPC_65SC02_92, + OPC_65C02_92, OPC_65C02_NOP11, // $93 OPC_6502_94, OPC_6502_95, OPC_6502_96, - OPC_Illegal, // $97: SMB1 currently unsupported + OPC_65C02_97, OPC_6502_98, OPC_6502_99, OPC_6502_9A, OPC_65C02_NOP11, // $9B - OPC_65SC02_9C, + OPC_65C02_9C, OPC_6502_9D, - OPC_65SC02_9E, - OPC_Illegal, // $9F: BBS1 currently unsupported + OPC_65C02_9E, + OPC_65C02_9F, OPC_6502_A0, OPC_6502_A1, OPC_6502_A2, @@ -3094,7 +4586,7 @@ static const OPFunc OP65C02Table[256] = { OPC_6502_A4, OPC_6502_A5, OPC_6502_A6, - OPC_Illegal, // $A7: SMB2 currently unsupported + OPC_65C02_A7, OPC_6502_A8, OPC_6502_A9, OPC_6502_AA, @@ -3102,15 +4594,15 @@ static const OPFunc OP65C02Table[256] = { OPC_6502_AC, OPC_6502_AD, OPC_6502_AE, - OPC_Illegal, // $AF: BBS2 currently unsupported + OPC_65C02_AF, OPC_6502_B0, OPC_6502_B1, - OPC_65SC02_B2, + OPC_65C02_B2, OPC_65C02_NOP11, // $B3 OPC_6502_B4, OPC_6502_B5, OPC_6502_B6, - OPC_Illegal, // $B7: SMB3 currently unsupported + OPC_65C02_B7, OPC_6502_B8, OPC_6502_B9, OPC_6502_BA, @@ -3118,7 +4610,7 @@ static const OPFunc OP65C02Table[256] = { OPC_6502_BC, OPC_6502_BD, OPC_6502_BE, - OPC_Illegal, // $BF: BBS3 currently unsupported + OPC_65C02_BF, OPC_6502_C0, OPC_6502_C1, OPC_65C02_NOP22, // $C2 @@ -3126,7 +4618,7 @@ static const OPFunc OP65C02Table[256] = { OPC_6502_C4, OPC_6502_C5, OPC_6502_C6, - OPC_Illegal, // $C7: SMB4 currently unsupported + OPC_65C02_C7, OPC_6502_C8, OPC_6502_C9, OPC_6502_CA, @@ -3134,61 +4626,65 @@ static const OPFunc OP65C02Table[256] = { OPC_6502_CC, OPC_6502_CD, OPC_6502_CE, - OPC_Illegal, // $CF: BBS4 currently unsupported + OPC_65C02_CF, OPC_6502_D0, OPC_6502_D1, - OPC_65SC02_D2, + OPC_65C02_D2, OPC_65C02_NOP11, // $D3 OPC_65C02_NOP24, // $D4 OPC_6502_D5, OPC_6502_D6, - OPC_Illegal, // $D7: SMB5 currently unsupported + OPC_65C02_D7, OPC_6502_D8, OPC_6502_D9, - OPC_65SC02_DA, + OPC_65C02_DA, OPC_Illegal, // $DB: STP currently unsupported OPC_65C02_NOP34, // $DC OPC_6502_DD, OPC_6502_DE, - OPC_Illegal, // $DF: BBS5 currently unsupported + OPC_65C02_DF, OPC_6502_E0, - OPC_6502_E1, + OPC_65C02_E1, OPC_65C02_NOP22, // $E2 OPC_65C02_NOP11, // $E3 OPC_6502_E4, - OPC_6502_E5, + OPC_65C02_E5, OPC_6502_E6, - OPC_Illegal, // $E7: SMB6 currently unsupported + OPC_65C02_E7, OPC_6502_E8, - OPC_6502_E9, + OPC_65C02_E9, OPC_6502_EA, OPC_65C02_NOP11, // $EB OPC_6502_EC, - OPC_6502_ED, + OPC_65C02_ED, OPC_6502_EE, - OPC_Illegal, // $EF: BBS6 currently unsupported + OPC_65C02_EF, OPC_6502_F0, - OPC_6502_F1, - OPC_65SC02_F2, + OPC_65C02_F1, + OPC_65C02_F2, OPC_65C02_NOP11, // $F3 OPC_65C02_NOP24, // $F4 - OPC_6502_F5, + OPC_65C02_F5, OPC_6502_F6, - OPC_Illegal, // $F7: SMB7 currently unsupported + OPC_65C02_F7, OPC_6502_F8, - OPC_6502_F9, - OPC_65SC02_FA, + OPC_65C02_F9, + OPC_65C02_FA, OPC_65C02_NOP11, // $FB OPC_65C02_NOP34, // $FC - OPC_6502_FD, + OPC_65C02_FD, OPC_6502_FE, - OPC_Illegal, // $FF: BBS7 currently unsupported + OPC_65C02_FF }; /* Tables with opcode handlers */ -static const OPFunc* Handlers[2] = {OP6502Table, OP65C02Table}; +static const OPFunc* Handlers[3] = { + OP6502Table, + OP65C02Table, + OP6502XTable +}; @@ -3202,7 +4698,7 @@ void IRQRequest (void) /* Generate an IRQ */ { /* Remember the request */ - HaveIRQRequest = 1; + HaveIRQRequest = true; } @@ -3211,7 +4707,7 @@ void NMIRequest (void) /* Generate an NMI */ { /* Remember the request */ - HaveNMIRequest = 1; + HaveNMIRequest = true; } @@ -3220,8 +4716,8 @@ void Reset (void) /* Generate a CPU RESET */ { /* Reset the CPU */ - HaveIRQRequest = 0; - HaveNMIRequest = 0; + HaveIRQRequest = false; + HaveNMIRequest = false; /* Bits 5 and 4 aren't used, and always are 1! */ Regs.SR = 0x30; @@ -3236,12 +4732,18 @@ unsigned ExecuteInsn (void) /* If we have an NMI request, handle it */ if (HaveNMIRequest) { - HaveNMIRequest = 0; + if (TraceMode != TRACE_DISABLED) { + PrintTraceNMI (); + } + + HaveNMIRequest = false; + Peripherals.Counter.NmiEvents += 1; + PUSH (PCH); PUSH (PCL); PUSH (Regs.SR & ~BF); SET_IF (1); - if (CPU != CPU_6502) + if (CPU == CPU_65C02) { SET_DF (0); } @@ -3250,12 +4752,18 @@ unsigned ExecuteInsn (void) } else if (HaveIRQRequest && GET_IF () == 0) { - HaveIRQRequest = 0; + if (TraceMode != TRACE_DISABLED) { + PrintTraceIRQ (); + } + + HaveIRQRequest = false; + Peripherals.Counter.IrqEvents += 1; + PUSH (PCH); PUSH (PCL); PUSH (Regs.SR & ~BF); SET_IF (1); - if (CPU != CPU_6502) + if (CPU == CPU_65C02) { SET_DF (0); } @@ -3265,12 +4773,23 @@ unsigned ExecuteInsn (void) } else { /* Normal instruction - read the next opcode */ - unsigned char OPC = MemReadByte (Regs.PC); + uint8_t OPC = MemReadByte (Regs.PC); - /* Execute it */ + /* Print a trace line, if trace mode is enabled. */ + if (TraceMode != TRACE_DISABLED) { + PrintTraceInstruction (); + } + + /* Increment the instruction counter by one. */ + Peripherals.Counter.CpuInstructions += 1; + + /* Execute the instruction. The handler sets the 'Cycles' variable. */ Handlers[CPU][OPC] (); } - /* Return the number of clock cycles needed by this insn */ + /* Increment the 64-bit clock cycle counter with the cycle count for the instruction that we just executed. */ + Peripherals.Counter.ClockCycles += Cycles; + + /* Return the number of clock cycles needed by this instruction */ return Cycles; } diff --git a/src/sim65/6502.h b/src/sim65/6502.h index 39b995793..d715f3288 100644 --- a/src/sim65/6502.h +++ b/src/sim65/6502.h @@ -37,6 +37,8 @@ #define _6502_H +#include <stdint.h> + /*****************************************************************************/ /* Data */ @@ -46,8 +48,9 @@ /* Supported CPUs */ typedef enum CPUType { - CPU_6502, - CPU_65C02 + CPU_6502 = 0, + CPU_65C02 = 1, + CPU_6502X = 2 } CPUType; /* Current CPU */ @@ -56,15 +59,17 @@ extern CPUType CPU; /* 6502 CPU registers */ typedef struct CPURegs CPURegs; struct CPURegs { - unsigned AC; /* Accumulator */ - unsigned XR; /* X register */ - unsigned YR; /* Y register */ - unsigned ZR; /* Z register */ - unsigned SR; /* Status register */ - unsigned SP; /* Stackpointer */ - unsigned PC; /* Program counter */ + uint8_t AC; /* Accumulator */ + uint8_t XR; /* X register */ + uint8_t YR; /* Y register */ + uint8_t SR; /* Status register */ + uint8_t SP; /* Stackpointer */ + uint16_t PC; /* Program counter */ }; +/* Current CPU registers */ +extern CPURegs Regs; + /* Status register bits */ #define CF 0x01 /* Carry flag */ #define ZF 0x02 /* Zero flag */ diff --git a/src/sim65/error.c b/src/sim65/error.c index fc24ca006..af8e88413 100644 --- a/src/sim65/error.c +++ b/src/sim65/error.c @@ -36,9 +36,10 @@ #include <stdio.h> #include <stdlib.h> #include <stdarg.h> +#include <inttypes.h> #include "error.h" - +#include "peripherals.h" /*****************************************************************************/ @@ -50,9 +51,6 @@ /* flag to print cycles at program termination */ int PrintCycles = 0; -/* cycles are counted by main.c */ -extern unsigned long long TotalCycles; - /*****************************************************************************/ @@ -120,7 +118,7 @@ void SimExit (int Code) /* Exit the simulation with an exit code */ { if (PrintCycles) { - fprintf (stdout, "%llu cycles\n", TotalCycles); + fprintf (stdout, "%" PRIu64 " cycles\n", Peripherals.Counter.ClockCycles); } exit (Code); } diff --git a/src/sim65/main.c b/src/sim65/main.c index 3c7cdc157..828ea498e 100644 --- a/src/sim65/main.c +++ b/src/sim65/main.c @@ -35,6 +35,7 @@ #include <string.h> #include <stdlib.h> +#include <stdbool.h> #include <errno.h> /* common */ @@ -47,7 +48,9 @@ #include "6502.h" #include "error.h" #include "memory.h" +#include "peripherals.h" #include "paravirt.h" +#include "trace.h" @@ -60,8 +63,8 @@ /* Name of program file */ const char* ProgramFile; -/* count of total cycles executed */ -unsigned long long TotalCycles = 0; +/* Set to True if CPU mode override is in effect. If set, the CPU is not read from the program file. */ +static bool CPUOverrideActive = false; /* exit simulator after MaxCycles Cccles */ unsigned long long MaxCycles = 0; @@ -97,6 +100,8 @@ static void Usage (void) "Long options:\n" " --help\t\tHelp (this text)\n" " --cycles\t\tPrint amount of executed CPU cycles\n" + " --cpu <type>\t\tOverride CPU type (6502, 65C02, 6502X)\n" + " --trace\t\tEnable CPU trace\n" " --verbose\t\tIncrease verbosity\n" " --version\t\tPrint the simulator version number\n", ProgName); @@ -114,6 +119,35 @@ static void OptHelp (const char* Opt attribute ((unused)), +static void OptCPU (const char* Opt, const char* Arg) +/* Set CPU type */ +{ + /* Don't use FindCPU here. Enum constants would clash. */ + if (strcmp(Arg, "6502") == 0) { + CPU = CPU_6502; + CPUOverrideActive = true; + } else if (strcmp(Arg, "65C02") == 0 || strcmp(Arg, "65c02") == 0) { + CPU = CPU_65C02; + CPUOverrideActive = true; + } else if (strcmp(Arg, "6502X") == 0 || strcmp(Arg, "6502x") == 0) { + CPU = CPU_6502X; + CPUOverrideActive = true; + } else { + AbEnd ("Invalid argument for %s: '%s'", Opt, Arg); + } +} + + + +static void OptTrace (const char* Opt attribute ((unused)), + const char* Arg attribute ((unused))) +/* Enable trace mode */ +{ + TraceMode = TRACE_ENABLE_FULL; /* Enable full trace mode. */ +} + + + static void OptVerbose (const char* Opt attribute ((unused)), const char* Arg attribute ((unused))) /* Increase verbosity */ @@ -137,16 +171,20 @@ static void OptVersion (const char* Opt attribute ((unused)), /* Print the simulator version */ { fprintf (stderr, "%s V%s\n", ProgName, GetVersionAsString ()); - exit(EXIT_SUCCESS); + exit (EXIT_SUCCESS); } + + static void OptQuitXIns (const char* Opt attribute ((unused)), - const char* Arg attribute ((unused))) -/* quit after MaxCycles cycles */ + const char* Arg) +/* Quit after MaxCycles cycles */ { MaxCycles = strtoull(Arg, NULL, 0); } + + static unsigned char ReadProgramFile (void) /* Load program into memory */ { @@ -175,12 +213,21 @@ static unsigned char ReadProgramFile (void) Error ("'%s': Invalid header version.", ProgramFile); } - /* Get the CPU type from the file header */ + /* Get the CPU type from the file header. + * Use it to set the CPU type, unless CPUOverrideActive is set. + */ if ((Val = fgetc(F)) != EOF) { - if (Val != CPU_6502 && Val != CPU_65C02) { - Error ("'%s': Invalid CPU type", ProgramFile); + if (!CPUOverrideActive) { + switch (Val) { + case CPU_6502: + case CPU_65C02: + case CPU_6502X: + CPU = Val; + break; + default: + Error ("'%s': Invalid CPU type", ProgramFile); + } } - CPU = Val; } /* Get the address of sp from the file header */ @@ -234,16 +281,22 @@ int main (int argc, char* argv[]) { /* Program long options */ static const LongOpt OptTab[] = { - { "--help", 0, OptHelp }, - { "--cycles", 0, OptCycles }, - { "--verbose", 0, OptVerbose }, - { "--version", 0, OptVersion }, + { "--help", 0, OptHelp }, + { "--cycles", 0, OptCycles }, + { "--cpu", 1, OptCPU }, + { "--trace", 0, OptTrace }, + { "--verbose", 0, OptVerbose }, + { "--version", 0, OptVersion }, }; unsigned I; unsigned char SPAddr; unsigned int Cycles; + /* Set reasonable defaults. */ + CPU = CPU_6502; + TraceMode = TRACE_DISABLED; /* Disabled by default */ + /* Initialize the cmdline module */ InitCmdLine (&argc, &argv, "sim65"); @@ -298,21 +351,34 @@ int main (int argc, char* argv[]) } /* Do we have a program file? */ - if (ProgramFile == 0) { + if (ProgramFile == NULL) { AbEnd ("No program file"); } + /* Reset memory */ MemInit (); + /* Reset peripherals. */ + PeripheralsInit (); + + /* Read program file into memory. + * This also sets the CPU type, unless a CPU override is in effect. + */ SPAddr = ReadProgramFile (); + + /* Initialize the paravirtualization subsystem. It requires the stack pointer address, to be able to + * simulate 6502 subroutine calls. + */ + + TraceInit(SPAddr); ParaVirtInit (I, SPAddr); + /* Reset the CPU */ Reset (); RemainCycles = MaxCycles; while (1) { Cycles = ExecuteInsn (); - TotalCycles += Cycles; if (MaxCycles) { if (Cycles > RemainCycles) { ErrorCode (SIM65_ERROR_TIMEOUT, "Maximum number of cycles reached."); diff --git a/src/sim65/memory.c b/src/sim65/memory.c index 68e7bb93b..c80bf0f93 100644 --- a/src/sim65/memory.c +++ b/src/sim65/memory.c @@ -36,7 +36,7 @@ #include <string.h> #include "memory.h" - +#include "peripherals.h" /*****************************************************************************/ @@ -45,8 +45,8 @@ -/* THE memory */ -unsigned char Mem[0x10000]; +/* The memory */ +uint8_t Mem[0x10000]; @@ -56,15 +56,22 @@ unsigned char Mem[0x10000]; -void MemWriteByte (unsigned Addr, unsigned char Val) +void MemWriteByte (uint16_t Addr, uint8_t Val) /* Write a byte to a memory location */ { - Mem[Addr] = Val; + if ((PERIPHERALS_APERTURE_BASE_ADDRESS <= Addr) && (Addr <= PERIPHERALS_APERTURE_LAST_ADDRESS)) + { + /* Defer the the memory-mapped peripherals handler for this write. */ + PeripheralsWriteByte (Addr - PERIPHERALS_APERTURE_BASE_ADDRESS, Val); + } else { + /* Write to the Mem array. */ + Mem[Addr] = Val; + } } -void MemWriteWord (unsigned Addr, unsigned Val) +void MemWriteWord (uint16_t Addr, uint16_t Val) /* Write a word to a memory location */ { MemWriteByte (Addr, Val & 0xFF); @@ -73,22 +80,37 @@ void MemWriteWord (unsigned Addr, unsigned Val) -unsigned MemReadWord (unsigned Addr) +uint8_t MemReadByte (uint16_t Addr) +/* Read a byte from a memory location */ +{ + if ((PERIPHERALS_APERTURE_BASE_ADDRESS <= Addr) && (Addr <= PERIPHERALS_APERTURE_LAST_ADDRESS)) + { + /* Defer the the memory-mapped peripherals handler for this read. */ + return PeripheralsReadByte (Addr - PERIPHERALS_APERTURE_BASE_ADDRESS); + } else { + /* Read from the Mem array. */ + return Mem[Addr]; + } +} + + + +uint16_t MemReadWord (uint16_t Addr) /* Read a word from a memory location */ { - unsigned W = MemReadByte (Addr++); + uint8_t W = MemReadByte (Addr++); return (W | (MemReadByte (Addr) << 8)); } -unsigned MemReadZPWord (unsigned char Addr) +uint16_t MemReadZPWord (uint8_t Addr) /* Read a word from the zero page. This function differs from MemReadWord in that ** the read will always be in the zero page, even in case of an address ** overflow. */ { - unsigned W = MemReadByte (Addr++); + uint8_t W = MemReadByte (Addr++); return (W | (MemReadByte (Addr) << 8)); } diff --git a/src/sim65/memory.h b/src/sim65/memory.h index cef786aaa..b70c5bd05 100644 --- a/src/sim65/memory.h +++ b/src/sim65/memory.h @@ -36,9 +36,9 @@ #ifndef MEMORY_H #define MEMORY_H -#include "inline.h" +#include <stdint.h> -extern unsigned char Mem[0x10000]; +extern uint8_t Mem[0x10000]; /*****************************************************************************/ /* Code */ @@ -46,26 +46,19 @@ extern unsigned char Mem[0x10000]; -void MemWriteByte (unsigned Addr, unsigned char Val); +void MemWriteByte (uint16_t Addr, uint8_t Val); /* Write a byte to a memory location */ -void MemWriteWord (unsigned Addr, unsigned Val); +void MemWriteWord (uint16_t Addr, uint16_t Val); /* Write a word to a memory location */ -#if defined(HAVE_INLINE) -INLINE unsigned char MemReadByte (unsigned Addr) +uint8_t MemReadByte (uint16_t Addr); /* Read a byte from a memory location */ -{ - return Mem[Addr]; -} -#else -#define MemReadByte(Addr) Mem[Addr] -#endif -unsigned MemReadWord (unsigned Addr); +uint16_t MemReadWord (uint16_t Addr); /* Read a word from a memory location */ -unsigned MemReadZPWord (unsigned char Addr); +uint16_t MemReadZPWord (uint8_t Addr); /* Read a word from the zero page. This function differs from MemReadWord in that ** the read will always be in the zero page, even in case of an address ** overflow. diff --git a/src/sim65/paravirt.c b/src/sim65/paravirt.c index 2e52d6e7e..4e68a3f0e 100644 --- a/src/sim65/paravirt.c +++ b/src/sim65/paravirt.c @@ -159,11 +159,35 @@ static void PVArgs (CPURegs* Regs) SetAX (Regs, ArgC); } +/* Match between standard POSIX whence and cc65 whence. */ +static unsigned SEEK_MODE_MATCH[3] = { + SEEK_CUR, + SEEK_END, + SEEK_SET +}; + +static void PVLseek (CPURegs* Regs) +{ + unsigned RetVal; + + unsigned Whence = GetAX (Regs); + unsigned Offset = PopParam (4); + unsigned FD = PopParam (2); + + Print (stderr, 2, "PVLseek ($%04X, $%08X, $%04X (%d))\n", + FD, Offset, Whence, SEEK_MODE_MATCH[Whence]); + + RetVal = lseek(FD, (off_t)Offset, SEEK_MODE_MATCH[Whence]); + Print (stderr, 2, "PVLseek returned %04X\n", RetVal); + + SetAX (Regs, RetVal); +} + static void PVOpen (CPURegs* Regs) { - char Path[PVOPEN_PATH_SIZE]; + char Path[PV_PATH_SIZE]; int OFlag = O_INITIAL; int OMode = 0; unsigned RetVal, I = 0; @@ -184,7 +208,7 @@ static void PVOpen (CPURegs* Regs) break; } ++I; - if (I >= PVOPEN_PATH_SIZE) { + if (I >= PV_PATH_SIZE) { Error("PVOpen path too long at address $%04X",Name); } } @@ -253,6 +277,35 @@ static void PVClose (CPURegs* Regs) +static void PVSysRemove (CPURegs* Regs) +{ + char Path[PV_PATH_SIZE]; + unsigned RetVal, I = 0; + + unsigned Name = GetAX (Regs); + + Print (stderr, 2, "PVSysRemove ($%04X)\n", Name); + + do { + if (!(Path[I] = MemReadByte ((Name + I) & 0xFFFF))) { + break; + } + ++I; + if (I >= PV_PATH_SIZE) { + Error("PVSysRemove path too long at address $%04X", Name); + } + } + while (1); + + Print (stderr, 2, "PVSysRemove (\"%s\")\n", Path); + + RetVal = remove (Path); + + SetAX (Regs, RetVal); +} + + + static void PVRead (CPURegs* Regs) { unsigned char* Data; @@ -305,7 +358,18 @@ static void PVWrite (CPURegs* Regs) +static void PVOSMapErrno (CPURegs* Regs) +{ + unsigned err = GetAX(Regs); + SetAX (Regs, err != 0 ? -1 : 0); +} + + + static const PVFunc Hooks[] = { + PVLseek, + PVSysRemove, + PVOSMapErrno, PVOpen, PVClose, PVRead, diff --git a/src/sim65/paravirt.h b/src/sim65/paravirt.h index 3badb50ea..83c7127a0 100644 --- a/src/sim65/paravirt.h +++ b/src/sim65/paravirt.h @@ -32,11 +32,12 @@ /*****************************************************************************/ - #ifndef PARAVIRT_H #define PARAVIRT_H +#include "6502.h" + /*****************************************************************************/ /* Data */ @@ -44,11 +45,11 @@ -#define PARAVIRT_BASE 0xFFF4 +#define PARAVIRT_BASE 0xFFF1 /* Lowest address used by a paravirtualization hook */ -#define PVOPEN_PATH_SIZE 1024 -/* Maximum path size supported by PVOpen */ +#define PV_PATH_SIZE 1024 +/* Maximum path size supported by PVOpen/PVSysRemove */ diff --git a/src/sim65/peripherals.c b/src/sim65/peripherals.c new file mode 100644 index 000000000..1d7ff6bdb --- /dev/null +++ b/src/sim65/peripherals.c @@ -0,0 +1,250 @@ +/*****************************************************************************/ +/* */ +/* peripherals.c */ +/* */ +/* Memory-mapped peripheral subsystem for the 6502 simulator */ +/* */ +/* */ +/* */ +/* (C) 2024-2025, Sidney Cadot */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed 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. */ +/* */ +/*****************************************************************************/ + +#include <stdbool.h> +#include <time.h> +#if defined(__MINGW64__) || defined(__MINGW32__) +/* For gettimeofday() */ +#include <sys/time.h> +#endif + + +#include "peripherals.h" +#include "trace.h" +#include "6502.h" + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* The system-wide state of the peripherals */ +Sim65Peripherals Peripherals; + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + +static bool GetWallclockTime (struct timespec * ts) +/* Get the wallclock time with nanosecond resolution. */ +{ + /* Note: the 'struct timespec' type is available on all compilers we want to support. */ + + bool time_valid; + +#if defined(__MINGW64__) + /* When using the MinGW64 compiler, neither timespec_get() nor clock_gettime() + * are available; using either of them makes the Linux PR build workflow build fail. + * The gettimeofday() function does work, so use that; its microsecond resolution + * is fine for most applications. + */ + struct timeval tv; + time_valid = (gettimeofday(&tv, NULL) == 0); + if (time_valid) { + ts->tv_sec = tv.tv_sec; + ts->tv_nsec = tv.tv_usec * 1000; + } +#elif defined(__MINGW32__) + /* Note: we test for MinGW32 after the test for MinGW64, as the __MINGW32__ symbol is also + * defined in MinGW64. This allows us to distinguish MinGW32 and MinGW64 build. + * + * When using the MinGW32 compiler, neither timespec_get() nor clock_gettime() + * are available; using either of them makes the Linux snapshot workflow build fail. + * The gettimeofday() function does work, so use that; its microsecond resolution + * is fine for most applications. + */ + struct timeval tv; + time_valid = (gettimeofday(&tv, NULL) == 0); + if (time_valid) { + ts->tv_sec = tv.tv_sec; + ts->tv_nsec = tv.tv_usec * 1000; + } +#elif defined(_MSC_VER) + /* Using the Microsoft C++ compiler. + * clock_gettime() is not available; use timespec_get() instead. + */ + time_valid = timespec_get(ts, TIME_UTC) == TIME_UTC; +#else + /* On all other compilers, assume that clock_gettime() is available. + * This is true on Linux and MacOS, at least. + */ + time_valid = clock_gettime(CLOCK_REALTIME, ts) == 0; +#endif + + return time_valid; +} + + + +void PeripheralsWriteByte (uint8_t Addr, uint8_t Val) +/* Write a byte to a memory location in the peripherals address aperture. */ +{ + switch (Addr) { + + /* Handle writes to the Counter peripheral. */ + + case PERIPHERALS_COUNTER_ADDRESS_OFFSET_LATCH: { + + /* A write to the "latch" register performs a simultaneous latch of all registers. */ + + /* Latch the current wallclock time before doing anything else. */ + + struct timespec ts; + bool time_valid = GetWallclockTime (&ts); + + if (time_valid) { + /* Wallclock time: number of nanoseconds since 1-1-1970. */ + Peripherals.Counter.LatchedWallclockTime = 1000000000 * (uint64_t)ts.tv_sec + ts.tv_nsec; + /* Wallclock time, split: high word is number of seconds since 1-1-1970, + * low word is number of nanoseconds since the start of that second. */ + Peripherals.Counter.LatchedWallclockTimeSplit = (uint64_t)ts.tv_sec << 32 | ts.tv_nsec; + } else { + /* Unable to get time. Report max uint64 value for both fields. */ + Peripherals.Counter.LatchedWallclockTime = -1; + Peripherals.Counter.LatchedWallclockTimeSplit = -1; + } + + /* Latch the counters that reflect the state of the processor. */ + Peripherals.Counter.LatchedClockCycles = Peripherals.Counter.ClockCycles; + Peripherals.Counter.LatchedCpuInstructions = Peripherals.Counter.CpuInstructions; + Peripherals.Counter.LatchedIrqEvents = Peripherals.Counter.IrqEvents; + Peripherals.Counter.LatchedNmiEvents = Peripherals.Counter.NmiEvents; + break; + } + case PERIPHERALS_COUNTER_ADDRESS_OFFSET_SELECT: { + /* Set the value of the visibility-selection register. */ + Peripherals.Counter.LatchedValueSelected = Val; + break; + } + + /* Handle writes to the SimControl peripheral. */ + + case PERIPHERALS_SIMCONTROL_ADDRESS_OFFSET_CPUMODE: { + if (Val == CPU_6502 || Val == CPU_65C02 || Val == CPU_6502X) { + CPU = Val; + } + break; + } + + case PERIPHERALS_SIMCONTROL_ADDRESS_OFFSET_TRACEMODE: { + TraceMode = Val; + break; + } + + /* Handle writes to unused and read-only peripheral addresses. */ + + default: { + /* No action. */ + } + } +} + + + +uint8_t PeripheralsReadByte (uint8_t Addr) +/* Read a byte from a memory location in the peripherals address aperture. */ +{ + switch (Addr) { + + /* Handle reads from the Counter peripheral. */ + + case PERIPHERALS_COUNTER_ADDRESS_OFFSET_SELECT: { + return Peripherals.Counter.LatchedValueSelected; + } + case PERIPHERALS_COUNTER_ADDRESS_OFFSET_VALUE + 0: + case PERIPHERALS_COUNTER_ADDRESS_OFFSET_VALUE + 1: + case PERIPHERALS_COUNTER_ADDRESS_OFFSET_VALUE + 2: + case PERIPHERALS_COUNTER_ADDRESS_OFFSET_VALUE + 3: + case PERIPHERALS_COUNTER_ADDRESS_OFFSET_VALUE + 4: + case PERIPHERALS_COUNTER_ADDRESS_OFFSET_VALUE + 5: + case PERIPHERALS_COUNTER_ADDRESS_OFFSET_VALUE + 6: + case PERIPHERALS_COUNTER_ADDRESS_OFFSET_VALUE + 7: { + /* Read from any of the eight counter bytes. + * The first byte is the 64 bit value's LSB, the seventh byte is its MSB. + */ + unsigned SelectedByteIndex = Addr - PERIPHERALS_COUNTER_ADDRESS_OFFSET_VALUE; /* 0 .. 7 */ + uint64_t Value; + switch (Peripherals.Counter.LatchedValueSelected) { + case PERIPHERALS_COUNTER_SELECT_CLOCKCYCLE_COUNTER: Value = Peripherals.Counter.LatchedClockCycles; break; + case PERIPHERALS_COUNTER_SELECT_INSTRUCTION_COUNTER: Value = Peripherals.Counter.LatchedCpuInstructions; break; + case PERIPHERALS_COUNTER_SELECT_IRQ_COUNTER: Value = Peripherals.Counter.LatchedIrqEvents; break; + case PERIPHERALS_COUNTER_SELECT_NMI_COUNTER: Value = Peripherals.Counter.LatchedNmiEvents; break; + case PERIPHERALS_COUNTER_SELECT_WALLCLOCK_TIME: Value = Peripherals.Counter.LatchedWallclockTime; break; + case PERIPHERALS_COUNTER_SELECT_WALLCLOCK_TIME_SPLIT: Value = Peripherals.Counter.LatchedWallclockTimeSplit; break; + default: Value = 0; /* Reading from a non-existent latch register will yield 0. */ + } + /* Return the desired byte of the latched counter; 0==LSB, 7==MSB. */ + return (uint8_t)(Value >> (SelectedByteIndex * 8)); + } + + /* Handle reads from the SimControl peripheral. */ + + case PERIPHERALS_SIMCONTROL_ADDRESS_OFFSET_CPUMODE: { + return CPU; + } + + case PERIPHERALS_SIMCONTROL_ADDRESS_OFFSET_TRACEMODE: { + return TraceMode; + } + + /* Handle reads from unused peripheral and write-only addresses. */ + + default: { + /* Return zero value. */ + return 0; + } + } +} + + + +void PeripheralsInit (void) +/* Initialize the peripherals. */ +{ + /* Initialize the Counter peripheral */ + + Peripherals.Counter.ClockCycles = 0; + Peripherals.Counter.CpuInstructions = 0; + Peripherals.Counter.IrqEvents = 0; + Peripherals.Counter.NmiEvents = 0; + + Peripherals.Counter.LatchedClockCycles = 0; + Peripherals.Counter.LatchedCpuInstructions = 0; + Peripherals.Counter.LatchedIrqEvents = 0; + Peripherals.Counter.LatchedNmiEvents = 0; + Peripherals.Counter.LatchedWallclockTime = 0; + Peripherals.Counter.LatchedWallclockTimeSplit = 0; + + Peripherals.Counter.LatchedValueSelected = 0; +} diff --git a/src/sim65/peripherals.h b/src/sim65/peripherals.h new file mode 100644 index 000000000..dc86e6c6e --- /dev/null +++ b/src/sim65/peripherals.h @@ -0,0 +1,125 @@ +/*****************************************************************************/ +/* */ +/* peripherals.h */ +/* */ +/* Memory-mapped peripheral subsystem for the 6502 simulator */ +/* */ +/* */ +/* */ +/* (C) 2024-2025, Sidney Cadot */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed 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. */ +/* */ +/*****************************************************************************/ + + + +#ifndef PERIPHERALS_H +#define PERIPHERALS_H + +#include <stdint.h> + +/* The memory range where the memory-mapped peripherals can be accessed. */ + +#define PERIPHERALS_APERTURE_BASE_ADDRESS 0xffc0 +#define PERIPHERALS_APERTURE_LAST_ADDRESS 0xffcb + +/* Declarations for the COUNTER peripheral */ + +#define PERIPHERALS_COUNTER_ADDRESS_OFFSET_LATCH 0x00 +#define PERIPHERALS_COUNTER_ADDRESS_OFFSET_SELECT 0x01 +#define PERIPHERALS_COUNTER_ADDRESS_OFFSET_VALUE 0x02 + +#define PERIPHERALS_COUNTER_LATCH (PERIPHERALS_APERTURE_BASE_ADDRESS + PERIPHERALS_ADDRESS_OFFSET_COUNTER_LATCH) +#define PERIPHERALS_COUNTER_SELECT (PERIPHERALS_APERTURE_BASE_ADDRESS + PERIPHERALS_ADDRESS_OFFSET_COUNTER_SELECT) +#define PERIPHERALS_COUNTER_VALUE (PERIPHERALS_APERTURE_BASE_ADDRESS + PERIPHERALS_ADDRESS_OFFSET_COUNTER) + +#define PERIPHERALS_COUNTER_SELECT_CLOCKCYCLE_COUNTER 0x00 +#define PERIPHERALS_COUNTER_SELECT_INSTRUCTION_COUNTER 0x01 +#define PERIPHERALS_COUNTER_SELECT_IRQ_COUNTER 0x02 +#define PERIPHERALS_COUNTER_SELECT_NMI_COUNTER 0x03 +#define PERIPHERALS_COUNTER_SELECT_WALLCLOCK_TIME 0x80 +#define PERIPHERALS_COUNTER_SELECT_WALLCLOCK_TIME_SPLIT 0x81 + +typedef struct { + /* The invisible counters that keep processor state. */ + uint64_t ClockCycles; + uint64_t CpuInstructions; + uint64_t IrqEvents; + uint64_t NmiEvents; + /* The 'latched_...' fields below hold values that are sampled upon a write + * to the PERIPHERALS_COUNTER_LATCH address. + * One of these will be visible (read only) through an eight-byte aperture. + * The purpose of these latched registers is to read 64-bit values one byte + * at a time, without having to worry that their content will change along + * the way. + */ + uint64_t LatchedClockCycles; + uint64_t LatchedCpuInstructions; + uint64_t LatchedIrqEvents; + uint64_t LatchedNmiEvents; + uint64_t LatchedWallclockTime; + uint64_t LatchedWallclockTimeSplit; + /* Select which of the six latched registers will be visible. + * This is a single byte, read/write register, accessible via address + * PERIPHERALS_COUNTER_SELECT. If a non-existent latch register is selected, + * the PERIPHERALS_COUNTER_VALUE will be zero. + */ + uint8_t LatchedValueSelected; +} CounterPeripheral; + +/* Declarations for the SIMCONTROL peripheral. */ + +#define PERIPHERALS_SIMCONTROL_ADDRESS_OFFSET_CPUMODE 0x0A +#define PERIPHERALS_SIMCONTROL_ADDRESS_OFFSET_TRACEMODE 0x0B + +#define PERIPHERALS_SIMCONTROL_CPUMODE (PERIPHERALS_APERTURE_BASE_ADDRESS + PERIPHERALS_SIMCONTROL_ADDRESS_OFFSET_CPUMODE) +#define PERIPHERALS_SIMCONTROL_TRACEMODE (PERIPHERALS_APERTURE_BASE_ADDRESS + PERIPHERALS_SIMCONTROL_ADDRESS_OFFSET_TRACEMODE) + +/* Declare the 'Sim65Peripherals' type and its single instance 'Peripherals'. */ + +typedef struct { + /* State of the peripherals available in sim65. */ + CounterPeripheral Counter; +} Sim65Peripherals; + +extern Sim65Peripherals Peripherals; + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +void PeripheralsWriteByte (uint8_t Addr, uint8_t Val); +/* Write a byte to a memory location in the peripheral address aperture. */ + + +uint8_t PeripheralsReadByte (uint8_t Addr); +/* Read a byte from a memory location in the peripheral address aperture. */ + + +void PeripheralsInit (void); +/* Initialize the peripherals. */ + + + +/* End of peripherals.h */ + +#endif diff --git a/src/sim65/trace.c b/src/sim65/trace.c new file mode 100644 index 000000000..ef1edd0bf --- /dev/null +++ b/src/sim65/trace.c @@ -0,0 +1,1157 @@ +/*****************************************************************************/ +/* */ +/* trace.c */ +/* */ +/* Instruction tracing functionality sim65 6502 simulator */ +/* */ +/* */ +/* */ +/* (C) 2025, Sidney Cadot */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed 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. */ +/* */ +/*****************************************************************************/ + +#include <stdio.h> +#include <stdlib.h> +#include <stdbool.h> +#include <inttypes.h> + +#include "6502.h" +#include "memory.h" +#include "trace.h" +#include "peripherals.h" + +/* Current Trace Mode. Tracing is off by default, and needs to be explicitly enabled. */ +uint8_t TraceMode = TRACE_DISABLED; + +/* CC65 stack pointer */ +uint8_t StackPointerZPageAddress; + +/* 6502, 65C02 addressing modes. */ +typedef enum { + ILLEGAL, + IMPLIED, + ACCUMULATOR, + IMMEDIATE, + REL, + ZP, + ZP_X, + ZP_Y, + ZP_IND, + ZP_X_IND, + ZP_IND_Y, + ZP_REL, + ABS, + ABS_X, + ABS_Y, + ABS_IND, + ABS_X_IND +} AddressingMode; + +/* Info for a specific opcode and addressing mode, for a specific CPU type. */ +typedef struct { + const char * mnemonic; + AddressingMode adrmode; +} InstructionInfo; + +/* Information for standard 6502 opcodes. */ +static InstructionInfo II_6502[256] = { + { "brk" , IMPLIED }, /* 0x00 to 0x0f */ + { "ora" , ZP_X_IND }, + { "???" , ILLEGAL }, + { "???" , ILLEGAL }, + { "???" , ILLEGAL }, + { "ora" , ZP }, + { "asl" , ZP }, + { "???" , ILLEGAL }, + { "php" , IMPLIED }, + { "ora" , IMMEDIATE }, + { "asl" , ACCUMULATOR }, + { "???" , ILLEGAL }, + { "???" , ILLEGAL }, + { "ora" , ABS }, + { "asl" , ABS }, + { "???" , ILLEGAL }, + + { "bpl" , REL }, /* 0x10 to 0x1f */ + { "ora" , ZP_IND_Y }, + { "???" , ILLEGAL }, + { "???" , ILLEGAL }, + { "???" , ILLEGAL }, + { "ora" , ZP_X }, + { "asl" , ZP_X }, + { "???" , ILLEGAL }, + { "clc" , IMPLIED }, + { "ora" , ABS_Y }, + { "???" , ILLEGAL }, + { "???" , ILLEGAL }, + { "???" , ILLEGAL }, + { "ora" , ABS_X }, + { "asl" , ABS_X }, + { "???" , ILLEGAL }, + + { "jsr" , ABS }, /* 0x20 to 0x2f */ + { "and" , ZP_X_IND }, + { "???" , ILLEGAL }, + { "???" , ILLEGAL }, + { "bit" , ZP }, + { "and" , ZP }, + { "rol" , ZP }, + { "???" , ILLEGAL }, + { "plp" , IMPLIED }, + { "and" , IMMEDIATE }, + { "rol" , ACCUMULATOR }, + { "???" , ILLEGAL }, + { "bit" , ABS }, + { "and" , ABS }, + { "rol" , ABS }, + { "???" , ILLEGAL }, + + { "bmi" , REL }, /* 0x30 to 0x3f */ + { "and" , ZP_IND_Y }, + { "???" , ILLEGAL }, + { "???" , ILLEGAL }, + { "???" , ILLEGAL }, + { "and" , ZP_X }, + { "rol" , ZP_X }, + { "???" , ILLEGAL }, + { "sec" , IMPLIED }, + { "and" , ABS_Y }, + { "???" , ILLEGAL }, + { "???" , ILLEGAL }, + { "???" , ILLEGAL }, + { "and" , ABS_X }, + { "rol" , ABS_X }, + { "???" , ILLEGAL }, + + { "rti" , IMPLIED }, /* 0x40 to 0x4f */ + { "eor" , ZP_X_IND }, + { "???" , ILLEGAL }, + { "???" , ILLEGAL }, + { "???" , ILLEGAL }, + { "eor" , ZP }, + { "lsr" , ZP }, + { "???" , ILLEGAL }, + { "pha" , IMPLIED }, + { "eor" , IMMEDIATE }, + { "lsr" , ACCUMULATOR }, + { "???" , ILLEGAL }, + { "jmp" , ABS }, + { "eor" , ABS }, + { "lsr" , ABS }, + { "???" , ILLEGAL }, + + { "bvc" , REL }, /* 0x50 to 0x5f */ + { "eor" , ZP_IND_Y }, + { "???" , ILLEGAL }, + { "???" , ILLEGAL }, + { "???" , ILLEGAL }, + { "eor" , ZP_X }, + { "lsr" , ZP_X }, + { "???" , ILLEGAL }, + { "cli" , IMPLIED }, + { "eor" , ABS_Y }, + { "???" , ILLEGAL }, + { "???" , ILLEGAL }, + { "???" , ILLEGAL }, + { "eor" , ABS_X }, + { "lsr" , ABS_X }, + { "???" , ILLEGAL }, + + { "rts" , IMPLIED }, /* 0x60 to 0x6f */ + { "adc" , ZP_X_IND }, + { "???" , ILLEGAL }, + { "???" , ILLEGAL }, + { "???" , ILLEGAL }, + { "adc" , ZP }, + { "ror" , ZP }, + { "???" , ILLEGAL }, + { "pla" , IMPLIED }, + { "adc" , IMMEDIATE }, + { "ror" , ACCUMULATOR }, + { "???" , ILLEGAL }, + { "jmp" , ABS_IND }, + { "adc" , ABS }, + { "ror" , ABS }, + { "???" , ILLEGAL }, + + { "bvs" , REL }, /* 0x70 to 0x7f */ + { "adc" , ZP_IND_Y }, + { "???" , ILLEGAL }, + { "???" , ILLEGAL }, + { "???" , ILLEGAL }, + { "adc" , ZP_X }, + { "ror" , ZP_X }, + { "???" , ILLEGAL }, + { "sei" , IMPLIED }, + { "adc" , ABS_Y }, + { "???" , ILLEGAL }, + { "???" , ILLEGAL }, + { "???" , ILLEGAL }, + { "adc" , ABS_X }, + { "ror" , ABS_X }, + { "???" , ILLEGAL }, + + { "???" , ILLEGAL }, /* 0x80 to 0x8f */ + { "sta" , ZP_X_IND }, + { "???" , ILLEGAL }, + { "???" , ILLEGAL }, + { "sty" , ZP }, + { "sta" , ZP }, + { "stx" , ZP }, + { "???" , ILLEGAL }, + { "dey" , IMPLIED }, + { "???" , ILLEGAL }, + { "txa" , IMPLIED }, + { "???" , ILLEGAL }, + { "sty" , ABS }, + { "sta" , ABS }, + { "stx" , ABS }, + { "???" , ILLEGAL }, + + { "bcc" , REL }, /* 0x90 to 0x9f */ + { "sta" , ZP_IND_Y }, + { "???" , ILLEGAL }, + { "???" , ILLEGAL }, + { "sty" , ZP_X }, + { "sta" , ZP_X }, + { "stx" , ZP_Y }, + { "???" , ILLEGAL }, + { "tya" , IMPLIED }, + { "sta" , ABS_Y }, + { "txs" , IMPLIED }, + { "???" , ILLEGAL }, + { "???" , ILLEGAL }, + { "sta" , ABS_X }, + { "???" , ILLEGAL }, + { "???" , ILLEGAL }, + + { "ldy" , IMMEDIATE }, /* 0xa0 to 0xaf */ + { "lda" , ZP_X_IND }, + { "ldx" , IMMEDIATE }, + { "???" , ILLEGAL }, + { "ldy" , ZP }, + { "lda" , ZP }, + { "ldx" , ZP }, + { "???" , ILLEGAL }, + { "tay" , IMPLIED }, + { "lda" , IMMEDIATE }, + { "tax" , IMPLIED }, + { "???" , ILLEGAL }, + { "ldy" , ABS }, + { "lda" , ABS }, + { "ldx" , ABS }, + { "???" , ILLEGAL }, + + { "bcs" , REL }, /* 0xb0 to 0xbf */ + { "lda" , ZP_IND_Y }, + { "???" , ILLEGAL }, + { "???" , ILLEGAL }, + { "ldy" , ZP_X }, + { "lda" , ZP_X }, + { "ldx" , ZP_Y }, + { "???" , ILLEGAL }, + { "clv" , IMPLIED }, + { "lda" , ABS_Y }, + { "tsx" , IMPLIED }, + { "???" , ILLEGAL }, + { "ldy" , ABS_X }, + { "lda" , ABS_X }, + { "ldx" , ABS_Y }, + { "???" , ILLEGAL }, + + { "cpy" , IMMEDIATE }, /* 0xc0 to 0xcf */ + { "cmp" , ZP_X_IND }, + { "???" , ILLEGAL }, + { "???" , ILLEGAL }, + { "cpy" , ZP }, + { "cmp" , ZP }, + { "dec" , ZP }, + { "???" , ILLEGAL }, + { "iny" , IMPLIED }, + { "cmp" , IMMEDIATE }, + { "dex" , IMPLIED }, + { "???" , ILLEGAL }, + { "cpy" , ABS }, + { "cmp" , ABS }, + { "dec" , ABS }, + { "???" , ILLEGAL }, + + { "bne" , REL }, /* 0xd0 to 0xdf */ + { "cmp" , ZP_IND_Y }, + { "???" , ILLEGAL }, + { "???" , ILLEGAL }, + { "???" , ILLEGAL }, + { "cmp" , ZP_X }, + { "dec" , ZP_X }, + { "???" , ILLEGAL }, + { "cld" , IMPLIED }, + { "cmp" , ABS_Y }, + { "???" , ILLEGAL }, + { "???" , ILLEGAL }, + { "???" , ILLEGAL }, + { "cmp" , ABS_X }, + { "dec" , ABS_X }, + { "???" , ILLEGAL }, + + { "cpx" , IMMEDIATE }, /* 0xe0 to 0xef */ + { "sbc" , ZP_X_IND }, + { "???" , ILLEGAL }, + { "???" , ILLEGAL }, + { "cpx" , ZP }, + { "sbc" , ZP }, + { "inc" , ZP }, + { "???" , ILLEGAL }, + { "inx" , IMPLIED }, + { "sbc" , IMMEDIATE }, + { "nop" , IMPLIED }, + { "???" , ILLEGAL }, + { "cpx" , ABS }, + { "sbc" , ABS }, + { "inc" , ABS }, + { "???" , ILLEGAL }, + + { "beq" , REL }, /* 0xf0 to 0xff */ + { "sbc" , ZP_IND_Y }, + { "???" , ILLEGAL }, + { "???" , ILLEGAL }, + { "???" , ILLEGAL }, + { "sbc" , ZP_X }, + { "inc" , ZP_X }, + { "???" , ILLEGAL }, + { "sed" , IMPLIED }, + { "sbc" , ABS_Y }, + { "???" , ILLEGAL }, + { "???" , ILLEGAL }, + { "???" , ILLEGAL }, + { "sbc" , ABS_X }, + { "inc" , ABS_X }, + { "???" , ILLEGAL } +}; + +/* Information for 65C02 opcodes. */ +static InstructionInfo II_65C02[256] = { + { "brk" , IMPLIED }, /* 0x00 to 0x0f */ + { "ora" , ZP_X_IND }, + { "nop" , IMMEDIATE }, + { "nop" , IMPLIED }, + { "tsb" , ZP }, + { "ora" , ZP }, + { "asl" , ZP }, + { "rmb0" , ZP }, + { "php" , IMPLIED }, + { "ora" , IMMEDIATE }, + { "asl" , ACCUMULATOR }, + { "nop" , IMPLIED }, + { "tsb" , ABS }, + { "ora" , ABS }, + { "asl" , ABS }, + { "bbr0" , ZP_REL }, + + { "bpl" , REL }, /* 0x10 to 0x1f */ + { "ora" , ZP_IND_Y }, + { "ora" , ZP_IND }, + { "nop" , IMPLIED }, + { "trb" , ZP }, + { "ora" , ZP_X }, + { "asl" , ZP_X }, + { "rmb1" , ZP }, + { "clc" , IMPLIED }, + { "ora" , ABS_Y }, + { "inc" , ACCUMULATOR }, + { "nop" , IMPLIED }, + { "trb" , ABS }, + { "ora" , ABS_X }, + { "asl" , ABS_X }, + { "bbr1" , ZP_REL }, + + { "jsr" , ABS }, /* 0x20 to 0x2f */ + { "and" , ZP_X_IND }, + { "nop" , IMMEDIATE }, + { "nop" , IMPLIED }, + { "bit" , ZP }, + { "and" , ZP }, + { "rol" , ZP }, + { "rmb2" , ZP }, + { "plp" , IMPLIED }, + { "and" , IMMEDIATE }, + { "rol" , ACCUMULATOR }, + { "nop" , IMPLIED }, + { "bit" , ABS }, + { "and" , ABS }, + { "rol" , ABS }, + { "bbr2" , ZP_REL }, + + { "bmi" , REL }, /* 0x30 to 0x3f */ + { "and" , ZP_IND_Y }, + { "and" , ZP_IND }, + { "nop" , IMPLIED }, + { "bit" , ZP_X }, + { "and" , ZP_X }, + { "rol" , ZP_X }, + { "rmb3" , ZP }, + { "sec" , IMPLIED }, + { "and" , ABS_Y }, + { "dec" , ACCUMULATOR }, + { "nop" , IMPLIED }, + { "bit" , ABS_X }, + { "and" , ABS_X }, + { "rol" , ABS_X }, + { "bbr3" , ZP_REL }, + + { "rti" , IMPLIED }, /* 0x40 to 0x4f */ + { "eor" , ZP_X_IND }, + { "nop" , IMMEDIATE }, + { "nop" , ILLEGAL }, + { "nop" , ZP }, + { "eor" , ZP }, + { "lsr" , ZP }, + { "rmb4" , ZP }, + { "pha" , IMPLIED }, + { "eor" , IMMEDIATE }, + { "lsr" , ACCUMULATOR }, + { "nop" , IMPLIED }, + { "jmp" , ABS }, + { "eor" , ABS }, + { "lsr" , ABS }, + { "bbr4" , ZP_REL }, + + { "bvc" , REL }, /* 0x50 to 0x5f */ + { "eor" , ZP_IND_Y }, + { "eor" , ZP_IND }, + { "nop" , IMPLIED }, + { "nop" , ZP_X }, + { "eor" , ZP_X }, + { "lsr" , ZP_X }, + { "rmb5" , ZP }, + { "cli" , IMPLIED }, + { "eor" , ABS_Y }, + { "phy" , IMPLIED }, + { "nop" , IMPLIED }, + { "nop" , ABS }, + { "eor" , ABS_X }, + { "lsr" , ABS_X }, + { "bbr5" , ZP_REL }, + + { "rts" , IMPLIED }, /* 0x60 to 0x6f */ + { "adc" , ZP_X_IND }, + { "nop" , IMMEDIATE }, + { "nop" , IMPLIED }, + { "stz" , ZP }, + { "adc" , ZP }, + { "ror" , ZP }, + { "rmb6" , ZP }, + { "pla" , IMPLIED }, + { "adc" , IMMEDIATE }, + { "ror" , ACCUMULATOR }, + { "nop" , IMPLIED }, + { "jmp" , ABS_IND }, + { "adc" , ABS }, + { "ror" , ABS }, + { "bbr6" , ZP_REL }, + + { "bvs" , REL }, /* 0x70 to 0x7f */ + { "adc" , ZP_IND_Y }, + { "adc" , ZP_IND }, + { "nop" , IMPLIED }, + { "stz" , ZP_X }, + { "adc" , ZP_X }, + { "ror" , ZP_X }, + { "rmb7" , ZP }, + { "sei" , IMPLIED }, + { "adc" , ABS_Y }, + { "ply" , IMPLIED }, + { "nop" , IMPLIED }, + { "jmp" , ABS_X_IND }, + { "adc" , ABS_X }, + { "ror" , ABS_X }, + { "bbr7" , ZP_REL }, + + { "bra" , REL }, /* 0x80 to 0x8f */ + { "sta" , ZP_X_IND }, + { "nop" , IMMEDIATE }, + { "nop" , IMPLIED }, + { "sty" , ZP }, + { "sta" , ZP }, + { "stx" , ZP }, + { "smb0" , ZP }, + { "dey" , IMPLIED }, + { "bit" , IMMEDIATE }, + { "txa" , IMPLIED }, + { "nop" , IMPLIED }, + { "sty" , ABS }, + { "sta" , ABS }, + { "stx" , ABS }, + { "bbs0" , ZP_REL }, + + { "bcc" , REL }, /* 0x90 to 0x9f */ + { "sta" , ZP_IND_Y }, + { "sta" , ZP_IND }, + { "nop" , IMPLIED }, + { "sty" , ZP_X }, + { "sta" , ZP_X }, + { "stx" , ZP_Y }, + { "smb1" , ZP }, + { "tya" , IMPLIED }, + { "sta" , ABS_Y }, + { "txs" , IMPLIED }, + { "nop" , IMPLIED }, + { "stz" , ABS }, + { "sta" , ABS_X }, + { "stz" , ABS_X }, + { "bbs1" , ZP_REL }, + + { "ldy" , IMMEDIATE }, /* 0xa0 to 0xaf */ + { "lda" , ZP_X_IND }, + { "ldx" , IMMEDIATE }, + { "nop" , IMPLIED }, + { "ldy" , ZP }, + { "lda" , ZP }, + { "ldx" , ZP }, + { "smb2" , ZP }, + { "tay" , IMPLIED }, + { "lda" , IMMEDIATE }, + { "tax" , IMPLIED }, + { "nop" , IMPLIED }, + { "ldy" , ABS }, + { "lda" , ABS }, + { "ldx" , ABS }, + { "bbs2" , ZP_REL }, + + { "bcs" , REL }, /* 0xb0 to 0xbf */ + { "lda" , ZP_IND_Y }, + { "lda" , ZP_IND }, + { "nop" , IMPLIED }, + { "ldy" , ZP_X }, + { "lda" , ZP_X }, + { "ldx" , ZP_Y }, + { "smb3" , ZP }, + { "clv" , IMPLIED }, + { "lda" , ABS_Y }, + { "tsx" , IMPLIED }, + { "nop" , IMPLIED }, + { "ldy" , ABS_X }, + { "lda" , ABS_X }, + { "ldx" , ABS_Y }, + { "bbs3" , ZP_REL }, + + { "cpy" , IMMEDIATE }, /* 0xc0 to 0xcf */ + { "cmp" , ZP_X_IND }, + { "nop" , IMMEDIATE }, + { "nop" , IMPLIED }, + { "cpy" , ZP }, + { "cmp" , ZP }, + { "dec" , ZP }, + { "smb4" , ZP }, + { "iny" , IMPLIED }, + { "cmp" , IMMEDIATE }, + { "dex" , IMPLIED }, + { "wai" , IMPLIED }, + { "cpy" , ABS }, + { "cmp" , ABS }, + { "dec" , ABS }, + { "bbs4" , ZP_REL }, + + { "bne" , REL }, /* 0xd0 to 0xdf */ + { "cmp" , ZP_IND_Y }, + { "cmp" , ZP_IND }, + { "nop" , IMPLIED }, + { "nop" , ZP_X }, + { "cmp" , ZP_X }, + { "dec" , ZP_X }, + { "smb5" , ZP }, + { "cld" , IMPLIED }, + { "cmp" , ABS_Y }, + { "phx" , IMPLIED }, + { "stp" , IMPLIED }, + { "nop" , ABS }, + { "cmp" , ABS_X }, + { "dec" , ABS_X }, + { "bbs5" , ZP_REL }, + + { "cpx" , IMMEDIATE }, /* 0xe0 to 0xef */ + { "sbc" , ZP_X_IND }, + { "nop" , IMMEDIATE }, + { "nop" , IMPLIED }, + { "cpx" , ZP }, + { "sbc" , ZP }, + { "inc" , ZP }, + { "smb6" , ZP }, + { "inx" , IMPLIED }, + { "sbc" , IMMEDIATE }, + { "nop" , IMPLIED }, + { "nop" , IMPLIED }, + { "cpx" , ABS }, + { "sbc" , ABS }, + { "inc" , ABS }, + { "bbs6" , ZP_REL }, + + { "beq" , REL }, /* 0xf0 to 0xff */ + { "sbc" , ZP_IND_Y }, + { "sbc" , ZP_IND }, + { "nop" , IMPLIED }, + { "nop" , ZP_X }, + { "sbc" , ZP_X }, + { "inc" , ZP_X }, + { "smb7" , ZP }, + { "sed" , IMPLIED }, + { "sbc" , ABS_Y }, + { "plx" , IMPLIED }, + { "nop" , IMPLIED }, + { "nop" , ABS }, + { "sbc" , ABS_X }, + { "inc" , ABS_X }, + { "bbs7" , ZP_REL } +}; + +/* Information for 6502X (6502 with undocumented instructions) opcodes. */ +static InstructionInfo II_6502X[256] = { + { "brk" , IMPLIED }, /* 0x00 to 0x0f */ + { "ora" , ZP_X_IND }, + { "jam" , IMPLIED }, + { "slo" , ZP_X_IND }, + { "nop" , ZP }, + { "ora" , ZP }, + { "asl" , ZP }, + { "slo" , ZP }, + { "php" , IMPLIED }, + { "ora" , IMMEDIATE }, + { "asl" , ACCUMULATOR }, + { "anc" , IMMEDIATE }, + { "nop" , ABS }, + { "ora" , ABS }, + { "asl" , ABS }, + { "slo" , ABS }, + + { "bpl" , REL }, /* 0x10 to 0x1f */ + { "ora" , ZP_IND_Y }, + { "jam" , IMPLIED }, + { "slo" , ZP_IND_Y }, + { "nop" , ZP_X }, + { "ora" , ZP_X }, + { "asl" , ZP_X }, + { "slo" , ZP }, + { "clc" , IMPLIED }, + { "ora" , ABS_Y }, + { "nop" , IMPLIED }, + { "slo" , ABS_Y }, + { "*nop" , ABS_X }, + { "ora" , ABS_X }, + { "asl" , ABS_X }, + { "slo" , ABS_X }, + + { "jsr" , ABS }, /* 0x20 to 0x2f */ + { "and" , ZP_X_IND }, + { "jam" , IMPLIED }, + { "rla" , ZP_X_IND }, + { "bit" , ZP }, + { "and" , ZP }, + { "rol" , ZP }, + { "rla" , ZP }, + { "plp" , IMPLIED }, + { "and" , IMMEDIATE }, + { "rol" , ACCUMULATOR }, + { "anc" , IMMEDIATE }, + { "bit" , ABS }, + { "and" , ABS }, + { "rol" , ABS }, + { "rla" , ABS }, + + { "bmi" , REL }, /* 0x30 to 0x3f */ + { "and" , ZP_IND_Y }, + { "jam" , IMPLIED }, + { "rla" , ZP_IND_Y }, + { "nop" , ZP_X }, + { "and" , ZP_X }, + { "rol" , ZP_X }, + { "rla" , ZP_X }, + { "sec" , IMPLIED }, + { "and" , ABS_Y }, + { "nop" , IMPLIED }, + { "rla" , ABS_Y }, + { "nop" , ABS_X }, + { "and" , ABS_X }, + { "rol" , ABS_X }, + { "rla" , ABS_X }, + + { "rti" , IMPLIED }, /* 0x40 to 0x4f */ + { "eor" , ZP_X_IND }, + { "jam" , IMPLIED }, + { "sre" , ZP_X_IND }, + { "nop" , ZP }, + { "eor" , ZP }, + { "lsr" , ZP }, + { "sre" , ZP }, + { "pha" , IMPLIED }, + { "eor" , IMMEDIATE }, + { "lsr" , ACCUMULATOR }, + { "alr" , IMMEDIATE }, + { "jmp" , ABS }, + { "eor" , ABS }, + { "lsr" , ABS }, + { "sre" , ABS }, + + { "bvc" , REL }, /* 0x50 to 0x5f */ + { "eor" , ZP_IND_Y }, + { "jam" , IMPLIED }, + { "sre" , ZP_IND_Y }, + { "nop" , ZP_X }, + { "eor" , ZP_X }, + { "lsr" , ZP_X }, + { "sre" , ZP_X }, + { "cli" , IMPLIED }, + { "eor" , ABS_Y }, + { "nop" , IMPLIED }, + { "sre" , ABS_Y }, + { "nop" , ABS_X }, + { "eor" , ABS_X }, + { "lsr" , ABS_X }, + { "sre" , ABS_X }, + + { "rts" , IMPLIED }, /* 0x60 to 0x6f */ + { "adc" , ZP_X_IND }, + { "jam" , IMPLIED }, + { "rra" , ZP_X_IND }, + { "nop" , ZP }, + { "adc" , ZP }, + { "ror" , ZP }, + { "rra" , ZP }, + { "pla" , IMPLIED }, + { "adc" , IMMEDIATE }, + { "ror" , ACCUMULATOR }, + { "arr" , IMMEDIATE }, + { "jmp" , ABS_IND }, + { "adc" , ABS }, + { "ror" , ABS }, + { "rra" , ABS }, + + { "bvs" , REL }, /* 0x70 to 0x7f */ + { "adc" , ZP_IND_Y }, + { "jam" , IMPLIED }, + { "sre" , ZP_IND_Y }, + { "nop" , ZP_X }, + { "adc" , ZP_X }, + { "ror" , ZP_X }, + { "rra" , ZP_X }, + { "sei" , IMPLIED }, + { "adc" , ABS_Y }, + { "nop" , IMPLIED }, + { "rra" , ABS_Y }, + { "nop" , ABS_X }, + { "adc" , ABS_X }, + { "ror" , ABS_X }, + { "rra" , ABS_X }, + + { "nop" , IMMEDIATE }, /* 0x80 to 0x8f */ + { "sta" , ZP_X_IND }, + { "nop" , IMMEDIATE }, + { "sax" , ZP_X_IND }, + { "sty" , ZP }, + { "sta" , ZP }, + { "stx" , ZP }, + { "sax" , ZP }, + { "dey" , IMPLIED }, + { "nop" , IMMEDIATE }, + { "txa" , IMPLIED }, + { "ane" , IMMEDIATE }, + { "sty" , ABS }, + { "sta" , ABS }, + { "stx" , ABS }, + { "sax" , ABS }, + + { "bcc" , REL }, /* 0x90 to 0x9f */ + { "sta" , ZP_IND_Y }, + { "jam" , IMPLIED }, + { "sha" , ZP_IND_Y }, + { "sty" , ZP_X }, + { "sta" , ZP_X }, + { "stx" , ZP_Y }, + { "sax" , ZP_Y }, + { "tya" , IMPLIED }, + { "sta" , ABS_Y }, + { "txs" , IMPLIED }, + { "tas" , ABS_Y }, + { "shy" , ABS_X }, + { "sta" , ABS_X }, + { "shx" , ABS_Y }, + { "sha" , ABS_Y }, + + { "ldy" , IMMEDIATE }, /* 0xa0 to 0xaf */ + { "lda" , ZP_X_IND }, + { "ldx" , IMMEDIATE }, + { "lax" , ZP_X_IND }, + { "ldy" , ZP }, + { "lda" , ZP }, + { "ldx" , ZP }, + { "lax" , ZP }, + { "tay" , IMPLIED }, + { "lda" , IMMEDIATE }, + { "tax" , IMPLIED }, + { "lax" , IMMEDIATE }, + { "ldy" , ABS }, + { "lda" , ABS }, + { "ldx" , ABS }, + { "lax" , ABS }, + + { "bcs" , REL }, /* 0xb0 to 0xbf */ + { "lda" , ZP_IND_Y }, + { "jam" , IMPLIED }, + { "lax" , ZP_IND_Y }, + { "ldy" , ZP_X }, + { "lda" , ZP_X }, + { "ldx" , ZP_Y }, + { "lax" , ZP_Y }, + { "clv" , IMPLIED }, + { "lda" , ABS_Y }, + { "tsx" , IMPLIED }, + { "las" , ABS_Y }, + { "ldy" , ABS_X }, + { "lda" , ABS_X }, + { "ldx" , ABS_Y }, + { "lax" , ABS_Y }, + + { "cpy" , IMMEDIATE }, /* 0xc0 to 0xcf */ + { "cmp" , ZP_X_IND }, + { "nop" , IMMEDIATE }, + { "dcp" , ZP_X_IND }, + { "cpy" , ZP }, + { "cmp" , ZP }, + { "dec" , ZP }, + { "dcp" , ZP }, + { "iny" , IMPLIED }, + { "cmp" , IMMEDIATE }, + { "dex" , IMPLIED }, + { "sbx" , IMMEDIATE }, + { "cpy" , ABS }, + { "cmp" , ABS }, + { "dec" , ABS }, + { "dcp" , ABS }, + + { "bne" , REL }, /* 0xd0 to 0xdf */ + { "cmp" , ZP_IND_Y }, + { "jam" , IMPLIED }, + { "dcp" , ZP_IND_Y }, + { "nop" , ZP_X }, + { "cmp" , ZP_X }, + { "dec" , ZP_X }, + { "dcp" , ZP_X }, + { "cld" , IMPLIED }, + { "cmp" , ABS_Y }, + { "nop" , IMPLIED }, + { "dcp" , ABS_Y }, + { "nop" , ABS_X }, + { "cmp" , ABS_X }, + { "dec" , ABS_X }, + { "dcp" , ABS_X }, + + { "cpx" , IMMEDIATE }, /* 0xe0 to 0xef */ + { "sbc" , ZP_X_IND }, + { "nop" , IMMEDIATE }, + { "isc" , ZP_X_IND }, + { "cpx" , ZP }, + { "sbc" , ZP }, + { "inc" , ZP }, + { "isc" , ZP }, + { "inx" , IMPLIED }, + { "sbc" , IMMEDIATE }, + { "nop" , IMPLIED }, + { "sbc" , IMMEDIATE }, + { "cpx" , ABS }, + { "sbc" , ABS }, + { "inc" , ABS }, + { "isc" , ABS }, + + { "beq" , REL }, /* 0xf0 to 0xff */ + { "sbc" , ZP_IND_Y }, + { "jam" , IMPLIED }, + { "isc" , ZP_IND_Y }, + { "nop" , ZP_X }, + { "sbc" , ZP_X }, + { "inc" , ZP_X }, + { "isc" , ZP_X }, + { "sed" , IMPLIED }, + { "sbc" , ABS_Y }, + { "nop" , IMPLIED }, + { "isc" , ABS_Y }, + { "nop" , ABS_X }, + { "sbc" , ABS_X }, + { "inc" , ABS_X }, + { "isc" , ABS_X } +}; + +static InstructionInfo * II[3] = { II_6502, II_65C02, II_6502X }; + +static unsigned GetInstructionLength (uint8_t opcode) +/* Get the number of bytes in the full instruction. Depends on the addressing mode. */ +{ + switch (II[CPU][opcode].adrmode) { + case ILLEGAL: + case IMPLIED: + case ACCUMULATOR: + return 1; + case IMMEDIATE: + case REL: + case ZP: + case ZP_X: + case ZP_Y: + case ZP_IND: + case ZP_X_IND: + case ZP_IND_Y: + return 2; + case ZP_REL: + case ABS: + case ABS_X: + case ABS_Y: + case ABS_IND: + case ABS_X_IND: + return 3; + } + + /* We should never get here. */ + return -1; +} + + + +static char * PrintAssemblyInstruction (char * ptr) +/* Print assembly instruction: mnemonic and addres-mode specific operand(s). */ +{ + uint8_t opcode; + + /* Print the instruction starting at the current program counter. */ + + opcode = MemReadByte (Regs.PC); + + ptr += sprintf (ptr, "%-4s ", II[CPU][opcode].mnemonic); + + switch (II[CPU][opcode].adrmode) { + case IMPLIED: + case ILLEGAL: + break; + case ACCUMULATOR: + ptr += sprintf (ptr, "A"); + break; + case IMMEDIATE: + ptr += sprintf (ptr, "#$%02X", MemReadByte (Regs.PC + 1)); + break; + case REL: + ptr += sprintf (ptr, "$%04X", Regs.PC + 2 + (int8_t)MemReadByte (Regs.PC + 1)); + break; + case ZP: + ptr += sprintf (ptr, "$%02X", MemReadByte (Regs.PC + 1)); + break; + case ZP_X: + ptr += sprintf (ptr, "$%02X,X", MemReadByte (Regs.PC + 1)); + break; + case ZP_Y: + ptr += sprintf (ptr, "$%02X,Y", MemReadByte (Regs.PC + 1)); + break; + case ZP_IND: + ptr += sprintf (ptr, "($%02X)", MemReadByte (Regs.PC + 1)); + break; + case ZP_X_IND: + ptr += sprintf (ptr, "($%02X,X)", MemReadByte (Regs.PC + 1)); + break; + case ZP_IND_Y: + ptr += sprintf (ptr, "($%02X),Y", MemReadByte (Regs.PC + 1)); + break; + case ZP_REL: + ptr += sprintf (ptr, "$%02X,$%04X", MemReadByte (Regs.PC + 1), Regs.PC + 3 + (int8_t)MemReadByte (Regs.PC + 2)); + break; + case ABS: + ptr += sprintf (ptr, "$%04X", MemReadWord (Regs.PC + 1)); + break; + case ABS_IND: + ptr += sprintf (ptr, "($%04X)", MemReadWord (Regs.PC + 1)); + break; + case ABS_X: + ptr += sprintf (ptr, "$%04X,X", MemReadWord (Regs.PC + 1)); + break; + case ABS_X_IND: + ptr += sprintf (ptr, "($%04X,X)", MemReadWord (Regs.PC + 1)); + break; + case ABS_Y: + ptr += sprintf (ptr, "$%04X,Y", MemReadWord (Regs.PC + 1)); + break; + } + + return ptr; +} + + + +static void PrintTraceInstructionOrInterrupt (const char * InterruptType) +{ + char traceline[200]; + char * traceline_ptr = traceline; + uint8_t opcode; + unsigned k, num_bytes; + + if (TraceMode & TRACE_FIELD_INSTR_COUNTER) { + + if (traceline_ptr != traceline) { + /* Print field separator. */ + traceline_ptr += sprintf (traceline_ptr, " "); + } + + traceline_ptr += sprintf (traceline_ptr, "%12" PRIu64, Peripherals.Counter.CpuInstructions); + } + + if (TraceMode & TRACE_FIELD_CLOCK_COUNTER) { + + if (traceline_ptr != traceline) { + /* Print field separator. */ + traceline_ptr += sprintf (traceline_ptr, " "); + } + + traceline_ptr += sprintf (traceline_ptr, "%12" PRIu64, Peripherals.Counter.ClockCycles); + } + + if (TraceMode & TRACE_FIELD_PC) { + + if (traceline_ptr != traceline) { + /* Print field separator. */ + traceline_ptr += sprintf (traceline_ptr, " "); + } + + traceline_ptr += sprintf (traceline_ptr, "%04X", Regs.PC); + } + + if (TraceMode & TRACE_FIELD_INSTR_BYTES) { + + if (traceline_ptr != traceline) { + /* Print field separator. */ + traceline_ptr += sprintf (traceline_ptr, " "); + } + + if (InterruptType == NULL) + { + /* Get the opcode */ + opcode = MemReadByte (Regs.PC); + + /* How many bytes are in the full instruction? 1, 2 or 3. */ + num_bytes = GetInstructionLength (opcode); + } else { + num_bytes = 0; /* Consider interrupts as instructions that are inserted into the instruction stream. */ + } + + /* Print 0 to 3 bytes for the interrupt/instruction. */ + for (k = 0; k < 3; ++k) { + if (k != 0) { + *traceline_ptr++ = ' '; + } + if (k < num_bytes) { + traceline_ptr += sprintf (traceline_ptr, "%02X", MemReadByte (Regs.PC + k)); + } else { + traceline_ptr += sprintf (traceline_ptr, " "); + } + } + } + + if (TraceMode & TRACE_FIELD_INSTR_ASSEMBLY) { + + if (traceline_ptr != traceline) { + /* Print field separator. */ + traceline_ptr += sprintf (traceline_ptr, " "); + } + + char * save_ptr = traceline_ptr; + + if (InterruptType == NULL) { + traceline_ptr = PrintAssemblyInstruction (traceline_ptr); + } else { + /* Print interrupt message. */ + traceline_ptr += sprintf (traceline_ptr, "*** %s ***", InterruptType); + } + + /* Fill out the field to 16 characters */ + num_bytes = (unsigned)(traceline_ptr - save_ptr); + if (num_bytes < 16) { + traceline_ptr += sprintf (traceline_ptr, "%*s", 16 - num_bytes, ""); + } + } + + if (TraceMode & TRACE_FIELD_CPU_REGISTERS) { + + if (traceline_ptr != traceline) { + /* Print field separator. */ + traceline_ptr += sprintf (traceline_ptr, " "); + } + + traceline_ptr += sprintf (traceline_ptr, + "A=%02X X=%02X Y=%02X S=%02X Flags=%c%c%c%c%c%c", + Regs.AC, + Regs.XR, + Regs.YR, + Regs.SP, + (Regs.SR & SF) ? 'N' : 'n', + (Regs.SR & OF) ? 'V' : 'v', + (Regs.SR & DF) ? 'D' : 'd', + (Regs.SR & IF) ? 'I' : 'i', + (Regs.SR & ZF) ? 'Z' : 'z', + (Regs.SR & CF) ? 'C' : 'c' + ); + } + + if (TraceMode & TRACE_FIELD_CC65_SP) { + + if (traceline_ptr != traceline) { + /* Print field separator. */ + traceline_ptr += sprintf (traceline_ptr, " "); + } + + traceline_ptr += sprintf (traceline_ptr, + " SP=%04X", + MemReadZPWord (StackPointerZPageAddress) + ); + } + + if (traceline_ptr != traceline) { + puts (traceline); + } +} + + + +void TraceInit (uint8_t SPAddr) +{ + StackPointerZPageAddress = SPAddr; +} + + + +void PrintTraceNMI (void) +{ + PrintTraceInstructionOrInterrupt("NMI"); +} + + + +void PrintTraceIRQ (void) +{ + PrintTraceInstructionOrInterrupt("IRQ"); +} + + + +void PrintTraceInstruction (void) +{ + PrintTraceInstructionOrInterrupt(NULL); +} diff --git a/src/sim65/trace.h b/src/sim65/trace.h new file mode 100644 index 000000000..f982460d2 --- /dev/null +++ b/src/sim65/trace.h @@ -0,0 +1,89 @@ +/*****************************************************************************/ +/* */ +/* trace.h */ +/* */ +/* Instruction tracing functionality sim65 6502 simulator */ +/* */ +/* */ +/* */ +/* (C) 2025, Sidney Cadot */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed 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. */ +/* */ +/*****************************************************************************/ + + +#ifndef TRACE_H +#define TRACE_H + + +#include <stdint.h> + + +#include "6502.h" + +/* The trace mode is a bitfield that determines how trace lines are displayed. + * + * The value zero indicates that tracing is disabled (the default). + * + * In case TraceMode is not equal to zero, the value is interpreted as a bitfield: + * + * Bit Bit value Enables + * --- ----------- ------------------------------- + * 6 0x40 ( 64) Print the instruction counter. + * 5 0x20 ( 32) Print the clock cycle counter. + * 4 0x10 ( 16) Print the PC (program counter). + * 3 0x08 ( 8) Print the instruction bytes. + * 2 0x04 ( 4) Print the instruction assembly. + * 1 0x02 ( 2) Print the CPU registers. + * 0 0x01 ( 1) Print the CC65 stack pointer. + * + */ + +#define TRACE_FIELD_INSTR_COUNTER 0x40 +#define TRACE_FIELD_CLOCK_COUNTER 0x20 +#define TRACE_FIELD_PC 0x10 +#define TRACE_FIELD_INSTR_BYTES 0x08 +#define TRACE_FIELD_INSTR_ASSEMBLY 0x04 +#define TRACE_FIELD_CPU_REGISTERS 0x02 +#define TRACE_FIELD_CC65_SP 0x01 + +#define TRACE_DISABLED 0x00 +#define TRACE_ENABLE_FULL 0x7f + +/* Currently active tracing mode. */ +extern uint8_t TraceMode; + +void TraceInit (uint8_t SPAddr); +/* Initialize the trace subsystem. */ + +void PrintTraceNMI(void); +/* Print trace line for an NMI interrupt. */ + +void PrintTraceIRQ(void); +/* Print trace line for an IRQ interrupt. */ + +void PrintTraceInstruction (void); +/* Print trace line for the instruction at the currrent program counter. */ + + + +/* End of trace.h */ + +#endif diff --git a/src/sp65.vcxproj b/src/sp65.vcxproj index a9f0919a3..21816ee2b 100644 --- a/src/sp65.vcxproj +++ b/src/sp65.vcxproj @@ -5,10 +5,18 @@ <Configuration>Debug</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> <ProjectConfiguration Include="Release|Win32"> <Configuration>Release</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> </ItemGroup> <PropertyGroup Label="Globals"> <ProjectGuid>{4388D1AF-C7EA-4AD4-8E80-CA1FB7BF76BF}</ProjectGuid> @@ -21,15 +29,22 @@ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <UseDebugLibraries>true</UseDebugLibraries> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <UseDebugLibraries>true</UseDebugLibraries> + </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> <UseDebugLibraries>false</UseDebugLibraries> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <UseDebugLibraries>false</UseDebugLibraries> + </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <ImportGroup Label="ExtensionSettings"> </ImportGroup> <PropertyGroup Label="UserMacros" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> </PropertyGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> @@ -40,6 +55,15 @@ <SubSystem>Console</SubSystem> </Link> </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <PreprocessorDefinitions>_CONSOLE;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <DisableSpecificWarnings>4244;4267;%(DisableSpecificWarnings)</DisableSpecificWarnings> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + </Link> + </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ClCompile> <PreprocessorDefinitions>_CONSOLE;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> @@ -48,6 +72,15 @@ <SubSystem>Console</SubSystem> </Link> </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <PreprocessorDefinitions>_CONSOLE;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <DisableSpecificWarnings>4244;4267;%(DisableSpecificWarnings)</DisableSpecificWarnings> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + </Link> + </ItemDefinitionGroup> <ItemGroup> <ClCompile Include="sp65\asm.c" /> <ClCompile Include="sp65\attr.c" /> @@ -99,4 +132,4 @@ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <ImportGroup Label="ExtensionTargets"> </ImportGroup> -</Project> +</Project> \ No newline at end of file diff --git a/targettest/atari/mem.c b/targettest/atari/mem.c index bc70aded6..b15b215ed 100644 --- a/targettest/atari/mem.c +++ b/targettest/atari/mem.c @@ -21,6 +21,8 @@ unsigned int *MEMTOP = (unsigned int *)741; unsigned int *MEMLO = (unsigned int *)743; void *allocmem; +void code(void) { } + int main(void) { allocmem = malloc(257); @@ -35,7 +37,7 @@ int main(void) printf(" MEMLO = $%04X (%u)\n", *MEMLO, *MEMLO); printf(" ----------------------\n"); - printf(" main: $%04X (code)\n", &main); + printf(" code: $%04X (code)\n", &code); printf(" data: $%04X (data)\n", &data); printf(" _dos_type: $%04X (bss)\n", &_dos_type); printf(" allocmem: $%04X (dyn. data)\n", allocmem); diff --git a/targettest/pce/conio.c b/targettest/pce/conio.c index 55f828f26..819e601be 100644 --- a/targettest/pce/conio.c +++ b/targettest/pce/conio.c @@ -11,6 +11,8 @@ static char hex[16] = { "0123456789abcdef" }; static char charbuf[0x20]; static char colbuf[0x20]; +void func(void) { } + void main(void) { int stackvar = 42; @@ -65,7 +67,7 @@ void main(void) p[8],p[9],p[10],p[11],p[12],p[13],p[14],p[15] ); } - memcpy(p, main, i = 0); /* test that a zero length doesn't copy 64K */ + memcpy(p, func, i = 0); /* test that a zero length doesn't copy 64K */ gotoxy(0,ysize - 1); for (i = 0; i < xsize; ++i) { diff --git a/targettest/scanf-test.c b/targettest/scanf-test.c index f17b62294..e0ab95756 100644 --- a/targettest/scanf-test.c +++ b/targettest/scanf-test.c @@ -159,12 +159,14 @@ static void Pause(void) { #endif } +static void Nil() { } + int main(void) { long n0; unsigned t; int c, n1 = 12345, n2, n3; char s1[80], s2[80]; - void *p1 = main, *p2 = main, *p3 = main, *p4 = main; + void *p1 = Nil, *p2 = Nil, *p3 = Nil, *p4 = Nil; #ifndef USE_STDIO clrscr(); diff --git a/test/Makefile b/test/Makefile index 22e425c9c..495082fa4 100644 --- a/test/Makefile +++ b/test/Makefile @@ -12,9 +12,11 @@ endif WORKDIR = ../testwrk -.PHONY: test continue mostlyclean clean +.PHONY: test continue mostlyclean clean success_message -test: mostlyclean continue +test: + @$(MAKE) mostlyclean + @$(MAKE) continue continue: @$(MAKE) -C asm all @@ -23,8 +25,15 @@ continue: @$(MAKE) -C ref all @$(MAKE) -C err all @$(MAKE) -C standard all + @$(MAKE) -C standard_err all @$(MAKE) -C misc all @$(MAKE) -C todo all + @$(MAKE) success_message + +success_message: + $(info ###################################) + $(info ### validation suite successful ###) + $(info ###################################) mostlyclean: @$(MAKE) -C asm clean @@ -33,6 +42,7 @@ mostlyclean: @$(MAKE) -C ref clean @$(MAKE) -C err clean @$(MAKE) -C standard clean + @$(MAKE) -C standard_err clean @$(MAKE) -C misc clean @$(MAKE) -C todo clean diff --git a/test/asm/Makefile b/test/asm/Makefile index dea53f6b2..5b3bff3f8 100644 --- a/test/asm/Makefile +++ b/test/asm/Makefile @@ -12,23 +12,25 @@ endif WORKDIR = ../testwrk/asm -SUBDIRS = cpudetect opcodes listing val err misc - .PHONY: all continue mostlyclean clean all: mostlyclean continue -define CALL_template +continue: mostlyclean + @$(MAKE) -C cpudetect all + @$(MAKE) -C opcodes all + @$(MAKE) -C listing all + @$(MAKE) -C val all + @$(MAKE) -C err all + @$(MAKE) -C misc all -continue:: - @$(MAKE) -C $1 all - -mostlyclean:: - @$(MAKE) -C $1 clean - -endef - -$(foreach subdir,$(SUBDIRS),$(eval $(call CALL_template,$(subdir)))) +mostlyclean: + @$(MAKE) -C cpudetect clean + @$(MAKE) -C opcodes clean + @$(MAKE) -C listing clean + @$(MAKE) -C val clean + @$(MAKE) -C err clean + @$(MAKE) -C misc clean clean: mostlyclean @$(call RMDIR,$(WORKDIR)) diff --git a/test/asm/err/jmp-indirect-6502-error.s b/test/asm/err/jmp-indirect-6502-error.s new file mode 100644 index 000000000..aadc37b30 --- /dev/null +++ b/test/asm/err/jmp-indirect-6502-error.s @@ -0,0 +1,4 @@ +; test that jmp (indirect) on a page boundary will give an error for 6502 CPU + +.p02 +jmp ($10FF) diff --git a/test/asm/listing/060-ulabel.s b/test/asm/listing/060-ulabel.s new file mode 100644 index 000000000..f2e66da87 --- /dev/null +++ b/test/asm/listing/060-ulabel.s @@ -0,0 +1,25 @@ +; Test new-style (@:) and legacy-style (:) unnamed labels. +; Make sure that they have identical behavior. + +.ORG $0000 + +@: nop +: nop +.ASSERT @<< = $0000, error +.ASSERT @-- = $0000, error +.ASSERT :<< = $0000, error +.ASSERT :-- = $0000, error +.ASSERT @< = $0001, error +.ASSERT @- = $0001, error +.ASSERT :< = $0001, error +.ASSERT :- = $0001, error +.ASSERT @> = $0002, error +.ASSERT @+ = $0002, error +.ASSERT :> = $0002, error +.ASSERT :+ = $0002, error +.ASSERT @>> = $0003, error +.ASSERT @++ = $0003, error +.ASSERT :>> = $0003, error +.ASSERT :++ = $0003, error +@: nop +: nop diff --git a/test/asm/listing/070-include-macro.inc b/test/asm/listing/070-include-macro.inc new file mode 100644 index 000000000..0152a7965 --- /dev/null +++ b/test/asm/listing/070-include-macro.inc @@ -0,0 +1 @@ +foo: diff --git a/test/asm/listing/070-include-macro.s b/test/asm/listing/070-include-macro.s new file mode 100644 index 000000000..aad55cb52 --- /dev/null +++ b/test/asm/listing/070-include-macro.s @@ -0,0 +1,13 @@ +.macro IncludeFile FilePath + .proc bar + .include FilePath + .endproc +.endmacro + +IncludeFile "070-include-macro.inc" + +.ifdef bar::foo + .out "bar::foo is defined" +.else + .out "bar::foo is undefined" +.endif diff --git a/test/asm/listing/070-include-repeat.inc b/test/asm/listing/070-include-repeat.inc new file mode 100644 index 000000000..0eb22a710 --- /dev/null +++ b/test/asm/listing/070-include-repeat.inc @@ -0,0 +1 @@ +.out "include file" diff --git a/test/asm/listing/070-include-repeat.s b/test/asm/listing/070-include-repeat.s new file mode 100644 index 000000000..20170255f --- /dev/null +++ b/test/asm/listing/070-include-repeat.s @@ -0,0 +1,4 @@ +.repeat 3 + .include "070-include-repeat.inc" + .out "main file" +.endrepeat diff --git a/test/asm/listing/ref/070-include-macro.err-ref b/test/asm/listing/ref/070-include-macro.err-ref new file mode 100644 index 000000000..5faf98c4b --- /dev/null +++ b/test/asm/listing/ref/070-include-macro.err-ref @@ -0,0 +1 @@ +bar::foo is defined diff --git a/test/asm/listing/ref/070-include-repeat.err-ref b/test/asm/listing/ref/070-include-repeat.err-ref new file mode 100644 index 000000000..d42e70ee7 --- /dev/null +++ b/test/asm/listing/ref/070-include-repeat.err-ref @@ -0,0 +1,6 @@ +include file +main file +include file +main file +include file +main file diff --git a/test/asm/val/jmp-indirect-success.s b/test/asm/val/jmp-indirect-success.s new file mode 100644 index 000000000..592666576 --- /dev/null +++ b/test/asm/val/jmp-indirect-success.s @@ -0,0 +1,18 @@ +; test that jmp (indirect) on a page boundary will not give an error for non-6502 CPUs + +.pc02 +jmp ($10FF) + +.psc02 +jmp ($10FF) + +.p816 +jmp ($10FF) + +; main always returns success (the tested issue is only whether the assembly errors) +.import _exit +.export _main +_main: + lda #0 + tax + jmp _exit diff --git a/test/asm/val/struct.s b/test/asm/val/struct.s new file mode 100644 index 000000000..b6ef6f45d --- /dev/null +++ b/test/asm/val/struct.s @@ -0,0 +1,222 @@ +; test .struct and .union features + +.code + +; exit with 0 +.export _main +_main: + lda #0 + tax + rts + +; test storage allocator sizes and offsets + +.struct Storage + mb1 .byte + mb5 .byte 5 + mr1 .res 1 + mr5 .res 5 + mdb1 .dbyt + mdb5 .dbyt 5 + mw1 .word + mw5 .word 5 + ma1 .addr + ma5 .addr 5 + mf1 .faraddr + mf5 .faraddr 5 + mdw1 .dword + mdw5 .dword 5 +.endstruct + +.assert .sizeof(Storage::mb1) = 1, error, ".struct .byte member has unexpected .sizeof" +.assert .sizeof(Storage::mb5) = 5, error, ".struct .byte 5 member has unexpected .sizeof" +.assert .sizeof(Storage::mr1) = 1, error, ".struct .res 1 member has unexpected .sizeof" +.assert .sizeof(Storage::mr5) = 5, error, ".struct .res 5 member has unexpected .sizeof" +.assert .sizeof(Storage::mdb1) = 2, error, ".struct .dbyt member has unexpected .sizeof" +.assert .sizeof(Storage::mdb5) = 10, error, ".struct .dbyt 5 member has unexpected .sizeof" +.assert .sizeof(Storage::mw1) = 2, error, ".struct .word member has unexpected .sizeof" +.assert .sizeof(Storage::mw5) = 10, error, ".struct .word 5 member has unexpected .sizeof" +.assert .sizeof(Storage::ma1) = 2, error, ".struct .addr member has unexpected .sizeof" +.assert .sizeof(Storage::ma5) = 10, error, ".struct .addr 5 member has unexpected .sizeof" +.assert .sizeof(Storage::mf1) = 3, error, ".struct .faraddr member has unexpected .sizeof" +.assert .sizeof(Storage::mf5) = 15, error, ".struct .faraddr 5 member has unexpected .sizeof" +.assert .sizeof(Storage::mdw1) = 4, error, ".struct .dword member has unexpected .sizeof" +.assert .sizeof(Storage::mdw5) = 20, error, ".struct .dword 5 member has unexpected .sizeof" + +.assert Storage::mb1 = 0, error, ".struct storage offset is incorrect" +.assert Storage::mb5 = Storage::mb1 + .sizeof(Storage::mb1), error, ".struct storage offset is incorrect" +.assert Storage::mr1 = Storage::mb5 + .sizeof(Storage::mb5), error, ".struct storage offset is incorrect" +.assert Storage::mr5 = Storage::mr1 + .sizeof(Storage::mr1), error, ".struct storage offset is incorrect" +.assert Storage::mdb1 = Storage::mr5 + .sizeof(Storage::mr5), error, ".struct storage offset is incorrect" +.assert Storage::mdb5 = Storage::mdb1 + .sizeof(Storage::mdb1), error, ".struct storage offset is incorrect" +.assert Storage::mw1 = Storage::mdb5 + .sizeof(Storage::mdb5), error, ".struct storage offset is incorrect" +.assert Storage::mw5 = Storage::mw1 + .sizeof(Storage::mw1), error, ".struct storage offset is incorrect" +.assert Storage::ma1 = Storage::mw5 + .sizeof(Storage::mw5), error, ".struct storage offset is incorrect" +.assert Storage::ma5 = Storage::ma1 + .sizeof(Storage::ma1), error, ".struct storage offset is incorrect" +.assert Storage::mf1 = Storage::ma5 + .sizeof(Storage::ma5), error, ".struct storage offset is incorrect" +.assert Storage::mf5 = Storage::mf1 + .sizeof(Storage::mf1), error, ".struct storage offset is incorrect" +.assert Storage::mdw1 = Storage::mf5 + .sizeof(Storage::mf5), error, ".struct storage offset is incorrect" +.assert Storage::mdw5 = Storage::mdw1 + .sizeof(Storage::mdw1), error, ".struct storage offset is incorrect" +.assert .sizeof(Storage) = Storage::mdw5 + .sizeof(Storage::mdw5), error, ".struct has unexpected .sizeof" + +; test union offset and size + +.union UStorage + mb1 .byte + mb5 .byte 5 + mr1 .res 1 + mr5 .res 5 + mdb1 .dbyt + mdb5 .dbyt 5 + mw1 .word + mw5 .word 5 + ma1 .addr + ma5 .addr 5 + mf1 .faraddr + mf5 .faraddr 5 + mdw1 .dword + mdw5 .dword 5 +.endunion + +.assert .sizeof(UStorage::mb1) = 1, error, ".union .byte member has unexpected .sizeof" +.assert .sizeof(UStorage::mb5) = 5, error, ".union .byte 5 member has unexpected .sizeof" +.assert .sizeof(UStorage::mr1) = 1, error, ".union .res 1 member has unexpected .sizeof" +.assert .sizeof(UStorage::mr5) = 5, error, ".union .res 5 member has unexpected .sizeof" +.assert .sizeof(UStorage::mdb1) = 2, error, ".union .dbyt member has unexpected .sizeof" +.assert .sizeof(UStorage::mdb5) = 10, error, ".union .dbyt 5 member has unexpected .sizeof" +.assert .sizeof(UStorage::mw1) = 2, error, ".union .word member has unexpected .sizeof" +.assert .sizeof(UStorage::mw5) = 10, error, ".union .word 5 member has unexpected .sizeof" +.assert .sizeof(UStorage::ma1) = 2, error, ".union .addr member has unexpected .sizeof" +.assert .sizeof(UStorage::ma5) = 10, error, ".union .addr 5 member has unexpected .sizeof" +.assert .sizeof(UStorage::mf1) = 3, error, ".union .faraddr member has unexpected .sizeof" +.assert .sizeof(UStorage::mf5) = 15, error, ".union .faraddr 5 member has unexpected .sizeof" +.assert .sizeof(UStorage::mdw1) = 4, error, ".union .dword member has unexpected .sizeof" +.assert .sizeof(UStorage::mdw5) = 20, error, ".union .dword 5 member has unexpected .sizeof" +.assert .sizeof(UStorage) = 20, error, ".union has unexpected .sizeof" + +.assert UStorage::mb1 = 0, error, ".union storage offset is incorrect" +.assert UStorage::mb5 = 0, error, ".union storage offset is incorrect" +.assert UStorage::mr1 = 0, error, ".union storage offset is incorrect" +.assert UStorage::mr5 = 0, error, ".union storage offset is incorrect" +.assert UStorage::mdb1 = 0, error, ".union storage offset is incorrect" +.assert UStorage::mdb5 = 0, error, ".union storage offset is incorrect" +.assert UStorage::mw1 = 0, error, ".union storage offset is incorrect" +.assert UStorage::mw5 = 0, error, ".union storage offset is incorrect" +.assert UStorage::ma1 = 0, error, ".union storage offset is incorrect" +.assert UStorage::ma5 = 0, error, ".union storage offset is incorrect" +.assert UStorage::mf1 = 0, error, ".union storage offset is incorrect" +.assert UStorage::mf5 = 0, error, ".union storage offset is incorrect" +.assert UStorage::mdw1 = 0, error, ".union storage offset is incorrect" +.assert UStorage::mdw5 = 0, error, ".union storage offset is incorrect" + +; test tag + +storage: .tag Storage +.assert (*-storage)=.sizeof(Storage), error, ".tag reserved size incorrect" + +; test nested structures + +.struct Point + xc .word + yc .word +.endstruct + +.struct Nested + pad .res 13 + tag .tag Point + ch .struct Child + ca .word ; offset = 0 + gch .struct Grandchild + gca .word ; offset = 0 + gcb .byte + .endstruct + cb .byte + .endstruct + anon .struct + aa .dword ; offset = Nested::anon (anonymous .struct) + ab .dword + .endstruct + chu .union Chunion + ua .byte ; offset = 0 + ub .dword + .endunion + chanon .union + uc .byte ; offset = Nested::chanon + ud .dword + .endunion + last .byte +.endstruct + +.assert Nested::pad = 0, error, "Nested .struct has unexpected starting offset" +.assert Nested::Child::ca = 0, error, "Nested .struct has unexpected starting offset" +.assert Nested::Child::Grandchild::gca = 0, error, "Nested .struct has unexpected starting offset" + +.assert .sizeof(Nested::tag) = .sizeof(Point), error, ".tag in .struct has unexpected .sizeof" +.assert .sizeof(Nested::Child::Grandchild) = 2 + 1, error, "Nested .struct has unexpected .sizeof" +.assert .sizeof(Nested::Child) = 2 + 1 + .sizeof(Nested::Child::Grandchild), error, "Nested .struct has unpexpected .sizeof" +.assert .sizeof(Nested::ch) = .sizeof(Nested::Child), error, "Nested .struct has unexpected member .sizeof" +.assert .sizeof(Nested::Child::gch) = .sizeof(Nested::Child::Grandchild), error, "Nested .struct has unexpected member .sizeof" +.assert .sizeof(Nested::anon) = 8, error, "Nested anonymous member .struct has unexpected .sizeof" +.assert .sizeof(Nested::aa) = 4, error, "Nested anonymous .struct member has unexpected .sizeof" +.assert .sizeof(Nested::ab) = 4, error, "Nested anonymous .struct member has unexpected .sizeof" +.assert .sizeof(Nested::Chunion) = 4, error, "Nested .union has unexpected .sizeof" +.assert .sizeof(Nested::chu) = .sizeof(Nested::Chunion), error, "Nested member .union has unexpected .sizeof" +.assert .sizeof(Nested::chanon) = 4, error, "Nested anonymous member .union as unexpected .sizeof" + +.assert Nested::tag = Nested::pad + .sizeof(Nested::pad), error, ".tag within .struct has unexpected offset" +.assert Nested::ch = Nested::tag + .sizeof(Nested::tag), error, "Nested .struct has unexpected offset" +.assert Nested::anon = Nested::ch + .sizeof(Nested::ch), error, "Nested anonymous member .struct has unexpected offset" +.assert Nested::aa = Nested::anon, error, "Nested anonymous .struct member has unexpected offset" +.assert Nested::ab = Nested::aa + .sizeof(Nested::aa), error, "Nested anonymous .struct member has unexpected offset" +.assert Nested::chu = Nested::ab + .sizeof(Nested::ab), error, "Nested member .union has unexpected offset" +.assert Nested::chanon = Nested::chu + .sizeof(Nested::Chunion), error, "Nested anonymous member .union has unexpected offset" +.assert Nested::uc = Nested::chanon, error, "Nested anonymous .union member has unexpected offset" +.assert Nested::ud = Nested::chanon, error, "Nested anonymous .union member has unexpected offset" +.assert Nested::last = Nested::ud + .sizeof(Nested::ud), error, ".struct member has unexpected offset after anonymous nested .struct" + +; test .org + +start: + +.struct OrgStruct + ma .byte + mb .byte + .org $1234 + mc .byte + md .byte + .struct Nested + me .byte + .org $5678 + mf .byte + mg .byte + .endstruct + mh .byte +.endstruct + +.assert start <> (OrgStruct::mh+1), error, "Fatal test error: accidental code position conflict, move OrgStruct .org to another arbitrary address." +.assert * = start, error, ".org within .struct does not return to previous location at .endstruct" +.assert OrgStruct::ma = 0, error, ".struct with .org has unexpected offset" +.assert OrgStruct::mb = 1, error, ".struct with .org has unexpected offset" +.assert OrgStruct::mc = $1234, error, ".struct with .org has unexpected offset" +.assert OrgStruct::md = $1235, error, ".struct with .org has unexpected offset" +.assert OrgStruct::Nested::me = 0, error, "Nested .struct with .org has unexpected offset" +.assert OrgStruct::Nested::mf = $5678, error, "Nested .struct with .org has unexpected offset" +.assert OrgStruct::Nested::mg = $5679, error, "Nested .struct with .org has unexpected offset" +.assert OrgStruct::mh = $1239, error, ".struct with .org has unexpected offset" +.assert .sizeof(OrgStruct) = 8, error, ".struct with .org has unexpected .sizeof" + +.union OrgUnion + ma .byte + mb .word + .org $1234 + mc .byte + md .word +.endunion + +.assert start <> OrgUnion::md, error, "Fatal test error: accidental code position conflict, move OrgUnion .org to another arbitrary address." +.assert * = start, error, ".org within .union does not return to previous location at .endunion" +.assert OrgUnion::ma = 0, error, ".union with .org has unexpected offset" +.assert OrgUnion::mb = 0, error, ".union with .org has unexpected offset" +.assert OrgUnion::mc = $1234, error, ".union with .org has unexpected offset" +.assert OrgUnion::md = $1234, error, ".union with .org has unexpected offset" +.assert .sizeof(OrgUnion) = 2, error, ".union with .org has unexpected .sizeof" diff --git a/test/err/bug2016-fam-member.c b/test/err/bug2016-fam-member.c new file mode 100644 index 000000000..473aae702 --- /dev/null +++ b/test/err/bug2016-fam-member.c @@ -0,0 +1,16 @@ +/* Bug #2016 - cc65 erroneously allows struct fields that are structs with flexible array members */ + +typedef struct x { + int a; + int b[]; /* Ok: Flexible array member can be last */ +} x; + +typedef union u { + int a; + x x; /* Ok: Union member can contain flexible array member */ +} u; + +struct y { + u u; /* Not ok: Contains union that contains flexible array member */ + int a; +}; diff --git a/test/err/bug2017-fam-element.c b/test/err/bug2017-fam-element.c new file mode 100644 index 000000000..c97ae42ec --- /dev/null +++ b/test/err/bug2017-fam-element.c @@ -0,0 +1,13 @@ +/* Bug #2017 - cc65 erroneously allows arrays of structs with flexible array members */ + +typedef struct x { + int a; + int b[]; /* Ok: Flexible array member can be last */ +} x; + +typedef union u { + int a; + x x; /* Ok: Union member can contain flexible array member */ +} u; + +union u y[3]; /* Should be an error */ diff --git a/test/err/bug2018-bitfield.c b/test/err/bug2018-bitfield.c new file mode 100644 index 000000000..ea2928659 --- /dev/null +++ b/test/err/bug2018-bitfield.c @@ -0,0 +1,14 @@ +/* Bug #2018 - Compiler has problems with const struct fields */ + +typedef union U { + int a : 16; + const int b : 16; +} U; + +int main(void) +{ + U x = { 42 }; + x.b = 0; + + return 0; +} diff --git a/test/err/bug2018.c b/test/err/bug2018.c new file mode 100644 index 000000000..61892992e --- /dev/null +++ b/test/err/bug2018.c @@ -0,0 +1,25 @@ +/* Bug #2018 - Compiler has problems with const struct fields */ + +struct X { + struct { + int a; + } a; + union { + int a; + const int b; + } b; +}; + +struct X f(void) +{ + struct X x = { 42 }; + return x; +} + +int main(void) +{ + struct X x = { 0 }; + x = f(); /* Error since X is read only */ + + return 0; +} diff --git a/test/err/bug2020-definition.c b/test/err/bug2020-definition.c new file mode 100644 index 000000000..7443e3a9a --- /dev/null +++ b/test/err/bug2020-definition.c @@ -0,0 +1,6 @@ +/* Bug #2020 - ISO/IEC 9899:1999 (E), 6.9.1 footnote 137: +** "The intent is that the type category in a function definition cannot be inherited from a typedef" + */ + +typedef void F(void); +F c { } /* Should fail */ diff --git a/test/err/bug2144.c b/test/err/bug2144.c new file mode 100644 index 000000000..eb27d672b --- /dev/null +++ b/test/err/bug2144.c @@ -0,0 +1,13 @@ +/* Bug #2144 - Maximum parameter size is not checked for variadic functions */ + +void a(...) {} + +void b() +{ + /* Argument size > 255 */ + a(1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, + 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, + 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, + 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, + 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L); +} diff --git a/test/err/bug2162-none-extern-auto.c b/test/err/bug2162-none-extern-auto.c new file mode 100644 index 000000000..777fc48e7 --- /dev/null +++ b/test/err/bug2162-none-extern-auto.c @@ -0,0 +1,8 @@ +/* Bug #2162 - conflicting declarations in functions */ + +int main(void) +{ + extern int i; + int i = 42; /* Error */ + return i; +} diff --git a/test/err/bug2162-none-static-extern.c b/test/err/bug2162-none-static-extern.c new file mode 100644 index 000000000..cffb6a3bf --- /dev/null +++ b/test/err/bug2162-none-static-extern.c @@ -0,0 +1,8 @@ +/* Bug #2162 - conflicting declarations in functions */ + +int main(void) +{ + static int i = 42; + extern int i; /* Error */ + return i; +} diff --git a/test/err/bug2162-static-extern-auto.c b/test/err/bug2162-static-extern-auto.c new file mode 100644 index 000000000..08d91e0e1 --- /dev/null +++ b/test/err/bug2162-static-extern-auto.c @@ -0,0 +1,10 @@ +/* Bug #2162 - conflicting declarations in functions */ + +static int i; + +int main(void) +{ + extern int i; /* cc65 allows this */ + int i = 42; /* Error - if this were accepted, it would be confusing which object i refers to */ + return i; +} diff --git a/test/err/bug2162-static-static-extern.c b/test/err/bug2162-static-static-extern.c new file mode 100644 index 000000000..e0a536d6c --- /dev/null +++ b/test/err/bug2162-static-static-extern.c @@ -0,0 +1,10 @@ +/* Bug #2162 - conflicting declarations in functions */ + +static int i; + +int main(void) +{ + static int i = 42; /* OK - this shadows the i in file scope */ + extern int i; /* Error - if this were accepted, it would be confusing which object i refers to */ + return i; +} diff --git a/test/err/bug2285-composite-type.c b/test/err/bug2285-composite-type.c new file mode 100644 index 000000000..18a7b80a5 --- /dev/null +++ b/test/err/bug2285-composite-type.c @@ -0,0 +1,5 @@ +/* Bug #2285 - Regression in type composition */ + +void foo(); /* OK */ +void foo(int (*)(int)); /* OK */ +void foo(int (*)(long)); /* WRONG: Should be an error */ diff --git a/test/err/bug2286-param-qualifier.c b/test/err/bug2286-param-qualifier.c new file mode 100644 index 000000000..a014d0a0c --- /dev/null +++ b/test/err/bug2286-param-qualifier.c @@ -0,0 +1,4 @@ +/* Bug #2286 - Qualifiers of pointees of function parameters ignored for type compatibility check */ + +void woo(int* p); +void woo(const int* p); /* WRONG: Should be an error */ diff --git a/test/err/bug2301-trailing-coma-1.c b/test/err/bug2301-trailing-coma-1.c new file mode 100644 index 000000000..66cd4fae3 --- /dev/null +++ b/test/err/bug2301-trailing-coma-1.c @@ -0,0 +1,3 @@ +/* Bug #2301 - Function parameter list with an extra trailing comma should not compile */ + +int foo(int a,); /* Should fail */ diff --git a/test/err/bug2301-trailing-coma-2.c b/test/err/bug2301-trailing-coma-2.c new file mode 100644 index 000000000..40ca3d0ca --- /dev/null +++ b/test/err/bug2301-trailing-coma-2.c @@ -0,0 +1,6 @@ +/* Bug #2301 - Function parameter list with an extra trailing comma should not compile */ + +int bar(a,) int a; /* Should fail */ +{ + return a; +} diff --git a/test/err/bug2303.c b/test/err/bug2303.c new file mode 100644 index 000000000..609725ad8 --- /dev/null +++ b/test/err/bug2303.c @@ -0,0 +1 @@ +int f(a); /* Should be an error */ diff --git a/test/err/bug2304-var-use.c b/test/err/bug2304-var-use.c new file mode 100644 index 000000000..8a88405e2 --- /dev/null +++ b/test/err/bug2304-var-use.c @@ -0,0 +1,15 @@ +/* Bug 2304 - Visibility of objects/functions undeclared in file scope but 'extern'-declared in unrelated block scopes */ + +void f1(void) +{ + extern int a; +} + +/* 'a' is still invisible in the file scope */ + +int main(void) +{ + return a * 0; /* Usage of 'a' should be an error */ +} + +int a = 42; diff --git a/test/err/bug2312-pperror-only.c b/test/err/bug2312-pperror-only.c new file mode 100644 index 000000000..bdec33956 --- /dev/null +++ b/test/err/bug2312-pperror-only.c @@ -0,0 +1,8 @@ +/* Bug #2312 */ + +#error "Compiler should exit with failure" + +int main(void) +{ + return 0; +} diff --git a/test/err/bug2523.c b/test/err/bug2523.c new file mode 100644 index 000000000..7ab798557 --- /dev/null +++ b/test/err/bug2523.c @@ -0,0 +1,3 @@ +#if (1, 0) < 0 +#error +#endif diff --git a/test/err/type-name-extra-identifier.c b/test/err/type-name-extra-identifier.c new file mode 100644 index 000000000..72de4778d --- /dev/null +++ b/test/err/type-name-extra-identifier.c @@ -0,0 +1,25 @@ +/* + Copyright 2023 The cc65 Authors + + 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. +*/ + +/* + Test of type name with extra identifier +*/ + +int a = sizeof (int b); diff --git a/test/err/zero-size.c b/test/err/zero-size.c new file mode 100644 index 000000000..9e7510c91 --- /dev/null +++ b/test/err/zero-size.c @@ -0,0 +1,6 @@ +char a[][] = { 0, 0 }; /* Error: Array type has incomplete element type 'char[]' */ + +int main(void) +{ + return 0; +} diff --git a/test/misc/Makefile b/test/misc/Makefile index c708b160b..48293e504 100644 --- a/test/misc/Makefile +++ b/test/misc/Makefile @@ -58,24 +58,6 @@ $(ISEQUAL): ../isequal.c | $(WORKDIR) define PRG_template -# should compile, but gives an error -$(WORKDIR)/int-static-1888.$1.$2.prg: int-static-1888.c | $(WORKDIR) - @echo "FIXME: " $$@ "currently does not compile." - $(if $(QUIET),echo misc/int-static-1888.$1.$2.prg) - $(NOT) $(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) - -# should compile, but gives an error -$(WORKDIR)/bug760.$1.$2.prg: bug760.c | $(WORKDIR) - @echo "FIXME: " $$@ "currently does not compile." - $(if $(QUIET),echo misc/bug760.$1.$2.prg) - $(NOT) $(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) - -# should compile, but gives an error -$(WORKDIR)/bug1437.$1.$2.prg: bug1437.c | $(WORKDIR) - @echo "FIXME: " $$@ "currently does not compile." - $(if $(QUIET),echo misc/bug1437.$1.$2.prg) - $(NOT) $(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) - # should compile, but gives an error $(WORKDIR)/bug1209-ind-goto-rev.$1.$2.prg: bug1209-ind-goto-rev.c | $(WORKDIR) @echo "FIXME: " $$@ "currently does not compile." @@ -106,18 +88,6 @@ $(WORKDIR)/pptest2.$1.$2.prg: pptest2.c | $(WORKDIR) $(if $(QUIET),echo misc/pptest2.$1.$2.prg) $(NOT) $(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) -# should compile, but gives an error -$(WORKDIR)/bug1263.$1.$2.prg: bug1263.c | $(WORKDIR) - @echo "FIXME: " $$@ "currently does not compile." - $(if $(QUIET),echo misc/bug1263.$1.$2.prg) - $(NOT) $(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) - -# should compile, but gives an error -$(WORKDIR)/bug1357.$1.$2.prg: bug1357.c | $(WORKDIR) - @echo "FIXME: " $$@ "currently does not compile." - $(if $(QUIET),echo misc/bug1357.$1.$2.prg) - $(NOT) $(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) - # should compile, but compiler exits with internal error $(WORKDIR)/bug1211-ice-move-refs-2.$1.$2.prg: bug1211-ice-move-refs-2.c | $(WORKDIR) @echo "FIXME: " $$@ "currently does not compile." @@ -132,6 +102,20 @@ $(WORKDIR)/bug1265.$1.$2.prg: bug1265.c | $(WORKDIR) $(LD65) -t sim$2 -o $$@ $$(@:.prg=.o) sim$2.lib $(NULLERR) $(SIM65) $(SIM65FLAGS) $$@ $(NULLOUT) $(NULLERR) +# should not compile, but gives different diagnostics in C99 mode than in others +$(WORKDIR)/bug2515.$1.$2.prg: bug2515.c | $(WORKDIR) + $(if $(QUIET),echo misc/bug2515.$1.$2.prg) + $(NOT) $(CC65) --standard c99 -t sim$2 -$1 -o $$(@:.prg=.s) $$< 2>$(WORKDIR)/bug2515.$1.$2.out + $(ISEQUAL) $(WORKDIR)/bug2515.$1.$2.out bug2515.c99.ref + $(NOT) $(CC65) -t sim$2 -$1 -o $$(@:.prg=.s) $$< 2>$(WORKDIR)/bug2515.$1.$2.out + $(ISEQUAL) $(WORKDIR)/bug2515.$1.$2.out bug2515.ref + +# should not issue any warnings in C99 mode +$(WORKDIR)/bug2637.$1.$2.prg: bug2637.c | $(WORKDIR) + $(if $(QUIET),echo misc/bug2637.$1.$2.prg) + $(CC65) --standard c99 -t sim$2 -$1 -o $$(@:.prg=.s) $$< 2>$(WORKDIR)/bug2637.$1.$2.out + $(ISEQUAL) $(WORKDIR)/bug2637.$1.$2.out bug2637.ref + # this one requires -Werror $(WORKDIR)/bug1768.$1.$2.prg: bug1768.c | $(WORKDIR) $(if $(QUIET),echo misc/bug1768.$1.$2.prg) @@ -150,6 +134,11 @@ $(WORKDIR)/endless.$1.$2.prg: endless.c | $(WORKDIR) # these need reference data that can't be generated by a host-compiled program, # in a useful way +$(WORKDIR)/bug2655.$1.$2.prg: bug2655.c $(ISEQUAL) | $(WORKDIR) + $(if $(QUIET),echo misc/bug2655.$1.$2.prg) + $(CC65) -t sim$2 -$1 -o $$@ $$< 2>$(WORKDIR)/bug2655.$1.$2.out + $(ISEQUAL) $(WORKDIR)/bug2655.$1.$2.out bug2655.ref + $(WORKDIR)/limits.$1.$2.prg: limits.c $(ISEQUAL) | $(WORKDIR) $(if $(QUIET),echo misc/limits.$1.$2.prg) $(CC65) -t sim$2 -$1 -o $$(@:.prg=.s) $$< $(NULLERR) @@ -163,6 +152,12 @@ $(WORKDIR)/goto.$1.$2.prg: goto.c $(ISEQUAL) | $(WORKDIR) $(CC65) -t sim$2 -$1 -o $$@ $$< 2>$(WORKDIR)/goto.$1.$2.out $(ISEQUAL) $(WORKDIR)/goto.$1.$2.out goto.ref +# this one requires failure with --std=c89, it fails with --std=cc65 due to +# stricter checks +$(WORKDIR)/bug2304-implicit-func.$1.$2.prg: bug2304-implicit-func.c | $(WORKDIR) + $(if $(QUIET),echo misc/bug2304-implicit-func.$1.$2.prg) + $(NOT) $(CC65) --standard c89 -t sim$2 -$1 -o $$@ $$< $(NULLERR) + # should not compile until 3-byte struct by value tests are re-enabled $(WORKDIR)/struct-by-value.$1.$2.prg: struct-by-value.c | $(WORKDIR) $(if $(QUIET),echo misc/struct-by-value.$1.$2.prg) diff --git a/test/misc/bug2304-implicit-func.c b/test/misc/bug2304-implicit-func.c new file mode 100644 index 000000000..f6b7450ff --- /dev/null +++ b/test/misc/bug2304-implicit-func.c @@ -0,0 +1,21 @@ +/* Bug 2304 - Visibility of objects/functions undeclared in file scope but 'extern'-declared in unrelated block scopes */ + +/* This one should fail even in C89 */ + +void f1(void) +{ + extern unsigned int f(); +} + +/* 'f' is still invisible in the file scope */ + +int main(void) +{ + f(); /* Should be a conflict since the implicit function type is incompatible */ + return 0; +} + +unsigned int f() +{ + return 42; +} diff --git a/test/misc/bug2515.c b/test/misc/bug2515.c new file mode 100644 index 000000000..62ee52a25 --- /dev/null +++ b/test/misc/bug2515.c @@ -0,0 +1,4 @@ + +#line 13"x" +#define X"y" +int main() { foo; } diff --git a/test/misc/bug2515.c99.ref b/test/misc/bug2515.c99.ref new file mode 100644 index 000000000..f6cf2fcea --- /dev/null +++ b/test/misc/bug2515.c99.ref @@ -0,0 +1,2 @@ +x:13: Warning: ISO C99 requires whitespace after the macro name +x:14: Error: Undeclared identifier 'foo' diff --git a/test/misc/bug2515.ref b/test/misc/bug2515.ref new file mode 100644 index 000000000..affbaebb9 --- /dev/null +++ b/test/misc/bug2515.ref @@ -0,0 +1 @@ +x:14: Error: Undeclared identifier 'foo' diff --git a/test/misc/bug2637.c b/test/misc/bug2637.c new file mode 100644 index 000000000..f6b716465 --- /dev/null +++ b/test/misc/bug2637.c @@ -0,0 +1,15 @@ +#include <stdarg.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> + +// compile with --standard c99 +int main() +{ + return 0; +} diff --git a/test/misc/bug2637.ref b/test/misc/bug2637.ref new file mode 100644 index 000000000..e69de29bb diff --git a/test/misc/bug2655.c b/test/misc/bug2655.c new file mode 100644 index 000000000..e0b1c939c --- /dev/null +++ b/test/misc/bug2655.c @@ -0,0 +1,18 @@ +void f1(void) +{ + if (1) { + f1(); + } else { + f1(); + } +} + +void f2(void) +{ + if (0) { + f2(); + } else { + f2(); + } +} + diff --git a/test/misc/bug2655.ref b/test/misc/bug2655.ref new file mode 100644 index 000000000..99e2d6698 --- /dev/null +++ b/test/misc/bug2655.ref @@ -0,0 +1,2 @@ +bug2655.c:5: Warning: Unreachable code +bug2655.c:12: Warning: Unreachable code diff --git a/test/readme.txt b/test/readme.txt index d3f17148e..0aa8799b9 100644 --- a/test/readme.txt +++ b/test/readme.txt @@ -12,6 +12,8 @@ compiler is working as expected (when the tests behave as described): /val - The bulk of tests are contained here, individual tests should exit with an exit code of EXIT_SUCCESS when they pass, or EXIT_FAILURE on error. +/err - contains tests that MUST NOT compile + /standard - like the tests in /val, the tests must exit with EXIT_SUCCESS on success. Unlike the tests in /val these are not compiled for every combination of optimizer options, but instead always with -Osir and then @@ -19,6 +21,10 @@ compiler is working as expected (when the tests behave as described): to check for regressions in standard conformance of the compiler and the library. +/standard_err - like the tests in /err, these tests MUST NOT compile, and like + the tests in /standard, these are compiled -Osir and then for each + supported C-Standard. + /ref - These tests produce output that must be compared with reference output. Normally the reference output is produced by compiling the program on the host (using gcc mostly) and then running them on the host. Tests should @@ -43,8 +49,6 @@ compiler is working as expected (when the tests behave as described): only ever use this as a last resort when something can not be tested by other means. -/err - contains tests that MUST NOT compile - /todo and /misc generally contain the tests that fail because of known bugs: diff --git a/test/ref/Makefile b/test/ref/Makefile index abd3e9bc0..e82c6de37 100644 --- a/test/ref/Makefile +++ b/test/ref/Makefile @@ -39,8 +39,18 @@ OPTIONS = g O Os Osi Osir Osr Oi Oir Or ISEQUAL = ..$S..$Stestwrk$Sisequal$(EXE) +# NOTE: the current test bench may include K&R style C, C89 style C, C99 - and +# even things from later standards. Technically C99 removed certain C89 +# constructs - However, so far GCC would still compile them and issue a +# warning (instead of an error). Now, GCC 14 will be more strict about this, +# and by default make those things an error instead. We use -std=gnu17 here +# so we can still build the references with a modern compiler, and don't +# have to deal with special-casing individual tests that use constructs +# from those old standards. Should this become a problem in the future, we +# will have to change that, and create said special cases here. +# see discussion in https://github.com/cc65/cc65/issues/2277 CC = gcc -CFLAGS = -O2 -Wall -W -Wextra -funsigned-char -fwrapv -fno-strict-overflow +CFLAGS = -std=gnu17 -O2 -Wall -W -Wextra -funsigned-char -fwrapv -fno-strict-overflow -Wno-error=implicit-int -Wno-error=int-conversion .PHONY: all clean @@ -53,7 +63,9 @@ CUSTOMSOURCES = \ # exact error output is required ERRORSOURCES = \ custom-reference-error.c \ - bug1889-missing-identifier.c + inline-error.c \ + bug1889-missing-identifier.c \ + bug2312-preprocessor-error.c SOURCES := $(filter-out $(CUSTOMSOURCES) $(ERRORSOURCES),$(wildcard *.c)) diff --git a/test/ref/bug1889-missing-identifier.c b/test/ref/bug1889-missing-identifier.c index d9cf4aa52..a8140565f 100644 --- a/test/ref/bug1889-missing-identifier.c +++ b/test/ref/bug1889-missing-identifier.c @@ -3,6 +3,8 @@ int enum { a } x; inline enum { b }; +_Static_assert(); + int main(void) { return 0; diff --git a/test/ref/bug1889-missing-identifier.cref b/test/ref/bug1889-missing-identifier.cref index cd3f76849..70c485fab 100644 --- a/test/ref/bug1889-missing-identifier.cref +++ b/test/ref/bug1889-missing-identifier.cref @@ -1,3 +1,4 @@ -bug1889-missing-identifier.c:3: Error: Identifier expected -bug1889-missing-identifier.c:4: Error: Identifier expected -bug1889-missing-identifier.c:4: Warning: Implicit 'int' is an obsolete feature +bug1889-missing-identifier.c:3: Error: Identifier or ';' expected after declaration specifiers +bug1889-missing-identifier.c:3: Warning: Implicit 'int' type specifier is an obsolete feature +bug1889-missing-identifier.c:4: Error: 'inline' on empty declaration +bug1889-missing-identifier.c:6: Error: Expression expected diff --git a/test/ref/bug2134.c b/test/ref/bug2134.c new file mode 100644 index 000000000..9dd1e5c55 --- /dev/null +++ b/test/ref/bug2134.c @@ -0,0 +1,3 @@ +int i = { 0 }; +char* p = { 0 }; +int main() { return 0; } diff --git a/test/ref/bug2134.cref b/test/ref/bug2134.cref new file mode 100644 index 000000000..72bbbad04 --- /dev/null +++ b/test/ref/bug2134.cref @@ -0,0 +1,2 @@ +bug2134.c:1: Warning: Braces around scalar initializer +bug2134.c:2: Warning: Braces around scalar initializer diff --git a/test/ref/bug2312-preprocessor-error.c b/test/ref/bug2312-preprocessor-error.c new file mode 100644 index 000000000..b7872f514 --- /dev/null +++ b/test/ref/bug2312-preprocessor-error.c @@ -0,0 +1,8 @@ +/* Bug #2312 - Error recovery from preprocessor errors at the end of a declaration */ + +typedef int A; /* ';' consumption triggers PP below */ + +#define /* PP error during ';' consumption */ + +A f(void); /* Should be OK */ +int A(void); /* Should be an error */ diff --git a/test/ref/bug2312-preprocessor-error.cref b/test/ref/bug2312-preprocessor-error.cref new file mode 100644 index 000000000..680950fd6 --- /dev/null +++ b/test/ref/bug2312-preprocessor-error.cref @@ -0,0 +1,2 @@ +bug2312-preprocessor-error.c:5: Error: Missing macro name +bug2312-preprocessor-error.c:8: Error: Redefinition of typedef 'A' as different kind of symbol diff --git a/test/ref/custom-reference-error.c b/test/ref/custom-reference-error.c index c86a8b9e9..455e0276d 100644 --- a/test/ref/custom-reference-error.c +++ b/test/ref/custom-reference-error.c @@ -13,9 +13,14 @@ and then "make" again to confirm */ -int main(int argc, char* argv[]) +typedef short return_t; +#error This is an/* produce an error */error + +return_t main(int argc, char* argv[]) { - printf("%02x", 0x42); - n = 0; /* produce an error */ - /* another error */ + printf("%02x", 0x42); /* produce an error */ + n = 0; /* produce an error */ + /* produce a warning */ } +int b = 0; +int arr[b]; /* produce an error */ diff --git a/test/ref/custom-reference-error.cref b/test/ref/custom-reference-error.cref index fa584f307..9ffa581cd 100644 --- a/test/ref/custom-reference-error.cref +++ b/test/ref/custom-reference-error.cref @@ -1,5 +1,7 @@ -custom-reference-error.c:18: Error: Call to undeclared function 'printf' -custom-reference-error.c:19: Error: Undefined symbol: 'n' -custom-reference-error.c:21: Warning: Control reaches end of non-void function [-Wreturn-type] -custom-reference-error.c:21: Warning: Parameter 'argc' is never used -custom-reference-error.c:21: Warning: Parameter 'argv' is never used +custom-reference-error.c:17: Error: #error: This is an error +custom-reference-error.c:21: Error: Call to undeclared function 'printf' +custom-reference-error.c:22: Error: Undeclared identifier 'n' +custom-reference-error.c:24: Warning: Control reaches end of non-void function [-Wreturn-type] +custom-reference-error.c:24: Warning: Parameter 'argc' is never used +custom-reference-error.c:24: Warning: Parameter 'argv' is never used +custom-reference-error.c:26: Error: Constant integer expression expected diff --git a/test/ref/custom-reference.c b/test/ref/custom-reference.c index 5d9c356df..b48f8eb78 100644 --- a/test/ref/custom-reference.c +++ b/test/ref/custom-reference.c @@ -20,5 +20,4 @@ int main(int argc, char* argv[]) { printf("%02x", 0x42); /* produce a warning */ - return 0; } diff --git a/test/ref/custom-reference.cref b/test/ref/custom-reference.cref index 4dba6009b..868710849 100644 --- a/test/ref/custom-reference.cref +++ b/test/ref/custom-reference.cref @@ -1,2 +1,2 @@ -custom-reference.c:24: Warning: Parameter 'argc' is never used -custom-reference.c:24: Warning: Parameter 'argv' is never used +custom-reference.c:23: Warning: Parameter 'argc' is never used +custom-reference.c:23: Warning: Parameter 'argv' is never used diff --git a/test/ref/inline-error.c b/test/ref/inline-error.c new file mode 100644 index 000000000..2dad41b6d --- /dev/null +++ b/test/ref/inline-error.c @@ -0,0 +1,38 @@ +/* C99 inline in declarations */ + +inline typedef int; /* Error */ +static inline int; /* Error */ +inline static int a1; /* Error */ +int inline (*fp1)(void); /* Error */ +typedef inline int f1_t(void); /* Error */ +inline int f1a(void); /* OK here warning later */ +inline extern int f1b(void); /* OK here warning later */ +extern inline int f1b(void); /* Same as above */ +inline static int f1c(void); /* OK here warning later */ +static inline int f1c(void); /* Same as above */ + +void foo(inline int x); /* Error */ +int a = sizeof (inline int); /* TODO: better error message */ +int b = sizeof (inline int (int)); /* TODO: better error message */ + +inline int main(void) /* Error */ +{ + inline typedef int; /* Error */ + static inline int; /* Error */ + extern inline int a2; /* Error */ + int inline (*fp2)(void); /* Error */ + typedef inline int f2_t(void); /* Error */ + inline int f2a(void); /* OK here warning later */ + inline extern int f2b(void); /* OK here warning later */ + extern inline int f2b(void); /* Same as above */ + + f1a(); /* Still imported */ + f1b(); /* Still imported */ + f1c(); /* Not imported */ + f2a(); /* Still imported */ + f2b(); /* Still imported */ +} + +inline int main(void); /* Error */ + +/* Warning: non-external inline functions declared but undefined in TU */ diff --git a/test/ref/inline-error.cref b/test/ref/inline-error.cref new file mode 100644 index 000000000..4ce1d7073 --- /dev/null +++ b/test/ref/inline-error.cref @@ -0,0 +1,21 @@ +inline-error.c:3: Error: 'inline' on empty declaration +inline-error.c:4: Error: 'inline' on empty declaration +inline-error.c:5: Error: 'inline' on non-function declaration +inline-error.c:6: Error: 'inline' on non-function declaration +inline-error.c:7: Error: 'inline' on non-function declaration +inline-error.c:14: Error: Unexpected function specifiers +inline-error.c:15: Error: Mixed declarations and code are not supported in cc65 +inline-error.c:16: Error: Mixed declarations and code are not supported in cc65 +inline-error.c:19: Error: 'main' cannot be declared inline +inline-error.c:20: Error: 'inline' on empty declaration +inline-error.c:21: Error: 'inline' on empty declaration +inline-error.c:22: Error: 'inline' on non-function declaration +inline-error.c:23: Error: 'inline' on non-function declaration +inline-error.c:24: Error: 'inline' on non-function declaration +inline-error.c:34: Warning: Variable 'fp2' is defined but never used +inline-error.c:36: Error: 'main' cannot be declared inline +inline-error.c:39: Warning: Inline function 'f1a' used but never defined +inline-error.c:39: Warning: Inline function 'f1b' used but never defined +inline-error.c:39: Warning: Static function 'f1c' used but never defined +inline-error.c:39: Warning: Inline function 'f2a' used but never defined +inline-error.c:39: Warning: Inline function 'f2b' used but never defined diff --git a/test/ref/test_fgets.c b/test/ref/test_fgets.c new file mode 100644 index 000000000..0529b1651 --- /dev/null +++ b/test/ref/test_fgets.c @@ -0,0 +1,67 @@ +/* + !!DESCRIPTION!! fgets test + !!LICENCE!! Public domain +*/ + +#include "common.h" + +#include <ctype.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +FILE *in, *out; +char buf[32]; + +#define INFILE "cf.in" + +int main(int argc,char **argv) +{ + static char outfile_path[FILENAME_MAX+1]; + + sprintf(outfile_path, "%s.test.out", argv[0]); + + out = fopen(outfile_path, "wb"); + if (out == NULL) { + return EXIT_FAILURE; + } + if (fgets(buf, sizeof(buf), out) != NULL) { + printf("Error, could fgets with write-only file\n"); + return 1; + } + if (!ferror(out)) { + printf("Error: file pointer should be in error state\n"); + } + fclose(out); + unlink(outfile_path); + + in = fopen(INFILE, "rb"); + if (in == NULL) { + return EXIT_FAILURE; + } + + if (fgets(NULL, 0, in) != NULL) { + printf("Error, could fgets with zero size\n"); + return 1; + } + + /* Test ungetc while we're at it */ + buf[0] = fgetc(in); + ungetc(buf[0], in); + + + while (fgets(buf, sizeof(buf), in) != NULL) + { + printf("%s",buf); + } + + if (!feof(in)) + { + printf("We should have EOF!\n"); + } + + fclose(in); + return 0; +} diff --git a/test/ref/test_fputc.c b/test/ref/test_fputc.c new file mode 100644 index 000000000..a19aeafaf --- /dev/null +++ b/test/ref/test_fputc.c @@ -0,0 +1,39 @@ +/* + !!DESCRIPTION!! fgets test + !!LICENCE!! Public domain +*/ + +#include "common.h" + +#include <ctype.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +FILE *in, *out; +int c, err; + +#define INFILE "cf.in" + +int main(int argc,char **argv) +{ + in = fopen(INFILE, "rb"); + if (in == NULL) { + return EXIT_FAILURE; + } + + if (fputc(c, in) != EOF) { + printf("Error: can fputc to a file opened for reading\n"); + return EXIT_FAILURE; + } + clearerr(in); + + while ((c = fgetc(in)) != EOF) { + fputc(c, stdout); + } + + fclose(in); + return 0; +} diff --git a/test/ref/test_fputs.c b/test/ref/test_fputs.c new file mode 100644 index 000000000..ad0552317 --- /dev/null +++ b/test/ref/test_fputs.c @@ -0,0 +1,40 @@ +/* + !!DESCRIPTION!! fgets test + !!LICENCE!! Public domain +*/ + +#include "common.h" + +#include <ctype.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +FILE *in, *out; +char buf[512], err; + +#define INFILE "cf.in" + +int main(int argc,char **argv) +{ + in = fopen(INFILE, "rb"); + if (in == NULL) { + return EXIT_FAILURE; + } + + strcpy(buf, "test"); + if (fputs(buf, in) != EOF) { + printf("Error: can fputs to a file opened for reading\n"); + return EXIT_FAILURE; + } + clearerr(in); + + while (fgets(buf, 512, in) != NULL) { + fputs(buf, stdout); + } + + fclose(in); + return 0; +} diff --git a/test/ref/test_fread.c b/test/ref/test_fread.c new file mode 100644 index 000000000..99411ab28 --- /dev/null +++ b/test/ref/test_fread.c @@ -0,0 +1,72 @@ +/* + !!DESCRIPTION!! fread test + !!LICENCE!! Public domain +*/ + +#include "common.h" + +#include <ctype.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +FILE *in, *out; +char buf[32]; + +#define INFILE "cf.in" + +int main(int argc,char **argv) +{ + static char outfile_path[FILENAME_MAX+1]; + static int r; + + sprintf(outfile_path, "%s.test.out", argv[0]); + + out = fopen(outfile_path, "wb"); + if (out == NULL) { + return EXIT_FAILURE; + } + if (fread(buf, 1, sizeof(buf), out) != NULL) { + printf("Error, could fread with write-only file\n"); + return 1; + } + if (!ferror(out)) { + printf("Error: file pointer should be in error state\n"); + } + fclose(out); + unlink(outfile_path); + + in = fopen(INFILE, "rb"); + if (in == NULL) { + return EXIT_FAILURE; + } + + /* Test that ungetc doesn't break fread */ + buf[0] = fgetc(in); + ungetc(buf[0], in); + + r = fread(buf, 1, sizeof(buf), out); + + if (r == 0) { + printf("Error: could not start reading.\n"); + return EXIT_FAILURE; + } + fwrite(buf, 1, r, stdout); + + /* Finish reading file. */ + while ((r = fread(buf, 1, sizeof(buf), out)) != 0) + { + fwrite(buf, 1, r, stdout); + } + + if (!feof(in)) + { + printf("We should have EOF!\n"); + return EXIT_FAILURE; + } + + fclose(in); + return 0; +} diff --git a/test/ref/test_fseek.c b/test/ref/test_fseek.c new file mode 100644 index 000000000..157d0bdf0 --- /dev/null +++ b/test/ref/test_fseek.c @@ -0,0 +1,73 @@ +/* + !!DESCRIPTION!! fseek test + !!LICENCE!! Public domain +*/ + +#include "common.h" + +#include <ctype.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +FILE *in; +char bufA[32]; +char bufB[32]; + +#define INFILE "cf.in" + +int main(int argc,char **argv) +{ + static int r; + + in = fopen(INFILE, "rb"); + if (in == NULL) { + return EXIT_FAILURE; + } + + r = fread(bufA, 1, sizeof(bufA), in); + + if (r == 0) { + printf("Error: could not read.\n"); + return EXIT_FAILURE; + } + fwrite(bufA, 1, r, stdout); + + /* Test SEEK_SET */ + fseek(in, 0, SEEK_SET); + r = fread(bufB, 1, sizeof(bufB), in); + + if (r == 0) { + printf("Error: could not re-read after SEEK_SET.\n"); + return EXIT_FAILURE; + } + fwrite(bufB, 1, r, stdout); + + if (memcmp(bufA, bufB, sizeof(bufA))) { + printf("reads differ.\n"); + return EXIT_FAILURE; + } + + /* Test SEEK_CUR */ + fseek(in, 0, SEEK_SET); + fseek(in, 1, SEEK_CUR); + r = fread(bufB, 1, sizeof(bufB), in); + + if (r == 0) { + printf("Error: could not re-read after SEEK_CUR.\n"); + return EXIT_FAILURE; + } + fwrite(bufB, 1, r, stdout); + + if (memcmp(bufA+1, bufB, sizeof(bufA)-1)) { + printf("reads differ.\n"); + return EXIT_FAILURE; + } + + + fclose(in); + + return 0; +} diff --git a/test/ref/test_gets.c b/test/ref/test_gets.c new file mode 100644 index 000000000..003da5569 --- /dev/null +++ b/test/ref/test_gets.c @@ -0,0 +1,44 @@ +/* + !!DESCRIPTION!! gets test + !!LICENCE!! Public domain +*/ + +#include "common.h" + +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <fcntl.h> +#include <ctype.h> + +char buf[512]; + +#define INFILE "cf.in" + +#ifndef __CC65__ +/* Force declaration on host compiler, as gets() is deprecated for + * being dangerous as hell */ +char *gets (char *__s); +#endif + +#ifdef NO_OLD_FUNC_DECL +int main(int argc,char **argv) +#else +main(argc, argv) +int argc; +char *argv[]; +#endif +{ + /* Fake stdin with the reference file */ + if (freopen(INFILE, "rb", stdin) == NULL) { + return EXIT_FAILURE; + } + + while (gets(buf) != NULL) + { + printf("%s",buf); + } + + fclose(stdin); + return 0; +} diff --git a/test/ref/test_rewind.c b/test/ref/test_rewind.c new file mode 100644 index 000000000..d1b361983 --- /dev/null +++ b/test/ref/test_rewind.c @@ -0,0 +1,56 @@ +/* + !!DESCRIPTION!! rewind test + !!LICENCE!! Public domain +*/ + +#include "common.h" + +#include <ctype.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +FILE *in; +char bufA[32]; +char bufB[32]; + +#define INFILE "cf.in" + +int main(int argc,char **argv) +{ + static int r; + + in = fopen(INFILE, "rb"); + if (in == NULL) { + return EXIT_FAILURE; + } + + r = fread(bufA, 1, sizeof(bufA), in); + + if (r == 0) { + printf("Error: could not read.\n"); + return EXIT_FAILURE; + } + fwrite(bufA, 1, r, stdout); + + rewind(in); + printf("rewind.\n"); + r = fread(bufB, 1, sizeof(bufB), in); + + if (r == 0) { + printf("Error: could not re-read.\n"); + return EXIT_FAILURE; + } + fwrite(bufB, 1, r, stdout); + + fclose(in); + + if (memcmp(bufA, bufB, sizeof(bufA))) { + printf("reads differ.\n"); + return EXIT_FAILURE; + } + + return 0; +} diff --git a/test/standard/issue2607_cc65.c b/test/standard/issue2607_cc65.c new file mode 100644 index 000000000..82ff9d1aa --- /dev/null +++ b/test/standard/issue2607_cc65.c @@ -0,0 +1,14 @@ +#include <stdio.h> +#include <stdlib.h> + +/* this should succeed on all three standards + * yet use only \e on CC65 + */ +int main(void) { + +#if __CC65_STD__ == __CC65_STD_CC65__ + printf("\e"); +#endif + + return EXIT_SUCCESS; +} diff --git a/test/standard/stdint.c b/test/standard/stdint.c new file mode 100644 index 000000000..29b48346a --- /dev/null +++ b/test/standard/stdint.c @@ -0,0 +1,167 @@ +/* Test definitions from stdint.h */ + +#include <stdio.h> +#include <stdint.h> +#include <stddef.h> +#include <stdlib.h> +#include <limits.h> +#include <signal.h> + +/* All macros from stdint.h must be evaluatable by the preprocessor */ +#if INT8_MIN +#endif +#if INT8_MAX +#endif +#if INT16_MIN +#endif +#if INT16_MAX +#endif +#if INT32_MIN +#endif +#if INT32_MAX +#endif +#if UINT8_MAX +#endif +#if UINT16_MAX +#endif +#if UINT32_MAX +#endif +#if INT_LEAST8_MIN +#endif +#if INT_LEAST8_MAX +#endif +#if INT_LEAST16_MIN +#endif +#if INT_LEAST16_MAX +#endif +#if INT_LEAST32_MIN +#endif +#if INT_LEAST32_MAX +#endif +#if UINT_LEAST8_MAX +#endif +#if UINT_LEAST16_MAX +#endif +#if UINT_LEAST32_MAX +#endif +#if INT_FAST8_MIN +#endif +#if INT_FAST8_MAX +#endif +#if INT_FAST16_MIN +#endif +#if INT_FAST16_MAX +#endif +#if INT_FAST32_MIN +#endif +#if INT_FAST32_MAX +#endif +#if UINT_FAST8_MAX +#endif +#if UINT_FAST16_MAX +#endif +#if UINT_FAST32_MAX +#endif +#if INTPTR_MIN +#endif +#if INTPTR_MAX +#endif +#if UINTPTR_MAX +#endif +#if INTMAX_MIN +#endif +#if INTMAX_MAX +#endif +#if UINTMAX_MAX +#endif +#if PTRDIFF_MIN +#endif +#if PTRDIFF_MAX +#endif +#if SIG_ATOMIC_MIN +#endif +#if SIG_ATOMIC_MAX +#endif +#if SIZE_MAX +#endif + +#define SMIN(type) ((type)(1L << (sizeof(type) * CHAR_BIT - 1))) +#define SMAX(type) ((type)(~SMIN(type))) +#define UMAX(type) ((type)(~(type)0)) + +#define SMIN_CHECK(type, val) \ + if (SMIN(type) != val) { \ + ++failures; \ + printf("Mismatch for %s, minimum (%ld) is not %s (%ld)\n", \ + #type, (long)SMIN(type), #val, (long)val); \ + } +#define SMAX_CHECK(type, val) \ + if (SMAX(type) != val) { \ + ++failures; \ + printf("Mismatch for %s, maximum (%ld) is not %s (%ld)\n", \ + #type, (long)SMAX(type), #val, (long)val); \ + } +#define UMAX_CHECK(type, val) \ + if (UMAX(type) != val) { \ + ++failures; \ + printf("Mismatch for %s, maximum (%lu) is not %s (%lu)\n", \ + #type, (unsigned long)UMAX(type), #val, \ + (unsigned long)val); \ + } + +static unsigned failures = 0; + +int main() +{ + SMIN_CHECK(int8_t, INT8_MIN); + SMAX_CHECK(int8_t, INT8_MAX); + SMIN_CHECK(int16_t, INT16_MIN); + SMAX_CHECK(int16_t, INT16_MAX); + SMIN_CHECK(int32_t, INT32_MIN); + SMAX_CHECK(int32_t, INT32_MAX); + UMAX_CHECK(uint8_t, UINT8_MAX); + UMAX_CHECK(uint16_t, UINT16_MAX); + UMAX_CHECK(uint32_t, UINT32_MAX); + + SMIN_CHECK(int_least8_t, INT_LEAST8_MIN); + SMAX_CHECK(int_least8_t, INT_LEAST8_MAX); + SMIN_CHECK(int_least16_t, INT_LEAST16_MIN); + SMAX_CHECK(int_least16_t, INT_LEAST16_MAX); + SMIN_CHECK(int_least32_t, INT_LEAST32_MIN); + SMAX_CHECK(int_least32_t, INT_LEAST32_MAX); + UMAX_CHECK(uint_least8_t, UINT_LEAST8_MAX); + UMAX_CHECK(uint_least16_t, UINT_LEAST16_MAX); + UMAX_CHECK(uint_least32_t, UINT_LEAST32_MAX); + + SMIN_CHECK(int_fast8_t, INT_FAST8_MIN); + SMAX_CHECK(int_fast8_t, INT_FAST8_MAX); + SMIN_CHECK(int_fast16_t, INT_FAST16_MIN); + SMAX_CHECK(int_fast16_t, INT_FAST16_MAX); + SMIN_CHECK(int_fast32_t, INT_FAST32_MIN); + SMAX_CHECK(int_fast32_t, INT_FAST32_MAX); + UMAX_CHECK(uint_fast8_t, UINT_FAST8_MAX); + UMAX_CHECK(uint_fast16_t, UINT_FAST16_MAX); + UMAX_CHECK(uint_fast32_t, UINT_FAST32_MAX); + + SMIN_CHECK(intptr_t, INTPTR_MIN); + SMAX_CHECK(intptr_t, INTPTR_MAX); + UMAX_CHECK(uintptr_t, UINTPTR_MAX); + + SMIN_CHECK(intmax_t, INTMAX_MIN); + SMAX_CHECK(intmax_t, INTMAX_MAX); + UMAX_CHECK(uintmax_t, UINTMAX_MAX); + + SMIN_CHECK(ptrdiff_t, PTRDIFF_MIN); + SMAX_CHECK(ptrdiff_t, PTRDIFF_MAX); + +#if SIG_ATOMIC_MIN < 0 + SMIN_CHECK(sig_atomic_t, SIG_ATOMIC_MIN); + SMAX_CHECK(sig_atomic_t, SIG_ATOMIC_MAX); +#else + UMAX_CHECK(sig_atomic_t, SIG_ATOMIC_MAX); +#endif + + UMAX_CHECK(size_t, SIZE_MAX); + + return failures; +} diff --git a/test/standard_err/Makefile b/test/standard_err/Makefile new file mode 100644 index 000000000..700a52eea --- /dev/null +++ b/test/standard_err/Makefile @@ -0,0 +1,55 @@ +# Makefile for the tests that MUST NOT compile + +ifneq ($(shell echo),) + CMD_EXE = 1 +endif + +ifdef CMD_EXE + S = $(subst /,\,/) + NOT = - # Hack + NULLDEV = nul: + MKDIR = mkdir $(subst /,\,$1) + RMDIR = -rmdir /s /q $(subst /,\,$1) +else + S = / + NOT = ! + NULLDEV = /dev/null + MKDIR = mkdir -p $1 + RMDIR = $(RM) -r $1 +endif + +ifdef QUIET + .SILENT: + NULLERR = 2>$(NULLDEV) +endif + +CC65 := $(if $(wildcard ../../bin/cc65*),..$S..$Sbin$Scc65,cc65) + +WORKDIR = ../../testwrk/standard_err + +OPTIONS = c89 c99 cc65 + +.PHONY: all clean + +SOURCES := $(wildcard *.c) +TESTS = $(foreach option,$(OPTIONS),$(SOURCES:%.c=$(WORKDIR)/%.$(option).6502.prg)) + +all: $(TESTS) + +$(WORKDIR): + $(call MKDIR,$(WORKDIR)) + +define PRG_template + +$(WORKDIR)/%.$1.$2.prg: %.c | $(WORKDIR) + $(if $(QUIET),echo standard_err/$$*.$1.$2.prg) + $(NOT) $(CC65) -t sim$2 $$(CC65FLAGS) -Osir --add-source --standard $1 -o $$(@:.prg=.s) $$< $(NULLERR) + +endef # PRG_template + +$(foreach option,$(OPTIONS),$(eval $(call PRG_template,$(option),6502))) + +#$(foreach option,$(OPTIONS),$(eval $(call PRG_template,$(option),65c02))) + +clean: + @$(call RMDIR,$(WORKDIR)) diff --git a/test/standard_err/issue2607_not_cc65.c b/test/standard_err/issue2607_not_cc65.c new file mode 100644 index 000000000..5e56df557 --- /dev/null +++ b/test/standard_err/issue2607_not_cc65.c @@ -0,0 +1,14 @@ +#include <stdio.h> +#include <stdlib.h> + +/* this should fail on all three standards + */ +int main(void) { + +#if __CC65_STD__ != __CC65_STD_CC65__ + printf("\e"); +#else +#error "this needs to error on CC65 to make it through validation" +#endif + return EXIT_SUCCESS; +} diff --git a/test/val/and-eor-ora-optimizations.c b/test/val/and-eor-ora-optimizations.c new file mode 100644 index 000000000..6b5aba68c --- /dev/null +++ b/test/val/and-eor-ora-optimizations.c @@ -0,0 +1,86 @@ +/* Test some new optimization passes */ + +#include <stdio.h> + +static unsigned failures = 0; + +int func0() +{ + unsigned a = 0x1234, b = 0x55AA; + return (a & 0x00FF) & (b & 0xFF00); +} + +int func1() +{ + unsigned a = 0x1234, b = 0x55AA; + return (0x00FF & a) & (0xFF00 & b); +} + +int func2() +{ + unsigned a = 0x1234, b = 0x55AA; + return (a | 0x00FF) & (b | 0xFF00); +} + +int func3() +{ + unsigned a = 0x1234, b = 0x55AA; + return (0x00FF | a) & (0xFF00 | b); +} + +int func4() +{ + unsigned a = 0x1234, b = 0x55AA; + return (a | 0x00FF) | (b | 0xFF00); +} + +int func5() +{ + unsigned a = 0x1234, b = 0x55AA; + return (0x00FF | a) | (0xFF00 | b); +} + +int func6() +{ + unsigned a = 0x1234, b = 0x55AA; + return (a ^ 0x00FF) & (b & 0xFF00); +} + +int func7() +{ + unsigned a = 0x1234, b = 0x55AA; + return (0x00FF ^ a) & (0xFF00 & b); +} + +int func8() +{ + unsigned a = 0x1234, b = 0x55AA; + return (a | 0x00FF) | (b ^ 0xFF00); +} + +int func9() +{ + unsigned a = 0x1234, b = 0x55AA; + return (0x00FF | a) | (0xFF00 ^ b); +} + +void onetest(unsigned count, int (*f1)(void), int (*f2)(void), int result) +{ + int r1 = f1(); + int r2 = f2(); + if (r1 != result || r2 != result) { + printf("Test %u failed! Expected 0x%04X but got 0x%04X/0x%04X\n", + count, result, r1, r2); + ++failures; + } +} + +int main() +{ + onetest(1, func0, func1, 0x0000); + onetest(2, func2, func3, 0x12AA); + onetest(3, func4, func5, 0xFFFF); + onetest(4, func6, func7, 0x1000); + onetest(5, func8, func9, 0xBAFF); + return failures; +} diff --git a/test/val/bitfield-union.c b/test/val/bitfield-union.c index 1fd201456..4c01d2183 100644 --- a/test/val/bitfield-union.c +++ b/test/val/bitfield-union.c @@ -1,5 +1,5 @@ /* - Copyright 2020 The cc65 Authors + Copyright 2020-2023 The cc65 Authors This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -25,6 +25,7 @@ #include <stdio.h> typedef union { + const unsigned int : 1; unsigned int bf; struct { @@ -38,8 +39,12 @@ static unsigned char failures = 0; int main (void) { - bitfield_t bitfield = {0}; + bitfield_t bitfield = { 42 }; + printf ("Bitfield: %u\n", bitfield.bf); + if (bitfield.bf != 42) failures++; + + bitfield.bf ^= 42; printf ("Bitfield: %u\n", bitfield.bf); if (bitfield.bf != 0) failures++; diff --git a/test/val/booltrans.c b/test/val/booltrans.c new file mode 100644 index 000000000..51439d45b --- /dev/null +++ b/test/val/booltrans.c @@ -0,0 +1,161 @@ +/* Optimization bugs with multiple inverse Z branches following one boolean transformer */ + +#include <stdint.h> +#include <stdio.h> + +unsigned failures; + +int a; + +/* To reveal the bug, the second Z branch must jump over the destination of the first Z branch */ + +int test_booltrans(int8_t x) +{ + a = x; + __asm__("lda #$00"); + __asm__("cmp %v", a); + __asm__("jsr booleq"); + __asm__("jeq %g", L1); + __asm__("jne %g", L0); +L1: + return 1; +L0: + return 0; +} + +int test_bnega2(int8_t x) +{ + a = x; + __asm__("lda %v", a); + __asm__("jsr bnega"); + __asm__("jeq %g", L1); + __asm__("jne %g", L0); +L1: + return 1; +L0: + return 0; +} + +int test_bnegax2(int16_t x) +{ + int a = x; + __asm__("ldy #%o+1", a); + __asm__("jsr ldaxysp"); + __asm__("jsr bnegax"); + __asm__("jeq %g", L1); + __asm__("jne %g", L0); +L1: + return 1; +L0: + return 0; +} + +void __fastcall__ f(void) {} + +int test_bnegax3(int16_t x) +{ + a = x; + __asm__("lda %v", a); + __asm__("ldx %v+1", a); + __asm__("jsr %v", f); + __asm__("jsr bnegax"); + __asm__("jeq %g", L1); + __asm__("jne %g", L0); +L1: + return 1; +L0: + return 0; +} + +int test_bnegax4(int16_t x) +{ + a = x; + __asm__("lda %v", a); + __asm__("ldx %v+1", a); + __asm__("jsr bnegax"); + __asm__("jeq %g", L1); + __asm__("jne %g", L0); +L1: + return 1; +L0: + return 0; +} + +int main(void) +{ + a = test_booltrans(0); + if (a != 0) + { + ++failures; + printf("test_booltrans(0): %d, expected: 0\n", a); + } + + a = test_booltrans(1); + if (a != 1) + { + ++failures; + printf("test_booltrans(1): %d, expected: 1\n", a); + } + + a = test_bnega2(0); + if (a != 0) + { + ++failures; + printf("test_bnega2(0): %d, expected: 0\n", a); + } + + a = test_bnega2(1); + if (a != 1) + { + ++failures; + printf("test_bnega2(1): %d, expected: 1\n", a); + } + + a = test_bnegax2(0); + if (a != 0) + { + ++failures; + printf("test_bnegax2(0): %d, expected: 0\n", a); + } + + a = test_bnegax2(1); + if (a != 1) + { + ++failures; + printf("test_bnegax2(1): %d, expected: 1\n", a); + } + + a = test_bnegax3(0); + if (a != 0) + { + ++failures; + printf("test_bnegax3(0): %d, expected: 0\n", a); + } + + a = test_bnegax3(1); + if (a != 1) + { + ++failures; + printf("test_bnegax3(1): %d, expected: 1\n", a); + } + + a = test_bnegax4(0); + if (a != 0) + { + ++failures; + printf("test_bnegax4(0): %d, expected: 0\n", a); + } + + a = test_bnegax4(1); + if (a != 1) + { + ++failures; + printf("test_bnegax4(1): %d, expected: 1\n", a); + } + + if (failures > 0) + { + printf("failures: %u\n", failures); + } + return failures; +} diff --git a/test/val/bug1178-struct-copy.c b/test/val/bug1178-struct-copy.c index 7fb7e7803..322002c02 100644 --- a/test/val/bug1178-struct-copy.c +++ b/test/val/bug1178-struct-copy.c @@ -62,7 +62,7 @@ void dotest2(void) StructArray2[0] = test2; - printf ("test2: %d, %d, %d, %d, %d\n", + printf ("test2: %d, %d, %d, %d\n", (int)StructArray2[0].a, (int)StructArray2[0].b, (int)StructArray2[0].c, (int)StructArray2[0].d); if ((StructArray2[0].a != 42) || diff --git a/test/val/bug2018-ok.c b/test/val/bug2018-ok.c new file mode 100644 index 000000000..567beb301 --- /dev/null +++ b/test/val/bug2018-ok.c @@ -0,0 +1,71 @@ +/* Bug #2018 - Compiler has problems with const struct fields */ + +#include <stdio.h> + +unsigned failures; + +struct X { + const int; /* Useless empty declaration */ + const void; /* Useless empty declaration */ + const struct U; /* Useless(?) declaration */ + const struct V { int a; }; /* Useless(?) declaration */ + + const struct { /* Qualifier ignored in cc65 */ + int a; + }; + const union { /* Qualifier ignored in cc65 */ + int b; + }; +}; + +union Y { + const int; /* Useless empty declaration */ + const void; /* Useless empty declaration */ + const union W; /* Useless(?) declaration */ + const union T { int a; }; /* Useless(?) declaration */ + + const struct { /* Qualifier ignored in cc65 */ + int a; + }; + const union { /* Qualifier ignored in cc65 */ + int b; + }; +}; + +struct X f(struct X a) +{ + struct X x = { 42 }; + return --a.a ? a : x; +} + +union Y g(union Y a) +{ + union Y y = { 42 }; + return --a.a ? a : y; +} + +int main(void) +{ + struct X x = { 1 }; + union Y y = { 1 }; + + x = f(x); /* Allowed in cc65 since X is not read only */ + y = g(y); /* Allowed in cc65 since Y is not read only */ + + if (x.a != 42) + { + ++failures; + } + + if (y.a != 42) + { + ++failures; + } + + if (failures > 0) + { + printf("failures: %u\n", failures); + } + + return failures; +} diff --git a/test/val/bug2019-case-value.c b/test/val/bug2019-case-value.c new file mode 100644 index 000000000..b604e083a --- /dev/null +++ b/test/val/bug2019-case-value.c @@ -0,0 +1,77 @@ +/* Bug #2019 - Type promotion in switch statements seems to be broken */ + +#include <limits.h> +#include <stdio.h> + +unsigned failures; + +int f1(void) +{ + unsigned char c = 0xFF; + switch (c) { + case (signed char)0xFF: break; + case (unsigned char)0xFF: return 0; + } + return -1; +} + +int f2(void) +{ + signed char c = SCHAR_MIN; + switch (c) { + case (unsigned char)SCHAR_MIN: break; + case SCHAR_MIN: return 0; + } + return -1; +} + +int f3(void) +{ + signed int c = (int)UINT_MAX; + switch (c) { + case UINT_MAX: return 0; + } + return -1; +} + +int f4(void) +{ + unsigned int c = UINT_MAX; + switch (c) { + case -1L: return 0; + } + return -1; +} + +int main(void) +{ + if (f1()) + { + ++failures; + printf("f1() failed\n"); + } + + if (f2()) + { + ++failures; + printf("f2() failed\n"); + } + + if (f3()) + { + ++failures; + printf("f3() failed\n"); + } + + if (f4()) + { + ++failures; + printf("f4() failed\n"); + } + + if (failures > 0) + { + printf("failures: %u\n", failures); + } + return failures; +} diff --git a/test/val/bug2020-ok.c b/test/val/bug2020-ok.c new file mode 100644 index 000000000..6c52a9498 --- /dev/null +++ b/test/val/bug2020-ok.c @@ -0,0 +1,16 @@ +/* Bug #2020 - Right cases */ + +typedef int F(void); // type F is "function with no parameters returning int" + +F f, g; // f and g both have type compatible with F +int f(void) { return 0; } // RIGHT: f has type compatible with F +int g() { return 0; } // RIGHT: g has type compatible with F +F *e(void) { return 0; } // e returns a pointer to a function +F *((h))(void) { return 0; } // similar: parentheses irrelevant +int (*fp)(void); // fp points to a function that has type F +F *Fp; // Fp points to a function that has type + +int main(void) +{ + return 0; +} diff --git a/test/val/bug2135.c b/test/val/bug2135.c new file mode 100644 index 000000000..1da0d2316 --- /dev/null +++ b/test/val/bug2135.c @@ -0,0 +1,47 @@ +/* Bug #2135 - Compound initialization consumes wrong amount of initializers with omitted +** enclosing curly braces when an array/struct/union to initialize is itself +** a member/element of a struct/union/array. +*/ + +#include <stdint.h> +#include <stdio.h> + +struct s { + union { + int8_t a[2][2]; + char c[sizeof (int8_t) * 2 * 2 + sizeof (int16_t) * 4]; + }; + int16_t b[4]; +}; +struct s x = { 1, 2, 3, 4, 5, 6 }; +struct s y = { {{{1, 2}, {3, 4}}}, {5, 6} }; + +unsigned failures; + +int main(void) +{ + unsigned i, j; + + for (i = 0; i < 2; ++i) + { + for (j = 0; j < 2; ++j) + { + if (x.a[i][j] != y.a[i][j]) + { + ++failures; + printf("x.a[%u][%u] = %d\n, expected %d\n", i, j, x.a[i][j], y.a[i][j]); + } + } + } + + for (i = 0; i < 4; ++i) + { + if (x.b[i] != y.b[i]) + { + ++failures; + printf("x.b[%u] = %d\n, expected %d\n", i, x.b[i], y.b[i]); + } + } + + return failures; +} diff --git a/test/val/bug2151.c b/test/val/bug2151.c new file mode 100644 index 000000000..25f145506 --- /dev/null +++ b/test/val/bug2151.c @@ -0,0 +1,77 @@ +/* Bug #2151 - #pragma causes errors when used within functions */ + +#include <stdio.h> +#include <string.h> + +#pragma charmap(0x61, 0x61) +_Static_assert('A'== +#pragma charmap(0x61, 0x41) +'a' +#pragma charmap(0x61, 0x42) +, +#pragma charmap(0x61, 0x61) +"charmap failed"); + +char str[] = +"a" +#pragma charmap(0x61, 0x42) +"a" +#pragma charmap(0x61, 0x43) +"a" +#pragma charmap(0x61, 0x61) +; + +unsigned failures; + +#pragma bss-name("BSS1") +int +#pragma code-name("CODE_WUT") +main _Pragma +#pragma charmap(0x61, 0x32) +( +"message(\"_Pragma string" +/* Concatenated string literals in _Pragma is a cc65 extension */ +" unaffected by charmap\")" +) +#pragma charmap(0x61, 0x61) +( +void +_Pragma _Pragma ( +#pragma message("nested message 1") +"message(\"nested message 2\")" +) +( +"message(\"_Pragma in function parentheses\")") +#pragma code-name("CODE") +) +#pragma bss-name("BSS") +{ + extern int y; +#pragma bss-name("BSS2") + static +#pragma zpsym ("y") + int x; // TODO: currently in "BSS", but supposed to be in "BSS2"? + x = 0; + + if (memcmp(str, "aBC", 3)) + { + ++failures; + printf("%3s\n", str); + } + + if (x + y != 0) + { + ++failures; + printf("%d\n", x + y); + } + + if (failures != 0) + { + printf("faiures: %d\n", failures); + } + + return failures; +#pragma bss-name("BSS") +} + +int y; diff --git a/test/val/bug2186.c b/test/val/bug2186.c new file mode 100644 index 000000000..24546fb3d --- /dev/null +++ b/test/val/bug2186.c @@ -0,0 +1,39 @@ +/* Bug #2186 - Wrong array indexing when index comes from bit-field */ + +#include <stdio.h> + +unsigned failures; + +typedef struct { + char flag : 1; + char index : 7; +} weird_type; + +const char array[] = { '6', '5', '0', '2' }; + +weird_type data; + +int main(void) { + data.flag = 1; + + data.index = 0; + if (array[data.index] != array[0]) + { + ++failures; + printf("Got '%c', expected '%c'\n", array[data.index], array[0]); + } + + data.index = 1; + if (array[data.index] != array[1]) + { + ++failures; + printf("Got '%c', expected '%c'\n", array[data.index], array[1]); + } + + if (failures > 0) + { + printf("Failures: %u\n", failures); + } + + return failures; +} diff --git a/test/val/bug2207.c b/test/val/bug2207.c new file mode 100644 index 000000000..df4887fe3 --- /dev/null +++ b/test/val/bug2207.c @@ -0,0 +1,569 @@ +#include <assert.h> +#include <stdio.h> +#include <stdint.h> +#include <string.h> + + + +/********* Macros *********/ + +#define DEBUG_DETAIL 0 + +/* TODO: enable these when they can be compiled */ +#define SKIP_VOID_RETURN_VALUE_TESTS 1 +#define SKIP_INLINED_ARG_SIDE_EFFECT_TESTS 1 + +#define CHECK(C) \ + if (!(C)) { \ + ++failures; \ + print_header(); \ + printf(" failed: expected %s\n", #C); \ + } + +#define CHECK_RANGE(L, R, D, N) \ + index = my_memcmp(L, R, D, N); \ + if (index >= 0) { \ + ++failures; \ + print_header(); \ + printf(" failed: %s vs %s dismatch at [%d]\n", #L, #R, index); \ + } + +#define MEM_SIZE 512 +#define STACK_SIZE 160 +#define ZP_SIZE 8 +#define MAGIC_SIZE 129 + +#define BROKEN_STR "hello\0!" +#define BROKEN_STR_SIZE 6 +#define BROKEN_STR_LEN 5 + + + +/********* Data *********/ + +unsigned failures; +int need_header = 1; +const char* test_header; + +static int x; +static int y; +static int z; +static int index; + +static char mem_dst[MEM_SIZE]; +static char mem_src[MEM_SIZE]; +static char mem_ori[MEM_SIZE]; + +#pragma data-name(push, "ZEROPAGE", "zp") +#pragma bss-name(push, "ZEROPAGE", "zp") +static char zp_src[ZP_SIZE]; +static char zp_dst[ZP_SIZE]; +static char zp_ori[ZP_SIZE]; +static char* p_zp_src; +static char* p_zp_dst; +static char* p_zp_ori; +#pragma bss-name(pop) +#pragma data-name(pop) + + + +/********* Helpers *********/ + +void my_memfill(void *dst, int init, size_t size) +{ + unsigned i; + + for (i = 0; i < size; ++i) + { + ((char*)dst)[i] = init; + init += 3; + } +} + +void my_memset(void *dst, int val, size_t size) +{ + unsigned i; + + for (i = 0; i < size; ++i) + { + ((unsigned char *)dst)[i] = val; + } +} + +void my_memcpy(void *dst, const void *src, size_t size) +{ + unsigned i; + + for (i = 0; i < size; ++i) + { + ((char*)dst)[i] = ((char*)src)[i]; + } +} + +int my_memcmp(const void *dst, const void *src, int diff, size_t size) +{ + unsigned i; + + diff %= 256; + for (i = 0; i < size; ++i) + { + if ((unsigned char)(((unsigned char*)dst)[i] - ((unsigned char*)src)[i]) != diff) + { +#if DEBUG_DETAIL + /* DEBUG */ + printf("%d vs %d\n", (unsigned char)(((unsigned char*)dst)[i] - ((unsigned char*)src)[i]), diff); +#endif + return i; + } + } + + return -1; +} + +void* mul_two(void* p, int* px) +{ + *px *= 2; + return p; +} + +void* add_one(void* p, int* px) +{ + *px += 1; + return p; +} + +void* negate(void* p, int* px) +{ + *px = -*px; + return p; +} + +void set_header(const char* name) +{ + if (need_header == 0) + { + printf("\n"); + } + test_header = name; + need_header = 1; +} + +void print_header(void) +{ + if (need_header) + { + need_header = 0; + printf("%s test\n", test_header); + } +} + + + +/********* Tests *********/ + +/* memcpy */ +void test_memcpy(void) +{ + const char *name = 0; + unsigned size = 0; + void *p; + + /* init */ + my_memfill(mem_ori, 1000, sizeof mem_ori); + my_memfill(zp_ori, 1000, sizeof zp_ori); + p_zp_src = zp_src; + p_zp_dst = zp_dst; + p_zp_ori = zp_ori; + +#if !SKIP_INLINED_ARG_SIDE_EFFECT_TESTS + /* arg3 == 0 */ + set_header("p = memcpy(arg1, arg2, 0)"); + size = MEM_SIZE; + my_memfill(mem_dst, 3000, size); + my_memfill(mem_src, 2000, size); + x = 42; + y = -42; + z = 36; + p = memcpy(mul_two(mem_dst, &x), add_one(mem_src, &y), (negate(0, &z), 0)); + CHECK(p == mem_dst); + CHECK(x == 84); + CHECK(y == -41); + CHECK(z == -36); + CHECK_RANGE(mem_dst, mem_ori, 2000, size); + CHECK_RANGE(mem_src, mem_ori, 1000, size); +#endif + +#if !SKIP_INLINED_ARG_SIDE_EFFECT_TESTS + /* Check if the arguments are still generated if the return value is unused. + ** We have this question since the first argument could be directly used as + ** the return value when this function gets inlined. + */ +#if !SKIP_VOID_RETURN_VALUE_TESTS + set_header("(void)memcpy(arg1, arg2, 0)"); +#else + set_header("memcpy(arg1, arg2, 0)"); +#endif + size = MEM_SIZE; + my_memfill(mem_dst, 3000, size); + my_memfill(mem_src, 2000, size); + x = 42; + y = -42; + z = 36; +#if !SKIP_VOID_RETURN_VALUE_TESTS + (void) +#endif + memcpy(mul_two(mem_dst, &x), add_one(mem_src, &y), (negate(0, &z), 0)); + CHECK(x == 84); + CHECK(y == -41); + CHECK(z == -36); + CHECK_RANGE(mem_dst, mem_ori, 2000, size); + CHECK_RANGE(mem_src, mem_ori, 1000, size); +#endif + + /* The memcpy inliner will give up with further optimizations if any of + ** the arguments have side effects. + */ + + /* arg1: const addr, arg2: const addr, arg3 <= 129 */ + set_header("memcpy(const_addr_1, const_addr_2, 129) w/ side-effects"); + size = MEM_SIZE; + my_memfill(mem_dst, 3000, size); + my_memfill(mem_src, 2000, size); + x = 42; + y = -42; + z = 36; + /* memcpy size here must be an integer constant to allow the optimization */ + p = memcpy(mul_two(mem_dst, &x), add_one(mem_src, &y), (negate(0, &z), MAGIC_SIZE)); + size = MAGIC_SIZE; + CHECK(p == mem_dst); + CHECK(x == 84); + CHECK(y == -41); + CHECK(z == -36); + CHECK_RANGE(mem_dst, mem_ori, 1000, size); + CHECK_RANGE(mem_dst + size, mem_ori + size, 2000, MEM_SIZE - size); + + /* arg1: (void*)ptr, arg2: const_addr_2 */ + set_header("memcpy((void*)ptr, const_addr_2, 129)"); + { + void *ptr = mem_dst; + size = MEM_SIZE; + my_memfill(mem_dst, 3000, size); + my_memfill(mem_src, 2000, size); + /* memcpy size here must be an integer constant to allow the optimization */ + p = memcpy((void*)ptr, mem_src, MAGIC_SIZE); + size = MAGIC_SIZE; + CHECK(p == mem_dst); + CHECK_RANGE(mem_dst, mem_ori, 1000, size); + CHECK_RANGE(mem_dst + size, mem_ori + size, 2000, MEM_SIZE - size); + } + + /* arg1: const addr, arg2: const addr, arg3 <= 129 */ + set_header("memcpy(const_addr_1, const_addr_2, 129)"); + size = MEM_SIZE; + my_memfill(mem_dst, 3000, size); + my_memfill(mem_src, 2000, size); + /* memcpy size here must be an integer constant to allow the optimization */ + p = memcpy(mem_dst, mem_src, MAGIC_SIZE); + size = MAGIC_SIZE; + CHECK(p == mem_dst); + CHECK_RANGE(mem_dst, mem_ori, 1000, size); + CHECK_RANGE(mem_dst + size, mem_ori + size, 2000, MEM_SIZE - size); + + /* arg1: const addr, arg2: const addr, arg3 <= 256 */ + set_header("memcpy(const_addr_1, const_addr_2, 256)"); + size = MEM_SIZE; + my_memfill(mem_dst, 3000, size); + my_memfill(mem_src, 2000, size); + /* memcpy size here must be an integer constant to allow the optimization */ + p = memcpy(mem_dst, mem_src, 256); + size = 256; + CHECK(p == mem_dst); + CHECK_RANGE(mem_dst, mem_ori, 1000, size); + CHECK_RANGE(mem_dst + size, mem_ori + size, 2000, size - size); + + /* arg1: ptr on zp, arg2: ptr on zp, arg3 <= 256 */ + set_header("memcpy(p_on_zp_1, p_on_zp_2, 4)"); + /* We cannot allocate 256 bytes on the zeropage, unfortunately */ + my_memfill(zp_dst, 3000, ZP_SIZE); + my_memfill(zp_src, 2000, ZP_SIZE); + /* memcpy size here must be an integer constant to allow the optimization */ + p = memcpy(p_zp_dst, p_zp_src, ZP_SIZE / 2); + size = ZP_SIZE / 2; + CHECK(p == zp_dst); + CHECK_RANGE(zp_dst, zp_ori, 1000, size); + CHECK_RANGE(zp_dst + size, zp_ori + size, 2000, ZP_SIZE - size); + + /* arg1: on stack, arg2: const addr, arg3 <= 129 */ + set_header("memcpy(on_stack_1, const_addr_2, 129)"); + { + char sp_dst[STACK_SIZE]; + my_memfill(sp_dst, 3000, STACK_SIZE); + my_memfill(mem_src, 2000, STACK_SIZE); + /* memcpy size here must be an integer constant to allow the optimization */ + p = memcpy(sp_dst, mem_src, MAGIC_SIZE); + size = MAGIC_SIZE; + CHECK(p == sp_dst); + CHECK_RANGE(sp_dst, mem_ori, 1000, size); + CHECK_RANGE(sp_dst + size, mem_ori + size, 2000, STACK_SIZE - size); + } + + /* arg1: on stack, arg2: const addr, arg3 <= 256 */ + set_header("memcpy(on_stack_1, const_addr_2, 144)"); + { + char sp_dst[STACK_SIZE]; + /* We cannot allocate 256 bytes on the stack, unfortunately */ + my_memfill(sp_dst, 3000, STACK_SIZE); + my_memfill(mem_src, 2000, STACK_SIZE); + /* memcpy size here must be an integer constant to allow the optimization */ + p = memcpy(sp_dst, mem_src, 144); + size = 144; + CHECK(p == sp_dst); + CHECK_RANGE(sp_dst, mem_ori, 1000, size); + CHECK_RANGE(sp_dst + size, mem_ori + size, 2000, STACK_SIZE - size); + } + + /* arg1: const addr, arg2: on stack, arg3 <= 129 */ + set_header("memcpy(const_addr_1, on_stack_2, 129)"); + { + char sp_src[STACK_SIZE]; + /* We cannot allocate 256 bytes on the stack, unfortunately */ + my_memfill(mem_dst, 3000, STACK_SIZE); + my_memfill(sp_src, 2000, STACK_SIZE); + /* memcpy size here must be an integer constant to allow the optimization */ + p = memcpy(mem_dst, sp_src, 129); + size = 129; + CHECK(p == mem_dst); + CHECK_RANGE(mem_dst, mem_ori, 1000, size); + CHECK_RANGE(mem_dst + size, mem_ori + size, 2000, STACK_SIZE - size); + } + + /* arg1: const addr, arg2: on stack, arg3 <= 256 */ + set_header("memcpy(const_addr_1, on_stack_2, 144)"); + { + char sp_src[STACK_SIZE]; + /* We cannot allocate 256 bytes on the stack, unfortunately */ + my_memfill(mem_dst, 3000, STACK_SIZE); + my_memfill(sp_src, 2000, STACK_SIZE); + /* memcpy size here must be an integer constant to allow the optimization */ + p = memcpy(mem_dst, sp_src, 144); + size = 144; + CHECK(p == mem_dst); + CHECK_RANGE(mem_dst, mem_ori, 1000, size); + CHECK_RANGE(mem_dst + size, mem_ori + size, 2000, STACK_SIZE - size); + } +} + +/* memset */ +void test_memset(void) +{ + const char *name = 0; + unsigned size = 0; + void *p; + + /* init */ + my_memfill(mem_ori, 1000, sizeof mem_ori); + my_memfill(zp_ori, 1000, sizeof zp_ori); + p_zp_dst = zp_dst; + p_zp_ori = zp_ori; + +#if !SKIP_INLINED_ARG_SIDE_EFFECT_TESTS + /* arg3 == 0 */ + set_header("p = memset(arg1, arg2, 0)"); + size = MEM_SIZE; + my_memfill(mem_dst, 3000, size); + x = 42; + y = -42; + z = 36; + p = memset(mul_two(mem_dst, &x), (add_one(0, &y), 42), (negate(0, &z), 0)); + CHECK(p == mem_dst); + CHECK(x == 84); + CHECK(y == -41); + CHECK(z == -36); + CHECK_RANGE(mem_dst, mem_ori, 2000, size); +#endif + +#if !SKIP_INLINED_ARG_SIDE_EFFECT_TESTS + /* Check if the arguments are still generated if the return value is unused. + ** We have this question since the first argument could be directly used as + ** the return value when this function gets inlined. + */ +#if !SKIP_VOID_RETURN_VALUE_TESTS + set_header("(void)memset(arg1, arg2, 0)"); +#else + set_header("memset(arg1, arg2, 0)"); +#endif + size = MEM_SIZE; + my_memfill(mem_dst, 3000, size); + x = 42; + y = -42; + z = 36; +#if !SKIP_VOID_RETURN_VALUE_TESTS + (void) +#endif + memset(mul_two(mem_dst, &x), (add_one(0, &y), 42), (negate(0, &z), 0)); + CHECK(x == 84); + CHECK(y == -41); + CHECK(z == -36); + CHECK_RANGE(mem_dst, mem_ori, 2000, size); +#endif +} + +/* strcmp */ +void test_strcmp(void) +{ + const char *name = 0; + unsigned size = 0; + int res = 0; + + /* init */ + p_zp_dst = zp_dst; + + /* Compared to zero-length C string literal */ + set_header("strcmp(arg1, \"\\0Z\")"); + { + char str[] = "AA"; + size = sizeof str; + my_memcpy(zp_dst, str, size); + x = 42; + y = -42; + + res = strcmp(zp_dst, "\0Z"); + CHECK(res > 0); + +#if !SKIP_INLINED_ARG_SIDE_EFFECT_TESTS + res = strcmp(mul_two(p_zp_dst, &x), (add_one(0, &y), "\0Z")); + CHECK(res > 0); + CHECK(x == 84); + CHECK(y == -41); +#endif + } +} + +/* strcpy */ +void test_strcpy(void) +{ + const char *name = 0; + unsigned size = 0; + void *p; + char stack_dst[ZP_SIZE]; + char stack_src[ZP_SIZE]; + + /* init */ + { + char str[] = BROKEN_STR; + size = BROKEN_STR_LEN + 1; + my_memfill(mem_src, 1000, ZP_SIZE); + my_memfill(stack_src, 1000, ZP_SIZE); + my_memfill(zp_src, 1000, ZP_SIZE); + my_memcpy(mem_src, str, size); + my_memcpy(stack_src, str, size); + my_memcpy(zp_src, str, size); + p_zp_src = zp_src; + p_zp_dst = zp_dst; + p_zp_dst = zp_dst; + } + + /* arg1: const addr, arg2: const addr */ + set_header("strcpy(const_addr_1, const_addr_2)"); + my_memfill(mem_dst, 2000, ZP_SIZE); + size = BROKEN_STR_LEN + 1; + p = strcpy(mem_dst, mem_src); + CHECK(p == mem_dst); + CHECK_RANGE(mem_dst, mem_src, 0, size); + CHECK_RANGE(mem_dst + size, mem_src + size, 1000, ZP_SIZE - size); + + /* arg1: ptr on zp, arg2: ptr on zp */ + set_header("strcpy(p_on_zp_1, p_on_zp_2)"); + my_memfill(zp_dst, 2000, ZP_SIZE); + size = BROKEN_STR_LEN + 1; + p = strcpy(zp_dst, zp_src); + CHECK(p == zp_dst); + CHECK_RANGE(zp_dst, zp_src, 0, size); + CHECK_RANGE(zp_dst + size, zp_src + size, 1000, ZP_SIZE - size); + + /* arg1: on stack, arg2: on stack */ + set_header("strcpy(on_stack_1, on_stack_2)"); + my_memfill(stack_dst, 2000, ZP_SIZE); + size = BROKEN_STR_LEN + 1; + p = strcpy(stack_dst, stack_src); + CHECK(p == stack_dst); + CHECK_RANGE(stack_dst, stack_src, 0, size); + CHECK_RANGE(stack_dst + size, stack_src + size, 1000, ZP_SIZE - size); + + /* TODO: args side-effects tests */ +} + +/* strlen */ +void test_strlen(void) +{ + const char *name = 0; + size_t len; + + /* init */ + { + char str[] = BROKEN_STR; + my_memcpy(mem_ori, str, ZP_SIZE); + //my_memcpy(stack_ori, str, ZP_SIZE); + my_memcpy(zp_ori, str, ZP_SIZE); + p_zp_ori = zp_ori; + } + + /* arg1: string_literal */ + set_header("strlen(\"hello\\0!\")"); + x = 42; + y = -42; + len = strlen((mul_two(0, &x), BROKEN_STR)); + (void)strlen((add_one(0, &y), BROKEN_STR)); + CHECK(len == BROKEN_STR_LEN); + CHECK(x == 84); + CHECK(y == -41); + + /* arg1: array with const addr */ + set_header("strlen(array_const_addr[8])"); + x = 42; + y = -42; + len = strlen((mul_two(0, &x), zp_ori)); + (void)strlen((add_one(0, &y), zp_ori)); + CHECK(len == BROKEN_STR_LEN); + CHECK(x == 84); + CHECK(y == -41); + + /* arg1: array on stack */ + set_header("strlen(array_on_stack[8])"); + { + char p_on_stack[] = BROKEN_STR; + x = 42; + y = -42; + len = strlen((mul_two(0, &x), p_on_stack)); + strlen((add_one(0, &y), p_on_stack)); + CHECK(sizeof p_on_stack == 8); + CHECK(len == BROKEN_STR_LEN); + CHECK(x == 84); + CHECK(y == -41); + } + + /* arg1: ptr on zp */ + set_header("strlen(ptr_on_zp)"); + x = 42; + y = -42; + len = strlen((mul_two(0, &x), p_zp_ori)); + (void)strlen((add_one(0, &y), p_zp_ori)); + CHECK(len == BROKEN_STR_LEN); + CHECK(x == 84); + CHECK(y == -41); +} + +int main(void) +{ + test_memcpy(); + test_memset(); + test_strcmp(); + test_strcpy(); + test_strlen(); + + if (failures > 0) + { + printf("failed items: %u\n", failures); + } + return failures; +} diff --git a/test/val/bug2302.c b/test/val/bug2302.c new file mode 100644 index 000000000..3ca8a5572 --- /dev/null +++ b/test/val/bug2302.c @@ -0,0 +1,32 @@ +/* Bug #2302 - Parameters of function types not parsed correctly */ + +#include <stdio.h> + +typedef int A; +int zoo(A ()); /* OK: int zoo(int (*)()) */ +int zoo(A (())); /* OK: int zoo(int ((*)())) aka. int zoo(int (*)()) */ +int zoo(A (A)); /* OK: int zoo(int (*)(int)) */ +int zoo(A ((A))); /* OK: int zoo(int ((*)(int))) aka. int zoo(int (*)(int)) */ +int zoo(A A(A)); /* OK: int zoo(int (*A)(int)) */ +int zoo(A (*)(A)); /* OK: int zoo(int (*)(int)) */ +int zoo(A (*A)(A)); /* OK: int zoo(int (*A)(int)) */ +int zoo(A ((*A))(A)); /* OK: int zoo(int (*A)(int)) */ +int zoo(A ((((*((fp))))(A A)))) /* OK: int zoo(int (*fp)(int A)) */ +{ + return fp(42); +} + +int bar(int a) +{ + return a ^ 42; +} + +int main(void) +{ + int a = zoo((int (*)())bar); + if (a != 0) + { + printf("failed: a = %d\n", a); + } + return a; +} diff --git a/test/val/bug2327-cconv-type.c b/test/val/bug2327-cconv-type.c new file mode 100644 index 000000000..678727278 --- /dev/null +++ b/test/val/bug2327-cconv-type.c @@ -0,0 +1,111 @@ +/* Bug #2327 - Calling conventions and address size specifiers of functions in type names and parameter types are not parsed correctly */ + +#include <stdint.h> +#include <stdio.h> + +unsigned failures; +int need_header = 1; +const char* test_header; + +/* Helpers */ +void set_header(const char* name) +{ + if (need_header == 0) + { + printf("\n"); + } + test_header = name; + need_header = 1; +} + +void print_header(void) +{ + if (need_header) + { + need_header = 0; + printf("<%s test>\n", test_header); + } +} + +#define CHECK(R, E) \ + do { \ + if ((R) != (E)) { \ + ++failures; \ + print_header(); \ + printf(" fail: %s = %d\n", #R, (R)); \ + } \ + } while (0); + +#define CHECK_RV() CHECK(rv, 42) +#define CHECK_SP() CHECK(x - (intptr_t)&x, 0) + +#define FUNC_QUAL __cdecl__ __near__ + +typedef int hoo_t(int __far__ __cdecl__ ()); +typedef int hoo_t(int __far__ (__cdecl__)()); /* Question: should this be rejected? */ +typedef int hoo_t(int __far__ (__cdecl__ ())); +typedef int hoo_t(int __far__ (__cdecl__ *)()); +typedef int hoo_t(int __far__ (__cdecl__ (*)())); +typedef int hoo_t(int __far__ ((__cdecl__ *)())); + +typedef int hoo_t(int __cdecl__ __far__ ()); +typedef int hoo_t(int (__cdecl__ __far__)()); /* Question: should this be rejected? */ +typedef int hoo_t(int (__cdecl__ __far__ ())); +typedef int hoo_t(int (__cdecl__ __far__ *)()); +typedef int hoo_t(int (__cdecl__ (__far__ *)())); +typedef int hoo_t(int ((__cdecl__ __far__ *)())); + +typedef int (FUNC_QUAL foo_t)(int, int); +typedef int (FUNC_QUAL *pfoo_t)(int, int); + +int FUNC_QUAL foo(int a, int b) +{ + return a * b; +} + +int (FUNC_QUAL * const pfoo)() = (int (FUNC_QUAL *)())foo; + +/* Incompatible and not working for cc65 if used as-is */ +int (*qfoo)(int, ...) = foo; + +int main(void) +{ + int rv; + intptr_t x; + + set_header("init"); + x = (intptr_t)&x; + CHECK_SP() + + set_header("foo"); + rv = foo((int8_t)-3, (int32_t)-14); + CHECK_RV() + CHECK_SP() + + set_header("pfoo"); +#if 0 + /* This would fail */ + rv = pfoo((int8_t)-6, (int32_t)-7); +#else + rv = ((pfoo_t)pfoo)((int8_t)-6, (int32_t)-7); +#endif + CHECK_RV() + CHECK_SP() + + set_header("qfoo"); +#if 0 + /* This would fail */ + rv = (qfoo)((int32_t)-6, (int8_t)-7); +#else + rv = ((foo_t *)qfoo)((int32_t)-6, (int8_t)-7); +#endif + CHECK_RV() + CHECK_SP() + + if (failures > 0) + { + printf("\nfailures: %u\n", failures); + } + + return failures; +} diff --git a/test/val/bug2357.c b/test/val/bug2357.c new file mode 100644 index 000000000..a0cff0d19 --- /dev/null +++ b/test/val/bug2357.c @@ -0,0 +1,38 @@ +/* bug #2357 - Compiler produces invalid code after d8a3938 +*/ + +unsigned long test; + +unsigned long longarray[7]; + +void jsr_threebytes(void) { + +} + +/* having replaced two sty $zp with two sta $abs, but forgetting + * to update the instruction size, coptlong.c could cause a build + * error "Error: Range error (131 not in [-128..127])" if the + * computed codesize was under 126, but the real codesize was above + * 127. + * This tests verifies that the bug is fixed. + */ +unsigned char __fastcall__ foo (unsigned char res) +{ + if (res == 0) { + longarray[1]=test; /* 24 bytes - but the compiler thought 22 */ + longarray[2]=test; /* 48 bytes - but 44 */ + longarray[3]=test; /* 72 bytes - 66 */ + longarray[4]=test; /* 96 bytes - 88 */ + longarray[6]=test; /* 120 bytes - 110 */ + jsr_threebytes(); /* 123 - 113 */ + jsr_threebytes(); /* 126 - 116 */ + jsr_threebytes(); /* 129 - 119 */ + } + return 0; +} + +int main (void) +{ + foo(42); + return 0; +} diff --git a/test/val/bug2395.c b/test/val/bug2395.c new file mode 100644 index 000000000..07c5cd7c5 --- /dev/null +++ b/test/val/bug2395.c @@ -0,0 +1,44 @@ + +/* bug #2395: Bitwise operators with a boolean expression fail when optimized */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +unsigned char a, b; +unsigned char c = 199; +unsigned char d = 100; + +int main(void) { + int fails = 0; + + a = c ^ (d != 0); + b = c ^ 1; + + printf("%u ^ (%u != 0) => %u\n", c, d, a); + if (a != b) { + printf("XOR error: a %d instead of %d\n", a, b); + fails++; + } + + a = c | (d != 0); + b = c | 1; + + printf("%u | (%u != 0) => %u\n", c, d, a); + if (a != b) { + printf("OR error: a %d instead of %d\n", a, b); + fails++; + } + + a = c & (d != 0); + b = c & 1; + + printf("%u & (%u != 0) => %u\n", c, d, a); + if (a != b) { + printf("AND error: a %d instead of %d\n", a, b); + fails++; + } + printf("%d errors\n", fails); + + return fails; +} diff --git a/test/val/bug2458.c b/test/val/bug2458.c new file mode 100644 index 000000000..1494a7946 --- /dev/null +++ b/test/val/bug2458.c @@ -0,0 +1,10 @@ +#define str(arg) #arg +#include str(bug2458.h) /* Ok, macro replacement */ + +#define string foo +#include <string.h> /* Ok, no macro replacement */ + +int main() +{ + return 0; +} diff --git a/test/val/bug2458.h b/test/val/bug2458.h new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/test/val/bug2458.h @@ -0,0 +1 @@ + diff --git a/test/val/bug2461.c b/test/val/bug2461.c new file mode 100644 index 000000000..e8340b224 --- /dev/null +++ b/test/val/bug2461.c @@ -0,0 +1,67 @@ +/* related to bug #2461 */ + +/* Note: The values for MASK1, MASK2, the return values of GarbleAX and the + * arguments for CALC() are carefully chosen to elicit the bug. + */ + +#include <stdio.h> + +#define MASK1 0x000FU +#define MASK2 0x00FFU +#define CALC(num, op) (((num) & (~MASK1)) op ((num) & MASK2)) + +static unsigned Failures = 0; +static unsigned TestCount = 0; + +unsigned GarbleAX(void) +{ + static const unsigned Garbage[] = { + 0x1234, 0x0000, 0x1234, 0x1234 + }; + return Garbage[TestCount - 1]; +} + +unsigned WrongAdd(unsigned num) +{ + unsigned ret=GarbleAX(); + return CALC(num, +); +} + +unsigned WrongAnd(unsigned num) +{ + unsigned ret=GarbleAX(); + return CALC(num, &); +} + +unsigned WrongOr(unsigned num) +{ + unsigned ret=GarbleAX(); + return CALC(num, |); +} + +unsigned WrongXor(unsigned num) +{ + unsigned ret=GarbleAX(); + return CALC(num, ^); +} + +void Test(unsigned (*F)(unsigned), unsigned Num, unsigned Ref) +{ + unsigned Res; + ++TestCount; + Res = F(Num); + if (Res != Ref) { + printf("Test %u failed: got %04X, expected %04X\n", TestCount, Res, Ref); + ++Failures; + } +} + +int main(void) +{ + Test(WrongAdd, 0x4715, CALC(0x4715, +)); + Test(WrongAnd, 0x4715, CALC(0x4715, &)); + Test(WrongOr, 0x4715, CALC(0x4715, |)); + Test(WrongXor, 0x4715, CALC(0x4715, ^)); + printf("Failures: %u\n", Failures); + return Failures; +} diff --git a/test/val/bug2520.c b/test/val/bug2520.c new file mode 100644 index 000000000..7a216df4d --- /dev/null +++ b/test/val/bug2520.c @@ -0,0 +1,4 @@ +#if (1 ? 2 : 0) != 2 +#error +#endif +int main() { return 0; } diff --git a/test/val/bug2523.c b/test/val/bug2523.c new file mode 100644 index 000000000..8db72d055 --- /dev/null +++ b/test/val/bug2523.c @@ -0,0 +1,29 @@ +#if (0u - 1) < 0 +#error +#endif + +#if !1u - 1 > 0 +#error +#endif + +#if (1 & 1u) - 2 < 0 +#error +#endif + +#if (1 | 1u) - 2 < 0 +#error +#endif + +#if (1 ^ 1u) - 2 < 0 +#error +#endif + +#if (1u >> 1) - 2 < 0 +#error +#endif + +#if (0u << 1) - 1 < 0 +#error +#endif + +int main() { return 0; } diff --git a/test/val/bug2566.c b/test/val/bug2566.c new file mode 100644 index 000000000..29292c3dd --- /dev/null +++ b/test/val/bug2566.c @@ -0,0 +1,49 @@ + +/* Regression test for https://github.com/cc65/cc65/issues/2566 + * + * The issue was introduced in an innocious-looking commit back in 2020: + * + * https://github.com/cc65/cc65/commit/c3a6b399456937093eda9994f19b7f722731528d + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdbool.h> + +typedef struct { + unsigned long Field1; + unsigned long Field2; +} RecordType; + +typedef struct { + char dummy; // -- Offsets the Rec field by 1 byte. + RecordType Rec; +} StructTypeA; + +typedef struct { + RecordType Rec; +} StructTypeB; + +int main(void) +{ + StructTypeA A, *Aptr; + StructTypeB B; + bool ok; + + A.Rec.Field1 = 0x11111111; + A.Rec.Field2 = 0x22222222; + + Aptr = &A; + B.Rec = Aptr->Rec; + + /* These print statements give some clues as to what's going on. */ + /* + printf("A.Rec: %lx, %lx\n", A.Rec.Field1, A.Rec.Field2); + printf("B.Rec: %lx, %lx\n", B.Rec.Field1, B.Rec.Field2); + */ + + ok = (A.Rec.Field1 == B.Rec.Field1 && A.Rec.Field2 == B.Rec.Field2); + + return ok ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/test/val/bug2609.c b/test/val/bug2609.c new file mode 100644 index 000000000..418673e98 --- /dev/null +++ b/test/val/bug2609.c @@ -0,0 +1,72 @@ +/* Bug #2609 - charmap translation violates C specification 6.4.4.4 Character constant */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> + +#pragma charmap (0x07, 0x62) /* map \a to b */ +static_assert('\a' == 0x62); +static_assert('\07' == 0x07); +static_assert('\x07' == 0x07); + +#pragma charmap (0x07, 0x63) /* map \a to c */ +static_assert('\a' == 0x63); +static_assert('\07' == 0x07); +static_assert('\x07' == 0x07); + +#pragma charmap (0x07, 0x07) /* map \a back to x07 */ +static_assert('\a' == 0x07); +static_assert('\07' == 0x07); +static_assert('\x07' == 0x07); + +#pragma charmap (0x07, 0x61) /* map \a to a */ + +char *s = "\07\a\x07"; +char t[] = { 7, 0x61, 7, 0 }; + +static_assert('\a' == 0x61); +static_assert('\07' == 0x07); +static_assert('\x07' == 0x07); + +char c_back_a = '\a'; +char c_hex_07 = '\x07'; +char c_oct_07 = '\07'; +int i_back_a = '\a'; +int i_hex_07 = '\x07'; +int i_oct_07 = '\07'; + +#define TEST(a,b) \ + if (a != b) { printf("\n\n !FAIL! %s = %04x not %04x\n\n", #a, a, b); return EXIT_FAILURE; } + +int main (void) { + int i; + + TEST(c_back_a, 0x61) + TEST(c_hex_07, 0x07) + TEST(c_oct_07, 07) + + TEST(i_back_a, 0x61) + TEST(i_hex_07, 0x07) + TEST(i_oct_07, 07) + + assert('\a' == 0x61); + assert('\07' == 0x07); + assert('\x07' == 0x07); + + if (strcmp(s,t) || s[0] == s[1]) { + printf("\n\n !FAIL! strcmp\n"); + for (i = 0; i < 4; i++) { + printf("%02x ", s[i]); + } + printf("\n"); + for (i = 0; i < 4; i++) { + printf("%02x ", t[i]); + } + printf("\n"); + printf("\n"); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} diff --git a/test/val/bug2610.c b/test/val/bug2610.c new file mode 100644 index 000000000..75169b81d --- /dev/null +++ b/test/val/bug2610.c @@ -0,0 +1,15 @@ +#include <stdio.h> +#if '\x0A' != 0x0A +#error "Suspicious character set translation" +#endif +int main() +{ + char c = '\x0A'; + if (c == 0x0A) { + printf("Ok\n"); + return 0; + } else { + printf("Failed\n"); + return 1; + } +} diff --git a/test/val/compare8.c b/test/val/compare8.c index 2621dad1d..3004787ff 100644 --- a/test/val/compare8.c +++ b/test/val/compare8.c @@ -23,6 +23,8 @@ bit bit0 = 0; #endif int int0 = 0; int int1 = 0; +unsigned int uint0 = 0; +unsigned int uint1 = 0; unsigned char uchar0 = 0; unsigned char uchar1 = 0; signed char char0 = 0; @@ -296,12 +298,95 @@ void int_compare2(void) c_int_gt_lit2(0xff); } +void c_uint_gt_lit1(unsigned char expected_result) +{ + result = 0; + + if(uint0 > 0) + result |= 1; + + if(uint0 > 1) + result |= 2; + + if(uint0 > 0xff) + result |= 4; + + if(uint0 > 0x100) + result |= 8; + + if(uint0 > 0x0101) + result |= 0x10; + + if(uint0 > 0x01ff) + result |= 0x20; + + if(uint0 > 0x0200) + result |= 0x40; + + if(uint0 > 0x0201) + result |= 0x80; + + if(result != expected_result) + failures=1; +} + +void uint_compare1(void) +{ + uint0 = 0; + c_uint_gt_lit1(0x00); + + uint0 = 1; + c_uint_gt_lit1(0x01); + + uint0 = 2; + c_uint_gt_lit1(0x03); + + uint0 = 0xfe; + c_uint_gt_lit1(0x03); + + uint0 = 0xff; + c_uint_gt_lit1(0x03); + + uint0 = 0x100; + c_uint_gt_lit1(0x07); + + uint0 = 0x101; + c_uint_gt_lit1(0x0f); + + uint0 = 0x102; + c_uint_gt_lit1(0x1f); + + uint0 = 0x1fe; + c_uint_gt_lit1(0x1f); + + uint0 = 0x1ff; + c_uint_gt_lit1(0x1f); + + uint0 = 0x200; + c_uint_gt_lit1(0x3f); + + uint0 = 0x201; + c_uint_gt_lit1(0x7f); + + uint0 = 0x7f00; + c_uint_gt_lit1(0xff); + + /* now check contiguous ranges */ + + for(uint0 = 2; uint0 != 0xff; uint0++) + c_uint_gt_lit1(0x03); + + for(uint0 = 0x202; uint0 != 0xffff; uint0++) + c_uint_gt_lit1(0xff); +} + int main (void) { char_compare(); int_compare1(); int_compare2(); + uint_compare1(); success = failures; done (); diff --git a/test/val/fam.c b/test/val/fam.c new file mode 100644 index 000000000..df5df2876 --- /dev/null +++ b/test/val/fam.c @@ -0,0 +1,31 @@ +/* Bug #2016 and #2017 - flexible array members */ + +typedef struct { + int a; + int b[]; /* Ok: Flexible array member can be last */ +} X; + +typedef union { + X x; /* Ok: Contains flexible array member */ + int a; +} U; + +typedef struct { + struct { + int a; + }; + int b[]; /* Ok: Flexible array member can be last */ +} Y; + +X x; +U u; +Y y; + +_Static_assert(sizeof x == sizeof (int), "sizeof x != sizeof (int)"); +_Static_assert(sizeof u == sizeof (int), "sizeof u != sizeof (int)"); +_Static_assert(sizeof y == sizeof (int), "sizeof y != sizeof (int)"); + +int main(void) +{ + return 0; +} diff --git a/test/val/float-const-convert.c b/test/val/float-const-convert.c new file mode 100644 index 000000000..729595e0f --- /dev/null +++ b/test/val/float-const-convert.c @@ -0,0 +1,14 @@ +/* Demonstrates that floating point constants are allowed in a limited way. + Value will be converted to an int, with a warning if precision is lost. */ + +int a = 3.0; +int b = 23.1; +int c = -5.0; + +int main(void) +{ + if (a != 3) return 1; + if (b != 23) return 2; + if (c != -5) return 3; + return 0; +} diff --git a/test/todo/inline-asm-1489.c b/test/val/inline-asm-1489.c similarity index 83% rename from test/todo/inline-asm-1489.c rename to test/val/inline-asm-1489.c index 57699ab0a..a531c7405 100644 --- a/test/todo/inline-asm-1489.c +++ b/test/val/inline-asm-1489.c @@ -1,9 +1,11 @@ +#include <stddef.h> #include <stdlib.h> #include <stdio.h> #include <stdint.h> #include <string.h> +#ifdef __CC65__ #define testasm1(C) (__AX__ = (C), \ asm("and #$3f"),\ __AX__) @@ -11,6 +13,22 @@ #define testasm2(C) (__A__ = (C), \ asm("and #$3f"),\ __A__) +#else +/* Non-cc65 compiler. Just make the code compile and work. */ +uint16_t testasm1(uint16_t C) +{ + uint16_t AX = C; + AX &= 0x3f; + return AX; +} + +uint8_t testasm2(uint8_t C) +{ + uint8_t A = C; + A &= 0x3f; + return A; +} +#endif uint8_t src[32] = { 0x10, 0x41, 0x62, 0x83, 0xb4, 0xf5, 0xe6, 0xc7, 0, 0 }; uint8_t src2[32] = { 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0, 0 }; @@ -46,7 +64,7 @@ void dotest1b(uint8_t *s, uint8_t *d) void dotest2a (void) { char *p = &src2[0]; - uint16_t scaddr=&dest[0]; //output to line 11 on the screen + uintptr_t scaddr=&dest[0]; //output to line 11 on the screen printf("dotest2a\n"); while (*p != 0) { @@ -58,7 +76,7 @@ void dotest2a (void) void dotest2b (void) { char *p = &src2[0]; - uint16_t scaddr=&dest[0]; //output to line 11 on the screen + uintptr_t scaddr=&dest[0]; //output to line 11 on the screen printf("dotest2b\n"); while (*p != 0) { diff --git a/test/val/inline-func.c b/test/val/inline-func.c new file mode 100644 index 000000000..b9e127aae --- /dev/null +++ b/test/val/inline-func.c @@ -0,0 +1,20 @@ +/* C99 inline */ + +#include <stdlib.h> + +inline static int f(int x, ...) +{ + return x * 2; +} + +extern inline int g(int x); + +int main(void) +{ + return f(g(7)) == 42 ? EXIT_SUCCESS : EXIT_FAILURE; +} + +int g(int x) +{ + return x * 3; +} diff --git a/test/val/lib_common_gmtime_localtime.c b/test/val/lib_common_gmtime_localtime.c new file mode 100644 index 000000000..f0d9a8332 --- /dev/null +++ b/test/val/lib_common_gmtime_localtime.c @@ -0,0 +1,110 @@ +#include <stdio.h> +#include <string.h> +#include <time.h> + +int fails = 0; + +typedef struct _test_data { + time_t t; + char *gmt; + char *local; +} test_data; + +/* Test data generated using glibc 2.37 */ +test_data data[] = { + /* First year */ + {0x00000000, "Thu Jan 1 00:00:00 1970\n", "Thu Jan 1 01:00:00 1970\n"}, + {0x004e7970, "Sun Mar 1 12:34:56 1970\n", "Sun Mar 1 13:34:56 1970\n"}, + {0x01e1337f, "Thu Dec 31 23:59:59 1970\n", "Fri Jan 1 00:59:59 1971\n"}, + + /* First leap year */ + {0x03c26700, "Sat Jan 1 00:00:00 1972\n", "Sat Jan 1 01:00:00 1972\n"}, + {0x03c8fe7f, "Wed Jan 5 23:59:59 1972\n", "Thu Jan 6 00:59:59 1972\n"}, + {0x041180ff, "Tue Feb 29 23:59:59 1972\n", "Wed Mar 1 00:59:59 1972\n"}, + {0x04118100, "Wed Mar 1 00:00:00 1972\n", "Wed Mar 1 01:00:00 1972\n"}, + {0x05a4ebff, "Sun Dec 31 23:59:59 1972\n", "Mon Jan 1 00:59:59 1973\n"}, + + /* A non-leap year */ + {0x63b0cd00, "Sun Jan 1 00:00:00 2023\n", "Sun Jan 1 01:00:00 2023\n"}, + {0x63fe957f, "Tue Feb 28 23:59:59 2023\n", "Wed Mar 1 00:59:59 2023\n"}, + {0x63fe9580, "Wed Mar 1 00:00:00 2023\n", "Wed Mar 1 01:00:00 2023\n"}, + {0x656d4ec0, "Mon Dec 4 04:00:00 2023\n", "Mon Dec 4 05:00:00 2023\n"}, + {0x6592007f, "Sun Dec 31 23:59:59 2023\n", "Mon Jan 1 00:59:59 2024\n"}, + + /* Another leap year */ + {0x65920080, "Mon Jan 1 00:00:00 2024\n", "Mon Jan 1 01:00:00 2024\n"}, + {0x65e11a7f, "Thu Feb 29 23:59:59 2024\n", "Fri Mar 1 00:59:59 2024\n"}, + {0x65e11a80, "Fri Mar 1 00:00:00 2024\n", "Fri Mar 1 01:00:00 2024\n"}, + {0x6774857f, "Tue Dec 31 23:59:59 2024\n", "Wed Jan 1 00:59:59 2025\n"}, + + /* End of century */ + {0xf48656ff, "Thu Dec 31 23:59:59 2099\n", "Fri Jan 1 00:59:59 2100\n"}, + + /* A non-leap year for exceptional reasons */ + {0xf4865700, "Fri Jan 1 00:00:00 2100\n", "Fri Jan 1 01:00:00 2100\n"}, + {0xf4d41f7f, "Sun Feb 28 23:59:59 2100\n", "Mon Mar 1 00:59:59 2100\n"}, + {0xf4d41f80, "Mon Mar 1 00:00:00 2100\n", "Mon Mar 1 01:00:00 2100\n"}, + {0xf4fceff0, "Wed Mar 31 23:00:00 2100\n", "Thu Apr 1 00:00:00 2100\n"}, + {0xf6678a7f, "Fri Dec 31 23:59:59 2100\n", "Sat Jan 1 00:59:59 2101\n"}, + + /* First post-2100 leap year */ + {0xfc0b2500, "Tue Jan 1 00:00:00 2104\n", "Tue Jan 1 01:00:00 2104\n"}, + {0xfc5a3eff, "Fri Feb 29 23:59:59 2104\n", "Sat Mar 1 00:59:59 2104\n"}, + {0xfc5a3f00, "Sat Mar 1 00:00:00 2104\n", "Sat Mar 1 01:00:00 2104\n"}, + {0xfcaa9c70, "Wed Apr 30 23:00:00 2104\n", "Thu May 1 00:00:00 2104\n"}, + + /* End of epoch */ + {0xfdedaa00, "Thu Jan 1 00:00:00 2105\n", "Thu Jan 1 01:00:00 2105\n"}, + {0xffffffff, "Sun Feb 7 06:28:15 2106\n", "Thu Jan 1 00:59:59 1970\n"} +}; + +int main (void) +{ + int i; + struct tm *tm; + char *str; + + tm = gmtime(NULL); + if (tm != NULL) { + printf("gmtime should return NULL with a NULL parameter\n"); + fails++; + } + + tm = localtime(NULL); + if (tm != NULL) { + printf("localtime should return NULL with a NULL parameter\n"); + fails++; + } + + /* Verify conversion both ways */ + for (i = 0; ; i++) { + time_t t = data[i].t; + + tm = gmtime(&t); + str = asctime(tm); + if (strcmp(data[i].gmt, str)) { + printf("0x%lx: gmtime: unexpected result: expected %s, got %s\n", t, data[i].gmt, str); + fails++; + } + + _tz.timezone = 0; + tm = localtime(&t); + str = asctime(tm); + if (strcmp(data[i].gmt, str)) { + printf("0x%lx: localtime (UTC+0): unexpected result: expected %s, got %s\n", t, data[i].gmt, str); + fails++; + } + + _tz.timezone = 3600; + tm = localtime(&t); + str = asctime(tm); + if (strcmp(data[i].local, str)) { + printf("0x%lx: localtime (UTC+1): unexpected result: expected %s, got %s\n", t, data[i].local, str); + fails++; + } + + if (t == 0xFFFFFFFF) + break; + } + return fails; +} diff --git a/test/val/lib_common_htonl.c b/test/val/lib_common_htonl.c new file mode 100644 index 000000000..53a210a84 --- /dev/null +++ b/test/val/lib_common_htonl.c @@ -0,0 +1,34 @@ +/* + !!DESCRIPTION!! A small test for htons. + !!ORIGIN!! + !!LICENCE!! + !!AUTHOR!! Colin Leroy-Mira +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <arpa/inet.h> + +static unsigned int Failures = 0; + +static void CheckHtonl (long input, long expected) +{ + long result = htonl(input); + if (result != expected) { + printf ("htonl error:\n" + " result = %ld for %ld, should be %ld\n", result, input, expected); + ++Failures; + } +} + +int main (void) +{ + CheckHtonl(0x00000000, 0x00000000); + CheckHtonl(0x12345678, 0x78563412); + CheckHtonl(0xAABBCCDD, 0xDDCCBBAA); + CheckHtonl(0xFFFFFFFF, 0xFFFFFFFF); + + printf ("Failures: %u\n", Failures); + + return Failures; +} diff --git a/test/val/lib_common_htons.c b/test/val/lib_common_htons.c new file mode 100644 index 000000000..42bbb3d6b --- /dev/null +++ b/test/val/lib_common_htons.c @@ -0,0 +1,34 @@ +/* + !!DESCRIPTION!! A small test for htons. + !!ORIGIN!! + !!LICENCE!! + !!AUTHOR!! Colin Leroy-Mira +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <arpa/inet.h> + +static unsigned int Failures = 0; + +static void CheckHtons (int input, int expected) +{ + int result = htons(input); + if (result != expected) { + printf ("htons error:\n" + " result = %d for %d, should be %d\n", result, input, expected); + ++Failures; + } +} + +int main (void) +{ + CheckHtons(0x0000, 0x0000); + CheckHtons(0x1234, 0x3412); + CheckHtons(0xA0F2, 0xF2A0); + CheckHtons(0xFFFF, 0xFFFF); + + printf ("Failures: %u\n", Failures); + + return Failures; +} diff --git a/test/val/lib_common_malloc.c b/test/val/lib_common_malloc.c new file mode 100644 index 000000000..5c68dc39a --- /dev/null +++ b/test/val/lib_common_malloc.c @@ -0,0 +1,34 @@ +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include "unittest.h" + +TEST +{ + char *buf, *buf2; + unsigned int i; + + buf = malloc(0); + ASSERT_IsTrue (buf == NULL, "malloc (0) returned something"); + + for (i = 1; i < 10; i++) { + buf = malloc(i); + ASSERT_IsTrue (buf != NULL, "small returned nothing"); + } + + buf = malloc(4096); + ASSERT_IsTrue (buf != NULL, "malloc (4096) returned nothing"); + + buf = malloc(61000UL); + ASSERT_IsTrue (buf == NULL, "malloc (61000) returned something"); + + for (i = 65535UL; i > _heapmaxavail(); i--) { + buf = malloc(i); + ASSERT_IsTrue (buf == NULL, "malloc returned something but shouldn't have"); + } + + buf = malloc(i); + ASSERT_IsTrue (buf != NULL, "malloc returned nothing but should have"); + ASSERT_IsTrue(_heapmaxavail() == 0, "heapmaxavail should be 0"); +} +ENDTEST diff --git a/test/val/lib_common_mktime.c b/test/val/lib_common_mktime.c new file mode 100644 index 000000000..6cabef2be --- /dev/null +++ b/test/val/lib_common_mktime.c @@ -0,0 +1,106 @@ +#include <stdio.h> +#include <string.h> +#include <time.h> + +int fails = 0; + +typedef struct _test_data { + time_t t; + struct tm tm; + char *str; +} test_data; + +/* Test data generated using glibc 2.37 */ +test_data data[] = { + /* First year */ + {0x00000000, {0, 0, 0, 1, 0, 70, 0, 4}, "Thu Jan 1 00:00:00 1970\n"}, + {0x004e7970, {56, 34, 12, 1, 2, 70, 59, 0}, "Sun Mar 1 12:34:56 1970\n"}, + {0x01e1337f, {59, 59, 23, 31, 11, 70, 364, 4}, "Thu Dec 31 23:59:59 1970\n"}, + + /* First leap year */ + {0x03c26700, {0, 0, 0, 1, 0, 72, 0, 6}, "Sat Jan 1 00:00:00 1972\n"}, + {0x03c8fe7f, {59, 59, 23, 5, 0, 72, 4, 3}, "Wed Jan 5 23:59:59 1972\n"}, + {0x041180ff, {59, 59, 23, 29, 1, 72, 59, 2}, "Tue Feb 29 23:59:59 1972\n"}, + {0x04118100, {0, 0, 0, 1, 2, 72, 60, 3}, "Wed Mar 1 00:00:00 1972\n"}, + {0x05a4ebff, {59, 59, 23, 31, 11, 72, 365, 0}, "Sun Dec 31 23:59:59 1972\n"}, + + /* A non-leap year */ + {0x63b0cd00, {0, 0, 0, 1, 0, 123, 0, 0}, "Sun Jan 1 00:00:00 2023\n"}, + {0x63fe957f, {59, 59, 23, 28, 1, 123, 58, 2}, "Tue Feb 28 23:59:59 2023\n"}, + {0x63fe9580, {0, 0, 0, 1, 2, 123, 59, 3}, "Wed Mar 1 00:00:00 2023\n"}, + {0x656d4ec0, {0, 0, 4, 4, 11, 123, 337, 1}, "Mon Dec 4 04:00:00 2023\n"}, + {0x6592007f, {59, 59, 23, 31, 11, 123, 364, 0}, "Sun Dec 31 23:59:59 2023\n"}, + + /* Another leap year */ + {0x65920080, {0, 0, 0, 1, 0, 124, 0, 1}, "Mon Jan 1 00:00:00 2024\n"}, + {0x65e11a7f, {59, 59, 23, 29, 1, 124, 59, 4}, "Thu Feb 29 23:59:59 2024\n"}, + {0x65e11a80, {0, 0, 0, 1, 2, 124, 60, 5}, "Fri Mar 1 00:00:00 2024\n"}, + {0x6774857f, {59, 59, 23, 31, 11, 124, 365, 2}, "Tue Dec 31 23:59:59 2024\n"}, + + /* End of century */ + {0xf48656ff, {59, 59, 23, 31, 11, 199, 364, 4}, "Thu Dec 31 23:59:59 2099\n"}, + + /* A non-leap year for exceptional reasons */ + {0xf4865700, {0, 0, 0, 1, 0, 200, 0, 5}, "Fri Jan 1 00:00:00 2100\n"}, + {0xf4d41f7f, {59, 59, 23, 28, 1, 200, 58, 0}, "Sun Feb 28 23:59:59 2100\n"}, + {0xf4d41f80, {0, 0, 0, 1, 2, 200, 59, 1}, "Mon Mar 1 00:00:00 2100\n"}, + {0xf4fceff0, {0, 0, 23, 31, 2, 200, 89, 3}, "Wed Mar 31 23:00:00 2100\n"}, + {0xf6678a7f, {59, 59, 23, 31, 11, 200, 364, 5}, "Fri Dec 31 23:59:59 2100\n"}, + + /* First post-2100 leap year */ + {0xfc0b2500, {0, 0, 0, 1, 0, 204, 0, 2}, "Tue Jan 1 00:00:00 2104\n"}, + {0xfc5a3eff, {59, 59, 23, 29, 1, 204, 59, 5}, "Fri Feb 29 23:59:59 2104\n"}, + {0xfc5a3f00, {0, 0, 0, 1, 2, 204, 60, 6}, "Sat Mar 1 00:00:00 2104\n"}, + {0xfcaa9c70, {0, 0, 23, 30, 3, 204, 120, 3}, "Wed Apr 30 23:00:00 2104\n"}, + + /* End of epoch */ + {0xfdedaa00, {0, 0, 0, 1, 0, 205, 0, 4}, "Thu Jan 1 00:00:00 2105\n"}, + {0xffffffff, {15, 28, 6, 7, 1, 206, 37, 0}, "Sun Feb 7 06:28:15 2106\n"} +}; + +static int compare_tm(time_t t, struct tm *tm, struct tm *ref) { + if (memcmp(tm, ref, sizeof(tm))) { + printf("0x%lx: unexpected tm from gmtime: " + "expected {%u, %u, %u, %u, %u, %u, %u, %u}, " + "got {%u, %u, %u, %u, %u, %u, %u, %u}\n", + t, + ref->tm_sec, ref->tm_min, ref->tm_hour, ref->tm_mday, ref->tm_mon, ref->tm_year, ref->tm_yday, ref->tm_wday, + tm->tm_sec, tm->tm_min, tm->tm_hour, tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_yday, tm->tm_wday); + return 1; + } + return 0; +} + +int main (void) +{ + int i; + + if (mktime(NULL) != (time_t)-1) { + printf("mktime should return -1 with a NULL parameter\n"); + fails++; + } + + /* Verify conversion both ways */ + for (i = 0; ; i++) { + time_t t = data[i].t; + time_t r; + struct tm *tm = gmtime(&t); + r = mktime(tm); + + if (t != r) { + printf("unexpected timestamp from mktime: expected 0x%lx, got 0x%lx\n", t, r); + fails++; + } + if (compare_tm(t, tm, &data[i].tm)) { + fails++; + } + if (strcmp(data[i].str, ctime(&t))) { + printf("0x%lx: unexpected ctime result: expected %s, got %s", t, data[i].str, ctime(&t)); + fails++; + } + + if (t == 0xFFFFFFFF) + break; + } + return fails; +} diff --git a/test/val/lib_common_pmemalign.c b/test/val/lib_common_pmemalign.c new file mode 100644 index 000000000..0e9e5f52f --- /dev/null +++ b/test/val/lib_common_pmemalign.c @@ -0,0 +1,31 @@ +#include <errno.h> +#include <stdlib.h> +#include "unittest.h" + +TEST +{ + void *buf; + int r; + + r = posix_memalign(&buf, 123, 1024); + ASSERT_IsTrue(r == EINVAL, "posix_memalign did not return EINVAL with wrong alignment"); + ASSERT_IsTrue(buf == NULL, "posix_memalign did not set buf to NULL with wrong alignment"); + + r = posix_memalign(&buf, 0, 1024); + ASSERT_IsTrue(r == EINVAL, "posix_memalign did not return EINVAL with 0 alignment"); + ASSERT_IsTrue(buf == NULL, "posix_memalign did not set buf to NULL with 0 alignment"); + + r = posix_memalign(&buf, 256, 0); + ASSERT_IsTrue(r == EINVAL, "posix_memalign did not return EINVAL with 0 size"); + ASSERT_IsTrue(buf == NULL, "posix_memalign did not set buf to NULL with 0 size"); + + r = posix_memalign(&buf, 256, 32768U); + ASSERT_IsTrue(r == 0, "posix_memalign did not return 0 on correct call"); + ASSERT_IsTrue(buf != NULL, "posix_memalign left buf set to NULL on correct call"); + ASSERT_IsTrue(((unsigned int)buf & 0x00FF) == 0x00, "posix_memalign did not align memory"); + + r = posix_memalign(&buf, 256, 32768U); + ASSERT_IsTrue(r == ENOMEM, "posix_memalign did not return ENOMEM when no memory is available"); + ASSERT_IsTrue(buf == NULL, "posix_memalign did not set buf to NULL when no memory is available"); +} +ENDTEST diff --git a/test/val/lib_common_realloc.c b/test/val/lib_common_realloc.c new file mode 100644 index 000000000..d1e4fa3eb --- /dev/null +++ b/test/val/lib_common_realloc.c @@ -0,0 +1,81 @@ +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include "unittest.h" + +TEST +{ + char *buf, *buf2; + unsigned int i; + + buf = realloc(NULL, 0); + ASSERT_IsTrue (buf == NULL, "realloc (NULL, 0) returned something"); + + for (i = 1; i < 10; i++) { + buf2 = realloc(buf, i); + ASSERT_IsTrue (buf2 != NULL, "small realloc returned nothing"); + if (i > 1) { + ASSERT_IsTrue (buf2 == buf, "buf shouldn't have moved"); + } + buf = buf2; + } + + buf = realloc(NULL, 15); + ASSERT_IsTrue (buf != NULL, "realloc (NULL, 15) returned nothing"); + + buf = realloc(buf, 0); + ASSERT_IsTrue (buf == NULL, "realloc (buf, 0) returned something"); + + buf = realloc(buf, 32); + memset(buf, 'a', 32); + for (i = 0; i < 32; i++) { + ASSERT_IsTrue(buf[i] == 'a', "wrong contents in buf"); + } + + /* Now realloc larger, while there's nothing else in the heap */ + buf = realloc(buf, 64); + memset(buf+32, 'b', 32); + for (i = 0; i < 32; i++) { + ASSERT_IsTrue(buf[i] == 'a', "wrong contents in start of buf"); + } + for (i = 32; i < 64; i++) { + ASSERT_IsTrue(buf[i] == 'b', "wrong contents in end of buf"); + } + + /* Now realloc smaller, while there's nothing else in the heap */ + buf = realloc(buf, 40); + for (i = 0; i < 32; i++) { + ASSERT_IsTrue(buf[i] == 'a', "wrong contents in start of buf"); + } + for (i = 32; i < 40; i++) { + ASSERT_IsTrue(buf[i] == 'b', "wrong contents in end of buf"); + } + + /* Allocate something else, so next realloc has to change block */ + malloc(50); + + /* Now realloc larger, with something else in the heap */ + buf = realloc(buf, 128); + for (i = 0; i < 32; i++) { + ASSERT_IsTrue(buf[i] == 'a', "wrong contents in start of buf"); + } + for (i = 32; i < 40; i++) { + ASSERT_IsTrue(buf[i] == 'b', "wrong contents in end of buf"); + } + + for (i = 129; i < 8192; i++) { + buf = realloc(buf, i); + ASSERT_IsTrue(buf != NULL, "realloc failed"); + } + + malloc(4096); + + buf2 = realloc(buf, 58000UL); + ASSERT_IsTrue (buf2 == NULL, "realloc (buf, 58000) returned something"); + + for (i = 65535UL; i > 65527UL; i--) { + buf2 = realloc(buf, i); + ASSERT_IsTrue (buf2 == NULL, "realloc returned something but shouldn't have"); + } +} +ENDTEST diff --git a/test/val/lib_common_strdup.c b/test/val/lib_common_strdup.c new file mode 100644 index 000000000..748317017 --- /dev/null +++ b/test/val/lib_common_strdup.c @@ -0,0 +1,60 @@ +#include <string.h> +#include "unittest.h" + +#define SHORT_STR "abcdefghijklmnopqrstuvwxyz" + +#define MID_STR_LEN 700 /* Two pages and something */ +#define LONG_STR_LEN 40000UL /* Two long to duplicate */ +TEST +{ + char *dst; + char *src; + int i; + + dst = strdup(""); + ASSERT_IsTrue(dst != NULL, "strdup returned NULL") + ASSERT_IsTrue(!strcmp(dst, ""), "strings differ"); + free(dst); + + dst = strdup(SHORT_STR); + ASSERT_IsTrue(dst != NULL, "strdup returned NULL"); + ASSERT_IsTrue(strlen(dst) == strlen(SHORT_STR), "string lengths differ"); + ASSERT_IsTrue(!strcmp(dst, SHORT_STR), "strings differ"); + free(dst); + + src = malloc(MID_STR_LEN+1); + ASSERT_IsTrue(src != NULL, "Could not allocate source string"); + memset(src, 'a', MID_STR_LEN-1); + src[MID_STR_LEN] = '\0'; + + dst = strdup(src); + ASSERT_IsTrue(dst != NULL, "strdup returned NULL"); + printf("strlens %zu %zu\n", strlen(src), strlen(dst)); + ASSERT_IsTrue(strlen(dst) == strlen(src), "string lengths differ"); + ASSERT_IsTrue(!strcmp(dst, src), "strings differ"); + free(dst); + free(src); + + src = malloc(LONG_STR_LEN+1); + ASSERT_IsTrue(src != NULL, "Could not allocate source string"); + memset(src, 'a', LONG_STR_LEN-1); + src[LONG_STR_LEN] = '\0'; + + dst = strdup(src); + ASSERT_IsTrue(dst == NULL, "strdup did not return NULL"); + free(src); + + for (i = 254; i < 258; i++) { + src = malloc(i+1); + memset(src, 'a', i-1); + src[i] = '\0'; + + dst = strdup(src); + ASSERT_IsTrue(dst != NULL, "strdup returned NULL"); + ASSERT_IsTrue(strlen(dst) == strlen(src), "string lengths differ"); + ASSERT_IsTrue(!strcmp(dst, src), "strings differ"); + free (dst); + free(src); + } +} +ENDTEST diff --git a/test/val/lib_runtime_aslax7.c b/test/val/lib_runtime_aslax7.c new file mode 100644 index 000000000..7e3b4796a --- /dev/null +++ b/test/val/lib_runtime_aslax7.c @@ -0,0 +1,44 @@ +/* + !!DESCRIPTION!! A small test for aslax7. + !!ORIGIN!! + !!LICENCE!! + !!AUTHOR!! +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <limits.h> +#include <errno.h> + +int main (void) +{ + signed int ai = -32768, ti, refi; + signed char ac = -128, tc, refc; + + do { + refi = ai << 4; + refi = refi << 3; + + ti = ai << 7; + + if (ti != refi) { + printf("wrong result on int %d << 7: %04X, expected %04X\n", ai, ti, refi); + return 1; + } + } while (++ai != -32768); + + do { + refc = ac << 4; + refc = refc << 3; + + tc = ac << 7; + + if (tc != refc) { + printf("wrong result on char %d << 7: %04X, expected %04X\n", ac, tc, refc); + return 1; + } + } while (++ac != -128); + + return 0; +} diff --git a/test/val/lib_runtime_asrax7.c b/test/val/lib_runtime_asrax7.c new file mode 100644 index 000000000..3cdf2aab8 --- /dev/null +++ b/test/val/lib_runtime_asrax7.c @@ -0,0 +1,44 @@ +/* + !!DESCRIPTION!! A small test for asrax7. + !!ORIGIN!! + !!LICENCE!! + !!AUTHOR!! +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <limits.h> +#include <errno.h> + +int main (void) +{ + signed int ai = -32768, ti, refi; + signed char ac = -128, tc, refc; + + do { + refi = ai >> 4; + refi = refi >> 3; + + ti = ai >> 7; + + if (ti != refi) { + printf("wrong result on int %d >> 7: %04X, expected %04X\n", ai, ti, refi); + return 1; + } + } while (++ai != -32768); + + do { + refc = ac >> 4; + refc = refc >> 3; + + tc = ac >> 7; + + if (tc != refc) { + printf("wrong result on char %d >> 7: %04X, expected %04X\n", ac, tc, refc); + return 1; + } + } while (++ac != -128); + + return 0; +} diff --git a/test/val/lib_runtime_shlax7.c b/test/val/lib_runtime_shlax7.c new file mode 100644 index 000000000..d2073ca3a --- /dev/null +++ b/test/val/lib_runtime_shlax7.c @@ -0,0 +1,44 @@ +/* + !!DESCRIPTION!! A small test for shlax7. + !!ORIGIN!! + !!LICENCE!! + !!AUTHOR!! +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <limits.h> +#include <errno.h> + +int main (void) +{ + unsigned int ai = 0, ti, refi; + unsigned char ac = 0, tc, refc; + + do { + refi = ai << 4; + refi = refi << 3; + + ti = ai << 7; + + if (ti != refi) { + printf("wrong result on int %u << 7: %04X, expected %04X\n", ai, ti, refi); + return 1; + } + } while (++ai != 0); + + do { + refc = ac << 4; + refc = refc << 3; + + tc = ac << 7; + + if (tc != refc) { + printf("wrong result on char %u << 7: %04X, expected %04X\n", ac, tc, refc); + return 1; + } + } while (++ac != 0); + + return 0; +} diff --git a/test/val/lib_runtime_shrax7.c b/test/val/lib_runtime_shrax7.c new file mode 100644 index 000000000..d3bc0db73 --- /dev/null +++ b/test/val/lib_runtime_shrax7.c @@ -0,0 +1,44 @@ +/* + !!DESCRIPTION!! A small test for shrax7. + !!ORIGIN!! + !!LICENCE!! + !!AUTHOR!! +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <limits.h> +#include <errno.h> + +int main (void) +{ + unsigned int ai = 0, ti, refi; + unsigned char ac = 0, tc, refc; + + do { + refi = ai >> 4; + refi = refi >> 3; + + ti = ai >> 7; + + if (ti != refi) { + printf("wrong result on int %d >> 7: %04X, expected %04X\n", ai, ti, refi); + return 1; + } + } while (++ai != 0); + + do { + refc = ac >> 4; + refc = refc >> 3; + + tc = ac >> 7; + + if (tc != refc) { + printf("wrong result on char %d >> 7: %04X, expected %04X\n", ac, tc, refc); + return 1; + } + } while (++ac != 0); + + return 0; +} diff --git a/test/val/long.c b/test/val/long.c new file mode 100644 index 000000000..076483c9b --- /dev/null +++ b/test/val/long.c @@ -0,0 +1,41 @@ +#include <stdint.h> +#include <stdio.h> + +int res = 0; + +int main(void) +{ + long a, b; + + a = 0x12345678L; + + /* Test assignment */ + b = a; + if (b != a) { + res++; + } + + /* Test increment */ + b++; + if (b != 0x12345679L) { + res++; + } + + /* Test decrement */ + b--; + if (b != 0x12345678L) { + res++; + } + + /* Test pre-decrement with test */ + if (--b != 0x12345677L) { + res++; + } + + a = --b; + if (a != 0x12345676L) { + res++; + } + + return res; +} diff --git a/test/val/lzsa1.c b/test/val/lzsa1.c new file mode 100644 index 000000000..9e627295a --- /dev/null +++ b/test/val/lzsa1.c @@ -0,0 +1,478 @@ +/* + !!DESCRIPTION!! lzsa1 decompression + !!ORIGIN!! cc65 regression tests + !!LICENCE!! BSD 2-clause + !!AUTHOR!! Colin Leroy-Mira +*/ + +#include <zlib.h> +#include <stdio.h> +#include <lzsa.h> + +/* The sample data is the original lz4.h, compressed with: + * lzsa -r -f 1 lz4.h lz4.lzsa1 + * + * We reused lz4.h from the LZ4 test to have a matching adler32 sum. + */ +static const unsigned char compressed[] = { + 0x71, 0x22, 0x2f, 0x2a, 0x0a, 0x20, 0x20, 0x20, 0x4c, 0x5a, 0x34, 0x20, 0x2d, 0x20, 0x46, 0x61, + 0x73, 0x74, 0x20, 0x4c, 0x5a, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x20, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0xd9, 0x71, 0x04, 0x48, 0x65, + 0x61, 0x64, 0x65, 0x72, 0x20, 0x46, 0x69, 0x6c, 0x65, 0xf1, 0x70, 0x16, 0x43, 0x6f, 0x70, 0x79, + 0x72, 0x69, 0x67, 0x68, 0x74, 0x20, 0x28, 0x43, 0x29, 0x20, 0x32, 0x30, 0x31, 0x31, 0x2d, 0x32, + 0x30, 0x31, 0x35, 0x2c, 0x20, 0x59, 0x61, 0x6e, 0x6e, 0xe2, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, + 0x0a, 0xd6, 0x73, 0x2a, 0x42, 0x53, 0x44, 0x20, 0x32, 0x2d, 0x43, 0x6c, 0x61, 0x75, 0x73, 0x65, + 0x20, 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x20, 0x28, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, + 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, + 0x6f, 0x72, 0x67, 0x2f, 0x6c, 0xdd, 0x64, 0x73, 0x2f, 0x62, 0x73, 0x64, 0x2d, 0xf3, 0x52, 0x2e, + 0x70, 0x68, 0x70, 0x29, 0xb2, 0x72, 0x04, 0x52, 0x65, 0x64, 0x69, 0x73, 0x74, 0x72, 0x69, 0x62, + 0x75, 0x74, 0x5d, 0x31, 0x6e, 0x64, 0x20, 0xa8, 0x33, 0x69, 0x6e, 0x20, 0xbd, 0x02, 0xee, 0x70, + 0x08, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2c, 0x20, 0x77, + 0x3d, 0x32, 0x20, 0x6f, 0x72, 0xf8, 0x31, 0x6f, 0x75, 0x74, 0xba, 0x71, 0x01, 0x6d, 0x6f, 0x64, + 0x69, 0x66, 0x69, 0x63, 0x61, 0xbc, 0x70, 0x0f, 0x2c, 0x20, 0x61, 0x72, 0x65, 0x20, 0x70, 0x65, + 0x72, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x64, 0x20, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0xf7, 0x40, + 0x74, 0x68, 0x61, 0x74, 0xfb, 0x30, 0x65, 0x20, 0x66, 0x32, 0xd0, 0x6f, 0x77, 0x69, 0x6e, 0x67, + 0xe2, 0xfe, 0x31, 0x6e, 0x64, 0x69, 0xcb, 0x11, 0x73, 0xcb, 0x02, 0xb6, 0x30, 0x65, 0x74, 0x3a, + 0x67, 0x03, 0xff, 0x1c, 0x2a, 0x61, 0x44, 0x73, 0x20, 0x6f, 0x66, 0x68, 0x00, 0xc5, 0xd0, 0x64, + 0x65, 0x20, 0x6d, 0x75, 0x9a, 0xfe, 0x62, 0x72, 0x65, 0x74, 0x61, 0x69, 0x6e, 0xa6, 0x40, 0x61, + 0x62, 0x6f, 0x76, 0xe5, 0x84, 0xb4, 0xfe, 0x02, 0x65, 0x30, 0x6e, 0x6f, 0x74, 0x0c, 0x10, 0x2c, + 0xe1, 0x40, 0x69, 0x73, 0x20, 0x6c, 0xb3, 0x00, 0xbc, 0x0a, 0x88, 0x2c, 0x6e, 0x64, 0x6b, 0x00, + 0x8e, 0x7f, 0x01, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x65, 0x72, 0x2e, 0x77, 0x08, 0xa9, 0x69, 0x6e, + 0xea, 0xfe, 0x05, 0x77, 0x00, 0x14, 0x46, 0x64, 0x75, 0x63, 0x65, 0x74, 0x01, 0x2d, 0x07, 0x71, + 0x0f, 0x74, 0x2b, 0x01, 0xb6, 0x04, 0x1a, 0xf5, 0x02, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, + 0x74, 0x61, 0x4d, 0xfe, 0x50, 0x2f, 0x6f, 0x72, 0x20, 0x6f, 0xe6, 0xf6, 0x04, 0x72, 0x20, 0x6d, + 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x73, 0x89, 0xfe, 0x83, 0x51, 0xfe, 0x22, 0x74, 0x68, + 0x75, 0x09, 0x3c, 0x83, 0xb2, 0xfd, 0x71, 0x3c, 0x54, 0x48, 0x49, 0x53, 0x20, 0x53, 0x4f, 0x46, + 0x54, 0x57, 0x41, 0x52, 0x45, 0x20, 0x49, 0x53, 0x20, 0x50, 0x52, 0x4f, 0x56, 0x49, 0x44, 0x45, + 0x44, 0x20, 0x42, 0x59, 0x20, 0x54, 0x48, 0x45, 0x20, 0x43, 0x4f, 0x50, 0x59, 0x52, 0x49, 0x47, + 0x48, 0x54, 0x20, 0x48, 0x4f, 0x4c, 0x44, 0x45, 0x52, 0x53, 0x20, 0x41, 0x4e, 0x44, 0x20, 0x43, + 0x4f, 0x4e, 0x54, 0x52, 0x49, 0x42, 0x55, 0x54, 0x4f, 0x52, 0x53, 0xb9, 0x30, 0x22, 0x41, 0x53, + 0xc3, 0x12, 0x22, 0xe4, 0x70, 0x0d, 0x41, 0x4e, 0x59, 0x20, 0x45, 0x58, 0x50, 0x52, 0x45, 0x53, + 0x53, 0x20, 0x4f, 0x52, 0x20, 0x49, 0x4d, 0x50, 0x4c, 0x49, 0xb0, 0x00, 0x9f, 0x70, 0x0d, 0x52, + 0x41, 0x4e, 0x54, 0x49, 0x45, 0x53, 0x2c, 0x20, 0x49, 0x4e, 0x43, 0x4c, 0x55, 0x44, 0x49, 0x4e, + 0x47, 0x2c, 0x20, 0xbc, 0xc2, 0x20, 0x4e, 0x4f, 0x54, 0xc6, 0xfc, 0x40, 0x49, 0x4d, 0x49, 0x54, + 0xd6, 0x32, 0x54, 0x4f, 0x2c, 0x85, 0x0e, 0xc6, 0x00, 0xb0, 0x72, 0x0a, 0x46, 0x20, 0x4d, 0x45, + 0x52, 0x43, 0x48, 0x41, 0x4e, 0x54, 0x41, 0x42, 0x49, 0x4c, 0x49, 0x54, 0x59, 0x8d, 0x41, 0x46, + 0x49, 0x54, 0x4e, 0x91, 0x31, 0x46, 0x4f, 0x52, 0xb7, 0x71, 0x0e, 0x41, 0x20, 0x50, 0x41, 0x52, + 0x54, 0x49, 0x43, 0x55, 0x4c, 0x41, 0x52, 0x20, 0x50, 0x55, 0x52, 0x50, 0x4f, 0x53, 0x45, 0x20, + 0x20, 0x70, 0x04, 0x44, 0x49, 0x53, 0x43, 0x4c, 0x41, 0x49, 0x4d, 0x45, 0x44, 0x2e, 0x7c, 0x00, + 0x88, 0x7a, 0x05, 0x20, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x20, 0x53, 0x48, 0x41, 0x4c, 0x4c, 0x11, + 0x02, 0x6f, 0x50, 0x4f, 0x57, 0x4e, 0x45, 0x52, 0x3c, 0x0a, 0x11, 0x30, 0x20, 0x42, 0x45, 0x56, + 0x41, 0x41, 0x42, 0x4c, 0x45, 0x91, 0x02, 0x12, 0x61, 0x44, 0x49, 0x52, 0x45, 0x43, 0x54, 0x29, + 0x07, 0xf6, 0x40, 0x43, 0x49, 0x44, 0x45, 0x5b, 0x21, 0x4c, 0x2c, 0xb7, 0x50, 0x53, 0x50, 0x45, + 0x43, 0x49, 0xf4, 0x60, 0x20, 0x45, 0x58, 0x45, 0x4d, 0x50, 0x6a, 0x24, 0x59, 0x2c, 0xa9, 0x40, + 0x53, 0x45, 0x51, 0x55, 0xd8, 0x00, 0xe3, 0x60, 0x20, 0x44, 0x41, 0x4d, 0x41, 0x47, 0x1c, 0x9f, + 0x28, 0xe2, 0xfe, 0x0f, 0x81, 0x5b, 0xfe, 0x51, 0x43, 0x55, 0x52, 0x45, 0x4d, 0x44, 0xf2, 0x0b, + 0x4f, 0x46, 0x20, 0x53, 0x55, 0x42, 0x53, 0x54, 0x49, 0x54, 0x55, 0x54, 0x45, 0x20, 0x47, 0x4f, + 0x4f, 0x44, 0x89, 0xfe, 0xf2, 0x06, 0x53, 0x45, 0x52, 0x56, 0x49, 0x43, 0x45, 0x53, 0x3b, 0x20, + 0x4c, 0x4f, 0x53, 0xc7, 0xfe, 0x32, 0x55, 0x53, 0x45, 0x73, 0x41, 0x44, 0x41, 0x54, 0x41, 0x81, + 0x01, 0xba, 0x51, 0x46, 0x49, 0x54, 0x53, 0x3b, 0xf4, 0xc2, 0x42, 0x55, 0x53, 0x49, 0xbd, 0xfe, + 0x70, 0x0d, 0x49, 0x4e, 0x54, 0x45, 0x52, 0x52, 0x55, 0x50, 0x54, 0x49, 0x4f, 0x4e, 0x29, 0x20, + 0x48, 0x4f, 0x57, 0x45, 0x56, 0x45, 0x56, 0x10, 0x41, 0xc2, 0x92, 0x44, 0x94, 0xfe, 0x21, 0x4f, + 0x4e, 0x0c, 0x83, 0xc4, 0xfd, 0x41, 0x45, 0x4f, 0x52, 0x59, 0xa5, 0xa4, 0x4c, 0x49, 0x72, 0xfe, + 0x50, 0x2c, 0x20, 0x57, 0x48, 0x45, 0xe8, 0x10, 0x52, 0xb8, 0x83, 0xca, 0xfe, 0x91, 0x41, 0xf1, + 0xfe, 0x69, 0x53, 0x54, 0x52, 0x49, 0x43, 0x54, 0xd9, 0x20, 0x4f, 0x52, 0x37, 0x11, 0x52, 0x27, + 0x08, 0x10, 0x71, 0x04, 0x20, 0x4e, 0x45, 0x47, 0x4c, 0x49, 0x47, 0x45, 0x4e, 0x43, 0x45, 0xdf, + 0x11, 0x4f, 0xb7, 0xd0, 0x57, 0x49, 0x53, 0x45, 0x29, 0x43, 0xfe, 0x21, 0x49, 0x53, 0xdf, 0x12, + 0x49, 0x83, 0x30, 0x20, 0x57, 0x41, 0x89, 0x11, 0x55, 0x04, 0x01, 0x7b, 0x01, 0x26, 0x82, 0x51, + 0xfe, 0x9b, 0x46, 0x31, 0xfd, 0x92, 0x2c, 0x22, 0xfe, 0x70, 0x01, 0x20, 0x49, 0x46, 0x20, 0x41, + 0x44, 0x56, 0x49, 0x40, 0x05, 0xd0, 0x53, 0x50, 0x4f, 0x53, 0x53, 0x49, 0x76, 0x83, 0xc1, 0xfe, + 0xa4, 0x43, 0x48, 0x82, 0xfe, 0x83, 0xeb, 0xfc, 0xf1, 0x00, 0x59, 0x6f, 0x75, 0x20, 0x63, 0x61, + 0x6e, 0x65, 0xfc, 0xc3, 0x74, 0x61, 0x63, 0x74, 0x31, 0xfc, 0xe0, 0x75, 0x74, 0x68, 0x6f, 0x72, + 0x20, 0x3d, 0xfb, 0x11, 0x3a, 0xdd, 0x91, 0x2d, 0x18, 0xfa, 0x84, 0x74, 0xfb, 0x81, 0x07, 0xfc, + 0xd0, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0xf2, 0xfb, 0xa1, 0x3a, 0x20, 0x76, 0xfa, 0x77, 0x14, 0x73, + 0x3a, 0x2f, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x43, 0x79, + 0x61, 0x6e, 0x34, 0x39, 0x37, 0x33, 0x2f, 0x6c, 0x7a, 0x34, 0xc3, 0xe1, 0x70, 0x75, 0x62, 0x6c, + 0x69, 0x63, 0xc0, 0xfb, 0x29, 0x75, 0x6d, 0xc8, 0x72, 0x05, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x2e, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0xc1, 0x02, 0xde, 0x32, 0x2f, 0x23, 0x21, 0xf8, 0x01, 0xbc, + 0x71, 0x20, 0x63, 0x0a, 0x2a, 0x2f, 0x0a, 0x23, 0x70, 0x72, 0x61, 0x67, 0x6d, 0x61, 0x20, 0x6f, + 0x6e, 0x63, 0x65, 0x0a, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, + 0x20, 0x28, 0x5f, 0x5f, 0x63, 0x70, 0x6c, 0x75, 0x73, 0xfc, 0xf1, 0x10, 0x29, 0x0a, 0x65, 0x78, + 0x74, 0x65, 0x72, 0x6e, 0x20, 0x22, 0x43, 0x22, 0x20, 0x7b, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, + 0x66, 0x0a, 0x0a, 0x55, 0xf9, 0x20, 0x2a, 0x20, 0xb5, 0xa5, 0x2e, 0x68, 0xd6, 0xfb, 0xfa, 0x00, + 0x73, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x4e, 0xf9, 0xc2, 0x66, 0x75, 0x6e, 0x63, 0x65, 0xfb, + 0x92, 0x2c, 0x64, 0xfb, 0x50, 0x67, 0x69, 0x76, 0x65, 0x73, 0xeb, 0xf2, 0x02, 0x6c, 0x6c, 0x20, + 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0xde, 0xfe, 0x61, 0x72, 0x6f, 0x6c, 0x20, 0x74, 0x6f, 0xb9, + 0xc3, 0x67, 0x72, 0x61, 0x6d, 0xc9, 0xfa, 0xe0, 0x2a, 0x20, 0x49, 0x66, 0x20, 0x79, 0xb9, 0xfe, + 0x41, 0x6e, 0x65, 0x65, 0x64, 0xe2, 0xf1, 0x00, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0xa6, + 0xf9, 0x00, 0x71, 0x30, 0x2d, 0x6f, 0x70, 0xf2, 0x36, 0x62, 0x6c, 0x65, 0x92, 0x00, 0xda, 0x60, + 0x64, 0x61, 0x74, 0x61, 0x20, 0x28, 0xf3, 0x20, 0x70, 0x65, 0x8b, 0xa2, 0x6e, 0x67, 0xdb, 0xfe, + 0x10, 0x66, 0xad, 0x21, 0x65, 0x20, 0xed, 0x86, 0x9f, 0xf9, 0x20, 0x29, 0x2c, 0xa0, 0x01, 0x70, + 0x82, 0x5c, 0xfe, 0xa3, 0x6c, 0x65, 0x60, 0xfe, 0xc1, 0x6c, 0x69, 0x62, 0x72, 0x71, 0xfa, 0x10, + 0x68, 0xe7, 0x10, 0x6c, 0x99, 0xf1, 0x0e, 0x74, 0x73, 0x20, 0x6f, 0x77, 0x6e, 0x20, 0x6d, 0x65, + 0x6d, 0x6f, 0x72, 0x79, 0x2c, 0x20, 0x70, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x24, 0xf9, 0x01, 0x0a, + 0x02, 0xaa, 0x20, 0x2e, 0x68, 0x73, 0xe1, 0x73, 0x74, 0x65, 0x61, 0x64, 0x2e, 0xb0, 0xfe, 0x3f, + 0x0a, 0x2f, 0x2a, 0xff, 0x13, 0xf1, 0x00, 0x0a, 0x2a, 0x20, 0x20, 0x56, 0x65, 0x72, 0xe0, 0xfe, + 0x1f, 0x0a, 0xce, 0x14, 0xb3, 0x2f, 0x0a, 0x23, 0x65, 0xfe, 0x01, 0x2e, 0xd0, 0x5f, 0x56, 0x45, + 0x52, 0x53, 0xa4, 0xfc, 0xe1, 0x5f, 0x4d, 0x41, 0x4a, 0x4f, 0x52, 0xa3, 0xf9, 0x11, 0x31, 0xfb, + 0xa1, 0x2f, 0x2a, 0xf7, 0xfd, 0x60, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x04, 0x83, 0xd9, 0xfe, + 0xa1, 0x66, 0x61, 0x17, 0xf9, 0x00, 0x2e, 0x5f, 0x67, 0x65, 0x73, 0x20, 0x20, 0xb8, 0x06, 0x23, + 0x49, 0x4e, 0xb8, 0x18, 0x37, 0xb8, 0x75, 0x02, 0x6e, 0x65, 0x77, 0x20, 0x28, 0x6e, 0x6f, 0x6e, + 0x2d, 0xaf, 0x19, 0x29, 0xae, 0xe0, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x8b, 0xf9, 0x2f, 0x65, + 0x73, 0xaa, 0x06, 0x7b, 0x00, 0x52, 0x45, 0x4c, 0x45, 0x41, 0x53, 0x45, 0x62, 0x20, 0x74, 0x77, + 0xb3, 0xa0, 0x73, 0x2c, 0x03, 0xfe, 0x60, 0x67, 0x2d, 0x66, 0x69, 0x78, 0x65, 0xf5, 0x00, 0xea, + 0xf1, 0x00, 0x64, 0x65, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x6e, 0xf9, 0x0f, 0xb2, 0x06, 0xc0, 0x4e, + 0x55, 0x4d, 0x42, 0xec, 0xfb, 0x1f, 0x28, 0x00, 0x00, 0x41, 0x2a, 0x31, 0x30, 0x30, 0xfc, 0x2f, + 0x20, 0x2b, 0x2b, 0x01, 0x06, 0xe7, 0x0e, 0x68, 0x20, 0x29, 0x0a, 0x31, 0x02, 0xe7, 0x93, 0x76, + 0x75, 0xfe, 0xff, 0x07, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x20, 0x28, 0x76, 0x6f, 0x69, 0x64, + 0x29, 0x3b, 0x33, 0xfe, 0x1b, 0xb1, 0x54, 0x75, 0x6e, 0x8e, 0xfe, 0xa1, 0x70, 0x61, 0xeb, 0xfd, + 0xbf, 0x74, 0x65, 0x72, 0x2a, 0xfe, 0x16, 0x83, 0xbb, 0xfc, 0x02, 0x7b, 0xb0, 0x4d, 0x45, 0x4d, + 0xf6, 0xfa, 0xb0, 0x5f, 0x55, 0x53, 0xbd, 0xfb, 0x21, 0x20, 0x3a, 0xea, 0x92, 0x4d, 0x88, 0xfd, + 0xe2, 0x20, 0x75, 0x73, 0x61, 0x67, 0x65, 0xdd, 0xf7, 0x71, 0x25, 0x75, 0x6c, 0x61, 0x20, 0x3a, + 0x20, 0x4e, 0x2d, 0x3e, 0x32, 0x5e, 0x4e, 0x20, 0x42, 0x79, 0x74, 0x65, 0x73, 0x20, 0x28, 0x65, + 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x20, 0x3a, 0x20, 0x31, 0x30, 0x20, 0x2d, 0x3e, 0x20, + 0x31, 0x4b, 0x42, 0x3b, 0x20, 0x31, 0x32, 0xf5, 0x40, 0x34, 0x4b, 0x42, 0x20, 0xf4, 0x11, 0x36, + 0xf4, 0x21, 0x36, 0x34, 0xe8, 0x13, 0x32, 0xdd, 0x10, 0x4d, 0xf5, 0xd2, 0x65, 0x74, 0x63, 0x2e, + 0x29, 0x9a, 0xfc, 0x61, 0x6e, 0x63, 0x72, 0x65, 0x61, 0x73, 0x40, 0x19, 0x6d, 0x8e, 0xa1, 0x69, + 0x6d, 0x2a, 0xfc, 0xaa, 0x65, 0x73, 0x32, 0xfc, 0x91, 0x72, 0xc1, 0xfc, 0x93, 0x0a, 0x33, 0xf7, + 0x4b, 0x75, 0x63, 0x65, 0x64, 0xcd, 0x34, 0x63, 0x61, 0x6e, 0xc9, 0x82, 0x90, 0xfc, 0xe0, 0x65, + 0x64, 0x2c, 0x20, 0x64, 0x75, 0x35, 0xf7, 0x10, 0x6f, 0xe6, 0xd0, 0x63, 0x68, 0x65, 0x20, 0x65, + 0x0f, 0xfc, 0x21, 0x63, 0x74, 0xc1, 0x70, 0x05, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x20, + 0x76, 0x61, 0x6c, 0x75, 0x95, 0x51, 0x73, 0x20, 0x31, 0x34, 0x2c, 0x1c, 0x00, 0x52, 0xa0, 0x4b, + 0x42, 0xe0, 0xf5, 0xe0, 0x68, 0x69, 0x63, 0x68, 0x20, 0x6e, 0x16, 0xf7, 0xe1, 0x6c, 0x79, 0x20, + 0x66, 0x69, 0x74, 0xd3, 0xf6, 0x20, 0x74, 0x6f, 0x55, 0x73, 0x03, 0x74, 0x65, 0x6c, 0x20, 0x78, + 0x38, 0x36, 0x20, 0x4c, 0x31, 0xad, 0x99, 0x0a, 0xd1, 0xfd, 0x8e, 0xb9, 0xfe, 0xbf, 0x31, 0x34, + 0x0a, 0x39, 0xfe, 0x1b, 0xa1, 0x53, 0x69, 0xb4, 0xfe, 0xa5, 0x20, 0x46, 0x4d, 0xfb, 0x8f, 0x39, + 0xfe, 0x17, 0x86, 0xb5, 0xfd, 0x85, 0xd4, 0xfe, 0x23, 0x5f, 0x64, 0x1f, 0x20, 0x28, 0x63, 0xb9, + 0x91, 0x74, 0x7f, 0xfc, 0xa4, 0x72, 0x2a, 0x15, 0xfa, 0x14, 0x2c, 0xf2, 0x51, 0x64, 0x65, 0x73, + 0x74, 0x2c, 0x28, 0x04, 0xe8, 0x43, 0x53, 0x69, 0x7a, 0x65, 0xf0, 0x40, 0x6d, 0x61, 0x78, 0x44, + 0xe3, 0x01, 0xef, 0x26, 0x29, 0x3b, 0xa5, 0x26, 0x64, 0x65, 0xa3, 0x5f, 0x73, 0x61, 0x66, 0x65, + 0x20, 0xa5, 0x12, 0x88, 0xe4, 0xfa, 0x0c, 0xa1, 0x0b, 0xe7, 0x83, 0x11, 0xfd, 0x1f, 0x0a, 0x3e, + 0x03, 0x93, 0x29, 0x52, 0xf9, 0x25, 0x20, 0x43, 0xce, 0x37, 0x73, 0x20, 0x27, 0x4e, 0xb2, 0x27, + 0x20, 0x62, 0x77, 0xfd, 0xc4, 0x66, 0x72, 0x6f, 0x6d, 0x2e, 0xfa, 0x05, 0xe1, 0x11, 0x27, 0xc9, + 0x83, 0x47, 0xfe, 0xb0, 0x61, 0x6c, 0x72, 0xdc, 0xfa, 0x10, 0x79, 0xf8, 0xc1, 0x6c, 0x6f, 0x63, + 0x61, 0x2d, 0xf4, 0x11, 0x27, 0x5c, 0x14, 0x27, 0xce, 0x82, 0x6c, 0xf4, 0x00, 0xb3, 0x28, 0x20, + 0x27, 0x05, 0x2a, 0x27, 0x2e, 0x89, 0xb1, 0x69, 0x6f, 0x6e, 0xd8, 0xfd, 0xa0, 0x67, 0x75, 0x9c, + 0xfc, 0xa4, 0x6e, 0x74, 0xed, 0xf9, 0x41, 0x73, 0x75, 0x63, 0x63, 0xf5, 0x2b, 0x69, 0x66, 0xc5, + 0xba, 0x20, 0x3e, 0x3d, 0x71, 0xfe, 0x67, 0x42, 0x6f, 0x75, 0x6e, 0x64, 0x28, 0x4a, 0x13, 0x29, + 0xa4, 0x20, 0x49, 0x74, 0x6e, 0x60, 0x73, 0x6f, 0x20, 0x72, 0x75, 0x6e, 0x3e, 0xa0, 0x61, 0x73, + 0x46, 0xfc, 0x20, 0x2c, 0x20, 0xf0, 0xb0, 0x69, 0x74, 0x27, 0xb6, 0xf4, 0xa1, 0x20, 0x72, 0xda, + 0xfe, 0xb1, 0x6d, 0x65, 0x6e, 0xfd, 0xf4, 0xb1, 0x73, 0x65, 0x74, 0xaf, 0xf9, 0x04, 0xc8, 0x91, + 0x66, 0x9c, 0xf4, 0x86, 0x25, 0xf9, 0x81, 0x08, 0xfd, 0xa7, 0x6e, 0x6f, 0x93, 0xfe, 0x86, 0xff, + 0xfe, 0x04, 0x03, 0xc1, 0x20, 0x6d, 0x6f, 0x72, 0xa2, 0xf9, 0x2a, 0x6d, 0x69, 0x06, 0xc2, 0x64, + 0x67, 0x65, 0x74, 0xb2, 0xf6, 0x8a, 0x9e, 0xfc, 0xc0, 0x73, 0x74, 0x6f, 0x70, 0xad, 0xfa, 0x10, + 0x69, 0x87, 0xc0, 0x64, 0x69, 0x61, 0x74, 0xff, 0xfc, 0x92, 0x2a, 0xc6, 0xf8, 0x0b, 0x8e, 0x00, + 0xd2, 0xb1, 0x75, 0x6c, 0x74, 0xef, 0xfe, 0x43, 0x7a, 0x65, 0x72, 0x6f, 0x6a, 0x11, 0x41, 0x4c, + 0x81, 0xeb, 0xfd, 0xd0, 0x65, 0x71, 0x75, 0x65, 0x6e, 0xf2, 0xfd, 0x04, 0x95, 0x82, 0x9e, 0xf8, + 0x22, 0x65, 0x6e, 0xd2, 0xa2, 0x6e, 0x6f, 0x8c, 0xfc, 0x23, 0x69, 0x64, 0xcd, 0x47, 0x54, 0x68, + 0x69, 0x73, 0xab, 0xc0, 0x6e, 0x65, 0x76, 0x65, 0x6c, 0xf2, 0xa1, 0x72, 0x69, 0x2f, 0xfe, 0xc0, + 0x6f, 0x75, 0x74, 0x73, 0x2d, 0xf8, 0x8b, 0x57, 0xfe, 0x10, 0x2c, 0xbf, 0xa1, 0x72, 0x20, 0x34, + 0xfe, 0x05, 0xe0, 0x05, 0x14, 0x03, 0xde, 0x88, 0x10, 0xf3, 0x88, 0x2a, 0xfd, 0x90, 0x20, 0x63, + 0xfb, 0xb0, 0x4d, 0x61, 0x78, 0x52, 0xfe, 0xc0, 0x70, 0x70, 0x6f, 0x72, 0xfc, 0xfe, 0x86, 0x09, + 0xfc, 0x83, 0x4d, 0xfc, 0x75, 0x07, 0x41, 0x58, 0x5f, 0x49, 0x4e, 0x50, 0x55, 0x54, 0x5f, 0x56, + 0x41, 0x4c, 0x55, 0x45, 0xbf, 0x89, 0xf9, 0xfc, 0xa3, 0x20, 0x3a, 0xcd, 0xf7, 0xa1, 0x6f, 0x72, + 0x9c, 0xfa, 0xc3, 0x74, 0x69, 0x61, 0x6c, 0xd0, 0xfd, 0x24, 0x6f, 0x66, 0x89, 0x05, 0x59, 0x92, + 0x28, 0xc6, 0xfb, 0x83, 0xb4, 0xf2, 0xaf, 0x62, 0x65, 0x88, 0xfd, 0x00, 0x16, 0x29, 0xa2, 0xc0, + 0x72, 0x65, 0x74, 0x75, 0x2c, 0xf7, 0x92, 0x3a, 0xa8, 0xfe, 0x93, 0x6e, 0x05, 0xfa, 0xa4, 0x6f, + 0x66, 0x32, 0xfd, 0xa1, 0x77, 0x72, 0x87, 0xf1, 0x92, 0x6e, 0x40, 0xfe, 0x0d, 0x9e, 0xb0, 0x6e, + 0x65, 0x63, 0x55, 0xfe, 0xb0, 0x61, 0x72, 0x69, 0x65, 0xfb, 0x21, 0x3c, 0x3d, 0x5a, 0x52, 0x4f, + 0x75, 0x74, 0x70, 0x75, 0x58, 0x00, 0xa1, 0x09, 0xff, 0x01, 0x50, 0xcb, 0x30, 0x20, 0x69, 0x66, + 0xef, 0xf6, 0xef, 0x61, 0x69, 0x6c, 0x73, 0x0a, 0x0a, 0x2d, 0xfc, 0x01, 0x85, 0x95, 0xfc, 0x88, + 0x4a, 0xfc, 0x04, 0x03, 0x22, 0x69, 0x73, 0x59, 0x00, 0xeb, 0xc1, 0x63, 0x69, 0x73, 0x65, 0xf4, + 0xfe, 0x86, 0xff, 0xfe, 0x01, 0xe7, 0x08, 0xcf, 0x83, 0x7e, 0xf6, 0xa6, 0x2e, 0x0a, 0xbf, 0xfe, + 0x0f, 0xb4, 0x05, 0x06, 0xc1, 0x01, 0x2a, 0xa2, 0x69, 0x6e, 0xf8, 0xf1, 0x86, 0x1b, 0xfe, 0x8f, + 0xbb, 0xfe, 0x0d, 0xaf, 0x2e, 0x0a, 0xbf, 0xfe, 0x0f, 0x18, 0x64, 0x88, 0x82, 0xba, 0xfe, 0x0f, + 0x98, 0x01, 0x8e, 0xb5, 0xfe, 0x0e, 0x50, 0x8e, 0xaf, 0xfe, 0x1f, 0x49, 0x50, 0x02, 0x85, 0x28, + 0xfd, 0xc0, 0x6c, 0x61, 0x72, 0x67, 0x9c, 0xf9, 0x62, 0x6e, 0x6f, 0x75, 0x67, 0x68, 0x2c, 0x7e, + 0x91, 0x64, 0x2b, 0xf9, 0xc2, 0x77, 0x69, 0x6c, 0x6c, 0xaa, 0xfc, 0x82, 0xba, 0xfc, 0x92, 0x6f, + 0x54, 0xfe, 0x00, 0xf5, 0xe3, 0x20, 0x65, 0x72, 0x72, 0x6f, 0x72, 0xfc, 0xef, 0x5d, 0x28, 0x3c, + 0x30, 0x29, 0x2e, 0x96, 0x01, 0x28, 0x85, 0x62, 0xf4, 0x61, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, + 0x97, 0xe1, 0x64, 0x65, 0x74, 0x65, 0x63, 0x74, 0x07, 0xf9, 0xa1, 0x61, 0x6c, 0x6c, 0xf8, 0xba, + 0x65, 0x64, 0x2c, 0x66, 0xfc, 0x07, 0x91, 0x06, 0x7e, 0x01, 0x88, 0x85, 0xd2, 0xfe, 0x50, 0x61, + 0x20, 0x6e, 0x65, 0x67, 0x43, 0xa4, 0x76, 0x65, 0x3d, 0xfc, 0x09, 0x89, 0x8e, 0x6f, 0xfc, 0x92, + 0x69, 0x7c, 0xf0, 0x04, 0x8f, 0xb1, 0x61, 0x67, 0x61, 0x94, 0xf5, 0x86, 0xcd, 0xfa, 0xc0, 0x76, + 0x65, 0x72, 0x66, 0x1d, 0xf0, 0xe0, 0x20, 0x65, 0x78, 0x70, 0x6c, 0x6f, 0xdd, 0xf8, 0x81, 0x23, + 0xfa, 0x31, 0x63, 0x6c, 0x75, 0x8e, 0x01, 0x63, 0xe3, 0x69, 0x63, 0x69, 0x6f, 0x75, 0x73, 0xee, + 0xf4, 0x7d, 0x00, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x1d, 0x9f, 0x74, 0x0e, 0xfc, 0x03, + 0x84, 0xdd, 0xfe, 0x8e, 0x0e, 0xfc, 0x07, 0xdf, 0x27, 0x69, 0x6e, 0xe0, 0x81, 0x08, 0xf5, 0x8f, + 0x9b, 0xf8, 0x1c, 0xd0, 0x41, 0x64, 0x76, 0x61, 0x6e, 0xc5, 0xf7, 0x87, 0x99, 0xf8, 0x8f, 0xfc, + 0xf4, 0x1e, 0x8c, 0xca, 0xfb, 0x45, 0x53, 0x49, 0x5a, 0x45, 0x2c, 0x52, 0x30, 0x78, 0x37, 0x45, + 0x30, 0xff, 0x83, 0x8d, 0xf5, 0xf4, 0x06, 0x32, 0x20, 0x31, 0x31, 0x33, 0x20, 0x39, 0x32, 0x39, + 0x20, 0x32, 0x31, 0x36, 0x64, 0xfd, 0x0c, 0xb7, 0xb2, 0x43, 0x4f, 0x4d, 0x97, 0xef, 0xf1, 0x01, + 0x42, 0x4f, 0x55, 0x4e, 0x44, 0x28, 0x69, 0x73, 0x8a, 0xfd, 0xc0, 0x20, 0x20, 0x28, 0x28, 0xec, + 0xf9, 0x65, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x29, 0xec, 0x27, 0x3e, 0x20, 0xec, 0x0f, 0x7e, 0x01, + 0x65, 0x3f, 0x20, 0x30, 0x20, 0x3a, 0x20, 0xd3, 0x34, 0x2b, 0x20, 0x28, 0xf5, 0x41, 0x2f, 0x32, + 0x35, 0x35, 0xf0, 0xb2, 0x31, 0x36, 0x29, 0x9b, 0xf8, 0x8e, 0x68, 0xf9, 0x86, 0x09, 0xfc, 0x95, + 0x50, 0xba, 0xf2, 0xb1, 0x74, 0x68, 0x65, 0xf3, 0xfc, 0xc2, 0x69, 0x6d, 0x75, 0x6d, 0x58, 0xfc, + 0x82, 0x08, 0xed, 0x01, 0x53, 0x8a, 0xb7, 0xfb, 0xb5, 0x6d, 0x61, 0x79, 0x54, 0xfe, 0x91, 0x69, + 0xa6, 0xfd, 0xc1, 0x22, 0x77, 0x6f, 0x72, 0xef, 0xf7, 0xe0, 0x61, 0x73, 0x65, 0x22, 0x20, 0x73, + 0x5b, 0xec, 0xe2, 0x61, 0x72, 0x69, 0x6f, 0x20, 0x28, 0x52, 0xfe, 0x83, 0xf8, 0xfd, 0x89, 0x5b, + 0xf9, 0xef, 0x69, 0x62, 0x6c, 0x65, 0x29, 0x0a, 0x8c, 0xfd, 0x05, 0xa3, 0x69, 0x6d, 0x6b, 0xfc, + 0xe1, 0x75, 0x73, 0x65, 0x66, 0x75, 0x6c, 0x67, 0xf6, 0x83, 0x17, 0xf6, 0x85, 0xfb, 0xfb, 0x02, + 0xd6, 0xe1, 0x70, 0x75, 0x72, 0x70, 0x6f, 0x73, 0x7b, 0xf5, 0x8f, 0x6c, 0xfc, 0x01, 0x97, 0x73, + 0x9a, 0xf8, 0xdf, 0x4d, 0x61, 0x63, 0x72, 0x6f, 0x95, 0xfe, 0x01, 0x10, 0x29, 0x94, 0x82, 0x80, + 0xf8, 0x86, 0x97, 0xed, 0x01, 0x97, 0x02, 0x5f, 0x22, 0x69, 0x6c, 0xb3, 0xc0, 0x2d, 0x74, 0x69, + 0x6d, 0x2e, 0xfc, 0x81, 0xd2, 0xf9, 0x03, 0xa3, 0x6f, 0x28, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x74, + 0x00, 0x02, 0xc7, 0x83, 0xf5, 0xf4, 0x05, 0x8a, 0xb4, 0x4e, 0x6f, 0x74, 0xc8, 0xfe, 0x8f, 0x2a, + 0xf7, 0x05, 0x04, 0xed, 0x85, 0x07, 0xf8, 0xd2, 0x20, 0x77, 0x68, 0x65, 0x6e, 0xa2, 0xfb, 0x09, + 0x3d, 0x00, 0x5d, 0x8f, 0xb4, 0xf7, 0x05, 0xaa, 0x72, 0x63, 0x60, 0xfb, 0x83, 0xeb, 0xfc, 0x85, + 0x12, 0xf9, 0x9f, 0x6d, 0x12, 0xf9, 0x05, 0x8e, 0xca, 0xfd, 0x8e, 0x71, 0xf9, 0x85, 0x08, 0xfe, + 0x84, 0x26, 0xfe, 0x02, 0x7b, 0x8f, 0x21, 0xfe, 0x09, 0x8f, 0x90, 0xf9, 0x01, 0x43, 0x2c, 0x20, + 0x69, 0x66, 0x72, 0x06, 0x3e, 0xb4, 0x74, 0x6f, 0x6f, 0xe2, 0xfa, 0x3f, 0x28, 0x20, 0x3e, 0x7a, + 0x01, 0xc3, 0x29, 0x0a, 0x2a, 0x2f, 0x3d, 0xf5, 0x0e, 0x18, 0x00, 0xea, 0x03, 0x25, 0x8f, 0xd9, + 0xf5, 0x06, 0xb7, 0x66, 0x61, 0x73, 0xdc, 0xf5, 0x91, 0x53, 0x96, 0xf0, 0xaf, 0x61, 0x73, 0x8d, + 0xfe, 0x05, 0x81, 0x0a, 0xf2, 0x92, 0x74, 0x4a, 0xfe, 0xa2, 0x77, 0x73, 0x42, 0xf6, 0xc2, 0x65, + 0x6c, 0x65, 0x63, 0x74, 0xfa, 0x40, 0x22, 0x61, 0x63, 0x63, 0xf3, 0x92, 0x72, 0x33, 0xfe, 0xd0, + 0x22, 0x20, 0x66, 0x61, 0x63, 0xf6, 0xee, 0x85, 0x5d, 0xf7, 0x13, 0x65, 0x44, 0x92, 0x72, 0xbc, + 0xee, 0x09, 0xd7, 0x83, 0xa7, 0xfe, 0x83, 0x80, 0xfa, 0x01, 0x79, 0x03, 0xdd, 0x87, 0xdc, 0xe8, + 0x05, 0x90, 0xa3, 0x73, 0x6f, 0x21, 0xf0, 0x00, 0x73, 0x03, 0xdd, 0x89, 0xb9, 0xfc, 0x84, 0xf6, + 0xf5, 0x83, 0x0e, 0xf6, 0xa0, 0x74, 0x72, 0xae, 0xe8, 0x50, 0x2d, 0x6f, 0x66, 0x66, 0x2e, 0xee, + 0x82, 0x31, 0xf3, 0xb2, 0x62, 0x65, 0x20, 0xd6, 0xfb, 0xd4, 0x74, 0x75, 0x6e, 0x65, 0x64, 0x47, + 0xe9, 0xc2, 0x65, 0x61, 0x63, 0x68, 0x85, 0xf5, 0x01, 0xbe, 0x23, 0x76, 0x65, 0x79, 0x83, 0x3b, + 0xfd, 0x82, 0x82, 0xfa, 0x91, 0x72, 0x7e, 0xf9, 0x80, 0xc2, 0xfc, 0x42, 0x2b, 0x7e, 0x33, 0x25, + 0x18, 0xa5, 0x70, 0x65, 0xcb, 0xf8, 0x2f, 0x41, 0x6e, 0x3e, 0x00, 0x82, 0x79, 0xf8, 0xb6, 0x22, + 0x31, 0x22, 0x66, 0xf8, 0x84, 0xbb, 0xfe, 0x40, 0x72, 0x65, 0x67, 0x75, 0x06, 0x8f, 0xb3, 0xfe, + 0x05, 0x02, 0xb1, 0x11, 0x56, 0xc1, 0x91, 0x73, 0xc4, 0xf8, 0x93, 0x30, 0x87, 0xf9, 0x92, 0x62, + 0xb6, 0xed, 0xb1, 0x6c, 0x61, 0x63, 0xf6, 0xf7, 0xd0, 0x79, 0x20, 0x41, 0x43, 0x43, 0x13, 0xf1, + 0xb1, 0x52, 0x41, 0x54, 0x08, 0xf1, 0xf2, 0x05, 0x44, 0x45, 0x46, 0x41, 0x55, 0x4c, 0x54, 0x20, + 0x28, 0x73, 0x65, 0x65, 0x48, 0xee, 0xa4, 0x63, 0x29, 0x15, 0xf8, 0x82, 0x7f, 0xf2, 0x95, 0x2e, + 0xfb, 0xfd, 0x8e, 0x21, 0xfe, 0x9f, 0x20, 0x3a, 0xf3, 0x2a, 0x06, 0xef, 0x0a, 0x07, 0xbf, 0x29, + 0x3b, 0x0a, 0xb5, 0xfd, 0x04, 0xe0, 0x5f, 0x65, 0x78, 0x74, 0x53, 0x74, 0x9a, 0xf7, 0x8a, 0xac, + 0xfd, 0x8f, 0xae, 0xed, 0x03, 0xb1, 0x2c, 0x20, 0x6a, 0x5e, 0xf7, 0xa3, 0x75, 0x73, 0x9c, 0xf8, + 0x93, 0x20, 0x5a, 0xed, 0xb6, 0x61, 0x6c, 0x6c, 0x98, 0xf3, 0x87, 0x60, 0xf1, 0xd2, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x74, 0xfe, 0x3b, 0x74, 0x6f, 0x72, 0xb1, 0x11, 0x73, 0x92, 0x83, 0x62, 0xfe, + 0xa3, 0x55, 0x73, 0xed, 0xf9, 0x81, 0xc8, 0xfc, 0x24, 0x6f, 0x66, 0x79, 0x01, 0xca, 0x60, 0x6b, + 0x6e, 0x6f, 0x77, 0x20, 0x68, 0xfc, 0x44, 0x6d, 0x75, 0x63, 0x68, 0xac, 0x85, 0xe0, 0xf6, 0x07, + 0x93, 0x83, 0x33, 0xf4, 0x36, 0x61, 0x6e, 0x64, 0xed, 0x40, 0x20, 0x69, 0x74, 0x20, 0x9d, 0xa3, + 0x38, 0x2d, 0x86, 0xf9, 0x91, 0x62, 0xad, 0xfc, 0xb1, 0x61, 0x72, 0x69, 0xcc, 0xfa, 0x03, 0x49, + 0x12, 0x6d, 0xd6, 0x01, 0x9b, 0x41, 0x79, 0x70, 0x69, 0x63, 0x44, 0x96, 0x29, 0x09, 0xfd, 0xa4, + 0x6e, 0x2c, 0xe7, 0xfa, 0x02, 0xb9, 0xc1, 0x61, 0x73, 0x20, 0x27, 0x66, 0xef, 0x13, 0x2a, 0x4f, + 0x10, 0x27, 0x69, 0x8f, 0xea, 0xfe, 0x03, 0x85, 0x39, 0xfe, 0x0e, 0x37, 0x01, 0xc3, 0x84, 0xb1, + 0xf1, 0x8f, 0x8a, 0xfe, 0x08, 0x28, 0x20, 0x28, 0x9c, 0xaf, 0x2c, 0x20, 0x08, 0xfe, 0x0e, 0x86, + 0xe2, 0xfb, 0x8f, 0x09, 0xfe, 0x2a, 0x13, 0x64, 0xd1, 0x87, 0x0e, 0xfe, 0xa1, 0x52, 0x65, 0x6e, + 0xee, 0x93, 0x65, 0x4b, 0xfc, 0xd0, 0x6f, 0x67, 0x69, 0x63, 0x2c, 0x34, 0xfd, 0x05, 0x13, 0x83, + 0x12, 0xfe, 0x92, 0x73, 0x76, 0xfe, 0x82, 0x2a, 0xf9, 0x01, 0xf3, 0xa3, 0x70, 0x6f, 0x2f, 0xf9, + 0x82, 0x79, 0xf1, 0x8d, 0x6d, 0xf3, 0x8f, 0x79, 0xf1, 0x09, 0x8b, 0x24, 0xf4, 0x87, 0x79, 0xf1, + 0x91, 0x74, 0x8f, 0xfb, 0x99, 0x74, 0x76, 0xf1, 0x8e, 0xd8, 0xf8, 0xa1, 0x65, 0x69, 0xd2, 0xe6, + 0x06, 0x6a, 0x84, 0x4a, 0xf8, 0xd6, 0x65, 0x6e, 0x74, 0x69, 0x72, 0xeb, 0xf2, 0x86, 0x85, 0xf2, + 0x02, 0x81, 0x05, 0x9a, 0xa2, 0x69, 0x66, 0x9f, 0xf1, 0x8b, 0x50, 0xf5, 0x94, 0x0a, 0x41, 0xfa, + 0xc9, 0x66, 0x69, 0x6c, 0x6c, 0xe3, 0xf0, 0x04, 0x9d, 0xa1, 0x6c, 0x65, 0xf6, 0xf1, 0x82, 0x81, + 0xfb, 0x0f, 0x03, 0x15, 0x87, 0x13, 0xf6, 0x97, 0x2a, 0x66, 0xfc, 0xd5, 0x50, 0x74, 0x72, 0x20, + 0x3a, 0xd6, 0xfb, 0x84, 0x8d, 0xe4, 0x83, 0xb6, 0xf0, 0xc1, 0x69, 0x6e, 0x64, 0x69, 0x4e, 0xfd, + 0x83, 0x1a, 0xfd, 0xb5, 0x61, 0x6e, 0x79, 0xf6, 0xf2, 0x00, 0x1b, 0x92, 0x65, 0x0e, 0xf2, 0x0b, + 0xa1, 0x00, 0xcc, 0x08, 0x54, 0x08, 0x92, 0x0e, 0xff, 0xb7, 0x4e, 0x65, 0x77, 0xfe, 0xf8, 0x8c, + 0x0e, 0xf4, 0xa4, 0x6f, 0x6c, 0xe2, 0xf8, 0x9f, 0x2e, 0xf7, 0xf8, 0x00, 0xaf, 0x4e, 0x62, 0x73, + 0xf2, 0x01, 0x8f, 0x7a, 0xf2, 0x06, 0x86, 0x4f, 0xfe, 0x8f, 0x79, 0xf2, 0x1c, 0x88, 0x0d, 0xfb, + 0x8e, 0x70, 0xfd, 0x8f, 0x9e, 0xee, 0x14, 0xaa, 0x2a, 0x20, 0xa2, 0xfe, 0x02, 0xec, 0x0c, 0x72, + 0x89, 0x0b, 0xfd, 0xaf, 0x64, 0x65, 0xc7, 0xf8, 0x04, 0xd0, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x44, + 0xfb, 0x8a, 0x5c, 0xf2, 0x06, 0xea, 0x85, 0x00, 0xf0, 0xb1, 0x72, 0x65, 0x66, 0x42, 0xfb, 0xa7, + 0x75, 0x6e, 0xa3, 0xf2, 0x82, 0x4f, 0xfd, 0x8f, 0x70, 0xf2, 0x0f, 0x87, 0x3f, 0xfe, 0x88, 0x2a, + 0xf3, 0x86, 0x79, 0xf2, 0xa4, 0x69, 0x6e, 0x05, 0xe4, 0xd3, 0x77, 0x6f, 0x72, 0x64, 0x73, 0xb0, + 0xf8, 0x0c, 0x9e, 0x9f, 0x29, 0xe2, 0xf2, 0x73, 0x9e, 0x44, 0x98, 0xf5, 0x8f, 0x5b, 0xf1, 0x09, + 0x42, 0x20, 0x49, 0x74, 0x73, 0x44, 0x07, 0xdc, 0xc3, 0x20, 0x6d, 0x69, 0x6e, 0xa3, 0xf6, 0xb5, + 0x66, 0x20, 0x27, 0x8d, 0xfe, 0x88, 0x3a, 0xed, 0x03, 0x91, 0x91, 0x6e, 0xc6, 0xf5, 0x9c, 0x3a, + 0x11, 0xfc, 0xa1, 0x66, 0x75, 0xa8, 0xf9, 0x84, 0xa6, 0xe7, 0x84, 0xfe, 0xf9, 0x89, 0x30, 0xfa, + 0x91, 0x66, 0x65, 0xef, 0x91, 0x72, 0x6c, 0xe7, 0x00, 0xd9, 0x03, 0x05, 0x8d, 0x67, 0xe7, 0x0a, + 0x33, 0xa2, 0x49, 0x74, 0x02, 0xf5, 0xc7, 0x20, 0x62, 0x69, 0x74, 0x44, 0xf7, 0xbf, 0x61, 0x6e, + 0x20, 0xe9, 0xef, 0x03, 0x0a, 0xc6, 0xb1, 0x48, 0x6f, 0x77, 0xe0, 0xfa, 0x91, 0x2c, 0xeb, 0xf9, + 0xb2, 0x64, 0x6f, 0x65, 0x04, 0xf1, 0x86, 0xd7, 0xf9, 0xb3, 0x61, 0x6e, 0x79, 0xcd, 0xf1, 0x02, + 0x4a, 0x86, 0xcc, 0xf1, 0x92, 0x69, 0x7a, 0xfb, 0xa2, 0x69, 0x6f, 0xe2, 0xf8, 0x84, 0x08, 0xfc, + 0x04, 0x6a, 0x85, 0x42, 0xfe, 0x96, 0x28, 0xcc, 0xf1, 0x83, 0x2e, 0xfa, 0x0b, 0x8b, 0x91, 0x55, + 0x71, 0xfa, 0x8b, 0xd8, 0xf3, 0x50, 0x6e, 0x20, 0x74, 0x72, 0x75, 0x47, 0x90, 0x64, 0x3d, 0xfb, + 0xd2, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x42, 0xe8, 0x20, 0x6f, 0x6e, 0x9f, 0x12, 0x28, 0xa7, 0xa3, + 0x74, 0x6f, 0x1b, 0xfe, 0x82, 0xb3, 0xfd, 0x85, 0xdd, 0xeb, 0x15, 0x61, 0xca, 0x84, 0xc3, 0xfd, + 0x9a, 0x29, 0x50, 0xf9, 0xaf, 0x64, 0x65, 0x87, 0xf7, 0x20, 0x86, 0xc4, 0xfc, 0x8a, 0x5e, 0xf5, + 0x8e, 0xba, 0xfe, 0x94, 0x5f, 0xc7, 0xed, 0x83, 0x8d, 0xfc, 0x8e, 0x31, 0xfa, 0x07, 0xd2, 0xae, + 0x20, 0x61, 0xb1, 0xee, 0x87, 0xe7, 0xf9, 0x8b, 0x62, 0xef, 0x91, 0x27, 0x34, 0xe4, 0x81, 0x48, + 0xe4, 0x01, 0xbd, 0x8a, 0x09, 0xeb, 0x8e, 0x0c, 0xef, 0x8f, 0x96, 0xf9, 0x06, 0x8c, 0x0c, 0xef, + 0x89, 0x91, 0xf9, 0x89, 0xc8, 0xfc, 0x92, 0x74, 0x9b, 0xfd, 0xa5, 0x74, 0x6f, 0xc4, 0xfc, 0x89, + 0xf3, 0xf8, 0xa5, 0x6f, 0x70, 0xb2, 0xf5, 0x52, 0x61, 0x73, 0x20, 0x73, 0x6f, 0xf8, 0x85, 0x3d, + 0xf9, 0x87, 0x65, 0xed, 0x30, 0x27, 0x20, 0x68, 0xe9, 0xe1, 0x62, 0x65, 0x65, 0x6e, 0x20, 0x72, + 0x3f, 0xf5, 0x85, 0x51, 0xf7, 0x91, 0x72, 0x37, 0xe8, 0x00, 0xb9, 0x08, 0xab, 0x02, 0x8f, 0xbf, + 0x69, 0x6d, 0x65, 0x18, 0xee, 0x15, 0xb4, 0x64, 0x65, 0x64, 0xa9, 0xdf, 0x8f, 0x1b, 0xee, 0x2e, + 0x92, 0x4e, 0x86, 0xfc, 0x82, 0x97, 0xfd, 0x04, 0x96, 0x85, 0x7c, 0xf4, 0x1f, 0x3c, 0x2f, 0x02, + 0xef, 0x73, 0x68, 0x6f, 0x75, 0x6c, 0x64, 0xff, 0xec, 0x03, 0x87, 0x83, 0xfd, 0x01, 0xc2, 0x91, + 0x73, 0x99, 0xf6, 0xac, 0x65, 0x72, 0xb2, 0xfb, 0xe5, 0x41, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x80, + 0xe3, 0x8e, 0x07, 0xf9, 0x35, 0x65, 0x72, 0x65, 0x21, 0x8f, 0xe4, 0xed, 0x81, 0x8f, 0x45, 0xee, + 0x05, 0x9c, 0x66, 0x42, 0xee, 0x01, 0xa0, 0x02, 0xd5, 0x8d, 0x3c, 0xee, 0xab, 0x6f, 0x66, 0x39, + 0xee, 0x83, 0x6f, 0xfb, 0x88, 0x9b, 0xf9, 0x8e, 0x87, 0xed, 0x8f, 0xab, 0xed, 0x04, 0x86, 0xbd, + 0xf8, 0x8f, 0x8d, 0xfc, 0x09, 0x8f, 0x55, 0xe7, 0x28, 0x89, 0x0c, 0xfe, 0x8f, 0x3f, 0xe7, 0x0d, + 0x8a, 0x7b, 0xed, 0x8f, 0x0d, 0xe6, 0x19, 0x82, 0x42, 0xfe, 0xb9, 0x69, 0x6e, 0x67, 0x97, 0xe7, + 0x8f, 0x65, 0xed, 0x02, 0x8f, 0x5c, 0xed, 0x22, 0xe1, 0x53, 0x54, 0x52, 0x45, 0x41, 0x4d, 0x9e, + 0xf0, 0xc0, 0x5f, 0x55, 0x36, 0x34, 0xee, 0xed, 0xed, 0x31, 0x20, 0x3c, 0x3c, 0x20, 0x28, 0x58, + 0xe5, 0xb1, 0x2d, 0x33, 0x29, 0xdf, 0xed, 0x2f, 0x34, 0x29, 0xc3, 0x01, 0x86, 0x23, 0xed, 0x1f, + 0x28, 0xaf, 0x01, 0xa3, 0x2a, 0x20, 0x1f, 0xf4, 0x30, 0x28, 0x6c, 0x6f, 0x3f, 0x01, 0xfb, 0xa8, + 0x29, 0x29, 0xad, 0xe3, 0x83, 0x66, 0xfd, 0x92, 0x5f, 0x8f, 0xe4, 0xa1, 0x69, 0x6e, 0x6e, 0xfd, + 0x83, 0x66, 0xfc, 0x00, 0xe8, 0xd2, 0x75, 0x63, 0x74, 0x75, 0x72, 0xef, 0xf2, 0xa1, 0x74, 0x72, + 0xae, 0xee, 0x83, 0x86, 0xf9, 0x84, 0x34, 0xfd, 0x12, 0x2e, 0xcf, 0x91, 0x6d, 0x42, 0xef, 0xb1, + 0x61, 0x6e, 0x74, 0x71, 0xf7, 0xb2, 0x6e, 0x69, 0x74, 0x6a, 0xfc, 0x07, 0xc5, 0x86, 0x26, 0xf5, + 0x93, 0x62, 0xd1, 0xfd, 0xd2, 0x66, 0x69, 0x72, 0x73, 0x74, 0xe4, 0xe0, 0x10, 0x21, 0xc2, 0x85, + 0xbc, 0xf8, 0xaa, 0x6f, 0x6e, 0x73, 0xf2, 0x60, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0xed, 0x38, + 0x74, 0x68, 0x65, 0xb5, 0x93, 0x69, 0x18, 0xe0, 0xb2, 0x61, 0x72, 0x65, 0x88, 0xf3, 0x83, 0xf8, + 0xf2, 0xd4, 0x20, 0x6c, 0x69, 0x6e, 0x6b, 0x3d, 0xe0, 0x00, 0xae, 0x86, 0x84, 0xfc, 0x06, 0xd4, + 0x84, 0x07, 0xf2, 0xe1, 0x6c, 0x69, 0x62, 0x6c, 0x7a, 0x34, 0x0d, 0xfb, 0xda, 0x61, 0x20, 0x44, + 0x4c, 0x4c, 0x59, 0xe0, 0x51, 0x62, 0x65, 0x6c, 0x6f, 0x77, 0x56, 0x03, 0x94, 0x82, 0xcb, 0xec, + 0xe6, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x73, 0x49, 0xe0, 0x82, 0xbf, 0xe3, 0xc0, 0x74, 0x79, 0x70, + 0x65, 0x86, 0xfe, 0x04, 0x6d, 0xb6, 0x20, 0x7b, 0x20, 0xb0, 0xfe, 0xa1, 0x20, 0x74, 0x9d, 0xdf, + 0x9f, 0x5b, 0x84, 0xfe, 0x00, 0xca, 0x5d, 0x3b, 0x20, 0x7d, 0x9b, 0xfe, 0xa8, 0x3b, 0x0a, 0x86, + 0xfe, 0xd3, 0x72, 0x65, 0x73, 0x65, 0x74, 0xa5, 0xfd, 0x01, 0xed, 0x8e, 0xc2, 0xf8, 0x83, 0x8e, + 0xf4, 0x91, 0x69, 0x04, 0xef, 0x87, 0xe1, 0xfe, 0x0a, 0xb1, 0x87, 0xe1, 0xfe, 0x02, 0x5e, 0x81, + 0x6a, 0xf2, 0x0d, 0xa6, 0x29, 0x20, 0x28, 0xcf, 0x94, 0x2a, 0x43, 0xfe, 0x4a, 0x50, 0x74, 0x72, + 0x29, 0x74, 0x81, 0x22, 0xe2, 0x23, 0x74, 0x65, 0xcd, 0x82, 0x7e, 0xfb, 0x05, 0x8e, 0x82, 0x67, + 0xd8, 0x02, 0x79, 0x51, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0xf1, 0x0f, 0x7d, 0x05, 0x05, 0xb5, 0x35, + 0x66, 0x72, 0x65, 0xb7, 0xa2, 0x72, 0x65, 0x99, 0xfe, 0x91, 0x73, 0xda, 0xde, 0x84, 0xf1, 0xf6, + 0x83, 0x4c, 0xde, 0x81, 0xf7, 0xf9, 0x84, 0xe2, 0xfd, 0x91, 0x78, 0x63, 0xd9, 0x83, 0x69, 0xfe, + 0xa3, 0x20, 0x28, 0x58, 0xfe, 0x9a, 0x29, 0x60, 0xfe, 0x20, 0x74, 0x68, 0x32, 0x86, 0x6d, 0xfe, + 0xc5, 0x72, 0x61, 0x74, 0x68, 0xf9, 0xf6, 0xa5, 0x74, 0x68, 0xf4, 0xfd, 0x04, 0x7a, 0x02, 0xa3, + 0xc2, 0x54, 0x68, 0x65, 0x79, 0x05, 0xfe, 0x92, 0x6d, 0x8d, 0xfd, 0xb3, 0x75, 0x74, 0x75, 0x57, + 0xfb, 0xa1, 0x6f, 0x66, 0xe7, 0xfb, 0x82, 0xe5, 0xec, 0x02, 0x8e, 0x83, 0x0f, 0xdf, 0x01, 0xf4, + 0x0c, 0x2f, 0x00, 0x1a, 0x83, 0x0e, 0xfe, 0x09, 0xe9, 0x8f, 0xd9, 0xfe, 0x00, 0x89, 0xd4, 0xf0, + 0x06, 0xff, 0x86, 0xfe, 0xfe, 0x8f, 0x82, 0xfe, 0x1b, 0xff, 0x01, 0x6c, 0x6f, 0x61, 0x64, 0x44, + 0x69, 0x63, 0x74, 0xf9, 0xfd, 0x07, 0x01, 0xdf, 0x25, 0x20, 0x61, 0x13, 0x22, 0x64, 0x69, 0xe7, + 0xb2, 0x61, 0x72, 0x79, 0xbe, 0xf7, 0x02, 0x41, 0x88, 0x6b, 0xfc, 0x92, 0x41, 0x21, 0xf6, 0xa6, + 0x65, 0x76, 0x7a, 0xfa, 0x86, 0x34, 0xf2, 0xd1, 0x66, 0x6f, 0x72, 0x67, 0x6f, 0xd9, 0xf2, 0x93, + 0x2c, 0x8b, 0xfc, 0x17, 0x27, 0xb6, 0x13, 0x27, 0xdb, 0xd1, 0x72, 0x65, 0x6d, 0x61, 0x69, 0x48, + 0xf6, 0x89, 0x51, 0xfe, 0xb3, 0x4c, 0x6f, 0x61, 0x79, 0xf9, 0x86, 0xf0, 0xe5, 0xc3, 0x30, 0x20, + 0x69, 0x73, 0x5c, 0xec, 0x22, 0x65, 0x64, 0xdd, 0x94, 0x52, 0x00, 0xf8, 0x09, 0x64, 0x94, 0x73, + 0x74, 0xfa, 0x83, 0xd4, 0xf8, 0x8e, 0x1c, 0xf8, 0xd0, 0x36, 0x34, 0x20, 0x4b, 0x42, 0xa3, 0xdf, + 0x83, 0xe5, 0xf9, 0x8a, 0xf8, 0xfe, 0x8f, 0xc9, 0xfe, 0x07, 0x8a, 0x93, 0xef, 0x08, 0x94, 0x82, + 0x0d, 0xfa, 0x01, 0xf0, 0x88, 0x7e, 0xeb, 0x02, 0x46, 0x8e, 0x3c, 0xef, 0x81, 0x8c, 0xfd, 0xb1, + 0x69, 0x6e, 0x75, 0x52, 0xfd, 0x86, 0x33, 0xfa, 0x84, 0x9b, 0xdb, 0x86, 0x52, 0xfb, 0xe3, 0x27, + 0x73, 0x72, 0x63, 0x27, 0x2c, 0xc5, 0xfb, 0x82, 0xbf, 0xfe, 0x82, 0x6a, 0xf5, 0x86, 0xac, 0xfe, + 0xae, 0x6c, 0x79, 0xb6, 0xf7, 0x49, 0x73, 0x20, 0x61, 0x73, 0x05, 0xa6, 0x74, 0x6f, 0x1d, 0xdf, + 0x8e, 0xe7, 0xde, 0x83, 0xfe, 0xfc, 0x88, 0xc7, 0xfa, 0x99, 0x50, 0x54, 0xfe, 0x06, 0xb0, 0x91, + 0x72, 0x21, 0xec, 0xb4, 0x73, 0x75, 0x6d, 0x3c, 0xe1, 0x94, 0x74, 0x3d, 0xfe, 0x01, 0xb4, 0x00, + 0x5c, 0x83, 0xbf, 0xd4, 0x95, 0x6e, 0x5b, 0xf4, 0x83, 0xb0, 0xfa, 0xdf, 0x27, 0x64, 0x73, 0x74, + 0x27, 0x0c, 0xf3, 0x0f, 0x84, 0xca, 0xda, 0x82, 0xdf, 0xf8, 0x83, 0x3c, 0xf1, 0x8f, 0x43, 0xe9, + 0x0c, 0xbf, 0x2c, 0x20, 0x63, 0xa2, 0xe0, 0x11, 0x82, 0xbb, 0xf7, 0x89, 0xdc, 0xe0, 0x05, 0x99, + 0x33, 0x6e, 0x6f, 0x74, 0xe4, 0xad, 0x69, 0x66, 0x7b, 0xda, 0x85, 0xed, 0xe0, 0xa3, 0x66, 0x69, + 0x04, 0xef, 0x0a, 0x3f, 0x84, 0xc1, 0xfd, 0x8e, 0x0a, 0xe1, 0x02, 0xb3, 0x89, 0x1c, 0xe1, 0xb1, + 0x74, 0x75, 0x72, 0x2d, 0xd5, 0x85, 0x1c, 0xe1, 0x8d, 0x60, 0xeb, 0x8e, 0x06, 0xfe, 0x8f, 0xa3, + 0xfd, 0x15, 0x00, 0x01, 0x86, 0x81, 0xf7, 0x86, 0x42, 0xeb, 0xab, 0x72, 0x63, 0x9f, 0xf7, 0x8f, + 0x3d, 0xed, 0x09, 0x89, 0x9d, 0xf8, 0xb5, 0x61, 0x76, 0x65, 0x21, 0xfc, 0xaf, 0x49, 0x66, 0xb8, + 0xfd, 0x04, 0x88, 0x03, 0xfe, 0x84, 0x55, 0xe3, 0x8b, 0x98, 0xfe, 0x85, 0x66, 0xfc, 0xd2, 0x61, + 0x76, 0x61, 0x69, 0x6c, 0x2e, 0xd9, 0xa8, 0x61, 0x74, 0xa9, 0xfa, 0x95, 0x20, 0x1b, 0xe7, 0x01, + 0x9c, 0x01, 0x90, 0x95, 0x20, 0xac, 0xfe, 0x10, 0x61, 0xf1, 0x81, 0xb1, 0xfe, 0x82, 0x10, 0xea, + 0x22, 0x20, 0x28, 0x23, 0x02, 0xed, 0x92, 0x42, 0x99, 0xfe, 0x81, 0x22, 0xdc, 0x84, 0xad, 0xf4, + 0x82, 0xcc, 0xf8, 0xd6, 0x64, 0x6f, 0x6e, 0x27, 0x74, 0xb2, 0xd8, 0x81, 0x9b, 0xf8, 0x8a, 0x66, + 0xfc, 0xd0, 0x28, 0x29, 0x20, 0x61, 0x66, 0x22, 0xfe, 0xa1, 0x77, 0x61, 0xfb, 0xef, 0x87, 0x8d, + 0xf8, 0x8a, 0x6c, 0xfb, 0xa8, 0x73, 0x20, 0x67, 0xdf, 0x31, 0x20, 0x75, 0x73, 0x51, 0xb3, 0x2c, + 0x20, 0x79, 0x13, 0xd7, 0x87, 0xc2, 0xf5, 0x02, 0xa2, 0x8f, 0x57, 0xfe, 0x09, 0xa9, 0x28, 0x29, + 0xa6, 0xfb, 0x02, 0x2a, 0x9d, 0x64, 0xa0, 0xfb, 0x8f, 0xa1, 0xfb, 0x07, 0x87, 0xf3, 0xfb, 0x95, + 0x2c, 0x26, 0xee, 0x83, 0x33, 0xe2, 0x8a, 0xde, 0xfd, 0x01, 0xac, 0x8f, 0x8f, 0xfb, 0x0d, 0x8d, + 0xcf, 0xfe, 0x86, 0x95, 0xfb, 0x89, 0x49, 0xee, 0x8f, 0xac, 0xf5, 0x2b, 0xbf, 0x44, 0x65, 0x63, + 0xaa, 0xf5, 0x32, 0x00, 0x29, 0x8f, 0xe5, 0xf5, 0x01, 0xe6, 0x44, 0x45, 0x43, 0x4f, 0x44, 0x45, + 0xf3, 0xf5, 0x2f, 0x20, 0x34, 0xdc, 0x07, 0x87, 0xbb, 0xf5, 0x0d, 0xc2, 0x8e, 0xb5, 0xf5, 0x86, + 0x1d, 0xe3, 0x99, 0x20, 0xac, 0xf5, 0x8e, 0xdf, 0xf6, 0x0a, 0xda, 0x8e, 0xd6, 0xf6, 0x0e, 0x9f, + 0x8e, 0xd0, 0xf6, 0x92, 0x44, 0xa7, 0xf2, 0xb3, 0x5f, 0x74, 0x3b, 0x51, 0xf5, 0x0e, 0xe6, 0x8f, + 0x4b, 0xf5, 0x24, 0x8f, 0x57, 0xf5, 0x09, 0x84, 0xd8, 0xe7, 0x03, 0x97, 0x38, 0x65, 0x74, 0x53, + 0x94, 0x93, 0x20, 0x22, 0xe3, 0x00, 0xea, 0xaf, 0x28, 0x29, 0x31, 0xf5, 0x04, 0x8f, 0x27, 0xf7, + 0x12, 0x85, 0x28, 0xf7, 0xe4, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0xef, 0xd8, 0xaf, 0x6f, 0x66, + 0x82, 0xf5, 0x03, 0x83, 0x67, 0xf5, 0x8f, 0x2d, 0xf7, 0x26, 0x8e, 0xd4, 0xfe, 0x87, 0xda, 0xe3, + 0x25, 0x74, 0x68, 0xbc, 0x9b, 0x2e, 0x01, 0xf6, 0x09, 0x10, 0x8f, 0xfb, 0xf5, 0x0e, 0x0e, 0x9f, + 0x8f, 0xf5, 0xf5, 0x05, 0x0a, 0xab, 0x8f, 0xef, 0xf5, 0x06, 0x2f, 0x2f, 0x0a, 0xb2, 0x00, 0x0f, + 0x67, 0x05, 0x8a, 0x5d, 0xe7, 0x0c, 0xff, 0x0f, 0x8c, 0x04, 0x1f, 0x28, 0xa4, 0x01, 0x08, 0x42, + 0x89, 0x55, 0xfa, 0x8e, 0x0a, 0xfe, 0x8f, 0x68, 0xf4, 0x09, 0x03, 0x13, 0x83, 0x05, 0xe9, 0x83, + 0x18, 0xe9, 0x83, 0x81, 0xda, 0x86, 0x5e, 0xfb, 0x84, 0xc3, 0xf6, 0xcf, 0x53, 0x65, 0x74, 0x74, + 0xc3, 0xf6, 0x08, 0xd5, 0x20, 0x28, 0x73, 0x61, 0x6d, 0x0c, 0xd7, 0x83, 0x30, 0xe4, 0x00, 0x7d, + 0x9b, 0x29, 0xac, 0xf6, 0x91, 0x31, 0x43, 0xfb, 0xbf, 0x4f, 0x4b, 0x2c, 0x3a, 0xfb, 0x07, 0x2f, + 0x65, 0x74, 0x0e, 0x10, 0x0d, 0xec, 0x8f, 0xb5, 0xf6, 0x1b, 0x96, 0x2a, 0x6b, 0xfa, 0x88, 0xe6, + 0xec, 0x00, 0x56, 0x86, 0x5d, 0xef, 0x86, 0x3e, 0xd2, 0x04, 0x26, 0x8b, 0xd7, 0xed, 0x01, 0x0a, + 0xd0, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0xc7, 0xd6, 0x85, 0x1f, 0xf7, 0xd5, 0x69, 0x6e, 0x20, 0x22, + 0x73, 0xfc, 0xfa, 0x91, 0x22, 0x91, 0xeb, 0x84, 0xb5, 0xed, 0x96, 0x50, 0xe1, 0xf8, 0x03, 0xa2, + 0x87, 0x9c, 0xf6, 0x91, 0x2a, 0x2c, 0xf7, 0x9f, 0x2a, 0xf6, 0xf8, 0x03, 0xb4, 0x74, 0x68, 0x65, + 0xf6, 0xf8, 0x86, 0x95, 0xec, 0x85, 0x65, 0xfe, 0xba, 0x68, 0x65, 0x79, 0x58, 0xee, 0xc0, 0x20, + 0x28, 0x75, 0x70, 0x4f, 0xfe, 0x86, 0x80, 0xf5, 0xa4, 0x20, 0x20, 0x49, 0xfc, 0x88, 0xb8, 0xfc, + 0x10, 0x72, 0x2d, 0x84, 0x78, 0xf7, 0x97, 0x73, 0xda, 0xdb, 0x8c, 0xa5, 0xf6, 0x84, 0x22, 0xe6, + 0x84, 0xf0, 0xfe, 0xd2, 0x2d, 0x20, 0x45, 0x78, 0x61, 0x48, 0xf1, 0xb4, 0x73, 0x61, 0x6d, 0xc6, + 0xda, 0x5a, 0x61, 0x73, 0x20, 0x65, 0x6e, 0xc4, 0x83, 0xe3, 0xe1, 0x03, 0xdd, 0xb1, 0x75, 0x70, + 0x64, 0xa5, 0xfc, 0x20, 0x72, 0x75, 0x3d, 0x12, 0x28, 0x1d, 0x89, 0xd4, 0xe9, 0x22, 0x61, 0x74, + 0xda, 0x06, 0x31, 0x11, 0x73, 0x55, 0x03, 0x53, 0x83, 0x70, 0xe2, 0x01, 0x51, 0x83, 0x28, 0xe1, + 0x07, 0x5f, 0x16, 0x26, 0x90, 0x07, 0x3d, 0x84, 0xe0, 0xec, 0xb2, 0x68, 0x61, 0x76, 0x06, 0xea, + 0x02, 0x66, 0x89, 0xf9, 0xdb, 0xc3, 0x76, 0x65, 0x72, 0x79, 0x03, 0xed, 0xb1, 0x20, 0x6f, 0x6e, + 0xb7, 0xf8, 0xa4, 0x20, 0x3c, 0xe4, 0xfe, 0x14, 0x2e, 0x27, 0x95, 0x4c, 0xa6, 0xe0, 0x00, 0xb9, + 0x0e, 0x30, 0xab, 0x62, 0x79, 0xcd, 0xe8, 0x41, 0x6d, 0x61, 0x78, 0x42, 0x34, 0xb3, 0x53, 0x69, + 0x7a, 0x50, 0xfb, 0x89, 0xca, 0xe8, 0x1b, 0x20, 0xe1, 0xb2, 0x69, 0x73, 0x20, 0xb3, 0xd4, 0x87, + 0x9b, 0xcb, 0xb1, 0x64, 0x65, 0x70, 0xb5, 0xd6, 0xb3, 0x6e, 0x74, 0x2e, 0x92, 0xe0, 0x8e, 0x1b, + 0xdd, 0xb3, 0x79, 0x6f, 0x75, 0x57, 0xe9, 0x99, 0x64, 0x24, 0xe3, 0x86, 0x15, 0xf7, 0x00, 0x66, + 0x85, 0x9d, 0xfd, 0x9f, 0x2e, 0xd0, 0xfe, 0x04, 0xa7, 0x65, 0x6e, 0xab, 0xec, 0x8d, 0x26, 0xfe, + 0xc0, 0x73, 0x20, 0x64, 0x6f, 0x85, 0xf6, 0x86, 0x00, 0xf7, 0x82, 0xe9, 0xeb, 0xc0, 0x79, 0x6e, + 0x63, 0x68, 0x47, 0xe9, 0xa3, 0x69, 0x7a, 0xf3, 0xea, 0x84, 0x42, 0xe2, 0x8f, 0x97, 0xfe, 0x20, + 0x8f, 0x9c, 0xfe, 0x0c, 0xe1, 0x5f, 0x41, 0x74, 0x20, 0x6c, 0x65, 0x01, 0xf7, 0x03, 0xe7, 0xc4, + 0x20, 0x2b, 0x20, 0x38, 0x26, 0xf7, 0x9a, 0x2b, 0xcb, 0xfe, 0x0f, 0x29, 0x7f, 0x97, 0x6c, 0xe2, + 0xfd, 0x0d, 0x87, 0x03, 0x54, 0xb2, 0x57, 0x68, 0x65, 0xa3, 0xeb, 0x83, 0x36, 0xf0, 0x8c, 0x74, + 0xc8, 0x01, 0x68, 0x86, 0x63, 0xe3, 0x93, 0x2c, 0x45, 0xf5, 0x92, 0x74, 0x32, 0xde, 0xa2, 0x73, + 0x74, 0x70, 0xd1, 0x33, 0x20, 0x6f, 0x66, 0xa9, 0x84, 0xdc, 0xf4, 0x89, 0x2a, 0xf5, 0x04, 0x99, + 0x86, 0xad, 0xe0, 0x86, 0x5f, 0xe3, 0x84, 0x04, 0xfc, 0x91, 0x69, 0x51, 0xeb, 0x83, 0xc7, 0xf5, + 0x8f, 0x26, 0xf8, 0x08, 0xaf, 0x28, 0x29, 0x62, 0xeb, 0x09, 0x85, 0xca, 0xf3, 0x8f, 0xab, 0xfa, + 0x17, 0x8f, 0x90, 0xd2, 0x3b, 0x8f, 0x0d, 0xe7, 0x06, 0x0f, 0x6a, 0x3a, 0x8e, 0xde, 0xe6, 0x89, + 0x72, 0xe3, 0x85, 0x79, 0xd8, 0x8f, 0x18, 0xfa, 0x02, 0xc2, 0x3a, 0x0a, 0x2a, 0x5f, 0xab, 0xfe, + 0x81, 0xe8, 0xf3, 0x8f, 0xe9, 0xf9, 0x10, 0xc9, 0x77, 0x6f, 0x72, 0x6b, 0x46, 0xdd, 0x01, 0xd2, + 0x83, 0xba, 0xe6, 0x95, 0x62, 0xf3, 0xe7, 0xaf, 0x6f, 0x66, 0x60, 0xfe, 0x04, 0x84, 0x58, 0xc7, + 0x82, 0x52, 0xdd, 0x8d, 0xf1, 0xfe, 0x98, 0x78, 0xd1, 0xf3, 0x05, 0x7f, 0x95, 0x79, 0xda, 0xeb, + 0xd0, 0x6e, 0x64, 0x2d, 0x61, 0x6c, 0x8f, 0xfc, 0x12, 0x2e, 0xea, 0x89, 0x24, 0xf3, 0xb3, 0x6e, + 0x6f, 0x72, 0x80, 0xfa, 0x8f, 0x34, 0xf7, 0x0f, 0x9f, 0x2e, 0xe2, 0xfd, 0x09, 0x07, 0x00, 0x8f, + 0x97, 0xd0, 0x3b, 0x8e, 0x65, 0xf8, 0x81, 0x89, 0xde, 0xa7, 0x72, 0x74, 0x66, 0xf8, 0x8f, 0xe2, + 0xfd, 0x0c, 0x06, 0x6a, 0x8f, 0xe5, 0xe4, 0x1d, 0x0f, 0x85, 0x1a, 0x8f, 0x32, 0xd6, 0x1c, 0xc0, + 0x4f, 0x62, 0x73, 0x6f, 0x63, 0xdf, 0x8f, 0xcb, 0xce, 0x22, 0x91, 0x2f, 0xf9, 0xcd, 0x00, 0x24, + 0x82, 0x2a, 0xfc, 0xb1, 0x57, 0x61, 0x72, 0xbd, 0xcc, 0x23, 0x73, 0x20, 0xe7, 0x96, 0x53, 0x25, + 0xe6, 0x45, 0x73, 0x65, 0x20, 0x77, 0xe4, 0xb1, 0x6d, 0x65, 0x73, 0xa9, 0xf4, 0x93, 0x73, 0x33, + 0xe2, 0xc0, 0x70, 0x72, 0x6f, 0x62, 0x9f, 0xf9, 0x81, 0xdd, 0xfb, 0x83, 0xf1, 0xfb, 0x84, 0xde, + 0xc9, 0xb7, 0x6c, 0x6c, 0x79, 0xec, 0xde, 0xa1, 0x74, 0x6f, 0x4e, 0xc5, 0x12, 0x61, 0xf5, 0x13, + 0x68, 0xd3, 0x83, 0x58, 0xf8, 0x65, 0x2d, 0x57, 0x6e, 0x6f, 0x2d, 0x64, 0x80, 0x10, 0x64, 0xf5, + 0xb3, 0x63, 0x6c, 0x61, 0x1d, 0xf0, 0x83, 0x32, 0xe2, 0xc3, 0x67, 0x63, 0x63, 0x0a, 0xc4, 0xdf, + 0xb0, 0x5f, 0x43, 0x52, 0xd8, 0xd8, 0x91, 0x45, 0xd1, 0xc6, 0xc0, 0x5f, 0x4e, 0x4f, 0x5f, 0xe8, + 0xc7, 0xd1, 0x4e, 0x49, 0x4e, 0x47, 0x53, 0x18, 0xf7, 0xe9, 0x56, 0x69, 0x73, 0x75, 0x61, 0x6c, + 0x81, 0xd7, 0x98, 0x2e, 0x02, 0xc8, 0x83, 0x84, 0xd9, 0x88, 0x6b, 0xf2, 0xb0, 0x44, 0x45, 0x50, + 0x23, 0xc6, 0x35, 0x41, 0x54, 0x45, 0xbb, 0x81, 0x6b, 0xda, 0xe2, 0x42, 0x4c, 0x4f, 0x43, 0x4b, + 0x2e, 0x69, 0xd5, 0x30, 0x69, 0x66, 0x6e, 0xd2, 0x0f, 0xd5, 0x0d, 0x34, 0x0a, 0x23, 0x20, 0xac, + 0x0f, 0xd7, 0x1c, 0xb5, 0x47, 0x43, 0x43, 0xfc, 0xca, 0x81, 0x20, 0xc8, 0xe0, 0x47, 0x4e, 0x55, + 0x43, 0x5f, 0x5f, 0x73, 0xf5, 0x83, 0xde, 0xca, 0x03, 0xef, 0x83, 0xca, 0xca, 0x31, 0x5f, 0x5f, + 0x29, 0xc4, 0x4d, 0x69, 0x66, 0x20, 0x28, 0xc7, 0xd0, 0x3e, 0x3d, 0x20, 0x34, 0x30, 0x30, 0xd5, + 0xa5, 0x7c, 0x7c, 0xd4, 0xc7, 0x81, 0xd5, 0xc7, 0x44, 0x6c, 0x61, 0x6e, 0x67, 0xcb, 0x0f, 0x64, + 0x04, 0xa4, 0x44, 0x28, 0x44, 0xfe, 0x10, 0x29, 0x97, 0xa3, 0x61, 0x74, 0xbe, 0xc3, 0xd7, 0x65, + 0x5f, 0x5f, 0x28, 0x28, 0x7d, 0xfe, 0x06, 0xdd, 0x12, 0x29, 0xb7, 0x2f, 0x65, 0x6c, 0x80, 0x05, + 0x3f, 0x33, 0x30, 0x31, 0x96, 0x2d, 0x07, 0x9f, 0x07, 0x3b, 0x22, 0x4d, 0x53, 0x9b, 0x0f, 0xa6, + 0x16, 0x81, 0xd1, 0xfd, 0x81, 0xf4, 0xdf, 0x0f, 0x40, 0x01, 0x05, 0xa2, 0x23, 0x73, 0x65, 0xb4, + 0x84, 0x9b, 0xc6, 0x04, 0xe2, 0xa4, 0x28, 0x22, 0x59, 0xfe, 0xb7, 0x3a, 0x20, 0x59, 0x23, 0xc7, + 0x87, 0xd3, 0xf6, 0x0c, 0x8c, 0x83, 0xcb, 0xc8, 0xb4, 0x68, 0x69, 0x73, 0xe8, 0xd4, 0x3f, 0x65, + 0x72, 0x22, 0x5d, 0x12, 0x02, 0x7d, 0x82, 0x57, 0xc6, 0x84, 0x50, 0xc6, 0xbf, 0x20, 0x2f, 0x2a, + 0xd2, 0xfd, 0x0d, 0x93, 0x20, 0x3f, 0xc7, 0x85, 0x2e, 0xfc, 0x8f, 0x8c, 0xd8, 0x04, 0x84, 0x60, + 0xfc, 0x83, 0xf4, 0xf9, 0x07, 0xea, 0x83, 0x50, 0xc0, 0xc5, 0x6c, 0x61, 0x6e, 0x6e, 0x2c, 0xeb, + 0xb5, 0x61, 0x72, 0x74, 0x4f, 0xc6, 0x88, 0x3f, 0xfc, 0x90, 0x62, 0xbd, 0xde, 0xe0, 0x31, 0x33, + 0x31, 0x20, 0x61, 0x70, 0x43, 0xfc, 0xb2, 0x78, 0x69, 0x6d, 0xb9, 0xed, 0x8f, 0x28, 0xec, 0x02, + 0x8b, 0x02, 0xf2, 0x8f, 0x7f, 0xd7, 0x1e, 0x8f, 0x19, 0xd9, 0x02, 0x84, 0x81, 0xcc, 0x82, 0xb4, + 0xe3, 0x8f, 0x2e, 0xd7, 0x28, 0x03, 0xc1, 0x0f, 0x9c, 0x06, 0x81, 0xa7, 0xfb, 0x82, 0xb9, 0xd8, + 0x0b, 0x41, 0x8f, 0xab, 0xd8, 0x26, 0x0f, 0x35, 0x13, 0x07, 0x8b, 0x0f, 0x99, 0x26, 0x0f, 0x1f, + 0x18, 0x87, 0x63, 0xf7, 0x0d, 0x1f, 0x8f, 0xb0, 0xe6, 0x00, 0x86, 0x84, 0xea, 0x0f, 0x0f, 0x3e, + 0x06, 0x7b, 0x0f, 0x89, 0x0c, 0x8f, 0xff, 0xfe, 0x32, 0x8b, 0xf0, 0xfc, 0xaf, 0x64, 0x65, 0xee, + 0xfc, 0x18, 0xd2, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0xe9, 0xfc, 0x89, 0x41, 0xd8, 0x87, 0xd4, 0xfb, + 0x81, 0x2d, 0xf5, 0x83, 0x93, 0xf1, 0xa2, 0x6e, 0x6f, 0xc1, 0xec, 0xc2, 0x65, 0x72, 0x20, 0x62, + 0xeb, 0xe4, 0xb8, 0x64, 0x2e, 0x0a, 0x15, 0xf7, 0x82, 0x33, 0xe6, 0x87, 0xe0, 0xd0, 0x81, 0x05, + 0xf5, 0x86, 0xdb, 0xd0, 0xb2, 0x61, 0x74, 0x69, 0x7d, 0xc4, 0x94, 0x79, 0xb1, 0xbc, 0xc5, 0x6c, + 0x64, 0x65, 0x72, 0xbd, 0xc2, 0x96, 0x73, 0x1f, 0xf2, 0xb7, 0x5a, 0x34, 0x5f, 0xd0, 0xd9, 0x8c, + 0x9d, 0xd3, 0x8f, 0x9d, 0xf7, 0x02, 0x0f, 0xc8, 0x03, 0x00, 0xf5, 0x81, 0xfd, 0xd4, 0x97, 0x6e, + 0xe3, 0xfe, 0x0e, 0xb6, 0x8e, 0xbd, 0xf6, 0x81, 0x6a, 0xd9, 0x8c, 0xea, 0xfe, 0x82, 0xab, 0xdf, + 0x91, 0x6f, 0xb3, 0xeb, 0x85, 0xdf, 0xf3, 0x95, 0x77, 0x62, 0xf8, 0xa3, 0x64, 0x3b, 0x29, 0xd9, + 0x81, 0xfd, 0xfa, 0x82, 0x56, 0xf8, 0x02, 0x06, 0x85, 0xe5, 0xe1, 0xa1, 0x72, 0x65, 0x2a, 0xf8, + 0x82, 0xd3, 0xfa, 0x02, 0xe3, 0xa6, 0x2e, 0x0a, 0xbd, 0xda, 0xa1, 0x68, 0x69, 0x94, 0xd2, 0x89, + 0x54, 0xc8, 0x85, 0xd9, 0xdc, 0x83, 0xe8, 0xf3, 0x83, 0xbc, 0xf7, 0x08, 0x86, 0x83, 0x85, 0xfe, + 0xa1, 0x69, 0x67, 0x66, 0xfb, 0x01, 0xd5, 0x81, 0x8e, 0xef, 0xa2, 0x74, 0x61, 0x0a, 0xc1, 0xb5, + 0x6f, 0x6e, 0x65, 0x31, 0xfe, 0x00, 0xec, 0x8c, 0xc4, 0xfe, 0x8f, 0x8e, 0xf6, 0x15, 0x88, 0xc1, + 0xfd, 0x07, 0xb5, 0x8f, 0xb1, 0xfe, 0x0f, 0x0f, 0xa3, 0x14, 0xaf, 0x69, 0x73, 0x56, 0xfd, 0x06, + 0x8c, 0x43, 0xfa, 0x85, 0x6a, 0xdd, 0x8a, 0x58, 0xf4, 0x92, 0x3b, 0x30, 0xeb, 0x34, 0x6e, 0x65, + 0x77, 0xe3, 0x8b, 0xdc, 0xc1, 0x94, 0x77, 0x35, 0xf2, 0x86, 0xc5, 0xf6, 0x01, 0xad, 0x8c, 0xa3, + 0xf9, 0x11, 0x22, 0xbf, 0x8e, 0xd3, 0xe2, 0x95, 0x29, 0xb3, 0xe0, 0xb2, 0x22, 0x29, 0x20, 0x7e, + 0xfb, 0x07, 0xdd, 0x85, 0x6a, 0xe7, 0x83, 0x98, 0xfc, 0x84, 0x69, 0xe7, 0x1f, 0x3b, 0xa9, 0x20, + 0x82, 0xee, 0xeb, 0x88, 0x2e, 0xd3, 0x03, 0xdd, 0x87, 0x28, 0xd3, 0x0f, 0xac, 0x04, 0x89, 0xcf, + 0xe0, 0x0e, 0xad, 0x03, 0xac, 0x07, 0xdd, 0x08, 0xad, 0x8b, 0xb8, 0xfa, 0x0f, 0x3f, 0x15, 0x8a, + 0x8b, 0xe7, 0x0b, 0x96, 0x23, 0x20, 0x20, 0xbb, 0x03, 0xdd, 0x57, 0x6c, 0x69, 0x64, 0x65, 0x49, + 0xb2, 0x8a, 0x4a, 0xfa, 0xaf, 0x29, 0x3b, 0x3a, 0xfe, 0x05, 0x8f, 0x89, 0xf2, 0x01, 0x8f, 0x60, + 0xfe, 0x05, 0x8f, 0x33, 0xf3, 0x08, 0x0e, 0x67, 0x8f, 0x05, 0xf3, 0x05, 0x82, 0xae, 0xf9, 0xb0, + 0x50, 0x72, 0x65, 0x61, 0xc0, 0xdf, 0x36, 0x34, 0x6b, 0x20, 0x28, 0xc5, 0xe4, 0x06, 0x8f, 0x05, + 0xf3, 0x0e, 0x85, 0x02, 0xd5, 0x0f, 0x5a, 0x0e, 0x8e, 0x23, 0xf3, 0x0e, 0x5a, 0x0f, 0xd2, 0x02, + 0x0f, 0x5a, 0x1c, 0x8e, 0xfc, 0xf0, 0x8f, 0x11, 0xbd, 0x0a, 0x95, 0x7d, 0x1c, 0xbd, 0x0f, 0x00, + 0xee, 0x00, 0x00, +}; + +static unsigned char buf[18830]; + +int main() { + + unsigned long cksum = adler32(0, NULL, 0); + + decompress_lzsa1(compressed, buf); + cksum = adler32(cksum, buf, 18830); + + return cksum == 0xf748269d ? 0 : 1; +} diff --git a/test/val/lzsa2.c b/test/val/lzsa2.c new file mode 100644 index 000000000..5babbb38c --- /dev/null +++ b/test/val/lzsa2.c @@ -0,0 +1,438 @@ +/* + !!DESCRIPTION!! lzsa2 decompression + !!ORIGIN!! cc65 regression tests + !!LICENCE!! BSD 2-clause + !!AUTHOR!! Colin Leroy-Mira +*/ + +#include <zlib.h> +#include <stdio.h> +#include <lzsa.h> + +/* The sample data is the original lz4.h, compressed with: + * lzsa -r -f 1 lz4.h lz4.lzsa1 + * + * We reused lz4.h from the LZ4 test to have a matching adler32 sum. + */ +static const unsigned char compressed[] = { + 0x19, 0xda, 0x2f, 0x2a, 0x0a, 0x20, 0x20, 0x20, 0x4c, 0x5a, 0x34, 0x20, 0x2d, 0x20, 0x46, 0x61, + 0x73, 0x74, 0x5a, 0xf8, 0x04, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x20, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0xd9, 0x1a, 0x48, 0x65, 0x61, + 0x64, 0x65, 0x72, 0x20, 0x46, 0x69, 0x6c, 0x65, 0x81, 0x38, 0x43, 0x6f, 0x70, 0x79, 0x2a, 0x19, + 0x67, 0x68, 0x74, 0x20, 0x28, 0x43, 0x29, 0x20, 0x32, 0x30, 0x31, 0x31, 0x2d, 0xd4, 0x39, 0x35, + 0x2c, 0x20, 0x59, 0x61, 0x6e, 0x6e, 0x13, 0x5a, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x0a, 0xd6, 0x38, + 0x00, 0x42, 0x53, 0x44, 0x39, 0xac, 0x2d, 0x43, 0x6c, 0x61, 0x75, 0x73, 0x65, 0x20, 0x4c, 0x69, + 0x63, 0x65, 0x6e, 0x18, 0x6f, 0x28, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x48, 0x2e, + 0xb6, 0x01, 0x60, 0x38, 0x6f, 0x75, 0x72, 0x3a, 0x00, 0x5c, 0x13, 0x72, 0x67, 0x2f, 0x6c, 0xdd, + 0x1d, 0x73, 0x2f, 0x62, 0x73, 0x64, 0x2d, 0x92, 0x5b, 0x2e, 0x70, 0x68, 0x70, 0x29, 0xb2, 0x5b, + 0x80, 0x52, 0x65, 0x64, 0x69, 0x73, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x5d, 0x5a, 0x6e, 0x64, + 0x20, 0xa8, 0x08, 0x69, 0xa7, 0x44, 0xbd, 0x23, 0x08, 0x62, 0x89, 0x59, 0x61, 0x72, 0x79, 0x20, + 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2c, 0x20, 0x77, 0x3d, 0x08, 0x20, 0xac, 0x23, 0x48, 0x6f, 0xc9, + 0x42, 0xba, 0xf0, 0x6d, 0x6f, 0x48, 0x66, 0x75, 0x4a, 0x61, 0xbc, 0x50, 0x2c, 0x20, 0xd4, 0xf8, + 0x10, 0x65, 0x20, 0x70, 0x65, 0x20, 0x48, 0x74, 0xa2, 0x20, 0xb2, 0x19, 0x72, 0x6f, 0x76, 0x69, + 0x64, 0xb0, 0x30, 0x74, 0x68, 0x01, 0xd2, 0x20, 0x49, 0x66, 0x32, 0x50, 0x6f, 0x77, 0x9d, 0x69, + 0x67, 0xe2, 0x48, 0x6e, 0x81, 0x42, 0xcb, 0xea, 0x73, 0x43, 0xb6, 0x40, 0x19, 0xe9, 0x3a, 0x04, + 0xf6, 0x4f, 0x2a, 0x61, 0x48, 0x73, 0x89, 0x4e, 0x66, 0x68, 0x48, 0x63, 0x8d, 0x00, 0xd2, 0x71, + 0x6d, 0x75, 0x9a, 0x68, 0x72, 0xe6, 0x48, 0x61, 0x50, 0x43, 0xa6, 0x50, 0x61, 0x62, 0x94, 0x01, + 0x65, 0xb4, 0x43, 0x65, 0x79, 0x00, 0x6e, 0x6f, 0x74, 0xdc, 0x09, 0x2c, 0x48, 0x69, 0xc1, 0x49, + 0x6c, 0x14, 0x41, 0xbc, 0x47, 0x88, 0x4b, 0x20, 0x47, 0x6b, 0x6f, 0x61, 0xef, 0x50, 0x63, 0x6c, + 0xae, 0x48, 0x6d, 0x3e, 0x4f, 0x2e, 0x77, 0x02, 0x77, 0x69, 0x6e, 0xea, 0x3f, 0x46, 0x77, 0x41, + 0x14, 0x51, 0x64, 0x75, 0x63, 0x46, 0x74, 0x63, 0x25, 0xee, 0x63, 0x47, 0x74, 0x25, 0x62, 0x99, + 0x45, 0x1a, 0x38, 0x17, 0x64, 0x6f, 0x63, 0x75, 0x48, 0x6e, 0x0a, 0x66, 0x4d, 0x69, 0x2f, 0x72, + 0x29, 0x6f, 0x3d, 0x20, 0x28, 0x6d, 0x6d, 0x20, 0x7f, 0x10, 0x69, 0x61, 0x6c, 0x73, 0x89, 0x64, + 0x51, 0x00, 0x13, 0x43, 0x75, 0x67, 0x14, 0x84, 0xfb, 0xb2, 0x39, 0x54, 0x48, 0x49, 0x53, 0x20, + 0x53, 0x4f, 0x46, 0x54, 0x57, 0x41, 0x52, 0x45, 0x20, 0xa8, 0x19, 0x50, 0x52, 0x4f, 0x56, 0x49, + 0x44, 0x45, 0x44, 0x20, 0x42, 0x59, 0x16, 0x00, 0x38, 0xa4, 0x43, 0x4f, 0x50, 0x59, 0x52, 0x49, + 0x47, 0x48, 0x54, 0x20, 0x48, 0x4f, 0x4c, 0x39, 0x35, 0x52, 0x53, 0x20, 0x41, 0x4e, 0x44, 0xf0, + 0x4e, 0x54, 0x18, 0x17, 0x42, 0x55, 0x54, 0x4f, 0x42, 0xb9, 0x59, 0x02, 0x22, 0x41, 0x53, 0xc3, + 0x2a, 0x22, 0x21, 0x06, 0x40, 0xc6, 0x50, 0x45, 0x58, 0xb8, 0x30, 0x45, 0x53, 0x00, 0x04, 0x00, + 0x59, 0x12, 0x4d, 0x50, 0x4c, 0x49, 0xb0, 0x41, 0x9f, 0x08, 0x52, 0x10, 0x54, 0x49, 0x46, 0x28, + 0x2c, 0x38, 0x2d, 0x4e, 0x43, 0x4c, 0x55, 0x44, 0x08, 0x47, 0xa1, 0x41, 0xbc, 0xbb, 0x20, 0x4e, + 0x4f, 0x54, 0xfa, 0xc6, 0x40, 0xd5, 0x51, 0x49, 0x54, 0xd6, 0x40, 0xab, 0x4a, 0x2c, 0x85, 0x47, + 0xc6, 0x48, 0x20, 0x57, 0x50, 0x20, 0x4d, 0x7c, 0x11, 0x43, 0x48, 0x80, 0x38, 0x41, 0x42, 0x49, + 0x1b, 0x53, 0x54, 0x59, 0x71, 0x08, 0x46, 0x4a, 0x4e, 0x91, 0x48, 0x46, 0x71, 0x42, 0x2b, 0xf8, + 0x7a, 0x41, 0x20, 0x50, 0x41, 0x52, 0x54, 0x49, 0x43, 0x55, 0x4c, 0x00, 0x5a, 0x46, 0x55, 0x52, + 0x50, 0x4f, 0x53, 0x45, 0x20, 0x20, 0x48, 0x44, 0x5c, 0x28, 0x43, 0x50, 0x49, 0x4d, 0x21, 0x49, + 0x2e, 0x7c, 0x41, 0x88, 0x40, 0x57, 0x50, 0x56, 0x45, 0xb5, 0x50, 0x20, 0x53, 0xaf, 0x57, 0x4c, + 0x4c, 0x11, 0x44, 0x43, 0x6f, 0x50, 0x4f, 0x57, 0xaa, 0x49, 0x52, 0x3c, 0x47, 0x11, 0x60, 0xe7, + 0x49, 0x45, 0x56, 0x40, 0x82, 0x52, 0x4c, 0x45, 0x91, 0x43, 0x12, 0x50, 0x44, 0x49, 0x13, 0x52, + 0x43, 0x54, 0x29, 0x27, 0xb1, 0x69, 0x43, 0xbb, 0x41, 0x5b, 0x72, 0x4c, 0x2c, 0x9a, 0x30, 0x53, + 0x50, 0x0a, 0x29, 0x49, 0x61, 0xe8, 0x68, 0x45, 0xf1, 0x41, 0x6a, 0x55, 0x59, 0x2c, 0xa9, 0x40, + 0x67, 0x51, 0x51, 0x55, 0x7e, 0x01, 0x13, 0x59, 0x20, 0x44, 0x41, 0x4d, 0x41, 0x47, 0x1c, 0x6f, + 0x28, 0xe2, 0xf2, 0x09, 0x62, 0x5b, 0x50, 0x43, 0x55, 0x8c, 0x49, 0x4d, 0x44, 0x62, 0xed, 0x18, + 0x53, 0x55, 0x42, 0x53, 0x54, 0x10, 0x08, 0x55, 0x7b, 0x26, 0x20, 0x47, 0x4f, 0x4f, 0x44, 0x89, + 0x68, 0x53, 0x55, 0x68, 0x56, 0xfe, 0x60, 0x93, 0x48, 0x3b, 0x53, 0x73, 0x4f, 0x53, 0xc7, 0x08, + 0x55, 0x43, 0x73, 0x70, 0x44, 0x41, 0xc4, 0x42, 0x81, 0x62, 0x15, 0x61, 0xc7, 0x32, 0x53, 0x3b, + 0xa1, 0x28, 0x42, 0x6b, 0x49, 0xbd, 0x20, 0xd4, 0x70, 0x54, 0x45, 0x57, 0xf0, 0x55, 0x50, 0x40, + 0x63, 0x69, 0x29, 0x0c, 0x69, 0x57, 0xde, 0x61, 0xff, 0x49, 0x41, 0xc2, 0x6b, 0x44, 0x05, 0x00, + 0x62, 0x1e, 0x84, 0xf4, 0xc4, 0x50, 0x45, 0x4f, 0x36, 0x62, 0x6c, 0x75, 0x4c, 0x49, 0x72, 0x60, + 0x47, 0xe8, 0x57, 0x20, 0x61, 0xca, 0xf4, 0x49, 0x4e, 0x6a, 0x41, 0xe7, 0x28, 0x53, 0xc0, 0xe8, + 0x49, 0x47, 0xd9, 0x44, 0x02, 0x61, 0xb3, 0x62, 0x09, 0x47, 0x10, 0x20, 0x68, 0x20, 0x3b, 0x08, + 0x47, 0x30, 0x47, 0x45, 0x8c, 0x4a, 0x45, 0x3d, 0x4a, 0x4f, 0xb7, 0x70, 0x57, 0x49, 0x44, 0x69, + 0x29, 0x43, 0x00, 0x42, 0xdf, 0x4b, 0x49, 0x83, 0x81, 0xff, 0xb4, 0x41, 0x89, 0x4a, 0x55, 0x04, + 0x62, 0x4b, 0x42, 0x26, 0x63, 0x51, 0x8f, 0x46, 0x31, 0x5e, 0x6b, 0x2c, 0x22, 0x40, 0xcc, 0xe8, + 0x46, 0x51, 0x44, 0x56, 0xb9, 0x4e, 0x44, 0xd0, 0x69, 0x50, 0xee, 0x4c, 0x49, 0x4f, 0x64, 0xc1, + 0x75, 0x43, 0x48, 0x82, 0xa4, 0x9d, 0x9a, 0x4e, 0x59, 0x6f, 0x75, 0x20, 0x63, 0x61, 0x6e, 0x61, + 0x80, 0xed, 0xa5, 0xf4, 0x63, 0x74, 0x28, 0x75, 0xa1, 0xfe, 0x99, 0x81, 0x3d, 0x4a, 0x3a, 0x94, + 0xaa, 0x2d, 0xee, 0x18, 0xa6, 0xdc, 0xf9, 0x5e, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, + 0xb2, 0x3a, 0x20, 0x76, 0xa9, 0x73, 0xee, 0x75, 0xa9, 0x67, 0xe1, 0x99, 0x0d, 0x75, 0x62, 0x2e, + 0xff, 0x5f, 0xa1, 0x2f, 0x43, 0x79, 0x61, 0x6e, 0x34, 0x39, 0x37, 0x33, 0x2f, 0x6c, 0x7a, 0x34, + 0xc3, 0x28, 0x70, 0x1e, 0xa1, 0x64, 0xa2, 0xe3, 0xaa, 0x57, 0x75, 0x6d, 0xc8, 0x48, 0x72, 0xab, + 0x00, 0xb4, 0x5b, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0xc1, 0x43, 0xde, 0x3b, 0x0c, 0x2f, + 0x23, 0x21, 0x42, 0xbc, 0xb8, 0xae, 0x63, 0x0a, 0x2a, 0x2f, 0x0a, 0x23, 0x70, 0x72, 0x61, 0x67, + 0x6d, 0x61, 0x20, 0x8e, 0xf9, 0xa5, 0x63, 0x65, 0x0a, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x64, 0x65, + 0x66, 0x69, 0x6e, 0x3a, 0x28, 0x5f, 0x5f, 0x63, 0x70, 0x6c, 0x75, 0x73, 0xe1, 0x99, 0x29, 0x0a, + 0x65, 0x78, 0xe8, 0xed, 0xb9, 0x6e, 0x20, 0x22, 0x43, 0x22, 0x20, 0x7b, 0x0a, 0x23, 0x65, 0x6e, + 0xed, 0x56, 0x92, 0x0a, 0x0a, 0x55, 0x51, 0x2a, 0x20, 0xb5, 0xb6, 0x2e, 0x68, 0xe4, 0x5f, 0x9f, + 0x73, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0xd4, 0x4e, 0x50, 0x66, 0x75, 0xa3, 0xa3, 0xed, 0x61, + 0x8b, 0x2c, 0xf8, 0x40, 0x2d, 0x08, 0x76, 0x35, 0x01, 0x7b, 0x60, 0x6c, 0x6c, 0x20, 0x62, 0x75, + 0x66, 0x66, 0x65, 0x72, 0xde, 0xf8, 0x72, 0x6f, 0x6c, 0x4a, 0x6f, 0xb9, 0x48, 0x67, 0x6e, 0xac, + 0x6d, 0xe1, 0xc9, 0x68, 0x2a, 0x8a, 0x40, 0x70, 0x69, 0x79, 0xb9, 0x40, 0x70, 0x32, 0x65, 0x64, + 0x30, 0x67, 0x65, 0xb1, 0x20, 0x8a, 0x74, 0xd0, 0xa6, 0x41, 0x71, 0x39, 0x2d, 0x6f, 0x70, 0x90, + 0x40, 0x01, 0x4f, 0x65, 0x92, 0x41, 0x49, 0x08, 0x64, 0x09, 0x48, 0x61, 0x44, 0x01, 0x20, 0x26, + 0x41, 0x8b, 0x73, 0x6e, 0x67, 0x9e, 0x49, 0x66, 0xad, 0x12, 0x65, 0x20, 0x87, 0xd0, 0x9f, 0x51, + 0x29, 0x2c, 0xa0, 0x42, 0x70, 0x63, 0x5c, 0x74, 0x6c, 0x65, 0x60, 0x60, 0xb5, 0x92, 0x62, 0x72, + 0xd3, 0x5b, 0x09, 0x68, 0x41, 0xa8, 0x91, 0x69, 0x74, 0xdd, 0xd3, 0x91, 0x77, 0x6e, 0xb0, 0x69, + 0x6d, 0x69, 0x48, 0x2c, 0x62, 0x20, 0x6d, 0x48, 0x61, 0x9b, 0x82, 0x24, 0x42, 0x0a, 0x43, 0xaa, + 0x51, 0x2e, 0x68, 0x73, 0xb1, 0x73, 0x74, 0xdf, 0x78, 0x69, 0x2e, 0xb0, 0x62, 0xec, 0x07, 0xff, + 0x0d, 0x70, 0x0a, 0x2a, 0x01, 0x48, 0x56, 0x42, 0x62, 0xe0, 0x4f, 0x0a, 0xce, 0x0d, 0x62, 0x54, + 0x64, 0x65, 0x42, 0x2e, 0xb1, 0x5f, 0x56, 0xef, 0xbc, 0xa1, 0xa4, 0x99, 0x1f, 0x5f, 0x4d, 0x41, + 0x4a, 0x1c, 0x01, 0xfd, 0x0a, 0x31, 0xb2, 0x2f, 0x2a, 0xdd, 0xa1, 0x50, 0x20, 0x62, 0x01, 0x51, + 0x61, 0x6b, 0x04, 0x64, 0xd9, 0x92, 0x66, 0x61, 0x17, 0x41, 0x2e, 0x69, 0x67, 0x62, 0x4f, 0x20, + 0xb8, 0xf2, 0x00, 0xf4, 0x49, 0x4e, 0xef, 0x37, 0x80, 0xf3, 0xf9, 0xe8, 0x77, 0x68, 0x6e, 0xd4, + 0x4e, 0x2d, 0xaf, 0x4f, 0x29, 0xae, 0x70, 0x61, 0x70, 0x84, 0xb1, 0x69, 0x6c, 0xde, 0x87, 0x61, + 0x0b, 0x47, 0x62, 0xff, 0x45, 0x52, 0x45, 0x4c, 0x45, 0x41, 0x53, 0x45, 0xf1, 0x74, 0x77, 0x71, + 0x73, 0x2c, 0x03, 0x50, 0x67, 0x2d, 0xce, 0x11, 0x78, 0x65, 0xa5, 0x21, 0x9a, 0x4d, 0x64, 0x65, + 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x6e, 0x47, 0xb2, 0xf1, 0x00, 0x99, 0x4e, 0x55, 0x4d, 0x42, 0xe9, + 0xba, 0x4f, 0x28, 0x00, 0x3a, 0x1e, 0x2a, 0x31, 0x30, 0x30, 0x57, 0x20, 0x2b, 0x2b, 0xa3, 0x04, + 0x47, 0x68, 0xb3, 0x51, 0x29, 0x0a, 0x31, 0x03, 0x6c, 0x76, 0x75, 0x7f, 0xbf, 0x4e, 0x75, 0x6d, + 0x62, 0x65, 0x72, 0x20, 0x28, 0x76, 0x6f, 0x69, 0x64, 0x29, 0x3b, 0x33, 0x15, 0x7a, 0x0f, 0x54, + 0x75, 0x6e, 0x8e, 0x92, 0x70, 0x61, 0xeb, 0x61, 0x8a, 0x67, 0x2a, 0xff, 0x10, 0xa4, 0xbb, 0x63, + 0x2c, 0xb8, 0x0e, 0x4d, 0x45, 0x4d, 0xbc, 0xf0, 0x59, 0x5f, 0xa1, 0xe5, 0x3f, 0x32, 0x20, 0x3a, + 0x8b, 0x4d, 0xfc, 0x88, 0xa1, 0xb4, 0x68, 0x61, 0x44, 0xa3, 0xcd, 0xc7, 0x79, 0x75, 0x6c, 0x61, + 0x20, 0x3a, 0x20, 0x4e, 0x2d, 0x3e, 0x32, 0x5e, 0x4e, 0x20, 0x42, 0x79, 0x74, 0x2e, 0x99, 0x2f, + 0x28, 0x65, 0x78, 0x61, 0x6d, 0x67, 0x09, 0x73, 0x32, 0x68, 0x31, 0xfe, 0x00, 0x20, 0xd0, 0x18, + 0x4b, 0x42, 0x3b, 0xaa, 0xea, 0x32, 0xe8, 0x34, 0x29, 0x20, 0xea, 0x36, 0x32, 0x36, 0x34, 0x42, + 0x4c, 0x32, 0xdd, 0xe9, 0x4d, 0xbb, 0x65, 0x74, 0x63, 0x2e, 0x29, 0xff, 0x9a, 0x91, 0x6e, 0x63, + 0xce, 0xea, 0x73, 0x4f, 0x6d, 0x8e, 0x3c, 0xb2, 0x69, 0x6d, 0x89, 0x97, 0x65, 0x73, 0xb4, 0x80, + 0xaa, 0x72, 0xcc, 0x60, 0xab, 0x0a, 0xaa, 0x82, 0xc4, 0x5a, 0x4f, 0x64, 0xcd, 0xa2, 0xff, 0xb3, + 0x45, 0xc9, 0xa3, 0x90, 0x00, 0x0c, 0x60, 0x18, 0x91, 0x64, 0x75, 0x35, 0x29, 0x6f, 0x3c, 0x89, + 0x63, 0x8a, 0xa9, 0x65, 0xfa, 0x0f, 0x52, 0x63, 0x74, 0x24, 0x08, 0x44, 0x59, 0x5c, 0x61, 0x75, + 0x6c, 0x74, 0x20, 0x76, 0x61, 0x6c, 0xdf, 0xa1, 0xa9, 0x5a, 0x0b, 0x31, 0x34, 0x2c, 0x1c, 0x41, + 0x52, 0x91, 0x4b, 0x42, 0xe0, 0x50, 0x68, 0x69, 0xd0, 0x91, 0x20, 0x6e, 0xbb, 0x66, 0x89, 0x6c, + 0xcc, 0xa2, 0xf7, 0x77, 0x61, 0x54, 0x49, 0x6f, 0x55, 0x08, 0x74, 0x5c, 0x4f, 0x20, 0x78, 0x38, + 0x36, 0x20, 0x4c, 0x31, 0xad, 0x8f, 0x0a, 0x2d, 0x28, 0x67, 0xb9, 0x41, 0xab, 0x6f, 0x0a, 0x39, + 0xfe, 0x15, 0x72, 0x53, 0x69, 0xb4, 0x96, 0x20, 0x46, 0x4d, 0x67, 0x39, 0xff, 0x11, 0x87, 0xb5, + 0x0f, 0x66, 0xd4, 0x54, 0x5f, 0x64, 0x1f, 0x51, 0x28, 0x63, 0xb9, 0xaa, 0x74, 0x7f, 0xb5, 0x72, + 0x2a, 0xb9, 0xf1, 0x2d, 0x2c, 0xa1, 0xe4, 0xd1, 0x52, 0x74, 0x2c, 0x28, 0x25, 0x40, 0x83, 0x34, + 0x7a, 0x65, 0x81, 0x19, 0x6d, 0x61, 0x78, 0x44, 0x17, 0x02, 0x56, 0x29, 0x3b, 0xa5, 0x41, 0xae, + 0x47, 0xa3, 0x0f, 0x60, 0x93, 0xe8, 0x66, 0x47, 0xa5, 0x0c, 0xa7, 0xe2, 0xe4, 0x47, 0xa1, 0x63, + 0x07, 0x5f, 0x84, 0x11, 0x4f, 0x0a, 0x3e, 0xcd, 0x8c, 0x29, 0x52, 0x56, 0x20, 0x43, 0xce, 0x60, + 0x53, 0x4f, 0x27, 0x4e, 0x10, 0x9b, 0x27, 0x20, 0x62, 0xf0, 0x77, 0x10, 0x66, 0x72, 0xa5, 0xe0, + 0x2e, 0x06, 0x4a, 0x27, 0xc9, 0x64, 0x47, 0x91, 0x61, 0x6c, 0xec, 0x6b, 0x31, 0x64, 0x79, 0x81, + 0xdb, 0xe0, 0xaa, 0x61, 0x2d, 0x4a, 0x27, 0x01, 0x4d, 0x27, 0xce, 0xa3, 0xb5, 0x6c, 0x41, 0x01, + 0x20, 0x47, 0x05, 0x2a, 0x56, 0x27, 0x2e, 0x89, 0x86, 0x02, 0x82, 0xff, 0xd8, 0xb1, 0x67, 0x75, + 0x9c, 0x95, 0x6e, 0x74, 0xd1, 0xed, 0x1a, 0x73, 0x75, 0x63, 0x63, 0xa5, 0x57, 0x69, 0x66, 0xc5, + 0x7f, 0x04, 0x20, 0x3e, 0x3d, 0x71, 0x70, 0x42, 0x6f, 0x2d, 0x77, 0x64, 0x28, 0x98, 0x15, 0x4c, + 0x29, 0xa4, 0x51, 0x49, 0x74, 0x6e, 0x20, 0x30, 0x20, 0x72, 0x1f, 0x41, 0x3e, 0xb1, 0x61, 0x73, + 0x46, 0x69, 0x2c, 0xb5, 0xa1, 0xea, 0x06, 0x89, 0x27, 0xb2, 0x00, 0x4b, 0x62, 0x8e, 0xa1, 0xda, + 0x82, 0xad, 0x86, 0x60, 0xd7, 0x8a, 0x74, 0xaf, 0x45, 0xc8, 0x8b, 0x66, 0xad, 0x7b, 0x86, 0x25, + 0xa2, 0xd1, 0x17, 0x77, 0x6e, 0x6f, 0x93, 0x67, 0xff, 0x0d, 0x45, 0x03, 0x89, 0x20, 0xc0, 0x82, + 0xd4, 0xa2, 0x57, 0x6d, 0x69, 0x06, 0x50, 0x64, 0x67, 0xac, 0xa3, 0xcf, 0xb2, 0xa7, 0x9e, 0x42, + 0x20, 0xb1, 0x6f, 0x70, 0xe0, 0xad, 0x49, 0x69, 0x87, 0x71, 0x64, 0x69, 0xd5, 0xbb, 0x6c, 0x79, + 0x2a, 0xd5, 0xc6, 0x47, 0x8e, 0x41, 0xd2, 0x61, 0x64, 0x62, 0xef, 0x70, 0x7a, 0x65, 0x86, 0x44, + 0x6a, 0x4a, 0x41, 0x4c, 0x82, 0xf2, 0x90, 0x99, 0x65, 0x71, 0x75, 0x65, 0x6e, 0xfc, 0x97, 0x65, + 0x9b, 0x83, 0x7c, 0x00, 0x7f, 0x43, 0xd2, 0xb3, 0x6e, 0x6f, 0x8c, 0x74, 0x69, 0x64, 0xa3, 0x89, + 0x54, 0xa1, 0x2e, 0x47, 0xab, 0xa9, 0x6e, 0xea, 0x7a, 0xa1, 0x6c, 0x72, 0x72, 0x69, 0x2f, 0xa1, + 0xaa, 0x69, 0xa9, 0x73, 0x8c, 0x67, 0x57, 0x5f, 0xa0, 0x5c, 0xe9, 0x6e, 0x62, 0x34, 0x26, 0x0a, + 0x46, 0x14, 0x44, 0xde, 0x87, 0x10, 0x2f, 0x87, 0x2a, 0x2e, 0x89, 0x20, 0x4a, 0x68, 0x4d, 0x62, + 0x61, 0x52, 0x70, 0x70, 0x70, 0xf6, 0x62, 0x02, 0xa6, 0xfe, 0x09, 0x84, 0x06, 0x5e, 0xbf, 0x41, + 0x58, 0x5f, 0x49, 0x4e, 0x50, 0x55, 0x54, 0x5f, 0x56, 0x41, 0x4c, 0x55, 0x45, 0xbf, 0xa7, 0xf9, + 0x3c, 0x94, 0x20, 0x3a, 0xcd, 0xb2, 0x6f, 0x72, 0xea, 0x9c, 0x89, 0x74, 0x62, 0x84, 0xfe, 0xd0, + 0x55, 0x6f, 0x66, 0x67, 0x46, 0x15, 0x8b, 0x28, 0xc6, 0xa4, 0xaf, 0x2b, 0x97, 0x62, 0x65, 0x88, + 0x9a, 0x4e, 0x29, 0xa2, 0xa2, 0x0d, 0xf0, 0x75, 0x72, 0x6b, 0x3a, 0xa8, 0xac, 0x6e, 0xef, 0x05, + 0x94, 0x6f, 0x66, 0x32, 0x63, 0xfc, 0x61, 0xcd, 0x63, 0x40, 0x47, 0x9e, 0x7e, 0x60, 0xda, 0x48, + 0x63, 0xde, 0xf0, 0x73, 0x61, 0x81, 0x65, 0x52, 0x3c, 0x3d, 0x5a, 0x68, 0x4f, 0xd5, 0x53, 0x70, + 0x75, 0x58, 0x46, 0xa1, 0x06, 0xff, 0x61, 0xd3, 0x89, 0x30, 0x63, 0xa7, 0xc5, 0xef, 0xbf, 0x3f, + 0x61, 0x69, 0x6c, 0x73, 0x0a, 0x0a, 0x2d, 0xaf, 0xa6, 0x95, 0xa7, 0xf6, 0x4a, 0x00, 0x55, 0x61, + 0x18, 0x43, 0x59, 0x01, 0x30, 0x63, 0x69, 0x53, 0x63, 0xf4, 0x66, 0xff, 0x02, 0x47, 0xcf, 0x2c, + 0xa4, 0x7e, 0x61, 0x22, 0x66, 0xbf, 0x47, 0xb4, 0xee, 0x46, 0xc1, 0x83, 0x79, 0x94, 0x69, 0x6e, + 0x9f, 0xf8, 0x66, 0x1b, 0x67, 0xbb, 0x07, 0x77, 0x2e, 0x0a, 0xbf, 0xf2, 0x09, 0x4f, 0x64, 0x88, + 0x63, 0xba, 0x47, 0x98, 0xa7, 0x67, 0xb5, 0x47, 0x50, 0x7a, 0x67, 0xaf, 0x4f, 0x49, 0x50, 0xbf, + 0x86, 0x28, 0x68, 0x6c, 0x6c, 0x89, 0x67, 0xda, 0x9c, 0x00, 0x5b, 0x1d, 0x75, 0x67, 0x68, 0x2c, + 0x7e, 0xaa, 0x64, 0x6b, 0x70, 0x77, 0x69, 0xd3, 0xa3, 0xf8, 0xaa, 0x83, 0x78, 0x6b, 0x6f, 0x54, + 0x01, 0xa8, 0x48, 0x20, 0x7f, 0x68, 0x72, 0x61, 0x84, 0xfc, 0x5f, 0x27, 0x28, 0x3c, 0x30, 0x29, + 0x2e, 0x96, 0x62, 0xda, 0xa6, 0xbd, 0x62, 0x71, 0x73, 0x74, 0xfd, 0x6a, 0x6d, 0xc5, 0xa9, 0x64, + 0x21, 0x8a, 0x63, 0xf8, 0x3d, 0xe8, 0x6d, 0x82, 0x33, 0x81, 0xdf, 0x1c, 0xa7, 0x66, 0x42, 0x47, + 0x91, 0x46, 0x7e, 0x42, 0x88, 0x66, 0xd2, 0xa9, 0x61, 0xff, 0x9b, 0x49, 0x67, 0x43, 0xb5, 0x76, + 0x65, 0x3d, 0x47, 0x89, 0x2f, 0xa7, 0x6f, 0x99, 0xab, 0x69, 0x7c, 0x45, 0x8f, 0x48, 0x61, 0xc8, + 0x82, 0xbe, 0x94, 0xa7, 0xcd, 0x0c, 0xa9, 0x76, 0x23, 0xa1, 0x8c, 0xfc, 0x40, 0x38, 0x10, 0x78, + 0x70, 0x81, 0xbd, 0x54, 0x82, 0xb4, 0x5a, 0x0b, 0x63, 0x6c, 0x75, 0x0c, 0x42, 0x63, 0x50, 0x69, + 0x63, 0xbe, 0xb4, 0x75, 0x73, 0xee, 0x5f, 0x47, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x1d, + 0xaf, 0x74, 0xfc, 0x0e, 0x65, 0xdd, 0xa7, 0xf8, 0x0e, 0x47, 0xdf, 0x10, 0x37, 0x69, 0x6e, 0x1b, + 0x82, 0x08, 0xa7, 0xdf, 0x9b, 0x16, 0x99, 0x2c, 0x41, 0x64, 0x76, 0x61, 0x6e, 0xc5, 0xa7, 0xdf, + 0x99, 0x1a, 0xa6, 0xde, 0x17, 0x87, 0xca, 0x61, 0x5e, 0x53, 0x49, 0x5a, 0x45, 0x2c, 0x1b, 0x2f, + 0x30, 0x78, 0x37, 0x45, 0x30, 0xa4, 0xbc, 0xef, 0xa0, 0xf8, 0xf8, 0x6d, 0x31, 0x31, 0x33, 0x20, + 0x39, 0x32, 0x39, 0x20, 0x32, 0x85, 0x55, 0x47, 0xb7, 0x60, 0x9b, 0x43, 0x4f, 0x4d, 0x85, 0x97, + 0xba, 0x42, 0x4f, 0x55, 0x4e, 0x44, 0x28, 0x69, 0x73, 0xf9, 0x39, 0x81, 0xae, 0x89, 0x28, 0xda, + 0xec, 0xb1, 0x69, 0x67, 0xf4, 0x2e, 0x29, 0x61, 0xf7, 0x3e, 0x20, 0x47, 0x7e, 0xaf, 0xa9, 0x3f, + 0x15, 0x56, 0x3a, 0x20, 0xd3, 0xe8, 0x2b, 0x05, 0xa1, 0x3a, 0x2f, 0x32, 0x35, 0x35, 0x8d, 0x40, + 0x80, 0xab, 0x29, 0x9b, 0x87, 0xd9, 0x68, 0xa6, 0xda, 0x9e, 0xac, 0x50, 0xba, 0xa3, 0xfc, 0x14, + 0x82, 0xfb, 0xb1, 0x69, 0x6d, 0xa7, 0x2a, 0x42, 0xbc, 0x83, 0x08, 0x42, 0x53, 0x87, 0x63, 0xf4, + 0x41, 0xde, 0x6e, 0x79, 0x54, 0x8a, 0x69, 0xfc, 0xa6, 0x70, 0x22, 0x77, 0x56, 0x82, 0x94, 0xf8, + 0x17, 0x61, 0x73, 0x65, 0x22, 0xa1, 0x2b, 0x81, 0xef, 0x5c, 0x48, 0x6f, 0x6f, 0x63, 0x52, 0x84, + 0xf8, 0x87, 0xd3, 0x5b, 0xa9, 0x69, 0xaf, 0xc5, 0x97, 0x29, 0x0a, 0x8c, 0xee, 0x93, 0x69, 0x6d, + 0x20, 0xa2, 0x7a, 0x38, 0xa1, 0x55, 0x82, 0xab, 0xf1, 0x84, 0xe4, 0xa6, 0xd9, 0x3e, 0x43, 0xd6, + 0x40, 0xa9, 0x89, 0x72, 0x4c, 0x82, 0xbf, 0x7b, 0xa7, 0x6c, 0xad, 0xaf, 0x73, 0x9a, 0x12, 0x7f, + 0x4d, 0x61, 0x63, 0x72, 0x6f, 0x95, 0xad, 0x49, 0x29, 0x94, 0xa4, 0x80, 0xa6, 0x7d, 0x20, 0x42, + 0x97, 0x43, 0x5f, 0x53, 0x69, 0x6c, 0xb3, 0x08, 0x2d, 0xa9, 0x6d, 0xfb, 0x2e, 0x82, 0xdb, 0x44, + 0xa3, 0xb1, 0x28, 0x73, 0x99, 0xb3, 0x4f, 0x6b, 0x74, 0x43, 0xc7, 0xa4, 0xbc, 0xf5, 0x46, 0x8a, + 0x48, 0x4e, 0x14, 0x65, 0xc8, 0x87, 0x2a, 0xe6, 0x05, 0xa6, 0xdb, 0x07, 0x81, 0x8c, 0xb3, 0x65, + 0x6e, 0xc3, 0x6b, 0x47, 0x3d, 0x61, 0xf1, 0x87, 0xce, 0xb4, 0xb7, 0x72, 0x63, 0xe5, 0x0f, 0x63, + 0x99, 0x86, 0xdc, 0x12, 0xef, 0x6d, 0x87, 0xfa, 0x48, 0x87, 0xd8, 0x71, 0x66, 0x08, 0x65, 0x26, + 0x43, 0x7b, 0x67, 0x21, 0xfd, 0x03, 0x87, 0x90, 0xac, 0xaa, 0x2c, 0xf2, 0x64, 0x0b, 0x46, 0x3e, + 0xbd, 0x0e, 0x74, 0x6f, 0x6f, 0xe2, 0x48, 0x28, 0x32, 0x47, 0x7a, 0xa8, 0x89, 0x29, 0xf3, 0x82, + 0xba, 0x3d, 0x47, 0x18, 0x21, 0x5b, 0x45, 0x25, 0x87, 0xd9, 0xeb, 0x61, 0xc2, 0x87, 0xdc, 0x19, + 0xaa, 0x53, 0x96, 0x00, 0x7e, 0x67, 0x8d, 0xa2, 0xac, 0x0a, 0x6b, 0x74, 0x4a, 0xb3, 0x77, 0x73, + 0x42, 0x68, 0x65, 0x52, 0xab, 0x63, 0xe9, 0x74, 0x68, 0x22, 0x2a, 0x09, 0x63, 0x6b, 0x72, 0x33, + 0x69, 0x22, 0x6e, 0xa9, 0x63, 0x8c, 0xf6, 0x86, 0x5d, 0x4c, 0x65, 0x44, 0xab, 0x72, 0x83, 0xbc, + 0x47, 0xd7, 0x64, 0xa7, 0xa3, 0xea, 0x80, 0x65, 0x3b, 0x02, 0xa7, 0x51, 0xdc, 0x46, 0x90, 0xb4, + 0x73, 0x6f, 0x95, 0x21, 0x41, 0x51, 0x44, 0xba, 0xa7, 0xad, 0x3b, 0x85, 0xf6, 0xa4, 0xc5, 0x0e, + 0xb1, 0x74, 0x72, 0xae, 0x70, 0x2d, 0x6f, 0x01, 0x29, 0x2e, 0x78, 0xa2, 0x40, 0x82, 0xc9, 0xbb, + 0xa3, 0x89, 0x91, 0x74, 0x75, 0xe5, 0xf5, 0x85, 0x47, 0x89, 0x65, 0xab, 0x33, 0x83, 0x85, 0x42, + 0xbe, 0x74, 0x76, 0x65, 0x20, 0x84, 0x5d, 0x5b, 0x83, 0x8e, 0x8a, 0x72, 0xdc, 0x7e, 0x81, 0xe2, + 0x79, 0x1a, 0x2b, 0x7e, 0x33, 0x25, 0x84, 0xa4, 0xf5, 0xa5, 0xc9, 0x5c, 0x4f, 0x6e, 0x3e, 0x83, + 0xc0, 0x39, 0xbf, 0x22, 0x31, 0x22, 0xd0, 0x66, 0x65, 0xbb, 0x40, 0x5f, 0x51, 0x67, 0x75, 0x06, + 0x67, 0xb3, 0xec, 0x63, 0xe3, 0x6a, 0x56, 0xff, 0x8a, 0x73, 0x79, 0x8c, 0x30, 0xd7, 0x18, 0x8b, + 0x62, 0xb6, 0xb2, 0x6c, 0x61, 0xe9, 0xa0, 0x50, 0x62, 0x79, 0x91, 0xb1, 0x43, 0x43, 0x7b, 0xb2, + 0x52, 0x41, 0x75, 0x76, 0xb9, 0x5f, 0x44, 0x45, 0x46, 0x41, 0x55, 0x4c, 0x54, 0xf8, 0xbb, 0xb3, + 0x65, 0x65, 0x48, 0xb5, 0x63, 0x29, 0xaa, 0x96, 0xa3, 0x7f, 0x8e, 0x2e, 0xf8, 0xfb, 0x67, 0x21, + 0x8f, 0x20, 0xaf, 0x3a, 0x25, 0x06, 0x74, 0x67, 0x45, 0xa1, 0x9f, 0xa0, 0x87, 0xb5, 0xd7, 0x89, + 0x5f, 0x94, 0x91, 0x53, 0x74, 0xaf, 0xdd, 0x87, 0xac, 0x47, 0x87, 0xae, 0xc5, 0x40, 0xaa, 0xaa, + 0x6a, 0x44, 0x20, 0xed, 0xa4, 0x9c, 0x8c, 0x20, 0x7a, 0x5a, 0x81, 0x9d, 0x87, 0xa2, 0x98, 0x86, + 0x9f, 0x2d, 0x71, 0x73, 0x70, 0xed, 0x63, 0x74, 0x81, 0xa7, 0x47, 0xb1, 0x5d, 0x4a, 0x73, 0x92, + 0x64, 0x62, 0x94, 0x55, 0x73, 0xed, 0x82, 0xad, 0x74, 0x55, 0x6f, 0x66, 0x79, 0x42, 0xca, 0xb1, + 0x6b, 0x6e, 0x97, 0x29, 0x68, 0xeb, 0x70, 0x6d, 0x75, 0xfb, 0x45, 0xac, 0x86, 0x9b, 0xa7, 0xc1, + 0xe8, 0xea, 0x2c, 0x82, 0x46, 0x00, 0x06, 0x82, 0x7d, 0xbc, 0x49, 0x20, 0x9d, 0x94, 0x38, 0x2d, + 0x86, 0x8a, 0x62, 0xab, 0x79, 0x81, 0xc6, 0xa2, 0x9f, 0x47, 0x44, 0x49, 0x4b, 0x6d, 0xd6, 0x42, + 0x9b, 0x70, 0x79, 0x70, 0x77, 0x42, 0x44, 0x8f, 0x29, 0x09, 0x0e, 0xb5, 0x6e, 0x2c, 0xe7, 0x43, + 0xb9, 0xa9, 0x61, 0xa8, 0x89, 0x82, 0x66, 0x4c, 0x2a, 0x4f, 0x49, 0x27, 0x69, 0x67, 0xea, 0xc7, + 0x66, 0x39, 0x47, 0x37, 0x85, 0x82, 0x29, 0x06, 0x67, 0x8a, 0xe2, 0x57, 0x20, 0x28, 0x9c, 0x77, + 0x2c, 0x20, 0x08, 0xfe, 0x09, 0x86, 0xe2, 0x67, 0x09, 0xf1, 0x24, 0x4c, 0x64, 0xd1, 0x67, 0x0e, + 0xb2, 0x52, 0x65, 0x87, 0x6e, 0xac, 0x65, 0x6c, 0x70, 0x6f, 0x67, 0xe8, 0x89, 0x2c, 0xf7, 0x34, + 0x46, 0x13, 0x64, 0x12, 0x6b, 0x73, 0x76, 0xa3, 0x10, 0x02, 0x9d, 0x94, 0x70, 0x6f, 0x2f, 0x83, + 0x9a, 0x79, 0x87, 0x6d, 0x79, 0x87, 0x79, 0xfa, 0x03, 0x87, 0xc2, 0x59, 0x87, 0x79, 0x1e, 0x8a, + 0x74, 0x8f, 0x8f, 0x74, 0x90, 0x76, 0xa7, 0xab, 0xd3, 0xb3, 0x65, 0x69, 0x4d, 0xd2, 0x46, 0x3a, + 0xa5, 0x4a, 0xa1, 0x48, 0xad, 0x08, 0x69, 0x46, 0x7e, 0xa7, 0xa0, 0x85, 0x43, 0x81, 0x46, 0x9a, + 0x93, 0x69, 0x66, 0x9b, 0x9f, 0x87, 0x50, 0x5a, 0x8d, 0x0a, 0xd1, 0x89, 0x66, 0xb9, 0x52, 0xa7, + 0xe3, 0x39, 0x45, 0x9d, 0x92, 0x6c, 0x65, 0xf6, 0xa3, 0x3f, 0xc8, 0x47, 0x03, 0x0f, 0x87, 0xb1, + 0x30, 0x8f, 0x2a, 0x81, 0xa0, 0x50, 0x50, 0x74, 0x14, 0x8e, 0x3a, 0xe3, 0xd6, 0xa5, 0x8d, 0xa4, + 0x66, 0xa3, 0xa9, 0x69, 0x2b, 0x62, 0xed, 0x84, 0xf0, 0x1a, 0xbe, 0x61, 0x6e, 0x79, 0xaa, 0xf6, + 0x41, 0x1b, 0xab, 0x65, 0x0e, 0x67, 0xa4, 0x62, 0x57, 0x74, 0x6f, 0x54, 0x43, 0x92, 0x27, 0xfe, + 0xbf, 0x08, 0x4e, 0x65, 0x77, 0x19, 0x1a, 0xa7, 0xc3, 0x69, 0x95, 0x6f, 0x6c, 0xf4, 0xaf, 0x2e, + 0xd9, 0xf7, 0xb7, 0x4e, 0x62, 0xab, 0x73, 0xa7, 0xae, 0x7a, 0x67, 0x4f, 0x0a, 0xa7, 0x79, 0xfe, + 0x16, 0x86, 0x0d, 0x87, 0xfb, 0x70, 0xa7, 0x8f, 0x9e, 0x0e, 0x05, 0x36, 0x66, 0xa2, 0x23, 0x47, + 0x72, 0x6e, 0x87, 0x14, 0x3d, 0xb7, 0x64, 0x65, 0xc7, 0xd2, 0xa9, 0x6f, 0x47, 0x89, 0x69, 0xea, + 0x44, 0xa7, 0x5c, 0x55, 0x26, 0x84, 0x28, 0x9e, 0x62, 0x76, 0x8a, 0x66, 0xb6, 0xb7, 0x75, 0x6e, + 0xa1, 0xa3, 0xa3, 0x8a, 0xc8, 0xa7, 0x70, 0xf1, 0x09, 0x67, 0x3f, 0x87, 0xa3, 0x2a, 0xa6, 0xa3, + 0x79, 0xb5, 0x69, 0x6e, 0x05, 0x81, 0xba, 0xbf, 0x94, 0x64, 0x73, 0x30, 0x47, 0x9e, 0x6a, 0xaf, + 0x29, 0xe2, 0xfa, 0x6d, 0xaf, 0x44, 0x04, 0x89, 0x87, 0x5b, 0xfd, 0x02, 0xa2, 0x3e, 0x6b, 0x73, + 0xe2, 0x47, 0xdc, 0x1c, 0xa0, 0xa3, 0xf4, 0x69, 0x6e, 0x81, 0x77, 0xd0, 0x66, 0x8d, 0x87, 0x3a, + 0x2b, 0x84, 0xc6, 0xea, 0x6e, 0xaf, 0x3a, 0xa5, 0x75, 0x01, 0xbd, 0x82, 0xa8, 0x85, 0x4d, 0xa6, + 0x85, 0xfe, 0xa7, 0xe3, 0x30, 0x8a, 0x66, 0x84, 0x65, 0x8a, 0x72, 0x6c, 0x41, 0xd9, 0x44, 0x05, + 0x87, 0x47, 0x67, 0x47, 0x33, 0x4b, 0x93, 0x49, 0x74, 0x02, 0xa1, 0x1c, 0xe8, 0x8f, 0x74, 0x44, + 0x1c, 0x81, 0x90, 0x87, 0x8c, 0xe9, 0x47, 0xc6, 0x40, 0xba, 0x48, 0x6f, 0x77, 0xed, 0xe0, 0x8a, + 0x2c, 0xeb, 0x9c, 0x09, 0x64, 0x6f, 0x65, 0x04, 0x86, 0xdf, 0xd7, 0xa1, 0x47, 0x84, 0x99, 0xcd, + 0x44, 0x4a, 0x86, 0xcc, 0x00, 0xd7, 0x20, 0xe9, 0x6e, 0xa3, 0xdf, 0xe2, 0xa5, 0x08, 0x45, 0x6a, + 0x66, 0x42, 0x8f, 0x28, 0x90, 0xcc, 0xa4, 0xa5, 0x20, 0x47, 0x8b, 0xaa, 0x55, 0xe3, 0x71, 0x67, + 0xef, 0x83, 0x1e, 0xb7, 0x51, 0x72, 0x75, 0x47, 0x89, 0x64, 0x0b, 0xe8, 0x76, 0x00, 0x65, 0xa3, + 0x42, 0x00, 0xc4, 0x41, 0x9f, 0x4b, 0x28, 0xa7, 0x74, 0x74, 0x6f, 0x1b, 0xa3, 0x5e, 0x86, 0x66, + 0xdd, 0x4e, 0x61, 0xca, 0xa5, 0xf2, 0x8e, 0x29, 0xcc, 0x89, 0x45, 0x06, 0x87, 0x87, 0xf6, 0x1b, + 0x66, 0x37, 0x87, 0x37, 0x2a, 0x67, 0xba, 0x8d, 0x5f, 0x7b, 0xc7, 0x86, 0x54, 0x67, 0x20, 0x61, + 0x47, 0xd2, 0xb7, 0x20, 0x61, 0x88, 0xb1, 0x87, 0x61, 0x60, 0xa7, 0x65, 0xc9, 0xa8, 0x27, 0xe0, + 0x81, 0xea, 0x74, 0x83, 0x80, 0x87, 0x64, 0x09, 0x87, 0x89, 0x0c, 0x87, 0xde, 0x96, 0xa7, 0x6a, + 0x73, 0x86, 0xd7, 0x91, 0xa7, 0x10, 0x3c, 0x8b, 0x74, 0xcb, 0x96, 0x74, 0x6f, 0x8d, 0xa6, 0xa7, + 0xf3, 0x33, 0xa3, 0xf7, 0x64, 0x11, 0x69, 0x73, 0xdb, 0x23, 0xcd, 0x86, 0x3d, 0x84, 0x70, 0x65, + 0x44, 0x54, 0x09, 0x68, 0x91, 0x62, 0x65, 0x7b, 0x2a, 0x8a, 0x72, 0x3f, 0x86, 0xc5, 0x51, 0xaa, + 0x72, 0x37, 0x83, 0x06, 0x4c, 0x8f, 0x65, 0x79, 0x3a, 0xa2, 0x86, 0xa7, 0x8f, 0x18, 0x0f, 0xa1, + 0x60, 0xe1, 0x85, 0xa9, 0xa7, 0x8f, 0x1b, 0x28, 0xac, 0x4e, 0xfb, 0x86, 0xe9, 0x74, 0x45, 0x96, + 0xa6, 0x7c, 0x4f, 0x3c, 0x2f, 0xb7, 0xc9, 0x73, 0xdd, 0xc4, 0xb7, 0x6c, 0x64, 0xff, 0xcf, 0x87, + 0x83, 0x1c, 0x42, 0xc2, 0xaa, 0x73, 0x99, 0x83, 0x6f, 0x64, 0x07, 0x33, 0x9e, 0x41, 0x6c, 0x77, + 0x61, 0x79, 0x73, 0x2d, 0x80, 0x87, 0x07, 0x8d, 0x81, 0x08, 0x46, 0x21, 0x87, 0x7f, 0xe4, 0x7b, + 0xa7, 0x8e, 0x45, 0xaf, 0x66, 0x86, 0x42, 0x42, 0xa0, 0x45, 0xd5, 0xa6, 0x88, 0x3c, 0x46, 0xd6, + 0xa7, 0x39, 0x5e, 0x84, 0x6f, 0x87, 0xd1, 0x9b, 0x87, 0x79, 0x87, 0x87, 0x7d, 0xab, 0xa7, 0xd4, + 0xbd, 0xa7, 0xfe, 0x8d, 0x87, 0x4f, 0x55, 0x22, 0x67, 0x0c, 0x34, 0x87, 0x3f, 0xf4, 0x07, 0xa7, + 0x16, 0x44, 0xa7, 0x0d, 0xf4, 0x13, 0x63, 0x42, 0x61, 0x76, 0x87, 0x97, 0x33, 0x87, 0xfe, 0xb7, + 0x87, 0x5c, 0xf0, 0x1c, 0x81, 0x4c, 0xba, 0x09, 0x45, 0x41, 0x4d, 0x9e, 0x18, 0x8c, 0x5f, 0x55, + 0x36, 0x34, 0x20, 0x28, 0x28, 0x31, 0x20, 0x3c, 0x3c, 0xa7, 0x37, 0x11, 0x9a, 0x07, 0x2d, 0x33, + 0x29, 0xcf, 0x57, 0x34, 0x29, 0xc3, 0xea, 0x63, 0x01, 0x4f, 0x28, 0xaf, 0x94, 0x2a, 0x20, 0xa0, + 0x56, 0x59, 0x28, 0x6c, 0x6f, 0x3f, 0x02, 0xd2, 0x97, 0x29, 0x29, 0xad, 0x26, 0x84, 0x4a, 0xab, + 0x5f, 0x3f, 0x8f, 0x92, 0x69, 0x6e, 0x6e, 0xa4, 0x84, 0x47, 0x21, 0x91, 0x75, 0x63, 0x5a, 0x02, + 0xa3, 0xef, 0x20, 0xa8, 0xa2, 0xae, 0x84, 0xd6, 0x86, 0x85, 0x18, 0x4b, 0x2e, 0xcf, 0xaa, 0x6d, + 0x5f, 0x54, 0x69, 0x61, 0x26, 0x69, 0x3a, 0x8b, 0xb3, 0x69, 0x74, 0x6a, 0x47, 0xc5, 0x24, 0x86, + 0xab, 0x8c, 0x62, 0xc7, 0x6c, 0x91, 0x66, 0x69, 0x84, 0xa3, 0x1d, 0xe4, 0x49, 0x21, 0x81, 0xa6, + 0xbc, 0xb7, 0x6f, 0x6e, 0xa4, 0x73, 0xb1, 0x64, 0x69, 0x46, 0xad, 0x08, 0x74, 0x82, 0x52, 0x7f, + 0x47, 0xb5, 0xac, 0x69, 0x1a, 0x18, 0x08, 0x61, 0xa3, 0xaa, 0x73, 0xa4, 0xf8, 0xa1, 0x41, 0xd0, + 0xb5, 0x6e, 0x6b, 0x3d, 0x42, 0xae, 0xa6, 0x60, 0x68, 0x47, 0xd4, 0xa4, 0xa1, 0x07, 0xa2, 0x4b, + 0xa1, 0x07, 0xed, 0x00, 0xa2, 0x40, 0x4f, 0xbf, 0x44, 0x4c, 0x4c, 0x14, 0x59, 0xb1, 0x62, 0x65, + 0x67, 0xdf, 0x42, 0x56, 0x44, 0x0e, 0xa3, 0xcb, 0xa9, 0x65, 0x01, 0x7c, 0xb7, 0x64, 0x73, 0x49, + 0x02, 0x83, 0xbf, 0xa1, 0xad, 0x83, 0x69, 0x65, 0x49, 0x65, 0xe7, 0x73, 0x20, 0x7b, 0xb5, 0x04, + 0x8a, 0x74, 0x09, 0x9d, 0x6f, 0x5b, 0x33, 0x7f, 0x14, 0x5d, 0x3b, 0x20, 0x7d, 0x9b, 0x77, 0x3b, + 0x0a, 0x86, 0x26, 0x00, 0x81, 0x3f, 0xd0, 0x84, 0xa5, 0x01, 0x6d, 0xa7, 0xc2, 0x9b, 0xa4, 0x8e, + 0x8a, 0x69, 0x51, 0x78, 0x67, 0xe1, 0x47, 0xb1, 0x41, 0x67, 0x5b, 0x43, 0x5e, 0x82, 0x17, 0x6c, + 0x47, 0xa6, 0x57, 0x20, 0x28, 0x80, 0x3b, 0x6d, 0x2a, 0x43, 0x81, 0x7b, 0x4f, 0x29, 0x74, 0x42, + 0xa2, 0x22, 0x54, 0x74, 0x65, 0xcd, 0x84, 0x80, 0xdb, 0x66, 0x6f, 0xa2, 0x6f, 0x63, 0x24, 0x89, + 0x69, 0x58, 0xd1, 0x0a, 0x7a, 0x47, 0x7d, 0xed, 0x46, 0xb5, 0xe8, 0x66, 0x46, 0xb7, 0x00, 0x63, + 0x99, 0xaa, 0x73, 0x00, 0xda, 0xa5, 0xde, 0xa4, 0x0d, 0x4c, 0x82, 0xf7, 0x85, 0xf3, 0xe2, 0xca, + 0x78, 0xd8, 0xd7, 0x64, 0x69, 0x74, 0x20, 0x28, 0x58, 0x6f, 0x29, 0x60, 0x42, 0xd6, 0x20, 0xb1, + 0x66, 0x6d, 0x82, 0xa4, 0xae, 0x68, 0xc7, 0xf9, 0x20, 0x86, 0xf9, 0xf4, 0x45, 0x7a, 0x62, 0x57, + 0xa2, 0xeb, 0x6b, 0x79, 0x05, 0x41, 0x91, 0xa2, 0x3e, 0x81, 0x94, 0x74, 0x75, 0x57, 0x92, 0x6f, + 0x66, 0x56, 0x03, 0x83, 0x06, 0x43, 0x8e, 0x84, 0x0a, 0x0f, 0x22, 0x67, 0xac, 0x64, 0x41, 0x1a, + 0x64, 0x0e, 0x07, 0x39, 0x67, 0xd9, 0xa7, 0x93, 0xd4, 0x07, 0xf1, 0x66, 0xfe, 0x67, 0x82, 0xf3, + 0x15, 0x9a, 0x6c, 0x6f, 0x61, 0x64, 0x44, 0x69, 0x1f, 0x0b, 0x87, 0xf9, 0xe3, 0x42, 0xdf, 0x56, + 0x20, 0x61, 0x13, 0x12, 0x64, 0x69, 0xc2, 0xd6, 0xd5, 0x83, 0x1f, 0x0e, 0x46, 0x41, 0xa6, 0x6b, + 0xab, 0x41, 0xc5, 0x21, 0xb7, 0x65, 0x76, 0x25, 0x0a, 0xa5, 0x34, 0x83, 0x03, 0xe0, 0x92, 0x67, + 0x6f, 0x4c, 0xac, 0x2c, 0xc1, 0x7d, 0x4f, 0x27, 0xb6, 0x4b, 0x27, 0xdb, 0xc1, 0xd7, 0x1c, 0xea, + 0x6d, 0x00, 0xe3, 0x67, 0x51, 0x48, 0x4c, 0x61, 0x84, 0x43, 0x5d, 0xa6, 0xb0, 0x82, 0x37, 0x50, + 0xac, 0x73, 0x5c, 0xa1, 0x41, 0x17, 0xa3, 0x16, 0xa5, 0xa3, 0x6f, 0x47, 0x64, 0x8d, 0x73, 0x15, + 0x54, 0xa4, 0xae, 0xa7, 0x38, 0xec, 0x81, 0xe0, 0x14, 0x91, 0x4b, 0x42, 0xa3, 0x84, 0x64, 0xaa, + 0x67, 0xf8, 0x67, 0xc9, 0xf8, 0x01, 0x87, 0x93, 0x42, 0x67, 0xf8, 0x83, 0xd8, 0xe3, 0x22, 0x84, + 0x68, 0x7e, 0x66, 0xa3, 0x87, 0x3c, 0xac, 0x41, 0xc0, 0x81, 0x83, 0x8b, 0x75, 0xf1, 0x52, 0x86, + 0x53, 0x86, 0xa2, 0x1c, 0xa6, 0xfd, 0xa9, 0x27, 0x67, 0x48, 0x94, 0x27, 0x2c, 0xcc, 0x63, 0xbf, + 0xa4, 0xa3, 0xeb, 0x66, 0xac, 0xb7, 0x6c, 0x79, 0xb5, 0x83, 0x61, 0xf3, 0x6f, 0x73, 0x69, 0x96, + 0x74, 0x6f, 0x00, 0x1d, 0xa7, 0xe7, 0x9e, 0xa3, 0xc7, 0xef, 0x49, 0x24, 0x6f, 0x50, 0x54, 0x46, + 0xb0, 0xaa, 0x72, 0x60, 0xdc, 0x9d, 0x73, 0x75, 0x6d, 0x19, 0x3c, 0x6d, 0x74, 0x3d, 0x42, 0xb4, + 0x41, 0x5c, 0xc4, 0xd4, 0xbf, 0xae, 0x6e, 0x63, 0xa4, 0xe8, 0xb0, 0x91, 0x27, 0x64, 0xef, 0x87, + 0xaf, 0x0c, 0x09, 0xc5, 0xda, 0xca, 0x83, 0x02, 0xbf, 0xa4, 0xc7, 0x87, 0x5f, 0x43, 0x06, 0x61, + 0x8c, 0xa7, 0x1f, 0xa2, 0x0b, 0x83, 0xc1, 0xbb, 0xa7, 0xdc, 0x31, 0x45, 0x99, 0x82, 0xb3, 0x24, + 0x2a, 0x97, 0x69, 0x66, 0x14, 0x71, 0xa6, 0xed, 0x94, 0x66, 0x69, 0x84, 0x04, 0x47, 0x3f, 0x84, + 0xf1, 0xc1, 0x87, 0x0a, 0x91, 0x44, 0x97, 0x86, 0x1c, 0xa5, 0x21, 0x5e, 0x61, 0x80, 0x84, 0x1c, + 0x87, 0xf3, 0xb1, 0x67, 0x06, 0xef, 0x87, 0xa3, 0xf0, 0x0f, 0x41, 0x01, 0xa7, 0x7b, 0x00, 0xa5, + 0x7c, 0x22, 0x60, 0xa7, 0x7f, 0x56, 0x87, 0x46, 0xff, 0x00, 0xa7, 0x21, 0x51, 0xfe, 0x73, 0x61, + 0x76, 0x65, 0x97, 0x49, 0x66, 0xfd, 0xb8, 0x67, 0x03, 0x21, 0xa5, 0x7d, 0x67, 0x98, 0x5f, 0xa6, + 0x66, 0x91, 0x61, 0x76, 0x1e, 0xe6, 0xc3, 0xd9, 0x2e, 0xb7, 0x61, 0x74, 0xa9, 0x24, 0xae, 0x20, + 0x8f, 0x42, 0x9c, 0x42, 0x90, 0x6e, 0x20, 0xac, 0x09, 0x61, 0x86, 0x62, 0xb1, 0xa3, 0x10, 0x53, + 0x20, 0x28, 0x23, 0x03, 0x6f, 0x6b, 0x42, 0x99, 0xa2, 0x7f, 0xa5, 0xbd, 0xad, 0xa3, 0xa0, 0x68, + 0x64, 0xfd, 0xe8, 0x27, 0xc6, 0xd8, 0xb2, 0x82, 0x6f, 0x93, 0xa7, 0x66, 0x30, 0xa2, 0x17, 0x71, + 0x61, 0x66, 0x22, 0x92, 0x77, 0x61, 0x8d, 0xfb, 0xa7, 0x8d, 0x1e, 0x87, 0x6c, 0x40, 0x97, 0x73, + 0x20, 0x67, 0x03, 0x83, 0xe1, 0x42, 0x51, 0x4a, 0x2c, 0xa2, 0x62, 0x0f, 0x87, 0x82, 0x5d, 0x46, + 0xa2, 0x67, 0x57, 0xee, 0x97, 0x28, 0x29, 0xa6, 0x3e, 0x43, 0x2a, 0x8f, 0x64, 0xa0, 0x7e, 0x87, + 0xa1, 0xfe, 0x01, 0x87, 0xf3, 0x11, 0xae, 0x2c, 0x9f, 0xa4, 0x2e, 0x33, 0x86, 0x8f, 0x67, 0x66, + 0x0e, 0x87, 0x8f, 0xf7, 0x07, 0x67, 0xcf, 0x87, 0xe8, 0x95, 0x62, 0x16, 0x87, 0xbf, 0xac, 0x25, + 0x81, 0xab, 0x16, 0x87, 0xaa, 0xfb, 0x2c, 0x41, 0x29, 0x87, 0xa8, 0xa1, 0x38, 0x44, 0x45, 0x43, + 0x4f, 0xeb, 0x87, 0xa2, 0x0f, 0x57, 0x20, 0x34, 0xdc, 0x03, 0x86, 0xb3, 0xbb, 0x27, 0x7b, 0x87, + 0xb5, 0x92, 0x86, 0x09, 0x8f, 0x20, 0xb2, 0xac, 0xa7, 0xc9, 0xdf, 0x46, 0xda, 0xa7, 0xc8, 0xd6, + 0x47, 0x61, 0x7c, 0xa7, 0xd0, 0xe8, 0x8b, 0x44, 0xe7, 0xa1, 0xcb, 0xca, 0x84, 0x51, 0x27, 0x38, + 0x87, 0xbf, 0x4b, 0x1e, 0x87, 0xbf, 0x57, 0x03, 0x85, 0x4c, 0xd8, 0x63, 0xe5, 0xa5, 0xbe, 0x46, + 0x7a, 0x8c, 0x20, 0x25, 0x22, 0x21, 0x97, 0x28, 0x29, 0xbd, 0x31, 0x87, 0xcf, 0x27, 0x0c, 0x86, + 0xb2, 0x88, 0x61, 0x12, 0x81, 0x00, 0xc5, 0xd8, 0x4a, 0x97, 0x6f, 0x66, 0xbc, 0x82, 0x84, 0xbc, + 0x67, 0x87, 0x2d, 0xf2, 0x2a, 0x66, 0xba, 0x86, 0xda, 0x41, 0x05, 0x46, 0xbc, 0xaf, 0x2e, 0xc9, + 0x01, 0x46, 0x10, 0x87, 0xbf, 0xfb, 0x11, 0x46, 0x9f, 0x87, 0xbe, 0xf5, 0x47, 0xab, 0x5b, 0x87, + 0xef, 0xe9, 0x77, 0x2f, 0x0a, 0x0b, 0x47, 0x67, 0xe4, 0x87, 0x5d, 0x4f, 0x07, 0x6d, 0x47, 0x8c, + 0x4f, 0x28, 0xa4, 0xd6, 0x26, 0xa7, 0xe0, 0x55, 0x67, 0x0a, 0xbb, 0xa7, 0x68, 0xf5, 0x03, 0x44, + 0x13, 0x84, 0x05, 0x84, 0x56, 0x18, 0xa4, 0x81, 0xa7, 0xc2, 0x62, 0x63, 0xa1, 0xcc, 0x53, 0xd9, + 0xec, 0xa7, 0xce, 0xc3, 0xb1, 0x20, 0x28, 0x33, 0x37, 0xc6, 0xd7, 0x0c, 0xa4, 0x30, 0x41, 0x7d, + 0xaf, 0x29, 0xc5, 0xac, 0xaa, 0x31, 0xd0, 0xc4, 0x9f, 0x4f, 0x4b, 0x2c, 0xea, 0x3a, 0x47, 0x4b, + 0xae, 0x47, 0x0e, 0x27, 0x67, 0xa7, 0xcf, 0xb5, 0x15, 0xaf, 0x2a, 0xe2, 0x6b, 0xa6, 0x7b, 0xe6, + 0xa3, 0x69, 0xa6, 0x60, 0x5f, 0x67, 0xe3, 0x45, 0x26, 0x87, 0x75, 0xd7, 0x42, 0x0a, 0xa9, 0x6d, + 0x6f, 0x56, 0x89, 0x69, 0x01, 0xa6, 0xc2, 0xcf, 0xa9, 0x69, 0x2f, 0xae, 0x73, 0x96, 0xa8, 0x8a, + 0x22, 0x91, 0xa5, 0x3c, 0xa0, 0xaf, 0x50, 0x99, 0x07, 0x86, 0xc7, 0x46, 0xcd, 0x8a, 0x2a, 0xcd, + 0x2c, 0xaf, 0x2a, 0xf6, 0xc0, 0xfe, 0x74, 0x68, 0x65, 0xa6, 0x7a, 0x95, 0x66, 0x65, 0x81, 0xf1, + 0xa7, 0x84, 0x58, 0xa1, 0x3b, 0xaa, 0x6a, 0x70, 0x3d, 0x86, 0x80, 0x00, 0xfa, 0x85, 0x70, 0x87, + 0xa2, 0xe5, 0x69, 0x72, 0x4e, 0x85, 0xc1, 0x78, 0xcf, 0x73, 0xdb, 0xda, 0x87, 0x56, 0xb1, 0xa5, + 0x42, 0x22, 0x65, 0xf0, 0x9b, 0x2d, 0x20, 0x45, 0x78, 0x61, 0x94, 0x48, 0x62, 0x2e, 0x63, 0x13, + 0x62, 0x30, 0x57, 0x65, 0x6e, 0xc4, 0x84, 0x1f, 0xe3, 0x44, 0xdd, 0x40, 0x79, 0xaa, 0x64, 0xa5, + 0x51, 0x72, 0x75, 0x3d, 0x6b, 0x28, 0xea, 0x87, 0x53, 0xd4, 0x00, 0x22, 0x44, 0xb7, 0x46, 0x31, + 0x4a, 0x73, 0x55, 0x44, 0x53, 0xa4, 0x70, 0x42, 0x51, 0x83, 0x12, 0x28, 0x67, 0x7a, 0x4f, 0x26, + 0x90, 0x01, 0x47, 0x3d, 0xa5, 0x7c, 0xe0, 0x8a, 0x68, 0xeb, 0x20, 0xcb, 0xa4, 0x69, 0xc7, 0xdb, + 0xf9, 0x33, 0xa1, 0xb9, 0x8c, 0x79, 0x76, 0x03, 0xa1, 0x68, 0xa2, 0xd1, 0xb7, 0x75, 0x20, 0x3c, + 0xe4, 0x4d, 0x2e, 0x27, 0xae, 0x4c, 0xa6, 0x57, 0x61, 0x6e, 0x30, 0x95, 0xb7, 0x62, 0x79, 0xcd, + 0x3b, 0x83, 0xa9, 0x6a, 0x42, 0x51, 0x81, 0xba, 0xa7, 0xa4, 0x7d, 0xa7, 0x53, 0xca, 0x0f, 0x20, + 0x04, 0x84, 0xce, 0xd6, 0xa1, 0xed, 0xc3, 0xcb, 0x9b, 0xa5, 0x6a, 0x9e, 0x28, 0x70, 0x09, 0x64, + 0x81, 0xaa, 0x2e, 0x92, 0xc7, 0xdd, 0x1b, 0x9c, 0x82, 0x5c, 0x84, 0x52, 0x57, 0x8f, 0x64, 0x24, + 0x3c, 0x86, 0x15, 0xa2, 0xef, 0x42, 0x86, 0x9d, 0x6f, 0x2e, 0xd0, 0xc4, 0x63, 0x6f, 0x86, 0xad, + 0x67, 0x26, 0x7c, 0x89, 0x73, 0x01, 0x82, 0xbc, 0x4f, 0x86, 0x00, 0x83, 0x65, 0xe9, 0x50, 0x79, + 0x6e, 0xc4, 0x81, 0x47, 0xb4, 0x69, 0x7a, 0x22, 0x44, 0xa5, 0x42, 0x67, 0x97, 0xff, 0x1a, 0x67, + 0x9c, 0x06, 0x99, 0x00, 0x5f, 0x41, 0x74, 0x84, 0x82, 0xa3, 0x5e, 0x04, 0xa1, 0x84, 0x15, 0xad, + 0x38, 0x69, 0x6f, 0x2b, 0xac, 0x4f, 0x47, 0x29, 0x79, 0x8f, 0x6c, 0xf1, 0xe2, 0x67, 0xb0, 0x60, + 0xa5, 0x63, 0xe8, 0x57, 0x83, 0x69, 0x78, 0xa4, 0x36, 0xc7, 0xc8, 0x74, 0x62, 0x43, 0x68, 0xa6, + 0x66, 0x8b, 0x2c, 0xb0, 0x45, 0xa3, 0x55, 0xe9, 0x6c, 0xc3, 0xd1, 0x70, 0x81, 0xeb, 0xc4, 0x44, + 0xa9, 0xa5, 0xdc, 0x87, 0xb3, 0x2a, 0x65, 0x49, 0xa7, 0x11, 0xad, 0x86, 0x22, 0x5f, 0x85, 0x6e, + 0x8a, 0x69, 0x6b, 0x51, 0x84, 0xc7, 0xa7, 0xdf, 0x26, 0x02, 0x97, 0x28, 0x29, 0x6f, 0x62, 0x03, + 0x86, 0xae, 0xca, 0xa7, 0xab, 0xff, 0x11, 0xc7, 0xd2, 0x90, 0x36, 0x87, 0x4e, 0x0d, 0x47, 0x6a, + 0xf4, 0x3d, 0xa6, 0xde, 0x87, 0x23, 0x72, 0xc6, 0xd8, 0x79, 0xa7, 0xeb, 0x18, 0x89, 0x3a, 0xda, + 0xea, 0x63, 0xab, 0x82, 0xe8, 0x87, 0xdf, 0xe9, 0x09, 0x82, 0x23, 0xc9, 0xcf, 0x6b, 0xdd, 0x46, + 0x64, 0x4c, 0x62, 0xf4, 0x8e, 0x62, 0x4d, 0xf3, 0x77, 0x6f, 0x66, 0x60, 0xc5, 0xc6, 0x37, 0xc3, + 0xdd, 0x52, 0x67, 0xf1, 0x7a, 0x8f, 0x78, 0xd1, 0x26, 0x46, 0x7f, 0x8e, 0x79, 0xda, 0xf8, 0x0e, + 0x6e, 0x64, 0x2d, 0x81, 0x2b, 0x2b, 0x2e, 0x5a, 0x87, 0x24, 0x2e, 0xc2, 0xd3, 0x5e, 0xa4, 0x80, + 0x87, 0xcf, 0x34, 0x09, 0x8f, 0x2e, 0xff, 0xe2, 0x03, 0x47, 0x00, 0x1f, 0xc7, 0xd0, 0x97, 0x34, + 0xa7, 0xda, 0x65, 0xb0, 0x53, 0x74, 0xd8, 0x66, 0xef, 0x74, 0x87, 0xfe, 0xe2, 0x47, 0x6a, 0xff, + 0x18, 0x66, 0x07, 0x47, 0x85, 0x14, 0xc7, 0xce, 0xcd, 0xf1, 0x16, 0xf8, 0x4f, 0x62, 0x73, 0x6f, + 0xcf, 0x74, 0xce, 0xcb, 0xf6, 0x1c, 0xca, 0x2f, 0xcd, 0xf9, 0x41, 0x24, 0x83, 0x75, 0x48, 0x57, + 0x6b, 0xc2, 0xcc, 0xbd, 0x14, 0x73, 0x20, 0x34, 0xab, 0x53, 0x25, 0x85, 0x62, 0xdf, 0x2e, 0x77, + 0x81, 0x2b, 0xba, 0xa2, 0xa9, 0xab, 0x73, 0x2b, 0x33, 0xa2, 0xda, 0x89, 0x62, 0xde, 0x9f, 0x82, + 0xdd, 0x84, 0xe2, 0xf1, 0xc4, 0xc9, 0xde, 0x83, 0x1d, 0xa6, 0x02, 0xec, 0x83, 0x7b, 0x20, 0x3a, + 0x0b, 0x61, 0x4c, 0x68, 0xd3, 0xa4, 0x03, 0xba, 0x5e, 0x2d, 0x57, 0x6e, 0x6f, 0x2d, 0x64, 0x80, + 0x09, 0x64, 0xa9, 0xc1, 0xc4, 0x4e, 0xa4, 0x1d, 0xa4, 0x21, 0x32, 0xbc, 0x67, 0x63, 0x63, 0x0a, + 0x04, 0x6c, 0xda, 0x5f, 0x43, 0x52, 0x54, 0x5f, 0x53, 0x45, 0xc6, 0xd1, 0x9a, 0x97, 0x5f, 0x4e, + 0x4f, 0x5f, 0x57, 0x41, 0x52, 0x4e, 0x49, 0x4e, 0x47, 0x53, 0x00, 0xa8, 0x56, 0xc3, 0x10, 0xe8, + 0x75, 0xc7, 0xd7, 0x81, 0xcf, 0x2e, 0xc8, 0x02, 0x2a, 0xc4, 0xd7, 0x22, 0xa7, 0x6b, 0x26, 0x5e, + 0x44, 0x45, 0x50, 0x52, 0x45, 0x43, 0x41, 0x54, 0x45, 0xbb, 0x21, 0x74, 0x59, 0x46, 0x42, 0x4c, + 0x4f, 0x43, 0x4b, 0x2e, 0x07, 0xc2, 0xc8, 0x7e, 0xe9, 0x6e, 0x47, 0xd5, 0xff, 0x07, 0x40, 0xd9, + 0x4d, 0x20, 0xac, 0x47, 0xd7, 0x16, 0xde, 0x00, 0x47, 0x43, 0x43, 0xc9, 0xc6, 0xc2, 0xc8, 0x20, + 0x38, 0x47, 0x4e, 0x55, 0x87, 0xa9, 0x5f, 0xd4, 0xc4, 0xca, 0xc5, 0x04, 0x71, 0xc4, 0xc9, 0xf5, + 0x00, 0x49, 0x29, 0x9b, 0x82, 0xb6, 0x65, 0x4f, 0x28, 0xc7, 0x82, 0x7d, 0xb0, 0xd1, 0x34, 0x30, + 0xd5, 0x30, 0xd6, 0x7c, 0x7c, 0xc7, 0xd4, 0x41, 0xb5, 0x61, 0xc6, 0x55, 0x6e, 0x67, 0xcb, 0x47, + 0x64, 0x75, 0x44, 0x28, 0x44, 0x49, 0x29, 0x97, 0xd4, 0x61, 0x74, 0xc1, 0xd2, 0x08, 0x65, 0xa1, + 0x77, 0x28, 0x28, 0x7d, 0x47, 0xdd, 0x0e, 0x4b, 0x29, 0x82, 0x57, 0x65, 0x6c, 0x80, 0x5f, 0x0f, + 0x33, 0x30, 0x31, 0x96, 0x27, 0x47, 0x9f, 0x11, 0x47, 0x3b, 0x73, 0x4d, 0x53, 0xe2, 0x47, 0x3c, + 0xff, 0x10, 0x82, 0xd1, 0x82, 0x0a, 0xf4, 0x47, 0x40, 0x46, 0x41, 0x74, 0x73, 0x65, 0xf0, 0xc5, + 0xc6, 0x9b, 0x25, 0x1f, 0x75, 0x28, 0x22, 0x59, 0x8a, 0x3a, 0xda, 0xa6, 0xdc, 0x48, 0xa7, 0xd3, + 0x16, 0x67, 0xc8, 0xa2, 0x61, 0x27, 0xa3, 0x86, 0xc5, 0xd4, 0xe8, 0x7f, 0x0f, 0x65, 0x72, 0x22, + 0x99, 0x0c, 0x63, 0xbe, 0xc4, 0xc6, 0x57, 0x0b, 0x23, 0xcf, 0xc1, 0xd2, 0xf3, 0x87, 0x7e, 0xff, + 0x07, 0xcc, 0x20, 0xc7, 0x3f, 0xa6, 0x2e, 0xc7, 0xd8, 0x8c, 0xdf, 0xa5, 0x60, 0x85, 0xa5, 0xdd, + 0x26, 0x84, 0x62, 0x2c, 0xfd, 0x70, 0x6c, 0x61, 0x6e, 0x6e, 0x82, 0xef, 0x2b, 0xc6, 0xc6, 0x4f, + 0xa7, 0x3f, 0x20, 0xa9, 0x62, 0xbd, 0xb8, 0x0f, 0x31, 0x33, 0x31, 0x43, 0xe9, 0x70, 0xa1, 0xc7, + 0x05, 0x83, 0xb9, 0xa7, 0x7b, 0x28, 0xa7, 0xa5, 0x02, 0xc7, 0xd7, 0x7f, 0xfb, 0x18, 0xc7, 0xd9, + 0x19, 0xc5, 0xcc, 0x81, 0x83, 0x1f, 0xc0, 0xc7, 0xd7, 0x2e, 0x22, 0xa5, 0x1e, 0xb0, 0x47, 0x9c, + 0x82, 0xef, 0xa7, 0xc4, 0xd8, 0xb9, 0x07, 0x4f, 0xc7, 0xd8, 0xab, 0x20, 0x47, 0x35, 0xf1, 0x0d, + 0x47, 0x8b, 0x47, 0x99, 0xff, 0x20, 0x47, 0x1f, 0x12, 0xa7, 0x61, 0x97, 0x67, 0x60, 0x74, 0xa7, + 0xb0, 0x96, 0xa7, 0x84, 0x0f, 0x47, 0x0f, 0x39, 0x46, 0x7b, 0x47, 0x89, 0xfe, 0x33, 0x67, 0x1e, + 0xa7, 0xf5, 0xf0, 0xb7, 0x64, 0x65, 0xff, 0xee, 0x12, 0xb1, 0x20, 0x6e, 0xc5, 0xf5, 0xa3, 0x15, + 0xc7, 0xd8, 0x41, 0x3e, 0x87, 0x14, 0x1b, 0x82, 0x2d, 0x84, 0x92, 0x93, 0xb2, 0x6e, 0x6f, 0x47, + 0xa2, 0xb2, 0x99, 0x8a, 0x62, 0x4b, 0x85, 0x0c, 0x93, 0x86, 0x15, 0xa3, 0x21, 0xbe, 0xc7, 0xd0, + 0xe0, 0x83, 0xbd, 0x05, 0xc6, 0xd0, 0xdb, 0x81, 0x54, 0xc3, 0xc4, 0x7d, 0xc4, 0xd7, 0xe9, 0xc2, + 0xd8, 0xb4, 0x20, 0x1a, 0xc6, 0xc2, 0xbd, 0xad, 0x73, 0x1f, 0x63, 0xec, 0xc7, 0xd9, 0xd0, 0x16, + 0xc7, 0xd3, 0x9d, 0x87, 0xcb, 0x9d, 0x47, 0xc8, 0xca, 0x01, 0xc2, 0xd4, 0xfd, 0x6f, 0x6e, 0xe3, + 0x16, 0x47, 0xb6, 0xa7, 0xcb, 0xbd, 0x41, 0x38, 0x67, 0xea, 0x60, 0x83, 0xab, 0xaa, 0x6f, 0x2a, + 0x92, 0x86, 0xdf, 0xae, 0x77, 0xd9, 0x62, 0xd4, 0x64, 0x3b, 0xd9, 0x29, 0x82, 0xd0, 0xa3, 0xd1, + 0x56, 0x43, 0x06, 0x86, 0xe5, 0xb2, 0x72, 0x65, 0xd5, 0x2a, 0x83, 0x44, 0x03, 0x10, 0x62, 0xdc, + 0x85, 0x4e, 0xd2, 0x68, 0x69, 0xd2, 0x94, 0xc7, 0xc8, 0x54, 0x31, 0xc6, 0xdc, 0xd9, 0x84, 0xdf, + 0x84, 0xc2, 0xbc, 0x47, 0x86, 0x64, 0x85, 0x69, 0x69, 0xda, 0xab, 0x74, 0x83, 0x22, 0xa2, 0xea, + 0x92, 0x74, 0x61, 0xd9, 0x36, 0xa2, 0xdb, 0x66, 0x31, 0x21, 0x66, 0x67, 0xc4, 0xa7, 0xcf, 0x8e, + 0x0f, 0x87, 0xe2, 0xdf, 0x47, 0xb5, 0xbe, 0x67, 0xb1, 0x47, 0xa3, 0xfe, 0x0e, 0x97, 0x69, 0x73, + 0x74, 0xfe, 0x00, 0xa7, 0x43, 0x71, 0x85, 0xf3, 0xa7, 0x84, 0x41, 0xaa, 0x3b, 0x21, 0x8c, 0xc2, + 0xc2, 0x29, 0x05, 0xc7, 0xc1, 0xdc, 0x5a, 0xae, 0x77, 0x35, 0xa6, 0xad, 0x4e, 0x42, 0xad, 0xa7, + 0x3c, 0x62, 0x49, 0x22, 0xbf, 0xa7, 0xd3, 0x91, 0xae, 0x29, 0xb3, 0x28, 0x22, 0xbe, 0x83, 0x7e, + 0x47, 0xdd, 0x14, 0x86, 0x6a, 0xa4, 0xf4, 0x98, 0x85, 0x69, 0x4f, 0x3b, 0xa9, 0xf2, 0x1a, 0xa3, + 0x77, 0xc7, 0xd2, 0x65, 0x32, 0x43, 0x22, 0xc7, 0xd3, 0x28, 0x47, 0xac, 0xe1, 0xa6, 0xcf, 0x47, + 0xad, 0xa2, 0x46, 0xac, 0x46, 0xdd, 0x47, 0xad, 0xa7, 0xe5, 0x51, 0x47, 0x3f, 0xf3, 0x14, 0x86, + 0xf1, 0x47, 0x96, 0x4f, 0x01, 0x44, 0xbb, 0x44, 0x3f, 0xa9, 0x6c, 0xf1, 0x36, 0x6f, 0x49, 0xf1, + 0x87, 0xd4, 0xe3, 0x77, 0x29, 0x3b, 0x3a, 0xe7, 0xa7, 0x72, 0xae, 0x67, 0x60, 0x87, 0xaf, 0x33, + 0x06, 0x67, 0xaa, 0x7b, 0x47, 0xd2, 0x83, 0xd2, 0x39, 0x81, 0x46, 0x79, 0x33, 0x66, 0x69, 0x78, + 0x36, 0x34, 0x6b, 0x3e, 0xa7, 0xc5, 0xea, 0x87, 0x05, 0x93, 0xa7, 0xbe, 0xbd, 0x67, 0x65, 0x87, + 0xae, 0x23, 0x47, 0x5a, 0xeb, 0x47, 0xd2, 0x47, 0x5a, 0xf9, 0x10, 0xa7, 0xfc, 0xef, 0xc7, 0xbd, + 0x11, 0x04, 0xce, 0x7d, 0xbd, 0x1c, 0xe7, 0xf0, 0xe8, +}; + +static unsigned char buf[18830]; + +int main() { + + unsigned long cksum = adler32(0, NULL, 0); + + decompress_lzsa2(compressed, buf); + cksum = adler32(cksum, buf, 18830); + + return cksum == 0xf748269d ? 0 : 1; +} diff --git a/test/val/mult1.c b/test/val/mult1.c index 95141d76d..b1a074e41 100644 --- a/test/val/mult1.c +++ b/test/val/mult1.c @@ -55,6 +55,7 @@ void m3(unsigned char uc) /* testing literal multiply with same source and destination */ vuc = uc; uc2 = 0; + uc1 = vuc; uc1 = uc1*0; if( uc1 != 0 ) failures++; uc1 = vuc; uc1 = uc1*1; if( uc1 != (uc2+=TESTLIT) ) failures++; uc1 = vuc; uc1 = uc1*2; if( uc1 != (uc2+=TESTLIT) ) failures++; uc1 = vuc; uc1 = uc1*3; if( uc1 != (uc2+=TESTLIT) ) failures++; diff --git a/test/val/nullptr.c b/test/val/nullptr.c new file mode 100644 index 000000000..a5b72e8c5 --- /dev/null +++ b/test/val/nullptr.c @@ -0,0 +1,73 @@ +/* Bug # - Pointer compared to null pointer constant */ + +#include <stdio.h> + +unsigned failures; + +struct S { + char a[4]; +} *p; + +#define TEST_NULL(E) \ + do { \ + a = (E) == 0 && !(E); \ + if (!a) \ + { \ + ++failures; \ + printf("failed: " #E " should be null\n"); \ + } \ + } while(0); + +#define TEST_NON_NULL(E) \ + do { \ + a = (E) != 0 && !!(E) && (E); \ + if (!a) \ + { \ + ++failures; \ + printf("failed: " #E " should be non-null\n"); \ + } \ + } while(0); + +void func() { } + +int main() +{ + int a; + + /* Null pointer constant (per ISO C) compared equal to null pointer constant */ + TEST_NULL((void*)0) + + /* Null pointer compared equal to null pointer constant */ + TEST_NULL((char*)0) + + /* Null pointer obtained with -> */ + TEST_NULL(((struct S*)0)->a) + + /* Null pointer obtained with -> */ + TEST_NULL(p->a) + + /* Null pointer obtained with cast and -> */ + TEST_NULL(((struct S*)(a = 0))->a) + + /* Null pointer obtained with cast and -> */ + TEST_NULL((a = 0, ((struct S*)a)->a)) + + /* Non-null pointer obtained with cast and -> */ + TEST_NON_NULL(((struct S*)(long)(a = 0x1234))->a) + + /* Non-null pointer obtained with cast and -> */ + TEST_NON_NULL((a = 0x1234, ((struct S*)a)->a)) + + /* Non-null pointer obtained with cast and -> */ + TEST_NON_NULL(((struct S*)&a)->a) + + /* Non-null pointer obtained with cast and -> */ + TEST_NON_NULL(((struct S*)&func)->a) + + if (failures != 0) + { + printf("failures: %u\n", failures); + } + + return failures; +} diff --git a/test/val/optimizer-bug-pr2262.c b/test/val/optimizer-bug-pr2262.c new file mode 100644 index 000000000..f2c08a98c --- /dev/null +++ b/test/val/optimizer-bug-pr2262.c @@ -0,0 +1,53 @@ + +// optimizer bug that occured after PR #2262, fixed by PR #2295 + +#include <stdio.h> + +unsigned char n; +unsigned long fp1; + +int failures = 0; + +void test1(void) +{ + asm("lda _n"); + asm("jeq %g", L0004); + + asm("lda #$3F"); + asm("sta sreg+1"); + asm("lda #$C0"); + asm("sta sreg"); + asm("lda #$00"); + asm("ldx #$00"); + asm("jmp %g", L0005); + +L0004: + asm("lda #$3F"); + asm("sta sreg+1"); + asm("lda #$00"); + asm("sta sreg"); + asm("lda #$00"); + asm("ldx #$00"); + +L0005: + asm("sta _fp1"); + asm("stx _fp1+1"); + asm("ldy sreg"); + asm("sty _fp1+2"); + asm("ldy sreg+1"); + asm("sty _fp1+3"); +} + +int main(void) +{ + n = 0; + test1(); + printf("fp1:%08lx\n", fp1); + if (fp1 != 0x3f000000) failures++; + n = 0xff; + test1(); + printf("fp1:%08lx\n", fp1); + if (fp1 != 0x3fc00000) failures++; + printf("failures:%d\n", failures); + return failures; +} diff --git a/test/val/pragmas.c b/test/val/pragmas.c new file mode 100644 index 000000000..802c7a5c3 --- /dev/null +++ b/test/val/pragmas.c @@ -0,0 +1,44 @@ +/* Note: This tests just if the #pragmas are understood. It doesn't test if +** they do really work. This would require much more work. +*/ + +void func(void); +#pragma align(push, 1024) +#pragma allow-eager-inline(push, on) +#pragma allow_eager_inline(pop) +#pragma bss-name(push, "BSS") +#pragma bss_name(pop) +#pragma charmap(1, 1) +#pragma check-stack(on) +#pragma check_stack(off) +#pragma code-name(push, "CODE") +#pragma code_name("CODE") +#pragma codesize(200) +#pragma data-name("DATA") +#pragma data_name("DATA") +#pragma inline-stdfuncs(off) +#pragma inline_stdfuncs(on) +#pragma local-strings(off) +#pragma local_strings(off) +#pragma message("in a bottle") +#pragma optimize(off) +#pragma register-vars(off) +#pragma register_vars(on) +#pragma regvaraddr(on) +#pragma rodata-name("RODATA") +#pragma rodata_name("RODATA") +#pragma signed-chars(off) +#pragma signed_chars(on) +#pragma static-locals(off) +#pragma static_locals(on) +#pragma warn(unused-param, on) +#pragma wrapped-call(push, func, 0) // push is required for this #pragma +#pragma wrapped_call(push, func, 1) +#pragma writable-strings(on) +#pragma writable_strings(off) +#pragma zpsym("func") + +int main () +{ + return 0; +} diff --git a/test/val/remove.c b/test/val/remove.c new file mode 100644 index 000000000..eecf8be8f --- /dev/null +++ b/test/val/remove.c @@ -0,0 +1,55 @@ +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +int fails = 0; + + +static void create_out_file(const char *outfile_path) { + FILE *out; + + + out = fopen(outfile_path, "wb"); + if (out == NULL) { + printf("Could not create %s\n", outfile_path); + fails++; + return; + } + fclose(out); +} + +int main (int argc, char **argv) +{ + int r; + static char outfile_path[FILENAME_MAX+1]; + + sprintf(outfile_path, "%s.test.out", argv[0]); + + create_out_file(outfile_path); + r = remove(outfile_path); + if (r != 0) { + printf("could not remove() %s\n", outfile_path); + fails++; + } + + create_out_file(outfile_path); + r = unlink(outfile_path); + if (r != 0) { + printf("could not unlink() %s\n", outfile_path); + fails++; + } + + r = remove("klsdfjqlsjdflkqjdsoizu"); + if (r == 0) { + printf("remove()ing non-existent file succeeded\n"); + fails++; + } + + r = unlink("klsdfjqlsjdflkqjdsoizu"); + if (r == 0) { + printf("unlink()ing non-existent file succeeded\n"); + fails++; + } + + return fails; +} diff --git a/test/val/static-long.c b/test/val/static-long.c new file mode 100644 index 000000000..a2e4e53a3 --- /dev/null +++ b/test/val/static-long.c @@ -0,0 +1,41 @@ +#include <stdint.h> +#include <stdio.h> + +int res = 0; + +int main(void) +{ + static long a, b; + + a = 0x12345678L; + + /* Test assignment */ + b = a; + if (b != a) { + res++; + } + + /* Test increment */ + b++; + if (b != 0x12345679L) { + res++; + } + + /* Test decrement */ + b--; + if (b != 0x12345678L) { + res++; + } + + /* Test pre-decrement with test */ + if (--b != 0x12345677L) { + res++; + } + + a = --b; + if (a != 0x12345676L) { + res++; + } + + return res; +} diff --git a/test/val/stpcpy.c b/test/val/stpcpy.c new file mode 100644 index 000000000..1cc6458ed --- /dev/null +++ b/test/val/stpcpy.c @@ -0,0 +1,53 @@ +// 2024-07-15 Sven Michael Klose <pixel@hugbox.org> + +#include <assert.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#define STR_SHORT "Hello, World!" +#define STR_LONG "This is a longer test string for stpcpy." + +char dest[512]; +char multi_page[300]; + +int +main () +{ + const char *src_empty; + const char *src_short; + const char *src_long; + char *end; + + src_empty = ""; + end = stpcpy (dest, src_empty); + assert(!strcmp (dest, src_empty)); + assert(!*end); + assert(end == dest); + printf ("Test 1 passed.\n"); + + src_short = STR_SHORT; + end = stpcpy (dest, src_short); + assert(!strcmp (dest, src_short)); + assert(!*end); + assert(end == &dest[sizeof (STR_SHORT) - 1]); + printf ("Test 2 passed.\n"); + + src_long = STR_LONG; + end = stpcpy (dest, src_long); + assert(!strcmp (dest, src_long)); + assert(!*end); + assert(end == &dest[sizeof (STR_LONG) - 1]); + printf ("Test 3 passed.\n"); + + memset(multi_page, 'a', sizeof(multi_page)-1); + multi_page[sizeof(multi_page)-1] = '\0'; + end = stpcpy (dest, multi_page); + assert(!strcmp (dest, multi_page)); + assert(!*end); + assert(end == &dest[sizeof (multi_page) - 1]); + printf ("Test 4 passed.\n"); + + printf ("All tests passed.\n"); + return EXIT_SUCCESS; +} diff --git a/test/val/strstr-test.c b/test/val/strstr-test.c new file mode 100644 index 000000000..5c8a147b0 --- /dev/null +++ b/test/val/strstr-test.c @@ -0,0 +1,41 @@ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +int fails = 0; + +#define STRSTR_TEST(needle,expected) \ + if (strstr(haystack, (needle)) != (expected)) { \ + printf("strstr failure: expected %p for \"%s\", " \ + "got %p\n", \ + expected, needle, strstr(haystack, (needle)));\ + fails++; \ + } + +#define STRCASESTR_TEST(needle,expected) \ + if (strcasestr(haystack, (needle)) != (expected)) { \ + printf("strcasestr failure: expected %p for \"%s\", " \ + "got %p\n", \ + expected, needle, strcasestr(haystack, (needle)));\ + fails++; \ + } + +int main (void) +{ + const char *haystack = "This is a string to search in"; + + STRSTR_TEST("This is", haystack + 0); + STRSTR_TEST("a string", haystack + 8); + STRSTR_TEST("This is a string to search in", haystack); + STRSTR_TEST("search in", haystack + 20); + STRSTR_TEST("This is a string to search in with extra chars", NULL); + STRSTR_TEST("nowhere", NULL); + + STRCASESTR_TEST("this is", haystack + 0); + STRCASESTR_TEST("a STRING", haystack + 8); + STRCASESTR_TEST("this is a string TO search in", haystack); + STRCASESTR_TEST("This is a string to search in with extra chars", NULL); + STRCASESTR_TEST("search IN", haystack + 20); + + return fails; +} diff --git a/test/val/strtok.c b/test/val/strtok.c new file mode 100644 index 000000000..4d63bfb2a --- /dev/null +++ b/test/val/strtok.c @@ -0,0 +1,41 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +void +error (void) +{ + printf ("strtok() test failed!\n"); + exit (EXIT_FAILURE); +} + +void +test (char * s) +{ + if (strcmp ("test", strtok (s, "/"))) + error (); + if (strcmp ("foo", strtok (NULL, "/"))) + error (); + if (strcmp ("bar", strtok (NULL, "/"))) + error (); + if (strtok (NULL, "/")) + error (); + if (strtok (NULL, "/")) + error (); +} + +int +main (void) +{ + char s1[] = "test/foo/bar"; + char s2[] = "/test/foo/bar"; + char s3[] = "//test/foo/bar"; + char s4[] = "//test/foo/bar//"; + + test (s1); + test (s2); + test (s3); + test (s4); + + return EXIT_SUCCESS; +} diff --git a/test/val/sub3.c b/test/val/sub3.c new file mode 100644 index 000000000..dd050224e --- /dev/null +++ b/test/val/sub3.c @@ -0,0 +1,219 @@ +/* + !!DESCRIPTION!! Subtraction Test + !!ORIGIN!! SDCC regression tests + !!LICENCE!! GPL, read COPYING.GPL +*/ + +#include <stdio.h> +#include <limits.h> + +unsigned char failures=0; + +int int0 = 5; +unsigned int int1 = 5; + +void pre_dec_test(void) +{ + if(int0 != 5) + failures++; + if(int1 != 5) + failures++; + + --int0; + --int1; + + if(int0 != 4) + failures++; + if(int1 != 4) + failures++; + + --int0; + --int1; + --int0; + --int1; + --int0; + --int1; + --int0; + --int1; + + if(int0 != 0) + failures++; + if(int1 != 0) + failures++; + + --int0; + --int1; + + if(int0 != -1) + failures++; + if(int1 != 65535U) + failures++; +} + +void post_dec_test(void) +{ + if(int0 != 5) + failures++; + if(int1 != 5) + failures++; + + int0--; + int1--; + + if(int0 != 4) + failures++; + if(int1 != 4) + failures++; + + int0--; + int1--; + int0--; + int1--; + int0--; + int1--; + int0--; + int1--; + + if(int0 != 0) + failures++; + if(int1 != 0) + failures++; + + int0--; + int1--; + + if(int0 != -1) + failures++; + if(int1 != 65535U) + failures++; +} + +void pre_dec_assign_test(void) +{ + int a; + unsigned int b; + if(int0 != 5) + failures++; + if(int1 != 5) + failures++; + + a = --int0; + b = --int1; + + if(int0 != 4 || a != int0) + failures++; + if(int1 != 4 || b != int1) + failures++; + + a = --int0; + b = --int1; + a = --int0; + b = --int1; + a = --int0; + b = --int1; + a = --int0; + b = --int1; + + if(int0 != 0 || a != int0) + failures++; + if(int1 != 0 || b != int1) + failures++; + + a = --int0; + b = --int1; + + if(int0 != -1 || a != int0) + failures++; + if(int1 != 65535U || b != int1) + failures++; +} + +void post_dec_assign_test(void) +{ + int a; + unsigned int b; + if(int0 != 5) + failures++; + if(int1 != 5) + failures++; + + a = int0--; + b = int1--; + + if(int0 != 4 || a != 5) + failures++; + if(int1 != 4 || b != 5) + failures++; + + a = int0--; + b = int1--; + a = int0--; + b = int1--; + a = int0--; + b = int1--; + a = int0--; + b = int1--; + + if(int0 != 0 || a != 1) + failures++; + if(int1 != 0 || b != 1) + failures++; + + a = int0--; + b = int1--; + + if(int0 != -1 || a != 0) + failures++; + if(int1 != 65535U || b != 0) + failures++; +} + +void dex_tests(void) { + static unsigned int a, b; + + a = 257; + b = a - 1; + if (b != 256) { + printf("fail 257 => 256\n"); + failures++; + } + + a = 256; + b = a - 1; + if (b != 255) { + printf("fail 256 => 255\n"); + failures++; + } + + a = 255; + b = a - 1; + if (b != 254) { + printf("fail 255 => 254\n"); + failures++; + } +} + +int main(void) +{ + int0 = 5; + int1 = 5; + pre_dec_test(); + + int0 = 5; + int1 = 5; + post_dec_test(); + + int0 = 5; + int1 = 5; + pre_dec_assign_test(); + + int0 = 5; + int1 = 5; + post_dec_assign_test(); + + dex_tests(); + + printf("failures: %d\n",failures); + + return failures; +} diff --git a/test/val/time-test2.c b/test/val/time-test2.c new file mode 100644 index 000000000..725bbd0e6 --- /dev/null +++ b/test/val/time-test2.c @@ -0,0 +1,61 @@ +/* Another test for time() */ + +#include <stdio.h> +#include <time.h> + +static int failures = 0; +#define INV_TIME ((time_t)-1) +#define TEST_TIME ((time_t)0x78AB1234) + +/* We supply our own clock_gettime function so we can control the values +** supplied to time() internally. +*/ +static time_t timeval; +static int timeres; +int __fastcall__ clock_gettime (clockid_t, struct timespec *tp) +{ + /* Don't touch tp in case of an error */ + if (timeres != -1) { + tp->tv_sec = timeval; + tp->tv_nsec = 0; + } + return timeres; +} + +int main() +{ + time_t res, pres; + + /* First test: Force time() to return an error. Check that both, the + ** returned value and the value passed via pointer are (time_t)-1. + */ + timeval = 42; + timeres = -1; + res = time(&pres); + if (res != INV_TIME || pres != INV_TIME) { + printf("Error in test 1\n"); + ++failures; + } + + /* Second test: Return a valid value and check both results */ + timeval = TEST_TIME; + timeres = 0; + res = time(&pres); + if (res != TEST_TIME || pres != TEST_TIME) { + printf("Error in test 2\n"); + ++failures; + } + + /* Third test: Return no error but an invalid value and check both + ** results + */ + timeval = INV_TIME; + timeres = 0; + res = time(&pres); + if (res != INV_TIME || pres != INV_TIME) { + printf("Error in test 3\n"); + ++failures; + } + + return failures; +} diff --git a/test/val/trampoline.c b/test/val/trampoline.c index 8f1e1547c..d3e73b47d 100644 --- a/test/val/trampoline.c +++ b/test/val/trampoline.c @@ -22,23 +22,23 @@ void func3() { } -#pragma wrapped-call(push, trampoline_inc, 0) - void func2() { func3(); } +#pragma wrapped-call(push, trampoline_inc, 0) + +void func2(void); + #pragma wrapped-call(push, trampoline_set, 4) -void func1(void); - -#pragma wrapped-call(pop) -#pragma wrapped-call(pop) - void func1(void) { func2(); } +#pragma wrapped-call(pop) +#pragma wrapped-call(pop) + int main(void) { flag = 0; diff --git a/test/val/zx02.c b/test/val/zx02.c new file mode 100644 index 000000000..0c123f968 --- /dev/null +++ b/test/val/zx02.c @@ -0,0 +1,416 @@ +/* + !!DESCRIPTION!! zx02 decompression + !!ORIGIN!! cc65 regression tests + !!LICENCE!! BSD 2-clause + !!AUTHOR!! Colin Leroy-Mira +*/ + +#include <zlib.h> +#include <stdio.h> +#include <zx02.h> + +/* The sample data is the original lz4.h, compressed with: + * zx02 lz4.h lz4.zx02 + * + * We reused lz4.h from the LZ4 test to have a matching adler32 sum. + */ +static const unsigned char compressed[] = { + 0xa2, 0x2f, 0x2a, 0x0a, 0x20, 0x2e, 0x42, 0x4c, 0x5a, 0x34, 0x20, 0x2d, 0x20, 0x46, 0x61, 0x73, + 0x74, 0x15, 0xf9, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x20, + 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x4e, 0x4d, 0x4d, 0x48, 0x65, 0x61, 0x64, + 0x65, 0x72, 0x4e, 0x69, 0x6c, 0x65, 0x4a, 0x1d, 0x41, 0x43, 0x6f, 0x70, 0x79, 0x36, 0x67, 0x2b, + 0x68, 0x43, 0x28, 0x43, 0x29, 0x20, 0x32, 0x30, 0x31, 0x31, 0x2d, 0x09, 0xd0, 0x35, 0x2c, 0x20, + 0x59, 0x61, 0x6e, 0x6e, 0x3b, 0x4d, 0x6c, 0x4c, 0x74, 0x2e, 0x0a, 0x4d, 0x53, 0x42, 0x53, 0x44, + 0x3a, 0x3e, 0x15, 0x2d, 0x43, 0x6c, 0x61, 0x75, 0x73, 0x65, 0x20, 0x4c, 0x69, 0x63, 0x65, 0x0e, + 0x68, 0x39, 0x70, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x14, 0x00, 0x2e, 0x92, 0x25, 0x35, 0x6f, + 0x75, 0x72, 0x32, 0x29, 0x14, 0x72, 0x67, 0x2f, 0x6c, 0x33, 0x45, 0x96, 0x73, 0x2f, 0x62, 0x73, + 0x64, 0x2d, 0x19, 0x2d, 0x2e, 0x70, 0x68, 0x70, 0x29, 0x22, 0x9b, 0x99, 0x52, 0x65, 0x64, 0x69, + 0x52, 0x21, 0xf2, 0x62, 0x75, 0x94, 0x45, 0xd8, 0x14, 0x64, 0xaf, 0x53, 0x69, 0x14, 0x85, 0x48, + 0x23, 0xe0, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x0a, 0x66, 0x06, 0x72, 0x6d, 0x73, 0x2c, 0x77, + 0x05, 0x85, 0x20, 0x14, 0x24, 0x0f, 0x46, 0x52, 0x74, 0x8b, 0x21, 0x6d, 0x6f, 0x66, 0x15, 0xb4, + 0x61, 0x87, 0x56, 0x40, 0x56, 0x6c, 0x51, 0x06, 0x56, 0x3e, 0x74, 0x4c, 0xba, 0x12, 0x72, 0x6f, + 0x76, 0x11, 0x64, 0x11, 0x51, 0x5a, 0x3e, 0x09, 0x58, 0x36, 0x8e, 0x9a, 0x48, 0x6f, 0x8a, 0x6e, + 0x67, 0x15, 0x63, 0x5c, 0x70, 0x42, 0x69, 0x73, 0x92, 0x93, 0x60, 0xcc, 0x3a, 0xd3, 0x01, 0x19, + 0x2a, 0x3d, 0xe4, 0x4a, 0x99, 0x6f, 0x66, 0x2f, 0xd5, 0x74, 0xa6, 0x64, 0x5a, 0x56, 0xa4, 0x45, + 0x72, 0x64, 0x61, 0x98, 0x51, 0xb3, 0x88, 0x62, 0x47, 0xd6, 0x35, 0x76, 0x97, 0x41, 0x35, 0x6e, + 0x44, 0x72, 0x5c, 0x2c, 0x3d, 0x48, 0x90, 0x20, 0x6c, 0x51, 0x5e, 0x87, 0x69, 0xef, 0x67, 0x12, + 0x29, 0x84, 0x64, 0x50, 0x16, 0x63, 0xa2, 0x26, 0x12, 0x72, 0x2e, 0x6b, 0x11, 0x5c, 0x50, 0x2b, + 0xf6, 0x76, 0x11, 0x61, 0xd6, 0x2a, 0x75, 0x62, 0xda, 0x17, 0xd1, 0x81, 0x8b, 0x1d, 0x67, 0x17, + 0xe9, 0x58, 0x93, 0xcb, 0xd3, 0x28, 0x46, 0x6f, 0x63, 0x75, 0x22, 0x6e, 0x69, 0xea, 0x65, 0xc6, + 0x2f, 0x51, 0x04, 0x92, 0x33, 0x14, 0x0a, 0x6d, 0x26, 0x0a, 0x6d, 0x69, 0xd0, 0x77, 0x96, 0xed, + 0x68, 0x5d, 0xd1, 0x39, 0x58, 0x81, 0x87, 0xf6, 0xcc, 0x9b, 0xf9, 0x54, 0x48, 0x49, 0x53, 0x20, + 0x53, 0x4f, 0x46, 0x54, 0x57, 0x41, 0x52, 0x45, 0x20, 0x0a, 0x17, 0x82, 0x50, 0x52, 0x4f, 0x56, + 0x49, 0x44, 0x45, 0x44, 0x44, 0x42, 0x59, 0x39, 0x24, 0xa0, 0x43, 0x4f, 0x50, 0x59, 0xa9, 0x49, + 0x47, 0x48, 0x54, 0x20, 0x48, 0x4f, 0x4c, 0x13, 0x2e, 0x52, 0x42, 0x42, 0x41, 0x4e, 0x44, 0x2b, + 0x22, 0x4e, 0x54, 0x05, 0x42, 0x55, 0x4f, 0x20, 0x4d, 0x8d, 0x22, 0x41, 0x53, 0x05, 0x79, 0x22, + 0x37, 0x44, 0x07, 0x72, 0xd1, 0x45, 0x58, 0x50, 0x9c, 0x53, 0x52, 0x26, 0x3c, 0x2c, 0xc9, 0x4d, + 0x50, 0x4c, 0x49, 0x45, 0x04, 0xc1, 0x52, 0x34, 0x49, 0x54, 0x14, 0x53, 0x2c, 0x20, 0x26, 0x4e, + 0x43, 0x91, 0x55, 0x44, 0x0a, 0x47, 0x50, 0x14, 0x86, 0xb2, 0x4e, 0x1f, 0x54, 0x24, 0x73, 0x54, + 0x91, 0x49, 0x54, 0x53, 0x15, 0xa8, 0x2c, 0xf5, 0x2e, 0x73, 0x40, 0x9e, 0x46, 0x62, 0x4d, 0x06, + 0x43, 0x43, 0x48, 0x1d, 0x45, 0x41, 0x42, 0x49, 0x3a, 0x54, 0xd4, 0x44, 0xe5, 0x46, 0x10, 0x45, + 0x4e, 0xdd, 0x0e, 0x45, 0xde, 0x91, 0x24, 0x94, 0x8a, 0x64, 0x5e, 0xd4, 0x43, 0x55, 0x4c, 0x0c, + 0x14, 0x0d, 0x55, 0x50, 0x4f, 0x53, 0x65, 0x9a, 0xbf, 0x25, 0xe4, 0x53, 0x43, 0x26, 0x46, 0xae, + 0xa8, 0x2e, 0x11, 0x07, 0xef, 0x89, 0x50, 0x56, 0x45, 0x74, 0x94, 0x00, 0xa0, 0x99, 0x4c, 0x4c, + 0xdd, 0xb5, 0x8f, 0x25, 0x4f, 0x57, 0xaa, 0x59, 0x80, 0xa6, 0xdd, 0xa6, 0x54, 0x62, 0x4a, 0xf0, + 0xfa, 0x95, 0x4c, 0x45, 0xdd, 0x94, 0xdb, 0xad, 0x49, 0xb6, 0x43, 0x54, 0x94, 0xad, 0x13, 0xb0, + 0x43, 0x05, 0x44, 0x68, 0x14, 0xac, 0x2c, 0x91, 0xd4, 0x53, 0x50, 0x45, 0x1e, 0x17, 0x41, 0xdc, + 0x58, 0x86, 0xa9, 0x25, 0x14, 0x59, 0x2c, 0xad, 0x98, 0x30, 0x91, 0x51, 0x55, 0x4f, 0x12, 0x39, + 0x8a, 0x9c, 0x41, 0x4d, 0x41, 0x47, 0x3a, 0x8a, 0x20, 0x28, 0xad, 0xa4, 0x49, 0x65, 0x43, 0xac, + 0x94, 0x1c, 0x73, 0x25, 0x46, 0xbc, 0x55, 0xb6, 0x28, 0x04, 0xdc, 0x54, 0x3c, 0xc1, 0x20, 0x47, + 0x4f, 0x44, 0xc9, 0xed, 0x27, 0xb0, 0x52, 0x56, 0x45, 0x02, 0x9c, 0x3b, 0x70, 0x9c, 0xfa, 0x71, + 0x85, 0x55, 0x24, 0x94, 0x19, 0xcf, 0x05, 0x54, 0xfd, 0x57, 0x8b, 0x15, 0x71, 0x42, 0x17, 0x15, + 0x42, 0x3a, 0xe0, 0xd5, 0x85, 0x0a, 0x72, 0x8a, 0xdc, 0x26, 0x55, 0x50, 0x1a, 0x38, 0x29, 0x77, + 0xe6, 0x04, 0x18, 0x43, 0x53, 0x15, 0x41, 0x3e, 0xf6, 0xc9, 0xd7, 0x65, 0x2c, 0xe7, 0x1d, 0x91, + 0x5f, 0x45, 0x72, 0x59, 0xb5, 0x66, 0x36, 0x99, 0x1b, 0xa0, 0x70, 0x57, 0x95, 0x2e, 0x62, 0x1c, + 0x8f, 0x6b, 0xc7, 0x41, 0x44, 0x1d, 0x53, 0x0e, 0x24, 0x49, 0x4d, 0xe5, 0xe5, 0x71, 0x99, 0x96, + 0xb1, 0x38, 0xdf, 0x61, 0x20, 0x02, 0x47, 0x75, 0x3c, 0x06, 0x1e, 0x95, 0x90, 0x38, 0x04, 0x41, + 0x91, 0x57, 0x08, 0xf0, 0x29, 0x55, 0x52, 0x0c, 0x41, 0x56, 0x06, 0xf9, 0xc4, 0x23, 0xed, 0x65, + 0xdc, 0xf5, 0x46, 0x47, 0x55, 0xb3, 0x91, 0x79, 0x1a, 0x9d, 0xb5, 0xca, 0xa5, 0xbb, 0x08, 0x66, + 0x46, 0x76, 0x44, 0x16, 0x17, 0x7f, 0x5f, 0x6c, 0x1f, 0x58, 0x9a, 0x13, 0xdc, 0x7d, 0xdb, 0x76, + 0xa0, 0xfb, 0x7c, 0x29, 0xc6, 0x59, 0xb0, 0xc4, 0x20, 0x7d, 0xae, 0xa9, 0x35, 0x6b, 0x23, 0xb4, + 0x63, 0x74, 0x9b, 0xc0, 0x40, 0x68, 0x24, 0x72, 0x1a, 0x51, 0x3a, 0x45, 0x2d, 0xe9, 0xcf, 0x6e, + 0x66, 0x17, 0xa4, 0xf1, 0x9f, 0x6f, 0x73, 0x44, 0xcc, 0x3c, 0x79, 0x38, 0x7a, 0x20, 0x47, 0x13, + 0x73, 0xa0, 0x15, 0x7c, 0x67, 0xef, 0x37, 0x75, 0x62, 0x2e, 0xb0, 0x01, 0xd2, 0x2f, 0x43, 0x79, + 0x98, 0xa4, 0x34, 0x39, 0x37, 0x33, 0x2f, 0x6c, 0x7a, 0x34, 0x79, 0xb1, 0x70, 0x7a, 0x3a, 0x1a, + 0x1d, 0xd6, 0x7f, 0xa4, 0x9a, 0x6f, 0xf1, 0x72, 0x4f, 0xa8, 0x10, 0x49, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x7d, 0x52, 0x43, 0x0a, 0x49, 0x23, 0x21, 0x0f, 0x4e, 0x87, 0xd9, 0x63, 0x0a, + 0x2a, 0x2f, 0x0a, 0x23, 0x70, 0x72, 0x61, 0x67, 0x6d, 0x61, 0x20, 0x46, 0x85, 0x02, 0x0a, 0x1a, + 0xbc, 0xfe, 0xa0, 0x20, 0x64, 0x65, 0x66, 0xc2, 0x6e, 0x65, 0x64, 0x81, 0x28, 0x5f, 0x5f, 0x63, + 0x6c, 0xb9, 0x22, 0x5e, 0x07, 0x89, 0xca, 0x65, 0x78, 0xac, 0x25, 0x6b, 0x30, 0x70, 0x22, 0x43, + 0x22, 0x5a, 0x7b, 0x4c, 0xde, 0x6c, 0x85, 0x53, 0x5c, 0xf9, 0x55, 0x04, 0x2a, 0x95, 0x26, 0x2e, + 0x68, 0xb7, 0x53, 0x06, 0x73, 0x01, 0x1c, 0x6f, 0x6b, 0xf9, 0x63, 0xa2, 0x5b, 0x66, 0x75, 0xb8, + 0x88, 0x35, 0x6e, 0x2c, 0x26, 0x37, 0x14, 0xa4, 0x76, 0x30, 0x29, 0x6e, 0x4d, 0x3a, 0x56, 0x75, + 0x66, 0x66, 0x72, 0xac, 0x43, 0x56, 0x7c, 0x1c, 0x51, 0xf6, 0x8d, 0x67, 0x86, 0x22, 0x6d, 0xf5, + 0x6d, 0x46, 0xb7, 0x49, 0x1c, 0x1e, 0x79, 0x8d, 0x66, 0x1e, 0x51, 0x20, 0x3b, 0x67, 0x5d, 0xf0, + 0x30, 0x78, 0xed, 0xb3, 0x60, 0x1d, 0x6f, 0x2d, 0x45, 0x24, 0x1b, 0xde, 0x5d, 0x1c, 0xdb, 0x04, + 0x4b, 0x64, 0x3c, 0x04, 0x61, 0x28, 0x19, 0x56, 0x36, 0xe8, 0xe1, 0xf9, 0xd5, 0x49, 0xd8, 0x15, + 0xa5, 0x4a, 0x25, 0x7b, 0x28, 0xc1, 0x95, 0x29, 0x2c, 0xbf, 0x96, 0x1f, 0x95, 0x47, 0x86, 0xa3, + 0x3f, 0x71, 0x94, 0x62, 0x69, 0x54, 0x00, 0xd1, 0x8c, 0x31, 0x1d, 0xaf, 0xf8, 0x9b, 0x50, 0xd5, + 0x84, 0x3c, 0x78, 0xf9, 0x36, 0x67, 0x2a, 0x7c, 0x7f, 0x16, 0x5f, 0xde, 0xc8, 0x96, 0xb7, 0x52, + 0xeb, 0xab, 0x61, 0xf5, 0x6f, 0xca, 0x49, 0x1e, 0x2e, 0x64, 0x2e, 0xc7, 0x9f, 0x52, 0x27, 0x01, + 0xe9, 0x68, 0x56, 0x80, 0x67, 0x56, 0x7a, 0x54, 0x3f, 0x14, 0x01, 0xaf, 0x69, 0x57, 0x68, 0x35, + 0xd9, 0xa3, 0x1f, 0x5f, 0x07, 0x9d, 0x53, 0xc1, 0xb7, 0x5f, 0xb1, 0xdc, 0x4a, 0xe1, 0xc7, 0x05, + 0x01, 0x31, 0x09, 0x5b, 0xf8, 0x46, 0x11, 0x20, 0x61, 0x86, 0x1c, 0x6b, 0x87, 0xf7, 0x31, 0x4d, + 0x66, 0xb7, 0xf8, 0x19, 0x43, 0xa2, 0x75, 0x1e, 0x28, 0x44, 0x3f, 0x8f, 0x23, 0x49, 0x4e, 0x82, + 0x37, 0xf6, 0xc2, 0x0c, 0x77, 0x18, 0x6e, 0x42, 0x5c, 0x2d, 0xa1, 0x4f, 0x29, 0xa3, 0x27, 0x61, + 0x70, 0x1d, 0xf6, 0x69, 0x60, 0xd4, 0x98, 0xac, 0xab, 0xfd, 0xac, 0xb8, 0x07, 0x4c, 0x41, 0xd9, + 0x64, 0x3b, 0xb2, 0x36, 0x74, 0x77, 0xda, 0x16, 0x09, 0xf9, 0x67, 0x2d, 0x04, 0x62, 0x78, 0x15, + 0x45, 0x2b, 0x7a, 0xb1, 0x2a, 0x6c, 0xa7, 0xa0, 0xe5, 0x23, 0x3f, 0x9b, 0x29, 0x4e, 0x55, 0x4d, + 0x42, 0x66, 0x14, 0x22, 0x2a, 0xff, 0x4d, 0x60, 0x31, 0x30, 0x30, 0x49, 0x07, 0x20, 0x2b, 0x8b, + 0xa9, 0x93, 0x31, 0x62, 0x2f, 0xf7, 0x90, 0xac, 0xc4, 0x74, 0xb1, 0x76, 0xa3, 0x15, 0x1f, 0x4e, + 0x16, 0x32, 0x62, 0x12, 0x07, 0xe6, 0x76, 0x89, 0x94, 0x29, 0x3b, 0xa3, 0x99, 0xe8, 0x79, 0x54, + 0xc8, 0xd7, 0xe3, 0x6d, 0x26, 0x29, 0x71, 0x47, 0xa2, 0xab, 0xfd, 0xf6, 0x89, 0x64, 0x3b, 0x06, + 0x4d, 0x4d, 0xf2, 0x86, 0x26, 0x59, 0x5f, 0xb1, 0x85, 0xa9, 0x94, 0x46, 0x2b, 0x4d, 0xc9, 0xef, + 0xb0, 0xdf, 0x69, 0x61, 0x76, 0xab, 0x27, 0x45, 0xc4, 0x68, 0x61, 0x32, 0xac, 0x20, 0x4e, 0x2d, + 0x3e, 0x32, 0x5e, 0x4e, 0x20, 0x42, 0x46, 0xce, 0x73, 0x6a, 0x58, 0x5e, 0x34, 0xe2, 0x50, 0x30, + 0x12, 0x31, 0x65, 0x31, 0xd0, 0x34, 0x34, 0x0a, 0x4b, 0x42, 0x3b, 0x14, 0x28, 0x32, 0x21, 0x34, + 0x20, 0x02, 0x17, 0x36, 0x84, 0x36, 0x19, 0x44, 0x16, 0x32, 0x45, 0xc3, 0x4d, 0x63, 0x4c, 0x7c, + 0x63, 0x2e, 0x29, 0xcb, 0x9a, 0x9b, 0x3e, 0x02, 0x63, 0x73, 0x84, 0x6d, 0xe3, 0xf6, 0xad, 0x62, + 0xa9, 0xab, 0x5a, 0xb2, 0x9a, 0x9b, 0x1f, 0x72, 0x59, 0x7d, 0x39, 0x06, 0x52, 0xb8, 0x4b, 0x56, + 0x06, 0x65, 0x9f, 0x57, 0x99, 0x6d, 0x7d, 0xdf, 0x5a, 0x3c, 0x58, 0xb8, 0x4a, 0x76, 0x11, 0x74, + 0x33, 0xe5, 0x06, 0x10, 0xa1, 0xaa, 0x66, 0xa8, 0x2f, 0x50, 0x7d, 0x44, 0x6d, 0x8c, 0x80, 0x9c, + 0x74, 0xd5, 0x76, 0x61, 0x6c, 0x40, 0x66, 0x36, 0xc8, 0x31, 0x34, 0x2c, 0x58, 0xc7, 0x5b, 0x66, + 0x4e, 0xb8, 0xba, 0x25, 0x77, 0x5e, 0xe5, 0x02, 0x08, 0xb1, 0x2a, 0x79, 0x6a, 0x2c, 0x59, 0x11, + 0x3c, 0x61, 0x8a, 0x55, 0x80, 0xe6, 0x6c, 0xdc, 0x78, 0x38, 0x36, 0x62, 0x4c, 0x31, 0xa5, 0x6d, + 0x0a, 0x5d, 0xa7, 0x7d, 0x8d, 0x1a, 0xa8, 0x0a, 0x3e, 0x8d, 0x86, 0x53, 0x1a, 0x79, 0x81, 0xf1, + 0x46, 0xb9, 0x65, 0xda, 0x3a, 0x8d, 0x9b, 0x29, 0x95, 0xb9, 0x17, 0xc1, 0x5f, 0x8c, 0xc1, 0x55, + 0x28, 0x20, 0x8c, 0x34, 0x9b, 0x52, 0x1e, 0x32, 0x2a, 0x98, 0xd5, 0x59, 0x2c, 0x1b, 0x54, 0x42, + 0x30, 0x16, 0x71, 0x59, 0x2f, 0x24, 0xf8, 0x7a, 0x65, 0x1f, 0xde, 0x89, 0x22, 0x78, 0x44, 0x15, + 0x39, 0x21, 0xe4, 0x0e, 0xb5, 0xa5, 0x5e, 0x29, 0xb9, 0xc2, 0xd8, 0x66, 0x4a, 0xb5, 0xf6, 0xf3, + 0x37, 0x97, 0xbd, 0x96, 0x31, 0x95, 0xcd, 0xe5, 0x15, 0x8e, 0x83, 0x87, 0x29, 0x97, 0x1b, 0xad, + 0xbd, 0x5d, 0x63, 0xa1, 0x18, 0x27, 0x8b, 0x63, 0x02, 0x27, 0x1e, 0x62, 0x79, 0x93, 0xa3, 0x3d, + 0x72, 0x6f, 0x6d, 0x65, 0x3d, 0x34, 0x5a, 0x6d, 0x36, 0x71, 0x9b, 0xca, 0x09, 0xc5, 0x64, 0x79, + 0x1e, 0x0f, 0xc6, 0x3f, 0xd5, 0x84, 0xe8, 0x46, 0x91, 0x47, 0x27, 0x77, 0x63, 0xa9, 0x27, 0x45, + 0x99, 0x2a, 0x8b, 0xf5, 0x45, 0x7c, 0x2e, 0xed, 0xa6, 0x86, 0x1f, 0xd2, 0x4f, 0x7c, 0x67, 0x75, + 0xc7, 0x5e, 0xa4, 0xcd, 0x25, 0x78, 0x68, 0x06, 0x57, 0x63, 0x15, 0xa5, 0xb0, 0x75, 0xb3, 0x18, + 0x20, 0x3e, 0x3d, 0x99, 0xf1, 0x42, 0x9e, 0x20, 0x86, 0xa4, 0x28, 0x74, 0x6b, 0xb6, 0x38, 0x29, + 0x98, 0x49, 0x74, 0x23, 0x41, 0x2a, 0x20, 0x59, 0x3a, 0xb2, 0xda, 0x67, 0x78, 0x14, 0x46, 0x44, + 0x1f, 0xd9, 0xe0, 0x84, 0x5d, 0x32, 0x2c, 0x4b, 0x79, 0xc0, 0x46, 0x72, 0xc3, 0x1e, 0xec, 0x74, + 0xd5, 0xa1, 0x6f, 0x97, 0xdc, 0xb5, 0x5f, 0xf9, 0xb5, 0xde, 0x5b, 0xef, 0xdc, 0x50, 0xd9, 0xb7, + 0x29, 0x01, 0x99, 0xf9, 0xde, 0xe2, 0x7b, 0xb0, 0x5e, 0xbb, 0xe6, 0x9a, 0x68, 0xf3, 0x86, 0x64, + 0x67, 0x95, 0x4a, 0x9f, 0xf6, 0xc3, 0x96, 0x36, 0xf6, 0x0e, 0xf1, 0xa5, 0x5f, 0x58, 0xf0, 0xdd, + 0xcc, 0x54, 0xf0, 0x01, 0x7f, 0x2a, 0x25, 0x73, 0xe3, 0xb4, 0x5b, 0x68, 0x37, 0x75, 0x21, 0x9d, + 0x98, 0xf2, 0x8c, 0x2b, 0x65, 0x41, 0x67, 0xb4, 0x29, 0xd9, 0x65, 0x71, 0x75, 0x68, 0xb1, 0x1b, + 0x77, 0xd5, 0xf5, 0xc3, 0x49, 0x20, 0x5b, 0x9f, 0x4c, 0x26, 0xe7, 0xe4, 0xc0, 0x65, 0xc9, 0x54, + 0x68, 0x4b, 0x26, 0xa9, 0x06, 0x6e, 0xf1, 0x13, 0xf0, 0xf6, 0x72, 0x69, 0xa1, 0x7e, 0x86, 0x2d, + 0x5a, 0x1e, 0x7e, 0x6d, 0x51, 0x56, 0xa4, 0x80, 0x42, 0x95, 0x97, 0x3f, 0xd9, 0xd7, 0xd7, 0x43, + 0x45, 0xaf, 0x01, 0x9c, 0xdb, 0xb4, 0x1a, 0x06, 0x3a, 0x4d, 0x98, 0x3a, 0xb4, 0x85, 0x75, 0x70, + 0x7c, 0xd6, 0x07, 0xa7, 0xed, 0x6a, 0x33, 0x65, 0x83, 0x41, 0x58, 0x5f, 0x49, 0x4e, 0x50, 0xd7, + 0x54, 0x5f, 0x56, 0x41, 0x4c, 0x55, 0x45, 0x81, 0x7c, 0x0d, 0xf5, 0x80, 0xab, 0x35, 0x65, 0x7a, + 0xbd, 0xc7, 0x65, 0x58, 0x7c, 0xb3, 0x5f, 0x6d, 0x6e, 0x66, 0xed, 0x71, 0x4d, 0x28, 0xac, 0x73, + 0x9e, 0xf3, 0x97, 0x06, 0x62, 0xca, 0xef, 0xc5, 0x29, 0xbb, 0xdf, 0xa5, 0xe5, 0x85, 0x3e, 0x6e, + 0xb0, 0xd6, 0xaf, 0x7a, 0xaa, 0x35, 0xf5, 0x9c, 0xe6, 0x9b, 0x75, 0x07, 0xc6, 0x65, 0x89, 0x7f, + 0x7d, 0xc3, 0xc4, 0x4a, 0x63, 0x42, 0x89, 0x73, 0x61, 0xb8, 0x35, 0x26, 0x3c, 0x3d, 0x47, 0x4b, + 0x4f, 0x6e, 0x14, 0x98, 0x10, 0x4f, 0x91, 0xbd, 0x3d, 0x01, 0x91, 0x5f, 0x30, 0xe1, 0x39, 0xa6, + 0xc1, 0x9b, 0x47, 0xce, 0x72, 0x73, 0xda, 0xdc, 0x8b, 0xa5, 0x9f, 0x76, 0xd5, 0xa7, 0x6b, 0x97, + 0x28, 0x59, 0x53, 0x4d, 0x55, 0x28, 0x60, 0x14, 0x75, 0x0e, 0x17, 0xdd, 0x01, 0x53, 0x31, 0x61, + 0x9b, 0xa3, 0x03, 0x69, 0x0a, 0xca, 0x81, 0x4f, 0x97, 0xd7, 0x7d, 0x65, 0xab, 0x9f, 0xcc, 0xb3, + 0x0f, 0x69, 0xc9, 0xdd, 0x89, 0xf9, 0x77, 0xba, 0x81, 0xf5, 0xaa, 0x39, 0xef, 0xc9, 0x8b, 0x2e, + 0xcf, 0x77, 0x95, 0xd9, 0x5f, 0xf7, 0x2e, 0xa1, 0x18, 0x49, 0x5f, 0xbd, 0xe7, 0xaf, 0x17, 0x6c, + 0x90, 0xe1, 0x07, 0xa6, 0x6a, 0xcd, 0xee, 0x67, 0x68, 0x2c, 0x88, 0x03, 0x7f, 0x64, 0x90, 0xff, + 0x77, 0x67, 0x6e, 0xc9, 0xab, 0xf2, 0x8b, 0x5a, 0x38, 0x55, 0x57, 0x14, 0x78, 0x81, 0x5c, 0x72, + 0x44, 0x76, 0x49, 0x66, 0xda, 0x28, 0x3c, 0x30, 0xd7, 0x22, 0xd3, 0xd9, 0xaf, 0x79, 0x89, 0x91, + 0x7d, 0xc1, 0x05, 0x6d, 0x51, 0xd1, 0x5a, 0x74, 0x79, 0xb0, 0x85, 0x09, 0x6d, 0xaa, 0x5f, 0x27, + 0x86, 0xc7, 0xa6, 0x33, 0x93, 0xdd, 0x99, 0x03, 0xd5, 0xef, 0xdc, 0x5b, 0x18, 0x61, 0xe6, 0x61, + 0x67, 0x79, 0xf6, 0xd0, 0xa6, 0x85, 0x5a, 0xed, 0x6a, 0x7d, 0x21, 0x1f, 0x17, 0xc4, 0x99, 0xe1, + 0xd5, 0x74, 0x6e, 0x5b, 0x98, 0xb0, 0xca, 0x65, 0x5b, 0x84, 0xa3, 0x14, 0x08, 0x6c, 0x6f, 0x77, + 0x65, 0x78, 0x5f, 0x0c, 0xc7, 0x45, 0xa1, 0xb9, 0x7d, 0x6c, 0xac, 0x59, 0xe3, 0x39, 0x69, 0x82, + 0x69, 0x82, 0x80, 0xbf, 0x37, 0x23, 0xdb, 0x66, 0x16, 0x24, 0x65, 0x42, 0x7c, 0xc5, 0x6a, 0x74, + 0x3a, 0xe3, 0x77, 0x45, 0x6a, 0x7d, 0xe3, 0x2d, 0x41, 0x4b, 0xc2, 0x3f, 0x6f, 0x97, 0xef, 0xf3, + 0xc9, 0xec, 0x9a, 0x41, 0x64, 0x99, 0xfa, 0x50, 0x9f, 0xa0, 0xda, 0xcd, 0xb6, 0xa9, 0xd1, 0xda, + 0xde, 0x6b, 0x7e, 0xc1, 0xea, 0x5a, 0xf7, 0xe3, 0x28, 0x30, 0x78, 0x37, 0x45, 0x49, 0x01, 0xbb, + 0x36, 0xe5, 0xbc, 0x0e, 0xac, 0x31, 0x31, 0x33, 0x20, 0x39, 0x32, 0x39, 0x20, 0x32, 0x9e, 0x65, + 0x37, 0x91, 0xe3, 0x6a, 0x43, 0x4f, 0x4d, 0xb2, 0xd1, 0x01, 0x42, 0x55, 0xfe, 0x1d, 0x34, 0x28, + 0xe4, 0xb5, 0xeb, 0x26, 0x76, 0x28, 0x28, 0x6a, 0x3a, 0x06, 0x5c, 0x67, 0xb1, 0x65, 0x72, 0x27, + 0x2e, 0x3e, 0x20, 0x62, 0x03, 0xe0, 0x3f, 0x41, 0xe6, 0x3a, 0x70, 0x59, 0x2b, 0x96, 0x15, 0x29, + 0x2f, 0x32, 0x35, 0x35, 0x51, 0x1f, 0xfe, 0x29, 0xfd, 0xc9, 0x9f, 0x9f, 0x2f, 0x6a, 0x71, 0xed, + 0x50, 0xef, 0x66, 0x8b, 0xd7, 0xa1, 0xd7, 0x19, 0xb7, 0x06, 0xe8, 0xab, 0x56, 0x87, 0xbe, 0x34, + 0xef, 0x71, 0x6b, 0x69, 0x91, 0x1a, 0x42, 0x79, 0x71, 0x57, 0x69, 0xb4, 0xb3, 0x9a, 0x22, 0x77, + 0x6a, 0x52, 0xd7, 0x21, 0xb8, 0x49, 0x17, 0x22, 0x6c, 0x7c, 0xc2, 0x48, 0x26, 0x72, 0x68, 0x0c, + 0x5b, 0xdb, 0x5f, 0x0f, 0x52, 0x4b, 0x83, 0xde, 0xf1, 0x75, 0x9b, 0x20, 0x3e, 0xe7, 0x5a, 0xf2, + 0x8c, 0x29, 0x5a, 0x75, 0x94, 0xc5, 0xd7, 0x38, 0x6e, 0xb0, 0x99, 0xd1, 0xad, 0x09, 0xd5, 0x53, + 0x1f, 0xac, 0x72, 0xe1, 0x67, 0x5a, 0x94, 0xc4, 0x8b, 0x27, 0x99, 0x6d, 0x7d, 0xa7, 0x86, 0x4d, + 0xd9, 0xfa, 0xb0, 0xcb, 0xd5, 0x85, 0x29, 0xd7, 0x7f, 0x5a, 0xff, 0xec, 0xd1, 0xb5, 0xd1, 0x65, + 0x41, 0x48, 0xf6, 0x99, 0x57, 0x2d, 0x08, 0xec, 0x9e, 0xc0, 0xd4, 0x5b, 0xb9, 0xc5, 0x28, 0xce, + 0xe1, 0x95, 0x8b, 0x17, 0x95, 0x71, 0xbf, 0x35, 0x15, 0xeb, 0xc6, 0x4e, 0x76, 0xd6, 0x6f, 0x6b, + 0x8f, 0xab, 0x96, 0x25, 0x6a, 0x9d, 0xf1, 0xf7, 0x0a, 0x59, 0xe8, 0x8e, 0x93, 0x63, 0x85, 0xd8, + 0x45, 0x6a, 0xcf, 0x97, 0x9a, 0xdb, 0x2c, 0x9b, 0x3f, 0x72, 0xcd, 0x7e, 0x70, 0xdb, 0x6d, 0xae, + 0x9b, 0x5f, 0x6b, 0x8a, 0x1d, 0x9a, 0x76, 0xef, 0x9d, 0xb3, 0x96, 0x09, 0x9b, 0xbd, 0x9e, 0xcb, + 0xdf, 0x87, 0x2c, 0xb5, 0xe1, 0x5d, 0x62, 0x79, 0xbd, 0x3b, 0x00, 0x74, 0x6f, 0xf1, 0x28, 0x98, + 0x9a, 0x0b, 0xb9, 0x9f, 0x8a, 0x1e, 0x27, 0x86, 0x4b, 0x2e, 0xcf, 0x44, 0x2b, 0x99, 0xdb, 0xb3, + 0x4d, 0xf7, 0x5e, 0x7b, 0x28, 0x83, 0x7f, 0x53, 0xd5, 0xd3, 0x20, 0xcf, 0xe5, 0x9f, 0xa5, 0xeb, + 0x69, 0x9a, 0x6b, 0x19, 0x77, 0x27, 0x62, 0x3c, 0x07, 0x65, 0x6c, 0x63, 0xa5, 0x17, 0x9a, 0xb6, + 0x10, 0xaa, 0x63, 0x19, 0x68, 0x72, 0x99, 0x99, 0xba, 0x59, 0x5e, 0x2c, 0x94, 0xae, 0x75, 0x45, + 0xa2, 0x95, 0x77, 0x38, 0xf2, 0x2d, 0x4f, 0x51, 0x73, 0xb1, 0x6f, 0x36, 0xff, 0x57, 0x0d, 0x45, + 0x6f, 0xf7, 0x47, 0x5c, 0xdf, 0x15, 0x73, 0x2e, 0x8b, 0x61, 0x19, 0x37, 0x45, 0xcf, 0x8d, 0x6e, + 0xdd, 0x13, 0xba, 0x55, 0xe3, 0x36, 0xb0, 0xbc, 0x00, 0x1a, 0x2d, 0x17, 0xfc, 0x2e, 0x22, 0x5e, + 0xd5, 0x16, 0x9d, 0x58, 0xb2, 0x53, 0x69, 0x3a, 0xa8, 0xed, 0x6f, 0xe6, 0x71, 0x1e, 0x65, 0xb1, + 0xf3, 0xbb, 0x25, 0xf5, 0x83, 0x6f, 0x63, 0x80, 0x0d, 0x79, 0x89, 0x9b, 0xd1, 0xfb, 0x72, 0xf9, + 0x03, 0x7c, 0x7b, 0x29, 0x2b, 0x7e, 0x33, 0x25, 0x87, 0xcf, 0xbc, 0x15, 0xdb, 0xa6, 0x47, 0x18, + 0x6e, 0x83, 0xb9, 0xfd, 0x6d, 0x36, 0x22, 0x31, 0x22, 0xa8, 0x33, 0xa7, 0x66, 0x89, 0xc1, 0x24, + 0x67, 0x87, 0xf3, 0x3e, 0x99, 0x48, 0x9d, 0x57, 0x56, 0x7d, 0x82, 0x42, 0x3c, 0x1e, 0x30, 0xc9, + 0xf1, 0x95, 0x5d, 0x4c, 0xf5, 0x08, 0xc6, 0x6e, 0xe0, 0x12, 0xdb, 0xdc, 0x7f, 0x43, 0x43, 0x83, + 0xd9, 0x7f, 0x52, 0x41, 0x54, 0x90, 0xef, 0x44, 0x2d, 0x46, 0x41, 0x55, 0x4c, 0x54, 0xf1, 0x89, + 0x9a, 0x1c, 0xe8, 0x6f, 0x89, 0x63, 0x29, 0xaa, 0x77, 0xd5, 0xe9, 0x01, 0x1b, 0x2e, 0x76, 0x09, + 0x9f, 0xbd, 0x5e, 0x22, 0xe7, 0x8b, 0xe9, 0x76, 0x21, 0x69, 0xf1, 0xb1, 0x93, 0xb3, 0x95, 0xb1, + 0x5f, 0xa9, 0x8a, 0x11, 0x64, 0x74, 0x46, 0x65, 0xb6, 0xa7, 0x9a, 0xec, 0xa3, 0xe9, 0xa0, 0xbc, + 0x6a, 0x61, 0x2b, 0x7f, 0x06, 0x25, 0xc7, 0x2e, 0xaf, 0x8d, 0x4b, 0xb1, 0xbd, 0xae, 0x39, 0x55, + 0xad, 0x55, 0xda, 0x71, 0x04, 0x25, 0xa2, 0x17, 0x6c, 0xb1, 0x5b, 0x9d, 0x51, 0x22, 0x51, 0xbf, + 0x30, 0x83, 0x55, 0x7b, 0x37, 0x25, 0xd6, 0x6f, 0x99, 0x26, 0x0d, 0x94, 0x6b, 0x6f, 0x6b, 0x01, + 0x72, 0x77, 0x68, 0x1a, 0x07, 0x99, 0x32, 0xec, 0x66, 0xa7, 0xbd, 0x3f, 0xd2, 0xd9, 0xde, 0xa3, + 0x99, 0x6a, 0x95, 0xdb, 0x25, 0xd9, 0xee, 0x42, 0x42, 0xc5, 0x7b, 0x38, 0x2d, 0x25, 0xf3, 0x58, + 0xf5, 0xa5, 0xa8, 0x6d, 0x6f, 0x58, 0x67, 0x6d, 0x94, 0x86, 0x53, 0x94, 0xc9, 0x9a, 0x79, 0x70, + 0x64, 0x10, 0x77, 0x78, 0x29, 0xed, 0xa6, 0x6d, 0xc0, 0xa7, 0x95, 0x8d, 0xb4, 0x21, 0x6a, 0x27, + 0xe4, 0x33, 0x63, 0x2a, 0x61, 0x18, 0x27, 0x2d, 0x73, 0x2b, 0xa6, 0x9d, 0x8d, 0x9f, 0x91, 0x6a, + 0xe7, 0xad, 0x57, 0x35, 0x6b, 0xeb, 0x64, 0x2e, 0xc7, 0xe0, 0x2c, 0x69, 0xef, 0xfd, 0xac, 0x3b, + 0xa6, 0x9e, 0xed, 0xf1, 0x64, 0x36, 0x5d, 0x8b, 0xe3, 0x1a, 0x52, 0xe5, 0x2b, 0xa7, 0x14, 0xc9, + 0x19, 0xc7, 0x3a, 0x67, 0x5d, 0x2e, 0x90, 0x7e, 0x9d, 0xd9, 0xd7, 0x6d, 0x5a, 0x19, 0x5f, 0x13, + 0x89, 0xab, 0x5b, 0x19, 0xdf, 0xd0, 0x8d, 0xa1, 0xfe, 0x27, 0x0d, 0xb9, 0x25, 0xf7, 0xf9, 0x0d, + 0xb9, 0xea, 0x6d, 0xb7, 0xfe, 0x2c, 0x0d, 0x6b, 0x74, 0x47, 0xe1, 0x74, 0xf9, 0x13, 0xa7, 0xf7, + 0x4f, 0xc6, 0x65, 0xb1, 0x13, 0x62, 0x72, 0x2b, 0x9a, 0xa6, 0x6b, 0x69, 0x68, 0x59, 0x38, 0x1c, + 0x03, 0xde, 0xf2, 0xf5, 0xd5, 0xfd, 0x70, 0xcb, 0x69, 0x95, 0x76, 0x42, 0xbe, 0x6d, 0x5f, 0x26, + 0xc3, 0xd6, 0xaa, 0xbe, 0xa1, 0x8b, 0xff, 0x79, 0x39, 0x41, 0xc5, 0x6c, 0xfb, 0x5a, 0x13, 0xc9, + 0xfd, 0x8b, 0xf9, 0xb6, 0x5f, 0x67, 0xa6, 0x13, 0x0b, 0x2a, 0x86, 0x50, 0xe7, 0xc0, 0x6b, 0xc6, + 0x77, 0x53, 0xbf, 0x67, 0xe5, 0xfc, 0x93, 0xd9, 0x40, 0x75, 0x16, 0x25, 0xe3, 0xcb, 0x78, 0x86, + 0x7b, 0x79, 0xdd, 0x13, 0x85, 0xc9, 0x28, 0xd5, 0x6d, 0xbd, 0xb4, 0x67, 0x63, 0x57, 0x95, 0xdb, + 0x3f, 0x01, 0x37, 0x4e, 0x65, 0x77, 0xf2, 0x03, 0xde, 0xa7, 0xe3, 0x89, 0x6f, 0x6c, 0xfd, 0x3b, + 0x97, 0x7e, 0xf7, 0x11, 0xd1, 0x6e, 0x62, 0xfa, 0x2e, 0x19, 0x7e, 0x8f, 0x0b, 0xda, 0x29, 0x61, + 0xfa, 0x3e, 0x0d, 0xdf, 0x77, 0x57, 0x8e, 0x1f, 0x9b, 0x8b, 0xed, 0xa5, 0x14, 0xda, 0xbb, 0x49, + 0x27, 0x9e, 0x1b, 0x78, 0xe9, 0xf5, 0x6e, 0xfc, 0x71, 0xed, 0xf8, 0x61, 0x17, 0x67, 0x72, 0x7e, + 0x12, 0x9b, 0x47, 0x5d, 0x2b, 0xaa, 0x9d, 0xff, 0x1b, 0x6e, 0x66, 0x97, 0x7b, 0x9e, 0x04, 0xf3, + 0xb9, 0x9e, 0x5f, 0x61, 0xa2, 0x1f, 0xa9, 0xa2, 0x81, 0xde, 0xe3, 0xab, 0xdf, 0xa7, 0x0d, 0x55, + 0xc4, 0x50, 0xbb, 0x77, 0xd2, 0xfa, 0x0d, 0x3a, 0x64, 0x57, 0x45, 0xc3, 0x87, 0x29, 0xbc, 0x3b, + 0xab, 0xa1, 0x44, 0xbb, 0x2a, 0xcf, 0x7f, 0x9a, 0x49, 0xda, 0xa4, 0x83, 0x62, 0x73, 0x77, 0x4b, + 0x47, 0x56, 0x12, 0x7a, 0xbc, 0xb9, 0xd8, 0x7e, 0x77, 0x27, 0xb9, 0x6b, 0xe3, 0x8b, 0x9b, 0xb3, + 0x73, 0x0d, 0x6e, 0xda, 0x8c, 0x9e, 0xdd, 0x5e, 0x10, 0xd7, 0xaf, 0xab, 0x67, 0xb3, 0xb6, 0x03, + 0x7a, 0x3d, 0x9f, 0x87, 0xdb, 0xa5, 0x5b, 0x11, 0x3a, 0x72, 0x4d, 0x8d, 0xf5, 0xeb, 0x9f, 0x31, + 0x66, 0x99, 0x99, 0x38, 0xbe, 0x24, 0xfb, 0xee, 0x1a, 0x69, 0xe2, 0x77, 0xdf, 0x86, 0x89, 0xab, + 0x3a, 0x2d, 0x5a, 0x73, 0x1a, 0x48, 0x9b, 0x40, 0xd7, 0x3f, 0x51, 0xb0, 0x72, 0xac, 0x6f, 0x65, + 0x48, 0x5f, 0xec, 0x51, 0xa6, 0xa1, 0x71, 0xfb, 0x76, 0x65, 0x5f, 0x6b, 0xb7, 0x67, 0x50, 0x08, + 0x22, 0x6e, 0xa7, 0xb5, 0xc3, 0xa9, 0xef, 0x99, 0x2b, 0x9a, 0x71, 0x7b, 0x28, 0xfb, 0x29, 0x67, + 0xe8, 0xa3, 0xd6, 0xe9, 0xc7, 0x55, 0xa5, 0x1d, 0xeb, 0x6d, 0x4f, 0x5c, 0x04, 0x70, 0xb1, 0x45, + 0x8d, 0xb4, 0x1b, 0xf6, 0x72, 0x82, 0xd0, 0x6d, 0x95, 0x0c, 0xc0, 0x49, 0x96, 0xb1, 0xa6, 0xca, + 0x8d, 0xc9, 0xb2, 0x99, 0x6e, 0xb7, 0x45, 0x17, 0x61, 0x6b, 0x6d, 0x79, 0x96, 0xcc, 0xa3, 0x07, + 0xd6, 0x56, 0xad, 0xf1, 0xae, 0x69, 0x91, 0xdb, 0xe6, 0x43, 0xdd, 0x8b, 0xf1, 0x5f, 0xae, 0xd9, + 0x71, 0xf7, 0xe5, 0x69, 0xbf, 0xe4, 0x5b, 0xb6, 0x6a, 0x44, 0xf2, 0x9d, 0xa7, 0xb2, 0x31, 0xd2, + 0x33, 0xdb, 0x26, 0x6d, 0xf0, 0xfc, 0x74, 0xd5, 0x34, 0x85, 0x6e, 0xe2, 0xed, 0xe7, 0xbc, 0x2f, + 0xe7, 0xb3, 0xd3, 0xf6, 0xae, 0x79, 0xe7, 0xec, 0xdd, 0xf7, 0xcf, 0x6f, 0x1b, 0x74, 0x25, 0xc9, + 0xa4, 0xf7, 0x77, 0x7f, 0x3d, 0x19, 0xb5, 0xe9, 0x37, 0xc7, 0x71, 0x64, 0x49, 0x27, 0x0f, 0xe7, + 0x85, 0x6b, 0xe2, 0x35, 0xd1, 0xe6, 0x68, 0x7c, 0x2c, 0x53, 0x33, 0x65, 0x6e, 0x20, 0x76, 0x86, + 0xb9, 0x5d, 0x97, 0x18, 0xaa, 0x51, 0x91, 0x8d, 0x3d, 0xa9, 0x1e, 0x85, 0xf5, 0xf3, 0xae, 0x8b, + 0xcf, 0xe7, 0xe9, 0x71, 0x55, 0x80, 0x3d, 0xae, 0x9f, 0xc9, 0xf1, 0x4e, 0xf3, 0xf3, 0x0d, 0x74, + 0x67, 0xd3, 0xa9, 0x07, 0x96, 0x70, 0x2e, 0xa1, 0x70, 0x16, 0x68, 0x21, 0x75, 0x6c, 0xaf, 0xce, + 0x01, 0x9b, 0x39, 0xf9, 0x5a, 0x7a, 0x50, 0xf5, 0xcd, 0x6b, 0x98, 0x78, 0x9b, 0xe7, 0x41, 0x6c, + 0x77, 0x61, 0x79, 0x73, 0xeb, 0x77, 0xff, 0xe2, 0xf1, 0xa6, 0xf1, 0xdb, 0x9d, 0xbd, 0xae, 0xcb, + 0x37, 0xae, 0x6b, 0xa3, 0x75, 0xe1, 0x66, 0xae, 0x9e, 0x7b, 0x55, 0xbf, 0x55, 0x9b, 0xd4, 0x3f, + 0x53, 0xf6, 0xba, 0x6d, 0x8d, 0xb8, 0x21, 0xde, 0xcb, 0xc9, 0x6b, 0xb2, 0xf1, 0xb6, 0xbb, 0x3e, + 0xa9, 0x6a, 0x77, 0x7d, 0xdb, 0xe5, 0x9e, 0xb9, 0x55, 0xeb, 0x68, 0xe7, 0xf7, 0xae, 0x7e, 0x81, + 0x6b, 0xe6, 0x09, 0x9e, 0xe8, 0xe5, 0xee, 0x68, 0x7b, 0x9a, 0x1e, 0x13, 0xac, 0xd1, 0xf6, 0xbe, + 0x2f, 0x35, 0x7f, 0xa6, 0x4f, 0xbc, 0x83, 0x53, 0x54, 0x7f, 0x45, 0x41, 0x4d, 0xd7, 0xc3, 0xbe, + 0x2c, 0x20, 0x36, 0x34, 0x20, 0x28, 0x28, 0x78, 0x90, 0x5e, 0x3c, 0x0c, 0xf9, 0x4f, 0xf3, 0x6b, + 0x2d, 0x33, 0x29, 0xb4, 0x41, 0x93, 0x34, 0x29, 0x79, 0xe6, 0x95, 0xf9, 0x6a, 0x2e, 0xa1, 0x67, + 0x7e, 0xa8, 0xc1, 0xc7, 0x28, 0x98, 0xe4, 0x81, 0x55, 0x09, 0x90, 0xfa, 0xce, 0xa5, 0x78, 0x33, + 0xc9, 0x5f, 0x74, 0x57, 0x1f, 0xaa, 0x96, 0x23, 0xa3, 0x33, 0x46, 0x2f, 0xa6, 0xe4, 0xc7, 0xe5, + 0xbc, 0x21, 0x96, 0x16, 0x86, 0x8f, 0xc5, 0x4d, 0x63, 0x0e, 0x20, 0x12, 0x2e, 0x61, 0x74, 0x1a, + 0x60, 0x1c, 0x74, 0xdd, 0x11, 0x3a, 0x79, 0xb6, 0xca, 0xa2, 0x2b, 0x4e, 0x75, 0x6f, 0x9c, 0xb3, + 0x6c, 0x62, 0x5d, 0xd8, 0x6c, 0x7e, 0x72, 0x89, 0x17, 0x11, 0x12, 0x21, 0x7b, 0xfd, 0x87, 0xd7, + 0x46, 0xe9, 0x19, 0xa7, 0x95, 0xe2, 0x52, 0x7a, 0x6d, 0x24, 0xcd, 0x4e, 0x95, 0x1a, 0x69, 0xaa, + 0x36, 0xcf, 0x95, 0xd6, 0x29, 0x7b, 0x50, 0xcd, 0x0f, 0x97, 0x9c, 0xea, 0xfb, 0x59, 0xeb, 0x2f, + 0x57, 0xa3, 0xdd, 0xf7, 0x2d, 0x57, 0x54, 0xd8, 0x3d, 0x4c, 0x7f, 0x62, 0x85, 0x97, 0x20, 0xe9, + 0x6f, 0x36, 0x44, 0x4c, 0x4c, 0xaa, 0x9a, 0x4d, 0x64, 0x36, 0xf2, 0x65, 0x77, 0x53, 0x5f, 0xd7, + 0x30, 0x6d, 0x6d, 0x48, 0xf6, 0x6f, 0x64, 0x6a, 0xa8, 0x6d, 0xb6, 0xeb, 0x5e, 0x1d, 0xf1, 0xf9, + 0x99, 0x3a, 0x8e, 0x8d, 0x25, 0x81, 0xe0, 0x7b, 0xca, 0x9f, 0x78, 0x03, 0x11, 0x62, 0x9e, 0x5b, + 0xca, 0xf7, 0xc8, 0x5d, 0x3b, 0x1d, 0x7d, 0xc9, 0xa7, 0x9c, 0x00, 0xf3, 0xe6, 0xc5, 0x8f, 0xce, + 0xb3, 0xb5, 0x47, 0x25, 0xf2, 0x7b, 0xe7, 0x73, 0xf0, 0xa9, 0x77, 0xbe, 0x2d, 0x3d, 0x69, 0x9d, + 0xcb, 0x3d, 0x62, 0x43, 0x7e, 0x95, 0x2b, 0xb3, 0xf6, 0xd3, 0x24, 0x61, 0xd4, 0xca, 0x13, 0xc4, + 0x50, 0x0a, 0x66, 0x29, 0x17, 0x85, 0x63, 0x2a, 0x16, 0xbb, 0x65, 0x6e, 0x52, 0x03, 0xe3, 0x9a, + 0xd6, 0x39, 0x5a, 0x0d, 0x96, 0x47, 0xd5, 0x8e, 0x1d, 0x8f, 0x05, 0x97, 0x95, 0x09, 0x66, 0x75, + 0x91, 0x08, 0xc9, 0xcd, 0xc0, 0x91, 0x74, 0x6b, 0xd9, 0x1d, 0xd6, 0x97, 0xf0, 0x36, 0x6e, 0xdb, + 0x60, 0x3b, 0x78, 0x9e, 0x68, 0x36, 0x2d, 0xd9, 0x56, 0xa3, 0x4f, 0x1a, 0x29, 0x3d, 0x3f, 0x56, + 0x53, 0x0a, 0x8a, 0x25, 0x79, 0x10, 0x6b, 0x0e, 0xdd, 0x0d, 0x6d, 0x08, 0x17, 0xd9, 0x0b, 0x95, + 0xb9, 0xfd, 0x25, 0x1a, 0x79, 0x24, 0xf5, 0xdd, 0x6f, 0x59, 0xad, 0x36, 0xb8, 0x51, 0xd6, 0xca, + 0xb5, 0x31, 0x55, 0xe8, 0xaf, 0xe3, 0x6a, 0xb8, 0xe1, 0xd5, 0x17, 0x9e, 0xa1, 0x61, 0xcb, 0xa3, + 0xe3, 0x4f, 0x2d, 0x72, 0x4d, 0xb7, 0xfc, 0x57, 0xf4, 0x01, 0xb7, 0x77, 0x03, 0x3e, 0xfb, 0x9d, + 0xd2, 0xb2, 0xc8, 0x66, 0x44, 0x69, 0x7e, 0xda, 0x0d, 0x95, 0x41, 0x99, 0x62, 0xd9, 0xdf, 0x49, + 0x42, 0x31, 0x86, 0xc6, 0x79, 0xac, 0x83, 0xd2, 0xc7, 0xdc, 0xbb, 0x81, 0x41, 0x82, 0xfe, 0x70, + 0x1e, 0x76, 0x8b, 0x0b, 0x7e, 0x9d, 0x97, 0xa8, 0xbb, 0x21, 0x67, 0x6f, 0xf2, 0xe8, 0x38, 0x6e, + 0x2c, 0x4b, 0x27, 0x93, 0x13, 0x27, 0x49, 0x6e, 0xb8, 0xc6, 0x29, 0x6d, 0x68, 0x04, 0x5d, 0xe5, + 0xbe, 0x7e, 0xfa, 0x37, 0x0d, 0xbb, 0x28, 0x1f, 0x6f, 0x30, 0xa3, 0x2b, 0x7c, 0x71, 0x79, 0x22, + 0x21, 0x45, 0x52, 0xaa, 0x74, 0xff, 0xa3, 0xb4, 0x59, 0x9d, 0xe5, 0x7f, 0x66, 0x57, 0xa9, 0xc7, + 0xf6, 0xe0, 0x35, 0xdd, 0x4b, 0x42, 0x29, 0xd1, 0x75, 0x85, 0xda, 0x0f, 0x76, 0x6d, 0xa6, 0xab, + 0x69, 0xd9, 0x39, 0xd7, 0xe8, 0xe5, 0xd5, 0x1f, 0xba, 0x39, 0xbf, 0x6a, 0xb2, 0xe2, 0x87, 0xe6, + 0xd4, 0xe7, 0x56, 0x79, 0x75, 0x5b, 0x7a, 0x29, 0x99, 0xf9, 0xbd, 0x9f, 0xbc, 0x05, 0xa0, 0x27, + 0x7b, 0x06, 0xf6, 0x27, 0xb6, 0x75, 0x75, 0x81, 0xbe, 0x37, 0x2b, 0x76, 0xa7, 0x6a, 0x7c, 0xca, + 0x93, 0x95, 0x84, 0xb6, 0x8f, 0xf5, 0x69, 0x28, 0xaa, 0xe7, 0xc5, 0x6a, 0xbc, 0x31, 0xad, 0xbc, + 0x71, 0x82, 0x49, 0xf1, 0x50, 0xa6, 0x57, 0x97, 0x9f, 0x55, 0x28, 0x06, 0x6a, 0xae, 0x5b, 0xbe, + 0xc4, 0xa3, 0xa3, 0x69, 0x85, 0x95, 0x97, 0x96, 0x47, 0xa4, 0x2b, 0x1e, 0x75, 0xa7, 0x49, 0x6f, + 0x36, 0x9f, 0x9b, 0x9c, 0xb5, 0x6b, 0xee, 0x7f, 0xe7, 0x6b, 0xbd, 0x6b, 0xdf, 0xd7, 0x41, 0xec, + 0xf7, 0xdb, 0xf9, 0x79, 0xed, 0xc7, 0xe7, 0xff, 0x2b, 0xbb, 0x9a, 0xb2, 0x89, 0x7f, 0xf3, 0x47, + 0xd6, 0xcd, 0x7a, 0x53, 0xc9, 0x37, 0x67, 0x50, 0xb9, 0xd7, 0xf7, 0xff, 0x76, 0x25, 0x6a, 0x82, + 0xe3, 0xf7, 0x66, 0x81, 0x9b, 0x37, 0x7d, 0xfe, 0x2b, 0xeb, 0x4d, 0x99, 0xf7, 0x39, 0x6b, 0xd9, + 0x2d, 0xc7, 0x5f, 0xfe, 0x36, 0xc7, 0xdd, 0x9d, 0xa6, 0xf3, 0xe6, 0xcb, 0xb9, 0xe6, 0x1a, 0xfd, + 0xb2, 0xfd, 0x9a, 0xb6, 0xfb, 0x56, 0x27, 0xad, 0xc1, 0xb6, 0xbe, 0x6e, 0x85, 0x7f, 0x3c, 0xc5, + 0x87, 0x61, 0x76, 0x97, 0xad, 0x36, 0x23, 0xce, 0x8f, 0xda, 0x39, 0xf9, 0xfb, 0x99, 0x55, 0xdb, + 0xcf, 0x6a, 0x75, 0x33, 0x94, 0x1a, 0x0a, 0x6c, 0xfe, 0x25, 0xa3, 0x5e, 0xbc, 0xad, 0xb5, 0x92, + 0xeb, 0x9d, 0xc9, 0x51, 0xc7, 0xdf, 0x77, 0x3c, 0xa7, 0x57, 0xa8, 0x1c, 0x5b, 0x9d, 0xe9, 0xdf, + 0x61, 0xf7, 0x8d, 0xa3, 0x47, 0x25, 0x42, 0x26, 0xcd, 0xa5, 0x01, 0xbf, 0x77, 0xa5, 0xf4, 0x67, + 0x70, 0x64, 0x04, 0x27, 0x56, 0xfc, 0xe9, 0x7f, 0x5a, 0xc9, 0x9a, 0x33, 0x7b, 0xc5, 0x31, 0x74, + 0xa1, 0xbb, 0xbf, 0x6a, 0x28, 0xb5, 0x09, 0xfc, 0xe5, 0xb6, 0xe6, 0x27, 0x99, 0x28, 0xaa, 0xe3, + 0x31, 0x9f, 0x19, 0x8d, 0x5d, 0x15, 0x2c, 0xbb, 0xa5, 0xe1, 0xbb, 0x39, 0x7b, 0x5a, 0xbb, 0x6e, + 0x51, 0x5a, 0xd6, 0xcf, 0xb3, 0x65, 0xab, 0x6b, 0xa6, 0x7d, 0xbf, 0xad, 0xbd, 0xad, 0x5e, 0x51, + 0x66, 0x7f, 0xba, 0x75, 0xb3, 0xee, 0xb0, 0x42, 0x72, 0xed, 0x5a, 0xa7, 0xdf, 0xe1, 0x9d, 0x61, + 0xf6, 0xb2, 0xd5, 0xa6, 0x96, 0xd3, 0xed, 0xa7, 0xfa, 0x6f, 0x86, 0x4d, 0xec, 0xab, 0xab, 0xd8, + 0xad, 0x6e, 0xcb, 0x35, 0x8a, 0x5a, 0x44, 0x45, 0x43, 0x4f, 0x06, 0xe7, 0x21, 0x45, 0xec, 0x34, + 0x47, 0xe9, 0xbb, 0x75, 0x89, 0x33, 0xf6, 0xed, 0x95, 0xf7, 0xee, 0x71, 0xc5, 0x20, 0xbb, 0x39, + 0xa7, 0xaf, 0x2b, 0x41, 0x5d, 0x4b, 0xaf, 0x6a, 0x53, 0x5d, 0xc1, 0xaf, 0x3e, 0x5f, 0x75, 0x31, + 0x6b, 0xa0, 0xc7, 0x6b, 0xd9, 0x97, 0x7d, 0x33, 0xbe, 0x6e, 0x69, 0xdb, 0xe6, 0x51, 0xf7, 0xa6, + 0xe7, 0x56, 0xd1, 0xbd, 0x83, 0x4e, 0xd7, 0x6c, 0x89, 0x7d, 0xd5, 0x47, 0x2b, 0x9b, 0x2c, 0xe3, + 0x9d, 0xb6, 0xb8, 0xb1, 0xaf, 0x6b, 0x9d, 0xaf, 0xa1, 0xdb, 0xb1, 0x95, 0xaf, 0xf6, 0x21, 0x5b, + 0x4a, 0xb3, 0xfb, 0xa6, 0xf8, 0x31, 0xda, 0xe7, 0xa5, 0xad, 0xdf, 0x57, 0x7c, 0x5f, 0xa5, 0xba, + 0x71, 0x87, 0x2e, 0xba, 0x79, 0xfd, 0x39, 0x5f, 0xbb, 0x3a, 0x09, 0x97, 0x61, 0x6e, 0xdb, 0x15, + 0xd7, 0x47, 0x6e, 0xcf, 0x21, 0xde, 0x4a, 0x72, 0x9b, 0xd8, 0x31, 0xfd, 0xaf, 0x3d, 0xed, 0x79, + 0x01, 0x3b, 0xe7, 0x13, 0x28, 0xb7, 0xb5, 0x27, 0xde, 0x9a, 0x55, 0x69, 0xeb, 0xf7, 0xa9, 0x2f, + 0xb9, 0x8d, 0xd9, 0xbf, 0x8d, 0xf5, 0xbf, 0x8d, 0xcf, 0xbb, 0xcd, 0xfd, 0xb8, 0x43, 0xa6, 0xbd, + 0x79, 0x8a, 0x3b, 0x53, 0x65, 0x74, 0x74, 0x98, 0x3a, 0x5b, 0x73, 0xd6, 0xae, 0x77, 0xe7, 0xea, + 0x36, 0x9f, 0x06, 0x05, 0x29, 0xbd, 0xa7, 0xb1, 0x31, 0xb9, 0x79, 0x36, 0x4f, 0x4b, 0x2c, 0xe6, + 0x8b, 0xa5, 0x50, 0x8e, 0xe3, 0xb5, 0x27, 0xda, 0xf3, 0x95, 0xe8, 0x7a, 0x2a, 0x29, 0x29, 0xaf, + 0xce, 0x33, 0x61, 0x53, 0xab, 0x8b, 0x45, 0x77, 0x39, 0x66, 0xb3, 0x6b, 0xb6, 0x51, 0xd9, 0xeb, + 0x6e, 0xf8, 0x31, 0x5e, 0x3e, 0x1a, 0xfd, 0xe6, 0xc1, 0x79, 0x11, 0x18, 0x22, 0x1f, 0xd0, 0x12, + 0x67, 0x56, 0x3c, 0x8c, 0xbb, 0x61, 0x95, 0x50, 0xfc, 0x3d, 0xa6, 0xaf, 0x75, 0xb3, 0x65, 0xc5, + 0x2a, 0x86, 0x1f, 0x62, 0x2a, 0xcb, 0x13, 0xdd, 0xf1, 0x6d, 0x9f, 0x9a, 0xfc, 0xd5, 0xa6, 0x9d, + 0x35, 0xf1, 0x77, 0xae, 0x9a, 0x4f, 0x72, 0x3c, 0x69, 0x75, 0x70, 0x61, 0x6e, 0xdd, 0xff, 0x6a, + 0x00, 0x67, 0x6d, 0xcb, 0x8f, 0x58, 0xc4, 0x27, 0x6b, 0x99, 0x0f, 0xf9, 0xca, 0x8a, 0xc9, 0x6b, + 0xde, 0xb5, 0x7b, 0xa6, 0xbb, 0x76, 0x1f, 0x2d, 0x2d, 0x20, 0x45, 0x78, 0x61, 0xfe, 0x26, 0x6f, + 0x96, 0xa3, 0x8d, 0xd9, 0x45, 0x9e, 0x46, 0x6e, 0x77, 0xa7, 0xfb, 0x34, 0x39, 0x45, 0xd9, 0x0c, + 0xaa, 0x1f, 0xe7, 0x02, 0x27, 0x75, 0x66, 0x26, 0x26, 0xc5, 0xfb, 0x3d, 0x57, 0x4d, 0x34, 0x4b, + 0x9c, 0x9d, 0x65, 0x73, 0x55, 0x8d, 0x59, 0xfe, 0x99, 0x1f, 0x95, 0x5d, 0x9e, 0x68, 0x9a, 0x0b, + 0xe1, 0x26, 0x29, 0xdf, 0x8f, 0x85, 0x7f, 0x21, 0xed, 0x68, 0xdb, 0x5e, 0xe8, 0xf3, 0xd9, 0x33, + 0x6b, 0xac, 0x0d, 0xf5, 0x2c, 0xda, 0x68, 0xf8, 0xf9, 0xc5, 0x20, 0xca, 0xfd, 0x91, 0x7f, 0x76, + 0x7a, 0x37, 0x19, 0x2e, 0xb1, 0x87, 0x4c, 0xff, 0x75, 0xb3, 0x7c, 0x8a, 0x9f, 0x9b, 0xfc, 0x64, + 0x3e, 0x79, 0x49, 0x90, 0x78, 0x42, 0x96, 0x97, 0xc6, 0x7b, 0xe6, 0x5f, 0x6f, 0xf7, 0x6b, 0x55, + 0x01, 0x3d, 0xa6, 0xac, 0x53, 0xdb, 0x1e, 0x27, 0xeb, 0x2d, 0xc9, 0x9e, 0x92, 0xdd, 0x86, 0x02, + 0x1a, 0x1d, 0xed, 0xd7, 0x1a, 0x27, 0xbe, 0x2b, 0xc9, 0x6a, 0xd7, 0x8b, 0x94, 0x41, 0x49, 0x7e, + 0xe3, 0xb7, 0xda, 0xe2, 0xd5, 0x98, 0x33, 0x6d, 0xc5, 0xc7, 0x2e, 0x3b, 0x5f, 0x67, 0x81, 0x6e, + 0x5a, 0xa5, 0x7c, 0xb3, 0x50, 0x73, 0x20, 0xa6, 0x6e, 0x6b, 0x8b, 0xff, 0x69, 0xc8, 0x04, 0xa4, + 0x79, 0x76, 0x65, 0x72, 0x34, 0xf8, 0xbb, 0xd9, 0x19, 0x57, 0x00, 0x75, 0x6a, 0xd1, 0xdd, 0xc7, + 0xec, 0x90, 0x5f, 0x41, 0xea, 0x6c, 0x6b, 0x94, 0xfd, 0x31, 0xda, 0xe8, 0xd5, 0x1c, 0x38, 0x73, + 0xd7, 0x12, 0x69, 0x69, 0x8b, 0xad, 0xaa, 0x1b, 0x6c, 0x2d, 0x3b, 0x7d, 0xf1, 0xff, 0x8c, 0x4b, + 0x36, 0x57, 0xa5, 0xa4, 0x45, 0x6b, 0x5f, 0x65, 0xaa, 0x79, 0x17, 0x97, 0x2f, 0xee, 0x75, 0x39, + 0xac, 0x45, 0xb8, 0xcb, 0x4f, 0x5c, 0xaa, 0x15, 0x71, 0x13, 0xdb, 0x10, 0x13, 0xa7, 0xad, 0x6f, + 0xd9, 0x47, 0xbe, 0x3d, 0xab, 0x67, 0xcd, 0xff, 0x2d, 0xa5, 0xfb, 0x9d, 0x41, 0xa9, 0xf7, 0x96, + 0xcc, 0xc5, 0xc5, 0xa7, 0x5a, 0x76, 0xa6, 0xb3, 0xb6, 0xe6, 0xa2, 0xee, 0x6e, 0x3b, 0x6e, 0x76, + 0xef, 0xf3, 0xa9, 0xa9, 0xbe, 0xf2, 0xdf, 0xfa, 0x7a, 0xe3, 0xe5, 0xf6, 0x3b, 0x2b, 0xa7, 0xaf, + 0x7d, 0x43, 0xe9, 0xef, 0x6e, 0xa9, 0x0d, 0xde, 0x8b, 0xcf, 0xc7, 0x3a, 0xb1, 0x2b, 0xc9, 0xa9, + 0xbf, 0x5e, 0x81, 0xca, 0x2d, 0xb1, 0x77, 0x1a, 0xbe, 0x6b, 0xbe, 0x3d, 0x73, 0xa3, 0x67, 0x74, + 0x17, 0x7a, 0x62, 0xb7, 0x19, 0x69, 0xd2, 0xa3, 0x3f, 0xb7, 0xeb, 0x99, 0x4f, 0xab, 0xe2, 0x5b, + 0x77, 0x1d, 0xc7, 0x78, 0xe3, 0x33, 0x99, 0x01, 0xc6, 0x79, 0xeb, 0x73, 0x4b, 0x25, 0x6e, 0x64, + 0x2d, 0x3c, 0xf4, 0x66, 0x2b, 0x9e, 0xe3, 0xb7, 0x96, 0x08, 0x6f, 0x1a, 0x36, 0xff, 0xb9, 0x97, + 0xfd, 0x6d, 0x76, 0x3b, 0xb9, 0x8b, 0xff, 0x6f, 0xfc, 0xd1, 0xbb, 0xda, 0xa2, 0x35, 0xe7, 0x56, + 0x1e, 0x12, 0x87, 0x56, 0x1d, 0xde, 0x3b, 0xd8, 0x2b, 0xff, 0x69, 0xf1, 0xd3, 0xf5, 0xbd, 0xbb, + 0xa3, 0x9b, 0xec, 0xa8, 0x4f, 0x62, 0x73, 0x6f, 0x6c, 0x65, 0x74, 0x65, 0xeb, 0xd7, 0xca, 0xbc, + 0x3f, 0x61, 0xb7, 0xa8, 0xab, 0x86, 0x57, 0x18, 0x14, 0x6e, 0xb5, 0x6d, 0x98, 0x31, 0x31, 0x53, + 0xee, 0x9d, 0xb5, 0x90, 0x94, 0x36, 0x77, 0xa8, 0x5b, 0x6d, 0x1a, 0xf5, 0xad, 0x6a, 0x10, 0xe5, + 0xf9, 0xbf, 0x5a, 0x4b, 0xc1, 0xb1, 0x6d, 0xad, 0x45, 0x6b, 0x37, 0x1d, 0xbe, 0xd9, 0x43, 0xbe, + 0xd6, 0x79, 0xb7, 0xed, 0x6f, 0x59, 0x25, 0xb6, 0x55, 0x68, 0x15, 0x98, 0x36, 0x59, 0xa8, 0x4f, + 0xc9, 0x2d, 0x57, 0xb2, 0x50, 0x5d, 0x2d, 0x64, 0xff, 0xb7, 0x9a, 0x1b, 0xd3, 0x97, 0x30, 0x78, + 0x8c, 0xa3, 0x24, 0x66, 0x98, 0x95, 0x63, 0x63, 0x59, 0x0f, 0x13, 0x7e, 0x5f, 0x43, 0x52, 0x54, + 0x5f, 0x53, 0x45, 0xbd, 0x5d, 0x2f, 0x1a, 0x5f, 0x4e, 0x4f, 0x5f, 0x57, 0x41, 0x52, 0x4e, 0x49, + 0x4e, 0x47, 0xe4, 0xcf, 0x45, 0x56, 0xb6, 0x75, 0xe0, 0xba, 0xb3, 0xfd, 0xc7, 0x2e, 0xea, 0x8e, + 0xfb, 0x6b, 0xec, 0xf7, 0xde, 0xbe, 0x38, 0x3b, 0x09, 0x44, 0x50, 0x52, 0x21, 0x8e, 0x41, 0x54, + 0x74, 0x89, 0x23, 0x3d, 0x46, 0x42, 0x4c, 0x4f, 0x43, 0x4b, 0x2e, 0x87, 0xf1, 0xea, 0x90, 0x03, + 0x6e, 0xd7, 0x55, 0xe4, 0x4c, 0x59, 0x20, 0xa7, 0x3e, 0x51, 0xcd, 0x47, 0x43, 0x43, 0xee, 0xf7, + 0x07, 0x7e, 0xa9, 0xbf, 0x35, 0x47, 0x4e, 0x55, 0x1e, 0xf9, 0x24, 0xde, 0xd6, 0xef, 0x34, 0x43, + 0x21, 0xde, 0xef, 0x34, 0x6b, 0x16, 0x56, 0x29, 0x77, 0x57, 0x1a, 0x4e, 0x71, 0x9a, 0xed, 0x9f, + 0x3d, 0x34, 0x30, 0x35, 0x29, 0x20, 0x7c, 0x7c, 0x61, 0xb5, 0x64, 0x1c, 0x95, 0x73, 0x69, 0x3c, + 0x66, 0x69, 0x3b, 0x37, 0x26, 0x44, 0x28, 0x99, 0x77, 0x5d, 0x72, 0x4e, 0xc8, 0xfe, 0xb3, 0x83, + 0x12, 0x65, 0x14, 0x68, 0x28, 0x28, 0x05, 0xb4, 0x45, 0xa5, 0x00, 0x5e, 0x91, 0xb4, 0x9e, 0xff, + 0xf8, 0x05, 0x33, 0x31, 0xd3, 0xfe, 0x4b, 0xc1, 0x62, 0x89, 0xc9, 0x4d, 0x53, 0x24, 0xc9, 0xb3, + 0xbf, 0x6d, 0x5d, 0x6a, 0xad, 0x17, 0x62, 0x7f, 0xe5, 0xbb, 0xde, 0x4d, 0x72, 0x97, 0xfa, 0xf6, + 0xc9, 0x58, 0x3b, 0x9a, 0x28, 0x22, 0x61, 0x4d, 0x3a, 0xb5, 0x4b, 0xaa, 0x76, 0x6f, 0xbc, 0x59, + 0xb5, 0xe7, 0xe6, 0xd7, 0xb5, 0xb8, 0x47, 0x9b, 0xbf, 0x67, 0x2f, 0x86, 0xbe, 0x22, 0x2e, 0x45, + 0x95, 0x47, 0xfb, 0xa2, 0x51, 0x52, 0x10, 0x0d, 0x1f, 0x20, 0x6d, 0xa8, 0x5b, 0xf9, 0xe5, 0x01, + 0xf5, 0xf5, 0xa9, 0xa3, 0xda, 0xfb, 0x3a, 0xbb, 0x6a, 0x67, 0x3f, 0xb6, 0x17, 0x5d, 0x2b, 0xbb, + 0x8c, 0xa7, 0xb3, 0x70, 0x6c, 0x61, 0x6e, 0x6e, 0xda, 0xd7, 0xb3, 0xee, 0x9d, 0x61, 0xa8, 0x81, + 0xb7, 0xa4, 0xc9, 0xa6, 0x72, 0x31, 0x33, 0x31, 0xa0, 0x78, 0x70, 0xdb, 0xa1, 0xf5, 0xae, 0xc9, + 0x8d, 0xba, 0x8b, 0xaf, 0xdf, 0xa6, 0xf9, 0xdb, 0xae, 0x3f, 0x01, 0xda, 0xfe, 0x2f, 0xcd, 0x7a, + 0xfd, 0xfd, 0x9f, 0xac, 0x97, 0x9b, 0xae, 0x7a, 0xa3, 0xd3, 0x7d, 0x4f, 0xc7, 0xda, 0xd6, 0xb1, + 0xbf, 0xcd, 0x8d, 0x69, 0x01, 0xaf, 0xf6, 0xa9, 0xfd, 0x8b, 0x95, 0xa4, 0xe9, 0xb5, 0xcd, 0xbf, + 0x63, 0xc1, 0xad, 0xae, 0x2d, 0x39, 0x9f, 0xc1, 0x7a, 0xf2, 0x9f, 0xb6, 0xbf, 0x29, 0xcf, 0x8e, + 0xe1, 0xa9, 0x9d, 0x09, 0x7b, 0xed, 0x72, 0x01, 0xaf, 0x7d, 0x1f, 0xb5, 0x6a, 0xf3, 0x23, 0xac, + 0x80, 0x20, 0x6e, 0x6d, 0x7c, 0x2d, 0x9b, 0xaa, 0x3d, 0x7d, 0xac, 0x57, 0xb6, 0xf9, 0xa5, 0x7e, + 0xcd, 0xd9, 0xf9, 0x0a, 0xaf, 0xd7, 0x7d, 0xe0, 0xa8, 0x72, 0xa5, 0x20, 0xea, 0x96, 0x89, 0xb8, + 0xd5, 0xf5, 0x34, 0x6f, 0x6e, 0xfc, 0x3f, 0xb5, 0x2a, 0x6f, 0x24, 0xfc, 0x49, 0xa7, 0xe1, 0x57, + 0xfe, 0xa2, 0x05, 0x6e, 0xad, 0x2d, 0x9f, 0xef, 0x32, 0x84, 0x64, 0x65, 0x72, 0xa1, 0x73, 0xeb, + 0x77, 0xf9, 0x59, 0x27, 0x26, 0x9d, 0x71, 0xbe, 0xb7, 0xc5, 0x9a, 0xb2, 0xc5, 0xf4, 0x6f, 0xe9, + 0x1b, 0x15, 0xbf, 0x47, 0x05, 0x6e, 0x2d, 0x39, 0x7e, 0x93, 0x6b, 0xd4, 0x85, 0x93, 0x77, 0x2b, + 0x99, 0x3b, 0x6b, 0x61, 0x3a, 0x60, 0x70, 0xd9, 0x35, 0x1a, 0xa5, 0xa7, 0x3b, 0x34, 0x64, 0x3b, + 0x20, 0xdd, 0x9b, 0xd5, 0x05, 0xb1, 0x18, 0x6d, 0xf3, 0x9f, 0xed, 0x35, 0xd6, 0x56, 0xa9, 0xab, + 0x6f, 0x35, 0x59, 0x39, 0x65, 0xbb, 0xaa, 0xe6, 0x63, 0x7b, 0xc1, 0x50, 0x67, 0x7e, 0x34, 0xa9, + 0x57, 0xa6, 0xaf, 0xdd, 0x4d, 0xaf, 0x26, 0x59, 0xac, 0x87, 0xd6, 0xf3, 0x9d, 0xf5, 0x5b, 0x6a, + 0x95, 0x33, 0x55, 0x69, 0xe6, 0xa1, 0xf3, 0x15, 0x07, 0x70, 0xe4, 0x69, 0x08, 0x9d, 0xd1, 0x27, + 0xde, 0x07, 0x6b, 0xcb, 0xe3, 0xe6, 0xce, 0x7d, 0x4e, 0x95, 0x77, 0x9d, 0xf4, 0xb9, 0xbb, 0x67, + 0xdc, 0x8f, 0x53, 0xde, 0x8a, 0x79, 0x9f, 0x37, 0x0f, 0xa9, 0x4f, 0xa5, 0x48, 0xdc, 0x20, 0xe7, + 0x05, 0x65, 0x39, 0x9f, 0xfb, 0x6c, 0x47, 0x7e, 0x77, 0x9d, 0x95, 0xaf, 0x75, 0x75, 0xa5, 0x7b, + 0x78, 0xb9, 0x47, 0x22, 0x81, 0xef, 0x2b, 0x59, 0x1f, 0x29, 0xfd, 0x99, 0xc5, 0x22, 0x12, 0xb8, + 0x03, 0x92, 0x45, 0xde, 0xb9, 0x2b, 0xdf, 0x37, 0xcf, 0xae, 0x61, 0x2d, 0x3b, 0x6a, 0xad, 0xd9, + 0xe3, 0x1b, 0x20, 0xee, 0x69, 0xa3, 0x5b, 0x45, 0xee, 0x2d, 0xaf, 0x6e, 0xa7, 0x7f, 0xf7, 0x61, + 0x5f, 0xa5, 0x5d, 0xa7, 0x74, 0x45, 0xa5, 0xe6, 0xf6, 0x8f, 0xd8, 0x81, 0xef, 0x7a, 0xb7, 0xe9, + 0x5b, 0xd3, 0x53, 0x00, 0x89, 0x4c, 0x45, 0x6a, 0x6c, 0x04, 0x93, 0x49, 0x9b, 0xb7, 0xa6, 0x6b, + 0x96, 0xb4, 0x8f, 0x8b, 0x9e, 0xf2, 0xed, 0xe6, 0x8f, 0x3f, 0x9e, 0xe7, 0x99, 0xb7, 0x7d, 0xab, + 0x3a, 0x5b, 0x6b, 0x47, 0x9d, 0x50, 0x8b, 0x4e, 0x19, 0x66, 0x69, 0x78, 0x36, 0x34, 0x3e, 0xef, + 0xcf, 0x75, 0xde, 0xe7, 0xf5, 0xf5, 0x3e, 0x5d, 0x1f, 0x5b, 0xad, 0xad, 0xf3, 0xf6, 0x3a, 0x4b, + 0xd5, 0x5b, 0x8f, 0x4b, 0xe7, 0xfd, 0x07, 0xf6, 0xab, 0xe6, 0xdd, 0xf1, 0x7d, 0xaa, 0xf9, 0xc7, + 0xda, 0xaa, 0xa0, +}; + +static unsigned char buf[18830]; + +int main() { + + unsigned long cksum = adler32(0, NULL, 0); + + decompress_zx02(compressed, buf); + cksum = adler32(cksum, buf, 18830); + + return cksum == 0xf748269d ? 0 : 1; +}